def compute_transit_prob(self): self.transit_prob, self.etransit_prob = np.zeros_like(self.sens), \ np.zeros_like(self.sens) self.logtransit_prob,self.elogtransit_prob = np.zeros_like(self.sens), \ np.zeros_like(self.sens) for i in range(self._xlen): for j in range(self._ylen): # compute transit probability in log-linear space Pmid = 10**(np.log10(self.logPgrid[i]) + \ np.diff(np.log10(self.logPgrid[:2])/2)) rpmid = self.rpgrid[j] + np.diff(self.rpgrid)[0] / 2. sma = rvs.AU2m( rvs.semimajoraxis(Pmid, unp.uarray(self.Ms, self.e_Ms), 0)) transit_prob = (rvs.Rsun2m(unp.uarray(self.Rs, self.e_Rs)) + \ rvs.Rearth2m(rpmid)) / sma self.transit_prob[i, j] = unp.nominal_values(transit_prob) self.etransit_prob[i, j] = unp.std_devs(transit_prob) # compute transit probability in log-linear space rpmid = 10**(np.log10(self.logrpgrid[j]) + \ np.diff(np.log10(self.logrpgrid[:2])/2)) transit_prob = (rvs.Rsun2m(unp.uarray(self.Rs, self.e_Rs)) + \ rvs.Rearth2m(rpmid)) / sma self.logtransit_prob[i, j] = unp.nominal_values(transit_prob) self.elogtransit_prob[i, j] = unp.std_devs(transit_prob) # correction from beta distribution fit (Kipping 2013) factor = 1.08 self.transit_prob *= factor self.etransit_prob *= factor self.logtransit_prob *= factor self.elogtransit_prob *= factor
def compute_transit_duration(rp, P, K, Rs, b=0): mp = np.array([runTESS.get_planet_mass(i) for i in rp]) Ms = runTESS.get_stellar_mass(P, mp, K) sma = rvs.AU2m(rvs.semimajoraxis(P, Ms, mp)) Rs2 = rvs.Rsun2m(Rs) tau0 = P * Rs2 / (2 * np.pi * sma) return 2. * tau0 * np.sqrt(1. - b**2)
def _fit_params(params, bjd, fcorr, ef, Ms, Rs, Teff, Kep=False, TESS=False): '''Get best-fit parameters.''' assert params.shape == (4, ) P, T0, depth, duration = params if Kep: u1, u2 = llnl.get_LDcoeffs_Kepler(Ms, Rs, Teff) elif TESS: u1, u2 = llnl.get_LDcoeffs_TESS(Ms, Rs, Teff) aRs = rvs.AU2m(rvs.semimajoraxis(P, Ms, 0)) / rvs.Rsun2m(Rs) rpRs = np.sqrt(depth) p0 = aRs, rpRs, 90. bnds = ((aRs * .9, 0, float(rvs.inclination(P, Ms, Rs, 1.1))), (aRs * 1.1, 1, float(rvs.inclination(P, Ms, Rs, -1.1)))) try: popt, _ = curve_fit(transit_model_func_curve_fit(P, T0, u1, u2), bjd, fcorr, p0=p0, sigma=ef, absolute_sigma=True, bounds=bnds) aRs, rpRs, inc = popt depth = rpRs**2 b = rvs.impactparam_inc(P, Ms, Rs, inc) duration = P / (np.pi * aRs) * np.sqrt((1 + np.sqrt(depth))**2 - b * b) return P, T0, depth, duration except RuntimeError: return params
def _is_Vshaped(params, bjd, fcorr, ef, Ms, Rs, Teff, Kep, TESS, duration_frac=.05): '''check if transit is V-shaped although this does not imply an EB because inclined transiting planets can look like this as well.''' # fit transit model assert params.shape == (4, ) P, T0, depth = params[:3] if Kep: u1, u2 = llnl.get_LDcoeffs_Kepler(Ms, Rs, Teff) elif TESS: u1, u2 = llnl.get_LDcoeffs_TESS(Ms, Rs, Teff) aRs = rvs.AU2m(rvs.semimajoraxis(P, Ms, 0)) / rvs.Rsun2m(Rs) rpRs = np.sqrt(depth) p0 = aRs, rpRs, 90. bnds = ((aRs * .9, 0, float(rvs.inclination(P, Ms, Rs, 1.1))), (aRs * 1.1, 1, float(rvs.inclination(P, Ms, Rs, -1.1)))) try: popt, _ = curve_fit(transit_model_func_curve_fit(P, T0, u1, u2), bjd, fcorr, p0=p0, sigma=ef, absolute_sigma=True, bounds=bnds) aRs, rpRs, inc = popt except RuntimeError: return False, 0. # get ingress, egress times and duration transit_func = transit_model_func_curve_fit(P, T0, 0, 0) fmodel = transit_func(bjd, *popt) phase = foldAt(bjd, P, T0) phase[phase > .5] -= 1 depth = fmodel.min() T1, T4 = phase[(phase<=0) & (fmodel==1)].max()*P, \ phase[(phase>=0) & (fmodel==1)].min()*P in_ingress = (phase <= 0) & np.isclose(fmodel, depth, rtol=1e-4) T2 = phase[in_ingress].min() * P if in_ingress.sum() > 0 else (T4 - T1) * .1 in_egress = (phase >= 0) & np.isclose(fmodel, depth, rtol=1e-4) T3 = phase[in_egress].max() * P if in_egress.sum() > 0 else (T4 - T1) * .1 Tingress, Tegress, duration = T2 - T1, T4 - T3, T4 - T1 Tedge = Tingress + Tegress # V-shaped if T2==T3 or if ingress+egress time is the same as the duration if T2 == T3: return True, duration elif np.isclose(Tedge, duration, rtol=duration_frac): return True, duration else: return False, duration
def depth2rp(P_days, depth, duration_days, Ms, Rs): '''Compute the planet radius from the transit depth and duration using the analtyical treatment from Mandel & Agol 2002''' assert 0 < depth < 1 # compute distance from centres at T0 sma = rvs.semimajoraxis(P_days, Ms, 0) a_Rs = rvs.AU2m(sma) / rvs.Rsun2m(Rs) assert a_Rs > 1 b = rvs.impactparam_T(P_days, Ms, Rs, duration_days) assert abs(b) <= 1 inc = float(rvs.inclination(P_days, Ms, Rs, b)) z = compute_distance_center(a_Rs, inc) # compute size ratio (p=rp/Rs) p_simple = unp.sqrt(depth) if z <= 1 - p_simple: p = p_simple else: ps = np.logspace(-6, 0, 1000) depths = p2depth_grazing(ps, z) if (np.nanmax(depths) < z) or (np.nanmin(depths) > z): p = p_simple else: fint = interp1d(ps, depths) p = float(fint(depth)) # compute planet radius rp = rvs.m2Rearth(rvs.Rsun2m(p * Rs)) return rp
def optimize_singletransit_params(params, bjd, fcorr, ef, Ms, Rs, u1, u2, pltt=True): '''Get best-fit parameters using the periodic fit parameters for initialization (i.e. P_input < P_singletransit).''' assert params.shape == (4, ) P, T0, depth, duration = params if depth >= .9: # sometimes the dimming is passed instead of depth return np.nan, np.nan, np.nan, np.nan, \ np.repeat(np.nan, bjd.size), np.repeat(np.nan, 7) # focus on data centered around T0 g = (bjd >= T0 - 10 * duration) & (bjd <= T0 + 10 * duration) bjdred, fcorrred, efred = bjd[g], fcorr[g], ef[g] # initialize aRs = rvs.AU2m(rvs.semimajoraxis(P, Ms, 0)) / rvs.Rsun2m(Rs) rpRs = np.sqrt(depth) p0 = P, T0, aRs, rpRs, 90. incs = np.array([ float(rvs.inclination(P, Ms, Rs, 1)), float(rvs.inclination(P, Ms, Rs, -1)) ]) bnds = ((0, T0 - .2, 0, 0, incs.min()), (P * 100, T0 + .2, aRs * 100, 1, incs.max())) try: popt, _ = curve_fit(llnl.transit_model_func_curve_fit(u1, u2), bjdred, fcorrred, p0=p0, sigma=efred, absolute_sigma=True, bounds=bnds) P, T0, aRs, rpRs, inc = popt depth = rpRs**2 b = rvs.impactparam_inc(P, Ms, Rs, inc) duration = P / (np.pi * aRs) * np.sqrt((1 + np.sqrt(depth))**2 - b * b) func = llnl.transit_model_func_curve_fit(u1, u2) fmodel = func(bjdred, P, T0, aRs, rpRs, inc) params = np.array([P, T0, aRs, rpRs, inc, u1, u2]) except RuntimeError, ValueError: func = llnl.transit_model_func_curve_fit(u1, u2) fmodel = func(bjdred, P, T0, aRs, rpRs, 90.) P, T0, depth, duration = np.repeat(np.nan, 4) params = np.repeat(np.nan, 7)
def fit_params(params, bjd, fcorr, ef, Ms, Rs, Teff, Kep=False, TESS=False): '''Get best-fit parameters.''' assert params.shape == (4, ) P, T0, depth, duration = params if depth >= .9: # sometimes the dimming is passed instead of depth return np.nan, np.nan, np.nan, np.nan, \ np.repeat(np.nan, bjd.size), np.repeat(np.nan, 7) if Kep: u1, u2 = get_LDcoeffs_Kepler(Ms, Rs, Teff) if TESS: u1, u2 = get_LDcoeffs_TESS(Ms, Rs, Teff) assert np.all(np.isfinite([u1, u2])) aRs = rvs.AU2m(rvs.semimajoraxis(P, Ms, 0)) / rvs.Rsun2m(Rs) rpRs = np.sqrt(depth) p0 = P, T0, aRs, rpRs, 90. incs = np.array([ float(rvs.inclination(P, Ms, Rs, 1)), float(rvs.inclination(P, Ms, Rs, -1)) ]) bnds = ((P * .9, T0 - P * 1.1, aRs * .9, 0, incs.min()), (P * 1.1, T0 + P * 1.1, aRs * 1.1, 1, incs.max())) try: popt, _ = curve_fit(transit_model_func_curve_fit(u1, u2), bjd, fcorr, p0=p0, sigma=ef, absolute_sigma=True, bounds=bnds) P, T0, aRs, rpRs, inc = popt depth = rpRs**2 b = rvs.impactparam_inc(P, Ms, Rs, inc) duration = P / (np.pi * aRs) * np.sqrt((1 + np.sqrt(depth))**2 - b * b) func = transit_model_func_curve_fit(u1, u2) fmodel = func(bjd, P, T0, aRs, rpRs, inc) params = np.array([P, T0, aRs, rpRs, inc, u1, u2]) except RuntimeError, ValueError: func = transit_model_func_curve_fit(u1, u2) fmodel = func(bjd, P, T0, aRs, rpRs, 90.) P, T0, depth, duration = np.repeat(np.nan, 4) params = np.repeat(np.nan, 7)
def setupTOI175simv2(bjd0, Ms, eccs, incs_deg, outname, interval_yrs, random=True, DeltaOmegamax=180): '''Create a simulation of TOI175 with custom input for the planetary parameters in the form of 1d arrays.''' # Initialize sim = rebound.Simulation() sim.integrator = "whfast" sim.units = ('AU', 'Msun', 'yr') sim.automateSimulationArchive("%s.bin" % outname, interval=interval_yrs) # Get keplerian parameters assert eccs.size == 3 assert incs_deg.size == 3 Ps = np.array([2.2531, 3.6904, 7.4512]) if random: Ps += np.array([ np.random.randn() * 4e-4, np.random.randn() * 3e-4, np.random.randn() * 8e-4 ]) while np.any(Ps <= 0): Ps = np.array([2.2531, 3.6904, 7.4512]) + np.array([ np.random.randn() * 4e-4, np.random.randn() * 3e-4, np.random.randn() * 8e-4 ]) T0s = np.array([1366.1708, 1367.2752, 1362.7376]) + 2457000 if random: T0s += np.array([ np.random.randn() * 1e-4, np.random.randn() * 6e-4, np.random.randn() * 9e-4 ]) # sample mass of the smallest planet directly from the PDF since it's not gaussian (i.e. a non-detection) mp_175d03 = np.random.choice(np.load( 'toi175d03_2d25_planetmass_PDF.npy')) + np.random.randn() * 5e-4 mps = np.array([mp_175d03, 2.6, 2.4]) if random: mps += np.array([0, np.random.randn() * 0.4, np.random.randn() * 0.6]) while np.any(mps <= 0): mps = np.array([0.34, 2.6, 2.4]) + np.array([ np.random.randn() * .42, np.random.randn() * 0.4, np.random.randn() * 0.6 ]) smas = rvs.m2AU(rvs.semimajoraxis(Ps, Ms, mps)) mps = rvs.kg2Msun(rvs.Mearth2kg(mps)) nplanets = Ps.size omegas = np.random.uniform(-np.pi, np.pi, nplanets) Omegas = np.random.uniform(-np.pi, np.pi, nplanets) thetas = 2 * np.pi * foldAt(bjd0, Ps, T0s) - np.pi # Add star sim.add(m=Ms, hash='star') # Add planets for i in range(nplanets): sim.add(m=mps[i], a=smas[i], inc=np.deg2rad(incs_deg[i] - 90), e=eccs[i], omega=omegas[i], Omega=Omegas[i], theta=thetas[i]) sim.move_to_com() RHill1 = (mps[:2].sum() / (3. * Ms))**(1. / 3) * smas[:2].mean() RHill2 = (mps[1:].sum() / (3. * Ms))**(1. / 3) * smas[1:].mean() sim.exit_min_distance = float(np.max([RHill1, RHill2])) return sim
def sample_planet_params(self, index, postGAIA=True): '''sample distribution of planet parameters from observables and stellar pdfs''' # get stellar parameters PDFs either from derived from GAIA distances # or from original Kepler parameters (approximate distributions as skewnormal) g = int(index) print self.KepIDs[g] if postGAIA: path = '../GAIAMdwarfs/Gaia-DR2-distances_custom/DistancePosteriors/' try: samp_Rs, samp_Teff, samp_Ms = np.loadtxt('%s/KepID_allpost_%i' % (path, self.KepIDs[g]), delimiter=',', usecols=(9, 10, 11)).T except IOError: samp_Rs, samp_Teff, samp_Ms = np.zeros(1000), np.zeros( 1000), np.zeros(1000) if np.all(np.isnan(samp_Rs)) or np.all(np.isnan(samp_Teff)) or np.all( np.isnan(samp_Ms)): samp_Rs, samp_Teff, samp_Ms = np.zeros(1000), np.zeros( 1000), np.zeros(1000) samp_Rs = resample_PDF(samp_Rs[np.isfinite(samp_Rs)], samp_Rs.size, sig=1e-3) samp_Teff = resample_PDF(samp_Teff[np.isfinite(samp_Teff)], samp_Teff.size, sig=5) samp_Ms = resample_PDF(samp_Ms[np.isfinite(samp_Ms)], samp_Ms.size, sig=1e-3) else: _, _, samp_Rs = get_samples_from_percentiles(self.Rss1[g], self.ehi_Rss1[g], self.elo_Rss1[g], Nsamp=1e3) _, _, samp_Teff = get_samples_from_percentiles(self.Teffs1[g], self.ehi_Teffs1[g], self.elo_Teffs1[g], Nsamp=1e3) _, _, samp_Ms = get_samples_from_percentiles(self.Mss1[g], self.ehi_Mss1[g], self.elo_Mss1[g], Nsamp=1e3) # sample rp/Rs distribution from point estimates _, _, samp_rpRs = get_samples_from_percentiles(self.rpRs[g], self.ehi_rpRs[g], self.elo_rpRs[g], Nsamp=samp_Rs.size) # compute planet radius PDF samp_rp = rvs.m2Rearth(rvs.Rsun2m(samp_rpRs * samp_Rs)) v = np.percentile(samp_rp, (16, 50, 84)) rps = v[1], v[2] - v[1], v[1] - v[0] # compute semi-major axis PDF samp_Ps = np.random.normal(self.Ps[g], self.e_Ps[g], samp_Ms.size) samp_as = rvs.semimajoraxis(samp_Ps, samp_Ms, 0) v = np.percentile(samp_as, (16, 50, 84)) smas = v[1], v[2] - v[1], v[1] - v[0] # compute equilibrium T PDF (Bond albedo=0) samp_Teq = samp_Teff * np.sqrt( .5 * rvs.Rsun2m(samp_Rs) / rvs.AU2m(samp_as)) v = np.percentile(samp_Teq, (16, 50, 84)) Teqs = v[1], v[2] - v[1], v[1] - v[0] # compute insolation samp_F = samp_Rs**2 * (samp_Teff / 5778.)**4 / samp_as**2 v = np.percentile(samp_F, (16, 50, 84)) Fs = v[1], v[2] - v[1], v[1] - v[0] return rps, smas, Teqs, Fs
def run_mcmc(params, bjd, fcorr, ef, Ms, eMs, Rs, eRs, Teff, eTeff, u1, u2, nwalkers=200, burnin=500, nsteps=400, pltt=True): assert params.shape == (4, ) params_optimized = np.zeros(5) nwalkers, burnin, nsteps = int(nwalkers), int(burnin), int(nsteps) params_results = np.zeros((3, 5)) # get optimized parameters and get the LC around T0 bjdred, fcorrred, efred, fmodel_opt, theta = \ optimize_singletransit_params(params, bjd, fcorr, ef, Ms, Rs, u1, u2, pltt=1) params_opt = theta[:5] print params, params_opt # run MCMC on transit LC initialize = [1, 1e-3, 1, 1e-2, 1] print 'Running MCMC on single-transit model' sampler, samples = run_emcee(params_opt, bjd, bjdred, fcorrred, efred, initialize, u1, u2, Ms, Rs, a=2, nwalkers=nwalkers, burnin=burnin, nsteps=nsteps, zeroplanetmodel=False) results = get_results(samples) params_results = results func = llnl.transit_model_func_curve_fit(u1, u2) fmodel_mcmc = func(bjdred, *params_results[0]) # estimate single transit P, aRs, rpRs, and inc Nsamp = samples.shape[0] samp_Ms = np.random.randn(Nsamp) * eMs + Ms samp_Rs = np.random.randn(Nsamp) * eRs + Rs samp_rho = rvs.Msun2kg(samp_Ms) / rvs.Rsun2m(samp_Rs)**3 samp_aRs = samples[:, 2] v = np.percentile(samp_aRs, (16, 50, 84)) aRs_est = [v[1], v[2] - v[1], v[1] - v[0]] samp_rpRs = samples[:, 3] v = np.percentile(samp_rpRs, (16, 50, 84)) rpRs_est = [v[1], v[2] - v[1], v[1] - v[0]] samp_inc = samples[:, 4] v = np.percentile(samp_inc, (16, 50, 84)) inc_est = [v[1], v[2] - v[1], v[1] - v[0]] #samp_P=rvs.sec2days(np.sqrt(4*np.pi*np.pi/(6.67e-11*samp_rho)*samp_aRs**3)) samp_P = samples[:, 0] v = np.percentile(samp_P, (16, 50, 84)) P_est = [v[1], v[2] - v[1], v[1] - v[0]] # estimate F samp_Teff = np.random.randn(Nsamp) * eTeff + Teff samp_Ls = compute_Ls(samp_Rs, samp_Teff) samp_F = compute_F(samp_Ls, rvs.semimajoraxis(samp_P, samp_Ms, 0)) v = np.percentile(samp_F, (16, 50, 84)) F_est = [v[1], v[2] - v[1], v[1] - v[0]] # plotting if pltt: t0 = 2457000 plt.figure(1) plt.plot(bjdred - t0, fcorrred, '.') plt.plot(bjdred - t0, fmodel_opt, '-', lw=3, label='optimized') plt.plot(bjdred - t0, fmodel_mcmc, '-', lw=2, label='MCMC model') plt.legend(loc='upper left') plt.figure(2) plt.hist(samp_P, bins=30) plt.show() return bjd, fcorr, ef, params_opt, fmodel_opt, samples, params_results, fmodel_mcmc, samp_P, P_est, samp_F, F_est, aRs_est, rpRs_est, inc_est
def run_emcee(theta, bjd, bjdred, fred, efred, initialize, u1, u2, Ms, Rs, nwalkers=200, burnin=200, nsteps=400, a=2, zeroplanetmodel=False): '''Run mcmc on an input light curve with no transit model.''' # get limits on P and aRs P, T0 = theta[:2] Plim = np.max([T0 - bjd.min(), bjd.max() - T0]) aRslim = rvs.AU2m(rvs.semimajoraxis(Plim, Ms, 0)) / rvs.Rsun2m(Rs) print 'Plim = %.4f' % Plim print 'aRslim = %.4f' % aRslim # initialize chains assert len(theta) == len(initialize) assert len(theta) == 5 theta[0], theta[2] = Plim + 10, aRslim + 10 p0 = [] for i in range(nwalkers): p0.append(theta + initialize * np.random.randn(len(theta))) # initialize sampler P = theta[0] inclims = np.array([ float(rvs.inclination(P, Ms, Rs, 1)), float(rvs.inclination(P, Ms, Rs, -1)) ]) args = (theta, bjdred, fred, efred, Plim, aRslim, inclims, u1, u2, zeroplanetmodel) sampler = emcee.EnsembleSampler(nwalkers, len(theta), lnprob, args=args, a=a) # run burnin print 'Running burnin...' t0 = time.time() p0, _, _ = sampler.run_mcmc(p0, burnin) print 'Burnin acceptance fraction is %.4f' % np.mean( sampler.acceptance_fraction) print 'Burnin took %.4f minutes\n' % ((time.time() - t0) / 60.) sampler.reset() # run MCMC print 'Running full MCMC...' p0, _, _ = sampler.run_mcmc(p0, nsteps) samples = sampler.chain.reshape((-1, len(theta))) print "Mean acceptance fraction: %.4f" % np.mean( sampler.acceptance_fraction) print 'Full MCMC took %.4f minutes' % ((time.time() - t0) / 60.) return sampler, samples
def setup_sim(Ms, planettheta, bjd0, randomOmega=True, eccupperlim=0): ''' Setup a simulation with a central star and planets. Parameters ---------- `Ms` : float The mass of the central star in solar masses. `planettheta` : tuple of dict Tuple containing 5 or 6 dictionaries of each planet's 1) orbital period in days, 2) time of inferior conjuction in BJD, 3) RV semi-amplitude in m/s, 4) h = sqrt(e)*cos(omega), 5) k = sqrt(e)*sin(omega). If there are 6 dictionaries, the sixth and last is 6) orbital inclination in degrees. `bjd0` : scalar Reference epoch to begin the simulation at. `randomOmega` : bool If True, randomly sample the value of each planet's longitude of the ascending node from U(0,2pi). `eccupperlim` : float The upper limit of eccentricities to sample from. If `eccupperlim` is 0, then don't resample the eccentricities. Returns ------- `sim` : rebound simulation object The rebound.Simulation containing the all injected particles. Example ------- >>> Ms, planettheta = .1, initialize_system_parameters() >>> sim = setup_sim(Ms, planettheta, 2450000) ''' # Initialize simulation sim = rebound.Simulation() sim.integrator = "whfast" sim.units = ('AU', 'Msun', 'yr') # Set timestep if len(planettheta) == 5: Ps, T0s, Ks, hs, ks = planettheta incs = {} for p in Ps.keys(): incs[p] = 90. else: Ps, T0s, Ks, hs, ks, incs = planettheta hs, ks = np.array(hs.values()), np.array(ks.values()) eccstmp, omegastmp = hs * hs + ks * ks, np.arctan2(ks, hs) eccs, omegas, ind = {}, {}, 0 for p in Ps.keys(): eccs[p], omegas[p] = eccstmp[ind], omegastmp[ind] ind += 1 # Add star sim.add(m=Ms, hash='star') # Add planets for p in Ps.keys(): print Ps[p], Ms, Ks[p], eccs[p] mp = rvs.kg2Msun( rvs.Mearth2kg(rvs.RV_mp(Ps[p], Ms, Ks[p], ecc=eccs[p]))) Pp = Ps[p] ap = rvs.m2AU(rvs.semimajoraxis(Pp, Ms, 0)) thetap = 2 * np.pi * foldAt(bjd0, Pp, T0s[p]) eccp, omegap, incp = eccs[p], omegas[p], np.deg2rad(incs[p]) if eccupperlim != 0: eccp = np.random.uniform(0, eccupperlim) omegap = np.random.uniform(0, 2 * np.pi) Omegap = np.random.uniform(0, 2 * np.pi) if randomOmega else 0. sim.add(m=mp, a=ap, inc=incp, e=eccp, omega=omegap, Omega=Omegap, theta=thetap, hash=p) sim.move_to_com() return sim