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
30#include <boost/mpi.hpp>
31#include <boost/mpi/collectives/all_reduce.hpp>
32#include <boost/mpi/collectives/broadcast.hpp>
33
34#include <algorithm>
35#include <cassert>
36#include <filesystem>
37#include <functional>
38#include <memory>
39#include <optional>
40#include <sstream>
41#include <stdexcept>
42#include <string>
43#include <vector>
44
46
47std::unordered_map<std::string, int> const EKVTKHandle::obs_map = {
48 {"density", static_cast<int>(EKOutputVTK::density)},
49 {"flux", static_cast<int>(EKOutputVTK::flux)},
50};
51
53 VariantMap const &parameters) {
54 if (method == "update_flux_boundary_from_shape") {
56 if (get_lattice()->lattice()->get_ghost_layers() < 2) {
57 if (context()->get_comm().size() > 1) {
58 throw std::runtime_error("The number of ghostlayers should be > 1 "
59 "when using flux boundaries and mpi.");
60 }
61 runtimeWarningMsg() << "The number of ghostlayers should be > 1 when "
62 "using flux boundaries and mpi.";
63 }
64 });
66 std::ranges::for_each(values, [this](double &v) { v *= m_conv_flux; });
67
68 m_instance->update_flux_boundary_from_shape(
69 get_value<std::vector<int>>(parameters, "raster"), values);
70 return {};
71 }
72 if (method == "update_density_boundary_from_shape") {
74 std::ranges::for_each(values, [this](double &v) { v *= m_conv_density; });
75 m_instance->update_density_boundary_from_shape(
76 get_value<std::vector<int>>(parameters, "raster"), values);
77 return {};
78 }
79 if (method == "clear_flux_boundaries") {
80 m_instance->clear_flux_boundaries();
81 return {};
82 }
83 if (method == "clear_density_boundaries") {
84 m_instance->clear_density_boundaries();
85 return {};
86 }
87 if (method == "save_checkpoint") {
88 auto const path = get_value<std::filesystem::path>(parameters, "path");
89 auto const mode = get_value<int>(parameters, "mode");
90 save_checkpoint(path, mode);
91 return {};
92 }
93 if (method == "load_checkpoint") {
94 auto const path = get_value<std::filesystem::path>(parameters, "path");
95 auto const mode = get_value<int>(parameters, "mode");
96 load_checkpoint(path, mode);
97 return {};
98 }
100}
101
103 auto const diffusion = get_value<double>(params, "diffusion");
104 auto const ext_efield = get_value<Utils::Vector3d>(params, "ext_efield");
105 auto const density = get_value<double>(params, "density");
106 auto const kT = get_value<double>(params, "kT");
109 auto const ek_density = density * m_conv_density;
110 auto const ek_kT = kT * m_conv_energy;
112 m_lattice->lattice(), ek_diffusion, ek_kT,
114 get_value<bool>(params, "advection"),
115 get_value<bool>(params, "friction_coupling"),
116 get_value<bool>(params, "single_precision"),
117 get_value_or<bool>(params, "thermalized", false),
118 static_cast<uint>(get_value_or<int>(params, "seed", 0)));
119 m_instance->ghost_communication();
120}
121
122#ifdef ESPRESSO_CUDA
124 auto const diffusion = get_value<double>(params, "diffusion");
125 auto const ext_efield = get_value<Utils::Vector3d>(params, "ext_efield");
126 auto const density = get_value<double>(params, "density");
127 auto const kT = get_value<double>(params, "kT");
130 auto const ek_density = density * m_conv_density;
131 auto const ek_kT = kT * m_conv_energy;
133 m_lattice->lattice(), ek_diffusion, ek_kT,
135 get_value<bool>(params, "advection"),
136 get_value<bool>(params, "friction_coupling"),
137 get_value<bool>(params, "single_precision"),
138 get_value_or<bool>(params, "thermalized", false),
139 static_cast<uint>(get_value_or<int>(params, "seed", 0)));
140 m_instance->ghost_communication();
141}
142#endif // ESPRESSO_CUDA
143
148 auto const agrid = get_value<double>(m_lattice->get_parameter("agrid"));
149 auto const density = get_value<double>(params, "density");
150 auto const kT = get_value<double>(params, "kT");
151 auto const tau = m_tau = get_value<double>(params, "tau");
152 auto const seed = get_value_or<int>(params, "seed", 0);
153 context()->parallel_try_catch([&]() {
154 if (get_value<bool>(params, "thermalized") and
155 not params.contains("seed")) {
156 throw std::invalid_argument(
157 "Parameter 'seed' is required for thermalized EKSpecies");
158 }
159 if (seed < 0) {
160 throw std::domain_error("Parameter 'seed' must be >= 0");
161 }
162 if (tau <= 0.) {
163 throw std::domain_error("Parameter 'tau' must be > 0");
164 }
165 if (kT < 0.) {
166 throw std::domain_error("Parameter 'kT' must be >= 0");
167 }
168 if (density < 0.) {
169 throw std::domain_error("Parameter 'density' must be >= 0");
170 }
171 m_conv_energy = Utils::int_pow<2>(tau) / Utils::int_pow<2>(agrid);
172 m_conv_diffusion = tau / Utils::int_pow<2>(agrid);
173 m_conv_ext_efield = Utils::int_pow<2>(tau) / agrid;
174 m_conv_density = Utils::int_pow<3>(agrid);
175 m_conv_flux = tau * Utils::int_pow<2>(agrid);
178 for (auto &vtk : m_vtk_writers) {
179 vtk->attach_to_lattice(m_instance, get_lattice_to_md_units_conversion());
180 }
181 });
182}
183
184void EKSpecies::load_checkpoint(std::filesystem::path const &path, int mode) {
185 auto &ek_obj = *m_instance;
186
187 auto const read_metadata = [&ek_obj](CheckpointFile &cpfile) {
188 auto const expected_grid_size = ek_obj.get_lattice().get_grid_dimensions();
192 std::stringstream message;
193 message << "grid dimensions mismatch, read [" << read_grid_size << "], "
194 << "expected [" << expected_grid_size << "].";
195 throw std::runtime_error(message.str());
196 }
197 };
198
199 auto const read_data = [&ek_obj](CheckpointFile &cpfile) {
200 auto const grid_size = ek_obj.get_lattice().get_grid_dimensions();
201 auto const i_max = grid_size[0];
202 auto const j_max = grid_size[1];
203 auto const k_max = grid_size[2];
205 for (int i = 0; i < i_max; i++) {
206 for (int j = 0; j < j_max; j++) {
207 for (int k = 0; k < k_max; k++) {
208 auto const ind = Utils::Vector3i{{i, j, k}};
209 cpfile.read(cpnode.density);
210 cpfile.read(cpnode.is_boundary_density);
211 if (cpnode.is_boundary_density) {
212 cpfile.read(cpnode.density_boundary);
213 }
214 cpfile.read(cpnode.is_boundary_flux);
215 if (cpnode.is_boundary_flux) {
216 cpfile.read(cpnode.flux_boundary);
217 }
218 ek_obj.set_node_density(ind, cpnode.density);
219 if (cpnode.is_boundary_density) {
220 ek_obj.set_node_density_boundary(ind, cpnode.density_boundary);
221 }
222 if (cpnode.is_boundary_flux) {
223 ek_obj.set_node_flux_boundary(ind, cpnode.flux_boundary);
224 }
225 }
226 }
227 }
228 };
229
230 auto const on_success = [&ek_obj]() { ek_obj.ghost_communication(); };
231
233 on_success);
234}
235
236void EKSpecies::save_checkpoint(std::filesystem::path const &path, int mode) {
237 auto &ek_obj = *m_instance;
238
239 auto const write_metadata = [&ek_obj,
240 mode](std::shared_ptr<CheckpointFile> cpfile_ptr,
241 Context const &context) {
242 auto const grid_size = ek_obj.get_lattice().get_grid_dimensions();
243 if (context.is_head_node()) {
244 cpfile_ptr->write(grid_size);
246 }
247 };
248
249 auto const on_failure = [](std::shared_ptr<CheckpointFile> const &,
250 Context const &context) {
251 if (context.is_head_node()) {
252 auto failure = true;
253 boost::mpi::broadcast(context.get_comm(), failure, 0);
254 }
255 };
256
257 auto const write_data = [&ek_obj,
258 mode](std::shared_ptr<CheckpointFile> cpfile_ptr,
259 Context const &context) {
260 auto const get_node_checkpoint =
261 [&](Utils::Vector3i const &ind) -> std::optional<EKWalberlaNodeState> {
262 auto const density = ek_obj.get_node_density(ind);
263 auto const is_b_d = ek_obj.get_node_is_density_boundary(ind);
264 auto const dens_b = ek_obj.get_node_density_at_boundary(ind);
265 auto const is_b_f = ek_obj.get_node_is_flux_boundary(ind);
266 auto const flux_b = ek_obj.get_node_flux_at_boundary(ind);
268 ((*is_b_d) ? dens_b.has_value() : true) and
269 ((*is_b_f) ? flux_b.has_value() : true)) {
272 cpnode.is_boundary_density = *is_b_d;
273 if (*is_b_d) {
274 cpnode.density_boundary = *dens_b;
275 }
276 cpnode.is_boundary_flux = *is_b_f;
277 if (*is_b_f) {
278 cpnode.flux_boundary = *flux_b;
279 }
280 return cpnode;
281 }
282 return std::nullopt;
283 };
284
285 auto failure = false;
286 auto const &comm = context.get_comm();
287 auto const is_head_node = context.is_head_node();
288 auto const unit_test_mode = (mode != static_cast<int>(CptMode::ascii)) and
289 (mode != static_cast<int>(CptMode::binary));
290 auto const grid_size = ek_obj.get_lattice().get_grid_dimensions();
291 auto const i_max = grid_size[0];
292 auto const j_max = grid_size[1];
293 auto const k_max = grid_size[2];
295 for (int i = 0; i < i_max; i++) {
296 for (int j = 0; j < j_max; j++) {
297 for (int k = 0; k < k_max; k++) {
298 auto const ind = Utils::Vector3i{{i, j, k}};
299 auto const result = get_node_checkpoint(ind);
300 if (!unit_test_mode) {
301 assert(1 == boost::mpi::all_reduce(comm, static_cast<int>(!!result),
302 std::plus<>()) &&
303 "Incorrect number of return values");
304 }
305 if (is_head_node) {
306 if (result) {
307 cpnode = *result;
308 } else {
309 comm.recv(boost::mpi::any_source, 42, cpnode);
310 }
311 auto &cpfile = *cpfile_ptr;
312 cpfile.write(cpnode.density);
313 cpfile.write(cpnode.is_boundary_density);
314 if (cpnode.is_boundary_density) {
315 cpfile.write(cpnode.density_boundary);
316 }
317 cpfile.write(cpnode.is_boundary_flux);
318 if (cpnode.is_boundary_flux) {
319 cpfile.write(cpnode.flux_boundary);
320 }
321 boost::mpi::broadcast(comm, failure, 0);
322 } else {
323 if (result) {
324 comm.send(0, 42, *result);
325 }
326 boost::mpi::broadcast(comm, failure, 0);
327 if (failure) {
328 return;
329 }
330 }
331 }
332 }
333 }
334 };
335
338}
339
340} // namespace ScriptInterface::walberla
341
342#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 make_instance(VariantMap const &params) override
void make_instance(VariantMap const &params) override
::LatticeModel::units_map get_lattice_to_md_units_conversion() const override
Variant do_call_method(std::string const &method, VariantMap const &parameters) override
Definition EKSpecies.cpp:52
void do_construct(VariantMap const &params) override
Variant do_call_method(std::string const &method_name, VariantMap const &params) override
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.
This file contains the errorhandling code for severe errors, like a broken bond or illegal parameter ...
#define runtimeWarningMsg()
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
static FUNC_PREFIX double *RESTRICT const double *RESTRICT int64_t const int64_t const int64_t const int64_t const int64_t const int64_t const int64_t const int64_t const int64_t const int64_t const int64_t const int64_t const int64_t const int64_t const int64_t const double grid_size
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)
static SteepestDescentParameters params
Currently active steepest descent instance.
Checkpoint data for a EK node.
Recursive variant implementation.
Definition Variant.hpp:84