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 "PropagationMode.hpp"
29#include "accumulators/AutoUpdateAccumulators.hpp"
35#include "collision_detection/CollisionDetection.hpp"
36#include "communication.hpp"
38#include "errorhandling.hpp"
41#include "npt.hpp"
42#include "particle_node.hpp"
45#include "thermostat.hpp"
46#include "virtual_sites/com.hpp"
48
49#include <utils/Vector.hpp>
51
52#include <boost/mpi/collectives/all_reduce.hpp>
53
54#include <algorithm>
55#include <cassert>
56#include <cstddef>
57#include <functional>
58#include <memory>
59#include <stdexcept>
60#include <utility>
61
62namespace System {
63
64static std::shared_ptr<System> instance = System::create();
65
66std::shared_ptr<System> System::create() {
67 auto handle = std::make_shared<System>(Private());
68 handle->initialize();
69 return handle;
70}
71
73 box_geo = std::make_shared<BoxGeometry>();
74 local_geo = std::make_shared<LocalBox>();
75 cell_structure = std::make_shared<CellStructure>(*box_geo);
76#ifdef ESPRESSO_SHARED_MEMORY_PARALLELISM
77 cell_structure->set_kokkos_handle(::kokkos_handle);
78#endif
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
144void System::set_time_step(double value) {
145 if (value <= 0.)
146 throw std::domain_error("time_step must be > 0.");
147 if (lb.is_solver_set()) {
148 lb.veto_time_step(value);
149 }
150 if (ek.is_solver_set()) {
151 ek.veto_time_step(value);
152 }
153 time_step = value;
154 on_timestep_change();
155}
156
157void System::check_kT(double value) const {
158 if (lb.is_solver_set()) {
159 lb.veto_kT(value);
160 }
161 if (ek.is_solver_set()) {
162 ek.veto_kT(value);
163 }
164}
165
166void System::set_force_cap(double value) {
167 force_cap = value;
168 propagation->recalc_forces = true;
169}
170
171void System::set_min_global_cut(double value) {
172 min_global_cut = value;
173 on_verlet_skin_change();
174}
175
178 if (cell_structure->decomposition_type() == CellStructureType::REGULAR) {
179 // get fully connected info from existing regular decomposition
181 dynamic_cast<RegularDecomposition const &>(
182 std::as_const(*cell_structure).decomposition());
183 cell_structure->set_regular_decomposition(
184 get_interaction_range(),
185 old_regular_decomposition.fully_connected_boundary());
186 } else { // prev. decomposition is not a regular decomposition
187 cell_structure->set_regular_decomposition(get_interaction_range(), {});
188 }
189 } else if (topology == CellStructureType::NSQUARE) {
190 cell_structure->set_atom_decomposition();
191 } else {
193 /* Get current HybridDecomposition to extract n_square_types */
194 auto &old_hybrid_decomposition = dynamic_cast<HybridDecomposition const &>(
195 std::as_const(*cell_structure).decomposition());
196 cell_structure->set_hybrid_decomposition(
197 old_hybrid_decomposition.get_cutoff_regular(),
198 old_hybrid_decomposition.get_n_square_types());
199 }
200}
201
203 set_cell_structure_topology(cell_structure->decomposition_type());
204}
205
207 update_local_geo();
208 rebuild_cell_structure();
209
210 /* Now give methods a chance to react to the change in box length */
212 lb.on_boxl_change();
213 ek.on_boxl_change();
214#ifdef ESPRESSO_ELECTROSTATICS
215 coulomb.on_boxl_change();
216#endif
217#ifdef ESPRESSO_DIPOLES
218 dipoles.on_boxl_change();
219#endif
220 }
221 constraints->on_boxl_change();
222}
223
226 auto const n_part = boost::mpi::all_reduce(
227 ::comm_cart, cell_structure->local_particles().size(), std::plus<>());
228 if (n_part > 0ul) {
229 throw std::runtime_error(
230 "Cannot reset the box length when particles are present");
231 }
232 }
233 constraints->veto_boxl_change();
234 lb.veto_boxl_change();
235 ek.veto_boxl_change();
236}
237
239 update_local_geo();
240 lb.on_node_grid_change();
241 ek.on_node_grid_change();
242#ifdef ESPRESSO_ELECTROSTATICS
243 coulomb.on_node_grid_change();
244#endif
245#ifdef ESPRESSO_DIPOLES
246 dipoles.on_node_grid_change();
247#endif
248 rebuild_cell_structure();
249}
250
252#ifdef ESPRESSO_ELECTROSTATICS
253 coulomb.on_periodicity_change();
254#endif
255
256#ifdef ESPRESSO_DIPOLES
257 dipoles.on_periodicity_change();
258#endif
259
260#ifdef ESPRESSO_STOKESIAN_DYNAMICS
261 if (propagation->integ_switch == INTEG_METHOD_SD) {
262 if (box_geo->periodic(0u) or box_geo->periodic(1u) or box_geo->periodic(2u))
263 runtimeErrorMsg() << "Stokesian Dynamics requires periodicity "
264 << "(False, False, False)\n";
265 }
266#endif
267 on_verlet_skin_change();
268}
269
272 lb.on_cell_structure_change();
273 ek.on_cell_structure_change();
274#ifdef ESPRESSO_ELECTROSTATICS
275 coulomb.on_cell_structure_change();
276#endif
277#ifdef ESPRESSO_DIPOLES
278 dipoles.on_cell_structure_change();
279#endif
280}
281
282void System::on_thermostat_param_change() { reinit_thermo = true; }
283
285 rebuild_cell_structure();
286#ifdef ESPRESSO_ELECTROSTATICS
287 coulomb.on_coulomb_change();
288#endif
289#ifdef ESPRESSO_DIPOLES
290 dipoles.on_dipoles_change();
291#endif
292 on_short_range_ia_change();
293}
294
296 lb.on_temperature_change();
297 ek.on_temperature_change();
298}
299
301 lb.on_timestep_change();
302 ek.on_timestep_change();
303 on_thermostat_param_change();
304}
305
307 rebuild_cell_structure();
308 propagation->recalc_forces = true;
309}
310
312 nonbonded_ias->recalc_maximal_cutoffs();
313 rebuild_cell_structure();
314 on_thermostat_param_change();
315 propagation->recalc_forces = true;
316}
317
319#ifdef ESPRESSO_ELECTROSTATICS
320 coulomb.on_coulomb_change();
321#endif
322 on_short_range_ia_change();
323}
324
326#ifdef ESPRESSO_DIPOLES
327 dipoles.on_dipoles_change();
328#endif
329 on_short_range_ia_change();
330}
331
332void System::on_constraint_change() { propagation->recalc_forces = true; }
333
335 propagation->recalc_forces = true;
336}
337
339 cell_structure->update_ghosts_and_resort_particle(get_global_ghost_flags());
340 propagation->recalc_forces = true;
341 propagation->recalc_used_propagations = true;
342}
343
345 if (cell_structure->decomposition_type() == CellStructureType::HYBRID) {
346 cell_structure->set_resort_particles(Cells::RESORT_GLOBAL);
347 } else {
348 cell_structure->set_resort_particles(Cells::RESORT_LOCAL);
349 }
350#ifdef ESPRESSO_ELECTROSTATICS
351 coulomb.on_particle_change();
352#endif
353#ifdef ESPRESSO_DIPOLES
354 dipoles.on_particle_change();
355#endif
356 propagation->recalc_forces = true;
357 propagation->recalc_used_propagations = true;
358
359 /* the particle information is no longer valid */
361#ifdef ESPRESSO_SHARED_MEMORY_PARALLELISM
362 cell_structure->clear_local_properties();
363#endif
364}
365
367#ifdef ESPRESSO_ELECTROSTATICS
368 coulomb.on_particle_change();
369#endif
370}
371
373#ifdef ESPRESSO_VIRTUAL_SITES
374 if (propagation->recalc_used_propagations) {
375 update_used_propagations();
376 }
377#ifdef ESPRESSO_VIRTUAL_SITES_RELATIVE
378 if (propagation->used_propagations &
381 vs_relative_update_particles(*cell_structure, *box_geo);
382 }
383#endif
384#ifdef ESPRESSO_VIRTUAL_SITES_CENTER_OF_MASS
385 if (propagation->used_propagations &
387 vs_com_update_particles(*cell_structure, *box_geo);
388 }
389#endif
390 cell_structure->update_ghosts_and_resort_particle(get_global_ghost_flags());
391#endif
392
393#ifdef ESPRESSO_ELECTROSTATICS
394 if (has_icc_enabled()) {
395#ifdef ESPRESSO_SHARED_MEMORY_PARALLELISM
396 rebuild_aosoa();
397#endif
398 update_icc_particles();
399 }
400#endif
401
402 // Here we initialize volume conservation
403 // This function checks if the reference volumes have been set and if
404 // necessary calculates them
405 immersed_boundaries->init_volume_conservation(*cell_structure);
406}
407
409 /* Prepare particle structure: Communication step: number of ghosts and ghost
410 * information */
411 cell_structure->update_ghosts_and_resort_particle(get_global_ghost_flags());
412 update_dependent_particles();
413
414#ifdef ESPRESSO_ELECTROSTATICS
415 coulomb.on_observable_calc();
416#endif
417
418#ifdef ESPRESSO_DIPOLES
419 dipoles.on_observable_calc();
420#endif
421
423#ifdef ESPRESSO_SHARED_MEMORY_PARALLELISM
424 rebuild_aosoa();
425#endif
426}
427
428#ifdef ESPRESSO_SHARED_MEMORY_PARALLELISM
430#ifdef ESPRESSO_COLLISION_DETECTION
431 auto const collision_detection_cutoff = collision_detection->cutoff();
432#else
434#endif
435
437 cell_structure->get_verlet_skin(),
438 get_interaction_range(),
439 coulomb.cutoff(),
440 dipoles.cutoff(),
442
443 update_cabana_state(*cell_structure, verlet_criterion,
444 get_interaction_range(), propagation->integ_switch);
445}
446#endif // ESPRESSO_SHARED_MEMORY_PARALLELISM
447
448void System::on_lees_edwards_change() { lb.on_lees_edwards_change(); }
449
455
457 auto max_cut = inactive_cutoff;
458 max_cut = std::max(max_cut, get_min_global_cut());
459 max_cut = std::max(max_cut, coulomb.cutoff());
460 max_cut = std::max(max_cut, dipoles.cutoff());
461 if (::communicator.size > 1) {
462 // If there is just one node, the bonded cutoff can be omitted
463 // because bond partners are always on the local node.
464 max_cut = std::max(max_cut, bonded_ias->maximal_cutoff());
465 }
466 max_cut = std::max(max_cut, nonbonded_ias->maximal_cutoff());
467
468#ifdef ESPRESSO_COLLISION_DETECTION
469 max_cut = std::max(max_cut, collision_detection->cutoff());
470#endif
471 return max_cut;
472}
473
475 try {
476#ifdef ESPRESSO_ELECTROSTATICS
477 coulomb.sanity_checks();
478#endif
479#ifdef ESPRESSO_DIPOLES
480 dipoles.sanity_checks();
481#endif
482 } catch (std::runtime_error const &err) {
483 runtimeErrorMsg() << err.what();
484 return true;
485 }
486 return false;
487}
488
490 auto const max_cut = maximal_cutoff();
491 auto const verlet_skin = cell_structure->get_verlet_skin();
492 /* Consider skin only if there are actually interactions */
493 return (max_cut > 0.) ? max_cut + verlet_skin : inactive_cutoff;
494}
495
497 box_geo->set_length(box_l);
498 on_boxl_change();
499}
500
502 // sanity checks
503 integrator_sanity_checks();
504 long_range_interactions_sanity_checks();
505 lb.sanity_checks();
506 ek.sanity_checks();
507
508#ifdef ESPRESSO_NPT
509 if (propagation->integ_switch == INTEG_METHOD_NPT_ISO_AND ||
510 propagation->integ_switch == INTEG_METHOD_NPT_ISO_MTK) {
511 npt_ensemble_init(propagation->recalc_forces);
512 }
513#endif
514
515 /* Prepare the thermostat */
516 if (reinit_thermo) {
517 thermostat->recalc_prefactors(time_step);
518 reinit_thermo = false;
519 propagation->recalc_forces = true;
520 }
521
523#ifdef ESPRESSO_SHARED_MEMORY_PARALLELISM
524 cell_structure->clear_local_properties();
525#endif
526
527#ifdef ESPRESSO_ADDITIONAL_CHECKS
528 if (!Utils::Mpi::all_compare(::comm_cart, cell_structure->use_verlet_list)) {
529 runtimeErrorMsg() << "Nodes disagree about use of verlet lists.";
530 }
531#ifdef ESPRESSO_ELECTROSTATICS
532 {
533 auto const &actor = coulomb.impl->solver;
534 if (not Utils::Mpi::all_compare(::comm_cart, static_cast<bool>(actor)) or
535 (actor and not Utils::Mpi::all_compare(::comm_cart, (*actor).index())))
536 runtimeErrorMsg() << "Nodes disagree about Coulomb long-range method";
537 }
538#endif
539#ifdef ESPRESSO_DIPOLES
540 {
541 auto const &actor = dipoles.impl->solver;
542 if (not Utils::Mpi::all_compare(::comm_cart, static_cast<bool>(actor)) or
543 (actor and not Utils::Mpi::all_compare(::comm_cart, (*actor).index())))
544 runtimeErrorMsg() << "Nodes disagree about dipolar long-range method";
545 }
546#endif
547#endif /* ESPRESSO_ADDITIONAL_CHECKS */
548
549 on_observable_calc();
550}
551
552/**
553 * @brief Returns the ghost flags required for running pair
554 * kernels for the global state, e.g. the force calculation.
555 * @return Required data parts;
556 */
558 /* Position and Properties are always requested. */
560
561 if (lb.is_solver_set())
563
564 if (thermostat->thermo_switch & THERMO_DPD)
566
567 if (thermostat->thermo_switch & THERMO_BOND) {
570 }
571
572#ifdef ESPRESSO_COLLISION_DETECTION
573 if (not collision_detection->is_off()) {
575 }
576#endif
577
578 return data_parts;
579}
580
581#ifdef ESPRESSO_NPT
583 return (propagation->integ_switch == INTEG_METHOD_NPT_ISO_AND) or
584 (propagation->integ_switch == INTEG_METHOD_NPT_ISO_MTK);
585}
586#endif
587
589#ifdef ESPRESSO_NPT
590 if (has_npt_enabled()) {
591 return &npt_inst_pressure->p_vir;
592 }
593#endif
594 return nullptr;
595}
596
597#ifdef ESPRESSO_COLLISION_DETECTION
599 return not collision_detection->is_off();
600}
601#endif
602
603} // 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
Returns true if the particles are to be considered for short range interactions.
void vs_com_update_particles(CellStructure &cell_structure, BoxGeometry const &box_geo)
Definition com.cpp:130
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
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:120
See for the Stokesian dynamics method used here.
ESPRESSO_ATTR_ALWAYS_INLINE void update_cabana_state(CellStructure &cell_structure, auto const &verlet_criterion, double const pair_cutoff, auto const integ_switch)
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.