Hipparcos Modelling

This tutorial explains how to model Hipparcos IAD data. The first example reproduces the catalog values of position, parallax, and proper motion. The second uses Hipparcos to constrain the mass of a directly imaged planet.

Reproduce Catalog Values

This is the so-called "Nielsen" test from Nielsen et al (2020) and available in Orbitize!.

We start by using a system with a planet with zero mass to fit the straight line motion.

using Octofitter
using Distributions
using CairoMakie

hip_like = Octofitter.HipparcosIADLikelihood(;
    hip_id=21547,
    renormalize=true, # default: true
)

@planet b AbsoluteVisual{KepOrbit} begin
    mass = 0.
    e = 0.
    ω = 0.
    a = 1.
    i = 0
    Ω = 0.
    tp = 0.
end
@system c_Eri_straight_line begin
    M = 1.0 # Host mass not important for this example
    rv = 0.0 # system RV not significant for this example
    plx ~ Uniform(0,100)
    pmra ~ Uniform(-100, 100)
    pmdec ~  Uniform(-100, 100)


    # It is convenient to put a prior of the catalog value +- 10,000 mas on position
    ra_hip_offset_mas ~  Normal(0, 10000)
    dec_hip_offset_mas ~ Normal(0, 10000)
    dec = $hip_like.hip_sol.dedeg + system.ra_hip_offset_mas/60/60/1000
    ra = $hip_like.hip_sol.radeg + system.dec_hip_offset_mas/60/60/1000/cosd(system.dec)

    ref_epoch = Octofitter.hipparcos_catalog_epoch_mjd

end hip_like b

model = Octofitter.LogDensityModel(c_Eri_straight_line)
LogDensityModel for System c_Eri_straight_line of dimension 5 with fields .ℓπcallback and .∇ℓπcallback

We can now sample from the model using Hamiltonian Monte Carlo. This should only take about 15 seconds.

# Typical depth is ~4, so limiting the max_depth down from default 12 speeds warm-up
chain = octofit(model, iterations=4_000, max_depth=6)
Chains MCMC chain (4000×30×1 Array{Float64, 3}):

Iterations        = 1:1:4000
Number of chains  = 1
Samples per chain = 4000
Wall duration     = 5.87 seconds
Compute duration  = 5.87 seconds
parameters        = plx, pmra, pmdec, ra_hip_offset_mas, dec_hip_offset_mas, M, rv, dec, ra, ref_epoch, b_mass, b_e, b_ω, b_a, b_i, 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  ⋯
              Symbol      Float64   Float64   Float64     Float64     Float64  ⋯

                 plx      33.9872    0.3383    0.0048   4885.6451   3056.9226  ⋯
                pmra      44.2135    0.3368    0.0050   4482.2881   3276.2329  ⋯
               pmdec     -64.3921    0.2739    0.0041   4374.9667   3311.7790  ⋯
   ra_hip_offset_mas      -0.0009    0.2016    0.0032   3960.6553   3091.6610  ⋯
  dec_hip_offset_mas      -0.0053    0.2909    0.0043   4584.8065   2948.7832  ⋯
                   M       1.0000    0.0000       NaN         NaN         NaN  ⋯
                  rv       0.0000    0.0000       NaN         NaN         NaN  ⋯
                 dec      -2.4734    0.0000    0.0000   3960.6443   3091.6610  ⋯
                  ra      69.4004    0.0000    0.0000   4584.8065   2948.7832  ⋯
           ref_epoch   48348.5625    0.0000       NaN         NaN         NaN  ⋯
              b_mass       0.0000    0.0000       NaN         NaN         NaN  ⋯
                 b_e       0.0000    0.0000       NaN         NaN         NaN  ⋯
                 b_ω       0.0000    0.0000       NaN         NaN         NaN  ⋯
                 b_a       1.0000    0.0000       NaN         NaN         NaN  ⋯
                 b_i       0.0000    0.0000       NaN         NaN         NaN  ⋯
                 b_Ω       0.0000    0.0000       NaN         NaN         NaN  ⋯
                b_tp       0.0000    0.0000       NaN         NaN         NaN  ⋯
                                                               2 columns omitted

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

                 plx      33.2964      33.7609      33.9909      34.2121       ⋯
                pmra      43.5613      43.9853      44.2145      44.4371       ⋯
               pmdec     -64.9221     -64.5771     -64.3945     -64.2052     - ⋯
   ra_hip_offset_mas      -0.4043      -0.1350      -0.0000       0.1353       ⋯
  dec_hip_offset_mas      -0.5870      -0.2030      -0.0043       0.1923       ⋯
                   M       1.0000       1.0000       1.0000       1.0000       ⋯
                  rv       0.0000       0.0000       0.0000       0.0000       ⋯
                 dec      -2.4734      -2.4734      -2.4734      -2.4734       ⋯
                  ra      69.4004      69.4004      69.4004      69.4004       ⋯
           ref_epoch   48348.5625   48348.5625   48348.5625   48348.5625   483 ⋯
              b_mass       0.0000       0.0000       0.0000       0.0000       ⋯
                 b_e       0.0000       0.0000       0.0000       0.0000       ⋯
                 b_ω       0.0000       0.0000       0.0000       0.0000       ⋯
                 b_a       1.0000       1.0000       1.0000       1.0000       ⋯
                 b_i       0.0000       0.0000       0.0000       0.0000       ⋯
                 b_Ω       0.0000       0.0000       0.0000       0.0000       ⋯
                b_tp       0.0000       0.0000       0.0000       0.0000       ⋯
                                                                1 column omitted

