Fit Radial Velocity and Astrometry

You can use Octofitter to jointly fit relative astrometry data and radial velocity data. Below is an example. For more information on these functions, see previous guides.

Import required packages

using Octofitter
using OctofitterRadialVelocity
using CairoMakie
using PairPlots
using Distributions
using PlanetOrbits

We now use PlanetOrbits.jl to create sample data. We start with a template orbit and record it's positon and velocity at a few epochs.

orb_template = orbit(
    a = 1.0,
    e = 0.7,
    i= pi/4,
    Ω = 0.1,
    ω = 1π/4,
    M = 1.0,
    plx=100.0,
    m =0,
    tp =58829-40
)
Makie.lines(orb_template,axis=(;autolimitaspect=1))
Example block output

Sample position and store as relative astrometry measurements:

epochs = [58849,58852,58858,58890]
astrom_dat = Table(
    epoch=epochs,
    ra=raoff.(orb_template, epochs),
    dec=decoff.(orb_template, epochs),
    σ_ra=fill(1.0, size(epochs)),
    σ_dec=fill(1.0, size(epochs)),
    cor=fill(0.0, size(epochs))
)

astrom = PlanetRelAstromLikelihood(
    astrom_dat,
    name = "simulated",
    variables = @variables begin
        # Fixed values for this example - could be free variables:
        jitter = 0        # mas [could use: jitter ~ Uniform(0, 10)]
        northangle = 0    # radians [could use: northangle ~ Normal(0, deg2rad(1))]
        platescale = 1    # relative [could use: platescale ~ truncated(Normal(1, 0.01), lower=0)]
    end
)
PlanetRelAstromLikelihood Table with 6 columns and 4 rows:
     epoch  ra        dec       σ_ra  σ_dec  cor
   ┌────────────────────────────────────────────
 1 │ 58849  -18.3017  -108.907  1.0   1.0    0.0
 2 │ 58852  -21.1181  -111.432  1.0   1.0    0.0
 3 │ 58858  -26.6349  -115.889  1.0   1.0    0.0
 4 │ 58890  -53.1334  -129.043  1.0   1.0    0.0

And plot our simulated astrometry measurments:

fig = Makie.lines(orb_template,axis=(;autolimitaspect=1))
Makie.scatter!(astrom.table.ra, astrom.table.dec)
fig
Example block output

Generate a simulated RV curve from the same orbit:

using Random
Random.seed!(1)

epochs = 58849 .+ range(0,step=1.5, length=20)
planet_sim_mass = 0.001 # solar masses here


rvlike = MarginalizedStarAbsoluteRVLikelihood(
    Table(
        epoch=epochs,
        rv=radvel.(orb_template, epochs, planet_sim_mass) .+ 150,
        σ_rv=fill(5.0, size(epochs)),
    ),
    name="inst1",
    variables=@variables begin
        jitter ~ LogUniform(0.1, 100) # m/s
    end
)

epochs = 58949 .+ range(0,step=1.5, length=20)

rvlike2 = MarginalizedStarAbsoluteRVLikelihood(
    Table(
        epoch=epochs,
        rv=radvel.(orb_template, epochs, planet_sim_mass) .- 150,
        σ_rv=fill(5.0, size(epochs)),
    ),
    name="inst2",
    variables=@variables begin
        jitter ~ LogUniform(0.1, 100) # m/s
    end
)

fap = Makie.scatter(rvlike.table.epoch[:], rvlike.table.rv[:])
Makie.scatter!(rvlike2.table.epoch[:], rvlike2.table.rv[:])
fap
Example block output

Now specify model and fit it:

planet_b = Planet(
    name="b",
    basis=Visual{KepOrbit},
    likelihoods=[astrom],
    variables=@variables begin
        e ~ Uniform(0,0.999999)
        a ~ truncated(Normal(1, 1),lower=0.1)
        mass ~ truncated(Normal(1, 1), lower=0.)
        i ~ Sine()
        M = system.M
        Ω ~ UniformCircular()
        ω ~ UniformCircular()
        θ ~ UniformCircular()
        tp = θ_at_epoch_to_tperi(θ, 58849.0; M, e, a, i, ω, Ω)  # reference epoch for θ. Choose an MJD date near your data.
    end
)

sys = System(
    name="test",
    companions=[planet_b],
    likelihoods=[rvlike, rvlike2],
    variables=@variables begin
        M ~ truncated(Normal(1, 0.04),lower=0.1) # (Baines & Armstrong 2011).
        plx = 100.0
    end
)

model = Octofitter.LogDensityModel(sys)

using Random
rng = Xoshiro(0) # seed the random number generator for reproducible results

results = octofit(rng, model, max_depth=9, adaptation=300, iterations=400)
Chains MCMC chain (400×35×1 Array{Float64, 3}):

