def test_2planet_nomass(): """ Compare multiplanet to rebound for planets with mass. """ # generate a planet orbit mjup = u.Mjup.to(u.Msun) mass_b = 0 * mjup mass_c = 0 * mjup params = np.array([ 10, 0.1, np.radians(45), np.radians(45), np.radians(45), 0.5, 3, 0.1, np.radians(45), np.radians(190), np.radians(45), 0.2, 1, mass_b, mass_c, 1.5 - mass_b - mass_c ]) tau_ref_epoch = 0 epochs = np.linspace(0, 365.25 * 4, 100) + tau_ref_epoch # nearly the full period, MJD # doesn't matter that this is right, just needs to be the same size. below doesn't include effect of c # just want to generate some measurements of plaent b to test compute model b_ra_model, b_dec_model, b_vz_model = kepler.calc_orbit( epochs, params[0], params[1], params[2], params[3], params[4], params[5], params[-2], params[-1], tau_ref_epoch=tau_ref_epoch) # generate some fake measurements of planet b, just to feed into system.py to test bookkeeping t = table.Table([ epochs, np.ones(epochs.shape, dtype=int), b_ra_model, np.zeros(b_ra_model.shape), b_dec_model, np.zeros(b_dec_model.shape) ], names=[ "epoch", "object", "raoff", "raoff_err", "decoff", "decoff_err" ]) filename = os.path.join(orbitize.DATADIR, "rebound_2planet.csv") t.write(filename) # create the orbitize system and generate model predictions using the ground truth astrom_dat = read_input.read_file(filename) sys = system.System(2, astrom_dat, params[-1], params[-2], tau_ref_epoch=tau_ref_epoch, fit_secondary_mass=True) # generate measurement radec_orbitize, _ = sys.compute_model(params) b_ra_orb = radec_orbitize[:, 0] b_dec_orb = radec_orbitize[:, 1] # now project the orbit with rebound b_manom = basis.tau_to_manom(epochs[0], params[0], params[-1] + params[-3], params[5], tau_ref_epoch) c_manom = basis.tau_to_manom(epochs[0], params[0 + 6], params[-1] + params[-2], params[5 + 6], tau_ref_epoch) sim = rebound.Simulation() sim.units = ('yr', 'AU', 'Msun') # add star sim.add(m=params[-1]) # add two planets sim.add(m=mass_c, a=params[0 + 6], e=params[1 + 6], M=c_manom, omega=params[3 + 6], Omega=params[4 + 6] + np.pi / 2, inc=params[2 + 6]) sim.add(m=mass_b, a=params[0], e=params[1], M=b_manom, omega=params[3], Omega=params[4] + np.pi / 2, inc=params[2]) ps = sim.particles sim.move_to_com() # Use Wisdom Holman integrator (fast), with the timestep being < 5% of inner planet's orbital period sim.integrator = "ias15" sim.dt = ps[1].P / 1000. # integrate and measure star/planet separation b_ra_reb = [] b_dec_reb = [] for t in epochs: sim.integrate(t / 365.25) b_ra_reb.append(-(ps[2].x - ps[0].x)) # ra is negative x b_dec_reb.append(ps[2].y - ps[0].y) b_ra_reb = np.array(b_ra_reb) b_dec_reb = np.array(b_dec_reb) diff_ra = b_ra_reb - b_ra_orb / params[6 * 2] diff_dec = b_dec_reb - b_dec_orb / params[6 * 2] # should be as good as the one planet case assert np.all(np.abs(diff_ra) / (params[0] * params[6 * 2]) < 1e-9) assert np.all(np.abs(diff_dec) / (params[0] * params[6 * 2]) < 1e-9)
def test_2planet_massive(): """ Compare multiplanet to rebound for planets with mass. """ # generate a planet orbit mjup = u.Mjup.to(u.Msun) mass_b = 12 * mjup mass_c = 9 * mjup params = np.array([ 10, 0.1, np.radians(45), np.radians(45), np.radians(45), 0.5, 3, 0.1, np.radians(45), np.radians(190), np.radians(45), 0.2, 50, mass_b, mass_c, 1.5 - mass_b - mass_c ]) params_noc = np.array([ 10, 0.1, np.radians(45), np.radians(45), np.radians(45), 0.5, 3, 0.1, np.radians(45), np.radians(190), np.radians(45), 0.2, 50, mass_b, 0, 1.5 - mass_b ]) tau_ref_epoch = 0 epochs = np.linspace(0, 365.25 * 10, 100) + tau_ref_epoch # nearly the full period, MJD # doesn't matter that this is right, just needs to be the same size. below doesn't include effect of c # just want to generate some measurements of plaent b to test compute model b_ra_model, b_dec_model, b_vz_model = kepler.calc_orbit( epochs, params[0], params[1], params[2], params[3], params[4], params[5], params[-2], params[-1], tau_ref_epoch=tau_ref_epoch) # generate some fake measurements of planet b, just to feed into system.py to test bookkeeping t = table.Table([ epochs, np.ones(epochs.shape, dtype=int), b_ra_model, np.zeros(b_ra_model.shape), b_dec_model, np.zeros(b_dec_model.shape) ], names=[ "epoch", "object", "raoff", "raoff_err", "decoff", "decoff_err" ]) filename = os.path.join(orbitize.DATADIR, "rebound_2planet_outer.csv") t.write(filename) #### TEST THE OUTER PLANET #### # create the orbitize system and generate model predictions using the ground truth astrom_dat = read_input.read_file(filename) sys = system.System(2, astrom_dat, params[-1], params[-4], tau_ref_epoch=tau_ref_epoch, fit_secondary_mass=True) # generate measurement radec_orbitize, _ = sys.compute_model(params) b_ra_orb = radec_orbitize[:, 0] b_dec_orb = radec_orbitize[:, 1] # debug, generate measurement without c having any mass radec_orb_noc, _ = sys.compute_model(params_noc) b_ra_orb_noc = radec_orb_noc[:, 0] b_dec_orb_noc = radec_orb_noc[:, 1] # check that planet c's perturbation is imprinted (nonzero)) #assert np.all(b_ra_orb_noc != b_ra_orb) # now project the orbit with rebound b_manom = basis.tau_to_manom(epochs[0], params[0], params[-1] + params[-3], params[5], tau_ref_epoch) c_manom = basis.tau_to_manom(epochs[0], params[0 + 6], params[-1] + params[-2], params[5 + 6], tau_ref_epoch) sim = rebound.Simulation() sim.units = ('yr', 'AU', 'Msun') # add star sim.add(m=params[-1]) # add two planets sim.add(m=mass_c, a=params[0 + 6], e=params[1 + 6], M=c_manom, omega=params[3 + 6], Omega=params[4 + 6] + np.pi / 2, inc=params[2 + 6]) sim.add(m=mass_b, a=params[0], e=params[1], M=b_manom, omega=params[3], Omega=params[4] + np.pi / 2, inc=params[2]) ps = sim.particles sim.move_to_com() # Use Wisdom Holman integrator (fast), with the timestep being < 5% of inner planet's orbital period sim.integrator = "ias15" sim.dt = ps[1].P / 1000. # integrate and measure star/planet separation b_ra_reb = [] b_dec_reb = [] for t in epochs: sim.integrate(t / 365.25) b_ra_reb.append(-(ps[2].x - ps[0].x)) # ra is negative x b_dec_reb.append(ps[2].y - ps[0].y) b_ra_reb = np.array(b_ra_reb) b_dec_reb = np.array(b_dec_reb) diff_ra = b_ra_reb - b_ra_orb / params[6 * 2] diff_dec = b_dec_reb - b_dec_orb / params[6 * 2] # we placed the planets far apart to minimize secular interactions but there are still some, so relax precision assert np.all(np.abs(diff_ra) / (params[0]) < 1e-3) assert np.all(np.abs(diff_dec) / (params[0]) < 1e-3) ###### NOW TEST THE INNER PLANET ####### # generate some fake measurements of planet c, just to feed into system.py to test bookkeeping t = table.Table([ epochs, np.ones(epochs.shape, dtype=int) * 2, b_ra_model, np.zeros(b_ra_model.shape), b_dec_model, np.zeros(b_dec_model.shape) ], names=[ "epoch", "object", "raoff", "raoff_err", "decoff", "decoff_err" ]) filename = os.path.join(orbitize.DATADIR, "rebound_2planet_inner.csv") t.write(filename) # create the orbitize system and generate model predictions using the ground truth astrom_dat = read_input.read_file(filename) sys = system.System(2, astrom_dat, params[-1], params[-2], tau_ref_epoch=tau_ref_epoch, fit_secondary_mass=True) # generate measurement radec_orbitize, _ = sys.compute_model(params) c_ra_orb = radec_orbitize[:, 0] c_dec_orb = radec_orbitize[:, 1] # start the REBOUND sim again sim = rebound.Simulation() sim.units = ('yr', 'AU', 'Msun') # add star sim.add(m=params[-1]) # add two planets sim.add(m=mass_c, a=params[0 + 6], e=params[1 + 6], M=c_manom, omega=params[3 + 6], Omega=params[4 + 6] + np.pi / 2, inc=params[2 + 6]) sim.add(m=mass_b, a=params[0], e=params[1], M=b_manom, omega=params[3], Omega=params[4] + np.pi / 2, inc=params[2]) ps = sim.particles sim.move_to_com() # Use Wisdom Holman integrator (fast), with the timestep being < 5% of inner planet's orbital period sim.integrator = "ias15" sim.dt = ps[1].P / 1000. # integrate and measure star/planet separation c_ra_reb = [] c_dec_reb = [] for t in epochs: sim.integrate(t / 365.25) c_ra_reb.append(-(ps[1].x - ps[0].x)) # ra is negative x c_dec_reb.append(ps[1].y - ps[0].y) c_ra_reb = np.array(c_ra_reb) c_dec_reb = np.array(c_dec_reb) diff_ra = c_ra_reb - c_ra_orb / params[6 * 2] diff_dec = c_dec_reb - c_dec_orb / params[6 * 2] # planet is 3 times closer, so roughly 3 times larger secular errors. assert np.all(np.abs(diff_ra) / (params[0]) < 3e-3) assert np.all(np.abs(diff_dec) / (params[0]) < 3e-3)
def test_1planet(): """ Sanity check that things agree for 1 planet case """ # generate a planet orbit sma = 1 ecc = 0.1 inc = np.radians(45) aop = np.radians(45) pan = np.radians(45) tau = 0.5 plx = 1 mtot = 1 tau_ref_epoch = 0 mjup = u.Mjup.to(u.Msun) mass_b = 12 * mjup epochs = np.linspace(0, 300, 100) + tau_ref_epoch # nearly the full period, MJD ra_model, dec_model, vz_model = kepler.calc_orbit( epochs, sma, ecc, inc, aop, pan, tau, plx, mtot, tau_ref_epoch=tau_ref_epoch) # generate some fake measurements just to feed into system.py to test bookkeeping t = table.Table([ epochs, np.ones(epochs.shape, dtype=int), ra_model, np.zeros(ra_model.shape), dec_model, np.zeros(dec_model.shape) ], names=[ "epoch", "object", "raoff", "raoff_err", "decoff", "decoff_err" ]) filename = os.path.join(orbitize.DATADIR, "rebound_1planet.csv") t.write(filename) # create the orbitize system and generate model predictions using the ground truth astrom_dat = read_input.read_file(filename) sys = system.System(1, astrom_dat, mtot, plx, tau_ref_epoch=tau_ref_epoch) params = np.array([sma, ecc, inc, aop, pan, tau, plx, mtot]) radec_orbitize, _ = sys.compute_model(params) ra_orb = radec_orbitize[:, 0] dec_orb = radec_orbitize[:, 1] # now project the orbit with rebound manom = basis.tau_to_manom(epochs[0], sma, mtot, tau, tau_ref_epoch) sim = rebound.Simulation() sim.units = ('yr', 'AU', 'Msun') # add star sim.add(m=mtot - mass_b) # add one planet sim.add(m=mass_b, a=sma, e=ecc, M=manom, omega=aop, Omega=pan + np.pi / 2, inc=inc) ps = sim.particles sim.move_to_com() # Use Wisdom Holman integrator (fast), with the timestep being < 5% of inner planet's orbital period sim.integrator = "ias15" sim.dt = ps[1].P / 1000. # integrate and measure star/planet separation ra_reb = [] dec_reb = [] for t in epochs: sim.integrate(t / 365.25) ra_reb.append(-(ps[1].x - ps[0].x)) # ra is negative x dec_reb.append(ps[1].y - ps[0].y) ra_reb = np.array(ra_reb) dec_reb = np.array(dec_reb) diff_ra = ra_reb - ra_orb / plx diff_dec = dec_reb - dec_orb / plx assert np.all(np.abs(diff_ra) < 1e-9) assert np.all(np.abs(diff_dec) < 1e-9)
def calc_orbit( epochs, sma, ecc, inc, aop, pan, tau, plx, mtot, tau_ref_epoch=58849, m_pl=None, output_star=False ): """ Solves for position for a set of input orbital elements using rebound. Args: epochs (np.array): MJD times for which we want the positions of the planet sma (np.array): semi-major axis of orbit [au] ecc (np.array): eccentricity of the orbit [0,1] inc (np.array): inclination [radians] aop (np.array): argument of periastron [radians] pan (np.array): longitude of the ascending node [radians] tau (np.array): epoch of periastron passage in fraction of orbital period past MJD=0 [0,1] plx (np.array): parallax [mas] mtot (np.array): total mass of the two-body orbit (M_* + M_planet) [Solar masses] tau_ref_epoch (float, optional): reference date that tau is defined with respect to m_pl (np.array, optional): mass of the planets[units] output_star (bool): if True, also return the position of the star relative to the barycenter. Returns: 3-tuple: raoff (np.array): array-like (n_dates x n_orbs) of RA offsets between the bodies (origin is at the other body) [mas] deoff (np.array): array-like (n_dates x n_orbs) of Dec offsets between the bodies [mas] vz (np.array): array-like (n_dates x n_orbs) of radial velocity of one of the bodies (see `mass_for_Kamp` description) [km/s] .. Note:: return is in format [raoff[planet1, planet2,...,planetn], deoff[planet1, planet2,...,planetn], vz[planet1, planet2,...,planetn] """ sim = rebound.Simulation() #creating the simulation in Rebound sim.units = ('AU','yr','Msun') #converting units for uniformity sim.G = 39.476926408897626 #Using a more accurate value in order to minimize differences from prev. Kepler solver ps = sim.particles #for easier calls tx = len(epochs) #keeping track of how many time steps te = epochs-epochs[0] #days indv = len(sma) #number of planets orbiting the star num_planets = np.arange(0,indv) #creates an array of indeces for each planet that exists if m_pl is None: #if no planet masses are input, planet masses set ot zero and mass of star is equal to mtot sim.add(m = mtot) m_pl = np.zeros(len(sma)) m_star = mtot else: #mass of star is always (mass of system)-(sum of planet masses) m_star = mtot - sum(m_pl) sim.add(m = m_star) #for each planet, create a body in the Rebound sim for i in num_planets: #calculating mean anomaly m_interior = m_star + sum(m_pl[0:i+1]) mnm = basis.tau_to_manom(epochs[0], sma[i], m_interior, tau[i], tau_ref_epoch) #adding each planet sim.add(m = m_pl[i], a = sma[i], e = ecc[i], inc = inc[i], Omega = pan[i] + np.pi/2, omega =aop[i], M =mnm) sim.move_to_com() sim.integrator = "ias15" sim.dt = ps[1].P/100. #good rule of thumb: timestep should be at most 10% of the shortest orbital period if output_star: ra_reb = np.zeros((tx, indv+1)) #numpy.zeros(number of [arrays], size of each array) dec_reb = np.zeros((tx, indv+1)) vz = np.zeros((tx, indv+1)) for j,t in enumerate(te): sim.integrate(t/365.25) #for the star and each planet in each epoch denoted by j,t find the RA, Dec, and RV com = sim.calculate_com() ra_reb[j,0] = -(ps[0].x-com.x)# ra is negative x dec_reb[j,0] = ps[0].y-com.y vz[j,0] = ps[0].vz for i in num_planets: ra_reb[j,i+1] = -(ps[int(i+1)].x - ps[0].x) # ra is negative x dec_reb[j,i+1] = ps[int(i+1)].y - ps[0].y vz[j,i+1] = ps[int(i+1)].vz else: ra_reb = np.zeros((tx, indv)) #numpy.zeros(number of [arrays], size of each array) dec_reb = np.zeros((tx, indv)) vz = np.zeros((tx, indv)) #integrate at each epoch for j,t in enumerate(te): sim.integrate(t/365.25) #for each planet in each epoch denoted by j,t find the RA, Dec, and RV for i in num_planets: ra_reb[j,i] = -(ps[int(i+1)].x - ps[0].x) # ra is negative x dec_reb[j,i] = ps[int(i+1)].y - ps[0].y vz[j,i] = ps[int(i+1)].vz #adjusting for parallax raoff = plx*ra_reb deoff = plx*dec_reb #for formatting purposes if len(sma)==1: raoff = raoff.reshape(tx,) deoff = deoff.reshape(tx,) vz = vz.reshape(tx,) return raoff, deoff, vz else: return raoff, deoff, vz