Basic RV Fit

You can use Octofitter to fit radial velocity data, either alone or in combination with other kinds of data. Multiple instruments (any number) are supported, as are arbitrary trends, and gaussian processes to model stellar activity.

Note

Radial velocity modelling is supported in Octofitter via the extension package OctofitterRadialVelocity. To install it, run pkg> add OctofitterRadialVelocity

For this example, we will fit the orbit of the planet K2-131, and reproduce this RadVel tutorial.

We will use the following packages:

using Octofitter
using OctofitterRadialVelocity
using PlanetOrbits
using CairoMakie
using PairPlots
using CSV
using DataFrames
using Distributions

We will start by downloading and preparing a table of radial velocity measurements, and create a StarAbsoluteRVLikelihood object to hold them.

The following functions allow you to directly load data from various public RV databases:

  • HARPS_DR1_rvs("star-name")
  • HARPS_RVBank_observations("star-name")
  • Lick_rvs("star-name")
  • HIRES_rvs("star-name")

Basic Fit

Start by fitting a 1-planet Keplerian model.

Calling those functions with the name of a star will return a StarAbsoluteRVLikelihood table.

If you would like to manually specify RV data, use the following format:

rv_like = StarAbsoluteRVLikelihood(
    # epoch is in units of MJD. `jd2mjd` is a helper function to convert.
    # you can also put `years2mjd(2016.1231)`.
    # rv and σ_rv are in units of meters/second
    (epoch=jd2mjd(2455110.97985),  rv=−6.54, σ_rv=1.30),
    (epoch=jd2mjd(2455171.90825),  rv=−3.33, σ_rv=1.09),
    # ... etc.
    instrument_name="insert name here"
)

For this example, to replicate the results of RadVel, we will download their example data for K2-131 and format it for Octofitter:

rv_file = download("https://raw.githubusercontent.com/California-Planet-Search/radvel/master/example_data/k2-131.txt")
rv_dat_raw = CSV.read(rv_file, DataFrame, delim=' ')
rv_dat = DataFrame();
rv_dat.epoch = jd2mjd.(rv_dat_raw.time)
rv_dat.rv = rv_dat_raw.mnvel
rv_dat.σ_rv = rv_dat_raw.errvel
tels = sort(unique(rv_dat_raw.tel))

# This table includes data from two insturments. We create a separate
# likelihood object for each:
rvlike_harps = StarAbsoluteRVLikelihood(
    rv_dat[rv_dat_raw.tel .== "harps-n",:],
    instrument_name="harps-n",
    offset=:rv0_harps,
    jitter=:jitter_harps,
)
rvlike_pfs = StarAbsoluteRVLikelihood(
    rv_dat[rv_dat_raw.tel .== "pfs",:],
    instrument_name="pfs",
    offset=:rv0_pfs,
    jitter=:jitter_pfs,
)
StarAbsoluteRVLikelihood Table with 3 columns and 31 rows:
      epoch    rv      σ_rv
    ┌──────────────────────
 1  │ 57828.4  -45.17  3.88
 2  │ 57828.4  -36.47  4.03
 3  │ 57829.2  -20.52  3.55
 4  │ 57829.2  -4.58   6.07
 5  │ 57830.1  17.71   3.56
 6  │ 57830.1  23.34   3.48
 7  │ 57830.2  21.34   3.51
 8  │ 57830.2  19.92   3.26
 9  │ 57830.2  -0.74   3.44
 10 │ 57830.3  16.9    3.5
 11 │ 57830.3  8.25    4.64
 12 │ 57830.4  -4.06   4.91
 13 │ 57832.1  -29.51  4.96
 14 │ 57832.1  -21.56  4.11
 15 │ 57832.2  -25.76  5.59
 16 │ 57833.1  2.1     3.91
 17 │ 57833.1  4.38    3.73
 ⋮  │    ⋮       ⋮      ⋮

Now, create a planet. We can use the RadialVelocityOrbit type from PlanetOrbits.jl that requires fewer parameters (eg no inclination or longitude of ascending node). We could instead use a Visual{KepOrbit} or similar if we wanted to include these parameters and visualize the orbit in the plane of the sky.

