ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
core/system/System.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2014-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 "config/config.hpp"
21
22#include "System.hpp"
23#include "System.impl.hpp"
24
25#include "BoxGeometry.hpp"
26#include "LocalBox.hpp"
27#include "PropagationMode.hpp"
33#include "collision.hpp"
34#include "communication.hpp"
35#include "constraints.hpp"
37#include "errorhandling.hpp"
39#include "npt.hpp"
40#include "particle_node.hpp"
41#include "thermostat.hpp"
43
44#include <utils/Vector.hpp>
46
47#include <boost/mpi/collectives/all_reduce.hpp>
48
49#include <algorithm>
50#include <cstddef>
51#include <functional>
52#include <memory>
53#include <stdexcept>
54#include <utility>
55
56namespace System {
57
58static std::shared_ptr<System> instance = System::create();
59
60std::shared_ptr<System> System::create() {
61 auto handle = std::make_shared<System>(Private());
62 handle->initialize();
63 return handle;
64}
65
67 box_geo = std::make_shared<BoxGeometry>();
68 local_geo = std::make_shared<LocalBox>();
69 cell_structure = std::make_shared<CellStructure>(*box_geo);
70 propagation = std::make_shared<Propagation>();
71 thermostat = std::make_shared<Thermostat::Thermostat>();
72 nonbonded_ias = std::make_shared<InteractionsNonBonded>();
73 comfixed = std::make_shared<ComFixed>();
74 galilei = std::make_shared<Galilei>();
75 bond_breakage = std::make_shared<BondBreakage::BondBreakage>();
76 lees_edwards = std::make_shared<LeesEdwards::LeesEdwards>();
77 reinit_thermo = true;
78 time_step = -1.;
79 sim_time = 0.;
80 force_cap = 0.;
81 min_global_cut = INACTIVE_CUTOFF;
82}
83
84void System::initialize() {
85 auto handle = shared_from_this();
86 cell_structure->bind_system(handle);
87 lees_edwards->bind_system(handle);
88 thermostat->bind_system(handle);
89#ifdef CUDA
90 gpu.bind_system(handle);
91 gpu.initialize();
92#endif
93 lb.bind_system(handle);
94 ek.bind_system(handle);
95}
96
97bool is_system_set() { return instance != nullptr; }
98
99void reset_system() { instance.reset(); }
100
101void set_system(std::shared_ptr<System> new_instance) {
102 instance = new_instance;
103}
104
106
107void System::set_time_step(double value) {
108 if (value <= 0.)
109 throw std::domain_error("time_step must be > 0.");
110 if (lb.is_solver_set()) {
111 lb.veto_time_step(value);
112 }
113 if (ek.is_solver_set()) {
114 ek.veto_time_step(value);
115 }
116 time_step = value;
117 on_timestep_change();
118}
119
120void System::check_kT(double value) const {
121 if (lb.is_solver_set()) {
122 lb.veto_kT(value);
123 }
124 if (ek.is_solver_set()) {
125 ek.veto_kT(value);
126 }
127}
128
129void System::set_force_cap(double value) {
130 force_cap = value;
131 propagation->recalc_forces = true;
132}
133
134void System::set_min_global_cut(double value) {
135 min_global_cut = value;
136 on_verlet_skin_change();
137}
138
140 if (topology == CellStructureType::REGULAR) {
141 cell_structure->set_regular_decomposition(get_interaction_range());
142 } else if (topology == CellStructureType::NSQUARE) {
143 cell_structure->set_atom_decomposition();
144 } else {
145 assert(topology == CellStructureType::HYBRID);
146 /* Get current HybridDecomposition to extract n_square_types */
147 auto &old_hybrid_decomposition = dynamic_cast<HybridDecomposition const &>(
148 std::as_const(*cell_structure).decomposition());
149 cell_structure->set_hybrid_decomposition(
150 old_hybrid_decomposition.get_cutoff_regular(),
151 old_hybrid_decomposition.get_n_square_types());
152 }
153}
154
156 set_cell_structure_topology(cell_structure->decomposition_type());
157}
158
159void System::on_boxl_change(bool skip_method_adaption) {
160 update_local_geo();
161 rebuild_cell_structure();
162
163 /* Now give methods a chance to react to the change in box length */
164 if (not skip_method_adaption) {
165 lb.on_boxl_change();
166 ek.on_boxl_change();
167#ifdef ELECTROSTATICS
168 coulomb.on_boxl_change();
169#endif
170#ifdef DIPOLES
171 dipoles.on_boxl_change();
172#endif
173 }
174 Constraints::constraints.on_boxl_change();
175}
176
177void System::veto_boxl_change(bool skip_particle_checks) const {
178 if (not skip_particle_checks) {
179 auto const n_part = boost::mpi::all_reduce(
180 ::comm_cart, cell_structure->local_particles().size(), std::plus<>());
181 if (n_part > 0ul) {
182 throw std::runtime_error(
183 "Cannot reset the box length when particles are present");
184 }
185 }
186 Constraints::constraints.veto_boxl_change();
187 lb.veto_boxl_change();
188 ek.veto_boxl_change();
189}
190
192 update_local_geo();
193 lb.on_node_grid_change();
194 ek.on_node_grid_change();
195#ifdef ELECTROSTATICS
196 coulomb.on_node_grid_change();
197#endif
198#ifdef DIPOLES
199 dipoles.on_node_grid_change();
200#endif
201 rebuild_cell_structure();
202}
203
205#ifdef ELECTROSTATICS
206 coulomb.on_periodicity_change();
207#endif
208
209#ifdef DIPOLES
210 dipoles.on_periodicity_change();
211#endif
212
213#ifdef STOKESIAN_DYNAMICS
214 if (propagation->integ_switch == INTEG_METHOD_SD) {
215 if (box_geo->periodic(0u) or box_geo->periodic(1u) or box_geo->periodic(2u))
216 runtimeErrorMsg() << "Stokesian Dynamics requires periodicity "
217 << "(False, False, False)\n";
218 }
219#endif
220 on_verlet_skin_change();
221}
222
225 lb.on_cell_structure_change();
226 ek.on_cell_structure_change();
227#ifdef ELECTROSTATICS
228 coulomb.on_cell_structure_change();
229#endif
230#ifdef DIPOLES
231 dipoles.on_cell_structure_change();
232#endif
233}
234
235void System::on_thermostat_param_change() { reinit_thermo = true; }
236
238 rebuild_cell_structure();
239#ifdef ELECTROSTATICS
240 coulomb.on_coulomb_change();
241#endif
242#ifdef DIPOLES
243 dipoles.on_dipoles_change();
244#endif
245 on_short_range_ia_change();
246}
247
249 lb.on_temperature_change();
250 ek.on_temperature_change();
251}
252
254 lb.on_timestep_change();
255 ek.on_timestep_change();
256 on_thermostat_param_change();
257}
258
260 rebuild_cell_structure();
261 propagation->recalc_forces = true;
262}
263
265 nonbonded_ias->recalc_maximal_cutoffs();
266 rebuild_cell_structure();
267 propagation->recalc_forces = true;
268}
269
271#ifdef ELECTROSTATICS
272 coulomb.on_coulomb_change();
273#endif
274 on_short_range_ia_change();
275}
276
278#ifdef DIPOLES
279 dipoles.on_dipoles_change();
280#endif
281 on_short_range_ia_change();
282}
283
284void System::on_constraint_change() { propagation->recalc_forces = true; }
285
287 propagation->recalc_forces = true;
288}
289
291 cell_structure->update_ghosts_and_resort_particle(get_global_ghost_flags());
292 propagation->recalc_forces = true;
293}
294
296 if (cell_structure->decomposition_type() == CellStructureType::HYBRID) {
297 cell_structure->set_resort_particles(Cells::RESORT_GLOBAL);
298 } else {
299 cell_structure->set_resort_particles(Cells::RESORT_LOCAL);
300 }
301#ifdef ELECTROSTATICS
302 coulomb.on_particle_change();
303#endif
304#ifdef DIPOLES
305 dipoles.on_particle_change();
306#endif
307 propagation->recalc_forces = true;
308
309 /* the particle information is no longer valid */
311}
312
314#ifdef ELECTROSTATICS
315 coulomb.on_particle_change();
316#endif
317}
318
320#ifdef VIRTUAL_SITES
321#ifdef VIRTUAL_SITES_RELATIVE
322 vs_relative_update_particles(*cell_structure, *box_geo);
323#endif
324 cell_structure->update_ghosts_and_resort_particle(get_global_ghost_flags());
325#endif
326
327#ifdef ELECTROSTATICS
328 update_icc_particles();
329#endif
330
331 // Here we initialize volume conservation
332 // This function checks if the reference volumes have been set and if
333 // necessary calculates them
335}
336
338 /* Prepare particle structure: Communication step: number of ghosts and ghost
339 * information */
340 cell_structure->update_ghosts_and_resort_particle(get_global_ghost_flags());
341 update_dependent_particles();
342
343#ifdef ELECTROSTATICS
344 coulomb.on_observable_calc();
345#endif
346
347#ifdef DIPOLES
348 dipoles.on_observable_calc();
349#endif
350
352}
353
359
361 auto max_cut = INACTIVE_CUTOFF;
362 max_cut = std::max(max_cut, get_min_global_cut());
363#ifdef ELECTROSTATICS
364 max_cut = std::max(max_cut, coulomb.cutoff());
365#endif
366#ifdef DIPOLES
367 max_cut = std::max(max_cut, dipoles.cutoff());
368#endif
369 if (::communicator.size > 1) {
370 // If there is just one node, the bonded cutoff can be omitted
371 // because bond partners are always on the local node.
372 max_cut = std::max(max_cut, maximal_cutoff_bonded());
373 }
374 max_cut = std::max(max_cut, nonbonded_ias->maximal_cutoff());
375
376 max_cut = std::max(max_cut, collision_detection_cutoff());
377 return max_cut;
378}
379
381 try {
382#ifdef ELECTROSTATICS
383 coulomb.sanity_checks();
384#endif
385#ifdef DIPOLES
386 dipoles.sanity_checks();
387#endif
388 } catch (std::runtime_error const &err) {
389 runtimeErrorMsg() << err.what();
390 return true;
391 }
392 return false;
393}
394
396 auto const max_cut = maximal_cutoff();
397 auto const verlet_skin = cell_structure->get_verlet_skin();
398 /* Consider skin only if there are actually interactions */
399 return (max_cut > 0.) ? max_cut + verlet_skin : INACTIVE_CUTOFF;
400}
401
403 box_geo->set_length(box_l);
404 on_boxl_change();
405}
406
408 // sanity checks
409 integrator_sanity_checks();
410#ifdef NPT
412#endif
413 long_range_interactions_sanity_checks();
414 lb.sanity_checks();
415 ek.sanity_checks();
416
417 /* Prepare the thermostat */
418 if (reinit_thermo) {
419 thermostat->recalc_prefactors(time_step);
420 reinit_thermo = false;
421 propagation->recalc_forces = true;
422 }
423
424#ifdef NPT
425 if (propagation->integ_switch == INTEG_METHOD_NPT_ISO) {
426 npt_ensemble_init(box_geo->length(), propagation->recalc_forces);
427 }
428#endif
429
431
432#ifdef ADDITIONAL_CHECKS
433 if (!Utils::Mpi::all_compare(::comm_cart, cell_structure->use_verlet_list)) {
434 runtimeErrorMsg() << "Nodes disagree about use of verlet lists.";
435 }
436#ifdef ELECTROSTATICS
437 {
438 auto const &actor = coulomb.impl->solver;
439 if (not Utils::Mpi::all_compare(::comm_cart, static_cast<bool>(actor)) or
440 (actor and not Utils::Mpi::all_compare(::comm_cart, (*actor).index())))
441 runtimeErrorMsg() << "Nodes disagree about Coulomb long-range method";
442 }
443#endif
444#ifdef DIPOLES
445 {
446 auto const &actor = dipoles.impl->solver;
447 if (not Utils::Mpi::all_compare(::comm_cart, static_cast<bool>(actor)) or
448 (actor and not Utils::Mpi::all_compare(::comm_cart, (*actor).index())))
449 runtimeErrorMsg() << "Nodes disagree about dipolar long-range method";
450 }
451#endif
452#endif /* ADDITIONAL_CHECKS */
453
454 on_observable_calc();
455}
456
457/**
458 * @brief Returns the ghost flags required for running pair
459 * kernels for the global state, e.g. the force calculation.
460 * @return Required data parts;
461 */
463 /* Position and Properties are always requested. */
465
466 if (lb.is_solver_set())
467 data_parts |= Cells::DATA_PART_MOMENTUM;
468
469 if (thermostat->thermo_switch & THERMO_DPD)
470 data_parts |= Cells::DATA_PART_MOMENTUM;
471
472 if (thermostat->thermo_switch & THERMO_BOND) {
473 data_parts |= Cells::DATA_PART_MOMENTUM;
474 data_parts |= Cells::DATA_PART_BONDS;
475 }
476
477#ifdef COLLISION_DETECTION
479 data_parts |= Cells::DATA_PART_BONDS;
480 }
481#endif
482
483 return data_parts;
484}
485
486} // namespace System
CellStructureType
Cell structure topology.
@ NSQUARE
Atom decomposition (N-square).
@ HYBRID
Hybrid decomposition.
@ REGULAR
Regular decomposition.
@ INTEG_METHOD_SD
@ INTEG_METHOD_NPT_ISO
@ THERMO_BOND
@ THERMO_DPD
Vector implementation and trait types for boost qvm interoperability.
float u[3]
double maximal_cutoff_bonded()
Calculate the maximal cutoff of bonded interactions, required to determine the cell size for communic...
Data structures for bonded interactions.
Hybrid decomposition cell system.
void init_volume_conservation(CellStructure &cs)
Initialize volume conservation.
static LocalBox make_regular_decomposition(Utils::Vector3d const &box_l, Utils::Vector3i const &node_index, Utils::Vector3i const &node_grid)
Definition LocalBox.hpp:69
Main system class.
double get_interaction_range() const
Get the interaction range.
double maximal_cutoff() const
Calculate the maximal cutoff of all interactions.
void on_constraint_change()
Called every time a constraint is changed.
unsigned get_global_ghost_flags() const
Returns the ghost flags required for running pair kernels for the global state, e....
void on_boxl_change(bool skip_method_adaption=false)
Called when the box length has changed.
void set_box_l(Utils::Vector3d const &box_l)
Change the box dimensions.
void set_force_cap(double value)
Set force_cap.
void update_dependent_particles()
Update particles with properties depending on other particles, namely virtual sites and ICC charges.
void on_lb_boundary_conditions_change()
Called when the LB boundary conditions change (geometry, slip velocity, or both).
void veto_boxl_change(bool skip_particle_checks=false) const
void set_time_step(double value)
Set time_step.
void on_particle_change()
Called every time a particle property changes.
bool long_range_interactions_sanity_checks() const
Check electrostatic and magnetostatic methods are properly initialized.
void on_particle_charge_change()
Called every time a particle charge changes.
void on_particle_local_change()
Called every time a particle local property changes.
void on_observable_calc()
called before calculating observables, i.e.
void check_kT(double value) const
Veto temperature change.
static std::shared_ptr< System > create()
void set_min_global_cut(double value)
Set min_global_cut.
void set_cell_structure_topology(CellStructureType topology)
Change cell structure topology.
void rebuild_cell_structure()
Rebuild cell lists.
Collision_parameters collision_params
Parameters for collision detection.
Definition collision.cpp:73
@ OFF
Deactivate collision detection.
double collision_detection_cutoff()
Communicator communicator
boost::mpi::communicator comm_cart
The communicator.
This file contains the defaults for ESPResSo.
This file contains the errorhandling code for severe errors, like a broken bond or illegal parameter ...
#define runtimeErrorMsg()
ICC is a method that allows to take into account the influence of arbitrarily shaped dielectric inter...
ImmersedBoundaries immersed_boundaries
@ DATA_PART_MOMENTUM
Particle::m.
@ DATA_PART_PROPERTIES
Particle::p.
@ DATA_PART_BONDS
Particle::bonds.
@ DATA_PART_POSITION
Particle::r.
Constraints< ParticleRange, Constraint > constraints
void reset_system()
System & get_system()
bool is_system_set()
static std::shared_ptr< System > instance
void set_system(std::shared_ptr< System > new_instance)
bool all_compare(boost::mpi::communicator const &comm, T const &value)
Compare values on all nodes.
constexpr double INACTIVE_CUTOFF
Cutoff for deactivated interactions.
void npt_ensemble_init(Utils::Vector3d const &box_l, bool recalc_forces)
Definition npt.cpp:112
void integrator_npt_sanity_checks()
Definition npt.cpp:122
Exports for the NpT code.
void invalidate_fetch_cache()
Invalidate the fetch cache for get_particle_data.
void clear_particle_node()
Invalidate particle_node.
Particles creation and deletion.
void vs_relative_update_particles(CellStructure &cell_structure, BoxGeometry const &box_geo)
Definition relative.cpp:122
Utils::Vector3i calc_node_index() const
Calculate the node index in the Cartesian topology.
Utils::Vector3i node_grid
Routines to thermalize the center of mass and distance of a particle pair.