Exemplo n.º 1
0
def test_sff_priors():
    """SFF Spline flux mean should == lc.flux.mean()
    SFF arclength component should have mean 0
    """
    n_points = 300
    fn = get_pkg_data_filename("../../tests/data/ep60021426alldiagnostics.csv")
    data = np.genfromtxt(fn, delimiter=",", skip_header=1)
    raw_flux = data[:, 1][:n_points]
    centroid_col = data[:, 3][:n_points]
    centroid_row = data[:, 4][:n_points]

    time = np.concatenate((
        np.linspace(0, 20, int(n_points / 3)),
        np.linspace(30, 78, int(n_points / 3)),
        np.linspace(80, 100, int(n_points / 3)),
    ))
    lc = KeplerLightCurve(
        time=time,
        flux=raw_flux,
        flux_err=np.ones(n_points) * 0.0001,
        centroid_col=centroid_col,
        centroid_row=centroid_row,
    )

    sff = SFFCorrector(lc)
    sff.correct()  # should not raise an exception
    assert np.isclose(sff.diagnostic_lightcurves["spline"].flux.mean(),
                      1,
                      atol=1e-3)
    assert np.isclose(sff.diagnostic_lightcurves["sff"].flux.mean(),
                      0,
                      atol=1e-3)
Exemplo n.º 2
0
def test_sine_sff():
    """Can we recover a synthetic sine curve using SFF and LombScargle?"""
    # Retrieve the custom, known signal properties
    tpf = KeplerTargetPixelFile(filename_synthetic_sine)
    true_period = np.float(tpf.hdu[3].header["PERIOD"])
    true_amplitude = np.float(tpf.hdu[3].header["SINE_AMP"])

    # Run the SFF algorithm
    lc = tpf.to_lightcurve()
    corrector = SFFCorrector(lc)
    cor_lc = corrector.correct(
        tpf.pos_corr2,
        tpf.pos_corr1,
        niters=4,
        windows=1,
        bins=7,
        restore_trend=True,
        timescale=0.5,
    )

    # Verify that we get the period within ~20%
    pg = cor_lc.to_periodogram(
        method="lombscargle", minimum_period=1, maximum_period=10, oversample_factor=10
    )
    ret_period = pg.period_at_max_power.value
    threshold = 0.2
    assert (ret_period > true_period * (1 - threshold)) & (
        ret_period < true_period * (1 + threshold)
    )

    # Verify that we get the amplitude to within 10%
    n_cad = len(tpf.time)
    design_matrix = np.vstack(
        [
            np.ones(n_cad),
            np.sin(2.0 * np.pi * cor_lc.time.value / ret_period),
            np.cos(2.0 * np.pi * cor_lc.time.value / ret_period),
        ]
    ).T
    ATA = np.dot(design_matrix.T, design_matrix / cor_lc.flux_err[:, None] ** 2)
    least_squares_coeffs = np.linalg.solve(
        ATA, np.dot(design_matrix.T, cor_lc.flux / cor_lc.flux_err ** 2)
    )
    const, sin_weight, cos_weight = least_squares_coeffs

    fractional_amplitude = (sin_weight ** 2 + cos_weight ** 2) ** (0.5) / const
    assert (fractional_amplitude > true_amplitude / 1.1) & (
        fractional_amplitude < true_amplitude * 1.1
    )
Exemplo n.º 3
0
def test_sff_knots():
    """Is SFF robust against gaps in time and irregular time sampling?
    This test creates a light curve with gaps in time between
    days 20-30 and days 78-80.  In addition, the time sampling rate changes
    in the interval between day 30 and 78.  SFF should fail without error.
    """
    n_points = 300
    fn = get_pkg_data_filename("../../tests/data/ep60021426alldiagnostics.csv")
    data = np.genfromtxt(fn, delimiter=",", skip_header=1)
    raw_flux = data[:, 1][:n_points]
    centroid_col = data[:, 3][:n_points]
    centroid_row = data[:, 4][:n_points]

    time = np.concatenate((
        np.linspace(0, 20, int(n_points / 3)),
        np.linspace(30, 78, int(n_points / 3)),
        np.linspace(80, 100, int(n_points / 3)),
    ))
    lc = KeplerLightCurve(
        time=time,
        flux=raw_flux,
        flux_err=np.ones(n_points) * 0.0001,
        centroid_col=centroid_col,
        centroid_row=centroid_row,
    )

    # These calls should not raise an exception:
    SFFCorrector(lc).correct()
    lc.to_corrector(method="sff").correct()
