Ejemplo n.º 1
0
def mask_orbit_start_and_end(time,
                             flux,
                             orbitgap=1,
                             expected_norbits=2,
                             orbitpadding=6 / (24)):
    """
    Ignore the times near the edges of orbits. (Works if you know the gap
    structure within the orbits).

    args:
        time, flux
    returns:
        time, flux: with `orbitpadding` days trimmed out
    """
    norbits, groups = lcmath.find_lc_timegroups(time, mingap=orbitgap)

    if norbits != expected_norbits:
        errmsg = 'got {} orbits, expected {}. groups are {}'.format(
            norbits, expected_norbits, repr(groups))
        raise AssertionError

    sel = np.zeros_like(time).astype(bool)
    for group in groups:
        tg_time = time[group]
        start_mask = (np.min(tg_time), np.min(tg_time) + orbitpadding)
        end_mask = (np.max(tg_time) - orbitpadding, np.max(tg_time))
        sel |= ((time > max(start_mask)) & (time < min(end_mask)))

    return_time = time[sel]
    return_flux = flux[sel]

    return return_time, return_flux
Ejemplo n.º 2
0
def remove_janky_lc_segments(time,
                             flux,
                             othervectors=None,
                             gap=0.5,
                             verbose=True):
    """
    Sometimes a star's light curve in one sector is trash compared to the other
    sectors (e.g., if it turned out to be on the chip edge).

    Given the time, flux, and any ancillary time-series, this function filters
    out such light curve segments.
    """

    if not np.all(np.isfinite(flux)):
        raise AssertionError(
            'mask_timegap_edges requires all fluxes to be finite')

    n_groups, groups = lcmath.find_lc_timegroups(time, mingap=gap)

    print('remove_janky_lc_segments: got {} timegroups'.format(n_groups))

    sel = np.zeros_like(time).astype(bool)

    for group in groups:

        tg_time = time[group]
        tg_flux = flux[group]

        tg_std = np.std(tg_flux)

    #FIXME: WIP finish implementing!
    # for group in groups:

    #     tg_time = time[group]

    #     start_mask = (np.min(tg_time), np.min(tg_time) + padding)
    #     end_mask = (np.max(tg_time) - padding, np.max(tg_time))

    #     sel |= (
    #         (time > max(start_mask)) & (time < min(end_mask))
    #     )

    # return_time = time[sel]
    # return_flux = flux[sel]

    # if isinstance(othervectors, list):
    #     return_vectors = []
    #     for othervector in othervectors:
    #         return_vectors.append(
    #             othervector[sel]
    #         )
    # else:
    #     return_vectors = None

    return return_time, return_flux, return_vectors
def stellarvar_process_raw_lc(lcfile, mingap=0.5, windowsize=48 * 3):
    """
    process raw lightcurve (IRM2) to show off stellar variability.  turn it to
    relative flux units. stitch consistent normalization across orbits. (also,
    median filter with a BIG window).  get the stellingwerf period and t0 as
    well.

        mingap (float): units of days

    return:

        time, whitened_flux, err_flux, flux, outdir, spdm
    """
    if windowsize % 2 == 0:
        windowsize += 1

    hdulist = fits.open(lcfile)
    data = hdulist[1].data

    time, mag, err_mag = data['TMID_BJD'], data['IRM2'], data['IRE2']

    time, flux, err_flux = _get_flux_from_mags(time, mag, err_mag)

    # for detrending, need to split into orbits.  get time groups, and filter
    # each one
    ngroups, groups = lcmath.find_lc_timegroups(time, mingap=mingap)
    assert ngroups == 2

    tg_smooth_flux = []
    for group in groups:
        #tg_smooth_flux.append( medfilt(flux[group], windowsize) )
        tg_smooth_flux.append(savgol(flux[group], windowsize, polyorder=1))

    flux_smooth = np.concatenate(tg_smooth_flux)

    func = interp1d(time, flux_smooth)
    flux_to_div = func(time)

    outdir = os.path.dirname(lcfile)
    divsavpath = os.path.join(
        outdir, 'DIVMODEL_{}.png'.format(os.path.basename(lcfile)))
    _divmodel_plot(time, flux, flux_to_div, divsavpath)

    whitened_flux = flux / flux_to_div

    spdm = periodbase.stellingwerf_pdm(time,
                                       whitened_flux,
                                       err_flux,
                                       magsarefluxes=True,
                                       nworkers=8)

    return time, whitened_flux, err_flux, flux, outdir, spdm
Ejemplo n.º 4
0
def mask_timegap_edges(time,
                       flux,
                       othervectors=None,
                       gap=0.5,
                       padding=12 / (24),
                       verbose=True):
    """
    Drop the times near the edges of any kind of time gap; this is the obvious
    way of treating the "ramp" systematic.

    args:

        time: times with FINITE fluxes.

        flux: FINITE fluxes corresponding to times

        othervectors: None, or list of vectors with same length as flux.

        gap (float): minimum amount of gap space to be considered a gap.

        padding (float): amount of time at the edge of each time group to drop.
        12 hours seems to be a typical duration for the ramp systematic.

    returns:
        time, flux, othervectors: with `padding` days trimmed out
    """

    if not np.all(np.isfinite(flux)):
        raise AssertionError(
            'mask_timegap_edges requires all fluxes to be finite')

    n_groups, groups = lcmath.find_lc_timegroups(time, mingap=gap)

    if verbose:
        print('mask_timegap_edges: got {} timegroups'.format(n_groups))

    sel = np.zeros_like(time).astype(bool)

    for group in groups:

        tg_time = time[group]

        try:
            start_mask = (np.min(tg_time), np.min(tg_time) + padding)
        except Exception as e:
            print(e)
            import IPython
            IPython.embed()
        end_mask = (np.max(tg_time) - padding, np.max(tg_time))

        sel |= ((time > max(start_mask)) & (time < min(end_mask)))

    return_time = time[sel]
    return_flux = flux[sel]

    if isinstance(othervectors, list):
        return_vectors = []
        for othervector in othervectors:
            return_vectors.append(othervector[sel])
    else:
        return_vectors = None

    return return_time, return_flux, return_vectors
def transit_process_raw_lc(lcfile,
                           mingap=0.5,
                           windowsize=48 * 2,
                           extra_maskfrac=0.03):
    """
    process raw lightcurve (IRM2) to show off transits.  turn it to relative
    flux units. apply a median filter with a windowsize of whatever's given.

        mingap (float): units of days

        windowsize (int): units of n_cadences, to be applied in the median
        filter.

    return:

        time, whitened_flux, err_flux, time_oot, flux_oot, err_flux_oot, flux
    """

    hdulist = fits.open(lcfile)
    data = hdulist[1].data

    time, mag, err_mag = data['TMID_BJD'], data['IRM2'], data['IRE2']

    time, flux, err_flux = _get_flux_from_mags(time, mag, err_mag)

    ##########################################
    outdir = os.path.dirname(lcfile)
    blsfit_savpath = os.path.join(
        outdir, 'BLS_{}.png'.format(os.path.basename(lcfile)))
    trapfit_savpath = os.path.join(
        outdir, 'TRAPEZOIDAL_{}.png'.format(os.path.basename(lcfile)))
    inout_savpath = os.path.join(
        outdir, 'INOUTTRANSIT_{}.png'.format(os.path.basename(lcfile)))

    time_oot, flux_oot, err_flux_oot = (
        transits.given_lc_get_out_of_transit_points(
            time,
            flux,
            err_flux,
            blsfit_savpath=blsfit_savpath,
            trapfit_savpath=trapfit_savpath,
            in_out_transit_savpath=inout_savpath,
            nworkers=8,
            extra_maskfrac=extra_maskfrac))

    # for detrending, need to split into orbits.  get time groups, and filter
    # each one
    ngroups, groups = lcmath.find_lc_timegroups(time_oot, mingap=mingap)
    assert ngroups == 2

    tg_smooth_oot_flux = []
    for group in groups:
        tg_smooth_oot_flux.append(medfilt(flux_oot[group], windowsize))

    flux_oot_smooth = np.concatenate(tg_smooth_oot_flux)

    func = interp1d(time_oot, flux_oot_smooth)
    flux_to_div = func(time)

    divsavpath = os.path.join(
        outdir, 'DIVMODEL_{}.png'.format(os.path.basename(lcfile)))
    _divmodel_plot(time, flux, flux_to_div, divsavpath)

    whitened_flux = flux / flux_to_div

    return (time, whitened_flux, err_flux, time_oot, flux_oot, err_flux_oot,
            flux, outdir)
Ejemplo n.º 6
0
def plot_mag_vs_EPD_parameters(lcpaths,
                               lcdatalist,
                               magtype='IRM1',
                               get_norbits=1,
                               expected_norbits=2,
                               savdir=None):

    xcols = ['TMID_BJD', 'CCDTEMP', 'XIC', 'YIC', 'FSV', 'FDV', 'FKV']
    xkeys = ['tmidbjd', 'T', 'x', 'y', 's', 'd', 'k']
    # could also include other parameters, like decimal_x, decimal_y,
    # cross-terms...
    desiredx = xkeys

    if get_norbits not in [1]:
        raise AssertionError('you usually only want to get 1 orbit')

    for lcpath, lc in zip(lcpaths, lcdatalist):

        # collect the data for the first orbit in this sector
        times = lc['TMID_BJD']

        orbitgap = 1.  # days
        found_norbits, groups = lcmath.find_lc_timegroups(times,
                                                          mingap=orbitgap)

        if found_norbits != expected_norbits:
            outmsg = ('assuming given two orbits. {} orbits. Time {}'.format(
                norbits, repr(times)))
            raise AssertionError(outmsg)

        if get_norbits == 1:
            group = groups[0]
        else:
            raise NotImplementedError

        # collect the data for the first orbit in this sector
        d = {}
        for xcol, k in zip(xcols, xkeys):
            d[k] = lc[xcol][group]

        if np.all(pd.isnull(lc[magtype])):
            print('mags are all NaN for {}, continue'.format(savname))
            continue

        ##########################################

        # begin plotting.
        nrows = len(xcols)

        plt.close('all')
        fig, axs = plt.subplots(nrows=nrows, ncols=1, figsize=(3, 2 * nrows))
        axs = axs.flatten()

        yval = lc[magtype][group]
        yoffset = np.round(np.median(yval), decimals=2)
        yval -= yoffset
        yval *= 1e3

        savname = ('{}_vs_EPD_parameters_{}_norbit{}.png'.format(
            magtype,
            os.path.basename(lcpath).replace('.fits', ''), get_norbits))
        savpath = os.path.join(savdir, savname)

        if os.path.exists(savpath):
            print('found {}, continue'.format(savpath))
            continue

        for xstr, ax in zip(desiredx, axs):

            if xstr in ['s', 'd', 'k', 'x', 'y', 'T', 'tmidbjd']:
                xval = d[xstr]
            elif xstr == 'decimal_x':
                xval = d['x'] - np.floor(d['x'])
            elif xstr == 'decimal_y':
                xval = d['y'] - np.floor(d['y'])
            elif xstr == 'sin(2pi*x)':
                xval = np.sin(2 * np.pi * d['x'])
            elif xstr == 'sin(2pi*y)':
                xval = np.sin(2 * np.pi * d['y'])
            elif xstr == 'cos(2pi*x)':
                xval = np.cos(2 * np.pi * d['x'])
            elif xstr == 'cos(2pi*y)':
                xval = np.cos(2 * np.pi * d['y'])
            elif xstr == 's^2':
                xval = d['s'] * d['s']
            elif xstr == 'd^2':
                xval = d['d'] * d['d']
            elif xstr == 'k^2':
                xval = d['k'] * d['k']
            elif xstr == 's*d':
                xval = d['s'] * d['d']
            elif xstr == 'd*k':
                xval = d['d'] * d['k']
            elif xstr == 's*k':
                xval = d['s'] * d['k']

            if xstr == 'tmidbjd':
                xval -= np.median(xval)
            if xstr in ['x', 'y']:
                xoffset = int(np.median(xval))
                xval -= xoffset

            if xstr == 'tmidbjd':
                ax.scatter(xval,
                           yval,
                           rasterized=True,
                           alpha=0.8,
                           zorder=3,
                           c='k',
                           lw=0,
                           s=5)
            else:
                ax.scatter(xval,
                           yval,
                           rasterized=True,
                           alpha=0.8,
                           zorder=3,
                           c='k',
                           lw=0,
                           s=4)

            if xstr == 'tmidbjd':
                xstr = 't - t$_0$ [d]'
            if xstr in ['x', 'y']:
                xstr = xstr + ' - {}'.format(xoffset)
            ax.text(0.98,
                    0.02,
                    xstr,
                    fontsize='x-small',
                    ha='right',
                    va='bottom',
                    transform=ax.transAxes)
            ax.set_ylabel('{} - {} [mmag]'.format(magtype, yoffset),
                          fontsize='x-small')

            ax.get_yaxis().set_tick_params(which='both', direction='in')
            ax.get_xaxis().set_tick_params(which='both', direction='in')

            ylim = ax.get_ylim()
            ax.set_ylim((max(ylim), min(ylim)))

        fig.tight_layout(h_pad=0.15)
        fig.savefig(savpath, dpi=400, bbox_inches='tight')

        print('made {}'.format(savpath))
