Ejemplo n.º 1
0
 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)
Ejemplo n.º 2
0
 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)
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
 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)
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
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
Ejemplo n.º 7
0
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
Ejemplo n.º 8
0
 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)
Ejemplo n.º 9
0
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)
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
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)
Ejemplo n.º 13
0
        # 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")
Ejemplo n.º 14
0
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
Ejemplo n.º 15
0
 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)
Ejemplo n.º 16
0
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(
Ejemplo n.º 17
0
                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"):
Ejemplo n.º 18
0
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)