diff --git a/doc/sphinx/CMakeLists.txt b/doc/sphinx/CMakeLists.txt
index 5d0cc753e52..7dc5dc3a0bb 100644
--- a/doc/sphinx/CMakeLists.txt
+++ b/doc/sphinx/CMakeLists.txt
@@ -87,6 +87,8 @@ if(SPHINX_FOUND)
${Python_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/samples.py"
COMMAND ${SPHINX_API_DOC_EXE} -f -o ${CMAKE_CURRENT_BINARY_DIR}
${SPHINX_PYTHON_DIR} ${EXCLUDE}
+ COMMAND ${Python_EXECUTABLE}
+ "${CMAKE_CURRENT_SOURCE_DIR}/autodoc_overrides.py"
COMMAND
${SPHINX_EXECUTABLE} -q -W -b html -c "${CMAKE_CURRENT_BINARY_DIR}" -d
"${SPHINX_CACHE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}" "${SPHINX_HTML_DIR}"
diff --git a/doc/sphinx/advanced_methods.rst b/doc/sphinx/advanced_methods.rst
index 39a0935b25c..b18b813281d 100644
--- a/doc/sphinx/advanced_methods.rst
+++ b/doc/sphinx/advanced_methods.rst
@@ -68,7 +68,6 @@ Several modes are available for different types of binding.
the point of contact or you can use :class:`espressomd.interactions.Virtual` which acts as a marker, only.
The method is setup as follows::
- system.virtual_sites = espressomd.virtual_sites.VirtualSitesRelative()
system.collision_detection.set_params(
mode="bind_at_point_of_collision",
distance=0.1,
@@ -116,7 +115,6 @@ Several modes are available for different types of binding.
The method is used as follows::
- system.virtual_sites = espressomd.virtual_sites.VirtualSitesRelative()
system.collision_detection.set_params(
mode="glue_to_surface",
distance=0.1,
diff --git a/src/script_interface/virtual_sites/CMakeLists.txt b/doc/sphinx/autodoc_overrides.py
similarity index 56%
rename from src/script_interface/virtual_sites/CMakeLists.txt
rename to doc/sphinx/autodoc_overrides.py
index c98d7a553e5..9f9885bcbeb 100644
--- a/src/script_interface/virtual_sites/CMakeLists.txt
+++ b/doc/sphinx/autodoc_overrides.py
@@ -1,5 +1,5 @@
#
-# Copyright (C) 2020-2022 The ESPResSo project
+# Copyright (C) 2023 The ESPResSo project
#
# This file is part of ESPResSo.
#
@@ -17,5 +17,21 @@
# along with this program. If not, see .
#
-target_sources(espresso_script_interface
- PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp)
+
+def update_file(filepath, function):
+ with open(filepath, "r+") as f:
+ content = function(f.read())
+ f.seek(0)
+ f.truncate()
+ f.write(content)
+
+
+def update_espressomd(content):
+ token = ".. automodule:: espressomd.propagation"
+ assert token in content
+ assert content.count(token) == 1
+ content = content.replace(token, token + "\n :member-order: bysource", 1)
+ return content
+
+
+update_file("espressomd.rst", update_espressomd)
diff --git a/doc/sphinx/particles.rst b/doc/sphinx/particles.rst
index 36d7a9fa590..0106aa354c6 100644
--- a/doc/sphinx/particles.rst
+++ b/doc/sphinx/particles.rst
@@ -251,26 +251,8 @@ according to their respective particle type. Before the next integration
step, the forces accumulated on a virtual site are distributed back to
those particles, from which the virtual site was derived.
-
-There are different schemes for virtual sites, described in the
-following sections. To switch the active scheme, the system
-:attr:`~espressomd.system.System.virtual_sites` property can be used::
-
- import espressomd
- import espressomd.virtual_sites
-
- system = espressomd.System(box_l=[1, 1, 1])
- system.virtual_sites = espressomd.virtual_sites.VirtualSitesRelative(have_quaternion=False)
- # or
- system.virtual_sites = espressomd.virtual_sites.VirtualSitesOff()
-
-By default, :class:`espressomd.virtual_sites.VirtualSitesOff` is selected.
-This means that virtual particles are not touched during integration.
-The ``have_quaternion`` parameter determines whether the quaternion of
-the virtual particle is updated (useful in combination with the
-:attr:`~espressomd.particle_data.ParticleHandle.vs_quat` property of the
-virtual particle which defines the orientation of the virtual particle
-in the body fixed frame of the related real particle).
+There are different schemes for virtual sites, described in the following sections.
+The scheme acting on a virtual site is dependent on its propagation mode.
.. _Rigid arrangements of particles:
@@ -298,19 +280,17 @@ the non-virtual particle rotates, the virtual sites rotates on an orbit
around the non-virtual particles center.
To use this implementation of virtual sites, activate the feature
-``VIRTUAL_SITES_RELATIVE``. Furthermore, an instance of
-:class:`~espressomd.virtual_sites.VirtualSitesRelative` has to be set as the
-active virtual sites scheme (see above). To set up a virtual site:
+``VIRTUAL_SITES_RELATIVE``. Furthermore, particles have to be set up with the
+propagation modes :attr:`~espressomd.propagation.Propagation.TRANS_VS_RELATIVE`
+and :attr:`~espressomd.propagation.Propagation.ROT_VS_RELATIVE`.
#. Place the particle to which the virtual site should be related.
It needs to be in the center of mass of the rigid arrangement of
particles you create::
import espressomd
- import espressomd.virtual_sites
system = espressomd.System(box_l=[10., 10., 10.])
- system.virtual_sites = espressomd.virtual_sites.VirtualSitesRelative()
p1 = system.part.add(pos=[1., 2., 3.])
#. Place a particle at the desired relative position, make it virtual
@@ -320,8 +300,8 @@ active virtual sites scheme (see above). To set up a virtual site:
p2 = system.part.add(pos=p1.pos + rel_offset)
p2.vs_auto_relate_to(p1)
- This will set the :attr:`~espressomd.particle_data.ParticleHandle.virtual`
- attribute on particle ``p2`` to ``True``.
+ The :meth:`~espressomd.particle_data.ParticleHandle.is_virtual`
+ method on particle ``p2`` will now return ``True``.
#. Repeat the previous step with more virtual sites, if desired.
@@ -371,22 +351,16 @@ Please note:
Inertialess lattice-Boltzmann tracers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-:class:`espressomd.virtual_sites.VirtualSitesInertialessTracers`
-
-When this implementation is selected, the virtual sites follow the motion of a
-lattice-Boltzmann fluid (both, CPU and GPU). This is achieved by integrating
+Using the propagation mode :attr:`~espressomd.propagation.Propagation.TRANS_LB_TRACER`,
+the virtual sites follow the motion of a LB fluid. This is achieved by integrating
their position using the fluid velocity at the virtual sites' position.
Forces acting on the virtual sites are directly transferred as force density
onto the lattice-Boltzmann fluid, making the coupling free of inertia.
+Please note that the velocity attribute of the virtual particles
+does not carry valid information for this virtual sites scheme.
The feature stems from the implementation of the
:ref:`Immersed Boundary Method for soft elastic objects`, but can be used independently.
-For correct results, the LB thermostat has to be deactivated for virtual sites::
-
- system.thermostat.set_lb(kT=0, act_on_virtual=False)
-
-Please note that the velocity attribute of the virtual particles does not carry valid information for this virtual sites scheme.
-
.. _Interacting with groups of particles:
Interacting with groups of particles
@@ -667,24 +641,22 @@ the total system is net force-free at all times. The resulting flow-field can th
monopolar contributions in the far field and the slowest-decaying flow-field mode is a dipole.
In |es|, the propulsion mechanism can be mimicked by applying a force to the fluid that is equal in
magnitude but opposite in sign to the forward force on the swimming particle.
-For this, particles can be marked as force appliers as shown in the following example:
-
-::
+For this, particles can be marked as force appliers as shown in the following example::
- dipole_partcl = system.part.add(pos=[1, 0, 0],
- virtual = True,
- swimming={'f_swim': swimmer.swimming['f_swim'],
- 'is_engine_force_on_fluid': True})
+ dipole = system.part.add(pos=[1, 0, 0],
+ swimming={"f_swim": swimmer.swimming["f_swim"],
+ "is_engine_force_on_fluid": True})
+ dipole.vs_auto_relate_to(swimmer)
+ dipole.vs_quat = calc_quaternions_from_angles(np.pi, 0.)
-This makes the particle not experience any friction or stochastic forces, but apply ``f_swim``
-along its internal orientation (``director``).
-To make the force applier follow the swimmer and also update its orientation accordingly,
-the :class:`espressomd.virtual_sites.VirtualSitesRelative` mechanism is used.
+This makes the dipole particle not experience any friction or stochastic forces,
+but apply ``f_swim`` along its internal orientation (``director``)
+and follow the swimmer while updating its orientation accordingly.
|es| provides a helper function :func:`~espressomd.swimmer_helpers.add_dipole_particle` to set
up the virtual particle with the correct distance, relative position and orientation::
import espressomd.swimmer_helpers.add_dipole_particle as add_dip
- dipole_partcl = add_dip(system, swimmer, 2., 0, mode="pusher")
+ dipole = add_dip(system, swimmer, 2., 0, mode="pusher")
It creates pushers with the propulsion behind the swimmer and pullers with the
propulsion in front of the swimmer.
diff --git a/doc/tutorials/active_matter/active_matter.ipynb b/doc/tutorials/active_matter/active_matter.ipynb
index 19c6874dffb..e8842d37e27 100644
--- a/doc/tutorials/active_matter/active_matter.ipynb
+++ b/doc/tutorials/active_matter/active_matter.ipynb
@@ -874,11 +874,7 @@
"outputs": [],
"source": [
"import espressomd.lb\n",
- "import espressomd.virtual_sites\n",
- "import espressomd.swimmer_helpers\n",
- "system.virtual_sites = espressomd.virtual_sites.VirtualSitesRelative(\n",
- " have_quaternion=True\n",
- ")"
+ "import espressomd.swimmer_helpers"
]
},
{
diff --git a/doc/tutorials/raspberry_electrophoresis/raspberry_electrophoresis.ipynb b/doc/tutorials/raspberry_electrophoresis/raspberry_electrophoresis.ipynb
index 01dda0921ee..1d914418550 100644
--- a/doc/tutorials/raspberry_electrophoresis/raspberry_electrophoresis.ipynb
+++ b/doc/tutorials/raspberry_electrophoresis/raspberry_electrophoresis.ipynb
@@ -71,7 +71,6 @@
"import espressomd.interactions\n",
"import espressomd.electrostatics\n",
"import espressomd.lb\n",
- "import espressomd.virtual_sites\n",
"\n",
"import sys\n",
"import tqdm\n",
@@ -335,43 +334,15 @@
"id": "478a2301",
"metadata": {},
"source": [
- "Now that the beads are arranged in the shape of a raspberry, the surface beads are made virtual particles\n",
+ "Now that the beads are arranged in the shape of a raspberry, the surface beads need to be set up as virtual particles\n",
"using the `VirtualSitesRelative` scheme. This converts the raspberry to a rigid body\n",
- "in which the surface particles follow the translation and rotation of the central particle.\n",
+ "in which the surface particles follow the translation and rotation of the central particle,\n",
+ "and transfer the interpolated LB momentum back to the central particle.\n",
"Newton's equations of motion are only integrated for the central particle.\n",
"It is given an appropriate mass and moment of inertia tensor (note that the inertia tensor\n",
"is given in the frame in which it is diagonal.)"
]
},
- {
- "cell_type": "markdown",
- "id": "cb7fc3a7",
- "metadata": {},
- "source": [
- "#### Exercise\n",
- "Activate the `VirtualSitesRelative` implementation in **ESPResSo**. See the [documentation](https://espressomd.github.io/doc/particles.html#virtual-sites) for help."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "fae9dbe4",
- "metadata": {},
- "outputs": [],
- "source": [
- "# SOLUTION CELL\n",
- "# Select the desired implementation for virtual sites\n",
- "system.virtual_sites = espressomd.virtual_sites.VirtualSitesRelative()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "9f256d8e",
- "metadata": {},
- "outputs": [],
- "source": []
- },
{
"cell_type": "code",
"execution_count": null,
@@ -435,7 +406,7 @@
"# Convert the surface particles to virtual sites related to the central particle\n",
"# The id of the central particles is 0, the ids of the surface particles start at 1.\n",
"for p in surface_parts:\n",
- " p.vs_auto_relate_to(central_part)"
+ " p.vs_auto_relate_to(central_part, couple_to_lb=True)"
]
},
{
diff --git a/samples/drude_bmimpf6.py b/samples/drude_bmimpf6.py
index 20bb311cb7e..8579c480573 100644
--- a/samples/drude_bmimpf6.py
+++ b/samples/drude_bmimpf6.py
@@ -34,7 +34,6 @@
import espressomd.electrostatics
import espressomd.interactions
import espressomd.drude_helpers
-import espressomd.virtual_sites
import espressomd.visualization
required_features = ["LENNARD_JONES", "P3M", "MASS", "ROTATION",
@@ -80,7 +79,6 @@
print("\n-->Ion pairs:", n_ionpairs, "Box size:", box_l)
system = espressomd.System(box_l=[box_l, box_l, box_l])
-system.virtual_sites = espressomd.virtual_sites.VirtualSitesRelative()
if args.visu:
d_scale = 0.988 * 0.5
diff --git a/samples/immersed_boundary/addSoft.py b/samples/immersed_boundary/addSoft.py
index e851bed8d45..c0baed64073 100644
--- a/samples/immersed_boundary/addSoft.py
+++ b/samples/immersed_boundary/addSoft.py
@@ -31,7 +31,7 @@ def AddSoft(system, comX, comY, comZ, k1, k2):
X = float(line[0]) + comX
Y = float(line[1]) + comY
Z = float(line[2]) + comZ
- system.part.add(id=i, pos=[X, Y, Z], virtual=True)
+ system.part.add(id=i, pos=[X, Y, Z])
# triangles
import espressomd.interactions
diff --git a/samples/immersed_boundary/sampleImmersedBoundary.py b/samples/immersed_boundary/sampleImmersedBoundary.py
index 09fa59b2ef5..b8ac18771a1 100644
--- a/samples/immersed_boundary/sampleImmersedBoundary.py
+++ b/samples/immersed_boundary/sampleImmersedBoundary.py
@@ -26,7 +26,6 @@
import espressomd
import espressomd.lb
import espressomd.shapes
-import espressomd.virtual_sites
required_features = ["VIRTUAL_SITES_INERTIALESS_TRACERS", "WALBERLA"]
espressomd.assert_features(required_features)
@@ -48,7 +47,6 @@
system = espressomd.System(box_l=(20, 20, boxZ))
system.time_step = 1 / 6.
system.cell_system.skin = 0.1
-system.virtual_sites = espressomd.virtual_sites.VirtualSitesInertialessTracers()
print(f"Parallelization: {system.cell_system.node_grid}")
force = 0.001
@@ -79,7 +77,7 @@
agrid=1, density=1, kinematic_viscosity=1, tau=system.time_step,
ext_force_density=[force, 0, 0])
system.lb = lbf
-system.thermostat.set_lb(LB_fluid=lbf, gamma=1.0, act_on_virtual=False)
+system.thermostat.set_lb(LB_fluid=lbf, gamma=1.0)
# Setup boundaries
wall_shapes = [None] * 2
diff --git a/samples/rigid_body.py b/samples/rigid_body.py
index b7b8edcd353..40fc44d5040 100644
--- a/samples/rigid_body.py
+++ b/samples/rigid_body.py
@@ -26,9 +26,7 @@
import math
import numpy as np
-
import espressomd
-import espressomd.virtual_sites
import espressomd.rotation
required_features = ["VIRTUAL_SITES_RELATIVE", "MASS", "ROTATIONAL_INERTIA"]
@@ -36,7 +34,6 @@
system = espressomd.System(box_l=[10.0] * 3)
-system.virtual_sites = espressomd.virtual_sites.VirtualSitesRelative()
system.time_step = 0.01
system.thermostat.set_langevin(kT=1.0, gamma=20.0, seed=42)
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 53a9a8c039c..6a53208beae 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -39,6 +39,7 @@ add_library(
particle_node.cpp
polymer.cpp
pressure.cpp
+ propagation.cpp
rattle.cpp
rotation.cpp
Observable_stat.cpp
diff --git a/src/core/virtual_sites/VirtualSitesOff.hpp b/src/core/CellParticleIterator.hpp
similarity index 72%
rename from src/core/virtual_sites/VirtualSitesOff.hpp
rename to src/core/CellParticleIterator.hpp
index 71bd1e9f71e..b6a5ee9d26d 100644
--- a/src/core/virtual_sites/VirtualSitesOff.hpp
+++ b/src/core/CellParticleIterator.hpp
@@ -16,15 +16,10 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef VIRTUAL_SITES_VIRTUAL_SITES_OFF_HPP
-#define VIRTUAL_SITES_VIRTUAL_SITES_OFF_HPP
-#include "config/config.hpp"
-#ifdef VIRTUAL_SITES
-#include "VirtualSites.hpp"
+#pragma once
-/** @brief Do-nothing virtual-sites implementation */
-class VirtualSitesOff : public VirtualSites {};
+#include "ParticleIterator.hpp"
+#include "cell_system/Cell.hpp"
-#endif
-#endif
+using CellParticleIterator = ParticleIterator;
diff --git a/src/core/Particle.hpp b/src/core/Particle.hpp
index 67ee102e4dd..46b9085e043 100644
--- a/src/core/Particle.hpp
+++ b/src/core/Particle.hpp
@@ -16,12 +16,13 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef ESPRESSO_CORE_PARTICLE_HPP
-#define ESPRESSO_CORE_PARTICLE_HPP
+
+#pragma once
#include "config/config.hpp"
#include "BondList.hpp"
+#include "PropagationMode.hpp"
#include
#include
@@ -74,13 +75,8 @@ struct ParticleProperties {
int mol_id = 0;
/** particle type, used for non-bonded interactions. */
int type = 0;
-
-#ifdef VIRTUAL_SITES
- /** is particle virtual */
- bool is_virtual = false;
-#else // VIRTUAL_SITES
- static constexpr bool is_virtual = false;
-#endif // VIRTUAL_SITES
+ /** which propagation schemes should be applied to the particle **/
+ int propagation = PropagationMode::SYSTEM_DEFAULT;
#ifdef ROTATION
/** Bitfield for the particle axes of rotation.
@@ -206,6 +202,8 @@ struct ParticleProperties {
ar &identity;
ar &mol_id;
ar &type;
+ ar &propagation;
+
#ifdef MASS
ar &mass;
#endif
@@ -228,12 +226,9 @@ struct ParticleProperties {
#ifdef DIPOLE_FIELD_TRACKING
ar &dip_fld;
#endif
-#ifdef VIRTUAL_SITES
- ar &is_virtual;
#ifdef VIRTUAL_SITES_RELATIVE
ar &vs_relative;
#endif
-#endif // VIRTUAL_SITES
#ifdef THERMOSTAT_PER_PARTICLE
ar γ
@@ -422,6 +417,9 @@ struct Particle { // NOLINT(bugprone-exception-escape)
auto const &type() const { return p.type; }
auto &type() { return p.type; }
+ auto const &propagation() const { return p.propagation; }
+ auto &propagation() { return p.propagation; }
+
bool operator==(Particle const &rhs) const { return id() == rhs.id(); }
bool operator!=(Particle const &rhs) const { return id() != rhs.id(); }
@@ -516,17 +514,18 @@ struct Particle { // NOLINT(bugprone-exception-escape)
auto &mu_E() { return p.mu_E; }
#endif
#ifdef VIRTUAL_SITES
- auto &virtual_flag() { return p.is_virtual; }
- auto const &virtual_flag() const { return p.is_virtual; }
- auto is_virtual() const { return p.is_virtual; }
- void set_virtual(bool const virt_flag) { p.is_virtual = virt_flag; }
+ auto is_virtual() const {
+ return (p.propagation & (PropagationMode::TRANS_VS_RELATIVE |
+ PropagationMode::ROT_VS_RELATIVE |
+ PropagationMode::TRANS_LB_TRACER)) != 0;
+ }
+#else
+ constexpr auto is_virtual() const { return false; }
+#endif // VIRTUAL_SITES
#ifdef VIRTUAL_SITES_RELATIVE
auto const &vs_relative() const { return p.vs_relative; }
auto &vs_relative() { return p.vs_relative; }
#endif // VIRTUAL_SITES_RELATIVE
-#else
- constexpr auto is_virtual() const { return p.is_virtual; }
-#endif
#ifdef THERMOSTAT_PER_PARTICLE
auto const &gamma() const { return p.gamma; }
auto &gamma() { return p.gamma; }
@@ -624,5 +623,3 @@ BOOST_IS_BITWISE_SERIALIZABLE(ParticleRattle)
#ifdef VIRTUAL_SITES_RELATIVE
BOOST_IS_BITWISE_SERIALIZABLE(decltype(ParticleProperties::vs_relative))
#endif
-
-#endif
diff --git a/src/core/ParticleRange.hpp b/src/core/ParticleRange.hpp
index 378ca0418e9..5334098b9df 100644
--- a/src/core/ParticleRange.hpp
+++ b/src/core/ParticleRange.hpp
@@ -16,19 +16,19 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef ESPRESSO_SRC_CORE_PARTICLE_RANGE_HPP
-#define ESPRESSO_SRC_CORE_PARTICLE_RANGE_HPP
+#pragma once
+
+#include "CellParticleIterator.hpp"
#include "Particle.hpp"
#include "ParticleIterator.hpp"
+#include "PropagationPredicate.hpp"
#include "cell_system/Cell.hpp"
#include
#include
-using CellParticleIterator = ParticleIterator;
-
/**
* @brief A range of particles.
*
@@ -49,8 +49,13 @@ class ParticleRange : public boost::iterator_range {
return assert(m_size >= 0), static_cast(m_size);
}
+ template
+ ParticleRangeFiltered filter(Predicate pred) const {
+ auto const predicate = PropagationPredicate(pred);
+ return {boost::make_filter_iterator(predicate, begin(), end()),
+ boost::make_filter_iterator(predicate, end(), end())};
+ }
+
private:
base_type::difference_type mutable m_size = -1;
};
-
-#endif
diff --git a/src/core/PropagationMode.hpp b/src/core/PropagationMode.hpp
new file mode 100644
index 00000000000..ff87400f10a
--- /dev/null
+++ b/src/core/PropagationMode.hpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The ESPResSo project
+ *
+ * This file is part of ESPResSo.
+ *
+ * ESPResSo is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ESPResSo is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+namespace PropagationMode {
+/**
+ * @brief Flags to create bitmasks for propagation modes.
+ */
+enum PropagationMode : int {
+ NONE = 0,
+ SYSTEM_DEFAULT = 1 << 0,
+ TRANS_NEWTON = 1 << 1,
+ TRANS_LANGEVIN = 1 << 2,
+ TRANS_LANGEVIN_NPT = 1 << 3,
+ TRANS_VS_RELATIVE = 1 << 4,
+ TRANS_LB_MOMENTUM_EXCHANGE = 1 << 5,
+ TRANS_LB_TRACER = 1 << 6,
+ TRANS_BROWNIAN = 1 << 7,
+ TRANS_STOKESIAN = 1 << 8,
+ ROT_EULER = 1 << 10,
+ ROT_LANGEVIN = 1 << 11,
+ ROT_VS_RELATIVE = 1 << 12,
+ ROT_BROWNIAN = 1 << 13,
+ ROT_STOKESIAN = 1 << 14,
+};
+} // namespace PropagationMode
+
+/** \name Integrator switches */
+enum IntegratorSwitch : int {
+ INTEG_METHOD_NPT_ISO = 0,
+ INTEG_METHOD_NVT = 1,
+ INTEG_METHOD_STEEPEST_DESCENT = 2,
+ INTEG_METHOD_BD = 3,
+ INTEG_METHOD_SD = 7,
+};
diff --git a/src/core/PropagationPredicate.hpp b/src/core/PropagationPredicate.hpp
new file mode 100644
index 00000000000..d2db196f58a
--- /dev/null
+++ b/src/core/PropagationPredicate.hpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The ESPResSo project
+ *
+ * This file is part of ESPResSo.
+ *
+ * ESPResSo is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ESPResSo is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include "CellParticleIterator.hpp"
+#include "ParticleIterator.hpp"
+
+#include
+
+#include
+
+template struct PropagationPredicate {
+ Predicate predicate;
+
+ PropagationPredicate(Predicate pred) : predicate(pred) {}
+
+ bool operator()(Particle const &p) { return predicate(p.propagation()); };
+};
+
+template
+class ParticleRangeFiltered
+ : public boost::iterator_range, CellParticleIterator>> {
+ using base_type = boost::iterator_range, CellParticleIterator>>;
+
+public:
+ using base_type::base_type;
+ auto size() const { return std::distance(this->begin(), this->end()); };
+};
diff --git a/src/core/accumulators/Correlator.cpp b/src/core/accumulators/Correlator.cpp
index cbc1c453a07..fccadbd62cd 100644
--- a/src/core/accumulators/Correlator.cpp
+++ b/src/core/accumulators/Correlator.cpp
@@ -18,8 +18,6 @@
*/
#include "Correlator.hpp"
-#include "integrate.hpp"
-
#include
#include
#include
diff --git a/src/core/accumulators/Correlator.hpp b/src/core/accumulators/Correlator.hpp
index 42588719223..e60fa27d40b 100644
--- a/src/core/accumulators/Correlator.hpp
+++ b/src/core/accumulators/Correlator.hpp
@@ -101,8 +101,8 @@
*/
#include "AccumulatorBase.hpp"
-#include "integrate.hpp"
#include "observables/Observable.hpp"
+#include "system/System.hpp"
#include
@@ -155,11 +155,12 @@ class Correlator : public AccumulatorBase {
obs_ptr obs2, Utils::Vector3d correlation_args_ = {})
: AccumulatorBase(delta_N), finalized(false), t(0),
m_correlation_args(correlation_args_), m_tau_lin(tau_lin),
- m_dt(delta_N * get_time_step()), m_tau_max(tau_max),
+ m_dt(::System::get_system().get_time_step()), m_tau_max(tau_max),
compressA_name(std::move(compress1_)),
compressB_name(std::move(compress2_)),
corr_operation_name(std::move(corr_operation)), A_obs(std::move(obs1)),
B_obs(std::move(obs2)) {
+ m_dt *= static_cast(delta_N);
initialize();
}
diff --git a/src/core/bonded_interactions/thermalized_bond_utils.cpp b/src/core/bonded_interactions/thermalized_bond_utils.cpp
index edde9e90e03..1b87b98c063 100644
--- a/src/core/bonded_interactions/thermalized_bond_utils.cpp
+++ b/src/core/bonded_interactions/thermalized_bond_utils.cpp
@@ -20,7 +20,6 @@
*/
#include "thermalized_bond_utils.hpp"
-#include "integrate.hpp"
#include "thermalized_bond.hpp"
#include "bonded_interactions/bonded_interaction_data.hpp"
diff --git a/src/core/cell_system/HybridDecomposition.hpp b/src/core/cell_system/HybridDecomposition.hpp
index f5f7220a926..c8993027b7d 100644
--- a/src/core/cell_system/HybridDecomposition.hpp
+++ b/src/core/cell_system/HybridDecomposition.hpp
@@ -31,7 +31,6 @@
#include "LocalBox.hpp"
#include "Particle.hpp"
#include "ghosts.hpp"
-#include "integrate.hpp"
#include
#include
diff --git a/src/core/cells.cpp b/src/core/cells.cpp
index 4c8676074b9..a0f95342f86 100644
--- a/src/core/cells.cpp
+++ b/src/core/cells.cpp
@@ -35,7 +35,6 @@
#include "Particle.hpp"
#include "communication.hpp"
#include "errorhandling.hpp"
-#include "integrate.hpp"
#include "particle_node.hpp"
#include "system/System.hpp"
diff --git a/src/core/collision.cpp b/src/core/collision.cpp
index 88941d72a6c..51e5cc1e83c 100644
--- a/src/core/collision.cpp
+++ b/src/core/collision.cpp
@@ -356,7 +356,6 @@ static void place_vs_and_relate_to_particle(CellStructure &cell_structure,
auto p_vs = cell_structure.add_particle(std::move(new_part));
vs_relate_to(*p_vs, get_part(cell_structure, relate_to), box_geo,
min_global_cut);
- p_vs->set_virtual(true);
p_vs->type() = collision_params.vs_particle_type;
}
@@ -481,7 +480,7 @@ static void three_particle_binding_domain_decomposition(
// Handle the collisions stored in the queue
void handle_collisions(CellStructure &cell_structure) {
- auto const &system = System::get_system();
+ auto &system = System::get_system();
auto const &box_geo = *system.box_geo;
// Note that the glue to surface mode adds bonds between the centers
// but does so later in the process. This is needed to guarantee that
@@ -657,6 +656,7 @@ void handle_collisions(CellStructure &cell_structure) {
cell_structure.update_ghosts_and_resort_particle(
Cells::DATA_PART_PROPERTIES | Cells::DATA_PART_BONDS);
}
+ system.update_used_propagations();
} // are we in one of the vs_based methods
#endif // defined VIRTUAL_SITES_RELATIVE
diff --git a/src/core/dpd.cpp b/src/core/dpd.cpp
index da57ce7fe5e..9ee236f798c 100644
--- a/src/core/dpd.cpp
+++ b/src/core/dpd.cpp
@@ -30,7 +30,6 @@
#include "BoxGeometry.hpp"
#include "cell_system/CellStructure.hpp"
#include "cells.hpp"
-#include "integrate.hpp"
#include "nonbonded_interactions/nonbonded_interaction_data.hpp"
#include "random.hpp"
#include "system/System.hpp"
diff --git a/src/core/ek/Solver.cpp b/src/core/ek/Solver.cpp
index a11aed8eb98..2de532a762d 100644
--- a/src/core/ek/Solver.cpp
+++ b/src/core/ek/Solver.cpp
@@ -26,7 +26,6 @@
#include "ek/EKNone.hpp"
#include "ek/EKWalberla.hpp"
-#include "integrate.hpp"
#include "system/System.hpp"
#include
diff --git a/src/core/electrostatics/coulomb.cpp b/src/core/electrostatics/coulomb.cpp
index 870bd72ea92..52850937530 100644
--- a/src/core/electrostatics/coulomb.cpp
+++ b/src/core/electrostatics/coulomb.cpp
@@ -32,8 +32,6 @@
#include "communication.hpp"
#include "electrostatics/icc.hpp"
#include "errorhandling.hpp"
-#include "integrate.hpp"
-#include "npt.hpp"
#include "system/System.hpp"
#include
@@ -114,8 +112,7 @@ struct LongRangePressure {
#ifdef P3M
auto operator()(std::shared_ptr const &actor) const {
- actor->charge_assign(m_particles);
- return actor->p3m_calc_kspace_pressure_tensor();
+ return actor->long_range_pressure(m_particles);
}
#endif // P3M
@@ -215,24 +212,10 @@ struct LongRangeForce {
#ifdef P3M
void operator()(std::shared_ptr const &actor) const {
- actor->charge_assign(m_particles);
-#ifdef NPT
- if (integ_switch == INTEG_METHOD_NPT_ISO) {
- auto const energy = actor->long_range_kernel(true, true, m_particles);
- npt_add_virial_contribution(energy);
- } else
-#endif // NPT
- actor->add_long_range_forces(m_particles);
+ actor->add_long_range_forces(m_particles);
}
#ifdef CUDA
void operator()(std::shared_ptr const &actor) const {
-#ifdef NPT
- if (integ_switch == INTEG_METHOD_NPT_ISO) {
- actor->charge_assign(m_particles);
- auto const energy = actor->long_range_energy(m_particles);
- npt_add_virial_contribution(energy);
- }
-#endif // NPT
actor->add_long_range_forces(m_particles);
}
#endif // CUDA
@@ -266,7 +249,6 @@ struct LongRangeEnergy {
#ifdef P3M
auto operator()(std::shared_ptr const &actor) const {
- actor->charge_assign(m_particles);
return actor->long_range_energy(m_particles);
}
auto
diff --git a/src/core/electrostatics/icc.cpp b/src/core/electrostatics/icc.cpp
index dd7348163a5..975e76d4f44 100644
--- a/src/core/electrostatics/icc.cpp
+++ b/src/core/electrostatics/icc.cpp
@@ -34,13 +34,14 @@
#include "Particle.hpp"
#include "ParticleRange.hpp"
+#include "PropagationMode.hpp"
#include "actor/visitors.hpp"
#include "cell_system/CellStructure.hpp"
#include "communication.hpp"
#include "electrostatics/coulomb.hpp"
#include "electrostatics/coulomb_inline.hpp"
#include "errorhandling.hpp"
-#include "integrate.hpp"
+#include "integrators/Propagation.hpp"
#include "system/System.hpp"
#include
@@ -269,7 +270,7 @@ struct SanityChecksICC {
void ICCStar::sanity_check() const {
sanity_checks_active_solver();
#ifdef NPT
- if (integ_switch == INTEG_METHOD_NPT_ISO) {
+ if (get_system().propagation->integ_switch == INTEG_METHOD_NPT_ISO) {
throw std::runtime_error("ICC does not work in the NPT ensemble");
}
#endif
diff --git a/src/core/electrostatics/p3m.cpp b/src/core/electrostatics/p3m.cpp
index e62d5ff7724..1a78b36d108 100644
--- a/src/core/electrostatics/p3m.cpp
+++ b/src/core/electrostatics/p3m.cpp
@@ -43,12 +43,14 @@
#include "Particle.hpp"
#include "ParticlePropertyIterator.hpp"
#include "ParticleRange.hpp"
+#include "PropagationMode.hpp"
#include "actor/visitors.hpp"
#include "cell_system/CellStructure.hpp"
#include "cell_system/CellStructureType.hpp"
#include "communication.hpp"
#include "errorhandling.hpp"
-#include "integrate.hpp"
+#include "integrators/Propagation.hpp"
+#include "npt.hpp"
#include "system/System.hpp"
#include "tuning.hpp"
@@ -404,7 +406,8 @@ static auto calc_dipole_moment(boost::mpi::communicator const &comm,
* in @cite essmann95a. The part \f$\Pi_{\textrm{corr}, \alpha, \beta}\f$
* eq. (2.8) is not present here since M is the empty set in our simulations.
*/
-Utils::Vector9d CoulombP3M::p3m_calc_kspace_pressure_tensor() {
+Utils::Vector9d
+CoulombP3M::long_range_pressure(ParticleRange const &particles) {
using namespace detail::FFT_indexing;
auto const &box_geo = *get_system().box_geo;
@@ -412,6 +415,7 @@ Utils::Vector9d CoulombP3M::p3m_calc_kspace_pressure_tensor() {
Utils::Vector9d node_k_space_pressure_tensor{};
if (p3m.sum_q2 > 0.) {
+ charge_assign(particles);
p3m.sm.gather_grid(p3m.rs_mesh.data(), comm_cart, p3m.local_mesh.dim);
fft_perform_forw(p3m.rs_mesh.data(), p3m.fft, comm_cart);
@@ -464,12 +468,25 @@ Utils::Vector9d CoulombP3M::p3m_calc_kspace_pressure_tensor() {
double CoulombP3M::long_range_kernel(bool force_flag, bool energy_flag,
ParticleRange const &particles) {
- auto const &box_geo = *get_system().box_geo;
+ auto const &system = get_system();
+ auto const &box_geo = *system.box_geo;
+#ifdef NPT
+ auto const npt_flag =
+ force_flag and (system.propagation->integ_switch == INTEG_METHOD_NPT_ISO);
+#else
+ auto constexpr npt_flag = false;
+#endif
- /* Gather information for FFT grid inside the nodes domain (inner local mesh)
- * and perform forward 3D FFT (Charge Assignment Mesh). */
- p3m.sm.gather_grid(p3m.rs_mesh.data(), comm_cart, p3m.local_mesh.dim);
- fft_perform_forw(p3m.rs_mesh.data(), p3m.fft, comm_cart);
+ if (p3m.sum_q2 > 0.) {
+ if (not has_actor_of_type(
+ system.coulomb.impl->solver)) {
+ charge_assign(particles);
+ }
+ /* Gather information for FFT grid inside the nodes domain (inner local
+ * mesh) and perform forward 3D FFT (Charge Assignment Mesh). */
+ p3m.sm.gather_grid(p3m.rs_mesh.data(), comm_cart, p3m.local_mesh.dim);
+ fft_perform_forw(p3m.rs_mesh.data(), p3m.fft, comm_cart);
+ }
auto p_q_range = ParticlePropertyRange::charge_range(particles);
auto p_force_range = ParticlePropertyRange::force_range(particles);
@@ -549,7 +566,7 @@ double CoulombP3M::long_range_kernel(bool force_flag, bool energy_flag,
}
/* === k-space energy calculation === */
- if (energy_flag) {
+ if (energy_flag or npt_flag) {
auto node_energy = 0.;
for (int i = 0; i < p3m.fft.plan[3].new_size; i++) {
// Use the energy optimized influence function for energy!
@@ -575,7 +592,14 @@ double CoulombP3M::long_range_kernel(bool force_flag, bool energy_flag,
energy += pref * box_dipole.value().norm2();
}
}
- return prefactor * energy;
+ energy *= prefactor;
+ if (npt_flag) {
+ npt_add_virial_contribution(energy);
+ }
+ if (not energy_flag) {
+ energy = 0.;
+ }
+ return energy;
}
return 0.;
diff --git a/src/core/electrostatics/p3m.hpp b/src/core/electrostatics/p3m.hpp
index 82506a93590..776eb57cb3f 100644
--- a/src/core/electrostatics/p3m.hpp
+++ b/src/core/electrostatics/p3m.hpp
@@ -218,7 +218,7 @@ struct CoulombP3M : public Coulomb::Actor {
}
/** Compute the k-space part of the pressure tensor */
- Utils::Vector9d p3m_calc_kspace_pressure_tensor();
+ Utils::Vector9d long_range_pressure(ParticleRange const &particles);
/** Compute the k-space part of energies. */
double long_range_energy(ParticleRange const &particles) {
diff --git a/src/core/electrostatics/p3m_gpu.cpp b/src/core/electrostatics/p3m_gpu.cpp
index 425adff720c..f8cf8abfe88 100644
--- a/src/core/electrostatics/p3m_gpu.cpp
+++ b/src/core/electrostatics/p3m_gpu.cpp
@@ -29,6 +29,9 @@
#include "electrostatics/coulomb.hpp"
#include "ParticleRange.hpp"
+#include "PropagationMode.hpp"
+#include "integrators/Propagation.hpp"
+#include "npt.hpp"
#include "system/GpuParticleData.hpp"
#include "system/System.hpp"
@@ -46,7 +49,15 @@ static auto get_n_part_safe(GpuParticleData const &gpu) {
return static_cast(n_part);
}
-void CoulombP3MGPU::add_long_range_forces(ParticleRange const &) {
+void CoulombP3MGPU::add_long_range_forces(ParticleRange const &particles) {
+#ifdef NPT
+ if (get_system().propagation->integ_switch == INTEG_METHOD_NPT_ISO) {
+ auto const energy = long_range_energy(particles);
+ npt_add_virial_contribution(energy);
+ }
+#else
+ static_cast(particles);
+#endif
if (this_node == 0) {
auto &gpu = get_system().gpu;
p3m_gpu_add_farfield_force(*m_gpu_data, gpu, prefactor,
diff --git a/src/core/energy.cpp b/src/core/energy.cpp
index 1ab986975c6..ae66fd23e5b 100644
--- a/src/core/energy.cpp
+++ b/src/core/energy.cpp
@@ -24,9 +24,7 @@
#include "cell_system/CellStructure.hpp"
#include "constraints.hpp"
#include "energy_inline.hpp"
-#include "forces.hpp"
#include "global_ghost_flags.hpp"
-#include "integrate.hpp"
#include "nonbonded_interactions/nonbonded_interaction_data.hpp"
#include "short_range_loop.hpp"
#include "system/System.hpp"
diff --git a/src/core/forces.cpp b/src/core/forces.cpp
index d10ff935d9e..1bc21bffcbf 100644
--- a/src/core/forces.cpp
+++ b/src/core/forces.cpp
@@ -38,7 +38,7 @@
#include "forces_inline.hpp"
#include "galilei/ComFixed.hpp"
#include "immersed_boundaries.hpp"
-#include "integrate.hpp"
+#include "integrators/Propagation.hpp"
#include "lb/particle_coupling.hpp"
#include "magnetostatics/dipoles.hpp"
#include "nonbonded_interactions/VerletCriterion.hpp"
@@ -49,7 +49,7 @@
#include "system/System.hpp"
#include "thermostat.hpp"
#include "thermostats/langevin_inline.hpp"
-#include "virtual_sites.hpp"
+#include "virtual_sites/relative.hpp"
#include
@@ -64,11 +64,8 @@
#include
#include
-/** Initialize the forces for a ghost particle */
-inline ParticleForce init_ghost_force(Particle const &) { return {}; }
-
/** External particle forces */
-inline ParticleForce external_force(Particle const &p) {
+static ParticleForce external_force(Particle const &p) {
ParticleForce f = {};
#ifdef EXTERNAL_FORCES
@@ -89,63 +86,22 @@ inline ParticleForce external_force(Particle const &p) {
return f;
}
-inline ParticleForce thermostat_force(Particle const &p, double time_step,
- double kT) {
- extern LangevinThermostat langevin;
- if (!(thermo_switch & THERMO_LANGEVIN)) {
- return {};
- }
-
-#ifdef ROTATION
- return {friction_thermo_langevin(langevin, p, time_step, kT),
- p.can_rotate() ? convert_vector_body_to_space(
- p, friction_thermo_langevin_rotation(
- langevin, p, time_step, kT))
- : Utils::Vector3d{}};
-#else
- return friction_thermo_langevin(langevin, p, time_step, kT);
-#endif
-}
-
-/** Initialize the forces for a real particle */
-inline ParticleForce init_real_particle_force(Particle const &p,
- double time_step, double kT) {
- return thermostat_force(p, time_step, kT) + external_force(p);
-}
-
-static void init_forces(const ParticleRange &particles,
- const ParticleRange &ghost_particles, double time_step,
- double kT) {
+static void init_forces(ParticleRange const &particles,
+ ParticleRange const &ghost_particles) {
#ifdef CALIPER
CALI_CXX_MARK_FUNCTION;
#endif
- /* The force initialization depends on the used thermostat and the
- thermodynamic ensemble */
-
-#ifdef NPT
- npt_reset_instantaneous_virials();
-#endif
-
- /* initialize forces with Langevin thermostat forces
- or zero depending on the thermostat
- set torque to zero for all and rescale quaternions
- */
for (auto &p : particles) {
- p.force_and_torque() = init_real_particle_force(p, time_step, kT);
+ p.force_and_torque() = external_force(p);
}
- /* initialize ghost forces with zero
- set torque to zero for all and rescale quaternions
- */
- for (auto &p : ghost_particles) {
- p.force_and_torque() = init_ghost_force(p);
- }
+ init_forces_ghosts(ghost_particles);
}
-void init_forces_ghosts(const ParticleRange &particles) {
+void init_forces_ghosts(ParticleRange const &particles) {
for (auto &p : particles) {
- p.force_and_torque() = init_ghost_force(p);
+ p.force_and_torque() = {};
}
}
@@ -188,8 +144,12 @@ void System::System::calculate_forces(double kT) {
(**icc).iteration(*cell_structure, particles, ghost_particles);
}
}
+#endif // ELECTROSTATICS
+#ifdef NPT
+ npt_reset_instantaneous_virials();
#endif
- init_forces(particles, ghost_particles, time_step, kT);
+ init_forces(particles, ghost_particles);
+ thermostats_force_init(kT);
calc_long_range_forces(particles);
@@ -254,7 +214,7 @@ void System::System::calculate_forces(double kT) {
immersed_boundaries.volume_conservation(*cell_structure);
if (lb.is_solver_set()) {
- LB::couple_particles(thermo_virtual, particles, ghost_particles, time_step);
+ LB::couple_particles(particles, ghost_particles, time_step);
}
#ifdef CUDA
@@ -267,12 +227,14 @@ void System::System::calculate_forces(double kT) {
#endif
#endif // CUDA
-// VIRTUAL_SITES distribute forces
-#ifdef VIRTUAL_SITES
- virtual_sites()->back_transfer_forces_and_torques();
+#ifdef VIRTUAL_SITES_RELATIVE
+ if (propagation->used_propagations &
+ (PropagationMode::TRANS_VS_RELATIVE | PropagationMode::ROT_VS_RELATIVE)) {
+ vs_relative_back_transfer_forces_and_torques(*cell_structure);
+ }
#endif
- // Communication Step: ghost forces
+ // Communication step: ghost forces
cell_structure->ghosts_reduce_forces();
// should be pretty late, since it needs to zero out the total force
@@ -282,7 +244,7 @@ void System::System::calculate_forces(double kT) {
force_capping(particles, force_cap);
// mark that forces are now up-to-date
- recalc_forces = false;
+ propagation->recalc_forces = false;
}
void calc_long_range_forces(const ParticleRange &particles) {
@@ -293,7 +255,6 @@ void calc_long_range_forces(const ParticleRange &particles) {
#ifdef ELECTROSTATICS
/* calculate k-space part of electrostatic interaction. */
Coulomb::get_coulomb().calc_long_range_force(particles);
-
#endif // ELECTROSTATICS
#ifdef DIPOLES
diff --git a/src/core/forces.hpp b/src/core/forces.hpp
index e353fc23fa7..b65c4155018 100644
--- a/src/core/forces.hpp
+++ b/src/core/forces.hpp
@@ -28,24 +28,20 @@
*/
#include "ParticleRange.hpp"
-#include "system/System.hpp"
#include
-#include
-
-/** initialize real particle forces with thermostat forces and
- ghost particle forces with zero. */
-void init_forces(const ParticleRange &particles, double time_step);
+/** Assign external forces/torques to real particles and zero to ghosts. */
+void init_forces(ParticleRange const &particles, double time_step);
/** Set forces of all ghosts to zero */
-void init_forces_ghosts(const ParticleRange &particles);
+void init_forces_ghosts(ParticleRange const &particles);
/** Calculate long range forces (P3M, ...). */
-void calc_long_range_forces(const ParticleRange &particles);
+void calc_long_range_forces(ParticleRange const &particles);
#ifdef NPT
/** Update the NpT virial */
-void npt_add_virial_force_contribution(const Utils::Vector3d &force,
- const Utils::Vector3d &d);
+void npt_add_virial_force_contribution(Utils::Vector3d const &force,
+ Utils::Vector3d const &d);
#endif
diff --git a/src/core/ghosts.cpp b/src/core/ghosts.cpp
index 4655c09ae86..c513656ffb9 100644
--- a/src/core/ghosts.cpp
+++ b/src/core/ghosts.cpp
@@ -126,10 +126,7 @@ serialize_and_reduce(Archive &ar, Particle &p, unsigned int data_parts,
BoxGeometry const &box_geo,
Utils::Vector3d const *ghost_shift) {
if (data_parts & GHOSTTRANS_PROPRTS) {
- ar &p.id() & p.mol_id() & p.type();
-#ifdef VIRTUAL_SITES
- ar &p.virtual_flag();
-#endif
+ ar &p.id() & p.mol_id() & p.type() & p.propagation();
#ifdef ROTATION
ar &p.rotation();
#ifdef ROTATIONAL_INERTIA
diff --git a/src/core/integrate.cpp b/src/core/integrate.cpp
index 97578018a75..9b2bcd50bb2 100644
--- a/src/core/integrate.cpp
+++ b/src/core/integrate.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2022 The ESPResSo project
+ * Copyright (C) 2010-2023 The ESPResSo project
* Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010
* Max-Planck-Institute for Polymer Research, Theory Group
*
@@ -27,6 +27,7 @@
*/
#include "integrate.hpp"
+#include "integrators/Propagation.hpp"
#include "integrators/brownian_inline.hpp"
#include "integrators/steepest_descent.hpp"
#include "integrators/stokesian_dynamics_inline.hpp"
@@ -35,6 +36,7 @@
#include "BoxGeometry.hpp"
#include "ParticleRange.hpp"
+#include "PropagationMode.hpp"
#include "accumulators.hpp"
#include "bond_breakage/bond_breakage.hpp"
#include "bonded_interactions/rigid_bond.hpp"
@@ -55,7 +57,9 @@
#include "signalhandling.hpp"
#include "system/System.hpp"
#include "thermostat.hpp"
-#include "virtual_sites.hpp"
+#include "thermostats/langevin_inline.hpp"
+#include "virtual_sites/lb_tracers.hpp"
+#include "virtual_sites/relative.hpp"
#include
@@ -83,107 +87,161 @@
#endif
#endif
-int integ_switch = INTEG_METHOD_NVT;
-
-/** Actual simulation time. */
-static double sim_time = 0.0;
-
-bool recalc_forces = true;
-
-static int lb_skipped_md_steps = 0;
-static int ek_skipped_md_steps = 0;
-
namespace {
volatile std::sig_atomic_t ctrl_C = 0;
} // namespace
namespace LeesEdwards {
-/** @brief Currently active Lees-Edwards protocol. */
-static std::shared_ptr protocol = nullptr;
-
-std::weak_ptr get_protocol() { return protocol; }
/**
* @brief Update the Lees-Edwards parameters of the box geometry
* for the current simulation time.
*/
-static void update_box_params(BoxGeometry &box_geo) {
+void LeesEdwards::update_box_params(BoxGeometry &box_geo, double sim_time) {
if (box_geo.type() == BoxType::LEES_EDWARDS) {
- assert(protocol != nullptr);
- box_geo.lees_edwards_update(get_pos_offset(sim_time, *protocol),
- get_shear_velocity(sim_time, *protocol));
+ assert(m_protocol != nullptr);
+ box_geo.lees_edwards_update(get_pos_offset(sim_time, *m_protocol),
+ get_shear_velocity(sim_time, *m_protocol));
}
}
-void set_protocol(std::shared_ptr new_protocol) {
- auto &system = System::get_system();
+void LeesEdwards::set_protocol(std::shared_ptr protocol) {
+ auto &system = get_system();
auto &cell_structure = *system.cell_structure;
auto &box_geo = *system.box_geo;
box_geo.set_type(BoxType::LEES_EDWARDS);
- protocol = std::move(new_protocol);
- LeesEdwards::update_box_params(box_geo);
- ::recalc_forces = true;
+ m_protocol = std::move(protocol);
+ update_box_params(box_geo, system.get_sim_time());
+ system.propagation->recalc_forces = true;
cell_structure.set_resort_particles(Cells::RESORT_LOCAL);
}
-void unset_protocol() {
- auto &system = System::get_system();
+void LeesEdwards::unset_protocol() {
+ auto &system = get_system();
auto &cell_structure = *system.cell_structure;
auto &box_geo = *system.box_geo;
- protocol = nullptr;
+ m_protocol = nullptr;
box_geo.set_type(BoxType::CUBOID);
- ::recalc_forces = true;
+ system.propagation->recalc_forces = true;
cell_structure.set_resort_particles(Cells::RESORT_LOCAL);
}
-template void run_kernel(BoxGeometry const &box_geo) {
- if (box_geo.type() == BoxType::LEES_EDWARDS) {
- auto &system = System::get_system();
- auto &cell_structure = *system.cell_structure;
- auto const kernel = Kernel{box_geo};
- auto const particles = cell_structure.local_particles();
- std::for_each(particles.begin(), particles.end(),
- [&kernel](auto &p) { kernel(p); });
+} // namespace LeesEdwards
+
+void Propagation::update_default_propagation() {
+ switch (integ_switch) {
+ case INTEG_METHOD_STEEPEST_DESCENT:
+ default_propagation = PropagationMode::NONE;
+ break;
+ case INTEG_METHOD_NVT: {
+ // NOLINTNEXTLINE(bugprone-branch-clone)
+ if (thermo_switch & THERMO_LB & THERMO_LANGEVIN) {
+ default_propagation = PropagationMode::TRANS_LB_MOMENTUM_EXCHANGE;
+#ifdef ROTATION
+ default_propagation |= PropagationMode::ROT_LANGEVIN;
+#endif
+ } else if (thermo_switch & THERMO_LB) {
+ default_propagation = PropagationMode::TRANS_LB_MOMENTUM_EXCHANGE;
+#ifdef ROTATION
+ default_propagation |= PropagationMode::ROT_EULER;
+#endif
+ } else if (thermo_switch & THERMO_LANGEVIN) {
+ default_propagation = PropagationMode::TRANS_LANGEVIN;
+#ifdef ROTATION
+ default_propagation |= PropagationMode::ROT_LANGEVIN;
+#endif
+ } else {
+ default_propagation = PropagationMode::TRANS_NEWTON;
+#ifdef ROTATION
+ default_propagation |= PropagationMode::ROT_EULER;
+#endif
+ }
+ break;
+ }
+#ifdef NPT
+ case INTEG_METHOD_NPT_ISO:
+ default_propagation = PropagationMode::TRANS_LANGEVIN_NPT;
+ break;
+#endif
+ case INTEG_METHOD_BD:
+ default_propagation = PropagationMode::TRANS_BROWNIAN;
+#ifdef ROTATION
+ default_propagation |= PropagationMode::ROT_BROWNIAN;
+#endif
+ break;
+#ifdef STOKESIAN_DYNAMICS
+ case INTEG_METHOD_SD:
+ default_propagation = PropagationMode::TRANS_STOKESIAN;
+ break;
+#endif // STOKESIAN_DYNAMICS
+ default:
+ throw std::runtime_error("Unknown value for integ_switch");
}
}
-} // namespace LeesEdwards
+
+void System::System::update_used_propagations() {
+ int used_propagations = PropagationMode::NONE;
+ for (auto &p : cell_structure->local_particles()) {
+ used_propagations |= p.propagation();
+ }
+ if (used_propagations & PropagationMode::SYSTEM_DEFAULT) {
+ used_propagations |= propagation->default_propagation;
+ }
+ used_propagations = boost::mpi::all_reduce(::comm_cart, used_propagations,
+ std::bit_or());
+ propagation->used_propagations = used_propagations;
+}
void System::System::integrator_sanity_checks() const {
if (time_step <= 0.) {
runtimeErrorMsg() << "time_step not set";
}
- switch (integ_switch) {
- case INTEG_METHOD_STEEPEST_DESCENT:
- if (thermo_switch != THERMO_OFF)
+ if (propagation->integ_switch == INTEG_METHOD_STEEPEST_DESCENT) {
+ if (thermo_switch != THERMO_OFF) {
runtimeErrorMsg()
<< "The steepest descent integrator is incompatible with thermostats";
- break;
- case INTEG_METHOD_NVT:
- if (thermo_switch & (THERMO_NPT_ISO | THERMO_BROWNIAN | THERMO_SD))
+ }
+ }
+ if (propagation->integ_switch == INTEG_METHOD_NVT) {
+ if (thermo_switch & (THERMO_NPT_ISO | THERMO_BROWNIAN | THERMO_SD)) {
runtimeErrorMsg() << "The VV integrator is incompatible with the "
"currently active combination of thermostats";
- break;
+ }
+ }
#ifdef NPT
- case INTEG_METHOD_NPT_ISO:
- if (thermo_switch != THERMO_OFF and thermo_switch != THERMO_NPT_ISO)
+ if (propagation->used_propagations & PropagationMode::TRANS_LANGEVIN_NPT) {
+ if (thermo_switch != THERMO_OFF and thermo_switch != THERMO_NPT_ISO) {
runtimeErrorMsg() << "The NpT integrator requires the NpT thermostat";
- if (box_geo->type() == BoxType::LEES_EDWARDS)
+ }
+ if (box_geo->type() == BoxType::LEES_EDWARDS) {
runtimeErrorMsg() << "The NpT integrator cannot use Lees-Edwards";
- break;
+ }
+ }
#endif
- case INTEG_METHOD_BD:
- if (thermo_switch != THERMO_BROWNIAN)
+ if (propagation->used_propagations & PropagationMode::TRANS_BROWNIAN) {
+ if (thermo_switch != THERMO_BROWNIAN) {
runtimeErrorMsg() << "The BD integrator requires the BD thermostat";
- break;
+ }
+ }
+ if (propagation->used_propagations & PropagationMode::TRANS_STOKESIAN) {
#ifdef STOKESIAN_DYNAMICS
- case INTEG_METHOD_SD:
- if (thermo_switch != THERMO_OFF and thermo_switch != THERMO_SD)
+ if (thermo_switch != THERMO_OFF && thermo_switch != THERMO_SD) {
runtimeErrorMsg() << "The SD integrator requires the SD thermostat";
- break;
+ }
#endif
- default:
- runtimeErrorMsg() << "Unknown value for integ_switch";
}
+#ifdef ROTATION
+ for (auto const &p : cell_structure->local_particles()) {
+ using namespace PropagationMode;
+ if (p.can_rotate() and not p.is_virtual() and
+ (p.propagation() & (SYSTEM_DEFAULT | ROT_EULER | ROT_LANGEVIN |
+ ROT_BROWNIAN | ROT_STOKESIAN)) == 0) {
+ runtimeErrorMsg()
+ << "Rotating particles must have a rotation propagation mode enabled";
+ break;
+ }
+ }
+#endif
}
#ifdef WALBERLA
@@ -238,76 +296,130 @@ static void resort_particles_if_needed(System::System &system) {
}
}
+void System::System::thermostats_force_init(double kT) {
+ auto const &propagation = *this->propagation;
+ for (auto &p : cell_structure->local_particles()) {
+ if (propagation.should_propagate_with(p, PropagationMode::TRANS_LANGEVIN))
+ p.force() += friction_thermo_langevin(langevin, p, time_step, kT);
+#ifdef ROTATION
+ if (propagation.should_propagate_with(p, PropagationMode::ROT_LANGEVIN))
+ p.torque() += convert_vector_body_to_space(
+ p, friction_thermo_langevin_rotation(langevin, p, time_step, kT));
+#endif
+ }
+}
+
/** @brief Calls the hook for propagation kernels before the force calculation
* @return whether or not to stop the integration loop early.
*/
static bool integrator_step_1(ParticleRange const &particles,
+ Propagation const &propagation, double kT,
double time_step) {
- bool early_exit = false;
- switch (integ_switch) {
- case INTEG_METHOD_STEEPEST_DESCENT:
- early_exit = steepest_descent_step(particles);
- break;
- case INTEG_METHOD_NVT:
- velocity_verlet_step_1(particles, time_step);
- break;
+ // steepest decent
+ if (propagation.integ_switch == INTEG_METHOD_STEEPEST_DESCENT)
+ return steepest_descent_step(particles);
+
+ for (auto &p : particles) {
+#ifdef VIRTUAL_SITES
+ // virtual sites are updated later in the integration loop
+ if (p.is_virtual())
+ continue;
+#endif
+ if (propagation.should_propagate_with(
+ p, PropagationMode::TRANS_LB_MOMENTUM_EXCHANGE))
+ velocity_verlet_propagator_1(p, time_step);
+ if (propagation.should_propagate_with(p, PropagationMode::TRANS_NEWTON))
+ velocity_verlet_propagator_1(p, time_step);
+#ifdef ROTATION
+ if (propagation.should_propagate_with(p, PropagationMode::ROT_EULER))
+ velocity_verlet_rotator_1(p, time_step);
+#endif
+ if (propagation.should_propagate_with(p, PropagationMode::TRANS_LANGEVIN))
+ velocity_verlet_propagator_1(p, time_step);
+#ifdef ROTATION
+ if (propagation.should_propagate_with(p, PropagationMode::ROT_LANGEVIN))
+ velocity_verlet_rotator_1(p, time_step);
+#endif
+ if (propagation.should_propagate_with(p, PropagationMode::TRANS_BROWNIAN))
+ brownian_dynamics_propagator(brownian, p, time_step, kT);
+#ifdef ROTATION
+ if (propagation.should_propagate_with(p, PropagationMode::ROT_BROWNIAN))
+ brownian_dynamics_rotator(brownian, p, time_step, kT);
+#endif
+ }
+
#ifdef NPT
- case INTEG_METHOD_NPT_ISO:
- velocity_verlet_npt_step_1(particles, time_step);
- break;
+ if ((propagation.used_propagations & PropagationMode::TRANS_LANGEVIN_NPT) and
+ (propagation.default_propagation & PropagationMode::TRANS_LANGEVIN_NPT)) {
+ auto pred = PropagationPredicateNPT(propagation.default_propagation);
+ velocity_verlet_npt_step_1(particles.filter(pred), time_step);
+ }
#endif
- case INTEG_METHOD_BD:
- // the Ermak-McCammon's Brownian Dynamics requires a single step
- // so, just skip here
- break;
+
#ifdef STOKESIAN_DYNAMICS
- case INTEG_METHOD_SD:
- stokesian_dynamics_step_1(particles, time_step);
- break;
-#endif // STOKESIAN_DYNAMICS
- default:
- throw std::runtime_error("Unknown value for integ_switch");
+ if ((propagation.used_propagations & PropagationMode::TRANS_STOKESIAN) and
+ (propagation.default_propagation & PropagationMode::TRANS_STOKESIAN)) {
+ auto pred = PropagationPredicateStokesian(propagation.default_propagation);
+ stokesian_dynamics_step_1(particles.filter(pred), time_step);
}
- return early_exit;
+#endif // STOKESIAN_DYNAMICS
+
+ return false;
}
-/** Calls the hook of the propagation kernels after force calculation */
-static void integrator_step_2(ParticleRange const &particles, double kT,
+static void integrator_step_2(ParticleRange const &particles,
+ Propagation const &propagation, double kT,
double time_step) {
- switch (integ_switch) {
- case INTEG_METHOD_STEEPEST_DESCENT:
- // Nothing
- break;
- case INTEG_METHOD_NVT:
- velocity_verlet_step_2(particles, time_step);
- break;
-#ifdef NPT
- case INTEG_METHOD_NPT_ISO:
- velocity_verlet_npt_step_2(particles, time_step);
- break;
+ if (propagation.integ_switch == INTEG_METHOD_STEEPEST_DESCENT)
+ return;
+
+ for (auto &p : particles) {
+#ifdef VIRTUAL_SITES
+ // virtual sites are updated later in the integration loop
+ if (p.is_virtual())
+ continue;
+#endif
+ if (propagation.should_propagate_with(
+ p, PropagationMode::TRANS_LB_MOMENTUM_EXCHANGE))
+ velocity_verlet_propagator_2(p, time_step);
+ if (propagation.should_propagate_with(p, PropagationMode::TRANS_NEWTON))
+ velocity_verlet_propagator_2(p, time_step);
+#ifdef ROTATION
+ if (propagation.should_propagate_with(p, PropagationMode::ROT_EULER))
+ velocity_verlet_rotator_2(p, time_step);
+#endif
+ if (propagation.should_propagate_with(p, PropagationMode::TRANS_LANGEVIN))
+ velocity_verlet_propagator_2(p, time_step);
+#ifdef ROTATION
+ if (propagation.should_propagate_with(p, PropagationMode::ROT_LANGEVIN))
+ velocity_verlet_rotator_2(p, time_step);
#endif
- case INTEG_METHOD_BD:
- // the Ermak-McCammon's Brownian Dynamics requires a single step
- brownian_dynamics_propagator(brownian, particles, time_step, kT);
- break;
-#ifdef STOKESIAN_DYNAMICS
- case INTEG_METHOD_SD:
- // Nothing
- break;
-#endif // STOKESIAN_DYNAMICS
- default:
- throw std::runtime_error("Unknown value for INTEG_SWITCH");
}
-}
-namespace System {
+#ifdef NPT
+ if ((propagation.used_propagations & PropagationMode::TRANS_LANGEVIN_NPT) and
+ (propagation.default_propagation & PropagationMode::TRANS_LANGEVIN_NPT)) {
+ auto pred = PropagationPredicateNPT(propagation.default_propagation);
+ velocity_verlet_npt_step_2(particles.filter(pred), time_step);
+ }
+#endif
+}
-int System::integrate(int n_steps, int reuse_forces) {
+int System::System::integrate(int n_steps, int reuse_forces) {
#ifdef CALIPER
CALI_CXX_MARK_FUNCTION;
#endif
+ auto &propagation = *this->propagation;
+#ifdef VIRTUAL_SITES_RELATIVE
+ auto const has_vs_rel = [&propagation]() {
+ return propagation.used_propagations & (PropagationMode::ROT_VS_RELATIVE |
+ PropagationMode::TRANS_VS_RELATIVE);
+ };
+#endif
// Prepare particle structure and run sanity checks of all active algorithms
+ propagation.update_default_propagation();
+ update_used_propagations();
on_integration_start();
// If any method vetoes (e.g. P3M not initialized), immediately bail out
@@ -316,14 +428,17 @@ int System::integrate(int n_steps, int reuse_forces) {
// Additional preparations for the first integration step
if (reuse_forces == INTEG_REUSE_FORCES_NEVER or
- (recalc_forces and reuse_forces != INTEG_REUSE_FORCES_ALWAYS)) {
+ ((reuse_forces != INTEG_REUSE_FORCES_ALWAYS) and
+ propagation.recalc_forces)) {
#ifdef CALIPER
CALI_MARK_BEGIN("Initial Force Calculation");
#endif
lb_lbcoupling_deactivate();
-#ifdef VIRTUAL_SITES
- virtual_sites()->update();
+#ifdef VIRTUAL_SITES_RELATIVE
+ if (has_vs_rel()) {
+ vs_relative_update_particles(*cell_structure, *box_geo);
+ }
#endif
// Communication step: distribute ghost positions
@@ -331,7 +446,7 @@ int System::integrate(int n_steps, int reuse_forces) {
calculate_forces(::temperature);
- if (integ_switch != INTEG_METHOD_STEEPEST_DESCENT) {
+ if (propagation.integ_switch != INTEG_METHOD_STEEPEST_DESCENT) {
#ifdef ROTATION
convert_initial_torques(cell_structure->local_particles());
#endif
@@ -358,7 +473,7 @@ int System::integrate(int n_steps, int reuse_forces) {
auto lb_active = false;
auto ek_active = false;
- if (integ_switch != INTEG_METHOD_STEEPEST_DESCENT) {
+ if (propagation.integ_switch != INTEG_METHOD_STEEPEST_DESCENT) {
lb_active = lb.is_solver_set();
ek_active = ek.is_ready_for_propagation();
}
@@ -386,15 +501,22 @@ int System::integrate(int n_steps, int reuse_forces) {
save_old_position(particles, cell_structure->ghost_particles());
#endif
- LeesEdwards::update_box_params(*box_geo);
- bool early_exit = integrator_step_1(particles, time_step);
+ lees_edwards->update_box_params(*box_geo, sim_time);
+ bool early_exit =
+ integrator_step_1(particles, propagation, ::temperature, time_step);
if (early_exit)
break;
- LeesEdwards::run_kernel(*box_geo);
+ sim_time += time_step;
+ if (box_geo->type() == BoxType::LEES_EDWARDS) {
+ auto const kernel = LeesEdwards::Push{*box_geo};
+ for (auto &p : particles) {
+ kernel(p);
+ }
+ }
#ifdef NPT
- if (integ_switch != INTEG_METHOD_NPT_ISO)
+ if (propagation.integ_switch != INTEG_METHOD_NPT_ISO)
#endif
{
resort_particles_if_needed(*this);
@@ -410,9 +532,17 @@ int System::integrate(int n_steps, int reuse_forces) {
}
#endif
-#ifdef VIRTUAL_SITES
- virtual_sites()->update();
-#endif
+#ifdef VIRTUAL_SITES_RELATIVE
+ if (has_vs_rel()) {
+#ifdef NPT
+ if (propagation.integ_switch == INTEG_METHOD_NPT_ISO) {
+ cell_structure->update_ghosts_and_resort_particle(
+ Cells::DATA_PART_PROPERTIES);
+ }
+#endif // NPT
+ vs_relative_update_particles(*cell_structure, *box_geo);
+ }
+#endif // VIRTUAL_SITES_RELATIVE
if (cell_structure->get_resort_particles() >= Cells::RESORT_LOCAL)
n_verlet_updates++;
@@ -424,14 +554,21 @@ int System::integrate(int n_steps, int reuse_forces) {
calculate_forces(::temperature);
-#ifdef VIRTUAL_SITES
- virtual_sites()->after_force_calc(time_step);
+#ifdef VIRTUAL_SITES_INERTIALESS_TRACERS
+ if (propagation.used_propagations & PropagationMode::TRANS_LB_TRACER) {
+ lb_tracers_add_particle_force_to_fluid(*cell_structure, time_step);
+ }
#endif
- integrator_step_2(particles, temperature, time_step);
- if (integ_switch == INTEG_METHOD_BD) {
+ integrator_step_2(particles, propagation, ::temperature, time_step);
+ if (propagation.integ_switch == INTEG_METHOD_BD) {
resort_particles_if_needed(*this);
}
- LeesEdwards::run_kernel(*box_geo);
+ if (box_geo->type() == BoxType::LEES_EDWARDS) {
+ auto const kernel = LeesEdwards::UpdateOffset{*box_geo};
+ for (auto &p : particles) {
+ kernel(p);
+ }
+ }
#ifdef BOND_CONSTRAINT
// SHAKE velocity updates
if (n_rigidbonds) {
@@ -440,7 +577,7 @@ int System::integrate(int n_steps, int reuse_forces) {
#endif
// propagate one-step functionalities
- if (integ_switch != INTEG_METHOD_STEEPEST_DESCENT) {
+ if (propagation.integ_switch != INTEG_METHOD_STEEPEST_DESCENT) {
if (lb_active and ek_active) {
// assume that they are coupled, which is not necessarily true
auto const md_steps_per_lb_step = calc_md_steps_per_tau(lb.get_tau());
@@ -451,36 +588,39 @@ int System::integrate(int n_steps, int reuse_forces) {
<< "LB and EK are active but with different time steps.";
}
- assert(lb_skipped_md_steps == ek_skipped_md_steps);
+ assert(propagation.lb_skipped_md_steps ==
+ propagation.ek_skipped_md_steps);
- lb_skipped_md_steps += 1;
- ek_skipped_md_steps += 1;
- if (lb_skipped_md_steps >= md_steps_per_lb_step) {
- lb_skipped_md_steps = 0;
- ek_skipped_md_steps = 0;
+ propagation.lb_skipped_md_steps += 1;
+ propagation.ek_skipped_md_steps += 1;
+ if (propagation.lb_skipped_md_steps >= md_steps_per_lb_step) {
+ propagation.lb_skipped_md_steps = 0;
+ propagation.ek_skipped_md_steps = 0;
lb.propagate();
ek.propagate();
}
lb_lbcoupling_propagate();
} else if (lb_active) {
auto const md_steps_per_lb_step = calc_md_steps_per_tau(lb.get_tau());
- lb_skipped_md_steps += 1;
- if (lb_skipped_md_steps >= md_steps_per_lb_step) {
- lb_skipped_md_steps = 0;
+ propagation.lb_skipped_md_steps += 1;
+ if (propagation.lb_skipped_md_steps >= md_steps_per_lb_step) {
+ propagation.lb_skipped_md_steps = 0;
lb.propagate();
}
lb_lbcoupling_propagate();
} else if (ek_active) {
auto const md_steps_per_ek_step = calc_md_steps_per_tau(ek.get_tau());
- ek_skipped_md_steps += 1;
- if (ek_skipped_md_steps >= md_steps_per_ek_step) {
- ek_skipped_md_steps = 0;
+ propagation.ek_skipped_md_steps += 1;
+ if (propagation.ek_skipped_md_steps >= md_steps_per_ek_step) {
+ propagation.ek_skipped_md_steps = 0;
ek.propagate();
}
}
-#ifdef VIRTUAL_SITES
- virtual_sites()->after_lb_propagation(time_step);
+#ifdef VIRTUAL_SITES_INERTIALESS_TRACERS
+ if (propagation.used_propagations & PropagationMode::TRANS_LB_TRACER) {
+ lb_tracers_propagate(*cell_structure, time_step);
+ }
#endif
#ifdef COLLISION_DETECTION
@@ -503,7 +643,7 @@ int System::integrate(int n_steps, int reuse_forces) {
}
} // for-loop over integration steps
- LeesEdwards::update_box_params(*box_geo);
+ lees_edwards->update_box_params(*box_geo, sim_time);
#ifdef CALIPER
CALI_CXX_MARK_LOOP_END(integration_loop);
#endif
@@ -512,15 +652,17 @@ int System::integrate(int n_steps, int reuse_forces) {
CALLGRIND_STOP_INSTRUMENTATION;
#endif
-#ifdef VIRTUAL_SITES
- virtual_sites()->update();
+#ifdef VIRTUAL_SITES_RELATIVE
+ if (has_vs_rel()) {
+ vs_relative_update_particles(*cell_structure, *box_geo);
+ }
#endif
// Verlet list statistics
cell_structure->update_verlet_stats(n_steps, n_verlet_updates);
#ifdef NPT
- if (integ_switch == INTEG_METHOD_NPT_ISO) {
+ if (propagation.integ_switch == INTEG_METHOD_NPT_ISO) {
synchronize_npt_state();
}
#endif
@@ -534,8 +676,8 @@ int System::integrate(int n_steps, int reuse_forces) {
return integrated_steps;
}
-int System::integrate_with_signal_handler(int n_steps, int reuse_forces,
- bool update_accumulators) {
+int System::System::integrate_with_signal_handler(int n_steps, int reuse_forces,
+ bool update_accumulators) {
assert(n_steps >= 0);
// Override the signal handler so that the integrator obeys Ctrl+C
@@ -585,23 +727,8 @@ int System::integrate_with_signal_handler(int n_steps, int reuse_forces,
return 0;
}
-} // namespace System
-
-double get_time_step() { return System::get_system().get_time_step(); }
-
-double get_sim_time() { return sim_time; }
-
-void increment_sim_time(double amount) { sim_time += amount; }
-
-void set_time(double value) {
- ::sim_time = value;
- ::recalc_forces = true;
- auto &system = System::get_system();
- auto &box_geo = *system.box_geo;
- LeesEdwards::update_box_params(box_geo);
-}
-
-void set_integ_switch(int value) {
- ::integ_switch = value;
- ::recalc_forces = true;
+void System::System::set_sim_time(double value) {
+ sim_time = value;
+ propagation->recalc_forces = true;
+ lees_edwards->update_box_params(*box_geo, sim_time);
}
diff --git a/src/core/integrate.hpp b/src/core/integrate.hpp
index 52f59ae1e3f..5ef12825249 100644
--- a/src/core/integrate.hpp
+++ b/src/core/integrate.hpp
@@ -29,21 +29,14 @@
#include "config/config.hpp"
+#include "PropagationMode.hpp"
+
#ifdef WALBERLA
#include
#include
#endif
-/** \name Integrator switches */
-/**@{*/
-#define INTEG_METHOD_NPT_ISO 0
-#define INTEG_METHOD_NVT 1
-#define INTEG_METHOD_STEEPEST_DESCENT 2
-#define INTEG_METHOD_BD 3
-#define INTEG_METHOD_SD 7
-/**@}*/
-
/** \name Integrator error codes */
/**@{*/
#define INTEG_ERROR_RUNTIME -1
@@ -54,18 +47,12 @@
/**@{*/
/// recalculate forces unconditionally (mostly used for timing)
#define INTEG_REUSE_FORCES_NEVER -1
-/// recalculate forces if @ref recalc_forces is set
+/// recalculate forces only if @ref Propagation::recalc_forces is set
#define INTEG_REUSE_FORCES_CONDITIONALLY 0
/// do not recalculate forces (mostly when reading checkpoints with forces)
#define INTEG_REUSE_FORCES_ALWAYS 1
/**@}*/
-/** Switch determining which integrator to use. */
-extern int integ_switch;
-
-/** If true, the forces will be recalculated before the next integration. */
-extern bool recalc_forces;
-
#ifdef WALBERLA
void walberla_tau_sanity_checks(std::string method, double tau,
double time_step);
@@ -76,17 +63,3 @@ void walberla_agrid_sanity_checks(std::string method,
Utils::Vector3d const &lattice_right,
double agrid);
#endif // WALBERLA
-
-/** Get time step */
-double get_time_step();
-
-/** Get simulation time */
-double get_sim_time();
-
-/** Increase simulation time (only on head node) */
-void increment_sim_time(double amount);
-
-/** @brief Set the simulation time. */
-void set_time(double value);
-
-void set_integ_switch(int value);
diff --git a/src/script_interface/virtual_sites/VirtualSitesOff.hpp b/src/core/integrators/Propagation.hpp
similarity index 51%
rename from src/script_interface/virtual_sites/VirtualSitesOff.hpp
rename to src/core/integrators/Propagation.hpp
index 0987558aa55..f810c5f1ba9 100644
--- a/src/script_interface/virtual_sites/VirtualSitesOff.hpp
+++ b/src/core/integrators/Propagation.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2022 The ESPResSo project
+ * Copyright (C) 2010-2023 The ESPResSo project
* Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010
* Max-Planck-Institute for Polymer Research, Theory Group
*
@@ -19,35 +19,31 @@
* along with this program. If not, see .
*/
-#ifndef SCRIPT_INTERFACE_VIRTUAL_SITES_VIRTUAL_SITES_OFF_HPP
-#define SCRIPT_INTERFACE_VIRTUAL_SITES_VIRTUAL_SITES_OFF_HPP
+#pragma once
-#include "config/config.hpp"
+#include "PropagationMode.hpp"
-#ifdef VIRTUAL_SITES
-
-#include "VirtualSites.hpp"
-
-#include "core/virtual_sites/VirtualSitesOff.hpp"
-
-#include
-
-namespace ScriptInterface {
-namespace VirtualSites {
-
-class VirtualSitesOff : public VirtualSites {
+class Propagation {
public:
- VirtualSitesOff() : m_virtual_sites(std::make_shared<::VirtualSitesOff>()) {}
- /** Vs implementation we are wrapping */
- std::shared_ptr<::VirtualSites> virtual_sites() override {
- return m_virtual_sites;
+ int integ_switch = INTEG_METHOD_NVT;
+ int used_propagations = PropagationMode::NONE;
+ int default_propagation = PropagationMode::NONE;
+ int lb_skipped_md_steps = 0;
+ int ek_skipped_md_steps = 0;
+ /** If true, forces will be recalculated before the next integration. */
+ bool recalc_forces = true;
+
+ void update_default_propagation();
+
+ template
+ bool should_propagate_with(Particle const &p, int mode) const {
+ return (p.propagation() & mode) or
+ ((default_propagation & mode) and
+ (p.propagation() & PropagationMode::SYSTEM_DEFAULT));
}
-private:
- std::shared_ptr<::VirtualSitesOff> m_virtual_sites;
+ void set_integ_switch(int value) {
+ integ_switch = value;
+ recalc_forces = true;
+ }
};
-
-} /* namespace VirtualSites */
-} /* namespace ScriptInterface */
-#endif // VIRTUAL_SITES
-#endif
diff --git a/src/core/integrators/brownian_inline.hpp b/src/core/integrators/brownian_inline.hpp
index 3396d7cabe3..9b238d182c1 100644
--- a/src/core/integrators/brownian_inline.hpp
+++ b/src/core/integrators/brownian_inline.hpp
@@ -19,41 +19,33 @@
* along with this program. If not, see .
*/
-#ifndef INTEGRATORS_BROWNIAN_INLINE_HPP
-#define INTEGRATORS_BROWNIAN_INLINE_HPP
+#pragma once
#include "config/config.hpp"
-#include "ParticleRange.hpp"
-#include "integrate.hpp"
#include "rotation.hpp"
#include "thermostat.hpp"
#include "thermostats/brownian_inline.hpp"
-#include
-
inline void brownian_dynamics_propagator(BrownianThermostat const &brownian,
- const ParticleRange &particles,
- double time_step, double kT) {
- for (auto &p : particles) {
- // Don't propagate translational degrees of freedom of vs
- if (!p.is_virtual() or thermo_virtual) {
- p.pos() += bd_drag(brownian.gamma, p, time_step);
- p.v() = bd_drag_vel(brownian.gamma, p);
- p.pos() += bd_random_walk(brownian, p, time_step, kT);
- p.v() += bd_random_walk_vel(brownian, p);
-#ifdef ROTATION
- if (!p.can_rotate())
- continue;
- convert_torque_to_body_frame_apply_fix(p);
- p.quat() = bd_drag_rot(brownian.gamma_rotation, p, time_step);
- p.omega() = bd_drag_vel_rot(brownian.gamma_rotation, p);
- p.quat() = bd_random_walk_rot(brownian, p, time_step, kT);
- p.omega() += bd_random_walk_vel_rot(brownian, p);
-#endif // ROTATION
- }
- }
- increment_sim_time(time_step);
+ Particle &p, double time_step,
+ double kT) {
+ p.pos() += bd_drag(brownian.gamma, p, time_step);
+ p.v() = bd_drag_vel(brownian.gamma, p);
+ p.pos() += bd_random_walk(brownian, p, time_step, kT);
+ p.v() += bd_random_walk_vel(brownian, p);
}
-#endif // INTEGRATORS_BROWNIAN_INLINE_HPP
+#ifdef ROTATION
+inline void brownian_dynamics_rotator(BrownianThermostat const &brownian,
+ Particle &p, double time_step,
+ double kT) {
+ if (!p.can_rotate())
+ return;
+ convert_torque_to_body_frame_apply_fix(p);
+ p.quat() = bd_drag_rot(brownian.gamma_rotation, p, time_step);
+ p.omega() = bd_drag_vel_rot(brownian.gamma_rotation, p);
+ p.quat() = bd_random_walk_rot(brownian, p, time_step, kT);
+ p.omega() += bd_random_walk_vel_rot(brownian, p);
+}
+#endif // ROTATION
diff --git a/src/core/integrators/steepest_descent.cpp b/src/core/integrators/steepest_descent.cpp
index 2370da4b02e..0e7b9b3540b 100644
--- a/src/core/integrators/steepest_descent.cpp
+++ b/src/core/integrators/steepest_descent.cpp
@@ -55,19 +55,16 @@ bool steepest_descent_step(const ParticleRange &particles) {
for (unsigned int j = 0; j < 3; j++) {
// Skip, if coordinate is fixed
if (!p.is_fixed_along(j)) {
- // Skip positional increments of virtual particles
- if (!p.is_virtual()) {
- // Square of force on particle
- f += Utils::sqr(p.force()[j]);
-
- // Positional increment, crop to maximum allowed by user
- auto const dp =
- std::clamp(params.gamma * p.force()[j], -params.max_displacement,
- params.max_displacement);
-
- // Move particle
- p.pos()[j] += dp;
- }
+ // Square of force on particle
+ f += Utils::sqr(p.force()[j]);
+
+ // Positional increment, crop to maximum allowed by user
+ auto const dp =
+ std::clamp(params.gamma * p.force()[j], -params.max_displacement,
+ params.max_displacement);
+
+ // Move particle
+ p.pos()[j] += dp;
}
}
#ifdef ROTATION
diff --git a/src/core/integrators/stokesian_dynamics_inline.hpp b/src/core/integrators/stokesian_dynamics_inline.hpp
index 1127ff3e685..4150e8a819c 100644
--- a/src/core/integrators/stokesian_dynamics_inline.hpp
+++ b/src/core/integrators/stokesian_dynamics_inline.hpp
@@ -16,30 +16,24 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef STOKESIAN_DYNAMICS_INLINE_HPP
-#define STOKESIAN_DYNAMICS_INLINE_HPP
+
+#pragma once
#include "config/config.hpp"
#ifdef STOKESIAN_DYNAMICS
-#include "ParticleRange.hpp"
-#include "communication.hpp"
-#include "integrate.hpp"
+
#include "rotation.hpp"
#include "stokesian_dynamics/sd_interface.hpp"
-inline void stokesian_dynamics_propagate_vel_pos(const ParticleRange &particles,
- double time_step) {
-
- // Compute new (translational and rotational) velocities
- propagate_vel_pos_sd(particles, comm_cart, time_step);
+inline void stokesian_dynamics_step_1(ParticleRangeStokesian const &particles,
+ double time_step) {
+ propagate_vel_pos_sd(particles, time_step);
for (auto &p : particles) {
-
- // Translate particle
+ // translate
p.pos() += p.v() * time_step;
-
- // Perform rotation
+ // rotate
auto const norm = p.omega().norm();
if (norm != 0.) {
auto const omega_unit = (1. / norm) * p.omega();
@@ -48,11 +42,4 @@ inline void stokesian_dynamics_propagate_vel_pos(const ParticleRange &particles,
}
}
-inline void stokesian_dynamics_step_1(const ParticleRange &particles,
- double time_step) {
- stokesian_dynamics_propagate_vel_pos(particles, time_step);
- increment_sim_time(time_step);
-}
-
#endif // STOKESIAN_DYNAMICS
-#endif
diff --git a/src/core/integrators/velocity_verlet_inline.hpp b/src/core/integrators/velocity_verlet_inline.hpp
index 7ae712fc53b..909dd1e2d0c 100644
--- a/src/core/integrators/velocity_verlet_inline.hpp
+++ b/src/core/integrators/velocity_verlet_inline.hpp
@@ -16,15 +16,14 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef INTEGRATORS_VELOCITY_VERLET_HPP
-#define INTEGRATORS_VELOCITY_VERLET_HPP
+
+#pragma once
#include "config/config.hpp"
#include "Particle.hpp"
#include "ParticleRange.hpp"
#include "cell_system/CellStructure.hpp"
-#include "integrate.hpp"
#include "rotation.hpp"
/** Propagate the velocities and positions. Integration steps before force
@@ -32,26 +31,15 @@
* v(t) + 0.5 \Delta t f(t)/m \f] \f[ p(t+\Delta t) = p(t) + \Delta t
* v(t+0.5 \Delta t) \f]
*/
-inline void velocity_verlet_propagate_vel_pos(const ParticleRange &particles,
- double time_step) {
-
- for (auto &p : particles) {
-#ifdef ROTATION
- propagate_omega_quat_particle(p, time_step);
-#endif
-
- // Don't propagate translational degrees of freedom of vs
- if (p.is_virtual())
- continue;
- for (unsigned int j = 0; j < 3; j++) {
- if (!p.is_fixed_along(j)) {
- /* Propagate velocities: v(t+0.5*dt) = v(t) + 0.5 * dt * a(t) */
- p.v()[j] += 0.5 * time_step * p.force()[j] / p.mass();
+inline void velocity_verlet_propagator_1(Particle &p, double time_step) {
+ for (unsigned int j = 0; j < 3; j++) {
+ if (!p.is_fixed_along(j)) {
+ /* Propagate velocities: v(t+0.5*dt) = v(t) + 0.5 * dt * a(t) */
+ p.v()[j] += 0.5 * time_step * p.force()[j] / p.mass();
- /* Propagate positions (only NVT): p(t + dt) = p(t) + dt *
- * v(t+0.5*dt) */
- p.pos()[j] += time_step * p.v()[j];
- }
+ /* Propagate positions (only NVT): p(t + dt) = p(t) + dt *
+ * v(t+0.5*dt) */
+ p.pos()[j] += time_step * p.v()[j];
}
}
}
@@ -59,35 +47,23 @@ inline void velocity_verlet_propagate_vel_pos(const ParticleRange &particles,
/** Final integration step of the Velocity Verlet integrator
* \f[ v(t+\Delta t) = v(t+0.5 \Delta t) + 0.5 \Delta t f(t+\Delta t)/m \f]
*/
-inline void velocity_verlet_propagate_vel_final(const ParticleRange &particles,
- double time_step) {
-
- for (auto &p : particles) {
- // Virtual sites are not propagated during integration
- if (p.is_virtual())
- continue;
-
- for (unsigned int j = 0; j < 3; j++) {
- if (!p.is_fixed_along(j)) {
- /* Propagate velocity: v(t+dt) = v(t+0.5*dt) + 0.5*dt * a(t+dt) */
- p.v()[j] += 0.5 * time_step * p.force()[j] / p.mass();
- }
+inline void velocity_verlet_propagator_2(Particle &p, double time_step) {
+ for (unsigned int j = 0; j < 3; j++) {
+ if (!p.is_fixed_along(j)) {
+ /* Propagate velocity: v(t+dt) = v(t+0.5*dt) + 0.5*dt * a(t+dt) */
+ p.v()[j] += 0.5 * time_step * p.force()[j] / p.mass();
}
}
}
-inline void velocity_verlet_step_1(const ParticleRange &particles,
- double time_step) {
- velocity_verlet_propagate_vel_pos(particles, time_step);
- increment_sim_time(time_step);
-}
-
-inline void velocity_verlet_step_2(const ParticleRange &particles,
- double time_step) {
- velocity_verlet_propagate_vel_final(particles, time_step);
#ifdef ROTATION
- convert_torques_propagate_omega(particles, time_step);
-#endif
+inline void velocity_verlet_rotator_1(Particle &p, double time_step) {
+ if (p.can_rotate())
+ propagate_omega_quat_particle(p, time_step);
}
-#endif
+inline void velocity_verlet_rotator_2(Particle &p, double time_step) {
+ if (p.can_rotate())
+ convert_torque_propagate_omega(p, time_step);
+}
+#endif // ROTATION
diff --git a/src/core/integrators/velocity_verlet_npt.cpp b/src/core/integrators/velocity_verlet_npt.cpp
index f73592cb936..328bf31859d 100644
--- a/src/core/integrators/velocity_verlet_npt.cpp
+++ b/src/core/integrators/velocity_verlet_npt.cpp
@@ -28,7 +28,6 @@
#include "cell_system/CellStructure.hpp"
#include "communication.hpp"
#include "errorhandling.hpp"
-#include "integrate.hpp"
#include "npt.hpp"
#include "system/System.hpp"
#include "thermostat.hpp"
@@ -44,14 +43,12 @@
static constexpr Utils::Vector3i nptgeom_dir{{1, 2, 4}};
-void velocity_verlet_npt_propagate_vel_final(const ParticleRange &particles,
- double time_step) {
+static void
+velocity_verlet_npt_propagate_vel_final(ParticleRangeNPT const &particles,
+ double time_step) {
nptiso.p_vel = {};
for (auto &p : particles) {
- // Virtual sites are not propagated during integration
- if (p.is_virtual())
- continue;
auto const noise = friction_therm0_nptiso<2>(npt_iso, p.v(), p.id());
for (unsigned int j = 0; j < 3; j++) {
if (!p.is_fixed_along(j)) {
@@ -68,7 +65,7 @@ void velocity_verlet_npt_propagate_vel_final(const ParticleRange &particles,
}
/** Scale and communicate instantaneous NpT pressure */
-void velocity_verlet_npt_finalize_p_inst(double time_step) {
+static void velocity_verlet_npt_finalize_p_inst(double time_step) {
/* finalize derivation of p_inst */
nptiso.p_inst = 0.0;
for (unsigned int i = 0; i < 3; i++) {
@@ -87,8 +84,8 @@ void velocity_verlet_npt_finalize_p_inst(double time_step) {
}
}
-void velocity_verlet_npt_propagate_pos(const ParticleRange &particles,
- double time_step) {
+static void velocity_verlet_npt_propagate_pos(ParticleRangeNPT const &particles,
+ double time_step) {
auto &system = System::get_system();
auto &box_geo = *system.box_geo;
@@ -125,8 +122,6 @@ void velocity_verlet_npt_propagate_pos(const ParticleRange &particles,
/* propagate positions while rescaling positions and velocities */
for (auto &p : particles) {
- if (p.is_virtual())
- continue;
for (unsigned int j = 0; j < 3; j++) {
if (!p.is_fixed_along(j)) {
if (nptiso.geometry & ::nptgeom_dir[j]) {
@@ -163,21 +158,11 @@ void velocity_verlet_npt_propagate_pos(const ParticleRange &particles,
system.on_boxl_change(true);
}
-void velocity_verlet_npt_propagate_vel(const ParticleRange &particles,
- double time_step) {
+static void velocity_verlet_npt_propagate_vel(ParticleRangeNPT const &particles,
+ double time_step) {
nptiso.p_vel = {};
for (auto &p : particles) {
-#ifdef ROTATION
- if (p.can_rotate()) {
- runtimeErrorMsg() << "The isotropic NpT integrator doesn't propagate "
- "angular velocities";
- }
-#endif
-
- // Don't propagate translational degrees of freedom of vs
- if (p.is_virtual())
- continue;
for (unsigned int j = 0; j < 3; j++) {
if (!p.is_fixed_along(j)) {
auto const noise = friction_therm0_nptiso<1>(npt_iso, p.v(), p.id());
@@ -193,16 +178,16 @@ void velocity_verlet_npt_propagate_vel(const ParticleRange &particles,
}
}
-void velocity_verlet_npt_step_1(const ParticleRange &particles,
+void velocity_verlet_npt_step_1(ParticleRangeNPT const &particles,
double time_step) {
velocity_verlet_npt_propagate_vel(particles, time_step);
velocity_verlet_npt_propagate_pos(particles, time_step);
- increment_sim_time(time_step);
}
-void velocity_verlet_npt_step_2(const ParticleRange &particles,
+void velocity_verlet_npt_step_2(ParticleRangeNPT const &particles,
double time_step) {
velocity_verlet_npt_propagate_vel_final(particles, time_step);
velocity_verlet_npt_finalize_p_inst(time_step);
}
+
#endif // NPT
diff --git a/src/core/integrators/velocity_verlet_npt.hpp b/src/core/integrators/velocity_verlet_npt.hpp
index 9b707b8a421..127296dfee1 100644
--- a/src/core/integrators/velocity_verlet_npt.hpp
+++ b/src/core/integrators/velocity_verlet_npt.hpp
@@ -16,14 +16,30 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef INTEGRATORS_VELOCITY_VERLET_NPT_HPP
-#define INTEGRATORS_VELOCITY_VERLET_NPT_HPP
+
+#pragma once
#include "config/config.hpp"
#ifdef NPT
#include "ParticleRange.hpp"
+#include "PropagationMode.hpp"
+#include "PropagationPredicate.hpp"
+
+struct PropagationPredicateNPT {
+ int modes;
+ PropagationPredicateNPT(int default_propagation) {
+ modes = PropagationMode::TRANS_LANGEVIN_NPT;
+ if (default_propagation & PropagationMode::TRANS_LANGEVIN_NPT) {
+ modes |= PropagationMode::SYSTEM_DEFAULT;
+ }
+ }
+
+ bool operator()(int prop) const { return (prop & modes); }
+};
+
+using ParticleRangeNPT = ParticleRangeFiltered;
/** Special propagator for NpT isotropic.
* Propagate the velocities and positions. Integration steps before force
@@ -34,7 +50,7 @@
* Propagate pressure, box_length (2 times) and positions, rescale
* positions and velocities and check Verlet list criterion (only NpT).
*/
-void velocity_verlet_npt_step_1(const ParticleRange &particles,
+void velocity_verlet_npt_step_1(ParticleRangeNPT const &particles,
double time_step);
/** Final integration step of the Velocity Verlet+NpT integrator.
@@ -42,8 +58,7 @@ void velocity_verlet_npt_step_1(const ParticleRange &particles,
* \f[ v(t+\Delta t) = v(t+0.5 \Delta t)
* + 0.5 \Delta t \cdot F(t+\Delta t)/m \f]
*/
-void velocity_verlet_npt_step_2(const ParticleRange &particles,
+void velocity_verlet_npt_step_2(ParticleRangeNPT const &particles,
double time_step);
#endif // NPT
-#endif
diff --git a/src/core/lb/particle_coupling.cpp b/src/core/lb/particle_coupling.cpp
index 416522eae59..10dcc8be663 100644
--- a/src/core/lb/particle_coupling.cpp
+++ b/src/core/lb/particle_coupling.cpp
@@ -241,9 +241,6 @@ Utils::Vector3d ParticleCoupling::get_noise_term(Particle const &p) const {
}
void ParticleCoupling::kernel(Particle &p) {
- if (p.is_virtual() and not m_couple_virtual)
- return;
-
auto const agrid = m_lb.get_agrid();
auto const &box_geo = *System::get_system().box_geo;
@@ -303,7 +300,7 @@ static void lb_coupling_sanity_checks(Particle const &p) {
}
#endif
-void couple_particles(bool couple_virtual, ParticleRange const &real_particles,
+void couple_particles(ParticleRange const &real_particles,
ParticleRange const &ghost_particles, double time_step) {
#ifdef CALIPER
CALI_CXX_MARK_FUNCTION;
@@ -311,11 +308,11 @@ void couple_particles(bool couple_virtual, ParticleRange const &real_particles,
if (lb_particle_coupling.couple_to_md) {
auto &lb = System::get_system().lb;
if (lb.is_solver_set()) {
- ParticleCoupling coupling{lb, couple_virtual, time_step};
+ ParticleCoupling coupling{lb, time_step};
CouplingBookkeeping bookkeeping{};
for (auto const &particle_range : {real_particles, ghost_particles}) {
for (auto &p : particle_range) {
- if (bookkeeping.should_be_coupled(p)) {
+ if (not LB::is_tracer(p) and bookkeeping.should_be_coupled(p)) {
#if defined(THERMOSTAT_PER_PARTICLE) and defined(PARTICLE_ANISOTROPY)
lb_coupling_sanity_checks(p);
#endif
diff --git a/src/core/lb/particle_coupling.hpp b/src/core/lb/particle_coupling.hpp
index fce6d7ceb52..2213c8ee3f1 100644
--- a/src/core/lb/particle_coupling.hpp
+++ b/src/core/lb/particle_coupling.hpp
@@ -22,6 +22,7 @@
#include "BoxGeometry.hpp"
#include "Particle.hpp"
#include "ParticleRange.hpp"
+#include "PropagationMode.hpp"
#include "lb/Solver.hpp"
#include
@@ -129,21 +130,18 @@ extern LB::ParticleCouplingConfig lb_particle_coupling;
namespace LB {
/** @brief Calculate particle-lattice interactions. */
-void couple_particles(bool couple_virtual, ParticleRange const &real_particles,
+void couple_particles(ParticleRange const &real_particles,
ParticleRange const &ghost_particles, double time_step);
class ParticleCoupling {
LB::Solver &m_lb;
- bool m_couple_virtual;
bool m_thermalized;
double m_time_step;
double m_noise_pref_wo_gamma;
public:
- ParticleCoupling(LB::Solver &lb, bool couple_virtual, double time_step,
- double kT)
- : m_lb{lb}, m_couple_virtual{couple_virtual}, m_thermalized{kT != 0.},
- m_time_step{time_step} {
+ ParticleCoupling(LB::Solver &lb, double time_step, double kT)
+ : m_lb{lb}, m_thermalized{kT != 0.}, m_time_step{time_step} {
assert(kT >= 0.);
/* Eq. (16) @cite ahlrichs99a, without the gamma term.
* The factor 12 comes from the fact that we use random numbers
@@ -154,8 +152,8 @@ class ParticleCoupling {
m_noise_pref_wo_gamma = std::sqrt(variance_inv * 2. * kT / time_step);
}
- ParticleCoupling(LB::Solver &lb, bool couple_virtual, double time_step)
- : ParticleCoupling(lb, couple_virtual, time_step,
+ ParticleCoupling(LB::Solver &lb, double time_step)
+ : ParticleCoupling(lb, time_step,
lb.get_kT() * Utils::sqr(lb.get_lattice_speed())) {}
Utils::Vector3d get_noise_term(Particle const &p) const;
@@ -188,6 +186,19 @@ class CouplingBookkeeping {
public:
/** @brief Determine if a given particle should be coupled. */
bool should_be_coupled(Particle const &p) {
+ auto const propagation = p.propagation();
+ if ((propagation & PropagationMode::TRANS_LB_MOMENTUM_EXCHANGE) == 0 and
+ (propagation & PropagationMode::SYSTEM_DEFAULT) == 0 and
+ (propagation & PropagationMode::TRANS_LB_TRACER) == 0) {
+ return false;
+ }
+#ifdef VIRTUAL_SITES_RELATIVE
+ if ((propagation & PropagationMode::TRANS_LB_MOMENTUM_EXCHANGE) == 0 and
+ propagation & (PropagationMode::TRANS_VS_RELATIVE |
+ PropagationMode::ROT_VS_RELATIVE)) {
+ return false;
+ }
+#endif
// real particles: always couple
if (not p.is_ghost()) {
return true;
@@ -202,4 +213,8 @@ class CouplingBookkeeping {
}
};
+inline bool is_tracer(Particle const &p) {
+ return (p.propagation() & PropagationMode::TRANS_LB_TRACER) != 0;
+}
+
} // namespace LB
diff --git a/src/core/lees_edwards/lees_edwards.hpp b/src/core/lees_edwards/lees_edwards.hpp
index 631c945a3f7..49f592d9984 100644
--- a/src/core/lees_edwards/lees_edwards.hpp
+++ b/src/core/lees_edwards/lees_edwards.hpp
@@ -16,17 +16,17 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef CORE_LEES_EDWARDS_LEES_EDWARDS_HPP
-#define CORE_LEES_EDWARDS_LEES_EDWARDS_HPP
+
+#pragma once
#include "BoxGeometry.hpp"
#include "Particle.hpp"
#include "lees_edwards/protocols.hpp"
+#include "system/Leaf.hpp"
#include
#include
-#include
namespace LeesEdwards {
class UpdateOffset {
protected:
@@ -90,14 +90,20 @@ inline Utils::Vector3d verlet_list_offset(BoxGeometry const &box,
return {};
}
-/** @brief Get currently active Lees-Edwards protocol. */
-std::weak_ptr get_protocol();
+class LeesEdwards : public System::Leaf {
+ std::shared_ptr m_protocol;
+
+public:
+ /** @brief Get currently active Lees-Edwards protocol. */
+ auto get_protocol() const { return m_protocol; }
-/** @brief Set a new Lees-Edwards protocol. */
-void set_protocol(std::shared_ptr new_protocol);
+ /** @brief Set a new Lees-Edwards protocol. */
+ void set_protocol(std::shared_ptr protocol);
-/** @brief Delete the currently active Lees-Edwards protocol. */
-void unset_protocol();
+ /** @brief Delete the currently active Lees-Edwards protocol. */
+ void unset_protocol();
+
+ void update_box_params(BoxGeometry &box_geo, double sim_time);
+};
} // namespace LeesEdwards
-#endif
diff --git a/src/core/lees_edwards/protocols.hpp b/src/core/lees_edwards/protocols.hpp
index 29737168b40..8c4fc0f936c 100644
--- a/src/core/lees_edwards/protocols.hpp
+++ b/src/core/lees_edwards/protocols.hpp
@@ -16,14 +16,13 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef CORE_LEES_EDWARDS_PROTOCOLS_HPP
-#define CORE_LEES_EDWARDS_PROTOCOLS_HPP
-#include
+#pragma once
-#include
+#include
#include
+#include
namespace LeesEdwards {
@@ -73,12 +72,12 @@ struct OscillatoryShear {
};
/** Type which holds the currently active protocol */
-using ActiveProtocol = boost::variant;
+using ActiveProtocol = std::variant;
-class PosOffsetGetter : public boost::static_visitor {
+class PosOffsetGetter {
public:
PosOffsetGetter(double time) : m_time{time} {}
- template double operator()(const T &protocol) const {
+ template double operator()(T const &protocol) const {
return protocol.pos_offset(m_time);
}
@@ -87,14 +86,14 @@ class PosOffsetGetter : public boost::static_visitor {
};
inline double get_pos_offset(double time, ActiveProtocol const &protocol) {
- return boost::apply_visitor(PosOffsetGetter(time), protocol);
+ return std::visit(PosOffsetGetter(time), protocol);
}
/** Visitor to get shear velocity from the Lees-Edwards protocol */
-class ShearVelocityGetter : public boost::static_visitor {
+class ShearVelocityGetter {
public:
ShearVelocityGetter(double time) : m_time{time} {}
- template double operator()(const T &protocol) const {
+ template double operator()(T const &protocol) const {
return protocol.shear_velocity(m_time);
}
@@ -104,9 +103,7 @@ class ShearVelocityGetter : public boost::static_visitor {
/** Calculation of current velocity */
inline double get_shear_velocity(double time, ActiveProtocol const &protocol) {
- return boost::apply_visitor(ShearVelocityGetter(time), protocol);
+ return std::visit(ShearVelocityGetter(time), protocol);
}
} // namespace LeesEdwards
-
-#endif
diff --git a/src/core/magnetostatics/dipoles.cpp b/src/core/magnetostatics/dipoles.cpp
index 18f6eedcdeb..4f60e1fa8bf 100644
--- a/src/core/magnetostatics/dipoles.cpp
+++ b/src/core/magnetostatics/dipoles.cpp
@@ -31,8 +31,6 @@
#include "actor/visitors.hpp"
#include "communication.hpp"
#include "errorhandling.hpp"
-#include "integrate.hpp"
-#include "npt.hpp"
#include "system/System.hpp"
#include
@@ -40,7 +38,6 @@
#include
#include
-#include
#include
#include
@@ -123,15 +120,7 @@ struct LongRangeForce {
#ifdef DP3M
void operator()(std::shared_ptr const &actor) const {
- actor->dipole_assign(m_particles);
-#ifdef NPT
- if (integ_switch == INTEG_METHOD_NPT_ISO) {
- auto const energy = actor->kernel(true, true, m_particles);
- npt_add_virial_contribution(energy);
- fprintf(stderr, "dipolar_P3M at this moment is added to p_vir[0]\n");
- } else
-#endif // NPT
- actor->kernel(true, false, m_particles);
+ actor->add_long_range_forces(m_particles);
}
#endif // DP3M
void operator()(std::shared_ptr const &actor) const {
@@ -165,8 +154,7 @@ struct LongRangeEnergy {
#ifdef DP3M
double operator()(std::shared_ptr const &actor) const {
- actor->dipole_assign(m_particles);
- return actor->kernel(false, true, m_particles);
+ return actor->long_range_energy(m_particles);
}
#endif // DP3M
double
diff --git a/src/core/magnetostatics/dp3m.cpp b/src/core/magnetostatics/dp3m.cpp
index 7fb65ec0be7..d262e857b0f 100644
--- a/src/core/magnetostatics/dp3m.cpp
+++ b/src/core/magnetostatics/dp3m.cpp
@@ -43,11 +43,12 @@
#include "LocalBox.hpp"
#include "Particle.hpp"
#include "ParticleRange.hpp"
+#include "PropagationMode.hpp"
#include "cell_system/CellStructure.hpp"
#include "cell_system/CellStructureType.hpp"
#include "communication.hpp"
#include "errorhandling.hpp"
-#include "integrate.hpp"
+#include "integrators/Propagation.hpp"
#include "npt.hpp"
#include "system/System.hpp"
#include "tuning.hpp"
@@ -64,6 +65,7 @@
#include
#include
+#include
#include
#include
#include
@@ -251,17 +253,22 @@ template struct AssignForces {
};
} // namespace
-double DipolarP3M::kernel(bool force_flag, bool energy_flag,
- ParticleRange const &particles) {
- int i, ind, j[3];
+double DipolarP3M::long_range_kernel(bool force_flag, bool energy_flag,
+ ParticleRange const &particles) {
/* k-space energy */
- double k_space_energy_dip = 0.;
- double tmp0, tmp1;
-
- auto const &box_geo = *get_system().box_geo;
+ double energy = 0.;
+ auto const &system = get_system();
+ auto const &box_geo = *system.box_geo;
auto const dipole_prefac = prefactor / Utils::int_pow<3>(dp3m.params.mesh[0]);
-
- if (dp3m.sum_mu2 > 0) {
+#ifdef NPT
+ auto const npt_flag =
+ force_flag and (system.propagation->integ_switch == INTEG_METHOD_NPT_ISO);
+#else
+ auto constexpr npt_flag = false;
+#endif
+
+ if (dp3m.sum_mu2 > 0.) {
+ dipole_assign(particles);
/* Gather information for FFT grid inside the nodes domain (inner local
* mesh) and perform forward 3D FFT (Charge Assignment Mesh). */
std::array meshes = {{dp3m.rs_mesh_dip[0].data(),
@@ -279,20 +286,21 @@ double DipolarP3M::kernel(bool force_flag, bool energy_flag,
}
/* === k-space energy calculation === */
- if (energy_flag) {
+ if (energy_flag or npt_flag) {
/*********************
Dipolar energy
**********************/
- if (dp3m.sum_mu2 > 0) {
+ if (dp3m.sum_mu2 > 0.) {
/* i*k differentiation for dipolar gradients:
* |(\Fourier{\vect{mu}}(k)\cdot \vect{k})|^2 */
- ind = 0;
- i = 0;
- double node_k_space_energy_dip = 0.0;
+ int ind = 0;
+ int i = 0;
+ int j[3];
+ double node_energy = 0.0;
for (j[0] = 0; j[0] < dp3m.fft.plan[3].new_mesh[0]; j[0]++) {
for (j[1] = 0; j[1] < dp3m.fft.plan[3].new_mesh[1]; j[1]++) {
for (j[2] = 0; j[2] < dp3m.fft.plan[3].new_mesh[2]; j[2]++) {
- node_k_space_energy_dip +=
+ node_energy +=
dp3m.g_energy[i] *
(Utils::sqr(
dp3m.rs_mesh_dip[0][ind] *
@@ -313,23 +321,19 @@ double DipolarP3M::kernel(bool force_flag, bool energy_flag,
}
}
}
- node_k_space_energy_dip *=
- dipole_prefac * Utils::pi() * box_geo.length_inv()[0];
- boost::mpi::reduce(comm_cart, node_k_space_energy_dip, k_space_energy_dip,
- std::plus<>(), 0);
+ node_energy *= dipole_prefac * Utils::pi() * box_geo.length_inv()[0];
+ boost::mpi::reduce(comm_cart, node_energy, energy, std::plus<>(), 0);
if (dp3m.energy_correction == 0.)
calc_energy_correction();
if (this_node == 0) {
/* self energy correction */
- k_space_energy_dip -= prefactor * dp3m.sum_mu2 * 2. *
- Utils::int_pow<3>(dp3m.params.alpha) *
- Utils::sqrt_pi_i() / 3.;
+ energy -= prefactor * dp3m.sum_mu2 * Utils::sqrt_pi_i() * (2. / 3.) *
+ Utils::int_pow<3>(dp3m.params.alpha);
/* dipolar energy correction due to systematic Madelung-self effects */
- auto const volume = box_geo.volume();
- k_space_energy_dip += prefactor * dp3m.energy_correction / volume;
+ energy += prefactor * dp3m.energy_correction / box_geo.volume();
}
}
} // if (energy_flag)
@@ -339,11 +343,13 @@ double DipolarP3M::kernel(bool force_flag, bool energy_flag,
/****************************
* DIPOLAR TORQUES (k-space)
****************************/
- if (dp3m.sum_mu2 > 0) {
+ if (dp3m.sum_mu2 > 0.) {
auto const two_pi_L_i = 2. * Utils::pi() * box_geo.length_inv()[0];
/* fill in ks_mesh array for torque calculation */
- ind = 0;
- i = 0;
+ int ind = 0;
+ int i = 0;
+ int j[3];
+ double tmp0, tmp1;
for (j[0] = 0; j[0] < dp3m.fft.plan[3].new_mesh[0]; j[0]++) { // j[0]=n_y
for (j[1] = 0; j[1] < dp3m.fft.plan[3].new_mesh[1];
@@ -497,12 +503,20 @@ double DipolarP3M::kernel(bool force_flag, bool energy_flag,
if (dp3m.params.epsilon != P3M_EPSILON_METALLIC) {
auto const surface_term =
- calc_surface_term(force_flag, energy_flag, particles);
- if (this_node == 0)
- k_space_energy_dip += surface_term;
+ calc_surface_term(force_flag, energy_flag or npt_flag, particles);
+ if (this_node == 0) {
+ energy += surface_term;
+ }
+ }
+ if (npt_flag) {
+ npt_add_virial_contribution(energy);
+ fprintf(stderr, "dipolar_P3M at this moment is added to p_vir[0]\n");
+ }
+ if (not energy_flag) {
+ energy = 0.;
}
- return k_space_energy_dip;
+ return energy;
}
double DipolarP3M::calc_surface_term(bool force_flag, bool energy_flag,
diff --git a/src/core/magnetostatics/dp3m.hpp b/src/core/magnetostatics/dp3m.hpp
index fedfb2c22ab..85bfcdeb638 100644
--- a/src/core/magnetostatics/dp3m.hpp
+++ b/src/core/magnetostatics/dp3m.hpp
@@ -169,9 +169,19 @@ struct DipolarP3M : public Dipoles::Actor {
void tune();
bool is_tuned() const { return m_is_tuned; }
+ /** Compute the k-space part of energies. */
+ double long_range_energy(ParticleRange const &particles) {
+ return long_range_kernel(false, true, particles);
+ }
+
+ /** Compute the k-space part of forces. */
+ void add_long_range_forces(ParticleRange const &particles) {
+ long_range_kernel(true, false, particles);
+ }
+
/** Compute the k-space part of forces and energies. */
- double kernel(bool force_flag, bool energy_flag,
- ParticleRange const &particles);
+ double long_range_kernel(bool force_flag, bool energy_flag,
+ ParticleRange const &particles);
/** Calculate real-space contribution of p3m dipolar pair forces and torques.
* If NPT is compiled in, update the NpT virial.
diff --git a/src/core/npt.cpp b/src/core/npt.cpp
index 0ea9016b6aa..d75e4bbe068 100644
--- a/src/core/npt.cpp
+++ b/src/core/npt.cpp
@@ -20,11 +20,12 @@
#ifdef NPT
+#include "PropagationMode.hpp"
#include "communication.hpp"
#include "config/config.hpp"
#include "electrostatics/coulomb.hpp"
#include "errorhandling.hpp"
-#include "integrate.hpp"
+#include "integrators/Propagation.hpp"
#include "magnetostatics/dipoles.hpp"
#include "system/System.hpp"
@@ -108,21 +109,19 @@ Utils::Vector NptIsoParameters::get_direction() const {
static_cast(geometry & ::nptgeom_dir[2])};
}
-void npt_ensemble_init(const BoxGeometry &box) {
- if (integ_switch == INTEG_METHOD_NPT_ISO) {
- /* prepare NpT-integration */
- nptiso.inv_piston = 1. / nptiso.piston;
- nptiso.volume = pow(box.length()[nptiso.non_const_dim], nptiso.dimension);
- if (recalc_forces) {
- nptiso.p_inst = 0.0;
- nptiso.p_vir = Utils::Vector3d{};
- nptiso.p_vel = Utils::Vector3d{};
- }
+void npt_ensemble_init(Utils::Vector3d const &box_l, bool recalc_forces) {
+ nptiso.inv_piston = 1. / nptiso.piston;
+ nptiso.volume = std::pow(box_l[nptiso.non_const_dim], nptiso.dimension);
+ if (recalc_forces) {
+ nptiso.p_inst = 0.0;
+ nptiso.p_vir = Utils::Vector3d{};
+ nptiso.p_vel = Utils::Vector3d{};
}
}
void integrator_npt_sanity_checks() {
- if (integ_switch == INTEG_METHOD_NPT_ISO) {
+ if (::System::get_system().propagation->used_propagations &
+ PropagationMode::TRANS_LANGEVIN_NPT) {
try {
nptiso.coulomb_dipole_sanity_checks();
} catch (std::runtime_error const &err) {
@@ -133,19 +132,23 @@ void integrator_npt_sanity_checks() {
/** reset virial part of instantaneous pressure */
void npt_reset_instantaneous_virials() {
- if (integ_switch == INTEG_METHOD_NPT_ISO)
+ if (::System::get_system().propagation->used_propagations &
+ PropagationMode::TRANS_LANGEVIN_NPT) {
nptiso.p_vir = Utils::Vector3d{};
+ }
}
void npt_add_virial_contribution(double energy) {
- if (integ_switch == INTEG_METHOD_NPT_ISO) {
+ if (::System::get_system().propagation->used_propagations &
+ PropagationMode::TRANS_LANGEVIN_NPT) {
nptiso.p_vir[0] += energy;
}
}
void npt_add_virial_contribution(const Utils::Vector3d &force,
const Utils::Vector3d &d) {
- if (integ_switch == INTEG_METHOD_NPT_ISO) {
+ if (::System::get_system().propagation->used_propagations &
+ PropagationMode::TRANS_LANGEVIN_NPT) {
nptiso.p_vir += hadamard_product(force, d);
}
}
diff --git a/src/core/npt.hpp b/src/core/npt.hpp
index 658526c033a..defec034b7b 100644
--- a/src/core/npt.hpp
+++ b/src/core/npt.hpp
@@ -22,13 +22,11 @@
* Exports for the NpT code.
*/
-#ifndef NPT_H
-#define NPT_H
+#pragma once
#include "config/config.hpp"
#ifdef NPT
-#include "BoxGeometry.hpp"
#include
@@ -84,7 +82,7 @@ extern NptIsoParameters nptiso;
/** @brief Synchronizes NpT state such as instantaneous and average pressure
*/
void synchronize_npt_state();
-void npt_ensemble_init(const BoxGeometry &box);
+void npt_ensemble_init(Utils::Vector3d const &box_l, bool recalc_forces);
void integrator_npt_sanity_checks();
void npt_reset_instantaneous_virials();
void npt_add_virial_contribution(double energy);
@@ -92,4 +90,3 @@ void npt_add_virial_contribution(const Utils::Vector3d &force,
const Utils::Vector3d &d);
#endif // NPT
-#endif
diff --git a/src/core/particle_node.cpp b/src/core/particle_node.cpp
index 385644ed9a8..3238d86631a 100644
--- a/src/core/particle_node.cpp
+++ b/src/core/particle_node.cpp
@@ -89,8 +89,6 @@ static void mpi_synchronize_max_seen_pid_local() {
boost::mpi::broadcast(::comm_cart, ::max_seen_pid, 0);
}
-REGISTER_CALLBACK(mpi_synchronize_max_seen_pid_local)
-
void init_type_map(int type) {
if (type < 0) {
throw std::runtime_error("Types may not be negative");
diff --git a/src/core/pressure.cpp b/src/core/pressure.cpp
index 3c385a2b501..0a06a6a2065 100644
--- a/src/core/pressure.cpp
+++ b/src/core/pressure.cpp
@@ -32,10 +32,11 @@
#include "pressure_inline.hpp"
#include "short_range_loop.hpp"
#include "system/System.hpp"
-#include "virtual_sites.hpp"
+#include "virtual_sites/relative.hpp"
#include
#include
+#include
#include
@@ -107,10 +108,11 @@ std::shared_ptr System::calculate_pressure() {
Dipoles::get_dipoles().calc_pressure_long_range();
#endif
-#ifdef VIRTUAL_SITES
+#ifdef VIRTUAL_SITES_RELATIVE
if (!obs_pressure.virtual_sites.empty()) {
- auto const vs_pressure = virtual_sites()->pressure_tensor();
- boost::copy(flatten(vs_pressure), obs_pressure.virtual_sites.begin());
+ auto const vs_pressure = vs_relative_pressure_tensor(*cell_structure);
+ boost::copy(Utils::flatten(vs_pressure),
+ obs_pressure.virtual_sites.begin());
}
#endif
diff --git a/src/core/propagation.cpp b/src/core/propagation.cpp
new file mode 100644
index 00000000000..e9a423e55ec
--- /dev/null
+++ b/src/core/propagation.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2023 The ESPResSo project
+ *
+ * This file is part of ESPResSo.
+ *
+ * ESPResSo is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ESPResSo is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "propagation.hpp"
+
+#include "config/config.hpp"
+
+#include "PropagationMode.hpp"
+
+#include
+#include
+#include
+
+// no-op function to generate a trace for code coverage
+static bool force_code_coverage(bool value) { return value; }
+
+/**
+ * Note for developers: when enabling new propagation mode combinations,
+ * make sure every single line of this function has code coverage.
+ */
+bool is_valid_propagation_combination(int propagation) {
+ using namespace PropagationMode;
+ assert(propagation >= 0);
+ // check allowlist
+ switch (propagation) {
+ // only one mode
+ case NONE: // NOLINT(bugprone-branch-clone)
+ return force_code_coverage(true);
+ case SYSTEM_DEFAULT:
+ return force_code_coverage(true);
+ case TRANS_NEWTON:
+ return force_code_coverage(true);
+ case TRANS_BROWNIAN:
+ return force_code_coverage(true);
+ case TRANS_LANGEVIN:
+ return force_code_coverage(true);
+#ifdef NPT
+ case TRANS_LANGEVIN_NPT:
+ return force_code_coverage(true);
+#endif
+#ifdef ROTATION
+ case ROT_EULER:
+ return force_code_coverage(true);
+ case ROT_BROWNIAN:
+ return force_code_coverage(true);
+ case ROT_LANGEVIN:
+ return force_code_coverage(true);
+#endif // ROTATION
+ case TRANS_LB_MOMENTUM_EXCHANGE:
+ return force_code_coverage(true);
+#ifdef VIRTUAL_SITES_INERTIALESS_TRACERS
+ case TRANS_LB_TRACER:
+ return force_code_coverage(true);
+#endif
+#ifdef ROTATION
+ // same mode for translation and rotation
+ case TRANS_NEWTON | ROT_EULER:
+ return force_code_coverage(true);
+ case TRANS_LANGEVIN | ROT_LANGEVIN:
+ return force_code_coverage(true);
+ case TRANS_BROWNIAN | ROT_BROWNIAN:
+ return force_code_coverage(true);
+#ifdef STOKESIAN_DYNAMICS
+ case TRANS_STOKESIAN | ROT_STOKESIAN:
+ return force_code_coverage(true);
+#endif
+ // other mode combinations
+ case TRANS_LB_MOMENTUM_EXCHANGE | ROT_EULER:
+ return force_code_coverage(true);
+ case TRANS_LB_MOMENTUM_EXCHANGE | ROT_LANGEVIN:
+ return force_code_coverage(true);
+#ifdef VIRTUAL_SITES_RELATIVE
+ case TRANS_VS_RELATIVE | ROT_VS_RELATIVE:
+ return force_code_coverage(true);
+ case TRANS_VS_RELATIVE | ROT_VS_RELATIVE | TRANS_LB_MOMENTUM_EXCHANGE:
+ return force_code_coverage(true);
+ case TRANS_VS_RELATIVE | ROT_VS_RELATIVE | TRANS_LANGEVIN | ROT_LANGEVIN:
+ return force_code_coverage(true);
+ case TRANS_VS_RELATIVE | ROT_VS_RELATIVE | TRANS_LB_MOMENTUM_EXCHANGE |
+ ROT_LANGEVIN:
+ return force_code_coverage(true);
+#endif // VIRTUAL_SITES_RELATIVE
+#endif // ROTATION
+ }
+ return force_code_coverage(false);
+}
+
+std::unordered_map propagation_flags_map() {
+ using namespace PropagationMode;
+ std::unordered_map enum_values{};
+ enum_values["NONE"] = NONE;
+ enum_values["SYSTEM_DEFAULT"] = SYSTEM_DEFAULT;
+ enum_values["TRANS_NEWTON"] = TRANS_NEWTON;
+ enum_values["TRANS_LANGEVIN"] = TRANS_LANGEVIN;
+ enum_values["TRANS_LANGEVIN_NPT"] = TRANS_LANGEVIN_NPT;
+ enum_values["TRANS_VS_RELATIVE"] = TRANS_VS_RELATIVE;
+ enum_values["TRANS_LB_MOMENTUM_EXCHANGE"] = TRANS_LB_MOMENTUM_EXCHANGE;
+ enum_values["TRANS_LB_TRACER"] = TRANS_LB_TRACER;
+ enum_values["TRANS_BROWNIAN"] = TRANS_BROWNIAN;
+ enum_values["TRANS_STOKESIAN"] = TRANS_STOKESIAN;
+ enum_values["ROT_EULER"] = ROT_EULER;
+ enum_values["ROT_LANGEVIN"] = ROT_LANGEVIN;
+ enum_values["ROT_VS_RELATIVE"] = ROT_VS_RELATIVE;
+ enum_values["ROT_BROWNIAN"] = ROT_BROWNIAN;
+ enum_values["ROT_STOKESIAN"] = ROT_STOKESIAN;
+ return enum_values;
+}
+
+std::string propagation_bitmask_to_string(int propagation) {
+ std::string serialized{""};
+ for (auto const &[name, flag] : propagation_flags_map()) {
+ if (propagation & flag) {
+ serialized += "|" + name;
+ }
+ }
+ if (serialized.empty()) {
+ serialized = "NONE";
+ } else {
+ serialized = serialized.substr(1);
+ }
+ return serialized;
+}
diff --git a/src/script_interface/virtual_sites/initialize.hpp b/src/core/propagation.hpp
similarity index 56%
rename from src/script_interface/virtual_sites/initialize.hpp
rename to src/core/propagation.hpp
index 3f126eca186..14f62965b6c 100644
--- a/src/script_interface/virtual_sites/initialize.hpp
+++ b/src/core/propagation.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015-2022 The ESPResSo project
+ * Copyright (C) 2023 The ESPResSo project
*
* This file is part of ESPResSo.
*
@@ -17,19 +17,24 @@
* along with this program. If not, see .
*/
-#ifndef SCRIPT_INTERFACE_VIRTUAL_SITES_INITIALIZE_HPP
-#define SCRIPT_INTERFACE_VIRTUAL_SITES_INITIALIZE_HPP
+#pragma once
-#include
+#include "PropagationMode.hpp"
-#include
+#include
+#include
-namespace ScriptInterface {
-namespace VirtualSites {
-
-void initialize(Utils::Factory *om);
+/**
+ * @brief Check allowlist of valid propagation modes combinations.
+ */
+bool is_valid_propagation_combination(int propagation);
-} /* namespace VirtualSites */
-} /* namespace ScriptInterface */
+/**
+ * @brief Convert @ref PropagationMode::PropagationMode to name/value pairs.
+ */
+std::unordered_map propagation_flags_map();
-#endif
+/**
+ * @brief Convert a propagation modes bitmask to a string.
+ */
+std::string propagation_bitmask_to_string(int propagation);
diff --git a/src/core/rotation.cpp b/src/core/rotation.cpp
index c77622499a7..d248e483389 100644
--- a/src/core/rotation.cpp
+++ b/src/core/rotation.cpp
@@ -123,10 +123,7 @@ static void define_Qdd(Particle const &p, Utils::Quaternion &Qd,
* \todo implement for fixed_coord_flag
*/
void propagate_omega_quat_particle(Particle &p, double time_step) {
-
- // If rotation for the particle is disabled entirely, return early.
- if (!p.can_rotate())
- return;
+ assert(p.can_rotate());
Utils::Quaternion Qd{}, Qdd{};
Utils::Vector3d S{}, Wd{};
@@ -159,38 +156,32 @@ void propagate_omega_quat_particle(Particle &p, double time_step) {
}
}
-void convert_torques_propagate_omega(const ParticleRange &particles,
- double time_step) {
- for (auto &p : particles) {
- // Skip particle if rotation is turned off entirely for it.
- if (!p.can_rotate())
- continue;
-
- convert_torque_to_body_frame_apply_fix(p);
+void convert_torque_propagate_omega(Particle &p, double time_step) {
+ assert(p.can_rotate());
+ convert_torque_to_body_frame_apply_fix(p);
- // Propagation of angular velocities
- p.omega() += hadamard_division(0.5 * time_step * p.torque(), p.rinertia());
+ // Propagation of angular velocities
+ p.omega() += hadamard_division(0.5 * time_step * p.torque(), p.rinertia());
- // zeroth estimate of omega
- Utils::Vector3d omega_0 = p.omega();
+ // zeroth estimate of omega
+ Utils::Vector3d omega_0 = p.omega();
- /* if the tensor of inertia is isotropic, the following refinement is not
- needed.
- Otherwise repeat this loop 2-3 times depending on the required accuracy
- */
+ /* if the tensor of inertia is isotropic, the following refinement is not
+ needed.
+ Otherwise repeat this loop 2-3 times depending on the required accuracy
+ */
- const double rinertia_diff_01 = p.rinertia()[0] - p.rinertia()[1];
- const double rinertia_diff_12 = p.rinertia()[1] - p.rinertia()[2];
- const double rinertia_diff_20 = p.rinertia()[2] - p.rinertia()[0];
- for (int times = 0; times <= 5; times++) {
- Utils::Vector3d Wd;
+ const double rinertia_diff_01 = p.rinertia()[0] - p.rinertia()[1];
+ const double rinertia_diff_12 = p.rinertia()[1] - p.rinertia()[2];
+ const double rinertia_diff_20 = p.rinertia()[2] - p.rinertia()[0];
+ for (int times = 0; times <= 5; times++) {
+ Utils::Vector3d Wd;
- Wd[0] = p.omega()[1] * p.omega()[2] * rinertia_diff_12 / p.rinertia()[0];
- Wd[1] = p.omega()[2] * p.omega()[0] * rinertia_diff_20 / p.rinertia()[1];
- Wd[2] = p.omega()[0] * p.omega()[1] * rinertia_diff_01 / p.rinertia()[2];
+ Wd[0] = p.omega()[1] * p.omega()[2] * rinertia_diff_12 / p.rinertia()[0];
+ Wd[1] = p.omega()[2] * p.omega()[0] * rinertia_diff_20 / p.rinertia()[1];
+ Wd[2] = p.omega()[0] * p.omega()[1] * rinertia_diff_01 / p.rinertia()[2];
- p.omega() = omega_0 + (0.5 * time_step) * Wd;
- }
+ p.omega() = omega_0 + (0.5 * time_step) * Wd;
}
}
diff --git a/src/core/rotation.hpp b/src/core/rotation.hpp
index 48e13aca1b5..be3b2fe8cdc 100644
--- a/src/core/rotation.hpp
+++ b/src/core/rotation.hpp
@@ -47,11 +47,7 @@
*/
void propagate_omega_quat_particle(Particle &p, double time_step);
-/** @brief Convert torques to the body-fixed frame and propagate
- * angular velocities.
- */
-void convert_torques_propagate_omega(const ParticleRange &particles,
- double time_step);
+void convert_torque_propagate_omega(Particle &p, double time_step);
/** Convert torques to the body-fixed frame before the integration loop. */
void convert_initial_torques(const ParticleRange &particles);
diff --git a/src/core/stokesian_dynamics/sd_interface.cpp b/src/core/stokesian_dynamics/sd_interface.cpp
index 656e5833cd8..e9802142dd9 100644
--- a/src/core/stokesian_dynamics/sd_interface.cpp
+++ b/src/core/stokesian_dynamics/sd_interface.cpp
@@ -26,7 +26,7 @@
#include "BoxGeometry.hpp"
#include "Particle.hpp"
-#include "ParticleRange.hpp"
+#include "communication.hpp"
#include "system/System.hpp"
#include "thermostat.hpp"
@@ -87,7 +87,8 @@ void register_integrator(StokesianDynamicsParameters const &obj) {
}
/** Update translational and rotational velocities of all particles. */
-void sd_update_locally(ParticleRange const &parts) {
+template
+void sd_update_locally(ParticleIterable const &parts) {
std::size_t i = 0;
// Even though on the head node, the v_sd vector is larger than
@@ -96,11 +97,6 @@ void sd_update_locally(ParticleRange const &parts) {
// (which holds the velocities of ALL particles).
for (auto &p : parts) {
- // skip virtual particles
- if (p.is_virtual()) {
- continue;
- }
-
// Copy velocities
p.v()[0] = v_sd[6 * i + 0];
p.v()[1] = v_sd[6 * i + 1];
@@ -110,7 +106,7 @@ void sd_update_locally(ParticleRange const &parts) {
p.omega()[1] = v_sd[6 * i + 4];
p.omega()[2] = v_sd[6 * i + 5];
- i++;
+ ++i;
}
}
@@ -141,19 +137,19 @@ void set_sd_kT(double kT) {
double get_sd_kT() { return sd_kT; }
-void propagate_vel_pos_sd(const ParticleRange &particles,
- const boost::mpi::communicator &comm,
- const double time_step) {
+void propagate_vel_pos_sd(ParticleRangeStokesian const &particles,
+ double const time_step) {
+
static std::vector parts_buffer{};
parts_buffer.clear();
boost::transform(particles, std::back_inserter(parts_buffer),
[](auto const &p) { return SD_particle_data(p); });
- Utils::Mpi::gather_buffer(parts_buffer, comm, 0);
+ Utils::Mpi::gather_buffer(parts_buffer, ::comm_cart, 0);
/* Buffer that holds local particle data, and all particles on the head
* node used for sending particle data to head node. */
- if (comm.rank() == 0) {
+ if (::comm_cart.rank() == 0) {
std::size_t n_part = parts_buffer.size();
static std::vector x_host{};
@@ -182,9 +178,7 @@ void propagate_vel_pos_sd(const ParticleRange &particles,
f_host[6 * i + 4] = p.ext_force.torque[1];
f_host[6 * i + 5] = p.ext_force.torque[2];
- double radius = params.radii[p.type];
-
- a_host[i] = radius;
+ a_host[i] = params.radii.at(p.type);
++i;
}
@@ -197,8 +191,8 @@ void propagate_vel_pos_sd(const ParticleRange &particles,
v_sd.resize(particles.size() * 6);
} // if (this_node == 0) {...} else
- Utils::Mpi::scatter_buffer(v_sd.data(),
- static_cast(particles.size() * 6), comm, 0);
+ Utils::Mpi::scatter_buffer(
+ v_sd.data(), static_cast(particles.size() * 6), ::comm_cart, 0);
sd_update_locally(particles);
}
diff --git a/src/core/stokesian_dynamics/sd_interface.hpp b/src/core/stokesian_dynamics/sd_interface.hpp
index bdc95116e0a..7977c31e066 100644
--- a/src/core/stokesian_dynamics/sd_interface.hpp
+++ b/src/core/stokesian_dynamics/sd_interface.hpp
@@ -17,24 +17,38 @@
* along with this program. If not, see .
*/
+#pragma once
+
/** @file
* See @cite durlofsky87a for the Stokesian dynamics method used here.
* See @cite banchio03a and @cite brady88a for the thermalization method.
*/
-#ifndef STOKESIAN_DYNAMICS_INTERFACE_H
-#define STOKESIAN_DYNAMICS_INTERFACE_H
-
#include "config/config.hpp"
#ifdef STOKESIAN_DYNAMICS
#include "ParticleRange.hpp"
-
-#include
+#include "PropagationMode.hpp"
+#include "PropagationPredicate.hpp"
#include
+struct PropagationPredicateStokesian {
+ int modes;
+ PropagationPredicateStokesian(int default_propagation) {
+ modes = PropagationMode::TRANS_STOKESIAN;
+ if (default_propagation & PropagationMode::TRANS_STOKESIAN) {
+ modes |= PropagationMode::SYSTEM_DEFAULT;
+ }
+ }
+
+ bool operator()(int prop) const { return (prop & modes); }
+};
+
+using ParticleRangeStokesian =
+ ParticleRangeFiltered;
+
struct StokesianDynamicsParameters {
double viscosity;
std::unordered_map radii;
@@ -61,9 +75,7 @@ double get_sd_kT();
* is gathered from all nodes and their velocities and angular velocities are
* set according to the Stokesian Dynamics method.
*/
-void propagate_vel_pos_sd(const ParticleRange &particles,
- const boost::mpi::communicator &comm,
+void propagate_vel_pos_sd(ParticleRangeStokesian const &particles,
double time_step);
#endif // STOKESIAN_DYNAMICS
-#endif
diff --git a/src/core/system/System.cpp b/src/core/system/System.cpp
index 10331064ee4..075bbb30be0 100644
--- a/src/core/system/System.cpp
+++ b/src/core/system/System.cpp
@@ -24,6 +24,7 @@
#include "BoxGeometry.hpp"
#include "LocalBox.hpp"
+#include "PropagationMode.hpp"
#include "bonded_interactions/bonded_interaction_data.hpp"
#include "cell_system/CellStructure.hpp"
#include "cell_system/CellStructureType.hpp"
@@ -35,12 +36,10 @@
#include "errorhandling.hpp"
#include "global_ghost_flags.hpp"
#include "immersed_boundaries.hpp"
-#include "integrate.hpp"
#include "npt.hpp"
#include "particle_node.hpp"
#include "thermostat.hpp"
-#include "virtual_sites.hpp"
-#include "virtual_sites/VirtualSitesOff.hpp"
+#include "virtual_sites/relative.hpp"
#include
#include
@@ -61,12 +60,15 @@ System::System(Private) {
box_geo = std::make_shared();
local_geo = std::make_shared();
cell_structure = std::make_shared(*box_geo);
+ propagation = std::make_shared();
nonbonded_ias = std::make_shared();
comfixed = std::make_shared();
galilei = std::make_shared();
bond_breakage = std::make_shared();
+ lees_edwards = std::make_shared();
reinit_thermo = true;
time_step = -1.;
+ sim_time = 0.;
force_cap = 0.;
min_global_cut = INACTIVE_CUTOFF;
}
@@ -74,6 +76,7 @@ System::System(Private) {
void System::initialize() {
auto handle = shared_from_this();
cell_structure->bind_system(handle);
+ lees_edwards->bind_system(handle);
#ifdef CUDA
gpu.bind_system(handle);
gpu.initialize();
@@ -107,7 +110,7 @@ void System::set_time_step(double value) {
void System::set_force_cap(double value) {
force_cap = value;
- ::recalc_forces = true;
+ propagation->recalc_forces = true;
}
void System::set_min_global_cut(double value) {
@@ -176,7 +179,7 @@ void System::on_periodicity_change() {
#endif
#ifdef STOKESIAN_DYNAMICS
- if (::integ_switch == INTEG_METHOD_SD) {
+ if (propagation->integ_switch == INTEG_METHOD_SD) {
if (box_geo->periodic(0u) or box_geo->periodic(1u) or box_geo->periodic(2u))
runtimeErrorMsg() << "Stokesian Dynamics requires periodicity "
<< "(False, False, False)\n";
@@ -223,13 +226,13 @@ void System::on_timestep_change() {
void System::on_short_range_ia_change() {
rebuild_cell_structure();
- ::recalc_forces = true;
+ propagation->recalc_forces = true;
}
void System::on_non_bonded_ia_change() {
nonbonded_ias->recalc_maximal_cutoffs();
rebuild_cell_structure();
- ::recalc_forces = true;
+ propagation->recalc_forces = true;
}
void System::on_coulomb_change() {
@@ -246,13 +249,15 @@ void System::on_dipoles_change() {
on_short_range_ia_change();
}
-void System::on_constraint_change() { ::recalc_forces = true; }
+void System::on_constraint_change() { propagation->recalc_forces = true; }
-void System::on_lb_boundary_conditions_change() { ::recalc_forces = true; }
+void System::on_lb_boundary_conditions_change() {
+ propagation->recalc_forces = true;
+}
void System::on_particle_local_change() {
cell_structure->update_ghosts_and_resort_particle(global_ghost_flags());
- ::recalc_forces = true;
+ propagation->recalc_forces = true;
}
void System::on_particle_change() {
@@ -267,7 +272,7 @@ void System::on_particle_change() {
#ifdef DIPOLES
dipoles.on_particle_change();
#endif
- ::recalc_forces = true;
+ propagation->recalc_forces = true;
/* the particle information is no longer valid */
invalidate_fetch_cache();
@@ -281,7 +286,9 @@ void System::on_particle_charge_change() {
void System::update_dependent_particles() {
#ifdef VIRTUAL_SITES
- virtual_sites()->update();
+#ifdef VIRTUAL_SITES_RELATIVE
+ vs_relative_update_particles(*cell_structure, *box_geo);
+#endif
cell_structure->update_ghosts_and_resort_particle(global_ghost_flags());
#endif
@@ -379,11 +386,13 @@ void System::on_integration_start() {
if (reinit_thermo) {
thermo_init(time_step);
reinit_thermo = false;
- ::recalc_forces = true;
+ propagation->recalc_forces = true;
}
#ifdef NPT
- npt_ensemble_init(*box_geo);
+ if (propagation->integ_switch == INTEG_METHOD_NPT_ISO) {
+ npt_ensemble_init(box_geo->length(), propagation->recalc_forces);
+ }
#endif
invalidate_fetch_cache();
@@ -420,9 +429,4 @@ void mpi_init_stand_alone(int argc, char **argv) {
// initialize the MpiCallbacks framework
Communication::init(mpi_env);
-
- // default-construct global state of the system
-#ifdef VIRTUAL_SITES
- set_virtual_sites(std::make_shared());
-#endif
}
diff --git a/src/core/system/System.hpp b/src/core/system/System.hpp
index 832d54f2b49..0e6174457ce 100644
--- a/src/core/system/System.hpp
+++ b/src/core/system/System.hpp
@@ -39,6 +39,7 @@
class BoxGeometry;
class LocalBox;
struct CellStructure;
+class Propagation;
class InteractionsNonBonded;
class ComFixed;
class Galilei;
@@ -46,6 +47,9 @@ class Observable_stat;
namespace BondBreakage {
class BondBreakage;
}
+namespace LeesEdwards {
+class LeesEdwards;
+}
namespace System {
@@ -76,6 +80,12 @@ class System : public std::enable_shared_from_this {
/** @brief Set @ref time_step. */
void set_time_step(double value);
+ /** @brief Get @ref sim_time. */
+ auto get_sim_time() const { return sim_time; }
+
+ /** @brief Set @ref sim_time. */
+ void set_sim_time(double value);
+
/** @brief Get @ref force_cap. */
auto get_force_cap() const { return force_cap; }
@@ -151,7 +161,7 @@ class System : public std::enable_shared_from_this {
* It is up to the propagation kernels to increment the simulation time.
*
* This function propagates the system according to the choice of integrator
- * stored in @ref integ_switch. The general structure is:
+ * stored in @ref Propagation::integ_switch. The general structure is:
* - if reuse_forces is zero, recalculate the forces based on the current
* state of the system
* - Loop over the number of simulation steps:
@@ -178,6 +188,8 @@ class System : public std::enable_shared_from_this {
int integrate_with_signal_handler(int n_steps, int reuse_forces,
bool update_accumulators);
+ void thermostats_force_init(double kT);
+
/** \name Hook procedures
* These procedures are called if several significant changes to
* the system happen which may make a reinitialization of subsystems
@@ -216,10 +228,6 @@ class System : public std::enable_shared_from_this {
void on_particle_change();
/** @brief Called every time a particle charge changes. */
void on_particle_charge_change();
- /** @brief Update particles with properties depending on other particles,
- * namely virtual sites and ICC charges.
- */
- void update_dependent_particles();
/** called before calculating observables, i.e. energy, pressure or
* the integrator (forces). Initialize any methods here which are not
* initialized immediately (P3M etc.).
@@ -227,6 +235,16 @@ class System : public std::enable_shared_from_this {
void on_observable_calc();
/**@}*/
+ /**
+ * @brief Update particles with properties depending on other particles,
+ * namely virtual sites and ICC charges.
+ */
+ void update_dependent_particles();
+ /**
+ * @brief Update the global propagation bitmask.
+ */
+ void update_used_propagations();
+
Coulomb::Solver coulomb;
Dipoles::Solver dipoles;
LB::Solver lb;
@@ -234,10 +252,12 @@ class System : public std::enable_shared_from_this {
std::shared_ptr box_geo;
std::shared_ptr local_geo;
std::shared_ptr cell_structure;
+ std::shared_ptr propagation;
std::shared_ptr nonbonded_ias;
std::shared_ptr comfixed;
std::shared_ptr galilei;
std::shared_ptr bond_breakage;
+ std::shared_ptr lees_edwards;
protected:
/** @brief Whether the thermostat has to be reinitialized before integration.
@@ -245,6 +265,8 @@ class System : public std::enable_shared_from_this {
bool reinit_thermo;
/** @brief Molecular dynamics integrator time step. */
double time_step;
+ /** @brief Molecular dynamics integrator simulation time. */
+ double sim_time;
/** @brief Molecular dynamics integrator force capping. */
double force_cap;
/**
diff --git a/src/core/system/System.impl.hpp b/src/core/system/System.impl.hpp
index fdb950d2b4c..3a2c1e9f662 100644
--- a/src/core/system/System.impl.hpp
+++ b/src/core/system/System.impl.hpp
@@ -29,6 +29,8 @@
#include "cell_system/CellStructure.hpp"
#include "galilei/ComFixed.hpp"
#include "galilei/Galilei.hpp"
+#include "integrators/Propagation.hpp"
+#include "lees_edwards/lees_edwards.hpp"
#include "nonbonded_interactions/nonbonded_interaction_data.hpp"
#include "BoxGeometry.hpp"
diff --git a/src/core/thermostat.cpp b/src/core/thermostat.cpp
index 3b0ce275bad..cc44101ac32 100644
--- a/src/core/thermostat.cpp
+++ b/src/core/thermostat.cpp
@@ -29,7 +29,6 @@
#include "communication.hpp"
#include "dpd.hpp"
#include "errorhandling.hpp"
-#include "integrate.hpp"
#include "npt.hpp"
#include "system/System.hpp"
#include "thermostat.hpp"
@@ -41,7 +40,6 @@
int thermo_switch = THERMO_OFF;
double temperature = 0.0;
-bool thermo_virtual = true;
using Thermostat::GammaType;
@@ -185,16 +183,6 @@ void mpi_set_langevin_gamma_rot(GammaType const &gamma) {
mpi_call_all(mpi_set_langevin_gamma_rot_local, gamma);
}
-void mpi_set_thermo_virtual_local(bool thermo_virtual) {
- ::thermo_virtual = thermo_virtual;
-}
-
-REGISTER_CALLBACK(mpi_set_thermo_virtual_local)
-
-void mpi_set_thermo_virtual(bool thermo_virtual) {
- mpi_call_all(mpi_set_thermo_virtual_local, thermo_virtual);
-}
-
void mpi_set_temperature_local(double temperature) {
::temperature = temperature;
try {
diff --git a/src/core/thermostat.hpp b/src/core/thermostat.hpp
index dfd0c507c36..88878664599 100644
--- a/src/core/thermostat.hpp
+++ b/src/core/thermostat.hpp
@@ -18,8 +18,9 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef CORE_THERMOSTAT_HPP
-#define CORE_THERMOSTAT_HPP
+
+#pragma once
+
/** \file
* Implementation in \ref thermostat.cpp.
*/
@@ -93,9 +94,6 @@ extern int thermo_switch;
/** Temperature of the thermostat. */
extern double temperature;
-/** True if the thermostat should act on virtual particles. */
-extern bool thermo_virtual;
-
#ifdef THERMOSTAT_PER_PARTICLE
inline auto const &
handle_particle_gamma(Thermostat::GammaType const &particle_gamma,
@@ -409,8 +407,6 @@ void mpi_set_brownian_gamma_rot(Thermostat::GammaType const &gamma);
void mpi_set_langevin_gamma(Thermostat::GammaType const &gamma);
void mpi_set_langevin_gamma_rot(Thermostat::GammaType const &gamma);
-void mpi_set_thermo_virtual(bool thermo_virtual);
-
void mpi_set_temperature(double temperature);
void mpi_set_temperature_local(double temperature);
@@ -421,5 +417,3 @@ void mpi_set_thermo_switch_local(int thermo_switch);
void mpi_set_nptiso_gammas(double gamma0, double gammav);
void mpi_set_nptiso_gammas_local(double gamma0, double gammav);
#endif
-
-#endif
diff --git a/src/core/thermostats/brownian_inline.hpp b/src/core/thermostats/brownian_inline.hpp
index bc2ee7675f1..f010b03eeaa 100644
--- a/src/core/thermostats/brownian_inline.hpp
+++ b/src/core/thermostats/brownian_inline.hpp
@@ -19,8 +19,7 @@
* along with this program. If not, see .
*/
-#ifndef THERMOSTATS_BROWNIAN_INLINE_HPP
-#define THERMOSTATS_BROWNIAN_INLINE_HPP
+#pragma once
#include "config/config.hpp"
@@ -150,10 +149,6 @@ inline Utils::Vector3d bd_drag_vel(Thermostat::GammaType const &brownian_gamma,
*/
inline Utils::Vector3d bd_random_walk(BrownianThermostat const &brownian,
Particle const &p, double dt, double kT) {
- // skip the translation thermalizing for virtual sites unless enabled
- if (p.is_virtual() and !thermo_virtual)
- return {};
-
Thermostat::GammaType sigma_pos = brownian.sigma_pos;
#ifdef THERMOSTAT_PER_PARTICLE
// override default if particle-specific gamma
@@ -219,10 +214,6 @@ inline Utils::Vector3d bd_random_walk(BrownianThermostat const &brownian,
*/
inline Utils::Vector3d bd_random_walk_vel(BrownianThermostat const &brownian,
Particle const &p) {
- // skip the translation thermalizing for virtual sites unless enabled
- if (p.is_virtual() and !thermo_virtual)
- return {};
-
auto const noise = Random::noise_gaussian(
brownian.rng_counter(), brownian.rng_seed(), p.id());
Utils::Vector3d velocity = {};
@@ -384,5 +375,3 @@ bd_random_walk_vel_rot(BrownianThermostat const &brownian, Particle const &p) {
return mask(p.rotation(), domega);
}
#endif // ROTATION
-
-#endif // THERMOSTATS_BROWNIAN_INLINE_HPP
diff --git a/src/core/thermostats/langevin_inline.hpp b/src/core/thermostats/langevin_inline.hpp
index 419d1314536..602758c4a11 100644
--- a/src/core/thermostats/langevin_inline.hpp
+++ b/src/core/thermostats/langevin_inline.hpp
@@ -19,8 +19,7 @@
* along with this program. If not, see .
*/
-#ifndef THERMOSTATS_LANGEVIN_INLINE_HPP
-#define THERMOSTATS_LANGEVIN_INLINE_HPP
+#pragma once
#include "config/config.hpp"
@@ -44,11 +43,6 @@
inline Utils::Vector3d
friction_thermo_langevin(LangevinThermostat const &langevin, Particle const &p,
double time_step, double kT) {
- // Early exit for virtual particles without thermostat
- if (p.is_virtual() and !thermo_virtual) {
- return {};
- }
-
// Determine prefactors for the friction and the noise term
#ifdef THERMOSTAT_PER_PARTICLE
auto const gamma = handle_particle_gamma(p.gamma(), langevin.gamma);
@@ -61,7 +55,6 @@ friction_thermo_langevin(LangevinThermostat const &langevin, Particle const &p,
auto const friction_op = handle_particle_anisotropy(p, pref_friction);
auto const noise_op = handle_particle_anisotropy(p, pref_noise);
-
return friction_op * p.v() +
noise_op * Random::noise_uniform(
langevin.rng_counter(), langevin.rng_seed(), p.id());
@@ -97,6 +90,4 @@ friction_thermo_langevin_rotation(LangevinThermostat const &langevin,
return -hadamard_product(pref_friction, p.omega()) +
hadamard_product(pref_noise, noise);
}
-
#endif // ROTATION
-#endif
diff --git a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp
index db3f81fc755..ee604d1f2b3 100644
--- a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp
+++ b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp
@@ -29,6 +29,7 @@ namespace utf = boost::unit_test;
#include "Observable_stat.hpp"
#include "Particle.hpp"
+#include "PropagationMode.hpp"
#include "accumulators/TimeSeries.hpp"
#include "actor/registration.hpp"
#include "bonded_interactions/bonded_interaction_utils.hpp"
@@ -42,6 +43,7 @@ namespace utf = boost::unit_test;
#include "electrostatics/p3m.hpp"
#include "galilei/Galilei.hpp"
#include "integrate.hpp"
+#include "integrators/Propagation.hpp"
#include "magnetostatics/dipoles.hpp"
#include "nonbonded_interactions/lj.hpp"
#include "nonbonded_interactions/nonbonded_interaction_data.hpp"
@@ -340,7 +342,7 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory) {
// check integration
{
// set up velocity-Verlet integrator
- set_integ_switch(INTEG_METHOD_NVT);
+ espresso::system->propagation->set_integ_switch(INTEG_METHOD_NVT);
// reset system
remove_translational_motion(system);
diff --git a/src/core/unit_tests/Particle_test.cpp b/src/core/unit_tests/Particle_test.cpp
index 8eef353fc60..a03c2c2e514 100644
--- a/src/core/unit_tests/Particle_test.cpp
+++ b/src/core/unit_tests/Particle_test.cpp
@@ -24,6 +24,7 @@
#include
#include "Particle.hpp"
+#include "PropagationMode.hpp"
#include "config/config.hpp"
#include
@@ -36,6 +37,7 @@
#include
#include
#include
+#include
#include
#include
@@ -290,4 +292,8 @@ BOOST_AUTO_TEST_CASE(particle_bitfields) {
p.set_cannot_rotate_all_axes();
BOOST_CHECK(not p.can_rotate());
#endif
+
+ static_assert(
+ std::is_same_v,
+ decltype(ParticleProperties::propagation)>);
}
diff --git a/src/core/unit_tests/Verlet_list_test.cpp b/src/core/unit_tests/Verlet_list_test.cpp
index 143147720fd..04d2ca1f4d0 100644
--- a/src/core/unit_tests/Verlet_list_test.cpp
+++ b/src/core/unit_tests/Verlet_list_test.cpp
@@ -36,14 +36,17 @@ namespace bdata = boost::unit_test::data;
#include "particle_management.hpp"
#include "Particle.hpp"
+#include "PropagationMode.hpp"
#include "cell_system/CellStructureType.hpp"
#include "cells.hpp"
#include "communication.hpp"
#include "integrate.hpp"
+#include "integrators/Propagation.hpp"
#include "integrators/steepest_descent.hpp"
#include "nonbonded_interactions/lj.hpp"
#include "npt.hpp"
#include "particle_node.hpp"
+#include "system/System.hpp"
#include "thermostat.hpp"
#include
@@ -85,7 +88,8 @@ struct : public IntegratorHelper {
void set_integrator() const override {
mpi_set_thermo_switch_local(THERMO_OFF);
register_integrator(SteepestDescentParameters(0., 0.01, 100.));
- set_integ_switch(INTEG_METHOD_STEEPEST_DESCENT);
+ espresso::system->propagation->set_integ_switch(
+ INTEG_METHOD_STEEPEST_DESCENT);
}
void set_particle_properties(int pid) const override {
set_particle_property(pid, &Particle::ext_force,
@@ -98,7 +102,7 @@ struct : public IntegratorHelper {
struct : public IntegratorHelper {
void set_integrator() const override {
mpi_set_thermo_switch_local(THERMO_OFF);
- set_integ_switch(INTEG_METHOD_NVT);
+ espresso::system->propagation->set_integ_switch(INTEG_METHOD_NVT);
}
void set_particle_properties(int pid) const override {
set_particle_v(pid, {20., 0., 0.});
@@ -110,7 +114,7 @@ struct : public IntegratorHelper {
struct : public IntegratorHelper {
void set_integrator() const override {
::nptiso = NptIsoParameters(1., 1e9, {true, true, true}, true);
- set_integ_switch(INTEG_METHOD_NPT_ISO);
+ espresso::system->propagation->set_integ_switch(INTEG_METHOD_NPT_ISO);
mpi_set_temperature_local(1.);
mpi_npt_iso_set_rng_seed(0);
mpi_set_thermo_switch_local(thermo_switch | THERMO_NPT_ISO);
diff --git a/src/core/unit_tests/energy_test.cpp b/src/core/unit_tests/energy_test.cpp
index cd9e9dbf455..09a5637f3a9 100644
--- a/src/core/unit_tests/energy_test.cpp
+++ b/src/core/unit_tests/energy_test.cpp
@@ -22,6 +22,7 @@
#include
#include "Particle.hpp"
+#include "PropagationMode.hpp"
#include "energy_inline.hpp"
#include
@@ -47,7 +48,7 @@ BOOST_AUTO_TEST_CASE(translational_kinetic_energy_) {
#ifdef MASS
p.mass() = 2.;
#endif
- p.set_virtual(true);
+ p.propagation() = PropagationMode::TRANS_VS_RELATIVE;
p.v() = {3., 4., 5.};
auto const expected = 0.;
@@ -78,7 +79,7 @@ BOOST_AUTO_TEST_CASE(rotational_kinetic_energy_) {
#ifdef ROTATIONAL_INERTIA
p.rinertia() = {1., 2., 3.};
#endif
- p.set_virtual(true);
+ p.propagation() = PropagationMode::ROT_VS_RELATIVE;
p.omega() = {3., 4., 5.};
p.set_can_rotate_all_axes();
diff --git a/src/core/unit_tests/lb_particle_coupling_test.cpp b/src/core/unit_tests/lb_particle_coupling_test.cpp
index 73ba0a71db0..50e512ceb55 100644
--- a/src/core/unit_tests/lb_particle_coupling_test.cpp
+++ b/src/core/unit_tests/lb_particle_coupling_test.cpp
@@ -183,7 +183,7 @@ BOOST_AUTO_TEST_CASE(rng) {
lb_particle_coupling.gamma = 0.2;
auto &lb = espresso::system->lb;
- LB::ParticleCoupling coupling{lb, true, params.time_step, 1.};
+ LB::ParticleCoupling coupling{lb, params.time_step, 1.};
BOOST_REQUIRE(lb_particle_coupling.rng_counter_coupling);
BOOST_CHECK_EQUAL(lb_lbcoupling_get_rng_state(), 17);
BOOST_CHECK(not lb_lbcoupling_is_seed_required());
@@ -209,7 +209,7 @@ BOOST_AUTO_TEST_CASE(rng) {
BOOST_CHECK(step1_random1 != step2_random1);
BOOST_CHECK(step1_random1 != step2_random2);
- LB::ParticleCoupling coupling_unthermalized{lb, true, params.time_step, 0.};
+ LB::ParticleCoupling coupling_unthermalized{lb, params.time_step, 0.};
auto const step3_norandom =
coupling_unthermalized.get_noise_term(test_partcl_2);
BOOST_CHECK((step3_norandom == Utils::Vector3d{0., 0., 0.}));
@@ -218,7 +218,7 @@ BOOST_AUTO_TEST_CASE(rng) {
BOOST_AUTO_TEST_CASE(drift_vel_offset) {
Particle p{};
auto &lb = espresso::system->lb;
- LB::ParticleCoupling coupling{lb, false, params.time_step};
+ LB::ParticleCoupling coupling{lb, params.time_step};
BOOST_CHECK_EQUAL(coupling.lb_drift_velocity_offset(p).norm(), 0);
Utils::Vector3d expected{};
#ifdef LB_ELECTROHYDRODYNAMICS
@@ -261,7 +261,7 @@ BOOST_DATA_TEST_CASE(swimmer_force, bdata::make(kTs), kT) {
// swimmer coupling
{
if (in_local_halo(p.pos())) {
- LB::ParticleCoupling coupling{lb, true, params.time_step};
+ LB::ParticleCoupling coupling{lb, params.time_step};
coupling.kernel(p);
auto const interpolated = LB::get_force_to_be_applied(p.pos());
auto const expected =
@@ -312,7 +312,7 @@ BOOST_DATA_TEST_CASE(particle_coupling, bdata::make(kTs), kT) {
auto const gamma = 0.2;
lb_lbcoupling_set_gamma(gamma);
Particle p{};
- LB::ParticleCoupling coupling{lb, false, params.time_step};
+ LB::ParticleCoupling coupling{lb, params.time_step};
auto expected = coupling.get_noise_term(p);
#ifdef LB_ELECTROHYDRODYNAMICS
p.mu_E() = Utils::Vector3d{-2., 1.5, 1.};
@@ -371,7 +371,7 @@ BOOST_DATA_TEST_CASE_F(CleanupActorLB, coupling_particle_lattice_ia,
set_particle_property(pid, &Particle::mu_E, Utils::Vector3d{-2., 1.5, 1.});
#endif
- LB::ParticleCoupling coupling{lb, thermo_virtual, params.time_step};
+ LB::ParticleCoupling coupling{lb, params.time_step};
auto const p_opt = copy_particle_to_head_node(comm, system, pid);
auto expected = Utils::Vector3d{};
if (rank == 0) {
@@ -443,8 +443,7 @@ BOOST_DATA_TEST_CASE_F(CleanupActorLB, coupling_particle_lattice_ia,
lb_lbcoupling_broadcast();
auto const particles = cell_structure.local_particles();
auto const ghost_particles = cell_structure.ghost_particles();
- LB::couple_particles(thermo_virtual, particles, ghost_particles,
- params.time_step);
+ LB::couple_particles(particles, ghost_particles, params.time_step);
auto const p_opt = copy_particle_to_head_node(comm, system, pid);
if (rank == 0) {
auto const &p = *p_opt;
@@ -468,8 +467,7 @@ BOOST_DATA_TEST_CASE_F(CleanupActorLB, coupling_particle_lattice_ia,
}
}
// couple particle to LB
- LB::couple_particles(thermo_virtual, particles, ghost_particles,
- params.time_step);
+ LB::couple_particles(particles, ghost_particles, params.time_step);
{
auto const p_opt = copy_particle_to_head_node(comm, system, pid);
if (rank == 0) {
diff --git a/src/core/virtual_sites.cpp b/src/core/virtual_sites.cpp
index 1838760dbf6..370beeeef9f 100644
--- a/src/core/virtual_sites.cpp
+++ b/src/core/virtual_sites.cpp
@@ -21,7 +21,7 @@
#include "config/config.hpp"
-#ifdef VIRTUAL_SITES
+#ifdef VIRTUAL_SITES_RELATIVE
#include "virtual_sites.hpp"
@@ -29,7 +29,6 @@
#include "Particle.hpp"
#include "communication.hpp"
#include "errorhandling.hpp"
-#include "integrate.hpp"
#include "nonbonded_interactions/nonbonded_interaction_data.hpp"
#include
@@ -40,24 +39,10 @@
#include
#include
-namespace {
-std::shared_ptr m_virtual_sites;
-}
-
-const std::shared_ptr &virtual_sites() { return m_virtual_sites; }
-
-void set_virtual_sites(std::shared_ptr const &v) {
- m_virtual_sites = v;
- recalc_forces = true;
-}
-
-#ifdef VIRTUAL_SITES_RELATIVE
-
-/** Calculate the rotation quaternion and distance between two particles */
std::tuple, double>
calculate_vs_relate_to_params(Particle const &p_vs, Particle const &p_relate_to,
- BoxGeometry const &box_geo,
- double min_global_cut) {
+ BoxGeometry const &box_geo, double min_global_cut,
+ bool override_cutoff_check) {
// get the distance between the particles
auto d = box_geo.get_mi_vector(p_vs.pos(), p_relate_to.pos());
@@ -65,7 +50,7 @@ calculate_vs_relate_to_params(Particle const &p_vs, Particle const &p_relate_to,
// than minimum global cutoff. If so, warn user.
auto const dist = d.norm();
if (dist > min_global_cut and ::communicator.size > 1 and
- not virtual_sites()->get_override_cutoff_check()) {
+ not override_cutoff_check) {
runtimeErrorMsg()
<< "Warning: The distance between virtual and non-virtual particle ("
<< dist << ") is larger than the minimum global cutoff ("
@@ -128,4 +113,3 @@ calculate_vs_relate_to_params(Particle const &p_vs, Particle const &p_relate_to,
}
#endif // VIRTUAL_SITES_RELATIVE
-#endif // VIRTUAL_SITES
diff --git a/src/core/virtual_sites.hpp b/src/core/virtual_sites.hpp
index ae85a2a570c..aeafd663fb5 100644
--- a/src/core/virtual_sites.hpp
+++ b/src/core/virtual_sites.hpp
@@ -21,30 +21,22 @@
#include "config/config.hpp"
-#ifdef VIRTUAL_SITES
-#include "Particle.hpp"
-
-#include "virtual_sites/VirtualSites.hpp"
-
-#include
-
-/** @brief Get active virtual sites implementation */
-const std::shared_ptr &virtual_sites();
-
-/** @brief Set active virtual sites implementation */
-void set_virtual_sites(std::shared_ptr const &v);
-
#ifdef VIRTUAL_SITES_RELATIVE
#include "BoxGeometry.hpp"
+#include "Particle.hpp"
+#include "PropagationMode.hpp"
#include
#include
-std::tuple, double> calculate_vs_relate_to_params(
- Particle const &p_current, Particle const &p_relate_to,
- BoxGeometry const &box_geo, double min_global_cut);
+/** Calculate the rotation quaternion and distance between two particles */
+std::tuple, double>
+calculate_vs_relate_to_params(Particle const &p_current,
+ Particle const &p_relate_to,
+ BoxGeometry const &box_geo, double min_global_cut,
+ bool override_cutoff_check = false);
/**
* @brief Setup a virtual site to track a real particle.
@@ -61,7 +53,8 @@ inline void vs_relate_to(Particle &p_vs, Particle const &p_relate_to,
vs_relative.to_particle_id = p_relate_to.id();
std::tie(vs_relative.rel_orientation, vs_relative.distance) =
calculate_vs_relate_to_params(p_vs, p_relate_to, box_geo, min_global_cut);
+ p_vs.propagation() =
+ PropagationMode::TRANS_VS_RELATIVE | PropagationMode::ROT_VS_RELATIVE;
}
#endif // VIRTUAL_SITES_RELATIVE
-#endif // VIRTUAL_SITES
diff --git a/src/core/virtual_sites/CMakeLists.txt b/src/core/virtual_sites/CMakeLists.txt
index 13401606e58..a59c29840e2 100644
--- a/src/core/virtual_sites/CMakeLists.txt
+++ b/src/core/virtual_sites/CMakeLists.txt
@@ -17,7 +17,5 @@
# along with this program. If not, see .
#
-target_sources(
- espresso_core
- PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/VirtualSitesInertialessTracers.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/VirtualSitesRelative.cpp)
+target_sources(espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/lb_tracers.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/relative.cpp)
diff --git a/src/core/virtual_sites/VirtualSites.hpp b/src/core/virtual_sites/VirtualSites.hpp
deleted file mode 100644
index 0d172059d22..00000000000
--- a/src/core/virtual_sites/VirtualSites.hpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2010-2022 The ESPResSo project
- * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010
- * Max-Planck-Institute for Polymer Research, Theory Group
- *
- * This file is part of ESPResSo.
- *
- * ESPResSo is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ESPResSo is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-#ifndef VIRTUAL_SITES_VIRTUAL_SITES_HPP
-#define VIRTUAL_SITES_VIRTUAL_SITES_HPP
-
-/** \file
- * This file contains routine to handle virtual sites
- * Virtual sites are like particles, but they will not be integrated.
- * Step performed for virtual sites:
- * - update virtual sites
- * - calculate forces
- * - distribute forces
- * - move non-virtual particles
- * - update virtual sites
- */
-
-#include "config/config.hpp"
-
-#ifdef VIRTUAL_SITES
-#include
-#include
-
-/** @brief Base class for virtual sites implementations */
-class VirtualSites {
-public:
- VirtualSites() = default;
- virtual ~VirtualSites() = default;
-
- /**
- * @brief Update positions and velocities of virtual sites.
- */
- virtual void update() const {}
- /** Back-transfer forces (and torques) to non-virtual particles. */
- virtual void back_transfer_forces_and_torques() const {}
- /** @brief Called after force calculation (and before rattle/shake) */
- virtual void after_force_calc(double) {}
- virtual void after_lb_propagation(double) {}
- /** @brief Pressure contribution. */
- virtual Utils::Matrix pressure_tensor() const { return {}; }
- /** @brief Enable/disable quaternion calculations for vs.*/
- void set_have_quaternion(const bool &have_quaternion) {
- m_have_quaternion = have_quaternion;
- }
- bool have_quaternions() const { return m_have_quaternion; }
- /** @brief Enable/disable override for the vs cutoff check */
- void set_override_cutoff_check(const bool &override_cutoff_check) {
- m_override_cutoff_check = override_cutoff_check;
- }
- bool get_override_cutoff_check() const { return m_override_cutoff_check; }
-
-private:
- bool m_have_quaternion = false;
- bool m_override_cutoff_check = false;
-};
-
-#endif
-#endif
diff --git a/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp b/src/core/virtual_sites/lb_tracers.cpp
similarity index 88%
rename from src/core/virtual_sites/VirtualSitesInertialessTracers.cpp
rename to src/core/virtual_sites/lb_tracers.cpp
index 4993ac0012f..b688a5ab13c 100644
--- a/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp
+++ b/src/core/virtual_sites/lb_tracers.cpp
@@ -20,9 +20,8 @@
#ifdef VIRTUAL_SITES_INERTIALESS_TRACERS
-#include "VirtualSitesInertialessTracers.hpp"
-
#include "BoxGeometry.hpp"
+#include "PropagationMode.hpp"
#include "cell_system/CellStructure.hpp"
#include "errorhandling.hpp"
#include "forces.hpp"
@@ -46,10 +45,10 @@ static bool lb_active_check(DeferredActiveLBChecks const &check) {
return true;
}
-void VirtualSitesInertialessTracers::after_force_calc(double time_step) {
+void lb_tracers_add_particle_force_to_fluid(CellStructure &cell_structure,
+ double time_step) {
DeferredActiveLBChecks check_lb_solver_set{};
auto &system = System::get_system();
- auto &cell_structure = *system.cell_structure;
auto const &box_geo = *system.box_geo;
auto const agrid = (check_lb_solver_set()) ? system.lb.get_agrid() : 0.;
auto const to_lb_units = (check_lb_solver_set()) ? 1. / agrid : 0.;
@@ -58,14 +57,14 @@ void VirtualSitesInertialessTracers::after_force_calc(double time_step) {
init_forces_ghosts(cell_structure.ghost_particles());
cell_structure.update_ghosts_and_resort_particle(Cells::DATA_PART_FORCE);
- // Set to store ghost particles (ids) that have already been coupled
+ // Keep track of ghost particles (ids) that have already been coupled
LB::CouplingBookkeeping bookkeeping{};
- // Apply particle forces to the LB fluid at particle positions
- // For physical particles, also set particle velocity = fluid velocity
+ // Apply particle forces to the LB fluid at particle positions.
+ // For physical particles, also set particle velocity = fluid velocity.
for (auto const &particle_range :
{cell_structure.local_particles(), cell_structure.ghost_particles()}) {
for (auto const &p : particle_range) {
- if (!p.is_virtual())
+ if (!LB::is_tracer(p))
continue;
if (!lb_active_check(check_lb_solver_set)) {
return;
@@ -82,17 +81,16 @@ void VirtualSitesInertialessTracers::after_force_calc(double time_step) {
init_forces_ghosts(cell_structure.ghost_particles());
}
-void VirtualSitesInertialessTracers::after_lb_propagation(double time_step) {
+void lb_tracers_propagate(CellStructure &cell_structure, double time_step) {
DeferredActiveLBChecks check_lb_solver_set{};
auto &system = System::get_system();
- auto &cell_structure = *system.cell_structure;
auto const &lb = system.lb;
auto const verlet_skin = cell_structure.get_verlet_skin();
auto const verlet_skin_sq = verlet_skin * verlet_skin;
// Advect particles
for (auto &p : cell_structure.local_particles()) {
- if (!p.is_virtual())
+ if (!LB::is_tracer(p))
continue;
if (!lb_active_check(check_lb_solver_set)) {
return;
diff --git a/src/core/virtual_sites/VirtualSitesInertialessTracers.hpp b/src/core/virtual_sites/lb_tracers.hpp
similarity index 65%
rename from src/core/virtual_sites/VirtualSitesInertialessTracers.hpp
rename to src/core/virtual_sites/lb_tracers.hpp
index 0fbc25bb1be..927d82de611 100644
--- a/src/core/virtual_sites/VirtualSitesInertialessTracers.hpp
+++ b/src/core/virtual_sites/lb_tracers.hpp
@@ -16,22 +16,17 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef VIRTUAL_SITES_VIRTUAL_SITES_INERTIALESS_TRACERS_HPP
-#define VIRTUAL_SITES_VIRTUAL_SITES_INERTIALESS_TRACERS_HPP
+
+#pragma once
#include "config/config.hpp"
#ifdef VIRTUAL_SITES_INERTIALESS_TRACERS
-#include "VirtualSites.hpp"
+#include "BoxGeometry.hpp"
-/** @brief Virtual sites which are advected with an lb fluid. Forces on them are
- * instantaneously transferred to the fluid
- */
-class VirtualSitesInertialessTracers : public VirtualSites {
- void after_force_calc(double time_step) override;
- void after_lb_propagation(double time_step) override;
-};
+void lb_tracers_add_particle_force_to_fluid(CellStructure &cell_structure,
+ double time_step);
+void lb_tracers_propagate(CellStructure &cell_structure, double time_step);
#endif // VIRTUAL_SITES_INERTIALESS_TRACERS
-#endif
diff --git a/src/core/virtual_sites/VirtualSitesRelative.cpp b/src/core/virtual_sites/relative.cpp
similarity index 69%
rename from src/core/virtual_sites/VirtualSitesRelative.cpp
rename to src/core/virtual_sites/relative.cpp
index 5503950c26d..2a8887f7cde 100644
--- a/src/core/virtual_sites/VirtualSitesRelative.cpp
+++ b/src/core/virtual_sites/relative.cpp
@@ -17,27 +17,25 @@
* along with this program. If not, see .
*/
-#include "VirtualSitesRelative.hpp"
-
+#include "config/config.hpp"
#ifdef VIRTUAL_SITES_RELATIVE
#include "BoxGeometry.hpp"
#include "Particle.hpp"
+#include "PropagationMode.hpp"
#include "cell_system/CellStructure.hpp"
#include "cells.hpp"
#include "errorhandling.hpp"
#include "forces.hpp"
-#include "integrate.hpp"
+#include "lees_edwards/lees_edwards.hpp"
#include "rotation.hpp"
-#include "system/System.hpp"
#include
#include
#include
#include
-#include "integrate.hpp"
-#include "lees_edwards/lees_edwards.hpp"
+#include
/**
* @brief Vector pointing from the real particle to the virtual site.
@@ -79,17 +77,14 @@ static Utils::Vector3d velocity(Particle const &p_ref, Particle const &p_vs) {
* @param p Virtual site.
* @return Pointer to real particle.
*/
-static Particle *get_reference_particle(Particle const &p) {
- if (!p.is_virtual()) {
- return nullptr;
- }
+static Particle *get_reference_particle(CellStructure &cell_structure,
+ Particle const &p) {
auto const &vs_rel = p.vs_relative();
if (vs_rel.to_particle_id == -1) {
runtimeErrorMsg() << "Particle with id " << p.id()
<< " is a dangling virtual site";
return nullptr;
}
- auto &cell_structure = *System::get_system().cell_structure;
auto p_ref_ptr = cell_structure.get_local_particle(vs_rel.to_particle_id);
if (!p_ref_ptr) {
runtimeErrorMsg() << "No real particle with id " << vs_rel.to_particle_id
@@ -112,33 +107,52 @@ static auto constraint_stress(Particle const &p_ref, Particle const &p_vs) {
return tensor_product(-p_vs.force(), connection_vector(p_ref, p_vs));
}
-void VirtualSitesRelative::update() const {
- auto const &system = System::get_system();
- auto const &box_geo = *system.box_geo;
- auto &cell_structure = *system.cell_structure;
+static bool is_vs_relative_trans(Particle const &p) {
+ return p.propagation() & PropagationMode::TRANS_VS_RELATIVE;
+}
+static bool is_vs_relative_rot(Particle const &p) {
+ return p.propagation() & PropagationMode::ROT_VS_RELATIVE;
+}
+
+static bool is_vs_relative(Particle const &p) {
+ return (is_vs_relative_trans(p) or is_vs_relative_rot(p));
+}
+
+void vs_relative_update_particles(CellStructure &cell_structure,
+ BoxGeometry const &box_geo) {
cell_structure.ghosts_update(Cells::DATA_PART_POSITION |
Cells::DATA_PART_MOMENTUM);
auto const particles = cell_structure.local_particles();
for (auto &p : particles) {
- auto const *p_ref_ptr = get_reference_particle(p);
+ if (!is_vs_relative(p)) {
+ continue;
+ }
+
+ auto const *p_ref_ptr = get_reference_particle(cell_structure, p);
if (!p_ref_ptr)
continue;
auto const &p_ref = *p_ref_ptr;
- p.image_box() = p_ref.image_box();
- p.pos() = p_ref.pos() + connection_vector(p_ref, p);
- p.v() = velocity(p_ref, p);
-
- if (box_geo.type() == BoxType::LEES_EDWARDS) {
- auto push = LeesEdwards::Push(box_geo);
- push(p, -1); // includes a position fold
- } else {
- box_geo.fold_position(p.pos(), p.image_box());
+
+ // position update
+ if (is_vs_relative_trans(p)) {
+ p.image_box() = p_ref.image_box();
+ p.pos() = p_ref.pos() + connection_vector(p_ref, p);
+ p.v() = velocity(p_ref, p);
+
+ if (box_geo.type() == BoxType::LEES_EDWARDS) {
+ auto push = LeesEdwards::Push(box_geo);
+ push(p, -1); // includes a position fold
+ } else {
+ box_geo.fold_position(p.pos(), p.image_box());
+ }
}
- if (have_quaternions())
+ // Orientation update
+ if (is_vs_relative_rot(p)) {
p.quat() = p_ref.quat() * p.vs_relative().quat;
+ }
}
if (cell_structure.check_resort_required()) {
@@ -148,36 +162,43 @@ void VirtualSitesRelative::update() const {
// Distribute forces that have accumulated on virtual particles to the
// associated real particles
-void VirtualSitesRelative::back_transfer_forces_and_torques() const {
- auto &cell_structure = *System::get_system().cell_structure;
+void vs_relative_back_transfer_forces_and_torques(
+ CellStructure &cell_structure) {
cell_structure.ghosts_reduce_forces();
init_forces_ghosts(cell_structure.ghost_particles());
// Iterate over all the particles in the local cells
for (auto &p : cell_structure.local_particles()) {
- auto *p_ref_ptr = get_reference_particle(p);
- if (!p_ref_ptr)
+ if (!is_vs_relative(p))
continue;
- // Add forces and torques
+ auto *p_ref_ptr = get_reference_particle(cell_structure, p);
+ assert(p_ref_ptr != nullptr);
+
auto &p_ref = *p_ref_ptr;
- p_ref.force() += p.force();
- p_ref.torque() +=
- vector_product(connection_vector(p_ref, p), p.force()) + p.torque();
+ if (is_vs_relative_trans(p)) {
+ p_ref.force() += p.force();
+ p_ref.torque() += vector_product(connection_vector(p_ref, p), p.force());
+ }
+
+ if (is_vs_relative_rot(p)) {
+ p_ref.torque() += p.torque();
+ }
}
}
// Rigid body contribution to scalar pressure and pressure tensor
-Utils::Matrix VirtualSitesRelative::pressure_tensor() const {
- auto const &cell_structure = *System::get_system().cell_structure;
-
+Utils::Matrix
+vs_relative_pressure_tensor(CellStructure const &cell_structure) {
Utils::Matrix pressure_tensor = {};
for (auto const &p : cell_structure.local_particles()) {
- auto const *p_ref_ptr = get_reference_particle(p);
- if (p_ref_ptr) {
- pressure_tensor += constraint_stress(*p_ref_ptr, p);
+ if (is_vs_relative_trans(p)) {
+ if (auto const *p_ref_ptr = cell_structure.get_local_particle(
+ p.vs_relative().to_particle_id)) {
+ pressure_tensor += constraint_stress(*p_ref_ptr, p);
+ }
}
}
diff --git a/src/core/virtual_sites/VirtualSitesRelative.hpp b/src/core/virtual_sites/relative.hpp
similarity index 59%
rename from src/core/virtual_sites/VirtualSitesRelative.hpp
rename to src/core/virtual_sites/relative.hpp
index e7050f9c348..c0a655d23a6 100644
--- a/src/core/virtual_sites/VirtualSitesRelative.hpp
+++ b/src/core/virtual_sites/relative.hpp
@@ -17,29 +17,24 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef VIRTUAL_SITES_VIRTUAL_SITES_RELATIVE_HPP
-#define VIRTUAL_SITES_VIRTUAL_SITES_RELATIVE_HPP
+
+#pragma once
#include "config/config.hpp"
+
#ifdef VIRTUAL_SITES_RELATIVE
-#include "VirtualSites.hpp"
+#include "BoxGeometry.hpp"
+#include "cell_system/CellStructure.hpp"
#include
#include
-/** @brief Virtual sites implementation for rigid bodies */
-class VirtualSitesRelative : public VirtualSites {
-public:
- VirtualSitesRelative() = default;
- /** @copydoc VirtualSites::update */
- void update() const override;
- /** @copydoc VirtualSites::back_transfer_forces_and_torques */
- void back_transfer_forces_and_torques() const override;
- /** @copydoc VirtualSites::pressure_tensor */
- Utils::Matrix pressure_tensor() const override;
-};
-
-#endif
+void vs_relative_update_particles(CellStructure &cell_structure,
+ BoxGeometry const &box_geo);
+void vs_relative_back_transfer_forces_and_torques(
+ CellStructure &cell_structure);
+Utils::Matrix
+vs_relative_pressure_tensor(CellStructure const &cell_structure);
-#endif
+#endif // VIRTUAL_SITES_RELATIVE
diff --git a/src/python/espressomd/particle_data.py b/src/python/espressomd/particle_data.py
index d9b72500f1d..bde3cb5218a 100644
--- a/src/python/espressomd/particle_data.py
+++ b/src/python/espressomd/particle_data.py
@@ -26,6 +26,7 @@
from .utils import check_type_or_throw_except
from .code_features import assert_features, has_features
from .script_interface import script_interface_register, ScriptInterfaceHelper
+from .propagation import Propagation
import itertools
@@ -368,12 +369,15 @@ class ParticleHandle(ScriptInterfaceHelper):
delete_bond : Delete an unverified bond held by the particle.
bonds : ``Particle`` property containing a list of all current bonds held by ``Particle``.
+ is_virtual()
+ Whether the particle is a virtual site.
+
"""
_so_name = "Particles::ParticleHandle"
_so_creation_policy = "GLOBAL"
_so_bind_methods = (
- "delete_all_bonds",
+ "delete_all_bonds", "is_virtual",
)
# here we must redefine the script interface setters
@@ -383,6 +387,8 @@ def set_params(self, **kwargs):
self.set_parameter(name, value)
def set_parameter(self, name, value):
+ if name == "propagation":
+ value = int(value)
return self.call_method("set_param_parallel", name=name, value=value)
def remove(self):
@@ -419,6 +425,7 @@ def to_dict(self):
del pdict[k]
if has_features("EXCLUSIONS"):
pdict["exclusions"] = self.exclusions
+ pdict["propagation"] = self.propagation
pdict["bonds"] = self.bonds
return pdict
@@ -552,7 +559,16 @@ def exclusions(self, p_ids):
assert_features("EXCLUSIONS")
self.call_method("set_exclusions", p_ids=p_ids)
- def vs_auto_relate_to(self, rel_to):
+ @property
+ def propagation(self):
+ return Propagation(self.get_parameter("propagation"))
+
+ @propagation.setter
+ def propagation(self, value):
+ self.set_parameter("propagation", int(value))
+
+ def vs_auto_relate_to(self, rel_to, override_cutoff_check=False,
+ couple_to_lb=False, couple_to_langevin=False):
"""
Setup this particle as virtual site relative to the particle
in argument ``rel_to``. A particle cannot relate to itself.
@@ -561,6 +577,16 @@ def vs_auto_relate_to(self, rel_to):
-----------
rel_to : :obj:`int` or :obj:`ParticleHandle`
Particle to relate to (either particle id or particle object).
+ override_cutoff_check : :obj:`bool`
+ If True, does not check whether the cell system cutoffs
+ are consistent with the distance between virtual and
+ non-virtual particles.
+ couple_to_lb : :obj:`bool`
+ If True, the virtual site is coupled to LB friction and noise.
+ couple_to_langevin : :obj:`bool`
+ If True, the virtual site is coupled to Langevin friction
+ and noise. If ``couple_to_lb`` is also True, propagate LB's
+ equations of motion and Langevin's equations of rotation.
"""
if isinstance(rel_to, ParticleHandle):
@@ -568,8 +594,19 @@ def vs_auto_relate_to(self, rel_to):
else:
check_type_or_throw_except(
rel_to, 1, int, "Argument of 'vs_auto_relate_to' has to be of type ParticleHandle or int")
- self.call_method("vs_relate_to", pid=rel_to)
+ self.call_method(
+ "vs_relate_to",
+ pid=rel_to,
+ override_cutoff_check=override_cutoff_check)
handle_errors("vs_auto_relate_to")
+ if self.propagation != Propagation.NONE:
+ if couple_to_lb:
+ self.propagation |= Propagation.TRANS_LB_MOMENTUM_EXCHANGE
+ if couple_to_langevin:
+ if not couple_to_lb:
+ self.propagation |= Propagation.ROT_LANGEVIN | Propagation.TRANS_LANGEVIN
+ else:
+ self.propagation |= Propagation.ROT_LANGEVIN
def add_verified_bond(self, bond):
"""
@@ -1088,6 +1125,8 @@ def add(self, *args, **kwargs):
def _place_new_particle(self, p_dict):
bonds = []
+ if "propagation" in p_dict:
+ p_dict["propagation"] = int(p_dict["propagation"])
if "bonds" in p_dict:
bonds = p_dict.pop("bonds")
if nesting_level(bonds) == 1:
diff --git a/src/python/espressomd/propagation.py b/src/python/espressomd/propagation.py
new file mode 100644
index 00000000000..22bbf3cfe4b
--- /dev/null
+++ b/src/python/espressomd/propagation.py
@@ -0,0 +1,60 @@
+#
+# Copyright (C) 2023 The ESPResSo project
+#
+# This file is part of ESPResSo.
+#
+# ESPResSo is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# ESPResSo is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+
+import enum
+
+
+class Propagation(enum.IntFlag):
+ """
+ Flags for propagation modes. Use the pipe operator to combine multiple
+ propagation modes. Not all combinations of propagation modes are allowed.
+ Flags for virtual sites are special and instruct the propagator to skip
+ integration; virtual sites can still be coupled to thermostats (Langevin,
+ lattice-Boltzmann) to apply friction and noise to their forces and torques.
+ """
+ NONE = 0
+ """No propagation."""
+ SYSTEM_DEFAULT = 2**0
+ """Use the system default propagator for motion and rotation."""
+ TRANS_NEWTON = 2**1
+ """Velocity-Verlet algorithm that integrates Newton's equations of motion."""
+ TRANS_LANGEVIN = 2**2
+ """Velocity-Verlet algorithm that integrates Langevin's equations of motion."""
+ TRANS_LANGEVIN_NPT = 2**3
+ """Velocity-Verlet algorithm that integrates Langevin's equations of motion coupled to a piston."""
+ TRANS_VS_RELATIVE = 2**4
+ """Algorithm for virtual sites relative motion."""
+ TRANS_LB_MOMENTUM_EXCHANGE = 2**5
+ """Algorithm for momentum exchange between particles and LB fluid cells."""
+ TRANS_LB_TRACER = 2**6
+ """Algorithm for LB inertialess tracers advection."""
+ TRANS_BROWNIAN = 2**7
+ """Euler algorithm that integrates Brownian's equations of motion."""
+ TRANS_STOKESIAN = 2**8
+ """Euler algorithm that integrates Stoke's equations of motion."""
+ ROT_EULER = 2**10
+ """Velocity-Verlet algorithm that integrates Euler's equations of rotation."""
+ ROT_LANGEVIN = 2**11
+ """Velocity-Verlet algorithm that integrates Langevin's equations of rotation."""
+ ROT_VS_RELATIVE = 2**12
+ """Algorithm for virtual sites relative rotation."""
+ ROT_BROWNIAN = 2**13
+ """Euler algorithm that integrates Brownian's equations of rotation."""
+ ROT_STOKESIAN = 2**14
+ """Euler algorithm that integrates Stokes' equations of rotation."""
diff --git a/src/python/espressomd/swimmer_helpers.py b/src/python/espressomd/swimmer_helpers.py
index 1585f8c5079..efd042f09e3 100644
--- a/src/python/espressomd/swimmer_helpers.py
+++ b/src/python/espressomd/swimmer_helpers.py
@@ -20,8 +20,6 @@
import numpy as np
from . import code_features
from .math import calc_quaternions_from_angles
-if code_features.has_features("VIRTUAL_SITES_RELATIVE"):
- from .virtual_sites import VirtualSitesRelative
def add_dipole_particle(system, particle, dipole_length,
@@ -66,19 +64,13 @@ def add_dipole_particle(system, particle, dipole_length,
raise ValueError("'dipole_length' must be >= 0.")
code_features.assert_features(["ENGINE", "VIRTUAL_SITES_RELATIVE"])
- if not isinstance(system.virtual_sites, VirtualSitesRelative):
- raise RuntimeError(
- "system.virtual_sites must be espressomd.virtual_sites.VirtualSitesRelative.")
- if not system.virtual_sites.have_quaternion:
- raise RuntimeError(
- "system.virtual_sites must have quaternion option turned on ('have_quaternion = True').")
p = system.part.add(
pos=particle.pos + dip_sign * dipole_length * particle.director,
- virtual=True, type=dipole_particle_type, swimming={
+ type=dipole_particle_type, swimming={
"f_swim": particle.swimming["f_swim"],
"is_engine_force_on_fluid": True,
})
- p.vs_auto_relate_to(particle)
+ p.vs_auto_relate_to(particle, couple_to_lb=True)
p.vs_quat = calc_quaternions_from_angles(np.pi, 0.)
return p
diff --git a/src/python/espressomd/system.py b/src/python/espressomd/system.py
index 43d06407342..1fd8802017c 100644
--- a/src/python/espressomd/system.py
+++ b/src/python/espressomd/system.py
@@ -36,7 +36,6 @@
from . import lees_edwards
from . import particle_data
from . import thermostat
-from . import virtual_sites
from .code_features import has_features, assert_features
from . import utils
@@ -178,8 +177,6 @@ def __init__(self, **kwargs):
setable_properties = ["box_l", "min_global_cut", "periodicity", "time",
"time_step", "force_cap", "max_oif_objects"]
- if has_features("VIRTUAL_SITES"):
- setable_properties.append("_active_virtual_sites_handle")
self.call_method("set_system_handle", obj=_System(**kwargs))
self.integrator = integrate.IntegratorHandle()
@@ -215,9 +212,6 @@ def __init__(self, **kwargs):
self.non_bonded_inter = interactions.NonBondedInteractions()
self.part = particle_data.ParticleList()
self.thermostat = thermostat.Thermostat()
- if has_features("VIRTUAL_SITES"):
- self._active_virtual_sites_handle = virtual_sites.ActiveVirtualSitesHandle(
- implementation=virtual_sites.VirtualSitesOff())
# lock class
self.call_method("lock_system_creation")
@@ -242,11 +236,8 @@ def _restore_object(cls, so_callback, so_callback_args, state):
return so
def __getstate__(self):
- checkpointable_properties = []
- if has_features("VIRTUAL_SITES"):
- checkpointable_properties.append("_active_virtual_sites_handle")
- checkpointable_properties += [
- "non_bonded_inter", "bonded_inter", "lees_edwards",
+ checkpointable_properties = [
+ "non_bonded_inter", "bonded_inter",
"part", "auto_update_accumulators",
"constraints",
]
@@ -340,24 +331,6 @@ def max_cut_bonded(self):
"""
return self.cell_system.max_cut_bonded
- @property
- def virtual_sites(self):
- """
- Set the virtual site implementation.
-
- Requires feature ``VIRTUAL_SITES``.
-
- Type: :obj:`espressomd.virtual_sites.ActiveVirtualSitesHandle`
-
- """
- assert_features("VIRTUAL_SITES")
- return self._active_virtual_sites_handle.implementation
-
- @virtual_sites.setter
- def virtual_sites(self, value):
- assert_features("VIRTUAL_SITES")
- self._active_virtual_sites_handle.implementation = value
-
@property
def lb(self):
"""
diff --git a/src/python/espressomd/thermostat.pxd b/src/python/espressomd/thermostat.pxd
index ccaceaacfc5..5830f75948a 100644
--- a/src/python/espressomd/thermostat.pxd
+++ b/src/python/espressomd/thermostat.pxd
@@ -25,7 +25,6 @@ from .utils cimport Vector3d
cdef extern from "thermostat.hpp":
double temperature
int thermo_switch
- cbool thermo_virtual
int THERMO_OFF
int THERMO_LANGEVIN
int THERMO_LB
@@ -103,7 +102,6 @@ cdef extern from "thermostat.hpp":
void mpi_set_langevin_gamma(const double & gamma)
void mpi_set_langevin_gamma_rot(const double & gamma)
- void mpi_set_thermo_virtual(cbool thermo_virtual)
void mpi_set_temperature(double temperature)
void mpi_set_thermo_switch(int thermo_switch)
diff --git a/src/python/espressomd/thermostat.pyx b/src/python/espressomd/thermostat.pyx
index 80baef72f8b..ae269c4d38e 100644
--- a/src/python/espressomd/thermostat.pyx
+++ b/src/python/espressomd/thermostat.pyx
@@ -32,7 +32,7 @@ def AssertThermostatType(*allowed_thermostats):
cdef class Thermostat:
@AssertThermostatType(THERMO_LANGEVIN, THERMO_DPD)
def set_langevin(self, kT=None, gamma=None, gamma_rotation=None,
- act_on_virtual=False, seed=None):
+ seed=None):
...
This will prefix an assertion that prevents setting up the Langevin
@@ -104,7 +104,6 @@ cdef class Thermostat:
if thmst["type"] == "LANGEVIN":
self.set_langevin(kT=thmst["kT"], gamma=thmst["gamma"],
gamma_rotation=thmst["gamma_rotation"],
- act_on_virtual=thmst["act_on_virtual"],
seed=thmst["seed"])
langevin_set_rng_counter(thmst["counter"])
if thmst["type"] == "LB":
@@ -117,7 +116,6 @@ cdef class Thermostat:
thmst["LB_fluid"].call_method("activate")
self.set_lb(
LB_fluid=thmst["LB_fluid"],
- act_on_virtual=thmst["act_on_virtual"],
gamma=thmst["gamma"],
seed=thmst["rng_counter_fluid"])
thmst["LB_fluid"].call_method("deactivate")
@@ -133,7 +131,6 @@ cdef class Thermostat:
if thmst["type"] == "BROWNIAN":
self.set_brownian(kT=thmst["kT"], gamma=thmst["gamma"],
gamma_rotation=thmst["gamma_rotation"],
- act_on_virtual=thmst["act_on_virtual"],
seed=thmst["seed"])
brownian_set_rng_counter(thmst["counter"])
if thmst["type"] == "SD":
@@ -155,7 +152,6 @@ cdef class Thermostat:
lang_dict = {}
lang_dict["type"] = "LANGEVIN"
lang_dict["kT"] = temperature
- lang_dict["act_on_virtual"] = thermo_virtual
lang_dict["seed"] = langevin.rng_seed()
lang_dict["counter"] = langevin.rng_counter()
IF PARTICLE_ANISOTROPY:
@@ -179,7 +175,6 @@ cdef class Thermostat:
lang_dict = {}
lang_dict["type"] = "BROWNIAN"
lang_dict["kT"] = temperature
- lang_dict["act_on_virtual"] = thermo_virtual
lang_dict["seed"] = brownian.rng_seed()
lang_dict["counter"] = brownian.rng_counter()
IF PARTICLE_ANISOTROPY:
@@ -204,7 +199,6 @@ cdef class Thermostat:
lb_dict["LB_fluid"] = self._LB_fluid
lb_dict["gamma"] = lb_lbcoupling_get_gamma()
lb_dict["type"] = "LB"
- lb_dict["act_on_virtual"] = thermo_virtual
lb_dict["rng_counter_fluid"] = lb_lbcoupling_get_rng_state()
thermo_list.append(lb_dict)
if thermo_switch & THERMO_NPT_ISO:
@@ -246,7 +240,6 @@ cdef class Thermostat:
"""
self._set_temperature(0.)
- mpi_set_thermo_virtual(True)
IF PARTICLE_ANISOTROPY:
mpi_set_langevin_gamma(utils.make_Vector3d((0., 0., 0.)))
mpi_set_brownian_gamma(utils.make_Vector3d((0., 0., 0.)))
@@ -264,9 +257,8 @@ cdef class Thermostat:
lb_lbcoupling_set_gamma(0.0)
mpi_bcast_lb_particle_coupling()
- @AssertThermostatType(THERMO_LANGEVIN, THERMO_DPD)
- def set_langevin(self, kT, gamma, gamma_rotation=None,
- act_on_virtual=False, seed=None):
+ @AssertThermostatType(THERMO_LANGEVIN, THERMO_DPD, THERMO_LB)
+ def set_langevin(self, kT, gamma, gamma_rotation=None, seed=None):
"""
Sets the Langevin thermostat.
@@ -283,9 +275,6 @@ cdef class Thermostat:
The same applies to ``gamma_rotation``, which requires the feature
``ROTATION`` to work properly. But also accepts three floats
if ``PARTICLE_ANISOTROPY`` is also compiled in.
- act_on_virtual : :obj:`bool`, optional
- If ``True`` the thermostat will act on virtual sites, default is
- ``False``.
seed : :obj:`int`
Initial counter value (or seed) of the philox RNG.
Required on first activation of the Langevin thermostat.
@@ -390,11 +379,8 @@ cdef class Thermostat:
IF ROTATION:
mpi_set_langevin_gamma_rot(gamma_rotation)
- mpi_set_thermo_virtual(act_on_virtual)
-
@AssertThermostatType(THERMO_BROWNIAN)
- def set_brownian(self, kT, gamma, gamma_rotation=None,
- act_on_virtual=False, seed=None):
+ def set_brownian(self, kT, gamma, gamma_rotation=None, seed=None):
"""Sets the Brownian thermostat.
Parameters
@@ -410,9 +396,6 @@ cdef class Thermostat:
The same applies to ``gamma_rotation``, which requires the feature
``ROTATION`` to work properly. But also accepts three floats
if ``PARTICLE_ANISOTROPY`` is also compiled in.
- act_on_virtual : :obj:`bool`, optional
- If ``True`` the thermostat will act on virtual sites, default is
- ``False``.
seed : :obj:`int`
Initial counter value (or seed) of the philox RNG.
Required on first activation of the Brownian thermostat.
@@ -518,15 +501,8 @@ cdef class Thermostat:
IF ROTATION:
mpi_set_brownian_gamma_rot(gamma_rotation)
- mpi_set_thermo_virtual(act_on_virtual)
-
- @AssertThermostatType(THERMO_LB, THERMO_DPD)
- def set_lb(
- self,
- seed=None,
- act_on_virtual=True,
- LB_fluid=None,
- gamma=0.0):
+ @AssertThermostatType(THERMO_LB, THERMO_DPD, THERMO_LANGEVIN)
+ def set_lb(self, seed=None, LB_fluid=None, gamma=0.0):
"""
Sets the LB thermostat.
@@ -538,8 +514,6 @@ cdef class Thermostat:
seed : :obj:`int`
Seed for the random number generator, required if kT > 0.
Must be positive.
- act_on_virtual : :obj:`bool`, optional
- If ``True`` the thermostat will act on virtual sites (default).
gamma : :obj:`float`
Frictional coupling constant for the MD particle coupling.
@@ -547,7 +521,7 @@ cdef class Thermostat:
from .lb import HydrodynamicInteraction
if not isinstance(LB_fluid, HydrodynamicInteraction):
raise ValueError(
- "The LB thermostat requires a LB / LBGPU instance as a keyword arg.")
+ "The LB thermostat requires a LB instance as a keyword arg.")
self._LB_fluid = LB_fluid
if LB_fluid.kT > 0.:
@@ -568,7 +542,6 @@ cdef class Thermostat:
global thermo_switch
mpi_set_thermo_switch(thermo_switch | THERMO_LB)
- mpi_set_thermo_virtual(act_on_virtual)
lb_lbcoupling_set_gamma(gamma)
mpi_bcast_lb_particle_coupling()
diff --git a/src/python/espressomd/virtual_sites.py b/src/python/espressomd/virtual_sites.py
deleted file mode 100644
index ab29f03448c..00000000000
--- a/src/python/espressomd/virtual_sites.py
+++ /dev/null
@@ -1,72 +0,0 @@
-#
-# Copyright (C) 2010-2022 The ESPResSo project
-#
-# This file is part of ESPResSo.
-#
-# ESPResSo is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# ESPResSo is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-#
-
-from .code_features import has_features
-from .script_interface import ScriptInterfaceHelper, script_interface_register
-
-if has_features("VIRTUAL_SITES"):
- @script_interface_register
- class ActiveVirtualSitesHandle(ScriptInterfaceHelper):
-
- """Handle for the virtual sites implementation active in the core
-
- This should not be used directly.
-
- Attributes
- ----------
- have_quaternion : :obj:`bool`
- Whether the virtual sites has a quaternion (only relevant for
- :class:`~espressomd.virtual_sites.VirtualSitesRelative`).
- override_cutoff_check : :obj:`bool`
- Whether to disable the sanity check that triggers when attempting
- to set up a virtual site too far away from the real particle in a
- MPI-parallel simulation with more than 1 core. Disabling this
- check is not recommended; it is only relevant in rare situations
- when using the :ref:`Hybrid decomposition` cell system.
-
- """
- _so_name = "VirtualSites::ActiveVirtualSitesHandle"
-
- @script_interface_register
- class VirtualSitesOff(ScriptInterfaceHelper):
-
- """Virtual sites implementation which does nothing (default)"""
- _so_name = "VirtualSites::VirtualSitesOff"
-
-
-if has_features("VIRTUAL_SITES_INERTIALESS_TRACERS"):
- @script_interface_register
- class VirtualSitesInertialessTracers(ScriptInterfaceHelper):
-
- """Virtual sites which are advected with an LB fluid without inertia.
- Forces are on them are transferred to the fluid instantly.
-
- """
- _so_name = "VirtualSites::VirtualSitesInertialessTracers"
-
-
-if has_features("VIRTUAL_SITES_RELATIVE"):
- @script_interface_register
- class VirtualSitesRelative(ScriptInterfaceHelper):
-
- """Virtual sites implementation placing virtual sites relative to other
- particles. See :ref:`Rigid arrangements of particles` for details.
-
- """
- _so_name = "VirtualSites::VirtualSitesRelative"
diff --git a/src/script_interface/CMakeLists.txt b/src/script_interface/CMakeLists.txt
index 290d6becf61..94030a06391 100644
--- a/src/script_interface/CMakeLists.txt
+++ b/src/script_interface/CMakeLists.txt
@@ -50,7 +50,6 @@ add_subdirectory(reaction_methods)
add_subdirectory(scafacos)
add_subdirectory(shapes)
add_subdirectory(system)
-add_subdirectory(virtual_sites)
add_subdirectory(walberla)
install(TARGETS espresso_script_interface
diff --git a/src/script_interface/cell_system/CellSystem.cpp b/src/script_interface/cell_system/CellSystem.cpp
index 5a88f0d1612..42388dc9bce 100644
--- a/src/script_interface/cell_system/CellSystem.cpp
+++ b/src/script_interface/cell_system/CellSystem.cpp
@@ -28,7 +28,6 @@
#include "core/cell_system/RegularDecomposition.hpp"
#include "core/cells.hpp"
#include "core/communication.hpp"
-#include "core/integrate.hpp"
#include "core/nonbonded_interactions/nonbonded_interaction_data.hpp"
#include "core/particle_node.hpp"
#include "core/system/System.hpp"
diff --git a/src/script_interface/h5md/h5md.cpp b/src/script_interface/h5md/h5md.cpp
index 2bace3a2e82..e3218ce8701 100644
--- a/src/script_interface/h5md/h5md.cpp
+++ b/src/script_interface/h5md/h5md.cpp
@@ -26,7 +26,6 @@
#include "h5md.hpp"
#include "cell_system/CellStructure.hpp"
-#include "core/integrate.hpp"
#include "core/system/System.hpp"
#include
@@ -38,12 +37,12 @@ namespace Writer {
Variant H5md::do_call_method(const std::string &name,
const VariantMap ¶meters) {
if (name == "write") {
- auto const &system = System::get_system();
- auto &cell_structure = *system.cell_structure;
- m_h5md->write(
- cell_structure.local_particles(), get_sim_time(),
- static_cast(std::round(get_sim_time() / get_time_step())),
- *system.box_geo);
+ auto const &system = ::System::get_system();
+ auto const particles = system.cell_structure->local_particles();
+ auto const sim_time = system.get_sim_time();
+ auto const time_step = system.get_time_step();
+ auto const n_steps = static_cast(std::round(sim_time / time_step));
+ m_h5md->write(particles, sim_time, n_steps, *system.box_geo);
} else if (name == "flush") {
m_h5md->flush();
} else if (name == "close") {
diff --git a/src/script_interface/initialize.cpp b/src/script_interface/initialize.cpp
index efecee634f4..a114aae664e 100644
--- a/src/script_interface/initialize.cpp
+++ b/src/script_interface/initialize.cpp
@@ -45,7 +45,6 @@
#include "reaction_methods/initialize.hpp"
#include "shapes/initialize.hpp"
#include "system/initialize.hpp"
-#include "virtual_sites/initialize.hpp"
#include "walberla/initialize.hpp"
namespace ScriptInterface {
@@ -72,7 +71,6 @@ void initialize(Utils::Factory *f) {
Profiler::initialize(f);
Shapes::initialize(f);
System::initialize(f);
- VirtualSites::initialize(f);
ReactionMethods::initialize(f);
#ifdef H5MD
Writer::initialize(f);
diff --git a/src/script_interface/integrators/BrownianDynamics.cpp b/src/script_interface/integrators/BrownianDynamics.cpp
index ecc7e7699cd..e4af62a7b2c 100644
--- a/src/script_interface/integrators/BrownianDynamics.cpp
+++ b/src/script_interface/integrators/BrownianDynamics.cpp
@@ -21,12 +21,15 @@
#include "script_interface/ScriptInterface.hpp"
-#include "core/integrate.hpp"
+#include "core/PropagationMode.hpp"
+#include "core/integrators/Propagation.hpp"
namespace ScriptInterface {
namespace Integrators {
-void BrownianDynamics::activate() { set_integ_switch(INTEG_METHOD_BD); }
+void ScriptInterface::Integrators::BrownianDynamics::activate() {
+ get_system().propagation->set_integ_switch(INTEG_METHOD_BD);
+}
} // namespace Integrators
} // namespace ScriptInterface
diff --git a/src/script_interface/integrators/IntegratorHandle.cpp b/src/script_interface/integrators/IntegratorHandle.cpp
index 59dacfe4830..3b08badf6ea 100644
--- a/src/script_interface/integrators/IntegratorHandle.cpp
+++ b/src/script_interface/integrators/IntegratorHandle.cpp
@@ -27,7 +27,8 @@
#include "VelocityVerlet.hpp"
#include "VelocityVerletIsoNPT.hpp"
-#include "core/integrate.hpp"
+#include "core/PropagationMode.hpp"
+#include "core/integrators/Propagation.hpp"
#include "core/system/System.hpp"
#include
@@ -44,8 +45,11 @@ IntegratorHandle::IntegratorHandle() {
[&]() { get_system().set_time_step(get_value(v)); });
},
[this]() { return get_system().get_time_step(); }},
- {"time", [](Variant const &v) { set_time(get_value(v)); },
- []() { return get_sim_time(); }},
+ {"time",
+ [&](Variant const &v) {
+ get_system().set_sim_time(get_value(v));
+ },
+ [this]() { return get_system().get_sim_time(); }},
{"force_cap",
[this](Variant const &v) {
get_system().set_force_cap(get_value(v));
@@ -62,7 +66,7 @@ IntegratorHandle::IntegratorHandle() {
m_instance->activate();
},
[this]() {
- switch (::integ_switch) {
+ switch (get_system().propagation->integ_switch) {
case INTEG_METHOD_STEEPEST_DESCENT:
return Variant{
std::dynamic_pointer_cast(m_instance)};
@@ -93,7 +97,8 @@ void IntegratorHandle::on_bind_system(::System::System &system) {
auto const ¶ms = *m_params;
for (auto const &key : get_parameter_insertion_order()) {
if (params.count(key) != 0ul) {
- if (not(key == "time_step" and ::integ_switch == INTEG_METHOD_NVT and
+ if (not(key == "time_step" and
+ system.propagation->integ_switch == INTEG_METHOD_NVT and
system.get_time_step() == -1. and
is_type(params.at(key)) and
get_value(is_type(params.at(key))) == -1.)) {
diff --git a/src/script_interface/integrators/SteepestDescent.cpp b/src/script_interface/integrators/SteepestDescent.cpp
index c3a4b1bb02a..5b8f7ddce13 100644
--- a/src/script_interface/integrators/SteepestDescent.cpp
+++ b/src/script_interface/integrators/SteepestDescent.cpp
@@ -21,7 +21,9 @@
#include "script_interface/ScriptInterface.hpp"
+#include "core/PropagationMode.hpp"
#include "core/integrate.hpp"
+#include "core/integrators/Propagation.hpp"
#include "core/integrators/steepest_descent.hpp"
#include
@@ -69,7 +71,7 @@ void SteepestDescent::do_construct(VariantMap const ¶ms) {
void SteepestDescent::activate() {
register_integrator(get_instance());
- set_integ_switch(INTEG_METHOD_STEEPEST_DESCENT);
+ get_system().propagation->set_integ_switch(INTEG_METHOD_STEEPEST_DESCENT);
}
} // namespace Integrators
diff --git a/src/script_interface/integrators/StokesianDynamics.cpp b/src/script_interface/integrators/StokesianDynamics.cpp
index 54e7ab8c8f5..40d0c7b7845 100644
--- a/src/script_interface/integrators/StokesianDynamics.cpp
+++ b/src/script_interface/integrators/StokesianDynamics.cpp
@@ -25,7 +25,8 @@
#include "script_interface/ScriptInterface.hpp"
-#include "core/integrate.hpp"
+#include "core/PropagationMode.hpp"
+#include "core/integrators/Propagation.hpp"
#include "core/stokesian_dynamics/sd_interface.hpp"
#include
@@ -92,7 +93,7 @@ void StokesianDynamics::do_construct(VariantMap const ¶ms) {
void StokesianDynamics::activate() {
context()->parallel_try_catch([&]() {
register_integrator(get_instance());
- set_integ_switch(INTEG_METHOD_SD);
+ get_system().propagation->set_integ_switch(INTEG_METHOD_SD);
});
}
diff --git a/src/script_interface/integrators/VelocityVerlet.cpp b/src/script_interface/integrators/VelocityVerlet.cpp
index 4d7c7e32ea7..325ea420522 100644
--- a/src/script_interface/integrators/VelocityVerlet.cpp
+++ b/src/script_interface/integrators/VelocityVerlet.cpp
@@ -21,12 +21,15 @@
#include "script_interface/ScriptInterface.hpp"
-#include "core/integrate.hpp"
+#include "core/PropagationMode.hpp"
+#include "core/integrators/Propagation.hpp"
namespace ScriptInterface {
namespace Integrators {
-void VelocityVerlet::activate() { set_integ_switch(INTEG_METHOD_NVT); }
+void VelocityVerlet::activate() {
+ get_system().propagation->set_integ_switch(INTEG_METHOD_NVT);
+}
} // namespace Integrators
} // namespace ScriptInterface
diff --git a/src/script_interface/integrators/VelocityVerletIsoNPT.cpp b/src/script_interface/integrators/VelocityVerletIsoNPT.cpp
index 8464f75247c..52c43b9faef 100644
--- a/src/script_interface/integrators/VelocityVerletIsoNPT.cpp
+++ b/src/script_interface/integrators/VelocityVerletIsoNPT.cpp
@@ -25,7 +25,8 @@
#include "script_interface/ScriptInterface.hpp"
-#include "core/integrate.hpp"
+#include "core/PropagationMode.hpp"
+#include "core/integrators/Propagation.hpp"
#include "core/npt.hpp"
#include
@@ -64,7 +65,7 @@ void VelocityVerletIsoNPT::do_construct(VariantMap const ¶ms) {
void VelocityVerletIsoNPT::activate() {
::nptiso = get_instance();
- set_integ_switch(INTEG_METHOD_NPT_ISO);
+ get_system().propagation->set_integ_switch(INTEG_METHOD_NPT_ISO);
get_system().on_thermostat_param_change();
}
diff --git a/src/script_interface/lees_edwards/LeesEdwards.hpp b/src/script_interface/lees_edwards/LeesEdwards.hpp
index 579b4493f6f..d4ddd81fb99 100644
--- a/src/script_interface/lees_edwards/LeesEdwards.hpp
+++ b/src/script_interface/lees_edwards/LeesEdwards.hpp
@@ -16,18 +16,18 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef SCRIPT_INTERFACE_LEES_EDWARDS_LEES_EDWARDS_HPP
-#define SCRIPT_INTERFACE_LEES_EDWARDS_LEES_EDWARDS_HPP
+
+#pragma once
#include "Protocol.hpp"
#include "core/BoxGeometry.hpp"
#include "core/lees_edwards/LeesEdwardsBC.hpp"
#include "core/lees_edwards/lees_edwards.hpp"
-#include "core/system/System.hpp"
#include "script_interface/ScriptInterface.hpp"
#include "script_interface/auto_parameters/AutoParameters.hpp"
+#include "script_interface/system/Leaf.hpp"
#include
#include
@@ -35,32 +35,32 @@
namespace ScriptInterface {
namespace LeesEdwards {
-class LeesEdwards : public AutoParameters {
+class LeesEdwards : public AutoParameters {
+ std::shared_ptr<::LeesEdwards::LeesEdwards> m_lees_edwards;
std::shared_ptr m_protocol;
- LeesEdwardsBC const &m_lebc;
+ std::unique_ptr m_params;
public:
- LeesEdwards()
- : m_protocol{nullptr},
- m_lebc{System::get_system().box_geo->lees_edwards_bc()} {
+ LeesEdwards() {
add_parameters(
{{"protocol",
[this](Variant const &value) {
if (is_none(value)) {
- auto const &system = System::get_system();
+ auto const &system = get_system();
context()->parallel_try_catch([&system]() {
auto constexpr invalid_dir = LeesEdwardsBC::invalid_dir;
system.lb.lebc_sanity_checks(invalid_dir, invalid_dir);
});
m_protocol = nullptr;
system.box_geo->set_lees_edwards_bc(LeesEdwardsBC{});
- ::LeesEdwards::unset_protocol();
+ m_lees_edwards->unset_protocol();
return;
}
context()->parallel_try_catch([this]() {
auto constexpr invalid_dir = LeesEdwardsBC::invalid_dir;
- if (m_lebc.shear_direction == invalid_dir or
- m_lebc.shear_plane_normal == invalid_dir) {
+ auto const &lebc = get_lebc();
+ if (lebc.shear_direction == invalid_dir or
+ lebc.shear_plane_normal == invalid_dir) {
throw std::runtime_error(
"Parameters 'shear_plane_normal' and 'shear_direction' "
"must be initialized together with 'protocol' on first "
@@ -68,7 +68,7 @@ class LeesEdwards : public AutoParameters {
}
});
m_protocol = get_value>(value);
- ::LeesEdwards::set_protocol(m_protocol->protocol());
+ m_lees_edwards->set_protocol(m_protocol->protocol());
},
[this]() {
if (m_protocol)
@@ -76,13 +76,13 @@ class LeesEdwards : public AutoParameters {
return make_variant(none);
}},
{"shear_velocity", AutoParameter::read_only,
- [this]() { return m_lebc.shear_velocity; }},
+ [this]() { return get_lebc().shear_velocity; }},
{"pos_offset", AutoParameter::read_only,
- [this]() { return m_lebc.pos_offset; }},
+ [this]() { return get_lebc().pos_offset; }},
{"shear_direction", AutoParameter::read_only,
- [this]() { return get_shear_name(m_lebc.shear_direction); }},
+ [this]() { return get_shear_name(get_lebc().shear_direction); }},
{"shear_plane_normal", AutoParameter::read_only,
- [this]() { return get_shear_name(m_lebc.shear_plane_normal); }}});
+ [this]() { return get_shear_name(get_lebc().shear_plane_normal); }}});
}
Variant do_call_method(std::string const &name,
@@ -103,21 +103,19 @@ class LeesEdwards : public AutoParameters {
throw std::invalid_argument("Parameters 'shear_direction' and "
"'shear_plane_normal' must differ");
}
- auto const &system = System::get_system();
+ auto const &system = get_system();
system.lb.lebc_sanity_checks(shear_direction, shear_plane_normal);
// update box geometry and cell structure
system.box_geo->set_lees_edwards_bc(
LeesEdwardsBC{0., 0., shear_direction, shear_plane_normal});
- ::LeesEdwards::set_protocol(m_protocol->protocol());
+ m_lees_edwards->set_protocol(m_protocol->protocol());
});
}
return {};
}
void do_construct(VariantMap const ¶ms) override {
- if (not params.empty()) {
- do_call_method("set_boundary_conditions", params);
- }
+ m_params = std::make_unique(params);
}
private:
@@ -147,9 +145,20 @@ class LeesEdwards : public AutoParameters {
}
return {none};
}
+
+ LeesEdwardsBC const &get_lebc() const {
+ return get_system().box_geo->lees_edwards_bc();
+ }
+
+ void on_bind_system(::System::System &system) override {
+ m_lees_edwards = system.lees_edwards;
+ auto const ¶ms = *m_params;
+ if (not params.empty()) {
+ do_call_method("set_boundary_conditions", params);
+ }
+ m_params.reset();
+ }
};
} // namespace LeesEdwards
} // namespace ScriptInterface
-
-#endif
diff --git a/src/script_interface/lees_edwards/LinearShear.hpp b/src/script_interface/lees_edwards/LinearShear.hpp
index 270a84dcc33..ceaa279da8d 100644
--- a/src/script_interface/lees_edwards/LinearShear.hpp
+++ b/src/script_interface/lees_edwards/LinearShear.hpp
@@ -16,34 +16,32 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef SCRIPT_INTERFACE_LEES_EDWARDS_LINEAR_SHEAR_HPP
-#define SCRIPT_INTERFACE_LEES_EDWARDS_LINEAR_SHEAR_HPP
+
+#pragma once
#include "core/lees_edwards/lees_edwards.hpp"
#include "script_interface/ScriptInterface.hpp"
#include "script_interface/auto_parameters/AutoParameters.hpp"
-#include
-
#include
+#include
namespace ScriptInterface {
namespace LeesEdwards {
class LinearShear : public Protocol {
+ using CoreClass = ::LeesEdwards::LinearShear;
+
public:
LinearShear()
- : m_protocol{std::make_shared<::LeesEdwards::ActiveProtocol>(
- ::LeesEdwards::LinearShear())} {
+ : m_protocol{
+ std::make_shared<::LeesEdwards::ActiveProtocol>(CoreClass())} {
add_parameters(
{{"initial_pos_offset",
- boost::get<::LeesEdwards::LinearShear>(*m_protocol)
- .m_initial_pos_offset},
- {"shear_velocity",
- boost::get<::LeesEdwards::LinearShear>(*m_protocol).m_shear_velocity},
- {"time_0",
- boost::get<::LeesEdwards::LinearShear>(*m_protocol).m_time_0}});
+ std::get(*m_protocol).m_initial_pos_offset},
+ {"shear_velocity", std::get(*m_protocol).m_shear_velocity},
+ {"time_0", std::get(*m_protocol).m_time_0}});
}
std::shared_ptr<::LeesEdwards::ActiveProtocol> protocol() override {
return m_protocol;
@@ -55,5 +53,3 @@ class LinearShear : public Protocol {
} // namespace LeesEdwards
} // namespace ScriptInterface
-
-#endif
diff --git a/src/script_interface/lees_edwards/Off.hpp b/src/script_interface/lees_edwards/Off.hpp
index d5de550e72d..8d77b60bb26 100644
--- a/src/script_interface/lees_edwards/Off.hpp
+++ b/src/script_interface/lees_edwards/Off.hpp
@@ -16,8 +16,8 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef SCRIPT_INTERFACE_LEES_EDWARDS_OFF_HPP
-#define SCRIPT_INTERFACE_LEES_EDWARDS_OFF_HPP
+
+#pragma once
#include "core/lees_edwards/lees_edwards.hpp"
@@ -44,5 +44,3 @@ class Off : public Protocol {
} // namespace LeesEdwards
} // namespace ScriptInterface
-
-#endif
diff --git a/src/script_interface/lees_edwards/OscillatoryShear.hpp b/src/script_interface/lees_edwards/OscillatoryShear.hpp
index 7bf9923b193..d860c6c77cd 100644
--- a/src/script_interface/lees_edwards/OscillatoryShear.hpp
+++ b/src/script_interface/lees_edwards/OscillatoryShear.hpp
@@ -16,36 +16,32 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef SCRIPT_INTERFACE_LEES_EDWARDS_OSCILLATORY_SHEAR_HPP
-#define SCRIPT_INTERFACE_LEES_EDWARDS_OSCILLATORY_SHEAR_HPP
+
+#pragma once
#include "core/lees_edwards/lees_edwards.hpp"
#include "script_interface/ScriptInterface.hpp"
#include "script_interface/auto_parameters/AutoParameters.hpp"
-#include
-
#include
+#include
namespace ScriptInterface {
namespace LeesEdwards {
class OscillatoryShear : public Protocol {
+ using CoreClass = ::LeesEdwards::OscillatoryShear;
+
public:
OscillatoryShear()
- : m_protocol{std::make_shared<::LeesEdwards::ActiveProtocol>(
- ::LeesEdwards::OscillatoryShear())} {
- add_parameters(
- {{"initial_pos_offset",
- boost::get<::LeesEdwards::OscillatoryShear>(*m_protocol)
- .m_initial_pos_offset},
- {"amplitude",
- boost::get<::LeesEdwards::OscillatoryShear>(*m_protocol).m_amplitude},
- {"omega",
- boost::get<::LeesEdwards::OscillatoryShear>(*m_protocol).m_omega},
- {"time_0",
- boost::get<::LeesEdwards::OscillatoryShear>(*m_protocol).m_time_0}});
+ : m_protocol{
+ std::make_shared<::LeesEdwards::ActiveProtocol>(CoreClass())} {
+ add_parameters({{"initial_pos_offset",
+ std::get(*m_protocol).m_initial_pos_offset},
+ {"amplitude", std::get(*m_protocol).m_amplitude},
+ {"omega", std::get(*m_protocol).m_omega},
+ {"time_0", std::get(*m_protocol).m_time_0}});
}
std::shared_ptr<::LeesEdwards::ActiveProtocol> protocol() override {
return m_protocol;
@@ -57,5 +53,3 @@ class OscillatoryShear : public Protocol {
} // namespace LeesEdwards
} // namespace ScriptInterface
-
-#endif
diff --git a/src/script_interface/lees_edwards/Protocol.hpp b/src/script_interface/lees_edwards/Protocol.hpp
index faa09aa5a9e..e4af81552c9 100644
--- a/src/script_interface/lees_edwards/Protocol.hpp
+++ b/src/script_interface/lees_edwards/Protocol.hpp
@@ -16,8 +16,8 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef SCRIPT_INTERFACE_LEES_EDWARDS_PROTOCOL_HPP
-#define SCRIPT_INTERFACE_LEES_EDWARDS_PROTOCOL_HPP
+
+#pragma once
#include "core/lees_edwards/lees_edwards.hpp"
@@ -36,5 +36,3 @@ class Protocol : public AutoParameters {
} // namespace LeesEdwards
} // namespace ScriptInterface
-
-#endif
diff --git a/src/script_interface/particle_data/ParticleHandle.cpp b/src/script_interface/particle_data/ParticleHandle.cpp
index edb4156a77b..fab4cb40323 100644
--- a/src/script_interface/particle_data/ParticleHandle.cpp
+++ b/src/script_interface/particle_data/ParticleHandle.cpp
@@ -25,11 +25,13 @@
#include "script_interface/get_value.hpp"
#include "core/BoxGeometry.hpp"
+#include "core/PropagationMode.hpp"
#include "core/bonded_interactions/bonded_interaction_data.hpp"
#include "core/cell_system/CellStructure.hpp"
#include "core/exclusions.hpp"
#include "core/nonbonded_interactions/nonbonded_interaction_data.hpp"
#include "core/particle_node.hpp"
+#include "core/propagation.hpp"
#include "core/rotation.hpp"
#include "core/system/System.hpp"
#include "core/virtual_sites.hpp"
@@ -238,19 +240,6 @@ ParticleHandle::ParticleHandle() {
},
#endif // ELECTROSTATICS
[this]() { return get_particle_data(m_pid).q(); }},
- {"virtual",
-#ifdef VIRTUAL_SITES
- [this](Variant const &value) {
- set_particle_property(&Particle::virtual_flag, value);
- },
-#else // VIRTUAL_SITES
- [](Variant const &value) {
- if (get_value(value)) {
- throw std::runtime_error("Feature VIRTUAL_SITES not compiled in");
- }
- },
-#endif // VIRTUAL_SITES
- [this]() { return get_particle_data(m_pid).is_virtual(); }},
#ifdef ROTATION
{"director",
[this](Variant const &value) {
@@ -375,6 +364,17 @@ ParticleHandle::ParticleHandle() {
[this]() { return get_particle_data(m_pid).ext_torque(); }},
#endif // ROTATION
#endif // EXTERNAL_FORCES
+ {"propagation",
+ [this](Variant const &value) {
+ auto const propagation = get_value(value);
+ if (!is_valid_propagation_combination(propagation)) {
+ throw std::domain_error(error_msg(
+ "propagation", "propagation combination not accepted: " +
+ propagation_bitmask_to_string(propagation)));
+ }
+ set_particle_property(&Particle::propagation, value);
+ },
+ [this]() { return get_particle_data(m_pid).propagation(); }},
#ifdef THERMOSTAT_PER_PARTICLE
{"gamma",
[this](Variant const &value) {
@@ -396,7 +396,6 @@ ParticleHandle::ParticleHandle() {
auto const &box_geo = *System::get_system().box_geo;
return box_geo.folded_position(get_particle_data(m_pid).pos());
}},
-
{"lees_edwards_offset",
[this](Variant const &value) {
set_particle_property(&Particle::lees_edwards_offset, value);
@@ -446,8 +445,14 @@ ParticleHandle::ParticleHandle() {
throw std::invalid_argument(error_msg(
"vs_relative", "must take the form [id, distance, quaternion]"));
}
- set_particle_property(
- [&vs_relative](Particle &p) { p.vs_relative() = vs_relative; });
+ set_particle_property([&vs_relative](Particle &p) {
+ p.vs_relative() = vs_relative;
+ if (vs_relative.to_particle_id != -1) {
+ p.propagation() = PropagationMode::TRANS_VS_RELATIVE |
+ PropagationMode::ROT_VS_RELATIVE;
+ }
+ assert(is_valid_propagation_combination(p.propagation()));
+ });
},
[this]() {
auto const vs_rel = get_particle_data(m_pid).vs_relative();
@@ -587,12 +592,19 @@ Variant ParticleHandle::do_call_method(std::string const &name,
std::ignore = get_real_particle(context()->get_comm(), m_pid);
remove_particle(m_pid);
});
+ } else if (name == "is_virtual") {
+ if (not context()->is_head_node()) {
+ return {};
+ }
+ return get_particle_data(m_pid).is_virtual();
#ifdef VIRTUAL_SITES_RELATIVE
} else if (name == "vs_relate_to") {
if (not context()->is_head_node()) {
return {};
}
auto const other_pid = get_value(params, "pid");
+ auto const override_cutoff_check =
+ get_value(params, "override_cutoff_check");
if (m_pid == other_pid) {
throw std::invalid_argument("A virtual site cannot relate to itself");
}
@@ -613,10 +625,10 @@ Variant ParticleHandle::do_call_method(std::string const &name,
auto const &p_current = get_particle_data(m_pid);
auto const &p_relate_to = get_particle_data(other_pid);
auto const [quat, dist] = calculate_vs_relate_to_params(
- p_current, p_relate_to, *system.box_geo, system.get_min_global_cut());
+ p_current, p_relate_to, *system.box_geo, system.get_min_global_cut(),
+ override_cutoff_check);
set_parameter("vs_relative", Variant{std::vector{
{other_pid, dist, quat2vector(quat)}}});
- set_parameter("virtual", true);
#endif // VIRTUAL_SITES_RELATIVE
#ifdef EXCLUSIONS
} else if (name == "has_exclusion") {
@@ -763,38 +775,41 @@ void ParticleHandle::do_construct(VariantMap const ¶ms) {
// create a default-constructed particle
make_new_particle(m_pid, pos);
- context()->parallel_try_catch([&]() {
- // set particle properties (filter out read-only and deferred properties)
- std::set const skip = {
- "pos_folded", "pos", "quat", "director", "id", "lees_edwards_flag",
- "exclusions", "dip", "node", "image_box", "bonds", "__cpt_sentinel",
- };
+ try {
+ context()->parallel_try_catch([&]() {
+ /* clang-format off */
+ // set particle properties (filter out read-only and deferred properties)
+ std::set const skip = {
+ "pos_folded", "pos", "quat", "director", "id",
+ "exclusions", "dip", "node", "image_box", "bonds",
+ "lees_edwards_flag", "__cpt_sentinel",
+ };
+ /* clang-format on */
#ifdef ROTATION
- // multiple parameters can potentially set the quaternion, but only one
- // can be allowed to; these conditionals are required to handle a reload
- // from a checkpoint file, where all properties exist (avoids accidentally
- // overwriting the quaternion by the default-constructed dipole moment)
- for (std::string name : {"quat", "director", "dip"}) {
- if (has_param(name)) {
- do_set_parameter(name, params.at(name));
- break;
+ // multiple parameters can potentially set the quaternion, but only one
+ // can be allowed to; these conditionals are required to handle a reload
+ // from a checkpoint file, where all properties exist (avoids accidentally
+ // overwriting the quaternion by the default-constructed dipole moment)
+ for (std::string name : {"quat", "director", "dip"}) {
+ if (has_param(name)) {
+ do_set_parameter(name, params.at(name));
+ break;
+ }
}
- }
#endif // ROTATION
- std::vector sorted_param_names = {};
- std::for_each(params.begin(), params.end(), [&](auto const &kv) {
- if (skip.count(kv.first) == 0) {
- sorted_param_names.push_back(kv.first);
+ for (auto const &name : get_parameter_insertion_order()) {
+ if (params.count(name) != 0ul and skip.count(name) == 0ul) {
+ do_set_parameter(name, params.at(name));
+ }
+ }
+ if (not has_param("type")) {
+ do_set_parameter("type", 0);
}
});
- std::sort(sorted_param_names.begin(), sorted_param_names.end());
- for (auto const &name : sorted_param_names) {
- do_set_parameter(name, params.at(name));
- }
- if (not has_param("type")) {
- do_set_parameter("type", 0);
- }
- });
+ } catch (...) {
+ remove_particle(m_pid);
+ throw;
+ }
}
} // namespace Particles
diff --git a/src/script_interface/system/System.cpp b/src/script_interface/system/System.cpp
index d80a9840b7d..1a8f1a45225 100644
--- a/src/script_interface/system/System.cpp
+++ b/src/script_interface/system/System.cpp
@@ -31,6 +31,7 @@
#include "core/nonbonded_interactions/nonbonded_interaction_data.hpp"
#include "core/object-in-fluid/oif_global_forces.hpp"
#include "core/particle_node.hpp"
+#include "core/propagation.hpp"
#include "core/rotation.hpp"
#include "core/system/System.hpp"
#include "core/system/System.impl.hpp"
@@ -42,6 +43,7 @@
#include "script_interface/galilei/ComFixed.hpp"
#include "script_interface/galilei/Galilei.hpp"
#include "script_interface/integrators/IntegratorHandle.hpp"
+#include "script_interface/lees_edwards/LeesEdwards.hpp"
#include "script_interface/magnetostatics/Container.hpp"
#include
@@ -74,6 +76,7 @@ struct System::Leaves {
std::shared_ptr comfixed;
std::shared_ptr galilei;
std::shared_ptr bond_breakage;
+ std::shared_ptr lees_edwards;
#ifdef ELECTROSTATICS
std::shared_ptr electrostatics;
#endif
@@ -110,7 +113,7 @@ System::System() : m_instance{}, m_leaves{std::make_shared | |