如前所述,这个问题没有直接的解决方案。
一种可能的解决方案是提供一个
- 报告 ODE 已停止的整数向量。因此,如果该向量的第 i 个元素为零,则意味着第 i 个 ODE 仍在运行。
- runge_kutta_dopri5 中的自定义错误检查器,如果当前系统已经停止,则将错误设置为 0。从而避免此错误影响步长控制机制,至少不会对步长控制产生负面影响。请参阅下面的草图,了解如何实现这一点
- 检查集成是否停止的函数。这原则上可以放在观察者中。
自定义错误检查器的草图可以是(它是从 default_error_checker 复制的):
class custom_error_checker
{
public:
typedef double value_type;
typedef thrust_algebra algebra_type;
typedef thrust_operations operations_type;
default_error_checker(
const thrust::device_vector< int > &is_stopped ,
value_type eps_abs = static_cast< value_type >( 1.0e-6 ) ,
value_type eps_rel = static_cast< value_type >( 1.0e-6 ) ,
value_type a_x = static_cast< value_type >( 1 ) ,
value_type a_dxdt = static_cast< value_type >( 1 ) )
: m_is_stopped( is_stopped ) ,
m_eps_abs( eps_abs ) , m_eps_rel( eps_rel ) ,
m_a_x( a_x ) , m_a_dxdt( a_dxdt )
{ }
template< class State , class Deriv , class Err , class Time >
value_type error( const State &x_old ,
const Deriv &dxdt_old ,
Err &x_err , Time dt ) const
{
return error( algebra_type() , x_old , dxdt_old , x_err , dt );
}
template< class State , class Deriv , class Err , class Time >
value_type error( algebra_type &algebra ,
const State &x_old ,
const Deriv &dxdt_old ,
Err &x_err , Time dt ) const
{
// this overwrites x_err !
algebra.for_each3( x_err , x_old , dxdt_old ,
typename operations_type::template rel_error< value_type >(
m_eps_abs , m_eps_rel , m_a_x ,
m_a_dxdt * get_unit_value( dt ) ) );
thrust::replace_if( x_err.begin() ,
x_err.end() ,
m_is_stopped.begin() ,
thrust::identity< double >()
0.0 );
value_type res = algebra.reduce( x_err ,
typename operations_type::template maximum< value_type >() ,
static_cast< value_type >( 0 ) );
return res;
}
private:
thrust::device_vector< int > m_is_stopped;
value_type m_eps_abs;
value_type m_eps_rel;
value_type m_a_x;
value_type m_a_dxdt;
};
您可以通过以下方式在受控的 runge kutta 中使用这样的检查器
typedef runge_kutta_dopri5< double , state_type , double , state_type ,
thrust_algebra , thrust_operation > stepper;
typedef controlled_runge_kutta< stepper ,
custom_error_checker > controlled_stepper ;
typedef dense_output_runge_kutta< controlled_stepper > dense_output_stepper;
thrust::device_vector< int > is_stopped;
// initialize an is_finished
dense_output_stepper s(
controlled_stepper(
custom_controller( is_stopped , ... )));
最后,您需要一个函数来检查哪个 ODE 已经停止。让我们调用这个函数check_finish_status
:
void check_finish_status(
const state_type &x ,
thrust::device_vector< int > &is_stopped );
您可以在观察者内部调用该函数,并且需要将 is_stopped 传递给观察者。
我们还有一个实验性的脏分支,其中步长控制直接在 GPU 上运行,并分别控制每个 ODE。这确实非常强大且高性能。不幸的是,此功能无法轻松集成到主分支中,因为__device__ __host__
需要将许多说明符添加到 odeint 的方法中。如果您愿意,可以检查 odeint 存储库中的 cuda_controlled_stepper 分支和/或给我留言以获取更多说明。