示例#1
0
    def in_transit(self, t, r=None, texp=None, light_delay=False):
        """Get a list of timestamps that are in transit

        Args:
            t (vector): A vector of timestamps to be evaluated.
            r (Optional): The radii of the planets.
            texp (Optional[float]): The exposure time.

        Returns:
            The indices of the timestamps that are in transit.

        """
        if light_delay:
            raise NotImplementedError(
                "Light travel time delay is not implemented for simple orbits")
        dt = tt.mod(tt.shape_padright(t) - self._ref_time, self.period)
        dt -= self._half_period
        if r is None:
            tol = 0.5 * self.duration
        else:
            x = (r + self.r_star)**2 - self._b_norm**2
            tol = tt.sqrt(x) / self.speed
        if texp is not None:
            tol += 0.5 * texp
        mask = tt.any(tt.abs_(dt) < tol, axis=-1)
        return tt.arange(t.size)[mask]
示例#2
0
    def get_value(self, tau0):
        dt = self.delta
        ar, cr, a, b, c, d = self.term.coefficients

        # Format the lags correctly
        tau0 = tt.abs_(tau0)
        tau = tau0[..., None]

        # Precompute some factors
        dpt = dt + tau
        dmt = dt - tau

        # Real parts:
        # tau > Delta
        crd = cr * dt
        cosh = tt.cosh(crd)
        norm = 2 * ar / crd ** 2
        K_large = tt.sum(norm * (cosh - 1) * tt.exp(-cr * tau), axis=-1)

        # tau < Delta
        crdmt = cr * dmt
        K_small = K_large + tt.sum(norm * (crdmt - tt.sinh(crdmt)), axis=-1)

        # Complex part
        cd = c * dt
        dd = d * dt
        c2 = c ** 2
        d2 = d ** 2
        c2pd2 = c2 + d2
        C1 = a * (c2 - d2) + 2 * b * c * d
        C2 = b * (c2 - d2) - 2 * a * c * d
        norm = 1.0 / (dt * c2pd2) ** 2
        k0 = tt.exp(-c * tau)
        cdt = tt.cos(d * tau)
        sdt = tt.sin(d * tau)

        # For tau > Delta
        cos_term = 2 * (tt.cosh(cd) * tt.cos(dd) - 1)
        sin_term = 2 * (tt.sinh(cd) * tt.sin(dd))
        factor = k0 * norm
        K_large += tt.sum(
            (C1 * cos_term - C2 * sin_term) * factor * cdt, axis=-1
        )
        K_large += tt.sum(
            (C2 * cos_term + C1 * sin_term) * factor * sdt, axis=-1
        )

        # tau < Delta
        edmt = tt.exp(-c * dmt)
        edpt = tt.exp(-c * dpt)
        cos_term = (
            edmt * tt.cos(d * dmt) + edpt * tt.cos(d * dpt) - 2 * k0 * cdt
        )
        sin_term = (
            edmt * tt.sin(d * dmt) + edpt * tt.sin(d * dpt) - 2 * k0 * sdt
        )
        K_small += tt.sum(2 * (a * c + b * d) * c2pd2 * dmt * norm, axis=-1)
        K_small += tt.sum((C1 * cos_term + C2 * sin_term) * norm, axis=-1)

        return tt.switch(tt.le(tau0, dt), K_small, K_large)
示例#3
0
 def get_value(self, tau):
     ar, cr, ac, bc, cc, dc = self.coefficients
     tau = tt.abs_(tau)
     tau = tt.shape_padright(tau)
     K = tt.sum(ar * tt.exp(-cr * tau), axis=-1)
     factor = tt.exp(-cc * tau)
     K += tt.sum(ac * factor * tt.cos(dc * tau), axis=-1)
     K += tt.sum(bc * factor * tt.sin(dc * tau), axis=-1)
     return K
示例#4
0
    def _get_retarded_position(self, a, t, parallax=None, z0=0.0, _pad=True):
        """Get the retarded position of a body, accounting for light delay.

        Args:
            a: the semi-major axis of the orbit.
            t: the time (or tensor of times) to calculate the position.
            parallax: (arcseconds) if provided, return the position in
                units of arcseconds.
            z0: the reference point along the z axis whose light travel time
                delay is taken to be zero. Default is the origin.

        Returns:
            The position of the body in the observer frame. Default is in units
            of R_sun, but if parallax is provided, then in units of arcseconds.

        """
        sinf, cosf = self._get_true_anomaly(t, _pad=_pad)

        # Compute the orbital radius and the component of the velocity
        # in the z direction
        angvel = 2 * np.pi / self.period
        if self.ecc is None:
            r = a
            vamp = angvel * a
            vz = vamp * self.sin_incl * cosf
        else:
            r = a * (1.0 - self.ecc**2) / (1 + self.ecc * cosf)
            vamp = angvel * a / tt.sqrt(1 - self.ecc**2)
            cwf = self.cos_omega * cosf - self.sin_omega * sinf
            vz = vamp * self.sin_incl * (self.ecc * self.cos_omega + cwf)

        # True position of the body
        x, y, z = self._rotate_vector(r * cosf, r * sinf)

        # Component of the acceleration in the z direction
        az = -(angvel**2) * (a / r)**3 * z

        # Compute the time delay at the **retarded** position, accounting
        # for the instantaneous velocity and acceleration of the body.
        # See the derivation at https://github.com/rodluger/starry/issues/66
        delay = tt.switch(
            tt.lt(tt.abs_(az), 1.0e-10),
            (z0 - z) / (c_light + vz),
            (c_light / az) *
            ((1 + vz / c_light) - tt.sqrt((1 + vz / c_light) *
                                          (1 + vz / c_light) - 2 * az *
                                          (z0 - z) / c_light**2)),
        )

        if _pad:
            new_t = tt.shape_padright(t) - delay
        else:
            new_t = t - delay

        # Re-compute Kepler's equation, this time at the **retarded** position
        return self._get_position(a, new_t, parallax, _pad=False)
