def test_remove_sines_iteratively(a, b, c, d):

    # define light curve with two sinusoidal modulation
    x = np.linspace(10, 40, 1200)
    y1 = 20. + np.random.normal(0, .01,
                                1200) + a * np.sin(b * x) + c * np.sin(d * x)

    flc = FlareLightCurve(
        time=x,
        flux=y1,
        flux_err=np.full_like(y1, .01),
    )
    flc.detrended_flux = y1
    flc.detrended_flux_err = np.full_like(y1, .01)

    # find median
    flc = find_iterative_median(flc)
    #     flc.plot()

    # apply function
    flcd = remove_sines_iteratively(flc)

    #     plt.plot(flcd.time, flcd.flux)
    #     plt.plot(flcd.time, flcd.detrended_flux)

    # do some checks
    assert flcd.detrended_flux.std() == pytest.approx(0.01, rel=1e-1)
    assert flcd.detrended_flux.max() < 20.2
    assert flcd.detrended_flux.min() > 19.8
Example #2
0
def _convert_TPF_to_FLC(tpf, lc):
    keys = {
        'primary_header': tpf.hdu[0].header,
        'data_header': tpf.hdu[1].header,
        'pos_corr1': tpf.pos_corr1,
        'pos_corr2': tpf.pos_corr2,
        'pixel_flux': tpf.flux,
        'pixel_flux_err': tpf.flux_err,
        'pipeline_mask': tpf.pipeline_mask
    }

    attributes = lc.__dict__
    z = attributes.copy()
    z.update(keys)
    if "_flux_unit" in z.keys():
        del z["_flux_unit"]
    flc = FlareLightCurve(time_unit=u.day,
                          origin="TPF",
                          flux_unit=u.electron / u.s,
                          **z)
    if flc.pos_corr1 is None:
        flc.pos_corr1 = flc.centroid_col
    if flc.pos_corr2 is None:
        flc.pos_corr2 = flc.centroid_row
    flc = flc[np.isfinite(flc.time) & np.isfinite(flc.flux)
              & np.isfinite(flc.pos_corr1) & np.isfinite(flc.pos_corr2)
              & np.isfinite(flc.cadenceno)]
    return flc
def test_remove_exponential_fringes(a, b, median, c, d):

    # seed numpy random to exclude outliers
    np.random.seed(42)

    # define light curve with two positive fringes
    x = np.linspace(10, 40, 1200)
    y1 = (a * np.exp(-1 * (b - x) * 2) + median + c * np.exp(
        (d - x) * 2) + np.random.normal(0, .0005 * median, 1200))
    y1[800:810] = median + median * .05 * np.linspace(1, 0, 10)

    # define lightcurve
    flc = FlareLightCurve(time=x,
                          flux=y1,
                          flux_err=np.full_like(y1, .0005 * median))
    flc.detrended_flux = y1
    flc.detrended_flux_err = np.full_like(y1, .0005 * median)

    # get iterative median
    flc = find_iterative_median(flc)

    # run the function
    flcd = remove_exponential_fringes(flc)

    #     plt.plot(flcd.time, flcd.flux)
    #     plt.plot(flcd.time, flcd.detrended_flux)

    # do some checks

    #     print(flcd.detrended_flux.std(), flcd.detrended_flux.min(), flcd.detrended_flux.max())
    assert flcd.detrended_flux[:799].std() == pytest.approx(.0005 * median,
                                                            rel=1e-1)
    assert flcd.detrended_flux.max() == pytest.approx(median * 1.05)
    assert flcd.detrended_flux.min() > median * 0.995
def test_estimate_detrended_noise():

    # setup light curve
    time = np.linspace(10, 30, 200)

    # seed numpy to get the same error array
    np.random.seed(30)

    # define flux with gaussian noise and baseline flux
    flux = np.random.normal(0, 40, time.shape[0]) + 200.

    # define light curve
    flc = FlareLightCurve(time=time)
    flc.detrended_flux = flux

    # this should work
    flces = estimate_detrended_noise(flc,
                                     mask_pos_outliers_sigma=2.5,
                                     std_window=100)

    # error should be similar to input error of 40
    np.median(
        flces.detrended_flux_err.value) == pytest.approx(41.38048677022836)

    # re-seed and add a flare
    np.random.seed(30)
    flux = np.random.normal(0, 40, time.shape[0]) + 200.
    flux[120:124] = [500, 380, 300, 270]
    flc = FlareLightCurve(time=time)
    flc.detrended_flux = flux

    # should mask flare, error should not grow
    flces = estimate_detrended_noise(flc,
                                     mask_pos_outliers_sigma=2.5,
                                     std_window=100)

    np.median(
        flces.detrended_flux_err.value) == pytest.approx(41.24232394552432)

    # re-seed and add some NaNs
    np.random.seed(30)
    flux = np.random.normal(0, 40, time.shape[0]) + 200.
    flux[120:124] = [500, 380, 300, 270]
    flux[30:40] = np.nan
    flc = FlareLightCurve(time=time)
    flc.detrended_flux = flux

    # should work regardless
    flces = estimate_detrended_noise(flc,
                                     mask_pos_outliers_sigma=2.5,
                                     std_window=100)

    # error should not change too much
    np.median(
        flces.detrended_flux_err.value) == pytest.approx(41.23144256208637)
