ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
script_interface/walberla/EKContainer.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2022-2026 The ESPResSo project
3 *
4 * This file is part of ESPResSo.
5 *
6 * ESPResSo is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ESPResSo is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
22#include <config/config.hpp>
23
24#ifdef ESPRESSO_WALBERLA
25
26#include "EKFFT.hpp"
27#include "EKNone.hpp"
28#include "EKReactions.hpp"
29#include "EKSpecies.hpp"
30
33
36#include "core/ek/Solver.hpp"
38
42
43#include <cassert>
44#include <memory>
45#include <optional>
46#include <stdexcept>
47#include <string>
48#include <variant>
49
51
52class EKContainer : public ObjectList<EKSpecies, EK::Container> {
54 using Base::value_type;
55
56 std::variant<
57#ifdef ESPRESSO_WALBERLA_FFT
58 std::shared_ptr<EKFFT>,
59#endif
60 std::shared_ptr<EKNone>>
61 m_poisson_solver;
62
63 std::shared_ptr<EKReactions> m_ek_reactions;
64 std::shared_ptr<::EK::EKWalberla> m_ek_instance;
65 std::shared_ptr<::EK::EKWalberla::ek_container_type> m_ek_container;
66 bool m_is_active;
67
68 auto get_precision(decltype(m_poisson_solver) const &solver) const {
69 std::optional<bool> result = std::nullopt;
70 std::visit(
71 [&](auto const &ptr) {
72 using SolverType = std::remove_cvref_t<decltype(ptr)>::element_type;
73 if (ptr and not std::is_same_v<SolverType, EKNone>) {
74 result = get_value<bool>(ptr->get_parameter("single_precision"));
75 }
76 },
77 solver);
78 return result;
79 }
80
81 auto get_precision(value_type const &species) const {
82 return get_value<bool>(species->get_parameter("single_precision"));
83 }
84
85 auto get_precision(std::vector<value_type> const &species_list) const {
86 std::optional<bool> result = std::nullopt;
87 for (auto const &species : species_list) {
88 result = get_precision(species);
89 }
90 return result;
91 }
92
93 bool has_in_core(value_type const &obj_ptr) const override {
94 return m_ek_container->contains(obj_ptr->get_ekinstance());
95 }
96 void add_in_core(value_type const &obj_ptr) override {
97 context()->parallel_try_catch([this, &obj_ptr]() {
98 auto const prec_new_species = get_precision(obj_ptr);
99 auto const prec_old_species = get_precision(elements());
100 auto const prec_solver = get_precision(m_poisson_solver);
103 throw std::runtime_error(
104 "Cannot mix single and double precision kernels");
105 }
106 auto const tau_container = m_ek_container->get_tau();
107 auto const tau_species = get_value<double>(obj_ptr->get_parameter("tau"));
108 if (tau_species != tau_container) {
109 throw std::runtime_error(
110 "EK species and EK container must have the same tau");
111 }
112 ek_throw_if_expired(obj_ptr->get_mpi_cart_comm_observer());
113 m_ek_container->add(obj_ptr->get_ekinstance());
114 });
115 }
116 void remove_in_core(value_type const &obj_ptr) final {
117 m_ek_container->remove(obj_ptr->get_ekinstance());
118 }
119
120 struct GetPoissonSolverAsVariant {
121 template <typename T>
122 auto operator()(std::shared_ptr<T> const &solver) const {
123 return (solver) ? Variant{solver} : Variant{none};
124 }
125 };
126
127 Variant get_solver() const {
128 return std::visit(GetPoissonSolverAsVariant(), m_poisson_solver);
129 }
130
131 struct GetPoissonSolverCoreInstance {
132 template <typename T>
133 std::shared_ptr<::walberla::PoissonSolver>
134 operator()(std::shared_ptr<T> const &solver) const {
135 return solver->get_instance();
136 }
137 };
138
139 auto extract_solver(Variant const &v) {
140 std::optional<decltype(m_poisson_solver)> solver;
142 if (auto ptr = std::dynamic_pointer_cast<EKNone>(so_ptr)) {
143 solver = std::move(ptr);
144 }
145#ifdef ESPRESSO_WALBERLA_FFT
146 else if (auto ptr = std::dynamic_pointer_cast<EKFFT>(so_ptr)) {
147 solver = std::move(ptr);
148 }
149#endif // ESPRESSO_WALBERLA_FFT
150 if (not solver.has_value()) {
151 context()->parallel_try_catch([]() {
152 throw std::invalid_argument("EK solver is of the wrong type");
153 });
154 }
155 return *solver;
156 }
157
158 void set_solver(Variant const &v) {
159 auto new_solver = extract_solver(v);
160 auto handle = std::visit(GetPoissonSolverCoreInstance{}, new_solver);
161 context()->parallel_try_catch([&]() {
162 auto const prec_solver = get_precision(new_solver);
163 auto const prec_species = get_precision(elements());
165 throw std::runtime_error(
166 "Cannot mix single and double precision kernels");
167 }
168 m_ek_container->set_poisson_solver(handle);
169 });
170 m_poisson_solver = new_solver;
171 }
172
173public:
175 add_parameters({
177 [this]() { return m_ek_container->get_tau(); }},
178 {"solver", [this](Variant const &v) { set_solver(v); },
179 [this]() { return get_solver(); }},
180 {"reactions", AutoParameter::read_only,
181 [this]() { return m_ek_reactions; }},
182 {"is_active", AutoParameter::read_only,
183 [this]() { return m_is_active; }},
185 [this]() { return m_ek_container->is_gpu(); }},
186 });
187 }
188
189 ~EKContainer() override { do_destruct(); }
190
191 void do_construct(VariantMap const &params) override {
192 m_is_active = false;
193 auto const tau = get_value<double>(params, "tau");
194 context()->parallel_try_catch([&]() {
195 if (tau <= 0.) {
196 throw std::domain_error("Parameter 'tau' must be > 0");
197 }
198 if (not params.contains("solver")) {
199 throw std::runtime_error("Parameter 'solver' is required; use EKNone "
200 "if all species are electrically neutral");
201 }
202 });
203 m_poisson_solver = extract_solver(params.at("solver"));
204 m_ek_container = std::make_shared<::EK::EKWalberla::ek_container_type>(
205 tau, std::visit(GetPoissonSolverCoreInstance{}, m_poisson_solver));
206 m_ek_reactions = get_value<decltype(m_ek_reactions)>(params, "reactions");
207 m_ek_instance = std::make_shared<::EK::EKWalberla>(
208 m_ek_container, m_ek_reactions->get_handle());
209 // EK species must be added after tau
210 Base::do_construct(params);
211 }
212
213protected:
214 Variant do_call_method(std::string const &method,
215 VariantMap const &parameters) override {
216 if (method == "activate") {
217 get_system().ek.set<::EK::EKWalberla>(m_ek_instance);
218 m_is_active = true;
219 return {};
220 }
221 if (method == "deactivate") {
222 get_system().ek.reset();
223 m_is_active = false;
224 return {};
225 }
226
228 }
229};
230
231} // namespace ScriptInterface::walberla
232
233#endif // ESPRESSO_WALBERLA
Owning list of object handles.
void do_construct(VariantMap const &params) override
Variant do_call_method(std::string const &method, VariantMap const &parameters) override
bool has_in_core(value_type const &obj_ptr) const override
Variant do_call_method(std::string const &method, VariantMap const &parameters) override
This file contains the asynchronous MPI communication.
void ek_throw_if_expired(std::optional< ResourceObserver > const &mpi_obs)
Definition EKSpecies.hpp:57
T get_value(Variant const &v)
Extract value of specific type T from a Variant.
std::unordered_map< std::string, Variant > VariantMap
Definition Variant.hpp:133
make_recursive_variant< ObjectRef > Variant
Possible types for parameters.
Definition Variant.hpp:131
constexpr const None none
None-"literal".
Definition Variant.hpp:126
void set(Args... args)
Set the EK solver.
void reset()
Remove the EK solver.
Definition ek/Solver.cpp:60
static constexpr const ReadOnly read_only
Recursive variant implementation.
Definition Variant.hpp:84