def test_reflected_light(): pri = starry.Primary(starry.Map(amp=0), r=1) sec = starry.Secondary(starry.Map(reflected=True), porb=1.0, r=1) sys = starry.System(pri, sec) t = np.concatenate((np.linspace(0.1, 0.4, 50), np.linspace(0.6, 0.9, 50))) flux = sys.flux(t)
import theano import theano.tensor as tt import numpy as np import starry import matplotlib.pyplot as plt import pytest map = starry.Map(ydeg=1, reflected=True) _b = tt.dvector("b") _theta = tt.dvector("theta") _bo = tt.dvector("bo") _ro = tt.dscalar("ro") _sigr = tt.dscalar("sigr") _s = theano.function([_b, _theta, _bo, _ro, _sigr], map.ops.sT(_b, _theta, _bo, _ro, _sigr)) def s(b, theta, bo, ro, sigr, n=0): if hasattr(ro, "__len__"): assert not (hasattr(b, "__len__") or hasattr(theta, "__len__") or hasattr(bo, "__len__") or hasattr(sigr, "__len__")) return [ _s([b], [theta], [bo], ro[i], sigr)[0, n] for i in range(len(ro)) ] elif hasattr(sigr, "__len__"): assert not (hasattr(b, "__len__") or hasattr(theta, "__len__") or hasattr(bo, "__len__") or hasattr(ro, "__len__")) return [ _s([b], [theta], [bo], ro, sigr[i])[0, n] for i in range(len(sigr)) ] else:
def test_default_system_units(): pri = starry.Primary(starry.Map()) sec = starry.Secondary(starry.Map(), porb=1.0) sys = starry.System(pri, sec) assert sys.time_unit == u.day
def earth_eclipse(lmax=20): """Compute the error on the secondary eclipse of the Earth.""" npts = 1000 # Create our map map = starry.Map(lmax) map.load_image('earth') # Compute. Ingress duration is # dt = (2 REARTH) / (2 PI * 1 AU / 1 year) ~ 7 minutes yo = 0 ro = 6.957e8 / 6.3781e6 time = np.linspace(0, 7 * 1.5, npts) xo = np.linspace(-(ro + 1.5), -(ro - 1.5), npts, -1) flux = np.array(map.flux(xo=xo, yo=yo, ro=ro)) # Compute at high precision map_128 = starry.Map(lmax, multi=True) map_128[:, :] = map[:, :] flux128 = np.array(map_128.flux(xo=xo, yo=yo, ro=ro)) # Show fig = pl.figure(figsize=(7, 6)) nim = 10 ax = [pl.subplot2grid((7, nim), (1, 0), colspan=nim, rowspan=3), pl.subplot2grid((7, nim), (4, 0), colspan=nim, rowspan=3)] fig.subplots_adjust(hspace=0.6) ax[0].plot(time, flux / flux[0]) ax[1].plot(time, np.abs(flux - flux128)) ax[1].set_yscale('log') ax[1].axhline(1e-3, color='k', ls='--', alpha=0.75, lw=0.5) ax[1].axhline(1e-6, color='k', ls='--', alpha=0.75, lw=0.5) ax[1].axhline(1e-9, color='k', ls='--', alpha=0.75, lw=0.5) ax[1].annotate("ppt", xy=(1e-3, 1e-3), xycoords="data", xytext=(3, -3), textcoords="offset points", ha="left", va="top", alpha=0.75) ax[1].annotate("ppm", xy=(1e-3, 1e-6), xycoords="data", xytext=(3, -3), textcoords="offset points", ha="left", va="top", alpha=0.75) ax[1].annotate("ppb", xy=(1e-3, 1e-9), xycoords="data", xytext=(3, -3), textcoords="offset points", ha="left", va="top", alpha=0.75) ax[1].set_ylim(5e-17, 20.) ax[0].set_xlim(0, time[-1]) ax[1].set_xlim(0, time[-1]) ax[1].set_xlabel("Time [minutes]", fontsize=16) ax[0].set_ylabel("Normalized flux", fontsize=16, labelpad=15) ax[1].set_ylabel("Relative error", fontsize=16) # Plot the earth images res = 100 ax_im = [pl.subplot2grid((7, nim), (0, n)) for n in range(nim)] x, y = np.meshgrid(np.linspace(-1, 1, res), np.linspace(-1, 1, res)) map.axis = [0, 1, 0] for n in range(nim): i = int(np.linspace(0, npts - 1, nim)[n]) I = [map(theta=0, x=x[j], y=y[j]) for j in range(res)] ax_im[n].imshow(I, origin="lower", interpolation="none", cmap='plasma', extent=(-1, 1, -1, 1)) xm = np.linspace(xo[i] - ro + 1e-5, xo[i] + ro - 1e-5, 10000) ax_im[n].fill_between(xm, yo - np.sqrt(ro ** 2 - (xm - xo[i]) ** 2), yo + np.sqrt(ro ** 2 - (xm - xo[i]) ** 2), color='w') ax_im[n].axis('off') ax_im[n].set_xlim(-1.05, 1.05) ax_im[n].set_ylim(-1.05, 1.05) fig.savefig("stability_earth.pdf", bbox_inches='tight')
def test_lightcurve(b, theta, ro, ydeg=1, ns=1000, nb=50, res=999, plot=False): # Array over full occultation, including all singularities xo = 0.0 yo = np.linspace(0, 1 + ro, ns, endpoint=True) for pt in [ro, 1, 1 - ro, b + ro]: if pt >= 0: yo[np.argmin(np.abs(yo - pt))] = pt if theta == 0: xs = 0 ys = 1 else: xs = 0.5 ys = -xs / np.tan(theta) rxy2 = xs ** 2 + ys ** 2 if b == 0: zs = 0 elif b == 1: zs = -1 xs = 0 ys = 0 elif b == -1: zs = 1 xs = 0 ys = 0 else: zs = -np.sign(b) * np.sqrt(rxy2 / (b ** -2 - 1)) # Compute analytic map = starry.Map(ydeg=ydeg, reflected=True) map[1:, :] = 1 flux = map.flux(xs=xs, ys=ys, zs=zs, xo=xo, yo=yo, ro=ro) # Compute numerical flux_num = np.zeros_like(yo) * np.nan computed = np.zeros(ns, dtype=bool) (lat, lon), (x, y, z) = map.ops.compute_ortho_grid(res) img = map.render(xs=xs, ys=ys, zs=zs, res=res).flatten() for i, yoi in tqdm(enumerate(yo), total=len(yo)): if (i == 0) or (i == ns - 1) or (i % (ns // nb) == 0): idx = (x - xo) ** 2 + (y - yoi) ** 2 > ro ** 2 flux_num[i] = np.nansum(img[idx]) * 4 / res ** 2 computed[i] = True # Interpolate over numerical result f = interp1d(yo[computed], flux_num[computed], kind="cubic") flux_num_interp = f(yo) # Plot if plot: fig = plt.figure() plt.plot(yo, flux, "C0-", label="starry", lw=2) plt.plot(yo, flux_num, "C1o", label="brute") plt.plot(yo, flux_num_interp, "C1-", lw=1) plt.legend(loc="best") plt.xlabel("impact parameter") plt.ylabel("flux") fig.savefig( "test_lightcurve[{}-{}-{}].pdf".format(b, theta, ro), bbox_inches="tight", ) plt.close() # Compare with very lax tolerance; we're mostly looking # for gross outliers diff = np.abs(flux - flux_num_interp) assert np.max(diff) < 0.001
def test_show_moll(): map = starry.Map(ydeg=1, udeg=1) map.show(file="tmp.pdf", projection="moll") os.remove("tmp.pdf")
def test_show_ld(): map = starry.Map(udeg=2) map.show(file="tmp.pdf") os.remove("tmp.pdf")
ax[0, 0].set_visible(False) ax[0, 1].set_visible(False) ax[1, 0].set_ylabel("flux [normalized]", fontsize=10) ax[1, 0].set_title("thermal phase curve", fontsize=12, fontweight="bold") ax[1, 1].set_title("reflected phase curve", fontsize=12, fontweight="bold") ax[2, 0].set_ylabel("flux [normalized]", fontsize=10) ax[3, 0].set_title("thermal occultation", fontsize=12, fontweight="bold") ax[3, 1].set_title("reflected occultation", fontsize=12, fontweight="bold") for axis in np.append(ax[1, :], ax[3, :]): axis.tick_params(labelsize=8) axis.set_ylim(-0.05, 1.65) # Show the true map ax_top = fig.add_subplot(5, 1, 1) ax_top.set_title("input", fontsize=12, fontweight="bold") map = starry.Map(20) map.load("earth", sigma=0.1) map.show(ax=ax_top, projection="moll") # Solve the least-squares problem for thermal & reflected phase curves for j, rmoon in enumerate([0, 0.25]): for i, reflected in enumerate([False, True]): # Instantiate a map of the Earth map = starry.Map(20, reflected=reflected) map.load("earth", sigma=0.1) obl = 23.5 porb = 365.25 prot = 1.0 pmoon = 1.37 amoon = 2.0
def test_I_stability(noon, plot=False): # FB found this unstable limit. The instability comes # from two places: # 1. The upward recursion in the I integral is unstable. # We need to either implement a tridiagonal solver as # in the J integral and/or refine our computation of kappa. # 2. The terminator parameter b = 1, which causes `get_angles` to # oscillate between different integration codes. When b # approaches unity, we should switch to the regular starry # solver, since the terminator is so close to the limb that its # presence doesn't matter. xo, yo, zo, ro = ( 31.03953239062832, 23.892679948795926, 1.0, 39.10406741663172, ) # Exactly noon? if noon: xs = 0.0 else: xs = 0.1 ys = 0.0 zs = 1.0 xo = np.linspace(xo - 2, xo + 2, 1000) # Compute analytic map = starry.Map(ydeg=10, reflected=True) map[10, :] = 1 flux1 = map.flux(xo=xo, yo=yo, zo=zo, ro=ro, xs=xs, ys=ys, zs=zs) if noon: # The flux above should be *exactly* equal to 2/3 the flux of a # linearly-limb darkened source with u_1 = 1.0, since linear # limb darkening weights the surface brightness by the same # cosine-like profile (2/3 is the geometrical albedo of a # perfect Lambert sphere) map_e = starry.Map(ydeg=10, udeg=1) map_e[10, :] = 1 map_e[1] = 1 flux2 = (2.0 / 3.0) * map_e.flux(xo=xo, yo=yo, zo=zo, ro=ro) atol = 1e-12 else: # Compute numerical res = 500 x, y, z = map.ops.compute_ortho_grid(res) image = map.render(xs=xs, ys=ys, zs=zs, res=res).flatten() flux2 = np.zeros_like(flux1) for k in range(len(xo)): idx = (x - xo[k]) ** 2 + (y - yo) ** 2 > ro ** 2 flux2[k] = np.nansum(image[idx]) flux2 *= 4 / res ** 2 atol = 1e-3 # Plot it if plot: plt.plot(xo, flux1) plt.plot(xo, flux2) plt.show() # Compare assert np.allclose(flux1, flux2, atol=atol)
def map(): return starry.Map(1, reflected=True)
# -*- coding: utf-8 -*- """ There's a bug in tt.mgrid that causes different behavior whether it's compiled or not. We implemented some hacks in `starry` to circumvent this. See docstring of `compute_ortho_grid` in "core.py" """ import starry import pytest import theano.tensor as tt import itertools import numpy as np map = starry.Map(1) @pytest.mark.parametrize("compile", [True, False]) def test_ortho_grid(compile): for res in np.arange(30, 101): if compile: (lat, lon), (x, y, z) = map.ops.compute_ortho_grid(res) else: latlon, xyz = map.ops.compute_ortho_grid( tt.as_tensor_variable(res)) x, y, z = xyz.eval() assert len(x) == res**2
"""55 cancrie e secondary eclipse example.""" import starry import matplotlib.pyplot as plt import numpy as np import astropy.units as u # Config starry.config.lazy = False # Star star_map = starry.Map(udeg=2, amp=1) star_map[1:] = [0.5, 0.25] star = starry.Primary(star_map, m=0.905, mass_unit=u.M_sun, r=0.943, length_unit=u.R_sun) # Planet kwargs = dict( m=7.99, mass_unit=u.earthMass, porb=0.73654737, r=1.875, length_unit=u.earthRad, inc=83.59, t0=0.400473685, ) # Time arrays (secondary eclipse ingress / full phase curve) t_ingress = np.linspace(0, 0.002, 1000)
# -*- coding: utf-8 -*- import matplotlib import matplotlib.pyplot as plt import numpy as np import starry ydeg = 10 theta = np.linspace(-180, 180, 1000) s = np.zeros(((ydeg + 1)**2, len(theta))) map = starry.Map(ydeg, lazy=False) n = 0 for l in range(ydeg + 1): for m in range(-l, l + 1): map.reset() if l > 0: map[l, m] = 1.0 s[n] = map.flux(theta=theta) n += 1 # Set up the plot fig, ax = plt.subplots(ydeg + 1, 2 * ydeg + 1, figsize=(16, 10), sharex=True, sharey=True) fig.subplots_adjust(hspace=0) for axis in ax.flatten(): axis.spines['top'].set_visible(False) axis.spines['right'].set_visible(False) axis.spines['bottom'].set_visible(False) axis.spines['left'].set_visible(False)
def map(request): (nw, ) = request.param map = starry.Map(ydeg=5, udeg=2, nw=nw) return map
# -*- coding: utf-8 -*- """ Show the effect a rotating spot has on an absorption line. """ import matplotlib.pyplot as plt import numpy as np import starry import paparazzi as pp from matplotlib.animation import FuncAnimation # Generate two maps ydeg = 20 N = (ydeg + 1)**2 map1 = starry.Map(ydeg) map1.add_spot(amp=-0.03, sigma=0.05, lat=30, lon=0) map1.inc = 90 map2 = starry.Map(ydeg) map2.load("spot") map2.inc = 40 for map, name in zip([map1, map2], ["spot1", "spot2"]): # Get the map coeffs y1 = np.array(map.y.eval())[1:] # Generate the dataset vsini = 40.0 # km/s nt = 100 theta = np.linspace(-180, 180, nt)
def map(request): ydeg, udeg, nw, rv, reflected = request.param map = starry.Map(ydeg=ydeg, udeg=udeg, nw=nw, reflected=reflected, rv=rv) map.reflected = reflected return map
def test_show_with_figure(): map = starry.Map(ydeg=1, udeg=1) fig, ax = plt.subplots(1) map.show(ax=ax, file="tmp.pdf", projection="ortho") os.remove("tmp.pdf")
def test_amplitude(): """Test the amplitude attribute of a multi-wavelength map.""" map = starry.Map(ydeg=1, nw=5) assert np.allclose(map.amp, np.ones(5)) map.amp = 10.0 assert np.allclose(map.amp, 10.0 * np.ones(5))
def test_show_colorbar(): map = starry.Map(ydeg=1, udeg=1) map.show(file="tmp.pdf", projection="ortho", colorbar=True) os.remove("tmp.pdf")
def test_edges(xs, ys, zs, ro, y=[1, 1, 1], ns=100, nb=50, res=999, atol=1e-2, plot=False): # Instantiate ydeg = np.sqrt(len(y) + 1) - 1 map = starry.Map(ydeg=ydeg, reflected=True) map[1:, :] = y # bo - ro singularities singularities = [ro - 1, 0, ro, 1, 1 - ro, 1 + ro] labels = [ "$b_o = r_o - 1$", "$b_o = 0$", "$b_o = r_o$", "$b_o = 1$", "$b_o = 1 - r_o$", "$b_o = 1 + r_o$", "grazing", "grazing", ] # Find where the occultor grazes the terminator rs = np.sqrt(xs**2 + ys**2 + zs**2) b = -zs / rs theta = -np.arctan2(xs, ys) tol = 1e-15 nx = 10 c = np.cos(theta) s = np.sin(theta) t = np.tan(theta) q2 = c**2 + b**2 * s**2 # Bottom / top half of occultor for sgn0 in [1, -1]: # Successively refine x array xest = 0 xdel = ro for j in range(10): x = np.linspace(xest - xdel, xest + xdel, nx) # Divide & conquer yomax = 1 + ro yomin = -1 - ro niter = 0 xest = 0 while niter < 100 and np.abs(yomax - yomin) > tol: yo_ = 0.5 * (yomax + yomin) y = yo_ + sgn0 * np.sqrt(ro**2 - x**2) try: # Scan the x axis for an intersection for i in range(nx): # There are two solutions to the quadratic; pick # the one that's actually on the ellipse p = (x[i] * c - b * s * np.sqrt(q2 - x[i]**2)) / q2 yt1 = p * s + b * np.sqrt(1 - p**2) * c xr = x[i] * c + yt1 * s yr = -x[i] * s + yt1 * c arg1 = np.abs(xr**2 + (yr / b)**2 - 1) p = (x[i] * c + b * s * np.sqrt(q2 - x[i]**2)) / q2 yt2 = p * s + b * np.sqrt(1 - p**2) * c xr = x[i] * c + yt2 * s yr = -x[i] * s + yt2 * c arg2 = np.abs(xr**2 + (yr / b)**2 - 1) if arg1 < arg2: if arg1 < 1e-6: yt = yt1 else: continue elif arg2 < arg1: if arg2 < 1e-6: yt = yt2 else: continue else: continue if (sgn0 == -1) and (y[i] < yt): # Part of the occultor has dipped below the terminator yomin = yo_ xest = x[i] raise StopIteration if (sgn0 == 1) and (y[i] > yt): # Part of the occultor has dipped above the terminator yomax = yo_ xest = x[i] raise StopIteration except StopIteration: niter += 1 continue else: niter += 1 if sgn0 == -1: # The occultor is above the terminator everywhere yomax = yo_ else: # The occultor is below the terminator everywhere yomin = yo_ # Increase res by 10x xdel /= 10 singularities.append(yo_) # Arrays over singularities yo_s = np.zeros((8, ns)) logdelta = np.append(-np.inf, np.linspace(-16, -2, ns // 2 - 1)) delta = np.concatenate((-(10**logdelta[::-1]), 10**logdelta)) for i, pt in enumerate(singularities): yo_s[i] = pt + delta yo_s = yo_s[np.argsort(singularities)] labels = list(np.array(labels)[np.argsort(singularities)]) # Array over full occultation yo_full = np.linspace(yo_s[0, 0], yo_s[-1, -1], ns, endpoint=True) # All yo = np.concatenate((yo_full.reshape(1, -1), yo_s)) # Compute analytic flux = np.zeros_like(yo) msg = [["" for n in range(yo.shape[1])] for m in range(yo.shape[0])] for i in range(len(yo)): for k in tqdm(range(ns)): try: flux[i, k] = map.flux(xs=xs, ys=ys, zs=zs, xo=0, yo=yo[i, k], ro=ro) except Exception as e: flux[i, k] = 0.0 msg[i][k] = str(e).split("\n")[0] # Compute numerical flux_num = np.zeros_like(yo) * np.nan flux_num_interp = np.zeros_like(yo) * np.nan x, y, z = map.ops.compute_ortho_grid(res) img = map.render(xs=xs, ys=ys, zs=zs, res=res).flatten() for i in range(len(yo)): for k in tqdm(range(ns)): idx = x**2 + (y - yo[i, k])**2 > ro**2 flux_num_interp[i, k] = np.nansum(img[idx]) * 4 / res**2 if (k == 0) or (k == ns - 1) or (k % (ns // nb) == 0): flux_num[i, k] = flux_num_interp[i, k] # Adjust the baseline offset = np.nanmedian(flux[i]) - np.nanmedian(flux_num_interp[i]) flux_num_interp[i] += offset flux_num[i] += offset # Plot if plot: fig = plt.figure(figsize=(10, 8)) fig.subplots_adjust(hspace=0.35) ax = [ plt.subplot2grid((40, 40), (0, 0), rowspan=15, colspan=40), plt.subplot2grid((40, 40), (20, 0), rowspan=10, colspan=10), plt.subplot2grid((40, 40), (20, 10), rowspan=10, colspan=10), plt.subplot2grid((40, 40), (20, 20), rowspan=10, colspan=10), plt.subplot2grid((40, 40), (20, 30), rowspan=10, colspan=10), plt.subplot2grid((40, 40), (30, 0), rowspan=10, colspan=10), plt.subplot2grid((40, 40), (30, 10), rowspan=10, colspan=10), plt.subplot2grid((40, 40), (30, 20), rowspan=10, colspan=10), plt.subplot2grid((40, 40), (30, 30), rowspan=10, colspan=10), ] # Prepare image for plotting img[(img < 0) | (img > 0.0)] = 1 img = img.reshape(res, res) cmap = plt.get_cmap("plasma") cmap.set_under("grey") # Full light curve ax[0].plot(yo[0], flux[0], "k-", lw=1) ax[0].plot(yo[0], flux_num[0], "k.", lw=1) ax[0].tick_params(labelsize=10) ax[0].set_xlabel("$b_o$") ax[0].set_ylabel("flux") # Each singularity for i in range(1, len(yo)): ax[0].plot(yo[i], flux[i], lw=3, color="C{}".format(i - 1)) ax[i].plot( 2 + logdelta, flux[i][:ns // 2], lw=2, color="C{}".format(i - 1), ) ax[i].plot( -(2 + logdelta)[::-1], flux[i][ns // 2:], lw=2, color="C{}".format(i - 1), ) ax[i].plot(2 + logdelta, flux_num[i][:ns // 2], "k.", ms=2) ax[i].plot(-(2 + logdelta)[::-1], flux_num[i][ns // 2:], "k.", ms=2) ax[i].set_xticks([]) ax[i].set_yticks([]) # Show the map axins = inset_axes(ax[i], width="30%", height="30%", loc=4, borderpad=1) axins.imshow( img, origin="lower", cmap=cmap, extent=(-1, 1, -1, 1), vmin=1e-8, ) circ = plt.Circle( (0, yo[i, ns // 2]), ro, fc="k", ec="k", clip_on=(ro > 0.75), zorder=99, ) axins.add_artist(circ) axins.annotate( labels[i - 1], xy=(0.5, -0.1), xycoords="axes fraction", clip_on=False, ha="center", va="top", fontsize=8, ) axins.set_xlim(-1.01, 1.01) axins.set_ylim(-1.01, 1.01) axins.axis("off") plt.show() # Compare if not np.allclose(flux, flux_num_interp, atol=atol): index = np.unravel_index(np.argmax(np.abs(flux - flux_num_interp)), flux.shape) if index[0] > 0: raise ValueError("Error in singular region {}/8: {}".format( index[0], labels[index[0] - 1]))
foo = subprocess.check_output(['julia', "compare_to_batman_grad.jl"]) agol_grad_time[i] = float(foo.decode('utf-8')) # pytransit m = pytransit.Gimenez(nldc=len(u_g), interpolate=False) tstart = time.time() for k in range(10): pytransit_flux = m(b, 0.1, u_g) pytransit_time[i] = (time.time() - tstart) / 10 # starry (sph) # Using the dense spherical harmonic # integration algorithm (slow, since we don't # actually need the majority of the terms!) if N < 30: map = starry.Map(ydeg=1, udeg=N) map[1:] = u tstart = time.time() for k in range(10): starry_ylm_flux = map.flux(xo=b, ro=0.1) starry_ylm_time[i] = (time.time() - tstart) / 10 tstart = time.time() else: # Let's not even bother computing these! starry_ylm_flux = np.zeros_like(b) * np.nan starry_ylm_time[i] = np.nan # starry (ld) map = starry.Map(ydeg=0, udeg=N) map[1:] = u tstart = time.time()
nlam = 199 nt = 3 P = 1 inc = 90.0 ydeg = 5 vsini = 40.0 sigma = 7.5e-6 dlam = np.log(1.0 + 1.0 / R) lam = np.arange(-(nlam // 2), nlam // 2 + 1) * dlam t = np.linspace(-0.5 * P, 0.5 * P, nt + 1)[:-1] doppler = pp.Doppler(lam, ydeg=ydeg, vsini=vsini, inc=inc, P=P) D = doppler.D(t=t) lam_padded = doppler.lam_padded vT = np.ones_like(lam_padded) vT = 1 - 0.5 * np.exp(-0.5 * lam_padded ** 2 / sigma ** 2) map = starry.Map(ydeg) map.load("vogtstar.jpg") u = np.array(map.y.eval()) A = u.reshape(-1, 1).dot(vT.reshape(1, -1)) for i in tqdm(range(100)): F = D.dot(A.reshape(-1)).reshape(nt, -1) # Conv A_t = tt.dmatrix() F_t = theano.function( [A_t], flux(lam, t, A_t, ydeg=ydeg, vsini=vsini, inc=inc, P=P) ) for i in tqdm(range(100)): foo = F_t(A)
def test_rotate_spectral(): map = starry.Map(1, nw=2) map[1, 1, 0] = 1 map[1, -1, 1] = 1 map.rotate(np.array([0, 0, 1]), np.array(90.0)) assert np.allclose(map.y, [[1, 1], [1, 0], [0, 0], [0, -1]])
def __init__(self, lmax=5): self.map = starry.Map(lmax, oblate=True, gdeg=0)
def test_X(xs, ys, zs, theta=0, ro=0.1, res=300, ydeg=2, tol=1e-3, plot=False): # Params npts = 250 xo = np.linspace(-1.5, 1.5, npts) yo = np.linspace(-0.3, 0.5, npts) theta = 0 ro = 0.1 res = 300 ydeg = 2 tol = 1e-3 # Instantiate map = starry.Map(ydeg=ydeg, reflected=True) # Analytic X = map.amp * map.design_matrix( xs=xs, ys=ys, zs=zs, theta=theta, xo=xo, yo=yo, ro=ro ) # Numerical (lat, lon), (x, y, z) = map.ops.compute_ortho_grid(res) image = np.zeros((map.Ny, res * res)) image[0] = map.render(theta=theta, xs=xs, ys=ys, zs=zs, res=res).flatten() n = 1 for l in range(1, map.ydeg + 1): for m in range(-l, l + 1): map.reset() map[l, m] = 1 image[n] = ( map.render(theta=theta, xs=xs, ys=ys, zs=zs, res=res).flatten() ) - image[0] n += 1 X_num = np.zeros_like(X) for k in range(len(xo)): idx = (x - xo[k]) ** 2 + (y - yo[k]) ** 2 > ro ** 2 for n in range(map.Ny): X_num[k, n] = np.nansum(image[n][idx]) X_num *= 4 / res ** 2 # Plot if plot: fig, ax = plt.subplots( ydeg + 1, 2 * ydeg + 1, figsize=(9, 6), sharex=True, sharey=True ) for axis in ax.flatten(): axis.set_xticks([]) axis.set_yticks([]) axis.spines["top"].set_visible(False) axis.spines["right"].set_visible(False) axis.spines["bottom"].set_visible(False) axis.spines["left"].set_visible(False) n = 0 for i, l in enumerate(range(ydeg + 1)): for j, m in enumerate(range(-l, l + 1)): j += ydeg - l med = np.median(X_num[:, n]) ax[i, j].plot(X[:, n] - med, lw=2) ax[i, j].plot(X_num[:, n] - med, lw=1) n += 1 fig.savefig( "test_X_{}.pdf".format(datetime.now().strftime("%d%m%Y%H%M%S")), bbox_inches="tight", ) plt.close() # Compare diff = (X - X_num).flatten() assert np.max(np.abs(diff)) < tol
import starry import numpy as np map = starry.Map(15, inc=40) map.load("spot") map.show(theta=np.linspace(0, 360, 100), res=300, file="spot.mp4")
def design_matrix(time, ydeg=10, nt=2, period=1.0, phase0=0.0, fit_linear_term=False): """ Compute and return the design matrix. Args: time: The time array in TJD. ydeg: The maximum spherical harmonic degree. nt: The number of map temporal components. phase0: The phase of the map at `t = 0` in degrees. """ # Instantiate a `starry` map map = starry.Map(ydeg=ydeg, udeg=0, reflected=True, nt=nt) # Load the SPICE data ephemFiles = glob.glob('../data/TESS_EPH_PRE_LONG_2018*.bsp') tlsFile = '../data/tess2018338154046-41240_naif0012.tls' solarSysFile = '../data/tess2018338154429-41241_de430.bsp' #print(spice.tkvrsn('TOOLKIT')) for ephFil in ephemFiles: spice.furnsh(ephFil) spice.furnsh(tlsFile) spice.furnsh(solarSysFile) # JD time range allTJD = time + TJD0 nT = len(allTJD) allET = np.zeros((nT, ), dtype=np.float) for i, t in enumerate(allTJD): allET[i] = spice.unitim(t, 'JDTDB', 'ET') # Calculate positions of TESS, the Earth, and the Sun tess = np.zeros((3, len(allET))) sun = np.zeros((3, len(allET))) for i, et in enumerate(allET): outTuple = spice.spkezr('Mgs Simulation', et, 'J2000', 'NONE', 'Earth') tess[0, i] = outTuple[0][0] * REARTH tess[1, i] = outTuple[0][1] * REARTH tess[2, i] = outTuple[0][2] * REARTH outTuple = spice.spkezr('Sun', et, 'J2000', 'NONE', 'Earth') sun[0, i] = outTuple[0][0] * REARTH sun[1, i] = outTuple[0][1] * REARTH sun[2, i] = outTuple[0][2] * REARTH # Compute the linear starry model t = (time - time[0]) / (time[-1] - time[0]) t = 2 * (t - 0.5) X = np.empty((nT, map.Ny * map.nt)) for i in tqdm(range(len(time))): # Find the rotation matrix `R` that rotates TESS onto the +z axis r = np.sqrt(np.sum(tess[:, i]**2)) costheta = np.dot(tess[:, i], [0, 0, r]) axis = np.cross(tess[:, i], [0, 0, r]) sintheta = np.sqrt(np.sum(axis**2)) axis /= sintheta R = starry.RAxisAngle(axis, 180. / np.pi * np.arctan2(sintheta, costheta)) # Rotate into this new frame. The Earth is still # at the origin, TESS is along the +z axis, and # the Sun is at `source`. nx, ny, nz = np.dot(R, [0, 0, 1]) source = np.dot(R, sun[:, i]) source /= np.sqrt(np.sum(source**2, axis=0)) # We need to rotate the map of the Earth so the # north pole is at (nx, ny, nz). We also need to # rotate the Earth about the pole to get it to the # correct phase. We'll do this with a compound # rotation by an angle `theta` about the axis computed # below. phase = 2 * np.pi / period * (time[i]) + np.pi / 180. * phase0 cosphase = np.cos(phase) sinphase = np.sin(phase) map.axis = [ nz + nz * cosphase + nx * sinphase, (1 + ny) * sinphase, -nx - nx * cosphase + nz * sinphase ] costheta = 0.5 * (-1 + ny + cosphase + ny * cosphase) sintheta = np.sqrt(1 - costheta**2) theta = 180 / np.pi * np.arctan2(sintheta, costheta) X[i] = map.linear_flux_model(t=t[i], theta=theta, source=source) if fit_linear_term: return X else: # Let's remove the Y_{0,0} from the design matrix and return # the static constant term so that it can be subtracted from the data. X00 = np.array(X[:, 0]) X = np.delete(X, [n * map.Ny for n in range(map.nt)], axis=1) return X, X00
from multiprocessing import Pool time, vels, verr = np.loadtxt('../data/vst222259.ascii', usecols=[1,2,3], unpack=True) ha = np.loadtxt('../data/222259_mbcvel.ascii', usecols=[-2]) time = time[:-4] vels = vels[:-4] verr = verr[:-4] ha = ha[:-7] time -= 18706.5 map = starry.Map(ydeg=4, udeg=2, rv=True, lazy=False) map.reset() Prot = 2.85 # days P = 8.1387 # days e = 0.0 w = 0.0 inc = 90.0 tuse = time + 0.0 euse = verr + 0.0 vuse = vels + 0.0 bnds = ((12000, 24000), (0.04, 0.09), (-1.0, 0.0), (15,25), (0,1),(0,1), (-30,90), (-500,500), (0.0, 3.0), (0, 40.0), (0.0, 1.0), (0.16, 0.175), (-100000, 0), (-1000, 0.539), (0.0, 10.0))
def test_rotate_spectral(): map = starry.Map(1, nw=2) map[1, 1, 0] = 1 map[1, -1, 1] = 1 map.rotate([0, 0, 1], 90.0) assert np.allclose(map.y.eval(), [[1, 1], [1, 0], [0, 0], [0, -1]])
def test_bodies(): pri = starry.Primary(starry.Map()) sec = starry.Secondary(starry.Map(ydeg=1), porb=1.0) sys = starry.System(pri, sec) assert sys.primary == pri assert sys.secondaries[0] == sec