Exemple #1
0
def test_get_aor_from_transit_duration():
    duration = 0.12
    period = 10.1235
    b = 0.34
    ror = 0.06
    r_star = 0.7

    dv = tt.as_tensor_variable(duration)
    aor, jac = get_aor_from_transit_duration(dv, period, b, ror)

    assert np.allclose(theano.grad(aor, dv).eval(), jac.eval())

    for orbit in [
            KeplerianOrbit(period=period,
                           t0=0.0,
                           b=b,
                           a=r_star * aor,
                           r_star=r_star),
            KeplerianOrbit(
                period=period,
                t0=0.0,
                b=b,
                duration=duration,
                r_star=r_star,
                ror=ror,
            ),
    ]:
        x, y, z = orbit.get_planet_position(0.5 * duration)
        assert np.allclose(tt.sqrt(x**2 + y**2).eval(), r_star * (1 + ror))

        x, y, z = orbit.get_planet_position(-0.5 * duration)
        assert np.allclose(tt.sqrt(x**2 + y**2).eval(), r_star * (1 + ror))

        x, y, z = orbit.get_planet_position(period + 0.5 * duration)
        assert np.allclose(tt.sqrt(x**2 + y**2).eval(), r_star * (1 + ror))
Exemple #2
0
    def get_ror_from_approx_transit_depth(self, delta, b, jac=False):
        """Get the radius ratio corresponding to a particular transit depth

        This result will be approximate and it requires ``|b| < 1`` because it
        relies on the small planet approximation.

        Args:
            delta (tensor): The approximate transit depth in relative units
            b (tensor): The impact parameter
            jac (bool): If true, the Jacobian ``d ror / d delta`` is also
                returned

        Returns:
            ror: The radius ratio that approximately corresponds to the depth
            ``delta`` at impact parameter ``b``.

        """
        b = as_tensor_variable(b)
        delta = as_tensor_variable(delta)
        f0 = 1 - 2 * self.u1 / 6.0 - 2 * self.u2 / 12.0
        arg = 1 - tt.sqrt(1 - b**2)
        f = 1 - self.u1 * arg - self.u2 * arg**2
        factor = f0 / f
        ror = tt.sqrt(delta * factor)
        if not jac:
            return tt.reshape(ror, b.shape)
        drorddelta = 0.5 * factor / ror
        return tt.reshape(ror, b.shape), tt.reshape(drorddelta, b.shape)
Exemple #3
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)
Exemple #4
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]
Exemple #5
0
    def get_relative_angles(self, t, parallax=None, light_delay=False):
        """The planets' relative position to the star in the sky plane, in
        separation, position angle coordinates.

        .. note:: This treats each planet independently and does not take the
            other planets into account when computing the position of the
            star. This is fine as long as the planet masses are small.

        Args:
            t: The times where the position should be evaluated.
            light_delay: account for the light travel time delay? Default is
                False.

        Returns:
            The separation (arcseconds) and position angle (radians,
            measured east of north) of the planet relative to the star.

        """
        X, Y, Z = self._get_position(-self.a,
                                     t,
                                     parallax,
                                     light_delay=light_delay)

        # calculate rho and theta
        rho = tt.squeeze(tt.sqrt(X**2 + Y**2))  # arcsec
        theta = tt.squeeze(tt.arctan2(Y, X))  # radians between [-pi, pi]

        return (rho, theta)
Exemple #6
0
def get_aor_from_transit_duration(duration, period, b, ror=None):
    """Get the semimajor axis implied by a circular orbit and duration

    Args:
        duration: The transit duration
        period: The orbital period
        b: The impact parameter of the transit
        ror: The radius ratio of the planet to the star

    Returns:
        The semimajor axis in units of the stellar radius and the Jacobian
        ``d a / d duration``

    """
    if ror is None:
        ror = as_tensor_variable(0.0)
    b2 = b**2
    opk2 = (1 + ror)**2
    phi = np.pi * duration / period
    sinp = tt.sin(phi)
    cosp = tt.cos(phi)
    num = tt.sqrt(opk2 - b2 * cosp**2)
    aor = num / sinp
    grad = np.pi * cosp * (b2 - opk2) / (num * period * sinp**2)
    return aor, grad
