Make a trapezoid model at the given input parameters.  This routine
        generates the ioblk class which is used in the transit model.
        You can save time if you want to generate many models by
        calling this function once to generate the ioblk and then call
        trapezoid_model_raw() to generate the models at other inputs
            bypassing some of the setup routines in this function.
       INPUT:
           ts - Mid cadence time stamps
           period - Period of signal ***assumed fixed during model generation**
           epoch - Estimated epoch of signal.  Must be on same system
                           as ts
           depth [ppm] - Model depth
           bigT [hr] -full transit duration in hours
           littleT [hr] - ingress time in hours
           subsample - Subsample each cadence by this factor
        OUTPUT:
            ioblk - structure class containing model ligh curve
                    located at ioblk.modellc


If you have a preexisting ioblk from fit or trapezoid_model_onemodel()
        You can just call this function to get another model
        at a different epoch depth duration and ingress time
        ****period is not variable at this point call
        trapezoid_model_onemodel() instead
       INPUT:
           ioblk - pre-existing ioblk from fitting or trapezoid_model_onemodel()
           epoch - Estimated epoch of signal.  Must be on same system
                           as ts
           depth [ppm] - Model depth
           bigT [hr] -full transit duration in hours
           littleT [hr] - ingress time in hour
        OUTPUT:
            ioblk - structure class containing model ligh curve
                    located at ioblk.modellc


Generate a subsampled model at the current parameters
       INPUT:
       ioblk - [class] trp_ioblk class structure
       OUTPUT:
       ioblk - [class] modified ioblk
       err - [0 ok; 1 not ok] Error flag


Setup various data products before minimizing
       INPUT:
       ioblk - [class] trp_ioblk class structure
       OUTPUT:
       ioblk - [class] modified ioblk


Return a residual time series of data minus model
       trp_setup(ioblk) should be called before this function is called
       INPUT:
       pars - [numpy array] vector of parameter values
       ioblk - [class] trp_ioblk class structure
       OUTPUT:
       residuals - sum of squares of residuals of data - model
       ioblk - [class] modified ioblk


Peform multiple iterations starting from random initial conditions
       return the best solution in a chi2 sense among the nIter iterations


Convert the trapezoid fit solution into a crude estimate
       of a planet model that is close to trapezoid solution
       This fills out values in trp_planetestimates class


Perform a trapezoid fit to a normalized flux time series
       Assumes all data has the same cadence duration
       Period is fixed during the trapezoid fit
       AUTHOR: Christopher J Burke
       INPUT:
           timeSeries - Mid cadence time stamps
           dataSeries - Normalized time series
           errorSeries - Error time series
           signalPeriod - Period of signal ***assumed fixed during model fit**
           signalEpoch - Estimated epoch of signal.  Must be on same system
                           as timeSeries
           signalDuration [hr] - Estimated signal duration ***In hours**
           signalDepth [ppm] - Estimated signal depth
           fitTrialN - How many trial fits to perform starting at random
                       initial locations.  Increase this if you find the
                       minimization is returning local minima
           fitRegion - Fit data within fitRegion*signalDuration of signalEpoch
           errorScale - Default 1.0 - Scale the errorbars by this factor
           debugLevel - 0 Show nothing; 1-Show some text about iterations
                        2 Show some more text; 3 - Show graphical fit in
                           progress; 4 - pause for each graphical fit
           sampleN - Subsample each cadence by this factor
           showFitInterval - If debugLevel >=3 the show every showFitInterval
                              function evaluation
        OUTPUT:
           ioblk - An instance of trp_ioblk which is a class used to store
                   all information pertaining to fit results


Perfom the penalized least-squares smoothing of data of Garcia, D. (2010)
       http://www.biomecardio.com/matlab/smoothn.html
       The smoothing allows for iterative robust smoothing with missing data.
       The smoothing parameter can be automatically determined using a
       generalized cross-validation score method.
       Originally implemented in MATLAB by
       AUTHOR Damien Garcia
       Ported to python by
       AUTHOR: Christopher J Burke
       ***Currently limited to the 1D case***
       For missing, corrupted, or in-transit data that you dont want
       to influence the fit it, is not sufficient to set the weight
       value to 0 for the bad data points.  In addition to setting
       the weight=0, you MUST also do one of the two following choices.
       1) Also set the bad data to NaN in the data vector (y), and this
          function will use linear interpolation across the gap
          to fill in approximate values OR
       2) Interpolate across the gap to fill in the bad data in the data vector
          before calling this function
       INPUT:
       yin - data vector one wants to find a smoothing function for
       w - [0-1] data weights
       s - smoothing parameter if not specified it is determined with GCVS
       robust - Perform iterative reweighting for outliers.
       tolZ - relative tolerance for change between iterations
       maxIter - maximum number of iterations for convergence
       OUTPUT:
       z - smoothed model for data vector
       w - final weighting vector
       s - final smoothing parameter
       exitflag - flag if solution converged before maxIter


Estimate the SNR of a transit.

    SNR is defined as (transit depth) / (rms scatter). Transit depth
    is an input parameter, snr is calculated with the Marshall method

    Inputs:
    -------------
    time, flux, flags
        (np 1d arrays) arrays of time flux and flag values. All flag
        values > 0 are treated as though they indicate bad data.

    period_days, epoch_bkjd, duration_hrs, depth_frac
        (floats) Parameters of transit

    Optional Inputs:
    ----------------
    nDurForClip
        Points within nDurForClip*duration_hrs around each transit are
        excluded from the estimate of noise.


Estimate the typical scatter in a lightcurve.

    Uses the same method as Marshall (Mullally et al 2015 submitted)

    Inputs:
    ----------
    flux
        (np 1d array). Flux to measure scatter of

    Returns:
    ------------
    (float) scatter of data in the same units as in the input ``flux``

    Notes:
    ----------
    Algorithm is reasonably sensitive to outliers. For best results
    uses outlier rejection on your lightcurve before computing scatter.


    Compute a list of periods to search assuming a transit duration.

    If searching a fixed set of durations at every period, use the shortest
    trial duration to compute the period list

    Inputs:
    ----------
    timespan_days
        (float) Timespan of data == max(time) - min(time)
    duration_days
        (float) Trial transit duration to be used for search.

    Optional Inputs:
    -----------------
    attenuation
        Trial duration spacing will be chosen so BLS SNR is never smaller
        than this fraction of of the maximum strengths. A between 0 and 1

    Returns
    -----------
    A 1d numpy array of periods to be searched in units of days to
    ensure any signal is detected within a fraction of the max SNR
    (assuming duration and phase measured exactly)


Compute ln false alarm prob for a given lightcurve

    Inputs:
    ------------
    time, flux
        (1d np array) Lightcurve of target
    unc
        (float) Average uncertainty of flux values.

    periodList:
        (list or array) List of periods to search. Can be created, e.g by
        computePeriodList()

    phaseOverResolution
        (int) When binning the folded lightcurve, how many bins per trial transit
        duration to use. Can be computed with computePhaseOverResolution(), but
        values in the range 5-10 are typically reasonable.

    durationFunc
        (function) A function, that when called, returns the list of durations
        to search. See notes below.

    Returns:
    ---------------
    A 2d array of length **periodList**. The columns are

    0:
        Input trial transit period
    1:
        Natural log of false alarm probability of most signficant peak
        in BLS transform at this period. The row with the lowest value
        in this column gives the period of the strongest transit signal
        in the data
    2:
        The BLS amplitude of the most significant peak at this period.
        Do not use this column to decide which peak is most significant
        across different periods
    3:
        The trial transit duration corresponding to the most significant
        peak at this period
    4:
        The phase of this peak. Phase is given in units of **time** since
        the first data point.

    Notes:
    ----------
    Ofir (2014) suggests only searching a range of durations for a given
    trial period. To accomodate this approach, the durations are not passed
    as a list, but as a function (technically a *callback* function). This
    function takes one argument, the period, and returns a range of durations
    to search at that period.

    np.array = def durationFunc(period).

    This function returns an array of durations in the same units as the
    input period.

    For real world situations, a function object (like. e.g ConstantDurationSearch
    below) are much more powerful

    TODO:
    -----------
    **unc** should optionally be an array, to allow for weighting points
    by their individual flux uncertainty.


Compute the BLS amplitude across all phases in a folded,
    binned lightcurve

    The BLS amplitude is defined as the sum of flux of all points
    within a **transitWidth** of a given point, divded by the square root
    of the number of summed points .

    Inputs:
    -------------
    y
        (1d np array) Flux in each bin. Each bin is assumed to have an
        equal width in time units.
    counts
        (1d np array) Number of points in each bin
    transitWidth
        (int) Width of trial pulse in units of a single bin width.
        An error is raised if transitWidth is negative, or greater than
        the number of bins.
    uncPerUnbinnedPoint
        (float) A single value representing the typical scatter in the
        unbinned photometry points.
    Returns:
    ------------
    bls
        The BLS amplitude for each bin for the given input width.


Fold and bin a (possibly unevenly spaced) lightcurve.

    Most of the time required to compute a BLS spectrum is spent
    folding and refolding the data at different periods. Time spent
    optimsing this action is time well spent. This is the fastest
    function I could think of. If you can think of away to do
    the same with one call to histogram you will halve the time spent
    on the entire BLS.

    Inputs:
    -----------
    t, y
        (1d arrays) Input time and flux. Time need not be evenly spaced.
    period
        (float) Period to fold at. Units the same as **t** (usually days)
    nBins
        (int) Number of bins to fold at.

    Returns:
    -----------
    1d array. The lightcurve is folded at the input period, then binned
    into nBin points. The epoch is chosen so that time==0 is mapped to
    phi==0.

    Note:
    ---------
    When using this function in a BLS code, you'd be wise to
    choose a time offset such that time==0 happens within the dataset. Otherwise
    the phase numbers might be unreliable..


Compute the false alarm probability of a signal of strength
    sigma assuming it is the largest of nDraws samples.

    Take **nDraws** samples from a gaussian distribution with mean zero
    and standard deviation of one. Take the largest sample, **sigma**. The
    probability distribution of this signal follows a Gumbel distribution.
    The probability of seeing a signal of a given strength due to noise
    alone can then be computed with this function.

    For large signals, the false alarm probabilities quickly get ludicrously
    small, and uncomputatable. This function uses the approximation
    $\ln{FAP} \\approx \ln(nDraws) + \ln( 1 - cdf)$
    for values of **sigma** > 9

    Inputs:
    ----------
    sigma
        (1d array) The strength of the signal in units of sigma.

    nDraws
        (1d array) Number of samples taken before choosing the
        given **sigma** as the largest

    Returns:
    -----------
    ln(FAP), the natural log of the false alarm probability. Shape is
    the same as sigma.


An example duration function for computeFBls().
    For illustration only, shouldn't be used in production code


