上級 · 三体の軌道

レッスン8:力と積分

Pythonで 「三体の軌道」 を作りながら、次のことを学びます:

  • ベクトルで表すニュートンの重力
  • 天体の組を回す入れ子のループ
  • 積分のリープフロッグ法
  • 3次元配列:ステップ × 天体 × 座標
  • 変わってはいけないもので確かめるシミュレーション

まず見てみる

クリックするとYouTubeから再生されます。

three-body-orbits.py
"""Three bodies: the figure-eight orbit, from the starting values computed by Carles Simo
(in Chenciner and Montgomery, 2000). G = 1, three masses of 1.
Run: python three-body-orbits.py   (needs: pip install numpy matplotlib)
"""
import numpy as np
import matplotlib.pyplot as plt

plt.style.use("dark_background")

x1 = np.array([0.97000436, -0.24308753])
V = np.array([-0.93240737, -0.86473146])  # the velocity of body 3
pos = np.array([x1, -x1, [0.0, 0.0]])
vel = np.array([-V / 2, -V / 2, V])
period, dt = 6.32591398, 0.0005
turn = 0  # degrees to turn the first velocity by (the film breaks the eight with 15)

a = np.radians(turn)
vel[0] = [vel[0, 0] * np.cos(a) - vel[0, 1] * np.sin(a), vel[0, 0] * np.sin(a) + vel[0, 1] * np.cos(a)]


def pull(p):
    """Each body's acceleration: the sum of the other two's gravity, G*m / r**2 towards each"""
    acc = np.zeros_like(p)
    for i in range(3):
        for j in range(3):
            if i != j:
                d = p[j] - p[i]
                acc[i] += d / np.linalg.norm(d) ** 3
    return acc


track = []
acc = pull(pos)
for _ in range(int(3 * period / dt)):  # three periods, leapfrog steps (they keep the energy well)
    vel += acc * dt / 2
    pos += vel * dt
    acc = pull(pos)
    vel += acc * dt / 2
    track.append(pos.copy())
track = np.array(track)

colours = ["#5eead4", "#f0abfc", "#f5c76b"]
plt.figure(figsize=(7, 4))
for body in range(3):  # all three run along the same track, a third of a period apart
    plt.plot(track[:, body, 0], track[:, body, 1], color=colours[body], linewidth=1)
plt.scatter(track[-1, :, 0], track[-1, :, 1], c=colours, s=80, zorder=3)
plt.axis("equal")
plt.axis("off")
plt.show()

# Try: turn = 15 -> the three tracks no longer close; the eight falls apart

このコンピューターにPythonが入っていませんか?ノートブックなら、同じスクリプト、練習問題、答えを、ブラウザ上のGoogle Colabで実行できます。ノートブックは英語です。

1行ずつ

  1. def pull(p):

    ほかの2つの天体の重力による、各天体の加速度です。

  2. acc[i] += d / np.linalg.norm(d) ** 3

    重力 G·m / r² に向き d / r を合わせると d / r³ になります。ここでは G と質量は1です。

  3. vel += acc * dt / 2

    リープフロッグ法:速度を半ステップ、位置を1ステップ進め、それから新しい力で残りの半ステップを進めます。

  4. pos += vel * dt

    位置は、半分だけ更新した速度で動きます。

  5. track = np.array(track)

    すべてのステップをひとつの配列に:track[ステップ, 天体, x または y]。

  6. plt.scatter(track[-1, :, 0], track[-1, :, 1], c=colours, s=80, zorder=3)

    3つの天体が最後にいる位置。ひとつの共通の軌跡の上に、周期の3分の1ずつ離れて並んでいます。

やってみよう

  1. 最後に、重心はどこにありますか?

    ヒント

    track[-1].mean(axis=0)。

    答え

    約10⁻¹³の誤差で (0, 0) です。全運動量がゼロなので、重心はけっして動きません。

  2. 動画と同じように turn = 15 にしてみましょう。何が起こり、今度は重心はどこへ行きますか?

    ヒント

    出発時と、1周期後の位置を比べます。

    答え

    軌跡はもう閉じません。1周期後、3つの天体は合わせて、出発した位置から約2単位離れています。速度のひとつを回すと系に運動量も加わるので、重心は流れていき、最後には約 (−0.81, 0.67) に来ます。

  3. dt を10倍の0.005にしてみましょう。8の字はまだ閉じますか?

    ヒント

    1周期後に、天体が出発点からどれだけ離れているかを測ります。

    答え

    閉じますが、正確さは下がります。1周期後、3つは合わせて出発点から約0.006離れています。dt = 0.0005 なら約0.0001です。

クイズ

1. d / |d|² ではなく d / |d|³ なのはなぜですか?
2. リープフロッグ法が、1回のオイラー法のステップよりよいのはなぜですか?
3. track[-1, :, 0] は何ですか?
4. 8の字で重心が動かないのはなぜですか?
5. 8の字が存在することを証明したのは誰ですか?

フラッシュカード

表を読み、裏に何があるかを自分で言ってみてから、カードを開いてください。

  • 天体iにかかる重力

    ほかの天体jについての Σ G·mⱼ·(rⱼ − rᵢ) / |rⱼ − rᵢ|³。

  • リープフロッグ法

    速度を半ステップ、位置を1ステップ、さらに速度を半ステップ。

  • 周期軌道

    1周期ののちに、ちょうど自分自身に戻ってくる出発点。

  • 重心

    質量で重みをつけた位置の平均。全運動量がゼロでないときにだけ動きます。

  • カオス

    出発時のごく小さな変化が育って、やがて運動がまったく別物になること。

このフラッシュカードを別のページで →