示例#5
0
    def get_relative_position(self, t, light_delay=False):
        """The planets' positions relative to the star

        Args:
            t: The times where the position should be evaluated.

        Returns:
            The components of the position vector at ``t`` in units of
            ``R_sun``.

        """
        if light_delay:
            raise NotImplementedError(
                "Light travel time delay is not implemented for simple orbits")
        dt = tt.mod(tt.shape_padright(t) - self._ref_time, self.period)
        dt -= self._half_period
        x = tt.squeeze(self.speed * dt)
        y = tt.squeeze(self._b_norm + tt.zeros_like(dt))
        m = tt.abs_(dt) < 0.5 * self.duration
        z = tt.squeeze(m * 1.0 - (~m) * 1.0)
        return x, y, z
示例#6
0
def duration_to_eccentricity(func, duration, ror,
                             **kwargs):  # pragma: no cover
    num_planets = kwargs.pop("num_planets", 1)
    orbit_type = kwargs.pop("orbit_type", KeplerianOrbit)
    name = kwargs.get("name", "dur_ecc")

    inputs = _get_consistent_inputs(
        kwargs.get("a", None),
        kwargs.get("period", None),
        kwargs.get("rho_star", None),
        kwargs.get("r_star", None),
        kwargs.get("m_star", None),
        kwargs.get("rho_star_units", None),
        kwargs.get("m_planet", 0.0),
        kwargs.get("m_planet_units", None),
    )
    a, period, rho_star, r_star, m_star, m_planet = inputs
    b = kwargs.get("b", 0.0)
    s = tt.sin(kwargs["omega"])
    umax_inv = tt.switch(tt.lt(s, 0), tt.sqrt(1 - s**2), 1.0)

    const = (period * tt.shape_padright(r_star) * tt.sqrt((1 + ror)**2 - b**2))
    const /= np.pi * a

    u = duration / const

    e1 = -s * u**2 / ((s * u)**2 + 1)
    e2 = tt.sqrt((s**2 - 1) * u**2 + 1) / ((s * u)**2 + 1)

    models = []
    logjacs = []
    logprobs = []
    for args in product(*(zip("np", (-1, 1)) for _ in range(num_planets))):
        labels, signs = zip(*args)

        # Compute the eccentricity branch
        ecc = tt.stack([e1[i] + signs[i] * e2[i] for i in range(num_planets)])

        # Work out the Jacobian
        valid_ecc = tt.and_(tt.lt(ecc, 1.0), tt.ge(ecc, 0.0))
        logjac = tt.switch(
            tt.all(valid_ecc),
            tt.sum(0.5 * tt.log(1 - ecc**2) + 2 * tt.log(s * ecc + 1) -
                   tt.log(tt.abs_(s + ecc)) - tt.log(const)),
            -np.inf,
        )
        ecc = tt.switch(valid_ecc, ecc, tt.zeros_like(ecc))

        # Create a sub-model to capture this component
        with pm.Model(name="dur_ecc_" + "_".join(labels)) as model:
            pm.Deterministic("ecc", ecc)
            orbit = orbit_type(ecc=ecc, **kwargs)
            logprob = tt.sum(func(orbit))

        models.append(model)
        logjacs.append(logjac)
        logprobs.append(logprob)

    # Compute the marginalized likelihood
    logjacs = tt.stack(logjacs)
    logprobs = tt.stack(logprobs)
    logprob = tt.switch(
        tt.gt(1.0 / u, umax_inv),
        tt.sum(pm.logsumexp(logprobs + logjacs)),
        -np.inf,
    )
    pm.Potential(name + "_logp", logprob)
    pm.Deterministic(name + "_logjacs", logjacs)
    pm.Deterministic(name + "_logprobs", logprobs)

    # Loop over models and compute the marginalized values for all the
    # parameters and deterministics
    norm = tt.sum(pm.logsumexp(logjacs))
    logw = tt.switch(
        tt.gt(1.0 / u, umax_inv),
        logjacs - norm,
        -np.inf + tt.zeros_like(logjacs),
    )
    pm.Deterministic(name + "_logw", logw)
    for k in models[0].named_vars.keys():
        name = k[len(models[0].name) + 1:]
        pm.Deterministic(
            name,
            sum(
                tt.exp(logw[i]) * model.named_vars[model.name + "_" + name]
                for i, model in enumerate(models)),
        )