Find plateaus in an array, i.e continuous regions that exceed threshold
	Given an array of numbers, return a 2d array such that
	out[:,0] marks the indices where the array crosses threshold from
	below, and out[:,1] marks the next time the array crosses that
	same threshold from below.
	Inputs:
	array       (1d numpy array)
	threshold   (float or array) If threshold is a single number, any point
				above that value is above threshold. If it's an array,
				it must have the same length as the first argument, and
				an array[i] > threshold[i] to be included as a plateau
	Returns:
	Numpy 2d array with 2 columns.
	Notes:
	To find the length of the plateaus, use
	out[:,1] - out[:,0]
	To find the length of the largest plateau, use
	np.max(out[:,1] - out[:,0])
	The algorithm fails if a value is exactly equal to the threshold.
	To guard against this, we add a very small amount to threshold
	to ensure floating point arithmetic prevents two numbers being
	exactly equal.


Fit a transit model to a lightcurve.
	1. Remove outliers.
	2. Detrend the data with a binwidth of 26 cadences. Since MAX_DURATION_HOURS = 6,
	   and 6 hours = ~13 cadences (ceiling of 12.245), detrending with a binwidth of double
	   this value will preserve all events with a duration of 13 cadences or less.
	3. Create an "S" object. (???) [1 is added to the flux to avoid a division by zero error]
	4. Run the BLS algorithm and Tom's transit fitting algorithm. Since the BLS can lock
	   on to an incorrect, shorter period event, I run it on four different minimum periods,
	   chosen somewhat arbitrarily. These results go into a dictionary sorted by the
	   calculated SNR of each fit, and the parameters which give the maximum SNR are used.
	5. Plot the original lightcurve, the BLS statistics from its minimum to maximum period,
	   and a phased lightcurve.
	6. Save the plot, and return a string containing the parameters of the fit.


    Remove single point outliers.

    Preserves consecutive outliers, and those that are evenly spaced in
    time. This protects short duration transits.

    Inputs:
    ------------
    time, flux
        (1d numpy array) Input data. Flux should have mean (or median) value
        of zero
    precision_days
        (float) Points that are evenly spaced to within this precision
        are considered periodic, and not marked as outliers

    threshold_sigma
        (float) Points more than this many sigma from zero are considered
        potential outliers.

    Returns:
    ------------
    An array of indices indicating which points are single point
    outliers. The length of this array is equal to the number of outlier
    points flagged

    TODO:
    ---------
    For consistency with rest of code, this could return an array of booleans


Find plateaus in an array, i.e continuous regions that exceed threshold
    Given an array of numbers, return a 2d array such that
    out[:,0] marks the indices where the array crosses threshold from
    below, and out[:,1] marks the next time the array crosses that
    same threshold from below.
    Inputs:
    array       (1d numpy array)
    threshold   (float or array) If threshold is a single number, any point
                above that value is above threshold. If it's an array,
                it must have the same length as the first argument, and
                an array[i] > threshold[i] to be included as a plateau
    Returns:
    Numpy 2d array with 2 columns.
    Notes:
    To find the length of the plateaus, use
    out[:,1] - out[:,0]
    To find the length of the largest plateau, use
    np.max(out[:,1] - out[:,0])
    The algorithm fails if a value is exactly equal to the threshold.
    To guard against this, we add a very small amount to threshold
    to ensure floating point arithmetic prevents two numbers being
    exactly equal.


Private method. Run the bls


Compute 1d bls spectrum

        Returns:
        ------------
        A 2d array with columns of period and strongest amplitude at
        that period.


Plot the 2d BLS spectrum, showing the strongest peak
        across all duratons for each period/epoch pair


    Compute a bls spectrum of a lightcurve

    Inputs:
    ----------
    time, flux
        (1d np arrays) Times and fluxes of data. flux should have a mean
        of approximately zero.

    periodRange
        (2 element list) Minimum and maximum periods to search. Units
        the same as the time array

    durations
        (list) List of transit durations to search for. Note this
        is not necessarily in hours, but in the same units as the time array
        (which is commonly days).

    Returns:
    ----------
    Not yet decided

    Notes:
    ----------
    See bls.tex for notes on computationaly complexity.


    Compute the list of periods needed to comphrensively search a lightcurve
    with BLS.

    Inputs:
    -----------
    Plwr, Pupr
        (floats) Minimum and maximum periods to search

    time
        (list or array) times at which data is sampled. May be an array
        of all time values, or just a list of the first and last points.
    duration
        (float) Assumed transit duration in units of **time**
    overres
        How many times the minimum sampling should be performed

    Returns:
    -----------
    An array of periods to search at.

    Note:
    ---------
    See bls.tex in this directory for a description of this algorithm


Worker function for fbls

    See fbls() for a description of inputs. Values for durationList
    and periodList should be given in the same units as t (usually days)

    Todo:
    Allow passing in weights on each data point to better account for
    varying noise.

    Do some experiments to fnd the best value for minBinsPerTransit.
    Kovacs suggests 15, I find 10 works well, I haven't tried other numbers


Find the index of the best peak in the bls spectrum.

    The best peak is not usually the strongest one. The noise floor
    of a BLS increases at long period, so this must be accounted for
    before selecting the strongest amplitude.

    Inputs:
    -------------
    blsArray
        (3d numpy array)

    Optional Inputs:
    -----------------
    nPointsForSmooth
        (int) Tuning parameter. This is the number of points near a given
        period used to determine what the typical background BLS value is.

    offset
        (int) Debugging option. Change the chosen period by this number
        of indices.

    Returns:
    ------------
    A three element tuple of indices into blsArray giving the best
    period, duration and phase of transit.


window in days


Median Absolute Deviation


        inputs to bls are:
        p,bper,bpow,depth,qtran,in1,in2 = eebls(
            t,x,e,nf,fmin,df,nb,qmi,qma,[n])
        where:
        t is time
        x is flux
        e is err2
        nf is number of frequencies (5000)
        fmin is minimum freq
        df is the stepsize ()
        nb is numbe rof bins
        qmin frac in transit
        qmax frac in transit
        compute_bls(time,
                    lc, df = freq_step,
                    nf =  (1./min_period)/freq_step, nb = 1400,
                    qmi = float(min_duration_hours)/24./450.,
                    qma = float(max_duration_hours)/24./300.,
                    fmin = (1./(float(max_period)*1.1)))


Show continuous behaviour around the break between analytic
    and approximate methods (around sigma==8), and non-linear behaviour
    at low sigma and high values of nDraw


    Calculate the LPP transit metric given a time, flux (detrended)

    inputs
    ----------
    time : Time array in days
        array
    period : in days
        float
    duration : in hours
        float
    phase : in days
        .float
    This runs octave code.

    outputs
    -------
    Tlpp : LPP transit metric value

    binnedFlux :  The sorted, folded, binned flux values input to LPP


Calculate the out of sample X reduced dimensions using mean and
    the mapping M.


Get all the data via the MAST API


        read in matlab blob
        Using the DV trained one.


    This function takes a data class with light curve info
    and the mapInfo with information about the mapping to use.
    It then returns a lpp metric value.


    Take a running median of size dt
    Return values at times given in runt


    Fold and bin light curve for input to LPP metric calculation

    data contains time, tzero, dur, priod,mes and flux (centered around zero)

    ntrfr -- number of transit fraction for binning around transit ~1.5
    npts -- number of points in the final binning.


    Perform the matrix transformation with LPP
    Do the knn test to get a raw LPP transit metric number.


    For a group of known transits and a new one.
    Use knn to determine how close the new one is to the known transits
    using knn minkowski p = 3 ()
    Using scipy signal to do this.


    Normalize the rawTransitMetric value by those with the closest period.
    This part removes the period dependence of the metric at short periods.
    Plus it makes a value near one be the threshold between good and bad.

    newPerMes is the np.array([period, mes]) of the new sample


    Chop down the full time series to one orbital period.
    Then gather the lpp value for that one transit.


    Create the loop over individual transits and return
    array normalized lpp values, mean and std.
    Input TCE object and mapInfo object.

    It is unclear that this individual transit approach
    separates out several new false positives.
    It probably would require retuning for low SNR signals.


Calculate the x-mean, y-mean, and cov matrix of an image.


Plot bars with a length of 2 stddev along the principal axes.


Make lines a length of 2 stddev.


window in days


Median Absolute Deviation


        inputs to bls are:

        p,bper,bpow,depth,qtran,in1,in2 = eebls(

            t,x,e,nf,fmin,df,nb,qmi,qma,[n])

        where:

        t is time

        x is flux

        e is err2

        nf is number of frequencies (5000)

        fmin is minimum freq

        df is the stepsize ()

        nb is numbe rof bins

        qmin frac in transit

        qmax frac in transit

        compute_bls(time,

                    lc, df = freq_step,

                    nf =  (1./min_period)/freq_step, nb = 1400,

                    qmi = float(min_duration_hours)/24./450.,

                    qma = float(max_duration_hours)/24./300.,

                    fmin = (1./(float(max_period)*1.1)))


Find plateaus in an array, i.e continuous regions that exceed threshold

	Given an array of numbers, return a 2d array such that

	out[:,0] marks the indices where the array crosses threshold from

	below, and out[:,1] marks the next time the array crosses that

	same threshold from below.

	Inputs:

	array       (1d numpy array)

	threshold   (float or array) If threshold is a single number, any point

				above that value is above threshold. If it's an array,

				it must have the same length as the first argument, and

				an array[i] > threshold[i] to be included as a plateau

	Returns:

	Numpy 2d array with 2 columns.

	Notes:

	To find the length of the plateaus, use

	out[:,1] - out[:,0]

	To find the length of the largest plateau, use

	np.max(out[:,1] - out[:,0])

	The algorithm fails if a value is exactly equal to the threshold.

	To guard against this, we add a very small amount to threshold

	to ensure floating point arithmetic prevents two numbers being

	exactly equal.


    subtract the background from a series of images
    by assuming the aperture is large enough to be
    predominantly background


smooth the data using a window with requested size.

     This method is based on the convolution of a scaled window with the signal.
     The signal is prepared by introducing reflected copies of the signal
     (with the window size) in both ends so that transient parts are minimized
     in the begining and end part of the output signal.

     input:
         x: the input signal
         window_len: the dimension of the smoothing window
         window: the type of window from 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'
             flat window will produce a moving average smoothing.

     output:
         the smoothed signal

     example:

     t=linspace(-2,2,0.1)
     x=sin(t)+randn(len(t))*0.1
     y=smooth(x)

     see also:

     numpy.hanning, numpy.hamming, numpy.bartlett, numpy.blackman, numpy.convolve
     scipy.signal.lfilter

     TODO: the window parameter could be the window itself if an array instead of a string


    Load a file full of ephemerides
    return data array
    0=epicId
    1=campaign
    2=period
    3=epoch
    4=depth


    Load a file with information you need for the configuration
    Add into your config file and return.


    run the exporters based on the input clip.
    Append the important information to the output File.


