Intro to Analyzing RV Timeseries
Astro 589: Week 4
Circular RV Model
Velocity of Planet:
$$v_{pl} = 2\pi a_{pl} / P$$
$$a = a_{pl} + a_\star$$
Velocity of Star:
$$M_\star v_\star = m_{pl} v_{pl}$$
$$v_\star = \frac{m_{pl}}{M_\star+m_{pl}} \frac{2\pi a}{P}$$
Doppler effective is only sensitive to motion projected onto observer's line of sight
$$RV_{\star} = \frac{m_{pl}}{M_\star+m_{pl}} \frac{2\pi a}{P} \sin i$$
Keplerian RV Model
$$\Delta RV(t) = \frac{K}{\sqrt{1-e^2}} \left[\cos(\omega+ν(t)) + e \cos(\omega) \right]$$
$$\begin{eqnarray} K & = & \frac{2\pi a \sin i}{P} \frac{m_{pl}}{M_\star+m_{pl}} \\ % & = & \frac{2\pi a \sin i}{P} \\ \end{eqnarray}$$
$$\tan\left(\frac{\nu(t)}{2}\right) = \sqrt{\frac{1+e}{1-e}} \tan\left(\frac{E(t)}{2}\right)$$
Mean anomaly (\(M(t)\)) increases linear with time
Eccentric anomaly (\(E(t)\)) specifies position in orbit using angle from center of elipse
True anomaly (\(\nu(t)\) or \(f(t)\) or \(T(t)\)) specifies position in orbit using angle from focus of ellipse
Credit: CheCheDaWaff via Wikipedia (CC BY-SA 4.0 license)
Keplerian Orbit in Time
$$M(t) = \frac{2π t}{P} + M_0$$
Kepler Equation
$$M(t) = E(t) - e \sin(E(t))$$
No closed form solution for \(E\), so solve for \(E(t)\) iteratively (see code for calc_ecc_anom(M, e)
make_rv_vs_phase_panel (generic function with 1 method)
Interactive Keplerian RV Model
P:
Example RV Data
# Examples to show: 114783, 119850, 13931, 187123
Star ID to plot: HD
Fitting RV Model to data
Fit one planet
Fit 1-planet model:
θinit1 = [496.9, 10, 0.1, 0.1, 0.1, 1, 2, 1e-4, 2.0] # for 114783
9-element Vector{Float64}:
496.9
10.0
0.1
0.1
0.1
1.0
2.0
0.0001
2.0
Fit 2nd planet to residuals
Fit 2nd planet to residuals:
θinit_resid = [4319.0, 5.0, 0.1, 0.1, π/4, 1.0, -1.0 , 0.0001, 1.0]; # for 114783
Fit 2-planet model
Fit 2nd planet model:
Setup
begin
using CSV, DataFrames, Query
using Optim
using Plots, LaTeXStrings, Plots.Measures
using PlutoUI, PlutoTeachingTools
using Downloads
using ParameterHandling
default(size=(1200,800))
end
Keplerian Radial Velocity Model Code
begin
""" Calculate RV from t, P, K, e, ω and M0 """
function calc_rv_keplerian end
#calc_rv(t, p::Vector) = calc_rv(t, p[1],p[2],p[3],p[4],p[5])
calc_rv_keplerian(t, p::Vector) = calc_rv_keplerian(t, p...)
function calc_rv_keplerian(t, P,K,e,ω,M0)
mean_anom = t*2π/P-M0
ecc_anom = calc_ecc_anom(mean_anom,e)
true_anom = calc_true_anom(ecc_anom,e)
rv = K/sqrt((1-e)*(1+e))*(cos(ω+true_anom)+e*cos(ω))
end
end
calc_rv_keplerian (generic function with 2 methods)
function calc_true_anom(ecc_anom::Real, e::Real)
true_anom = 2*atan(sqrt((1+e)/(1-e))*tan(ecc_anom/2))
end
calc_true_anom (generic function with 1 method)
begin
calc_ecc_anom_cell_id = PlutoRunner.currently_running_cell_id[] |> string
calc_ecc_anom_url = "#$(calc_ecc_anom_cell_id)"
"""
calc_ecc_anom( mean_anomaly, eccentricity )
calc_ecc_anom( param::Vector )
Estimates eccentric anomaly for given 'mean_anomaly' and 'eccentricity'.
If passed a parameter vector, param[1] = mean_anomaly and param[2] = eccentricity.
Optional parameter `tol` specifies tolerance (default 1e-8)
"""
function calc_ecc_anom end
function calc_ecc_anom(mean_anom::Real, ecc::Real; tol::Real = 1.0e-8)
if !(0 <= ecc <= 1.0)
println("mean_anom = ",mean_anom," ecc = ",ecc)
end
@assert 0 <= ecc <= 1.0
@assert 1e-16 <= tol < 1
M = rem2pi(mean_anom,RoundNearest)
E = ecc_anom_init_guess_danby(M,ecc)
local E_old
max_its_laguerre = 200
for i in 1:max_its_laguerre
E_old = E
E = update_ecc_anom_laguerre(E_old, M, ecc)
if abs(E-E_old) < tol break end
end
return E
end
function calc_ecc_anom(param::Vector; tol::Real = 1.0e-8)
@assert length(param) == 2
calc_ecc_anom(param[1], param[2], tol=tol)
end;
end
calc_ecc_anom (generic function with 2 methods)
"""
ecc_anom_init_guess_danby(mean_anomaly, eccentricity)
Returns initial guess for the eccentric anomaly for use by itterative solvers of Kepler's equation for bound orbits.
Based on "The Solution of Kepler's Equations - Part Three"
Danby, J. M. A. (1987) Journal: Celestial Mechanics, Volume 40, Issue 3-4, pp. 303-312 (1987CeMec..40..303D)
"""
function ecc_anom_init_guess_danby(M::Real, ecc::Real)
@assert -2π<= M <= 2π
@assert 0 <= ecc <= 1.0
if M < zero(M)
M += 2π
end
E = (M<π) ? M + 0.85*ecc : M - 0.85*ecc
end;
"""
update_ecc_anom_laguerre(eccentric_anomaly_guess, mean_anomaly, eccentricity)
Update the current guess for solution to Kepler's equation
Based on "An Improved Algorithm due to Laguerre for the Solution of Kepler's Equation"
Conway, B. A. (1986) Celestial Mechanics, Volume 39, Issue 2, pp.199-211 (1986CeMec..39..199C)
"""
function update_ecc_anom_laguerre(E::Real, M::Real, ecc::Real)
#es = ecc*sin(E)
#ec = ecc*cos(E)
(es, ec) = ecc .* sincos(E) # Does combining them provide any speed benefit?
F = (E-es)-M
Fp = one(M)-ec
Fpp = es
n = 5
root = sqrt(abs((n-1)*((n-1)*Fp*Fp-n*F*Fpp)))
denom = Fp>zero(E) ? Fp+root : Fp-root
return E-n*F/denom
end;
Fitting Keplerian RV model
begin
""" Calculate RV from t, P, K, e, ω, M0 and C"""
function calc_rv_keplerian_plus_const end
calc_rv_keplerian_plus_const(t, p::Vector) = calc_rv_keplerian_plus_const(t, p...)
function calc_rv_keplerian_plus_const(t, P,K,e,ω,M0,C)
calc_rv_keplerian(t, P,K,e,ω,M0) + C
end
end
calc_rv_keplerian_plus_const (generic function with 2 methods)
begin
""" Calculate RV from t, P1, K1, e1, ω1, M01, P2, K2, e2, ω2, M02 and C"""
calc_rv_2keplerians_plus_const(t, p::Vector) = calc_rv_2keplerians_plus_const(t, p...)
function calc_rv_2keplerians_plus_const(t, P1,K1,e1,ω1,M01,P2,K2,e2,ω2,M02,C)
calc_rv_keplerian(t, P1,K1,e1,ω1,M01) + calc_rv_keplerian(t, P2,K2,e2,ω2,M02) + C
end
end
calc_rv_2keplerians_plus_const (generic function with 2 methods)
""" Calculate RV from t, P, K, e, ω, M0 and C with optional slope and t_mean"""
function model_1pl(t, P, K, e, ω, M, C; slope=0.0, t_mean = 0.0)
calc_rv_keplerian(t-t_mean,P,K,e,ω,M) + C + slope * (t-t_mean)
end
""" Calculate RV from t, P1, K1, e1, ω1, M01, P2, K2, e2, ω2, M02 and C with optional slope and t_mean"""
function model_2pl(t, P1, K1, e1, ω1, M1, P2, K2, e2, ω2, M2, C; slope=0.0, t_mean = 0.0)
rv = calc_rv_keplerian(t-t_mean,P1,K1,e1,ω1,M1) +
calc_rv_keplerian(t-t_mean,P2,K2,e2,ω2,M2) +
C + slope * (t-t_mean)
end
""" Convert vector of (P,K,h,k,ω+M0) to vector of (P, K, e, ω, M0) """
function PKhkωpM_to_PKeωM(x::Vector)
(P, K, h, k, ωpM) = x
ω = atan(h,k)
return [P, K, sqrt(h^2+k^2), ω, ωpM-ω]
end
Loss functions
The functions below:
assume observations from exactly two instruments.
make use of the global variables
data1,data2andt_mean.
loss_1pl (generic function with 1 method)
loss_1pl_resid (generic function with 1 method)
loss_2pl (generic function with 1 method)
Ingest data
Read RVs for 719 stars from California Legacy Survey from https://github.com/leerosenthalj/CLSI & from Rosenthal et al. (2021) into df_all.
Code for parsing machine readable tables from AAS Journals
(Not actually used here, since found CSV version of files at https://github.com/leerosenthalj/CLSI. But potentially useful for student projects.)
read_apj_mrt (generic function with 1 method)
extract_entry (generic function with 1 method)
Built with Julia 1.11.1 and
CSV 0.10.15DataFrames 1.3.6
Downloads 1.6.0
LaTeXStrings 1.3.1
Optim 1.7.8
ParameterHandling 0.4.10
Plots 1.26.1
PlutoTeachingTools 0.1.7
PlutoUI 0.7.60
Query 1.0.0
To run this notebook locally, download this file and open it with Pluto.jl.