दूसरे क्रम के ODE के लिए रन-कुट्टा विधि लागू करना


11

मैं रनवे-कुट्टा 4 के क्रम से यूलर विधि को कैसे बदल सकता हूं ताकि निरंतर गुरुत्वाकर्षण परिमाण में मुक्त गिरावट गति का निर्धारण किया जा सके (उदाहरण के लिए जमीन से 10 000 किमी ऊपर से गिरना)।

अब तक मैंने यूलर विधि द्वारा सरल एकीकरण लिखा है:

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

x चर का अर्थ है वर्तमान स्थिति, v का अर्थ है वेग, getMagnitude (x) x स्थिति पर त्वरण लौटाता है।

मैंने आरके 4 को लागू करने की कोशिश की:

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

लेकिन कुछ गलत है, क्योंकि मैं केवल एक बार आरके 4 (त्वरण) का उपयोग करके एकीकृत कर रहा हूं। RK4 का उपयोग करते हुए वेग को एकीकृत करना समझ में नहीं आता क्योंकि यह v * dt के समान है।

क्या आप मुझे बता सकते हैं कि रनगे-कुट्टा एकीकरण का उपयोग करके दूसरे क्रम के अंतर समीकरणों को कैसे हल किया जाए? क्या मुझे k1, l1, k2, l2 ... l4 गुणांक की गणना करके RK4 को लागू करना चाहिए? मैं उसे कैसे कर सकता हूँ?


हाय @Marcin, मैंने आपके शीर्षक को बेहतर तरीके से प्रतिबिंबित करने के लिए संपादित किया कि मुझे क्या लगता है कि आपकी समस्या वास्तव में है। मुझे लगता है कि हमें अधिक उपयोगी उत्तर मिल सकते हैं और यह दूसरों के लिए अधिक खोज योग्य होगा जो भविष्य में इस सवाल को नए शीर्षक के साथ देखते हैं। यदि आप असहमत हैं तो इसे वापस बदलने के लिए स्वतंत्र महसूस करें।
डग लिपिंस्की

जवाबों:


17

बहु-चरण (जैसे रन-कुट्टा) विधियों को 2 या उच्चतर ऑर्डर ODEs या ODEs के सिस्टम में कैसे लागू किया जाए, इस बारे में काफी भ्रम की स्थिति है। एक बार समझने के बाद प्रक्रिया बहुत सरल है, लेकिन एक अच्छी व्याख्या के बिना शायद स्पष्ट नहीं है। निम्नलिखित विधि वह है जो मुझे सबसे सरल लगती है।

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
" दुर्भाग्य से सी ++ इस तरह से वेक्टर ऑपरेशन का मूल रूप से समर्थन नहीं करता है " मुझे लगता है कि यह मानक पुस्तकालय में भी करता है, लेकिन अन्य रैखिक बीजगणित पुस्तकालयों के साथ उपयोग करना आसान नहीं है: en.cppreference.com/w/cpp/numeric/valpray मुझे लगता है। Eigen की तरह आम रेखीय बीजगणित पुस्तकालयों को भी "समर्थन" के रूप में गिना जाना चाहिए।
किरिल

1
@Kirill, टिप के लिए धन्यवाद। मैं अभी भी C ++ में अपेक्षाकृत नया हूं और मैंने पहले वैलेरे का उपयोग नहीं किया है, मैंने अभी कुछ उपयोगी सीखा है! जोड़ने का संपादन।
डग लिपिंस्की

1
हो सकता है कि यह सलाह तब भी सहायक हो: 1) अपने कोड को स्वचालित रूप से प्रारूपित करने के लिए क्लैंग-प्रारूप का उपयोग करें, यह वास्तव में मानक और समान है। 2) typedef std::valarray<double> Vectorआमतौर पर उपयोग किए जाने वाले प्रकारों के लिए उपयोग करें । 3) टाइप सुरक्षा और शुद्धता const int NDIM = 2के #defineलिए उपयोग करें । 4) C ++ 11 के बाद से आप RHS के शरीर को बस से बदल सकते हैं return {X[1], 1}। 5) सी ++ (सी के विपरीत) को पहले वेरिएबल घोषित करना वास्तव में असामान्य है, फिर बाद में उन्हें इनिशियलाइज़ करें, उसी जगह पर वैरिएबल घोषित करना पसंद करें जहाँ आप उन्हें इनिशियलाइज़ करते हैं ( 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 के बाद से काम करता है, जिसका अर्थ है कि यह सबसे लोकप्रिय संकलक पर डिफ़ॉल्ट संकलक विकल्पों के साथ काम नहीं करता है। मैंने उस चीज़ के पक्ष में छोड़ने का फैसला किया जो पुराने मानकों के साथ काम करती है और कोड को संकलित करने में असमर्थता के कारण भ्रम को कम करती है।
डग लिपिंस्की
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.