Ejemplo n.º 1
0
def build_model(mask):
    with pm.Model() as model:
        # Systemic parameters
        mean_lc = pm.Normal("mean_lc", mu=0.0, sd=5.0)
        mean_rv = pm.Normal("mean_rv", mu=0.0, sd=50.0)
        u1 = xo.QuadLimbDark("u1")
        u2 = xo.QuadLimbDark("u2")

        # Parameters describing the primary
        M1 = pm.Lognormal("M1", mu=0.0, sigma=10.0, testval=0.696)
        R1 = pm.Lognormal("R1", mu=0.0, sigma=10.0, testval=0.687)

        # Secondary ratios
        k = pm.Lognormal("k", mu=0.0, sigma=10.0,
                         testval=0.9403)  # radius ratio
        q = pm.Lognormal("q", mu=0.0, sigma=10.0, testval=0.9815)  # mass ratio
        s = pm.Lognormal("s", mu=np.log(0.5),
                         sigma=10.0)  # surface brightness ratio

        # Prior on flux ratio
        pm.Normal(
            "flux_prior",
            mu=lit_flux_ratio[0],
            sigma=lit_flux_ratio[1],
            observed=k**2 * s,
        )

        # Parameters describing the orbit
        b = xo.ImpactParameter("b", ror=k, testval=1.5)
        period = pm.Lognormal("period", mu=np.log(lit_period), sigma=1.0)
        t0 = pm.Normal("t0", mu=lit_t0, sigma=1.0)

        # Parameters describing the eccentricity: ecs = [e * cos(w), e * sin(w)]
        ecs = xo.UnitDisk("ecs", testval=np.array([1e-5, 0.0]))
        ecc = pm.Deterministic("ecc", tt.sqrt(tt.sum(ecs**2)))
        omega = pm.Deterministic("omega", tt.arctan2(ecs[1], ecs[0]))

        # Build the orbit
        R2 = pm.Deterministic("R2", k * R1)
        M2 = pm.Deterministic("M2", q * M1)
        orbit = xo.orbits.KeplerianOrbit(
            period=period,
            t0=t0,
            ecc=ecc,
            omega=omega,
            b=b,
            r_star=R1,
            m_star=M1,
            m_planet=M2,
        )

        # Track some other orbital elements
        pm.Deterministic("incl", orbit.incl)
        pm.Deterministic("a", orbit.a)

        # Noise model for the light curve
        sigma_lc = pm.InverseGamma("sigma_lc",
                                   testval=1.0,
                                   **xo.estimate_inverse_gamma_parameters(
                                       0.1, 2.0))
        S_tot_lc = pm.InverseGamma("S_tot_lc",
                                   testval=2.5,
                                   **xo.estimate_inverse_gamma_parameters(
                                       1.0, 5.0))
        ell_lc = pm.InverseGamma("ell_lc",
                                 testval=2.0,
                                 **xo.estimate_inverse_gamma_parameters(
                                     1.0, 5.0))
        kernel_lc = xo.gp.terms.SHOTerm(S_tot=S_tot_lc,
                                        w0=2 * np.pi / ell_lc,
                                        Q=1.0 / 3)

        # Noise model for the radial velocities
        sigma_rv1 = pm.InverseGamma("sigma_rv1",
                                    testval=1.0,
                                    **xo.estimate_inverse_gamma_parameters(
                                        0.5, 5.0))
        sigma_rv2 = pm.InverseGamma("sigma_rv2",
                                    testval=1.0,
                                    **xo.estimate_inverse_gamma_parameters(
                                        0.5, 5.0))
        S_tot_rv = pm.InverseGamma("S_tot_rv",
                                   testval=2.5,
                                   **xo.estimate_inverse_gamma_parameters(
                                       1.0, 5.0))
        ell_rv = pm.InverseGamma("ell_rv",
                                 testval=2.0,
                                 **xo.estimate_inverse_gamma_parameters(
                                     1.0, 5.0))
        kernel_rv = xo.gp.terms.SHOTerm(S_tot=S_tot_rv,
                                        w0=2 * np.pi / ell_rv,
                                        Q=1.0 / 3)

        # Set up the light curve model
        lc = xo.SecondaryEclipseLightCurve(u1, u2, s)

        def model_lc(t):
            return (
                mean_lc + 1e3 *
                lc.get_light_curve(orbit=orbit, r=R2, t=t, texp=texp)[:, 0])

        # Condition the light curve model on the data
        gp_lc = xo.gp.GP(kernel_lc,
                         x[mask],
                         tt.zeros(mask.sum())**2 + sigma_lc**2,
                         mean=model_lc)
        gp_lc.marginal("obs_lc", observed=y[mask])

        # Set up the radial velocity model
        def model_rv1(t):
            return mean_rv + 1e-3 * orbit.get_radial_velocity(t)

        def model_rv2(t):
            return mean_rv - 1e-3 * orbit.get_radial_velocity(t) / q

        # Condition the radial velocity model on the data
        gp_rv1 = xo.gp.GP(kernel_rv,
                          x_rv,
                          tt.zeros(len(x_rv))**2 + sigma_rv1**2,
                          mean=model_rv1)
        gp_rv1.marginal("obs_rv1", observed=y1_rv)
        gp_rv2 = xo.gp.GP(kernel_rv,
                          x_rv,
                          tt.zeros(len(x_rv))**2 + sigma_rv2**2,
                          mean=model_rv2)
        gp_rv2.marginal("obs_rv2", observed=y2_rv)

        # Optimize the logp
        map_soln = model.test_point

        # First the RV parameters
        map_soln = xo.optimize(map_soln, [mean_rv, q])
        map_soln = xo.optimize(
            map_soln, [mean_rv, sigma_rv1, sigma_rv2, S_tot_rv, ell_rv])

        # Then the LC parameters
        map_soln = xo.optimize(map_soln, [mean_lc, R1, k, s, b])
        map_soln = xo.optimize(map_soln, [mean_lc, R1, k, s, b, u1, u2])
        map_soln = xo.optimize(map_soln, [mean_lc, sigma_lc, S_tot_lc, ell_lc])
        map_soln = xo.optimize(map_soln, [t0, period])

        # Then all the parameters together
        map_soln = xo.optimize(map_soln)

        model.gp_lc = gp_lc
        model.model_lc = model_lc
        model.gp_rv1 = gp_rv1
        model.model_rv1 = model_rv1
        model.gp_rv2 = gp_rv2
        model.model_rv2 = model_rv2

        model.x = x[mask]
        model.y = y[mask]

    return model, map_soln