De-trend a light curve using a windowed median.


    code to run self flat field on K2 data

    Keyword arguments:
    time -- time time array
    flux -- the array of brightnesses
    xpos -- the x-pixel position
    ypos -- the y-pixel position
    ferr -- flux error, will be assumed to be uniform if None
    qflags -- data quality flags
    inpflag -- flags to use to explude data, non-zero values are removed
    ap -- a random number


Make diagnostic plots

    Inputs:
    -------------
    basename
        Base name of the output pdf file.
    time
        The full time array.
    fullflux
        The full raw flux time series.
    detrendflux
        The detrended flux time series.
    modelflux
        The fitted model flux time series.
    period
        The period of the system in days.
    epoch
        The epoch of the system in days.
    duration
        The duration of the transit in days.

    Returns:
    -------------
    Nothing at present. Maybe an error code?

    Output:
    ----------
    A pdf of plots


Take a clipboard, clip, and create plots
    put these plots all into one multi paged document
    specifieed by outfile


Returns a figure showing multiple lightcurves separated by an offset.

    Parameters
    ----------
    time : 1D array of size N.
        Times (i.e. x axis).

    fluxes: 1D array of size N, or sequence of 1D arrays of size N.
        Fluxes (i.e. y axis).

    labels : str, or sequence of str
        Labels corresponding to the fluxes.

    offset_sigma: float
        The lightcurves will be shown with an offset of
        offset_sigma times the overall standard deviation.

    title : str
        Title to show at the top of the figure.

    markersize : float
        Size of the symbols in the plot.

    epoch : float
        If `epoch` and `period` are set, the lightcurves will be folded.

    period : float
        If `epoch` and `period` are set, the lightcurves will be folded.


    The clipboard needs to contain
       clip.inject:  period_days,epoch_bkjd,depth_ppm,duration_hrs,ingress_hrs
       clip.serve: needs to contain the serve and cotrend keys

       the output clip has altered the clip.cotrend.flux_frac with
       transits specified in clip.inject


    Now that the light curve has a transit in it, run the rest of the pipeline.
    dpp.lppMetricTask
        dpp.modshiftTask dpp.measureDiffImgCentroidsTask dpp.dispositionTask
        dpp.saveClip


Look at what was found and determine if it matches the input.


Read in the stellar file and return a dictionary


    Read in the stellar file and return a dictionary -- spaces were removed from file
    data.ix[201121245]['Teff'] is example how to get vlaues out


    Load the stellar panda File
    Add a stellar key to the clip and include all the fields in infoList.
    InfoList should have the names as used by the stellar table.


    Given the information in the clip, depth adn stellar info
    Return a planet dictionary with useful information
    like planet radii and insolation flux.
    Must run addStellarToClip with the following added
    Mass
    Rad
    Teff


    This routine determines an optimal apertures and outputs the flux (i.e. a light curve) from a TPF.

    Inputs:
    ------------
    t_time = 1D array of 'TIME' from TPF
    t_fluxarr = 1D array of 'FLUX' from TPF
    t_quality = 1D array of 'QUALITY' from TPF
    qual_cut = exclude cadences with a non-zero quality flag; this is False by default
    return_qual = if True then nothing is returned; this is True by default
    toss_resat = exclude cadences where there is a wheel resaturation event; this is True by default
    bg_cut = threshold to find pixels that are bg_cut * MAD above the median
    skip = index of first cadence that should be used in the time series

    Outputs:
    ------------
    time = 1D array of time in BKJD
    lc = 1D array of flux measured in optimal aperture
    xbar = 1D array of x-coordinate of target centroid
    ybar = 1D array of y-coordinate of target centroid
    regnum = integer value of brightest pixel
    lab = 2D array identifying pixels used in aperture

    Usage:
    ------------
    tpf,tpf_hdr = ar.getLongTpf(k2id, campaign, header=True)

    tpf_time = tpf['TIME']
    tpf_flux = tpf['FLUX']
    tpf_quality = tpf['QUALITY']

    time,lc,xbar,ybar,regnum,lab = optimalAperture(tpf_time, tpf_flux, tpf_quality, qual_cut=False, return_qual=False, toss_resat=True, bg_cut=5, skip=0)


Model data by finding best fit distribution to data


Generate distributions's Probability Distribution Function


    calculate the probability of drought recovery
    :param data_charc:[lat,lon,dm,rt,prec]
    :param max_serious_global:lat*lon*[dm,rt,prec]
    :param season_prec:lat*lon*[3~5 months, 1-28 days moving average precipitation, 6~8 months, 1-28 days moving average precipitation, 9~11 months, ..., 12~1 months, ...]
    :param num_file
    :return:


Record the date when the drought occurred and ended


Calculate drought characteristics


all events in the region


    returns a dictionnary of interpolation function. Interpolation is
    FLAMBDA (erg/s/cm2/m/ster) as a function of wavelength (um). Keys
    of the dictionnary are (teff, logg, metalicity).


    interpolate Imu(wavel) for array of 'wavel' but scalar mu, teff,
    logg. Requires global variables 'model', '_teff' and '_logg' to be set.

    limitation: there is a lot of check for NaNs and Infs that appear
    from some reason.


    use to interpret y(x) for x0+-dx0/2.

    use if y is over-resolved compared to dx0 (same shape as x0)

    assumes x and x0 sorted!!!


    returns a dictionnary with keys 'TEFF', 'LOGG', 'VTURB', 'LH' (all
    floats) and 'HNU', 'WAVEL', 'FLAMBDA' (np.array) units: TEFF in K,
    LOGG in log10(m/s2), VTURB in km/s, WAVEL in um, HNU in
    erg/s/cm/Hz/ster and FLAMBDA in erg/s/cm2/m/ster

    L/H is mixing length

    In each file: col.3=wavelength in nm
              col.4=frequency nu (s^-1)
              col.5=Hnu (erg/cm^2/s/Hz/ster)
              col.6=Hcont (erg/cm^2/s/Hz/ster)
              col.7=Hnu/Hcont
    Flambda=4*Hnu*c/wavelength^2  c=light velocity


    computes the LD profile I(mu) for Teff, Logg, and M/H. mu and wl are 1D
    ndarray.

    LD_data[(teff, logg, M/H, wl)] = (I0, c1, c2, c3, c4)
    all Teff, logg, M/H and wavelength values are tabulated in
    LD_teff, LD_logg, LD_MH, LD_wl.


    returns the magnitude of a star of TEFF, LOGG and angular
    diameter DIAMTER_MASS (in mas).

    relies on 'photfilt2py'


    fit the photometric diameters to magnitudes. if fixedTeff is set,
    the Teff is set to the given value and not fitted

    obs = [('filtername', measurement, error), ()...]


    bi-linear interpolation in teff (K) and logg (log10 m/s2) as a
    function of wavelength (um).

    result in erg/s/cm2/m/ster


erase all hard coded files used to accelerate the calculations. Run if you added a new photometric filter, modified the photometric calculations or if you modified the CSE model for instance.


    perform a parallax of pulsation fit to the data 'obs'.

    'obs' is a list of tuple containing the observations, each tuple should have
    the form accetpable by 'model' or 'modelM' function, with the measured
    values and errors added to it. For example:

    (mjd, 'vpuls') -> (mjd, 'vpuls', 12.24, 0.07)

    etc.


    'f' is the result from the "fit" function, ran with "plot=True" and
    "exportFits=True"


