    Creates a species of Baum ice crystals with a Gaussian cloud shape.

    Parameters
    ----------
    particlesize_microns : float
        Particle size used for the :py:class:`sasktran.BaumIceCrystal` optical property
    cloud_top_m : float
        Cloud top altitude in m.  Defined such that 95% of the cloud is below this height. [m]
    cloud_width_fwhm_m : float
        Full width at half maximum of the Gaussian cloud. [m]
    vertical_optical_depth : float
        Vertical optical depth of the cloud specified at vertical_optical_depth_wavel_nm
    vertical_optical_depth_wavel_nm : float
        Wavelength where the cloud has the optical depth specified by vertical_optical_depth. [nm]
    altitude_resolution_m : float, optional
        Internal resolution to calculate the cloud number density at.  Default 10 m.
    num_sigma : int, optional
        Number of standard deviations to include away from the center of the cloud.  Default 5.
    name : str, optional
        Name of the species, only necessary to change if you want a different name or want to add multiple ice cloud
        layers.
    use_delta_eddington : bool, optional
        True if the delta-eddington reduction should be applied to the cloud optical property


    Utility class for obtaining the solar spectrum

    Parameters
    ----------
    name: str, optional
        Solar spectrum to use, valid choices are 'sao2010' and 'FONTELA_UVIS_3MICRON'.  Default is 'sao2010'

    Examples
    --------
    >>> from sasktran import SolarSpectrum
    >>> spectrum = SolarSpectrum()
    >>> spectrum.irradiance([340, 600])
    array([  1.76034000e+14,   5.38339000e+14])


    Generic optical property container that is applicable to a wide variety of optical properties.

    Parameters
    ----------
    name : str
        Name of the optical property to create

    Examples
    --------
    >>> from sasktran import OpticalProperty, Climatology
    >>> opt_prop = OpticalProperty('O3_OSIRISRES')
    >>> print(opt_prop)
    SasktranIF Optical Property: O3_OSIRISRES
    >>> atmospheric_state = Climatology('msis90')
    >>> opt_prop.calculate_cross_sections(atmospheric_state, latitude=0, longitude=0, altitude=20000, mjd=54372,\
                                          wavelengths=[350, 600])
    CrossSections(wavelengths=array([350, 600]), absorption=array([  7.09857544e-23,   5.27719024e-21]), scattering=array([ 0.,  0.]), total=array([  7.09857544e-23,   5.27719024e-21]))


    User defined absorption optical property that may be temperature dependent.

    Parameters
    ----------
    wavelengths: np.ndarray
        Wavelengths the cross section is specified at. Shape (n,)
    cross_sections: np.ndarray
        Cross sections in /cm2. Shape (n,) if the cross section is not temperature dependent, and (n, m) if it is.
    temperatures: np.ndarray, optional
        Temperatures in K the cross sections are specified at, shape (m,).  May be None if the cross sections are
        not temperature dependent. Default None.

    Examples
    --------
    >>> from sasktran import UserDefinedAbsorption, MSIS90
    >>> wavelengths = [350, 800]
    >>> cross_sections = [1, 2]
    >>> opt_prop = UserDefinedAbsorption(wavelengths, cross_sections)
    >>> print(opt_prop)
    SasktranIF Optical Property: USERDEFINED_TABLES
    >>> atmospheric_state = MSIS90()
    >>> opt_prop.calculate_cross_sections(atmospheric_state, latitude=0, longitude=0, altitude=20000, mjd=54372,\
                                          wavelengths=[350, 600])
    CrossSections(wavelengths=array([350, 600]), absorption=array([ 1.        ,  1.55555556]), scattering=array([ 0.,  0.]), total=array([ 1.        ,  1.55555556]))


    A user defined absorption optical property that is a function of temperature, pressure, wavelength, and VMR
    of a secondary species that broadens the profile.

    Parameters
    ----------
    broadener_vmr: np.array
        VMR of the broadening species.  Must be in ascending order. Shape (Nv)
    pressures: np.array
        Pressure in Pa, must be in ascending order. Shape (Np)
    temperatures: np.array
        Temperatures in K at each pressure level.  Must be in ascending order. Shape (Np, Nt)
    wavel_nm: np.array
        Wavelengths in nm.  Must be in ascending order. Shape (Nw)
    xs: np.ndarray
        Cross sections in cm^2.  Shape (Nv, Np, Nt, Nw)
    broadener_clim: Optional[Climatology]
        A climatology for the broadening species, can be None if there is no broadener. Default None.
    broadener_handle: Optional[str]
        The climatology handle for the broadener. Can be None if there is no broadener. Default None.


