def inject_signal(time, ap_mag, inj_dict): # mags to flux f_x0 = 1e4 m_x0 = 10 ap_flux = f_x0 * 10**(-0.4 * (ap_mag - m_x0)) ap_flux /= np.nanmedian(ap_flux) # ignore the times near the edges of orbits for TLS. time, flux = moe.mask_orbit_start_and_end(time, ap_flux, raise_expectation_error=False) # initialize model to inject: 90 degrees, LD coeffs for 5000 K dwarf star # in TESS band (Claret 2018) no eccentricity, random phase, b=0, stellar # density set to 1.5x solar. Eq (30) Winn 2010 to get a/Rstar. params = batman.TransitParams() density_sun = 3 * u.Msun / (4 * np.pi * u.Rsun**3) density_star = 1.5 * density_sun a_by_rstar = ((c.G * (inj_dict['period'] * u.day)**2 / (3 * np.pi) * density_star)**(1 / 3)).cgs.value params.inc = 90. q1, q2 = 0.4, 0.2 params.ecc = 0 params.limb_dark = "quadratic" params.u = [q1, q2] w = np.random.uniform(low=np.rad2deg(-np.pi), high=np.rad2deg(np.pi)) params.w = w params.per = inj_dict['period'] t0 = np.nanmin(time) + inj_dict['epoch'] params.t0 = t0 params.rp = np.sqrt(inj_dict['depth']) params.a = a_by_rstar exp_time_minutes = 30. exp_time_days = exp_time_minutes / (24. * 60) ss_factor = 10 m_toinj = batman.TransitModel(params, time, supersample_factor=ss_factor, exp_time=exp_time_days) # calculate light curve and inject flux_toinj = m_toinj.light_curve(params) inj_flux = flux + (flux_toinj - 1.) * np.nanmedian(flux) return time, inj_flux, t0
def explore_flux_lightcurves( data, ticid, outdir=None, period=None, epoch=None, pipeline=None, detrend=False, window_length=None, do_phasefold=0, badtimewindows=None, get_lc=False, require_quality_zero=1, forceylim=None, normstitch=True, slideclipdict={'window_length':1, 'high':3, 'low':8}, mask_orbit_edges=False ): """ Given a list of SPOC 2 minute data FITS tables, stitch them across sectors and make diagnostic plots. Args: data (list): from `get_tess_data`, contents [hdulistA[1].data, hdulistB[1].data], etc.. ticid (str): TIC ID. pipeline (str): one of ['cdips', 'spoc', 'eleanor', 'cdipspre', 'kepler', 'qlp']. This is used to access the flux, provenance, etc. outdir (str): diagnostic plots are written here. If None, goes to cdips_followup results directory. Optional kwargs: period, epoch (float): optional detrend (bool, or string): 'biweight' or 'pspline' accepted. Default parameters assumed for each. badtimewindows (list): to manually mask out, [(1656, 1658), (1662, 1663)], for instance. get_lc (bool): if True, returns time and flux arrays. require_quality_zero (bool): if True, sets QUALITY==0, throwing out lots of data. normstitch (bool): normalize flux across sectors s.t. the relative amplitude remains fixed. slideclipdict (dict): e.g., {'window_length':1, 'high':3, 'low':8} for 1 day sliding window, exclude +3MAD from median above and -8MAD from median below. """ assert isinstance(data, list), 'Expected list of FITStables.' if pipeline not in ['spoc', 'kepler', 'qlp', 'cdips']: raise NotImplementedError if isinstance(epoch, float): if epoch < 2450000: raise ValueError(f'Expected epoch in BJDTDB. Got epoch={epoch:.6f}.') ykey = LCKEYDICT[pipeline]['flux'] xkey = LCKEYDICT[pipeline]['time'] qualkey = LCKEYDICT[pipeline]['quality'] prov = LCKEYDICT[pipeline]['prov'] inst = LCKEYDICT[pipeline]['inst'] if outdir is None: outdir = os.path.join(RESULTSDIR, 'quicklooklc', f'TIC{ticid}') times, fluxs= [], [] for ix, d in enumerate(data): savpath = os.path.join( outdir, f'TIC{ticid}_{prov}_{inst}_lightcurve_{str(ix).zfill(2)}.png' ) if detrend: savpath = os.path.join( outdir, f'TIC{ticid}_{prov}_{inst}_lightcurve_{detrend}_{str(ix).zfill(2)}.png' ) plt.close('all') f,ax = plt.subplots(figsize=(16*2,4*1.5)) if require_quality_zero: okkey = 0 if pipeline in 'spoc,kepler,qlp'.split(',') else 'G' sel = (d[qualkey] == okkey) & (d[ykey] > 0) print(42*'.') print('WRN!: omitting all non-zero quality flags. throws out good data!') print(42*'.') else: sel = (d[ykey] > 0) if badtimewindows is not None: for w in badtimewindows: sel &= ~( (d[xkey] > w[0]) & (d[xkey] < w[1]) ) # correct time column to BJD_TDB x_offset = LCKEYDICT[pipeline]['time_offset'] x_obs = d[xkey][sel] + x_offset # get the median-normalized flux y_obs = d[ykey][sel] if pipeline == 'cdips': y_obs, _ = _given_mag_get_flux(y_obs, y_obs*1e-3) y_obs /= np.nanmedian(y_obs) if mask_orbit_edges: x_obs, y_obs, _ = moe.mask_orbit_start_and_end( x_obs, y_obs, raise_expectation_error=False, orbitgap=0.7, orbitpadding=12/(24), return_inds=True ) # slide clip -- remove outliers with windowed stdevn removal if isinstance(slideclipdict, dict): y_obs = slide_clip(x_obs, y_obs, slideclipdict['window_length'], low=slideclipdict['low'], high=slideclipdict['high'], method='mad', center='median') if detrend: ax.scatter(x_obs, y_obs, c='k', s=4, zorder=2) # # default pspline detrending if detrend=='pspline': y_obs, y_trend = dtr.detrend_flux(x_obs, y_obs) x_trend = deepcopy(x_obs) # in some cases, might prefer the biweight elif detrend == 'biweight': y_obs, y_trend = dtr.detrend_flux(x_obs, y_obs, method='biweight', cval=5, window_length=0.5, break_tolerance=0.5) x_trend = deepcopy(x_obs) elif detrend == 'minimal': y_obs, y_trend = dtr.detrend_flux(x_obs, y_obs, method='biweight', cval=2, window_length=3.5, break_tolerance=0.5) x_trend = deepcopy(x_obs) elif detrend == 'median': y_obs, y_trend = dtr.detrend_flux(x_obs, y_obs, method='median', window_length=0.6, break_tolerance=0.5, edge_cutoff=0.) x_trend = deepcopy(x_obs) elif detrend == 'best': from cdips.lcproc.find_planets import run_periodograms_and_detrend dtr_dict = {'method':'best', 'break_tolerance':0.5, 'window_length':0.5} lsp_options = {'period_min':0.1, 'period_max':20} # r = [source_id, ls_period, ls_fap, ls_amplitude, tls_period, tls_sde, # tls_t0, tls_depth, tls_duration, tls_distinct_transit_count, # tls_odd_even, dtr_method] r, search_time, search_flux, dtr_stages_dict = run_periodograms_and_detrend( ticid, x_obs, y_obs, dtr_dict, period_min=lsp_options['period_min'], period_max=lsp_options['period_max'], dtr_method='best', return_extras=True, magisflux=True ) y_trend, x_trend, dtr_method = ( dtr_stages_dict['trend_flux'], dtr_stages_dict['trend_time'], dtr_stages_dict['dtr_method_used'] ) x_obs, y_obs = deepcopy(search_time), deepcopy(search_flux) print(f'TIC{ticid} TLS results') print(f'dtr_method_used: {dtr_method}') print(r) else: raise NotImplementedError if detrend: ax.plot(x_trend, y_trend, c='r', lw=0.5, zorder=3) else: ax.scatter(x_obs, y_obs, c='k', s=4, zorder=2) times.append( x_obs ) fluxs.append( y_obs ) ax.set_xlabel('time [bjdtdb]') ax.set_ylabel(ykey) ylim = ax.get_ylim() ax.set_title(ix) if detrend: _ylim = _get_ylim(y_trend) else: _ylim = _get_ylim(y_obs) ax.set_ylim(_ylim) if isinstance(forceylim, list) or isinstance(forceylim, tuple): ax.set_ylim(forceylim) if not epoch is None: tra_times = epoch + np.arange(-1000,1000,1)*period xlim = ax.get_xlim() ylim = ax.get_ylim() ax.set_ylim((min(ylim), max(ylim))) ax.vlines(tra_times, min(ylim), max(ylim), color='orangered', linestyle='--', zorder=-2, lw=0.5, alpha=0.3) ax.set_ylim((min(ylim), max(ylim))) ax.set_xlim(xlim) f.savefig(savpath, dpi=300, bbox_inches='tight') print('made {}'.format(savpath)) if normstitch: times, fluxs, _ = lcu.stitch_light_curves( times, fluxs, fluxs, magsarefluxes=True, normstitch=True ) else: times = np.hstack(np.array(times).flatten()) fluxs = np.hstack(np.array(fluxs).flatten()) # NOTE: this call is deprecated stimes, smags, _ = lcmath.sigclip_magseries( times, fluxs, np.ones_like(fluxs), sigclip=[20,20], iterative=True, magsarefluxes=True ) savpath = os.path.join( outdir, f'TIC{ticid}_{prov}_{inst}_lightcurve_{str(ykey).zfill(2)}_allsector.png' ) if detrend: savpath = os.path.join( outdir, f'TIC{ticid}_{prov}_{inst}_lightcurve_{detrend}_{str(ykey).zfill(2)}_allsector.png' ) plt.close('all') f,ax = plt.subplots(figsize=(16,4)) ax.scatter(stimes, smags, c='k', s=1) if not epoch is None: tra_times = epoch + np.arange(-1000,1000,1)*period xlim = ax.get_xlim() ylim = ax.get_ylim() ax.set_ylim((min(ylim), max(ylim))) ax.vlines(tra_times, min(ylim), max(ylim), color='orangered', linestyle='--', zorder=-2, lw=0.5, alpha=0.3) ax.set_ylim((min(ylim), max(ylim))) ax.set_xlim(xlim) ax.set_xlabel('time [bjdtdb]') ax.set_ylabel('relative '+ykey) ax.set_title(ix) f.savefig(savpath, dpi=400, bbox_inches='tight') print('made {}'.format(savpath)) csvpath = savpath.replace('.png','_sigclipped.csv') pd.DataFrame({ 'time': stimes, 'flux': smags, }).to_csv(csvpath, index=False) print(f'made {csvpath}') if do_phasefold: assert ( isinstance(period, (float,int)) and isinstance(epoch, (float,int)) ) # # ax: primary transit # if inst == 'kepler': phasebin = 1e-3 elif inst == 'tess': phasebin = 5e-3 minbinelems = 2 plotxlims = [(-0.5, 0.5), (-0.05,0.05)] xlimstrs = ['xwide','xnarrow'] plotylim = [0.9, 1.08]#None #[0.9,1.1] do_vlines = False for plotxlim, xstr in zip(plotxlims, xlimstrs): plt.close('all') fig, ax = plt.subplots(figsize=(4,3)) # use times and fluxs, instead of the sigma clipped thing. _make_phased_magseries_plot(ax, 0, times, fluxs, np.ones_like(fluxs)/1e4, period, epoch, True, True, phasebin, minbinelems, plotxlim, '', xliminsetmode=False, magsarefluxes=True, phasems=0.8, phasebinms=4.0, verbose=True) if isinstance(plotylim, (list, tuple)): ax.set_ylim(plotylim) else: plotylim = _get_ylim(fluxs) ax.set_ylim(plotylim) if do_vlines: ax.vlines(1/6, min(plotylim), max(plotylim), color='orangered', linestyle='--', zorder=-2, lw=1, alpha=0.8) ax.set_ylim(plotylim) dstr = detrend if detrend else '' savpath = os.path.join( outdir, f'TIC{ticid}_{prov}_{inst}_lightcurve_{dstr}_{ykey}_{xstr}_allsector_phasefold.png' ) fig.savefig(savpath, dpi=400, bbox_inches='tight') print(f'made {savpath}') csvpath = savpath.replace('png','csv') pd.DataFrame({ 'time': times, 'flux': fluxs }).to_csv(csvpath, index=False) print(f'made {csvpath}') if get_lc: return times, fluxs
def get_toi1937_lightcurve(): """ Create the stitched CDIPS FFI light curve for TOI 1937. (Starting from the raw light curves, and the PCA eigenvectors previously made for this sector). Note: the main execution of this PCA detrending happens on phtess2. A few notes: * 3 eigenvectors were used, plus the background light BGV timeseries. * a +/-12 hour orbit edge mask was used (to avoid what looked like scattered light) * the output can be checked at /results/quicklook_lcs/5489726768531119616_allvar_report.pdf """ picklepath = os.path.join(PHOTDIR, 'toi1937_merged_stitched_s7s9_lc_20201130.pkl') if not os.path.exists(picklepath): # Use the CDIPS IRM2 light curves as starting base. # 5489726768531119616_s09_llc.fits lcpaths = glob(os.path.join(PHOTDIR, '*_s??_llc.fits')) assert len(lcpaths) == 2 infodicts = [ { 'SECTOR': 7, 'CAMERA': 3, 'CCD': 4, 'PROJID': 1527 }, { 'SECTOR': 9, 'CAMERA': 3, 'CCD': 3, 'PROJID': 1558 }, ] ########################################## # next ~45 lines pinched from cdips.drivers.do_allvariable_report_making ########################################## # # detrend systematics. each light curve yields tuples of: # primaryhdr, data, ap, dtrvecs, eigenvecs, smooth_eigenvecs # dtr_infos = [] for lcpath, infodict in zip(lcpaths, infodicts): dtr_info = dtr.detrend_systematics(lcpath, infodict=infodict, max_n_comp=3) dtr_infos.append(dtr_info) # # stitch all available light curves # ap = dtr_infos[0][2] timelist = [d[1]['TMID_BJD'] for d in dtr_infos] maglist = [d[1][f'PCA{ap}'] for d in dtr_infos] magerrlist = [d[1][f'IRE{ap}'] for d in dtr_infos] extravecdict = {} extravecdict[f'IRM{ap}'] = [d[1][f'IRM{ap}'] for d in dtr_infos] for i in range(0, 7): extravecdict[f'CBV{i}'] = [d[3][i, :] for d in dtr_infos] time, flux, fluxerr, vec_dict = lcu.stitch_light_curves( timelist, maglist, magerrlist, extravecdict) # # mask orbit edges # s_time, s_flux, inds = moe.mask_orbit_start_and_end( time, flux, raise_expectation_error=False, orbitgap=0.7, orbitpadding=12 / 24, return_inds=True) s_fluxerr = fluxerr[inds] # # save output # ap = dtr_infos[0][2] lcdict = { 'source_id': np.int64(5489726768531119616), 'E_BpmRp': 0.1343, 'ap': ap, 'TMID_BJD': time, f'IRM{ap}': vec_dict[f'IRM{ap}'], f'PCA{ap}': flux, f'IRE{ap}': fluxerr, 'STIME': s_time.astype(np.float64), f'SPCA{ap}': s_flux.astype(np.float64), f'SPCAE{ap}': s_fluxerr.astype(np.float64), 'dtr_infos': dtr_infos, 'vec_dict': vec_dict, 'tess_texp': np.nanmedian(np.diff(s_time)) } with open(picklepath, 'wb') as f: pickle.dump(lcdict, f) # # verify output # from cdips.plotting.allvar_report import make_allvar_report plotdir = os.path.join(RESULTSDIR, 'quicklook_lcs') outd = make_allvar_report(lcdict, plotdir) with open(picklepath, 'rb') as f: print(f'Found {picklepath}: loading it!') lcdict = pickle.load(f) return (lcdict['STIME'].astype(np.float64) - 2457000, lcdict['SPCA2'].astype(np.float64), lcdict['SPCAE2'].astype(np.float64), lcdict['tess_texp'])
def get_clean_ptfo_data(binsize=120 * 5): """ get data. mask orbit edges... quality cut and remove weird end points. bin to 10 minutes, to speed fitting (which is linear in time). """ d = get_ptfo_data(cdips=0, spoc=1)[0] time = d.TIME flux = d.PDCSAP_FLUX flux_err = d.PDCSAP_FLUX_ERR N_i = len(time) # initial quality = d.QUALITY time, flux, flux_err = (time[quality == 0], flux[quality == 0], flux_err[quality == 0]) N_ii = len(time) # after quality cut time, flux, sel = mask_orbit_start_and_end(time, flux, orbitgap=0.5, expected_norbits=2, orbitpadding=6 / (24), raise_expectation_error=True, return_inds=True) flux_err = flux_err[sel] N_iii = len(time) # after orbit edge masking # 2457000 + 1488.3 = 2458488.3 sel = (time < 1488.3) x_obs = time[sel] time_offset = 1468.2 x_obs -= time_offset # reverse offset: 2457000 + 1468.2 = 2458468.2 N_iv = len(x_obs) # after dropping end of orbit 20 y_obs = (flux[sel] / np.nanmedian(flux[sel])) - 1 y_err = flux_err[sel] / np.nanmedian(flux[sel]) print(42 * '-') print('N initial: {}'.format(N_i)) print('N after quality cut: {}'.format(N_ii)) print('N after quality cut + orbit edge masking: {}'.format(N_iii)) print( 'N after quality cut + orbit edge masking + dropping end of orbit 20: {}' .format(N_iv)) print(42 * '-') if isinstance(binsize, int): bd = time_bin_magseries_with_errs(x_obs, y_obs, y_err, binsize=binsize, minbinelems=5) x_obs = bd['binnedtimes'] y_obs = bd['binnedmags'] # assume errors scale as sqrt(N) original_cadence = 120 y_err = bd['binnederrs'] / (binsize / original_cadence)**(1 / 2) assert len(x_obs) == len(y_obs) == len(y_err) return (x_obs.astype(np.float64), y_obs.astype(np.float64), y_err.astype(np.float64))
def clean_rotationsignal_tess_singlesector_light_curve(time, mag, magisflux=False, dtr_dict=None, lsp_dict=None, maskorbitedge=True, lsp_options={ 'period_min': 0.1, 'period_max': 20 }, verbose=True): """ The goal of this function is to remove a stellar rotation signal from a single TESS light curve (ideally one without severe insturmental systematics) while preserving transits. "Cleaning" by default is taken to mean the sequence of mask_orbit_edge -> slide_clip -> detrend -> slide_clip. "Detrend" can mean any of the Wotan flatteners, Notch, or LOCOR. "slide_clip" means apply windowed sigma-clipping removal. Args: time, mag (np.ndarray): time and magnitude (or flux) vectors magisflux (bool): True if the "mag" vector is a flux already dtr_dict (optional dict): dictionary containing arguments passed to Wotan, Notch, or LOCOR. Relevant keys should include: 'dtr_method' (str): one of: ['best', 'notch', 'locor', 'pspline', 'biweight', 'none'] 'break_tolerance' (float): number of days past which a segment of light curve is considered a "new segment". 'window_length' (float): length of sliding window in days lsp_dict (optional dict): dictionary containing Lomb Scargle periodogram information, which is used in the "best" method for choosing between LOCOR or Notch detrending. If this is not passed, it'll be constructed here after the mask_orbit_edge -> slide_clip steps. lsp_options: contains keys period_min and period_max, used for the internal Lomb Scargle periodogram search. maskorbitedge (bool): whether to apply the initial "mask_orbit_edge" step. Probably would only want to be false if you had already done it elsewhere. Returns: search_time, search_flux, dtr_stages_dict (np.ndarrays and dict): light curve ready for TLS or BLS style periodograms; and a dictionary of the different processing stages (see comments for details of `dtr_stages_dict` contents). """ dtr_method = _get_detrending_method(dtr_dict) # # convert mag to flux and median-normalize # if magisflux: flux = mag else: f_x0 = 1e4 m_x0 = 10 flux = f_x0 * 10**(-0.4 * (mag - m_x0)) flux /= np.nanmedian(flux) # # ignore the times near the edges of orbits for TLS. # if maskorbitedge: _time, _flux = moe.mask_orbit_start_and_end( time, flux, raise_expectation_error=False, verbose=verbose) else: _time, _flux = time, flux # # sliding sigma clip asymmetric [20,3]*MAD, about median. use a 3-day # window, to give ~100 to 150 data points. mostly to avoid big flares. # clip_window = 3 clipped_flux = slide_clip(_time, _flux, window_length=clip_window, low=20, high=3, method='mad', center='median') sel0 = ~np.isnan(clipped_flux) # # for "best" or LOCOR detrending, you need to know the stellar rotation # period. so, if it hasn't already been run, run the LS periodogram here. # in `lsp_dict`, cache the LS peak period, amplitude, and FAP. # if (not isinstance(lsp_dict, dict)) and (dtr_method in ['locor', 'best']): period_min = lsp_options['period_min'] period_max = lsp_options['period_max'] ls = LombScargle(_time[sel0], clipped_flux[sel0], clipped_flux[sel0] * 1e-3) freq, power = ls.autopower(minimum_frequency=1 / period_max, maximum_frequency=1 / period_min) ls_fap = ls.false_alarm_probability(power.max()) best_freq = freq[np.argmax(power)] ls_period = 1 / best_freq theta = ls.model_parameters(best_freq) ls_amplitude = theta[1] lsp_dict = {} lsp_dict['ls_period'] = ls_period lsp_dict['ls_amplitude'] = np.abs(ls_amplitude) lsp_dict['ls_fap'] = ls_fap if not isinstance(dtr_dict, dict): dtr_dict = {} dtr_dict['method'] = dtr_method # # apply the detrending call based on the method given # dtr_method_used = dtr_method if dtr_method in ['pspline', 'biweight', 'none']: if 'break_tolerance' not in dtr_dict: dtr_dict['break_tolerance'] = None if 'window_length' not in dtr_dict: dtr_dict['window_length'] = None flat_flux, trend_flux = detrend_flux( _time[sel0], clipped_flux[sel0], break_tolerance=dtr_dict['break_tolerance'], method=dtr_dict['method'], cval=None, window_length=dtr_dict['window_length'], edge_cutoff=None) elif dtr_method == 'notch': flat_flux, trend_flux, notch = _run_notch(_time[sel0], clipped_flux[sel0], dtr_dict, verbose=verbose) elif dtr_method == 'locor': flat_flux, trend_flux, notch = _run_locor(_time[sel0], clipped_flux[sel0], dtr_dict, lsp_dict) elif dtr_method == 'best': # for stars with Prot < 1 day, use LOCOR. for stars with Prot > 1 day, # use Notch. (or pspline?). PERIOD_CUTOFF = 1.0 if lsp_dict['ls_period'] > PERIOD_CUTOFF: flat_flux, trend_flux, notch = _run_notch(_time[sel0], clipped_flux[sel0], dtr_dict, verbose=verbose) dtr_method_used += '-notch' elif (lsp_dict['ls_period'] < PERIOD_CUTOFF and lsp_dict['ls_period'] > 0): flat_flux, trend_flux, notch = _run_locor(_time[sel0], clipped_flux[sel0], dtr_dict, lsp_dict) dtr_method_used += '-locor' else: raise NotImplementedError(f"Got LS period {lsp_dict['ls_period']}") # # re-apply sliding sigma clip asymmetric [20,3]*MAD, about median, after # detrending. # clip_window = 3 clipped_flat_flux = slide_clip(_time[sel0], flat_flux, window_length=clip_window, low=20, high=3, method='mad', center='median') sel1 = ~np.isnan(clipped_flat_flux) search_flux = clipped_flat_flux[sel1] search_time = _time[sel0][sel1] dtr_stages_dict = { # non-nan indices from clipped_flux 'sel0': sel0, # non-nan indices from clipped_flat_flux 'sel1': sel1, # after initial window sigma_clip on flux, what is left? 'clipped_flux': clipped_flux, # after detrending, what is left? 'flat_flux': flat_flux, # after window sigma_clip on flat_flux, what is left? 'clipped_flat_flux': clipped_flat_flux, # what does the detrending algorithm give as the "trend"? 'trend_flux': trend_flux, 'trend_time': _time[sel0], # what method was used? if "best", gives "best-notch" or "best-locor" 'dtr_method_used': dtr_method_used, # times and fluxes used 'search_time': search_time, 'search_flux': search_flux } if isinstance(lsp_dict, dict): # in most cases, cache the LS period, amplitude, and FAP dtr_stages_dict['lsp_dict'] = lsp_dict return search_time, search_flux, dtr_stages_dict
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)