Esempio n. 1
0
def plot_sky(trace, m):
    # plot sky position for a full orbit
    xs_phase = np.linspace(0, 1, num=1000)

    with m.model:
        ts_full = xs_phase * m.P + m.t_periastron
        predict_full = m.orbit.get_relative_angles(ts_full, m.parallax)

    fig, ax = plt.subplots(nrows=1, figsize=(4, 4))

    for sample in xo.get_samples_from_trace(trace, size=20):

        # we'll want to cache these functions when we evaluate many samples
        rho_full, theta_full = xo.eval_in_model(
            predict_full, point=sample, model=m.model
        )

        x_full = rho_full * np.cos(theta_full)  # X North
        y_full = rho_full * np.sin(theta_full)
        ax.plot(y_full, x_full, color="C0", lw=0.8, alpha=0.7)

    xs = d.wds[1] * np.cos(d.wds[3])  # X is north
    ys = d.wds[1] * np.sin(d.wds[3])  # Y is east

    ax.plot(ys, xs, "ko")
    ax.set_ylabel(r"$\Delta \delta$ ['']")
    ax.set_xlabel(r"$\Delta \alpha \cos \delta$ ['']")
    ax.invert_xaxis()
    ax.plot(0, 0, "k*")
    ax.set_aspect("equal", "datalim")
    fig.subplots_adjust(left=0.18, right=0.82)

    return fig
Esempio n. 2
0
def evaluate_prediction_quantiles(event, pm_model, trace, t_grid, alert_time):
    """
    Evaluates median model prediciton in data space.
    """
    n_samples = 500

    samples = xo.get_samples_from_trace(trace, size=n_samples)

    with pm_model:
        # Compute the trajectory of the lens
        trajectory = ca.trajectory.Trajectory(
            event, alert_time + T.exp(pm_model.ln_delta_t0), pm_model.u0,
            pm_model.tE)
        u_dense = trajectory.compute_trajectory(t_grid)

        # Compute the magnification
        mag_dense = (u_dense**2 + 2) / (u_dense * T.sqrt(u_dense**2 + 4))

        F_base = 10**(-(pm_model.m_b - 22.0) / 2.5)

        # Compute the mean model
        prediction = pm_model.f * F_base * mag_dense + (1 -
                                                        pm_model.f) * F_base

    # Evaluate model for each sample on a fine grid
    n_pts_dense = T.shape(t_grid)[0].eval()
    n_bands = len(event.light_curves)

    prediction_eval = np.zeros((n_samples, n_bands, n_pts_dense))

    # Evaluate predictions in model context
    with pm_model:
        for i, sample in enumerate(samples):
            prediction_eval[i] = xo.eval_in_model(prediction, sample)

    for i in range(n_bands):
        q = np.percentile(prediction_eval[:, i, :], [16, 50, 84], axis=0)

    return q
Esempio n. 3
0
def plot_sep_pa(trace, m):
    # plot separation and PA across the observed dates
    ts_obs = np.linspace(
        np.min(d.wds[0]) - 300, np.max(d.wds[0]) + 300, num=1000
    )  # days

    with m.model:
        predict_fine = m.orbit.get_relative_angles(ts_obs, m.parallax)

    # we can plot the maximum posterior solution to see
    # pkw = {'marker':".", "color":"k", 'ls':""}
    ekw = {"color": "C1", "marker": "o", "ls": ""}

    fig, ax = plt.subplots(nrows=2, sharex=True, figsize=(6, 4))
    ax[0].set_ylabel(r'$\rho\,$ ["]')
    ax[1].set_ylabel(r"P.A. [radians]")
    ax[1].set_xlabel("JD [days]")

    for sample in xo.get_samples_from_trace(trace, size=20):

        # we'll want to cache these functions when we evaluate many samples
        rho_fine, theta_fine = xo.eval_in_model(
            predict_fine, point=sample, model=m.model
        )

        ax[0].plot(ts_obs, rho_fine, "C0")
        ax[1].plot(ts_obs, theta_fine, "C0")

    # get map sol for tot_rho_err
    tot_rho_err = np.sqrt(d.wds[2] ** 2 + np.exp(2 * np.median(trace["logRhoS"])))
    tot_theta_err = np.sqrt(d.wds[4] ** 2 + np.exp(2 * np.median(trace["logThetaS"])))

    # ax[0].plot(d.wds[0], d.wds[1], **pkw)
    ax[0].errorbar(d.wds[0], d.wds[1], yerr=tot_rho_err, **ekw)

    # ax[1].plot(jds, theta_data, **pkw)
    ax[1].errorbar(d.wds[0], d.wds[3], yerr=tot_theta_err, **ekw)

    return fig