Ejemplo n.º 7
0
def get_light_detrended_data(data):
    """
    clip +/12 hours on every orbit
    fit out a quadratic to each orbit

    return a dict with keys:
        orbit1, orbit2, orbit3, orbit4, sector7, sector9, allsectors

        and each key leads to another dictionary with time and all available
        apertures+detrended stages.
    """

    data_dict = {}
    dtrtypes = ['IRM', 'PCA', 'TFA']
    apnums = [1, 2, 3]
    sectornums = [7, 9]

    orbitgap = 1
    orbitpadding = 0.5

    for sectornum, sector_data in zip(sectornums, data):

        time = sector_data['TMID_BJD']

        flux_sector_dict = {}
        for dtrtype in dtrtypes:
            for apnum in apnums:
                k = dtrtype + str(apnum)
                this_flux = (vp._given_mag_get_flux(sector_data[k]))

                # now mask orbit start and end
                time_copy = deepcopy(time)

                trim_time, trim_this_flux = moe.mask_orbit_start_and_end(
                    time_copy,
                    this_flux,
                    orbitgap=orbitgap,
                    expected_norbits=2,
                    orbitpadding=orbitpadding)

                # now fit out quadratics from each orbit and rejoin them
                norbits, trim_time_groups = lcmath.find_lc_timegroups(
                    trim_time, mingap=orbitgap)
                if norbits != 2:
                    raise AssertionError('expected 2 orbits')

                save_flux = []
                for time_group in trim_time_groups:
                    trim_time_orbit = trim_time[time_group]
                    trim_this_flux_orbit = trim_this_flux[time_group]

                    order = 2  # fit out a quadtric! #FIXME CHECK
                    p = Legendre.fit(trim_time_orbit, trim_this_flux_orbit,
                                     order)
                    trim_this_flux_orbit_fit = p(trim_time_orbit)

                    save_flux_orbit = (trim_this_flux_orbit /
                                       trim_this_flux_orbit_fit)

                    save_flux.append(save_flux_orbit)

                flux_sector_dict[k] = np.concatenate(save_flux)

        # update time to be same length as all the trimmed fluxes
        time = trim_time

        sectorkey = 'sector{}'.format(sectornum)
        data_dict[sectorkey] = {}
        data_dict[sectorkey]['time'] = time
        data_dict[sectorkey]['fluxes'] = flux_sector_dict

    sectorkeys = ['sector{}'.format(s) for s in sectornums]

    # create the "allsectors" dataset
    data_dict['allsectors'] = {}
    data_dict['allsectors']['time'] = np.concatenate(
        [data_dict[sectorkey]['time'] for sectorkey in sectorkeys])

    data_dict['allsectors']['fluxes'] = {}
    for dtrtype in dtrtypes:
        for apnum in apnums:
            k = dtrtype + str(apnum)
            data_dict['allsectors']['fluxes'][k] = np.concatenate([
                data_dict[sectorkey]['fluxes'][k] for sectorkey in sectorkeys
            ])

    return data_dict
Ejemplo n.º 8
0
def get_lc_given_fficutout(workingdir, cutouts, c_obj, return_pkl=False):
    """
    Do simple aperture photometry on FFI cutouts. Uses world's simplest
    background subtraction -- the cutout median. Imposes an aperture radius of
    3 pixels. Invents the error bars as 1/sqrt(n_counts).

    To clean up the light curve, the following steps did a decent job:

    If multi-sector, each sector is normalized by its median. Sectors are
    then stitched together, and only quality==0 cadences are taken.
    If any "timegroup" (usually sectors, but not strictly -- here I define it
    by 0.5 day gaps) has much worse interquartile range than the other (>5x the
    median IQR), drop that timegroup. This usually means that the star was on
    bad pixels, or the edge of the detector, or something similar for one
    sector.

    Then, sigma clip out any ridiculous outliers via [7sigma, 7sigma] symmetric
    clip.

    Then, required all fluxes and errors were finite, and for each timegroup
    masked out 0.5 days at the beginning, and 0.5 days at the end. This makes
    the gaps bigger, but mostly throws out ramp systematic-infested data that
    othewise throws off the period measurement.

    Finally, an apparently relatively common long-term systematic in these LCs
    looks just like a linear slope over the course of an orbit. (Maybe b/c
    stars are moving, but aperture center is not? Unclear.) Therefore, to
    detrend, I fit out a LINE in time from each time group, if the group has at
    least two days worth of data. (Not if shorter, to avoid overfitting).

    Then, save the lightcurve and related data to a pickle file, in workingdir.
    If the pickle is found to already exist, and return_pkl is True, it is
    loaded and returned

    ----------
    args:

        workingdir (str): directory to which the light curve is written

        cutouts (list of length at least 1): paths to fficutouts. assumed to be
        from different sectors.

        c_obj (astropy.skycoord): coordinate of object, used to project wcs to
        image.

    ----------
    returns:

        if fails to get a good light curve, returns None. else, returns
        dictionary with the following keys.

        'time': time array
        'quality': quality flag array
        'flux': sigma-clipped flux (counts)
        'rel_flux': sigma-clipped relative flux, fully detrended
        'rel_flux_err': sigma-clipped relative flux errors
        'predetrending_time':
        'predetrending_rel_flux': before fitting out the line, rel flux values
        'predetrending_rel_flux_err':
        'x': location of aperture used to extract light curve
        'y': ditto
        'median_imgs': list of median images of the stack used to extract apertures
        'cutout_wcss': WCSs to the above images
    """

    outpath = os.path.join(workingdir, 'multisector_lc.pkl')
    if os.path.exists(outpath) and not return_pkl:
        print('WRN! found {}, returning without load'.format(outpath))
        return
    elif os.path.exists(outpath) and return_pkl:
        print('found {}, returning with load'.format(outpath))
        with open(outpath, 'rb') as f:
            out_dict = pickle.load(f)
        return out_dict

    if len(cutouts) == 0:
        raise AssertionError('something wrong in tesscut! fix this!')

    # img_flux and img_flux_err are image cubes of (time x spatial x spatial).
    # make lists of them for each sector.
    img_fluxs = [iu.get_data_keyword(f, 'FLUX') for f in cutouts]

    # for the background, just take the median of the image. very simple.
    bkgd_fluxs = [np.array([np.nanmedian(img_flux[ix, :, :]) for ix in
                            range(len(img_flux))]) for img_flux in img_fluxs]

    img_flux_errs = [iu.get_data_keyword(f, 'FLUX_ERR') for f in cutouts]

    times = [iu.get_data_keyword(f, 'TIME')+2457000 for f in cutouts]
    qualitys = [iu.get_data_keyword(f, 'QUALITY') for f in cutouts]

    cut_hduls = [fits.open(f) for f in cutouts]
    cutout_wcss = [wcs.WCS(cuthdul[2].header) for cuthdul in cut_hduls]

    # get the location to put down the apertures
    try:
        xs, ys = [], []
        for cutout_wcs in cutout_wcss:
            _x, _y = cutout_wcs.all_world2pix(
                c_obj.icrs.ra, c_obj.icrs.dec, 0
            )
            xs.append(_x)
            ys.append(_y)
    except Exception as e:
        print('ERR! wcs all_world2pix got {}'.format(repr(e)))
        import IPython; IPython.embed()

    #
    # plop down a 3 pixel circular aperture at the locations. then make the
    # light curves by doing the sum!
    #
    positions = [(x, y) for x,y in zip(xs, ys)]

    try:
        circ_apertures = [
            CircularAperture(position, r=3) for position in positions
        ]

    except ValueError as e:

        print('ERR1 {}'.format(e))

        out_dict = {
            'time':[],
            'quality':[],
            'flux':[],
            'rel_flux':[],
            'rel_flux_err':[],
            'predetrending_time':[],
            'predetrending_rel_flux':[],
            'predetrending_rel_flux_err':[]
        }

        with open(outpath, 'wb') as f:
            pickle.dump(out_dict, f)

        return None

    fluxs = []
    median_imgs = []
    # iterate over sectors
    for img, bkgd, aper in zip(img_fluxs, bkgd_fluxs, circ_apertures):

        img_stack = img - bkgd[:,None,None]

        # iterate over cadences in sector
        s_flux = []
        for _img in img_stack:

            phot_table = aperture_photometry(_img, aper)

            s_flux.append(phot_table['aperture_sum'])

        fluxs.append(np.array(s_flux))

        median_img = np.nanmedian(img_stack, axis=0)
        median_imgs.append(median_img)

    # normalize each sector by its median
    rel_fluxs = [f/np.nanmedian(f) for f in fluxs]
    rel_flux_errs = [np.sqrt(f)/np.nanmedian(f) for f in fluxs]

    #
    # concatenate sectors together and take only quality==0 cadences.
    #
    time = np.concatenate(times).flatten()
    quality = np.concatenate(qualitys).flatten()
    flux = np.concatenate(fluxs).flatten()
    rel_flux = np.concatenate(rel_fluxs).flatten()
    rel_flux_err = np.concatenate(rel_flux_errs).flatten()

    sel = (quality == 0)

    time = time[sel]
    flux = flux[sel]
    rel_flux = rel_flux[sel]
    rel_flux_err = rel_flux_err[sel]
    quality = quality[sel]

    #
    # sort everything into time order
    #
    sind = np.argsort(time)

    time = time[sind]
    quality = quality[sind]
    flux = flux[sind]
    rel_flux = rel_flux[sind]
    rel_flux_err = rel_flux_err[sind]

    #
    # if any "timegroup" (usually sectors, but not strictly -- here I define it
    # by 0.5 day gaps) has much worse interquartile range, drop it. This
    # usually means that the star was on bad pixels, or the edge of the
    # detector, or something similar for one sector.
    #
    ngroups, groups = find_lc_timegroups(time, mingap=0.5)

    rel_flux_iqrs = nparr([
        iqr(rel_flux[group], rng=(25,75)) for group in groups]
    )

    if ngroups >= 3:

        median_iqr = np.nanmedian(rel_flux_iqrs)

        bad_groups = (rel_flux_iqrs > 5*median_iqr)

        if len(bad_groups[bad_groups]) > 0:
            print('WRN! got {} bad time-groups. dropping them.'.
                  format(len(bad_groups[bad_groups])))

            gd_inds = nparr(groups)[~bad_groups]

            time = np.concatenate([time[gd] for gd in gd_inds]).flatten()
            quality = np.concatenate([quality[gd] for gd in gd_inds]).flatten()
            flux = np.concatenate([flux[gd] for gd in gd_inds]).flatten()
            rel_flux = np.concatenate([rel_flux[gd] for gd in gd_inds]).flatten()
            rel_flux_err = np.concatenate([rel_flux_err[gd] for gd in gd_inds]).flatten()

        else:
            # did not find any bad groups
            pass

    else:
        # trickier to detect outlying sectors with fewer groups
        pass

    #
    # sigma clip out any ridiculous outliers via [7sigma, 7sigma] symmetric
    # clip.
    #
    stime, srel_flux, srel_flux_err, [sflux, squality] = (
        sigclip_magseries_with_extparams(
        time, rel_flux, rel_flux_err, [flux, quality],
        sigclip=[7,7], iterative=False, magsarefluxes=True)
    )

    #
    # require finite fluxes. then mask gap edges. if you get no finite values,
    # save the dud pickle and return None.
    #
    sel = np.isfinite(srel_flux)

    stime = stime[sel]
    sflux = sflux[sel]
    srel_flux = srel_flux[sel]
    srel_flux_err = srel_flux_err[sel]
    squality = squality[sel]

    if len(stime) == len(sflux) == 0:

        out_dict = {
            'time':stime,
            'quality':squality,
            'flux':sflux,
            'rel_flux':srel_flux,
            'rel_flux_err':srel_flux_err,
            'predetrending_time':stime,
            'predetrending_rel_flux':srel_flux,
            'predetrending_rel_flux_err':srel_flux_err,
            'x':np.array(xs).flatten(),
            'y':np.array(ys).flatten(),
            'median_imgs': median_imgs,
            'cutout_wcss': cutout_wcss
        }

        with open(outpath, 'wb') as f:
            pickle.dump(out_dict, f)

        return None

    if not np.any(median_imgs[0]) and np.any(sflux):
        print('somehow getting nan image but finite flux')
        import IPython; IPython.embed()

    stime, srel_flux, [srel_flux_err, sflux, squality] = (
        lcu.mask_timegap_edges(stime, srel_flux,
                               othervectors=[srel_flux_err, sflux, squality],
                               gap=0.5, padding=12/(24))
    )

    #
    # Fit out a LINE in time from each time group, if the group has at least
    # two days worth of data. I added this because an apparently relatively
    # common long-term systematic in these LCs looks just like a linear slope
    # over the course of an orbit. (Maybe b/c stars are moving, but aperture
    # center is not? Unclear.)
    #
    predetrending_time = stime
    predetrending_rel_flux = srel_flux
    predetrending_rel_flux_err = srel_flux_err

    ngroups, groups = find_lc_timegroups(stime, mingap=0.5)

    _time, _rflux, _rflux_err = [], [], []

    for group in groups:

        tg_time = stime[group]
        tg_rel_flux = srel_flux[group]
        tg_rel_flux_err = srel_flux_err[group]

        if len(tg_time) <= 1:
            # singletons or zero-groups would cause problems
            continue

        if tg_time.max() - tg_time.min() < 2:

            # don't try fitting out trends in small time groups (risks
            # overfitting).

            _time.append(tg_time)
            _rflux.append(tg_rel_flux)
            _rflux_err.append(tg_rel_flux_err)

            continue

        try:

            p = Legendre.fit(tg_time, tg_rel_flux, 1)
            coeffs = p.coef

            tg_fit_rel_flux = p(tg_time)

            # divide out the linear fit
            tg_dtr_rel_flux = tg_rel_flux/tg_fit_rel_flux

            _time.append(tg_time)
            _rflux.append(tg_dtr_rel_flux)
            _rflux_err.append(tg_rel_flux_err)

        except np.linalg.LinAlgError:
            print('WRN! Legendre.fit failed, b/c bad data for this group. '
                  'Continue.')
            continue

    if len(_time) == 0:

        out_dict = {
            'time':stime,
            'quality':squality,
            'flux':sflux,
            'rel_flux':srel_flux,
            'rel_flux_err':srel_flux_err,
            'predetrending_time':stime,
            'predetrending_rel_flux':srel_flux,
            'predetrending_rel_flux_err':srel_flux_err,
            'x':np.array(xs).flatten(),
            'y':np.array(ys).flatten(),
            'median_imgs': median_imgs,
            'cutout_wcss': cutout_wcss
        }

        with open(outpath, 'wb') as f:
            pickle.dump(out_dict, f)

        return None

    stime = np.concatenate(_time).flatten()
    srel_flux = np.concatenate(_rflux).flatten()
    srel_flux_err = np.concatenate(_rflux_err).flatten()

    #
    # 1-dimensional arrays:
    # time, quality, flux, rel_flux, and rel_flux_err are all of same length.
    #
    # xs and ys are length n_sectors; they are the positions used in the
    # aperture.
    #
    # median_imgs is list of length n_sectors, for which each entry is the
    # median image in that sector.
    #
    # cutout_wcss is list of length n_sectors, each entry is the WCS
    # corresponding to median_image
    #
    out_dict = {
        'time':stime,
        'quality':squality,
        'flux':sflux,
        'rel_flux':srel_flux,
        'rel_flux_err':srel_flux_err,
        'predetrending_time':predetrending_time,
        'predetrending_rel_flux':predetrending_rel_flux,
        'predetrending_rel_flux_err':predetrending_rel_flux_err,
        'x':np.array(xs).flatten(),
        'y':np.array(ys).flatten(),
        'median_imgs': median_imgs,
        'cutout_wcss': cutout_wcss
    }

    with open(outpath, 'wb') as f:
        pickle.dump(out_dict, f)

    if len(stime) == len(sflux) == 0:
        return None

    else:
        return out_dict
