Exemple #1
0
def add_mask(time_i, flatten_i, results_pdcsap, periods, durations, tos, a):
    if results_pdcsap.snr > SNR[a]:
        intransit = transit_mask(time_i, results_pdcsap.period,
                                 2 * results_pdcsap.duration,
                                 results_pdcsap.T0)
    else:
        intransit = transit_mask(time_i, periods[a], 2 * durations[a], tos[a])

    flatten_i[intransit] = np.nan
    time_j, flatten_j = cleaned_array(time_i, flatten_i)

    return time_j, flatten_j
Exemple #2
0
 def __transit_masks(self, transit_masks, time):
     result = np.full(len(time), False)
     for mask in transit_masks:
         intransit = transit_mask(time, mask["P"], 2 * mask["D"],
                                  mask["T0"])
         result[intransit] = True
     return result
Exemple #3
0
def mask(time, flux, flux_err, period, duration, T0):
    intransit = transit_mask(time, period, duration, T0)
    time = time[~intransit]
    flux = flux[~intransit]
    if flux_err is not None:
        flux_err = flux_err[~intransit]
        time, flux, flux_err = cleaned_array(time, flux, flux_err)
    else:
        time, flux = cleaned_array(time, flux)
    return time, flux, flux_err
Exemple #4
0
 def __tls_search(self, time, flux, rstar, rstar_min, rstar_max, mass,
                  mstar_min, mstar_max, ab, intransit, epoch, period,
                  min_period, max_period, min_snr, cores, transit_template):
     SNR = 1e12
     FOUND_SIGNAL = False
     time = time[~intransit]
     flux = flux[~intransit]
     time, flux = cleaned_array(time, flux)
     run = 0
     flux = wotan.flatten(time,
                          flux,
                          window_length=0.5,
                          return_trend=False,
                          method='biweight',
                          break_tolerance=0.5)
     #::: search for the rest
     while (SNR >= min_snr) and (not FOUND_SIGNAL):
         model = transitleastsquares(time, flux)
         # R_starx = rstar / u.R_sun
         results = model.power(
             u=ab,
             R_star=rstar,  # rstar/u.R_sun,
             R_star_min=rstar_min,  # rstar_min/u.R_sun,
             R_star_max=rstar_max,  # rstar_max/u.R_sun,
             M_star=mass,  # mstar/u.M_sun,
             M_star_min=mstar_min,  # mstar_min/u.M_sun,
             M_star_max=mstar_max,  # mstar_max/u.M_sun,
             period_min=min_period,
             period_max=max_period,
             n_transits_min=2,
             show_progress_bar=False,
             use_threads=cores,
             transit_template=transit_template)
         SNR = results.snr
         if results.snr >= min_snr:
             intransit_result = transit_mask(time, results.period,
                                             2 * results.duration,
                                             results.T0)
             time = time[~intransit_result]
             flux = flux[~intransit_result]
             time, flux = cleaned_array(time, flux)
             right_period = self.__is_multiple_of(results.period,
                                                  period / 2.)
             right_epoch = False
             for tt in results.transit_times:
                 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, results.SDE, run
Exemple #5
0
def seach_one_koi(KOI):

    print('Working on file', KOI)
    time, flux = loadkoi(str(KOI))

    #KOI = 1206.01
    #print(catalog["KOI"])
    #catalog_KOIs =
    #catalog_KOIs = round(catalog_KOIs, 0)
    #print(catalog_KOIs)

    #print(int(float(KOI)))
    #print(catalog["KOI"].astype(int)[:20])

    row = numpy.argmax(catalog["KOI"].astype(int) == int(float(KOI)))
    KIC = catalog["KIC"][row]
    print('row', row)
    print('KIC', KIC)

    max_t14 = T14(R_s=catalog["R_star"][row],
                  M_s=catalog["mass"][row],
                  P=period_max)
    window = 3 * max_t14

    print('R_star, mass, max_t14, window', catalog["R_star"][row],
          catalog["mass"][row], max_t14, window)

    #"KIC","KOI","KepName","Period","T0","T14","ld1","ld2","R_star","rad_up","rad_down","mass","mass_up","mass_down"
    #10337517,1165.01,,7.053934488,136.357253,0.07185,0.1959,0.5086,0.863,0.073,-0.065,0.854,0.054,-0.045

    #plt.scatter(time, flux, s=1)
    #plt.show()
    #print(time)
    #print(flux)

    if time is not None:

        # Remove known planets
        periods, t0s, tdurs = get_planets(KIC)
        print('periods', periods)
        for no in range(len(periods)):
            print('Removing planet', no + 1, 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)

        print('Next planet is number', no + 2)

        # Detrend data and remove high outliers

        print('Detrending...')
        #print(time)
        #print(flux)
        trend1 = trend(time, flux, window=window, c=5)
        #plt.scatter(time, flux, s=1, color='black')
        #plt.plot(time, trend1, color='red')
        #plt.show()

        #trend = scipy.signal.medfilt(flux, 31)
        #trend = scipy.signal.savgol_filter(trend, 25, 2)
        rawflux = flux.copy()
        rawtime = time.copy()
        y_filt = flux / trend1
        y_filt = sigma_clip(y_filt, sigma_upper=3, sigma_lower=1e10)
        time, y_filt = cleaned_array(time, y_filt)

        #plt.close()
        #plt.scatter(time, y_filt, s=1, color='black')
        #plt.show()

        a = catalog["ld1"][row]
        b = catalog["ld2"][row]
        print('LD ab = ', a, b)
        model = transitleastsquares(time, y_filt)
        results = model.power(
            #n_transits_min=1,
            u=(a, b),
            R_star=catalog["R_star"][row],
            R_star_max=catalog["R_star"][row] + 2 * catalog["rad_up"][row],
            # sign is OK, is negative in catalog:
            R_star_min=catalog["R_star"][row] + 2 * catalog["rad_down"][row],
            M_star=catalog["mass"][row],
            M_star_max=catalog["mass"][row] + 2 * catalog["mass_up"][row],
            M_star_min=catalog["mass"][row] + 2 * catalog["mass_down"][row],
            oversampling_factor=5,
            duration_grid_step=1.05,
            use_threads=1,
            #period_min=4.9,
            #period_max=5.0,
            show_progress_bar=False,
            period_max=period_max,
            T0_fit_margin=0.1)
        tls_worked = True
        #except ValueError:
        #    tls_worked = False

        valid = True
        if tls_worked:
            # Check if phase space has gaps
            bins = numpy.linspace(0, 1, 100)
            digitized = numpy.digitize(results.folded_phase, bins)
            bin_means = [
                results.folded_phase[digitized == i].mean()
                for i in range(1, len(bins))
            ]
            #print('bin_means', bin_means)
            if numpy.isnan(bin_means).any():
                print('Vetting fail! Phase bins contain NaNs')
                valid = False

            if results.distinct_transit_count == 1 and results.transit_count >= 2:
                valid = False
                print(
                    'Vetting fail! results.distinct_transit_count==1 and results.transit_count == 2'
                )
            if results.distinct_transit_count == 2 and results.transit_count >= 3:
                valid = False
                print(
                    'Vetting fail! results.distinct_transit_count==2 and results.transit_count == 3'
                )
            if results.SDE < 8:
                valid = False
                print('Vetting fail! results.SDE < 8', results.SDE)
            if results.snr < 7:
                valid = False
                print('Vetting fail! results.snr < 7', results.snr)

            upper_transit_depths = results.transit_depths + results.transit_depths_uncertainties
            if results.transit_count == 2 and max(upper_transit_depths) > 1:
                valid = False
                print('Vetting fail! 2 transits, only 1 significant')

            upper_transit_depths = results.transit_depths + results.transit_depths_uncertainties
            if results.transit_count == 3 and max(upper_transit_depths) > 1:
                valid = False
                print('Vetting fail! 3 transits, not all 3 significant')

            upper_transit_depths = results.transit_depths + results.transit_depths_uncertainties
            if results.transit_count == 4 and max(upper_transit_depths) > 1:
                valid = False
                print('Vetting fail! 4 transits, not all 4 significant')

            if results.depth < 0.95:
                valid = False
                print('Vetting fail! Transit depth < 0.95', results.depth)

                #if results.SDE > 9:  # 9
                print('Signal detection efficiency (SDE):',
                      format(results.SDE, '.1f'))
                print('SNR:', format(results.snr, '.1f'))
                print('Search completed')

                # Make figure
                #ttime.sleep(1)

            #valid = True

            if valid:
                print('Valid result, attempting figure...')
                make_figure(KOI=str(KOI),
                            planet_number=no + 2,
                            results=results,
                            t=time,
                            y=flux,
                            y_filt=y_filt,
                            trend=trend1,
                            rawtime=rawtime,
                            rawflux=rawflux)
                #ttime.sleep(1)
                #print('Figure made')
            else:
                print('No figure made, vetting failed!')
        else:
            print('TLS failed')

        process = psutil.Process(os.getpid())
        ram_mb = process.memory_info().rss / 2**20
        print('RAM (MB)', ram_mb)
        if ram_mb > 25000:
            sys.exit()