def test_find_period():

    # Create a target description
    target = pd.Series({
        "h_mission": "TESS",
        "origin": "flc",
        "ID": 1000,
        "QCS": 10,
        "typ": "custom",
        "mission": "tess",
        "SpT": "M8",
        "prefix": "TIC"
    })

    # Test a case
    start, stop, N, hours = 1000, 1020, 10000, 6
    flc = FlareLightCurve(
        time=np.linspace(start, stop, N),
        flux=400 +
        50 * np.sin(np.linspace(start, stop, N) * np.pi * 48 / hours),
        flux_err=20 * np.random.rand(N),
        quality=np.full(N, 0),
        cadenceno=np.arange(100, N + 100))
    period, mfp = find_period(target,
                              minfreq=.1,
                              maxfreq=40,
                              plot=False,
                              save=False,
                              flc=flc,
                              custom=False)

    # Do some checks
    assert period.value == pytest.approx(hours)
    assert mfp.value == pytest.approx(24 / hours)
Example #6
0
def _convert_LC_to_FLC(lc, origin=None, **kwargs):
    attributes = lc.__dict__
    attributes.update(kwargs)
    print(attributes)
    if "_flux_unit" in attributes.keys():
        del attributes["_flux_unit"]
    flc = FlareLightCurve(time_unit=u.day,
                          origin=origin,
                          flux_unit=u.electron / u.s,
                          **attributes)
    flc = flc[np.isfinite(flc.time) & np.isfinite(flc.flux)
              & np.isfinite(flc.cadenceno)]
    return flc
def generate_lightcurve(errorval,
                        a1,
                        a2,
                        period1,
                        period2,
                        quad,
                        cube,
                        mean=3400.):
    """Generate wild light curves with variability on several
    timescales.
    
    Returns:
    ---------
    FlareLightCurve with time, flux, and flux_err attributes
    """
    time = np.arange(10, 10 + 10 * np.pi, .0008)

    # define the flux
    flux = (np.random.normal(0, errorval, time.shape[0]) + mean +
            a1 * mean * np.sin(period1 * time + 1.) +
            a2 * mean * np.sin(period2 * time) + quad * (time - 25)**2 - cube *
            (time - 25)**3)

    # add a gap in the data
    flux[5600:7720] = np.nan

    # add big and long flare
    l = 66
    flux[5280:5280 + l] = flux[5280:5280 + l] + np.linspace(1000, 250, l)

    # add tiny flare
    l = 3
    flux[15280:15280 + l] = flux[15280:15280 + l] + np.linspace(100, 60, l)

    # add intermediate flare
    l, s = 15, 25280
    flux[s:s + l] = flux[s:s + l] + np.linspace(200, 60, l)

    # typically Kepler and TESS underestimate the real noise
    err = np.full_like(time, errorval / 3 * 2)

    # define FLC
    return FlareLightCurve(time=time, flux=flux, flux_err=err)
Example #8
0
def _from_path_AltaiPony(path):

    rhdul = fits.open(path)
    attrs = dict()
    for k, v in rhdul[0].header.items():
        if str.lower(k) not in ['simple', 'bitpix', 'naxis', 'extend']:
            if str.lower(k) == "keplerid":  #rename keplerid if it appears
                k = "targetid"
            attrs[str.lower(k)] = v

    for k in [
            'time', 'flux', 'flux_err', 'centroid_col', 'centroid_row',
            'quality', 'cadenceno', 'detrended_flux', 'detrended_flux_err',
            'quality_bitmask', 'saturation'
    ]:
        try:
            attrs[k] = rhdul[1].data[k].byteswap().newbyteorder()
        except KeyError:
            LOG.info("Warning: Keyword {} not in file.".format(k))
            continue

    return FlareLightCurve(**attrs)
