ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
EKSpecies.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2021-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#include <config/config.hpp>
20
21#ifdef ESPRESSO_WALBERLA
22
23#include "EKSpecies.hpp"
26#include "errorhandling.hpp"
27
29
33
34#include <boost/mpi.hpp>
35#include <boost/mpi/collectives/all_reduce.hpp>
36#include <boost/mpi/collectives/broadcast.hpp>
37
38#include <algorithm>
39#include <cassert>
40#include <filesystem>
41#include <functional>
42#include <memory>
43#include <optional>
44#include <sstream>
45#include <stdexcept>
46#include <string>
47#include <vector>
48
50
51std::unordered_map<std::string, int> const EKVTKHandle::obs_map = {
52 {"density", static_cast<int>(EKOutputVTK::density)},
53 {"flux", static_cast<int>(EKOutputVTK::flux)},
54 {"boundary", static_cast<int>(EKOutputVTK::boundary)},
55};
56
58 VariantMap const &parameters) {
59 if (method == "update_flux_boundary_from_shape") {
62 std::ranges::for_each(values, [this](double &v) { v *= m_conv_flux; });
63
64 m_instance->update_flux_boundary_from_shape(
65 get_value<std::vector<int>>(parameters, "raster"), values);
66 return {};
67 }
68 if (method == "update_density_boundary_from_shape") {
70 std::ranges::for_each(values, [this](double &v) { v *= m_conv_density; });
71 m_instance->update_density_boundary_from_shape(
72 get_value<std::vector<int>>(parameters, "raster"), values);
73 return {};
74 }
75 if (method == "clear_flux_boundaries") {
76 m_instance->clear_flux_boundaries();
77 return {};
78 }
79 if (method == "clear_density_boundaries") {
80 m_instance->clear_density_boundaries();
81 return {};
82 }
83 if (method == "save_checkpoint") {
84 auto const path = get_value<std::filesystem::path>(parameters, "path");
85 auto const mode = get_value<int>(parameters, "mode");
86 save_checkpoint(path, mode);
87 return {};
88 }
89 if (method == "load_checkpoint") {
90 auto const path = get_value<std::filesystem::path>(parameters, "path");
91 auto const mode = get_value<int>(parameters, "mode");
92 load_checkpoint(path, mode);
93 return {};
94 }
96}
97
99 auto const diffusion = get_value<double>(params, "diffusion");
100 auto const ext_efield = get_value<Utils::Vector3d>(params, "ext_efield");
101 auto const density = get_value<double>(params, "density");
102 auto const kT = get_value<double>(params, "kT");
103 auto const gpu = get_value_or(params, "gpu", false);
106 auto const ek_density = density * m_conv_density;
107 auto const ek_kT = kT * m_conv_energy;
108 auto *make_new_instance = &::walberla::new_ek_walberla_cpu;
109 if (gpu) {
110 std::vector<std::string> required_features;
111 required_features.emplace_back("CUDA");
113#ifdef ESPRESSO_CUDA
114 make_new_instance = &::walberla::new_ek_walberla_gpu;
115#endif
116 }
117 m_instance = make_new_instance(
118 m_lattice->lattice(), ek_diffusion, ek_kT,
119 get_value<double>(params, "valency"), ek_ext_efield, ek_density,
120 get_value<bool>(params, "advection"),
121 get_value<bool>(params, "friction_coupling"),
122 get_value_or<bool>(params, "single_precision", gpu),
123 get_value_or<bool>(params, "thermalized", false),
124 static_cast<uint>(get_value_or<int>(params, "seed", 0)));
125 m_instance->ghost_communication();
126}
127
131 get_value_or<decltype(m_vtk_writers)>(params, "vtk_writers", {});
132 auto const agrid = get_value<double>(m_lattice->get_parameter("agrid"));
133 auto const density = get_value<double>(params, "density");
134 auto const kT = get_value<double>(params, "kT");
135 auto const tau = m_tau = get_value<double>(params, "tau");
136 auto const seed = get_value_or<int>(params, "seed", 0);
137 context()->parallel_try_catch([&]() {
138 if (get_value<bool>(params, "thermalized") and
139 not params.contains("seed")) {
140 throw std::invalid_argument(
141 "Parameter 'seed' is required for thermalized EKSpecies");
142 }
143 if (seed < 0) {
144 throw std::domain_error("Parameter 'seed' must be >= 0");
145 }
146 if (tau <= 0.) {
147 throw std::domain_error("Parameter 'tau' must be > 0");
148 }
149 if (kT < 0.) {
150 throw std::domain_error("Parameter 'kT' must be >= 0");
151 }
152 if (density < 0.) {
153 throw std::domain_error("Parameter 'density' must be >= 0");
154 }
155 m_conv_energy = Utils::int_pow<2>(tau) / Utils::int_pow<2>(agrid);
156 m_conv_diffusion = tau / Utils::int_pow<2>(agrid);
157 m_conv_ext_efield = Utils::int_pow<2>(tau) / agrid;
158 m_conv_density = Utils::int_pow<3>(agrid);
159 m_conv_flux = tau * Utils::int_pow<2>(agrid);
161 make_instance(params);
163 for (auto &vtk : m_vtk_writers) {
164 vtk->attach_to_lattice(m_instance, get_lattice_to_md_units_conversion());
165 }
166 });
167}
168
169void EKSpecies::load_checkpoint(std::filesystem::path const &path, int mode) {
170 auto &ek_obj = *m_instance;
171
172 auto const read_metadata = [&ek_obj](CheckpointFile &cpfile) {
173 auto const expected_grid_size = ek_obj.get_lattice().get_grid_dimensions();
177 std::stringstream message;
178 message << "grid dimensions mismatch, read [" << read_grid_size << "], "
179 << "expected [" << expected_grid_size << "].";
180 throw std::runtime_error(message.str());
181 }
182 };
183
184 auto const read_data = [&ek_obj](CheckpointFile &cpfile) {
185 auto const grid_size = ek_obj.get_lattice().get_grid_dimensions();
186 auto const i_max = grid_size[0];
187 auto const j_max = grid_size[1];
188 auto const k_max = grid_size[2];
190 for (int i = 0; i < i_max; i++) {
191 for (int j = 0; j < j_max; j++) {
192 for (int k = 0; k < k_max; k++) {
193 auto const ind = Utils::Vector3i{{i, j, k}};
194 cpfile.read(cpnode.density);
195 cpfile.read(cpnode.is_boundary_density);
196 if (cpnode.is_boundary_density) {
197 cpfile.read(cpnode.density_boundary);
198 }
199 cpfile.read(cpnode.is_boundary_flux);
200 if (cpnode.is_boundary_flux) {
201 cpfile.read(cpnode.flux_boundary);
202 }
203 ek_obj.set_node_density(ind, cpnode.density);
204 if (cpnode.is_boundary_density) {
205 ek_obj.set_node_density_boundary(ind, cpnode.density_boundary);
206 }
207 if (cpnode.is_boundary_flux) {
208 ek_obj.set_node_flux_boundary(ind, cpnode.flux_boundary);
209 }
210 }
211 }
212 }
213 };
214
215 auto const on_success = [&ek_obj]() { ek_obj.ghost_communication(); };
216
218 on_success);
219}
220
221void EKSpecies::save_checkpoint(std::filesystem::path const &path, int mode) {
222 auto &ek_obj = *m_instance;
223
224 auto const write_metadata = [&ek_obj,
225 mode](std::shared_ptr<CheckpointFile> cpfile_ptr,
226 Context const &context) {
227 auto const grid_size = ek_obj.get_lattice().get_grid_dimensions();
228 if (context.is_head_node()) {
229 cpfile_ptr->write(grid_size);
231 }
232 };
233
234 auto const on_failure = [](std::shared_ptr<CheckpointFile> const &,
235 Context const &context) {
236 if (context.is_head_node()) {
237 auto failure = true;
238 boost::mpi::broadcast(context.get_comm(), failure, 0);
239 }
240 };
241
242 auto const write_data = [&ek_obj,
243 mode](std::shared_ptr<CheckpointFile> cpfile_ptr,
244 Context const &context) {
245 auto const get_node_checkpoint =
246 [&](Utils::Vector3i const &ind) -> std::optional<EKWalberlaNodeState> {
247 auto const density = ek_obj.get_node_density(ind);
248 auto const is_b_d = ek_obj.get_node_is_density_boundary(ind);
249 auto const dens_b = ek_obj.get_node_density_at_boundary(ind);
250 auto const is_b_f = ek_obj.get_node_is_flux_boundary(ind);
251 auto const flux_b = ek_obj.get_node_flux_at_boundary(ind);
253 ((*is_b_d) ? dens_b.has_value() : true) and
254 ((*is_b_f) ? flux_b.has_value() : true)) {
257 cpnode.is_boundary_density = *is_b_d;
258 if (*is_b_d) {
259 cpnode.density_boundary = *dens_b;
260 }
261 cpnode.is_boundary_flux = *is_b_f;
262 if (*is_b_f) {
263 cpnode.flux_boundary = *flux_b;
264 }
265 return cpnode;
266 }
267 return std::nullopt;
268 };
269
270 auto failure = false;
271 auto const &comm = context.get_comm();
272 auto const is_head_node = context.is_head_node();
273 auto const unit_test_mode = (mode != static_cast<int>(CptMode::ascii)) and
274 (mode != static_cast<int>(CptMode::binary));
275 auto const grid_size = ek_obj.get_lattice().get_grid_dimensions();
276 auto const i_max = grid_size[0];
277 auto const j_max = grid_size[1];
278 auto const k_max = grid_size[2];
280 for (int i = 0; i < i_max; i++) {
281 for (int j = 0; j < j_max; j++) {
282 for (int k = 0; k < k_max; k++) {
283 auto const ind = Utils::Vector3i{{i, j, k}};
284 auto const result = get_node_checkpoint(ind);
285 if (!unit_test_mode) {
286 assert(1 == boost::mpi::all_reduce(comm, static_cast<int>(!!result),
287 std::plus<>()) &&
288 "Incorrect number of return values");
289 }
290 if (is_head_node) {
291 if (result) {
292 cpnode = *result;
293 } else {
294 comm.recv(boost::mpi::any_source, 42, cpnode);
295 }
296 auto &cpfile = *cpfile_ptr;
297 cpfile.write(cpnode.density);
298 cpfile.write(cpnode.is_boundary_density);
299 if (cpnode.is_boundary_density) {
300 cpfile.write(cpnode.density_boundary);
301 }
302 cpfile.write(cpnode.is_boundary_flux);
303 if (cpnode.is_boundary_flux) {
304 cpfile.write(cpnode.flux_boundary);
305 }
306 boost::mpi::broadcast(comm, failure, 0);
307 } else {
308 if (result) {
309 comm.send(0, 42, *result);
310 }
311 boost::mpi::broadcast(comm, failure, 0);
312 if (failure) {
313 return;
314 }
315 }
316 }
317 }
318 }
319 };
320
323}
324
325} // namespace ScriptInterface::walberla
326
327#endif // ESPRESSO_WALBERLA
virtual void parallel_try_catch(std::function< void()> const &cb) const =0
virtual bool is_head_node() const =0
virtual boost::mpi::communicator const & get_comm() const =0
Context * context() const
Responsible context.
void flux_boundary_ghost_layer_size_sanity_check() const
::LatticeModel::units_map get_lattice_to_md_units_conversion() const override
std::optional< ResourceObserver > m_mpi_cart_comm_observer
Definition EKSpecies.hpp:67
void make_instance(VariantMap const &params) override
Definition EKSpecies.cpp:98
Variant do_call_method(std::string const &method, VariantMap const &parameters) override
Definition EKSpecies.cpp:57
void do_construct(VariantMap const &params) override
Variant do_call_method(std::string const &method_name, VariantMap const &params) override
This file contains the errorhandling code for severe errors, like a broken bond or illegal parameter ...
void check_features(std::vector< std::string > const &features)
Definition CodeInfo.cpp:74
void save_checkpoint_common(Context const &context, std::string const classname, std::filesystem::path const &path, int mode, F1 const write_metadata, F2 const write_data, F3 const on_failure)
void unit_test_handle(int mode)
Inject code for unit tests.
void load_checkpoint_common(Context const &context, std::string const classname, std::filesystem::path const &path, int mode, F1 const read_metadata, F2 const read_data, F3 const on_success)
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
T get_value_or(VariantMap const &vals, std::string const &name, T const &default_)
Get a value from a VariantMap by name, or return a default value if it does not exist.
std::shared_ptr< EKinWalberlaBase > new_ek_walberla_cpu(std::shared_ptr< LatticeWalberla > const &lattice, double diffusion, double kT, double valency, Utils::Vector3d ext_efield, double density, bool advection, bool friction_coupling, bool single_precision, bool thermalized, unsigned int seed)
std::shared_ptr< EKinWalberlaBase > new_ek_walberla_gpu(std::shared_ptr< LatticeWalberla > const &lattice, double diffusion, double kT, double valency, Utils::Vector3d ext_efield, double density, bool advection, bool friction_coupling, bool single_precision, bool thermalized, unsigned int seed)
ResourceObserver get_mpi_cart_comm_observer()
Get an observer on waLBerla's MPI Cartesian communicator status.
Checkpoint data for a EK node.
Recursive variant implementation.
Definition Variant.hpp:84