Ejemplo n.º 2
0
    logK = pm.Uniform(
        "logK",
        lower=0,
        upper=np.log(200),
        testval=np.log(0.5 * (np.max(rv) - np.min(rv))),
    )
    logP = pm.Uniform("logP",
                      lower=0,
                      upper=np.log(10),
                      testval=np.log(lit_period))
    phi = pm.Uniform("phi", lower=0, upper=2 * np.pi, testval=0.1)

    # Parameterize the eccentricity using:
    #  h = sqrt(e) * sin(w)
    #  k = sqrt(e) * cos(w)
    hk = xo.UnitDisk("hk", testval=np.array([0.01, 0.01]))
    e = pm.Deterministic("e", hk[0]**2 + hk[1]**2)
    w = pm.Deterministic("w", tt.arctan2(hk[1], hk[0]))

    rv0 = pm.Normal("rv0", mu=0.0, sd=10.0, testval=0.0)
    rvtrend = pm.Normal("rvtrend", mu=0.0, sd=10.0, testval=0.0)

    # Deterministic transformations
    n = 2 * np.pi * tt.exp(-logP)
    P = pm.Deterministic("P", tt.exp(logP))
    K = pm.Deterministic("K", tt.exp(logK))
    cosw = tt.cos(w)
    sinw = tt.sin(w)
    t0 = (phi + w) / n

    # The RV model