r"""
    A user defined optical property for a scattering species that has constant optical properties in location space.

    Currently this optical property only supports specifying the phase function in terms of the Legendre expansion,
    this means that it is only useful for the SASKTRAN-DO engine.

    The Legendre expansion is defined in terms of "greek" coefficients, where the phase matrix has the standard form

    .. math::

        P(\Theta) = \begin{pmatrix}
                      a1(\Theta) & b1(\Theta) & 0 & 0 \\
                      b1(\Theta) & a2(\Theta) & 0 & 0 \\
                      0 & 0 & a3(\Theta) & b2(\Theta) \\
                      0 & 0 & -b2(\Theta) & a4(\Theta)
                    \end{pmatrix}

    The phase function is then expanded in terms of Wigner functions (generalized spherical functions related to
    Legendre polynomials/associated Legendre functions)

    .. math::

        a_1(\Theta) = \sum_l \alpha_{1,l} \, d^{l}_{00}(\Theta)

        a_2(\Theta) + a_3(\Theta) = \sum_l (\alpha_{2,l} + \alpha_{3,l}) \, d^{l}_{2,2}(\Theta)

        a_2(\Theta) - a_3(\Theta) = \sum_l (\alpha_{2,l} - \alpha_{3,l}) \, d^{l}_{2,-2}(\Theta)

        a_4(\Theta) = \sum_l \alpha_{4,l} \, d^{l}_{00}(\Theta)

        b_1(\Theta) = \sum_l \beta_{1,l} \, d^{l}_{02}(\Theta)

        b_2(\Theta) = -\sum_l \beta_{2,l} \, d^{l}_{02}(\Theta)

    Parameters
    ----------
    wavelengths : np.array
        Array of wavelengths in nm.  Shape (n,)
    xs_scat : np.array
        Scattering cross section in cm2 at wavelengths. Shape (n,)
    xs_abs : np.array
        Absorption cross section in cm2 at wavelengths. Shape (n,)
    lm_a1: np.ndarray
        Legendre coefficients :math:`\alpha_{1,l}`.  Shape (n, m).
    lm_a2: np.ndarray
        Legendre coefficients :math:`\alpha_{2,l}`.  Shape (n, m).
    lm_a3: np.ndarray
        Legendre coefficients :math:`\alpha_{3,l}`.  Shape (n, m).
    lm_a4: np.ndarray
        Legendre coefficients :math:`\alpha_{4,l}`.  Shape (n, m).
    lm_b1: np.ndarray
        Legendre coefficients :math:`\beta_{1,l}`.  Shape (n, m).
    lm_b2: np.ndarray
        Legendre coefficients :math:`\beta_{2,l}`.  Shape (n, m).
    delta_m_order: int
        Delta-m order to truncaate the optical property by.  None implies no scaling


    Generic optical property container that is applicable to a wide variety of optical properties.

    Parameters
    ----------
    name : str
        Name of the optical property to create
    psf_wavelength : np.ndarray
        array of wavelengths in nanometers where the point spread function is defined.
    psf : np.ndarray
        array of point spread function values in nanometers. Same length as psf_wavelength
    wavel_spacing : scalar, optional
        Spacing to calculate the high resolution wavelengths on in nanometers, default is 0.01nm
    num_stdev : scalar, optional
        Number of standard deviations to use in the gaussian for convolution, default is 4
    output_spacing : scalar, string optional
        Spacing to calculate the convolved cross section on. Either a scalar to use a fixed spacing, or None to use
        the wavelengths of the point spread function (wavel parameter). Default is none.

    Examples
    --------
    >>> from sasktran import OpticalPropertyConvolved, MSIS90, O3DBM
    >>> import numpy as np
    >>> convolved_dbm = OpticalPropertyConvolved(O3DBM(), psf_wavelength=np.linspace(250, 1000, 50),\
                                                 psf=np.linspace(0.5, 10, 50))
    >>> print(convolved_dbm)
    SasktranIF Optical Property: O3_DBM_Convolved
    >>> convolved_dbm.calculate_cross_sections(MSIS90(), latitude=0, longitude=0, altitude=20000, mjd=54372,\
                                               wavelengths=[350, 600])
    CrossSections(wavelengths=array([350, 600]), absorption=array([  2.83084701e-22,   5.01481379e-21]), scattering=array([ 0.,  0.]), total=array([  2.83084701e-22,   5.01481379e-21]))


    Specialized OpticalProperty which supports Mie Aerosol calculations.

    Parameters
    ----------
    particlesize_climatology : sasktran.Climatology

    species : str
        Molecule to use, one of ['H2SO4', 'ICE', 'WATER']


    Calculates the optical absorption and extinction of various atmospheric molecules using the Voigt line-shape and
    the HITRAN spectral line database. The object supports all of the HITRAN species specified in the HITRAN database
    file molparam.txt.  This optical property requires additional configuration, see :ref:`configuration` for more information.

    Parameters
    ----------
    chemical_name : str
        Chemical abbreviation of the molecule of interest.
    isotope_filter : int, optional
        Allows the HITRAN object to load in just one isotope of the requested molecule. The value set must match one of
        the isotope labels used for the given molecule in the HITRAN database file, molparam.txt. Note that the code
        does not adjust the line strength but uses the line strength value as written in the HITRAN database. This
        means you may have to account for and/or remove the abundance automatically built into the HITRAN database line
        strength values.
    line_tolerance : float, optional
        Allows the user to set the tolerance used to reject weak lines from the current micro-window as part of a speed optimization strategy. The default value
        is 0.0 which disables the optimization. Larger values speed up calculation of spectra but may result in choppy spectra at the smaller
        intensities, especially in extinction/absorption spectra which typically follow the log of the cross-section. A
        smaller value will reduce choppiness but increase computational speed. Only values greater
        than or equal to zero are acceptable. A value of 1.0E-09 is a good starting value for users wishing to investigate
        the effectiveness of the speed optimization.
    max_line_strength : float, optional
        Allows the user to manually set the maximum line strength within a micro-window. By default the object will take this value
        from the strongest line in the micro-window. The value is used with the line tolerance to reject weak lines from
        spectral calculations. A value of zero will disable the manual setting and reinstate usage of the default.
    use_cache: bool, optional
        If true then cross sections will be cached when used internally inside a radiative transfer calculation.
        The default is True, this should be left as True unless trying to calculate millions of wavelengths where
        memory starts to become an issue.


    These are the cross-sections used in the OSIRIS level 2 analysis for Saskmart V5.07. This table is based upon
    the cross-sections of Bogumil Orphal and Burrows and has been reduced to the nominal resolution of the OSIRIS
    instrument (approx. 1.0 nm). The cross-sections range from 270nm to 820 nm in 0.1 nm steps.


    Calculates the absorption cross section of NO2 molecules from 230 nm to 795 nm and 221K to 293K. The cross-sections
    have been reduced to the resolution of OSIRIS and these cross-sections have been used in the OSIRIS level 2 MART
    retrievals.


    An optical property object that provides Rayleigh molecular scattering in dry-air. The code closely follows the
    algorithm published by Bates 1984 and exactly replicates his cross-section calculations to the 4 significant digits
    in his Table 1. The cross-section is weighted to account for the different gas ratios in standard atmospheric
    composition. No attempt is made to track changes in CO2 composition. Note that water vapour effects are implicitly
    ignored as it only considers dry air. The only difference with Bates is that this object takes into account the
    tiny fraction of gas that is not N2, O2, Argon or CO2. Bates ignores this component while this object assumes it
    the residual gas with properties similar to Argon.


    An optical property object that provides Rayleigh molecular scattering without any corrections.
    This is very similar to the :py:class:`sasktran.Rayleigh` optical property except that the Bates correction factor
    is not included. For most calculations it is preferred to use the :py:class:`sasktran.Rayleigh` optical property
    instead.


    An optical property class that provides Rayleigh molecular scattering in dry-air, with the elastic Cabannes line
    separated from the inelastic Raman wings. Extinction is exactly the same as the Rayleigh class, but the scattering
    cross section now represents the Cabannes line only, which is a few percent smaller than the full Rayleigh scatter.
    The Raman lines are currently inaccessible through the python interface.


    Scattering non-spherical ice crystals based upon the database from Baum.

    Parameters
    ----------
    effective_size_microns : float
        Size of the particles, typically from 10 microns to 50 microns
    use_delta_eddington : bool, optional
        True if the delta eddington approximation is to be used.  Should be True for use
        in either the HR or DO engines. Default: True.


    Calculates the absorption cross section of SO2 molecules from 227 nm to 420 nm at 298 K, 318 K, 338 K, and 358 K
    following [1] and [2].

    .. [1] C. Hermans, A.C. Vandaele, and S. Fally. "Fourier transform measurements of SO2 absorption cross sections:
           I. Temperature dependence in the 24000-29000 cm-1 (345-420 nm) region," J. Quant. Spectrosc. Radiat.
           Transfer 110, 756-765 (2009); DOI: 10.1016/j.jqsrt.2009.01.031
    .. [2] A.C. Vandaele, C. Hermans, and S. Fally, "Fourier transform measurements of SO2 absorption cross sections:
           II. Temperature dependence in the 29000-44000 cm-1 (227-345 nm) region," J. Quant. Spectrosc. Radiat.
           Transfer 110, 2115-2126 (2009); DOI: 10.1016/j.jqsrt.2009.05.006


    The O2-O2 collision induced absorption cross-sections distributed in Hitran 2016 as described by Karman et al. 2019.
    It is composed of 8 spectral regions measured by several researchers that extend from 335nm to over 8 microns.
    Some of the regions are temperature dependent and others are not


    Class which handles the representation of the Earth geometry.  The object is initialized through one of the
    `from` methods, and then after initialization various parameters are available.

    Parameters
    ----------
    geoid_name: str, optional

    Examples
    --------
    >>> import sasktran as sk
    >>> geodetic = sk.Geodetic()
    >>> geodetic.from_lat_lon_alt(latitude=60, longitude=30, altitude=0)
    >>> print(geodetic)
    ISKGeodetic: IAU 1976
     Latitude: 60.0, Longitude: 30.0, Altitude: 0.0
    >>> print(geodetic.local_up)
    [ 0.4330127  0.25       0.8660254]
    >>> print(geodetic.location)
    [ 2768775.09176071  1598553.04455358  5500479.72571591]


Base class for objects which can perform radative transfer calculations.


    Examples
    --------
    >>> import sasktran as sk
    >>> # configure your geometry
    >>> geometry = sk.VerticalImage()
    >>> geometry.from_sza_saa(60, 60, 0, 0, [10, 20, 30, 40], 54372, 0)
    >>> # configure your atmosphere
    >>> atmosphere = sk.Atmosphere()
    >>> atmosphere['rayleigh'] = sk.Species(sk.Rayleigh(), sk.MSIS90())
    >>> atmosphere.atmospheric_state = sk.MSIS90()
    >>> atmosphere['o3'] = sk.Species(sk.O3DBM(), sk.Labow())
    >>> atmosphere['no2'] = sk.Species(sk.NO2Vandaele1998(), sk.Pratmo())
    >>> atmosphere.brdf = sk.Lambertian(0.3)
    >>> # make your engine
    >>> engine = sk.EngineHR(geometry=geometry, atmosphere=atmosphere, wavelengths=[350, 400, 450])
    >>> rad = engine.calculate_radiance()
    >>> print(rad) #doctest: +ELLIPSIS
    [[ 0.11674...  0.10872...  0.04904...  0.01456...]
     [ 0.10837...  0.08537...  0.02897...  0.00804...]
     [ 0.10318...  0.06396...  0.01829...  0.00483...]]


    Examples
    --------
    >>> import sasktran as sk
    >>> # configure your geometry
    >>> geometry = sk.VerticalImage()
    >>> geometry.from_sza_saa(60, 60, 0, 0, [10, 20, 30, 40], 54372, 0)
    >>> # configure your atmosphere
    >>> atmosphere = sk.Atmosphere()
    >>> atmosphere['rayleigh'] = sk.Species(sk.Rayleigh(), sk.MSIS90())
    >>> atmosphere.atmospheric_state = sk.MSIS90()
    >>> atmosphere['o3'] = sk.Species(sk.O3DBM(), sk.Labow())
    >>> atmosphere['no2'] = sk.Species(sk.NO2Vandaele1998(), sk.Pratmo())
    >>> atmosphere.brdf = sk.Lambertian(0.3)
    >>> # configure your options
    >>> options = {'setmcdebugmode': 1234, 'setnumphotonsperlos': 500, 'setsolartabletype': 0}
    >>> # make your engine
    >>> engine = sk.EngineMC(geometry=geometry, atmosphere=atmosphere, wavelengths=[350, 400, 450], options=options)
    >>> rad = engine.calculate_radiance()
    >>> print(rad) #doctest: +ELLIPSIS
    [[ 0.11689...  0.10768...  0.04666...  0.01477...]
     [ 0.10643...  0.08429...  0.02972...  0.00802...]
     [ 0.10589...  0.06424...  0.01797...  0.00499...]]


    Calculates the optical absorption and extinction of various atmospheric molecules using the Voigt line-shape and
    the HITRAN spectral line database. The object supports all of the HITRAN species specified in the HITRAN database
    file molparam.txt.

    This is a modified version of the HITRANChemical class for use with the TIR engine. The use_cache property is
    forced to False because the TIR engine uses the internal array-based cross section calculation. This property
    may cause issues if set to True, particularly when calculate_radiance is more than once on the same engine object.

    Examples
    --------
    >>> from sasktran.tir.opticalproperty import HITRANChemicalTIR
    >>> from sasktran import MSIS90
    >>> hitran_co2 = HITRANChemicalTIR('CO2', micro_window_margin=50)
    >>> atmospheric_state = MSIS90()
    >>> hitran_co2.calculate_cross_sections(atmospheric_state, latitude=0, longitude=0, altitude=20000, mjd=54372,\
                                            wavelengths=[10000, 10001])
    CrossSections(wavelengths=array([10000, 10001]), absorption=array([8.23591895e-30, 2.03913420e-28]), scattering=array([0., 0.]), total=array([8.23591895e-30, 2.03913420e-28]))

    Parameters
    ----------
    chemical_name : str
        Chemical abbreviation of the molecule of interest.
    isotope_filter : int, optional
        Allows the HITRAN object to load in just one isotope of the requested molecule. The value set must match one of
        the isotope labels used for the given molecule in the HITRAN database file, molparam.txt. Note that the code
        does not adjust the line strength but uses the line strength value as written in the HITRAN database. This
        means you may have to account for and/or remove the abundance automatically built into the HITRAN database line
        strength values.
    line_tolerance : float, optional
        Allows the user to set the tolerance used to reject weak lines from the current micro-window. The default value
        is 1.0E-09. A larger value will speed up calculation of spectra but may result in choppy spectra at the smaller
        intensities, especially in extinction/absorption spectra which typically follow the log of the cross-section. A
        smaller value will reduce choppiness but increase computational speed. A similar result can be achieved by
        changing property max_line_strength; the choice is really down to the users preference. Only values greater
        than zero are acceptable.
    max_line_strength : float, optional
        Allows the user to manually set the maximum line strength within a micro-window. By default the object will use
        the strongest line in the micro-window. The value is used with the line tolerance to reject weak lines from
        spectral calculations. Reducing the value of the maximum line strength can reduce choppiness in the spectra.
        Its use is similar to property line_tolerance. Only values greater than zero are acceptable. A negative value
        is an error. A value of zero will disable the manual setting and reinstate usage of the default.
    micro_window_margin: double, optional
        Sets the margin of the micro-window in wavenumbers. This margin extends the upper and lower bounds of the
        micro-window once it is loaded into memory. The margin value is used to ensure that cross-section calculations
        near the edge of the micro-window are accurate and have contributions from lines outside the micro-window. The
        default value is 10 wavenumbers. It is the users responsibility to choose a value for the margin that provides
        the necessary accuracy for their application.


    Creates a custom climatology, containing temperature and pressure, for use as an atmospheric state.
    The profiles are from http://eodg.atm.ox.ac.uk/RFM/atm/ and are divided into three folders:
    'FASCODE/ICRCCM Model Atmospheres', 'MIPAS Model Atmospheres (1998)', and 'MIPAS Model Atmospheres (2001)'.
    Each folder contains several '.atm' files, where each file contains a set of profiles describing various
    latitudinal regions and times.

    NOTE: These are one-dimensional atmospheric profiles. Latitude, longitude, and mjd will not affect the returned values.


    Create a custom climatology using a species profile from data files. By default uses the US Standard Atmosphere.
    The CLIMATOLOGY_HANDLE of the species will be SKCLIMATOLOGY_<species>_CM3. Note that despite the files
    providing profiles of species in PPMV (parts per million by volume) the climatology created will be a profile
    with units of molecules per cm^3. This is because the VMR climatologies are currently not supported in the
    SASKTRAN cross section calculation. The species PPMV is converted to using the temperature, :math:`T`, and
    pressure, :math:`p`, in the specified '.atm' file and using the Ideal Gas Law to compute a neutral density,
    :math:`n`:

    .. math::

        n = p / (k T)

    where k is the Boltzmann constant.  The species VMR is multiplied by the neutral density to obtain the species
    density.

    NOTE: These are one-dimensional atmospheric profiles. Latitude, longitude, and MJD will not affect the returned
    values.

    Parameters
    ----------
    species : str
        Chemical formula of the species to use. E.g. 'O3', 'CO2', etc.
    dataset : str, optional
        Name of dataset to use. Valid choices are 'fascode', 'mipas_1998', and 'mipas_2001'. Default is 'fascode'.
    climatology : str, optional
        Name of the data file within the chosen dataset to load. Default is 'std'.
    interp : str, optional
        One of 'linear' or 'log'. Defines the interpolation space. Default is 'linear'.
    spline: bool, optional
        One of True or False.  If True then a quadratic spline will be used to interpolate, if False the piecewise
        linear interpolation is done.  Default is False.

    Examples
    --------
    >>> from sasktran.tir.climatology import ClimatologySpecies
    >>> mipas_2001_polar_summer_co2 = ClimatologySpecies('CO2', dataset='mipas_2001', climatology='sum')
    >>> mipas_2001_polar_summer_co2.get_parameter('SKCLIMATOLOGY_CO2_CM3', latitude=0, longitude=0, altitudes=[10000, 11000], mjd=54372)
    array([2.94644516e+15, 2.50292282e+15])

    The following table lists the species supported by each climatology.
    Each dataset contains several different climatologies. Each climatology contains unique profiles of temperature,
    pressure, and VMRs of the major species. The VMRs for the minor species are the same for all climatologies in each
    dataset. However, the number density of each species is computed using the pressure and temperature of the selected
    climatology.

    +------------+---------------------------------+---------------------------------+---------------------------------+
    | Dataset    | Climatologies                   | Major Species                   | Minor Species                   |
    +============+=================================+=================================+=================================+
    | fascode    | tro (Tropical),                 | H2O, CO2, O3, N2O, CO, CH4, O2  | NO, SO2, NO2, NH3, HNO3, OH,    |
    |            | mls (Mid-Latitude Summer),      |                                 | HF, HCl, HBr, HI, ClO, OCS,     |
    |            | mlw (Mid-Latitude Winter),      |                                 | H2CO, HOCl, N2, HCN, CH3Cl,     |
    |            | sas (Sub-Arctic Summer),        |                                 | H2O2, C2H2, C2H6, PH3, COF2,    |
    |            | saw (Sub-Arctic Winter),        |                                 | SF6, H2S, CFCl3, CF2Cl2, CClF3, |
    |            | std (US Standard Atmosphere),   |                                 | CF4, CHCl2F, CHClF2, C2Cl3F3,   |
    |            |                                 |                                 | C2Cl2F4, C2ClF5, CCl4, ClONO2,  |
    |            |                                 |                                 | N2O5, HNO4                      |
    +------------+---------------------------------+---------------------------------+---------------------------------+
    | mipas_1998 | day_imk (Mid-Latitude Day)      | N2, O2, O3P, CO2, O3, H2O, CH4, | HCN, H2O2, F12, F14, F22, COF2, |
    |            | ngt_imk (Mid-Latitude Night)    | N2O, HNO3, CO, NO2, N2O5, ClO,  | OCS, NH3, SO2, CFCl3, C2H2,     |
    |            | win_imk (Polar Winter)          | HOCl, ClONO2, NO                | C2H6, CCl4, SF6, HNO4, CH3Cl,   |
    |            | sum_imk (Polar Summer)          |                                 | CClF3, CHCl2F, C2Cl3F3, C2Cl2F4 |
    +------------+---------------------------------+---------------------------------+---------------------------------+
    | mipas_2001 | day (Mid-Latitude Day)          | N2, O2, CO2, O3, H2O, CH4, N2O, | CClF3, CHCl2F, C2Cl3F3,         |
    |            | ngt (Mid-Latitude Night)        | HNO3, CO, NO2, N2O5, ClO, HOCl, | C2Cl2F4, C2ClF5, CH3Cl, H2S     |
    |            | win (Polar Winter)              | ClONO2, NO, HNO4, HCN, NH3,     |                                 |
    |            | sum (Polar Summer)              | F11, F12, F14, F22, CCl4, COF2, |                                 |
    |            | equ (Equatorial Day)            | H2O2, C2H2, C2H6, OCS, SO2, SF6 |                                 |
    +------------+---------------------------------+---------------------------------+---------------------------------+


    Similar to ClimatologySpecies but the pressure and temperature are given by a user specified climatology.
    Designed to be used when the atmospheric state of an engine is set to some climatology such as MSIS90 or ECMWF,
    so that the conversion from VMR to number density uses the same pressure and temperature.

    The climatology created by this class is still a 1D vertical profile, so the latitude, longitude, and MJD must be
    passed as inputs along with the climatology containing the desired pressure and temperature.

    Examples
    --------
    Use MSIS90 pressure and temperature at 0 latitude, 0 longitude to convert the CH4 VMR given by the US Standard
    atmosphere to number density.

    >>> from sasktran.tir.climatology import ClimatologySpeciesCustomPT
    >>> from sasktran import MSIS90
    >>> clim = ClimatologySpeciesCustomPT('CH4', MSIS90(), latitude=0, longitude=0, mjd=54372)
    >>> clim.get_parameter('SKCLIMATOLOGY_CH4_CM3', latitude=0, longitude=0, altitudes=[10000, 11000], mjd=54372)
    array([1.45841301e+13, 1.30346981e+13])

    Parameters
    ----------
    species : str
        Chemical formula of the species to use. E.g. 'O3', 'CO2', etc.
    atmospheric_state : sk.Climatology
        Climatology which must define pressure and temperature.
    latitude :float
        Latitude where the pressure and temperature profile will be taken from.
    longitude : float
        Longitude where the pressure and temperature profile will be taken from.
    mjd : float
        Time where the pressure and temperature profile will be taken from.
    dataset : str, optional
        Name of dataset to use. Valid choices are 'fascode', 'mipas_1998', and 'mipas_2001'. Default is 'fascode'.
    climatology : str, optional
        Name of the data file within the chosen dataset to load. Default is 'std'.
    interp : str, optional
        One of 'linear' or 'log'. Defines the interpolation space. Default is 'linear'.
    spline: bool, optional
        One of True or False.  If True then a quadratic spline will be used to interpolate, if False the piecewise
        linear interpolation is done.  Default is False.


    Creates a custom climatology from data files. Similar to ``ClimatologySpecies`` and ``ClimatologyAtmosphericState``
    but includes ALL profiles defined in the data file. See ``ClimatologySpecies`` for the species contained in each
    file.
    If minor species are included, they are interpolated to the same altitude grid as the major species.

    Examples
    --------
    >>> from sasktran_tir.climatology import ClimatologyFull
    >>> clim = ClimatologyFull()  # US Standard Atmosphere
    >>> clim.get_parameter('SKCLIMATOLOGY_TEMPERATURE_K', latitude=0, longitude=0, altitudes=[10000, 11000], mjd=54372)
    array([223.3, 216.8])
    >>> clim.get_parameter('SKCLIMATOLOGY_PRESSURE_PA', latitude=0, longitude=0, altitudes=[10000, 11000], mjd=54372)
    array([26500., 22700.])
    >>> clim.get_parameter('SKCLIMATOLOGY_O3_CM3', latitude=0, longitude=0, altitudes=[10000, 11000], mjd=54372)
    array([1.12859623e+12, 1.62974521e+12])

    Parameters
    ----------
    dataset : str, optional
        Name of dataset to use. Valid choices are 'fascode', 'mipas_1998', and 'mipas_2001'. Default is 'fascode'.
    climatology : str, optional
        Name of the data file within the chosen dataset to load. Default is 'std'.
    include_minor : bool, optional
        If True, minor species are included in the climatology. Default is True.
    interp : str, optional
        One of 'linear' or 'log'. Defines the interpolation space. Default is 'linear'.
    spline: bool, optional
        One of True or False.  If True then a quadratic spline will be used to interpolate, if False the piecewise
        linear interpolation is done.  Default is False.


Internal C structure representing the lowlevel C atmosphere object


    Test bench to run sanity checks on SASKTRAN-DISCO. Note that the primary function of these tests if verifying the
    SKIF interface as well as the internal interface between the SASKTRAN framework and the DISCO kernel (eg. making
    sure species are being parameterized properly for the DISCO kernel). This test bench also is used to track
    continuity between releases (eg. track that radiances between releases are the same).

    The primary test bench for DISCO is the ``sasktran_disco_engine_tests`` which is a program that runs lots of edge-
    case tests, but only verifies that the DISCO kernel is operating properly.


overall object describes the geo-locations


Simple 5 parameter model


Contains data used by the driver.


    Contains the stateful variables in use by the `simulate` function.

    An instance of this class is yielded by `simulate` each time an event takes
    place.


A 1D, planar cartesian mesh with equal grid spacing.


    A 1D or 2D mesh with logarithmic radial binning and homologous expansion.

    The comoving coordinates are time-independent; proper distances are
    affected by the scale factor derivative.


    A 2D mesh with rectangular binning.

    The length of the domain is related to height by an aspect ratio. The
    minimum demarcations along an axis is also variable allowing for
    non-square meshing if need be.


    Rule for how an event can recur throughout a simulation.

    The available rules are:

    - linear: at evenly spaced time intervals
    - log: at multiplicative time intervals; `next = time * (1 + delta)`


    State of a recurring event that occurs during a simulation.

    Events signal side effects to a loop which monitors simulation progress,
    include writing checkpoints, collecting time series data, and other
    post-processing tasks.


    A model parameter: enables runtime configuring of a setup.

    `Parameter` instances are assigned as class variables to `Setup`
    sub-classes. The `mutable` flag should be set to to `True` if the
    parameter is logically adjustable either during the run, or can be
    superseded when a run is restarted.


    Abstract base class to describe a simulation model setup.

    Subclasses are used by the driver to define initial and boundary
    conditions, select a hydrodynamics solver and parameters, and describe
    physics conditions such as gravity and thermodynamics. Basic setups only
    need to implement a subset of the possible methods; most of the methods
    below have stub default implementations.


Base class for solver implementations.


    A star exploding.

    The model is based on Duffell & MacFadyen (2015). Code was adapted from
    Marcus Dupont's setup in Simbi.


Tests the srhd_2d solver geometrical source terms.


    A cylindrical explosion in 2D planar geometry; isothermal or gamma-law.

    This problem is useful for testing bare-bones setups with minimal physics.
    A circular region of high density and pressure is initiated at the center
    of a square domain. In isothermal mode, the sound speed is set to 1
    everywhere. In gamma-law mode, the adiabatic index is 5/3.

    Currently this setup can specify either the `cbdiso_2d` or `cbdgam_2d`
    solvers.


A relativistic shell or jet launched into a homologous, relativistic envelope.


r"""
    A circumbinary disk setup for binary problems, isothermal or gamma-law.

    This problem is the primary science setup for the cbdiso_2d and cbdgam_2d
    solvers. The initial data consists of nearly stationary axisymmetric
    solutions, which partly means that Keplerian velocity profiles are imposed
    with a softened radial coordinate.

    In isothermal mode, since only constant-nu viscosity is supported, the
    disk is constant density = 1.0. Note that the overall scale of the density
    is not physically meaningful for the isothermal hydro equations, since
    those equations are invariant under uniform rescalings of the density.

    In gamma-law mode, since only constant-alpha viscosity is supported, the
    disk is nominally `Shakura & Sunyaev (1973)`_. However, we follow the
    purely Newtonian treatment given in `Goodman (2003)`_, setting radiation
    pressure to zero. In particular, we impose the following power law
    profiles on the surface density :math:`\Sigma` and vertically-integrated
    pressure :math:`\mathcal{P}`:

    .. math::
        \Sigma \propto r^{-3/5}, \, \mathcal{P} \propto r^{-3/2}

    .. _Shakura & Sunyaev (1973): https://ui.adsabs.harvard.edu/abs/1973A%26A....24..337S
    .. _Goodman (2003): https://ui.adsabs.harvard.edu/abs/2003MNRAS.339..937G


    Simulates Bondi-Hoyle-Lyttleton type accretion in a binary system.

    The gas is isothermal with uniform sound speed.


