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
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#include <boost/variant.hpp>
43
44#include <algorithm>
45#include <cassert>
46#include <iterator>
47#include <optional>
48#include <set>
49#include <sstream>
50#include <stdexcept>
51#include <string>
52#include <unordered_map>
53#include <utility>
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'");
93 if (n_nodes_new != n_nodes_old) {
94 std::stringstream reason;
95 reason << ": MPI world size " << n_nodes_old << " incompatible "
96 << "with new node grid [" << new_node_grid << "]";
97 throw std::invalid_argument(error_msg + reason.str());
98 }
99 try {
101 get_system().on_node_grid_change();
102 } catch (...) {
104 get_system().on_node_grid_change();
105 throw;
106 }
107 });
108 },
109 []() { return ::communicator.node_grid; }},
110 {"skin",
111 [this](Variant const &v) {
112 auto const new_skin = get_value<double>(v);
113 if (new_skin < 0.) {
114 if (context()->is_head_node()) {
115 throw std::domain_error("Parameter 'skin' must be >= 0");
116 }
117 throw Exception("");
118 }
119 get_cell_structure().set_verlet_skin(new_skin);
120 },
121 [this]() { return get_cell_structure().get_verlet_skin(); }},
122 {"decomposition_type", AutoParameter::read_only,
123 [this]() {
124 return cs_type_to_name.at(get_cell_structure().decomposition_type());
125 }},
126 {"n_square_types", AutoParameter::read_only,
127 [this]() {
128 if (get_cell_structure().decomposition_type() !=
130 return Variant{none};
131 }
132 auto const hd = get_hybrid_decomposition();
133 auto const ns_types = hd.get_n_square_types();
134 return Variant{std::vector<int>(ns_types.begin(), ns_types.end())};
135 }},
136 {"fully_connected_boundary", AutoParameter::read_only,
137 [this]() {
138 if (get_cell_structure().decomposition_type() !=
140 return Variant{none};
141 }
142 auto const rd = get_regular_decomposition();
143 auto const fcb = rd.fully_connected_boundary();
144 if (not fcb)
145 return Variant{none};
146 return Variant{std::unordered_map<std::string, Variant>{
147 {{"boundary", Variant{coord_letter((*fcb).first)}},
148 {"direction", Variant{coord_letter((*fcb).second)}}}}};
149 }},
150 {"cutoff_regular", AutoParameter::read_only,
151 [this]() {
152 if (get_cell_structure().decomposition_type() !=
154 return Variant{none};
155 }
156 auto const hd = get_hybrid_decomposition();
157 return Variant{hd.get_cutoff_regular()};
158 }},
159 {"max_cut_nonbonded", AutoParameter::read_only,
160 [this]() { return get_system().nonbonded_ias->maximal_cutoff(); }},
161 {"max_cut_bonded", AutoParameter::read_only,
162 [this]() { return get_system().bonded_ias->maximal_cutoff(); }},
163 {"interaction_range", AutoParameter::read_only,
164 [this]() { return get_system().get_interaction_range(); }},
165 });
166}
167
168Variant CellSystem::do_call_method(std::string const &name,
169 VariantMap const &params) {
170 if (name == "initialize") {
171 auto const cs_name = get_value<std::string>(params, "name");
172 auto const cs_type = cs_name_to_type.at(cs_name);
173 initialize(cs_type, params);
174 return {};
175 }
176 if (name == "resort") {
177 auto const global_flag = get_value_or<bool>(params, "global_flag", true);
178 return mpi_resort_particles(global_flag);
179 }
180 if (name == "get_state") {
181 auto state = get_parameters();
182 auto const cs_type = get_cell_structure().decomposition_type();
184 auto const rd = get_regular_decomposition();
185 state["cell_grid"] = Variant{rd.cell_grid};
186 state["cell_size"] = Variant{rd.cell_size};
187 } else if (cs_type == CellStructureType::HYBRID) {
188 auto const hd = get_hybrid_decomposition();
189 state["cell_grid"] = Variant{hd.get_cell_grid()};
190 state["cell_size"] = Variant{hd.get_cell_size()};
191 mpi_resort_particles(true); // needed to get correct particle counts
192 state["parts_per_decomposition"] =
193 Variant{std::unordered_map<std::string, Variant>{
194 {"regular", hd.count_particles_in_regular()},
195 {"n_square", hd.count_particles_in_n_square()}}};
196 }
197 state["verlet_reuse"] = get_cell_structure().get_verlet_reuse();
198 state["n_nodes"] = context()->get_comm().size();
199 return state;
200 }
201 if (name == "get_pairs") {
202 std::vector<Variant> out;
203 context()->parallel_try_catch([this, &params, &out]() {
204 auto &system = get_system();
205 system.on_observable_calc();
206 std::vector<std::pair<int, int>> pair_list;
207 auto const distance = get_value<double>(params, "distance");
208 if (boost::get<std::string>(&params.at("types")) != nullptr) {
209 auto const key = get_value<std::string>(params, "types");
210 if (key != "all") {
211 throw std::invalid_argument("Unknown argument types='" + key + "'");
212 }
213 pair_list = get_pairs(system, distance);
214 } else {
215 auto const types = get_value<std::vector<int>>(params, "types");
217 }
219 std::ranges::transform(pair_list, std::back_inserter(out),
220 [](auto const &pair) {
221 return std::vector<int>{pair.first, pair.second};
222 });
223 });
224 return out;
225 }
226 if (name == "get_neighbors") {
227 std::vector<std::vector<int>> neighbors_global;
229 auto &system = get_system();
230 system.on_observable_calc();
231 auto const dist = get_value<double>(params, "distance");
232 auto const pid = get_value<int>(params, "pid");
233 auto const ret = get_short_range_neighbors(system, pid, dist);
234 std::vector<int> neighbors_local;
235 if (ret) {
237 }
238 boost::mpi::gather(context()->get_comm(), neighbors_local,
240 });
241 std::vector<int> neighbors;
242 for (auto const &neighbors_local : neighbors_global) {
243 if (not neighbors_local.empty()) {
244 neighbors = neighbors_local;
245 break;
246 }
247 }
248 return neighbors;
249 }
250 if (name == "non_bonded_loop_trace") {
251 auto &system = get_system();
252 system.on_observable_calc();
253 std::vector<Variant> out;
254 auto pair_list =
255 non_bonded_loop_trace(system, context()->get_comm().rank());
257 std::ranges::transform(
258 pair_list, std::back_inserter(out), [](auto const &pair) {
259 return std::vector<Variant>{pair.id1, pair.id2, pair.pos1,
260 pair.pos2, pair.vec21, pair.node};
261 });
262 return out;
263 }
264 if (name == "tune_skin") {
265 auto &system = get_system();
266 system.tune_verlet_skin(
267 get_value<double>(params, "min_skin"),
268 get_value<double>(params, "max_skin"), get_value<double>(params, "tol"),
269 get_value<int>(params, "int_steps"),
270 get_value_or<bool>(params, "adjust_max_skin", false));
271 return get_cell_structure().get_verlet_skin();
272 }
273 if (name == "get_max_range") {
274 return get_cell_structure().max_range();
275 }
276 return {};
277}
278
279std::vector<int> CellSystem::mpi_resort_particles(bool global_flag) const {
280 auto &cell_structure = get_cell_structure();
281 cell_structure.resort_particles(global_flag);
283 auto const size = static_cast<int>(cell_structure.local_particles().size());
284 std::vector<int> n_part_per_node;
285 boost::mpi::gather(context()->get_comm(), size, n_part_per_node, 0);
286 return n_part_per_node;
287}
288
289void CellSystem::initialize(CellStructureType const &cs_type,
290 VariantMap const &params) {
291 auto const verlet = get_value_or<bool>(params, "use_verlet_lists", true);
292 auto &system = get_system();
293 m_cell_structure->use_verlet_list = verlet;
295 auto const cutoff_regular = get_value<double>(params, "cutoff_regular");
296 auto const ns_types =
297 get_value_or<std::vector<int>>(params, "n_square_types", {});
298 auto n_square_types = std::set<int>{ns_types.begin(), ns_types.end()};
299 m_cell_structure->set_hybrid_decomposition(cutoff_regular, n_square_types);
300 } else if (cs_type == CellStructureType::REGULAR) {
301 std::optional<std::pair<int, int>> fcb_pair = std::nullopt;
302 if (params.contains("fully_connected_boundary") and
303 not is_none(params.at("fully_connected_boundary"))) {
304 auto const variant =
305 get_value<VariantMap>(params, "fully_connected_boundary");
307 fcb_pair = {{coord(boost::get<std::string>(variant.at("boundary"))),
308 coord(boost::get<std::string>(variant.at("direction")))}};
309 });
310 }
311 context()->parallel_try_catch([this, &fcb_pair]() {
312 m_cell_structure->set_regular_decomposition(
313 get_system().get_interaction_range(), fcb_pair);
314 });
315 } else {
316 system.set_cell_structure_topology(cs_type);
317 }
318}
319
323
327
328} // namespace CellSystem
329} // 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 &)
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.
boost::string_ref name() const
Context * context() const
Responsible context.
std::weak_ptr<::System::System > m_system
Communicator communicator
This file contains the asynchronous MPI communication.
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
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
bool is_none(Variant const &v)
Definition Variant.hpp:115
constexpr const None none
None-"literal".
Definition Variant.hpp:50
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:375
Various procedures concerning interactions between particles.
void clear_particle_node()
Invalidate particle_node.
Particles creation and deletion.
static SteepestDescentParameters params
Currently active steepest descent instance.
Utils::Vector3i node_grid
void set_node_grid(Utils::Vector3i const &value)
Set new Cartesian topology.
static constexpr const ReadOnly read_only