 Script to compute KK estimated anisotropies for the BICEP field using different noise_powers or FWHM.
	Powers are defined in NoisePowerLists. (in microK-arcmin)
	Setting input defines whether to compare 'FWHM' or 'noise_power'

	Output is .npy files in NoiseAnalysis subfolder.


Return max/min of dataset across all powers


 Routine to compute and plot fractional errors in maps.
	Inputs: setting -> use noise_power or FWHM errors
	map_size, map separation
	Outputs: plots in 3deg0.5Comparison/ folder


Recreate significances from parameters.


Function to compute <I^2> for a map patch, from the cut-out T maps (unsmoothed).


Approximate the standard deviation by the 15.865, 84.135 percentiles. This reduces errors from some large data points.


 Compute the estimated angle, amplitude and polarisation fraction with noise, using zero-padding.
	Noise model is from Hu & Okamoto 2002 and errors are estimated using MC simulations, which are all saved.

	Input: map_id (tile number)
	padding_ratio (ratio of real-space padded tile width to initial tile width)
	map_size (tile width in degrees)
	sep (separation of tile centres in degrees)
	N_sims (number of MC simulations)
	noise_power (noise power in microK-arcmin)
	FWHM (noise FWHM in arcmin)
	slope (fiducial slope of C_l isotropic dust dependance)
	lMin / lMax (range of ell values to apply the estimators over)
	KKmethod (Boolean, controlling which SNR to use (see KKtest.py))
	rot (angle to rotate by before applying estimators)
	freq (desired map frequency; 150 GHz for BICEP, 353 GHz for Vansyngel)
	delensing_fraction (efficiency of delensing; i.e. 0.1=90% removed)
	debiasA (Boolean, whether to remove bias of monopole amplitude using noise-only sims)
	useTensors (Boolean, whether to include tensor noise from IGWs with r = 0.1)

	Output: First 6 values: [estimate,isotropic mean, isotropic stdev] for {A,Afs,Afc,fs,fc,str,ang}
	7th: full data for N_sims as a sequence of 7 lists for each estimate (each of length N_sims)
	8th: full data for N_sims for the monopole bias term
	9th: [estimate,isotropic mean, isotropic stdev] for Hexadecapole power


 Function to create plots for each tile.
	MakePlots command creates plots of epsilon histogram in the Maps/HistPlots/ directory.
	Other plots are saved in the Maps/ directory


 Function to create plots for each tile. These use hexadecapolar power mostly
	Plots are saved in the CorrectedMaps/ directory


Compute the global hexadecapole anisotropy over the patch, summing the epsilon values weighted by the S/N.
	The estimate is assumed Gaussian by Central Limit Theorem.
	Errors are obtained by computing estimate for many MC sims


Compute the global anisotropy over the patch, summing the epsilon values weighted by the S/N.
	The estimate is assumed Gaussian by Central Limit Theorem.
	Errors are obtained by computing estimate for many MC sims


Simple wrapper to plot a patch map for input data array


Recreate + plot significances from parameters.


Recreate + plot hexadecapoles from parameters.


 Return a spline fit to the ffp10 lensing C_l^BB spectrum. This is NOT identical to the CAMB spectrum used otherwise.
    If camb_spectrum=True; this uses the CAMB spectrum instead for rescaling.


Rescale the Fourier lens map using the power lensing map to ensure that it reproduces the correct
    spectrum for ell, to avoid excess of power at high ell, as observed in cut-outs.


 Function to create 2D B-mode fourier map from FFP10 lensed scalar map.
    Yanked from PaddedPower.MakePowerAndFourierMaps
    Input: map_id (tile number)
    map_size (in degrees)
    sep (separation of map centres)
    padding_ratio (ratio of padded map width to original (real-space) map width)
    fourier (return fourier space map?)
    power (return power map?)
    delensing_fraction (how much (uniform) delensing is applied)

    Output: lensing B-mode map in fourier [and power]-space


Use modified KK14 estimators to compute polarisation hexadecapole amplitude and angle via Afs,Afc parameters.
    This uses the noise model in hades.NoisePower.noise_model.
    A is computed recursively, since the S/N ratio depends on it (weakly).
    Rotation is performed to account for the effect of pixellation.

    Inputs: map (in power-space)
    map_size = width of map in degrees
    lMin,lMax= fitting range of l
    slope -> fiducial C_l map slope
    rot -> optional angle for pre-rotation of power-space map in degrees.
    delensing_fraction -> efficiency of delensing (0.1-> 90% removed)
    factor -> expected amplitude factor (to speed convergence)
    useTensors -> Boolean whether to include r = 0.1 tensor modes from IGWs
    debiasAmplitude -> Boolean whether to subtract noise+lensing C_l for estimation of A - this is ONLY for A
    rot_average -> Whether to average over rotations to correct for pixellation (NB: this does not change mean(Afs))
    correct -> TESTING - whether to correct for angle rotation (default = True)
    true_lensing -> if true use FFP10 lensing map B-mode spectrum rather than default CAMB spectrum

    Outputs:
    A,fs,fc, Afs, Afc from estimators. NB these are corrected for map rotation.


This is total C_l noise from lensing, B-modes and experimental noise


Correct for rotation using given angle (in radians)


Use modified KK14 estimators to compute polarisation hexadecapole amplitude and angle via Afs,Afc parameters.
    This uses the noise model in hades.NoisePower.noise_model.
    A is computed recursively, since the S/N ratio depends on it (weakly)

    Inputs: map (in power-space)
    map_size = width of map in degrees
    lMin,lMax= fitting range of l
    slope -> fiducial C_l map slope
    rot -> optional angle for pre-rotation of power-space map in degrees.
    delensing_fraction -> efficiency of delensing (0.1-> 90% removed)
    factor -> expected amplitude factor (to speed convergence)
    useTensors -> Boolean whether to include r = 0.1 tensor modes from IGWs
    debiasAmplitude -> Boolean whether to subtract noise+lensing C_l for estimation of A

    Outputs:
    A,fs,fc, Afs, Afc from estimators. NB these are corrected for any map pre-rotation.


Compute correlation function between monopole amplitude and debiased epsilon parameter across the whole (masked) sky, using data created using the above full_sky_reconstructor function.

	Inputs:
	NSIDE - Healpix NSIDE value (no. HEALPix pixels should exceed no. data points)
	LMAX - maxm ell to compute power spectra for
	maxDec - maxm declination used here (to avoid projection errors)

	Outputs:
	Correlations + power spectra saved in FullSky/Correlations/ subdirectory


This function bins a given set of data and errors.
    Errors are calculated using the unbiased weighted mean errors.

    Inputs: ells [xcoords]
    dat [ycoords]
    err [absolute errors]
    BIN [no. points per bin]

    Outputs:
    ellCen [central bin x]
    binDat [binned y value]
    binErr [error estimate]


 Function to compute mean np.sqrt(Q**2+U**2) and mean(T) for a map patch.
	Also saves mean window Factor <W^2> for each.


 Compute RA/dec coordinates of edge of BICEP region.
    Output is RA,dec in degrees.


