在 boost::odeint 中指定插值时间

Specify interpolation times in boost::odeint

我已经开始在我的 C++ 代码中使用 boost::odeint,我认为我缺少其他集成器中可用的一个简单功能,即 Scipy 的 odeint

scipy.odeint 允许用户指定必须将状态添加到输出状态历史记录的时间。scipy.odeint 是一个可变时间步长积分器,其单行调用如下所示(状态从初始条件 X0 和 interpolated/stored 在 times 中指定的时间积分)

X = scipy.odeint(dxdt,X0,times,atol = 1e-13,rtol = 1e-13) 

其中 X 是一个矩阵,其行数与 times

中的元素数相同

基本上,我正在 boost::odeint 中寻找类似的功能,以便做两件事:

  1. 将状态从 t0 传播到 tf,但只检索状态的最终值。我认为如果内部时间满足 t == tf,我可以编写一个只存储状态的观察者,但这看起来像是一个相当丑陋的 hack。如果我想让积分器选择合适的内部时间步来满足公差值,存储中间状态是一个不必要的负担。
  2. 将状态从 t0 传播到 tf,但在预先指定的时间存储状态,这些时间不一定均匀分布,其方式与对 scipy.odeint 的调用类似上文,同时还让积分器选择合适的内部时间步长。

我最接近实现的是以下

size_t steps = integrate_adaptive( make_controlled< error_stepper_type >( 1.0e-10 , 1.0e-16 ) , 
    dynamics , x , 0.0 , 10.0 , 1. , push_back_state_and_time( x_vec , times ));

满足公差,但所有状态都由观察者存储到 x_vec 中,没有让我指定存储时间应该是多少。

我该如何进行?

您似乎在寻找 integrate_times 函数:

它允许您指定将调用观察器的确切时间范围,如有必要,调整步长以准确达到每个时间步。

特别是对于自适应方法,这非常有用,因为它会在您指定的确切时间计算解决方案,同时仍然控制时间步长不超过误差范围。

因此您当前的调用可以修改为

auto stepper = make_controlled<error_stepper_type>( 1.0e-10 , 1.0e-16 );
// std::vector<time> times;
// std::vector<state> x_vec;
// time tstep;
auto tbegin = times.begin();
auto tend = times.end();
integrate_times(stepper, dynamics, x, tbegin, tend, tstep, push_back_state(x_vec));