Ejemplo n.º 9
0
def main(modelid):

    make_threadsafe = 0
    cut_tess = 1

    fitindiv = 0
    phaseplot = 0
    grounddepth = 0
    cornerplot = 0
    subsetcorner = 0

    N_samples = 30000 # took 2h 20m, but Rhat=1.0 for all
    # N_samples = 100 # testing
    target_accept = 0.9

    OVERWRITE = 1
    REALID = 'TOI_837'

    PLOTDIR = os.path.join(
        RESULTSDIR, '{}_{}_phot_results'.format(REALID, modelid)
    )
    if not os.path.exists(PLOTDIR):
        os.mkdir(PLOTDIR)
    datestr = '20200805'
    PLOTDIR = os.path.join(PLOTDIR, datestr)

    ##########################################

    assert modelid in ['evenindivtransit', 'oddindivtransit']

    print(42*'#')
    print(modelid)
    print(42*'#')

    if not os.path.exists(PLOTDIR):
        os.mkdir(PLOTDIR)

    pklpath = os.path.join(
        os.path.expanduser('~'), 'local', 'timmy',
        f'{REALID}_model_{modelid}_{datestr}.pkl'
    )
    np.random.seed(42)

    # get tess data
    provenance = 'spoc' # could be "cdips"
    yval = 'PDCSAP_FLUX' # could be SAP_FLUX 
    x_obs, y_obs, y_err = get_clean_tessphot(provenance, yval, binsize=None,
                                             maskflares=1)

    s = np.isfinite(y_obs) & np.isfinite(x_obs) & np.isfinite(y_err)
    x_obs, y_obs, y_err = x_obs[s], y_obs[s], y_err[s]
    if cut_tess:
        if 'even' in modelid:
            onlyeven = True
            onlyodd = False
        elif 'odd' in modelid:
            onlyeven = False
            onlyodd = True
        else:
            raise NotImplementedError

        x_obs, y_obs, y_err = _subset_cut(
            x_obs, y_obs, y_err, n=3.5, onlyeven=onlyeven, onlyodd=onlyodd
        )

    ngroups, groupinds = find_lc_timegroups(x_obs, mingap=4.0)
    assert ngroups in [2,3]

    datasets = OrderedDict()
    for ix, g in enumerate(groupinds):
        tess_texp = np.nanmedian(np.diff(x_obs[g]))
        datasets[f'tess_{ix}'] = [x_obs[g], y_obs[g], y_err[g], tess_texp]

    # see /doc/20200805_ephemeris_counting.txt
    if 'even' in modelid:
        datestrs = ['20200401', '20200521']
    elif 'odd' in modelid:
        datestrs = ['20200426', '20200614']
    for ix, d in enumerate(datestrs):
        x_obs, y_obs, y_err = get_elsauce_phot(datestr=d)
        x_obs -= 2457000 # convert to BTJD
        elsauce_texp = np.nanmedian(np.diff(x_obs))
        datasets[f'elsauce_{ix}'] = [x_obs, y_obs, y_err, elsauce_texp]

    if 'even' in modelid:
        datestrs = ['20200623']
    elif 'odd' in modelid:
        datestrs = ['20200529', '20200614']
    for ix, d in enumerate(datestrs):
        x_obs, y_obs, y_err = get_astep_phot(datestr=d)
        x_obs += 2450000 # convert to BJD_TDB
        x_obs -= 2457000 # convert to BTJD
        astep_texp = np.nanmedian(np.diff(x_obs))
        datasets[f'astep_{ix}'] = [x_obs, y_obs, y_err, astep_texp]

    mp = ModelParser(modelid)

    prior_d = initialize_prior_d(mp.modelcomponents, datasets=datasets)

    m = ModelFitter(modelid, datasets, prior_d, plotdir=PLOTDIR,
                    pklpath=pklpath, overwrite=OVERWRITE, N_samples=N_samples,
                    target_accept=target_accept)

    print(pm.summary(m.trace, var_names=list(prior_d.keys())))
    summdf = pm.summary(m.trace, var_names=list(prior_d.keys()), round_to=10,
                        kind='stats', stat_funcs={'median':np.nanmedian},
                        extend=True)

    printparams = ['r_planet', 'b', 'log_r', 'log_r_sq']
    print(42*'-')
    print(modelid)
    for p in printparams:
        if p != 'log_r_sq':
            med = np.percentile(m.trace[p], 50)
            up = np.percentile(m.trace[p], 84)
            low = np.percentile(m.trace[p], 36)
        else:
            med = np.percentile(np.exp(m.trace['log_r'])**2, 50)
            up = np.percentile(np.exp(m.trace['log_r'])**2, 84)
            low = np.percentile(np.exp(m.trace['log_r'])**2, 36)
        print(f'{p} limit: {med:.6f} +{up-med:.6f} -{med-low:.6f}')
    print(42*'-')


    if make_threadsafe:
        pass

    else:
        if grounddepth:
            outpath = join(PLOTDIR, f'{REALID}_{modelid}_grounddepth.png')
            tp.plot_grounddepth(m, summdf, outpath, modelid=modelid,
                                showerror=0)

        if subsetcorner:
            outpath = join(PLOTDIR, f'{REALID}_{modelid}_subsetcorner.png')
            tp.plot_subsetcorner(m, outpath)

        if phaseplot:
            outpath = join(PLOTDIR, f'{REALID}_{modelid}_phaseplot.png')
            tp.plot_phasefold(m, summdf, outpath, modelid=modelid, inppt=1)

        if fitindiv:
            outpath = join(PLOTDIR, f'{REALID}_{modelid}_fitindiv.png')
            tp.plot_fitindiv(m, summdf, outpath, modelid=modelid)

        if cornerplot:
            outpath = join(PLOTDIR, f'{REALID}_{modelid}_cornerplot.png')
            tp.plot_cornerplot(prior_d, m, outpath)