Scalar advection, evolution of a smooth wave, using the DG solver.


Burgers equation, evolution of a smooth wave using the DG solver.


    Discontinuous initial data, with uniform density and pressure to either
    side of the discontintuity at x=0.5.


    A sinusoidal variation of the gas density, with possible uniform
    translation. The gas pressure is uniform.


A cold, spherically symmetric relativistic wind.


A restricted container to hold n-tree nodes.


    Represents a node in a self-similar n-tree.

    Nodes are allowed to have a value, a list of children, both, or neither. The
    branching ratio (number of children), is uniform across the tree. Tree nodes
    are mutable. Nodes do not have a parent pointer, so in principle a node
    could be the child of multiple parent nodes. Traversal is done pre-order and
    with memory-efficient (stackful) generator routines. This makes it efficient
    to do map and zip operations on the values of distinct trees with the same
    topology. Trees can be reconstructed from their sequence representation.


    Buffers for the solution on a subset of the solution domain.

    This class also takes care of generating initial conditions if needed, and
    issuing calls to the solver kernel functions.


Adapter class to drive the srhd_1d C extension module.


    Gauss weights, quadrature points, and tabulated Legendre polonomials.

    This class works for n-th order Gaussian quadrature in 1D.


    An n-th order, discontinuous Galerkin solver for 1D scalar advection.

    Time-advance integrator options:

    - :code:`rk1`: Forward Euler
    - :code:`rk2`: SSP-RK2 of Shu & Osher (1988; Eq. 2.15)
    - :code:`rk3`: SSP-RK3 of Shu & Osher (1988; Eq. 2.18)
    - :code:`rk3-sr02`: four-stage 3rd Order SSP-4RK3 of Spiteri & Ruuth (2002)


