    Predicate which determines if a Kinesis stream with the given name exists
    in the configured AWS environment.


Posts a message to a Kinesis stream.


    Predicate which determines if an s3 bucket with the given name exists
    in the configured AWS environment.


Stages data into an s3 bucket at a given path.


Populates a configuration file based on user input.


Summarizes the contents of a configuration file.


Validates the contents of local JSON files.


Processes science data files based on configuration file contents.


Returns a ConfigParser by reading the specified file.


    Returns a value from the provided config parser; any value for the key that
    is provided in the 'overrides' dictionary will take precedence.


    Returns a valid Config object that is populated from the provided config
    parser based on the 'environment', and with values overriden with anything
    provided in 'overrides'.


Validates each value in the configuration.


Initialize the logger for metgenc.


Displays the name of this utility using incredible ASCII-art.


    Prompts the user for configuration values and then creates a valid
    configuration file.


Process all Granules and record the results and summary.


    Determine which file reader to use for the given data files. This currently
    is limited to handling one data file type (and one reader) per collection.
    In a future issue, we may handle granules with multiple data file types per granule.
    In that future work this needs to be refactored to handle this case.


    Higher-order function that, given a granule operation function and a
    Ledger, will execute the function on the Ledger's granule, record the
    results, and return the resulting new Ledger.


Determine whether errors were raised during pipeline steps thus far.


Start a new Ledger of the operations on the given Granule.


Finalize the Ledger of operations on its Granule.


    Authenticate with Earthdata using user name and password retrieved
    from environment variables.


Confirm required elements exist in the UMM-C record returned from CMR.


Look for list of keys in a UMM-C record and log the status.


    Map a cumulus ingest environment to the environment string needed for
    Earthdata login via earthaccess.


Identify CMR provider based on application environment.


Retrieve collection metadata in UMM-C format if it exists.


Identify file(s) related to each granule.


Identify granules based on a "granuleid" regex match group


    Identify granules based on unique data file basenames (minus file name
    extension) in lieu of a "granuleid" regex match group.


    Important! granule_regex argument must include a captured match group.

    Return a tuple representing a granule:
        - A string used as the "identifier" (in UMMG output) and "name" (in CNM output).
          This is the granule file name in the case of a single data file granule,
          otherwise the common name elements of all files related to a granule.
        - A set of one or more full paths to data file(s)
        - A set of zero or more full paths to associated browse file(s)
        - Path to an associated premet file (may be None or empty string)
        - Path to an associated spatial (or spo) file (may be None or empty string)


Associate collection information with the Granule.


Confirm collection metadata meet requirements for our granule metadata generation.


    Verify collection temporal extent information is usable if a collection
    temporal override is requested.


Get range or single temporal value details from the previously-extracted temporal extent object.


    Ensure granule spatial representation exists, and verify the collection
    geometry can be used if a collection geometry override is requested.


Prepare the Granule for creating metadata and submitting it.


Create the UMM-G file for the Granule.


Stage a set of files for the Granule in S3.


Create a CNM submission message for the Granule.


Write a CNM message to a file.


Publish a CNM message to a Kinesis stream.


Log a Ledger of the operations performed on a Granule.


Log a summary of the operations performed on all Granules.


Returns the full s3 URL for the given file name.


Returns the full s3 object path for the granule


    Return a string representation of a geometry (point, bounding box, gpolygon)
    Optionally generates optimized polygons when spatial files are present.


Return a string representation of a bounding rectangle


    Return a string representation of a point or polygon, based on the length
    of the spatial_values list.


Return a string representation of a temporal range or single value, as appropriate.


    Return a string representation of any additional attributes or platform information
    found in a premet file.


Validate local CNM or UMM-G (JSON) files


Return directory containing JSON files to be validated.


Identify the schema to be used for validation


Apply JSON schema to generated JSON content.


Extract temporal information from premet file contents.


    Read premet file and return a dict containing temporal information and
    any included additional attributes and/or platform information.


Parse ISO-standard datetime strings without a timezone identifier.


Extract temporal information from collection metadata or premet files according to .ini file settings.


Reformat a list of temporal values to match the format from UMM-C metadata


    Retrieve spatial information from a granule-specific spatial (or spo) file, or
    the collection metadata.


Read (lon, lat) points from a .spatial or .spo file.


    Read points from a .spo file, reverse the order of the points to comply with
    the Cumulus requirement for a clockwise order to polygon points, and ensure
    the polygon is closed. Raise an exception if either the granule spatial representation
    or the number of points don't support a gpolygon.


    Return a copy of the input list, extended if necessary to ensure the first
    and last points of the list have the same value. The original list is not
    modified.


    Parse spatial information from collection metadata. Annoyingly, this process
    will be reversed when we apply the template to populate UMM-G output. The
    current approach allows for consistent template population steps regardless
    of whether points were retrieved from a spatial file or the collection
    metadata, though.


    Read the content at netcdf_path and return a structure with temporal coverage
    information, spatial coverage information, file size, and production datetime.


Return an array of datetime strings


    Use time_coverage_end attribute if it exists, otherwise use a duration
    value from the ini file to calculate the time_coverage_end.

    TODO: Look for time_coverage_duration attribute in the netCDF file before
    using a value from the ini file.


    Return an array of dicts, each dict representing one lat/lon pair like so:
        {
            "Longitude": float,
            "Latitude": float
        }
    Eventually this should be pulled out of the netCDF-specific code into a
    general-use module.


Generate the thinned perimeter of a grid.


Get spatial coverage from spatial file if it exists, otherwise parse from CSV


    Extract metadata for generic data files.

    This reader is used when no specific reader exists for the data file type.
    It relies on spatial/spo files or collection metadata for geometry information,
    and premet files or collection metadata for temporal information.


Get temporal extent from premet file if it exists, otherwise parse from CSV


    Determine which file reader to use for the given collection auth_id and
    data file extension.


    Filter polygon points to ensure minimum spacing according to CMR tolerance requirements.

    Uses Shapely's set_precision to snap points to a grid, which automatically
    merges vertices that are closer than the tolerance. This ensures that no two
    successive points in the polygon boundary are within the tolerance distance.

    Parameters:
    -----------
    polygon : shapely.geometry.Polygon
        Polygon whose vertices need filtering
    tolerance : float
        Minimum required distance between points in degrees (default: 0.0001)

    Returns:
    --------
    shapely.geometry.Polygon : Filtered polygon with tolerance-compliant vertices


    Create a polygon representing the flightline coverage using concave hull.

    This function uses a simple, reliable approach with the concave_hull library
    and configurable quality parameters, ensuring all points meet CMR tolerance requirements.

    Parameters:
    -----------
    lon : array-like
        Longitude coordinates
    lat : array-like
        Latitude coordinates
    target_coverage : float, optional
        Target data coverage percentage (default: 0.98)
    max_vertices : int, optional
        Maximum number of vertices in final polygon (default: 100)
    cartesian_tolerance : float, optional
        Minimum spacing between points in degrees (default: 0.0001)
        This ensures CMR validation passes

    Returns:
    --------
    tuple: (polygon, metadata)
        - polygon: Shapely Polygon object with tolerance-compliant vertices
        - metadata: Dictionary with generation details


    Calculate what percentage of data points are covered by the polygon.

    Parameters:
    -----------
    polygon : shapely.geometry.Polygon
        The polygon to test
    data_points : array-like
        Nx2 array of [lon, lat] data points
    sample_size : int
        Maximum number of points to sample for performance

    Returns:
    --------
    float : Coverage ratio (0.0 to 1.0)


    Enhance polygon coverage by applying a simple buffer to expand coverage.

    This is much more efficient than complex point-by-point enhancement and
    maintains reasonable vertex counts while improving data coverage.

    Parameters:
    -----------
    polygon : shapely.geometry.Polygon
        Initial polygon
    data_points : array-like
        Nx2 array of [lon, lat] data points
    target_coverage : float
        Target coverage ratio to achieve

    Returns:
    --------
    shapely.geometry.Polygon or None : Buffered polygon or None if enhancement failed


    Smooth and simplify a buffered polygon to reduce vertex count while preserving coverage.

    Parameters:
    -----------
    polygon : shapely.geometry.Polygon
        Buffered polygon to smooth
    data_points : array-like
        Original data points for coverage checking

    Returns:
    --------
    shapely.geometry.Polygon or None : Smoothed polygon or None if failed


    Ensure polygon has counter-clockwise winding order as required by CMR.

    The Common Metadata Repository (CMR) requires that polygon points be
    specified in counter-clockwise order. This function checks the orientation
    and corrects it if necessary.

    Parameters:
    -----------
    polygon : shapely.geometry.Polygon
        Polygon to check and potentially reorient

    Returns:
    --------
    shapely.geometry.Polygon : Polygon with counter-clockwise exterior ring


MetGenC spatial polygon tools for comparison and validation.

    This utility provides tools for comparing generated polygons with CMR polygons,
    validating spatial coverage, and analyzing polygon generation performance.


Compare generated polygons with CMR polygons for a collection.

    COLLECTION: The collection short name (e.g., LVISF2, ILVIS2)

    Examples:

    \b
    # Compare 10 random LVISF2 granules
    metgenc-lab-polygons compare LVISF2 -n 10 --provider NSIDC_CPRD

    \b
    # Compare specific granule
    metgenc-lab-polygons compare LVISF2 --granule "GRANULE_NAME"

    \b
    # Use custom output directory
    metgenc-lab-polygons compare ILVIS2 -n 20 -o /tmp/polygon_analysis


Validate a spatial polygon file.

    POLYGON_FILE: Path to polygon file to validate

    Examples:

    \b
    # Validate a GeoJSON polygon
    metgenc-lab-polygons validate my_polygon.geojson

    \b
    # Validate and check coverage against source points
    metgenc-lab-polygons validate polygon.json --check-coverage --points-file points.csv


Display information about the spatial polygon tools.


        Process random granules from a collection.

        Parameters:
        -----------
        short_name : str
            Collection short name
        provider : str, optional
            Data provider
        n_granules : int
            Number of random granules to process
        data_extensions : list
            Valid data file extensions


