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 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 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 _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 __init__(self, folder, index, index2, Nyrs=1e6, Nout=500): self.index, self.index2, self.Nyrs, self.Nout = int(index), int( index2), int(Nyrs), int(Nout) self.Ms = 0. while self.Ms <= 0: self.Ms = Ms + np.random.randn() * .0032 self.bjd0 = 2458354.101878819 # first TESS photometry epoch self.DONE = False # Setup simulation self.folder = folder self.outname = '%s/SimArchive/archived%.4d_%.4d' % ( self.folder, self.index, self.index2) sim = setupsim(self, self.outname) self.mps, self.sma0, self.ecc0, self.inc0, self.omega0, self.Omega0, self.theta0 = get_initial_parameters( sim) self.inc0 += 90 #self.Rhill = get_Rhill_init(self.mps, self.Ms, self.sma0) self.pickleobject() # Integrate simulation print 'Integrating system...' self.bjds,self.RVs,self.smas,self.eccs,self.incs,self.dist,self.stable = \ integrate_sim(np.linspace(0, rvs.yrs2days(self.Nyrs), self.Nout) + self.bjd0, sim) self.bs = np.zeros(self.incs.shape) for i in range(self.bjds.size): self.bs[i] = rvs.impactparam_inc(rvs.AU2m(self.smas[i]) / rvs.Rsun2m(Rs), self.incs[i] + 90, ecc=self.eccs[i]) self.DONE = True self.pickleobject()
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 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_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 integrate_sim(bjd, sim): ''' Integrate the system forward in time with outputs computed over the window function. Parameters ---------- `bjd` : array (Nobs,) Numpy array of the window function to output measurements at in BJD. For dynamical modeling of RVs this should be the window function of observations. For dynamical stability it should be much larger. `sim` : rebound simulation object Output from setup_sim(); the simulation to integrate forward in time. Returns ------- `bjd` : array (Nobs,) The input window function; time arrray. `RVs` : array (Nobs,) Numpy array of the stellar RVs in m/s. `smas` : array (Nobs, Nplanets,) Numpy array of each planets' semimajor axis at the times in bjd in AU. `eccs` : array (Nobs, Nplanets,) Numpy array of each planets' eccentricity at the times in bjd. `incs` : array (Nobs, Nplanets,) Numpy array of each planets' inclination at the times in bjd in degrees. ''' # Get window function for outputs times = days2years( bjd - bjd.min()) ##np.linspace(sim.t, sim.t+Tfin_yrs, int(Nout)) # Get timestep nparticles, dts = len( sim.particles.keys()), [np.diff(days2years(times)).min() * 1e-1] for i in range(1, nparticles): dts.append(sim.particles[i].P * 1e-1) sim.dt = np.min(dts) # Save output nparticles = len(sim.particles.keys()) RVs = np.zeros(times.size) stable = True smas, eccs, incs = np.zeros((times.size, nparticles-1)), \ np.zeros((times.size, nparticles-1)), \ np.zeros((times.size, nparticles-1)) dist = np.zeros(times.size) ps = sim.particles try: for i in range(times.size): sim.integrate(times[i]) dp = ps[1] - ps[2] dist[i] = np.sqrt(dp.x**2 + dp.y**2 + dp.z**2) RVs[i] = -sim.particles['star'].vx for j in range(nparticles - 1): smas[i, j] = sim.particles[j + 1].a # AU eccs[i, j] = sim.particles[j + 1].e incs[i, j] = np.rad2deg(sim.particles[j + 1].inc) except rebound.Encounter as error: stable = False # Convert units (AU/yr -> m/s) RVs = rvs.AU2m(RVs) / (365.25 * 24 * 60 * 60) return bjd, RVs, smas, eccs, incs, dist, stable