Exemplo n.º 4
0
def test_transit_sff():
    """Can we recover a synthetic exoplanet signal using SFF and BLS?"""
    # Retrieve the custom, known signal properties
    tpf = KeplerTargetPixelFile(filename_synthetic_transit)
    true_period = np.float(tpf.hdu[3].header["PERIOD"])
    true_rprs = np.float(tpf.hdu[3].header["RPRS"])
    true_transit_lc = tpf.hdu[3].data["NOISELESS_INPUT"]
    max_depth = 1 - np.min(true_transit_lc)

    # Run the SFF algorithm
    lc = tpf.to_lightcurve().normalize()
    corrector = SFFCorrector(lc)
    cor_lc = corrector.correct(
        tpf.pos_corr2,
        tpf.pos_corr1,
        niters=4,
        windows=1,
        bins=7,
        restore_trend=False,
        timescale=0.5,
    )

    # Verify that we get the transit period within 5%
    pg = cor_lc.to_periodogram(
        method="bls",
        minimum_period=1,
        maximum_period=9,
        frequency_factor=0.05,
        duration=np.arange(0.1, 0.6, 0.1),
    )
    ret_period = pg.period_at_max_power.value
    threshold = 0.05
    assert (ret_period > true_period * (1 - threshold)) & (
        ret_period < true_period * (1 + threshold)
    )

    # Verify that we get the transit depth in expected bounds
    assert (pg.depth_at_max_power >= true_rprs ** 2) & (
        pg.depth_at_max_power < max_depth
    )
Exemplo n.º 5
0
def test_detrending_residuals():
    """Test the detrending residual distributions"""
    # Retrieve the custom, known signal properties
    tpf = KeplerTargetPixelFile(filename_synthetic_flat)

    # Run the SFF algorithm
    lc = tpf.to_lightcurve()
    corrector = SFFCorrector(lc)
    cor_lc = corrector.correct(
        tpf.pos_corr2, tpf.pos_corr1, niters=10, windows=5, bins=7, restore_trend=True
    )

    # Verify that we get a significant reduction in RMS
    cdpp_improvement = lc.estimate_cdpp() / cor_lc.estimate_cdpp()
    assert cdpp_improvement > 10.0

    # The residuals should be Gaussian-"ish"
    # Table 4.1 of Ivezic, Connolly, Vanerplas, Gray 2014
    anderson_threshold = 1.57

    resid_n_sigmas = (cor_lc.flux - np.mean(cor_lc.flux)) / cor_lc.flux_err
    A_value, _, _ = stats.anderson(resid_n_sigmas)
    assert A_value ** 2 < anderson_threshold

    n_sigma = np.std(resid_n_sigmas)
    assert n_sigma < 2.0

    corrector = tpf.to_corrector("pld")
    cor_lc = corrector.correct(restore_trend=False)

    cdpp_improvement = lc.estimate_cdpp() / cor_lc.estimate_cdpp()
    assert cdpp_improvement > 10.0

    resid_n_sigmas = (cor_lc.flux - np.mean(cor_lc.flux)) / cor_lc.flux_err
    A_value, crit, sig = stats.anderson(resid_n_sigmas)
    assert A_value ** 2 < anderson_threshold

    n_sigma = np.std(resid_n_sigmas)
    assert n_sigma < 2.0
