ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
CellSystem.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2022 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#include "CellSystem.hpp"
21
25
26#include "core/BoxGeometry.hpp"
31#include "core/cells.hpp"
36#include "core/tuning.hpp"
37
38#include <utils/Vector.hpp>
40
41#include <boost/mpi/collectives/gather.hpp>
42
43#include <algorithm>
44#include <cassert>
45#include <iterator>
46#include <optional>
47#include <set>
48#include <sstream>
49#include <stdexcept>
50#include <string>
51#include <unordered_map>
52#include <utility>
53#include <variant>
54#include <vector>
55
56static int coord(std::string const &s) {
57 if (s == "x")
58 return 0;
59 if (s == "y")
60 return 1;
61 if (s == "z")
62 return 2;
63 throw std::invalid_argument("Invalid Cartesian coordinate: '" + s + "'");
64}
65
66static std::string coord_letter(int c) {
67 if (c == 0)
68 return "x";
69 if (c == 1)
70 return "y";
71 assert(c == 2);
72 return "z";
73}
74
75namespace ScriptInterface {
76namespace CellSystem {
77
80 {"use_verlet_lists",
81 [this](Variant const &v) {
82 get_cell_structure().use_verlet_list = get_value<bool>(v);
83 },
84 [this]() { return get_cell_structure().use_verlet_list; }},
85 {"node_grid",
86 [this](Variant const &v) {
87 context()->parallel_try_catch([this, &v]() {
88 auto const error_msg = std::string("Parameter 'node_grid'");
91 if (::communicator.locked_for_checkpointing) {
93 return;
94 }
97 if (n_nodes_new != n_nodes_old) {
98 std::stringstream reason;
99 reason << ": MPI world size " << n_nodes_old << " incompatible "
100 << "with new node grid [" << new_node_grid << "]";
101 throw std::invalid_argument(error_msg + reason.str());
102 }
103 try {
105 get_system().on_node_grid_change();
106 } catch (...) {
108 get_system().on_node_grid_change();
109 throw;
110 }
111 });
112 },
113 []() { return ::communicator.node_grid; }},
114 {"skin",
115 [this](Variant const &v) {
116 auto const new_skin = get_value<double>(v);
117 if (new_skin < 0.) {
118 if (context()->is_head_node()) {
119 throw std::domain_error("Parameter 'skin' must be >= 0");
120 }
121 throw Exception("");
122 }
123 get_cell_structure().set_verlet_skin(new_skin);
124 },
125 [this]() { return get_cell_structure().get_verlet_skin(); }},
126 {"decomposition_type", AutoParameter::read_only,
127 [this]() {
128 return cs_type_to_name.at(get_cell_structure().decomposition_type());
129 }},
130 {"n_square_types", AutoParameter::read_only,
131 [this]() {
132 if (get_cell_structure().decomposition_type() !=
134 return Variant{none};
135 }
136 auto const hd = get_hybrid_decomposition();
137 auto const ns_types = hd.get_n_square_types();
138 return Variant{std::vector<int>(ns_types.begin(), ns_types.end())};
139 }},
140 {"fully_connected_boundary", AutoParameter::read_only,
141 [this]() {
142 if (get_cell_structure().decomposition_type() !=
144 return Variant{none};
145 }
146 auto const rd = get_regular_decomposition();
147 auto const fcb = rd.fully_connected_boundary();
148 if (not fcb)
149 return Variant{none};
150 return Variant{std::unordered_map<std::string, Variant>{
151 {{"boundary", Variant{coord_letter((*fcb).first)}},
152 {"direction", Variant{coord_letter((*fcb).second)}}}}};
153 }},
154 {"cutoff_regular", AutoParameter::read_only,
155 [this]() {
156 if (get_cell_structure().decomposition_type() !=
158 return Variant{none};
159 }
160 auto const hd = get_hybrid_decomposition();
161 return Variant{hd.get_cutoff_regular()};
162 }},
163 {"max_cut_nonbonded", AutoParameter::read_only,
164 [this]() { return get_system().nonbonded_ias->maximal_cutoff(); }},
165 {"max_cut_bonded", AutoParameter::read_only,
166 [this]() { return get_system().bonded_ias->maximal_cutoff(); }},
167 {"interaction_range", AutoParameter::read_only,
168 [this]() { return get_system().get_interaction_range(); }},
169 });
170}
171
172Variant CellSystem::do_call_method(std::string const &name,
173 VariantMap const &params) {
174 if (name == "initialize") {
175 auto const cs_name = get_value<std::string>(params, "name");
176 auto const cs_type = cs_name_to_type.at(cs_name);
177 initialize(cs_type, params);
178 return {};
179 }
180 if (name == "resort") {
181 auto const global_flag = get_value_or<bool>(params, "global_flag", true);
182 return mpi_resort_particles(global_flag);
183 }
184 if (name == "get_state") {
185 auto state = get_parameters();
186 auto const cs_type = get_cell_structure().decomposition_type();
188 auto const rd = get_regular_decomposition();
189 state["cell_grid"] = Variant{rd.cell_grid};
190 state["cell_size"] = Variant{rd.cell_size};
191 } else if (cs_type == CellStructureType::HYBRID) {
192 auto const hd = get_hybrid_decomposition();
193 state["cell_grid"] = Variant{hd.get_cell_grid()};
194 state["cell_size"] = Variant{hd.get_cell_size()};
195 mpi_resort_particles(true); // needed to get correct particle counts
196 state["parts_per_decomposition"] =
197 Variant{std::unordered_map<std::string, Variant>{
198 {"regular", hd.count_particles_in_regular()},
199 {"n_square", hd.count_particles_in_n_square()}}};
200 }
201 state["verlet_reuse"] = get_cell_structure().get_verlet_reuse();
202 state["n_nodes"] = context()->get_comm().size();
203 return state;
204 }
205 if (name == "get_pairs") {
206 std::vector<Variant> out;
207 context()->parallel_try_catch([this, &params, &out]() {
208 auto &system = get_system();
209 system.on_observable_calc();
210 std::vector<std::pair<int, int>> pair_list;
211 auto const distance = get_value<double>(params, "distance");
212 if (std::get_if<std::string>(&params.at("types"))) {
213 auto const key = get_value<std::string>(params, "types");
214 if (key != "all") {
215 throw std::invalid_argument("Unknown argument types='" + key + "'");
216 }
217 pair_list = get_pairs(system, distance);
218 } else {
219 auto const types = get_value<std::vector<int>>(params, "types");
221 }
223 std::ranges::transform(pair_list, std::back_inserter(out),
224 [](auto const &pair) {
225 return std::vector<int>{pair.first, pair.second};
226 });
227 });
228 return out;
229 }
230 if (name == "get_neighbors") {
231 std::vector<std::vector<int>> neighbors_global;
232 context()->parallel_try_catch([this, &neighbors_global, &params]() {
233 auto &system = get_system();
234 system.on_observable_calc();
235 auto const dist = get_value<double>(params, "distance");
236 auto const pid = get_value<int>(params, "pid");
237 auto const ret = get_short_range_neighbors(system, pid, dist);
238 std::vector<int> neighbors_local;
239 if (ret) {
241 }
242 boost::mpi::gather(context()->get_comm(), neighbors_local,
244 });
245 std::vector<int> neighbors;
246 for (auto const &neighbors_local : neighbors_global) {
247 if (not neighbors_local.empty()) {
248 neighbors = neighbors_local;
249 break;
250 }
251 }
252 return neighbors;
253 }
254 if (name == "non_bonded_loop_trace") {
255 auto &system = get_system();
256 system.on_observable_calc();
257 std::vector<Variant> out;
258 auto pair_list =
259 non_bonded_loop_trace(system, context()->get_comm().rank());
261 std::ranges::transform(
262 pair_list, std::back_inserter(out), [](auto const &pair) {
263 return std::vector<Variant>{pair.id1, pair.id2, pair.pos1,
264 pair.pos2, pair.vec21, pair.node};
265 });
266 return out;
267 }
268 if (name == "tune_skin") {
269 auto &system = get_system();
270 system.tune_verlet_skin(
271 get_value<double>(params, "min_skin"),
272 get_value<double>(params, "max_skin"), get_value<double>(params, "tol"),
273 get_value<int>(params, "int_steps"),
274 get_value_or<bool>(params, "adjust_max_skin", false));
275 return get_cell_structure().get_verlet_skin();
276 }
277 if (name == "get_max_range") {
278 return get_cell_structure().max_range();
279 }
280 return {};
281}
282
283std::vector<int> CellSystem::mpi_resort_particles(bool global_flag) const {
284 auto &cell_structure = get_cell_structure();
285 cell_structure.resort_particles(global_flag);
287 auto const size = static_cast<int>(cell_structure.local_particles().size());
288 std::vector<int> n_part_per_node;
289 boost::mpi::gather(context()->get_comm(), size, n_part_per_node, 0);
290 return n_part_per_node;
291}
292
293void CellSystem::initialize(CellStructureType const &cs_type,
294 VariantMap const &params) {
295 auto const verlet = get_value_or<bool>(params, "use_verlet_lists", true);
296 auto &system = get_system();
297 m_cell_structure->use_verlet_list = verlet;
299 auto const cutoff_regular = get_value<double>(params, "cutoff_regular");
300 auto const ns_types =
301 get_value_or<std::vector<int>>(params, "n_square_types", {});
302 auto n_square_types = std::set<int>{ns_types.begin(), ns_types.end()};
303 m_cell_structure->set_hybrid_decomposition(cutoff_regular, n_square_types);
304 } else if (cs_type == CellStructureType::REGULAR) {
305 std::optional<std::pair<int, int>> fcb_pair = std::nullopt;
306 if (params.contains("fully_connected_boundary") and
307 not is_none(params.at("fully_connected_boundary"))) {
308 auto const variant =
309 get_value<VariantMap>(params, "fully_connected_boundary");
311 fcb_pair = {{coord(std::get<std::string>(variant.at("boundary"))),
312 coord(std::get<std::string>(variant.at("direction")))}};
313 });
314 }
315 context()->parallel_try_catch([this, &fcb_pair]() {
316 m_cell_structure->set_regular_decomposition(
317 get_system().get_interaction_range(), fcb_pair);
318 });
319 } else {
320 system.set_cell_structure_topology(cs_type);
321 }
322}
323
327
331
333 m_node_grid = get_value_or(params, "node_grid", ::communicator.node_grid);
334}
335
337 VariantMap const &params) {
338 if (name == "get_node_grid") {
339 return ::communicator.node_grid;
340 }
341 if (name == "acquire_lock") {
342 ::communicator.set_node_grid(m_node_grid);
344 } else if (name == "release_lock") {
346 }
347 return {};
348}
349
350} // namespace CellSystem
351} // namespace ScriptInterface
CellStructureType
Cell structure topology.
@ HYBRID
Hybrid decomposition.
@ REGULAR
Regular decomposition.
static int coord(std::string const &s)
static std::string coord_letter(int c)
Vector implementation and trait types for boost qvm interoperability.
Data structures for bonded interactions.
std::optional< std::vector< int > > get_short_range_neighbors(System::System const &system, int const pid, double const distance)
Get ids of particles that are within a certain distance of another particle.
Definition cells.cpp:119
std::vector< PairInfo > non_bonded_loop_trace(System::System const &system, int const rank)
Returns pairs of particle ids, positions and distance as seen by the non-bonded loop.
Definition cells.cpp:176
std::vector< std::pair< int, int > > get_pairs_of_types(System::System const &system, double const distance, std::vector< int > const &types)
Get pairs closer than distance if both their types are in types.
Definition cells.cpp:167
std::vector< std::pair< int, int > > get_pairs(System::System const &system, double const distance)
Get pairs closer than distance from the cells.
Definition cells.cpp:159
This file contains everything related to the global cell structure / cell system.
void add_parameters(std::vector< AutoParameter > &&params)
Variant do_call_method(std::string const &name, VariantMap const &params) override
void configure(Particles::ParticleHandle &)
void do_construct(VariantMap const &params) override
Variant do_call_method(std::string const &name, VariantMap const &params) override
virtual void parallel_try_catch(std::function< void()> const &cb) const =0
virtual boost::mpi::communicator const & get_comm() const =0
VariantMap get_parameters() const
Get current parameters.
Context * context() const
Responsible context.
std::string_view name() const
std::weak_ptr<::System::System > m_system
Communicator communicator
This file contains the asynchronous MPI communication.
constexpr bool is_none(Variant const &v)
Definition Variant.hpp:163
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.
constexpr const None none
None-"literal".
Definition Variant.hpp:126
void gather_buffer(std::vector< T, Allocator > &buffer, boost::mpi::communicator const &comm, int root=0)
Gather buffer with different size on each node.
T product(Vector< T, N > const &v)
Definition Vector.hpp:373
Various procedures concerning interactions between particles.
void clear_particle_node()
Invalidate particle_node.
Particles creation and deletion.
Utils::Vector3i node_grid
void set_node_grid(Utils::Vector3i const &value)
Set new Cartesian topology.
static constexpr const ReadOnly read_only
Recursive variant implementation.
Definition Variant.hpp:84