Plot the posterior values:

octoplot(model,chain,show_astrom=false,show_astrom_time=false)
Example block output

We now visualize the model fit compared to the Hipparcos catalog values:

using LinearAlgebra, StatsBase
fig = Figure(size=(1080,720))
j = i = 1
for prop in (
    (;chain=:ra, hip=:radeg, hip_err=:e_ra),
    (;chain=:dec, hip=:dedeg, hip_err=:e_de),
    (;chain=:plx, hip=:plx, hip_err=:e_plx),
    (;chain=:pmra, hip=:pm_ra, hip_err=:e_pmra),
    (;chain=:pmdec, hip=:pm_de, hip_err=:e_pmde)
)
    global i, j, ax
    ax = Axis(
        fig[j,i],
        xlabel=string(prop.chain),
    )
    i+=1
    if i > 3
        j+=1
        i = 1
    end
    unc = hip_like.hip_sol[prop.hip_err]
    if prop.chain == :ra
        unc /= 60*60*1000 * cosd(hip_like.hip_sol.dedeg)
    end
    if prop.chain == :dec
        unc /= 60*60*1000
    end
    if prop.hip == :zero
        n = Normal(0, unc)
    else
        mu = hip_like.hip_sol[prop.hip]
        n = Normal(mu, unc)
    end
    n0,n1=quantile.(n,(1e-4, 1-1e-4))
    nxs = range(n0,n1,length=200)
    h = fit(Histogram, chain[prop.chain][:], nbins=55)
    h = normalize(h, mode=:pdf)
    barplot!(ax, (h.edges[1][1:end-1] .+ h.edges[1][2:end])./2, h.weights, gap=0, color=:red, label="posterior")
    lines!(ax, nxs, pdf.(n,nxs), label="Hipparcos Catalog", color=:black, linewidth=2)
end
Legend(fig[i-1,j+1],ax,tellwidth=false)
fig
Example block output

Constrain Planet Mass

We now allow the planet to have a non zero mass and free orbit. We start by retrieving relative astrometry data on the planet, collated by Jason Wang and co on whereistheplanet.com.

astrom_like1,astrom_like2 = Octofitter.Whereistheplanet_astrom("51erib",object=1)
┌ Warning: Checksum not provided, add to the Datadep Registration the following hash line
│   hash = "344043fa845f1a096f0a67f32bce9eeb8c94b1ae210a6b91a7fddfe0602c30ec"
└ @ DataDeps ~/.julia/packages/DataDeps/Y2lje/src/verification.jl:44

7-Zip (a) [64] 17.04 : Copyright (c) 1999-2021 Igor Pavlov : 2017-08-28
p7zip Version 17.04 (locale=C.UTF-8,Utf16=on,HugeFiles=on,64 bits,4 CPUs AMD EPYC 7763 64-Core Processor                 (A00F11),ASM,AES-NI)

Scanning the drive for archives:
1 file, 11870993 bytes (12 MiB)

Extracting archive: /home/runner/.julia/scratchspaces/124859b0-ceae-595e-8997-d05f6a7a8dfe/datadeps/Whereistheplanet/whereistheplanet-master.zip
--
Path = /home/runner/.julia/scratchspaces/124859b0-ceae-595e-8997-d05f6a7a8dfe/datadeps/Whereistheplanet/whereistheplanet-master.zip
Type = zip
Physical Size = 11870993
Comment = d393657c7b6224ed832209e1218a4cd00c6ba0ec

Everything is Ok

Folders: 8
Files: 99
Size:       15005637
Compressed: 11870993

We specify our full model:

@planet b AbsoluteVisual{KepOrbit} begin
    a ~ truncated(Normal(10,1),lower=0)
    e ~ Uniform(0,0.99)
    ω ~ UniformCircular()
    i ~ Sine()
    Ω ~ UniformCircular()
    θ ~ UniformCircular()
    tp = θ_at_epoch_to_tperi(system,b,57463)
    mass = system.M_sec
end astrom_like1

