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 cell_structure->set_kokkos_handle(::kokkos_handle);
77#ifdef ESPRESSO_CUDA
78 gpu = std::make_shared<GpuParticleData>();
79#endif
80 propagation = std::make_shared<Propagation>();
81 bonded_ias = std::make_shared<BondedInteractionsMap>();
82 thermostat = std::make_shared<Thermostat::Thermostat>();
83 nonbonded_ias = std::make_shared<InteractionsNonBonded>();
84 comfixed = std::make_shared<ComFixed>();
85 galilei = std::make_shared<Galilei>();
86 oif_global = std::make_shared<OifGlobal>();
87 immersed_boundaries = std::make_shared<ImmersedBoundaries>();
88#ifdef ESPRESSO_COLLISION_DETECTION
89 collision_detection =
90 std::make_shared<CollisionDetection::CollisionDetection>();
91#endif
92 bond_breakage = std::make_shared<BondBreakage::BondBreakage>();
93 lees_edwards = std::make_shared<LeesEdwards::LeesEdwards>();
94 auto_update_accumulators =
95 std::make_shared<Accumulators::AutoUpdateAccumulators>();
96 constraints = std::make_shared<Constraints::Constraints>();
97 steepest_descent = std::make_shared<SteepestDescent>();
98#ifdef ESPRESSO_STOKESIAN_DYNAMICS
99 stokesian_dynamics = std::make_shared<StokesianDynamics>();
100#endif
101#ifdef ESPRESSO_NPT
102 nptiso = std::make_shared<NptIsoParameters>();
103 npt_inst_pressure = std::make_shared<InstantaneousPressure>();
104#endif
105 reinit_thermo = true;
106 time_step = -1.;
107 sim_time = 0.;
108 force_cap = 0.;
109 min_global_cut = inactive_cutoff;
110}
111
112void System::initialize() {
113 auto handle = shared_from_this();
114 cell_structure->bind_system(handle);
115 lees_edwards->bind_system(handle);
116 bonded_ias->bind_system(handle);
117 thermostat->bind_system(handle);
118 nonbonded_ias->bind_system(handle);
119 oif_global->bind_system(handle);
120 immersed_boundaries->bind_system(handle);
121#ifdef ESPRESSO_COLLISION_DETECTION
122 collision_detection->bind_system(handle);
123#endif
124 auto_update_accumulators->bind_system(handle);
125 constraints->bind_system(handle);
126#ifdef ESPRESSO_CUDA
127 gpu->bind_system(handle);
128 gpu->initialize();
129#endif
130 lb.bind_system(handle);
131 ek.bind_system(handle);
132}
133
134void reset_system() { instance.reset(); }
135
136void set_system(std::shared_ptr<System> new_instance) {
137 instance = new_instance;
138}
139
141
142void System::set_time_step(double value) {
143 if (value <= 0.)
144 throw std::domain_error("time_step must be > 0.");
145 if (lb.is_solver_set()) {
146 lb.veto_time_step(value);
147 }
148 if (ek.is_solver_set()) {
149 ek.veto_time_step(value);
150 }
151 time_step = value;
152 on_timestep_change();
153}
154
155void System::check_kT(double value) const {
156 if (lb.is_solver_set()) {
157 lb.veto_kT(value);
158 }
159 if (ek.is_solver_set()) {
160 ek.veto_kT(value);
161 }
162}
163
164void System::set_force_cap(double value) {
165 force_cap = value;
166 propagation->recalc_forces = true;
167}
168
169void System::set_min_global_cut(double value) {
170 min_global_cut = value;
171 on_verlet_skin_change();
172}
173
175 if (topology == CellStructureType::REGULAR) {
176 if (cell_structure->decomposition_type() == CellStructureType::REGULAR) {
177 // get fully connected info from existing regular decomposition
178 auto &old_regular_decomposition =
179 dynamic_cast<RegularDecomposition const &>(
180 std::as_const(*cell_structure).decomposition());
181 cell_structure->set_regular_decomposition(
182 get_interaction_range(),
183 old_regular_decomposition.fully_connected_boundary());
184 } else { // prev. decomposition is not a regular decomposition
185 cell_structure->set_regular_decomposition(get_interaction_range(), {});
186 }
187 } else if (topology == CellStructureType::NSQUARE) {
188 cell_structure->set_atom_decomposition();
189 } else {
190 assert(topology == CellStructureType::HYBRID);
191 /* Get current HybridDecomposition to extract n_square_types */
192 auto &old_hybrid_decomposition = dynamic_cast<HybridDecomposition const &>(
193 std::as_const(*cell_structure).decomposition());
194 cell_structure->set_hybrid_decomposition(
195 old_hybrid_decomposition.get_cutoff_regular(),
196 old_hybrid_decomposition.get_n_square_types());
197 }
198}
199
201 set_cell_structure_topology(cell_structure->decomposition_type());
202}
203
204void System::on_boxl_change(bool skip_method_adaption) {
205 update_local_geo();
206 rebuild_cell_structure();
207
208 /* Now give methods a chance to react to the change in box length */
209 if (not skip_method_adaption) {
210 lb.on_boxl_change();
211 ek.on_boxl_change();
212#ifdef ESPRESSO_ELECTROSTATICS
213 coulomb.on_boxl_change();
214#endif
215#ifdef ESPRESSO_DIPOLES
216 dipoles.on_boxl_change();
217#endif
218 }
219 constraints->on_boxl_change();
220}
221
222void System::veto_boxl_change(bool skip_particle_checks) const {
223 if (not skip_particle_checks) {
224 auto const n_part = boost::mpi::all_reduce(
225 ::comm_cart, cell_structure->local_particles().size(), std::plus<>());
226 if (n_part > 0ul) {
227 throw std::runtime_error(
228 "Cannot reset the box length when particles are present");
229 }
230 }
231 constraints->veto_boxl_change();
232 lb.veto_boxl_change();
233 ek.veto_boxl_change();
234}
235
237 update_local_geo();
238 lb.on_node_grid_change();
239 ek.on_node_grid_change();
240#ifdef ESPRESSO_ELECTROSTATICS
241 coulomb.on_node_grid_change();
242#endif
243#ifdef ESPRESSO_DIPOLES
244 dipoles.on_node_grid_change();
245#endif
246 rebuild_cell_structure();
247}
248
250#ifdef ESPRESSO_ELECTROSTATICS
251 coulomb.on_periodicity_change();
252#endif
253
254#ifdef ESPRESSO_DIPOLES
255 dipoles.on_periodicity_change();
256#endif
257
258#ifdef ESPRESSO_STOKESIAN_DYNAMICS
259 if (propagation->integ_switch == INTEG_METHOD_SD) {
260 if (box_geo->periodic(0u) or box_geo->periodic(1u) or box_geo->periodic(2u))
261 runtimeErrorMsg() << "Stokesian Dynamics requires periodicity "
262 << "(False, False, False)\n";
263 }
264#endif
265 on_verlet_skin_change();
266}
267
270 lb.on_cell_structure_change();
271 ek.on_cell_structure_change();
272#ifdef ESPRESSO_ELECTROSTATICS
273 coulomb.on_cell_structure_change();
274#endif
275#ifdef ESPRESSO_DIPOLES
276 dipoles.on_cell_structure_change();
277#endif
278}
279
280void System::on_thermostat_param_change() { reinit_thermo = true; }
281
283 rebuild_cell_structure();
284#ifdef ESPRESSO_ELECTROSTATICS
285 coulomb.on_coulomb_change();
286#endif
287#ifdef ESPRESSO_DIPOLES
288 dipoles.on_dipoles_change();
289#endif
290 on_short_range_ia_change();
291}
292
294 lb.on_temperature_change();
295 ek.on_temperature_change();
296}
297
299 lb.on_timestep_change();
300 ek.on_timestep_change();
301 on_thermostat_param_change();
302}
303
305 rebuild_cell_structure();
306 propagation->recalc_forces = true;
307}
308
310 nonbonded_ias->recalc_maximal_cutoffs();
311 rebuild_cell_structure();
312 on_thermostat_param_change();
313 propagation->recalc_forces = true;
314}
315
317#ifdef ESPRESSO_ELECTROSTATICS
318 coulomb.on_coulomb_change();
319#endif
320 on_short_range_ia_change();
321}
322
324#ifdef ESPRESSO_DIPOLES
325 dipoles.on_dipoles_change();
326#endif
327 on_short_range_ia_change();
328}
329
330void System::on_constraint_change() { propagation->recalc_forces = true; }
331
333 propagation->recalc_forces = true;
334}
335
337 cell_structure->update_ghosts_and_resort_particle(get_global_ghost_flags());
338 propagation->recalc_forces = true;
339 propagation->recalc_used_propagations = true;
340}
341
343 if (cell_structure->decomposition_type() == CellStructureType::HYBRID) {
344 cell_structure->set_resort_particles(Cells::RESORT_GLOBAL);
345 } else {
346 cell_structure->set_resort_particles(Cells::RESORT_LOCAL);
347 }
348#ifdef ESPRESSO_ELECTROSTATICS
349 coulomb.on_particle_change();
350#endif
351#ifdef ESPRESSO_DIPOLES
352 dipoles.on_particle_change();
353#endif
354 propagation->recalc_forces = true;
355 propagation->recalc_used_propagations = true;
356
357 /* the particle information is no longer valid */
359 cell_structure->clear_local_properties();
360}
361
363#ifdef ESPRESSO_ELECTROSTATICS
364 coulomb.on_particle_change();
365#endif
366}
367
369#ifdef ESPRESSO_VIRTUAL_SITES
370 if (propagation->recalc_used_propagations) {
371 update_used_propagations();
372 }
373#ifdef ESPRESSO_VIRTUAL_SITES_RELATIVE
374 if (propagation->used_propagations &
377 vs_relative_update_particles(*cell_structure, *box_geo);
378 }
379#endif
380#ifdef ESPRESSO_VIRTUAL_SITES_CENTER_OF_MASS
381 if (propagation->used_propagations &
383 vs_com_update_particles(*cell_structure, *box_geo);
384 }
385#endif
386 cell_structure->update_ghosts_and_resort_particle(get_global_ghost_flags());
387#endif
388
389#ifdef ESPRESSO_ELECTROSTATICS
390 if (has_icc_enabled()) {
391 rebuild_aosoa();
392 update_icc_particles();
393 }
394#endif
395
396 // Here we initialize volume conservation
397 // This function checks if the reference volumes have been set and if
398 // necessary calculates them
399 immersed_boundaries->init_volume_conservation(*cell_structure);
400}
401
403 /* Prepare particle structure: Communication step: number of ghosts and ghost
404 * information */
405 cell_structure->update_ghosts_and_resort_particle(get_global_ghost_flags());
406 update_dependent_particles();
407
408#ifdef ESPRESSO_ELECTROSTATICS
409 coulomb.on_observable_calc();
410#endif
411
412#ifdef ESPRESSO_DIPOLES
413 dipoles.on_observable_calc();
414#endif
415
417 rebuild_aosoa();
418}
419
421#ifdef ESPRESSO_COLLISION_DETECTION
422 auto const collision_detection_cutoff = collision_detection->cutoff();
423#else
424 auto const collision_detection_cutoff = inactive_cutoff;
425#endif
426
427 VerletCriterion<> const verlet_criterion{*this,
428 cell_structure->get_verlet_skin(),
429 get_interaction_range(),
430 coulomb.cutoff(),
431 dipoles.cutoff(),
432 collision_detection_cutoff};
433
434 update_cabana_state(*cell_structure, verlet_criterion,
435 get_interaction_range(), propagation->integ_switch);
436}
437
438void System::on_lees_edwards_change() { lb.on_lees_edwards_change(); }
439
445
447 auto max_cut = inactive_cutoff;
448 max_cut = std::max(max_cut, get_min_global_cut());
449 max_cut = std::max(max_cut, coulomb.cutoff());
450 max_cut = std::max(max_cut, dipoles.cutoff());
451 if (::communicator.size > 1) {
452 // If there is just one node, the bonded cutoff can be omitted
453 // because bond partners are always on the local node.
454 max_cut = std::max(max_cut, bonded_ias->maximal_cutoff());
455 }
456 max_cut = std::max(max_cut, nonbonded_ias->maximal_cutoff());
457
458#ifdef ESPRESSO_COLLISION_DETECTION
459 max_cut = std::max(max_cut, collision_detection->cutoff());
460#endif
461 return max_cut;
462}
463
465 try {
466#ifdef ESPRESSO_ELECTROSTATICS
467 coulomb.sanity_checks();
468#endif
469#ifdef ESPRESSO_DIPOLES
470 dipoles.sanity_checks();
471#endif
472 } catch (std::runtime_error const &err) {
473 runtimeErrorMsg() << err.what();
474 return true;
475 }
476 return false;
477}
478
480 auto const max_cut = maximal_cutoff();
481 auto const verlet_skin = cell_structure->get_verlet_skin();
482 /* Consider skin only if there are actually interactions */
483 return (max_cut > 0.) ? max_cut + verlet_skin : inactive_cutoff;
484}
485
487 box_geo->set_length(box_l);
488 on_boxl_change();
489}
490
492 // sanity checks
493 integrator_sanity_checks();
494 long_range_interactions_sanity_checks();
495 lb.sanity_checks();
496 ek.sanity_checks();
497
498#ifdef ESPRESSO_NPT
499 if (propagation->integ_switch == INTEG_METHOD_NPT_ISO_AND ||
500 propagation->integ_switch == INTEG_METHOD_NPT_ISO_MTK) {
501 npt_ensemble_init(propagation->recalc_forces);
502 }
503#endif
504
505 /* Prepare the thermostat */
506 if (reinit_thermo) {
507 thermostat->recalc_prefactors(time_step);
508 reinit_thermo = false;
509 propagation->recalc_forces = true;
510 }
511
513 cell_structure->clear_local_properties();
514
515#ifdef ESPRESSO_ADDITIONAL_CHECKS
516 if (!Utils::Mpi::all_compare(::comm_cart, cell_structure->use_verlet_list)) {
517 runtimeErrorMsg() << "Nodes disagree about use of verlet lists.";
518 }
519#ifdef ESPRESSO_ELECTROSTATICS
520 {
521 auto const &actor = coulomb.impl->solver;
522 if (not Utils::Mpi::all_compare(::comm_cart, static_cast<bool>(actor)) or
523 (actor and not Utils::Mpi::all_compare(::comm_cart, (*actor).index())))
524 runtimeErrorMsg() << "Nodes disagree about Coulomb long-range method";
525 }
526#endif
527#ifdef ESPRESSO_DIPOLES
528 {
529 auto const &actor = dipoles.impl->solver;
530 if (not Utils::Mpi::all_compare(::comm_cart, static_cast<bool>(actor)) or
531 (actor and not Utils::Mpi::all_compare(::comm_cart, (*actor).index())))
532 runtimeErrorMsg() << "Nodes disagree about dipolar long-range method";
533 }
534#endif
535#endif /* ESPRESSO_ADDITIONAL_CHECKS */
536
537 on_observable_calc();
538}
539
540/**
541 * @brief Returns the ghost flags required for running pair
542 * kernels for the global state, e.g. the force calculation.
543 * @return Required data parts;
544 */
546 /* Position and Properties are always requested. */
548
549 if (lb.is_solver_set())
550 data_parts |= Cells::DATA_PART_MOMENTUM;
551
552 if (thermostat->thermo_switch & THERMO_DPD)
553 data_parts |= Cells::DATA_PART_MOMENTUM;
554
555 if (thermostat->thermo_switch & THERMO_BOND) {
556 data_parts |= Cells::DATA_PART_MOMENTUM;
557 data_parts |= Cells::DATA_PART_BONDS;
558 }
559
560#ifdef ESPRESSO_COLLISION_DETECTION
561 if (not collision_detection->is_off()) {
562 data_parts |= Cells::DATA_PART_BONDS;
563 }
564#endif
565
566 return data_parts;
567}
568
569#ifdef ESPRESSO_NPT
571 return (propagation->integ_switch == INTEG_METHOD_NPT_ISO_AND) or
572 (propagation->integ_switch == INTEG_METHOD_NPT_ISO_MTK);
573}
574#endif
575
577#ifdef ESPRESSO_NPT
578 if (has_npt_enabled()) {
579 return &npt_inst_pressure->p_vir;
580 }
581#endif
582 return nullptr;
583}
584
585#ifdef ESPRESSO_COLLISION_DETECTION
587 return not collision_detection->is_off();
588}
589#endif
590
591} // 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
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.