고급 · 삼체 궤도

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. def pull(p):

    다른 두 천체의 중력으로 생기는 각 천체의 가속도예요.

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

    방향 d / r을 따르는 중력 G·m / r²을 합치면 d / r³이 돼요. 여기서 G와 질량은 1이에요.

  3. vel += acc * dt / 2

    립프로그예요. 속도를 반 단계, 위치를 한 단계, 그다음 새 힘으로 속도를 나머지 반 단계 움직여요.

  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분의 1씩 떨어져 있어요.

직접 해 보기

  1. 끝에서 질량 중심은 어디에 있을까요?

    힌트

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

    정답

    약 10⁻¹³ 이내로 (0, 0)에 있어요. 전체 운동량이 0이라서 질량 중심은 전혀 움직이지 않아요.

  2. 영상처럼 turn = 15로 해 보세요. 무슨 일이 일어나고, 이제 질량 중심은 어디로 갈까요?

    힌트

    출발할 때와 한 주기 뒤의 위치를 비교해 보세요.

    정답

    궤적이 더 이상 닫히지 않아요. 한 주기 뒤 세 천체는 합쳐서 출발한 자리에서 약 2단위 떨어져 있어요. 속도 하나를 돌리면 계에 운동량도 조금 생겨서 질량 중심이 떠밀려 가고, 끝에는 약 (−0.81, 0.67)에 있어요.

  3. dt를 열 배 크게, 0.005로 해 보세요. 8자는 여전히 닫힐까요?

    힌트

    한 주기 뒤 천체들이 출발점에서 얼마나 떨어져 있는지 재 보세요.

    정답

    네, 하지만 덜 정확해요. 한 주기 뒤 세 천체는 합쳐서 출발점에서 약 0.006 떨어져 있어요. dt = 0.0005일 때는 약 0.0001이에요.

퀴즈

1. 왜 d / |d|²가 아니라 d / |d|³일까요?
2. 립프로그가 오일러 한 단계보다 나은 점은 무엇일까요?
3. track[-1, :, 0]은 무엇일까요?
4. 8자 궤도에서 질량 중심이 가만히 있는 이유는 무엇일까요?
5. 8자 궤도가 존재한다는 것을 증명한 사람은 누구일까요?

플래시카드

앞면을 읽고, 뒷면의 내용을 스스로 말해 본 다음 카드를 펼쳐 보세요.

  • 천체 i에 작용하는 중력

    다른 천체 j에 대한 Σ G·mⱼ·(rⱼ − rᵢ) / |rⱼ − rᵢ|³.

  • 립프로그

    속도 반 단계, 위치 한 단계, 다시 속도 반 단계.

  • 주기 궤도

    한 주기 뒤에 정확히 자기 자신으로 돌아오는 출발.

  • 질량 중심

    질량으로 가중한 평균 위치. 전체 운동량이 0이 아닐 때만 움직여요.

  • 혼돈

    출발의 아주 작은 변화가 점점 커져서 운동이 완전히 달라지는 것.

이 플래시카드만 따로 보기 →