@system cEri begin
    M_pri ~ truncated(Normal(1.75,0.05), lower=0.03) # Msol
    M_sec ~ Uniform(0, 100) # MJup
    M = system.M_pri + system.M_sec*Octofitter.mjup2msol # Msol

    rv =  12.60e3 # m/s
    plx ~ Uniform(0,100)
    pmra ~ Uniform(-100, 100)
    pmdec ~  Uniform(-100, 100)

    # It is convenient to put a prior of the catalog value +- 1000 mas on position
    ra_hip_offset_mas ~  Normal(0, 10000)
    dec_hip_offset_mas ~ Normal(0, 10000)
    dec = $hip_like.hip_sol.dedeg + system.ra_hip_offset_mas/60/60/1000
    ra = $hip_like.hip_sol.radeg + system.dec_hip_offset_mas/60/60/1000/cos(system.dec)

    ref_epoch = Octofitter.hipparcos_catalog_epoch_mjd
end hip_like b

model = Octofitter.LogDensityModel(cEri)
LogDensityModel for System cEri of dimension 16 with fields .ℓπcallback and .∇ℓπcallback

Now we sample:

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

Iterations        = 1:1:1000
Number of chains  = 1
Samples per chain = 1000
Wall duration     = 14.98 seconds
Compute duration  = 14.98 seconds
parameters        = M_pri, M_sec, plx, pmra, pmdec, ra_hip_offset_mas, dec_hip_offset_mas, M, rv, dec, ra, ref_epoch, b_a, b_e, b_ωy, b_ωx, b_i, b_Ωy, b_Ωx, b_θy, b_θx, b_ω, b_Ω, b_θ, b_tp, b_mass
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    ⋯
              Symbol      Float64   Float64   Float64    Float64    Float64    ⋯

               M_pri       0.0300    0.0000    0.0000     2.7478        NaN    ⋯
               M_sec     100.0000    0.0000    0.0000     2.4460    12.2549    ⋯
                 plx      99.8193    0.0063    0.0034     2.3835    12.2549    ⋯
                pmra     -76.8476    0.8557    0.4492     2.3837    12.2549    ⋯
               pmdec     -86.4913    0.6983    0.3647     2.3837    12.2549    ⋯
   ra_hip_offset_mas    -203.9307    0.0011    0.0006     2.3975    12.2549    ⋯
  dec_hip_offset_mas    4871.0452    0.0053    0.0028     2.3836    12.0444    ⋯
                   M       0.1255    0.0000    0.0000     2.4430    11.9953    ⋯
                  rv   12600.0000    0.0000       NaN        NaN        NaN    ⋯
                 dec      -2.4734    0.0000    0.0000     2.3975    12.2549    ⋯
                  ra      69.3987    0.0000    0.0000     2.3836    12.2549    ⋯
           ref_epoch   48348.5625    0.0000       NaN        NaN        NaN    ⋯
                 b_a     477.8678   11.1742    5.3018     2.4035    12.2555    ⋯
                 b_e       0.9851    0.0005    0.0003     2.3835    12.2549    ⋯
                b_ωy       2.3556    0.0298    0.0155     2.3845    12.2549    ⋯
                b_ωx      -0.9565    0.0185    0.0093     2.4412    12.2549    ⋯
                 b_i       1.5198    0.0040    0.0021     2.4453    12.2549    ⋯
          ⋮                ⋮           ⋮         ⋮         ⋮          ⋮        ⋱
                                                    2 columns and 9 rows omitted

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

               M_pri       0.0300       0.0300       0.0300       0.0300       ⋯
               M_sec     100.0000     100.0000     100.0000     100.0000     1 ⋯
                 plx      99.8032      99.8208      99.8222      99.8228       ⋯
                pmra     -77.3292     -77.2923     -77.2220     -77.0826     - ⋯
               pmdec     -86.8785     -86.8511     -86.7953     -86.6866     - ⋯
   ra_hip_offset_mas    -203.9339    -203.9304    -203.9302    -203.9301    -2 ⋯
  dec_hip_offset_mas    4871.0421    4871.0424    4871.0429    4871.0438    48 ⋯
                   M       0.1255       0.1255       0.1255       0.1255       ⋯
                  rv   12600.0000   12600.0000   12600.0000   12600.0000   126 ⋯
                 dec      -2.4734      -2.4734      -2.4734      -2.4734       ⋯
                  ra      69.3987      69.3987      69.3987      69.3987       ⋯
           ref_epoch   48348.5625   48348.5625   48348.5625   48348.5625   483 ⋯
                 b_a     472.8102     473.0719     473.2738     473.5860     5 ⋯
                 b_e       0.9836       0.9853       0.9854       0.9854       ⋯
                b_ωy       2.2701       2.3639       2.3684       2.3709       ⋯
                b_ωx      -1.0115      -0.9509      -0.9488      -0.9475       ⋯
                 b_i       1.5082       1.5208       1.5215       1.5219       ⋯
          ⋮                ⋮            ⋮            ⋮            ⋮            ⋱
                                                     1 column and 9 rows omitted
octoplot(model, chain, show_mass=true)
Example block output

We see that we constrained both the orbit and the parallax. The mass is not strongly constrained by Hipparcos.