Process granules sequentially (original method).


        Process a specific granule by name.

        Parameters:
        -----------
        short_name : str
            Collection short name
        granule_ur : str
            Specific granule name/UR to process
        provider : str, optional
            Data provider
        data_extensions : list
            Valid data file extensions


        Process a single granule.

        Parameters:
        -----------
        granule_entry : earthaccess.results.DataGranule or dict
            Earthaccess granule result or CMR granule entry
        output_dir : Path
            Output directory
        data_extensions : list
            Valid data file extensions

        Returns:
        --------
        dict or None : Processing results


        Download data file from URL.

        Parameters:
        -----------
        url : str
            Data file URL
        output_dir : Path
            Output directory

        Returns:
        --------
        Path or None : Downloaded file path


Create a dummy LVIS data file for testing.


Calculate what fraction of data points the CMR polygon covers.


        Load data points from file.

        Parameters:
        -----------
        data_file : Path
            Data file path

        Returns:
        --------
        tuple : (lon, lat) arrays or (None, None)


        Create GeoJSON from polygon.

        Parameters:
        -----------
        polygon : shapely.geometry.Polygon
            Generated polygon
        metadata : dict
            Generation metadata
        granule_ur : str
            Granule identifier

        Returns:
        --------
        dict : GeoJSON FeatureCollection


        Create visual summary for a granule.

        Parameters:
        -----------
        output_dir : Path
            Output directory
        granule_ur : str
            Granule identifier
        lon, lat : arrays
            Data coordinates
        cmr_geojson : dict
            CMR polygon GeoJSON
        generated_geojson : dict
            Generated polygon GeoJSON
        metrics : dict
            Comparison metrics
        metadata : dict
            Generation metadata


        Create summary report for entire collection.

        Parameters:
        -----------
        output_dir : Path
            Output directory
        short_name : str
            Collection short name
        results : list
            Processing results for all granules


        Create visualization of aggregate metrics.

        Parameters:
        -----------
        output_dir : Path
            Output directory
        results : list
            Processing results


    Sanitize granule UR for use as filename/directory name.

    Parameters:
    -----------
    granule_ur : str
        Granule UR to sanitize

    Returns:
    --------
    str : Sanitized granule UR safe for filesystem use


        Extract polygon information from UMM-G JSON.

        Parameters:
        -----------
        umm_json : dict
            UMM-G JSON response
        granule_ur : str, optional
            Granule UR for error messages

        Returns:
        --------
        dict : GeoJSON FeatureCollection


        Extract data file URLs from UMM-G JSON.

        Parameters:
        -----------
        umm_json : dict
            UMM-G JSON response

        Returns:
        --------
        list : List of data URLs


        Find the first data file URL matching the given extensions.

        Parameters:
        -----------
        urls : list
            List of URLs to search
        extensions : list, optional
            List of file extensions to match (e.g., ['.h5', '.HDF5'])

        Returns:
        --------
        str or None : First matching URL or None


        Compare CMR and generated polygons.

        Parameters:
        -----------
        cmr_geojson : dict
            CMR polygon as GeoJSON
        generated_geojson : dict
            Generated polygon as GeoJSON
        data_points : array-like, optional
            Nx2 array of [lon, lat] points for coverage calculation

        Returns:
        --------
        dict : Comparison metrics


        Calculate what percentage of data points are covered by the polygon.

        Parameters:
        -----------
        polygon : shapely.geometry.Polygon
            The polygon to test
        data_points : array-like
            Nx2 array of [lon, lat] data points
        sample_size : int
            Maximum number of points to sample for performance

        Returns:
        --------
        float : Coverage ratio (0.0 to 1.0)


        Download and process n OLVIS1A granules.

        Parameters:
        -----------
        n_granules : int
            Number of granules to process


        Process a single granule.

        Parameters:
        -----------
        granule_result : earthaccess.results.DataGranule
            Earthaccess granule result


        Extract coordinates from UMM-G metadata.

        Parameters:
        -----------
        umm_json : dict
            UMM-G metadata

        Returns:
        --------
        tuple : (lon_array, lat_array) or (None, None)


        Generate premet file for granule.

        Parameters:
        -----------
        data_file : Path
            Data file path
        umm_json : dict
            UMM-G metadata


        Generate spatial file with lon/lat coordinates.

        Parameters:
        -----------
        data_file : Path
            Data file path
        lon : array
            Longitude values
        lat : array
            Latitude values


A dictionary that includes all of the parameters of the system.

        This is an immutable property, and will raise an error if you try to set it.
        If altering parameters that would affect a lightcurve, pass those as a
        dictionary to the :func:`lightcurve` method. If altering the data or times at
        which to generate the lightcurve, just define a new system with those values.

        Returns:
            dict:
            A dictionary of all the parameters of the system, including those specified
            by the user, default values, and those calculated by combinations of the
            two.


Visualize the layout of the system at one or more times.

        This method, if run in a jupyter notebook, will display a plot of some
        combination of the star, planet, and its orbit. It can color in the planet
        according to its reflected or emission profile, and the star according to its
        limb darkening profile. Helpful for checking the orientation of planet hotspots
        and/or its orientation after deformation.

        Args:
            times (array-like, [Days], default=None):
                The times at which to illustrate the system. The gap between times is
                assumed to be in units of days, but any zero-point/standard system
                (e.g. BJD) will work. Provide either this or ``true_anomalies`` but not
                both.
            true_anomalies (array-like, [Radian], default=None):
                The true anomalies at which to illustrate the system. Provide either
                this or ``times`` but not both.
            orbit (bool, default=True):
                Whether to plot a trace of the planet's orbital path
            reflected (bool, default=False):
                Whether to color in the planet according to its reflected flux profile.
                Can optionally include this or ``emitted`` but not both.
            emitted (bool, default=False):
                Whether to color in the planet according to its emitted flux profile.
                Can optionally include this or ``reflected`` but not both.
            star_fill (bool, default=True):
                Whether to color in the star according to its limb darkening profile.
                Note that the lowest color contour is bounded at zero, so if you have an
                unphysical limb darkening law where some radii are negative, those
                will appear as gaps (most often the contours will not reach the black
                outline of the star, which is always drawn).
            window_size (float, [Rstar], default=0.4):
                The size of the plotting window. The window will be centered on the
                mean position of the planet across all of the suggested times, unless
                ``star_centered`` is set to ``True``.
            star_centered (bool, default=False):
                Whether to center the plot on the star rather than the planet.
            nsamples (int, default=50_000):
                The number of random samples of the planet's surface to draw when
                illustrating the system. A larger number will increase the resolution
                of the plot but result in longer computation times.
            figsize (tuple, default=(8, 8)):
                The size of the figure to display. Passed directly to
                ``matplotlib.pyplot.subplots``.

        Returns:
            None:
            This method is used for its side effects of displaying a plot, not for its
            return value.


Convert a stellar limb darkening profile to a polynomial representation.

        Given a set of stellar parameters, one can use a grid of stellar models to compute
        the limb darkening profile as a function of projected `r` or of
        `mu = sqrt(1 - r**2)`. These profiles are often then approximated with one of a few
        common limb darkening "laws", such as the quadratic or 4-parameter non-linear laws.
        Since `squishyplanet` only supports polynomial limb darkening profiles, but can
        support nearly arbitrary orders, we can approximate the profile with a polynomial.
        This is a convenience function for converting between a grid-derived profile and its
        best-fit polynomial representation in the correct basis for `squishyplanet`.

        Args:
            intensities (array-like):
                The relative intensities of the star at a given `mu` or `r`.
            order (int):
                The order of the polynomial to fit to the limb darkening profile. Note that
                in the `squishyplanet` basis, the polynomial is defined as
                `1 - sum_{i=1}^{order} u_i (1 - mu)^i`, so the number of coefficients is
                `order`, not `order+1`.
            mus (array-like, default=None):
                The `mu` values at which the intensities were computed. If `rs` is not
                provided, this is required.
            rs (array-like, default=None):
                The `r` values at which the intensities were computed. If `mus` is not
                provided, this is required.

        Returns:
            array-like:
                The coefficients of the polynomial representation of the limb darkening
                profile. These can then be used as the `ld_u_coeffs` parameter in
                `OblateSystem`.


Compute the limb darkening profile of the star at a given radius.

        Meant as a helper function for sanity checks and plotting, especially if you're
        using higher-order limb darkening laws and are concerned if the profile is
        positive/monotonic.

        Args:
            ld_u_coeffs (array-like, default=self.state["ld_u_coeffs"]):
                The coefficients of the polynomial limb darkening law.
            r (float or array-like, default=None):
                The radius at which to compute the limb darkening profile. Must be
                between 0 and 1. If provided, ``mu`` should be ``None``.
            mu (float or array-like, default=None):
                The cosine of the angle between the line of sight and the normal to the
                surface of the star. Must be between 0 and 1. If provided, ``r`` should
                be ``None``.

        Returns:
            Array:
                The limb darkening profile of the star at the given r or mu values.


Compute the light curve of the system.

        This method will return the light curve of the system at the times specified
        when the system was initialized. If you want to compute the light curve at
        different times, or with different orbital parameters, you can pass those
        parameters as a dictionary to this method.

        The first time this is run for a given system, JAX will jit-compile the
        function, which can take some time. Subsequent calls will be much faster unless
        you change the shape of any of the input arrays (e.g., changing the number of
        times or the order of the polynomial limb darkening law). In those cases, or if
        changing any of boolean flags, JAX will need to re-compile the function again.

        Args:
            params (dict, default={}):
                A dictionary of parameters to update in the system state. Any keys
                not provided will be pulled from the current state of the system.

        Returns:
            Array: The timeseries lightcurve of the system. The length will be equal to
            `state["times"]`, and each index corresponds to a time in that array.

        Examples:
            >>> state = {
                    "t_peri" : 0.0,
                    "times" : jnp.linspace(-jnp.pi, 2*jnp.pi, 3504),
                    "a" : 2.0,
                    "period" : 2*jnp.pi,
                    "r" : 0.1,
                    "compute_reflected_phase_curve" : True,
                    "compute_emitted_phase_curve" : True,
                    "emitted_scale" : 1e-5,
                }
            >>> system = OblateSystem(**state)
            >>> system.lightcurve()


