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-2023 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};
55
57 VariantMap const &parameters) {
58 if (method == "update_flux_boundary_from_shape") {
60 if (get_lattice()->lattice()->get_ghost_layers() < 2) {
61 if (context()->get_comm().size() > 1) {
62 throw std::runtime_error("The number of ghostlayers should be > 1 "
63 "when using flux boundaries and mpi.");
64 }
65 runtimeWarningMsg() << "The number of ghostlayers should be > 1 when "
66 "using flux boundaries and mpi.";
67 }
68 });
70 std::ranges::for_each(values, [this](double &v) { v *= m_conv_flux; });
71
72 m_instance->update_flux_boundary_from_shape(
73 get_value<std::vector<int>>(parameters, "raster"), values);
74 return {};
75 }
76 if (method == "update_density_boundary_from_shape") {
78 std::ranges::for_each(values, [this](double &v) { v *= m_conv_density; });
79 m_instance->update_density_boundary_from_shape(
80 get_value<std::vector<int>>(parameters, "raster"), values);
81 return {};
82 }
83 if (method == "clear_flux_boundaries") {
84 m_instance->clear_flux_boundaries();
85 return {};
86 }
87 if (method == "clear_density_boundaries") {
88 m_instance->clear_density_boundaries();
89 return {};
90 }
91 if (method == "save_checkpoint") {
92 auto const path = get_value<std::filesystem::path>(parameters, "path");
93 auto const mode = get_value<int>(parameters, "mode");
94 save_checkpoint(path, mode);
95 return {};
96 }
97 if (method == "load_checkpoint") {
98 auto const path = get_value<std::filesystem::path>(parameters, "path");
99 auto const mode = get_value<int>(parameters, "mode");
100 load_checkpoint(path, mode);
101 return {};
102 }
104}
105
107 auto const diffusion = get_value<double>(params, "diffusion");
108 auto const ext_efield = get_value<Utils::Vector3d>(params, "ext_efield");
109 auto const density = get_value<double>(params, "density");
110 auto const kT = get_value<double>(params, "kT");
111 auto const gpu = get_value_or(params, "gpu", false);
114 auto const ek_density = density * m_conv_density;
115 auto const ek_kT = kT * m_conv_energy;
116 auto *make_new_instance = &::walberla::new_ek_walberla_cpu;
117 if (gpu) {
118 std::vector<std::string> required_features;
119 required_features.emplace_back("CUDA");
121#ifdef ESPRESSO_CUDA
122 make_new_instance = &::walberla::new_ek_walberla_gpu;
123#endif
124 }
125 m_instance = make_new_instance(
126 m_lattice->lattice(), ek_diffusion, ek_kT,
127 get_value<double>(params, "valency"), ek_ext_efield, ek_density,
128 get_value<bool>(params, "advection"),
129 get_value<bool>(params, "friction_coupling"),
130 get_value_or<bool>(params, "single_precision", gpu),
131 get_value_or<bool>(params, "thermalized", false),
132 static_cast<uint>(get_value_or<int>(params, "seed", 0)));
133 m_instance->ghost_communication();
134}
135
139 get_value_or<decltype(m_vtk_writers)>(params, "vtk_writers", {});
140 auto const agrid = get_value<double>(m_lattice->get_parameter("agrid"));
141 auto const density = get_value<double>(params, "density");
142 auto const kT = get_value<double>(params, "kT");
143 auto const tau = m_tau = get_value<double>(params, "tau");
144 auto const seed = get_value_or<int>(params, "seed", 0);
145 context()->parallel_try_catch([&]() {
146 if (get_value<bool>(params, "thermalized") and
147 not params.contains("seed")) {
148 throw std::invalid_argument(
149 "Parameter 'seed' is required for thermalized EKSpecies");
150 }
151 if (seed < 0) {
152 throw std::domain_error("Parameter 'seed' must be >= 0");
153 }
154 if (tau <= 0.) {
155 throw std::domain_error("Parameter 'tau' must be > 0");
156 }
157 if (kT < 0.) {
158 throw std::domain_error("Parameter 'kT' must be >= 0");
159 }
160 if (density < 0.) {
161 throw std::domain_error("Parameter 'density' must be >= 0");
162 }
163 m_conv_energy = Utils::int_pow<2>(tau) / Utils::int_pow<2>(agrid);
164 m_conv_diffusion = tau / Utils::int_pow<2>(agrid);
165 m_conv_ext_efield = Utils::int_pow<2>(tau) / agrid;
166 m_conv_density = Utils::int_pow<3>(agrid);
167 m_conv_flux = tau * Utils::int_pow<2>(agrid);
169 make_instance(params);
171 for (auto &vtk : m_vtk_writers) {
172 vtk->attach_to_lattice(m_instance, get_lattice_to_md_units_conversion());
173 }
174 });
175}
176
177void EKSpecies::load_checkpoint(std::filesystem::path const &path, int mode) {
178 auto &ek_obj = *m_instance;
179
180 auto const read_metadata = [&ek_obj](CheckpointFile &cpfile) {
181 auto const expected_grid_size = ek_obj.get_lattice().get_grid_dimensions();
185 std::stringstream message;
186 message << "grid dimensions mismatch, read [" << read_grid_size << "], "
187 << "expected [" << expected_grid_size << "].";
188 throw std::runtime_error(message.str());
189 }
190 };
191
192 auto const read_data = [&ek_obj](CheckpointFile &cpfile) {
193 auto const grid_size = ek_obj.get_lattice().get_grid_dimensions();
194 auto const i_max = grid_size[0];
195 auto const j_max = grid_size[1];
196 auto const k_max = grid_size[2];
198 for (int i = 0; i < i_max; i++) {
199 for (int j = 0; j < j_max; j++) {
200 for (int k = 0; k < k_max; k++) {
201 auto const ind = Utils::Vector3i{{i, j, k}};
202 cpfile.read(cpnode.density);
203 cpfile.read(cpnode.is_boundary_density);
204 if (cpnode.is_boundary_density) {
205 cpfile.read(cpnode.density_boundary);
206 }
207 cpfile.read(cpnode.is_boundary_flux);
208 if (cpnode.is_boundary_flux) {
209 cpfile.read(cpnode.flux_boundary);
210 }
211 ek_obj.set_node_density(ind, cpnode.density);
212 if (cpnode.is_boundary_density) {
213 ek_obj.set_node_density_boundary(ind, cpnode.density_boundary);
214 }
215 if (cpnode.is_boundary_flux) {
216 ek_obj.set_node_flux_boundary(ind, cpnode.flux_boundary);
217 }
218 }
219 }
220 }
221 };
222
223 auto const on_success = [&ek_obj]() { ek_obj.ghost_communication(); };
224
226 on_success);
227}
228
229void EKSpecies::save_checkpoint(std::filesystem::path const &path, int mode) {
230 auto &ek_obj = *m_instance;
231
232 auto const write_metadata = [&ek_obj,
233 mode](std::shared_ptr<CheckpointFile> cpfile_ptr,
234 Context const &context) {
235 auto const grid_size = ek_obj.get_lattice().get_grid_dimensions();
236 if (context.is_head_node()) {
237 cpfile_ptr->write(grid_size);
239 }
240 };
241
242 auto const on_failure = [](std::shared_ptr<CheckpointFile> const &,
243 Context const &context) {
244 if (context.is_head_node()) {
245 auto failure = true;
246 boost::mpi::broadcast(context.get_comm(), failure, 0);
247 }
248 };
249
250 auto const write_data = [&ek_obj,
251 mode](std::shared_ptr<CheckpointFile> cpfile_ptr,
252 Context const &context) {
253 auto const get_node_checkpoint =
254 [&](Utils::Vector3i const &ind) -> std::optional<EKWalberlaNodeState> {
255 auto const density = ek_obj.get_node_density(ind);
256 auto const is_b_d = ek_obj.get_node_is_density_boundary(ind);
257 auto const dens_b = ek_obj.get_node_density_at_boundary(ind);
258 auto const is_b_f = ek_obj.get_node_is_flux_boundary(ind);
259 auto const flux_b = ek_obj.get_node_flux_at_boundary(ind);
261 ((*is_b_d) ? dens_b.has_value() : true) and
262 ((*is_b_f) ? flux_b.has_value() : true)) {
265 cpnode.is_boundary_density = *is_b_d;
266 if (*is_b_d) {
267 cpnode.density_boundary = *dens_b;
268 }
269 cpnode.is_boundary_flux = *is_b_f;
270 if (*is_b_f) {
271 cpnode.flux_boundary = *flux_b;
272 }
273 return cpnode;
274 }
275 return std::nullopt;
276 };
277
278 auto failure = false;
279 auto const &comm = context.get_comm();
280 auto const is_head_node = context.is_head_node();
281 auto const unit_test_mode = (mode != static_cast<int>(CptMode::ascii)) and
282 (mode != static_cast<int>(CptMode::binary));
283 auto const grid_size = ek_obj.get_lattice().get_grid_dimensions();
284 auto const i_max = grid_size[0];
285 auto const j_max = grid_size[1];
286 auto const k_max = grid_size[2];
288 for (int i = 0; i < i_max; i++) {
289 for (int j = 0; j < j_max; j++) {
290 for (int k = 0; k < k_max; k++) {
291 auto const ind = Utils::Vector3i{{i, j, k}};
292 auto const result = get_node_checkpoint(ind);
293 if (!unit_test_mode) {
294 assert(1 == boost::mpi::all_reduce(comm, static_cast<int>(!!result),
295 std::plus<>()) &&
296 "Incorrect number of return values");
297 }
298 if (is_head_node) {
299 if (result) {
300 cpnode = *result;
301 } else {
302 comm.recv(boost::mpi::any_source, 42, cpnode);
303 }
304 auto &cpfile = *cpfile_ptr;
305 cpfile.write(cpnode.density);
306 cpfile.write(cpnode.is_boundary_density);
307 if (cpnode.is_boundary_density) {
308 cpfile.write(cpnode.density_boundary);
309 }
310 cpfile.write(cpnode.is_boundary_flux);
311 if (cpnode.is_boundary_flux) {
312 cpfile.write(cpnode.flux_boundary);
313 }
314 boost::mpi::broadcast(comm, failure, 0);
315 } else {
316 if (result) {
317 comm.send(0, 42, *result);
318 }
319 boost::mpi::broadcast(comm, failure, 0);
320 if (failure) {
321 return;
322 }
323 }
324 }
325 }
326 }
327 };
328
331}
332
333} // namespace ScriptInterface::walberla
334
335#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.
::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
Variant do_call_method(std::string const &method, VariantMap const &parameters) override
Definition EKSpecies.cpp:56
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 ...
#define runtimeWarningMsg()
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