Example #1
0
def calculate_true_anomaly(epoch, sma, ecc, tau, mtot):
    """ Calculate true anomaly

	Args:
		epoch (float): epoch at which to calculate contrast [mjd]
		sma (float): semimajor axis [au]
		ecc (float): eccentricity
		tau (float): epoch of periastron, in mjd, divided by period
		mtot (float): total mass [solar masses]

	Returns: true anomaly [float]

	History:
		2018: shamelessly stolen from orbitize.kepler.calc_orbit() by Sarah Blunt
	"""

    # compute period (from Kepler's third law) and mean motion
    period = np.sqrt((sma**3) / mtot) * 365.25
    mean_motion = 2 * np.pi / (period)  # in rad/day

    # compute mean anomaly
    manom = (mean_motion * epoch - 2 * np.pi * tau) % (2.0 * np.pi)

    # compute eccentric anomalies
    eanom = _calc_ecc_anom(manom, ecc)

    # compute the true anomalies
    tanom = 2. * np.arctan(
        np.sqrt((1.0 + ecc) / (1.0 - ecc)) * np.tan(0.5 * eanom))

    return tanom
def profile_mikkola_ecc_anom_solver(n_orbits=1000, use_c=True):
    """
    Test orbitize.kepler._calc_ecc_anom() in the iterative solver regime (e < 0.95) by comparing the mean anomaly computed from
    _calc_ecc_anom() output vs the input mean anomaly
    """
    mean_anoms = np.linspace(0, 2.0 * np.pi, n_orbits)
    eccs = np.linspace(.95, 0.999999, n_orbits)
    for ee in eccs:
        ecc_anoms = kepler._calc_ecc_anom(mean_anoms, ee, use_c=use_c)
Example #3
0
def test_iterative_ecc_anom_solver():
    """
    Test orbitize.kepler._calc_ecc_anom() in the iterative solver regime (e < 0.95) by comparing the mean anomaly computed from
    _calc_ecc_anom() output vs the input mean anomaly
    """
    mean_anoms = np.linspace(0, 2.0 * np.pi, 100)
    eccs = np.linspace(0, 0.9499999, 100)
    for ee in eccs:
        ecc_anoms = kepler._calc_ecc_anom(mean_anoms, ee, tolerance=1e-9)
        calc_ma = (ecc_anoms - ee * np.sin(ecc_anoms)) % (
            2 * np.pi)  # plug solutions into Kepler's equation
        for meas, truth in zip(calc_ma, mean_anoms):
            assert angle_diff(meas, truth) == pytest.approx(0.0, abs=threshold)
Example #4
0
def test_analytical_ecc_anom_solver(use_c=False, use_gpu=False):
    """
    Test orbitize.kepler._calc_ecc_anom() in the analytical solver regime (e > 0.95) by comparing the mean anomaly computed from
    _calc_ecc_anom() output vs the input mean anomaly
    """
    mean_anoms = np.linspace(0, 2.0 * np.pi, 1000)
    eccs = np.linspace(0.95, 0.999999, 100)
    for ee in eccs:
        ecc_anoms = kepler._calc_ecc_anom(mean_anoms,
                                          ee,
                                          tolerance=1e-9,
                                          use_c=use_c,
                                          use_gpu=use_gpu)
        calc_mm = (ecc_anoms - ee * np.sin(ecc_anoms)) % (
            2 * np.pi)  # plug solutions into Kepler's equation
        for meas, truth in zip(calc_mm, mean_anoms):
            assert angle_diff(meas, truth) == pytest.approx(0.0, abs=threshold)