Compute the log likelihood of the system given the observed data and some set of
        parameters.

        This method will call :func:`lightcurve` with the provided parameters and
        compare the output to the observed data. The likelihood is assumed to be
        Gaussian with no correlation between times. The jitter term is added in
        quadrature to the provided uncertainties.

        Args:
            params (dict, default={}):
                A dictionary of parameters to update in the system state. Any keys
                not provided will be pulled from the current state of the system.

        Returns:
            float:
            The log likelihood of the system given the observed data and the
            provided parameters.


Convert a stellar limb darkening profile to a polynomial representation.

    Given a set of stellar parameters, one can use a grid of stellar models to compute
    the limb darkening profile as a function of projected `r` or of
    `mu = sqrt(1 - r**2)`. These profiles are often then approximated with one of a few
    common limb darkening "laws", such as the quadratic or 4-parameter non-linear laws.
    Since `squishyplanet` only supports polynomial limb darkening profiles, but can
    support nearly arbitrary orders, we can approximate the profile with a polynomial.
    This is a convenience function for converting between a grid-derived profile and its
    best-fit polynomial representation in the correct basis for `squishyplanet`.

    Args:
        intensities (array-like):
            The relative intensities of the star at a given `mu` or `r`.
        order (int):
            The order of the polynomial to fit to the limb darkening profile. Note that
            in the `squishyplanet` basis, the polynomial is defined as
            `1 - sum_{i=1}^{order} u_i (1 - mu)^i`, so the number of coefficients is
            `order`, not `order+1`.
        mus (array-like, default=None):
            The `mu` values at which the intensities were computed. If `rs` is not
            provided, this is required.
        rs (array-like, default=None):
            The `r` values at which the intensities were computed. If `mus` is not
            provided, this is required.

    Returns:
        array-like:
            The coefficients of the polynomial representation of the limb darkening
            profile. These can then be used as the `ld_u_coeffs` parameter in
            `OblateSystem`.