Ejemplo n.º 10
0
def detrend_flux(time,
                 flux,
                 break_tolerance=0.5,
                 method='pspline',
                 cval=None,
                 window_length=None,
                 edge_cutoff=None):
    """
    Apply the wotan flatten function. Implemented methods include pspline,
    biweight, and median.

    Args:
        time, flux (np.ndarray): array of times and fluxes.

        break_tolerance (float): maximum time past which light curve is split
        into timegroups, each of which is detrended individually.

        method (str): 'pspline', 'biweight', 'median'.

        cval (float): the wotan 'biweight' tuning parameter.

        window_length (float): length of the window in days.

        edge_cutoff (float): how much of the edge to remove. Only works for
        'median' method.

    Returns:
        flat_flux, trend_flux (np.ndarray): flattened array, and the trend
        vector that was divided out.

    See also:
        https://wotan.readthedocs.io/en/latest/
    """

    # Initial pre-processing: verify that under break_tolerance, time and flux
    # do not have any sections with <=6 points. Spline detrending routines do
    # not like fitting lines.
    N_groups, group_inds = find_lc_timegroups(time, mingap=break_tolerance)
    SECTION_CUTOFF = 6
    for g in group_inds:
        if len(time[g]) <= SECTION_CUTOFF:
            time[g], flux[g] = np.nan, np.nan

    try:
        if method == 'pspline':

            # Detrend each time segment individually, to prevent overfitting
            # based on the `max_splines` parameter.

            flat_fluxs, trend_fluxs = [], []
            for g in group_inds:
                tgtime, tgflux = time[g], flux[g]

                t_min, t_max = np.min(tgtime), np.max(tgtime)
                t_baseline = t_max - t_min
                transit_timescale = 6 / 24.

                # e.g., for a 25 day segment, we want max_splines to be ~100,
                # i.e., 1 spline point every 6 hours.  this helps prevent
                # overfitting.
                max_splines = int(t_baseline / transit_timescale)

                # a 2-sigma cutoff is standard, but there's no obvious reason for
                # this being the case. generally, anything far from the median
                # shouldn't go into the fit.
                stdev_cut = 1.5

                edge_cutoff = 0

                # note: major API update in wotan v1.6
                # pspline(time, flux, edge_cutoff, max_splines, stdev_cut, return_nsplines, verbose)
                _trend_flux, _nsplines = pspline(tgtime, tgflux, edge_cutoff,
                                                 max_splines, stdev_cut, False,
                                                 False)

                _flat_flux = tgflux / _trend_flux

                flat_fluxs.append(_flat_flux)
                trend_fluxs.append(_trend_flux)

            flat_flux = np.hstack(flat_fluxs)
            trend_flux = np.hstack(trend_fluxs)

        elif method == 'biweight':
            flat_flux, trend_flux = flatten(time,
                                            flux,
                                            method='biweight',
                                            return_trend=True,
                                            break_tolerance=break_tolerance,
                                            window_length=window_length,
                                            cval=cval)

        elif method == 'median':
            flat_flux, trend_flux = flatten(time,
                                            flux,
                                            method='median',
                                            return_trend=True,
                                            break_tolerance=break_tolerance,
                                            window_length=window_length,
                                            edge_cutoff=edge_cutoff)

        elif method == 'none':
            flat_flux = flux
            trend_flux = None

        else:
            raise NotImplementedError

    except ValueError as e:
        msg = ('WRN! {}. Probably have a short segment. Trying to nan it out.'.
               format(repr(e)))
        print(msg)

        SECTION_CUTOFF = min([len(time[g]) for g in group_inds])
        for g in group_inds:
            if len(time[g]) <= SECTION_CUTOFF:
                time[g], flux[g] = np.nan, np.nan

        # NOTE: code duplication here
        if method == 'pspline':
            # matched detrending to do_initial_period_finding
            flat_flux, trend_flux = flatten(time,
                                            flux,
                                            method='pspline',
                                            return_trend=True,
                                            break_tolerance=break_tolerance,
                                            robust=True)
        elif method == 'biweight':
            # another option:
            flat_flux, trend_flux = flatten(time,
                                            flux,
                                            method='biweight',
                                            return_trend=True,
                                            break_tolerance=break_tolerance,
                                            window_length=window_length,
                                            cval=cval)
        else:
            raise NotImplementedError

    return flat_flux, trend_flux
Ejemplo n.º 11
0
def measure_centroid(t0, per, dur, sector, sourceid, c_obj, outdir):
    """
    Quoting Kostov+2019, who I followed:

    '''
    1) create the mean in-transit and out-of-transit images for each transit
    (ignoring cadences with non-zero quality flags), where the latter are based
    on the same number of exposure cadences as the former, split evenly before
    and after the transit;

    2) calculate the overall mean in-transit and out-of-transit images by
    averaging over all transits;

    3) subtract the overall mean out-of-transit image from the overall
    in-transit image to produce the overall mean difference image; and

    4) measure the center-of-light for each difference
    and out-of-transit image by calculating the corresponding x- and y-moments
    of the image. The measured photocenters for the three planet candidates are
    shown in Figures 9, 10, and 11, and listed in Table 3. We detect no
    significant photocenter shifts between the respective difference images and
    out-of-transit images for any of the planet candidates, which confirms that
    the targets star is the source of the transits.
    '''

    args:

        t0,per,dur : float, days. Epoch, period, duration.  Used to isolate
        transit windows.

        sector (int)

        sourceid (int)

        c_obj (SkyCoord): location of target star

    returns:

        outdict = {
            'm_oot_flux':m_oot_flux, # mean OOT image
            'm_oot_flux_err':m_oot_flux_err, # mean OOT image uncert
            'm_intra_flux':m_intra_flux, # mean in transit image
            'm_intra_flux_err':m_intra_flux_err,  # mean in transit image uncert
            'm_oot_minus_intra_flux':m_oot_minus_intra_flux, # mean OOT - mean intra
            'm_oot_minus_intra_flux_err':m_oot_minus_intra_flux_err,
            'm_oot_minus_intra_snr':m_oot_minus_intra_snr,
            'ctds_intra':ctds_intra, # centroids of all transits
            'ctds_oot':ctds_oot, # centroids of all ootransits
            'ctds_oot_minus_intra':ctds_oot_minus_intra, # centroids of diff
            'm_ctd_intra':m_ctd_intra, # centroid of mean intransit image
            'm_ctd_oot':m_ctd_oot,
            'intra_imgs_flux':intra_imgs_flux,
            'oot_imgs_flux':oot_imgs_flux,
            'intra_imgs_flux_err':intra_imgs_flux_err,
            'oot_imgs_flux_err':oot_imgs_flux_err
        }
    """

    print('beginning tesscut for {}'.format(repr(c_obj)))
    try:
        cuthdul = Tesscut.get_cutouts(c_obj, size=10, sector=sector)
    except (requests.exceptions.HTTPError,
            requests.exceptions.ConnectionError) as e:
        print('got {}, try again'.format(repr(e)))
        pytime.sleep(30)
        try:
            cuthdul = Tesscut.get_cutouts(c_obj, size=10, sector=sector)
        except (requests.exceptions.HTTPError,
                requests.exceptions.ConnectionError) as e:

            print('ERR! sourceid {} FAILED TO GET TESSCUTOUT'.format(sourceid))
            return None

    if len(cuthdul) != 1:
        print('ERR! sourceid {} GOT {} CUTOUTS'.format(sourceid, len(cuthdul)))
        return None
    else:
        cuthdul = cuthdul[0]
    data, data_hdr = cuthdul[1].data, cuthdul[1].header
    cutout_wcs = wcs.WCS(cuthdul[2].header)

    # flux and flux_err are image cubes of (time x spatial x spatial)
    quality = data['QUALITY']

    flux = data['FLUX']
    flux_err = data['FLUX_ERR']
    time = data['TIME']  # in TJD
    time += 2457000  # now in BJD

    time = time[quality == 0]
    flux = flux[quality == 0]
    flux_err = flux_err[quality == 0]

    intra = transit_mask(time, per, dur, t0)

    mingap = per / 2
    ngroups, groups = lcmath.find_lc_timegroups(time[intra], mingap=mingap)

    oot_times = time[~intra]

    # make mean in-transit and out-of-transit images & uncertainty maps for
    # each transit
    intra_imgs_flux, oot_imgs_flux = [], []
    intra_imgs_flux_err, oot_imgs_flux_err = [], []
    for group in groups:

        thistra_intra_time = time[intra][group]
        thistra_intra_flux = flux[intra][group]
        thistra_intra_flux_err = flux_err[intra][group]

        thistra_oot_time = _get_desired_oot_times(time, thistra_intra_time)
        thistra_oot_flux = flux[np.in1d(time, thistra_oot_time)]
        thistra_oot_flux_err = flux_err[np.in1d(time, thistra_oot_time)]

        intra_imgs_flux.append(np.mean(thistra_intra_flux, axis=0))
        intra_imgs_flux_err.append(np.mean(thistra_intra_flux_err, axis=0))
        oot_imgs_flux.append(np.mean(thistra_oot_flux, axis=0))
        oot_imgs_flux_err.append(np.mean(thistra_oot_flux_err, axis=0))

    # shapes: (n_transits, spatial, spatial)
    intra_imgs_flux = nparr(intra_imgs_flux)
    intra_imgs_flux_err = nparr(intra_imgs_flux_err)
    oot_imgs_flux = nparr(oot_imgs_flux)
    oot_imgs_flux_err = nparr(oot_imgs_flux_err)

    # average over transits, to get mean in-transit and out-of-transit images.
    m_intra_flux = np.mean(intra_imgs_flux, axis=0)
    m_intra_flux_err = np.mean(intra_imgs_flux_err, axis=0)
    m_oot_flux = np.mean(oot_imgs_flux, axis=0)
    m_oot_flux_err = np.mean(oot_imgs_flux_err, axis=0)

    # compute x and y centroid values for mean image
    m_ctd_intra = compute_centroid_from_first_moment(m_intra_flux)
    m_ctd_oot = compute_centroid_from_first_moment(m_oot_flux)
    ctd_m_oot_minus_m_intra = compute_centroid_from_first_moment(m_oot_flux -
                                                                 m_intra_flux)

    # compute x and y centroid values for each transit
    ctds_intra = nparr([
        compute_centroid_from_first_moment(intra_imgs_flux[ix, :, :])
        for ix in range(intra_imgs_flux.shape[0])
    ])
    ctds_oot = nparr([
        compute_centroid_from_first_moment(oot_imgs_flux[ix, :, :])
        for ix in range(oot_imgs_flux.shape[0])
    ])
    ctds_oot_minus_intra = nparr([
        compute_centroid_from_first_moment(oot_imgs_flux[ix, :, :] -
                                           intra_imgs_flux[ix, :, :])
        for ix in range(oot_imgs_flux.shape[0])
    ])

    # make OOT - intra image. NOTE: the uncertainty map might be a bit wrong,
    # b/c you should get a sqrt(N) on the flux in the mean image. I think.
    m_oot_minus_intra_flux = m_oot_flux - m_intra_flux
    m_oot_minus_intra_flux_err = np.sqrt(m_oot_flux_err**2 + m_intra_flux**2)

    m_oot_minus_intra_snr = m_oot_minus_intra_flux / m_oot_minus_intra_flux_err

    # calculate the centroid shift amplitude, deltaC = C_oot - C_intra
    delta_ctd_px = np.sqrt((m_ctd_oot[0] - m_ctd_intra[0])**2 +
                           (m_ctd_oot[1] - m_ctd_intra[1])**2)
    delta_ctd_arcsec = delta_ctd_px * 21  # 21arcsec/px

    # error in centroid shift amplitude: assume it is roughly the scatter in
    # OOT centroids
    delta_ctd_err_px = np.sqrt(
        np.std(ctds_oot, axis=0)[0]**2 + np.std(ctds_oot, axis=0)[1]**2)
    delta_ctd_err_arcsec = delta_ctd_err_px * 21

    outdict = {
        'cutout_wcs': cutout_wcs,
        'm_oot_flux': m_oot_flux,  # mean OOT image
        'm_oot_flux_err': m_oot_flux_err,  # mean OOT image uncert
        'm_intra_flux': m_intra_flux,  # mean in transit image
        'm_intra_flux_err': m_intra_flux_err,  # mean in transit image uncert
        'm_oot_minus_intra_flux':
        m_oot_minus_intra_flux,  # mean OOT - mean intra
        'm_oot_minus_intra_flux_err': m_oot_minus_intra_flux_err,
        'm_oot_minus_intra_snr': m_oot_minus_intra_snr,
        'ctds_intra': ctds_intra,  # centroids of all transits
        'ctds_oot': ctds_oot,  # centroids of all ootransits
        'ctds_oot_minus_intra': ctds_oot_minus_intra,
        'm_ctd_intra': m_ctd_intra,  # centroid of mean intransit image
        'm_ctd_oot': m_ctd_oot,
        'ctd_m_oot_minus_m_intra': ctd_m_oot_minus_m_intra,
        'intra_imgs_flux': intra_imgs_flux,
        'oot_imgs_flux': oot_imgs_flux,
        'intra_imgs_flux_err': intra_imgs_flux_err,
        'oot_imgs_flux_err': oot_imgs_flux_err,
        'delta_ctd_arcsec': delta_ctd_arcsec,
        'delta_ctd_err_arcsec': delta_ctd_err_arcsec,
        'delta_ctd_sigma': delta_ctd_arcsec / delta_ctd_err_arcsec
    }

    outpath = os.path.join(outdir, '{}_ctds.pkl'.format(str(sourceid)))
    with open(outpath, 'wb') as f:
        pickle.dump(outdict, f)
    print('made {}'.format(outpath))

    return outdict