Esempio n. 4
0
        # truths=truth,
        labels=["S1", "S2", "w1", "w2", "log_Q"],
    )
    fig.savefig('../results/test_results/gp_model/test_gp_corner.png')
    plt.close('all')

    #
    # Generate 50 realizations of the prediction sampling randomly from the chain
    #
    N_pred = 50
    pred_mu = np.empty((N_pred, len(true_t)))
    pred_var = np.empty((N_pred, len(true_t)))
    with model:
        pred = gp.predict(true_t, return_var=True, predict_mean=True)
        for i, sample in enumerate(
                xo.get_samples_from_trace(trace, size=N_pred)):
            pred_mu[i], pred_var[i] = xo.eval_in_model(pred, sample)

    # Plot the predictions
    for i in range(len(pred_mu)):
        mu = pred_mu[i]
        sd = np.sqrt(pred_var[i])
        label = None if i else "prediction"
        art = plt.fill_between(true_t, mu + sd, mu - sd, color="C1", alpha=0.1)
        art.set_edgecolor("none")
        plt.plot(true_t, mu, color="C1", label=label, alpha=0.1)

    plt.errorbar(t, y, yerr=yerr, fmt=".k", capsize=0, label="data")
    plt.plot(true_t, true_y, "k", lw=1.5, alpha=0.3, label="truth")
    plt.legend(fontsize=12, loc=2)
    plt.xlabel("t")
Esempio n. 5
0
)
_ = corner.corner(samples)

# %% [markdown]
# The "posterior predictive" plot that I like to make isn't the same as a "posterior predictive check" (which can be a good thing to do too).
# Instead, I like to look at the predictions of the model in the space of the data.
# We could have saved these predictions using a `pymc3.Deterministic` distribution, but that adds some overhead to each evaluation of the model so instead, we can use :func:`exoplanet.utils.get_samples_from_trace` to loop over a few random samples from the chain and then the :func:`exoplanet.eval_in_model` function to evaluate the prediction just for those samples.

# %%
# Generate 50 realizations of the prediction sampling randomly from the chain
N_pred = 50
pred_mu = np.empty((N_pred, len(true_t)))
pred_var = np.empty((N_pred, len(true_t)))
with model:
    pred = gp.predict(true_t, return_var=True, predict_mean=True)
    for i, sample in enumerate(xo.get_samples_from_trace(trace, size=N_pred)):
        pred_mu[i], pred_var[i] = xo.eval_in_model(pred, sample)

# Plot the predictions
for i in range(len(pred_mu)):
    mu = pred_mu[i]
    sd = np.sqrt(pred_var[i])
    label = None if i else "prediction"
    art = plt.fill_between(true_t, mu + sd, mu - sd, color="C1", alpha=0.1)
    art.set_edgecolor("none")
    plt.plot(true_t, mu, color="C1", label=label, alpha=0.1)