Linear limb-darkening law.

    .. math::
        \\frac{I(\\mu)}{I(\\mu = 1)} = 1 - u_1 (1 - \\mu)

    Args:
        u1 (float): Linear limb-darkening coefficient
        return_profile (bool, default=False):
            Whether to return a dictionary describing the intensity profile

    Returns:
        Array:
            u coefficients used by squishyplanet to compute the intensity profile
            (here it's just [u1, 0], since we always need at least two coefficients)
        dict (if return_profile=True):
            Dictionary describing the intensity profile


Quadratic limb-darkening law.

    .. math::
        \\frac{I(\\mu)}{I(\\mu = 1)} = 1 - u_1 (1 - \\mu) - u_2 (1 - \\mu)^2

    Args:
        u1 (float): Linear limb-darkening coefficient
        u2 (float): Quadratic limb-darkening coefficient
        return_profile (bool, default=False):
            Whether to return a dictionary describing the intensity profile

    Returns:
        Array:
            u coefficients used by squishyplanet to compute the intensity profile
            (here it's just [u1, u2], without modification: this is a silly function
            included only to give a consistent interface to the limb-darkening laws)
        dict (if return_profile=True):
            Dictionary describing the intensity profile


Kipping limb-darkening law.

    A restriction of the quadratic law from Kipping 2013 that guaratees a monotonic
    increasing intensity profile

    .. math::
        \\frac{I(\\mu)}{I(\\mu = 1)} = 1 - u_1 (1 - \\mu) - u_2 (1 - \\mu)^2

    where

    .. math::
        u_1 = 2 \\sqrt{q_1} q_2

    .. math::
        u_2 = \\sqrt{q_1} (1 - 2 q_2)

    Args:
        q1 (float):
            Kipping limb-darkening coefficient
        q2 (float):
            Kipping limb-darkening coefficient
        return_profile (bool, default=False):
            Whether to return a dictionary describing the intensity profile

    Returns:
        Array or dict:
            If `return_profile` is False, returns an array of `u` coefficients used by
            squishyplanet to compute the intensity profile.

            If `return_profile` is True, returns a dictionary describing the intensity
            profile.


Square root limb-darkening law.

    .. math::
        \\frac{I(\\mu)}{I(\\mu = 1)} = 1 - u_1 (1 - \\mu) - u_2 (1 - \\sqrt{\\mu})

    Args:
        u1 (float): Linear limb-darkening coefficient
        u2 (float): Square root limb-darkening coefficient
        order (int, default=5): Order of the polynomial fit to the intensity profile
        return_profile (bool, default=False):
            Whether to return a dictionary describing the intensity profile

    Returns:
        Array:
            u coefficients of the least-squares polynomial fit to the intensity profile
            created by the limb-darkening law across a dense grid of mu values
        dict (if return_profile=True):
            Dictionary describing the intensity profile


Non-linear 3-parameter limb-darkening law.

    .. math::
        \\frac{I(\\mu)}{I(\\mu = 1)} = 1 - u_1 (1 - \\mu) - u_2 (1 - \\mu^{1.5}) - u_3 (1 - \\mu^2)

    Args:
        u1 (float): Linear limb-darkening coefficient
        u2 (float): Square root limb-darkening coefficient
        u3 (float): Square limb-darkening coefficient
        order (int, default=5): Order of the polynomial fit to the intensity profile
        return_profile (bool, default=False):
            Whether to return a dictionary describing the intensity profile

    Returns:
        Array:
            u coefficients of the least-squares polynomial fit to the intensity profile
            created by the limb-darkening law across a dense grid of mu values
        dict (if return_profile=True):
            Dictionary describing the intensity profile


Non-linear 4-parameter limb-darkening law.

    .. math::
        \\frac{I(\\mu)}{I(\\mu = 1)} = 1 - u_1 (1 - \\mu^{0.5}) - u_2 (1 - \\mu) - u_2 (1 - \\mu^{1.5}) - u_3 (1 - \\mu^2)

    Args:
        u1 (float): Linear limb-darkening coefficient
        u2 (float): Square root limb-darkening coefficient
        u3 (float): Square limb-darkening coefficient
        u4 (float): Square limb-darkening coefficient
        order (int, default=5): Order of the polynomial fit to the intensity profile
        return_profile (bool, default=False):
            Whether to return a dictionary describing the intensity profile

    Returns:
        Array:
            u coefficients of the least-squares polynomial fit to the intensity profile
            created by the limb-darkening law across a dense grid of mu values
        dict (if return_profile=True):
            Dictionary describing the intensity profile


A helper function for :func:`poly_to_parametric`.

    Args:
        rho_xx (Array [Dimensionless]): Coefficient of x^2
        rho_xy (Array [Dimensionless]): Coefficient of xy
        rho_x0 (Array [Dimensionless]): Coefficient of x
        rho_yy (Array [Dimensionless]): Coefficient of y^2
        rho_y0 (Array [Dimensionless]): Coefficient of y
        rho_00 (Array [Dimensionless]): Constant term

    Returns:
        Tuple:
            - r1 (Array [Rstar]): Semi-major axis of the projected ellipse
            - r2 (Array [Rstar]): Semi-minor axis of the projected ellipse
            - xc (Array [Rstar]): x-coordinate of the center of the ellipse
            - yc (Array [Rstar]): y-coordinate of the center of the ellipse
            - cosa (Array [Dimensionless]): Cosine of the rotation angle
            - sina (Array [Dimensionless]): Sine of the rotation angle


Convert between the coefficients that describe an implicit to those
    defining a parametric ellipse.

    The input coefficients are those of the implicit ellipse equation:

    .. math::
        \\rho_{xx} x^2 + \\rho_{xy} xy + \\rho_{x0} x + \\rho_{yy} y^2 + \\rho_{y0} y + \\rho_{00} = 1

    Args:
        rho_xx (Array [Dimensionless]): Coefficient of x^2
        rho_xy (Array [Dimensionless]): Coefficient of xy
        rho_x0 (Array [Dimensionless]): Coefficient of x
        rho_yy (Array [Dimensionless]): Coefficient of y^2
        rho_y0 (Array [Dimensionless]): Coefficient of y
        rho_00 (Array [Dimensionless]): Constant term

    Returns:
        dict:
            Dictionary of coefficients for the parametric ellipse. The ellipse can now
            be described by the following parametric equations for parameter :math:`\\alpha`:

            .. math::
                x = c_{x1} * \\cos(\\alpha) + c_{x2} * \\sin(\\alpha) + c_{x3}
                y = c_{y1} * \\cos(\\alpha) + c_{y2} * \\sin(\\alpha) + c_{y3}


Given a set of x and y coordinates corresponding to the intersection of the planet
    and star, compute the angle :math:`\\alpha` that corresponds to each point.

    Here, :math:`\\alpha` is the parameter in the parametric equations of the ellipse.
    See :func:`poly_to_parametric` for more details.

    Args:
        xs (Array [Rstar]): x-coordinates of the intersection points
        ys (Array [Rstar]): y-coordinates of the intersection points
        c_x1 (Array [Dimensionless]): Coefficient of x^2
        c_x2 (Array [Dimensionless]): Coefficient of xy
        c_x3 (Array [Dimensionless]): Coefficient of x
        c_y1 (Array [Dimensionless]): Coefficient of y^2
        c_y2 (Array [Dimensionless]): Coefficient of y
        c_y3 (Array [Dimensionless]): Constant term

    Returns:
        Array [Rstar]: The angle :math:`\\alpha` corresponding to each intersection point


Calculate and return the coefficients that describe the planet as an implicit
    surface in 3D space as a function of its orbital state.

    This function computes a dictionary of coefficients related to the 3D position
    and orientation of a planet given its orbital and rotational characteristics. All
    inputs must be jnp.ndarrays. They can either be shape (1,) or (N,) with 1 unique
    N allowed per call (e.g., everything but f and prec are single-valued, but those
    two are length N).

    Args:
        a (Array [Rstar]):
            Semi-major axis of the orbit in units of R_star
        e (Array [Dimensionless]):
            Eccentricity of the orbit
        f (Array [Radian]):
            True anomaly, the angle between the direction of periapsis and the current
            position of the planet as seen from the star.
        Omega (Array [Radian]):
            Longitude of the ascending node
        i (Array [Radian]):
            Orbital inclination
        omega (Array [Radian]):
            Argument of periapsis
        r (Array [Rstar]):
            Equatorial radius of the planet, in units of R_star
        obliq (Array [Radian]):
            Obliquity, the angle between the planet's orbital plane and its equatorial
            plane. Defined when the planet is at periapsis with an Omega of zero as a
            rotation around the sky-frame y-axis, such that a positive obliquity tips
            the planet's north pole away from the star.
        prec (Array [Radian]):
            Precession angle, or equivalently the longitude of ascending node of the
            planet's equatorial plane. This is defined at periapsis with an Omega of
            zero as a rotation about the sky-frame z-axis.
        f1 (Array [Dimensionless]):
            The flattening coefficient of the planet that describes the compression
            along the planet's polar axis. A value of 0.0 indicates no flattening.
        f2 (Array [Dimensionless]):
            The flattening coefficient of the planet that describes the compression
            along the planet's "y" axis, the vector in its equatorial plane that is
            perpendicular to the direction of motion at periapsis, assuming the 0.0
            obliquity.
        **kwargs:
            Unused additional keyword arguments. These are included so that we can can
            take in a larger state dictionary that includes all of the required
            parameters along with other unnecessary ones.

    Returns:
        dict:
            A dictionary with keys representing different coefficient names and
            their corresponding values. The coeffients satisfy the implicit equation:

            .. math::
                p_{xx} x^2 + p_{xy} xy + p_{xz} xz + p_{x0} x + p_{yy} y^2 + p_{yz} yz + p_{y0} y + p_{zz} z^2 + p_{z0} z + p_{00} = 1


Generate a set of points uniformly distributed on the portion of the star visible
    from the planet.

    This takes a spherical cap of points centered on the north pole of the star,
    squishes them together so they'd all be visible from the planet, and then rotates
    them to be centered on the sub-planet point on the star.

    Args:
        a (Array [Rstar]):
            Semi-major axis of the orbit in units of R_star
        e (Array [Dimensionless]):
            Eccentricity of the orbit
        f (Array [Radian]):
            True anomaly, the angle between the direction of periapsis and the current
            position of the planet as seen from the star.
        Omega (Array [Radian]):
            Longitude of the ascending node
        i (Array [Radian]):
            Orbital inclination
        omega (Array [Radian]):
            Argument of periapsis
        extended_illumination_points (Array):
            A set of points lying on a unit hemisphere centered at the origin that are
            evenly distributed across the projected disk when viewed from above. Created
            in during initialization of an :class:`OblateSystem` object.
        **kwargs:
            Unused additional keyword arguments. These are included so that we can can
            take in a larger state dictionary that includes all of the required
            parameters along with other unnecessary ones.

    Returns:
        Array:
            A set of points on the star that are visible from the planet, evenly
            distributed across the projected disk of the star as seen by the planet, and
            centered on the sub-planet point on the star.


Generate many sets of p coefficients that describe same planet offset from its
    true position by different amounts.

    Since the star is not actually a point source, we slightly underestimate the
    area of the illuminated portion of the planet. The limb of the star can "see around
    the horizon", and this extra illumination will affect the reflected portion of a
    phase curve. To crudely account for this, we can break the star into many point
    sources distributed over the portion of the star that is visible from the planet,
    then add their resulting lightcurves. This isn't perfect for a few reasons: how
    should we distribute this point sources, and how should we weight them? Also, for
    a non-spherical planet, what do we mean by "the portion of the star that is visible
    from the planet"? For now, we avoid those questions by assigning equal intensities
    to a set of points distributed uniformly over the portion of the hemisphere of the
    star that would be visible to an observer at the center of the planet.

    Args:
        a (Array [Rstar]):
            Semi-major axis of the orbit in units of R_star
        e (Array [Dimensionless]):
            Eccentricity of the orbit
        f (Array [Radian]):
            True anomaly, the angle between the direction of periapsis and the current
            position of the planet as seen from the star.
        Omega (Array [Radian]):
            Longitude of the ascending node
        i (Array [Radian]):
            Orbital inclination
        omega (Array [Radian]):
            Argument of periapsis
        r (Array [Rstar]):
            Equatorial radius of the planet, in units of R_star
        obliq (Array [Radian]):
            Obliquity, the angle between the planet's orbital plane and its equatorial
            plane. Defined when the planet is at periapsis with an Omega of zero as a
            rotation around the sky-frame y-axis, such that a positive obliquity tips
            the planet's north pole away from the star.
        prec (Array [Radian]):
            Precession angle, or equivalently the longitude of ascending node of the
            planet's equatorial plane. This is defined at periapsis with an Omega of
            zero as a rotation about the sky-frame z-axis.
        f1 (Array [Dimensionless]):
            The flattening coefficient of the planet that describes the compression
            along the planet's polar axis. A value of 0.0 indicates no flattening.
        f2 (Array [Dimensionless]):
            The flattening coefficient of the planet that describes the compression
            along the planet's "y" axis, the vector in its equatorial plane that is
            perpendicular to the direction of motion at periapsis, assuming the 0.0
            obliquity.
        offsets (Array [Rstar]):
            An array of offsets from the planet's true position. Each offset is a
            3-element array representing the x, y, and z offsets from the planet's
            true position in units of R_star. Used when splitting the star into many
            point sources to account for extended illumination.
        **kwargs:
            Unused additional keyword arguments. These are included so that we can can
            take in a larger state dictionary that includes all of the required
            parameters along with other unnecessary ones.

    Returns:
        dict:
            A dictionary similar to the one returned by :func:`planet_3d_coeffs`, but
            with now describing the planet after being translated by the provided
            offsets.


Compute the coefficients that describe the planet as an implicit 2D surface from the
    observer's perspective.

    This function transforms the coefficients that describe the planet's 3D shape into
    a new set of coefficients that describe its projected outline as seen from
    z=infinity. The input p coefficients satisfy the equation:

    .. math::
                p_{xx} x^2 + p_{xy} xy + p_{xz} xz + p_{x0} x + p_{yy} y^2 + p_{yz} yz + p_{y0} y + p_{zz} z^2 + p_{z0} z + p_{00} = 1

    Args:
        p_xx (Array): Coefficient representing xx interaction in the 3D model.
        p_xy (Array): Coefficient representing xy interaction in the 3D model.
        p_xz (Array): Coefficient representing xz interaction in the 3D model.
        p_x0 (Array): Coefficient representing x0 interaction in the 3D model.
        p_yy (Array): Coefficient representing yy interaction in the 3D model.
        p_yz (Array): Coefficient representing yz interaction in the 3D model.
        p_y0 (Array): Coefficient representing y0 interaction in the 3D model.
        p_zz (Array): Coefficient representing zz interaction in the 3D model.
        p_z0 (Array): Coefficient representing z0 interaction in the 3D model.
        p_00 (Array): Coefficient representing 00 interaction in the 3D model.
        **kwargs: Unused additional keyword arguments. These are included to allow for
                flexibility in providing additional data that may be ignored during the
                computation but included for interface consistency.

    Returns:
        dict:
            A dictionary with keys representing different transformed coefficient
            names ('rho_xx', 'rho_xy', 'rho_x0', 'rho_yy', 'rho_y0', 'rho_00') and
            their corresponding values. These coefficients describe the outline of the
            planet as an implicit curve that satisfies the equation:

            .. math::
                \\rho_{xx} x^2 + \\rho_{xy} xy + \\rho_{x0} x + \\rho_{yy} y^2 + \\rho_{y0} y + \\rho_{00} = 1


Solve Kepler's equation to compute the true anomaly.

    This implementation is based on that within `jaxoplanet <https://github.com/exoplanet-dev/jaxoplanet/>`_, many thanks to the authors.

    Args:
        M (Array [Radian]): Mean anomaly
        ecc (Array [Dimensionless]): Eccentricity

    Returns:
        Array: True anomaly in radians


Compute the cartesian coordinates of the center of the planet in the sky frame
    given its orbital elements.

    Args:
        a (Array [Rstar]): Semi-major axis of the orbit in units of R_star
        e (Array [Dimensionless]): Eccentricity of the orbit
        f (Array [Radian]): True anomaly, the angle between the direction of periapsis
                            and the current position of the planet as seen from
                            the star.
        Omega (Array [Radian]): Longitude of the ascending node
        i (Array [Radian]): Orbital inclination
        omega (Array [Radian]): Argument of periapsis
        **kwargs: Unused additional keyword arguments. These are included so that we
            can can take in a larger state dictionary that includes all of the
            required parameters along with other unnecessary ones.

    Returns:
        Array: The cartesian coordinates of the planet in the sky frame. Shape [3, N].


Computes the true anomaly at the instant of minimum star/planet separation.

    Uses equations 4.12-4.18 of
    `Kipping 2011 <https://ui.adsabs.harvard.edu/abs/2011PhDT.......294K/abstract>`_
    to compute the true anomaly at the instant of minimum star/planet separation.

    Args:
        e (Array [Dimensionless]): Eccentricity of the orbit
        i (Array [Radian]): Orbital inclination
        omega (Array [Radian]): Argument of periapsis

    Returns:
        Array: True anomaly at the instant of minimum star/planet separation in radians.


Convert from the alternative sky-projected parameterization to the same format
    used by the 3D parameterization.

    A good chunk of the code assumes that the planet's center is determined by the
    orbital elements and that it's outline is derived from an equatorial radius ``r``,
    a z-flattening ``f1``, a y-flattening ``f2``, and two body-centered rotations
    ``obliq`` and ``prec``. This are useful to have when working with phase curves that
    are sensitive to the actual 3D orientation of the planet, but when dealing with
    transits only, this parameterization is overkill and allows a bunch of degeneracies.
    So, if only doing transits, it is more convenient to parameterize the planet by
    its projected radius in the x and y directions, and the angle of the projected
    ellipse. This function takes in those parameters and returns the same dictionaries
    you'd get if you fed a full 3D parameterization into
    :func:`planet_2d.planet_2d_coeffs`.

    Args:
        projected_r (float): The projected "x" radius of the planet.
        projected_f (float): The flattening of the projected ellipse.
        projected_theta (float): The angle of the projected ellipse.

    Returns:
        tuple:
            A tuple of two dictionaries. The first dictionary contains the coefficients
            of the quadratic equation that describes the projected ellipse. The second
            dictionary contains coefficients that describe the parametric form of that
            same ellipse.


Compute the "solution vector" for a 1D path across the star that lies on the outline
    of the planet.

    This computes Eq. 21 of `Agol, Luger, and Foreman-Mackey 2020
    <https://ui.adsabs.harvard.edu/abs/2020AJ....159..123A/abstract>`_. But, instead of
    doing it analytically, this uses the ``quadax`` package to numerically solve the
    required integrals. For terms s_2 and higher, this is straightforward to do based on
    the equations in the paper: we simply parameterize the outline of the planet by
    some angle :math:`\\alpha`, then numerically integrate the dot product of Eq. 62
    with that parameterization between the two endpoints of the path. For the first two
    lower-order terms however, Agol et al. do not provide an equivalent of Eq. 62 and
    instead provide only the analytic solutions. We therefore use the following as the
    equivalents for Eq. 62 for these terms:

    .. math::

        G_0 = \\{0, x\\}

    .. math::

        G_1 = \\left\\{0, \\frac{1}{2} \\left(x \\sqrt{-x^2-y^2+1}-\\left(y^2-1\\right) \\tan ^{-1}\\left(\\frac{x}{\\sqrt{-x^2-y^2+1}}\\right)\\right)+\\frac{\\pi }{12} \\right\\}

    These expressions were derived by solving the required PDE in Eq. 14 with the
    boundary conditions from Eq. 27. Finally, the C coefficients here describe the
    parametric form of the planet's outline as seen by the observer, and they satisfy:

    .. math::

        x = c_{x1} \\cos(\\alpha) + c_{x2} \\sin(\\alpha) + c_{x3}

        y = c_{y1} \\cos(\\alpha) + c_{y2} \\sin(\\alpha) + c_{y3}

    for some angle :math:`\\alpha \\in [0, 2\\pi)`.

    Args:
        a (float):
            The starting parameter for the path along the planet's outline,
            :math:`\\alpha_0`.
        b (float):
            The ending parameter for the path along the planet's outline,
            :math:`\\alpha_1`.
        g_coeffs (Array):
            The system-specific limb darkening coefficients in the Green's basis.
            Computed by multiplying the u coefficients with the change of basis matrix
            from :func:`greens_basis_transform.generate_change_of_basis_matrix`.
        c_x1 (float):
            The first coefficient of the parametric 2D outline of the planet.
        c_x2 (float):
            The second coefficient of the parametric 2D outline of the planet.
        c_x3 (float):
            The third coefficient of the parametric 2D outline of the planet.
        c_y1 (float):
            The fourth coefficient of the parametric 2D outline of the planet.
        c_y2 (float):
            The fifth coefficient of the parametric 2D outline of the planet.
        c_y3 (float):
            The sixth coefficient of the parametric 2D outline of the planet.

    Returns:
        Array:
            The solution vector for the path along the planet's outline. The shape will
            match that of the input ``g_coeffs``.


Compute the "solution vector" for a 1D path across the star that lies on the edge
    of the star itself.

    This is equivalent to :func:`planet_solution_vec`, but instead of integrating over
    paths that lie on the planet's outline, we integrate over paths that lie on the edge
    of the star. As pointed out in the paragraph following Eq. 69 in `Agol, Luger, and
    Foreman-Mackey 2020 <https://ui.adsabs.harvard.edu/abs/2020AJ....159..123A/abstract>`_,
    the contribution of all terms higher than :math:`G_1` will be zero in this case
    since we have limited ourselves to :math:`z=0` by remaining on the star's boundary.
    This simplifies things somewhat, though we do still have to numerically integrate
    the dot product of the parametric form of the star's outline with the :math:`G_0`
    and :math:`G_1` terms written out in :func:`planet_solution_vec`. Technically we
    probably could use the analytic solutions for these terms, but so far we have not.

    Args:
        a (float):
            The starting parameter for the path along the star's outline,
            :math:`\\alpha_0`. Note: here :math:`\\alpha` is the angle parameterizing
            the path on the *planet's* outline, not the star's, even though the path
            we will integrate over is on the star. We convert to the relevant parameters
            internally.
        b (float):
            The ending parameter for the path along the star's outline,
            :math:`\\alpha_1`.
        g_coeffs (Array):
            The system-specific limb darkening coefficients in the Green's basis.
            Computed by multiplying the u coefficients with the change of basis matrix
            from :func:`greens_basis_transform.generate_change_of_basis_matrix`.
        c_x1 (float):
            The first coefficient of the parametric 2D outline of the planet.
        c_x2 (float):
            The second coefficient of the parametric 2D outline of the planet.
        c_x3 (float):
            The third coefficient of the parametric 2D outline of the planet.
        c_y1 (float):
            The fourth coefficient of the parametric 2D outline of the planet.
        c_y2 (float):
            The fifth coefficient of the parametric 2D outline of the planet.
        c_y3 (float):
            The sixth coefficient of the parametric 2D outline of the planet.

    Returns:
        Array:
            The solution vector for the path along the planet's outline. The shape will
            match that of the input ``g_coeffs``.


The main function for computing a transit light curve.

    This function will return a 1-D array representing the flux recieved from the star,
    where each entry corresponds to a time in the input `state` dictionary. It first
    transforms the `state` into the implicit 3D surface of the planet, the implicit 2D
    sky-projected outline of the planet, and a parametric form of that outline for each
    time step. These are vectorized operations that are computed simulataneously across
    all times. It then solves for the intersection points of the planet and star, and
    if the planet is either partially or fully transiting, numerically solves the
    required 1D integrals that leverage Green's Theorem to compute the blocked flux. The
    flux-blocking calculations are done sequentially for each timestep using
    ``jax.lax.scan``, which seemed to be more efficient than vectorizing again while
    switching between braches with something like ``jax.lax.cond``. Keep these different
    behaviors in mind when computing dense lightcurves with ~100s of thousands of time
    steps: the first part will require enough memory to compute and store ~30 values for
    each step, but then the actual 1D integrals will be computed sequentially.

    Args:
        state (dict):
            A dictionary containing all of the keys that are included in an
            :func:`OblateSystem` ``state`` attribute.
        parameterize_with_projected_ellipse (bool):
            If ``True``, the planet's outline will be parameterized by the projected
            ellipse as seen by the observer. If ``False``, the planet's outline will be
            set by the full 3D parameterization of the planet. When dealing with planets
            that are not tidally locked and/or far from their host star and/or very
            close to spherical, you won't be able to tell the difference between these
            two parameterizations since the projected area won't be changing. In that
            case, it's better to use the simpler 2D parameterization to avoid the
            degeneracies and extra computation that can arise from the 3D
            parameterization. This argument is static for the JIT-compiled function.

    Returns:
        Array:
            The flux received from the star at each time step for the times included as
            ``state["times"]``.


Return the coefficient multiplying `term` in `expression`.


Return the change of basis matrix A1.


Return the n^th term in the Green's basis.


Return the polynomial basis representation of the Green's polynomial `g`.


Return the change of basis matrix A2. The columns of the **inverse** of this matrix are given by `p_G`.


Generate the change of basis matrix to convert limb darking u coefficients to
    Green's basis coefficients.

    This function is only run once per system, though the resulting matrix is used
    repeatedly in the light curve calculation. It implements Eq. 17 of `Agol, Luger, and
    Foreman-Mackey 2020 <https://ui.adsabs.harvard.edu/abs/2020AJ....159..123A/abstract>`_.

    Args:
        N (int): The order of the polynomial limb darkening law.

    Returns:
        Array:
            The change of basis matrix. When solving for the blocked flux, this will
            be multiplied by the u limb darkening coefficients to convert them to the
            Green's basis.


Create a random set of radii and thetas for sampling the planet's surface.

    These are uniformly distributed through a unit circle and will be scaled and rotated
    to match the planet's shape and orientation at each timestep. However, they will
    be re-used at every time step, which could introduce a bias but makes things much
    faster. Be sure you use sufficient samples to keep the bias small, then try multiple
    random keys to quantify it.

    Args:
        key (Array): A jax.random.PRNGKey for generating random numbers.
        num_points (int): The number of points to generate.

    Returns:
        Tuple:
            A tuple of two arrays, the first containing the radii and the second
            containing the thetas.


Convert randomly sampled :math:`(x, y)` points on the projected planet to
    :math:`(x, y, z)` points on the planet's surface.

    The :math:`rho` coefficients are calculated with :func:`planet_2d.planet_2d_coeffs`,
    the :math:`p` coefficients are calculated with :func:`planet_3d.planet_3d_coeffs`,
    and the sample points are generated with :func:`generate_sample_radii_thetas`.

    Args:
        sample_radii (Array): The radii of the sampled points.
        sample_thetas (Array): The angles of the sampled points.
        rho_xx (Array): xx coefficient in the 2D implicit representation.
        rho_xy (Array): xy coefficient in the 2D implicit representation.
        rho_x0 (Array): x0 coefficient in the 2D implicit representation.
        rho_yy (Array): yy coefficient in the 2D implicit representation.
        rho_y0 (Array): y0 coefficient in the 2D implicit representation.
        rho_00 (Array): 00 coefficient in the 2D implicit representation.
        p_xx (Array): xx coefficient in the 3D implicit representation.
        p_xy (Array): xy coefficient in the 3D implicit representation.
        p_xz (Array): xz coefficient in the 3D implicit representation.
        p_x0 (Array): x0 coefficient in the 3D implicit representation.
        p_yy (Array): yy coefficient in the 3D implicit representation.
        p_yz (Array): yz coefficient in the 3D implicit representation.
        p_y0 (Array): y0 coefficient in the 3D implicit representation.
        p_zz (Array): zz coefficient in the 3D implicit representation.
        p_z0 (Array): z0 coefficient in the 3D implicit representation.
        p_00 (Array): 00 coefficient in the 3D implicit representation.
        **kwargs:
            Additional unused keyword arguments, included so that we can pass in
            a larger state dictionary that includes all of the required parameters along
            with other unnecessary ones.

    Returns:
        Tuple:
            A tuple of three arrays, the first containing the x values, the second
            containing the y values, and the third containing the z values.


Compute the unit normal vector to the planet's surface at a given point.

    The input :math:`(x, y, z)` points are assumed to lie on the planet's surface.

    Args:
        x (Array): The x values of the points.
        y (Array): The y values of the points.
        z (Array): The z values of the points.
        p_xx (Array): xx coefficient in the 3D implicit representation.
        p_xy (Array): xy coefficient in the 3D implicit representation.
        p_xz (Array): xz coefficient in the 3D implicit representation.
        p_x0 (Array): x0 coefficient in the 3D implicit representation.
        p_yy (Array): yy coefficient in the 3D implicit representation.
        p_yz (Array): yz coefficient in the 3D implicit representation.
        p_y0 (Array): y0 coefficient in the 3D implicit representation.
        p_zz (Array): zz coefficient in the 3D implicit representation.
        p_z0 (Array): z0 coefficient in the 3D implicit representation.
        p_00 (Array): 00 coefficient in the 3D implicit representation.

    Returns:
        Array:
            An array of shape (3, n) containing the unit normal vectors at each point.


A helper function to compute the cosine of the angle between the planet's surface
    normal vector and the vector linking the center of the planet to the star.

    This is an approximation that the star is a) a point source and b) that
    all light coming from the star is parallel. Neither of these are strictly true. The
    former could be handled the same way starry does it, by distributing point sources
    across the surface of the star and averaging. I don't know of any attempts to
    address the latter, though in principle it wouldn't be hard to do here since we're
    already doing so much numerically.

    Args:
        planet_surface_normal (Array): The unit normal vectors to the planet's surface.
        x_c (Array): The x coordinate of the center of the planet.
        y_c (Array): The y coordinate of the center of the planet.
        z_c (Array): The z coordinate of the center of the planet.
        **kwargs:
            Additional unused keyword arguments, included so that we can pass in
            a larger state dictionary that includes all of the required parameters along
            with other unnecessary ones.

    Returns:
        Array:
            The cosine of the angle between the planet's surface normal and the vector
            pointing from the planet's center to the star.


