Julia言語の常微分方程式ソルバーを使って円制限三体問題の軌道計算をしてみる 〜軌道伝播・境界値問題編〜
1. はじめに
本記事では,宇宙軌道力学分野でよく登場する円制限三体問題を題材にして,常微分方程式のソルバーの使い方を紹介する.Julia言語が最も活躍するシチュエーションは「常微分方程式 (Ordinary Differential Equation; ODE) を解く」というような数値計算である.プログラミング言語ごとの常微分方程式ソルバーを比較したPDFを見てもらうと,Julia言語がいかに強力か感じ取れるだろう.
Julia言語を用いて,常微分方程式の数値解法を行う際に,最も広く利用されているライブラリはDifferentialEquations.jl
である.その他にもOrdinaryDiffEq.jl
,ODE.jl
,Sundials.jl
等のライブラリがある.ODE.jl
はDifferentialEquations.jl
等に取って代わられた旧式のライブラリであり,本記事を投稿した時点では,使用することが非推奨とされている.また,OrdinaryDiffEq.jl
およびSundials.jl
は,DifferentialEquations.jl
の中に組み込まれている(一部の機能を利用するためには,個別にパッケージをインストールする必要がありそう)ため,DifferentialEquations.jl
が最も一般的なライブラリとなっている.以下では,DifferentialEquations.jl
を利用した解析を行う.DifferentailEquations.jl
のライブラリはMITライセンスなので,ライセンスを気にせず,かなり緩く使えそうである.
Juliaでの軌道の描画には,Makie.jl
を用いる.描画ライブラリで何を使うかは個人の好みだが,Makie.jl
は,Plots.jl
のように扱いやすく,PlotlyJS.jl
のようにインタラクティブに動かせ,かつ,美しい描画ができるので,個人的なお気に入りである.
本記事で紹介するJulia言語と比較用のPythonスクリプトは,以下のGithubリポジトリのworkディレクトリ内に掲載している.
Juliaプロジェクト化しているため,以下の記事を参考に,Githubからクローン後にCRTBPのプロジェクト環境を読み込めば,必要なパッケージを一括でセットアップすることができる.
2. 円制限三体問題(CRTBP)の運動方程式
本記事では,宇宙軌道力学分野でよく登場する円制限三体問題の運動方程式を扱う.ここでは,円制限三体問題に関する詳しい説明は割愛するが,Juliaの常微分方程式ライブラリにのみ興味のある読者は本章は軽く読み飛ばしても構わない.
お互いに万有引力が作用する3つの天体がどのような運動をするのかを問う問題を三体問題(Three-Body Problem) と呼ぶ.三体問題は,その軌道を与える一般解が求積法では求まらない問題として知られており(=要は,ある時刻に対する位置・速度を解析的な式で計算できない),宇宙軌道力学の分野でも数値解法が用いられる.三体問題の中でも,第三天体が及ぼす万有引力を無視した問題を制限三体問題 (Restricted Three-Body Problem) と呼び,更に,第一天体と第二天体がその共通重心の周りを円軌道で周回する問題を円制限三体問題 (Circular Restricted Three-Body Problem; CRTBP) と呼ぶ.宇宙軌道力学分野で対象となる「太陽=地球=宇宙機の三体問題」や「地球=月=宇宙機の三体問題」は,CRTBPで非常によく近似できる.よく近似できるとは言え,あくまで近似であり,実際の宇宙機運用を考えると無視できない誤差が生じる.それにも関わらず,なぜ軌道力学の専門家が,CRTBPを使いたがるかと言うと,CRTBPの運動方程式は,自励系(=独立変数を陽に含まない常微分方程式)となっているからである.自励系で表されることで,力学系理論等の解析的なテクニックを利用することが可能となる.
CRTBPで用いる座標系
CRTBPでは,第一天体と第二天体の共通重心を中心として,第一天体から見た第二天体の位置ベクトル方向を
ただし,
である.
3. 円制限三体問題の軌道伝播
まず円制限三体問題の運動方程式をJuliaの関数として記述しよう.Julia言語の配列は1オリジン(1-based numbering) な定義になっている.MATLABやFORTRANユーザーは1オリジンの書き方に慣れていると思うが,PythonやC言語ユーザーは0オリジン(zero-based numbering) の書き方に慣れているため,注意が必要である.また,Julia言語では,ParameterizedFunctions.jl
を使って,分かりやすく記述する方法もあるようだが,計算速度の観点では不利に働くため,ここではParameterizedFunctions
を利用しない方法を紹介する.
using LinearAlgebra
# Equation of Motion of CRTBP
function eom_crtbp!(dx,x,μ,t) #関数内で引数`dx`を変更するので,慣例にならって`!`を関数名の後ろに着けている
# Preparation
r1 = x[1:3] + [μ,0,0]
r2 = x[1:3] - [1-μ,0,0]
a_cori = [2x[5],-2x[4],0]
a_cf = [x[1],x[2],0]
# dx/dt
dx[1:3] = x[4:6]
dx[4:6] = -(1-μ)*r1/(norm(r1)^3) - μ*r2/(norm(r2)^3) + a_cori + a_cf
end
早速,DifferentialEquations.jl
で,月軌道ゲートウェイが建設される予定の地球・月系のNear-Rectilinear Halo Orbit(NRHO)の初期条件を与えて,軌道伝播をしてみよう.ここでは,Pythonコードと比較するために,DP8()
という手法を用いて,常微分方程式を解いている.公式ドキュメント曰く,同程度の精度を目指す場合はVern7()
の方が性能が良いらしい.
using DifferentialEquations
using GLMakie
Makie.inline!(true)
# 問題定義 (地球・月系のNear-Rectilinear Halo Orbitを与える初期条件)
x0 = [0.987384153663276, 0.0, 0.008372273063008, 0.0, 1.67419265037912, 0.0]
tspan = (0.0, 0.5*pi)
μ = 0.01215058426994 # 地球・月系のμ
prob = ODEProblem(eom_crtbp!, x0, tspan, μ)
# 計算
@time sol = solve(prob, DP8(), reltol=1e-10, abstol=1e-10)
# 描画
fig = Figure()
ax = Axis3(fig[1, 1]; aspect=:data, perspectiveness=0.0, title="CRTBP Example 1 (NRHO)", xlabel=L"x", ylabel=L"y", zlabel=L"z")
scatterlines!(ax, 1 - μ, 0, 0; markersize=20, color=:blue)
lines!(ax, sol[1, :], sol[2, :], sol[3, :], linewidth=1, color=:red)
fig
Near-Rectilinear Halo Orbitの軌道伝播結果
本プログラムの実行時間は5.030ms(ミリ秒)であった(初回は時間が掛かるので注意).同様の計算をPythonのscipy.integrate.solve_ivp
の'DOP853'
を用いて計算してみた結果,その実行時間は32msとなった.Julia言語の方がPythonよりも,6倍ほど計算時間が早いことが分かる.上記の例では,@time
で時刻計測を行なっているが,CPU時間で評価する@CPUtime
でも同程度の差がある.
DifferentialEquations.jl
の拡張機能 〜境界値問題の解法〜
4. DifferentialEquations.jl
には,便利な拡張機能がある.その1つの(常微分方程式の)境界値問題(Boundary Value Problem)のソルバーを用いれば,ある境界条件を満たすような初期条件を計算することが可能である.宇宙軌道力学の分野でも,ランベール問題(Lambert's Problem)をはじめとして,二点境界値問題(Two-Point Boundary Value Problem; TPBVP) がよく登場する.この拡張機能を利用するためには,
(CRTBP) pkg> add BoundaryValueDiffEq
によって,BoundaryValueDiffEq.jl
パッケージを導入する必要がある.本記事を執筆した段階では,BoundaryValueDiffEq
は軌道伝播時間(tspan
)を可変にした境界値問題を解くことができない.個人的にはBoundaryValueDiffEq
を使わずに自分でシューティング法を実装するアプローチが好みだが,そのアプローチはここでは紹介しない(必要があれば別の記事にまとめようと思う).
「軌道伝播時間を可変にした境界値問題を解くことができない」と言ったが,ちょっとしたテクニックを使えば,BoundaryValueDiffEq
でも軌道伝播時間を可変にした問題を解くことが可能となる.例えば,
さて,この境界値問題のソルバーを用いて,ハロー軌道 (Halo Orbit) というCRTBPの周期軌道を生成してみよう
なお,
を追加することを考える.他にも
では,BoundaryValueDiffEq
ライブラリのシューティング法(Shooting()
)を用いて,ハロー軌道を生成してみよう.BoundaryValueDiffEq
ライブラリのBVProblem
という構造体で問題を定義して,solve()
関数で問題を解く流れで問題が解かれる.
bvp = BVProblem(f,bc!,u0,tspan,p=NullParameters();kwargs...)
ここで,f(du,u,p,t)
はODEの関数(今回の場合はCRTBPの運動方程式eom_crtbp_w_time!(dx,x,p,t)
),bc!(residual, u, p, t)
は境界条件を規定する関数,u0
は初期予想値,tspan
はODEの計算時間,p
はf(du,u,p,t)
やbc!(residual, u, p, t)
に渡すパラメータである.bc!(residual, u, p, t)
は,u, p, t
を入力としてredisual
の配列がゼロとなるように境界条件である.
あとはsol = solve(bvp, Shooting(Vern7()))
と実行すれば,境界値問題を解いてくれる.solve()
関数の第一引数は境界値問題の構造体bvp
で,第二引数は境界値問題のソルバーである.3種類くらいしかないが,ここではODEソルバーとしてVern7()
を使ったシューティング法Shooting(Vern7()))
を使う.
実際にハロー軌道を生成するコードを以下に示す.
using BoundaryValueDiffEq
using DifferentialEquations
using LinearAlgebra
using GLMakie
GLMakie.activate!()
Makie.inline!(true)
# CRTBPの運動方程式の定義(第7変数に時刻のスケーリングを追加することで可変時刻問題に対応できるように変更)
function eom_crtbp_w_time!(dx,x,p,t)
# Preparation
μ = p[1]
r1 = x[1:3] + [μ,0,0]
r2 = x[1:3] - [1-μ,0,0]
a_cori = [2x[5],-2x[4],0]
a_cf = [x[1],x[2],0]
# dx/dt
dx[1:3] = x[7]*(x[4:6])
dx[4:6] = x[7]*(-(1-μ)*r1/(norm(r1)^3) - μ*r2/(norm(r2)^3) + a_cori + a_cf)
dx[7] = 0 # 第7変数に時刻のスケーリング値を追加
end
# z0を固定した上での垂直交差(Perpendicular Crossing)の境界条件
function bc!(residual, x, p, t) # u[1] is the beginning of the time span, and u[end] is the ending
# 初期時刻における垂直交差の境界条件
residual[1] = x[1][2]
residual[2] = x[1][4]
residual[3] = x[1][6]
# 終端時刻における垂直交差の境界条件
residual[4] = x[end][2]
residual[5] = x[end][4]
residual[6] = x[end][6]
# ハロー軌道を1つに特定するためにz0固定という条件を追加(x0固定,ヤコビ定数固定,遷移時間固定等に変えても構わない)
residual[7] = x[1][3] - p[2]
end
# 初期条件
x0 = [0.987384153663276, 0.0, 0.008372273063008, 0.0, 1.67419265037912, 0.0, 1.0] # 初期予想
tspan = (0.0, 0.25 * pi)
μ = 0.01215058426994 # 地球・月系のμ
# BoundaryValueDiffEqのライブラリを用いて境界値問題を解く
z0 = 0.07
bvp = BVProblem(eom_crtbp_w_time!, bc!, x0, tspan, [μ,z0])
sol = solve(bvp, Shooting(Vern7()), abstol=1e-10, reltol=1e-10)
# 描画を目的とした軌道逆伝播
tspan_neg = (0.0, -0.25 * pi)
prob = ODEProblem(eom_crtbp_w_time!, sol[:,1], tspan_neg, μ)
sol_neg = solve(prob, DP8(), reltol=1e-10, abstol=1e-10)
# 描画
fig = Figure(; resolution=(1024, 1024))
ax = Axis3(fig[1, 1]; aspect=:data, perspectiveness=0.0, title="Example of Halo Orbit", xlabel=L"x", ylabel=L"y", zlabel=L"z")
scatterlines!(ax, 1 - μ, 0, 0; markersize=20, color=:blue)
lines!(ax, sol[1, :], sol[2, :], sol[3, :], linewidth=1, color=:red)
lines!(ax, sol_neg[1, :], sol_neg[2, :], sol_neg[3, :], linewidth=1, color=:blue)
fig
境界値問題を用いたハロー軌道の計算結果
5. まとめ
本記事では,宇宙軌道力学分野で用いられる円制限三体問題を題材として,常微分方程式の数値解析ライブラリDifferentialEquations.jl
の使い方を紹介をした.また,DifferentialEquations.jl
の拡張機能の1つである境界値問題ライブラリBoundaryValueDiffEq.jl
の使い方に関しても紹介し,ハロー軌道の生成を行った.DifferentialEquations.jl
には,その他にも便利な拡張機能として,軌道伝播の途中で"ある条件"を満たした時に"ある処理"を実行するイベント処理(Event Handler) 機能があり,以下の記事で紹介しているため,参照いただきたい.
参考
- Kathleen Connor Howell, "Three-dimensional, Periodic, ‘Halo’ Orbits." Celestial Mechanics, Vol.32, pp.53–71, 1984. https://doi.org/10.1007/BF01358403
Discussion