Example #9
0
def interpolate_missing_cadences(lc, **kwargs):
    """Interpolate missing cadences in 
    light curve, skipping larger gaps in data.
    
    Parameters:
    -----------
    lc : FlareLightCurve
        the light curve
    kwargs : dict
        keyword arguments to pass to find_gaps method
    
    Return:
    -------
    interpolated FlareLightCurve
    """

    # find gaps that are too big to be interpolated with a good conscience
    gaps = lc.find_gaps().gaps

    # set up interpolated array
    time, flux, flux_err, newcadence = [], [], [], []

    # interpolate within each gap
    for i, j in gaps:

        # select gap
        gaplc = lc[i:j]

        # get old cadence
        oldx = gaplc.cadenceno.value

        # cadenceno are complete in uncorrected flux,
        # so we fill in the removed cadences
        newx = np.arange(gaplc.cadenceno.value[0], gaplc.cadenceno.value[-1])
        newcadence.append(newx)

        # interpolate flux error
        f = interp1d(oldx, gaplc.flux_err.value)
        flux_err.append(f(newx))

        # interpolate time
        f = interp1d(oldx, gaplc.time.value)
        time.append(f(newx))

        # interpolate flux
        f = interp1d(oldx, gaplc.flux.value)
        flux.append(f(newx))

    # stitch together new light curve
    newlc = FlareLightCurve(time=np.concatenate(time),
                            flux=np.concatenate(flux),
                            flux_err=np.concatenate(flux_err),
                            targetid=lc.targetid)

    # add new cadence array
    newcadenceno = np.concatenate(newcadence)
    newlc["cadenceno"] = newcadenceno

    # flag values that have been interpolated in the new light curve
    newvals = np.sort(list(set(newcadenceno) - set(lc.cadenceno.value)))
    newvalindx = np.searchsorted(newcadenceno, newvals)
    newlc["interpolated"] = 0  # not interpolated values
    newlc.interpolated[newvalindx] = 1  # interpolated values

    return newlc
def test_get_full_transit_mask():
    flc = FlareLightCurve(time=np.linspace(10,30,101))

    system = pd.DataFrame({"pl_trandur":[12.,20.,np.nan],
                           "pl_tranmidepoch" : [9., 37., np.nan], 
                           "pl_orbper": [5.,10.,19.],
                           "pl_tranmidepocherr" : [0.001, 0.001, .1], 
                           "pl_orbpererr": [.001,.001,.1]})

    # if the table contains NaN, demand cleanup of system parameters
    error_message = ("Some planets in your system are not transiting"
                             ", or lack necessary info about transit epoch"
                             ", period, and duration.")
    with pytest.raises(ValueError, match=error_message):
        get_full_transit_mask(system, flc)

    # drop last row everything should work again    
    system = system.iloc[:2]

    # the results are manually verified here:
    mask = get_full_transit_mask(system, flc)
   
    assert (flc.time.value[mask] == 
            pytest.approx(np.array([13.8, 14. , 14.2, 16.6, 16.8, 17. , 
                                    17.2, 17.4, 18.8, 19. , 19.2, 23.8, 
                                    24. , 24.2, 26.6, 26.8, 27. , 27.2, 
                                    27.4, 28.8, 29. , 29.2])))
    

    assert mask.dtype == "bool"

    # Check overlapping transits are treated okay
    system = pd.DataFrame({"pl_trandur":[12., 20.],
                           "pl_tranmidepoch" : [9., 9.], 
                           "pl_orbper": [5.,10.],
                           "pl_tranmidepocherr" : [0.001, 0.001], 
                           "pl_orbpererr": [.001,.001]})

    mask = get_full_transit_mask(system, flc)
    

    # These are also manually confirmed
    assert (flc.time.value[mask] == 
            pytest.approx(np.array([13.8, 14. , 14.2, 18.6, 18.8,
                                    19. , 19.2, 19.4, 23.8, 24. ,
                                    24.2, 28.6, 28.8, 29. , 29.2, 
                                    29.4])))
    
    # Check that single transits are treated okay
    system = pd.DataFrame({"pl_trandur":[12., 20.],
                           "pl_tranmidepoch" : [9., 9.], 
                           "pl_orbper": [5.,np.nan],
                           "pl_tranmidepocherr" : [0.001, 0.001], 
                           "pl_orbpererr": [.001,.001]})

    mask = get_full_transit_mask(system, flc)


    # These are also manually confirmed
    assert (flc.time.value[mask] == 
            pytest.approx(np.array([13.8, 14. , 14.2, 18.8,
                                    19. , 19.2, 23.8, 24. ,
                                    24.2, 28.8, 29. , 29.2,])))