Compute the coefficients of the planet's 3D shape from the star's perspective,
    as if it were aligned the the :math:`z` axis.

    When computing the reflected flux from the planet, we need to know how much flux
    initial reaches it from the star. To do that, we need to know the planet's projected
    area as seen from the star, which importantly, could be different than the projected
    area as seen from the observer. To compute this area, we first use this function to
    get a 3D representation of the planet as seen from the star, then will use those
    coefficients to compute an implicit 2D representation, then will use those to get
    the area.

    The x_c, y_c, and z_c inputs are all technically encoded in the p inputs as well,
    but it was easier just to carry them around explicitly.

    Args:
        p_xx (Array): xx coefficient in the 3D implicit representation.
        p_xy (Array): xy coefficient in the 3D implicit representation.
        p_xz (Array): xz coefficient in the 3D implicit representation.
        p_x0 (Array): x0 coefficient in the 3D implicit representation.
        p_yy (Array): yy coefficient in the 3D implicit representation.
        p_yz (Array): yz coefficient in the 3D implicit representation.
        p_y0 (Array): y0 coefficient in the 3D implicit representation.
        p_zz (Array): zz coefficient in the 3D implicit representation.
        p_z0 (Array): z0 coefficient in the 3D implicit representation.
        p_00 (Array): 00 coefficient in the 3D implicit representation.
        x_c (Array): The x coordinate of the center of the planet.
        y_c (Array): The y coordinate of the center of the planet.
        z_c (Array): The z coordinate of the center of the planet.

    Returns:
        dict:
            A dictionary containing the coefficients of the planet's shape as seen from
            the star. Will look identical to the output of :func:`planet_3d.planet_3d_coeffs`.


