def test_transits(): """Test transit light curve generation.""" # Input params u1 = 0.4 u2 = 0.26 mstar = 1 # solar masses rstar = 1 # solar radii rplanet = 0.1 # fraction of stellar radius b0 = 0.5 # impact parameter P = 50 # orbital period in days npts = 25 time = np.linspace(-0.25, 0.25, npts) # Compute the semi-major axis from Kepler's third law in units of rstar a = ((P * 86400) ** 2 * (1.32712440018e20 * mstar) / (4 * np.pi ** 2)) ** (1. / 3.) / (6.957e8 * rstar) # Get the inclination in degrees inc = np.arccos(b0 / a) * 180 / np.pi # Compute the flux from numerical integration f = 2 * np.pi / P * time b = a * np.sqrt(1 - np.sin(np.pi / 2. + f) ** 2 * np.sin(inc * np.pi / 180) ** 2) nF = np.zeros_like(time) for i in range(npts): nF[i] = NumericalFlux(b[i], rplanet, u1, u2) nF /= np.nanmax(nF) den = (1 - nF) den[den == 0] = 1e-10 # Compute the starry flux # Instantiate a second-order map and a third-order map with u(3) = 0 # The second-order map is optimized for speed and uses different # equations, but they should yield identical results. for lmax in [2, 3]: star = Primary(lmax) star[1] = u1 star[2] = u2 planet = Secondary() planet.r = rplanet planet.a = a planet.inc = inc planet.porb = P planet.lambda0 = 90 system = System(star, planet) system.compute(time) sF = np.array(star.lightcurve) sF /= sF[0] # Compute the error, check that it's better than 1 ppb error = np.max((np.abs(nF - sF) / den)[np.where(nF < 1)]) assert error < 1e-9
def generate(x, tstart=1, tend=5.3, npts=100, ning=100, neg=100): """Generate a synthetic light curve.""" # Instantiate the star (params known exactly) star = Primary() star[1] = 0.4 star[2] = 0.26 # Instantiate the planet planet = Secondary(lmax=1) planet.lambda0 = 270 planet.r = 0.0916 planet.L = 5e-3 planet.inc = 87 planet.a = 11.12799 planet.prot = 4.3 planet.porb = 4.3 planet.tref = 2.0 # Instantiate the system system = System(star, planet) # Set the map coeffs set_coeffs(x, planet) # Time array w/ extra resolution at ingress/egress ingress = (1.94, 1.96) egress = (2.04, 2.06) time = np.linspace(tstart, tend, npts) if ingress is not None: t = np.linspace(ingress[0], ingress[1], ning) time = np.append(time, t) if egress is not None: t = np.linspace(egress[0], egress[1], neg) time = np.append(time, t) time = time[np.argsort(time)] # Compute and plot the starry flux system.compute(time) flux = np.array(system.lightcurve) # Noise it yerr = 1e-4 * np.nanmedian(flux) y = flux + yerr * np.random.randn(len(flux)) # Compute the flux at hi res for plotting time_hires = np.linspace(tstart, tend, npts * 100) system.compute(time_hires) flux_hires = np.array(system.lightcurve) return time, y, yerr, star, planet, system, time_hires, flux_hires
# System #1: A one-planet system with a hotspot offset # ---------------------------------------------------- # Instantiate the star star = Primary() # Give the star a quadratic limb darkening profile star[1] = 0.4 star[2] = 0.26 # Instantiate planet b b = Secondary() b.r = 0.091679 b.L = 5e-3 b.inc = 90 b.porb = 4.3 b.prot = 4.3 b.a = 11.127991 b.lambda0 = 90 b.tref = 2 b.axis = [0, 1, 0] # Give the planet a simple dipole map b[1, 0] = 0.5 # Rotate the planet map to produce a hotspot offset of 15 degrees b.rotate(theta=15) # Compute and plot the starry flux time = np.linspace(0, 20, 10000) system = System(star, b)
# Time arrays time_transit = np.linspace(-0.3, 0.2, 2500) time_secondary = np.linspace(24.75, 25.5, 2500) # Limb-darkened star star = Primary() star[1] = 0.4 star[2] = 0.26 star.r_m = 0 # Dipole-map hot jupiter planet = Secondary() planet.r = 0.1 planet.a = 60 planet.inc = 89.5 planet.porb = 50 planet.prot = 2.49 planet.lambda0 = 89.9 planet.ecc = 0.3 planet.w = 89 planet.L = 1e-3 planet[1, 0] = 0.5 # Instantiate the system system = System(star, planet) system.exposure_time = 0 # Set up the plot fig = pl.figure(figsize=(8, 8)) fig.subplots_adjust(hspace=0, bottom=0.05, top=0.95)
def test_gradients(plot=False): """Test the gradients in the `System` class.""" # Limb-darkened star A = Primary(lmax=3) A[1] = 0.4 A[2] = 0.26 A[3] = -0.25 A.r_m = 1e11 # Dipole-map hot jupiter b = Secondary(lmax=2) b.r = 0.09 b.a = 60 b.inc = 89.943 b.porb = 50 b.prot = 2.49 b.lambda0 = 89.9 b.ecc = 0.3 b.w = 89 b.L = 1.75e-3 b[1, 0] = 0.5 b[2, 1] = 0.1 b[2, 2] = -0.05 # Dipole-map hot jupiter c = Secondary(lmax=1) c.r = 0.12 c.a = 80 c.inc = 89.95 c.porb = 100 c.prot = 7.83 c.lambda0 = 85 c.ecc = 0.29 c.w = 87.4 c.L = 1.5e-3 c[1, 0] = 0.4 # Instantiate the system # We're adding a ton of light travel delay # and a finite exposure time: this is a # comprehensive test of the main `starry` features system = System(A, b, c) system.exposure_time = 0.02 # Light curves and gradients of this object object = system # Let's plot transit, eclipse, and a PPO for t1, t2, figname in zip([-0.425, 25.1, -2.6], [0.0, 25.75, -2.0], [ "gradients_transit.png", "gradients_eclipse.png", "gradients_ppo.png" ]): # Time arrays time = np.linspace(t1, t2, 500) time_num = np.linspace(t1, t2, 50) # Set up the plot if plot: fig = pl.figure(figsize=(6, 10)) fig.subplots_adjust(hspace=0, bottom=0.05, top=0.95) # Run! system.compute(time, gradient=True) flux = np.array(object.lightcurve) grad = dict(object.gradient) # Numerical flux system.compute(time_num, gradient=True) flux_num = np.array(object.lightcurve) # Plot it if plot: ax = pl.subplot2grid((18, 3), (0, 0), rowspan=5, colspan=3) ax.plot(time, flux, color='C0') ax.plot(time_num, flux_num, 'o', ms=3, color='C1') ax.set_yticks([]) ax.set_xticks([]) [i.set_linewidth(0.) for i in ax.spines.values()] col = 0 row = 0 eps = 1e-8 error_rel = [] for key in grad.keys(): if key.endswith('.y') or key.endswith('.u'): for i, gradient in enumerate(grad[key]): if plot: axg = pl.subplot2grid((18, 3), (5 + row, col), colspan=1) axg.plot(time, gradient, lw=1, color='C0') if key.endswith('.y'): y0 = eval(key) y = np.array(y0) y[i + 1] += eps exec(key[0] + "[:, :] = y") system.compute(time) exec(key[0] + "[:, :] = y0") else: u0 = eval(key) u = np.array(u0) u[i] += eps exec(key[0] + "[:] = u") system.compute(time) exec(key[0] + "[:] = u0") numgrad = (object.lightcurve - flux) / eps error_rel.append(np.max(abs(numgrad - gradient))) if plot: axg.plot(time, numgrad, lw=1, alpha=0.5, color='C1') axg.set_ylabel(r"$%s_%d$" % (key, i), fontsize=5) axg.margins(None, 0.5) axg.set_xticks([]) axg.set_yticks([]) [i.set_linewidth(0.) for i in axg.spines.values()] if row < 12: row += 1 else: row = 0 col += 1 else: if plot: axg = pl.subplot2grid((18, 3), (5 + row, col), colspan=1) axg.plot(time, grad[key], lw=1, color='C0') exec(key + " += eps") system.compute(time) exec(key + " -= eps") numgrad = (object.lightcurve - flux) / eps error_rel.append(np.max(abs(numgrad - grad[key]))) if plot: axg.plot(time, numgrad, lw=1, alpha=0.5, color='C1') axg.margins(None, 0.5) axg.set_xticks([]) axg.set_yticks([]) axg.set_ylabel(r"$%s$" % key, fontsize=5) [i.set_linewidth(0.) for i in axg.spines.values()] if row < 12: row += 1 else: row = 0 col += 1 # Generous error tolerance assert np.all(np.array(error_rel) < 1e-5) # Save the figure if plot: fig.savefig(figname, bbox_inches='tight', dpi=300) pl.close()
def test_light_travel(): """Run the test.""" # No delay star = Primary() star[1] = 0.40 star[2] = 0.26 star.r_m = 0 planet = Secondary() planet.L = 1e-3 planet.r = 0.1 planet.porb = 10 planet.prot = 10 planet.a = 22 planet[1, 0] = 0.5 system = System(star, planet) time = np.concatenate( (np.linspace(0, 0.5, 10000, endpoint=False), np.linspace(0.5, 4.5, 100, endpoint=False), np.linspace(4.5, 5.5, 10000, endpoint=False), np.linspace(5.5, 9.5, 100, endpoint=False), np.linspace(9.5, 10, 10000))) phase = time / 10. system.compute(time) transit = phase[np.argmax(planet.Z)] eclipse = phase[np.argmin(planet.Z)] assert transit == 0 assert eclipse == 0.5 # Let's give the system a real length scale now star.r_m = 6.95700e8 eps = 1e-3 time = np.concatenate( (np.linspace(0, 0.5, 100000, endpoint=False), np.linspace(0.5, 5 - eps, 100, endpoint=False), np.linspace(5 - eps, 5 + eps, 1000000, endpoint=False), np.linspace(5 + eps, 9.5, 100, endpoint=False), np.linspace(9.5, 10, 100000))) phase = time / planet.porb system.compute(time) transit = phase[np.argmin(np.abs(planet.X) + (planet.Z < 0))] eclipse = phase[np.argmin(np.abs(planet.X) + (planet.Z > 0))] assert transit == 0.999940999409994 assert eclipse == 0.5000590894 # Run some extra tests c = 299792458. RSUN = 6.957e8 time = np.concatenate( (np.linspace(0, 0.5, 100000, endpoint=False), np.linspace(0.5, 4.5, 100, endpoint=False), np.linspace(4.5, 5.5, 100000, endpoint=False), np.linspace(5.5, 9.5, 100, endpoint=False), np.linspace(9.5, 10, 100000))) phase = time / planet.porb # Test this on an inclined orbit planet.inc = 30 system.compute(time) transit = phase[np.argmax(planet.Z)] eclipse = phase[np.argmin(planet.Z)] delay_starry = (0.5 - (transit - eclipse)) * planet.porb * 86400 a = planet.a * star.r_m delay_analytic = 2 * a / c * np.sin(planet.inc * np.pi / 180) assert np.abs(1 - delay_starry / delay_analytic) < 0.01 # Test this on an orbit with nonzero Omega planet.inc = 90 planet.Omega = 90 system.compute(time) transit = phase[np.argmax(planet.Z)] eclipse = phase[np.argmin(planet.Z)] delay_starry = (0.5 - (transit - eclipse)) * planet.porb * 86400 a = planet.a * star.r_m delay_analytic = 2 * a / c assert np.abs(1 - delay_starry / delay_analytic) < 0.01 # Compute the light travel time from the secondary # eclipse to the barycenter for an eccentric orbit. # First, compute the phase of secondary eclipse # with no light travel time delay: planet.inc = 90 planet.Omega = 0 planet.w = 30 planet.ecc = 0.25 star.r_m = 0 system.compute(time) eclipse0 = phase[np.argmin(planet.Z)] z0 = planet.Z[np.argmin(planet.Z)] # Now, compute the phase w/ the delay star.r_m = 6.95700e8 system.compute(time) eclipse = phase[np.argmin(planet.Z)] # Compute the light travel time to the barycenter in days: travel_time_starry = (eclipse - eclipse0) * planet.porb * 86400 # Compute the analytic light travel time to the barycenter in days: travel_time_analytic = (0 - z0) * star.r_m / c assert np.abs(1 - travel_time_starry / travel_time_analytic) < 0.01
# Compute the semi-major axis from Kepler's third law in units of rstar a = ((P * 86400)**2 * (1.32712440018e20 * mstar) / (4 * np.pi**2))**(1. / 3.) / (6.957e8 * rstar) # Get the inclination in degrees inc = np.arccos(b0 / a) * 180 / np.pi # Compute and plot the starry flux star = Primary() star[1] = u1 star[2] = u2 planet = Secondary() planet.r = rplanet planet.inc = inc planet.porb = P planet.a = a planet.lambda0 = 90 system = System(star, planet) system.compute(time) sF = np.array(star.lightcurve) sF /= sF[0] ax[0].plot(time, sF, '-', color='C0', label='starry') # Compute and plot the flux from numerical integration print("Computing numerical flux...") f = 2 * np.pi / P * time b = a * np.sqrt(1 - np.sin(np.pi / 2. + f)**2 * np.sin(inc * np.pi / 180)**2) nF = np.zeros_like(time) for i in tqdm(range(npts)): nF[i] = NumericalFlux(b[i], rplanet, u1, u2)