将odeint的runge_kutta4与Matlab的ode45进行比较

时间:2014-11-05 00:10:55

标签: c++ matlab boost numerical-integration odeint

我想在odeint C++ library中使用runge_kutta4方法。我在Matlab中解决了这个问题。我在Matlab中使用以下代码来解决x'' = -x - g*x',初始值为x1 = 1x2 = 0,如下所示

main.m

clear all
clc
t = 0:0.1:10;
x0 = [1; 0];
[t, x] = ode45('ODESolver', t, x0);
plot(t, x(:,1));
title('Position');
xlabel('time (sec)');
ylabel('x(t)');

ODESolver.m

function dx = ODESolver(t, x)
dx = zeros(2,1);
g  = 0.15;
dx(1) =  x(2);
dx(2) = -x(1) - g*x(2);
end

我已经安装了odeint库。我使用runge_kutta4的代码如下

#include <iostream>

#include <boost/numeric/odeint.hpp>

using namespace std;
using namespace boost::numeric::odeint;

/* The type of container used to hold the state vector */
typedef std::vector< double > state_type;

const double gam = 0.15;

/* The rhs of x' = f(x) */
void lorenz( const state_type &x , state_type &dx ,  double t )
{
    dx[0] =  x[1];
    dx[1] = -x[0] - gam*x[1];
}


int main(int argc, char **argv)
{
    const double dt = 0.1;
    runge_kutta_dopri5<state_type> stepper;
    state_type x(2);
    x[0] = 1.0;
    x[1] = 0.0;


   double t = 0.0;
   cout << x[0] << endl;
   for ( size_t i(0); i <= 100; ++i){
       stepper.do_step(lorenz, x , t, dt );
       t += dt;
       cout << x[0] << endl;
   }


    return 0;
}

结果如下图所示 enter image description here

我的问题是结果有何不同?我的C ++代码有问题吗?

这两个方法的第一个值

Matlab    C++
-----------------
1.0000    0.9950
0.9950    0.9803
0.9803    0.9560
0.9560    0.9226
0.9226    0.8806
0.8806    0.8304
0.8304    0.7728
0.7728    0.7084
0.7083    0.6379

更新

问题是我忘了在我的C ++代码中包含初始值。感谢@horchler注意到它。包含正确的值并使用runge_kutta_dopri5作为@horchler建议后,结果为

Matlab    C++
-----------------
1.0000    1.0000
0.9950    0.9950
0.9803    0.9803
0.9560    0.9560
0.9226    0.9226
0.8806    0.8806
0.8304    0.8304
0.7728    0.7728
0.7083    0.7084

我已更新代码以反映这些修改。

2 个答案:

答案 0 :(得分:9)

odeint中的runge_kutta4步进器与Matlab的ode45完全不同,后者是基于Dormand-Prince方法的自适应方案。要复制Matlab的结果,您应该尝试使用runge_kutta_dopri5步进器。另外,请确保您的C ++代码使用与ode45相同的绝对和相对容差(默认值分别为1e-61e-3)。最后,看起来您可能无法在C ++结果中保存/打印初始条件。

如果您对ode45未指定固定步骤的原因感到困惑,即使您指定了t = 0:0.1:10;,请参阅my detailed answer here

如果必须使用固定步骤runge_kutta4步进器,那么您需要减少C ++代码中的集成步长以匹配Matlab的输出。

答案 1 :(得分:3)

Matlab ode45函数已经包含错误控制,我认为也是插值(密集输出)。要与boost.odeint进行比较,您应该使用相同的功能。如果使用的步进算法提供此功能,Boost.odeint提供执行步长控制和密集输出的integrate函数。下面的代码片段显示了如何使用horchler提供的Matlab中的默认错误控制参数:

#include <boost/numeric/odeint.hpp>

using namespace std;
using namespace boost::numeric::odeint;

/* The type of container used to hold the state vector */
typedef std::vector< double > state_type;

const double gam = 0.15;

/* The rhs of x' = f(x) */
void damped_osc( const state_type &x , state_type &dx , const double t )
{
    dx[0] =  x[1];
    dx[1] = -x[0] - gam*x[1];
}

void print( const state_type &x, const double t )
{
    cout << x[0] << endl;
}

int main(int argc, char **argv)
{
    cout.precision(16);  // full precision output
    const double dt = 0.1;
    typedef runge_kutta_dopri5<state_type> stepper_type;
    state_type x(2);
    x[0] = 1.0;
    x[1] = 0.0;

    integrate_const(make_dense_output<stepper_type>( 1E-6, 1E-3 ),
                    damped_osc, x, 0.0, 10.0, dt , print);

    return 0;
}

请注意,结果可能仍然不是完全相同(与所有16位数字相同),因为Boost.odeint中的错误控件可能未被强制完全在Matlab的ode45。