Contains parameters which are solver specific options.


    Holds the array buffer state for the solution on a subset of the
    solution domain.


Adapter class to drive the cbdisodg_2d C extension module.


Adapter class to drive the cbdgam_2d C extension module.


Adapter class to drive the iso_2d C extension module.


The mass, 2D position, and 2D velocity of a point-like particle


The position, velocity, and orientation of a two-body orbit


The orbital elements of a two-body system on a bound orbit


time, mdot, ldot, mass_moment, eccentricity_vector


r"""
    Describes a gravitating point mass

    The point mass's mass, x-y position, and softening length fully determine
    the gravitational potential:

    .. math::
        \phi = \frac{G M}{\sqrt{\Delta r^2 + r_{\rm soft}^2}} \, .

    Here, :math:`\Delta r` is the distance from the particle to the field
    point, and :math:`r_{\rm soft}` is the :obj:`softening_length` parameter
    below. Note that the gravitational constant is assumed to be 1 by the
    physics solvers.

    Point masses can also act as sinks of mass and momentum. This behavior is
    controlled by the sink model, sink rate, and a sink radius. If the sink
    model has the value :obj:`SINK_MODEL_INACTIVE`, then that particle may
    still be a source of gravitational potential, but it does model accretion
    by subtracting mass or momentum. The acceleration-free and torque-free
    modes are described in literature here:

    - `Dittmann & Ryan (2021) <https://ui.adsabs.harvard.edu/abs/2021arXiv210205684D>`_
    - `Dempsey et. al (2020) <https://ui.adsabs.harvard.edu/abs/arXiv:2002.05164>`_

    The acceleration-free sink model is there for logical completeness, but it
    should not be used in practice.


    Physics configuration for the binary accretion solvers

    Configuration categories are:

    1. Equation of state: globally isothermal, locally isothermal, or
       gamma-law

       In globally isothermal mode, a uniform sound speed is specified. In
       globally isothermal mode, an orbital mach number is specified, and used
       to assign the sound speed based on the local gravitational potential
       (this option only makes sense when gravitating point masses are
       provided). In gamma-law mode, the sound speed is determined
       self-consistently from the internal energy. The cbdiso_2d solver only
       supports the former two modes, and the cbdgam_2d solver only upports
       the last mode.

    2. Gravitating point masses

       Point masses can be optionally provided to model stars or black holes.
       The point mass properties include a point mass's mass, and its x-y
       position. These properties fully determine the gravitational potential
       sourced by the particle. Point masses can also act as sinks of mass and
       momentum (see the :obj:`PointMass` struct above for details). The point
       masses are supplied to the solver implicitly through a callback
       function, mapping the simulation time to a sequence of particles.
       Currently, solvers support either zero, one, or two particles.

    3. Viscosity model

       Two different viscosity models are nominally supported: constant-nu,
       and constant alpha. Currently the isothermal solver only supports
       constant-nu viscosity.

    4. Thermal cooling

       Todo.

    5. An outer buffer zone

       For binary accretion problems in a square domain, it can be useful to
       impose a wave-damping zone (or sponge layer) to avoid artifacts fromt
       the outer boundary on the orbiting gas. The buffer zone, if enabled,
       will drive the solution outside a "buffer onset radius" toward an
       appropriate hydrodynamic state. This should be gas with uniform gas
       pressure and density (which may be sampled from the initial condition),
       and that gas should be on circular Keplerian orbits. The
       :obj:`buffer_driving_rate` parameter controls how fast the solution is
       driven toward the smooth one. The buffer is applied in a region
       between the domain radius (the half-width of a square domain), extending
       inwards by an amount specified by the :obj:`buffer_onset_width`
       parameter.


A kernel whose execution shape is specified and is ready to be invoked.


    An object that uses `__getitem__` syntax to return a
    :py:class:`KernelInvocation` instance.


Builds and maintains (in memory) a CPU or GPU dynamically compiled module.


Scraped from contextlib source in Python >= 3.7 for backwards compatibility.


        Fields
        ------

        cooling_rate:  the cooling rate
        optical_depth: the optical depth


        A circular explosion setup

        This model supports euler2d and iso2d systems. It sets up a circular
        region of high density and pressure where you can watch the explosion
        expand outward.

        Fields
        ------

        radius:           radius of the high-pressure region
        pressure_inside:  pressure of the high-pressure region
        gamma_law_index:  the gamma-law index of the gas
        physics:          more physics details


    Deals with the simulation control flow and input/output

    The simulation driver takes care of setting problem initial data,
    constructing solver instances, recording timeseries data (on-the-fly
    science products), and writing checkpoint files.

    Fields
    ------

    tstart: time when the simulation starts (does not need to be t=0)
    tfinal: time when the simulation ends
    report: number of iterations between one-line report messages
    timeseries: high-cadence recording of science products
    cfl_number: safety factor in the Courant-Friedrichs-Lewy condition
    new_timestep_cadence: iterations between recomputing the Courant-limited timestep


Adiabatic EOS with given gamma-law index


Isothermal EOS with a uniform sound speed


Isothermal EOS with a uniform nominal Mach number


    Physics equations to be solved

    Fields
    ------

    equation_of_state: thermodynamic EOS
    metric:            currently either newtonian or minkowski


    Algorithm parameters for the solution scheme

    A scheme refers to the parts of a solver that affect the numerical
    solution up to the truncation order.

    Fields
    ------

    reconstruction:    spatial reconstruction method
    time_integration:  time-integration scheme (fwd|rk1|rk2|rk3)


    Algorithm parameters only affecting performance

    With everything else held fixed, differences in the solver strategy should
    only affect how fast the code runs, and not the numerical solution it
    generates.

    Fields
    ------

    hardware:    compute device [cpu|gpu]
    data_layout: array-of-struct (fields-last) or struct-of-array (fields-first)
    cache_flux:  one Riemann problem per face, difference the resulting array
    cache_prim:  pre-compute primitive quantities, vs. re-compute over stencil
    num_patches: decompose domain to enable threads, streams, or multiple GPU's
    num_threads: use a thread pool of this size to drive a multi-patch solver
    gpu_streams: use the per-thread-default-stream, or one stream per grid patch


    Soft boundary condition, drives fields to some value in a defined region

    rate:   rate at which the solution is driven to the target value
    ramp:   distance over which the region
    where:  a string describing the zone in which the buffer is active


    Top-level application configuration struct

    Fields
    ------

    name:               a name given to the run
    driver:             runs the simulation main loop, handles IO
    physics:            the physics equations to be solved
    initial_data:       initial condition if not a restart (also called a model)
    boundary_condition: boundary condition to apply at domain edges
    domain:             the physical domain of the problem
    strategy:           the solution strategy; does not affect the numerical solution
    scheme:             algorithmic choices which can affect the solution accuracy
    forcing:            driving of the hydrodynamic fields to implement a soft BC


Classic Sod shocktube initial data


    Uniform initial data

    This model data could be useful for things like testing the correctness of
    boundary conditions or geometrical source terms, see example presets below.


    Cylindrical explosion initial data

    Initializes a circular region of high pressure and density. A shock wave
    and contact discontinuity should expand outward with a circular profile.


    A round cylinder immersed in a dilute wind

    A circular region of high density is immersed in a low-density ambient
    medium moving from left to right. After some time, the high-density region
    is disrupted by Kelvin-Helmholtz instabilities.


    1d Riemann problem (RAM problem 1; Sec 4.1)

    Adapted from "RAM: A Relativistic Adaptive Mesh Refinement Hydrodynamics
    Code" The Astrophysical Journal Supplement Series, Volume 164, Issue 1,
    pp. 255-279.


    1d Riemann problem (RAM problem 2; Sec 4.2)

    Adapted from "RAM: A Relativistic Adaptive Mesh Refinement Hydrodynamics
    Code" The Astrophysical Journal Supplement Series, Volume 164, Issue 1,
    pp. 255-279.


    1d Riemann problem (RAM problem 3; Sec 4.3)

    Adapted from "RAM: A Relativistic Adaptive Mesh Refinement Hydrodynamics
    Code" The Astrophysical Journal Supplement Series, Volume 164, Issue 1,
    pp. 255-279.


    1d Riemann problem (RAM problem 4; Sec 4.4)

    Status: Failing

    Adapted from "RAM: A Relativistic Adaptive Mesh Refinement Hydrodynamics
    Code" The Astrophysical Journal Supplement Series, Volume 164, Issue 1,
    pp. 255-279.


    1d shock heating Riemann problem (RAM problem 5; Sec 4.5)

    Status: Failing

    Adapted from "RAM: A Relativistic Adaptive Mesh Refinement Hydrodynamics
    Code" The Astrophysical Journal Supplement Series, Volume 164, Issue 1,
    pp. 255-279.


    1d Riemann problem with transverse velocity: (RAM Hard Test; sec 6.1)

    Status: Failing

    Adapted from "RAM: A Relativistic Adaptive Mesh Refinement Hydrodynamics
    Code" The Astrophysical Journal Supplement Series, Volume 164, Issue 1,
    pp. 255-279.


    Lax problem initial data

    Adapted from Example 3.3 from G. Fu and C.-W. Shu, "A new trouble-cell
    indicator for discontinuous Galerkin methods for hyperbolic conservation
    laws," Journal of Computational Physics, v347 (2017), pp.305-327.


    Lax problem: double rarefaction wave

    Adapted from Example 3.4 from G. Fu and C.-W. Shu, "A new trouble-cell
    indicator for discontinuous Galerkin methods for hyperbolic conservation
    laws," Journal of Computational Physics, v347 (2017), pp.305-327.


    LeBlanc problem

    Adapted from Example 3.5 from G. Fu and C.-W. Shu, "A new trouble-cell
    indicator for discontinuous Galerkin methods for hyperbolic conservation
    laws," Journal of Computational Physics, v347 (2017), pp.305-327.

    Note: use log-scaling to plot the mass density profile.


    Shu-Osher problem

    Adapted from Example 3.6 from G. Fu and C.-W. Shu, "A new trouble-cell
    indicator for discontinuous Galerkin methods for hyperbolic conservation
    laws," Journal of Computational Physics, v347 (2017), pp.305-327.


    Blast wave interaction

    Adapted from Example 3.7 from G. Fu and C.-W. Shu, "A new trouble-cell
    indicator for discontinuous Galerkin methods for hyperbolic conservation
    laws," Journal of Computational Physics, v347 (2017), pp.305-327.

    Notes:
    - this problem should be run with a reflecting BC at each end
    - use log-scaling to plot the mass density profile


Sinusoidal density wave translating rigidly


    Encapsulates creation and indexing of arrays representing fields data

    This class helps simplify boilerplate code arising from two issues:

    1. arrays generally have guard zones on their space axes with length > 1
    2. arrays generally have memory layouts different from their logical shape


    Domain with uniformly spaced grid cells

    A coordinate box is a (hyper-)rectangular region in a coordinate chart. It
    is agnostic to the geometry of the chart, e.g. it could be a box in x, y,
    z space or r-theta-phi space.

    Fields
    ------

    extent_i:  extent of the coordinate box on the i-axis
    extent_j:  extent of the coordinate box on the j-axis
    extent_k:  extent of the coordinate box on the k-axis
    num_zones: number of zones on each axis


    Spherical-polar curvilinear coordinates

    Geometry formulas are provided for:

    - 1d in the spherical radius (r) (num_zones = [ni, 1, 1])
    - 2d in the r-q (meridional) plane (num_zones = [ni, nj, 1])
    - 3d in r-q-f (num_zones = [ni, nj, nk])


    Cylindrical-polar curvilinear coordinates

    Geometry formulas are provided for:

    - 1d in the cylindrical radius (r) (num_zones = [ni, 1, 1])
    - 2d in the r-z plane (num_zones = [ni, 1, nk])
    - 3d in r-f-z (num_zones = [ni, nj, nk])

    Support for 2d cylindrical coordinates in the r-f plane may be added in
    the future.


Handles batch-generation of PLM gradient estimation


    Handles conversion between primitive and conserved hydrodynamic fields

    This kernel class provides cons_to_prim_array and prim_to_cons_array
    functions which can operate either in fields-last or fields-first
    (struct-of-arrays, or transposed) data layout. These functions treat the
    input and output arrays as flattened, so they work for any domain
    dimensionality. The dim parameter is used to infer the number of fields.


Handles the calculation of geometric and driving source terms


Godunov scheme using method-of-lines and many strategy modes


Message class; yielded by patch solvers te request boundary data


    A no-overhead alternative to a thread pool

    This class should be used as the context manager rather than ThreadPool if
    num_threads=1.


Collection of kernel functions used by the solver


    Represents a kernel function that failed to be created for some reason.

    Instances of this class are used when the failure should be silently
    forgiven unless calling code tries to invoke the function that could not
    be compiled. The main use case is where either cffi or cupy is not
    installed, so compilation of the respective CPU or GPU extension was not
    possible. This should not trigger an error unless code tries to invoke the
    kernel in the mode that was unavailable.


A CPU or GPU extension module that could not be created for some reason.


Represents a maybe-compiled extension function (internal use)


This class uses the gumbel-softmax trick to reparameterize the Bernoulli variable.


This class randomly samples from the Bernoulli latent space.


This class creates the encoder model with CNN architecture.


This class creates the decoder model with CNN architecture.


This class creates the VAE model with CNN architecture.


This class creates the dataset for the convolutional model (3D data)


This class implements an RBM prior in the latent space (version: 'bipartite latent-space RBM').


This class implements an RBM prior in the latent space (version: 'RBM with augmented positive phase').


This class reparameterizes the Gaussian variable.


This class randomly samples from the Beranoulli latent space.


The abstract base "term" that is the superclass of all other terms

    Subclasses should overload at least the :func:`Term.get_coefficients`
    method.


A sum of multiple :class:`Term` objects

    The resulting kernel function is the sum of the functions and the width of
    the resulting low-rank representation will be the sum of widths for each
    of the terms.

    Args:
        *terms: Any number of :class:`Term` subclasses to add together.


A product of two :class:`Term` objects

    The resulting kernel function is the product of the two functions and the
    resulting low-rank representation will in general be wider than the sum of
    the two widths.

    Args:
        term1 (Term): The left term.
        term2 (Term): The right term.


Take the first derivative of a term with respect to the lag

    Args:
        term (Term): The term to differentiate.


A term corresponding to the integral of another term over a boxcar

    The process produced by this term is equivalent to the process produced by
    convolving the base process with a boxcar of length ``delta``. This can be
    useful, for example, when taking exposure time integration into account.

    Args:
        term (Term): The term describing the base process.
        delta (float): The width of the boxcar filter (for example, the
            exposure time).


r"""The simplest celerite term

    .. warning:: You should only use this term if you really know what you're
        doing because it will generally behave poorly. Check out
        :class:`SHOTerm` instead!

    This term has the form

    .. math::

        k(\tau) = a_j\,e^{-c_j\,\tau}

    with the parameters ``a`` and ``c``.

    Strictly speaking, for a sum of terms, the parameter ``a`` could be
    allowed to go negative but since it is somewhat subtle to ensure positive
    definiteness, we recommend keeping both parameters strictly positive.
    Advanced users can build a custom term that has negative coefficients but
    care should be taken to ensure positivity.

    Args:
        a: The amplitude of the term.
        c: The exponent of the term.