Exemple #7
0
    def backward(self, y):
        y = tt.nnet.sigmoid(y)
        r1 = y[0]
        r2 = y[1]
        pl, pu = self.min_radius, self.max_radius

        b1 = (1 + pl) * (1 + (r1 - 1) / (1 - self.Ar))
        p1 = pl + r2 * self.dr

        q1 = r1 / self.Ar
        q2 = r2
        b2 = (1 + pl) + tt.sqrt(q1) * q2 * self.dr
        p2 = pu - self.dr * tt.sqrt(q1) * (1 - q2)

        pb = tt.switch(
            r1 > self.Ar,
            tt.stack((p1, b1), axis=0),
            tt.stack((p2, b2), axis=0),
        )
        return pb
Exemple #8
0
    def __init__(self, *, sigma, period, Q0, dQ, f, **kwargs):
        self.sigma = tt.as_tensor_variable(sigma).astype("float64")
        self.period = tt.as_tensor_variable(period).astype("float64")
        self.Q0 = tt.as_tensor_variable(Q0).astype("float64")
        self.dQ = tt.as_tensor_variable(dQ).astype("float64")
        self.f = tt.as_tensor_variable(f).astype("float64")

        self.amp = self.sigma ** 2 / (1 + self.f)

        # One term with a period of period
        Q1 = 0.5 + self.Q0 + self.dQ
        w1 = 4 * np.pi * Q1 / (self.period * tt.sqrt(4 * Q1 ** 2 - 1))
        S1 = self.amp / (w1 * Q1)

        # Another term at half the period
        Q2 = 0.5 + self.Q0
        w2 = 8 * np.pi * Q2 / (self.period * tt.sqrt(4 * Q2 ** 2 - 1))
        S2 = self.f * self.amp / (w2 * Q2)

        super().__init__(
            SHOTerm(S0=S1, w0=w1, Q=Q1), SHOTerm(S0=S2, w0=w2, Q=Q2), **kwargs
        )
Exemple #9
0
 def underdamped(self):
     Q = self.Q
     f = tt.sqrt(tt.maximum(4.0 * Q ** 2 - 1.0, self.eps))
     a = self.S0 * self.w0 * Q
     c = 0.5 * self.w0 / Q
     empty = tt.zeros(0, dtype=self.dtype)
     return (
         empty,
         empty,
         tt.stack([a]),
         tt.stack([a / f]),
         tt.stack([c]),
         tt.stack([c * f]),
     )
Exemple #10
0
 def overdamped(self):
     Q = self.Q
     f = tt.sqrt(tt.maximum(1.0 - 4.0 * Q ** 2, self.eps))
     empty = tt.zeros(0, dtype=self.dtype)
     return (
         0.5
         * self.S0
         * self.w0
         * Q
         * tt.stack([1.0 + 1.0 / f, 1.0 - 1.0 / f]),
         0.5 * self.w0 / Q * tt.stack([1.0 - f, 1.0 + f]),
         empty,
         empty,
         empty,
         empty,
     )
Exemple #11
0
def test_impact():
    m_star = 0.151
    r_star = 0.189
    period = 0.4626413
    t0 = 0.2
    b = 0.5
    ecc = 0.8
    omega = 0.1

    orbit = KeplerianOrbit(
        r_star=r_star,
        m_star=m_star,
        period=period,
        t0=t0,
        b=b,
        ecc=ecc,
        omega=omega,
    )
    coords = orbit.get_relative_position(t0)
    assert np.allclose((tt.sqrt(coords[0]**2 + coords[1]**2) / r_star).eval(),
                       b)
    assert coords[2].eval() > 0