Ejemplo n.º 12
0
def calculate_linear_model_mag(y,
                               basisvecs,
                               n_components,
                               method='LinearRegression',
                               verbose=False):
    """
    Given a set of basis vectors in a linear model for a target light curve
    (y), calculate the coefficients and apply the linear model prediction.

    `basisvecs` needs to have shape: N_templates x N_times

    model:
    y(w, x) = w_0 + w_1 x_1 + ... + w_p x_p.

    where X is matrix of (n_samples, n_features), so each template is a
    "sample", and each time is a "feature". Analogous to e.g., doing PCA to
    reconstruct a spectrum, and you want each wavelength bin to be a feature.

    Allow methods:

        'LinearRegression': ordinary least squares.

        'RidgeCV': ridge regression, which is ordinary least squares, plus an
        L2 norm with a regularization coefficient. The regularization is solved
        for via a cross-validation grid search.

    Returns:
        out_mag, n_comp: the model light curve (supposedly with instrumental
        systematic removed), and the number of PCA components used during the
        regression.
    """

    if method == 'LinearRegression':
        reg = LinearRegression(fit_intercept=True)
    elif method == 'RidgeCV':
        reg = RidgeCV(alphas=np.logspace(-10, 10, 210), fit_intercept=True)

    y2 = y
    y2[y2 == np.inf] = np.nan
    y2[y2 == -np.inf] = np.nan

    if np.all(pd.isnull(y2)):
        # if all nan, job is easy
        out_mag = y
        n_comp = 'nan'

    elif np.any(pd.isnull(y2)):

        #
        # if some nan in target light curve, tricky. strategy: impose the
        # same nans onto the eigenvectors. then fit without nans. then put
        # nans back in.
        #
        mean_mag = np.nanmean(y2)
        std_mag = np.nanstd(y2)
        norm_mag = (y2[~pd.isnull(y2)] - mean_mag) / std_mag

        _X = basisvecs[:n_components, :]

        _X = _X[:, ~pd.isnull(y2)]

        reg.fit(_X.T, norm_mag)

        # see note below in the else statement about how the prediction step
        # works.
        model_mag = mean_mag + std_mag * reg.predict(_X.T)

        #
        # now insert nans where they were in original target light curve.
        # typically nans occur in groups. insert by groups. this is a
        # tricky procedure, so test after to ensure nan indice in the model
        # and target light curves are the same.
        #
        naninds = np.argwhere(pd.isnull(y2)).flatten()

        ngroups, groups = find_lc_timegroups(naninds, mingap=1)

        for group in groups:

            thisgroupnaninds = naninds[group]

            model_mag = np.insert(model_mag, np.min(thisgroupnaninds),
                                  [np.nan] * len(thisgroupnaninds))

        np.testing.assert_(model_mag.shape == y.shape)
        np.testing.assert_(
            (len((model_mag - y2)[pd.isnull(model_mag - y2)]) == len(
                y[pd.isnull(y2)])),
            'got different nan indices in model mag than target mag')

        #
        # save result as IRM mag - PCA model mag + mean IRM mag
        #
        out_mag = y - model_mag + mean_mag
        n_comp = n_components

    else:
        # otherwise, just directly fit the principal components
        mean_mag = np.nanmean(y)
        std_mag = np.nanstd(y)
        norm_mag = (y - mean_mag) / std_mag

        _X = basisvecs[:n_components, :]

        reg.fit(_X.T, norm_mag)

        # NOTE: in ordinary least squares, the line below is the same as
        # `model_mag = reg.intercept_ + (reg.coef_ @ _X)`. But the sklearn
        # interface is sick, and using reg.predict is easier than doing the
        # extra terms for other regressors.
        model_mag = mean_mag + std_mag * reg.predict(_X.T)

        out_mag = y - model_mag + mean_mag
        n_comp = n_components

    if verbose:
        if method == 'RidgeCV':
            try:
                print(f'RidgeCV alpha: {reg.alpha_:.2e}')
            except AttributeError:
                pass

    return out_mag, n_comp
Ejemplo n.º 13
0
def generate_verification_page(lcd, ls, freq, power, cutoutpaths, c_obj,
                               outvppath, outd, show_binned=True):
    """
    Make the verification page, which consists of:

    top row: entire light curve (with horiz bar showing rotation period)

    bottom row:
        lomb scargle periodogram  |  phased light curve  |  image w/ aperture

    ----------
    args:

        lcd (dict): has the light curve, aperture positions, some lomb
        scargle results.

        ls: LombScargle instance with everything passed.

        cutoutpaths (list): FFI cutout FITS paths.

        c_obj (SkyCoord): astropy sky coordinate of the target

        outvppath (str): path to save verification page to
    """
    cutout_wcs = lcd['cutout_wcss'][0]

    mpl.rcParams['xtick.direction'] = 'in'
    mpl.rcParams['ytick.direction'] = 'in'

    plt.close('all')

    fig = plt.figure(figsize=(12,12))

    #ax0 = plt.subplot2grid((3, 3), (0, 0), colspan=3)
    #ax1 = plt.subplot2grid((3, 3), (1, 0), colspan=3)
    #ax2 = plt.subplot2grid((3, 3), (2, 0))
    #ax3 = plt.subplot2grid((3, 3), (2, 1))
    #ax4 = plt.subplot2grid((3, 3), (2, 2), projection=cutout_wcs)

    ax0 = plt.subplot2grid((3, 3), (1, 0), colspan=3)
    ax1 = plt.subplot2grid((3, 3), (2, 0), colspan=3)
    ax2 = plt.subplot2grid((3, 3), (0, 0))
    ax3 = plt.subplot2grid((3, 3), (0, 1))
    ax4 = plt.subplot2grid((3, 3), (0, 2), projection=cutout_wcs)

    #
    # row 0: entire light curve, pre-detrending (with horiz bar showing
    # rotation period). plot model LC too.
    #
    try:
        ax0.scatter(lcd['predetrending_time'], lcd['predetrending_rel_flux'],
                    c='k', alpha=1.0, zorder=3, s=10, rasterized=True,
                    linewidths=0)
    except KeyError as e:
        print('ERR! {}\nReturning.'.format(e))
        return


    try:
        model_flux = nparr(lcd['predetrending_rel_flux']/lcd['rel_flux'])
    except ValueError:
        model_flux = 0

    if isinstance(model_flux, np.ndarray):
        ngroups, groups = find_lc_timegroups(lcd['predetrending_time'], mingap=0.5)
        for group in groups:
            ax0.plot(lcd['predetrending_time'][group], model_flux[group], c='C0',
                     alpha=1.0, zorder=2, rasterized=True, lw=2)

    # add the bar showing the derived period
    ymax = np.percentile(lcd['predetrending_rel_flux'], 95)
    ymin = np.percentile(lcd['predetrending_rel_flux'], 5)
    ydiff = 1.15*(ymax-ymin)

    epoch = np.nanmin(lcd['predetrending_time']) + lcd['ls_period']
    ax0.plot([epoch, epoch+lcd['ls_period']], [ymax, ymax], color='red', lw=2,
             zorder=4)

    ax0.set_ylim((ymin-ydiff,ymax+ydiff))

    #ax0.set_xlabel('Time [BJD$_{\mathrm{TDB}}$]')
    ax0.set_xticklabels('')
    ax0.set_ylabel('Raw flux')

    name = outd['name']
    group_id = outd['group_id']
    if name=='nan':
        nstr = 'Group {}'.format(group_id)
    else:
        nstr = '{}'.format(name)


    if not np.isfinite(outd['teff']):
        outd['teff'] = 0

    ax0.text(0.98, 0.97,
        'Teff={:d}K. {}'.format(int(outd['teff']), nstr),
             ha='right', va='top', fontsize='large', zorder=2,
             transform=ax0.transAxes
    )

    #
    # row 1: entire light curve (with horiz bar showing rotation period)
    #
    ax1.scatter(lcd['time'], lcd['rel_flux'], c='k', alpha=1.0, zorder=2, s=10,
                rasterized=True, linewidths=0)

    # add the bar showing the derived period
    ymax = np.percentile(lcd['rel_flux'], 95)
    ymin = np.percentile(lcd['rel_flux'], 5)
    ydiff = 1.15*(ymax-ymin)

    epoch = np.nanmin(lcd['time']) + lcd['ls_period']
    ax1.plot([epoch, epoch+lcd['ls_period']], [ymax, ymax], color='red', lw=2)

    ax1.set_ylim((ymin-ydiff,ymax+ydiff))

    ax1.set_xlabel('Time [BJD$_{\mathrm{TDB}}$]')
    ax1.set_ylabel('Detrended flux')

    #
    # row 2, col 0: lomb scargle periodogram
    #
    ax2.plot(1/freq, power, c='k')
    ax2.set_xscale('log')
    ax2.text(0.03, 0.97, 'FAP={:.1e}\nP={:.1f}d'.format(
        lcd['ls_fap'], lcd['ls_period']), ha='left', va='top',
        fontsize='large', zorder=2, transform=ax2.transAxes
    )
    ax2.set_xlabel('Period [day]', labelpad=-1)
    ax2.set_ylabel('LS power')

    #
    # row 2, col 1: phased light curve 
    #
    phzd = phase_magseries(lcd['time'], lcd['rel_flux'], lcd['ls_period'],
                           lcd['time'][np.argmin(lcd['rel_flux'])], wrap=False,
                           sort=True)

    ax3.scatter(phzd['phase'], phzd['mags'], c='k', rasterized=True, s=10,
                linewidths=0, zorder=1)

    if show_binned:
        try:
            binphasedlc = phase_bin_magseries(phzd['phase'], phzd['mags'],
                                              binsize=1e-2, minbinelems=5)
            binplotphase = binphasedlc['binnedphases']
            binplotmags = binphasedlc['binnedmags']

            ax3.scatter(binplotphase, binplotmags, s=10, c='darkorange',
                        linewidths=0, zorder=3, rasterized=True)
        except TypeError as e:
            print(e)
            pass

    xlim = ax3.get_xlim()
    ax3.hlines(1.0, xlim[0], xlim[1], colors='gray', linestyles='dotted',
               zorder=2)
    ax3.set_xlim(xlim)

    ymax = np.percentile(lcd['rel_flux'], 95)
    ymin = np.percentile(lcd['rel_flux'], 5)
    ydiff = 1.15*(ymax-ymin)
    ax3.set_ylim((ymin-ydiff,ymax+ydiff))

    ax3.set_xlabel('Phase', labelpad=-1)
    ax3.set_ylabel('Flux', labelpad=-0.5)

    #
    # row2, col2: image w/ aperture. put on the nbhr stars as dots too, to
    # ensure the wcs isn't wonky!
    #

    # acquire neighbor stars.
    radius = 2.0*u.arcminute

    nbhr_stars = Catalogs.query_region(
        "{} {}".format(float(c_obj.ra.value), float(c_obj.dec.value)),
        catalog="TIC",
        radius=radius
    )

    try:
        Tmag_cutoff = 15
        px,py = cutout_wcs.all_world2pix(
            nbhr_stars[nbhr_stars['Tmag'] < Tmag_cutoff]['ra'],
            nbhr_stars[nbhr_stars['Tmag'] < Tmag_cutoff]['dec'],
            0
        )
    except Exception as e:
        print('ERR! wcs all_world2pix got {}'.format(repr(e)))
        return

    tmags = nbhr_stars[nbhr_stars['Tmag'] < Tmag_cutoff]['Tmag']

    sel = (px > 0) & (px < 19) & (py > 0) & (py < 19)
    px,py = px[sel], py[sel]
    tmags = tmags[sel]

    ra, dec = float(c_obj.ra.value), float(c_obj.dec.value)
    target_x, target_y = cutout_wcs.all_world2pix(ra,dec,0)

    #
    # finally make it
    #

    img = lcd['median_imgs'][0]

    # some images come out as nans.
    if np.all(np.isnan(img)):
        img = np.ones_like(img)

    interval = vis.PercentileInterval(99.9)
    vmin,vmax = interval.get_limits(img)
    norm = vis.ImageNormalize(
        vmin=vmin, vmax=vmax, stretch=vis.LogStretch(1000))

    cset = ax4.imshow(img, cmap='YlGnBu_r', origin='lower', zorder=1,
                      norm=norm)

    ax4.scatter(px, py, marker='x', c='r', s=5, rasterized=True, zorder=2,
                linewidths=1)
    ax4.plot(target_x, target_y, mew=0.5, zorder=5, markerfacecolor='yellow',
             markersize=7, marker='*', color='k', lw=0)

    #ax4.coords.grid(True, color='white', ls='dotted', lw=1)
    lon = ax4.coords['ra']
    lat = ax4.coords['dec']

    lon.set_ticks(spacing=1*u.arcminute)
    lat.set_ticks(spacing=1*u.arcminute)

    lon.set_ticklabel(exclude_overlapping=True)
    lat.set_ticklabel(exclude_overlapping=True)

    ax4.coords.grid(True, color='white', alpha=0.3, lw=0.3, ls='dotted')

    #cb0 = fig.colorbar(cset, ax=ax4, extend='neither', fraction=0.046, pad=0.04)

    # overplot aperture
    radius_px = 3
    circle = plt.Circle((target_x, target_y), radius_px,
                         color='C1', fill=False, zorder=5)
    ax4.add_artist(circle)

    #
    # cleanup
    # 
    for ax in [ax0,ax1,ax2,ax3,ax4]:
        ax.get_yaxis().set_tick_params(which='both', direction='in',
                                       labelsize='small', top=True, right=True)
        ax.get_xaxis().set_tick_params(which='both', direction='in',
                                       labelsize='small', top=True, right=True)

    fig.tight_layout(w_pad=0.5, h_pad=0)

    #
    # save
    #
    fig.savefig(outvppath, dpi=300, bbox_inches='tight')
    print('made {}'.format(outvppath))