plt.errorbar(t, y, yerr=yerr, fmt=".k", capsize=0, label="data")
plt.plot(true_t, true_y, "k", lw=1.5, alpha=0.3, label="truth")
plt.legend(fontsize=12, loc=2)
plt.xlabel("t")
Esempio n. 6
0
    """

    phase = get_phase(data[0])

    # evaluate the model with the current parameter settings
    offset = offset_dict[label]
    d = data[1] - offset
    resid = d - model
    err = np.sqrt(data[2]**2 + np.exp(2 * err_dict[label]))

    color = color_dict[label]
    a.errorbar(phase, d, yerr=err, label=label, **ekw, color=color)
    a_r.errorbar(phase, resid, yerr=err, **ekw, color=color)


for sample in xo.get_samples_from_trace(trace, size=1):

    # we'll want to cache these functions when we evaluate many samples
    rv1_m = xo.eval_in_model(rv1, point=sample, model=m.model)
    rv2_m = xo.eval_in_model(rv2, point=sample, model=m.model)

    ax1.plot(xs_phase, rv1_m, **pkw)
    ax2.plot(xs_phase, rv2_m, **pkw)

    err_dict = {
        "CfA": sample["logjittercfa"],
        "Keck": sample["logjitterkeck"],
        "FEROS": sample["logjitterferos"],
        "du Pont": sample["logjitterdupont"],
    }
    offset_dict = {
Esempio n. 7
0
def PLD(tpf,
        planet_mask=None,
        aperture=None,
        return_soln=False,
        return_quick_corrected=False,
        sigma=5,
        trim=0,
        ndraws=1000):
    ''' Use exoplanet, pymc3 and theano to perform PLD correction

    Parameters
    ----------
    tpf : lk.TargetPixelFile
        Target Pixel File to Correct
    planet_mask : np.ndarray
        Boolean array. Cadences where planet_mask is False will be excluded from the
        PLD correction. Use this to mask out planet transits.
    '''
    if planet_mask is None:
        planet_mask = np.ones(len(tpf.time), dtype=bool)
    if aperture is None:
        aperture = tpf.pipeline_mask

    time = np.asarray(tpf.time, np.float64)
    if trim > 0:
        flux = np.asarray(tpf.flux[:, trim:-trim, trim:-trim], np.float64)
        flux_err = np.asarray(tpf.flux_err[:, trim:-trim, trim:-trim],
                              np.float64)
        aper = np.asarray(aperture, bool)[trim:-trim, trim:-trim]
    else:
        flux = np.asarray(tpf.flux, np.float64)
        flux_err = np.asarray(tpf.flux_err, np.float64)
        aper = np.asarray(aperture, bool)

    raw_flux = np.asarray(np.nansum(flux[:, aper], axis=(1)), np.float64)
    raw_flux_err = np.asarray(
        np.nansum(flux_err[:, aper]**2, axis=(1))**0.5, np.float64)

    raw_flux_err /= np.median(raw_flux)
    raw_flux /= np.median(raw_flux)
    raw_flux -= 1

    # Setting to Parts Per Thousand keeps us from hitting machine precision errors...
    raw_flux *= 1e3
    raw_flux_err *= 1e3

    # Build the first order PLD basis
    #    X_pld = np.reshape(flux[:, aper], (len(flux), -1))
    saturation = (np.nanpercentile(flux, 100, axis=0) > 175000)
    X_pld = np.reshape(flux[:, aper & ~saturation], (len(tpf.flux), -1))

    extra_pld = np.zeros((len(time), np.any(saturation, axis=0).sum()))
    idx = 0
    for column in saturation.T:
        if column.any():
            extra_pld[:, idx] = np.sum(flux[:, column, :], axis=(1, 2))
            idx += 1
    X_pld = np.hstack([X_pld, extra_pld])

    # Remove NaN pixels
    X_pld = X_pld[:, ~((~np.isfinite(X_pld)).all(axis=0))]
    X_pld = X_pld / np.sum(flux[:, aper], axis=-1)[:, None]

    # Build the second order PLD basis and run PCA to reduce the number of dimensions
    X2_pld = np.reshape(X_pld[:, None, :] * X_pld[:, :, None], (len(flux), -1))
    # Remove NaN pixels
    X2_pld = X2_pld[:, ~((~np.isfinite(X2_pld)).all(axis=0))]
    U, _, _ = np.linalg.svd(X2_pld, full_matrices=False)
    X2_pld = U[:, :X_pld.shape[1]]

    # Construct the design matrix and fit for the PLD model
    X_pld = np.concatenate((np.ones((len(flux), 1)), X_pld, X2_pld), axis=-1)

    # Create a matrix with small numbers along diagonal to ensure that
    # X.T * sigma^-1 * X is not singular, and prevent it from being non-invertable
    diag = np.diag((1e-8 * np.ones(X_pld.shape[1])))

    if (~np.isfinite(X_pld)).any():
        raise ValueError('NaNs in components.')
    if (np.any(raw_flux_err == 0)):
        raise ValueError('Zeros in raw_flux_err.')

    def build_model(mask=None, start=None):
        ''' Build a PYMC3 model

        Parameters
        ----------
        mask : np.ndarray
            Boolean array to mask cadences. Cadences that are False will be excluded
            from the model fit
        start : dict
            MAP Solution from exoplanet

        Returns
        -------
        model : pymc3.model.Model
            A pymc3 model
        map_soln : dict
            Best fit solution
        '''

        if mask is None:
            mask = np.ones(len(time), dtype=bool)

        with pm.Model() as model:
            # GP
            # --------
            logs2 = pm.Normal("logs2", mu=np.log(np.var(raw_flux[mask])), sd=4)
            #            pm.Potential("logs2_prior1", tt.switch(logs2 < -3, -np.inf, 0.0))

            logsigma = pm.Normal("logsigma",
                                 mu=np.log(np.std(raw_flux[mask])),
                                 sd=4)
            logrho = pm.Normal("logrho", mu=np.log(150), sd=4)
            kernel = xo.gp.terms.Matern32Term(log_rho=logrho,
                                              log_sigma=logsigma)
            gp = xo.gp.GP(kernel, time[mask],
                          tt.exp(logs2) + raw_flux_err[mask]**2)

            # Motion model
            #------------------
            A = tt.dot(X_pld[mask].T, gp.apply_inverse(X_pld[mask]))
            A = A + diag  # Add small numbers to diagonal to prevent it from being singular
            B = tt.dot(X_pld[mask].T, gp.apply_inverse(raw_flux[mask, None]))
            C = tt.slinalg.solve(A, B)
            motion_model = pm.Deterministic("motion_model",
                                            tt.dot(X_pld[mask], C)[:, 0])

            # Likelihood
            #------------------
            pm.Potential("obs",
                         gp.log_likelihood(raw_flux[mask] - motion_model))

            # gp predicted flux
            gp_pred = gp.predict()
            pm.Deterministic("gp_pred", gp_pred)
            pm.Deterministic("weights", C)

            # Optimize
            #------------------
            if start is None:
                start = model.test_point
            map_soln = xo.optimize(start=start, vars=[logsigma])
            map_soln = xo.optimize(start=start, vars=[logrho, logsigma])
            map_soln = xo.optimize(start=start, vars=[logsigma])
            map_soln = xo.optimize(start=start, vars=[logrho, logsigma])
            map_soln = xo.optimize(start=map_soln, vars=[logs2])
            map_soln = xo.optimize(start=map_soln,
                                   vars=[logrho, logsigma, logs2])
            return model, map_soln, gp

    # First rough correction
    log.info('Optimizing roughly')
    with silence():
        model0, map_soln0, gp = build_model(mask=planet_mask)

    # Remove outliers, make sure to remove a few nearby points incase of flares.
    with model0:
        motion = np.dot(X_pld, map_soln0['weights']).reshape(-1)
        stellar = xo.eval_in_model(gp.predict(time), map_soln0)
        corrected = raw_flux - motion - stellar
        mask = ~sigma_clip(corrected, sigma=sigma).mask
        mask = ~(convolve(mask, Box1DKernel(3), fill_value=1) != 1)
        mask &= planet_mask

    # Optimize PLD
    log.info('Optimizing without outliers')
    with silence():
        model, map_soln, gp = build_model(mask, map_soln0)
    lc_fig = _plot_light_curve(map_soln, model, mask, time, raw_flux,
                               raw_flux_err, X_pld, gp)

    if return_soln:
        motion = np.dot(X_pld, map_soln['weights']).reshape(-1)
        with model:
            stellar = xo.eval_in_model(gp.predict(time), map_soln)
        return model, map_soln, motion, stellar

    if return_quick_corrected:
        raw_lc = tpf.to_lightcurve()
        clc = lk.KeplerLightCurve(
            time=time,
            flux=(raw_flux - stellar - motion) * 1e-3 + 1,
            flux_err=(raw_flux_err) * 1e-3,
            time_format=raw_lc.time_format,
            centroid_col=tpf.estimate_centroids()[0],
            centroid_row=tpf.estimate_centroids()[0],
            quality=raw_lc.quality,
            channel=raw_lc.channel,
            campaign=raw_lc.campaign,
            quarter=raw_lc.quarter,
            mission=raw_lc.mission,
            cadenceno=raw_lc.cadenceno,
            targetid=raw_lc.targetid,
            ra=raw_lc.ra,
            dec=raw_lc.dec,
            label='{} PLD Corrected'.format(raw_lc.targetid))
        return clc

    # Burn in
    sampler = xo.PyMC3Sampler()
    with model:
        burnin = sampler.tune(tune=np.max([int(ndraws * 0.3), 150]),
                              start=map_soln,
                              step_kwargs=dict(target_accept=0.9),
                              chains=4)
    # Sample
    with model:
        trace = sampler.sample(draws=ndraws, chains=4)

    varnames = ["logrho", "logsigma", "logs2"]
    pm.traceplot(trace, varnames=varnames)

    samples = pm.trace_to_dataframe(trace, varnames=varnames)
    corner.corner(samples)

    # Generate 50 realizations of the prediction sampling randomly from the chain
    N_pred = 50
    pred_mu = np.empty((N_pred, len(time)))
    pred_motion = np.empty((N_pred, len(time)))
    with model:
        pred = gp.predict(time)
        for i, sample in enumerate(
                tqdm(xo.get_samples_from_trace(trace, size=N_pred),
                     total=N_pred)):
            pred_mu[i] = xo.eval_in_model(pred, sample)
            pred_motion[i, :] = np.dot(X_pld, sample['weights']).reshape(-1)

    star_model = np.mean(pred_mu + pred_motion, axis=0)
    star_model_err = np.std(pred_mu + pred_motion, axis=0)

    raw_lc = tpf.to_lightcurve()

    meta = {
        'samples': samples,
        'trace': trace,
        'pred_mu': pred_mu,
        'pred_motion': pred_motion
    }

    clc = lk.KeplerLightCurve(
        time=time,
        flux=(raw_flux - star_model) * 1e-3 + 1,
        flux_err=((raw_flux_err**2 + star_model_err**2)**0.5) * 1e-3,
        time_format=raw_lc.time_format,
        centroid_col=tpf.estimate_centroids()[0],
        centroid_row=tpf.estimate_centroids()[0],
        quality=raw_lc.quality,
        channel=raw_lc.channel,
        campaign=raw_lc.campaign,
        quarter=raw_lc.quarter,
        mission=raw_lc.mission,
        cadenceno=raw_lc.cadenceno,
        targetid=raw_lc.targetid,
        ra=raw_lc.ra,
        dec=raw_lc.dec,
        label='{} PLD Corrected'.format(raw_lc.targetid),
        meta=meta)
    return clc
Esempio n. 8
0
def plot_sep_pa(trace, m):

    # Plot the astrometric fits.
    # 1 square panel on left, then 3 rows of panels on right (rho, theta, v_B)

    # set up the figure

    # plot styles
    pkw = {"marker": "o", "ms": 3, "color": "k", "ls": "", "zorder": 20}
    lkw = {"ls": "-", "lw": 0.5}
    ekw = {
        "marker": "o",
        "ms": 3,
        "ls": "",
        "elinewidth": 0.8,
        "zorder": 20,
        "color": "k",
    }
    kekw = {**ekw, "color": "maroon"}

    xx = 7.1  # [in] textwidth
    hmargin = 0.0
    tmargin = 0.45
    bmargin = 0.5
    lmargin = 0.5
    rmargin = 0.5
    mmargin = 0.75

    ax_width = (xx - lmargin - rmargin - mmargin) / 2
    cax_frac = 0.7
    cax_margin = 0.05
    cax_height = 0.08
    ax_height = ax_width
    sax_height = (ax_height - hmargin) / 3

    yy = ax_height + tmargin + bmargin

    fig = plt.figure(figsize=(xx, yy))

    ax_sky = fig.add_axes(
        [lmargin / xx, bmargin / yy, ax_width / xx, ax_height / yy])
    ax_sky.set_xlabel(r"$\Delta \alpha \cos \delta\; [{}^{\prime\prime}]$")
    ax_sky.set_ylabel(r"$\Delta \delta\; [{}^{\prime\prime}]$")
    # ax_sky.invert_xaxis()

    cax = fig.add_axes([
        (lmargin + (1 - cax_frac) * ax_width / 2) / xx,
        (bmargin + ax_height + cax_margin) / yy,
        cax_frac * ax_width / xx,
        cax_height / yy,
    ])

    # load and plot the gas image here; routine in gas.py
    # frame width also set there
    plot_gas(ax_sky, cax)

    # set colorbar label
    cax.tick_params(
        axis="both",
        labelsize="small",
        labeltop=True,
        labelbottom=False,
        which="both",
        direction="in",
        bottom=False,
        top=True,
        pad=2,
    )
    cax.xaxis.set_label_position("top")
    cax.set_xlabel(r"$v_\mathrm{BARY} \quad {\rm[km\,s^{-1}]}$", labelpad=2)

    # plot the star
    ax_sky.plot(0, 0, "*", ms=5, color="k", mew=0.1, zorder=99)
    # plot the sky positions
    X_ABs = d.wds[1] * np.cos(d.wds[3])  # north
    Y_ABs = d.wds[1] * np.sin(d.wds[3])  # east
    ax_sky.plot(Y_ABs, X_ABs, **pkw)
    plot_errorbar(ax_sky,
                  d.wds[3],
                  d.wds[1],
                  d.wds[4],
                  d.wds[2],
                  color="C0",
                  lw=0.8)

    # make the right-column plots
    yr_lim = (1990, 2020)
    ax_sep = fig.add_axes([
        (lmargin + ax_width + mmargin) / xx,
        (2 * (sax_height + hmargin) + bmargin) / yy,
        ax_width / xx,
        sax_height / yy,
    ])
    ax_pa = fig.add_axes([
        (lmargin + ax_width + mmargin) / xx,
        (sax_height + hmargin + bmargin) / yy,
        ax_width / xx,
        sax_height / yy,
    ])
    ax_V = fig.add_axes([
        (lmargin + ax_width + mmargin) / xx,
        bmargin / yy,
        ax_width / xx,
        sax_height / yy,
    ])

    # get the mean value of all samples to use for errorbars and offset
    sep_err = np.sqrt(d.wds[2]**2 + np.exp(2 * np.mean(trace["logRhoS"])))
    ax_sep.errorbar(jd_to_year(d.wds[0]), d.wds[1], yerr=sep_err, **ekw)
    ax_sep.set_ylabel(r"$\rho\;[{}^{\prime\prime}]$")
    ax_sep.set_xlim(*yr_lim)
    ax_sep.xaxis.set_ticklabels([])

    pa_err = np.sqrt(d.wds[4]**2 + np.exp(2 * np.mean(trace["logThetaS"])))
    ax_pa.errorbar(jd_to_year(d.wds[0]),
                   d.wds[3] / deg + 360,
                   yerr=pa_err / deg,
                   **ekw)
    ax_pa.set_ylabel(r"$\theta\ [{}^\circ]$")
    ax_pa.set_xlim(*yr_lim)
    ax_pa.xaxis.set_ticklabels([])

    VB_err = np.sqrt(d.keck3[2]**2 +
                     np.exp(2 * np.mean(trace["logjitterkeck"])))
    ax_V.errorbar(
        jd_to_year(d.keck3[0]),
        d.keck3[1] - np.mean(trace["offsetKeck"]),
        yerr=VB_err,
        **kekw,
        label="Keck",
    )

    ax_V.set_xlim(*yr_lim)
    ax_V.set_ylim(6, 11)
    ax_V.set_ylabel(r"$v_\mathrm{B} \quad [\mathrm{km\;s}^{-1}$]")
    ax_V.set_xlabel("Epoch [yr]")
    ax_V.legend(
        loc="upper left",
        fontsize="xx-small",
        labelspacing=0.5,
        handletextpad=0.2,
        borderpad=0.4,
        borderaxespad=1.0,
    )

    ######
    # Plot the orbits on the  sep_pa.pdf figure
    ######

    # define new Theano variables to get the
    # 1) absolute X,Y,Z positions evaluated over the full orbital period
    # 2) the sep, PA values evaluated over the observational window
    # 3) the vB evaluated over the observational window

    # phases for the full orbital period
    phases = np.linspace(0, 1.0, num=500)

    # times for the observational window (unchanging)
    t_yr = Time(np.linspace(*yr_lim, num=500), format="byear").byear  # [yrs]
    t_obs = Time(np.linspace(*yr_lim, num=500),
                 format="byear").jd - jd0  # [days]

    with m.model:
        # the reason why we can refer to m.orbit_outer here (rather than, say)
        # m.model.orbit_outer, is because orbit_outer is actually in the scope of the
        # model module. It is in the *context* of the pymc3 model.model.
        # so we need both, for it to make sense.

        # convert phases to times stretching the full orbital period [days]
        t_period = phases * m.P_outer  # [days]

        # 1) absolute X,Y,Z positions evaluated over the full orbital period
        pos_outer = m.orbit_outer.get_relative_position(t_period, m.parallax)

        # 2) the sep, PA values evaluated over the observational window
        angles_outer = m.orbit_outer.get_relative_angles(t_obs, m.parallax)

        # 3) the vB evaluated over the observational window
        rv3 = (
            conv * m.orbit_outer.get_planet_velocity(t_obs)[2] + m.gamma_outer
            # do not include the offset, because we are plotting in the CfA frame
            # + m.offset_keck
        )

    # iterate among several samples to plot the orbits on the sep_pa.pdf figure.
    for sample in xo.get_samples_from_trace(trace, size=30):

        vBs = xo.eval_in_model(rv3, point=sample, model=m.model)
        # we can select orbits which have a higher vB[-1] than vB[0]
        # and colorize them

        # increasing = vBs[-1] > vBs[0]
        if sample["increasing"]:
            lkw["color"] = "C0"
            ax_V.plot(t_yr, vBs, **lkw, zorder=1)
        else:
            lkw["color"] = "C1"
            ax_V.plot(t_yr, vBs, **lkw, zorder=0)

        rho, theta = xo.eval_in_model(angles_outer,
                                      point=sample,
                                      model=m.model)
        ax_sep.plot(t_yr, rho, **lkw)
        ax_pa.plot(t_yr, theta / deg + 360, **lkw)

        X, Y, Z = xo.eval_in_model(pos_outer, point=sample, model=m.model)
        ax_sky.plot(Y, X, **lkw)

    return fig
Esempio n. 9
0
def plot_interior_RV(trace, m):
    """
    plot the interior RV

    Args: 
        trace : pymc3 trace
        m : the reference to the model module
    """

    # choose the plot styles
    color_dict = {"CfA": "C5", "Keck": "C2", "FEROS": "C3", "du Pont": "C4"}
    hkw = {"lw": 1.0, "color": "0.4", "ls": ":"}
    pkw = {"ls": "-", "lw": 0.5, "color": "C0"}
    ekw = {"marker": ".", "ms": 2.5, "ls": "", "elinewidth": 0.6, "zorder": 20}

    # set the figure dimensions

    lmargin = 0.47
    rmargin = lmargin
    bmargin = 0.4
    tmargin = 0.05
    mmargin = 0.07
    mmmargin = 0.15

    ax_height = 1.0
    ax_r_height = 0.4

    # \textwidth=7.1in
    # \columnsep=0.3125in
    # column width = (7.1 - 0.3125)/2 = 3.393

    xx = 3.393
    ax_width = xx - lmargin - rmargin

    yy = bmargin + tmargin + 2 * mmargin + mmmargin + 2 * ax_height + 2 * ax_r_height

    # fill out the axes

    fig = plt.figure(figsize=(xx, yy))
    ax1 = fig.add_axes([
        lmargin / xx, 1 - (tmargin + ax_height) / yy, ax_width / xx,
        ax_height / yy
    ])
    ax1_r = fig.add_axes([
        lmargin / xx,
        1 - (tmargin + mmargin + ax_height + ax_r_height) / yy,
        ax_width / xx,
        ax_r_height / yy,
    ])
    ax1_r.axhline(0.0, **hkw)
    ax2 = fig.add_axes([
        lmargin / xx,
        (bmargin + mmargin + ax_r_height) / yy,
        ax_width / xx,
        ax_height / yy,
    ])
    ax2_r = fig.add_axes(
        [lmargin / xx, bmargin / yy, ax_width / xx, ax_r_height / yy])
    ax2_r.axhline(0.0, **hkw)

    xs_phase = np.linspace(0, 1, num=500)

    # define new Theano variables to get the continuous model RVs and the
    # discrete models RVs to plot
    with m.model:
        # the reason why we can refer to m.P_inner here (rather than, say)
        # m.model.P_inner, is because P_inner is actually in the scope of the
        # model module. It is in the *context* of the pymc3 model.model.
        # so we need both, for it to make sense.
        ts_phases = xs_phase * m.P_inner + m.t_periastron_inner  # new theano var
        rv1, rv2 = m.get_RVs(ts_phases, ts_phases, 0.0)

        # We want to plot the model and data phased to a single reference orbit
        # this means we need to take out any additional motion to gamma_A relative
        # to the reference orbit from both the model and data
        gamma_A_epoch = m.get_gamma_A(ts_phases[0])

        err_dict = {
            "CfA": m.logjit_cfa,
            "Keck": m.logjit_keck,
            "FEROS": m.logjit_feros,
            "du Pont": m.logjit_dupont,
        }

        offset_dict = {
            "CfA": 0.0,
            "Keck": m.offset_keck,
            "FEROS": m.offset_feros,
            "du Pont": m.offset_dupont,
        }

        def get_centered_rvs(label, data1, data2):
            """
            Return the data and model discrete velocities centered on the reference 
            epoch bary velocity
            """
            t1 = tt.as_tensor_variable(data1[0])
            t2 = tt.as_tensor_variable(data2[0])

            offset = offset_dict[label]
            d1_corrected = data1[1] - offset - m.get_gamma_A(
                t1) + gamma_A_epoch
            d2_corrected = data2[1] - offset - m.get_gamma_A(
                t2) + gamma_A_epoch

            rv1_base, rv2_base = m.get_RVs(t1, t2, 0.0)
            rv1_corrected = rv1_base - m.get_gamma_A(t1) + gamma_A_epoch
            rv2_corrected = rv2_base - m.get_gamma_A(t2) + gamma_A_epoch

            e1 = np.sqrt(data1[2]**2 + np.exp(2 * err_dict[label]))
            e2 = np.sqrt(data2[2]**2 + np.exp(2 * err_dict[label]))

            return (
                t1,
                d1_corrected,
                e1,
                rv1_corrected,
                t2,
                d2_corrected,
                e2,
                rv2_corrected,
            )

        # repack to yield a set of corrected data, errors, and corrected model velocities
        rv_cfa0 = get_centered_rvs("CfA", d.cfa1, d.cfa2)
        rv_keck0 = get_centered_rvs("Keck", d.keck1, d.keck2)
        rv_feros0 = get_centered_rvs("FEROS", d.feros1, d.feros2)
        rv_dupont0 = get_centered_rvs("du Pont", d.dupont1, d.dupont2)

    # data, then phase them, then plot them.
    def phase_and_plot(rv_package, label):
        """
        Calculate inner orbit radial velocities and residuals for a given dataset and star.

        Args:
            data: tuple containing (date, rv, err)
            model: model rvs evaluated at date (with no offset)
            a: primary matplotlib axes
            a_r: residual matplotlib axes
            label: instrument that acquired data

        Returns:
            None
        """
        (
            t1,
            d1_corrected,
            e1,
            rv1_corrected,
            t2,
            d2_corrected,
            e2,
            rv2_corrected,
        ) = rv_package

        phase1 = get_phase(t1)
        phase2 = get_phase(t2)

        # evaluate the model with the current parameter settings
        color = color_dict[label]

        ax1.errorbar(phase1,
                     d1_corrected,
                     yerr=e1,
                     label=label,
                     **ekw,
                     color=color)
        ax1_r.errorbar(phase1,
                       d1_corrected - rv1_corrected,
                       yerr=e1,
                       **ekw,
                       color=color)

        ax2.errorbar(phase2,
                     d2_corrected,
                     yerr=e2,
                     label=label,
                     **ekw,
                     color=color)
        ax2_r.errorbar(phase2,
                       d2_corrected - rv2_corrected,
                       yerr=e2,
                       **ekw,
                       color=color)

    # plot many samples for the RV trace
    for sample in xo.get_samples_from_trace(trace, size=10):
        rv1_m = xo.eval_in_model(rv1, point=sample, model=m.model)
        rv2_m = xo.eval_in_model(rv2, point=sample, model=m.model)

        ax1.plot(xs_phase, rv1_m, **pkw)
        ax2.plot(xs_phase, rv2_m, **pkw)

    # get just a single sample to plot the data and model
    for sample in xo.get_samples_from_trace(trace, size=1):

        # create a phasing function local to these sampled values of
        # t_periastron and period
        def get_phase(dates):
            return ((dates - sample["tPeriastronInner"]) %
                    sample["PInner"]) / sample["PInner"]

        # Plot the data and residuals for this sample
        phase_and_plot(xo.eval_in_model(rv_cfa0, point=sample, model=m.model),
                       "CfA")
        phase_and_plot(xo.eval_in_model(rv_keck0, point=sample, model=m.model),
                       "Keck")
        phase_and_plot(
            xo.eval_in_model(rv_feros0, point=sample, model=m.model), "FEROS")
        phase_and_plot(
            xo.eval_in_model(rv_dupont0, point=sample, model=m.model),
            "du Pont")

    ax1.legend(
        loc="upper left",
        fontsize="xx-small",
        labelspacing=0.5,
        handletextpad=0.2,
        borderpad=0.4,
        borderaxespad=1.0,
    )

    ax1.set_ylabel(r"$v_\mathrm{Aa}$ [$\mathrm{km s}^{-1}$]", labelpad=0)
    ax1_r.set_ylabel(r"$O-C$", labelpad=-1)

    ax2.set_ylabel(r"$v_\mathrm{Ab}$ [$\mathrm{km s}^{-1}$]", labelpad=-5)
    ax2_r.set_ylabel(r"$O-C$", labelpad=-2)
    ax2_r.set_xlabel("phase")

    ax = [ax1, ax1_r, ax2, ax2_r]
    for a in ax:
        a.set_xlim(0, 1)

    ax1.xaxis.set_ticklabels([])
    ax1_r.xaxis.set_ticklabels([])
    ax2.xaxis.set_ticklabels([])

    return fig