Example #5
0
def calc_orbit_3d(epochs,
                  sma,
                  ecc,
                  inc,
                  argp,
                  lan,
                  tau,
                  plx,
                  mtot,
                  mass=None,
                  tau_ref_epoch=0,
                  tolerance=1e-9,
                  max_iter=100):
    """
    Returns the separation and radial velocity of the body given array of
    orbital parameters (size n_orbs) at given epochs (array of size n_dates)

    Based on orbit solvers from James Graham and Rob De Rosa. Adapted by Jason Wang and Henry Ngo.

    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]
        argp (np.array): argument of periastron [radians]
        lan (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 [Solar masses]
        mass (np.array, optional): mass of the body [Solar masses]. For planets mass ~ 0 (default)
        tau_ref_epoch (float, optional): reference date that tau is defined with respect to (i.e., tau=0)
        tolerance (float, optional): absolute tolerance of iterative computation. Defaults to 1e-9.
        max_iter (int, optional): maximum number of iterations before switching. Defaults to 100.

    Return:
        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 offset between the bodies  [km/s]

    Written: Jason Wang, Henry Ngo, 2018
    """

    n_orbs = np.size(sma)  # num sets of input orbital parameters
    n_dates = np.size(epochs)  # number of dates to compute offsets and vz

    # Necessary for _calc_ecc_anom, for now
    if np.isscalar(epochs):  # just in case epochs is given as a scalar
        epochs = np.array([epochs])
    ecc_arr = np.tile(ecc, (n_dates, 1))

    # If mass not given, assume test particle case
    if mass is None:
        mass = np.zeros(n_orbs)

    # Compute period (from Kepler's third law) and mean motion
    period = np.sqrt(4 * np.pi**2.0 * (sma * u.AU)**3 / (consts.G *
                                                         (mtot * u.Msun)))
    period = period.to(u.day).value
    mean_motion = 2 * np.pi / (period)  # in rad/day

    # # compute mean anomaly (size: n_orbs x n_dates)
    manom = (mean_motion * (epochs[:, None] - tau_ref_epoch) -
             2 * np.pi * tau) % (2.0 * np.pi)

    # compute eccentric anomalies (size: n_orbs x n_dates)
    eanom = _calc_ecc_anom(manom,
                           ecc_arr,
                           tolerance=tolerance,
                           max_iter=max_iter)

    # compute the true anomalies (size: n_orbs x n_dates)
    # Note: matrix multiplication makes the shapes work out here and below
    tanom = 2. * np.arctan(
        np.sqrt((1.0 + ecc) / (1.0 - ecc)) * np.tan(0.5 * eanom))
    # compute 3-D orbital radius of second body (size: n_orbs x n_dates)
    radius = sma * (1.0 - ecc * np.cos(eanom))

    # compute ra/dec offsets (size: n_orbs x n_dates)
    # math from James Graham. Lots of trig
    c2i2 = np.cos(0.5 * inc)**2
    s2i2 = np.sin(0.5 * inc)**2
    arg1 = tanom + argp + lan
    arg2 = tanom + argp - lan
    c1 = np.cos(arg1)
    c2 = np.cos(arg2)
    s1 = np.sin(arg1)
    s2 = np.sin(arg2)

    # updated sign convention for Green Eq. 19.4-19.7
    raoff = radius * (c2i2 * s1 - s2i2 * s2) * plx
    deoff = radius * (c2i2 * c1 + s2i2 * c2) * plx
    # zoff = np.ones(raoff.shape)
    zoff = radius * np.sin(tanom + argp) * np.sin(inc) * plx

    # compute the radial velocity (vz) of the body (size: n_orbs x n_dates)
    # first comptue the RV semi-amplitude (size: n_orbs x n_dates)
    m1 = mtot - mass  # mass of the primary star
    Kv = np.sqrt(consts.G /
                 (1.0 - ecc**2)) * (m1 * u.Msun * np.sin(inc)) / np.sqrt(
                     mtot * u.Msun) / np.sqrt(sma * u.au)
    # Convert to km/s
    Kv = Kv.to(u.km / u.s)
    # compute the vz
    vz = Kv.value * (ecc * np.cos(argp) + np.cos(argp + tanom))

    # Squeeze out extra dimension (useful if n_orbs = 1, does nothing if n_orbs > 1)
    # [()] used to convert 1-element arrays into scalars, has no effect for larger arrays
    # raoff = np.transpose(np.squeeze(raoff)[()])
    # deoff = np.transpose(np.squeeze(deoff)[()])
    # vz = np.transpose(np.squeeze(vz)[()])
    vz = np.squeeze(vz)[()]

    return raoff, deoff, zoff, vz