Runge-Kuttaメソッドを2次ODEに適用する


11

オイラー法をルンゲ・クッタの4次に置き換えて、一定の重力ではない自由落下運動(たとえば、地上1万kmからの自由落下)を決定する方法を教えてください。

これまで、オイラー法による簡単な統合を書きました。

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(加速)を使用して1回だけ統合しているため、何かが間違っています。RK4を使用した速度の積分は、v * dtと同じであるため意味がありません。

Runge-Kutta積分を使用して2次微分方程式を解く方法を教えてください。k1、l1、k2、l2 ... l4係数を計算してRK4を実装する必要がありますか?どうやってやるの?


こんにちは@Marcin、私はあなたのタイトルを編集して、あなたの問題が実際にあると思うものをよりよく反映させました。私たちはもっと役に立つ答えを得るかもしれないし、新しいタイトルでこの質問を将来見る他の人にとってはもっと検索可能になると思います。同意しない場合は、お気軽に元に戻してください。
ダグリピンスキー

回答:


17

マルチステップ(ルンゲクッタなど)メソッドを2次以上のODEまたはODEシステムに適用する方法については、かなりの混乱があるようです。プロセスは一度理解すれば非常に簡単ですが、適切な説明がなければ明らかではありません。次の方法は、私が最も簡単な方法です。

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のような一般的な線形代数ライブラリも「サポート」としてカウントする必要があります。
キリル

1
@キリル、ヒントをありがとう。私はまだ比較的C ++に慣れておらず、valarrayを使用したことはありません。有用なことも学びました。追加する編集。
ダグリピンスキー

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.)、など
キリル

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]) }です。
ダグリピンスキー

1
@Kirillは知っていますが、それはC ++ 11以降でのみ機能します。つまり、最も一般的なコンパイラのデフォルトのコンパイラオプションでは機能しません。古い標準でも同様に機能するものを優先し、コードをコンパイルできないことによる混乱を減らすことを希望しました。
ダグリピンスキー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.