Ejemplo n.º 14
0
def plot_mag_vs_EPD_parameters_four_stars(magtype='IRM1',
                                          get_norbits=1,
                                          expected_norbits=2,
                                          savdir=None):
    """
    for 4 chosen stars, do columns of their flux vs EPD parameters (x,y,T,
    etc).

    two left stars are selected to be representative of center of field.
    two right stars are selected to be representative of corner of field.
    """

    # get the lcpaths and data list for the selected stars

    # brightish, center
    # IRM1_vs_EPD_parameters_4979631232408118784_llc_norbit1.png
    # IRM1_vs_EPD_parameters_4979603607178484608_llc_norbit1.png
    # ditto, corner
    # IRM1_vs_EPD_parameters_4955130505568582528_llc_norbit1.png
    # IRM1_vs_EPD_parameters_4955138064711041280_llc_norbit1.png

    centerdir = '../results/projid1088_cam2_ccd2_lcs/center_lcs/'
    cornerdir = '../results/projid1088_cam2_ccd2_lcs/corner_lcs/'
    lcpaths = [
        os.path.join(centerdir, '4979631232408118784_llc.fits'),
        os.path.join(centerdir, '4979603607178484608_llc.fits'),
        os.path.join(cornerdir, '4955130505568582528_llc.fits'),
        os.path.join(cornerdir, '4955138064711041280_llc.fits'),
    ]

    lcdatalist = []
    for lcfile in lcpaths:
        hdulist = fits.open(lcfile)
        lcdatalist.append(hdulist[1].data)
        hdulist.close()

    xcols = ['TMID_BJD', 'CCDTEMP', 'XIC', 'YIC', 'FSV', 'FDV', 'FKV']
    xkeys = ['tmidbjd', 'T', 'x', 'y', 's', 'd', 'k']
    desiredx = xkeys

    if get_norbits not in [1]:
        raise AssertionError('you only want to get 1 orbit')

    plt.close('all')
    nrows = len(xcols)
    ncols = len(lcpaths)
    fig, axs = plt.subplots(nrows=nrows,
                            ncols=ncols,
                            figsize=(3 * ncols, 2 * nrows))

    savname = ('{}_vs_EPD_parameters_fourstars_norbit{}.png'.format(
        magtype, get_norbits))
    savpath = os.path.join(savdir, savname)

    if os.path.exists(savpath):
        print('found {}, quitting'.format(savpath))
        return 0

    # iterate over columns
    for lcpath, lc, colaxs in zip(lcpaths, lcdatalist, axs.T):

        # collect the data for the first orbit in this sector
        times = lc['TMID_BJD']

        orbitgap = 1.  # days
        found_norbits, groups = lcmath.find_lc_timegroups(times,
                                                          mingap=orbitgap)

        if found_norbits != expected_norbits:
            outmsg = ('assuming given two orbits. {} orbits. Time {}'.format(
                norbits, repr(times)))
            raise AssertionError(outmsg)

        if get_norbits == 1:
            group = groups[0]
        else:
            raise NotImplementedError

        # collect the data for the first orbit in this sector
        d = {}
        for xcol, k in zip(xcols, xkeys):
            d[k] = lc[xcol][group]

        if np.all(pd.isnull(lc[magtype])):
            print('mags are all NaN for {}, continue'.format(savname))
            continue

        ##########################################

        # begin plotting.
        yval = lc[magtype][group]
        yoffset = np.round(np.median(yval), decimals=2)
        yval -= yoffset
        yval *= 1e3

        for xstr, ax in zip(desiredx, colaxs):

            if xstr in ['s', 'd', 'k', 'x', 'y', 'T', 'tmidbjd']:
                xval = d[xstr]

            if xstr == 'tmidbjd':
                xval -= np.median(xval)
            if xstr in ['x', 'y']:
                xoffset = int(np.median(xval))
                xval -= xoffset

            if xstr == 'tmidbjd':
                ax.scatter(xval,
                           yval,
                           rasterized=True,
                           alpha=0.8,
                           zorder=3,
                           c='k',
                           lw=0,
                           s=5)
            else:
                ax.scatter(xval,
                           yval,
                           rasterized=True,
                           alpha=0.8,
                           zorder=3,
                           c='k',
                           lw=0,
                           s=4)

            if xstr == 'tmidbjd':
                xstr = 't - t$_0$ [d]'
            if xstr in ['x', 'y']:
                xstr = xstr + ' - {}'.format(xoffset)
            ax.text(0.98,
                    0.02,
                    xstr,
                    fontsize='x-small',
                    ha='right',
                    va='bottom',
                    transform=ax.transAxes)
            ax.set_ylabel('{} - {} [mmag]'.format(magtype, yoffset),
                          fontsize='x-small')

            ax.get_yaxis().set_tick_params(which='both', direction='in')
            ax.get_xaxis().set_tick_params(which='both', direction='in')

            ylim = ax.get_ylim()
            ax.set_ylim((max(ylim), min(ylim)))

    fig.tight_layout(h_pad=0.15)
    fig.savefig(savpath, dpi=400, bbox_inches='tight')

    print('made {}'.format(savpath))
Ejemplo n.º 15
0
def main(modelid):

    make_threadsafe = 0
    cut_tess = 1

    fitindiv = 1
    phaseplot = 1
    grounddepth = 1
    cornerplot = 1
    subsetcorner = 1

    N_samples = 30000  # took 2h 20m, but Rhat=1.0 for all
    # N_samples = 2000 # took 16m, 14s. but Rhat=1.01 for b, rp/rs, + a few a1/a2s
    target_accept = 0.9

    OVERWRITE = 1
    REALID = 'TOI_837'

    PLOTDIR = os.path.join(RESULTSDIR,
                           '{}_{}_phot_results'.format(REALID, modelid))
    if not os.path.exists(PLOTDIR):
        os.mkdir(PLOTDIR)
    datestr = '20200711'
    PLOTDIR = os.path.join(PLOTDIR, datestr)

    ##########################################

    assert modelid in ['allindivtransit']

    print(42 * '#')
    print(modelid)
    print(42 * '#')

    if not os.path.exists(PLOTDIR):
        os.mkdir(PLOTDIR)

    pklpath = os.path.join(os.path.expanduser('~'), 'local', 'timmy',
                           f'{REALID}_model_{modelid}_{datestr}.pkl')
    np.random.seed(42)

    # get tess data
    provenance = 'spoc'  # could be "cdips"
    yval = 'PDCSAP_FLUX'  # could be SAP_FLUX
    x_obs, y_obs, y_err = get_clean_tessphot(provenance,
                                             yval,
                                             binsize=None,
                                             maskflares=1)
    #y_flat, y_trend = detrend_tessphot(x_obs, y_obs, y_err)
    s = np.isfinite(y_obs) & np.isfinite(x_obs) & np.isfinite(y_err)
    x_obs, y_obs, y_err = x_obs[s], y_obs[s], y_err[s]
    if cut_tess:
        x_obs, y_obs, y_err = _subset_cut(x_obs, y_obs, y_err, n=3.5)

    ngroups, groupinds = find_lc_timegroups(x_obs, mingap=4.0)
    assert ngroups == 5

    datasets = OrderedDict()
    for ix, g in enumerate(groupinds):
        tess_texp = np.nanmedian(np.diff(x_obs[g]))
        datasets[f'tess_{ix}'] = [x_obs[g], y_obs[g], y_err[g], tess_texp]

    datestrs = ['20200401', '20200426', '20200521', '20200614']
    for ix, d in enumerate(datestrs):
        x_obs, y_obs, y_err = get_elsauce_phot(datestr=d)
        x_obs -= 2457000  # convert to BTJD
        elsauce_texp = np.nanmedian(np.diff(x_obs))
        datasets[f'elsauce_{ix}'] = [x_obs, y_obs, y_err, elsauce_texp]

    datestrs = ['20200529', '20200614', '20200623']
    for ix, d in enumerate(datestrs):
        x_obs, y_obs, y_err = get_astep_phot(datestr=d)
        x_obs += 2450000  # convert to BJD_TDB
        x_obs -= 2457000  # convert to BTJD
        astep_texp = np.nanmedian(np.diff(x_obs))
        datasets[f'astep_{ix}'] = [x_obs, y_obs, y_err, astep_texp]

    mp = ModelParser(modelid)

    prior_d = initialize_prior_d(mp.modelcomponents, datasets=datasets)

    m = ModelFitter(modelid,
                    datasets,
                    prior_d,
                    plotdir=PLOTDIR,
                    pklpath=pklpath,
                    overwrite=OVERWRITE,
                    N_samples=N_samples,
                    target_accept=target_accept)

    print(pm.summary(m.trace, var_names=list(prior_d.keys())))
    summdf = pm.summary(m.trace,
                        var_names=list(prior_d.keys()),
                        round_to=10,
                        kind='stats',
                        stat_funcs={'median': np.nanmedian},
                        extend=True)
    rp_limit = np.percentile(m.trace.r_planet, 1 - 0.9973)
    print(42 * '-')
    print(f'Rp limit: {rp_limit:.3f} Rjup')
    print(42 * '-')

    if make_threadsafe:
        pass

    else:
        if grounddepth:
            outpath = join(PLOTDIR, f'{REALID}_{modelid}_grounddepth.png')
            tp.plot_grounddepth(m,
                                summdf,
                                outpath,
                                modelid=modelid,
                                showerror=0)

        if subsetcorner:
            outpath = join(PLOTDIR, f'{REALID}_{modelid}_subsetcorner.png')
            tp.plot_subsetcorner(m, outpath)

        if phaseplot:
            outpath = join(PLOTDIR, f'{REALID}_{modelid}_phaseplot.png')
            tp.plot_phasefold(m, summdf, outpath, modelid=modelid, inppt=1)

        if fitindiv:
            outpath = join(PLOTDIR, f'{REALID}_{modelid}_fitindiv.png')
            tp.plot_fitindiv(m, summdf, outpath, modelid=modelid)

        if cornerplot:
            outpath = join(PLOTDIR, f'{REALID}_{modelid}_cornerplot.png')
            tp.plot_cornerplot(prior_d, m, outpath)
