在MATLAB中针对ODE45绘制时,容错在C++中不起作用

50few1ms  于 2022-11-15  发布在  Matlab
关注(0)|答案(1)|浏览(104)

我正在比较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
pgpifvop

pgpifvop1#

这并不是不同的实现之间的区别(我认为它们都遵循海尔和沙姆派恩建立的标准)。我在python语言中得到了相同的图形,其中包含以下短行

def grav(t,u): p,v = u[:3],u[3:]; a = -mu*p*sum(p**2)**-1.5; return *v, *a

mu, p0, v0 = 3.986e5, [3.1117e3 , 0 , 4.565e3], [3.0, 4.5, 6.8]
u0 = [*p0, *v0]
t = np.arange(0,4.2706e4, 0.1)

u6 = solve_ivp(grav, t[[0,-1]], u0, t_eval=t, atol=1e-6, rtol=1e-6)
u9 = solve_ivp(grav, t[[0,-1]], u0, t_eval=t, atol=1e-9, rtol=1e-9)

plt.plot(t,u9.y[0]-u6.y[0]); plt.grid(); plt.show()

当然,相对于该组件的范围[-1000, 4000],这是最大的误差0.2 %,因此,如果组件的图形绘制在一起,则低于可视差异。
尽管如此,它仍然远远超过了预期的误差水平。让我们通过提供绝对误差的刻度来改变这一点

sc = np.concatenate([[1e3]*3,[1]*3])
def solve(tol): return solve_ivp(grav, t[[0,-1]], u0, t_eval=t, atol=sc*tol, rtol=tol)
u6 = solve(1e-6)
u7 = solve(1e-7)
u8 = solve(1e-8)
u9 = solve(1e-9)

这并没有像预期的那样工作,在u6u9之间,误差范围减少了一半,而不是数量级。从u8u9的误差范围是0.2,从u7u91.5
在全位置差的对数图中

plt.semilogy(t,norm(u6.y[:3]-u9.y[:3])/norm(u9.y[:3])); 
plt.grid(); plt.show()

可以看到,期望的误差水平保持在大约t=3000

在此之后,它更像是时间轴移动,因此位置差异更多的是在不同时间处于同一轨道上,而不是轨迹的误差。当比较u9u12时,得到完全相同的图案,后者在tol=1e-12,只是误差水平降低了1e-3
这可能是长期积分的影响,要么是方法步长误差在轨迹方向上具有明显的分量,改变了沿轨迹的速度,要么是时间步长求和中的累积误差变得明显。

相关问题