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