Compute the reflected intensity at a specific point on the planet's surface assuming
    a simple Lambertian reflection model.

    This is a simple model that assumes the planet reflects light according to Lambert's
    cosine law, which states that the intensity of reflected light is proportional to
    the cosine of the angle between the surface normal and the illumination direction.
    That arrangement means it does *not* depend on the observer's viewing angle, only
    the illumination angle. This helper function also assumes a uniform albedo of 1
    across the planet's surface (the final reflected flux will be scaled by the provided
    albedo, though is still always assumed to be uniform).

    This function will also mask out any points on the planet's surface that are on the
    wrong side of the terminator.

    Args:
        surface_star_cos_angle (Array): The cosine of the angle between the planet's
            surface normal and the vector pointing from the planet's center to the star.
        x (Array): The x values of the points on the planet's surface.
        y (Array): The y values of the points on the planet's surface.
        z (Array): The z values of the points on the planet's surface.

    Returns:
        Array: The intensity of the reflected light at each point.


Compute the time-dependent normalization factor for the reflected light.

    The reflected light computations are almost entirely carried out assuming the star
    is a point source 1 R_star from the center of the planet emitting plane-parallel
    rays. To convert these to actual reflected flux, we need to a) correct for the
    distance between the planet and the star and b) account for how much area the planet
    actually subtends as seen from the star. a) is easy and common across all
    implementations, it's just the inverse square law. b) is more complicated for oblate
    planets than spherical planets however, since even on circular orbits, the subtended
    area (and consequently area that is recieves flux and and is able to reflect it) can
    change as a function of orbital phase. Note however that it will not vary with phase
    if the planet is tidally locked and always shows the same face to the star.

    Args:
        two (dict):
            A dictionary containing the rho coefficients of the planet's
            implicit 2D representation, as seen from the observer and calculated with
            :func:`planet_2d.planet_2d_coeffs`.
        three (dict):
            A dictionary containing the p coefficients of the planet's 3D shape, as seen
            from the observer and calculated with :func:`planet_3d.planet_3d_coeffs`.
        x_c (Array): The x coordinate of the center of the planet.
        y_c (Array): The y coordinate of the center of the planet.
        z_c (Array): The z coordinate of the center of the planet.
        xo (float or Array):
            An offset to add to the x coordinate of the center of the planet, used when
            correcting for extended source illuminations. Default is 0.0.
        yo (float or Array):
            An offset to add to the y coordinate of the center of the planet, used when
            correcting for extended source illuminations. Default is 0.0.
        zo (float or Array):
            An offset to add to the z coordinate of the center of the planet, used when
            correcting for extended source illuminations. Default is 0.0.
        **kwargs:
            Additional unused keyword arguments, included so that we can pass in
            a larger state dictionary that includes all of the required parameters along
            with other unnecessary ones.

    Returns:
        Array: The normalization factor for the reflected light.


Compute the timeseries of light reflected from  the planet.

    This function computes the reflected light from the planet at each time step. It
    assume the planet is a) a Lambertian reflector, b) that the star is a point source
    sending out parallel rays, c) that the :math:`a/R_s >> R_p` (i.e., the distance
    between each point on the surface to the star is essentially constant), and d) that
    the planet has a spatially uniform albedo of unity (the entire curve can be scaled
    by an actual albedo later, but the assumption of uniformity is baked-in). However,
    it does take into account the planet's oblateness and orientation.

    Args:
        sample_radii (Array):
            Randomly sampled radii from a unit sphere, used to generate points on the
            visible disk of the planet at each timestep. Create with
            :func:`generate_sample_radii_thetas`.
        sample_thetas (Array):
            Randomly sampled thetas from a unit sphere, used to generate points on the
            visible disk of the planet at each timestep. Create with
            :func:`generate_sample_radii_thetas`.
        two (dict):
            A dictionary containing the rho coefficients of the planet's implicit 2D
            representation, as seen from the observer and calculated with
            :func:`planet_2d.planet_2d_coeffs`.
        three (dict):
            A dictionary containing the p coefficients of the planet's 3D shape, as seen
            from the observer and calculated with :func:`planet_3d.planet_3d_coeffs`.
        state (dict):
            A dictionary containing all of the parameters needed to compute the phase
            curve. This includes the planet's orbital parameters, the observer's
            parameters, and the hotspot parameters.
        x_c (Array): The x coordinate of the center of the planet.
        y_c (Array): The y coordinate of the center of the planet.
        z_c (Array): The z coordinate of the center of the planet.
        xo (Array):
            An offset to add to the x coordinate of the center of the planet, used when
            correcting for extended source illuminations. Default is 0.0.
        yo (Array):
            An offset to add to the y coordinate of the center of the planet, used when
            correcting for extended source illuminations. Default is 0.0.
        zo (Array):
            An offset to add to the z coordinate of the center of the planet, used when
            correcting for extended source illuminations. Default is 0.0.

    Returns:
        Array:
            The timeseries of reflected light from the planet. Each element of the array
            corresponds to the time of the corresponding element in state["times"].


WIP, not yet implemented. Hiding behind a NotImplementedError when setting
    `extended_illumination_npts` to anything greater than 1 when initializing an
    :class:`OblateSystem` object.


Compute the rotation matrix to go from the sky frame to the planet's.

    This is the underlying transformation behind everything in
    :func:`planet_3d.planet_3d_coeffs`, except that module never actually uses it
    in this form since it applies it then goes ahead and gathers terms.

    Args:
        a (Array): The semi-major axis of the planet.
        e (Array): The eccentricity of the planet.
        f (Array): The true anomaly of the planet.
        Omega (Array): The longitude of the ascending node of the planet.
        i (Array): The inclination of the planet.
        omega (Array): The argument of periapsis of the planet.
        r (Array): The equatorial radius of the planet.
        obliq (Array): The obliquity of the planet.
        prec (Array): The precession of the planet.
        **kwargs:
            Additional unused keyword arguments, included so that we can pass in
            a larger state dictionary that includes all of the required parameters along
            with other unnecessary ones.

    Returns:
        Array:
            A matrix that can be used to rotate vectors from the sky frame to the
            planet's frame.


A helper function to :func:`emission_at_timestep`, broken out only to be used for
    illustrations in :func:`OblateSystem.illustrate`.


