def catalog_info(self, id): """Takes KIC_ID, returns stellar information from online catalog using Vizier""" if type(id) is not int: raise TypeError('KIC_ID ID must be of type "int"') columns = [ "Teff", "log(g)", "Rad", "E_Rad", "e_Rad", "Mass", "E_Mass", "e_Mass", "_RA", "_DE" ] catalog = "J/ApJS/229/30/catalog" result = (Vizier(columns=columns).query_constraints( KIC=id, catalog=catalog)[0].as_array()) Teff = result[0][0] logg = result[0][1] radius = result[0][2] radius_max = result[0][3] radius_min = result[0][4] mass = result[0][5] mass_max = result[0][6] mass_min = result[0][7] ra = result[0][8] dec = result[0][9] lum = self.star_luminosity(Teff, radius) ld, mass, mass_min, mass_max, radius, radius_min, radius_max = tls.catalog_info( KIC_ID=id) return (ld, Teff, lum, logg, radius, radius_min, radius_max, mass, mass_min, mass_max, ra, dec)
def catalog_info(self, id): """Takes EPIC_ID, returns stellar information from online catalog using Vizier""" if type(id) is not int: raise TypeError('EPIC_ID ID must be of type "int"') if (id < 201000001) or (id > 251813738): raise TypeError( "EPIC_ID ID must be in range 201000001 to 251813738") columns = [ "Teff", "logg", "Rad", "E_Rad", "e_Rad", "Mass", "E_Mass", "e_Mass", "RAJ2000", "DEJ2000" ] catalog = "IV/34/epic" result = (Vizier(columns=columns).query_constraints( ID=id, catalog=catalog)[0].as_array()) Teff = result[0][0] logg = result[0][1] radius = result[0][2] radius_max = result[0][3] radius_min = result[0][4] mass = result[0][5] mass_max = result[0][6] mass_min = result[0][7] ra = result[0][8] dec = result[0][9] lum = self.star_luminosity(Teff, radius) ld, mass, mass_min, mass_max, radius, radius_min, radius_max = tls.catalog_info( EPIC_ID=id) return (ld, Teff, lum, logg, radius, radius_min, radius_max, mass, mass_min, mass_max, ra, dec)
def inject_and_tls_search_by_tic(tic_id, sigma_multiplier=3, **kwargs): ''' Summary: ------- - wrapper around inject_and_tls_search() - retrieves the SPOC PDC-SAP lightcurve - retrieves all TIC catalog information from MAST - injects a planet signal via ellc, for a given period and radius (at random epoch) - runs TLS on these injected data and infos Inputs: ------- tic_id : str or int TIC ID Optional Inputs: ---------------- sigma_multiplier : float set TLS' uniform bounds for the host's radius and mass at sigma_multiplier times the error bars from TICv8 default is 3 **kwargs : keyword arguments any other keyword arguments, see inject_and_tls_search() Returns: ------- a list of all TLS results will get saved to a log file ''' #::: format inputs tic_id = str(int(tic_id)) #::: load data time, flux, flux_err = tessio.get(tic_id, pipeline='spoc', PDC=True, unpack=True) #::: load TIC info ldc, R_host, R_host_lerr, R_host_uerr, M_host, M_host_lerr, M_host_uerr = catalog_info( TIC_ID=int(tic_id)) print('\nTICv8 info:') print('Quadratic limb darkening u_0, u_1', ldc[0], ldc[1]) print('Stellar radius', R_host, '+', R_host_lerr, '-', R_host_uerr) print('Stellar mass', M_host, '+', M_host_lerr, '-', M_host_uerr) #::: run inject_and_tls_search(time, flux, flux_err, R_host=R_host, R_host_min=R_host - sigma_multiplier * R_host_lerr, R_host_max=R_host + sigma_multiplier * R_host_uerr, M_host=M_host, M_host_min=M_host - sigma_multiplier * M_host_lerr, M_host_max=M_host + sigma_multiplier * M_host_uerr, ldc=ldc, **kwargs)
def inject(self, phases, min_period, max_period, step_period, min_radius, max_radius, step_radius): #TODO get mission and proper id and author object_info = MissionObjectInfo(self.id, self.sectors) lc, lc_data, star_info, transits_min_count, sectors, quarters = \ MissionLightcurveBuilder().build(object_info, None) ab, mass, massmin, massmax, radius, radiusmin, radiusmax = catalog_info( TIC_ID=id) # units for ellc rstar = star_info.radius * u.R_sun mstar = star_info.mass * u.M_sun mstar_min = star_info.mass_min * u.M_sun mstar_max = star_info.mass_max * u.M_sun rstar_min = star_info.radius_min * u.R_sun rstar_max = star_info.radius_max * u.R_sun print('\n STELLAR PROPERTIES FOR THE SIGNAL SEARCH') print('================================================\n') print('limb-darkening estimates using quadratic LD (a,b)=', ab) print('mass =', format(mstar, '0.5f')) print('mass_min =', format(mstar_min, '0.5f')) print('mass_max =', format(mstar_max, '0.5f')) print('radius =', format(rstar, '0.5f')) print('radius_min =', format(rstar_min, '0.5f')) print('radius_max =', format(rstar_max, '0.5f')) lc_new = lk.LightCurve(time=lc.time, flux=lc.flux, flux_err=lc.flux_err) clean = lc_new.remove_outliers(sigma_lower=float('inf'), sigma_upper=3) flux0 = clean.flux time = clean.time flux_err = clean.flux_err if not os.path.isdir(self.dir): os.mkdir(self.dir) for period in np.arange(min_period, max_period, step_period): for t0 in np.arange(time[60], time[60] + period - 0.1, period / phases): for rplanet in np.arange(min_radius, max_radius, step_radius): rplanet = np.around(rplanet, decimals=2) * u.R_earth print('\n') print('P = ' + str(period) + ' days, Rp = ' + str(rplanet) + ", T0 = " + str(t0)) time_model, flux_model, flux_err_model = self.__make_model( time, flux0, flux_err, rstar, mstar, t0, period, rplanet) file_name = os.path.join(self.dir + '/P' + str(period) + '_R' + str(rplanet.value) + '_' + str(t0) + '.csv') lc_df = pd.DataFrame(columns=['#time', 'flux', 'flux_err']) lc_df['#time'] = time_model lc_df['flux'] = flux_model lc_df['flux_err'] = flux_err_model lc_df.to_csv(file_name, index=False)
def get_tls_kwargs_by_tic(tic_id, tls_kwargs=None): u, R_star, R_star_lerr, R_star_uerr, M_star, M_star_lerr, M_star_uerr = catalog_info(TIC_ID=int(tic_id)) print('TICv8 info:') print('Quadratic limb darkening u_0, u_1', u[0], u[1]) print('Stellar radius', R_star, '+', R_star_lerr, '-', R_star_uerr) print('Stellar mass', M_star, '+', M_star_lerr, '-', M_star_uerr) if tls_kwargs is None: tls_kwargs = {} tls_kwargs['R_star']=float(R_star) tls_kwargs['R_star_min']=R_star-3*R_star_lerr tls_kwargs['R_star_max']=R_star+3*R_star_uerr tls_kwargs['M_star']=float(M_star) tls_kwargs['M_star_min']=M_star-3*M_star_lerr tls_kwargs['M_star_max']=M_star+3*M_star_uerr tls_kwargs['u']=u return tls_kwargs
def get_priors_from_tic(tic_id): """ Get stellar mass and radius priors from the TESS Input Catalog. Parameters ---------- tic_id : int A TESS Input Catalog ID. Returns ------- pri_m_star : ndarray Mean and standard deviation of the stellar mass estimate in solar masses. pri_r_star : ndarray Mean and standard deviation of the stellar radius estimate in solar radii. """ tic_params = tls.catalog_info(TIC_ID=tic_id) ld, M_star, M_star_l, M_star_u, R_star, R_star_l, R_star_u = tic_params # Guard against bad values in the TIC if not np.isfinite(R_star): R_star = 1. if not np.isfinite(M_star): M_star = 1. if not np.isfinite(M_star_u): M_star_u = 0.1 if not np.isfinite(M_star_l): M_star_l = 0.1 if not np.isfinite(R_star_u): R_star_u = 0.1 if not np.isfinite(R_star_l): R_star_l = 0.1 # Use stellar parameters from TIC pri_m_star_mean = M_star pri_m_star_err = (M_star_u + M_star_l) / 2. pri_m_star = np.append(pri_m_star_mean, pri_m_star_err) pri_r_star_mean = R_star pri_r_star_err = (R_star_u + R_star_l) / 2. pri_r_star = np.append(pri_r_star_mean, pri_r_star_err) return pri_m_star, pri_r_star
def get_tls_kwargs_by_tic(tic_id, sigma=3, tls_kwargs=None, quiet=True): #mass comes first, radius comes second in the TLS source code for catalog_info() u, M_star, M_star_lerr, M_star_uerr, R_star, R_star_lerr, R_star_uerr = catalog_info( TIC_ID=int(tic_id)) if not quiet: print('TICv8 info:') print('Quadratic limb darkening u_0, u_1', u[0], u[1]) print('Stellar radius', R_star, '+', R_star_lerr, '-', R_star_uerr) print('Stellar mass', M_star, '+', M_star_lerr, '-', M_star_uerr) if tls_kwargs is None: tls_kwargs = {} tls_kwargs['R_star'] = float(R_star) tls_kwargs['R_star_min'] = R_star - sigma * R_star_lerr tls_kwargs['R_star_max'] = R_star + sigma * R_star_uerr tls_kwargs['M_star'] = float(M_star) tls_kwargs['M_star_min'] = M_star - sigma * M_star_lerr tls_kwargs['M_star_max'] = M_star + sigma * M_star_uerr tls_kwargs['u'] = u return tls_kwargs
def catalog_info(self, id): """Takes TIC_ID, returns stellar information from online catalog using Vizier""" if type(id) is not int: raise TypeError('TIC_ID ID must be of type "int"') result = Catalogs.query_criteria(catalog="Tic", ID=id).as_array() Teff = result[0][64] lum = result[0]['lum'] logg = result[0][66] radius = result[0][70] ra = result[0][13] dec = result[0][14] radius_max = result[0][71] radius_min = result[0][71] mass = result[0][72] mass_max = result[0][73] mass_min = result[0][73] if lum is None or np.isnan(lum): lum = self.star_luminosity(Teff, radius) ld, mass, mass_min, mass_max, radius, radius_min, radius_max = tls.catalog_info( TIC_ID=id) return (ld, Teff, lum, logg, radius, radius_min, radius_max, mass, mass_min, mass_max, ra, dec)
def inject_and_tls_search_by_tic(tic_id, periods, rplanets, logfname, SNR_threshold=5., known_transits=None, show_plot=False, save_plot=False): ''' Inputs: ------- tic_id : str or int TIC ID periods : float or array of float a period or list of periods for injections rplanets : float or array of float a planet radius or list of planet radii for injections logfname : str file path and name for the log file SNR_threshold : float the SNR threshold at which to stop the TLS search known_transits : None or dict if dict and one transit is already known: known_transits = {'period':[1.3], 'duration':[2.1], 'epoch':[245800.0]} if dict and multiple transits are already known: known_transits = {'period':[1.3, 21.0], 'duration':[2.1, 4.1], 'epoch':[245800.0, 245801.0]} 'period' is the period of the transit 'duration' must be the total duration, i.e. from first ingress point to last egrees point, in days 'epoch' is the epoch of the transit show_plot : bool show a plot in the terminal or not save_plot : bool save a plot Summary: ------- - retrieves the SPOC PDC-SAP lightcurve - retrieves all TIC catalog information from MAST - injects a planet signal via ellc, for a given period and radius (at random epoch) - runs TLS on these injected data and infos Returns: ------- - a list of all TLS results will get saved to a log file ''' #::: format inputs tic_id = str(int(tic_id)) periods = np.atleast_1d(periods) rplanets = np.atleast_1d(rplanets) #::: load data dic = tessio.get(tic_id) time, flux, flux_err = dic['time'], dic['flux'], dic['flux_err'] #::: load TIC info ab, R_star, R_star_lerr, R_star_uerr, M_star, M_star_lerr, M_star_uerr = catalog_info( TIC_ID=int(tic_id)) print('TICv8 info:') print('Quadratic limb darkening a, b', ab[0], ab[1]) print('Stellar radius', R_star, '+', R_star_lerr, '-', R_star_uerr) print('Stellar mass', M_star, '+', M_star_lerr, '-', M_star_uerr) #::: run inject_and_tls_search(time, flux, flux_err, periods, rplanets, logfname, SNR_threshold=SNR_threshold, known_transits=known_transits, R_star=R_star, R_star_min=R_star - R_star_lerr, R_star_max=R_star + R_star_uerr, M_star=M_star, M_star_min=M_star - M_star_lerr, M_star_max=M_star + M_star_uerr, show_plot=show_plot, save_plot=save_plot)
def featvec(x_axis, sampledata, ticid=None, v=0): """calculates the feature vector of a single light curve version 0: features 0-15 version 1: features 0-19 0 - Average - lower alpha 1 - Variance - upper case beta (B) 2 - Skewness - upper case gamma 3 - Kurtosis - upper case delta 4 - ln variance - lowercase beta 5 - ln skewness - lowercase gamma 6 - ln kurtosis - lowercase delta (over 0.1 to 10 days) 7 - maximum power - upper episolon (E) 8 - ln maximum power - lower epsilon 9 - period of maximum power - upper zeta (Z) 10 - slope - upper eta (H) 11 - ln slope - lower eta (integration of periodogram over time frame) 12 - P0 - 0.1-1 days - upper theta 13 - P1 - 1-3 days - upper iota 14 - P2 - 3-10 days - upper kappa (over 0-0.1 days, for moving objects) 15 - Period of max power - upper lambda (from transitleastsquares, OPTIONAL based on tls argument) 16 - best fit period - upper mu (M) - days 17 - best fit duration - lower mu - days 18 - best fit depth - upper nu (N) - ppt, measured from bottom 19 - power of best fit period - lower nu for title purposes: features_greek = [r'$\alpha$', 'B', r'$\Gamma$', r'$\Delta$', r'$\beta$', r'$\gamma$',r'$\delta$', "E", r'$\epsilon$', "Z", "H", r'$\eta$', r'$\Theta$', "I", "K", r'$\Lambda$', "M", r'$\mu$' ,"N", r'$\nu$'] *** version 1 note: you may wish to go into the transitleastsquares's main.py file and comment out all 'print' statements in order to save space while running this over lots of light curves modified [lcg 07202020]""" #empty feature vector featvec = [] if v == 0: #moments featvec.append( np.mean(sampledata)) #mean (don't use moment, always gives 0) featvec.append(moment(sampledata, moment=2)) #variance featvec.append(moment(sampledata, moment=3)) #skew featvec.append(moment(sampledata, moment=4)) #kurtosis featvec.append(np.log(np.abs(moment(sampledata, moment=2)))) #ln variance featvec.append(np.log(np.abs(moment(sampledata, moment=3)))) #ln skew featvec.append(np.log(np.abs(moment(sampledata, moment=4)))) #ln kurtosis #periods f = np.linspace(0.6, 62.8, 5000) #period range converted to frequencies periods = np.linspace(0.1, 10, 5000) #0.1 to 10 day period pg = signal.lombscargle(x_axis, sampledata, f, normalize=True) rel_maxes = argrelextrema(pg, np.greater) powers = [] indexes = [] for n in range(len(rel_maxes[0])): index = rel_maxes[0][n] indexes.append(index) power_level_at_rel_max = pg[index] powers.append(power_level_at_rel_max) max_power = np.max(powers) index_of_max_power = np.argmax(powers) index_of_f_max = rel_maxes[0][index_of_max_power] f_max_power = f[index_of_f_max] period_max_power = 2 * np.pi / f_max_power featvec.append(max_power) featvec.append(np.log(np.abs(max_power))) featvec.append(period_max_power) slope = stats.linregress(x_axis, sampledata)[0] featvec.append(slope) featvec.append(np.log(np.abs(slope))) #integrates the whole 0.1-10 day range integrating1 = np.trapz(pg[457:5000], periods[457:5000]) #0.1 days to 1 days integrating2 = np.trapz(pg[121:457], periods[121:457]) #1-3 days integrating3 = np.trapz(pg[0:121], periods[0:121]) #3-10 days featvec.append(integrating1) featvec.append(integrating2) featvec.append(integrating3) #for 0.001 to 1 day periods f2 = np.linspace(62.8, 6283.2, 20) #period range converted to frequencies p2 = np.linspace(0.001, 0.1, 20) #0.001 to 1 day periods pg2 = signal.lombscargle(x_axis, sampledata, f2, normalize=True) rel_maxes2 = argrelextrema(pg2, np.greater) powers2 = [] indexes2 = [] for n in range(len(rel_maxes2[0])): index2 = rel_maxes2[0][n] indexes2.append(index2) power_level_at_rel_max2 = pg2[index2] powers2.append(power_level_at_rel_max2) max_power2 = np.max(powers2) index_of_max_power2 = np.argmax(powers2) index_of_f_max2 = rel_maxes2[0][index_of_max_power2] f_max_power2 = f2[index_of_f_max2] period_max_power2 = 2 * np.pi / f_max_power2 featvec.append(period_max_power2) #print("done") #tls elif v == 1: from transitleastsquares import transitleastsquares, period_grid, catalog_info model = transitleastsquares(x_axis, sampledata) if type(ticid) != type(None): dt = np.max(x_axis) - np.min(x_axis) ab, mass, mass_min, mass_max, radius, radius_min, radius_max = catalog_info( TIC_ID=int(ticid)) # >> find smallest period grid rm_set = [] grid_lengths = [period_grid(1, 1, dt).shape[0]] rm_set.append([1, 1]) if not np.isnan(radius): grid_lengths.append(period_grid(radius, 1, dt).shape[0]) rm_set.append([radius, 1]) if not np.isnan(mass): grid_lengths.append(period_grid(1, mass, dt).shape[0]) rm_set.append([1, mass]) ind = np.argmin(grid_lengths) R_star, M_star = rm_set[ind] else: R_star, M_star = 1, 1 results = model.power(show_progress_bar=True, R_star=R_star, M_star=M_star) featvec.append(results.period) featvec.append(results.duration) featvec.append((1 - results.depth)) featvec.append((results.power.max())) return featvec
def tls_search(*args, tic=None, shape='default', star_params=None, rms_calc=True, norm_val=1., clean_lc=False, starparams_out=False, del_dur=1., verbose=False, nthreads=6, **kwargs): #!!Change if flux_err is all nans to replace it with rms flux_err!! """ Function to perform a search for periodic signals using the Transit Least Squares (TLS) algorithm developed by Hippke & Heller 2018. While slower than Box Least Squares, the transit shape used in the search is more realistic. Parameters ---------- *args : `lightkurve.LightCurve` object or multiple numpy array arguments If the len = 1, then the argument is assumed to be a `lightkurve.LightCurve` object with at least two columns, labeled ``'time'`` and ``'flux'``, respectively, with an optional ``'flux_err'`` column as the third column. If the len of > 1, then it is assumed the user is passing ``time``, ``flux``, and ``flux_err`` (optional), respectively. These columns or arguments should be arrays of equal length. tic : int or None TIC ID of the source that the light curve comes from. This will be used to query the TIC for the stellar parameters of the system. May be set to ``None`` if a full dictionary of stellar params are provided to the ``star_params`` keyword. shape : str The shape used by TLS to search for periodic signals. The user may specify ``'default'``, ``'grazing'``, or ``'box'``. See Hippke & Heller 2018 for an in-depth description of these shapes. star_params : dict or None A dictionary containing stellar parameters to be used in the TLS search. The dictionary can contain an array of limb-darkening parameters, stellar radius, lower radius error, upper radius error, stellar mass, lower mass error, and upper mass error labeled ``'ab'``, ``'rstar'``, ``'rlow'``, ``'rhigh'``, ``'mstar'``, ``'mlow'``, and ``'mhigh'``, respectively. The error values are the errors themselves and not the upper and lower values for each of the parameters. A partial list may be included, but in this case, the TIC must also be given. rms_calc : bool A flag to denote whether the root mean square error will be applied in the case that error values are not provided. norm_val : float Value that the light curve is normalized to. Default is 1. Only 1 or 0 are valid normalizations for TLS. clean_lc : bool Flag to indicate whether or not to output a cleaned lightcurve with the recovered periodic signal masked out. Results in an additional expected output. Default ``False``. starparams_out : bool Flag to indicate whether or not to output the dictionary of stellar parameters used in the TLS search. Results in an additional expected output. del_dur : float How many durations worth of data points should be excluded from cleaned light curve centered on the transit center. Default is 1. Values < 1 will result in some in-transit points remaining while values > 1 will remove some points outside the transit. verbose : bool Flag to have function print more while it runs. nthreads : int Number of threads to be used for running the signal search. Many times, cores have the capability to run multiple threads, so be sure to check your machine to optimize this parameter. **kwargs : dict Optional arguments passed to the ``transitleastsquares.power`` function. Returns ------- results : dict Results of the TLS fit. See TLS documentation for the contents of this dictionary and descriptions of each element. cleaned_lc : ``lightkurve.LightCurve`` object, optional A light curve with the transits masked out based on the results of the TLS search. """ dy = None #processing inputs if not tic and (not star_params or len(star_params) != 7): raise ValueError('Either tic or full star_params dictionary must' + ' be given!') if len(args) == 1: lc = args[0] time = lc.time flux = lc.flux try: if lc.flux_err is None: print('No flux errors provided') else: dy = lc.flux_err except: print('No flux errors provided') elif len(args) > 1: time = args[0] flux = args[1] if len(args) == 3: if args[2] is not None: dy = args[2] else: print('No flux_errors provided') else: print('No flux errors provided') if rms_calc == True and not isinstance(dy, np.ndarray): dy = np.ones(len(flux)) * rms(flux, norm_val=norm_val) print('RMS will be used for errors') #get catalog info and/or parse user-provided values if tic and (not star_params or len(star_params) != 7): print(f'Gathering stellar params that were not provided...', end='\r') ab, R_star, R_star_lowe, R_star_highe, M_star, M_star_lowe, M_star_highe = tls.catalog_info(TIC_ID=int(tic)) cat = {'ab' : ab, 'rstar' : R_star, 'rlow' : R_star_lowe, 'rhigh' : R_star_highe, 'mstar' : M_star, 'mlow' : M_star_lowe, 'mhigh' : M_star_highe} if not star_params: star_params = {} missing = list(set(cat) - set(star_params)) star_params.update({k: cat[k] for k in missing}) print('Gathering stellar params that were not provided... Done!') #quality control for stellar params dc = star_params dc['rstar'] = 1.0 if math.isnan(dc['rstar']) else dc['rstar'] dc['mstar'] = 1.0 if math.isnan(dc['mstar']) else dc['mstar'] dc['mlow'] = 0.1 if math.isnan(dc['mlow']) else dc['mlow'] dc['mhigh'] = 0.1 if math.isnan(dc['mhigh']) else dc['mhigh'] dc['rlow'] = 0.1 if math.isnan(dc['rlow']) else dc['rlow'] dc['rhigh'] = 0.1 if math.isnan(dc['rhigh']) else dc['rhigh'] rmax = dc['rstar'] + dc['rhigh'] rmin = dc['rstar'] - dc['rlow'] mmax = dc['mstar'] + dc['mhigh'] mmin = dc['mstar'] - dc['mlow'] if verbose: print('Stellar params used:') for i in list(dc.keys()): print(str(i) + ' = ' + str(dc[i])) print('(defaults are solar and 0.1 for errors)') #beginning TLS search print('Searching using TLS using %s shape...' % shape) model = tls.transitleastsquares(t=time, y=flux, dy=dy) results = model.power(R_star=dc['rstar'], R_star_min=rmin, R_star_max=rmax, M_star=dc['mstar'], M_star_min=mmin, M_star_max=mmax, u=dc['ab'], transit_template=shape, use_threads=nthreads, **kwargs) #cleaning light curve if clean_lc flag if clean_lc: intransit = tls.transit_mask(time, results.period, del_dur * results.duration, results.T0) time2 = time[~intransit] flux2 = flux[~intransit] dy2 = dy[~intransit] time2, flux2, dy2 = tls.cleaned_array(time2, flux2, dy2) lc_clean = LightCurve(time=time2, flux=flux2, flux_err=dy2) if clean_lc and not starparams_out: return results, lc_clean elif not clean_lc and starparams_out: return results, dc elif clean_lc and starparams_out: return results, lc_clean, dc else: return results
def run(): # ************************************************************************************************************************************************************* # Main # ************************************************************************************************************************************************************* # ------------------------------------------------------------------------------------------------------------------------------------------------------------- # Initialization # ------------------------------------------------------------------------------------------------------------------------------------------------------------- # Search of data by lightkurve function concerning the TIC_ID provided # It retrieves all the data (sectors) available. # save in log file the info ab, mass, mass_min, mass_max, radius, radius_min, radius_max = catalog_info( TIC_ID=TIC_ID) #search and donwload the TESS data using lightkurve lcf = lk.search_lightcurvefile('TIC ' + str(TIC_ID), mission="tess").download_all() #we set the minum number of transits based on the sectors available if len(lcf) > 1: n_tra = 2 else: n_tra = 1 # saving usere definitions to keep the track of what have be done already logprint( '\n SHERLOCK (Searching for Hints of Exoplanets fRom Lightcurves Of spaCe-base seeKers)' ) logprint('\n Version ', Version) logprint('\n USER DEFINITIONS') logprint('========================\n') logprint('TIC_ID: ', TIC_ID) if det_met == 2: logprint('Detrend method: Gaussian Process Matern 2/3') else: logprint('Detrend method: Bi-Weight') logprint('No of detrend models applied: ', N_detrends) logprint('No of sectors available: ', len(lcf)) logprint('Minimum number of transits: ', n_tra) logprint('Period planet protected: ', P_protec) logprint('Minimum Period (d): ', Pmin) logprint('Maximum Period (d): ', Pmax) logprint('Binning size (min): ', minutes) if time_units == 1: logprint('Units of time: Barycenter TESS Julian Day') else: logprint('Units of time: Julian Day') if mask == 1: logprint('Mask: yes') else: logprint('Mask: no') logprint('Threshold limit for SNR: ', SNR_min) logprint('Threshold limit for SDE: ', SDE_min) logprint('Threshold limit for FAP: ', FAP_max) # We save here in a log file all the parameters available of the star. mass_min = mass - mass_min mass_max = mass + mass_max radius_min = radius - radius_min radius_max = radius + radius_max logprint('\n STELLAR PROPERTIES FOR THE SIGNAL SEARCH') logprint('================================================\n') logprint('limb-darkening estimates using quadratic LD (a,b)=', ab) logprint('mass =', format(mass, '0.5f')) logprint('mass_min =', format(mass_min, '0.5f')) logprint('mass_max =', format(mass_max, '0.5f')) logprint('radius =', format(radius, '0.5f')) logprint('radius_min =', format(radius_min, '0.5f')) logprint('radius_max =', format(radius_max, '0.5f')) logprint('\n SECTORS INFO') logprint('================================================\n') logprint('Sectors :', lcf) lc = lcf.PDCSAP_FLUX.stitch().remove_nans() # remove of the nans flux = lc.flux flux_err = lc.flux_err if time_units == 0: time = lc.astropy_time.jd #transform from TESS julian date to jd elif time_units == 1: time = lc.time #keep the TESS julian date lc_new = lk.LightCurve(time=time, flux=flux, flux_err=flux_err) flux_clean = lc_new.remove_outliers( sigma_lower=float('inf'), sigma_upper=3) #remove outliers over 3sigma if mask == 1: #first mask (example of how to mask a range) time_in = 1449.8 time_out = 1451.50 mask1 = (flux_clean.time < time_in) | (flux_clean.time > time_out) flux_clean = flux_clean[mask1] time_b = 1463.5 mask2 = (flux_clean.time < time_b) flux_clean = flux_clean[mask2] logprint('** Masking the lightcurve **') else: None # ------------------------------------------------------------------------------------------------------------------------------------------------------------- # Start of the runs # ------------------------------------------------------------------------------------------------------------------------------------------------------------- print( "________________________________ run 1 ________________________________" ) id_run = 1 time_i = flux_clean.time flatten_i = flux_clean.flux results_pdcsap, SNR, key, periods, durations, tos = analyse( det_met, time_i, flatten_i, n_tra, ab, mass, mass_min, mass_max, radius, radius_min, radius_max, id_run) while key == 1: id_run += 1 print("________________________________ run", id_run, "________________________________") SNR = np.nan_to_num(SNR) a = np.nanargmax(SNR) # check the maximum SDE # applying a new mask time_i, flatten_i = add_mask(time_i, flatten_i, results_pdcsap, periods, durations, SNR, tos, a) # analyzing the data with the new mask results_pdcsap, SNR, key, periods, durations, tos = analyse( det_met, time_i, flatten_i, n_tra, ab, mass, mass_min, mass_max, radius, radius_min, radius_max, id_run)
# Remove known planets periods, t0s, tdurs = get_planets(EPIC_id) for no in range(len(periods)): print('Removing planet', periods[no], t0s[no], tdurs[no]) intransit = transit_mask(time, periods[no], 2 * tdurs[no], t0s[no]) flux = flux[~intransit] time = time[~intransit] time, flux = cleaned_array(time, flux) # Detrend data and remove high outliers trend = scipy.signal.medfilt(flux, MEDIAN_KERNEL_WINDOW) flux = flux / trend flux = sigma_clip(flux, sigma_upper=3, sigma_lower=6) # Search flushed data for additional planets ab, mass, mass_min, mass_max, radius, radius_min, radius_max = catalog_info( EPIC_id) model = transitleastsquares(time, flux) results = model.power(n_transits_min=1, u=ab, oversampling_factor=5, duration_grid_step=1.05) print('Signal detection efficiency (SDE):', format(results.SDE, '.1f')) # Figure of power spectrum plt.figure() ax = plt.gca() ax.axvline(results.period, alpha=0.4, lw=3) plt.xlim(numpy.min(results.periods), numpy.max(results.periods)) for n in range(2, 10): ax.axvline(n * results.period, alpha=0.4, lw=1, linestyle="dashed") ax.axvline(results.period / n, alpha=0.4, lw=1, linestyle="dashed")
def fit_and_remove_transits(time, flux, ID=None, mission=None, pl_orbper=None, pl_orbpererr=None, **kwargs): """Fit and subtract transits of a planet from light curves using prior information about the star-planet system. Parameters: ----------- time : n-array time array in units of days flux : n-array flux array mission : str "TESS" or "Kepler" pl_orbper : float orbital period of planet in units of days pl_orbpererr : float uncertainty on orbital period Return: ------- n-array - flux with transit signal removed """ # transit durations in TESS vary from 0.2 hours to over 30 h # at 1 min cadence this would be at least 12 datapoints per transit # which should be fine given that we have # more constraints on the transits given anyways # So: Downsample to 1 min cadence if needed: dt = np.diff(time).mean() one_min = 1. / 60. / 24. if dt < one_min: new_time, new_flux = tls.resample(time, flux / np.median(flux), factor=one_min / dt) else: new_time, new_flux = time, flux / np.median(flux) # Fetch stellar parameters from TESS and Kepler catalogs if mission is not None: if mission == "TESS": kwarg = {"TIC_ID": ID} elif mission == "Kepler": kwarg = {"KIC_ID": ID} ab, mass, mass_min, mass_max, radius, radius_min, radius_max = tls.catalog_info( **kwarg) r = radius rmin = radius - 3 * radius_min rmax = radius + 3 * radius_max m = mass mmin = mass - 3 * mass_min mmax = mass + 3 * mass_max else: # pick defaults ab = [0.4804, 0.1867] # (a G2V star in the Kepler bandpass), m, mmin, mmax = 1, 0.08, 1.3 r, rmin, rmax = 1, 0.13, 1.3 # roughly everything with a convection zone if (pl_orbper is None) | (np.isnan(pl_orbper)): print("No transit duration given.") n_transits_min = 2 period_min, period_max = 0., np.inf else: print(new_time[-1], new_time[0], pl_orbper) n_transits_min = int((new_time[-1] - new_time[0]) // pl_orbper) if pl_orbpererr is None: print("No transit duration uncertainty given.") period_min, period_max = 0.75 * pl_orbper, 1.5 * pl_orbper else: print("Transit duration and uncertainty given.") period_min = pl_orbper - 3 * pl_orbpererr period_max = pl_orbper + 3 * pl_orbpererr # Initialize the LST model model = tls.transitleastsquares(new_time, new_flux) print(new_time, new_flux, new_time.shape, new_flux.shape) print(period_min, period_max, n_transits_min, mmin, mmax, rmin, rmax, n_transits_min) # Run the search with system contraints from the catalogs results = model.power( u=ab, R_star=r, R_star_min=rmin, R_star_max=rmax, M_star=m, M_star_min=mmin, M_star_max=mmax, period_min=period_min, period_max=period_max, n_transits_min=max(n_transits_min, 1), # this could be 1, 0 would throw an error **kwargs) # resample model light curve to original cadence if type(results.model_lightcurve_model) == float: print("Transit not recovered.") return flux, results else: model_flux = np.interp(time, results.model_lightcurve_time, results.model_lightcurve_model) # get the transit mask in_transit = tls.transit_mask(time, results.period, results.duration, results.T0) # get new flux array with transits subtracted notransit_flux = np.copy(flux) notransit_flux[ in_transit] = notransit_flux[in_transit] / model_flux[in_transit] return notransit_flux, results
def recovery(self, cores, simplified=True, sherlock_samples=3): object_info = MissionObjectInfo(self.id, self.sectors) lc, lc_data, star_info, transits_min_count, sectors, quarters = \ MissionLightcurveBuilder().build(object_info, None) ab, mass, massmin, massmax, radius, radiusmin, radiusmax = catalog_info( TIC_ID=id) # units for ellc rstar = star_info.radius * u.R_sun mstar = star_info.mass * u.M_sun mstar_min = star_info.mass_min * u.M_sun mstar_max = star_info.mass_max * u.M_sun rstar_min = star_info.radius_min * u.R_sun rstar_max = star_info.radius_max * u.R_sun print('\n STELLAR PROPERTIES FOR THE SIGNAL SEARCH') print('================================================\n') print('limb-darkening estimates using quadratic LD (a,b)=', ab) print('mass =', format(mstar, '0.5f')) print('mass_min =', format(mstar_min, '0.5f')) print('mass_max =', format(mstar_max, '0.5f')) print('radius =', format(rstar, '0.5f')) print('radius_min =', format(rstar_min, '0.5f')) print('radius_max =', format(rstar_max, '0.5f')) lc_new = lk.LightCurve(time=lc.time, flux=lc.flux, flux_err=lc.flux_err) clean = lc_new.remove_outliers(sigma_lower=float('inf'), sigma_upper=3) flux0 = clean.flux time = clean.time flux_err = clean.flux_err report = {} reports_df = pd.DataFrame(columns=[ 'period', 'radius', 'epoch', 'found', 'snr', 'sde', 'run' ]) known_transits = [{ "T0": 2458684.832891 - 2450000, "P": 3.119035, "D": 1.271571 / 24 }, { "T0": 2458687.539955 - 2450000, "P": 6.387611, "D": 1.246105 / 24 }] inject_dir = self.dir + "/curves/" for file in os.listdir(inject_dir): if file.endswith(".csv"): try: period = float(re.search("P([0-9]+\\.[0-9]+)", file)[1]) r_planet = float(re.search("R([0-9]+\\.[0-9]+)", file)[1]) epoch = float( re.search("_([0-9]+\\.[0-9]+)\\.csv", file)[1]) df = pd.read_csv(inject_dir + file, float_precision='round_trip', sep=',', usecols=['#time', 'flux', 'flux_err']) if len(df) == 0: found = True snr = 20 sde = 20 run = 1 else: lc = lk.LightCurve(time=df['#time'], flux=df['flux'], flux_err=df['flux_err']) clean = lc.remove_nans().remove_outliers( sigma_lower=float('inf'), sigma_upper=3) # remove outliers over 3sigma flux = clean.flux time = clean.time intransit = self.__transit_masks(known_transits, time) found, snr, sde, run = self.__tls_search( time, flux, radius, rstar_min, rstar_max, mass, mstar_min, mstar_max, intransit, epoch, period, 0.5, 45, 5, cores, "default") new_report = { "period": period, "radius": r_planet, "epoch": epoch, "found": found, "snr": snr, "sde": sde, "run": run } reports_df = reports_df.append(new_report, ignore_index=True) print("P=" + str(period) + ", R=" + str(r_planet) + ", T0=" + str(epoch) + ", FOUND WAS " + str(found) + " WITH SNR " + str(snr) + " AND SDE " + str(sde)) reports_df.to_csv(inject_dir + "a_tls_report.csv", index=False) except Exception as e: traceback.print_exc() print("File not valid: " + file) if not simplified: report = {} reports_df = pd.DataFrame(columns=[ 'period', 'radius', 'epoch', 'found', 'snr', 'sde', 'run' ]) a = False tls_report_df = pd.read_csv( inject_dir + "a_tls_report.csv", float_precision='round_trip', sep=',', usecols=['period', 'radius', 'epoch', 'found', 'snr', 'run']) samples_analysed = sherlock_samples for index, row in tls_report_df[::-1].iterrows(): file = os.path.join('P' + str(row['period']) + '_R' + str(row['radius']) + '_' + str(row['epoch']) + '.csv') first_false = index > 0 and tls_report_df.iloc[index - 1]['found'] and \ not tls_report_df.iloc[index]['found'] if first_false: samples_analysed = 0 elif tls_report_df.iloc[index]['found']: samples_analysed = sherlock_samples if samples_analysed < sherlock_samples: try: samples_analysed = samples_analysed + 1 period = float( re.search("P([0-9]+\\.[0-9]+)", file)[1]) r_planet = float( re.search("R([0-9]+\\.[0-9]+)", file)[1]) epoch = float( re.search("_([0-9]+\\.[0-9]+)\\.csv", file)[1]) signal_selection_algorithm = QuorumSnrBorderCorrectedStopWhenMatchSignalSelector( 1, 0, period, epoch) df = pd.read_csv(inject_dir + file, float_precision='round_trip', sep=',', usecols=['#time', 'flux', 'flux_err']) if len(df) == 0: found = True snr = 20 sde = 20 run = 1 else: sherlock.Sherlock(sherlock_targets=[MissionInputObjectInfo(self.id, inject_dir + file)]) \ .setup_detrend(True, True, 1.5, 4, 12, "biweight", 0.2, 1.0, 20, False, 0.25, "cosine", None) \ .setup_transit_adjust_params(5, None, None, 10, None, None, 0.4, 14, 10, 20, 5, 5.5, 0.05, "mask", "quorum", 1, 0, signal_selection_algorithm) \ .run() df = pd.read_csv(self.id.replace(" ", "") + "_INP/candidates.csv", float_precision='round_trip', sep=',', usecols=[ 'curve', 'period', 't0', 'run', 'snr', 'sde', 'rad_p', 'transits' ]) snr = df["snr"].iloc[len(df) - 1] run = df["run"].iloc[len(df) - 1] sde = df["sde"].iloc[len(df) - 1] per_run = 0 found_period = False j = 0 for per in df["period"]: if signal_selection_algorithm.is_harmonic( per, period / 2.): found_period = True t0 = df["t0"].iloc[j] break j = j + 1 right_epoch = False if found_period: for i in range(-5, 5): right_epoch = right_epoch or ( np.abs(t0 - epoch + i * period) < (1. / 24.)) if right_epoch: snr = df["snr"].iloc[j] run = df["run"].iloc[j] sde = df["sde"].iloc[j] break found = right_epoch new_report = { "period": period, "radius": r_planet, "epoch": epoch, "found": found, "sde": sde, "snr": snr, "run": int(run) } reports_df = reports_df.append(new_report, ignore_index=True) reports_df.to_csv(inject_dir + "a_sherlock_report.csv", index=False) print("P=" + str(period) + ", R=" + str(r_planet) + ", T0=" + str(epoch) + ", FOUND WAS " + str(found) + " WITH SNR " + str(snr) + "and SDE " + str(sde)) except Exception as e: print(e) print("File not valid: " + file)
from __future__ import division, print_function import numpy from transitleastsquares import catalog_info if __name__ == "__main__": print("Starting test: catalog_data...") (a, b), mass, mass_min, mass_max, radius, radius_min, radius_max = catalog_info( EPIC_ID=204341806 ) numpy.testing.assert_almost_equal((a, b), (1.1605, -0.1704)) numpy.testing.assert_almost_equal(mass, numpy.nan) numpy.testing.assert_almost_equal(mass_min, numpy.nan) numpy.testing.assert_almost_equal(mass_max, numpy.nan) numpy.testing.assert_almost_equal(radius, numpy.nan) numpy.testing.assert_almost_equal(radius_min, numpy.nan) numpy.testing.assert_almost_equal(radius_max, numpy.nan) (a, b), mass, mass_min, mass_max, radius, radius_min, radius_max = catalog_info( EPIC_ID=204099713 ) numpy.testing.assert_almost_equal((a, b), (0.4804, 0.1867)) numpy.testing.assert_almost_equal(mass, 1.046) numpy.testing.assert_almost_equal(mass_min, 0.898) numpy.testing.assert_almost_equal(mass_max, 0.642) numpy.testing.assert_almost_equal(radius, 1.261) numpy.testing.assert_almost_equal(radius_min, 1.044) numpy.testing.assert_almost_equal(radius_max, 0.925) print("Test passed: EPIC catalog pull from Vizier using astroquery") (a, b), mass, mass_min, mass_max, radius, radius_min, radius_max = catalog_info(
for i in range(-5, 5): right_epoch = right_epoch or (np.abs(tt - epoch + i * period) < ( 1. / 24.)) # check if any epochs matches to within 1 hour # right_depth = (np.abs(np.sqrt(1.-results.depth)*rstar - rplanet)/rplanet < 0.05) #check if the depth matches if right_period and right_epoch: FOUND_SIGNAL = True break run = run + 1 return FOUND_SIGNAL, results.snr, run #::: load data and set the units correctly TIC_ID = 85400193 # TIC_ID of our candidate lcf = lk.search_lightcurvefile('TIC ' + str(TIC_ID), mission="tess").download_all() ab, mass, massmin, massmax, radius, radiusmin, radiusmax = catalog_info(TIC_ID=TIC_ID) # units for ellc rstar = radius * u.R_sun # mass and radius for the TLS # rstar=radius # mstar=mass mstar_min = mass - massmin mstar_max = mass + massmax rstar_min = radius - radiusmin rstar_max = radius + radiusmax dir = "../run_tests/experiment/curves/" results_dir = "../run_tests/experiment/curves/" report = {} reports_df = pd.DataFrame(columns=['period', 'radius', 'epoch', 'found', 'snr', 'run']) for file in os.listdir(dir): if file.endswith(".csv"):
def tls_search_by_tic(tic_id, time=None, flux=None, flux_err=None, SNR_threshold=5., known_transits=None, show_plot=False, save_plot=False, outdir=''): ''' Inputs: ------- tic_id : str TIC ID Optional Inputs: ---------------- time : array of flaot time stamps of observations (to overwrite the SPOC PDC SAP lightcurve) flux : array of flaot normalized flux (to overwrite the SPOC PDC SAP lightcurve) flux_err : array of flaot error of normalized flux (to overwrite the SPOC PDC SAP lightcurve) SNR_threshold : float the SNR threshold at which to stop the TLS search known_transits : None or dict if dict and one transit is already known: known_transits = {'period':[1.3], 'duration':[2.1], 'epoch':[245800.0]} if dict and multiple transits are already known: known_transits = {'period':[1.3, 21.0], 'duration':[2.1, 4.1], 'epoch':[245800.0, 245801.0]} 'period' is the period of the transit 'duration' must be the total duration, i.e. from first ingress point to last egrees point, in days 'epoch' is the epoch of the transit Summary: ------- - retrieves the SPOC PDC-SAP lightcurve - retrieves all TIC catalog information from MAST - runs TLS on these data and infos Returns: ------- - list of all TLS results ''' #::: format inputs tic_id = str(int(tic_id)) #::: load data and inject transit dic = tessio.get(tic_id, pipeline='spoc', PDC=True) if time is None: time = dic['time'] if flux is None: flux = dic['flux'] if flux_err is None: flux_err = dic['flux_err'] #::: load TIC info ab, R_star, R_star_lerr, R_star_uerr, M_star, M_star_lerr, M_star_uerr = catalog_info( TIC_ID=int(tic_id)) print('TICv8 info:') print('Quadratic limb darkening a, b', ab[0], ab[1]) print('Stellar radius', R_star, '+', R_star_lerr, '-', R_star_uerr) print('Stellar mass', M_star, '+', M_star_lerr, '-', M_star_uerr) return tls_search(time, flux, flux_err, R_star=R_star, R_star_min=R_star - R_star_lerr, R_star_max=R_star + R_star_uerr, M_star=M_star, M_star_min=M_star - M_star_lerr, M_star_max=M_star + M_star_uerr, known_transits=known_transits, show_plot=show_plot, save_plot=save_plot, outdir=outdir)