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 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)