Runge-Kutta 방법을 2 차 ODE에 적용


11

오일러 방법을 Runge-Kutta 4 차 순서로 대체하여 일정한 중력 크기가 아닌 자유 낙하 운동을 결정하려면 어떻게해야합니까 (예 : 지상 10,000km에서 자유 낙하)?

지금까지 나는 오일러 방법으로 간단한 통합을 작성했습니다.

while()
{
    v += getMagnitude(x) * dt;
    x += v * dt;
    time += dt;
}

x 변수는 현재 위치를, v는 속도를, getMagnitude (x)는 x 위치에서 가속을 반환합니다.

RK4 구현을 시도했습니다.

while()
{
    v += rk4(x, dt) * dt; // rk4() instead of getMagintude()
    x += v * dt;
    time += dt;
}

rk4 () 함수 본문은 다음과 같습니다.

inline double rk4(double tx, double tdt)
{
   double k1 = getMagnitude(tx);
   double k2 = getMagnitude(tx + 0.5 * tdt * k1);
   double k3 = getMagnitude(tx + 0.5 * tdt * k2);
   double k4 = getMagnitude(tx + tdt * k3);

   return (k1 + 2*k2 + 2*k3 + k4)/6.0;
}

그러나 RK4 (가속)를 사용하여 한 번만 통합하기 때문에 무언가 잘못되었습니다. RK4를 사용한 속도 통합은 v * dt와 같기 때문에 의미가 없습니다.

Runge-Kutta 적분을 사용하여 2 차 미분 방정식을 푸는 방법을 알려주시겠습니까? k1, l1, k2, l2 ... l4 계수를 계산하여 RK4를 구현해야합니까? 어떻게해야합니까?


안녕하세요 @Marcin, 문제가 실제로 어떻게 생각되는지 더 잘 반영하도록 제목을 편집했습니다. 더 유용한 답변을 얻을 수있을 것으로 생각되며 앞으로 새로운 제목으로이 질문을하는 다른 사람들이 더 많이 검색 할 수있을 것입니다. 동의하지 않으면 언제든지 다시 변경하십시오.
Doug Lipinski

답변:


17

2 단계 이상의 ODE 또는 ODE 시스템에 다단계 (예 : Runge-Kutta) 방법을 적용하는 방법에 대해서는 약간의 혼동이있는 것 같습니다. 프로세스는 일단 이해하면 매우 간단하지만, 설명이 없으면 명확하지 않을 수 있습니다. 다음 방법은 내가 찾은 가장 간단한 방법입니다.

F=mx¨

[x˙v˙]=[vF/m]

vxk1k4(x,v)

while (t<TMAX)
    k1 = RHS( t, X );
    k2 = RHS( t + dt / 2, X + dt / 2 * k1 );
    k3 = RHS( t + dt / 2, X + dt / 2 * k2 );
    k4 = RHS( t + dt, X + dt * k3 );
    X = X + dt / 6 * ( k1 + 2 * k2 + 2 * k3 + k4 );
    t = t + dt;
end

X=(x,v)RHS( t, X )(x˙(t),v˙(t))

불행히도 C ++은 이와 같은 벡터 연산을 기본적으로 지원하지 않으므로 벡터 라이브러리를 사용하거나 루프를 사용하거나 별도의 부분을 수동으로 작성해야합니다. C ++에서는 std::valarray동일한 효과를 얻기 위해 사용할 수 있습니다 . 일정한 가속도를 가진 간단한 작업 예가 있습니다.

#include <valarray>
#include <iostream>

const size_t NDIM = 2;

typedef std::valarray<double> Vector;

Vector RHS( const double t, const Vector X )
{
  // Right hand side of the ODE to solve, in this case:
  // d/dt(x) = v;
  // d/dt(v) = 1;
  Vector output(NDIM);
  output[0] = X[1];
  output[1] = 1;
  return output;
}

int main()
{

  //initialize values

  // State variable X is [position, velocity]
  double init[] = { 0., 0. };
  Vector X( init, NDIM );

  double t = 0.;
  double tMax=5.;
  double dt = 0.1;

  //time loop
  int nSteps = round( ( tMax - t ) / dt );
  for (int stepNumber = 1; stepNumber<=nSteps; ++stepNumber)
  {

    Vector k1 = RHS( t, X );
    Vector k2 = RHS( t + dt / 2.0,  X + dt / 2.0 * k1 );
    Vector k3 = RHS( t + dt / 2.0, X + dt / 2.0 * k2 );
    Vector k4 = RHS( t + dt, X + dt * k3 );

    X += dt / 6.0 * ( k1 + 2.0 * k2 + 2.0 * k3 + k4 );
    t += dt;
  }
  std::cout<<"Final time: "<<t<<std::endl;
  std::cout<<"Final position: "<<X[0]<<std::endl;
  std::cout<<"Final velocity: "<<X[1]<<std::endl;

}

6
" 불행하게도 C ++에서 다음과 같이하지 기본적으로 지원 벡터 연산을 수행 내가 심지어 표준 라이브러리에 않습니다 생각", 그러나 반드시 쉽게 다른 선형 대수 라이브러리와 함께 사용하기에 : en.cppreference.com/w/cpp/numeric/valarray 내 생각 Eigen과 같은 일반적인 선형 대수 라이브러리도 "지원"으로 계산되어야합니다.
Kirill

1
@Kirill, 팁 주셔서 감사합니다. 나는 아직도 C ++에 비교적 익숙하지 않고 전에 valarray를 사용하지 않았습니다. 추가 편집.
Doug Lipinski

1
아마도이 조언이 도움이 될 것입니다 : 1) 코드를 자동으로 포맷하려면 clang-format을 사용하십시오. 정말 표준이며 균일합니다. 2) typedef std::valarray<double> Vector일반적으로 사용되는 유형에 사용하십시오 . 3) 형식 안전성과 정확성 const int NDIM = 2대신에 사용하십시오 #define. 4) C ++ 11부터 RHS 본문을 간단히로 바꿀 수 있습니다 return {X[1], 1}. 5) C ++에서 (C와 달리) 변수를 먼저 선언 한 다음 나중에 초기화하고 변수를 초기화하는 위치 ( double t = 0., 등) 에서 변수를 선언하는 것을 선호합니다
Kirill

1
@MarcinW. RHS()미분 방정식의 우변을 계산합니다. 상태 벡터 X는 (x, v)이므로 dX / dt = (dx / dt, dv / dt) = (v, a)입니다. 문제의 경우 (a = G * M / x ^ 2 인 경우) RHS가를 반환해야합니다 { X[1], G*M/(X[0]*X[0]) }.
Doug Lipinski

1
@Kirill 알고 있지만 C ++ 11 이후에만 작동하므로 가장 인기있는 컴파일러의 기본 컴파일러 옵션에서는 작동하지 않습니다. 나는 오래된 표준과 함께 작동하는 것을 선호하고 코드를 컴파일 할 수 없기 때문에 혼란을 줄이기를 희망했습니다.
Doug Lipinski
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.