운동방정식의 수치해

물리학 2020년 07월 29일

물리학에서 운동방정식은 질점의 운동을 설명하며, 다음과 같이 표현할 수 있다.\[\ddot{x} = F(\dot{x}, x, t)\]

이는 2계 미분방정식으로, 시간에 따른 물체의 운동은 곧 \(x(t)\)를 의미하여 이를 구하기 위해서는 미분방정식의 해를 구해야 한다. 본 글에서는 예시로 계수를 고려하지 않은 강제진동자 (Driven Oscillator)의 다음 운동방정식을 살펴보려 한다.\[\ddot{x} + \dot{x} + x  = \sin{t}\]이때, 이 식은 2계 1차 비동차 상미분방정식이다.

Euler 방법

\(y\)에 대해 \[\textrm{d}{y} / \textrm{d}{t} = f(t,y)\]라 하자. 이때, \[y_{n+1} = y_n + h f(t_n, y_n)\]로 근사가 가능하고, 이를 Euler 방법이라 한다.

이는 \(y\)의 테일러 전개 \[y(t_0 + h) = y(t_0) + h\dot{y}(t_0) + \frac{1}{2} h^2 \ddot{y}(t_0) + \mathcal{O}(h ^3)\]\[t_{n+1} = t_n + h\]

에서 2차 이상의 항을 무시하여 유도할 수 있다. 곧, 오일러 방법은 1차항만 고려한 근사이다. 이로 인해 오일러 방법은 상당한 오차가 발생하고, 이를 해결하기 위해서는 고차항도 고려해야 한다.

Runge-Kutta 방법

Runge-Kutta 방법은 Euler 방법의 일반화로, 다양한 차수의 근사가 존재한다. 그 중 가장 많이 사용되는 방법은 RK4, 곧 4차 Runge-Kutta 방법이다.

위와 같은 \(f\)와 \(y\)에 대해,\[\begin{aligned} k_1 &= f(t_n,  y_n), \\ k_2 &= f(t_n + h/2, y_n + h k_1 / 2), \\ k_3 &= f(t_n + h/2, y_n + h k_2 / 2), \\ k_4 &= f(t_n + h, y_n + h k_3)\end{aligned} \]를 정의하자. 이때,\[y_{n+1} = y_n + \frac{1}{6} h (k_1 + 2k_2 + 2k_3 + k_4)\]\[t_{n+1} = t_n + h\]

로 근사가 가능하고, 이것이 RK4이다. 이 근사의 유도는 테일러 전개를 통해 가능하다.

운동방정식의 풀이

위에서 설명한 Euler 방법과 Runge-Kutta 방법은 1계 미분방정식에 대해서만 적용이 가능하지만, 운동방정식은 2계 미분방정식이다. 운동방정식에 Runge-Kutta 방법에 적용하기 위해서는 추가적인 변형이 필요하다.

\(\ddot{x} + \dot{x} + x  = \sin{t}\)에서, \(u \coloneqq \dot{x}\), \(v \coloneqq x\)로 정의하자. 이때 운동방정식을 다시 나타내면,\[\dot{u} + u + v = \sin{t}\]이고, 곧\[\dot{u} = \sin{t} - u - v,\quad\dot{v} = u\]이다.

이를 다시 나타내면, \[\dot{u} = f(u, v, t), \quad \dot{v} = g(u, v, t)\]의 꼴로, 이는 Runge-Kutta 방법을 2번 적용하여 해를 구할 수 있는 형태이다. 이를 python 코드로 나타내면 다음과 같다.

from numpy import sin, arange

h = 0.01

def rk4(f, t, u, v):
    k1 = f(t, u, v)
    k2 = f(t + h/2, u + h * k1 / 2, v + h * k1 / 2)
    k3 = f(t + h/2, u + h * k2 / 2, v + h * k2 / 2)
    k4 = f(t + h, u + h * k3, v + h * k3)
    
    return u + h * (k1 + 2 * k2 + 2 * k3 + k4) / 6
    
for t in arange(0, 1, h):    
    vel_ = rk4(
              lambda t_, u, v,: sin(t_) - u - v,
              t, vel, pos
          )

    pos = rk4(
              lambda t_, v, u,: u,
              t, pos, vel
          )
  
    vel = vel_
    
    print(f"t={t}, x={pos}, x'={vel}")

결과는 다음과 같다.

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.