firstGuess should at least contain 'PERIOD', 'MJD0' and 'd_kpc'


    returns observable 'x' (Vpuls, photometry, etc) for a given model
    based on parameters 'a':

    x = list of tuples: (mjd, type, data...)
        if MJD <1, then the code assumes it is a pulsation phase and will not
        recompute it with the ephemeris given in 'a' ('PERIOD', 'MJD0')

        for example *x* means optional:
        (mjd, 'vpuls') -> Vpuls in km/s

        (mjd, 'vrad') -> radial velocity in km/s

        (mjd, 'vgamma') -> average Vpuls in km/s (mjd is ignored)

        (mjd, 'diam', *[wavelength_um, baseline_m]*) -> diameters in mas

        (mjd, 'UDdiam', [wl_um, *baseline_m*]) -> UD diameter in mas, at wavelength wl (um)

        (mjd, 'v2fluor', baseline_m) -> squared visibility for fluor

        (mjd, 'teff') -> effective temperature

        (mjd, 'Mbol') -> Absolute bolometric magnitude

        (mjd, 'mag', 'filter') -> photometric magnitude, for a given filter
        (mjd, 'flux', 'filter') -> apparent flux in Jy
        (None, 'mag', 'filter') -> photometric magnitude, for a given filter,
                        at unknown phase

        (mjd, 'color', 'filter1-filter2') -> filter1-filter2

        (mjd, 'normalized spectrum', wl_um) -> normalized spectrum;
                         assumes only one instrument is used,
                         i.e. single Rspec fo all spectra)

    Note that only the part before ';' will be considered for an observation's
    name, leaving room to keep track of comments (such as origin of the
    observation). For instance:

    (mjd, 'vrad; Bersier et al. 1994')

    a is a dictionnary: e.g,

    a={'PERIOD':35.551341000000001,
       'MJD0':52290.508124788146,
       'DIAM0':2.7653404798701073, # in mas
       'd_kpc':0.60066719626599596,  # in kpc
       'VPULS PHI0':-0.12539413431736629,
       'VPULS PHI1': 0.033484967745679037,
       'VPULS PHI2': 0.71483220499480626,
       'VPULS PHI3': 0.75988519192623627,
       'VPULS PHI4': 0.94896962017838637,
       'VPULS VAL0': 18.631596918810043, # in km/s
       'VPULS VAL1': -21.016020628354106,
       'VPULS VAL2': 27.495143188934478,
       'VPULS VAL3': 28.402426499671989,
       'VPULS VAL4': -12.592838804125455,
       'TEFF PHI0': -0.14499127119607119,
       'TEFF PHI1': 0.022753518466214342,
       'TEFF PHI2': 0.71599188489829835,
       'TEFF VAL0': 4963.4638463137926, # in K
       'TEFF VAL1': 5156.5715783762689,
       'TEFF VAL2': 4556.2069715595271,
       'E(B-V)':0.077356456362560944,
       }

    - 'd_kpc' is the distance in kilo-parsecs
    - 'P-FACTOR' is the spectroscopi projection factor
    - 'METAL:'' optional keyword to give the metalicity (0 for Solar)
    - 'MJD0', 'PERIOD': are the pulsation ephemeris (period in days)
    - optional: PERIOD1, PERIOD2, the 1rst and second order period variations
    - DIAM0: is the average angular diameter in mas
    - E(B-V) is the color excess (for redenning correction).
    - Rspec is the spectral resolution /1000. (will convolve outpu spectra)
    - Vspec,  additional velocity for spectra in km/s
    - 'K EXCESS' K band magnitude excess (applies also to H band, but with a
      factor 0.5). >0 means the model will be brighter in K
    - 'Rv': reddening function (between 3.1 and 5, default is 3.1)

    additional parameters are: 'COMP DIAM', 'COMP TEFF', 'COMP LOGG' for a
    non-pulsating companion in the photometric field of view.

    VPULS and TEFF phase-dependent profiles can be parametrized using periodic
    splines or Fourier terms. In case of Spline, the parameters are {'VPULS
    PHI0', 'VPULS VAL0', ..., 'VPULS PHIi', 'VPULS VALi', ...} for the nodes
    positions (phi_i, val_i). In case of Fourier parameters, one should give
    {'VPULS A0', 'VPULS A1', 'VPULS PHI1',..., 'VPULS Ai','VPULS PHIi', ...} for
    the amplitudes and phases of the Fourier terms. Note that 'PHI0' is not a
    useful parameters and that 'A0' will be the gamma velocity for VPULS and the
    average temperature for TEFF.

    Splines can be also defined using a 'comb' for the phase axis. The comb is
    defined usinf 'POW' and 'PHI0': 'POW' defines the over-density of nodes
    around phase 'PHI0'. 'POW' of 1 is uniform phase coverage, and larger values
    of POW will lead to higher densities. Typical value of POW is 2.5. Nodes
    values can be defined using 'VAL0' for the first one and then using 'DVALi'
    for i>0. 'VAL0' defines the value of the node for the lowest phase density,
    which ensures that VAL0 is close to the average value of the profile. This is
    unfortunately at the expense of naming logic, because one might have expected
    VAL0 == value at PHI0. Example:
    {
    'VRAD DVAL1':   17.301,
    'VRAD DVAL2':   8.933,
    'VRAD DVAL3':   -3.685,
    'VRAD DVAL4':   -11.586,
    'VRAD DVAL5':   -16.593,
    'VRAD PHI0':    0.91675,
    'VRAD POW':     2.388,
    'VRAD VAL0':    21.612,
    }

    Note about redenning: the redenning is corrected using a law for ISM


read fits and extract parameters dictionnary and data vector


    Nvrad: number of radial velocity measurements
    Nvis: number of visible photometric measurements
    Nir: number of IR photometric measurements
    Ninterf: number of interferometric measurements
    Nteff: number of spectra and Teff measurements

    stats on max gaps:
    gap        50%   90%
    0.20 for N> 17 N> 26
    0.10 for N> 42 N> 64
    0.05 for N>102 N>147


    parallelized version of model. 'bunch' is the size of the data to
    be treated by each subprocess.


sub function for modelM


    sub function for modelM

    if INIT is a integer, initialize the result with an array on length INIT

    if OUTPUT is set to True, will return the result

    else, expects x=(k, value), where value can be anything. Will set
    res1[k] to value.


    for each deltaMJD range (in days), compute best period offset.

    obs -> array of tuples, describing the dataset
    a -> parameters

    -> assumes first elements of x are MJD, not phases


assumes that all MJD are MJDs, not phase (0-1)


set phases between 0 and 2pi and corrects negative amplitudes


dephase the model by dphi (0->1). This is usefull to phase the model so maximum luminosity is attained for phi==0


Teff and logg are 1D array. Returns grid of magnitudes for 1mas star


    compute photometry for diam (mas), teff (K) and filter name. If
    SED==True, returns the wavelength table and SED (in W/m2/um),
    instead of the integrated value.

    z is redshift


    http://articles.adsabs.harvard.edu/pdf/1968ApJ...154...21O eq 2
    http://adsabs.harvard.edu/full/1996PASP..108..190K in section 2
    m_obs = m_z=0 + Kcorr

    https://en.wikipedia.org/wiki/K_correction
    m_0 = m_obs - K so K = m_obs - m_0

    Richard and I defined "K = m0 - mobs" -> "m_obs = m_true - K"


    a -> parameters for a model
    filters -> list of filters


    Compare colors computed from ATLAS9 models and the tabulation found in
    Astrophysical Quantities


    A*(M1-M2)+B = 4.2207 - 0.1*M1 + 0.5*log10(diam)

    (A+0.1)*M1 - A*M2 + (B-4.2207) = 0.5*log10(diam)

    if diam == 0

    (A+0.1)/A*M1 + (B-4.22027)/A = M2

    c[0]*M1 + c[1] = M2


    wl in um
    P, in mbar (default 743.2mbar)
    T, in K    (default 290.0K)
    e partial pressure of water vapour, in mbar (default 74.32)


    wl in microns of filter name. Returns A(lambda) for each wavelength.

    Here A(lambda) is the extinction, in magnitudes, or 1.086tau(lambda), where tau
    is the optical depth in dust.

    see Astrophysical Quantities
        8.4 INTERSTELLAR EXTINCTION IN THE ULTRAVIOLET
        21.2 GALACTIC INTERSTELLAR EXTINCTION [[[not used anymore!]]]

    TABLE 3 and 4 from FITZPATRICK: 1999 PASP, 111-63 for visible, IR


test the robustness of the Wesenheit relations to reddenning.


    comes from hypothesis that Rv = Av/E(B-V) so the expression "V - Rv*(B-V)"
    is free of redenning. Using this function, one can compute the Wesenheit
    relation for various pairs of photometric systems.

    Weseinheit ~= Intrinsic, Essential in German


    centro symetric squared visibility function.

    params = {'UD DIAM':} # UD diam in mas
           = {'nu DIAM':, 'nu':} # nu=1 -> UD case; nu>1 LD case
           = {'nu DIAM':, 'nu':, 'CSE':} # CSE in % of flux

    wavel in um or name of passband (e.g. FLUOR)


    complex visibility of star+shell

    B: array of baselines, in meters
    params: {'wavel':in um,
             'Tstar':, 'Tshell': in K,
             'LD DIAM': in mas,
             'nu': LD coef for the star (1 is UD, default),
             'Rshell/Rstar':>1,
             'tau': optical depth of the layer}


    Intesity profile for a star surrounded by a shell.
    params: {'wavel':in um,
             'Tstar':, 'Tshell': in K,
             'nu': LD coef for the star (1 is UD, default)
             'Rshell/Rstar':>1,
             'tau': optical depth of the layer}
    r_rshell is an array of values in [0..1]

    see Perrin et al. A&A 436-317 (2005), eq 1 and 2

    returns [Istar, Ishell]


    diameter bias (>1) due to the presence of a shell

    only works for scalar diam, B and Kexcess

    validity: Kexcess>0 and Kexcess<0.1 and B*diam <~ 500

    return 1 if Kexcess <= 0


    - mass: in solar mass
    - i: crossing of the instability strip (default=2)

    based on Fadeyev 2014:
    "Theoretical Rates of Pulsation Period Change in the Galactic Cepheids"
    https://arxiv.org/pdf/1401.6547.pdf


very simple example


    very simple example with correlated error bars

    <<< DOES NOT WORK! >>>


    allows to call any combination of function defines inside dpfunc:

    params={'funcA;1:p1':, 'funcA;1:p2':,
            'funcA;2:p1':, 'funcA;2:p2':,
            'funcB:p1':, etc}

    funcA and funcB should be defined in dpfunc.py. Allows to call many
    instances of the same function (here funcA) and combine different functions.
    Outputs of the difference functions will be sumed usinf operator '+'.


    - params is a Dict containing the first guess.

    - fits 'y +- err = func(x,params)'. errors are optionnal. in case err is a
      ndarray of 2 dimensions, it is treated as the covariance of the
      errors.

      np.array([[err1**2, 0, .., 0],
                [0, err2**2, 0, .., 0],
                [0, .., 0, errN**2]]) is the equivalent of 1D errors

    - follow=[...] list of parameters to "follow" in the fit, i.e. to print in
      verbose mode

    - fitOnly is a LIST of keywords to fit. By default, it fits all
      parameters in 'params'. Alternatively, one can give a list of
      parameters not to be fitted, as 'doNotFit='

    - doNotFit has a similar purpose: for example if params={'a0':,
      'a1': 'b1':, 'b2':}, doNotFit=['a'] will result in fitting only
      'b1' and 'b2'. WARNING: if you name parameter 'A' and another one 'AA',
      you cannot use doNotFit to exclude only 'A' since 'AA' will be excluded as
      well...

    - normalizedUncer=True: the uncertainties are independent of the Chi2, in
      other words the uncertainties are scaled to the Chi2. If set to False, it
      will trust the values of the error bars: it means that if you grossely
      underestimate the data's error bars, the uncertainties of the parameters
      will also be underestimated (and vice versa).

    returns dictionary with:
    'best': bestparam,
    'uncer': uncertainties,
    'chi2': chi2_reduced,
    'model': func(x, bestparam)
    'cov': covariance matrix (normalized if normalizedUncer)
    'fitOnly': names of the columns of 'cov'


    get a set of randomized parameters (list of dictionnaries) around the best
    fited value, using a gaussian probability, taking into account the correlations
    from the covariance matrix.

    fit is the result of leastsqFit (dictionnary)

    returns a fit dictionnary with: 'ymin', 'ymax' and 'r_param' (a list of the
    randomized parameters)


    bootstraping, called like leastsqFit. returns a list of fits: the first one
    is the 'normal' one, the Nboot following one are with ramdomization of data. If
    Nboot is not given, it is set to 10*len(x).


    interface to leastsq from scipy:
    - x,y,err are the data to fit: f(x) = y +- err
    - pfit is a list of the paramters
    - pfitsKeys are the keys to build the dict
    pfit and pfix (optional) and combines the two
    in 'A', in order to call F(X,A)

    in case err is a ndarray of 2 dimensions, it is treated as the
    covariance of the errors.
    np.array([[err1**2, 0, .., 0],
             [ 0, err2**2, 0, .., 0],
             [0, .., 0, errN**2]]) is the equivalent of 1D errors


    sA2 is the variance of param A
    sB2 is the variance of param B
    sAB = rho*sA*sB the diagonal term (rho: correlation)

    returns the semi-major axis, semi-minor axis and orientation (in rad) of the
    ellipse.

    sMa, sma, a = ellParam(...)

    t = np.linspace(0,2*np.pi,100)
    X,Y = sMa*np.cos(t), sma*np.sin(t)
    X,Y = X*np.cos(a)+Y*np.sin(a), Y*np.cos(a)-X*np.sin(a)

    ref: http://www.scribd.com/doc/50336914/Error-Ellipse-2nd


    Attempt to make an automatic dowload a set of commonly used filters from the Spanish
    Virtual Observatory


    wl in um
    return T**expo where T is the transmission of the atmosphere between
    0.3 and 5um in wavelength,
    for 1.0mm of water vapor if expo=1.0


    quick and dirty reader for .xml files

    for more download VOTable files in directory '_dir' from
    http://svo.cab.inta-csic.es/theory/fps/index.php?mode=browse


    filt is the dictionarry read from an xml file (or built manually)

    assumes profile's wavelength are in um, will be converted in Angstrom


calibrated from Martin Groenewegen, email from Alex 2017/


    Polynomial function. e.g. params={'A0':1.0, 'A2':2.0} returns
    x->1+2*x**2. The coefficients do not have to start at 0 and do not
    need to be continuous. Actually, the way the function is written,
    it will accept any '_x' where '_' is any character and x is a float.


    return params['AMP']*(X - params['X0'])**params['POW'] + params['OFFSET']

    only params['POW'] is mandatory


    1D gaussian function of moment 'MU' and variance 'SIGMA'**2

    params: {'MU':, 'SIGMA':, ('OFFSET':,) ('AMP':)} 'AMP' and
    'OFFSET' are optional: if AMP is not given, the amplitude is set
    to 1/(sigma*sqrt(2*pi)).


    1D lorentzian function of moment 'MU' and parameter 'GAMMA'

    params: {'MU':, 'GAMMA':, ('OFFSET':,) ('AMP':)}. 'AMP' and
    'OFFSET' are optional: if AMP is not given, the amplitude is set to
    1/(sigma*sqrt(2*pi)).


    sinusoidal wave
    params: {'AMP':amplitude, 'WAV':wavelength, ('OFFSET':offset), ('PHI':phase
    in radian)}

    returns AMP*sin(2*pi*x/WAV + PHI) + OFFSET


    cosinusoidal wave
    params: {'AMP':amplitude, 'WAV':wavelength, ('OFFSET':offset), ('PHI':phase
    in radian)}

    returns AMP*cos(2*pi*x/WAV + PHI) + OFFSET


    fourier serie:
    (A0) + sum(Ak*cos(k*2*pi*x/WAV +PHIk))_k=1...

    A0 is optional. Parameters should contain at least WAV and one (Ak,PHIk),
    for example:

    {'WAV':1.0, 'A1':1.0, 'PHI1':0} -> cos(2*pi*x)
    {'A0':1.0, 'WAV':2*np.pi, 'A2':1.0, 'PHI2':np.pi/4} -> 1+cos(2*x+np.pi/4)

    warning, will crash if dictionnary is ill formed, for example:
    {'WAV':1.0, 'A1':1.0, 'PHI2':0}


explanation for referee that scaling != clipping


    monochromatic visibility of uniform disk
    x = np.pi*B_m*theta_rad/lambda_m


find value of r corresponding to the maximum value of dI/dr


    *monochromatic* visibility of uniform disk, unless 'R' is defined
    bl = Baseline / wavelength in m/um


    v2 function for fit using SATLAS spherical atmospheric models.

    bl = Baseline / wavelength in m/um


    fit V2 in OIFITS data file ("oifits") using a V2 profile computed from an SATLAS models.

    oifits can be dict: {'B/l':, 'v2':, 'ev2':, 'R':, 'avgWl':, 'station':}
    ndarray (all same length):
    'B/l': baseline/wavelength (m/m)
    'v2': squared visibilities (0..1)
    'ev2': absolute squared visibilities error (0..1)
    'station': list of baseline names, used for bootstrapping

    scalar:
    'R': spectral resolution (scalar), for synthetic visibilities
    'avgWl': average wavelength (in um), to determine that adequate band

    Models can be found at: ftp://cdsarc.u-strasbg.fr/J/A+A/554/A98/spheric
    and should downloaded locally. "model" should be the address of a .dat model

    firstGuess is the expected diameter in mas


Hestroffer LD (power law)


    bw: B/wl in m/m

    param = {'diam':, 'a1': 'a2':, 'a3':, 'a4':}


    squared vibility for linear LD, from Brown 1974
    params = {'diam' in mas,
              'wavel' in um,
              'linLD'}


Check PSF

    This method checks that the input PSFs are properly normalised and updates
    the opts namespace with the PSF type

    Parameters
    ----------
    data : np.ndarray
        Input data array
    log : logging.Logger
        Log instance


Run script

    This method runs the script.

    Parameters
    ----------
    log : logging.Logger
        Log instance


Convert argument line to arguments

        This method overrides the default method of argparse. It skips blank
        and comment lines, and allows .ini style formatting.

        Parameters
        ----------
        line : str
            Input argument string

        Yields
        ------
        str
            Argument strings


Check data format

    This method checks that the input data has the correct number of dimensions

    Parameters
    ----------
    data : np.ndarray
        Input data array
    n_dim : int or list of ints
        Expected number of dimensions

    Raises
    ------
    ValueError
        For invalid array dimensions


Read FITS file

    This method reads image array data from a FITS file.

    Parameters
    ----------
    file_name : str
        Name of file with path

    Returns
    -------
    np.ndarray array of image data


Write FITS file

    This method writes the output image array data to a FITS file.

    Parameters
    ----------
    file_name : str
        Name of file with path
    data : np.ndarray
        Image data array


Read file

    This method reads image array data from a file.

    Parameters
    ----------
    file_name : str
        Name of file with path

    Returns
    -------
    np.ndarray array of image data

    Raises
    ------
    ValueError
        For invalid file extension


Read input files

    This method reads image array data from the specified input files.

    Parameters
    ----------
    data_file_name : str
        Name of file with path for the noisy image data
    psf_file_name : str
        Name of file with path for the PSF image data
    current_file_name : str, optional
        Name of file with path for the current results

    Returns
    -------
    tuple of np.ndarray arrays of image data

    Raises
    ------
    ValueError
        If number of noisy images less than the number of PSFs
    ValueError
        If the shape of the current results does not match the input data


Write output files

    This method writes the image data results to the specified output file(s)

    Parameters
    ----------
    output_file_name : str
        Name of file with path for the output data
    primal_res : np.ndarray
        Array of primal output results
    dual_res : np.ndarray, optional
        Array of dual output results
    psf_res : np.ndarray, optional
        Array of PSF output results
    output_format : str, optional
        Output file format (numpy binary or FITS)


Perform reweighting

    This method updates the weights used for thresholding in the sparse domain

    Returns
    -------
    dict Updated keyword arguments


Run deconvolution

    This method initialises the operator classes and runs the optimisation
    algorithm

    Parameters
    ----------
    data : np.ndarray
        Input data array, an array of 2D images
    psf : np.ndarray
        Input PSF array, a single 2D PSF or an array of 2D PSFs

    Returns
    -------
    np.ndarray decconvolved data


Convolve data with PSF

    This method convolves an image with a PSF

    Parameters
    ----------
    data : np.ndarray
        Input data array, normally an array of 2D images
    psf : np.ndarray
        Input PSF array, normally either a single 2D PSF or an array of 2D
        PSFs
    psf_rot: bool
        Option to rotate PSF by 180 degrees
    psf_type : str {'fixed', 'obj_var'}, optional
        PSF type (default is 'fixed')
    method : str {'astropy', 'scipy'}, optional
        Convolution method (default is 'astropy')

        'fixed':
            The PSF is fixed, i.e. it is the same for each image

        'obj_var':
            The PSF is object variant, i.e. it is different for each image

    Returns
    -------
    np.ndarray convolved data

    Raises
    ------
    ValueError
        If `psf_type` is not 'fixed' or 'obj_var'


H matrix operation

        This method calculates the action of the matrix H on the input data, in
        this case the convolution of the the input data with the PSF

        Parameters
        ----------
        x : np.ndarray
            Input data array, an array of recovered 2D images

        Returns
        -------
        np.ndarray result


Ht matrix operation

        This method calculates the action of the transpose of the matrix H on
        the input data, in this case the convolution of the the input data with
        the rotated PSF

        Parameters
        ----------
        x : np.ndarray
            Input data array, an array of recovered 2D images

        Returns
        -------
        np.ndarray result


Get the gradient at the given iteration

        This method calculates the gradient value from the input data

        Parameters
        ----------
        x : np.ndarray
            Input data array, an array of recovered 2D images


Calculate gradient component of the cost

        This method returns the l2 norm error of the difference between the
        original data and the data obtained after optimisation

        Returns
        -------
        float gradient cost component


Update the regularisation parameter lambda_reg

        This method implements the update method for lambda_reg


Update the current estimate of the PSF

        This method calculates the gradient of the PSF and updates the current
        estimate

        Parameters
        ----------
        x : np.ndarray
            Input data array, an array of recovered 2D images


Get the gradient step

        This method returns an array of zeroes

        Parameters
        ----------
        x : np.ndarray
            Input data array

        Returns
        -------
        np.zeros array size


    Returns the radius [earth radii] of a solid planet given
    its observed mass and minumum core radius fraction (CRFmin).

    Note:
        This is the forward model. For a more practical approach, see
        hardcore.invert

    Args:
        M_obs (float/int): mass [earth masses], recommended for >0.1
        CRF (float/int): core radius fraction, 0 <= CRF <= 1

    Returns:
        float


    Returns the f(x)/f'(x) term in Newton's method used in hardcore.invert

    Args:
        M_obs (float/int): mass [earth masses], recommended for >0.1
        R_obs (float/int): radius [earth radii]
        crf (float/int): core radius fraction, 0 <= CRF <= 1

    Returns:
        float


    Returns the radius of a solid planet if it was to have no iron core,
    given its mass.

    Args:
        M_obs (float/int): mass [earth masses], recommended for >0.1

    Returns:
        float


    Returns the radius of a solid planet if it was made of 100% iron.

    Args:
        M_obs (float/int): mass [earth masses], recommended for >0.1

    Returns:
        float


    Returns the maximum core radius fraction (CRFmax) of a solid planet given
    its observed mass and radius.

    Args:
        M_obs (float/int): mass [earth masses], recommended for >0.1
        R_obs (float/int): radius [earth radii]

    Returns:
        float


    Returns:
        The minimum core radius fraction (CRFmin) of a solid planet given
            its observed mass and radius.
        The maximum core radius fraction (CRFmax) of a solid planet from
            hardcore.CRFmax
        The marginal core radius fraction (CRFmarg) of a solid planet as
            a random sample from uniform distribution
            between CRFmin and CRFmax.

    Args:
        M_test (float/int): mass [earth masses], recommended for >0.1
        R_test (float/int): radius [earth radii]

    Returns:
        tuple


Convert a url of a JSON table to a Pandas DataFrame.

        Args:
            url (str): URL to the webpage

        Returns:
            DataFrame: DataFrame of JSON table


        Use Series to loop over multiple webpages.

        Proceed to concatenate them to a single DataFrame

        Args:
            endings (iterables): The list/series/column over which to loop
            url (str): The base url

        Returns:
            DataFrame


Clean up the data.


Filter frbcat in various ways.


Apply coordinate transformations.


Hack to make for more informative print statements.


Convert coordinates expressed in hh:mm:ss to fractional degrees.


    Convert from ra, dec to galactic coordinates.

    Formulas from 'An Introduction to Modern Astrophysics (2nd Edition)' by
    Bradley W. Carroll, Dale A. Ostlie (Eq. 24.16 onwards).

    NOTE: This function is not as accurate as the astropy conversion, nor as
    the Javascript calculators found online. However, as using astropy was
    prohibitively slow while running over large populations, we use this
    function. While this function is not as accurate, the under/over
    estimations of the coordinates are equally distributed meaning the errors
    cancel each other in the limit of large populations.

    Args:
        ra (string): Right ascension given in the form '19:06:53'
        dec (string): Declination given in the form '-40:37:14'
        frac (bool): Denote whether coordinates are already fractional or not
    Returns:
        gl, gb (float): Galactic longitude and latitude [fractional degrees]


Convert row of html table to json format.


Get a 2d array from an input table.

    In the table, the data is flattened to a 1d form.
    The first two columns are x and y, like this:
    The first column looks like this with many duplicates:
    [1,1,1,1,1,1,2,2,2,2,2,2,3,3,3, ...].
    Column B repeats like this: [1,2,3,4,5,6,1,2,3,4,5,6,1,2,3, ...].

    All remaining columns are data on the same x-y grid, and the grid
    has to be regular.

    Parameters
    ----------
    tab : `astropy.table.Table`
        Table as read in. Useful to access units or other meta data.

    Returns
    -------
    x, y : `astropy.table.Column`
        Unique entries in first and second column
    dat : np.array
        The remaining outputs are np.arrays of shape (len(x), len(y))


Calculate direction of photons in homogeneous coordinates.

        Parameters
        ----------
        coos : `astropy.coordiantes.SkyCoord`
            Origin of each photon on the sky
        time : np.array
            Time for each photons in sec

        Returns
        -------
        photons_dir : np.array of shape (n, 4)
            Homogeneous direction vector for each photon


Calculate a polarization vector for linearly polarized light.

        The current definition cannot handle photons coming exactly from either
        the North pole or the South Pole of the sphere, because the polangle
        definition "North through east" is not well-defined in these positions.

        Parameters
        ----------
        photonsdir : np.array of shape (n, 4)
            Direction of photons
        polangle : np.array
            Polarization angle measured N through E. If polangle has no
            units, it is assumed to be specified in radian.
        time : np.array
            Time for each photons in sec


        Parameters
        ----------
        photons : `astropy.table.Table`


Return a function that generates Poisson distributed times with rate ``rate``.

    Parameters
    ----------
    rate :  `~astropy.units.quantity.Quantity`
        Expectation value for the total rate of photons with unit 1 / cm**2 / s.

    Returns
    -------
    poisson_rate : function
        Function that generates Poisson distributed times with rate ``rate``.


Generate Poisson distributed times.

        Parameters
        ----------
        exposuretime : `~astropy.units.quantity.Quantity`
            Exposure time
        geomarea : `~astropy.units.quantity.Quantity`
            Geometric opening area of telescope

        Returns
        -------
        times :  `~astropy.units.quantity.Quantity`
            Poisson distributed times.


Central function to generate photons.

        Calling this function generates a photon table according to
        the `flux`, `energy`, and `polarization` of this source. The
        number of photons depends on the total exposure time, which is
        a parameter of this function. Depending on the setting for
        `flux` the photons could be distributed equally over the
        interval 0..exposuretime or follow some other distribution.

        Parameters
        ----------
        exposuretime : `astropy.quantity.Quantity`
            Total exposure time.

        Returns
        -------
        photons : `astropy.table.Table`
            Table with photon properties.


Photon positions are generated in a frame that is centered on the
        coordinates set in ``coords``, then they get transformed into
        the global sky system.


Radial distribution function.

        See http://6degreesoffreedom.co/circle-random-sampling/ for an
        explanation of how to derive the formula. Note however, that
        here we use sin instead of cos because we measure the angle
        from the top.


Walk a hirachy of simulation elements to all elements of a class.

        This walks the hiracy of `marxs.simulator.Sequence` and
        `marxs.simulator.Parallel` objects. It descends depth-first.
        Typically, this function is used to
        find, e.g. all detectors of an instrument or all gratings.

        If `stop_at_first` is set it descends depth-first only until
        it find an object of type `cls`. When it does, it adds that object to
        the output list and returns to the next higher level, continuing
        the search for the remaining elements of that list. This sounds more
        complicated than it is in practice.
        Only if container types such as `marxs.simulator.Parallel` are searched
        for this becomes more complex. The search does not descend any deeper
        if a matching element is found. For example, if it finds a
        `~marxs.simulator.Parallel` object, where the elements of this object
        contain other `~marxs.simulator.Parallel` objects it will only return
        the top level one.

        Parameters
        ----------
        cls : class
            The class searched for, e.g. `marxs.optics.FlatDetector` to find all
            detectors.
        subclass_ok : bool
            Controls if subclasses of `cls` count as match.
        stop_at_first : bool
            Controls if the search continues along a branch after a match is found.

        Returns
        -------
        out : list
            Python list of all objects found.


Return the index of the first sub-element that has ``cls`` in it.

        This walks the hiracy of `marxs.simulator.Sequence` and
        `marxs.simulator.Parallel` objects and returns the index of the
        first sub-element that contains a certain class.

        Typically, this function is used to study and instrument with small
        modifications. If e.g. only the detector are changed, we might want to
        run a simulation up to the detectors, save te rays and then try out
        different detector combinations without rerunning the simulations for
        all earlier steps. This methid can help to identify the step that
        contains the detectors.

        Parameters
        ----------
        cls : class
            The class searched for, e.g. `marxs.optics.FlatDetector` to find all
            detectors.
        subclass_ok : bool
            Controls if subclasses of `cls` count as match.

        Returns
        -------
        out : int or None
            index for the first element in ``self.elements`` that is or contains
            an objects of class ``cls``. If none is found, the method return
            ``None``.


Move the relative origin of the coordinate system.

        Individual element positions in ``elem_pos`` are relative to the
        global ``pos4d`` of this object. This function changes the
        ``pos4d`` and adjusts the ``elem_pos`` accordingly, so that the
        elements still have the same position in global coordinates.

        This function is useful when studying uncertainties. The attribute
        ``uncertainty`` can be used to apply the same change to all elements.
        Any rotation will be applied around the reference position contained
        in ``pos4d``. Use this function to change that reference position.

        Parameters
        ----------
        change4d : np.array of shape (4,4)
            Homogeneous coordinate transformation matrix


Initialize all optical elements.

        After any of the ``elem_pos``, ``elem_uncertainty`` or
        ``uncertainty`` attributes has changed, `generate_elements` needs to be
        called to regenerate the positions of the individual elements using

        - the global position of ``Parallel.pos4d``
        - the position of each element ``Parallel.elem_pos`` relative to the global position
        - the global uncertainty `uncertainty`.
        - the uncertainty for individual facets.


Calculate the position of elements based on some algorithm.

        Classes derived from `ParallelCalculated` can overwrite this
        method if they want to provide a way to calculate the pos4d
        matrices for the elements that make up this
        `ParallelCalculated` element.

        Returns
        -------
        pos4d : list of arrays
            List of affine transformations that bring an optical
            element centered on the origin of the coordinate system
            with the active plane in the yz-plane to the required
            facet position on the Rowland torus.


Format saved position columns as a single array.

        `KeepCol` keeps the value of a column (e.g. the photon position)
        at every step of the simulation. This function reformats that list of
        columns for use graphical display programs. Note that `format_positions`
        will only work for columns that store data in homogeneous coordinates,
        for all other cases, simply call `numpy.asanyarray` on your `KeepCol`
        object.

        Parameters
        ----------
        atol : float or None
            Sometimes several consecutive elements record identical photon positions in
            `keepcol`. Those are removed from the output to speed up rendering in 3D
            programs.
            ``atol`` sets the limit up to which two positions are considered *identical*.
            See `np.allclose` for a detailed description of ``atol``.
            Set to ``None`` to skip this step.

        Returns
        -------
        pos : np.array
            Array of shape (N, n, 3), where N is the number of photons and n the number of
            unique photon positions in euclidean coordinates.


Move photons along the rays.

    This function moves all photons in the photon list by a specified amount
    forwards or backwards along the ray.
    Normally, photons only move forward in the simulation, but sometimes it
    can be useful to reset the photons to an earlier position, e.g. to see
    how the pattern on a detector changes for different detector positions.

    Parameters
    ----------
    photons : `astropy.table.Table`
        Photon table with pos and dir entries
    d : float
        Distance for the photons to move. Negative values move photons
        backwards.

    Returns
    -------
    photons : `astropy.table.Table`
        Photon table with pos and dir entries


Compare two arrays and allow nan==nan


Check that the meta data between two simulations indicates consistency.

    This check compares a number of keywords (e.g. the version of marxs) to
    indicate if the simulations where run with the same version of marxs.
    If a simulation records more information in the metadata,
    the check can be more complete.
    This function raises an `AssertionError` if the two sets of
    meta information are different.

    Parameters
    ----------
    meta1 : dict
        header from photon list 1
    meta2 : dict
        header from photon list 2
    keywords : list
        Keywords to be checked
    allow_missing : bool
        This flag allows a keyword to be missing from both dictionaries
        (e.g. MARXSGIT is not set if run with a released version); however,
        it is still an error if a key is present only in one, but not the
        dictionary.

    Raises
    ------
    AssertionError, KeyError


Check if the energy is the same for all photons.

    If there is no energy colum, then this also passes.


Add output columns to the photon array.

        This function takes the column names that are added to ``photons`` from
        several sources:

        - `id_col` (if not ``None``)
        - `output_columns`
        - the ``colnames`` parameter.

        Parameters
        ----------
        photons : `astropy.table.Table`
            Table columns are added to.
        colnames : list of elements
            Each element can be a string (in this case a float column with
            initial value ``np.nan`` is added) or a dictionay of arguments
            for `astropy.table.column.Column`. If the dictionay has a keys
            "value" then the column will be initialized to that value.
            Column names to be added; in addition several object properties can
            be used to set the column names, see description above.


Parse keywords to define position.

    If ``pos4d`` is given, use that, otherwise look for ``position``, ``zoom``
    and ``orientation``.

    Parameters
    ----------
    pos4d : 4x4 array
        Transformation to bring an element from the default position
        (see description of individual elements) to a certain point in
        the space of homogeneous coordinates.
    position : 3-d vector in real space
        Measured from the origin of the spacecraft coordinate system.
    orientation : Rotation matrix or ``None``
        Relative orientation of the base vectors of this optical
        element relative to the orientation of the coordinate system
        of the spacecraft. The default is no rotation (i.e. the axes of both
        coordinate systems are parallel).
    zoom : float or 3-d vector
        Scale the size of an optical element in each dimension by
        ``zoom``. If ``zoom`` is a scalar, the same scale is applied
        in all dimesions. This only affects the oter dimensions of the
        optical element, not internal scales like the pixel size or
        grating constant (if defined for the optical element in
        question).


Get position of zeroth order

    Parameters
    ----------
    coord : string
        Name of coordinate
    channel : string
        Channel id, e.g. "1m"
    conf : dict
        Arcus configuration dictionary

    Returns
    -------
    pos : float
        coordinate of zeroth order


Get R and Aeff from a single photon event list

    Parameters
    ----------
    photons : `astropy.table.Table`
        Photon event list
    orders : np.array
        list of orders to be analyzed
    reference_meta : dict or None
        If not ``None`` check that the meta information of the photon
        list matches the meta information in ``reference_meta``
    conf : dict
        Arcus configuration (the zero order position for each channel
        is taken from this dict).

    Returns
    -------
    res : np.array
        Array of R values for each aperture and order, measured from
        photons that hit a CCD. For signal close to a CCD boundary, the
        detected photon distribution may be artificially narrow.
        In general it is better to use res (because the CCDs don't follow
        the Rowland circle exactly) but it's so close that ``circ_phi``
        can be used for those cases. ``res`` is set to ``np.nan`` if there
        is no signal (e.g. a chip gap).
    Aeff : quantity
        Effective area for each aperture and order, using only photons
        that are not L1 cross-dispersed and detected on a CCD.


Analyze a grid of simulations for R and Aeff

    Parameters
    ----------
    inpath : string
        Path to the simulations grid
    conf : dict
        Arcus configuration (the zero order position for each channel
        is taken from this dict).
    orders : list
        List of integer order numbers to be analyzed
    apertures : list
        List of aperture ids in the simulations
    allow_inconsistent_meta : bool
        If ``False`` (the default) the routine checks that all ray files in
        the grid have compatible meta information (e.g. code version).
        Setting this to ``True`` bypasses that check.

    Returns
    -------
    out : `astropy.table.QTable`
        Table with instrument performance metrics


Rewrite one column in ``aeffRfromraygrid`` to csv file

    Turn one vector-valued (all orders in one cell) column into a
    csv table with one entry per cell.


Resolve the tangent plane coordinates for a circular detector

    .. warning::
        This method is still under development.

    Parameters
    ----------
    evt : `astropy.table.Table`
        Table with photon events
    defaultconf : dict
        Arcus configuration dictionary


Print information about loaded files to standard out.

    Parameters
    ----------
    dirname : string
        Name for the directory in the caldb-input file structure
    filename : string
        Name of data file (without the ".csv" part)


Get a single number from an ecsv input file

    Parameters
    ----------
    dirname : string
        Name for the directory in the caldb-input file structure
    filename : string
        Name of data file (without the ".csv" part)
    valuename : string
        Name of the column that hold the value

    Returns
    -------
    val : float or `astropy.units.Quantity`
        If the unit of the column is set, returns a `astropy.units.Quantity`
        instance, otherwise a plain float.


Get a table from an ecsv input file

    Parameters
    ----------
    dirname : string
        Name for the directory in the caldb-input file structure
    filename : string
        Name of data file (without the ".csv" part)

    Returns
    -------
    val : `astropy.table.Table`


Looking at SPO positions, find midpoint of all SPOs.

    To simplify this code it only considers the mid-points of the SPOs, not
    their rotation angle. Since the rectangles are rotated a bit, e.g.
    the aperture needs to be a bit bigger than the area covered by SPOs.
    Also, if elements are above or below the SPOs, the beam may converge
    or come from the side. `azscale` and `rscale` can be set to adjust
    the outer corners a bit.

    Parameters
    ----------
    conf : dict
        Configuration dictionary with entry 'spo_geom' and 'spo_pos4d'

    Returns
    -------
    zoom : float
        middle of the SPO array in y direction


Looking at SPO positions, find zoom of rectangle that covers it.

    To simplify this code it only considers the mid-points of the SPOs, not
    their rotation angle. Since the rectangles are rotated a bit, e.g.
    the aperture needs to be a bit bigger than the area covered by SPOs.
    Also, if elements are above or below the SPOs, the beam may converge
    or come from the side. `azscale` and `rscale` can be set to adjust
    the outer corners a bit.

    Parameters
    ----------
    conf : dict
        Configuration dictionary with entry 'spo_geom' and 'spo_pos4d'
    azscale : float
        The outer edge is set to the center of the outermost SPO, plus
        `azscale` times the dimension in azimuth (global x coordinate)
    rscale : float
        The outer edge is set to the center of the outermost SPO, plus
        `rscale` times the dimension in radius (global y coordinate)

    Returns
    -------
    zoom : np.array of shape (3,)
        Zoom array as input to make pos4d


Reformat the numbers from LSF-CAT-Alignment-v3.xls

    Randall gives 3 sigma errors, while I need 1 sigma a input here.
    Also, units need to be converted: mu -> mm, arcsec -> rad
    Last, global misalignment (that's not random) must be
    scaled in some way. Here, I use 0.8 sigma, which is the
    mean absolute deviation for a Gaussian.

    Parameters
    ----------
    budget : list
        See reference implementation for list format
    globalfac : ``None`` or float
        Factor to apply for global tolerances. A "global" tolerance is drawn
        only once per simulation. In contrast, for "individual" tolerances
        many draws are done and thus the resulting layout actually
        represents a distribution. For a "global" tolerance, the result hinges
        essentially on a single random draw. If this is set to ``None``,
        misalignments are drawn statistically. Instead, the tolerances can be
        scaled deterministically, e.g. by "0.8 sigma" (the mean absolute
        deviation for a Gaussian distribution).


Add four sided boom. Only the top two bays contribute any
        absorption, so we can save time by not modelling the remaining bays.


Add detectors to the element list

        This is a separate function that is called from __init__
        because all detectors need different parameters. Placing this
        specific code in it's own function makes it easy to override
        for derived classes.


Positions for rods on one side of one bay

        Returns
        -------
        pos4d : list
            List of pos4d matrices


Calculate pos4d matrices for each element on the boom


Generate default filename from ARF or RMF header values

    Parameters
    ----------
    filetype : string
        file ending (the file is usually saved as fits, but the ending
        might be "arf", "rmf", or any other string)
    kwargs: string/int
        Keywords used to construct filename


Make an ARF for 0th order or a grating spectrum

    Parameters
    ----------
    channel_edges : `~astropy.units.quantity.Quantity`
        Edges of the channels in energy or wavelength. The ARF that is
        generated from  n edges will have n-1 bins.
    order : int
        Diffraction order
    mirr_grat : callable or None
        Function that interpolates mirror and grating efficiency. If None,
        it is read from ``mirr_grat.tab`` in the caldb.
    trans_filters_qe : callable or None
        Function that interpolates filter curves and CCD QE. If None,
        it is initialized from `FiltersQE` which uses Arcus defaults and
        allows extrapolation.
    conf : dict
        Arcus configuration
    channels : list
        List of channel names for an ARF. ARFs can be made for a single order
        or be combined for a set of orders.

    Returns
    -------
    transmission : np.array
        Transmission in the range 0..1 for each bin.


Make RMF for 0 order

    The RMF is a single Gaussian.

    Parameters
    ----------
    channel_edges : `~astropy.units.quantity.Quantity`
        Edges of the channels in energy or wavelength. The RMF that is
        generated from n edges will have n-1 bins.
    threshold : float
        To reduce the file size, RMF components below the threshold value
        are cut-off
    ccdfwhm : `astropy.table.Table`
        Table with CCD properties. Two columns are expected with
        'energy' (in keV) and 'FWHM' (in eV). Note that the units are
        hardcoded and different between columns.


Write README file to directory with ARFs

        Parameters
        ----------
        osip : `marxs.reduction.osip.OSIPBase` object
            OSIP objet that generates data in this directory.
        outpath : string
            Location where the output ARFs are deposited
        outroot : string
            prefix for output filename


Get a single number from an ecsv input file

    Parameters
    ----------
    filename : string
        Name of data file
    valuename : string
        Name of the column that hold the value

    Returns
    -------
    val : float or `astropy.units.Quantity`
        If the unit of the column is set, returns a `astropy.units.Quantity`
        instance, otherwise a plain float.


Get a table from an ecsv input file

    Parameters
    ----------
    filename : string
        Name of data file

    Returns
    -------
    val : `astropy.table.Table`


Add detectors to the element list

        This is a separate function that is called from __init__ because all
        detectors need different parameters. Placing this specific code in its own
        function makes it easy to override for derived classes.


Reformat the error budget

    Last, global misalignment (that's not random) must be
    scaled in some way. Here, I use 0.8 sigma, which is the
    mean absolute deviation for a Gaussian.

    Parameters
    ----------
    budget : list
        See reference implementation for list format
    globalfac : ``None`` or float
        Factor to apply for global tolerances. A "global" tolerance is drawn
        only once per simulation. In contrast, for "individual" tolerances
        many draws are done and thus the resulting layout actually
        represents a distribution. For a "global" tolerance, the result hinges
        essentially on a single random draw. If this is set to ``None``,
        misalignments are drawn statistically. Instead, the tolerances can be
        scaled determinisitically, e.g. by "0.8 sigma" (the mean absolute
        deviation for a Gaussian distribution).


Remove photons that do not go through an SPO but hit the
    frame part of the petal.


Write a Chandra event level 1 file.

        As opposed to directly saving the photon list, this adds some Chandra
        specific meta data.

        Parameters
        ----------
        photons :  `astropy.table.Table` or `astropy.table.Row`
            Table with photon properties. Some meta data from the header of this table
            is required (e.g. the length of the observation).
        filename : string
            Path and file name where the file is saved.


Configuration Control Component


Observation info component


Add CIAO keywords to header of an eventfile.


Complete fits header for Chandra fits files.

    This method add the common keywords that are required for all CXC fit files.
    With few exception, the methods will not overwrite existing keywords, but will
    only fill in the value of required keywords if they have not been set before.
    There are certain keywords that cannot be generated without more information
    (e.g. the name of the detector used). These keywords must be set outside of these
    routines.
    The exception where keywords are overwritten is timing information that is contained in
    redundant keywords. This method read "EXPOSURE" and "DATE-OBS" from the header and
    calculates "TSTART", "TSTOP" and "OBS-MJD" from those values.

    Parameters
    ----------
    header : a dictionary-like object
        In most cases, this will be ``photons.meta``.
    data : `astropy.table.Table` or ``None``
        For event tables the header includes keywords that depend on the order of the columns
        in the data (e.g. column 5 and 6 define the WCS). Pass in the full table as data for
        those cases.
    content : string
        Content keyword as specified by ASC
    hduclass : list of stings
        The list can contain 1-4 elements, depending on the data product.
        See appendix A1 in the
        `ASC FITS File Designers' Guide ASC-FITS-2.1.0 <cxc.harvard.edu/contrib/arots/fits/ascfits.ps>`.
    References
    ----------
    `ASC FITS File Designers' Guide ASC-FITS-2.1.0 <cxc.harvard.edu/contrib/arots/fits/ascfits.ps>`.


Clean up the order of column names

    Parameters
    ----------
    photons : `astropy.table.Table`
        Event list.

    Returns
    -------
    photons : `astropy.table.Table`
        Event list with columns sorted such that pairs of columns that describe coordinates
        appear together (e.g. "detx" and "dety"). All other columns are sorted alphabetically.


Read position of facets from file.

        Based on the positions, other grating parameters are chosen
        that differ between HEG and MEG, e.g. grating constant.


Convert CHIP coordinates to TDET coordiantes.

    See eqn (5) in `the Chandra coordiante memo <http://cxc.harvard.edu/contrib/jcm/ncoords.ps>`_.

    Parameters
    ----------
    chip : (N, 2) np.array
        chip coordiantes
    tdet : dict
        dictionary with definitions for the coordiante conversion.
        See `ACISTDET` for an example.
    id_num : integer
        chip ID number (e.g. ``1`` for ACIS-I1)


Calculate the dither offset relative to pointing direction.

        Parameters
        ----------
        time : np.array
            Time when the dither motion should be calculated

        Returns
        -------
        delta : np.array of shape (N, 3)
            dither motion offset in pitch, yaw, roll for N times in rad


Calculate the pointing direction for a set of times
        There are the steps to convert the dither (which are offsets) to the total
        pointing direction with the roll properly taken care of.
        (See marxasp.c in the marx code for more details.)
        1.  Convert ra/dec offsets to absolute pointing.
        2.  Roll resulting vector about nominal pointing
        3.  Convert result to ra/dec.

        Parameters
        ----------
        time : np.array
            Array of times

        Returns
        -------
        pointing : (n, 3) np.array
            Ra, Dec, roll values in radian for the pointing direction at time t.


Calculate direction on photons in homogeneous coordinates.

        Parameters
        ----------
        coos : `astropy.coordiantes.SkyCoord`
            Origin of each photon on the sky
        time : np.array
            Time for each photons in sec

        Returns
        -------
        photons_dir : np.array of shape (n, 4)
            Homogeneous direction vector for each photon


Write an aspect solution (asol) file

        Chandra analysis scripts often require an aspect solution,
        which is essientially a list of pointing directions in the
        dither pattern vs. time.  This method write such a list to a
        file.

        Parameters
        ----------
        photons :  `astropy.table.Table` or `astropy.table.Row`
            Table with photon properties. Some meta data from the header of
            this table is required (e.g. the length of the observation).
        asolfile : string
            Path and file name where the asol file is saved.
        timestamp : float
            Time step between entries in the asol file in seconds.


Obtain the probabilities for photons to go into a particular order.

        This has the same parameters as the ``__call__`` method, but it returns
        the raw probabilities, while ``__call__`` will draw from these
        probabilities and assign an order and a total survival probability to
        each photon.

        Parameters
        ----------
        energies : np.array
            Energy for each photons
        pol : np.array
            Polarization for each photon (not used in this class)
        blaze : np.array
            Blaze angle for each photon

        Returns
        -------
        orders : np.array
            Array of orders
        interpprobs : np.array
            This array contains a probability array for each photon to reach a
            particular order


        Parameters
        ----------
        intercoos : np.array
            intercoos coordinates for photons interacting with optical element


Metal structure that holds grating facets will absorb all photons
    that do not pass through a grating facet.

    We might want to call this L3 support ;-)


Move and rotate elements around principal axes accounting for bar tilt.

    This function is made for `~marxs.optics.CATGrating`, and simulates a situation
    where the bar angles are not normal to the grating surface.
    We draw a bar tilt angle for each grating from a given distribution. Then,
    an element is rotated to compensate this bar tilt angle. This represents the a
    correction that happens when the grating is bonded into a frame.
    Additionally, the "normal" random misalignment is applied.

    Parameters
    ----------
    e : `marxs.simulator.Parallel` or list of those elements
        Elements where uncertainties will be set
    dx, dy, dz : float
        accuracy of positioning in x, y, z (in mm) - Gaussian sigma, not FWHM!
    rx, ry, rz : float
        accuracy of positioning. Rotation around x, y, z (in rad) - Gaussian
        sigma, not FWHM!
    max_bar_tilt : float
        Maximum value for bar tilt, drawn from a uniform distribution.


Return a triangulation of the baffle hole embedded in a square.

        The size of the outer square is determined by the ``'outer_factor'`` element
        in ``self.display``.

        Returns
        -------
        xyz : np.array
            Numpy array of vertex positions in Eukeldian space
        triangles : np.array
            Array of index numbers that define triangles


add columns ['pos'] to photon array


Area of the aperture.

        This does not take into account any projection effects for
        apertures that are not perpendicular to the optical axis.


Generate x, y in the local coordinate system

        Parameters
        ----------
        n : int
            number of x, y coordinate pairs requested

        Returns
        -------
        x, y : arrays of floats
            (x, y) coordinates of the photons in the local frame


Assign new values to a subset of photons in a photon list

    The column 'probability' receives special treatment: Probability values
    are not replaced by the new numbers, but multiplied by the new numbers.
    That way, a function just need to generate the *change in probability* and,
    as a photon propagates through an instrument, all those changes multiply
    together.

    The probability column is also subject to extra checks because we want to
    maintain probabilities between 0 and 1. In particular negative
    probabilities can really cause problems later. Thus, this methods will
    raise an Exception if probabilities are outside of the 0..1 range.
    This can happen, for example, when an interpolation of a table goes wrong
    or when numerical errors accumulate. In this case, the function that
    generates the numbers for the probabilities in the first place, should
    clip them to the allowed range.

    Parameters
    ----------
    photons: `astropy.table.Table`
        Table with photon properties
    col : string
        name of column
    index : int or array
        index of the photons that are assigned new values. Can be a single
        number or a boolean array.
    value : scalar or array
        New valuees to the assigned to column `col`. If this is an array, it
        needs to match the shape selected by the index array ``index``.


Simulate interaction of optical element with a single photon.

        This is called from the `process_photons` method in a loop over all
        photons. That method also collects the output values and inserts them
        into the photon list. ``process_photon`` can return any number of
        values in additon to the required dir, pos, etc.. Define a class
        attribute ``output_columns`` as a list of strings to determine how into
        which column these numbers should be inserted.

        Parameters
        ----------
        dir : `numpy.ndarray`
            4-d direction vector of ray in homogeneous coordinates
        pos : `numpy.ndarray`
            4-d position of last interaction pf the photons with any optical
            element in homogeneous coordinates. Together with ``dir`` this
            determines the equation of the ray.
        energy : float
            Photon energy in keV.
        polarization : float
            Polarization angle of the photons.

        Returns
        -------
        dir : `numpy.ndarray`
            4-d direction vector of ray in homogeneous coordinates
        pos : `numpy.ndarray`
            4-d position of last interaction pf the photons with any optical
            element in homogeneous coordinates. Together with ``dir`` this
            determines the equation of the ray.
        energy : float
            Photon energy in keV.
        polarization : float
            Polarization angle of the photons.
        probability : float
            Probability that the photon passes this optical element. Set to 0
            if the photon is absorbed, to 1 if it passes and to number between
            0 and 1 to express a probability that the photons passes.
        other : floats
            One number per entry in `output_columns`.


Simulate interaction of optical element with photons - vectorized.

        Derived classes should overwrite this function or `process_photon`.

        Parameters
        ----------
        photons: `astropy.table.Table` or `astropy.table.Row`
            Table with photon properties
        intersect : array
            Boolean array marking which photons should be processed by this
            element.
        interpos, intercoos : array (N, 4)
            The array ``interpos`` contains the intersection points in the
            global coordinate system, ``intercoos`` in a local coordiante
            system (2d in most cases).

        Returns
        -------
        photons: `astropy.table.Table` or `astropy.table.Row`
            Table with photon properties.
            If possible, the input table is modified in place, but in some
            cases this might not be possible and the returned Table may be
            a copy. Do not rely on either - use ``photons.copy()`` if you want
            to ensure you are working with an independent copy.


Parameters
        ----------
        intersect, interpos, intercoos : array (N, 4)
            The array ``interpos`` contains the intersection points in
            the global coordinate system, ``intercoos`` in the local
            (y,z) system of the grating.


Decorator for calculation that require a local coordinate system

    This is specifically meant to wrap the :meth:`process_photons` methods of
    any :class:`OpticalElement`; the current implementation expects the call
    signature of :meth:`process_photons`.

    This decorator transforms coordinates from the global system to the local
    system before a function call and back to the global system again after
    the function call.

    Parameters
    ----------
    f : callable with signature ``f(self, photons, *args, **kwargs)``
        The function to be decorated. In the signature, ``photons`` is a
        `astropy.table.Table`.
    colnames : list of strings
        List of all column names in the photon table to be transformed into a
        different coordinate system.


Return the Gaussian sigma of the width of the CCD resolution

        Parameters
        ----------
        energy : `~astropy.units.quantity.Quantity`
            True photon energy.

        Returns
        -------
        sigma : `~astropy.units.quantity.Quantity`
            Width of the Gaussian


Set sign convention for grating orders.

        This sets the following, somewhat arbitrary convention:
        Positive grating orders will are displaced along the local
        :math:`\hat e_z` vector, negative orders in the opposite
        direction. If the grating is rotated by :math:`-\pi` the
        physical situation is the same, but the sign of the grating
        order will be reversed.  In this sense, the convention chosen
        is arbitrarily. However, it has some practical advantages: The
        implementation is fast and all photons passing through the
        grating in the same diffraction order are displaced in the
        same way. (Contrary to the convention in :class:`CATGrating`.)
        ``order_sign_convention`` has to be a callable that accepts an
        array of eukledian direction vectors as input and returns
        ``+1``, ``-1``, or an array filled with ``-1`` or ``+1``.

        Parameters
        ----------
        p : np.array
            Array of Eucleadian direction vectors
        e_perp_groove : np.array
            Array of local groove directions at the positions where the photons
            hit


Get coordiante orthonormal coordinate system along groove direction.

        Parameters
        ----------
         intercoos : `numpy.ndarray` of shape (N, 2)
            coordinates in the coordiante system of the geometry (e.g. (x, y), or
            (r, phi)).

        Returns
        -------
        e_groove, e_perp_groove, n : `numpy.ndarray` of shape (N, 4)
            Vectors pointing along the groove direction (parallel to the surface),
            perpendicular to the groove direction (parallel to surface),
            and normal to surface.