我正在比较C的ODE解算器(Boost ODEINT)和MatLab的ODE45。每组代码都有相同的方程和初始条件。当在C代码中添加容错时,两个值(C++和MatLab)之间的差异相当大。两者的价值应该大致相同。增加误差容限会导致值的巨大差异是没有意义的。
#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;
}
以下是MatLab代码:
%% 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
1条答案
按热度按时间pgpifvop1#
这并不是不同的实现之间的区别(我认为它们都遵循海尔和沙姆派恩建立的标准)。我在python语言中得到了相同的图形,其中包含以下短行
当然,相对于该组件的范围
[-1000, 4000]
,这是最大的误差0.2 %
,因此,如果组件的图形绘制在一起,则低于可视差异。尽管如此,它仍然远远超过了预期的误差水平。让我们通过提供绝对误差的刻度来改变这一点
这并没有像预期的那样工作,在
u6
和u9
之间,误差范围减少了一半,而不是数量级。从u8
到u9
的误差范围是0.2
,从u7
到u9
到1.5
。在全位置差的对数图中
可以看到,期望的误差水平保持在大约
t=3000
在此之后,它更像是时间轴移动,因此位置差异更多的是在不同时间处于同一轨道上,而不是轨迹的误差。当比较
u9
和u12
时,得到完全相同的图案,后者在tol=1e-12
,只是误差水平降低了1e-3
。这可能是长期积分的影响,要么是方法步长误差在轨迹方向上具有明显的分量,改变了沿轨迹的速度,要么是时间步长求和中的累积误差变得明显。