Exemple #6
0
def make_figure(KOI, planet_number, results, t, y, y_filt, trend, rawtime,
                rawflux):

    fig = plt.figure(figsize=(10, 14))
    G = gridspec.GridSpec(9, 3)
    G.update(hspace=0)

    radius = catalog["R_star"][row]
    radius_max = catalog["rad_up"][row]
    radius_min = catalog["rad_down"][row]
    mass = catalog["mass"][row]
    mass_max = catalog["mass_up"][row]
    mass_min = catalog["mass_down"][row]

    # Raw flux
    axes_1a = plt.subplot(G[0:1, :2])
    plt.plot(rawtime, rawflux / numpy.mean(rawflux), "k", linewidth=0.5)
    plt.plot(rawtime, trend / numpy.mean(y), color='red', linewidth=0.5)
    plt.xlim(min(t), max(t))
    plt.xticks(())
    plt.ylabel(r'Raw Flux')

    ppm_flux = -(1 - y_filt) * 10**6

    # Detrended flux
    G = gridspec.GridSpec(9, 3)
    G.update(hspace=0.4)
    axes_1b = plt.subplot(G[1:2, :2])
    plt.plot(t, ppm_flux, "k", linewidth=0.5)

    top_ppm = -(1 - max(y_filt)) * 10**6
    bottom_ppm = -(1 - min(y_filt)) * 10**6
    y_range = abs(bottom_ppm) + abs(top_ppm)
    x_range = max(t) - min(t)

    offset_y = bottom_ppm + y_range / 40
    offset_x = min(t) + x_range / 80
    #print(top_ppm, bottom_ppm, y_range, offset_y)

    std = numpy.std(ppm_flux)
    text = r'$\sigma=$' + format(std, '.0f') + r'$\,$ppm'
    #print(bottom_ppm, top_ppm, offset, offset)
    plt.text(offset_x, offset_y, text, color='red')

    plt.ylabel(r'Flux (ppm)')
    plt.xlabel('Time (BKJD, days)')
    plt.xlim(min(t), max(t))

    # Phase fold
    axes_2 = plt.subplot(G[2, 0:2])
    plt.plot((results.model_folded_phase - 0.5) * results.period,
             -(1 - results.model_folded_model) * 10**6,
             color='red',
             zorder=99)
    plt.scatter((results.folded_phase - 0.5) * results.period,
                -(1 - results.folded_y) * 10**6,
                color='black',
                s=2,
                alpha=0.5,
                zorder=2)
    plt.xlim(-2 * results.duration, 2 * results.duration)
    plt.xlabel('Time from mid-transit (days)')
    plt.ylabel('Flux')
    plt.ylim(
        (numpy.percentile(-(1 - y_filt) * 10**6,
                          0.1), numpy.percentile(-(1 - y_filt) * 10**6, 99.5)))
    plt.text(-1.95 * results.duration,
             numpy.percentile(-(1 - y_filt) * 10**6, 0.1), 'primary')
    plt.plot((results.folded_phase - 0.5) * results.period,
             -(1 - running_mean_equal_length(results.folded_y, 10)) * 10**6,
             color='blue',
             linestyle='dashed',
             linewidth=1,
             zorder=3)

    # Phase fold to secondary eclipse
    G = gridspec.GridSpec(9, 3)
    G.update(hspace=0.3)
    axes_3 = plt.subplot(G[2, 2])
    plt.yticks(())
    phases = fold(time=t, period=results.period, T0=results.T0)
    sort_index = numpy.argsort(phases, kind="mergesort")
    phases = phases[sort_index]
    flux = y_filt[sort_index]
    plt.scatter((phases - 0.5) * results.period,
                flux,
                color='black',
                s=2,
                alpha=0.5,
                zorder=2)
    plt.plot((-0.5, 0.5), (1, 1), color='red')
    plt.xlim(-2 * results.duration, 2 * results.duration)
    plt.ylim(numpy.percentile(y_filt, 0.1), numpy.percentile(y_filt, 99.5))
    plt.plot((phases - 0.5) * results.period,
             running_mean_equal_length(flux, 10),
             color='blue',
             linestyle='dashed',
             linewidth=1,
             zorder=3)
    # Calculate secondary eclipse depth
    intransit_secondary = numpy.where(
        numpy.logical_and(
            (phases - 0.5) * results.period > (-0.5 * results.duration),
            (phases - 0.5) * results.period < (0.5 * results.duration)))
    mean = -(1 - numpy.mean(flux[intransit_secondary])) * 10**6
    stabw = -(numpy.std(flux[intransit_secondary]) /
              numpy.sqrt(len(flux[intransit_secondary]))) * 10**6
    significance_eb = mean / stabw

    #print(mean, stabw, significance)
    plt.scatter((phases[intransit_secondary] - 0.5) * results.period,
                flux[intransit_secondary],
                color='orange',
                s=20,
                alpha=0.5,
                zorder=0)
    if numpy.isnan(mean):
        mean = 0
    if numpy.isnan(stabw):
        stabw = 0
    if numpy.isnan(significance_eb):
        significance_eb = 99
    text = r'secondary ' + str(
        int(mean)) + r'$\pm$' + str(int(stabw)) + ' ppm (' + format(
            significance_eb, '.1f') + r'$\,\sigma$)'
    plt.text(-1.95 * results.duration, numpy.percentile(y_filt, 0.1), text)
    print('secondary vespa', (mean - stabw) * 10**-6)
    # Full phase
    G = gridspec.GridSpec(9, 3)
    G.update(hspace=1)
    axes_5 = plt.subplot(G[3, :])
    plt.plot(results.model_folded_phase,
             -(1 - results.model_folded_model) * 10**6,
             color='red')
    plt.scatter(results.folded_phase,
                -(1 - results.folded_y) * 10**6,
                color='black',
                s=2,
                alpha=0.5,
                zorder=2)
    plt.xlim(0, 1)
    plt.ylim(numpy.percentile(ppm_flux, 0.1), numpy.percentile(ppm_flux, 99.9))
    plt.xlabel('Phase')
    plt.ylabel('Flux')

    # Check if phase gaps
    phase_diffs = abs(results.folded_phase -
                      numpy.roll(results.folded_phase, -1))
    phase_diffs = phase_diffs[:-1]
    largest_phase_peak = max(phase_diffs) / numpy.median(phase_diffs)

    # All transits in time series
    G = gridspec.GridSpec(9, 3)
    G.update(hspace=0)
    axes_6b = plt.subplot(G[4, :])
    y_filt = -(1 - y_filt) * 10**6
    in_transit = transit_mask(t, results.period, results.duration, results.T0)

    t_diff1 = abs(t - numpy.roll(t, -1))
    t_diff2 = abs(t - numpy.roll(t, +1))
    if max(t_diff1[in_transit]) > 0.1 or max(
            t_diff2[in_transit]) > 0.1:  # in days
        transit_near_gaps = True
        plt.text(min(t), numpy.percentile(ppm_flux, 0.1),
                 'Warning: Transits near gaps')
    else:
        transit_near_gaps = False

    transit_touches_edge = False
    if max(t) in t[in_transit]:
        plt.text(min(t), numpy.percentile(ppm_flux, 0.1),
                 'Warning: Last transit touches end')
        transit_touches_edge = True
    if max(t) in t[in_transit]:
        plt.text(min(t), numpy.percentile(ppm_flux, 0.1),
                 'Warning: First transit touches start')
        transit_touches_edge = True
    plt.scatter(t[in_transit], y_filt[in_transit], color='red', s=2, zorder=0)
    plt.scatter(t[~in_transit],
                y_filt[~in_transit],
                color='black',
                alpha=0.5,
                s=2,
                zorder=0)
    plt.plot(results.model_lightcurve_time,
             -(1 - results.model_lightcurve_model) * 10**6,
             alpha=0.5,
             color='red',
             zorder=1)
    plt.xlim(min(t), max(t))
    plt.ylim(numpy.percentile(ppm_flux, 0.1), numpy.percentile(ppm_flux, 99.9))
    plt.xlabel('Flux')
    plt.ylabel('Flux')
    plt.xticks(())

    # Transit depths error bars
    avg = -(1 - results.depth_mean[0]) * 10**6
    if numpy.isnan(results.transit_depths_uncertainties).any():
        step = 0
    else:
        step = max(results.transit_depths_uncertainties) * 10**6
    down = avg - step
    G = gridspec.GridSpec(9, 3)
    G.update(hspace=1)
    axes_6 = plt.subplot(G[5, :])
    plt.errorbar(results.transit_times,
                 -(1 - results.transit_depths) * 10**6,
                 yerr=step,
                 fmt='o',
                 color='red')
    plt.plot((min(t), max(t)), (0, 0), color='black')
    plt.plot((min(t), max(t)), (avg, avg), color='black', linestyle='dashed')
    plt.xlim(min(t), max(t))
    for transit in range(len(results.transit_times)):
        plt.text(results.transit_times[transit],
                 down,
                 str(int(results.per_transit_count[transit])),
                 horizontalalignment='center')
        #print(str(int(results.per_transit_count[transit])))
    plt.xlabel('Time (BKJD, days)')
    plt.ylabel('Flux')

    # Test statistic
    G = gridspec.GridSpec(9, 3)
    G.update(hspace=0)
    #G.update(hspace=3)
    axes_7 = plt.subplot(G[6, :])
    axes_7.axvline(results.period, alpha=0.4, lw=3)
    for n in range(2, 5):
        axes_7.axvline(n * results.period, alpha=0.4, lw=1, linestyle="dashed")
        axes_7.axvline(results.period / n, alpha=0.4, lw=1, linestyle="dashed")
    plt.ylabel(r'TLS SDE')
    plt.xticks(())
    plt.plot(results.periods, results.power, color='black', lw=0.5)
    plt.xlim(0, max(results.periods))
    plt.text(results.period + 1, results.SDE * 0.9,
             'SDE=' + format(results.SDE, '.1f'))

    # LS periodogram raw
    #freqs, lspower = LombScargle(rawtime, rawflux).autopower()
    freqs = numpy.geomspace(1 / min(results.periods), 1 / max(results.periods),
                            10000)
    freqs = numpy.unique(freqs)
    #print(freqs)
    lspower = LombScargle(rawtime, rawflux).power(freqs)
    freqs = 1 / freqs
    #for i in range(len(freqs)):
    #    print(freqs[i], lspower[i])
    #print('max(results.periods)', max(results.periods))
    #idx_end = numpy.argmax(freqs<max(results.periods))
    #print(lspower)
    #print('idx_end', idx_end)
    #print('idx_end2', numpy.argmax(freqs<5))
    #freqs = freqs[:idx_end]
    #lspower = lspower[:idx_end]
    #print(lspower)
    #continuum = numpy.std(lspower)
    #lspower = numpy.divide(lspower, continuum)  # Normalize to S/N
    #print(lspower)

    G = gridspec.GridSpec(9, 3)
    G.update(hspace=0)
    #G.update(hspace=0)
    axes_8 = plt.subplot(G[7, :])
    axes_8.axvline(results.period, alpha=0.4, lw=15)
    #axes_8.set_yscale("log")

    plt.ylabel(r'LS (raw)')
    plt.plot(freqs, lspower, color='black', lw=0.5)
    plt.xlim(0, max(results.periods))
    plt.ylim(0, max(lspower) * 1.1)
    plt.xlabel('Period (days)')
    #idx_start = numpy.argmax(freqs>results.period-2)
    #idx_end = numpy.argmax(freqs>results.period+2)
    """
    lspower_at_period = max(lspower[idx_start:idx_end])
    plt.text(results.period+1, max(lspower)*0.9, 'LS power=' + format(lspower_at_period, '.1f'))
    if lspower_at_period > results.SDE:
        text = 'sinusoidal model preferred'
    else:
        text = 'transit model preferred'
    plt.text(results.period+1, max(lspower)*0.8, text)
    """

    # Calculate Lomb-Scargle periodogram detrended
    freqs = numpy.geomspace(1 / min(results.periods), 1 / max(results.periods),
                            10000)
    freqs = numpy.unique(freqs)
    lspower = LombScargle(t, y_filt).power(freqs)
    freqs = 1 / freqs

    #freqs, lspower = LombScargle(t, y_filt).autopower()
    #idx_end = numpy.argmax(freqs>max(results.periods))
    #freqs = freqs[:idx_end]
    #lspower = lspower[:idx_end]
    continuum = numpy.std(lspower)
    lspower = numpy.divide(lspower, continuum)  # Normalize to S/N

    G = gridspec.GridSpec(9, 3)
    G.update(hspace=0)
    #G.update(hspace=3)
    axes_9 = plt.subplot(G[8, :])
    peak_index = numpy.argmax(lspower)
    #print(peak_index, freqs[peak_index])
    axes_9.axvline(freqs[peak_index], alpha=0.4, lw=10)
    plt.ylabel(r'LS (detrended)')
    plt.plot(freqs, lspower, color='black', lw=0.5)
    plt.xlim(0, max(results.periods))
    plt.ylim(0, max(lspower) * 1.1)
    plt.xlabel('Period (days)')

    #plt.text(freqs[peak_index]+1, max(lspower)*0.9, 'LS power=' + format(max(lspower), '.1f'))
    #if max(lspower) > results.SDE:
    #    text = 'sinusoidal model preferred'
    #    model_preferred = 'sine'
    #else:
    #    text = 'transit model preferred'
    #    model_preferred = 'transit'
    #plt.text(results.period+1, max(lspower)*0.8, text)

    # Text
    G = gridspec.GridSpec(9, 3)
    G.update(hspace=0, wspace=0.1)
    axes_8 = plt.subplot(G[0:2, 2])
    plt.xticks(())
    plt.yticks(())
    plt.text(0,
             0,
             'KOI ' + str(KOI) + '.0' + str(planet_number),
             fontweight='bold')

    #from astroquery.vizier import Vizier
    #result = Vizier(columns=["Kpmag", "Dist"]).query_constraints(ID=EPIC_id, catalog="IV/34/epic")[0].as_array()
    #Kpmag = result[0][0]
    #Dist = result[0][1]
    #plt.text(0, -0.15, 'KPmag=' + format(Kpmag, '.1f') + ', d=' + format(Dist, '.0f') + ' pc')

    plt.text(
        0, -0.30, 'SDE=' + format(results.SDE, '.1f') + ', SNR=' +
        format(results.snr, '.1f'))
    plt.text(
        0, -0.45, 'P=' + format(results.period, '.5f') + ' +-' +
        format(results.period_uncertainty, '.5f') + r'$\,$d')
    plt.text(0, -0.60,
             r'$T_{dur}=$' + format(results.duration, '.5f') + r'$\,$d')
    plt.text(
        0, -0.75,
        r'$R_*=$' + format(radius, '.2f') + ' (+' + format(radius_max, '.2f') +
        ', -' + format(radius_min, '.2f') + ') $\,R_{\odot}$')
    plt.text(
        0, -0.90,
        r'$M_*=$' + format(mass, '.2f') + ' (+' + format(mass_max, '.2f') +
        ', -' + format(mass_min, '.2f') + ') $\,M_{\odot}$')
    plt.text(0, -1.05, r'$R_P/R_*=$' + format(results.rp_rs, '.3f'))

    print('rp_rs', results.rp_rs)

    rp = results.rp_rs * radius  # in solar radii
    sun = 695700
    earth = 6371
    jupi = 69911
    rp_earth = (sun / earth) * rp
    rp_jupi = (sun / jupi) * rp
    plt.text(
        0, -1.20, r'$R_P=$' + format(rp_earth, '.2f') + '$\,R_{\oplus}=$' +
        format(rp_jupi, '.2f') + '$\,R_{Jup}$')
    plt.text(
        0, -1.35,
        r'$\delta=$' + \
        format((1-results.depth_mean[0])*10**6, '.0f') + ' ppm (' + \
        format((1-results.depth_mean_odd[0])*10**6, '.0f') + ', ' + \
        format((1-results.depth_mean_even[0])*10**6, '.0f') + ')')
    plt.text(
        0, -1.50, r'odd/even mismatch: ' +
        format(results.odd_even_mismatch, '.2f') + '$\,\sigma$')
    plt.text(
        0, -1.65,
        str(results.distinct_transit_count) + '/' +
        str(results.transit_count) + ' transits with data')
    plt.xlim(-0.1, 2)
    plt.ylim(-2, 0.1)
    axes_8.axis('off')
    plt.subplots_adjust(hspace=0.4)

    # Additional vetting criteria
    valid = True

    #if largest_phase_peak > 10:
    #    valid = False
    #    print('Vetting fail! largest_phase_peak > 10 median cadences: ', largest_phase_peak)

    if abs(significance_eb) > 3:
        valid = False
        print('Vetting fail! significance_eb > 3')

    #if numpy.isnan(results.odd_even_mismatch):
    #    valid = False
    #    print('Vetting fail! numpy.isnan(results.odd_even_mismatch)')

    if abs(results.odd_even_mismatch) > 3:
        valid = False
        print('Vetting fail! odd_even_mismatch larger 3 sigma')

    if transit_near_gaps and results.distinct_transit_count < 4:
        valid = False
        print('Vetting fail! Transit near gaps and distinct_transit_count < 4')

    #if model_preferred != 'transit':
    #    valid = False
    #    print('Sinusoidal model was preferred over transit model')
    #if max(lspower) > results.SDE:
    #    valid = False
    #    print('Vetting fail! Sinusoidal model preferred')

    #if transit_touches_edge:
    #    valid = False
    #    print('Vetting fail! First or last transit touches edge of time series')
    #valid = True
    if valid:
        figure_out_path = str(KOI) + '_0' + str(planet_number)
        plt.savefig(figure_out_path + '.png', bbox_inches='tight')
        #plt.savefig(figure_out_path + '.pdf', bbox_inches='tight')
        print('Figure made:', figure_out_path)
        """
        print('Creating full CSV result file')
        numpy.savetxt(
            fname=str(EPIC_id) + '_statistics.csv',
            X=numpy.array([EPIC_id, results.period, results.T0]),
            delimiter=',',
            newline=" ",
            fmt='%.5f',
            )
        """
    else:
        print('Vetting criteria failed! No figure made.')
    #plt.close()
    print('T0', results.T0)

    return valid