Iterations        = 1:1:400
Number of chains  = 1
Samples per chain = 400
Wall duration     = 16.05 seconds
Compute duration  = 16.05 seconds
parameters        = M, plx, inst1_jitter, inst2_jitter, b_e, b_a, b_mass, b_i, b_Ωx, b_Ωy, b_ωx, b_ωy, b_θx, b_θy, b_Ω, b_ω, b_θ, b_M, b_tp, b_simulated_jitter, b_simulated_northangle, b_simulated_platescale
internals         = n_steps, is_accept, acceptance_rate, hamiltonian_energy, hamiltonian_energy_error, max_hamiltonian_energy_error, tree_depth, numerical_error, step_size, nom_step_size, is_adapt, loglike, logpost, tree_depth, numerical_error

Summary Statistics
              parameters         mean        std      mcse   ess_bulk   ess_ta ⋯
                  Symbol      Float64    Float64   Float64    Float64    Float ⋯

                       M       0.9962     0.0365    0.0023   262.0562   175.17 ⋯
                     plx     100.0000     0.0000       NaN        NaN        N ⋯
            inst1_jitter       0.4192     0.3472    0.0165   310.9348   306.25 ⋯
            inst2_jitter       0.4218     0.3738    0.0226   138.3492   158.67 ⋯
                     b_e       0.4376     0.2631    0.0812    10.2130    20.49 ⋯
                     b_a       1.6073     0.5316    0.0671    63.1805    60.21 ⋯
                  b_mass       0.8007     0.5585    0.0400   125.5125   190.51 ⋯
                     b_i       1.0293     0.1828    0.0512    15.5406    67.28 ⋯
                    b_Ωx       0.7049     0.4434    0.2277     6.7782    13.65 ⋯
                    b_Ωy       0.1698     0.5420    0.2491     6.8653    15.45 ⋯
                    b_ωx       0.0664     0.6395    0.1556    29.1120   220.81 ⋯
                    b_ωy       0.5471     0.5513    0.1292    23.7542    39.77 ⋯
                    b_θx      -0.9978     0.1026    0.0062   304.9518   207.82 ⋯
                    b_θy      -0.1679     0.0179    0.0010   307.6933   248.51 ⋯
                     b_Ω       0.0518     0.8737    0.4161     6.7691    12.81 ⋯
                     b_ω       1.1821     1.1934    0.3420    11.8772    58.04 ⋯
                     b_θ      -2.9748     0.0053    0.0002   561.2506   304.56 ⋯
            ⋮                  ⋮           ⋮          ⋮         ⋮          ⋮   ⋱
                                                    3 columns and 5 rows omitted

Quantiles
              parameters         2.5%        25.0%        50.0%        75.0%   ⋯
                  Symbol      Float64      Float64      Float64      Float64   ⋯

                       M       0.9193       0.9746       0.9943       1.0198   ⋯
                     plx     100.0000     100.0000     100.0000     100.0000   ⋯
            inst1_jitter       0.1080       0.1705       0.2769       0.5635   ⋯
            inst2_jitter       0.1021       0.1691       0.2880       0.5745   ⋯
                     b_e       0.0162       0.1871       0.4682       0.6469   ⋯
                     b_a       0.9064       1.2163       1.5109       1.8314   ⋯
                  b_mass       0.0632       0.3586       0.6827       1.1387   ⋯
                     b_i       0.6170       0.9173       1.0896       1.1642   ⋯
                    b_Ωx      -0.5694       0.6786       0.8506       0.9664   ⋯
                    b_Ωy      -0.9622      -0.0223       0.3331       0.5549   ⋯
                    b_ωx      -1.0055      -0.5075       0.1480       0.6454   ⋯
                    b_ωy      -0.8426       0.3985       0.7425       0.9264   ⋯
                    b_θx      -1.2491      -1.0598      -0.9955      -0.9272   ⋯
                    b_θy      -0.2054      -0.1790      -0.1674      -0.1547   ⋯
                     b_Ω      -2.1833      -0.0227       0.3369       0.5791   ⋯
                     b_ω      -1.7728       0.6876       1.3367       2.0482   ⋯
                     b_θ      -2.9854      -2.9784      -2.9747      -2.9712   ⋯
            ⋮                  ⋮            ⋮            ⋮            ⋮        ⋱
                                                     1 column and 5 rows omitted

Display results as a corner plot:

octocorner(model,results, small=true)
Example block output

Plot RV curve, phase folded curve, and binned residuals:

Octofitter.rvpostplot(model, results)
Example block output

Display RV, PMA, astrometry, relative separation, position angle, and 3D projected views:

octoplot(model, results)
Example block output