Ejemplo n.º 16
0
def main(modelid):

    make_threadsafe = 0
    cut_tess = 1

    fitindiv = 1
    phaseplot = 1
    cornerplot = 1
    subsetcorner = 1
    grounddepth = 1

    N_samples = 30000
    target_accept = 0.9

    OVERWRITE = 1
    REALID = 'TOI_837'

    PLOTDIR = os.path.join(RESULTSDIR,
                           '{}_{}_phot_results'.format(REALID, modelid))
    if not os.path.exists(PLOTDIR):
        os.mkdir(PLOTDIR)
    datestr = '20200711'
    PLOTDIR = os.path.join(PLOTDIR, datestr)

    ##########################################

    assert modelid in ['tessindivtransit']

    print(42 * '#')
    print(modelid)
    print(42 * '#')

    if not os.path.exists(PLOTDIR):
        os.mkdir(PLOTDIR)

    pklpath = os.path.join(os.path.expanduser('~'), 'local', 'timmy',
                           f'{REALID}_model_{modelid}_{datestr}.pkl')
    np.random.seed(42)

    # get tess data
    provenance = 'spoc'  # could be "cdips"
    yval = 'PDCSAP_FLUX'  # could be SAP_FLUX
    x_obs, y_obs, y_err = get_clean_tessphot(provenance,
                                             yval,
                                             binsize=None,
                                             maskflares=1)
    #y_flat, y_trend = detrend_tessphot(x_obs, y_obs, y_err)
    s = np.isfinite(y_obs) & np.isfinite(x_obs) & np.isfinite(y_err)
    x_obs, y_obs, y_err = x_obs[s], y_obs[s], y_err[s]
    if cut_tess:
        x_obs, y_obs, y_err = _subset_cut(x_obs, y_obs, y_err, n=3.5)

    ngroups, groupinds = find_lc_timegroups(x_obs, mingap=4.0)
    assert ngroups == 5

    datasets = OrderedDict()
    for ix, g in enumerate(groupinds):
        tess_texp = np.nanmedian(np.diff(x_obs[g]))
        datasets[f'tess_{ix}'] = [x_obs[g], y_obs[g], y_err[g], tess_texp]

    mp = ModelParser(modelid)

    prior_d = initialize_prior_d(mp.modelcomponents, datasets=datasets)

    m = ModelFitter(modelid,
                    datasets,
                    prior_d,
                    plotdir=PLOTDIR,
                    pklpath=pklpath,
                    overwrite=OVERWRITE,
                    N_samples=N_samples,
                    target_accept=target_accept)

    print(pm.summary(m.trace, var_names=list(prior_d.keys())))
    summdf = pm.summary(m.trace,
                        var_names=list(prior_d.keys()),
                        round_to=10,
                        kind='stats',
                        stat_funcs={'median': np.nanmedian},
                        extend=True)

    printparams = ['r_planet', 'b']
    print(42 * '-')
    for p in printparams:
        med = np.percentile(m.trace[p], 50)
        up = np.percentile(m.trace[p], 84)
        low = np.percentile(m.trace[p], 36)
        print(f'{p} limit: {med:.3f} +{up-med:.3f} -{med-low:.3f}')
    print(42 * '-')

    if make_threadsafe:
        pass

    else:
        # if grounddepth:
        #     outpath = join(PLOTDIR, f'{REALID}_{modelid}_grounddepth.png')
        #     tp.plot_grounddepth(m, summdf, outpath, modelid=modelid,
        #                         showerror=0)

        if subsetcorner:
            outpath = join(PLOTDIR, f'{REALID}_{modelid}_subsetcorner.png')
            tp.plot_subsetcorner(m, outpath)

        if phaseplot:
            outpath = join(PLOTDIR, f'{REALID}_{modelid}_phaseplot.png')
            tp.plot_phasefold(m, summdf, outpath, modelid=modelid, inppt=1)

        if fitindiv:
            outpath = join(PLOTDIR, f'{REALID}_{modelid}_fitindiv.png')
            tp.plot_fitindiv(m, summdf, outpath, modelid=modelid)

        if cornerplot:
            outpath = join(PLOTDIR, f'{REALID}_{modelid}_cornerplot.png')
            tp.plot_cornerplot(prior_d, m, outpath)
