def stellingwerf_pdm_theta(times, mags, errs, frequency,<EOL>binsize=<NUM_LIT>, minbin=<NUM_LIT:9>):

period = <NUM_LIT:1.0>/frequency<EOL>fold_time = times[<NUM_LIT:0>]<EOL>phased = phase_magseries(times,<EOL>mags,<EOL>period,<EOL>fold_time,<EOL>wrap=False,<EOL>sort=True)<EOL>phases = phased['<STR_LIT>']<EOL>pmags = phased['<STR_LIT>']<EOL>bins = nparange(<NUM_LIT:0.0>, <NUM_LIT:1.0>, binsize)<EOL>binnedphaseinds = npdigitize(phases, bins)<EOL>binvariances = []<EOL>binndets = []<EOL>goodbins = <NUM_LIT:0><EOL>for x in npunique(binnedphaseinds):<EOL><INDENT>thisbin_inds = binnedphaseinds == x<EOL>thisbin_mags = pmags[thisbin_inds]<EOL>if thisbin_mags.size > minbin:<EOL><INDENT>thisbin_variance = npvar(thisbin_mags,ddof=<NUM_LIT:1>)<EOL>binvariances.append(thisbin_variance)<EOL>binndets.append(thisbin_mags.size)<EOL>goodbins = goodbins + <NUM_LIT:1><EOL><DEDENT><DEDENT>binvariances = nparray(binvariances)<EOL>binndets = nparray(binndets)<EOL>theta_top = npsum(binvariances*(binndets - <NUM_LIT:1>)) / (npsum(binndets) -<EOL>goodbins)<EOL>theta_bot = npvar(pmags,ddof=<NUM_LIT:1>)<EOL>theta = theta_top/theta_bot<EOL>return theta<EOL>

This calculates the Stellingwerf PDM theta value at a test frequency.

Parameters
----------

times,mags,errs : np.array
    The input time-series and associated errors.

frequency : float
    The test frequency to calculate the theta statistic at.

binsize : float
    The phase bin size to use.

minbin : int
    The minimum number of items in a phase bin to consider in the
    calculation of the statistic.

Returns
-------

theta_pdm : float
    The value of the theta statistic at the specified `frequency`.


times, mags, errs, frequency, binsize, minbin = task<EOL>try:<EOL><INDENT>theta = stellingwerf_pdm_theta(times, mags, errs, frequency,<EOL>binsize=binsize, minbin=minbin)<EOL>return theta<EOL><DEDENT>except Exception as e:<EOL><INDENT>return npnan<EOL><DEDENT>

This is a parallel worker for the function below.

Parameters
----------

task : tuple
    This is of the form below::

        task[0] = times
        task[1] = mags
        task[2] = errs
        task[3] = frequency
        task[4] = binsize
        task[5] = minbin

Returns
-------

theta_pdm : float
    The theta value at the specified frequency. nan if the calculation
    fails.


<EOL>stimes, smags, serrs = sigclip_magseries(times,<EOL>mags,<EOL>errs,<EOL>magsarefluxes=magsarefluxes,<EOL>sigclip=sigclip)<EOL>if len(stimes) > <NUM_LIT:9> and len(smags) > <NUM_LIT:9> and len(serrs) > <NUM_LIT:9>:<EOL><INDENT>if startp:<EOL><INDENT>endf = <NUM_LIT:1.0>/startp<EOL><DEDENT>else:<EOL><INDENT>endf = <NUM_LIT:1.0>/<NUM_LIT:0.1><EOL><DEDENT>if endp:<EOL><INDENT>startf = <NUM_LIT:1.0>/endp<EOL><DEDENT>else:<EOL><INDENT>startf = <NUM_LIT:1.0>/(stimes.max() - stimes.min())<EOL><DEDENT>if not autofreq:<EOL><INDENT>frequencies = nparange(startf, endf, stepsize)<EOL>if verbose:<EOL><INDENT>LOGINFO(<EOL>'<STR_LIT>' %<EOL>(frequencies.size, <NUM_LIT:1.0>/endf, <NUM_LIT:1.0>/startf)<EOL>)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>frequencies = get_frequency_grid(stimes,<EOL>minfreq=startf,<EOL>maxfreq=endf)<EOL>if verbose:<EOL><INDENT>LOGINFO(<EOL>'<STR_LIT>'<EOL>'<STR_LIT>' %<EOL>(frequencies.size,<EOL><NUM_LIT:1.0>/frequencies.max(),<EOL><NUM_LIT:1.0>/frequencies.min())<EOL>)<EOL><DEDENT><DEDENT>if (not nworkers) or (nworkers > NCPUS):<EOL><INDENT>nworkers = NCPUS<EOL>if verbose:<EOL><INDENT>LOGINFO('<STR_LIT>' % nworkers)<EOL><DEDENT><DEDENT>pool = Pool(nworkers)<EOL>if normalize:<EOL><INDENT>nmags = (smags - npmedian(smags))/npstd(smags)<EOL><DEDENT>else:<EOL><INDENT>nmags = smags<EOL><DEDENT>tasks = [(stimes, nmags, serrs, x, phasebinsize, mindetperbin)<EOL>for x in frequencies]<EOL>lsp = pool.map(_stellingwerf_pdm_worker, tasks)<EOL>pool.close()<EOL>pool.join()<EOL>del pool<EOL>lsp = nparray(lsp)<EOL>periods = <NUM_LIT:1.0>/frequencies<EOL>finitepeakind = npisfinite(lsp)<EOL>finlsp = lsp[finitepeakind]<EOL>finperiods = periods[finitepeakind]<EOL>try:<EOL><INDENT>bestperiodind = npargmin(finlsp)<EOL><DEDENT>except ValueError:<EOL><INDENT>LOGERROR('<STR_LIT>'<EOL>'<STR_LIT>')<EOL>return {'<STR_LIT>':npnan,<EOL>'<STR_LIT>':npnan,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':'<STR_LIT>',<EOL>'<STR_LIT>':{'<STR_LIT>':startp,<EOL>'<STR_LIT>':endp,<EOL>'<STR_LIT>':stepsize,<EOL>'<STR_LIT>':normalize,<EOL>'<STR_LIT>':phasebinsize,<EOL>'<STR_LIT>':mindetperbin,<EOL>'<STR_LIT>':autofreq,<EOL>'<STR_LIT>':periodepsilon,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':sigclip}}<EOL><DEDENT>sortedlspind = npargsort(finlsp)<EOL>sortedlspperiods = finperiods[sortedlspind]<EOL>sortedlspvals = finlsp[sortedlspind]<EOL>nbestperiods, nbestlspvals, peakcount = (<EOL>[finperiods[bestperiodind]],<EOL>[finlsp[bestperiodind]],<EOL><NUM_LIT:1><EOL>)<EOL>prevperiod = sortedlspperiods[<NUM_LIT:0>]<EOL>for period, lspval in zip(sortedlspperiods, sortedlspvals):<EOL><INDENT>if peakcount == nbestpeaks:<EOL><INDENT>break<EOL><DEDENT>perioddiff = abs(period - prevperiod)<EOL>bestperiodsdiff = [abs(period - x) for x in nbestperiods]<EOL>if (perioddiff > (periodepsilon*prevperiod) and<EOL>all(x > (periodepsilon*period) for x in bestperiodsdiff)):<EOL><INDENT>nbestperiods.append(period)<EOL>nbestlspvals.append(lspval)<EOL>peakcount = peakcount + <NUM_LIT:1><EOL><DEDENT>prevperiod = period<EOL><DEDENT>return {'<STR_LIT>':finperiods[bestperiodind],<EOL>'<STR_LIT>':finlsp[bestperiodind],<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':nbestlspvals,<EOL>'<STR_LIT>':nbestperiods,<EOL>'<STR_LIT>':lsp,<EOL>'<STR_LIT>':periods,<EOL>'<STR_LIT>':'<STR_LIT>',<EOL>'<STR_LIT>':{'<STR_LIT>':startp,<EOL>'<STR_LIT>':endp,<EOL>'<STR_LIT>':stepsize,<EOL>'<STR_LIT>':normalize,<EOL>'<STR_LIT>':phasebinsize,<EOL>'<STR_LIT>':mindetperbin,<EOL>'<STR_LIT>':autofreq,<EOL>'<STR_LIT>':periodepsilon,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':sigclip}}<EOL><DEDENT>else:<EOL><INDENT>LOGERROR('<STR_LIT>')<EOL>return {'<STR_LIT>':npnan,<EOL>'<STR_LIT>':npnan,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':'<STR_LIT>',<EOL>'<STR_LIT>':{'<STR_LIT>':startp,<EOL>'<STR_LIT>':endp,<EOL>'<STR_LIT>':stepsize,<EOL>'<STR_LIT>':normalize,<EOL>'<STR_LIT>':phasebinsize,<EOL>'<STR_LIT>':mindetperbin,<EOL>'<STR_LIT>':autofreq,<EOL>'<STR_LIT>':periodepsilon,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':sigclip}}<EOL><DEDENT>

This runs a parallelized Stellingwerf phase-dispersion minimization (PDM)
    period search.

    Parameters
    ----------

    times,mags,errs : np.array
        The mag/flux time-series with associated measurement errors to run the
        period-finding on.

    magsarefluxes : bool
        If the input measurement values in `mags` and `errs` are in fluxes, set
        this to True.

    startp,endp : float or None
        The minimum and maximum periods to consider for the transit search.

    stepsize : float
        The step-size in frequency to use when constructing a frequency grid for
        the period search.

    autofreq : bool
        If this is True, the value of `stepsize` will be ignored and the
        :py:func:`astrobase.periodbase.get_frequency_grid` function will be used
        to generate a frequency grid based on `startp`, and `endp`. If these are
        None as well, `startp` will be set to 0.1 and `endp` will be set to
        `times.max() - times.min()`.

    normalize : bool
        This sets if the input time-series is normalized to 0.0 and rescaled
        such that its variance = 1.0. This is the recommended procedure by
        Schwarzenberg-Czerny 1996.

    phasebinsize : float
        The bin size in phase to use when calculating the PDM theta statistic at
        a test frequency.

    mindetperbin : int
        The minimum number of elements in a phase bin to consider it valid when
        calculating the PDM theta statistic at a test frequency.

    nbestpeaks : int
        The number of 'best' peaks to return from the periodogram results,
        starting from the global maximum of the periodogram peak values.

    periodepsilon : float
        The fractional difference between successive values of 'best' periods
        when sorting by periodogram power to consider them as separate periods
        (as opposed to part of the same periodogram peak). This is used to avoid
        broad peaks in the periodogram and make sure the 'best' periods returned
        are all actually independent.

    sigclip : float or int or sequence of two floats/ints or None
        If a single float or int, a symmetric sigma-clip will be performed using
        the number provided as the sigma-multiplier to cut out from the input
        time-series.

        If a list of two ints/floats is provided, the function will perform an
        'asymmetric' sigma-clip. The first element in this list is the sigma
        value to use for fainter flux/mag values; the second element in this
        list is the sigma value to use for brighter flux/mag values. For
        example, `sigclip=[10., 3.]`, will sigclip out greater than 10-sigma
        dimmings and greater than 3-sigma brightenings. Here the meaning of
        "dimming" and "brightening" is set by *physics* (not the magnitude
        system), which is why the `magsarefluxes` kwarg must be correctly set.

        If `sigclip` is None, no sigma-clipping will be performed, and the
        time-series (with non-finite elems removed) will be passed through to
        the output.

    nworkers : int
        The number of parallel workers to use when calculating the periodogram.

    verbose : bool
        If this is True, will indicate progress and details about the frequency
        grid used for the period search.

    Returns
    -------

    dict
        This function returns a dict, referred to as an `lspinfo` dict in other
        astrobase functions that operate on periodogram results. This is a
        standardized format across all astrobase period-finders, and is of the
        form below::

            {'bestperiod': the best period value in the periodogram,
             'bestlspval': the periodogram peak associated with the best period,
             'nbestpeaks': the input value of nbestpeaks,
             'nbestlspvals': nbestpeaks-size list of best period peak values,
             'nbestperiods': nbestpeaks-size list of best periods,
             'lspvals': the full array of periodogram powers,
             'periods': the full array of periods considered,
             'method':'pdm' -> the name of the period-finder method,
             'kwargs':{ dict of all of the input kwargs for record-keeping}}


from scipy.stats import beta<EOL>frequencies = <NUM_LIT:1.0>/lspinfo['<STR_LIT>']<EOL>M = independent_freq_count(frequencies,<EOL>times,<EOL>conservative=conservative_nfreq_eff)<EOL>if peakvals is None:<EOL><INDENT>peakvals = lspinfo['<STR_LIT>']<EOL><DEDENT>nphasebins = nparange(<NUM_LIT:0.0>, <NUM_LIT:1.0>, lspinfo['<STR_LIT>']['<STR_LIT>']).size<EOL>ndet = times.size<EOL>false_alarm_probs = []<EOL>for peakval in peakvals:<EOL><INDENT>prob_xval = ((ndet-nphasebins)/(nphasebins-<NUM_LIT:1.0>))*peakval<EOL>prob_exceeds_val = beta.cdf(prob_xval,<EOL>(ndet-nphasebins)/<NUM_LIT>,<EOL>(nphasebins-<NUM_LIT:1.0>)/<NUM_LIT>)<EOL>import ipdb; ipdb.set_trace()<EOL>false_alarm_probs.append(<NUM_LIT:1.0> - (<NUM_LIT:1.0> - prob_exceeds_val)**M)<EOL><DEDENT>if inplace:<EOL><INDENT>lspinfo['<STR_LIT>'] = false_alarm_probs<EOL><DEDENT>return false_alarm_probs<EOL>

