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-2026 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 "GpuParticleData.hpp"
27#include "LocalBox.hpp"
28#include "Observable_stat.hpp"
29#include "PropagationMode.hpp"
30#include "accumulators/AutoUpdateAccumulators.hpp"
36#include "collision_detection/CollisionDetection.hpp"
37#include "communication.hpp"
39#include "errorhandling.hpp"
42#include "npt.hpp"
43#include "particle_node.hpp"
47#include "thermostat.hpp"
48#include "virtual_sites/com.hpp"
50
51#include <utils/Vector.hpp>
53
54#include <boost/mpi/collectives/all_reduce.hpp>
55
56#include <algorithm>
57#include <cassert>
58#include <cstddef>
59#include <functional>
60#include <memory>
61#include <stdexcept>
62#include <utility>
63
64namespace System {
65
66static std::shared_ptr<System> instance = System::create();
67
68std::shared_ptr<System> System::create() {
69 auto handle = std::make_shared<System>(Private());
70 handle->initialize();
71 return handle;
72}
73
75 box_geo = std::make_shared<BoxGeometry>();
76 local_geo = std::make_shared<LocalBox>();
77 cell_structure = std::make_shared<CellStructure>(*box_geo);
78 cell_structure->set_kokkos_handle(::kokkos_handle);
79#ifdef ESPRESSO_CUDA
80 gpu = std::make_shared<GpuParticleData>();
81#endif
82 propagation = std::make_shared<Propagation>();
83 bonded_ias = std::make_shared<BondedInteractionsMap>();
84 thermostat = std::make_shared<Thermostat::Thermostat>();
85 nonbonded_ias = std::make_shared<InteractionsNonBonded>();
86 comfixed = std::make_shared<ComFixed>();
87 galilei = std::make_shared<Galilei>();
88 oif_global = std::make_shared<OifGlobal>();
89 immersed_boundaries = std::make_shared<ImmersedBoundaries>();
90#ifdef ESPRESSO_COLLISION_DETECTION
91 collision_detection =
92 std::make_shared<CollisionDetection::CollisionDetection>();
93#endif
94 bond_breakage = std::make_shared<BondBreakage::BondBreakage>();
95 lees_edwards = std::make_shared<LeesEdwards::LeesEdwards>();
96 auto_update_accumulators =
97 std::make_shared<Accumulators::AutoUpdateAccumulators>();
98 constraints = std::make_shared<Constraints::Constraints>();
99 steepest_descent = std::make_shared<SteepestDescent>();
100#ifdef ESPRESSO_STOKESIAN_DYNAMICS
101 stokesian_dynamics = std::make_shared<StokesianDynamics>();
102#endif
103#ifdef ESPRESSO_NPT
104 nptiso = std::make_shared<NptIsoParameters>();
105 npt_inst_pressure = std::make_shared<InstantaneousPressure>();
106#endif
107 reinit_thermo = true;
108 time_step = -1.;
109 sim_time = 0.;
110 force_cap = 0.;
111 min_global_cut = inactive_cutoff;
112}
113
114void System::initialize() {
115 auto handle = shared_from_this();
116 cell_structure->bind_system(handle);
117 lees_edwards->bind_system(handle);
118 bonded_ias->bind_system(handle);
119 thermostat->bind_system(handle);
120 nonbonded_ias->bind_system(handle);
121 oif_global->bind_system(handle);
122 immersed_boundaries->bind_system(handle);
123#ifdef ESPRESSO_COLLISION_DETECTION
124 collision_detection->bind_system(handle);
125#endif
126 auto_update_accumulators->bind_system(handle);
127 constraints->bind_system(handle);
128#ifdef ESPRESSO_CUDA
129 gpu->bind_system(handle);
130 gpu->initialize();
131#endif
132 lb.bind_system(handle);
133 ek.bind_system(handle);
134}
135
136void reset_system() { instance.reset(); }
137
138void set_system(std::shared_ptr<System> new_instance) {
140}
141
143
144bool is_same_system(System const *const system) {
145 assert(system != nullptr);
146 return system == instance.get();
147}
148
149void System::set_time_step(double value) {
150 if (value <= 0.)
151 throw std::domain_error("time_step must be > 0.");
152 if (lb.is_solver_set()) {
153 lb.veto_time_step(value);
154 }
155 if (ek.is_solver_set()) {
156 ek.veto_time_step(value);
157 }
158 time_step = value;
159 on_timestep_change();
160}
161
162void System::check_kT(double value) const {
163 if (lb.is_solver_set()) {
164 lb.veto_kT(value);
165 }
166 if (ek.is_solver_set()) {
167 ek.veto_kT(value);
168 }
169}
170
171void System::set_force_cap(double value) {
172 force_cap = value;
173 propagation->recalc_forces = true;
174}
175
176void System::set_min_global_cut(double value) {
177 min_global_cut = value;
178 on_verlet_skin_change();
179}
180
183 if (cell_structure->decomposition_type() == CellStructureType::REGULAR) {
184 // get fully connected info from existing regular decomposition
186 dynamic_cast<RegularDecomposition const &>(
187 std::as_const(*cell_structure).decomposition());
188 cell_structure->set_regular_decomposition(
189 get_interaction_range(),
190 old_regular_decomposition.fully_connected_boundary());
191 } else { // prev. decomposition is not a regular decomposition
192 cell_structure->set_regular_decomposition(get_interaction_range(), {});
193 }
194 } else if (topology == CellStructureType::NSQUARE) {
195 cell_structure->set_atom_decomposition();
196 } else {
198 /* Get current HybridDecomposition to extract n_square_types */
199 auto &old_hybrid_decomposition = dynamic_cast<HybridDecomposition const &>(
200 std::as_const(*cell_structure).decomposition());
201 cell_structure->set_hybrid_decomposition(
202 old_hybrid_decomposition.get_cutoff_regular(),
203 old_hybrid_decomposition.get_n_square_types());
204 }
205}
206
208 set_cell_structure_topology(cell_structure->decomposition_type());
209}
210
212 update_local_geo();
213 rebuild_cell_structure();
214
215 /* Now give methods a chance to react to the change in box length */
217 lb.on_boxl_change();
218 ek.on_boxl_change();
219#ifdef ESPRESSO_ELECTROSTATICS
220 coulomb.on_boxl_change();
221#endif
222#ifdef ESPRESSO_DIPOLES
223 dipoles.on_boxl_change();
224#endif
225 }
226 constraints->on_boxl_change();
227}
228
231 auto const n_part = boost::mpi::all_reduce(
232 ::comm_cart, cell_structure->local_particles().size(), std::plus<>());
233 if (n_part > 0ul) {
234 throw std::runtime_error(
235 "Cannot reset the box length when particles are present");
236 }
237 }
238 constraints->veto_boxl_change();
239 lb.veto_boxl_change();
240 ek.veto_boxl_change();
241}
242
244 update_local_geo();
245 lb.on_node_grid_change();
246 ek.on_node_grid_change();
247#ifdef ESPRESSO_ELECTROSTATICS
248 coulomb.on_node_grid_change();
249#endif
250#ifdef ESPRESSO_DIPOLES
251 dipoles.on_node_grid_change();
252#endif
253 rebuild_cell_structure();
254}
255
257#ifdef ESPRESSO_ELECTROSTATICS
258 coulomb.on_periodicity_change();
259#endif
260
261#ifdef ESPRESSO_DIPOLES
262 dipoles.on_periodicity_change();
263#endif
264
265#ifdef ESPRESSO_STOKESIAN_DYNAMICS
266 if (propagation->integ_switch == INTEG_METHOD_SD) {
267 if (box_geo->periodic(0u) or box_geo->periodic(1u) or box_geo->periodic(2u))
268 runtimeErrorMsg() << "Stokesian Dynamics requires periodicity "
269 << "(False, False, False)\n";
270 }
271#endif
272 on_verlet_skin_change();
273}
274
277 lb.on_cell_structure_change();
278 ek.on_cell_structure_change();
279#ifdef ESPRESSO_ELECTROSTATICS
280 coulomb.on_cell_structure_change();
281#endif
282#ifdef ESPRESSO_DIPOLES
283 dipoles.on_cell_structure_change();
284#endif
285}
286
287void System::on_thermostat_param_change() { reinit_thermo = true; }
288
290 rebuild_cell_structure();
291#ifdef ESPRESSO_ELECTROSTATICS
292 coulomb.on_coulomb_change();
293#endif
294#ifdef ESPRESSO_DIPOLES
295 dipoles.on_dipoles_change();
296#endif
297 on_short_range_ia_change();
298}
299
301 lb.on_temperature_change();
302 ek.on_temperature_change();
303}
304
306 lb.on_timestep_change();
307 ek.on_timestep_change();
308 on_thermostat_param_change();
309}
310
312 rebuild_cell_structure();
313 propagation->recalc_forces = true;
314}
315
317 nonbonded_ias->recalc_maximal_cutoffs();
318 rebuild_cell_structure();
319 on_thermostat_param_change();
320 propagation->recalc_forces = true;
321}
322
324#ifdef ESPRESSO_ELECTROSTATICS
325 coulomb.on_coulomb_change();
326#endif
327 on_short_range_ia_change();
328}
329
331#ifdef ESPRESSO_DIPOLES
332 dipoles.on_dipoles_change();
333#endif
334 on_short_range_ia_change();
335}
336
337void System::on_constraint_change() { propagation->recalc_forces = true; }
338
340 propagation->recalc_forces = true;
341}
342
344 cell_structure->update_ghosts_and_resort_particle(get_global_ghost_flags());
345 propagation->recalc_forces = true;
346 propagation->recalc_used_propagations = true;
347}
348
350 if (cell_structure->decomposition_type() == CellStructureType::HYBRID) {
351 cell_structure->set_resort_particles(Cells::RESORT_GLOBAL);
352 } else {
353 cell_structure->set_resort_particles(Cells::RESORT_LOCAL);
354 }
355#ifdef ESPRESSO_ELECTROSTATICS
356 coulomb.on_particle_change();
357#endif
358#ifdef ESPRESSO_DIPOLES
359 dipoles.on_particle_change();
360#endif
361 propagation->recalc_forces = true;
362 propagation->recalc_used_propagations = true;
363
364 /* the particle information is no longer valid */
366 cell_structure->clear_local_properties();
367}
368
370#ifdef ESPRESSO_ELECTROSTATICS
371 coulomb.on_particle_change();
372#endif
373}
374
376#ifdef ESPRESSO_VIRTUAL_SITES
377 if (propagation->recalc_used_propagations) {
378 update_used_propagations();
379 }
380#ifdef ESPRESSO_VIRTUAL_SITES_RELATIVE
381 if (propagation->used_propagations &
384 vs_relative_update_particles(*cell_structure, *box_geo);
385 }
386#endif
387#ifdef ESPRESSO_VIRTUAL_SITES_CENTER_OF_MASS
388 if (propagation->used_propagations &
390 vs_com_update_particles(*cell_structure, *box_geo);
391 }
392#endif
393 cell_structure->update_ghosts_and_resort_particle(get_global_ghost_flags());
394#endif
395
396#ifdef ESPRESSO_ELECTROSTATICS
397 if (has_icc_enabled()) {
398 rebuild_aosoa();
399 update_icc_particles();
400 }
401#endif
402
403 // Here we initialize volume conservation
404 // This function checks if the reference volumes have been set and if
405 // necessary calculates them
406 immersed_boundaries->init_volume_conservation(*cell_structure);
407}
408
410 /* Prepare particle structure: Communication step: number of ghosts and ghost
411 * information */
412 cell_structure->update_ghosts_and_resort_particle(get_global_ghost_flags());
413 update_dependent_particles();
414
415#ifdef ESPRESSO_ELECTROSTATICS
416 coulomb.on_observable_calc();
417#endif
418
419#ifdef ESPRESSO_DIPOLES
420 dipoles.on_observable_calc();
421#endif
422
424 rebuild_aosoa();
425}
426
428#ifdef ESPRESSO_COLLISION_DETECTION
429 auto const collision_detection_cutoff = collision_detection->cutoff();
430#else
432#endif
433
435}
436
437void System::on_lees_edwards_change() { lb.on_lees_edwards_change(); }
438
444
446 auto max_cut = inactive_cutoff;
447 max_cut = std::max(max_cut, get_min_global_cut());
448 max_cut = std::max(max_cut, coulomb.cutoff());
449 max_cut = std::max(max_cut, dipoles.cutoff());
450 if (::communicator.size > 1) {
451 // If there is just one node, the bonded cutoff can be omitted
452 // because bond partners are always on the local node.
453 max_cut = std::max(max_cut, bonded_ias->maximal_cutoff());
454 }
455 max_cut = std::max(max_cut, nonbonded_ias->maximal_cutoff());
456
457#ifdef ESPRESSO_COLLISION_DETECTION
458 max_cut = std::max(max_cut, collision_detection->cutoff());
459#endif
460 return max_cut;
461}
462
464 try {
465#ifdef ESPRESSO_ELECTROSTATICS
466 coulomb.sanity_checks();
467#endif
468#ifdef ESPRESSO_DIPOLES
469 dipoles.sanity_checks();
470#endif
471 } catch (std::runtime_error const &err) {
472 runtimeErrorMsg() << err.what();
473 return true;
474 }
475 return false;
476}
477
479 auto const max_cut = maximal_cutoff();
480 auto const verlet_skin = cell_structure->get_verlet_skin();
481 /* Consider skin only if there are actually interactions */
482 return (max_cut > 0.) ? max_cut + verlet_skin : inactive_cutoff;
483}
484
486 box_geo->set_length(box_l);
487 on_boxl_change();
488}
489
491 // sanity checks
492 integrator_sanity_checks();
493 long_range_interactions_sanity_checks();
494 lb.sanity_checks();
495 ek.sanity_checks();
496
497#ifdef ESPRESSO_NPT
498 if (propagation->integ_switch == INTEG_METHOD_NPT_ISO_AND ||
499 propagation->integ_switch == INTEG_METHOD_NPT_ISO_MTK) {
500 npt_ensemble_init(propagation->recalc_forces);
501 }
502#endif
503
504 /* Prepare the thermostat */
505 if (reinit_thermo) {
506 thermostat->recalc_prefactors(time_step);
507 reinit_thermo = false;
508 propagation->recalc_forces = true;
509 }
510
512 cell_structure->clear_local_properties();
513
514#ifdef ESPRESSO_ADDITIONAL_CHECKS
515 if (!Utils::Mpi::all_compare(::comm_cart, cell_structure->use_verlet_list)) {
516 runtimeErrorMsg() << "Nodes disagree about use of verlet lists.";
517 }
518#ifdef ESPRESSO_ELECTROSTATICS
519 {
520 auto const &actor = coulomb.impl->solver;
521 if (not Utils::Mpi::all_compare(::comm_cart, static_cast<bool>(actor)) or
522 (actor and not Utils::Mpi::all_compare(::comm_cart, (*actor).index())))
523 runtimeErrorMsg() << "Nodes disagree about Coulomb long-range method";
524 }
525#endif
526#ifdef ESPRESSO_DIPOLES
527 {
528 auto const &actor = dipoles.impl->solver;
529 if (not Utils::Mpi::all_compare(::comm_cart, static_cast<bool>(actor)) or
530 (actor and not Utils::Mpi::all_compare(::comm_cart, (*actor).index())))
531 runtimeErrorMsg() << "Nodes disagree about dipolar long-range method";
532 }
533#endif
534#endif /* ESPRESSO_ADDITIONAL_CHECKS */
535
536 on_observable_calc();
537}
538
539/**
540 * @brief Returns the ghost flags required for running pair
541 * kernels for the global state, e.g. the force calculation.
542 * @return Required data parts;
543 */
545 /* Position and Properties are always requested. */
547
548 if (lb.is_solver_set())
550
551 if (thermostat->thermo_switch & THERMO_DPD)
553
554 if (thermostat->thermo_switch & THERMO_BOND) {
557 }
558
559#ifdef ESPRESSO_COLLISION_DETECTION
560 if (not collision_detection->is_off()) {
562 }
563#endif
564
565 return data_parts;
566}
567
568#ifdef ESPRESSO_NPT
570 return (propagation->integ_switch == INTEG_METHOD_NPT_ISO_AND) or
571 (propagation->integ_switch == INTEG_METHOD_NPT_ISO_MTK);
572}
573#endif
574
576#ifdef ESPRESSO_NPT
577 if (has_npt_enabled()) {
578 return &npt_inst_pressure->p_vir;
579 }
580#endif
581 return nullptr;
582}
583
584#ifdef ESPRESSO_COLLISION_DETECTION
586 return not collision_detection->is_off();
587}
588#endif
589
590} // namespace System
CellStructureType
Cell structure topology.
@ NSQUARE
Atom decomposition (N-square).
@ HYBRID
Hybrid decomposition.
@ REGULAR
Regular decomposition.
@ INTEG_METHOD_NPT_ISO_AND
@ INTEG_METHOD_SD
@ INTEG_METHOD_NPT_ISO_MTK
@ THERMO_BOND
@ THERMO_DPD
Vector implementation and trait types for boost qvm interoperability.
Data structures for bonded interactions.
Hybrid decomposition cell system.
static LocalBox make_regular_decomposition(Utils::Vector3d const &box_l, Utils::Vector3i const &node_index, Utils::Vector3i const &node_grid)
Definition LocalBox.hpp:72
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()
Utils::Vector3d * get_npt_virial() const
void set_min_global_cut(double value)
Set min_global_cut.
bool has_npt_enabled() const
void set_cell_structure_topology(CellStructureType topology)
Change cell structure topology.
void rebuild_cell_structure()
Rebuild cell lists.
bool has_collision_detection_enabled() const
void vs_com_update_particles(CellStructure &cell_structure, BoxGeometry const &box_geo)
Definition com.cpp:131
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.
std::shared_ptr< KokkosHandle > kokkos_handle
Communicator communicator
boost::mpi::communicator comm_cart
The communicator.
constexpr double inactive_cutoff
Special cutoff value for an inactive interaction.
Definition config.hpp:53
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...
@ DATA_PART_MOMENTUM
Particle::m.
@ DATA_PART_PROPERTIES
Particle::p.
@ DATA_PART_BONDS
Particle::bonds.
@ DATA_PART_POSITION
Particle::r.
System & get_system()
static std::shared_ptr< System > instance
bool is_same_system(System const *const system)
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.
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:121
See for the Stokesian dynamics method used here.
void update_verlet_state(System::System const &system, double const collision_cut)
Utils::Vector3i calc_node_index() const
Calculate the node index in the Cartesian topology.
Utils::Vector3i node_grid
Regular decomposition cell system.
Routines to thermalize the center of mass and distance of a particle pair.