Ejemplo n.º 17
0
def main(modelid, datestr):

    assert modelid in ['allindivtransit', 'tessindivtransit']
    yval = 'PDCSAP_FLUX' # could be SAP_FLUX 

    OVERWRITE = 0
    REALID = 'TOI_837'
    provenance = 'spoc'
    PLOTDIR = os.path.join(
        RESULTSDIR, '{}_{}_phot_results'.format(REALID, modelid)
    )
    PLOTDIR = os.path.join(PLOTDIR, datestr)

    summarypath = os.path.join(
        PLOTDIR, 'posterior_table_raw_{}.csv'.format(modelid)
    )
    pklpath = os.path.join(
        os.path.expanduser('~'), 'local', 'timmy',
        '{}_model_{}_{}.pkl'.format(REALID, modelid, datestr)
    )
    np.random.seed(42)

    ########################################## 
    # get allindivtransit initialized
    ########################################## 
    provenance = 'spoc' # could be "cdips"
    yval = 'PDCSAP_FLUX' # could be SAP_FLUX 
    x_obs, y_obs, y_err = get_clean_tessphot(provenance, yval, binsize=None,
                                             maskflares=1)
    s = np.isfinite(y_obs) & np.isfinite(x_obs) & np.isfinite(y_err)
    x_obs, y_obs, y_err = x_obs[s], y_obs[s], y_err[s]
    cut_tess = 1
    if cut_tess:
        x_obs, y_obs, y_err = _subset_cut(x_obs, y_obs, y_err, n=3.5)

    ngroups, groupinds = find_lc_timegroups(x_obs, mingap=4.0)
    assert ngroups == 5

    datasets = OrderedDict()
    for ix, g in enumerate(groupinds):
        tess_texp = np.nanmedian(np.diff(x_obs[g]))
        datasets[f'tess_{ix}'] = [x_obs[g], y_obs[g], y_err[g], tess_texp]

    if modelid == 'allindivtransit':
        datestrs = ['20200401', '20200426', '20200521', '20200614']
        for ix, d in enumerate(datestrs):
            x_obs, y_obs, y_err = get_elsauce_phot(datestr=d)
            x_obs -= 2457000 # convert to BTJD
            elsauce_texp = np.nanmedian(np.diff(x_obs))
            datasets[f'elsauce_{ix}'] = [x_obs, y_obs, y_err, elsauce_texp]

        datestrs = ['20200529', '20200614', '20200623']
        for ix, d in enumerate(datestrs):
            x_obs, y_obs, y_err = get_astep_phot(datestr=d)
            x_obs += 2450000 # convert to BJD_TDB
            x_obs -= 2457000 # convert to BTJD
            astep_texp = np.nanmedian(np.diff(x_obs))
            datasets[f'astep_{ix}'] = [x_obs, y_obs, y_err, astep_texp]

    mp = ModelParser(modelid)

    prior_d = initialize_prior_d(mp.modelcomponents, datasets=datasets)
    ########################################## 
    # end intiialization
    ########################################## 

    if not os.path.exists(summarypath):

        m = ModelFitter(modelid, datasets, prior_d, plotdir=PLOTDIR,
                        pklpath=pklpath, overwrite=OVERWRITE)

        # stat_funcsdict = A list of functions or a dict of functions with
        # function names as keys used to calculate statistics. By default, the
        # mean, standard deviation, simulation standard error, and highest
        # posterior density intervals are included.
        stat_funcsdict = {
            'median': np.nanmedian
        }

        df = pm.summary(
            m.trace,
            round_to=10, kind='stats',
            stat_funcs=stat_funcsdict,
            extend=True
        )

        df.to_csv(summarypath, index=True)

    else:
        df = pd.read_csv(summarypath, index_col=0)

    fitted_params = [
        'period', 't0', 'log_r', 'b', 'u[0]', 'u[1]', 'r_star', 'logg_star'
    ]
    for i in range(5):
        fitted_params.append(f'tess_{i}_mean')
        fitted_params.append(f'tess_{i}_a1')
        fitted_params.append(f'tess_{i}_a2')
    if modelid == 'allindivtransit':
        for i in range(4):
            fitted_params.append(f'elsauce_{i}_mean')
            fitted_params.append(f'elsauce_{i}_a1')
            fitted_params.append(f'elsauce_{i}_a2')
        for i in range(3):
            fitted_params.append(f'astep_{i}_mean')
            fitted_params.append(f'astep_{i}_a1')
            fitted_params.append(f'astep_{i}_a2')

    n_fitted = len(fitted_params)

    derived_params = [
        'r', 'rho_star', 'r_planet', 'a_Rs', 'cosi', 'T_14', 'T_13'
    ]
    n_derived = len(derived_params)

    srows = []
    for f in fitted_params:
        srows.append(f)
    for d in derived_params:
        srows.append(d)

    df = df.loc[srows]

    cols = ['median', 'mean', 'sd', 'hpd_3%', 'hpd_97%']

    df = df[cols]

    print(df)

    from timmy.priors import (
        LOGG, LOGG_STDEV, RSTAR, RSTAR_STDEV
    )

    delta_u = 0.15
    pr = {
        'period': normal_str(
            mu=prior_d['period'], sd=1e-1, fmtstr='({:.4f}; {:.4f})'
        ),
        't0': normal_str(
            mu=prior_d['t0'], sd=1e-1, fmtstr='({:.6f}; {:.4f})'
        ),
        'log_r': uniform_str(
            lower=np.log(1e-2), upper=np.log(1), fmtstr='({:.3f}; {:.3f})'
        ),
        'b': r'$\mathcal{U}(0; 1+R_{\mathrm{p}}/R_\star)$',
        #'u[0]': '(2)',
        #'u[1]': '(2)',
        'u[0]': uniform_str(prior_d['u[0]']-delta_u, prior_d['u[0]']+delta_u,
                            fmtstr='({:.3f}; {:.3f})') + '$^{(2)}$',
        'u[1]': uniform_str(prior_d['u[1]']-delta_u, prior_d['u[1]']+delta_u,
                            fmtstr='({:.3f}; {:.3f})') + '$^{(2)}$',
        'r_star': truncnormal_str(
            mu=RSTAR, sd=RSTAR_STDEV, fmtstr='({:.3f}; {:.3f})'
        ),
        'logg_star': normal_str(
            mu=LOGG, sd=LOGG_STDEV, fmtstr='({:.3f}; {:.3f})'
        )
    }
    ufmt = '({:.2f}; {:.2f})'

    delta_trend = 0.05
    for i in range(5):
        pr[f'tess_{i}_mean'] = normal_str(mu=prior_d[f'tess_{i}_mean'],
                                          sd=0.01, fmtstr=ufmt)
        pr[f'tess_{i}_a1'] = uniform_str(lower=-delta_trend, upper=delta_trend, fmtstr=ufmt)
        pr[f'tess_{i}_a2'] = uniform_str(lower=-delta_trend, upper=delta_trend, fmtstr=ufmt)
    if modelid == 'allindivtransit':
        for i in range(4):
            pr[f'elsauce_{i}_mean'] = normal_str(mu=prior_d[f'elsauce_{i}_mean'],
                                                 sd=0.01, fmtstr=ufmt)
            pr[f'elsauce_{i}_a1'] = uniform_str(lower=-delta_trend, upper=delta_trend, fmtstr=ufmt)
            pr[f'elsauce_{i}_a2'] = uniform_str(lower=-delta_trend, upper=delta_trend, fmtstr=ufmt)
        for i in range(3):
            pr[f'astep_{i}_mean'] = normal_str(mu=prior_d[f'astep_{i}_mean'],
                                                 sd=0.01, fmtstr=ufmt)
            pr[f'astep_{i}_a1'] = uniform_str(lower=-delta_trend, upper=delta_trend, fmtstr=ufmt)
            pr[f'astep_{i}_a2'] = uniform_str(lower=-delta_trend, upper=delta_trend, fmtstr=ufmt)


    for d in derived_params:
        pr[d] = '--'

    # round everything. requires a double transpose because df.round
    # operates column-wise
    if modelid in ['allindivtransit', 'tessindivtransit']:
        round_precision = [7, 7, 5, 4, 3, 3, 3, 3]
        n_rp = len(round_precision)
        for i in range(n_fitted - n_rp):
            round_precision.append(4)
    else:
        raise NotImplementedError
    for d in derived_params:
        round_precision.append(2)

    df = df.T.round(
        decimals=dict(
            zip(df.index, round_precision)
        )
    ).T

    df['priors'] = list(pr.values())

    # units
    ud = {
        'period': 'd',
        't0': 'd',
        'log_r': '--',
        'b': '--',
        'u[0]': '--',
        'u[1]': '--',
        'r_star': r'$R_\odot$',
        'logg_star': 'cgs'
    }
    for i in range(5):
        ud[f'tess_{i}_mean'] = '--'
        ud[f'tess_{i}_a1'] = 'd$^{-1}$'
        ud[f'tess_{i}_a2'] = 'd$^{-2}$'
    if modelid == 'allindivtransit':
        for i in range(4):
            ud[f'elsauce_{i}_mean'] = '--'
            ud[f'elsauce_{i}_a1'] = 'd$^{-1}$'
            ud[f'elsauce_{i}_a2'] = 'd$^{-2}$'
        for i in range(3):
            ud[f'astep_{i}_mean'] = '--'
            ud[f'astep_{i}_a1'] = 'd$^{-1}$'
            ud[f'astep_{i}_a2'] = 'd$^{-2}$'

    ud['r'] = '--'
    ud['rho_star'] = 'g$\ $cm$^{-3}$'
    ud['r_planet'] = '$R_{\mathrm{Jup}}$'
    ud['a_Rs'] = '--'
    ud['cosi'] = '--'
    ud['T_14'] = 'hr'
    ud['T_13'] = 'hr'

    df['units'] = list(ud.values())

    df = df[
        ['units', 'priors', 'median', 'mean', 'sd', 'hpd_3%', 'hpd_97%']
    ]

    latexparams = [
        #useful
        r"$P$",
        r"$t_0^{(1)}$",
        r"$\log R_{\rm p}/R_\star$",
        "$b$",
        "$u_1$",
        "$u_2$",
        "$R_\star$",
        "$\log g$"
    ]
    for i in range(5):
        latexparams.append('$a_{'+str(i)+'0;\mathrm{TESS}}$')
        latexparams.append('$a_{'+str(i)+'1;\mathrm{TESS}}$')
        latexparams.append('$a_{'+str(i)+'2;\mathrm{TESS}}$')
    if modelid == 'allindivtransit':
        for i in range(4):
            latexparams.append('$a_{'+str(i)+'0;\mathrm{Sauce}}$')
            latexparams.append('$a_{'+str(i)+'1;\mathrm{Sauce}}$')
            latexparams.append('$a_{'+str(i)+'2;\mathrm{Sauce}}$')
        for i in range(3):
            latexparams.append('$a_{'+str(i)+'0;\mathrm{ASTEP}}$')
            latexparams.append('$a_{'+str(i)+'1;\mathrm{ASTEP}}$')
            latexparams.append('$a_{'+str(i)+'2;\mathrm{ASTEP}}$')

    from billy.convenience import flatten
    dlatexparams = [
        r"$R_{\rm p}/R_\star$",
        r"$\rho_\star$",
        r"$R_{\rm p}$",
        "$a/R_\star$",
        '$\cos i$',
        '$T_{14}$',
        '$T_{13}$'
    ]
    latexparams = flatten([latexparams, dlatexparams])
    df.index = latexparams

    outpath = os.path.join(PLOTDIR,
                           'posterior_table_clean_{}.csv'.format(modelid))
    df.to_csv(outpath, float_format='%.12f', na_rep='NaN')
    print('made {}'.format(outpath))

    # df.to_latex is dumb with float formatting.
    outpath = os.path.join(PLOTDIR,
                           'posterior_table_clean_{}.tex'.format(modelid))
    df.to_csv(outpath, sep=',', line_terminator=' \\\\\n',
              float_format='%.12f', na_rep='NaN')

    with open(outpath, 'r') as f:
        lines = f.readlines()

    for ix, l in enumerate(lines):

        # replace commas with latex ampersands
        thisline = deepcopy(l.replace(',', ' & '))

        # replace quotes with nada
        thisline = thisline.replace('"', '')

        # replace }0 with },0
        thisline = thisline.replace('}0', '},0')
        thisline = thisline.replace('}1', '},1')
        thisline = thisline.replace('}2', '},2')

        if ix == 0:
            lines[ix] = thisline
            continue

        # iteratively replace stupid trailing zeros with whitespace
        while re.search("0{2,10}\ ", thisline) is not None:
            r = re.search("0{2,10}\ ", thisline)
            thisline = thisline.replace(
                thisline[r.start():r.end()],
                ' '
            )

        lines[ix] = thisline

    with open(outpath, 'w') as f:
        f.writelines(lines)

    print('made {}'.format(outpath))
Ejemplo n.º 18
0
def plot_phase(fpath,
               ax,
               ind,
               s=3,
               alpha=0.3,
               lctype='IRM2',
               periodogramtype=None,
               peakindex=0,
               plot_bin_phase=False,
               overwritecsv=1):

    outsavpath = os.path.join(
        OUTDIR,
        'quilt_s6_s7_' + os.path.basename(fpath).replace('.fits', '.csv'))

    if os.path.exists(outsavpath) and not overwritecsv:
        df = pd.read_csv(outsavpath)
        phase = nparr(df['phase'])
        phz_flux = nparr(df['phz_flux'])
        period = nparr(df['period'])[0]

    else:
        #
        # get data. fpath here is a fits LC file. apply the periodogram requested.
        #
        time = iu.get_data_keyword(fpath, 'TMID_BJD', ext=1)
        mag = iu.get_data_keyword(fpath, lctype, ext=1)

        f_x0 = 1e4
        m_x0 = 10
        flux = f_x0 * 10**(-0.4 * (mag - m_x0))
        flux /= np.nanmedian(flux)

        time, flux = moe.mask_orbit_start_and_end(time, flux)

        # fit out long term trend (light detrending) with median filter of 5 days.
        if 'IRM' in lctype:
            ngroups, groups = lcmath.find_lc_timegroups(time, mingap=0.5)
            assert ngroups == 2

            windowsize = 48 * 5 + 1  # 5 days
            tg_smooth_flux = []
            for group in groups:

                #
                # fit out arbitrary order legendre series
                # p(x) = c_0*L_0(x) + c_1*L_1(x) + c_2*L_2(x) + ... + c_n*L_n(x)
                #
                legendredeg = 2
                p = Legendre.fit(time[group], flux[group], legendredeg)
                coeffs = p.coef
                fit_flux = p(time[group])

                tg_smooth_flux.append(flux[group] / fit_flux)

            flux = np.concatenate(tg_smooth_flux)

        if periodogramtype == 'tls':
            period_min, period_max = 0.5, 5
            tlsp = periodbase.tls_parallel_pfind(
                time,
                flux,
                1e-3 * flux,
                magsarefluxes=True,
                tls_rstar_min=0.1,
                tls_rstar_max=10,
                tls_mstar_min=0.1,
                tls_mstar_max=5.0,
                tls_oversample=8,
                tls_mintransits=1,
                tls_transit_template='default',
                nbestpeaks=5,
                sigclip=None,
                nworkers=52)

            period = tlsp['nbestperiods'][peakindex]
            t0 = tlsp['tlsresult']['T0']
            if peakindex == 1:
                t0 += period / 2

        elif periodogramtype == 'gls':
            period_min, period_max = 0.1, 5
            ls = LombScargle(time, flux, flux * 1e-3)
            freq, power = ls.autopower(minimum_frequency=1 / period_max,
                                       maximum_frequency=1 / period_min,
                                       samples_per_peak=20)
            period = 1 / freq[np.argmax(power)]
            t0 = time[np.argmin(flux)]

        else:
            raise NotImplementedError(
                'got {}, not imlemented'.format(periodogramtype))

        #
        # phase data
        #
        phzd = phase_magseries(time, flux, period, t0, wrap=True, sort=True)

        phase = phzd['phase']
        phz_flux = phzd['mags']

    #
    # plot data
    #
    ax.scatter(phase,
               phz_flux,
               c='k',
               alpha=alpha,
               zorder=3,
               s=s,
               rasterized=True,
               linewidths=0)

    ax.text(0.88,
            0.03,
            '{:.2f}d'.format(period),
            transform=ax.transAxes,
            ha='right',
            va='bottom')

    ax.text(0.04,
            0.06,
            '{}'.format(ind),
            transform=ax.transAxes,
            ha='left',
            va='bottom')

    if overwritecsv:
        outdf = pd.DataFrame({
            'phase': phase,
            'phz_flux': phz_flux,
            'period': np.ones_like(phase) * period
        })
        outdf.to_csv(outsavpath, index=False)

    if plot_bin_phase:

        binphasedlc = phase_bin_magseries(phase,
                                          phz_flux,
                                          binsize=2e-2,
                                          minbinelems=3)
        binplotphase = binphasedlc['binnedphases']
        binplotmags = binphasedlc['binnedmags']

        ax.scatter(binplotphase,
                   binplotmags,
                   c='orange',
                   alpha=alpha,
                   zorder=4,
                   s=s,
                   rasterized=True,
                   linewidths=0)