C++; 我在C++中使用了不同的ODE求解器,熟悉标准的理论和更先进的ODE数值方法。我想了解的是ODE类的“设计模式”是什么。比如看

C++; 我在C++中使用了不同的ODE求解器,熟悉标准的理论和更先进的ODE数值方法。我想了解的是ODE类的“设计模式”是什么。比如看,c++,class,design-patterns,numerical-methods,ode,C++,Class,Design Patterns,Numerical Methods,Ode,我们注意到,r.h.s的定义包括一个void函数,该函数引用z和dzdt,如下所示: void ode( const state_type &z , state_type &dzdt , double t ) { dzdt[0] = z[1]; dzdt[1] = -1 * z[0] * w * w; } 然后在主体上与主体进行整合 int main() { ... integrate( ode , z , t , 1000 , 0.1 ,

我们注意到,r.h.s的定义包括一个
void
函数,该函数引用
z
dzdt
,如下所示:

void ode( const state_type &z , state_type &dzdt , double t ) { 
    dzdt[0] = z[1]; 
    dzdt[1] = -1 * z[0] * w * w; 
} 
然后在主体上与主体进行整合

int main() { ... 

    integrate( ode , z , t , 1000 , 0.1 , write_ode ); 
    return 0;

}
当然,这样的库确实是硬编码的,但我只想掌握“步进器”背后的一般思想,比如说显式Euler方法


由于r.h.s的定义类似于
void-ode(…)
,我认为在步进部分中有一个对
void-ode(…)
的调用,允许更新
dzdt
。它可以如下实现(使用
std::vector
类)


第一个想法是,RK方法各阶段的导数向量是解算器类的组成部分。这可以防止在integrator运行期间频繁分配内存和取消分配/垃圾回收。也许最好使用高阶方法来看看为什么这是有用的,因为欧拉太琐碎了,可能会给出错误的直觉

下一个要考虑的想法是使用可变的、自适应的步长方法。这意味着您有在需要时执行的内部步骤,以及通过插值供外部使用的评估,通常使用“密集输出”概念。在那里,您可以完全隐藏内部步骤,或者公开它们和插值函数/对象。这两种思想都可以在
scipy中进行研究。集成
解算器,旧的步进器类
ode
隐藏了内部步骤,步进器类
RK45,Radau,…
在新的
解算器ivp
接口后面实现了第二个概念

然后你很快就到达了一个有结构状态空间的点。我们可以实现这样一种理念,即数据必须组装在一个平面的一维向量中,这是(旧的)标准。或者可以为状态空间类配备必要的向量和范数运算。您应该在boost::odeint模板参数中找到这一点

下一点是,状态空间可能有段,如不同的对象、位置与速度等,这些段在很大程度上是不同的量级,应在步长控制器的误差估计(包括绝对误差公差)中单独处理(也就是说,计算每个段的最佳步长,然后取最小值)


最后一点,只有当解算器具有事件操作机制时,它才会变得普遍有用状态的某些函数的过零,动作可以是记录事件、在事件处终止,或者更不标准的状态向量修改。

非常感谢您的详细回答。对我来说最重要的部分是第一段,即“导数是ode解算器类的组件”。我用RK类编辑了我的问题,我想了解如何实现上述概念。在实践中,问题是:“RK方法各阶段的导数向量以何种方式成为解算器类的组成部分”实现了吗?我想你指的是ode的rhs是带导数的向量,但你如何在代码中编写它?作为类内的结构?任何最小的工作示例都受到高度赞赏@LutzLehmann
void do_step(std::vector<double>& z, double tn, double h){
    //tn current time, h time step
    std::vector<double> dzdt(2); 
    ode(tn,y,dzdt);
    z[0] += h*dzdt[0];
    z[1] += h*dzdt[1];
}
#include <iostream>
#include <cmath>
#include <vector>


std::vector<double> operator+(const std::vector<double>& a, const std::vector<double>& b){
    std::vector<double> ret(a.size());
    for(unsigned int i=0;i<a.size();++i){
        ret[i] = a[i]+b[i];
    }
    return ret;
}


constexpr double k = 3.0;
constexpr double m = 2.0;
constexpr double km  = k/m;

class Rk{
    
private:
    std::vector<double> f(const double t, const std::vector<double>& y){
        std::vector<double> state(2);
        state[0] = y[1];
        state[1] = -(k/m)*y[0] + t;
        return state;
    }
    
    std::vector<double> y0;
    const double T;
    double dt;
    
public:
    Rk( std::vector<double> _y0,const double _T,double _dt) : y0{_y0}, T{_T}, dt{_dt}{}
    ~Rk()=default;
    
    
    
    std::vector<double> mvec(const std::vector<double>& v, const double c) {
        //implements m*vec
        const auto size = v.size();
        std::vector<double> res(size);
        for (std::size_t i = 0; i < size; ++i){
            res[i] = c*v[i];
        }
        return res;
    }
    
    
    
    
    void step(std::vector<double>& state, const double t){
        //performs a Rk4 step from time t to t+dt
        //state: current state y_1(tn),y_2(tn),...
        const double dth = 0.5*dt;
        std::vector<double> k1 = f(t,state);
        std::vector<double> k2 = f(t+dth,state+mvec(k1,0.5*dt));
        std::vector<double> k3 = f(t+dth,state+mvec(k2,0.5*dt));
        std::vector<double> k4 = f(t+dt,state+mvec(k3,dt));
        state = state + mvec ((k1+mvec(k2,2)+mvec(k3,2) + k4),dt/6.0);
    }
    
    void integrate(){
        std::vector<double> state(2);
        state = y0;
        double t =  0.0;
        for (unsigned int i=0;i<std::ceil(T/dt);++i){
            step(state,t);
            t+=dt;
            //            double err = std::fabs(std::sqrt(1/km) * std::sin(std::sqrt(km)*(t)) - state[0]);
            double err = std::fabs((1.0/9.0) * (6*t+std::sqrt(6)*std::sin(std::sqrt(km)*t)) - state[0]);
            std::cout << err <<std::endl;
            
        }
    }
    
    
};







int main(){
    const double dt = 0.01;
    std::vector<double> y0;
    y0.push_back(0.0);
    y0.push_back(1.0);
    Rk my_ode{y0,1.0,dt};
    my_ode.integrate();
    
    
    return 0;
    
}