Exemple #12
0
def _get_consistent_inputs(a, period, rho_star, r_star, m_star, m_planet):
    if a is None and period is None:
        raise ValueError("values must be provided for at least one of a "
                         "and period")

    if m_planet is not None:
        m_planet = as_tensor_variable(to_unit(m_planet, u.M_sun))

    if a is not None:
        a = as_tensor_variable(to_unit(a, u.R_sun))
        if m_planet is None:
            m_planet = tt.zeros_like(a)
    if period is not None:
        period = as_tensor_variable(to_unit(period, u.day))
        if m_planet is None:
            m_planet = tt.zeros_like(period)

    # Compute the implied density if a and period are given
    implied_rho_star = False
    if a is not None and period is not None:
        if rho_star is not None or m_star is not None:
            raise ValueError("if both a and period are given, you can't "
                             "also define rho_star or m_star")

        # Default to a stellar radius of 1 if not provided
        if r_star is None:
            r_star = as_tensor_variable(1.0)
        else:
            r_star = as_tensor_variable(to_unit(r_star, u.R_sun))

        # Compute the implied mass via Kepler's 3rd law
        m_tot = 4 * np.pi * np.pi * a**3 / (G_grav * period**2)

        # Compute the implied density
        m_star = m_tot - m_planet
        vol_star = 4 * np.pi * r_star**3 / 3.0
        rho_star = m_star / vol_star
        implied_rho_star = True

    # Make sure that the right combination of stellar parameters are given
    if r_star is None and m_star is None:
        r_star = 1.0
        if rho_star is None:
            m_star = 1.0
    if (not implied_rho_star) and sum(
            arg is None for arg in (rho_star, r_star, m_star)) != 1:
        raise ValueError("values must be provided for exactly two of "
                         "rho_star, m_star, and r_star")

    if rho_star is not None and not implied_rho_star:
        if has_unit(rho_star):
            rho_star = as_tensor_variable(
                to_unit(rho_star, u.M_sun / u.R_sun**3))
        else:
            rho_star = as_tensor_variable(rho_star) / gcc_per_sun
    if r_star is not None:
        r_star = as_tensor_variable(to_unit(r_star, u.R_sun))
    if m_star is not None:
        m_star = as_tensor_variable(to_unit(m_star, u.M_sun))

    # Work out the stellar parameters
    if rho_star is None:
        rho_star = 3 * m_star / (4 * np.pi * r_star**3)
    elif r_star is None:
        r_star = (3 * m_star / (4 * np.pi * rho_star))**(1 / 3)
    elif m_star is None:
        m_star = 4 * np.pi * r_star**3 * rho_star / 3.0

    # Work out the planet parameters
    if a is None:
        a = (G_grav * (m_star + m_planet) * period**2 / (4 * np.pi**2))**(1.0 /
                                                                          3)
    elif period is None:
        period = (2 * np.pi * a**(3 / 2) / (tt.sqrt(G_grav *
                                                    (m_star + m_planet))))

    return a, period, rho_star * gcc_per_sun, r_star, m_star, m_planet
Exemple #13
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)),
        )
