Использование шагового двигателя boost :: numeri c :: odeint внутри сложного класса - PullRequest
1 голос
/ 05 мая 2020

У меня чрезвычайно запутанная система числовых уравнений скорости, которую должен решить класс ElectronSolver. Электронное состояние обрабатывается отдельным классом state_type, не показанным здесь. Упрощенный шаблон класса задачи выглядит как

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, как объявлено здесь, должен вызываться «в классе», поэтому его нельзя просто передать как ссылку. Этот вопрос обошел это, объявив sys как stati c, но здесь это не сработает, поскольку я полагаюсь на вызов других членов ElectronSolver в sys.

Существует небольшая веская причина, по которой мне понадобилось бы несколько экземпляров ElectronSolver, но я бы хотел оставить этот вариант на тот случай, если разработчики кода захотят иметь две разные электронные модели.

Насколько я могу скажите, у меня есть четыре варианта:

  1. сделать все sys нужно static (менее желательно из-за ElectronSolver наследования от других классов, но выполнимо)
  2. Построить некоторые своего рода оболочка для функции sys (возможно, снижение производительности и, что более важно, я не знаю, как это сделать)
  3. Самостоятельная реализация степпера ODE, чтобы избежать головной боли при использовании boost.
  4. ????

Какое решение дает лучшую сделку между - Производительность (хотя самым большим узким местом производительности является вероятно время, которое требуется * 104 0 * выполнить) - Элегантность кода - Модульность?

1 Ответ

2 голосов
/ 24 августа 2020

Хотите ли вы продолжать использовать ускорение - это решение, которое можете принять только вы, но легко создать оболочку без снижения производительности.

Вы захотите обернуть sys в лямбда который захватывает this. Это не должно сказаться на производительности, так как лямбда будет встроена при включении оптимизации.

Вы бы назвали это так:

abm.initialize(
    [this](const state_type& s, state_type& sdot, const double t) {
        this->sys(s, sdot, t);
    },
    Y[0],
    T[0],
    dt
);

Лямбда - это, по сути, неявная структура оболочки который содержит ссылку на this и определяет operator()(const state_type& s, state_type& sdot, const double t).

Я создал пример в godbolt , который показывает это, упрощая и заполняя ваш код там, где это необходимо для примера. Если вы измените оптимизацию между -O0 и -O3, вы увидите, что лямбда-код удален, а внутренний метод полностью встроен.

Другой вариант - использовать 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 по ссылке, а не по копии. Этот метод также будет иметь нулевые накладные расходы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...