ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
integrate.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2010-2026 The ESPResSo project
3 * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010
4 * Max-Planck-Institute for Polymer Research, Theory Group
5 *
6 * This file is part of ESPResSo.
7 *
8 * ESPResSo is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * ESPResSo is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22/** \file
23 * Molecular dynamics integrator.
24 *
25 * For more information about the integrator
26 * see \ref integrate.hpp "integrate.hpp".
27 */
28
29#include "integrate.hpp"
37
38#include "BoxGeometry.hpp"
39#include "PropagationMode.hpp"
40#include "accumulators/AutoUpdateAccumulators.hpp"
45#include "cells.hpp"
46#include "collision_detection/CollisionDetection.hpp"
47#include "communication.hpp"
48#include "errorhandling.hpp"
50#include "lb/utils.hpp"
53#include "npt.hpp"
54#include "rattle.hpp"
55#include "rotation.hpp"
56#include "signalhandling.hpp"
58#include "system/System.hpp"
60#include "thermostat.hpp"
62#include "virtual_sites/com.hpp"
65
67
68#include <boost/mpi/collectives/all_reduce.hpp>
69
70#ifdef ESPRESSO_CALIPER
71#include <caliper/cali.h>
72#endif
73
74#ifdef ESPRESSO_VALGRIND
75#include <callgrind.h>
76#endif
77
78#include <algorithm>
79#include <cassert>
80#include <cmath>
81#include <csignal>
82#include <functional>
83#include <limits>
84#include <sstream>
85#include <stdexcept>
86#include <string>
87#include <utility>
88
89#ifdef ESPRESSO_WALBERLA
90#ifdef ESPRESSO_WALBERLA_STATIC_ASSERT
91#error "waLberla headers should not be visible to the ESPResSo core"
92#endif
93#endif
94
95namespace {
96volatile std::sig_atomic_t ctrl_C = 0;
97} // namespace
98
99namespace LeesEdwards {
100
101/**
102 * @brief Update the Lees-Edwards parameters of the box geometry
103 * for the current simulation time.
104 */
105void LeesEdwards::update_box_params(BoxGeometry &box_geo, double sim_time) {
106 if (box_geo.type() == BoxType::LEES_EDWARDS) {
107 assert(m_protocol != nullptr);
108 box_geo.lees_edwards_update(get_pos_offset(sim_time, *m_protocol),
109 get_shear_velocity(sim_time, *m_protocol));
110 }
111}
112
113void LeesEdwards::set_protocol(std::shared_ptr<ActiveProtocol> protocol) {
114 auto &system = get_system();
115 auto &cell_structure = *system.cell_structure;
116 auto &box_geo = *system.box_geo;
117 box_geo.set_type(BoxType::LEES_EDWARDS);
118 m_protocol = std::move(protocol);
119 update_box_params(box_geo, system.get_sim_time());
120 system.propagation->recalc_forces = true;
121 cell_structure.set_resort_particles(Cells::RESORT_LOCAL);
122}
123
125 auto &system = get_system();
126 auto &cell_structure = *system.cell_structure;
127 auto &box_geo = *system.box_geo;
128 m_protocol = nullptr;
129 box_geo.set_type(BoxType::CUBOID);
130 system.propagation->recalc_forces = true;
131 cell_structure.set_resort_particles(Cells::RESORT_LOCAL);
132}
133
134} // namespace LeesEdwards
135
137 switch (integ_switch) {
140 break;
141 case INTEG_METHOD_NVT:
143 // NOLINTNEXTLINE(bugprone-branch-clone)
144 if ((thermo_switch & THERMO_LB) and (thermo_switch & THERMO_LANGEVIN)) {
146#ifdef ESPRESSO_ROTATION
148#endif
149 } else if (thermo_switch & THERMO_LB) {
151#ifdef ESPRESSO_ROTATION
153#endif
154 } else if (thermo_switch & THERMO_LANGEVIN) {
156#ifdef ESPRESSO_ROTATION
158#endif
159 } else {
161#ifdef ESPRESSO_ROTATION
163#endif
164 }
165 break;
166 }
167#ifdef ESPRESSO_NPT
171 break;
172#endif
173 case INTEG_METHOD_BD:
175#ifdef ESPRESSO_ROTATION
177#endif
178 break;
179#ifdef ESPRESSO_STOKESIAN_DYNAMICS
180 case INTEG_METHOD_SD:
182 break;
183#endif // ESPRESSO_STOKESIAN_DYNAMICS
184 default:
185 throw std::runtime_error("Unknown value for integ_switch");
186 }
187}
188
190 int used_propagations = PropagationMode::NONE;
191 for (auto &p : cell_structure->local_particles()) {
192 used_propagations |= p.propagation();
193 }
194 if (used_propagations & PropagationMode::SYSTEM_DEFAULT) {
195 used_propagations |= propagation->default_propagation;
196 }
197 used_propagations = boost::mpi::all_reduce(::comm_cart, used_propagations,
198 std::bit_or<int>());
199 propagation->used_propagations = used_propagations;
200 propagation->recalc_used_propagations = false;
201}
202
203void System::System::integrator_sanity_checks() const {
204 auto const thermo_switch = thermostat->thermo_switch;
205 if (time_step <= 0.) {
206 runtimeErrorMsg() << "time_step not set";
207 }
208 if (propagation->integ_switch == INTEG_METHOD_STEEPEST_DESCENT) {
209 if (thermo_switch != THERMO_OFF) {
211 << "The steepest descent integrator is incompatible with thermostats";
212 }
213 }
214 if (propagation->integ_switch == INTEG_METHOD_NVT) {
215 if (thermo_switch & (THERMO_NPT_ISO | THERMO_BROWNIAN | THERMO_SD)) {
216 runtimeErrorMsg() << "The VV integrator is incompatible with the "
217 "currently active combination of thermostats";
218 }
219 }
220#ifdef ESPRESSO_NPT
221 if (propagation->used_propagations & PropagationMode::TRANS_LANGEVIN_NPT) {
222 if (thermo_switch != THERMO_NPT_ISO) {
223 runtimeErrorMsg() << "The NpT integrator requires the NpT thermostat";
224 }
225 if (box_geo->type() == BoxType::LEES_EDWARDS) {
226 runtimeErrorMsg() << "The NpT integrator cannot use Lees-Edwards";
227 }
228 try {
229 nptiso->coulomb_dipole_sanity_checks(*this);
230 } catch (std::runtime_error const &err) {
231 runtimeErrorMsg() << err.what();
232 }
233 }
234#endif
235 if (propagation->used_propagations & PropagationMode::TRANS_BROWNIAN) {
236 if (thermo_switch != THERMO_BROWNIAN) {
237 runtimeErrorMsg() << "The BD integrator requires the BD thermostat";
238 }
239 }
240 if (propagation->used_propagations & PropagationMode::TRANS_STOKESIAN) {
241#ifdef ESPRESSO_STOKESIAN_DYNAMICS
242 if (thermo_switch != THERMO_SD) {
243 runtimeErrorMsg() << "The SD integrator requires the SD thermostat";
244 }
245#endif
246 }
247 if (lb.is_solver_set() and (propagation->used_propagations &
250 if (thermostat->lb == nullptr) {
251 runtimeErrorMsg() << "The LB integrator requires the LB thermostat";
252 }
253 }
254 if (bonded_ias->get_n_thermalized_bonds() >= 1 and
255 (thermostat->thermalized_bond == nullptr or
256 (thermo_switch & THERMO_BOND) == 0)) {
258 << "Thermalized bonds require the thermalized_bond thermostat";
259 }
260
261#ifdef ESPRESSO_ROTATION
262 for (auto const &p : cell_structure->local_particles()) {
263 using namespace PropagationMode;
264 if (p.can_rotate() and not p.is_virtual() and
265 (p.propagation() & (SYSTEM_DEFAULT | ROT_EULER | ROT_LANGEVIN |
266 ROT_BROWNIAN | ROT_STOKESIAN)) == 0) {
268 << "Rotating particles must have a rotation propagation mode enabled";
269 break;
270 }
271 }
272#endif // ESPRESSO_ROTATION
273
274#ifdef ESPRESSO_VIRTUAL_SITES_CENTER_OF_MASS
275#ifdef ESPRESSO_EXTERNAL_FORCES
276 if (propagation->used_propagations &
278 for (auto const &p : cell_structure->local_particles()) {
279 using namespace PropagationMode;
280 if ((p.propagation() & TRANS_VS_CENTER_OF_MASS) and
281 p.has_fixed_coordinates()) {
282 runtimeErrorMsg() << "VS COM particles cannot be fixed in space";
283 break;
284 }
285 }
286 }
287#endif // ESPRESSO_EXTERNAL_FORCES
288#ifdef ESPRESSO_BOND_CONSTRAINT
289 if (bonded_ias->get_n_rigid_bonds()) {
290 using namespace PropagationMode;
291 for (auto const &p : cell_structure->local_particles()) {
292 if (p.propagation() & TRANS_VS_CENTER_OF_MASS) {
293 for (auto const bond : p.bonds()) {
294 if (std::holds_alternative<RigidBond>(
295 *bonded_ias->at(bond.bond_id()))) {
296 runtimeErrorMsg() << "VS COM particles cannot use rigid bonds";
297 break;
298 }
299 }
300 }
301 }
302 }
303#endif // ESPRESSO_BOND_CONSTRAINT
304#endif // ESPRESSO_VIRTUAL_SITES_CENTER_OF_MASS
305
306#ifdef ESPRESSO_THERMAL_STONER_WOHLFARTH
307 if ((thermo_switch & THERMO_LANGEVIN) == 0) {
308 for (auto const &p : cell_structure->local_particles()) {
309 if (p.stoner_wohlfarth_is_enabled()) {
310 runtimeErrorMsg() << "The thermal Stoner-Wohlfarth model requires the "
311 "Langevin thermostat";
312 break;
313 }
314 }
315 }
316#endif // ESPRESSO_THERMAL_STONER_WOHLFARTH
317}
318
319#ifdef ESPRESSO_WALBERLA
320void walberla_tau_sanity_checks(std::string const &method, double tau,
321 double time_step) {
322 if (time_step <= 0.) {
323 return;
324 }
325 // use float epsilon since tau may be a float
326 auto const eps = static_cast<double>(std::numeric_limits<float>::epsilon());
327 if ((tau - time_step) / (tau + time_step) < -eps)
328 throw std::invalid_argument(method + " tau (" + std::to_string(tau) +
329 ") must be >= MD time_step (" +
330 std::to_string(time_step) + ")");
331 auto const factor = tau / time_step;
332 if (std::fabs(std::round(factor) - factor) / factor > eps)
333 throw std::invalid_argument(method + " tau (" + std::to_string(tau) +
334 ") must be an integer multiple of the "
335 "MD time_step (" +
336 std::to_string(time_step) + "). Factor is " +
337 std::to_string(factor));
338}
339
340void walberla_agrid_sanity_checks(std::string const &method,
341 Utils::Vector3d const &geo_left,
342 Utils::Vector3d const &geo_right,
343 Utils::Vector3d const &lattice_left,
344 Utils::Vector3d const &lattice_right,
345 double agrid) {
346 // waLBerla and ESPResSo must agree on domain decomposition
347 auto const tol = agrid / 1E6;
348 if ((lattice_left - geo_left).norm2() > tol or
349 (lattice_right - geo_right).norm2() > tol) {
350 std::stringstream error_msg;
351 error_msg << "waLBerla and ESPResSo disagree about domain decomposition"
352 << "\nMPI rank " << ::this_node << ": "
353 << "left ESPResSo: [" << geo_left << "], "
354 << "left waLBerla: [" << lattice_left << "]"
355 << "\nMPI rank " << ::this_node << ": "
356 << "right ESPResSo: [" << geo_right << "], "
357 << "right waLBerla: [" << lattice_right << "]"
358 << "\nfor method: " << method;
359 throw std::runtime_error(error_msg.str());
360 }
361}
362#endif // ESPRESSO_WALBERLA
363
365#ifdef ESPRESSO_CALIPER
366 CALI_CXX_MARK_FUNCTION;
367#endif
368 auto &cell_structure = *system.cell_structure;
369 auto const offset = LeesEdwards::verlet_list_offset(
370 *system.box_geo, cell_structure.get_le_pos_offset_at_last_resort());
371 if (cell_structure.check_resort_required(offset)) {
372 cell_structure.set_resort_particles(Cells::RESORT_LOCAL);
373 }
374}
375
376/** @brief Calls the hook for propagation kernels before the force calculation
377 * @return whether or not to stop the integration loop early.
378 */
379static bool integrator_step_1(CellStructure &cell_structure,
380 Propagation const &propagation,
381 System::System &system, double time_step) {
382#ifdef ESPRESSO_CALIPER
383 CALI_CXX_MARK_FUNCTION;
384#endif
385 // steepest decent
387 return system.steepest_descent->propagate(cell_structure);
388
389 auto const &thermostat = *system.thermostat;
390 auto const kT = thermostat.kT;
391 cell_structure.for_each_local_particle([&](Particle &p) {
392#ifdef ESPRESSO_VIRTUAL_SITES
393 // virtual sites are updated later in the integration loop
394 if (p.is_virtual())
395 return;
396#endif
397 if (propagation.integ_switch == INTEG_METHOD_SYMPLECTIC_EULER) {
398 if (propagation.should_propagate_with(
400 symplectic_euler_propagator_1(p, time_step);
402 symplectic_euler_propagator_1(p, time_step);
403#ifdef ESPRESSO_ROTATION
405 symplectic_euler_rotator_1(p, time_step);
406#endif
408 symplectic_euler_propagator_1(p, time_step);
409#ifdef ESPRESSO_ROTATION
411 symplectic_euler_rotator_1(p, time_step);
412#endif
413 } else {
414 if (propagation.should_propagate_with(
416 velocity_verlet_propagator_1(p, time_step);
418 velocity_verlet_propagator_1(p, time_step);
419#ifdef ESPRESSO_ROTATION
421 velocity_verlet_rotator_1(p, time_step);
422#endif
424 velocity_verlet_propagator_1(p, time_step);
425#ifdef ESPRESSO_ROTATION
427 velocity_verlet_rotator_1(p, time_step);
428#endif
429 }
431 brownian_dynamics_propagator(*thermostat.brownian, p, time_step, kT);
432#ifdef ESPRESSO_ROTATION
434 brownian_dynamics_rotator(*thermostat.brownian, p, time_step, kT);
435#endif
436 });
437
438#ifdef ESPRESSO_NPT
441 auto pred = PropagationPredicateNPT(propagation.default_propagation);
442 if (propagation.integ_switch == INTEG_METHOD_NPT_ISO_AND) {
444 cell_structure.local_particles().filter(pred), *thermostat.npt_iso,
445 time_step, system);
446 } else if (propagation.integ_switch == INTEG_METHOD_NPT_ISO_MTK) {
448 cell_structure.local_particles().filter(pred), *thermostat.npt_iso,
449 time_step, system);
450 }
451 }
452#endif
453
454#ifdef ESPRESSO_STOKESIAN_DYNAMICS
457 auto pred = PropagationPredicateStokesian(propagation.default_propagation);
458 stokesian_dynamics_step_1(cell_structure.local_particles().filter(pred),
459 *system.stokesian_dynamics, *thermostat.stokesian,
460 time_step, kT);
461 }
462#endif // ESPRESSO_STOKESIAN_DYNAMICS
463
464 return false;
465}
466
467static void integrator_step_2(CellStructure &cell_structure,
468 Propagation const &propagation,
469 [[maybe_unused]] System::System &system,
470 double time_step) {
471#ifdef ESPRESSO_CALIPER
472 CALI_CXX_MARK_FUNCTION;
473#endif
475 return;
476
477 cell_structure.for_each_local_particle([&](Particle &p) {
478#ifdef ESPRESSO_VIRTUAL_SITES
479 // virtual sites are updated later in the integration loop
480 if (p.is_virtual())
481 return;
482#endif
483 if (propagation.integ_switch == INTEG_METHOD_SYMPLECTIC_EULER) {
484 if (propagation.should_propagate_with(
486 symplectic_euler_propagator_2(p, time_step);
488 symplectic_euler_propagator_2(p, time_step);
489#ifdef ESPRESSO_ROTATION
491 symplectic_euler_rotator_2(p, time_step);
492#endif
494 symplectic_euler_propagator_2(p, time_step);
495#ifdef ESPRESSO_ROTATION
497 symplectic_euler_rotator_2(p, time_step);
498#endif
499 } else {
500 if (propagation.should_propagate_with(
502 velocity_verlet_propagator_2(p, time_step);
504 velocity_verlet_propagator_2(p, time_step);
505#ifdef ESPRESSO_ROTATION
507 velocity_verlet_rotator_2(p, time_step);
508#endif
510 velocity_verlet_propagator_2(p, time_step);
511#ifdef ESPRESSO_ROTATION
513 velocity_verlet_rotator_2(p, time_step);
514#endif
515 }
516 });
517
518#ifdef ESPRESSO_NPT
521 auto pred = PropagationPredicateNPT(propagation.default_propagation);
522 if (propagation.integ_switch == INTEG_METHOD_NPT_ISO_AND) {
524 cell_structure.local_particles().filter(pred), time_step, system);
525 } else if (propagation.integ_switch == INTEG_METHOD_NPT_ISO_MTK) {
527 cell_structure.local_particles().filter(pred), time_step, system);
528 }
529 }
530#endif
531}
532
533int System::System::integrate(int n_steps, int reuse_forces) {
534#ifdef ESPRESSO_CALIPER
535 CALI_CXX_MARK_FUNCTION;
536#endif
537 auto &propagation = *this->propagation;
538#ifdef ESPRESSO_VIRTUAL_SITES_RELATIVE
539 auto const has_vs_rel = [&propagation]() {
540 return propagation.used_propagations &
544 };
545#endif
546#ifdef ESPRESSO_VIRTUAL_SITES_CENTER_OF_MASS
547 auto const has_vs_com = [&propagation]() {
548 return propagation.used_propagations &
550 };
551#endif
552#ifdef ESPRESSO_BOND_CONSTRAINT
553 auto const n_rigid_bonds = bonded_ias->get_n_rigid_bonds();
554#endif
555
556 // Prepare particle structure and run sanity checks of all active algorithms
557 propagation.update_default_propagation(thermostat->thermo_switch);
558 update_used_propagations();
559 on_integration_start();
560
561 // If any method vetoes (e.g. P3M not initialized), immediately bail out
563 return INTEG_ERROR_RUNTIME;
564
565 // Additional preparations for the first integration step
566 if (reuse_forces == INTEG_REUSE_FORCES_NEVER or
567 ((reuse_forces != INTEG_REUSE_FORCES_ALWAYS) and
568 propagation.recalc_forces)) {
569#ifdef ESPRESSO_CALIPER
570 CALI_MARK_BEGIN("Initial Force Calculation");
571#endif
572 thermostat->lb_coupling_deactivate();
573
574#ifdef ESPRESSO_VIRTUAL_SITES_RELATIVE
575 if (has_vs_rel()) {
576 vs_relative_update_particles(*cell_structure, *box_geo);
577 }
578#endif
579#ifdef ESPRESSO_VIRTUAL_SITES_CENTER_OF_MASS
580 if (has_vs_com()) {
581 vs_com_update_particles(*cell_structure, *box_geo);
582 }
583#endif
584
585 // Communication step: distribute ghost positions
586 cell_structure->update_ghosts_and_resort_particle(get_global_ghost_flags());
587
588 calculate_forces();
589
590 if (propagation.integ_switch != INTEG_METHOD_STEEPEST_DESCENT) {
591#ifdef ESPRESSO_ROTATION
592 convert_initial_torques(cell_structure->local_particles());
593#endif
594 }
595
596#ifdef ESPRESSO_CALIPER
597 CALI_MARK_END("Initial Force Calculation");
598#endif
599 }
600
601 thermostat->lb_coupling_activate();
602
604 return INTEG_ERROR_RUNTIME;
605
606 // Keep track of the number of Verlet updates (i.e. particle resorts)
607 int n_verlet_updates = 0;
608
609 // Keep track of whether an interrupt signal was caught (only in singleton
610 // mode, since signal handlers are unreliable with more than 1 MPI rank)
611 auto const singleton_mode = comm_cart.size() == 1;
612 auto caught_sigint = false;
613 auto caught_error = false;
614
615 auto lb_active = false;
616 auto ek_active = false;
617 if (propagation.integ_switch != INTEG_METHOD_STEEPEST_DESCENT) {
618 lb_active = lb.is_solver_set();
619 ek_active = ek.is_ready_for_propagation();
620 }
621 auto const calc_md_steps_per_tau = [this](double tau) {
622 return static_cast<int>(std::round(tau / time_step));
623 };
624
625#ifdef ESPRESSO_VALGRIND
626 CALLGRIND_START_INSTRUMENTATION;
627#endif
628 // Integration loop
629#ifdef ESPRESSO_CALIPER
630 CALI_CXX_MARK_LOOP_BEGIN(integration_loop, "Integration loop");
631#endif
632 int integrated_steps = 0;
633 for (int step = 0; step < n_steps; step++) {
634#ifdef ESPRESSO_CALIPER
635 CALI_CXX_MARK_LOOP_ITERATION(integration_loop, step);
636#endif
637
638#ifdef ESPRESSO_BOND_CONSTRAINT
639 if (n_rigid_bonds)
640 save_old_position(cell_structure->local_particles(),
641 cell_structure->ghost_particles());
642#endif
643
644 lees_edwards->update_box_params(*box_geo, sim_time);
645 bool early_exit =
646 integrator_step_1(*cell_structure, propagation, *this, time_step);
647 if (early_exit)
648 break;
649
650 sim_time += time_step;
651 if (box_geo->type() == BoxType::LEES_EDWARDS) {
652 auto const kernel = LeesEdwards::Push{*box_geo};
653 cell_structure->for_each_local_particle(
654 [&kernel](Particle &p) { kernel(p); });
655 }
656
657#ifdef ESPRESSO_NPT
658 if (not has_npt_enabled())
659#endif
660 {
662 }
663 // Propagate philox RNG counters
664 thermostat->philox_counter_increment();
665
666#ifdef ESPRESSO_BOND_CONSTRAINT
667 // Correct particle positions that participate in a rigid/constrained bond
668 if (n_rigid_bonds) {
669 correct_position_shake(*cell_structure, *box_geo, *bonded_ias);
670 }
671#endif
672
673#ifdef ESPRESSO_VIRTUAL_SITES_RELATIVE
674 if (has_vs_rel()) {
675#ifdef ESPRESSO_NPT
676 if (has_npt_enabled()) {
677 cell_structure->update_ghosts_and_resort_particle(
679 }
680#endif // ESPRESSO_NPT
681 vs_relative_update_particles(*cell_structure, *box_geo);
682 }
683#endif // ESPRESSO_VIRTUAL_SITES_RELATIVE
684#ifdef ESPRESSO_VIRTUAL_SITES_CENTER_OF_MASS
685 if (has_vs_com()) {
686#ifdef ESPRESSO_NPT
687 if (has_npt_enabled()) {
688 cell_structure->update_ghosts_and_resort_particle(
690 }
691#endif // ESPRESSO_NPT
692 vs_com_update_particles(*cell_structure, *box_geo);
693 }
694#endif // ESPRESSO_VIRTUAL_SITES_CENTER_OF_MASS
695
696 if (cell_structure->get_resort_particles() >= Cells::RESORT_LOCAL)
697 n_verlet_updates++;
698
699 // Communication step: distribute ghost positions
700 cell_structure->update_ghosts_and_resort_particle(get_global_ghost_flags());
701
702#ifdef ESPRESSO_THERMAL_STONER_WOHLFARTH
703 integrate_magnetodynamics();
704#endif
705
706 calculate_forces();
707
708#ifdef ESPRESSO_VIRTUAL_SITES_INERTIALESS_TRACERS
709 if (thermostat->lb and
710 (propagation.used_propagations & PropagationMode::TRANS_LB_TRACER)) {
711 lb_tracers_add_particle_force_to_fluid(*cell_structure, *box_geo,
712 *local_geo, lb);
713 }
714#endif
715 integrator_step_2(*cell_structure, propagation, *this, time_step);
716 if (propagation.integ_switch == INTEG_METHOD_BD) {
718 }
719 if (box_geo->type() == BoxType::LEES_EDWARDS) {
720 auto const kernel = LeesEdwards::UpdateOffset{*box_geo};
721 cell_structure->for_each_local_particle(
722 [&kernel](Particle &p) { kernel(p); });
723 }
724#ifdef ESPRESSO_BOND_CONSTRAINT
725 if (n_rigid_bonds) {
726 correct_velocity_shake(*cell_structure, *box_geo, *bonded_ias);
727 }
728#endif
729
730 // propagate one-step functionalities
731 if (propagation.integ_switch != INTEG_METHOD_STEEPEST_DESCENT) {
732 if (lb_active and ek_active) {
733 // assume that they are coupled, which is not necessarily true
734 auto const md_steps_per_lb_step = calc_md_steps_per_tau(lb.get_tau());
735 auto const md_steps_per_ek_step = calc_md_steps_per_tau(ek.get_tau());
736
737 if (md_steps_per_lb_step != md_steps_per_ek_step) {
739 << "LB and EK are active but with different time steps.";
740 }
741
742 assert(lb.is_gpu() == ek.is_gpu());
743 assert(propagation.lb_skipped_md_steps ==
744 propagation.ek_skipped_md_steps);
745
746 propagation.lb_skipped_md_steps += 1;
747 propagation.ek_skipped_md_steps += 1;
748 if (propagation.lb_skipped_md_steps >= md_steps_per_lb_step) {
749 propagation.lb_skipped_md_steps = 0;
750 propagation.ek_skipped_md_steps = 0;
751#ifdef ESPRESSO_CALIPER
752 CALI_MARK_BEGIN("lb_propagation");
753#endif
754 lb.propagate();
755 lb.ghost_communication_vel();
756#ifdef ESPRESSO_CALIPER
757 CALI_MARK_END("lb_propagation");
758#endif
759#ifdef ESPRESSO_CALIPER
760 CALI_MARK_BEGIN("ek_propagation");
761#endif
762 ek.propagate();
763#ifdef ESPRESSO_CALIPER
764 CALI_MARK_END("ek_propagation");
765#endif
766 }
767 } else if (lb_active) {
768 auto const md_steps_per_lb_step = calc_md_steps_per_tau(lb.get_tau());
769 propagation.lb_skipped_md_steps += 1;
770 if (propagation.lb_skipped_md_steps >= md_steps_per_lb_step) {
771 propagation.lb_skipped_md_steps = 0;
772#ifdef ESPRESSO_CALIPER
773 CALI_MARK_BEGIN("lb_propagation");
774#endif
775 lb.propagate();
776#ifdef ESPRESSO_CALIPER
777 CALI_MARK_END("lb_propagation");
778#endif
779 }
780 } else if (ek_active) {
781 auto const md_steps_per_ek_step = calc_md_steps_per_tau(ek.get_tau());
782 propagation.ek_skipped_md_steps += 1;
783 if (propagation.ek_skipped_md_steps >= md_steps_per_ek_step) {
784 propagation.ek_skipped_md_steps = 0;
785#ifdef ESPRESSO_CALIPER
786 CALI_MARK_BEGIN("ek_propagation");
787#endif
788 ek.propagate();
789#ifdef ESPRESSO_CALIPER
790 CALI_MARK_END("ek_propagation");
791#endif
792 }
793 }
794 if (lb_active and (propagation.used_propagations &
796 thermostat->lb->rng_increment();
797 }
798
799#ifdef ESPRESSO_VIRTUAL_SITES_INERTIALESS_TRACERS
800 if (thermostat->lb and
801 (propagation.used_propagations & PropagationMode::TRANS_LB_TRACER)) {
802#ifdef ESPRESSO_CALIPER
803 CALI_MARK_BEGIN("lb_tracers_propagation");
804#endif
805 if (lb_active) {
806 lb.ghost_communication_vel();
807 }
808 lb_tracers_propagate(*cell_structure, lb, time_step);
809#ifdef ESPRESSO_CALIPER
810 CALI_MARK_END("lb_tracers_propagation");
811#endif
812 }
813#endif
814
815#ifdef ESPRESSO_COLLISION_DETECTION
816 cell_structure->clear_new_bonds();
817 collision_detection->handle_collisions();
818 cell_structure->rebuild_bond_list();
819#endif
820 bond_breakage->process_queue(*this);
821 }
822
823 integrated_steps++;
824
826 caught_error = true;
827 break;
828 }
829
830 // Check if SIGINT has been caught.
831 if (singleton_mode and ctrl_C == 1) {
832 caught_sigint = true;
833 break;
834 }
835
836 } // for-loop over integration steps
837 if (lb_active) {
838 lb.ghost_communication();
839 }
840 lees_edwards->update_box_params(*box_geo, sim_time);
841#ifdef ESPRESSO_CALIPER
842 CALI_CXX_MARK_LOOP_END(integration_loop);
843#endif
844
845#ifdef ESPRESSO_VALGRIND
846 CALLGRIND_STOP_INSTRUMENTATION;
847#endif
848
849#ifdef ESPRESSO_VIRTUAL_SITES_RELATIVE
850 if (has_vs_rel()) {
851 vs_relative_update_particles(*cell_structure, *box_geo);
852 }
853#endif
854#ifdef ESPRESSO_VIRTUAL_SITES_CENTER_OF_MASS
855 if (has_vs_com()) {
856 vs_com_update_particles(*cell_structure, *box_geo);
857 }
858#endif
859
860 // Verlet list statistics
861 cell_structure->update_verlet_stats(n_steps, n_verlet_updates);
862
863#ifdef ESPRESSO_NPT
864 if (has_npt_enabled()) {
865 synchronize_npt_state();
866 }
867#endif
868 if (caught_sigint) {
869 ctrl_C = 0;
870 return INTEG_ERROR_SIGINT;
871 }
872 if (caught_error) {
873 return INTEG_ERROR_RUNTIME;
874 }
875 if (boost::mpi::all_reduce(::comm_cart, not cell_structure->use_verlet_list,
876 std::logical_or<>())) {
877 cell_structure->use_verlet_list = false;
878 }
879 return integrated_steps;
880}
881
882int System::System::integrate_with_signal_handler(int n_steps, int reuse_forces,
883 bool update_accumulators) {
884 assert(n_steps >= 0);
885
886 // Override the signal handler so that the integrator obeys Ctrl+C
887 SignalHandler sa(SIGINT, [](int) { ctrl_C = 1; });
888
889 /* if skin wasn't set, do an educated guess now */
890 if (not cell_structure->is_verlet_skin_set()) {
891 try {
892 cell_structure->set_verlet_skin_heuristic();
893 } catch (...) {
894 if (comm_cart.rank() == 0) {
895 throw;
896 }
897 return INTEG_ERROR_RUNTIME;
898 }
899 }
900
901 if (not update_accumulators or n_steps == 0) {
902 return integrate(n_steps, reuse_forces);
903 }
904
905 for (int i = 0; i < n_steps;) {
906 /* Integrate to either the next accumulator update, or the
907 * end, depending on what comes first. */
908 auto const steps =
909 std::min((n_steps - i), auto_update_accumulators->next_update());
910
911 auto const local_retval = integrate(steps, reuse_forces);
912
913 // make sure all ranks exit when one rank fails
914 std::remove_const_t<decltype(local_retval)> global_retval;
915 boost::mpi::all_reduce(comm_cart, local_retval, global_retval,
916 std::plus<int>());
917 if (global_retval < 0) {
918 return global_retval; // propagate error code
919 }
920
921 reuse_forces = INTEG_REUSE_FORCES_ALWAYS;
922
923 (*auto_update_accumulators)(comm_cart, steps);
924
925 i += steps;
926 }
927
928 return 0;
929}
930
932 sim_time = value;
933 propagation->recalc_forces = true;
934 lees_edwards->update_box_params(*box_geo, sim_time);
935}
@ LEES_EDWARDS
@ INTEG_METHOD_NPT_ISO_AND
@ INTEG_METHOD_SD
@ INTEG_METHOD_STEEPEST_DESCENT
@ INTEG_METHOD_NVT
@ INTEG_METHOD_SYMPLECTIC_EULER
@ INTEG_METHOD_BD
@ INTEG_METHOD_NPT_ISO_MTK
@ THERMO_SD
@ THERMO_BROWNIAN
@ THERMO_BOND
@ THERMO_LB
@ THERMO_LANGEVIN
@ THERMO_NPT_ISO
@ THERMO_OFF
Data structures for bonded interactions.
This file contains everything related to the global cell structure / cell system.
void lees_edwards_update(double pos_offset, double shear_velocity)
Update the Lees-Edwards parameters of the box geometry for the current simulation time.
BoxType type() const
Describes a cell structure / cell system.
void for_each_local_particle(Callable &&f, bool parallel=true) const
Run a kernel on all local particles.
ParticleRange local_particles() const
void update_box_params(BoxGeometry &box_geo, double sim_time)
Update the Lees-Edwards parameters of the box geometry for the current simulation time.
void set_protocol(std::shared_ptr< ActiveProtocol > protocol)
Set a new Lees-Edwards protocol.
void unset_protocol()
Delete the currently active Lees-Edwards protocol.
ParticleRangeFiltered< Predicate > filter(Predicate pred) const
int default_propagation
void update_default_propagation(int thermo_switch)
bool should_propagate_with(Particle const &p, int mode) const
int used_propagations
RAII guard for signal handling.
Main system class.
std::shared_ptr< StokesianDynamics > stokesian_dynamics
void update_used_propagations()
Update the global propagation bitmask.
void set_sim_time(double value)
Set sim_time.
int integrate_with_signal_handler(int n_steps, int reuse_forces, bool update_accumulators)
std::shared_ptr< SteepestDescent > steepest_descent
int integrate(int n_steps, int reuse_forces)
Integrate equations of motion.
std::shared_ptr< Thermostat::Thermostat > thermostat
std::shared_ptr< CellStructure > cell_structure
std::shared_ptr< BoxGeometry > box_geo
void vs_com_update_particles(CellStructure &cell_structure, BoxGeometry const &box_geo)
Definition com.cpp:131
boost::mpi::communicator comm_cart
The communicator.
int this_node
The number of this node.
int check_runtime_errors(boost::mpi::communicator const &comm)
Count runtime errors on all nodes.
This file contains the errorhandling code for severe errors, like a broken bond or illegal parameter ...
#define runtimeErrorMsg()
static bool integrator_step_1(CellStructure &cell_structure, Propagation const &propagation, System::System &system, double time_step)
Calls the hook for propagation kernels before the force calculation.
void walberla_tau_sanity_checks(std::string const &method, double tau, double time_step)
static void resort_particles_if_needed(System::System &system)
static void integrator_step_2(CellStructure &cell_structure, Propagation const &propagation, System::System &system, double time_step)
void walberla_agrid_sanity_checks(std::string const &method, Utils::Vector3d const &geo_left, Utils::Vector3d const &geo_right, Utils::Vector3d const &lattice_left, Utils::Vector3d const &lattice_right, double agrid)
Molecular dynamics integrator.
#define INTEG_ERROR_RUNTIME
Definition integrate.hpp:42
#define INTEG_ERROR_SIGINT
Definition integrate.hpp:43
#define INTEG_REUSE_FORCES_NEVER
recalculate forces unconditionally (mostly used for timing)
Definition integrate.hpp:49
#define INTEG_REUSE_FORCES_ALWAYS
do not recalculate forces (mostly when reading checkpoints with forces)
Definition integrate.hpp:53
void brownian_dynamics_rotator(BrownianThermostat const &brownian, Particle &p, double time_step, double kT)
void brownian_dynamics_propagator(BrownianThermostat const &brownian, Particle &p, double time_step, double kT)
void lb_tracers_propagate(CellStructure &cell_structure, LB::Solver const &lb, double time_step)
void lb_tracers_add_particle_force_to_fluid(CellStructure &cell_structure, BoxGeometry const &box_geo, LocalBox const &local_box, LB::Solver &lb)
@ DATA_PART_PROPERTIES
Particle::p.
Utils::Vector3d verlet_list_offset(BoxGeometry const &box, double pos_offset_at_last_resort)
double get_shear_velocity(double time, ActiveProtocol const &protocol)
Calculation of current velocity.
double get_pos_offset(double time, ActiveProtocol const &protocol)
Definition protocols.hpp:94
volatile std::sig_atomic_t ctrl_C
Definition integrate.cpp:96
Various procedures concerning interactions between particles.
Exports for the NpT code.
void correct_velocity_shake(CellStructure &cs, BoxGeometry const &box_geo, BondedInteractionsMap const &bonded_ias)
Correction of current velocities using RATTLE algorithm.
Definition rattle.cpp:229
void save_old_position(const ParticleRange &particles, const ParticleRange &ghost_particles)
copy current position
Definition rattle.cpp:59
void correct_position_shake(CellStructure &cs, BoxGeometry const &box_geo, BondedInteractionsMap const &bonded_ias)
Propagate velocity and position while using SHAKE algorithm for bond constraint.
Definition rattle.cpp:157
RATTLE algorithm ().
void vs_relative_update_particles(CellStructure &cell_structure, BoxGeometry const &box_geo)
Definition relative.cpp:121
void convert_initial_torques(const ParticleRange &particles)
Convert torques to the body-fixed frame before the integration loop.
Definition rotation.cpp:192
This file contains all subroutines required to process rotational motion.
See for the Stokesian dynamics method used here.
void stokesian_dynamics_step_1(ParticleRangeStokesian const &particles, StokesianDynamics const &integrator, StokesianThermostat const &stokesian, double time_step, double kT)
Struct holding all information for one particle.
Definition Particle.hpp:435
auto is_virtual() const
Definition Particle.hpp:588
void symplectic_euler_rotator_2(Particle &, double)
void symplectic_euler_rotator_1(Particle &p, double time_step)
void symplectic_euler_propagator_2(Particle &, double)
Final integration step of the Symplectic Euler integrator For symplectic Euler, there is no second st...
void symplectic_euler_propagator_1(Particle &p, double time_step)
Propagate the velocities and positions.
void velocity_verlet_rotator_1(Particle &p, double time_step)
void velocity_verlet_propagator_2(Particle &p, double time_step)
Final integration step of the Velocity Verlet integrator.
void velocity_verlet_propagator_1(Particle &p, double time_step)
Propagate the velocities and positions.
void velocity_verlet_rotator_2(Particle &p, double time_step)
void velocity_verlet_npt_MTK_step_1(ParticleRangeNPT const &particles, IsotropicNptThermostat const &npt_iso, double time_step, System::System &system)
Special propagator for velocity Verlet NpT with the Andersen method.
void velocity_verlet_npt_MTK_step_2(ParticleRangeNPT const &particles, double time_step, System::System &system)
Final integration step of the velocity Verlet NpT integrator with the MTK method.
void velocity_verlet_npt_Andersen_step_1(ParticleRangeNPT const &particles, IsotropicNptThermostat const &npt_iso, double time_step, System::System &system)
Special propagator for velocity Verlet NpT with the Andersen method.
void velocity_verlet_npt_Andersen_step_2(ParticleRangeNPT const &particles, double time_step, System::System &system)
Final integration step of the velocity Verlet NpT integrator with the Andersen method.