Exemplo n.º 6
0
def test_sff_breakindex():
    """Regression test for #616."""
    lc = LightCurve(flux=np.ones(20))
    with warnings.catch_warnings():
        # Ignore "LightkurveWarning: The design matrix has low rank".
        warnings.simplefilter("ignore", LightkurveWarning)
        corr = SFFCorrector(lc)
        corr.correct(
            breakindex=[5, 10],
            centroid_col=np.random.randn(20),
            centroid_row=np.random.randn(20),
        )
        assert 5 in corr.window_points
        assert 10 in corr.window_points
        corr.correct(
            breakindex=[5, 10],
            centroid_col=np.random.randn(20),
            centroid_row=np.random.randn(20),
            windows=1,
        )
        assert_array_equal(corr.window_points, np.asarray([5, 10]))
 def build(self, object_info, sherlock_dir, caches_root_dir):
     mission_id = object_info.mission_id()
     sherlock_id = object_info.sherlock_id()
     logging.info("Retrieving star catalog info...")
     mission, mission_prefix, id = super().parse_object_id(mission_id)
     cadence = object_info.cadence if object_info.cadence is not None else "long"
     author = object_info.author if object_info.author is not None else self.authors[
         mission]
     transits_min_count = 1
     star_info = None
     if mission_prefix not in self.star_catalogs:
         raise ValueError("Wrong object id " + mission_id)
     sectors = None if object_info.sectors == 'all' or mission != constants.MISSION_TESS else object_info.sectors
     campaigns = None if object_info.sectors == 'all' or mission != constants.MISSION_K2 else object_info.sectors
     quarters = None if object_info.sectors == 'all' or mission != constants.MISSION_KEPLER else object_info.sectors
     apertures = {}
     tpf_search_results = lk.search_targetpixelfile(str(mission_id))
     for tpf_search_result in tpf_search_results:
         logging.info(
             "There is data for Mission: %s, Year %.0f, Author: %s, ExpTime: %.0f",
             tpf_search_result.mission[0], tpf_search_result.year[0],
             tpf_search_result.author[0],
             tpf_search_result.exptime[0].value)
     tpfs_dir = sherlock_dir + "/tpfs/"
     if not os.path.exists(tpfs_dir):
         os.mkdir(tpfs_dir)
     if mission_prefix == self.MISSION_ID_KEPLER or mission_prefix == self.MISSION_ID_KEPLER_2:
         source = "tpf"
         lcf_search_results = lk.search_lightcurvefile(str(mission_id),
                                                       mission=mission,
                                                       cadence=cadence,
                                                       author=author,
                                                       sector=sectors,
                                                       quarter=quarters,
                                                       campaign=campaigns)
         lcf = lcf_search_results.download_all(
             download_dir=caches_root_dir + LIGHTKURVE_CACHE_DIR)
         tpfs = lk.search_targetpixelfile(str(mission_id), mission=mission, cadence=cadence,
                                        sector=sectors, quarter=quarters,
                                        campaign=campaigns, author=author)\
             .download_all(download_dir=caches_root_dir + LIGHTKURVE_CACHE_DIR,
                           cutout_size=(CUTOUT_SIZE, CUTOUT_SIZE))
         lc_data = self.extract_lc_data(lcf)
         lc = lcf.PDCSAP_FLUX.stitch().remove_nans()
         transits_min_count = 1 if len(lcf) == 0 else 2
         if mission_prefix == self.MISSION_ID_KEPLER:
             sectors = [lcfile.quarter for lcfile in lcf]
         elif mission_prefix == self.MISSION_ID_KEPLER_2:
             logging.info("Correcting K2 motion in light curve...")
             sectors = [lcfile.campaign for lcfile in lcf]
             lc = SFFCorrector(lc).correct(windows=20)
         if not os.path.exists(tpfs_dir):
             os.mkdir(tpfs_dir)
         for tpf in tpfs:
             shutil.copy(tpf.path, tpfs_dir + os.path.basename(tpf.path))
             if mission_prefix == self.MISSION_ID_KEPLER:
                 sector = tpf.quarter
             elif mission_prefix == self.MISSION_ID_KEPLER_2:
                 sector = tpf.campaign
             apertures[sector] = ApertureExtractor.from_boolean_mask(
                 tpf.pipeline_mask, tpf.column, tpf.row)
         star_info = starinfo.StarInfo(
             sherlock_id,
             *self.star_catalogs[mission_prefix].catalog_info(id))
     else:
         source = "eleanor"
         if isinstance(object_info, MissionFfiCoordsObjectInfo):
             coords = SkyCoord(ra=object_info.ra,
                               dec=object_info.dec,
                               unit=(u.deg, u.deg))
             star = eleanor.source.multi_sectors(
                 coords=coords,
                 sectors=object_info.sectors,
                 post_dir=caches_root_dir + ELEANOR_CACHE_DIR,
                 metadata_path=caches_root_dir + ELEANOR_CACHE_DIR)
         else:
             object_id_parsed = re.search(super().NUMBERS_REGEX,
                                          object_info.id)
             object_id_parsed = object_info.id[
                 object_id_parsed.regs[0][0]:object_id_parsed.regs[0][1]]
             star = eleanor.multi_sectors(
                 tic=object_id_parsed,
                 sectors=object_info.sectors,
                 post_dir=caches_root_dir + ELEANOR_CACHE_DIR,
                 metadata_path=caches_root_dir + ELEANOR_CACHE_DIR)
         if star is None:
             raise ValueError("No data for this object")
         if star[0].tic:
             # TODO FIX star info objectid
             logging.info("Assotiated TIC is " + str(star[0].tic))
             tpfs = lk.search_targetpixelfile("TIC " + str(star[0].tic), mission=constants.MISSION_TESS,
                                              cadence=cadence, sector=sectors, quarter=quarters,
                                              campaign=campaigns, author="TESS-SPOC") \
                 .download_all(download_dir=caches_root_dir + LIGHTKURVE_CACHE_DIR,
                               cutout_size=(CUTOUT_SIZE, CUTOUT_SIZE))
             star_info = starinfo.StarInfo(
                 object_info.sherlock_id(),
                 *self.star_catalog.catalog_info(int(star[0].tic)))
         data = []
         for s in star:
             datum = TargetData(s,
                                height=CUTOUT_SIZE,
                                width=CUTOUT_SIZE,
                                do_pca=True)
             data.append(datum)
             for tpf in tpfs:
                 if tpf.sector == s.sector:
                     shutil.copy(tpf.path,
                                 tpfs_dir + os.path.basename(tpf.path))
                     apertures[
                         s.sector] = ApertureExtractor.from_boolean_mask(
                             datum.aperture.astype(bool), tpf.column,
                             tpf.row)
         quality_bitmask = np.bitwise_and(data[0].quality.astype(int), 175)
         lc_data = self.extract_eleanor_lc_data(data)
         lc = data[0].to_lightkurve(
             data[0].__dict__[object_info.eleanor_corr_flux],
             quality_mask=quality_bitmask).remove_nans().flatten()
         sectors = [datum.source_info.sector for datum in data]
         if len(data) > 1:
             for datum in data[1:]:
                 quality_bitmask = np.bitwise_and(datum.quality, 175)
                 lc = lc.append(
                     datum.to_lightkurve(datum.pca_flux,
                                         quality_mask=quality_bitmask).
                     remove_nans().flatten())
             transits_min_count = 2
     return LcBuild(lc, lc_data, star_info, transits_min_count, cadence,
                    None, sectors, source, apertures)