Plot in Albers Equal Area Projection conic grid using the skymapper package.
    Inputs:
    data -> values to plot
    ra,dec -> coordinates
    cbar_label -> label for colorbar
    cmap -> colormap (e.g. 'jet)
    minMax -> [min,max] for colorbar
    decLims/raLims -> give min/max/step for RA and dec lines
    border -> [ra,dec] of border of BICEP region
    outFile -> location to save plot in
    sym_log -> whether to use symmetric log colorbar
    show -> boolean whether to show plot

    Output: image saved in outFile directory.


Plot in Mollweide Projection.
    Inputs:
    data -> values to plot
    ra,dec -> coordinates
    cbar_label -> label for colorbar
    cmap -> colormap (e.g. 'jet)
    minMax -> [min,max] for colorbar
    decLims/raLims -> give min/max/step for RA and dec lines
    border -> [ra,dec] of border of BICEP region
    outFile -> location to save plot in
    show -> boolean whether to show plot

    Output: image saved in outFile directory.


 Function to create plots for each tile.
	Other plots are saved in the Maps/ directory


 Compute the estimated angle, amplitude and polarisation strength in the presence of noise, following Hu & Okamoto 2002 noise model. Error is from MC simulations.
	Output: list of data for A, fs, fc (i.e. output[0]-> A etc.), with structure [map estimate, MC_standard_deviation, MC_mean]


 Compute a Monte Carlo random map, created from a Gaussian isotropic spectrum.
	Inputs: tempMap -> template power-domain map
	model_power -> model function for C_l(ell) (including noise + lensing)
	Output -> MC random map as a two-dimensional power-map


 Noise model of Hu & Okamoto 2002.
	Inputs: l,
	Full-Width-Half-Maximum of experiment in arcminutes,
	noise-power (Delta_P) in microK-arcmin.

	Unit conversions are done to put output in K^2
	-> to match input maps in K_CMB.


 Lensed C_l^{BB} power spectrum, generated by CAMB (Challinor, Lewis, Lasenby 2000) using Planck best-fit parameters.
	NB: file gives C_l (transformed from ell(ell+1)C_l/2pi from CAMB).
	This is in uK^2 - convert to K^2 for this.
	This returns a FUNCTION for input ell


 Tensor C_l^{BB} power spectrum, generated by CAMB (Challinor, Lewis, Lasenby 2000) using Planck best-fit parameters.
	NB: file gives C_l (transformed from ell(ell+1)C_l/2pi from CAMB).
	This is in uK^2 - convert to K^2 for this.
	This returns a FUNCTION for input ell

	r=0.1 is used here for an UPPER bound


 Create a Gaussian random noise power map from a given noise profile + lensing Cl from CAMB.
	NB: either real OR power map needs to be specified as template (power map faster)

	Inputs: realMap -> real space map to use as template
	powMap -> power2D space map to use as a template
	lMin/lMax -> l-range to create map over (matching that for estimators)
	FWHM/noise_power -> noise parameters (see noise_model() )
	windowFactor -> <W^2> for window function needed for window corrections
	delensing_fraction -> efficiency of lensing removal (90% removed -> 0.1)

	Output: power2D instance with Gaussian random noise power matching given C_l


 Compute RA/dec coordinates in correct patch.
	Output is RA,dec in degrees. N is no. data points


Find tile corresponding to some coordinates


 Create the B-,E-,T- space 2D power maps from input simulation maps. Simulation maps are those of Vansyngel+16 provided and reduced by Alex van Engelen. This uses the flipperPol hybrid scheme to minimise E-B leakage in B-maps.

    Input: map_id - number of map - integer from 00001-00315
    warnings - whether to ignore overwrite warnings
    plotPNG - whether to save PNG files
    Fits = whether to save FITS file

    Output: T,B,E maps are saved in the ProcMaps/ directory and pngs are in the ProcMaps/png directory


 Function to read in Power FITS file saved in createMap process.
    Input: map_ids: 0-315 denoting map number
    maptype - E,B or T map to open

    NB: THIS IS NOT CURRENTLY WORKING


Function to create 2D power map of the correct type (i.e. B,E or T). Returns this map. IN: map_type


 Ratio of dust spectral flux at two frequencies in GHz.
    This is uses a modified black-body spectrum, plus unit and color correction factors from Planck HFI XI (exact values from Rotti+Huffenberger16)).
    Default values (dust temperature Tdust in K and spectral index beta) are from Planck XXII.
    nu2 is a reference dust frequency


 Semi-analytically compute the central-ell value of a bin using a given expected ell dependence.
    Inputs: expected function for Cell (including noise etc.)
    [l1,l2] range of bin.

    This just computes the mean of input_func over the annulus by approx. integration, then computes the central ell from the inverse function.


 Compute the one-dimensional power spectrum of a given map from binning in annuli.
	Inputs: powermap, min.max l and bin size.
	binErr -> whether to return error in power from binning
	exactCen -> whether to compute centre of bin semi-analytically
	C_ell_model -> function modelling the C_ell (inc. lensing etc.)
	(needed if exactCen=True)

	Outputs: central l value, binned power, [binned power error]


Compute slope of power spectrum of given map in the [l_min,l_max] range, using a binning of l_step. Returns power-law slope and amplitude, with covariance matrix. returnFit => Boolean - whether to return bin widths and amplitude+error


 Create + plot the power map rescaled by a factor of l^{slope} where slope is found from radially binning the power spectrum in a specified range.

    NB: This can also plot unscaled plots using rescale=False

    Inputs: map_id - 0 to 315
    map_type='B'
    map_size = [3,5,10] -> map widths
    rescale - whether to rescale plot (else just return power map)
    show - Display rescaled map
    save - Save rescaled map
    showFit - show fitting of radially binned spectrum to model
    saveFit - save fitting as png
    l_min,l_max,l_step - define min/max/separation of l values
    returnMap = return power map instance + fitted slope

    Output: rescaled plot + fitted slope, saved in RescaledPlots/ dir (if save)
    + fitting plot (if saveFit)


 Plot the power map * l^{index} where index is the best fit power law index from radially binning the power plot.
    NB: the radial fit does not take into account errors.

    NB: NOW DEPRACATED - DO NOT USE

    Input: map_id number,
    plot -> whether to display map,
    save=whether to save map as png
    fitPlot = whether to plot fitting of radially binned spectrum

    Output: plot (if show=True) + saved in /ProcMaps/Aniso as png (if save=True)


Compute MC values of estimated quantities (yanked from PaddedPower.padded_estimates)


 Kamerud 1978 distribution PDF for X/Y for X,Y normally independently distributed.
	This uses form found in CG Ciao 2006 (https://www.emis.de/journals/HOA/ADS/Volume2006/78375.pdf)


 Compute the estimated angle, amplitude and polarisation fraction with noise, using zero-padding.
	Noise model is from Hu & Okamoto 2002 and errors are estimated using MC simulations.

	Input: map_id (tile number)
	padding_ratio (ratio of real-space padded tile width to initial tile width)
	map_size (tile width in degrees)
	sep (separation of tile centres in degrees)
	N_sims (number of MC simulations)
	noise_power (noise power in microK-arcmin)
	FWHM (noise FWHM in arcmin)
	slope (fiducial slope of C_l isotropic dust dependance)
	lMin / lMax (range of ell values to apply the estimators over)
	KKmethod (Boolean, controlling which SNR to use (see KKtest.py))
	rot (angle to rotate by before applying estimators - for testing)
	NB: if rot!=0, only ang, A, frac can be used

	Output: Estimated data for A, fs, fc and errors.


 Function to create 2D B-mode power map from real space map padded with zeros.
    Input: map_id (tile number)
    map_size (in degrees)
    sep (separation of map centres)
    padding_ratio (ratio of padded map width to original (real-space) map width)
    freq (experiment frequency (calibrated for 100-353 GHz))

    Output: B-mode map in power-space


Remove unnecessary high-ell data from litemap by Fourier transforming. This returns a real space map.


Remove unnecessary high-ell data from Fourier transform map, keeping the same power in the remaining pixels.


 Create a fourier space map of dust with given noise parameters. This is padded to the desired padding ratio.

	Inputs: padded_mask -> mask file, with appropriate padding - used as a real-space template here
	unpadded_mask -> original mask with no padding applied
	ell,CellNoise -> C_l for noise spectrum
	padding_ratio -> ratio of padded map width to unpadded map width
	unpadded -> whether to also return unpadded noise map
	log -> use logarithmic interpolation when generating the ell-Cell function


 Pad the real-space map with zeros.
	Padding_factor is ratio of padded map width to original map width.
	NB: WCS data is NOT changed by the zero-padding, so will be inaccurate if used.
	(this doesn't affect any later processes)


 Compute the estimated angle, amplitude and polarisation fraction with noise, correcting for bias.
	Noise model is from Hu & Okamoto 2002 and errors are estimated using MC simulations, which are all saved.

	Input: map_id (tile number)
	map_size (tile width in degrees)
	sep (separation of tile centres in degrees)
	N_sims (number of MC simulations)
	N_bias (no. sims used for bias computation)
	noise_power (noise power in microK-arcmin)
	FWHM (noise FWHM in arcmin)
	slope (fiducial slope of C_l isotropic dust dependance)
	lMin / lMax (range of ell values to apply the estimators over)
	l_step (step size for binning of 2D spectra)
	rot (angle to rotate by before applying estimators)
	freq (desired map frequency; 150 GHz for BICEP, 353 GHz for Vansyngel)
	delensing_fraction (efficiency of delensing; i.e. 0.1=90% removed)
	useTensors (Boolean, whether to include tensor noise from IGWs with r = 0.1)
	f_dust (factor to reduce mean dust amplitude by - for null testing - default = 1 - no reduction)
	rot_average (Boolean, whether to correct for pixellation error by performing (corrected) map rotations)
	useBias (Boolean, whether to use bias)

	Output: First 6 values: [estimate,isotropic mean, isotropic stdev] for {A,Afs,Afc,fs,fc,str,ang}
	7th: full data for N_sims as a sequence of 7 lists for each estimate (each of length N_sims)
	8th: full data for N_sims for the monopole bias term
	9th: [estimate,isotropic mean, isotropic stdev] for Hexadecapole power
	10: true monopole (from noiseless simulation) - for testing
	11th: bias (isotropic estimate of <H^2>)


 Compute the estimated angle, amplitude and polarisation fraction with noise, using zero-padding.
    Noise model is from Hu & Okamoto 2002 and errors are estimated using MC simulations, which are all saved.

    Input: map_id (tile number)
    padding_ratio (ratio of real-space padded tile width to initial tile width)
    map_size (tile width in degrees)
    sep (separation of tile centres in degrees)
    N_sims (number of MC simulations)
    noise_power (noise power in microK-arcmin)
    FWHM (noise FWHM in arcmin)
    slope (fiducial slope of C_l isotropic dust dependance)
    lMin / lMax (range of ell values to apply the estimators over)
    l_step (step size for binning of 2D spectra)
    rot (angle to rotate by before applying estimators)
    freq (desired map frequency; 150 GHz for BICEP, 353 GHz for Vansyngel)
    delensing_fraction (efficiency of delensing; i.e. 0.1=90% removed)
    useTensors (Boolean, whether to include tensor noise from IGWs with r = 0.1)
    f_dust (factor to reduce mean dust amplitude by - for null testing - default = 1 - no reduction)

    Output: First 6 values: [estimate,isotropic mean, isotropic stdev] for {A,Afs,Afc,fs,fc,str,ang}
    7th: full data for N_sims as a sequence of 7 lists for each estimate (each of length N_sims)
    8th: full data for N_sims for the monopole bias term
    9th: [estimate,isotropic mean, isotropic stdev] for Hexadecapole power
    10: true monopole (from noiseless simulation) - for testing


Use the estimate for A to construct analytic model.
        NB: This is just used for finding the centres of the actual binned data.


 Function to create plots for each tile. These use hexadecapolar power mostly
    Plots are saved in the CorrectedMaps/ directory. This is designed to read masked full sky data such as from cutoutAndSimulate.py


Compute the global hexadecapole anisotropy over the patch, summing the epsilon values weighted by the S/N.
    The estimate is assumed Gaussian by Central Limit Theorem.
    Errors are obtained by computing estimate for many MC sims

    returnAll parameter returns mean,std,estimate H^2 values
    plot parameter controls whether to create H^2 histogram


 This function calculates the slope of a plot using MCMC.
	Inputs: binned l values, bin width, mean and error in binned power spectrum.


Log prior used here - top hat function


 This is log of likelihood function - taken here as a Gaussian.
	We use log10(A) here for faster computations


Binning function (Bin size BIN) for correlations. Error is just standard deviation of inputs


Code to investigate dependence of angle and phase output on noise parameters and lMin


Plot parameter space for previous defined data


Returns the fixed distance to transform mass samples from detector
        to source frame if one is specified.


Sometimes scientific notation values in spex output are expressed
    without an 'e', e.g.  5.62495-100, so we need to convert by hand.


Write script to output lines.


Write header to script sent to spex.


Process all the temperatures and generate output files.


Given lines list, produce line HDU.

    lines is (element, ion, wavelength, epsilon, energy) list.


Interpret dumped lines file.

    Returns new HDU, number of lines, and number of elements


Take spex dumped model spectrum file, and extract continuum.


Interpret dumped continuum file.


Interpret spex dumped spectra.


Compute pseudo continuum and enter into continuum HDU.


Build up continuum output file.


    Returns the Lagrangian biases, (b1,b2), given nu.
    This is helpful if we want to make our basis set f, nu.


Returns the Lagrangian biases, (b1,b2), given nu.
    This is helpful if we want to make our basis set f, nu.


Query SDSS Data Release 18's SQL Server


Build the image directory and query the respective images based on the dataframe generated by the csv.


Create a SpatioTemporal Asset Catalog (STAC).

    These are used as inputs for Harmony requests, containing the URL and other
    information for input granules.

    For simplicity the geometric and temporal properties of each item are
    set to default values.


        Re-arrange axes and make a RA-DEC-POL-FREQ cube. assume
        axinorder() is already run and 4 axes exist in order.
        Helper function for tp2vis()

        input:   image
        output:  temporary image name


 return 2d kernel image of schwab's spheroidal function (Schwab 1984)

    CASA's sdimaging() uses the tabulated approx. of spheroidal function
    in Schwab 1984. Schwab's definition is different from the common
    definition as used in scipy. Within Schwab, the definition of
    key parameters (e.g., alpha, m) are not consistent in text and in table.
    Using the definition in the Schwab's table, the Schwab's spheroidal
    function is related to the scipy's as:

    Schwab(alpha,m,eta) =
      (1-eta^2)^((2-alpha)/2) *  scipy.special.pro_ang1(0, 0, m*pi/2, eta)[0]

    This means in the scipy's notation, n=0, alpha=m=0, and gamma=m*pi/2.
    In our definition, dist2d is eta, and the new parameter rscale to
    Note again that this alpha and m are different from Schwab's.

    Required:
    ---------
    alpha       Schwab's parameter alpha
    m           Schwab's parameter m
    rscale      size of kernel - it becomes zero at pixel=rscale
    dist2d      2d image that contains distances from center


    Mismatch of dirty and clean/restore beam areas become noticable in
    TP+INT joint-deconvolution. This function compares the two beam areas,
    rescales the flux density in residual map, and re-calculates the cleaned map.

    Note the mismatch problem exists even for INT alone, but without TP,
    the true flux is not known, so the problem is not noticable.

    Parameters
    -----------
    dirtyname   pre-name of dirty images (normally the filename without '.image')
    cleanname   pre-name of clean images
    pbcut       cutoff level of .pb map to define area for flux integration
                @todo clean() was using minpb=,   tclean() now uses pblimit=
    mask        user specified mask

    dirty and clean images must have the same shape, and it is assumed that
    your version of tclean() has also create the corresponding .residual and
    .pb images.

    Example usage:
    --------------

    Adjust beam size of residual image and add model and residual.
    > tp2vistweak('dirty','clean')
    This will expect 'dirty.image' and 'clean.image' as well as
    'clean.pb' and 'clean.residual'
    > tp2vistweak('dirty','clean',pbcut=0.9,mask='test_clean.image>10')


    Plotting TP, 7m, and 12m MSs

    MS should have been mstransform'd to the same freq range

    Parameters:
    -----------
    # need at least msTP
    mslist    list of measurement sets to plot
    ampPlot   True     amp-uvdistance plot
              False    weight-uvdistance plot
    show      True     plot in display as well as in file
              False    not plot in display, but in file
    outfig    file name of output figure


Create a plot of Slepian eigenvalues against rank.


Harmonic coefficients of the Earth for the polar cap region.


Compute the Slepian coefficients.


Create a plot of Slepian coefficients against rank.


Create the plot.


    Create fig 5.1 from Spatiospectral Concentration on a Sphere
    by Simons et al 2006.


Plot the required order and specified ranks.


Calculate the difference in Slepian coefficients by integration of the sphere.


Calculate the difference in Slepian coefficients by integration of the region.


Find the effective p rank of the Slepian function.


    Create fig 5.4 from Spatiospectral Concentration on a Sphere
    by Simons et al 2006.


Denoising demo using Slepian wavelets.


Plot the tiling of the Slepian line.


Compute the theoretical covariance of the wavelet coefficients.


Plot the Slepian wavelet coefficients for the Africa region.


Plot the axisymmetric wavelet coefficients for the Africa region.


    Plot a comparison of the absolute values of the wavelet coefficients
    compared to the Slepian coefficients. Expect the Slepian coefficients to
    decay faster than the wavelet coefficients.


Reconstruct a signal in Slepian space.


    Plot the difference between the theoretical &
    experimental covariances for the Slepian wavelets.


Plot the Slepian wavelet coefficients for the South America region.


Plot the axisymmetric wavelet coefficients for the South America region.


Denoising demo using Slepian function.


Plot the tiling of the harmonic line.


    Compute whether the function follows ergodicity.

    ergodicity fails for J_min = 0, because the scaling function will only
    cover f00. Hence <flm flm*> will be 0 in that case and the scaling
    coefficients will all be the same. So, if we do have J_min=0, we take the
    variance over all realisations instead (of course, we then won't have a
    standard deviation to compare it to the theoretical variance).


    Compute theoretical covariance of wavelet coefficients.

    the covariance <Wj(omega)Wj*(omega)> is given by the following expression:
    sigma^2 Sum(l,0) |Psi^j_l0|^2

    where sigma^2 is the variance of the harmonic coefficients and Psi^j_l0
    are the harmonic coefficients of the j-th wavelet

    a similar expression applies for the scaling function coefficients

    should we use the actual variance of each realisation instead?


Contrast denosiing with an Earth map versus South America map.


Reproduce the denoising demo from S2LET paper.


Analysis of the translation norm for referee.


Contrast denosiing with an Earth map versus Africa map.


Plot a Dirac impulse and it's Fourier transform.


Create a Ricker wavelet.


Plot the Slepian eigenvalues of the given mesh.


Create a Slepian limited latitude longitude class.


Earth with polar cap region.


Earth with limited latitude longitude region.


South America already has region.


Create a polar cap Slepian Dirac delta.


Create a limited latitude longitude Slepian Dirac delta.


Compute the Slepian wavelets for the polar cap region.


Compute the Slepian wavelets for the lim_lat_lon region.


Create a field on the mesh.


    Compute the signal to noise ratio.

    Args:
        signal: The unnoised signal.
        noise: The noised signal.
        signal_type: Specifier to improve logging.

    Returns:
        The signal-to-noise value of the noised signal.


    Compute the standard deviation of the noise.

    Args:
        signal: The noised signal.
        snr_in: The parameter controlling the signal-to-noise.
        denominator: Coefficients to use in computing the noise, defaults to all.

    Returns:
        The standard deviation of the noise.


Compute Gaussian white noise.


Compute Gaussian white noise in Slepian space.


Set pixels in pixel space to zero if the magnitude is less than the threshold.


r"""
    Perform thresholding in harmonic space.

    Args:
        L: The spherical harmonic bandlimit.
        wav_coeffs: The harmonic wavelet coefficients.
        sigma_j: The wavelet standard deviation \(\sigma_{j}\).
        n_sigma: The number of \(\sigma_{j}\) to threshold.

    Returns:
        The thresholded wavelet coefficients.


r"""
    Perform thresholding in Slepian wavelet space.

    Args:
        L: The spherical harmonic bandlimit.
        wav_coeffs: The Slepian wavelet coefficients
        sigma_j: The wavelet standard deviation \(\sigma_{j}\).
        n_sigma: The number of \(\sigma_{j}\) to threshold.
        slepian: The given Slepian object.

    Returns:
        The hard thresholded Slepian wavelet coefficients.


r"""
    Perform thresholding in Slepian space.

    Args:
        L: The spherical harmonic bandlimit.
        coefficients: The Slepian coefficients.
        sigma: The standard deviation of the noise \(\sigma\).
        n_sigma: The number of \(\sigma\) to threshold.
        slepian: The given Slepian object.

    Returns:
        The thresholded Slepian coefficients.


Compute sigma_j for wavelets used in denoising the signal.


r"""
    Compute \(\sigma_{j}\) for wavelets used in denoising the signal.

    Args:
        mesh_slepian: The Slepian mesh object containing the eigensolutions.
        signal: The noised signal.
        psi_j: The Slepian wavelet coefficients.
        snr_in: The parameter controlling the signal-to-noise ratio.

    Returns:
        The standard deviation of the noise.


r"""
    Perform thresholding in Slepian space of the mesh.

    Args:
        mesh_slepian: The Slepian mesh object containing the eigensolutions.
        wav_coeffs: The Slepian wavelet coefficients of the mesh.
        sigma_j: The wavelet standard deviation \(\sigma_{j}\).
        n_sigma: The number of \(\sigma\) to threshold.

    Returns:
        The thresholded wavelet coefficients of the mesh.


Split L into a list of arrays for parallelism.


Create a shared memory array to be used in a parallel function.


Attach an array to the shared memory within the parallel function.


Releases the shared memory object.


    Create a mask of a region of interested, the output will be based
    on the value of the provided L. The mask could be either:
    * polar cap - if theta_max provided
    * limited latitude longitude - if one of theta_min, theta_max,
                                   phi_min or phi_max is provided
    * arbitrary - just checks the shape of the input mask.


Attempt to read the mask from the config file.


Ensure the coefficients is bandlimited for a given region.


Create default region.


Create a boolean region for the given mesh.


Ensure that signal in pixel space is bandlimited.


Convert the region on vertices to faces.


Create the Africa region mask.


Create the South America region mask.


    Apply Gaussian smoothing to the given signal.

    s_lm = exp(-ell^2 sigma^2)
    s(omega) = exp(-theta^2 / sigma^2)
    FWHM = 2 * sqrt(ln2) * sigma


Circular mask at the north pole.


Define a region by angles, just need one to not be the default.


    Calculate appropriate resolution for given L.

    Args:
        L: The spherical harmonic bandlimit.

    Returns:
        The output spherical harmonic bandlimit to boost the resolution.


    Calculate nearest index of alpha/beta for translation
    this is due to calculating omega' through the pixel
    values - the translation needs to be at the same position
    as the rotation such that the difference error is small.


    For a given set of coefficients it finds the largest absolute value for a
    given plot type such that plots can have the same scale as the input.

    Args:
        function: The `Coefficients` value to find the amplitude of.
        plot_type: Select the `real`, `imag`, `abs` or `sum` value of the field.
        upsample: Whether to upsample the output.

    Returns:
        The maximum amplitude value.


Get the given plot type of the field.


    For the Slepian region set the outside area to negative infinity
    hence it is clear we are only interested in the coloured region.


Normalise function between 0 and 1 for visualisation.


Inverts and then boosts the field before plotting.


    For the noised mesh plots fix the amplitude.

    Args:
        f: The mesh field value.

    Returns:
        The noise adjusted maximum amplitude.


Compute the field over the whole mesh from the harmonic/Slepian coefficients.


    For the noised sphere plots fix the amplitude.

    Args:
        f: The spherical field value.

    Returns:
        The noise adjusted maximum amplitude value.


Compute the field over the samples from the harmonic/Slepian coefficients.


Create a spherical harmonic in harmonic space for the given index.


Calculate a boost in resolution for given flm.


    Upsamples the signal and performs the inverse harmonic transform .

    Args:
        flm: The spherical harmonic coefficients.
        L: The spherical harmonic bandlimit.
        resolution: The output resolution of the field values.
        reality: Whether the given spherical signal is real or not.
        spin: The value of the spin.

    Returns:
        The boosted field value.


    If the function created is created in pixel space rather than harmonic
    space then need to transform it into harmonic space first before using it.


Create vector of m values for a given L.


    Generate a normally distributed random signal of a
    complex signal with mean `0` and variance `1`.

    Args:
        L: The spherical harmonic bandlimit.
        rng: The random number generator object.
        var_signal: The variance of the signal.

    Returns:
        The coefficients of a random signal on the sphere.


    Compute the mesh forward transform from pixel space to Fourier space.

    Args:
        mesh: The given mesh object.
        u: The signal field value on the mesh.

    Returns:
        The basis functions of the mesh in Fourier space.


    Compute the mesh inverse transform from Fourier space to pixel space.

    Args:
        mesh: The given mesh object.
        u_i: The Fourier coefficients on the mesh.

    Returns:
        The values on the mesh in pixel space.


    Rotates the harmonic coefficients of the Earth to a view centred on South America.

    Args:
        earth_flm: The spherical harmonic coefficients of the Earth.
        L: The spherical harmonic bandlimit.

    Returns:
        The spherical harmonic coefficients of the Earth centred on South America.


    Rotates the harmonic coefficients of the Earth to a view centred on Africa.

    Args:
        earth_flm: The spherical harmonic coefficients of the Earth.
        L: The spherical harmonic bandlimit.

    Returns:
        The spherical harmonic coefficients of the Earth centred on Africa.


Create filename for angle as multiple of pi.


Add an extra argument to filename.


Middle part of filename.


    Print the unicode pi with a prefix of the multiple unless it's 1
    i.e. pi, 2pi, 3pi.


Convert a list of classes to snake case.


    Require all functions to be defined on faces
    this method handles an arbitrary number of functions.


Read in the given mesh region settings file.


    Compute the eigendecomposition of the mesh represented
    as a graph if already computed then it loads the data.


Read in the given mesh.


Compute the cotagent mesh laplacian.


For computing the Slepian D matrix the basis functions must be orthonormal.


    Compute the coefficients of the given tiling function in Slepian space.

    Args:
        f_p: The Slepian coefficients.
        wavelets: The Slepian wavelets.
        shannon: The Shannon number.

    Returns:
        The Slepian wavelets coefficients of the signal.


    Compute the inverse wavelet transform in Slepian space.

    Args:
        wav_coeffs: The Slepian wavelet coefficients.
        wavelets: The Slepian wavelets.
        shannon: The Shannon number.

    Returns:
        The coefficients of the signal in Slepian space.


    Compute the coefficients of the axisymmetric wavelets.

    Args:
        L: The spherical harmonic bandlimit.
        flm: The spherical harmonic coefficients.
        wavelets: Axisymmetric wavelets.

    Returns:
        Axisymmetric wavelets coefficients.


    Compute the inverse axisymmetric wavelet transform.

    Args:
        L: The spherical harmonic bandlimit.
        wav_coeffs: Axisymmetric wavelet coefficients.
        wavelets: Axisymmetric wavelets.

    Returns:
        Spherical harmonic coefficients of the signal.


Compute the axisymmetric wavelets.


r"""
    Compute the Slepian wavelets.

    Args:
        xlim: The x-axis value. \(L\) or \(L^2\) in the harmonic or Slepian case.
        B: The wavelet parameter. Represented as \(\lambda\) in the papers.
        j_min: The minimum wavelet scale. Represented as \(J_{0}\) in the papers.

    Returns:
        The Slepian wavelet generating functions.


    Find the coefficients within the shannon number to speed up computations.

    Args:
        wav_coeffs: The wavelet coefficients.
        axis: The axis to search over.

    Returns:
        The non-zero wavelet coefficients.


    Initialise Slepian object depending on input.

    Args:
        L: The spherical harmonic bandlimit.
        region: The Slepian region.

    Raises:
        ValueError: Invalid `region_type`, likely cause is invalid `mask_name`.

    Returns:
        The given Slepian object.


    Compute the Slepian inverse transform up to the Shannon number.

    Args:
        f_p: The Slepian coefficients.
        L: The spherical harmonic bandlimit.
        slepian: The given Slepian object.

    Returns:
        The values on the sphere in pixel space.


    Compute the Slepian forward transform for all coefficients.

    Args:
        L: The spherical harmonic bandlimit.
        slepian: The given Slepian object.
        f: The field value.
        flm: The spherical harmonic coefficients.
        mask: A boolean mask of the Slepian region.
        n_coeffs: The number of Slepian coefficients to use.

    Returns:
        The Slepian coefficients of the inputs.


r"""
    Compute \(S_{p}(\omega)\) for a given region.

    Args:
        L: The spherical harmonic bandlimit.
        slepian: The given Slepian object.

    Returns:
        The complex \(S_{p}(\omega)\) values.


Pick out the desired angle from Sp(omega).


    Compute the Slepian forward transform for all coefficients on the mesh.

    Args:
        mesh_slepian: The Slepian mesh object containing the eigensolutions.
        u: The signal field value on the mesh.
        u_i: The Fourier coefficients of the mesh.
        mask: Whether to use the mask to compute the coefficients.
        n_coeffs: The number of Slepian coefficients to use.

    Returns:
        The Slepian coefficients on the mesh.


    Compute the Slepian inverse transform on the mesh up to the Shannon number.

    Args:
        mesh_slepian: The Slepian mesh object containing the eigensolutions.
        f_p: The Slepian wavelet coefficients.

    Returns:
        The value of a function on the mesh in pixel space.


Calculate Sp(omega) for a given region.


Need eigenvalues and eigenvectors to be in a certain format.


Compute the effective Shannon number for a region of a mesh.


Compute the spherical Jacobian for the integration.


Compute the integration for the whole sphere.


Compute the integration for a region of the sphere.


Compute the integral of functions on the vertices.


Compute the integral of a region of functions on the vertices.


Create default camera view with a zoom factor.


Create the default plotly layout.


Create tick mark to use when using a non-normalised plot.


Create the default plotly colour bar.


Compute the sifting convolution between two multipoles.


Compute wavelet coefficients in Slepian space.


Compute field on the vertices of the mesh.


Decompose the signal into its Slepian coefficients via the given method.


Decompose all ranks of the Slepian coefficients.


Detect what method is used to perform the decomposition.


Check the requested rank is valid.


Create Slepian wavelets of the mesh.


Compute the Slepian functions of the mesh.


Compute the D matrix for the mesh eigenfunctions.


Calculate D matrix components for each chunk.


Fill in the D matrix elements using symmetries.


Calculate the D integral between two mesh basis functions.


Add Gaussian white noise to the signal.


Add region to the name if present if not a Slepian function.


Create the flm on the north pole.


Add Gaussian white noise converted to Slepian space.


Perform the plot.


Create the data for the matplotlib/plotly plot.


Boosts, forces plot type and then scales the field before plotting.


        Perform the plot.

        Args:
            colour: A built-in colormap name


Scales the field before plotting.


        For the Slepian region set the outside area to negative infinity
        hence it is clear we are only interested in the coloured region.


Create the flm for the whole Earth.


Load coefficients from file.


    Lazily loads POOCH registry before executing a function.

    Args:
        func: Function to be decorated

    Returns:
        Decorated function


        Load the POOCH registry if not already loaded and execute the function.

        Args:
            *args: Positional arguments for the function
            **kwargs: Keyword arguments for the function.

        Returns:
            Result of the decorated function


    Find a file on POOCH first and if not look in data folder.

    Args:
        filename: Filename to find

    Returns:
        The sought after file or nothing if not found


Create the flm for the whole CMB.


Check if valid map.


Check if valid function.


Read args from the command line.


Master plotting method.


Perform the rotation specific steps.


Perform the translation specific steps.


Perform the convolution specific steps.


Check if valid mesh name.


Define the function on the grid.


Compute wavelet coefficients of South America.


Compute all wavelets.


Compute ring in harmonic space.


r"""
        Perform the translation of the coefficients, used in the sifting convolution.

        Args:
            alpha: The point on the 2-sphere to translate to, i.e. the \(\phi\) value.
            beta: The point on the 2-sphere to translate to, i.e. the \(\theta\) value.
            shannon: The Shannon number, only used in the Slepian case.

        Returns:
            The translated spherical harmonic coefficients.


        Perform the sifting convolution of the two inputs.

        Args:
            f_coefficient: Input harmonic/Slepian coefficients.
            g_coefficient: Input harmonic/Slepian coefficients.
            shannon: The Shannon number, only used in the Slepian case.

        Returns:
            The sifting convolution of the two inputs.


        Add region to the name if present if not a Slepian function
        adds noise/smoothing if appropriate and bandlimit.


r"""
        Rotates given flm on the sphere by alpha/beta/gamma.

        Args:
            alpha: The third Euler angle, a \(\alpha\) rotation about the z-axis.
            beta: The second Euler angle, a \(\beta\) rotation about the y-axis.
            gamma: The first Euler angle, a \(\gamma\) rotation about the z-axis.

        Returns:
            The rotated spherical harmonic coefficients.


Compute the basis function at omega' for translation.


Compute alpha/beta if not provided.


Compute wavelet coefficients of Africa.


Compute wavelet coefficients of the Earth.


Compute the K matrix.


        Syntax:
        G = _slepian_integral().

        Output:
        G  =  Sub-integral matrix (obtained after the use of Wigner-D and
        Wigner-d functions in computing the Slepian integral) for all orders

        Description:
        This piece of code computes the sub-integral matrix (obtained after the
        use of Wigner-D and Wigner-d functions in computing the Slepian integral)
        for all orders using the formulation given in "Slepian spatialspectral
        concentration problem on the sphere: Analytical formulation for limited
        colatitude-longitude spatial region" by A. P. Bates, Z. Khalid and R. A.
        Kennedy.


Use conjugate symmetry property to reduce the number of iterations.


        Syntax:
        K = _slepian_matrix(dl, L, N, G).

        Input:
        G  =  Sub-integral matrix (obtained after the use of Wigner-D and
        Wigner-d functions in computing the Slepian integral) for all orders

        Output:
        K  =  Slepian matrix

        Description:
        This piece of code computes the Slepian matrix using the formulation
        given in "Slepian spatialspectral concentration problem on the sphere:
        Analytical formulation for limited colatitude-longitude spatial region"
        by A. P. Bates, Z. Khalid and R. A. Kennedy.


Solve the eigenproblem for the given function.


Identify region type based on the angle inputs or a mask name.


Solve eigenproblem with files already saved.


Solve eigenproblem from scratch and then saves the files.


Solve the eigenproblem for a given order m.


Sort all eigenvalues and eigenvectors for all orders.


        Syntax:
        Dm = _create_Dm_matrix(m, P).

        Input:
        m  =  order
        P(:,1)  =  Pl = Legendre Polynomials column vector for l = 0 : L-1
        P(:,2)  =  ell values vector

        Output:
        Dm = (L - m) square Slepian matrix for order m

        Description:
        This piece of code computes the Slepian matrix, Dm, for order m and all
        degrees, using the formulation given in "Spatiospectral Concentration on
        a Sphere" by F.J. Simons, F.A. Dahlen and M.A. Wieczorek.


Create Legendre polynomials table for matrix calculation.


Use in both serial and parallel calculations.


        Syntax:
        s = _wigner3j (l1, l2, l3, m1, m2, m3).

        Input:
        l1  =  first degree in Wigner 3j symbol
        l2  =  second degree in Wigner 3j symbol
        l3  =  third degree in Wigner 3j symbol
        m1  =  first order in Wigner 3j symbol
        m2  =  second order in Wigner 3j symbol
        m3  =  third order in Wigner 3j symbol

        Output:
        s  =  Wigner 3j symbol for l1,m1; l2,m2; l3,m3

        Description:
        Computes Wigner 3j symbol using Racah formula


        Eq 67 - Spherical Slepian functions and the polar gap in geodesy
        multiply by 1 + (-1)*(ell+ell').


Compute the D matrix in parallel.


        Use in both serial and parallel calculations.

        The hack with splitting into real and imaginary parts
        is not required for the serial case but here for ease


Calculate the D integral between two spherical harmonics.


    Sort the coordinates of the input data (e.g. lat or lon). Sort in ascending order by default.
    Used in all jet statistics and jet core algorithms included in this package.

    Parameters
    ----------
    coords : array-like
        Coords to sort in xarray data
    ascending : Boolean (default=True)
        Whether the coords should be sorted in ascending order


r"""
    This method detects jet cores and defines a boundary region beside those cores based on two windspeed thresholds.
    Two additional checks are applied after initial detection of cores to check whether cores within the same windspeed region
    are part of the same feature (see 'jet_boundary_ws_threshold'). This is achieved by checking whether regions with multiple
    jet cores are more than a certain distance apart (see 'jet_core_lat_distance') and the windspeed between two cores does
    not drop below a threshold (see 'ws_drop_threshold'). This function runs this method on a single time unit.

    This method returns four outputs
        1. 'jet_core_mask' -- Regions within each latitude/altitude that are local maxima have windspeeds above the 'jet_core_ws_threshold'
        2. 'jet_region_mask' -- Regions above, below, left and right of the jet core with windspeed above the 'jet_boundary_ws_threshold'
        3. 'jet_region_above_ws_threshold_mask' -- All contigious regions of windspeeds emcompassing a jet core above the 'jet_boundary_ws_threshold' (i.e. not just above, below, left and right)
        4. 'ws' -- Wind speed calculated from 'ua', 'va' inputs.

    Component of method of algorithm originally introduced in Manney et al. (2011) https://doi.org/10.5194/acp-11-6115-2011

    Parameters
    ----------
    row : xarray.Dataset
        Data of single time unit containing the variables: 'ua' and 'va', and the coordinates: 'lon', 'lat', 'plev'.
    jet_core_plev_limit: tuple or array
        Sequence of two values relating to the pressure level limit of the jet cores (original paper uses (100, 400) hPa).
    jet_core_ws_threshold : int or float
        Threshold used for jet-stream core point.
    jet_boundary_ws_threshold : int or float
        Threshold for jet-stream boundary point.
    ws_drop_threshold : int or float
        Threshold for drop in windspeed along the line between cores.
    jet_core_lat_distance : int or float
        Threshold for maximum distance between cores to be counted the same.
    check_diagonals : bool
        Whether to check the diagonal edges of each maxima in the latitude-altitude plane.

    Returns
    ----------
    row : xarray.Dataset
        Data for one time unit containing four new variables (ws, jet_core_mask, jet_region_mask, jet_region_above_ws_threshold_mask)


    Find indices of local maximas within a 2-D array. Should return two values which relate to the position of local maximas (if any).

    Component of method of algorithm originally introduced in Manney et al. (2011) https://doi.org/10.5194/acp-11-6115-2011

    Parameters
    ----------
    arr : xr.DataArray
        A 2-D array with numeric values (i.e. dtypes float or int)

    Returns
    ----------
    local_maxima : xr.DataArray
        A 2-D array with values relating to the index of the local maximas

    Examples
    ----------
    .. code-block:: python

        import numpy as np
        import xarray as xr

        # Example array
        data = np.array([
            [1, 2, 3, 4, 5],
            [2, 3, 4, 5, 4],
            [3, 4, 5, 6, 7],
            [4, 5, 6, 7, 6],
            [5, 6, 7, 8, 9]
        ])

        # Convert NumPy array to xarray DataArray
        data_array = xr.DataArray(data)

        local_maxima_indices = find_local_maxima_in_2d_dataarray(data_array)

        for i, j in local_maxima_indices:
            print(f"Local maximum at ({i}, {j}): {data_array[i, j]}")


    Find indices of local maximas within a 2-D array with check for the diagonals.
    Should return two values which relate to the position of local maximas (if any).

    Component of method from Manney et al. (2011) https://doi.org/10.5194/acp-11-6115-2011
    & Kuang et al (2014) (https://doi.org/10.1007/s00704-013-0994-x)

    Parameters
    ----------
    arr : xr.DataArray
        A 2-D array with numeric values (i.e. dtypes float or int)

    Returns
    ----------
    local_maxima : xr.DataArray
        A 1-D array with values relating to the index of the local maximas

    Examples
    ----------
    .. code-block:: python

        import numpy as np
        import xarray as xr

        # Example array
        data = np.array([
            [1, 2, 3, 4, 5],
            [2, 3, 4, 5, 4],
            [3, 4, 5, 6, 7],
            [4, 5, 6, 7, 6],
            [5, 6, 7, 8, 9]
        ])

        # Convert NumPy array to xarray DataArray
        data_array = xr.DataArray(data)

        local_maxima_indices = find_local_maxima_in_2d_dataarray_with_diagonals(data_array)

        for i, j in local_maxima_indices:
            print(f"Local maximum at ({i}, {j}): {data_array[i, j]}")


    Will create a mask with the same dimensions as the inputted mask_shape
    and only the local maxima as value 1. All other values will be 0.

    Component of method of algorithm originally introduced in Manney et al. (2011) https://doi.org/10.5194/acp-11-6115-2011

    Parameters
    ----------
    local_maximas : np.array
        An array containing values relating to the index of the local maximas in a mask with shape: mask_shape

    mask_shape : tuple
        Values to create a mask of given shape from.

    Returns
    ----------
    mask : np.array
        An array with shape: mask_shape with only the indexes of the local maximas as 1 All other values will 0.


    Will subset jet region mask to only those with an actual jet core in them.

    Component of method of algorithm originally introduced in Manney et al. (2011) https://doi.org/10.5194/acp-11-6115-2011

    Parameters
    ----------
    potential_jet_regions_mask : numpy.array
        Array of jet regions clusters as returned by scipy.ndimage.label

    actual_jet_region_nums : list
        A list of cluster ID numbers with local maximas in them

    Returns
    ----------
    potential_jet_regions_mask : numpy.array
        Subset version of jet regions mask containing only jet region clusters with local maxima in them


    This method will remove all values not left, right, above or below the input
    x, y coordinates in a 2-D array.

    Component of method of algorithm originally introduced in Manney et al. (2011) https://doi.org/10.5194/acp-11-6115-2011

    Parameters
    ----------
    array : np.array
        A 2-D array with numeric values relating to the jet region (i.e. dtypes float or int)
    x : int
        x-coordinate of a local maxima to subset jet region by
    y: int
        y-coordinate of a local maxima to subset input array by

    Returns
    ----------
    refined_array : np.array
        New 2-D array with values only around x and y coordinates


    Will check for a windspeed drop of a given threshold between cores

    Parameters
    ----------
    ws_between_cores : np.array
        A 1-D array with windspeed between two cores as determined by 'get_values_along_a_line_between_two_coordinates'
    ws_drop_threshold : int or float
        Windspeed threshold to check

    Returns
    ----------
    out : boolean
        True if windspeeds input have a drop greater than 'ws_drop_threshold'


    This method runs two checks on the jet cores to check whether there are regions with multiple jet cores.
    Firstly, it checks whether regions with multiple jet cores are more than a certain distance apart
    (default is 15 degrees, see 'jet_core_lat_distance'), and hence seperate cores.
    Secondly, it will check whether the windspeed between two cores drops below a threshold
    (default is 25 m/s, see 'ws_drop_threshold'), if so it will remove the latter core.

    Component of method of algorithm originally introduced in Manney et al. (2011) https://doi.org/10.5194/acp-11-6115-2011

    Parameters
    ----------
    row : xarray.Dataset
        Data of single time unit containing the variables: 'jet_region_contour_mask', and the coordinates: 'lon', 'lat', 'plev'
    initial_jet_core_masks :
        Initial mask of jet cores to check.
    ws_drop_threshold : int or float
        Threshold for drop in windspeed along the line between cores (default: 25 m/s)
    jet_core_lat_distance : int or float
        Threshold for maximum distance between cores to be counted the same (default: 15 degrees)

    Returns
    ----------
    jet_core_masks : numpy.array
        Final jet cores mask of shape of 'initial_jet_core_masks'.


    Subdivide the local_wind_maxima values into the Subtropical Jet (STJ) and Polar Front Jet (PFJ) based on Table 1 pg. 2709
    from Pena-Ortiz (2013) https://doi.org/10.1002/jgrd.50305.
    After the method in that paper, categorisation for the Northern Hemisphere STJ is only possible in DJF,
    and the latitude bands used are not based on December, March, June or September in Table 1 of that study.

    Component of method from Pena-Ortiz (2013) https://doi.org/10.1002/jgrd.50305

    Parameters
    ----------
    data : xarray.Dataset
        Input data with local wind maxima values

    Returns
    ----------
    data : xarray.Dataset
        data with polar_front_jet and subtropical_jet subdivisions


    Runs JetStreamCoreIdentificationAlgorithm method on a single day.

    Component of method from Kuang et al (2014) https://doi.org/10.1007/s00704-013-0994-x

    Parameters
    ----------
    row : xarray.Dataset
        Data of single time unit containing the variables: 'ua' and 'va', and the coordinates: 'lon', 'lat', 'plev'
    occurence_ws_threshold : int or float
        Threshold used to identify a jet-stream occurence point

    Returns
    ----------
    occ_alg.output_data : xarray.Dataset
        Data with jet occurence and centre points (1 for occurence, 2 for centre)


Class method for running the algorithm.


Run the algorithm.


        Calculates jet-stream centres based on if one jet-stream occurence grid
        is surrounded by 8 cells of jet-stream occurence (default is 30 m/s)


Get all coords with the jet occurence value.


Get all latitudes available in 3 by 3 grid.


Will return a list of the coordinates for all jet-centre points


        Will label all non 2 values of windspeed for the occurence
        Used in Kuang et al. 2014


    Runs JetStreamCoreIdentificationAlgorithm method on a single time unit.

    Component of method of jet_core_identification_algorithm in jsmetrics and is inspired by
    the method from Manney et al. (2011) (https://doi.org/10.5194/acp-11-6115-2011),
    which is described in Section 3.1 of that study.

    Parameters
    ----------
    row : xarray.Dataset
        Data of single time unit containing the variables: 'ua' and 'va', and the coordinates: 'lon', 'lat', 'plev'

    ws_core_threshold : int or float
        Threshold used for jet-stream core point
    ws_boundary_threshold : int or float
        Threshold for jet-stream boundary point

    Returns
    ----------
    row : xarray.Dataset
        Data for one time unit containing jet-cores (ID number relates to each unique core)


Class method for running algorithm


        Will return the indexes in the ws data that ARE jet-stream cores
        and COULD BE jet-stream core boundaries


        Will return an array of indexes to check for potential boundaries
        or jetstream cores.


        Recursive function that will return the IDs of a jet core boundary
        i.e. above 30 m/s surrounding a core of 40 m/s
        Will check one an area of potential boundaries contains a core
        and thus can be called boundaries.


        Recursive function that will return the IDs of all jet core boundaries
        i.e. above 30 m/s surrounding a core of 40 m/s
        Will check if an area of potential boundaries contains a core
        and thus can be called boundaries.


Add cores to data.


    Calculates the Meridional Circulation Index (MCI):
    MCI = v * abs(v) / u**2 * v**2
    When MCI = 0, the wind is purely zonal, and when MCI= 1 (-1), the flow is
    from the South (North).
    Component of method from Francis and Vavrus (2015) https://doi.org/10.1088/1748-9326/10/1/014005

    NOTE: The paper is not clear about whether the absolute value for MCI
    is taken instead thus 0-1

    Parameters
    ----------
    data : xarray.Dataset
        Data containing u-component and v-component wind-speed values

    Returns
    ----------
    output : xarray.DataArray
        Array of Meridional Circulation Index (MCI) values


    Calculates sinuosity by comparing the great circle distance between two (multi-)linestrings.

    Component of method from Cattiaux et al (2016) https://doi.org/10.1002/2016GL070309

    Parameters
    ----------
    line1 : shapely.geometry.LineString or shapely.geometry.MultiLineString
        Line to calculate great circle sinuosity from

    line2 : shapely.geometry.LineString or shapely.geometry.MultiLineString
        Line to calculate great circle distance from

    Returns
    ----------
    sinuosity : int or float
        Sinuosity value between length of two lines


r"""
    This method calculates a waviness metric: Meridional Circulation Index (MCI) from u- and v-components of wind.

    MCI is calculated as follows:

    .. math::
        MCI = \frac{v*|v|}{u^2+v^2}

    When MCI = 0, the wind is purely zonal, and when MCI= 1 (-1), the flow is from the South (North).

    This method was originally introduce in Francis & Vavrus (2015) https://doi.org/10.1088/1748-9326/10/1/014005
    and is described in the Section entitled: 'Meridional circulation Index (MCI)'.

    Please see 'Notes' below for any additional information about the implementation of this method
    to this package.

    Parameters
    ----------
    data : xarray.Dataset
        Data which should containing the variables: 'ua' and 'va', and the coordinates: 'lon', 'lat', 'plev' and 'time'.

    Returns
    ----------
    output : xarray.Dataset
        Data containing the one output: 'mci'

    Notes
    -----
    In the original methodology, MCI is expressed in terms of seasonal anomaly, we show you how to do this in 'Examples'

    Examples
    --------
    .. code-block:: python

        import jsmetrics
        import xarray as xr

        # Load in dataset with u and v components:
        uv_data = xr.open_dataset('path_to_uv_data')

        # Subset dataset to range used in original methodology (500 hPa & 20-80 N)):
        uv_sub = uv_data.sel(plev=500, lat=slice(20, 80))

        # Run statistic:
        fv15 = jsmetrics.waviness_metrics.francis_vavrus_2015(uv_sub)

        # Express MCI as a seasonal anomaly
        fv15_seasonal_anomalies = (mci['mci'] - mci['mci'].groupby('time.season').mean())


    Will calculate the atmospheric mass at a given pressure level.

    Radius of earth (R) = 6.372E3 km;
    Surface area of earth = 4 Pi R^2 = 5.1E8 km^2

    Component of method from Archer & Caldiera (2008) https://doi.org/10.1029/2008GL033614
    & Barnes & Polvani (2013) https://doi.org/10.1175/JCLI-D-12-00536.1

    Parameters
    ---------------
    pressure : float
        in kPa
    gravity : float
        m/s^2


    Calculate mass weighted average wind-speed.
    Component of method from Archer & Caldiera (2008) https://doi.org/10.1029/2008GL033614
    & Barnes & Polvani (2013) https://doi.org/10.1175/JCLI-D-12-00536.1

    Parameters
    ----------
    data : xarray.Dataset
        Data containing plev
    ws_col : string
        Name of column to calculate weighted average from (e.g. 'ws', 'ua', 'va')

    Returns
    ----------
    weighted_average : xr.DataArray
        Data with weighted average windspeed based on sum atmospheric mass


    Calculate mass-flux weighted pressure.

    Component of method from Archer & Caldiera (2008) https://doi.org/10.1029/2008GL033614

    Parameters
    ----------
    data : xarray.Dataset
        Data containing windspeed and plev
    ws_col : string
        Name of column to calculate weighted average from (e.g. 'ws', 'ua', 'va')

    Returns
    ----------
    mass_flux_weighted_pressure : xr.DataArray
        Data with mass flux weighted pressure


    Calculate mass-flux weighted latitude.

    Component of method from Archer & Caldiera (2008) https://doi.org/10.1029/2008GL033614
    NOTE: Problem with including 1000 hPa

    Parameters
    ----------
    data : xarray.Dataset
        Data containing windspeed and lat
    lat_min : int or float
        Minimum latitude to consider for weighted latitude
    lat_max : int or float
        Maximum latitude to consider for weighted latitude
    ws_col : string
        Name of column to calculate weighted average from (e.g. 'ws', 'ua', 'va')

    Returns
    ----------
    mass_flux_weighted_latitude : xr.DataArray
        Data with mass flux weighted latitude


    Calculate weights for a low pass Lanczos filter.
    A low-pass filter removes short-term random fluctations in a time series

    Component of method from Woollings et al (2010) http://dx.doi.org/10.1002/qj.625
    & Barnes & Polvani (2013) https://doi.org/10.1175/JCLI-D-12-00536.1

    TAKEN FROM:
    https://scitools.org.uk/iris/docs/v1.2/examples/graphics/SOI_filtering.html

    Parameters
    ----------
    window : int
        The length of the filter window.
    cutoff : float
        The cutoff frequency in inverse time steps.

    Returns
    ----------
    output : numpy.array
        filtered weights


    Will carry out Lanczos low-pass filter

    Component of method from Woollings et al (2010) http://dx.doi.org/10.1002/qj.625
    & Barnes & Polvani (2013) https://doi.org/10.1175/JCLI-D-12-00536.1

    Parameters
    ----------
    datarray : xarray.DataArray
        Data to apply filter to containing zonal mean (u-component) wind
    filter_freq : int
        number of days in filter
    window_size : int
        number of days in window for Lancoz filter

    Returns
    ----------
    window_cons : xarray.DataArray
        Filtered zonal mean data


    Will return the latitude and windspeed at the index of maximum wind speed
    from a row of data
    TODO: expects only one cell to be max

    Component of method from Woollings et al (2010) http://dx.doi.org/10.1002/qj.625
    & Barnes & Polvani (2013) https://doi.org/10.1175/JCLI-D-12-00536.1
    Barnes & Simpson (2017) https://doi.org/10.1175/JCLI-D-17-0299.1
    & Grise & Polvani (2016) https://doi.org/10.1002/2015JD024687

    Parameters
    ----------
    dataarray : xarray.dataset
        Dataset containing u-component wind

    Returns
    ----------
    jet_lats :
        Latitude of maximum windspeed
    jet_speeds : int or float
        Speed at latitude of maximum windspeed


    Will return a data array with the maximum windspeed and latitude of that maximum wind speed

    Component of method from Woollings et al (2010) http://dx.doi.org/10.1002/qj.625
    & Barnes & Simpson 2017 https://doi.org/10.1175/JCLI-D-17-0299.1

    Parameters
    ----------
    data : xarray.DataArray
        Data of single time unit containing u-component wind
    max_lat_ws : np.array
        Array of maximum latitude and windspeed at those maximums for each timeunit

    Returns
    ----------
    data_with_max_lats_ws : xarray.Dataset
        Data with maximum latitude and windspeed attached


    Carries out a Fourier transform for filtering keeping only low frequencies.

    Component of method from Woollings et al (2010) http://dx.doi.org/10.1002/qj.625

    ADAPTED FROM: https://scipy-lectures.org/intro/scipy/auto_examples/plot_fftpack.html

    Parameters
    ----------
    data : np.array - 1-d
        time series data at regular intervals
    highest_freq_to_keep : int
        highest frequency to keep in the fourier transform expression
        NOTE: starts at 0, so highest_freq_to_keep=1 will only keep the constant and first expresion

    Returns
    ----------
    filtered_sig : np.array
        Fourier-filtered signal

    Usage
    ----------
    # Apply filter of the two lowest frequencies
    apply_low_freq_fourier_filter(data, highest_freq_to_keep=2)


    Assigns the filtered data back to the returned dataset

    Component of method from Woollings et al (2010) http://dx.doi.org/10.1002/qj.625

    Parameters
    ----------
    data : xarray.Dataset
        Data to have fourier filtered data assigned to
    filtered_max_lats : numpy.array
        Fourier-filtered maximum latitude by given timeunit
    filtered_max_lats : numpy.array
        Fourier-filtered maximum speed at maximum latitude by given timeunit

    Returns
    ----------
    filtered_data : xarray.Dataset
        Data with fourier-filtered maximum lat and windspeed attached to it


    Calculates jet width on a given data array using the calculated jet lat and speed at that lat

    Component of method from Barnes & Polvani (2013) https://doi.org/10.1175/JCLI-D-12-00536.1

    Parameters
    ----------
    data_row : xarray.DataArray
        Data of single time unit containing u-component wind
    jet_lat : int or float
        Latitude of jet
    jet_speed : int or float
        Speed of jet at jet_lat

    Returns
    ----------
    jet_widths : array-like
        Width of jet-stream in latitude. Units for latitude are the same as the input units i.e. likely to be 'degrees_north'


    Creates a list of latitudes that are within a given distance from the jet_lat and only in one direction i.e. increasing or decreasing

    Component of method from Barnes & Polvani (2013) https://doi.org/10.1175/JCLI-D-12-00536.1

    Parameters
    ----------
    jet_lat : int or float
        Latitude of jet
    possible_surrounding_jet_lat_vals : array-like
        DataArray of lats where the windspeed value is within half speed of the jet_speed
    amount_to_add_to_lat_val : int or float
        Check if lat within this amount

    Returns
    ----------
    lats_within_jet : list
        All latitudes in continous group (i.e. within lat_resolution) around jet_lat


    Will downscale or upscale the resolution of latitude using a quadratic func.

    Component of method from Barnes & Polvani (2013) https://doi.org/10.1175/JCLI-D-12-00536.1
    & Grise & Polvani (2016) https://doi.org/10.1002/2015JD024687

    Parameters
    ----------
    lats : xr.DataArray or array-like
        Array of latitude values
    speeds :  xr.DataArray or array-like
        Array of wind-speeds
    scaled_lats : array-like
        Array of scaled latitude values of a given resolution (see rescale_lat_resolution function)

    Returns
    ----------
    scaled_lat_vals : xr.DataArray or array-like
        Array of rescaled latitude values based scaled_lats


    Add NaN value to array if any values in the array so it is a certain length

    Component of method from Barnes & Polvani (2013) https://doi.org/10.1175/JCLI-D-12-00536.1
    & Grise & Polvani (2016) https://doi.org/10.1002/2015JD024687

    Parameters
    ----------
    arr : array-like
        Array to check.
    len_required : int
        length of array required

    Returns
    ----------
    arr : array-like
        new array


    Quadratic function.

    Component of method from Barnes & Polvani (2013) https://doi.org/10.1175/JCLI-D-12-00536.1
    & Grise & Polvani (2016) https://doi.org/10.1002/2015JD024687

    Parameters
    ----------
    x : xr.DataArray or array-like
        Array 1
    y : xr.DataArray or array-like
        Array 2


    Apply quadratic function to an array of values (vals).

    Component of method from Barnes & Polvani (2013) https://doi.org/10.1175/JCLI-D-12-00536.1
    & Grise & Polvani (2016) https://doi.org/10.1002/2015JD024687

    Parameters
    ----------
    x : xr.DataArray or array-like
        Array 1
    y : xr.DataArray or array-like
        Array 2
    vals : array-like
        Values to apply function to

    Returns
    ----------
    output : array-like
        quadratic function output


    Fits a parabola.

    Component of method from Barnes & Polvani (2015) http://journals.ametsoc.org/doi/10.1175/JCLI-D-14-00589.1


    Joins the values for latitude and windspeed of the point of maximum windspeed for a given sector to the ten_day_mean data.

    Component of method from Barnes & Simpson 2017 https://doi.org/10.1175/JCLI-D-17-0299.1

    Parameters
    ----------
    ten_day_mean : xarray.Dataset
        Data containing u-component wind and resampled by 10 days

    Returns
    ----------
    ten_day_mean : xarray.Dataset
        Data with jet latitude and windspeed


    Joins the values for latitude and windspeed of the point of maximum windspeed to the main data

    Component of method from Barnes & Simpson 2017 https://doi.org/10.1175/JCLI-D-17-0299.1

    Parameters
    ----------
    data : xarray.Dataset
        Data containing u-component wind

    Returns
    ----------
    output : xarray.Dataset
        Data with jet latitude and windspeed


    Runs a cubic spline interpolation using 2 equal-length arrays.

    Component of method from Bracegirdle et al (2018) https://doi.org/10.1175/JCLI-D-17-0320.1

    Parameters
    ----------
    x : xarray.DataArray or array-like
        array 1 to run cubic spline interpolation
    y : xarray.DataArray or array-like
        array 2 to run cubic spline interpolation

    Returns
    ----------
    cubic_interpolation : np.array
        Cubic spline interpolation


    Runs a cubic spline interpolation to find maximum latitude and maximum windspeed at a given resolution of latitude.

    Component of method from Bracegirdle et al (2018) https://doi.org/10.1175/JCLI-D-17-0320.1

    Parameters
    ----------
    data : xr.Dataset
        must contain coords lat
    lat_resolution : int or float
        Latitude resolution in degrees for cubic spline interpolation
    ua_col : str
        u-component windspeed column (default='ua')

    Returns
    ----------
    max_lat : float
        latitude with the maximum wind-speed for time period as deterimined by cubic spline interpolation to a given lat resolution
    max_ws : float
        maximum wind-speed at the given latitude for time period


    Runs a cubic spline interpolation to find maximum latitude and maximum windspeed at a given resolution of latitude for each.

    Component of method from Bracegirdle et al (2018) https://doi.org/10.1175/JCLI-D-17-0320.1

    Parameters
    ----------
    data : xarray.Dataset
        Data containing u-component wind-speed. In the case of Bracegirdle et al. 2018, uses a seasonal and annual mean/climatology
    resolution : int or float
        Latitude resolution in degrees for cubic spline interpolation
    time_col : str
        Column name containing period (i.e. season, year, month, etc.)

    Returns
    ----------
    max_lats : list
        list of latitudes with the maximum wind-speed
    max_ws : list
        list of maximum wind-speed at the given latitudes

    Usage
    ----------
    seasonal_climatology = jetstream_metrics_components.get_climatology(data, "season")
    seasonal_zonal_mean = seasonal_climatology.mean("lon")
    (
        seasonal_max_lats,
        seasonal_max_ws,
    ) = jetstream_metrics_utils.run_cubic_spline_interpolation_for_each_unit_of_climatology_to_get_max_lat_and_ws(
        seasonal_zonal_mean, resolution=0.075, time_col="season"
    )


    Will get the centroid latitude of the u-component wind using:
    jet_lat = integral(60deg, 30deg)(zonal_u**2*lat) dlat / integral(60deg, 30deg)(zonal_u**2) dlat

    Component of method from Ceppi et al (2018) https://doi.org/10.1175/JCLI-D-17-0323.1 & Zappa et al. 2018 https://doi.org/10.1029/2019GL083653

    Parameters
    ----------
    zonal_mean : xarray.Dataset
        zonally-average data containing u-component wind data
    area_by_lat : xarray.DataArray
        Information on area in each latitude (i.e. m2 per latitude)


    Smooth jet position (jet latitude) by carrying out a convolution with a rectangular pulse.

    Component of method from Kerr et al. (2020) https://onlinelibrary.wiley.com/doi/10.1029/2020JD032735

    Parameters
    ----------
    jet_lat_data : xarray.DataArray
        Data detailing the latitude of jet-stream at each longitude of one time unit
    width_of_pulse : float or int
        Width to make rectangular pulse (likely to be in  'degrees_east')

    Returns
    ----------
    filtered_data : xarray.DataArray
        Data detailing the latitude of jet-stream that has been smoothed using rectangular pulse


r"""
    This method follows a two-step procedure used to detect jet-event occurences (here: 'jet_events_ws').

    The weighted average windspeed (:math:`\alpha vel`) for the jet events is calculated as follows:

    .. math::
        \alpha vel =  \frac{1}{p2-p1} \int_{p1}^{p2} (u^2+v^2)^{1/2} \,dp

    where :math:`p1`, :math:`p2` is min, max pressure level.

    After calculating :math:`\alpha vel`, in a second step a windspeed threshold to isolate jet events (the default is :math:`30 m s^{-1}`).

    This method was first introduced in Koch et al (2006) (https://doi.org/10.1002/joc.1255)
    and is described in section 2.2.2 of that study. The original methodology provides a third step
    (to produce a climatology of jet events), but this has been ignored in this implementation.
    Instead, we have provided an example of how to calculate this after running this method
    in 'Examples' below.

    Please see 'Notes' below for any additional information about the implementation of this method
    to this package.

    Parameters
    ----------
    data : xarray.Dataset
        Data which should containing the variables: 'ua' and 'va', and the coordinates: 'lon', 'lat', 'plev' and 'time'.
    ws_threshold : int or float
        Windspeed threshold used to extract from weighted average (default: 30 ms-1)

    Returns
    ----------
    output : xr.Dataset
        Data containing the output variable 'jet_events_ws'

    Notes
    -----
    The original methodology uses windspeed between 100-400 hPa to calculated the weighted average
    and 30 meters per second as the windspeed threshold.

    This equation for this method is provided on pg 287 of the Koch et al. 2006 paper.

    In the original paper, they accumulate the jet events into two-class jet typology (described in section 2.2.3
    of Koch et al. 2006)

    Examples
    --------
    .. code-block:: python

        import jsmetrics
        import xarray as xr

        # Load in dataset with u and v components:
        uv_data = xr.open_dataset('path_to_uv_data')

        # Subset dataset to range used in original methodology (100-400 hPa)):
        uv_sub = uv_data.sel(plev=slice(100, 400))

        # Run algorithm:
        koch_outputs = jsmetrics.jet_core_algorithms.koch_et_al_2006(uv_sub, ws_threshold=30)

        # Produce climatology of jet occurence events for each season and each month:
        koch_month_climatology = koch_outputs.groupby("time.month").mean("time")
        koch_season_climatology = koch_outputs.groupby("time.season").mean("time")


r"""
    This method detects 'jet occurrences', whereby each jet occurence is detected based
    on three rules applied to inputted wind speed (:math:`V = [u, v]`):

    1. :math:`|V|` is a local maxima in latitude and altitude plane
    2. :math:`|V| \ge 30 m s^{-1}`
    3. :math:`u \ge 0 m s^{-1}`.

    The implementation of this method here allows you to edit the :math:`|V|` threshold
    (by changing 'ws_threshold'), and :math:`u` threshold (by changing 'u_threshold').

    This method was originally introduce in Schiemann et al 2009 (https://doi.org/10.1175/2008JCLI2625.1)
    and is described in Section 2 of that study.

    Please see 'Notes' below for any additional information about the implementation of this method
    to this package.

    Parameters
    ----------
    data : xarray.Dataset
        Data which should containing the variables: 'ua' and 'va', and the coordinates: 'lon', 'lat', 'plev' and 'time'.
    ws_threshold : int or float
        Windspeed threshold used to extract jet maxima from resultant windspeed (default: 30 m/s)
    u_threshold : int or float
        Windspeed threshold used to extract u-component wind speed (default: 0 m/s)

    Returns
    ----------
    output : xr.Dataset
        Data containing the two output variables: 'ws' and 'jet_occurence'

    Notes
    -----
    While the original method is built on a four dimension slice of wind speed (time, lat, lon, plev),
    This implementation will work where there is only one pressure level, so a 3-d slice (time, lat, lon).

    **Slow method:** due to the nature of this method, it currently takes a moderately long time to run,
    i.e. 7.6 seconds per time unit on AMD Ryzen 5 3600 6-core processor.

    Examples
    --------
    .. code-block:: python

        import jsmetrics
        import xarray as xr

        # Load in dataset with u and v components:
        uv_data = xr.open_dataset('path_to_uv_data')

        # Subset dataset to range used in original methodology (100-500 hPa & 16.7-58.25 N, 42.5-220.5 E)):
        uv_sub = uv_data.sel(plev=slice(100, 500), lat=slice(16.7, 58.25), lon=slice(42.5, 220.5))

        # Run algorithm:
        schiemann_outputs = jsmetrics.jet_core_algorithms.schiemann_et_al_2009(uv_sub, ws_threshold=30)

        # Get jet occurence for first day in data
        schiemann_jet_occurence_first_day = schiemann_outputs['jet_occurence'].isel(time=0).max('plev')

        # Produce a jet occurence count across all pressure levels
        schiemann_jet_counts_all_levels = schiemann_outputs['jet_occurence'].sum(('time', 'plev'))

        # Use the jet occurence values as a mask to extract the jet windspeeds
        schiemann_jet_ws = schiemann_outputs.where(schiemann_outputs['jet_occurence'] > 0)['ws']


r"""
    This method detects jet cores (within an altitude range see 'jet_core_plev_limit') and a boundary region surrounding
    those cores based on two windspeed thresholds. Two checks are applied after initial detection of cores to check whether
    boundaries with more then one core are part of the same feature (the default threshold for these boundaries is 30 m/s,
    see 'jet_boundary_ws_threshold'). The two checks seperate cores based on whether the cores are more than a certain distance apart
    (default is 15 degrees, see 'jet_core_lat_distance') and whether the windspeed between two given cores does not drop
    below a windspeed threshold (default is 25 m/s, see 'ws_drop_threshold')

    This method returns four outputs
        1. **jet_core_mask** -- Regions within each latitude-altitude slice that are local maxima and have windspeeds above the 'jet_core_ws_threshold'
        2. **jet_region_mask** -- Regions above, below, left and right of any given jet core with windspeed above the 'jet_boundary_ws_threshold'
        3. **jet_region_contour_mask** -- All contigious regions of windspeeds emcompassing a jet core above the 'jet_boundary_ws_threshold' (i.e. not just above, below, left and right)
        4. **ws** -- Resultant wind speed calculated from 'ua', 'va' inputs.

    This method was originally introduce in Manney et al. (2011) (https://doi.org/10.5194/acp-11-6115-2011),
    and is described in Section 3.1 of that study. This method is also known as the JETPAC (Jet and
    Tropopause Products for Analysis and Characterization) software package, and available in its original
    form at request to NASA JPL.

    Please see 'Notes' below for any additional information about the implementation of this method
    to this package.

    Parameters
    ----------
    data : xarray.Dataset
        Data which should containing the variables: 'ua' and 'va', and the coordinates: 'lon', 'lat', 'plev' and 'time'.
    jet_core_plev_limit: tuple or array
        Sequence of two values relating to the pressure level limit of the jet cores (original paper uses 100-400 hPa)
    jet_core_ws_threshold : int or float
        Threshold used for jet cores (default=40 m/s)
    jet_boundary_ws_threshold : int or float
        Threshold for jet boundaries (default=30 m/s)
    ws_drop_threshold : int or float
        Threshold for drop in windspeed along direct interpolated path between cores (default: 25 m/s)
    jet_core_lat_distance : int or float
        Threshold for maximum distance between cores to be counted the same (default: 15 degrees)
    check_diagonals : bool
        Whether to check the diagonal edges of each maxima in the latitude-altitude plane. The original method does not include this (default: False).

    Returns
    ----------
    output : xarray.Dataset
        Data containing the four output variables: 'ws', 'jet_region_mask', 'jet_region_contour_mask', and 'jet_core_mask'

    Notes
    -----
    The implementation of this method varies slightly from the original, because this method will return a mask rather
    than dynamical values. The intention of this, is to allow these masks to be used to subset other variables such as windspeed
    (see 'Examples' for demonstration of how to use these masks).

    There is an update to this method introduced in Manney & Hegglin 2018 to include physically-based method to extract the
    subtropical jet is identified (and thus distinguished from polar jets). This update is not included in jsmetrics.

    'jet_region_above_ws_threshold_mask' is provided here, but not explicitly in the original methodology. It is meant as
    an alternative to using a contour to check which regions encompass jet cores.

    Examples
    --------
    .. code-block:: python

        import jsmetrics
        import xarray as xr

        # Load in dataset with u and v components:
        uv_data = xr.open_dataset('path_to_uv_data')

        # Subset dataset to range appropriate for original methodology (100-1000 hPa)):
        uv_sub = uv_data.sel(plev=slice(100, 1900))

        # Run algorithm:
        manney_outputs = jsmetrics.jet_core_algorithms.manney_et_al_2011(uv_sub, ws_core_threshold=40, ws_boundary_threshold=30, jet_core_plev_limit=(100, 400))

        # Use the jet core mask to extract the jet windspeeds
        manney_jet_ws = manney_outputs.where(manney_outputs['jet_core_mask'])['ws']


r"""
    This method follows a two step procedure for calculating local wind maxima and then subcategorising the local maxima into
    two distinct jet masks: the Subtropical Jet (STJ) and Polar Front Jet (PFJ).

    This method returns 4 outputs:
        1. **local_wind_maxima** -- Binary mask of local wind maxima
        2. **local_wind_maxima_by_monthyear** -- Same as above, but for monthyear frequency
        3. **polar_front_jet** -- Binary mask of the PFJ (by monthyear).
        4. **subtropical_jet** -- Binary ask of the STJ (by monthyear).

    This method was first introduced in Pena-Ortiz et al. (2013) (https://doi.org/10.1002/jgrd.50305) and
    is described in section 2 of that study.

    Please see 'Notes' below for any additional information about the implementation of this method
    to this package.

    Parameters
    ----------
    data : xarray.Dataset
        Data which should containing the variables: 'ua' and 'va', and the coordinates: 'lon', 'lat', 'plev' and 'time'.

    ws_threshold : int or float
        windspeed threshold to apply (default=30 ms-1)

    Returns
    ----------
    output : xarray.Dataset
        Data containing the four output variables: 'local_wind_maxima', 'local_wind_maxima_by_monthyear', 'polar_front_jet', and 'subtropical_jet'

    Notes
    -----
    See Table 1 in the respective paper for the categories used to seperate the STJ and PFJ.
    The STJ is only seperated in DJF for the Northern Hemisphere.

    **Slow method**: currently takes a long time i.e. 1.3 seconds per time unit with 8 plevs (i.e. 1.3 seconds per day)
    on a AMD Ryzen 5 3600 6-core processor.

    Examples
    --------
    .. code-block:: python

        import jsmetrics
        import xarray as xr

        # Load in dataset with u and v components:
        uv_data = xr.open_dataset('path_to_uv_data')

        # Subset dataset to range used in original methodology (100-400 hPa)):
        uv_sub = uv_data.sel(plev=slice(100, 400))

        # Run algorithm:
        pena_outputs = jsmetrics.penaortiz_et_al_2013(uv_sub)

        # Produce a count of polar front jet values across all pressure levels
        pena_pfj_counts_all_levels = pena_outputs['polar_front_jet'].sum(('monthyear', 'plev'))

        # Use the polar front jet values as a mask to extract the jet windspeeds
        uv_sub["ws_by_monthyear"] = (
            data["ws"]
            .resample(time="MS")
            .sum()
            .rename({"time": "monthyear"})
        )
        pena_pfj_ws = pena_outputs.where(pena_outputs['polar_front_jet'] > 0)['ws_by_monthyear']


r"""
    This method produces an event-based jet occurrences and jet center occurrences of the jet stream
    in a given atmospheric column.

    The outputs of this method will produce categorical values of three types:
        0. is not determined to be part of the jet
        1. is a jet occurence
        2. is jet core (upgraded from a jet occurence)

    This method was first introduced in Kuang et al (2014) (https://doi.org/10.1007/s00704-013-0994-x) and
    is described in section 2 of that study.

    Please see 'Notes' below for any additional information about the implementation of this method
    to this package.

    Parameters
    ----------
    data : xarray.Dataset
        Data which should containing the variables: 'ua' and 'va', and the coordinates: 'lon', 'lat', 'plev' and 'time'.
    occurence_ws_threshold : int or float
        Threshold used to identify a jet-stream occurence point (default=30)

    Returns
    ----------
    output : xarray.Dataset
        Data containing jet-occurence and jet-centres (1 is occurence, 2 is core, 0 is no jet)

    Notes
    -----
    **Slow method**: currently takes a long time i.e. 2 seconds per time unit with 1 plev (i.e. 2 seconds per day) on AMD Ryzen 5 3600 6-core processor

    Examples
    --------
    .. code-block:: python

        import jsmetrics
        import xarray as xr

        # Load in dataset with u and v components:
        uv_data = xr.open_dataset('path_to_uv_data')

        # Subset dataset to range used in original methodology (200 hPa)):
        uv_sub = uv_data.sel(plev=slice(200, 200))

        # Run algorithm:
        kuang_outputs = jsmetrics.kuang_et_al_2014(uv_sub, occurence_ws_threshold=30)

        # Extract only jet centers
        kuang_jet_centers = kuang_outputs.where(kuang_outputs['jet_ocurrence1_jet_centre2']==2)

        # Produce a count of jet centers across all pressure levels
        kuang_jet_centers_counts_all_levels = kuang_jet_centers.sum(('time', 'plev'))

        # Use the jet occurence values as a mask to extract the jet windspeeds
        kuang_jet_ws = kuang_outputs.where(kuang_outputs['jet_ocurrence1_jet_centre2'] > 0)['ws']


r"""
    This method extract seperate jet cores based on boundary and core windspeed thresholds.

    The output variable of this method includes two types:
        - 0 -- regions not determined to be part of the jet
        - 1-n -- Seperate jet core regions seperated by one condition: if two cores in the same region are more than 15 degrees of latitude away

    This method is inspired by the method from Manney et al. (2011) (https://doi.org/10.5194/acp-11-6115-2011),
    which is described in Section 3.1 of that study.

    Parameters
    ----------
    data : xarray.Dataset
        Data which should containing the variables: 'ua' and 'va', and the coordinates: 'lon', 'lat', 'plev' and 'time'.
    ws_core_threshold : int or float
        Threshold used for jet-stream core point (default=40)
    ws_boundary_threshold : int or float
        Threshold for jet-stream boundary point (default=30)

    Returns
    ----------
    output : xarray.Dataset
        Data containing the variable 'jet-core_id' (ID number relates to each unique core)

    Notes
    -----
    **Slow method**: currently takes a long time i.e. 7.6 seconds per time unit with 8 plevs
    (i.e. 7.6 seconds per day) on AMD Ryzen 5 3600 6-core processor

    Examples
    --------
    .. code-block:: python

        import jsmetrics
        import xarray as xr

        # Load in dataset with u and v components:
        uv_data = xr.open_dataset('path_to_uv_data')

        # Subset dataset to range appropriate for methodology (100-400 hPa)):
        uv_sub = uv_data.sel(plev=slice(100, 400))

        # Run algorithm:
        jca_outputs = jsmetrics.jet_core_algorithms.jet_core_identification_algorithm(uv_sub, ws_core_threshold=40, ws_boundary_threshold=30)


r"""
    This method follows an in-text description of 4-steps describing the algorithm of jet-stream identification
    from Woollings et al. (2010).

    This method returns four outputs:
        1. **jet_lat** -- latitude of maximum speed within low-pass filtered zonally averaged wind profile
        2. **jet_speed** -- speed at the 'jet_lat'
        3. **ff_jet_lat** -- Fourier-filtered 'jet_lat' by season
        4. **ff_jet_speed** -- Fourier-filtered 'jet_speed' by season

    This method was first introduce in Woollings et al (2010) (http://dx.doi.org/10.1002/qj.625) and
    is described in section 2 of that study.

    Please see 'Notes' below for any additional information about the implementation of this method
    to this package including Step 5 of the methodology.

    Parameters
    ----------
    data : xarray.Dataset
        Data which should containing the variables: 'ua', and the coordinates: 'lon', 'lat', 'plev' and 'time'.
    filter_freq : int
        number of days in filter (default=10 days)
    window_size : int
        number of days in window for Lancoz filter (default=61 days)

    Returns
    ----------
    output : xarray.Dataset
        Data containing the four output variables: 'jet_lat', 'jet_speed', 'ff_jet_lat' and 'ff_jet_speed'

    Notes
    -----
    In the original paper, a further step (Step 5) is carried out to express the values of jet latitude
    and jet speed anomalies from the seasonal cycle, this is shown in the 'Examples'

    Examples
    --------
    .. code-block:: python

        import jsmetrics
        import xarray as xr

        # Load in dataset with u component wind:
        ua_data = xr.open_dataset('path_to_u_data')

        # Subset dataset to range used in original methodology (700-925 hPa & 20-70 N, 300-360 W)):
        ua_sub = ua.sel(plev=slice(700, 925), lat=slice(20, 70), lon=slice(300, 360))

        # Run statistic with a filter frequency and window size used in the original methodology:
        w10 = jsmetrics.jet_statistics.woollings_et_al_2010(ua_sub, filter_freq=10, window_size=61)

        # Express jet latitude and speed as anomalies from smoothed seasonal cycle (Step 5 of methodology)
        w10_seasonal_anomalies = w10.groupby('time.season').apply(lambda row: row['jet_lat'] - row['ff_jet_lat'])


r"""
    This method constructs the 'eddy-driven jet' by performing a pressure-weighted average of zonal winds. The winds
    are then low-pass frequency filtered at each grid point using a 10-day Lanczos filter with 41 weights by default.
    Finally a 0.01 degree quadratic function is fitted to the peak of the subsequent wind speed profile for each time step.

    This method returns three outputs:
        1. **jet_lat** -- latitude of maximum speed at an interval of 0.01 degree within the subseqeunt profile
        2. **jet_speed** -- speed at the 'jet_lat'
        3. **jet_width** -- full width at half of the maximum 'jet_speed' within the 0.01 quadratic fitted to the peak of the wind speed profile

    *Note:* 'jet_width' is undefined where the 'jet_speed' never drops below half maximum.

    This method was originally introduce in Barnes & Polvani (2013) https://doi.org/10.1175/JCLI-D-12-00536.1
    and is described in Section 2b and 2c of that study.

    Please see 'Notes' below for any additional information about the implementation of this method
    to this package.

    Parameters
    ----------
    data : xarray.Dataset
        Data which should containing the variables: 'ua', and the coordinates: 'lon', 'lat', 'plev' and 'time'.
    filter_freq : int
        number of days in filter (default=10 timeunits)
    window_size : int
        number of days in window for low-pass Lancoz filter (default=41 timeunits)

    Returns
    ----------
    output : xarray.Dataset
        Data containing the three output variables: 'jet_lat', 'jet_speed', 'jet_width'

    Notes
    -----
    Whereas the original analysis using this method focuses on three distinct sections of the globe,
    the method here does not make any distinction. Instead we highlight how to do subset the input data and
    calculate this metric for those three sections in 'Examples.

    This method was based on the method from Woollings et al. (2010) (http://dx.doi.org/10.1002/qj.625)

    Examples
    --------
    .. code-block:: python

        import jsmetrics
        import xarray as xr

        # Load in dataset with u component wind:
        ua_data = xr.open_dataset('path_to_u_data')

        # Subset dataset to three sectors of the globe used in original methodology:
        ua_sh = ua.sel(plev=slice(700, 850), lat=slice(-90, 0)) # the Southern Hemisphere
        ua_na = ua.sel(plev=slice(700, 850), lat=slice(0, 90), lon=slice(300, 360) # the North Atlantic
        ua_np = ua.sel(plev=slice(700, 850), lat=slice(0, 90), lon=slice(135, 235) # the North Pacific

        # Run statistic with a filter frequency and window size used in the original methodology:
        bp13_sh = jsmetrics.jet_statistics.barnes_polvani_2013(ua_sh, filter_freq=10, window_size=41)
        bp13_na = jsmetrics.jet_statistics.barnes_polvani_2013(ua_na, filter_freq=10, window_size=41)
        bp13_np = jsmetrics.jet_statistics.barnes_polvani_2013(ua_np, filter_freq=10, window_size=41)


r"""
    This method calculates the latitude of the midlatitude eddy-driven jet ('jet_lat') by finding the peak value of the input u-wind field.
    A polynomial fit is then applied to get an appropriate value of 'jet_lat' at a resolution 0.01 degrees.
    As opposed to the original method, this implementation also returns the speed at the 'jet_lat': the 'jet_speed'

    This method was originally introduce in Grise & Polvani (2014) https://doi.org/10.1002/2013GL058466
    and is described in Section 2 of that study.

    Please see 'Notes' below for any additional information about the implementation of this method
    to this package.

    Parameters
    ----------
    data : xarray.Dataset
        Data which should containing the variables: 'ua', and the coordinates: 'lon', 'lat', 'plev' and 'time'.

    Returns
    ----------
    output : xarray.Dataset
        Data containing the two outputs: 'jet_lat' and 'jet_speed'

    Notes
    -----
    This method was originally developed for the jet streams in the Southern Hemisphere.

    The original paper also includes two other metrics for zonal mean atmospheric circulation.

    Examples
    --------
    .. code-block:: python

        import jsmetrics
        import xarray as xr

        # Load in dataset with u component wind:
        ua_data = xr.open_dataset('path_to_u_data')

        # Subset dataset to range used in original methodology (850 hPa &  -65--30N)):
        ua_sub = ua.sel(plev=850, lat=slice(-65, -30))

        # Run statistic:
        gp16 = jsmetrics.jet_statistics.grise_polvani_2014(ua_sub)