Compute the emitted intensity at a given point on the planet's surface.

    Args:
        x (Array):
            The x values of the points on the planet's surface in the sky frame
        y (Array):
            The y values of the points on the planet's surface in the sky frame
        z (Array):
            The z values of the points on the planet's surface in the sky frame
        transform (Array):
            The rotation matrix to transform the sky frame to the planet's frame,
            calculated with :func:`pre_squish_transform`.
        r (Array):
            The equatorial radius of the planet.
        f1 (Array):
            The planet's :math:`z` flattening coefficient.
        f2 (Array):
            The planet's :math:`y` flattening coefficient.
        hotspot_latitude (Array):
            The "latitude" of the hotspot on the planet. Defined the physics way for
            :math:`\\theta` though, not the geography way: 0 is the north pole,
            :math:`\\pi/2` is the equator, and :math:`\\pi` is the south pole.
        hotspot_longitude (Array):
            The longitude of the hotspot on the planet.
        hotspot_concentration (Array):
            The concentration of the hotspot on the planet. :math:`\\kappa` in the
            von Mises-Fisher distribution.

    Returns:
        Array:
            The intensity of the emitted light at each point.


Compute the timeseries of the emitted light from the planet.

    This function does a Monte Carlo estimation of the visible flux emitted by the
    planet at each time step assuming that a) the surface intensity is modeled by a
    von Mises-Fisher distribution and b) the planet is a Lambertian emitter. To save
    on computation, it takes one set of randomly generated samples of a unit disk,
    then coverts these to points on the planet's visible disk at each timestep. This
    introduces some bias- be sure to use a large enough sample it is below an
    appropriate threshold. Also, to compute secondary eclipses, samples are zeroed
    out when they fall behind the star.

    Args:
        sample_radii (Array):
            Randomly sampled radii from a unit sphere, used to generate points on the
            visible disk of the planet at each timestep. Create with
            :func:`generate_sample_radii_thetas`.
        sample_thetas (Array):
            Randomly sampled thetas from a unit sphere, used to generate points on the
            visible disk of the planet at each timestep. Create with
            :func:`generate_sample_radii_thetas`.
        two (dict):
            A dictionary containing the rho coefficients of the planet's implicit 2D
            representation, as seen from the observer and calculated with
            :func:`planet_2d.planet_2d_coeffs`.
        three (dict):
            A dictionary containing the p coefficients of the planet's 3D shape, as seen
            from the observer and calculated with :func:`planet_3d.planet_3d_coeffs`.
        state (dict):
            A dictionary containing all of the parameters needed to compute the phase
            curve. This includes the planet's orbital parameters, the observer's
            parameters, and the hotspot parameters.

    Returns:
        Array:
            The timeseries of observed emitted light from the planet. Each element of
            the array is the total observed emitted flux at that corresponding time in
            the state["times"] array.


Compute the contributions to a phase curve for a star with ellipsoidal variations.

    A simple sinusoid model with minima at primary and secondary eclipse, meant to
    capture gravitational  Works only for a circular orbit and assumes
    that :math:`\\Omega=\\pi`. Uses the model in
    `Shporer et al. 2014 <https://ui.adsabs.harvard.edu/abs/2014ApJ...788...92S/abstract>`_.

    Technically the amplitude in `Morris, Heng, and Kitzmann 2024 <https://ui.adsabs.harvard.edu/abs/2024arXiv240113635M/abstract>`_ is given by

    .. math::

        A_{ellip} = \\frac{\\alpha}{0.077} \\frac{M_p}{M_J} \\left(\\frac{R_s}{R_\\odot}\\right)^3 \\left(\\frac{P}{1 \\text{day}}\\right)^{-2}

    But we instead roll everything into the alpha parameter.

    Args:
        true_anomalies (Array):
            The true anomaly of the planet at each time step.
        stellar_ellipsoidal_alpha (float):
            The amplitude of the ellipsoidal variations.
        period (float):
            The orbital period of the planet.

    Returns:
        Array:
            The contribution to the phase curve from the star's ellipsoidal variations.


Compute the contributions to a phase curve for a star with Doppler variations.

    A simple sinusoid model with a phase of 90 degrees at primary transit meant to
    capture Doppler boosting/flux falling in and out of the bandpass.

    Args:
        true_anomalies (Array):
            The true anomaly of the planet at each time step.
        stellar_doppler_alpha (float):
            The amplitude of the Doppler variations.
        period (float):
            The orbital period of the planet.

    Returns:
        Array:
            The contribution to the phase curve from the star's Doppler variations.


Compute the reflected and emitted phase curves of the planet.

    This is essentially a wrapper for :func:`reflected_phase_curve` and
    :func:`emission_phase_curve`, except is reuses computations where it can, and also
    applies the appriate scalings to each (albedo/distance from star/area seen by star
    for reflection, and the emitted scale for emission).

    Args:
        sample_radii (Array):
            Randomly sampled radii from a unit sphere, used to generate points on the
            visible disk of the planet at each timestep. Create with
            :func:`generate_sample_radii_thetas`.
        sample_thetas (Array):
            Randomly sampled thetas from a unit sphere, used to generate points on the
            visible disk of the planet at each timestep. Create with
            :func:`generate_sample_radii_thetas`.
        two (dict):
            A dictionary containing the rho coefficients of the planet's implicit 2D
            representation, as seen from the observer and calculated with
            :func:`planet_2d.planet_2d_coeffs`.
        three (dict):
            A dictionary containing the p coefficients of the planet's 3D shape, as seen
            from the observer and calculated with :func:`planet_3d.planet_3d_coeffs`.
        state (dict):
            A dictionary containing all of the parameters needed to compute the phase
            curve. This includes the planet's orbital parameters, the observer's
            parameters, and the hotspot parameters.
        x_c (Array): The x coordinate of the center of the planet.
        y_c (Array): The y coordinate of the center of the planet.
        z_c (Array): The z coordinate of the center of the planet.

    Returns:
        Tuple:
            The correctly scaled reflected and emitted contributions to the phase curve.


    A FFT solver that uses discrete Fast Fourier Transform to
    solve the poisson Equation:
    We apply the correction due to the finite difference grid of phi

    Input: 1. The source function density in this case
           2. # of grid point N
           3. Domain Size in each dimension

    Output: the potential phi and the field g


    This function solves the hydrodynamic Eqns in 1D with/without self gravity using LAX methods
    described above

    Input:  Time till the system is integrated :time
            Number of Xgrid points : N
            Courant number : nu
            Wavelength : If lambda> lambdaJ (with gravity--> Instability) else waves propagation
            Number of waves : The domain size changes and with this maintains periodicity
            Density perturbation : rho_1 (for linear or non-linear perturbation)
            Gravity:  If True it deploys the FFT routine to estimate the potential
            isplot(optional): if True plots the outputs
            Comparison (optional) : If True then the plots are overplotted with LT solutions for comparison
            Animation (optional): Not used at the moment

    Output: Density, velocity + (phi and g if gravity is True)
            isplot: True then the plots are generated


Calculate standard deviation weighted by errors


Builds a requests session that caches responses where possible, making redirects faster.

    Returns:
        requests.Session -- A shared session to use for the notebook


Adds debugging output to HTTP requests to show redirects, headers, etc


Thin wrapper around requests.Request, logging URL sent and Content-Type received

    See https://requests.readthedocs.io/en/master/api/#requests.Request for args

    Returns:
        requests.Response -- The response to the request


Performs a POST request using the request wrapper

    See https://requests.readthedocs.io/en/master/api/#requests.Request for args

    Returns:
        requests.Response -- The response to the request


Plots the shapefile in the given filename with optional basemap (ESRI or GeoJSON)

    Arguments:
        filename {string} -- The filename of the shapefile to display

    Keyword Arguments:
        basemap {bool} -- Whether to display a basemap under the shapefile (default: {True})


Shows a variety of responses possible from Harmony for its example data

    Handles NetCDF files with red_var, green_var, blue_var, and alpha_var bands, compositing output
    into a single colored image, ESRI Shapefiles with basemaps, and any type of image that can be
    read by PIL, including GeoTIFF

    Arguments:
        response {requests.Response} -- The response containing the data to display
        varList {array} -- If set, only plot the variables listed in varList.  Otherwise, plot all.

    Keyword Arguments:
        color_index {number} -- Set for monochromatic images to put the output in a color band (0=red, 1=green, 2=blue) (default: {None})
        immediate {bool} -- True if the data should be shown immediately in the notebook (default: {True})


Shows an asynchronous Harmony response.

    Polls the output, displaying it as it changes, displaying any http data
    links in the response as they arrive, and ultimately ending once the request
    is successful or failed

    Arguments:
        response {response.Response} -- the response to display
        varList {array} -- If set, only plot the variables listed in varList.  Otherwise, plot all.

    Returns:
        response.Response -- the response from the final successful or failed poll


Prints the status, progress and any messages for the async job

    Arguments:
        body {json} -- the response body to display


Shows a condensed version of the asynchronous Harmony response.  Useful for getting status if you don't care about the results.

    Polls the output, displaying status as it changes, and ultimately ending once the request
    is successful or failed

    Arguments:
        response {response.Response} -- the response to display
        varList {array} -- If set, only plot the variables listed in varList.  Otherwise, plot all.
        show_results {bool} -- True will display the results as they arrive.  (default: {True})


Asserts if the spatial extents of the data in the response are within the requested bbox of a spatial subset

    #####  CHECK_BBOX_SUBSET currently is not in use; placeholder for the next round of regression test work

    Arguments:
        response {response.Response} -- the response to display
        req_lat_min -- The minimimum latitude from the request bbox for a spatial subset
        req_lat_max -- The maximum latitude from the request bbox for a spatial subset
        req_lon_min -- The minimimum longitude from the request bbox for a spatial subset
        req_lon_max -- The maximum longitude from the request bbox for a spatial subset


Asserts if the response is a 200, if not, print out the response code

    Arguments:
        response {response.Response} -- the response to display


Asserts if the response contains a valid STAC catalog and prints it out.  More robust assertions could
      be done here in the future to confirm that the STAC metadata is valid per the request parameters

    Arguments:
        response {response.Response} -- the response to display


Ensure the metadata attributes of two NetCDF-4 objects (groups or
    variables) are the same, with the exception of `history` and
    `history_json`, which will include references to the request time.


Compare two NetCDF-4 variables, ensuring they have the same data in
    their arrays and the same metadata attribute.


Compare two NetCDF-4 file groups, ensuring they have the same metadata
    attributes (excluding provenance), child variables and child groups.
    Child variables and groups are then compared recursively.


Compare two NetCDF-4 files recursively, checking that the both have the
    same group structure, variables and metadata attributes.


Submit a Harmony request via a `harmony-py` client. Wait for the
    Harmony job to finish, then download the results to the specified file
    path.