Exemple #7
0
def tls_vetsheet(lc, results=0, show=True, save=False, savename='vetsheet.png'):
    #!!Add functionality to plot without having to run search?!!
    #!!Add vertical lines for expected transit times on unprocessed LC!!
    #!!Change x-axis phase values for odd-even plot!!
    #!!Make processed and unprocessed x-axes line up, especially when LC is 
    #  masked. Maybe include grayed-out parts that were masked!!
    """
    Function to plot a vetting sheet after running the 
    transit_tools.signal_search function. Output is formatted to fit onto a
    standard 8"x11" sheet of paper.

    Parameters
    ----------
    lc : `transit_tools.lightcurve` object
       Input transit_tools `transit_tools.lightcurve` object that has had a 
       TLS signal search performed on it.
    results : int
       Index of results attribute array of provided 'lightcurve' object. 
       Indicates which set of results to plot if signal_search produced more
       than one set of output results. Can be set to -1 to display the most
       recent signal run that did not meet the significance threshold.
    show : bool or str
       Flag to determine whether plots will be displayed or not. Must be set to
       False or 'both' for output matplotlib object to be expected.
    save : bool
       Flag to determine whether the plots will be saved as a PNG.
    savename : str or None
       File name for plots to be saved as if save is set to True.

    Returns
    -------
    plots : matplotlib object
       Output matplotlib plot object. Optional if show is set to False or 
       'both'.
    """
    if results == -1 and len(lc.results) > 0:
        time = lc.cleanlc[-1].time.value
        flux = lc.cleanlc[-1].flux.value
        flux_err = lc.cleanlc[-1].flux_err.value
    elif len(lc.results) > 0:
        res = lc.results[results]

    if results == -1:
        res = lc.bad_search[0]

    if results == 0 or len(lc.results) == 0:
        time = lc.time.value
        flux = lc.flux.value
        flux_err = lc.flux_err.value
    else:
        time = lc.cleanlc[results-1].time.value
        flux = lc.cleanlc[results-1].flux.value
        flux_err = lc.cleanlc[results-1].flux_err.value
    
    #setting up figure
    fig = plt.figure(figsize=(8, 9.2))
    gs = fig.add_gridspec(5, 2)

    #phase-folded light curve with transit model
    ax = fig.add_subplot(gs[0, 1])

    fold = LightCurve(res.folded_phase, res.folded_y).bin(20)
    
    ax.plot(res.model_folded_phase, res.model_folded_model, color='red',
            alpha=0.7)
    ax.scatter(res.folded_phase, res.folded_y, color='blue', s=0.2, alpha=0.5,
            zorder=2)
    ax.scatter(fold.time.value, fold.flux.value, s=2., c='k')
    
    ax.set_xlim(0.45, 0.55)
    ax.set_xlabel('Phase')
    ax.set_ylabel('Relative Flux')

    #raw light curve
    ax = fig.add_subplot(gs[1, :])

    ax.scatter(lc.raw_lc.time.value, lc.raw_lc.flux.value, s=0.2, c='b',
               alpha=0.5)
    
    ax.set_xlabel('Time [BTJD]')
    ax.set_ylabel('Raw Relative Flux')
    ax.set_xlim(lc.raw_lc.time.value.min(), lc.raw_lc.time.value.max())

    if hasattr(lc, 'trend'):
        plt.plot(lc.trend.time.value, lc.trend.flux.value, c='g')

    #processed light curve with transit model
    ax = fig.add_subplot(gs[2, :])

    transit_time = transit_mask(time, res.period, res.duration, res.T0)
    time_notrans = time[~transit_time]
    flux_notrans = flux[~transit_time]
    flux_err_notrans = flux_err[~transit_time]

    ax.scatter(time[transit_time], flux[transit_time], color='red', s=0.2,
               zorder=0)
    ax.scatter(time[~transit_time], flux[~transit_time], color='blue',
               alpha=0.5, s=0.2, zorder=0)
    ax.plot(res.model_lightcurve_time, res.model_lightcurve_model, alpha=0.5,
            color='red', zorder=1)
    ax.set_xlim(time.min(), time.max())
    ax.set_ylim(flux.min()-0.001, flux.max()+0.001)
    ax.set_xlabel('Time [BTJD]')
    ax.set_ylabel('Relative Flux')
    
    #TLS periodogram
    ax = fig.add_subplot(gs[3, :])
    
    ax.axvline(res.period, alpha=0.4, lw=3)
    ax.set_xlim(np.min(res.periods), np.max(res.periods))
    for n in range(2, 10):
        ax.axvline(n * res.period, alpha=0.4, lw=1, linestyle='dashed')
        ax.axvline(res.period / n, alpha=0.4, lw=1, linestyle='dashed')

    ax.set_ylabel('SDE')
    ax.set_xlabel('Period [days]')
    ax.plot(res.periods, res.power, color='k', lw=0.5)
    ax.set_xlim(0, max(res.periods))

    #secondary eclipse
    ax = fig.add_subplot(gs[4, 0])

    ax.plot(res.model_folded_phase-0.5,
            np.roll(res.model_folded_model, len(res.model_folded_model)//2),
            color='red')
    ax.scatter(res.folded_phase-0.5,
               np.roll(res.folded_y, len(res.folded_y)//2), color='blue',
               s=0.2, alpha=0.5, zorder=2)
    ax.set_xlim(-0.05, 0.05)
    ax.set_xlabel('Phase')
    ax.set_ylabel('Relative Flux')

    ax.axvspan(-(res.duration/2/res.period), (res.duration/2/res.period),
               alpha=0.3, color='orange', label='Transit Duration')

    ax.legend(loc=2, fontsize='x-small')
    
    #Odd-Even comparison
    ax = fig.add_subplot(gs[4, 1])

    oe = LightCurve(time, flux, flux_err)

    oe_odd = oe.fold(2*res.period, res.T0)
    oe_even = oe.fold(2*res.period, res.T0+res.period)

    ax.scatter(oe_odd.time.value+0.5, oe_odd.flux.value, s=0.2, c='b', label='Odd')
    ax.scatter(oe_even.time.value+0.5, oe_even.flux.value, s=0.2, c='r', label='Even')
    
    ax.set_xlim(0.475, 0.525)
    ax.set_xlabel('Phase')
    ax.set_ylabel('Relative Flux')
    ax.set_xticks([0.48, 0.49, 0.50, 0.51, 0.52])
    ax.set_xticklabels(['0.46', '0.48', '0.50', '0.52', '0.54'])
    ax.legend(loc=2, fontsize='x-small')
    
    #plot summary text
    fig.text(0.12, 0.97, s=(str(lc.name) + '  (TIC ' + str(lc.tic) + ')'),
             fontweight='bold')
    fig.text(0.04, 0.95,
             s=(r'P = %.5f +/- %.5f d, $t_{0}$ = %.5f BTJD' %
                (res.period, res.period_uncertainty, res.T0)))
    fig.text(0.04, 0.93,
             s=(r'$T_{dur}$ = %.5f d,  %s/%s transits with data' %
                (res.duration, res.distinct_transit_count, res.transit_count)))
    fig.text(0.04, 0.91,
             s=('SDE = %.2f,  SNR = %.2f,  FAP = %.3e' %
                (res.SDE, res.snr, res.FAP)))
    fig.text(0.04, 0.89,
             s=(r'$R_{P}$/$R_{*}$ = %.4f,  $R_{P}$ = %.3f $R_{\bigoplus}$ = %.3f $R_{Jup}$' %
                (res.rp_rs,
                 (lc.star_params_tls['rstar']*res.rp_rs*c.Rsolar_m)/c.Rearth_m,
                 (lc.star_params_tls['rstar']*res.rp_rs*c.Rsolar_m)/c.Rjup_m)))
    fig.text(0.04, 0.87,
             s=(r'odd/even mismatch = %.2f $\sigma$,  $\delta$ = %.4f' %
                (res.odd_even_mismatch, 1-res.depth)))
    fig.text(0.04, 0.85,
             s=(r'$R_{*}$ = %.2f (+%.2f, -%.2f) $R_{\bigodot}$'
                % (lc.star_params_tls['rstar'], lc.star_params_tls['rhigh'],
                   lc.star_params_tls['rlow'])))
    fig.text(0.04, 0.83, s=(r'$M_{*}$ = %.2f (+%.2f, -%.2f) $M_{\bigodot}$' %
                            (lc.star_params_tls['mstar'],
                             lc.star_params_tls['mhigh'],
                             lc.star_params_tls['mlow'])))
    if lc.star_params is not None and lc.star_params['Tmag'] is not None:
        fig.text(0.04, 0.81, s=('Tmag = %.2f' % (lc.star_params['Tmag'])))
    
    plt.tight_layout()
    
    if show:
        plt.show()

    if save:
        plt.savefig(savename)
Exemple #8
0
def mask(time, flux, period, duration, T0):
    intransit = transit_mask(time, period, duration, T0)
    time = time[~intransit]
    flux = flux[~intransit]
    time, flux = cleaned_array(time, flux)
    return time, flux
Exemple #9
0
def tls_search(time, flux, epoch, period, rplanet, min_snr, transit_template):
    SNR = 1e12
    FOUND_SIGNAL = False

    #::: mask out the first detection at 6.2 days, with a duration of 2.082h, and T0=1712.54922
    # intransit = transit_mask(time, 6.26391, 2*2.082/24., 1712.54922)
    # time = time[~intransit]
    # flux = flux[~intransit]
    time, flux = cleaned_array(time, flux)
    run = 0
    flux = wotan.flatten(time, flux, window_length=0.25, return_trend=False, method='biweight',
                  break_tolerance=0.5)
    #::: search for the rest
    while (SNR >= min_snr) and (not FOUND_SIGNAL):
        model = transitleastsquares(time, flux)
        R_starx = rstar / u.R_sun
        results = model.power(u=ab,
                              R_star=radius,  # rstar/u.R_sun,
                              R_star_min=rstar_min,  # rstar_min/u.R_sun,
                              R_star_max=rstar_max,  # rstar_max/u.R_sun,
                              M_star=mass,  # mstar/u.M_sun,
                              M_star_min=mstar_min,  # mstar_min/u.M_sun,
                              M_star_max=mstar_max,  # mstar_max/u.M_sun,
                              period_min=0.5,
                              period_max=14,
                              n_transits_min=2,
                              show_progress_bar=False,
                              use_threads=20,
                              transit_template=transit_template
                              )

        # 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+raduismax
        SNR = results.snr
        if results.snr >= min_snr:
            intransit = transit_mask(time, results.period, 2 * results.duration, results.T0)
            time = time[~intransit]
            flux = flux[~intransit]
            time, flux = cleaned_array(time, flux)

            #::: check if it found the right signal
            right_period = IsMultipleOf(results.period,
                                        period / 2.)  # check if it is a multiple of half the period to within 5%

            right_epoch = False
            for tt in results.transit_times:
                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
Exemple #10
0
def make_figure(KOI,
                planet_number,
                results,
                t,
                y,
                y_filt,
                trend,
                rawtime,
                rawflux,
                catalog,
                row,
                valid=False):
    fig = plt.figure(figsize=(10, 14))
    G = gridspec.GridSpec(9, 3)
    G.update(hspace=0)
    radius = catalog["R_star"][row]
    radius_max = catalog["rad_up"][row]
    radius_min = catalog["rad_down"][row]
    mass = catalog["mass"][row]
    mass_max = catalog["mass_up"][row]
    mass_min = catalog["mass_down"][row]

    # Raw flux
    axes_1a = plt.subplot(G[0:1, :2])
    plt.plot(rawtime, rawflux / numpy.mean(rawflux), "k", linewidth=0.5)
    plt.plot(rawtime, trend / numpy.mean(y), color='red', linewidth=0.5)
    plt.xlim(min(t), max(t))
    plt.xticks(())
    plt.ylabel(r'Raw Flux')

    ppm_flux = -(1 - y_filt) * 10**6

    # Detrended flux
    G = gridspec.GridSpec(9, 3)
    G.update(hspace=0.4)
    axes_1b = plt.subplot(G[1:2, :2])
    plt.plot(t, ppm_flux, "k", linewidth=0.5)

    top_ppm = -(1 - max(y_filt)) * 10**6
    bottom_ppm = -(1 - min(y_filt)) * 10**6
    y_range = abs(bottom_ppm) + abs(top_ppm)
    x_range = max(t) - min(t)

    offset_y = bottom_ppm + y_range / 40
    offset_x = min(t) + x_range / 80

    std = numpy.std(ppm_flux)
    text = r'$\sigma=$' + format(std, '.0f') + r'$\,$ppm'
    plt.text(offset_x, offset_y, text, color='red')

    plt.ylabel(r'Flux (ppm)')
    plt.xlabel('Time (BKJD, days)')
    plt.xlim(min(t), max(t))

    # Phase fold
    axes_2 = plt.subplot(G[2, 0:2])
    plt.plot((results.model_folded_phase - 0.5) * results.period,
             -(1 - results.model_folded_model) * 10**6,
             color='red',
             zorder=99)
    plt.scatter((results.folded_phase - 0.5) * results.period,
                -(1 - results.folded_y) * 10**6,
                color='black',
                s=2,
                alpha=0.5,
                zorder=2)
    plt.xlim(-2 * results.duration, 2 * results.duration)
    plt.xlabel('Time from mid-transit (days)')
    plt.ylabel('Flux')
    plt.ylim(
        (numpy.percentile(-(1 - y_filt) * 10**6,
                          0.1), numpy.percentile(-(1 - y_filt) * 10**6, 99.5)))
    plt.text(-1.95 * results.duration,
             numpy.percentile(-(1 - y_filt) * 10**6, 0.1), 'primary')
    plt.plot((results.folded_phase - 0.5) * results.period,
             -(1 - running_mean_equal_length(results.folded_y, 10)) * 10**6,
             color='blue',
             linestyle='dashed',
             linewidth=1,
             zorder=3)

    # Phase fold to secondary eclipse
    G = gridspec.GridSpec(9, 3)
    G.update(hspace=0.3)
    axes_3 = plt.subplot(G[2, 2])
    plt.yticks(())
    phases = fold(time=t, period=results.period, T0=results.T0)
    sort_index = numpy.argsort(phases, kind="mergesort")
    phases = phases[sort_index]
    flux = y_filt[sort_index]
    plt.scatter((phases - 0.5) * results.period,
                flux,
                color='black',
                s=2,
                alpha=0.5,
                zorder=2)
    plt.plot((-0.5, 0.5), (1, 1), color='red')
    plt.xlim(-2 * results.duration, 2 * results.duration)
    plt.ylim(numpy.percentile(y_filt, 0.1), numpy.percentile(y_filt, 99.5))
    plt.plot((phases - 0.5) * results.period,
             running_mean_equal_length(flux, 10),
             color='blue',
             linestyle='dashed',
             linewidth=1,
             zorder=3)
    # Calculate secondary eclipse depth
    intransit_secondary = numpy.where(
        numpy.logical_and(
            (phases - 0.5) * results.period > (-0.5 * results.duration),
            (phases - 0.5) * results.period < (0.5 * results.duration)))
    mean = -(1 - numpy.mean(flux[intransit_secondary])) * 10**6
    stabw = -(numpy.std(flux[intransit_secondary]) /
              numpy.sqrt(len(flux[intransit_secondary]))) * 10**6
    significance_eb = mean / stabw

    plt.scatter((phases[intransit_secondary] - 0.5) * results.period,
                flux[intransit_secondary],
                color='orange',
                s=20,
                alpha=0.5,
                zorder=0)
    if numpy.isnan(mean):
        mean = 0
    if numpy.isnan(stabw):
        stabw = 0
    if numpy.isnan(significance_eb):
        significance_eb = 99
    text = r'secondary ' + str(
        int(mean)) + r'$\pm$' + str(int(stabw)) + ' ppm (' + format(
            significance_eb, '.1f') + r'$\,\sigma$)'
    plt.text(-1.95 * results.duration, numpy.percentile(y_filt, 0.1), text)
    # Full phase
    G = gridspec.GridSpec(9, 3)
    G.update(hspace=1)
    axes_5 = plt.subplot(G[3, :])
    plt.plot(results.model_folded_phase,
             -(1 - results.model_folded_model) * 10**6,
             color='red')
    plt.scatter(results.folded_phase,
                -(1 - results.folded_y) * 10**6,
                color='black',
                s=2,
                alpha=0.5,
                zorder=2)
    plt.xlim(0, 1)
    plt.ylim(numpy.percentile(ppm_flux, 0.1), numpy.percentile(ppm_flux, 99.9))
    plt.xlabel('Phase')
    plt.ylabel('Flux')

    # Check if phase gaps
    phase_diffs = abs(results.folded_phase -
                      numpy.roll(results.folded_phase, -1))
    phase_diffs = phase_diffs[:-1]
    largest_phase_peak = max(phase_diffs) / numpy.median(phase_diffs)

    # All transits in time series
    G = gridspec.GridSpec(9, 3)
    G.update(hspace=0)
    axes_6b = plt.subplot(G[4, :])
    y_filt = -(1 - y_filt) * 10**6
    in_transit = transit_mask(t, results.period, results.duration, results.T0)

    t_diff1 = abs(t - numpy.roll(t, -1))
    t_diff2 = abs(t - numpy.roll(t, +1))
    if max(t_diff1[in_transit]) > 0.1 or max(
            t_diff2[in_transit]) > 0.1:  # in days
        transit_near_gaps = True
        plt.text(min(t), numpy.percentile(ppm_flux, 0.1),
                 'Warning: Transits near gaps')
    else:
        transit_near_gaps = False

    transit_touches_edge = False
    if max(t) in t[in_transit]:
        plt.text(min(t), numpy.percentile(ppm_flux, 0.1),
                 'Warning: Last transit touches end')
        transit_touches_edge = True
    if max(t) in t[in_transit]:
        plt.text(min(t), numpy.percentile(ppm_flux, 0.1),
                 'Warning: First transit touches start')
        transit_touches_edge = True
    plt.scatter(t[in_transit], y_filt[in_transit], color='red', s=2, zorder=0)
    plt.scatter(t[~in_transit],
                y_filt[~in_transit],
                color='black',
                alpha=0.5,
                s=2,
                zorder=0)
    plt.plot(results.model_lightcurve_time,
             -(1 - results.model_lightcurve_model) * 10**6,
             alpha=0.5,
             color='red',
             zorder=1)
    plt.xlim(min(t), max(t))
    plt.ylim(numpy.percentile(ppm_flux, 0.1), numpy.percentile(ppm_flux, 99.9))
    plt.xlabel('Flux')
    plt.ylabel('Flux')
    plt.xticks(())

    # Transit depths error bars
    avg = -(1 - results.depth_mean[0]) * 10**6
    if numpy.isnan(results.transit_depths_uncertainties).any():
        step = 0
    else:
        step = max(results.transit_depths_uncertainties) * 10**6
    down = avg - step
    G = gridspec.GridSpec(9, 3)
    G.update(hspace=1)
    axes_6 = plt.subplot(G[5, :])
    plt.errorbar(results.transit_times,
                 -(1 - results.transit_depths) * 10**6,
                 yerr=step,
                 fmt='o',
                 color='red')
    plt.plot((min(t), max(t)), (0, 0), color='black')
    plt.plot((min(t), max(t)), (avg, avg), color='black', linestyle='dashed')
    plt.xlim(min(t), max(t))
    for transit in range(len(results.transit_times)):
        plt.text(results.transit_times[transit],
                 down,
                 str(int(results.per_transit_count[transit])),
                 horizontalalignment='center')
    plt.xlabel('Time (BKJD, days)')
    plt.ylabel('Flux')

    # Test statistic
    G = gridspec.GridSpec(9, 3)
    G.update(hspace=0)
    #G.update(hspace=3)
    axes_7 = plt.subplot(G[6, :])
    axes_7.axvline(results.period, alpha=0.4, lw=3)
    for n in range(2, 5):
        axes_7.axvline(n * results.period, alpha=0.4, lw=1, linestyle="dashed")
        axes_7.axvline(results.period / n, alpha=0.4, lw=1, linestyle="dashed")
    plt.ylabel(r'TLS SDE')
    plt.xticks(())
    plt.plot(results.periods, results.power, color='black', lw=0.5)
    plt.xlim(min(results.periods), max(results.periods))
    plt.text(results.period + 0.1, results.SDE * 0.9,
             'SDE=' + format(results.SDE, '.1f'))

    # LS periodogram raw
    freqs = numpy.geomspace(1 / min(results.periods), 1 / max(results.periods),
                            10000)
    freqs = numpy.unique(freqs)
    lspower = LombScargle(rawtime, rawflux).power(freqs)
    freqs = 1 / freqs

    G = gridspec.GridSpec(9, 3)
    G.update(hspace=0)
    axes_8 = plt.subplot(G[7, :])
    axes_8.axvline(results.period, alpha=0.4, lw=15)

    plt.ylabel(r'LS (raw)')
    plt.plot(freqs, lspower, color='black', lw=0.5)
    plt.xlim(min(results.periods), max(results.periods))
    plt.ylim(0, max(lspower) * 1.1)
    plt.xlabel('Period (days)')
    # Calculate Lomb-Scargle periodogram detrended
    freqs = numpy.geomspace(1 / min(results.periods), 1 / max(results.periods),
                            10000)
    freqs = numpy.unique(freqs)
    lspower = LombScargle(t, y_filt).power(freqs)
    freqs = 1 / freqs

    continuum = numpy.std(lspower)
    lspower = numpy.divide(lspower, continuum)  # Normalize to S/N

    G = gridspec.GridSpec(9, 3)
    G.update(hspace=0)
    axes_9 = plt.subplot(G[8, :])
    peak_index = numpy.argmax(lspower)
    axes_9.axvline(freqs[peak_index], alpha=0.4, lw=10)
    plt.ylabel(r'LS (detrended)')
    plt.plot(freqs, lspower, color='black', lw=0.5)
    plt.xlim(min(results.periods), max(results.periods))
    plt.ylim(0, max(lspower) * 1.1)
    plt.xlabel('Period (days)')

    # Text
    G = gridspec.GridSpec(9, 3)
    G.update(hspace=0, wspace=0.1)
    axes_8 = plt.subplot(G[0:2, 2])
    plt.xticks(())
    plt.yticks(())
    plt.text(0,
             0,
             'KOI ' + str(KOI) + '.0' + str(planet_number),
             fontweight='bold')

    plt.text(
        0, -0.30, 'SDE=' + format(results.SDE, '.1f') + ', SNR=' +
        format(results.snr, '.1f'))
    plt.text(
        0, -0.45, 'P=' + format(results.period, '.5f') + ' +-' +
        format(results.period_uncertainty, '.5f') + r'$\,$d')
    plt.text(0, -0.60,
             r'$T_{dur}=$' + format(results.duration, '.5f') + r'$\,$d')
    plt.text(
        0, -0.75,
        r'$R_*=$' + format(radius, '.2f') + ' (+' + format(radius_max, '.2f') +
        ', -' + format(radius_min, '.2f') + ') $\,R_{\odot}$')
    plt.text(
        0, -0.90,
        r'$M_*=$' + format(mass, '.2f') + ' (+' + format(mass_max, '.2f') +
        ', -' + format(mass_min, '.2f') + ') $\,M_{\odot}$')
    plt.text(0, -1.05, r'$R_P/R_*=$' + format(results.rp_rs, '.3f'))

    print('rp_rs', results.rp_rs)

    rp = results.rp_rs * radius  # in solar radii
    sun = 695700
    earth = 6371
    jupi = 69911
    rp_earth = (sun / earth) * rp
    rp_jupi = (sun / jupi) * rp
    plt.text(
        0, -1.20, r'$R_P=$' + format(rp_earth, '.2f') + '$\,R_{\oplus}=$' +
        format(rp_jupi, '.2f') + '$\,R_{Jup}$')
    plt.text(
        0, -1.35,
        r'$\delta=$' + \
        format((1-results.depth_mean[0])*10**6, '.0f') + ' ppm (' + \
        format((1-results.depth_mean_odd[0])*10**6, '.0f') + ', ' + \
        format((1-results.depth_mean_even[0])*10**6, '.0f') + ')')
    plt.text(
        0, -1.50, r'odd/even mismatch: ' +
        format(results.odd_even_mismatch, '.2f') + '$\,\sigma$')
    plt.text(
        0, -1.65,
        str(results.distinct_transit_count) + '/' +
        str(results.transit_count) + ' transits with data')
    plt.xlim(-0.1, 2)
    plt.ylim(-2, 0.1)
    axes_8.axis('off')
    plt.subplots_adjust(hspace=0.4)

    if abs(significance_eb) > 3:
        valid = False
        print('Vetting fail! significance_eb > 3')

    if abs(results.odd_even_mismatch) > 3:
        valid = False
        print('Vetting fail! odd_even_mismatch larger 3 sigma')

    if transit_near_gaps and results.distinct_transit_count < 4:
        valid = False
        print('Vetting fail! Transit near gaps and distinct_transit_count < 4')

    if valid:  # 5-50d
        figure_out_path = 'GOOD0-5d_' + str(KOI) + '_0' + str(planet_number)
    else:
        figure_out_path = '_FAIL0-5d_' + str(KOI) + '_0' + str(planet_number)

    plt.savefig(figure_out_path + '.png', bbox_inches='tight')
    print('Figure made:', figure_out_path)
    plt.close

    return valid
Exemple #11
0
                               skip_header=1,
                               delimiter=',',
                               dtype="int32, f8, f8, f8",
                               names=["EPIC_id", "period", "t0", "tdur"])
    rows = range(len(catalog["EPIC_id"]))

    for row in rows:
        EPIC_id = catalog["EPIC_id"][row]
        print('New planet EPIC_id', EPIC_id)
        time, flux = loadfile(EPIC_id)

        # 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,
Exemple #12
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
Exemple #13
0
def find_planets(time,
                 raw_flux,
                 cor_flux,
                 campaign_no,
                 star_id,
                 provenance,
                 summary_log,
                 max_planets=1,
                 plotno=1,
                 interactive=False):
    """
    Find one or more planet transits given input time, raw flux and corrected
    flux series. Save the results to the log files and display plots of
    light curves and folded light curves from TLS.

    Parameters
    ----------
    time: numpy.ndarray
        Time series.

    raw_flux: numpy.ndarray
        Raw flux series.

    cor_flux: numpy.ndarray
        Corrected flux series.

    campaign_no: int, str
        Campaign number or "sequence number". Can be a wildcard character `'*'`
        to indicate that all campaigns should be searched. Used for logging.

    star_id: int, str
        EPIC ID of the star. Used for logging.

    provenance: str
        Provenance name in MAST archive, e.g., ``'K2'``, ``'EVEREST'``,
        ``'K2SFF'``. Used for logging.

    summary_log: file object
        File object of the summary log file with write access and initialized
        with appropriate column names.

    max_planets: int
        Maximum number of planets to find.

    plotno: int
        Plot number to be passed directly to `~matplotlib.pyplot.figure`.
        If `None`, a new figure will be created.

    interactive: bool
        Indicates whether or not to draw figures on screen. When ``interactive``
        is `False`, figures are saved to files in the ``'Graphs/tls/'`` and
        ``'Graphs/lc/'`` sub-directories. Figure's file names will be
        constructed using the following pattern:

            ``star_id + '_' + provenance + '_tls.log'``
            ``star_id + '_' + provenance + '_lc.log'``
    """
    planet_id = 1
    while (max_planets and planet_id <= max_planets) or max_planets is None:
        lmask, umask = detect_outliers(cor_flux)
        # lmask indicates lower outliers and possible transits
        # umask indicates indicates outliers above the trend
        # remove upper outliers:
        umask = np.logical_not(umask)
        lmask = lmask[umask]
        time = time[umask]
        raw_flux = raw_flux[umask]
        cor_flux = cor_flux[umask]

        # "detrend" corrected flux:
        normalized_cor_flux, trend = normalize_lc(cor_flux, lmask)

        # run TLS:
        tls_results, valid = run_tls(time, normalized_cor_flux)
        if not valid:
            break

        # if the detection is significant, plot it and log it:
        if _calc_stats(tls_results):
            if isinstance(tls_results.transit_times, list):
                plot_lc(time,
                        raw_flux,
                        cor_flux,
                        normalized_cor_flux,
                        trend,
                        star_id,
                        planet_id,
                        provenance,
                        tls_results,
                        interactive=interactive)
                plotno += 1

                plot_tls(star_id,
                         planet_id,
                         provenance,
                         plotno,
                         tls_results,
                         interactive=interactive)
                plotno += 1

                log_results(star_id, planet_id, provenance, tls_results)
                append_summary_log(summary_log, star_id, campaign_no,
                                   planet_id, provenance, tls_results)

        # Remove detected transit and remove it from the data series:
        intransit = transit_mask(time, tls_results.period,
                                 2 * tls_results.duration, tls_results.T0)
        raw_flux = raw_flux[~intransit]
        cor_flux = cor_flux[~intransit]
        time = time[~intransit]
        time, cor_flux = cleaned_array(time, cor_flux)
        planet_id += 1
Exemple #14
0
def tls_search(time, flux, epoch, period, rplanet):
    SNR = 1e12
    SNR_threshold = 5.
    FOUND_SIGNAL = False
    
    #::: mask out the first detection at 6.2 days, with a duration of 2.082h, and T0=1712.54922
    #intransit = transit_mask(time, 6.26391, 2*2.082/24., 1712.54922)
    #time = time[~intransit]
    #flux = flux[~intransit]
    time, flux = cleaned_array(time, flux)
    
    #::: search for the rest
    while (SNR >= SNR_threshold) and (FOUND_SIGNAL==False):
        logprint('\n====================================================================')
    
        model = transitleastsquares(time, flux)
        R_starx=rstar/u.R_sun
        #print(rstar_min/u.R_sun)
        #print(rstar_max/u.R_sun)
        #print(mstar/u.M_sun) 
        #print(mstar_min/u.M_sun)
        #print(mstar_max/u.M_sun)
        results = model.power(u=ab,
                              R_star= radius,#rstar/u.R_sun, 
                              R_star_min=rstar_min, #rstar_min/u.R_sun,
                              R_star_max=rstar_max, #rstar_max/u.R_sun,
                              M_star= mass, #mstar/u.M_sun, 
                              M_star_min= mstar_min,#mstar_min/u.M_sun,
                              M_star_max=mstar_max, #mstar_max/u.M_sun,
                              period_min=0.5,
                              period_max=20,
                              n_transits_min=1,
                              show_progress_bar=False
                              )
        
        #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+raduismax
        
        
        if results.snr >= SNR_threshold:
            
            logprint('\nPeriod:', format(results.period, '.5f'), 'd')
            logprint(len(results.transit_times), 'transit times in time series:', ['{0:0.5f}'.format(i) for i in results.transit_times])
            logprint('Transit depth:', format(1.-results.depth, '.5f'))
            logprint('Best duration (days):', format(results.duration, '.5f'))
            logprint('Signal detection efficiency (SDE):', results.SDE)
            logprint('Signal-to-noise ratio (SNR):', results.snr)
        
            intransit = transit_mask(time, results.period, 2*results.duration, results.T0)
            time = time[~intransit]
            flux = flux[~intransit]
            time, flux = cleaned_array(time, flux)
            
            
            #::: check if it found the right signal
            right_period  = IsMultipleOf(results.period, period/2.) #check if it is a multiple of half the period to within 5%
            
            right_epoch = False
            for tt in results.transit_times:
                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:
                logprint('*** SUCCESFULLY FOUND THE TRANSIT ***')
                with open( os.path.join('tls_table/tls_table.csv'), 'a' ) as f:
                    f.write(str(period)+','+str(rplanet/u.R_earth)+','+'1\n')
                    FOUND_SIGNAL = True
                
          
        
        else:
            logprint('No other signals detected with SNR >= ' + str(SNR_threshold))
            if FOUND_SIGNAL == False:                
                logprint('*** FAILED FINDING THE TRANSIT ***')
                with open( os.path.join('tls_table/tls_table.csv'), 'a' ) as f:
                    f.write(str(period)+','+str(rplanet)+','+'0\n')
    
        SNR = results.snr
Exemple #15
0
def save_plot(time_i, global_final, results, i):
    #start the plotting
    fig, (ax1, ax2, ax3) = plt.subplots(nrows=1,
                                        ncols=3,
                                        figsize=(10, 3),
                                        constrained_layout=True)
    #1-Plot all the transits
    in_transit = transit_mask(time_i, results.period, results.duration,
                              results.T0)
    ax1.scatter(time_i[in_transit],
                global_final[i][in_transit],
                color='red',
                s=2,
                zorder=0)
    ax1.scatter(time_i[~in_transit],
                global_final[i][~in_transit],
                color='black',
                alpha=0.05,
                s=2,
                zorder=0)
    ax1.plot(results.model_lightcurve_time,
             results.model_lightcurve_model,
             alpha=1,
             color='red',
             zorder=1)
    #plt.scatter(time_n, flux_new_n, color='orange', alpha=0.3, s=20, zorder=3)
    plt.xlim(time_i.min(), time_i.max())
    #plt.xlim(1362.0,1364.0)
    ax1.set(xlabel='Time (days)', ylabel='Relative flux')

    #phase folded plus binning
    ax2.plot(results.model_folded_phase,
             results.model_folded_model,
             color='red')
    ax2.scatter(results.folded_phase,
                results.folded_y,
                color='black',
                s=10,
                alpha=0.05,
                zorder=2)
    if (results.SDE) != 0:
        bins = 200
        bin_means, bin_edges, binnumber = stats.binned_statistic(
            results.folded_phase,
            results.folded_y,
            statistic='mean',
            bins=bins)
        bin_stds, _, _ = stats.binned_statistic(results.folded_phase,
                                                results.folded_y,
                                                statistic='std',
                                                bins=bins)
        bin_width = (bin_edges[1] - bin_edges[0])
        bin_centers = bin_edges[1:] - bin_width / 2
        ax2.errorbar(bin_centers,
                     bin_means,
                     yerr=bin_stds / 2,
                     xerr=bin_width / 2,
                     marker='o',
                     markersize=4,
                     color='teal',
                     alpha=1,
                     linestyle='none')
        ax2.set_xlim(0.45, 0.55)
        ax2.set(xlabel='Phase', ylabel='Relative flux')
        plt.ticklabel_format(useOffset=False)
    else:
        ax2.set_xlim(0.45, 0.55)
        ax2.set(xlabel='Phase', ylabel='Relative flux')
        plt.ticklabel_format(useOffset=False)

    #SDE
    ax3 = plt.gca()
    ax3.axvline(results.period, alpha=0.4, lw=3)
    plt.xlim(np.min(results.periods), np.max(results.periods))
    for n in range(2, 10):
        ax3.axvline(n * results.period, alpha=0.4, lw=1, linestyle="dashed")
        ax3.axvline(results.period / n, alpha=0.4, lw=1, linestyle="dashed")
    ax3.set(xlabel='Period (days)', ylabel='SDE')
    ax3.plot(results.periods, results.power, color='black', lw=0.5)
    ax3.set_xlim(0., max(results.periods))

    return fig
Exemple #16
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
Exemple #17
0
def measure_centroid(t0, per, dur, sector, sourceid, c_obj, outdir):
    """
    Quoting Kostov+2019, who I followed:

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

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

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

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

    args:

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

        sector (int)

        sourceid (int)

        c_obj (SkyCoord): location of target star

    returns:

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

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

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

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

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

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

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

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

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

    oot_times = time[~intra]

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

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

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

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

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

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

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

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

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

    m_oot_minus_intra_snr = m_oot_minus_intra_flux / m_oot_minus_intra_flux_err

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

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

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

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

    return outdict
Exemple #18
0
def seach_one_koi(KOI):

    # Check if vetting file exists already
    figure_out_path = '_FAIL0-5d_' + str(KOI)
    figure_out_path2 = 'GOOD0-5d_' + str(KOI)
    if figure_out_path in " ".join(
            glob.glob("*.png")) or figure_out_path2 in " ".join(
                glob.glob("*.png")):
        print('Vetting sheet for this KOI exists already, skipping KOI', KOI)
    else:

        print('Working on file', KOI)
        time, flux = loadkoi(str(KOI))
        row = numpy.argmax(catalog["KOI"].astype(int) == int(float(KOI)))
        KIC = catalog["KIC"][row]
        print('row', row)
        print('KIC', KIC)

        max_t14 = T14(R_s=catalog["R_star"][row],
                      M_s=catalog["mass"][row],
                      P=period_max)
        window = 3 * max_t14

        print('R_star, mass, max_t14, window', catalog["R_star"][row],
              catalog["mass"][row], max_t14, window)
        if time is not None:

            # Remove known planets
            periods, t0s, tdurs = get_planets(KIC)
            print('periods', periods)
            for no in range(len(periods)):
                print('Removing planet', no + 1, 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)

            print('Next planet is number', no + 2)

            # Detrend data and remove high outliers

            print('Detrending with window...', window)
            #print(time)
            #print(flux)
            trend1 = trend(time, flux, window=window, c=5)
            #plt.scatter(time, flux, s=1, color='black')
            #plt.plot(time, trend1, color='red')
            #plt.show()

            #trend = scipy.signal.medfilt(flux, 31)
            #trend = scipy.signal.savgol_filter(trend, 25, 2)
            rawflux = flux.copy()
            rawtime = time.copy()
            y_filt = flux / trend1
            y_filt = sigma_clip(y_filt, sigma_upper=3, sigma_lower=1e10)
            time, y_filt = cleaned_array(time, y_filt)

            #plt.close()
            #plt.scatter(time, y_filt, s=1, color='black')
            #plt.show()

            a = catalog["ld1"][row]
            b = catalog["ld2"][row]
            print('LD ab = ', a, b)
            try:
                model = transitleastsquares(time, y_filt)
                results = model.power(
                    #n_transits_min=1,
                    u=(a, b),
                    R_star=catalog["R_star"][row],
                    R_star_max=catalog["R_star"][row] +
                    2 * catalog["rad_up"][row],
                    # sign is OK, is negative in catalog:
                    R_star_min=catalog["R_star"][row] +
                    2 * catalog["rad_down"][row],
                    M_star=catalog["mass"][row],
                    M_star_max=catalog["mass"][row] +
                    2 * catalog["mass_up"][row],
                    M_star_min=catalog["mass"][row] +
                    2 * catalog["mass_down"][row],
                    oversampling_factor=5,
                    duration_grid_step=1.05,
                    use_threads=1,
                    period_min=period_min,
                    period_max=period_max,
                    show_progress_bar=False,
                    T0_fit_margin=0.1)
                tls_worked = True
                #except ValueError:
                #    tls_worked = False

                valid = True
                if tls_worked:
                    # Check if phase space has gaps
                    bins = numpy.linspace(0, 1, 100)
                    digitized = numpy.digitize(results.folded_phase, bins)
                    bin_means = [
                        results.folded_phase[digitized == i].mean()
                        for i in range(1, len(bins))
                    ]
                    #print('bin_means', bin_means)
                    if numpy.isnan(bin_means).any():
                        print('Vetting fail! Phase bins contain NaNs')
                        valid = False

                    if results.distinct_transit_count == 1 and results.transit_count >= 2:
                        valid = False
                        print(
                            'Vetting fail! results.distinct_transit_count==1 and results.transit_count == 2'
                        )
                    if results.distinct_transit_count == 2 and results.transit_count >= 3:
                        valid = False
                        print(
                            'Vetting fail! results.distinct_transit_count==2 and results.transit_count == 3'
                        )
                    if results.SDE < 8:
                        valid = False
                        print('Vetting fail! results.SDE < 8', results.SDE)
                    if results.snr < 7:
                        valid = False
                        print('Vetting fail! results.snr < 7', results.snr)

                    upper_transit_depths = results.transit_depths + results.transit_depths_uncertainties
                    if results.transit_count == 2 and max(
                            upper_transit_depths) > 1:
                        valid = False
                        print('Vetting fail! 2 transits, only 1 significant')

                    upper_transit_depths = results.transit_depths + results.transit_depths_uncertainties
                    if results.transit_count == 3 and max(
                            upper_transit_depths) > 1:
                        valid = False
                        print(
                            'Vetting fail! 3 transits, not all 3 significant')

                    upper_transit_depths = results.transit_depths + results.transit_depths_uncertainties
                    if results.transit_count == 4 and max(
                            upper_transit_depths) > 1:
                        valid = False
                        print(
                            'Vetting fail! 4 transits, not all 4 significant')

                    if results.depth < 0.95:
                        valid = False
                        print('Vetting fail! Transit depth < 0.95',
                              results.depth)

                    print('Signal detection efficiency (SDE):',
                          format(results.SDE, '.1f'))
                    print('SNR:', format(results.snr, '.1f'))
                    print('Search completed')
                    #if valid:
                    print('Attempting figure...')
                    make_figure(KOI=str(KOI),
                                planet_number=no + 2,
                                results=results,
                                t=time,
                                y=flux,
                                y_filt=y_filt,
                                trend=trend1,
                                rawtime=rawtime,
                                rawflux=rawflux,
                                catalog=catalog,
                                row=row,
                                valid=valid)
                    #else:
                    #    print('No figure made, vetting failed!')
                else:
                    print('TLS failed')

                process = psutil.Process(os.getpid())
                ram_mb = process.memory_info().rss / 2**20
                print('RAM (MB)', ram_mb)
                if ram_mb > 25000:
                    sys.exit()
            except:
                pass
# Visualise fit on phase-folded curve
plt.figure()
plt.plot(results.model_folded_phase, results.model_folded_model, color='red')
plt.scatter(results.folded_phase,
            results.folded_y,
            color='blue',
            s=10,
            alpha=0.5,
            zorder=2)
#plt.xlim(0.49, 0.51)
plt.xlabel('Phase')
plt.ylabel('Relative flux')

# Plot over original data
plt.figure()
in_transit = transit_mask(lc.time, results.period, results.duration,
                          results.T0)
plt.scatter(lc.time[in_transit],
            lc.flux[in_transit],
            color='red',
            s=2,
            zorder=0)
plt.scatter(lc.time[~in_transit],
            lc.flux[~in_transit],
            color='blue',
            alpha=0.5,
            s=2,
            zorder=0)
plt.plot(results.model_lightcurve_time,
         results.model_lightcurve_model,
         alpha=0.5,
         color='red',
Exemple #20
0
    numpy.testing.assert_almost_equal(max(results.power),
                                      45.934646920004326,
                                      decimal=5)
    numpy.testing.assert_almost_equal(max(results.power_raw),
                                      44.00867236551441,
                                      decimal=5)
    numpy.testing.assert_almost_equal(min(results.power),
                                      -0.620153987656165,
                                      decimal=5)
    numpy.testing.assert_almost_equal(min(results.power_raw),
                                      -0.29015390864908414,
                                      decimal=5)
    print("Detrending of power spectrum from power_raw passed")

    # Mask of the first planet
    intransit = transit_mask(t, results.period, 2 * results.duration,
                             results.T0)
    y_second_run = y_filt[~intransit]
    t_second_run = t[~intransit]
    t_second_run, y_second_run = cleaned_array(t_second_run, y_second_run)

    # Search for second planet
    model_second_run = transitleastsquares(t_second_run, y_second_run)
    results_second_run = model_second_run.power()
    numpy.testing.assert_almost_equal(results_second_run.duration,
                                      0.1478628403227008,
                                      decimal=5)
    numpy.testing.assert_almost_equal(results_second_run.SDE,
                                      34.98291056410117,
                                      decimal=5)
    numpy.testing.assert_almost_equal(results_second_run.rp_rs,
                                      0.025852178872027086,
def test_fit_and_remove_transits():

    # --------------------------------------------------------
    # test case: transitleastsuqares ORIGINAL TEST reproduced
    # --------------------------------------------------------

    np.random.seed(seed=0)  # reproducibility 

    # Create test data
    time_start = 3.14
    data_duration = 100
    samples_per_day = 48
    samples = int(data_duration * samples_per_day)
    time = np.linspace(time_start, time_start + data_duration, samples)

    # Use batman to create transits
    ma = batman.TransitParams()
    ma.t0 = time_start  # time of inferior conjunction; first transit is X days after start
    ma.per = 10.123  # orbital period
    ma.rp = 6371 / 696342  # 6371 planet radius (in units of stellar radii)
    ma.a = 19  # semi-major axis (in units of stellar radii)
    ma.inc = 90  # orbital inclination (in degrees)
    ma.ecc = 0  # eccentricity
    ma.w = 90  # longitude of periastron (in degrees)
    ma.u = [0.4, 0.4]  # limb darkening coefficients
    ma.limb_dark = "quadratic"  # limb darkening model
    m = batman.TransitModel(ma, time)  # initializes model
    synthetic_signal = m.light_curve(ma)  # calculates light curve

    # Create noise and merge with flux
    ppm = 50  # Noise level in parts per million
    noise = np.random.normal(0, 10**-6 * ppm, int(samples))
    flux = synthetic_signal + noise


    # call the fit function
    trflux, results = fit_and_remove_transits(time, flux)

    # test transit parameters
    assert ma.rp == pytest.approx(results.rp_rs, rel=.05)
    assert results.period == pytest.approx(ma.per, rel=1e-3)
    assert results.duration == pytest.approx(0.171, rel=1e-2)
    assert results.T0 == pytest.approx(ma.per + time_start, rel=1e-2)

    in_transit = tls.transit_mask(time, results.period, results.duration, results.T0)

    assert (np.where(in_transit)[0] == 
            np.array([   0,    1,    2,    3,  482,  483,  484,  485,  486,  487,  488,
                       489,  968,  969,  970,  971,  972,  973,  974,  975, 1453, 1454,
                      1455, 1456, 1457, 1458, 1459, 1460, 1461, 1939, 1940, 1941, 1942,
                      1943, 1944, 1945, 1946, 1947, 2425, 2426, 2427, 2428, 2429, 2430,
                      2431, 2432, 2911, 2912, 2913, 2914, 2915, 2916, 2917, 2918, 3397,
                      3398, 3399, 3400, 3401, 3402, 3403, 3404, 3883, 3884, 3885, 3886,
                      3887, 3888, 3889, 3890, 4369, 4370, 4371, 4372, 4373, 4374, 4375,
                      4376])).all()

    # out of transit the flux is conserved
    assert ((trflux-flux)[~in_transit] < 1e-16).all()

    # -------------------------------
    # test case: SINGLE KNOWN TRANSIT
    # -------------------------------

    np.random.seed(seed=0) 
    ma.per = 70.123  # orbital period upped to get a single transit
    ma.t0 = time_start - 10 # shift the first transit such there is really only one transit left
    m = batman.TransitModel(ma, time)  # initializes model
    synthetic_signal = m.light_curve(ma)  # calculates light curve

    # Create noise and merge with flux
    ppm = 50  # Noise level in parts per million
    noise = np.random.normal(0, 10**-6 * ppm, int(samples))
    flux = synthetic_signal + noise

    # call the fit function
    trflux, results = fit_and_remove_transits(time, flux)

    # the period recovered is too short when we have a single transit
    assert results.period == pytest.approx(15.48, rel=1e-2)

    # call the fit function with constrained orbital period 
    # and uncertainty improves the result drastically
    trflux, results = fit_and_remove_transits(time, flux, pl_orbper=70., pl_orbpererr=2.)

    # test transit parameters
    assert ma.rp == pytest.approx(results.rp_rs, rel=.1)

    # single transits have trouble finding a correct period obviously for a single transit
    assert results.period == pytest.approx(71.75, rel=1e-2)

    # but the centering on the transit is fine
    assert results.T0 == pytest.approx(ma.per + ma.t0, rel=1e-2)

    in_transit = tls.transit_mask(time, results.period, results.duration, results.T0)

    # single transit recovered, yes
    assert (np.where(in_transit)[0] == np.arange(2865,2915)).all()

    # out of transit the flux is conserved
    assert ((trflux-flux)[~in_transit] < 1e-16).all()