Runge-Kutta yöntemini ikinci dereceden ODE'lere uygulama


11

Sabit yerçekimi büyüklüğünde olmayan serbest düşme hareketini belirlemek için Euler yöntemini Runge-Kutta 4. sıra ile nasıl değiştirebilirim (örn. Yerden 10 000 km yukarıda serbest düşüş)?

Şimdiye kadar Euler yöntemi ile basit entegrasyon yazdım:

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

x değişkeni geçerli konum, v hız anlamına gelir, getMagnitude (x) x konumunda ivme döndürür.

RK4 uygulamasını denedim:

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

burada rk4 () işlev gövdesi:

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;
}

Ama bir şeyler yanlış, çünkü sadece bir kez RK4 (hızlanma) kullanarak entegre oluyorum. RK4 dozunu kullanarak hızı entegre etmek mantıklı değildir çünkü v * dt ile aynıdır.

Runge-Kutta entegrasyonunu kullanarak ikinci mertebeden diferansiyel denklemlerin nasıl çözüleceğini söyleyebilir misiniz? K1, l1, k2, l2 ... l4 katsayılarını hesaplayarak RK4'ü uygulamalıyım? Bunu nasıl yapabilirim?


Selam @Marcin, başlığınızı düzenleyerek probleminizin gerçekte ne olduğunu düşündüğümü daha iyi yansıttım. Sanırım daha yararlı cevaplar alabiliriz ve bu soruyu gelecekte yeni başlık ile gören başkaları için daha aranabilir olacaktır. Kabul etmezseniz, geri değiştirmekten çekinmeyin.
Doug Lipinski

Yanıtlar:


17

Çok aşamalı (örneğin Runge-Kutta) yöntemlerin 2. veya daha yüksek dereceden ODE'lere veya ODE sistemlerine nasıl uygulanacağı konusunda biraz karışıklık var gibi görünüyor. İşlem, anladıktan sonra çok basittir, ancak iyi bir açıklama olmadan belki de açık değildir. Aşağıdaki yöntem en basit bulduğum yöntemdir.

Sizin durumunuzda, çözmek istediğiniz diferansiyel denklem . İlk adım, bu ikinci dereceden ODE'yi birinci dereceden ODE'lerin bir sistemi olarak yazmaktır. Bu şu şekilde yapılır:F=mx¨

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

Bu sistemde tüm denklemler sen gerektiğini söylemek olan eşzamanlı çözülmesi gerektiğini değil avans ve sonra avans , her ikisi de aynı anda ileri olmalıdır. Döngüsüz vektör işlemlerini destekleyen dillerde, bu, uzunluk 2 kod vektörlerinizde gerekli tüm terimleri yaparak kolayca yapılabilir. , için 2 uzunluğunda vektörler gerekir ve durum değişkeni zaman atlama için gerekli olan kod olarak yazılabilir MATLAB uzunluğu, 2'nin bir vektör olması gerekir:x ( x , v )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

burada ve içeren bir vektör döndürür . Gördüğünüz gibi, şeyleri vektörleştirerek, ODE sisteminizde kaç denklem olursa olsun RK4 kodunun sözdizimini değiştirmeniz bile gerekmez.X( ˙ x ( t ) , ˙ v ( t ) )=(x,v)RHS( t, X )(x˙(t),v˙(t))

Ne yazık ki C ++ böyle vektör işlemlerini yerel olarak desteklemediğinden, bir vektör kütüphanesi kullanmanız, döngüler kullanmanız veya ayrı parçaları manuel olarak yazmanız gerekir. C ++ ' std::valarrayda aynı efekti elde etmek için kullanabilirsiniz . İşte sabit ivmeli basit bir çalışma örneği.

#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
" Ne yazık ki C ++ böyle değil doğal olarak destek vektör işlemlerini yapar bunu bile standart kütüphanesinde yapar düşünüyorum", şart olmamakla birlikte kolay diğer lineer cebir kütüphaneleri ile kullanım için: en.cppreference.com/w/cpp/numeric/valarray bence Eigen gibi ortak lineer cebir kütüphaneleri de "destek" olarak sayılmalıdır.
Kirill

1
@Kirill, bahşiş için teşekkürler. Hala C ++ için nispeten yeniyim ve daha önce valarray kullanmadım, ben de yararlı bir şey öğrendim! Eklemek için düzenleme.
Doug Lipinski

1
Belki bu tavsiye de yardımcı olacaktır: 1) Kodunuzu otomatik olarak biçimlendirmek için clang formatını kullanın, gerçekten standart ve tek tiptir. 2) typedef std::valarray<double> VectorYaygın olarak kullanılan tipler için kullanın . 3) Tip güvenliği ve doğruluğu const int NDIM = 2yerine kullanın #define. 4) C ++ 11 olduğundan, RHS gövdesini basitçe değiştirebilirsiniz return {X[1], 1}. 5) C ++ 'da (C'den farklı olarak) önce değişkenleri bildirmek, daha sonra onları başlatmak, değişkenleri başlattığınız yerde ( double t = 0.vb.) Bildirmeyi tercih etmek çok nadirdir
Kirill

1
@MarcinW. RHS()diferansiyel denklemin sağ tarafını hesaplar. X durumu vektörü (x, v) 'dir, bu nedenle dX / dt = (dx / dt, dv / dt) = (v, a). Sorununuz için (a = G * M / x ^ 2 ise) RHS dönmelidir { X[1], G*M/(X[0]*X[0]) }.
Doug Lipinski

1
@Kirill biliyorum, ama bu sadece C ++ 11'den beri çalışıyor, bu da en popüler derleyicilerdeki varsayılan derleyici seçenekleriyle çalışmadığı anlamına geliyor. Bunu eski standartlarla çalışan bir şey lehine bırakmayı seçtim ve umarım kodu derleyememenin neden olduğu karışıklığı azalttım.
Doug Lipinski
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.