Remove all NetCDF-4 files downloaded during the Swath Projector
    regression tests.


Remove all HDF-4 files downloaded during the Geoloco
    regression tests.


Compares two data dimension sizes


Use the supplied Harmony client (e.g., Prod, UAT, SIT) to make the
    specified request. Wait for the request to complete, and then save the
    output to a user-specified directory.


Extract information from a Harmony GDAL Adapter request output
    and compare to the expected results.


Compare the array values of the output results to those of a reference
    image.


Check all variables from the reference image are in the test output
    and have the same value. This function assumes the NetCDF-4 file to be
    flat.


Remove all HDF-4 files downloaded during the Subset-Band-Name
    regression tests.


Helper function to retrieve outputs from Harmony request and compare to reference
    metadata and files.

    Checks:

    * The expected number of files are returned.
    * The output files are valid Cloud Optimized GeoTIFFs.
    * The data values and metadata match a reference file.
    * The expected CRS value and output file CRS match.


Check that the generated data matches the expected data.


Helper function to open, validate the bounding box, and plot the COG file.

    Checks:

    * The bounding box value in the output file is identical to the expected bounding box


Remove all hdf5 files downloaded during the Trajectory Subsetter
    regression tests.


Compare two files as DataTrees

    Args:
        results_file_name: Path to the results file to validate

        reference_file_name: Path to the reference file to compare against

        identical: If True, use strict comparison including attributes; if
                   False, compare only values

        coordinates_to_fix: List of coordinate names to be renamed in the case
                            that the input has "unalignable" names.

        subset_selector: Dict of top level group names to selection
                         dictionaries used to subset the input DataTree to the
                         previously subsetted reference data. (see
                         `subset_datatree`'s doc string for more information)

    Raises:
      AssertionError: when files don't match according to comparison criteria.


Using a selector dictionary return a subset of the input DataTree.

    Args:
        dt: Input Datatree to subset
        selectors: Dictionary mapping top level group names to dictionaries,
                   where each dictionary contains dimension names to indices
                   selected by slice.

    a sample selector dictionary:
    selectors = {
        "Soil_Moisture_Retrieval_Data_3km": {
            "y-dim": slice(1000, 2000),
            "x-dim": slice(8800, 9800),
        },
        "Soil_Moisture_Retrieval_Data": {
            "y-dim": slice(0, 1000),
            "x-dim": slice(2500, 3500),
        },
    }

    This is used to subset the SPL2SMAP granule, the data groups are the keys
    and the dimensions are subset into 1000x1000 grids that include valid
    output data.


Rename coordinates with different dimensions across datasets.

    This function addresses the issue of datasets having coordinates with the
    same name but different dimensions, which causes problems when creating a
    DataTree. Specifically for handling data products like ATL04 ICESat2, where
    common coordinates (e.g., "delta_time") have different lengths across
    datasets.

    The function renames the specified coordinate in each dataset where it appears,
    assigning a unique identifier to each instance. This allows for the creation of
    a DataTree from the modified dictionary of datasets.

    Parameters:
    -----------
    dict_of_datasets : dict[str, Dataset]
        A dictionary of xarray Datasets, typically obtained from xarray.open_groups().
    coordinate : str
        The name of the coordinate to be renamed across Datasets.

    Returns:
    --------
    dict[str, Dataset]
        A new dictionary of datasets with the specified coordinate
        incrementally renamed when present.


Use native `xarray` functionality to compare data values and metadata
    attributes of downloaded results to a reference file.


Remove all NetCDF-4 files downloaded during the regression tests.


Check that the generated data matches the expected data.
    This function compares the metadata and the array values of
    the generated test output against a reference file. Some
    metadata read by `rasterio`, such as the CRS and geotransform,
    are retrieved from a sibling `.aux.xml` file, meaning the
    content of the test output and reference files for these
    siblings is also being tested.


 Returns the number of worker processes to be spawned.

        The default value is the number of physical cpu cores seen by python.
        :code:`OMP_NUM_THREADS` environment variable overrides it.

        On PBS/torque systems if OMP_NUM_THREADS is empty, we try to
        use the value of :code:`PBS_NUM_PPN` variable.

        Notes
        -----
        On some machines the physical number of cores does not equal
        the number of cpus shall be used. PSC Blacklight for example.


 Copy an array to the shared memory.

        Notes
        -----
        copy is not always necessary because the private memory is always copy-on-write.

        Use :code:`a = copy(a)` to immediately dereference the old 'a' on private memory


Construct a Kafka client configuration dictionary from env variables.
    This uses the same rules as
    https://docs.confluent.io/platform/current/installation/docker/config-reference.html
    to convert from configuration variables to environment variable names:
    * Start the environment variable name with the given prefix.
    * Convert to upper-case.
    * Replace periods (`.`) with single underscores (`_`).
    * Replace dashes (`-`) with double underscores (`__`).
    * Replace underscores (`-`) with triple underscores (`___`).


Fold the given time series over the orbital period to transform to phase space.

    Parameters
    ----------
    times: numpy.ndarray[float]
        Timestamps of the time series
    p_orb: float
        The orbital period with which the time series is folded
    zero: float, None
        Reference zero point in time when the phase equals zero

    Returns
    -------
    phases: numpy.ndarray[float]
        Phase array for all timestamps. Phases are between -0.5 and 0.5


Fold the given time series over the orbital period

    Parameters
    ----------
    times: numpy.ndarray[float]
        Timestamps of the time series
    p_orb: float
        The orbital period with which the time series is folded
    t_zero: float, None
        Reference zero point in time (with respect to the time series mean time)
        when the phase equals zero
    t_ext_1: float
        Negative time interval to extend the folded time series to the left.
    t_ext_2: float
        Positive time interval to extend the folded time series to the right.

    Returns
    -------
    t_extended: numpy.ndarray[float]
        Folded time series array for all timestamps (and possible extensions).
    ext_left: numpy.ndarray[bool]
        Mask of points to extend time series to the left (for if t_ext_1!=0)
    ext_right: numpy.ndarray[bool]
        Mask of points to extend time series to the right (for if t_ext_2!=0)


Average the phase folded signal within a given number of bins.

    Parameters
    ----------
    phases: numpy.ndarray[float]
        The phase-folded timestamps of the time series, between -0.5 and 0.5.
    signal: numpy.ndarray[float]
        Measurement values of the time series
    bins: int, numpy.ndarray[float]
        Either the number of bins or a set of bin edges to be used
    midpoints: bool
        To return bins as midpoints instead of edges, set True
    statistic: str
        The statistic to calculate for each bin (see scipy.stats.binned_statistic)

    Returns
    -------
    bins: numpy.ndarray[float]
        The bin edges, or bin midpoints if midpoints=True
    binned: numpy.ndarray[float]
        The calculated statistic for each bin

    Notes
    -----
    Uses scipy.stats.binned_statistic for flexibility. For the use in number
    crunching, use a specialised function that can be JIT-ted instead.


Mark gaps in a folded series of time points.

    Parameters
    ----------
    times: numpy.ndarray[float]
        Timestamps of the time series
    p_orb: float
        The orbital period with which the time series is folded
    width: float
        Minimum width for a gap (in time units)

    Returns
    -------
    gaps: numpy.ndarray[float]
        Gap timestamps in pairs


Mask out everything except the parts between the given timestamps

    Parameters
    ----------
    times: numpy.ndarray[float]
        Timestamps of the time series
    stamps: numpy.ndarray[float]
        Pairs of timestamps

    Returns
    -------
    mask: numpy.ndarray[bool]
        Boolean mask that is True between the stamps


Phase dispersion, as in PDM, without overlapping bins.

    Parameters
    ----------
    phases: numpy.ndarray[float]
        The phase-folded timestamps of the time series, between -0.5 and 0.5.
    signal: numpy.ndarray[float]
        Measurement values of the time series
    n_bins: int
        The number of bins over the orbital phase

    Returns
    -------
    total_var/overall_var: float
        Phase dispersion, or summed variance over the bins divided by
        the variance of the signal

    Notes
    -----
    Intentionally does not make use of bin_folded_signal (which uses scipy)
    to enable JIT-ting, which makes this considerably faster.


Determine the phase dispersion over a set of periods to find the minimum

    Parameters
    ----------
    times: numpy.ndarray[float]
        Timestamps of the time series
    signal: numpy.ndarray[float]
        Measurement values of the time series
    f_n: numpy.ndarray[float]
        The frequencies of a number of sine waves
    local: bool
        If set True, only searches the given frequencies,
        else also fractions of the frequencies are searched

    Returns
    -------
    periods: numpy.ndarray[float]
        Periods at which the phase dispersion is calculated
    pd_all: numpy.ndarray[float]
        Phase dispersion at the given periods


Calculate the Lomb-Scargle noise spectrum by a convolution with a flat window of a certain width.

    Parameters
    ----------
    times: numpy.ndarray[float]
        Timestamps of the time series
    resid: numpy.ndarray[float]
        Residual measurement values of the time series
    window_width: float
        The width of the window used to compute the noise spectrum,
        in inverse unit of the times array (i.e. 1/d if time is in d).

    Returns
    -------
    noise: numpy.ndarray[float]
        The noise spectrum calculated as the mean in a frequency window
        in the residual periodogram

    Notes
    -----
    The values calculated here capture the amount of noise on fitting a
    sinusoid of a certain frequency to all data points.
    Not to be confused with the noise on the individual data points of the
    time series.


Calculate the Lomb-Scargle noise spectrum by a convolution with a flat window of a certain width,
    given an amplitude spectrum.

    Parameters
    ----------
    freqs: numpy.ndarray[float]
        Frequencies at which the periodogram was calculated
    ampls: numpy.ndarray[float]
        The periodogram spectrum in the chosen units
    window_width: float
        The width of the window used to compute the noise spectrum,
        in inverse unit of the times array (i.e. 1/d if time is in d).

    Returns
    -------
    noise: numpy.ndarray[float]
        The noise spectrum calculated as the mean in a frequency window
        in the residual periodogram

    Notes
    -----
    The values calculated here capture the amount of noise on fitting a
    sinusoid of a certain frequency to all data points.
    Not to be confused with the noise on the individual data points of the
    time series.