在基础和派生中分离 ODE 和 ODE 求解器 类
Seperating ODE and ODE solver in base and derived classes
我觉得这个问题有点长所以我觉得还是先考虑一下简化版比较好:
有两个classA和B。B继承自A。B中有一个成员函数(add)需要运行使用A中的成员函数。
class A;
typedef int(A::*operation)(int c);
typedef void (A::*myfunction)(operation, int);
class A
{
public:
int a;
int b;
int do_something(myfunction f, operation op)
{
(this->*f)(op, 1);
}
void dummy_name(operation op, int a)
{
int c = (this->*op)(a);
}
};
class B : public A
{
public:
int a, b;
B(int a, int b): a(a), b(b) {}
int add(int c)
{
return a+b+c;
}
};
int main()
{
B inst_B(2, 5);
inst_B.do_something(&A::dummy_name, &B::add);
}
simple.cpp:45:41: error: cannot convert ‘int (B::*)(int)’ to ‘operation’ {aka ‘int (A::*)(int)’}
45 | inst_B.do_something(&A::dummy_name, &B::add);
| ^~~~~~~
| |
| int (B::*)(int)
simple.cpp:17:47: note: initializing argument 2 of ‘void A::do_something(myfunction, operation)’
17 | void do_something(myfunction f, operation op)
| ~~~~~~~~~~^~
为了编写一个简单的 ode 求解器并避免在模型 class 内部应对积分器,对于包含常微分方程组的每个模型,我将求解器和方程分成两个 class,而模型继承自 ode 求解器。
class HarmonicOscillator: public ODE_Sover
这是一个简化的示例,其中包含一些参数。为了避免传递许多参数和抽象,我更喜欢在 class.
中定义 ODE
我还为导数(dy/dt = f'(y) 的右侧)和积分器(这里只有欧拉积分器)使用了两个函数模板。
这是我想出的:
#include <iostream>
#include <assert.h>
#include <random>
#include <vector>
#include <string>
using std::string;
using std::vector;
class ODE_Solver;
class HarmonicOscillator;
typedef vector<double> dim1;
typedef dim1 (ODE_Solver::*derivative)(const dim1 &, dim1&, const double t);
typedef void (ODE_Solver::*Integrator)(derivative, dim1 &, dim1&, const double t);
class ODE_Solver
{
public:
ODE_Solver()
{}
double t;
double dt;
dim1 state;
dim1 dydt;
void integrate(Integrator integrator,
derivative ode_system,
const int N,
const double ti,
const double tf,
const double dt)
{
dim1 dydt(N);
const size_t num_steps = int((tf-ti) / dt);
for (size_t step = 0; step < num_steps; ++step)
{
double t = step * dt;
(this->*integrator)(ode_system, state, dydt, t);
// print state
}
}
void eulerIntegrator(derivative RHS, dim1 &y, dim1 &dydt, const double t)
{
int n = y.size();
(this->*RHS)(y, dydt, t);
for (int i = 0; i < n; i++)
y[i] += dydt[i] * dt;
}
};
class HarmonicOscillator: public ODE_Solver
{
public:
int N;
double dt;
double gamma;
string method;
dim1 state;
// constructor
HarmonicOscillator(int N,
double gamma,
dim1 state
) : N {N}, gamma{gamma}, state{state}
{ }
//---------------------------------------------------//
dim1 dampedOscillator(const dim1 &x, dim1&dxdt, const double t)
{
dxdt[0] = x[1];
dxdt[1] = -x[0] - gamma * x[1];
return dxdt;
}
};
//-------------------------------------------------------//
int main(int argc, char **argv)
{
const int N = 2;
const double gamma = 0.05;
const double t_iinit = 0.0;
const double t_final = 10.0;
const double dt = 0.01;
dim1 x0{0.0, 1.0};
HarmonicOscillator ho(N, gamma, x0);
ho.integrate(&ODE_Solver::eulerIntegrator,
&HarmonicOscillator::dampedOscillator,
N, t_iinit, t_final, dt);
return 0;
}
我收到这些错误:
example.cpp: In function ‘int main(int, char**)’:
example.cpp:93:18: error: cannot convert ‘dim1 (HarmonicOscillator::*)(const dim1&, dim1&, double)’ {aka ‘std::vector<double> (HarmonicOscillator::*)(const std::vector<double>&, std::vector<double>&, double)’} to ‘derivative’ {aka ‘std::vector<double> (ODE_Solver::*)(const std::vector<double>&, std::vector<double>&, double)’}
93 | &HarmonicOscillator::dampedOscillator,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| dim1 (HarmonicOscillator::*)(const dim1&, dim1&, double) {aka std::vector<double> (HarmonicOscillator::*)(const std::vector<double>&, std::vector<double>&, double)}
example.cpp:29:31: note: initializing argument 2 of ‘void ODE_Solver::integrate(Integrator, derivative, int, double, double, double)’
29 | derivative ode_system,
| ~~~~~~~~~~~^~~~~~~~~~
如果我在 ode 求解器的相同 class 处定义了 ode,它就可以工作 link to github。
那么你的想法是什么?
您的代码存在几个问题。首先,当您想将任意函数作为参数传递给其他函数时,请考虑使用 std::function
.
其次,应该使用继承来声明"is-a" relationships. Since a harmonic oscillator is not an ODE solver, don't use inheritance for this. There's also not a "has-a" relationship (so composition也是不合适的),而不是求解器作用于一个给定的函数,所以最合适的事情要做就是将谐振子作为参数传递给求解器函数。
代码的示例:
class HarmonicOscillator {
...
public:
...
double operator()(double t) {
...
return /* value at time t */;
}
};
double integrate(std::function<double(double)> func, double start, double end, double dt) {
double sum = 0;
for (double t = start; t < end; t += dt)
sum += func(t) * dt;
return sum;
}
然后你就这样称呼它:
HarmonicOscillator ho(...);
auto result = integrate(ho, t_iinit, t_final, dt);
上面的内容可能并不完全符合您的要求,但我认为这就是您应该追求的代码结构。
如果您希望能够处理不仅接受 double
和 return 一个 double
,而且接受任意类型的函数,您可以使 integrate()
成为一个模板:
template <typename Function, typename T>
auto integrate(Function func, T start, T end, T dt) {
decltype(func(start)) sum{};
for (T t = start; t < end; t += dt)
sum += func(t) * dt;
return sum;
}
如果您为支持算术运算的输入和输出值创建适当的类型,这将起作用,它不适用于您的 dim1
。我建议您尝试找到一个实现数学向量类型的库,例如 Eigen。
我觉得这个问题有点长所以我觉得还是先考虑一下简化版比较好:
有两个classA和B。B继承自A。B中有一个成员函数(add)需要运行使用A中的成员函数。
class A;
typedef int(A::*operation)(int c);
typedef void (A::*myfunction)(operation, int);
class A
{
public:
int a;
int b;
int do_something(myfunction f, operation op)
{
(this->*f)(op, 1);
}
void dummy_name(operation op, int a)
{
int c = (this->*op)(a);
}
};
class B : public A
{
public:
int a, b;
B(int a, int b): a(a), b(b) {}
int add(int c)
{
return a+b+c;
}
};
int main()
{
B inst_B(2, 5);
inst_B.do_something(&A::dummy_name, &B::add);
}
simple.cpp:45:41: error: cannot convert ‘int (B::*)(int)’ to ‘operation’ {aka ‘int (A::*)(int)’}
45 | inst_B.do_something(&A::dummy_name, &B::add);
| ^~~~~~~
| |
| int (B::*)(int)
simple.cpp:17:47: note: initializing argument 2 of ‘void A::do_something(myfunction, operation)’
17 | void do_something(myfunction f, operation op)
| ~~~~~~~~~~^~
为了编写一个简单的 ode 求解器并避免在模型 class 内部应对积分器,对于包含常微分方程组的每个模型,我将求解器和方程分成两个 class,而模型继承自 ode 求解器。
class HarmonicOscillator: public ODE_Sover
这是一个简化的示例,其中包含一些参数。为了避免传递许多参数和抽象,我更喜欢在 class.
中定义 ODE我还为导数(dy/dt = f'(y) 的右侧)和积分器(这里只有欧拉积分器)使用了两个函数模板。 这是我想出的:
#include <iostream>
#include <assert.h>
#include <random>
#include <vector>
#include <string>
using std::string;
using std::vector;
class ODE_Solver;
class HarmonicOscillator;
typedef vector<double> dim1;
typedef dim1 (ODE_Solver::*derivative)(const dim1 &, dim1&, const double t);
typedef void (ODE_Solver::*Integrator)(derivative, dim1 &, dim1&, const double t);
class ODE_Solver
{
public:
ODE_Solver()
{}
double t;
double dt;
dim1 state;
dim1 dydt;
void integrate(Integrator integrator,
derivative ode_system,
const int N,
const double ti,
const double tf,
const double dt)
{
dim1 dydt(N);
const size_t num_steps = int((tf-ti) / dt);
for (size_t step = 0; step < num_steps; ++step)
{
double t = step * dt;
(this->*integrator)(ode_system, state, dydt, t);
// print state
}
}
void eulerIntegrator(derivative RHS, dim1 &y, dim1 &dydt, const double t)
{
int n = y.size();
(this->*RHS)(y, dydt, t);
for (int i = 0; i < n; i++)
y[i] += dydt[i] * dt;
}
};
class HarmonicOscillator: public ODE_Solver
{
public:
int N;
double dt;
double gamma;
string method;
dim1 state;
// constructor
HarmonicOscillator(int N,
double gamma,
dim1 state
) : N {N}, gamma{gamma}, state{state}
{ }
//---------------------------------------------------//
dim1 dampedOscillator(const dim1 &x, dim1&dxdt, const double t)
{
dxdt[0] = x[1];
dxdt[1] = -x[0] - gamma * x[1];
return dxdt;
}
};
//-------------------------------------------------------//
int main(int argc, char **argv)
{
const int N = 2;
const double gamma = 0.05;
const double t_iinit = 0.0;
const double t_final = 10.0;
const double dt = 0.01;
dim1 x0{0.0, 1.0};
HarmonicOscillator ho(N, gamma, x0);
ho.integrate(&ODE_Solver::eulerIntegrator,
&HarmonicOscillator::dampedOscillator,
N, t_iinit, t_final, dt);
return 0;
}
我收到这些错误:
example.cpp: In function ‘int main(int, char**)’:
example.cpp:93:18: error: cannot convert ‘dim1 (HarmonicOscillator::*)(const dim1&, dim1&, double)’ {aka ‘std::vector<double> (HarmonicOscillator::*)(const std::vector<double>&, std::vector<double>&, double)’} to ‘derivative’ {aka ‘std::vector<double> (ODE_Solver::*)(const std::vector<double>&, std::vector<double>&, double)’}
93 | &HarmonicOscillator::dampedOscillator,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| dim1 (HarmonicOscillator::*)(const dim1&, dim1&, double) {aka std::vector<double> (HarmonicOscillator::*)(const std::vector<double>&, std::vector<double>&, double)}
example.cpp:29:31: note: initializing argument 2 of ‘void ODE_Solver::integrate(Integrator, derivative, int, double, double, double)’
29 | derivative ode_system,
| ~~~~~~~~~~~^~~~~~~~~~
如果我在 ode 求解器的相同 class 处定义了 ode,它就可以工作 link to github。 那么你的想法是什么?
您的代码存在几个问题。首先,当您想将任意函数作为参数传递给其他函数时,请考虑使用 std::function
.
其次,应该使用继承来声明"is-a" relationships. Since a harmonic oscillator is not an ODE solver, don't use inheritance for this. There's also not a "has-a" relationship (so composition也是不合适的),而不是求解器作用于一个给定的函数,所以最合适的事情要做就是将谐振子作为参数传递给求解器函数。
代码的示例:
class HarmonicOscillator {
...
public:
...
double operator()(double t) {
...
return /* value at time t */;
}
};
double integrate(std::function<double(double)> func, double start, double end, double dt) {
double sum = 0;
for (double t = start; t < end; t += dt)
sum += func(t) * dt;
return sum;
}
然后你就这样称呼它:
HarmonicOscillator ho(...);
auto result = integrate(ho, t_iinit, t_final, dt);
上面的内容可能并不完全符合您的要求,但我认为这就是您应该追求的代码结构。
如果您希望能够处理不仅接受 double
和 return 一个 double
,而且接受任意类型的函数,您可以使 integrate()
成为一个模板:
template <typename Function, typename T>
auto integrate(Function func, T start, T end, T dt) {
decltype(func(start)) sum{};
for (T t = start; t < end; t += dt)
sum += func(t) * dt;
return sum;
}
如果您为支持算术运算的输入和输出值创建适当的类型,这将起作用,它不适用于您的 dim1
。我建议您尝试找到一个实现数学向量类型的库,例如 Eigen。