gwpy/cli/gwpy_plot.py

Summary

Maintainability
A
0 mins
Test Coverage
# -*- coding: utf-8 -*-
# Copyright (C) Joseph Areeda (2015-2020)
#
# This file is part of GWpy.
#
# GWpy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GWpy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GWpy.  If not, see <http://www.gnu.org/licenses/>.

"""Generate plots of GW observatory data using GWpy
"""

import time

import os
import sys
from argparse import (
    ArgumentParser,
    ArgumentDefaultsHelpFormatter,
    RawTextHelpFormatter,
)

from matplotlib import use

from .. import __version__
from . import PRODUCTS

__author__ = 'Joseph Areeda <joseph.areeda@ligo.org>'

# if launched from a terminal with no display
# Must be done before modules like pyplot are imported
if len(os.getenv('DISPLAY', '')) == 0:
    use('Agg')

PROG_START = time.time()    # verbose enough times major ops

INTERACTIVE = hasattr(sys, 'ps1')

EPILOG = f"""
Examples:

    $ gwpy-plot timeseries --chan H1:GDS-CALIB_STRAIN --start 1126259457

    $ gwpy-plot spectrum --chan H1:GDS-CALIB_STRAIN L1:GDS-CALIB_STRAIN --chan V1:Hrec_hoft_16384Hz --start 1187008866 --duration 32 --xmin 10 --xmax 4000

    $ gwpy-plot coherencegram --chan H1:GDS-CALIB_STRAIN H1:PEM-CS_ACC_PSL_PERISCOPE_X_DQ --start 1126260017 --duration 600

Written by {__author__}.
Report bugs to https://github.com/gwpy/gwpy/issues/.
"""  # noqa: E501


# -- init command line --------------------------------------------------------

class HelpFormatter(ArgumentDefaultsHelpFormatter, RawTextHelpFormatter):
    def _format_usage(self, usage, actions, groups, prefix):
        if prefix is None:
            prefix = "Usage: "
        return super()._format_usage(
            usage,
            actions,
            groups,
            prefix,
        )


class _ArgumentParser(ArgumentParser):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._positionals.title = 'Positional arguments'
        self._optionals.title = 'Options'


def create_parser():
    parser = _ArgumentParser(
        description=__doc__,
        formatter_class=HelpFormatter,
        epilog=EPILOG,
    )
    parser.add_argument('-V', '--version', action='version',
                        version=__version__)

    # set the argument parser to act as the parent
    parentparser = _ArgumentParser(add_help=False)
    parentparser._optionals.title = 'Verbosity options'
    parentparser.add_argument('-v', '--verbose', action='count', default=1,
                              help='increase verbose output')
    parentparser.add_argument('-s', '--silent', action='store_true',
                              help='show only fatal errors')

    # subparsers are dependent on which action is chosen
    subparsers = parser.add_subparsers(
        dest='mode', title='Actions',
        description='Select one of the following actions:')

    # Add the subparsers for each plot product
    for product, product_class in PRODUCTS.items():
        subparser = subparsers.add_parser(
            product, help=product_class.__doc__.strip().split('\n')[0],
            parents=[parentparser],
            formatter_class=ArgumentDefaultsHelpFormatter,
        )
        product_class.init_cli(subparser)

    return parser


def parse_command_line(args=None):
    parser = create_parser()
    return parser.parse_args(args=args)


# -- run ----------------------------------------------------------------------

def main(args=None):
    """Run gwpy-plot

    Returns the relevant exit code, that can be passed to :func:`sys.exit`.
    """
    # parse the command line and create a product object
    args = parse_command_line(args=args)
    prod = PRODUCTS[args.mode](args)
    prod.log(2, f'{prod.action} created')

    # log how long it took us to get here
    setup_time = time.time() - PROG_START
    prod.log(2, f'Setup time {setup_time:.1f} sec')

    # -- generate the plot
    prod.run()

    # overload the current namespace for interactive (i)python users
    if INTERACTIVE:
        # import pyplot so that user has access to it
        from matplotlib import pyplot as plt  # noqa: F401
        plot = prod.plot
        # pull raw data and plotted results from product for their use
        timeseries = prod.timeseries  # noqa: F841
        result = prod.result  # noqa: F841
        print('Raw data is in "timeseries", plotted data is in "result"')
        ax = plot.gca()  # noqa: F841

    run_time = time.time() - PROG_START
    prod.log(1, f'Program run time: {run_time:.1f}')
    if prod.got_error:
        return 2     # make sure when running batch they can test for error


if __name__ == "__main__":  # pragma: no-cover
    sys.exit(main())