Exemple #14
0
    def __init__(self,
                 period=None,
                 a=None,
                 t0=None,
                 t_periastron=None,
                 incl=None,
                 b=None,
                 duration=None,
                 ecc=None,
                 omega=None,
                 sin_omega=None,
                 cos_omega=None,
                 Omega=None,
                 m_planet=0.0,
                 m_star=None,
                 r_star=None,
                 rho_star=None,
                 ror=None,
                 model=None,
                 **kwargs):
        add_citations_to_model(self.__citations__, model=model)

        if "m_planet_units" in kwargs:
            deprecation_warning(
                "'m_planet_units' is deprecated; Use `with_unit` instead")
            m_planet = with_unit(m_planet, kwargs.pop("m_planet_units"))
        if "rho_star_units" in kwargs:
            deprecation_warning(
                "'rho_star_units' is deprecated; Use `with_unit` instead")
            rho_star = with_unit(rho_star, kwargs.pop("rho_star_units"))

        self.jacobians = defaultdict(lambda: defaultdict(None))

        daordtau = None
        if ecc is None and duration is not None:
            if r_star is None:
                r_star = as_tensor_variable(1.0)
            if b is None:
                raise ValueError(
                    "'b' must be provided for a circular orbit with a "
                    "'duration'")
            if ror is None:
                warnings.warn(
                    "When using the 'duration' parameter in KeplerianOrbit, "
                    "the 'ror' parameter should also be provided.",
                    UserWarning,
                )
            aor, daordtau = get_aor_from_transit_duration(duration,
                                                          period,
                                                          b,
                                                          ror=ror)
            a = r_star * aor
            duration = None

        inputs = _get_consistent_inputs(a, period, rho_star, r_star, m_star,
                                        m_planet)
        (
            self.a,
            self.period,
            self.rho_star,
            self.r_star,
            self.m_star,
            self.m_planet,
        ) = inputs
        self.m_total = self.m_star + self.m_planet

        self.n = 2 * np.pi / self.period
        self.a_star = self.a * self.m_planet / self.m_total
        self.a_planet = -self.a * self.m_star / self.m_total

        # Track the Jacobian between the duration and a
        if daordtau is not None:
            dadtau = self.r_star * daordtau
            self.jacobians["duration"]["a"] = dadtau
            self.jacobians["duration"]["a_star"] = (dadtau * self.m_planet /
                                                    self.m_total)
            self.jacobians["duration"]["a_planet"] = (-dadtau * self.m_star /
                                                      self.m_total)

            # rho = 3 * pi * (a/R)**3 / (G * P**2)
            # -> drho / d(a/R) = 9 * pi * (a/R)**2 / (G * P**2)
            self.jacobians["duration"]["rho_star"] = (
                9 * np.pi * (self.a / self.r_star)**2 * daordtau *
                gcc_per_sun / (G_grav * self.period**2))

        self.K0 = self.n * self.a / self.m_total

        if Omega is None:
            self.Omega = None
        else:
            self.Omega = as_tensor_variable(Omega)
            self.cos_Omega = tt.cos(self.Omega)
            self.sin_Omega = tt.sin(self.Omega)

        # Eccentricity
        if ecc is None:
            self.ecc = None
            self.M0 = 0.5 * np.pi + tt.zeros_like(self.n)
            incl_factor = 1
        else:
            self.ecc = as_tensor_variable(ecc)
            if omega is not None:
                if sin_omega is not None and cos_omega is not None:
                    raise ValueError(
                        "either 'omega' or 'sin_omega' and 'cos_omega' can be "
                        "provided")
                self.omega = as_tensor_variable(omega)
                self.cos_omega = tt.cos(self.omega)
                self.sin_omega = tt.sin(self.omega)
            elif sin_omega is not None and cos_omega is not None:
                self.cos_omega = as_tensor_variable(cos_omega)
                self.sin_omega = as_tensor_variable(sin_omega)
                self.omega = tt.arctan2(self.sin_omega, self.cos_omega)

            else:
                raise ValueError("both e and omega must be provided")

            opsw = 1 + self.sin_omega
            E0 = 2 * tt.arctan2(
                tt.sqrt(1 - self.ecc) * self.cos_omega,
                tt.sqrt(1 + self.ecc) * opsw,
            )
            self.M0 = E0 - self.ecc * tt.sin(E0)

            ome2 = 1 - self.ecc**2
            self.K0 /= tt.sqrt(ome2)
            incl_factor = (1 + self.ecc * self.sin_omega) / ome2

        # The Jacobian for the transform cos(i) -> b
        self.dcosidb = self.jacobians["b"]["cos_incl"] = (incl_factor *
                                                          self.r_star / self.a)

        if b is not None:
            if incl is not None or duration is not None:
                raise ValueError(
                    "only one of 'incl', 'b', and 'duration' can be given")
            self.b = as_tensor_variable(b)
            self.cos_incl = self.dcosidb * self.b
            self.incl = tt.arccos(self.cos_incl)
        elif incl is not None:
            if duration is not None:
                raise ValueError(
                    "only one of 'incl', 'b', and 'duration' can be given")
            self.incl = as_tensor_variable(incl)
            self.cos_incl = tt.cos(self.incl)
            self.b = self.cos_incl / self.dcosidb
        elif duration is not None:
            # This assertion should never be hit because of the first
            # conditional in this method, but let's keep it here anyways
            assert self.ecc is not None

            self.duration = as_tensor_variable(to_unit(duration, u.day))
            c = tt.sin(np.pi * self.duration * incl_factor / self.period)
            c2 = c * c
            aor = self.a_planet / self.r_star
            esinw = self.ecc * self.sin_omega
            self.b = tt.sqrt(
                (aor**2 * c2 - 1) / (c2 * esinw**2 + 2 * c2 * esinw + c2 -
                                     self.ecc**4 + 2 * self.ecc**2 - 1))
            self.b *= 1 - self.ecc**2
            self.cos_incl = self.dcosidb * self.b
            self.incl = tt.arccos(self.cos_incl)
        else:
            zla = tt.zeros_like(self.a)
            self.incl = 0.5 * np.pi + zla
            self.cos_incl = zla
            self.b = zla

        if t0 is not None and t_periastron is not None:
            raise ValueError("you can't define both t0 and t_periastron")
        if t0 is None and t_periastron is None:
            t0 = tt.zeros_like(self.period)

        if t0 is None:
            self.t_periastron = as_tensor_variable(t_periastron)
            self.t0 = self.t_periastron + self.M0 / self.n
        else:
            self.t0 = as_tensor_variable(t0)
            self.t_periastron = self.t0 - self.M0 / self.n

        self.tref = self.t_periastron - self.t0

        self.sin_incl = tt.sin(self.incl)
