Reputation: 153
Below is my 4th order Runge-Kutta algorithm to solve a first order ODE. I am checking it against the wikipedia example found here to solve:
\frac{dx}{dt} = tan(x) + 1
Unfortunately it is out by a little bit. I have toyed around for a long while, but I can't find the error. The answer should be t = 1.1 and x = 1.33786352224364362. The below code gives t = 1.1 and x = 1.42223.
/*
This code is a 1D classical Runge-Kutta method. Compare to the Wikipedia page.
*/
#include <math.h>
#include <iostream>
#include <iomanip>
double x,t,K,K1,K2,K3,K4;
const double sixth = 1.0 / 6.0;
static double dx_dt(double t, double x){
return tan(x) + 1;
}
int main(int argc, const char * argv[]) {
/*======================================================================*/
/*===================== Runge-Kutta Method for ODE =====================*/
/*======================================================================*/
double t_initial = 1.0;// initial time
double x_initial = 1.0;// initial x position
double t_final = 1.1;// value of t wish to know x
double dt = 0.025;// time interval for updates
double halfdt = 0.5*dt;
/*======================================================================*/
while(t_initial < t_final){
/*============================ Runge-Kutta increments =================================*/
double K1 = dt*dx_dt( t_initial, x_initial );
double K2 = dt*dx_dt( t_initial + halfdt, x_initial + halfdt*K1 );
double K3 = dt*dx_dt( t_initial + halfdt, x_initial + halfdt*K2 );
double K4 = dt*dx_dt( t_initial + dt, x_initial + dt*K3 );
x_initial += sixth*(K1 + 2*(K2 + K3) + K4);
/*============================ prints =================================*/
std::cout << t_initial << std::setw(16) << x_initial << "\n";
/*============================ re-setting update conditions =================================*/
t_initial += dt;
/*======================================================================*/
}
std::cout<<"----------------------------------------------\n";
std::cout << "t = "<< t_initial << ", x = "<< x_initial << std::endl;
}/* main */
Upvotes: 5
Views: 3552
Reputation: 26356
The problem is that the tableau used for your code is different than the one for the code you cited in wikipedia. The one you're using is this:
0 |
1/2 | 1/2
1/2 | 0 1/2
1 | 0 0 1
-------------------------------------
| 1/6 1/3 1/3 1/6
And the one used in wikipedia is
0 |
2/3 | 2/3
---------------------
| 1/4 3/4
Different tableaus will yield different results depending on the step-size, which is the way used to make sure that the step-size is good enough for a certain accuracy. However, when dt -> 0
, then all tableaus are the same.
Besides all this, your code is wrong anyway even for RK4. The second part of the function should have halves, not 0.5*dt
:
double K1 = dt*dx_dt( t_initial, x_initial );
double K2 = dt*dx_dt( t_initial + halfdt, x_initial + 0.5*K1 );
double K3 = dt*dx_dt( t_initial + halfdt, x_initial + 0.5*K2 );
double K4 = dt*dx_dt( t_initial + dt, x_initial + K3 );
Upvotes: 5
Reputation: 5352
I think you are including one too many increments and have introduced problems by rearranging the mathematics. Try this:
#include <math.h>
#include <iostream>
#include <iomanip>
static double dx_dt(double t, double x)
{
return tan(x) + 1;
}
int main(int argc, const char * argv[])
{
double t = 1.0;
double t_end = 1.1;
double y = 1.0;
double h = 0.025;
std::cout << std::setprecision(16);
int n = static_cast<int>((t_end - t) / h);
for (int i = 0; i < n; i++)
{
double k1 = dx_dt(t, y);
double k2 = dx_dt(t + h / 2.0, y + h*k1 / 2.0);
double k3 = dx_dt(t + h / 2.0, y + h*k2 / 2.0);
double k4 = dx_dt(t + h, y + h*k3);
y += (k1 + 2 * k2 + 2 * k3 + k4) * h / 6.0;
std::cout << t << ": " << y << std::endl;
t += h;
}
std::cout << "----------------------------------------------\n";
std::cout << "t = " << t << ", x = " << y << std::endl;
std::getchar();
}
I precalculate how many times to do the iteration, this avoids a few different issues. Also as others have mentioned, the worked example on wikipedia is for a two stage variant of the algorithm.
I've taken the liberty of changing the variable names to match wikipedia. A good tip is always match the naming of your reference text until the thing works.
Upvotes: 3
Reputation: 26040
You are making a rather usual mistake in trying to be overly correct and implement the two variants of the algorithm at once.
It should either be
k2 = dt*f(t+0.5*dt, x+0.5*k1)
or
k2 = f(t+0.5*dt, x+0.5*dt*k1)
the other k
s accordingly.
Note that in both cases the slope f
only gets multiplied with dt
once.
Upvotes: 4