There seems to be quite a bit of confusion about how to apply multi-step (e.g. Runge-Kutta) methods to 2nd or higher order ODEs or systems of ODEs. The process is very simple once you understand it, but perhaps not obvious without a good explanation. The following method is the one I find simplest.
In your case, the differential equation you would like to solve is $F = m\ddot{x}$. The first step is to write this second-order ODE as a system of first order ODEs. This is done as
$$
\left[\begin{array}{c}
\dot{x} \\
\dot{v}
\end{array}\right]
=
\left[\begin{array}{c}
v \\
F/m
\end{array}\right]
$$
All equations in this system must be solved simultaneously, which is to say that you should not advance $v$ and then advance $x$, they should both be advanced at the same time. In languages that support vector operations without loops this is easily done by making all the necessary terms in your code vectors of length 2. The function that computes the right hand side (the rate of change) of your ODE should return a vector of length 2, k1
to k4
should be vectors of length 2, and your state variable $(x,v)$ should be a vector of length 2. In MATLAB the necessary code for the time stepping can be written as:
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
where X
$=(x,v)$ and RHS( t, X )
returns a vector containing $(\dot{x}(t),\dot{v}(t))$. As you can see, by vectorizing things you don't even need to change the syntax of the RK4 code no matter how many equations are in your ODE system.
Unfortunately C++ does not natively support vector operations like this so you need to either use a vector library, use loops, or manually write out the separate parts. In C++ you can use std::valarray
to achieve the same effect. Here's a simple working example with constant acceleration.
#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;
}