Exemple #15
0
    def in_transit(self, t, r=0.0, 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 not yet implemented for `in_transit`")

        z = tt.zeros_like(self.a)
        r = as_tensor_variable(r) + z
        R = self.r_star + z

        # Wrap the times into time since transit
        hp = 0.5 * self.period
        dt = tt.mod(self._warp_times(t) + hp, self.period) - hp

        if self.ecc is None:
            # Equation 14 from Winn (2010)
            k = r / R
            arg = tt.square(1 + k) - tt.square(self.b)
            factor = R / (self.a * self.sin_incl)
            hdur = hp * tt.arcsin(factor * tt.sqrt(arg)) / np.pi
            t_start = -hdur
            t_end = hdur
            flag = z

        else:
            M_contact = ops.contact_points(
                self.a,
                self.ecc,
                self.cos_omega,
                self.sin_omega,
                self.cos_incl + z,
                self.sin_incl + z,
                R + r,
            )
            flag = M_contact[2]

            t_start = (M_contact[0] - self.M0) / self.n
            t_start = tt.mod(t_start + hp, self.period) - hp
            t_end = (M_contact[1] - self.M0) / self.n
            t_end = tt.mod(t_end + hp, self.period) - hp

            t_start = tt.switch(tt.gt(t_start, 0.0), t_start - self.period,
                                t_start)
            t_end = tt.switch(tt.lt(t_end, 0.0), t_end + self.period, t_end)

        if texp is not None:
            t_start -= 0.5 * texp
            t_end += 0.5 * texp

        mask = tt.any(tt.and_(dt >= t_start, dt <= t_end), axis=-1)

        result = ifelse(
            tt.all(tt.eq(flag, 0)),
            tt.arange(t.shape[0])[mask],
            tt.arange(t.shape[0]),
        )

        return result
Exemple #16
0
 def backward(self, y):
     q = tt.nnet.sigmoid(y)
     sqrtq1 = tt.sqrt(q[0])
     twoq2 = 2 * q[1]
     u = tt.stack([sqrtq1 * twoq2, sqrtq1 * (1 - twoq2)])
     return u
Exemple #17
0
	def get_amplitude(self):
		return tt.sqrt(self.cos**2 + self.sin**2)
Exemple #18
0
    def get_light_curve(
        self,
        orbit=None,
        r=None,
        t=None,
        texp=None,
        oversample=7,
        order=0,
        use_in_transit=None,
        light_delay=False,
    ):
        """Get the light curve for an orbit at a set of times

        Args:
            orbit: An object with a ``get_relative_position`` method that
                takes a tensor of times and returns a list of Cartesian
                coordinates of a set of bodies relative to the central source.
                This method should return three tensors (one for each
                coordinate dimension) and each tensor should have the shape
                ``append(t.shape, r.shape)`` or ``append(t.shape, oversample,
                r.shape)`` when ``texp`` is given. The first two coordinate
                dimensions are treated as being in the plane of the sky and the
                third coordinate is the line of sight with positive values
                pointing *away* from the observer. For an example, take a look
                at :class:`orbits.KeplerianOrbit`.
            r (tensor): The radius of the transiting body in the same units as
                ``r_star``. This should have a shape that is consistent with
                the coordinates returned by ``orbit``. In general, this means
                that it should probably be a scalar or a vector with one entry
                for each body in ``orbit``. Note that this is a different
                quantity than the planet-to-star radius ratio; do not confuse
                the two!
            t (tensor): The times where the light curve should be evaluated.
            texp (Optional[tensor]): The exposure time of each observation.
                This can be a scalar or a tensor with the same shape as ``t``.
                If ``texp`` is provided, ``t`` is assumed to indicate the
                timestamp at the *middle* of an exposure of length ``texp``.
            oversample (Optional[int]): The number of function evaluations to
                use when numerically integrating the exposure time.
            order (Optional[int]): The order of the numerical integration
                scheme. This must be one of the following: ``0`` for a
                centered Riemann sum (equivalent to the "resampling" procedure
                suggested by Kipping 2010), ``1`` for the trapezoid rule, or
                ``2`` for Simpson's rule.
            use_in_transit (Optional[bool]): If ``True``, the model will only
                be evaluated for the data points expected to be in transit
                as computed using the ``in_transit`` method on ``orbit``.

        """
        if orbit is None:
            raise ValueError("missing required argument 'orbit'")
        if r is None:
            raise ValueError("missing required argument 'r'")
        if t is None:
            raise ValueError("missing required argument 't'")

        use_in_transit = (not light_delay
                          if use_in_transit is None else use_in_transit)

        r = as_tensor_variable(r)
        r = tt.reshape(r, (r.size, ))
        t = as_tensor_variable(t)

        # If use_in_transit, we should only evaluate the model at times where
        # at least one planet is transiting
        if use_in_transit:
            transit_model = tt.shape_padleft(tt.zeros_like(r),
                                             t.ndim) + tt.shape_padright(
                                                 tt.zeros_like(t), r.ndim)
            inds = orbit.in_transit(t, r=r, texp=texp, light_delay=light_delay)
            t = t[inds]

        # Handle exposure time integration
        if texp is None:
            tgrid = t
            rgrid = tt.shape_padleft(r, tgrid.ndim) + tt.shape_padright(
                tt.zeros_like(tgrid), r.ndim)
        else:
            texp = as_tensor_variable(texp)

            oversample = int(oversample)
            oversample += 1 - oversample % 2
            stencil = np.ones(oversample)

            # Construct the exposure time integration stencil
            if order == 0:
                dt = np.linspace(-0.5, 0.5, 2 * oversample + 1)[1:-1:2]
            elif order == 1:
                dt = np.linspace(-0.5, 0.5, oversample)
                stencil[1:-1] = 2
            elif order == 2:
                dt = np.linspace(-0.5, 0.5, oversample)
                stencil[1:-1:2] = 4
                stencil[2:-1:2] = 2
            else:
                raise ValueError("order must be <= 2")
            stencil /= np.sum(stencil)

            if texp.ndim == 0:
                dt = texp * dt
            else:
                if use_in_transit:
                    dt = tt.shape_padright(texp[inds]) * dt
                else:
                    dt = tt.shape_padright(texp) * dt
            tgrid = tt.shape_padright(t) + dt

            # Madness to get the shapes to work out...
            rgrid = tt.shape_padleft(r, tgrid.ndim) + tt.shape_padright(
                tt.zeros_like(tgrid), 1)

        # Evalute the coordinates of the transiting body in the plane of the
        # sky
        coords = orbit.get_relative_position(tgrid, light_delay=light_delay)
        b = tt.sqrt(coords[0]**2 + coords[1]**2)
        b = tt.reshape(b, rgrid.shape)
        los = tt.reshape(coords[2], rgrid.shape)

        lc = self._compute_light_curve(b / orbit.r_star, rgrid / orbit.r_star,
                                       los / orbit.r_star)

        if texp is not None:
            stencil = tt.shape_padright(tt.shape_padleft(stencil, t.ndim), 1)
            lc = tt.sum(stencil * lc, axis=t.ndim)

        if use_in_transit:
            transit_model = tt.set_subtensor(transit_model[inds], lc)
            return transit_model
        else:
            return lc