I am comparing the ODE solver of C++ (BOOST ODEINT) against MATLAB's ODE45. Each set of code has the same equations and initial conditions. When error tolerance is added to the C++ code, the difference between the two values (C++ and MATLAB) is quite large. The values of the two should be roughly the same. It does not make sense that the error tolerance addition would cause a large difference in values.
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <fstream>
#include <sstream>
#include <boost/numeric/odeint.hpp>
#include <cmath>
#include <math.h>
#include <boost/array.hpp>
#include <boost/numeric/odeint/integrate/integrate_adaptive.hpp>
using namespace std;
using namespace boost::numeric::odeint;
const double sigma = 10.0;
const double R = 28.0;
const double b = 8.0 / 3.0;
const float mu = 3.986e5;
typedef boost::array< double, 6 > state_type;
ofstream opfile;
void lorenz(const state_type& x, state_type& dxdt, double t)
{
float r_2 = (pow(x[0],2) + pow(x[1],2) + pow(x[2],2));
float r = sqrt(r_2);
dxdt[0] = x[3];
dxdt[1] = x[4];
dxdt[2] = x[5];
dxdt[3] = (-mu / pow(r, 3)) * x[0];
dxdt[4] = (-mu / pow(r, 3)) * x[1];
dxdt[5] = (-mu / pow(r, 3)) * x[2];
}
void write_lorenz(const state_type& x, const double t)
{
opfile << t << '\t' << x[0] << '\t' << x[1] << '\t' << x[2]<< '\t' << x[3] << '\t'
<< x[4] << '\t' << x[5] << '\n'<< endl;
}
int main(int argc, char** argv)
{
double T = 4.2706e4;
typedef runge_kutta_dopri5<state_type> stepper_type;
state_type x = {3.1117e3 , 0 , 4.565e3, 3.0, 4.5, 6.8}; // initial conditions
const double dt = 0.1;
cout.precision(16); // full precision output
opfile.open("test_rk_dopri.txt");
integrate_const(make_dense_output<stepper_type>(1E-9, 1E-9), lorenz, x, 0.0, T, dt, write_lorenz);
//integrate_const(make_dense_output(1.0e-6, 1.0e-6, runge_kutta_dopri5< state_type >()), lorenz, x, 0.0, T, dt, write_lorenz);
opfile.close();
return 0;
}
Here is the MATLAB code:
%% ODE45 plotted against C++ odeint solvers
%% initial conditions
r0_vec = [3.1117e3;0;4.565e3];
v0_vec = [3.0; 4.5; 6.8];
mew = 3.986e5;
%% Runge Kutta cash karp
fid = fopen('test_cashkarp_Rk.txt', 'rt');
a = textscan(fid, '%f%f%f%f%f%f%f');
fclose(fid);
rk_cashkarp = cell2mat(a);
% t = linspace(0.1,10,length(rk_cashkarp));
fid_1 = fopen('test_rk4.txt', 'rt');
b = textscan(fid_1, '%f%f%f%f%f%f%f');
fclose(fid_1);
rk_4 = cell2mat(b);
% t = linspace(0.1,10,length(rk_4));
fid_2 = fopen('test_rk_dopri.txt', 'rt');
c = textscan(fid_2, '%f%f%f%f%f%f%f');
fclose(fid_2);
rk_dopri = cell2mat(c);
% t = linspace(0.1,10,length(rk_dopri));
fid_3 = fopen('test_rk_fehlberg.txt', 'rt');
d = textscan(fid_3, '%f%f%f%f%f%f%f');
fclose(fid_3);
rk_fehlberg = cell2mat(d);
% t = linspace(0.1,10,length(rk_fehlberg));
T = 4.2706e4;
%%ODE45 function
options = odeset('relTol',1e-6,'absTol',1e-6);
tspan = rk_dopri(:,1);
[t,y] = ode45(@(t,y) ODE_eqnsofMot(t,y,mew),tspan, [r0_vec; v0_vec],options);
close all
figure ('name','Postion Ode45')
plot(rk_dopri(:,2)-y(:,1))
% plot3(y(:,1),y(:,2),y(:,3),'.')
%
% figure ('name','Postion rk4')
% plot3(rk_4(:,2),rk_4(:,3),rk_4(:,4),'.')
%
% figure ('name','Postion rkcashkarp')
% plot3(rk_cashkarp(:,2),rk_cashkarp(:,3),rk_cashkarp(:,4),'.')
%
% figure ('name','Postion rkdopri')
% plot3(rk_dopri(:,2),rk_dopri(:,3),rk_dopri(:,4),'.')
%
% figure ('name','Postion rkfehlberg')
% plot3(rk_fehlberg(:,2),rk_fehlberg(:,3),rk_fehlberg(:,4),'.')
%
%%ODE45 function
function dydt = ODE_eqnsofMot(t, y, mew)
dydt = zeros(6,1);
r = sqrt(y(1)^2+(y(2))^2+(y(3))^2);
dydt(1) = y(4);
dydt(2) = y(5);
dydt(3) = y(6);
dydt(4) = (-mew/(r)^3)*y(1);
dydt(5) = (-mew/(r)^3)*y(2);
dydt(6) = (-mew/(r)^3)*y(3);
end

This is not a difference between the different implementations (I think they all follow the standard that Hairer and Shampine established). I get the same graph in python with the following short lines
Of course relative to the range
[-1000, 4000]of that component this is an error of0.2 %at the extreme, so below a visual difference if the graphs of the components are plotted together.Still, it is far above the desired error level. Let's change that by providing a scale for the absolute error
That does not work as expected, between
u6andu9the range of the error reduces by half, not by orders of magnitude. The error fromu8tou9has a range to0.2, fromu7tou9to1.5.In a log-plot of the full position difference
one can see that the desired error level holds to about
t=3000After that it is more like the time axes are shifted, so that the position difference is more of being on the same orbit at different times than errors of the trajectory. Exactly the same pattern is obtained when comparing
u9tou12, the latter attol=1e-12, only with the error level reduced by1e-3.This is likely an effect of long-term integration, either that the method step error has a noticeable component in direction of the trajectory, changing the speed along it, or that accumulated errors in the time step summation become noticeable.