Ejemplo n.º 3
0
    def run_rv_inference(self, prior_d, pklpath, make_threadsafe=True):

        # if the model has already been run, pull the result from the
        # pickle. otherwise, run it.
        if os.path.exists(pklpath):
            d = pickle.load(open(pklpath, 'rb'))
            self.model = d['model']
            self.trace = d['trace']
            self.map_estimate = d['map_estimate']
            return 1

        with pm.Model() as model:

            # Fixed data errors.
            sigma = self.y_err

            # Define priors and PyMC3 random variables to sample over.

            # Stellar parameters. (Following tess.world notebooks).
            logg_star = pm.Normal("logg_star",
                                  mu=prior_d['logg_star'][0],
                                  sd=prior_d['logg_star'][1])

            r_star = pm.Bound(pm.Normal, lower=0.0)("r_star",
                                                    mu=prior_d['r_star'][0],
                                                    sd=prior_d['r_star'][1])
            rho_star = pm.Deterministic("rho_star",
                                        factor * 10**logg_star / r_star)

            # RV parameters.

            # Chen & Kipping predicted M: 49.631 Mearth, based on Rp of 8Re. It
            # could be bigger, e.g., 94m/s if 1 Mjup.
            # Predicted K: 14.26 m/s

            #K = pm.Lognormal("K", mu=np.log(prior_d['K'][0]),
            #                 sigma=prior_d['K'][1])
            log_K = pm.Uniform('log_K',
                               lower=prior_d['log_K'][0],
                               upper=prior_d['log_K'][1])
            K = pm.Deterministic('K', tt.exp(log_K))

            period = pm.Normal("period",
                               mu=prior_d['period'][0],
                               sigma=prior_d['period'][1])

            ecs = xo.UnitDisk("ecs", testval=np.array([0.7, -0.3]))
            ecc = pm.Deterministic("ecc", tt.sum(ecs**2))

            omega = pm.Deterministic("omega", tt.arctan2(ecs[1], ecs[0]))

            phase = xo.UnitUniform("phase")

            # use time of transit, rather than time of periastron. we do, after
            # all, know it.
            t0 = pm.Normal("t0",
                           mu=prior_d['t0'][0],
                           sd=prior_d['t0'][1],
                           testval=prior_d['t0'][0])

            orbit = xo.orbits.KeplerianOrbit(period=period,
                                             t0=t0,
                                             rho_star=rho_star,
                                             ecc=ecc,
                                             omega=omega)

            #FIXME edit these
            # noise model parameters: FIXME what are these?
            S_tot = pm.Lognormal("S_tot",
                                 mu=np.log(prior_d['S_tot'][0]),
                                 sigma=prior_d['S_tot'][1])
            ell = pm.Lognormal("ell",
                               mu=np.log(prior_d['ell'][0]),
                               sigma=prior_d['ell'][1])

            # per instrument parameters
            means = pm.Normal(
                "means",
                mu=np.array([
                    np.median(self.y_obs[self.telvec == u])
                    for u in self.uniqueinstrs
                ]),
                sigma=500,
                shape=self.num_inst,
            )

            # different instruments have different intrinsic jitters. assign
            # those based on the reported error bars. (NOTE: might inflate or
            # overwrite these, for say, CHIRON)
            sigmas = pm.HalfNormal("sigmas",
                                   sigma=np.array([
                                       np.median(self.y_err[self.telvec == u])
                                       for u in self.uniqueinstrs
                                   ]),
                                   shape=self.num_inst)

            # Compute the RV offset and jitter for each data point depending on
            # its instrument
            mean = tt.zeros(len(self.x_obs))
            diag = tt.zeros(len(self.x_obs))
            for i, u in enumerate(self.uniqueinstrs):
                mean += means[i] * (self.telvec == u)
                diag += (self.y_err**2 + sigmas[i]**2) * (self.telvec == u)
            pm.Deterministic("mean", mean)
            pm.Deterministic("diag", diag)

            # NOTE: local function definition is jank
            def rv_model(x):
                return orbit.get_radial_velocity(x, K=K)

            kernel = xo.gp.terms.SHOTerm(S_tot=S_tot,
                                         w0=2 * np.pi / ell,
                                         Q=1.0 / 3)
            # NOTE temp
            gp = xo.gp.GP(kernel, self.x_obs, diag, mean=rv_model)
            # gp = xo.gp.GP(kernel, self.x_obs, diag,
            #               mean=orbit.get_radial_velocity(self.x_obs, K=K))
            # the actual "conditioning" step, i.e. the likelihood definition
            gp.marginal("obs", observed=self.y_obs - mean)
            pm.Deterministic("gp_pred", gp.predict())

            map_estimate = model.test_point
            map_estimate = xo.optimize(map_estimate, [means])
            map_estimate = xo.optimize(map_estimate, [means, phase])
            map_estimate = xo.optimize(map_estimate, [means, phase, log_K])
            map_estimate = xo.optimize(map_estimate,
                                       [means, t0, log_K, period, ecs])
            map_estimate = xo.optimize(map_estimate, [sigmas, S_tot, ell])
            map_estimate = xo.optimize(map_estimate)

            #
            # Derived parameters
            #

            #TODO
            # # planet radius in jupiter radii
            # r_planet = pm.Deterministic(
            #     "r_planet", (r*r_star)*( 1*units.Rsun/(1*units.Rjup) ).cgs.value
            # )

        # Plot the simulated data and the maximum a posteriori model to
        # make sure that our initialization looks ok.

        # i.e., "detrended". the "rv data" are y_obs - mean. The "trend" model
        # is a GP. FIXME: AFAIK, it doesn't do much as-implemented.
        self.y_MAP = (self.y_obs - map_estimate["mean"] -
                      map_estimate["gp_pred"])

        t_pred = np.linspace(self.x_obs.min() - 10,
                             self.x_obs.max() + 10, 10000)

        with model:
            # NOTE temp
            y_pred_MAP = xo.eval_in_model(rv_model(t_pred), map_estimate)
            # # NOTE temp
            # y_pred_MAP = xo.eval_in_model(
            #     orbit.get_radial_velocity(t_pred, K=K), map_estimate
            # )

        self.x_pred = t_pred
        self.y_pred_MAP = y_pred_MAP

        if make_threadsafe:
            pass
        else:
            # as described in
            # https://github.com/matplotlib/matplotlib/issues/15410
            # matplotlib is not threadsafe. so do not make plots before
            # sampling, because some child processes tries to close a
            # cached file, and crashes the sampler.

            print(map_estimate)

            if self.PLOTDIR is None:
                raise NotImplementedError
            outpath = os.path.join(self.PLOTDIR,
                                   'test_{}_MAP.png'.format(self.modelid))

            plot_MAP_rv(self.x_obs, self.y_obs, self.y_MAP, self.y_err,
                        self.telcolors, self.x_pred, self.y_pred_MAP,
                        map_estimate, outpath)

        with model:
            # sample from the posterior defined by this model.
            trace = pm.sample(
                tune=self.N_samples,
                draws=self.N_samples,
                start=map_estimate,
                cores=self.N_cores,
                chains=self.N_chains,
                step=xo.get_dense_nuts_step(target_accept=0.8),
            )

        # with open(pklpath, 'wb') as buff:
        #     pickle.dump({'model': model, 'trace': trace,
        #                  'map_estimate': map_estimate}, buff)

        self.model = model
        self.trace = trace
        self.map_estimate = map_estimate
Ejemplo n.º 4
0
    # Prior on flux ratio
    pm.Normal(
        "flux_prior",
        mu=lit_flux_ratio[0],
        sigma=lit_flux_ratio[1],
        observed=k**2 * s,
    )

    # Parameters describing the orbit
    b = xo.ImpactParameter("b", ror=k, testval=1.5)
    period = pm.Lognormal("period", mu=np.log(lit_period), sigma=1.0)
    t0 = pm.Normal("t0", mu=lit_t0, sigma=1.0)

    # Parameters describing the eccentricity: ecs = [e * cos(w), e * sin(w)]
    ecs = xo.UnitDisk("ecs", testval=np.array([1e-5, 0.0]))
    ecc = pm.Deterministic("ecc", tt.sqrt(tt.sum(ecs**2)))
    omega = pm.Deterministic("omega", tt.arctan2(ecs[1], ecs[0]))

    # Build the orbit
    R2 = pm.Deterministic("R2", k * R1)
    M2 = pm.Deterministic("M2", q * M1)
    orbit = xo.orbits.KeplerianOrbit(
        period=period,
        t0=t0,
        ecc=ecc,
        omega=omega,
        b=b,
        r_star=R1,
        m_star=M1,
        m_planet=M2,