@planet b RadialVelocityOrbit begin
    e = 0
    ω = 0.0

    # To match RadVel, we set a prior on Period and calculate semi-major axis from it
    P ~ truncated(
        Normal(0.3693038/365.256360417, 0.0000091/365.256360417),
        lower=0.0001
    )
    a = cbrt(system.M * b.P^2) # note the equals sign.

    τ ~ UniformCircular(1.0)
    tp = b.τ*b.P*365.256360417 + 57782 # reference epoch for τ. Choose an MJD date near your data.

    # minimum planet mass [jupiter masses]. really m*sin(i)
    mass ~ LogUniform(0.001, 10)
end


@system k2_132 begin
    # total mass [solar masses]
    M ~ truncated(Normal(0.82, 0.02),lower=0.1) # (Baines & Armstrong 2011).

    rv0_harps ~ Normal(-6693,100) # m/s
    rv0_pfs ~ Normal(0,100) # m/s

    jitter_harps ~ LogUniform(0.1,100) # m/s
    jitter_pfs ~ LogUniform(0.1,100) # m/s

end rvlike_harps rvlike_pfs b
System model k2_132
Derived:
  
Priors:
  M	Truncated(Distributions.Normal{Float64}(μ=0.82, σ=0.02); lower=0.1)
  rv0_harps	Distributions.Normal{Float64}(μ=-6693.0, σ=100.0)
  rv0_pfs	Distributions.Normal{Float64}(μ=0.0, σ=100.0)
  jitter_harps	Distributions.LogUniform{Float64}(a=0.1, b=100.0)
  jitter_pfs	Distributions.LogUniform{Float64}(a=0.1, b=100.0)
Planet b
Derived:
  e, ω, a, τ, tp, 
Priors:
  P	Truncated(Distributions.Normal{Float64}(μ=0.001011081092683449, σ=2.4914008313533153e-8); lower=0.0001)
  τy	Distributions.Normal{Float64}(μ=0.0, σ=1.0)
  τx	Distributions.Normal{Float64}(μ=0.0, σ=1.0)
  mass	Distributions.LogUniform{Float64}(a=0.001, b=10.0)
Octofitter.UnitLengthPrior{:τy, :τx}: √(τy^2+τx^2) ~ LogNormal(log(1), 0.02)


StarAbsoluteRVLikelihood Table with 3 columns and 39 rows:
      epoch    rv        σ_rv
    ┌─────────────────────────
 1  │ 57782.2  -6682.78  3.71
 2  │ 57783.1  -6710.55  5.95
 3  │ 57783.2  -6698.96  8.76
 4  │ 57812.1  -6672.32  4.0
 5  │ 57812.2  -6672.0   4.22
 6  │ 57812.2  -6690.31  4.63
 7  │ 57813.0  -6701.27  4.12
 8  │ 57813.1  -6700.71  5.34
 9  │ 57813.1  -6695.97  3.67
 10 │ 57813.1  -6705.87  4.23
 11 │ 57813.1  -6694.27  5.12
 12 │ 57813.2  -6699.99  5.0
 13 │ 57813.2  -6696.17  10.43
 14 │ 57836.0  -6675.4   7.03
 15 │ 57836.0  -6665.92  7.48
 16 │ 57836.0  -6661.9   6.03
 17 │ 57836.1  -6657.92  5.08
 ⋮  │    ⋮        ⋮        ⋮StarAbsoluteRVLikelihood Table with 3 columns and 31 rows:
      epoch    rv      σ_rv
    ┌──────────────────────
 1  │ 57828.4  -45.17  3.88
 2  │ 57828.4  -36.47  4.03
 3  │ 57829.2  -20.52  3.55
 4  │ 57829.2  -4.58   6.07
 5  │ 57830.1  17.71   3.56
 6  │ 57830.1  23.34   3.48
 7  │ 57830.2  21.34   3.51
 8  │ 57830.2  19.92   3.26
 9  │ 57830.2  -0.74   3.44
 10 │ 57830.3  16.9    3.5
 11 │ 57830.3  8.25    4.64
 12 │ 57830.4  -4.06   4.91
 13 │ 57832.1  -29.51  4.96
 14 │ 57832.1  -21.56  4.11
 15 │ 57832.2  -25.76  5.59
 16 │ 57833.1  2.1     3.91
 17 │ 57833.1  4.38    3.73
 ⋮  │    ⋮       ⋮      ⋮

Note how the rvlike object was attached to the k2_132 system instead of the planet. This is because the observed radial velocity is of the star, and is caused by any/all orbiting planets.

