Loading [MathJax]/extensions/TeX/AMSmath.js
ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages Concepts
LBFluid.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 WALBERLA
22
23#include "LBFluid.hpp"
26
27#include "core/BoxGeometry.hpp"
32
34
38
39#include <utils/Vector.hpp>
40#include <utils/matrix.hpp>
42
43#include <boost/mpi.hpp>
44#include <boost/mpi/collectives/all_reduce.hpp>
45#include <boost/mpi/collectives/broadcast.hpp>
46
47#include <algorithm>
48#include <cassert>
49#include <cstddef>
50#include <functional>
51#include <memory>
52#include <optional>
53#include <sstream>
54#include <stdexcept>
55#include <string>
56#include <vector>
57
59
60std::unordered_map<std::string, int> const LBVTKHandle::obs_map = {
61 {"density", static_cast<int>(OutputVTK::density)},
62 {"velocity_vector", static_cast<int>(OutputVTK::velocity_vector)},
63 {"pressure_tensor", static_cast<int>(OutputVTK::pressure_tensor)},
64};
65
66Variant LBFluid::do_call_method(std::string const &name,
67 VariantMap const &params) {
68 if (name == "activate") {
69 context()->parallel_try_catch([this]() {
71 });
72 m_is_active = true;
73 return {};
74 }
75 if (name == "deactivate") {
76 if (m_is_active) {
78 m_is_active = false;
79 }
80 return {};
81 }
82 if (name == "add_force_at_pos") {
83 auto const &box_geo = *::System::get_system().box_geo;
84 auto const pos = get_value<Utils::Vector3d>(params, "pos");
85 auto const f = get_value<Utils::Vector3d>(params, "force");
86 auto const folded_pos = box_geo.folded_position(pos);
87 m_instance->add_force_at_pos(folded_pos * m_conv_dist, f * m_conv_force);
88 return {};
89 }
90 if (name == "get_interpolated_velocity") {
91 auto const pos = get_value<Utils::Vector3d>(params, "pos");
92 return get_interpolated_velocity(pos);
93 }
94 if (name == "get_pressure_tensor") {
95 return get_average_pressure_tensor();
96 }
97 if (name == "load_checkpoint") {
98 auto const path = get_value<std::string>(params, "path");
99 auto const mode = get_value<int>(params, "mode");
100 load_checkpoint(path, mode);
101 return {};
102 }
103 if (name == "save_checkpoint") {
104 auto const path = get_value<std::string>(params, "path");
105 auto const mode = get_value<int>(params, "mode");
106 save_checkpoint(path, mode);
107 return {};
108 }
109 if (name == "clear_boundaries") {
110 m_instance->clear_boundaries();
112 return {};
113 }
114 if (name == "add_boundary_from_shape") {
115 m_instance->update_boundary_from_shape(
116 get_value<std::vector<int>>(params, "raster"),
117 get_value<std::vector<double>>(params, "values"));
118 return {};
119 }
120 if (name == "get_lattice_speed") {
121 return 1. / m_conv_speed;
122 }
123
125}
126
128 auto const visc = get_value<double>(params, "kinematic_viscosity");
129 auto const dens = get_value<double>(params, "density");
130 auto const precision = get_value<bool>(params, "single_precision");
131 auto const lb_lattice = m_lattice->lattice();
132 auto const lb_visc = m_conv_visc * visc;
133 auto const lb_dens = m_conv_dens * dens;
135}
136
137#ifdef CUDA
139 auto const visc = get_value<double>(params, "kinematic_viscosity");
140 auto const dens = get_value<double>(params, "density");
141 auto const precision = get_value<bool>(params, "single_precision");
143 m_lattice->get_parameter("blocks_per_mpi_rank"));
144 if (blocks_per_mpi_rank != Utils::Vector3i{{1, 1, 1}}) {
145 throw std::runtime_error(
146 "Using more than one block per MPI rank is not supported for GPU LB");
147 }
148 auto const lb_lattice = m_lattice->lattice();
149 auto const lb_visc = m_conv_visc * visc;
150 auto const lb_dens = m_conv_dens * dens;
152}
153#endif // CUDA
154
159 auto const tau = get_value<double>(params, "tau");
160 auto const agrid = get_value<double>(m_lattice->get_parameter("agrid"));
161 auto const visc = get_value<double>(params, "kinematic_viscosity");
162 auto const dens = get_value<double>(params, "density");
163 auto const kT = get_value<double>(params, "kT");
164 auto const ext_f = get_value<Utils::Vector3d>(params, "ext_force_density");
165 m_lb_params = std::make_shared<::LB::LBWalberlaParams>(agrid, tau);
166 m_is_active = false;
167 auto const seed = get_value<int>(params, "seed");
168 context()->parallel_try_catch([&]() {
169 if (tau <= 0.) {
170 throw std::domain_error("Parameter 'tau' must be > 0");
171 }
172 m_conv_dist = 1. / agrid;
173 m_conv_visc = Utils::int_pow<1>(tau) / Utils::int_pow<2>(agrid);
174 m_conv_energy = Utils::int_pow<2>(tau) / Utils::int_pow<2>(agrid);
175 m_conv_dens = Utils::int_pow<3>(agrid);
176 m_conv_speed = Utils::int_pow<1>(tau) / Utils::int_pow<1>(agrid);
177 m_conv_press = Utils::int_pow<2>(tau) * Utils::int_pow<1>(agrid);
178 m_conv_force = Utils::int_pow<2>(tau) / Utils::int_pow<1>(agrid);
179 m_conv_force_dens = Utils::int_pow<2>(tau) * Utils::int_pow<2>(agrid);
180 auto const lb_visc = m_conv_visc * visc;
181 auto const lb_dens = m_conv_dens * dens;
182 auto const lb_kT = m_conv_energy * kT;
183 auto const lb_ext_f = m_conv_force_dens * ext_f;
184 if (seed < 0) {
185 throw std::domain_error("Parameter 'seed' must be >= 0");
186 }
187 if (lb_kT < 0.) {
188 throw std::domain_error("Parameter 'kT' must be >= 0");
189 }
190 if (lb_dens <= 0.) {
191 throw std::domain_error("Parameter 'density' must be > 0");
192 }
193 if (lb_visc < 0.) {
194 throw std::domain_error("Parameter 'kinematic_viscosity' must be >= 0");
195 }
198 static_cast<unsigned int>(seed));
199 m_instance->set_external_force(lb_ext_f);
200 m_instance->ghost_communication();
201 for (auto &vtk : m_vtk_writers) {
202 vtk->attach_to_lattice(m_instance, get_latice_to_md_units_conversion());
203 }
204 });
205}
206
207std::vector<Variant> LBFluid::get_average_pressure_tensor() const {
208 auto const local = m_instance->get_pressure_tensor() / m_conv_press;
209 auto const tensor_flat = mpi_reduce_sum(context()->get_comm(), local);
211 std::ranges::copy(tensor_flat, tensor.m_data.begin());
212 return std::vector<Variant>{tensor.row<0>().as_vector(),
213 tensor.row<1>().as_vector(),
214 tensor.row<2>().as_vector()};
215}
216
217Variant LBFluid::get_interpolated_velocity(Utils::Vector3d const &pos) const {
218 auto const &box_geo = *::System::get_system().box_geo;
219 auto const lb_pos = box_geo.folded_position(pos) * m_conv_dist;
220 auto const result = m_instance->get_velocity_at_pos(lb_pos);
221 return Utils::Mpi::reduce_optional(context()->get_comm(), result) /
223}
224
225void LBFluid::load_checkpoint(std::string const &filename, int mode) {
226 auto &lb_obj = *m_instance;
227
228 auto const read_metadata = [&lb_obj](CheckpointFile &cpfile) {
229 auto const expected_grid_size = lb_obj.get_lattice().get_grid_dimensions();
230 auto const expected_pop_size = lb_obj.stencil_size();
232 std::size_t read_pop_size;
234 cpfile.read(read_pop_size);
236 std::stringstream message;
237 message << "grid dimensions mismatch, read [" << read_grid_size << "], "
238 << "expected [" << expected_grid_size << "].";
239 throw std::runtime_error(message.str());
240 }
242 throw std::runtime_error("population size mismatch, read " +
243 std::to_string(read_pop_size) + ", expected " +
244 std::to_string(expected_pop_size) + ".");
245 }
246 };
247
248 auto const read_data = [&lb_obj](CheckpointFile &cpfile) {
249 auto const grid_size = lb_obj.get_lattice().get_grid_dimensions();
250 auto const i_max = grid_size[0];
251 auto const j_max = grid_size[1];
252 auto const k_max = grid_size[2];
254 cpnode.populations.resize(lb_obj.stencil_size());
255 for (int i = 0; i < i_max; i++) {
256 for (int j = 0; j < j_max; j++) {
257 for (int k = 0; k < k_max; k++) {
258 auto const ind = Utils::Vector3i{{i, j, k}};
259 cpfile.read(cpnode.populations);
260 cpfile.read(cpnode.last_applied_force);
261 cpfile.read(cpnode.is_boundary);
262 if (cpnode.is_boundary) {
263 cpfile.read(cpnode.slip_velocity);
264 }
265 lb_obj.set_node_population(ind, cpnode.populations);
266 lb_obj.set_node_last_applied_force(ind, cpnode.last_applied_force);
267 if (cpnode.is_boundary) {
268 lb_obj.set_node_velocity_at_boundary(ind, cpnode.slip_velocity);
269 }
270 }
271 }
272 }
273 };
274
275 auto const on_success = [&lb_obj]() {
276 lb_obj.ghost_communication();
277 lb_obj.reallocate_ubb_field();
278 };
279
282}
283
284void LBFluid::save_checkpoint(std::string const &filename, int mode) {
285 auto &lb_obj = *m_instance;
286
287 auto const write_metadata = [&lb_obj,
288 mode](std::shared_ptr<CheckpointFile> cpfile_ptr,
289 Context const &context) {
290 auto const grid_size = lb_obj.get_lattice().get_grid_dimensions();
291 auto const pop_size = lb_obj.stencil_size();
292 if (context.is_head_node()) {
293 cpfile_ptr->write(grid_size);
294 cpfile_ptr->write(pop_size);
296 }
297 };
298
299 auto const on_failure = [](std::shared_ptr<CheckpointFile> const &,
300 Context const &context) {
301 if (context.is_head_node()) {
302 auto failure = true;
303 boost::mpi::broadcast(context.get_comm(), failure, 0);
304 }
305 };
306
307 auto const write_data = [&lb_obj,
308 mode](std::shared_ptr<CheckpointFile> cpfile_ptr,
309 Context const &context) {
310 auto const get_node_checkpoint =
311 [&](Utils::Vector3i const &ind) -> std::optional<LBWalberlaNodeState> {
312 auto const pop = lb_obj.get_node_population(ind);
313 auto const laf = lb_obj.get_node_last_applied_force(ind);
314 auto const lbb = lb_obj.get_node_is_boundary(ind);
315 auto const vbb = lb_obj.get_node_velocity_at_boundary(ind);
316 if (pop and laf and lbb and ((*lbb) ? vbb.has_value() : true)) {
319 cpnode.last_applied_force = *laf;
320 cpnode.is_boundary = *lbb;
321 if (*lbb) {
322 cpnode.slip_velocity = *vbb;
323 }
324 return {cpnode};
325 }
326 return std::nullopt;
327 };
328
329 auto failure = false;
330 auto const &comm = context.get_comm();
331 auto const is_head_node = context.is_head_node();
332 auto const unit_test_mode = (mode != static_cast<int>(CptMode::ascii)) and
333 (mode != static_cast<int>(CptMode::binary));
334 auto const grid_size = lb_obj.get_lattice().get_grid_dimensions();
335 auto const i_max = grid_size[0];
336 auto const j_max = grid_size[1];
337 auto const k_max = grid_size[2];
339 for (int i = 0; i < i_max; i++) {
340 for (int j = 0; j < j_max; j++) {
341 for (int k = 0; k < k_max; k++) {
342 auto const ind = Utils::Vector3i{{i, j, k}};
343 auto const result = get_node_checkpoint(ind);
344 if (!unit_test_mode) {
345 assert(1 == boost::mpi::all_reduce(comm, static_cast<int>(!!result),
346 std::plus<>()) &&
347 "Incorrect number of return values");
348 }
349 if (is_head_node) {
350 if (result) {
351 cpnode = *result;
352 } else {
353 comm.recv(boost::mpi::any_source, 42, cpnode);
354 }
355 auto &cpfile = *cpfile_ptr;
356 cpfile.write(cpnode.populations);
357 cpfile.write(cpnode.last_applied_force);
358 cpfile.write(cpnode.is_boundary);
359 if (cpnode.is_boundary) {
360 cpfile.write(cpnode.slip_velocity);
361 }
362 boost::mpi::broadcast(comm, failure, 0);
363 } else {
364 if (result) {
365 comm.send(0, 42, *result);
366 }
367 boost::mpi::broadcast(comm, failure, 0);
368 if (failure) {
369 return;
370 }
371 }
372 }
373 }
374 }
375 };
376
379}
380
381} // namespace ScriptInterface::walberla
382
383#endif // WALBERLA
Vector implementation and trait types for boost qvm interoperability.
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
boost::string_ref name() const
Context * context() const
Responsible context.
void make_instance(VariantMap const &params) override
Definition LBFluid.cpp:127
void make_instance(VariantMap const &params) override
Definition LBFluid.cpp:138
void do_construct(VariantMap const &params) override
Definition LBFluid.cpp:155
Variant do_call_method(std::string const &name, VariantMap const &params) override
Definition LBFluid.cpp:66
std::shared_ptr<::LB::LBWalberlaParams > m_lb_params
Definition LBFluid.hpp:58
::LatticeModel::units_map get_latice_to_md_units_conversion() const override
Definition LBFluid.hpp:130
Variant do_call_method(std::string const &method_name, VariantMap const &params) override
void on_lb_boundary_conditions_change()
Called when the LB boundary conditions change (geometry, slip velocity, or both).
std::shared_ptr< BoxGeometry > box_geo
This file contains the defaults for ESPResSo.
std::shared_ptr< LBWalberlaBase > new_lb_walberla_cpu(std::shared_ptr< LatticeWalberla > const &lattice, double viscosity, double density, bool single_precision)
std::shared_ptr< LBWalberlaBase > new_lb_walberla_gpu(std::shared_ptr< LatticeWalberla > const &lattice, double viscosity, double density, bool single_precision)
Matrix implementation and trait types for boost qvm interoperability.
void save_checkpoint_common(Context const &context, std::string const classname, std::string const &filename, 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::string const &filename, 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:69
T mpi_reduce_sum(boost::mpi::communicator const &comm, T const &result)
Reduce object by sum on the head node.
boost::make_recursive_variant< None, bool, int, std::size_t, double, std::string, ObjectRef, Utils::Vector3b, Utils::Vector3i, Utils::Vector2d, Utils::Vector3d, Utils::Vector4d, std::vector< int >, std::vector< double >, std::vector< boost::recursive_variant_ >, std::unordered_map< int, boost::recursive_variant_ >, std::unordered_map< std::string, boost::recursive_variant_ > >::type Variant
Possible types for parameters.
Definition Variant.hpp:67
System & get_system()
T reduce_optional(boost::mpi::communicator const &comm, std::optional< T > const &result)
Reduce an optional on the head node.
static FUNC_PREFIX 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 double grid_size
static SteepestDescentParameters params
Currently active steepest descent instance.
Checkpoint data for a LB node.
std::vector< double > populations
void update_collision_model()
void reset()
Remove the LB solver.
Definition lb/Solver.cpp:66
void set(Args... args)
Set the LB solver.
Matrix representation with static size.
Definition matrix.hpp:65