Exemplo n.º 8
0
def test_sff_corrector():
    """Does our code agree with the example presented in Vanderburg
    and Johnson (2014)?"""
    # The following csv file, provided by Vanderburg and Johnson
    # at https://www.cfa.harvard.edu/~avanderb/k2/ep60021426.html,
    # contains the results of applying SFF to EPIC 60021426.
    fn = get_pkg_data_filename("../../tests/data/ep60021426alldiagnostics.csv")
    data = np.genfromtxt(fn, delimiter=",", skip_header=1)
    mask = data[:, -2] == 0  # indicates whether the thrusters were on or off
    time = data[:, 0]
    raw_flux = data[:, 1]
    corrected_flux = data[:, 2]
    centroid_col = data[:, 3]
    centroid_row = data[:, 4]

    # NOTE: we need a small number of windows below because this test data set
    # is unusually short, i.e. has an unusually small number of cadences.
    lc = LightCurve(time=time,
                    flux=raw_flux,
                    flux_err=np.ones(len(raw_flux)) * 0.0001)
    sff = SFFCorrector(lc)
    corrected_lc = sff.correct(
        centroid_col=centroid_col,
        centroid_row=centroid_row,
        restore_trend=True,
        windows=1,
    )
    assert np.isclose(corrected_flux, corrected_lc.flux, atol=0.001).all()
    assert len(sff.window_points) == 0  # expect 0 break points for 1 window

    # masking
    corrected_lc = sff.correct(
        centroid_col=centroid_col,
        centroid_row=centroid_row,
        windows=3,
        restore_trend=True,
        cadence_mask=mask,
    )
    assert np.isclose(corrected_flux, corrected_lc.flux, atol=0.001).all()
    assert len(sff.window_points) == 2  # expect 2 break points for 3 windows

    # masking and breakindex
    corrected_lc = sff.correct(
        centroid_col=centroid_col,
        centroid_row=centroid_row,
        windows=3,
        restore_trend=True,
        cadence_mask=mask,
    )
    assert np.isclose(corrected_flux, corrected_lc.flux, atol=0.001).all()

    # masking and breakindex and iters
    corrected_lc = sff.correct(
        centroid_col=centroid_col,
        centroid_row=centroid_row,
        windows=3,
        restore_trend=True,
        cadence_mask=mask,
        niters=3,
    )
    assert np.isclose(corrected_flux, corrected_lc.flux, atol=0.001).all()

    # masking and breakindex and bins
    corrected_lc = sff.correct(
        centroid_col=centroid_col,
        centroid_row=centroid_row,
        windows=3,
        restore_trend=True,
        cadence_mask=mask,
        bins=5,
    )
    assert np.isclose(corrected_flux, corrected_lc.flux, atol=0.001).all()
    assert np.all((sff.lc.flux_err / sff.corrected_lc.flux_err) == 1)

    # masking and breakindex and bins and propagate_errors
    corrected_lc = sff.correct(
        centroid_col=centroid_col,
        centroid_row=centroid_row,
        windows=3,
        restore_trend=True,
        cadence_mask=mask,
        bins=5,
        propagate_errors=True,
    )
    assert np.isclose(corrected_flux, corrected_lc.flux, atol=0.001).all()
    assert np.all((sff.lc.flux_err / sff.corrected_lc.flux_err) < 1)

    # test using KeplerLightCurve interface
    klc = KeplerLightCurve(
        time=time,
        flux=raw_flux,
        flux_err=np.ones(len(raw_flux)) * 0.0001,
        centroid_col=centroid_col,
        centroid_row=centroid_row,
    )
    sff = klc.to_corrector("sff")
    klc = sff.correct(windows=3, restore_trend=True)
    assert np.isclose(corrected_flux, klc.flux, atol=0.001).all()

    # Can plot
    sff.diagnose()
Exemplo n.º 9
0
def test_remote_data(path):
    """Can we correct a simple K2 light curve?"""
    lc = KeplerLightCurve.read(path, quality_bitmask=None)
    sff = SFFCorrector(lc.remove_nans())
    sff.correct(windows=10, bins=5, timescale=0.5)
    sff.correct(windows=10, bins=5, timescale=0.5, sparse=True)
Exemplo n.º 10
0
def test_sff_tess_warning():
    """SFF is not designed for TESS, so we raise a warning."""
    lc = TessLightCurve(flux=[1, 2, 3], meta={"MISSION": "TESS"})
    with pytest.warns(LightkurveWarning, match="not suitable"):
        corr = SFFCorrector(lc)