在复杂的 class 中使用 boost::numeric::odeint 步进器
Using a boost::numeric::odeint stepper inside a complicated class
我有一个极其复杂的数值速率方程组,将由 class ElectronSolver
求解。电子态由单独的 class state_type
处理,此处未显示。
问题 class 的简化模板显示为
ElectronSolver.h
class ElectronSolver {
public:
ElectronSolver(const char* filename, ofstream& log);
void solve();
void print(const std::string& fname);
std::vector<double> T; // Times, in fs
std::vector<state_type> Y; // stores the state_t's
private:
// steps, State, value, Derivative, Time, Algebra
adams_bashforth_moulton< 5, state_type, double, state_type, double, vector_space_algebra > abm;
void set_initial_conditions();
// Model parameters
PhotonFlux pf;
void sys(const state_type& s, state_type& sdot, const double t);
};
ElectronSolver.cpp
void ElectronSolver::set_initial_conditions(){
// Set the initial T such that pulse peak occurs at T=0
T[0] = -timespan/2;
Y[0] = state_type(Store, num_elec_points);
abm.initialize( sys, Y[0], T[0], dt ); // This line produces an error
}
void ElectronSolver::sys(const state_type& s, state_type& sdot, const double t){
// complicated system modifying sdot
sdot.flux += pf(t)*s.flux;
}
但是,一些研究揭示了为什么标记的行会引发编译错误。
据我所知,此处声明的 sys
必须称为 "on a class",因此不能简单地作为引用传递。 This question 通过将 sys
声明为静态来解决这个问题,但这在这里不起作用,因为我依赖于在 sys
.[=27 中调用 ElectronSolver
的其他成员=]
我需要多个 ElectronSolver 实例的理由很少,但我想保留该选项以防任何代码维护者想要两个不同的电子模型。
据我所知,我有四个选择:
- 满足
sys
需要的一切 static
(由于 ElectronSolver
从其他 class 继承,因此不太理想,但可行)
- 为
sys
函数构造某种包装器(可能会影响性能,更重要的是,我不知道该怎么做)
- 自己实现 ODE 步进器以避免使用
boost
的麻烦。
- ????
哪种解决方案最划算
- 性能(虽然最大的性能瓶颈是 可能 执行所花费的时间 sys
- 代码优雅
- 模块化
?
您是否要继续使用 boost 是您唯一可以做出的决定,但是创建一个不影响性能的包装器很容易。
您需要将 sys
包装在捕获 this
的 lambda 中。这应该不会影响性能,因为当打开优化时,lambda 将被内联。
你可以这样称呼它:
abm.initialize(
[this](const state_type& s, state_type& sdot, const double t) {
this->sys(s, sdot, t);
},
Y[0],
T[0],
dt
);
lambda 基本上是一个隐式包装结构,它包含对 this
的引用并定义 operator()(const state_type& s, state_type& sdot, const double t)
.
我创建了一个 example in godbolt 来展示这一点,在示例需要的地方简化并填写您的代码。如果您更改 -O0
和 -O3
之间的优化,您可以看到 lambda 代码被剥离并且内部方法被完全内联。
另一种选择是使用 std::bind
从成员函数中创建一个裸函数:
abm.initialize(
std::bind(&ElectronSolver::sys, std::ref(*this), _1, _2, _3),
Y[0],
T[0],
dt
);
连同其他地方的这个:
#include <functional>
using namespace std::placeholders;
将所有符号放入范围。 std::bind
创建一个新函数,其中一些参数已经填充。在这种情况下,隐式第一个参数,即对象本身,用对 this
的引用填充。其余参数被赋予特殊的占位符,以指示新函数将用其参数填充它们。 std::ref
强制通过引用而不是复制来获取 this
。此方法也将具有零开销。
我有一个极其复杂的数值速率方程组,将由 class ElectronSolver
求解。电子态由单独的 class state_type
处理,此处未显示。
问题 class 的简化模板显示为
ElectronSolver.h
class ElectronSolver {
public:
ElectronSolver(const char* filename, ofstream& log);
void solve();
void print(const std::string& fname);
std::vector<double> T; // Times, in fs
std::vector<state_type> Y; // stores the state_t's
private:
// steps, State, value, Derivative, Time, Algebra
adams_bashforth_moulton< 5, state_type, double, state_type, double, vector_space_algebra > abm;
void set_initial_conditions();
// Model parameters
PhotonFlux pf;
void sys(const state_type& s, state_type& sdot, const double t);
};
ElectronSolver.cpp
void ElectronSolver::set_initial_conditions(){
// Set the initial T such that pulse peak occurs at T=0
T[0] = -timespan/2;
Y[0] = state_type(Store, num_elec_points);
abm.initialize( sys, Y[0], T[0], dt ); // This line produces an error
}
void ElectronSolver::sys(const state_type& s, state_type& sdot, const double t){
// complicated system modifying sdot
sdot.flux += pf(t)*s.flux;
}
但是,一些研究揭示了为什么标记的行会引发编译错误。
据我所知,此处声明的 sys
必须称为 "on a class",因此不能简单地作为引用传递。 This question 通过将 sys
声明为静态来解决这个问题,但这在这里不起作用,因为我依赖于在 sys
.[=27 中调用 ElectronSolver
的其他成员=]
我需要多个 ElectronSolver 实例的理由很少,但我想保留该选项以防任何代码维护者想要两个不同的电子模型。
据我所知,我有四个选择:
- 满足
sys
需要的一切static
(由于ElectronSolver
从其他 class 继承,因此不太理想,但可行) - 为
sys
函数构造某种包装器(可能会影响性能,更重要的是,我不知道该怎么做) - 自己实现 ODE 步进器以避免使用
boost
的麻烦。 - ????
哪种解决方案最划算
- 性能(虽然最大的性能瓶颈是 可能 执行所花费的时间 sys
- 代码优雅
- 模块化
?
您是否要继续使用 boost 是您唯一可以做出的决定,但是创建一个不影响性能的包装器很容易。
您需要将 sys
包装在捕获 this
的 lambda 中。这应该不会影响性能,因为当打开优化时,lambda 将被内联。
你可以这样称呼它:
abm.initialize(
[this](const state_type& s, state_type& sdot, const double t) {
this->sys(s, sdot, t);
},
Y[0],
T[0],
dt
);
lambda 基本上是一个隐式包装结构,它包含对 this
的引用并定义 operator()(const state_type& s, state_type& sdot, const double t)
.
我创建了一个 example in godbolt 来展示这一点,在示例需要的地方简化并填写您的代码。如果您更改 -O0
和 -O3
之间的优化,您可以看到 lambda 代码被剥离并且内部方法被完全内联。
另一种选择是使用 std::bind
从成员函数中创建一个裸函数:
abm.initialize(
std::bind(&ElectronSolver::sys, std::ref(*this), _1, _2, _3),
Y[0],
T[0],
dt
);
连同其他地方的这个:
#include <functional>
using namespace std::placeholders;
将所有符号放入范围。 std::bind
创建一个新函数,其中一些参数已经填充。在这种情况下,隐式第一个参数,即对象本身,用对 this
的引用填充。其余参数被赋予特殊的占位符,以指示新函数将用其参数填充它们。 std::ref
强制通过引用而不是复制来获取 this
。此方法也将具有零开销。