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
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)
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)
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)
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,])))