r"""A general celerite term

    .. warning:: You should only use this term if you really know what you're
        doing because it will generally behave poorly. Check out
        :class:`SHOTerm` instead!

    This term has the form

    .. math::

        k(\tau) = \frac{1}{2}\,\left[(a_j + b_j)\,e^{-(c_j+d_j)\,\tau}
         + (a_j - b_j)\,e^{-(c_j-d_j)\,\tau}\right]

    with the parameters ``a``, ``b``, ``c``, and ``d``.

    This term will only correspond to a positive definite kernel (on its own)
    if :math:`a_j\,c_j \ge b_j\,d_j`.

    Args:
        a: The real part of amplitude.
        b: The imaginary part of amplitude.
        c: The real part of the exponent.
        d: The imaginary part of exponent.


r"""A term representing a stochastically-driven, damped harmonic oscillator

    The PSD of this term is

    .. math::

        S(\omega) = \sqrt{\frac{2}{\pi}} \frac{S_0\,\omega_0^4}
        {(\omega^2-{\omega_0}^2)^2 + {\omega_0}^2\,\omega^2/Q^2}

    with the parameters ``S0``, ``Q``, and ``w0``.
    Consult the `Celerite paper <https://doi.org/10.3847/1538-3881/aa9332>`_ or
    `its preprint on arXiv <https://arxiv.org/abs/1703.09710>`_ for a more
    detailed mathematical description.

    This implementation also supports the following reparameterizations that
    can be easier to use and reason about:

    1. ``rho``, the undamped period of the oscillator, defined as :math:`\rho
       = 2\,\pi / \omega_0`,
    2. ``tau``, the damping timescale of the process, defined as :math:`\tau =
       2\,Q / \omega_0`, and
    3. ``sigma``, the standard deviation of the process, defined as
       :math:`\sigma = \sqrt{S_0\,\omega_0\,Q}`.

    Args:
        w0: The undamped angular frequency, :math:`\omega_0` above.
        rho: Alternative parameterization for ``w0`` as described above.
        Q: The quality factor, :math:`Q` above.
        tau: Alternative parameterization for ``Q`` as described above.
        S0: :math:`S_0` in the PSD above. The power at :math:`\omega = 0` is
            :math:`S_0\sqrt{2/\pi}`.
        sigma: Alternative parameterization for ``S0`` as described above.
        eps (optional): A regularization parameter used for numerical stability
            when computing :math:`\sqrt{1-4\,Q^2}` or :math:`\sqrt{4\,Q^2-1}`.


r"""A term that approximates a Matern-3/2 function

    This term is defined as

    .. math::

        k(\tau) = \sigma^2\,\left[
            \left(1+1/\epsilon\right)\,e^{-(1-\epsilon)\sqrt{3}\,\tau/\rho}
            \left(1-1/\epsilon\right)\,e^{-(1+\epsilon)\sqrt{3}\,\tau/\rho}
        \right]

    with the parameters ``sigma`` and ``rho``. The parameter ``eps``
    controls the quality of the approximation since, in the limit
    :math:`\epsilon \to 0` this becomes the Matern-3/2 function

    .. math::

        \lim_{\epsilon \to 0} k(\tau) = \sigma^2\,\left(1+
        \frac{\sqrt{3}\,\tau}{\rho}\right)\,
        \exp\left(-\frac{\sqrt{3}\,\tau}{\rho}\right)

    This term should be used with care since there could be numerical issues
    with this approximation.

    Args:
        sigma: The parameter :math:`\sigma`.
        rho: The parameter :math:`\rho`.
        eps (optional): The value of the parameter :math:`\epsilon`.


r"""A mixture of two SHO terms that can be used to model stellar rotation

    This term has two modes in Fourier space: one at ``period`` and one at
    ``0.5 * period``. This can be a good descriptive model for a wide range of
    stochastic variability in stellar time series from rotation to pulsations.

    More precisely, the parameters of the two :class:`SHOTerm` terms are
    defined as

    .. math::

        Q_1 = 1/2 + Q_0 + \delta Q \\
        \omega_1 = \frac{4\,\pi\,Q_1}{P\,\sqrt{4\,Q_1^2 - 1}} \\
        S_1 = \frac{\sigma^2}{(1 + f)\,\omega_1\,Q_1}

    for the primary term, and

    .. math::

        Q_2 = 1/2 + Q_0 \\
        \omega_2 = \frac{8\,\pi\,Q_1}{P\,\sqrt{4\,Q_1^2 - 1}} \\
        S_2 = \frac{f\,\sigma^2}{(1 + f)\,\omega_2\,Q_2}

    for the secondary term.

    Args:
        sigma: The standard deviation of the process.
        period: The primary period of variability.
        Q0: The quality factor (or really the quality factor minus one half;
            this keeps the system underdamped) for the secondary oscillation.
        dQ: The difference between the quality factors of the first and the
            second modes. This parameterization (if ``dQ > 0``) ensures that
            the primary mode alway has higher quality.
        f: The fractional amplitude of the secondary mode compared to the
            primary. This should probably always be ``0 < f < 1``, but that
            is not enforced.


A wrapper around terms defined using the original celerite package

    Args:
        term (celerite.terms.Term): The term defined using ``celerite``.


The main interface to the celerite2 Gaussian Process (GP) solver

    Args:
        kernel: An instance of a subclass of :class:`terms.Term`.
        mean (optional): The mean function of the process. This can either
            be a callable (it will be evaluated with a single argument, a
            vector of ``x`` values) or a scalar. (default: ``0.0``)
        **kwargs: Other arguments will be passed directly to
            :func:`GaussianProcess.compute` if the argument ``t`` is specified.


A multivariate normal distribution with a celerite covariance matrix

    Args:
        gp (celerite2.theano.GaussianProcess): The Gaussian Process computation
            engine.


A multivariate normal distribution with a celerite covariance matrix


Differential evolution mutation type. users can select what kind of mutation type to use

    Args:
        de_rand_1_bin: Differential evolution using mutation with rand crossover and mutation
        de_best_1_bin: Differential evolution using mutation with crossover with best individual
        simple: Differential evolution using mutation with cross over and mutation using best individual
        de_rand_1_bin_spawn: Applies mutation and crossover using de_rand_1_bin to a list of individuals to spawn even more individual combinations
        de_dmp: uses Difference Mean Based Perturbation style crossover and mutation


Data class for storing the mutation parameters used for NSGA and differential evolution problems

    Args:
        mutation_type (de_mutation_type): type of mutation to use
        sigma (float): mutation step size 0.1
        mu (float): mutation rate
        F (float): Amplification Factor [0,2]
        C (float): Crossover factor [0,1]


These settings control how the optimizer will execute.
    Example:
        .. code-block: python
            config = parallel_settings()
            ns = NSGA3()
            ns.parallel_settings = config

    Args:
        concurrent_executions (int): Number of concurrent executions per machine
        cores_per_execution (int): number of cores per exectuion. Defaults to 1. Set it to 0 to ignore using cores per execution and just go with concurrent executions
        execution_timeout (int): execution timeout in minutes. Defaults to 10 minutes
        machine_filename (str): path to the machine file. Defaults to 'machinefile.txt'
        database_filename (str): path to the database. Defaults to 'database.csv'
        ignore_cores (bool): indicate whether to ignore the number of cores per execution and just execute the code