The rv0 and jitter parameters specify priors for the instrument-specific offset and white noise jitter standard deviation. The _i index matches the inst_idx used to create the observation table.

Note also here that the mass variable is really msini, or the minimum mass of the planet.

We can now prepare our model for sampling.

model = Octofitter.LogDensityModel(k2_132)
LogDensityModel for System k2_132 of dimension 9 and 71 epochs with fields .ℓπcallback and .∇ℓπcallback

Sample:

using Random
rng = Random.Xoshiro(0)

chain = octofit(rng, model)
Chains MCMC chain (1000×27×1 Array{Float64, 3}):

Iterations        = 1:1:1000
Number of chains  = 1
Samples per chain = 1000
Wall duration     = 26.69 seconds
Compute duration  = 26.69 seconds
parameters        = M, rv0_harps, rv0_pfs, jitter_harps, jitter_pfs, b_P, b_τy, b_τx, b_mass, b_e, b_ω, b_a, b_τ, b_tp
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_tail      rh ⋯
        Symbol      Float64   Float64   Float64     Float64    Float64   Float ⋯

             M       0.8196    0.0196    0.0006   1030.3595   684.0823    0.99 ⋯
     rv0_harps   -6693.4783    2.5758    0.0973    726.9743   424.3015    0.99 ⋯
       rv0_pfs       1.6931    3.2297    0.1151    796.2978   644.1833    1.00 ⋯
  jitter_harps      13.8430    1.8935    0.0796    588.6520   495.5306    1.00 ⋯
    jitter_pfs      17.4532    2.5239    0.0995    643.5161   577.2825    1.00 ⋯
           b_P       0.0010    0.0000    0.0000   1038.2698   804.1324    1.00 ⋯
          b_τy       0.7228    0.1742    0.0062    805.8618   808.0348    1.00 ⋯
          b_τx      -0.6628    0.1821    0.0068    733.8291   579.6001    1.00 ⋯
        b_mass       0.0416    0.0085    0.0002   1142.5047   719.8360    1.00 ⋯
           b_e       0.0000    0.0000       NaN         NaN        NaN       N ⋯
           b_ω       0.0000    0.0000       NaN         NaN        NaN       N ⋯
           b_a       0.0094    0.0001    0.0000   1030.8953   684.0823    0.99 ⋯
           b_τ      -0.1181    0.0369    0.0014    702.1388   594.5669    1.00 ⋯
          b_tp   57781.9564    0.0136    0.0005    702.1487   594.5669    1.00 ⋯
                                                               2 columns omitted

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

             M       0.7826       0.8068       0.8193       0.8325       0.858 ⋯
     rv0_harps   -6698.7371   -6695.1180   -6693.4538   -6691.7607   -6688.148 ⋯
       rv0_pfs      -4.4870      -0.4591       1.7041       3.6608       8.055 ⋯
  jitter_harps      10.7424      12.4838      13.6777      14.9815      18.090 ⋯
    jitter_pfs      13.2987      15.6344      17.1543      18.8888      23.340 ⋯
           b_P       0.0010       0.0010       0.0010       0.0010       0.001 ⋯
          b_τy       0.3700       0.6074       0.7360       0.8511       1.029 ⋯
          b_τx      -0.9869      -0.7867      -0.6711      -0.5464      -0.254 ⋯
        b_mass       0.0259       0.0357       0.0414       0.0473       0.058 ⋯
           b_e       0.0000       0.0000       0.0000       0.0000       0.000 ⋯
           b_ω       0.0000       0.0000       0.0000       0.0000       0.000 ⋯
           b_a       0.0093       0.0094       0.0094       0.0095       0.009 ⋯
           b_τ      -0.1878      -0.1440      -0.1180      -0.0921      -0.042 ⋯
          b_tp   57781.9306   57781.9468   57781.9564   57781.9660   57781.984 ⋯
                                                                1 column omitted

Excellent! Let's plot an orbit sampled from the posterior:

using CairoMakie: Makie
fig = Octofitter.rvpostplot(model, chain) # saved to "k2_132-rvpostplot.png"
Example block output

We can also plot many number of samples from the posterior:

using CairoMakie: Makie
octoplot(model, chain)
Example block output

And create a corner plot:

using PairPlots
using CairoMakie: Makie
octocorner(model, chain)
Example block output

This example continues in Fit Gaussian Process.