This returns the analytic false alarm probabilities for periodogram
    peak values.

    FIXME: this doesn't actually work. Fix later.

    The calculation follows that on page 3 of Zechmeister & Kurster (2009)::

        FAP = 1 − [1 − Prob(z > z0)]**M

    where::

        M is the number of independent frequencies
        Prob(z > z0) is the probability of peak with value > z0
        z0 is the peak value we're evaluating

    For PDM, the Prob(z > z0) is described by the beta distribution, according
    to:

    - Schwarzenberg-Czerny (1997;
      https://ui.adsabs.harvard.edu/#abs/1997ApJ...489..941S)

    - Zalian, Chadid, and Stellingwerf (2013;
      http://adsabs.harvard.edu/abs/2014MNRAS.440...68Z)

    This is given by::

        beta( (N-B)/2, (B-1)/2; ((N-B)/(B-1))*theta_pdm )

    Where::

        N = number of observations
        B = number of phase bins

    This translates to a scipy.stats call to the beta distribution CDF::

        x = ((N-B)/(B-1))*theta_pdm_best
        prob_exceeds_val = scipy.stats.beta.cdf(x, (N-B)/2.0, (B-1.0)/2.0)

    Which we can then plug into the false alarm prob eqn above with the
    calculation of M.

    Parameters
    ----------

    lspinfo : dict
        The dict returned by the
        :py:func:`~astrobase.periodbase.spdm.stellingwerf_pdm` function.

    times : np.array
        The times for which the periodogram result in ``lspinfo`` was
        calculated.

    conservative_nfreq_eff : bool
        If True, will follow the prescription given in Schwarzenberg-Czerny
        (2003):

        http://adsabs.harvard.edu/abs/2003ASPC..292..383S

        and estimate the effective number of independent frequences M_eff as::

            min(N_obs, N_freq, DELTA_f/delta_f)

    peakvals : sequence or None
        The peak values for which to evaluate the false-alarm probability. If
        None, will calculate this for each of the peak values in the
        ``nbestpeaks`` key of the ``lspinfo`` dict.

    inplace : bool
        If True, puts the results of the FAP calculation into the ``lspinfo``
        dict as a list available as ``lspinfo['falsealarmprob']``.

    Returns
    -------

    list
        The calculated false alarm probabilities for each of the peak values in
        ``peakvals``.


def _smooth_acf(acf, windowfwhm=<NUM_LIT:7>, windowsize=<NUM_LIT>):

convkernel = Gaussian1DKernel(windowfwhm, x_size=windowsize)<EOL>smoothed = convolve(acf, convkernel, boundary='<STR_LIT>')<EOL>return smoothed<EOL>

This returns a smoothed version of the ACF.

    Convolves the ACF with a Gaussian of given `windowsize` and `windowfwhm`.

    Parameters
    ----------

    acf : np.array
        The auto-correlation function array to smooth.

    windowfwhm : int
        The smoothing window Gaussian kernel's FWHM .

    windowsize : int
        The number of input points to apply the smoothing over.

    Returns
    -------

    np.array
        Smoothed version of the input ACF array.


def _smooth_acf_savgol(acf, windowsize=<NUM_LIT>, polyorder=<NUM_LIT:2>):

smoothed = savgol_filter(acf, windowsize, polyorder)<EOL>return smoothed<EOL>

This returns a smoothed version of the ACF.

This version uses the Savitsky-Golay smoothing filter.

Parameters
----------

acf : np.array
    The auto-correlation function array to smooth.

windowsize : int
    The number of input points to apply the smoothing over.

polyorder : int
    The order of the polynomial to use in the Savitsky-Golay filter.

Returns
-------

np.array
    Smoothed version of the input ACF array.


def _get_acf_peakheights(lags, acf, npeaks=<NUM_LIT:20>, searchinterval=<NUM_LIT:1>):

maxinds = argrelmax(acf, order=searchinterval)[<NUM_LIT:0>]<EOL>maxacfs = acf[maxinds]<EOL>maxlags = lags[maxinds]<EOL>mininds = argrelmin(acf, order=searchinterval)[<NUM_LIT:0>]<EOL>minacfs = acf[mininds]<EOL>minlags = lags[mininds]<EOL>relpeakheights = npzeros(npeaks)<EOL>relpeaklags = npzeros(npeaks,dtype=npint64)<EOL>peakindices = npzeros(npeaks,dtype=npint64)<EOL>for peakind, mxi in enumerate(maxinds[:npeaks]):<EOL><INDENT>if npall(mxi < mininds):<EOL><INDENT>continue<EOL><DEDENT>leftminind = mininds[mininds < mxi][-<NUM_LIT:1>]  <EOL>rightminind = mininds[mininds > mxi][<NUM_LIT:0>]  <EOL>relpeakheights[peakind] = (<EOL>acf[mxi] - (acf[leftminind] + acf[rightminind])/<NUM_LIT><EOL>)<EOL>relpeaklags[peakind] = lags[mxi]<EOL>peakindices[peakind] = peakind<EOL><DEDENT>if relpeakheights[<NUM_LIT:0>] > relpeakheights[<NUM_LIT:1>]:<EOL><INDENT>bestlag = relpeaklags[<NUM_LIT:0>]<EOL>bestpeakheight = relpeakheights[<NUM_LIT:0>]<EOL>bestpeakindex = peakindices[<NUM_LIT:0>]<EOL><DEDENT>else:<EOL><INDENT>bestlag = relpeaklags[<NUM_LIT:1>]<EOL>bestpeakheight = relpeakheights[<NUM_LIT:1>]<EOL>bestpeakindex = peakindices[<NUM_LIT:1>]<EOL><DEDENT>return {'<STR_LIT>':maxinds,<EOL>'<STR_LIT>':maxacfs,<EOL>'<STR_LIT>':maxlags,<EOL>'<STR_LIT>':mininds,<EOL>'<STR_LIT>':minacfs,<EOL>'<STR_LIT>':minlags,<EOL>'<STR_LIT>':relpeakheights,<EOL>'<STR_LIT>':relpeaklags,<EOL>'<STR_LIT>':peakindices,<EOL>'<STR_LIT>':bestlag,<EOL>'<STR_LIT>':bestpeakheight,<EOL>'<STR_LIT>':bestpeakindex}<EOL>

This calculates the relative peak heights for first npeaks in ACF.

    Usually, the first peak or the second peak (if its peak height > first peak)
    corresponds to the correct lag. When we know the correct lag, the period is
    then::

        bestperiod = time[lags == bestlag] - time[0]

    Parameters
    ----------

    lags : np.array
        An array of lags that the ACF is calculated at.

    acf : np.array
        The array containing the ACF values.

    npeaks : int
        THe maximum number of peaks to consider when finding peak heights.

    searchinterval : int
        From `scipy.signal.argrelmax`: "How many points on each side to use for
        the comparison to consider comparator(n, n+x) to be True." This
        effectively sets how many points on each of the current peak will be
        used to check if the current peak is the local maximum.

    Returns
    -------

    dict
        This returns a dict of the following form::

            {'maxinds':the indices of the lag array where maxes are,
             'maxacfs':the ACF values at each max,
             'maxlags':the lag values at each max,
             'mininds':the indices of the lag array where mins are,
             'minacfs':the ACF values at each min,
             'minlags':the lag values at each min,
             'relpeakheights':the relative peak heights of each rel. ACF peak,
             'relpeaklags':the lags at each rel. ACF peak found,
             'peakindices':the indices of arrays where each rel. ACF peak is,
             'bestlag':the lag value with the largest rel. ACF peak height,
             'bestpeakheight':the largest rel. ACF peak height,
             'bestpeakindex':the largest rel. ACF peak's number in all peaks}


def plot_acf_results(acfp, outfile, maxlags=<NUM_LIT>, yrange=(-<NUM_LIT>,<NUM_LIT>)):

import matplotlib.pyplot as plt<EOL>lags = acfp['<STR_LIT>']['<STR_LIT>'][:maxlags]<EOL>smoothedacf = acfp['<STR_LIT>'][:maxlags]<EOL>unsmoothedacf = acfp['<STR_LIT>']['<STR_LIT>'][:maxlags]<EOL>acfparams = acfp['<STR_LIT>']['<STR_LIT>'].copy()<EOL>acfparams.update({'<STR_LIT>': int(acfp['<STR_LIT>']['<STR_LIT>']/<NUM_LIT>)})<EOL>fig, ax1 = plt.subplots()<EOL>ax1.plot(lags, unsmoothedacf, label='<STR_LIT>',color='<STR_LIT>')<EOL>ax1.plot(lags, smoothedacf, label='<STR_LIT>', color='<STR_LIT>')<EOL>ax1.set_xlim((<NUM_LIT:0>,maxlags))<EOL>ax1.set_xlabel('<STR_LIT>')<EOL>acfmaxinds = acfp['<STR_LIT>']['<STR_LIT>']<EOL>for i, maxind in enumerate(acfmaxinds):<EOL><INDENT>if i == <NUM_LIT:0>:<EOL><INDENT>ax1.axvline(maxind,<EOL>linewidth=<NUM_LIT>,<EOL>color='<STR_LIT>',<EOL>ymin=<NUM_LIT>, ymax=<NUM_LIT>,<EOL>label='<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>ax1.axvline(maxind,<EOL>linewidth=<NUM_LIT>,<EOL>color='<STR_LIT>',<EOL>ymin=<NUM_LIT>, ymax=<NUM_LIT>)<EOL><DEDENT><DEDENT>plt.ylabel('<STR_LIT>')<EOL>plt.ylim(yrange)<EOL>ax1.legend()<EOL>plt.title('<STR_LIT:%s>' % repr(acfparams))<EOL>plt.tight_layout()<EOL>plt.savefig(outfile)<EOL>plt.close('<STR_LIT:all>')<EOL>return outfile<EOL>

This plots the unsmoothed/smoothed ACF vs lag.

Parameters
----------

acfp : dict
    This is the dict returned from `macf_period_find` below.

outfile : str
    The output file the plot will be written to.

maxlags: int
    The maximum number of lags to include in the plot.

yrange : sequence of two floats
    The y-range of the ACF vs. lag plot to use.


def macf_period_find(<EOL>times,<EOL>mags,<EOL>errs,<EOL>fillgaps=<NUM_LIT:0.0>,<EOL>filterwindow=<NUM_LIT:11>,<EOL>forcetimebin=None,<EOL>maxlags=None,<EOL>maxacfpeaks=<NUM_LIT:10>,<EOL>smoothacf=<NUM_LIT>,  <EOL>smoothfunc=_smooth_acf_savgol,<EOL>smoothfunckwargs=None,<EOL>magsarefluxes=False,<EOL>sigclip=<NUM_LIT>,<EOL>verbose=True,<EOL>periodepsilon=<NUM_LIT:0.1>,  <EOL>nworkers=None,      <EOL>startp=None,        <EOL>endp=None,          <EOL>autofreq=None,      <EOL>stepsize=None,      <EOL>):

<EOL>acfres = autocorr_magseries(<EOL>times,<EOL>mags,<EOL>errs,<EOL>maxlags=maxlags,<EOL>fillgaps=fillgaps,<EOL>forcetimebin=forcetimebin,<EOL>sigclip=sigclip,<EOL>magsarefluxes=magsarefluxes,<EOL>filterwindow=filterwindow,<EOL>verbose=verbose<EOL>)<EOL>xlags = acfres['<STR_LIT>']<EOL>if smoothacf and isinstance(smoothacf, int) and smoothacf > <NUM_LIT:0>:<EOL><INDENT>if smoothfunckwargs is None:<EOL><INDENT>sfkwargs = {'<STR_LIT>':smoothacf}<EOL><DEDENT>else:<EOL><INDENT>sfkwargs = smoothfunckwargs.copy()<EOL>sfkwargs.update({'<STR_LIT>':smoothacf})<EOL><DEDENT>xacf = smoothfunc(acfres['<STR_LIT>'], **sfkwargs)<EOL><DEDENT>else:<EOL><INDENT>xacf = acfres['<STR_LIT>']<EOL><DEDENT>peakres = _get_acf_peakheights(xlags, xacf, npeaks=maxacfpeaks,<EOL>searchinterval=int(smoothacf/<NUM_LIT:2>))<EOL>bestlspval = peakres['<STR_LIT>']<EOL>try:<EOL><INDENT>fity = npconcatenate((<EOL>[<NUM_LIT:0.0>, peakres['<STR_LIT>']],<EOL>peakres['<STR_LIT>'][peakres['<STR_LIT>'] > peakres['<STR_LIT>']]<EOL>))<EOL>fity = fity*acfres['<STR_LIT>']<EOL>fitx = nparange(fity.size)<EOL>fitcoeffs, fitcovar = nppolyfit(fitx, fity, <NUM_LIT:1>, cov=True)<EOL>fitbestperiod = fitcoeffs[<NUM_LIT:0>]<EOL>bestperiodrms = npsqrt(fitcovar[<NUM_LIT:0>,<NUM_LIT:0>])  <EOL><DEDENT>except Exception as e:<EOL><INDENT>LOGWARNING('<STR_LIT>'<EOL>'<STR_LIT>'<EOL>'<STR_LIT>')<EOL>fitcoeffs = nparray([npnan, npnan])<EOL>fitcovar = nparray([[npnan, npnan], [npnan, npnan]])<EOL>fitbestperiod = npnan<EOL>bestperiodrms = npnan<EOL>raise<EOL><DEDENT>naivebestperiod = peakres['<STR_LIT>']*acfres['<STR_LIT>']<EOL>if fitbestperiod < naivebestperiod:<EOL><INDENT>LOGWARNING('<STR_LIT>'<EOL>'<STR_LIT>' %<EOL>(fitbestperiod, naivebestperiod))<EOL><DEDENT>if npisfinite(fitbestperiod):<EOL><INDENT>bestperiod = fitbestperiod<EOL><DEDENT>else:<EOL><INDENT>bestperiod = naivebestperiod<EOL><DEDENT>return {'<STR_LIT>':bestperiod,<EOL>'<STR_LIT>':bestlspval,<EOL>'<STR_LIT>':maxacfpeaks,<EOL>'<STR_LIT>':npconcatenate([<EOL>[fitbestperiod],<EOL>peakres['<STR_LIT>'][<NUM_LIT:1>:maxacfpeaks]*acfres['<STR_LIT>']<EOL>]),<EOL>'<STR_LIT>':peakres['<STR_LIT>'][:maxacfpeaks],<EOL>'<STR_LIT>':xacf,<EOL>'<STR_LIT>':xlags*acfres['<STR_LIT>'],<EOL>'<STR_LIT>':xacf,<EOL>'<STR_LIT>':xlags,<EOL>'<STR_LIT>':'<STR_LIT>',<EOL>'<STR_LIT>':naivebestperiod,<EOL>'<STR_LIT>':fitbestperiod,<EOL>'<STR_LIT>':bestperiodrms,<EOL>'<STR_LIT>':fitcoeffs,<EOL>'<STR_LIT>':fitcovar,<EOL>'<STR_LIT>':{'<STR_LIT>':maxlags,<EOL>'<STR_LIT>':maxacfpeaks,<EOL>'<STR_LIT>':fillgaps,<EOL>'<STR_LIT>':filterwindow,<EOL>'<STR_LIT>':smoothacf,<EOL>'<STR_LIT>':sfkwargs,<EOL>'<STR_LIT>':magsarefluxes,<EOL>'<STR_LIT>':sigclip},<EOL>'<STR_LIT>':acfres,<EOL>'<STR_LIT>':peakres}<EOL>

This finds periods using the McQuillan+ (2013a, 2014) ACF method.

    The kwargs from `periodepsilon` to `stepsize` don't do anything but are used
    to present a consistent API for all periodbase period-finders to an outside
    driver (e.g. the one in the checkplotserver).

    Parameters
    ----------

    times,mags,errs : np.array
        The input magnitude/flux time-series to run the period-finding for.

    fillgaps : 'noiselevel' or float
        This sets what to use to fill in gaps in the time series. If this is
        'noiselevel', will smooth the light curve using a point window size of
        `filterwindow` (this should be an odd integer), subtract the smoothed LC
        from the actual LC and estimate the RMS. This RMS will be used to fill
        in the gaps. Other useful values here are 0.0, and npnan.

    filterwindow : int
        The light curve's smoothing filter window size to use if
        `fillgaps='noiselevel`'.

    forcetimebin : None or float
        This is used to force a particular cadence in the light curve other than
        the automatically determined cadence. This effectively rebins the light
        curve to this cadence. This should be in the same time units as `times`.

    maxlags : None or int
        This is the maximum number of lags to calculate. If None, will calculate
        all lags.

    maxacfpeaks : int
        This is the maximum number of ACF peaks to use when finding the highest
        peak and obtaining a fit period.

    smoothacf : int
        This is the number of points to use as the window size when smoothing
        the ACF with the `smoothfunc`. This should be an odd integer value. If
        this is None, will not smooth the ACF, but this will probably lead to
        finding spurious peaks in a generally noisy ACF.

        For Kepler, a value between 21 and 51 seems to work fine. For ground
        based data, much larger values may be necessary: between 1001 and 2001
        seem to work best for the HAT surveys. This is dependent on cadence, RMS
        of the light curve, the periods of the objects you're looking for, and
        finally, any correlated noise in the light curve. Make a plot of the
        smoothed/unsmoothed ACF vs. lag using the result dict of this function
        and the `plot_acf_results` function above to see the identified ACF
        peaks and what kind of smoothing might be needed.

        The value of `smoothacf` will also be used to figure out the interval to
        use when searching for local peaks in the ACF: this interval is 1/2 of
        the `smoothacf` value.

    smoothfunc : Python function
        This is the function that will be used to smooth the ACF. This should
        take at least one kwarg: 'windowsize'. Other kwargs can be passed in
        using a dict provided in `smoothfunckwargs`. By default, this uses a
        Savitsky-Golay filter, a Gaussian filter is also provided but not
        used. Another good option would be an actual low-pass filter (generated
        using scipy.signal?) to remove all high frequency noise from the ACF.

    smoothfunckwargs : dict or None
        The dict of optional kwargs to pass in to the `smoothfunc`.

    magsarefluxes : bool
        If your input measurements in `mags` are actually fluxes instead of
        mags, set this is True.

    sigclip : float or int or sequence of two floats/ints or None
        If a single float or int, a symmetric sigma-clip will be performed using
        the number provided as the sigma-multiplier to cut out from the input
        time-series.

        If a list of two ints/floats is provided, the function will perform an
        'asymmetric' sigma-clip. The first element in this list is the sigma
        value to use for fainter flux/mag values; the second element in this
        list is the sigma value to use for brighter flux/mag values. For
        example, `sigclip=[10., 3.]`, will sigclip out greater than 10-sigma
        dimmings and greater than 3-sigma brightenings. Here the meaning of
        "dimming" and "brightening" is set by *physics* (not the magnitude
        system), which is why the `magsarefluxes` kwarg must be correctly set.

        If `sigclip` is None, no sigma-clipping will be performed, and the
        time-series (with non-finite elems removed) will be passed through to
        the output.

    verbose : bool
        If True, will indicate progress and report errors.

    Returns
    -------

    dict
        Returns a dict with results. dict['bestperiod'] is the estimated best
        period and dict['fitperiodrms'] is its estimated error. Other
        interesting things in the output include:

        - dict['acfresults']: all results from calculating the ACF. in
          particular, the unsmoothed ACF might be of interest:
          dict['acfresults']['acf'] and dict['acfresults']['lags'].

        - dict['lags'] and dict['acf'] contain the ACF after smoothing was
          applied.

        - dict['periods'] and dict['lspvals'] can be used to construct a
          pseudo-periodogram.

        - dict['naivebestperiod'] is obtained by multiplying the lag at the
          highest ACF peak with the cadence. This is usually close to the fit
          period (dict['fitbestperiod']), which is calculated by doing a fit to
          the lags vs. peak index relation as in McQuillan+ 2014.


baseline = times.max() - times.min()<EOL>nsamples = times.size<EOL>df = <NUM_LIT:1.> / baseline / samplesperpeak<EOL>if minfreq is not None:<EOL><INDENT>f0 = minfreq<EOL><DEDENT>else:<EOL><INDENT>f0 = <NUM_LIT:0.5> * df<EOL><DEDENT>if maxfreq is not None:<EOL><INDENT>Nf = int(npceil((maxfreq - f0) / df))<EOL><DEDENT>else:<EOL><INDENT>Nf = int(<NUM_LIT:0.5> * samplesperpeak * nyquistfactor * nsamples)<EOL><DEDENT>if returnf0dfnf:<EOL><INDENT>return f0, df, Nf, f0 + df * nparange(Nf)<EOL><DEDENT>else:<EOL><INDENT>return f0 + df * nparange(Nf)<EOL><DEDENT>

This calculates a frequency grid for the period finding functions in this
    module.

    Based on the autofrequency function in astropy.stats.lombscargle.

    http://docs.astropy.org/en/stable/_modules/astropy/stats/lombscargle/core.html#LombScargle.autofrequency


mod_mag = (mag - npmin(mag))/(<NUM_LIT>*(npmax(mag) - npmin(mag))) - <NUM_LIT><EOL>fold_time = npmin(time) <EOL>init_f = <NUM_LIT:1.0>/end_p<EOL>end_f = <NUM_LIT:1.0>/init_p<EOL>n_freqs = npceil((end_f - init_f)/f_step)<EOL>if verbose:<EOL><INDENT>print('<STR_LIT>' %<EOL>(n_freqs,init_f,end_f))<EOL><DEDENT>out_periods = npempty(n_freqs,dtype=np.float64)<EOL>out_strlens = npempty(n_freqs,dtype=np.float64)<EOL>p_goodflags = npempty(n_freqs,dtype=bool)<EOL>j_range = len(mag)-<NUM_LIT:1><EOL>for i in range(int(n_freqs)):<EOL><INDENT>period = <NUM_LIT:1.0>/init_f<EOL>phase = (time - fold_time)/period - npfloor((time - fold_time)/period)<EOL>phase_sort_ind = npargsort(phase)<EOL>phase_sorted = phase[phase_sort_ind]<EOL>mod_mag_sorted = mod_mag[phase_sort_ind]<EOL>strlen = <NUM_LIT:0.0><EOL>epsilon = <NUM_LIT> * npmean(err)<EOL>delta_l = <NUM_LIT> * (epsilon - <NUM_LIT:0.5>*(epsilon**<NUM_LIT:2>)) * (len(time) -<EOL>npsqrt(<NUM_LIT>/epsilon))<EOL>keep_threshold_1 = <NUM_LIT> + <NUM_LIT>*delta_l<EOL>l = <NUM_LIT>*len(time)<EOL>sig_l = len(time)/<NUM_LIT><EOL>keep_threshold_2 = l + <NUM_LIT>*sig_l<EOL>for j in range(j_range):<EOL><INDENT>strlen += npsqrt( (mod_mag_sorted[j+<NUM_LIT:1>] - mod_mag_sorted[j])**<NUM_LIT:2> +<EOL>(phase_sorted[j+<NUM_LIT:1>] - phase_sorted[j])**<NUM_LIT:2> )<EOL><DEDENT>strlen += npsqrt( (mod_mag_sorted[<NUM_LIT:0>] - mod_mag_sorted[-<NUM_LIT:1>])**<NUM_LIT:2> +<EOL>(phase_sorted[<NUM_LIT:0>] - phase_sorted[-<NUM_LIT:1>] + <NUM_LIT:1>)**<NUM_LIT:2> )<EOL>if ((strlen < keep_threshold_1) or (strlen < keep_threshold_2)):<EOL><INDENT>p_goodflags[i] = True<EOL><DEDENT>out_periods[i] = period<EOL>out_strlens[i] = strlen<EOL>init_f += f_step<EOL><DEDENT>return (out_periods,out_strlens,p_goodflags)<EOL>

This is the super-slow naive version taken from my thesis work.

Uses the string length method in Dworetsky 1983 to calculate the period of a
time-series of magnitude measurements and associated magnitude
errors. Searches in linear frequency space (which obviously doesn't
correspond to a linear period space).

PARAMETERS:

time: series of times at which mags were measured (usually some form of JD)
mag: timeseries of magnitudes (np.array)
err: associated errs per magnitude measurement (np.array)
init_p, end_p: interval to search for periods between (both ends inclusive)
f_step: step in frequency [days^-1] to use

RETURNS:

tuple of the following form:

(periods (np.array),
 string_lengths (np.array),
 good_period_mask (boolean array))


def pwd_phasebin(phases, mags, binsize=<NUM_LIT>, minbin=<NUM_LIT:9>):

bins = np.arange(<NUM_LIT:0.0>, <NUM_LIT:1.0>, binsize)<EOL>binnedphaseinds = npdigitize(phases, bins)<EOL>binnedphases, binnedmags = [], []<EOL>for x in npunique(binnedphaseinds):<EOL><INDENT>thisbin_inds = binnedphaseinds == x<EOL>thisbin_phases = phases[thisbin_inds]<EOL>thisbin_mags = mags[thisbin_inds]<EOL>if thisbin_inds.size > minbin:<EOL><INDENT>binnedphases.append(npmedian(thisbin_phases))<EOL>binnedmags.append(npmedian(thisbin_mags))<EOL><DEDENT><DEDENT>return np.array(binnedphases), np.array(binnedmags)<EOL>

This bins the phased mag series using the given binsize.


frequency = task[<NUM_LIT:0>]<EOL>times, modmags = task[<NUM_LIT:1>], task[<NUM_LIT:2>]<EOL>fold_time = task[<NUM_LIT:3>]<EOL>j_range = range(task[<NUM_LIT:4>])<EOL>keep_threshold_1 = task[<NUM_LIT:5>]<EOL>keep_threshold_2 = task[<NUM_LIT:6>]<EOL>phasebinsize = task[<NUM_LIT:7>]<EOL>try:<EOL><INDENT>period = <NUM_LIT:1.0>/frequency<EOL>phased = phase_magseries(times,<EOL>modmags,<EOL>period,<EOL>fold_time,<EOL>wrap=False,<EOL>sort=True)<EOL>if phasebinsize is not None and phasebinsize > <NUM_LIT:0>:<EOL><INDENT>bphased = pwd_phasebin(phased['<STR_LIT>'],<EOL>phased['<STR_LIT>'],<EOL>binsize=phasebinsize)<EOL>phase_sorted = bphased[<NUM_LIT:0>]<EOL>mod_mag_sorted = bphased[<NUM_LIT:1>]<EOL>j_range = range(len(mod_mag_sorted) - <NUM_LIT:1>)<EOL><DEDENT>else:<EOL><INDENT>phase_sorted = phased['<STR_LIT>']<EOL>mod_mag_sorted = phased['<STR_LIT>']<EOL><DEDENT>rolledmags = nproll(mod_mag_sorted,<NUM_LIT:1>)<EOL>rolledphases = nproll(phase_sorted,<NUM_LIT:1>)<EOL>strings = (<EOL>(rolledmags - mod_mag_sorted)*(rolledmags - mod_mag_sorted) +<EOL>(rolledphases - phase_sorted)*(rolledphases - phase_sorted)<EOL>)<EOL>strings[<NUM_LIT:0>] = (<EOL>((mod_mag_sorted[<NUM_LIT:0>] - mod_mag_sorted[-<NUM_LIT:1>]) *<EOL>(mod_mag_sorted[<NUM_LIT:0>] - mod_mag_sorted[-<NUM_LIT:1>])) +<EOL>((phase_sorted[<NUM_LIT:0>] - phase_sorted[-<NUM_LIT:1>] + <NUM_LIT:1>) *<EOL>(phase_sorted[<NUM_LIT:0>] - phase_sorted[-<NUM_LIT:1>] + <NUM_LIT:1>))<EOL>)<EOL>strlen = npsum(npsqrt(strings))<EOL>if (keep_threshold_1 < strlen < keep_threshold_2):<EOL><INDENT>p_goodflag  = True<EOL><DEDENT>else:<EOL><INDENT>p_goodflag = False<EOL><DEDENT>return (period, strlen, p_goodflag)<EOL><DEDENT>except Exception as e:<EOL><INDENT>LOGEXCEPTION('<STR_LIT>')<EOL>return(period, npnan, False)<EOL><DEDENT>

This is the parallel worker for the function below.

task[0] = frequency for this worker
task[1] = times array
task[2] = mags array
task[3] = fold_time
task[4] = j_range
task[5] = keep_threshold_1
task[6] = keep_threshold_2
task[7] = phasebinsize

we don't need errs for the worker.


<EOL>find = npisfinite(times) & npisfinite(mags) & npisfinite(errs)<EOL>ftimes, fmags, ferrs = times[find], mags[find], errs[find]<EOL>mod_mags = (fmags - npmin(fmags))/(<NUM_LIT>*(npmax(fmags) - npmin(fmags))) - <NUM_LIT><EOL>if len(ftimes) > <NUM_LIT:9> and len(fmags) > <NUM_LIT:9> and len(ferrs) > <NUM_LIT:9>:<EOL><INDENT>median_mag = np.median(fmags)<EOL>stddev_mag = (np.median(np.abs(fmags - median_mag))) * <NUM_LIT><EOL>if sigclip:<EOL><INDENT>sigind = (np.abs(fmags - median_mag)) < (sigclip * stddev_mag)<EOL>stimes = ftimes[sigind]<EOL>smags = fmags[sigind]<EOL>serrs = ferrs[sigind]<EOL>LOGINFO('<STR_LIT>'<EOL>'<STR_LIT>' %<EOL>(sigclip, len(times), len(stimes)))<EOL><DEDENT>else:<EOL><INDENT>stimes = ftimes<EOL>smags = fmags<EOL>serrs = ferrs<EOL><DEDENT>if len(stimes) > <NUM_LIT:9> and len(smags) > <NUM_LIT:9> and len(serrs) > <NUM_LIT:9>:<EOL><INDENT>if init_p:<EOL><INDENT>endf = <NUM_LIT:1.0>/init_p<EOL><DEDENT>else:<EOL><INDENT>endf = <NUM_LIT:1.0>/<NUM_LIT:0.1><EOL><DEDENT>if end_p:<EOL><INDENT>startf = <NUM_LIT:1.0>/end_p<EOL><DEDENT>else:<EOL><INDENT>startf = <NUM_LIT:1.0>/(stimes.max() - stimes.min())<EOL><DEDENT>if not autofreq:<EOL><INDENT>frequencies = np.arange(startf, endf, stepsize)<EOL>LOGINFO(<EOL>'<STR_LIT>' %<EOL>(frequencies.size, <NUM_LIT:1.0>/endf, <NUM_LIT:1.0>/startf)<EOL>)<EOL><DEDENT>else:<EOL><INDENT>frequencies = get_frequency_grid(stimes,<EOL>minfreq=startf,<EOL>maxfreq=endf)<EOL>LOGINFO(<EOL>'<STR_LIT>'<EOL>'<STR_LIT>' %<EOL>(frequencies.size,<EOL><NUM_LIT:1.0>/frequencies.max(),<EOL><NUM_LIT:1.0>/frequencies.min())<EOL>)<EOL><DEDENT>fold_time = npmin(ftimes) <EOL>j_range = len(fmags)-<NUM_LIT:1><EOL>epsilon = <NUM_LIT> * npmean(ferrs)<EOL>delta_l = <NUM_LIT> * (epsilon - <NUM_LIT:0.5>*(epsilon**<NUM_LIT:2>)) * (len(ftimes) -<EOL>npsqrt(<NUM_LIT>/epsilon))<EOL>keep_threshold_1 = <NUM_LIT> + <NUM_LIT>*delta_l<EOL>l = <NUM_LIT>*len(ftimes)<EOL>sig_l = len(ftimes)/<NUM_LIT><EOL>keep_threshold_2 = l + <NUM_LIT>*sig_l<EOL>tasks = [(x,<EOL>ftimes,<EOL>mod_mags,<EOL>fold_time,<EOL>j_range,<EOL>keep_threshold_1,<EOL>keep_threshold_2,<EOL>phasebinsize) for x in frequencies]<EOL>if (not nworkers) or (nworkers > NCPUS):<EOL><INDENT>nworkers = NCPUS<EOL>LOGINFO('<STR_LIT>' % nworkers)<EOL><DEDENT>pool = Pool(nworkers)<EOL>strlen_results = pool.map(pdw_worker, tasks)<EOL>pool.close()<EOL>pool.join()<EOL>del pool<EOL>periods, strlens, goodflags = zip(*strlen_results)<EOL>periods, strlens, goodflags = (np.array(periods),<EOL>np.array(strlens),<EOL>np.array(goodflags))<EOL>strlensort = npargsort(strlens)<EOL>nbeststrlens = strlens[strlensort[:<NUM_LIT:5>]]<EOL>nbestperiods = periods[strlensort[:<NUM_LIT:5>]]<EOL>nbestflags = goodflags[strlensort[:<NUM_LIT:5>]]<EOL>bestperiod = nbestperiods[<NUM_LIT:0>]<EOL>beststrlen = nbeststrlens[<NUM_LIT:0>]<EOL>bestflag = nbestflags[<NUM_LIT:0>]<EOL>return {'<STR_LIT>':bestperiod,<EOL>'<STR_LIT>':beststrlen,<EOL>'<STR_LIT>':bestflag,<EOL>'<STR_LIT>':nbeststrlens,<EOL>'<STR_LIT>':nbestperiods,<EOL>'<STR_LIT>':nbestflags,<EOL>'<STR_LIT>':strlens,<EOL>'<STR_LIT>':periods,<EOL>'<STR_LIT>':goodflags}<EOL><DEDENT>else:<EOL><INDENT>LOGERROR('<STR_LIT>')<EOL>return {'<STR_LIT>':npnan,<EOL>'<STR_LIT>':npnan,<EOL>'<STR_LIT>':npnan,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None}<EOL><DEDENT><DEDENT>else:<EOL><INDENT>LOGERROR('<STR_LIT>')<EOL>return {'<STR_LIT>':npnan,<EOL>'<STR_LIT>':npnan,<EOL>'<STR_LIT>':npnan,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None}<EOL><DEDENT>

This is the parallel version of the function above.

    Uses the string length method in Dworetsky 1983 to calculate the period of a
    time-series of magnitude measurements and associated magnitude errors. This
    can optionally bin in phase to try to speed up the calculation.

    PARAMETERS:

    time: series of times at which mags were measured (usually some form of JD)
    mag: timeseries of magnitudes (np.array)
    err: associated errs per magnitude measurement (np.array)
    init_p, end_p: interval to search for periods between (both ends inclusive)
    f_step: step in frequency [days^-1] to use

    RETURNS:

    tuple of the following form:

    (periods (np.array),
     string_lengths (np.array),
     good_period_mask (boolean array))


def townsend_lombscargle_value(times, mags, omega):

cos_omegat = npcos(omega*times)<EOL>sin_omegat = npsin(omega*times)<EOL>xc = npsum(mags*cos_omegat)<EOL>xs = npsum(mags*sin_omegat)<EOL>cc = npsum(cos_omegat*cos_omegat)<EOL>ss = npsum(sin_omegat*sin_omegat)<EOL>cs = npsum(cos_omegat*sin_omegat)<EOL>tau = nparctan(<NUM_LIT:2>*cs/(cc - ss))/(<NUM_LIT:2>*omega)<EOL>ctau = npcos(omega*tau)<EOL>stau = npsin(omega*tau)<EOL>leftsumtop = (ctau*xc + stau*xs)*(ctau*xc + stau*xs)<EOL>leftsumbot = ctau*ctau*cc + <NUM_LIT>*ctau*stau*cs + stau*stau*ss<EOL>leftsum = leftsumtop/leftsumbot<EOL>rightsumtop = (ctau*xs - stau*xc)*(ctau*xs - stau*xc)<EOL>rightsumbot = ctau*ctau*ss - <NUM_LIT>*ctau*stau*cs + stau*stau*cc<EOL>rightsum = rightsumtop/rightsumbot<EOL>pval = <NUM_LIT:0.5>*(leftsum + rightsum)<EOL>return pval<EOL>

This calculates the periodogram value for each omega (= 2*pi*f). Mags must
be normalized to zero with variance scaled to unity.


try:<EOL><INDENT>return townsend_lombscargle_value(*task)<EOL><DEDENT>except Exception as e:<EOL><INDENT>return npnan<EOL><DEDENT>

This wraps the function above for use with mp.Pool.

task[0] = times
task[1] = mags
task[2] = omega


def parallel_townsend_lsp(times, mags, startp, endp,<EOL>stepsize=<NUM_LIT>,<EOL>nworkers=<NUM_LIT:4>):

<EOL>finiteind = np.isfinite(times) & np.isfinite(mags)<EOL>ftimes, fmags = times[finiteind], mags[finiteind]<EOL>nmags = (fmags - np.median(fmags))/np.std(fmags)<EOL>startf = <NUM_LIT:1.0>/endp<EOL>endf = <NUM_LIT:1.0>/startp<EOL>omegas = <NUM_LIT:2>*np.pi*np.arange(startf, endf, stepsize)<EOL>if (not nworkers) or (nworkers > NCPUS):<EOL><INDENT>nworkers = NCPUS<EOL>LOGINFO('<STR_LIT>' % nworkers)<EOL><DEDENT>pool = Pool(nworkers)<EOL>tasks = [(ftimes, nmags, x) for x in omegas]<EOL>lsp = pool.map(townsend_lombscargle_wrapper, tasks)<EOL>pool.close()<EOL>pool.join()<EOL>return np.array(omegas), np.array(lsp)<EOL>

This calculates the Lomb-Scargle periodogram for the frequencies
corresponding to the period interval (startp, endp) using a frequency step
size of stepsize cycles/day. This uses the algorithm in Townsend 2010.


def parallel_townsend_lsp_sharedarray(times, mags, startp, endp,<EOL>stepsize=<NUM_LIT>,<EOL>nworkers=<NUM_LIT:16>):

This is a version of the above which uses shared ctypes arrays for the times
and mags arrays so as not to copy them to each worker process.

TODO: we'll need to pass a single argument to the worker so make a 2D array
and wrap the worker function with partial?

FIXME: implement this later.


try:<EOL><INDENT>return lombscargle(*task)<EOL><DEDENT>except Exception as e:<EOL><INDENT>return npnan<EOL><DEDENT>

This is a worker to wrap the scipy lombscargle function.


<EOL>finiteind = np.isfinite(mags) & np.isfinite(errs)<EOL>ftimes, fmags, ferrs = times[finiteind], mags[finiteind], errs[finiteind]<EOL>if len(ftimes) > <NUM_LIT:0> and len(fmags) > <NUM_LIT:0>:<EOL><INDENT>if sigclip:<EOL><INDENT>worktimes, workmags, _ = sigclip_magseries(ftimes,<EOL>fmags,<EOL>ferrs,<EOL>sigclip=sigclip)<EOL>LOGINFO('<STR_LIT>' % len(worktimes))<EOL><DEDENT>else:<EOL><INDENT>worktimes = ftimes<EOL>workmags = fmags<EOL><DEDENT>if timebin:<EOL><INDENT>binned = time_bin_magseries(worktimes, workmags, binsize=timebin)<EOL>worktimes = binned['<STR_LIT>']<EOL>workmags = binned['<STR_LIT>']<EOL><DEDENT>normmags = (workmags - np.median(workmags))/np.std(workmags)<EOL>startf = <NUM_LIT:1.0>/endp<EOL>endf = <NUM_LIT:1.0>/startp<EOL>omegas = <NUM_LIT:2>*np.pi*np.arange(startf, endf, stepsize)<EOL>tasks = []<EOL>chunksize = int(float(len(omegas))/nworkers) + <NUM_LIT:1><EOL>tasks = [omegas[x*chunksize:x*chunksize+chunksize]<EOL>for x in range(nworkers)]<EOL>if (not nworkers) or (nworkers > NCPUS):<EOL><INDENT>nworkers = NCPUS<EOL>LOGINFO('<STR_LIT>' % nworkers)<EOL><DEDENT>pool = Pool(nworkers)<EOL>tasks = [(worktimes, normmags, x) for x in tasks]<EOL>lsp = pool.map(parallel_scipylsp_worker, tasks)<EOL>pool.close()<EOL>pool.join()<EOL>lsp = np.concatenate(lsp)<EOL>periods = <NUM_LIT>*np.pi/omegas<EOL>finitepeakind = npisfinite(lsp)<EOL>finlsp = lsp[finitepeakind]<EOL>finperiods = periods[finitepeakind]<EOL>bestperiodind = npargmax(finlsp)<EOL>sortedlspind = np.argsort(finlsp)[::-<NUM_LIT:1>]<EOL>sortedlspperiods = finperiods[sortedlspind]<EOL>sortedlspvals = finlsp[sortedlspind]<EOL>prevbestlspval = sortedlspvals[<NUM_LIT:0>]<EOL>nbestperiods, nbestlspvals, peakcount = (<EOL>[finperiods[bestperiodind]],<EOL>[finlsp[bestperiodind]],<EOL><NUM_LIT:1><EOL>)<EOL>prevperiod = sortedlspperiods[<NUM_LIT:0>]<EOL>for period, lspval in zip(sortedlspperiods, sortedlspvals):<EOL><INDENT>if peakcount == nbestpeaks:<EOL><INDENT>break<EOL><DEDENT>perioddiff = abs(period - prevperiod)<EOL>bestperiodsdiff = [abs(period - x) for x in nbestperiods]<EOL>if (perioddiff > periodepsilon and<EOL>all(x > periodepsilon for x in bestperiodsdiff)):<EOL><INDENT>nbestperiods.append(period)<EOL>nbestlspvals.append(lspval)<EOL>peakcount = peakcount + <NUM_LIT:1><EOL><DEDENT>prevperiod = period<EOL><DEDENT>return {'<STR_LIT>':finperiods[bestperiodind],<EOL>'<STR_LIT>':finlsp[bestperiodind],<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':nbestlspvals,<EOL>'<STR_LIT>':nbestperiods,<EOL>'<STR_LIT>':lsp,<EOL>'<STR_LIT>':omegas,<EOL>'<STR_LIT>':periods,<EOL>'<STR_LIT>':'<STR_LIT>'}<EOL><DEDENT>else:<EOL><INDENT>LOGERROR('<STR_LIT>')<EOL>return {'<STR_LIT>':npnan,<EOL>'<STR_LIT>':npnan,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':'<STR_LIT>'}<EOL><DEDENT>

This uses the LSP function from the scipy library, which is fast as hell. We
try to make it faster by running LSP for sections of the omegas array in
parallel.


def aovhm_theta(times, mags, errs, frequency,<EOL>nharmonics, magvariance):

period = <NUM_LIT:1.0>/frequency<EOL>ndet = times.size<EOL>two_nharmonics = nharmonics + nharmonics<EOL>phasedseries = phase_magseries_with_errs(<EOL>times, mags, errs, period, times[<NUM_LIT:0>],<EOL>sort=True, wrap=False<EOL>)<EOL>phase = phasedseries['<STR_LIT>']<EOL>pmags = phasedseries['<STR_LIT>']<EOL>perrs = phasedseries['<STR_LIT>']<EOL>pweights = <NUM_LIT:1.0>/perrs<EOL>phase = phase * <NUM_LIT> * pi_value<EOL>z = npcos(phase) + <NUM_LIT>*npsin(phase)<EOL>phase = nharmonics * phase<EOL>psi = pmags * pweights * (npcos(phase) + <NUM_LIT>*npsin(phase))<EOL>zn = <NUM_LIT:1.0> + <NUM_LIT><EOL>phi = pweights + <NUM_LIT><EOL>theta_aov = <NUM_LIT:0.0><EOL>for _ in range(two_nharmonics):<EOL><INDENT>phi_dot_phi = npsum(phi * phi.conjugate())<EOL>alpha = npsum(pweights * z * phi)<EOL>phi_dot_psi = npvdot(phi, psi)<EOL>phi_dot_phi = npmax([phi_dot_phi, <NUM_LIT>])<EOL>alpha = alpha / phi_dot_phi<EOL>theta_aov = (theta_aov +<EOL>npabs(phi_dot_psi) * npabs(phi_dot_psi) / phi_dot_phi)<EOL>phi = phi * z - alpha * zn * phi.conjugate()<EOL>zn = zn * z<EOL><DEDENT>theta_aov = ( (ndet - two_nharmonics - <NUM_LIT:1.0>) * theta_aov /<EOL>(two_nharmonics * npmax([magvariance - theta_aov,<EOL><NUM_LIT>])) )<EOL>return theta_aov<EOL>

This calculates the harmonic AoV theta statistic for a frequency.

    This is a mostly faithful translation of the inner loop in `aovper.f90`. See
    the following for details:

    - http://users.camk.edu.pl/alex/
    - Schwarzenberg-Czerny (`1996
      <http://iopscience.iop.org/article/10.1086/309985/meta>`_)

    Schwarzenberg-Czerny (1996) equation 11::

        theta_prefactor = (K - 2N - 1)/(2N)
        theta_top = sum(c_n*c_n) (from n=0 to n=2N)
        theta_bot = variance(timeseries) - sum(c_n*c_n) (from n=0 to n=2N)

        theta = theta_prefactor * (theta_top/theta_bot)

        N = number of harmonics (nharmonics)
        K = length of time series (times.size)

    Parameters
    ----------

    times,mags,errs : np.array
        The input time-series to calculate the test statistic for. These should
        all be of nans/infs and be normalized to zero.

    frequency : float
        The test frequency to calculate the statistic for.

    nharmonics : int
        The number of harmonics to calculate up to.The recommended range is 4 to
        8.

    magvariance : float
        This is the (weighted by errors) variance of the magnitude time
        series. We provide it as a pre-calculated value here so we don't have to
        re-calculate it for every worker.

    Returns
    -------

    aov_harmonic_theta : float
        THe value of the harmonic AoV theta for the specified test `frequency`.


times, mags, errs, frequency, nharmonics, magvariance = task<EOL>try:<EOL><INDENT>theta = aovhm_theta(times, mags, errs, frequency,<EOL>nharmonics, magvariance)<EOL>return theta<EOL><DEDENT>except Exception as e:<EOL><INDENT>return npnan<EOL><DEDENT>

This is a parallel worker for the function below.

Parameters
----------

tasks : tuple
    This is of the form below::

        task[0] = times
        task[1] = mags
        task[2] = errs
        task[3] = frequency
        task[4] = nharmonics
        task[5] = magvariance

Returns
-------

harmonic_aov_theta : float
    The value of the harmonic AoV statistic for the test frequency used.
    If something goes wrong with the calculation, nan is returned.


<EOL>stimes, smags, serrs = sigclip_magseries(times,<EOL>mags,<EOL>errs,<EOL>magsarefluxes=magsarefluxes,<EOL>sigclip=sigclip)<EOL>if len(stimes) > <NUM_LIT:9> and len(smags) > <NUM_LIT:9> and len(serrs) > <NUM_LIT:9>:<EOL><INDENT>if startp:<EOL><INDENT>endf = <NUM_LIT:1.0>/startp<EOL><DEDENT>else:<EOL><INDENT>endf = <NUM_LIT:1.0>/<NUM_LIT:0.1><EOL><DEDENT>if endp:<EOL><INDENT>startf = <NUM_LIT:1.0>/endp<EOL><DEDENT>else:<EOL><INDENT>startf = <NUM_LIT:1.0>/(stimes.max() - stimes.min())<EOL><DEDENT>if not autofreq:<EOL><INDENT>frequencies = nparange(startf, endf, stepsize)<EOL>if verbose:<EOL><INDENT>LOGINFO(<EOL>'<STR_LIT>' %<EOL>(frequencies.size, <NUM_LIT:1.0>/endf, <NUM_LIT:1.0>/startf)<EOL>)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>frequencies = get_frequency_grid(stimes,<EOL>minfreq=startf,<EOL>maxfreq=endf)<EOL>if verbose:<EOL><INDENT>LOGINFO(<EOL>'<STR_LIT>'<EOL>'<STR_LIT>' %<EOL>(frequencies.size,<EOL><NUM_LIT:1.0>/frequencies.max(),<EOL><NUM_LIT:1.0>/frequencies.min())<EOL>)<EOL><DEDENT><DEDENT>if (not nworkers) or (nworkers > NCPUS):<EOL><INDENT>nworkers = NCPUS<EOL>if verbose:<EOL><INDENT>LOGINFO('<STR_LIT>' % nworkers)<EOL><DEDENT><DEDENT>pool = Pool(nworkers)<EOL>if normalize:<EOL><INDENT>nmags = (smags - npmedian(smags))/npstd(smags)<EOL><DEDENT>else:<EOL><INDENT>nmags = smags<EOL><DEDENT>magvariance_top = npsum(nmags/(serrs*serrs))<EOL>magvariance_bot = (nmags.size - <NUM_LIT:1>)*npsum(<NUM_LIT:1.0>/(serrs*serrs)) / nmags.size<EOL>magvariance = magvariance_top/magvariance_bot<EOL>tasks = [(stimes, nmags, serrs, x, nharmonics, magvariance)<EOL>for x in frequencies]<EOL>lsp = pool.map(_aovhm_theta_worker, tasks)<EOL>pool.close()<EOL>pool.join()<EOL>del pool<EOL>lsp = nparray(lsp)<EOL>periods = <NUM_LIT:1.0>/frequencies<EOL>finitepeakind = npisfinite(lsp)<EOL>finlsp = lsp[finitepeakind]<EOL>finperiods = periods[finitepeakind]<EOL>try:<EOL><INDENT>bestperiodind = npargmax(finlsp)<EOL><DEDENT>except ValueError:<EOL><INDENT>LOGERROR('<STR_LIT>'<EOL>'<STR_LIT>')<EOL>return {'<STR_LIT>':npnan,<EOL>'<STR_LIT>':npnan,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':'<STR_LIT>',<EOL>'<STR_LIT>':{'<STR_LIT>':startp,<EOL>'<STR_LIT>':endp,<EOL>'<STR_LIT>':stepsize,<EOL>'<STR_LIT>':normalize,<EOL>'<STR_LIT>':nharmonics,<EOL>'<STR_LIT>':autofreq,<EOL>'<STR_LIT>':periodepsilon,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':sigclip}}<EOL><DEDENT>sortedlspind = npargsort(finlsp)[::-<NUM_LIT:1>]<EOL>sortedlspperiods = finperiods[sortedlspind]<EOL>sortedlspvals = finlsp[sortedlspind]<EOL>nbestperiods, nbestlspvals, peakcount = (<EOL>[finperiods[bestperiodind]],<EOL>[finlsp[bestperiodind]],<EOL><NUM_LIT:1><EOL>)<EOL>prevperiod = sortedlspperiods[<NUM_LIT:0>]<EOL>for period, lspval in zip(sortedlspperiods, sortedlspvals):<EOL><INDENT>if peakcount == nbestpeaks:<EOL><INDENT>break<EOL><DEDENT>perioddiff = abs(period - prevperiod)<EOL>bestperiodsdiff = [abs(period - x) for x in nbestperiods]<EOL>if (perioddiff > (periodepsilon*prevperiod) and<EOL>all(x > (periodepsilon*period) for x in bestperiodsdiff)):<EOL><INDENT>nbestperiods.append(period)<EOL>nbestlspvals.append(lspval)<EOL>peakcount = peakcount + <NUM_LIT:1><EOL><DEDENT>prevperiod = period<EOL><DEDENT>return {'<STR_LIT>':finperiods[bestperiodind],<EOL>'<STR_LIT>':finlsp[bestperiodind],<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':nbestlspvals,<EOL>'<STR_LIT>':nbestperiods,<EOL>'<STR_LIT>':lsp,<EOL>'<STR_LIT>':periods,<EOL>'<STR_LIT>':'<STR_LIT>',<EOL>'<STR_LIT>':{'<STR_LIT>':startp,<EOL>'<STR_LIT>':endp,<EOL>'<STR_LIT>':stepsize,<EOL>'<STR_LIT>':normalize,<EOL>'<STR_LIT>':nharmonics,<EOL>'<STR_LIT>':autofreq,<EOL>'<STR_LIT>':periodepsilon,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':sigclip}}<EOL><DEDENT>else:<EOL><INDENT>LOGERROR('<STR_LIT>')<EOL>return {'<STR_LIT>':npnan,<EOL>'<STR_LIT>':npnan,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':'<STR_LIT>',<EOL>'<STR_LIT>':{'<STR_LIT>':startp,<EOL>'<STR_LIT>':endp,<EOL>'<STR_LIT>':stepsize,<EOL>'<STR_LIT>':normalize,<EOL>'<STR_LIT>':nharmonics,<EOL>'<STR_LIT>':autofreq,<EOL>'<STR_LIT>':periodepsilon,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':sigclip}}<EOL><DEDENT>

This runs a parallelized harmonic Analysis-of-Variance (AoV) period
    search.

    NOTE: normalize = True here as recommended by Schwarzenberg-Czerny 1996,
    i.e. mags will be normalized to zero and rescaled so their variance = 1.0.

    Parameters
    ----------

    times,mags,errs : np.array
        The mag/flux time-series with associated measurement errors to run the
        period-finding on.

    magsarefluxes : bool
        If the input measurement values in `mags` and `errs` are in fluxes, set
        this to True.

    startp,endp : float or None
        The minimum and maximum periods to consider for the transit search.

    stepsize : float
        The step-size in frequency to use when constructing a frequency grid for
        the period search.

    autofreq : bool
        If this is True, the value of `stepsize` will be ignored and the
        :py:func:`astrobase.periodbase.get_frequency_grid` function will be used
        to generate a frequency grid based on `startp`, and `endp`. If these are
        None as well, `startp` will be set to 0.1 and `endp` will be set to
        `times.max() - times.min()`.

    normalize : bool
        This sets if the input time-series is normalized to 0.0 and rescaled
        such that its variance = 1.0. This is the recommended procedure by
        Schwarzenberg-Czerny 1996.

    nharmonics : int
        The number of harmonics to use when calculating the AoV theta value at a
        test frequency. This should be between 4 and 8 in most cases.

    nbestpeaks : int
        The number of 'best' peaks to return from the periodogram results,
        starting from the global maximum of the periodogram peak values.

    periodepsilon : float
        The fractional difference between successive values of 'best' periods
        when sorting by periodogram power to consider them as separate periods
        (as opposed to part of the same periodogram peak). This is used to avoid
        broad peaks in the periodogram and make sure the 'best' periods returned
        are all actually independent.

    sigclip : float or int or sequence of two floats/ints or None
        If a single float or int, a symmetric sigma-clip will be performed using
        the number provided as the sigma-multiplier to cut out from the input
        time-series.

        If a list of two ints/floats is provided, the function will perform an
        'asymmetric' sigma-clip. The first element in this list is the sigma
        value to use for fainter flux/mag values; the second element in this
        list is the sigma value to use for brighter flux/mag values. For
        example, `sigclip=[10., 3.]`, will sigclip out greater than 10-sigma
        dimmings and greater than 3-sigma brightenings. Here the meaning of
        "dimming" and "brightening" is set by *physics* (not the magnitude
        system), which is why the `magsarefluxes` kwarg must be correctly set.

        If `sigclip` is None, no sigma-clipping will be performed, and the
        time-series (with non-finite elems removed) will be passed through to
        the output.

    nworkers : int
        The number of parallel workers to use when calculating the periodogram.

    verbose : bool
        If this is True, will indicate progress and details about the frequency
        grid used for the period search.

    Returns
    -------

    dict
        This function returns a dict, referred to as an `lspinfo` dict in other
        astrobase functions that operate on periodogram results. This is a
        standardized format across all astrobase period-finders, and is of the
        form below::

            {'bestperiod': the best period value in the periodogram,
             'bestlspval': the periodogram peak associated with the best period,
             'nbestpeaks': the input value of nbestpeaks,
             'nbestlspvals': nbestpeaks-size list of best period peak values,
             'nbestperiods': nbestpeaks-size list of best periods,
             'lspvals': the full array of periodogram powers,
             'periods': the full array of periods considered,
             'method':'mav' -> the name of the period-finder method,
             'kwargs':{ dict of all of the input kwargs for record-keeping}}


from scipy.stats import f<EOL>frequencies = <NUM_LIT:1.0>/lspinfo['<STR_LIT>']<EOL>M = independent_freq_count(frequencies,<EOL>times,<EOL>conservative=conservative_nfreq_eff)<EOL>if peakvals is None:<EOL><INDENT>peakvals = lspinfo['<STR_LIT>']<EOL><DEDENT>nharmonics = lspinfo['<STR_LIT>']['<STR_LIT>']<EOL>ndet = times.size<EOL>false_alarm_probs = []<EOL>for peakval in peakvals:<EOL><INDENT>prob_xval = peakval<EOL>prob_exceeds_val = f.cdf(prob_xval,<EOL><NUM_LIT:2>*nharmonics,<EOL>ndet - <NUM_LIT:2>*nharmonics - <NUM_LIT:1>)<EOL>false_alarm_probs.append(<NUM_LIT:1.0> - (<NUM_LIT:1.0> - prob_exceeds_val)**M)<EOL><DEDENT>if inplace:<EOL><INDENT>lspinfo['<STR_LIT>'] = false_alarm_probs<EOL><DEDENT>return false_alarm_probs<EOL>

This returns the analytic false alarm probabilities for periodogram
    peak values.

    FIXME: this doesn't actually work. Fix later.

    The calculation follows that on page 3 of Zechmeister & Kurster (2009)::

        FAP = 1 − [1 − Prob(z > z0)]**M

    where::

        M is the number of independent frequencies
        Prob(z > z0) is the probability of peak with value > z0
        z0 is the peak value we're evaluating

    For AoV and AoV-harmonic, the Prob(z > z0) is described by the F
    distribution, according to:

    - Schwarzenberg-Czerny (1997;
      https://ui.adsabs.harvard.edu/#abs/1997ApJ...489..941S)

    - Schwarzenberg-Czerny (1996;
      http://adsabs.harvard.edu/abs/1996ApJ...460L.107S)

    This is given by::

        F( 2N, K - 2N - 1; theta_aov )

    Where::

        N = number of harmonics used for AOV_harmonic
        K = number of observations

    This translates to a scipy.stats call to the F distribution CDF::

        x = theta_aov_best
        prob_exceeds_val = scipy.stats.f.cdf(x, 2N, K - 2N - 1)

    Which we can then plug into the false alarm prob eqn above with the
    calculation of M.

    Parameters
    ----------

    lspinfo : dict
        The dict returned by the
        :py:func:`~astrobase.periodbase.spdm.aovhm_periodfind` function.

    times : np.array
        The times for which the periodogram result in ``lspinfo`` was
        calculated.

    conservative_nfreq_eff : bool
        If True, will follow the prescription given in Schwarzenberg-Czerny
        (2003):

        http://adsabs.harvard.edu/abs/2003ASPC..292..383S

        and estimate the effective number of independent frequences M_eff as::

            min(N_obs, N_freq, DELTA_f/delta_f)

    peakvals : sequence or None
        The peak values for which to evaluate the false-alarm probability. If
        None, will calculate this for each of the peak values in the
        ``nbestpeaks`` key of the ``lspinfo`` dict.

    inplace : bool
        If True, puts the results of the FAP calculation into the ``lspinfo``
        dict as a list available as ``lspinfo['falsealarmprob']``.

    Returns
    -------

    list
        The calculated false alarm probabilities for each of the peak values in
        ``peakvals``.


def bls_serial_pfind(times, mags, errs,<EOL>magsarefluxes=False,<EOL>startp=<NUM_LIT:0.1>,  <EOL>endp=<NUM_LIT>,  <EOL>stepsize=<NUM_LIT>,<EOL>mintransitduration=<NUM_LIT>,  <EOL>maxtransitduration=<NUM_LIT>,   <EOL>ndurations=<NUM_LIT:100>,<EOL>autofreq=True,  <EOL>blsobjective='<STR_LIT>',<EOL>blsmethod='<STR_LIT>',<EOL>blsoversample=<NUM_LIT:10>,<EOL>blsmintransits=<NUM_LIT:3>,<EOL>blsfreqfactor=<NUM_LIT>,<EOL>periodepsilon=<NUM_LIT:0.1>,<EOL>nbestpeaks=<NUM_LIT:5>,<EOL>sigclip=<NUM_LIT>,<EOL>verbose=True,<EOL>raiseonfail=False):

<EOL>stimes, smags, serrs = sigclip_magseries(times,<EOL>mags,<EOL>errs,<EOL>magsarefluxes=magsarefluxes,<EOL>sigclip=sigclip)<EOL>if len(stimes) > <NUM_LIT:9> and len(smags) > <NUM_LIT:9> and len(serrs) > <NUM_LIT:9>:<EOL><INDENT>if isinstance(autofreq, bool) and autofreq:<EOL><INDENT>stepsize = <NUM_LIT>*mintransitduration/(stimes.max()-stimes.min())<EOL>minfreq = <NUM_LIT:1.0>/endp<EOL>maxfreq = <NUM_LIT:1.0>/startp<EOL>nfreq = int(npceil((maxfreq - minfreq)/stepsize))<EOL>if verbose:<EOL><INDENT>LOGINFO('<STR_LIT>'<EOL>'<STR_LIT>' % (startp, endp, nfreq,<EOL>minfreq, maxfreq))<EOL>LOGINFO('<STR_LIT>'<EOL>'<STR_LIT>'<EOL>'<STR_LIT>' %<EOL>(stepsize, ndurations,<EOL>mintransitduration, maxtransitduration))<EOL><DEDENT>use_autoperiod = False<EOL><DEDENT>elif isinstance(autofreq, bool) and not autofreq:<EOL><INDENT>minfreq = <NUM_LIT:1.0>/endp<EOL>maxfreq = <NUM_LIT:1.0>/startp<EOL>nfreq = int(npceil((maxfreq - minfreq)/stepsize))<EOL>if verbose:<EOL><INDENT>LOGINFO('<STR_LIT>'<EOL>'<STR_LIT>' % (startp, endp, nfreq,<EOL>minfreq, maxfreq))<EOL>LOGINFO('<STR_LIT>'<EOL>'<STR_LIT>'<EOL>'<STR_LIT>' %<EOL>(stepsize, ndurations,<EOL>mintransitduration, maxtransitduration))<EOL><DEDENT>use_autoperiod = False<EOL><DEDENT>elif isinstance(autofreq, str) and autofreq == '<STR_LIT>':<EOL><INDENT>use_autoperiod = True<EOL>minfreq = <NUM_LIT:1.0>/endp<EOL>maxfreq = <NUM_LIT:1.0>/startp<EOL><DEDENT>else:<EOL><INDENT>LOGERROR("<STR_LIT>")<EOL>return None<EOL><DEDENT>if minfreq < (<NUM_LIT:1.0>/(stimes.max() - stimes.min())):<EOL><INDENT>if verbose:<EOL><INDENT>LOGWARNING('<STR_LIT>'<EOL>'<STR_LIT>'<EOL>'<STR_LIT>'<EOL>% (endp, stimes.max() - stimes.min()))<EOL><DEDENT>minfreq = <NUM_LIT>/(stimes.max() - stimes.min())<EOL>if verbose:<EOL><INDENT>LOGINFO('<STR_LIT>' %<EOL>(minfreq, maxfreq))<EOL><DEDENT><DEDENT>try:<EOL><INDENT>durations = nplinspace(mintransitduration*startp,<EOL>maxtransitduration*startp,<EOL>ndurations)<EOL>if magsarefluxes:<EOL><INDENT>blsmodel = BoxLeastSquares(<EOL>stimes*u.day,<EOL>smags*u.dimensionless_unscaled,<EOL>dy=serrs*u.dimensionless_unscaled<EOL>)<EOL><DEDENT>else:<EOL><INDENT>blsmodel = BoxLeastSquares(<EOL>stimes*u.day,<EOL>smags*u.mag,<EOL>dy=serrs*u.mag<EOL>)<EOL><DEDENT>if use_autoperiod:<EOL><INDENT>periods = nparray(<EOL>blsmodel.autoperiod(<EOL>durations,<EOL>minimum_period=startp,<EOL>maximum_period=endp,<EOL>minimum_n_transit=blsmintransits,<EOL>frequency_factor=blsfreqfactor<EOL>)<EOL>)<EOL>nfreq = periods.size<EOL>if verbose:<EOL><INDENT>LOGINFO(<EOL>"<STR_LIT>"<EOL>"<STR_LIT>"<EOL>"<STR_LIT>" %<EOL>(blsmintransits, blsfreqfactor)<EOL>)<EOL>LOGINFO('<STR_LIT>'<EOL>'<STR_LIT>' %<EOL>(abs(<NUM_LIT:1.0>/periods[<NUM_LIT:1>] - <NUM_LIT:1.0>/periods[<NUM_LIT:0>]),<EOL>nfreq,<EOL><NUM_LIT:1.0>/periods.max(),<EOL><NUM_LIT:1.0>/periods.min(),<EOL>durations.size))<EOL><DEDENT><DEDENT>else:<EOL><INDENT>frequencies = minfreq + nparange(nfreq)*stepsize<EOL>periods = <NUM_LIT:1.0>/frequencies<EOL><DEDENT>if nfreq > <NUM_LIT>:<EOL><INDENT>if verbose:<EOL><INDENT>LOGWARNING('<STR_LIT>'<EOL>'<STR_LIT>'<EOL>'<STR_LIT>'<EOL>'<STR_LIT>')<EOL><DEDENT><DEDENT>blsresult = blsmodel.power(<EOL>periods*u.day,<EOL>durations*u.day,<EOL>objective=blsobjective,<EOL>method=blsmethod,<EOL>oversample=blsoversample<EOL>)<EOL>lsp = nparray(blsresult.power)<EOL>finitepeakind = npisfinite(lsp)<EOL>finlsp = lsp[finitepeakind]<EOL>finperiods = periods[finitepeakind]<EOL>try:<EOL><INDENT>bestperiodind = npargmax(finlsp)<EOL><DEDENT>except ValueError:<EOL><INDENT>LOGERROR('<STR_LIT>'<EOL>'<STR_LIT>')<EOL>return {'<STR_LIT>':npnan,<EOL>'<STR_LIT>':npnan,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':'<STR_LIT>',<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':{'<STR_LIT>':startp,<EOL>'<STR_LIT>':endp,<EOL>'<STR_LIT>':stepsize,<EOL>'<STR_LIT>':mintransitduration,<EOL>'<STR_LIT>':maxtransitduration,<EOL>'<STR_LIT>':ndurations,<EOL>'<STR_LIT>':blsobjective,<EOL>'<STR_LIT>':blsmethod,<EOL>'<STR_LIT>':blsoversample,<EOL>'<STR_LIT>':blsmintransits,<EOL>'<STR_LIT>':blsfreqfactor,<EOL>'<STR_LIT>':autofreq,<EOL>'<STR_LIT>':periodepsilon,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':sigclip,<EOL>'<STR_LIT>':magsarefluxes}}<EOL><DEDENT>sortedlspind = npargsort(finlsp)[::-<NUM_LIT:1>]<EOL>sortedlspperiods = finperiods[sortedlspind]<EOL>sortedlspvals = finlsp[sortedlspind]<EOL>nbestperiods, nbestlspvals, nbestinds, peakcount = (<EOL>[finperiods[bestperiodind]],<EOL>[finlsp[bestperiodind]],<EOL>[bestperiodind],<EOL><NUM_LIT:1><EOL>)<EOL>prevperiod = sortedlspperiods[<NUM_LIT:0>]<EOL>for period, lspval, ind in zip(sortedlspperiods,<EOL>sortedlspvals,<EOL>sortedlspind):<EOL><INDENT>if peakcount == nbestpeaks:<EOL><INDENT>break<EOL><DEDENT>perioddiff = abs(period - prevperiod)<EOL>bestperiodsdiff = [abs(period - x) for x in nbestperiods]<EOL>if (perioddiff > (periodepsilon*prevperiod) and<EOL>all(x > (periodepsilon*period)<EOL>for x in bestperiodsdiff)):<EOL><INDENT>nbestperiods.append(period)<EOL>nbestlspvals.append(lspval)<EOL>nbestinds.append(ind)<EOL>peakcount = peakcount + <NUM_LIT:1><EOL><DEDENT>prevperiod = period<EOL><DEDENT>resultdict = {<EOL>'<STR_LIT>':finperiods[bestperiodind],<EOL>'<STR_LIT>':finlsp[bestperiodind],<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':nbestinds,<EOL>'<STR_LIT>':nbestlspvals,<EOL>'<STR_LIT>':nbestperiods,<EOL>'<STR_LIT>':lsp,<EOL>'<STR_LIT>':frequencies,<EOL>'<STR_LIT>':periods,<EOL>'<STR_LIT>':durations,<EOL>'<STR_LIT>':blsresult,<EOL>'<STR_LIT>':blsmodel,<EOL>'<STR_LIT>':stepsize,<EOL>'<STR_LIT>':nfreq,<EOL>'<STR_LIT>':mintransitduration,<EOL>'<STR_LIT>':maxtransitduration,<EOL>'<STR_LIT>':'<STR_LIT>',<EOL>'<STR_LIT>':{'<STR_LIT>':startp,<EOL>'<STR_LIT>':endp,<EOL>'<STR_LIT>':stepsize,<EOL>'<STR_LIT>':mintransitduration,<EOL>'<STR_LIT>':maxtransitduration,<EOL>'<STR_LIT>':ndurations,<EOL>'<STR_LIT>':blsobjective,<EOL>'<STR_LIT>':blsmethod,<EOL>'<STR_LIT>':blsoversample,<EOL>'<STR_LIT>':blsmintransits,<EOL>'<STR_LIT>':blsfreqfactor,<EOL>'<STR_LIT>':autofreq,<EOL>'<STR_LIT>':periodepsilon,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':sigclip,<EOL>'<STR_LIT>':magsarefluxes}<EOL>}<EOL>return resultdict<EOL><DEDENT>except Exception as e:<EOL><INDENT>LOGEXCEPTION('<STR_LIT>')<EOL>if raiseonfail:<EOL><INDENT>raise<EOL><DEDENT>return {'<STR_LIT>':npnan,<EOL>'<STR_LIT>':npnan,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':stepsize,<EOL>'<STR_LIT>':nfreq,<EOL>'<STR_LIT>':mintransitduration,<EOL>'<STR_LIT>':maxtransitduration,<EOL>'<STR_LIT>':'<STR_LIT>',<EOL>'<STR_LIT>':{'<STR_LIT>':startp,<EOL>'<STR_LIT>':endp,<EOL>'<STR_LIT>':stepsize,<EOL>'<STR_LIT>':mintransitduration,<EOL>'<STR_LIT>':maxtransitduration,<EOL>'<STR_LIT>':ndurations,<EOL>'<STR_LIT>':blsobjective,<EOL>'<STR_LIT>':blsmethod,<EOL>'<STR_LIT>':blsoversample,<EOL>'<STR_LIT>':blsmintransits,<EOL>'<STR_LIT>':blsfreqfactor,<EOL>'<STR_LIT>':autofreq,<EOL>'<STR_LIT>':periodepsilon,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':sigclip,<EOL>'<STR_LIT>':magsarefluxes}}<EOL><DEDENT><DEDENT>else:<EOL><INDENT>LOGERROR('<STR_LIT>')<EOL>return {'<STR_LIT>':npnan,<EOL>'<STR_LIT>':npnan,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':stepsize,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':mintransitduration,<EOL>'<STR_LIT>':maxtransitduration,<EOL>'<STR_LIT>':'<STR_LIT>',<EOL>'<STR_LIT>':{'<STR_LIT>':startp,<EOL>'<STR_LIT>':endp,<EOL>'<STR_LIT>':stepsize,<EOL>'<STR_LIT>':mintransitduration,<EOL>'<STR_LIT>':maxtransitduration,<EOL>'<STR_LIT>':ndurations,<EOL>'<STR_LIT>':blsobjective,<EOL>'<STR_LIT>':blsmethod,<EOL>'<STR_LIT>':blsoversample,<EOL>'<STR_LIT>':blsmintransits,<EOL>'<STR_LIT>':blsfreqfactor,<EOL>'<STR_LIT>':autofreq,<EOL>'<STR_LIT>':periodepsilon,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':sigclip,<EOL>'<STR_LIT>':magsarefluxes}}<EOL><DEDENT>

Runs the Box Least Squares Fitting Search for transit-shaped signals.

    Based on the version of BLS in Astropy 3.1:
    `astropy.stats.BoxLeastSquares`. If you don't have Astropy 3.1, this module
    will fail to import. Note that by default, this implementation of
    `bls_serial_pfind` doesn't use the `.autoperiod()` function from
    `BoxLeastSquares` but uses the same auto frequency-grid generation as the
    functions in `periodbase.kbls`. If you want to use Astropy's implementation,
    set the value of `autofreq` kwarg to 'astropy'.

    The dict returned from this function contains a `blsmodel` key, which is the
    generated model from Astropy's BLS. Use the `.compute_stats()` method to
    calculate the required stats like SNR, depth, duration, etc.

    Parameters
    ----------

    times,mags,errs : np.array
        The magnitude/flux time-series to search for transits.

    magsarefluxes : bool
        If the input measurement values in `mags` and `errs` are in fluxes, set
        this to True.

    startp,endp : float
        The minimum and maximum periods to consider for the transit search.

    stepsize : float
        The step-size in frequency to use when constructing a frequency grid for
        the period search.

    mintransitduration,maxtransitduration : float
        The minimum and maximum transitdurations (in units of phase) to consider
        for the transit search.

    ndurations : int
        The number of transit durations to use in the period-search.

    autofreq : bool or str
        If this is True, the values of `stepsize` and `nphasebins` will be
        ignored, and these, along with a frequency-grid, will be determined
        based on the following relations::

            nphasebins = int(ceil(2.0/mintransitduration))
            if nphasebins > 3000:
                nphasebins = 3000

            stepsize = 0.25*mintransitduration/(times.max()-times.min())

            minfreq = 1.0/endp
            maxfreq = 1.0/startp
            nfreq = int(ceil((maxfreq - minfreq)/stepsize))

        If this is False, you must set `startp`, `endp`, and `stepsize` as
        appropriate.

        If this is str == 'astropy', will use the
        `astropy.stats.BoxLeastSquares.autoperiod()` function to calculate the
        frequency grid instead of the kbls method.

    blsobjective : {'likelihood','snr'}
        Sets the type of objective to optimize in the `BoxLeastSquares.power()`
        function.

    blsmethod : {'fast','slow'}
        Sets the type of method to use in the `BoxLeastSquares.power()`
        function.

    blsoversample : {'likelihood','snr'}
        Sets the `oversample` kwarg for the `BoxLeastSquares.power()` function.

    blsmintransits : int
        Sets the `min_n_transits` kwarg for the `BoxLeastSquares.autoperiod()`
        function.

    blsfreqfactor : float
        Sets the `frequency_factor` kwarg for the `BoxLeastSquares.autperiod()`
        function.

    periodepsilon : float
        The fractional difference between successive values of 'best' periods
        when sorting by periodogram power to consider them as separate periods
        (as opposed to part of the same periodogram peak). This is used to avoid
        broad peaks in the periodogram and make sure the 'best' periods returned
        are all actually independent.

    nbestpeaks : int
        The number of 'best' peaks to return from the periodogram results,
        starting from the global maximum of the periodogram peak values.

    sigclip : float or int or sequence of two floats/ints or None
        If a single float or int, a symmetric sigma-clip will be performed using
        the number provided as the sigma-multiplier to cut out from the input
        time-series.

        If a list of two ints/floats is provided, the function will perform an
        'asymmetric' sigma-clip. The first element in this list is the sigma
        value to use for fainter flux/mag values; the second element in this
        list is the sigma value to use for brighter flux/mag values. For
        example, `sigclip=[10., 3.]`, will sigclip out greater than 10-sigma
        dimmings and greater than 3-sigma brightenings. Here the meaning of
        "dimming" and "brightening" is set by *physics* (not the magnitude
        system), which is why the `magsarefluxes` kwarg must be correctly set.

        If `sigclip` is None, no sigma-clipping will be performed, and the
        time-series (with non-finite elems removed) will be passed through to
        the output.

    verbose : bool
        If this is True, will indicate progress and details about the frequency
        grid used for the period search.

    raiseonfail : bool
        If True, raises an exception if something goes wrong. Otherwise, returns
        None.

    Returns
    -------

    dict
        This function returns a dict, referred to as an `lspinfo` dict in other
        astrobase functions that operate on periodogram results. This is a
        standardized format across all astrobase period-finders, and is of the
        form below::

            {'bestperiod': the best period value in the periodogram,
             'bestlspval': the periodogram peak associated with the best period,
             'nbestpeaks': the input value of nbestpeaks,
             'nbestlspvals': nbestpeaks-size list of best period peak values,
             'nbestperiods': nbestpeaks-size list of best periods,
             'lspvals': the full array of periodogram powers,
             'frequencies': the full array of frequencies considered,
             'periods': the full array of periods considered,
             'durations': the array of durations used to run BLS,
             'blsresult': Astropy BLS result object (BoxLeastSquaresResult),
             'blsmodel': Astropy BLS BoxLeastSquares object used for work,
             'stepsize': the actual stepsize used,
             'nfreq': the actual nfreq used,
             'durations': the durations array used,
             'mintransitduration': the input mintransitduration,
             'maxtransitduration': the input maxtransitdurations,
             'method':'bls' -> the name of the period-finder method,
             'kwargs':{ dict of all of the input kwargs for record-keeping}}


try:<EOL><INDENT>times, mags, errs = task[:<NUM_LIT:3>]<EOL>magsarefluxes = task[<NUM_LIT:3>]<EOL>minfreq, nfreq, stepsize = task[<NUM_LIT:4>:<NUM_LIT:7>]<EOL>ndurations, mintransitduration, maxtransitduration = task[<NUM_LIT:7>:<NUM_LIT:10>]<EOL>blsobjective, blsmethod, blsoversample = task[<NUM_LIT:10>:]<EOL>frequencies = minfreq + nparange(nfreq)*stepsize<EOL>periods = <NUM_LIT:1.0>/frequencies<EOL>durations = nplinspace(mintransitduration*periods.min(),<EOL>maxtransitduration*periods.min(),<EOL>ndurations)<EOL>if magsarefluxes:<EOL><INDENT>blsmodel = BoxLeastSquares(<EOL>times*u.day,<EOL>mags*u.dimensionless_unscaled,<EOL>dy=errs*u.dimensionless_unscaled<EOL>)<EOL><DEDENT>else:<EOL><INDENT>blsmodel = BoxLeastSquares(<EOL>times*u.day,<EOL>mags*u.mag,<EOL>dy=errs*u.mag<EOL>)<EOL><DEDENT>blsresult = blsmodel.power(<EOL>periods*u.day,<EOL>durations*u.day,<EOL>objective=blsobjective,<EOL>method=blsmethod,<EOL>oversample=blsoversample<EOL>)<EOL>return {<EOL>'<STR_LIT>': blsresult,<EOL>'<STR_LIT>': blsmodel,<EOL>'<STR_LIT>': durations,<EOL>'<STR_LIT>': nparray(blsresult.power)<EOL>}<EOL><DEDENT>except Exception as e:<EOL><INDENT>LOGEXCEPTION('<STR_LIT>' %<EOL>(frequencies[<NUM_LIT:0>], frequencies[-<NUM_LIT:1>]))<EOL>return {<EOL>'<STR_LIT>': None,<EOL>'<STR_LIT>': None,<EOL>'<STR_LIT>': durations,<EOL>'<STR_LIT>': nparray([npnan for x in range(nfreq)]),<EOL>}<EOL><DEDENT>

This wraps Astropy's BoxLeastSquares for use with bls_parallel_pfind below.

`task` is a tuple::

    task[0] = times
    task[1] = mags
    task[2] = errs
    task[3] = magsarefluxes

    task[4] = minfreq
    task[5] = nfreq
    task[6] = stepsize

    task[7] = ndurations
    task[8] = mintransitduration
    task[9] = maxtransitduration

    task[10] = blsobjective
    task[11] = blsmethod
    task[12] = blsoversample


def bls_parallel_pfind(<EOL>times, mags, errs,<EOL>magsarefluxes=False,<EOL>startp=<NUM_LIT:0.1>,  <EOL>endp=<NUM_LIT>,  <EOL>stepsize=<NUM_LIT>,<EOL>mintransitduration=<NUM_LIT>,  <EOL>maxtransitduration=<NUM_LIT>,   <EOL>ndurations=<NUM_LIT:100>,<EOL>autofreq=True,  <EOL>blsobjective='<STR_LIT>',<EOL>blsmethod='<STR_LIT>',<EOL>blsoversample=<NUM_LIT:5>,<EOL>blsmintransits=<NUM_LIT:3>,<EOL>blsfreqfactor=<NUM_LIT>,<EOL>nbestpeaks=<NUM_LIT:5>,<EOL>periodepsilon=<NUM_LIT:0.1>,  <EOL>sigclip=<NUM_LIT>,<EOL>verbose=True,<EOL>nworkers=None,<EOL>):

<EOL>stimes, smags, serrs = sigclip_magseries(times,<EOL>mags,<EOL>errs,<EOL>magsarefluxes=magsarefluxes,<EOL>sigclip=sigclip)<EOL>if len(stimes) > <NUM_LIT:9> and len(smags) > <NUM_LIT:9> and len(serrs) > <NUM_LIT:9>:<EOL><INDENT>if isinstance(autofreq, bool) and autofreq:<EOL><INDENT>stepsize = <NUM_LIT>*mintransitduration/(stimes.max()-stimes.min())<EOL>minfreq = <NUM_LIT:1.0>/endp<EOL>maxfreq = <NUM_LIT:1.0>/startp<EOL>nfreq = int(npceil((maxfreq - minfreq)/stepsize))<EOL>if verbose:<EOL><INDENT>LOGINFO('<STR_LIT>'<EOL>'<STR_LIT>' % (startp, endp, nfreq,<EOL>minfreq, maxfreq))<EOL>LOGINFO('<STR_LIT>'<EOL>'<STR_LIT>'<EOL>'<STR_LIT>' %<EOL>(stepsize, ndurations,<EOL>mintransitduration, maxtransitduration))<EOL><DEDENT>use_autoperiod = False<EOL><DEDENT>elif isinstance(autofreq, bool) and not autofreq:<EOL><INDENT>minfreq = <NUM_LIT:1.0>/endp<EOL>maxfreq = <NUM_LIT:1.0>/startp<EOL>nfreq = int(npceil((maxfreq - minfreq)/stepsize))<EOL>if verbose:<EOL><INDENT>LOGINFO('<STR_LIT>'<EOL>'<STR_LIT>' % (startp, endp, nfreq,<EOL>minfreq, maxfreq))<EOL>LOGINFO('<STR_LIT>'<EOL>'<STR_LIT>'<EOL>'<STR_LIT>' %<EOL>(stepsize, ndurations,<EOL>mintransitduration, maxtransitduration))<EOL><DEDENT>use_autoperiod = False<EOL><DEDENT>elif isinstance(autofreq, str) and autofreq == '<STR_LIT>':<EOL><INDENT>use_autoperiod = True<EOL>minfreq = <NUM_LIT:1.0>/endp<EOL>maxfreq = <NUM_LIT:1.0>/startp<EOL><DEDENT>else:<EOL><INDENT>LOGERROR("<STR_LIT>")<EOL>return None<EOL><DEDENT>if minfreq < (<NUM_LIT:1.0>/(stimes.max() - stimes.min())):<EOL><INDENT>minfreq = <NUM_LIT>/(stimes.max() - stimes.min())<EOL>if verbose:<EOL><INDENT>LOGWARNING('<STR_LIT>'<EOL>'<STR_LIT>'<EOL>'<STR_LIT>'<EOL>% (endp, stimes.max() - stimes.min()))<EOL>LOGINFO('<STR_LIT>' %<EOL>(minfreq, maxfreq))<EOL><DEDENT><DEDENT>if not nworkers or nworkers > NCPUS:<EOL><INDENT>nworkers = NCPUS<EOL>if verbose:<EOL><INDENT>LOGINFO('<STR_LIT>' % nworkers)<EOL><DEDENT><DEDENT>if use_autoperiod:<EOL><INDENT>durations = nplinspace(mintransitduration*startp,<EOL>maxtransitduration*startp,<EOL>ndurations)<EOL>if magsarefluxes:<EOL><INDENT>blsmodel = BoxLeastSquares(<EOL>stimes*u.day,<EOL>smags*u.dimensionless_unscaled,<EOL>dy=serrs*u.dimensionless_unscaled<EOL>)<EOL><DEDENT>else:<EOL><INDENT>blsmodel = BoxLeastSquares(<EOL>stimes*u.day,<EOL>smags*u.mag,<EOL>dy=serrs*u.mag<EOL>)<EOL><DEDENT>periods = nparray(<EOL>blsmodel.autoperiod(<EOL>durations*u.day,<EOL>minimum_period=startp,<EOL>maximum_period=endp,<EOL>minimum_n_transit=blsmintransits,<EOL>frequency_factor=blsfreqfactor<EOL>)<EOL>)<EOL>frequencies = <NUM_LIT:1.0>/periods<EOL>nfreq = frequencies.size<EOL>if verbose:<EOL><INDENT>LOGINFO(<EOL>"<STR_LIT>"<EOL>"<STR_LIT>"<EOL>"<STR_LIT>" %<EOL>(blsmintransits, blsfreqfactor)<EOL>)<EOL>LOGINFO('<STR_LIT>'<EOL>'<STR_LIT>' %<EOL>(abs(frequencies[<NUM_LIT:1>] - frequencies[<NUM_LIT:0>]),<EOL>nfreq,<EOL><NUM_LIT:1.0>/periods.max(),<EOL><NUM_LIT:1.0>/periods.min(),<EOL>durations.size))<EOL><DEDENT>del blsmodel<EOL>del durations<EOL><DEDENT>else:<EOL><INDENT>frequencies = minfreq + nparange(nfreq)*stepsize<EOL><DEDENT>csrem = int(fmod(nfreq, nworkers))<EOL>csint = int(float(nfreq/nworkers))<EOL>chunk_minfreqs, chunk_nfreqs = [], []<EOL>for x in range(nworkers):<EOL><INDENT>this_minfreqs = frequencies[x*csint]<EOL>if x < (nworkers - <NUM_LIT:1>):<EOL><INDENT>this_nfreqs = frequencies[x*csint:x*csint+csint].size<EOL><DEDENT>else:<EOL><INDENT>this_nfreqs = frequencies[x*csint:x*csint+csint+csrem].size<EOL><DEDENT>chunk_minfreqs.append(this_minfreqs)<EOL>chunk_nfreqs.append(this_nfreqs)<EOL><DEDENT>tasks = [(stimes, smags, serrs, magsarefluxes,<EOL>chunk_minf, chunk_nf, stepsize,<EOL>ndurations, mintransitduration, maxtransitduration,<EOL>blsobjective, blsmethod, blsoversample)<EOL>for (chunk_minf, chunk_nf)<EOL>in zip(chunk_minfreqs, chunk_nfreqs)]<EOL>if verbose:<EOL><INDENT>for ind, task in enumerate(tasks):<EOL><INDENT>LOGINFO('<STR_LIT>' %<EOL>(ind+<NUM_LIT:1>, task[<NUM_LIT:4>], task[<NUM_LIT:5>]))<EOL><DEDENT>LOGINFO('<STR_LIT>')<EOL><DEDENT>pool = Pool(nworkers)<EOL>results = pool.map(_parallel_bls_worker, tasks)<EOL>pool.close()<EOL>pool.join()<EOL>del pool<EOL>lsp = npconcatenate([x['<STR_LIT>'] for x in results])<EOL>periods = <NUM_LIT:1.0>/frequencies<EOL>finitepeakind = npisfinite(lsp)<EOL>finlsp = lsp[finitepeakind]<EOL>finperiods = periods[finitepeakind]<EOL>try:<EOL><INDENT>bestperiodind = npargmax(finlsp)<EOL><DEDENT>except ValueError:<EOL><INDENT>LOGERROR('<STR_LIT>'<EOL>'<STR_LIT>')<EOL>return {'<STR_LIT>':npnan,<EOL>'<STR_LIT>':npnan,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':'<STR_LIT>',<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':{'<STR_LIT>':startp,<EOL>'<STR_LIT>':endp,<EOL>'<STR_LIT>':stepsize,<EOL>'<STR_LIT>':mintransitduration,<EOL>'<STR_LIT>':maxtransitduration,<EOL>'<STR_LIT>':ndurations,<EOL>'<STR_LIT>':blsobjective,<EOL>'<STR_LIT>':blsmethod,<EOL>'<STR_LIT>':blsoversample,<EOL>'<STR_LIT>':autofreq,<EOL>'<STR_LIT>':periodepsilon,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':sigclip,<EOL>'<STR_LIT>':magsarefluxes}}<EOL><DEDENT>sortedlspind = npargsort(finlsp)[::-<NUM_LIT:1>]<EOL>sortedlspperiods = finperiods[sortedlspind]<EOL>sortedlspvals = finlsp[sortedlspind]<EOL>nbestperiods, nbestlspvals, nbestinds, peakcount = (<EOL>[finperiods[bestperiodind]],<EOL>[finlsp[bestperiodind]],<EOL>[bestperiodind],<EOL><NUM_LIT:1><EOL>)<EOL>prevperiod = sortedlspperiods[<NUM_LIT:0>]<EOL>for period, lspval, ind in zip(sortedlspperiods,<EOL>sortedlspvals,<EOL>sortedlspind):<EOL><INDENT>if peakcount == nbestpeaks:<EOL><INDENT>break<EOL><DEDENT>perioddiff = abs(period - prevperiod)<EOL>bestperiodsdiff = [abs(period - x) for x in nbestperiods]<EOL>if (perioddiff > (periodepsilon*prevperiod) and<EOL>all(x > (periodepsilon*period)<EOL>for x in bestperiodsdiff)):<EOL><INDENT>nbestperiods.append(period)<EOL>nbestlspvals.append(lspval)<EOL>nbestinds.append(ind)<EOL>peakcount = peakcount + <NUM_LIT:1><EOL><DEDENT>prevperiod = period<EOL><DEDENT>resultdict = {<EOL>'<STR_LIT>':finperiods[bestperiodind],<EOL>'<STR_LIT>':finlsp[bestperiodind],<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':nbestinds,<EOL>'<STR_LIT>':nbestlspvals,<EOL>'<STR_LIT>':nbestperiods,<EOL>'<STR_LIT>':lsp,<EOL>'<STR_LIT>':frequencies,<EOL>'<STR_LIT>':periods,<EOL>'<STR_LIT>':[x['<STR_LIT>'] for x in results],<EOL>'<STR_LIT>':[x['<STR_LIT>'] for x in results],<EOL>'<STR_LIT>':[x['<STR_LIT>'] for x in results],<EOL>'<STR_LIT>':stepsize,<EOL>'<STR_LIT>':nfreq,<EOL>'<STR_LIT>':mintransitduration,<EOL>'<STR_LIT>':maxtransitduration,<EOL>'<STR_LIT>':'<STR_LIT>',<EOL>'<STR_LIT>':{'<STR_LIT>':startp,<EOL>'<STR_LIT>':endp,<EOL>'<STR_LIT>':stepsize,<EOL>'<STR_LIT>':mintransitduration,<EOL>'<STR_LIT>':maxtransitduration,<EOL>'<STR_LIT>':ndurations,<EOL>'<STR_LIT>':blsobjective,<EOL>'<STR_LIT>':blsmethod,<EOL>'<STR_LIT>':blsoversample,<EOL>'<STR_LIT>':autofreq,<EOL>'<STR_LIT>':periodepsilon,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':sigclip,<EOL>'<STR_LIT>':magsarefluxes}<EOL>}<EOL>return resultdict<EOL><DEDENT>else:<EOL><INDENT>LOGERROR('<STR_LIT>')<EOL>return {'<STR_LIT>':npnan,<EOL>'<STR_LIT>':npnan,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':stepsize,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':None,<EOL>'<STR_LIT>':mintransitduration,<EOL>'<STR_LIT>':maxtransitduration,<EOL>'<STR_LIT>':'<STR_LIT>',<EOL>'<STR_LIT>':{'<STR_LIT>':startp,<EOL>'<STR_LIT>':endp,<EOL>'<STR_LIT>':stepsize,<EOL>'<STR_LIT>':mintransitduration,<EOL>'<STR_LIT>':maxtransitduration,<EOL>'<STR_LIT>':ndurations,<EOL>'<STR_LIT>':blsobjective,<EOL>'<STR_LIT>':blsmethod,<EOL>'<STR_LIT>':blsoversample,<EOL>'<STR_LIT>':autofreq,<EOL>'<STR_LIT>':periodepsilon,<EOL>'<STR_LIT>':nbestpeaks,<EOL>'<STR_LIT>':sigclip,<EOL>'<STR_LIT>':magsarefluxes}}<EOL><DEDENT>

Runs the Box Least Squares Fitting Search for transit-shaped signals.

    Breaks up the full frequency space into chunks and passes them to parallel
    BLS workers.

    Based on the version of BLS in Astropy 3.1:
    `astropy.stats.BoxLeastSquares`. If you don't have Astropy 3.1, this module
    will fail to import. Note that by default, this implementation of
    `bls_parallel_pfind` doesn't use the `.autoperiod()` function from
    `BoxLeastSquares` but uses the same auto frequency-grid generation as the
    functions in `periodbase.kbls`. If you want to use Astropy's implementation,
    set the value of `autofreq` kwarg to 'astropy'. The generated period array
    will then be broken up into chunks and sent to the individual workers.

    NOTE: the combined BLS spectrum produced by this function is not identical
    to that produced by running BLS in one shot for the entire frequency
    space. There are differences on the order of 1.0e-3 or so in the respective
    peak values, but peaks appear at the same frequencies for both methods. This
    is likely due to different aliasing caused by smaller chunks of the
    frequency space used by the parallel workers in this function. When in
    doubt, confirm results for this parallel implementation by comparing to
    those from the serial implementation above.

    In particular, when you want to get reliable estimates of the SNR, transit
    depth, duration, etc. that Astropy's BLS gives you, rerun `bls_serial_pfind`
    with `startp`, and `endp` close to the best period you want to characterize
    the transit at. The dict returned from that function contains a `blsmodel`
    key, which is the generated model from Astropy's BLS. Use the
    `.compute_stats()` method to calculate the required stats.

    Parameters
    ----------

    times,mags,errs : np.array
        The magnitude/flux time-series to search for transits.

    magsarefluxes : bool
        If the input measurement values in `mags` and `errs` are in fluxes, set
        this to True.

    startp,endp : float
        The minimum and maximum periods to consider for the transit search.

    stepsize : float
        The step-size in frequency to use when constructing a frequency grid for
        the period search.

    mintransitduration,maxtransitduration : float
        The minimum and maximum transitdurations (in units of phase) to consider
        for the transit search.

    ndurations : int
        The number of transit durations to use in the period-search.

    autofreq : bool or str
        If this is True, the values of `stepsize` and `nphasebins` will be
        ignored, and these, along with a frequency-grid, will be determined
        based on the following relations::

            nphasebins = int(ceil(2.0/mintransitduration))
            if nphasebins > 3000:
                nphasebins = 3000

            stepsize = 0.25*mintransitduration/(times.max()-times.min())

            minfreq = 1.0/endp
            maxfreq = 1.0/startp
            nfreq = int(ceil((maxfreq - minfreq)/stepsize))

        If this is False, you must set `startp`, `endp`, and `stepsize` as
        appropriate.

        If this is str == 'astropy', will use the
        `astropy.stats.BoxLeastSquares.autoperiod()` function to calculate the
        frequency grid instead of the kbls method.

    blsobjective : {'likelihood','snr'}
        Sets the type of objective to optimize in the `BoxLeastSquares.power()`
        function.

    blsmethod : {'fast','slow'}
        Sets the type of method to use in the `BoxLeastSquares.power()`
        function.

    blsoversample : {'likelihood','snr'}
        Sets the `oversample` kwarg for the `BoxLeastSquares.power()` function.

    blsmintransits : int
        Sets the `min_n_transits` kwarg for the `BoxLeastSquares.autoperiod()`
        function.

    blsfreqfactor : float
        Sets the `frequency_factor` kwarg for the `BoxLeastSquares.autoperiod()`
        function.

    periodepsilon : float
        The fractional difference between successive values of 'best' periods
        when sorting by periodogram power to consider them as separate periods
        (as opposed to part of the same periodogram peak). This is used to avoid
        broad peaks in the periodogram and make sure the 'best' periods returned
        are all actually independent.

    nbestpeaks : int
        The number of 'best' peaks to return from the periodogram results,
        starting from the global maximum of the periodogram peak values.

    sigclip : float or int or sequence of two floats/ints or None
        If a single float or int, a symmetric sigma-clip will be performed using
        the number provided as the sigma-multiplier to cut out from the input
        time-series.

        If a list of two ints/floats is provided, the function will perform an
        'asymmetric' sigma-clip. The first element in this list is the sigma
        value to use for fainter flux/mag values; the second element in this
        list is the sigma value to use for brighter flux/mag values. For
        example, `sigclip=[10., 3.]`, will sigclip out greater than 10-sigma
        dimmings and greater than 3-sigma brightenings. Here the meaning of
        "dimming" and "brightening" is set by *physics* (not the magnitude
        system), which is why the `magsarefluxes` kwarg must be correctly set.

        If `sigclip` is None, no sigma-clipping will be performed, and the
        time-series (with non-finite elems removed) will be passed through to
        the output.

    verbose : bool
        If this is True, will indicate progress and details about the frequency
        grid used for the period search.

    nworkers : int or None
        The number of parallel workers to launch for period-search. If None,
        nworkers = NCPUS.

    Returns
    -------

    dict
        This function returns a dict, referred to as an `lspinfo` dict in other
        astrobase functions that operate on periodogram results. This is a
        standardized format across all astrobase period-finders, and is of the
        form below::

            {'bestperiod': the best period value in the periodogram,
             'bestlspval': the periodogram peak associated with the best period,
             'nbestpeaks': the input value of nbestpeaks,
             'nbestlspvals': nbestpeaks-size list of best period peak values,
             'nbestperiods': nbestpeaks-size list of best periods,
             'lspvals': the full array of periodogram powers,
             'frequencies': the full array of frequencies considered,
             'periods': the full array of periods considered,
             'durations': the array of durations used to run BLS,
             'blsresult': Astropy BLS result object (BoxLeastSquaresResult),
             'blsmodel': Astropy BLS BoxLeastSquares object used for work,
             'stepsize': the actual stepsize used,
             'nfreq': the actual nfreq used,
             'durations': the durations array used,
             'mintransitduration': the input mintransitduration,
             'maxtransitduration': the input maxtransitdurations,
             'method':'bls' -> the name of the period-finder method,
             'kwargs':{ dict of all of the input kwargs for record-keeping}}


kepmag = <NUM_LIT> - <NUM_LIT>*nplog10(keplerflux/f12)<EOL>return kepmag<EOL>

This converts the Kepler flux in electrons/sec to Kepler magnitude.

    The kepler mag/flux relation is::

        fkep = (10.0**(-0.4*(kepmag - 12.0)))*f12
        f12 = 1.74e5 # electrons/sec

    Parameters
    ----------

    keplerflux : float or array-like
        The flux value(s) to convert to magnitudes.

    f12 : float
        The flux value in the Kepler band corresponding to Kepler mag = 12.0.

    Returns
    -------

    np.array
        Magnitudes in the Kepler band corresponding to the input `keplerflux`
        flux value(s).


kepflux = (<NUM_LIT>**(-<NUM_LIT>*(keplermag - <NUM_LIT>)))*f12<EOL>return kepflux<EOL>

This converts the Kepler mag back to Kepler flux.

    Parameters
    ----------

    keplermag : float or array-like
        The Kepler magnitude value(s) to convert to fluxes.

    f12 : float
        The flux value in the Kepler band corresponding to Kepler mag = 12.0.

    Returns
    -------

    np.array
        Fluxes in the Kepler band corresponding to the input `keplermag`
        magnitude value(s).


def keplermag_to_sdssr(keplermag, kic_sdssg, kic_sdssr):

kic_sdssgr = kic_sdssg - kic_sdssr<EOL>if kic_sdssgr < <NUM_LIT>:<EOL><INDENT>kepsdssr = (keplermag - <NUM_LIT>*kic_sdssg)/<NUM_LIT><EOL><DEDENT>else:<EOL><INDENT>kepsdssr = (keplermag - <NUM_LIT:0.1>*kic_sdssg)/<NUM_LIT><EOL><DEDENT>return kepsdssr<EOL>

Converts magnitude measurements in Kepler band to SDSS r band.

    Parameters
    ----------

    keplermag : float or array-like
        The Kepler magnitude value(s) to convert to fluxes.

    kic_sdssg,kic_sdssr : float or array-like
        The SDSS g and r magnitudes of the object(s) from the Kepler Input
        Catalog. The .llc.fits MAST light curve file for a Kepler object
        contains these values in the FITS extension 0 header.

    Returns
    -------

    float or array-like
        SDSS r band magnitude(s) converted from the Kepler band magnitude.


return -<NUM_LIT>*nplog10(<NUM_LIT:1.0> - ppm/<NUM_LIT>)<EOL>

This converts Kepler's flux parts-per-million to magnitudes.

    Mostly useful for turning PPMs reported by Kepler or TESS into millimag
    values to compare with ground-based surveys.

    Parameters
    ----------

    ppm : float or array-like
        Kepler flux measurement errors or RMS values in parts-per-million.

    Returns
    -------

    float or array-like
        Measurement errors or RMS values expressed in magnitudes.