This class represents each individual or "evaluation". In each evaluation there are a set of parameters for objectives as well as additional parameters that are kept track.


This represents a value that a single evaluation keeps track of. For example the objective or if this is an computational simulation, we keep track of the stress, strain, volume, etc., anything that isn't a constraint.

        Inputs:
            name: (string) name of the parameter
            lower_value: (float) this represents the lower bound of the variable
            upper_value: (float) this represents the upper bound of the variable
            value_if_failed: (float) this is set to 100000, probably a good value for a minimization problem
            constr_less_than: (float) default is None for no constraint
            constr_greater_than: (float) default is None for no constraint


Base class for starting an optimization


	A class for each reduced IFU datacube.

	Attributes:
		hdu (astropy HDU): FITS HDU of intensity cube
		header (astropy HDU header): FITS header of data cube
		data (3D array): intensity cube
		var (3D array): variance cube
		mask (3D array): mask cube
		wvl_zcorr (1D array): redshift-corrected wavelength
		wcs (astropy WCS): coordinate system

	Methods:


	A class for each reduced IFU datacube.

	Attributes:
		hdu (astropy HDU): FITS HDU of intensity cube
		header (astropy HDU header): FITS header of data cube
		data (3D array): intensity cube
		var (3D array): variance cube
		mask (3D array): mask cube
		wvl_zcorr (1D array): redshift-corrected wavelength
		wcs (astropy WCS): coordinate system
		verbose (bool): if 'True', make test plots

	Methods:


    A CubeP3M clumping factor file.

    Use the read_from_file method to load a clumping factor file, or
    pass the filename to the constructor.

    Some useful attributes of this class are:

    * raw_clumping (numpy array): the clumping factor in simulation units
    * z (float): the redshift of the file (-1 if it couldn't be determined from the file name)


Temperature only destriping

    Apply does the left side of the destriping equation


Preconditioning operator

    just divides the accumulated quantity by the number of samples per baseline


MPI Communication metadata

    Stores data distributions and Export objects
    for communication


MPI I/O array write, using mpi4py


/// \brief Derived class implementing Differential Evolution minimization,
///        specialized for Imfit


// Note that we need to declare and then define a virtual destructor for this
// class to avoid annoying (Clang) compiler warnings due to the "delete it->second"
// line in FreeFactories() --
// "warning: delete called on 'factory' that is abstract but has non-virtual destructor"
// (see http://stackoverflow.com/questions/10024796/c-virtual-functions-but-no-virtual-destructors)


/**
         * @brief A custom thread pool implementation.
         *
         * Practicing ThreadPooling based on these resources:
         * https://stackoverflow.com/a/32593825/13874039
         * https://www.cnblogs.com/sinkinben/p/16064857.html
         * https://gist.github.com/tonykero/9512f2fb7f47d1ee687ae8595b17666e
         * https://stackoverflow.com/questions/23896421/efficiently-waiting-for-all-tasks-in-a-threadpool-to-finish
         */


/**
     * @brief Visitor interface for InitialConditions
     *
     * This visitor defines the interface for processing InitialConditions
     * fields. Concrete visitors implement this interface to populate, validate,
     * or process the InitialConditions in different ways.
     */


/**
     * @brief Visitor that populates InitialConditions from a ConfigDict
     */


/**
     * @brief Visitor that applies default values to InitialConditions
     */


/**
     * @brief Visitor that validates InitialConditions values
     */


// control blocks for type erasure


// utility class for terminal capabilities detection


// main enhanced table class


// factory class for creating beautifully themed tables


/**
 * @brief Handler to activate nominal mission path
 */


/**
 * @brief Handler for takeoff trigger
 */


/**
 * @brief A return to mission handler using only vector commands
 *
 */


/**
 * @brief Return to mission using a flightplan
 *
 */


/**
 * @brief Handler to plan a path to the next feasible waypoint
 *
 */


/**
 * @brief Landing handler
 *
 */


/**
 * @brief Traffic conflict handler (for daidalus guidance)
 *
 */


/**
 * @brief Merging handler
 *
 */


/**
 * @brief Handler to initiate a ditch request (for Safe2Ditch)
 *
 */


/**
 * @brief Ditching handler
 *
 */


/**
 * @brief Post Ditch TOD handler
 *
 */


// Handler to return to launch point, continuing to monitor triggers


// Handler to return to launch point by sending RTL command


// Handler for geofence conflicts


/**
 * @brief Core class for performing guidance computations
 *
 */


/**
 * @brief Trajectory Management object
 */


/**
 * @brief DubinsPlanner object
 *
 */


// -1X : Error in shooting method
// -10 : Error in finding starting conditions
// -11 : Numerical integration failed
// -12 : Error using ZGESV solver with boundary condition
//
// -2X : Error in propagation matrix method
// -20 : Unknown core starting conditions
// -21 : Error using ZGESV solver with boundary condition


// There are 4 dependent y values in the EOS ODE:
// 0 : gravity
// 1 : pressure
// 2 : total mass
// 3 : moment of inertia
// Additionally there are 5 extra parameters that are tracked.
// 4 : density
// 5 : Real part of complex shear modulus
// 6 : Imag part of complex shear modulus
// 7 : Real part of complex bulk modulus
// 8 : Imag part of complex bulk modulus


//! Sparsity Averaging Reweighted Analysis


// Makes members public so we can test one at a time


/**
 * @class cudaUpchannelizer_chord_U8
 * @brief cudaCommand for Upchannelizer_chord_U8
 */


/**
 * @class cudaTransposeKernel_pathfinder
 * @brief cudaCommand for TransposeKernel_pathfinder
 */


/**
 * @class cudaDeviceInterface
 * @brief Class to handle CUDA interactions with GPU hardware.
 *
 * @par GPU Memory
 * @gpu_mem  bf_output       Output from the FRB pipeline, size 1024x128x16
 *     @gpu_mem_type         staging
 *     @gpu_mem_format       Array of @c float
 *     @gpu_mem_metadata     chimeMetadata
 *
 * @author Keith Vanderlinde
 */


/**
 * @class cudaQuantize
 * @brief cudaCommand for quantizing float16 to int4, with Cuda code from Hans Hopkins.
 *
 * @author Dustin Lang
 *
 * @par GPU Memory
 * @gpu_mem  gpu_mem_input  Input time-stream (matrix) of float16 values
 *   @gpu_mem_type   staging
 *   @gpu_mem_format Array of @c float16
 * @gpu_mem  gpu_mem_meanstd  Output array of float16 means and standard deviations (one mean,std
 * per 256-element "chunk")
 *   @gpu_mem_type   staging
 *   @gpu_mem_format Array of @c float16
 * @gpu_mem  gpu_mem_output  Output array of int4 quantized values
 *   @gpu_mem_type   staging
 *   @gpu_mem_format Array of @c int4
 *
 * @conf   num_chunks          Int.  Number of 256-element "chunks" in the gpu_mem_input
 * time-stream.  Must be a factor of 32.
 */


/**
 * @class cudaUpchannelize
 * @brief cudaCommand for upchannelizing baseband data.
 *
 * Kernel by Kendrick Smith and Erik Schnetter, eg
 * https://github.com/eschnett/IndexSpaces.jl/blob/main/output-A40/upchan.ptx
 *
 * @par GPU Memory
 * @gpu_mem  gpu_mem_input_voltage  Input complex voltages of size samples_per_data_set * num_dishes
 * * 2 polarizations * num_local_freq
 *   @gpu_mem_type   staging
 *   @gpu_mem_format Array of @c int4+4 complex
 * @gpu_mem  gpu_mem_output_voltage  Output complex voltages of size (samples_per_data_set/upchan) *
 * num_dishes * 2 polarizations * (num_local_freq*upchan)
 * 2
 *   @gpu_mem_type   staging
 *   @gpu_mem_format Array of @c int4+4 complex
 * @gpu_mem  gpu_mem_gains  Input per-output-frequency gains, size (num_local_freq * upchan)
 *   @gpu_mem_type   staging
 *   @gpu_mem_format Array of @c float16
 * @gpu_mem  gpu_mem_info  Output status information; size threads_x * threads_y * blocks_x
 *   @gpu_mem_type   staging
 *   @gpu_mem_format Array of @c int32
 *
 * @conf  num_dishes            Int.  Number of dishes.
 * @conf  num_local_freq        Int.  Number of frequencies in each frame.
 * @conf  samples_per_data_set  Int.  Number of time samples per frame.
 * @conf  upchan_factor         Int.  Upchannelization factor.
 */


/**
 * @class cudaCorrelator
 * @brief cudaCommand for doing an N2 correlation, with Cuda code from Kendrick.
 *
 * @author Andre Renard (by kindly telling Dustin Lang what to type)
 *
 * An example of this stage being used can be found in
 * `config/tests/verify_cuda_n2k.yaml`.
 *
 * A CPU implementation is in `lib/testing/gpuSimulateN2k.hpp`.
 *
 * @par GPU Memory
 * @gpu_mem  gpu_mem_voltage  Input complex voltages of size samples_per_data_set * num_elements *
 * num_local_freq
 *   @gpu_mem_type   staging
 *   @gpu_mem_format Array of @c 4+4-bit complex
 * @gpu_mem  gpu_mem_correlation_triangle  Output complex correlation values of size per frame:
 * (samples_per_data_set / sub_integration_ntimes) * num_elements^2 * 2 * sizeof(int32)
 *   @gpu_mem_type   staging
 *   @gpu_mem_format Array of @c int32
 *
 * @conf   num_elements          Int.  Number of feeds.
 * @conf   num_local_freq        Int.  Number of frequencies.
 * @conf   samples_per_data_set  Int.  Number of time samples per Kotekan block.
 * @conf   sub_integration_ntime Int.  Number of time samples that will be summed into the
 * correlation matrix.
 *
 * Note: While the output is only supposed to fill the upper triangle
 * of the correlation matrices, this implementation fills a few of the
 * below-the-diagonal elements with non-zero values.
 */


/**
 * @class cudaProcess
 * @brief Stage to manage all the kernels and copy commands for a GPU
 *
 * This stage is responsible for running the cudaCommandObjects which in turn run the
 * various host<->device copies and kernel calls.  Much of the logic exists in the base
 * class @c gpuProcess, so that class for more details.
 *
 * @conf num_cuda_streams The number of CUDA streams to setup, the default is 3 for one
 *                        host->device, one device->host, and one kernel stream.
 *                        Can be set higher if more than one stream is need for each type
 *                        of operation.  See @c cudaCommand and @c cudaSyncStream for more details.
 *
 * @author Keith Vanderlinde and Andre Renard
 */


/**
 * @class hipInputData
 * @brief hipCommand for copying data onto the GPU.
 *
 * This is a hipCommand that async copies a buffer from CPU to GPU.
 * This code also passes metadata along.
 *
 * @par GPU Memory
 * @gpu_mem in_buf           Input buffer, arbitrary size
 *     @gpu_mem_type         staging
 *     @gpu_mem_format       Any
 *
 * @author Andre Renard
 *
 */


/**
 * @class hipEventContainer
 * @brief Class to handle HIP events for pipelining kernels & copies.
 *
 * @author Andre Renard
 */


/**
 * @class hipDeviceInterface
 * @brief Class to handle HIP interactions with GPU hardware.
 *
 * @par GPU Memory
 * @gpu_mem  bf_output       Output from the FRB pipeline, size 1024x128x16
 *     @gpu_mem_type         staging
 *     @gpu_mem_format       Array of @c float
 *     @gpu_mem_metadata     chimeMetadata
 *
 * @todo   Add profiling flag.
 *
 * @author Andre Renard
 */


/**
 * @class hipOutputData
 * @brief hipCommand for copying data onto the GPU.
 *
 * This is a hipCommand that async copies a buffer from GPU to CPU.
 * This code also passes metadata along from another buffer.
 *
 * @par GPU Memory
 * @gpu_mem out_buf          Output buffer, arbitrary size
 *     @gpu_mem_type         staging
 *     @gpu_mem_format       Any
 * @gpu_mem in_buf           Input buffer from which to copy metadata
 *     @gpu_mem_type         staging
 *     @gpu_mem_format       Any
 *
 * @author Andre Renard
 *
 */


/**
 * @class hipCommand
 * @brief Base class for defining HIP commands to execute on GPUs
 *
 * This is a base class for HIP commands to run on NVidia hardware.
 * Kernels and other opersations (I/O) should derive from this class,
 * which handles a lot of queueing and device interface issues.
 *
 * @todo Clean up and refactor GPU code to not require external kernels.
 *
 * @author Andre Renard
 */


/**
 * @class KotekanTrackers
 * @brief Class to manage and keep tracking all registered stat trackers
 *
 * This class must be registered with a kotekan REST server instance by
 * using the @c register_with_server() function.
 *
 * The usage is to call @c add_tracker() and save the returned shared pointer
 * of created tracker in the calling stage. To add samples, call function
 * @c add_sample() from that shared pointer.
 *
 * Two endpoints are used to display tracker content in json format. @c /trackers
 * shows all samples and their timestamps, and @c /trackers_current gives
 * min/max/avg/std stats of each tracker.
 *
 * To enable tracker dump when error occurs, set trackers->enabled to true and
 * give a dump file path in trackers->dump_path. The dump file is named
 * (hostname)_crash_stats_(time).json.
 *
 * This class is a singleton, and can be accessed with @c instance()
 */


/**
 * @class basebandApiManager
 * @brief Class for receiving baseband dump requests and sending request status
 *
 * This class must be registered with a kotekan REST server instance,
 * using the @c register_with_server() function.
 *
 * This class is a singleton, and can be accessed with @c instance(). The normal
 * use is for the @c basebandReadout stage to call @c get_next_request in a
 * loop, and when the result is non-null, use the returned @c basebandDumpStatus
 * to keep track of the data written so far. Once the writing of the data file
 * is completed, the ``state`` of the request should be set to ``DONE``.
 *
 * @par Metrics
 * @metric kotekan_baseband_requests_total
 *         The count of dump requests received by the REST endpoint ``/baseband``
 *
 * @author Davor Cubranic
 */


/**
 * @class basebandReadoutManager
 * @brief Class for managing readout state
 */


/**
 * @class CpuMonitor
 *
 * @brief Create a new thread to keep tracking all stage cpu usage.
 * This implementation is only applicable on Linux.
 **/


/**
 * @class Metric
 * @brief An internal base class for storing metric value for a given combination of label values
 */


/**
 * @class Counter
 * @brief Represents a metric whose value can only go up
 *
 * @remark See [Prometheus
 * documentation](https://prometheus.io/docs/instrumenting/exposition_formats/) for the precise
 * format specification.
 */


/**
 * @class Gauge
 * @brief Represents a metric whose value can go up and down
 *
 * @remark See [Prometheus
 * documentation](https://prometheus.io/docs/instrumenting/exposition_formats/) for the precise
 * format specification.
 */


/**
 * @class Metrics
 * @brief Class for exporting system metrics to a prometheus server
 *
 * This class must be registered with a kotekan REST server instance.=,
 * using the @c register_with_server() function.
 *
 * The typical usage is to declare the metric with @c add_gauge or @c
 * add_counter methods, and then use the returned Metric instance to set the
 * metric's value (for a particular combination of label values, if applicable).
 *
 * This class is a singleton, and can be accessed with @c instance()
 *
 * @todo Make this class auto register with the REST server.
 *
 * @author Andre Renard
 */


/**
 * @brief Contains details of a request (POST or GET), and provides
 *        functions for replying to the request.
 *
 * Used in call back functions to provide a way to reply to
 * request with either an error, json, binary, text, or empty message.
 *
 * The @c send_ functions should called exactly once per connection instance.
 *
 * @author Andre Renard
 */


/**
 * @brief Framework level REST server (singleton)
 *
 * Provides the system for any framwork stage to register endpoints.
 *
 * This object uses libevent internally to handle the http requests.
 *
 * See the docs for examples of using this class.
 *
 * @author Andre Renard
 */


/**
 * @class configUpdater
 * @brief Kotekan core component that creates endpoints defined in the config
 * that stages can subscribe to to receive updates.
 *
 * An endpoint will be created for every updatable config block defined in the
 * configuration file. updatable blocks can be anywhere in the configuration
 * tree, but may not be inside another updatable block. They need to contain a
 * key `kotekan_update_endpoint` with the value `"json"`. They also need to
 * contain initial values for all fields that subscribing stages will expect
 * on an update.
 *
 * Example:
 * ```
 * foo:
 *     bar:
 *         kotekan_update_endpoint: "json"
 *         some_value: 0
 *         some_other_value: 1
 * ```
 *
 * In the config block of a stage that wants to get updates of a specific
 * updatable block, either a key "updatable_config" with the full path to the updatable
 * block as a value has to exist, or an object called "updatable_config" with
 * a list of all updatable config blocks.
 *
 * Example:
 * ```
 * my_stage:
 *     updatable_config: "/foo/bar"
 * ```
 * or
 * ```
 * my_stage:
 *     updatable_config:
 *         bar: "/foo/bar"
 *         fu: "/foo/fu"
 * ```
 *
 * Every stage that subscribes to this update endpoint by calling
 * ```
 * configUpdater config_updater = configUpdater::instance();
 * config_updater.subscribe(this, std::bind(&my_stage::my_callback, this, _1));
 * ```
 * or
 * ```
 * std::map<std::string, std::function<bool(nlohmann::json &)> callbacks;
 *
 * callbacks["bar"] = std::bind(&my_stage::my_bar_callback, this, _1);
 *
 * callbacks["fu"] = std::bind(&my_stage::my_fu_callback, this, _1);
 *
 * configUpdater::instance().subscribe(this, callbacks);
 * ```
 * will receive an initial update on each callback function with the initial
 * values defined in the config file (in the first example
 * `{"some_value": 0, some_other_value: 1}`).
 * That's why the stage must be ready to receive updates **before** it
 * subscribes.
 *
 * All and only the variables defined in the updatable config block in the
 * config file are guaranteed to be in the json block passed to the stage
 * callback function.
 * It is up to the stage, though, to check the data types, sizes and
 * the actual values in the callback function and return `false` if anything
 * is wrong.
 *
 * The stage must be ready to receive updates **before** it subscribes and it
 * has to apply save threading principles.
 *
 * @author Rick Nitsche
 */


/** @brief A Bitshuffle header file.
 *
 * Header file to store common Bitshuffle constants.
 *
 * @author James Willis
 **/


/**
 * @brief The description of a dataset consisting of a dataset state and a base
 * dataset.
 *
 * A dataset is described by a dataset state applied to a base dataset. If the
 * flag for this dataset being a root dataset (a dataset that has no base
 * dataset), the base dataset ID value is not defined.
 */


/**
 * @class movingAverage
 * @brief Calculate an exponentially weighted moving average of a time series.
 *
 * @author Richard Shaw
 **/


/**
 * @class SlidingWindowMinMax
 *
 * @brief Use two deques to keep tracking minimum and maximum values.
 *
 * This class is based on a modified version of:
 * https://www.nayuki.io/page/sliding-window-minimum-maximum-algorithm
 **/


/**
 * @class StatTracker
 *
 * @brief Store samples and compute statistics.
 *
 * There are two ways in this class to implement min/max (i.e., sliding window and brute force).
 * The sliding window approach uses O(1) time to get min/max but spends more time when a sample is
 *added. The brute force approach uses O(n) time to traverse the entire buffer without overhead in
 *add_sample(). "is_optimized" is the flag to switch between two methods (true: sliding window;
 *false: brute force). Normally, if get_min/max() is called often, "is_optimized" should be set to
 *true.
 **/


/**
 * @brief Class to hold buffer indices.
 **/


/**
 * @brief A telescope class to represent CHIME.
 *
 * @conf    require_frequency_map        Require a frequency map in the config file, or generate
 *                                       an error and exit if none exists. Default: false
 * @conf    allow_default_frequency_map  If @c require_frequency_map is set to false and
 *                                       @c allow_default_frequency_map is true allow queries from
 *                                       the default map.  If set to false, then do not generate
 *                                       a default map, and throw an exception when lookups are
 *                                       done for stream_ids.
 *                                       This can be useful for systems not expected to
 *                                       lookup stream_ids. This option has no effect if
 *                                       @c require_frequency_map is set to true.  Default: true
 * @conf    query_frequency_map  bool.   Should we get the frequency map from a remote source.
 * @conf    frequency_map_host   string. The frequency map server IP address.
 * @conf    frequency_map_port   uint.   The port number on the frequency map server.
 * @conf    frequency_map_endpoint string. The endpoint with the frequency map.
 **/


/**
 * @class HFBFrameView
 * @brief Provide a structured view of a hyperfine beam buffer.
 *
 * This class inherits from the FrameView base class and sets up a view on a hyperfine beam buffer
 *with the ability to interact with the data and metadata. Structural parameters can only be set at
 * creation, everything else is returned as a reference or pointer so can be
 * modified at will.
 *
 * @author James Willis
 **/


/**
 * @class Polyco
 * @brief Calculate pulsar timing given polynomial expansion of phase.
 *
 * Based on work by Tara Akhoundsadegh, Carol Ng and Kiyo Masui.
 *
 * @author Tristan Pinsonneault-Marotte
 **/


/**
 * @class SegmentedPolyco
 * @brief Collection of Polyco objects representing timing solutions
 *        over a segmented span of time.
 *
 * @author Tristan Pinsonneault-Marotte
 **/


/** @brief A CHIME correlator ring-style buffer file in raw format.
 *
 * The class manages a CHIME correlator file of fixed length in time,
 * where additional times get written at the start of the file again,
 * effectively maintaining a ring buffer of the most recent data stream.
 *
 * The file format is provided by visFileRaw.
 *
 * @warning Unlike visFileRaw, this class will overwrite an existing
 *          file with the same path.
 *
 * @author Tristan Pinsonneault-Marotte
 **/


/**
 * @brief Implementation for an ICEboard like telescope with a Casper PFB.
 *
 * @conf  sample_rate     int.     The sampling rate in MHz.
 * @conf  fft_length      int.     The FFT frame length in samples. Note this is the length of a
 *                                 basic FFT, the ICEBoard uses a PFB with a 8096 sample window,
 *                                 but the effective length is 2048 for computing the number
 *                                 of frequency bins.
 * @conf  nyquist_zone    int.     The Nyquist zone we are sampling in (zone=1 is standard
 *                                 sampling, zone>=2 is alias sampling).
 * @conf  query_gps       bool.    Should the telescope object get the GPS from a remote source
 *                                 if not available, or false, will try to retrieve from config.
 * @conf  gps_host        string.  The GPS server IP address
 * @conf  gps_port        uint.    The port number on the GPS server
 * @conf  gps_endpoint    string.  The endpoint with the GPS time
 **/


/**
 * @class restClient
 * @brief REST client: Send REST messages to a server and maybe get a reply.
 *
 * This class supports sending GET messages and POST messages with json data
 * using libevent and provides access to data from the reply of the server.
 *
 * Implementation
 * ==============
 *
 * There is an event loop running in the main_thread() that gets started by the constructor.
 * The event thread is sending out requests, waits for results and calls the assigned callback
 * functions. All this has to be done from the same thread that runs the event loop, which makes
 * class more complicated than you would expect:
 * The function `make_request` does not itself create the request, because it's usually called from
 * another thread. To bring the request to the event loop thread, libevent's `bufferevent_pair` is
 * used (see http://www.wangafu.net/~nickm/libevent-book/Ref6a_advanced_bufferevents.html).
 * One side of the pair (`bev_req_write`) takes new requests in the form
 *
 * | struct restRequest | string host | string path | string json_data |
 *
 * The length of each string is in the `restRequest` struct. Writing all of this data to the
 * bufferevent's output buffer has to be done atomically, so that the read callback
 * (`_bev_req_readcb`) attached to the other side of the pair (`bew_req_read`) is not called more
 * than once. To achieve this, a `evbuffer_iovec` is used to reserve the necessary space in the
 * output buffer, then copying all data in and finally committing the written data once, which will
 * trigger the read callback. The described sequence still has to be protected from concurrent
 * access (`_mtx_bev_buffer`), because it fails if the buffer is written to between reserving and
 * committing.
 *
 * The request is then finally made by the same thread that runs the event loop, in the
 * read callback `_bev_req_readcb` attached to the reading side `bev_req_read` of the
 * bufferevent pair. This is done by the following:
 *
 * - Create a connection base.
 * - Make a new HTTP request. The external callback function as well as the connection base are
 *   attached to the request in order to pass them to the internal callback function.
 * - Write request data into the HTTP request output buffer.
 * - Start the request.
 *
 * When the request is done, the internal callback function `http_request_done` is called.
 * It calls the external callback that was initially given to `make_request` and hands the
 * result of the request to it.
 *
 * @author Rick Nitsche
 */


/**
 * @class VisFrameView
 * @brief Provide a structured view of a visibility buffer.
 *
 * This class inherits from the FrameView base class and sets up a view on a visibility buffer with
 *the ability to interact with the data and metadata. Structural parameters can only be set at
 * creation, everything else is returned as a reference or pointer so can be
 * modified at will.
 *
 * @note There are multiple constructors: one for viewing already initialised
 *       buffers; one for initialising a buffer and returning a view of it; and
 *       one for copying an existing buffer into a new location and returning a
 *       view of that. Make sure to pick the right one!
 *
 * @todo This may want changing to use reference wrappers instead of bare
 *       references.
 *
 * @author Richard Shaw and James Willis
 **/


/**
 * @brief A class to hold telescope specific functionality.
 *
 *
 **/


/**
 * @class HFBFrameView
 * @brief Provide a structured view of a baseband dump buffer.
 *
 * This class inherits from the FrameView base class and sets up a view on a
 * baseband dump buffer with the ability to interact with the data and metadata.
 *
 * @author Davor Cubranic
 **/


/** @brief A CHIME correlator file in raw format.
 *
 * The class creates and manages writes to a CHIME style correlator output
 * file. It also manages the lock file.
 *
 * This output has the following structure.
 *
 * The metadata (including the file structure) is written into a `.meta` file
 * which is JSON serialised as `msgpack`. The interpretation should be pretty
 * straightforward of the index_map and attributes sections as these map
 * directly to the HDF5 equivalents. The `structure` section describes how to
 * interpret the final, giving the sizes in bytes of the metadata, data and the
 * overall frame size (which is aligned to a set boundary).
 *
 * The `.data` file contains the raw data output. This is packed as:
 *
 *  - 1st byte is set to `1` if data is present (or is implicitly zero).
 *  - visMetadata struct dump
 *  - visBuffer dump
 *
 * @author Richard Shaw
 **/


/** @brief A base class for files holding correlator data.
 *
 * The class specifies the interface that all correlator file types must follow.
 *
 * File types are expected to create and manage lock files, which should have
 * the format `.<datafilename>.lock`.
 *
 * @author Richard Shaw
 **/


/**
 * @brief Manage the set of correlator files being written.
 *
 * This abstraction above visFile allows us to hold open multiple files for
 * writing at the same time. This is needed because we roll over to a new file
 * after a certain number of samples, but in general we may still be waiting on
 * samples to go into the existing file.
 *
 * @author Richard Shaw
 **/


/** @brief A CHIME correlator archive file.
 *
 * The class creates and manages writes to a CHIME style correlator archive
 * file in the standard HDF5 format. It also manages the lock file.
 *
 * @author Richard Shaw
 **/


/** @brief A CHIME correlator file.
 *
 * The class creates and manages writes to a CHIME style correlator output
 * file in the standard HDF5 format. It also manages the lock file.
 *
 * @author Richard Shaw
 **/


/**
 * @brief A correlator output file with fast direct writing..
 *
 * This class writes HDF5 formatted files, but for improved speed bypasses HDF5
 * when writing out data. To do this it uses contiguous datasets, which means
 * that the files are pre-allocated to their maximum size. On close, the number
 * of time samples written is written into an attribute on the file called
 * `num_time`.
 *
 * Note that we rely on the behaviour of the filesystem to return 0 in
 * allocated but unwritten parts of the files to give zero weights for
 * unwritten data.
 *
 * @author Richard Shaw
 **/


/**
 * @brief A base class for representing state changes done to datasets.
 *
 * This is meant to be subclassed. All subclasses must implement a constructor
 * that can build the type from a `json` argument, and a `data_to_json` method
 * that can serialise the type into a `json` object.
 *
 * @author Richard Shaw, Rick Nitsche
 **/


/**
 * @brief A dataset state that describes the frequencies in a datatset.
 *
 * @author Richard Shaw, Rick Nitsche
 */


/**
 * @brief A dataset state that describes the inputs in a datatset.
 *
 * @author Richard Shaw, Rick Nitsche
 */


/**
 * @brief A dataset state that describes the products in a datatset.
 *
 * @author Richard Shaw, Rick Nitsche
 */


/**
 * @brief A dataset state that keeps the time information of a datatset.
 *
 * @author Rick Nitsche
 */


/**
 * @brief A dataset state that keeps the eigenvalues of a datatset.
 *
 * @author Rick Nitsche
 */


/**
 * @brief A dataset state that describes a redundant baseline stacking.
 *
 * @author Richard Shaw
 */


/**
 * @brief A dataset state that describes all the metadata that is written to
 * file as "attributes", but not defined by other states yet.
 *
 * @author Rick Nitsche
 */


/**
 * @brief A dataset state that describes the gains applied to the data.
 *
 * @author Richard Shaw
 */


/**
 * @brief A dataset state that describes the input flags being applied.
 *
 * @author Richard Shaw
 */


/**
 * @brief A dataset state that keeps the beam information of a datatset.
 *
 * @author James Willis
 */


/**
 * @brief A dataset state that keeps the sub-frequency information of a datatset.
 *
 * @author James Willis
 */


/**
 * @brief A dataset state that keeps the RFI frame-dropping information of a datatset.
 *
 * @author Rick Nitsche
 */


/** @brief A CHIME baseband file in raw format
 *
 * The class creates and manages writes to a baseband dump file for a single frequency. It also
 * manages the lock file.
 *
 * The output has the following structure:
 *  - 1st byte is set to `1` if data is present (or is implicitly zero).
 *  - BasebandMetadata struct dump
 *  - baseband buffer frame contents
 *
 * @author Davor Cubranic
 */


/**
 * @class FrameView
 * @brief Provide a structured view of a buffer.
 *
 * This class sets up a base view on a buffer with the ability to
 * interact with the data. All classes which inherit from this should provide the following API:
 *
 * calculate_buffer_layout(...);
 * copy_frame(Buffer* buf_src, int frame_id_src, Buffer* buf_dest,
 *            int frame_id_dest);
 * calculate_frame_size(kotekan::Config& config, const std::string& unique_name);
 * calculate_frame_size(...);
 * set_metdata(...);
 * create_frame_view(Buffer* buf, const uint32_t index, ...);
 * data_size();
 *
 * @author James Willis
 **/


/**
 * @brief Base class for specifying a gated data accumulation.
 *
 * Subclasses of need to implement weight_function to return a function which
 * given the start and end times of a frame of its frame, as well as it's
 * frequency, produces a weight with which it should be accumulated.
 *
 * @author Richard Shaw, Tristan Pinsonneault-Marotte
 *
 **/


/**
 * @brief Pulsar gating.
 *
 * Config message must contain:
 * @conf  enabled      Bool. Is the gating enabled or not.
 * @conf  pulsar_name  String. Name of the pulsar.
 * @conf  dm           Float. Dispersion measure in pc/cm^3.
 * @conf  rot_freq     Float. Rotational frequency in Hz.
 * @conf  pulse_width  Float. Width of pulse in s.
 * @conf  segment      Float. Length of polyco segments in s.
 * @conf  t_ref        Array of floats. Reference times (MJD) for solution
 *                     segment. Should be close to the observing time.
 * @conf  phase_ref    Array of floats. Phases of pulsar at t_ref.
 * @conf  coeff        Array of array of floats. Polyco coefficients
 *                     for every timing solution segment.
 **/


/**
 * @brief Uniformly weight all data.
 *
 * @note This is used to implement the nominal visibility dataset. It does not
 *       actually gate.
 **/


/**
 * @class BipBuffer
 * @brief A single-producer single-consumer circular buffer that always supports writing a
 * contiguous chunk of data.
 *
 * BipBuffer, short of "bipartite buffer", is a data structure first described by Simon Cooke
 * (https://www.codeproject.com/articles/3479/the-bip-buffer-the-circular-buffer-with-a-twist). This
 * version is based on a lockless implementation described by Andrea Lattuada and James Munns
 * (https://andrea.lattuada.me/blog/2019/the-design-and-implementation-of-a-lock-free-ring-buffer-with-contiguous-reservations.html).
 *
 * This design facilitates operating on the buffer by two threads, one doing the writing, and the
 * other the reading. These threads don't work on the buffer directly, but instead use the
 * `BipBufferWriter` and `BipBufferReader` classes, respectively.
 *
 * @author Davor Cubranic
 */


/**
 * @class BipBufferWriter
 * @brief Provides the interface to request access to continuous writeable regions of BipBuffer
 *
 * @author Davor Cubranic
 */


/**
 * @class BipBufferReader
 * @brief Provides the interface to request access to continuous readable regions of BipBuffer
 *
 * @author Davor Cubranic
 */


/** @brief A CHIME correlator file in raw format.
 *
 * The class creates and manages writes to a CHIME style correlator output
 * file. It also manages the lock file.
 *
 * This output has the following structure.
 *
 * The metadata (including the file structure) is written into a `.meta` file
 * which is JSON serialised as `msgpack`. The interpretation should be pretty
 * straightforward of the index_map and attributes sections as these map
 * directly to the HDF5 equivalents. The `structure` section describes how to
 * interpret the final, giving the sizes in bytes of the metadata, data and the
 * overall frame size (which is aligned to a set boundary).
 *
 * The `.data` file contains the raw data output. This is packed as:
 *
 *  - 1st byte is set to `1` if data is present (or is implicitly zero).
 *  - VisMetadata struct dump
 *  - VisFrameView dump
 *
 * @author Richard Shaw
 **/


/** @brief A CHIME correlator archive file.
 *
 * The class creates and manages writes to a CHIME style correlator archive
 * file in the standard HDF5 format. It also manages the lock file.
 *
 * @author James Willis
 **/