From 26fd176ab8bdf02e0e92158861948626d9d8d8b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 4 Jan 2022 13:34:54 +0100 Subject: [PATCH 01/99] python: Improve type-checking functions --- src/python/espressomd/integrate.pyx | 2 +- src/python/espressomd/utils.pyx | 22 +++++++++++++--------- testsuite/python/integrator_npt.py | 10 ++++++---- testsuite/python/save_checkpoint.py | 2 +- testsuite/python/utils.py | 11 ++++++++++- 5 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/python/espressomd/integrate.pyx b/src/python/espressomd/integrate.pyx index 5a8092c897f..96d7a0db2b2 100644 --- a/src/python/espressomd/integrate.pyx +++ b/src/python/espressomd/integrate.pyx @@ -380,7 +380,7 @@ IF NPT: check_type_or_throw_except( self._params["piston"], 1, float, "piston must be a float") check_type_or_throw_except( - self._params["direction"], 3, int, "direction must be an array-like of 3 bools") + self._params["direction"], 3, bool, "direction must be an array-like of 3 bools") check_type_or_throw_except( self._params["cubic_box"], 1, int, "cubic_box must be a bool") diff --git a/src/python/espressomd/utils.pyx b/src/python/espressomd/utils.pyx index 8133615d35d..c2d9860c644 100644 --- a/src/python/espressomd/utils.pyx +++ b/src/python/espressomd/utils.pyx @@ -37,12 +37,12 @@ cpdef check_type_or_throw_except(x, n, t, msg): raise ValueError( msg + f" -- {len(x)} values were given but {n} were expected.") for i in range(len(x)): - if not isinstance(x[i], t): - if not ((t == float and is_valid_type(x[i], int)) - or (t == float and issubclass(type(x[i]), np.integer))) \ - and not (t == int and issubclass(type(x[i]), np.integer)): - raise ValueError( - msg + f" -- Item {i} was of type {type(x[i]).__name__}") + if not (isinstance(x[i], t) + or (t == float and is_valid_type(x[i], int)) + or (t == int and is_valid_type(x[i], int)) + or (t == bool and (is_valid_type(x[i], bool) or x[i] in (0, 1)))): + raise ValueError( + msg + f" -- Item {i} was of type {type(x[i]).__name__}") else: # if n>1, but the user passed a single value, also throw exception raise ValueError( @@ -50,8 +50,10 @@ cpdef check_type_or_throw_except(x, n, t, msg): else: # N=1 and a single value if not isinstance(x, t): - if not (t == float and is_valid_type(x, int)) and not ( - t == int and issubclass(type(x), np.integer)): + if not (isinstance(x, t) + or (t == float and is_valid_type(x, int)) + or (t == int and is_valid_type(x, int)) + or (t == bool and is_valid_type(x, bool))): raise ValueError(msg + f" -- Got an {type(x).__name__}") @@ -275,7 +277,7 @@ def nesting_level(obj): def is_valid_type(value, t): """ - Extended checks for numpy int and float types. + Extended checks for numpy int, float and bool types. """ if value is None: @@ -288,6 +290,8 @@ def is_valid_type(value, t): value, (float, np.float16, np.float32, np.float64, np.float128, np.longdouble)) return isinstance( value, (float, np.float16, np.float32, np.float64, np.longdouble)) + elif t == bool: + return isinstance(value, (bool, np.bool, np.bool_)) else: return isinstance(value, t) diff --git a/testsuite/python/integrator_npt.py b/testsuite/python/integrator_npt.py index b5d7e869d89..95a8828022f 100644 --- a/testsuite/python/integrator_npt.py +++ b/testsuite/python/integrator_npt.py @@ -51,10 +51,10 @@ def test_integrator_exceptions(self): self.system.integrator.set_isotropic_npt(ext_pressure=1, piston=-1) with self.assertRaises(RuntimeError): self.system.integrator.set_isotropic_npt( - ext_pressure=1, piston=1, direction=[0, 0, 0]) + ext_pressure=1, piston=1, direction=[False, False, False]) with self.assertRaises(Exception): self.system.integrator.set_isotropic_npt( - ext_pressure=1, piston=1, direction=[1, 0]) + ext_pressure=1, piston=1, direction=[True, False]) def test_integrator_recovery(self): # the system is still in a valid state after a failure @@ -151,7 +151,8 @@ def test_dp3m_exception(self): prefactor=1.0, accuracy=1e-2, mesh=3 * [36], cao=7, r_cut=1.0, alpha=2.995, tune=False) with self.assertRaisesRegex(RuntimeError, 'If magnetostatics is being used you must use the cubic box NpT'): - self.run_with_p3m(dp3m, cubic_box=False, direction=(0, 1, 1)) + self.run_with_p3m( + dp3m, cubic_box=False, direction=(False, True, True)) self.tearDown() try: self.run_with_p3m(dp3m) @@ -166,7 +167,8 @@ def test_p3m_exception(self): prefactor=1.0, accuracy=1e-2, mesh=3 * [8], cao=3, r_cut=0.36, alpha=5.35, tune=False) with self.assertRaisesRegex(RuntimeError, 'If electrostatics is being used you must use the cubic box NpT'): - self.run_with_p3m(p3m, cubic_box=False, direction=(0, 1, 1)) + self.run_with_p3m( + p3m, cubic_box=False, direction=(False, True, True)) self.tearDown() try: self.run_with_p3m(p3m) diff --git a/testsuite/python/save_checkpoint.py b/testsuite/python/save_checkpoint.py index 8f3629ec948..01db89bb88a 100644 --- a/testsuite/python/save_checkpoint.py +++ b/testsuite/python/save_checkpoint.py @@ -179,7 +179,7 @@ # set integrator if 'INT.NPT' in modes and espressomd.has_features('NPT'): system.integrator.set_isotropic_npt(ext_pressure=2.0, piston=0.01, - direction=[1, 0, 0]) + direction=[True, False, False]) elif 'INT.SD' in modes: system.integrator.set_steepest_descent(f_max=2.0, gamma=0.1, max_displacement=0.01) diff --git a/testsuite/python/utils.py b/testsuite/python/utils.py index 0a19d4b5a94..f86b94dad7f 100644 --- a/testsuite/python/utils.py +++ b/testsuite/python/utils.py @@ -37,8 +37,14 @@ def test_check_type_or_throw_except(self): with self.assertRaisesRegex( ValueError, 'D -- Item 1 was of type str'): utils.check_type_or_throw_except([1, '2', '3'], 3, float, 'D') + # the following statements should not raise any exception try: - utils.check_type_or_throw_except([1, 2, 3], 3, float, 'E') + utils.check_type_or_throw_except([1, 2, 3], 3, float, '') + utils.check_type_or_throw_except(np.array([1, 2]), 2, float, '') + utils.check_type_or_throw_except(np.array(2 * [True]), 2, bool, '') + utils.check_type_or_throw_except(np.array([1, 2])[0], 1, float, '') + utils.check_type_or_throw_except(np.array([True])[0], 1, bool, '') + utils.check_type_or_throw_except(np.array(['12'])[0], 1, str, '') except ValueError as err: self.fail(f'check_type_or_throw_except raised ValueError("{err}")') @@ -52,6 +58,9 @@ def test_is_valid_type(self): self.assertTrue(utils.is_valid_type(1.0, float)) self.assertTrue(utils.is_valid_type(12345, int)) self.assertTrue(utils.is_valid_type('123', str)) + self.assertTrue(utils.is_valid_type(np.array([123.])[0], float)) + self.assertTrue(utils.is_valid_type(np.array([1234])[0], int)) + self.assertTrue(utils.is_valid_type(np.array([True])[0], bool)) # numpy types self.assertTrue(utils.is_valid_type( np.array([12], dtype=int)[0], int)) From 08b54dc318843f9d499f59f0994c8d5ad084cb5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 4 Jan 2022 13:50:35 +0100 Subject: [PATCH 02/99] tests: Check DPD on a wall constraint --- testsuite/python/dpd.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/testsuite/python/dpd.py b/testsuite/python/dpd.py index 03911093574..0c90e04de02 100644 --- a/testsuite/python/dpd.py +++ b/testsuite/python/dpd.py @@ -23,6 +23,7 @@ import espressomd import espressomd.observables +import espressomd.constraints import espressomd.shapes @@ -40,6 +41,7 @@ def setUp(self): def tearDown(self): self.system.part.clear() + self.system.constraints.clear() self.system.thermostat.turn_off() self.system.integrator.set_vv() @@ -275,22 +277,23 @@ def f(i): def test_constraint(self): system = self.system - system.constraints.add(shape=espressomd.shapes.Wall( - dist=0, normal=[1, 0, 0]), particle_type=0, particle_velocity=[1, 2, 3]) + dpd_vel = [1., 2., 3.] + wall = espressomd.shapes.Wall(dist=1., normal=[1., 0., 0.]) + system.constraints.add(shape=wall, penetrable=True, particle_type=0, + particle_velocity=dpd_vel) system.thermostat.set_dpd(kT=0.0, seed=42) system.non_bonded_inter[0, 0].dpd.set_params( weight_function=0, gamma=1., r_cut=1.0, trans_weight_function=0, trans_gamma=1., trans_r_cut=1.0) - p = system.part.add(pos=[0.5, 0, 0], type=0, v=[0, 0, 0]) - + p1 = system.part.add(pos=[0.5, 0., 0.], type=0) + p2 = system.part.add(pos=[1.5, 0., 0.], type=0) + p3 = system.part.add(pos=[1.0, 0., 0.], type=0) system.integrator.run(0) - - np.testing.assert_array_almost_equal(np.copy(p.f), [1., 2., 3.]) - - for c in system.constraints: - system.constraints.remove(c) + np.testing.assert_array_almost_equal(np.copy(p1.f), dpd_vel) + np.testing.assert_array_almost_equal(np.copy(p2.f), dpd_vel) + np.testing.assert_array_almost_equal(np.copy(p3.f), 0.) def test_dpd_stress(self): From ff4d6bb4e87982004e4a6c721af0268a4afe04ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 4 Jan 2022 13:58:24 +0100 Subject: [PATCH 03/99] tests: Check NpT in one direction --- testsuite/python/npt_thermostat.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/testsuite/python/npt_thermostat.py b/testsuite/python/npt_thermostat.py index 89466b891b9..9f22a103d35 100644 --- a/testsuite/python/npt_thermostat.py +++ b/testsuite/python/npt_thermostat.py @@ -112,6 +112,36 @@ def reset_particle_and_box(): self.assertTrue(np.all(np.not_equal(force4, force5))) self.assertTrue(np.all(np.not_equal(boxl4, boxl5))) + def test_02__direction(self): + """Test for NpT constrained in one direction.""" + + system = self.system + + ref_force_dir = [75.3778877, 35.1267878, 21.5026612] + ref_boxl_dir = [0.7669542, 0.7809505, 0.719799] + + for n in range(3): + system.box_l = [1., 1., 1.] + system.time_step = 0.01 + vel2force = system.time_step / 2. + p = system.part.add(pos=[0., 0., 0.], v=np.roll([1., 2., 3.], n)) + direction = np.roll([True, False, False], n) + ref_boxl = np.roll([ref_boxl_dir[n], 1., 1.], n) + ref_force_rng = np.roll([ref_force_dir[n], 0., 0.], n) + ref_force = np.copy(p.v) / vel2force + system.thermostat.set_npt(kT=1.0, gamma0=2.0, gammav=0.04, seed=42) + system.integrator.set_isotropic_npt(ext_pressure=2.0, piston=0.01, + direction=direction) + system.integrator.run(0, recalc_forces=True) + force0 = np.copy(p.v) / vel2force + system.integrator.run(1, recalc_forces=True) + force1 = np.copy(p.v) / vel2force + boxl1 = np.copy(system.box_l) + np.testing.assert_almost_equal(force0, ref_force) + np.testing.assert_almost_equal(force1, ref_force + ref_force_rng) + np.testing.assert_almost_equal(boxl1, ref_boxl) + self.tearDown() + @utx.skipIfMissingFeatures("VIRTUAL_SITES") def test_07__virtual(self): system = self.system From f8ce5dff49fda7cebd5309ef1399f3dbe9cbcdba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 5 Jan 2022 12:38:19 +0100 Subject: [PATCH 04/99] script_interface: Include what you use --- src/core/constraints/Constraint.hpp | 3 ++- src/core/forces.hpp | 1 - .../CylindricalTransformationParameters.hpp | 8 +++++--- src/script_interface/constraints/ExternalField.hpp | 1 + src/script_interface/constraints/ExternalPotential.hpp | 1 + src/script_interface/shapes/Cylinder.hpp | 3 +++ src/script_interface/shapes/Ellipsoid.hpp | 3 +++ src/script_interface/shapes/NoWhere.hpp | 2 ++ src/script_interface/shapes/Rhomboid.hpp | 3 +++ src/script_interface/shapes/SimplePore.hpp | 3 +++ src/script_interface/shapes/Slitpore.hpp | 3 +++ src/script_interface/shapes/Sphere.hpp | 3 +++ src/script_interface/shapes/SpheroCylinder.hpp | 3 +++ src/script_interface/shapes/Torus.hpp | 3 +++ src/script_interface/shapes/Wall.hpp | 3 +++ src/script_interface/tests/ObjectHandle_test.cpp | 3 +-- src/script_interface/tests/get_value_test.cpp | 1 + 17 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/core/constraints/Constraint.hpp b/src/core/constraints/Constraint.hpp index 827a34b94be..fcd0faa895c 100644 --- a/src/core/constraints/Constraint.hpp +++ b/src/core/constraints/Constraint.hpp @@ -21,7 +21,8 @@ #include "Observable_stat.hpp" #include "Particle.hpp" -#include "energy.hpp" + +#include namespace Constraints { class Constraint { diff --git a/src/core/forces.hpp b/src/core/forces.hpp index 21166dff9e5..cb7a1b3f6dd 100644 --- a/src/core/forces.hpp +++ b/src/core/forces.hpp @@ -32,7 +32,6 @@ #include "CellStructure.hpp" #include "ParticleRange.hpp" -#include "actor/Actor.hpp" #include "actor/ActorList.hpp" #include diff --git a/src/script_interface/CylindricalTransformationParameters.hpp b/src/script_interface/CylindricalTransformationParameters.hpp index 92114a4f207..5b983e9f57b 100644 --- a/src/script_interface/CylindricalTransformationParameters.hpp +++ b/src/script_interface/CylindricalTransformationParameters.hpp @@ -22,11 +22,13 @@ #ifndef SCRIPT_INTERFACE_CYL_TRANSFORM_PARAMS_HPP #define SCRIPT_INTERFACE_CYL_TRANSFORM_PARAMS_HPP -#include +#include "script_interface/auto_parameters/AutoParameters.hpp" +#include "script_interface/get_value.hpp" -#include "script_interface/ScriptInterface.hpp" +#include -#include "utils/math/cylindrical_transformation_parameters.hpp" +#include +#include namespace ScriptInterface { diff --git a/src/script_interface/constraints/ExternalField.hpp b/src/script_interface/constraints/ExternalField.hpp index eed29d387d2..0e544ced3d7 100644 --- a/src/script_interface/constraints/ExternalField.hpp +++ b/src/script_interface/constraints/ExternalField.hpp @@ -26,6 +26,7 @@ #include "fields.hpp" #include "script_interface/ScriptInterface.hpp" +#include "script_interface/constraints/Constraint.hpp" #include "core/constraints/Constraint.hpp" #include "core/constraints/ExternalField.hpp" diff --git a/src/script_interface/constraints/ExternalPotential.hpp b/src/script_interface/constraints/ExternalPotential.hpp index 6d2231538fe..071fb6d0790 100644 --- a/src/script_interface/constraints/ExternalPotential.hpp +++ b/src/script_interface/constraints/ExternalPotential.hpp @@ -29,6 +29,7 @@ #include "fields.hpp" #include "script_interface/ScriptInterface.hpp" +#include "script_interface/constraints/Constraint.hpp" #include diff --git a/src/script_interface/shapes/Cylinder.hpp b/src/script_interface/shapes/Cylinder.hpp index d326f13697f..a6e739fff0f 100644 --- a/src/script_interface/shapes/Cylinder.hpp +++ b/src/script_interface/shapes/Cylinder.hpp @@ -23,8 +23,11 @@ #define SCRIPT_INTERFACE_CYLINDER_WALL_HPP #include "Shape.hpp" + #include +#include + namespace ScriptInterface { namespace Shapes { diff --git a/src/script_interface/shapes/Ellipsoid.hpp b/src/script_interface/shapes/Ellipsoid.hpp index f64b1bffa4f..f816284bed2 100644 --- a/src/script_interface/shapes/Ellipsoid.hpp +++ b/src/script_interface/shapes/Ellipsoid.hpp @@ -23,8 +23,11 @@ #define SCRIPT_INTERFACE_SHAPES_ELLIPSOID_HPP #include "Shape.hpp" + #include +#include + namespace ScriptInterface { namespace Shapes { diff --git a/src/script_interface/shapes/NoWhere.hpp b/src/script_interface/shapes/NoWhere.hpp index 25a52edb21b..e13c1c0c540 100644 --- a/src/script_interface/shapes/NoWhere.hpp +++ b/src/script_interface/shapes/NoWhere.hpp @@ -26,6 +26,8 @@ #include +#include + namespace ScriptInterface { namespace Shapes { diff --git a/src/script_interface/shapes/Rhomboid.hpp b/src/script_interface/shapes/Rhomboid.hpp index 978a7e718e3..fc4ed015eaf 100644 --- a/src/script_interface/shapes/Rhomboid.hpp +++ b/src/script_interface/shapes/Rhomboid.hpp @@ -23,8 +23,11 @@ #define SCRIPT_INTERFACE_SHAPES_RHOMBOID_HPP #include "Shape.hpp" + #include +#include + namespace ScriptInterface { namespace Shapes { diff --git a/src/script_interface/shapes/SimplePore.hpp b/src/script_interface/shapes/SimplePore.hpp index 73afbb74e6d..3bcd119246e 100644 --- a/src/script_interface/shapes/SimplePore.hpp +++ b/src/script_interface/shapes/SimplePore.hpp @@ -21,8 +21,11 @@ #define SCRIPT_INTERFACE_SHAPES_SIMPLE_PORE_HPP #include "Shape.hpp" + #include +#include + namespace ScriptInterface { namespace Shapes { diff --git a/src/script_interface/shapes/Slitpore.hpp b/src/script_interface/shapes/Slitpore.hpp index 6fa4f946375..004e357358c 100644 --- a/src/script_interface/shapes/Slitpore.hpp +++ b/src/script_interface/shapes/Slitpore.hpp @@ -23,8 +23,11 @@ #define SCRIPT_INTERFACE_SHAPES_SLITPORE_HPP #include "script_interface/shapes/Shape.hpp" + #include +#include + namespace ScriptInterface { namespace Shapes { diff --git a/src/script_interface/shapes/Sphere.hpp b/src/script_interface/shapes/Sphere.hpp index 6068830e9ff..c359ec678d7 100644 --- a/src/script_interface/shapes/Sphere.hpp +++ b/src/script_interface/shapes/Sphere.hpp @@ -23,8 +23,11 @@ #define SCRIPT_INTERFACE_SHAPES_SPHERE_HPP #include "Shape.hpp" + #include +#include + namespace ScriptInterface { namespace Shapes { diff --git a/src/script_interface/shapes/SpheroCylinder.hpp b/src/script_interface/shapes/SpheroCylinder.hpp index a81cc40a238..d8c7ef81b39 100644 --- a/src/script_interface/shapes/SpheroCylinder.hpp +++ b/src/script_interface/shapes/SpheroCylinder.hpp @@ -23,8 +23,11 @@ #define SCRIPT_INTERFACE_SHAPES_SPHEROCYLINDER_HPP #include "Shape.hpp" + #include +#include + namespace ScriptInterface { namespace Shapes { diff --git a/src/script_interface/shapes/Torus.hpp b/src/script_interface/shapes/Torus.hpp index 0a21e45487b..fb903b7a53f 100644 --- a/src/script_interface/shapes/Torus.hpp +++ b/src/script_interface/shapes/Torus.hpp @@ -20,8 +20,11 @@ #define SCRIPT_INTERFACE_TORUS_WALL_HPP #include "Shape.hpp" + #include +#include + namespace ScriptInterface { namespace Shapes { diff --git a/src/script_interface/shapes/Wall.hpp b/src/script_interface/shapes/Wall.hpp index 87b8bba0f4c..0319b61a1be 100644 --- a/src/script_interface/shapes/Wall.hpp +++ b/src/script_interface/shapes/Wall.hpp @@ -23,8 +23,11 @@ #define SCRIPT_INTERFACE_SHAPES_WALL_HPP #include "Shape.hpp" + #include +#include + namespace ScriptInterface { namespace Shapes { diff --git a/src/script_interface/tests/ObjectHandle_test.cpp b/src/script_interface/tests/ObjectHandle_test.cpp index 18ee3bb01c2..2e99a62c16a 100644 --- a/src/script_interface/tests/ObjectHandle_test.cpp +++ b/src/script_interface/tests/ObjectHandle_test.cpp @@ -19,8 +19,6 @@ * along with this program. If not, see . */ -#include - #define BOOST_TEST_MODULE ObjectHandle test #define BOOST_TEST_DYN_LINK #include @@ -31,6 +29,7 @@ #include #include +#include #include #include diff --git a/src/script_interface/tests/get_value_test.cpp b/src/script_interface/tests/get_value_test.cpp index c7aacea20df..60c02746740 100644 --- a/src/script_interface/tests/get_value_test.cpp +++ b/src/script_interface/tests/get_value_test.cpp @@ -23,6 +23,7 @@ #include "script_interface/get_value.hpp" +#include #include #include #include From 25190af47ac132d4932d7149c4addd2724675cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 5 Jan 2022 13:06:24 +0100 Subject: [PATCH 05/99] script_interface: Narrow includes of MpiCallbacks Most components of the core and script interface don't need to know about the MPI implementation details. --- src/core/interactions.cpp | 3 +-- src/core/nonbonded_interactions/nonbonded_interaction_data.hpp | 3 --- src/script_interface/ScriptInterface.hpp | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/core/interactions.cpp b/src/core/interactions.cpp index 51fa4c887d7..4002ef7bd8c 100644 --- a/src/core/interactions.cpp +++ b/src/core/interactions.cpp @@ -20,12 +20,11 @@ */ #include "communication.hpp" -#include "TabulatedPotential.hpp" #include "bonded_interactions/bonded_interaction_data.hpp" -#include "bonded_interactions/bonded_tab.hpp" #include "collision.hpp" #include "electrostatics_magnetostatics/coulomb.hpp" #include "electrostatics_magnetostatics/dipole.hpp" +#include "event.hpp" #include "grid.hpp" #include "serialization/IA_parameters.hpp" diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index 0bc9b9c9300..daab467c72e 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -29,11 +29,8 @@ #include -#include #include #include -#include -#include #include #include diff --git a/src/script_interface/ScriptInterface.hpp b/src/script_interface/ScriptInterface.hpp index 87cac0f6fb8..54022e25578 100644 --- a/src/script_interface/ScriptInterface.hpp +++ b/src/script_interface/ScriptInterface.hpp @@ -24,7 +24,7 @@ #include -#include "ContextManager.hpp" +#include "Context.hpp" #include "ObjectHandle.hpp" #include "Variant.hpp" #include "auto_parameters/AutoParameters.hpp" From 37e9fed0dd31b7a14dd5a746eb7540462f62242c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 6 Jan 2022 18:12:11 +0100 Subject: [PATCH 06/99] script_interface: Generate a clear error message Distinguish between invalid type conversions and nullptr conversions. --- src/script_interface/get_value.hpp | 46 +++++++++++++++---- src/script_interface/tests/get_value_test.cpp | 43 ++++++++++++++++- 2 files changed, 77 insertions(+), 12 deletions(-) diff --git a/src/script_interface/get_value.hpp b/src/script_interface/get_value.hpp index 5ca3d6fd101..acff18edf23 100644 --- a/src/script_interface/get_value.hpp +++ b/src/script_interface/get_value.hpp @@ -181,6 +181,9 @@ template <> struct get_value_helper, void> { } }; +/** Custom error for a conversion that fails when the value is a nullptr. */ +class bad_get_nullptr : public boost::bad_get {}; + /* This allows direct retrieval of a shared_ptr to the object from * an ObjectRef variant. If the type is a derived type, the type is * also checked. @@ -193,7 +196,7 @@ struct get_value_helper< std::shared_ptr operator()(Variant const &v) const { auto so_ptr = boost::get(v); if (!so_ptr) { - throw boost::bad_get{}; + throw bad_get_nullptr{}; } auto t_ptr = std::dynamic_pointer_cast(so_ptr); @@ -205,6 +208,32 @@ struct get_value_helper< throw boost::bad_get{}; } }; + +/** + * @brief Re-throw a @c boost::bad_get exception wrapped in an @ref Exception. + * Write a custom error message for invalid conversions due to type mismatch + * and due to nullptr values, possibly with context information if the variant + * is in a container. + * @tparam T Type which the variant was supposed to convert to + * @tparam U Type of the container the variant is contained in + */ +template +inline void handle_bad_get(Variant const &v) { + using Utils::demangle; + auto const what = "Provided argument of type " + type_label(v); + auto const context = + (std::is_void::value) + ? "" + : " (raised during the creation of a " + demangle() + ")"; + try { + throw; + } catch (bad_get_nullptr const &) { + throw Exception(what + " is a null pointer" + context); + } catch (boost::bad_get const &) { + throw Exception(what + " is not convertible to " + demangle() + context); + } +} + } // namespace detail /** @@ -219,9 +248,9 @@ struct get_value_helper< template T get_value(Variant const &v) { try { return detail::get_value_helper{}(v); - } catch (const boost::bad_get &) { - throw Exception("Provided argument of type " + detail::type_label(v) + - " is not convertible to " + Utils::demangle()); + } catch (...) { + detail::handle_bad_get(v); + throw; } } @@ -233,12 +262,9 @@ std::unordered_map get_map(std::unordered_map const &v) { for (; it != v.end(); ++it) { ret.insert({it->first, detail::get_value_helper{}(it->second)}); } - } catch (const boost::bad_get &) { - throw Exception("Provided map value of type " + - detail::type_label(it->second) + " is not convertible to " + - Utils::demangle() + - " (raised during the creation of a " + - Utils::demangle>() + ")"); + } catch (...) { + detail::handle_bad_get>(v); + throw; } return ret; } diff --git a/src/script_interface/tests/get_value_test.cpp b/src/script_interface/tests/get_value_test.cpp index 60c02746740..8ee73e66028 100644 --- a/src/script_interface/tests/get_value_test.cpp +++ b/src/script_interface/tests/get_value_test.cpp @@ -21,6 +21,7 @@ #define BOOST_TEST_DYN_LINK #include +#include "script_interface/ObjectHandle.hpp" #include "script_interface/get_value.hpp" #include @@ -111,7 +112,45 @@ BOOST_AUTO_TEST_CASE(get_map_value) { std::unordered_map const map = get_map(map_variant); BOOST_CHECK_EQUAL(map.at(1), 1.5); BOOST_CHECK_EQUAL(map.at(2), 2.5); +} + +BOOST_AUTO_TEST_CASE(exceptions) { + using ScriptInterface::get_map; + using ScriptInterface::get_value; + using ScriptInterface::Variant; + + using so_ptr_t = std::shared_ptr; + + auto const so_obj = so_ptr_t(); + auto const so_ptr_tn = Utils::demangle(); - std::unordered_map const mixed{{1, 1}, {2, std::string("2")}}; - BOOST_CHECK_THROW((get_map(mixed)), std::exception); + { + auto const predicate = [](std::string const &type, std::string const &why) { + auto const message = "Provided argument of type " + type + " is " + why; + return [=](std::exception const &ex) { return ex.what() == message; }; + }; + auto const so_variant = Variant(so_obj); + BOOST_CHECK_EXCEPTION((get_value(so_variant)), std::exception, + predicate(so_ptr_tn, "a null pointer")); + BOOST_CHECK_EXCEPTION((get_value(so_variant)), std::exception, + predicate(so_ptr_tn, "not convertible to int")); + } + { + auto const predicate = [](std::string const &why, std::string const &type) { + auto const context = "raised during the creation of a std::unordered_map"; + auto const message = why + " (" + context + "{{1, so_obj}}; + BOOST_CHECK_EXCEPTION((get_map(so_map)), std::exception, + predicate("is a null pointer", so_ptr_tn)); + BOOST_CHECK_EXCEPTION((get_map(so_map)), std::exception, + predicate("is not convertible to int", "int")); + } + { + std::unordered_map const mixed{{1, 1}, {2, std::string("2")}}; + BOOST_CHECK_THROW((get_map(mixed)), std::exception); + } } From 53baf7c7d4b5fd57d5c2e916e8e84d6418f36662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 7 Jan 2022 13:00:56 +0100 Subject: [PATCH 07/99] core: Fix various regressions --- src/core/RuntimeErrorCollector.hpp | 7 ++++--- src/utils/include/utils/Histogram.hpp | 7 +++---- src/utils/include/utils/math/bspline.hpp | 8 +++++--- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/core/RuntimeErrorCollector.hpp b/src/core/RuntimeErrorCollector.hpp index 74d2c0600f4..388a66eee24 100644 --- a/src/core/RuntimeErrorCollector.hpp +++ b/src/core/RuntimeErrorCollector.hpp @@ -54,17 +54,18 @@ class RuntimeErrorCollector { const char *file, int line); /** - * \brief Return the number of all flying messages. + * \brief Get the number of all flying messages on all nodes. * * @return Total number of messages. */ int count() const; /** - * \brief Number of Messages that have at least level level. + * \brief Get the number of messages that have at least severity + * @p level on this node. * * @param level Severity filter. - * @return Number of Messages that have at least level. + * @return Number of messages that match the filter. */ int count(RuntimeError::ErrorLevel level); diff --git a/src/utils/include/utils/Histogram.hpp b/src/utils/include/utils/Histogram.hpp index 715d0d4b492..b9977da4282 100644 --- a/src/utils/include/utils/Histogram.hpp +++ b/src/utils/include/utils/Histogram.hpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -150,11 +151,9 @@ class Histogram { * \param pos Position to check. */ bool check_limits(Span pos) const { - if (pos.size() != M) { - throw std::invalid_argument("Wrong dimensions for the coordinates"); - } + assert(pos.size() == M); bool within_range = true; - for (std::size_t i = 0; i < pos.size(); ++i) { + for (std::size_t i = 0; i < M; ++i) { if (pos[i] < m_limits[i].first or pos[i] >= m_limits[i].second) within_range = false; } diff --git a/src/utils/include/utils/math/bspline.hpp b/src/utils/include/utils/math/bspline.hpp index 2bdc8c2a245..8c178d0fc6e 100644 --- a/src/utils/include/utils/math/bspline.hpp +++ b/src/utils/include/utils/math/bspline.hpp @@ -205,8 +205,9 @@ template auto bspline(int i, T x, int k) { } /** @brief Derivative of the B-spline. */ -template inline T bspline_d(int i, T x) { - static_assert(order <= 7, ""); +template +DEVICE_QUALIFIER auto bspline_d(int i, T x) + -> std::enable_if_t<(order > 0) && (order <= 7), T> { DEVICE_ASSERT(i < order); DEVICE_ASSERT(x >= T(-0.5)); DEVICE_ASSERT(x <= T(0.5)); @@ -318,7 +319,8 @@ template inline T bspline_d(int i, T x) { } } - throw std::runtime_error("Internal interpolation error."); + DEVICE_THROW(std::runtime_error("Internal interpolation error.")); + return T{}; } } // namespace Utils From 2378fde8556278d0dde728216d127f0aaa7766a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 7 Jan 2022 14:11:18 +0100 Subject: [PATCH 08/99] tests: Improve unit tests Add extra checks for script interface objects and the runtime error collector. Run unit tests designed for MPI on more than 1 core. Add MPI barriers to avoid unspecified behavior. Enable code coverage of static assertions in Boost checks by adding BOOST_TEST_PASSPOINT(). Boost optimizes out test cases containing only constexpr statements so we add these anonymous checkpoints to serve as no-op statements. --- src/core/unit_tests/CMakeLists.txt | 2 +- .../unit_tests/RuntimeErrorCollector_test.cpp | 83 +++++++++---------- .../unit_tests/field_coupling_fields_test.cpp | 1 + src/script_interface/tests/CMakeLists.txt | 4 +- src/script_interface/tests/Exception_test.cpp | 1 + src/script_interface/tests/None_test.cpp | 2 + .../tests/ObjectHandle_test.cpp | 16 +++- src/script_interface/tests/get_value_test.cpp | 54 ++++++++++-- src/shapes/unit_tests/Ellipsoid_test.cpp | 31 +++---- src/shapes/unit_tests/NoWhere_test.cpp | 20 ++--- src/utils/tests/Array_test.cpp | 23 +++-- src/utils/tests/Cache_test.cpp | 50 +++++++++-- src/utils/tests/Factory_test.cpp | 6 +- src/utils/tests/Span_test.cpp | 1 + src/utils/tests/Vector_test.cpp | 42 ++++++++-- src/utils/tests/as_const_test.cpp | 2 + src/utils/tests/bspline_test.cpp | 6 ++ src/utils/tests/histogram.cpp | 2 + src/utils/tests/matrix_test.cpp | 1 + src/utils/tests/memcpy_archive_test.cpp | 22 +++-- src/utils/tests/quaternion_test.cpp | 1 + src/utils/tests/type_traits_test.cpp | 1 + 22 files changed, 251 insertions(+), 120 deletions(-) diff --git a/src/core/unit_tests/CMakeLists.txt b/src/core/unit_tests/CMakeLists.txt index da942f2a4ad..b3771dd21e6 100644 --- a/src/core/unit_tests/CMakeLists.txt +++ b/src/core/unit_tests/CMakeLists.txt @@ -23,7 +23,7 @@ include(unit_test) unit_test(NAME RuntimeError_test SRC RuntimeError_test.cpp DEPENDS Boost::serialization) unit_test(NAME RuntimeErrorCollector_test SRC RuntimeErrorCollector_test.cpp - DEPENDS EspressoCore Boost::mpi MPI::MPI_CXX) + DEPENDS EspressoCore Boost::mpi MPI::MPI_CXX NUM_PROC 2) unit_test(NAME EspressoSystemStandAlone_test SRC EspressoSystemStandAlone_test.cpp DEPENDS EspressoCore Boost::mpi MPI::MPI_CXX NUM_PROC 2) diff --git a/src/core/unit_tests/RuntimeErrorCollector_test.cpp b/src/core/unit_tests/RuntimeErrorCollector_test.cpp index 9ed3f527f79..d860a231ad7 100644 --- a/src/core/unit_tests/RuntimeErrorCollector_test.cpp +++ b/src/core/unit_tests/RuntimeErrorCollector_test.cpp @@ -32,59 +32,43 @@ #include #include -#include #include -int main(int argc, char **argv) { - boost::mpi::environment mpi_env(argc, argv); - - return boost::unit_test::unit_test_main(init_unit_test, argc, argv); -} - -namespace Testing { - -void reduce_and_check(const boost::mpi::communicator &comm, bool local_value) { - if (comm.rank() == 0) { - bool total = true; - boost::mpi::reduce(comm, local_value, total, std::logical_and(), 0); - BOOST_CHECK(total); - } else { - boost::mpi::reduce(comm, local_value, std::logical_and(), 0); - } -} -} // namespace Testing - using ErrorHandling::RuntimeError; using ErrorHandling::RuntimeErrorCollector; +using ErrorLevel = ErrorHandling::RuntimeError::ErrorLevel; BOOST_AUTO_TEST_CASE(count) { boost::mpi::communicator world; RuntimeErrorCollector rec(world); - Testing::reduce_and_check(world, rec.count() == 0); + BOOST_REQUIRE_EQUAL(rec.count(), 0); + world.barrier(); /* MPI guarantees that size >= 1 and rank 0 exists. */ - if (world.rank() == (world.size() - 1)) { + auto const rank_of_error = world.size() - 1; + if (world.rank() == rank_of_error) { rec.error("Test_error", "Test_functions", "Test_file", 42); } - - Testing::reduce_and_check(world, rec.count() == 1); - + world.barrier(); + BOOST_REQUIRE_EQUAL(rec.count(), 1); + world.barrier(); rec.warning("Test_error", "Test_functions", "Test_file", 42); - - Testing::reduce_and_check(world, rec.count() == world.size() + 1); + world.barrier(); + BOOST_REQUIRE_EQUAL(rec.count(), world.size() + 1); /* There should now be one error and world.size() warnings */ - Testing::reduce_and_check(world, - rec.count(RuntimeError::ErrorLevel::ERROR) == 1); - /* All messages are at least WARNING or higher. */ - { - /* Beware of the execution order */ - int total = rec.count(); - Testing::reduce_and_check( - world, rec.count(RuntimeError::ErrorLevel::WARNING) == total); - } + auto const n_local_errors = static_cast(world.rank() == rank_of_error); + auto const n_local_warnings = n_local_errors + 1; + BOOST_REQUIRE_EQUAL(rec.count(ErrorLevel::ERROR), n_local_errors); + BOOST_REQUIRE_EQUAL(rec.count(ErrorLevel::WARNING), n_local_warnings); + + /* Clear list of errors */ + world.barrier(); + rec.clear(); + world.barrier(); + BOOST_REQUIRE_EQUAL(rec.count(), 0); } /* @@ -100,10 +84,11 @@ BOOST_AUTO_TEST_CASE(gather) { rec.error("Test_error", "Test_functions", "Test_file", world.rank()); rec.warning("Test_error", "Test_functions", "Test_file", world.rank()); + world.barrier(); if (world.rank() == 0) { /* Gathered error messages */ - auto results = rec.gather(); + auto const results = rec.gather(); /* Track how many messages we have seen from which node. */ std::vector present(world.size()); @@ -115,18 +100,24 @@ BOOST_AUTO_TEST_CASE(gather) { BOOST_CHECK(std::all_of(present.begin(), present.end(), [](int i) { return i == 2; })); /* Count warnings, should be world.rank() many */ - BOOST_CHECK(std::count_if( - results.begin(), results.end(), [](const RuntimeError &e) { - return e.level() == RuntimeError::ErrorLevel::WARNING; - }) == world.size()); + BOOST_CHECK(std::count_if(results.begin(), results.end(), + [](const RuntimeError &e) { + return e.level() == ErrorLevel::WARNING; + }) == world.size()); /* Count errors, should be world.rank() many */ - BOOST_CHECK(std::count_if( - results.begin(), results.end(), [](const RuntimeError &e) { - return e.level() == RuntimeError::ErrorLevel::ERROR; - }) == world.size()); + BOOST_CHECK(std::count_if(results.begin(), results.end(), + [](const RuntimeError &e) { + return e.level() == ErrorLevel::ERROR; + }) == world.size()); } else { rec.gather_local(); } - Testing::reduce_and_check(world, rec.count() == 0); + BOOST_REQUIRE_EQUAL(rec.count(), 0); +} + +int main(int argc, char **argv) { + boost::mpi::environment mpi_env(argc, argv); + + return boost::unit_test::unit_test_main(init_unit_test, argc, argv); } diff --git a/src/core/unit_tests/field_coupling_fields_test.cpp b/src/core/unit_tests/field_coupling_fields_test.cpp index 9823c0e3572..a6d5a43cdbe 100644 --- a/src/core/unit_tests/field_coupling_fields_test.cpp +++ b/src/core/unit_tests/field_coupling_fields_test.cpp @@ -49,6 +49,7 @@ BOOST_AUTO_TEST_CASE(jacobian_type_test) { static_assert( is_same, Utils::Matrix>::value, ""); + BOOST_TEST_PASSPOINT(); } BOOST_AUTO_TEST_CASE(constant_scalar_field) { diff --git a/src/script_interface/tests/CMakeLists.txt b/src/script_interface/tests/CMakeLists.txt index cb43d79a48b..d034009b3f1 100644 --- a/src/script_interface/tests/CMakeLists.txt +++ b/src/script_interface/tests/CMakeLists.txt @@ -26,12 +26,12 @@ unit_test(NAME AutoParameters_test SRC AutoParameters_test.cpp DEPENDS unit_test(NAME AutoParameter_test SRC AutoParameter_test.cpp DEPENDS ScriptInterface) unit_test(NAME Variant_test SRC Variant_test.cpp DEPENDS ScriptInterface) -unit_test(NAME get_value SRC get_value_test.cpp DEPENDS ScriptInterface) +unit_test(NAME get_value_test SRC get_value_test.cpp DEPENDS ScriptInterface) unit_test(NAME None_test SRC None_test.cpp DEPENDS ScriptInterface) unit_test(NAME LocalContext_test SRC LocalContext_test.cpp DEPENDS ScriptInterface) unit_test(NAME GlobalContext_test SRC GlobalContext_test.cpp DEPENDS - ScriptInterface) + ScriptInterface Boost::mpi MPI::MPI_CXX NUM_PROC 2) unit_test(NAME Exception_test SRC Exception_test.cpp DEPENDS ScriptInterface) unit_test(NAME packed_variant_test SRC packed_variant_test.cpp DEPENDS ScriptInterface) diff --git a/src/script_interface/tests/Exception_test.cpp b/src/script_interface/tests/Exception_test.cpp index 9b3d5e5c4e7..8321f6156f2 100644 --- a/src/script_interface/tests/Exception_test.cpp +++ b/src/script_interface/tests/Exception_test.cpp @@ -34,6 +34,7 @@ BOOST_AUTO_TEST_CASE(ctor) { static_assert( std::is_constructible::value, ""); + BOOST_TEST_PASSPOINT(); } BOOST_AUTO_TEST_CASE(what_) { diff --git a/src/script_interface/tests/None_test.cpp b/src/script_interface/tests/None_test.cpp index 4cf69d2d2aa..9f5f17c5dfe 100644 --- a/src/script_interface/tests/None_test.cpp +++ b/src/script_interface/tests/None_test.cpp @@ -29,12 +29,14 @@ using ScriptInterface::None; BOOST_AUTO_TEST_CASE(constructor_bool) { static_assert(!None{}, ""); static_assert(!None{nullptr}, ""); + BOOST_TEST_PASSPOINT(); } BOOST_AUTO_TEST_CASE(comparison) { static_assert(None{} == None{}, ""); static_assert(!(None{} != None{}), ""); static_assert(!(None{} < None{}), ""); + BOOST_TEST_PASSPOINT(); } BOOST_AUTO_TEST_CASE(from_nullptr) { diff --git a/src/script_interface/tests/ObjectHandle_test.cpp b/src/script_interface/tests/ObjectHandle_test.cpp index 2e99a62c16a..65237f500ad 100644 --- a/src/script_interface/tests/ObjectHandle_test.cpp +++ b/src/script_interface/tests/ObjectHandle_test.cpp @@ -89,6 +89,7 @@ struct LogHandle : public ObjectHandle { BOOST_AUTO_TEST_CASE(non_copyable) { static_assert(!std::is_copy_constructible::value, ""); static_assert(!std::is_copy_assignable::value, ""); + BOOST_TEST_PASSPOINT(); } /* @@ -165,12 +166,14 @@ struct LogContext : public Context { bool is_head_node() const override { return true; }; }; +} // namespace Testing /* * Check that Objecthandle::set_parameter does * notify the context. */ BOOST_AUTO_TEST_CASE(notify_set_parameter_) { + using namespace Testing; auto log_ctx = std::make_shared(); auto o = log_ctx->make_shared({}, {}); @@ -192,6 +195,7 @@ BOOST_AUTO_TEST_CASE(notify_set_parameter_) { * notify the context. */ BOOST_AUTO_TEST_CASE(notify_call_method_) { + using namespace Testing; auto log_ctx = std::make_shared(); auto o = log_ctx->make_shared({}, {}); @@ -205,4 +209,14 @@ BOOST_AUTO_TEST_CASE(notify_call_method_) { BOOST_CHECK((boost::get(log_entry.second) == MockCall::CallMethod{&name, ¶ms})); } -} // namespace Testing + +/* + * Check basic interface. + */ +BOOST_AUTO_TEST_CASE(interface_) { + using namespace Testing; + auto log_ctx = std::make_shared(); + auto o = log_ctx->make_shared({}, {}); + BOOST_CHECK(log_ctx->is_head_node()); + BOOST_CHECK_EQUAL(log_ctx->name(o.get()), "Dummy"); +} diff --git a/src/script_interface/tests/get_value_test.cpp b/src/script_interface/tests/get_value_test.cpp index 8ee73e66028..055605008ba 100644 --- a/src/script_interface/tests/get_value_test.cpp +++ b/src/script_interface/tests/get_value_test.cpp @@ -34,10 +34,20 @@ BOOST_AUTO_TEST_CASE(default_case) { using ScriptInterface::get_value; using ScriptInterface::Variant; - auto const s = std::string{"Abc"}; - auto const v = Variant(s); + { + auto const s = std::string{"Abc"}; + auto const v = Variant(s); - BOOST_CHECK(s == get_value(v)); + BOOST_CHECK_EQUAL(get_value(v), s); + } + { + auto const vec = Utils::Vector{1., 2., 3.}; + auto const var = Variant{vec}; + + BOOST_CHECK_EQUAL((get_value>(var)), vec); + BOOST_CHECK_EQUAL((get_value>(var)), vec); + BOOST_CHECK_EQUAL((get_value(var)), vec); + } } BOOST_AUTO_TEST_CASE(conversions) { @@ -49,8 +59,9 @@ BOOST_AUTO_TEST_CASE(conversions) { static_assert(allow_conversion::value, ""); static_assert(not allow_conversion::value, ""); - BOOST_CHECK_EQUAL(3.1415, get_value(3.1415)); - BOOST_CHECK_EQUAL(double(3), get_value(3)); + BOOST_CHECK_EQUAL(get_value(3.1415), 3.1415); + BOOST_CHECK_EQUAL(get_value(3), 3.); + BOOST_CHECK_EQUAL(get_value(Variant{3}), 3.); } BOOST_AUTO_TEST_CASE(static_vector) { @@ -63,6 +74,11 @@ BOOST_AUTO_TEST_CASE(static_vector) { auto const expected = Utils::Vector3d{1., 2., 3.}; BOOST_CHECK(get_value(v) == expected); } + { + Variant v = std::vector({1., 2., 3.}); + auto const expected = Utils::Vector3d{1., 2., 3.}; + BOOST_CHECK(get_value(v) == expected); + } /* Conversion applied */ { @@ -82,6 +98,11 @@ BOOST_AUTO_TEST_CASE(heap_vector) { auto const expected = std::vector{1., 2., 3.}; BOOST_CHECK(get_value>(v) == expected); } + { + Variant v = std::vector({1., 2., 3.}); + auto const expected = std::vector{1., 2., 3.}; + BOOST_CHECK(get_value>(v) == expected); + } /* Conversion applied */ { @@ -102,6 +123,7 @@ BOOST_AUTO_TEST_CASE(get_value_from_map) { BOOST_CHECK(3.1 == get_value(map, "e")); BOOST_CHECK(13 == get_value_or(map, "a", -1)); BOOST_CHECK(-1 == get_value_or(map, "nope", -1)); + BOOST_CHECK_THROW((get_value(map, "unknown")), std::exception); } BOOST_AUTO_TEST_CASE(get_map_value) { @@ -114,6 +136,16 @@ BOOST_AUTO_TEST_CASE(get_map_value) { BOOST_CHECK_EQUAL(map.at(2), 2.5); } +BOOST_AUTO_TEST_CASE(get_unordered_map) { + using ScriptInterface::get_value; + using ScriptInterface::Variant; + + auto const var = Variant{std::unordered_map{{1, 1}, {2, 2.5}}}; + auto const map = get_value>(var); + BOOST_CHECK_EQUAL(get_value(map.at(1)), 1); + BOOST_CHECK_EQUAL(get_value(map.at(2)), 2.5); +} + BOOST_AUTO_TEST_CASE(exceptions) { using ScriptInterface::get_map; using ScriptInterface::get_value; @@ -150,7 +182,19 @@ BOOST_AUTO_TEST_CASE(exceptions) { predicate("is not convertible to int", "int")); } { + using Utils::Vector3d; std::unordered_map const mixed{{1, 1}, {2, std::string("2")}}; BOOST_CHECK_THROW((get_map(mixed)), std::exception); + std::vector const v_var = {1., 2.}; + std::vector const v_dbl = {1., 2.}; + BOOST_CHECK_THROW((get_value(Variant{v_var})), std::exception); + BOOST_CHECK_THROW((get_value(Variant{v_dbl})), std::exception); + Utils::Vector4d const quat{}; + BOOST_CHECK_THROW((get_value(Variant{quat})), std::exception); + BOOST_CHECK_THROW((get_value(Variant{1.})), std::exception); + BOOST_CHECK_THROW((get_value>(Variant{})), std::exception); + BOOST_CHECK_THROW((get_value(Variant{v_dbl})), std::exception); + BOOST_CHECK_THROW((get_value>(Variant{})), + std::exception); } } diff --git a/src/shapes/unit_tests/Ellipsoid_test.cpp b/src/shapes/unit_tests/Ellipsoid_test.cpp index d6d9492cc4e..aa37f116d94 100644 --- a/src/shapes/unit_tests/Ellipsoid_test.cpp +++ b/src/shapes/unit_tests/Ellipsoid_test.cpp @@ -30,10 +30,18 @@ #include #include -#include +#include -bool check_distance_function(const Shapes::Shape &s) { - double semiaxes[3] = {3.1, 2.2, 1.3}; +BOOST_AUTO_TEST_CASE(dist_function) { + // multiply by 100 because BOOST_REQUIRE_CLOSE takes a percentage tolerance + auto constexpr tol = std::numeric_limits::epsilon() * 100; + double const semiaxes[3] = {3.1, 2.2, 1.3}; + + Shapes::Ellipsoid e; + e.set_semiaxis_a(semiaxes[0]); + e.set_semiaxis_b(semiaxes[1]); + e.set_semiaxis_c(semiaxes[2]); + auto const &s = e; // const handle int N = 100; for (int i = 0; i < N; i++) { @@ -51,8 +59,7 @@ bool check_distance_function(const Shapes::Shape &s) { /* check that points on ellipsoid yield zero distance */ s.calculate_dist(pos, d, dist); - if (std::abs(d) > 1e-12) - return false; + BOOST_REQUIRE_SMALL(d, tol); /* pos outside of surface */ for (int dim = 0; dim < 3; dim++) @@ -60,19 +67,7 @@ bool check_distance_function(const Shapes::Shape &s) { s.calculate_dist(pos, d, dist); /* trivial test */ - if ((dist.norm2() - d * d) > 1e-12) - return false; + BOOST_REQUIRE_CLOSE(dist.norm2(), d * d, tol); } } - - return true; -} - -BOOST_AUTO_TEST_CASE(dist_function) { - Shapes::Ellipsoid e; - e.set_semiaxis_a(3.1); - e.set_semiaxis_b(2.2); - e.set_semiaxis_c(1.3); - - BOOST_CHECK(check_distance_function(e)); } diff --git a/src/shapes/unit_tests/NoWhere_test.cpp b/src/shapes/unit_tests/NoWhere_test.cpp index 057b0a1f2b6..1b7b26ad501 100644 --- a/src/shapes/unit_tests/NoWhere_test.cpp +++ b/src/shapes/unit_tests/NoWhere_test.cpp @@ -30,9 +30,11 @@ #include -bool dist_is_always_inf(const Shapes::Shape &s) { +BOOST_AUTO_TEST_CASE(dist_function) { constexpr auto infinity = std::numeric_limits::infinity(); + Shapes::NoWhere const s; + Utils::Vector3d const positions[2] = { {0.0, 1.0, 2.0}, {-10.0, 0.1, 5.0}, @@ -43,22 +45,10 @@ bool dist_is_always_inf(const Shapes::Shape &s) { double d; s.calculate_dist(pos, d, dist); - if (d != infinity) { - return false; - } + BOOST_CHECK_EQUAL(d, infinity); for (auto xyz : dist) { - if (xyz != infinity) { - return false; - } + BOOST_CHECK_EQUAL(xyz, infinity); } } - - return true; -} - -BOOST_AUTO_TEST_CASE(dist_function) { - Shapes::NoWhere nw; - - BOOST_CHECK(dist_is_always_inf(nw)); } diff --git a/src/utils/tests/Array_test.cpp b/src/utils/tests/Array_test.cpp index ee9bca11871..ab517392b50 100644 --- a/src/utils/tests/Array_test.cpp +++ b/src/utils/tests/Array_test.cpp @@ -39,6 +39,7 @@ using Utils::Array; BOOST_AUTO_TEST_CASE(const_expr_ctor) { static_assert(4 == Array().size(), ""); static_assert(4 == Array().max_size(), ""); + BOOST_TEST_PASSPOINT(); } BOOST_AUTO_TEST_CASE(array_ctor) { @@ -54,30 +55,37 @@ BOOST_AUTO_TEST_CASE(array_ctor) { BOOST_AUTO_TEST_CASE(iterators) { auto a = Array{{1, 2, 3, 4}}; - BOOST_CHECK(*(a.begin()) == 1); - BOOST_CHECK(*(a.cbegin()) == 1); - BOOST_CHECK(*(a.end() - 1) == 4); - BOOST_CHECK(*(a.cend() - 1) == 4); + BOOST_CHECK_EQUAL(*(a.begin()), 1); + BOOST_CHECK_EQUAL(*(a.cbegin()), 1); + BOOST_CHECK_EQUAL(*(a.end() - 1), 4); + BOOST_CHECK_EQUAL(*(a.cend() - 1), 4); } BOOST_AUTO_TEST_CASE(element_access) { auto a = Array{{5, 6, 7, 8, 9}}; + auto const &b = a; int c = 5; + int j = 0; for (int i : a) { - BOOST_CHECK(i == c); - BOOST_CHECK(i == c); + BOOST_CHECK_EQUAL(i, c); + BOOST_CHECK_EQUAL(a[j], c); + BOOST_CHECK_EQUAL(b[j], c); + BOOST_CHECK_EQUAL(a.at(j), c); + BOOST_CHECK_EQUAL(b.at(j), c); ++c; + ++j; } BOOST_CHECK_THROW(a.at(a.size()), std::out_of_range); + BOOST_CHECK_THROW(b.at(b.size()), std::out_of_range); } BOOST_AUTO_TEST_CASE(fill) { Array a{}; a.fill(10); for (int i : a) { - BOOST_CHECK(i == 10); + BOOST_CHECK_EQUAL(i, 10); } } @@ -86,6 +94,7 @@ BOOST_AUTO_TEST_CASE(broadcast) { static_assert(a[0] == 5, ""); static_assert(a[1] == 5, ""); static_assert(a[2] == 5, ""); + BOOST_TEST_PASSPOINT(); } BOOST_AUTO_TEST_CASE(serialization) { diff --git a/src/utils/tests/Cache_test.cpp b/src/utils/tests/Cache_test.cpp index db22a0a75c1..0e0a3ba97b7 100644 --- a/src/utils/tests/Cache_test.cpp +++ b/src/utils/tests/Cache_test.cpp @@ -33,6 +33,7 @@ BOOST_AUTO_TEST_CASE(types) { using cache_type = Cache; static_assert(std::is_same::value, ""); static_assert(std::is_same::value, ""); + BOOST_TEST_PASSPOINT(); } BOOST_AUTO_TEST_CASE(get_value) { @@ -41,15 +42,15 @@ BOOST_AUTO_TEST_CASE(get_value) { cache.put(41, 42); cache.put(42, 43); BOOST_REQUIRE(cache.get(41)); - BOOST_CHECK(42 == *cache.get(41)); + BOOST_CHECK_EQUAL(*cache.get(41), 42); BOOST_REQUIRE(cache.get(42)); - BOOST_CHECK(43 == *cache.get(42)); + BOOST_CHECK_EQUAL(*cache.get(42), 43); } { Cache cache; - BOOST_CHECK(nullptr == cache.get(123)); - BOOST_CHECK(nullptr == cache.get(11)); + BOOST_CHECK_EQUAL(cache.get(123), nullptr); + BOOST_CHECK_EQUAL(cache.get(11), nullptr); } } @@ -81,9 +82,21 @@ BOOST_AUTO_TEST_CASE(invalidate) { } BOOST_AUTO_TEST_CASE(put) { - Cache cache; + { + Cache cache; + cache.put(0, 2); + BOOST_CHECK(cache.has(0)); + } - cache.put(0, 2); + { + Cache cache; + std::vector const keys = {1, 2, 3}; + std::vector const values = {0, 1, 2}; + cache.put(keys.begin(), keys.end(), values.begin()); + BOOST_CHECK(cache.has(1)); + BOOST_CHECK(cache.has(2)); + BOOST_CHECK(cache.has(3)); + } } BOOST_AUTO_TEST_CASE(max_size) { @@ -105,7 +118,7 @@ BOOST_AUTO_TEST_CASE(max_size) { { const Cache::size_type max_size = 1000; Cache cache(max_size); - BOOST_CHECK(max_size == cache.max_size()); + BOOST_CHECK_EQUAL(cache.max_size(), max_size); } { @@ -116,7 +129,28 @@ BOOST_AUTO_TEST_CASE(max_size) { cache.put(i, 11); } - BOOST_CHECK(max_size == cache.size()); + BOOST_CHECK_EQUAL(cache.size(), max_size); + } + + { + // Cache::drop_random_element() should work when cache is empty + Cache cache(0); + + for (int i = 0; i < 10; i++) { + cache.put(i, 11); + } + + BOOST_CHECK_EQUAL(cache.size(), 1); + } + + { + Cache cache(0); + + std::vector const keys = {1, 2, 3}; + std::vector const values = {0, 1, 2}; + cache.put(keys.begin(), keys.end(), values.begin()); + + BOOST_CHECK_EQUAL(cache.size(), 0); } } diff --git a/src/utils/tests/Factory_test.cpp b/src/utils/tests/Factory_test.cpp index ad461867dae..ff736c2c378 100644 --- a/src/utils/tests/Factory_test.cpp +++ b/src/utils/tests/Factory_test.cpp @@ -29,6 +29,7 @@ #include "utils/Factory.hpp" +#include #include struct TestClass { @@ -74,6 +75,9 @@ BOOST_AUTO_TEST_CASE(type_name) { /* Make an object */ auto o = factory.make(derived_class_name); - + o->method(); BOOST_CHECK_EQUAL(factory.type_name(*o.get()), derived_class_name); + + /* Make an unknown object */ + BOOST_CHECK_THROW(factory.make("unknown"), std::domain_error); } diff --git a/src/utils/tests/Span_test.cpp b/src/utils/tests/Span_test.cpp index d1e8b0a034e..577596f2cf7 100644 --- a/src/utils/tests/Span_test.cpp +++ b/src/utils/tests/Span_test.cpp @@ -32,6 +32,7 @@ using Utils::Span; BOOST_AUTO_TEST_CASE(const_expr_ctor) { static_assert(4 == Span(nullptr, 4).size(), ""); + BOOST_TEST_PASSPOINT(); } BOOST_AUTO_TEST_CASE(array_ctor) { diff --git a/src/utils/tests/Vector_test.cpp b/src/utils/tests/Vector_test.cpp index ac736785386..c49ebb22548 100644 --- a/src/utils/tests/Vector_test.cpp +++ b/src/utils/tests/Vector_test.cpp @@ -25,8 +25,7 @@ #define BOOST_TEST_DYN_LINK #include -#include "utils/Vector.hpp" -using Utils::Vector; +#include #include @@ -41,6 +40,8 @@ using Utils::Vector; #include #include +using Utils::Vector; + /* Number of nontrivial Baxter permutations of length 2n-1. (A001185) */ #define TEST_NUMBERS \ { 0, 1, 1, 7, 21, 112, 456, 2603, 13203 } @@ -226,7 +227,7 @@ BOOST_AUTO_TEST_CASE(decay_to_scalar_test) { static_assert(std::is_same::value, ""); } - + BOOST_TEST_PASSPOINT(); { using original_t = Utils::Vector3i; using decayed_t = typename Utils::decay_to_scalar::type; @@ -270,12 +271,38 @@ BOOST_AUTO_TEST_CASE(conversion) { using Utils::Vector3d; using Utils::Vector3f; + /* The Vector class has a user-defined conversion method, + * however static_cast() will not use it. Instead, the + * range-based constructor is called. */ + auto const orig = Vector3d{1., 2., 3.}; - auto const result = static_cast(orig); + auto const expected = + Vector3f{static_cast(orig[0]), static_cast(orig[1]), + static_cast(orig[2])}; + + // check range-based conversion + { + auto const result = static_cast(orig); + BOOST_TEST(result == expected); + } - BOOST_CHECK_EQUAL(result[0], static_cast(orig[0])); - BOOST_CHECK_EQUAL(result[1], static_cast(orig[1])); - BOOST_CHECK_EQUAL(result[2], static_cast(orig[2])); + // check cast operator + { + auto const result = orig.operator Utils::Vector3f(); + BOOST_TEST(result == expected); + } + + // check vector conversion + { + auto const result = Utils::Vector3d{static_cast>(orig)}; + BOOST_TEST(result == orig); + } + + // check vector conversion + { + auto const result = Utils::Vector3d{orig.as_vector()}; + BOOST_TEST(result == orig); + } } BOOST_AUTO_TEST_CASE(vector_product_test) { @@ -343,4 +370,5 @@ BOOST_AUTO_TEST_CASE(type_deduction) { typename boost::qvm::deduce_vec, 3>::type, Utils::Vector>::value, ""); + BOOST_TEST_PASSPOINT(); } diff --git a/src/utils/tests/as_const_test.cpp b/src/utils/tests/as_const_test.cpp index 34a7d4c9852..2fe0406b11a 100644 --- a/src/utils/tests/as_const_test.cpp +++ b/src/utils/tests/as_const_test.cpp @@ -29,6 +29,7 @@ BOOST_AUTO_TEST_CASE(non_const) { using actual = decltype(as_const(std::declval())); static_assert(std::is_same::value, ""); + BOOST_TEST_PASSPOINT(); } BOOST_AUTO_TEST_CASE(const_) { @@ -36,6 +37,7 @@ BOOST_AUTO_TEST_CASE(const_) { using actual = decltype(as_const(std::declval())); static_assert(std::is_same::value, ""); + BOOST_TEST_PASSPOINT(); } BOOST_AUTO_TEST_CASE(value) { diff --git a/src/utils/tests/bspline_test.cpp b/src/utils/tests/bspline_test.cpp index 606d424092a..391b8192629 100644 --- a/src/utils/tests/bspline_test.cpp +++ b/src/utils/tests/bspline_test.cpp @@ -26,6 +26,7 @@ #include #include +#include template using integer_list = boost::mpl::list...>; @@ -83,3 +84,8 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(bspline_derivatives, T, test_bspline_orders) { } } } + +BOOST_AUTO_TEST_CASE(exceptions) { + BOOST_CHECK_THROW((Utils::bspline<2, double>(-100, 0.)), std::runtime_error); + BOOST_CHECK_THROW((Utils::bspline_d<2, float>(-1, 0.f)), std::runtime_error); +} diff --git a/src/utils/tests/histogram.cpp b/src/utils/tests/histogram.cpp index 42dd8738a21..1eb99143d1e 100644 --- a/src/utils/tests/histogram.cpp +++ b/src/utils/tests/histogram.cpp @@ -57,6 +57,7 @@ BOOST_AUTO_TEST_CASE(histogram) { std::vector{{10.0, 10.0}}); BOOST_CHECK((hist.get_histogram())[0] == 11.0); BOOST_CHECK((hist.get_histogram())[1] == 11.0); + // Check exceptions BOOST_CHECK_THROW(hist.update(std::vector{{1.0, 5.0, 3.0}}), std::invalid_argument); BOOST_CHECK_THROW(hist.update(std::vector{{0.0, 0.0}}, @@ -97,6 +98,7 @@ BOOST_AUTO_TEST_CASE(cylindrical_histogram) { BOOST_CHECK((hist.get_histogram())[0] == 11.0); BOOST_CHECK((hist.get_histogram())[1] == 11.0); BOOST_CHECK((hist.get_histogram())[2] == 11.0); + // Check exceptions BOOST_CHECK_THROW(hist.update(std::vector{{1.0, pi}}), std::invalid_argument); BOOST_CHECK_THROW(hist.update(std::vector{{0.0, 0.0, 0.0}}, diff --git a/src/utils/tests/matrix_test.cpp b/src/utils/tests/matrix_test.cpp index d3a9533cd2f..88b21fac6cf 100644 --- a/src/utils/tests/matrix_test.cpp +++ b/src/utils/tests/matrix_test.cpp @@ -90,6 +90,7 @@ BOOST_AUTO_TEST_CASE(type_deduction) { Utils::Vector, 2>::type, Utils::Vector>::value, ""); + BOOST_TEST_PASSPOINT(); } BOOST_AUTO_TEST_CASE(matrix_matrix) { diff --git a/src/utils/tests/memcpy_archive_test.cpp b/src/utils/tests/memcpy_archive_test.cpp index 6997b4b8522..f8a362de282 100644 --- a/src/utils/tests/memcpy_archive_test.cpp +++ b/src/utils/tests/memcpy_archive_test.cpp @@ -60,11 +60,8 @@ struct is_statically_serializable> BOOST_AUTO_TEST_CASE(packing_size_test) { BOOST_CHECK_EQUAL(Utils::MemcpyIArchive::packing_size(), sizeof(int)); - - { - BOOST_CHECK_EQUAL(Utils::MemcpyIArchive::packing_size(), - Utils::MemcpyIArchive::packing_size()); - } + BOOST_CHECK_EQUAL(Utils::MemcpyIArchive::packing_size(), + Utils::MemcpyIArchive::packing_size()); } BOOST_AUTO_TEST_CASE(type_traits) { @@ -75,9 +72,11 @@ BOOST_AUTO_TEST_CASE(type_traits) { static_assert(Utils::is_statically_serializable::value, ""); static_assert(not Utils::detail::use_memcpy::value, ""); static_assert(Utils::detail::use_serialize::value, ""); + + BOOST_TEST_PASSPOINT(); } -BOOST_AUTO_TEST_CASE(skiping_and_position) { +BOOST_AUTO_TEST_CASE(skipping_and_position) { std::array buf; auto ar = Utils::MemcpyOArchive(Utils::make_span(buf)); @@ -91,9 +90,13 @@ BOOST_AUTO_TEST_CASE(memcpy_processing) { std::array buf; auto const test_number = 5; - auto oa = Utils::MemcpyOArchive(Utils::make_span(buf)); - oa << test_number; - BOOST_CHECK_EQUAL(oa.bytes_written(), sizeof(test_number)); + + { + auto oa = Utils::MemcpyOArchive(Utils::make_span(buf)); + oa << test_number; + BOOST_CHECK_EQUAL(oa.bytes_written(), sizeof(test_number)); + BOOST_CHECK_EQUAL(oa.get_library_version(), 4); + } { auto ia = Utils::MemcpyIArchive(Utils::make_span(buf)); @@ -101,6 +104,7 @@ BOOST_AUTO_TEST_CASE(memcpy_processing) { ia >> out; BOOST_CHECK_EQUAL(out, test_number); BOOST_CHECK_EQUAL(ia.bytes_read(), sizeof(test_number)); + BOOST_CHECK_EQUAL(ia.get_library_version(), 4); } } diff --git a/src/utils/tests/quaternion_test.cpp b/src/utils/tests/quaternion_test.cpp index d70b99235a1..360a8c853bb 100644 --- a/src/utils/tests/quaternion_test.cpp +++ b/src/utils/tests/quaternion_test.cpp @@ -165,4 +165,5 @@ BOOST_AUTO_TEST_CASE(type_deduction) { Quaternion>::type, Quaternion>::value, ""); + BOOST_TEST_PASSPOINT(); } diff --git a/src/utils/tests/type_traits_test.cpp b/src/utils/tests/type_traits_test.cpp index fde913f5378..131e7a9beec 100644 --- a/src/utils/tests/type_traits_test.cpp +++ b/src/utils/tests/type_traits_test.cpp @@ -30,4 +30,5 @@ BOOST_AUTO_TEST_CASE(size_in_bits) { static_assert(CHAR_BIT == Utils::size_in_bits::value, ""); static_assert(CHAR_BIT * sizeof(int) == Utils::size_in_bits::value, ""); + BOOST_TEST_PASSPOINT(); } From a4b8f9d630d551a7f3207b2fc8b83687a0d63e14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 10 Jan 2022 17:41:12 +0100 Subject: [PATCH 09/99] tests: Unit test accumulators script interface --- .../tests/Accumulators_test.cpp | 183 ++++++++++++++++++ src/script_interface/tests/CMakeLists.txt | 2 + 2 files changed, 185 insertions(+) create mode 100644 src/script_interface/tests/Accumulators_test.cpp diff --git a/src/script_interface/tests/Accumulators_test.cpp b/src/script_interface/tests/Accumulators_test.cpp new file mode 100644 index 00000000000..17751f0a0ff --- /dev/null +++ b/src/script_interface/tests/Accumulators_test.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (C) 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 . + */ + +#define BOOST_TEST_MODULE Accumulators test +#define BOOST_TEST_DYN_LINK +#include + +#include + +#include "script_interface/accumulators/Correlator.hpp" +#include "script_interface/accumulators/MeanVarianceCalculator.hpp" +#include "script_interface/accumulators/TimeSeries.hpp" +#include "script_interface/get_value.hpp" +#include "script_interface/observables/ParamlessObservable.hpp" + +#include "core/observables/Observable.hpp" + +#include + +#include +#include +#include +#include + +namespace Observables { +class MockObservable : public Observable { +public: + std::vector operator()() const override { return {1., 2., 3., 4.}; } + std::vector shape() const override { return {2u, 2u}; } +}; +} // namespace Observables + +namespace ScriptInterface { +namespace Observables { +NEW_PARAMLESS_OBSERVABLE(MockObservable) +} // namespace Observables +} // namespace ScriptInterface + +using TestObs = ScriptInterface::Observables::MockObservable; +using TestObsPtr = std::shared_ptr; +using namespace ScriptInterface; + +BOOST_AUTO_TEST_CASE(time_series) { + auto const obs = std::make_shared(); + ScriptInterface::Accumulators::TimeSeries acc; + acc.do_construct({{"obs", obs}, {"delta_N", 2}}); + acc.do_call_method("update", VariantMap{}); + { + BOOST_CHECK_EQUAL(get_value(acc.get_parameter("delta_N")), 2); + BOOST_CHECK_EQUAL(get_value(acc.get_parameter("obs")), obs); + } + { + auto const shape_ref = std::vector{{1, 2, 2}}; + // check non-const access + auto const variant = acc.do_call_method("shape", VariantMap{}); + auto const shape = get_value>(variant); + BOOST_TEST(shape == shape_ref, boost::test_tools::per_element()); + // check const access + auto const shape_const = Utils::as_const(acc).accumulator()->shape(); + BOOST_TEST(shape_const == shape_ref, boost::test_tools::per_element()); + } + { + auto const variant = acc.do_call_method("time_series", VariantMap{}); + auto const time_series = get_value>(variant); + BOOST_REQUIRE_EQUAL(time_series.size(), 1u); + auto const series = get_value>(time_series[0]); + auto const series_ref = std::vector{1., 2., 3., 4.}; + BOOST_TEST(series == series_ref, boost::test_tools::per_element()); + } +} + +BOOST_AUTO_TEST_CASE(correlator) { + auto const obs = std::make_shared(); + ScriptInterface::Accumulators::Correlator acc; + acc.do_construct({{"obs1", obs}, + {"delta_N", 2}, + {"tau_lin", 4}, + {"tau_max", 2.}, + {"corr_operation", std::string("componentwise_product")}}); + acc.do_call_method("update", VariantMap{}); + acc.do_call_method("finalize", VariantMap{}); + { + BOOST_CHECK_EQUAL(get_value(acc.get_parameter("delta_N")), 2); + BOOST_CHECK_EQUAL(get_value(acc.get_parameter("tau_lin")), 4); + BOOST_CHECK_EQUAL(get_value(acc.get_parameter("tau_max")), 2.); + BOOST_CHECK_EQUAL( + get_value(acc.get_parameter("corr_operation")), + std::string("componentwise_product")); + BOOST_CHECK_EQUAL(get_value(acc.get_parameter("obs1")), obs); + BOOST_CHECK_EQUAL(get_value(acc.get_parameter("obs2")), obs); + } + { + auto const shape_ref = std::vector{{5, 2, 2}}; + // check non-const access + auto const variant = acc.do_call_method("shape", VariantMap{}); + auto const shape = get_value>(variant); + BOOST_TEST(shape == shape_ref, boost::test_tools::per_element()); + // check const access + auto const shape_const = Utils::as_const(acc).accumulator()->shape(); + BOOST_TEST(shape_const == shape_ref, boost::test_tools::per_element()); + } + { + auto const variant = acc.do_call_method("get_correlation", VariantMap{}); + auto const correlation = get_value>(variant); + BOOST_REQUIRE_EQUAL(correlation.size(), 20u); + auto corr_ref = std::vector{1., 4., 9., 16.}; + corr_ref.resize(correlation.size()); + BOOST_TEST(correlation == corr_ref, boost::test_tools::per_element()); + } + { + auto const variant = acc.do_call_method("get_lag_times", VariantMap{}); + auto const lag_times = get_value>(variant); + BOOST_REQUIRE_EQUAL(lag_times.size(), 5u); + auto const lag_times_ref = std::vector{0., -2., -4., -6., -8.}; + BOOST_TEST(lag_times == lag_times_ref, boost::test_tools::per_element()); + } + { + auto const variant = acc.do_call_method("get_samples_sizes", VariantMap{}); + auto const samples_n = get_value>(variant); + BOOST_REQUIRE_EQUAL(samples_n.size(), 5u); + auto const samples_n_ref = std::vector{1, 0, 0, 0, 0}; + BOOST_TEST(samples_n == samples_n_ref, boost::test_tools::per_element()); + } +} + +BOOST_AUTO_TEST_CASE(mean_variance) { + auto const obs = std::make_shared(); + ScriptInterface::Accumulators::MeanVarianceCalculator acc; + acc.do_construct({{"obs", obs}, {"delta_N", 2}}); + acc.do_call_method("update", VariantMap{}); + acc.do_call_method("update", VariantMap{}); + { + BOOST_CHECK_EQUAL(get_value(acc.get_parameter("delta_N")), 2); + BOOST_CHECK_EQUAL(get_value(acc.get_parameter("obs")), obs); + } + { + auto const shape_ref = std::vector{{2, 2}}; + // check non-const access + auto const variant = acc.do_call_method("shape", VariantMap{}); + auto const shape = get_value>(variant); + BOOST_TEST(shape == shape_ref, boost::test_tools::per_element()); + // check const access + auto const shape_const = Utils::as_const(acc).accumulator()->shape(); + BOOST_TEST(shape_const == shape_ref, boost::test_tools::per_element()); + } + { + auto const variant = acc.do_call_method("mean", VariantMap{}); + auto const mean = get_value>(variant); + BOOST_REQUIRE_EQUAL(mean.size(), 4u); + auto const mean_ref = std::vector{1., 2., 3., 4.}; + BOOST_TEST(mean == mean_ref, boost::test_tools::per_element()); + } + { + auto const variant = acc.do_call_method("variance", VariantMap{}); + auto const variance = get_value>(variant); + BOOST_REQUIRE_EQUAL(variance.size(), 4u); + auto const variance_ref = std::vector{0., 0., 0., 0.}; + BOOST_TEST(variance == variance_ref, boost::test_tools::per_element()); + } + { + auto const variant = acc.do_call_method("std_error", VariantMap{}); + auto const stderror = get_value>(variant); + BOOST_REQUIRE_EQUAL(stderror.size(), 4u); + auto const stderror_ref = std::vector{0., 0., 0., 0.}; + BOOST_TEST(stderror == stderror_ref, boost::test_tools::per_element()); + } +} diff --git a/src/script_interface/tests/CMakeLists.txt b/src/script_interface/tests/CMakeLists.txt index d034009b3f1..f4f475a4288 100644 --- a/src/script_interface/tests/CMakeLists.txt +++ b/src/script_interface/tests/CMakeLists.txt @@ -37,3 +37,5 @@ unit_test(NAME packed_variant_test SRC packed_variant_test.cpp DEPENDS ScriptInterface) unit_test(NAME ObjectList_test SRC ObjectList_test.cpp DEPENDS ScriptInterface) unit_test(NAME ObjectMap_test SRC ObjectMap_test.cpp DEPENDS ScriptInterface) +unit_test(NAME Accumulators_test SRC Accumulators_test.cpp DEPENDS + ScriptInterface) From 04e4e1c93f99f9c1e19eb04aa582ca71ee50e499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 10 Jan 2022 18:22:27 +0100 Subject: [PATCH 10/99] tests: Unit test constraints script interface --- src/script_interface/tests/CMakeLists.txt | 2 + .../tests/Constraints_test.cpp | 183 ++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 src/script_interface/tests/Constraints_test.cpp diff --git a/src/script_interface/tests/CMakeLists.txt b/src/script_interface/tests/CMakeLists.txt index f4f475a4288..bdbf22b8c6f 100644 --- a/src/script_interface/tests/CMakeLists.txt +++ b/src/script_interface/tests/CMakeLists.txt @@ -39,3 +39,5 @@ unit_test(NAME ObjectList_test SRC ObjectList_test.cpp DEPENDS ScriptInterface) unit_test(NAME ObjectMap_test SRC ObjectMap_test.cpp DEPENDS ScriptInterface) unit_test(NAME Accumulators_test SRC Accumulators_test.cpp DEPENDS ScriptInterface) +unit_test(NAME Constraints_test SRC Constraints_test.cpp DEPENDS + ScriptInterface) diff --git a/src/script_interface/tests/Constraints_test.cpp b/src/script_interface/tests/Constraints_test.cpp new file mode 100644 index 00000000000..a963996f962 --- /dev/null +++ b/src/script_interface/tests/Constraints_test.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (C) 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 . + */ + +#define BOOST_TEST_MODULE Constraints test +#define BOOST_TEST_DYN_LINK +#include + +#include "script_interface/constraints/ExternalField.hpp" +#include "script_interface/constraints/ExternalPotential.hpp" +#include "script_interface/constraints/HomogeneousMagneticField.hpp" +#include "script_interface/constraints/ShapeBasedConstraint.hpp" +#include "script_interface/constraints/couplings.hpp" +#include "script_interface/constraints/fields.hpp" + +#include "script_interface/get_value.hpp" +#include "script_interface/shapes/NoWhere.hpp" +#include "script_interface/shapes/Shape.hpp" +#include "script_interface/shapes/Sphere.hpp" + +#include +#include + +#include +#include + +using namespace ScriptInterface; + +BOOST_AUTO_TEST_CASE(shape_based_constraint) { + ScriptInterface::Constraints::ShapeBasedConstraint constraint; + { + // check const and non-const access + BOOST_TEST(constraint.constraint()->fits_in_box({})); + BOOST_TEST(Utils::as_const(constraint).constraint()->fits_in_box({})); + BOOST_TEST(constraint.shape_based_constraint()->fits_in_box({})); + } + { + // check shape setter and getter + auto const shape = std::make_shared(); + constraint.set_parameter("shape", shape); + auto const shape_si = + get_value>( + constraint.get_parameter("shape")); + auto const shape_core = shape_si->shape(); + BOOST_REQUIRE_EQUAL(shape_si, shape); + // check distance calculation + auto constexpr inf = std::numeric_limits::infinity(); + auto const inf_vec = Utils::Vector3d::broadcast(inf); + double dist; + Utils::Vector3d vec; + shape_core->calculate_dist({}, dist, vec); + BOOST_CHECK_EQUAL(dist, inf); + BOOST_TEST(vec == inf_vec, boost::test_tools::per_element()); + } + { + // check shape setter and getter + auto shape = std::make_shared(); + shape->set_parameter("radius", 2.); + constraint.set_parameter("shape", shape); + auto const shape_si = + get_value>( + constraint.get_parameter("shape")); + auto const shape_core = shape_si->shape(); + BOOST_REQUIRE_EQUAL(shape_si, shape); + // check distance calculation + auto const vec_ref = Utils::Vector3d{0.5, 0., 0.}; + auto const pos = Utils::Vector3d{2.5, 0., 0.}; + auto const variant = + get_value>(shape_si->do_call_method( + "calc_distance", VariantMap{{"position", pos}})); + auto const dist1 = get_value(variant[0]); + auto const vec1 = get_value(variant[1]); + double dist2; + Utils::Vector3d vec2; + shape_core->calculate_dist(pos, dist2, vec2); + BOOST_CHECK_EQUAL(dist1, 0.5); + BOOST_CHECK_EQUAL(dist2, 0.5); + BOOST_TEST(vec1 == vec_ref, boost::test_tools::per_element()); + BOOST_TEST(vec2 == vec_ref, boost::test_tools::per_element()); + } +} + +BOOST_AUTO_TEST_CASE(field_constraints) { + using namespace FieldCoupling::Coupling; + using namespace FieldCoupling::Fields; + using namespace ScriptInterface::Constraints; + using Gravity = ExternalField>; + { + // check getters and setters + HomogeneousMagneticField field; + auto const h_ref = Utils::Vector3d{1., 2., 3.}; + field.set_parameter("H", Variant{h_ref}); + auto const h = get_value(field.get_parameter("H")); + BOOST_TEST(h == h_ref, boost::test_tools::per_element()); + // check const and non-const access + BOOST_TEST(field.constraint()->fits_in_box({})); + BOOST_TEST(Utils::as_const(field).constraint()->fits_in_box({})); + } + { + // check constructor and getters + Gravity field; + auto const gravity_constant = Utils::Vector3d{1., 2., 3.}; + field.do_construct({{"value", Variant{gravity_constant}}}); + auto const g = get_value(field.get_parameter("value")); + BOOST_TEST(g == gravity_constant, boost::test_tools::per_element()); + // check const and non-const access + BOOST_TEST(field.constraint()->fits_in_box({})); + BOOST_TEST(Utils::as_const(field).constraint()->fits_in_box({})); + } +} + +BOOST_AUTO_TEST_CASE(potential_constraints) { + using namespace FieldCoupling::Coupling; + using namespace FieldCoupling::Fields; + using namespace ScriptInterface::Constraints; + using ElectricPotential = ExternalPotential>; + { + // check constructor and getters + ElectricPotential potential; + auto const grid_spacing_ref = Utils::Vector3d{{1., 1., 1.}}; + auto const field_shape_ref = Utils::Vector3i{{1, 2, 3}}; + auto const field_codim_ref = 1; + auto const field_data_ref = std::vector{1., 2., 3., 4., 5., 6.}; + potential.do_construct( + {{"_field_shape", Variant{field_shape_ref}}, + {"_field_codim", Variant{field_codim_ref}}, + {"_field_data", Variant{std::vector(field_data_ref.begin(), + field_data_ref.end())}}, + {"grid_spacing", Variant{grid_spacing_ref}}}); + auto const grid_spacing = + get_value(potential.get_parameter("grid_spacing")); + auto const field_shape = + get_value(potential.get_parameter("_field_shape")); + auto const field_codim = + get_value(potential.get_parameter("_field_codim")); + auto const field_data = + get_value>(potential.get_parameter("_field_data")); + BOOST_TEST(grid_spacing == grid_spacing_ref, + boost::test_tools::per_element()); + BOOST_TEST(field_shape == field_shape_ref, + boost::test_tools::per_element()); + BOOST_TEST(field_data == field_data_ref, boost::test_tools::per_element()); + BOOST_CHECK_EQUAL(field_codim, field_codim_ref); + // check const and non-const access + BOOST_TEST(potential.constraint()->fits_in_box({})); + BOOST_TEST(Utils::as_const(potential).constraint()->fits_in_box({})); + } + { + // check exception mechanism + ElectricPotential potential; + auto const grid_spacing = Utils::Vector3d{{1., 1., 1.}}; + auto const field_data = std::vector{1., 2., 3., 4., 5., 6.}; + auto const wrong_params1 = + VariantMap{{"_field_shape", Variant{Utils::Vector3i{{0, 2, 3}}}}, + {"_field_codim", Variant{1}}, + {"_field_data", field_data}, + {"grid_spacing", Variant{grid_spacing}}}; + auto const wrong_params2 = + VariantMap{{"_field_shape", Variant{Utils::Vector3i{{1, 2, 3}}}}, + {"_field_codim", Variant{5}}, + {"_field_data", field_data}, + {"grid_spacing", Variant{grid_spacing}}}; + BOOST_CHECK_THROW(potential.do_construct(wrong_params1), + std::runtime_error); + BOOST_CHECK_THROW(potential.do_construct(wrong_params2), + std::runtime_error); + } +} From b6e1ca6007d869ae2966eb5c4eef2fb46d08256a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 11 Jan 2022 11:51:25 +0100 Subject: [PATCH 11/99] tests: Handle AppleClang symbol --- src/script_interface/tests/get_value_test.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/script_interface/tests/get_value_test.cpp b/src/script_interface/tests/get_value_test.cpp index 055605008ba..d6d346ace08 100644 --- a/src/script_interface/tests/get_value_test.cpp +++ b/src/script_interface/tests/get_value_test.cpp @@ -25,6 +25,7 @@ #include "script_interface/get_value.hpp" #include +#include #include #include #include @@ -169,10 +170,12 @@ BOOST_AUTO_TEST_CASE(exceptions) { } { auto const predicate = [](std::string const &why, std::string const &type) { - auto const context = "raised during the creation of a std::unordered_map"; - auto const message = why + " (" + context + "{{1, so_obj}}; From c77ae1d8b7fd15b2d5cbb28002ea74114edbe60b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 12 Jan 2022 11:28:57 +0100 Subject: [PATCH 12/99] tests: Rewrite NpT directional test --- testsuite/python/npt_thermostat.py | 40 ++++++++++++++---------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/testsuite/python/npt_thermostat.py b/testsuite/python/npt_thermostat.py index 9f22a103d35..7a76573c661 100644 --- a/testsuite/python/npt_thermostat.py +++ b/testsuite/python/npt_thermostat.py @@ -19,6 +19,7 @@ import unittest as ut import unittest_decorators as utx import espressomd +import tests_common import numpy as np @@ -112,35 +113,32 @@ def reset_particle_and_box(): self.assertTrue(np.all(np.not_equal(force4, force5))) self.assertTrue(np.all(np.not_equal(boxl4, boxl5))) + @utx.skipIfMissingFeatures("WCA") def test_02__direction(self): """Test for NpT constrained in one direction.""" - system = self.system + data = np.genfromtxt(tests_common.abspath("data/npt_lj_system.data")) + ref_box_l = 1.01 * np.max(data[:, 0:3]) - ref_force_dir = [75.3778877, 35.1267878, 21.5026612] - ref_boxl_dir = [0.7669542, 0.7809505, 0.719799] + system = self.system + system.box_l = 3 * [ref_box_l] + system.part.add(pos=data[:, 0:3], type=len(data) * [2]) + system.non_bonded_inter[2, 2].wca.set_params(epsilon=1., sigma=1.) + system.time_step = 0.01 for n in range(3): - system.box_l = [1., 1., 1.] - system.time_step = 0.01 - vel2force = system.time_step / 2. - p = system.part.add(pos=[0., 0., 0.], v=np.roll([1., 2., 3.], n)) direction = np.roll([True, False, False], n) - ref_boxl = np.roll([ref_boxl_dir[n], 1., 1.], n) - ref_force_rng = np.roll([ref_force_dir[n], 0., 0.], n) - ref_force = np.copy(p.v) / vel2force - system.thermostat.set_npt(kT=1.0, gamma0=2.0, gammav=0.04, seed=42) - system.integrator.set_isotropic_npt(ext_pressure=2.0, piston=0.01, + system.box_l = 3 * [ref_box_l] + system.part.all().pos = data[:, 0:3] + system.part.all().v = data[:, 3:6] + system.thermostat.set_npt(kT=1.0, gamma0=2, gammav=0.004, seed=42) + system.integrator.set_isotropic_npt(ext_pressure=2.0, piston=0.0001, direction=direction) - system.integrator.run(0, recalc_forces=True) - force0 = np.copy(p.v) / vel2force - system.integrator.run(1, recalc_forces=True) - force1 = np.copy(p.v) / vel2force - boxl1 = np.copy(system.box_l) - np.testing.assert_almost_equal(force0, ref_force) - np.testing.assert_almost_equal(force1, ref_force + ref_force_rng) - np.testing.assert_almost_equal(boxl1, ref_boxl) - self.tearDown() + system.integrator.run(20) + box_l_rel = np.copy(system.box_l) / ref_box_l + box_l_rel_ref = np.roll([np.max(box_l_rel), 1., 1.], n) + np.testing.assert_allclose(box_l_rel, box_l_rel_ref, atol=1e-10) + self.assertGreater(np.max(box_l_rel), 2) @utx.skipIfMissingFeatures("VIRTUAL_SITES") def test_07__virtual(self): From 9e8ff66c08a7592c75e83305d86e2eab9d59f2ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 13 Jan 2022 11:33:05 +0100 Subject: [PATCH 13/99] CMake: Add support for Cppcheck --- .cppcheck | 9 +++++++++ CMakeLists.txt | 14 ++++++++++++++ src/core/CellStructure.hpp | 1 + 3 files changed, 24 insertions(+) create mode 100644 .cppcheck diff --git a/.cppcheck b/.cppcheck new file mode 100644 index 00000000000..23c339928ed --- /dev/null +++ b/.cppcheck @@ -0,0 +1,9 @@ +// clang-format off +constParameter +unusedFunction +missingIncludeSystem +noConstructor +noExplicitConstructor +redundantAssignment +uselessAssignmentPtrArg +preprocessorErrorDirective diff --git a/CMakeLists.txt b/CMakeLists.txt index 3635237e693..85b3a368b61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,6 +77,7 @@ option(WITH_STOKESIAN_DYNAMICS "Build with Stokesian Dynamics" OFF) option(WITH_BENCHMARKS "Enable benchmarks" OFF) option(WITH_VALGRIND_INSTRUMENTATION "Build with valgrind instrumentation markers" OFF) +option(WITH_CPPCHECK "Run Cppcheck during compilation" OFF) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") option(WITH_CLANG_TIDY "Run Clang-Tidy during compilation" OFF) endif() @@ -421,6 +422,19 @@ if(WITH_CLANG_TIDY) set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXE};--extra-arg=--cuda-host-only") endif() +if(WITH_CPPCHECK) + find_program(CMAKE_CXX_CPPCHECK NAMES cppcheck) + if(NOT CMAKE_CXX_CPPCHECK) + message(FATAL_ERROR "Could not find the program cppcheck.") + endif() + list(APPEND CMAKE_CXX_CPPCHECK "--enable=all" + "--std=c++${CMAKE_CXX_STANDARD}" "--quiet" "--inline-suppr" + "--suppressions-list=${CMAKE_CURRENT_SOURCE_DIR}/.cppcheck") + if(WARNINGS_ARE_ERRORS) + list(APPEND CMAKE_CXX_CPPCHECK "--error-exitcode=2") + endif() +endif() + # # Testing # diff --git a/src/core/CellStructure.hpp b/src/core/CellStructure.hpp index d792ae99a55..d204154c5ca 100644 --- a/src/core/CellStructure.hpp +++ b/src/core/CellStructure.hpp @@ -153,6 +153,7 @@ struct CellStructure { */ void update_particle_index(int id, Particle *p) { assert(id >= 0); + // cppcheck-suppress assertWithSideEffect assert(not p or id == p->identity()); if (id >= m_particle_index.size()) From c66da1f229523d0fe4566b3cc049b59c96ba3323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 13 Jan 2022 11:50:52 +0100 Subject: [PATCH 14/99] core: Refactor code and fix Cppcheck warnings Fix shadowed global variables, use STL algorithms, remove superfluous extern declarations, remove unused variables, reduce scope of temporary variables. --- src/core/MpiCallbacks.hpp | 4 +- src/core/Observable_stat.hpp | 6 +-- src/core/SystemInterface.hpp | 2 +- src/core/accumulators/Correlator.cpp | 45 +++++++++---------- src/core/actor/ActorList.hpp | 1 + .../electrostatics_magnetostatics/elc.cpp | 2 +- .../p3m-dipolar.cpp | 10 ++--- src/core/energy_inline.hpp | 8 ++-- src/core/forces.cpp | 1 - src/core/ghosts.hpp | 12 ----- src/core/integrators/velocity_verlet_npt.cpp | 4 -- src/core/nonbonded_interactions/ljcos.hpp | 10 ++--- .../nonbonded_interaction_data.cpp | 20 ++++----- .../nonbonded_interaction_data.hpp | 6 +-- src/core/observables/BondAngles.hpp | 12 +++-- src/core/particle_data.cpp | 14 +++--- src/core/random.hpp | 12 ++--- .../tests/ReactionEnsemble_test.cpp | 1 - .../tests/particle_tracking_test.cpp | 3 -- src/core/statistics_chain.cpp | 29 ++++++------ src/core/thermostats/npt_inline.hpp | 30 +++++-------- src/core/tuning.cpp | 5 +-- src/core/unit_tests/energy_test.cpp | 21 +++++---- .../field_coupling_force_field_test.cpp | 1 - src/script_interface/ObjectList.hpp | 2 +- .../CollisionDetection.hpp | 2 +- src/utils/tests/RunningAverage_test.cpp | 31 ++++++------- src/utils/tests/scatter_buffer_test.cpp | 16 +++---- 28 files changed, 137 insertions(+), 173 deletions(-) diff --git a/src/core/MpiCallbacks.hpp b/src/core/MpiCallbacks.hpp index 6c28cf079f1..7d2543c70fb 100644 --- a/src/core/MpiCallbacks.hpp +++ b/src/core/MpiCallbacks.hpp @@ -411,9 +411,9 @@ class MpiCallbacks { static auto &static_callbacks() { static std::vector< std::pair>> - m_callbacks; + callbacks; - return m_callbacks; + return callbacks; } public: diff --git a/src/core/Observable_stat.hpp b/src/core/Observable_stat.hpp index 72109bcf892..5f0597824eb 100644 --- a/src/core/Observable_stat.hpp +++ b/src/core/Observable_stat.hpp @@ -64,10 +64,8 @@ class Observable_stat { /** Rescale values */ void rescale(double volume) { - auto const factor = 1. / volume; - for (auto &e : m_data) { - e *= factor; - } + auto const fac = 1. / volume; + boost::transform(m_data, m_data.begin(), [fac](auto e) { return e * fac; }); } /** Contribution from linear and angular kinetic energy (accumulated). */ diff --git a/src/core/SystemInterface.hpp b/src/core/SystemInterface.hpp index 15b79825362..0a1abd0dec6 100644 --- a/src/core/SystemInterface.hpp +++ b/src/core/SystemInterface.hpp @@ -98,7 +98,7 @@ class SystemInterface { virtual Utils::Vector3d box() const = 0; private: - std::string error_message(std::string property) const { + std::string error_message(std::string const &property) const { return "No GPU available or particle " + property + " not compiled in."; } }; diff --git a/src/core/accumulators/Correlator.cpp b/src/core/accumulators/Correlator.cpp index 90e73e659b0..1c0180eb556 100644 --- a/src/core/accumulators/Correlator.cpp +++ b/src/core/accumulators/Correlator.cpp @@ -324,17 +324,21 @@ void Correlator::update() { // Let's find out how far we have to go back in the hierarchy to make space // for the new value - int i = 0; - while (true) { - if (((t - ((m_tau_lin + 1) * ((1 << (i + 1)) - 1) + 1)) % (1 << (i + 1)) == - 0)) { - if (i < (m_hierarchy_depth - 1) && n_vals[i] > m_tau_lin) { - highest_level_to_compress += 1; - i++; - } else + { + auto const max_depth = m_hierarchy_depth - 1; + int i = 0; + while (true) { + if (i >= max_depth or n_vals[i] <= m_tau_lin) { break; - } else - break; + } + auto const modulo = 1 << (i + 1); + auto const remainder = (t - (m_tau_lin + 1) * (modulo - 1) - 1) % modulo; + if (remainder != 0) { + break; + } + highest_level_to_compress += 1; + i++; + } } // Now we know we must make space on the levels 0..highest_level_to_compress @@ -426,24 +430,19 @@ int Correlator::finalize() { while (vals_ll) { // Check, if we will want to push the value from the lowest level - int highest_level_to_compress = -1; - if (vals_ll % 2) { - highest_level_to_compress = ll; - } + auto highest_level_to_compress = (vals_ll % 2) ? ll : -1; - int i = ll + 1; // lowest level for which we have to check for compression // Let's find out how far we have to go back in the hierarchy to make // space for the new value - while (highest_level_to_compress > -1) { - if (n_vals[i] % 2) { - if (i < (m_hierarchy_depth - 1) && n_vals[i] > m_tau_lin) { - highest_level_to_compress += 1; - i++; - } else { + { + auto const max_depth = m_hierarchy_depth - 1; + int i = ll + 1; // lowest level for which to check for compression + while (highest_level_to_compress > -1) { + if (i >= max_depth or n_vals[i] % 2 == 0 or n_vals[i] <= m_tau_lin) { break; } - } else { - break; + highest_level_to_compress += 1; + i++; } } vals_ll -= 1; diff --git a/src/core/actor/ActorList.hpp b/src/core/actor/ActorList.hpp index 3de187d9491..5a30c25a96c 100644 --- a/src/core/actor/ActorList.hpp +++ b/src/core/actor/ActorList.hpp @@ -20,6 +20,7 @@ #define _ACTOR_ACTORLIST_HPP #include "Actor.hpp" + #include class ActorList : public std::vector { diff --git a/src/core/electrostatics_magnetostatics/elc.cpp b/src/core/electrostatics_magnetostatics/elc.cpp index 6c60f0f5fab..b0ed9f95009 100644 --- a/src/core/electrostatics_magnetostatics/elc.cpp +++ b/src/core/electrostatics_magnetostatics/elc.cpp @@ -526,7 +526,7 @@ void setup_PoQ(std::size_t index, double omega, clear_vec(lclimge, size); clear_vec(gblcblk, size); - auto &sc_cache = (axis == PoQ::P) ? scxcache : scycache; + auto const &sc_cache = (axis == PoQ::P) ? scxcache : scycache; std::size_t ic = 0; auto const o = (index - 1) * particles.size(); diff --git a/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp b/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp index c068a228583..b21ee5d06ef 100644 --- a/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp +++ b/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp @@ -380,7 +380,7 @@ template struct AssignForces { double dp3m_calc_kspace_forces(bool force_flag, bool energy_flag, const ParticleRange &particles) { - int i, d, d_rs, ind, j[3]; + int i, ind, j[3]; /* k-space energy */ double k_space_energy_dip = 0.0; double tmp0, tmp1; @@ -508,8 +508,8 @@ double dp3m_calc_kspace_forces(bool force_flag, bool energy_flag, } /* Force component loop */ - for (d = 0; d < 3; d++) { - d_rs = (d + dp3m.ks_pnum) % 3; + for (int d = 0; d < 3; d++) { + auto const d_rs = (d + dp3m.ks_pnum) % 3; ind = 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]++) { @@ -576,8 +576,8 @@ double dp3m_calc_kspace_forces(bool force_flag, bool energy_flag, } /* Force component loop */ - for (d = 0; d < 3; d++) { /* direction in k-space: */ - d_rs = (d + dp3m.ks_pnum) % 3; + for (int d = 0; d < 3; d++) { /* direction in k-space: */ + auto const d_rs = (d + dp3m.ks_pnum) % 3; ind = 0; for (j[0] = 0; j[0] < dp3m.fft.plan[3].new_mesh[0]; j[0]++) { // j[0]=n_y diff --git a/src/core/energy_inline.hpp b/src/core/energy_inline.hpp index 41cc2fa565b..a43d1279633 100644 --- a/src/core/energy_inline.hpp +++ b/src/core/energy_inline.hpp @@ -230,7 +230,7 @@ calc_bonded_energy(Bonded_IA_Parameters const &iaparams, Particle const &p1, } #endif #ifdef BOND_CONSTRAINT - if (auto const *iap = boost::get(&iaparams)) { + if (boost::get(&iaparams)) { return boost::optional(0); } #endif @@ -239,7 +239,7 @@ calc_bonded_energy(Bonded_IA_Parameters const &iaparams, Particle const &p1, return iap->energy(dx); } #endif - if (auto const *iap = boost::get(&iaparams)) { + if (boost::get(&iaparams)) { return boost::optional(0); } throw BondUnknownTypeError(); @@ -257,7 +257,7 @@ calc_bonded_energy(Bonded_IA_Parameters const &iaparams, Particle const &p1, if (auto const *iap = boost::get(&iaparams)) { return iap->energy(p1.r.p, p2->r.p, p3->r.p); } - if (auto const *iap = boost::get(&iaparams)) { + if (boost::get(&iaparams)) { runtimeWarningMsg() << "Unsupported bond type " + std::to_string(iaparams.which()) + " in energy calculation."; @@ -272,7 +272,7 @@ calc_bonded_energy(Bonded_IA_Parameters const &iaparams, Particle const &p1, if (auto const *iap = boost::get(&iaparams)) { return iap->energy(p2->r.p, p1.r.p, p3->r.p, p4->r.p); } - if (auto const *iap = boost::get(&iaparams)) { + if (boost::get(&iaparams)) { runtimeWarningMsg() << "Unsupported bond type " + std::to_string(iaparams.which()) + " in energy calculation."; diff --git a/src/core/forces.cpp b/src/core/forces.cpp index d0758d36e2a..c7eb548456f 100644 --- a/src/core/forces.cpp +++ b/src/core/forces.cpp @@ -1,4 +1,3 @@ - /* * Copyright (C) 2010-2019 The ESPResSo project * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 diff --git a/src/core/ghosts.hpp b/src/core/ghosts.hpp index de0df7e25fc..a298240bec4 100644 --- a/src/core/ghosts.hpp +++ b/src/core/ghosts.hpp @@ -136,10 +136,6 @@ enum : unsigned { GHOSTTRANS_BONDS = 128u }; -/** \name Data Types */ -/************************************************************/ -/**@{*/ - struct GhostCommunication { /** Communication type. */ int type; @@ -167,17 +163,9 @@ struct GhostCommunicator { std::vector communications; }; -/**@}*/ - -/** \name Exported Functions */ -/************************************************************/ -/**@{*/ - /** * @brief Do a ghost communication with caller specified data parts. */ void ghost_communicator(const GhostCommunicator &gcr, unsigned int data_parts); -/**@}*/ - #endif diff --git a/src/core/integrators/velocity_verlet_npt.cpp b/src/core/integrators/velocity_verlet_npt.cpp index 1a758952378..c56279b9031 100644 --- a/src/core/integrators/velocity_verlet_npt.cpp +++ b/src/core/integrators/velocity_verlet_npt.cpp @@ -45,7 +45,6 @@ void velocity_verlet_npt_propagate_vel_final(const ParticleRange &particles, double time_step) { - extern IsotropicNptThermostat npt_iso; nptiso.p_vel = {}; for (auto &p : particles) { @@ -69,7 +68,6 @@ 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) { - extern IsotropicNptThermostat npt_iso; /* finalize derivation of p_inst */ nptiso.p_inst = 0.0; for (int i = 0; i < 3; i++) { @@ -105,7 +103,6 @@ void velocity_verlet_npt_propagate_pos(const ParticleRange &particles, pow(nptiso.volume, 2.0 / nptiso.dimension); nptiso.volume += nptiso.inv_piston * nptiso.p_diff * 0.5 * time_step; if (nptiso.volume < 0.0) { - runtimeErrorMsg() << "your choice of piston= " << nptiso.piston << ", dt= " << time_step << ", p_diff= " << nptiso.p_diff @@ -163,7 +160,6 @@ void velocity_verlet_npt_propagate_pos(const ParticleRange &particles, void velocity_verlet_npt_propagate_vel(const ParticleRange &particles, double time_step) { - extern IsotropicNptThermostat npt_iso; nptiso.p_vel = {}; for (auto &p : particles) { diff --git a/src/core/nonbonded_interactions/ljcos.hpp b/src/core/nonbonded_interactions/ljcos.hpp index 5a0821e9db5..6115f3ccb96 100644 --- a/src/core/nonbonded_interactions/ljcos.hpp +++ b/src/core/nonbonded_interactions/ljcos.hpp @@ -73,12 +73,10 @@ inline double ljcos_pair_energy(IA_parameters const &ia_params, double dist) { return 4.0 * ia_params.ljcos.eps * (Utils::sqr(frac6) - frac6); } /* cosine part of the potential. */ - if (dist < (ia_params.ljcos.cut + ia_params.ljcos.offset)) { - return .5 * ia_params.ljcos.eps * - (cos(ia_params.ljcos.alfa * Utils::sqr(r_off) + - ia_params.ljcos.beta) - - 1.); - } + return .5 * ia_params.ljcos.eps * + (cos(ia_params.ljcos.alfa * Utils::sqr(r_off) + + ia_params.ljcos.beta) - + 1.); } return 0.0; } diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index 4068eccf5f4..bbeeeae88d8 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -49,7 +49,7 @@ * variables *****************************************/ int max_seen_particle_type = 0; -std::vector ia_params; +std::vector nonbonded_ia_params; double min_global_cut = INACTIVE_CUTOFF; @@ -71,23 +71,23 @@ static void mpi_realloc_ia_params_local(int new_size) { } max_seen_particle_type = new_size; - std::swap(ia_params, new_params); + std::swap(nonbonded_ia_params, new_params); } REGISTER_CALLBACK(mpi_realloc_ia_params_local) -/** Increase the size of the @ref ia_params vector. */ +/** Increase the size of the @ref nonbonded_ia_params vector. */ inline void mpi_realloc_ia_params(int new_size) { mpi_call_all(mpi_realloc_ia_params_local, new_size); } static void mpi_bcast_all_ia_params_local() { - boost::mpi::broadcast(comm_cart, ia_params, 0); + boost::mpi::broadcast(comm_cart, nonbonded_ia_params, 0); } REGISTER_CALLBACK(mpi_bcast_all_ia_params_local) -/** Broadcast @ref ia_params to all nodes. */ +/** Broadcast @ref nonbonded_ia_params to all nodes. */ inline void mpi_bcast_all_ia_params() { mpi_call_all(mpi_bcast_all_ia_params_local); } @@ -100,7 +100,7 @@ IA_parameters *get_ia_param_safe(int i, int j) { std::string ia_params_get_state() { std::stringstream out; boost::archive::binary_oarchive oa(out); - oa << ia_params; + oa << nonbonded_ia_params; oa << max_seen_particle_type; return out.str(); } @@ -110,8 +110,8 @@ void ia_params_set_state(std::string const &state) { iostreams::array_source src(state.data(), state.size()); iostreams::stream ss(src); boost::archive::binary_iarchive ia(ss); - ia_params.clear(); - ia >> ia_params; + nonbonded_ia_params.clear(); + ia >> nonbonded_ia_params; ia >> max_seen_particle_type; mpi_realloc_ia_params(max_seen_particle_type); mpi_bcast_all_ia_params(); @@ -202,7 +202,7 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { double maximal_cutoff_nonbonded() { auto max_cut_nonbonded = INACTIVE_CUTOFF; - for (auto &data : ia_params) { + for (auto &data : nonbonded_ia_params) { data.max_cut = recalc_maximal_cutoff(data); max_cut_nonbonded = std::max(max_cut_nonbonded, data.max_cut); } @@ -211,7 +211,7 @@ double maximal_cutoff_nonbonded() { } void reset_ia_params() { - boost::fill(ia_params, IA_parameters{}); + boost::fill(nonbonded_ia_params, IA_parameters{}); mpi_bcast_all_ia_params(); } diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index daab467c72e..2ddf85705d9 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -275,7 +275,7 @@ struct IA_parameters { #endif }; -extern std::vector ia_params; +extern std::vector nonbonded_ia_params; /** Maximal particle type seen so far. */ extern int max_seen_particle_type; @@ -306,8 +306,8 @@ inline IA_parameters *get_ia_param(int i, int j) { assert(i >= 0 && i < max_seen_particle_type); assert(j >= 0 && j < max_seen_particle_type); - return &ia_params[Utils::upper_triangular(std::min(i, j), std::max(i, j), - max_seen_particle_type)]; + return &nonbonded_ia_params[Utils::upper_triangular( + std::min(i, j), std::max(i, j), max_seen_particle_type)]; } /** Get interaction parameters between particle types i and j. diff --git a/src/core/observables/BondAngles.hpp b/src/core/observables/BondAngles.hpp index 833afb4b7fe..d1c0d7b2ede 100644 --- a/src/core/observables/BondAngles.hpp +++ b/src/core/observables/BondAngles.hpp @@ -25,6 +25,8 @@ #include +#include + #include #include #include @@ -56,13 +58,9 @@ class BondAngles : public PidObservable { for (std::size_t i = 0, end = n_values(); i < end; i++) { auto v2 = box_geo.get_mi_vector(traits.position(particles[i + 2]), traits.position(particles[i + 1])); - auto n2 = v2.norm(); - auto cosine = (v1 * v2) / (n1 * n2); - // sanitize cosine value - if (cosine > TINY_COS_VALUE) - cosine = TINY_COS_VALUE; - else if (cosine < -TINY_COS_VALUE) - cosine = -TINY_COS_VALUE; + auto const n2 = v2.norm(); + auto const cosine = boost::algorithm::clamp( + (v1 * v2) / (n1 * n2), -TINY_COS_VALUE, TINY_COS_VALUE); /* to reduce computational time, after calculating an angle ijk, the * vector r_ij takes the value r_jk, but to orient it correctly, it has * to be multiplied -1; it's cheaper to do this operation on a double diff --git a/src/core/particle_data.cpp b/src/core/particle_data.cpp index bdf049d0cb6..b10b3cdaefc 100644 --- a/src/core/particle_data.cpp +++ b/src/core/particle_data.cpp @@ -62,6 +62,12 @@ #include #include +static bool type_list_enable; +static std::unordered_map> particle_type_map; + +void remove_id_from_map(int part_id, int type); +void add_id_to_type_map(int part_id, int type); + namespace { /** * @brief A generic particle update. @@ -382,14 +388,6 @@ void mpi_update_particle_property(int id, const T &value) { mpi_update_particle(id, value); } -/************************************************ - * variables - ************************************************/ -bool type_list_enable; -std::unordered_map> particle_type_map{}; -void remove_id_from_map(int part_id, int type); -void add_id_to_type_map(int part_id, int type); - /** * @brief id -> rank */ diff --git a/src/core/random.hpp b/src/core/random.hpp index 0ff4a000d0c..40522cceedf 100644 --- a/src/core/random.hpp +++ b/src/core/random.hpp @@ -166,11 +166,13 @@ auto noise_gaussian(uint64_t counter, uint32_t seed, int key1, int key2 = 0) { // sin/cos are evaluated simultaneously by gcc or separately by Clang Utils::VectorXd noise{}; constexpr double two_pi = 2.0 * Utils::pi(); - auto const modulo = sqrt(-2.0 * log(u[0])); - auto const angle = two_pi * u[1]; - noise[0] = modulo * cos(angle); - if (N > 1) { - noise[1] = modulo * sin(angle); + { + auto const modulo = sqrt(-2.0 * log(u[0])); + auto const angle = two_pi * u[1]; + noise[0] = modulo * cos(angle); + if (N > 1) { + noise[1] = modulo * sin(angle); + } } if (N > 2) { auto const modulo = sqrt(-2.0 * log(u[2])); diff --git a/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp b/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp index 436fc3fa779..3d172688cae 100644 --- a/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp +++ b/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp @@ -75,7 +75,6 @@ BOOST_AUTO_TEST_CASE(ReactionEnsemble_test) { SingleReaction const reaction(2., {type_A}, {1}, {type_B, type_C}, {3, 4}); // check acceptance probability - constexpr auto g = factorial_Ni0_divided_by_factorial_Ni0_plus_nu_i; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { for (int k = 0; k < 3; ++k) { diff --git a/src/core/reaction_methods/tests/particle_tracking_test.cpp b/src/core/reaction_methods/tests/particle_tracking_test.cpp index cdffe2cef92..fdf80b802eb 100644 --- a/src/core/reaction_methods/tests/particle_tracking_test.cpp +++ b/src/core/reaction_methods/tests/particle_tracking_test.cpp @@ -32,15 +32,12 @@ #include -#include #include #include // Check the mechanism that tracks particles of a certain type and the // function that selects a random particle in the pool of tracked particles. BOOST_FIXTURE_TEST_CASE(particle_type_map_test, ParticleFactory) { - constexpr double tol = 100 * std::numeric_limits::epsilon(); - // particle properties int const type = 10; int const pid = 1; diff --git a/src/core/statistics_chain.cpp b/src/core/statistics_chain.cpp index 1a174634b19..efdb887d141 100644 --- a/src/core/statistics_chain.cpp +++ b/src/core/statistics_chain.cpp @@ -19,8 +19,8 @@ * along with this program. If not, see . */ /** \file - Implementation of \ref statistics_chain.hpp "statistics_chain.hpp". -*/ + * Implementation of \ref statistics_chain.hpp "statistics_chain.hpp". + */ #include "statistics_chain.hpp" #include "Particle.hpp" @@ -28,6 +28,7 @@ #include "particle_data.hpp" #include +#include #include #include @@ -61,7 +62,6 @@ std::array calc_re(int chain_start, int chain_n_chains, std::array calc_rg(int chain_start, int chain_n_chains, int chain_length) { double r_G = 0.0, r_G2 = 0.0, r_G4 = 0.0; - double tmp; std::array rg; for (int i = 0; i < chain_n_chains; i++) { @@ -79,34 +79,35 @@ std::array calc_rg(int chain_start, int chain_n_chains, M += p.p.mass; } r_CM /= M; - tmp = 0.0; + double tmp = 0.0; for (int j = 0; j < chain_length; ++j) { auto const &p = get_particle_data(chain_start + i * chain_length + j); Utils::Vector3d const d = unfolded_position(p.r.p, p.l.i, box_geo.length()) - r_CM; tmp += d.norm2(); } - tmp /= (double)chain_length; + tmp /= static_cast(chain_length); r_G += sqrt(tmp); r_G2 += tmp; r_G4 += tmp * tmp; } - tmp = static_cast(chain_n_chains); + auto const tmp = static_cast(chain_n_chains); rg[0] = r_G / tmp; rg[2] = r_G2 / tmp; - rg[1] = sqrt(rg[2] - rg[0] * rg[0]); - rg[3] = sqrt(r_G4 / tmp - rg[2] * rg[2]); + rg[1] = sqrt(rg[2] - Utils::sqr(rg[0])); + rg[3] = sqrt(r_G4 / tmp - Utils::sqr(rg[2])); return rg; } std::array calc_rh(int chain_start, int chain_n_chains, int chain_length) { - double r_H = 0.0, r_H2 = 0.0, ri = 0.0, prefac, tmp; + double r_H = 0.0, r_H2 = 0.0; std::array rh; - prefac = 0.5 * chain_length * (chain_length - 1); + auto const chain_l = static_cast(chain_length); + auto const prefac = 0.5 * chain_l * (chain_l - 1.); for (int p = 0; p < chain_n_chains; p++) { - ri = 0.0; + double ri = 0.0; for (int i = chain_start + chain_length * p; i < chain_start + chain_length * (p + 1); i++) { auto const &p1 = get_particle_data(i); @@ -117,12 +118,12 @@ std::array calc_rh(int chain_start, int chain_n_chains, ri += 1.0 / d.norm(); } } - tmp = prefac / ri; + auto const tmp = prefac / ri; r_H += tmp; r_H2 += tmp * tmp; } - tmp = static_cast(chain_n_chains); + auto const tmp = static_cast(chain_n_chains); rh[0] = r_H / tmp; - rh[1] = sqrt(r_H2 / tmp - rh[0] * rh[0]); + rh[1] = sqrt(r_H2 / tmp - Utils::sqr(rh[0])); return rh; } diff --git a/src/core/thermostats/npt_inline.hpp b/src/core/thermostats/npt_inline.hpp index 72afebe7ed3..398147c277e 100644 --- a/src/core/thermostats/npt_inline.hpp +++ b/src/core/thermostats/npt_inline.hpp @@ -49,16 +49,13 @@ friction_therm0_nptiso(IsotropicNptThermostat const &npt_iso, static_assert(step == 1 or step == 2, "NPT only has 2 integration steps"); constexpr auto const salt = (step == 1) ? RNGSalt::NPTISO0_HALF_STEP1 : RNGSalt::NPTISO0_HALF_STEP2; - if (thermo_switch & THERMO_NPT_ISO) { - if (npt_iso.pref_noise_0 > 0.0) { - return npt_iso.pref_rescale_0 * vel + - npt_iso.pref_noise_0 * - Random::noise_uniform(npt_iso.rng_counter(), - npt_iso.rng_seed(), p_identity); - } - return npt_iso.pref_rescale_0 * vel; + if (npt_iso.pref_noise_0 > 0.0) { + return npt_iso.pref_rescale_0 * vel + + npt_iso.pref_noise_0 * + Random::noise_uniform(npt_iso.rng_counter(), + npt_iso.rng_seed(), p_identity); } - return {}; + return npt_iso.pref_rescale_0 * vel; } /** Add p_diff-dependent noise and friction for NpT-sims to \ref @@ -66,16 +63,13 @@ friction_therm0_nptiso(IsotropicNptThermostat const &npt_iso, */ inline double friction_thermV_nptiso(IsotropicNptThermostat const &npt_iso, double p_diff) { - if (thermo_switch & THERMO_NPT_ISO) { - if (npt_iso.pref_noise_V > 0.0) { - return npt_iso.pref_rescale_V * p_diff + - npt_iso.pref_noise_V * - Random::noise_uniform( - npt_iso.rng_counter(), npt_iso.rng_seed(), 0); - } - return npt_iso.pref_rescale_V * p_diff; + if (npt_iso.pref_noise_V > 0.0) { + return npt_iso.pref_rescale_V * p_diff + + npt_iso.pref_noise_V * + Random::noise_uniform( + npt_iso.rng_counter(), npt_iso.rng_seed(), 0); } - return 0.0; + return npt_iso.pref_rescale_V * p_diff; } #endif // NPT diff --git a/src/core/tuning.cpp b/src/core/tuning.cpp index 1a332acd270..e9c4f8a76df 100644 --- a/src/core/tuning.cpp +++ b/src/core/tuning.cpp @@ -97,7 +97,6 @@ void tune_skin(double min_skin, double max_skin, double tol, int int_steps, double a = min_skin; double b = max_skin; - double time_a, time_b; /* The maximal skin is the remainder from the required cutoff to * the maximal range that can be supported by the cell system, but @@ -111,10 +110,10 @@ void tune_skin(double min_skin, double max_skin, double tol, int int_steps, while (fabs(a - b) > tol) { mpi_set_skin(a); - time_a = time_calc(int_steps); + auto const time_a = time_calc(int_steps); mpi_set_skin(b); - time_b = time_calc(int_steps); + auto const time_b = time_calc(int_steps); if (time_a > time_b) { a = 0.5 * (a + b); diff --git a/src/core/unit_tests/energy_test.cpp b/src/core/unit_tests/energy_test.cpp index 8cd7cc7710c..eb261743027 100644 --- a/src/core/unit_tests/energy_test.cpp +++ b/src/core/unit_tests/energy_test.cpp @@ -24,19 +24,22 @@ #include "energy_inline.hpp" BOOST_AUTO_TEST_CASE(translational_kinetic_energy_) { - - Particle p; + // real particle + { + Particle p; #ifdef MASS - p.p.mass = 2.; + p.p.mass = 2.; #endif - p.m.v = {3., 4., 5.}; + p.m.v = {3., 4., 5.}; - auto const expected = 0.5 * p.p.mass * p.m.v.norm2(); - BOOST_TEST(translational_kinetic_energy(p) == expected); + auto const expected = 0.5 * p.p.mass * p.m.v.norm2(); + BOOST_TEST(translational_kinetic_energy(p) == expected); + } -/* virtual */ -#ifdef VIRTUAL_SITES + // virtual particle { +#ifdef VIRTUAL_SITES + Particle p; #ifdef MASS p.p.mass = 2.; @@ -46,8 +49,8 @@ BOOST_AUTO_TEST_CASE(translational_kinetic_energy_) { auto const expected = 0.; BOOST_TEST(translational_kinetic_energy(p) == expected); - } #endif + } } BOOST_AUTO_TEST_CASE(rotational_kinetic_energy_) { diff --git a/src/core/unit_tests/field_coupling_force_field_test.cpp b/src/core/unit_tests/field_coupling_force_field_test.cpp index 145b673eae3..b0761d47abc 100644 --- a/src/core/unit_tests/field_coupling_force_field_test.cpp +++ b/src/core/unit_tests/field_coupling_force_field_test.cpp @@ -113,7 +113,6 @@ BOOST_AUTO_TEST_CASE(ForceField_test) { auto ff = ForceField, DummyVectorField>(Id{}, DummyVectorField{}); const Utils::Vector3d x{1., 2., 3.}; - const int p = 5; BOOST_CHECK((9. * x) == ff.force(5, x, 9.)); BOOST_CHECK(1 == ff.coupling().count); diff --git a/src/script_interface/ObjectList.hpp b/src/script_interface/ObjectList.hpp index 19b57e892fe..b4196679bdf 100644 --- a/src/script_interface/ObjectList.hpp +++ b/src/script_interface/ObjectList.hpp @@ -31,6 +31,7 @@ #include #include #include +#include #include namespace ScriptInterface { @@ -108,7 +109,6 @@ class ObjectList : public BaseType { if (method == "get_elements") { std::vector ret; ret.reserve(m_elements.size()); - for (auto const &e : m_elements) ret.emplace_back(e); diff --git a/src/script_interface/collision_detection/CollisionDetection.hpp b/src/script_interface/collision_detection/CollisionDetection.hpp index 4b4cf8d040f..0af74c21dc4 100644 --- a/src/script_interface/collision_detection/CollisionDetection.hpp +++ b/src/script_interface/collision_detection/CollisionDetection.hpp @@ -63,7 +63,7 @@ class CollisionDetection : public AutoParameters { const VariantMap ¶ms) override { if (name == "validate") { return validate_collision_parameters(); - }; + } return none; }; }; diff --git a/src/utils/tests/RunningAverage_test.cpp b/src/utils/tests/RunningAverage_test.cpp index 1c4989df661..f9997131fa8 100644 --- a/src/utils/tests/RunningAverage_test.cpp +++ b/src/utils/tests/RunningAverage_test.cpp @@ -23,10 +23,11 @@ #define BOOST_TEST_DYN_LINK #include -#include "utils/statistics/RunningAverage.hpp" - #include "random_sequence.hpp" +#include +#include + #include #include #include @@ -80,9 +81,8 @@ BOOST_AUTO_TEST_CASE(simple_variance_check) { } BOOST_AUTO_TEST_CASE(mean_and_variance) { + auto constexpr sample_size = sizeof(RandomSequence::values) / sizeof(double); Utils::Statistics::RunningAverage running_average; - const std::size_t sample_size = - sizeof(RandomSequence::values) / sizeof(double); for (auto const &val : RandomSequence::values) { running_average.add_sample(val); @@ -91,18 +91,19 @@ BOOST_AUTO_TEST_CASE(mean_and_variance) { BOOST_CHECK(running_average.n() == sample_size); /* Directly calculate the mean from the data */ - const double m_mean = std::accumulate(std::begin(RandomSequence::values), - std::end(RandomSequence::values), 0.0) / - sample_size; + const double mean = std::accumulate(std::begin(RandomSequence::values), + std::end(RandomSequence::values), 0.0) / + sample_size; - BOOST_CHECK(std::fabs(running_average.avg() - m_mean) <= 1e-12); + BOOST_CHECK_SMALL((running_average.avg() - mean), 1e-12); /* Directly calculate the variance from the data */ - double m_var = 0.0; - for (auto const &val : RandomSequence::values) { - m_var += (val - m_mean) * (val - m_mean); - } - m_var /= sample_size; - - BOOST_CHECK(std::fabs(running_average.var() - m_var) <= 1e-12); + auto const var = std::accumulate(std::begin(RandomSequence::values), + std::end(RandomSequence::values), 0.0, + [=](double acc, double val) { + return acc + Utils::sqr(val - mean); + }) / + sample_size; + + BOOST_CHECK_SMALL((running_average.var() - var), 1e-12); } diff --git a/src/utils/tests/scatter_buffer_test.cpp b/src/utils/tests/scatter_buffer_test.cpp index e4d4a7492bb..1704f3a647a 100644 --- a/src/utils/tests/scatter_buffer_test.cpp +++ b/src/utils/tests/scatter_buffer_test.cpp @@ -24,24 +24,18 @@ #define BOOST_TEST_DYN_LINK #include -#include "utils/mpi/scatter_buffer.hpp" +#include #include #include #include -using Utils::Mpi::scatter_buffer; -namespace mpi = boost::mpi; - -void check_pointer(mpi::communicator comm, int root) { +void check_pointer(boost::mpi::communicator comm, int root) { std::vector buf; - if (comm.rank() == root) { auto const n = comm.size(); - const int total_size = n * (n + 1) / 2; - - std::vector buf; + auto const total_size = n * (n + 1) / 2; for (int i = 1; i <= comm.size(); i++) { for (int j = 0; j < i; j++) { @@ -64,12 +58,12 @@ void check_pointer(mpi::communicator comm, int root) { } BOOST_AUTO_TEST_CASE(pointer) { - mpi::communicator world; + boost::mpi::communicator world; check_pointer(world, 0); } BOOST_AUTO_TEST_CASE(pointer_root) { - mpi::communicator world; + boost::mpi::communicator world; auto root = (world.size() >= 3) ? world.size() - 2 : world.size() - 1; check_pointer(world, root); From 6b252d9d011d3aeae0439a4cd25bad1cf1d03c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 14 Jan 2022 15:59:38 +0100 Subject: [PATCH 15/99] core: Refactor DomainDecomposition --- src/core/DomainDecomposition.cpp | 40 ++++++++++++++------------------ 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/src/core/DomainDecomposition.cpp b/src/core/DomainDecomposition.cpp index 9d65a01f0cd..9ebebd83198 100644 --- a/src/core/DomainDecomposition.cpp +++ b/src/core/DomainDecomposition.cpp @@ -235,11 +235,10 @@ void DomainDecomposition::resort(bool global, } void DomainDecomposition::mark_cells() { - int cnt_c = 0; - m_local_cells.clear(); m_ghost_cells.clear(); + int cnt_c = 0; for (int o = 0; o < ghost_cell_grid[2]; o++) for (int n = 0; n < ghost_cell_grid[1]; n++) for (int m = 0; m < ghost_cell_grid[0]; m++) { @@ -250,6 +249,7 @@ void DomainDecomposition::mark_cells() { m_ghost_cells.push_back(&cells.at(cnt_c++)); } } + void DomainDecomposition::fill_comm_cell_lists(ParticleList **part_lists, const Utils::Vector3i &lc, const Utils::Vector3i &hc) { @@ -286,42 +286,37 @@ void DomainDecomposition::create_cell_grid(double range) { auto const cart_info = Utils::Mpi::cart_get<3>(m_comm); int n_local_cells; - double cell_range[3]; - - /* initialize */ - cell_range[0] = cell_range[1] = cell_range[2] = range; - - /* Min num cells can not be smaller than calc_processor_min_num_cells. */ - int min_num_cells = calc_processor_min_num_cells(); + auto cell_range = Utils::Vector3d::broadcast(range); + auto const min_num_cells = calc_processor_min_num_cells(); if (range <= 0.) { /* this is the non-interacting case */ auto const cells_per_dir = - static_cast(std::ceil(std::pow(min_num_cells, 1. / 3.))); + static_cast(std::ceil(std::cbrt(min_num_cells))); cell_grid = Utils::Vector3i::broadcast(cells_per_dir); n_local_cells = Utils::product(cell_grid); } else { /* Calculate initial cell grid */ - double volume = m_local_box.length()[0]; - for (int i = 1; i < 3; i++) - volume *= m_local_box.length()[i]; - double scale = pow(DomainDecomposition::max_num_cells / volume, 1. / 3.); + auto const &local_box_l = m_local_box.length(); + auto const volume = Utils::product(local_box_l); + auto const scale = std::cbrt(DomainDecomposition::max_num_cells / volume); + for (int i = 0; i < 3; i++) { /* this is at least 1 */ - cell_grid[i] = (int)ceil(m_local_box.length()[i] * scale); - cell_range[i] = m_local_box.length()[i] / cell_grid[i]; + cell_grid[i] = static_cast(std::ceil(local_box_l[i] * scale)); + cell_range[i] = local_box_l[i] / static_cast(cell_grid[i]); if (cell_range[i] < range) { /* ok, too many cells for this direction, set to minimum */ - cell_grid[i] = (int)floor(m_local_box.length()[i] / range); + cell_grid[i] = static_cast(std::floor(local_box_l[i] / range)); if (cell_grid[i] < 1) { - runtimeErrorMsg() << "interaction range " << range << " in direction " - << i << " is larger than the local box size " - << m_local_box.length()[i]; + runtimeErrorMsg() + << "interaction range " << range << " in direction " << i + << " is larger than the local box size " << local_box_l[i]; cell_grid[i] = 1; } - cell_range[i] = m_local_box.length()[i] / cell_grid[i]; + cell_range[i] = local_box_l[i] / static_cast(cell_grid[i]); } } @@ -360,9 +355,8 @@ void DomainDecomposition::create_cell_grid(double range) { } } - /* quit program if unsuccessful */ if (n_local_cells > DomainDecomposition::max_num_cells) { - runtimeErrorMsg() << "no suitable cell grid found "; + runtimeErrorMsg() << "no suitable cell grid found"; } auto const node_pos = cart_info.coords; From 381f3d350cfd564caa02655bb671336f783c1aeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 14 Jan 2022 18:30:02 +0100 Subject: [PATCH 16/99] core: Remove SOME_TAG macro --- src/core/communication.hpp | 7 ------- src/core/particle_data.cpp | 17 +++++++++-------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/core/communication.hpp b/src/core/communication.hpp index 724f2e37dfa..9c55393e342 100644 --- a/src/core/communication.hpp +++ b/src/core/communication.hpp @@ -58,13 +58,6 @@ extern int n_nodes; /** The communicator */ extern boost::mpi::communicator comm_cart; -/** - * Default MPI tag used by callbacks. - */ -#ifndef SOME_TAG -#define SOME_TAG 42 -#endif - namespace Communication { /** * @brief Returns a reference to the global callback class instance. diff --git a/src/core/particle_data.cpp b/src/core/particle_data.cpp index b10b3cdaefc..d5fe22ea72c 100644 --- a/src/core/particle_data.cpp +++ b/src/core/particle_data.cpp @@ -62,6 +62,7 @@ #include #include +constexpr auto some_tag = 42; static bool type_list_enable; static std::unordered_map> particle_type_map; @@ -332,7 +333,7 @@ struct UpdateVisitor : public boost::static_visitor { void mpi_send_update_message_local(int node, int id) { if (node == comm_cart.rank()) { UpdateMessage msg{}; - comm_cart.recv(0, SOME_TAG, msg); + comm_cart.recv(0, some_tag, msg); boost::apply_visitor(UpdateVisitor{id}, msg); } @@ -368,7 +369,7 @@ void mpi_send_update_message(int id, const UpdateMessage &msg) { * message to the target, otherwise we * can just apply the update directly. */ if (pnode != comm_cart.rank()) { - comm_cart.send(pnode, SOME_TAG, msg); + comm_cart.send(pnode, some_tag, msg); } else { boost::apply_visitor(UpdateVisitor{id}, msg); } @@ -415,7 +416,7 @@ static void mpi_who_has_local() { sendbuf.begin(), [](Particle const &p) { return p.p.identity; }); - MPI_Send(sendbuf.data(), n_part, MPI_INT, 0, SOME_TAG, comm_cart); + MPI_Send(sendbuf.data(), n_part, MPI_INT, 0, some_tag, comm_cart); } REGISTER_CALLBACK(mpi_who_has_local) @@ -439,7 +440,7 @@ void mpi_who_has() { } else if (n_parts[pnode] > 0) { pdata.resize(n_parts[pnode]); - MPI_Recv(pdata.data(), n_parts[pnode], MPI_INT, pnode, SOME_TAG, + MPI_Recv(pdata.data(), n_parts[pnode], MPI_INT, pnode, some_tag, comm_cart, MPI_STATUS_IGNORE); for (int i = 0; i < n_parts[pnode]; i++) particle_node[pdata[i]] = pnode; @@ -662,7 +663,7 @@ int mpi_place_new_particle(int p_id, const Utils::Vector3d &pos) { void mpi_place_particle_local(int pnode, int p_id) { if (pnode == this_node) { Utils::Vector3d pos; - comm_cart.recv(0, SOME_TAG, pos); + comm_cart.recv(0, some_tag, pos); local_place_particle(p_id, pos, 0); } @@ -684,7 +685,7 @@ void mpi_place_particle(int node, int p_id, const Utils::Vector3d &pos) { if (node == this_node) local_place_particle(p_id, pos, 0); else { - comm_cart.send(node, SOME_TAG, pos); + comm_cart.send(node, some_tag, pos); } cell_structure.set_resort_particles(Cells::RESORT_GLOBAL); @@ -991,7 +992,7 @@ void local_rescale_particles(int dir, double scale) { static void mpi_rescale_particles_local(int dir) { double scale = 0.0; - MPI_Recv(&scale, 1, MPI_DOUBLE, 0, SOME_TAG, comm_cart, MPI_STATUS_IGNORE); + MPI_Recv(&scale, 1, MPI_DOUBLE, 0, some_tag, comm_cart, MPI_STATUS_IGNORE); local_rescale_particles(dir, scale); on_particle_change(); } @@ -1004,7 +1005,7 @@ void mpi_rescale_particles(int dir, double scale) { if (pnode == this_node) { local_rescale_particles(dir, scale); } else { - MPI_Send(&scale, 1, MPI_DOUBLE, pnode, SOME_TAG, comm_cart); + MPI_Send(&scale, 1, MPI_DOUBLE, pnode, some_tag, comm_cart); } } on_particle_change(); From cd8680a088ccd2dc768dc29ef091b6705139eb20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 17 Jan 2022 12:26:03 +0100 Subject: [PATCH 17/99] python: Fix IBMTribend interface regression --- src/python/espressomd/interactions.pyx | 3 ++- testsuite/python/test_checkpoint.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index dc7925064b5..19158423ccb 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -2663,7 +2663,8 @@ class IBM_Tribend(BondedInteraction): return {"refShape": "Flat"} def _get_params_from_es_core(self): - return {"kb": self.kb, "theta0": self.theta0} + return {"kb": self.kb, "theta0": self.theta0, + "refShape": self.refShape} @script_interface_register diff --git a/testsuite/python/test_checkpoint.py b/testsuite/python/test_checkpoint.py index 914e26052fc..9e36f088969 100644 --- a/testsuite/python/test_checkpoint.py +++ b/testsuite/python/test_checkpoint.py @@ -329,7 +329,9 @@ def test_bonded_inter(self): self.assertEqual( ibm_volcons_bond.params, {'softID': 15, 'kappaV': 0.01}) if 'DP3M.CPU' not in modes: - self.assertEqual(ibm_tribend_bond.params, {'kb': 2., 'theta0': 0.}) + self.assertEqual( + ibm_tribend_bond.params, + {'kb': 2., 'theta0': 0., 'refShape': 'Initial'}) self.assertEqual( ibm_triel_bond.params, {'k1': 1.1, 'k2': 1.2, 'maxDist': 1.6, 'elasticLaw': 'NeoHookean'}) From 01a53b704c80ac0a30aad5dcf29179ce7ca2c961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 17 Jan 2022 22:06:04 +0100 Subject: [PATCH 18/99] tests: Improve code coverage --- src/core/unit_tests/ParticleFactory.hpp | 9 +-- .../field_coupling_force_field_test.cpp | 7 +- src/core/unit_tests/thermostats_test.cpp | 12 +++ src/script_interface/tests/CMakeLists.txt | 3 + .../tests/ObjectHandle_test.cpp | 33 +++++++- .../tests/serialization_mpi_guard_test.cpp | 78 +++++++++++++++++++ src/utils/tests/sampling_test.cpp | 23 +++--- testsuite/python/CMakeLists.txt | 1 + testsuite/python/collision_detection.py | 2 + testsuite/python/es_math.py | 7 +- .../python/interactions_bonded_interface.py | 3 + testsuite/python/lb_boundary.py | 7 ++ testsuite/python/pair_criteria.py | 5 ++ 13 files changed, 166 insertions(+), 24 deletions(-) create mode 100644 src/script_interface/tests/serialization_mpi_guard_test.cpp diff --git a/src/core/unit_tests/ParticleFactory.hpp b/src/core/unit_tests/ParticleFactory.hpp index d59e18b93a2..f1a66c00abb 100644 --- a/src/core/unit_tests/ParticleFactory.hpp +++ b/src/core/unit_tests/ParticleFactory.hpp @@ -35,14 +35,7 @@ struct ParticleFactory { } } - void create_particle(Utils::Vector3d const &pos, int pid = -1, - int type = -1) { - if (pid < 0) { - pid = get_maximal_particle_id() + 1; - } - if (type < 0) { - type = 0; - } + void create_particle(Utils::Vector3d const &pos, int pid, int type) { place_particle(pid, pos); set_particle_type(pid, type); particle_cache.emplace_back(pid); diff --git a/src/core/unit_tests/field_coupling_force_field_test.cpp b/src/core/unit_tests/field_coupling_force_field_test.cpp index b0761d47abc..0b10610da9e 100644 --- a/src/core/unit_tests/field_coupling_force_field_test.cpp +++ b/src/core/unit_tests/field_coupling_force_field_test.cpp @@ -65,10 +65,13 @@ BOOST_AUTO_TEST_CASE(BindCoupling_test) { { auto const id = Id{}; auto const bc = make_bind_coupling(id, Particle{}); - const int x = 5; + auto const x = 5; + BOOST_CHECK(id.count == 0); BOOST_CHECK(5 == bc(5)); BOOST_CHECK(id.count == 1); + BOOST_CHECK(x == bc(x)); + BOOST_CHECK(id.count == 2); } } @@ -114,6 +117,7 @@ BOOST_AUTO_TEST_CASE(ForceField_test) { ForceField, DummyVectorField>(Id{}, DummyVectorField{}); const Utils::Vector3d x{1., 2., 3.}; + BOOST_CHECK(0 == ff.coupling().count); BOOST_CHECK((9. * x) == ff.force(5, x, 9.)); BOOST_CHECK(1 == ff.coupling().count); } @@ -131,6 +135,7 @@ BOOST_AUTO_TEST_CASE(PotentialField_test) { auto pf = PotentialField, DummyScalarField>(Id{}, DummyScalarField{}); const Utils::Vector3d x{1., 2., 3.}; + BOOST_CHECK(0 == pf.coupling().count); BOOST_CHECK((2. * x.norm()) == pf.energy(5, x, 2.)); BOOST_CHECK(1 == pf.coupling().count); diff --git a/src/core/unit_tests/thermostats_test.cpp b/src/core/unit_tests/thermostats_test.cpp index fb65d0ad0bc..b95b2fa4f75 100644 --- a/src/core/unit_tests/thermostats_test.cpp +++ b/src/core/unit_tests/thermostats_test.cpp @@ -341,3 +341,15 @@ BOOST_AUTO_TEST_CASE(test_npt_iso_randomness) { } } #endif // NPT + +BOOST_AUTO_TEST_CASE(test_predicate) { + std::vector> const correlation = {{1., 0.}, {0., 1.}}; + auto const is_true = correlation_almost_equal(correlation, 0, 0, 1., 1e-10); + auto const is_false = correlation_almost_equal(correlation, 0, 1, 1., 1e-10); + BOOST_REQUIRE(is_true); + BOOST_REQUIRE(!is_false); + BOOST_CHECK_EQUAL(is_true.message(), ""); + BOOST_CHECK_EQUAL(is_false.message(), + "The correlation coefficient M[0][1]{0} " + "differs from 1 by 1 (> 1e-10)"); +} diff --git a/src/script_interface/tests/CMakeLists.txt b/src/script_interface/tests/CMakeLists.txt index bdbf22b8c6f..540f0bb0013 100644 --- a/src/script_interface/tests/CMakeLists.txt +++ b/src/script_interface/tests/CMakeLists.txt @@ -37,6 +37,9 @@ unit_test(NAME packed_variant_test SRC packed_variant_test.cpp DEPENDS ScriptInterface) unit_test(NAME ObjectList_test SRC ObjectList_test.cpp DEPENDS ScriptInterface) unit_test(NAME ObjectMap_test SRC ObjectMap_test.cpp DEPENDS ScriptInterface) +unit_test(NAME serialization_mpi_guard_test SRC + serialization_mpi_guard_test.cpp DEPENDS ScriptInterface Boost::mpi + MPI::MPI_CXX NUM_PROC 2) unit_test(NAME Accumulators_test SRC Accumulators_test.cpp DEPENDS ScriptInterface) unit_test(NAME Constraints_test SRC Constraints_test.cpp DEPENDS diff --git a/src/script_interface/tests/ObjectHandle_test.cpp b/src/script_interface/tests/ObjectHandle_test.cpp index 65237f500ad..f9068c54622 100644 --- a/src/script_interface/tests/ObjectHandle_test.cpp +++ b/src/script_interface/tests/ObjectHandle_test.cpp @@ -23,10 +23,15 @@ #define BOOST_TEST_DYN_LINK #include -#include - +#include "script_interface/ObjectHandle.hpp" +#include "script_interface/ObjectState.hpp" #include "script_interface/ScriptInterface.hpp" +#include + +#include +#include + #include #include #include @@ -92,6 +97,30 @@ BOOST_AUTO_TEST_CASE(non_copyable) { BOOST_TEST_PASSPOINT(); } +BOOST_AUTO_TEST_CASE(default_constructible) { + ObjectHandle handle; + + auto const param_name = std::string("unknown"); + handle.construct({{param_name, Variant{1}}}); + BOOST_CHECK(is_type(handle.get_parameter(param_name))); + BOOST_CHECK(is_type(handle.call_method("foo", {}))); + + // serialization should be empty + auto const bytestring_obj = handle.serialize(); + auto const bytestring_ref = Utils::pack(ObjectState{}); + BOOST_CHECK_EQUAL(bytestring_obj, bytestring_ref); + + // serialization of an empty ObjectState should only contain the library + // version and a string "serialization::archive", followed by a few integers + auto const trim_null_terminator_right = [](std::string const &s) { + return boost::trim_right_copy_if(s, [](char const c) { return c == '\0'; }); + }; + auto const bytestring_nul = Utils::pack(std::string{}); + auto const metadata_obj = trim_null_terminator_right(bytestring_obj); + auto const metadata_ref = trim_null_terminator_right(bytestring_nul); + BOOST_CHECK_EQUAL(metadata_obj, metadata_ref); +} + /* * Check that the call to ObjectHandle::construct is * forwarded correctly to the implementation. diff --git a/src/script_interface/tests/serialization_mpi_guard_test.cpp b/src/script_interface/tests/serialization_mpi_guard_test.cpp new file mode 100644 index 00000000000..88fea91e851 --- /dev/null +++ b/src/script_interface/tests/serialization_mpi_guard_test.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 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 . + */ + +#define BOOST_TEST_NO_MAIN +#define BOOST_TEST_MODULE Object container MPI guard test +#define BOOST_TEST_DYN_LINK +#include + +#include "script_interface/ObjectList.hpp" + +#include + +#include +#include +#include +#include +#include + +using ScriptInterface::ObjectHandle; + +namespace Testing { +struct ObjectContainer : ScriptInterface::ObjectList { + std::vector> objects; + +private: + void add_in_core(std::shared_ptr const &obj_ptr) override { + objects.push_back(obj_ptr); + } + void remove_in_core(std::shared_ptr const &obj_ptr) override { + objects.erase(std::remove(objects.begin(), objects.end(), obj_ptr), + objects.end()); + } +}; +} // namespace Testing + +BOOST_AUTO_TEST_CASE(parallel_exception) { + boost::mpi::communicator world; + auto const obj_ptr = std::make_shared(); + auto const predicate = [](std::exception const &ex) { + std::string message = + "Non-empty object containers do not support checkpointing " + "in MPI environments. Container contains 1 elements."; + return ex.what() == message; + }; + + Testing::ObjectContainer list; + BOOST_CHECK_NO_THROW(list.serialize()); + + list.add(obj_ptr); + if (world.size() > 1) { + BOOST_CHECK_EXCEPTION(list.serialize(), std::runtime_error, predicate); + } + + list.remove(obj_ptr); + BOOST_CHECK_NO_THROW(list.serialize()); +} + +int main(int argc, char **argv) { + boost::mpi::environment mpi_env(argc, argv); + + return boost::unit_test::unit_test_main(init_unit_test, argc, argv); +} diff --git a/src/utils/tests/sampling_test.cpp b/src/utils/tests/sampling_test.cpp index 6d5b3b2bf48..d2ac94376df 100644 --- a/src/utils/tests/sampling_test.cpp +++ b/src/utils/tests/sampling_test.cpp @@ -36,28 +36,33 @@ BOOST_AUTO_TEST_CASE(get_cylindrical_sampling_positions_test) { auto const max_phi = Utils::pi(); auto const min_z = 0.0; auto const max_z = 10.0; - auto const n_r_bins = 10; - auto const n_phi_bins = 10; - auto const n_z_bins = 10; + auto const n_r_bins = std::size_t{10}; + auto const n_phi_bins = std::size_t{11}; + auto const n_z_bins = std::size_t{12}; auto const sampling_density = 2.; + auto const sampling_positions = Utils::get_cylindrical_sampling_positions( std::make_pair(min_r, max_r), std::make_pair(min_phi, max_phi), std::make_pair(min_z, max_z), n_r_bins, n_phi_bins, n_z_bins, sampling_density); - std::array, 3> limits{ + + std::array const n_bins{{n_r_bins, n_phi_bins, n_z_bins}}; + std::array, 3> const limits{ {std::make_pair(min_r, max_r), std::make_pair(min_phi, max_phi), std::make_pair(min_z, max_z)}}; - std::array n_bins{{static_cast(n_r_bins), - static_cast(n_phi_bins), - static_cast(n_z_bins)}}; + Utils::CylindricalHistogram histogram(n_bins, limits); for (auto const &p : sampling_positions) { histogram.update(p); } + auto const tot_count = histogram.get_tot_count(); - std::array const dimensions{{n_r_bins, n_phi_bins, n_z_bins}}; - std::array index{}; for (auto const &c : tot_count) { BOOST_CHECK(c > 0); } + for (std::size_t i = 0; i < 3; ++i) { + BOOST_CHECK_EQUAL(histogram.get_n_bins()[i], n_bins[i]); + BOOST_CHECK_EQUAL(histogram.get_limits()[i].first, limits[i].first); + BOOST_CHECK_EQUAL(histogram.get_limits()[i].second, limits[i].second); + } } diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index 3fa015719a6..492a642c719 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -185,6 +185,7 @@ python_test(FILE analyze_distribution.py MAX_NUM_PROC 1) python_test(FILE observable_profile.py MAX_NUM_PROC 4) python_test(FILE observable_profileLB.py MAX_NUM_PROC 1 LABELS gpu) python_test(FILE rotate_system.py MAX_NUM_PROC 4) +python_test(FILE es_math.py MAX_NUM_PROC 1) python_test(FILE random_pairs.py MAX_NUM_PROC 4) python_test(FILE lb_electrohydrodynamics.py MAX_NUM_PROC 4 LABELS gpu) python_test(FILE cluster_analysis.py MAX_NUM_PROC 4) diff --git a/testsuite/python/collision_detection.py b/testsuite/python/collision_detection.py index b4ab3a5bbdb..e1ef7838f25 100644 --- a/testsuite/python/collision_detection.py +++ b/testsuite/python/collision_detection.py @@ -61,6 +61,7 @@ def get_state_set_state_consistency(self): def test_00_interface_and_defaults(self): # Is it off by default self.assertEqual(self.system.collision_detection.mode, "off") + # Make sure params cannot be set individually with self.assertRaises(Exception): self.system.collision_detection.mode = "bind_centers" @@ -69,6 +70,7 @@ def test_00_interface_and_defaults(self): for unknown_mode in (0, "unknown"): with self.assertRaisesRegex(Exception, "Mode not handled"): self.system.collision_detection.set_params(mode=unknown_mode) + self.assertIsNone(self.system.collision_detection.call_method("none")) # That should work self.system.collision_detection.set_params(mode="off") diff --git a/testsuite/python/es_math.py b/testsuite/python/es_math.py index 4481fc24311..012eb9ccd01 100644 --- a/testsuite/python/es_math.py +++ b/testsuite/python/es_math.py @@ -14,6 +14,7 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . + import numpy as np import unittest as ut import espressomd.math @@ -44,10 +45,8 @@ def test_cylindrical_transformation_parameters(self): center=3 * [42], axis=[0, 1, 0], orientation=[1, 0, 0]) self.check_orthonormality(ctp_full.axis, ctp_full.orientation) - with self.assertRaises(Exception): - ctp_only_center = espressomd.math.CylindricalTransformationParameters( - center=3 * [42]) - ctp_only_center.axis = 3 * [3] + with self.assertRaises(RuntimeError): + espressomd.math.CylindricalTransformationParameters(center=3 * [4]) if __name__ == "__main__": diff --git a/testsuite/python/interactions_bonded_interface.py b/testsuite/python/interactions_bonded_interface.py index b060f5d5031..ea1865372bb 100644 --- a/testsuite/python/interactions_bonded_interface.py +++ b/testsuite/python/interactions_bonded_interface.py @@ -147,6 +147,9 @@ def func(self): .format(bondClass(**params).type_name(), bondId, outParamsRef, outParams)) self.parameterKeys(outBond) + # check no-op + self.assertIsNone(outBond.call_method('unknown')) + return func test_fene = generateTestForBondParams( diff --git a/testsuite/python/lb_boundary.py b/testsuite/python/lb_boundary.py index 94a2c7cfb51..e0480d2e046 100644 --- a/testsuite/python/lb_boundary.py +++ b/testsuite/python/lb_boundary.py @@ -21,6 +21,7 @@ import espressomd.shapes import espressomd.lbboundaries import itertools +import numpy as np class LBBoundariesBase: @@ -67,6 +68,12 @@ def test_size(self): lbb.add(espressomd.lbboundaries.LBBoundary(shape=self.wall_shape1)) self.assertEqual(lbb.size(), 2) + def test_getters(self): + boundary = espressomd.lbboundaries.LBBoundary(shape=self.wall_shape1) + self.system.lbboundaries.add(boundary) + np.testing.assert_equal(np.copy(boundary.get_force()), [0., 0., 0.]) + self.assertIsNone(boundary.call_method('unknown')) + def test_empty(self): lbb = self.system.lbboundaries self.assertTrue(lbb.empty()) diff --git a/testsuite/python/pair_criteria.py b/testsuite/python/pair_criteria.py index 95790e0d2a4..87634241603 100644 --- a/testsuite/python/pair_criteria.py +++ b/testsuite/python/pair_criteria.py @@ -57,6 +57,11 @@ def test_distance_crit_non_periodic(self): self.assertTrue(not dc.decide(self.p1, self.p2)) self.assertTrue(not dc.decide(self.p1.id, self.p2.id)) + def test_distance_crit_exceptions(self): + dc = espressomd.pair_criteria.DistanceCriterion(cut_off=0.1) + with self.assertRaises(RuntimeError): + dc.call_method("unknown") + @utx.skipIfMissingFeatures("LENNARD_JONES") def test_energy_crit(self): # Setup purely repulsive lj From 103d528cf95723142c4d82e8e46e879986e8cd04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 18 Jan 2022 13:47:10 +0100 Subject: [PATCH 19/99] core: Remove unused code Remove unused functions, arguments, struct members, local variables and global variables (fixes all -Wunused-variable warnings and some of the -Wunused-function and -Wunused-parameter warnings). --- CMakeLists.txt | 1 - .../bonded_interactions/bonded_coulomb_sr.hpp | 5 +- .../coulomb_inline.hpp | 4 +- .../electrostatics_magnetostatics/icc.cpp | 6 +- .../electrostatics_magnetostatics/mmm1d.cpp | 2 +- .../electrostatics_magnetostatics/mmm1d.hpp | 2 +- .../p3m-dipolar.cpp | 62 +++++++++---------- src/core/energy_inline.hpp | 2 +- src/core/forces_inline.hpp | 2 +- .../electrokinetics_cuda.cu | 45 +++++--------- src/core/grid_based_algorithms/lb.cpp | 10 +-- .../grid_based_algorithms/lb_boundaries.cpp | 2 - .../lb_collective_interface.cpp | 8 +-- .../grid_based_algorithms/lb_interface.cpp | 2 - .../grid_based_algorithms/lb_interface.hpp | 5 -- .../lb_particle_coupling.cpp | 11 ++-- src/core/grid_based_algorithms/lbgpu.cpp | 5 -- src/core/grid_based_algorithms/lbgpu.hpp | 6 +- src/core/grid_based_algorithms/lbgpu_cuda.cu | 47 ++++++-------- .../immersed_boundary/ImmersedBoundaries.cpp | 1 - src/core/integrate.cpp | 2 +- .../integrators/stokesian_dynamics_inline.hpp | 2 - src/core/io/writer/h5md_core.cpp | 5 +- src/core/nonbonded_interactions/thole.hpp | 3 +- .../reaction_methods/ReactionAlgorithm.cpp | 11 +--- .../reaction_methods/ReactionAlgorithm.hpp | 3 +- src/core/reaction_methods/WidomInsertion.cpp | 9 +-- .../EspressoSystemStandAlone_test.cpp | 3 - src/core/unit_tests/Verlet_list_test.cpp | 1 - src/core/unit_tests/random_test.cpp | 2 +- .../virtual_sites/lb_inertialess_tracers.cpp | 1 - 31 files changed, 99 insertions(+), 171 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 85b3a368b61..d92d71dd700 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -323,7 +323,6 @@ target_compile_options( # disable warnings from -Wextra -Wno-sign-compare -Wno-unused-function - -Wno-unused-variable -Wno-unused-parameter -Wno-missing-braces $<$:-Wno-clobbered> diff --git a/src/core/bonded_interactions/bonded_coulomb_sr.hpp b/src/core/bonded_interactions/bonded_coulomb_sr.hpp index ccd647f2c3d..ea074c036c6 100644 --- a/src/core/bonded_interactions/bonded_coulomb_sr.hpp +++ b/src/core/bonded_interactions/bonded_coulomb_sr.hpp @@ -82,9 +82,8 @@ inline boost::optional BondedCoulombSR::energy(Particle const &p1, Particle const &p2, Utils::Vector3d const &dx) const { #ifdef ELECTROSTATICS - auto const dist2 = dx.norm2(); - auto const dist = sqrt(dist2); - return Coulomb::pair_energy(p1, p2, q1q2, dx, dist, dist2); + auto const dist = dx.norm(); + return Coulomb::pair_energy(p1, p2, q1q2, dx, dist); #else return .0; #endif diff --git a/src/core/electrostatics_magnetostatics/coulomb_inline.hpp b/src/core/electrostatics_magnetostatics/coulomb_inline.hpp index e3c912c94ae..d62e6e699ac 100644 --- a/src/core/electrostatics_magnetostatics/coulomb_inline.hpp +++ b/src/core/electrostatics_magnetostatics/coulomb_inline.hpp @@ -143,7 +143,7 @@ inline Utils::Matrix pair_pressure(Particle const &p1, // energy_inline inline double pair_energy(Particle const &p1, Particle const &p2, double const q1q2, Utils::Vector3d const &d, - double dist, double dist2) { + double dist) { /* real space Coulomb */ auto E = [&]() { switch (coulomb.method) { @@ -168,7 +168,7 @@ inline double pair_energy(Particle const &p1, Particle const &p2, case COULOMB_RF: return rf_coulomb_pair_energy(q1q2, dist); case COULOMB_MMM1D: - return mmm1d_coulomb_pair_energy(q1q2, d, dist2, dist); + return mmm1d_coulomb_pair_energy(q1q2, d, dist); default: return 0.; } diff --git a/src/core/electrostatics_magnetostatics/icc.cpp b/src/core/electrostatics_magnetostatics/icc.cpp index ea2cbcabf0c..0aa8a73b1eb 100644 --- a/src/core/electrostatics_magnetostatics/icc.cpp +++ b/src/core/electrostatics_magnetostatics/icc.cpp @@ -70,8 +70,8 @@ void force_calc_icc(CellStructure &cell_structure, * contributions are calculated */ inline void add_non_bonded_pair_force_icc(Particle &p1, Particle &p2, - Utils::Vector3d const &d, double dist, - double dist2) { + Utils::Vector3d const &d, + double dist) { auto forces = Coulomb::pair_force(p1, p2, d, dist); p1.f.f += std::get<0>(forces); @@ -193,7 +193,7 @@ void force_calc_icc(CellStructure &cell_structure, // calc ICC forces cell_structure.non_bonded_loop( [](Particle &p1, Particle &p2, Distance const &d) { - add_non_bonded_pair_force_icc(p1, p2, d.vec21, sqrt(d.dist2), d.dist2); + add_non_bonded_pair_force_icc(p1, p2, d.vec21, sqrt(d.dist2)); }); Coulomb::calc_long_range_force(particles); diff --git a/src/core/electrostatics_magnetostatics/mmm1d.cpp b/src/core/electrostatics_magnetostatics/mmm1d.cpp index 44194045274..0b0ff5e9a6f 100644 --- a/src/core/electrostatics_magnetostatics/mmm1d.cpp +++ b/src/core/electrostatics_magnetostatics/mmm1d.cpp @@ -268,7 +268,7 @@ void add_mmm1d_coulomb_pair_force(double chpref, Utils::Vector3d const &d, } double mmm1d_coulomb_pair_energy(double const chpref, Utils::Vector3d const &d, - double r2, double r) { + double r) { if (chpref == 0) return 0; diff --git a/src/core/electrostatics_magnetostatics/mmm1d.hpp b/src/core/electrostatics_magnetostatics/mmm1d.hpp index f6c90d99570..12f231619f3 100644 --- a/src/core/electrostatics_magnetostatics/mmm1d.hpp +++ b/src/core/electrostatics_magnetostatics/mmm1d.hpp @@ -69,7 +69,7 @@ void add_mmm1d_coulomb_pair_force(double chpref, Utils::Vector3d const &d, double r, Utils::Vector3d &force); double mmm1d_coulomb_pair_energy(double q1q2, Utils::Vector3d const &d, - double r2, double r); + double r); /** Tuning of the parameters which are not set by the user. Tune either the * @ref MMM1DParameters::far_switch_radius_2 "switching radius" or the diff --git a/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp b/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp index b21ee5d06ef..81877f6510f 100644 --- a/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp +++ b/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp @@ -115,9 +115,8 @@ static void dp3m_calc_influence_function_energy(); */ static void dp3m_compute_constants_energy_dipolar(); -static double dp3m_k_space_error(double box_size, double prefac, int mesh, - int cao, int n_c_part, double sum_q2, - double alpha_L); +static double dp3m_k_space_error(double box_size, int mesh, int cao, + int n_c_part, double sum_q2, double alpha_L); /**@}*/ /** Compute the dipolar surface terms */ @@ -128,8 +127,8 @@ static double calc_surface_term(bool force_flag, bool energy_flag, /************************************************************/ /**@{*/ -double dp3m_real_space_error(double box_size, double prefac, double r_cut_iL, - int n_c_part, double sum_q2, double alpha_L); +double dp3m_real_space_error(double box_size, double r_cut_iL, int n_c_part, + double sum_q2, double alpha_L); static void dp3m_tune_aliasing_sums(int nx, int ny, int nz, int mesh, double mesh_i, int cao, double alpha_L_i, double *alias1, double *alias2); @@ -137,9 +136,9 @@ static void dp3m_tune_aliasing_sums(int nx, int ny, int nz, int mesh, /** Compute the value of alpha through a bisection method. * Based on eq. (33) @cite wang01a. */ -double dp3m_rtbisection(double box_size, double prefac, double r_cut_iL, - int n_c_part, double sum_q2, double x1, double x2, - double xacc, double tuned_accuracy); +double dp3m_rtbisection(double box_size, double r_cut_iL, int n_c_part, + double sum_q2, double x1, double x2, double xacc, + double tuned_accuracy); /**@}*/ @@ -748,16 +747,15 @@ double dp3m_get_accuracy(int mesh, int cao, double r_cut_iL, double *_alpha_L, // Alpha cannot be zero in the dipolar case because real_space formula breaks // down - rs_err = - dp3m_real_space_error(box_geo.length()[0], dipole.prefactor, r_cut_iL, - dp3m.sum_dip_part, dp3m.sum_mu2, 0.001); + rs_err = dp3m_real_space_error(box_geo.length()[0], r_cut_iL, + dp3m.sum_dip_part, dp3m.sum_mu2, 0.001); if (Utils::sqrt_2() * rs_err > dp3m.params.accuracy) { /* assume rs_err = ks_err -> rs_err = accuracy/sqrt(2.0) -> alpha_L */ - alpha_L = dp3m_rtbisection( - box_geo.length()[0], dipole.prefactor, r_cut_iL, dp3m.sum_dip_part, - dp3m.sum_mu2, 0.0001 * box_geo.length()[0], 5.0 * box_geo.length()[0], - 0.0001, dp3m.params.accuracy); + alpha_L = dp3m_rtbisection(box_geo.length()[0], r_cut_iL, dp3m.sum_dip_part, + dp3m.sum_mu2, 0.0001 * box_geo.length()[0], + 5.0 * box_geo.length()[0], 0.0001, + dp3m.params.accuracy); if (alpha_L == -DP3M_RTBISECTION_ERROR) { *_rs_err = -1; *_ks_err = -1; @@ -774,11 +772,10 @@ double dp3m_get_accuracy(int mesh, int cao, double r_cut_iL, double *_alpha_L, *_alpha_L = alpha_L; /* calculate real space and k-space error for this alpha_L */ - rs_err = - dp3m_real_space_error(box_geo.length()[0], dipole.prefactor, r_cut_iL, - dp3m.sum_dip_part, dp3m.sum_mu2, alpha_L); - ks_err = dp3m_k_space_error(box_geo.length()[0], dipole.prefactor, mesh, cao, - dp3m.sum_dip_part, dp3m.sum_mu2, alpha_L); + rs_err = dp3m_real_space_error(box_geo.length()[0], r_cut_iL, + dp3m.sum_dip_part, dp3m.sum_mu2, alpha_L); + ks_err = dp3m_k_space_error(box_geo.length()[0], mesh, cao, dp3m.sum_dip_part, + dp3m.sum_mu2, alpha_L); *_rs_err = rs_err; *_ks_err = ks_err; @@ -1246,9 +1243,8 @@ void dp3m_count_magnetic_particles() { REGISTER_CALLBACK(dp3m_count_magnetic_particles) /** Calculate the k-space error of dipolar-P3M */ -static double dp3m_k_space_error(double box_size, double prefac, int mesh, - int cao, int n_c_part, double sum_q2, - double alpha_L) { +static double dp3m_k_space_error(double box_size, int mesh, int cao, + int n_c_part, double sum_q2, double alpha_L) { double he_q = 0.0; auto const mesh_i = 1. / mesh; auto const alpha_L_i = 1. / alpha_L; @@ -1313,8 +1309,8 @@ void dp3m_tune_aliasing_sums(int nx, int ny, int nz, int mesh, double mesh_i, * Please note that in this more refined approach we don't use * eq. (37), but eq. (33) which maintains all the powers in alpha. */ -double dp3m_real_space_error(double box_size, double prefac, double r_cut_iL, - int n_c_part, double sum_q2, double alpha_L) { +double dp3m_real_space_error(double box_size, double r_cut_iL, int n_c_part, + double sum_q2, double alpha_L) { double d_error_f, d_cc, d_dc, d_rcut2, d_con; double d_a2, d_c, d_RCUT; @@ -1346,18 +1342,18 @@ double dp3m_real_space_error(double box_size, double prefac, double r_cut_iL, * known to lie between x1 and x2. The root, returned as rtbis, will be * refined until its accuracy is \f$\pm\f$ @p xacc. */ -double dp3m_rtbisection(double box_size, double prefac, double r_cut_iL, - int n_c_part, double sum_q2, double x1, double x2, - double xacc, double tuned_accuracy) { +double dp3m_rtbisection(double box_size, double r_cut_iL, int n_c_part, + double sum_q2, double x1, double x2, double xacc, + double tuned_accuracy) { constexpr int JJ_RTBIS_MAX = 40; auto const constant = tuned_accuracy / Utils::sqrt_2(); auto const f1 = - dp3m_real_space_error(box_size, prefac, r_cut_iL, n_c_part, sum_q2, x1) - + dp3m_real_space_error(box_size, r_cut_iL, n_c_part, sum_q2, x1) - constant; auto const f2 = - dp3m_real_space_error(box_size, prefac, r_cut_iL, n_c_part, sum_q2, x2) - + dp3m_real_space_error(box_size, r_cut_iL, n_c_part, sum_q2, x2) - constant; if (f1 * f2 >= 0.0) { runtimeErrorMsg() @@ -1369,9 +1365,9 @@ double dp3m_rtbisection(double box_size, double prefac, double r_cut_iL, double rtb = f1 < 0.0 ? (dx = x2 - x1, x1) : (dx = x1 - x2, x2); for (int j = 1; j <= JJ_RTBIS_MAX; j++) { auto const xmid = rtb + (dx *= 0.5); - auto const fmid = dp3m_real_space_error(box_size, prefac, r_cut_iL, - n_c_part, sum_q2, xmid) - - constant; + auto const fmid = + dp3m_real_space_error(box_size, r_cut_iL, n_c_part, sum_q2, xmid) - + constant; if (fmid <= 0.0) rtb = xmid; if (fabs(dx) < xacc || fmid == 0.0) diff --git a/src/core/energy_inline.hpp b/src/core/energy_inline.hpp index a43d1279633..a6808ef361e 100644 --- a/src/core/energy_inline.hpp +++ b/src/core/energy_inline.hpp @@ -192,7 +192,7 @@ inline void add_non_bonded_pair_energy(Particle const &p1, Particle const &p2, #ifdef ELECTROSTATICS if (!obs_energy.coulomb.empty()) obs_energy.coulomb[0] += - Coulomb::pair_energy(p1, p2, p1.p.q * p2.p.q, d, dist, dist2); + Coulomb::pair_energy(p1, p2, p1.p.q * p2.p.q, d, dist); #endif #ifdef DIPOLES diff --git a/src/core/forces_inline.hpp b/src/core/forces_inline.hpp index d5758e7b90b..a89136a2e50 100644 --- a/src/core/forces_inline.hpp +++ b/src/core/forces_inline.hpp @@ -343,7 +343,7 @@ calc_bonded_three_body_force(Bonded_IA_Parameters const &iaparams, inline bool add_bonded_three_body_force(Bonded_IA_Parameters const &iaparams, Particle &p1, Particle &p2, Particle &p3) { - if (auto const *iap = boost::get(&iaparams)) { + if (boost::get(&iaparams)) { return false; } auto const result = calc_bonded_three_body_force(iaparams, p1, p2, p3); diff --git a/src/core/grid_based_algorithms/electrokinetics_cuda.cu b/src/core/grid_based_algorithms/electrokinetics_cuda.cu index 9640489e2b5..a254f9ea455 100644 --- a/src/core/grid_based_algorithms/electrokinetics_cuda.cu +++ b/src/core/grid_based_algorithms/electrokinetics_cuda.cu @@ -194,8 +194,6 @@ extern LB_node_force_density_gpu node_f, node_f_buf; extern LB_nodes_gpu *current_nodes; extern EKParameters *lb_ek_parameters; -LB_rho_v_gpu *ek_lb_device_values; - __device__ cufftReal ek_getNode(unsigned x, unsigned y, unsigned z) { auto *field = reinterpret_cast(ek_parameters_gpu->charge_potential); @@ -1093,9 +1091,8 @@ __device__ void ek_diffusion_migration_lbforce_nodecentered_stencil( } __device__ void -ek_add_advection_to_flux(unsigned int index, unsigned int *neighborindex, - unsigned int *coord, unsigned int species_index, - LB_node_force_density_gpu node_f, LB_nodes_gpu lb_node, +ek_add_advection_to_flux(unsigned int index, unsigned int *coord, + unsigned int species_index, LB_nodes_gpu lb_node, LB_parameters_gpu *ek_lbparameters_gpu) { float dx[3]; unsigned int di[3]; @@ -1376,7 +1373,6 @@ __global__ void ek_calculate_quantities(unsigned int species_index, LB_nodes_gpu lb_node, LB_node_force_density_gpu node_f, LB_parameters_gpu *ek_lbparameters_gpu, - LB_rho_v_gpu *d_v, uint64_t philox_counter) { unsigned int index = ek_getThreadIndex(); @@ -1479,8 +1475,8 @@ __global__ void ek_calculate_quantities(unsigned int species_index, /* advective contribution to flux */ if (ek_parameters_gpu->advection) - ek_add_advection_to_flux(index, neighborindex, coord, species_index, - node_f, lb_node, ek_lbparameters_gpu); + ek_add_advection_to_flux(index, coord, species_index, lb_node, + ek_lbparameters_gpu); /* fluctuation contribution to flux */ if (ek_parameters_gpu->fluctuations) @@ -1643,9 +1639,7 @@ __global__ void ek_propagate_densities(unsigned int species_index) { } } -__global__ void ek_apply_boundaries(unsigned int species_index, - LB_nodes_gpu lbnode, - LB_node_force_density_gpu node_f) { +__global__ void ek_apply_boundaries(LB_nodes_gpu lbnode) { unsigned int index = ek_getThreadIndex(); unsigned int neighborindex[22]; @@ -2140,7 +2134,7 @@ void ek_integrate() { for (unsigned i = 0; i < ek_parameters.number_of_species; i++) { KERNELCALL(ek_clear_fluxes, dim_grid, threads_per_block); KERNELCALL(ek_calculate_quantities, dim_grid, threads_per_block, i, - *current_nodes, node_f, ek_lbparameters_gpu, ek_lb_device_values, + *current_nodes, node_f, ek_lbparameters_gpu, philox_counter.value()); KERNELCALL(ek_propagate_densities, dim_grid, threads_per_block, i); @@ -2304,8 +2298,6 @@ int ek_init() { cuda_safe_mem(cudaMalloc((void **)&charge_gpu, sizeof(float))); - lb_get_device_values_pointer(&ek_lb_device_values); - if (cudaGetLastError() != cudaSuccess) { fprintf(stderr, "ERROR: Failed to allocate\n"); return 1; @@ -2600,14 +2592,12 @@ int ek_node_get_flux(int species, int x, int y, int z, double *flux) { KERNELCALL(ek_clear_fluxes, dim_grid, threads_per_block); KERNELCALL(ek_calculate_quantities, dim_grid, threads_per_block, static_cast(ek_parameters.species_index[species]), - *current_nodes, node_f, ek_lbparameters_gpu, ek_lb_device_values, + *current_nodes, node_f, ek_lbparameters_gpu, philox_counter.value()); reset_LB_force_densities_GPU(false); #ifdef EK_BOUNDARIES - KERNELCALL(ek_apply_boundaries, dim_grid, threads_per_block, - static_cast(ek_parameters.species_index[species]), - *current_nodes, node_f); + KERNELCALL(ek_apply_boundaries, dim_grid, threads_per_block, *current_nodes); #endif cuda_safe_mem(cudaMemcpy(fluxes.data(), ek_parameters.j, @@ -2823,14 +2813,12 @@ int ek_print_vtk_flux(int species, char *filename) { KERNELCALL(ek_clear_fluxes, dim_grid, threads_per_block); KERNELCALL(ek_calculate_quantities, dim_grid, threads_per_block, static_cast(ek_parameters.species_index[species]), - *current_nodes, node_f, ek_lbparameters_gpu, ek_lb_device_values, + *current_nodes, node_f, ek_lbparameters_gpu, philox_counter.value()); reset_LB_force_densities_GPU(false); #ifdef EK_BOUNDARIES - KERNELCALL(ek_apply_boundaries, dim_grid, threads_per_block, - static_cast(ek_parameters.species_index[species]), - *current_nodes, node_f); + KERNELCALL(ek_apply_boundaries, dim_grid, threads_per_block, *current_nodes); #endif cuda_safe_mem(cudaMemcpy(fluxes.data(), ek_parameters.j, @@ -3052,13 +3040,12 @@ int ek_print_vtk_flux_fluc(int species, char *filename) { KERNELCALL(ek_clear_fluxes, dim_grid, threads_per_block); KERNELCALL(ek_calculate_quantities, dim_grid, threads_per_block, static_cast(ek_parameters.species_index[species]), - *current_nodes, node_f, ek_lbparameters_gpu, ek_lb_device_values, + *current_nodes, node_f, ek_lbparameters_gpu, philox_counter.value()); reset_LB_force_densities_GPU(false); #ifdef EK_BOUNDARIES - KERNELCALL(ek_apply_boundaries, dim_grid, threads_per_block, - ek_parameters.species_index[species], *current_nodes, node_f); + KERNELCALL(ek_apply_boundaries, dim_grid, threads_per_block, *current_nodes); #endif cuda_safe_mem(cudaMemcpy(fluxes.data(), ek_parameters.j_fluc, @@ -3282,14 +3269,12 @@ int ek_print_vtk_flux_link(int species, char *filename) { KERNELCALL(ek_clear_fluxes, dim_grid, threads_per_block); KERNELCALL(ek_calculate_quantities, dim_grid, threads_per_block, static_cast(ek_parameters.species_index[species]), - *current_nodes, node_f, ek_lbparameters_gpu, ek_lb_device_values, + *current_nodes, node_f, ek_lbparameters_gpu, philox_counter.value()); reset_LB_force_densities_GPU(false); #ifdef EK_BOUNDARIES - KERNELCALL(ek_apply_boundaries, dim_grid, threads_per_block, - static_cast(ek_parameters.species_index[species]), - *current_nodes, node_f); + KERNELCALL(ek_apply_boundaries, dim_grid, threads_per_block, *current_nodes); #endif cuda_safe_mem(cudaMemcpy(fluxes.data(), ek_parameters.j, @@ -3631,13 +3616,11 @@ void ek_print_lbpar() { printf(" unsigned int dim_y = %d;\n", lbpar_gpu.dim[1]); printf(" unsigned int dim_z = %d;\n", lbpar_gpu.dim[2]); printf(" unsigned int number_of_nodes = %d;\n", lbpar_gpu.number_of_nodes); - printf(" int calc_val = %d;\n", lbpar_gpu.calc_val); printf(" int external_force_density = %d;\n", lbpar_gpu.external_force_density); printf(" float ext_force_density[3] = {%f, %f, %f};\n", lbpar_gpu.ext_force_density[0], lbpar_gpu.ext_force_density[1], lbpar_gpu.ext_force_density[2]); - printf(" unsigned int reinit = %d;\n", lbpar_gpu.reinit); printf("}\n"); } diff --git a/src/core/grid_based_algorithms/lb.cpp b/src/core/grid_based_algorithms/lb.cpp index c623d9c96bd..0b6016a1383 100644 --- a/src/core/grid_based_algorithms/lb.cpp +++ b/src/core/grid_based_algorithms/lb.cpp @@ -190,15 +190,15 @@ HaloCommunicator update_halo_comm = HaloCommunicator(0); /** * @brief Initialize fluid nodes. - * @param[out] fields Vector containing the fluid nodes + * @param[out] lb_fields Vector containing the fluid nodes * @param[in] lb_parameters Parameters for the LB * @param[in] lb_lattice Lattice instance */ -void lb_initialize_fields(std::vector &fields, +void lb_initialize_fields(std::vector &lb_fields, LB_Parameters const &lb_parameters, Lattice const &lb_lattice) { - fields.resize(lb_lattice.halo_grid_volume); - for (auto &field : fields) { + lb_fields.resize(lb_lattice.halo_grid_volume); + for (auto &field : lb_fields) { field.force_density = lb_parameters.ext_force_density; #ifdef LB_BOUNDARIES field.boundary = false; @@ -273,7 +273,7 @@ void lb_reinit_fluid(std::vector &lb_fields, Lattice const &lb_lattice, LB_Parameters const &lb_parameters) { lb_set_equilibrium_populations(lb_lattice, lb_parameters); - lb_initialize_fields(lbfields, lb_parameters, lb_lattice); + lb_initialize_fields(lb_fields, lb_parameters, lb_lattice); } void lb_reinit_parameters(LB_Parameters &lb_parameters) { diff --git a/src/core/grid_based_algorithms/lb_boundaries.cpp b/src/core/grid_based_algorithms/lb_boundaries.cpp index 8ffffeaf944..f140d06a95f 100644 --- a/src/core/grid_based_algorithms/lb_boundaries.cpp +++ b/src/core/grid_based_algorithms/lb_boundaries.cpp @@ -231,8 +231,6 @@ void lb_init_boundaries() { #endif /* defined (CUDA) && defined (LB_BOUNDARIES_GPU) */ } else if (lattice_switch == ActiveLB::CPU) { #if defined(LB_BOUNDARIES) - auto const lblattice = lb_lbfluid_get_lattice(); - boost::for_each(lbfields, [](auto &f) { f.boundary = 0; }); auto const node_pos = calc_node_pos(comm_cart); diff --git a/src/core/grid_based_algorithms/lb_collective_interface.cpp b/src/core/grid_based_algorithms/lb_collective_interface.cpp index fade1ffc743..4ea9ddcd5ef 100644 --- a/src/core/grid_based_algorithms/lb_collective_interface.cpp +++ b/src/core/grid_based_algorithms/lb_collective_interface.cpp @@ -91,10 +91,10 @@ mpi_lb_get_interpolated_density(Utils::Vector3d const &pos) { REGISTER_CALLBACK_ONE_RANK(mpi_lb_get_interpolated_density) auto mpi_lb_get_density(Utils::Vector3i const &index) { - return detail::lb_calc_fluid_kernel( - index, [&](auto const &modes, auto const &force_density) { - return lb_calc_density(modes, lbpar); - }); + return detail::lb_calc_fluid_kernel(index, + [&](auto const &modes, auto const &) { + return lb_calc_density(modes, lbpar); + }); } REGISTER_CALLBACK_ONE_RANK(mpi_lb_get_density) diff --git a/src/core/grid_based_algorithms/lb_interface.cpp b/src/core/grid_based_algorithms/lb_interface.cpp index 5d71d4683ec..cd460c1a28f 100644 --- a/src/core/grid_based_algorithms/lb_interface.cpp +++ b/src/core/grid_based_algorithms/lb_interface.cpp @@ -1100,8 +1100,6 @@ void lb_lbnode_set_pop(const Utils::Vector3i &ind, } } -const Lattice &lb_lbfluid_get_lattice() { return lblattice; } - ActiveLB lb_lbfluid_get_lattice_switch() { return lattice_switch; } static void mpi_lb_lbfluid_calc_fluid_momentum_local() { diff --git a/src/core/grid_based_algorithms/lb_interface.hpp b/src/core/grid_based_algorithms/lb_interface.hpp index f146a69b29f..cbb6eb3c2ce 100644 --- a/src/core/grid_based_algorithms/lb_interface.hpp +++ b/src/core/grid_based_algorithms/lb_interface.hpp @@ -66,11 +66,6 @@ uint64_t lb_lbfluid_get_rng_state(); */ void lb_lbfluid_set_rng_state(uint64_t counter); -/** - * @brief Return the instance of the Lattice within the LB method. - */ -const Lattice &lb_lbfluid_get_lattice(); - /** * @brief Get the global variable @ref lattice_switch. */ diff --git a/src/core/grid_based_algorithms/lb_particle_coupling.cpp b/src/core/grid_based_algorithms/lb_particle_coupling.cpp index 630a756713b..9968242523f 100644 --- a/src/core/grid_based_algorithms/lb_particle_coupling.cpp +++ b/src/core/grid_based_algorithms/lb_particle_coupling.cpp @@ -198,15 +198,12 @@ bool in_box(Vector const &pos, Box const &box) { * @brief Check if a position is within the local box + halo. * * @param pos Position to check - * @param local_box Geometry to check * @param halo Halo * * @return True iff the point is inside of the box up to halo. */ -template -bool in_local_domain(Vector const &pos, LocalBox const &local_box, - T const &halo = {}) { - auto const halo_vec = Vector::broadcast(halo); +inline bool in_local_domain(Vector3d const &pos, double halo = 0.) { + auto const halo_vec = Vector3d::broadcast(halo); return in_box( pos, {local_geo.my_left() - halo_vec, local_geo.my_right() + halo_vec}); @@ -223,7 +220,7 @@ bool in_local_domain(Vector const &pos, LocalBox const &local_box, bool in_local_halo(Vector3d const &pos) { auto const halo = 0.5 * lb_lbfluid_get_agrid(); - return in_local_domain(pos, local_geo, halo); + return in_local_domain(pos, halo); } #ifdef ENGINE @@ -298,7 +295,7 @@ void lb_lbcoupling_calc_particle_lattice_ia(bool couple_virtual, /* Particle is in our LB volume, so this node * is responsible to adding its force */ - if (in_local_domain(p.r.p, local_geo)) { + if (in_local_domain(p.r.p)) { auto const force = lb_viscous_coupling( p, noise_amplitude * f_random(p.identity()), time_step); /* add force to the particle */ diff --git a/src/core/grid_based_algorithms/lbgpu.cpp b/src/core/grid_based_algorithms/lbgpu.cpp index 3d01fecb276..b957539962d 100644 --- a/src/core/grid_based_algorithms/lbgpu.cpp +++ b/src/core/grid_based_algorithms/lbgpu.cpp @@ -76,14 +76,10 @@ LB_parameters_gpu lbpar_gpu = { // number_of_boundnodes 0, #endif - // calc_val - 1, // external_force 0, // ext_force {0.0, 0.0, 0.0}, - // reinit - 0, // Thermal energy 0.0}; @@ -99,7 +95,6 @@ void lb_reinit_fluid_gpu() { if (lbpar_gpu.number_of_nodes != 0) { lb_reinit_GPU(&lbpar_gpu); lb_reinit_extern_nodeforce_GPU(&lbpar_gpu); - lbpar_gpu.reinit = 1; } } diff --git a/src/core/grid_based_algorithms/lbgpu.hpp b/src/core/grid_based_algorithms/lbgpu.hpp index af0c8ebb6af..d50b11603e2 100644 --- a/src/core/grid_based_algorithms/lbgpu.hpp +++ b/src/core/grid_based_algorithms/lbgpu.hpp @@ -80,14 +80,11 @@ struct LB_parameters_gpu { #ifdef LB_BOUNDARIES_GPU unsigned int number_of_boundnodes; #endif - /** to calculate and print out physical values */ - int calc_val; int external_force_density; Utils::Array ext_force_density; - unsigned int reinit; // Thermal energy float kT; }; @@ -147,7 +144,6 @@ struct LB_rho_v_gpu { }; void lb_GPU_sanity_checks(); -void lb_get_device_values_pointer(LB_rho_v_gpu **pointer_address); void lb_get_boundary_force_pointer(float **pointer_address); void lb_get_para_pointer(LB_parameters_gpu **pointer_address); @@ -222,7 +218,7 @@ uint64_t lb_coupling_get_rng_state_gpu(); void lb_coupling_set_rng_state_gpu(uint64_t counter); /** Calculate the node index from its coordinates */ -inline unsigned int calculate_node_index(LB_parameters_gpu const &lbpar, +inline unsigned int calculate_node_index(LB_parameters_gpu const &lbpar_gpu, Utils::Vector3i const &coord) { return static_cast( Utils::get_linear_index(coord, Utils::Vector3i(lbpar_gpu.dim))); diff --git a/src/core/grid_based_algorithms/lbgpu_cuda.cu b/src/core/grid_based_algorithms/lbgpu_cuda.cu index 3d80fd71a52..9dd295615a4 100644 --- a/src/core/grid_based_algorithms/lbgpu_cuda.cu +++ b/src/core/grid_based_algorithms/lbgpu_cuda.cu @@ -1221,7 +1221,6 @@ velocity_interpolation(LB_nodes_gpu n_a, float const *particle_position, * @param[in,out] particle_force Particle force * @param[in] part_index Particle id / thread id * @param[out] node_index Node index around (8) particle - * @param[in] d_v Local device values * @param[in] flag_cs Determine if we are at the centre (0, * typical) or at the source (1, swimmer only) * @param[in] philox_counter Philox counter @@ -1235,8 +1234,8 @@ __device__ void calc_viscous_force( LB_nodes_gpu n_a, Utils::Array &delta, CUDA_particle_data *particle_data, float *particle_force, unsigned int part_index, float *delta_j, - Utils::Array &node_index, LB_rho_v_gpu *d_v, - bool flag_cs, uint64_t philox_counter, float friction, float time_step) { + Utils::Array &node_index, bool flag_cs, + uint64_t philox_counter, float friction, float time_step) { auto const flag_cs_float = static_cast(flag_cs); // Zero out workspace #pragma unroll @@ -1264,7 +1263,7 @@ __device__ void calc_viscous_force( #ifdef ENGINE // First calculate interpolated velocity for dipole source, - // such that we don't overwrite mode, d_v, etc. for the rest of the function + // such that we don't overwrite mode, etc. for the rest of the function float direction = float(particle_data[part_index].swim.push_pull) * particle_data[part_index].swim.dipole_length; // Extrapolate position by dipole length if we are at the centre of the @@ -1863,7 +1862,6 @@ __global__ void integrate(LB_nodes_gpu n_a, LB_nodes_gpu n_b, LB_rho_v_gpu *d_v, * @param[in,out] particle_data Particle position and velocity * @param[in,out] particle_force Particle force * @param[out] node_f Local node force - * @param[in] d_v Local device values * @param[in] couple_virtual If true, virtual particles are also coupled * @param[in] philox_counter Philox counter * @param[in] friction Friction constant for the particle coupling @@ -1872,11 +1870,12 @@ __global__ void integrate(LB_nodes_gpu n_a, LB_nodes_gpu n_b, LB_rho_v_gpu *d_v, * interpolation */ template -__global__ void calc_fluid_particle_ia( - LB_nodes_gpu n_a, Utils::Span particle_data, - float *particle_force, LB_node_force_density_gpu node_f, LB_rho_v_gpu *d_v, - bool couple_virtual, uint64_t philox_counter, float friction, - float time_step) { +__global__ void +calc_fluid_particle_ia(LB_nodes_gpu n_a, + Utils::Span particle_data, + float *particle_force, LB_node_force_density_gpu node_f, + bool couple_virtual, uint64_t philox_counter, + float friction, float time_step) { unsigned int part_index = blockIdx.y * gridDim.x * blockDim.x + blockDim.x * blockIdx.x + threadIdx.x; @@ -1892,15 +1891,14 @@ __global__ void calc_fluid_particle_ia( * force that acts back onto the fluid. */ calc_viscous_force( n_a, delta, particle_data.data(), particle_force, part_index, delta_j, - node_index, d_v, false, philox_counter, friction, time_step); + node_index, false, philox_counter, friction, time_step); calc_node_force(delta, delta_j, node_index, node_f); #ifdef ENGINE if (particle_data[part_index].swim.swimming) { calc_viscous_force( n_a, delta, particle_data.data(), particle_force, part_index, - delta_j, node_index, d_v, true, philox_counter, friction, - time_step); + delta_j, node_index, true, philox_counter, friction, time_step); calc_node_force(delta, delta_j, node_index, node_f); } #endif @@ -1982,8 +1980,8 @@ __global__ void lb_print_node(unsigned int single_nodeindex, } } -__global__ void momentum(LB_nodes_gpu n_a, LB_rho_v_gpu *d_v, - LB_node_force_density_gpu node_f, float *sum) { +__global__ void momentum(LB_nodes_gpu n_a, LB_node_force_density_gpu node_f, + float *sum) { unsigned int index = blockIdx.y * gridDim.x * blockDim.x + blockDim.x * blockIdx.x + threadIdx.x; @@ -2041,10 +2039,6 @@ void lb_get_boundary_force_pointer(float **pointer_address) { #endif } -void lb_get_device_values_pointer(LB_rho_v_gpu **pointer_address) { - *pointer_address = device_rho_v; -} - /** Initialization for the lb gpu fluid called from host * @param lbpar_gpu Pointer to parameters to setup the lb field */ @@ -2231,16 +2225,15 @@ void lb_calc_particle_lattice_ia_gpu(bool couple_virtual, double friction, assert(rng_counter_coupling_gpu); KERNELCALL(calc_fluid_particle_ia, dim_grid, threads_per_block, *current_nodes, device_particles, - gpu_get_particle_force_pointer(), node_f, device_rho_v, - couple_virtual, rng_counter_coupling_gpu->value(), - static_cast(friction), static_cast(time_step)); + gpu_get_particle_force_pointer(), node_f, couple_virtual, + rng_counter_coupling_gpu->value(), static_cast(friction), + static_cast(time_step)); } else { // We use a dummy value for the RNG counter if no temperature is set. KERNELCALL(calc_fluid_particle_ia, dim_grid, threads_per_block, *current_nodes, device_particles, - gpu_get_particle_force_pointer(), node_f, device_rho_v, - couple_virtual, 0, static_cast(friction), - static_cast(time_step)); + gpu_get_particle_force_pointer(), node_f, couple_virtual, 0, + static_cast(friction), static_cast(time_step)); } } template void lb_calc_particle_lattice_ia_gpu<8>(bool couple_virtual, @@ -2343,8 +2336,8 @@ void lb_calc_fluid_momentum_GPU(double *host_mom) { dim3 dim_grid = calculate_dim_grid(lbpar_gpu.number_of_nodes, 4, threads_per_block); - KERNELCALL(momentum, dim_grid, threads_per_block, *current_nodes, - device_rho_v, node_f, tot_momentum); + KERNELCALL(momentum, dim_grid, threads_per_block, *current_nodes, node_f, + tot_momentum); cuda_safe_mem(cudaMemcpy(host_momentum, tot_momentum, 3 * sizeof(float), cudaMemcpyDeviceToHost)); diff --git a/src/core/immersed_boundary/ImmersedBoundaries.cpp b/src/core/immersed_boundary/ImmersedBoundaries.cpp index a36b543bf73..930b74193f0 100644 --- a/src/core/immersed_boundary/ImmersedBoundaries.cpp +++ b/src/core/immersed_boundary/ImmersedBoundaries.cpp @@ -104,7 +104,6 @@ void ImmersedBoundaries::calc_volumes(CellStructure &cs) { // Loop over all particles on local node cs.bond_loop([&tempVol](Particle &p1, int bond_id, Utils::Span partners) { - auto const &iaparams = *bonded_ia_params.at(bond_id); auto vol_cons_params = vol_cons_parameters(p1); if (vol_cons_params && diff --git a/src/core/integrate.cpp b/src/core/integrate.cpp index b8fc96f8302..69950a960fd 100644 --- a/src/core/integrate.cpp +++ b/src/core/integrate.cpp @@ -195,7 +195,7 @@ void integrator_step_2(ParticleRange &particles, double kT) { break; #ifdef STOKESIAN_DYNAMICS case INTEG_METHOD_SD: - stokesian_dynamics_step_2(particles); + // Nothing break; #endif // STOKESIAN_DYNAMICS default: diff --git a/src/core/integrators/stokesian_dynamics_inline.hpp b/src/core/integrators/stokesian_dynamics_inline.hpp index 238da65e55b..c7b5e0ec1e0 100644 --- a/src/core/integrators/stokesian_dynamics_inline.hpp +++ b/src/core/integrators/stokesian_dynamics_inline.hpp @@ -54,7 +54,5 @@ inline void stokesian_dynamics_step_1(const ParticleRange &particles, increment_sim_time(time_step); } -inline void stokesian_dynamics_step_2(const ParticleRange &particles) {} - #endif // STOKESIAN_DYNAMICS #endif diff --git a/src/core/io/writer/h5md_core.cpp b/src/core/io/writer/h5md_core.cpp index b25232484ff..91a5ec98ae0 100644 --- a/src/core/io/writer/h5md_core.cpp +++ b/src/core/io/writer/h5md_core.cpp @@ -188,8 +188,7 @@ void File::load_file(const std::string &file_path) { load_datasets(); } -void write_box(const BoxGeometry &geometry, const h5xx::file &h5md_file, - h5xx::dataset &dataset) { +static void write_box(const BoxGeometry &geometry, h5xx::dataset &dataset) { auto const extents = static_cast(dataset).extents(); extend_dataset(dataset, Vector2hs{1, 0}); h5xx::write_dataset(dataset, geometry.length(), @@ -304,7 +303,7 @@ void write_td_particle_property(hsize_t prefix, hsize_t n_part_global, void File::write(const ParticleRange &particles, double time, int step, BoxGeometry const &geometry) { - write_box(geometry, m_h5md_file, datasets["particles/atoms/box/edges/value"]); + write_box(geometry, datasets["particles/atoms/box/edges/value"]); write_connectivity(particles); int const n_part_local = particles.size(); diff --git a/src/core/nonbonded_interactions/thole.hpp b/src/core/nonbonded_interactions/thole.hpp index 25c60a93cdf..0e982ed7a1c 100644 --- a/src/core/nonbonded_interactions/thole.hpp +++ b/src/core/nonbonded_interactions/thole.hpp @@ -80,8 +80,7 @@ inline double thole_pair_energy(Particle const &p1, Particle const &p2, auto const sd = thole_s * dist; auto const S_r = 1.0 - (1.0 + sd / 2.0) * exp(-sd); // Subtract p3m shortrange energy and add thole energy - return Coulomb::pair_energy(p1, p2, thole_q1q2 * (-1.0 + S_r), d, dist, - dist * dist); + return Coulomb::pair_energy(p1, p2, thole_q1q2 * (-1.0 + S_r), d, dist); } return 0.0; } diff --git a/src/core/reaction_methods/ReactionAlgorithm.cpp b/src/core/reaction_methods/ReactionAlgorithm.cpp index 925baab8523..23e9449d1fa 100644 --- a/src/core/reaction_methods/ReactionAlgorithm.cpp +++ b/src/core/reaction_methods/ReactionAlgorithm.cpp @@ -250,8 +250,7 @@ ReactionAlgorithm::make_reaction_attempt( * when a reaction attempt is rejected. */ void ReactionAlgorithm::restore_properties( - std::vector const &property_list, - const int number_of_saved_properties) { + std::vector const &property_list) { // this function restores all properties of all particles provided in the // property list, the format of the property list is (p_id,charge,type) // repeated for each particle that occurs in that list @@ -303,9 +302,6 @@ void ReactionAlgorithm::generic_oneway_reaction( std::vector p_ids_created_particles; std::vector hidden_particles_properties; std::vector changed_particles_properties; - // save p_id, charge and type of the reactant particle, only thing we - // need to hide the particle and recover it - const int number_of_saved_properties = 3; std::tie(changed_particles_properties, p_ids_created_particles, hidden_particles_properties) = @@ -353,10 +349,9 @@ void ReactionAlgorithm::generic_oneway_reaction( delete_particle(p_ids_created_particle); } // 2) restore previously hidden reactant particles - restore_properties(hidden_particles_properties, number_of_saved_properties); + restore_properties(hidden_particles_properties); // 3) restore previously changed reactant particles - restore_properties(changed_particles_properties, - number_of_saved_properties); + restore_properties(changed_particles_properties); } } diff --git a/src/core/reaction_methods/ReactionAlgorithm.hpp b/src/core/reaction_methods/ReactionAlgorithm.hpp index 0253a1eb523..f0c9ce312be 100644 --- a/src/core/reaction_methods/ReactionAlgorithm.hpp +++ b/src/core/reaction_methods/ReactionAlgorithm.hpp @@ -125,8 +125,7 @@ class ReactionAlgorithm { std::vector> generate_new_particle_positions(int type, int n_particles); void - restore_properties(std::vector const &property_list, - int number_of_saved_properties); + restore_properties(std::vector const &property_list); /** * @brief draws a random integer from the uniform distribution in the range diff --git a/src/core/reaction_methods/WidomInsertion.cpp b/src/core/reaction_methods/WidomInsertion.cpp index d8ca7342b2c..c0680182bdc 100644 --- a/src/core/reaction_methods/WidomInsertion.cpp +++ b/src/core/reaction_methods/WidomInsertion.cpp @@ -42,11 +42,6 @@ double WidomInsertion::calculate_particle_insertion_potential_energy( std::vector p_ids_created_particles; std::vector hidden_particles_properties; std::vector changed_particles_properties; - - // save p_id, charge and type of the reactant particle, only thing we - // need to hide the particle and recover it - auto constexpr number_of_saved_properties = 3; - std::tie(changed_particles_properties, p_ids_created_particles, hidden_particles_properties) = make_reaction_attempt(current_reaction); @@ -59,9 +54,9 @@ double WidomInsertion::calculate_particle_insertion_potential_energy( delete_particle(p_ids_created_particle); } // 2) restore previously hidden reactant particles - restore_properties(hidden_particles_properties, number_of_saved_properties); + restore_properties(hidden_particles_properties); // 3) restore previously changed reactant particles - restore_properties(changed_particles_properties, number_of_saved_properties); + restore_properties(changed_particles_properties); // calculate the particle insertion potential energy auto const E_pot_insertion = E_pot_new - E_pot_old; diff --git a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp index 47c5c876336..85c6368ef8f 100644 --- a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp +++ b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp @@ -226,9 +226,6 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, // measure energies auto const obs_energy = calculate_energy(); - auto const &p1 = get_particle_data(pid1); - auto const &p2 = get_particle_data(pid2); - auto const &p3 = get_particle_data(pid3); auto const none_energy = 0.0; auto const harm_energy = 0.5 * harm_bond.k * Utils::sqr(harm_bond.r - dist); auto const fene_energy = diff --git a/src/core/unit_tests/Verlet_list_test.cpp b/src/core/unit_tests/Verlet_list_test.cpp index c5b9e202a93..6d96b9ca95a 100644 --- a/src/core/unit_tests/Verlet_list_test.cpp +++ b/src/core/unit_tests/Verlet_list_test.cpp @@ -148,7 +148,6 @@ BOOST_DATA_TEST_CASE_F(ParticleFactory, verlet_list_update, boost::mpi::communicator world; auto const box_l = 8.; - auto const box_center = box_l / 2.; espresso::system->set_box_l(Utils::Vector3d::broadcast(box_l)); espresso::system->set_node_grid(node_grid); diff --git a/src/core/unit_tests/random_test.cpp b/src/core/unit_tests/random_test.cpp index eef455f911f..8c76e5e2f5a 100644 --- a/src/core/unit_tests/random_test.cpp +++ b/src/core/unit_tests/random_test.cpp @@ -35,7 +35,7 @@ BOOST_AUTO_TEST_CASE(test_noise_statistics) { constexpr std::size_t const sample_size = 60'000; - constexpr std::size_t const x = 0, y = 1, z = 2; + constexpr std::size_t const x = 0, y = 1; constexpr double const tol = 1e-12; double value = 1; diff --git a/src/core/virtual_sites/lb_inertialess_tracers.cpp b/src/core/virtual_sites/lb_inertialess_tracers.cpp index c564ad8f24a..b93a9579d12 100644 --- a/src/core/virtual_sites/lb_inertialess_tracers.cpp +++ b/src/core/virtual_sites/lb_inertialess_tracers.cpp @@ -48,7 +48,6 @@ bool *isHaloCache = nullptr; namespace { bool in_local_domain(Utils::Vector3d const &pos) { - auto const lblattice = lb_lbfluid_get_lattice(); auto const my_left = local_geo.my_left(); auto const my_right = local_geo.my_right(); From 52ce02ea585ea4c8f7a710f391e19c9a3a2fccd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 18 Jan 2022 19:53:56 +0100 Subject: [PATCH 20/99] core: Remove superfluous semicolons --- src/core/DomainDecomposition.hpp | 2 +- src/core/EspressoSystemInterface.hpp | 40 +++++++++---------- src/core/accumulators/AccumulatorBase.hpp | 4 +- src/core/actor/Actor.hpp | 6 +-- src/core/cells.hpp | 2 +- src/core/collision.hpp | 2 +- src/core/constraints/Constraint.hpp | 2 +- src/core/grid_based_algorithms/halo.hpp | 2 +- src/core/grid_based_algorithms/lbgpu_cuda.cu | 2 +- src/core/io/writer/h5md_core.cpp | 4 +- src/core/io/writer/h5md_core.hpp | 18 ++++----- src/core/pair_criteria/pair_criteria.hpp | 8 ++-- src/core/virtual_sites/VirtualSites.hpp | 10 ++--- src/script_interface/GlobalContext.hpp | 2 +- src/script_interface/LocalContext.hpp | 2 +- .../cluster_analysis/ClusterStructure.hpp | 4 +- .../CollisionDetection.hpp | 2 +- .../constraints/Constraints.hpp | 2 +- .../constraints/ShapeBasedConstraint.hpp | 2 +- .../lbboundaries/LBBoundary.hpp | 2 +- .../tests/ObjectHandle_test.cpp | 2 +- .../VirtualSitesInertialessTracers.hpp | 4 +- .../virtual_sites/VirtualSitesOff.hpp | 4 +- .../virtual_sites/VirtualSitesRelative.hpp | 4 +- src/utils/include/utils/Array.hpp | 14 +++---- src/utils/include/utils/matrix.hpp | 8 ++-- 26 files changed, 76 insertions(+), 78 deletions(-) diff --git a/src/core/DomainDecomposition.hpp b/src/core/DomainDecomposition.hpp index 747f19af010..ee6cc1e18c8 100644 --- a/src/core/DomainDecomposition.hpp +++ b/src/core/DomainDecomposition.hpp @@ -96,7 +96,7 @@ struct DomainDecomposition : public ParticleDecomposition { } GhostCommunicator const &collect_ghost_force_comm() const override { return m_collect_ghost_force_comm; - }; + } Utils::Span local_cells() override { return Utils::make_span(m_local_cells); diff --git a/src/core/EspressoSystemInterface.hpp b/src/core/EspressoSystemInterface.hpp index ef5b27e973d..9a146495a66 100644 --- a/src/core/EspressoSystemInterface.hpp +++ b/src/core/EspressoSystemInterface.hpp @@ -47,74 +47,72 @@ class EspressoSystemInterface : public SystemInterface { m_instance = new EspressoSystemInterface; return *m_instance; - }; + } void init() override; void update() override; #ifdef CUDA - float *rGpuBegin() override { return m_r_gpu_begin; }; - bool hasRGpu() override { return true; }; + float *rGpuBegin() override { return m_r_gpu_begin; } + bool hasRGpu() override { return true; } void requestRGpu() override { m_needsRGpu = hasRGpu(); m_splitParticleStructGpu |= m_needsRGpu; m_gpu |= m_needsRGpu; enableParticleCommunication(); - }; + } #ifdef DIPOLES - float *dipGpuBegin() override { return m_dip_gpu_begin; }; - bool hasDipGpu() override { return true; }; + float *dipGpuBegin() override { return m_dip_gpu_begin; } + bool hasDipGpu() override { return true; } void requestDipGpu() override { m_needsDipGpu = hasDipGpu(); m_splitParticleStructGpu |= m_needsRGpu; m_gpu |= m_needsRGpu; enableParticleCommunication(); - }; + } #endif #ifdef ELECTROSTATICS - float *qGpuBegin() override { return m_q_gpu_begin; }; - bool hasQGpu() override { return true; }; + float *qGpuBegin() override { return m_q_gpu_begin; } + bool hasQGpu() override { return true; } void requestQGpu() override { m_needsQGpu = hasQGpu(); m_splitParticleStructGpu |= m_needsQGpu; m_gpu |= m_needsQGpu; enableParticleCommunication(); - }; + } #endif void requestParticleStructGpu() { m_needsParticleStructGpu = true; m_gpu |= m_needsParticleStructGpu; enableParticleCommunication(); - }; + } - float *fGpuBegin() override { return gpu_get_particle_force_pointer(); }; - bool hasFGpu() override { return true; }; + float *fGpuBegin() override { return gpu_get_particle_force_pointer(); } + bool hasFGpu() override { return true; } void requestFGpu() override { m_needsFGpu = hasFGpu(); m_gpu |= m_needsFGpu; enableParticleCommunication(); - }; + } #ifdef ROTATION - float *torqueGpuBegin() override { - return gpu_get_particle_torque_pointer(); - }; - bool hasTorqueGpu() override { return true; }; + float *torqueGpuBegin() override { return gpu_get_particle_torque_pointer(); } + bool hasTorqueGpu() override { return true; } void requestTorqueGpu() override { m_needsTorqueGpu = hasTorqueGpu(); m_gpu |= m_needsTorqueGpu; enableParticleCommunication(); - }; + } #endif float *eGpu() override { // cast pointer from struct of floats to array of floats // https://stackoverflow.com/a/29278260 return reinterpret_cast(gpu_get_energy_pointer()); - }; + } #endif // ifdef CUDA @@ -126,7 +124,7 @@ class EspressoSystemInterface : public SystemInterface { #else return 0; #endif - }; + } protected: static EspressoSystemInterface *m_instance; diff --git a/src/core/accumulators/AccumulatorBase.hpp b/src/core/accumulators/AccumulatorBase.hpp index 7c311c00818..6c6528be246 100644 --- a/src/core/accumulators/AccumulatorBase.hpp +++ b/src/core/accumulators/AccumulatorBase.hpp @@ -26,8 +26,8 @@ namespace Accumulators { class AccumulatorBase { public: - explicit AccumulatorBase(int delta_N = 1) : m_delta_N(delta_N){}; - int &delta_N() { return m_delta_N; }; + explicit AccumulatorBase(int delta_N = 1) : m_delta_N(delta_N) {} + int &delta_N() { return m_delta_N; } virtual ~AccumulatorBase() = default; virtual void update() = 0; diff --git a/src/core/actor/Actor.hpp b/src/core/actor/Actor.hpp index cf9a0ba61be..92d7f22f235 100644 --- a/src/core/actor/Actor.hpp +++ b/src/core/actor/Actor.hpp @@ -27,9 +27,9 @@ */ class Actor { public: - virtual void computeForces(SystemInterface &s){}; - virtual void computeTorques(SystemInterface &s){}; - virtual void computeEnergy(SystemInterface &s){}; + virtual void computeForces(SystemInterface &s) {} + virtual void computeTorques(SystemInterface &s) {} + virtual void computeEnergy(SystemInterface &s) {} virtual ~Actor() = default; }; diff --git a/src/core/cells.hpp b/src/core/cells.hpp index 5c2bdc638d9..4924312ddc9 100644 --- a/src/core/cells.hpp +++ b/src/core/cells.hpp @@ -133,7 +133,7 @@ class PairInfo { PairInfo(int _id1, int _id2, Utils::Vector3d _pos1, Utils::Vector3d _pos2, Utils::Vector3d _vec21, int _node) : id1(_id1), id2(_id2), pos1(_pos1), pos2(_pos2), vec21(_vec21), - node(_node){}; + node(_node) {} int id1; int id2; Utils::Vector3d pos1; diff --git a/src/core/collision.hpp b/src/core/collision.hpp index 8606c337e68..eb01ac4c853 100644 --- a/src/core/collision.hpp +++ b/src/core/collision.hpp @@ -48,7 +48,7 @@ class Collision_parameters { public: Collision_parameters() : mode(COLLISION_MODE_OFF), distance(0.), distance2(0.), bond_centers(-1), - bond_vs(-1), bond_three_particles(-1){}; + bond_vs(-1), bond_three_particles(-1) {} /// collision handling mode, a combination of constants COLLISION_MODE_* int mode; diff --git a/src/core/constraints/Constraint.hpp b/src/core/constraints/Constraint.hpp index fcd0faa895c..d25bfd3453c 100644 --- a/src/core/constraints/Constraint.hpp +++ b/src/core/constraints/Constraint.hpp @@ -54,7 +54,7 @@ class Constraint { */ virtual bool fits_in_box(Utils::Vector3d const &box) const = 0; - virtual void reset_force(){}; + virtual void reset_force() {} virtual ~Constraint() = default; }; diff --git a/src/core/grid_based_algorithms/halo.hpp b/src/core/grid_based_algorithms/halo.hpp index ee61d40619a..c985da44cae 100644 --- a/src/core/grid_based_algorithms/halo.hpp +++ b/src/core/grid_based_algorithms/halo.hpp @@ -103,7 +103,7 @@ struct HaloInfo { * parallelization scheme */ class HaloCommunicator { public: - HaloCommunicator(int num) : num(num){}; + HaloCommunicator(int num) : num(num) {} int num; /**< number of halo communications in the scheme */ diff --git a/src/core/grid_based_algorithms/lbgpu_cuda.cu b/src/core/grid_based_algorithms/lbgpu_cuda.cu index 9dd295615a4..b1b95fca77d 100644 --- a/src/core/grid_based_algorithms/lbgpu_cuda.cu +++ b/src/core/grid_based_algorithms/lbgpu_cuda.cu @@ -2507,7 +2507,7 @@ struct lb_lbfluid_mass_of_particle { #else return 1.; #endif - }; + } }; /** Set the populations of a specific node on the GPU diff --git a/src/core/io/writer/h5md_core.cpp b/src/core/io/writer/h5md_core.cpp index 91a5ec98ae0..535077c8a1a 100644 --- a/src/core/io/writer/h5md_core.cpp +++ b/src/core/io/writer/h5md_core.cpp @@ -267,7 +267,7 @@ template struct slice_info {}; template <> struct slice_info<3> { static auto extent(hsize_t n_part_diff) { return Vector3hs{1, n_part_diff, 0}; - }; + } static constexpr auto count() { return Vector3hs{1, 1, 3}; } static auto offset(hsize_t n_time_steps, hsize_t prefix) { return Vector3hs{n_time_steps, prefix, 0}; @@ -275,7 +275,7 @@ template <> struct slice_info<3> { }; template <> struct slice_info<2> { - static auto extent(hsize_t n_part_diff) { return Vector2hs{1, n_part_diff}; }; + static auto extent(hsize_t n_part_diff) { return Vector2hs{1, n_part_diff}; } static constexpr auto count() { return Vector2hs{1, 1}; } static auto offset(hsize_t n_time_steps, hsize_t prefix) { return Vector2hs{n_time_steps, prefix}; diff --git a/src/core/io/writer/h5md_core.hpp b/src/core/io/writer/h5md_core.hpp index cd58125f9a7..c912019fbb7 100644 --- a/src/core/io/writer/h5md_core.hpp +++ b/src/core/io/writer/h5md_core.hpp @@ -75,7 +75,7 @@ class File { m_velocity_unit(std::move(velocity_unit)), m_charge_unit(std::move(charge_unit)), m_comm(std::move(comm)) { init_file(file_path); - }; + } ~File() = default; /** @@ -99,49 +99,49 @@ class File { * @brief Retrieve the path to the hdf5 file. * @return The path as a string. */ - std::string file_path() const { return m_h5md_file.name(); }; + std::string file_path() const { return m_h5md_file.name(); } /** * @brief Retrieve the path to the simulation script. * @return The path as a string. */ - std::string &script_path() { return m_script_path; }; + std::string &script_path() { return m_script_path; } /** * @brief Retrieve the set mass unit. * @return The unit as a string. */ - std::string &mass_unit() { return m_mass_unit; }; + std::string &mass_unit() { return m_mass_unit; } /** * @brief Retrieve the set length unit. * @return The unit as a string. */ - std::string &length_unit() { return m_length_unit; }; + std::string &length_unit() { return m_length_unit; } /** * @brief Retrieve the set time unit. * @return The unit as a string. */ - std::string &time_unit() { return m_time_unit; }; + std::string &time_unit() { return m_time_unit; } /** * @brief Retrieve the set force unit. * @return The unit as a string. */ - std::string &force_unit() { return m_force_unit; }; + std::string &force_unit() { return m_force_unit; } /** * @brief Retrieve the set velocity unit. * @return The unit as a string. */ - std::string &velocity_unit() { return m_velocity_unit; }; + std::string &velocity_unit() { return m_velocity_unit; } /** * @brief Retrieve the set charge unit. * @return The unit as a string. */ - std::string &charge_unit() { return m_charge_unit; }; + std::string &charge_unit() { return m_charge_unit; } /** * @brief Method to enforce flushing the buffer to disk. diff --git a/src/core/pair_criteria/pair_criteria.hpp b/src/core/pair_criteria/pair_criteria.hpp index fde2c8b4a2b..561e7ce4e00 100644 --- a/src/core/pair_criteria/pair_criteria.hpp +++ b/src/core/pair_criteria/pair_criteria.hpp @@ -52,7 +52,7 @@ class DistanceCriterion : public PairCriterion { public: bool decide(const Particle &p1, const Particle &p2) const override { return box_geo.get_mi_vector(p1.r.p, p2.r.p).norm() <= m_cut_off; - }; + } double get_cut_off() { return m_cut_off; } void set_cut_off(double c) { m_cut_off = c; } @@ -73,7 +73,7 @@ class EnergyCriterion : public PairCriterion { return (calc_non_bonded_pair_energy(p1, p2, ia_params, vec21, dist_betw_part)) >= m_cut_off; - }; + } double get_cut_off() { return m_cut_off; } void set_cut_off(double c) { m_cut_off = c; } @@ -87,8 +87,8 @@ class BondCriterion : public PairCriterion { bool decide(const Particle &p1, const Particle &p2) const override { return pair_bond_exists_on(p1.bonds(), p2.identity(), m_bond_type) || pair_bond_exists_on(p2.bonds(), p1.identity(), m_bond_type); - }; - int get_bond_type() { return m_bond_type; }; + } + int get_bond_type() { return m_bond_type; } void set_bond_type(int t) { m_bond_type = t; } private: diff --git a/src/core/virtual_sites/VirtualSites.hpp b/src/core/virtual_sites/VirtualSites.hpp index 56cb97044a5..ffc50b24780 100644 --- a/src/core/virtual_sites/VirtualSites.hpp +++ b/src/core/virtual_sites/VirtualSites.hpp @@ -49,15 +49,15 @@ class VirtualSites { /** 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(){}; - virtual void after_lb_propagation(double){}; + virtual void after_force_calc() {} + virtual void after_lb_propagation(double) {} /** @brief Pressure contribution. */ - virtual Utils::Matrix pressure_tensor() const { return {}; }; + 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 get_have_quaternion() const { return m_have_quaternion; }; + } + bool get_have_quaternion() const { return m_have_quaternion; } private: bool m_have_quaternion = false; diff --git a/src/script_interface/GlobalContext.hpp b/src/script_interface/GlobalContext.hpp index 44f09222046..5dd712f83e5 100644 --- a/src/script_interface/GlobalContext.hpp +++ b/src/script_interface/GlobalContext.hpp @@ -161,7 +161,7 @@ class GlobalContext : public Context { boost::string_ref name(const ObjectHandle *o) const override; - bool is_head_node() const override { return m_is_head_node; }; + bool is_head_node() const override { return m_is_head_node; } }; } // namespace ScriptInterface diff --git a/src/script_interface/LocalContext.hpp b/src/script_interface/LocalContext.hpp index 64b4fad1f96..e057c26d0f2 100644 --- a/src/script_interface/LocalContext.hpp +++ b/src/script_interface/LocalContext.hpp @@ -67,7 +67,7 @@ class LocalContext : public Context { return factory().type_name(*o); } - bool is_head_node() const override { return m_is_head_node; }; + bool is_head_node() const override { return m_is_head_node; } }; } // namespace ScriptInterface diff --git a/src/script_interface/cluster_analysis/ClusterStructure.hpp b/src/script_interface/cluster_analysis/ClusterStructure.hpp index 29097355d61..f9ad0e4b8ef 100644 --- a/src/script_interface/cluster_analysis/ClusterStructure.hpp +++ b/src/script_interface/cluster_analysis/ClusterStructure.hpp @@ -45,10 +45,10 @@ class ClusterStructure : public AutoParameters { get_value>(value); if (m_pc) { m_cluster_structure.set_pair_criterion(m_pc->pair_criterion()); - }; + } }, [this]() { return m_pc; }}}); - }; + } Variant do_call_method(std::string const &method, VariantMap const ¶meters) override { if (method == "get_cluster") { diff --git a/src/script_interface/collision_detection/CollisionDetection.hpp b/src/script_interface/collision_detection/CollisionDetection.hpp index 0af74c21dc4..043c4d3e870 100644 --- a/src/script_interface/collision_detection/CollisionDetection.hpp +++ b/src/script_interface/collision_detection/CollisionDetection.hpp @@ -65,7 +65,7 @@ class CollisionDetection : public AutoParameters { return validate_collision_parameters(); } return none; - }; + } }; } /* namespace CollisionDetection */ diff --git a/src/script_interface/constraints/Constraints.hpp b/src/script_interface/constraints/Constraints.hpp index cc3cb1b678d..4cf0ba24083 100644 --- a/src/script_interface/constraints/Constraints.hpp +++ b/src/script_interface/constraints/Constraints.hpp @@ -37,7 +37,7 @@ class Constraints : public ObjectList { } void remove_in_core(std::shared_ptr const &obj_ptr) override { ::Constraints::constraints.remove(obj_ptr->constraint()); - }; + } }; } /* namespace Constraints */ } /* namespace ScriptInterface */ diff --git a/src/script_interface/constraints/ShapeBasedConstraint.hpp b/src/script_interface/constraints/ShapeBasedConstraint.hpp index 0e28ba38745..49793a599de 100644 --- a/src/script_interface/constraints/ShapeBasedConstraint.hpp +++ b/src/script_interface/constraints/ShapeBasedConstraint.hpp @@ -53,7 +53,7 @@ class ShapeBasedConstraint : public Constraint { get_value>(value); if (m_shape) { m_constraint->set_shape(m_shape->shape()); - }; + } }, [this]() { return m_shape; }}, {"particle_velocity", m_constraint->velocity()}}); diff --git a/src/script_interface/lbboundaries/LBBoundary.hpp b/src/script_interface/lbboundaries/LBBoundary.hpp index 01a740b20c9..dfdafd25a2e 100644 --- a/src/script_interface/lbboundaries/LBBoundary.hpp +++ b/src/script_interface/lbboundaries/LBBoundary.hpp @@ -47,7 +47,7 @@ class LBBoundary : public AutoParameters { if (m_shape) { m_lbboundary->set_shape(m_shape->shape()); - }; + } }, [this]() { return m_shape; }}}); #ifdef EK_BOUNDARIES diff --git a/src/script_interface/tests/ObjectHandle_test.cpp b/src/script_interface/tests/ObjectHandle_test.cpp index f9068c54622..7ed7b1aa6b6 100644 --- a/src/script_interface/tests/ObjectHandle_test.cpp +++ b/src/script_interface/tests/ObjectHandle_test.cpp @@ -193,7 +193,7 @@ struct LogContext : public Context { return "Dummy"; } - bool is_head_node() const override { return true; }; + bool is_head_node() const override { return true; } }; } // namespace Testing diff --git a/src/script_interface/virtual_sites/VirtualSitesInertialessTracers.hpp b/src/script_interface/virtual_sites/VirtualSitesInertialessTracers.hpp index 0b473d32843..21a79d7e5ab 100644 --- a/src/script_interface/virtual_sites/VirtualSitesInertialessTracers.hpp +++ b/src/script_interface/virtual_sites/VirtualSitesInertialessTracers.hpp @@ -33,11 +33,11 @@ namespace VirtualSites { class VirtualSitesInertialessTracers : public VirtualSites { public: VirtualSitesInertialessTracers() - : m_virtual_sites(new ::VirtualSitesInertialessTracers()){}; + : m_virtual_sites(new ::VirtualSitesInertialessTracers()) {} /** Vs implementation we are wrapping */ std::shared_ptr<::VirtualSites> virtual_sites() override { return m_virtual_sites; - }; + } private: std::shared_ptr<::VirtualSitesInertialessTracers> m_virtual_sites; diff --git a/src/script_interface/virtual_sites/VirtualSitesOff.hpp b/src/script_interface/virtual_sites/VirtualSitesOff.hpp index 605d5360674..851bb3374a3 100644 --- a/src/script_interface/virtual_sites/VirtualSitesOff.hpp +++ b/src/script_interface/virtual_sites/VirtualSitesOff.hpp @@ -32,11 +32,11 @@ namespace VirtualSites { class VirtualSitesOff : public VirtualSites { public: - VirtualSitesOff() : m_virtual_sites(new ::VirtualSitesOff()){}; + VirtualSitesOff() : m_virtual_sites(new ::VirtualSitesOff()) {} /** Vs implementation we are wrapping */ std::shared_ptr<::VirtualSites> virtual_sites() override { return m_virtual_sites; - }; + } private: std::shared_ptr<::VirtualSitesOff> m_virtual_sites; diff --git a/src/script_interface/virtual_sites/VirtualSitesRelative.hpp b/src/script_interface/virtual_sites/VirtualSitesRelative.hpp index 56ec0ca372a..4ead3f72024 100644 --- a/src/script_interface/virtual_sites/VirtualSitesRelative.hpp +++ b/src/script_interface/virtual_sites/VirtualSitesRelative.hpp @@ -32,11 +32,11 @@ namespace VirtualSites { class VirtualSitesRelative : public VirtualSites { public: - VirtualSitesRelative() : m_virtual_sites(new ::VirtualSitesRelative()){}; + VirtualSitesRelative() : m_virtual_sites(new ::VirtualSitesRelative()) {} /** Vs implementation we are wrapping */ std::shared_ptr<::VirtualSites> virtual_sites() override { return m_virtual_sites; - }; + } private: std::shared_ptr<::VirtualSitesRelative> m_virtual_sites; diff --git a/src/utils/include/utils/Array.hpp b/src/utils/include/utils/Array.hpp index 7f27eb1c07c..5beb5cf92d4 100644 --- a/src/utils/include/utils/Array.hpp +++ b/src/utils/include/utils/Array.hpp @@ -50,7 +50,7 @@ struct ArrayFormatterStream { std::ostream &stream; char const *separator; ArrayFormatterStream(std::ostream &s, char const *sep) - : stream(s), separator(sep){}; + : stream(s), separator(sep) {} }; struct ArrayFormatter { @@ -120,27 +120,27 @@ template struct Array { DEVICE_QUALIFIER constexpr iterator begin() noexcept { return &m_storage.m_data[0]; - }; + } DEVICE_QUALIFIER constexpr const_iterator begin() const noexcept { return &m_storage.m_data[0]; - }; + } DEVICE_QUALIFIER constexpr const_iterator cbegin() const noexcept { return &m_storage.m_data[0]; - }; + } DEVICE_QUALIFIER constexpr iterator end() noexcept { return &m_storage.m_data[N]; - }; + } DEVICE_QUALIFIER constexpr const_iterator end() const noexcept { return &m_storage.m_data[N]; - }; + } DEVICE_QUALIFIER constexpr const_iterator cend() const noexcept { return &m_storage.m_data[N]; - }; + } DEVICE_QUALIFIER constexpr bool empty() const noexcept { return size() == 0; } diff --git a/src/utils/include/utils/matrix.hpp b/src/utils/include/utils/matrix.hpp index 1e5656c0e82..ba3081f5d2f 100644 --- a/src/utils/include/utils/matrix.hpp +++ b/src/utils/include/utils/matrix.hpp @@ -127,24 +127,24 @@ template struct Matrix { * @brief Iterator access (non const). * @return Returns an iterator to the first element of the matrix. */ - constexpr iterator begin() noexcept { return m_data.begin(); }; + constexpr iterator begin() noexcept { return m_data.begin(); } /** * @brief Iterator access (const). * @return Returns an iterator to the first element of the matrix. */ - constexpr const_iterator begin() const noexcept { return m_data.begin(); }; + constexpr const_iterator begin() const noexcept { return m_data.begin(); } /** * @brief Iterator access (non const). * @return Returns an iterator to the element following the last element of * the matrix. */ - constexpr iterator end() noexcept { return m_data.end(); }; + constexpr iterator end() noexcept { return m_data.end(); } /** * @brief Iterator access (non const). * @return Returns an iterator to the element following the last element of * the matrix. */ - constexpr const_iterator end() const noexcept { return m_data.end(); }; + constexpr const_iterator end() const noexcept { return m_data.end(); } /** * @brief Retrieve an entire matrix row. * @tparam R The row index. From 4c67cc9fa1ba5f3f3b764809d071cb3a9dea4f4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 19 Jan 2022 14:48:56 +0100 Subject: [PATCH 21/99] core: Fix -Wmaybe-uninitialized --- src/core/electrostatics_magnetostatics/fft.cpp | 3 +++ src/utils/include/utils/interpolation/bspline_3d.hpp | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/electrostatics_magnetostatics/fft.cpp b/src/core/electrostatics_magnetostatics/fft.cpp index f7e318e2032..0e0a369ae4a 100644 --- a/src/core/electrostatics_magnetostatics/fft.cpp +++ b/src/core/electrostatics_magnetostatics/fft.cpp @@ -471,6 +471,9 @@ int map_3don2d_grid(int const g3d[3], int g2d[3], int mult[3]) { /** Calculate most square 2D grid. */ void calc_2d_grid(int n, int grid[3]) { + grid[0] = n; + grid[1] = 1; + grid[2] = 1; for (auto i = static_cast(std::sqrt(n)); i >= 1; i--) { if (n % i == 0) { grid[0] = n / i; diff --git a/src/utils/include/utils/interpolation/bspline_3d.hpp b/src/utils/include/utils/interpolation/bspline_3d.hpp index 244a356bbf8..01becffd000 100644 --- a/src/utils/include/utils/interpolation/bspline_3d.hpp +++ b/src/utils/include/utils/interpolation/bspline_3d.hpp @@ -50,8 +50,8 @@ void bspline_3d(const Vector3d &pos, const Kernel &kernel, const auto block = detail::ll_and_dist(pos, grid_spacing, offset); /* Precalc weights that are used multiple times. */ - std::array w_y; - std::array w_z; + std::array w_y{}; + std::array w_z{}; for (int i = 0; i < order; i++) { w_y[i] = bspline(i, block.distance[1]); w_z[i] = bspline(i, block.distance[2]); From d7d5b8d29b56821b7cf0516b3c1a7a5376898fea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 19 Jan 2022 15:29:17 +0100 Subject: [PATCH 22/99] core: Fix -Wmissing-braces --- CMakeLists.txt | 1 - src/core/collision.cpp | 2 +- .../dp3m_influence_function.hpp | 12 ++---- .../p3m-common.hpp | 2 +- .../p3m-data_struct.hpp | 3 +- .../p3m-dipolar.cpp | 12 +++--- .../electrostatics_magnetostatics/p3m.cpp | 2 +- .../p3m_influence_function.hpp | 3 +- src/core/grid_based_algorithms/lb.cpp | 37 ++++++++----------- src/core/grid_based_algorithms/lbgpu.cpp | 10 ++--- src/core/io/writer/h5md_core.cpp | 3 +- src/core/observables/LBProfileObservable.hpp | 6 +-- src/core/random.hpp | 6 +-- src/core/unit_tests/BondList_test.cpp | 12 +++--- src/core/unit_tests/LocalBox_test.cpp | 2 +- src/core/unit_tests/Particle_test.cpp | 2 +- src/core/unit_tests/p3m_test.cpp | 14 ++++--- src/core/unit_tests/random_test.cpp | 22 +++++------ src/core/unit_tests/thermostats_test.cpp | 18 ++++----- src/core/virtual_sites.cpp | 28 +++++++------- src/utils/include/utils/math/quaternion.hpp | 6 +-- src/utils/tests/Array_test.cpp | 14 +++---- src/utils/tests/Bag_test.cpp | 16 ++++---- src/utils/tests/bspline_test.cpp | 6 +-- src/utils/tests/flatten_test.cpp | 10 ++--- src/utils/tests/quaternion_test.cpp | 36 +++++++++--------- src/utils/tests/rotation_matrix_test.cpp | 6 +-- src/utils/tests/tuple_test.cpp | 8 ++-- 28 files changed, 142 insertions(+), 157 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d92d71dd700..efac6ca65d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -324,7 +324,6 @@ target_compile_options( -Wno-sign-compare -Wno-unused-function -Wno-unused-parameter - -Wno-missing-braces $<$:-Wno-clobbered> $<$:-wd592>) diff --git a/src/core/collision.cpp b/src/core/collision.cpp index 9243b401ce2..8692a5a1f18 100644 --- a/src/core/collision.cpp +++ b/src/core/collision.cpp @@ -360,7 +360,7 @@ void coldet_do_three_particle_bond(Particle &p, Particle const &p1, collision_params.bond_three_particles); // Create the bond - const std::array bondT = {p1.p.identity, p2.p.identity}; + const std::array bondT = {{p1.p.identity, p2.p.identity}}; p.bonds().insert({bond_id, bondT}); } diff --git a/src/core/electrostatics_magnetostatics/dp3m_influence_function.hpp b/src/core/electrostatics_magnetostatics/dp3m_influence_function.hpp index 5d2000d8343..9407f27924c 100644 --- a/src/core/electrostatics_magnetostatics/dp3m_influence_function.hpp +++ b/src/core/electrostatics_magnetostatics/dp3m_influence_function.hpp @@ -123,10 +123,8 @@ std::vector grid_influence_function(P3MParameters const ¶ms, double fak1 = Utils::int_pow<3>(static_cast(params.mesh[0])) * 2.0 / Utils::sqr(box_l[0]); - auto const shifts = - detail::calc_meshift({params.mesh[0], params.mesh[1], params.mesh[2]}); - auto const d_ops = detail::calc_meshift( - {params.mesh[0], params.mesh[1], params.mesh[2]}, true); + auto const shifts = detail::calc_meshift(params.mesh, false); + auto const d_ops = detail::calc_meshift(params.mesh, true); Utils::Vector3i n{}; for (n[0] = n_start[0]; n[0] < n_end[0]; n[0]++) { @@ -192,10 +190,8 @@ double grid_influence_function_self_energy(P3MParameters const ¶ms, std::vector const &g) { auto const size = n_end - n_start; - auto const shifts = - detail::calc_meshift({params.mesh[0], params.mesh[1], params.mesh[2]}); - auto const d_ops = detail::calc_meshift( - {params.mesh[0], params.mesh[1], params.mesh[2]}, true); + auto const shifts = detail::calc_meshift(params.mesh, false); + auto const d_ops = detail::calc_meshift(params.mesh, true); double energy = 0.0; Utils::Vector3i n{}; diff --git a/src/core/electrostatics_magnetostatics/p3m-common.hpp b/src/core/electrostatics_magnetostatics/p3m-common.hpp index 954e646e870..d5757521e89 100644 --- a/src/core/electrostatics_magnetostatics/p3m-common.hpp +++ b/src/core/electrostatics_magnetostatics/p3m-common.hpp @@ -205,7 +205,7 @@ namespace detail { * \ldots, -1\right) @f$. */ std::array, 3> inline calc_meshift( - std::array const &mesh_size, bool zero_out_midpoint = false) { + int const mesh_size[3], bool zero_out_midpoint = false) { std::array, 3> ret{}; for (std::size_t i = 0; i < 3; i++) { diff --git a/src/core/electrostatics_magnetostatics/p3m-data_struct.hpp b/src/core/electrostatics_magnetostatics/p3m-data_struct.hpp index 8ed57989090..1840660a2de 100644 --- a/src/core/electrostatics_magnetostatics/p3m-data_struct.hpp +++ b/src/core/electrostatics_magnetostatics/p3m-data_struct.hpp @@ -49,8 +49,7 @@ struct p3m_data_struct_base { * i.e. the prefactor @f$ 2i\pi/L @f$ is missing! */ void calc_differential_operator() { - d_op = detail::calc_meshift( - {params.mesh[0], params.mesh[1], params.mesh[2]}, true); + d_op = detail::calc_meshift(params.mesh, true); } }; diff --git a/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp b/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp index 81877f6510f..e34dd0dcf32 100644 --- a/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp +++ b/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp @@ -390,9 +390,9 @@ double dp3m_calc_kspace_forces(bool force_flag, bool energy_flag, if (dp3m.sum_mu2 > 0) { /* 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(), - dp3m.rs_mesh_dip[1].data(), - dp3m.rs_mesh_dip[2].data()}; + std::array meshes = {{dp3m.rs_mesh_dip[0].data(), + dp3m.rs_mesh_dip[1].data(), + dp3m.rs_mesh_dip[2].data()}}; dp3m.sm.gather_grid(Utils::make_span(meshes), comm_cart, dp3m.local_mesh.dim); @@ -613,9 +613,9 @@ double dp3m_calc_kspace_forces(bool force_flag, bool energy_flag, fft_perform_back(dp3m.rs_mesh_dip[2].data(), false, dp3m.fft, comm_cart); /* redistribute force component mesh */ - std::array meshes = {dp3m.rs_mesh_dip[0].data(), - dp3m.rs_mesh_dip[1].data(), - dp3m.rs_mesh_dip[2].data()}; + std::array meshes = {{dp3m.rs_mesh_dip[0].data(), + dp3m.rs_mesh_dip[1].data(), + dp3m.rs_mesh_dip[2].data()}}; dp3m.sm.spread_grid(Utils::make_span(meshes), comm_cart, dp3m.local_mesh.dim); diff --git a/src/core/electrostatics_magnetostatics/p3m.cpp b/src/core/electrostatics_magnetostatics/p3m.cpp index 933da1cf80f..16ec3f29d5a 100644 --- a/src/core/electrostatics_magnetostatics/p3m.cpp +++ b/src/core/electrostatics_magnetostatics/p3m.cpp @@ -532,7 +532,7 @@ double p3m_calc_kspace_forces(bool force_flag, bool energy_flag, { std::array E_fields = { - p3m.E_mesh[0].data(), p3m.E_mesh[1].data(), p3m.E_mesh[2].data()}; + {p3m.E_mesh[0].data(), p3m.E_mesh[1].data(), p3m.E_mesh[2].data()}}; /* redistribute force component mesh */ p3m.sm.spread_grid(Utils::make_span(E_fields), comm_cart, p3m.local_mesh.dim); diff --git a/src/core/electrostatics_magnetostatics/p3m_influence_function.hpp b/src/core/electrostatics_magnetostatics/p3m_influence_function.hpp index bfc4392b704..da1eb763d6a 100644 --- a/src/core/electrostatics_magnetostatics/p3m_influence_function.hpp +++ b/src/core/electrostatics_magnetostatics/p3m_influence_function.hpp @@ -132,8 +132,7 @@ std::vector grid_influence_function(const P3MParameters ¶ms, const Utils::Vector3d &box_l) { using namespace detail::FFT_indexing; - auto const shifts = - detail::calc_meshift({params.mesh[0], params.mesh[1], params.mesh[2]}); + auto const shifts = detail::calc_meshift(params.mesh); auto const size = n_end - n_start; diff --git a/src/core/grid_based_algorithms/lb.cpp b/src/core/grid_based_algorithms/lb.cpp index 0b6016a1383..75c783a92a7 100644 --- a/src/core/grid_based_algorithms/lb.cpp +++ b/src/core/grid_based_algorithms/lb.cpp @@ -714,16 +714,9 @@ std::array lb_calc_n_from_m(const std::array &modes) { Utils::Vector19d lb_get_population_from_density_momentum_density_stress( double density, Utils::Vector3d const &momentum_density, Utils::Vector6d const &stress) { - std::array modes{density, - momentum_density[0], - momentum_density[1], - momentum_density[2], - stress[0], - stress[1], - stress[2], - stress[3], - stress[4], - stress[5]}; + std::array modes{ + {density, momentum_density[0], momentum_density[1], momentum_density[2], + stress[0], stress[1], stress[2], stress[3], stress[4], stress[5]}}; return Utils::Vector19d{lb_calc_n_from_m(modes)}; } @@ -845,18 +838,18 @@ std::array lb_apply_forces(const std::array &modes, Utils::Vector3d{modes[1], modes[2], modes[3]} + T{0.5} * f / density; auto const C = std::array{ - (1. + lb_parameters.gamma_shear) * u[0] * f[0] + - 1. / 3. * (lb_parameters.gamma_bulk - lb_parameters.gamma_shear) * - (u * f), - 1. / 2. * (1. + lb_parameters.gamma_shear) * (u[0] * f[1] + u[1] * f[0]), - (1. + lb_parameters.gamma_shear) * u[1] * f[1] + - 1. / 3. * (lb_parameters.gamma_bulk - lb_parameters.gamma_shear) * - (u * f), - 1. / 2. * (1. + lb_parameters.gamma_shear) * (u[0] * f[2] + u[2] * f[0]), - 1. / 2. * (1. + lb_parameters.gamma_shear) * (u[1] * f[2] + u[2] * f[1]), - (1. + lb_parameters.gamma_shear) * u[2] * f[2] + - 1. / 3. * (lb_parameters.gamma_bulk - lb_parameters.gamma_shear) * - (u * f)}; + {(1. + lb_parameters.gamma_shear) * u[0] * f[0] + + 1. / 3. * (lb_parameters.gamma_bulk - lb_parameters.gamma_shear) * + (u * f), + 1. / 2. * (1. + lb_parameters.gamma_shear) * (u[0] * f[1] + u[1] * f[0]), + (1. + lb_parameters.gamma_shear) * u[1] * f[1] + + 1. / 3. * (lb_parameters.gamma_bulk - lb_parameters.gamma_shear) * + (u * f), + 1. / 2. * (1. + lb_parameters.gamma_shear) * (u[0] * f[2] + u[2] * f[0]), + 1. / 2. * (1. + lb_parameters.gamma_shear) * (u[1] * f[2] + u[2] * f[1]), + (1. + lb_parameters.gamma_shear) * u[2] * f[2] + + 1. / 3. * (lb_parameters.gamma_bulk - lb_parameters.gamma_shear) * + (u * f)}}; return {{modes[0], /* update momentum modes */ diff --git a/src/core/grid_based_algorithms/lbgpu.cpp b/src/core/grid_based_algorithms/lbgpu.cpp index b957539962d..326dc7ad9e6 100644 --- a/src/core/grid_based_algorithms/lbgpu.cpp +++ b/src/core/grid_based_algorithms/lbgpu.cpp @@ -64,12 +64,8 @@ LB_parameters_gpu lbpar_gpu = { -1.0, // tau -1.0, - // dim_x; - 0, - // dim_y; - 0, - // dim_z; - 0, + // dim + {{{0, 0, 0}}}, // number_of_nodes 0, #ifdef LB_BOUNDARIES_GPU @@ -79,7 +75,7 @@ LB_parameters_gpu lbpar_gpu = { // external_force 0, // ext_force - {0.0, 0.0, 0.0}, + {{{0.0, 0.0, 0.0}}}, // Thermal energy 0.0}; diff --git a/src/core/io/writer/h5md_core.cpp b/src/core/io/writer/h5md_core.cpp index 535077c8a1a..d2c0328062d 100644 --- a/src/core/io/writer/h5md_core.cpp +++ b/src/core/io/writer/h5md_core.cpp @@ -198,7 +198,8 @@ static void write_box(const BoxGeometry &geometry, h5xx::dataset &dataset) { void write_attributes(const std::string &espresso_version, h5xx::file &h5md_file) { auto h5md_group = h5xx::group(h5md_file, "h5md"); - h5xx::write_attribute(h5md_group, "version", boost::array{1, 1}); + h5xx::write_attribute(h5md_group, "version", + boost::array{{1, 1}}); auto h5md_creator_group = h5xx::group(h5md_group, "creator"); h5xx::write_attribute(h5md_creator_group, "name", "ESPResSo"); h5xx::write_attribute(h5md_creator_group, "version", espresso_version); diff --git a/src/core/observables/LBProfileObservable.hpp b/src/core/observables/LBProfileObservable.hpp index 14b9007f022..d887588c0cb 100644 --- a/src/core/observables/LBProfileObservable.hpp +++ b/src/core/observables/LBProfileObservable.hpp @@ -43,9 +43,9 @@ class LBProfileObservable : public ProfileObservable { double max_z, bool allow_empty_bins = false) : ProfileObservable(n_x_bins, n_y_bins, n_z_bins, min_x, max_x, min_y, max_y, min_z, max_z), - sampling_delta{sampling_delta_x, sampling_delta_y, sampling_delta_z}, - sampling_offset{sampling_offset_x, sampling_offset_y, - sampling_offset_z}, + sampling_delta{{sampling_delta_x, sampling_delta_y, sampling_delta_z}}, + sampling_offset{ + {sampling_offset_x, sampling_offset_y, sampling_offset_z}}, allow_empty_bins(allow_empty_bins) { if (sampling_delta[0] <= 0.) throw std::domain_error("sampling_delta_x has to be > 0"); diff --git a/src/core/random.hpp b/src/core/random.hpp index 40522cceedf..b2d1212c153 100644 --- a/src/core/random.hpp +++ b/src/core/random.hpp @@ -78,12 +78,12 @@ auto philox_4_uint64s(uint64_t counter, uint32_t seed, int key1, int key2 = 0) { using ctr_type = rng_type::ctr_type; using key_type = rng_type::key_type; - const ctr_type c{counter}; + const ctr_type c{{counter, 0u, 0u, 0u}}; auto const id1 = static_cast(key1); auto const id2 = static_cast(key2); - const key_type k{Utils::u32_to_u64(id1, id2), - Utils::u32_to_u64(static_cast(salt), seed)}; + const key_type k{{Utils::u32_to_u64(id1, id2), + Utils::u32_to_u64(static_cast(salt), seed)}}; return rng_type{}(c, k); } diff --git a/src/core/unit_tests/BondList_test.cpp b/src/core/unit_tests/BondList_test.cpp index 186e30aede3..765f3ca638a 100644 --- a/src/core/unit_tests/BondList_test.cpp +++ b/src/core/unit_tests/BondList_test.cpp @@ -33,7 +33,7 @@ BOOST_AUTO_TEST_CASE(BondView_) { /* Dummy values */ auto const id = 5; - auto const partners = std::array{12, 13, 14}; + auto const partners = std::array{{12, 13, 14}}; /* BondView can be constructed from an id and a partner range */ auto const view = BondView{id, partners}; @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(BondView_) { /* Comparison ops */ { auto const partners_same = partners; - auto const partners_different = std::array{15, 16}; + auto const partners_different = std::array{{15, 16}}; BOOST_CHECK((BondView{id, partners} == BondView{id, partners_same})); BOOST_CHECK(not(BondView{id, partners} != BondView{id, partners_same})); @@ -90,7 +90,7 @@ BOOST_AUTO_TEST_CASE(Iterator_incement_) { BOOST_AUTO_TEST_CASE(insert_) { /* Dummy values */ - auto const partners = std::array{1, 2, 3}; + auto const partners = std::array{{1, 2, 3}}; auto const bond1 = BondView{1, partners}; auto const bond2 = BondView{2, partners}; @@ -114,7 +114,7 @@ BOOST_AUTO_TEST_CASE(insert_) { } BOOST_AUTO_TEST_CASE(erase_) { - auto const partners = std::array{1, 2, 3}; + auto const partners = std::array{{1, 2, 3}}; auto const bond1 = BondView{1, partners}; auto const bond2 = BondView{2, partners}; auto const bond3 = BondView{3, partners}; @@ -166,7 +166,7 @@ BOOST_AUTO_TEST_CASE(erase_) { } BOOST_AUTO_TEST_CASE(clear_) { - auto const partners = std::array{1, 2, 3}; + auto const partners = std::array{{1, 2, 3}}; auto const bond1 = BondView{1, partners}; auto const bond2 = BondView{2, partners}; @@ -181,7 +181,7 @@ BOOST_AUTO_TEST_CASE(clear_) { } BOOST_AUTO_TEST_CASE(serialization_) { - auto const partners = std::array{4, 5, 6}; + auto const partners = std::array{{4, 5, 6}}; auto const bond1 = BondView{1, partners}; auto const bond2 = BondView{2, partners}; diff --git a/src/core/unit_tests/LocalBox_test.cpp b/src/core/unit_tests/LocalBox_test.cpp index 1d951894f98..36fc95ed7b6 100644 --- a/src/core/unit_tests/LocalBox_test.cpp +++ b/src/core/unit_tests/LocalBox_test.cpp @@ -52,7 +52,7 @@ BOOST_AUTO_TEST_CASE(constructors) { { Utils::Vector const lower_corner = {1., 2., 3.}; Utils::Vector const local_box_length = {4., 5., 6.}; - Utils::Array const boundaries = {-1, 0, 1, 1, 0, -1}; + Utils::Array const boundaries = {{{-1, 0, 1, 1, 0, -1}}}; auto const box = LocalBox(lower_corner, local_box_length, boundaries); diff --git a/src/core/unit_tests/Particle_test.cpp b/src/core/unit_tests/Particle_test.cpp index 63cc49aed37..aeb2594a7bc 100644 --- a/src/core/unit_tests/Particle_test.cpp +++ b/src/core/unit_tests/Particle_test.cpp @@ -63,7 +63,7 @@ BOOST_AUTO_TEST_CASE(serialization) { auto p = Particle(); auto const bond_id = 5; - auto const bond_partners = std::array{12, 13, 14}; + auto const bond_partners = std::array{{12, 13, 14}}; std::vector el = {5, 6, 7, 8}; diff --git a/src/core/unit_tests/p3m_test.cpp b/src/core/unit_tests/p3m_test.cpp index 37f2afaa9fc..d62e86deaae 100644 --- a/src/core/unit_tests/p3m_test.cpp +++ b/src/core/unit_tests/p3m_test.cpp @@ -29,10 +29,11 @@ BOOST_AUTO_TEST_CASE(calc_meshift_false) { std::array, 3> const ref = { - std::vector{0}, std::vector{0, 1, -2, -1}, - std::vector{0, 1, 2, 3, -3, -2, -1}}; + {std::vector{0}, std::vector{0, 1, -2, -1}, + std::vector{0, 1, 2, 3, -3, -2, -1}}}; - auto const val = detail::calc_meshift({1, 4, 7}, false); + int const mesh[3] = {1, 4, 7}; + auto const val = detail::calc_meshift(mesh, false); for (std::size_t i = 0; i < 3; ++i) { for (std::size_t j = 0; j < ref[i].size(); ++j) { @@ -43,10 +44,11 @@ BOOST_AUTO_TEST_CASE(calc_meshift_false) { BOOST_AUTO_TEST_CASE(calc_meshift_true) { std::array, 3> const ref = { - std::vector{0}, std::vector{0, 1, 0, -1}, - std::vector{0, 1, 2, 0, -3, -2, -1}}; + {std::vector{0}, std::vector{0, 1, 0, -1}, + std::vector{0, 1, 2, 0, -3, -2, -1}}}; - auto const val = detail::calc_meshift({1, 4, 7}, true); + int const mesh[3] = {1, 4, 7}; + auto const val = detail::calc_meshift(mesh, true); for (std::size_t i = 0; i < 3; ++i) { for (std::size_t j = 0; j < ref[i].size(); ++j) { diff --git a/src/core/unit_tests/random_test.cpp b/src/core/unit_tests/random_test.cpp index 8c76e5e2f5a..a0dac3e720a 100644 --- a/src/core/unit_tests/random_test.cpp +++ b/src/core/unit_tests/random_test.cpp @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(test_noise_statistics) { std::tie(means, variances, covariance, correlation) = noise_statistics( [&value]() -> std::array { value *= -1; - return {Utils::Vector2d{value, -value}}; + return {{Utils::Vector2d{value, -value}}}; }, sample_size); // check pooled mean and variance @@ -71,7 +71,7 @@ BOOST_AUTO_TEST_CASE(test_noise_uniform_1d) { std::vector> correlation; std::tie(means, variances, covariance, correlation) = noise_statistics( [counter = 0]() mutable -> std::array { - return {Random::noise_uniform(counter++, 0, 1)}; + return {{Random::noise_uniform(counter++, 0, 1)}}; }, sample_size); // check pooled mean and variance @@ -88,7 +88,7 @@ BOOST_AUTO_TEST_CASE(test_noise_uniform_3d) { std::vector> correlation; std::tie(means, variances, covariance, correlation) = noise_statistics( [counter = 0]() mutable -> std::array { - return {Random::noise_uniform(counter++, 1, 0)}; + return {{Random::noise_uniform(counter++, 1, 0)}}; }, sample_size); // check pooled mean and variance @@ -113,8 +113,8 @@ BOOST_AUTO_TEST_CASE(test_noise_gaussian_4d) { std::vector> correlation; std::tie(means, variances, covariance, correlation) = noise_statistics( [counter = 0]() mutable -> std::array { - return { - Random::noise_gaussian(counter++, 0, 0)}; + return {{Random::noise_gaussian(counter++, 0, + 0)}}; }, sample_size); // check pooled mean and variance @@ -147,9 +147,9 @@ BOOST_AUTO_TEST_CASE(test_uncorrelated_consecutive_ids) { [counter = 0]() mutable -> std::array { counter++; auto prng = Random::noise_uniform; - return {prng(counter, seed, pid, 0), - prng(counter, seed, pid + pid_offset, 0), - prng(counter + pid_offset, seed, pid, 0)}; + return {{prng(counter, seed, pid, 0), + prng(counter, seed, pid + pid_offset, 0), + prng(counter + pid_offset, seed, pid, 0)}}; }, sample_size)); // check correlation @@ -171,9 +171,9 @@ BOOST_AUTO_TEST_CASE(test_uncorrelated_consecutive_seeds) { [counter = 0]() mutable -> std::array { counter++; auto prng = Random::noise_uniform; - return {prng(counter, seed, pid, 0), - prng(counter, seed + seed_offset, pid, 0), - prng(counter + seed_offset, seed, pid, 0)}; + return {{prng(counter, seed, pid, 0), + prng(counter, seed + seed_offset, pid, 0), + prng(counter + seed_offset, seed, pid, 0)}}; }, sample_size)); // check correlation diff --git a/src/core/unit_tests/thermostats_test.cpp b/src/core/unit_tests/thermostats_test.cpp index b95b2fa4f75..4fac2916ad7 100644 --- a/src/core/unit_tests/thermostats_test.cpp +++ b/src/core/unit_tests/thermostats_test.cpp @@ -227,9 +227,9 @@ BOOST_AUTO_TEST_CASE(test_noise_statistics) { auto const correlation = std::get<3>(noise_statistics( [&p1, &p2, &thermostat]() -> std::array { thermostat.rng_increment(); - return {friction_thermo_langevin(thermostat, p1, time_step, kT), - -friction_thermo_langevin(thermostat, p1, time_step, kT), - friction_thermo_langevin(thermostat, p2, time_step, kT)}; + return {{friction_thermo_langevin(thermostat, p1, time_step, kT), + -friction_thermo_langevin(thermostat, p1, time_step, kT), + friction_thermo_langevin(thermostat, p2, time_step, kT)}}; }, sample_size)); for (std::size_t i = 0; i < correlation.size(); ++i) { @@ -263,14 +263,14 @@ BOOST_AUTO_TEST_CASE(test_brownian_randomness) { auto const correlation = std::get<3>(noise_statistics( [&p, &thermostat]() -> std::array { thermostat.rng_increment(); - return { + return {{ bd_random_walk(thermostat, p, time_step, kT), bd_random_walk_vel(thermostat, p), #ifdef ROTATION bd_random_walk_rot(thermostat, p, time_step, kT), bd_random_walk_vel_rot(thermostat, p), #endif - }; + }}; }, sample_size)); for (std::size_t i = 0; i < correlation.size(); ++i) { @@ -295,12 +295,12 @@ BOOST_AUTO_TEST_CASE(test_langevin_randomness) { auto const correlation = std::get<3>(noise_statistics( [&p, &thermostat]() -> std::array { thermostat.rng_increment(); - return { + return {{ friction_thermo_langevin(thermostat, p, time_step, kT), #ifdef ROTATION friction_thermo_langevin_rotation(thermostat, p, time_step, kT), #endif - }; + }}; }, sample_size)); for (std::size_t i = 0; i < correlation.size(); ++i) { @@ -327,11 +327,11 @@ BOOST_AUTO_TEST_CASE(test_npt_iso_randomness) { auto const correlation = std::get<3>(noise_statistics( [&p, &thermostat]() -> std::array { thermostat.rng_increment(); - return { + return {{ friction_therm0_nptiso<1>(thermostat, p.m.v, 0), friction_therm0_nptiso<2>(thermostat, p.m.v, 0), friction_thermV_nptiso(thermostat, 1.5), - }; + }}; }, sample_size)); for (std::size_t i = 0; i < correlation.size(); ++i) { diff --git a/src/core/virtual_sites.cpp b/src/core/virtual_sites.cpp index 63f56725d1d..73061e84abe 100644 --- a/src/core/virtual_sites.cpp +++ b/src/core/virtual_sites.cpp @@ -99,20 +99,20 @@ calculate_vs_relate_to_params(Particle const &p_current, Utils::convert_director_to_quaternion(d); // Define quaternion as described above - quat = - Utils::Quaternion{Utils::dot(p_relate_to.r.quat, quat_director), - -quat_director[0] * p_relate_to.r.quat[1] + - quat_director[1] * p_relate_to.r.quat[0] + - quat_director[2] * p_relate_to.r.quat[3] - - quat_director[3] * p_relate_to.r.quat[2], - p_relate_to.r.quat[1] * quat_director[3] + - p_relate_to.r.quat[0] * quat_director[2] - - p_relate_to.r.quat[3] * quat_director[1] - - p_relate_to.r.quat[2] * quat_director[0], - quat_director[3] * p_relate_to.r.quat[0] - - p_relate_to.r.quat[3] * quat_director[0] + - p_relate_to.r.quat[2] * quat_director[1] - - p_relate_to.r.quat[1] * quat_director[2]}; + quat = Utils::Quaternion{ + {{{Utils::dot(p_relate_to.r.quat, quat_director), + -quat_director[0] * p_relate_to.r.quat[1] + + quat_director[1] * p_relate_to.r.quat[0] + + quat_director[2] * p_relate_to.r.quat[3] - + quat_director[3] * p_relate_to.r.quat[2], + p_relate_to.r.quat[1] * quat_director[3] + + p_relate_to.r.quat[0] * quat_director[2] - + p_relate_to.r.quat[3] * quat_director[1] - + p_relate_to.r.quat[2] * quat_director[0], + quat_director[3] * p_relate_to.r.quat[0] - + p_relate_to.r.quat[3] * quat_director[0] + + p_relate_to.r.quat[2] * quat_director[1] - + p_relate_to.r.quat[1] * quat_director[2]}}}}; quat /= p_relate_to.r.quat.norm2(); // Verify result diff --git a/src/utils/include/utils/math/quaternion.hpp b/src/utils/include/utils/math/quaternion.hpp index 53c830d6453..bef1962dd97 100644 --- a/src/utils/include/utils/math/quaternion.hpp +++ b/src/utils/include/utils/math/quaternion.hpp @@ -57,7 +57,7 @@ Quaternion convert_director_to_quaternion(Vector const &d) { // null vectors cannot be converted to quaternions if (dm < std::numeric_limits::epsilon()) { - return {1, 0, 0, 0}; + return {{{{1, 0, 0, 0}}}}; } // Calculate angles @@ -83,8 +83,8 @@ Quaternion convert_director_to_quaternion(Vector const &d) { auto const sin_theta2 = std::sin(theta2); auto const cos_phi2 = std::cos(phi2); auto const sin_phi2 = std::sin(phi2); - return {cos_theta2 * cos_phi2, -sin_theta2 * cos_phi2, -sin_theta2 * sin_phi2, - cos_theta2 * sin_phi2}; + return {{{{cos_theta2 * cos_phi2, -sin_theta2 * cos_phi2, + -sin_theta2 * sin_phi2, cos_theta2 * sin_phi2}}}}; } } // namespace Utils diff --git a/src/utils/tests/Array_test.cpp b/src/utils/tests/Array_test.cpp index ab517392b50..680fdfe7789 100644 --- a/src/utils/tests/Array_test.cpp +++ b/src/utils/tests/Array_test.cpp @@ -53,7 +53,7 @@ BOOST_AUTO_TEST_CASE(array_ctor) { } BOOST_AUTO_TEST_CASE(iterators) { - auto a = Array{{1, 2, 3, 4}}; + auto a = Array{{{1, 2, 3, 4}}}; BOOST_CHECK_EQUAL(*(a.begin()), 1); BOOST_CHECK_EQUAL(*(a.cbegin()), 1); @@ -62,7 +62,7 @@ BOOST_AUTO_TEST_CASE(iterators) { } BOOST_AUTO_TEST_CASE(element_access) { - auto a = Array{{5, 6, 7, 8, 9}}; + auto a = Array{{{5, 6, 7, 8, 9}}}; auto const &b = a; int c = 5; @@ -124,12 +124,12 @@ BOOST_AUTO_TEST_CASE(tuple_protocol) { static_assert(std::is_same, int>::value, ""); static_assert(A{}.size() == Utils::tuple_size::value, ""); - BOOST_CHECK_EQUAL(Utils::get<1>(A{1, 2, 3, 4}), 2); + BOOST_CHECK_EQUAL(Utils::get<1>(A{{{1, 2, 3, 4}}}), 2); } BOOST_AUTO_TEST_CASE(streaming_operator) { { - auto const a = Utils::Array{1}; + auto const a = Utils::Array{{{1}}}; std::stringstream ss; ss << a; @@ -138,7 +138,7 @@ BOOST_AUTO_TEST_CASE(streaming_operator) { } { - auto const a = Utils::Array{1, 2, 3}; + auto const a = Utils::Array{{{1, 2, 3}}}; std::stringstream ss; ss << a; @@ -149,7 +149,7 @@ BOOST_AUTO_TEST_CASE(streaming_operator) { BOOST_AUTO_TEST_CASE(formatter_and_streaming_operator) { { - auto const a = Utils::Array{1}; + auto const a = Utils::Array{{{1}}}; std::stringstream ss; ss << a.formatter("xyz") << a; @@ -158,7 +158,7 @@ BOOST_AUTO_TEST_CASE(formatter_and_streaming_operator) { } { - auto const a = Utils::Array{1, 2, 3}; + auto const a = Utils::Array{{{1, 2, 3}}}; std::stringstream ss; ss << a.formatter(" + ") << a; diff --git a/src/utils/tests/Bag_test.cpp b/src/utils/tests/Bag_test.cpp index 387aa6e07a2..66fdfb8fa44 100644 --- a/src/utils/tests/Bag_test.cpp +++ b/src/utils/tests/Bag_test.cpp @@ -42,7 +42,7 @@ BOOST_AUTO_TEST_CASE(constructor_) { BOOST_AUTO_TEST_CASE(insert_) { /* Copy insert */ { - auto const elements = std::array{1, 2, 3}; + auto const elements = std::array{{1, 2, 3}}; auto bag = Utils::Bag(); /* Elements can be inserted into the bag */ @@ -74,7 +74,7 @@ BOOST_AUTO_TEST_CASE(insert_) { } BOOST_AUTO_TEST_CASE(erase_) { - auto const elements = std::array{1, 2, 3}; + auto const elements = std::array{{1, 2, 3}}; { /* Given a bag with elements */ @@ -126,7 +126,7 @@ BOOST_AUTO_TEST_CASE(erase_) { } BOOST_AUTO_TEST_CASE(size_) { - auto const elements = std::array{1, 2, 3, 5, 6}; + auto const elements = std::array{{1, 2, 3, 5, 6}}; /* Given a bag with elements */ auto bag = Utils::Bag(); @@ -139,7 +139,7 @@ BOOST_AUTO_TEST_CASE(size_) { } BOOST_AUTO_TEST_CASE(iterator_range_) { - auto const elements = std::array{1, 2, 3, 5, 6}; + auto const elements = std::array{{1, 2, 3, 5, 6}}; /* Given a bag with elements */ auto bag = Utils::Bag(); @@ -192,7 +192,7 @@ BOOST_AUTO_TEST_CASE(reserve_) { } BOOST_AUTO_TEST_CASE(resize_) { - auto const elements = std::array{1, 2, 3, 5, 6}; + auto const elements = std::array{{1, 2, 3, 5, 6}}; /* Given a bag with elements */ auto bag = Utils::Bag(); @@ -212,8 +212,8 @@ BOOST_AUTO_TEST_CASE(resize_) { } BOOST_AUTO_TEST_CASE(swap_) { - auto const elements1 = std::array{1, 2, 3}; - auto const elements2 = std::array{1, 2, 3}; + auto const elements1 = std::array{{1, 2, 3}}; + auto const elements2 = std::array{{1, 2, 3}}; /* Given two bags with elements */ auto bag1 = Utils::Bag(); @@ -242,7 +242,7 @@ BOOST_AUTO_TEST_CASE(swap_) { } BOOST_AUTO_TEST_CASE(serialize_) { - auto const elements = std::array{1, 2, 3, 5, 6}; + auto const elements = std::array{{1, 2, 3, 5, 6}}; /* Given a bag with elements */ auto bag = Utils::Bag(); diff --git a/src/utils/tests/bspline_test.cpp b/src/utils/tests/bspline_test.cpp index 391b8192629..0aa95c41d81 100644 --- a/src/utils/tests/bspline_test.cpp +++ b/src/utils/tests/bspline_test.cpp @@ -36,7 +36,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(bspline_normalization, T, test_bspline_orders) { // check that B-splines are normalized constexpr auto order = T::value; constexpr auto tol = 1e-10; - constexpr std::array x_values{-0.49999, 0.25, 0., 0.25, 0.49999}; + constexpr std::array x_values{{-0.49999, 0.25, 0., 0.25, 0.49999}}; for (auto const x : x_values) { double sum = 0; @@ -52,7 +52,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(bspline_symmetry, T, test_bspline_orders) { constexpr auto order = T::value; constexpr auto order_mid = (order % 2 == 0) ? order / 2 : (order + 1) / 2; constexpr auto tol = 1e-10; - constexpr std::array x_values{-0.49999, 0.25, 0.1}; + constexpr std::array x_values{{-0.49999, 0.25, 0.1}}; for (int i = 0; i < order_mid; ++i) { for (auto const x : x_values) { @@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(bspline_derivatives, T, test_bspline_orders) { // check that B-splines derivatives are correct constexpr auto order = T::value; constexpr auto tol = 1e-8; - constexpr std::array x_values{-0.49999, 0.25, 0., 0.25, 0.49999}; + constexpr std::array x_values{{-0.49999, 0.25, 0., 0.25, 0.49999}}; // approximate a derivative using the two-point central difference formula auto bspline_d_approx = [](int i, double x, int order) { diff --git a/src/utils/tests/flatten_test.cpp b/src/utils/tests/flatten_test.cpp index ecdc5a04d64..f0eb1894cb3 100644 --- a/src/utils/tests/flatten_test.cpp +++ b/src/utils/tests/flatten_test.cpp @@ -34,7 +34,7 @@ BOOST_AUTO_TEST_CASE(flatten_) { /* not nested */ { - const std::array in = {1, 2, 3, 4}; + const std::array in = {{1, 2, 3, 4}}; std::array out{}; flatten(in, out.begin()); BOOST_CHECK_EQUAL_COLLECTIONS(in.begin(), in.end(), out.begin(), out.end()); @@ -42,22 +42,22 @@ BOOST_AUTO_TEST_CASE(flatten_) { /* nested */ { - const std::array, 2> in{{{1, 2}, {3, 4}}}; + const std::array, 2> in{{{{1, 2}}, {{3, 4}}}}; std::array out{}; flatten(in, out.begin()); - const std::array expected = {1, 2, 3, 4}; + const std::array expected = {{1, 2, 3, 4}}; BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), out.begin(), out.end()); } { - const std::vector in = {1, 2, 3, 4}; + const std::vector in = {{1, 2, 3, 4}}; std::vector out; flatten(in, std::back_inserter(out)); BOOST_CHECK_EQUAL_COLLECTIONS(in.begin(), in.end(), out.begin(), out.end()); } { - const std::vector in = {1, 2, 3, 4}; + const std::vector in = {{1, 2, 3, 4}}; std::list out; flatten(in, std::front_inserter(out)); BOOST_CHECK_EQUAL_COLLECTIONS(in.rbegin(), in.rend(), out.begin(), diff --git a/src/utils/tests/quaternion_test.cpp b/src/utils/tests/quaternion_test.cpp index 360a8c853bb..d6d248adcac 100644 --- a/src/utils/tests/quaternion_test.cpp +++ b/src/utils/tests/quaternion_test.cpp @@ -34,9 +34,9 @@ constexpr int x = 2; constexpr int y = 3; constexpr int z = 4; -Utils::Quaternion scalar_quat{w, 0, 0, 0}; -Utils::Quaternion full_quat{w, x, y, z}; -Utils::Quaternion vector_quat{0, x, y, z}; +Utils::Quaternion scalar_quat{{{{w, 0, 0, 0}}}}; +Utils::Quaternion full_quat{{{{w, x, y, z}}}}; +Utils::Quaternion vector_quat{{{{0, x, y, z}}}}; BOOST_AUTO_TEST_CASE(multiply_quaternions) { /* identities */ @@ -50,7 +50,7 @@ BOOST_AUTO_TEST_CASE(multiply_quaternions) { -vector_quat.norm2() * Utils::Quaternion::identity()); /* other */ - Utils::Quaternion const reference_quat{{-4, -20, -30, -40}}; + Utils::Quaternion const reference_quat{{{{-4, -20, -30, -40}}}}; BOOST_CHECK(full_quat * full_quat == reference_quat); } @@ -79,13 +79,13 @@ BOOST_AUTO_TEST_CASE(convert_director_to_quaternion) { #define CHECK_QUAT(input, ref) \ BOOST_CHECK_LE((convert_director_to_quaternion(input) - (ref)).norm2(), eps); /* identities */ - CHECK_QUAT((Vector3d{{0, 0, 0}}), (Quat{{1, 0, 0, 0}})); - CHECK_QUAT((Vector3d{{0, 0, +1}}), (Quat{{1, 0, 0, 0}})); - CHECK_QUAT((Vector3d{{0, 0, -1}}), (Quat{{0, -1, 0, 0}})); - CHECK_QUAT((Vector3d{{+1, 0, 0}}), (Quat{{+1, -1, +1, -1}} / 2.)); - CHECK_QUAT((Vector3d{{-1, 0, 0}}), (Quat{{-1, +1, +1, -1}} / 2.)); - CHECK_QUAT((Vector3d{{0, +1, 0}}), (Quat{{+1, -1, 0, 0}} * cos_pi_4)); - CHECK_QUAT((Vector3d{{0, -1, 0}}), (Quat{{0, 0, +1, -1}} * cos_pi_4)); + CHECK_QUAT((Vector3d{{0, 0, 0}}), (Quat{{{{1, 0, 0, 0}}}})); + CHECK_QUAT((Vector3d{{0, 0, +1}}), (Quat{{{{1, 0, 0, 0}}}})); + CHECK_QUAT((Vector3d{{0, 0, -1}}), (Quat{{{{0, -1, 0, 0}}}})); + CHECK_QUAT((Vector3d{{+1, 0, 0}}), (Quat{{{{+1, -1, +1, -1}}}} / 2.)); + CHECK_QUAT((Vector3d{{-1, 0, 0}}), (Quat{{{{-1, +1, +1, -1}}}} / 2.)); + CHECK_QUAT((Vector3d{{0, +1, 0}}), (Quat{{{{+1, -1, 0, 0}}}} * cos_pi_4)); + CHECK_QUAT((Vector3d{{0, -1, 0}}), (Quat{{{{0, 0, +1, -1}}}} * cos_pi_4)); /* self-consistency */ using Utils::convert_quaternion_to_director; for (int i = -2; i < 3; ++i) { @@ -108,18 +108,18 @@ BOOST_AUTO_TEST_CASE(convert_director_to_quaternion) { } BOOST_AUTO_TEST_CASE(quat_type) { - Utils::Quaternion test{1, 2, 3, 4}; + Utils::Quaternion test{{{{1, 2, 3, 4}}}}; BOOST_CHECK(test[0] == 1); test.normalize(); BOOST_CHECK_LE(test.norm() - 1.0, std::numeric_limits::epsilon()); BOOST_CHECK((Utils::Quaternion::identity() == - Utils::Quaternion{1, 0, 0, 0})); - BOOST_CHECK( - (Utils::Quaternion::zero() == Utils::Quaternion{0, 0, 0, 0})); - BOOST_CHECK((Utils::Quaternion{1, 0, 0, 0} == - Utils::Quaternion{2, 0, 0, 0}.normalized())); + Utils::Quaternion{{{{1, 0, 0, 0}}}})); + BOOST_CHECK((Utils::Quaternion::zero() == + Utils::Quaternion{{{{0, 0, 0, 0}}}})); + BOOST_CHECK((Utils::Quaternion{{{{1, 0, 0, 0}}}} == + Utils::Quaternion{{{{2, 0, 0, 0}}}}.normalized())); BOOST_CHECK_SMALL( - (Utils::Quaternion{2, 1, 3, 4}.normalized().norm() - 1.0), + (Utils::Quaternion{{{{2, 1, 3, 4}}}}.normalized().norm() - 1.0), std::numeric_limits::epsilon()); } diff --git a/src/utils/tests/rotation_matrix_test.cpp b/src/utils/tests/rotation_matrix_test.cpp index 8354f7f2268..cb1745e1f7e 100644 --- a/src/utils/tests/rotation_matrix_test.cpp +++ b/src/utils/tests/rotation_matrix_test.cpp @@ -41,9 +41,9 @@ BOOST_AUTO_TEST_CASE(rotation_matrix_test) { auto const axis = Vector3d{1., 2., 3.}.normalize(); auto const angle = 0.7; - auto const q = - Quaternion{cos(angle / 2), sin(angle / 2) * axis[0], - sin(angle / 2) * axis[1], sin(angle / 2) * axis[2]}; + auto const q = Quaternion{ + {{{cos(angle / 2), sin(angle / 2) * axis[0], sin(angle / 2) * axis[1], + sin(angle / 2) * axis[2]}}}}; auto const M = rotation_matrix(q); auto const v = Vector3d{3., 2., 1.}; diff --git a/src/utils/tests/tuple_test.cpp b/src/utils/tests/tuple_test.cpp index de913b5c6ec..2e8c03737da 100644 --- a/src/utils/tests/tuple_test.cpp +++ b/src/utils/tests/tuple_test.cpp @@ -37,7 +37,7 @@ BOOST_AUTO_TEST_CASE(for_each_) { /* l-value reference tuple */ { - auto a = std::array{2, 3, 5}; + auto a = std::array{{2, 3, 5}}; for_each( [i = 0, a](int &e) mutable { @@ -74,7 +74,7 @@ BOOST_AUTO_TEST_CASE(for_each_) { BOOST_AUTO_TEST_CASE(apply_) { /* constexpr */ { - static_assert(Utils::apply(std::plus<>(), std::array{3, 8}) == 11, + static_assert(Utils::apply(std::plus<>(), std::array{{3, 8}}) == 11, ""); } @@ -117,14 +117,14 @@ BOOST_AUTO_TEST_CASE(apply_) { BOOST_AUTO_TEST_CASE(find_if_) { { auto const result = Utils::find_if([](int e) { return e == 2; }, - std::array{1, 2, 3, 4}, + std::array{{1, 2, 3, 4}}, [](int e) { BOOST_CHECK_EQUAL(e, 2); }); BOOST_CHECK(result); } { auto const result = Utils::find_if([](int e) { return e == 5; }, - std::array{1, 2, 3, 4}, + std::array{{1, 2, 3, 4}}, [](int e) { BOOST_CHECK(false); }); BOOST_CHECK(not result); } From ba4fb2eab704e4f042600d5887dbc20396431922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 19 Jan 2022 22:56:08 +0100 Subject: [PATCH 23/99] CMake: Use portable "pretty function" macro Macro names with leading underscores are protected. --- CMakeLists.txt | 12 ++++++++---- cmake/cmake_config.cmakein | 10 ++++++---- src/core/errorhandling.hpp | 4 ++-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index efac6ca65d5..47a5588cd9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,18 +127,22 @@ include(MyConfig) include(CheckCXXSourceCompiles) -set(__PRETTYFUNC__ __func__) -foreach(func_name __PRETTY_FUNCTION__ __FUNCTION__) +# cross-platform macro to print the function name in error messages +set(PRETTY_FUNCTION_EXTENSION __func__) + +# search for a supported compiler extension that prints the function +# name as well as its list of arguments, return type and namespace +foreach(func_name __PRETTY_FUNCTION__ __FUNCSIG__ __FUNCTION__) check_cxx_source_compiles( " #include int main() { std::string(${func_name}); } " result${func_name}) if(result${func_name}) - set(__PRETTYFUNC__ ${func_name}) + set(PRETTY_FUNCTION_EXTENSION ${func_name}) break() endif(result${func_name}) -endforeach(func_name __PRETTY_FUNCTION__ __FUNCTION__) +endforeach() # # Libraries diff --git a/cmake/cmake_config.cmakein b/cmake/cmake_config.cmakein index 1fae2b1fd75..8a6bfc227ed 100644 --- a/cmake/cmake_config.cmakein +++ b/cmake/cmake_config.cmakein @@ -23,7 +23,9 @@ #define PACKAGE_NAME "${PROJECT_NAME}" -#define __PRETTYFUNC__ @__PRETTYFUNC__@ - - - +/** + * @brief Compiler-specific macro containing the demangled name + * of the function in the current scope. When the current compiler + * doesn't provide such an extension, defaults to @c __func__. + */ +#define PRETTY_FUNCTION_EXTENSION @PRETTY_FUNCTION_EXTENSION@ diff --git a/src/core/errorhandling.hpp b/src/core/errorhandling.hpp index f12016e1d46..132783cc7e6 100644 --- a/src/core/errorhandling.hpp +++ b/src/core/errorhandling.hpp @@ -87,12 +87,12 @@ RuntimeErrorStream _runtimeMessageStream(RuntimeError::ErrorLevel level, #define runtimeErrorMsg() \ ErrorHandling::_runtimeMessageStream( \ ErrorHandling::RuntimeError::ErrorLevel::ERROR, __FILE__, __LINE__, \ - __PRETTYFUNC__) + PRETTY_FUNCTION_EXTENSION) #define runtimeWarningMsg() \ ErrorHandling::_runtimeMessageStream( \ ErrorHandling::RuntimeError::ErrorLevel::WARNING, __FILE__, __LINE__, \ - __PRETTYFUNC__) + PRETTY_FUNCTION_EXTENSION) std::vector mpi_gather_runtime_errors(); From 6e5693a48778dafac506527170cff43781bedd37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 20 Jan 2022 13:29:56 +0100 Subject: [PATCH 24/99] core: Fix header includes and ifdef guards ESPResSo feature guards only work when config.hpp is included. Rename include guards to avoid leading underscores (triggers the Clang-Tidy bugprone-reserved-identifier warning) and disambiguate header files with similar names. --- src/core/accumulators/AccumulatorBase.hpp | 4 ++-- src/core/accumulators/Correlator.hpp | 4 ++-- .../accumulators/MeanVarianceCalculator.hpp | 4 ++-- src/core/actor/Actor.hpp | 6 +++--- src/core/actor/ActorList.hpp | 6 +++--- src/core/bonded_interactions/bonded_coulomb.hpp | 4 ++-- .../bonded_interactions/bonded_coulomb_sr.hpp | 4 ++-- .../bonded_interaction_data.hpp | 4 ++-- .../bonded_interaction_utils.hpp | 4 ++-- src/core/bonded_interactions/fene.hpp | 4 ++-- src/core/bonded_interactions/harmonic.hpp | 4 ++-- src/core/bonded_interactions/quartic.hpp | 4 ++-- src/core/cells.hpp | 4 ++-- src/core/collision.hpp | 6 +++--- src/core/communication.hpp | 4 ++-- src/core/cuda_init.hpp | 4 ++-- src/core/cuda_utils.cuh | 4 ++-- src/core/cuda_utils.hpp | 4 ++-- src/core/electrostatics_magnetostatics/elc.hpp | 4 ++-- src/core/electrostatics_magnetostatics/fft.hpp | 4 ++-- .../mdlc_correction.hpp | 8 ++++---- .../mmm-common.hpp | 4 ++-- .../p3m-common.hpp | 6 +++--- .../p3m-dipolar.hpp | 6 +++--- src/core/electrostatics_magnetostatics/p3m.hpp | 8 ++++---- .../electrostatics_magnetostatics/p3m_gpu.hpp | 6 +++--- .../p3m_gpu_cuda.cu | 10 +++++----- src/core/energy.hpp | 5 ++--- src/core/energy_inline.hpp | 8 ++++---- src/core/forces_inline.hpp | 9 ++++++--- src/core/ghosts.hpp | 4 ++-- src/core/grid.hpp | 4 ++-- .../grid_based_algorithms/electrokinetics.hpp | 6 +++--- .../grid_based_algorithms/fd-electrostatics.cuh | 4 ++-- src/core/grid_based_algorithms/halo.hpp | 7 +++---- src/core/integrators/steepest_descent.hpp | 6 +++--- src/core/interactions.hpp | 4 ++-- src/core/io/mpiio/mpiio.hpp | 6 +++--- .../nonbonded_interactions/VerletCriterion.hpp | 4 ++-- src/core/nonbonded_interactions/gay_berne.hpp | 6 +++--- src/core/nonbonded_interactions/lj.hpp | 4 ++-- src/core/nonbonded_interactions/ljcos.hpp | 6 +++--- src/core/nonbonded_interactions/ljcos2.hpp | 4 ++-- src/core/nonbonded_interactions/ljgen.hpp | 10 ++++------ src/core/nonbonded_interactions/morse.hpp | 6 +++--- .../nonbonded_interaction_data.hpp | 4 ++-- src/core/nonbonded_interactions/thole.hpp | 6 +++--- src/core/object-in-fluid/oif_global_forces.hpp | 4 ++-- src/core/object-in-fluid/oif_local_forces.hpp | 4 ++-- src/core/observables/ParticleForces.hpp | 1 + src/core/pair_criteria/pair_criteria.hpp | 2 -- src/core/particle_data.hpp | 4 ++-- src/core/pressure_inline.hpp | 9 ++++----- src/core/statistics.hpp | 4 ++-- src/core/virtual_sites/VirtualSites.hpp | 2 ++ .../virtual_sites/lb_inertialess_tracers.cpp | 1 + .../include/particle_observables/algorithms.hpp | 12 ++++++++---- .../collision_detection/CollisionDetection.hpp | 7 ++++--- src/script_interface/interactions/bonded.hpp | 4 ++-- .../lbboundaries/LBBoundary.hpp | 5 +++-- src/script_interface/mpiio/si_mpiio.hpp | 6 +++--- .../virtual_sites/ActiveVirtualSitesHandle.hpp | 14 +++++++++----- .../virtual_sites/VirtualSites.hpp | 10 ++++++---- .../VirtualSitesInertialessTracers.hpp | 11 ++++++++--- .../virtual_sites/VirtualSitesOff.hpp | 11 ++++++++--- .../virtual_sites/VirtualSitesRelative.hpp | 11 ++++++++--- src/shapes/include/shapes/Cylinder.hpp | 4 ++-- src/shapes/include/shapes/Rhomboid.hpp | 4 ++-- src/shapes/include/shapes/Slitpore.hpp | 4 ++-- src/shapes/include/shapes/Sphere.hpp | 4 ++-- src/shapes/include/shapes/SpheroCylinder.hpp | 4 ++-- src/shapes/include/shapes/Torus.hpp | 4 ++-- src/utils/include/utils/Array.hpp | 14 ++++++++++---- src/utils/include/utils/Vector.hpp | 12 +++++++++--- src/utils/include/utils/as_const.hpp | 4 ++-- src/utils/include/utils/matrix.hpp | 17 ++++++++--------- src/utils/include/utils/quaternion.hpp | 17 ++++++++++------- .../include/utils/statistics/RunningAverage.hpp | 4 ++-- 78 files changed, 251 insertions(+), 210 deletions(-) diff --git a/src/core/accumulators/AccumulatorBase.hpp b/src/core/accumulators/AccumulatorBase.hpp index 6c6528be246..6d0bb13507e 100644 --- a/src/core/accumulators/AccumulatorBase.hpp +++ b/src/core/accumulators/AccumulatorBase.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 CORE_ACCUMULATORS_ACCUMULATORBASE -#define CORE_ACCUMULATORS_ACCUMULATORBASE +#ifndef CORE_ACCUMULATORS_ACCUMULATOR_BASE_HPP +#define CORE_ACCUMULATORS_ACCUMULATOR_BASE_HPP #include #include diff --git a/src/core/accumulators/Correlator.hpp b/src/core/accumulators/Correlator.hpp index d67ec5dbb96..c5b95350c0b 100644 --- a/src/core/accumulators/Correlator.hpp +++ b/src/core/accumulators/Correlator.hpp @@ -16,6 +16,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#ifndef CORE_ACCUMULATORS_CORRELATOR_HPP +#define CORE_ACCUMULATORS_CORRELATOR_HPP /** @file * * This module computes correlations (and other two time averages) on @@ -97,8 +99,6 @@ * the topology concept * - Write a destructor */ -#ifndef _STATISTICS_CORRELATION_H -#define _STATISTICS_CORRELATION_H #include "AccumulatorBase.hpp" #include "integrate.hpp" diff --git a/src/core/accumulators/MeanVarianceCalculator.hpp b/src/core/accumulators/MeanVarianceCalculator.hpp index 5a3ce3e8617..f65c1812ae2 100644 --- a/src/core/accumulators/MeanVarianceCalculator.hpp +++ b/src/core/accumulators/MeanVarianceCalculator.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 _ACCUMULATORS_ACCUMULATOR_H -#define _ACCUMULATORS_ACCUMULATOR_H +#ifndef CORE_ACCUMULATORS_MEAN_VARIANCE_CALCULATOR_HPP +#define CORE_ACCUMULATORS_MEAN_VARIANCE_CALCULATOR_HPP #include "AccumulatorBase.hpp" #include "observables/Observable.hpp" diff --git a/src/core/actor/Actor.hpp b/src/core/actor/Actor.hpp index 92d7f22f235..1b740b69a5d 100644 --- a/src/core/actor/Actor.hpp +++ b/src/core/actor/Actor.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 _ACTOR_ACTOR_HPP -#define _ACTOR_ACTOR_HPP +#ifndef CORE_ACTOR_ACTOR_HPP +#define CORE_ACTOR_ACTOR_HPP #include "SystemInterface.hpp" @@ -33,4 +33,4 @@ class Actor { virtual ~Actor() = default; }; -#endif /* _ACTOR_ACTOR_HPP */ +#endif /* CORE_ACTOR_ACTOR_HPP */ diff --git a/src/core/actor/ActorList.hpp b/src/core/actor/ActorList.hpp index 5a30c25a96c..d004069b912 100644 --- a/src/core/actor/ActorList.hpp +++ b/src/core/actor/ActorList.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 _ACTOR_ACTORLIST_HPP -#define _ACTOR_ACTORLIST_HPP +#ifndef CORE_ACTOR_ACTORLIST_HPP +#define CORE_ACTOR_ACTORLIST_HPP #include "Actor.hpp" @@ -29,4 +29,4 @@ class ActorList : public std::vector { void remove(Actor *actor); }; -#endif /* _ACTOR_ACTORLIST_HPP */ +#endif /* CORE_ACTOR_ACTORLIST_HPP */ diff --git a/src/core/bonded_interactions/bonded_coulomb.hpp b/src/core/bonded_interactions/bonded_coulomb.hpp index 472fb3f0b46..7f1957d5ed4 100644 --- a/src/core/bonded_interactions/bonded_coulomb.hpp +++ b/src/core/bonded_interactions/bonded_coulomb.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _BONDED_COULOMB_HPP -#define _BONDED_COULOMB_HPP +#ifndef CORE_BN_IA_BONDED_COULOMB_HPP +#define CORE_BN_IA_BONDED_COULOMB_HPP /** \file * Routines to calculate the bonded Coulomb potential between * particle pairs. diff --git a/src/core/bonded_interactions/bonded_coulomb_sr.hpp b/src/core/bonded_interactions/bonded_coulomb_sr.hpp index ea074c036c6..9d9fc788fa2 100644 --- a/src/core/bonded_interactions/bonded_coulomb_sr.hpp +++ b/src/core/bonded_interactions/bonded_coulomb_sr.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _BONDED_COULOMB_SR_HPP -#define _BONDED_COULOMB_SR_HPP +#ifndef CORE_BN_IA_BONDED_COULOMB_SR_HPP +#define CORE_BN_IA_BONDED_COULOMB_SR_HPP /** \file * Routines to calculate the short-range part of the bonded Coulomb potential * between particle pairs. Can be used to subtract certain intramolecular diff --git a/src/core/bonded_interactions/bonded_interaction_data.hpp b/src/core/bonded_interactions/bonded_interaction_data.hpp index 0b864ad8fa7..bd81d69077d 100644 --- a/src/core/bonded_interactions/bonded_interaction_data.hpp +++ b/src/core/bonded_interactions/bonded_interaction_data.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 _BONDED_INTERACTION_DATA_HPP -#define _BONDED_INTERACTION_DATA_HPP +#ifndef CORE_BN_IA_BONDED_INTERACTION_DATA_HPP +#define CORE_BN_IA_BONDED_INTERACTION_DATA_HPP /** @file * Data structures for bonded interactions. * For more information on how to add new interactions, see @ref bondedIA_new. diff --git a/src/core/bonded_interactions/bonded_interaction_utils.hpp b/src/core/bonded_interactions/bonded_interaction_utils.hpp index ada4adbeb88..f21ec152722 100644 --- a/src/core/bonded_interactions/bonded_interaction_utils.hpp +++ b/src/core/bonded_interactions/bonded_interaction_utils.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 _BONDED_INTERACTION_UTILS_HPP -#define _BONDED_INTERACTION_UTILS_HPP +#ifndef CORE_BN_IA_BONDED_INTERACTION_UTILS_HPP +#define CORE_BN_IA_BONDED_INTERACTION_UTILS_HPP #include "bonded_interaction_data.hpp" diff --git a/src/core/bonded_interactions/fene.hpp b/src/core/bonded_interactions/fene.hpp index f3dddbc58b3..a18f34322b6 100644 --- a/src/core/bonded_interactions/fene.hpp +++ b/src/core/bonded_interactions/fene.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _FENE_HPP -#define _FENE_HPP +#ifndef CORE_BN_IA_FENE_HPP +#define CORE_BN_IA_FENE_HPP /** \file * Routines to calculate the FENE potential between particle pairs. * diff --git a/src/core/bonded_interactions/harmonic.hpp b/src/core/bonded_interactions/harmonic.hpp index e1018f7460f..77f8a4e06cb 100644 --- a/src/core/bonded_interactions/harmonic.hpp +++ b/src/core/bonded_interactions/harmonic.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _HARMONIC_HPP -#define _HARMONIC_HPP +#ifndef CORE_BN_IA_HARMONIC_HPP +#define CORE_BN_IA_HARMONIC_HPP /** \file * Routines to calculate the harmonic bond potential between particle pairs. */ diff --git a/src/core/bonded_interactions/quartic.hpp b/src/core/bonded_interactions/quartic.hpp index c461658e064..86b777305fd 100644 --- a/src/core/bonded_interactions/quartic.hpp +++ b/src/core/bonded_interactions/quartic.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _QUARTIC_HPP -#define _QUARTIC_HPP +#ifndef CORE_BN_IA_QUARTIC_HPP +#define CORE_BN_IA_QUARTIC_HPP /** \file * Routines to calculate the quartic potential between particle pairs. */ diff --git a/src/core/cells.hpp b/src/core/cells.hpp index 4924312ddc9..8b9abd36936 100644 --- a/src/core/cells.hpp +++ b/src/core/cells.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _CELLS_H -#define _CELLS_H +#ifndef CORE_CELLS_HPP +#define CORE_CELLS_HPP /** \file * This file contains everything related to the global cell structure / cell * system. diff --git a/src/core/collision.hpp b/src/core/collision.hpp index eb01ac4c853..e90fc460335 100644 --- a/src/core/collision.hpp +++ b/src/core/collision.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 _COLLISION_H -#define _COLLISION_H +#ifndef CORE_COLLISION_HPP +#define CORE_COLLISION_HPP #include "config.hpp" @@ -154,7 +154,7 @@ inline void detect_collision(Particle const &p1, Particle const &p2, queue_collision(p1.p.identity, p2.p.identity); } -#endif +#endif // COLLISION_DETECTION inline double collision_detection_cutoff() { #ifdef COLLISION_DETECTION diff --git a/src/core/communication.hpp b/src/core/communication.hpp index 9c55393e342..c7562703965 100644 --- a/src/core/communication.hpp +++ b/src/core/communication.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _COMMUNICATION_HPP -#define _COMMUNICATION_HPP +#ifndef CORE_COMMUNICATION_HPP +#define CORE_COMMUNICATION_HPP /** \file * This file contains the asynchronous MPI communication. * diff --git a/src/core/cuda_init.hpp b/src/core/cuda_init.hpp index b81d54dc551..955d4303b82 100644 --- a/src/core/cuda_init.hpp +++ b/src/core/cuda_init.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 _CUDA_INIT_H -#define _CUDA_INIT_H +#ifndef CORE_CUDA_INIT_H +#define CORE_CUDA_INIT_H #include "config.hpp" diff --git a/src/core/cuda_utils.cuh b/src/core/cuda_utils.cuh index 5eec836394b..5b80e462417 100644 --- a/src/core/cuda_utils.cuh +++ b/src/core/cuda_utils.cuh @@ -16,8 +16,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _CUDA_UTILS_CUH -#define _CUDA_UTILS_CUH +#ifndef CORE_CUDA_UTILS_CUH +#define CORE_CUDA_UTILS_CUH #if !defined(__CUDACC__) #error Do not include CUDA headers in normal C++-code!!! diff --git a/src/core/cuda_utils.hpp b/src/core/cuda_utils.hpp index faf49dbb472..6c3e1aa0eef 100644 --- a/src/core/cuda_utils.hpp +++ b/src/core/cuda_utils.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 _CUDA_UTILS_HPP -#define _CUDA_UTILS_HPP +#ifndef CORE_CUDA_UTILS_HPP +#define CORE_CUDA_UTILS_HPP #include "config.hpp" diff --git a/src/core/electrostatics_magnetostatics/elc.hpp b/src/core/electrostatics_magnetostatics/elc.hpp index f710f1d7d1a..8079a6accf3 100644 --- a/src/core/electrostatics_magnetostatics/elc.hpp +++ b/src/core/electrostatics_magnetostatics/elc.hpp @@ -26,8 +26,8 @@ * see MMM in general. The ELC method works together with any three-dimensional * method, for example \ref p3m.hpp "P3M", with metallic boundary conditions. */ -#ifndef _ELC_H -#define _ELC_H +#ifndef CORE_ELECTROSTATICS_MAGNETOSTATICS_ELC_HPP +#define CORE_ELECTROSTATICS_MAGNETOSTATICS_ELC_HPP #include "config.hpp" diff --git a/src/core/electrostatics_magnetostatics/fft.hpp b/src/core/electrostatics_magnetostatics/fft.hpp index cad4d7a634c..86d7cc7e1a5 100644 --- a/src/core/electrostatics_magnetostatics/fft.hpp +++ b/src/core/electrostatics_magnetostatics/fft.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _FFT_H -#define _FFT_H +#ifndef CORE_ELECTROSTATICS_MAGNETOSTATICS_FFT_HPP +#define CORE_ELECTROSTATICS_MAGNETOSTATICS_FFT_HPP /** \file * * Routines, row decomposition, data structures and communication for the diff --git a/src/core/electrostatics_magnetostatics/mdlc_correction.hpp b/src/core/electrostatics_magnetostatics/mdlc_correction.hpp index 542fdebecbf..166baa7731e 100644 --- a/src/core/electrostatics_magnetostatics/mdlc_correction.hpp +++ b/src/core/electrostatics_magnetostatics/mdlc_correction.hpp @@ -18,6 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#ifndef CORE_ELECTROSTATICS_MAGNETOSTATICS_DLC_DIPOLAR_HPP +#define CORE_ELECTROSTATICS_MAGNETOSTATICS_DLC_DIPOLAR_HPP /** \file * main header-file for MDLC (magnetic dipolar layer correction). * @@ -38,14 +40,12 @@ * Limitations: at this moment it is restricted to work with 1 cpu */ -#ifndef _DLC_DIPOLAR_H -#define _DLC_DIPOLAR_H - #include "config.hpp" -#include #ifdef DIPOLES +#include + /** parameters for the MDLC method */ struct DLC_struct { /** maximal pairwise error of the potential and force */ diff --git a/src/core/electrostatics_magnetostatics/mmm-common.hpp b/src/core/electrostatics_magnetostatics/mmm-common.hpp index c7d6f503e54..f25f76a7a6d 100644 --- a/src/core/electrostatics_magnetostatics/mmm-common.hpp +++ b/src/core/electrostatics_magnetostatics/mmm-common.hpp @@ -27,8 +27,8 @@ * directly from @cite abramowitz65a. For details, see @cite arnold02a. */ -#ifndef MMM_COMMON_H -#define MMM_COMMON_H +#ifndef CORE_ELECTROSTATICS_MAGNETOSTATICS_MMM_COMMON_HPP +#define CORE_ELECTROSTATICS_MAGNETOSTATICS_MMM_COMMON_HPP #include "mmm-modpsi.hpp" diff --git a/src/core/electrostatics_magnetostatics/p3m-common.hpp b/src/core/electrostatics_magnetostatics/p3m-common.hpp index d5757521e89..c5642389010 100644 --- a/src/core/electrostatics_magnetostatics/p3m-common.hpp +++ b/src/core/electrostatics_magnetostatics/p3m-common.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _P3M_COMMON_H -#define _P3M_COMMON_H +#ifndef CORE_ELECTROSTATICS_MAGNETOSTATICS_P3M_COMMON_HPP +#define CORE_ELECTROSTATICS_MAGNETOSTATICS_P3M_COMMON_HPP /** \file * Common functions for dipolar and charge P3M. * @@ -223,4 +223,4 @@ std::array, 3> inline calc_meshift( } } // namespace detail -#endif /* _P3M_COMMON_H */ +#endif /* CORE_ELECTROSTATICS_MAGNETOSTATICS_P3M_COMMON_HPP */ diff --git a/src/core/electrostatics_magnetostatics/p3m-dipolar.hpp b/src/core/electrostatics_magnetostatics/p3m-dipolar.hpp index 1053bdcdddb..d74b3ddd2a0 100644 --- a/src/core/electrostatics_magnetostatics/p3m-dipolar.hpp +++ b/src/core/electrostatics_magnetostatics/p3m-dipolar.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _P3M_MAGNETOSTATICS_H -#define _P3M_MAGNETOSTATICS_H +#ifndef CORE_ELECTROSTATICS_MAGNETOSTATICS_P3M_DIPOLAR_HPP +#define CORE_ELECTROSTATICS_MAGNETOSTATICS_P3M_DIPOLAR_HPP /** \file * P3M algorithm for long range magnetic dipole-dipole interaction. * @@ -284,4 +284,4 @@ inline double dp3m_pair_energy(Particle const &p1, Particle const &p2, } #endif /* DP3M */ -#endif /* _P3M_DIPOLES_H */ +#endif /* CORE_ELECTROSTATICS_MAGNETOSTATICS_P3M_DIPOLAR_HPP */ diff --git a/src/core/electrostatics_magnetostatics/p3m.hpp b/src/core/electrostatics_magnetostatics/p3m.hpp index 21fcda2a134..7312ef07b26 100644 --- a/src/core/electrostatics_magnetostatics/p3m.hpp +++ b/src/core/electrostatics_magnetostatics/p3m.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _P3M_H -#define _P3M_H +#ifndef CORE_ELECTROSTATICS_MAGNETOSTATICS_P3M_HPP +#define CORE_ELECTROSTATICS_MAGNETOSTATICS_P3M_HPP /** \file * P3M algorithm for long range Coulomb interaction. * @@ -242,6 +242,6 @@ inline double p3m_pair_energy(double chgfac, double dist) { return 0.0; } -#endif /* of ifdef P3M */ +#endif /* P3M */ -#endif /*of ifndef P3M_H */ +#endif /* CORE_ELECTROSTATICS_MAGNETOSTATICS_P3M_HPP */ diff --git a/src/core/electrostatics_magnetostatics/p3m_gpu.hpp b/src/core/electrostatics_magnetostatics/p3m_gpu.hpp index 1b6a9633d9c..f173a74c945 100644 --- a/src/core/electrostatics_magnetostatics/p3m_gpu.hpp +++ b/src/core/electrostatics_magnetostatics/p3m_gpu.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 _P3M_GPU_H -#define _P3M_GPU_H +#ifndef CORE_ELECTROSTATICS_MAGNETOSTATICS_P3M_GPU_HPP +#define CORE_ELECTROSTATICS_MAGNETOSTATICS_P3M_GPU_HPP /** \file * P3M electrostatics on GPU. * @@ -27,4 +27,4 @@ void p3m_gpu_init(int cao, const int mesh[3], double alpha); void p3m_gpu_add_farfield_force(); -#endif /* _P3M_GPU_H */ +#endif /* CORE_ELECTROSTATICS_MAGNETOSTATICS_P3M_GPU_HPP */ diff --git a/src/core/electrostatics_magnetostatics/p3m_gpu_cuda.cu b/src/core/electrostatics_magnetostatics/p3m_gpu_cuda.cu index b03339e7927..8ae7a05e8bf 100644 --- a/src/core/electrostatics_magnetostatics/p3m_gpu_cuda.cu +++ b/src/core/electrostatics_magnetostatics/p3m_gpu_cuda.cu @@ -28,10 +28,10 @@ #ifdef ELECTROSTATICS -#define _P3M_GPU_FLOAT -//#define _P3M_GPU_REAL_DOUBLE +#define P3M_GPU_FLOAT +//#define P3M_GPU_REAL_DOUBLE -#ifdef _P3M_GPU_FLOAT +#ifdef P3M_GPU_FLOAT #define REAL_TYPE float #define FFT_TYPE_COMPLEX cufftComplex #define FFT_FORW_FFT cufftExecR2C @@ -40,7 +40,7 @@ #define FFT_PLAN_BACK_FLAG CUFFT_C2R #endif -#ifdef _P3M_GPU_REAL_DOUBLE +#ifdef P3M_GPU_REAL_DOUBLE #define REAL_TYPE double #define FFT_TYPE_COMPLEX cufftDoubleComplex #define FFT_FORW_FFT cufftExecD2Z @@ -198,7 +198,7 @@ __global__ void calculate_influence_function_device(const P3MGpuData p) { } } -#ifdef _P3M_GPU_REAL_DOUBLE +#ifdef P3M_GPU_REAL_DOUBLE __device__ double atomicAdd(double *address, double val) { unsigned long long int *address_as_ull = (unsigned long long int *)address; unsigned long long int old = *address_as_ull, assumed; diff --git a/src/core/energy.hpp b/src/core/energy.hpp index d9ad254c7a2..d6e40ca947e 100644 --- a/src/core/energy.hpp +++ b/src/core/energy.hpp @@ -18,15 +18,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#ifndef CORE_ENERGY_HPP +#define CORE_ENERGY_HPP /** \file * Energy calculation. * * Implementation in energy.cpp. */ -#ifndef _ENERGY_H -#define _ENERGY_H - #include "Observable_stat.hpp" #include "actor/ActorList.hpp" diff --git a/src/core/energy_inline.hpp b/src/core/energy_inline.hpp index a6808ef361e..6b76be94b3b 100644 --- a/src/core/energy_inline.hpp +++ b/src/core/energy_inline.hpp @@ -18,11 +18,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#ifndef CORE_ENERGY_INLINE_HPP +#define CORE_ENERGY_INLINE_HPP /** \file - * Implementation of the energy calculation. + * Energy calculation. */ -#ifndef ENERGY_INLINE_HPP -#define ENERGY_INLINE_HPP #include "config.hpp" @@ -314,4 +314,4 @@ inline double calc_kinetic_energy(Particle const &p) { return translational_kinetic_energy(p) + rotational_kinetic_energy(p); } -#endif // ENERGY_INLINE_HPP +#endif // CORE_ENERGY_INLINE_HPP diff --git a/src/core/forces_inline.hpp b/src/core/forces_inline.hpp index a89136a2e50..e0d4f978508 100644 --- a/src/core/forces_inline.hpp +++ b/src/core/forces_inline.hpp @@ -18,8 +18,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _FORCES_INLINE_HPP -#define _FORCES_INLINE_HPP +#ifndef CORE_FORCES_INLINE_HPP +#define CORE_FORCES_INLINE_HPP +/** \file + * Force calculation. + */ #include "config.hpp" @@ -421,4 +424,4 @@ inline bool add_bonded_force(Particle &p1, int bond_id, } } -#endif +#endif // CORE_FORCES_INLINE_HPP diff --git a/src/core/ghosts.hpp b/src/core/ghosts.hpp index a298240bec4..dcfabff2e42 100644 --- a/src/core/ghosts.hpp +++ b/src/core/ghosts.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _GHOSTS_H -#define _GHOSTS_H +#ifndef CORE_GHOSTS_HPP +#define CORE_GHOSTS_HPP /** \file * Ghost particles and particle exchange. * diff --git a/src/core/grid.hpp b/src/core/grid.hpp index 26afae452e4..af5beaf2e71 100644 --- a/src/core/grid.hpp +++ b/src/core/grid.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _GRID_H -#define _GRID_H +#ifndef CORE_GRID_HPP +#define CORE_GRID_HPP /** @file * Domain decomposition for parallel computing. * diff --git a/src/core/grid_based_algorithms/electrokinetics.hpp b/src/core/grid_based_algorithms/electrokinetics.hpp index c7799cbfcd9..c76d6edca8a 100644 --- a/src/core/grid_based_algorithms/electrokinetics.hpp +++ b/src/core/grid_based_algorithms/electrokinetics.hpp @@ -17,8 +17,8 @@ * along with this program. If not, see . */ -#ifndef _ELECTROKINETICS_HPP -#define _ELECTROKINETICS_HPP +#ifndef CORE_GRID_BASED_ALGORITHMS_ELECTROKINETICS_HPP +#define CORE_GRID_BASED_ALGORITHMS_ELECTROKINETICS_HPP #include "config.hpp" #include "grid_based_algorithms/lb_boundaries.hpp" @@ -189,4 +189,4 @@ void ek_init_species_density_wallcharge(float *wallcharge_species_density, #endif /* CUDA */ -#endif /* ELECTROKINETICS_H */ +#endif /* CORE_GRID_BASED_ALGORITHMS_ELECTROKINETICS_HPP */ diff --git a/src/core/grid_based_algorithms/fd-electrostatics.cuh b/src/core/grid_based_algorithms/fd-electrostatics.cuh index 567e37b5ab9..241ba7588a6 100644 --- a/src/core/grid_based_algorithms/fd-electrostatics.cuh +++ b/src/core/grid_based_algorithms/fd-electrostatics.cuh @@ -16,8 +16,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _FD_ELECTROSTATICS_HPP -#define _FD_ELECTROSTATICS_HPP +#ifndef CORE_GRID_BASED_ALGORITHMS_FD_ELECTROSTATICS_HPP +#define CORE_GRID_BASED_ALGORITHMS_FD_ELECTROSTATICS_HPP #include diff --git a/src/core/grid_based_algorithms/halo.hpp b/src/core/grid_based_algorithms/halo.hpp index c985da44cae..cb2bc726c58 100644 --- a/src/core/grid_based_algorithms/halo.hpp +++ b/src/core/grid_based_algorithms/halo.hpp @@ -18,6 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#ifndef CORE_GRID_BASED_ALGORITHMS_HALO_HPP +#define CORE_GRID_BASED_ALGORITHMS_HALO_HPP /** \file * * Halo scheme for parallelization of lattice algorithms. @@ -25,9 +27,6 @@ * */ -#ifndef _HALO_HPP -#define _HALO_HPP - #include "grid_based_algorithms/lattice.hpp" #include @@ -133,4 +132,4 @@ void release_halo_communication(HaloCommunicator &hc); */ void halo_communication(const HaloCommunicator &hc, char *base); -#endif /* HALO_H */ +#endif /* CORE_GRID_BASED_ALGORITHMS_HALO_HPP */ diff --git a/src/core/integrators/steepest_descent.hpp b/src/core/integrators/steepest_descent.hpp index ce04ced64de..fe20f7cbee8 100644 --- a/src/core/integrators/steepest_descent.hpp +++ b/src/core/integrators/steepest_descent.hpp @@ -19,8 +19,8 @@ * along with this program. If not, see . */ -#ifndef __STEEPEST_DESCENT_HPP -#define __STEEPEST_DESCENT_HPP +#ifndef CORE_INTEGRATORS_STEEPEST_DESCENT_HPP +#define CORE_INTEGRATORS_STEEPEST_DESCENT_HPP #include "ParticleRange.hpp" @@ -64,4 +64,4 @@ void steepest_descent_init(double f_max, double gamma, double max_displacement); */ bool steepest_descent_step(const ParticleRange &particles); -#endif /* __STEEPEST_DESCENT_HPP */ +#endif /* CORE_INTEGRATORS_STEEPEST_DESCENT_HPP */ diff --git a/src/core/interactions.hpp b/src/core/interactions.hpp index e891b74af97..9e5b79c0a59 100644 --- a/src/core/interactions.hpp +++ b/src/core/interactions.hpp @@ -21,8 +21,8 @@ /** \file * This file contains the asynchronous MPI communication for interactions. */ -#ifndef _INTERACTIONS_HPP -#define _INTERACTIONS_HPP +#ifndef CORE_INTERACTIONS_HPP +#define CORE_INTERACTIONS_HPP /** Calculate the maximal cutoff of all interactions. */ double maximal_cutoff(); diff --git a/src/core/io/mpiio/mpiio.hpp b/src/core/io/mpiio/mpiio.hpp index be678886bd5..6a36fa9fc2a 100644 --- a/src/core/io/mpiio/mpiio.hpp +++ b/src/core/io/mpiio/mpiio.hpp @@ -18,13 +18,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#ifndef CORE_IO_MPIIO_MPIIO_HPP +#define CORE_IO_MPIIO_MPIIO_HPP + /** \file * Implements binary output using MPI-IO. */ -#ifndef _MPIIO_HPP -#define _MPIIO_HPP - #include "ParticleRange.hpp" namespace Mpiio { diff --git a/src/core/nonbonded_interactions/VerletCriterion.hpp b/src/core/nonbonded_interactions/VerletCriterion.hpp index cb770106d10..73e67366015 100644 --- a/src/core/nonbonded_interactions/VerletCriterion.hpp +++ b/src/core/nonbonded_interactions/VerletCriterion.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _VERLETCRITERION_HPP -#define _VERLETCRITERION_HPP +#ifndef CORE_NB_IA_VERLETCRITERION_HPP +#define CORE_NB_IA_VERLETCRITERION_HPP #include "Particle.hpp" #include "config.hpp" diff --git a/src/core/nonbonded_interactions/gay_berne.hpp b/src/core/nonbonded_interactions/gay_berne.hpp index 33cc0b5fa97..7e17e15079c 100644 --- a/src/core/nonbonded_interactions/gay_berne.hpp +++ b/src/core/nonbonded_interactions/gay_berne.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _GB_HPP -#define _GB_HPP +#ifndef CORE_NB_IA_GB_HPP +#define CORE_NB_IA_GB_HPP /** \file * Routines to calculate the Gay-Berne potential between particle pairs. @@ -175,5 +175,5 @@ inline double gb_pair_energy(Utils::Vector3d const &ui, return E(r_eff(dist)) - E(r_eff(ia_params.gay_berne.cut)); } -#endif +#endif // GAY_BERNE #endif diff --git a/src/core/nonbonded_interactions/lj.hpp b/src/core/nonbonded_interactions/lj.hpp index 57fd4205933..ee07a3b7fde 100644 --- a/src/core/nonbonded_interactions/lj.hpp +++ b/src/core/nonbonded_interactions/lj.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _LJ_H -#define _LJ_H +#ifndef CORE_NB_IA_LJ_HPP +#define CORE_NB_IA_LJ_HPP #include "config.hpp" diff --git a/src/core/nonbonded_interactions/ljcos.hpp b/src/core/nonbonded_interactions/ljcos.hpp index 6115f3ccb96..8c0762befae 100644 --- a/src/core/nonbonded_interactions/ljcos.hpp +++ b/src/core/nonbonded_interactions/ljcos.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _LJCOS_H -#define _LJCOS_H +#ifndef CORE_NB_IA_LJCOS_HPP +#define CORE_NB_IA_LJCOS_HPP /** \file * Routines to calculate the Lennard-Jones+cosine potential between * particle pairs. @@ -81,5 +81,5 @@ inline double ljcos_pair_energy(IA_parameters const &ia_params, double dist) { return 0.0; } -#endif +#endif // LJCOS #endif diff --git a/src/core/nonbonded_interactions/ljcos2.hpp b/src/core/nonbonded_interactions/ljcos2.hpp index b54dbdb61fd..d53bf488661 100644 --- a/src/core/nonbonded_interactions/ljcos2.hpp +++ b/src/core/nonbonded_interactions/ljcos2.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _LJCOS2_H -#define _LJCOS2_H +#ifndef CORE_NB_IA_LJCOS2_HPP +#define CORE_NB_IA_LJCOS2_HPP /** \file * Routines to calculate the Lennard-Jones with cosine tail potential diff --git a/src/core/nonbonded_interactions/ljgen.hpp b/src/core/nonbonded_interactions/ljgen.hpp index 034a390da46..2db9d517bbd 100644 --- a/src/core/nonbonded_interactions/ljgen.hpp +++ b/src/core/nonbonded_interactions/ljgen.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _LJGEN_H -#define _LJGEN_H +#ifndef CORE_NB_IA_LJGEN_HPP +#define CORE_NB_IA_LJGEN_HPP /** \file * Routines to calculate the generalized Lennard-Jones potential between @@ -111,7 +111,5 @@ inline double ljgen_pair_energy(IA_parameters const &ia_params, double dist) { return 0.0; } -#endif - -/* LJGEN_H */ -#endif +#endif // LENNARD_JONES_GENERIC +#endif // CORE_NB_IA_LJGEN_HPP diff --git a/src/core/nonbonded_interactions/morse.hpp b/src/core/nonbonded_interactions/morse.hpp index eae871f9afa..a571f54510e 100644 --- a/src/core/nonbonded_interactions/morse.hpp +++ b/src/core/nonbonded_interactions/morse.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _MORSE_H -#define _MORSE_H +#ifndef CORE_NB_IA_MORSE_HPP +#define CORE_NB_IA_MORSE_HPP /** \file * Routines to calculate the Morse potential between particle pairs. @@ -65,4 +65,4 @@ inline double morse_pair_energy(IA_parameters const &ia_params, double dist) { } #endif /* ifdef MORSE */ -#endif /* ifdef _MORSE_H */ +#endif /* ifdef CORE_NB_IA_MORSE_HPP */ diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index 2ddf85705d9..347fe99e5ce 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _INTERACTION_DATA_H -#define _INTERACTION_DATA_H +#ifndef CORE_NB_IA_INTERACTION_DATA_HPP +#define CORE_NB_IA_INTERACTION_DATA_HPP /** \file * Various procedures concerning interactions between particles. */ diff --git a/src/core/nonbonded_interactions/thole.hpp b/src/core/nonbonded_interactions/thole.hpp index 0e982ed7a1c..2e6ff05c2f4 100644 --- a/src/core/nonbonded_interactions/thole.hpp +++ b/src/core/nonbonded_interactions/thole.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _THOLE_H -#define _THOLE_H +#ifndef CORE_NB_IA_THOLE_HPP +#define CORE_NB_IA_THOLE_HPP /** \file * Routines to calculate the Thole damping potential between particle pairs. * See @cite thole81a. @@ -84,5 +84,5 @@ inline double thole_pair_energy(Particle const &p1, Particle const &p2, } return 0.0; } -#endif +#endif // THOLE #endif diff --git a/src/core/object-in-fluid/oif_global_forces.hpp b/src/core/object-in-fluid/oif_global_forces.hpp index aa8a9dea2f5..a0177900607 100644 --- a/src/core/object-in-fluid/oif_global_forces.hpp +++ b/src/core/object-in-fluid/oif_global_forces.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 _OBJECT_IN_FLUID_OIF_GLOBAL_FORCES_H -#define _OBJECT_IN_FLUID_OIF_GLOBAL_FORCES_H +#ifndef CORE_OBJECT_IN_FLUID_OIF_GLOBAL_FORCES_HPP +#define CORE_OBJECT_IN_FLUID_OIF_GLOBAL_FORCES_HPP /** \file * Routines to calculate the OIF global forces energy or/and and force * for a particle triple (triangle from mesh). See @cite dupin07a. diff --git a/src/core/object-in-fluid/oif_local_forces.hpp b/src/core/object-in-fluid/oif_local_forces.hpp index e8d482e7852..68b86811c5b 100644 --- a/src/core/object-in-fluid/oif_local_forces.hpp +++ b/src/core/object-in-fluid/oif_local_forces.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 _OBJECT_IN_FLUID_OIF_LOCAL_FORCES_H -#define _OBJECT_IN_FLUID_OIF_LOCAL_FORCES_H +#ifndef CORE_OBJECT_IN_FLUID_OIF_LOCAL_FORCES_HPP +#define CORE_OBJECT_IN_FLUID_OIF_LOCAL_FORCES_HPP /** \file * Routines to calculate the OIF local forces for a particle quadruple diff --git a/src/core/observables/ParticleForces.hpp b/src/core/observables/ParticleForces.hpp index a6ed45788b2..bcd0702a276 100644 --- a/src/core/observables/ParticleForces.hpp +++ b/src/core/observables/ParticleForces.hpp @@ -21,6 +21,7 @@ #include "Particle.hpp" #include "PidObservable.hpp" + #include #include diff --git a/src/core/pair_criteria/pair_criteria.hpp b/src/core/pair_criteria/pair_criteria.hpp index 561e7ce4e00..a64d7061f65 100644 --- a/src/core/pair_criteria/pair_criteria.hpp +++ b/src/core/pair_criteria/pair_criteria.hpp @@ -24,8 +24,6 @@ #include "grid.hpp" #include "particle_data.hpp" -#include - namespace PairCriteria { /** @brief Criterion which provides a true/false for a pair of particles */ class PairCriterion { diff --git a/src/core/particle_data.hpp b/src/core/particle_data.hpp index bc078c3a60a..390ae8b7387 100644 --- a/src/core/particle_data.hpp +++ b/src/core/particle_data.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _PARTICLE_DATA_H -#define _PARTICLE_DATA_H +#ifndef CORE_PARTICLE_DATA_HPP +#define CORE_PARTICLE_DATA_HPP /** \file * Particles and particle lists. * diff --git a/src/core/pressure_inline.hpp b/src/core/pressure_inline.hpp index eef2ff4718f..d5693d3d87b 100644 --- a/src/core/pressure_inline.hpp +++ b/src/core/pressure_inline.hpp @@ -18,12 +18,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -/** \file - * Pressure calculation. Really similar to energy.hpp. - */ - #ifndef CORE_PRESSURE_INLINE_HPP #define CORE_PRESSURE_INLINE_HPP +/** \file + * Pressure calculation. + */ #include "config.hpp" @@ -170,4 +169,4 @@ inline void add_kinetic_virials(Particle const &p1, obs_pressure.kinetic[k * 3 + l] += p1.m.v[k] * p1.m.v[l] * p1.p.mass; } -#endif +#endif // CORE_PRESSURE_INLINE_HPP diff --git a/src/core/statistics.hpp b/src/core/statistics.hpp index 5b7f95e2ffa..598542bee4b 100644 --- a/src/core/statistics.hpp +++ b/src/core/statistics.hpp @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _STATISTICS_H -#define _STATISTICS_H +#ifndef CORE_STATISTICS_HPP +#define CORE_STATISTICS_HPP /** \file * Statistical tools to analyze simulations. * diff --git a/src/core/virtual_sites/VirtualSites.hpp b/src/core/virtual_sites/VirtualSites.hpp index ffc50b24780..3b6e80a14ab 100644 --- a/src/core/virtual_sites/VirtualSites.hpp +++ b/src/core/virtual_sites/VirtualSites.hpp @@ -32,6 +32,8 @@ * - update virtual sites */ +#include + #ifdef VIRTUAL_SITES #include #include diff --git a/src/core/virtual_sites/lb_inertialess_tracers.cpp b/src/core/virtual_sites/lb_inertialess_tracers.cpp index b93a9579d12..9dde81803af 100644 --- a/src/core/virtual_sites/lb_inertialess_tracers.cpp +++ b/src/core/virtual_sites/lb_inertialess_tracers.cpp @@ -34,6 +34,7 @@ #include "integrate.hpp" #include "lb_inertialess_tracers_cuda_interface.hpp" +#include #include #include diff --git a/src/particle_observables/include/particle_observables/algorithms.hpp b/src/particle_observables/include/particle_observables/algorithms.hpp index 6a18740f01e..4ec9aaaab2e 100644 --- a/src/particle_observables/include/particle_observables/algorithms.hpp +++ b/src/particle_observables/include/particle_observables/algorithms.hpp @@ -18,15 +18,19 @@ */ #ifndef ALGORITHMS_HPP #define ALGORITHMS_HPP + +/** + * @file + * + * Generic algorithms for the calculation of particle + * property derived observables. + */ + #include #include #include #include -/** @file algorithms.hpp - * This file contains generic algorithms for the calculation of particle - * property derived observables. - */ namespace ParticleObservables { namespace detail { struct One { diff --git a/src/script_interface/collision_detection/CollisionDetection.hpp b/src/script_interface/collision_detection/CollisionDetection.hpp index 043c4d3e870..cc336b717d4 100644 --- a/src/script_interface/collision_detection/CollisionDetection.hpp +++ b/src/script_interface/collision_detection/CollisionDetection.hpp @@ -24,12 +24,13 @@ #include "config.hpp" -#include "core/collision.hpp" +#ifdef COLLISION_DETECTION + #include "script_interface/ScriptInterface.hpp" -#include +#include "core/collision.hpp" -#ifdef COLLISION_DETECTION +#include namespace ScriptInterface { namespace CollisionDetection { diff --git a/src/script_interface/interactions/bonded.hpp b/src/script_interface/interactions/bonded.hpp index 60004a4997b..df53b89b338 100644 --- a/src/script_interface/interactions/bonded.hpp +++ b/src/script_interface/interactions/bonded.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_INTERACTIONS_BONDED_HPP -#define _SCRIPT_INTERFACE_INTERACTIONS_BONDED_HPP +#ifndef SCRIPT_INTERFACE_INTERACTIONS_BONDED_HPP +#define SCRIPT_INTERFACE_INTERACTIONS_BONDED_HPP /** @file * Functions to interface with the core boost::variant. diff --git a/src/script_interface/lbboundaries/LBBoundary.hpp b/src/script_interface/lbboundaries/LBBoundary.hpp index dfdafd25a2e..6167643bb40 100644 --- a/src/script_interface/lbboundaries/LBBoundary.hpp +++ b/src/script_interface/lbboundaries/LBBoundary.hpp @@ -21,12 +21,13 @@ #include "config.hpp" -#include "core/grid_based_algorithms/lb_interface.hpp" -#include "core/grid_based_algorithms/lbboundaries/LBBoundary.hpp" #include "script_interface/ScriptInterface.hpp" #include "script_interface/auto_parameters/AutoParameters.hpp" #include "script_interface/shapes/Shape.hpp" +#include "core/grid_based_algorithms/lb_interface.hpp" +#include "core/grid_based_algorithms/lbboundaries/LBBoundary.hpp" + #include #include diff --git a/src/script_interface/mpiio/si_mpiio.hpp b/src/script_interface/mpiio/si_mpiio.hpp index 966bd10c1d6..f57ca9acf79 100644 --- a/src/script_interface/mpiio/si_mpiio.hpp +++ b/src/script_interface/mpiio/si_mpiio.hpp @@ -22,12 +22,12 @@ #ifndef ESPRESSO_SCRIPTINTERFACE_MPIIO_HPP #define ESPRESSO_SCRIPTINTERFACE_MPIIO_HPP -#include "config.hpp" -#include "io/mpiio/mpiio.hpp" #include "script_interface/ScriptInterface.hpp" #include "script_interface/auto_parameters/AutoParameters.hpp" #include "script_interface/get_value.hpp" -#include + +#include "core/cells.hpp" +#include "core/io/mpiio/mpiio.hpp" #include diff --git a/src/script_interface/virtual_sites/ActiveVirtualSitesHandle.hpp b/src/script_interface/virtual_sites/ActiveVirtualSitesHandle.hpp index b105fb95ff4..38d0a2251ca 100644 --- a/src/script_interface/virtual_sites/ActiveVirtualSitesHandle.hpp +++ b/src/script_interface/virtual_sites/ActiveVirtualSitesHandle.hpp @@ -22,14 +22,18 @@ #ifndef SCRIPT_INTERFACE_VIRTUAL_SITES_ACTIVE_VIRTUAL_SITES_HANDLE_HPP #define SCRIPT_INTERFACE_VIRTUAL_SITES_ACTIVE_VIRTUAL_SITES_HANDLE_HPP -#include "VirtualSites.hpp" #include "config.hpp" -#include "core/virtual_sites.hpp" -#include "errorhandling.hpp" -#include "script_interface/auto_parameters/AutoParameters.hpp" #ifdef VIRTUAL_SITES +#include "VirtualSites.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include "core/errorhandling.hpp" +#include "core/virtual_sites.hpp" + +#include + namespace ScriptInterface { namespace VirtualSites { @@ -52,5 +56,5 @@ class ActiveVirtualSitesHandle }; } /* namespace VirtualSites */ } /* namespace ScriptInterface */ -#endif +#endif // VIRTUAL_SITES #endif diff --git a/src/script_interface/virtual_sites/VirtualSites.hpp b/src/script_interface/virtual_sites/VirtualSites.hpp index 26196daaf15..db22134ebab 100644 --- a/src/script_interface/virtual_sites/VirtualSites.hpp +++ b/src/script_interface/virtual_sites/VirtualSites.hpp @@ -23,15 +23,18 @@ #define SCRIPT_INTERFACE_VIRTUAL_SITES_VIRTUAL_SITES_HPP #include "config.hpp" -#include "core/virtual_sites.hpp" + +#ifdef VIRTUAL_SITES + #include "script_interface/auto_parameters/AutoParameters.hpp" +#include "core/virtual_sites.hpp" + #include namespace ScriptInterface { namespace VirtualSites { -#ifdef VIRTUAL_SITES class VirtualSites : public AutoParameters { public: VirtualSites() { @@ -46,8 +49,7 @@ class VirtualSites : public AutoParameters { virtual std::shared_ptr<::VirtualSites> virtual_sites() = 0; }; -#endif - } /* namespace VirtualSites */ } /* namespace ScriptInterface */ +#endif // VIRTUAL_SITES #endif diff --git a/src/script_interface/virtual_sites/VirtualSitesInertialessTracers.hpp b/src/script_interface/virtual_sites/VirtualSitesInertialessTracers.hpp index 21a79d7e5ab..1cc84c5365c 100644 --- a/src/script_interface/virtual_sites/VirtualSitesInertialessTracers.hpp +++ b/src/script_interface/virtual_sites/VirtualSitesInertialessTracers.hpp @@ -22,11 +22,16 @@ #ifndef SCRIPT_INTERFACE_VIRTUAL_SITES_VIRTUAL_SITES_INERTIALESS_TRACERS_HPP #define SCRIPT_INTERFACE_VIRTUAL_SITES_VIRTUAL_SITES_INERTIALESS_TRACERS_HPP -#include "VirtualSites.hpp" #include "config.hpp" -#include "core/virtual_sites/VirtualSitesInertialessTracers.hpp" + #ifdef VIRTUAL_SITES_INERTIALESS_TRACERS +#include "VirtualSites.hpp" + +#include "core/virtual_sites/VirtualSitesInertialessTracers.hpp" + +#include + namespace ScriptInterface { namespace VirtualSites { @@ -45,5 +50,5 @@ class VirtualSitesInertialessTracers : public VirtualSites { } /* namespace VirtualSites */ } /* namespace ScriptInterface */ -#endif +#endif // VIRTUAL_SITES_INERTIALESS_TRACERS #endif diff --git a/src/script_interface/virtual_sites/VirtualSitesOff.hpp b/src/script_interface/virtual_sites/VirtualSitesOff.hpp index 851bb3374a3..185a7506213 100644 --- a/src/script_interface/virtual_sites/VirtualSitesOff.hpp +++ b/src/script_interface/virtual_sites/VirtualSitesOff.hpp @@ -22,11 +22,16 @@ #ifndef SCRIPT_INTERFACE_VIRTUAL_SITES_VIRTUAL_SITES_OFF_HPP #define SCRIPT_INTERFACE_VIRTUAL_SITES_VIRTUAL_SITES_OFF_HPP -#include "VirtualSites.hpp" #include "config.hpp" -#include "core/virtual_sites/VirtualSitesOff.hpp" + #ifdef VIRTUAL_SITES +#include "VirtualSites.hpp" + +#include "core/virtual_sites/VirtualSitesOff.hpp" + +#include + namespace ScriptInterface { namespace VirtualSites { @@ -44,5 +49,5 @@ class VirtualSitesOff : public VirtualSites { } /* namespace VirtualSites */ } /* namespace ScriptInterface */ -#endif +#endif // VIRTUAL_SITES #endif diff --git a/src/script_interface/virtual_sites/VirtualSitesRelative.hpp b/src/script_interface/virtual_sites/VirtualSitesRelative.hpp index 4ead3f72024..362bb94f0b5 100644 --- a/src/script_interface/virtual_sites/VirtualSitesRelative.hpp +++ b/src/script_interface/virtual_sites/VirtualSitesRelative.hpp @@ -22,11 +22,16 @@ #ifndef SCRIPT_INTERFACE_VIRTUAL_SITES_VIRTUAL_SITES_RELATIVE_HPP #define SCRIPT_INTERFACE_VIRTUAL_SITES_VIRTUAL_SITES_RELATIVE_HPP -#include "VirtualSites.hpp" #include "config.hpp" -#include "core/virtual_sites/VirtualSitesRelative.hpp" #ifdef VIRTUAL_SITES_RELATIVE + +#include "VirtualSites.hpp" + +#include "core/virtual_sites/VirtualSitesRelative.hpp" + +#include + namespace ScriptInterface { namespace VirtualSites { @@ -44,5 +49,5 @@ class VirtualSitesRelative : public VirtualSites { } /* namespace VirtualSites */ } /* namespace ScriptInterface */ -#endif +#endif // VIRTUAL_SITES_RELATIVE #endif diff --git a/src/shapes/include/shapes/Cylinder.hpp b/src/shapes/include/shapes/Cylinder.hpp index 7873a7bce07..a6867cbea52 100644 --- a/src/shapes/include/shapes/Cylinder.hpp +++ b/src/shapes/include/shapes/Cylinder.hpp @@ -19,8 +19,8 @@ * along with this program. If not, see . */ -#ifndef __CYLINDER_HPP -#define __CYLINDER_HPP +#ifndef SRC_SHAPES_CYLINDER_HPP +#define SRC_SHAPES_CYLINDER_HPP #include "Shape.hpp" diff --git a/src/shapes/include/shapes/Rhomboid.hpp b/src/shapes/include/shapes/Rhomboid.hpp index 1136ec1825d..14e7e7a6046 100644 --- a/src/shapes/include/shapes/Rhomboid.hpp +++ b/src/shapes/include/shapes/Rhomboid.hpp @@ -19,8 +19,8 @@ * along with this program. If not, see . */ -#ifndef __RHOMBOID_HPP -#define __RHOMBOID_HPP +#ifndef SRC_SHAPES_RHOMBOID_HPP +#define SRC_SHAPES_RHOMBOID_HPP #include "Shape.hpp" #include diff --git a/src/shapes/include/shapes/Slitpore.hpp b/src/shapes/include/shapes/Slitpore.hpp index 5a0c61403e1..1309ee1ef55 100644 --- a/src/shapes/include/shapes/Slitpore.hpp +++ b/src/shapes/include/shapes/Slitpore.hpp @@ -19,8 +19,8 @@ * along with this program. If not, see . */ -#ifndef __SLITPORE_HPP -#define __SLITPORE_HPP +#ifndef SRC_SHAPES_SLITPORE_HPP +#define SRC_SHAPES_SLITPORE_HPP #include "Shape.hpp" diff --git a/src/shapes/include/shapes/Sphere.hpp b/src/shapes/include/shapes/Sphere.hpp index 313ea9ba76b..9eacfdc6b31 100644 --- a/src/shapes/include/shapes/Sphere.hpp +++ b/src/shapes/include/shapes/Sphere.hpp @@ -19,8 +19,8 @@ * along with this program. If not, see . */ -#ifndef __SPHERE_HPP -#define __SPHERE_HPP +#ifndef SRC_SHAPES_SPHERE_HPP +#define SRC_SHAPES_SPHERE_HPP #include "Shape.hpp" #include diff --git a/src/shapes/include/shapes/SpheroCylinder.hpp b/src/shapes/include/shapes/SpheroCylinder.hpp index 5d38139f999..93beccb8d95 100644 --- a/src/shapes/include/shapes/SpheroCylinder.hpp +++ b/src/shapes/include/shapes/SpheroCylinder.hpp @@ -19,8 +19,8 @@ * along with this program. If not, see . */ -#ifndef __SPHEROCYLINDER_HPP -#define __SPHEROCYLINDER_HPP +#ifndef SRC_SHAPES_SPHEROCYLINDER_HPP +#define SRC_SHAPES_SPHEROCYLINDER_HPP #include "Shape.hpp" #include diff --git a/src/shapes/include/shapes/Torus.hpp b/src/shapes/include/shapes/Torus.hpp index 8b972b61fc8..20adc4dc71a 100644 --- a/src/shapes/include/shapes/Torus.hpp +++ b/src/shapes/include/shapes/Torus.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 __TORUS_HPP -#define __TORUS_HPP +#ifndef SRC_SHAPES_TORUS_HPP +#define SRC_SHAPES_TORUS_HPP #include "Shape.hpp" #include diff --git a/src/utils/include/utils/Array.hpp b/src/utils/include/utils/Array.hpp index 5beb5cf92d4..2704fc3ceb6 100644 --- a/src/utils/include/utils/Array.hpp +++ b/src/utils/include/utils/Array.hpp @@ -16,8 +16,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef UTILS_ARRAY_HPP -#define UTILS_ARRAY_HPP +#ifndef SRC_UTILS_INCLUDE_UTILS_ARRAY_HPP +#define SRC_UTILS_INCLUDE_UTILS_ARRAY_HPP + +/** + * @file + * + * @brief Array implementation with CUDA support. + */ #include "device_qualifier.hpp" #include "get.hpp" @@ -29,8 +35,8 @@ #include #include #include -namespace Utils { +namespace Utils { namespace detail { template struct Storage { @@ -208,4 +214,4 @@ auto get(Array const &a) -> std::enable_if_t<(I < N), const T &> { } } // namespace Utils -#endif +#endif // SRC_UTILS_INCLUDE_UTILS_ARRAY_HPP diff --git a/src/utils/include/utils/Vector.hpp b/src/utils/include/utils/Vector.hpp index 3d8db6e977c..ab3d5741cd2 100644 --- a/src/utils/include/utils/Vector.hpp +++ b/src/utils/include/utils/Vector.hpp @@ -16,9 +16,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#ifndef SRC_UTILS_INCLUDE_UTILS_VECTOR_HPP +#define SRC_UTILS_INCLUDE_UTILS_VECTOR_HPP -#ifndef VECTOR_HPP -#define VECTOR_HPP +/** + * @file + * + * @brief Vector implementation and trait types + * for boost qvm interoperability. + */ #include #include @@ -500,4 +506,4 @@ template struct deduce_vec, 3> { } // namespace qvm } // namespace boost -#endif +#endif // SRC_UTILS_INCLUDE_UTILS_VECTOR_HPP diff --git a/src/utils/include/utils/as_const.hpp b/src/utils/include/utils/as_const.hpp index ea59bef2999..9e1f1f7068d 100644 --- a/src/utils/include/utils/as_const.hpp +++ b/src/utils/include/utils/as_const.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 CORE_UTILS_AS_CONST_HPP -#define CORE_UTILS_AS_CONST_HPP +#ifndef UTILS_AS_CONST_HPP +#define UTILS_AS_CONST_HPP #include diff --git a/src/utils/include/utils/matrix.hpp b/src/utils/include/utils/matrix.hpp index ba3081f5d2f..fd1fa56a377 100644 --- a/src/utils/include/utils/matrix.hpp +++ b/src/utils/include/utils/matrix.hpp @@ -19,6 +19,13 @@ #ifndef SRC_UTILS_INCLUDE_UTILS_MATRIX_HPP #define SRC_UTILS_INCLUDE_UTILS_MATRIX_HPP +/** + * @file + * + * @brief Matrix implementation and trait types + * for boost qvm interoperability. + */ + #include "utils/Array.hpp" #include "utils/Vector.hpp" #include "utils/flatten.hpp" @@ -47,13 +54,6 @@ #include #include -/** - * @file matrix.hpp - * - * @brief This file contains a matrix implementation and the trait types needed - * for the boost qvm interoperability. - */ - namespace Utils { /** @@ -300,5 +300,4 @@ struct deduce_mat2, Utils::Matrix, 3, 3> { } // namespace qvm } // namespace boost - -#endif +#endif // SRC_UTILS_INCLUDE_UTILS_MATRIX_HPP diff --git a/src/utils/include/utils/quaternion.hpp b/src/utils/include/utils/quaternion.hpp index bd5a9d3ed24..c8cfe693340 100644 --- a/src/utils/include/utils/quaternion.hpp +++ b/src/utils/include/utils/quaternion.hpp @@ -19,6 +19,13 @@ #ifndef SRC_UTILS_INCLUDE_UTILS_QUATERNION_HPP #define SRC_UTILS_INCLUDE_UTILS_QUATERNION_HPP +/** + * @file + * + * @brief Quaternion implementation and trait types + * for boost qvm interoperability. + */ + #include #include #include @@ -37,13 +44,6 @@ #include #include -/** - * @file quaternion.hpp - * - * @brief This file contains a matrix implementation and the trait types needed - * for the boost qvm interoperability. - */ - namespace Utils { /** @@ -58,11 +58,14 @@ template struct Quaternion { using value_type = typename container::value_type; using reference = typename container::reference; +private: friend class boost::serialization::access; template void serialize(Archive &ar, const unsigned int version) { ar &m_data; } + +public: /** * @brief Normalize the quaternion in place. */ diff --git a/src/utils/include/utils/statistics/RunningAverage.hpp b/src/utils/include/utils/statistics/RunningAverage.hpp index 1e098988d6e..e73a8f526c2 100644 --- a/src/utils/include/utils/statistics/RunningAverage.hpp +++ b/src/utils/include/utils/statistics/RunningAverage.hpp @@ -19,8 +19,8 @@ * along with this program. If not, see . */ -#ifndef __RUNING_AVERAGE_HPP -#define __RUNING_AVERAGE_HPP +#ifndef STATISTICS_RUNING_AVERAGE_HPP +#define STATISTICS_RUNING_AVERAGE_HPP #include #include From 0126be7500d5a3d1ec68dee5da7cace08ee1ecf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 20 Jan 2022 15:19:39 +0100 Subject: [PATCH 25/99] core: Code maintenance Make use of Utils::Vector3d::broadcast(), Vector comparison operators and streaming method. Refactor IBM function. Fix some of the -Wunused-parameter warnings and implicit float conversions, including all -bugprone-narrowing-conversions. Fix -modernize-return-braced-init-list and update list of Clang-Tidy rules based on Clang 13.0.0. --- .clang-tidy | 6 +- .github/actions/build_and_check/action.yml | 2 +- CMakeLists.txt | 4 +- doc/sphinx/inter_bonded.rst | 4 +- src/core/MpiCallbacks.hpp | 4 +- src/core/Observable_stat.hpp | 6 +- src/core/RuntimeErrorCollector.cpp | 17 +-- src/core/accumulators/AccumulatorBase.hpp | 3 +- src/core/accumulators/Correlator.cpp | 22 ++-- src/core/accumulators/Correlator.hpp | 6 +- src/core/actor/Actor.hpp | 6 +- .../bonded_interactions/bonded_coulomb_sr.hpp | 2 +- .../bonded_interaction_data.hpp | 6 +- src/core/collision.cpp | 2 +- .../constraints/HomogeneousMagneticField.hpp | 2 +- src/core/constraints/ShapeBasedConstraint.cpp | 2 +- src/core/dpd.cpp | 2 +- .../electrostatics_magnetostatics/fft.cpp | 44 +++----- .../electrostatics_magnetostatics/mmm1d.cpp | 2 +- .../specfunc.cpp | 48 ++++---- src/core/energy_inline.hpp | 2 +- src/core/errorhandling.cpp | 3 +- src/core/ghosts.cpp | 18 +-- src/core/grid.cpp | 3 +- .../electrokinetics_cuda.cu | 22 ++-- src/core/grid_based_algorithms/lb.cpp | 18 +-- .../grid_based_algorithms/lb_interface.cpp | 15 +-- src/core/grid_based_algorithms/lbgpu.cpp | 64 +++++------ src/core/grid_based_algorithms/lbgpu.hpp | 5 +- src/core/grid_based_algorithms/lbgpu_cuda.cu | 11 +- .../immersed_boundary/ImmersedBoundaries.cpp | 5 +- src/core/observables/BondAngles.hpp | 2 +- src/core/observables/BondDihedrals.hpp | 2 +- src/core/observables/CosPersistenceAngles.hpp | 4 +- .../observables/CylindricalDensityProfile.hpp | 4 +- ...alLBVelocityProfileAtParticlePositions.cpp | 2 +- ...alLBVelocityProfileAtParticlePositions.hpp | 4 +- .../CylindricalVelocityProfile.hpp | 2 +- src/core/observables/ForceDensityProfile.hpp | 4 +- src/core/observables/LBProfileObservable.hpp | 6 +- .../observables/ParticleAngularVelocities.hpp | 10 +- .../ParticleBodyAngularVelocities.hpp | 15 ++- .../observables/ParticleBodyVelocities.hpp | 2 +- src/core/observables/ParticleDistances.hpp | 2 +- src/core/observables/ParticleForces.hpp | 13 ++- src/core/observables/PidObservable.hpp | 2 +- src/core/observables/TotalForce.hpp | 10 +- src/core/particle_data.cpp | 74 ++++++------ src/core/polymer.cpp | 6 +- .../reaction_methods/ReactionAlgorithm.cpp | 1 - .../reaction_methods/ReactionAlgorithm.hpp | 8 +- src/core/statistics.cpp | 10 +- src/core/unit_tests/ParticleIterator_test.cpp | 7 +- .../field_coupling_force_field_test.cpp | 41 +++---- .../virtual_sites/lb_inertialess_tracers.cpp | 106 +++++++++--------- .../lb_inertialess_tracers_cuda_interface.cpp | 2 +- .../lbboundaries/LBBoundary.hpp | 3 +- src/shapes/unit_tests/Sphere_test.cpp | 12 +- src/utils/include/utils/Array.hpp | 5 +- src/utils/include/utils/Counter.hpp | 3 +- src/utils/include/utils/math/int_pow.hpp | 2 +- .../utils/math/matrix_vector_product.hpp | 2 +- src/utils/include/utils/matrix.hpp | 5 +- src/utils/include/utils/mpi/cart_comm.hpp | 7 +- src/utils/include/utils/quaternion.hpp | 3 +- src/utils/include/utils/sampling.hpp | 26 ++--- src/utils/include/utils/tuple.hpp | 2 +- src/utils/tests/Vector_test.cpp | 24 ++-- 68 files changed, 381 insertions(+), 408 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 07b79a9504f..08ce8a230d4 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -7,6 +7,8 @@ Checks: | -clang-analyzer-optin.mpi.MPI-Checker, -clang-analyzer-security.FloatLoopCounter, bugprone-*, + -bugprone-easily-swappable-parameters, + -bugprone-implicit-widening-of-multiplication-result, clang-analyzer-alpha.*, modernize-deprecated-headers, modernize-make-shared, @@ -71,7 +73,7 @@ CheckOptions: - key: modernize-make-shared.IgnoreMacros value: '1' - key: modernize-make-shared.IncludeStyle - value: '0' + value: 'llvm' - key: modernize-make-shared.MakeSmartPtrFunction value: 'std::make_shared' - key: modernize-make-shared.MakeSmartPtrFunctionHeader @@ -79,7 +81,7 @@ CheckOptions: - key: modernize-make-unique.IgnoreMacros value: '1' - key: modernize-make-unique.IncludeStyle - value: '0' + value: 'llvm' - key: modernize-make-unique.MakeSmartPtrFunction value: 'std::make_unique' - key: modernize-make-unique.MakeSmartPtrFunctionHeader diff --git a/.github/actions/build_and_check/action.yml b/.github/actions/build_and_check/action.yml index e9ea1f140b5..2dcce230289 100644 --- a/.github/actions/build_and_check/action.yml +++ b/.github/actions/build_and_check/action.yml @@ -22,7 +22,7 @@ runs: pip3 install numpy cython h5py scipy shell: bash - run: | - export myconfig=maxset with_cuda=false test_timeout=600 with_asan=${{ inputs.asan }} with_ubsan=${{ inputs.ubsan }} check_skip_long=${{ inputs.check_skip_long }} + export myconfig=maxset with_cuda=false test_timeout=800 with_asan=${{ inputs.asan }} with_ubsan=${{ inputs.ubsan }} check_skip_long=${{ inputs.check_skip_long }} bash maintainer/CI/build_cmake.sh shell: bash # This is a workaround for the unfortunate interaction of MacOS and OpenMPI 4 diff --git a/CMakeLists.txt b/CMakeLists.txt index 47a5588cd9b..4d42624323b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,8 +130,8 @@ include(CheckCXXSourceCompiles) # cross-platform macro to print the function name in error messages set(PRETTY_FUNCTION_EXTENSION __func__) -# search for a supported compiler extension that prints the function -# name as well as its list of arguments, return type and namespace +# search for a supported compiler extension that prints the function name as +# well as its list of arguments, return type and namespace foreach(func_name __PRETTY_FUNCTION__ __FUNCSIG__ __FUNCTION__) check_cxx_source_compiles( " diff --git a/doc/sphinx/inter_bonded.rst b/doc/sphinx/inter_bonded.rst index 84e6458e32d..465fb40f046 100644 --- a/doc/sphinx/inter_bonded.rst +++ b/doc/sphinx/inter_bonded.rst @@ -231,13 +231,13 @@ A virtual bond can be instantiated via :class:`espressomd.interactions.Virtual`:: import espressomd.interactions - tab = espressomd.interactions.Virtual() + vb = espressomd.interactions.Virtual() This creates a virtual bond type identifier for a pair bond without associated potential or force. It can be used to specify topologies and for some analysis that rely on bonds, or for bonds that should be -displayed in the visualization. +displayed in the visualizer. diff --git a/src/core/MpiCallbacks.hpp b/src/core/MpiCallbacks.hpp index 7d2543c70fb..21d7fd03269 100644 --- a/src/core/MpiCallbacks.hpp +++ b/src/core/MpiCallbacks.hpp @@ -186,7 +186,7 @@ struct callback_ignore_t final : public callback_concept_t { template explicit callback_ignore_t(FRef &&f) : m_f(std::forward(f)) {} - void operator()(boost::mpi::communicator const &comm, + void operator()(boost::mpi::communicator const &, boost::mpi::packed_iarchive &ia) const override { detail::invoke(m_f, ia); } @@ -239,7 +239,7 @@ struct callback_main_rank_t final : public callback_concept_t { template explicit callback_main_rank_t(FRef &&f) : m_f(std::forward(f)) {} - void operator()(boost::mpi::communicator const &comm, + void operator()(boost::mpi::communicator const &, boost::mpi::packed_iarchive &ia) const override { (void)detail::invoke(m_f, ia); } diff --git a/src/core/Observable_stat.hpp b/src/core/Observable_stat.hpp index 5f0597824eb..125c76ce983 100644 --- a/src/core/Observable_stat.hpp +++ b/src/core/Observable_stat.hpp @@ -56,8 +56,8 @@ class Observable_stat { if (m_chunk_size == 1) return boost::accumulate(m_data, acc); - for (auto it = m_data.begin() + column; it < m_data.end(); - it += m_chunk_size) + for (auto it = m_data.begin() + static_cast(column); + it < m_data.end(); it += static_cast(m_chunk_size)) acc += *it; return acc; } @@ -88,7 +88,7 @@ class Observable_stat { /** Get contribution from a bonded interaction */ Utils::Span bonded_contribution(int bond_id) const { auto const offset = m_chunk_size * static_cast(bond_id); - return Utils::Span(bonded.data() + offset, m_chunk_size); + return {bonded.data() + offset, m_chunk_size}; } void add_non_bonded_contribution(int type1, int type2, diff --git a/src/core/RuntimeErrorCollector.cpp b/src/core/RuntimeErrorCollector.cpp index ef5f3f68088..b08a463c58c 100644 --- a/src/core/RuntimeErrorCollector.cpp +++ b/src/core/RuntimeErrorCollector.cpp @@ -30,12 +30,9 @@ #include #include -using boost::mpi::all_reduce; -using boost::mpi::communicator; - namespace ErrorHandling { -RuntimeErrorCollector::RuntimeErrorCollector(communicator comm) +RuntimeErrorCollector::RuntimeErrorCollector(boost::mpi::communicator comm) : m_comm(std::move(comm)) {} RuntimeErrorCollector::~RuntimeErrorCollector() { @@ -99,18 +96,14 @@ void RuntimeErrorCollector::error(const std::ostringstream &mstr, } int RuntimeErrorCollector::count() const { - int totalMessages; - const int numMessages = m_errors.size(); - - all_reduce(m_comm, numMessages, totalMessages, std::plus()); - - return totalMessages; + return boost::mpi::all_reduce(m_comm, static_cast(m_errors.size()), + std::plus<>()); } int RuntimeErrorCollector::count(RuntimeError::ErrorLevel level) { - return std::count_if( + return static_cast(std::count_if( m_errors.begin(), m_errors.end(), - [level](const RuntimeError &e) { return e.level() >= level; }); + [level](const RuntimeError &e) { return e.level() >= level; })); } void RuntimeErrorCollector::clear() { m_errors.clear(); } diff --git a/src/core/accumulators/AccumulatorBase.hpp b/src/core/accumulators/AccumulatorBase.hpp index 6d0bb13507e..1edebb7bd59 100644 --- a/src/core/accumulators/AccumulatorBase.hpp +++ b/src/core/accumulators/AccumulatorBase.hpp @@ -27,9 +27,10 @@ namespace Accumulators { class AccumulatorBase { public: explicit AccumulatorBase(int delta_N = 1) : m_delta_N(delta_N) {} - int &delta_N() { return m_delta_N; } virtual ~AccumulatorBase() = default; + int &delta_N() { return m_delta_N; } + virtual void update() = 0; /** Dimensions needed to reshape the flat array returned by the accumulator */ virtual std::vector shape() const = 0; diff --git a/src/core/accumulators/Correlator.cpp b/src/core/accumulators/Correlator.cpp index 1c0180eb556..326827edcfd 100644 --- a/src/core/accumulators/Correlator.cpp +++ b/src/core/accumulators/Correlator.cpp @@ -85,8 +85,8 @@ std::vector scalar_product(std::vector const &A, "Error in scalar product: The vector sizes do not match"); } - return std::vector( - 1, std::inner_product(A.begin(), A.end(), B.begin(), 0.0)); + auto const result = std::inner_product(A.begin(), A.end(), B.begin(), 0.0); + return {result}; } std::vector componentwise_product(std::vector const &A, @@ -284,7 +284,7 @@ void Correlator::initialize() { auto const n_result = n_values(); n_sweeps = std::vector(n_result, 0); - n_vals = std::vector(m_hierarchy_depth, 0); + n_vals = std::vector(m_hierarchy_depth, 0); result.resize(std::array{{n_result, m_dim_corr}}); @@ -295,7 +295,7 @@ void Correlator::initialize() { } } - newest = std::vector(m_hierarchy_depth, m_tau_lin); + newest = std::vector(m_hierarchy_depth, m_tau_lin); tau.resize(n_result); for (int i = 0; i < m_tau_lin + 1; i++) { @@ -378,7 +378,7 @@ void Correlator::update() { } // Now update the lowest level correlation estimates - for (unsigned j = 0; j < min(m_tau_lin + 1, n_vals[0]); j++) { + for (long j = 0; j < min(m_tau_lin + 1, n_vals[0]); j++) { auto const index_new = newest[0]; auto const index_old = (newest[0] - j + m_tau_lin + 1) % (m_tau_lin + 1); auto const temp = @@ -392,8 +392,8 @@ void Correlator::update() { } // Now for the higher ones for (int i = 1; i < highest_level_to_compress + 2; i++) { - for (unsigned j = (m_tau_lin + 1) / 2 + 1; - j < min(m_tau_lin + 1, n_vals[i]); j++) { + for (long j = (m_tau_lin + 1) / 2 + 1; j < min(m_tau_lin + 1, n_vals[i]); + j++) { auto const index_new = newest[i]; auto const index_old = (newest[i] - j + m_tau_lin + 1) % (m_tau_lin + 1); auto const index_res = @@ -422,9 +422,9 @@ int Correlator::finalize() { finalized = true; for (int ll = 0; ll < m_hierarchy_depth - 1; ll++) { - int vals_ll; // number of values remaining in the lowest level + long vals_ll; // number of values remaining in the lowest level if (n_vals[ll] > m_tau_lin + 1) - vals_ll = m_tau_lin + static_cast(n_vals[ll]) % 2; + vals_ll = m_tau_lin + n_vals[ll] % 2; else vals_ll = n_vals[ll]; @@ -466,8 +466,8 @@ int Correlator::finalize() { // We only need to update correlation estimates for the higher levels for (int i = ll + 1; i < highest_level_to_compress + 2; i++) { - for (int j = (m_tau_lin + 1) / 2 + 1; j < min(m_tau_lin + 1, n_vals[i]); - j++) { + for (long j = (m_tau_lin + 1) / 2 + 1; + j < min(m_tau_lin + 1, n_vals[i]); j++) { auto const index_new = newest[i]; auto const index_old = (newest[i] - j + m_tau_lin + 1) % (m_tau_lin + 1); diff --git a/src/core/accumulators/Correlator.hpp b/src/core/accumulators/Correlator.hpp index c5b95350c0b..c69aeed74fd 100644 --- a/src/core/accumulators/Correlator.hpp +++ b/src/core/accumulators/Correlator.hpp @@ -195,7 +195,7 @@ class Correlator : public AccumulatorBase { return shape; } std::vector get_samples_sizes() const { - return std::vector(n_sweeps.begin(), n_sweeps.end()); + return {n_sweeps.begin(), n_sweeps.end()}; } std::vector get_lag_times() const; @@ -250,9 +250,9 @@ class Correlator : public AccumulatorBase { /// number of correlation sweeps at a particular value of tau std::vector n_sweeps; /// number of data values already present at a particular value of tau - std::vector n_vals; + std::vector n_vals; /// index of the newest entry in each hierarchy level - std::vector newest; + std::vector newest; std::vector A_accumulated_average; ///< all A values are added up here std::vector B_accumulated_average; ///< all B values are added up here diff --git a/src/core/actor/Actor.hpp b/src/core/actor/Actor.hpp index 1b740b69a5d..e866eec957c 100644 --- a/src/core/actor/Actor.hpp +++ b/src/core/actor/Actor.hpp @@ -27,9 +27,9 @@ */ class Actor { public: - virtual void computeForces(SystemInterface &s) {} - virtual void computeTorques(SystemInterface &s) {} - virtual void computeEnergy(SystemInterface &s) {} + virtual void computeForces(SystemInterface &) {} + virtual void computeTorques(SystemInterface &) {} + virtual void computeEnergy(SystemInterface &) {} virtual ~Actor() = default; }; diff --git a/src/core/bonded_interactions/bonded_coulomb_sr.hpp b/src/core/bonded_interactions/bonded_coulomb_sr.hpp index 9d9fc788fa2..ef891794727 100644 --- a/src/core/bonded_interactions/bonded_coulomb_sr.hpp +++ b/src/core/bonded_interactions/bonded_coulomb_sr.hpp @@ -85,7 +85,7 @@ BondedCoulombSR::energy(Particle const &p1, Particle const &p2, auto const dist = dx.norm(); return Coulomb::pair_energy(p1, p2, q1q2, dx, dist); #else - return .0; + return 0.; #endif } diff --git a/src/core/bonded_interactions/bonded_interaction_data.hpp b/src/core/bonded_interactions/bonded_interaction_data.hpp index bd81d69077d..da4d51679d4 100644 --- a/src/core/bonded_interactions/bonded_interaction_data.hpp +++ b/src/core/bonded_interactions/bonded_interaction_data.hpp @@ -67,8 +67,7 @@ struct NoneBond { private: friend boost::serialization::access; - template - void serialize(Archive &ar, long int /* version */) {} + template void serialize(Archive &, long int) {} }; /** Interaction type for virtual bonds */ @@ -78,8 +77,7 @@ struct VirtualBond { private: friend boost::serialization::access; - template - void serialize(Archive &ar, long int /* version */) {} + template void serialize(Archive &, long int) {} }; /** Visitor to get the number of bound partners from the bond parameter diff --git a/src/core/collision.cpp b/src/core/collision.cpp index 8692a5a1f18..ea8c87c8604 100644 --- a/src/core/collision.cpp +++ b/src/core/collision.cpp @@ -412,7 +412,7 @@ void bind_at_poc_create_bond_between_vs(const int current_vs_pid, void glue_to_surface_bind_part_to_vs(const Particle *const p1, const Particle *const p2, const int vs_pid_plus_one, - const CollisionPair &c) { + const CollisionPair &) { // Create bond between the virtual particles const int bondG[] = {vs_pid_plus_one - 1}; diff --git a/src/core/constraints/HomogeneousMagneticField.hpp b/src/core/constraints/HomogeneousMagneticField.hpp index 6f34ad40f24..1f92552b159 100644 --- a/src/core/constraints/HomogeneousMagneticField.hpp +++ b/src/core/constraints/HomogeneousMagneticField.hpp @@ -41,7 +41,7 @@ class HomogeneousMagneticField : public Constraint { ParticleForce force(const Particle &p, const Utils::Vector3d &, double) override; - bool fits_in_box(Utils::Vector3d const &box) const override { return true; } + bool fits_in_box(Utils::Vector3d const &) const override { return true; } private: Utils::Vector3d m_field; diff --git a/src/core/constraints/ShapeBasedConstraint.cpp b/src/core/constraints/ShapeBasedConstraint.cpp index a6ff9ccb553..f253b86a0ec 100644 --- a/src/core/constraints/ShapeBasedConstraint.cpp +++ b/src/core/constraints/ShapeBasedConstraint.cpp @@ -73,7 +73,7 @@ double ShapeBasedConstraint::min_dist(const ParticleRange &particles) { ParticleForce ShapeBasedConstraint::force(Particle const &p, Utils::Vector3d const &folded_pos, - double t) { + double) { ParticleForce pf{}; IA_parameters const &ia_params = *get_ia_param(p.p.type, part_rep.p.type); diff --git a/src/core/dpd.cpp b/src/core/dpd.cpp index d33a79b7399..5af8296fddd 100644 --- a/src/core/dpd.cpp +++ b/src/core/dpd.cpp @@ -58,7 +58,7 @@ using Utils::Vector3d; * 3. Two particle IDs (order-independent, decorrelates particles, gets rid of * seed-per-node) */ -Vector3d dpd_noise(uint32_t pid1, uint32_t pid2) { +Vector3d dpd_noise(int pid1, int pid2) { return Random::noise_uniform( dpd.rng_counter(), dpd.rng_seed(), (pid1 < pid2) ? pid2 : pid1, (pid1 < pid2) ? pid1 : pid2); diff --git a/src/core/electrostatics_magnetostatics/fft.cpp b/src/core/electrostatics_magnetostatics/fft.cpp index 0e0a369ae4a..7b47ba9b9f1 100644 --- a/src/core/electrostatics_magnetostatics/fft.cpp +++ b/src/core/electrostatics_magnetostatics/fft.cpp @@ -421,15 +421,12 @@ void back_grid_comm(fft_forw_plan plan_f, fft_back_plan plan_b, * that they are multiples of the 3D grid dimensions. * \param g3d 3D grid. * \param g2d 2D grid. - * \param mult factors between 3D and 2D grid dimensions * \return index of the row direction [0,1,2]. */ -int map_3don2d_grid(int const g3d[3], int g2d[3], int mult[3]) { +int map_3don2d_grid(int const g3d[3], int g2d[3]) { int row_dir = -1; /* trivial case */ if (g3d[2] == 1) { - for (int i = 0; i < 3; i++) - mult[i] = 1; return 2; } if (g2d[0] % g3d[0] == 0) { @@ -464,8 +461,6 @@ int map_3don2d_grid(int const g3d[3], int g2d[3], int mult[3]) { g2d[0] = 1; } } - for (int i = 0; i < 3; i++) - mult[i] = g2d[i] / g3d[i]; return row_dir; } @@ -489,9 +484,6 @@ int fft_init(const Utils::Vector3i &ca_mesh_dim, int const *ca_mesh_margin, int const *global_mesh_dim, double const *global_mesh_off, int &ks_pnum, fft_data_struct &fft, const Utils::Vector3i &grid, const boost::mpi::communicator &comm) { - int i, j; - /* helpers */ - int mult[3]; int n_grid[4][3]; /* The four node grids. */ int my_pos[4][3]; /* The position of comm.rank() in the node grids. */ @@ -503,18 +495,18 @@ int fft_init(const Utils::Vector3i &ca_mesh_dim, int const *ca_mesh_margin, fft.max_comm_size = 0; fft.max_mesh_size = 0; - for (i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) { n_id[i].resize(1 * comm.size()); n_pos[i].resize(3 * comm.size()); } /* === node grids === */ /* real space node grid (n_grid[0]) */ - for (i = 0; i < 3; i++) { + for (int i = 0; i < 3; i++) { n_grid[0][i] = grid[i]; my_pos[0][i] = node_pos[i]; } - for (i = 0; i < comm.size(); i++) { + for (int i = 0; i < comm.size(); i++) { MPI_Cart_coords(comm, i, 3, &(n_pos[0][3 * i + 0])); auto const lin_ind = get_linear_index( n_pos[0][3 * i + 0], n_pos[0][3 * i + 1], n_pos[0][3 * i + 2], @@ -525,11 +517,11 @@ int fft_init(const Utils::Vector3i &ca_mesh_dim, int const *ca_mesh_margin, /* FFT node grids (n_grid[1 - 3]) */ calc_2d_grid(comm.size(), n_grid[1]); /* resort n_grid[1] dimensions if necessary */ - fft.plan[1].row_dir = map_3don2d_grid(n_grid[0], n_grid[1], mult); + fft.plan[1].row_dir = map_3don2d_grid(n_grid[0], n_grid[1]); fft.plan[0].n_permute = 0; - for (i = 1; i < 4; i++) + for (int i = 1; i < 4; i++) fft.plan[i].n_permute = (fft.plan[1].row_dir + i) % 3; - for (i = 0; i < 3; i++) { + for (int i = 0; i < 3; i++) { n_grid[2][i] = n_grid[1][(i + 1) % 3]; n_grid[3][i] = n_grid[1][(i + 2) % 3]; } @@ -538,10 +530,10 @@ int fft_init(const Utils::Vector3i &ca_mesh_dim, int const *ca_mesh_margin, /* === communication groups === */ /* copy local mesh off real space charge assignment grid */ - for (i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) fft.plan[0].new_mesh[i] = ca_mesh_dim[i]; - for (i = 1; i < 4; i++) { + for (int i = 1; i < 4; i++) { using Utils::make_span; auto group = find_comm_groups( {n_grid[i - 1][0], n_grid[i - 1][1], n_grid[i - 1][2]}, @@ -577,7 +569,7 @@ int fft_init(const Utils::Vector3i &ca_mesh_dim, int const *ca_mesh_margin, fft.plan[i].n_ffts = fft.plan[i].new_mesh[0] * fft.plan[i].new_mesh[1]; /* === send/recv block specifications === */ - for (j = 0; j < fft.plan[i].group.size(); j++) { + for (int j = 0; j < fft.plan[i].group.size(); j++) { /* send block: comm.rank() to comm-group-node i (identity: node) */ int node = fft.plan[i].group[j]; fft.plan[i].send_size[j] = calc_send_block( @@ -608,13 +600,13 @@ int fft_init(const Utils::Vector3i &ca_mesh_dim, int const *ca_mesh_margin, fft.max_comm_size = fft.plan[i].recv_size[j]; } - for (j = 0; j < 3; j++) + for (int j = 0; j < 3; j++) fft.plan[i].old_mesh[j] = fft.plan[i - 1].new_mesh[j]; - if (i == 1) + if (i == 1) { fft.plan[i].element = 1; - else { + } else { fft.plan[i].element = 2; - for (j = 0; j < fft.plan[i].group.size(); j++) { + for (int j = 0; j < fft.plan[i].group.size(); j++) { fft.plan[i].send_size[j] *= 2; fft.plan[i].recv_size[j] *= 2; } @@ -624,12 +616,12 @@ int fft_init(const Utils::Vector3i &ca_mesh_dim, int const *ca_mesh_margin, /* Factor 2 for complex fields */ fft.max_comm_size *= 2; fft.max_mesh_size = Utils::product(ca_mesh_dim); - for (i = 1; i < 4; i++) + for (int i = 1; i < 4; i++) if (2 * fft.plan[i].new_size > fft.max_mesh_size) fft.max_mesh_size = 2 * fft.plan[i].new_size; /* === pack function === */ - for (i = 1; i < 4; i++) { + for (int i = 1; i < 4; i++) { fft.plan[i].pack_function = pack_block_permute2; } ks_pnum = 6; @@ -647,7 +639,7 @@ int fft_init(const Utils::Vector3i &ca_mesh_dim, int const *ca_mesh_margin, auto *c_data = (fftw_complex *)(fft.data_buf.data()); /* === FFT Routines (Using FFTW / RFFTW package)=== */ - for (i = 1; i < 4; i++) { + for (int i = 1; i < 4; i++) { fft.plan[i].dir = FFTW_FORWARD; /* FFT plan creation.*/ @@ -661,7 +653,7 @@ int fft_init(const Utils::Vector3i &ca_mesh_dim, int const *ca_mesh_margin, /* === The BACK Direction === */ /* this is needed because slightly different functions are used */ - for (i = 1; i < 4; i++) { + for (int i = 1; i < 4; i++) { fft.back[i].dir = FFTW_BACKWARD; if (fft.init_tag) diff --git a/src/core/electrostatics_magnetostatics/mmm1d.cpp b/src/core/electrostatics_magnetostatics/mmm1d.cpp index 0b0ff5e9a6f..2bbcf41cdd9 100644 --- a/src/core/electrostatics_magnetostatics/mmm1d.cpp +++ b/src/core/electrostatics_magnetostatics/mmm1d.cpp @@ -270,7 +270,7 @@ void add_mmm1d_coulomb_pair_force(double chpref, Utils::Vector3d const &d, double mmm1d_coulomb_pair_energy(double const chpref, Utils::Vector3d const &d, double r) { if (chpref == 0) - return 0; + return 0.; constexpr auto c_2pi = 2 * Utils::pi(); auto const n_modPsi = static_cast(modPsi.size() >> 1); diff --git a/src/core/electrostatics_magnetostatics/specfunc.cpp b/src/core/electrostatics_magnetostatics/specfunc.cpp index 027e820a6cd..496fbc32eeb 100644 --- a/src/core/electrostatics_magnetostatics/specfunc.cpp +++ b/src/core/electrostatics_magnetostatics/specfunc.cpp @@ -247,25 +247,25 @@ double hzeta(double s, double q) { } double K0(double x) { - double c, I0; if (x <= 2.0) { - c = evaluateAsChebychevSeriesAt(bk0_cs, 0.5 * x * x - 1.0); - I0 = evaluateAsChebychevSeriesAt(bi0_cs, x * x / 4.5 - 1.0); - return (-log(x) + Utils::ln_2()) * I0 + c; + auto const c = evaluateAsChebychevSeriesAt(bk0_cs, 0.5 * x * x - 1.0); + auto const i0 = evaluateAsChebychevSeriesAt(bi0_cs, x * x / 4.5 - 1.0); + return (-log(x) + Utils::ln_2()) * i0 + c; } - c = (x <= 8.0) ? evaluateAsChebychevSeriesAt(ak0_cs, (16.0 / x - 5.0) / 3.0) + auto const c = + (x <= 8.0) ? evaluateAsChebychevSeriesAt(ak0_cs, (16.0 / x - 5.0) / 3.0) : evaluateAsChebychevSeriesAt(ak02_cs, 16.0 / x - 1.0); return exp(-x) * c / sqrt(x); } double K1(double x) { - double c, I1; if (x <= 2.0) { - c = evaluateAsChebychevSeriesAt(bk1_cs, 0.5 * x * x - 1.0); - I1 = x * evaluateAsChebychevSeriesAt(bi1_cs, x * x / 4.5 - 1.0); - return (log(x) - Utils::ln_2()) * I1 + c / x; + auto const c = evaluateAsChebychevSeriesAt(bk1_cs, 0.5 * x * x - 1.0); + auto const i1 = x * evaluateAsChebychevSeriesAt(bi1_cs, x * x / 4.5 - 1.0); + return (log(x) - Utils::ln_2()) * i1 + c / x; } - c = (x <= 8.0) ? evaluateAsChebychevSeriesAt(ak1_cs, (16.0 / x - 5.0) / 3.0) + auto const c = + (x <= 8.0) ? evaluateAsChebychevSeriesAt(ak1_cs, (16.0 / x - 5.0) / 3.0) : evaluateAsChebychevSeriesAt(ak12_cs, 16.0 / x - 1.0); return exp(-x) * c / sqrt(x); } @@ -405,15 +405,15 @@ double LPK1(double x) { std::pair LPK01(double x) { if (x >= 27.) { auto const tmp = .5 * exp(-x) / sqrt(x); - auto const K0 = tmp * ak0_cs[0]; - auto const K1 = tmp * ak1_cs[0]; - return {K0, K1}; + auto const k0 = tmp * ak0_cs[0]; + auto const k1 = tmp * ak1_cs[0]; + return {k0, k1}; } if (x >= 23.) { auto const tmp = exp(-x) / sqrt(x), xx = (16. / 3.) / x - 5. / 3.; - auto const K0 = tmp * (xx * ak0_cs[1] + 0.5 * ak0_cs[0]); - auto const K1 = tmp * (xx * ak1_cs[1] + 0.5 * ak1_cs[0]); - return {K0, K1}; + auto const k0 = tmp * (xx * ak0_cs[1] + 0.5 * ak0_cs[0]); + auto const k1 = tmp * (xx * ak1_cs[1] + 0.5 * ak1_cs[0]); + return {k0, k1}; } if (x > 2) { int j = ak01_orders[((int)x) - 2]; @@ -440,9 +440,9 @@ std::pair LPK01(double x) { dd1 = tmp1; } auto const tmp = exp(-x) / sqrt(x); - auto const K0 = tmp * (0.5 * (s0[0] + x2 * d0) - dd0); - auto const K1 = tmp * (0.5 * (s1[0] + x2 * d1) - dd1); - return {K0, K1}; + auto const k0 = tmp * (0.5 * (s0[0] + x2 * d0) - dd0); + auto const k1 = tmp * (0.5 * (s1[0] + x2 * d1) - dd1); + return {k0, k1}; } /* x <= 2 */ { @@ -461,8 +461,8 @@ std::pair LPK01(double x) { dd1 = tmp1; } auto const tmp = log(x) - Utils::ln_2(); - auto K0 = -tmp * (0.5 * (bi0_cs[0] + x2 * d0) - dd0); - auto K1 = x * tmp * (0.5 * (bi1_cs[0] + x2 * d1) - dd1); + auto k0 = -tmp * (0.5 * (bi0_cs[0] + x2 * d0) - dd0); + auto k1 = x * tmp * (0.5 * (bi1_cs[0] + x2 * d1) - dd1); /* K0/K1 correction */ j = 9; @@ -478,8 +478,8 @@ std::pair LPK01(double x) { dd0 = tmp0; dd1 = tmp1; } - K0 += (0.5 * (x2 * d0 + bk0_cs[0]) - dd0); - K1 += (0.5 * (x2 * d1 + bk1_cs[0]) - dd1) / x; - return {K0, K1}; + k0 += (0.5 * (x2 * d0 + bk0_cs[0]) - dd0); + k1 += (0.5 * (x2 * d1 + bk1_cs[0]) - dd1) / x; + return {k0, k1}; } } diff --git a/src/core/energy_inline.hpp b/src/core/energy_inline.hpp index 6b76be94b3b..80e230135ef 100644 --- a/src/core/energy_inline.hpp +++ b/src/core/energy_inline.hpp @@ -204,7 +204,7 @@ inline void add_non_bonded_pair_energy(Particle const &p1, Particle const &p2, inline boost::optional calc_bonded_energy(Bonded_IA_Parameters const &iaparams, Particle const &p1, Utils::Span partners) { - auto const n_partners = partners.size(); + auto const n_partners = static_cast(partners.size()); auto p2 = (n_partners > 0) ? partners[0] : nullptr; auto p3 = (n_partners > 1) ? partners[1] : nullptr; diff --git a/src/core/errorhandling.cpp b/src/core/errorhandling.cpp index a96f3cdc8a3..edd346882d5 100644 --- a/src/core/errorhandling.cpp +++ b/src/core/errorhandling.cpp @@ -59,8 +59,7 @@ RuntimeErrorStream _runtimeMessageStream(RuntimeError::ErrorLevel level, const std::string &file, const int line, const std::string &function) { - return RuntimeErrorStream(*runtimeErrorCollector, level, file, line, - function); + return {*runtimeErrorCollector, level, file, line, function}; } void mpi_gather_runtime_errors_local() { diff --git a/src/core/ghosts.cpp b/src/core/ghosts.cpp index 626b74b2184..ae32e4dbaec 100644 --- a/src/core/ghosts.cpp +++ b/src/core/ghosts.cpp @@ -132,7 +132,7 @@ static void prepare_send_buffer(CommBuf &send_buffer, /* put in data */ for (auto part_list : ghost_comm.part_lists) { if (data_parts & GHOSTTRANS_PARTNUM) { - int np = part_list->size(); + int np = static_cast(part_list->size()); archiver << np; } else { for (Particle &part : *part_list) { @@ -276,7 +276,7 @@ static void cell_cell_transfer(const GhostCommunication &ghost_comm, auto *dst_list = ghost_comm.part_lists[pl + offset]; if (data_parts & GHOSTTRANS_PARTNUM) { - prepare_ghost_cell(dst_list, src_list->size()); + prepare_ghost_cell(dst_list, static_cast(src_list->size())); } else { auto const &src_part = *src_list; auto &dst_part = *dst_list; @@ -390,21 +390,23 @@ void ghost_communicator(const GhostCommunicator &gcr, unsigned int data_parts) { // (which consists of already serialized data). switch (comm_type) { case GHOST_RECV: - comm.recv(node, REQ_GHOST_SEND, recv_buffer.data(), recv_buffer.size()); + comm.recv(node, REQ_GHOST_SEND, recv_buffer.data(), + static_cast(recv_buffer.size())); comm.recv(node, REQ_GHOST_SEND, recv_buffer.bonds()); break; case GHOST_SEND: - comm.send(node, REQ_GHOST_SEND, send_buffer.data(), send_buffer.size()); + comm.send(node, REQ_GHOST_SEND, send_buffer.data(), + static_cast(send_buffer.size())); comm.send(node, REQ_GHOST_SEND, send_buffer.bonds()); break; case GHOST_BCST: if (node == comm.rank()) { - boost::mpi::broadcast(comm, send_buffer.data(), send_buffer.size(), - node); + boost::mpi::broadcast(comm, send_buffer.data(), + static_cast(send_buffer.size()), node); boost::mpi::broadcast(comm, send_buffer.bonds(), node); } else { - boost::mpi::broadcast(comm, recv_buffer.data(), recv_buffer.size(), - node); + boost::mpi::broadcast(comm, recv_buffer.data(), + static_cast(recv_buffer.size()), node); boost::mpi::broadcast(comm, recv_buffer.bonds(), node); } break; diff --git a/src/core/grid.cpp b/src/core/grid.cpp index 74e60db1a82..88b374299a0 100644 --- a/src/core/grid.cpp +++ b/src/core/grid.cpp @@ -38,6 +38,7 @@ #include #include +#include BoxGeometry box_geo; LocalBox local_geo; @@ -79,7 +80,7 @@ LocalBox regular_decomposition(const BoxGeometry &box, } Utils::Array boundaries; - for (int dir = 0; dir < 3; dir++) { + for (std::size_t dir = 0; dir < 3; dir++) { /* left boundary ? */ boundaries[2 * dir] = (node_pos[dir] == 0); /* right boundary ? */ diff --git a/src/core/grid_based_algorithms/electrokinetics_cuda.cu b/src/core/grid_based_algorithms/electrokinetics_cuda.cu index a254f9ea455..20515f2fda5 100644 --- a/src/core/grid_based_algorithms/electrokinetics_cuda.cu +++ b/src/core/grid_based_algorithms/electrokinetics_cuda.cu @@ -160,19 +160,19 @@ EKParameters ek_parameters = { nullptr, #endif // rho - {nullptr}, + {}, // species_index {-1}, // density - {0.0}, + {}, // D - {0.0}, + {}, // d - {0.0}, + {}, // valency - {0.0}, + {}, // ext_force_density - {0.0}, + {}, // node_is_catalyst nullptr, }; @@ -2250,9 +2250,9 @@ int ek_init() { ek_parameters.bulk_viscosity * time_step / Utils::sqr(lbpar_gpu.agrid); lbpar_gpu.external_force_density = - ek_parameters.lb_ext_force_density[0] != 0 || - ek_parameters.lb_ext_force_density[1] != 0 || - ek_parameters.lb_ext_force_density[2] != 0; + ek_parameters.lb_ext_force_density[0] != 0.f || + ek_parameters.lb_ext_force_density[1] != 0.f || + ek_parameters.lb_ext_force_density[2] != 0.f; lbpar_gpu.ext_force_density = Utils::Vector3f(ek_parameters.lb_ext_force_density) * Utils::sqr(lbpar_gpu.agrid * time_step); @@ -3616,8 +3616,8 @@ void ek_print_lbpar() { printf(" unsigned int dim_y = %d;\n", lbpar_gpu.dim[1]); printf(" unsigned int dim_z = %d;\n", lbpar_gpu.dim[2]); printf(" unsigned int number_of_nodes = %d;\n", lbpar_gpu.number_of_nodes); - printf(" int external_force_density = %d;\n", - lbpar_gpu.external_force_density); + printf(" bool external_force_density = %d;\n", + static_cast(lbpar_gpu.external_force_density)); printf(" float ext_force_density[3] = {%f, %f, %f};\n", lbpar_gpu.ext_force_density[0], lbpar_gpu.ext_force_density[1], lbpar_gpu.ext_force_density[2]); diff --git a/src/core/grid_based_algorithms/lb.cpp b/src/core/grid_based_algorithms/lb.cpp index 75c783a92a7..0d38711c9de 100644 --- a/src/core/grid_based_algorithms/lb.cpp +++ b/src/core/grid_based_algorithms/lb.cpp @@ -47,12 +47,14 @@ #include #include +#include #include #include #include -#include #include +#include + #include #include #include @@ -1329,21 +1331,21 @@ void lb_calc_fluid_momentum(double *result, const LB_Parameters &lb_parameters, } momentum *= lb_parameters.agrid / lb_parameters.tau; - MPI_Reduce(momentum.data(), result, 3, MPI_DOUBLE, MPI_SUM, 0, comm_cart); + boost::mpi::reduce(comm_cart, momentum.data(), 3, result, std::plus<>(), 0); } void lb_collect_boundary_forces(double *result) { #ifdef LB_BOUNDARIES - int n_lb_boundaries = LBBoundaries::lbboundaries.size(); - std::vector boundary_forces(3 * n_lb_boundaries); - int i = 0; + auto const lbb_data_len = 3 * LBBoundaries::lbboundaries.size(); + std::vector boundary_forces(lbb_data_len); + std::size_t i = 0; for (auto it = LBBoundaries::lbboundaries.begin(); it != LBBoundaries::lbboundaries.end(); ++it, i++) - for (int j = 0; j < 3; j++) + for (std::size_t j = 0; j < 3; j++) boundary_forces[3 * i + j] = (**it).force()[j]; - MPI_Reduce(boundary_forces.data(), result, 3 * n_lb_boundaries, MPI_DOUBLE, - MPI_SUM, 0, comm_cart); + boost::mpi::reduce(comm_cart, boundary_forces.data(), + static_cast(lbb_data_len), result, std::plus<>(), 0); #endif } diff --git a/src/core/grid_based_algorithms/lb_interface.cpp b/src/core/grid_based_algorithms/lb_interface.cpp index cd460c1a28f..f0ec5f994dc 100644 --- a/src/core/grid_based_algorithms/lb_interface.cpp +++ b/src/core/grid_based_algorithms/lb_interface.cpp @@ -45,6 +45,8 @@ ActiveLB lattice_switch = ActiveLB::NONE; +ActiveLB lb_lbfluid_get_lattice_switch() { return lattice_switch; } + struct NoLBActive : public std::exception { const char *what() const noexcept override { return "LB not activated"; } }; @@ -352,12 +354,9 @@ void lb_lbfluid_set_ext_force_density(const Utils::Vector3d &force_density) { lbpar_gpu.ext_force_density[0] = static_cast(force_density[0]); lbpar_gpu.ext_force_density[1] = static_cast(force_density[1]); lbpar_gpu.ext_force_density[2] = static_cast(force_density[2]); - if (force_density[0] != 0 || force_density[1] != 0 || - force_density[2] != 0) { - lbpar_gpu.external_force_density = 1; - } else { - lbpar_gpu.external_force_density = 0; - } + lbpar_gpu.external_force_density = force_density[0] != 0. || + force_density[1] != 0. || + force_density[2] != 0.; lb_reinit_extern_nodeforce_GPU(&lbpar_gpu); #endif // CUDA @@ -408,7 +407,7 @@ void check_tau_time_step_consistency(double tau, double time_step) { auto const factor = tau / time_step; if (fabs(round(factor) - factor) / factor > eps) throw std::invalid_argument("LB tau (" + std::to_string(tau) + - ") must be integer multiple of " + ") must be an integer multiple of the " "MD time_step (" + std::to_string(time_step) + "). Factor is " + std::to_string(factor)); @@ -1100,8 +1099,6 @@ void lb_lbnode_set_pop(const Utils::Vector3i &ind, } } -ActiveLB lb_lbfluid_get_lattice_switch() { return lattice_switch; } - static void mpi_lb_lbfluid_calc_fluid_momentum_local() { lb_calc_fluid_momentum(nullptr, lbpar, lbfields, lblattice); } diff --git a/src/core/grid_based_algorithms/lbgpu.cpp b/src/core/grid_based_algorithms/lbgpu.cpp index 326dc7ad9e6..02eeb840e0a 100644 --- a/src/core/grid_based_algorithms/lbgpu.cpp +++ b/src/core/grid_based_algorithms/lbgpu.cpp @@ -43,41 +43,41 @@ LB_parameters_gpu lbpar_gpu = { // rho - 0.0, + 0.f, // mu - 0.0, + 0.f, // viscosity - 0.0, + 0.f, // gamma_shear - 0.0, + 0.f, // gamma_bulk - 0.0, + 0.f, // gamma_odd - 0.0, + 0.f, // gamma_even - 0.0, + 0.f, // is_TRT false, // bulk_viscosity - -1.0, + -1.f, // agrid - -1.0, + -1.f, // tau - -1.0, + -1.f, // dim - {{{0, 0, 0}}}, + {{{0u, 0u, 0u}}}, // number_of_nodes - 0, + 0u, #ifdef LB_BOUNDARIES_GPU // number_of_boundnodes - 0, + 0u, #endif - // external_force - 0, - // ext_force - {{{0.0, 0.0, 0.0}}}, + // external_force_density + false, + // ext_force_density + {{{0.f, 0.f, 0.f}}}, // Thermal energy - 0.0}; + 0.f}; /** this is the array that stores the hydrodynamic fields for the output */ std::vector host_values(0); @@ -86,9 +86,8 @@ bool ek_initialized = false; /** (Re-)initialize the fluid according to the given value of rho. */ void lb_reinit_fluid_gpu() { - lb_reinit_parameters_gpu(); - if (lbpar_gpu.number_of_nodes != 0) { + if (lbpar_gpu.number_of_nodes != 0u) { lb_reinit_GPU(&lbpar_gpu); lb_reinit_extern_nodeforce_GPU(&lbpar_gpu); } @@ -98,15 +97,15 @@ void lb_reinit_fluid_gpu() { * See @cite dunweg07a and @cite dhumieres09a. */ void lb_reinit_parameters_gpu() { - lbpar_gpu.mu = 0.0; + lbpar_gpu.mu = 0.f; - if (lbpar_gpu.viscosity > 0.0 && lbpar_gpu.agrid > 0.0 && - lbpar_gpu.tau > 0.0) { + if (lbpar_gpu.viscosity > 0.f && lbpar_gpu.agrid > 0.f && + lbpar_gpu.tau > 0.f) { /* Eq. (80) @cite dunweg07a. */ lbpar_gpu.gamma_shear = 1.f - 2.f / (6.f * lbpar_gpu.viscosity + 1.f); } - if (lbpar_gpu.bulk_viscosity > 0.0) { + if (lbpar_gpu.bulk_viscosity > 0.f) { /* Eq. (81) @cite dunweg07a. */ lbpar_gpu.gamma_bulk = 1.f - 2.f / (9.f * lbpar_gpu.bulk_viscosity + 1.f); } @@ -124,10 +123,10 @@ void lb_reinit_parameters_gpu() { lbpar_gpu.gamma_bulk = lbpar_gpu.gamma_shear; lbpar_gpu.gamma_even = lbpar_gpu.gamma_shear; lbpar_gpu.gamma_odd = - -(7.0f * lbpar_gpu.gamma_even + 1.0f) / (lbpar_gpu.gamma_even + 7.0f); + -(7.f * lbpar_gpu.gamma_even + 1.f) / (lbpar_gpu.gamma_even + 7.f); } - if (lbpar_gpu.kT > 0.0) { /* fluctuating hydrodynamics ? */ + if (lbpar_gpu.kT > 0.f) { /* fluctuating hydrodynamics ? */ /* Eq. (51) @cite dunweg07a.*/ /* Note that the modes are not normalized as in the paper here! */ @@ -161,16 +160,16 @@ void lb_init_gpu() { void lb_GPU_sanity_checks() { if (this_node == 0) { - if (lbpar_gpu.agrid < 0.0) { + if (lbpar_gpu.agrid < 0.f) { runtimeErrorMsg() << "Lattice Boltzmann agrid not set"; } - if (lbpar_gpu.tau < 0.0) { + if (lbpar_gpu.tau < 0.f) { runtimeErrorMsg() << "Lattice Boltzmann time step not set"; } - if (lbpar_gpu.rho < 0.0) { + if (lbpar_gpu.rho < 0.f) { runtimeErrorMsg() << "Lattice Boltzmann fluid density not set"; } - if (lbpar_gpu.viscosity < 0.0) { + if (lbpar_gpu.viscosity < 0.f) { runtimeErrorMsg() << "Lattice Boltzmann fluid viscosity not set"; } } @@ -197,10 +196,9 @@ void lb_set_agrid_gpu(double agrid) { return std::abs(d) < std::numeric_limits::epsilon(); }); if (not commensurable) { - runtimeErrorMsg() << "Lattice spacing agrid= " << agrid + runtimeErrorMsg() << "Lattice spacing agrid=" << agrid << " is incompatible with one of the box dimensions: " - << box_geo.length()[0] << " " << box_geo.length()[1] - << " " << box_geo.length()[2]; + << "[" << box_geo.length() << "]"; } lbpar_gpu.number_of_nodes = std::accumulate(lbpar_gpu.dim.begin(), lbpar_gpu.dim.end(), 1u, diff --git a/src/core/grid_based_algorithms/lbgpu.hpp b/src/core/grid_based_algorithms/lbgpu.hpp index d50b11603e2..aabcdace3d7 100644 --- a/src/core/grid_based_algorithms/lbgpu.hpp +++ b/src/core/grid_based_algorithms/lbgpu.hpp @@ -55,8 +55,9 @@ struct LB_parameters_gpu { float gamma_shear; /** relaxation rate of bulk modes */ float gamma_bulk; - /** */ + /** relaxation rate of odd modes */ float gamma_odd; + /** relaxation rate of even modes */ float gamma_even; /** flag determining whether gamma_shear, gamma_odd, and gamma_even are * calculated from gamma_shear in such a way to yield a TRT LB with minimized @@ -81,7 +82,7 @@ struct LB_parameters_gpu { unsigned int number_of_boundnodes; #endif - int external_force_density; + bool external_force_density; Utils::Array ext_force_density; diff --git a/src/core/grid_based_algorithms/lbgpu_cuda.cu b/src/core/grid_based_algorithms/lbgpu_cuda.cu index b1b95fca77d..33457b5acdf 100644 --- a/src/core/grid_based_algorithms/lbgpu_cuda.cu +++ b/src/core/grid_based_algorithms/lbgpu_cuda.cu @@ -2221,7 +2221,7 @@ void lb_calc_particle_lattice_ia_gpu(bool couple_virtual, double friction, dim3 dim_grid = calculate_dim_grid( static_cast(device_particles.size()), 4, threads_per_block); - if (lbpar_gpu.kT > 0.0) { + if (lbpar_gpu.kT > 0.f) { assert(rng_counter_coupling_gpu); KERNELCALL(calc_fluid_particle_ia, dim_grid, threads_per_block, *current_nodes, device_particles, @@ -2343,9 +2343,10 @@ void lb_calc_fluid_momentum_GPU(double *host_mom) { cudaMemcpyDeviceToHost)); cudaFree(tot_momentum); - host_mom[0] = (double)(host_momentum[0] * lbpar_gpu.agrid / lbpar_gpu.tau); - host_mom[1] = (double)(host_momentum[1] * lbpar_gpu.agrid / lbpar_gpu.tau); - host_mom[2] = (double)(host_momentum[2] * lbpar_gpu.agrid / lbpar_gpu.tau); + auto const lattice_speed = lbpar_gpu.agrid / lbpar_gpu.tau; + host_mom[0] = static_cast(host_momentum[0] * lattice_speed); + host_mom[1] = static_cast(host_momentum[1] * lattice_speed); + host_mom[2] = static_cast(host_momentum[2] * lattice_speed); } /** Setup and call kernel for getting macroscopic fluid values of all nodes @@ -2505,7 +2506,7 @@ struct lb_lbfluid_mass_of_particle { #ifdef MASS return particle.mass; #else - return 1.; + return 1.f; #endif } }; diff --git a/src/core/immersed_boundary/ImmersedBoundaries.cpp b/src/core/immersed_boundary/ImmersedBoundaries.cpp index 930b74193f0..27c1eaec9a1 100644 --- a/src/core/immersed_boundary/ImmersedBoundaries.cpp +++ b/src/core/immersed_boundary/ImmersedBoundaries.cpp @@ -80,9 +80,8 @@ void ImmersedBoundaries::init_volume_conservation(CellStructure &cs) { } static const IBMVolCons *vol_cons_parameters(Particle const &p1) { - auto it = boost::find_if(p1.bonds(), [](auto const &bond) { - return boost::get(bonded_ia_params.at(bond.bond_id()).get()) != - nullptr; + auto const it = boost::find_if(p1.bonds(), [](auto const &bond) -> bool { + return boost::get(bonded_ia_params.at(bond.bond_id()).get()); }); return (it != p1.bonds().end()) diff --git a/src/core/observables/BondAngles.hpp b/src/core/observables/BondAngles.hpp index d1c0d7b2ede..f77673a1e5e 100644 --- a/src/core/observables/BondAngles.hpp +++ b/src/core/observables/BondAngles.hpp @@ -49,7 +49,7 @@ class BondAngles : public PidObservable { } std::vector - evaluate(Utils::Span> particles, + evaluate(ParticleReferenceRange particles, const ParticleObservables::traits &traits) const override { std::vector res(n_values()); auto v1 = box_geo.get_mi_vector(traits.position(particles[1]), diff --git a/src/core/observables/BondDihedrals.hpp b/src/core/observables/BondDihedrals.hpp index 721fc220ea5..68ff105d098 100644 --- a/src/core/observables/BondDihedrals.hpp +++ b/src/core/observables/BondDihedrals.hpp @@ -53,7 +53,7 @@ class BondDihedrals : public PidObservable { } std::vector - evaluate(Utils::Span> particles, + evaluate(ParticleReferenceRange particles, const ParticleObservables::traits &traits) const override { std::vector res(n_values()); auto v1 = box_geo.get_mi_vector(traits.position(particles[1]), diff --git a/src/core/observables/CosPersistenceAngles.hpp b/src/core/observables/CosPersistenceAngles.hpp index 4d0c5c46e11..32445f08c28 100644 --- a/src/core/observables/CosPersistenceAngles.hpp +++ b/src/core/observables/CosPersistenceAngles.hpp @@ -49,11 +49,11 @@ class CosPersistenceAngles : public PidObservable { } std::vector - evaluate(Utils::Span> particles, + evaluate(ParticleReferenceRange particles, const ParticleObservables::traits &traits) const override { auto const no_of_angles = n_values(); + auto const no_of_bonds = no_of_angles + 1; std::vector angles(no_of_angles); - auto const no_of_bonds = n_values() + 1; std::vector bond_vectors(no_of_bonds); auto get_bond_vector = [&](auto index) { return box_geo.get_mi_vector(traits.position(particles[index + 1]), diff --git a/src/core/observables/CylindricalDensityProfile.hpp b/src/core/observables/CylindricalDensityProfile.hpp index c347c8f797b..f4e67ee3a55 100644 --- a/src/core/observables/CylindricalDensityProfile.hpp +++ b/src/core/observables/CylindricalDensityProfile.hpp @@ -35,11 +35,11 @@ class CylindricalDensityProfile : public CylindricalPidProfileObservable { public: using CylindricalPidProfileObservable::CylindricalPidProfileObservable; std::vector - evaluate(Utils::Span> particles, + evaluate(ParticleReferenceRange particles, const ParticleObservables::traits &traits) const override { Utils::CylindricalHistogram histogram(n_bins(), limits()); - for (auto p : particles) { + for (auto const &p : particles) { histogram.update(Utils::transform_coordinate_cartesian_to_cylinder( folded_position(traits.position(p), box_geo) - transform_params->center(), diff --git a/src/core/observables/CylindricalLBVelocityProfileAtParticlePositions.cpp b/src/core/observables/CylindricalLBVelocityProfileAtParticlePositions.cpp index 4bb9a0ae7b4..2ba77f5e5c2 100644 --- a/src/core/observables/CylindricalLBVelocityProfileAtParticlePositions.cpp +++ b/src/core/observables/CylindricalLBVelocityProfileAtParticlePositions.cpp @@ -35,7 +35,7 @@ std::vector CylindricalLBVelocityProfileAtParticlePositions::evaluate( const ParticleObservables::traits &traits) const { Utils::CylindricalHistogram histogram(n_bins(), limits()); - for (auto p : particles) { + for (auto const &p : particles) { auto const pos = folded_position(traits.position(p), box_geo); auto const v = lb_lbfluid_get_interpolated_velocity(pos) * lb_lbfluid_get_lattice_speed(); diff --git a/src/core/observables/CylindricalLBVelocityProfileAtParticlePositions.hpp b/src/core/observables/CylindricalLBVelocityProfileAtParticlePositions.hpp index a22617effdf..64a14e63de0 100644 --- a/src/core/observables/CylindricalLBVelocityProfileAtParticlePositions.hpp +++ b/src/core/observables/CylindricalLBVelocityProfileAtParticlePositions.hpp @@ -37,8 +37,8 @@ class CylindricalLBVelocityProfileAtParticlePositions using CylindricalPidProfileObservable::CylindricalPidProfileObservable; std::vector - evaluate(Utils::Span> particles, - const ParticleObservables::traits &traits) const override; + evaluate(ParticleReferenceRange particles, + const ParticleObservables::traits &) const override; std::vector shape() const override { auto const b = n_bins(); diff --git a/src/core/observables/CylindricalVelocityProfile.hpp b/src/core/observables/CylindricalVelocityProfile.hpp index 1dfacfefd9c..e463b85d635 100644 --- a/src/core/observables/CylindricalVelocityProfile.hpp +++ b/src/core/observables/CylindricalVelocityProfile.hpp @@ -38,7 +38,7 @@ class CylindricalVelocityProfile : public CylindricalPidProfileObservable { using CylindricalPidProfileObservable::CylindricalPidProfileObservable; std::vector - evaluate(Utils::Span> particles, + evaluate(ParticleReferenceRange particles, const ParticleObservables::traits &traits) const override { Utils::CylindricalHistogram histogram(n_bins(), limits()); diff --git a/src/core/observables/ForceDensityProfile.hpp b/src/core/observables/ForceDensityProfile.hpp index a94b974a626..2b223bd33a6 100644 --- a/src/core/observables/ForceDensityProfile.hpp +++ b/src/core/observables/ForceDensityProfile.hpp @@ -42,9 +42,9 @@ class ForceDensityProfile : public PidProfileObservable { std::vector evaluate(ParticleReferenceRange particles, - const ParticleObservables::traits &traits) const override { + const ParticleObservables::traits &) const override { Utils::Histogram histogram(n_bins(), limits()); - for (auto p : particles) { + for (auto const &p : particles) { histogram.update(folded_position(p.get().r.p, box_geo), p.get().f.f); } histogram.normalize(); diff --git a/src/core/observables/LBProfileObservable.hpp b/src/core/observables/LBProfileObservable.hpp index d887588c0cb..10375349078 100644 --- a/src/core/observables/LBProfileObservable.hpp +++ b/src/core/observables/LBProfileObservable.hpp @@ -68,10 +68,8 @@ class LBProfileObservable : public ProfileObservable { void calculate_sampling_positions() { auto const lim = limits(); sampling_positions.clear(); - assert(sampling_delta[0] > 0. and sampling_delta[1] > 0. and - sampling_delta[2] > 0.); - assert(sampling_offset[0] >= 0. and sampling_offset[1] >= 0. and - sampling_offset[2] >= 0.); + assert(Utils::Vector3d(sampling_delta) > Utils::Vector3d::broadcast(0.)); + assert(Utils::Vector3d(sampling_offset) >= Utils::Vector3d::broadcast(0.)); const auto n_samples_x = static_cast( std::rint((lim[0].second - lim[0].first) / sampling_delta[0])); const auto n_samples_y = static_cast( diff --git a/src/core/observables/ParticleAngularVelocities.hpp b/src/core/observables/ParticleAngularVelocities.hpp index aa1d476b9c4..ab59e1270b7 100644 --- a/src/core/observables/ParticleAngularVelocities.hpp +++ b/src/core/observables/ParticleAngularVelocities.hpp @@ -34,15 +34,13 @@ class ParticleAngularVelocities : public PidObservable { using PidObservable::PidObservable; std::vector - evaluate(Utils::Span> particles, - const ParticleObservables::traits &traits) const override { + evaluate(ParticleReferenceRange particles, + const ParticleObservables::traits &) const override { std::vector res(n_values()); - #ifdef ROTATION std::size_t i = 0; - for (auto p : particles) { - const Utils::Vector3d omega = - convert_vector_body_to_space(p.get(), p.get().m.omega); + for (auto const &p : particles) { + auto const omega = convert_vector_body_to_space(p.get(), p.get().m.omega); res[3 * i + 0] = omega[0]; res[3 * i + 1] = omega[1]; res[3 * i + 2] = omega[2]; diff --git a/src/core/observables/ParticleBodyAngularVelocities.hpp b/src/core/observables/ParticleBodyAngularVelocities.hpp index 749fa22cc87..9e2b3c6b3e5 100644 --- a/src/core/observables/ParticleBodyAngularVelocities.hpp +++ b/src/core/observables/ParticleBodyAngularVelocities.hpp @@ -33,14 +33,17 @@ class ParticleBodyAngularVelocities : public PidObservable { using PidObservable::PidObservable; std::vector - evaluate(Utils::Span> particles, - const ParticleObservables::traits &traits) const override { + evaluate(ParticleReferenceRange particles, + const ParticleObservables::traits &) const override { std::vector res(n_values()); #ifdef ROTATION - for (std::size_t i = 0; i < particles.size(); i++) { - res[3 * i + 0] = particles[i].get().m.omega[0]; - res[3 * i + 1] = particles[i].get().m.omega[1]; - res[3 * i + 2] = particles[i].get().m.omega[2]; + std::size_t i = 0; + for (auto const &p : particles) { + auto const &omega = p.get().m.omega; + res[3 * i + 0] = omega[0]; + res[3 * i + 1] = omega[1]; + res[3 * i + 2] = omega[2]; + i++; } #endif return res; diff --git a/src/core/observables/ParticleBodyVelocities.hpp b/src/core/observables/ParticleBodyVelocities.hpp index 9de70465634..70775fa9cf2 100644 --- a/src/core/observables/ParticleBodyVelocities.hpp +++ b/src/core/observables/ParticleBodyVelocities.hpp @@ -35,7 +35,7 @@ class ParticleBodyVelocities : public PidObservable { using PidObservable::PidObservable; std::vector - evaluate(Utils::Span> particles, + evaluate(ParticleReferenceRange particles, const ParticleObservables::traits &traits) const override { std::vector res(n_values()); for (std::size_t i = 0; i < particles.size(); i++) { diff --git a/src/core/observables/ParticleDistances.hpp b/src/core/observables/ParticleDistances.hpp index 61002e4ef4e..fd7e0212b81 100644 --- a/src/core/observables/ParticleDistances.hpp +++ b/src/core/observables/ParticleDistances.hpp @@ -48,7 +48,7 @@ class ParticleDistances : public PidObservable { } std::vector - evaluate(Utils::Span> particles, + evaluate(ParticleReferenceRange particles, const ParticleObservables::traits &traits) const override { std::vector res(n_values()); diff --git a/src/core/observables/ParticleForces.hpp b/src/core/observables/ParticleForces.hpp index bcd0702a276..36688e37774 100644 --- a/src/core/observables/ParticleForces.hpp +++ b/src/core/observables/ParticleForces.hpp @@ -37,12 +37,15 @@ class ParticleForces : public PidObservable { std::vector evaluate(ParticleReferenceRange particles, - const ParticleObservables::traits &traits) const override { + const ParticleObservables::traits &) const override { std::vector res(n_values()); - for (std::size_t i = 0; i < particles.size(); i++) { - res[3 * i + 0] = particles[i].get().f.f[0]; - res[3 * i + 1] = particles[i].get().f.f[1]; - res[3 * i + 2] = particles[i].get().f.f[2]; + std::size_t i = 0; + for (auto const &p : particles) { + auto const &f = p.get().f.f; + res[3 * i + 0] = f[0]; + res[3 * i + 1] = f[1]; + res[3 * i + 2] = f[2]; + i++; } return res; }; diff --git a/src/core/observables/PidObservable.hpp b/src/core/observables/PidObservable.hpp index 523940e68d4..5e9da6d6bf5 100644 --- a/src/core/observables/PidObservable.hpp +++ b/src/core/observables/PidObservable.hpp @@ -113,7 +113,7 @@ template class ParticleObservable : public PidObservable { std::vector evaluate(ParticleReferenceRange particles, - const ParticleObservables::traits &traits) const override { + const ParticleObservables::traits &) const override { std::vector res; Utils::flatten(ObsType{}(particles), std::back_inserter(res)); return res; diff --git a/src/core/observables/TotalForce.hpp b/src/core/observables/TotalForce.hpp index 81c062cdf61..a333c46b85e 100644 --- a/src/core/observables/TotalForce.hpp +++ b/src/core/observables/TotalForce.hpp @@ -32,16 +32,14 @@ class TotalForce : public PidObservable { std::vector evaluate(ParticleReferenceRange particles, - const ParticleObservables::traits &traits) const override { - std::vector res(n_values()); + const ParticleObservables::traits &) const override { + Utils::Vector3d res{}; for (auto const &p : particles) { if (p.get().p.is_virtual) continue; - res[0] += p.get().f.f[0]; - res[1] += p.get().f.f[1]; - res[2] += p.get().f.f[2]; + res += p.get().f.f; } - return res; + return res.as_vector(); } }; } // Namespace Observables diff --git a/src/core/particle_data.cpp b/src/core/particle_data.cpp index d5fe22ea72c..459cb988d29 100644 --- a/src/core/particle_data.cpp +++ b/src/core/particle_data.cpp @@ -183,22 +183,20 @@ using UpdateForceMessage = boost::variant * @brief Delete specific bond. */ struct RemoveBond { - std::vector bond; + std::vector bond; - void operator()(Particle &p) const { - assert(not bond.empty()); - auto const view = BondView(bond.front(), {bond.data() + 1, bond.size() - 1}); - auto it = boost::find(p.bonds(), view); + void operator()(Particle &p) const { + assert(not bond.empty()); + auto const view = BondView(bond.front(), {bond.data() + 1, bond.size() - 1}); + auto it = boost::find(p.bonds(), view); - if(it != p.bonds().end()) { - p.bonds().erase(it); - } + if (it != p.bonds().end()) { + p.bonds().erase(it); } + } - template - void serialize(Archive &ar, long int) { - ar & bond; - } + template + void serialize(Archive &ar, long int) { ar & bond; } }; @@ -206,28 +204,25 @@ struct RemoveBond { * @brief Delete all bonds. */ struct RemoveBonds { - void operator()(Particle &p) const { - p.bonds().clear(); - } + void operator()(Particle &p) const { p.bonds().clear(); } - template - void serialize(Archive &ar, long int) { - } + template + void serialize(Archive &, long int) {} }; struct AddBond { - std::vector bond; + std::vector bond; - void operator()(Particle &p) const { - auto const view = BondView(bond.at(0), {bond.data() + 1, bond.size() - 1}); + void operator()(Particle &p) const { + auto const view = BondView(bond.at(0), {bond.data() + 1, bond.size() - 1}); - p.bonds().insert(view); - } + p.bonds().insert(view); + } - template - void serialize(Archive &ar, long int) { - ar & bond; - } + template + void serialize(Archive &ar, long int) { + ar & bond; + } }; using UpdateBondMessage = boost::variant @@ -238,17 +233,17 @@ using UpdateBondMessage = boost::variant #ifdef ROTATION struct UpdateOrientation { - Utils::Vector3d axis; - double angle; + Utils::Vector3d axis; + double angle; - void operator()(Particle &p) const { - local_rotate_particle(p, axis, angle); - } + void operator()(Particle &p) const { + local_rotate_particle(p, axis, angle); + } - template - void serialize(Archive &ar, long int) { - ar & axis & angle; - } + template + void serialize(Archive &ar, long int) { + ar & axis & angle; + } }; #endif @@ -528,7 +523,8 @@ static void mpi_get_particles_local() { return *cell_structure.get_local_particle(id); }); - Utils::Mpi::gatherv(comm_cart, parts.data(), parts.size(), 0); + Utils::Mpi::gatherv(comm_cart, parts.data(), static_cast(parts.size()), + 0); } REGISTER_CALLBACK(mpi_get_particles_local) @@ -578,8 +574,8 @@ std::vector mpi_get_particles(Utils::Span ids) { node_ids.cbegin(), node_ids.cend(), node_sizes.begin(), [](std::vector const &ids) { return static_cast(ids.size()); }); - Utils::Mpi::gatherv(comm_cart, parts.data(), parts.size(), parts.data(), - node_sizes.data(), 0); + Utils::Mpi::gatherv(comm_cart, parts.data(), static_cast(parts.size()), + parts.data(), node_sizes.data(), 0); return parts; } diff --git a/src/core/polymer.cpp b/src/core/polymer.cpp index 7629d0cc200..398bfda52b9 100644 --- a/src/core/polymer.cpp +++ b/src/core/polymer.cpp @@ -171,9 +171,8 @@ draw_polymer_positions(PartCfg &partCfg, int const n_polymers, /* Try up to max_tries times to draw a valid position */ auto draw_valid_monomer_position = [&](int p, int m) -> boost::optional { - for (unsigned _ = 0; _ < max_tries; _++) { + for (unsigned i = 0; i < max_tries; i++) { auto const trial_pos = draw_monomer_position(p, m); - if (is_valid_pos(trial_pos)) { return trial_pos; } @@ -187,7 +186,8 @@ draw_polymer_positions(PartCfg &partCfg, int const n_polymers, for (int attempts_poly = 0; attempts_poly < max_tries; attempts_poly++) { int rejections = 0; while (positions[p].size() < beads_per_chain) { - auto pos = draw_valid_monomer_position(p, positions[p].size()); + auto pos = draw_valid_monomer_position( + p, static_cast(positions[p].size())); if (pos) { /* Move on one position */ diff --git a/src/core/reaction_methods/ReactionAlgorithm.cpp b/src/core/reaction_methods/ReactionAlgorithm.cpp index 23e9449d1fa..352e56d02c8 100644 --- a/src/core/reaction_methods/ReactionAlgorithm.cpp +++ b/src/core/reaction_methods/ReactionAlgorithm.cpp @@ -528,7 +528,6 @@ void ReactionAlgorithm::move_particle(int p_id, Utils::Vector3d const &new_pos, std::vector> ReactionAlgorithm::generate_new_particle_positions(int type, int n_particles) { - std::vector p_id_s_changed_particles; std::vector> old_positions; old_positions.reserve(n_particles); diff --git a/src/core/reaction_methods/ReactionAlgorithm.hpp b/src/core/reaction_methods/ReactionAlgorithm.hpp index f0c9ce312be..6f26db7aa7b 100644 --- a/src/core/reaction_methods/ReactionAlgorithm.hpp +++ b/src/core/reaction_methods/ReactionAlgorithm.hpp @@ -141,10 +141,10 @@ class ReactionAlgorithm { all_reactant_particles_exist(SingleReaction const ¤t_reaction) const; protected: - virtual double calculate_acceptance_probability( - SingleReaction const ¤t_reaction, double E_pot_old, - double E_pot_new, std::map const &old_particle_numbers) const { - return -10; + virtual double + calculate_acceptance_probability(SingleReaction const &, double, double, + std::map const &) const { + return -10.; } private: diff --git a/src/core/statistics.cpp b/src/core/statistics.cpp index 5ad04741ed5..35b83325de7 100644 --- a/src/core/statistics.cpp +++ b/src/core/statistics.cpp @@ -273,7 +273,7 @@ void calc_structurefactor(PartCfg &partCfg, std::vector const &p_types, if (order < 1) throw std::domain_error("order has to be a strictly positive number"); - auto const order_sq = order * order; + auto const order_sq = Utils::sqr(static_cast(order)); std::vector ff(2 * order_sq + 1); auto const twoPI_L = 2 * Utils::pi() * box_geo.length_inv()[0]; @@ -281,7 +281,7 @@ void calc_structurefactor(PartCfg &partCfg, std::vector const &p_types, for (int j = -order; j <= order; j++) { for (int k = -order; k <= order; k++) { auto const n = i * i + j * j + k * k; - if ((n <= order_sq) && (n >= 1)) { + if ((static_cast(n) <= order_sq) && (n >= 1)) { double C_sum = 0.0, S_sum = 0.0; for (auto const &p : partCfg) { for (int t : p_types) { @@ -308,7 +308,7 @@ void calc_structurefactor(PartCfg &partCfg, std::vector const &p_types, } int length = 0; - for (int qi = 0; qi < order_sq; qi++) { + for (std::size_t qi = 0; qi < order_sq; qi++) { if (ff[2 * qi + 1] != 0) { ff[2 * qi] /= static_cast(n_particles) * ff[2 * qi + 1]; length++; @@ -319,9 +319,9 @@ void calc_structurefactor(PartCfg &partCfg, std::vector const &p_types, intensities.resize(length); int cnt = 0; - for (int i = 0; i < order_sq; i++) { + for (std::size_t i = 0; i < order_sq; i++) { if (ff[2 * i + 1] != 0) { - wavevectors[cnt] = twoPI_L * sqrt(i + 1); + wavevectors[cnt] = twoPI_L * sqrt(static_cast(i + 1)); intensities[cnt] = ff[2 * i]; cnt++; } diff --git a/src/core/unit_tests/ParticleIterator_test.cpp b/src/core/unit_tests/ParticleIterator_test.cpp index 892c44a204a..5953546756a 100644 --- a/src/core/unit_tests/ParticleIterator_test.cpp +++ b/src/core/unit_tests/ParticleIterator_test.cpp @@ -38,11 +38,8 @@ using Cell = Testing::Cell; std::vector> make_cells(std::size_t n) { std::vector> cells(n); - - for (auto &c : cells) { - c = std::make_unique(); - } - + std::generate(cells.begin(), cells.end(), + []() { return std::make_unique(); }); return cells; } diff --git a/src/core/unit_tests/field_coupling_force_field_test.cpp b/src/core/unit_tests/field_coupling_force_field_test.cpp index 0b10610da9e..c35d12efcf6 100644 --- a/src/core/unit_tests/field_coupling_force_field_test.cpp +++ b/src/core/unit_tests/field_coupling_force_field_test.cpp @@ -25,7 +25,6 @@ #include "field_coupling/detail/Base.hpp" #include "field_coupling/detail/BindCoupling.hpp" -using namespace FieldCoupling; #include @@ -67,11 +66,11 @@ BOOST_AUTO_TEST_CASE(BindCoupling_test) { auto const bc = make_bind_coupling(id, Particle{}); auto const x = 5; - BOOST_CHECK(id.count == 0); - BOOST_CHECK(5 == bc(5)); - BOOST_CHECK(id.count == 1); - BOOST_CHECK(x == bc(x)); - BOOST_CHECK(id.count == 2); + BOOST_CHECK_EQUAL(id.count, 0); + BOOST_CHECK_EQUAL(bc(5), 5); + BOOST_CHECK_EQUAL(id.count, 1); + BOOST_CHECK_EQUAL(bc(x), x); + BOOST_CHECK_EQUAL(id.count, 2); } } @@ -96,13 +95,13 @@ BOOST_AUTO_TEST_CASE(FieldBase_test) { /* ctor copy */ { - int c = 1; - int f = 2; + auto const c = 1; + auto const f = 2; auto base = Base(c, f); - BOOST_CHECK(1 == base.coupling()); - BOOST_CHECK(2 == base.field()); + BOOST_CHECK_EQUAL(base.coupling(), c); + BOOST_CHECK_EQUAL(base.field(), f); } } @@ -113,13 +112,14 @@ struct DummyVectorField { }; BOOST_AUTO_TEST_CASE(ForceField_test) { + using FieldCoupling::ForceField; auto ff = ForceField, DummyVectorField>(Id{}, DummyVectorField{}); - const Utils::Vector3d x{1., 2., 3.}; + auto const x = Utils::Vector3d{1., 2., 3.}; - BOOST_CHECK(0 == ff.coupling().count); - BOOST_CHECK((9. * x) == ff.force(5, x, 9.)); - BOOST_CHECK(1 == ff.coupling().count); + BOOST_CHECK_EQUAL(ff.coupling().count, 0); + BOOST_CHECK_EQUAL(ff.force(5, x, 9.), 9. * x); + BOOST_CHECK_EQUAL(ff.coupling().count, 1); } struct DummyScalarField { @@ -132,14 +132,15 @@ struct DummyScalarField { }; BOOST_AUTO_TEST_CASE(PotentialField_test) { + using FieldCoupling::PotentialField; auto pf = PotentialField, DummyScalarField>(Id{}, DummyScalarField{}); - const Utils::Vector3d x{1., 2., 3.}; - BOOST_CHECK(0 == pf.coupling().count); + auto const x = Utils::Vector3d{1., 2., 3.}; + BOOST_CHECK_EQUAL(pf.coupling().count, 0); - BOOST_CHECK((2. * x.norm()) == pf.energy(5, x, 2.)); - BOOST_CHECK(1 == pf.coupling().count); + BOOST_CHECK_EQUAL(pf.energy(5, x, 2.), 2. * x.norm()); + BOOST_CHECK_EQUAL(pf.coupling().count, 1); - BOOST_CHECK(-(3. * x) == pf.force(5, x, 0)); - BOOST_CHECK(2 == pf.coupling().count); + BOOST_CHECK_EQUAL(pf.force(5, x, 0), -3. * x); + BOOST_CHECK_EQUAL(pf.coupling().count, 2); } diff --git a/src/core/virtual_sites/lb_inertialess_tracers.cpp b/src/core/virtual_sites/lb_inertialess_tracers.cpp index 9dde81803af..a790e43aa65 100644 --- a/src/core/virtual_sites/lb_inertialess_tracers.cpp +++ b/src/core/virtual_sites/lb_inertialess_tracers.cpp @@ -35,31 +35,24 @@ #include "lb_inertialess_tracers_cuda_interface.hpp" #include +#include #include #include -void CoupleIBMParticleToFluid(Particle &p); +void CoupleIBMParticleToFluid(Particle const &p); void ParticleVelocitiesFromLB_CPU(); -bool IsHalo(int indexCheck); -void GetIBMInterpolatedVelocity(const Utils::Vector3d &p, Utils::Vector3d &v, - Utils::Vector3d &forceAdded); +bool IsHalo(std::size_t indexCheck); -bool *isHaloCache = nullptr; +static bool *isHaloCache = nullptr; -namespace { -bool in_local_domain(Utils::Vector3d const &pos) { +inline bool in_local_domain(Utils::Vector3d const &pos) { + auto const offset = Utils::Vector3d::broadcast(0.5 * lblattice.agrid); auto const my_left = local_geo.my_left(); auto const my_right = local_geo.my_right(); - return (pos[0] >= my_left[0] - 0.5 * lblattice.agrid && - pos[0] < my_right[0] + 0.5 * lblattice.agrid && - pos[1] >= my_left[1] - 0.5 * lblattice.agrid && - pos[1] < my_right[1] + 0.5 * lblattice.agrid && - pos[2] >= my_left[2] - 0.5 * lblattice.agrid && - pos[2] < my_right[2] + 0.5 * lblattice.agrid); + return pos >= (my_left - offset) and pos < (my_right + offset); } -} // namespace /** Put the calculated force stored on the ibm particles into the fluid by * updating the @ref lbfields structure. @@ -124,9 +117,9 @@ void IBM_UpdateParticlePositions(ParticleRange const &particles, } /** Put the momentum of a given particle into the LB fluid. */ -void CoupleIBMParticleToFluid(Particle &p) { +void CoupleIBMParticleToFluid(Particle const &p) { // Convert units from MD to LB - auto const delta_j = p.f.f * Utils::sqr(lbpar.tau * lbpar.tau) / lbpar.agrid; + auto const delta_j = p.f.f * Utils::int_pow<4>(lbpar.tau) / lbpar.agrid; // Get indices and weights of affected nodes using discrete delta function Utils::Vector node_index{}; @@ -138,7 +131,7 @@ void CoupleIBMParticleToFluid(Particle &p) { for (int y = 0; y < 2; y++) { for (int x = 0; x < 2; x++) { // Do not put force into a halo node - if (!IsHalo(node_index[(z * 2 + y) * 2 + x])) { + if (!IsHalo(static_cast(node_index[(z * 2 + y) * 2 + x]))) { // Add force into the lbfields structure auto &local_f = lbfields[node_index[(z * 2 + y) * 2 + x]].force_density; @@ -157,17 +150,20 @@ void CoupleIBMParticleToFluid(Particle &p) { * The fluid velocity is obtained by linear interpolation, * cf. eq. (11) in @cite ahlrichs99a. */ -void GetIBMInterpolatedVelocity(const Utils::Vector3d &pos, Utils::Vector3d &v, - Utils::Vector3d &forceAdded) { +template +Utils::Vector3d GetIBMInterpolatedVelocity(Utils::Vector3d const &pos) { + auto const f_ext = + lbpar.ext_force_density * Utils::sqr(lbpar.agrid * lbpar.tau); + /* determine elementary lattice cell surrounding the particle and the relative position of the particle in this cell */ Utils::Vector node_index{}; Utils::Vector6d delta{}; lblattice.map_position_to_lattice(pos, node_index, delta); - Utils::Vector3d interpolated_u = {}; // This for the f/2 contribution to the velocity - forceAdded = {}; + Utils::Vector3d force_added = {}; + Utils::Vector3d interpolated_u = {}; for (int z = 0; z < 2; z++) { for (int y = 0; y < 2; y++) { @@ -184,43 +180,50 @@ void GetIBMInterpolatedVelocity(const Utils::Vector3d &pos, Utils::Vector3d &v, // We probably can even set the boundary velocity directly. #ifdef LB_BOUNDARIES if (lbfields[index].boundary) { - local_density = lbpar.density; - local_j = lbpar.density * - (*LBBoundaries::lbboundaries[lbfields[index].boundary - 1]) - .velocity(); + if (ReturnVelocity) { + local_density = lbpar.density; + auto const i = lbfields[index].boundary - 1; + local_j = lbpar.density * LBBoundaries::lbboundaries[i]->velocity(); + } } else #endif { - auto const modes = lb_calc_modes(index, lbfluid); + auto const modes = lb_calc_modes(static_cast(index), lbfluid); local_density = lbpar.density + modes[0]; - // Add the +f/2 contribution!! - local_j[0] = modes[1] + f[0] / 2.; - local_j[1] = modes[2] + f[1] / 2.; - local_j[2] = modes[3] + f[2] / 2.; - - // Keep track of the forces that we added to the fluid. - // This is necessary for communication because this part is executed - // for real and ghost particles. - // Later on we sum the real and ghost contributions. - auto const f_ext = - lbpar.ext_force_density * Utils::sqr(lbpar.agrid * lbpar.tau); - forceAdded += local_delta * (f - f_ext) / (2. * local_density); + if (ReturnVelocity) { + // Add the +f/2 contribution!! + local_j[0] = modes[1] + f[0] / 2.; + local_j[1] = modes[2] + f[1] / 2.; + local_j[2] = modes[3] + f[2] / 2.; + } else { + // Keep track of the forces that we added to the fluid. + // This is necessary for communication because this part is executed + // for real and ghost particles. + // Later on we sum the real and ghost contributions. + force_added += local_delta * (f - f_ext) / (2. * local_density); + } } // Interpolate velocity - interpolated_u += local_delta * local_j / local_density; + if (ReturnVelocity) { + interpolated_u += local_j * (local_delta / local_density); + } } } } - v = interpolated_u * lbpar.agrid / lbpar.tau; + auto const unit_conversion = lbpar.agrid / lbpar.tau; + if (ReturnVelocity) { + return interpolated_u * unit_conversion; + } + return force_added * unit_conversion; } /** Build a cache structure which contains a flag for each LB node whether * that node is a halo node or not. */ -bool IsHalo(const int indexCheck) { +bool IsHalo(std::size_t indexCheck) { // First call --> build cache if (isHaloCache == nullptr) { isHaloCache = new bool[lblattice.halo_grid_volume]; @@ -228,7 +231,7 @@ bool IsHalo(const int indexCheck) { for (int i = 0; i < lblattice.halo_grid_volume; i++) isHaloCache[i] = true; // Loop through and check where indexCheck occurs - int index = lblattice.halo_offset; + auto index = lblattice.halo_offset; for (int z = 1; z <= lblattice.grid[2]; z++) { for (int y = 1; y <= lblattice.grid[1]; y++) { for (int x = 1; x <= lblattice.grid[0]; x++) { @@ -256,8 +259,7 @@ void ParticleVelocitiesFromLB_CPU() { if (p.p.is_virtual) { // Get interpolated velocity and store in the force (!) field // for later communication (see below) - Utils::Vector3d dummy; - GetIBMInterpolatedVelocity(p.r.p, p.f.f, dummy); + p.f.f = GetIBMInterpolatedVelocity(p.r.p); } } @@ -269,20 +271,18 @@ void ParticleVelocitiesFromLB_CPU() { // Try if we have to use *1.5 on the right if (in_local_domain(p.r.p)) { if (p.p.is_virtual) { - Utils::Vector3d force{}; // The force stemming from the ghost particle - Utils::Vector3d dummy; - GetIBMInterpolatedVelocity(p.r.p, dummy, force); - - // Rescale and store in the force field of the particle (for + // The force stemming from the ghost particle (for // communication, see below) - p.f.f = force * lbpar.agrid / lbpar.tau; + p.f.f = GetIBMInterpolatedVelocity(p.r.p); } else { + // Reset, necessary because we add all forces below. Also needs to + // be done for real particles! p.f.f = {}; - } // Reset, necessary because we add all forces below. Also needs to - // be done for the real particles! + } } else { + // Reset, necessary because we add all forces below p.f.f = {}; - } // Reset, necessary because we add all forces below + } } // Now the local particles contain a velocity (stored in the force field) diff --git a/src/core/virtual_sites/lb_inertialess_tracers_cuda_interface.cpp b/src/core/virtual_sites/lb_inertialess_tracers_cuda_interface.cpp index b575e61eeef..a6599f01cda 100644 --- a/src/core/virtual_sites/lb_inertialess_tracers_cuda_interface.cpp +++ b/src/core/virtual_sites/lb_inertialess_tracers_cuda_interface.cpp @@ -101,7 +101,7 @@ static void set_velocities(ParticleRange const &particles, * nodes. Analogous to @ref cuda_mpi_send_forces. */ void IBM_cuda_mpi_send_velocities(ParticleRange const &particles) { - auto const n_part = particles.size(); + auto const n_part = static_cast(particles.size()); if (this_node > 0) { static std::vector buffer; diff --git a/src/script_interface/lbboundaries/LBBoundary.hpp b/src/script_interface/lbboundaries/LBBoundary.hpp index 6167643bb40..fdd53e6360f 100644 --- a/src/script_interface/lbboundaries/LBBoundary.hpp +++ b/src/script_interface/lbboundaries/LBBoundary.hpp @@ -90,7 +90,8 @@ class LBBoundary : public AutoParameters { /* Keep a reference to the shape */ std::shared_ptr m_shape; -}; // class LBBoundary +}; + } // namespace LBBoundaries } /* namespace ScriptInterface */ #endif diff --git a/src/shapes/unit_tests/Sphere_test.cpp b/src/shapes/unit_tests/Sphere_test.cpp index f3e92d1710b..31ebbe798bf 100644 --- a/src/shapes/unit_tests/Sphere_test.cpp +++ b/src/shapes/unit_tests/Sphere_test.cpp @@ -37,13 +37,15 @@ void check_distance_function(Shapes::Sphere &s) { auto const tol = std::numeric_limits::epsilon() * 100; s.rad() = 1.0; - pos = {0., 0., 0.}; s.calculate_dist(pos, dist, vec); - double always_pos_dist = -s.direction() * dist; - BOOST_REQUIRE_GE(always_pos_dist, 0.0); - BOOST_REQUIRE_CLOSE(always_pos_dist, s.rad(), tol); - BOOST_REQUIRE_CLOSE(always_pos_dist, vec.norm(), tol); + + { + double always_pos_dist = -s.direction() * dist; + BOOST_REQUIRE_GE(always_pos_dist, 0.0); + BOOST_REQUIRE_CLOSE(always_pos_dist, s.rad(), tol); + BOOST_REQUIRE_CLOSE(always_pos_dist, vec.norm(), tol); + } for (int i = 0; i < 3; ++i) { pos[i] = 1.0; diff --git a/src/utils/include/utils/Array.hpp b/src/utils/include/utils/Array.hpp index 2704fc3ceb6..9369ecc584e 100644 --- a/src/utils/include/utils/Array.hpp +++ b/src/utils/include/utils/Array.hpp @@ -155,14 +155,15 @@ template struct Array { DEVICE_QUALIFIER constexpr size_type max_size() const noexcept { return N; } DEVICE_QUALIFIER void fill(const value_type &value) { - for (size_type i = 0; i < size(); ++i) + for (size_type i = 0; i != size(); ++i) { m_storage.m_data[i] = value; + } } DEVICE_QUALIFIER static constexpr Array broadcast(const value_type &value) { Array ret{}; - for (size_type i = 0; i < N; ++i) { + for (size_type i = 0; i != N; ++i) { ret[i] = value; } return ret; diff --git a/src/utils/include/utils/Counter.hpp b/src/utils/include/utils/Counter.hpp index 2812b8d17fd..8a265416a64 100644 --- a/src/utils/include/utils/Counter.hpp +++ b/src/utils/include/utils/Counter.hpp @@ -27,8 +27,7 @@ template class Counter { T m_val; T m_initial; friend class boost::serialization::access; - template - void serialize(Archive &ar, const unsigned int version) { + template void serialize(Archive &ar, const unsigned int) { ar &m_val; ar &m_initial; } diff --git a/src/utils/include/utils/math/int_pow.hpp b/src/utils/include/utils/math/int_pow.hpp index 79c679ebb3c..7bfebad9e47 100644 --- a/src/utils/include/utils/math/int_pow.hpp +++ b/src/utils/include/utils/math/int_pow.hpp @@ -47,7 +47,7 @@ template struct int_pow_impl { }; template struct int_pow_impl { - DEVICE_QUALIFIER constexpr T operator()(T x) const { return T{1}; } + DEVICE_QUALIFIER constexpr T operator()(T) const { return T{1}; } }; } // namespace detail diff --git a/src/utils/include/utils/math/matrix_vector_product.hpp b/src/utils/include/utils/math/matrix_vector_product.hpp index 1e40c321c56..fc23d044f15 100644 --- a/src/utils/include/utils/math/matrix_vector_product.hpp +++ b/src/utils/include/utils/math/matrix_vector_product.hpp @@ -34,7 +34,7 @@ template struct mul { }; template struct mul<0, T> { - constexpr T operator()(const T a) const { return T{}; } + constexpr T operator()(const T) const { return T{}; } }; template struct mul<1, T> { diff --git a/src/utils/include/utils/matrix.hpp b/src/utils/include/utils/matrix.hpp index fd1fa56a377..dd838bac2b5 100644 --- a/src/utils/include/utils/matrix.hpp +++ b/src/utils/include/utils/matrix.hpp @@ -74,12 +74,13 @@ template struct Matrix { container m_data; +private: friend class boost::serialization::access; - template - void serialize(Archive &ar, const unsigned int version) { + template void serialize(Archive &ar, const unsigned int) { ar &m_data; } +public: Matrix() = default; Matrix(std::initializer_list init_list) { assert(init_list.size() == Rows * Cols); diff --git a/src/utils/include/utils/mpi/cart_comm.hpp b/src/utils/include/utils/mpi/cart_comm.hpp index 0005f8b7678..d5882eeab4d 100644 --- a/src/utils/include/utils/mpi/cart_comm.hpp +++ b/src/utils/include/utils/mpi/cart_comm.hpp @@ -60,7 +60,7 @@ boost::mpi::communicator cart_create( (comm, dim, dims.data(), periodicity.data(), static_cast(reorder), &temp_comm)) - return boost::mpi::communicator(temp_comm, boost::mpi::comm_take_ownership); + return {temp_comm, boost::mpi::comm_take_ownership}; } /** @@ -113,13 +113,12 @@ inline std::pair cart_shift(boost::mpi::communicator const &comm, template Utils::Vector cart_neighbors(const boost::mpi::communicator &comm) { - using std::get; Vector ret; for (std::size_t i = 0; i < dim; i++) { - ret[2 * i + 0] = get<1>(cart_shift(comm, i, -1)); - ret[2 * i + 1] = get<1>(cart_shift(comm, i, +1)); + ret[2 * i + 0] = std::get<1>(cart_shift(comm, static_cast(i), -1)); + ret[2 * i + 1] = std::get<1>(cart_shift(comm, static_cast(i), +1)); } return ret; diff --git a/src/utils/include/utils/quaternion.hpp b/src/utils/include/utils/quaternion.hpp index c8cfe693340..7da863ac63e 100644 --- a/src/utils/include/utils/quaternion.hpp +++ b/src/utils/include/utils/quaternion.hpp @@ -60,8 +60,7 @@ template struct Quaternion { private: friend class boost::serialization::access; - template - void serialize(Archive &ar, const unsigned int version) { + template void serialize(Archive &ar, const unsigned int) { ar &m_data; } diff --git a/src/utils/include/utils/sampling.hpp b/src/utils/include/utils/sampling.hpp index 9b581ee69c5..2d9b26a9549 100644 --- a/src/utils/include/utils/sampling.hpp +++ b/src/utils/include/utils/sampling.hpp @@ -49,6 +49,7 @@ std::vector get_cylindrical_sampling_positions( std::pair const &phi_limits, std::pair const &z_limits, std::size_t n_r_bins, std::size_t n_phi_bins, std::size_t n_z_bins, double sampling_density) { + auto constexpr endpoint = false; auto const delta_r = (r_limits.second - r_limits.first) / static_cast(n_r_bins); auto const delta_phi = @@ -58,22 +59,20 @@ std::vector get_cylindrical_sampling_positions( // azimuthal angle per bin such that we fulfill the sampling density // requirement. auto const smallest_bin_volume = - pi() * Utils::sqr(r_limits.first + delta_r) * delta_phi / (2.0 * pi()); + Utils::sqr(r_limits.first + delta_r) * delta_phi / 2.; auto const min_n_samples = std::max(n_z_bins, static_cast(std::round( smallest_bin_volume * sampling_density))); auto const delta_z = (z_limits.second - z_limits.first) / static_cast(min_n_samples); - auto const r_range = - make_lin_space(r_limits.first + .5 * delta_r, r_limits.second, n_r_bins, - /* endpoint */ false); + auto const r_range = make_lin_space(r_limits.first + .5 * delta_r, + r_limits.second, n_r_bins, endpoint); auto const phi_range = make_lin_space(phi_limits.first + .5 * delta_phi, phi_limits.second, - n_phi_bins, /* endpoint */ false); - auto const z_range = - make_lin_space(z_limits.first + .5 * delta_z, z_limits.second, - min_n_samples, /* endpoint */ false); + n_phi_bins, endpoint); + auto const z_range = make_lin_space(z_limits.first + .5 * delta_z, + z_limits.second, min_n_samples, endpoint); // Create the sampling positions for the innermost bin. std::vector sampling_positions; @@ -84,17 +83,10 @@ std::vector get_cylindrical_sampling_positions( } // Scale the number of samples for larger bins - auto arc_length = [delta_phi, delta_r](int r_bin) { - return delta_phi * (r_bin + 1) * delta_r; - }; - auto n_phi_samples = [arc_length](int r_bin) { - return arc_length(r_bin) / arc_length(0); - }; - auto phis = [n_phi_samples, n_phi_bins, phi_limits](int r_bin) { + auto phis = [n_phi_bins, phi_limits](long r_bin) { auto const phis_range = make_lin_space( phi_limits.first, phi_limits.second, - n_phi_bins * static_cast(std::round(n_phi_samples(r_bin))), - /*endpoint */ false); + n_phi_bins * (static_cast(r_bin) + 1), endpoint); return phis_range; }; // Calculate the sampling positions diff --git a/src/utils/include/utils/tuple.hpp b/src/utils/include/utils/tuple.hpp index a2feb4cc06b..16415f1015f 100644 --- a/src/utils/include/utils/tuple.hpp +++ b/src/utils/include/utils/tuple.hpp @@ -139,7 +139,7 @@ struct filter_impl { } template - constexpr static auto get(Tuple const &t, std::false_type) { + constexpr static auto get(Tuple const &, std::false_type) { return std::make_tuple(); } diff --git a/src/utils/tests/Vector_test.cpp b/src/utils/tests/Vector_test.cpp index c49ebb22548..da66e53b182 100644 --- a/src/utils/tests/Vector_test.cpp +++ b/src/utils/tests/Vector_test.cpp @@ -171,15 +171,15 @@ BOOST_AUTO_TEST_CASE(algebraic_operators) { BOOST_CHECK(((v1 * 2) == Utils::Vector3i{2, 4, 6})); { - Utils::Vector3i v1{2, 4, 6}; - auto v2 = 2 * v1; - BOOST_CHECK(v2 == (v1 *= 2)); + Utils::Vector3i v3{2, 4, 6}; + auto v4 = 2 * v3; + BOOST_CHECK(v4 == (v3 *= 2)); } { - Utils::Vector3i v1{2, 4, 6}; - auto v2 = v1 / 2; - BOOST_CHECK(v2 == (v1 /= 2)); + Utils::Vector3i v3{2, 4, 6}; + auto v4 = v3 / 2; + BOOST_CHECK(v4 == (v3 /= 2)); } BOOST_CHECK((sqrt(Utils::Vector3d{1., 2., 3.}) == @@ -187,14 +187,14 @@ BOOST_AUTO_TEST_CASE(algebraic_operators) { /* modulo */ { - Utils::Vector3i v1{2, 7, 8}; - Utils::Vector3i v2{1, 2, 3}; + Utils::Vector3i v3{2, 7, 8}; + Utils::Vector3i v4{1, 2, 3}; - auto const res = v1 % v2; + auto const res = v3 % v4; - BOOST_CHECK_EQUAL(res[0], v1[0] % v2[0]); - BOOST_CHECK_EQUAL(res[1], v1[1] % v2[1]); - BOOST_CHECK_EQUAL(res[2], v1[2] % v2[2]); + BOOST_CHECK_EQUAL(res[0], v3[0] % v4[0]); + BOOST_CHECK_EQUAL(res[1], v3[1] % v4[1]); + BOOST_CHECK_EQUAL(res[2], v3[2] % v4[2]); } } From 70c698c3525dc49aa4eafabf0bb09e05420c3f9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 21 Jan 2022 23:45:44 +0100 Subject: [PATCH 26/99] tests: Modernize unit tests --- .../electrostatics_magnetostatics/p3m.cpp | 3 +- .../tests/ReactionEnsemble_test.cpp | 2 +- src/core/unit_tests/energy_test.cpp | 15 +++--- .../particle_observables/algorithms.hpp | 6 +-- .../particle_observables/observable.hpp | 6 +-- src/particle_observables/tests/algorithms.cpp | 53 ++++++++++++++----- src/shapes/src/Rhomboid.cpp | 2 +- src/utils/tests/sgn_test.cpp | 9 ++-- src/utils/tests/sinc_test.cpp | 8 ++- 9 files changed, 66 insertions(+), 38 deletions(-) diff --git a/src/core/electrostatics_magnetostatics/p3m.cpp b/src/core/electrostatics_magnetostatics/p3m.cpp index 16ec3f29d5a..1f8322da6e0 100644 --- a/src/core/electrostatics_magnetostatics/p3m.cpp +++ b/src/core/electrostatics_magnetostatics/p3m.cpp @@ -69,8 +69,6 @@ #include #include -using Utils::sinc; - p3m_data_struct p3m; /** \name Private Functions */ @@ -1218,6 +1216,7 @@ double p3m_k_space_error(double prefac, const int mesh[3], int cao, void p3m_tune_aliasing_sums(int nx, int ny, int nz, const int mesh[3], const double mesh_i[3], int cao, double alpha_L_i, double *alias1, double *alias2) { + using Utils::sinc; auto const factor1 = Utils::sqr(Utils::pi() * alpha_L_i); diff --git a/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp b/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp index 3d172688cae..a6d836947fc 100644 --- a/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp +++ b/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp @@ -57,7 +57,7 @@ BOOST_AUTO_TEST_CASE(ReactionEnsemble_test) { using ReactionEnsemble::generic_oneway_reaction; using ReactionEnsemble::ReactionEnsemble; }; - constexpr double tol = 100 * std::numeric_limits::epsilon(); + auto constexpr tol = 100 * std::numeric_limits::epsilon(); // check basic interface { diff --git a/src/core/unit_tests/energy_test.cpp b/src/core/unit_tests/energy_test.cpp index eb261743027..73552970075 100644 --- a/src/core/unit_tests/energy_test.cpp +++ b/src/core/unit_tests/energy_test.cpp @@ -21,8 +21,11 @@ #define BOOST_TEST_DYN_LINK #include +#include "Particle.hpp" #include "energy_inline.hpp" +#include "utils/Vector.hpp" + BOOST_AUTO_TEST_CASE(translational_kinetic_energy_) { // real particle { @@ -33,7 +36,7 @@ BOOST_AUTO_TEST_CASE(translational_kinetic_energy_) { p.m.v = {3., 4., 5.}; auto const expected = 0.5 * p.p.mass * p.m.v.norm2(); - BOOST_TEST(translational_kinetic_energy(p) == expected); + BOOST_CHECK_EQUAL(translational_kinetic_energy(p), expected); } // virtual particle @@ -48,13 +51,13 @@ BOOST_AUTO_TEST_CASE(translational_kinetic_energy_) { p.m.v = {3., 4., 5.}; auto const expected = 0.; - BOOST_TEST(translational_kinetic_energy(p) == expected); + BOOST_CHECK_EQUAL(translational_kinetic_energy(p), expected); #endif } } BOOST_AUTO_TEST_CASE(rotational_kinetic_energy_) { - BOOST_TEST(rotational_kinetic_energy(Particle{}) == 0.); + BOOST_CHECK_EQUAL(rotational_kinetic_energy(Particle{}), 0.); #ifdef ROTATION { @@ -64,7 +67,7 @@ BOOST_AUTO_TEST_CASE(rotational_kinetic_energy_) { auto const expected = 0.5 * (hadamard_product(p.m.omega, p.m.omega) * p.p.rinertia); - BOOST_TEST(rotational_kinetic_energy(p) == expected); + BOOST_CHECK_EQUAL(rotational_kinetic_energy(p), expected); } #endif } @@ -83,5 +86,5 @@ BOOST_AUTO_TEST_CASE(kinetic_energy_) { auto const expected = translational_kinetic_energy(p) + rotational_kinetic_energy(p); - BOOST_TEST(calc_kinetic_energy(p) == expected); -} \ No newline at end of file + BOOST_CHECK_EQUAL(calc_kinetic_energy(p), expected); +} diff --git a/src/particle_observables/include/particle_observables/algorithms.hpp b/src/particle_observables/include/particle_observables/algorithms.hpp index 4ec9aaaab2e..21c0c63c094 100644 --- a/src/particle_observables/include/particle_observables/algorithms.hpp +++ b/src/particle_observables/include/particle_observables/algorithms.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 ALGORITHMS_HPP -#define ALGORITHMS_HPP +#ifndef SRC_PARTICLE_OBSERVABLES_ALGORITHMS_HPP +#define SRC_PARTICLE_OBSERVABLES_ALGORITHMS_HPP /** * @file @@ -98,4 +98,4 @@ template struct Map { } }; } // namespace ParticleObservables -#endif // ALGORITHMS_HPP +#endif // SRC_PARTICLE_OBSERVABLES_ALGORITHMS_HPP diff --git a/src/particle_observables/include/particle_observables/observable.hpp b/src/particle_observables/include/particle_observables/observable.hpp index 7c928fcc2ef..2f5eb0d2e5f 100644 --- a/src/particle_observables/include/particle_observables/observable.hpp +++ b/src/particle_observables/include/particle_observables/observable.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 INCLUDE_OBSERVABLES_OBSERVABLE_HPP -#define INCLUDE_OBSERVABLES_OBSERVABLE_HPP +#ifndef SRC_PARTICLE_OBSERVABLES_OBSERVABLE_HPP +#define SRC_PARTICLE_OBSERVABLES_OBSERVABLE_HPP #include "algorithms.hpp" #include "properties.hpp" @@ -54,4 +54,4 @@ using Positions = Map; using Velocities = Map; } // namespace ParticleObservables -#endif // INCLUDE_OBSERVABLES_OBSERVABLE_HPP +#endif // SRC_PARTICLE_OBSERVABLES_OBSERVABLE_HPP diff --git a/src/particle_observables/tests/algorithms.cpp b/src/particle_observables/tests/algorithms.cpp index 597265677c2..1418ccdf6aa 100644 --- a/src/particle_observables/tests/algorithms.cpp +++ b/src/particle_observables/tests/algorithms.cpp @@ -23,6 +23,8 @@ #include #include +#include +#include #include #include @@ -38,34 +40,61 @@ struct One { struct PlusOne { template auto operator()(Particle const &p) { return p + 1; } }; + +template +T average(std::vector const &value, std::size_t denominator) { + auto const sum = std::accumulate(value.begin(), value.end(), T{0}); + return static_cast(sum) / static_cast(denominator); +} + } // namespace Testing -BOOST_AUTO_TEST_CASE(algorithms) { - std::vector values{1, 2, 3, 4}; +BOOST_AUTO_TEST_CASE(algorithms_integer) { + std::vector const values{1, 2, 3, 4}; { auto const res = WeightedAverage()(values); - BOOST_CHECK(res == std::accumulate(values.begin(), values.end(), 0) / - values.size()); + BOOST_CHECK_EQUAL(res, Testing::average(values, values.size())); } { auto const res = WeightedAverage()(values); - BOOST_CHECK(res == (1 * 2 + 2 * 3 + 3 * 4 + 4 * 5) / 14); - auto const res2 = - WeightedSum()(values); - BOOST_CHECK(res2 == (1 * 2 + 2 * 3 + 3 * 4 + 4 * 5)); + BOOST_CHECK_EQUAL(res, (1 * 2 + 2 * 3 + 3 * 4 + 4 * 5) / 14); + } + { + auto const res = WeightedSum()(values); + BOOST_CHECK_EQUAL(res, (1 * 2 + 2 * 3 + 3 * 4 + 4 * 5)); + } + { + auto const res = Average()(values); + BOOST_CHECK_EQUAL(res, Testing::average(values, values.size())); + } + { + auto const res = Sum{}(values); + BOOST_CHECK_EQUAL(res, Testing::average(values, 1u)); + } + { + auto const res = Map{}(values); + BOOST_TEST(res == values); + } +} + +BOOST_AUTO_TEST_CASE(algorithms_double) { + auto constexpr tol = 100 * std::numeric_limits::epsilon(); + std::vector const values{1., 2., 3., 4.}; + { + auto const res = WeightedAverage()(values); + BOOST_CHECK_CLOSE(res, Testing::average(values, values.size()), tol); } { auto const res = Average()(values); - BOOST_CHECK(res == std::accumulate(values.begin(), values.end(), 0) / - values.size()); + BOOST_CHECK_EQUAL(res, Testing::average(values, values.size())); } { auto const res = Sum{}(values); - BOOST_CHECK(res == std::accumulate(values.begin(), values.end(), 0)); + BOOST_CHECK_EQUAL(res, Testing::average(values, 1u)); } { auto const res = Map{}(values); - BOOST_CHECK(res == values); + BOOST_TEST(res == values); } } diff --git a/src/shapes/src/Rhomboid.cpp b/src/shapes/src/Rhomboid.cpp index a2f1c90901a..902ca2416ac 100644 --- a/src/shapes/src/Rhomboid.cpp +++ b/src/shapes/src/Rhomboid.cpp @@ -184,7 +184,7 @@ void Rhomboid::calculate_dist(const Utils::Vector3d &pos, double &dist, [=, &vec, &dist](auto op1, auto op2, Utils::Vector3d const &distance, Utils::Vector3d const &axis, double const dir_dot_axis, int sign) { - auto d = (distance)*axis; + auto d = distance * axis; if (op1(dir_dot_axis, 0)) { d *= -1; } diff --git a/src/utils/tests/sgn_test.cpp b/src/utils/tests/sgn_test.cpp index f40ae7a2ea8..036d4696aef 100644 --- a/src/utils/tests/sgn_test.cpp +++ b/src/utils/tests/sgn_test.cpp @@ -20,11 +20,10 @@ #include #include "utils/math/sgn.hpp" -using Utils::sgn; /* Check that it can be used in constexpr context */ -static_assert(sgn(1), ""); +static_assert(Utils::sgn(1), ""); -BOOST_AUTO_TEST_CASE(pos) { BOOST_CHECK(1 == sgn(89)); } -BOOST_AUTO_TEST_CASE(nul) { BOOST_CHECK(0 == sgn(0)); } -BOOST_AUTO_TEST_CASE(neg) { BOOST_CHECK(-1 == sgn(-89)); } +BOOST_AUTO_TEST_CASE(pos) { BOOST_CHECK_EQUAL(Utils::sgn(89), 1); } +BOOST_AUTO_TEST_CASE(nul) { BOOST_CHECK_EQUAL(Utils::sgn(0), 0); } +BOOST_AUTO_TEST_CASE(neg) { BOOST_CHECK_EQUAL(Utils::sgn(-89), -1); } diff --git a/src/utils/tests/sinc_test.cpp b/src/utils/tests/sinc_test.cpp index fb13e84fb7e..dace2e56b47 100644 --- a/src/utils/tests/sinc_test.cpp +++ b/src/utils/tests/sinc_test.cpp @@ -24,15 +24,13 @@ #include #include -using Utils::sinc; - -BOOST_AUTO_TEST_CASE(zero) { BOOST_CHECK(1.0 == sinc(0.0)); } +BOOST_AUTO_TEST_CASE(zero) { BOOST_CHECK_EQUAL(Utils::sinc(0.0), 1.0); } BOOST_AUTO_TEST_CASE(approx) { for (double x = 0.001; x <= 0.11; x += 0.01) { - auto const approx = sinc(x); + auto const approx = Utils::sinc(x); auto const pi_x = boost::math::constants::pi() * x; auto const exact = std::sin(pi_x) / (pi_x); - BOOST_CHECK(std::abs(approx - exact) <= 1e-13); + BOOST_CHECK_SMALL(approx - exact, 1e-13); } } From af14bc5a287e016975c274efd9c2da648d0a17e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 24 Jan 2022 12:22:28 +0100 Subject: [PATCH 27/99] core: Refactor and test actors exception mechanism Factor out code duplication, convert unreachable throw statements to assertions and reduce branching in LB, ELC, MDLC, P3M and DP3M code. Add test cases for actor exceptions. --- .../electrostatics_magnetostatics/coulomb.cpp | 6 +- .../electrostatics_magnetostatics/dipole.cpp | 3 +- .../electrostatics_magnetostatics/elc.cpp | 8 +-- .../electrostatics_magnetostatics/icc.cpp | 21 +++---- .../mdlc_correction.cpp | 8 +-- .../electrostatics_magnetostatics/mmm1d.cpp | 8 +-- .../electrostatics_magnetostatics/mmm1d.hpp | 2 +- .../p3m-dipolar.cpp | 9 ++- .../electrostatics_magnetostatics/p3m.cpp | 5 +- src/core/grid_based_algorithms/lb.cpp | 40 ++++++------- src/core/grid_based_algorithms/lb.hpp | 21 +++---- src/core/unit_tests/lb_exceptions.cpp | 13 +++++ testsuite/python/lb_boundary.py | 2 + testsuite/python/mmm1d.py | 8 +++ testsuite/python/p3m_tuning_exceptions.py | 56 +++++++++++++++++++ 15 files changed, 136 insertions(+), 74 deletions(-) diff --git a/src/core/electrostatics_magnetostatics/coulomb.cpp b/src/core/electrostatics_magnetostatics/coulomb.cpp index f2adfd61209..2e955867a6e 100644 --- a/src/core/electrostatics_magnetostatics/coulomb.cpp +++ b/src/core/electrostatics_magnetostatics/coulomb.cpp @@ -88,8 +88,7 @@ bool sanity_checks() { bool failed = false; switch (coulomb.method) { case COULOMB_MMM1D: - if (MMM1D_sanity_checks()) - failed = true; + failed |= MMM1D_sanity_checks(); break; #ifdef P3M case COULOMB_ELC_P3M: @@ -102,8 +101,7 @@ bool sanity_checks() { // fall through case COULOMB_P3M_GPU: case COULOMB_P3M: - if (p3m_sanity_checks()) - failed = true; + failed |= p3m_sanity_checks(); break; #endif default: diff --git a/src/core/electrostatics_magnetostatics/dipole.cpp b/src/core/electrostatics_magnetostatics/dipole.cpp index 4295ac62647..1e98a62146a 100644 --- a/src/core/electrostatics_magnetostatics/dipole.cpp +++ b/src/core/electrostatics_magnetostatics/dipole.cpp @@ -71,8 +71,7 @@ bool sanity_checks() { mdlc_sanity_checks(); // fall through case DIPOLAR_P3M: - if (dp3m_sanity_checks(node_grid)) - failed = true; + failed |= dp3m_sanity_checks(node_grid); break; case DIPOLAR_MDLC_DS: mdlc_sanity_checks(); diff --git a/src/core/electrostatics_magnetostatics/elc.cpp b/src/core/electrostatics_magnetostatics/elc.cpp index b0ed9f95009..36b2cd0cd9d 100644 --- a/src/core/electrostatics_magnetostatics/elc.cpp +++ b/src/core/electrostatics_magnetostatics/elc.cpp @@ -186,12 +186,10 @@ void distribute(std::size_t size) { */ inline void check_gap_elc(const Particle &p) { if (p.p.q != 0) { - if (p.r.p[2] < 0) + auto const z = p.r.p[2]; + if (z < 0. or z > elc_params.h) { runtimeErrorMsg() << "Particle " << p.p.identity << " entered ELC gap " - << "region by " << (p.r.p[2]); - else if (p.r.p[2] > elc_params.h) { - runtimeErrorMsg() << "Particle " << p.p.identity << " entered ELC gap " - << "region by " << (p.r.p[2] - elc_params.h); + << "region by " << ((z < 0.) ? z : z - elc_params.h); } } } diff --git a/src/core/electrostatics_magnetostatics/icc.cpp b/src/core/electrostatics_magnetostatics/icc.cpp index 0aa8a73b1eb..6685acb0e3b 100644 --- a/src/core/electrostatics_magnetostatics/icc.cpp +++ b/src/core/electrostatics_magnetostatics/icc.cpp @@ -45,9 +45,11 @@ #include #include -#include +#include +#include #include +#include #include #include #include @@ -220,9 +222,6 @@ void icc_set_params(int n_icc, double convergence, double relaxation, int first_id, double eps_out, std::vector &areas, std::vector &e_in, std::vector &sigma, std::vector &normals) { - if (n_icc < 0) - throw std::runtime_error("ICC: invalid number of particles. " + - std::to_string(n_icc)); if (convergence <= 0) throw std::runtime_error("ICC: invalid convergence value. " + std::to_string(convergence)); @@ -238,14 +237,12 @@ void icc_set_params(int n_icc, double convergence, double relaxation, if (eps_out <= 0) throw std::runtime_error("ICC: invalid eps_out. " + std::to_string(eps_out)); - if (areas.size() != n_icc) - throw std::runtime_error("ICC: invalid areas vector."); - if (e_in.size() != n_icc) - throw std::runtime_error("ICC: invalid e_in vector."); - if (sigma.size() != n_icc) - throw std::runtime_error("ICC: invalid sigma vector."); - if (normals.size() != n_icc) - throw std::runtime_error("ICC: invalid normals vector."); + + assert(n_icc >= 0); + assert(areas.size() == n_icc); + assert(e_in.size() == n_icc); + assert(sigma.size() == n_icc); + assert(normals.size() == n_icc); icc_cfg.n_icc = n_icc; icc_cfg.convergence = convergence; diff --git a/src/core/electrostatics_magnetostatics/mdlc_correction.cpp b/src/core/electrostatics_magnetostatics/mdlc_correction.cpp index bcefe278fdb..f280ef1aa4f 100644 --- a/src/core/electrostatics_magnetostatics/mdlc_correction.cpp +++ b/src/core/electrostatics_magnetostatics/mdlc_correction.cpp @@ -55,12 +55,10 @@ DLC_struct dlc_params = {1e100, 0., 0., false, 0.}; */ inline void check_gap_mdlc(const Particle &p) { if (p.p.dipm != 0.0) { - if (p.r.p[2] < 0.0) + auto const z = p.r.p[2]; + if (z < 0.0 or z > dlc_params.h) { runtimeErrorMsg() << "Particle " << p.p.identity << " entered MDLC gap " - << "region by " << (p.r.p[2]); - else if (p.r.p[2] > dlc_params.h) { - runtimeErrorMsg() << "Particle " << p.p.identity << " entered MDLC gap " - << "region by " << (p.r.p[2] - dlc_params.h); + << "region by " << z - ((z < 0.) ? 0. : dlc_params.h); } } } diff --git a/src/core/electrostatics_magnetostatics/mmm1d.cpp b/src/core/electrostatics_magnetostatics/mmm1d.cpp index 2bbcf41cdd9..f8ce1c9ce6b 100644 --- a/src/core/electrostatics_magnetostatics/mmm1d.cpp +++ b/src/core/electrostatics_magnetostatics/mmm1d.cpp @@ -147,16 +147,16 @@ void MMM1D_set_params(double switch_rad, double maxPWerror) { mpi_bcast_coulomb_params(); } -int MMM1D_sanity_checks() { +bool MMM1D_sanity_checks() { if (box_geo.periodic(0) || box_geo.periodic(1) || !box_geo.periodic(2)) { runtimeErrorMsg() << "MMM1D requires periodicity (0, 0, 1)"; - return ES_ERROR; + return true; } if (cell_structure.decomposition_type() != CELL_STRUCTURE_NSQUARE) { runtimeErrorMsg() << "MMM1D requires the N-square cellsystem"; - return ES_ERROR; + return true; } - return ES_OK; + return false; } int MMM1D_init() { diff --git a/src/core/electrostatics_magnetostatics/mmm1d.hpp b/src/core/electrostatics_magnetostatics/mmm1d.hpp index 12f231619f3..38c7a441998 100644 --- a/src/core/electrostatics_magnetostatics/mmm1d.hpp +++ b/src/core/electrostatics_magnetostatics/mmm1d.hpp @@ -60,7 +60,7 @@ extern MMM1DParameters mmm1d_params; void MMM1D_set_params(double switch_rad, double maxPWerror); /// check that MMM1D can run with the current parameters -int MMM1D_sanity_checks(); +bool MMM1D_sanity_checks(); /// initialize the MMM1D constants int MMM1D_init(); diff --git a/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp b/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp index e34dd0dcf32..72e79a9737e 100644 --- a/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp +++ b/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp @@ -1417,13 +1417,13 @@ bool dp3m_sanity_checks(const Utils::Vector3i &grid) { bool ret = false; if (!box_geo.periodic(0) || !box_geo.periodic(1) || !box_geo.periodic(2)) { - runtimeErrorMsg() << "dipolar P3M requires periodicity 1 1 1"; + runtimeErrorMsg() << "dipolar P3M requires periodicity (1, 1, 1)"; ret = true; } if (cell_structure.decomposition_type() != CELL_STRUCTURE_DOMDEC) { - runtimeErrorMsg() << "dipolar P3M at present requires the domain " - "decomposition cell system"; + runtimeErrorMsg() << "dipolar P3M requires the domain decomposition " + "cell system"; ret = true; } @@ -1439,8 +1439,7 @@ bool dp3m_sanity_checks(const Utils::Vector3i &grid) { ret = true; } - if (dp3m_sanity_checks_boxl()) - ret = true; + ret |= dp3m_sanity_checks_boxl(); if (dp3m.params.mesh[0] == 0) { runtimeErrorMsg() << "dipolar P3M_init: mesh size is not yet set"; diff --git a/src/core/electrostatics_magnetostatics/p3m.cpp b/src/core/electrostatics_magnetostatics/p3m.cpp index 1f8322da6e0..d6c03feafb9 100644 --- a/src/core/electrostatics_magnetostatics/p3m.cpp +++ b/src/core/electrostatics_magnetostatics/p3m.cpp @@ -1282,13 +1282,12 @@ bool p3m_sanity_checks_system(const Utils::Vector3i &grid) { bool ret = false; if (!box_geo.periodic(0) || !box_geo.periodic(1) || !box_geo.periodic(2)) { - runtimeErrorMsg() << "P3M requires periodicity 1 1 1"; + runtimeErrorMsg() << "P3M requires periodicity (1, 1, 1)"; ret = true; } if (cell_structure.decomposition_type() != CELL_STRUCTURE_DOMDEC) { - runtimeErrorMsg() - << "P3M at present requires the domain decomposition cell system"; + runtimeErrorMsg() << "P3M requires the domain decomposition cell system"; ret = true; } diff --git a/src/core/grid_based_algorithms/lb.cpp b/src/core/grid_based_algorithms/lb.cpp index 0d38711c9de..1b8faabb162 100644 --- a/src/core/grid_based_algorithms/lb.cpp +++ b/src/core/grid_based_algorithms/lb.cpp @@ -969,12 +969,9 @@ void lb_integrate() { #endif } -/***********************************************************************/ -/** \name Coupling part */ -/***********************************************************************/ -/**@{*/ #ifdef ADDITIONAL_CHECKS -template int compare_buffers(T const &buff_a, T const &buff_b) { +int compare_buffers(std::array const &buff_a, + std::array const &buff_b) { if (buff_a != buff_b) { runtimeErrorMsg() << "Halo buffers are not identical"; return ES_ERROR; @@ -982,6 +979,18 @@ template int compare_buffers(T const &buff_a, T const &buff_b) { return ES_OK; } +void log_buffer_diff(std::ostream &out, int dir, Lattice::index_t index, int x, + int y, int z) { + out << "buffers differ in dir=" << dir << " at node index=" << index; + if (x != -1) + out << " x=" << x; + if (y != -1) + out << " y=" << y; + if (z != -1) + out << " z=" << z; + out << "\n"; +} + /** Check consistency of the halo regions. * Test whether the halo regions have been exchanged correctly. */ @@ -1018,8 +1027,7 @@ void lb_check_halo_regions(const LB_Fluid &lb_fluid, for (i = 0; i < D3Q19::n_vel; i++) r_buffer[i] = lb_fluid[i][index]; if (compare_buffers(s_buffer, r_buffer)) { - std::cerr << "buffers differ in dir=" << 0 << " at index=" << index - << " y=" << y << " z=" << z << "\n"; + log_buffer_diff(std::cerr, 0, index, -1, y, z); } } @@ -1042,8 +1050,7 @@ void lb_check_halo_regions(const LB_Fluid &lb_fluid, for (i = 0; i < D3Q19::n_vel; i++) r_buffer[i] = lb_fluid[i][index]; if (compare_buffers(s_buffer, r_buffer)) { - std::cerr << "buffers differ in dir=0 at index=" << index - << " y=" << y << " z=" << z << "\n"; + log_buffer_diff(std::cerr, 0, index, -1, y, z); } } } @@ -1073,8 +1080,7 @@ void lb_check_halo_regions(const LB_Fluid &lb_fluid, for (i = 0; i < D3Q19::n_vel; i++) r_buffer[i] = lb_fluid[i][index]; if (compare_buffers(s_buffer, r_buffer)) { - std::cerr << "buffers differ in dir=1 at index=" << index - << " x=" << x << " z=" << z << "\n"; + log_buffer_diff(std::cerr, 1, index, x, -1, z); } } } @@ -1098,8 +1104,7 @@ void lb_check_halo_regions(const LB_Fluid &lb_fluid, for (i = 0; i < D3Q19::n_vel; i++) r_buffer[i] = lb_fluid[i][index]; if (compare_buffers(s_buffer, r_buffer)) { - std::cerr << "buffers differ in dir=1 at index=" << index - << " x=" << x << " z=" << z << "\n"; + log_buffer_diff(std::cerr, 1, index, x, -1, z); } } } @@ -1129,9 +1134,7 @@ void lb_check_halo_regions(const LB_Fluid &lb_fluid, for (i = 0; i < D3Q19::n_vel; i++) r_buffer[i] = lb_fluid[i][index]; if (compare_buffers(s_buffer, r_buffer)) { - std::cerr << "buffers differ in dir=2 at index=" << index - << " x=" << x << " y=" << y << " z=" << lb_lattice.grid[2] - << "\n"; + log_buffer_diff(std::cerr, 2, index, x, y, lb_lattice.grid[2]); } } } @@ -1157,8 +1160,7 @@ void lb_check_halo_regions(const LB_Fluid &lb_fluid, for (i = 0; i < D3Q19::n_vel; i++) r_buffer[i] = lb_fluid[i][index]; if (compare_buffers(s_buffer, r_buffer)) { - std::cerr << "buffers differ in dir=2 at index=" << index - << " x=" << x << " y=" << y << "\n"; + log_buffer_diff(std::cerr, 2, index, x, y, -1); } } } @@ -1348,5 +1350,3 @@ void lb_collect_boundary_forces(double *result) { static_cast(lbb_data_len), result, std::plus<>(), 0); #endif } - -/**@}*/ diff --git a/src/core/grid_based_algorithms/lb.hpp b/src/core/grid_based_algorithms/lb.hpp index 06a98248b88..be1c91fb812 100644 --- a/src/core/grid_based_algorithms/lb.hpp +++ b/src/core/grid_based_algorithms/lb.hpp @@ -18,6 +18,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#ifndef SRC_CORE_GRID_BASED_ALGORITHMS_LB_HPP +#define SRC_CORE_GRID_BASED_ALGORITHMS_LB_HPP /** \file * * %Lattice Boltzmann algorithm for hydrodynamic degrees of freedom. @@ -34,9 +36,6 @@ * Implementation in lb.cpp. */ -#ifndef LB_H -#define LB_H - #include "config.hpp" #include "grid_based_algorithms/lattice.hpp" #include "grid_based_algorithms/lb-d3q19.hpp" @@ -53,6 +52,7 @@ #include #include #include +#include #include /** Counter for the RNG */ @@ -171,11 +171,6 @@ template auto get(const LB_Fluid_Ref &lb_fluid) { /** Hydrodynamic fields of the fluid */ extern std::vector lbfields; -/************************************************************/ -/** \name Exported Functions */ -/************************************************************/ -/**@{*/ - /** Integrate the lattice-Boltzmann system for one time step. * This function performs the collision step and the streaming step. * If external force densities are present, they are applied prior to the @@ -196,9 +191,6 @@ void lb_set_population_from_density_momentum_density_stress( Lattice::index_t index, double density, Utils::Vector3d const &momentum_density, Utils::Vector6d const &stress); -#ifdef VIRTUAL_SITES_INERTIALESS_TRACERS -#endif - double lb_calc_density(std::array const &modes, const LB_Parameters &lb_parameters); Utils::Vector3d lb_calc_momentum_density(std::array const &modes, @@ -267,6 +259,9 @@ void lb_initialize_fields(std::vector &fields, Lattice const &lb_lattice); void lb_on_param_change(LBParam param); -/**@}*/ +#ifdef ADDITIONAL_CHECKS +void log_buffer_diff(std::ostream &out, int dir, Lattice::index_t index, int x, + int y, int z); +#endif // ADDITIONAL_CHECKS -#endif /* _LB_H */ +#endif // SRC_CORE_GRID_BASED_ALGORITHMS_LB_HPP diff --git a/src/core/unit_tests/lb_exceptions.cpp b/src/core/unit_tests/lb_exceptions.cpp index 8f43f20d67b..5844fe1e2d0 100644 --- a/src/core/unit_tests/lb_exceptions.cpp +++ b/src/core/unit_tests/lb_exceptions.cpp @@ -21,6 +21,7 @@ #define BOOST_TEST_DYN_LINK #include +#include "grid_based_algorithms/lb.hpp" #include "grid_based_algorithms/lb_interface.hpp" #include "grid_based_algorithms/lb_interpolation.hpp" #include "grid_based_algorithms/lb_particle_coupling.hpp" @@ -84,4 +85,16 @@ BOOST_AUTO_TEST_CASE(exceptions) { BOOST_CHECK_THROW(lb_lbfluid_get_interpolated_density({}), std::exception); ::lattice_switch = ActiveLB::NONE; mpi_set_interpolation_order_local(InterpolationOrder::linear); +#ifdef ADDITIONAL_CHECKS + { + std::stringstream stream_xy{}; + log_buffer_diff(stream_xy, 0, 1, 2, 3, -1); + BOOST_CHECK_EQUAL(stream_xy.str(), + "buffers differ in dir=0 at node index=1 x=2 y=3\n"); + std::stringstream stream_xyz{}; + log_buffer_diff(stream_xyz, 0, 1, 2, 3, 4); + BOOST_CHECK_EQUAL(stream_xyz.str(), + "buffers differ in dir=0 at node index=1 x=2 y=3 z=4\n"); + } +#endif // ADDITIONAL_CHECKS } diff --git a/testsuite/python/lb_boundary.py b/testsuite/python/lb_boundary.py index e0480d2e046..15c76eb7a3b 100644 --- a/testsuite/python/lb_boundary.py +++ b/testsuite/python/lb_boundary.py @@ -70,6 +70,8 @@ def test_size(self): def test_getters(self): boundary = espressomd.lbboundaries.LBBoundary(shape=self.wall_shape1) + with self.assertRaisesRegex(RuntimeError, "You probably tried to get the force of an lbboundary that was not added to system.lbboundaries"): + boundary.get_force() self.system.lbboundaries.add(boundary) np.testing.assert_equal(np.copy(boundary.get_force()), [0., 0., 0.]) self.assertIsNone(boundary.call_method('unknown')) diff --git a/testsuite/python/mmm1d.py b/testsuite/python/mmm1d.py index 1940b88aec5..2c66dd9c83f 100644 --- a/testsuite/python/mmm1d.py +++ b/testsuite/python/mmm1d.py @@ -45,6 +45,7 @@ class ElectrostaticInteractionsTests: def setUp(self): self.system.periodicity = [0, 0, 1] + self.system.cell_system.set_n_square() self.system.part.add(pos=self.p_pos, q=self.p_q) self.mmm1d = self.MMM1D(prefactor=1.0, maxPWerror=1e-20) self.system.actors.add(self.mmm1d) @@ -110,6 +111,13 @@ def test_exceptions(self): self.system.actors.add(mmm1d) self.system.periodicity = (0, 0, 1) self.system.actors.clear() + if self.MMM1D is espressomd.electrostatics.MMM1D: + with self.assertRaisesRegex(Exception, "MMM1D requires the N-square cellsystem"): + mmm1d = self.MMM1D(prefactor=1.0, maxPWerror=1e-2) + self.system.cell_system.set_domain_decomposition() + self.system.actors.add(mmm1d) + self.system.cell_system.set_n_square() + self.system.actors.clear() @utx.skipIfMissingFeatures(["ELECTROSTATICS"]) diff --git a/testsuite/python/p3m_tuning_exceptions.py b/testsuite/python/p3m_tuning_exceptions.py index 52781405816..52c1b8e97ba 100644 --- a/testsuite/python/p3m_tuning_exceptions.py +++ b/testsuite/python/p3m_tuning_exceptions.py @@ -19,6 +19,7 @@ import espressomd import unittest as ut import unittest_decorators as utx +import itertools class P3M_tuning_test(ut.TestCase): @@ -195,6 +196,26 @@ def test_04_invalid_params_p3m_cpu(self): self.check_invalid_params(espressomd.electrostatics.P3M) + # set up a valid actor + solver = espressomd.electrostatics.P3M( + prefactor=2, accuracy=0.1, cao=2, r_cut=3.18, mesh=8) + self.system.actors.add(solver) + + # check periodicity exceptions + for periodicity in itertools.product(range(2), range(2), range(2)): + if periodicity == (1, 1, 1): + continue + with self.assertRaisesRegex(Exception, r"P3M requires periodicity \(1, 1, 1\)"): + self.system.periodicity = periodicity + self.system.periodicity = (1, 1, 1) + + # check cell system exceptions + with self.assertRaisesRegex(Exception, "P3M requires the domain decomposition cell system"): + self.system.cell_system.set_n_square() + self.system.analysis.energy() + self.system.cell_system.set_domain_decomposition() + self.system.actors.clear() + @utx.skipIfMissingGPU() @utx.skipIfMissingFeatures("P3M") def test_04_invalid_params_p3m_gpu(self): @@ -215,6 +236,35 @@ def test_04_invalid_params_dp3m_cpu(self): self.check_invalid_params(espressomd.magnetostatics.DipolarP3M) + # check bisection exception + self.system.periodicity = (0, 0, 0) + with self.assertRaisesRegex(Exception, r"Root must be bracketed for bisection in dp3m_rtbisection"): + solver = espressomd.magnetostatics.DipolarP3M( + prefactor=2, accuracy=0.1) + self.system.actors.add(solver) + self.system.periodicity = (1, 1, 1) + self.system.actors.clear() + + # set up a valid actor + solver = espressomd.magnetostatics.DipolarP3M( + prefactor=2, accuracy=0.1, cao=1, r_cut=3.28125, mesh=5) + self.system.actors.add(solver) + + # check periodicity and cell system exceptions + for periodicity in itertools.product(range(2), range(2), range(2)): + if periodicity == (1, 1, 1): + continue + with self.assertRaisesRegex(Exception, r"dipolar P3M requires periodicity \(1, 1, 1\)"): + self.system.periodicity = periodicity + self.system.periodicity = (1, 1, 1) + + # check cell system exceptions + with self.assertRaisesRegex(Exception, "dipolar P3M requires the domain decomposition cell system"): + self.system.cell_system.set_n_square() + self.system.analysis.energy() + self.system.cell_system.set_domain_decomposition() + self.system.actors.clear() + @utx.skipIfMissingFeatures("P3M") def test_04_invalid_params_p3m_elc_cpu(self): import espressomd.electrostatics @@ -324,6 +374,12 @@ def test_04_invalid_params_dp3m_dlc_cpu(self): self.system.actors.add(solver_mdlc) self.system.actors.remove(solver_mdlc) + solver_mdlc = espressomd.magnetostatic_extensions.DLC( + gap_size=1, maxPWerror=1e-30) + with self.assertRaisesRegex(RuntimeError, "MDLC tuning failed: unable to find a proper cut-off for the given accuracy"): + self.system.actors.add(solver_mdlc) + self.system.actors.remove(solver_mdlc) + ########################################################### # block of tests where tuning should not throw exceptions # ########################################################### From 9d56f252c8518c915a254f16bc7aac0d48cbbcd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 24 Jan 2022 12:50:26 +0100 Subject: [PATCH 28/99] tests: Check VirtualBond and RigidBond --- src/core/energy_inline.hpp | 4 ++-- src/core/forces_inline.hpp | 7 +++++- testsuite/python/interactions_bonded.py | 29 +++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/core/energy_inline.hpp b/src/core/energy_inline.hpp index 80e230135ef..18883b2b318 100644 --- a/src/core/energy_inline.hpp +++ b/src/core/energy_inline.hpp @@ -231,7 +231,7 @@ calc_bonded_energy(Bonded_IA_Parameters const &iaparams, Particle const &p1, #endif #ifdef BOND_CONSTRAINT if (boost::get(&iaparams)) { - return boost::optional(0); + return {0.}; } #endif #ifdef TABULATED @@ -240,7 +240,7 @@ calc_bonded_energy(Bonded_IA_Parameters const &iaparams, Particle const &p1, } #endif if (boost::get(&iaparams)) { - return boost::optional(0); + return {0.}; } throw BondUnknownTypeError(); } // 1 partner diff --git a/src/core/forces_inline.hpp b/src/core/forces_inline.hpp index e0d4f978508..ef906923e51 100644 --- a/src/core/forces_inline.hpp +++ b/src/core/forces_inline.hpp @@ -279,12 +279,17 @@ calc_bond_pair_force(Particle const &p1, Particle const &p2, return iap->force(dx); } #endif +#ifdef BOND_CONSTRAINT + if (boost::get(&iaparams)) { + return Utils::Vector3d{}; + } +#endif #ifdef TABULATED if (auto const *iap = boost::get(&iaparams)) { return iap->force(dx); } #endif - if (boost::get(&iaparams) || boost::get(&iaparams)) { + if (boost::get(&iaparams)) { return Utils::Vector3d{}; } throw BondUnknownTypeError(); diff --git a/testsuite/python/interactions_bonded.py b/testsuite/python/interactions_bonded.py index 792a66d2e7c..c9aac3aa6ee 100644 --- a/testsuite/python/interactions_bonded.py +++ b/testsuite/python/interactions_bonded.py @@ -46,7 +46,9 @@ def setUp(self): self.system.part.add(pos=self.start_pos, type=0) def tearDown(self): + self.system.actors.clear() self.system.part.clear() + self.system.bonded_inter.clear() # Test Harmonic Bond def test_harmonic(self): @@ -78,6 +80,33 @@ def test_fene(self): scalar_r=r, k=fene_k, d_r_max=fene_d_r_max, r_0=fene_r_0), 0.01, fene_r_0 + fene_d_r_max, True) + def test_virtual_bond(self): + # add sentinel harmonic bond, otherwise short-range loop is skipped + hb = espressomd.interactions.HarmonicBond(k=1., r_0=0.1, r_cut=0.5) + vb = espressomd.interactions.Virtual() + self.system.bonded_inter.add(hb) + self.system.bonded_inter.add(vb) + p1, p2 = self.system.part.all() + p1.add_bond((vb, p2)) + + self.system.integrator.run(steps=0, recalc_forces=True) + self.assertEqual(self.system.analysis.energy()["total"], 0.) + np.testing.assert_allclose(np.copy(p1.f), 0., atol=1e-12, rtol=0) + np.testing.assert_allclose(np.copy(p2.f), 0., atol=1e-12, rtol=0) + + @utx.skipIfMissingFeatures(["BOND_CONSTRAINT"]) + def test_rigid_bond(self): + rb = espressomd.interactions.RigidBond(r=1.0, ptol=0.1, vtol=0.1) + self.system.bonded_inter.add(rb) + p1, p2 = self.system.part.all() + p2.pos = p1.pos + np.array([1.0, 0., 0.]) + p1.add_bond((rb, p2)) + + self.system.integrator.run(steps=0, recalc_forces=True) + self.assertEqual(self.system.analysis.energy()["total"], 0.) + np.testing.assert_allclose(np.copy(p1.f), 0., atol=1e-12, rtol=0) + np.testing.assert_allclose(np.copy(p2.f), 0., atol=1e-12, rtol=0) + @utx.skipIfMissingFeatures(["ELECTROSTATICS"]) def test_coulomb(self): coulomb_k = 1 From 998b9df548243c4063b7eba6139ecadaed8e8fbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 25 Jan 2022 18:42:29 +0100 Subject: [PATCH 29/99] CMake: Remove distutils Package distutils is deprecated in Python 3.10 and will be removed in Python 3.12. The functionality was moved to package sysconfig. --- cmake/FindPythonHeaders.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/FindPythonHeaders.cmake b/cmake/FindPythonHeaders.cmake index 26b96955ffa..3e5f5700254 100644 --- a/cmake/FindPythonHeaders.cmake +++ b/cmake/FindPythonHeaders.cmake @@ -19,14 +19,14 @@ # find the Python C++ headers execute_process( COMMAND ${PYTHON_EXECUTABLE} -c - "import distutils.sysconfig as cg; print(cg.get_python_inc())" + "import sysconfig; print(sysconfig.get_path('include'))" OUTPUT_VARIABLE PYTHON_INCLUDE_DIRS OUTPUT_STRIP_TRAILING_WHITESPACE) # find Python installation directory if(NOT PYTHON_INSTDIR) execute_process( COMMAND ${PYTHON_EXECUTABLE} -c - "import distutils.sysconfig as cg; print(cg.get_python_lib(prefix='${CMAKE_INSTALL_PREFIX}', plat_specific=True, standard_lib=False).replace('${CMAKE_INSTALL_PREFIX}/', '', 1))" + "import sysconfig; print(sysconfig.get_path('purelib', vars={'base': ''}).lstrip('/'))" OUTPUT_VARIABLE PYTHON_INSTDIR OUTPUT_STRIP_TRAILING_WHITESPACE) endif(NOT PYTHON_INSTDIR) From 608f9cfbb4467a84037f7e83a81277bf276a1fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 28 Jan 2022 17:56:02 +0100 Subject: [PATCH 30/99] core: Fix regression in VTK output function Also adapt the corresponding test to check a non-cubic box. --- .../grid_based_algorithms/lb_interface.cpp | 6 +++--- testsuite/python/lb_vtk.py | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/core/grid_based_algorithms/lb_interface.cpp b/src/core/grid_based_algorithms/lb_interface.cpp index f0ec5f994dc..a9b0414cb60 100644 --- a/src/core/grid_based_algorithms/lb_interface.cpp +++ b/src/core/grid_based_algorithms/lb_interface.cpp @@ -581,9 +581,9 @@ void lb_lbfluid_print_vtk_velocity(const std::string &filename, #ifdef CUDA host_values.resize(lbpar_gpu.number_of_nodes); lb_get_values_GPU(host_values.data()); - auto const box_l_x = lb_lbfluid_get_shape()[0]; - vtk_writer("lbfluid_gpu", [box_l_x](Utils::Vector3i const &pos) { - auto const j = box_l_x * box_l_x * pos[2] + box_l_x * pos[1] + pos[0]; + auto const box_l = lb_lbfluid_get_shape(); + vtk_writer("lbfluid_gpu", [&box_l](Utils::Vector3i const &pos) { + auto const j = box_l[0] * box_l[1] * pos[2] + box_l[0] * pos[1] + pos[0]; return Utils::Vector3d{host_values[j].v}; }); #endif // CUDA diff --git a/testsuite/python/lb_vtk.py b/testsuite/python/lb_vtk.py index bc6bc283b3d..7ed32ebef82 100644 --- a/testsuite/python/lb_vtk.py +++ b/testsuite/python/lb_vtk.py @@ -38,7 +38,7 @@ class TestLBWrite: - system = espressomd.System(box_l=3 * [16]) + system = espressomd.System(box_l=[10, 11, 12]) system.time_step = 0.01 system.cell_system.skin = 0.4 @@ -56,7 +56,7 @@ def set_lbf(self): self.system.lbboundaries.add(espressomd.lbboundaries.LBBoundary( shape=espressomd.shapes.Wall(normal=[1, 0, 0], dist=1.5))) self.system.lbboundaries.add(espressomd.lbboundaries.LBBoundary( - shape=espressomd.shapes.Wall(normal=[-1, 0, 0], dist=-14.5))) + shape=espressomd.shapes.Wall(normal=[-1, 0, 0], dist=-10.5))) return lbf def parse_vtk(self, filepath, name, shape): @@ -85,7 +85,7 @@ def test_vtk(self): if os.path.exists(filepath): os.remove(filepath) - shape = [16, 16, 16] + shape = [10, 11, 12] lbf = self.set_lbf() self.system.integrator.run(100) @@ -108,7 +108,7 @@ def test_vtk(self): lbf.write_vtk_velocity('vtk_out/delme', [1, 1], 3 * [1]) with self.assertRaises(ValueError): lbf.write_vtk_velocity('vtk_out/delme', 3 * [1], np.array([2, 3])) - bb1, bb2 = ([1, 2, 3], [13, 14, 15]) + bb1, bb2 = ([1, 2, 3], [9, 10, 11]) lbf.write_vtk_velocity('vtk_out/velocity_bb.vtk', bb1, bb2) # check VTK files exist @@ -156,7 +156,7 @@ def test_print(self): if os.path.exists(filepath): os.remove(filepath) - shape = [16, 16, 16] + shape = [10, 11, 12] lbf = self.set_lbf() self.system.integrator.run(100) @@ -184,10 +184,10 @@ def test_print(self): node_velocity[i, j, k] = node.velocity node_boundary[i, j, k] = node.boundary - seq = np.arange(16) - ref_coord = np.array([np.tile(seq, 16 * 16), - np.tile(np.repeat(seq, 16), 16), - np.repeat(seq, 16 * 16)]).T + ref_coord = np.array([ + np.tile(np.arange(shape[0]), shape[1] * shape[2]), + np.tile(np.repeat(np.arange(shape[1]), shape[0]), shape[2]), + np.repeat(np.arange(shape[2]), shape[0] * shape[1])]).T dat_velocity = np.loadtxt('vtk_out/velocity.dat') dat_coord = (dat_velocity[:, 0:3] - 0.5).astype(int) From 2930a42e4587ffa3610a2f63433130bbc606f259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 27 Jan 2022 13:31:02 +0100 Subject: [PATCH 31/99] python: Factor out key check logic Check actor keys using set arithmetic. Move check logic to utils. Make valid_keys() and required_keys() methods always return sets. --- src/python/espressomd/actors.pyx | 27 +++-------- src/python/espressomd/electrokinetics.pyx | 20 +++------ src/python/espressomd/electrostatics.pyx | 45 +++++++++---------- src/python/espressomd/integrate.pyx | 17 +++---- src/python/espressomd/interactions.pyx | 37 +++------------ src/python/espressomd/magnetostatics.pyx | 22 ++++----- src/python/espressomd/polymer.pyx | 15 ++----- src/python/espressomd/utils.pyx | 18 ++++++++ testsuite/python/actor.py | 19 +++++++- testsuite/python/ek_charged_plate.py | 11 ++++- testsuite/python/electrostaticInteractions.py | 8 ++++ testsuite/python/integrator_exceptions.py | 5 +++ .../python/interactions_bonded_interface.py | 4 ++ .../interactions_non-bonded_interface.py | 22 +++++++++ testsuite/python/polymer_linear.py | 13 +++++- 15 files changed, 153 insertions(+), 130 deletions(-) diff --git a/src/python/espressomd/actors.pyx b/src/python/espressomd/actors.pyx index 31e63852ad2..13f34a636b3 100644 --- a/src/python/espressomd/actors.pyx +++ b/src/python/espressomd/actors.pyx @@ -17,6 +17,7 @@ include "myconfig.pxi" from .highlander import ThereCanOnlyBeOne from .utils import handle_errors +from . import utils cdef class Actor: @@ -45,20 +46,10 @@ cdef class Actor: def __init__(self, *args, **kwargs): self._isactive = False + utils.check_valid_keys(self.valid_keys(), kwargs.keys()) + utils.check_required_keys(self.required_keys(), kwargs.keys()) self._params = self.default_params() - - # Check if all required keys are given - for k in self.required_keys(): - if k not in kwargs: - raise ValueError( - "At least the following keys have to be given as keyword arguments: " + self.required_keys().__str__() + " got " + kwargs.__str__()) - self._params[k] = kwargs[k] - - for k in kwargs: - if k in self.valid_keys(): - self._params[k] = kwargs[k] - else: - raise KeyError(f"{k} is not a valid key") + self._params.update(kwargs) def _activate(self): inter = self._get_interaction_type() @@ -107,18 +98,12 @@ cdef class Actor: def set_params(self, **p): """Update the given parameters.""" # Check if keys are valid - for k in p.keys(): - if k not in self.valid_keys(): - raise ValueError( - "Only the following keys are supported: " + self.valid_keys().__str__()) + utils.check_valid_keys(self.valid_keys(), p.keys()) # When an interaction is newly activated, all required keys must be # given if not self.is_active(): - for k in self.required_keys(): - if k not in p: - raise ValueError( - "At least the following keys have to be given as keyword arguments: " + self.required_keys().__str__()) + utils.check_required_keys(self.required_keys(), p.keys()) self._params.update(p) # validate updated parameters diff --git a/src/python/espressomd/electrokinetics.pyx b/src/python/espressomd/electrokinetics.pyx index b65d252fef9..7422bfd9ff5 100644 --- a/src/python/espressomd/electrokinetics.pyx +++ b/src/python/espressomd/electrokinetics.pyx @@ -394,34 +394,24 @@ IF ELECTROKINETICS: def __init__(self, **kwargs): Species.py_number_of_species += 1 self.id = Species.py_number_of_species + utils.check_required_keys(self.required_keys(), kwargs.keys()) + utils.check_valid_keys(self.valid_keys(), kwargs.keys()) self._params = self.default_params() - - # Check if all required keys are given - for k in self.required_keys(): - if k not in kwargs: - raise ValueError( - "At least the following keys have to be given as keyword arguments: " + self.required_keys().__str__() + " got " + kwargs.__str__()) - self._params[k] = kwargs[k] - - for k in kwargs: - if k in self.valid_keys(): - self._params[k] = kwargs[k] - else: - raise KeyError(f"{k} is not a valid key") + self._params.update(kwargs) def valid_keys(self): """ Returns the valid keys for the species. """ - return "density", "D", "valency", "ext_force_density" + return {"density", "D", "valency", "ext_force_density"} def required_keys(self): """ Returns the required keys for the species. """ - return ["density", "D", "valency"] + return {"density", "D", "valency"} def default_params(self): """ diff --git a/src/python/espressomd/electrostatics.pyx b/src/python/espressomd/electrostatics.pyx index 269bd511ddf..50e4e80ccf0 100644 --- a/src/python/espressomd/electrostatics.pyx +++ b/src/python/espressomd/electrostatics.pyx @@ -25,6 +25,7 @@ import numpy as np IF SCAFACOS == 1: from .scafacos import ScafacosConnector from . cimport scafacos +from . import utils from .utils import is_valid_type, check_type_or_throw_except, handle_errors from .analyze cimport partCfg, PartCfg from .particle_data cimport particle @@ -64,13 +65,9 @@ IF ELECTROSTATICS == 1: deactivate_method() handle_errors("Coulomb method deactivation") - def tune(self, **tune_params_subset): - if tune_params_subset is not None: - if all(k in self.valid_keys() for k in tune_params_subset): - self._params.update(tune_params_subset) - else: - raise ValueError( - "Invalid parameter given to tune function.") + def tune(self, **tune_params): + utils.check_valid_keys(self.valid_keys(), tune_params.keys()) + self._params.update(tune_params) self._tune() @@ -95,10 +92,10 @@ IF ELECTROSTATICS: pass def valid_keys(self): - return ["prefactor", "kappa", "r_cut", "check_neutrality"] + return {"prefactor", "kappa", "r_cut", "check_neutrality"} def required_keys(self): - return ["prefactor", "kappa", "r_cut"] + return {"prefactor", "kappa", "r_cut"} def _set_params_in_es_core(self): set_prefactor(self._params["prefactor"]) @@ -144,11 +141,11 @@ IF ELECTROSTATICS: pass def valid_keys(self): - return ["prefactor", "kappa", "epsilon1", "epsilon2", "r_cut", - "check_neutrality"] + return {"prefactor", "kappa", "epsilon1", "epsilon2", "r_cut", + "check_neutrality"} def required_keys(self): - return ["prefactor", "kappa", "epsilon1", "epsilon2", "r_cut"] + return {"prefactor", "kappa", "epsilon1", "epsilon2", "r_cut"} def _set_params_in_es_core(self): set_prefactor(self._params["prefactor"]) @@ -190,12 +187,12 @@ IF P3M == 1: mesh[i] = pmesh[i] def valid_keys(self): - return ["mesh", "cao", "accuracy", "epsilon", "alpha", "r_cut", + return {"mesh", "cao", "accuracy", "epsilon", "alpha", "r_cut", "prefactor", "tune", "check_neutrality", "timings", - "verbose", "mesh_off"] + "verbose", "mesh_off"} def required_keys(self): - return ["prefactor", "accuracy"] + return {"prefactor", "accuracy"} def default_params(self): return {"cao": 0, @@ -477,12 +474,12 @@ IF P3M == 1: "neutralize has to be a bool") def valid_keys(self): - return ["p3m_actor", "maxPWerror", "gap_size", "far_cut", + return {"p3m_actor", "maxPWerror", "gap_size", "far_cut", "neutralize", "delta_mid_top", "delta_mid_bot", - "const_pot", "pot_diff", "check_neutrality"] + "const_pot", "pot_diff", "check_neutrality"} def required_keys(self): - return ["p3m_actor", "maxPWerror", "gap_size"] + return {"p3m_actor", "maxPWerror", "gap_size"} def default_params(self): return {"maxPWerror": -1, @@ -576,12 +573,12 @@ IF ELECTROSTATICS: "verbose": True} def valid_keys(self): - return ["prefactor", "maxPWerror", "far_switch_radius", + return {"prefactor", "maxPWerror", "far_switch_radius", "bessel_cutoff", "tune", "check_neutrality", "timings", - "verbose"] + "verbose"} def required_keys(self): - return ["prefactor", "maxPWerror"] + return {"prefactor", "maxPWerror"} def _get_params_from_es_core(self): params = {} @@ -662,11 +659,11 @@ IF ELECTROSTATICS and MMM1D_GPU: "check_neutrality": True} def valid_keys(self): - return ["prefactor", "maxPWerror", "far_switch_radius", - "bessel_cutoff", "tune", "check_neutrality"] + return {"prefactor", "maxPWerror", "far_switch_radius", + "bessel_cutoff", "tune", "check_neutrality"} def required_keys(self): - return ["prefactor", "maxPWerror"] + return {"prefactor", "maxPWerror"} def _get_params_from_es_core(self): params = {} diff --git a/src/python/espressomd/integrate.pyx b/src/python/espressomd/integrate.pyx index 96d7a0db2b2..9851626ab2f 100644 --- a/src/python/espressomd/integrate.pyx +++ b/src/python/espressomd/integrate.pyx @@ -20,6 +20,7 @@ from cpython.exc cimport PyErr_CheckSignals, PyErr_SetInterrupt include "myconfig.pxi" from .utils cimport check_type_or_throw_except from .utils import handle_errors +from . import utils from . cimport integrate cdef class IntegratorHandle: @@ -158,13 +159,7 @@ cdef class Integrator: return self.__getstate__() def __init__(self, *args, **kwargs): - - # Check if all required keys are given - for k in self.required_keys(): - if k not in kwargs: - raise ValueError( - "At least the following keys have to be given as keyword arguments: " + self.required_keys().__str__()) - + utils.check_required_keys(self.required_keys(), kwargs.keys()) self._params = self.default_params() self._params.update(kwargs) self.validate_params() @@ -322,13 +317,13 @@ cdef class VelocityVerlet(Integrator): """All parameters that can be set. """ - return {} + return set() def required_keys(self): """Parameters that have to be set. """ - return {} + return set() def validate_params(self): return True @@ -411,13 +406,13 @@ cdef class BrownianDynamics(Integrator): """All parameters that can be set. """ - return {} + return set() def required_keys(self): """Parameters that have to be set. """ - return {} + return set() def validate_params(self): return True diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 19158423ccb..9b82e08cd61 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -22,6 +22,7 @@ cimport cpython.object import collections include "myconfig.pxi" +from . import utils from .utils import is_valid_type from .utils cimport check_type_or_throw_except from .script_interface import ScriptObjectRegistry, ScriptInterfaceHelper, script_interface_register @@ -56,16 +57,11 @@ cdef class NonBondedInteraction: # Or have we been called with keyword args describing the interaction elif len(args) == 0: + utils.check_required_keys(self.required_keys(), kwargs.keys()) + utils.check_valid_keys(self.valid_keys(), kwargs.keys()) # Initialize default values self._params = self.default_params() self._part_types = [-1, -1] - - # Check if all required keys are given - for k in self.required_keys(): - if k not in kwargs: - raise ValueError( - "At least the following keys have to be given as keyword arguments: " + self.required_keys().__str__()) - self._params.update(kwargs) self.validate_params() else: @@ -111,18 +107,12 @@ cdef class NonBondedInteraction: """ # Check, if any key was passed, which is not known - for k in p.keys(): - if k not in self.valid_keys(): - raise ValueError( - "Only the following keys are supported: " + self.valid_keys().__str__()) + utils.check_valid_keys(self.valid_keys(), p.keys()) # When an interaction is newly activated, all required keys must be # given if not self.is_active(): - for k in self.required_keys(): - if k not in p: - raise ValueError( - "At least the following keys have to be given as keyword arguments: " + self.required_keys().__str__()) + utils.check_required_keys(self.required_keys(), p.keys()) # If this instance refers to an interaction defined in the ESPResSo core, # load the parameters from there @@ -1696,7 +1686,7 @@ class BondedInteraction(ScriptInterfaceHelper): params.update(kwargs) self.validate_params(params) super().__init__(*args, **params) - self._check_keys(params.keys(), check_required=True) + utils.check_valid_keys(self.valid_keys(), kwargs.keys()) self._ctor_params = params self._bond_id = -1 else: @@ -1706,21 +1696,6 @@ class BondedInteraction(ScriptInterfaceHelper): self._bond_id = -1 self._ctor_params = self._get_params_from_es_core() - def _check_keys(self, keys, check_required=False): - def err_msg(key_set): - return f'{{{", ".join(key_set)}}}' - - if check_required: - for required_key in self.required_keys(): - if required_key not in keys: - raise ValueError( - f"At least the following keys have to be given as keyword arguments: {err_msg(self.required_keys())}") - - for key in keys: - if key not in self.valid_keys(): - raise ValueError( - f"Key '{key}' invalid! Only the following keys are supported: {err_msg(self.valid_keys())}") - def __reduce__(self): if self._bond_id != -1: # checkpointing constructor #1 diff --git a/src/python/espressomd/magnetostatics.pyx b/src/python/espressomd/magnetostatics.pyx index 3dc0571a3bf..c3dfea76264 100644 --- a/src/python/espressomd/magnetostatics.pyx +++ b/src/python/espressomd/magnetostatics.pyx @@ -131,12 +131,12 @@ IF DP3M == 1: raise ValueError("DipolarP3M timings must be > 0") def valid_keys(self): - return ["prefactor", "alpha_L", "r_cut_iL", "mesh", "mesh_off", + return {"prefactor", "alpha_L", "r_cut_iL", "mesh", "mesh_off", "cao", "accuracy", "epsilon", "cao_cut", "a", "ai", - "alpha", "r_cut", "cao3", "tune", "timings", "verbose"] + "alpha", "r_cut", "cao3", "tune", "timings", "verbose"} def required_keys(self): - return ["accuracy", ] + return {"accuracy"} def default_params(self): return {"cao": -1, @@ -218,10 +218,10 @@ IF DIPOLES == 1: return {} def required_keys(self): - return () + return set() def valid_keys(self): - return ("prefactor",) + return {"prefactor"} def _get_params_from_es_core(self): return {"prefactor": self.get_magnetostatics_prefactor()} @@ -256,10 +256,10 @@ IF DIPOLES == 1: return {} def required_keys(self): - return ("n_replica",) + return {"n_replica"} def valid_keys(self): - return ("prefactor", "n_replica") + return {"prefactor", "n_replica"} def _get_params_from_es_core(self): return {"prefactor": self.get_magnetostatics_prefactor(), @@ -341,10 +341,10 @@ IF DIPOLES == 1: return {} def required_keys(self): - return () + return set() def valid_keys(self): - return ("prefactor",) + return {"prefactor"} def _get_params_from_es_core(self): return {"prefactor": self.get_magnetostatics_prefactor()} @@ -382,10 +382,10 @@ IF DIPOLES == 1: "itolsq": 4.0} def required_keys(self): - return () + return set() def valid_keys(self): - return ("prefactor", "epssq", "itolsq") + return {"prefactor", "epssq", "itolsq"} def _get_params_from_es_core(self): return {"prefactor": self.get_magnetostatics_prefactor()} diff --git a/src/python/espressomd/polymer.pyx b/src/python/espressomd/polymer.pyx index 40e5ae68536..93c912554b4 100644 --- a/src/python/espressomd/polymer.pyx +++ b/src/python/espressomd/polymer.pyx @@ -19,6 +19,7 @@ include "myconfig.pxi" from . cimport polymer +from . import utils import numpy as np from .system import System from .interactions import BondedInteraction @@ -130,17 +131,9 @@ def linear_polymer_positions(**kwargs): required_keys = ["n_polymers", "beads_per_chain", "bond_length", "seed"] - for k in kwargs: - if k not in valid_keys: - raise ValueError(f"Unknown parameter '{k}'") - params[k] = kwargs[k] - - for k in required_keys: - if k not in kwargs: - print(k) - raise ValueError( - "At least the following keys have to be given as keyword arguments: " + required_keys.__str__()) - + utils.check_required_keys(required_keys, kwargs.keys()) + utils.check_valid_keys(valid_keys, kwargs.keys()) + params.update(kwargs) validate_params(params, default_params) cdef vector[Vector3d] start_positions diff --git a/src/python/espressomd/utils.pyx b/src/python/espressomd/utils.pyx index c2d9860c644..7b465b771a1 100644 --- a/src/python/espressomd/utils.pyx +++ b/src/python/espressomd/utils.pyx @@ -315,3 +315,21 @@ def requires_experimental_features(reason): ELSE: # Return original class return lambda x: x + + +def check_required_keys(required_keys, obtained_keys): + a = required_keys + b = obtained_keys + if not set(a).issubset(b): + raise ValueError( + "The following keys have to be given as keyword arguments: " + f"{sorted(a)}, got {sorted(b)} (missing {sorted(a - b)})") + + +def check_valid_keys(valid_keys, obtained_keys): + a = valid_keys + b = obtained_keys + if not set(b).issubset(a): + raise ValueError( + "Only the following keys can be given as keyword arguments: " + f"{sorted(a)}, got {sorted(b)} (unknown {sorted(b - a)})") diff --git a/testsuite/python/actor.py b/testsuite/python/actor.py index 9789f59573f..897bf1c7ba0 100644 --- a/testsuite/python/actor.py +++ b/testsuite/python/actor.py @@ -44,10 +44,10 @@ def _set_params_in_es_core(self): self._core_args = self._params def valid_keys(self): - return "a", "b", "c" + return {"a", "b", "c"} def required_keys(self): - return "a", "c" + return {"a", "c"} def default_params(self): return {"a": False, "b": False, "c": False} @@ -108,6 +108,21 @@ def test_deactivation(self): self.assertEqual(params["b"], False) self.assertEqual(params["c"], True) + def test_exception(self): + error_msg_valid = (r"Only the following keys can be given as keyword arguments: " + r"\['a', 'b', 'c'\], got \['a', 'c', 'd'\] \(unknown \['d'\]\)") + error_msg_required = (r"The following keys have to be given as keyword arguments: " + r"\['a', 'c'\], got \['a'\] \(missing \['c'\]\)") + with self.assertRaisesRegex(ValueError, error_msg_valid): + TestActor(a=True, c=True, d=True) + with self.assertRaisesRegex(ValueError, error_msg_required): + TestActor(a=True) + valid_actor = TestActor(a=True, c=True) + with self.assertRaisesRegex(ValueError, error_msg_valid): + valid_actor.set_params(a=True, c=True, d=True) + with self.assertRaisesRegex(ValueError, error_msg_required): + valid_actor.set_params(a=True) + class ActorsTest(ut.TestCase): diff --git a/testsuite/python/ek_charged_plate.py b/testsuite/python/ek_charged_plate.py index 6b13be4381b..d8976d4b161 100644 --- a/testsuite/python/ek_charged_plate.py +++ b/testsuite/python/ek_charged_plate.py @@ -164,11 +164,18 @@ def test(self): negative_ions[i, j, 30].density = 0.0 # Test error when trying to change ekin parameters after initialisation - ek._params.update({'agrid': 3, - 'T': 0.01}) with self.assertRaises(RuntimeError): + ek._params.update({'agrid': 3, 'T': 0.01}) ek._set_params_in_es_core() + # Check errors from the constructor + with self.assertRaisesRegex(ValueError, r"The following keys have to be given as keyword arguments: " + r"\[.+\], got \[.+\] \(missing \['D'\]\)"): + espressomd.electrokinetics.Species(density=0, valency=1) + with self.assertRaisesRegex(ValueError, r"Only the following keys can be given as keyword arguments: " + r"\[.+\], got \[.+\] \(unknown \['U'\]\)"): + espressomd.electrokinetics.Species(density=0, valency=1, D=0, U=1) + if __name__ == "__main__": ut.main() diff --git a/testsuite/python/electrostaticInteractions.py b/testsuite/python/electrostaticInteractions.py index e818bebfbd0..a9054f7ee3b 100644 --- a/testsuite/python/electrostaticInteractions.py +++ b/testsuite/python/electrostaticInteractions.py @@ -242,6 +242,14 @@ def test_rf_exceptions(self): self.system.actors.add(rf) self.system.actors.clear() + valid_actor = espressomd.electrostatics.ReactionField( + **params, prefactor=1.0) + with self.assertRaisesRegex(Exception, "chosen method does not support tuning"): + valid_actor.tune() + with self.assertRaisesRegex(ValueError, r"Only the following keys can be given as keyword arguments: " + r"\[.+\], got \[.+\] \(unknown \['coulomb_prefactor'\]\)"): + valid_actor.tune(coulomb_prefactor=1.0) + if __name__ == "__main__": ut.main() diff --git a/testsuite/python/integrator_exceptions.py b/testsuite/python/integrator_exceptions.py index db1ec33cc27..08360150939 100644 --- a/testsuite/python/integrator_exceptions.py +++ b/testsuite/python/integrator_exceptions.py @@ -65,6 +65,11 @@ def test_stokesian_integrator(self): self.system.integrator.run(0) def test_steepest_descent_integrator(self): + with self.assertRaisesRegex(ValueError, r"The following keys have to be given as keyword arguments: " + r"\['f_max', 'gamma', 'max_displacement'\], got " + r"\['f_max', 'gamma', 'max_d'\] \(missing \['max_displacement'\]\)"): + self.system.integrator.set_steepest_descent( + f_max=0, gamma=0.1, max_d=5) self.system.thermostat.set_langevin(kT=1.0, gamma=1.0, seed=42) self.system.integrator.set_steepest_descent( f_max=0, gamma=0.1, max_displacement=0.1) diff --git a/testsuite/python/interactions_bonded_interface.py b/testsuite/python/interactions_bonded_interface.py index ea1865372bb..dda1e79f3f4 100644 --- a/testsuite/python/interactions_bonded_interface.py +++ b/testsuite/python/interactions_bonded_interface.py @@ -253,6 +253,10 @@ def test_exceptions(self): # sanity checks during bond construction with self.assertRaisesRegex(RuntimeError, "Parameter 'r_0' is missing"): espressomd.interactions.HarmonicBond(k=1.) + with self.assertRaisesRegex(ValueError, r"Only the following keys can be given as keyword arguments: " + r"\['k', 'r_0', 'r_cut'\], got \['k', 'r_0', 'rcut'\] " + r"\(unknown \['rcut'\]\)"): + espressomd.interactions.HarmonicBond(k=1., r_0=1., rcut=2.) with self.assertRaisesRegex(ValueError, "Unknown refShape: 'Unknown'"): espressomd.interactions.IBM_Tribend( ind1=0, ind2=1, ind3=2, ind4=3, kb=1.1, refShape='Unknown') diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index 629bf0669a6..d893f28ef79 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -17,6 +17,7 @@ # along with this program. If not, see . # import unittest as ut +import unittest_decorators as utx import tests_common import espressomd @@ -156,6 +157,27 @@ def func(self): "k2": 5.0, "mu": 2.0, "nu": 1.0}, "gay_berne") + @utx.skipIfMissingFeatures("LENNARD_JONES") + def test_exceptions(self): + err_msg_required = (r"The following keys have to be given as keyword arguments: " + r"\['cutoff', 'epsilon', 'shift', 'sigma'\], got " + r"\['epsilon', 'sigma'\] \(missing \['cutoff', 'shift'\]\)") + err_msg_valid = (r"Only the following keys can be given as keyword arguments: " + r"\['cutoff', 'epsilon', 'min', 'offset', 'shift', 'sigma'\], got " + r"\['cutoff', 'epsilon', 'shift', 'sigma', 'unknown'\] \(unknown \['unknown'\]\)") + with self.assertRaisesRegex(ValueError, err_msg_required): + espressomd.interactions.LennardJonesInteraction( + epsilon=1., sigma=2.) + with self.assertRaisesRegex(ValueError, err_msg_required): + self.system.non_bonded_inter[0, 0].lennard_jones.set_params( + epsilon=1., sigma=2.) + with self.assertRaisesRegex(ValueError, err_msg_valid): + espressomd.interactions.LennardJonesInteraction( + epsilon=1., sigma=2., cutoff=3., shift=4., unknown=5.) + with self.assertRaisesRegex(ValueError, err_msg_valid): + self.system.non_bonded_inter[0, 0].lennard_jones.set_params( + epsilon=1., sigma=2., cutoff=3., shift=4., unknown=5.) + if __name__ == "__main__": ut.main() diff --git a/testsuite/python/polymer_linear.py b/testsuite/python/polymer_linear.py index 4c466ff0924..91e2f2eabbe 100644 --- a/testsuite/python/polymer_linear.py +++ b/testsuite/python/polymer_linear.py @@ -198,11 +198,20 @@ def test_respect_constraints_wall(self): respect_constraints=True, seed=self.seed) self.system.constraints.remove(wall_constraint) - def test_failure(self): + def test_exceptions(self): """ - Check the runtime error message. + Check runtime error messages. """ + with self.assertRaisesRegex(ValueError, r"The following keys have to be given as keyword arguments: " + r"\[.+\], got \[.+\] \(missing \['seed'\]\)"): + espressomd.polymer.linear_polymer_positions( + n_polymers=1, beads_per_chain=10, bond_length=0.1) + with self.assertRaisesRegex(ValueError, r"Only the following keys can be given as keyword arguments: " + r"\[.+\], got \[.+\] \(unknown \['bondangle'\]\)"): + espressomd.polymer.linear_polymer_positions( + n_polymers=1, beads_per_chain=10, bond_length=0.1, seed=10, + bondangle=0.1) with self.assertRaisesRegex(Exception, 'Failed to create polymer positions.'): espressomd.polymer.linear_polymer_positions( n_polymers=1, beads_per_chain=10, From 8b976f5816d605b1aceb1eed875fd7b4dbb4e9ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 1 Feb 2022 13:31:02 +0100 Subject: [PATCH 32/99] CI: Shorten job names --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3a18e9ebdca..ac59628b513 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -488,7 +488,7 @@ run_doxygen: - docker - linux -check_cuda_maxset_no_gpu: +maxset_no_gpu: <<: *global_job_definition stage: additional_checks when: on_success @@ -502,7 +502,7 @@ check_cuda_maxset_no_gpu: - docker - linux -check_with_odd_no_of_processors: +maxset_3_cores: <<: *global_job_definition stage: additional_checks when: on_success From e9bfc988f3662a146b06fa8bc03c5d00e5198b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 1 Feb 2022 14:03:00 +0100 Subject: [PATCH 33/99] CI: Switch to new docker image registry --- .gitlab-ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ac59628b513..c8a54090ae1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: docker.pkg.github.com/espressomd/docker/ubuntu-20.04:254edd4a9c6e4d7b557be73158e400f5794e4f99 +image: ghcr.io/espressomd/docker/ubuntu-20.04:254edd4a9c6e4d7b557be73158e400f5794e4f99 stages: - prepare @@ -127,7 +127,7 @@ no_rotation: ubuntu:wo-dependencies: <<: *global_job_definition stage: build - image: docker.pkg.github.com/espressomd/docker/ubuntu-wo-dependencies:9ef2166b82d4c0eb258d17f5ec29f7bc39991f5d + image: ghcr.io/espressomd/docker/ubuntu-wo-dependencies:9ef2166b82d4c0eb258d17f5ec29f7bc39991f5d variables: myconfig: 'maxset' with_cuda: 'false' @@ -145,7 +145,7 @@ ubuntu:wo-dependencies: debian:10: <<: *global_job_definition stage: build - image: docker.pkg.github.com/espressomd/docker/debian:9ef2166b82d4c0eb258d17f5ec29f7bc39991f5d + image: ghcr.io/espressomd/docker/debian:9ef2166b82d4c0eb258d17f5ec29f7bc39991f5d variables: with_cuda: 'false' myconfig: 'maxset' @@ -160,7 +160,7 @@ debian:10: fedora:34: <<: *global_job_definition stage: build - image: docker.pkg.github.com/espressomd/docker/fedora:9ef2166b82d4c0eb258d17f5ec29f7bc39991f5d + image: ghcr.io/espressomd/docker/fedora:9ef2166b82d4c0eb258d17f5ec29f7bc39991f5d variables: with_cuda: 'false' myconfig: 'maxset' @@ -201,7 +201,7 @@ clang-sanitizer: fast_math: <<: *global_job_definition stage: build - image: docker.pkg.github.com/espressomd/docker/cuda:9ef2166b82d4c0eb258d17f5ec29f7bc39991f5d + image: ghcr.io/espressomd/docker/cuda:9ef2166b82d4c0eb258d17f5ec29f7bc39991f5d variables: CC: 'gcc-9' CXX: 'g++-9' @@ -222,7 +222,7 @@ fast_math: cuda11-maxset: <<: *global_job_definition stage: build - image: docker.pkg.github.com/espressomd/docker/cuda:9ef2166b82d4c0eb258d17f5ec29f7bc39991f5d + image: ghcr.io/espressomd/docker/cuda:9ef2166b82d4c0eb258d17f5ec29f7bc39991f5d variables: CC: 'gcc-9' CXX: 'g++-9' From 283d35be7b3eabd4a5a1e2643ef00044efa45b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 2 Feb 2022 18:32:49 +0100 Subject: [PATCH 34/99] CMake: Detect MPI backend --- CMakeLists.txt | 9 +++++++ cmake/FindMpiexecBackend.cmake | 48 ++++++++++++++++++++++++++++++++++ cmake/unit_test.cmake | 14 ---------- 3 files changed, 57 insertions(+), 14 deletions(-) create mode 100644 cmake/FindMpiexecBackend.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d42624323b..b407ca9b5ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -275,6 +275,15 @@ endif(WITH_VALGRIND_INSTRUMENTATION) # find_package(MPI 3.0 REQUIRED) +find_package(MpiexecBackend) + +# OpenMPI checks the number of processes against the number of CPUs +if("${MPIEXEC_BACKEND_NAME}" STREQUAL "OpenMPI" AND "${MPIEXEC_BACKEND_VERSION}" + VERSION_GREATER_EQUAL 2.0.0) + set(MPIEXEC_OVERSUBSCRIBE "-oversubscribe") +else() + set(MPIEXEC_OVERSUBSCRIBE "") +endif() # # Boost diff --git a/cmake/FindMpiexecBackend.cmake b/cmake/FindMpiexecBackend.cmake new file mode 100644 index 00000000000..3ec12502150 --- /dev/null +++ b/cmake/FindMpiexecBackend.cmake @@ -0,0 +1,48 @@ +# +# Copyright (C) 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 . +# + +# Find the MPI backend. +# +# This code sets the following variables: +# +# MPIEXEC_BACKEND_NAME MPIEXEC_BACKEND_VERSION + +set(MPIEXEC_BACKEND_NAME "unknown") +set(MPIEXEC_BACKEND_VERSION 0.0.0) + +execute_process( + COMMAND ${MPIEXEC} --version RESULT_VARIABLE mpi_version_result + OUTPUT_VARIABLE mpi_version_output ERROR_VARIABLE mpi_version_output) +if(mpi_version_result EQUAL 0) + if(mpi_version_output MATCHES "Intel\\(R\\) MPI Library") + set(MPIEXEC_BACKEND_NAME "Intel") + string(REGEX REPLACE ".*Build ([0-9]+).*" "\\1" MPIEXEC_BACKEND_VERSION ${mpi_version_output}) + endif() + if(mpi_version_output MATCHES "HYDRA") + set(MPIEXEC_BACKEND_NAME "MPICH") + string(REGEX REPLACE ".*Version: +([0-9\\.]+).*" "\\1" MPIEXEC_BACKEND_VERSION ${mpi_version_output}) + endif() + if(mpi_version_output MATCHES "\\(Open(RTE| MPI)\\)") + set(MPIEXEC_BACKEND_NAME "OpenMPI") + string(REGEX REPLACE ".*\\(Open(RTE| MPI)\\) ([0-9\\.]+).*" "\\2" MPIEXEC_BACKEND_VERSION ${mpi_version_output}) + endif() +endif() + +include( FindPackageHandleStandardArgs ) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(MpiexecBackend REQUIRED_VARS MPIEXEC) diff --git a/cmake/unit_test.cmake b/cmake/unit_test.cmake index 014ad52c49c..7c22099244f 100644 --- a/cmake/unit_test.cmake +++ b/cmake/unit_test.cmake @@ -1,17 +1,3 @@ -if(EXISTS ${MPIEXEC}) - # OpenMPI 2.0 and higher checks the number of processes against the number of - # CPUs - execute_process( - COMMAND ${MPIEXEC} --version RESULT_VARIABLE mpi_version_result - OUTPUT_VARIABLE mpi_version_output ERROR_VARIABLE mpi_version_output) - if(mpi_version_result EQUAL 0 AND mpi_version_output MATCHES - "\\(Open(RTE| MPI)\\) ([2-9]\\.|1[0-9])") - set(MPIEXEC_OVERSUBSCRIBE "-oversubscribe") - else() - set(MPIEXEC_OVERSUBSCRIBE "") - endif() -endif() - # unit_test function function(UNIT_TEST) cmake_parse_arguments(TEST "" "NAME;NUM_PROC" "SRC;DEPENDS" ${ARGN}) From c2a7ea2b7a5e3112a4606d1bfb813d6f2ad3c7bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 2 Feb 2022 20:04:35 +0100 Subject: [PATCH 35/99] CMake: Fix race condition in OpenMPI OpenMPI store the process id and other relevant information in a folder named /tmp/ompi.hostname.userid, which is deleted when the test ends. This leads to a race condition when multiple MPI tests run in parallel. The folder name is now unique for each test. --- CMakeLists.txt | 18 ++++++++++++++++++ cmake/unit_test.cmake | 5 +++-- maintainer/CI/build_cmake.sh | 2 +- testsuite/python/CMakeLists.txt | 6 ++++-- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b407ca9b5ba..e9c9a0286b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,6 +98,7 @@ endif() option(WARNINGS_ARE_ERRORS "Treat warnings as errors during compilation" OFF) option(WITH_CCACHE "Use ccache compiler invocation." OFF) option(WITH_PROFILER "Enable profiler annotations." OFF) +option(INSIDE_DOCKER "Enable when running inside Docker." OFF) set(TEST_TIMEOUT "300" CACHE STRING "Timeout in seconds for each testsuite test") @@ -285,6 +286,23 @@ else() set(MPIEXEC_OVERSUBSCRIBE "") endif() +# OpenMPI cannot run two jobs in parallel in a Docker container, because the +# same base folder is used to store the process ids of multiple jobs. Since the +# base folder is deleted upon completion of a job, other jobs will fail when +# attempting to create subdirectories in the base folder. +# https://github.com/open-mpi/ompi/issues/8510 +if("${MPIEXEC_BACKEND_NAME}" STREQUAL "OpenMPI" AND INSIDE_DOCKER) + cmake_host_system_information(RESULT hostname QUERY HOSTNAME) + function(set_mpiexec_tmpdir) + set(MPIEXEC_TMPDIR --mca orte_tmpdir_base + "/tmp/ompi.${hostname}.$ENV{USER}.${ARGV0}" PARENT_SCOPE) + endfunction() +else() + function(set_mpiexec_tmpdir) + set(MPIEXEC_TMPDIR "" PARENT_SCOPE) + endfunction() +endif() + # # Boost # diff --git a/cmake/unit_test.cmake b/cmake/unit_test.cmake index 7c22099244f..225ef71be1c 100644 --- a/cmake/unit_test.cmake +++ b/cmake/unit_test.cmake @@ -16,10 +16,11 @@ function(UNIT_TEST) if(${TEST_NUM_PROC} GREATER ${TEST_NP}) set(TEST_NUM_PROC ${TEST_NP}) endif() - + set_mpiexec_tmpdir("${TEST_NAME}") add_test(${TEST_NAME} ${MPIEXEC} ${MPIEXEC_OVERSUBSCRIBE} ${MPIEXEC_NUMPROC_FLAG} ${TEST_NUM_PROC} ${MPIEXEC_PREFLAGS} - ${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME} ${MPIEXEC_POSTFLAGS}) + ${MPIEXEC_TMPDIR} ${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME} + ${MPIEXEC_POSTFLAGS}) else() add_test(${TEST_NAME} ${TEST_NAME}) endif() diff --git a/maintainer/CI/build_cmake.sh b/maintainer/CI/build_cmake.sh index 023c0f53a0c..91b44886c75 100755 --- a/maintainer/CI/build_cmake.sh +++ b/maintainer/CI/build_cmake.sh @@ -122,7 +122,7 @@ if [ "${with_fast_math}" = true ]; then fi cmake_params="-DCMAKE_BUILD_TYPE=${build_type} -DCMAKE_CXX_STANDARD=${with_cxx_standard} -DWARNINGS_ARE_ERRORS=ON ${cmake_params}" -cmake_params="${cmake_params} -DCMAKE_INSTALL_PREFIX=/tmp/espresso-unit-tests" +cmake_params="${cmake_params} -DCMAKE_INSTALL_PREFIX=/tmp/espresso-unit-tests -DINSIDE_DOCKER=ON" cmake_params="${cmake_params} -DCTEST_ARGS=-j${check_procs} -DTEST_TIMEOUT=${test_timeout}" if [ "${make_check_benchmarks}" = true ]; then diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index 492a642c719..2066db37756 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -23,12 +23,14 @@ function(PYTHON_TEST) endif() if(EXISTS ${MPIEXEC}) + set_mpiexec_tmpdir("${TEST_NAME}") add_test( NAME ${TEST_NAME} COMMAND ${MPIEXEC} ${MPIEXEC_OVERSUBSCRIBE} ${MPIEXEC_NUMPROC_FLAG} - ${TEST_NUM_PROC} ${MPIEXEC_PREFLAGS} ${CMAKE_BINARY_DIR}/pypresso - ${PYPRESSO_OPTIONS} ${TEST_FILE} ${MPIEXEC_POSTFLAGS}) + ${TEST_NUM_PROC} ${MPIEXEC_PREFLAGS} ${MPIEXEC_TMPDIR} + ${CMAKE_BINARY_DIR}/pypresso ${PYPRESSO_OPTIONS} ${TEST_FILE} + ${MPIEXEC_POSTFLAGS}) else() add_test(${TEST_NAME} ${CMAKE_BINARY_DIR}/pypresso ${PYPRESSO_OPTIONS} ${TEST_FILE}) From 43b146cbb83f9348c624f0748414194c74e5631b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 3 Feb 2022 13:22:49 +0100 Subject: [PATCH 36/99] maintainer: Improve benchmark scripts --- maintainer/benchmarks/runner.sh | 2 +- maintainer/benchmarks/suite.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/maintainer/benchmarks/runner.sh b/maintainer/benchmarks/runner.sh index 0f8130672ed..1153936a7a7 100644 --- a/maintainer/benchmarks/runner.sh +++ b/maintainer/benchmarks/runner.sh @@ -36,7 +36,7 @@ for config in ${configs}; do # add minimal features for the benchmarks to run sed -i '1 i\#define ELECTROSTATICS\n#define LENNARD_JONES\n#define MASS\n' "${config}" # remove checks - sed -ri "s/#define\s+ADDITIONAL_CHECKS//" "${config}" + sed -ri "/#define\s+ADDITIONAL_CHECKS/d" "${config}" done cat > benchmarks.csv << EOF diff --git a/maintainer/benchmarks/suite.sh b/maintainer/benchmarks/suite.sh index 4e49fb14df6..2c6a223a23b 100644 --- a/maintainer/benchmarks/suite.sh +++ b/maintainer/benchmarks/suite.sh @@ -51,7 +51,7 @@ cd "${build_dir}" # check for unstaged changes if [ -n "$(git status --porcelain -- ${directories})" ]; then echo "fatal: you have unstaged changes, please commit or stash them:" - git diff-index --name-only HEAD -- ${directories} + git status --porcelain -- ${directories} exit 1 fi From 7f52c22cbdfb51c79c401fb91427b9e5f05e56ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 4 Feb 2022 10:31:55 +0100 Subject: [PATCH 37/99] maintainer: Skip benchmarks with missing features --- maintainer/benchmarks/CMakeLists.txt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/maintainer/benchmarks/CMakeLists.txt b/maintainer/benchmarks/CMakeLists.txt index 25213e94aba..5a7f576b572 100644 --- a/maintainer/benchmarks/CMakeLists.txt +++ b/maintainer/benchmarks/CMakeLists.txt @@ -15,6 +15,12 @@ if(EXISTS ${MPIEXEC}) endif() endif() +function(SET_BENCHMARK_PROPERTIES) + set_tests_properties( + ${ARGV0} PROPERTIES RUN_SERIAL TRUE SKIP_REGULAR_EXPRESSION + "espressomd.FeaturesError: Missing features") +endfunction() + function(PYTHON_BENCHMARK) cmake_parse_arguments( BENCHMARK "" "FILE;RUN_WITH_MPI;MIN_NUM_PROC;MAX_NUM_PROC" @@ -63,14 +69,14 @@ function(PYTHON_BENCHMARK) ${MPIEXEC} ${MPIEXEC_OVERSUBSCRIBE} ${MPIEXEC_NUMPROC_FLAG} ${nproc} ${MPIEXEC_PREFLAGS} ${CMAKE_BINARY_DIR}/pypresso ${BENCHMARK_FILE} ${BENCHMARK_ARGUMENTS} ${MPIEXEC_POSTFLAGS}) - set_tests_properties(${BENCHMARK_TEST_NAME} PROPERTIES RUN_SERIAL TRUE) + set_benchmark_properties(${BENCHMARK_TEST_NAME}) endforeach(nproc) else() set(BENCHMARK_TEST_NAME benchmark__${BENCHMARK_NAME}__serial) add_test(NAME ${BENCHMARK_TEST_NAME} COMMAND ${CMAKE_BINARY_DIR}/pypresso ${BENCHMARK_FILE} ${BENCHMARK_ARGUMENTS}) - set_tests_properties(${BENCHMARK_TEST_NAME} PROPERTIES RUN_SERIAL TRUE) + set_benchmark_properties(${BENCHMARK_TEST_NAME}) endif() endfunction(PYTHON_BENCHMARK) From 088d6176b8f33df8747a6d7caaacacd1c247b48f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 4 Feb 2022 12:31:52 +0100 Subject: [PATCH 38/99] CMake: Fix MAX_NUM_PROC logic --- testsuite/python/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index 2066db37756..b716de35860 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -13,7 +13,7 @@ function(PYTHON_TEST) set(TEST_FILE ${TEST_FILE_CONFIGURED}) if(NOT DEFINED TEST_MAX_NUM_PROC) - set(TEST_MAX_NUM_PROC ${TEST_NP}) + set(TEST_MAX_NUM_PROC 1) endif() if(${TEST_MAX_NUM_PROC} GREATER ${TEST_NP}) @@ -100,7 +100,7 @@ python_test(FILE dds-and-bh-gpu.py MAX_NUM_PROC 4 LABELS gpu) python_test(FILE electrostaticInteractions.py MAX_NUM_PROC 2) python_test(FILE engine_langevin.py MAX_NUM_PROC 4) python_test(FILE engine_lb.py MAX_NUM_PROC 2 LABELS gpu) -python_test(FILE experimental_decorator.py) +python_test(FILE experimental_decorator.py MAX_NUM_PROC 1) python_test(FILE icc.py MAX_NUM_PROC 4) python_test(FILE mass-and-rinertia_per_particle.py MAX_NUM_PROC 2 LABELS long) python_test(FILE integrate.py MAX_NUM_PROC 4) @@ -223,7 +223,7 @@ python_test(FILE mpiio.py MAX_NUM_PROC 4) python_test(FILE gpu_availability.py MAX_NUM_PROC 2 LABELS gpu) python_test(FILE features.py MAX_NUM_PROC 1) python_test(FILE decorators.py MAX_NUM_PROC 1) -python_test(FILE galilei.py MAX_NUM_PROC 32) +python_test(FILE galilei.py MAX_NUM_PROC 4) python_test(FILE linear_momentum.py MAX_NUM_PROC 4) python_test(FILE linear_momentum_lb.py MAX_NUM_PROC 2 LABELS gpu) python_test(FILE mmm1d.py MAX_NUM_PROC 2) From d1623194ecd59bf993eb6368bd23dc6c1910d37d Mon Sep 17 00:00:00 2001 From: Patrick Kreissl Date: Fri, 4 Feb 2022 12:56:22 +0100 Subject: [PATCH 39/99] Rename DomainDecomposition->RegularDecomposition. --- doc/sphinx/system_setup.rst | 16 +++--- doc/sphinx/under_the_hood.rst | 6 +- doc/tutorials/constant_pH/constant_pH.ipynb | 2 +- src/core/CMakeLists.txt | 4 +- src/core/CellStructure.cpp | 8 +-- src/core/CellStructure.hpp | 12 ++-- ...mposition.cpp => RegularDecomposition.cpp} | 56 +++++++++---------- ...mposition.hpp => RegularDecomposition.hpp} | 26 ++++----- src/core/cells.cpp | 12 ++-- src/core/cells.hpp | 10 ++-- .../electrostatics_magnetostatics/fft.cpp | 6 +- .../p3m-dipolar.cpp | 4 +- .../electrostatics_magnetostatics/p3m.cpp | 4 +- src/core/event.cpp | 4 +- src/core/grid_based_algorithms/lb.cpp | 4 +- .../object-in-fluid/oif_global_forces.hpp | 2 +- src/python/espressomd/cellsystem.pxd | 8 +-- src/python/espressomd/cellsystem.pyx | 22 ++++---- 18 files changed, 103 insertions(+), 103 deletions(-) rename src/core/{DomainDecomposition.cpp => RegularDecomposition.cpp} (91%) rename src/core/{DomainDecomposition.hpp => RegularDecomposition.hpp} (91%) diff --git a/doc/sphinx/system_setup.rst b/doc/sphinx/system_setup.rst index 84f9569606d..7a260284b25 100644 --- a/doc/sphinx/system_setup.rst +++ b/doc/sphinx/system_setup.rst @@ -120,21 +120,21 @@ Details about the cell system can be obtained by :meth:`espressomd.system.System * ``type`` The current type of the cell system. * ``verlet_reuse`` Average number of integration steps the Verlet list is re-used. -.. _Domain decomposition: +.. _Regular decomposition: -Domain decomposition -~~~~~~~~~~~~~~~~~~~~ +Regular decomposition +~~~~~~~~~~~~~~~~~~~~~ -Invoking :py:meth:`~espressomd.cellsystem.CellSystem.set_domain_decomposition` -selects the domain decomposition cell scheme, using Verlet lists +Invoking :py:meth:`~espressomd.cellsystem.CellSystem.set_regular_decomposition` +selects the regular decomposition cell scheme, using Verlet lists for the calculation of the interactions. If you specify ``use_verlet_lists=False``, only the -domain decomposition is used, but not the Verlet lists. :: +regular decomposition is used, but not the Verlet lists. :: system = espressomd.System(box_l=[1, 1, 1]) - system.cell_system.set_domain_decomposition(use_verlet_lists=True) + system.cell_system.set_regular_decomposition(use_verlet_lists=True) -The domain decomposition cellsystem is the default system and suits most +The regular decomposition cellsystem is the default system and suits most applications with short ranged interactions. The particles are divided up spatially into small compartments, the cells, such that the cell size is larger than the maximal interaction range. In this case interactions diff --git a/doc/sphinx/under_the_hood.rst b/doc/sphinx/under_the_hood.rst index b8e2804397f..f841946de52 100644 --- a/doc/sphinx/under_the_hood.rst +++ b/doc/sphinx/under_the_hood.rst @@ -27,7 +27,7 @@ how they are distributed onto the processor nodes and how they are organized on each of them. Moreover a cell system also defines procedures to efficiently calculate the force, energy and pressure for the short ranged interactions, since these can be heavily optimized -depending on the cell system. For example, the domain decomposition +depending on the cell system. For example, the regular decomposition cellsystem allows an order N interactions evaluation. Technically, a cell is organized as a dynamically growing array, not as @@ -41,7 +41,7 @@ without direct knowledge of the currently used cell system. Only the force, energy and pressure loops are implemented separately for each cell model as explained above. -The domain decomposition or link cell algorithm is implemented such +The regular decomposition or link cell algorithm is implemented such that the cells equal the cells, i.e. each cell is a separate particle list. For an example let us assume that the simulation box has size :math:`20\times 20\times 20` and that we assign 2 processors to the @@ -108,7 +108,7 @@ memory organization of |es|, the particles are accessed in a virtually linear order. Because the force calculation goes through the cells in a linear fashion, all accesses to a single cell occur close in time, for the force calculation of the cell itself as well as for its neighbors. -Using the domain decomposition cell scheme, two cell layers have to be +Using the regular decomposition cell scheme, two cell layers have to be kept in the processor cache. For 10000 particles and a typical cell grid size of 20, these two cell layers consume roughly 200 KBytes, which nearly fits into the L2 cache. Therefore every cell has to be read from diff --git a/doc/tutorials/constant_pH/constant_pH.ipynb b/doc/tutorials/constant_pH/constant_pH.ipynb index 0c8724d48ed..86f6b36268c 100644 --- a/doc/tutorials/constant_pH/constant_pH.ipynb +++ b/doc/tutorials/constant_pH/constant_pH.ipynb @@ -503,7 +503,7 @@ "After the initial relaxation we set the electrostatic interactions between the particles if it has been enabled via the `USE_ELECTROSTATICS` flag. For electrostatics can use either the Debye-Hückel `DH` algorithm or the `P3M` algorithm. The `DH` algorithm is based on the Debye-Hückel approximation, the assumptions of which are not satisfied in our simulated system. However, it runs much faster than the `P3M` algorithm, and the approximate result closely resembles the correct one. Therefore, the `DH` algorithm should not be used in production simulations. By using the `DH` algorithm in this tutorial, we sacrifice the accuracy for speed.\n", "To obtain an accurate result, we can use the `P3M` algorithm using `accuracy` of $10^{-3}$ as an acceptable tradeoff between accuracy and performance. For production runs it might be necessary to use a lower value of `accuracy`, depending on the simulated system.\n", "\n", - "By default, ESPResSo uses the domain decomposition cell system to speed up the calculation of short-range interactions. However, for a system with small number of particles and without electrostatics, it runs faster with `n_square` cell system. See the [user guide](https://espressomd.github.io/doc/system_setup.html#n-squared) for additional details on the cell systems." + "By default, ESPResSo uses the regular decomposition cell system to speed up the calculation of short-range interactions. However, for a system with small number of particles and without electrostatics, it runs faster with `n_square` cell system. See the [user guide](https://espressomd.github.io/doc/system_setup.html#n-squared) for additional details on the cell systems." ] }, { diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8a24cfe27f0..af41473123e 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -28,6 +28,7 @@ set(EspressoCore_SRC rotate_system.cpp rotation.cpp Observable_stat.cpp + RegularDecomposition.cpp RuntimeErrorCollector.cpp RuntimeError.cpp RuntimeErrorStream.cpp @@ -41,8 +42,7 @@ set(EspressoCore_SRC CellStructure.cpp PartCfg.cpp AtomDecomposition.cpp - EspressoSystemStandAlone.cpp - DomainDecomposition.cpp) + EspressoSystemStandAlone.cpp) if(CUDA) set(EspressoCuda_SRC diff --git a/src/core/CellStructure.cpp b/src/core/CellStructure.cpp index a1a5921ea39..94ff565c80a 100644 --- a/src/core/CellStructure.cpp +++ b/src/core/CellStructure.cpp @@ -22,7 +22,7 @@ #include "CellStructure.hpp" #include "AtomDecomposition.hpp" -#include "DomainDecomposition.hpp" +#include "RegularDecomposition.hpp" #include @@ -246,10 +246,10 @@ void CellStructure::set_atom_decomposition(boost::mpi::communicator const &comm, m_type = CELL_STRUCTURE_NSQUARE; } -void CellStructure::set_domain_decomposition( +void CellStructure::set_regular_decomposition( boost::mpi::communicator const &comm, double range, BoxGeometry const &box, LocalBox const &local_geo) { set_particle_decomposition( - std::make_unique(comm, range, box, local_geo)); - m_type = CELL_STRUCTURE_DOMDEC; + std::make_unique(comm, range, box, local_geo)); + m_type = CELL_STRUCTURE_REGULAR; } diff --git a/src/core/CellStructure.hpp b/src/core/CellStructure.hpp index d204154c5ca..1f6a8ad257a 100644 --- a/src/core/CellStructure.hpp +++ b/src/core/CellStructure.hpp @@ -50,8 +50,8 @@ /** Cell Structure */ enum CellStructureType : int { - /** cell structure domain decomposition */ - CELL_STRUCTURE_DOMDEC = 1, + /** cell structure regular decomposition */ + CELL_STRUCTURE_REGULAR = 1, /** cell structure n square */ CELL_STRUCTURE_NSQUARE = 2 }; @@ -503,16 +503,16 @@ struct CellStructure { BoxGeometry const &box); /** - * @brief Set the particle decomposition to DomainDecomposition. + * @brief Set the particle decomposition to RegularDecomposition. * * @param comm Cartesian communicator to use. * @param range Interaction range. * @param box Box Geometry * @param local_geo Geometry of the local box. */ - void set_domain_decomposition(boost::mpi::communicator const &comm, - double range, BoxGeometry const &box, - LocalBox const &local_geo); + void set_regular_decomposition(boost::mpi::communicator const &comm, + double range, BoxGeometry const &box, + LocalBox const &local_geo); public: template void bond_loop(BondKernel const &bond_kernel) { diff --git a/src/core/DomainDecomposition.cpp b/src/core/RegularDecomposition.cpp similarity index 91% rename from src/core/DomainDecomposition.cpp rename to src/core/RegularDecomposition.cpp index 9ebebd83198..322d585018a 100644 --- a/src/core/DomainDecomposition.cpp +++ b/src/core/RegularDecomposition.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -#include "DomainDecomposition.hpp" +#include "RegularDecomposition.hpp" #include "RuntimeErrorStream.hpp" #include "errorhandling.hpp" @@ -45,7 +45,7 @@ /** Returns pointer to the cell which corresponds to the position if the * position is in the nodes spatial domain otherwise a nullptr pointer. */ -Cell *DomainDecomposition::position_to_cell(const Utils::Vector3d &pos) { +Cell *RegularDecomposition::position_to_cell(const Utils::Vector3d &pos) { Utils::Vector3i cpos; for (int i = 0; i < 3; i++) { @@ -76,7 +76,7 @@ Cell *DomainDecomposition::position_to_cell(const Utils::Vector3d &pos) { return &(cells.at(ind)); } -void DomainDecomposition::move_if_local( +void RegularDecomposition::move_if_local( ParticleList &src, ParticleList &rest, std::vector &modified_cells) { for (auto &part : src) { @@ -93,10 +93,10 @@ void DomainDecomposition::move_if_local( src.clear(); } -void DomainDecomposition::move_left_or_right(ParticleList &src, - ParticleList &left, - ParticleList &right, - int dir) const { +void RegularDecomposition::move_left_or_right(ParticleList &src, + ParticleList &left, + ParticleList &right, + int dir) const { for (auto it = src.begin(); it != src.end();) { if ((m_box.get_mi_coord(it->r.p[dir], m_local_box.my_left()[dir], dir) < 0.0) and @@ -115,7 +115,7 @@ void DomainDecomposition::move_left_or_right(ParticleList &src, } } -void DomainDecomposition::exchange_neighbors( +void RegularDecomposition::exchange_neighbors( ParticleList &pl, std::vector &modified_cells) { auto const node_neighbors = Utils::Mpi::cart_neighbors<3>(m_comm); static ParticleList send_buf_l, send_buf_r, recv_buf_l, recv_buf_r; @@ -168,8 +168,8 @@ void fold_and_reset(Particle &p, BoxGeometry const &box_geo) { } } // namespace -void DomainDecomposition::resort(bool global, - std::vector &diff) { +void RegularDecomposition::resort(bool global, + std::vector &diff) { ParticleList displaced_parts; for (auto &c : local_cells()) { @@ -234,7 +234,7 @@ void DomainDecomposition::resort(bool global, } } -void DomainDecomposition::mark_cells() { +void RegularDecomposition::mark_cells() { m_local_cells.clear(); m_ghost_cells.clear(); @@ -250,9 +250,9 @@ void DomainDecomposition::mark_cells() { } } -void DomainDecomposition::fill_comm_cell_lists(ParticleList **part_lists, - const Utils::Vector3i &lc, - const Utils::Vector3i &hc) { +void RegularDecomposition::fill_comm_cell_lists(ParticleList **part_lists, + const Utils::Vector3i &lc, + const Utils::Vector3i &hc) { for (int o = lc[0]; o <= hc[0]; o++) for (int n = lc[1]; n <= hc[1]; n++) for (int m = lc[2]; m <= hc[2]; m++) { @@ -261,7 +261,7 @@ void DomainDecomposition::fill_comm_cell_lists(ParticleList **part_lists, *part_lists++ = &(cells.at(i).particles()); } } -Utils::Vector3d DomainDecomposition::max_cutoff() const { +Utils::Vector3d RegularDecomposition::max_cutoff() const { auto dir_max_range = [this](int i) { return std::min(0.5 * m_box.length()[i], m_local_box.length()[i]); }; @@ -269,8 +269,8 @@ Utils::Vector3d DomainDecomposition::max_cutoff() const { return {dir_max_range(0), dir_max_range(1), dir_max_range(2)}; } -Utils::Vector3d DomainDecomposition::max_range() const { return cell_size; } -int DomainDecomposition::calc_processor_min_num_cells() const { +Utils::Vector3d RegularDecomposition::max_range() const { return cell_size; } +int RegularDecomposition::calc_processor_min_num_cells() const { /* the minimal number of cells can be lower if there are at least two nodes serving a direction, since this also ensures that the cell size is at most half the box @@ -282,7 +282,7 @@ int DomainDecomposition::calc_processor_min_num_cells() const { }); } -void DomainDecomposition::create_cell_grid(double range) { +void RegularDecomposition::create_cell_grid(double range) { auto const cart_info = Utils::Mpi::cart_get<3>(m_comm); int n_local_cells; @@ -300,7 +300,7 @@ void DomainDecomposition::create_cell_grid(double range) { /* Calculate initial cell grid */ auto const &local_box_l = m_local_box.length(); auto const volume = Utils::product(local_box_l); - auto const scale = std::cbrt(DomainDecomposition::max_num_cells / volume); + auto const scale = std::cbrt(RegularDecomposition::max_num_cells / volume); for (int i = 0; i < 3; i++) { /* this is at least 1 */ @@ -328,7 +328,7 @@ void DomainDecomposition::create_cell_grid(double range) { n_local_cells = Utils::product(cell_grid); /* done */ - if (n_local_cells <= DomainDecomposition::max_num_cells) + if (n_local_cells <= RegularDecomposition::max_num_cells) break; /* find coordinate with the smallest cell range */ @@ -355,7 +355,7 @@ void DomainDecomposition::create_cell_grid(double range) { } } - if (n_local_cells > DomainDecomposition::max_num_cells) { + if (n_local_cells > RegularDecomposition::max_num_cells) { runtimeErrorMsg() << "no suitable cell grid found"; } @@ -378,7 +378,7 @@ void DomainDecomposition::create_cell_grid(double range) { m_ghost_cells.resize(new_cells - n_local_cells); } -void DomainDecomposition::init_cell_interactions() { +void RegularDecomposition::init_cell_interactions() { /* loop all local cells */ for (int o = 1; o < cell_grid[2] + 1; o++) for (int n = 1; n < cell_grid[1] + 1; n++) @@ -457,7 +457,7 @@ Utils::Vector3d shift(BoxGeometry const &box, LocalBox const &local_box, } } // namespace -GhostCommunicator DomainDecomposition::prepare_comm() { +GhostCommunicator RegularDecomposition::prepare_comm() { int dir, lr, i, cnt, n_comm_cells[3]; Utils::Vector3i lc{}, hc{}, done{}; @@ -560,12 +560,12 @@ GhostCommunicator DomainDecomposition::prepare_comm() { return ghost_comm; } -DomainDecomposition::DomainDecomposition(boost::mpi::communicator comm, - double range, - const BoxGeometry &box_geo, - const LocalBox &local_geo) +RegularDecomposition::RegularDecomposition(boost::mpi::communicator comm, + double range, + const BoxGeometry &box_geo, + const LocalBox &local_geo) : m_comm(std::move(comm)), m_box(box_geo), m_local_box(local_geo) { - /* set up new domain decomposition cell structure */ + /* set up new regular decomposition cell structure */ create_cell_grid(range); /* setup cell neighbors */ diff --git a/src/core/DomainDecomposition.hpp b/src/core/RegularDecomposition.hpp similarity index 91% rename from src/core/DomainDecomposition.hpp rename to src/core/RegularDecomposition.hpp index ee6cc1e18c8..765d4ffbeaf 100644 --- a/src/core/DomainDecomposition.hpp +++ b/src/core/RegularDecomposition.hpp @@ -19,8 +19,8 @@ * along with this program. If not, see . */ -#ifndef ESPRESSO_DOMAIN_DECOMPOSITION_HPP -#define ESPRESSO_DOMAIN_DECOMPOSITION_HPP +#ifndef ESPRESSO_REGULAR_DECOMPOSITION_HPP +#define ESPRESSO_REGULAR_DECOMPOSITION_HPP #include "ParticleDecomposition.hpp" @@ -40,13 +40,13 @@ #include /** @brief Structure containing the information about the cell grid used for - * domain decomposition. + * regular decomposition. * * The domain of a node is split into a 3D cell grid with dimension * cell_grid. Together with one ghost cell * layer on each side the overall dimension of the ghost cell grid is - * ghost_cell_grid. The domain - * decomposition enables one the use of the linked cell algorithm + * ghost_cell_grid. The regular + * decomposition enables the use of the linked cell algorithm * which is in turn used for setting up the Verlet list for the * system. You can see a 2D graphical representation of the linked * cell grid below. @@ -65,7 +65,7 @@ * some ghost-ghost cell interaction as well, which we do not need! * */ -struct DomainDecomposition : public ParticleDecomposition { +struct RegularDecomposition : public ParticleDecomposition { /** Grid dimensions per node. */ Utils::Vector3i cell_grid = {}; /** Cell size. */ @@ -74,7 +74,7 @@ struct DomainDecomposition : public ParticleDecomposition { Utils::Vector3i cell_offset = {}; /** linked cell grid with ghost frame. */ Utils::Vector3i ghost_cell_grid = {}; - /** inverse cell size = \see DomainDecomposition::cell_size ^ -1. */ + /** inverse cell size = \see RegularDecomposition::cell_size ^ -1. */ Utils::Vector3d inv_cell_size = {}; boost::mpi::communicator m_comm; @@ -87,9 +87,9 @@ struct DomainDecomposition : public ParticleDecomposition { GhostCommunicator m_collect_ghost_force_comm; public: - DomainDecomposition(boost::mpi::communicator comm, double range, - const BoxGeometry &box_geo, - const LocalBox &local_geo); + RegularDecomposition(boost::mpi::communicator comm, double range, + const BoxGeometry &box_geo, + const LocalBox &local_geo); GhostCommunicator const &exchange_ghosts_comm() const override { return m_exchange_ghosts_comm; @@ -118,7 +118,7 @@ struct DomainDecomposition : public ParticleDecomposition { } private: - /** Fill @c m_local_cells list and @c m_ghost_cells list for use with domain + /** Fill @c m_local_cells list and @c m_ghost_cells list for use with regular * decomposition. */ void mark_cells(); @@ -196,14 +196,14 @@ struct DomainDecomposition : public ParticleDecomposition { */ void create_cell_grid(double range); - /** Init cell interactions for cell system domain decomposition. + /** Init cell interactions for cell system regular decomposition. * Initializes the interacting neighbor cell list of a cell. * This list of interacting neighbor cells is used by the Verlet * algorithm. */ void init_cell_interactions(); - /** Create communicators for cell structure domain decomposition (see \ref + /** Create communicators for cell structure regular decomposition (see \ref * GhostCommunicator). */ GhostCommunicator prepare_comm(); diff --git a/src/core/cells.cpp b/src/core/cells.cpp index ffc74396290..753d7d09832 100644 --- a/src/core/cells.cpp +++ b/src/core/cells.cpp @@ -34,8 +34,8 @@ #include "integrate.hpp" #include "particle_data.hpp" -#include "DomainDecomposition.hpp" #include "ParticleDecomposition.hpp" +#include "RegularDecomposition.hpp" #include #include @@ -197,9 +197,9 @@ std::vector mpi_resort_particles(int global_flag) { void cells_re_init(int new_cs) { switch (new_cs) { - case CELL_STRUCTURE_DOMDEC: - cell_structure.set_domain_decomposition(comm_cart, interaction_range(), - box_geo, local_geo); + case CELL_STRUCTURE_REGULAR: + cell_structure.set_regular_decomposition(comm_cart, interaction_range(), + box_geo, local_geo); break; case CELL_STRUCTURE_NSQUARE: cell_structure.set_atom_decomposition(comm_cart, box_geo); @@ -263,8 +263,8 @@ Cell *find_current_cell(const Particle &p) { return cell_structure.find_current_cell(p); } -const DomainDecomposition *get_domain_decomposition() { - return &dynamic_cast( +const RegularDecomposition *get_regular_decomposition() { + return &dynamic_cast( Utils::as_const(cell_structure).decomposition()); } diff --git a/src/core/cells.hpp b/src/core/cells.hpp index 8b9abd36936..01c049cad2c 100644 --- a/src/core/cells.hpp +++ b/src/core/cells.hpp @@ -29,8 +29,8 @@ * (regardless if they reside on the same or different nodes) * interact with each other. The following cell systems are implemented: * - * - domain decomposition: The simulation box is divided spatially - * into cells (see \ref DomainDecomposition.hpp). This is suitable for + * - regular decomposition: The simulation box is divided spatially + * into cells (see \ref RegularDecomposition.hpp). This is suitable for * short range interactions. * - nsquare: The particles are distributed equally on all nodes * regardless their spatial position (see \ref AtomDecomposition.hpp). @@ -40,8 +40,8 @@ #include "Cell.hpp" #include "CellStructure.hpp" -#include "DomainDecomposition.hpp" #include "Particle.hpp" +#include "RegularDecomposition.hpp" #include #include @@ -121,11 +121,11 @@ std::vector mpi_resort_particles(int global_flag); Cell *find_current_cell(const Particle &p); /** - * @brief Return a pointer to the global DomainDecomposition. + * @brief Return a pointer to the global RegularDecomposition. * * @return Pointer to the decomposition if it is set, nullptr otherwise. */ -const DomainDecomposition *get_domain_decomposition(); +const RegularDecomposition *get_regular_decomposition(); class PairInfo { public: diff --git a/src/core/electrostatics_magnetostatics/fft.cpp b/src/core/electrostatics_magnetostatics/fft.cpp index 7b47ba9b9f1..9d386502113 100644 --- a/src/core/electrostatics_magnetostatics/fft.cpp +++ b/src/core/electrostatics_magnetostatics/fft.cpp @@ -63,7 +63,7 @@ using Utils::permute_ifield; namespace { /** This ugly function does the bookkeeping: which nodes have to * communicate to each other, when you change the node grid. - * Changing the domain decomposition requires communication. This + * Changing the regular decomposition requires communication. This * function finds (hopefully) the best way to do this. As input it * needs the two grids (@p grid1, @p grid2) and a linear list (@p node_list1) * with the node identities for @p grid1. The linear list (@p node_list2) @@ -415,8 +415,8 @@ void back_grid_comm(fft_forw_plan plan_f, fft_back_plan plan_b, } /** Calculate 'best' mapping between a 2D and 3D grid. - * Required for the communication from 3D domain decomposition - * to 2D row decomposition. + * Required for the communication from 3D regular domain + * decomposition to 2D regular row decomposition. * The dimensions of the 2D grid are resorted, if necessary, in a way * that they are multiples of the 3D grid dimensions. * \param g3d 3D grid. diff --git a/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp b/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp index 72e79a9737e..44fba840d01 100644 --- a/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp +++ b/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp @@ -1421,8 +1421,8 @@ bool dp3m_sanity_checks(const Utils::Vector3i &grid) { ret = true; } - if (cell_structure.decomposition_type() != CELL_STRUCTURE_DOMDEC) { - runtimeErrorMsg() << "dipolar P3M requires the domain decomposition " + if (cell_structure.decomposition_type() != CELL_STRUCTURE_REGULAR) { + runtimeErrorMsg() << "dipolar P3M requires the regular decomposition " "cell system"; ret = true; } diff --git a/src/core/electrostatics_magnetostatics/p3m.cpp b/src/core/electrostatics_magnetostatics/p3m.cpp index d6c03feafb9..f6112bbe37b 100644 --- a/src/core/electrostatics_magnetostatics/p3m.cpp +++ b/src/core/electrostatics_magnetostatics/p3m.cpp @@ -1286,8 +1286,8 @@ bool p3m_sanity_checks_system(const Utils::Vector3i &grid) { ret = true; } - if (cell_structure.decomposition_type() != CELL_STRUCTURE_DOMDEC) { - runtimeErrorMsg() << "P3M requires the domain decomposition cell system"; + if (cell_structure.decomposition_type() != CELL_STRUCTURE_REGULAR) { + runtimeErrorMsg() << "P3M requires the regular decomposition cell system"; ret = true; } diff --git a/src/core/event.cpp b/src/core/event.cpp index 883818372fd..2d11b5aa7b8 100644 --- a/src/core/event.cpp +++ b/src/core/event.cpp @@ -89,8 +89,8 @@ void on_program_start() { init_node_grid(); - /* initially go for domain decomposition */ - cells_re_init(CELL_STRUCTURE_DOMDEC); + /* initially go for regular decomposition */ + cells_re_init(CELL_STRUCTURE_REGULAR); if (this_node == 0) { /* make sure interaction 0<->0 always exists */ diff --git a/src/core/grid_based_algorithms/lb.cpp b/src/core/grid_based_algorithms/lb.cpp index 1b8faabb162..cc1749da82c 100644 --- a/src/core/grid_based_algorithms/lb.cpp +++ b/src/core/grid_based_algorithms/lb.cpp @@ -613,8 +613,8 @@ void lb_sanity_checks(const LB_Parameters &lb_parameters) { if (lb_parameters.viscosity <= 0.0) { runtimeErrorMsg() << "Lattice Boltzmann fluid viscosity not set"; } - if (cell_structure.decomposition_type() != CELL_STRUCTURE_DOMDEC) { - runtimeErrorMsg() << "LB requires domain-decomposition cellsystem"; + if (cell_structure.decomposition_type() != CELL_STRUCTURE_REGULAR) { + runtimeErrorMsg() << "LB requires regular-decomposition cellsystem"; } } diff --git a/src/core/object-in-fluid/oif_global_forces.hpp b/src/core/object-in-fluid/oif_global_forces.hpp index a0177900607..5a44b31aa9f 100644 --- a/src/core/object-in-fluid/oif_global_forces.hpp +++ b/src/core/object-in-fluid/oif_global_forces.hpp @@ -33,7 +33,7 @@ * - calculates the global area and global volume for a cell before the forces * are handled * - MPI synchronization with all reduce - * - !!! loop over particles from domain_decomposition !!! + * - !!! loop over particles from regular_decomposition !!! */ Utils::Vector2d calc_oif_global(int molType, CellStructure &cs); diff --git a/src/python/espressomd/cellsystem.pxd b/src/python/espressomd/cellsystem.pxd index ecf56f006c8..26f5a477a4e 100644 --- a/src/python/espressomd/cellsystem.pxd +++ b/src/python/espressomd/cellsystem.pxd @@ -36,7 +36,7 @@ cdef extern from "communication.hpp": int n_nodes cdef extern from "cells.hpp": - int CELL_STRUCTURE_DOMDEC + int CELL_STRUCTURE_REGULAR int CELL_STRUCTURE_NSQUARE ctypedef struct CellStructure: @@ -45,7 +45,7 @@ cdef extern from "cells.hpp": CellStructure cell_structure - const DomainDecomposition * get_domain_decomposition() + const RegularDecomposition * get_regular_decomposition() vector[pair[int, int]] mpi_get_pairs(double distance) except + vector[pair[int, int]] mpi_get_pairs_of_types(double distance, vector[int] types) except + @@ -62,8 +62,8 @@ cdef extern from "integrate.hpp": void mpi_set_skin(double skin) double get_verlet_reuse() -cdef extern from "DomainDecomposition.hpp": - cppclass DomainDecomposition: +cdef extern from "RegularDecomposition.hpp": + cppclass RegularDecomposition: Vector3i cell_grid double cell_size[3] diff --git a/src/python/espressomd/cellsystem.pyx b/src/python/espressomd/cellsystem.pyx index 9291fb0acfd..baf1e9159ee 100644 --- a/src/python/espressomd/cellsystem.pyx +++ b/src/python/espressomd/cellsystem.pyx @@ -30,9 +30,9 @@ from .utils cimport Vector3i from .utils cimport check_type_or_throw_except, make_array_locked cdef class CellSystem: - def set_domain_decomposition(self, use_verlet_lists=True): + def set_regular_decomposition(self, use_verlet_lists=True): """ - Activates domain decomposition cell system. + Activates regular decomposition cell system. Parameters ---------- @@ -42,7 +42,7 @@ cdef class CellSystem: """ mpi_set_use_verlet_lists(use_verlet_lists) - mpi_bcast_cell_structure(CELL_STRUCTURE_DOMDEC) + mpi_bcast_cell_structure(CELL_STRUCTURE_REGULAR) handle_errors("Error while initializing the cell system.") return True @@ -66,12 +66,12 @@ cdef class CellSystem: def get_state(self): s = self.__getstate__() - if cell_structure.decomposition_type() == CELL_STRUCTURE_DOMDEC: - dd = get_domain_decomposition() + if cell_structure.decomposition_type() == CELL_STRUCTURE_REGULAR: + rd = get_regular_decomposition() s["cell_grid"] = np.array( - [dd.cell_grid[0], dd.cell_grid[1], dd.cell_grid[2]]) + [rd.cell_grid[0], rd.cell_grid[1], rd.cell_grid[2]]) s["cell_size"] = np.array( - [dd.cell_size[0], dd.cell_size[1], dd.cell_size[2]]) + [rd.cell_size[0], rd.cell_size[1], rd.cell_size[2]]) s["verlet_reuse"] = get_verlet_reuse() s["n_nodes"] = n_nodes @@ -81,8 +81,8 @@ cdef class CellSystem: def __getstate__(self): s = {"use_verlet_list": cell_structure.use_verlet_list} - if cell_structure.decomposition_type() == CELL_STRUCTURE_DOMDEC: - s["type"] = "domain_decomposition" + if cell_structure.decomposition_type() == CELL_STRUCTURE_REGULAR: + s["type"] = "regular_decomposition" if cell_structure.decomposition_type() == CELL_STRUCTURE_NSQUARE: s["type"] = "nsquare" @@ -94,8 +94,8 @@ cdef class CellSystem: self.skin = d['skin'] self.node_grid = d['node_grid'] if 'type' in d: - if d['type'] == "domain_decomposition": - self.set_domain_decomposition( + if d['type'] == "regular_decomposition": + self.set_regular_decomposition( use_verlet_lists=d['use_verlet_list']) elif d['type'] == "nsquare": self.set_n_square(use_verlet_lists=d['use_verlet_list']) From 9098a54db9b4dc6af12779dd337776d56264eda8 Mon Sep 17 00:00:00 2001 From: Patrick Kreissl Date: Fri, 4 Feb 2022 14:03:35 +0100 Subject: [PATCH 40/99] Make polymer_linear test deterministic. --- testsuite/python/polymer_linear.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testsuite/python/polymer_linear.py b/testsuite/python/polymer_linear.py index 91e2f2eabbe..8d4007c3121 100644 --- a/testsuite/python/polymer_linear.py +++ b/testsuite/python/polymer_linear.py @@ -16,7 +16,6 @@ # along with this program. If not, see . import unittest as ut import numpy as np -import random import espressomd import espressomd.polymer import espressomd.shapes @@ -34,7 +33,7 @@ class LinearPolymerPositions(ut.TestCase): """ box_l = 15 - seed = random.randint(0, 1000) + seed = 42 system = espressomd.System(box_l=[box_l, box_l, box_l]) @@ -128,6 +127,7 @@ def test_start_positions(self): num_poly = 90 num_mono = 25 bond_length = 0.83 + np.random.seed(seed=self.seed) start_positions = np.random.random((num_poly, 3)) * self.box_l # make sure that incorrect size leads to error From 9428e43d09ed224d8b7c816c307ee49262bcdc2b Mon Sep 17 00:00:00 2001 From: Patrick Kreissl Date: Fri, 4 Feb 2022 13:47:07 +0100 Subject: [PATCH 41/99] Use in samples and tests. --- samples/chamber_game.py | 2 +- samples/visualization_cellsystem.py | 2 +- samples/visualization_charged.py | 2 +- testsuite/python/CMakeLists.txt | 2 +- testsuite/python/brownian_dynamics.py | 2 +- testsuite/python/brownian_dynamics_stats.py | 2 +- testsuite/python/cellsystem.py | 7 ++++--- testsuite/python/collision_detection.py | 2 +- testsuite/python/coulomb_mixed_periodicity.py | 4 ++-- testsuite/python/ek_eof_one_species.py | 2 +- testsuite/python/elc_vs_analytic.py | 2 +- testsuite/python/langevin_thermostat.py | 2 +- testsuite/python/langevin_thermostat_stats.py | 2 +- testsuite/python/lj.py | 5 +++-- testsuite/python/mass-and-rinertia_per_particle.py | 2 +- testsuite/python/mmm1d.py | 2 +- testsuite/python/p3m_tuning_exceptions.py | 8 ++++---- testsuite/python/pairs.py | 4 ++-- testsuite/python/particle.py | 2 +- testsuite/python/random_pairs.py | 2 +- .../{domain_decomposition.py => regular_decomposition.py} | 4 ++-- testsuite/python/virtual_sites_relative.py | 4 ++-- 22 files changed, 34 insertions(+), 32 deletions(-) rename testsuite/python/{domain_decomposition.py => regular_decomposition.py} (97%) diff --git a/samples/chamber_game.py b/samples/chamber_game.py index 3a192fac16b..63621e72ad2 100644 --- a/samples/chamber_game.py +++ b/samples/chamber_game.py @@ -156,7 +156,7 @@ # CELLSYSTEM system.cell_system.skin = 3.0 -system.cell_system.set_domain_decomposition(use_verlet_lists=False) +system.cell_system.set_regular_decomposition(use_verlet_lists=False) # BONDS diff --git a/samples/visualization_cellsystem.py b/samples/visualization_cellsystem.py index 2eb015ddf44..c675088d88f 100644 --- a/samples/visualization_cellsystem.py +++ b/samples/visualization_cellsystem.py @@ -42,7 +42,7 @@ draw_cells=True) system.time_step = 0.0005 -system.cell_system.set_domain_decomposition(use_verlet_lists=True) +system.cell_system.set_regular_decomposition(use_verlet_lists=True) system.cell_system.skin = 0.4 #system.cell_system.node_grid = [i, j, k] diff --git a/samples/visualization_charged.py b/samples/visualization_charged.py index a3786933a1f..6375c2aac6d 100644 --- a/samples/visualization_charged.py +++ b/samples/visualization_charged.py @@ -29,7 +29,7 @@ box = [40, 40, 40] system = espressomd.System(box_l=box) -system.cell_system.set_domain_decomposition(use_verlet_lists=True) +system.cell_system.set_regular_decomposition(use_verlet_lists=True) visualizer = espressomd.visualization_opengl.openGLLive( system, background_color=[1, 1, 1], drag_enabled=True, drag_force=10) diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index 492a642c719..98ad4076ee2 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -143,7 +143,7 @@ python_test(FILE nsquare.py MAX_NUM_PROC 4) python_test(FILE virtual_sites_relative.py MAX_NUM_PROC 2) python_test(FILE virtual_sites_tracers.py MAX_NUM_PROC 2) python_test(FILE virtual_sites_tracers_gpu.py MAX_NUM_PROC 2 LABELS gpu) -python_test(FILE domain_decomposition.py MAX_NUM_PROC 4) +python_test(FILE regular_decomposition.py MAX_NUM_PROC 4) python_test(FILE integrator_npt.py MAX_NUM_PROC 4) python_test(FILE integrator_npt_stats.py MAX_NUM_PROC 4 LABELS long) python_test(FILE integrator_steepest_descent.py MAX_NUM_PROC 4) diff --git a/testsuite/python/brownian_dynamics.py b/testsuite/python/brownian_dynamics.py index b8e25c149eb..bdc663f5bd7 100644 --- a/testsuite/python/brownian_dynamics.py +++ b/testsuite/python/brownian_dynamics.py @@ -26,7 +26,7 @@ class BrownianThermostat(ut.TestCase): """Test Brownian Dynamics""" system = espressomd.System(box_l=[1.0, 1.0, 1.0]) - system.cell_system.set_domain_decomposition(use_verlet_lists=True) + system.cell_system.set_regular_decomposition(use_verlet_lists=True) system.cell_system.skin = 0 system.periodicity = [0, 0, 0] diff --git a/testsuite/python/brownian_dynamics_stats.py b/testsuite/python/brownian_dynamics_stats.py index 470e48fdeaf..c614c17a7cc 100644 --- a/testsuite/python/brownian_dynamics_stats.py +++ b/testsuite/python/brownian_dynamics_stats.py @@ -30,7 +30,7 @@ class BrownianThermostat(ut.TestCase, thermostats_common.ThermostatsCommon): """Tests velocity distributions and diffusion for Brownian Dynamics""" system = espressomd.System(box_l=[1.0, 1.0, 1.0]) - system.cell_system.set_domain_decomposition(use_verlet_lists=True) + system.cell_system.set_regular_decomposition(use_verlet_lists=True) system.cell_system.skin = 0 system.periodicity = [0, 0, 0] diff --git a/testsuite/python/cellsystem.py b/testsuite/python/cellsystem.py index a5a2b5358f9..c7f4b72070a 100644 --- a/testsuite/python/cellsystem.py +++ b/testsuite/python/cellsystem.py @@ -30,14 +30,15 @@ def test_cell_system(self): self.system.cell_system.set_n_square(use_verlet_lists=False) s = self.system.cell_system.get_state() self.assertEqual([s['use_verlet_list'], s['type']], [0, "nsquare"]) - self.system.cell_system.set_domain_decomposition(use_verlet_lists=True) + self.system.cell_system.set_regular_decomposition( + use_verlet_lists=True) s = self.system.cell_system.get_state() self.assertEqual( - [s['use_verlet_list'], s['type']], [1, "domain_decomposition"]) + [s['use_verlet_list'], s['type']], [1, "regular_decomposition"]) @ut.skipIf(n_nodes == 1, "Skipping test: only runs for n_nodes >= 2") def test_node_grid(self): - self.system.cell_system.set_domain_decomposition() + self.system.cell_system.set_regular_decomposition() for i in range(3): node_grid_ref = [1, 1, 1] node_grid_ref[i] = self.n_nodes diff --git a/testsuite/python/collision_detection.py b/testsuite/python/collision_detection.py index e1ef7838f25..8a5930c97a1 100644 --- a/testsuite/python/collision_detection.py +++ b/testsuite/python/collision_detection.py @@ -637,7 +637,7 @@ def test_bind_three_particles(self): system.part.add(id=4, pos=e) system.part.add(id=1, pos=b) - system.cell_system.set_domain_decomposition() + system.cell_system.set_regular_decomposition() system.integrator.run(1, recalc_forces=True) self.verify_triangle_binding(cutoff, system.bonded_inter[2], res) system.cell_system.set_n_square() diff --git a/testsuite/python/coulomb_mixed_periodicity.py b/testsuite/python/coulomb_mixed_periodicity.py index d5b276094ea..706f6774690 100644 --- a/testsuite/python/coulomb_mixed_periodicity.py +++ b/testsuite/python/coulomb_mixed_periodicity.py @@ -76,7 +76,7 @@ def test_elc(self): for p in self.system.part: assert p.pos[2] >= 0. and p.pos[2] <= 9., f'particle {p.id} in gap' - self.system.cell_system.set_domain_decomposition() + self.system.cell_system.set_regular_decomposition() self.system.cell_system.node_grid = sorted( self.system.cell_system.node_grid, key=lambda x: -x) self.system.periodicity = [1, 1, 1] @@ -95,7 +95,7 @@ def test_elc(self): 'Skipping test: missing feature SCAFACOS or p2nfft method') def test_scafacos_p2nfft(self): self.system.periodicity = [1, 1, 0] - self.system.cell_system.set_domain_decomposition() + self.system.cell_system.set_regular_decomposition() scafacos = espressomd.electrostatics.Scafacos( prefactor=1, diff --git a/testsuite/python/ek_eof_one_species.py b/testsuite/python/ek_eof_one_species.py index bc2aa3ed084..38af417e236 100644 --- a/testsuite/python/ek_eof_one_species.py +++ b/testsuite/python/ek_eof_one_species.py @@ -156,7 +156,7 @@ def bisection(): pntm = pnt0 + size else: sys.exit("Bisection method fails:\n" - "Tuning of domain boundaries may be required.") + "Tuning of regular boundaries may be required.") return pntm diff --git a/testsuite/python/elc_vs_analytic.py b/testsuite/python/elc_vs_analytic.py index 7805e8a26d6..333061ba6ad 100644 --- a/testsuite/python/elc_vs_analytic.py +++ b/testsuite/python/elc_vs_analytic.py @@ -54,7 +54,7 @@ def test_elc(self): q=-self.q[0]) self.system.box_l = [self.box_l, self.box_l, self.box_l + self.elc_gap] - self.system.cell_system.set_domain_decomposition( + self.system.cell_system.set_regular_decomposition( use_verlet_lists=True) self.system.periodicity = [1, 1, 1] p3m = espressomd.electrostatics.P3M(prefactor=self.prefactor, diff --git a/testsuite/python/langevin_thermostat.py b/testsuite/python/langevin_thermostat.py index 86f19538ab6..ed931f8ccbb 100644 --- a/testsuite/python/langevin_thermostat.py +++ b/testsuite/python/langevin_thermostat.py @@ -26,7 +26,7 @@ class LangevinThermostat(ut.TestCase): """Test Langevin Dynamics""" system = espressomd.System(box_l=[1.0, 1.0, 1.0]) - system.cell_system.set_domain_decomposition(use_verlet_lists=True) + system.cell_system.set_regular_decomposition(use_verlet_lists=True) system.cell_system.skin = 0 system.periodicity = [0, 0, 0] diff --git a/testsuite/python/langevin_thermostat_stats.py b/testsuite/python/langevin_thermostat_stats.py index 604ef46f7a9..6a865bca844 100644 --- a/testsuite/python/langevin_thermostat_stats.py +++ b/testsuite/python/langevin_thermostat_stats.py @@ -30,7 +30,7 @@ class LangevinThermostat(ut.TestCase, thermostats_common.ThermostatsCommon): """Tests velocity distributions and diffusion for Langevin Dynamics""" system = espressomd.System(box_l=[1.0, 1.0, 1.0]) - system.cell_system.set_domain_decomposition(use_verlet_lists=True) + system.cell_system.set_regular_decomposition(use_verlet_lists=True) system.cell_system.skin = 0 system.periodicity = [0, 0, 0] diff --git a/testsuite/python/lj.py b/testsuite/python/lj.py index 9631323c22c..aaa6ad46e52 100644 --- a/testsuite/python/lj.py +++ b/testsuite/python/lj.py @@ -54,14 +54,15 @@ def check(self): self.assertLess(max_deviation, 1e-5) def test_dd(self): - self.system.cell_system.set_domain_decomposition( + self.system.cell_system.set_regular_decomposition( use_verlet_lists=False) self.system.integrator.run(recalc_forces=True, steps=0) self.check() def test_dd_vl(self): - self.system.cell_system.set_domain_decomposition(use_verlet_lists=True) + self.system.cell_system.set_regular_decomposition( + use_verlet_lists=True) # Build VL and calc ia self.system.integrator.run(recalc_forces=True, steps=0) diff --git a/testsuite/python/mass-and-rinertia_per_particle.py b/testsuite/python/mass-and-rinertia_per_particle.py index d3c1d7c5b34..89fea0d1f63 100644 --- a/testsuite/python/mass-and-rinertia_per_particle.py +++ b/testsuite/python/mass-and-rinertia_per_particle.py @@ -60,7 +60,7 @@ class ThermoTest(ut.TestCase): @classmethod def setUpClass(cls): np.random.seed(seed=15) - cls.system.cell_system.set_domain_decomposition(use_verlet_lists=True) + cls.system.cell_system.set_regular_decomposition(use_verlet_lists=True) cls.system.cell_system.skin = 5.0 def setUp(self): diff --git a/testsuite/python/mmm1d.py b/testsuite/python/mmm1d.py index 2c66dd9c83f..a63c56afa09 100644 --- a/testsuite/python/mmm1d.py +++ b/testsuite/python/mmm1d.py @@ -114,7 +114,7 @@ def test_exceptions(self): if self.MMM1D is espressomd.electrostatics.MMM1D: with self.assertRaisesRegex(Exception, "MMM1D requires the N-square cellsystem"): mmm1d = self.MMM1D(prefactor=1.0, maxPWerror=1e-2) - self.system.cell_system.set_domain_decomposition() + self.system.cell_system.set_regular_decomposition() self.system.actors.add(mmm1d) self.system.cell_system.set_n_square() self.system.actors.clear() diff --git a/testsuite/python/p3m_tuning_exceptions.py b/testsuite/python/p3m_tuning_exceptions.py index 52c1b8e97ba..85e9c2b7a3b 100644 --- a/testsuite/python/p3m_tuning_exceptions.py +++ b/testsuite/python/p3m_tuning_exceptions.py @@ -210,10 +210,10 @@ def test_04_invalid_params_p3m_cpu(self): self.system.periodicity = (1, 1, 1) # check cell system exceptions - with self.assertRaisesRegex(Exception, "P3M requires the domain decomposition cell system"): + with self.assertRaisesRegex(Exception, "P3M requires the regular decomposition cell system"): self.system.cell_system.set_n_square() self.system.analysis.energy() - self.system.cell_system.set_domain_decomposition() + self.system.cell_system.set_regular_decomposition() self.system.actors.clear() @utx.skipIfMissingGPU() @@ -259,10 +259,10 @@ def test_04_invalid_params_dp3m_cpu(self): self.system.periodicity = (1, 1, 1) # check cell system exceptions - with self.assertRaisesRegex(Exception, "dipolar P3M requires the domain decomposition cell system"): + with self.assertRaisesRegex(Exception, "dipolar P3M requires the regular decomposition cell system"): self.system.cell_system.set_n_square() self.system.analysis.energy() - self.system.cell_system.set_domain_decomposition() + self.system.cell_system.set_regular_decomposition() self.system.actors.clear() @utx.skipIfMissingFeatures("P3M") diff --git a/testsuite/python/pairs.py b/testsuite/python/pairs.py index 8f032b50f3b..a466936980d 100644 --- a/testsuite/python/pairs.py +++ b/testsuite/python/pairs.py @@ -104,13 +104,13 @@ def test_nsquare_partial_z(self): self.run_and_check() def test_dd(self): - self.system.cell_system.set_domain_decomposition() + self.system.cell_system.set_regular_decomposition() self.system.periodicity = [1, 1, 1] self.run_and_check() self.check_range_exception() def test_dd_partial_z(self): - self.system.cell_system.set_domain_decomposition() + self.system.cell_system.set_regular_decomposition() self.system.periodicity = [1, 1, 0] self.run_and_check() self.check_range_exception() diff --git a/testsuite/python/particle.py b/testsuite/python/particle.py index d68e391d3ae..812b60e0d0c 100644 --- a/testsuite/python/particle.py +++ b/testsuite/python/particle.py @@ -344,7 +344,7 @@ def test_zz_remove_all(self): def test_coord_fold_corner_cases(self): system = self.system system.time_step = .5 - system.cell_system.set_domain_decomposition(use_verlet_lists=False) + system.cell_system.set_regular_decomposition(use_verlet_lists=False) system.cell_system.skin = 0 system.min_global_cut = 3 system.part.clear() diff --git a/testsuite/python/random_pairs.py b/testsuite/python/random_pairs.py index e08b01956b9..5c4b19d2e7c 100644 --- a/testsuite/python/random_pairs.py +++ b/testsuite/python/random_pairs.py @@ -82,7 +82,7 @@ def check_pairs(self, n2_pairs): self.assertEqual(n2_pairs ^ set(cs_pairs), set()) def check_dd(self, n2_pairs): - self.system.cell_system.set_domain_decomposition() + self.system.cell_system.set_regular_decomposition() self.check_pairs(n2_pairs) def check_n_squared(self, n2_pairs): diff --git a/testsuite/python/domain_decomposition.py b/testsuite/python/regular_decomposition.py similarity index 97% rename from testsuite/python/domain_decomposition.py rename to testsuite/python/regular_decomposition.py index 741a79ed13a..55b2ee62f29 100644 --- a/testsuite/python/domain_decomposition.py +++ b/testsuite/python/regular_decomposition.py @@ -23,12 +23,12 @@ np.random.seed(42) -class DomainDecomposition(ut.TestCase): +class RegularDecomposition(ut.TestCase): system = espressomd.System(box_l=3 * [50.0]) original_node_grid = tuple(system.cell_system.node_grid) def setUp(self): - self.system.cell_system.set_domain_decomposition( + self.system.cell_system.set_regular_decomposition( use_verlet_lists=False) self.system.cell_system.node_grid = self.original_node_grid self.system.time_step = 1e-3 diff --git a/testsuite/python/virtual_sites_relative.py b/testsuite/python/virtual_sites_relative.py index 9d0f2fc87a8..cefc2d2b86c 100644 --- a/testsuite/python/virtual_sites_relative.py +++ b/testsuite/python/virtual_sites_relative.py @@ -307,9 +307,9 @@ def test_lj(self): system.cell_system.skin = 0.4 system.cell_system.set_n_square(use_verlet_lists=True) self.run_test_lj() - system.cell_system.set_domain_decomposition(use_verlet_lists=True) + system.cell_system.set_regular_decomposition(use_verlet_lists=True) self.run_test_lj() - system.cell_system.set_domain_decomposition(use_verlet_lists=False) + system.cell_system.set_regular_decomposition(use_verlet_lists=False) self.run_test_lj() @utx.skipIfMissingFeatures("EXTERNAL_FORCES") From ae11c4488ea4a0c000d54832597b86da83c87da0 Mon Sep 17 00:00:00 2001 From: Patrick Kreissl Date: Fri, 4 Feb 2022 14:40:56 +0100 Subject: [PATCH 42/99] Use regular decomposition in p3m benchmark. --- maintainer/benchmarks/p3m.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintainer/benchmarks/p3m.py b/maintainer/benchmarks/p3m.py index 78a825c50fe..262cc675dae 100644 --- a/maintainer/benchmarks/p3m.py +++ b/maintainer/benchmarks/p3m.py @@ -87,7 +87,7 @@ # System ############################################################# system.box_l = 3 * (box_l,) -system.cell_system.set_domain_decomposition(use_verlet_lists=True) +system.cell_system.set_regular_decomposition(use_verlet_lists=True) # Integration parameters ############################################################# From 00e55ec5fe333dce58bd2532b9b3cab2b41646bc Mon Sep 17 00:00:00 2001 From: pmblanco Date: Tue, 15 Feb 2022 14:12:21 +0100 Subject: [PATCH 43/99] Rewrite reaction methods as script interface classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jean-Noël Grad --- doc/tutorials/constant_pH/constant_pH.ipynb | 9 +- .../benchmarks/mc_acid_base_reservoir.py | 8 +- samples/grand_canonical.py | 8 +- samples/reaction_ensemble.py | 17 +- samples/reaction_ensemble_complex_reaction.py | 6 +- samples/widom_insertion.py | 4 +- .../reaction_methods/ConstantpHEnsemble.hpp | 7 +- .../reaction_methods/ReactionAlgorithm.cpp | 34 +- .../reaction_methods/ReactionAlgorithm.hpp | 42 +- .../reaction_methods/ReactionEnsemble.hpp | 3 +- src/core/reaction_methods/SingleReaction.hpp | 8 + src/core/reaction_methods/WidomInsertion.hpp | 3 +- .../tests/ConstantpHEnsemble_test.cpp | 4 +- .../tests/ReactionAlgorithm_test.cpp | 30 +- .../tests/ReactionEnsemble_test.cpp | 11 +- src/python/espressomd/reaction_ensemble.pxd | 76 -- src/python/espressomd/reaction_ensemble.py | 542 ++++++++++++++ src/python/espressomd/reaction_ensemble.pyx | 686 ------------------ src/python/espressomd/script_interface.pyx | 12 +- src/script_interface/CMakeLists.txt | 1 + src/script_interface/initialize.cpp | 2 + .../reaction_methods/CMakeLists.txt | 2 + .../reaction_methods/ConstantpHEnsemble.hpp | 64 ++ .../reaction_methods/ReactionAlgorithm.hpp | 147 ++++ .../reaction_methods/ReactionEnsemble.hpp | 53 ++ .../reaction_methods/SingleReaction.hpp | 66 ++ .../reaction_methods/WidomInsertion.hpp | 65 ++ .../reaction_methods/initialize.cpp | 39 + .../reaction_methods/initialize.hpp | 32 + testsuite/python/CMakeLists.txt | 1 + testsuite/python/constant_pH.py | 10 +- testsuite/python/constant_pH_stats.py | 9 +- testsuite/python/reaction_ensemble.py | 92 +-- testsuite/python/reaction_methods.py | 273 +++++++ testsuite/python/widom_insertion.py | 5 +- 35 files changed, 1420 insertions(+), 951 deletions(-) delete mode 100644 src/python/espressomd/reaction_ensemble.pxd create mode 100644 src/python/espressomd/reaction_ensemble.py delete mode 100644 src/python/espressomd/reaction_ensemble.pyx create mode 100644 src/script_interface/reaction_methods/CMakeLists.txt create mode 100644 src/script_interface/reaction_methods/ConstantpHEnsemble.hpp create mode 100644 src/script_interface/reaction_methods/ReactionAlgorithm.hpp create mode 100644 src/script_interface/reaction_methods/ReactionEnsemble.hpp create mode 100644 src/script_interface/reaction_methods/SingleReaction.hpp create mode 100644 src/script_interface/reaction_methods/WidomInsertion.hpp create mode 100644 src/script_interface/reaction_methods/initialize.cpp create mode 100644 src/script_interface/reaction_methods/initialize.hpp create mode 100644 testsuite/python/reaction_methods.py diff --git a/doc/tutorials/constant_pH/constant_pH.ipynb b/doc/tutorials/constant_pH/constant_pH.ipynb index 86f6b36268c..0aa3e62cca6 100644 --- a/doc/tutorials/constant_pH/constant_pH.ipynb +++ b/doc/tutorials/constant_pH/constant_pH.ipynb @@ -581,9 +581,10 @@ "RE = espressomd.reaction_ensemble.ConstantpHEnsemble(\n", " kT=KT_REDUCED,\n", " exclusion_radius=exclusion_radius,\n", - " seed=77\n", + " seed=77,\n", + " constant_pH=2 # temporary value\n", ")\n", - "RE.set_non_interacting_type(len(TYPES)) # this parameter helps speed up the calculation in an interacting system\n", + "RE.set_non_interacting_type(type=len(TYPES)) # this parameter helps speed up the calculation in an interacting system\n", "```" ] }, @@ -682,7 +683,7 @@ "source": [ "```python\n", "def equilibrate_reaction(reaction_steps=1):\n", - " RE.reaction(reaction_steps)\n", + " RE.reaction(reaction_steps=reaction_steps)\n", "```" ] }, @@ -739,7 +740,7 @@ " if USE_WCA and np.random.random() < prob_integration:\n", " system.integrator.run(integration_steps)\n", " # we should do at least one reaction attempt per reactive particle\n", - " RE.reaction(reaction_steps) \n", + " RE.reaction(reaction_steps=reaction_steps) \n", " num_As[i] = system.number_of_particles(type=type_A)\n", "```" ] diff --git a/maintainer/benchmarks/mc_acid_base_reservoir.py b/maintainer/benchmarks/mc_acid_base_reservoir.py index 7a987000c63..7798fc33796 100644 --- a/maintainer/benchmarks/mc_acid_base_reservoir.py +++ b/maintainer/benchmarks/mc_acid_base_reservoir.py @@ -234,7 +234,7 @@ def calc_donnan_coefficient(c_acid, I_res, charge=-1): seed=77 ) # this parameter helps speed up the calculation in an interacting system -RE.set_non_interacting_type(max(TYPES.values()) + 1) +RE.set_non_interacting_type(type=max(TYPES.values()) + 1) RE.add_reaction( gamma=K_NaCl_reduced, @@ -259,7 +259,7 @@ def calc_donnan_coefficient(c_acid, I_res, charge=-1): def equilibrate_reaction(reaction_steps=1): - RE.reaction(reaction_steps) + RE.reaction(reaction_steps=reaction_steps) def report_progress(system, i, next_i): @@ -295,7 +295,7 @@ def report_progress(system, i, next_i): if MC_STEPS_PER_SAMPLE > 0: tick_MC = time.time() - RE.reaction(MC_STEPS_PER_SAMPLE) + RE.reaction(reaction_steps=MC_STEPS_PER_SAMPLE) tock_MC = time.time() t_MC = (tock_MC - tick_MC) / MC_STEPS_PER_SAMPLE @@ -332,7 +332,7 @@ def report_progress(system, i, next_i): for i in range(NUM_SAMPLES): if RUN_INTEGRATION: system.integrator.run(INTEGRATION_STEPS_PER_SAMPLE) - RE.reaction(MC_STEPS_PER_SAMPLE) + RE.reaction(reaction_steps=MC_STEPS_PER_SAMPLE) n_A = system.number_of_particles(type=TYPES['A']) n_As.append(n_A) n_All = len(system.part) diff --git a/samples/grand_canonical.py b/samples/grand_canonical.py index 79549132bbd..c0b0a77f82b 100644 --- a/samples/grand_canonical.py +++ b/samples/grand_canonical.py @@ -99,9 +99,9 @@ # Set the hidden particle type to the lowest possible number to speed # up the simulation -RE.set_non_interacting_type(max(types) + 1) +RE.set_non_interacting_type(type=max(types) + 1) -RE.reaction(10000) +RE.reaction(reaction_steps=10000) p3m = espressomd.electrostatics.P3M(prefactor=2.0, accuracy=1e-3) system.actors.add(p3m) @@ -134,14 +134,14 @@ system.thermostat.set_langevin(kT=temperature, gamma=.5, seed=42) # MC warmup -RE.reaction(1000) +RE.reaction(reaction_steps=1000) n_int_cycles = 10000 n_int_steps = 600 num_As = [] deviation = None for i in range(n_int_cycles): - RE.reaction(10) + RE.reaction(reaction_steps=10) system.integrator.run(steps=n_int_steps) num_As.append(system.number_of_particles(type=1)) if i > 2 and i % 50 == 0: diff --git a/samples/reaction_ensemble.py b/samples/reaction_ensemble.py index edaa86027b8..1f1c0e91a3a 100644 --- a/samples/reaction_ensemble.py +++ b/samples/reaction_ensemble.py @@ -90,8 +90,7 @@ default_charges=charge_dict) elif args.mode == "constant_pH_ensemble": RE = espressomd.reaction_ensemble.ConstantpHEnsemble( - kT=1, exclusion_radius=1, seed=77) - RE.constant_pH = 2 + kT=1, exclusion_radius=1, seed=77, constant_pH=2) RE.add_reaction(gamma=K_diss, reactant_types=[types["HA"]], product_types=[types["A-"], types["H+"]], default_charges=charge_dict) @@ -105,14 +104,20 @@ # Set the hidden particle type to the lowest possible number to speed # up the simulation -RE.set_non_interacting_type(max(types.values()) + 1) +RE.set_non_interacting_type(type=max(types.values()) + 1) for i in range(10000): - RE.reaction() + RE.reaction(reaction_steps=1) if i % 100 == 0: print("HA", system.number_of_particles(type=types["HA"]), "A-", system.number_of_particles(type=types["A-"]), "H+", system.number_of_particles(type=types["H+"])) -print("reaction 0 has acceptance rate: ", RE.get_acceptance_rate_reaction(0)) -print("reaction 1 has acceptance rate: ", RE.get_acceptance_rate_reaction(1)) +print( + "reaction 0 has acceptance rate: ", + RE.get_acceptance_rate_reaction( + reaction_id=0)) +print( + "reaction 1 has acceptance rate: ", + RE.get_acceptance_rate_reaction( + reaction_id=1)) diff --git a/samples/reaction_ensemble_complex_reaction.py b/samples/reaction_ensemble_complex_reaction.py index 4fc3b3a16cd..33de01ca339 100644 --- a/samples/reaction_ensemble_complex_reaction.py +++ b/samples/reaction_ensemble_complex_reaction.py @@ -97,13 +97,13 @@ # Set the hidden particle type to the lowest possible number to speed # up the simulation -RE.set_non_interacting_type(max(types) + 1) +RE.set_non_interacting_type(type=max(types) + 1) # warmup -RE.reaction(200) +RE.reaction(reaction_steps=200) for i in range(200): - RE.reaction(10) + RE.reaction(reaction_steps=10) for _type in types: numbers[_type].append(system.number_of_particles(type=_type)) diff --git a/samples/widom_insertion.py b/samples/widom_insertion.py index 356361d662d..e2dcc7c129b 100644 --- a/samples/widom_insertion.py +++ b/samples/widom_insertion.py @@ -116,7 +116,7 @@ # Set the hidden particle type to the lowest possible number to speed # up the simulation -widom.set_non_interacting_type(max(types) + 1) +widom.set_non_interacting_type(type=max(types) + 1) particle_insertion_potential_energy_samples = [] @@ -126,7 +126,7 @@ for i in range(n_iterations): for _ in range(n_samples_per_iteration): particle_insertion_potential_energy_samples.append( - widom.calculate_particle_insertion_potential_energy(0)) + widom.calculate_particle_insertion_potential_energy(reaction_id=insertion_reaction_id)) system.integrator.run(steps=500) if i % 20 == 0: diff --git a/src/core/reaction_methods/ConstantpHEnsemble.hpp b/src/core/reaction_methods/ConstantpHEnsemble.hpp index b533999cfa9..0fe32c23980 100644 --- a/src/core/reaction_methods/ConstantpHEnsemble.hpp +++ b/src/core/reaction_methods/ConstantpHEnsemble.hpp @@ -40,8 +40,11 @@ namespace ReactionMethods { */ class ConstantpHEnsemble : public ReactionAlgorithm { public: - ConstantpHEnsemble(int seed) : ReactionAlgorithm(seed) {} - double m_constant_pH = -10; + ConstantpHEnsemble(int seed, double kT, double exclusion_radius, + double constant_pH) + : ReactionAlgorithm(seed, kT, exclusion_radius), + m_constant_pH(constant_pH) {} + double m_constant_pH; protected: double calculate_acceptance_probability( diff --git a/src/core/reaction_methods/ReactionAlgorithm.cpp b/src/core/reaction_methods/ReactionAlgorithm.cpp index 352e56d02c8..d6bea4bc8b6 100644 --- a/src/core/reaction_methods/ReactionAlgorithm.cpp +++ b/src/core/reaction_methods/ReactionAlgorithm.cpp @@ -55,7 +55,7 @@ int ReactionAlgorithm::do_reaction(int reaction_steps) { auto current_E_pot = calculate_current_potential_energy_of_system(); for (int i = 0; i < reaction_steps; i++) { int reaction_id = i_random(static_cast(reactions.size())); - generic_oneway_reaction(reactions[reaction_id], current_E_pot); + generic_oneway_reaction(*reactions[reaction_id], current_E_pot); } return 0; } @@ -64,17 +64,12 @@ int ReactionAlgorithm::do_reaction(int reaction_steps) { * Adds a reaction to the reaction system */ void ReactionAlgorithm::add_reaction( - double gamma, const std::vector &reactant_types, - const std::vector &reactant_coefficients, - const std::vector &product_types, - const std::vector &product_coefficients) { - SingleReaction new_reaction(gamma, reactant_types, reactant_coefficients, - product_types, product_coefficients); + std::shared_ptr const &new_reaction) { // make ESPResSo count the particle numbers which take part in the reactions - for (int reactant_type : new_reaction.reactant_types) + for (int reactant_type : new_reaction->reactant_types) init_type_map(reactant_type); - for (int product_type : new_reaction.product_types) + for (int product_type : new_reaction->product_types) init_type_map(product_type); init_type_map(non_interacting_type); @@ -91,20 +86,13 @@ void ReactionAlgorithm::check_reaction_method() const { throw std::runtime_error("Reaction system not initialized"); } - if (kT < 0) { - throw std::runtime_error("kT cannot be negative," - "normally it should be 1.0. This will be used" - "directly to calculate beta:=1/(k_B T) which" - "occurs in the exp(-beta*E)\n"); - } - #ifdef ELECTROSTATICS // check for the existence of default charges for all types that take part in // the reactions for (const auto ¤t_reaction : reactions) { // check for reactants - for (int reactant_type : current_reaction.reactant_types) { + for (int reactant_type : current_reaction->reactant_types) { auto it = charges_of_types.find(reactant_type); if (it == charges_of_types.end()) { std::string message = std::string("Forgot to assign charge to type ") + @@ -113,7 +101,7 @@ void ReactionAlgorithm::check_reaction_method() const { } } // check for products - for (int product_type : current_reaction.product_types) { + for (int product_type : current_reaction->product_types) { auto it = charges_of_types.find(product_type); if (it == charges_of_types.end()) { std::string message = std::string("Forgot to assign charge to type ") + @@ -127,12 +115,9 @@ void ReactionAlgorithm::check_reaction_method() const { /** * Automatically sets the volume which is used by the reaction ensemble to the - * volume of a cuboid box, if not already initialized with another value. + * volume of a cuboid box. */ -void ReactionAlgorithm::set_cuboid_reaction_ensemble_volume() { - if (volume < 0) - volume = box_geo.volume(); -} +void ReactionAlgorithm::update_volume() { volume = box_geo.volume(); } /** * Checks whether all particles exist for the provided reaction. @@ -408,7 +393,7 @@ void ReactionAlgorithm::check_exclusion_radius(int p_id) { * delete unbonded particles since bonds are coupled to ids. This is used to * avoid the id range becoming excessively huge. */ -int ReactionAlgorithm::delete_particle(int p_id) { +void ReactionAlgorithm::delete_particle(int p_id) { auto const old_max_seen_id = get_maximal_particle_id(); if (p_id == old_max_seen_id) { // last particle, just delete @@ -430,7 +415,6 @@ int ReactionAlgorithm::delete_particle(int p_id) { throw std::runtime_error( "Particle id is greater than the max seen particle id"); } - return 0; } void ReactionAlgorithm::set_cyl_constraint(double center_x, double center_y, diff --git a/src/core/reaction_methods/ReactionAlgorithm.hpp b/src/core/reaction_methods/ReactionAlgorithm.hpp index 6f26db7aa7b..a089cdae3d5 100644 --- a/src/core/reaction_methods/ReactionAlgorithm.hpp +++ b/src/core/reaction_methods/ReactionAlgorithm.hpp @@ -28,7 +28,9 @@ #include #include +#include #include +#include #include #include #include @@ -45,24 +47,33 @@ struct StoredParticleProperty { class ReactionAlgorithm { public: - ReactionAlgorithm(int seed) + ReactionAlgorithm(int seed, double kT, double exclusion_radius) : m_generator(Random::mt19937(std::seed_seq({seed, seed, seed}))), m_normal_distribution(0.0, 1.0), m_uniform_real_distribution(0.0, 1.0) { + if (kT < 0.) { + throw std::domain_error("Invalid value for 'kT'"); + } + if (exclusion_radius < 0.) { + throw std::domain_error("Invalid value for 'exclusion_radius'"); + } + this->kT = kT; + this->exclusion_radius = exclusion_radius; + update_volume(); } virtual ~ReactionAlgorithm() = default; - std::vector reactions; + std::vector> reactions; std::map charges_of_types; - double kT = -10.0; + double kT; /** * Hard sphere radius. If particles are closer than this value, * it is assumed that their interaction energy gets approximately * infinite, therefore these configurations do not contribute * to the partition function and ensemble averages. */ - double exclusion_radius = 0.0; - double volume = -10.0; + double exclusion_radius; + double volume; int non_interacting_type = 100; int m_accepted_configurational_MC_moves = 0; @@ -72,21 +83,30 @@ class ReactionAlgorithm { static_cast(m_tried_configurational_MC_moves); } - void set_cuboid_reaction_ensemble_volume(); + auto get_kT() const { return kT; } + auto get_exclusion_radius() const { return exclusion_radius; } + auto get_volume() const { return volume; } + void set_volume(double new_volume) { + if (new_volume <= 0.) { + throw std::domain_error("Invalid value for 'volume'"); + } + volume = new_volume; + } + void update_volume(); virtual int do_reaction(int reaction_steps); void check_reaction_method() const; void remove_constraint() { m_reaction_constraint = ReactionConstraint::NONE; } void set_cyl_constraint(double center_x, double center_y, double radius); void set_slab_constraint(double slab_start_z, double slab_end_z); Utils::Vector2d get_slab_constraint_parameters() const { + if (m_reaction_constraint != ReactionConstraint::SLAB_Z) { + throw std::runtime_error("no slab constraint is currently active"); + } return {m_slab_start_z, m_slab_end_z}; } - int delete_particle(int p_id); - void add_reaction(double gamma, const std::vector &reactant_types, - const std::vector &reactant_coefficients, - const std::vector &product_types, - const std::vector &product_coefficients); + void delete_particle(int p_id); + void add_reaction(std::shared_ptr const &new_reaction); void delete_reaction(int reaction_id) { reactions.erase(reactions.begin() + reaction_id); } diff --git a/src/core/reaction_methods/ReactionEnsemble.hpp b/src/core/reaction_methods/ReactionEnsemble.hpp index c484e7176cd..4bfa2dcef6c 100644 --- a/src/core/reaction_methods/ReactionEnsemble.hpp +++ b/src/core/reaction_methods/ReactionEnsemble.hpp @@ -36,7 +36,8 @@ namespace ReactionMethods { */ class ReactionEnsemble : public ReactionAlgorithm { public: - ReactionEnsemble(int seed) : ReactionAlgorithm(seed) {} + ReactionEnsemble(int seed, double kT, double exclusion_radius) + : ReactionAlgorithm(seed, kT, exclusion_radius) {} protected: double calculate_acceptance_probability( diff --git a/src/core/reaction_methods/SingleReaction.hpp b/src/core/reaction_methods/SingleReaction.hpp index ffc89835db2..5bd1a75da49 100644 --- a/src/core/reaction_methods/SingleReaction.hpp +++ b/src/core/reaction_methods/SingleReaction.hpp @@ -32,6 +32,14 @@ struct SingleReaction { std::vector const &reactant_coefficients, std::vector const &product_types, std::vector const &product_coefficients) { + if (reactant_types.size() != reactant_coefficients.size()) { + throw std::invalid_argument( + "reactants: number of types and coefficients have to match"); + } + if (product_types.size() != product_coefficients.size()) { + throw std::invalid_argument( + "products: number of types and coefficients have to match"); + } this->reactant_types = reactant_types; this->reactant_coefficients = reactant_coefficients; this->product_types = product_types; diff --git a/src/core/reaction_methods/WidomInsertion.hpp b/src/core/reaction_methods/WidomInsertion.hpp index e455a045aa0..5196f950245 100644 --- a/src/core/reaction_methods/WidomInsertion.hpp +++ b/src/core/reaction_methods/WidomInsertion.hpp @@ -28,7 +28,8 @@ namespace ReactionMethods { /** Widom insertion method */ class WidomInsertion : public ReactionAlgorithm { public: - WidomInsertion(int seed) : ReactionAlgorithm(seed) {} + WidomInsertion(int seed, double kT, double exclusion_radius) + : ReactionAlgorithm(seed, kT, exclusion_radius) {} double calculate_particle_insertion_potential_energy( SingleReaction ¤t_reaction); }; diff --git a/src/core/reaction_methods/tests/ConstantpHEnsemble_test.cpp b/src/core/reaction_methods/tests/ConstantpHEnsemble_test.cpp index d110205fcaa..ec9a6eb930d 100644 --- a/src/core/reaction_methods/tests/ConstantpHEnsemble_test.cpp +++ b/src/core/reaction_methods/tests/ConstantpHEnsemble_test.cpp @@ -50,9 +50,7 @@ BOOST_AUTO_TEST_CASE(ConstantpHEnsemble_test) { }; constexpr double tol = 100 * std::numeric_limits::epsilon(); - ConstantpHEnsembleTest r_algo(42); - r_algo.kT = 20.; - r_algo.m_constant_pH = 1.; + ConstantpHEnsembleTest r_algo(42, 20., 0., 1.); // exception if no reaction was added BOOST_CHECK_THROW(r_algo.check_reaction_method(), std::runtime_error); diff --git a/src/core/reaction_methods/tests/ReactionAlgorithm_test.cpp b/src/core/reaction_methods/tests/ReactionAlgorithm_test.cpp index da8c2f9d880..2c6fd66e3ee 100644 --- a/src/core/reaction_methods/tests/ReactionAlgorithm_test.cpp +++ b/src/core/reaction_methods/tests/ReactionAlgorithm_test.cpp @@ -59,7 +59,7 @@ BOOST_AUTO_TEST_CASE(ReactionAlgorithm_test) { constexpr double tol = 100 * std::numeric_limits::epsilon(); // check acceptance rate - ReactionAlgorithmTest r_algo(42); + ReactionAlgorithmTest r_algo(42, 1., 0.); for (int tried_moves = 1; tried_moves < 5; ++tried_moves) { for (int accepted_moves = 0; accepted_moves < 5; ++accepted_moves) { r_algo.m_tried_configurational_MC_moves = tried_moves; @@ -87,11 +87,11 @@ BOOST_AUTO_TEST_CASE(ReactionAlgorithm_test) { // check reaction addition { - r_algo.add_reaction(reaction.gamma, reaction.reactant_types, - reaction.reactant_coefficients, reaction.product_types, - reaction.product_coefficients); + r_algo.add_reaction(std::make_shared( + reaction.gamma, reaction.reactant_types, reaction.reactant_coefficients, + reaction.product_types, reaction.product_coefficients)); BOOST_REQUIRE_EQUAL(r_algo.reactions.size(), 1ul); - auto const &value = r_algo.reactions[0]; + auto const &value = *r_algo.reactions[0]; BOOST_TEST(value.reactant_types == reaction.reactant_types, boost::test_tools::per_element()); BOOST_TEST(value.reactant_coefficients == reaction.reactant_coefficients, @@ -110,12 +110,6 @@ BOOST_AUTO_TEST_CASE(ReactionAlgorithm_test) { BOOST_CHECK_EQUAL(probability, -10.); } - // exception if kT is negative - BOOST_CHECK_THROW(r_algo.check_reaction_method(), std::runtime_error); - - // set kT - r_algo.kT = 1.; - #ifdef ELECTROSTATICS // exception if reactant types have no charge information BOOST_CHECK_THROW(r_algo.check_reaction_method(), std::runtime_error); @@ -131,16 +125,16 @@ BOOST_AUTO_TEST_CASE(ReactionAlgorithm_test) { // check reaction removal { - SingleReaction const new_reaction(5., {type_B}, {1}, {type_C}, {1}); - r_algo.add_reaction(new_reaction.gamma, new_reaction.reactant_types, - new_reaction.reactant_coefficients, - new_reaction.product_types, - new_reaction.product_coefficients); + auto const new_gamma = 5.; + auto const new_reaction = std::make_shared( + new_gamma, std::vector{type_B}, std::vector{1}, + std::vector{type_C}, std::vector{1}); + r_algo.add_reaction(new_reaction); BOOST_REQUIRE_EQUAL(r_algo.reactions.size(), 2ul); - BOOST_CHECK_EQUAL(r_algo.reactions[1].gamma, new_reaction.gamma); + BOOST_CHECK_EQUAL(r_algo.reactions[1]->gamma, new_gamma); r_algo.delete_reaction(1); BOOST_REQUIRE_EQUAL(r_algo.reactions.size(), 1ul); - BOOST_CHECK_EQUAL(r_algo.reactions[0].gamma, reaction.gamma); + BOOST_CHECK_EQUAL(r_algo.reactions[0]->gamma, reaction.gamma); r_algo.delete_reaction(0); BOOST_REQUIRE_EQUAL(r_algo.reactions.size(), 0ul); } diff --git a/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp b/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp index a6d836947fc..d115fb424e8 100644 --- a/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp +++ b/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp @@ -61,9 +61,8 @@ BOOST_AUTO_TEST_CASE(ReactionEnsemble_test) { // check basic interface { - ReactionEnsembleTest r_algo(42); - r_algo.volume = 10.; - r_algo.kT = 20.; + ReactionEnsembleTest r_algo(42, 20., 0.); + r_algo.set_volume(10.); // exception if no reaction was added BOOST_CHECK_THROW(r_algo.check_reaction_method(), std::runtime_error); @@ -98,10 +97,8 @@ BOOST_AUTO_TEST_CASE(ReactionEnsemble_test) { // check that the system energy is updated after a succesful reaction { - ReactionEnsembleTest test_reaction(42); - test_reaction.volume = 1.; - test_reaction.kT = 1.; - test_reaction.exclusion_radius = 0; + ReactionEnsembleTest test_reaction(42, 1., 0.); + test_reaction.set_volume(1.); // create a generic identity exchange reaction D <-> E int const type_D = 0; diff --git a/src/python/espressomd/reaction_ensemble.pxd b/src/python/espressomd/reaction_ensemble.pxd deleted file mode 100644 index 4a1261a468e..00000000000 --- a/src/python/espressomd/reaction_ensemble.pxd +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (C) 2010-2019 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 "myconfig.pxi" - -from libcpp cimport bool -from libcpp.vector cimport vector -from libcpp.pair cimport pair -from libcpp.string cimport string -from libcpp.map cimport map -from .utils cimport Vector2d - -cdef extern from "reaction_methods/SingleReaction.hpp" namespace "ReactionMethods": - - ctypedef struct SingleReaction: - vector[int] reactant_types - vector[int] reactant_coefficients - vector[int] product_types - vector[int] product_coefficients - double gamma - int nu_bar - double get_acceptance_rate() - -cdef extern from "reaction_methods/ReactionAlgorithm.hpp" namespace "ReactionMethods": - - cdef cppclass CReactionAlgorithm "ReactionMethods::ReactionAlgorithm": - int CReactionAlgorithm(int seed) - int do_reaction(int reaction_steps) except + - bool do_global_mc_move_for_particles_of_type(int type, int particle_number_of_type) - void set_cuboid_reaction_ensemble_volume() - int check_reaction_method() except + - double get_acceptance_rate_configurational_moves() - int delete_particle(int p_id) - void add_reaction(double gamma, vector[int] reactant_types, vector[int] reactant_coefficients, vector[int] product_types, vector[int] product_coefficients) except + - void delete_reaction(int reaction_id) - void set_cyl_constraint(double center_x, double center_y, double radius) except + - void set_slab_constraint(double slab_start_z, double slab_end_z) except + - void remove_constraint() - Vector2d get_slab_constraint_parameters() - - vector[SingleReaction] reactions - map[int, double] charges_of_types - double kT - double exclusion_radius - double volume - int non_interacting_type - -cdef extern from "reaction_methods/ReactionEnsemble.hpp" namespace "ReactionMethods": - - cdef cppclass CReactionEnsemble "ReactionMethods::ReactionEnsemble"(CReactionAlgorithm): - CReactionEnsemble(int seed) - -cdef extern from "reaction_methods/ConstantpHEnsemble.hpp" namespace "ReactionMethods": - - cdef cppclass CConstantpHEnsemble "ReactionMethods::ConstantpHEnsemble"(CReactionAlgorithm): - CConstantpHEnsemble(int seed) - double m_constant_pH - -cdef extern from "reaction_methods/WidomInsertion.hpp" namespace "ReactionMethods": - - cdef cppclass CWidomInsertion "ReactionMethods::WidomInsertion"(CReactionAlgorithm): - CWidomInsertion(int seed) - double calculate_particle_insertion_potential_energy(SingleReaction & current_reaction) except + diff --git a/src/python/espressomd/reaction_ensemble.py b/src/python/espressomd/reaction_ensemble.py new file mode 100644 index 00000000000..289aeefcc45 --- /dev/null +++ b/src/python/espressomd/reaction_ensemble.py @@ -0,0 +1,542 @@ +# Copyright (C) 2010-2019 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 numpy as np +import warnings +from .script_interface import ScriptInterfaceHelper, script_interface_register +from . import utils + + +@script_interface_register +class SingleReaction(ScriptInterfaceHelper): + _so_name = "ReactionMethods::SingleReaction" + _so_creation_policy = "LOCAL" + + def __init__(self, **kwargs): + super().__init__(**kwargs) + if not 'sip' in kwargs: + utils.check_valid_keys(self.valid_keys(), kwargs.keys()) + + def valid_keys(self): + return ("reactant_types", "reactant_coefficients", + "product_types", "product_coefficients", "gamma") + + def required_keys(self): + return ("reactant_types", "reactant_coefficients", + "product_types", "product_coefficients", "gamma") + + def make_backward_reaction(self): + return SingleReaction( + gamma=1. / self.gamma, reactant_types=self.product_types, + reactant_coefficients=self.product_coefficients, + product_types=self.reactant_types, + product_coefficients=self.reactant_coefficients) + + +@script_interface_register +class ReactionAlgorithm(ScriptInterfaceHelper): + """ + + This class provides the base class for Reaction Algorithms like + the Reaction Ensemble algorithm and the constant pH method. + Initialize the reaction algorithm by setting the + standard pressure, temperature, and the exclusion radius. + + Note: When creating particles the velocities of the new particles are set + according the Maxwell-Boltzmann distribution. In this step the mass of the + new particle is assumed to equal 1. + + + Parameters + ---------- + kT : :obj:`float` + Thermal energy of the system in simulation units + exclusion_radius : :obj:`float` + Minimal distance from any particle, within which new particle will not + be inserted. This is useful to avoid integrator failures if particles + are too close and there is a diverging repulsive interaction, or to + prevent two oppositely charged particles from being placed on top of + each other. The Boltzmann factor :math:`\\exp(-\\beta E)` gives these + configurations a small contribution to the partition function, + therefore they can be neglected. + seed : :obj:`int` + Initial counter value (or seed) of the Mersenne Twister RNG. + + Methods + ------- + remove_constraint() + Remove any previously defined constraint. + Requires setting the volume using :meth:`set_volume`. + + set_cylindrical_constraint_in_z_direction() + Constrain the reaction moves within a cylinder aligned on the z-axis. + Requires setting the volume using :meth:`set_volume`. + + Parameters + ---------- + center_x : :obj:`float` + x coordinate of center of the cylinder. + center_y : :obj:`float` + y coordinate of center of the cylinder. + radius_of_cylinder : :obj:`float` + radius of the cylinder + + set_wall_constraints_in_z_direction() + Restrict the sampling area to a slab in z-direction. Requires setting + the volume using :meth:`set_volume`. This constraint is necessary when + working with :ref:`Electrostatic Layer Correction (ELC)`. + + Parameters + ---------- + slab_start_z : :obj:`float` + z coordinate of the bottom wall. + slab_end_z : :obj:`float` + z coordinate of the top wall. + + Examples + -------- + + >>> import espressomd + >>> import espressomd.shapes + >>> import espressomd.electrostatics + >>> import espressomd.reaction_ensemble + >>> import numpy as np + >>> # setup a charged system + >>> box_l = 20 + >>> elc_gap = 10 + >>> system = espressomd.System(box_l=[box_l, box_l, box_l + elc_gap]) + >>> system.time_step = 0.001 + >>> system.cell_system.skin = 0.4 + >>> types = {"HA": 0, "A-": 1, "H+": 2, "wall": 3} + >>> charges = {types["HA"]: 0, types["A-"]: -1, types["H+"]: +1} + >>> for i in range(10): + ... system.part.add(pos=np.random.random(3) * box_l, type=types["A-"], q=charges[types["A-"]]) + ... system.part.add(pos=np.random.random(3) * box_l, type=types["H+"], q=charges[types["H+"]]) + >>> for particle_type in charges.keys(): + ... system.non_bonded_inter[particle_type, types["wall"]].wca.set_params(epsilon=1.0, sigma=1.0) + >>> # add ELC actor + >>> p3m = espressomd.electrostatics.P3M(prefactor=1.0, accuracy=1e-2) + >>> elc = espressomd.electrostatics.ELC(p3m_actor=p3m, maxPWerror=1.0, gap_size=elc_gap) + >>> system.actors.add(elc) + >>> # add constant pH method + >>> RE = espressomd.reaction_ensemble.ConstantpHEnsemble(kT=1, exclusion_radius=1, seed=77) + >>> RE.constant_pH = 2 + >>> RE.add_reaction(gamma=0.0088, reactant_types=[types["HA"]], + ... product_types=[types["A-"], types["H+"]], + ... default_charges=charges) + >>> # add walls for the ELC gap + >>> RE.set_wall_constraints_in_z_direction(0, box_l) + >>> RE.set_volume(box_l**3) + >>> system.constraints.add(shape=espressomd.shapes.Wall(dist=0, normal=[0, 0, 1]), + ... particle_type=types["wall"]) + >>> system.constraints.add(shape=espressomd.shapes.Wall(dist=-box_l, normal=[0, 0, -1]), + ... particle_type=types["wall"]) + + get_wall_constraints_in_z_direction() + Returns the restrictions of the sampling area in z-direction. + + set_volume() + Set the volume to be used in the acceptance probability of the reaction + ensemble. This can be useful when using constraints, if the relevant + volume is different from the box volume. If not used the default volume + which is used, is the box volume. + + Parameters + ---------- + volume : :obj:`float` + Volume of the system in simulation units + + get_volume() + Get the volume to be used in the acceptance probability of the reaction + ensemble. + + get_acceptance_rate_configurational_moves(): + Returns the acceptance rate for the configuration moves. + + get_acceptance_rate_reaction() + Returns the acceptance rate for the given reaction. + + Parameters + ---------- + reaction_id : :obj:`int` + Reaction id + + set_non_interacting_type() + Sets the particle type for non-interacting particles. + Default value: 100. + This is used to temporarily hide particles during a reaction trial + move, if they are to be deleted after the move is accepted. Please + change this value if you intend to use the type 100 for some other + particle types with interactions, or if you need improved performance, + as the default value of 100 causes some overhead. + Please also note that particles + in the current implementation of the Reaction Ensemble are only + hidden with respect to Lennard-Jones and Coulomb interactions. Hiding + of other interactions, for example a magnetic, needs to be implemented + in the code. + + Parameters + ---------- + type : :obj:`int` + Particle type for the hidden particles + + get_non_interacting_type() + Returns the type which is used for hiding particle + + reaction() + Performs randomly selected reactions. + + Parameters + ---------- + reaction_steps : :obj:`int`, optional + The number of reactions to be performed at once, defaults to 1. + + displacement_mc_move_for_particles_of_type() + Performs a displacement Monte Carlo move for particles of given type. + New positions of the displaced particles are chosen from the whole box + with a uniform probability distribution. If there are multiple types, + that are being moved in a simulation, they should be moved in a random + order to avoid artefacts. + + Parameters + ---------- + type_mc : :obj:`int` + Particle type which should be moved + particle_number_to_be_changed : :obj:`int` + Number of particles to move, defaults to 1. + + delete_particle() + Deletes the particle of the given p_id and makes sure that the particle + range has no holes. This function has some restrictions, as e.g. bonds + are not deleted. Therefore only apply this function to simple ions. + + Parameters + ---------- + p_id : :obj:`int` + Id of the particle to be deleted. + + change_reaction_constant() + Changes the reaction constant of a given reaction + (for both the forward and backward reactions). + The ``reaction_id`` which is assigned to a reaction + depends on the order in which :meth:`add_reaction` was called. + The 0th reaction has ``reaction_id=0``, the next added + reaction needs to be addressed with ``reaction_id=1``, etc. + + Parameters + ---------- + reaction_id : :obj:`int` + Reaction id + gamma : :obj:`float` + New reaction constant + + delete_reaction() + Delete a reaction from the set of used reactions + (the forward and backward reaction). + The ``reaction_id`` which is assigned to a reaction + depends on the order in which :meth:`add_reaction` was called. + The 0th reaction has ``reaction_id=0``, the next added + reaction needs to be addressed with ``reaction_id=1``, etc. + After the deletion of a reaction subsequent reactions + take the ``reaction_id`` of the deleted reaction. + + Parameters + ---------- + reaction_id : :obj:`int` + Reaction id + + """ + + _so_name = "ReactionMethods::ReactionAlgorithm" + _so_creation_policy = "LOCAL" + _so_bind_methods = ("remove_constraint", + "get_wall_constraints_in_z_direction", + "set_wall_constraints_in_z_direction", + "set_cylindrical_constraint_in_z_direction", + "set_volume", + "get_volume", + "get_acceptance_rate_reaction", + "set_non_interacting_type", + "get_non_interacting_type", + "reaction", + "displacement_mc_move_for_particles_of_type", + "check_reaction_method", + "change_reaction_constant", + "delete_reaction", + "delete_particle", + ) + + def __init__(self, **kwargs): + super().__init__(**kwargs) + if not 'sip' in kwargs: + utils.check_valid_keys(self.valid_keys(), kwargs.keys()) + + def valid_keys(self): + return {"kT", "exclusion_radius", "seed"} + + def required_keys(self): + return {"kT", "exclusion_radius", "seed"} + + def add_reaction(self, **kwargs): + """ + Sets up a reaction in the forward and backward direction. + + Parameters + ---------- + gamma : :obj:`float` + Equilibrium constant of the reaction in simulation units, :math:`\\gamma` (see the User + guide, section 16.7.1. for the definition and further details). + reactant_types : list of :obj:`int` + List of particle types of reactants in the reaction. + reactant_coefficients : list of :obj:`int` + List of stoichiometric coefficients of the reactants in the same + order as the list of their types. + product_types : list of :obj:`int` + List of particle types of products in the reaction. + product_coefficients : list of :obj:`int` + List of stoichiometric coefficients of products of the reaction in + the same order as the list of their types + default_charges : :obj:`dict` + A dictionary of default charges for types that occur + in the provided reaction. + check_for_electroneutrality : :obj:`bool` + Check for electroneutrality of the given reaction. + Default is ``True``. + + """ + default_charges = kwargs.pop("default_charges") + neutrality_check = kwargs.pop("check_for_electroneutrality", True) + forward_reaction = SingleReaction(**kwargs) + backward_reaction = forward_reaction.make_backward_reaction() + if neutrality_check: + self._check_charge_neutrality( + type2charge=default_charges, + reaction=forward_reaction) + + self.call_method("add_reaction", reaction=forward_reaction) + self.call_method("add_reaction", reaction=backward_reaction) + + for ptype, charge in default_charges.items(): + self.call_method("set_charge_of_type", type=ptype, charge=charge) + self.call_method("check_reaction_method") + + def _check_charge_neutrality(self, type2charge, reaction): + if not isinstance(type2charge, dict): + raise ValueError( + "No dictionary for relation between types and default charges provided.") + charges = np.array(list(type2charge.values())) + if np.count_nonzero(charges) == 0: + # all particles have zero charge + # no need to check electroneutrality + return + # calculate net change of electrical charge for the reaction + net_charge_change = 0.0 + for coef, ptype in zip( + reaction.reactant_coefficients, reaction.reactant_types): + net_charge_change -= coef * type2charge[ptype] + for coef, ptype in zip( + reaction.product_coefficients, reaction.product_types): + net_charge_change += coef * type2charge[ptype] + min_abs_nonzero_charge = np.min( + np.abs(charges[np.nonzero(charges)[0]])) + if abs(net_charge_change) / min_abs_nonzero_charge > 1e-10: + raise ValueError("Reaction system is not charge neutral") + + def get_status(self): + """ + Returns the status of the reaction ensemble in a dictionary containing + the used reactions, the used kT and the used exclusion radius. + + """ + + self.call_method("check_reaction_method") + reactions_list = [] + + for core_reaction in self.reactions: + reaction = {"reactant_coefficients": core_reaction.reactant_coefficients, + "reactant_types": core_reaction.reactant_types, + "product_types": core_reaction.product_types, + "product_coefficients": core_reaction.product_coefficients, + "reactant_types": core_reaction.reactant_types, + "gamma": core_reaction.gamma} + reactions_list.append(reaction) + + return {"reactions": reactions_list, "kT": self.kT, + "exclusion_radius": self.exclusion_radius} + + +@script_interface_register +class ReactionEnsemble(ReactionAlgorithm): + """ + This class implements the Reaction Ensemble. + """ + + _so_name = "ReactionMethods::ReactionEnsemble" + _so_creation_policy = "LOCAL" + + +@script_interface_register +class ConstantpHEnsemble(ReactionAlgorithm): + """ + This class implements the constant pH Ensemble. + + When adding an acid-base reaction, the acid and base particle types + are always assumed to be at index 0 of the lists passed to arguments + ``reactant_types`` and ``product_types``. + + """ + _so_name = "ReactionMethods::ConstantpHEnsemble" + _so_creation_policy = "LOCAL" + + def valid_keys(self): + return {"kT", "exclusion_radius", "seed", "constant_pH"} + + def required_keys(self): + return {"kT", "exclusion_radius", "seed", "constant_pH"} + + def add_reaction(self, *args, **kwargs): + warn_msg = ( + "arguments 'reactant_coefficients' and 'product_coefficients' " + "are deprecated and are no longer necessary for the constant pH " + "ensemble. They are kept for backward compatibility but might " + "be deleted in future versions.") + err_msg = ("All product and reactant coefficients must equal one in " + "the constant pH method as implemented in ESPResSo.") + warn_user = False + + if "reactant_coefficients" in kwargs: + if kwargs["reactant_coefficients"][0] != 1: + raise ValueError(err_msg) + else: + warn_user = True + else: + kwargs["reactant_coefficients"] = [1] + + if "product_coefficients" in kwargs: + if kwargs["product_coefficients"][0] != 1 or kwargs["product_coefficients"][1] != 1: + raise ValueError(err_msg) + else: + warn_user = True + else: + kwargs["product_coefficients"] = [1, 1] + + if warn_user: + warnings.warn(warn_msg, FutureWarning) + + if(len(kwargs["product_types"]) != 2 or len(kwargs["reactant_types"]) != 1): + raise ValueError( + "The constant pH method is only implemented for reactions " + "with two product types and one adduct type.") + + super().add_reaction(*args, **kwargs) + + +@script_interface_register +class WidomInsertion(ReactionAlgorithm): + """ + This class implements the Widom insertion method in the canonical ensemble + for homogeneous systems, where the excess chemical potential is not + depending on the location. + + """ + + _so_name = "ReactionMethods::WidomInsertion" + _so_creation_policy = "LOCAL" + + def required_keys(self): + return {"kT", "seed"} + + def valid_keys(self): + return {"kT", "seed"} + + def add_reaction(self, **kwargs): + kwargs['gamma'] = 1. + super().add_reaction(**kwargs) + + def calculate_particle_insertion_potential_energy(self, **kwargs): + """ + Measures the potential energy when particles are inserted in the + system following the reaction provided ``reaction_id``. Please + define the insertion moves first by calling the method + :meth:`~ReactionAlgorithm.add_reaction` (with only product types + specified). + + Note that although this function does not provide directly + the chemical potential, it can be used to calculate it. + For an example of such an application please check + :file:`/samples/widom_insertion.py`. + """ + # make inverse widom scheme (deletion of particles) inaccessible. + # The deletion reactions are the odd reaction_ids + + return self.call_method( + "calculate_particle_insertion_potential_energy", **kwargs) + + def calculate_excess_chemical_potential( + self, **kwargs): + """ + Given a set of samples of the particle insertion potential energy, + calculates the excess chemical potential and its statistical error. + + Parameters + ---------- + particle_insertion_potential_energy_samples : array_like of :obj:`float` + Samples of the particle insertion potential energy. + N_blocks : :obj:`int`, optional + Number of bins for binning analysis. + + Returns + ------- + mean : :obj:`float` + Mean excess chemical potential. + error : :obj:`float` + Standard error of the mean. + """ + + def do_block_analysis(samples, N_blocks): + """ + Performs a binning analysis of samples. + Divides the samples in ``N_blocks`` equispaced blocks + and returns the mean and its uncertainty + """ + size_block = int(len(samples) / N_blocks) + block_list = [] + for block in range(N_blocks): + block_list.append( + np.mean(samples[block * size_block:(block + 1) * size_block])) + + sample_mean = np.mean(block_list) + sample_std = np.std(block_list, ddof=1) + sample_uncertainty = sample_std / np.sqrt(N_blocks) + + return sample_mean, sample_uncertainty + + kT = self.kT + + gamma_samples = np.exp(-1.0 * np.array( + kwargs["particle_insertion_potential_energy_samples"]) / kT) + + gamma_mean, gamma_std = do_block_analysis( + samples=gamma_samples, N_blocks=kwargs.get("N_blocks", 16)) + + mu_ex_mean = -kT * np.log(gamma_mean) + + # full propagation of error + mu_ex_Delta = 0.5 * kT * abs(-np.log(gamma_mean + gamma_std) - + (-np.log(gamma_mean - gamma_std))) + + return mu_ex_mean, mu_ex_Delta diff --git a/src/python/espressomd/reaction_ensemble.pyx b/src/python/espressomd/reaction_ensemble.pyx deleted file mode 100644 index 246e6eba45a..00000000000 --- a/src/python/espressomd/reaction_ensemble.pyx +++ /dev/null @@ -1,686 +0,0 @@ -# Copyright (C) 2010-2019 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 "myconfig.pxi" -from libcpp.vector cimport vector -from libcpp.memory cimport unique_ptr -from cython.operator cimport dereference as deref -import numpy as np -import warnings - - -cdef class ReactionAlgorithm: - """ - - This class provides the base class for Reaction Algorithms like - the Reaction Ensemble algorithm and the constant pH method. - Initialize the reaction algorithm by setting the - standard pressure, temperature, and the exclusion radius. - - Note: When creating particles the velocities of the new particles are set - according the Maxwell-Boltzmann distribution. In this step the mass of the - new particle is assumed to equal 1. - - - Parameters - ---------- - kT : :obj:`float` - Thermal energy of the system in simulation units - exclusion_radius : :obj:`float` - Minimal distance from any particle, within which new particle will not - be inserted. This is useful to avoid integrator failures if particles - are too close and there is a diverging repulsive interaction, or to - prevent two oppositely charged particles from being placed on top of - each other. The Boltzmann factor :math:`\\exp(-\\beta E)` gives these - configurations a small contribution to the partition function, - therefore they can be neglected. - seed : :obj:`int` - Initial counter value (or seed) of the Mersenne Twister RNG. - """ - cdef object _params - cdef CReactionAlgorithm * RE - - def _valid_keys(self): - return "kT", "exclusion_radius", "seed" - - def _required_keys(self): - return "kT", "exclusion_radius", "seed" - - def _set_params_in_es_core(self): - deref(self.RE).kT = self._params["kT"] - # setting a volume is a side effect, sets the default volume of the - # reaction ensemble as the volume of the cuboid simulation box. this - # volume can be altered by the command "reaction ensemble volume - # " if one wants to simulate e.g. in a system with constraint - # (e.g. cuboid box with cylinder constraint, so that the particles are - # only contained in the volume of the cylinder) - if deref(self.RE).volume < 0: - deref(self.RE).set_cuboid_reaction_ensemble_volume() - deref(self.RE).exclusion_radius = self._params["exclusion_radius"] - - def remove_constraint(self): - """ - Remove any previously defined constraint. - Requires setting the volume using :meth:`set_volume`. - - """ - deref(self.RE).remove_constraint() - - def set_cylindrical_constraint_in_z_direction(self, center_x, center_y, - radius_of_cylinder): - """ - Constrain the reaction moves within a cylinder defined by its axis - passing through centres (:math:`x` and :math:`y`) and the radius. - Requires setting the volume using :meth:`set_volume`. - - Parameters - ---------- - center_x : :obj:`float` - x coordinate of center of the cylinder. - center_y : :obj:`float` - y coordinate of center of the cylinder. - radius_of_cylinder : :obj:`float` - radius of the cylinder - - """ - deref(self.RE).set_cyl_constraint( - center_x, center_y, radius_of_cylinder) - - def set_wall_constraints_in_z_direction(self, slab_start_z, slab_end_z): - """ - Restrict the sampling area to a slab in z-direction. Requires setting - the volume using :meth:`set_volume`. This constraint is necessary when - working with :ref:`Electrostatic Layer Correction (ELC)`. - - Parameters - ---------- - slab_start_z : :obj:`float` - z coordinate of the bottom wall. - slab_end_z : :obj:`float` - z coordinate of the top wall. - - Examples - -------- - - >>> import espressomd - >>> import espressomd.shapes - >>> import espressomd.electrostatics - >>> import espressomd.reaction_ensemble - >>> import numpy as np - >>> # setup a charged system - >>> box_l = 20 - >>> elc_gap = 10 - >>> system = espressomd.System(box_l=[box_l, box_l, box_l + elc_gap]) - >>> system.time_step = 0.001 - >>> system.cell_system.skin = 0.4 - >>> types = {"HA": 0, "A-": 1, "H+": 2, "wall": 3} - >>> charges = {types["HA"]: 0, types["A-"]: -1, types["H+"]: +1} - >>> for i in range(10): - ... system.part.add(pos=np.random.random(3) * box_l, type=types["A-"], q=charges[types["A-"]]) - ... system.part.add(pos=np.random.random(3) * box_l, type=types["H+"], q=charges[types["H+"]]) - >>> for particle_type in charges.keys(): - ... system.non_bonded_inter[particle_type, types["wall"]].wca.set_params(epsilon=1.0, sigma=1.0) - >>> # add ELC actor - >>> p3m = espressomd.electrostatics.P3M(prefactor=1.0, accuracy=1e-2) - >>> elc = espressomd.electrostatics.ELC(p3m_actor=p3m, maxPWerror=1.0, gap_size=elc_gap) - >>> system.actors.add(elc) - >>> # add constant pH method - >>> RE = espressomd.reaction_ensemble.ConstantpHEnsemble(kT=1, exclusion_radius=1, seed=77) - >>> RE.constant_pH = 2 - >>> RE.add_reaction(gamma=0.0088, reactant_types=[types["HA"]], - ... product_types=[types["A-"], types["H+"]], - ... default_charges=charges) - >>> # add walls for the ELC gap - >>> RE.set_wall_constraints_in_z_direction(0, box_l) - >>> RE.set_volume(box_l**3) - >>> system.constraints.add(shape=espressomd.shapes.Wall(dist=0, normal=[0, 0, 1]), - ... particle_type=types["wall"]) - >>> system.constraints.add(shape=espressomd.shapes.Wall(dist=-box_l, normal=[0, 0, -1]), - ... particle_type=types["wall"]) - - - """ - deref(self.RE).set_slab_constraint(slab_start_z, slab_end_z) - - def get_wall_constraints_in_z_direction(self): - """ - Returns the restrictions of the sampling area in z-direction. - - """ - v = deref(self.RE).get_slab_constraint_parameters() - return [v[0], v[1]] - - def set_volume(self, volume): - """ - Set the volume to be used in the acceptance probability of the reaction - ensemble. This can be useful when using constraints, if the relevant - volume is different from the box volume. If not used the default volume - which is used, is the box volume. - - """ - deref(self.RE).volume = volume - - def get_volume(self): - """ - Get the volume to be used in the acceptance probability of the reaction - ensemble. - - """ - return deref(self.RE).volume - - def get_acceptance_rate_configurational_moves(self): - """ - Returns the acceptance rate for the configuration moves. - - """ - return deref(self.RE).get_acceptance_rate_configurational_moves() - - def get_acceptance_rate_reaction(self, reaction_id): - """ - Returns the acceptance rate for the given reaction. - - """ - return deref(self.RE).reactions[reaction_id].get_acceptance_rate() - - def set_non_interacting_type(self, non_interacting_type): - """ - Sets the particle type for non-interacting particles. - Default value: 100. - This is used to temporarily hide particles during a reaction trial - move, if they are to be deleted after the move is accepted. Please - change this value if you intend to use the type 100 for some other - particle types with interactions, or if you need improved performance, - as the default value of 100 causes some overhead. - Please also note that particles - in the current implementation of the Reaction Ensemble are only - hidden with respect to Lennard-Jones and Coulomb interactions. Hiding - of other interactions, for example a magnetic, needs to be implemented - in the code. - """ - deref(self.RE).non_interacting_type = non_interacting_type - - def get_non_interacting_type(self): - """ - Returns the type which is used for hiding particles. - - """ - return deref(self.RE).non_interacting_type - - def add_reaction(self, *args, **kwargs): - """ - Sets up a reaction in the forward and backward direction. - - Parameters - ---------- - gamma : :obj:`float` - Equilibrium constant of the reaction, :math:`\\gamma` (see the User - guide, section 6.6 for the definition and further details). - reactant_types : list of :obj:`int` - List of particle types of reactants in the reaction. - reactant_coefficients : list of :obj:`int` - List of stoichiometric coefficients of the reactants in the same - order as the list of their types. - product_types : list of :obj:`int` - List of particle types of products in the reaction. - product_coefficients : list of :obj:`int` - List of stoichiometric coefficients of products of the reaction in - the same order as the list of their types - default_charges : :obj:`dict` - A dictionary of default charges for types that occur in the provided reaction. - check_for_electroneutrality : :obj:`bool` - Check for electroneutrality of the given reaction if ``True``. - - """ - self._params["check_for_electroneutrality"] = True - for k in self._required_keys_add(): - if k not in kwargs: - raise ValueError( - f"At least the following keys have to be given as keyword " - f"arguments: {self._required_keys()}, got {kwargs}") - self._params[k] = kwargs[k] - - for k in self._valid_keys_add(): - if k in kwargs: - self._params[k] = kwargs[k] - self._check_lengths_of_arrays() - self._validate_params_default_charge() - self._set_params_in_es_core_add() - - def _valid_keys_add(self): - return ("gamma", "reactant_types", "reactant_coefficients", - "product_types", "product_coefficients", "default_charges", - "check_for_electroneutrality") - - def _required_keys_add(self): - return ["gamma", "reactant_types", "reactant_coefficients", - "product_types", "product_coefficients", "default_charges"] - - def _check_lengths_of_arrays(self): - if(len(self._params["reactant_types"]) != len(self._params["reactant_coefficients"])): - raise ValueError( - "Reactants: Number of types and coefficients have to be equal") - if(len(self._params["product_types"]) != len(self._params["product_coefficients"])): - raise ValueError( - "Products: Number of types and coefficients have to be equal") - - def _set_params_in_es_core_add(self): - cdef vector[int] reactant_types - cdef vector[int] reactant_coefficients - cdef vector[int] product_types - cdef vector[int] product_coefficients - for value in self._params["reactant_types"]: - reactant_types.push_back(value) - for value in self._params["reactant_coefficients"]: - reactant_coefficients.push_back(value) - for value in self._params["product_types"]: - product_types.push_back(value) - for value in self._params["product_coefficients"]: - product_coefficients.push_back(value) - - # forward reaction - deref(self.RE).add_reaction( - self._params["gamma"], reactant_types, reactant_coefficients, product_types, product_coefficients) - # backward reaction - deref(self.RE).add_reaction( - 1.0 / self._params["gamma"], product_types, product_coefficients, reactant_types, reactant_coefficients) - - for key, value in self._params["default_charges"].items(): - deref(self.RE).charges_of_types[int(key)] = value - deref(self.RE).check_reaction_method() - - def _validate_params_default_charge(self): - if not isinstance(self._params["default_charges"], dict): - raise ValueError( - "No dictionary for relation between types and default charges provided.") - # check electroneutrality of the provided reaction - if self._params["check_for_electroneutrality"]: - charges = np.array(list(self._params["default_charges"].values())) - if np.count_nonzero(charges) == 0: - # all particles have zero charge - # no need to check electroneutrality - return - total_charge_change = 0.0 - for i in range(len(self._params["reactant_coefficients"])): - type_here = self._params["reactant_types"][i] - total_charge_change -= self._params["reactant_coefficients"][ - i] * self._params["default_charges"][type_here] - for j in range(len(self._params["product_coefficients"])): - type_here = self._params["product_types"][j] - total_charge_change += self._params["product_coefficients"][ - j] * self._params["default_charges"][type_here] - min_abs_nonzero_charge = np.min( - np.abs(charges[np.nonzero(charges)[0]])) - if abs(total_charge_change) / min_abs_nonzero_charge > 1e-10: - raise ValueError("Reaction system is not charge neutral") - - def reaction(self, reaction_steps=1): - """ - Performs randomly selected reactions. - - Parameters - ---------- - reaction_steps : :obj:`int`, optional - The number of reactions to be performed at once, defaults to 1. - - """ - deref(self.RE).do_reaction(int(reaction_steps)) - - def displacement_mc_move_for_particles_of_type(self, type_mc, - particle_number_to_be_changed=1): - """ - Performs a displacement Monte Carlo move for particles of given type. - New positions of the displaced particles are chosen from the whole box - with a uniform probability distribution. If there are multiple types, - that are being moved in a simulation, they should be moved in a random - order to avoid artefacts. - - Parameters - ---------- - type_mc : :obj:`int` - particle type which should be moved - - """ - - deref(self.RE).do_global_mc_move_for_particles_of_type( - type_mc, particle_number_to_be_changed) - - def get_status(self): - """ - Returns the status of the reaction ensemble in a dictionary containing - the used reactions, the used kT and the used exclusion radius. - - """ - deref(self.RE).check_reaction_method() - reactions = [] - for single_reaction_i in range(deref(self.RE).reactions.size()): - reactant_types = [] - for i in range( - deref(self.RE).reactions[single_reaction_i].reactant_types.size()): - reactant_types.append( - deref(self.RE).reactions[single_reaction_i].reactant_types[i]) - reactant_coefficients = [] - for i in range( - deref(self.RE).reactions[single_reaction_i].reactant_types.size()): - reactant_coefficients.append( - deref(self.RE).reactions[single_reaction_i].reactant_coefficients[i]) - - product_types = [] - for i in range( - deref(self.RE).reactions[single_reaction_i].product_types.size()): - product_types.append( - deref(self.RE).reactions[single_reaction_i].product_types[i]) - product_coefficients = [] - for i in range( - deref(self.RE).reactions[single_reaction_i].product_types.size()): - product_coefficients.append( - deref(self.RE).reactions[single_reaction_i].product_coefficients[i]) - reaction = {"reactant_coefficients": reactant_coefficients, - "reactant_types": reactant_types, - "product_types": product_types, - "product_coefficients": product_coefficients, - "reactant_types": reactant_types, - "gamma": deref(self.RE).reactions[single_reaction_i].gamma} - reactions.append(reaction) - - return {"reactions": reactions, "kT": deref( - self.RE).kT, "exclusion_radius": deref(self.RE).exclusion_radius} - - def delete_particle(self, p_id): - """ - Deletes the particle of the given p_id and makes sure that the particle - range has no holes. This function has some restrictions, as e.g. bonds - are not deleted. Therefore only apply this function to simple ions. - - """ - deref(self.RE).delete_particle(p_id) - - def change_reaction_constant(self, reaction_id, gamma): - """ - Changes the reaction constant of a given reaction - (for the forward and backward reaction). - The ``reaction_id`` which is assigned to a reaction - depends on the order in which :meth:`add_reaction` was called. - The 0th reaction has ``reaction_id=0``, the next added - reaction needs to be addressed with ``reaction_id=1``, etc. - - Parameters - ---------- - reaction_id : :obj:`int` - reaction_id - gamma : :obj:`float` - new reaction constant - - """ - reaction_id = int(reaction_id) - if(reaction_id > deref(self.RE).reactions.size() / 2 - 1 or reaction_id < 0): - raise ValueError( - "You provided an invalid reaction_id, please provide a valid reaction_id") - # for the forward reaction - deref(self.RE).reactions[2 * reaction_id].gamma = gamma - # for the backward reaction - deref(self.RE).reactions[2 * reaction_id + 1].gamma = 1.0 / gamma - - def delete_reaction(self, reaction_id): - """ - Delete a reaction from the set of used reactions - (the forward and backward reaction). - The ``reaction_id`` which is assigned to a reaction - depends on the order in which :meth:`add_reaction` was called. - The 0th reaction has ``reaction_id=0``, the next added - reaction needs to be addressed with ``reaction_id=1``, etc. - After the deletion of a reaction subsequent reactions - take the ``reaction_id`` of the deleted reaction. - - Parameters - ---------- - reaction_id : :obj:`int` - reaction_id - - """ - reaction_id = int(reaction_id) - if(reaction_id > deref(self.RE).reactions.size() / 2 - 1 or reaction_id < 0): - raise ValueError( - "You provided an invalid reaction_id, please provide a valid reaction_id") - deref(self.RE).delete_reaction(2 * reaction_id + 1) - deref(self.RE).delete_reaction(2 * reaction_id) - -cdef class ReactionEnsemble(ReactionAlgorithm): - """ - This class implements the Reaction Ensemble. - """ - - cdef unique_ptr[CReactionEnsemble] REptr - - def __init__(self, *args, **kwargs): - self._params = {"kT": 1, - "exclusion_radius": 0} - for k in self._required_keys(): - if k not in kwargs: - raise ValueError( - f"At least the following keys have to be given as keyword " - f"arguments: {self._required_keys()}, got {kwargs}") - self._params[k] = kwargs[k] - - self.REptr.reset(new CReactionEnsemble(int(self._params["seed"]))) - self.RE = self.REptr.get() - - for k in kwargs: - if k in self._valid_keys(): - self._params[k] = kwargs[k] - else: - raise KeyError(f"{k} is not a valid key") - - self._set_params_in_es_core() - -cdef class ConstantpHEnsemble(ReactionAlgorithm): - """ - This class implements the constant pH Ensemble. - - When adding an acid-base reaction, the acid and base particle types - are always assumed to be at index 0 of the lists passed to arguments - ``reactant_types`` and ``product_types``. - - """ - cdef unique_ptr[CConstantpHEnsemble] constpHptr - - def __init__(self, *args, **kwargs): - self._params = {"kT": 1, "exclusion_radius": 0} - for k in self._required_keys(): - if k not in kwargs: - raise ValueError( - f"At least the following keys have to be given as keyword " - f"arguments: {self._required_keys()}, got {kwargs}") - self._params[k] = kwargs[k] - - self.constpHptr.reset(new CConstantpHEnsemble(int(self._params["seed"]))) - self.RE = self.constpHptr.get() - - for k in kwargs: - if k in self._valid_keys(): - self._params[k] = kwargs[k] - else: - raise KeyError(f"{k} is not a valid key") - - self._set_params_in_es_core() - - def add_reaction(self, *args, **kwargs): - warn_msg = ( - "arguments 'reactant_coefficients' and 'product_coefficients' " - "are deprecated and are no longer necessary for the constant pH " - "ensemble. They are kept for backward compatibility but might " - "be deleted in future versions.") - err_msg = ("All product and reactant coefficients must equal one in " - "the constant pH method as implemented in ESPResSo.") - warn_user = False - - if "reactant_coefficients" in kwargs: - if kwargs["reactant_coefficients"][0] != 1: - raise ValueError(err_msg) - else: - warn_user = True - else: - kwargs["reactant_coefficients"] = [1] - - if "product_coefficients" in kwargs: - if kwargs["product_coefficients"][0] != 1 or kwargs["product_coefficients"][1] != 1: - raise ValueError(err_msg) - else: - warn_user = True - else: - kwargs["product_coefficients"] = [1, 1] - - if warn_user: - warnings.warn(warn_msg, FutureWarning) - - if(len(kwargs["product_types"]) != 2 or len(kwargs["reactant_types"]) != 1): - raise ValueError( - "The constant pH method is only implemented for reactions " - "with two product types and one adduct type.") - - super().add_reaction(*args, **kwargs) - - property constant_pH: - """ - Sets the input pH for the constant pH ensemble method. - - """ - - def __set__(self, double pH): - """ - Sets the pH that the method assumes for the implicit pH bath. - - """ - - deref(self.constpHptr).m_constant_pH = pH - -cdef class WidomInsertion(ReactionAlgorithm): - """ - This class implements the Widom insertion method in the canonical ensemble - for homogeneous systems, where the excess chemical potential is not - depending on the location. - - """ - - cdef unique_ptr[CWidomInsertion] WidomInsertionPtr - - def _required_keys(self): - return ("kT", "seed") - - def _valid_keys(self): - return ("kT", "seed") - - def _valid_keys_add(self): - return ("reactant_types", "reactant_coefficients", "product_types", - "product_coefficients", "default_charges", - "check_for_electroneutrality") - - def _required_keys_add(self): - return ("reactant_types", "reactant_coefficients", - "product_types", "product_coefficients", "default_charges") - - def __init__(self, *args, **kwargs): - self._params = {"kT": 1} - for k in self._required_keys(): - if k not in kwargs: - raise ValueError( - f"At least the following keys have to be given as keyword " - f"arguments: {self._required_keys()}, got {kwargs}") - self._params[k] = kwargs[k] - self._params["exclusion_radius"] = 0.0 # not used by this method - self._params["gamma"] = 1.0 # not used by this method - - self.WidomInsertionPtr.reset(new CWidomInsertion(int(self._params["seed"]))) - self.RE = self.WidomInsertionPtr.get() - for k in kwargs: - if k in self._valid_keys(): - self._params[k] = kwargs[k] - else: - raise KeyError(f"{k} is not a valid key") - - self._set_params_in_es_core() - - def calculate_particle_insertion_potential_energy(self, reaction_id=0): - """ - Measures the potential energy when particles are inserted in the - system following the reaction provided ``reaction_id``. Please - define the insertion moves first by calling the method - :meth:`~ReactionAlgorithm.add_reaction` (with only product types - specified). - - Note that although this function does not provide directly - the chemical potential, it can be used to calculate it. - For an example of such an application please check - :file:`/samples/widom_insertion.py`. - """ - # make inverse widom scheme (deletion of particles) inaccessible. - # The deletion reactions are the odd reaction_ids - if(reaction_id < 0 or reaction_id > (deref(self.WidomInsertionPtr).reactions.size() + 1) / 2): - raise ValueError("This reaction is not present") - return deref(self.WidomInsertionPtr).calculate_particle_insertion_potential_energy( - deref(self.WidomInsertionPtr).reactions[int(2 * reaction_id)]) - - def calculate_excess_chemical_potential( - self, particle_insertion_potential_energy_samples, N_blocks=16): - """ - Given a set of samples of the particle insertion potential energy, - calculates the excess chemical potential and its statistical error. - - Parameters - ---------- - particle_insertion_potential_energy_samples : array_like of :obj:`float` - Samples of the particle insertion potential energy. - N_blocks : :obj:`int`, optional - Number of bins for binning analysis. - - Returns - ------- - mean : :obj:`float` - Mean excess chemical potential. - error : :obj:`float` - Standard error of the mean. - """ - - def do_block_analysis(samples, N_blocks=16): - """ - Performs a binning analysis of samples. - Divides the samples in ``N_blocks`` equispaced blocks - and returns the mean and its uncertainty - """ - size_block = int(len(samples) / N_blocks) - block_list = [] - for block in range(N_blocks): - block_list.append( - np.mean(samples[block * size_block:(block + 1) * size_block])) - - sample_mean = np.mean(block_list) - sample_std = np.std(block_list, ddof=1) - sample_uncertainty = sample_std / np.sqrt(N_blocks) - - return sample_mean, sample_uncertainty - - gamma_samples = np.exp(-1.0 * np.array( - particle_insertion_potential_energy_samples) / self._params["kT"]) - - gamma_mean, gamma_std = do_block_analysis( - samples=gamma_samples, N_blocks=N_blocks) - - mu_ex_mean = -1 * np.log(gamma_mean) * self._params["kT"] - - # full propagation of error - mu_ex_Delta = 0.5 * self._params["kT"] * abs(-np.log(gamma_mean + gamma_std) - - (-np.log(gamma_mean - gamma_std))) - - return mu_ex_mean, mu_ex_Delta diff --git a/src/python/espressomd/script_interface.pyx b/src/python/espressomd/script_interface.pyx index e482f2a7088..2d0a207240c 100644 --- a/src/python/espressomd/script_interface.pyx +++ b/src/python/espressomd/script_interface.pyx @@ -15,8 +15,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import numpy as np -from .utils import to_char_pointer, to_str, handle_errors -from .utils cimport Vector3d, make_array_locked +from .utils import to_char_pointer, to_str, handle_errors, array_locked +from .utils cimport Vector2d, Vector3d, Vector4d, make_array_locked cimport cpython.object from libcpp.memory cimport make_shared @@ -229,6 +229,8 @@ cdef variant_to_python_object(const Variant & value) except +: cdef vector[Variant] vec cdef unordered_map[int, Variant] vmap cdef shared_ptr[ObjectHandle] ptr + cdef Vector2d vec2d + cdef Vector4d vec4d if is_none(value): return None if is_type[bool](value): @@ -243,8 +245,14 @@ cdef variant_to_python_object(const Variant & value) except +: return get_value[vector[int]](value) if is_type[vector[double]](value): return get_value[vector[double]](value) + if is_type[Vector4d](value): + vec4d = get_value[Vector4d](value) + return array_locked([vec4d[0], vec4d[1], vec4d[2], vec4d[3]]) if is_type[Vector3d](value): return make_array_locked(get_value[Vector3d](value)) + if is_type[Vector2d](value): + vec2d = get_value[Vector2d](value) + return array_locked([vec2d[0], vec2d[1]]) if is_type[shared_ptr[ObjectHandle]](value): # Get the id and build a corresponding object ptr = get_value[shared_ptr[ObjectHandle]](value) diff --git a/src/script_interface/CMakeLists.txt b/src/script_interface/CMakeLists.txt index b47690f9c7b..6c657ae04ac 100644 --- a/src/script_interface/CMakeLists.txt +++ b/src/script_interface/CMakeLists.txt @@ -15,6 +15,7 @@ add_subdirectory(pair_criteria) add_subdirectory(mpiio) add_subdirectory(shapes) add_subdirectory(h5md) +add_subdirectory(reaction_methods) install(TARGETS ScriptInterface LIBRARY DESTINATION ${PYTHON_INSTDIR}/espressomd) diff --git a/src/script_interface/initialize.cpp b/src/script_interface/initialize.cpp index 35b90d333d0..b6627905e84 100644 --- a/src/script_interface/initialize.cpp +++ b/src/script_interface/initialize.cpp @@ -36,6 +36,7 @@ #include "lbboundaries/initialize.hpp" #include "mpiio/initialize.hpp" #include "observables/initialize.hpp" +#include "reaction_methods/initialize.hpp" #include "virtual_sites/initialize.hpp" namespace ScriptInterface { @@ -54,6 +55,7 @@ void initialize(Utils::Factory *f) { VirtualSites::initialize(f); MPIIO::initialize(f); CollisionDetection::initialize(f); + ReactionMethods::initialize(f); f->register_new("ComFixed"); f->register_new( diff --git a/src/script_interface/reaction_methods/CMakeLists.txt b/src/script_interface/reaction_methods/CMakeLists.txt new file mode 100644 index 00000000000..43c571ff9a2 --- /dev/null +++ b/src/script_interface/reaction_methods/CMakeLists.txt @@ -0,0 +1,2 @@ +target_sources(ScriptInterface + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/reaction_methods/ConstantpHEnsemble.hpp b/src/script_interface/reaction_methods/ConstantpHEnsemble.hpp new file mode 100644 index 00000000000..5016c8e7504 --- /dev/null +++ b/src/script_interface/reaction_methods/ConstantpHEnsemble.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2021 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 . + */ + +#ifndef SCRIPT_INTERFACE_REACTION_METHODS_CONSTANT_PH_HPP +#define SCRIPT_INTERFACE_REACTION_METHODS_CONSTANT_PH_HPP + +#include "ReactionAlgorithm.hpp" + +#include "script_interface/ScriptInterface.hpp" + +#include "core/reaction_methods/ConstantpHEnsemble.hpp" +#include "core/reaction_methods/ReactionAlgorithm.hpp" + +#include + +namespace ScriptInterface { +namespace ReactionMethods { + +class ConstantpHEnsemble : public ReactionAlgorithm { +public: + std::shared_ptr<::ReactionMethods::ReactionAlgorithm> RE() override { + return m_re; + } + + ConstantpHEnsemble() { + add_parameters({ + {"constant_pH", + [this](Variant const &v) { + m_re->m_constant_pH = get_value(v); + }, + [this]() { return m_re->m_constant_pH; }}, + }); + } + + void do_construct(VariantMap const ¶ms) override { + m_re = std::make_shared<::ReactionMethods::ConstantpHEnsemble>( + get_value(params, "seed"), get_value(params, "kT"), + get_value(params, "exclusion_radius"), + get_value(params, "constant_pH")); + } + +private: + std::shared_ptr<::ReactionMethods::ConstantpHEnsemble> m_re; +}; +} /* namespace ReactionMethods */ +} /* namespace ScriptInterface */ + +#endif \ No newline at end of file diff --git a/src/script_interface/reaction_methods/ReactionAlgorithm.hpp b/src/script_interface/reaction_methods/ReactionAlgorithm.hpp new file mode 100644 index 00000000000..f6028b70e09 --- /dev/null +++ b/src/script_interface/reaction_methods/ReactionAlgorithm.hpp @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2021 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 . + */ + +#ifndef SCRIPT_INTERFACE_REACTION_METHODS_REACTION_ALGORITHM_HPP +#define SCRIPT_INTERFACE_REACTION_METHODS_REACTION_ALGORITHM_HPP + +#include "SingleReaction.hpp" + +#include "script_interface/ScriptInterface.hpp" + +#include "core/reaction_methods/ReactionAlgorithm.hpp" + +#include +#include +#include +#include + +namespace ScriptInterface { +namespace ReactionMethods { + +class ReactionAlgorithm : public AutoParameters { +protected: + /** Keep track of the script interface pointer of each reaction. */ + std::vector> m_reactions; + /** + * Check reaction id is within the reaction container bounds. + * Since each reaction has a corresponding backward reaction, + * the total number of reactions is doubled. Return the + * corresponding index for @ref ReactionAlgorithm::m_reactions. + */ + int get_reaction_index(int reaction_id) const { + auto const index = 2 * reaction_id; + if (index < 0 or index >= static_cast(m_reactions.size())) { + throw std::out_of_range("This reaction is not present"); + } + return index; + } + +public: + virtual std::shared_ptr<::ReactionMethods::ReactionAlgorithm> RE() = 0; + + ReactionAlgorithm() { + add_parameters({ + {"reactions", AutoParameter::read_only, + [this]() { + std::vector out; + for (auto const &e : m_reactions) { + out.emplace_back(e); + } + return out; + }}, + {"kT", AutoParameter::read_only, [this]() { return RE()->get_kT(); }}, + {"exclusion_radius", AutoParameter::read_only, + [this]() { return RE()->get_exclusion_radius(); }}, + }); + } + + Variant do_call_method(std::string const &name, + VariantMap const ¶meters) override { + if (name == "remove_constraint") { + RE()->remove_constraint(); + } else if (name == "set_cylindrical_constraint_in_z_direction") { + RE()->set_cyl_constraint(get_value(parameters, "center_x"), + get_value(parameters, "center_y"), + get_value(parameters, "radius")); + } else if (name == "set_wall_constraints_in_z_direction") { + RE()->set_slab_constraint(get_value(parameters, "slab_start_z"), + get_value(parameters, "slab_end_z")); + } else if (name == "get_wall_constraints_in_z_direction") { + return RE()->get_slab_constraint_parameters(); + } else if (name == "set_volume") { + RE()->set_volume(get_value(parameters, "volume")); + } else if (name == "get_volume") { + return RE()->get_volume(); + } else if (name == "get_acceptance_rate_reaction") { + auto const index = get_value(parameters, "reaction_id"); + if (index < 0 or index >= static_cast(m_reactions.size())) { + throw std::out_of_range("This reaction is not present"); + } + return m_reactions[index]->get_reaction()->get_acceptance_rate(); + } else if (name == "set_non_interacting_type") { + RE()->non_interacting_type = get_value(parameters, "type"); + } else if (name == "get_non_interacting_type") { + return RE()->non_interacting_type; + } else if (name == "reaction") { + RE()->do_reaction(get_value_or(parameters, "reaction_steps", 1)); + } else if (name == "displacement_mc_move_for_particles_of_type") { + RE()->do_global_mc_move_for_particles_of_type( + get_value(parameters, "type_mc"), + get_value_or(parameters, "particle_number_to_be_changed", 1)); + } else if (name == "check_reaction_method") { + RE()->check_reaction_method(); + } else if (name == "delete_particle") { + RE()->delete_particle(get_value(parameters, "p_id")); + } else if (name == "delete_reaction") { + auto const reaction_id = get_value(parameters, "reaction_id"); + auto const index = get_reaction_index(reaction_id); + // delete forward and backward reactions + delete_reaction(index + 1); + delete_reaction(index + 0); + } else if (name == "add_reaction") { + auto const reaction = + get_value>(parameters, "reaction"); + m_reactions.push_back(reaction); + RE()->add_reaction(reaction->get_reaction()); + } else if (name == "change_reaction_constant") { + auto const gamma = get_value(parameters, "gamma"); + auto const reaction_id = get_value(parameters, "reaction_id"); + auto const index = get_reaction_index(reaction_id); + m_reactions[index + 0]->get_reaction()->gamma = gamma; + m_reactions[index + 1]->get_reaction()->gamma = 1. / gamma; + } else if (name == "set_charge_of_type") { + auto const type = get_value(parameters, "type"); + auto const charge = get_value(parameters, "charge"); + RE()->charges_of_types[type] = charge; + } else { + throw std::runtime_error(("unknown method '" + name + "()'").c_str()); + } + return {}; + }; + +private: + void delete_reaction(int reaction_id) { + m_reactions.erase(m_reactions.begin() + reaction_id); + RE()->delete_reaction(reaction_id); + } +}; +} /* namespace ReactionMethods */ +} /* namespace ScriptInterface */ + +#endif \ No newline at end of file diff --git a/src/script_interface/reaction_methods/ReactionEnsemble.hpp b/src/script_interface/reaction_methods/ReactionEnsemble.hpp new file mode 100644 index 00000000000..cdd98e1aaa6 --- /dev/null +++ b/src/script_interface/reaction_methods/ReactionEnsemble.hpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2021 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 . + */ + +#ifndef SCRIPT_INTERFACE_REACTION_METHODS_REACTION_ENSEMBLE_HPP +#define SCRIPT_INTERFACE_REACTION_METHODS_REACTION_ENSEMBLE_HPP + +#include "ReactionAlgorithm.hpp" + +#include "script_interface/ScriptInterface.hpp" + +#include "core/reaction_methods/ReactionAlgorithm.hpp" +#include "core/reaction_methods/ReactionEnsemble.hpp" + +#include + +namespace ScriptInterface { +namespace ReactionMethods { + +class ReactionEnsemble : public ReactionAlgorithm { +public: + std::shared_ptr<::ReactionMethods::ReactionAlgorithm> RE() override { + return m_re; + } + + void do_construct(VariantMap const ¶ms) override { + m_re = std::make_shared<::ReactionMethods::ReactionEnsemble>( + get_value(params, "seed"), get_value(params, "kT"), + get_value(params, "exclusion_radius")); + } + +private: + std::shared_ptr<::ReactionMethods::ReactionEnsemble> m_re; +}; +} /* namespace ReactionMethods */ +} /* namespace ScriptInterface */ + +#endif \ No newline at end of file diff --git a/src/script_interface/reaction_methods/SingleReaction.hpp b/src/script_interface/reaction_methods/SingleReaction.hpp new file mode 100644 index 00000000000..dfd0c86b19f --- /dev/null +++ b/src/script_interface/reaction_methods/SingleReaction.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2021 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 . + */ + +#ifndef SCRIPT_INTERFACE_REACTION_METHODS_SINGLE_REACTION_HPP +#define SCRIPT_INTERFACE_REACTION_METHODS_SINGLE_REACTION_HPP + +#include "core/reaction_methods/SingleReaction.hpp" +#include "script_interface/ScriptInterface.hpp" +#include +#include + +namespace ScriptInterface { +namespace ReactionMethods { + +class SingleReaction : public AutoParameters { +public: + SingleReaction() { + add_parameters({ + {"gamma", AutoParameter::read_only, [this]() { return m_sr->gamma; }}, + {"reactant_types", AutoParameter::read_only, + [this]() { return m_sr->reactant_types; }}, + {"reactant_coefficients", AutoParameter::read_only, + [this]() { return m_sr->reactant_coefficients; }}, + {"product_types", AutoParameter::read_only, + [this]() { return m_sr->product_types; }}, + {"product_coefficients", AutoParameter::read_only, + [this]() { return m_sr->product_coefficients; }}, + }); + } + + void do_construct(VariantMap const ¶ms) override { + m_sr = std::make_shared<::ReactionMethods::SingleReaction>( + get_value(params, "gamma"), + get_value>(params, "reactant_types"), + get_value>(params, "reactant_coefficients"), + get_value>(params, "product_types"), + get_value>(params, "product_coefficients")); + } + + std::shared_ptr<::ReactionMethods::SingleReaction> get_reaction() { + return m_sr; + } + +private: + std::shared_ptr<::ReactionMethods::SingleReaction> m_sr; +}; +} /* namespace ReactionMethods */ +} /* namespace ScriptInterface */ + +#endif \ No newline at end of file diff --git a/src/script_interface/reaction_methods/WidomInsertion.hpp b/src/script_interface/reaction_methods/WidomInsertion.hpp new file mode 100644 index 00000000000..f60045507df --- /dev/null +++ b/src/script_interface/reaction_methods/WidomInsertion.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2021 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 . + */ + +#ifndef SCRIPT_INTERFACE_REACTION_METHODS_WIDOM_INSERTION_HPP +#define SCRIPT_INTERFACE_REACTION_METHODS_WIDOM_INSERTION_HPP + +#include "ReactionAlgorithm.hpp" + +#include "script_interface/ScriptInterface.hpp" + +#include "core/reaction_methods/ReactionAlgorithm.hpp" +#include "core/reaction_methods/WidomInsertion.hpp" + +#include +#include +#include + +namespace ScriptInterface { +namespace ReactionMethods { + +class WidomInsertion : public ReactionAlgorithm { +public: + std::shared_ptr<::ReactionMethods::ReactionAlgorithm> RE() override { + return m_re; + } + + void do_construct(VariantMap const ¶ms) override { + m_re = std::make_shared<::ReactionMethods::WidomInsertion>( + get_value(params, "seed"), get_value(params, "kT"), 0.); + } + + Variant do_call_method(std::string const &name, + VariantMap const ¶meters) override { + if (name == "calculate_particle_insertion_potential_energy") { + auto const reaction_id = get_value(parameters, "reaction_id"); + auto const index = get_reaction_index(reaction_id); + auto &reaction = *m_reactions[index]->get_reaction(); + return m_re->calculate_particle_insertion_potential_energy(reaction); + } + return ReactionAlgorithm::do_call_method(name, parameters); + } + +private: + std::shared_ptr<::ReactionMethods::WidomInsertion> m_re; +}; +} /* namespace ReactionMethods */ +} /* namespace ScriptInterface */ + +#endif \ No newline at end of file diff --git a/src/script_interface/reaction_methods/initialize.cpp b/src/script_interface/reaction_methods/initialize.cpp new file mode 100644 index 00000000000..eb01924dc19 --- /dev/null +++ b/src/script_interface/reaction_methods/initialize.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 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 "initialize.hpp" + +#include "SingleReaction.hpp" + +#include "ConstantpHEnsemble.hpp" +#include "ReactionEnsemble.hpp" +#include "WidomInsertion.hpp" + +#include "script_interface/ScriptInterface.hpp" + +namespace ScriptInterface { +namespace ReactionMethods { +void initialize(Utils::Factory *om) { + om->register_new("ReactionMethods::SingleReaction"); + om->register_new("ReactionMethods::WidomInsertion"); + om->register_new("ReactionMethods::ReactionEnsemble"); + om->register_new("ReactionMethods::ConstantpHEnsemble"); +} +} // namespace ReactionMethods +} // namespace ScriptInterface diff --git a/src/script_interface/reaction_methods/initialize.hpp b/src/script_interface/reaction_methods/initialize.hpp new file mode 100644 index 00000000000..5890b3411c3 --- /dev/null +++ b/src/script_interface/reaction_methods/initialize.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2021 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 . + */ + +#ifndef SCRIPT_INTERFACE_REACTION_METHODS_INITIALIZE_HPP +#define SCRIPT_INTERFACE_REACTION_METHODS_INITIALIZE_HPP + +#include +#include + +namespace ScriptInterface { +namespace ReactionMethods { +void initialize(Utils::Factory *om); +} // namespace ReactionMethods +} // namespace ScriptInterface + +#endif diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index aa80da33578..d3fc751bc83 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -124,6 +124,7 @@ python_test(FILE rotational_inertia.py MAX_NUM_PROC 4) python_test(FILE rotational-diffusion-aniso.py MAX_NUM_PROC 1 LABELS long) python_test(FILE rotational_dynamics.py MAX_NUM_PROC 1) python_test(FILE script_interface.py MAX_NUM_PROC 4) +python_test(FILE reaction_methods.py MAX_NUM_PROC 1) python_test(FILE reaction_ensemble.py MAX_NUM_PROC 4) python_test(FILE widom_insertion.py MAX_NUM_PROC 1) python_test(FILE constant_pH.py MAX_NUM_PROC 1) diff --git a/testsuite/python/constant_pH.py b/testsuite/python/constant_pH.py index b0e1d0f14d4..37d25fe5320 100644 --- a/testsuite/python/constant_pH.py +++ b/testsuite/python/constant_pH.py @@ -54,25 +54,25 @@ def test_ideal_alpha(self): RE = espressomd.reaction_ensemble.ConstantpHEnsemble( kT=1.0, exclusion_radius=1, - seed=44) + seed=44, + constant_pH=pH) RE.add_reaction( gamma=10**(-pKa), reactant_types=[types["HA"]], product_types=[types["A-"], types["H+"]], default_charges=charges_dict) - RE.constant_pH = pH # Set the hidden particle type to the lowest possible number to speed # up the simulation - RE.set_non_interacting_type(max(types.values()) + 1) + RE.set_non_interacting_type(type=max(types.values()) + 1) # equilibration - RE.reaction(800) + RE.reaction(reaction_steps=800) # sampling alphas = [] for _ in range(80): - RE.reaction(15) + RE.reaction(reaction_steps=15) num_H = system.number_of_particles(type=types["H+"]) num_HA = system.number_of_particles(type=types["HA"]) num_A = system.number_of_particles(type=types["A-"]) diff --git a/testsuite/python/constant_pH_stats.py b/testsuite/python/constant_pH_stats.py index b2671a8e3cd..f7d05385c45 100644 --- a/testsuite/python/constant_pH_stats.py +++ b/testsuite/python/constant_pH_stats.py @@ -52,7 +52,7 @@ class ReactionEnsembleTest(ut.TestCase): system.cell_system.skin = 0.4 system.time_step = 0.01 RE = espressomd.reaction_ensemble.ConstantpHEnsemble( - kT=1.0, exclusion_radius=1, seed=44) + kT=1.0, exclusion_radius=1, seed=44, constant_pH=pH) @classmethod def setUpClass(cls): @@ -65,7 +65,6 @@ def setUpClass(cls): reactant_types=[cls.types["HA"]], product_types=[cls.types["A-"], cls.types["H+"]], default_charges=cls.charges_dict) - cls.RE.constant_pH = cls.pH @classmethod def ideal_alpha(cls, pH): @@ -79,18 +78,18 @@ def test_ideal_titration_curve(self): # Set the hidden particle type to the lowest possible number to speed # up the simulation - RE.set_non_interacting_type(max(types.values()) + 1) + RE.set_non_interacting_type(type=max(types.values()) + 1) # chemical warmup - get close to chemical equilibrium before we start # sampling - RE.reaction(40 * N0) + RE.reaction(reaction_steps=40 * N0) average_NH = 0.0 average_NHA = 0.0 average_NA = 0.0 num_samples = 1000 for _ in range(num_samples): - RE.reaction(10) + RE.reaction(reaction_steps=10) average_NH += system.number_of_particles(type=types["H+"]) average_NHA += system.number_of_particles(type=types["HA"]) average_NA += system.number_of_particles(type=types["A-"]) diff --git a/testsuite/python/reaction_ensemble.py b/testsuite/python/reaction_ensemble.py index bc3c23ea378..96f160fd9e9 100644 --- a/testsuite/python/reaction_ensemble.py +++ b/testsuite/python/reaction_ensemble.py @@ -83,8 +83,7 @@ def setUpClass(cls): reactant_coefficients=cls.reactant_coefficients, product_types=cls.product_types, product_coefficients=cls.product_coefficients, - default_charges=cls.charge_dict, - check_for_electroneutrality=True) + default_charges=cls.charge_dict) def test_ideal_titration_curve(self): N0 = ReactionEnsembleTest.N0 @@ -97,18 +96,18 @@ def test_ideal_titration_curve(self): # Set the hidden particle type to the lowest possible number to speed # up the simulation - RE.set_non_interacting_type(max(types.values()) + 1) + RE.set_non_interacting_type(type=max(types.values()) + 1) # chemical warmup - get close to chemical equilibrium before we start # sampling - RE.reaction(5 * N0) + RE.reaction(reaction_steps=5 * N0) average_NH = 0.0 average_NHA = 0.0 average_NA = 0.0 num_samples = 300 for _ in range(num_samples): - RE.reaction(10) + RE.reaction(reaction_steps=10) average_NH += system.number_of_particles(type=types["H+"]) average_NHA += system.number_of_particles(type=types["HA"]) average_NA += system.number_of_particles(type=types["A-"]) @@ -133,84 +132,11 @@ def test_ideal_titration_curve(self): + f" average alpha: {average_alpha:.3f}" + f" target alpha: {target_alpha:.3f}" ) - - def test_reaction_system(self): - RE_status = ReactionEnsembleTest.RE.get_status() - forward_reaction = RE_status["reactions"][0] - for i in range(len(forward_reaction["reactant_types"])): - self.assertEqual( - ReactionEnsembleTest.reactant_types[i], - forward_reaction["reactant_types"][i], - msg="reactant type not set correctly.") - for i in range(len(forward_reaction["reactant_coefficients"])): - self.assertEqual( - ReactionEnsembleTest.reactant_coefficients[i], - forward_reaction["reactant_coefficients"][i], - msg="reactant coefficients not set correctly.") - for i in range(len(forward_reaction["product_types"])): - self.assertEqual( - ReactionEnsembleTest.product_types[i], - forward_reaction["product_types"][i], - msg="product type not set correctly.") - for i in range(len(forward_reaction["product_coefficients"])): - self.assertEqual( - ReactionEnsembleTest.product_coefficients[i], - forward_reaction["product_coefficients"][i], - msg="product coefficients not set correctly.") - - self.assertAlmostEqual( - ReactionEnsembleTest.temperature, - RE_status["kT"], - places=9, - msg="reaction ensemble kT not set correctly.") - self.assertAlmostEqual( - ReactionEnsembleTest.exclusion_radius, - RE_status["exclusion_radius"], - places=9, - msg="reaction ensemble exclusion radius not set correctly.") - - self.assertAlmostEqual( - ReactionEnsembleTest.volume, - ReactionEnsembleTest.RE.get_volume(), - places=9, - msg="reaction ensemble volume not set correctly.") - - def test_change_reaction_constant(self): - RE = ReactionEnsembleTest.RE - new_reaction_constant = 634.0 - RE.change_reaction_constant(0, new_reaction_constant) - RE_status = RE.get_status() - forward_reaction = RE_status["reactions"][0] - backward_reaction = RE_status["reactions"][1] - self.assertEqual( - new_reaction_constant, - forward_reaction["gamma"], - msg="new reaction constant was not set correctly.") - self.assertEqual( - 1.0 / new_reaction_constant, - backward_reaction["gamma"], - msg="new reaction constant was not set correctly.") - RE.change_reaction_constant(0, ReactionEnsembleTest.gamma) - - def test_delete_reaction(self): - RE = ReactionEnsembleTest.RE - RE.add_reaction( - gamma=1, - reactant_types=[5], - reactant_coefficients=[1], - product_types=[2, 3, 4], - product_coefficients=[1, 4, 3], - default_charges={5: 0, 2: 0, 3: 0, 4: 0}, - check_for_electroneutrality=True) - nr_reactions_after_addition = len(RE.get_status()["reactions"]) - RE.delete_reaction(1) - nr_reactions_after_deletion = len(RE.get_status()["reactions"]) - self.assertEqual( - 2, - nr_reactions_after_addition - nr_reactions_after_deletion, - msg="the difference in single reactions does not match,\ - deleting a full reaction (back and forward direction)\ - should result in deleting two single reactions.") + # for this setup, the acceptance rate is about 85% + rate0 = RE.get_acceptance_rate_reaction(reaction_id=0) + rate1 = RE.get_acceptance_rate_reaction(reaction_id=1) + self.assertAlmostEqual(rate0, 0.85, delta=0.05) + self.assertAlmostEqual(rate1, 0.85, delta=0.05) if __name__ == "__main__": diff --git a/testsuite/python/reaction_methods.py b/testsuite/python/reaction_methods.py new file mode 100644 index 00000000000..0a29f2eb4d5 --- /dev/null +++ b/testsuite/python/reaction_methods.py @@ -0,0 +1,273 @@ +# +# Copyright (C) 2013-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 . +# + +import espressomd +import espressomd.reaction_ensemble + +import unittest as ut + + +class ReactionMethods(ut.TestCase): + + """Test the reaction methods interface.""" + + system = espressomd.System(box_l=[10., 10., 10.]) + system.cell_system.skin = 0.4 + + def tearDown(self): + self.system.part.clear() + + def check_interface(self, method, kT, exclusion_radius, gamma): + def check_reaction_parameters(reactions, parameters): + for reaction, params in zip(reactions, parameters): + for key in reaction.required_keys(): + self.assertEqual(getattr(reaction, key), params[key]) + + reaction_forward = { + 'gamma': gamma, + 'reactant_types': [5], + 'reactant_coefficients': [1], + 'product_types': [2, 3], + 'product_coefficients': [1, 1], + 'default_charges': {5: 0, 2: 0, 3: 0}, + } + reaction_backward = { + 'gamma': 1. / gamma, + 'reactant_types': reaction_forward['product_types'], + 'reactant_coefficients': reaction_forward['product_coefficients'], + 'product_types': reaction_forward['reactant_types'], + 'product_coefficients': reaction_forward['reactant_coefficients'], + 'default_charges': reaction_forward['default_charges'], + } + + if isinstance(method, espressomd.reaction_ensemble.ConstantpHEnsemble): + method.add_reaction(gamma=reaction_forward['gamma'], + reactant_types=reaction_forward['reactant_types'], + product_types=reaction_forward['product_types'], + default_charges=reaction_forward['default_charges']) + else: + method.add_reaction(**reaction_forward) + reaction_parameters = (reaction_forward, reaction_backward) + + # check getters and setters + self.assertAlmostEqual(method.kT, kT, delta=1e-10) + self.assertAlmostEqual( + method.exclusion_radius, + exclusion_radius, + delta=1e-10) + self.assertAlmostEqual( + method.get_volume(), + self.system.volume(), + delta=1e-10) + method.set_volume(volume=1.) + self.assertAlmostEqual(method.get_volume(), 1., delta=1e-10) + self.assertEqual(method.get_non_interacting_type(), 100) + method.set_non_interacting_type(type=9) + self.assertEqual(method.get_non_interacting_type(), 9) + if isinstance(method, espressomd.reaction_ensemble.ConstantpHEnsemble): + self.assertAlmostEqual(method.constant_pH, 10., delta=1e-10) + method.constant_pH = 8. + self.assertAlmostEqual(method.constant_pH, 8., delta=1e-10) + + # check constraints + method.set_wall_constraints_in_z_direction( + slab_start_z=0.1, slab_end_z=0.9) + offsets = method.get_wall_constraints_in_z_direction() + self.assertAlmostEqual(offsets[0], 0.1, delta=1e-10) + self.assertAlmostEqual(offsets[1], 0.9, delta=1e-10) + method.remove_constraint() + + # check status + status = method.get_status() + self.assertEqual(status['kT'], kT) + self.assertEqual(status['exclusion_radius'], exclusion_radius) + self.assertEqual(len(status['reactions']), 2) + for reaction_flat, params in zip( + status['reactions'], reaction_parameters): + for key in reaction_flat: + if key == 'gamma': + self.assertAlmostEqual( + reaction_flat[key], params[key], delta=1e-10) + else: + self.assertEqual(reaction_flat[key], params[key]) + + # check reactions + reactions = method.reactions + self.assertEqual(len(reactions), 2) + check_reaction_parameters(method.reactions, reaction_parameters) + + # check reactions after parameter change + new_gamma = 634. + reaction_forward['gamma'] = new_gamma + reaction_backward['gamma'] = 1. / new_gamma + method.change_reaction_constant(reaction_id=0, gamma=new_gamma) + check_reaction_parameters(method.reactions, reaction_parameters) + status = method.get_status() + self.assertAlmostEqual( + status['reactions'][0]['gamma'], + reaction_forward['gamma'], + delta=1e-10) + self.assertAlmostEqual( + status['reactions'][1]['gamma'], + reaction_backward['gamma'], + delta=1e-10) + + # check particle deletion + p1, _, p3 = self.system.part.add( + pos=3 * [(0., 0., 0.)], type=[5, 2, 3]) + if isinstance(method, espressomd.reaction_ensemble.WidomInsertion): + potential_energy = method.calculate_particle_insertion_potential_energy( + reaction_id=0) + self.assertEqual(potential_energy, 0.) + method.delete_particle(p_id=p3.id) + self.assertEqual(len(self.system.part), 2) + method.delete_particle(p_id=p1.id) + self.assertEqual(len(self.system.part), 1) + self.system.part.clear() + + # check reaction deletion + method.delete_reaction(reaction_id=0) + self.assertEqual(len(method.reactions), 0) + + def test_interface(self): + # reaction ensemble + method = espressomd.reaction_ensemble.ReactionEnsemble( + kT=1.5, exclusion_radius=0.8, seed=12) + self.check_interface(method, kT=1.5, exclusion_radius=0.8, gamma=1.2) + + # constant pH ensemble + method = espressomd.reaction_ensemble.ConstantpHEnsemble( + kT=1.5, exclusion_radius=0.8, seed=12, constant_pH=10) + self.check_interface(method, kT=1.5, exclusion_radius=0.8, gamma=1.2) + + # Widom insertion + method = espressomd.reaction_ensemble.WidomInsertion(kT=1.6, seed=12) + self.check_interface(method, kT=1.6, exclusion_radius=0., gamma=1.) + + def test_exceptions(self): + single_reaction_params = { + 'gamma': 1., + 'reactant_types': [4], + 'reactant_coefficients': [1], + 'product_types': [2, 3], + 'product_coefficients': [1, 4], + } + reaction_params = { + 'default_charges': {2: 0, 3: 0, 4: 0}, + **single_reaction_params + } + widom = espressomd.reaction_ensemble.WidomInsertion(kT=1., seed=12) + method = espressomd.reaction_ensemble.ReactionEnsemble( + kT=1.5, exclusion_radius=0.8, seed=12) + method.add_reaction(**reaction_params) + widom.add_reaction(**reaction_params) + + # check invalid reactions + err_msg = 'number of types and coefficients have to match' + with self.assertRaisesRegex(ValueError, f'reactants: {err_msg}'): + method.add_reaction(**{**reaction_params, 'reactant_types': []}) + with self.assertRaisesRegex(ValueError, f'products: {err_msg}'): + method.add_reaction(**{**reaction_params, 'product_types': []}) + + # check charge conservation + err_msg = 'Reaction system is not charge neutral' + with self.assertRaisesRegex(ValueError, err_msg): + method.add_reaction(default_charges={2: 8, 3: 0, 4: -50}, + **single_reaction_params) + with self.assertRaisesRegex(ValueError, err_msg): + method.add_reaction(default_charges={2: 1, 3: 0, 4: 1 + 1e-10}, + **single_reaction_params) + + # check invalid reaction id exceptions + # (note: reactions id = 2 * reactions index) + self.assertEqual(len(method.reactions), 2) + for i in [-2, -1, 1, 2, 3]: + with self.assertRaisesRegex(IndexError, 'This reaction is not present'): + method.delete_reaction(reaction_id=i) + with self.assertRaisesRegex(IndexError, 'This reaction is not present'): + method.get_acceptance_rate_reaction(reaction_id=2 * i) + + # check constraint exceptions + set_cyl_constraint = method.set_cylindrical_constraint_in_z_direction + set_slab_constraint = method.set_wall_constraints_in_z_direction + get_slab_constraint = method.get_wall_constraints_in_z_direction + err_msg = "no slab constraint is currently active" + with self.assertRaisesRegex(RuntimeError, err_msg): + get_slab_constraint() + set_slab_constraint(slab_start_z=0.1, slab_end_z=0.9) + method.remove_constraint() + with self.assertRaisesRegex(RuntimeError, err_msg): + get_slab_constraint() + + # check invalid constraints + with self.assertRaisesRegex(ValueError, "center_x is outside the box"): + set_cyl_constraint(center_x=100., center_y=1., radius=1.) + with self.assertRaisesRegex(ValueError, "center_x is outside the box"): + set_cyl_constraint(center_x=-10., center_y=1., radius=1.) + with self.assertRaisesRegex(ValueError, "center_y is outside the box"): + set_cyl_constraint(center_y=100., center_x=1., radius=1.) + with self.assertRaisesRegex(ValueError, "center_y is outside the box"): + set_cyl_constraint(center_y=-10., center_x=1., radius=1.) + with self.assertRaisesRegex(ValueError, "radius is invalid"): + set_cyl_constraint(center_x=1., center_y=1., radius=-1.) + with self.assertRaisesRegex(ValueError, "slab_start_z is outside the box"): + set_slab_constraint(slab_start_z=100., slab_end_z=1.) + with self.assertRaisesRegex(ValueError, "slab_start_z is outside the box"): + set_slab_constraint(slab_start_z=-10., slab_end_z=1.) + with self.assertRaisesRegex(ValueError, "slab_end_z is outside the box"): + set_slab_constraint(slab_end_z=100., slab_start_z=1.) + with self.assertRaisesRegex(ValueError, "slab_end_z is outside the box"): + set_slab_constraint(slab_end_z=-10., slab_start_z=1.) + with self.assertRaisesRegex(ValueError, "slab_end_z must be >= slab_start_z"): + set_slab_constraint(slab_start_z=10., slab_end_z=1.) + + # check exceptions for missing particles + with self.assertRaisesRegex(RuntimeError, "Particle id is greater than the max seen particle id"): + method.delete_particle(p_id=0) + with self.assertRaisesRegex(RuntimeError, "Trying to remove some non-existing particles from the system via the inverse Widom scheme"): + widom.calculate_particle_insertion_potential_energy(reaction_id=0) + + # check other exceptions + with self.assertRaisesRegex(ValueError, "Invalid value for 'volume'"): + method.set_volume(volume=-10.) + with self.assertRaisesRegex(RuntimeError, r"unknown method 'unknown\(\)'"): + method.call_method('unknown', x=1) + err_msg = r"Only the following keys can be given as keyword arguments: \[.+\], got \[.+\] \(unknown \['x'\]\)" + with self.assertRaisesRegex(ValueError, err_msg): + espressomd.reaction_ensemble.SingleReaction( + x=1, **single_reaction_params) + with self.assertRaisesRegex(ValueError, err_msg): + espressomd.reaction_ensemble.ReactionEnsemble( + kT=1., exclusion_radius=1., seed=12, x=1) + with self.assertRaisesRegex(ValueError, err_msg): + espressomd.reaction_ensemble.ConstantpHEnsemble( + kT=1., exclusion_radius=1., seed=12, x=1, constant_pH=2) + with self.assertRaisesRegex(ValueError, err_msg): + espressomd.reaction_ensemble.WidomInsertion( + kT=1., seed=12, x=1) + with self.assertRaisesRegex(ValueError, "Invalid value for 'kT'"): + espressomd.reaction_ensemble.ReactionEnsemble( + kT=-1., exclusion_radius=1., seed=12) + with self.assertRaisesRegex(ValueError, "Invalid value for 'exclusion_radius'"): + espressomd.reaction_ensemble.ReactionEnsemble( + kT=1., exclusion_radius=-1., seed=12) + + +if __name__ == "__main__": + ut.main() diff --git a/testsuite/python/widom_insertion.py b/testsuite/python/widom_insertion.py index b2391d8558c..09fab8ee348 100644 --- a/testsuite/python/widom_insertion.py +++ b/testsuite/python/widom_insertion.py @@ -70,14 +70,13 @@ class WidomInsertionTest(ut.TestCase): system.cell_system.set_n_square() np.random.seed(69) # make reaction code fully deterministic system.cell_system.skin = 0.4 - volume = system.volume() Widom = espressomd.reaction_ensemble.WidomInsertion( kT=TEMPERATURE, seed=1) # Set the hidden particle type to the lowest possible number to speed # up the simulation - Widom.set_non_interacting_type(1) + Widom.set_non_interacting_type(type=1) def setUp(self): self.system.part.add(pos=0.5 * self.system.box_l, type=self.TYPE_HA) @@ -101,7 +100,7 @@ def test_widom_insertion(self): for _ in range(num_samples): # 0 for insertion reaction particle_insertion_potential_energy = self.Widom.calculate_particle_insertion_potential_energy( - 0) + reaction_id=0) particle_insertion_potential_energy_samples.append( particle_insertion_potential_energy) From ab619bde5cf747a394d4d0a9ef80e76c94cf30d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 15 Feb 2022 21:11:30 +0100 Subject: [PATCH 44/99] docs: Document reaction attributes constant_pH and gamma --- doc/sphinx/advanced_methods.rst | 9 +++++---- src/python/espressomd/reaction_ensemble.py | 9 +++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/doc/sphinx/advanced_methods.rst b/doc/sphinx/advanced_methods.rst index 1683388b345..2f1525fd5df 100644 --- a/doc/sphinx/advanced_methods.rst +++ b/doc/sphinx/advanced_methods.rst @@ -1707,7 +1707,7 @@ In the *forward* reaction, the appropriate number of reactants (given by :math:`\nu_i`) is removed from the system, and the concomitant number of products is inserted into the system. In the *backward* reaction, reactants and products exchange their roles. The acceptance probability -:math:`P^{\xi}` for move from state :math:`o` to :math:`n` reaction +:math:`P^{\xi}` for a move from state :math:`o` to :math:`n` in the reaction ensemble is given by the criterion :cite:`smith94c` .. math:: @@ -1718,10 +1718,11 @@ ensemble is given by the criterion :cite:`smith94c` where :math:`\Delta E=E_\mathrm{new}-E_\mathrm{old}` is the change in potential energy, :math:`V` is the simulation box volume, -and :math:`\beta=1/k_\mathrm{B}T`. -The extent of reaction, :math:`\xi=1` for the forward, and +:math:`\beta=1/k_\mathrm{B}T` is the Boltzmann factor, and +:math:`\xi` is the extent of reaction, with :math:`\xi=1` for the forward and :math:`\xi=-1` for the backward direction. -The parameter :math:`\Gamma` proportional to the reaction constant. It is defined as + +:math:`\Gamma` is proportional to the reaction constant. It is defined as .. math:: diff --git a/src/python/espressomd/reaction_ensemble.py b/src/python/espressomd/reaction_ensemble.py index 289aeefcc45..1f694b905b5 100644 --- a/src/python/espressomd/reaction_ensemble.py +++ b/src/python/espressomd/reaction_ensemble.py @@ -298,8 +298,8 @@ def add_reaction(self, **kwargs): Parameters ---------- gamma : :obj:`float` - Equilibrium constant of the reaction in simulation units, :math:`\\gamma` (see the User - guide, section 16.7.1. for the definition and further details). + Equilibrium constant :math:`\\Gamma` of the reaction in simulation + units (see section :ref:`Reaction Ensemble` for its definition). reactant_types : list of :obj:`int` List of particle types of reactants in the reaction. reactant_coefficients : list of :obj:`int` @@ -398,6 +398,11 @@ class ConstantpHEnsemble(ReactionAlgorithm): are always assumed to be at index 0 of the lists passed to arguments ``reactant_types`` and ``product_types``. + Attributes + ---------- + constant_pH : :obj:`float` + Constant pH value. + """ _so_name = "ReactionMethods::ConstantpHEnsemble" _so_creation_policy = "LOCAL" From 29cf77d9c4c24513230c7b45f3794e25f0256179 Mon Sep 17 00:00:00 2001 From: Christoph Lohrmann Date: Fri, 4 Feb 2022 13:25:00 +0100 Subject: [PATCH 45/99] introduce getters for particle properties --- src/core/PartCfg.cpp | 4 +- src/core/Particle.hpp | 168 ++++++++++++++++-- src/core/cells.cpp | 8 +- src/core/cells.hpp | 4 +- src/core/cluster_analysis/Cluster.cpp | 16 +- src/core/collision.cpp | 86 ++++----- src/core/constraints/Constraints.hpp | 4 +- src/core/constraints/ShapeBasedConstraint.cpp | 18 +- src/core/cuda_interface.cpp | 26 +-- src/core/energy_inline.hpp | 28 +-- src/core/forces_inline.hpp | 52 +++--- src/core/galilei.cpp | 18 +- src/core/ghosts.cpp | 12 +- .../lb_particle_coupling.cpp | 28 +-- .../immersed_boundary/ImmersedBoundaries.cpp | 14 +- src/core/immersed_boundary/ibm_common.cpp | 4 +- src/core/immersed_boundary/ibm_tribend.cpp | 22 ++- src/core/immersed_boundary/ibm_triel.cpp | 4 +- .../object-in-fluid/oif_global_forces.cpp | 12 +- src/core/object-in-fluid/oif_local_forces.hpp | 11 +- src/core/observables/ForceDensityProfile.hpp | 3 +- src/core/observables/ParticleTraits.hpp | 10 +- src/core/observables/fetch_particles.hpp | 4 +- src/core/pair_criteria/pair_criteria.hpp | 6 +- src/core/particle_data.cpp | 35 ++-- src/core/pressure_inline.hpp | 16 +- src/core/rattle.cpp | 31 ++-- src/core/rattle.hpp | 6 +- src/core/rotate_system.cpp | 8 +- src/core/statistics.cpp | 58 +++--- src/core/statistics_chain.cpp | 19 +- src/core/stokesian_dynamics/sd_interface.cpp | 4 +- .../EspressoSystemStandAlone_test.cpp | 38 ++-- src/core/unit_tests/Particle_test.cpp | 40 ++++- src/core/unit_tests/Verlet_list_test.cpp | 12 +- src/core/unit_tests/energy_test.cpp | 26 +-- src/core/unit_tests/link_cell_test.cpp | 6 +- src/core/unit_tests/thermostats_test.cpp | 28 +-- src/core/virtual_sites.cpp | 39 ++-- src/core/virtual_sites/VirtualSites.hpp | 2 +- .../VirtualSitesInertialessTracers.cpp | 2 +- .../virtual_sites/VirtualSitesRelative.cpp | 66 +++---- .../virtual_sites/lb_inertialess_tracers.cpp | 42 ++--- .../lb_inertialess_tracers_cuda_interface.cpp | 14 +- .../virtual_sites/VirtualSites.hpp | 2 +- 45 files changed, 614 insertions(+), 442 deletions(-) diff --git a/src/core/PartCfg.cpp b/src/core/PartCfg.cpp index aa890870454..a2437195526 100644 --- a/src/core/PartCfg.cpp +++ b/src/core/PartCfg.cpp @@ -50,8 +50,8 @@ void PartCfg::update() { m_parts.push_back(get_particle_data(id)); auto &p = m_parts.back(); - p.r.p += image_shift(p.l.i, box_geo.length()); - p.l.i = {}; + p.pos() += image_shift(p.image_box(), box_geo.length()); + p.image_box() = {}; } offset += this_size; diff --git a/src/core/Particle.hpp b/src/core/Particle.hpp index 004715e240a..8243f3610c3 100644 --- a/src/core/Particle.hpp +++ b/src/core/Particle.hpp @@ -29,10 +29,21 @@ #include +#include #include #include #include +namespace detail { +inline void check_axis_idx_valid(int const axis) { + assert(axis >= 0 and axis <= 2); +} + +inline bool get_nth_bit(uint8_t const bitfield, int const bit_idx) { + return bitfield & (1u << bit_idx); +} +} // namespace detail + enum : uint8_t { ROTATION_FIXED = 0u, ROTATION_X = 1u, @@ -57,9 +68,9 @@ enum : uint8_t { struct ParticleParametersSwimming { /** Is the particle a swimmer. */ bool swimming = false; - /** Constant velocity to relax to. */ - double f_swim = 0.; /** Imposed constant force. */ + double f_swim = 0.; + /** Constant velocity to relax to. */ double v_swim = 0.; /** Flag for the swimming mode in a LB fluid. * Values: @@ -421,11 +432,6 @@ struct Particle { // NOLINT(bugprone-exception-escape) private: BondList bl; -public: - auto &bonds() { return bl; } - auto const &bonds() const { return bl; } - -private: #ifdef EXCLUSIONS /** list of particles, with which this particle has no non-bonded * interactions @@ -434,21 +440,151 @@ struct Particle { // NOLINT(bugprone-exception-escape) #endif public: - std::vector &exclusions() { -#ifdef EXCLUSIONS - return el; + auto const &id() const { return p.identity; } + auto &id() { return p.identity; } + auto const &mol_id() const { return p.mol_id; } + auto &mol_id() { return p.mol_id; } + auto const &type() const { return p.type; } + auto &type() { return p.type; } + + auto const &bonds() const { return bl; } + auto &bonds() { return bl; } + + auto const &pos() const { return r.p; } + auto &pos() { return r.p; } + auto const &v() const { return m.v; } + auto &v() { return m.v; } + auto const &force() const { return f.f; } + auto &force() { return f.f; } + + bool is_ghost() const { return l.ghost; } + void set_ghost(bool const ghost_flag) { l.ghost = ghost_flag; } + auto &pos_at_last_verlet_update() { return l.p_old; } + auto const &pos_at_last_verlet_update() const { return l.p_old; } + auto const &image_box() const { return l.i; } + auto &image_box() { return l.i; } + +#ifdef MASS + auto const &mass() const { return p.mass; } + auto &mass() { return p.mass; } #else - throw std::runtime_error{"Exclusions not enabled."}; + constexpr auto &mass() const { return p.mass; } #endif +#ifdef ROTATION + bool can_rotate() const { + return can_rotate_around(0) or can_rotate_around(1) or can_rotate_around(2); } - - std::vector const &exclusions() const { -#ifdef EXCLUSIONS - return el; + bool can_rotate_around(int const axis) const { + detail::check_axis_idx_valid(axis); + return detail::get_nth_bit(p.rotation, axis); + } + void set_can_rotate_around(int const axis, bool const rot_flag) { + detail::check_axis_idx_valid(axis); + if (rot_flag) { + p.rotation |= static_cast(1u << axis); + } else { + p.rotation &= static_cast(~(1u << axis)); + } + } + void set_can_rotate_all_axes() { + for (int axis = 0; axis <= 2; axis++) { + set_can_rotate_around(axis, true); + } + } + auto const &quat() const { return r.quat; } + auto &quat() { return r.quat; } + auto const &torque() const { return f.torque; } + auto &torque() { return f.torque; } + auto const &omega() const { return m.omega; } + auto &omega() { return m.omega; } + auto const &ext_torque() const { return p.ext_torque; } + auto &ext_torque() { return p.ext_torque; } + auto calc_director() const { return r.calc_director(); } +#else + bool can_rotate() const { return false; } + bool can_rotate_around(int const axis) const { return false; } +#endif +#ifdef DIPOLES + auto const &dipm() const { return p.dipm; } + auto &dipm() { return p.dipm; } +#endif +#ifdef ROTATIONAL_INERTIA + auto const &rinertia() const { return p.rinertia; } + auto &rinertia() { return p.rinertia; } +#else + constexpr auto &rinertia() const { return p.rinertia; } +#endif +#ifdef ELECTROSTATICS + auto const &q() const { return p.q; } + auto &q() { return p.q; } #else - throw std::runtime_error{"Exclusions not enabled."}; + constexpr auto &q() const { return p.q; } +#endif +#ifdef LB_ELECTROHYDRODYNAMICS + auto const &mu_E() const { return p.mu_E; } + auto &mu_E() { return p.mu_E; } #endif +#ifdef VIRTUAL_SITES + bool is_virtual() const { return p.is_virtual; } + void set_virtual(bool const virt_flag) { p.is_virtual = virt_flag; } +#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; } +#ifdef ROTATION + auto const &gamma_rot() const { return p.gamma_rot; } + auto &gamma_rot() { return p.gamma_rot; } +#endif // ROTATION +#endif // THERMOSTAT_PER_PARTICLE +#ifdef EXTERNAL_FORCES + bool has_fixed_coordinates() const { + return detail::get_nth_bit(p.ext_flag, 0); + } + bool is_fixed_along(int const axis) const { + detail::check_axis_idx_valid(axis); + return detail::get_nth_bit(p.ext_flag, axis + 1); + } + void set_fixed_along(int const axis, bool const fixed_flag) { + // set new flag + if (fixed_flag) { + p.ext_flag |= static_cast(1u << (axis + 1)); + } else { + p.ext_flag &= static_cast(~(1u << (axis + 1))); + } + // check if any flag is set and store that in the 0th bit + if (p.ext_flag >> 1) { + p.ext_flag |= static_cast(1u); + } else { + p.ext_flag &= static_cast(~1u); + } } + auto const &ext_force() const { return p.ext_force; } + auto &ext_force() { return p.ext_force; } + +#else // EXTERNAL_FORCES + constexpr bool has_fixed_coordinates() const { return false; } + constexpr bool is_fixed_along(int const axis) const { return false; } +#endif // EXTERNAL_FORCES +#ifdef ENGINE + auto const &swimming() const { return p.swim; } + auto &swimming() { return p.swim; } +#endif +#ifdef BOND_CONSTRAINT + auto const &pos_last_time_step() const { return r.p_last_timestep; } + auto &pos_last_time_step() { return r.p_last_timestep; } + auto const &rattle_params() const { return rattle; } + auto &rattle_params() { return rattle; } +#endif +#ifdef EXCLUSIONS + auto const &exclusions() const { return el; } + auto &exclusions() { return el; } +#endif private: friend boost::serialization::access; diff --git a/src/core/cells.cpp b/src/core/cells.cpp index 753d7d09832..33068f14a3f 100644 --- a/src/core/cells.cpp +++ b/src/core/cells.cpp @@ -70,7 +70,7 @@ std::vector> get_pairs_filtered(double const distance, Particle const &p2, Distance const &d) { if (d.dist2 < cutoff2 and filter(p1) and filter(p2)) - ret.emplace_back(p1.p.identity, p2.p.identity); + ret.emplace_back(p1.id(), p2.id()); }; cell_structure.non_bonded_loop(pair_kernel); @@ -102,7 +102,7 @@ std::vector non_bonded_loop_trace() { std::vector ret; auto pair_kernel = [&ret](Particle const &p1, Particle const &p2, Distance const &d) { - ret.emplace_back(p1.p.identity, p2.p.identity, p1.r.p, p2.r.p, d.vec21, + ret.emplace_back(p1.id(), p2.id(), p1.pos(), p2.pos(), d.vec21, comm_cart.rank()); }; @@ -124,7 +124,7 @@ static auto mpi_get_pairs_of_types_local(double const distance, std::vector const &types) { auto pairs = get_pairs_filtered(distance, [types](Particle const &p) { return std::any_of(types.begin(), types.end(), - [p](int const type) { return p.p.type == type; }); + [p](int const type) { return p.type() == type; }); }); Utils::Mpi::gather_buffer(pairs, comm_cart); return pairs; @@ -246,7 +246,7 @@ void cells_update_ghosts(unsigned data_parts) { /* Add the ghost particles to the index if we don't already * have them. */ for (auto &part : cell_structure.ghost_particles()) { - if (cell_structure.get_local_particle(part.p.identity) == nullptr) { + if (cell_structure.get_local_particle(part.id()) == nullptr) { cell_structure.update_particle_index(part.identity(), &part); } } diff --git a/src/core/cells.hpp b/src/core/cells.hpp index 01c049cad2c..6452cbbb2e3 100644 --- a/src/core/cells.hpp +++ b/src/core/cells.hpp @@ -113,8 +113,8 @@ std::vector mpi_resort_particles(int global_flag); /** * @brief Find the cell in which a particle is stored. * - * Uses position_to_cell on p.r.p. If this is not on the node's domain, - * uses position at last Verlet list rebuild (p.l.p_old). + * Uses position_to_cell on p.pos(). If this is not on the node's domain, + * uses position at last Verlet list rebuild (p.p_old()). * * @return pointer to the cell or nullptr if the particle is not on the node */ diff --git a/src/core/cluster_analysis/Cluster.cpp b/src/core/cluster_analysis/Cluster.cpp index cfdae59aeef..0841aaccefd 100644 --- a/src/core/cluster_analysis/Cluster.cpp +++ b/src/core/cluster_analysis/Cluster.cpp @@ -57,15 +57,15 @@ Cluster::center_of_mass_subcluster(std::vector const &particle_ids) { // of the cluster is arbitrarily chosen as reference. auto const reference_position = - folded_position(get_particle_data(particles[0]).r.p, box_geo); + folded_position(get_particle_data(particles[0]).pos(), box_geo); double total_mass = 0.; for (int pid : particle_ids) { auto const folded_pos = - folded_position(get_particle_data(pid).r.p, box_geo); + folded_position(get_particle_data(pid).pos(), box_geo); auto const dist_to_reference = box_geo.get_mi_vector(folded_pos, reference_position); - com += dist_to_reference * get_particle_data(pid).p.mass; - total_mass += get_particle_data(pid).p.mass; + com += dist_to_reference * get_particle_data(pid).mass(); + total_mass += get_particle_data(pid).mass(); } // Normalize by number of particles @@ -83,8 +83,8 @@ double Cluster::longest_distance() { for (auto a = particles.begin(); a != particles.end(); a++) { for (auto b = a; ++b != particles.end();) { auto const dist = box_geo - .get_mi_vector(get_particle_data(*a).r.p, - get_particle_data(*b).r.p) + .get_mi_vector(get_particle_data(*a).pos(), + get_particle_data(*b).pos()) .norm(); // Larger than previous largest distance? @@ -107,7 +107,7 @@ Cluster::radius_of_gyration_subcluster(std::vector const &particle_ids) { for (auto const pid : particle_ids) { // calculate square length of this distance sum_sq_dist += - box_geo.get_mi_vector(com, get_particle_data(pid).r.p).norm2(); + box_geo.get_mi_vector(com, get_particle_data(pid).pos()).norm2(); } return sqrt(sum_sq_dist / static_cast(particle_ids.size())); @@ -137,7 +137,7 @@ std::pair Cluster::fractal_dimension(double dr) { std::vector distances; for (auto const &it : particles) { - distances.push_back(box_geo.get_mi_vector(com, get_particle_data(it).r.p) + distances.push_back(box_geo.get_mi_vector(com, get_particle_data(it).pos()) .norm()); // add distance from the current particle // to the com in the distances vectors } diff --git a/src/core/collision.cpp b/src/core/collision.cpp index ea8c87c8604..f93dccb9b4b 100644 --- a/src/core/collision.cpp +++ b/src/core/collision.cpp @@ -268,23 +268,23 @@ const Particle &glue_to_surface_calc_vs_pos(const Particle &p1, const Particle &p2, Utils::Vector3d &pos) { double c; - auto const vec21 = box_geo.get_mi_vector(p1.r.p, p2.r.p); + auto const vec21 = box_geo.get_mi_vector(p1.pos(), p2.pos()); const double dist_betw_part = vec21.norm(); // Find out, which is the particle to be glued. - if ((p1.p.type == collision_params.part_type_to_be_glued) && - (p2.p.type == collision_params.part_type_to_attach_vs_to)) { + if ((p1.type() == collision_params.part_type_to_be_glued) && + (p2.type() == collision_params.part_type_to_attach_vs_to)) { c = 1 - collision_params.dist_glued_part_to_vs / dist_betw_part; - } else if ((p2.p.type == collision_params.part_type_to_be_glued) && - (p1.p.type == collision_params.part_type_to_attach_vs_to)) { + } else if ((p2.type() == collision_params.part_type_to_be_glued) && + (p1.type() == collision_params.part_type_to_attach_vs_to)) { c = collision_params.dist_glued_part_to_vs / dist_betw_part; } else { throw std::runtime_error("This should never be thrown. Bug."); } for (int i = 0; i < 3; i++) { - pos[i] = p2.r.p[i] + vec21[i] * c; + pos[i] = p2.pos()[i] + vec21[i] * c; } - if (p1.p.type == collision_params.part_type_to_attach_vs_to) + if (p1.type() == collision_params.part_type_to_attach_vs_to) return p1; return p2; @@ -294,9 +294,9 @@ void bind_at_point_of_collision_calc_vs_pos(const Particle *const p1, const Particle *const p2, Utils::Vector3d &pos1, Utils::Vector3d &pos2) { - auto const vec21 = box_geo.get_mi_vector(p1->r.p, p2->r.p); - pos1 = p1->r.p - vec21 * collision_params.vs_placement; - pos2 = p1->r.p - vec21 * (1. - collision_params.vs_placement); + auto const vec21 = box_geo.get_mi_vector(p1->pos(), p2->pos()); + pos1 = p1->pos() - vec21 * collision_params.vs_placement; + pos2 = p1->pos() - vec21 * (1. - collision_params.vs_placement); } // Considers three particles for three_particle_binding and performs @@ -305,10 +305,12 @@ void coldet_do_three_particle_bond(Particle &p, Particle const &p1, Particle const &p2) { // If p1 and p2 are not closer or equal to the cutoff distance, skip // p1: - if (box_geo.get_mi_vector(p.r.p, p1.r.p).norm() > collision_params.distance) + if (box_geo.get_mi_vector(p.pos(), p1.pos()).norm() > + collision_params.distance) return; // p2: - if (box_geo.get_mi_vector(p.r.p, p2.r.p).norm() > collision_params.distance) + if (box_geo.get_mi_vector(p.pos(), p2.pos()).norm() > + collision_params.distance) return; // Check, if there already is a three-particle bond centered on p @@ -341,9 +343,9 @@ void coldet_do_three_particle_bond(Particle &p, Particle const &p1, // First, find the angle between the particle p, p1 and p2 /* vector from p to p1 */ - auto const vec1 = box_geo.get_mi_vector(p.r.p, p1.r.p).normalize(); + auto const vec1 = box_geo.get_mi_vector(p.pos(), p1.pos()).normalize(); /* vector from p to p2 */ - auto const vec2 = box_geo.get_mi_vector(p.r.p, p2.r.p).normalize(); + auto const vec2 = box_geo.get_mi_vector(p.pos(), p2.pos()).normalize(); auto const cosine = boost::algorithm::clamp(vec1 * vec2, -TINY_COS_VALUE, TINY_COS_VALUE); @@ -360,7 +362,7 @@ void coldet_do_three_particle_bond(Particle &p, Particle const &p1, collision_params.bond_three_particles); // Create the bond - const std::array bondT = {{p1.p.identity, p2.p.identity}}; + const std::array bondT = {{p1.id(), p2.id()}}; p.bonds().insert({bond_id, bondT}); } @@ -369,14 +371,14 @@ void place_vs_and_relate_to_particle(const int current_vs_pid, const Utils::Vector3d &pos, int relate_to) { Particle new_part; - new_part.p.identity = current_vs_pid; - new_part.r.p = pos; + new_part.id() = current_vs_pid; + new_part.pos() = pos; auto p_vs = cell_structure.add_particle(std::move(new_part)); local_vs_relate_to(*p_vs, get_part(relate_to)); p_vs->p.is_virtual = true; - p_vs->p.type = collision_params.vs_particle_type; + p_vs->type() = collision_params.vs_particle_type; } void bind_at_poc_create_bond_between_vs(const int current_vs_pid, @@ -416,10 +418,10 @@ void glue_to_surface_bind_part_to_vs(const Particle *const p1, // Create bond between the virtual particles const int bondG[] = {vs_pid_plus_one - 1}; - if (p1->p.type == collision_params.part_type_after_glueing) { - get_part(p1->p.identity).bonds().insert({collision_params.bond_vs, bondG}); + if (p1->type() == collision_params.part_type_after_glueing) { + get_part(p1->id()).bonds().insert({collision_params.bond_vs, bondG}); } else { - get_part(p2->p.identity).bonds().insert({collision_params.bond_vs, bondG}); + get_part(p2->id()).bonds().insert({collision_params.bond_vs, bondG}); } } @@ -438,7 +440,7 @@ static void three_particle_binding_do_search(Cell *basecell, Particle &p1, auto handle_cell = [&p1, &p2](Cell *c) { for (auto &P : c->particles()) { // Skip collided particles themselves - if ((P.p.identity == p1.p.identity) || (P.p.identity == p2.p.identity)) { + if ((P.id() == p1.id()) || (P.id() == p2.id())) { continue; } @@ -447,15 +449,15 @@ static void three_particle_binding_do_search(Cell *basecell, Particle &p1, // non-cyclic permutations). // coldet_do_three_particle_bond checks the bonding criterion and if // the involved particles are not already bonded before it binds them. - if (!P.l.ghost) { + if (!P.is_ghost()) { coldet_do_three_particle_bond(P, p1, p2); } - if (!p1.l.ghost) { + if (!p1.is_ghost()) { coldet_do_three_particle_bond(p1, P, p2); } - if (!p2.l.ghost) { + if (!p2.is_ghost()) { coldet_do_three_particle_bond(p2, P, p1); } } @@ -513,7 +515,7 @@ void handle_collisions() { if (bind_centers()) { for (auto &c : local_collision_queue) { // put the bond to the non-ghost particle; at least one partner always is - if (cell_structure.get_local_particle(c.pp1)->l.ghost) { + if (cell_structure.get_local_particle(c.pp1)->is_ghost()) { std::swap(c.pp1, c.pp2); } @@ -554,7 +556,7 @@ void handle_collisions() { // or one is ghost and one is not accessible // we only increase the counter for the ext id to use based on the // number of particles created by other nodes - if (((!p1 or p1->l.ghost) and (!p2 or p2->l.ghost)) or !p1 or !p2) { + if (((!p1 or p1->is_ghost()) and (!p2 or p2->is_ghost())) or !p1 or !p2) { // Increase local counters if (collision_params.mode & COLLISION_MODE_VS) { current_vs_pid++; @@ -563,12 +565,12 @@ void handle_collisions() { current_vs_pid++; if (collision_params.mode == COLLISION_MODE_GLUE_TO_SURF) { if (p1) - if (p1->p.type == collision_params.part_type_to_be_glued) { - p1->p.type = collision_params.part_type_after_glueing; + if (p1->type() == collision_params.part_type_to_be_glued) { + p1->type() = collision_params.part_type_after_glueing; } if (p2) - if (p2->p.type == collision_params.part_type_to_be_glued) { - p2->p.type = collision_params.part_type_after_glueing; + if (p2->type() == collision_params.part_type_to_be_glued) { + p2->type() = collision_params.part_type_after_glueing; } } // mode glue to surface @@ -580,14 +582,14 @@ void handle_collisions() { Utils::Vector3d pos1, pos2; // Enable rotation on the particles to which vs will be attached - p1->p.rotation = ROTATION_X | ROTATION_Y | ROTATION_Z; - p2->p.rotation = ROTATION_X | ROTATION_Y | ROTATION_Z; + p1->set_can_rotate_all_axes(); + p2->set_can_rotate_all_axes(); // Positions of the virtual sites bind_at_point_of_collision_calc_vs_pos(p1, p2, pos1, pos2); auto handle_particle = [&](Particle *p, Utils::Vector3d const &pos) { - if (not p->l.ghost) { + if (not p->is_ghost()) { place_vs_and_relate_to_particle(current_vs_pid, pos, p->identity()); // Particle storage locations may have changed due to @@ -618,8 +620,8 @@ void handle_collisions() { // can not always know whether or not a vs is placed if (collision_params.part_type_after_glueing != collision_params.part_type_to_be_glued) { - if ((p1->p.type == collision_params.part_type_after_glueing) || - (p2->p.type == collision_params.part_type_after_glueing)) { + if ((p1->type() == collision_params.part_type_after_glueing) || + (p2->type() == collision_params.part_type_after_glueing)) { current_vs_pid++; continue; } @@ -631,22 +633,22 @@ void handle_collisions() { // Add a bond between the centers of the colliding particles // The bond is placed on the node that has p1 - if (!p1->l.ghost) { + if (!p1->is_ghost()) { const int bondG[] = {c.pp2}; get_part(c.pp1).bonds().insert( {collision_params.bond_centers, bondG}); } // Change type of particle being attached, to make it inert - if (p1->p.type == collision_params.part_type_to_be_glued) { - p1->p.type = collision_params.part_type_after_glueing; + if (p1->type() == collision_params.part_type_to_be_glued) { + p1->type() = collision_params.part_type_after_glueing; } - if (p2->p.type == collision_params.part_type_to_be_glued) { - p2->p.type = collision_params.part_type_after_glueing; + if (p2->type() == collision_params.part_type_to_be_glued) { + p2->type() = collision_params.part_type_after_glueing; } // Vs placement happens on the node that has p1 - if (!attach_vs_to.l.ghost) { + if (!attach_vs_to.is_ghost()) { place_vs_and_relate_to_particle(current_vs_pid, pos, attach_vs_to.identity()); // Particle storage locations may have changed due to diff --git a/src/core/constraints/Constraints.hpp b/src/core/constraints/Constraints.hpp index a5179f11bed..e69f8599208 100644 --- a/src/core/constraints/Constraints.hpp +++ b/src/core/constraints/Constraints.hpp @@ -81,7 +81,7 @@ template class Constraints { reset_forces(); for (auto &p : particles) { - auto const pos = folded_position(p.r.p, box_geo); + auto const pos = folded_position(p.pos(), box_geo); ParticleForce force{}; for (auto const &c : *this) { force += c->force(p, pos, t); @@ -94,7 +94,7 @@ template class Constraints { void add_energy(const ParticleRange &particles, double time, Observable_stat &obs_energy) const { for (auto &p : particles) { - auto const pos = folded_position(p.r.p, box_geo); + auto const pos = folded_position(p.pos(), box_geo); for (auto const &constraint : *this) { constraint->add_energy(p, pos, time, obs_energy); diff --git a/src/core/constraints/ShapeBasedConstraint.cpp b/src/core/constraints/ShapeBasedConstraint.cpp index f253b86a0ec..c3e0e92c0eb 100644 --- a/src/core/constraints/ShapeBasedConstraint.cpp +++ b/src/core/constraints/ShapeBasedConstraint.cpp @@ -57,11 +57,11 @@ double ShapeBasedConstraint::min_dist(const ParticleRange &particles) { std::numeric_limits::infinity(), [this](double min, Particle const &p) { IA_parameters const &ia_params = - *get_ia_param(p.p.type, part_rep.p.type); + *get_ia_param(p.type(), part_rep.type()); if (checkIfInteraction(ia_params)) { double dist; Utils::Vector3d vec; - m_shape->calculate_dist(folded_position(p.r.p, box_geo), dist, vec); + m_shape->calculate_dist(folded_position(p.pos(), box_geo), dist, vec); return std::min(min, dist); } return min; @@ -75,7 +75,7 @@ ParticleForce ShapeBasedConstraint::force(Particle const &p, Utils::Vector3d const &folded_pos, double) { ParticleForce pf{}; - IA_parameters const &ia_params = *get_ia_param(p.p.type, part_rep.p.type); + IA_parameters const &ia_params = *get_ia_param(p.type(), part_rep.type()); if (checkIfInteraction(ia_params)) { double dist = 0.; @@ -112,12 +112,12 @@ ParticleForce ShapeBasedConstraint::force(Particle const &p, #endif } } else { - runtimeErrorMsg() << "Constraint violated by particle " << p.p.identity + runtimeErrorMsg() << "Constraint violated by particle " << p.id() << " dist " << dist; } #ifdef ROTATION - part_rep.f.torque += calc_opposing_force(pf, dist_vec).torque; + part_rep.torque() += calc_opposing_force(pf, dist_vec).torque; #endif #ifdef DPD pf.f += dpd_force; @@ -133,7 +133,7 @@ void ShapeBasedConstraint::add_energy(const Particle &p, Observable_stat &obs_energy) const { double energy = 0.0; - IA_parameters const &ia_params = *get_ia_param(p.p.type, part_rep.p.type); + IA_parameters const &ia_params = *get_ia_param(p.type(), part_rep.type()); if (checkIfInteraction(ia_params)) { double dist = 0.0; @@ -147,10 +147,10 @@ void ShapeBasedConstraint::add_energy(const Particle &p, -1.0 * dist); } } else { - runtimeErrorMsg() << "Constraint violated by particle " << p.p.identity; + runtimeErrorMsg() << "Constraint violated by particle " << p.id(); } } - if (part_rep.p.type >= 0) - obs_energy.add_non_bonded_contribution(p.p.type, part_rep.p.type, energy); + if (part_rep.type() >= 0) + obs_energy.add_non_bonded_contribution(p.type(), part_rep.type(), energy); } } // namespace Constraints diff --git a/src/core/cuda_interface.cpp b/src/core/cuda_interface.cpp index 26971b8f4eb..b04782c4a6f 100644 --- a/src/core/cuda_interface.cpp +++ b/src/core/cuda_interface.cpp @@ -40,12 +40,12 @@ static void pack_particles(ParticleRange particles, int i = 0; for (auto const &part : particles) { - buffer[i].p = static_cast(folded_position(part.r.p, box_geo)); + buffer[i].p = static_cast(folded_position(part.pos(), box_geo)); - buffer[i].identity = part.p.identity; + buffer[i].identity = part.id(); buffer[i].v = static_cast(part.m.v); #ifdef VIRTUAL_SITES - buffer[i].is_virtual = part.p.is_virtual; + buffer[i].is_virtual = part.is_virtual(); #endif #ifdef DIPOLES @@ -53,30 +53,30 @@ static void pack_particles(ParticleRange particles, #endif #ifdef LB_ELECTROHYDRODYNAMICS - buffer[i].mu_E = static_cast(part.p.mu_E); + buffer[i].mu_E = static_cast(part.mu_E()); #endif #ifdef ELECTROSTATICS - buffer[i].q = static_cast(part.p.q); + buffer[i].q = static_cast(part.q()); #endif #ifdef MASS - buffer[i].mass = static_cast(part.p.mass); + buffer[i].mass = static_cast(part.mass()); #endif #ifdef ROTATION - buffer[i].director = static_cast(part.r.calc_director()); + buffer[i].director = static_cast(part.calc_director()); #endif #ifdef ENGINE - buffer[i].swim.v_swim = static_cast(part.p.swim.v_swim); - buffer[i].swim.f_swim = static_cast(part.p.swim.f_swim); + buffer[i].swim.v_swim = static_cast(part.swimming().v_swim); + buffer[i].swim.f_swim = static_cast(part.swimming().f_swim); buffer[i].swim.director = buffer[i].director; - buffer[i].swim.push_pull = part.p.swim.push_pull; + buffer[i].swim.push_pull = part.swimming().push_pull; buffer[i].swim.dipole_length = - static_cast(part.p.swim.dipole_length); - buffer[i].swim.swimming = part.p.swim.swimming; + static_cast(part.swimming().dipole_length); + buffer[i].swim.swimming = part.swimming().swimming; #endif i++; } @@ -120,7 +120,7 @@ static void add_forces_and_torques(ParticleRange particles, for (int j = 0; j < 3; j++) { part.f.f[j] += forces[3 * i + j]; #ifdef ROTATION - part.f.torque[j] += torques[3 * i + j]; + part.torque()[j] += torques[3 * i + j]; #endif } i++; diff --git a/src/core/energy_inline.hpp b/src/core/energy_inline.hpp index 18883b2b318..6ec95207668 100644 --- a/src/core/energy_inline.hpp +++ b/src/core/energy_inline.hpp @@ -180,19 +180,19 @@ inline void add_non_bonded_pair_energy(Particle const &p1, Particle const &p2, Utils::Vector3d const &d, double const dist, double const dist2, Observable_stat &obs_energy) { - IA_parameters const &ia_params = *get_ia_param(p1.p.type, p2.p.type); + IA_parameters const &ia_params = *get_ia_param(p1.type(), p2.type()); #ifdef EXCLUSIONS if (do_nonbonded(p1, p2)) #endif obs_energy.add_non_bonded_contribution( - p1.p.type, p2.p.type, + p1.type(), p2.type(), calc_non_bonded_pair_energy(p1, p2, ia_params, d, dist)); #ifdef ELECTROSTATICS if (!obs_energy.coulomb.empty()) obs_energy.coulomb[0] += - Coulomb::pair_energy(p1, p2, p1.p.q * p2.p.q, d, dist); + Coulomb::pair_energy(p1, p2, p1.q() * p2.q(), d, dist); #endif #ifdef DIPOLES @@ -211,7 +211,7 @@ calc_bonded_energy(Bonded_IA_Parameters const &iaparams, Particle const &p1, auto p4 = (n_partners > 2) ? partners[2] : nullptr; if (n_partners == 1) { - auto const dx = box_geo.get_mi_vector(p1.r.p, p2->r.p); + auto const dx = box_geo.get_mi_vector(p1.pos(), p2->pos()); if (auto const *iap = boost::get(&iaparams)) { return iap->energy(dx); } @@ -223,7 +223,7 @@ calc_bonded_energy(Bonded_IA_Parameters const &iaparams, Particle const &p1, } #ifdef ELECTROSTATICS if (auto const *iap = boost::get(&iaparams)) { - return iap->energy(p1.p.q * p2->p.q, dx); + return iap->energy(p1.q() * p2->q(), dx); } if (auto const *iap = boost::get(&iaparams)) { return iap->energy(p1, *p2, dx); @@ -246,16 +246,16 @@ calc_bonded_energy(Bonded_IA_Parameters const &iaparams, Particle const &p1, } // 1 partner if (n_partners == 2) { if (auto const *iap = boost::get(&iaparams)) { - return iap->energy(p1.r.p, p2->r.p, p3->r.p); + return iap->energy(p1.pos(), p2->pos(), p3->pos()); } if (auto const *iap = boost::get(&iaparams)) { - return iap->energy(p1.r.p, p2->r.p, p3->r.p); + return iap->energy(p1.pos(), p2->pos(), p3->pos()); } if (auto const *iap = boost::get(&iaparams)) { - return iap->energy(p1.r.p, p2->r.p, p3->r.p); + return iap->energy(p1.pos(), p2->pos(), p3->pos()); } if (auto const *iap = boost::get(&iaparams)) { - return iap->energy(p1.r.p, p2->r.p, p3->r.p); + return iap->energy(p1.pos(), p2->pos(), p3->pos()); } if (boost::get(&iaparams)) { runtimeWarningMsg() << "Unsupported bond type " + @@ -267,10 +267,10 @@ calc_bonded_energy(Bonded_IA_Parameters const &iaparams, Particle const &p1, } // 2 partners if (n_partners == 3) { if (auto const *iap = boost::get(&iaparams)) { - return iap->energy(p2->r.p, p1.r.p, p3->r.p, p4->r.p); + return iap->energy(p2->pos(), p1.pos(), p3->pos(), p4->pos()); } if (auto const *iap = boost::get(&iaparams)) { - return iap->energy(p2->r.p, p1.r.p, p3->r.p, p4->r.p); + return iap->energy(p2->pos(), p1.pos(), p3->pos(), p4->pos()); } if (boost::get(&iaparams)) { runtimeWarningMsg() << "Unsupported bond type " + @@ -291,7 +291,7 @@ calc_bonded_energy(Bonded_IA_Parameters const &iaparams, Particle const &p1, * @param p particle for which to calculate energies */ inline double translational_kinetic_energy(Particle const &p) { - return p.p.is_virtual ? 0. : 0.5 * p.p.mass * p.m.v.norm2(); + return p.is_virtual() ? 0. : 0.5 * p.mass() * p.v().norm2(); } /** Calculate kinetic energies from rotation for one particle. @@ -299,8 +299,8 @@ inline double translational_kinetic_energy(Particle const &p) { */ inline double rotational_kinetic_energy(Particle const &p) { #ifdef ROTATION - return p.p.rotation - ? 0.5 * (hadamard_product(p.m.omega, p.m.omega) * p.p.rinertia) + return p.can_rotate() + ? 0.5 * (hadamard_product(p.omega(), p.omega()) * p.rinertia()) : 0.0; #else return 0.0; diff --git a/src/core/forces_inline.hpp b/src/core/forces_inline.hpp index ef906923e51..c455c01debf 100644 --- a/src/core/forces_inline.hpp +++ b/src/core/forces_inline.hpp @@ -150,8 +150,8 @@ inline ParticleForce calc_non_bonded_pair_force(Particle const &p1, #ifdef GAY_BERNE // The gb force function isn't inlined, probably due to its size if (dist < ia_params.gay_berne.cut) { - pf += gb_pair_force(p1.r.calc_director(), p2.r.calc_director(), ia_params, - d, dist); + pf += gb_pair_force(p1.calc_director(), p2.calc_director(), ia_params, d, + dist); } #endif pf.f += force_factor * d; @@ -183,7 +183,7 @@ inline ParticleForce calc_opposing_force(ParticleForce const &pf, inline void add_non_bonded_pair_force(Particle &p1, Particle &p2, Utils::Vector3d const &d, double dist, double dist2) { - IA_parameters const &ia_params = *get_ia_param(p1.p.type, p2.p.type); + IA_parameters const &ia_params = *get_ia_param(p1.type(), p2.type()); ParticleForce pf{}; /***********************************************/ @@ -207,8 +207,8 @@ inline void add_non_bonded_pair_force(Particle &p1, Particle &p2, pf.f += std::get<0>(forces); #ifdef P3M // forces from the virtual charges - p1.f.f += std::get<1>(forces); - p2.f.f += std::get<2>(forces); + p1.force() += std::get<1>(forces); + p2.force() += std::get<2>(forces); #endif } #endif @@ -229,8 +229,8 @@ inline void add_non_bonded_pair_force(Particle &p1, Particle &p2, #ifdef DPD if (thermo_switch & THERMO_DPD) { auto const force = dpd_pair_force(p1, p2, ia_params, d, dist, dist2); - p1.f.f += force; - p2.f.f -= force; + p1.force() += force; + p2.force() -= force; } #endif @@ -273,7 +273,7 @@ calc_bond_pair_force(Particle const &p1, Particle const &p2, } #ifdef ELECTROSTATICS if (auto const *iap = boost::get(&iaparams)) { - return iap->force(p1.p.q * p2.p.q, dx); + return iap->force(p1.q() * p2.q(), dx); } if (auto const *iap = boost::get(&iaparams)) { return iap->force(dx); @@ -297,22 +297,22 @@ calc_bond_pair_force(Particle const &p1, Particle const &p2, inline bool add_bonded_two_body_force(Bonded_IA_Parameters const &iaparams, Particle &p1, Particle &p2) { - auto const dx = box_geo.get_mi_vector(p1.r.p, p2.r.p); + auto const dx = box_geo.get_mi_vector(p1.pos(), p2.pos()); if (auto const *iap = boost::get(&iaparams)) { auto result = iap->forces(p1, p2, dx); if (result) { using std::get; - p1.f.f += get<0>(result.get()); - p2.f.f += get<1>(result.get()); + p1.force() += get<0>(result.get()); + p2.force() += get<1>(result.get()); return false; } } else { auto result = calc_bond_pair_force(p1, p2, iaparams, dx); if (result) { - p1.f.f += result.get(); - p2.f.f -= result.get(); + p1.force() += result.get(); + p2.force() -= result.get(); #ifdef NPT npt_add_virial_force_contribution(result.get(), dx); @@ -329,17 +329,17 @@ calc_bonded_three_body_force(Bonded_IA_Parameters const &iaparams, Particle const &p1, Particle const &p2, Particle const &p3) { if (auto const *iap = boost::get(&iaparams)) { - return iap->forces(p1.r.p, p2.r.p, p3.r.p); + return iap->forces(p1.pos(), p2.pos(), p3.pos()); } if (auto const *iap = boost::get(&iaparams)) { - return iap->forces(p1.r.p, p2.r.p, p3.r.p); + return iap->forces(p1.pos(), p2.pos(), p3.pos()); } if (auto const *iap = boost::get(&iaparams)) { - return iap->forces(p1.r.p, p2.r.p, p3.r.p); + return iap->forces(p1.pos(), p2.pos(), p3.pos()); } #ifdef TABULATED if (auto const *iap = boost::get(&iaparams)) { - return iap->forces(p1.r.p, p2.r.p, p3.r.p); + return iap->forces(p1.pos(), p2.pos(), p3.pos()); } #endif if (auto const *iap = boost::get(&iaparams)) { @@ -359,9 +359,9 @@ inline bool add_bonded_three_body_force(Bonded_IA_Parameters const &iaparams, using std::get; auto const &forces = result.get(); - p1.f.f += get<0>(forces); - p2.f.f += get<1>(forces); - p3.f.f += get<2>(forces); + p1.force() += get<0>(forces); + p2.force() += get<1>(forces); + p3.force() += get<2>(forces); return false; } @@ -380,11 +380,11 @@ calc_bonded_four_body_force(Bonded_IA_Parameters const &iaparams, return iap->calc_forces(p1, p2, p3, p4); } if (auto const *iap = boost::get(&iaparams)) { - return iap->forces(p2.r.p, p1.r.p, p3.r.p, p4.r.p); + return iap->forces(p2.pos(), p1.pos(), p3.pos(), p4.pos()); } #ifdef TABULATED if (auto const *iap = boost::get(&iaparams)) { - return iap->forces(p2.r.p, p1.r.p, p3.r.p, p4.r.p); + return iap->forces(p2.pos(), p1.pos(), p3.pos(), p4.pos()); } #endif throw BondUnknownTypeError(); @@ -398,10 +398,10 @@ inline bool add_bonded_four_body_force(Bonded_IA_Parameters const &iaparams, using std::get; auto const &forces = result.get(); - p1.f.f += get<0>(forces); - p2.f.f += get<1>(forces); - p3.f.f += get<2>(forces); - p4.f.f += get<3>(forces); + p1.force() += get<0>(forces); + p2.force() += get<1>(forces); + p3.force() += get<2>(forces); + p4.force() += get<3>(forces); return false; } diff --git a/src/core/galilei.cpp b/src/core/galilei.cpp index 410e18c9cc0..72a0054fa76 100644 --- a/src/core/galilei.cpp +++ b/src/core/galilei.cpp @@ -41,7 +41,7 @@ void local_kill_particle_motion(int omega, const ParticleRange &particles) { if (omega) { p.m = {}; } else { - p.m.v = {}; + p.v() = {}; } } } @@ -52,7 +52,7 @@ void local_kill_particle_forces(int torque, const ParticleRange &particles) { if (torque) { p.f = {}; } else { - p.f.f = {}; + p.force() = {}; } } } @@ -62,11 +62,11 @@ std::pair local_system_CMS() { return boost::accumulate( cell_structure.local_particles(), std::pair{}, [](auto sum, const Particle &p) { - if (not p.p.is_virtual) { + if (not p.is_virtual()) { return std::pair{ - sum.first + - p.p.mass * unfolded_position(p.r.p, p.l.i, box_geo.length()), - sum.second + p.p.mass}; + sum.first + p.mass() * unfolded_position(p.pos(), p.image_box(), + box_geo.length()), + sum.second + p.mass()}; } return std::pair{sum.first, sum.second}; }); @@ -77,9 +77,9 @@ std::pair local_system_CMS_velocity() { return boost::accumulate( cell_structure.local_particles(), std::pair{}, [](auto sum, const Particle &p) { - if (not p.p.is_virtual) { + if (not p.is_virtual()) { return std::pair{ - sum.first + p.p.mass * p.m.v, sum.second + p.p.mass}; + sum.first + p.mass() * p.v(), sum.second + p.mass()}; } return std::pair{sum.first, sum.second}; }); @@ -88,7 +88,7 @@ std::pair local_system_CMS_velocity() { /** Remove the CMS velocity */ void local_galilei_transform(const Utils::Vector3d &cmsvel) { for (auto &p : cell_structure.local_particles()) { - p.m.v -= cmsvel; + p.v() -= cmsvel; } } diff --git a/src/core/ghosts.cpp b/src/core/ghosts.cpp index ae32e4dbaec..72b8e7fbc5e 100644 --- a/src/core/ghosts.cpp +++ b/src/core/ghosts.cpp @@ -153,7 +153,7 @@ static void prepare_send_buffer(CommBuf &send_buffer, } #ifdef BOND_CONSTRAINT if (data_parts & GHOSTTRANS_RATTLE) { - archiver << part.rattle; + archiver << part.rattle_params(); } #endif if (data_parts & GHOSTTRANS_BONDS) { @@ -172,7 +172,7 @@ static void prepare_ghost_cell(ParticleList *cell, int size) { /* Mark particles as ghosts */ for (auto &p : *cell) { - p.l.ghost = true; + p.set_ghost(true); } } @@ -214,7 +214,7 @@ static void put_recv_buffer(CommBuf &recv_buffer, } #ifdef BOND_CONSTRAINT if (data_parts & GHOSTTRANS_RATTLE) { - archiver >> part.rattle; + archiver >> part.rattle_params(); } #endif } @@ -248,7 +248,7 @@ add_rattle_correction_from_recv_buffer(CommBuf &recv_buffer, for (Particle &part : *part_list) { ParticleRattle pr; archiver >> pr; - part.rattle += pr; + part.rattle_params() += pr; } } } @@ -295,7 +295,7 @@ static void cell_cell_transfer(const GhostCommunication &ghost_comm, if (data_parts & GHOSTTRANS_POSITION) { /* ok, this is not nice, but perhaps fast */ part2.r = part1.r; - part2.r.p += ghost_comm.shift; + part2.pos() += ghost_comm.shift; } if (data_parts & GHOSTTRANS_MOMENTUM) { part2.m = part1.m; @@ -304,7 +304,7 @@ static void cell_cell_transfer(const GhostCommunication &ghost_comm, part2.f += part1.f; #ifdef BOND_CONSTRAINT if (data_parts & GHOSTTRANS_RATTLE) - part2.rattle += part1.rattle; + part2.rattle_params() += part1.rattle_params(); #endif } } diff --git a/src/core/grid_based_algorithms/lb_particle_coupling.cpp b/src/core/grid_based_algorithms/lb_particle_coupling.cpp index 9968242523f..5e084386467 100644 --- a/src/core/grid_based_algorithms/lb_particle_coupling.cpp +++ b/src/core/grid_based_algorithms/lb_particle_coupling.cpp @@ -148,24 +148,24 @@ Utils::Vector3d lb_viscous_coupling(Particle const &p, /* calculate fluid velocity at particle's position this is done by linear interpolation (eq. (11) @cite ahlrichs99a) */ auto const interpolated_u = - lb_lbinterpolation_get_interpolated_velocity(p.r.p) * + lb_lbinterpolation_get_interpolated_velocity(p.pos()) * lb_lbfluid_get_lattice_speed(); Utils::Vector3d v_drift = interpolated_u; #ifdef ENGINE - if (p.p.swim.swimming) { - v_drift += p.p.swim.v_swim * p.r.calc_director(); + if (p.swimming().swimming) { + v_drift += p.swimming().v_swim * p.r.calc_director(); } #endif #ifdef LB_ELECTROHYDRODYNAMICS - v_drift += p.p.mu_E; + v_drift += p.mu_E(); #endif /* calculate viscous force (eq. (9) @cite ahlrichs99a) */ - auto const force = -lb_lbcoupling_get_gamma() * (p.m.v - v_drift) + f_random; + auto const force = -lb_lbcoupling_get_gamma() * (p.v() - v_drift) + f_random; - add_md_force(p.r.p, force, time_step); + add_md_force(p.pos(), force, time_step); return force; } @@ -225,18 +225,18 @@ bool in_local_halo(Vector3d const &pos) { #ifdef ENGINE void add_swimmer_force(Particle const &p, double time_step) { - if (p.p.swim.swimming) { + if (p.swimming().swimming) { // calculate source position const double direction = - double(p.p.swim.push_pull) * p.p.swim.dipole_length; + double(p.swimming().push_pull) * p.swimming().dipole_length; auto const director = p.r.calc_director(); - auto const source_position = p.r.p + direction * director; + auto const source_position = p.pos() + direction * director; if (not in_local_halo(source_position)) { return; } - add_md_force(source_position, p.p.swim.f_swim * director, time_step); + add_md_force(source_position, p.swimming().f_swim * director, time_step); } } #endif @@ -290,20 +290,20 @@ void lb_lbcoupling_calc_particle_lattice_ia(bool couple_virtual, }; auto couple_particle = [&](Particle &p) -> void { - if (p.p.is_virtual and !couple_virtual) + if (p.is_virtual() and !couple_virtual) return; /* Particle is in our LB volume, so this node * is responsible to adding its force */ - if (in_local_domain(p.r.p)) { + if (in_local_domain(p.pos())) { auto const force = lb_viscous_coupling( p, noise_amplitude * f_random(p.identity()), time_step); /* add force to the particle */ - p.f.f += force; + p.force() += force; /* Particle is not in our domain, but adds to the force * density in our domain, only calculate contribution to * the LB force density. */ - } else if (in_local_halo(p.r.p)) { + } else if (in_local_halo(p.pos())) { lb_viscous_coupling(p, noise_amplitude * f_random(p.identity()), time_step); } diff --git a/src/core/immersed_boundary/ImmersedBoundaries.cpp b/src/core/immersed_boundary/ImmersedBoundaries.cpp index 27c1eaec9a1..81a52000d0c 100644 --- a/src/core/immersed_boundary/ImmersedBoundaries.cpp +++ b/src/core/immersed_boundary/ImmersedBoundaries.cpp @@ -115,9 +115,10 @@ void ImmersedBoundaries::calc_volumes(CellStructure &cs) { // Unfold position of first node. // This is to get a continuous trajectory with no jumps when box // boundaries are crossed. - auto const x1 = unfolded_position(p1.r.p, p1.l.i, box_geo.length()); - auto const x2 = x1 + box_geo.get_mi_vector(p2.r.p, x1); - auto const x3 = x1 + box_geo.get_mi_vector(p3.r.p, x1); + auto const x1 = + unfolded_position(p1.pos(), p1.image_box(), box_geo.length()); + auto const x2 = x1 + box_geo.get_mi_vector(p2.pos(), x1); + auto const x3 = x1 + box_geo.get_mi_vector(p3.pos(), x1); // Volume of this tetrahedron // See @cite zhang01b @@ -174,12 +175,13 @@ void ImmersedBoundaries::calc_volume_force(CellStructure &cs) { // Unfold position of first node. // This is to get a continuous trajectory with no jumps when box // boundaries are crossed. - auto const x1 = unfolded_position(p1.r.p, p1.l.i, box_geo.length()); + auto const x1 = + unfolded_position(p1.pos(), p1.image_box(), box_geo.length()); // Unfolding seems to work only for the first particle of a triel // so get the others from relative vectors considering PBC - auto const a12 = box_geo.get_mi_vector(p2.r.p, x1); - auto const a13 = box_geo.get_mi_vector(p3.r.p, x1); + auto const a12 = box_geo.get_mi_vector(p2.pos(), x1); + auto const a13 = box_geo.get_mi_vector(p3.pos(), x1); // Now we have the true and good coordinates // This is eq. (9) in @cite dupin08a. diff --git a/src/core/immersed_boundary/ibm_common.cpp b/src/core/immersed_boundary/ibm_common.cpp index f66d2e7f3d3..e9e7e41422f 100644 --- a/src/core/immersed_boundary/ibm_common.cpp +++ b/src/core/immersed_boundary/ibm_common.cpp @@ -34,7 +34,7 @@ Utils::Vector3d get_ibm_particle_position(int pid) { auto *p = cell_structure.get_local_particle(pid); boost::optional opt_part{boost::none}; - if (p and not p->l.ghost) { + if (p and not p->is_ghost()) { opt_part = *p; } opt_part = boost::mpi::all_reduce(comm_cart, opt_part, @@ -46,6 +46,6 @@ Utils::Vector3d get_ibm_particle_position(int pid) { return item; }); if (opt_part) - return opt_part.get().r.p; + return opt_part.get().pos(); throw std::runtime_error("Immersed Boundary: Particle not found"); } \ No newline at end of file diff --git a/src/core/immersed_boundary/ibm_tribend.cpp b/src/core/immersed_boundary/ibm_tribend.cpp index da1cf6b0282..b776b6dc903 100644 --- a/src/core/immersed_boundary/ibm_tribend.cpp +++ b/src/core/immersed_boundary/ibm_tribend.cpp @@ -34,9 +34,9 @@ IBMTribend::calc_forces(Particle const &p1, Particle const &p2, Particle const &p3, Particle const &p4) const { // Get vectors making up the two triangles - auto const dx1 = box_geo.get_mi_vector(p1.r.p, p3.r.p); - auto const dx2 = box_geo.get_mi_vector(p2.r.p, p3.r.p); - auto const dx3 = box_geo.get_mi_vector(p4.r.p, p3.r.p); + auto const dx1 = box_geo.get_mi_vector(p1.pos(), p3.pos()); + auto const dx2 = box_geo.get_mi_vector(p2.pos(), p3.pos()); + auto const dx3 = box_geo.get_mi_vector(p4.pos(), p3.pos()); // Get normals on triangle; pointing outwards by definition of indices // sequence @@ -75,15 +75,19 @@ IBMTribend::calc_forces(Particle const &p1, Particle const &p2, // Force on particles: eq. (C.28-C.31) auto const force1 = - Pre * (vector_product(box_geo.get_mi_vector(p2.r.p, p3.r.p), v1) / Ai + - vector_product(box_geo.get_mi_vector(p3.r.p, p4.r.p), v2) / Aj); + Pre * + (vector_product(box_geo.get_mi_vector(p2.pos(), p3.pos()), v1) / Ai + + vector_product(box_geo.get_mi_vector(p3.pos(), p4.pos()), v2) / Aj); auto const force2 = - Pre * (vector_product(box_geo.get_mi_vector(p3.r.p, p1.r.p), v1) / Ai); + Pre * + (vector_product(box_geo.get_mi_vector(p3.pos(), p1.pos()), v1) / Ai); auto const force3 = - Pre * (vector_product(box_geo.get_mi_vector(p1.r.p, p2.r.p), v1) / Ai + - vector_product(box_geo.get_mi_vector(p4.r.p, p1.r.p), v2) / Aj); + Pre * + (vector_product(box_geo.get_mi_vector(p1.pos(), p2.pos()), v1) / Ai + + vector_product(box_geo.get_mi_vector(p4.pos(), p1.pos()), v2) / Aj); auto const force4 = - Pre * (vector_product(box_geo.get_mi_vector(p1.r.p, p3.r.p), v2) / Aj); + Pre * + (vector_product(box_geo.get_mi_vector(p1.pos(), p3.pos()), v2) / Aj); return std::make_tuple(force1, force2, force3, force4); } diff --git a/src/core/immersed_boundary/ibm_triel.cpp b/src/core/immersed_boundary/ibm_triel.cpp index 123ffa587ef..691a03c9d71 100644 --- a/src/core/immersed_boundary/ibm_triel.cpp +++ b/src/core/immersed_boundary/ibm_triel.cpp @@ -79,11 +79,11 @@ IBMTriel::calc_forces(Particle const &p1, Particle const &p2, // Calculate the current shape of the triangle (l,lp,cos(phi),sin(phi)); // l = length between 1 and 3 // get_mi_vector is an ESPResSo function which considers PBC - auto const vec2 = box_geo.get_mi_vector(p3.r.p, p1.r.p); + auto const vec2 = box_geo.get_mi_vector(p3.pos(), p1.pos()); auto const l = vec2.norm(); // lp = length between 1 and 2 - auto const vec1 = box_geo.get_mi_vector(p2.r.p, p1.r.p); + auto const vec1 = box_geo.get_mi_vector(p2.pos(), p1.pos()); auto const lp = vec1.norm(); // Check for sanity diff --git a/src/core/object-in-fluid/oif_global_forces.cpp b/src/core/object-in-fluid/oif_global_forces.cpp index bd7cd4c5d88..7658d5d9356 100644 --- a/src/core/object-in-fluid/oif_global_forces.cpp +++ b/src/core/object-in-fluid/oif_global_forces.cpp @@ -48,13 +48,14 @@ Utils::Vector2d calc_oif_global(int molType, CellStructure &cs) { cs.bond_loop([&partArea, &VOL_partVol, molType](Particle &p1, int bond_id, Utils::Span partners) { - if (p1.p.mol_id != molType) + if (p1.mol_id() != molType) return false; if (boost::get(bonded_ia_params.at(bond_id).get()) != nullptr) { // remaining neighbors fetched - auto const p11 = unfolded_position(p1.r.p, p1.l.i, box_geo.length()); + auto const p11 = + unfolded_position(p1.pos(), p1.image_box(), box_geo.length()); auto const p22 = p11 + box_geo.get_mi_vector(partners[0]->r.p, p11); auto const p33 = p11 + box_geo.get_mi_vector(partners[1]->r.p, p11); @@ -83,12 +84,13 @@ void add_oif_global_forces(Utils::Vector2d const &area_volume, int molType, cs.bond_loop([area, VOL_volume, molType](Particle &p1, int bond_id, Utils::Span partners) { - if (p1.p.mol_id != molType) + if (p1.mol_id() != molType) return false; if (auto const *iaparams = boost::get( bonded_ia_params.at(bond_id).get())) { - auto const p11 = unfolded_position(p1.r.p, p1.l.i, box_geo.length()); + auto const p11 = + unfolded_position(p1.pos(), p1.image_box(), box_geo.length()); auto const p22 = p11 + box_geo.get_mi_vector(partners[0]->r.p, p11); auto const p33 = p11 + box_geo.get_mi_vector(partners[1]->r.p, p11); @@ -117,7 +119,7 @@ void add_oif_global_forces(Utils::Vector2d const &area_volume, int molType, (m1_length * m1_length + m2_length * m2_length + m3_length * m3_length); - p1.f.f += fac * m1 + VOL_force; + p1.force() += fac * m1 + VOL_force; partners[0]->f.f += fac * m2 + VOL_force; partners[1]->f.f += fac * m3 + VOL_force; } diff --git a/src/core/object-in-fluid/oif_local_forces.hpp b/src/core/object-in-fluid/oif_local_forces.hpp index 68b86811c5b..1af2a03556b 100644 --- a/src/core/object-in-fluid/oif_local_forces.hpp +++ b/src/core/object-in-fluid/oif_local_forces.hpp @@ -118,10 +118,11 @@ OifLocalForcesBond::calc_forces(Particle const &p2, Particle const &p1, Particle const &p3, Particle const &p4) const { // first-fold-then-the-same approach - auto const fp2 = unfolded_position(p2.r.p, p2.l.i, box_geo.length()); - auto const fp1 = fp2 + box_geo.get_mi_vector(p1.r.p, fp2); - auto const fp3 = fp2 + box_geo.get_mi_vector(p3.r.p, fp2); - auto const fp4 = fp2 + box_geo.get_mi_vector(p4.r.p, fp2); + auto const fp2 = + unfolded_position(p2.pos(), p2.image_box(), box_geo.length()); + auto const fp1 = fp2 + box_geo.get_mi_vector(p1.pos(), fp2); + auto const fp3 = fp2 + box_geo.get_mi_vector(p3.pos(), fp2); + auto const fp4 = fp2 + box_geo.get_mi_vector(p4.pos(), fp2); Utils::Vector3d force1{}, force2{}, force3{}, force4{}; @@ -152,7 +153,7 @@ OifLocalForcesBond::calc_forces(Particle const &p2, Particle const &p1, if (kvisc > TINY_OIF_ELASTICITY_COEFFICIENT) { // to be implemented.... auto const dx = fp2 - fp3; auto const len2 = dx.norm2(); - auto const v_ij = p3.m.v - p2.m.v; + auto const v_ij = p3.v() - p2.v(); // Variant A // Here the force is in the direction of relative velocity btw points diff --git a/src/core/observables/ForceDensityProfile.hpp b/src/core/observables/ForceDensityProfile.hpp index 2b223bd33a6..f3a86945622 100644 --- a/src/core/observables/ForceDensityProfile.hpp +++ b/src/core/observables/ForceDensityProfile.hpp @@ -45,7 +45,8 @@ class ForceDensityProfile : public PidProfileObservable { const ParticleObservables::traits &) const override { Utils::Histogram histogram(n_bins(), limits()); for (auto const &p : particles) { - histogram.update(folded_position(p.get().r.p, box_geo), p.get().f.f); + histogram.update(folded_position(p.get().pos(), box_geo), + p.get().force()); } histogram.normalize(); return histogram.get_histogram(); diff --git a/src/core/observables/ParticleTraits.hpp b/src/core/observables/ParticleTraits.hpp index 0fb7aa82a92..ccc60cee2e0 100644 --- a/src/core/observables/ParticleTraits.hpp +++ b/src/core/observables/ParticleTraits.hpp @@ -29,17 +29,17 @@ namespace ParticleObservables { * of observables independent of the particle type. */ template <> struct traits { - auto position(Particle const &p) const { return p.r.p; } + auto position(Particle const &p) const { return p.pos(); } auto velocity(Particle const &p) const { return p.m.v; } auto mass(Particle const &p) const { #ifdef VIRTUAL_SITES // we exclude virtual particles since their mass does not have a meaning - if (p.p.is_virtual) - return decltype(p.p.mass){}; + if (p.is_virtual()) + return decltype(p.mass()){}; #endif - return p.p.mass; + return p.mass(); } - auto charge(Particle const &p) const { return p.p.q; } + auto charge(Particle const &p) const { return p.q(); } auto dipole_moment(Particle const &p) const { #if defined(ROTATION) && defined(DIPOLES) return p.calc_dip(); diff --git a/src/core/observables/fetch_particles.hpp b/src/core/observables/fetch_particles.hpp index 500ad964a64..cf5200166dd 100644 --- a/src/core/observables/fetch_particles.hpp +++ b/src/core/observables/fetch_particles.hpp @@ -52,8 +52,8 @@ inline std::vector fetch_particles(std::vector const &ids) { particles.push_back(get_particle_data(id)); auto &p = particles.back(); - p.r.p += image_shift(p.l.i, box_geo.length()); - p.l.i = {}; + p.pos() += image_shift(p.image_box(), box_geo.length()); + p.image_box() = {}; } offset += this_size; diff --git a/src/core/pair_criteria/pair_criteria.hpp b/src/core/pair_criteria/pair_criteria.hpp index a64d7061f65..7f249b95a7a 100644 --- a/src/core/pair_criteria/pair_criteria.hpp +++ b/src/core/pair_criteria/pair_criteria.hpp @@ -49,7 +49,7 @@ class PairCriterion { class DistanceCriterion : public PairCriterion { public: bool decide(const Particle &p1, const Particle &p2) const override { - return box_geo.get_mi_vector(p1.r.p, p2.r.p).norm() <= m_cut_off; + return box_geo.get_mi_vector(p1.pos(), p2.pos()).norm() <= m_cut_off; } double get_cut_off() { return m_cut_off; } void set_cut_off(double c) { m_cut_off = c; } @@ -63,11 +63,11 @@ class EnergyCriterion : public PairCriterion { public: bool decide(const Particle &p1, const Particle &p2) const override { // Distance between particles - auto const vec21 = box_geo.get_mi_vector(p1.r.p, p2.r.p); + auto const vec21 = box_geo.get_mi_vector(p1.pos(), p2.pos()); const double dist_betw_part = vec21.norm(); // Interaction parameters for particle types - IA_parameters const &ia_params = *get_ia_param(p1.p.type, p2.p.type); + IA_parameters const &ia_params = *get_ia_param(p1.type(), p2.type()); return (calc_non_bonded_pair_energy(p1, p2, ia_params, vec21, dist_betw_part)) >= m_cut_off; diff --git a/src/core/particle_data.cpp b/src/core/particle_data.cpp index 459cb988d29..1444816d977 100644 --- a/src/core/particle_data.cpp +++ b/src/core/particle_data.cpp @@ -408,8 +408,7 @@ static void mpi_who_has_local() { sendbuf.resize(n_part); std::transform(local_particles.begin(), local_particles.end(), - sendbuf.begin(), - [](Particle const &p) { return p.p.identity; }); + sendbuf.begin(), [](Particle const &p) { return p.id(); }); MPI_Send(sendbuf.data(), n_part, MPI_INT, 0, some_tag, comm_cart); } @@ -431,7 +430,7 @@ void mpi_who_has() { for (int pnode = 0; pnode < n_nodes; pnode++) { if (pnode == this_node) { for (auto const &p : local_particles) - particle_node[p.p.identity] = this_node; + particle_node[p.id()] = this_node; } else if (n_parts[pnode] > 0) { pdata.resize(n_parts[pnode]); @@ -482,7 +481,7 @@ std::size_t fetch_cache_max_size() { return particle_fetch_cache.max_size(); } boost::optional get_particle_data_local(int id) { auto p = cell_structure.get_local_particle(id); - if (p and (not p->l.ghost)) { + if (p and (not p->is_ghost())) { return *p; } @@ -599,7 +598,7 @@ void prefetch_particle_data(Utils::Span in_ids) { /* Fetch the particles... */ for (auto &p : mpi_get_particles(ids)) { - auto id = p.identity(); + auto id = p.id(); particle_fetch_cache.put(id, std::move(p)); } } @@ -620,16 +619,16 @@ Particle *local_place_particle(int id, const Utils::Vector3d &pos, int _new) { if (_new) { Particle new_part; - new_part.p.identity = id; - new_part.r.p = pp; - new_part.l.i = i; + new_part.id() = id; + new_part.pos() = pp; + new_part.image_box() = i; return cell_structure.add_local_particle(std::move(new_part)); } auto pt = cell_structure.get_local_particle(id); - pt->r.p = pp; - pt->l.i = i; + pt->pos() = pp; + pt->image_box() = i; return pt; } @@ -770,7 +769,7 @@ void set_particle_virtual(int part, bool is_virtual) { #ifdef VIRTUAL_SITES_RELATIVE void set_particle_vs_quat(int part, Utils::Quaternion const &vs_relative_quat) { - auto vs_relative = get_particle_data(part).p.vs_relative; + auto vs_relative = get_particle_data(part).vs_relative(); vs_relative.quat = vs_relative_quat; mpi_update_particle_property< @@ -815,7 +814,7 @@ void set_particle_type(int p_id, int type) { // check if the particle exists already and the type is changed, then remove // it from the list which contains it auto const &cur_par = get_particle_data(p_id); - int prev_type = cur_par.p.type; + int prev_type = cur_par.type(); if (prev_type != type) { // particle existed before so delete it from the list remove_id_from_map(p_id, prev_type); @@ -961,7 +960,7 @@ int remove_particle(int p_id) { auto const &cur_par = get_particle_data(p_id); if (type_list_enable) { // remove particle from its current type_list - int type = cur_par.p.type; + int type = cur_par.type(); remove_id_from_map(p_id, type); } @@ -979,9 +978,9 @@ int remove_particle(int p_id) { void local_rescale_particles(int dir, double scale) { for (auto &p : cell_structure.local_particles()) { if (dir < 3) - p.r.p[dir] *= scale; + p.pos()[dir] *= scale; else { - p.r.p *= scale; + p.pos() *= scale; } } } @@ -1080,7 +1079,7 @@ void auto_exclusions(int distance) { /* determine initial connectivity */ for (auto const &part1 : partCfg()) { - auto const p1 = part1.p.identity; + auto const p1 = part1.id(); for (auto const bond : part1.bonds()) { if ((bond.partner_ids().size() == 1) and (bond.partner_ids()[0] != p1)) { auto const p2 = bond.partner_ids()[0]; @@ -1095,7 +1094,7 @@ void auto_exclusions(int distance) { */ for (int count = 1; count < distance; count++) { for (auto const &p : partCfg()) { - auto const p1 = p.identity(); + auto const p1 = p.id(); for (int i = 0; i < partners[p1].size(); i += 2) { auto const p2 = partners[p1][i]; auto const dist1 = partners[p1][i + 1]; @@ -1139,7 +1138,7 @@ void init_type_map(int type) { map_for_type.clear(); for (auto const &p : partCfg()) { if (p.p.type == type) - map_for_type.insert(p.p.identity); + map_for_type.insert(p.id()); } } diff --git a/src/core/pressure_inline.hpp b/src/core/pressure_inline.hpp index d5693d3d87b..5b5f18302c8 100644 --- a/src/core/pressure_inline.hpp +++ b/src/core/pressure_inline.hpp @@ -61,12 +61,12 @@ inline void add_non_bonded_pair_virials(Particle const &p1, Particle const &p2, if (do_nonbonded(p1, p2)) #endif { - IA_parameters const &ia_params = *get_ia_param(p1.p.type, p2.p.type); + IA_parameters const &ia_params = *get_ia_param(p1.type(), p2.type()); auto const force = calc_non_bonded_pair_force(p1, p2, ia_params, d, dist).f; auto const stress = tensor_product(d, force); - auto const type1 = p1.p.mol_id; - auto const type2 = p2.p.mol_id; + auto const type1 = p1.mol_id(); + auto const type2 = p2.mol_id(); obs_pressure.add_non_bonded_contribution(type1, type2, flatten(stress)); } @@ -95,7 +95,7 @@ inline void add_non_bonded_pair_virials(Particle const &p1, Particle const &p2, boost::optional> calc_bonded_virial_pressure_tensor(Bonded_IA_Parameters const &iaparams, Particle const &p1, Particle const &p2) { - auto const dx = box_geo.get_mi_vector(p1.r.p, p2.r.p); + auto const dx = box_geo.get_mi_vector(p1.pos(), p2.pos()); auto const result = calc_bond_pair_force(p1, p2, iaparams, dx); if (result) { auto const &force = result.get(); @@ -116,8 +116,8 @@ calc_bonded_three_body_pressure_tensor(Bonded_IA_Parameters const &iaparams, (boost::get(&iaparams) != nullptr) || #endif (boost::get(&iaparams) != nullptr)) { - auto const dx21 = -box_geo.get_mi_vector(p1.r.p, p2.r.p); - auto const dx31 = box_geo.get_mi_vector(p3.r.p, p1.r.p); + auto const dx21 = -box_geo.get_mi_vector(p1.pos(), p2.pos()); + auto const dx31 = box_geo.get_mi_vector(p3.pos(), p1.pos()); auto const result = calc_bonded_three_body_force(iaparams, p1, p2, p3); if (result) { @@ -160,13 +160,13 @@ calc_bonded_pressure_tensor(Bonded_IA_Parameters const &iaparams, */ inline void add_kinetic_virials(Particle const &p1, Observable_stat &obs_pressure) { - if (p1.p.is_virtual) + if (p1.is_virtual()) return; /* kinetic pressure */ for (int k = 0; k < 3; k++) for (int l = 0; l < 3; l++) - obs_pressure.kinetic[k * 3 + l] += p1.m.v[k] * p1.m.v[l] * p1.p.mass; + obs_pressure.kinetic[k * 3 + l] += p1.v()[k] * p1.v()[l] * p1.mass(); } #endif // CORE_PRESSURE_INLINE_HPP diff --git a/src/core/rattle.cpp b/src/core/rattle.cpp index e141405167c..052dc931bef 100644 --- a/src/core/rattle.cpp +++ b/src/core/rattle.cpp @@ -44,7 +44,7 @@ */ void save_old_position(const ParticleRange &particles, const ParticleRange &ghost_particles) { - auto save_pos = [](Particle &p) { p.r.p_last_timestep = p.r.p; }; + auto save_pos = [](Particle &p) { p.pos_last_time_step() = p.pos(); }; boost::for_each(particles, save_pos); boost::for_each(ghost_particles, save_pos); @@ -58,7 +58,7 @@ void save_old_position(const ParticleRange &particles, */ static void init_correction_vector(const ParticleRange &particles, const ParticleRange &ghost_particles) { - auto reset_force = [](Particle &p) { p.rattle.correction.fill(0); }; + auto reset_force = [](Particle &p) { p.rattle_params().correction.fill(0); }; boost::for_each(particles, reset_force); boost::for_each(ghost_particles, reset_force); @@ -74,19 +74,19 @@ static void init_correction_vector(const ParticleRange &particles, */ static bool calculate_positional_correction(RigidBond const &ia_params, Particle &p1, Particle &p2) { - auto const r_ij = box_geo.get_mi_vector(p1.r.p, p2.r.p); + auto const r_ij = box_geo.get_mi_vector(p1.pos(), p2.pos()); auto const r_ij2 = r_ij.norm2(); if (std::abs(1.0 - r_ij2 / ia_params.d2) > ia_params.p_tol) { auto const r_ij_t = - box_geo.get_mi_vector(p1.r.p_last_timestep, p2.r.p_last_timestep); + box_geo.get_mi_vector(p1.pos_last_time_step(), p2.pos_last_time_step()); auto const r_ij_dot = r_ij_t * r_ij; auto const G = - 0.50 * (ia_params.d2 - r_ij2) / r_ij_dot / (p1.p.mass + p2.p.mass); + 0.50 * (ia_params.d2 - r_ij2) / r_ij_dot / (p1.mass() + p2.mass()); auto const pos_corr = G * r_ij_t; - p1.rattle.correction += pos_corr * p2.p.mass; - p2.rattle.correction -= pos_corr * p1.p.mass; + p1.rattle_params().correction += pos_corr * p2.mass(); + p2.rattle_params().correction -= pos_corr * p1.mass(); return true; } @@ -128,8 +128,8 @@ static bool compute_correction_vector(CellStructure &cs, Kernel kernel) { */ static void apply_positional_correction(const ParticleRange &particles) { boost::for_each(particles, [](Particle &p) { - p.r.p += p.rattle.correction; - p.m.v += p.rattle.correction; + p.pos() += p.rattle_params().correction; + p.v() += p.rattle_params().correction; }); } @@ -177,17 +177,17 @@ void correct_position_shake(CellStructure &cs) { */ static bool calculate_velocity_correction(RigidBond const &ia_params, Particle &p1, Particle &p2) { - auto const v_ij = p1.m.v - p2.m.v; - auto const r_ij = box_geo.get_mi_vector(p1.r.p, p2.r.p); + auto const v_ij = p1.v() - p2.v(); + auto const r_ij = box_geo.get_mi_vector(p1.pos(), p2.pos()); auto const v_proj = v_ij * r_ij; if (std::abs(v_proj) > ia_params.v_tol) { - auto const K = v_proj / ia_params.d2 / (p1.p.mass + p2.p.mass); + auto const K = v_proj / ia_params.d2 / (p1.mass() + p2.mass()); auto const vel_corr = K * r_ij; - p1.rattle.correction -= vel_corr * p2.p.mass; - p2.rattle.correction += vel_corr * p1.p.mass; + p1.rattle_params().correction -= vel_corr * p2.mass(); + p2.rattle_params().correction += vel_corr * p1.mass(); return true; } @@ -201,7 +201,8 @@ static bool calculate_velocity_correction(RigidBond const &ia_params, * @param particles particle range */ static void apply_velocity_correction(const ParticleRange &particles) { - boost::for_each(particles, [](Particle &p) { p.m.v += p.rattle.correction; }); + boost::for_each(particles, + [](Particle &p) { p.v() += p.rattle_params().correction; }); } void correct_velocity_shake(CellStructure &cs) { diff --git a/src/core/rattle.hpp b/src/core/rattle.hpp index a5dd83b2e62..3cea6c97a79 100644 --- a/src/core/rattle.hpp +++ b/src/core/rattle.hpp @@ -33,9 +33,9 @@ #include "CellStructure.hpp" -/** Transfer the current particle positions from @ref ParticlePosition::p - * "Particle::r::p" to @ref ParticlePosition::p_last_timestep - * "Particle::r::p_last_timestep" +/** Transfer the current particle positions from @ref Particle::pos + * "Particle::pos" to @ref Particle::pos_last_time_step + * "Particle::pos_last_time_step" */ void save_old_position(const ParticleRange &particles, const ParticleRange &ghost_particles); diff --git a/src/core/rotate_system.cpp b/src/core/rotate_system.cpp index f55dd95deb9..79800122e0a 100644 --- a/src/core/rotate_system.cpp +++ b/src/core/rotate_system.cpp @@ -42,9 +42,9 @@ static void mpi_rotate_system_local(double phi, double theta, double alpha) { double local_mass = 0.0; for (auto const &p : particles) { - if (not p.p.is_virtual) { - local_com += p.p.mass * p.r.p; - local_mass += p.p.mass; + if (not p.is_virtual()) { + local_com += p.mass() * p.pos(); + local_mass += p.mass(); } } @@ -62,7 +62,7 @@ static void mpi_rotate_system_local(double phi, double theta, double alpha) { // Rotate particle coordinates for (auto &p : particles) { // Move the center of mass of the system to the origin - p.r.p = com + Utils::vec_rotate(axis, alpha, p.r.p - com); + p.pos() = com + Utils::vec_rotate(axis, alpha, p.pos() - com); #ifdef ROTATION local_rotate_particle(p, axis, alpha); #endif diff --git a/src/core/statistics.cpp b/src/core/statistics.cpp index 35b83325de7..b6563441352 100644 --- a/src/core/statistics.cpp +++ b/src/core/statistics.cpp @@ -58,9 +58,9 @@ double mindist(PartCfg &partCfg, const std::vector &set1, for (auto jt = partCfg.begin(); jt != partCfg.end(); ++jt) { /* check which sets particle j belongs to (bit 0: set1, bit1: set2) */ auto in_set = 0u; - if (set1.empty() || contains(set1, jt->p.type)) + if (set1.empty() || contains(set1, jt->type())) in_set = 1u; - if (set2.empty() || contains(set2, jt->p.type)) + if (set2.empty() || contains(set2, jt->type())) in_set |= 2u; if (in_set == 0) continue; @@ -68,10 +68,10 @@ double mindist(PartCfg &partCfg, const std::vector &set1, for (auto it = std::next(jt); it != partCfg.end(); ++it) /* accept a pair if particle j is in set1 and particle i in set2 or vice * versa. */ - if (((in_set & 1u) && (set2.empty() || contains(set2, it->p.type))) || - ((in_set & 2u) && (set1.empty() || contains(set1, it->p.type)))) - mindist2 = - std::min(mindist2, box_geo.get_mi_vector(jt->r.p, it->r.p).norm2()); + if (((in_set & 1u) && (set2.empty() || contains(set2, it->type()))) || + ((in_set & 2u) && (set1.empty() || contains(set1, it->type())))) + mindist2 = std::min( + mindist2, box_geo.get_mi_vector(jt->pos(), it->pos()).norm2()); } return std::sqrt(mindist2); @@ -82,7 +82,7 @@ static Utils::Vector3d mpi_particle_momentum_local() { auto const momentum = std::accumulate(particles.begin(), particles.end(), Utils::Vector3d{}, [](Utils::Vector3d &m, Particle const &p) { - return m + p.p.mass * p.m.v; + return m + p.mass() * p.v(); }); return momentum; @@ -110,10 +110,10 @@ Utils::Vector3d centerofmass(PartCfg &partCfg, int type) { double mass = 0.0; for (auto const &p : partCfg) { - if ((p.p.type == type) || (type == -1)) - if (not p.p.is_virtual) { - com += p.r.p * p.p.mass; - mass += p.p.mass; + if ((p.type() == type) || (type == -1)) + if (not p.is_virtual()) { + com += p.pos() * p.mass(); + mass += p.mass(); } } com /= mass; @@ -124,9 +124,9 @@ Utils::Vector3d angularmomentum(PartCfg &partCfg, int type) { Utils::Vector3d am{}; for (auto const &p : partCfg) { - if ((p.p.type == type) || (type == -1)) - if (not p.p.is_virtual) { - am += p.p.mass * vector_product(p.r.p, p.m.v); + if ((p.type() == type) || (type == -1)) + if (not p.is_virtual()) { + am += p.mass() * vector_product(p.pos(), p.v()); } } return am; @@ -143,10 +143,10 @@ void momentofinertiamatrix(PartCfg &partCfg, int type, double *MofImatrix) { auto const com = centerofmass(partCfg, type); for (auto const &p : partCfg) { - if (type == p.p.type and (not p.p.is_virtual)) { + if (type == p.type() and (not p.is_virtual())) { count++; - p1 = p.r.p - com; - massi = p.p.mass; + p1 = p.pos() - com; + massi = p.mass(); MofImatrix[0] += massi * (p1[1] * p1[1] + p1[2] * p1[2]); MofImatrix[4] += massi * (p1[0] * p1[0] + p1[2] * p1[2]); MofImatrix[8] += massi * (p1[0] * p1[0] + p1[1] * p1[1]); @@ -172,16 +172,16 @@ std::vector nbhood(PartCfg &partCfg, const Utils::Vector3d &pos, for (auto const &p : partCfg) { if ((planedims[0] + planedims[1] + planedims[2]) == 3) { - d = box_geo.get_mi_vector(pt, p.r.p); + d = box_geo.get_mi_vector(pt, p.pos()); } else { /* Calculate the in plane distance */ for (int j = 0; j < 3; j++) { - d[j] = planedims[j] * (p.r.p[j] - pt[j]); + d[j] = planedims[j] * (p.pos()[j] - pt[j]); } } if (d.norm2() < r2) { - ids.push_back(p.p.identity); + ids.push_back(p.id()); } } @@ -192,8 +192,9 @@ double distto(PartCfg &partCfg, const Utils::Vector3d &pos, int pid) { auto mindist = std::numeric_limits::infinity(); for (auto const &part : partCfg) { - if (pid != part.p.identity) { - auto const d = box_geo.get_mi_vector({pos[0], pos[1], pos[2]}, part.r.p); + if (pid != part.id()) { + auto const d = + box_geo.get_mi_vector({pos[0], pos[1], pos[2]}, part.pos()); mindist = std::min(mindist, d.norm2()); } } @@ -222,15 +223,15 @@ void calc_part_distribution(PartCfg &partCfg, std::vector const &p1_types, /* particle loop: p1_types */ for (auto const &p1 : partCfg) { for (int t1 : p1_types) { - if (p1.p.type == t1) { + if (p1.type() == t1) { min_dist2 = start_dist2; /* particle loop: p2_types */ for (auto const &p2 : partCfg) { if (p1 != p2) { for (int t2 : p2_types) { - if (p2.p.type == t2) { + if (p2.type() == t2) { auto const act_dist2 = - box_geo.get_mi_vector(p1.r.p, p2.r.p).norm2(); + box_geo.get_mi_vector(p1.pos(), p2.pos()).norm2(); if (act_dist2 < min_dist2) { min_dist2 = act_dist2; } @@ -285,8 +286,9 @@ void calc_structurefactor(PartCfg &partCfg, std::vector const &p_types, double C_sum = 0.0, S_sum = 0.0; for (auto const &p : partCfg) { for (int t : p_types) { - if (p.p.type == t) { - auto const qr = twoPI_L * (Utils::Vector3i{{i, j, k}} * p.r.p); + if (p.type() == t) { + auto const qr = + twoPI_L * (Utils::Vector3i{{i, j, k}} * p.pos()); C_sum += cos(qr); S_sum += sin(qr); } @@ -302,7 +304,7 @@ void calc_structurefactor(PartCfg &partCfg, std::vector const &p_types, long n_particles = 0l; for (auto const &p : partCfg) { for (int t : p_types) { - if (p.p.type == t) + if (p.type() == t) n_particles++; } } diff --git a/src/core/statistics_chain.cpp b/src/core/statistics_chain.cpp index efdb887d141..3aca1593094 100644 --- a/src/core/statistics_chain.cpp +++ b/src/core/statistics_chain.cpp @@ -44,8 +44,9 @@ std::array calc_re(int chain_start, int chain_n_chains, get_particle_data(chain_start + i * chain_length + chain_length - 1); auto const &p2 = get_particle_data(chain_start + i * chain_length); - auto const d = unfolded_position(p1.r.p, p1.l.i, box_geo.length()) - - unfolded_position(p2.r.p, p2.l.i, box_geo.length()); + auto const d = + unfolded_position(p1.pos(), p1.image_box(), box_geo.length()) - + unfolded_position(p2.pos(), p2.image_box(), box_geo.length()); auto const norm2 = d.norm2(); dist += sqrt(norm2); dist2 += norm2; @@ -70,20 +71,21 @@ std::array calc_rg(int chain_start, int chain_n_chains, for (int j = 0; j < chain_length; j++) { auto const &p = get_particle_data(chain_start + i * chain_length + j); - if (p.p.is_virtual) { + if (p.is_virtual()) { throw std::runtime_error( "Gyration tensor is not well-defined for chains including virtual " "sites. Virtual sites do not have a meaningful mass."); } - r_CM += unfolded_position(p.r.p, p.l.i, box_geo.length()) * p.p.mass; - M += p.p.mass; + r_CM += unfolded_position(p.pos(), p.image_box(), box_geo.length()) * + p.mass(); + M += p.mass(); } r_CM /= M; double tmp = 0.0; for (int j = 0; j < chain_length; ++j) { auto const &p = get_particle_data(chain_start + i * chain_length + j); Utils::Vector3d const d = - unfolded_position(p.r.p, p.l.i, box_geo.length()) - r_CM; + unfolded_position(p.pos(), p.image_box(), box_geo.length()) - r_CM; tmp += d.norm2(); } tmp /= static_cast(chain_length); @@ -113,8 +115,9 @@ std::array calc_rh(int chain_start, int chain_n_chains, auto const &p1 = get_particle_data(i); for (int j = i + 1; j < chain_start + chain_length * (p + 1); j++) { auto const &p2 = get_particle_data(j); - auto const d = unfolded_position(p1.r.p, p1.l.i, box_geo.length()) - - unfolded_position(p2.r.p, p2.l.i, box_geo.length()); + auto const d = + unfolded_position(p1.pos(), p1.image_box(), box_geo.length()) - + unfolded_position(p2.pos(), p2.image_box(), box_geo.length()); ri += 1.0 / d.norm(); } } diff --git a/src/core/stokesian_dynamics/sd_interface.cpp b/src/core/stokesian_dynamics/sd_interface.cpp index e41c1045619..9b9c61e9a1b 100644 --- a/src/core/stokesian_dynamics/sd_interface.cpp +++ b/src/core/stokesian_dynamics/sd_interface.cpp @@ -49,7 +49,7 @@ namespace { struct SD_particle_data { SD_particle_data() = default; explicit SD_particle_data(Particle const &p) - : type(p.p.type), pos(p.r.p), ext_force(p.f) {} + : type(p.type()), pos(p.pos()), ext_force(p.f) {} int type = 0; @@ -93,7 +93,7 @@ void sd_update_locally(ParticleRange const &parts) { for (auto &p : parts) { // skip virtual particles - if (p.p.is_virtual) { + if (p.is_virtual()) { continue; } diff --git a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp index 85c6368ef8f..0498412943f 100644 --- a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp +++ b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp @@ -155,8 +155,8 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, auto const acc_value = time_series.back(); auto const obs_value = (*obs)(); - BOOST_TEST(obs_value == p.m.v, boost::test_tools::per_element()); - BOOST_TEST(acc_value == p.m.v, boost::test_tools::per_element()); + BOOST_TEST(obs_value == p.v(), boost::test_tools::per_element()); + BOOST_TEST(acc_value == p.v(), boost::test_tools::per_element()); } } @@ -166,7 +166,7 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, for (int i = 0; i < 5; ++i) { set_particle_v(pid2, {static_cast(i), 0., 0.}); auto const &p = get_particle_data(pid2); - auto const kinetic_energy = 0.5 * p.p.mass * p.m.v.norm2(); + auto const kinetic_energy = 0.5 * p.mass() * p.v().norm2(); auto const obs_energy = calculate_energy(); BOOST_CHECK_CLOSE(obs_energy->kinetic[0], kinetic_energy, tol); BOOST_CHECK_CLOSE(observable_compute_energy(), kinetic_energy, tol); @@ -253,7 +253,7 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, // measure energies auto const step = 0.02; - auto const pos1 = get_particle_data(pid1).r.p; + auto const pos1 = get_particle_data(pid1).pos(); Utils::Vector3d pos2{box_center, box_center - 0.1, 1.0}; for (int i = 0; i < 10; ++i) { // move particle @@ -292,19 +292,19 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, auto const &p2 = get_particle_data(pid2); auto const &p3 = get_particle_data(pid3); // forces are symmetric - BOOST_CHECK_CLOSE(p1.f.f[0], -p2.f.f[0], tol); - BOOST_CHECK_CLOSE(p3.f.f[1], -p2.f.f[1], tol); + BOOST_CHECK_CLOSE(p1.force()[0], -p2.force()[0], tol); + BOOST_CHECK_CLOSE(p3.force()[1], -p2.force()[1], tol); // periodic image contributions to the electrostatic force are negligible - BOOST_CHECK_LE(std::abs(p1.f.f[1]), tol); - BOOST_CHECK_LE(std::abs(p1.f.f[2]), tol); - BOOST_CHECK_LE(std::abs(p2.f.f[2]), tol); + BOOST_CHECK_LE(std::abs(p1.force()[1]), tol); + BOOST_CHECK_LE(std::abs(p1.force()[2]), tol); + BOOST_CHECK_LE(std::abs(p2.force()[2]), tol); // zero long-range contribution for uncharged particles - BOOST_CHECK_EQUAL(p3.f.f[0], 0.); - BOOST_CHECK_EQUAL(p3.f.f[2], 0.); + BOOST_CHECK_EQUAL(p3.force()[0], 0.); + BOOST_CHECK_EQUAL(p3.force()[2], 0.); // velocities are not propagated - BOOST_CHECK_EQUAL(p1.m.v.norm(), 0.); - BOOST_CHECK_EQUAL(p2.m.v.norm(), 0.); - BOOST_CHECK_EQUAL(p3.m.v.norm(), 0.); + BOOST_CHECK_EQUAL(p1.v().norm(), 0.); + BOOST_CHECK_EQUAL(p2.v().norm(), 0.); + BOOST_CHECK_EQUAL(p3.v().norm(), 0.); // check integrated trajectory; the time step is chosen // small enough so that particles don't travel too far @@ -316,15 +316,15 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, std::unordered_map expected; for (auto pid : pids) { auto p = get_particle_data(pid); - p.m.v += 0.5 * time_step * p.f.f / p.p.mass; - p.r.p += time_step * p.m.v; - expected[pid] = p.r.p; + p.v() += 0.5 * time_step * p.force() / p.p.mass; + p.pos() += time_step * p.v(); + expected[pid] = p.pos(); } mpi_integrate(1, 0); for (auto pid : pids) { auto const &p = get_particle_data(pid); - BOOST_CHECK_LE((p.r.p - expected[pid]).norm(), tol); - assert((p.r.p - pos_com).norm() < 0.5); + BOOST_CHECK_LE((p.pos() - expected[pid]).norm(), tol); + assert((p.pos() - pos_com).norm() < 0.5); } } } diff --git a/src/core/unit_tests/Particle_test.cpp b/src/core/unit_tests/Particle_test.cpp index aeb2594a7bc..af08ba21579 100644 --- a/src/core/unit_tests/Particle_test.cpp +++ b/src/core/unit_tests/Particle_test.cpp @@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE(serialization) { std::vector el = {5, 6, 7, 8}; - p.p.identity = 15; + p.id() = 15; p.bonds().insert({bond_id, bond_partners}); #ifdef EXCLUSIONS p.exclusions() = el; @@ -81,7 +81,7 @@ BOOST_AUTO_TEST_CASE(serialization) { auto q = Particle(); in_ar >> q; - BOOST_CHECK(q.p.identity == p.p.identity); + BOOST_CHECK(q.id() == p.id()); BOOST_CHECK((*q.bonds().begin() == BondView{bond_id, bond_partners})); #ifdef EXCLUSIONS @@ -241,4 +241,38 @@ BOOST_AUTO_TEST_CASE(rattle_constructors) { check_particle_rattle(out, pr); } } -#endif +#endif // BOND_CONSTRAINT + +#ifdef EXTERNAL_FORCES +#ifdef ROTATION +BOOST_AUTO_TEST_CASE(particle_bitfields) { + auto p = Particle(); + + // check default values + BOOST_CHECK(not p.has_fixed_coordinates()); + BOOST_CHECK(not p.can_rotate()); + BOOST_CHECK(not p.is_fixed_along(1)); + BOOST_CHECK(not p.can_rotate_around(1)); + + // check setting of one axis + p.set_fixed_along(1, true); + p.set_can_rotate_around(1, true); + BOOST_CHECK(p.is_fixed_along(1)); + BOOST_CHECK(p.can_rotate_around(1)); + BOOST_CHECK(p.has_fixed_coordinates()); + BOOST_CHECK(p.can_rotate()); + + // check that unsetting is properly registered + p.set_fixed_along(1, false); + p.set_can_rotate_around(1, false); + BOOST_CHECK(not p.has_fixed_coordinates()); + BOOST_CHECK(not p.can_rotate()); + + // check setting of all flags at once + p.set_can_rotate_all_axes(); + BOOST_CHECK(p.can_rotate_around(0)); + BOOST_CHECK(p.can_rotate_around(1)); + BOOST_CHECK(p.can_rotate_around(2)); +} +#endif // ROTATION +#endif // EXTERNAL_FORCES \ No newline at end of file diff --git a/src/core/unit_tests/Verlet_list_test.cpp b/src/core/unit_tests/Verlet_list_test.cpp index 6d96b9ca95a..de340f6f593 100644 --- a/src/core/unit_tests/Verlet_list_test.cpp +++ b/src/core/unit_tests/Verlet_list_test.cpp @@ -124,11 +124,11 @@ struct : public IntegratorHelper { } // namespace Testing inline double get_dist_from_last_verlet_update(Particle const &p) { - return (p.r.p - p.l.p_old).norm(); + return (p.pos() - p.pos_at_last_verlet_update()).norm(); } inline double get_dist_from_pair(Particle const &p1, Particle const &p2) { - return (p1.r.p - p2.r.p).norm(); + return (p1.pos() - p2.pos()).norm(); } auto const node_grids = std::vector{{4, 1, 1}, {2, 2, 1}}; @@ -206,10 +206,10 @@ BOOST_DATA_TEST_CASE_F(ParticleFactory, verlet_list_update, mpi_integrate(1, 0); auto const &p1 = get_particle_data(pid1); auto const &p2 = get_particle_data(pid2); - BOOST_CHECK_CLOSE(p1.f.f[0] - p1.p.ext_force[0], 480., 1e-9); - BOOST_CHECK_CLOSE(p1.f.f[1], 0., tol); - BOOST_CHECK_CLOSE(p1.f.f[2], 0., tol); - BOOST_TEST(p1.f.f - p1.p.ext_force == -p2.f.f, + BOOST_CHECK_CLOSE(p1.force()[0] - p1.ext_force()[0], 480., 1e-9); + BOOST_CHECK_CLOSE(p1.force()[1], 0., tol); + BOOST_CHECK_CLOSE(p1.force()[2], 0., tol); + BOOST_TEST(p1.force() - p1.ext_force() == -p2.force(), boost::test_tools::per_element()); BOOST_CHECK_LT(get_dist_from_last_verlet_update(p1), skin / 2.); } diff --git a/src/core/unit_tests/energy_test.cpp b/src/core/unit_tests/energy_test.cpp index 73552970075..81d34c20357 100644 --- a/src/core/unit_tests/energy_test.cpp +++ b/src/core/unit_tests/energy_test.cpp @@ -31,11 +31,11 @@ BOOST_AUTO_TEST_CASE(translational_kinetic_energy_) { { Particle p; #ifdef MASS - p.p.mass = 2.; + p.mass() = 2.; #endif - p.m.v = {3., 4., 5.}; + p.v() = {3., 4., 5.}; - auto const expected = 0.5 * p.p.mass * p.m.v.norm2(); + auto const expected = 0.5 * p.mass() * p.v().norm2(); BOOST_CHECK_EQUAL(translational_kinetic_energy(p), expected); } @@ -45,10 +45,10 @@ BOOST_AUTO_TEST_CASE(translational_kinetic_energy_) { Particle p; #ifdef MASS - p.p.mass = 2.; + p.mass() = 2.; #endif - p.p.is_virtual = true; - p.m.v = {3., 4., 5.}; + p.set_virtual(true); + p.v() = {3., 4., 5.}; auto const expected = 0.; BOOST_CHECK_EQUAL(translational_kinetic_energy(p), expected); @@ -62,11 +62,11 @@ BOOST_AUTO_TEST_CASE(rotational_kinetic_energy_) { #ifdef ROTATION { Particle p; - p.m.omega = {1., 2., 3.}; - p.p.rotation = 1; + p.omega() = {1., 2., 3.}; + p.set_can_rotate_all_axes(); auto const expected = - 0.5 * (hadamard_product(p.m.omega, p.m.omega) * p.p.rinertia); + 0.5 * (hadamard_product(p.omega(), p.omega()) * p.rinertia()); BOOST_CHECK_EQUAL(rotational_kinetic_energy(p), expected); } #endif @@ -75,13 +75,13 @@ BOOST_AUTO_TEST_CASE(rotational_kinetic_energy_) { BOOST_AUTO_TEST_CASE(kinetic_energy_) { Particle p; #ifdef MASS - p.p.mass = 2.; + p.mass() = 2.; #endif - p.m.v = {3., 4., 5.}; + p.v() = {3., 4., 5.}; #ifdef ROTATION - p.m.omega = {1., 2., 3.}; - p.p.rotation = 1; + p.omega() = {1., 2., 3.}; + p.set_can_rotate_all_axes(); #endif auto const expected = diff --git a/src/core/unit_tests/link_cell_test.cpp b/src/core/unit_tests/link_cell_test.cpp index d64406eac39..1dd2c2c9589 100644 --- a/src/core/unit_tests/link_cell_test.cpp +++ b/src/core/unit_tests/link_cell_test.cpp @@ -50,7 +50,7 @@ BOOST_AUTO_TEST_CASE(link_cell) { c.particles().resize(n_part_per_cell); for (auto &p : c.particles()) { - p.p.identity = id++; + p.id() = id++; } } @@ -59,8 +59,8 @@ BOOST_AUTO_TEST_CASE(link_cell) { Algorithm::link_cell(cells.begin(), cells.end(), [&lc_pairs](Particle const &p1, Particle const &p2) { - if (p1.p.identity <= p2.p.identity) - lc_pairs.emplace_back(p1.p.identity, p2.p.identity); + if (p1.id() <= p2.id()) + lc_pairs.emplace_back(p1.id(), p2.id()); }); BOOST_CHECK(lc_pairs.size() == (n_part * (n_part - 1) / 2)); diff --git a/src/core/unit_tests/thermostats_test.cpp b/src/core/unit_tests/thermostats_test.cpp index 4fac2916ad7..90124f370cc 100644 --- a/src/core/unit_tests/thermostats_test.cpp +++ b/src/core/unit_tests/thermostats_test.cpp @@ -46,10 +46,10 @@ constexpr auto tol = 6 * 100 * std::numeric_limits::epsilon(); Particle particle_factory() { Particle p{}; - p.p.identity = 0; - p.f.f = {1.0, 2.0, 3.0}; + p.id() = 0; + p.force() = {1.0, 2.0, 3.0}; #ifdef ROTATION - p.f.torque = 4.0 * p.f.f; + p.torque() = 4.0 * p.force(); #endif return p; } @@ -72,7 +72,7 @@ BOOST_AUTO_TEST_CASE(test_brownian_dynamics) { constexpr double kT = 3.0; auto const brownian = thermostat_factory(kT); auto const dispersion = - hadamard_division(particle_factory().f.f, brownian.gamma); + hadamard_division(particle_factory().force(), brownian.gamma); /* check translation */ { @@ -120,7 +120,7 @@ BOOST_AUTO_TEST_CASE(test_brownian_dynamics) { #ifdef ROTATION auto const dispersion_rotation = - hadamard_division(particle_factory().f.torque, brownian.gamma_rotation); + hadamard_division(particle_factory().torque(), brownian.gamma_rotation); /* check rotation */ { @@ -185,10 +185,10 @@ BOOST_AUTO_TEST_CASE(test_langevin_dynamics) { /* check translation */ { auto p = particle_factory(); - p.m.v = {1.0, 2.0, 3.0}; + p.v() = {1.0, 2.0, 3.0}; auto const noise = Random::noise_uniform(0, 0, 0); auto const pref = sqrt(prefactor_squared * langevin.gamma); - auto const ref = hadamard_product(-langevin.gamma, p.m.v) + + auto const ref = hadamard_product(-langevin.gamma, p.v()) + hadamard_product(pref, noise); auto const out = friction_thermo_langevin(langevin, p, time_step, kT); BOOST_CHECK_CLOSE(out[0], ref[0], tol); @@ -200,10 +200,10 @@ BOOST_AUTO_TEST_CASE(test_langevin_dynamics) { /* check rotation */ { auto p = particle_factory(); - p.m.omega = {1.0, 2.0, 3.0}; + p.omega() = {1.0, 2.0, 3.0}; auto const noise = Random::noise_uniform(0, 0, 0); auto const pref = sqrt(prefactor_squared * langevin.gamma_rotation); - auto const ref = hadamard_product(-langevin.gamma_rotation, p.m.omega) + + auto const ref = hadamard_product(-langevin.gamma_rotation, p.omega()) + hadamard_product(pref, noise); auto const out = friction_thermo_langevin_rotation(langevin, p, time_step, kT); @@ -221,8 +221,8 @@ BOOST_AUTO_TEST_CASE(test_noise_statistics) { auto thermostat = thermostat_factory(kT, time_step); auto p1 = particle_factory(); auto p2 = particle_factory(); - p1.p.identity = 0; - p2.p.identity = 1; + p1.id() = 0; + p2.id() = 1; auto const correlation = std::get<3>(noise_statistics( [&p1, &p2, &thermostat]() -> std::array { @@ -254,7 +254,7 @@ BOOST_AUTO_TEST_CASE(test_brownian_randomness) { auto thermostat = thermostat_factory(kT); auto p = particle_factory(); #ifdef ROTATION - p.p.rotation = ROTATION_X | ROTATION_Y | ROTATION_Z; + p.set_can_rotate_all_axes(); constexpr std::size_t N = 4; #else constexpr std::size_t N = 2; @@ -328,8 +328,8 @@ BOOST_AUTO_TEST_CASE(test_npt_iso_randomness) { [&p, &thermostat]() -> std::array { thermostat.rng_increment(); return {{ - friction_therm0_nptiso<1>(thermostat, p.m.v, 0), - friction_therm0_nptiso<2>(thermostat, p.m.v, 0), + friction_therm0_nptiso<1>(thermostat, p.v(), 0), + friction_therm0_nptiso<2>(thermostat, p.v(), 0), friction_thermV_nptiso(thermostat, 1.5), }}; }, diff --git a/src/core/virtual_sites.cpp b/src/core/virtual_sites.cpp index 73061e84abe..66325a2c19c 100644 --- a/src/core/virtual_sites.cpp +++ b/src/core/virtual_sites.cpp @@ -58,7 +58,7 @@ inline std::tuple, double> calculate_vs_relate_to_params(Particle const &p_current, Particle const &p_relate_to) { // get the distance between the particles - Utils::Vector3d d = box_geo.get_mi_vector(p_current.r.p, p_relate_to.r.p); + Utils::Vector3d d = box_geo.get_mi_vector(p_current.pos(), p_relate_to.pos()); // Check if the distance between virtual and non-virtual particles is larger // than minimum global cutoff. If so, warn user. @@ -99,24 +99,25 @@ calculate_vs_relate_to_params(Particle const &p_current, Utils::convert_director_to_quaternion(d); // Define quaternion as described above + auto relate_to_quat = p_relate_to.quat(); quat = Utils::Quaternion{ - {{{Utils::dot(p_relate_to.r.quat, quat_director), - -quat_director[0] * p_relate_to.r.quat[1] + - quat_director[1] * p_relate_to.r.quat[0] + - quat_director[2] * p_relate_to.r.quat[3] - - quat_director[3] * p_relate_to.r.quat[2], - p_relate_to.r.quat[1] * quat_director[3] + - p_relate_to.r.quat[0] * quat_director[2] - - p_relate_to.r.quat[3] * quat_director[1] - - p_relate_to.r.quat[2] * quat_director[0], - quat_director[3] * p_relate_to.r.quat[0] - - p_relate_to.r.quat[3] * quat_director[0] + - p_relate_to.r.quat[2] * quat_director[1] - - p_relate_to.r.quat[1] * quat_director[2]}}}}; - quat /= p_relate_to.r.quat.norm2(); + {{{Utils::dot(relate_to_quat, quat_director), + -quat_director[0] * relate_to_quat[1] + + quat_director[1] * relate_to_quat[0] + + quat_director[2] * relate_to_quat[3] - + quat_director[3] * relate_to_quat[2], + relate_to_quat[1] * quat_director[3] + + relate_to_quat[0] * quat_director[2] - + relate_to_quat[3] * quat_director[1] - + relate_to_quat[2] * quat_director[0], + quat_director[3] * relate_to_quat[0] - + relate_to_quat[3] * quat_director[0] + + relate_to_quat[2] * quat_director[1] - + relate_to_quat[1] * quat_director[2]}}}}; + quat /= relate_to_quat.norm2(); // Verify result - Utils::Quaternion qtemp = p_relate_to.r.quat * quat; + Utils::Quaternion qtemp = relate_to_quat * quat; for (int i = 0; i < 4; i++) if (fabs(qtemp[i] - quat_director[i]) > 1E-9) fprintf(stderr, "vs_relate_to: component %d: %f instead of %f\n", i, @@ -128,9 +129,9 @@ calculate_vs_relate_to_params(Particle const &p_current, void local_vs_relate_to(Particle &p_current, Particle const &p_relate_to) { // Set the particle id of the particle we want to relate to, the distance // and the relative orientation - p_current.p.vs_relative.to_particle_id = p_relate_to.identity(); - std::tie(p_current.p.vs_relative.rel_orientation, - p_current.p.vs_relative.distance) = + p_current.vs_relative().to_particle_id = p_relate_to.identity(); + std::tie(p_current.vs_relative().rel_orientation, + p_current.vs_relative().distance) = calculate_vs_relate_to_params(p_current, p_relate_to); } diff --git a/src/core/virtual_sites/VirtualSites.hpp b/src/core/virtual_sites/VirtualSites.hpp index 3b6e80a14ab..28a79c9f2de 100644 --- a/src/core/virtual_sites/VirtualSites.hpp +++ b/src/core/virtual_sites/VirtualSites.hpp @@ -59,7 +59,7 @@ class VirtualSites { void set_have_quaternion(const bool &have_quaternion) { m_have_quaternion = have_quaternion; } - bool get_have_quaternion() const { return m_have_quaternion; } + bool have_quaternions() const { return m_have_quaternion; } private: bool m_have_quaternion = false; diff --git a/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp b/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp index fcd2396c148..a19dfae3e14 100644 --- a/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp +++ b/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp @@ -41,7 +41,7 @@ void VirtualSitesInertialessTracers::after_force_calc() { #endif if (std::any_of(cell_structure.local_particles().begin(), cell_structure.local_particles().end(), - [](Particle &p) { return p.p.is_virtual; })) { + [](Particle &p) { return p.is_virtual(); })) { runtimeErrorMsg() << "Inertialess Tracers: No LB method was active but " "virtual sites present."; return; diff --git a/src/core/virtual_sites/VirtualSitesRelative.cpp b/src/core/virtual_sites/VirtualSitesRelative.cpp index 85f0bc1ca55..b8bd7e766d1 100644 --- a/src/core/virtual_sites/VirtualSitesRelative.cpp +++ b/src/core/virtual_sites/VirtualSitesRelative.cpp @@ -30,7 +30,6 @@ #include #include -#include #include #include @@ -44,9 +43,9 @@ namespace { * @return Orientation quaternion of the virtual site. */ Utils::Quaternion -orientation(Particle const *p_ref, +orientation(const Particle &p_ref, const ParticleProperties::VirtualSitesRelativeParameters &vs_rel) { - return p_ref->r.quat * vs_rel.quat; + return p_ref.quat() * vs_rel.quat; } /** @@ -57,7 +56,7 @@ orientation(Particle const *p_ref, */ inline Utils::Vector3d connection_vector( - Particle const *p_ref, + Particle const &p_ref, const ParticleProperties::VirtualSitesRelativeParameters &vs_rel) { // Calculate the quaternion defining the orientation of the vector connecting // the virtual site and the real particle @@ -65,7 +64,7 @@ inline Utils::Vector3d connection_vector( // of the real particle with the quaternion of the virtual particle, which // specifies the relative orientation. auto const director = Utils::convert_quaternion_to_director( - p_ref->r.quat * vs_rel.rel_orientation) + p_ref.quat() * vs_rel.rel_orientation) .normalize(); return vs_rel.distance * director; @@ -78,9 +77,9 @@ inline Utils::Vector3d connection_vector( * @return Position of the virtual site. */ Utils::Vector3d -position(Particle const *p_ref, +position(Particle const &p_ref, const ParticleProperties::VirtualSitesRelativeParameters &vs_rel) { - return p_ref->r.p + connection_vector(p_ref, vs_rel); + return p_ref.pos() + connection_vector(p_ref, vs_rel); } /** @@ -90,16 +89,16 @@ position(Particle const *p_ref, * @return Velocity of the virtual site. */ Utils::Vector3d -velocity(const Particle *p_ref, +velocity(const Particle &p_ref, const ParticleProperties::VirtualSitesRelativeParameters &vs_rel) { auto const d = connection_vector(p_ref, vs_rel); // Get omega of real particle in space-fixed frame auto const omega_space_frame = - convert_vector_body_to_space(*p_ref, p_ref->m.omega); + convert_vector_body_to_space(p_ref, p_ref.omega()); // Obtain velocity from v=v_real particle + omega_real_particle \times // director - return vector_product(omega_space_frame, d) + p_ref->m.v; + return vector_product(omega_space_frame, d) + p_ref.v(); } /** @@ -118,19 +117,6 @@ Particle *get_reference_particle( return p_ref; } -/** - * @brief Constraint force on the real particle. - * - * Calculates the force exerted by the constraint on the - * reference particle. - */ -ParticleForce constraint_force( - const ParticleForce &f, const Particle *p_ref, - const ParticleProperties::VirtualSitesRelativeParameters &vs_rel) { - return {f.f, - vector_product(connection_vector(p_ref, vs_rel), f.f) + f.torque}; -} - /** * @brief Constraint force to hold the particles at its prescribed position. * @@ -140,7 +126,7 @@ ParticleForce constraint_force( * @return Constraint force. */ auto constraint_stress( - const Utils::Vector3d &f, const Particle *p_ref, + const Utils::Vector3d &f, const Particle &p_ref, const ParticleProperties::VirtualSitesRelativeParameters &vs_rel) { /* The constraint force is minus the force on the particle, make it force * free. The counter force is translated by the connection vector to the @@ -155,21 +141,21 @@ void VirtualSitesRelative::update() const { auto const particles = cell_structure.local_particles(); for (auto &p : particles) { - if (!p.p.is_virtual) + if (!p.is_virtual()) continue; - const Particle *p_ref = get_reference_particle(p.p.vs_relative); + const Particle *p_ref = get_reference_particle(p.vs_relative()); - auto const new_pos = position(p_ref, p.p.vs_relative); + auto const new_pos = position(*p_ref, p.vs_relative()); /* The shift has to respect periodic boundaries: if the reference - * particles is not in the same image box, we potentially avoid to shift + * particles is not in the same image box, we potentially avoid shifting * to the other side of the box. */ - p.r.p += box_geo.get_mi_vector(new_pos, p.r.p); + p.pos() += box_geo.get_mi_vector(new_pos, p.pos()); - p.m.v = velocity(p_ref, p.p.vs_relative); + p.v() = velocity(*p_ref, p.vs_relative()); - if (get_have_quaternion()) - p.r.quat = orientation(p_ref, p.p.vs_relative); + if (have_quaternions()) + p.quat() = p_ref->quat() * p.vs_relative().quat; } if (cell_structure.check_resort_required(particles, skin)) { @@ -187,12 +173,16 @@ void VirtualSitesRelative::back_transfer_forces_and_torques() const { // Iterate over all the particles in the local cells for (auto &p : cell_structure.local_particles()) { // We only care about virtual particles - if (p.p.is_virtual) { + if (p.is_virtual()) { // First obtain the real particle responsible for this virtual particle: - Particle *p_ref = get_reference_particle(p.p.vs_relative); + Particle *p_ref = get_reference_particle(p.vs_relative()); // Add forces and torques - p_ref->f += constraint_force(p.f, p_ref, p.p.vs_relative); + p_ref->force() += p.force(); + p_ref->torque() += + vector_product(connection_vector(*p_ref, p.vs_relative()), + p.force()) + + p.torque(); } } } @@ -202,13 +192,13 @@ Utils::Matrix VirtualSitesRelative::pressure_tensor() const { Utils::Matrix pressure_tensor = {}; for (auto &p : cell_structure.local_particles()) { - if (!p.p.is_virtual) + if (!p.is_virtual()) continue; // First obtain the real particle responsible for this virtual particle: - const Particle *p_ref = get_reference_particle(p.p.vs_relative); + const Particle *p_ref = get_reference_particle(p.vs_relative()); - pressure_tensor += constraint_stress(p.f.f, p_ref, p.p.vs_relative); + pressure_tensor += constraint_stress(p.force(), *p_ref, p.vs_relative()); } return pressure_tensor; diff --git a/src/core/virtual_sites/lb_inertialess_tracers.cpp b/src/core/virtual_sites/lb_inertialess_tracers.cpp index a790e43aa65..3a171dc9c75 100644 --- a/src/core/virtual_sites/lb_inertialess_tracers.cpp +++ b/src/core/virtual_sites/lb_inertialess_tracers.cpp @@ -72,8 +72,8 @@ void IBM_ForcesIntoFluid_CPU() { for (auto &p : cell_structure.ghost_particles()) { // for ghost particles we have to check if they lie // in the range of the local lattice nodes - if (in_local_domain(p.r.p)) { - if (p.p.is_virtual) + if (in_local_domain(p.pos())) { + if (p.is_virtual()) CoupleIBMParticleToFluid(p); } } @@ -95,19 +95,13 @@ void IBM_UpdateParticlePositions(ParticleRange const &particles, // Euler integrator for (auto &p : particles) { - if (p.p.is_virtual) { + if (p.is_virtual()) { + for (int axis = 0; axis < 2; axis++) { #ifdef EXTERNAL_FORCES - if (!(p.p.ext_flag & 2)) + if (not p.is_fixed_along(axis)) #endif - p.r.p[0] += p.m.v[0] * time_step; -#ifdef EXTERNAL_FORCES - if (!(p.p.ext_flag & 4)) -#endif - p.r.p[1] += p.m.v[1] * time_step; -#ifdef EXTERNAL_FORCES - if (!(p.p.ext_flag & 8)) -#endif - p.r.p[2] += p.m.v[2] * time_step; + p.pos()[axis] += p.v()[axis] * time_step; + } } } @@ -119,12 +113,12 @@ void IBM_UpdateParticlePositions(ParticleRange const &particles, /** Put the momentum of a given particle into the LB fluid. */ void CoupleIBMParticleToFluid(Particle const &p) { // Convert units from MD to LB - auto const delta_j = p.f.f * Utils::int_pow<4>(lbpar.tau) / lbpar.agrid; + auto const delta_j = p.force() * Utils::int_pow<4>(lbpar.tau) / lbpar.agrid; // Get indices and weights of affected nodes using discrete delta function Utils::Vector node_index{}; Utils::Vector6d delta{}; - lblattice.map_position_to_lattice(p.r.p, node_index, delta); + lblattice.map_position_to_lattice(p.pos(), node_index, delta); // Loop over all affected nodes for (int z = 0; z < 2; z++) { @@ -256,10 +250,10 @@ void ParticleVelocitiesFromLB_CPU() { // Here all contributions are included: velocity, external force and // particle force. for (auto &p : cell_structure.local_particles()) { - if (p.p.is_virtual) { + if (p.is_virtual()) { // Get interpolated velocity and store in the force (!) field // for later communication (see below) - p.f.f = GetIBMInterpolatedVelocity(p.r.p); + p.force() = GetIBMInterpolatedVelocity(p.pos()); } } @@ -269,19 +263,19 @@ void ParticleVelocitiesFromLB_CPU() { // This criterion include the halo on the left, but excludes the halo on // the right // Try if we have to use *1.5 on the right - if (in_local_domain(p.r.p)) { - if (p.p.is_virtual) { + if (in_local_domain(p.pos())) { + if (p.is_virtual()) { // The force stemming from the ghost particle (for // communication, see below) - p.f.f = GetIBMInterpolatedVelocity(p.r.p); + p.force() = GetIBMInterpolatedVelocity(p.pos()); } else { // Reset, necessary because we add all forces below. Also needs to // be done for real particles! - p.f.f = {}; + p.force() = {}; } } else { // Reset, necessary because we add all forces below - p.f.f = {}; + p.force() = {}; } } @@ -299,8 +293,8 @@ void ParticleVelocitiesFromLB_CPU() { // Transfer to velocity field for (auto &p : cell_structure.local_particles()) { - if (p.p.is_virtual) { - p.m.v = p.f.f; + if (p.is_virtual()) { + p.v() = p.force(); } } } diff --git a/src/core/virtual_sites/lb_inertialess_tracers_cuda_interface.cpp b/src/core/virtual_sites/lb_inertialess_tracers_cuda_interface.cpp index a6599f01cda..5d5ea3b4f1f 100644 --- a/src/core/virtual_sites/lb_inertialess_tracers_cuda_interface.cpp +++ b/src/core/virtual_sites/lb_inertialess_tracers_cuda_interface.cpp @@ -46,17 +46,17 @@ static void pack_particles(ParticleRange const &particles, int i = 0; for (auto const &part : particles) { - auto const pos = folded_position(part.r.p, box_geo); + auto const pos = folded_position(part.pos(), box_geo); buffer[i].pos[0] = static_cast(pos[0]); buffer[i].pos[1] = static_cast(pos[1]); buffer[i].pos[2] = static_cast(pos[2]); - buffer[i].f[0] = static_cast(part.f.f[0]); - buffer[i].f[1] = static_cast(part.f.f[1]); - buffer[i].f[2] = static_cast(part.f.f[2]); + buffer[i].f[0] = static_cast(part.force()[0]); + buffer[i].f[1] = static_cast(part.force()[1]); + buffer[i].f[2] = static_cast(part.force()[2]); - buffer[i].is_virtual = part.p.is_virtual; + buffer[i].is_virtual = part.is_virtual(); i++; } @@ -89,9 +89,9 @@ static void set_velocities(ParticleRange const &particles, std::vector &buffer) { int i = 0; for (auto &part : particles) { - if (part.p.is_virtual) { + if (part.is_virtual()) { for (int j = 0; j < 3; j++) - part.m.v[j] = static_cast(buffer[i].v[j]); + part.v()[j] = static_cast(buffer[i].v[j]); } i++; } diff --git a/src/script_interface/virtual_sites/VirtualSites.hpp b/src/script_interface/virtual_sites/VirtualSites.hpp index db22134ebab..3d5ae5a6cde 100644 --- a/src/script_interface/virtual_sites/VirtualSites.hpp +++ b/src/script_interface/virtual_sites/VirtualSites.hpp @@ -43,7 +43,7 @@ class VirtualSites : public AutoParameters { [this](const Variant &v) { virtual_sites()->set_have_quaternion(get_value(v)); }, - [this]() { return virtual_sites()->get_have_quaternion(); }}}); + [this]() { return virtual_sites()->have_quaternions(); }}}); } /** Vs implementation we are wrapping */ virtual std::shared_ptr<::VirtualSites> virtual_sites() = 0; From 7d9b1acfefb8ede0ac7496c3238d8a91fb166b23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 7 Feb 2022 20:21:50 +0100 Subject: [PATCH 46/99] core: Make MPI-IO exceptions testable --- src/core/io/mpiio/mpiio.cpp | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/core/io/mpiio/mpiio.cpp b/src/core/io/mpiio/mpiio.cpp index 35d79e3751c..9fc6ef68f33 100644 --- a/src/core/io/mpiio/mpiio.cpp +++ b/src/core/io/mpiio/mpiio.cpp @@ -77,6 +77,20 @@ namespace Mpiio { +/** + * @brief Fatal error handler. + * On 1 MPI rank the error is recoverable and an exception is thrown. + * On more than 1 MPI rank the error is not recoverable. + */ +static void fatal_error() { + int size; + MPI_Comm_size(MPI_COMM_WORLD, &size); + if (size == 1) { + throw std::runtime_error(""); + } + errexit(); +} + /** Dumps arr of size len starting from prefix pref of type T using * MPI_T as MPI datatype. Beware, that T and MPI_T have to match! * @@ -103,7 +117,7 @@ static void mpiio_dump_array(const std::string &fn, T *arr, std::size_t len, buf[buf_len] = '\0'; fprintf(stderr, "MPI-IO Error: Could not open file \"%s\": %s\n", fn.c_str(), buf); - errexit(); + fatal_error(); } ret = MPI_File_set_view(f, pref * sizeof(T), MPI_T, MPI_T, const_cast("native"), MPI_INFO_NULL); @@ -111,7 +125,7 @@ static void mpiio_dump_array(const std::string &fn, T *arr, std::size_t len, MPI_File_close(&f); if (ret) { fprintf(stderr, "MPI-IO Error: Could not write file \"%s\".\n", fn.c_str()); - errexit(); + fatal_error(); } } @@ -127,7 +141,7 @@ static void dump_info(const std::string &fn, unsigned fields) { if (!f) { fprintf(stderr, "MPI-IO Error: Could not open %s for writing.\n", fn.c_str()); - errexit(); + fatal_error(); } static std::vector npartners; int success = (fwrite(&fields, sizeof(fields), 1, f) == 1); @@ -148,7 +162,7 @@ static void dump_info(const std::string &fn, unsigned fields) { fclose(f); if (!success) { fprintf(stderr, "MPI-IO Error: Failed to write %s.\n", fn.c_str()); - errexit(); + fatal_error(); } } @@ -255,7 +269,7 @@ static int get_num_elem(const std::string &fn, std::size_t elem_sz) { if (stat(fn.c_str(), &st) != 0) { fprintf(stderr, "MPI-IO Input Error: Could not get file size of %s: %s\n", fn.c_str(), strerror(errno)); - errexit(); + fatal_error(); } return static_cast(st.st_size / elem_sz); } @@ -280,7 +294,7 @@ static void mpiio_read_array(const std::string &fn, T *arr, std::size_t len, buf[buf_len] = '\0'; fprintf(stderr, "MPI-IO Error: Could not open file \"%s\": %s\n", fn.c_str(), buf); - errexit(); + fatal_error(); } ret = MPI_File_set_view(f, pref * sizeof(T), MPI_T, MPI_T, const_cast("native"), MPI_INFO_NULL); @@ -289,7 +303,7 @@ static void mpiio_read_array(const std::string &fn, T *arr, std::size_t len, MPI_File_close(&f); if (ret) { fprintf(stderr, "MPI-IO Error: Could not read file \"%s\".\n", fn.c_str()); - errexit(); + fatal_error(); } } @@ -305,11 +319,11 @@ static void read_head(const std::string &fn, int rank, unsigned *fields) { if (rank == 0) { if (!(f = fopen(fn.c_str(), "rb"))) { fprintf(stderr, "MPI-IO: Could not open %s.head.\n", fn.c_str()); - errexit(); + fatal_error(); } if (fread((void *)fields, sizeof(unsigned), 1, f) != 1) { fprintf(stderr, "MPI-IO: Read on %s.head failed.\n", fn.c_str()); - errexit(); + fatal_error(); } MPI_Bcast(fields, 1, MPI_UNSIGNED, 0, MPI_COMM_WORLD); fclose(f); @@ -355,7 +369,7 @@ void mpi_mpiio_common_read(const char *filename, unsigned fields) { if (rank == 0 && nproc != size) { fprintf(stderr, "MPI-IO Error: Trying to read a file with a different COMM " "size than at point of writing.\n"); - errexit(); + fatal_error(); } // 1.head on head node: @@ -366,7 +380,7 @@ void mpi_mpiio_common_read(const char *filename, unsigned fields) { if (rank == 0 && (fields & avail_fields) != fields) { fprintf(stderr, "MPI-IO Error: Requesting to read fields which were not dumped.\n"); - errexit(); + fatal_error(); } // 1.pref on all nodes: From 6c717e9bbedb52342af807248e6c59161327052f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 7 Feb 2022 21:08:18 +0100 Subject: [PATCH 47/99] tests: Extend MPI-IO test case Check multiple uses cases. Write files to a temporary directory to guarantee test files are always cleaned up and that two MPI-IO tests can safely run in parallel. Use a unique prefix derived from the test name to avoid filename collisions (which are fatal errors in MPI-IO). Check the MPI-IO exception mechanism on 1 MPI rank. --- testsuite/python/CMakeLists.txt | 1 + testsuite/python/mpiio.py | 125 +++++++++++++++++------- testsuite/python/mpiio_exceptions.py | 141 +++++++++++++++++++++++++++ 3 files changed, 232 insertions(+), 35 deletions(-) create mode 100644 testsuite/python/mpiio_exceptions.py diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index aa80da33578..32c1b6ce471 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -220,6 +220,7 @@ python_test(FILE sigint.py DEPENDENCIES sigint_child.py MAX_NUM_PROC 1) python_test(FILE lb_density.py MAX_NUM_PROC 1) python_test(FILE observable_chain.py MAX_NUM_PROC 4) python_test(FILE mpiio.py MAX_NUM_PROC 4) +python_test(FILE mpiio_exceptions.py MAX_NUM_PROC 1) python_test(FILE gpu_availability.py MAX_NUM_PROC 2 LABELS gpu) python_test(FILE features.py MAX_NUM_PROC 1) python_test(FILE decorators.py MAX_NUM_PROC 1) diff --git a/testsuite/python/mpiio.py b/testsuite/python/mpiio.py index 1a2b1f34d1a..9e09a8b20b4 100644 --- a/testsuite/python/mpiio.py +++ b/testsuite/python/mpiio.py @@ -29,16 +29,13 @@ import random import os import dataclasses +import tempfile # Number of particles npart = 10 # Number of different bond types nbonds = 100 -filename = "testdata.mpiio" -exts = ["head", "pref", "id", "type", "pos", "vel", "boff", "bond"] -filenames = [filename + "." + ext for ext in exts] - @dataclasses.dataclass class MockParticle: @@ -49,12 +46,6 @@ class MockParticle: bonds: list -def clean_files(): - for f in filenames: - if os.path.isfile(f): - os.remove(f) - - def randint_different_from(a, b, n): """Returns a random integer in [a, b) that is not n.""" r = n @@ -100,50 +91,114 @@ class MPIIOTest(ut.TestCase): bend=i, phi0=i) test_mock_particles = get_random_mock_particles() - def setUp(self): + @classmethod + def setUpClass(cls): + cls.temp_dir = tempfile.TemporaryDirectory() + + @classmethod + def tearDownClass(cls): + cls.temp_dir.cleanup() + + def add_particles(self): """Sets up a system from test_mock_particles and prepares environment for the tests.""" - clean_files() # Prior call might not have completed successfully for p in self.test_mock_particles: self.system.part.add(id=p.id, type=p.type, pos=p.pos, v=p.v) for b in p.bonds: self.system.part.by_id(p.id).add_bond(b) def tearDown(self): - clean_files() - - def check_files_exist(self): + self.system.part.clear() + + def generate_prefix(self, test_id): + return os.path.join(self.temp_dir.name, test_id.rsplit('.')[-1]) + + def build_list_of_expected_files(self, prefix, **fields): + exts = {'head', 'pref', 'id'} + if fields.get('types', False): + exts.add('type') + if fields.get('positions', False): + exts.add('pos') + if fields.get('velocities', False): + exts.add('vel') + if fields.get('bonds', False): + exts.add('boff') + exts.add('bond') + return {f'{prefix}.{ext}' for ext in exts} + + def check_files_exist(self, prefix, **fields): """Checks if all necessary files have been written.""" - for fn in filenames: - self.assertTrue(os.path.isfile(fn)) + filepaths = self.build_list_of_expected_files(prefix, **fields) + for filepath in filepaths: + self.assertTrue(os.path.isfile(filepath)) - def check_sample_system(self): - """Checks the particles in the ESPResSo system "self.s" against the - true values in "self.test_particles".""" + def check_sample_system(self, **fields): + """Checks particles in the system against the reference values.""" + self.assertEqual(len(self.system.part), len(self.test_mock_particles)) for p, q in zip(self.system.part, self.test_mock_particles): + ref_t = q.type if fields.get('types', False) else 0 + ref_p = q.pos if fields.get('positions', False) else [0., 0., 0.] + ref_v = q.v if fields.get('velocities', False) else [0., 0., 0.] + ref_b = q.bonds if fields.get('bonds', False) else [] self.assertEqual(p.id, q.id) - self.assertEqual(p.type, q.type) - np.testing.assert_array_equal(np.copy(p.pos), q.pos) - np.testing.assert_array_equal(np.copy(p.v), q.v) - self.assertEqual(len(p.bonds), len(q.bonds)) + self.assertEqual(p.type, ref_t) + np.testing.assert_array_equal(np.copy(p.pos), ref_p) + np.testing.assert_array_equal(np.copy(p.v), ref_v) + self.assertEqual(len(p.bonds), len(ref_b)) # Check all bonds - for bp, bq in zip(p.bonds, q.bonds): + for bp, bq in zip(p.bonds, ref_b): # Bond type - "bend" stores the index of the bond self.assertEqual(bp[0].params["bend"], bq[0]) # Bond partners np.testing.assert_array_equal(bp[1:], bq[1:]) def test_mpiio(self): - espressomd.io.mpiio.mpiio.write( - filename, types=True, positions=True, velocities=True, bonds=True) - - self.check_files_exist() - - self.system.part.clear() # Clear to be on the safe side - espressomd.io.mpiio.mpiio.read( - filename, types=True, positions=True, velocities=True, bonds=True) - - self.check_sample_system() + fields = { + 'types': True, + 'positions': True, + 'velocities': True, + 'bonds': True} + prefix = self.generate_prefix(self.id()) + mpiio = espressomd.io.mpiio.mpiio + + self.add_particles() + mpiio.write(prefix, **fields) + self.check_files_exist(prefix, **fields) + + self.system.part.clear() + mpiio.read(prefix, **fields) + self.check_sample_system(**fields) + + def test_mpiio_without_positions(self): + prefix = self.generate_prefix(self.id()) + mpiio = espressomd.io.mpiio.mpiio + self.add_particles() + mpiio.write(prefix, types=True, positions=False) + self.system.part.clear() + mpiio.read(prefix, types=True, positions=False) + self.check_sample_system(types=True, positions=False) + + def test_mpiio_without_types(self): + prefix = self.generate_prefix(self.id()) + mpiio = espressomd.io.mpiio.mpiio + self.add_particles() + mpiio.write(prefix, types=False, positions=True) + self.system.part.clear() + mpiio.read(prefix, types=False, positions=True) + self.check_sample_system(types=False, positions=True) + + def test_mpiio_exceptions(self): + mpiio = espressomd.io.mpiio.mpiio + prefix = self.generate_prefix(self.id()) + msg_prefix = "Need to supply output prefix via the 'prefix' argument." + with self.assertRaisesRegex(ValueError, msg_prefix): + mpiio.write(None, positions=True) + with self.assertRaisesRegex(ValueError, msg_prefix): + mpiio.read(None, positions=True) + with self.assertRaisesRegex(ValueError, "No output fields chosen."): + mpiio.write(prefix) + with self.assertRaisesRegex(ValueError, "No output fields chosen."): + mpiio.read(prefix) if __name__ == '__main__': diff --git a/testsuite/python/mpiio_exceptions.py b/testsuite/python/mpiio_exceptions.py new file mode 100644 index 00000000000..5d7a2e5bbec --- /dev/null +++ b/testsuite/python/mpiio_exceptions.py @@ -0,0 +1,141 @@ +# +# Copyright (C) 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 . +# + +import espressomd +import espressomd.io + +import unittest as ut +import os +import tempfile + + +class MPIIOMockGenerator: + """Mock MPI-IO files.""" + + def __init__(self, root): + self.root = root + self.counter = 0 + + def _create_file(self, fn, mode, content=b''): + with open(os.open(fn, os.O_CREAT | os.O_WRONLY, mode), 'wb') as f: + f.write(content) + + def create(self, *suffixes, read_only=True, from_ref=None): + mode = 0o444 if read_only else 0o777 + filepath = os.path.join(self.root, f'testdata.mpiio.{self.counter}') + filepath_derived = [] + for suffix in suffixes: + derived = f'{filepath}.{suffix}' + content = b'' + if from_ref is not None: + with open(f'{from_ref}.{suffix}', 'rb') as f: + content = f.read() + self._create_file(derived, mode, content) + filepath_derived.append(derived) + if len(filepath_derived) == 1: + filepath_derived = filepath_derived[0] + self.counter += 1 + return filepath, filepath_derived + + +class MPIIOTest(ut.TestCase): + + """ + Test class for the MPI-IO core functionality. + Check for exceptions when data cannot be read or written. + With 1 MPI rank, fatal errors are just exceptions. + """ + system = espressomd.system.System(box_l=[1, 1, 1]) + n_nodes = system.cell_system.get_state()["n_nodes"] + + @classmethod + def setUpClass(cls): + cls.temp_dir = tempfile.TemporaryDirectory() + + @classmethod + def tearDownClass(cls): + cls.temp_dir.cleanup() + + @ut.skipIf(n_nodes != 1, "only works on 1 MPI rank") + def test_exceptions(self): + generator = MPIIOMockGenerator(self.temp_dir.name) + mpiio = espressomd.io.mpiio.mpiio + + # generate reference data + self.system.part.add(pos=[0, 0, 0]) + path_ref = generator.create()[0] + mpiio.write(path_ref, types=True) + self.system.part.clear() + + # check reference data is valid + mpiio.read(path_ref, types=True) + self.system.part.clear() + + # exception when the metadata cannot be written + path, fn = generator.create('head', read_only=True) + with self.assertRaises(RuntimeError): + mpiio.write(path, types=True) + + # exception when the payload cannot be written + path, fn = generator.create('pref', read_only=True) + with self.assertRaises(RuntimeError): + mpiio.write(path, types=True) + + # exception when calculating the size of a non-existent file + path, _ = generator.create(read_only=True) + fn = f'{path}.pref' + with self.assertRaises(RuntimeError): + mpiio.read(path, types=True) + + # exception when the MPI world size differs for reading and writing + # (empty .pref file -> data was written with MPI world size of 0) + path, _ = generator.create('id', 'pref', read_only=True) + with self.assertRaises(RuntimeError): + mpiio.read(path, types=True) + + # exception when the particle types don't exist + path, _ = generator.create( + 'pref', 'id', 'head', read_only=False, from_ref=path_ref) + fn = f'{path}.type' + with self.assertRaises(RuntimeError): + mpiio.read(path, types=True) + + # exception when the metadata doesn't exist + path, _ = generator.create( + 'id', 'pref', read_only=False, from_ref=path_ref) + fn = f'{path}.head' + with self.assertRaises(RuntimeError): + mpiio.read(path, types=True) + + # exception when the metadata is empty + with open(fn, 'wb'): + pass + with self.assertRaises(RuntimeError): + mpiio.read(path, types=True) + + # exception when reading data that was not written to disk + # (empty .pref file -> data was written with MPI world size of 0) + path, _ = generator.create( + 'id', 'pref', 'head', read_only=False, from_ref=path_ref) + with self.assertRaises(RuntimeError): + mpiio.read(path, types=True, bonds=True) + + +if __name__ == '__main__': + ut.main() From aad1f5430a3a357bcda655c07c34eb3848dd06e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 8 Feb 2022 22:28:47 +0100 Subject: [PATCH 48/99] core: Refactor MPI-IO Return by value rather than by output argument, use std::string instead of char pointers, remove macro. --- src/core/io/mpiio/mpiio.cpp | 96 ++++++++++++------------- src/core/io/mpiio/mpiio.hpp | 10 +-- src/script_interface/mpiio/si_mpiio.hpp | 18 +++-- 3 files changed, 61 insertions(+), 63 deletions(-) diff --git a/src/core/io/mpiio/mpiio.cpp b/src/core/io/mpiio/mpiio.cpp index 9fc6ef68f33..b84de9a2fac 100644 --- a/src/core/io/mpiio/mpiio.cpp +++ b/src/core/io/mpiio/mpiio.cpp @@ -65,13 +65,13 @@ #include -#include #include #include #include #include #include #include +#include #include #include @@ -166,10 +166,9 @@ static void dump_info(const std::string &fn, unsigned fields) { } } -void mpi_mpiio_common_write(const char *filename, unsigned fields, +void mpi_mpiio_common_write(const std::string &prefix, unsigned fields, const ParticleRange &particles) { - std::string fnam(filename); - int const nlocalpart = static_cast(particles.size()); + auto const nlocalpart = static_cast(particles.size()); // Keep static buffers in order not having to allocate them on every // function call static std::vector pos, vel; @@ -215,17 +214,17 @@ void mpi_mpiio_common_write(const char *filename, unsigned fields, int rank; MPI_Comm_rank(MPI_COMM_WORLD, &rank); if (rank == 0) - dump_info(fnam + ".head", fields); - mpiio_dump_array(fnam + ".pref", &pref, 1, rank, MPI_INT); - mpiio_dump_array(fnam + ".id", id.data(), nlocalpart, pref, MPI_INT); + dump_info(prefix + ".head", fields); + mpiio_dump_array(prefix + ".pref", &pref, 1, rank, MPI_INT); + mpiio_dump_array(prefix + ".id", id.data(), nlocalpart, pref, MPI_INT); if (fields & MPIIO_OUT_POS) - mpiio_dump_array(fnam + ".pos", pos.data(), 3 * nlocalpart, + mpiio_dump_array(prefix + ".pos", pos.data(), 3 * nlocalpart, 3 * pref, MPI_DOUBLE); if (fields & MPIIO_OUT_VEL) - mpiio_dump_array(fnam + ".vel", vel.data(), 3 * nlocalpart, + mpiio_dump_array(prefix + ".vel", vel.data(), 3 * nlocalpart, 3 * pref, MPI_DOUBLE); if (fields & MPIIO_OUT_TYP) - mpiio_dump_array(fnam + ".type", type.data(), nlocalpart, pref, + mpiio_dump_array(prefix + ".type", type.data(), nlocalpart, pref, MPI_INT); if (fields & MPIIO_OUT_BND) { @@ -247,8 +246,8 @@ void mpi_mpiio_common_write(const char *filename, unsigned fields, int bonds_size = static_cast(bonds.size()); MPI_Exscan(&bonds_size, &bpref, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); - mpiio_dump_array(fnam + ".boff", &bonds_size, 1, rank, MPI_INT); - mpiio_dump_array(fnam + ".bond", bonds.data(), bonds.size(), bpref, + mpiio_dump_array(prefix + ".boff", &bonds_size, 1, rank, MPI_INT); + mpiio_dump_array(prefix + ".bond", bonds.data(), bonds.size(), bpref, MPI_CHAR); } } @@ -307,64 +306,65 @@ static void mpiio_read_array(const std::string &fn, T *arr, std::size_t len, } } -/** Read the header file and store the information in the pointer - * "field". To be called by all processes. +/** Read the header file and return the first value. + * To be called by all processes. * * \param fn Filename of the head file * \param rank The rank of the current process in MPI_COMM_WORLD - * \param fields Pointer to store the fields to */ -static void read_head(const std::string &fn, int rank, unsigned *fields) { +static unsigned read_head(const std::string &fn, int rank) { + unsigned n_fields = 0u; FILE *f = nullptr; if (rank == 0) { if (!(f = fopen(fn.c_str(), "rb"))) { fprintf(stderr, "MPI-IO: Could not open %s.head.\n", fn.c_str()); fatal_error(); } - if (fread((void *)fields, sizeof(unsigned), 1, f) != 1) { + if (fread(static_cast(&n_fields), sizeof(unsigned), 1, f) != 1) { fprintf(stderr, "MPI-IO: Read on %s.head failed.\n", fn.c_str()); fatal_error(); } - MPI_Bcast(fields, 1, MPI_UNSIGNED, 0, MPI_COMM_WORLD); + } + MPI_Bcast(&n_fields, 1, MPI_UNSIGNED, 0, MPI_COMM_WORLD); + if (f) { fclose(f); - } else { - MPI_Bcast(fields, 1, MPI_UNSIGNED, 0, MPI_COMM_WORLD); } + return n_fields; } -/** Reads the pref file and fills pref and nlocalpart with their - * corresponding values. Needs to be called by all processes. +/** Reads the pref file. + * Needs to be called by all processes. * * \param fn The file name of the prefs file * \param rank The rank of the current process in MPI_COMM_WORLD * \param size The size of MPI_COMM_WORLD * \param nglobalpart The global amount of particles - * \param pref Pointer to store the prefix to - * \param nlocalpart Pointer to store the amount of local particles to + * @return The prefix and the local number of particles. */ -static void read_prefs(const std::string &fn, int rank, int size, - int nglobalpart, int *pref, int *nlocalpart) { - mpiio_read_array(fn, pref, 1, rank, MPI_INT); +static std::tuple read_prefs(const std::string &fn, int rank, + int size, int nglobalpart) { + int pref = 0; + int nlocalpart = 0; + mpiio_read_array(fn, &pref, 1, rank, MPI_INT); if (rank > 0) - MPI_Send(pref, 1, MPI_INT, rank - 1, 0, MPI_COMM_WORLD); + MPI_Send(&pref, 1, MPI_INT, rank - 1, 0, MPI_COMM_WORLD); if (rank < size - 1) - MPI_Recv(nlocalpart, 1, MPI_INT, rank + 1, MPI_ANY_TAG, MPI_COMM_WORLD, + MPI_Recv(&nlocalpart, 1, MPI_INT, rank + 1, MPI_ANY_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE); else - *nlocalpart = nglobalpart; - *nlocalpart -= *pref; + nlocalpart = nglobalpart; + nlocalpart -= pref; + return {pref, nlocalpart}; } -void mpi_mpiio_common_read(const char *filename, unsigned fields) { - std::string fnam(filename); - +void mpi_mpiio_common_read(const std::string &prefix, unsigned fields) { cell_structure.remove_all_particles(); int size, rank; MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); - auto const nproc = get_num_elem(fnam + ".pref", sizeof(int)); - auto const nglobalpart = get_num_elem(fnam + ".id", sizeof(int)); + auto const nproc = get_num_elem(prefix + ".pref", sizeof(int)); + auto const nglobalpart = get_num_elem(prefix + ".id", sizeof(int)); if (rank == 0 && nproc != size) { fprintf(stderr, "MPI-IO Error: Trying to read a file with a different COMM " @@ -375,8 +375,7 @@ void mpi_mpiio_common_read(const char *filename, unsigned fields) { // 1.head on head node: // Read head to determine fields at time of writing. // Compare this var to the current fields. - unsigned avail_fields; - read_head(fnam + ".head", rank, &avail_fields); + auto const avail_fields = read_head(prefix + ".head", rank); if (rank == 0 && (fields & avail_fields) != fields) { fprintf(stderr, "MPI-IO Error: Requesting to read fields which were not dumped.\n"); @@ -388,7 +387,8 @@ void mpi_mpiio_common_read(const char *filename, unsigned fields) { // Communicate own prefix to rank-1 // Determine nlocalpart (prefix of rank+1 - own prefix) on every node. int pref, nlocalpart; - read_prefs(fnam + ".pref", rank, size, nglobalpart, &pref, &nlocalpart); + std::tie(pref, nlocalpart) = + read_prefs(prefix + ".pref", rank, size, nglobalpart); std::vector particles(nlocalpart); @@ -396,7 +396,7 @@ void mpi_mpiio_common_read(const char *filename, unsigned fields) { // 1.id on all nodes: // Read nlocalpart ints at defined prefix. std::vector id(nlocalpart); - mpiio_read_array(fnam + ".id", id.data(), nlocalpart, pref, MPI_INT); + mpiio_read_array(prefix + ".id", id.data(), nlocalpart, pref, MPI_INT); for (int i = 0; i < nlocalpart; ++i) { particles[i].p.identity = id[i]; @@ -407,12 +407,11 @@ void mpi_mpiio_common_read(const char *filename, unsigned fields) { // 1.pos on all nodes: // Read nlocalpart * 3 doubles at defined prefix * 3 std::vector pos(3 * nlocalpart); - mpiio_read_array(fnam + ".pos", pos.data(), 3 * nlocalpart, + mpiio_read_array(prefix + ".pos", pos.data(), 3 * nlocalpart, 3 * pref, MPI_DOUBLE); for (int i = 0; i < nlocalpart; ++i) { - particles[i].r.p = - Utils::Vector3d{pos[3 * i + 0], pos[3 * i + 1], pos[3 * i + 2]}; + particles[i].r.p = {pos[3 * i + 0], pos[3 * i + 1], pos[3 * i + 2]}; } } @@ -420,7 +419,7 @@ void mpi_mpiio_common_read(const char *filename, unsigned fields) { // 1.type on all nodes: // Read nlocalpart ints at defined prefix. std::vector type(nlocalpart); - mpiio_read_array(fnam + ".type", type.data(), nlocalpart, pref, + mpiio_read_array(prefix + ".type", type.data(), nlocalpart, pref, MPI_INT); for (int i = 0; i < nlocalpart; ++i) @@ -431,26 +430,25 @@ void mpi_mpiio_common_read(const char *filename, unsigned fields) { // 1.vel on all nodes: // Read nlocalpart * 3 doubles at defined prefix * 3 std::vector vel(3 * nlocalpart); - mpiio_read_array(fnam + ".vel", vel.data(), 3 * nlocalpart, + mpiio_read_array(prefix + ".vel", vel.data(), 3 * nlocalpart, 3 * pref, MPI_DOUBLE); for (int i = 0; i < nlocalpart; ++i) - particles[i].m.v = - Utils::Vector3d{vel[3 * i + 0], vel[3 * i + 1], vel[3 * i + 2]}; + particles[i].m.v = {vel[3 * i + 0], vel[3 * i + 1], vel[3 * i + 2]}; } if (fields & MPIIO_OUT_BND) { // 1.boff // 1 int per process int bonds_size = 0; - mpiio_read_array(fnam + ".boff", &bonds_size, 1, rank, MPI_INT); + mpiio_read_array(prefix + ".boff", &bonds_size, 1, rank, MPI_INT); int bpref = 0; MPI_Exscan(&bonds_size, &bpref, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); // 1.bond // nlocalbonds ints per process std::vector bond(bonds_size); - mpiio_read_array(fnam + ".bond", bond.data(), bonds_size, bpref, + mpiio_read_array(prefix + ".bond", bond.data(), bonds_size, bpref, MPI_CHAR); namespace io = boost::iostreams; diff --git a/src/core/io/mpiio/mpiio.hpp b/src/core/io/mpiio/mpiio.hpp index 6a36fa9fc2a..7bc01c7dce2 100644 --- a/src/core/io/mpiio/mpiio.hpp +++ b/src/core/io/mpiio/mpiio.hpp @@ -26,6 +26,7 @@ */ #include "ParticleRange.hpp" + namespace Mpiio { /** Constants which indicate what to output. To indicate the output of @@ -33,6 +34,7 @@ namespace Mpiio { * */ enum MPIIOOutputFields : unsigned int { + MPIIO_OUT_NON = 0u, MPIIO_OUT_POS = 1u, MPIIO_OUT_VEL = 2u, MPIIO_OUT_TYP = 4u, @@ -42,20 +44,20 @@ enum MPIIOOutputFields : unsigned int { /** Parallel binary output using MPI-IO. To be called by all MPI * processes. Aborts ESPResSo if an error occurs. * - * \param filename A null-terminated filename prefix. + * \param prefix Filepath prefix. * \param fields Output specifier which fields to dump. * \param particles range of particles to serialize. */ -void mpi_mpiio_common_write(const char *filename, unsigned fields, +void mpi_mpiio_common_write(const std::string &prefix, unsigned fields, const ParticleRange &particles); /** Parallel binary input using MPI-IO. To be called by all MPI * processes. Aborts ESPResSo if an error occurs. * - * \param filename A null-terminated filename prefix. + * \param prefix Filepath prefix. * \param fields Specifier which fields to read. */ -void mpi_mpiio_common_read(const char *filename, unsigned fields); +void mpi_mpiio_common_read(const std::string &prefix, unsigned fields); } // namespace Mpiio diff --git a/src/script_interface/mpiio/si_mpiio.hpp b/src/script_interface/mpiio/si_mpiio.hpp index f57ca9acf79..02f54ff6e35 100644 --- a/src/script_interface/mpiio/si_mpiio.hpp +++ b/src/script_interface/mpiio/si_mpiio.hpp @@ -31,8 +31,6 @@ #include -#define field_value(use, v) ((use) ? (v) : 0u) - namespace ScriptInterface { namespace MPIIO { @@ -43,22 +41,22 @@ class MPIIOScript : public AutoParameters { Variant do_call_method(const std::string &name, const VariantMap ¶meters) override { - auto pref = get_value(parameters.at("prefix")); + auto prefix = get_value(parameters.at("prefix")); auto pos = get_value(parameters.at("pos")); auto vel = get_value(parameters.at("vel")); auto typ = get_value(parameters.at("typ")); - auto bond = get_value(parameters.at("bond")); + auto bnd = get_value(parameters.at("bond")); - unsigned v = field_value(pos, Mpiio::MPIIO_OUT_POS) | - field_value(vel, Mpiio::MPIIO_OUT_VEL) | - field_value(typ, Mpiio::MPIIO_OUT_TYP) | - field_value(bond, Mpiio::MPIIO_OUT_BND); + auto const fields = ((pos) ? Mpiio::MPIIO_OUT_POS : Mpiio::MPIIO_OUT_NON) | + ((vel) ? Mpiio::MPIIO_OUT_VEL : Mpiio::MPIIO_OUT_NON) | + ((typ) ? Mpiio::MPIIO_OUT_TYP : Mpiio::MPIIO_OUT_NON) | + ((bnd) ? Mpiio::MPIIO_OUT_BND : Mpiio::MPIIO_OUT_NON); if (name == "write") - Mpiio::mpi_mpiio_common_write(pref.c_str(), v, + Mpiio::mpi_mpiio_common_write(prefix, fields, cell_structure.local_particles()); else if (name == "read") - Mpiio::mpi_mpiio_common_read(pref.c_str(), v); + Mpiio::mpi_mpiio_common_read(prefix, fields); return {}; } From 4e8f3ae165e19c8c12424d422e9da620505e15ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 9 Feb 2022 21:44:39 +0100 Subject: [PATCH 49/99] core: Improve MPI-IO documentation Fix incorrect error messages. Improve doxygen blocks and Sphinx docs. --- doc/sphinx/io.rst | 36 ++++++++++++----- src/core/io/mpiio/mpiio.cpp | 79 +++++++++++++++++++++---------------- src/core/io/mpiio/mpiio.hpp | 33 ++++++++++------ 3 files changed, 91 insertions(+), 57 deletions(-) diff --git a/doc/sphinx/io.rst b/doc/sphinx/io.rst index 1c07a34362f..8d4ea26de58 100644 --- a/doc/sphinx/io.rst +++ b/doc/sphinx/io.rst @@ -216,14 +216,16 @@ capabilities. The usage is quite simple: .. code:: python - import espressomd.io.mppiio + import espressomd + import espressomd.io system = espressomd.System(box_l=[1, 1, 1]) # ... add particles here - espressomd.io.mppiio.mpiio.write("/tmp/mydata", positions=True, velocities=True, types=True, bonds=True) + mpiio = espressomd.io.mpiio.mpiio + mpiio.write("/tmp/mydata", positions=True, velocities=True, types=True, bonds=True) -Here, :file:`/tmp/mydata` is the prefix used for several files. The call will output -particle positions, velocities, types and their bonds to the following files in -folder :file:`/tmp`: +Here, :file:`/tmp/mydata` is the prefix used to generate several files. +The call will output particle positions, velocities, types and their bonds +to the following files in folder :file:`/tmp`: - :file:`mydata.head` - :file:`mydata.id` @@ -235,11 +237,25 @@ folder :file:`/tmp`: - :file:`mydata.bond` Depending on the chosen output, not all of these files might be created. -To read these in again, simply call :meth:`espressomd.io.mpiio.Mpiio.read`. It has the same signature as -:meth:`espressomd.io.mpiio.Mpiio.write`. - -*WARNING*: Do not attempt to read these binary files on a machine with a different -architecture! +To read these in again, simply call :meth:`espressomd.io.mpiio.Mpiio.read`. +It has the same signature as :meth:`espressomd.io.mpiio.Mpiio.write`. +When writing files, make sure the prefix hasn't been used before +(e.g. by a different simulation script), otherwise the write operation +will fail to avoid accidentally overwriting pre-existing data. Likewise, +reading incomplete data (or complete data but with the wrong number of MPI +ranks) will throw an error. + +*WARNING*: Do not attempt to read these binary files on a machine +with a different architecture! This will read malformed data without +necessarily throwing an error. + +In case of read failure or write failure, the simulation will halt. +On 1 MPI rank, the simulation will halt with a python runtime error. +This exception can be recovered from; in case of a write operation, +any written file must be deleted before attempting to write again +(since the prefix argument must be unique). On more than 1 MPI rank, +the simulation will halt with a call to ``MPI_Abort`` and will send +the ``SIGABRT`` signal. .. _Writing VTF files: diff --git a/src/core/io/mpiio/mpiio.cpp b/src/core/io/mpiio/mpiio.cpp index b84de9a2fac..a50a6084cd8 100644 --- a/src/core/io/mpiio/mpiio.cpp +++ b/src/core/io/mpiio/mpiio.cpp @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -/** \file +/** @file * * Concerning the file layouts. * - Scalar arrays are written like this: @@ -91,14 +91,16 @@ static void fatal_error() { errexit(); } -/** Dumps arr of size len starting from prefix pref of type T using - * MPI_T as MPI datatype. Beware, that T and MPI_T have to match! +/** + * @brief Dump data @p arr of size @p len starting from prefix @p pref + * of type @p T using @p MPI_T as MPI datatype. Beware, that @p T and + * @p MPI_T have to match! * - * \param fn The file name to dump to. Must not exist already - * \param arr The array to dump - * \param len The number of elements to dump - * \param pref The prefix for this process - * \param MPI_T The MPI_Datatype corresponding to the template parameter T. + * @param fn The file name to write to (must not already exist!) + * @param arr The array to dump + * @param len The number of elements to dump + * @param pref The prefix for this process + * @param MPI_T The MPI datatype corresponding to the template parameter @p T */ template static void mpiio_dump_array(const std::string &fn, T *arr, std::size_t len, @@ -129,12 +131,12 @@ static void mpiio_dump_array(const std::string &fn, T *arr, std::size_t len, } } -/** Dumps some generic infos like the dumped fields and info to process - * the bond information offline (without ESPResSo). To be called by the - * head node only. +/** + * @brief Dump the fields and bond information. + * To be called by the head node only. * - * \param fn The filename to write to - * \param fields The dumped fields + * @param fn The filename to write to + * @param fields The dumped fields */ static void dump_info(const std::string &fn, unsigned fields) { FILE *f = fopen(fn.c_str(), "wb"); @@ -252,13 +254,13 @@ void mpi_mpiio_common_write(const std::string &prefix, unsigned fields, } } -/** Get the number of elements in a file by its file size and - * elem_sz. I.e. query the file size using stat(2) and divide it by - * elem_sz. +/** + * @brief Get the number of elements in a file by its file size and @p elem_sz. + * I.e. query the file size using stat(2) and divide it by @p elem_sz. * - * \param fn The filename - * \param elem_sz Sizeof a single element - * \return The number of elements stored binary in the file + * @param fn The filename + * @param elem_sz Size of a single element + * @return The number of elements stored in the file */ static int get_num_elem(const std::string &fn, std::size_t elem_sz) { // Could also be done via MPI_File_open, MPI_File_get_size, @@ -273,9 +275,16 @@ static int get_num_elem(const std::string &fn, std::size_t elem_sz) { return static_cast(st.st_size / elem_sz); } -/** Reads a previously dumped array of size len starting from prefix - * pref of type T using MPI_T as MPI datatype. Beware, that T and MPI_T - * have to match! +/** + * @brief Read a previously dumped array of size @p len starting from prefix + * @p pref of type @p T using @p MPI_T as MPI datatype. Beware, that + * @p T and @p MPI_T have to match! + * + * @param fn The file name to read from + * @param arr The array to populate + * @param len The number of elements to read + * @param pref The prefix for this process + * @param MPI_T The MPI datatype corresponding to the template parameter @p T */ template static void mpiio_read_array(const std::string &fn, T *arr, std::size_t len, @@ -306,22 +315,23 @@ static void mpiio_read_array(const std::string &fn, T *arr, std::size_t len, } } -/** Read the header file and return the first value. - * To be called by all processes. +/** + * @brief Read the header file and return the first value. + * To be called by all processes. * - * \param fn Filename of the head file - * \param rank The rank of the current process in MPI_COMM_WORLD + * @param fn Filename of the head file + * @param rank The rank of the current process in @c MPI_COMM_WORLD */ static unsigned read_head(const std::string &fn, int rank) { unsigned n_fields = 0u; FILE *f = nullptr; if (rank == 0) { if (!(f = fopen(fn.c_str(), "rb"))) { - fprintf(stderr, "MPI-IO: Could not open %s.head.\n", fn.c_str()); + fprintf(stderr, "MPI-IO: Could not open \"%s\".\n", fn.c_str()); fatal_error(); } if (fread(static_cast(&n_fields), sizeof(unsigned), 1, f) != 1) { - fprintf(stderr, "MPI-IO: Read on %s.head failed.\n", fn.c_str()); + fprintf(stderr, "MPI-IO: Read on \"%s\" failed.\n", fn.c_str()); fatal_error(); } } @@ -332,13 +342,14 @@ static unsigned read_head(const std::string &fn, int rank) { return n_fields; } -/** Reads the pref file. - * Needs to be called by all processes. +/** + * @brief Read the pref file. + * Needs to be called by all processes. * - * \param fn The file name of the prefs file - * \param rank The rank of the current process in MPI_COMM_WORLD - * \param size The size of MPI_COMM_WORLD - * \param nglobalpart The global amount of particles + * @param fn The file name of the prefs file + * @param rank The rank of the current process in @c MPI_COMM_WORLD + * @param size The size of @c MPI_COMM_WORLD + * @param nglobalpart The global amount of particles * @return The prefix and the local number of particles. */ static std::tuple read_prefs(const std::string &fn, int rank, diff --git a/src/core/io/mpiio/mpiio.hpp b/src/core/io/mpiio/mpiio.hpp index 7bc01c7dce2..29c3ce78462 100644 --- a/src/core/io/mpiio/mpiio.hpp +++ b/src/core/io/mpiio/mpiio.hpp @@ -21,7 +21,7 @@ #ifndef CORE_IO_MPIIO_MPIIO_HPP #define CORE_IO_MPIIO_MPIIO_HPP -/** \file +/** @file * Implements binary output using MPI-IO. */ @@ -29,9 +29,10 @@ namespace Mpiio { -/** Constants which indicate what to output. To indicate the output of - * multiple fields, OR the corresponding values. - * +/** + * @brief Constants which indicate what to output. + * To indicate the output of multiple fields, OR the + * corresponding values. */ enum MPIIOOutputFields : unsigned int { MPIIO_OUT_NON = 0u, @@ -41,21 +42,27 @@ enum MPIIOOutputFields : unsigned int { MPIIO_OUT_BND = 8u, }; -/** Parallel binary output using MPI-IO. To be called by all MPI - * processes. Aborts ESPResSo if an error occurs. +/** + * @brief Parallel binary output using MPI-IO. + * To be called by all MPI processes. Aborts ESPResSo if an error occurs. + * On 1 MPI rank, the error is converted to a runtime error and can be + * recovered by removing any file that may have already been written. * - * \param prefix Filepath prefix. - * \param fields Output specifier which fields to dump. - * \param particles range of particles to serialize. + * @param prefix Filepath prefix. + * @param fields Specifier for which fields to dump. + * @param particles Range of particles to serialize. */ void mpi_mpiio_common_write(const std::string &prefix, unsigned fields, const ParticleRange &particles); -/** Parallel binary input using MPI-IO. To be called by all MPI - * processes. Aborts ESPResSo if an error occurs. +/** + * @brief Parallel binary input using MPI-IO. + * To be called by all MPI processes. Aborts ESPResSo if an error occurs. + * On 1 MPI rank, the error is converted to a runtime error and can be + * recovered. * - * \param prefix Filepath prefix. - * \param fields Specifier which fields to read. + * @param prefix Filepath prefix. + * @param fields Specifier for which fields to read. */ void mpi_mpiio_common_read(const std::string &prefix, unsigned fields); From 27a5bd528a45de127624a87cdc914899ed95ee61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 10 Feb 2022 23:07:56 +0100 Subject: [PATCH 50/99] core: Refactor MPI-IO exception mechanism Factor code duplication. Wrap the error message inside the exception. --- src/core/io/mpiio/mpiio.cpp | 108 ++++++++++++++------------- testsuite/python/mpiio_exceptions.py | 16 ++-- 2 files changed, 66 insertions(+), 58 deletions(-) diff --git a/src/core/io/mpiio/mpiio.cpp b/src/core/io/mpiio/mpiio.cpp index a50a6084cd8..13f6c1a97b0 100644 --- a/src/core/io/mpiio/mpiio.cpp +++ b/src/core/io/mpiio/mpiio.cpp @@ -69,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -81,14 +82,52 @@ namespace Mpiio { * @brief Fatal error handler. * On 1 MPI rank the error is recoverable and an exception is thrown. * On more than 1 MPI rank the error is not recoverable. + * @param msg Custom error message + * @param fn File path + * @param extra Extra context */ -static void fatal_error() { +static bool fatal_error(char const *msg, std::string const &fn = "", + std::string const &extra = "") { + std::stringstream what; + what << "MPI-IO Error: " << msg; + if (not fn.empty()) { + what << " \"" << fn << "\""; + } + if (not extra.empty()) { + what << " :" << extra; + } int size; MPI_Comm_size(MPI_COMM_WORLD, &size); if (size == 1) { - throw std::runtime_error(""); + throw std::runtime_error(what.str()); } + fprintf(stderr, "%s\n", what.str().c_str()); errexit(); + return false; +} + +/** + * @brief Fatal error handler that closes an open file and queries the + * message associated with an MPI error code. + * On 1 MPI rank the error is recoverable and an exception is thrown. + * On more than 1 MPI rank the error is not recoverable. + * @param msg Custom error message + * @param fn File path + * @param fp File handle + * @param errnum MPI error code + */ +static bool fatal_error(char const *msg, std::string const &fn, MPI_File *fp, + int errnum) { + // get MPI error message + char buf[MPI_MAX_ERROR_STRING]; + int buf_len; + MPI_Error_string(errnum, buf, &buf_len); + buf[buf_len] = '\0'; + // close file handle if open + if (fp) { + MPI_File_close(fp); + } + return fatal_error(msg, fn, buf); } /** @@ -113,22 +152,13 @@ static void mpiio_dump_array(const std::string &fn, T *arr, std::size_t len, MPI_MODE_WRONLY | MPI_MODE_CREATE | MPI_MODE_EXCL, MPI_INFO_NULL, &f); if (ret) { - char buf[MPI_MAX_ERROR_STRING]; - int buf_len; - MPI_Error_string(ret, buf, &buf_len); - buf[buf_len] = '\0'; - fprintf(stderr, "MPI-IO Error: Could not open file \"%s\": %s\n", - fn.c_str(), buf); - fatal_error(); + fatal_error("Could not open file", fn, &f, ret); } ret = MPI_File_set_view(f, pref * sizeof(T), MPI_T, MPI_T, const_cast("native"), MPI_INFO_NULL); ret |= MPI_File_write_all(f, arr, len, MPI_T, MPI_STATUS_IGNORE); + static_cast(ret and fatal_error("Could not write file", fn, &f, ret)); MPI_File_close(&f); - if (ret) { - fprintf(stderr, "MPI-IO Error: Could not write file \"%s\".\n", fn.c_str()); - fatal_error(); - } } /** @@ -141,12 +171,10 @@ static void mpiio_dump_array(const std::string &fn, T *arr, std::size_t len, static void dump_info(const std::string &fn, unsigned fields) { FILE *f = fopen(fn.c_str(), "wb"); if (!f) { - fprintf(stderr, "MPI-IO Error: Could not open %s for writing.\n", - fn.c_str()); - fatal_error(); + fatal_error("Could not open file", fn); } static std::vector npartners; - int success = (fwrite(&fields, sizeof(fields), 1, f) == 1); + bool success = (fwrite(&fields, sizeof(fields), 1, f) == 1); // Pack the necessary information of bonded_ia_params: // The number of partners. This is needed to interpret the bond IntList. if (bonded_ia_params.size() > npartners.size()) @@ -162,10 +190,7 @@ static void dump_info(const std::string &fn, unsigned fields) { success && (fwrite(npartners.data(), sizeof(int), bonded_ia_params.size(), f) == bonded_ia_params.size()); fclose(f); - if (!success) { - fprintf(stderr, "MPI-IO Error: Failed to write %s.\n", fn.c_str()); - fatal_error(); - } + static_cast(success or fatal_error("Could not write file", fn)); } void mpi_mpiio_common_write(const std::string &prefix, unsigned fields, @@ -268,9 +293,8 @@ static int get_num_elem(const std::string &fn, std::size_t elem_sz) { struct stat st; errno = 0; if (stat(fn.c_str(), &st) != 0) { - fprintf(stderr, "MPI-IO Input Error: Could not get file size of %s: %s\n", - fn.c_str(), strerror(errno)); - fatal_error(); + auto const reason = strerror(errno); + fatal_error("Could not get file size of", fn, reason); } return static_cast(st.st_size / elem_sz); } @@ -294,25 +318,16 @@ static void mpiio_read_array(const std::string &fn, T *arr, std::size_t len, ret = MPI_File_open(MPI_COMM_WORLD, const_cast(fn.c_str()), MPI_MODE_RDONLY, MPI_INFO_NULL, &f); - if (ret) { - char buf[MPI_MAX_ERROR_STRING]; - int buf_len; - MPI_Error_string(ret, buf, &buf_len); - buf[buf_len] = '\0'; - fprintf(stderr, "MPI-IO Error: Could not open file \"%s\": %s\n", - fn.c_str(), buf); - fatal_error(); + fatal_error("Could not open file", fn, &f, ret); } + ret = MPI_File_set_view(f, pref * sizeof(T), MPI_T, MPI_T, const_cast("native"), MPI_INFO_NULL); ret |= MPI_File_read_all(f, arr, len, MPI_T, MPI_STATUS_IGNORE); + static_cast(ret and fatal_error("Could not read file", fn, &f, ret)); MPI_File_close(&f); - if (ret) { - fprintf(stderr, "MPI-IO Error: Could not read file \"%s\".\n", fn.c_str()); - fatal_error(); - } } /** @@ -326,14 +341,10 @@ static unsigned read_head(const std::string &fn, int rank) { unsigned n_fields = 0u; FILE *f = nullptr; if (rank == 0) { - if (!(f = fopen(fn.c_str(), "rb"))) { - fprintf(stderr, "MPI-IO: Could not open \"%s\".\n", fn.c_str()); - fatal_error(); - } - if (fread(static_cast(&n_fields), sizeof(unsigned), 1, f) != 1) { - fprintf(stderr, "MPI-IO: Read on \"%s\" failed.\n", fn.c_str()); - fatal_error(); - } + f = fopen(fn.c_str(), "rb"); + static_cast(not f and fatal_error("Could not open file", fn)); + auto const n = fread(static_cast(&n_fields), sizeof n_fields, 1, f); + static_cast((n == 1) or fatal_error("Could not read file", fn)); } MPI_Bcast(&n_fields, 1, MPI_UNSIGNED, 0, MPI_COMM_WORLD); if (f) { @@ -378,9 +389,8 @@ void mpi_mpiio_common_read(const std::string &prefix, unsigned fields) { auto const nglobalpart = get_num_elem(prefix + ".id", sizeof(int)); if (rank == 0 && nproc != size) { - fprintf(stderr, "MPI-IO Error: Trying to read a file with a different COMM " - "size than at point of writing.\n"); - fatal_error(); + fatal_error("Trying to read a file with a different COMM " + "size than at point of writing."); } // 1.head on head node: @@ -388,9 +398,7 @@ void mpi_mpiio_common_read(const std::string &prefix, unsigned fields) { // Compare this var to the current fields. auto const avail_fields = read_head(prefix + ".head", rank); if (rank == 0 && (fields & avail_fields) != fields) { - fprintf(stderr, - "MPI-IO Error: Requesting to read fields which were not dumped.\n"); - fatal_error(); + fatal_error("Requesting to read fields which were not dumped."); } // 1.pref on all nodes: diff --git a/testsuite/python/mpiio_exceptions.py b/testsuite/python/mpiio_exceptions.py index 5d7a2e5bbec..a72b57d9c5c 100644 --- a/testsuite/python/mpiio_exceptions.py +++ b/testsuite/python/mpiio_exceptions.py @@ -89,51 +89,51 @@ def test_exceptions(self): # exception when the metadata cannot be written path, fn = generator.create('head', read_only=True) - with self.assertRaises(RuntimeError): + with self.assertRaisesRegex(RuntimeError, f'Could not open file "{fn}"'): mpiio.write(path, types=True) # exception when the payload cannot be written path, fn = generator.create('pref', read_only=True) - with self.assertRaises(RuntimeError): + with self.assertRaisesRegex(RuntimeError, f'Could not open file "{fn}"'): mpiio.write(path, types=True) # exception when calculating the size of a non-existent file path, _ = generator.create(read_only=True) fn = f'{path}.pref' - with self.assertRaises(RuntimeError): + with self.assertRaisesRegex(RuntimeError, f'Could not get file size of "{fn}"'): mpiio.read(path, types=True) # exception when the MPI world size differs for reading and writing # (empty .pref file -> data was written with MPI world size of 0) path, _ = generator.create('id', 'pref', read_only=True) - with self.assertRaises(RuntimeError): + with self.assertRaisesRegex(RuntimeError, f'Trying to read a file with a different COMM size than at point of writing'): mpiio.read(path, types=True) # exception when the particle types don't exist path, _ = generator.create( 'pref', 'id', 'head', read_only=False, from_ref=path_ref) fn = f'{path}.type' - with self.assertRaises(RuntimeError): + with self.assertRaisesRegex(RuntimeError, f'Could not open file "{fn}"'): mpiio.read(path, types=True) # exception when the metadata doesn't exist path, _ = generator.create( 'id', 'pref', read_only=False, from_ref=path_ref) fn = f'{path}.head' - with self.assertRaises(RuntimeError): + with self.assertRaisesRegex(RuntimeError, f'Could not open file "{fn}"'): mpiio.read(path, types=True) # exception when the metadata is empty with open(fn, 'wb'): pass - with self.assertRaises(RuntimeError): + with self.assertRaisesRegex(RuntimeError, f'Could not read file "{fn}"'): mpiio.read(path, types=True) # exception when reading data that was not written to disk # (empty .pref file -> data was written with MPI world size of 0) path, _ = generator.create( 'id', 'pref', 'head', read_only=False, from_ref=path_ref) - with self.assertRaises(RuntimeError): + with self.assertRaisesRegex(RuntimeError, f'Requesting to read fields which were not dumped'): mpiio.read(path, types=True, bonds=True) From 925ec20ff35bb85c0da817f04978b3b3cc9d816e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 11 Feb 2022 19:13:46 +0100 Subject: [PATCH 51/99] python: Remove MPI-IO global variable It is now possible to instantiate multiple Mpiio objects. This is an API breaking change. --- doc/sphinx/io.rst | 2 +- src/python/espressomd/io/mpiio.py | 21 +++++---------- testsuite/python/mpiio.py | 38 +++++++++++++++++++++++++--- testsuite/python/mpiio_exceptions.py | 2 +- 4 files changed, 43 insertions(+), 20 deletions(-) diff --git a/doc/sphinx/io.rst b/doc/sphinx/io.rst index 8d4ea26de58..684e854aa7f 100644 --- a/doc/sphinx/io.rst +++ b/doc/sphinx/io.rst @@ -220,7 +220,7 @@ capabilities. The usage is quite simple: import espressomd.io system = espressomd.System(box_l=[1, 1, 1]) # ... add particles here - mpiio = espressomd.io.mpiio.mpiio + mpiio = espressomd.io.mpiio.Mpiio() mpiio.write("/tmp/mydata", positions=True, velocities=True, types=True, bonds=True) Here, :file:`/tmp/mydata` is the prefix used to generate several files. diff --git a/src/python/espressomd/io/mpiio.py b/src/python/espressomd/io/mpiio.py index e89ac28920d..bf617dcf1a1 100644 --- a/src/python/espressomd/io/mpiio.py +++ b/src/python/espressomd/io/mpiio.py @@ -14,22 +14,18 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from ..script_interface import PScriptInterface +from ..script_interface import ScriptInterfaceHelper, script_interface_register -class Mpiio: +@script_interface_register +class Mpiio(ScriptInterfaceHelper): """MPI-IO object. Used to output particle data using MPI-IO to binary files. - - .. note:: - See the :meth:`write` and :meth:`read` methods for documentation. """ - - def __init__(self): - self._instance = PScriptInterface( - "ScriptInterface::MPIIO::MPIIOScript") + _so_name = "ScriptInterface::MPIIO::MPIIOScript" + _so_creation_policy = "GLOBAL" def write(self, prefix=None, positions=False, velocities=False, types=False, bonds=False): @@ -75,7 +71,7 @@ def write(self, prefix=None, positions=False, velocities=False, if not positions and not velocities and not types and not bonds: raise ValueError("No output fields chosen.") - self._instance.call_method( + self.call_method( "write", prefix=prefix, pos=positions, vel=velocities, typ=types, bond=bonds) def read(self, prefix=None, positions=False, velocities=False, @@ -96,8 +92,5 @@ def read(self, prefix=None, positions=False, velocities=False, if not positions and not velocities and not types and not bonds: raise ValueError("No output fields chosen.") - self._instance.call_method( + self.call_method( "read", prefix=prefix, pos=positions, vel=velocities, typ=types, bond=bonds) - - -mpiio = Mpiio() diff --git a/testsuite/python/mpiio.py b/testsuite/python/mpiio.py index 9e09a8b20b4..00a4fab9106 100644 --- a/testsuite/python/mpiio.py +++ b/testsuite/python/mpiio.py @@ -159,7 +159,7 @@ def test_mpiio(self): 'velocities': True, 'bonds': True} prefix = self.generate_prefix(self.id()) - mpiio = espressomd.io.mpiio.mpiio + mpiio = espressomd.io.mpiio.Mpiio() self.add_particles() mpiio.write(prefix, **fields) @@ -171,7 +171,7 @@ def test_mpiio(self): def test_mpiio_without_positions(self): prefix = self.generate_prefix(self.id()) - mpiio = espressomd.io.mpiio.mpiio + mpiio = espressomd.io.mpiio.Mpiio() self.add_particles() mpiio.write(prefix, types=True, positions=False) self.system.part.clear() @@ -180,15 +180,45 @@ def test_mpiio_without_positions(self): def test_mpiio_without_types(self): prefix = self.generate_prefix(self.id()) - mpiio = espressomd.io.mpiio.mpiio + mpiio = espressomd.io.mpiio.Mpiio() self.add_particles() mpiio.write(prefix, types=False, positions=True) self.system.part.clear() mpiio.read(prefix, types=False, positions=True) self.check_sample_system(types=False, positions=True) + def test_mpiio_multiple_instances(self): + fields1 = { + 'types': True, + 'positions': True, + 'velocities': True, + 'bonds': True} + fields2 = { + 'types': True, + 'positions': True, + 'velocities': False, + 'bonds': False} + prefix1 = self.generate_prefix(self.id()) + '.1' + prefix2 = self.generate_prefix(self.id()) + '.2' + mpiio1 = espressomd.io.mpiio.Mpiio() + mpiio2 = espressomd.io.mpiio.Mpiio() + + self.add_particles() + mpiio1.write(prefix1, **fields1) + mpiio2.write(prefix2, **fields2) + self.check_files_exist(prefix1, **fields1) + self.check_files_exist(prefix2, **fields2) + + self.system.part.clear() + mpiio1.read(prefix1, **fields1) + self.check_sample_system(**fields1) + + self.system.part.clear() + mpiio2.read(prefix2, **fields2) + self.check_sample_system(**fields2) + def test_mpiio_exceptions(self): - mpiio = espressomd.io.mpiio.mpiio + mpiio = espressomd.io.mpiio.Mpiio() prefix = self.generate_prefix(self.id()) msg_prefix = "Need to supply output prefix via the 'prefix' argument." with self.assertRaisesRegex(ValueError, msg_prefix): diff --git a/testsuite/python/mpiio_exceptions.py b/testsuite/python/mpiio_exceptions.py index a72b57d9c5c..7f3595cfa12 100644 --- a/testsuite/python/mpiio_exceptions.py +++ b/testsuite/python/mpiio_exceptions.py @@ -75,7 +75,7 @@ def tearDownClass(cls): @ut.skipIf(n_nodes != 1, "only works on 1 MPI rank") def test_exceptions(self): generator = MPIIOMockGenerator(self.temp_dir.name) - mpiio = espressomd.io.mpiio.mpiio + mpiio = espressomd.io.mpiio.Mpiio() # generate reference data self.system.part.add(pos=[0, 0, 0]) From 2781a8ed2d8836858510f3856b59ed52a27c2cfa Mon Sep 17 00:00:00 2001 From: Patrick Kreissl Date: Fri, 11 Feb 2022 01:56:46 +0100 Subject: [PATCH 52/99] Make CellStructureType an enum class. --- src/core/CellStructure.cpp | 5 +-- src/core/CellStructure.hpp | 13 ++------ src/core/CellStructureType.hpp | 33 +++++++++++++++++++ src/core/actor/Mmm1dgpuForce.cpp | 4 ++- src/core/cells.cpp | 10 +++--- src/core/cells.hpp | 5 +-- .../electrostatics_magnetostatics/mmm1d.cpp | 4 ++- .../p3m-dipolar.cpp | 4 ++- .../electrostatics_magnetostatics/p3m.cpp | 4 ++- src/core/event.cpp | 3 +- src/core/grid_based_algorithms/lb.cpp | 4 ++- src/python/espressomd/cellsystem.pxd | 10 +++--- src/python/espressomd/cellsystem.pyx | 10 +++--- 13 files changed, 76 insertions(+), 33 deletions(-) create mode 100644 src/core/CellStructureType.hpp diff --git a/src/core/CellStructure.cpp b/src/core/CellStructure.cpp index 94ff565c80a..b513367f607 100644 --- a/src/core/CellStructure.cpp +++ b/src/core/CellStructure.cpp @@ -22,6 +22,7 @@ #include "CellStructure.hpp" #include "AtomDecomposition.hpp" +#include "CellStructureType.hpp" #include "RegularDecomposition.hpp" #include @@ -243,7 +244,7 @@ void CellStructure::resort_particles(int global_flag) { void CellStructure::set_atom_decomposition(boost::mpi::communicator const &comm, BoxGeometry const &box) { set_particle_decomposition(std::make_unique(comm, box)); - m_type = CELL_STRUCTURE_NSQUARE; + m_type = CellStructureType::CELL_STRUCTURE_NSQUARE; } void CellStructure::set_regular_decomposition( @@ -251,5 +252,5 @@ void CellStructure::set_regular_decomposition( LocalBox const &local_geo) { set_particle_decomposition( std::make_unique(comm, range, box, local_geo)); - m_type = CELL_STRUCTURE_REGULAR; + m_type = CellStructureType::CELL_STRUCTURE_REGULAR; } diff --git a/src/core/CellStructure.hpp b/src/core/CellStructure.hpp index 1f6a8ad257a..d815cb87aeb 100644 --- a/src/core/CellStructure.hpp +++ b/src/core/CellStructure.hpp @@ -25,6 +25,7 @@ #include "AtomDecomposition.hpp" #include "BoxGeometry.hpp" #include "Cell.hpp" +#include "CellStructureType.hpp" #include "LocalBox.hpp" #include "Particle.hpp" #include "ParticleDecomposition.hpp" @@ -48,14 +49,6 @@ #include #include -/** Cell Structure */ -enum CellStructureType : int { - /** cell structure regular decomposition */ - CELL_STRUCTURE_REGULAR = 1, - /** cell structure n square */ - CELL_STRUCTURE_NSQUARE = 2 -}; - namespace Cells { enum Resort : unsigned { RESORT_NONE = 0u, @@ -132,7 +125,7 @@ struct CellStructure { std::unique_ptr m_decomposition = std::make_unique(); /** Active type in m_decomposition */ - int m_type = CELL_STRUCTURE_NSQUARE; + CellStructureType m_type = CellStructureType::CELL_STRUCTURE_NSQUARE; /** One of @ref Cells::Resort, announces the level of resort needed. */ unsigned m_resort_particles = Cells::RESORT_NONE; @@ -247,7 +240,7 @@ struct CellStructure { } public: - int decomposition_type() const { return m_type; } + CellStructureType decomposition_type() const { return m_type; } /** Maximal cutoff supported by current cell system. */ Utils::Vector3d max_cutoff() const; diff --git a/src/core/CellStructureType.hpp b/src/core/CellStructureType.hpp new file mode 100644 index 00000000000..b3d7ffa56e5 --- /dev/null +++ b/src/core/CellStructureType.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2010-2020 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 ESPRESSO_CELLSTRUCTURETYPE_HPP +#define ESPRESSO_CELLSTRUCTURETYPE_HPP + +/** Cell structure */ +enum class CellStructureType : int { + /** cell structure regular decomposition */ + CELL_STRUCTURE_REGULAR = 1, + /** cell structure n square */ + CELL_STRUCTURE_NSQUARE = 2 +}; + +#endif // ESPRESSO_CELLSTRUCTURETYPE_HPP diff --git a/src/core/actor/Mmm1dgpuForce.cpp b/src/core/actor/Mmm1dgpuForce.cpp index d5695ceede6..a51d114f1d9 100644 --- a/src/core/actor/Mmm1dgpuForce.cpp +++ b/src/core/actor/Mmm1dgpuForce.cpp @@ -25,6 +25,7 @@ #include "electrostatics_magnetostatics/common.hpp" #include "electrostatics_magnetostatics/coulomb.hpp" +#include "CellStructureType.hpp" #include "cells.hpp" #include "energy.hpp" #include "forces.hpp" @@ -41,7 +42,8 @@ Mmm1dgpuForce::Mmm1dgpuForce(SystemInterface &s) { if (box_geo.periodic(0) || box_geo.periodic(1) || !box_geo.periodic(2)) { throw std::runtime_error("MMM1D requires periodicity (0, 0, 1)"); } - if (cell_structure.decomposition_type() != CELL_STRUCTURE_NSQUARE) { + if (cell_structure.decomposition_type() != + CellStructureType::CELL_STRUCTURE_NSQUARE) { throw std::runtime_error("MMM1D requires the N-square cellsystem"); } diff --git a/src/core/cells.cpp b/src/core/cells.cpp index 33068f14a3f..dd4d57b8ab9 100644 --- a/src/core/cells.cpp +++ b/src/core/cells.cpp @@ -195,13 +195,13 @@ std::vector mpi_resort_particles(int global_flag) { return n_parts; } -void cells_re_init(int new_cs) { +void cells_re_init(CellStructureType new_cs) { switch (new_cs) { - case CELL_STRUCTURE_REGULAR: + case CellStructureType::CELL_STRUCTURE_REGULAR: cell_structure.set_regular_decomposition(comm_cart, interaction_range(), box_geo, local_geo); break; - case CELL_STRUCTURE_NSQUARE: + case CellStructureType::CELL_STRUCTURE_NSQUARE: cell_structure.set_atom_decomposition(comm_cart, box_geo); break; default: @@ -213,7 +213,9 @@ void cells_re_init(int new_cs) { REGISTER_CALLBACK(cells_re_init) -void mpi_bcast_cell_structure(int cs) { mpi_call_all(cells_re_init, cs); } +void mpi_bcast_cell_structure(CellStructureType cs) { + mpi_call_all(cells_re_init, cs); +} void check_resort_particles() { auto const level = (cell_structure.check_resort_required( diff --git a/src/core/cells.hpp b/src/core/cells.hpp index 6452cbbb2e3..7073069d010 100644 --- a/src/core/cells.hpp +++ b/src/core/cells.hpp @@ -40,6 +40,7 @@ #include "Cell.hpp" #include "CellStructure.hpp" +#include "CellStructureType.hpp" #include "Particle.hpp" #include "RegularDecomposition.hpp" @@ -63,10 +64,10 @@ extern CellStructure cell_structure; /** Reinitialize the cell structures. * @param new_cs The new topology to use afterwards. */ -void cells_re_init(int new_cs); +void cells_re_init(CellStructureType new_cs); /** Change the cell structure on all nodes. */ -void mpi_bcast_cell_structure(int cs); +void mpi_bcast_cell_structure(CellStructureType cs); /** * @brief Set @ref CellStructure::use_verlet_list diff --git a/src/core/electrostatics_magnetostatics/mmm1d.cpp b/src/core/electrostatics_magnetostatics/mmm1d.cpp index f8ce1c9ce6b..f2f95305527 100644 --- a/src/core/electrostatics_magnetostatics/mmm1d.cpp +++ b/src/core/electrostatics_magnetostatics/mmm1d.cpp @@ -35,6 +35,7 @@ #include "electrostatics_magnetostatics/mmm-common.hpp" #include "electrostatics_magnetostatics/mmm-modpsi.hpp" +#include "CellStructureType.hpp" #include "cells.hpp" #include "errorhandling.hpp" #include "grid.hpp" @@ -152,7 +153,8 @@ bool MMM1D_sanity_checks() { runtimeErrorMsg() << "MMM1D requires periodicity (0, 0, 1)"; return true; } - if (cell_structure.decomposition_type() != CELL_STRUCTURE_NSQUARE) { + if (cell_structure.decomposition_type() != + CellStructureType::CELL_STRUCTURE_NSQUARE) { runtimeErrorMsg() << "MMM1D requires the N-square cellsystem"; return true; } diff --git a/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp b/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp index 44fba840d01..34f3ab055cf 100644 --- a/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp +++ b/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp @@ -45,6 +45,7 @@ #include "electrostatics_magnetostatics/p3m_interpolation.hpp" #include "electrostatics_magnetostatics/p3m_send_mesh.hpp" +#include "CellStructureType.hpp" #include "Particle.hpp" #include "ParticleRange.hpp" #include "cells.hpp" @@ -1421,7 +1422,8 @@ bool dp3m_sanity_checks(const Utils::Vector3i &grid) { ret = true; } - if (cell_structure.decomposition_type() != CELL_STRUCTURE_REGULAR) { + if (cell_structure.decomposition_type() != + CellStructureType::CELL_STRUCTURE_REGULAR) { runtimeErrorMsg() << "dipolar P3M requires the regular decomposition " "cell system"; ret = true; diff --git a/src/core/electrostatics_magnetostatics/p3m.cpp b/src/core/electrostatics_magnetostatics/p3m.cpp index f6112bbe37b..2c222efd3d2 100644 --- a/src/core/electrostatics_magnetostatics/p3m.cpp +++ b/src/core/electrostatics_magnetostatics/p3m.cpp @@ -31,6 +31,7 @@ #include "electrostatics_magnetostatics/elc.hpp" #include "electrostatics_magnetostatics/p3m_influence_function.hpp" +#include "CellStructureType.hpp" #include "Particle.hpp" #include "ParticleRange.hpp" #include "cells.hpp" @@ -1286,7 +1287,8 @@ bool p3m_sanity_checks_system(const Utils::Vector3i &grid) { ret = true; } - if (cell_structure.decomposition_type() != CELL_STRUCTURE_REGULAR) { + if (cell_structure.decomposition_type() != + CellStructureType::CELL_STRUCTURE_REGULAR) { runtimeErrorMsg() << "P3M requires the regular decomposition cell system"; ret = true; } diff --git a/src/core/event.cpp b/src/core/event.cpp index 2d11b5aa7b8..ecc90dff929 100644 --- a/src/core/event.cpp +++ b/src/core/event.cpp @@ -25,6 +25,7 @@ */ #include "event.hpp" +#include "CellStructureType.hpp" #include "bonded_interactions/thermalized_bond.hpp" #include "cells.hpp" #include "collision.hpp" @@ -90,7 +91,7 @@ void on_program_start() { init_node_grid(); /* initially go for regular decomposition */ - cells_re_init(CELL_STRUCTURE_REGULAR); + cells_re_init(CellStructureType::CELL_STRUCTURE_REGULAR); if (this_node == 0) { /* make sure interaction 0<->0 always exists */ diff --git a/src/core/grid_based_algorithms/lb.cpp b/src/core/grid_based_algorithms/lb.cpp index cc1749da82c..3aac8790b88 100644 --- a/src/core/grid_based_algorithms/lb.cpp +++ b/src/core/grid_based_algorithms/lb.cpp @@ -29,6 +29,7 @@ #include "grid_based_algorithms/lb.hpp" +#include "CellStructureType.hpp" #include "cells.hpp" #include "communication.hpp" #include "errorhandling.hpp" @@ -613,7 +614,8 @@ void lb_sanity_checks(const LB_Parameters &lb_parameters) { if (lb_parameters.viscosity <= 0.0) { runtimeErrorMsg() << "Lattice Boltzmann fluid viscosity not set"; } - if (cell_structure.decomposition_type() != CELL_STRUCTURE_REGULAR) { + if (cell_structure.decomposition_type() != + CellStructureType::CELL_STRUCTURE_REGULAR) { runtimeErrorMsg() << "LB requires regular-decomposition cellsystem"; } } diff --git a/src/python/espressomd/cellsystem.pxd b/src/python/espressomd/cellsystem.pxd index 26f5a477a4e..48092b5e1fe 100644 --- a/src/python/espressomd/cellsystem.pxd +++ b/src/python/espressomd/cellsystem.pxd @@ -35,12 +35,14 @@ cdef extern from "cells.hpp": cdef extern from "communication.hpp": int n_nodes -cdef extern from "cells.hpp": - int CELL_STRUCTURE_REGULAR - int CELL_STRUCTURE_NSQUARE +cdef extern from "CellStructureType.hpp": + ctypedef enum CellStructureType: + CELL_STRUCTURE_REGULAR "CellStructureType::CELL_STRUCTURE_REGULAR" + CELL_STRUCTURE_NSQUARE "CellStructureType::CELL_STRUCTURE_NSQUARE" +cdef extern from "cells.hpp": ctypedef struct CellStructure: - int decomposition_type() + CellStructureType decomposition_type() bool use_verlet_list CellStructure cell_structure diff --git a/src/python/espressomd/cellsystem.pyx b/src/python/espressomd/cellsystem.pyx index baf1e9159ee..3c023ea39f7 100644 --- a/src/python/espressomd/cellsystem.pyx +++ b/src/python/espressomd/cellsystem.pyx @@ -42,7 +42,7 @@ cdef class CellSystem: """ mpi_set_use_verlet_lists(use_verlet_lists) - mpi_bcast_cell_structure(CELL_STRUCTURE_REGULAR) + mpi_bcast_cell_structure(CellStructureType.CELL_STRUCTURE_REGULAR) handle_errors("Error while initializing the cell system.") return True @@ -59,14 +59,14 @@ cdef class CellSystem: """ mpi_set_use_verlet_lists(use_verlet_lists) - mpi_bcast_cell_structure(CELL_STRUCTURE_NSQUARE) + mpi_bcast_cell_structure(CellStructureType.CELL_STRUCTURE_NSQUARE) return True def get_state(self): s = self.__getstate__() - if cell_structure.decomposition_type() == CELL_STRUCTURE_REGULAR: + if cell_structure.decomposition_type() == CellStructureType.CELL_STRUCTURE_REGULAR: rd = get_regular_decomposition() s["cell_grid"] = np.array( [rd.cell_grid[0], rd.cell_grid[1], rd.cell_grid[2]]) @@ -81,9 +81,9 @@ cdef class CellSystem: def __getstate__(self): s = {"use_verlet_list": cell_structure.use_verlet_list} - if cell_structure.decomposition_type() == CELL_STRUCTURE_REGULAR: + if cell_structure.decomposition_type() == CellStructureType.CELL_STRUCTURE_REGULAR: s["type"] = "regular_decomposition" - if cell_structure.decomposition_type() == CELL_STRUCTURE_NSQUARE: + if cell_structure.decomposition_type() == CellStructureType.CELL_STRUCTURE_NSQUARE: s["type"] = "nsquare" s["skin"] = skin From 6c453c432a01a2c5af1dd2d2dfb089437528129d Mon Sep 17 00:00:00 2001 From: Patrick Kreissl Date: Mon, 21 Feb 2022 02:11:50 +0100 Subject: [PATCH 53/99] Store cell_structure_type in LocalBox. --- src/core/CellStructure.cpp | 7 +++++-- src/core/CellStructure.hpp | 10 ++++++---- src/core/LocalBox.hpp | 17 +++++++++++++++-- src/core/actor/Mmm1dgpuForce.cpp | 3 +-- src/core/cells.cpp | 2 +- .../electrostatics_magnetostatics/mmm1d.cpp | 3 +-- .../p3m-dipolar.cpp | 2 +- src/core/electrostatics_magnetostatics/p3m.cpp | 2 +- src/core/grid.cpp | 3 ++- src/core/grid_based_algorithms/lb.cpp | 4 ++-- 10 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/core/CellStructure.cpp b/src/core/CellStructure.cpp index b513367f607..99a813db0fb 100644 --- a/src/core/CellStructure.cpp +++ b/src/core/CellStructure.cpp @@ -242,15 +242,18 @@ void CellStructure::resort_particles(int global_flag) { } void CellStructure::set_atom_decomposition(boost::mpi::communicator const &comm, - BoxGeometry const &box) { + BoxGeometry const &box, + LocalBox &local_geo) { set_particle_decomposition(std::make_unique(comm, box)); m_type = CellStructureType::CELL_STRUCTURE_NSQUARE; + local_geo.set_cell_structure_type(m_type); } void CellStructure::set_regular_decomposition( boost::mpi::communicator const &comm, double range, BoxGeometry const &box, - LocalBox const &local_geo) { + LocalBox &local_geo) { set_particle_decomposition( std::make_unique(comm, range, box, local_geo)); m_type = CellStructureType::CELL_STRUCTURE_REGULAR; + local_geo.set_cell_structure_type(m_type); } diff --git a/src/core/CellStructure.hpp b/src/core/CellStructure.hpp index d815cb87aeb..1f3ee14cc33 100644 --- a/src/core/CellStructure.hpp +++ b/src/core/CellStructure.hpp @@ -490,22 +490,24 @@ struct CellStructure { * @brief Set the particle decomposition to AtomDecomposition. * * @param comm Communicator to use. - * @param box Box Geometry + * @param box Box Geometry. + * @param local_geo Geometry of the local box (holds cell structure type). */ void set_atom_decomposition(boost::mpi::communicator const &comm, - BoxGeometry const &box); + BoxGeometry const &box, + LocalBox &local_geo); /** * @brief Set the particle decomposition to RegularDecomposition. * * @param comm Cartesian communicator to use. * @param range Interaction range. - * @param box Box Geometry + * @param box Box Geometry. * @param local_geo Geometry of the local box. */ void set_regular_decomposition(boost::mpi::communicator const &comm, double range, BoxGeometry const &box, - LocalBox const &local_geo); + LocalBox &local_geo); public: template void bond_loop(BondKernel const &bond_kernel) { diff --git a/src/core/LocalBox.hpp b/src/core/LocalBox.hpp index d9fdb774daa..bd36f0aec08 100644 --- a/src/core/LocalBox.hpp +++ b/src/core/LocalBox.hpp @@ -19,6 +19,7 @@ #ifndef ESPRESSO_LOCALBOX_HPP #define ESPRESSO_LOCALBOX_HPP +#include "CellStructureType.hpp" #include template class LocalBox { @@ -26,15 +27,17 @@ template class LocalBox { Utils::Vector m_lower_corner = {0, 0, 0}; Utils::Vector m_upper_corner = {1, 1, 1}; Utils::Array m_boundaries = {}; + CellStructureType m_cell_structure_type; public: LocalBox() = default; LocalBox(Utils::Vector const &lower_corner, Utils::Vector const &local_box_length, - Utils::Array const &boundaries) + Utils::Array const &boundaries, + CellStructureType const cell_structure_type) : m_local_box_l(local_box_length), m_lower_corner(lower_corner), m_upper_corner(lower_corner + local_box_length), - m_boundaries(boundaries) {} + m_boundaries(boundaries), m_cell_structure_type(cell_structure_type) {} /** Left (bottom, front) corner of this nodes local box. */ Utils::Vector const &my_left() const { return m_lower_corner; } @@ -52,6 +55,16 @@ template class LocalBox { * @return Array with boundary information. */ Utils::Array const &boundary() const { return m_boundaries; } + + /** Return cell structure type. */ + CellStructureType const &cell_structure_type() const { + return m_cell_structure_type; + } + + /** Set cell structure type. */ + void set_cell_structure_type(CellStructureType cell_structure_type) { + m_cell_structure_type = cell_structure_type; + } }; #endif diff --git a/src/core/actor/Mmm1dgpuForce.cpp b/src/core/actor/Mmm1dgpuForce.cpp index a51d114f1d9..1a7e418a05e 100644 --- a/src/core/actor/Mmm1dgpuForce.cpp +++ b/src/core/actor/Mmm1dgpuForce.cpp @@ -26,7 +26,6 @@ #include "electrostatics_magnetostatics/coulomb.hpp" #include "CellStructureType.hpp" -#include "cells.hpp" #include "energy.hpp" #include "forces.hpp" #include "grid.hpp" @@ -42,7 +41,7 @@ Mmm1dgpuForce::Mmm1dgpuForce(SystemInterface &s) { if (box_geo.periodic(0) || box_geo.periodic(1) || !box_geo.periodic(2)) { throw std::runtime_error("MMM1D requires periodicity (0, 0, 1)"); } - if (cell_structure.decomposition_type() != + if (local_geo.cell_structure_type() != CellStructureType::CELL_STRUCTURE_NSQUARE) { throw std::runtime_error("MMM1D requires the N-square cellsystem"); } diff --git a/src/core/cells.cpp b/src/core/cells.cpp index dd4d57b8ab9..ef9151d965a 100644 --- a/src/core/cells.cpp +++ b/src/core/cells.cpp @@ -202,7 +202,7 @@ void cells_re_init(CellStructureType new_cs) { box_geo, local_geo); break; case CellStructureType::CELL_STRUCTURE_NSQUARE: - cell_structure.set_atom_decomposition(comm_cart, box_geo); + cell_structure.set_atom_decomposition(comm_cart, box_geo, local_geo); break; default: throw std::runtime_error("Unknown cell system type"); diff --git a/src/core/electrostatics_magnetostatics/mmm1d.cpp b/src/core/electrostatics_magnetostatics/mmm1d.cpp index f2f95305527..035354e0ab9 100644 --- a/src/core/electrostatics_magnetostatics/mmm1d.cpp +++ b/src/core/electrostatics_magnetostatics/mmm1d.cpp @@ -36,7 +36,6 @@ #include "electrostatics_magnetostatics/mmm-modpsi.hpp" #include "CellStructureType.hpp" -#include "cells.hpp" #include "errorhandling.hpp" #include "grid.hpp" #include "specfunc.hpp" @@ -153,7 +152,7 @@ bool MMM1D_sanity_checks() { runtimeErrorMsg() << "MMM1D requires periodicity (0, 0, 1)"; return true; } - if (cell_structure.decomposition_type() != + if (local_geo.cell_structure_type() != CellStructureType::CELL_STRUCTURE_NSQUARE) { runtimeErrorMsg() << "MMM1D requires the N-square cellsystem"; return true; diff --git a/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp b/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp index 34f3ab055cf..30a7367dcd0 100644 --- a/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp +++ b/src/core/electrostatics_magnetostatics/p3m-dipolar.cpp @@ -1422,7 +1422,7 @@ bool dp3m_sanity_checks(const Utils::Vector3i &grid) { ret = true; } - if (cell_structure.decomposition_type() != + if (local_geo.cell_structure_type() != CellStructureType::CELL_STRUCTURE_REGULAR) { runtimeErrorMsg() << "dipolar P3M requires the regular decomposition " "cell system"; diff --git a/src/core/electrostatics_magnetostatics/p3m.cpp b/src/core/electrostatics_magnetostatics/p3m.cpp index 2c222efd3d2..31dad4964cd 100644 --- a/src/core/electrostatics_magnetostatics/p3m.cpp +++ b/src/core/electrostatics_magnetostatics/p3m.cpp @@ -1287,7 +1287,7 @@ bool p3m_sanity_checks_system(const Utils::Vector3i &grid) { ret = true; } - if (cell_structure.decomposition_type() != + if (local_geo.cell_structure_type() != CellStructureType::CELL_STRUCTURE_REGULAR) { runtimeErrorMsg() << "P3M requires the regular decomposition cell system"; ret = true; diff --git a/src/core/grid.cpp b/src/core/grid.cpp index 88b374299a0..06dda104654 100644 --- a/src/core/grid.cpp +++ b/src/core/grid.cpp @@ -87,7 +87,8 @@ LocalBox regular_decomposition(const BoxGeometry &box, boundaries[2 * dir + 1] = -(node_pos[dir] == node_grid_par[dir] - 1); } - return {my_left, local_length, boundaries}; + return {my_left, local_length, boundaries, + CellStructureType::CELL_STRUCTURE_REGULAR}; } void grid_changed_box_l(const BoxGeometry &box) { diff --git a/src/core/grid_based_algorithms/lb.cpp b/src/core/grid_based_algorithms/lb.cpp index 3aac8790b88..7cac4ed2a0d 100644 --- a/src/core/grid_based_algorithms/lb.cpp +++ b/src/core/grid_based_algorithms/lb.cpp @@ -30,7 +30,6 @@ #include "grid_based_algorithms/lb.hpp" #include "CellStructureType.hpp" -#include "cells.hpp" #include "communication.hpp" #include "errorhandling.hpp" #include "grid.hpp" @@ -51,6 +50,7 @@ #include #include #include +#include #include #include @@ -614,7 +614,7 @@ void lb_sanity_checks(const LB_Parameters &lb_parameters) { if (lb_parameters.viscosity <= 0.0) { runtimeErrorMsg() << "Lattice Boltzmann fluid viscosity not set"; } - if (cell_structure.decomposition_type() != + if (local_geo.cell_structure_type() != CellStructureType::CELL_STRUCTURE_REGULAR) { runtimeErrorMsg() << "LB requires regular-decomposition cellsystem"; } From 559cfd978df3a6bab60af744e1f58c9dced19564 Mon Sep 17 00:00:00 2001 From: Patrick Kreissl Date: Mon, 21 Feb 2022 03:05:22 +0100 Subject: [PATCH 54/99] Add CellStructureType to LocalBox test. --- src/core/unit_tests/LocalBox_test.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/unit_tests/LocalBox_test.cpp b/src/core/unit_tests/LocalBox_test.cpp index 36fc95ed7b6..c0aa5ca1e3d 100644 --- a/src/core/unit_tests/LocalBox_test.cpp +++ b/src/core/unit_tests/LocalBox_test.cpp @@ -21,6 +21,7 @@ #define BOOST_TEST_DYN_LINK #include +#include "CellStructureType.hpp" #include "LocalBox.hpp" #include @@ -53,13 +54,15 @@ BOOST_AUTO_TEST_CASE(constructors) { Utils::Vector const lower_corner = {1., 2., 3.}; Utils::Vector const local_box_length = {4., 5., 6.}; Utils::Array const boundaries = {{{-1, 0, 1, 1, 0, -1}}}; + CellStructureType const type = CellStructureType::CELL_STRUCTURE_REGULAR; auto const box = - LocalBox(lower_corner, local_box_length, boundaries); + LocalBox(lower_corner, local_box_length, boundaries, type); BOOST_CHECK(box.my_left() == lower_corner); BOOST_CHECK(box.length() == local_box_length); BOOST_CHECK(boost::equal(boundaries, box.boundary())); + BOOST_CHECK(box.cell_structure_type() == type); check_length(box); } } From 0b57dfcd0f4244734dc9092ca7ebb76aa044fba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 21 Feb 2022 13:16:06 +0100 Subject: [PATCH 55/99] core: Modernize MPI-IO Use iterators and STL algorithms. Encapsulate MPI_Exscan logic. Roll out new Particle member accessors. Fix -Wconversion warnings. --- src/core/io/mpiio/mpiio.cpp | 243 ++++++++++-------- src/core/io/mpiio/mpiio.hpp | 2 + src/script_interface/mpiio/initialize.cpp | 2 +- .../mpiio/{si_mpiio.hpp => mpiio.hpp} | 4 +- 4 files changed, 142 insertions(+), 109 deletions(-) rename src/script_interface/mpiio/{si_mpiio.hpp => mpiio.hpp} (95%) diff --git a/src/core/io/mpiio/mpiio.cpp b/src/core/io/mpiio/mpiio.cpp index 13f6c1a97b0..11ebdea93b8 100644 --- a/src/core/io/mpiio/mpiio.cpp +++ b/src/core/io/mpiio/mpiio.cpp @@ -22,29 +22,29 @@ * * Concerning the file layouts. * - Scalar arrays are written like this: - * rank0 --- rank1 --- rank2 ... + * rank0 --- rank1 --- rank2 ... * where each rank dumps its scalars in the ordering of the particles. * - Vector arrays are written in the rank ordering like scalar arrays. - * The ordering of the vector data is: v[0] v[1] v[2], so the data + * The ordering of the vector data is: v[0] v[1] v[2], so the data * looks like this: - * v1[0] v1[1] v1[2] v2[0] v2[1] v2[2] v3[0] ... + * v1[0] v1[1] v1[2] v2[0] v2[1] v2[2] v3[0] ... * * To be able to determine the rank boundaries (a multiple of - * nlocalparts), the file 1.pref is written, which dumps the Exscan - * results of nlocalparts, i.e. the prefixes in scalar arrays: + * @c nlocalparts), the file 1.pref is written, which dumps the partial + * sum of @c nlocalparts, i.e. the prefixes in scalar arrays: * - 1.prefs looks like this: - * 0 nlocalpats_rank0 nlocalparts_rank0+nlocalparts_rank1 ... + * 0 nlocalpats_rank0 nlocalparts_rank0+nlocalparts_rank1 ... * * Bonds are dumped as two arrays, namely 1.bond which stores the * bonding partners of the particles and 1.boff which stores the * iteration indices for each particle. - * - 1.boff is a scalar array of size (nlocalpart + 1) per rank. - * - The last element (at index nlocalpart) of 1.boff's subpart - * [rank * (nlocalpart + 1) : (rank + 1) * (nlocalpart + 1)] - * determines the number of bonds for processor "rank". + * - 1.boff is a scalar array of size (nlocalpart + 1) per rank. + * - The last element (at index @c nlocalpart) of 1.boff's subpart + * [rank * (nlocalpart + 1) : (rank + 1) * (nlocalpart + 1)] + * determines the number of bonds for processor @c rank. * - In this subarray one can find the bonding partners of particle - * id[i]. The iteration indices for local part of 1.bonds are: - * subarray[i] : subarray[i+1] + * id[i]. The iteration indices for local part of 1.bonds are: + * subarray[i] : subarray[i+1] * - Take a look at the bond input code. It's easy to understand. */ @@ -65,6 +65,8 @@ #include +#include +#include #include #include #include @@ -123,7 +125,7 @@ static bool fatal_error(char const *msg, std::string const &fn, MPI_File *fp, int buf_len; MPI_Error_string(errnum, buf, &buf_len); buf[buf_len] = '\0'; - // close file handle if open + // close file handle if (fp) { MPI_File_close(fp); } @@ -142,11 +144,11 @@ static bool fatal_error(char const *msg, std::string const &fn, MPI_File *fp, * @param MPI_T The MPI datatype corresponding to the template parameter @p T */ template -static void mpiio_dump_array(const std::string &fn, T *arr, std::size_t len, - std::size_t pref, MPI_Datatype MPI_T) { +static void mpiio_dump_array(const std::string &fn, T const *arr, + std::size_t len, std::size_t pref, + MPI_Datatype MPI_T) { MPI_File f; int ret; - ret = MPI_File_open(MPI_COMM_WORLD, const_cast(fn.c_str()), // MPI_MODE_EXCL: Prohibit overwriting MPI_MODE_WRONLY | MPI_MODE_CREATE | MPI_MODE_EXCL, @@ -154,13 +156,27 @@ static void mpiio_dump_array(const std::string &fn, T *arr, std::size_t len, if (ret) { fatal_error("Could not open file", fn, &f, ret); } - ret = MPI_File_set_view(f, pref * sizeof(T), MPI_T, MPI_T, - const_cast("native"), MPI_INFO_NULL); - ret |= MPI_File_write_all(f, arr, len, MPI_T, MPI_STATUS_IGNORE); + auto const offset = + static_cast(pref) * static_cast(sizeof(T)); + ret = MPI_File_set_view(f, offset, MPI_T, MPI_T, const_cast("native"), + MPI_INFO_NULL); + ret |= MPI_File_write_all(f, arr, static_cast(len), MPI_T, + MPI_STATUS_IGNORE); static_cast(ret and fatal_error("Could not write file", fn, &f, ret)); MPI_File_close(&f); } +/** + * @brief Calculate the file offset on the local node. + * @param n_items Number of items on the local node. + * @return The number of items on all nodes with lower rank. + */ +static unsigned long mpi_calculate_file_offset(unsigned long n_items) { + unsigned long offset = 0ul; + MPI_Exscan(&n_items, &offset, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); + return offset; +} + /** * @brief Dump the fields and bond information. * To be called by the head node only. @@ -169,89 +185,89 @@ static void mpiio_dump_array(const std::string &fn, T *arr, std::size_t len, * @param fields The dumped fields */ static void dump_info(const std::string &fn, unsigned fields) { + // MPI-IO requires consecutive bond ids + auto const nbonds = bonded_ia_params.size(); + assert(static_cast(bonded_ia_params.get_next_key()) == nbonds); + FILE *f = fopen(fn.c_str(), "wb"); if (!f) { fatal_error("Could not open file", fn); } static std::vector npartners; - bool success = (fwrite(&fields, sizeof(fields), 1, f) == 1); + bool success = (fwrite(&fields, sizeof(fields), 1u, f) == 1); // Pack the necessary information of bonded_ia_params: // The number of partners. This is needed to interpret the bond IntList. - if (bonded_ia_params.size() > npartners.size()) - npartners.resize(bonded_ia_params.size()); + if (nbonds > npartners.size()) + npartners.resize(nbonds); - for (int i = 0; i < bonded_ia_params.size(); ++i) { - npartners[i] = number_of_partners(*bonded_ia_params.at(i)); + auto npartners_it = npartners.begin(); + for (int i = 0; i < bonded_ia_params.get_next_key(); ++i, ++npartners_it) { + *npartners_it = number_of_partners(*bonded_ia_params.at(i)); } - auto ia_params_size = static_cast(bonded_ia_params.size()); + success = success && (fwrite(&nbonds, sizeof(std::size_t), 1u, f) == 1); success = - success && (fwrite(&ia_params_size, sizeof(std::size_t), 1, f) == 1); - success = - success && (fwrite(npartners.data(), sizeof(int), bonded_ia_params.size(), - f) == bonded_ia_params.size()); + success && (fwrite(npartners.data(), sizeof(int), nbonds, f) == nbonds); fclose(f); static_cast(success or fatal_error("Could not write file", fn)); } void mpi_mpiio_common_write(const std::string &prefix, unsigned fields, const ParticleRange &particles) { - auto const nlocalpart = static_cast(particles.size()); - // Keep static buffers in order not having to allocate them on every + auto const nlocalpart = static_cast(particles.size()); + auto const offset = mpi_calculate_file_offset(nlocalpart); + // Keep static buffers in order to avoid allocating them on every // function call static std::vector pos, vel; static std::vector id, type; - // Nlocalpart prefixes - // Prefixes based for arrays: 3 * pref for vel, pos. - int pref = 0, bpref = 0; - MPI_Exscan(&nlocalpart, &pref, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); - // Realloc static buffers if necessary if (nlocalpart > id.size()) id.resize(nlocalpart); - if (fields & MPIIO_OUT_POS && 3 * nlocalpart > pos.size()) - pos.resize(3 * nlocalpart); - if (fields & MPIIO_OUT_VEL && 3 * nlocalpart > vel.size()) - vel.resize(3 * nlocalpart); + if (fields & MPIIO_OUT_POS && 3ul * nlocalpart > pos.size()) + pos.resize(3ul * nlocalpart); + if (fields & MPIIO_OUT_VEL && 3ul * nlocalpart > vel.size()) + vel.resize(3ul * nlocalpart); if (fields & MPIIO_OUT_TYP && nlocalpart > type.size()) type.resize(nlocalpart); // Pack the necessary information - // Esp. rescale the velocities. - int i1 = 0, i3 = 0; + auto id_it = id.begin(); + auto type_it = type.begin(); + auto pos_it = pos.begin(); + auto vel_it = vel.begin(); for (auto const &p : particles) { - id[i1] = p.p.identity; + *id_it = p.id(); + ++id_it; if (fields & MPIIO_OUT_POS) { - pos[i3] = p.r.p[0]; - pos[i3 + 1] = p.r.p[1]; - pos[i3 + 2] = p.r.p[2]; + std::copy_n(std::begin(p.pos()), 3u, pos_it); + pos_it += 3u; } if (fields & MPIIO_OUT_VEL) { - vel[i3] = p.m.v[0]; - vel[i3 + 1] = p.m.v[1]; - vel[i3 + 2] = p.m.v[2]; + std::copy_n(std::begin(p.v()), 3u, vel_it); + vel_it += 3u; } if (fields & MPIIO_OUT_TYP) { - type[i1] = p.p.type; + *type_it = p.type(); + ++type_it; } - i1++; - i3 += 3; } int rank; MPI_Comm_rank(MPI_COMM_WORLD, &rank); if (rank == 0) dump_info(prefix + ".head", fields); - mpiio_dump_array(prefix + ".pref", &pref, 1, rank, MPI_INT); - mpiio_dump_array(prefix + ".id", id.data(), nlocalpart, pref, MPI_INT); + auto const pref_offset = static_cast(rank); + mpiio_dump_array(prefix + ".pref", &offset, 1ul, pref_offset, + MPI_UNSIGNED_LONG); + mpiio_dump_array(prefix + ".id", id.data(), nlocalpart, offset, MPI_INT); if (fields & MPIIO_OUT_POS) - mpiio_dump_array(prefix + ".pos", pos.data(), 3 * nlocalpart, - 3 * pref, MPI_DOUBLE); + mpiio_dump_array(prefix + ".pos", pos.data(), 3ul * nlocalpart, + 3ul * offset, MPI_DOUBLE); if (fields & MPIIO_OUT_VEL) - mpiio_dump_array(prefix + ".vel", vel.data(), 3 * nlocalpart, - 3 * pref, MPI_DOUBLE); + mpiio_dump_array(prefix + ".vel", vel.data(), 3ul * nlocalpart, + 3ul * offset, MPI_DOUBLE); if (fields & MPIIO_OUT_TYP) - mpiio_dump_array(prefix + ".type", type.data(), nlocalpart, pref, + mpiio_dump_array(prefix + ".type", type.data(), nlocalpart, offset, MPI_INT); if (fields & MPIIO_OUT_BND) { @@ -270,12 +286,13 @@ void mpi_mpiio_common_write(const std::string &prefix, unsigned fields, } // Determine the prefixes in the bond file - int bonds_size = static_cast(bonds.size()); - MPI_Exscan(&bonds_size, &bpref, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + auto const bonds_size = static_cast(bonds.size()); + auto const bonds_offset = mpi_calculate_file_offset(bonds_size); - mpiio_dump_array(prefix + ".boff", &bonds_size, 1, rank, MPI_INT); - mpiio_dump_array(prefix + ".bond", bonds.data(), bonds.size(), bpref, - MPI_CHAR); + mpiio_dump_array(prefix + ".boff", &bonds_size, 1ul, + pref_offset, MPI_UNSIGNED_LONG); + mpiio_dump_array(prefix + ".bond", bonds.data(), bonds.size(), + bonds_offset, MPI_CHAR); } } @@ -287,7 +304,7 @@ void mpi_mpiio_common_write(const std::string &prefix, unsigned fields, * @param elem_sz Size of a single element * @return The number of elements stored in the file */ -static int get_num_elem(const std::string &fn, std::size_t elem_sz) { +static unsigned long get_num_elem(const std::string &fn, std::size_t elem_sz) { // Could also be done via MPI_File_open, MPI_File_get_size, // MPI_File_close. struct stat st; @@ -296,7 +313,7 @@ static int get_num_elem(const std::string &fn, std::size_t elem_sz) { auto const reason = strerror(errno); fatal_error("Could not get file size of", fn, reason); } - return static_cast(st.st_size / elem_sz); + return static_cast(st.st_size) / elem_sz; } /** @@ -315,17 +332,18 @@ static void mpiio_read_array(const std::string &fn, T *arr, std::size_t len, std::size_t pref, MPI_Datatype MPI_T) { MPI_File f; int ret; - ret = MPI_File_open(MPI_COMM_WORLD, const_cast(fn.c_str()), MPI_MODE_RDONLY, MPI_INFO_NULL, &f); if (ret) { fatal_error("Could not open file", fn, &f, ret); } + auto const offset = + static_cast(pref) * static_cast(sizeof(T)); + ret = MPI_File_set_view(f, offset, MPI_T, MPI_T, const_cast("native"), + MPI_INFO_NULL); - ret = MPI_File_set_view(f, pref * sizeof(T), MPI_T, MPI_T, - const_cast("native"), MPI_INFO_NULL); - - ret |= MPI_File_read_all(f, arr, len, MPI_T, MPI_STATUS_IGNORE); + ret |= MPI_File_read_all(f, arr, static_cast(len), MPI_T, + MPI_STATUS_IGNORE); static_cast(ret and fatal_error("Could not read file", fn, &f, ret)); MPI_File_close(&f); } @@ -363,16 +381,19 @@ static unsigned read_head(const std::string &fn, int rank) { * @param nglobalpart The global amount of particles * @return The prefix and the local number of particles. */ -static std::tuple read_prefs(const std::string &fn, int rank, - int size, int nglobalpart) { - int pref = 0; - int nlocalpart = 0; - mpiio_read_array(fn, &pref, 1, rank, MPI_INT); +static std::tuple +read_prefs(const std::string &fn, int rank, int size, + unsigned long nglobalpart) { + auto const pref_offset = static_cast(rank); + unsigned long pref = 0ul; + unsigned long nlocalpart = 0ul; + mpiio_read_array(fn, &pref, 1ul, pref_offset, + MPI_UNSIGNED_LONG); if (rank > 0) - MPI_Send(&pref, 1, MPI_INT, rank - 1, 0, MPI_COMM_WORLD); + MPI_Send(&pref, 1, MPI_UNSIGNED_LONG, rank - 1, 0, MPI_COMM_WORLD); if (rank < size - 1) - MPI_Recv(&nlocalpart, 1, MPI_INT, rank + 1, MPI_ANY_TAG, MPI_COMM_WORLD, - MPI_STATUS_IGNORE); + MPI_Recv(&nlocalpart, 1, MPI_UNSIGNED_LONG, rank + 1, MPI_ANY_TAG, + MPI_COMM_WORLD, MPI_STATUS_IGNORE); else nlocalpart = nglobalpart; nlocalpart -= pref; @@ -385,10 +406,10 @@ void mpi_mpiio_common_read(const std::string &prefix, unsigned fields) { int size, rank; MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); - auto const nproc = get_num_elem(prefix + ".pref", sizeof(int)); + auto const nproc = get_num_elem(prefix + ".pref", sizeof(unsigned long)); auto const nglobalpart = get_num_elem(prefix + ".id", sizeof(int)); - if (rank == 0 && nproc != size) { + if (rank == 0 && nproc != static_cast(size)) { fatal_error("Trying to read a file with a different COMM " "size than at point of writing."); } @@ -405,7 +426,7 @@ void mpi_mpiio_common_read(const std::string &prefix, unsigned fields) { // Read own prefix (1 int at prefix rank). // Communicate own prefix to rank-1 // Determine nlocalpart (prefix of rank+1 - own prefix) on every node. - int pref, nlocalpart; + unsigned long pref, nlocalpart; std::tie(pref, nlocalpart) = read_prefs(prefix + ".pref", rank, size, nglobalpart); @@ -415,22 +436,26 @@ void mpi_mpiio_common_read(const std::string &prefix, unsigned fields) { // 1.id on all nodes: // Read nlocalpart ints at defined prefix. std::vector id(nlocalpart); + auto id_it = id.begin(); mpiio_read_array(prefix + ".id", id.data(), nlocalpart, pref, MPI_INT); - for (int i = 0; i < nlocalpart; ++i) { - particles[i].p.identity = id[i]; + for (auto &p : particles) { + p.id() = *id_it; + ++id_it; } } if (fields & MPIIO_OUT_POS) { // 1.pos on all nodes: // Read nlocalpart * 3 doubles at defined prefix * 3 - std::vector pos(3 * nlocalpart); - mpiio_read_array(prefix + ".pos", pos.data(), 3 * nlocalpart, - 3 * pref, MPI_DOUBLE); + std::vector pos(3ul * nlocalpart); + auto pos_it = pos.begin(); + mpiio_read_array(prefix + ".pos", pos.data(), 3ul * nlocalpart, + 3ul * pref, MPI_DOUBLE); - for (int i = 0; i < nlocalpart; ++i) { - particles[i].r.p = {pos[3 * i + 0], pos[3 * i + 1], pos[3 * i + 2]}; + for (auto &p : particles) { + std::copy_n(pos_it, 3u, std::begin(p.pos())); + pos_it += 3u; } } @@ -438,41 +463,47 @@ void mpi_mpiio_common_read(const std::string &prefix, unsigned fields) { // 1.type on all nodes: // Read nlocalpart ints at defined prefix. std::vector type(nlocalpart); + auto type_it = type.begin(); mpiio_read_array(prefix + ".type", type.data(), nlocalpart, pref, MPI_INT); - for (int i = 0; i < nlocalpart; ++i) - particles[i].p.type = type[i]; + for (auto &p : particles) { + p.type() = *type_it; + ++type_it; + } } if (fields & MPIIO_OUT_VEL) { // 1.vel on all nodes: // Read nlocalpart * 3 doubles at defined prefix * 3 - std::vector vel(3 * nlocalpart); - mpiio_read_array(prefix + ".vel", vel.data(), 3 * nlocalpart, - 3 * pref, MPI_DOUBLE); + std::vector vel(3ul * nlocalpart); + auto vel_it = vel.begin(); + mpiio_read_array(prefix + ".vel", vel.data(), 3ul * nlocalpart, + 3ul * pref, MPI_DOUBLE); - for (int i = 0; i < nlocalpart; ++i) - particles[i].m.v = {vel[3 * i + 0], vel[3 * i + 1], vel[3 * i + 2]}; + for (auto &p : particles) { + std::copy_n(vel_it, 3u, std::begin(p.v())); + vel_it += 3u; + } } if (fields & MPIIO_OUT_BND) { // 1.boff - // 1 int per process - int bonds_size = 0; - mpiio_read_array(prefix + ".boff", &bonds_size, 1, rank, MPI_INT); - int bpref = 0; - MPI_Exscan(&bonds_size, &bpref, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + // 1 long int per process + auto const pref_offset = static_cast(rank); + unsigned long bonds_size = 0u; + mpiio_read_array(prefix + ".boff", &bonds_size, 1ul, + pref_offset, MPI_UNSIGNED_LONG); + auto const bonds_offset = mpi_calculate_file_offset(bonds_size); // 1.bond // nlocalbonds ints per process std::vector bond(bonds_size); - mpiio_read_array(prefix + ".bond", bond.data(), bonds_size, bpref, - MPI_CHAR); + mpiio_read_array(prefix + ".bond", bond.data(), bonds_size, + bonds_offset, MPI_CHAR); - namespace io = boost::iostreams; - io::array_source src(bond.data(), bond.size()); - io::stream ss(src); + boost::iostreams::array_source src(bond.data(), bond.size()); + boost::iostreams::stream ss(src); boost::archive::binary_iarchive ia(ss); for (auto &p : particles) { diff --git a/src/core/io/mpiio/mpiio.hpp b/src/core/io/mpiio/mpiio.hpp index 29c3ce78462..3ab09275416 100644 --- a/src/core/io/mpiio/mpiio.hpp +++ b/src/core/io/mpiio/mpiio.hpp @@ -27,6 +27,8 @@ #include "ParticleRange.hpp" +#include + namespace Mpiio { /** diff --git a/src/script_interface/mpiio/initialize.cpp b/src/script_interface/mpiio/initialize.cpp index 95f18615f4f..954e2dba466 100644 --- a/src/script_interface/mpiio/initialize.cpp +++ b/src/script_interface/mpiio/initialize.cpp @@ -18,7 +18,7 @@ */ #include "initialize.hpp" -#include "si_mpiio.hpp" +#include "mpiio.hpp" namespace ScriptInterface { namespace MPIIO { diff --git a/src/script_interface/mpiio/si_mpiio.hpp b/src/script_interface/mpiio/mpiio.hpp similarity index 95% rename from src/script_interface/mpiio/si_mpiio.hpp rename to src/script_interface/mpiio/mpiio.hpp index 02f54ff6e35..72ab1dbd056 100644 --- a/src/script_interface/mpiio/si_mpiio.hpp +++ b/src/script_interface/mpiio/mpiio.hpp @@ -19,8 +19,8 @@ * along with this program. If not, see . */ -#ifndef ESPRESSO_SCRIPTINTERFACE_MPIIO_HPP -#define ESPRESSO_SCRIPTINTERFACE_MPIIO_HPP +#ifndef ESPRESSO_SCRIPT_INTERFACE_MPIIO_MPIIO_HPP +#define ESPRESSO_SCRIPT_INTERFACE_MPIIO_MPIIO_HPP #include "script_interface/ScriptInterface.hpp" #include "script_interface/auto_parameters/AutoParameters.hpp" From a58262c018af92a90e3aa131c9f378fbaad00393 Mon Sep 17 00:00:00 2001 From: mebrito Date: Fri, 4 Feb 2022 18:28:44 +0100 Subject: [PATCH 56/99] Make VerletCriterion a class template --- src/core/forces.cpp | 4 ++-- .../nonbonded_interactions/VerletCriterion.hpp | 15 +++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/core/forces.cpp b/src/core/forces.cpp index c7eb548456f..526dc079d24 100644 --- a/src/core/forces.cpp +++ b/src/core/forces.cpp @@ -187,8 +187,8 @@ void force_calc(CellStructure &cell_structure, double time_step, double kT) { #endif }, maximal_cutoff(), maximal_cutoff_bonded(), - VerletCriterion{skin, interaction_range(), coulomb_cutoff, dipole_cutoff, - collision_detection_cutoff()}); + VerletCriterion<>{skin, interaction_range(), coulomb_cutoff, + dipole_cutoff, collision_detection_cutoff()}); Constraints::constraints.add_forces(particles, get_sim_time()); diff --git a/src/core/nonbonded_interactions/VerletCriterion.hpp b/src/core/nonbonded_interactions/VerletCriterion.hpp index 73e67366015..d6a62a40f03 100644 --- a/src/core/nonbonded_interactions/VerletCriterion.hpp +++ b/src/core/nonbonded_interactions/VerletCriterion.hpp @@ -28,10 +28,16 @@ #include #include +struct GetNonbondedCutoff { + auto operator()(int type_i, int type_j) const { + return get_ia_param(type_i, type_j)->max_cut; + } +}; + /** Returns true if the particles are to be considered for short range * interactions. */ -class VerletCriterion { +template class VerletCriterion { const double m_skin; const double m_eff_max_cut2; const double m_eff_coulomb_cut2 = 0.; @@ -42,6 +48,7 @@ class VerletCriterion { return INACTIVE_CUTOFF; return Utils::sqr(x + m_skin); } + CutoffGetter get_nonbonded_cutoff; public: VerletCriterion(double skin, double max_cut, double coulomb_cut = 0., @@ -78,9 +85,9 @@ class VerletCriterion { #endif // Within short-range distance (including dpd and the like) - auto const max_cut = get_ia_param(p1.p.type, p2.p.type)->max_cut; - return (max_cut != INACTIVE_CUTOFF) && - (dist2 <= Utils::sqr(max_cut + m_skin)); + auto const ia_cut = get_nonbonded_cutoff(p1.p.type, p2.p.type); + return (ia_cut != INACTIVE_CUTOFF) && + (dist2 <= Utils::sqr(ia_cut + m_skin)); } }; #endif From 137f09996652bad2ca5cd0ed629251acb7f384dc Mon Sep 17 00:00:00 2001 From: mebrito Date: Thu, 24 Feb 2022 14:36:20 +0100 Subject: [PATCH 57/99] Create unit test for VerletCriterion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test all conditional branches in the VerletCriterion. Co-authored-by: Jean-Noël Grad --- src/core/unit_tests/CMakeLists.txt | 2 + src/core/unit_tests/VerletCriterion_test.cpp | 108 +++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 src/core/unit_tests/VerletCriterion_test.cpp diff --git a/src/core/unit_tests/CMakeLists.txt b/src/core/unit_tests/CMakeLists.txt index b3771dd21e6..185fd3c0bb8 100644 --- a/src/core/unit_tests/CMakeLists.txt +++ b/src/core/unit_tests/CMakeLists.txt @@ -51,6 +51,8 @@ unit_test(NAME Lattice_test SRC Lattice_test.cpp DEPENDS EspressoCore) unit_test(NAME lb_exceptions SRC lb_exceptions.cpp DEPENDS EspressoCore) unit_test(NAME Verlet_list_test SRC Verlet_list_test.cpp DEPENDS EspressoCore NUM_PROC 4) +unit_test(NAME VerletCriterion_test SRC VerletCriterion_test.cpp DEPENDS + EspressoCore) unit_test(NAME thermostats_test SRC thermostats_test.cpp DEPENDS EspressoCore) unit_test(NAME random_test SRC random_test.cpp DEPENDS EspressoUtils Random123) unit_test(NAME BondList_test SRC BondList_test.cpp DEPENDS EspressoCore) diff --git a/src/core/unit_tests/VerletCriterion_test.cpp b/src/core/unit_tests/VerletCriterion_test.cpp new file mode 100644 index 00000000000..9de2de54834 --- /dev/null +++ b/src/core/unit_tests/VerletCriterion_test.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 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 . + */ + +#define BOOST_TEST_MODULE Verlet criterion checks +#define BOOST_TEST_DYN_LINK +#include + +#include "CellStructure.hpp" +#include "Particle.hpp" +#include "config.hpp" +#include "nonbonded_interactions/VerletCriterion.hpp" +#include "particle_data.hpp" + +BOOST_AUTO_TEST_CASE(VerletCriterion_test) { + auto constexpr skin = 0.4; + auto constexpr max_cut = 2.5; + auto constexpr coulomb_cut = 2.0; + auto constexpr dipolar_cut = 1.8; + auto constexpr collision_cut = 1.6; + + struct GetMaxCutoff { + double operator()(int, int) const { return skin + max_cut; } + }; + struct GetZeroCutoff { + double operator()(int, int) const { return -skin; } + }; + + VerletCriterion criterion(skin, max_cut); + VerletCriterion criterion_inactive(skin, INACTIVE_CUTOFF); + VerletCriterion criterion_long_range( + skin, max_cut, coulomb_cut, dipolar_cut, collision_cut); + + Particle p1, p2; + p1.id() = 1; + p2.id() = 2; + + { + auto constexpr cutoff = skin + max_cut; + auto const below = Distance{Utils::Vector3d{cutoff - 0.1, 0.0, 0.0}}; + auto const above = Distance{Utils::Vector3d{cutoff + 0.1, 0.0, 0.0}}; + BOOST_CHECK(criterion(p1, p2, below)); + BOOST_CHECK(!criterion_inactive(p1, p2, below)); + BOOST_CHECK(!criterion(p1, p2, above)); + BOOST_CHECK(!criterion_inactive(p1, p2, above)); + } + +#ifdef ELECTROSTATICS + { + auto constexpr cutoff = skin + coulomb_cut; + auto const below = Distance{Utils::Vector3d{cutoff - 0.1, 0.0, 0.0}}; + auto const above = Distance{Utils::Vector3d{cutoff + 0.1, 0.0, 0.0}}; + BOOST_CHECK(!criterion_long_range(p1, p2, below)); + BOOST_CHECK(!criterion_long_range(p1, p2, above)); + p2.q() = 1.; + BOOST_CHECK(!criterion_long_range(p1, p2, below)); + BOOST_CHECK(!criterion_long_range(p1, p2, above)); + p1.q() = 1.; + BOOST_CHECK(criterion_long_range(p1, p2, below)); + BOOST_CHECK(!criterion_long_range(p1, p2, above)); + p1.q() = 0.; + p2.q() = 0.; + } +#endif // ELECTROSTATICS + +#ifdef DIPOLES + { + auto constexpr cutoff = skin + dipolar_cut; + auto const below = Distance{Utils::Vector3d{cutoff - 0.1, 0.0, 0.0}}; + auto const above = Distance{Utils::Vector3d{cutoff + 0.1, 0.0, 0.0}}; + BOOST_CHECK(!criterion_long_range(p1, p2, below)); + BOOST_CHECK(!criterion_long_range(p1, p2, above)); + p2.dipm() = 1.; + BOOST_CHECK(!criterion_long_range(p1, p2, below)); + BOOST_CHECK(!criterion_long_range(p1, p2, above)); + p1.dipm() = 1.; + BOOST_CHECK(criterion_long_range(p1, p2, below)); + BOOST_CHECK(!criterion_long_range(p1, p2, above)); + p1.dipm() = 0.; + p2.dipm() = 0.; + } +#endif // DIPOLES + +#ifdef COLLISION_DETECTION + { + auto constexpr cutoff = skin + collision_cut; + auto const below = Distance{Utils::Vector3d{cutoff - 0.1, 0.0, 0.0}}; + auto const above = Distance{Utils::Vector3d{cutoff + 0.1, 0.0, 0.0}}; + BOOST_CHECK(criterion_long_range(p1, p2, below)); + BOOST_CHECK(!criterion_long_range(p1, p2, above)); + } +#endif // COLLISION_DETECTION +} From af9055a6984226d97fb3afce78b2ff08961be877 Mon Sep 17 00:00:00 2001 From: Rudolf Weeber Date: Thu, 20 Jan 2022 16:08:45 +0100 Subject: [PATCH 58/99] Implement bond breakage --- src/core/CMakeLists.txt | 1 + src/core/bond_breakage.cpp | 232 ++++++++++++++++++ src/core/bond_breakage.hpp | 50 ++++ src/core/forces.cpp | 3 +- src/core/forces_inline.hpp | 10 + src/core/integrate.cpp | 2 + src/core/particle_data.cpp | 30 +++ src/core/particle_data.hpp | 15 +- src/python/espressomd/bond_breakage.py | 30 +++ src/python/espressomd/script_interface.pyx | 83 ++++++- src/python/espressomd/system.pyx | 5 + src/script_interface/CMakeLists.txt | 1 + src/script_interface/ObjectMap.hpp | 13 + .../bond_breakage/BreakageSpec.hpp | 54 ++++ .../bond_breakage/BreakageSpecs.hpp | 64 +++++ .../bond_breakage/CMakeLists.txt | 2 + .../bond_breakage/initialize.cpp | 31 +++ .../bond_breakage/initialize.hpp | 31 +++ src/script_interface/initialize.cpp | 2 + testsuite/python/CMakeLists.txt | 1 + testsuite/python/bond_breakage.py | 152 ++++++++++++ testsuite/python/save_checkpoint.py | 5 + testsuite/python/test_checkpoint.py | 11 + 23 files changed, 824 insertions(+), 4 deletions(-) create mode 100644 src/core/bond_breakage.cpp create mode 100644 src/core/bond_breakage.hpp create mode 100644 src/python/espressomd/bond_breakage.py create mode 100644 src/script_interface/bond_breakage/BreakageSpec.hpp create mode 100644 src/script_interface/bond_breakage/BreakageSpecs.hpp create mode 100644 src/script_interface/bond_breakage/CMakeLists.txt create mode 100644 src/script_interface/bond_breakage/initialize.cpp create mode 100644 src/script_interface/bond_breakage/initialize.hpp create mode 100644 testsuite/python/bond_breakage.py diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8a24cfe27f0..4702a0b2baf 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,5 +1,6 @@ set(EspressoCore_SRC accumulators.cpp + bond_breakage.cpp bond_error.cpp cells.cpp collision.cpp diff --git a/src/core/bond_breakage.cpp b/src/core/bond_breakage.cpp new file mode 100644 index 00000000000..9549fa15417 --- /dev/null +++ b/src/core/bond_breakage.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) 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 . + */ +#include "bond_breakage.hpp" +#include "cells.hpp" +#include "communication.hpp" +#include "errorhandling.hpp" +#include "particle_data.hpp" + +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace BondBreakage { + +// Bond breakage specifications +std::unordered_map> breakage_specs; + +// Delete Actions +struct DeleteBond { + int particle_id; + int bond_partner_id; + int bond_type; + std::size_t hash_value() const { + std::size_t seed = 3875; + boost::hash_combine(seed, particle_id); + boost::hash_combine(seed, bond_partner_id); + boost::hash_combine(seed, bond_type); + return seed; + } + bool operator==(DeleteBond const &rhs) const { + return rhs.particle_id == particle_id and + rhs.bond_partner_id == bond_partner_id and + rhs.bond_type == bond_type; + } +}; + +struct DeleteAllBonds { + int particle_id_1; + int particle_id_2; + std::size_t hash_value() const { + std::size_t seed = 75; + boost::hash_combine(seed, particle_id_1); + boost::hash_combine(seed, particle_id_2); + return seed; + } + bool operator==(DeleteAllBonds const &rhs) const { + return rhs.particle_id_1 == particle_id_1 and + rhs.particle_id_2 == particle_id_2; + } +}; +} // namespace BondBreakage + +// Hash support for std::unordered_set +namespace boost { +template <> struct hash { + std::size_t operator()(BondBreakage::DeleteBond const &t) const noexcept { + return t.hash_value(); + } +}; +template <> struct hash { + std::size_t operator()(BondBreakage::DeleteAllBonds const &t) const noexcept { + return t.hash_value(); + } +}; +} // namespace boost + +namespace BondBreakage { +// Variant holding any of the actions +using Action = boost::variant; + +// Set of actions +using ActionSet = std::unordered_set; + +// Broken bond record +struct QueueEntry { + int particle_id; + int bond_partner_id; + int bond_type; + + /// Serialization for synchronization across mpi ranks + friend class boost::serialization::access; + template + void serialize(Archive &ar, const unsigned int version) { + ar &particle_id; + ar &bond_partner_id; + ar &bond_type; + } +}; + +/** @brief Queue to record bonds broken during a time step */ +using Queue = std::vector; +Queue queue; + +/** @brief Retrieve breakage specification for the bond type */ +boost::optional get_breakage_spec(int bond_type) { + if (breakage_specs.find(bond_type) != breakage_specs.end()) { + return {*(breakage_specs.at(bond_type))}; + } + return {}; +} + +/** Add a particle+bond combination to the breakage queue */ +void queue_breakage(int particle_id, int bond_partner_id, int bond_type) { + queue.emplace_back(QueueEntry{particle_id, bond_partner_id, bond_type}); +} + +bool check_and_handle_breakage(int particle_id, int bond_partner_id, + int bond_type, double distance) { + // Retrieve specification for this bond type + auto spec = get_breakage_spec(bond_type); + if (!spec) + return false; // No breakage rule for this bond type + + // Is the bond length longer than the breakage length? + if (distance >= (*spec).breakage_length) { + queue_breakage(particle_id, bond_partner_id, bond_type); + return true; + } + return false; +} + +void clear_queue() { queue.clear(); } + +/** @brief Gathers combined queue from all mpi ranks */ +Queue gather_global_queue(Queue const &local_queue) { + Queue res = local_queue; + Utils::Mpi::gather_buffer(res, comm_cart); + boost::mpi::broadcast(comm_cart, res, 0); + return res; +} + +/** @brief Constructs the actions to take for a breakage queue entry */ +ActionSet actions_for_breakage(QueueEntry const &e) { + // Retrieve relevant breakage spec + auto const spec = get_breakage_spec(e.bond_type); + assert(spec); + + // Handle different action types + if ((*spec).action_type == ActionType::DELETE_BOND) + return {DeleteBond{e.particle_id, e.bond_partner_id, e.bond_type}}; +#ifdef VIRTUAL_SITES_RELATIVE + if ((*spec).action_type == ActionType::REVERT_BIND_AT_POINT_OF_COLLISION) { + // We need to find the base particles for the two virtual sites + // between which the bond broke. + auto p1 = cell_structure.get_local_particle(e.particle_id); + auto p2 = cell_structure.get_local_particle(e.bond_partner_id); + if (!p1 || !p2) + return {}; // particles not on this mpi rank + + if (!p1->p.is_virtual || !p2->p.is_virtual) { + runtimeErrorMsg() << "The REVERT_BIND_AT_POINT_OF_COLLISION bond " + "breakage action has to be configured for the " + "bond on the virtual site. Encountered a particle " + "that is not virtual."; + return {}; + } + + return { + // Bond between virtual sites + DeleteBond{e.particle_id, e.bond_partner_id, e.bond_type}, + // Bond between base particles. We do not know, on which of the two + // the bond is defined, since bonds are stored only on one partner + DeleteAllBonds{p1->p.vs_relative.to_particle_id, + p2->p.vs_relative.to_particle_id}, + DeleteAllBonds{p2->p.vs_relative.to_particle_id, + p1->p.vs_relative.to_particle_id}, + }; + } +#endif // VIRTUAL_SITES_RELATIVE + return {}; +} + +// Handler for the different delete events +class execute : public boost::static_visitor<> { +public: + void operator()(DeleteBond const &d) const { + auto p = cell_structure.get_local_particle(d.particle_id); + if (!p) + return; + local_remove_bond(*p, {d.bond_type, d.bond_partner_id}); + } + void operator()(DeleteAllBonds const &d) const { + auto p = cell_structure.get_local_particle(d.particle_id_1); + if (!p) + return; + local_remove_pair_bonds_to(*p, d.particle_id_2); + } +}; + +void process_queue() { + if (breakage_specs.empty()) + return; + + auto global_queue = gather_global_queue(queue); + + // Construct delete actions from breakage queue + ActionSet actions = {}; + for (auto const &e : global_queue) { + // Convert to merge() once we are on C++17 + auto to_add = actions_for_breakage(e); + actions.insert(to_add.begin(), to_add.end()); + } + + // Execute actions + for (auto const &a : actions) { + boost::apply_visitor(execute{}, a); + } +} +} // namespace BondBreakage diff --git a/src/core/bond_breakage.hpp b/src/core/bond_breakage.hpp new file mode 100644 index 00000000000..5a6f0bcd689 --- /dev/null +++ b/src/core/bond_breakage.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 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 . + */ + +#pragma once + +#include +#include + +namespace BondBreakage { + +enum class ActionType { + NONE = 0, + DELETE_BOND = 1, + REVERT_BIND_AT_POINT_OF_COLLISION = 2 +}; + +struct BreakageSpec { + double breakage_length; + ActionType action_type; +}; + +extern std::unordered_map> breakage_specs; + +/** @brief Check if the bond between the particles should break, if yes, queue + * it. + */ +bool check_and_handle_breakage(int particle_id, int bond_partner_id, + int bond_type, double distance); + +void clear_queue(); + +void process_queue(); + +} // namespace BondBreakage diff --git a/src/core/forces.cpp b/src/core/forces.cpp index d0758d36e2a..be4be006626 100644 --- a/src/core/forces.cpp +++ b/src/core/forces.cpp @@ -27,6 +27,7 @@ #include "EspressoSystemInterface.hpp" +#include "bond_breakage.hpp" #include "cells.hpp" #include "collision.hpp" #include "comfixed_global.hpp" @@ -149,7 +150,7 @@ void force_calc(CellStructure &cell_structure, double time_step, double kT) { #ifdef COLLISION_DETECTION prepare_local_collision_queue(); #endif - + BondBreakage::clear_queue(); auto particles = cell_structure.local_particles(); auto ghost_particles = cell_structure.ghost_particles(); #ifdef ELECTROSTATICS diff --git a/src/core/forces_inline.hpp b/src/core/forces_inline.hpp index d5758e7b90b..d906f3654e8 100644 --- a/src/core/forces_inline.hpp +++ b/src/core/forces_inline.hpp @@ -25,6 +25,7 @@ #include "forces.hpp" +#include "bond_breakage.hpp" #include "bonded_interactions/bonded_interaction_data.hpp" #include "bonded_interactions/thermalized_bond_kernel.hpp" #include "immersed_boundary/ibm_tribend.hpp" @@ -403,6 +404,15 @@ inline bool add_bonded_four_body_force(Bonded_IA_Parameters const &iaparams, inline bool add_bonded_force(Particle &p1, int bond_id, Utils::Span partners) { + + // Consider for bond breakage + if (partners.size() == 1) { + auto d = box_geo.get_mi_vector(p1.r.p, partners[0]->r.p).norm(); + if (BondBreakage::check_and_handle_breakage( + p1.p.identity, partners[0]->p.identity, bond_id, d)) + return false; + } + auto const &iaparams = *bonded_ia_params.at(bond_id); switch (number_of_partners(iaparams)) { diff --git a/src/core/integrate.cpp b/src/core/integrate.cpp index b8fc96f8302..21515ad12d1 100644 --- a/src/core/integrate.cpp +++ b/src/core/integrate.cpp @@ -35,6 +35,7 @@ #include "ParticleRange.hpp" #include "accumulators.hpp" +#include "bond_breakage.hpp" #include "bonded_interactions/rigid_bond.hpp" #include "cells.hpp" #include "collision.hpp" @@ -325,6 +326,7 @@ int integrate(int n_steps, int reuse_forces) { #ifdef COLLISION_DETECTION handle_collisions(); #endif + BondBreakage::process_queue(); } integrated_steps++; diff --git a/src/core/particle_data.cpp b/src/core/particle_data.cpp index bdf049d0cb6..bd7a18140d8 100644 --- a/src/core/particle_data.cpp +++ b/src/core/particle_data.cpp @@ -194,6 +194,28 @@ struct RemoveBond { } }; +/** + * @brief Delete pair bonds to a specific partner + */ +struct RemovePairBondsTo { + int other_pid; + + void operator()(Particle &p) const { + using Bond = std::vector; + std::vector to_delete; + for (auto b: p.bonds()) { + if (b.partner_ids().size() == 1 and b.partner_ids()[0] == other_pid) + to_delete.push_back(Bond{b.bond_id(),other_pid}); + } + for (auto b: to_delete) { + RemoveBond{b}(p); + } + } + template + void serialize(Archive &ar, long int) { + ar & other_pid; + } +}; /** * @brief Delete all bonds. @@ -323,6 +345,14 @@ struct UpdateVisitor : public boost::static_visitor { }; } // namespace +void local_remove_bond(Particle &p, std::vector const &bond) { + RemoveBond{bond}(p); +} + +void local_remove_pair_bonds_to(Particle &p, int other_pid) { + RemovePairBondsTo{other_pid}(p); +} + void mpi_send_update_message_local(int node, int id) { if (node == comm_cart.rank()) { UpdateMessage msg{}; diff --git a/src/core/particle_data.hpp b/src/core/particle_data.hpp index bc078c3a60a..afb4252afdc 100644 --- a/src/core/particle_data.hpp +++ b/src/core/particle_data.hpp @@ -299,10 +299,21 @@ void delete_particle_bond(int part, Utils::Span bond); */ void delete_particle_bonds(int part); +/** @brief Removs the specified bond from the particle + * @param bond The bond in the form + * {bond_id, partner_1, partner_2, ...} + */ +void local_remove_bond(Particle &p, std::vector const &bond); + +/** @brief Removes all pair bonds on the particle which have the specified + * particle id as partner + */ +void local_remove_pair_bonds_to(Particle &p, int other_pid); + /** Call only on the head node: Add bond to particle. * @param part identity of principal atom of the bond. - * @param bond field containing the bond type number and the - * identity of all bond partners (secondary atoms of the bond). + * @param bond field containing the bond type number and the identity + * of all bond partners (secondary atoms of the bond). */ void add_particle_bond(int part, Utils::Span bond); diff --git a/src/python/espressomd/bond_breakage.py b/src/python/espressomd/bond_breakage.py new file mode 100644 index 00000000000..ef7228a9ab4 --- /dev/null +++ b/src/python/espressomd/bond_breakage.py @@ -0,0 +1,30 @@ +# +# Copyright (C) 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 .script_interface import script_interface_register, ScriptObjectMap, ScriptInterfaceHelper + + +@script_interface_register +class BreakageSpec(ScriptInterfaceHelper): + _so_name = "BondBreakage::BreakageSpec" + + +@script_interface_register +class BreakageSpecs(ScriptObjectMap): + _so_name = "BondBreakage::BreakageSpecs" + _key_type = int diff --git a/src/python/espressomd/script_interface.pyx b/src/python/espressomd/script_interface.pyx index e482f2a7088..94546abe719 100644 --- a/src/python/espressomd/script_interface.pyx +++ b/src/python/espressomd/script_interface.pyx @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import numpy as np -from .utils import to_char_pointer, to_str, handle_errors +from .utils import to_char_pointer, to_str, handle_errors, is_valid_type from .utils cimport Vector3d, make_array_locked cimport cpython.object @@ -377,6 +377,87 @@ class ScriptObjectRegistry(ScriptInterfaceHelper): return self.call_method("size") +class ScriptObjectMap(ScriptObjectRegistry): + + """ + Represents a script interface ObjectMap (dict)-like object + + Item acces via [key]. + """ + + _key_type = int + + def __init__(self, *args, **kwargs): + if args: + params, (_unpickle_so_class, (_so_name, bytestring)) = args + assert _so_name == self._so_name + self = _unpickle_so_class(_so_name, bytestring) + self.__setstate__(params) + else: + super().__init__(**kwargs) + + def remove(self, key): + """ + Removes the element with the given key + """ + # Validate key type + if not is_valid_type(key, self._key_type): + raise ValueError( + f"Key has to be of type {self._key_type.__name__}") + + self.call_method("erase", key=key) + + def clear(self): + """ + Remove all elements. + + """ + self.call_method("clear") + + def _get(self, key): + if not is_valid_type(key, self._key_type): + raise ValueError( + f"Key has to be of type {self._key_type.__name__}") + + return self.call_method("get", key=key) + + def __getitem__(self, key): + return self._get(key) + + def __setitem__(self, key, value): + self.call_method("insert", key=key, object=value) + + def __delitem__(self, key): + self.remove(key) + + def keys(self): + return self.call_method("keys") + + def __iter__(self): + for k in self.keys(): yield k + + def items(self): + for k in self.keys(): yield k, self[k] + + @classmethod + def _restore_object(cls, so_callback, so_callback_args, state): + so = so_callback(*so_callback_args) + so.__setstate__(state) + return so + + def __reduce__(self): + so_callback, (so_name, so_bytestring) = super().__reduce__() + return (ScriptObjectMap._restore_object, + (so_callback, (so_name, so_bytestring), self.__getstate__())) + + def __getstate__(self): + return dict(self.items()) + + def __setstate__(self, params): + for key, val in params.items(): + self[key] = val + + # Map from script object names to their corresponding python classes _python_class_by_so_name = {} diff --git a/src/python/espressomd/system.pyx b/src/python/espressomd/system.pyx index 995debf502d..9ad952757f8 100644 --- a/src/python/espressomd/system.pyx +++ b/src/python/espressomd/system.pyx @@ -44,6 +44,7 @@ if LB_BOUNDARIES or LB_BOUNDARIES_GPU: from .comfixed import ComFixed from .utils cimport check_type_or_throw_except from .utils import handle_errors, array_locked +from .bond_breakage import BreakageSpecs IF VIRTUAL_SITES: from .virtual_sites import ActiveVirtualSitesHandle, VirtualSitesOff @@ -143,6 +144,8 @@ cdef class System: """:class:`espressomd.actors.Actors`""" analysis """:class:`espressomd.analyze.Analysis`""" + bond_breakage + """:class:`espressomd.bond_breakage.BreakageSpecs`""" galilei """:class:`espressomd.galilei.GalileiTransform`""" integrator @@ -182,6 +185,7 @@ cdef class System: self.auto_update_accumulators = AutoUpdateAccumulators() self.bonded_inter = interactions.BondedInteractions() self.cell_system = CellSystem() + self.bond_breakage = BreakageSpecs() IF COLLISION_DETECTION == 1: self.collision_detection = CollisionDetection() self.comfixed = ComFixed() @@ -226,6 +230,7 @@ cdef class System: odict['lbboundaries'] = System.__getattribute__( self, "lbboundaries") odict['thermostat'] = System.__getattribute__(self, "thermostat") + odict['bond_breakage'] = System.__getattribute__(self, "bond_breakage") IF COLLISION_DETECTION: odict['collision_detection'] = System.__getattribute__( self, "collision_detection") diff --git a/src/script_interface/CMakeLists.txt b/src/script_interface/CMakeLists.txt index b47690f9c7b..1a7751e3194 100644 --- a/src/script_interface/CMakeLists.txt +++ b/src/script_interface/CMakeLists.txt @@ -4,6 +4,7 @@ add_library( GlobalContext.cpp ContextManager.cpp) add_subdirectory(accumulators) +add_subdirectory(bond_breakage) add_subdirectory(collision_detection) add_subdirectory(constraints) add_subdirectory(cluster_analysis) diff --git a/src/script_interface/ObjectMap.hpp b/src/script_interface/ObjectMap.hpp index 76c32ad05cb..d6af7da67df 100644 --- a/src/script_interface/ObjectMap.hpp +++ b/src/script_interface/ObjectMap.hpp @@ -124,6 +124,11 @@ class ObjectMap : public BaseType { return none; } + if (method == "get") { + auto key = get_value(parameters.at("key")); + return Variant{m_elements.at(key)}; + } + if (method == "get_map") { std::unordered_map ret; @@ -133,6 +138,14 @@ class ObjectMap : public BaseType { return ret; } + if (method == "keys") { + std::vector res; + for (auto const &kv : m_elements) { + res.push_back(kv.first); + } + return res; + } + if (method == "clear") { clear(); return none; diff --git a/src/script_interface/bond_breakage/BreakageSpec.hpp b/src/script_interface/bond_breakage/BreakageSpec.hpp new file mode 100644 index 00000000000..1921b2e6f73 --- /dev/null +++ b/src/script_interface/bond_breakage/BreakageSpec.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 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 . + */ + +#pragma once + +#include "bond_breakage.hpp" + +#include "script_interface/ScriptInterface.hpp" + +#include + +namespace ScriptInterface { +namespace BondBreakage { + +class BreakageSpec : public AutoParameters { +public: + BreakageSpec() : m_breakage_spec(new ::BondBreakage::BreakageSpec) { + add_parameters({ + {"breakage_length", m_breakage_spec->breakage_length}, + {"action_type", + [this](const Variant &v) { + m_breakage_spec->action_type = + ::BondBreakage::ActionType(boost::get(v)); + }, + [this]() { return Variant(int(m_breakage_spec->action_type)); }}, + }); + } + + std::shared_ptr<::BondBreakage::BreakageSpec> breakage_spec() const { + return m_breakage_spec; + } + +private: + std::shared_ptr<::BondBreakage::BreakageSpec> m_breakage_spec; +}; + +} // namespace BondBreakage +} // namespace ScriptInterface diff --git a/src/script_interface/bond_breakage/BreakageSpecs.hpp b/src/script_interface/bond_breakage/BreakageSpecs.hpp new file mode 100644 index 00000000000..214ab7df2ac --- /dev/null +++ b/src/script_interface/bond_breakage/BreakageSpecs.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 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 . + */ + +#pragma once + +#include "BreakageSpec.hpp" + +#include "core/bond_breakage.hpp" + +#include "script_interface/ObjectMap.hpp" +#include "script_interface/ScriptInterface.hpp" + +#include +#include +#include + +namespace ScriptInterface { +namespace BondBreakage { +class BreakageSpecs : public ObjectMap { + using container_type = std::unordered_map>; + +public: + using key_type = typename container_type::key_type; + using mapped_type = typename container_type::mapped_type; + + key_type insert_in_core(mapped_type const &) override { + if (context()->is_head_node()) { + throw std::runtime_error( + "Inserting breakage spec without a bond type is not permitted."); + } + return {}; + } + void insert_in_core(key_type const &key, + mapped_type const &obj_ptr) override { + auto core_spec = obj_ptr->breakage_spec(); + ::BondBreakage::breakage_specs.insert({key, core_spec}); + } + void erase_in_core(key_type const &key) override { + ::BondBreakage::breakage_specs.erase(key); + } + +private: + // disable serialization: bond breakage has its own pickling logic + std::string get_internal_state() const override { return {}; } + void set_internal_state(std::string const &state) override {} +}; +} // namespace BondBreakage +} // namespace ScriptInterface diff --git a/src/script_interface/bond_breakage/CMakeLists.txt b/src/script_interface/bond_breakage/CMakeLists.txt new file mode 100644 index 00000000000..43c571ff9a2 --- /dev/null +++ b/src/script_interface/bond_breakage/CMakeLists.txt @@ -0,0 +1,2 @@ +target_sources(ScriptInterface + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/bond_breakage/initialize.cpp b/src/script_interface/bond_breakage/initialize.cpp new file mode 100644 index 00000000000..380665fdd13 --- /dev/null +++ b/src/script_interface/bond_breakage/initialize.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 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 . + */ + +#include "initialize.hpp" +#include "BreakageSpec.hpp" +#include "BreakageSpecs.hpp" + +namespace ScriptInterface { +namespace BondBreakage { +void initialize(Utils::Factory *f) { + f->register_new("BondBreakage::BreakageSpec"); + f->register_new("BondBreakage::BreakageSpecs"); +} +} // namespace BondBreakage +} // namespace ScriptInterface diff --git a/src/script_interface/bond_breakage/initialize.hpp b/src/script_interface/bond_breakage/initialize.hpp new file mode 100644 index 00000000000..43a06dd7ef6 --- /dev/null +++ b/src/script_interface/bond_breakage/initialize.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 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 . + */ + +#pragma once + +#include "script_interface/ObjectHandle.hpp" + +#include + +namespace ScriptInterface { +namespace BondBreakage { +void initialize(Utils::Factory *f); + +} // namespace BondBreakage +} // namespace ScriptInterface diff --git a/src/script_interface/initialize.cpp b/src/script_interface/initialize.cpp index 35b90d333d0..9664399afb6 100644 --- a/src/script_interface/initialize.cpp +++ b/src/script_interface/initialize.cpp @@ -31,6 +31,7 @@ #include "ComFixed.hpp" #include "CylindricalTransformationParameters.hpp" #include "accumulators/initialize.hpp" +#include "bond_breakage/initialize.hpp" #include "collision_detection/initialize.hpp" #include "interactions/initialize.hpp" #include "lbboundaries/initialize.hpp" @@ -46,6 +47,7 @@ void initialize(Utils::Factory *f) { Writer::initialize(f); #endif Accumulators::initialize(f); + BondBreakage::initialize(f); Observables::initialize(f); ClusterAnalysis::initialize(f); Interactions::initialize(f); diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index 3fa015719a6..0f3b5a3316d 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -83,6 +83,7 @@ foreach( save_checkpoint_${TEST_COMBINATION}_${TEST_BINARY}) endforeach(TEST_BINARY) endforeach(TEST_COMBINATION) +python_test(FILE bond_breakage.py MAX_NUM_PROC 4) python_test(FILE cellsystem.py MAX_NUM_PROC 4) python_test(FILE tune_skin.py MAX_NUM_PROC 1) python_test(FILE constraint_homogeneous_magnetic_field.py MAX_NUM_PROC 4) diff --git a/testsuite/python/bond_breakage.py b/testsuite/python/bond_breakage.py new file mode 100644 index 00000000000..5549a6d6324 --- /dev/null +++ b/testsuite/python/bond_breakage.py @@ -0,0 +1,152 @@ +# +# Copyright (C) 2013-2019 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 unittest as ut +import unittest_decorators as utx +import espressomd +from espressomd.bond_breakage import BreakageSpec +from espressomd.interactions import HarmonicBond + + +@utx.skipIfMissingFeatures("VIRTUAL_SITES_RELATIVE") +class BondBreakage(ut.TestCase): + + @classmethod + def setUpClass(cls): + system = cls.system = espressomd.System(box_l=[10] * 3) + system.cell_system.skin = 0.4 + system.time_step = 0.01 + system.min_global_cut = 2 + + pos1 = system.box_l / 2 - 0.5 + pos2 = system.box_l / 2 + 0.5 + cls.p1 = system.part.add(pos=pos1) + cls.p2 = system.part.add(pos=pos2) + + cls.p1v = system.part.add(pos=pos1) + cls.p1v.vs_auto_relate_to(cls.p1) + + cls.p2v = system.part.add(pos=pos2) + cls.p2v.vs_auto_relate_to(cls.p2) + + cls.h1 = HarmonicBond(k=1, r_0=0) + cls.h2 = HarmonicBond(k=1, r_0=0) + system.bonded_inter.add(cls.h1) + system.bonded_inter.add(cls.h2) + + def tearDown(self): + self.system.bond_breakage.clear() + + def test_00_interface(self): + self.assertEqual(len(self.system.bond_breakage), 0) + + spec2 = BreakageSpec(breakage_length=1.2, action_type=1) + spec4 = BreakageSpec(breakage_length=0.2, action_type=2) + self.system.bond_breakage[2] = spec2 + self.system.bond_breakage[4] = spec4 + self.assertEqual(self.system.bond_breakage[2], spec2) + self.assertEqual(self.system.bond_breakage[4], spec4) + self.assertEqual(len(self.system.bond_breakage), 2) + self.assertEqual(sorted(self.system.bond_breakage.keys()), [2, 4]) + self.assertEqual( + sorted(self.system.bond_breakage.items()), + [(2, spec2), (4, spec4)]) + + self.system.bond_breakage.clear() + self.assertEqual(len(self.system.bond_breakage), 0) + self.assertEqual(self.system.bond_breakage.keys(), []) + with self.assertRaisesRegex(ValueError, "Key has to be of type int"): + self.system.bond_breakage[self.h1] + with self.assertRaisesRegex(ValueError, "Key has to be of type int"): + self.system.bond_breakage.remove(self.h1) + with self.assertRaisesRegex(RuntimeError, "Inserting breakage spec without a bond type is not permitted"): + self.system.bond_breakage.call_method("insert", object=spec2) + + def test_ignore(self): + system = self.system + + # Particles closer than cutoff + system.bond_breakage[self.h1._bond_id] = BreakageSpec( + breakage_length=2, action_type=1) + + self.p1.bonds = ((self.h1, self.p2)) + system.integrator.run(1) + self.assertEqual(self.p1.bonds, ((self.h1, self.p2.id),)) + + self.p2.bonds = [(self.h1, self.p1)] + system.integrator.run(1) + self.assertEqual(self.p2.bonds, ((self.h1, self.p1.id),)) + + # Different bond type + system.bond_breakage[self.h1._bond_id] = BreakageSpec( + breakage_length=0.2, action_type=1) + self.p1.bonds = [(self.h2, self.p2)] + self.p2.bonds = [(self.h2, self.p1)] + system.integrator.run(1) + self.assertEqual(self.p1.bonds, ((self.h2, self.p2.id),)) + self.assertEqual(self.p2.bonds, ((self.h2, self.p1.id),)) + + def test_delete_bond(self): + system = self.system + + # Particles closer than cutoff + system.bond_breakage[self.h1._bond_id] = BreakageSpec( + breakage_length=0, action_type=1) + + self.p1.bonds = [(self.h1, self.p2)] + system.integrator.run(1) + self.assertEqual(self.p1.bonds, ()) + + self.p2.bonds = [(self.h1, self.p1)] + system.integrator.run(1) + self.assertEqual(self.p2.bonds, ()) + + def test_revert_bind_at_point_of_collision(self): + system = self.system + + # Particles closer than cutoff + system.bond_breakage[self.h1._bond_id] = BreakageSpec( + breakage_length=0.5, action_type=2) + + self.p1.bonds = [(self.h2, self.p2)] + self.p1v.bonds = [(self.h1, self.p2v)] + system.integrator.run(1) + self.assertEqual(self.p1v.bonds, ()) + self.assertEqual(self.p1.bonds, ()) + + self.p2.bonds = [(self.h2, self.p1)] + self.p1v.bonds = [(self.h1, self.p2v)] + system.integrator.run(1) + self.assertEqual(self.p1.bonds, ()) + self.assertEqual(self.p1v.bonds, ()) + + def test_exceptions(self): + system = self.system + + # Particles closer than cutoff + system.bond_breakage[self.h2._bond_id] = BreakageSpec( + breakage_length=0.5, action_type=2) + + self.p1.bonds = [(self.h2, self.p2)] + self.p1v.bonds = [(self.h1, self.p2v)] + with self.assertRaisesRegex(Exception, "The REVERT_BIND_AT_POINT_OF_COLLISION bond breakage action has to be configured for the bond on the virtual site"): + system.integrator.run(1) + + +if __name__ == "__main__": + ut.main() diff --git a/testsuite/python/save_checkpoint.py b/testsuite/python/save_checkpoint.py index 01db89bb88a..924d5fc7053 100644 --- a/testsuite/python/save_checkpoint.py +++ b/testsuite/python/save_checkpoint.py @@ -31,6 +31,7 @@ import espressomd.lbboundaries import espressomd.shapes import espressomd.constraints +import espressomd.bond_breakage modes = {x for mode in set("@TEST_COMBINATION@".upper().split('-')) for x in [mode, mode.split('.')[0]]} @@ -231,6 +232,9 @@ ibm_triel_bond = espressomd.interactions.IBM_Triel( ind1=p1.id, ind2=p2.id, ind3=p3.id, k1=1.1, k2=1.2, maxDist=1.6, elasticLaw="NeoHookean") +break_spec = espressomd.bond_breakage.BreakageSpec( + breakage_length=5., action_type=2) +system.bond_breakage[strong_harmonic_bond._bond_id] = break_spec checkpoint.register("system") checkpoint.register("acc_mean_variance") @@ -239,6 +243,7 @@ checkpoint.register("ibm_volcons_bond") checkpoint.register("ibm_tribend_bond") checkpoint.register("ibm_triel_bond") +checkpoint.register("break_spec") # calculate forces system.integrator.run(0) diff --git a/testsuite/python/test_checkpoint.py b/testsuite/python/test_checkpoint.py index 914e26052fc..90588a8439f 100644 --- a/testsuite/python/test_checkpoint.py +++ b/testsuite/python/test_checkpoint.py @@ -339,6 +339,17 @@ def test_bonded_inter(self): bond_ids = system.bonded_inter.call_method('get_bond_ids') self.assertEqual(len(bond_ids), len(system.bonded_inter)) + def test_bond_breakage_specs(self): + # check the ObjectHandle was correctly initialized (including MPI) + spec_ids = list(system.bond_breakage.keys()) + self.assertEqual(len(spec_ids), 1) + cpt_spec = system.bond_breakage[spec_ids[0]] + self.assertAlmostEqual( + break_spec.breakage_length, + cpt_spec.breakage_length, + delta=1e-10) + self.assertEqual(break_spec.action_type, cpt_spec.action_type) + @ut.skipIf('THERM.LB' in modes, 'LB thermostat in modes') @utx.skipIfMissingFeatures(['ELECTROSTATICS', 'MASS', 'ROTATION']) def test_drude_helpers(self): From 3c124b877e7372a30f671eed63d44ce9947d5915 Mon Sep 17 00:00:00 2001 From: Sebastian Bindgen Date: Thu, 17 Feb 2022 13:03:29 +0100 Subject: [PATCH 59/99] Extend testcase for bond breakage with a random network --- testsuite/python/bond_breakage.py | 171 +++++++++++++++++++++++++++--- 1 file changed, 157 insertions(+), 14 deletions(-) diff --git a/testsuite/python/bond_breakage.py b/testsuite/python/bond_breakage.py index 5549a6d6324..931514f1906 100644 --- a/testsuite/python/bond_breakage.py +++ b/testsuite/python/bond_breakage.py @@ -19,35 +19,44 @@ import unittest as ut import unittest_decorators as utx import espressomd +import numpy as np +import espressomd.interactions from espressomd.bond_breakage import BreakageSpec from espressomd.interactions import HarmonicBond +class BondBreakageCommon: + system = espressomd.System(box_l=[10] * 3) + system.cell_system.skin = 0.4 + system.time_step = 0.01 + system.min_global_cut = 2 + + @utx.skipIfMissingFeatures("VIRTUAL_SITES_RELATIVE") -class BondBreakage(ut.TestCase): +class BondBreakage(BondBreakageCommon, ut.TestCase): @classmethod def setUpClass(cls): - system = cls.system = espressomd.System(box_l=[10] * 3) - system.cell_system.skin = 0.4 - system.time_step = 0.01 - system.min_global_cut = 2 - - pos1 = system.box_l / 2 - 0.5 - pos2 = system.box_l / 2 + 0.5 - cls.p1 = system.part.add(pos=pos1) - cls.p2 = system.part.add(pos=pos2) + pos1 = cls.system.box_l / 2 - 0.5 + pos2 = cls.system.box_l / 2 + 0.5 + cls.p1 = cls.system.part.add(pos=pos1) + cls.p2 = cls.system.part.add(pos=pos2) - cls.p1v = system.part.add(pos=pos1) + cls.p1v = cls.system.part.add(pos=pos1) cls.p1v.vs_auto_relate_to(cls.p1) - cls.p2v = system.part.add(pos=pos2) + cls.p2v = cls.system.part.add(pos=pos2) cls.p2v.vs_auto_relate_to(cls.p2) cls.h1 = HarmonicBond(k=1, r_0=0) cls.h2 = HarmonicBond(k=1, r_0=0) - system.bonded_inter.add(cls.h1) - system.bonded_inter.add(cls.h2) + cls.system.bonded_inter.add(cls.h1) + cls.system.bonded_inter.add(cls.h2) + + @classmethod + def tearDownClass(cls): + cls.system.part.clear() + cls.system.bonded_inter.clear() def tearDown(self): self.system.bond_breakage.clear() @@ -148,5 +157,139 @@ def test_exceptions(self): system.integrator.run(1) +@utx.skipIfMissingFeatures("LENNARD_JONES") +class NetworkBreakage(BondBreakageCommon, ut.TestCase): + + @classmethod + def setUpClass(cls): + cls.system.box_l = 3 * [20] + cls.system.min_global_cut = 0.6 + cls.system.time_step = 0.01 + cls.system.cell_system.skin = 0.4 + + def setUp(self): + + box_vol = self.system.box_l[0]**3. + phi = 0.4 + + r = 1. + solid_vol = phi * box_vol + part_vol = 4 / 3 * np.pi * r**3 + part_num = int(solid_vol / part_vol) + + np.random.seed(seed=678) + for i in range(part_num): + pos = np.random.rand(3) * self.system.box_l[0] + self.system.part.add(pos=pos) + + self.system.non_bonded_inter[0, 0].lennard_jones.set_params( + sigma=1., epsilon=1., cutoff=2**(1 / 6), shift='auto') + self.system.integrator.set_steepest_descent(f_max=0, + gamma=0.1, + max_displacement=0.1) + self.system.integrator.run(100) + self.system.integrator.set_vv() + + for i in range(part_num): + self.system.part.by_id(i).fix = [1, 1, 1] + + self.system.thermostat.set_langevin(kT=0.0, gamma=1.0, seed=41) + + def tearDown(self): + self.system.part.clear() + self.system.bonded_inter.clear() + self.system.thermostat.turn_off() + + def test_center_bonds(self): + + harm = espressomd.interactions.HarmonicBond(k=1.0, r_0=0.0, r_cut=5) + self.system.bonded_inter.add(harm) + + crit = 2**(1 / 6) * 2. + + self.system.collision_detection.set_params(mode="bind_centers", + distance=2**(1 / 6) * 2.2, bond_centers=harm) + self.system.integrator.run(1) + + self.system.collision_detection.set_params(mode="off") + self.system.bond_breakage[harm._bond_id] = BreakageSpec( + breakage_length=crit, action_type=1) + self.system.integrator.run(1) + + bonds_dist = 0 + pairs = self.system.cell_system.get_pairs(crit, types=[0]) + for pair in pairs: + dist = self.system.distance( + self.system.part.by_id(pair[0]), + self.system.part.by_id(pair[1])) + if dist <= crit: + bonds_dist += 1 + + bonds_count = 0 + for pair in pairs: + num_bonds = len(self.system.part.by_id(pair[0]).bonds) + for i in range(num_bonds): + if self.system.part.by_id(pair[0]).bonds[i][1] == pair[1]: + bonds_count += 1 + num_bonds = len(self.system.part.by_id(pair[1]).bonds) + for i in range(num_bonds): + if self.system.part.by_id(pair[1]).bonds[i][1] == pair[0]: + bonds_count += 1 + + np.testing.assert_equal(bonds_dist, bonds_count) + + @utx.skipIfMissingFeatures("VIRTUAL_SITES_RELATIVE") + def test_vs_bonds(self): + + harm = espressomd.interactions.HarmonicBond(k=1.0, r_0=0.0, r_cut=5) + virt = espressomd.interactions.Virtual() + self.system.bonded_inter.add(harm) + self.system.bonded_inter.add(virt) + + crit = 2**(1 / 6) * 1.5 + crit_vs = 2**(1 / 6) * 1 / 3 * 1.2 + + self.system.collision_detection.set_params(mode="bind_at_point_of_collision", + distance=crit, bond_centers=virt, bond_vs=harm, + part_type_vs=1, vs_placement=1 / 3) + self.system.integrator.run(1) + + self.system.collision_detection.set_params(mode="off") + self.system.bond_breakage[harm._bond_id] = BreakageSpec( + breakage_length=crit_vs, action_type=2) + self.system.integrator.run(1) + + bonds_dist = 0 + pairs = self.system.cell_system.get_pairs( + 2**(1 / 6) * 2 / 3, types=[1]) + + for pair in pairs: + r1 = self.system.part.by_id(pair[0]).vs_relative[0] + r2 = self.system.part.by_id(pair[1]).vs_relative[0] + dist = self.system.distance( + self.system.part.by_id(r1), + self.system.part.by_id(r2)) + dist_vs = self.system.distance( + self.system.part.by_id(pair[0]), + self.system.part.by_id(pair[1])) + if dist_vs <= crit_vs: + if dist <= crit: + if dist > 0.0: + bonds_dist += 1 + + bonds_count = 0 + for pair in pairs: + num_bonds = len(self.system.part.by_id(pair[0]).bonds) + for i in range(num_bonds): + if self.system.part.by_id(pair[0]).bonds[i][1] == pair[1]: + bonds_count += 1 + num_bonds = len(self.system.part.by_id(pair[1]).bonds) + for i in range(num_bonds): + if self.system.part.by_id(pair[1]).bonds[i][1] == pair[0]: + bonds_count += 1 + + np.testing.assert_equal(bonds_dist, bonds_count) + + if __name__ == "__main__": ut.main() From cac8d33f49001231b9cec0be342920211fca8b35 Mon Sep 17 00:00:00 2001 From: Sebastian Bindgen Date: Tue, 22 Feb 2022 17:22:49 +0100 Subject: [PATCH 60/99] Maps between enum action type and string --- .../bond_breakage/BreakageSpec.hpp | 21 ++++++++++++++++--- testsuite/python/save_checkpoint.py | 2 +- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/script_interface/bond_breakage/BreakageSpec.hpp b/src/script_interface/bond_breakage/BreakageSpec.hpp index 1921b2e6f73..d44f9900816 100644 --- a/src/script_interface/bond_breakage/BreakageSpec.hpp +++ b/src/script_interface/bond_breakage/BreakageSpec.hpp @@ -35,10 +35,13 @@ class BreakageSpec : public AutoParameters { {"breakage_length", m_breakage_spec->breakage_length}, {"action_type", [this](const Variant &v) { - m_breakage_spec->action_type = - ::BondBreakage::ActionType(boost::get(v)); + m_breakage_spec->action_type = ::BondBreakage::ActionType{ + m_breakage_str_to_enum.at(boost::get(v))}; }, - [this]() { return Variant(int(m_breakage_spec->action_type)); }}, + [this]() { + return Variant( + m_breakage_enum_to_str.at(m_breakage_spec->action_type)); + }}, }); } @@ -48,6 +51,18 @@ class BreakageSpec : public AutoParameters { private: std::shared_ptr<::BondBreakage::BreakageSpec> m_breakage_spec; + std::unordered_map<::BondBreakage::ActionType, std::string> + m_breakage_enum_to_str = { + {::BondBreakage::ActionType::NONE, "none"}, + {::BondBreakage::ActionType::DELETE_BOND, "revert_center_bond"}, + {::BondBreakage::ActionType::REVERT_BIND_AT_POINT_OF_COLLISION, + "revert_vs_bond"}}; + std::unordered_map + m_breakage_str_to_enum = { + {"none", ::BondBreakage::ActionType::NONE}, + {"revert_center_bond", ::BondBreakage::ActionType::DELETE_BOND}, + {"revert_vs_bond", + ::BondBreakage::ActionType::REVERT_BIND_AT_POINT_OF_COLLISION}}; }; } // namespace BondBreakage diff --git a/testsuite/python/save_checkpoint.py b/testsuite/python/save_checkpoint.py index 924d5fc7053..97862467dca 100644 --- a/testsuite/python/save_checkpoint.py +++ b/testsuite/python/save_checkpoint.py @@ -233,7 +233,7 @@ ind1=p1.id, ind2=p2.id, ind3=p3.id, k1=1.1, k2=1.2, maxDist=1.6, elasticLaw="NeoHookean") break_spec = espressomd.bond_breakage.BreakageSpec( - breakage_length=5., action_type=2) + breakage_length=5., action_type="revert_center_bond") system.bond_breakage[strong_harmonic_bond._bond_id] = break_spec checkpoint.register("system") From 8d210b8769e7de671c93dd49526f4d6879d9b3ac Mon Sep 17 00:00:00 2001 From: Sebastian Bindgen Date: Tue, 22 Feb 2022 17:23:44 +0100 Subject: [PATCH 61/99] Test case for bond breakage with new strings as parameter --- testsuite/python/bond_breakage.py | 54 ++++++++++++++----------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/testsuite/python/bond_breakage.py b/testsuite/python/bond_breakage.py index 931514f1906..ba538cb81c9 100644 --- a/testsuite/python/bond_breakage.py +++ b/testsuite/python/bond_breakage.py @@ -64,8 +64,10 @@ def tearDown(self): def test_00_interface(self): self.assertEqual(len(self.system.bond_breakage), 0) - spec2 = BreakageSpec(breakage_length=1.2, action_type=1) - spec4 = BreakageSpec(breakage_length=0.2, action_type=2) + spec2 = BreakageSpec( + breakage_length=1.2, + action_type="revert_center_bond") + spec4 = BreakageSpec(breakage_length=0.2, action_type="revert_vs_bond") self.system.bond_breakage[2] = spec2 self.system.bond_breakage[4] = spec4 self.assertEqual(self.system.bond_breakage[2], spec2) @@ -91,7 +93,7 @@ def test_ignore(self): # Particles closer than cutoff system.bond_breakage[self.h1._bond_id] = BreakageSpec( - breakage_length=2, action_type=1) + breakage_length=2, action_type="revert_center_bond") self.p1.bonds = ((self.h1, self.p2)) system.integrator.run(1) @@ -103,7 +105,7 @@ def test_ignore(self): # Different bond type system.bond_breakage[self.h1._bond_id] = BreakageSpec( - breakage_length=0.2, action_type=1) + breakage_length=0.2, action_type="revert_center_bond") self.p1.bonds = [(self.h2, self.p2)] self.p2.bonds = [(self.h2, self.p1)] system.integrator.run(1) @@ -115,7 +117,7 @@ def test_delete_bond(self): # Particles closer than cutoff system.bond_breakage[self.h1._bond_id] = BreakageSpec( - breakage_length=0, action_type=1) + breakage_length=0, action_type="revert_center_bond") self.p1.bonds = [(self.h1, self.p2)] system.integrator.run(1) @@ -130,7 +132,7 @@ def test_revert_bind_at_point_of_collision(self): # Particles closer than cutoff system.bond_breakage[self.h1._bond_id] = BreakageSpec( - breakage_length=0.5, action_type=2) + breakage_length=0.5, action_type="revert_vs_bond") self.p1.bonds = [(self.h2, self.p2)] self.p1v.bonds = [(self.h1, self.p2v)] @@ -149,7 +151,7 @@ def test_exceptions(self): # Particles closer than cutoff system.bond_breakage[self.h2._bond_id] = BreakageSpec( - breakage_length=0.5, action_type=2) + breakage_length=0.5, action_type="revert_vs_bond") self.p1.bonds = [(self.h2, self.p2)] self.p1v.bonds = [(self.h1, self.p2v)] @@ -167,6 +169,17 @@ def setUpClass(cls): cls.system.time_step = 0.01 cls.system.cell_system.skin = 0.4 + def count_bonds(self, pairs): + bonds_count = 0 + for pair in pairs: + for bond in self.system.part.by_id(pair[0]).bonds: + if bond[1] == pair[1]: + bonds_count += 1 + for bond in self.system.part.by_id(pair[1]).bonds: + if bond[1] == pair[0]: + bonds_count += 1 + return bonds_count + def setUp(self): box_vol = self.system.box_l[0]**3. @@ -213,7 +226,7 @@ def test_center_bonds(self): self.system.collision_detection.set_params(mode="off") self.system.bond_breakage[harm._bond_id] = BreakageSpec( - breakage_length=crit, action_type=1) + breakage_length=crit, action_type="revert_center_bond") self.system.integrator.run(1) bonds_dist = 0 @@ -225,17 +238,7 @@ def test_center_bonds(self): if dist <= crit: bonds_dist += 1 - bonds_count = 0 - for pair in pairs: - num_bonds = len(self.system.part.by_id(pair[0]).bonds) - for i in range(num_bonds): - if self.system.part.by_id(pair[0]).bonds[i][1] == pair[1]: - bonds_count += 1 - num_bonds = len(self.system.part.by_id(pair[1]).bonds) - for i in range(num_bonds): - if self.system.part.by_id(pair[1]).bonds[i][1] == pair[0]: - bonds_count += 1 - + bonds_count = self.count_bonds(pairs) np.testing.assert_equal(bonds_dist, bonds_count) @utx.skipIfMissingFeatures("VIRTUAL_SITES_RELATIVE") @@ -256,7 +259,7 @@ def test_vs_bonds(self): self.system.collision_detection.set_params(mode="off") self.system.bond_breakage[harm._bond_id] = BreakageSpec( - breakage_length=crit_vs, action_type=2) + breakage_length=crit_vs, action_type="revert_vs_bond") self.system.integrator.run(1) bonds_dist = 0 @@ -277,16 +280,7 @@ def test_vs_bonds(self): if dist > 0.0: bonds_dist += 1 - bonds_count = 0 - for pair in pairs: - num_bonds = len(self.system.part.by_id(pair[0]).bonds) - for i in range(num_bonds): - if self.system.part.by_id(pair[0]).bonds[i][1] == pair[1]: - bonds_count += 1 - num_bonds = len(self.system.part.by_id(pair[1]).bonds) - for i in range(num_bonds): - if self.system.part.by_id(pair[1]).bonds[i][1] == pair[0]: - bonds_count += 1 + bonds_count = self.count_bonds(pairs) np.testing.assert_equal(bonds_dist, bonds_count) From 4a73a10b0531930f039aef6c87d7b72de4605fbf Mon Sep 17 00:00:00 2001 From: Rudolf Weeber Date: Mon, 28 Feb 2022 13:55:47 +0100 Subject: [PATCH 62/99] Implement Lees-Edwards boundary conditions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sebastian Bindgen Co-authored-by: Jean-Noël Grad --- doc/sphinx/advanced_methods.rst | 8 - doc/sphinx/running.rst | 1 + doc/sphinx/system_setup.rst | 126 ++++ doc/sphinx/zrefs.bib | 21 + src/core/AtomDecomposition.hpp | 2 + src/core/BoxGeometry.hpp | 53 ++ src/core/CellStructure.cpp | 5 +- src/core/CellStructure.hpp | 31 +- src/core/LeesEdwardsBC.hpp | 58 ++ src/core/Particle.hpp | 10 + src/core/ParticleDecomposition.hpp | 2 + src/core/RegularDecomposition.hpp | 1 + src/core/cells.cpp | 6 +- src/core/dpd.cpp | 5 +- src/core/event.cpp | 2 - src/core/event.hpp | 4 - src/core/integrate.cpp | 66 ++- src/core/integrators/brownian_inline.hpp | 16 +- src/core/lees_edwards.hpp | 117 ++++ src/core/lees_edwards_protocol.hpp | 104 ++++ src/core/particle_data.cpp | 18 +- src/core/particle_data.hpp | 6 + src/core/unit_tests/CMakeLists.txt | 1 + src/core/unit_tests/grid_test.cpp | 74 +++ src/core/unit_tests/lees_edwards_test.cpp | 140 +++++ .../virtual_sites/VirtualSitesRelative.cpp | 17 +- src/python/espressomd/grid.pxd | 1 + src/python/espressomd/lees_edwards.py | 81 +++ src/python/espressomd/particle_data.pxd | 3 + src/python/espressomd/particle_data.pyx | 27 +- src/python/espressomd/system.pyx | 29 +- src/script_interface/CMakeLists.txt | 1 + src/script_interface/initialize.cpp | 2 + .../lees_edwards/CMakeLists.txt | 2 + .../lees_edwards/LeesEdwards.hpp | 67 +++ .../lees_edwards/LinearShear.hpp | 59 ++ src/script_interface/lees_edwards/Off.hpp | 46 ++ .../lees_edwards/OscillatoryShear.hpp | 58 ++ .../lees_edwards/Protocol.hpp | 40 ++ .../lees_edwards/initialize.cpp | 35 ++ .../lees_edwards/initialize.hpp | 35 ++ src/utils/include/utils/Vector.hpp | 10 + src/utils/tests/Vector_test.cpp | 6 + testsuite/python/CMakeLists.txt | 1 + testsuite/python/lees_edwards.py | 538 ++++++++++++++++++ 45 files changed, 1890 insertions(+), 45 deletions(-) create mode 100644 src/core/LeesEdwardsBC.hpp create mode 100644 src/core/lees_edwards.hpp create mode 100644 src/core/lees_edwards_protocol.hpp create mode 100644 src/core/unit_tests/lees_edwards_test.cpp create mode 100644 src/python/espressomd/lees_edwards.py create mode 100644 src/script_interface/lees_edwards/CMakeLists.txt create mode 100644 src/script_interface/lees_edwards/LeesEdwards.hpp create mode 100644 src/script_interface/lees_edwards/LinearShear.hpp create mode 100644 src/script_interface/lees_edwards/Off.hpp create mode 100644 src/script_interface/lees_edwards/OscillatoryShear.hpp create mode 100644 src/script_interface/lees_edwards/Protocol.hpp create mode 100644 src/script_interface/lees_edwards/initialize.cpp create mode 100644 src/script_interface/lees_edwards/initialize.hpp create mode 100644 testsuite/python/lees_edwards.py diff --git a/doc/sphinx/advanced_methods.rst b/doc/sphinx/advanced_methods.rst index 2f1525fd5df..cd8ff224c57 100644 --- a/doc/sphinx/advanced_methods.rst +++ b/doc/sphinx/advanced_methods.rst @@ -119,14 +119,6 @@ The following limitations currently apply for the collision detection: between virtual sites -.. _Lees-Edwards boundary conditions: - -Lees-Edwards boundary conditions --------------------------------- - -Lees-Edwards boundary conditions are not available in the current version of |es|. - - .. _Immersed Boundary Method for soft elastic objects: Immersed Boundary Method for soft elastic objects diff --git a/doc/sphinx/running.rst b/doc/sphinx/running.rst index 546b7520158..acc4942be46 100644 --- a/doc/sphinx/running.rst +++ b/doc/sphinx/running.rst @@ -198,6 +198,7 @@ Notes: * For the instantaneous pressure, the same limitations of applicability hold as described in :ref:`Pressure`. * The particle forces :math:`F` include interactions as well as a friction (:math:`\gamma^0`) and noise term (:math:`\sqrt{k_B T \gamma^0 dt} \overline{\eta}`) analogous to the terms in the :ref:`Langevin thermostat`. * The particle forces are only calculated in step 5 and then reused in step 1 of the next iteration. See :ref:`Velocity Verlet Algorithm` for the implications of that. +* The NpT algorithm doesn't support :ref:`Lees-Edwards boundary conditions`. .. _Steepest descent: diff --git a/doc/sphinx/system_setup.rst b/doc/sphinx/system_setup.rst index 7a260284b25..3cae73d8529 100644 --- a/doc/sphinx/system_setup.rst +++ b/doc/sphinx/system_setup.rst @@ -40,6 +40,7 @@ for further calculations, you should explicitly make a copy e.g. via dimension non-periodic does not hinder particles from leaving the box in this direction. In this case for keeping particles in the simulation box a constraint has to be set. + For more details, see :ref:`Boundary conditions`. * :py:attr:`~espressomd.system.System.time_step` @@ -81,6 +82,131 @@ or by calling the corresponding ``get_state()`` methods like:: gamma = system.thermostat.get_state()[0]['gamma'] gamma_rot = system.thermostat.get_state()[0]['gamma_rotation'] +.. _Boundary conditions: + +Boundary conditions +------------------- + +.. _Periodic boundaries: + +Periodic boundaries +~~~~~~~~~~~~~~~~~~~ + +With periodic boundary conditions, particles interact with periodic +images of all particles in the system. This is the default behavior. +When particles cross a box boundary, their position are folded and +their image box counter are incremented. + +From the Python interface, the folded position is accessed with +:attr:`~espressomd.particle_data.ParticleHandle.pos_folded` and the image +box counter with :attr:`~espressomd.particle_data.ParticleHandle.image_box`. +Note that :attr:`~espressomd.particle_data.ParticleHandle.pos` gives the +unfolded particle position. + +Example:: + + import espressomd + system = espressomd.System(box_l=[5.0, 5.0, 5.0], periodicity=[True, True, True]) + system.time_step = 0.1 + system.cell_system.skin = 0.0 + p = system.part.add(pos=[4.9, 0.0, 0.0], v=[0.1, 0.0, 0.0]) + system.integrator.run(20) + print(f"pos = {p.pos}") + print(f"pos_folded = {p.pos_folded}") + print(f"image_box = {p.image_box}") + +Output: + +.. code-block:: none + + pos = [5.1 0. 0. ] + pos_folded = [0.1 0. 0. ] + image_box = [1 0 0] + +.. _Open boundaries: + +Open boundaries +~~~~~~~~~~~~~~~ + +With open boundaries, particles can leave the simulation box. +What happens in this case depends on which algorithm is used. +Some algorithms may require open boundaries, +such as :ref:`Stokesian Dynamics`. + +Example:: + + import espressomd + system = espressomd.System(box_l=[5.0, 5.0, 5.0], periodicity=[False, False, False]) + system.time_step = 0.1 + system.cell_system.skin = 0.0 + p = system.part.add(pos=[4.9, 0.0, 0.0], v=[0.1, 0.0, 0.0]) + system.integrator.run(20) + print(f"pos = {p.pos}") + print(f"pos_folded = {p.pos_folded}") + print(f"image_box = {p.image_box}") + +Output: + +.. code-block:: none + + pos = [5.1 0. 0. ] + pos_folded = [5.1 0. 0. ] + image_box = [0 0 0] + +.. _Lees-Edwards boundary conditions: + +Lees--Edwards boundary conditions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Lees--Edwards boundary conditions (LEbc) are special periodic boundary +conditions to simulation systems under shear stress :cite:`lees72a`. +Periodic images of particles across the shear boundary appear with a +time-dependent position offset. When a particle crosses the shear boundary, +it appears to the opposite side of the simulation box with a position offset +and a shear velocity :cite:`bindgen21a`. + +LEbc require a fully periodic system and are configured with +:class:`~espressomd.lees_edwards.LinearShear` and +:class:`~espressomd.lees_edwards.OscillatoryShear`. +To temporarily disable LEbc, use :class:`~espressomd.lees_edwards.Off`. +To completely disable LEbc and reinitialize the box geometry, do +``system.lees_edwards.protocol = None``. + +Example:: + + import espressomd + import espressomd.lees_edwards + system = espressomd.System(box_l=[5.0, 5.0, 5.0]) + system.time_step = 0.1 + system.cell_system.skin = 0.0 + system.cell_system.set_n_square(use_verlet_lists=True) + le_protocol = espressomd.lees_edwards.LinearShear( + shear_velocity=-0.1, initial_pos_offset=0.0, time_0=-0.1) + system.lees_edwards.protocol = le_protocol + system.lees_edwards.shear_direction = 1 # shear along y-axis + system.lees_edwards.shear_plane_normal = 0 # shear when crossing the x-boundary + p = system.part.add(pos=[4.9, 0.0, 0.0], v=[0.1, 0.0, 0.0]) + system.integrator.run(20) + print(f"pos = {p.pos}") + print(f"pos_folded = {p.pos_folded}") + print(f"image_box = {p.image_box}") + print(f"velocity = {p.v}") + +Output: + +.. code-block:: none + + pos = [5.1 0.2 0. ] + pos_folded = [0.1 0.2 0. ] + image_box = [1 0 0] + velocity = [0.1 0.1 0. ] + +Particles inserted outside the box boundaries will be wrapped around +using the normal periodic boundary rules, i.e. they will not be sheared, +even though their :attr:`~espressomd.particle_data.ParticleHandle.image_box` +is *not* zero. + + .. _Cellsystems: Cellsystems diff --git a/doc/sphinx/zrefs.bib b/doc/sphinx/zrefs.bib index c900e014ea5..1db3576634f 100644 --- a/doc/sphinx/zrefs.bib +++ b/doc/sphinx/zrefs.bib @@ -67,6 +67,17 @@ @ARTICLE{ballenegger09a publisher = {AIP}, } +@Article{bindgen21a, + author = {Bindgen, Sebastian and Weik, Florian and Weeber, Rudolf and Koos, Erin and de Buyl, Pierre}, + title = {Lees-Edwards boundary conditions for translation invariant shear flow: implementation and transport properties}, + journal = {Physics of Fluids}, + year = {2021}, + volume = {33}, + number = {8}, + doi = {10.1063/5.0055396}, + pages = {083615}, +} + @ARTICLE{brodka04a, author = {Br\'{o}dka, A.}, title = {{E}wald summation method with electrostatic layer correction for interactions @@ -296,6 +307,16 @@ @ARTICLE{humphrey96a doi = {10.1016/0263-7855(96)00018-5}, } +@Article{lees72a, + author = {Lees, A. W. and Edwards, S. F.}, + title = {The computer study of transport processes under extreme conditions}, + journal = {Journal of Physics C: Solid State Physics}, + year = {1972}, + volume = {5}, + number = {15}, + doi = {10.1088/0022-3719/5/15/006}, +} + @ARTICLE{tyagi08a, author = {Sandeep Tyagi and Axel Arnold and Christian Holm}, title = {Electrostatic layer correction with image charges: A linear scaling diff --git a/src/core/AtomDecomposition.hpp b/src/core/AtomDecomposition.hpp index 1c36e7f5828..006292f9b57 100644 --- a/src/core/AtomDecomposition.hpp +++ b/src/core/AtomDecomposition.hpp @@ -101,6 +101,8 @@ class AtomDecomposition : public ParticleDecomposition { return m_box; } + BoxGeometry box() const override { return m_box; }; + private: /** * @brief Find cell for id. diff --git a/src/core/BoxGeometry.hpp b/src/core/BoxGeometry.hpp index 1dd471bd8d6..1a84330de2f 100644 --- a/src/core/BoxGeometry.hpp +++ b/src/core/BoxGeometry.hpp @@ -19,9 +19,11 @@ #ifndef CORE_BOX_GEOMETRY_HPP #define CORE_BOX_GEOMETRY_HPP +#include "LeesEdwardsBC.hpp" #include "algorithm/periodic_fold.hpp" #include +#include #include #include @@ -68,8 +70,28 @@ template T get_mi_coord(T a, T b, T box_length, bool periodic) { } } // namespace detail +enum class BoxType { CUBOID = 0, LEES_EDWARDS = 1 }; + class BoxGeometry { +public: + BoxGeometry() { + set_length(Utils::Vector3d{1., 1., 1.}); + set_periodic(0, true); + set_periodic(1, true); + set_periodic(2, true); + set_type(BoxType::CUBOID); + } + BoxGeometry(BoxGeometry const &rhs) { + m_type = rhs.type(); + set_length(rhs.length()); + set_periodic(0, rhs.periodic(0)); + set_periodic(1, rhs.periodic(1)); + set_periodic(2, rhs.periodic(2)); + m_lees_edwards_bc = rhs.m_lees_edwards_bc; + } + private: + BoxType m_type = BoxType::CUBOID; /** Flags for all three dimensions whether pbc are applied (default). */ std::bitset<3> m_periodic = 0b111; /** Side lengths of the box */ @@ -79,6 +101,9 @@ class BoxGeometry { /** Half side lengths of the box */ Utils::Vector3d m_length_half = {0.5, 0.5, 0.5}; + /** Lees-Edwards boundary conditions */ + LeesEdwardsBC m_lees_edwards_bc; + public: /** * @brief Set periodicity for direction @@ -161,9 +186,37 @@ class BoxGeometry { template Utils::Vector get_mi_vector(const Utils::Vector &a, const Utils::Vector &b) const { + if (type() == BoxType::LEES_EDWARDS) { + return clees_edwards_bc().distance(a - b, length(), length_half(), + length_inv(), m_periodic); + } + assert(type() == BoxType::CUBOID); return {get_mi_coord(a[0], b[0], 0), get_mi_coord(a[1], b[1], 1), get_mi_coord(a[2], b[2], 2)}; } + + BoxType type() const { return m_type; } + void set_type(BoxType type) { m_type = type; } + + LeesEdwardsBC &lees_edwards_bc() { return m_lees_edwards_bc; } + LeesEdwardsBC const &clees_edwards_bc() const { return m_lees_edwards_bc; } + void set_lees_edwards_bc(LeesEdwardsBC bc) { m_lees_edwards_bc = bc; } + + /** Calculate the velocity difference including the Lees-Edwards velocity */ + Utils::Vector3d velocity_difference(Utils::Vector3d const &x, + Utils::Vector3d const &y, + Utils::Vector3d const &u, + Utils::Vector3d const &v) const { + auto ret = u - v; + if (type() == BoxType::LEES_EDWARDS) { + auto const &le = m_lees_edwards_bc; + auto const dy = x[le.shear_plane_normal] - y[le.shear_plane_normal]; + if (fabs(dy) > 0.5 * length_half()[le.shear_plane_normal]) { + ret[le.shear_direction] -= Utils::sgn(dy) * le.shear_velocity; + } + } + return ret; + } }; /** @brief Fold a coordinate to primary simulation box. diff --git a/src/core/CellStructure.cpp b/src/core/CellStructure.cpp index 99a813db0fb..2b78d06cadd 100644 --- a/src/core/CellStructure.cpp +++ b/src/core/CellStructure.cpp @@ -24,6 +24,8 @@ #include "AtomDecomposition.hpp" #include "CellStructureType.hpp" #include "RegularDecomposition.hpp" +#include "grid.hpp" +#include "lees_edwards.hpp" #include @@ -221,7 +223,7 @@ struct UpdateParticleIndexVisitor { }; } // namespace -void CellStructure::resort_particles(int global_flag) { +void CellStructure::resort_particles(int global_flag, BoxGeometry const &box) { invalidate_ghosts(); static std::vector diff; @@ -234,6 +236,7 @@ void CellStructure::resort_particles(int global_flag) { } m_rebuild_verlet_list = true; + m_le_pos_offset_at_last_resort = box.clees_edwards_bc().pos_offset; #ifdef ADDITIONAL_CHECKS check_particle_index(); diff --git a/src/core/CellStructure.hpp b/src/core/CellStructure.hpp index 1f3ee14cc33..395be36fbdf 100644 --- a/src/core/CellStructure.hpp +++ b/src/core/CellStructure.hpp @@ -49,6 +49,8 @@ #include #include +extern BoxGeometry box_geo; + namespace Cells { enum Resort : unsigned { RESORT_NONE = 0u, @@ -131,6 +133,7 @@ struct CellStructure { unsigned m_resort_particles = Cells::RESORT_NONE; bool m_rebuild_verlet_list = true; std::vector> m_verlet_list; + double m_le_pos_offset_at_last_resort = 0.; public: bool use_verlet_list = true; @@ -360,17 +363,26 @@ struct CellStructure { /** * @brief Check whether a particle has moved further than half the skin * since the last Verlet list update, thus requiring a resort. - * @param particles Particles to check - * @param skin Skin + * @param particles Particles to check + * @param skin Skin + * @param additional_offset Offset which is added to the distance the + * particle has travelled when comparing to half + * the skin (e.g., for Lees-Edwards BC). * @return Whether a resort is needed. */ - bool check_resort_required(ParticleRange const &particles, double skin) { - auto const lim = Utils::sqr(skin / 2.); + bool + check_resort_required(ParticleRange const &particles, double skin, + Utils::Vector3d const &additional_offset = {}) const { + auto const lim = Utils::sqr(skin / 2.) - additional_offset.norm2(); return std::any_of( particles.begin(), particles.end(), [lim](const auto &p) { return ((p.r.p - p.l.p_old).norm2() > lim); }); } + auto get_le_pos_offset_at_last_resort() const { + return m_le_pos_offset_at_last_resort; + } + /** * @brief Synchronize number of ghosts. */ @@ -468,7 +480,7 @@ struct CellStructure { /** * @brief Resort particles. */ - void resort_particles(int global_flag); + void resort_particles(int global_flag, BoxGeometry const &box); private: /** @brief Set the particle decomposition, keeping the particles. */ @@ -531,9 +543,14 @@ struct CellStructure { if (maybe_box) { Algorithm::link_cell( first, last, - [&kernel, df = detail::MinimalImageDistance{*maybe_box}]( + [&kernel, df = detail::MinimalImageDistance{box_geo}]( Particle &p1, Particle &p2) { kernel(p1, p2, df(p1, p2)); }); } else { + if (decomposition().box().type() != BoxType::CUBOID) { + throw std::runtime_error("Non-cuboid box type is not compatible with a " + "particle decomposition that relies on " + "EuclideanDistance for distance calculation."); + } Algorithm::link_cell( first, last, [&kernel, df = detail::EuclidianDistance{}]( @@ -567,7 +584,7 @@ struct CellStructure { auto const maybe_box = decomposition().minimum_image_distance(); /* In this case the pair kernel is just run over the verlet list. */ if (maybe_box) { - auto const distance_function = detail::MinimalImageDistance{*maybe_box}; + auto const distance_function = detail::MinimalImageDistance{box_geo}; for (auto &pair : m_verlet_list) { pair_kernel(*pair.first, *pair.second, distance_function(*pair.first, *pair.second)); diff --git a/src/core/LeesEdwardsBC.hpp b/src/core/LeesEdwardsBC.hpp new file mode 100644 index 00000000000..f0d849e03fb --- /dev/null +++ b/src/core/LeesEdwardsBC.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021-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 . + */ +#ifndef CORE_LEES_EDWARDS_BC_HPP +#define CORE_LEES_EDWARDS_BC_HPP + +#include + +#include +#include +#include + +struct LeesEdwardsBC { + double pos_offset = 0.; + double shear_velocity = 0.; + int shear_direction = 0; + int shear_plane_normal = 0; + Utils::Vector3d distance(const Utils::Vector3d &d, const Utils::Vector3d &l, + const Utils::Vector3d &hal_l, + const Utils::Vector3d &l_inv, + const std::bitset<3> periodic) const { + assert(shear_plane_normal != shear_direction); + assert(shear_direction >= 0 and shear_direction <= 2); + assert(shear_plane_normal >= 0 and shear_plane_normal <= 2); + + Utils::Vector3d n_jumps{}; + Utils::Vector3d res = d; + + double n_le_crossings = + std::round(res[shear_plane_normal] * l_inv[shear_plane_normal]); + res[shear_direction] += n_le_crossings * pos_offset; + + for (int i : {0, 1, 2}) { + if (periodic[i]) + n_jumps[i] = std::round(res[i] * l_inv[i]); + res[i] -= n_jumps[i] * l[i]; + } + + return res; + } +}; + +#endif diff --git a/src/core/Particle.hpp b/src/core/Particle.hpp index 8243f3610c3..a490354725d 100644 --- a/src/core/Particle.hpp +++ b/src/core/Particle.hpp @@ -371,10 +371,16 @@ struct ParticleLocal { /** index of the simulation box image where the particle really sits. */ Utils::Vector3i i = {0, 0, 0}; + /** Accumulated applied Lees-Edwards offset. */ + double lees_edwards_offset = 0.; + short int lees_edwards_flag = 0; + template void serialize(Archive &ar, long int /* version */) { ar &ghost; ar &p_old; ar &i; + ar &lees_edwards_offset; + ar &lees_edwards_flag; } }; @@ -463,6 +469,10 @@ struct Particle { // NOLINT(bugprone-exception-escape) auto const &pos_at_last_verlet_update() const { return l.p_old; } auto const &image_box() const { return l.i; } auto &image_box() { return l.i; } + auto const &lees_edwards_offset() const { return l.lees_edwards_offset; } + auto &lees_edwards_offset() { return l.lees_edwards_offset; } + auto const &lees_edwards_flag() const { return l.lees_edwards_flag; } + auto &lees_edwards_flag() { return l.lees_edwards_flag; } #ifdef MASS auto const &mass() const { return p.mass; } diff --git a/src/core/ParticleDecomposition.hpp b/src/core/ParticleDecomposition.hpp index 8bc637c6d69..0a7748fa153 100644 --- a/src/core/ParticleDecomposition.hpp +++ b/src/core/ParticleDecomposition.hpp @@ -129,6 +129,8 @@ class ParticleDecomposition { */ virtual boost::optional minimum_image_distance() const = 0; + virtual BoxGeometry box() const = 0; + virtual ~ParticleDecomposition() = default; }; diff --git a/src/core/RegularDecomposition.hpp b/src/core/RegularDecomposition.hpp index 765d4ffbeaf..419f099b680 100644 --- a/src/core/RegularDecomposition.hpp +++ b/src/core/RegularDecomposition.hpp @@ -116,6 +116,7 @@ struct RegularDecomposition : public ParticleDecomposition { boost::optional minimum_image_distance() const override { return {}; } + BoxGeometry box() const override { return m_box; } private: /** Fill @c m_local_cells list and @c m_ghost_cells list for use with regular diff --git a/src/core/cells.cpp b/src/core/cells.cpp index ef9151d965a..0c7b95ff83b 100644 --- a/src/core/cells.cpp +++ b/src/core/cells.cpp @@ -173,7 +173,7 @@ std::vector mpi_non_bonded_loop_trace() { } static void mpi_resort_particles_local(int global_flag) { - cell_structure.resort_particles(global_flag); + cell_structure.resort_particles(global_flag, box_geo); boost::mpi::gather( comm_cart, static_cast(cell_structure.local_particles().size()), 0); @@ -183,7 +183,7 @@ REGISTER_CALLBACK(mpi_resort_particles_local) std::vector mpi_resort_particles(int global_flag) { mpi_call(mpi_resort_particles_local, global_flag); - cell_structure.resort_particles(global_flag); + cell_structure.resort_particles(global_flag, box_geo); clear_particle_node(); @@ -241,7 +241,7 @@ void cells_update_ghosts(unsigned data_parts) { : CELL_NEIGHBOR_EXCHANGE; /* Resort cell system */ - cell_structure.resort_particles(global); + cell_structure.resort_particles(global, box_geo); cell_structure.ghosts_count(); cell_structure.ghosts_update(data_parts); diff --git a/src/core/dpd.cpp b/src/core/dpd.cpp index 5af8296fddd..98bcb29a8e3 100644 --- a/src/core/dpd.cpp +++ b/src/core/dpd.cpp @@ -120,7 +120,7 @@ Utils::Vector3d dpd_pair_force(Particle const &p1, Particle const &p2, return {}; } - auto const v21 = p1.m.v - p2.m.v; + auto const v21 = box_geo.velocity_difference(p1.r.p, p2.r.p, p1.m.v, p2.m.v); auto const noise_vec = (ia_params.dpd_radial.pref > 0.0 || ia_params.dpd_trans.pref > 0.0) ? dpd_noise(p1.p.identity, p2.p.identity) @@ -143,7 +143,8 @@ static auto dpd_viscous_stress_local() { Utils::Matrix stress{}; cell_structure.non_bonded_loop( [&stress](const Particle &p1, const Particle &p2, Distance const &d) { - auto const v21 = p1.m.v - p2.m.v; + auto const v21 = + box_geo.velocity_difference(p1.r.p, p2.r.p, p1.m.v, p2.m.v); IA_parameters const &ia_params = *get_ia_param(p1.p.type, p2.p.type); auto const dist = std::sqrt(d.dist2); diff --git a/src/core/event.cpp b/src/core/event.cpp index ecc90dff929..5324e5af7e8 100644 --- a/src/core/event.cpp +++ b/src/core/event.cpp @@ -316,8 +316,6 @@ void on_timestep_change() { on_thermostat_param_change(); } -void on_simtime_change() { recalc_forces = true; } - void on_forcecap_change() { recalc_forces = true; } void on_nodegrid_change() { diff --git a/src/core/event.hpp b/src/core/event.hpp index 3b3829c876a..477e9739518 100644 --- a/src/core/event.hpp +++ b/src/core/event.hpp @@ -110,10 +110,6 @@ void on_thermostat_param_change(); */ void on_timestep_change(); -/** @brief Called when the simulation time changed. - */ -void on_simtime_change(); - /** @brief Called when the force cap changed. */ void on_forcecap_change(); diff --git a/src/core/integrate.cpp b/src/core/integrate.cpp index 581f03bd7bb..c09beaf21f6 100644 --- a/src/core/integrate.cpp +++ b/src/core/integrate.cpp @@ -47,6 +47,7 @@ #include "grid_based_algorithms/lb_interface.hpp" #include "grid_based_algorithms/lb_particle_coupling.hpp" #include "interactions.hpp" +#include "lees_edwards.hpp" #include "nonbonded_interactions/nonbonded_interaction_data.hpp" #include "npt.hpp" #include "rattle.hpp" @@ -64,6 +65,7 @@ #include #include #include +#include #ifdef VALGRIND_INSTRUMENTATION #include @@ -99,6 +101,47 @@ void notify_sig_int() { } } // namespace +namespace LeesEdwards { +/** @brief Currently active Lees-Edwards protocol. */ +static std::shared_ptr protocol = nullptr; + +/** + * @brief Update the Lees-Edwards parameters of the box geometry + * for the current simulation time. + */ +inline void update_box_params() { + if (box_geo.type() == BoxType::LEES_EDWARDS) { + assert(protocol != nullptr); + update_pos_offset(*protocol, box_geo, sim_time); + update_shear_velocity(*protocol, box_geo, sim_time); + } +} + +void set_protocol(std::shared_ptr new_protocol) { + box_geo.set_type(BoxType::LEES_EDWARDS); + protocol = std::move(new_protocol); + LeesEdwards::update_box_params(); + cell_structure.set_resort_particles(Cells::RESORT_LOCAL); +} + +void unset_protocol() { + protocol = nullptr; + box_geo.lees_edwards_bc().shear_velocity = 0; + box_geo.lees_edwards_bc().pos_offset = 0; + box_geo.set_type(BoxType::CUBOID); + cell_structure.set_resort_particles(Cells::RESORT_LOCAL); +} + +template void run_kernel() { + if (box_geo.type() == BoxType::LEES_EDWARDS) { + auto const kernel = Kernel{box_geo, time_step}; + auto const particles = cell_structure.local_particles(); + std::for_each(particles.begin(), particles.end(), + [&kernel](auto &p) { kernel(p); }); + } +} +} // namespace LeesEdwards + void integrator_sanity_checks() { if (time_step < 0.0) { runtimeErrorMsg() << "time_step not set"; @@ -118,6 +161,8 @@ void integrator_sanity_checks() { case INTEG_METHOD_NPT_ISO: 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) + runtimeErrorMsg() << "The NpT integrator cannot use Lees-Edwards"; break; #endif case INTEG_METHOD_BD: @@ -136,7 +181,9 @@ void integrator_sanity_checks() { } static void resort_particles_if_needed(ParticleRange &particles) { - if (cell_structure.check_resort_required(particles, skin)) { + auto const offset = LeesEdwards::verlet_list_offset( + box_geo, cell_structure.get_le_pos_offset_at_last_resort()); + if (cell_structure.check_resort_required(particles, skin, offset)) { cell_structure.set_resort_particles(Cells::RESORT_LOCAL); } } @@ -152,7 +199,6 @@ bool integrator_step_1(ParticleRange &particles) { break; case INTEG_METHOD_NVT: velocity_verlet_step_1(particles, time_step); - resort_particles_if_needed(particles); break; #ifdef NPT case INTEG_METHOD_NPT_ISO: @@ -166,7 +212,6 @@ bool integrator_step_1(ParticleRange &particles) { #ifdef STOKESIAN_DYNAMICS case INTEG_METHOD_SD: stokesian_dynamics_step_1(particles, time_step); - resort_particles_if_needed(particles); break; #endif // STOKESIAN_DYNAMICS default: @@ -265,10 +310,20 @@ int integrate(int n_steps, int reuse_forces) { save_old_position(particles, cell_structure.ghost_particles()); #endif + LeesEdwards::update_box_params(); bool early_exit = integrator_step_1(particles); if (early_exit) break; + LeesEdwards::run_kernel(); + +#ifdef NPT + if (integ_switch != INTEG_METHOD_NPT_ISO) +#endif + { + resort_particles_if_needed(particles); + } + /* Propagate philox rng counters */ philox_counter_increment(); @@ -298,6 +353,7 @@ int integrate(int n_steps, int reuse_forces) { virtual_sites()->after_force_calc(); #endif integrator_step_2(particles, temperature); + LeesEdwards::run_kernel(); #ifdef BOND_CONSTRAINT // SHAKE velocity updates if (n_rigidbonds) { @@ -341,6 +397,7 @@ int integrate(int n_steps, int reuse_forces) { } } // for-loop over integration steps + LeesEdwards::update_box_params(); ESPRESSO_PROFILER_CXX_MARK_LOOP_END(integration_loop); #ifdef VALGRIND_INSTRUMENTATION @@ -518,7 +575,8 @@ void mpi_set_skin(double skin) { mpi_call_all(mpi_set_skin_local, skin); } void mpi_set_time_local(double time) { sim_time = time; - on_simtime_change(); + recalc_forces = true; + LeesEdwards::update_box_params(); } REGISTER_CALLBACK(mpi_set_time_local) diff --git a/src/core/integrators/brownian_inline.hpp b/src/core/integrators/brownian_inline.hpp index ae3fd6055e5..a0ff4bc7b6a 100644 --- a/src/core/integrators/brownian_inline.hpp +++ b/src/core/integrators/brownian_inline.hpp @@ -42,16 +42,16 @@ inline void brownian_dynamics_propagator(BrownianThermostat const &brownian, p.m.v = bd_drag_vel(brownian.gamma, p); p.r.p += bd_random_walk(brownian, p, time_step, kT); p.m.v += bd_random_walk_vel(brownian, p); - } #ifdef ROTATION - if (!p.p.rotation) - continue; - convert_torque_to_body_frame_apply_fix(p); - p.r.quat = bd_drag_rot(brownian.gamma_rotation, p, time_step); - p.m.omega = bd_drag_vel_rot(brownian.gamma_rotation, p); - p.r.quat = bd_random_walk_rot(brownian, p, time_step, kT); - p.m.omega += bd_random_walk_vel_rot(brownian, p); + if (!p.p.rotation) + continue; + convert_torque_to_body_frame_apply_fix(p); + p.r.quat = bd_drag_rot(brownian.gamma_rotation, p, time_step); + p.m.omega = bd_drag_vel_rot(brownian.gamma_rotation, p); + p.r.quat = bd_random_walk_rot(brownian, p, time_step, kT); + p.m.omega += bd_random_walk_vel_rot(brownian, p); #endif // ROTATION + } } increment_sim_time(time_step); } diff --git a/src/core/lees_edwards.hpp b/src/core/lees_edwards.hpp new file mode 100644 index 00000000000..d20a3036bb5 --- /dev/null +++ b/src/core/lees_edwards.hpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2021-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 . + */ +#ifndef LEES_EDWARDS_HPP +#define LEES_EDWARDS_HPP + +#include "BoxGeometry.hpp" +#include "Particle.hpp" +#include "lees_edwards_protocol.hpp" + +#include +#include + +namespace LeesEdwards { +class UpdateOffset { +protected: + LeesEdwardsBC const &m_le; + double const m_half_time_step; + +public: + UpdateOffset(BoxGeometry const &box, double time_step) + : m_le{box.clees_edwards_bc()}, m_half_time_step{0.5 * time_step} {} + + void operator()(Particle &p) const { + p.lees_edwards_offset() -= m_half_time_step * + p.image_box()[m_le.shear_plane_normal] * + m_le.shear_velocity; + } +}; + +class Push : public UpdateOffset { + Utils::Vector3d const &m_box_l; + +public: + Push(BoxGeometry const &box, double time_step) + : UpdateOffset{box, time_step}, m_box_l{box.length()} {} + + void operator()(Particle &p) const { + // Lees-Edwards acts at the zero step for the velocity half update and at + // the midstep for the position. + // + // The update of the velocity at the end of the time step is triggered by + // the flag and occurs in @ref propagate_vel_finalize_p_inst + if (p.pos()[m_le.shear_plane_normal] >= m_box_l[m_le.shear_plane_normal]) { + p.lees_edwards_flag() = -1; // perform a negative half velocity shift + } else if (p.pos()[m_le.shear_plane_normal] < 0) { + p.lees_edwards_flag() = 1; // perform a positive half velocity shift + } else { + p.lees_edwards_flag() = 0; + } + + auto const dir = static_cast(p.lees_edwards_flag()); + p.v()[m_le.shear_direction] += dir * m_le.shear_velocity; + p.pos()[m_le.shear_direction] += dir * m_le.pos_offset; + p.lees_edwards_offset() -= dir * m_le.pos_offset; + // TODO: clarify whether the fold is needed + // p.pos()[m_le.shear_direction] = Algorithm::periodic_fold( + // p.pos()[m_le.shear_direction], m_box_l()[m_le.shear_direction]); + UpdateOffset::operator()(p); + } +}; + +inline double velocity_shift(short int le_flag, BoxGeometry const &box) { + if (box.type() != BoxType::LEES_EDWARDS) + return 0.; + + return le_flag * box.clees_edwards_bc().shear_velocity; +} + +inline Utils::Vector3d shear_direction(BoxGeometry const &box) { + return Utils::unit_vector(box.clees_edwards_bc().shear_direction); +} + +inline void update_pos_offset(ActiveProtocol const &protocol, BoxGeometry &box, + double time) { + assert(box.type() == BoxType::LEES_EDWARDS); + box.lees_edwards_bc().pos_offset = get_pos_offset(time, protocol); +} + +inline void update_shear_velocity(ActiveProtocol const &protocol, + BoxGeometry &box, double time) { + assert(box.type() == BoxType::LEES_EDWARDS); + box.lees_edwards_bc().shear_velocity = get_shear_velocity(time, protocol); +} + +inline Utils::Vector3d verlet_list_offset(BoxGeometry const &box, + double pos_offset_at_last_resort) { + if (box.type() == BoxType::LEES_EDWARDS) { + return shear_direction(box) * std::fabs(box.clees_edwards_bc().pos_offset - + pos_offset_at_last_resort); + } + return {}; +} + +/** @brief Set a new Lees-Edwards protocol. */ +void set_protocol(std::shared_ptr new_protocol); + +/** @brief Delete the currently active Lees-Edwards protocol. */ +void unset_protocol(); + +} // namespace LeesEdwards +#endif diff --git a/src/core/lees_edwards_protocol.hpp b/src/core/lees_edwards_protocol.hpp new file mode 100644 index 00000000000..ec88668afd5 --- /dev/null +++ b/src/core/lees_edwards_protocol.hpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2021-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 . + */ +#ifndef LEES_EDWARDS_PROTOCOL_HPP +#define LEES_EDWARDS_PROTOCOL_HPP + +#include + +#include + +namespace LeesEdwards { + +// Protocols determining shear rate and positional offset as a function of time + +/** Lees-Edwards protocol for un-altered periodic boundary conditions */ +struct Off { + double shear_velocity(double time) const { return 0.; } + double pos_offset(double time) const { return 0.; } +}; + +/** Lees-Edwards protocol for linear shearing */ +struct LinearShear { + LinearShear() : m_initial_pos_offset{0}, m_shear_velocity{0}, m_time_0{0} {} + LinearShear(double initial_offset, double shear_velocity, double time_0) + : m_initial_pos_offset{initial_offset}, + m_shear_velocity{shear_velocity}, m_time_0{time_0} {} + double shear_velocity(double time) const { return m_shear_velocity; } + double pos_offset(double time) const { + return m_initial_pos_offset + (time - m_time_0) * m_shear_velocity; + } + double m_initial_pos_offset; + double m_shear_velocity; + double m_time_0; +}; + +/** Lees-Edwards protocol for oscillatory shearing */ +struct OscillatoryShear { + OscillatoryShear() : m_amplitude{0}, m_omega{0}, m_time_0{0} {} + OscillatoryShear(double amplitude, double omega, double time_0) + : m_amplitude{amplitude}, m_omega{omega}, m_time_0{time_0} {} + double pos_offset(double time) const { + return m_amplitude * std::sin(m_omega * (time - m_time_0)); + } + double shear_velocity(double time) const { + return m_omega * m_amplitude * std::cos(m_omega * (time - m_time_0)); + } + double m_amplitude; + double m_omega; + double m_time_0; +}; + +/** Type which holds the currently active protocol */ +using ActiveProtocol = boost::variant; + +class PosOffsetGetter : public boost::static_visitor { +public: + PosOffsetGetter(double time) : m_time{time} {} + template double operator()(const T &protocol) const { + return protocol.pos_offset(m_time); + } + +private: + double m_time; +}; + +inline double get_pos_offset(double time, ActiveProtocol const &protocol) { + return boost::apply_visitor(PosOffsetGetter(time), protocol); +} + +/** Visitor to get shear velocity from the Lees-Edwards protocol */ +class ShearVelocityGetter : public boost::static_visitor { +public: + ShearVelocityGetter(double time) : m_time{time} {} + template double operator()(const T &protocol) const { + return protocol.shear_velocity(m_time); + } + +private: + double m_time; +}; + +/** Calculation of current velocity */ +inline double get_shear_velocity(double time, ActiveProtocol const &protocol) { + return boost::apply_visitor(ShearVelocityGetter(time), protocol); +} + +} // namespace LeesEdwards + +#endif diff --git a/src/core/particle_data.cpp b/src/core/particle_data.cpp index 501f1c98296..01cb703b551 100644 --- a/src/core/particle_data.cpp +++ b/src/core/particle_data.cpp @@ -94,6 +94,9 @@ struct UpdateParticle { template using UpdateProperty = UpdateParticle; + +template +using UpdateLocalProperty = UpdateParticle; template using UpdatePosition = UpdateParticle; template @@ -158,6 +161,9 @@ using UpdatePropertyMessage = boost::variant #endif >; +using UpdateLocalPropertyMessage = boost::variant + < UpdateLocalProperty>; + using UpdatePositionMessage = boost::variant < UpdatePosition #ifdef ROTATION @@ -282,7 +288,8 @@ struct UpdateOrientation { * variants with leaves that have such an operator() member. */ using UpdateMessage = boost::variant - < UpdatePropertyMessage + < UpdateLocalPropertyMessage + , UpdatePropertyMessage , UpdatePositionMessage , UpdateMomentumMessage , UpdateForceMessage @@ -303,6 +310,10 @@ template <> struct message_type { using type = UpdatePropertyMessage; }; +template <> struct message_type { + using type = UpdateLocalPropertyMessage; +}; + template <> struct message_type { using type = UpdatePositionMessage; }; @@ -733,6 +744,11 @@ void set_particle_v(int part, Utils::Vector3d const &v) { &ParticleMomentum::v>(part, v); } +void set_particle_lees_edwards_offset(int part, const double v) { + mpi_update_particle(part, v); +} + #ifdef ENGINE void set_particle_swimming(int part, ParticleParametersSwimming swim) { mpi_update_particle_property behave like normal get_mi_vector + { + auto const a = Vector3d{5.1, 12.2, -13.4}; + auto const b = Vector3d{-0.9, 8.8, 21.1}; + + auto const result = box.get_mi_vector(a, b); + + for (int i = 0; i < 3; i++) { + auto const expected = get_mi_coord(a[i], b[i], box_l[i], box.periodic(i)); + BOOST_CHECK_SMALL(std::abs(expected - result[i]), epsilon); + } + } + + // LE pos offset >0 but distance < half box length. Behave like normal + // get_mi_vector + le.pos_offset = 1.; + box.set_lees_edwards_bc(le); + { + auto const a = Vector3d{1.1, 12.2, -13.4}; + auto const b = Vector3d{-0.9, 8.8, 21.1}; + + auto const result = box.get_mi_vector(a, b); + + for (int i = 0; i < 3; i++) { + auto const expected = get_mi_coord(a[i], b[i], box_l[i], box.periodic(i)); + BOOST_CHECK_SMALL(std::abs(expected - result[i]), epsilon); + } + } + // LE pos offset and distance > box in shear plane normal direction + { + auto const a = Vector3d{1.1, 12.2, -13}; + auto const b = Vector3d{-11, 8.8, -13}; + auto const le_jumps = std::round((a[0] - b[0]) / box.length()[0]); + + auto const result = box.get_mi_vector(a, b); + + auto const expected = Vector3d{{ + std::fmod(a[0] - b[0], box.length()[0]), a[1] - b[1], + a[2] - b[2] + le_jumps * le.pos_offset - + box.length()[2] // Manually apply minimum image convention + }}; + for (int i : {0, 1, 2}) { + BOOST_CHECK_CLOSE(expected[i], result[i], epsilon); + } + } + + // Test a case where coordinate different + LE offset in shift dir > box_l/2 + box.set_type(BoxType::LEES_EDWARDS); + box.set_periodic(0, true); + box.set_periodic(1, true); + box.set_periodic(2, true); + box.set_length(Vector3d{5, 5, 5}); + box.lees_edwards_bc().pos_offset = 2.98; + box.lees_edwards_bc().shear_direction = 0; + box.lees_edwards_bc().shear_plane_normal = 1; + auto const result = + box.get_mi_vector(Vector3d{2.5, 1., 2.5}, Vector3d{4.52, 4., 2.5}); + BOOST_CHECK_SMALL(std::fabs(result[0]), box.length_half()[0]); +} + BOOST_AUTO_TEST_CASE(image_shift_test) { Utils::Vector3i img{1, -2, 3}; Utils::Vector3d box{1., 2., 3.}; diff --git a/src/core/unit_tests/lees_edwards_test.cpp b/src/core/unit_tests/lees_edwards_test.cpp new file mode 100644 index 00000000000..3d31ea2cff9 --- /dev/null +++ b/src/core/unit_tests/lees_edwards_test.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2019-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 . + */ + +#define BOOST_TEST_MODULE "Lees-Edwards boundary conditions" +#define BOOST_TEST_DYN_LINK +#include + +#include "BoxGeometry.hpp" +#include "LeesEdwardsBC.hpp" +#include "Particle.hpp" +#include "lees_edwards.hpp" + +#include + +#include + +#include +#include + +using namespace LeesEdwards; + +auto constexpr eps = std::numeric_limits::epsilon(); +auto constexpr tol = 100. * eps; + +BOOST_AUTO_TEST_CASE(test_shear_direction) { + LeesEdwardsBC le; + le.shear_direction = 1; + BoxGeometry box; + box.set_lees_edwards_bc(le); + auto const expected_direction = Utils::Vector3d{{0, 1, 0}}; + BOOST_CHECK_SMALL((shear_direction(box) - expected_direction).norm(), eps); +} + +BOOST_AUTO_TEST_CASE(test_update_offset) { + Particle p; + p.image_box() = {2, 4, -1}; + p.lees_edwards_offset() = 1.5; + + LeesEdwardsBC le; + le.shear_direction = 1; + le.shear_plane_normal = 2; + le.shear_velocity = 3.5; + BoxGeometry box; + box.set_lees_edwards_bc(le); + UpdateOffset(box, 3.5)(p); + auto const expected_offset = 1.5 - le.shear_velocity * 0.5 * 3.5 * + (p.image_box()[le.shear_plane_normal]); + BOOST_CHECK_CLOSE(p.lees_edwards_offset(), expected_offset, tol); +} + +BOOST_AUTO_TEST_CASE(test_push) { + auto const old_offset = -1.2; + auto const shear_l = 6.; + auto const shear_normal_l = 4.5; + auto const dt = 0.8; + auto const old_pos = Utils::Vector3d{{3., shear_normal_l * 1.1, 10.}}; + auto const old_vel = Utils::Vector3d{{-1.2, 2., 4.1}}; + + Particle p; + + p.pos() = old_pos; + p.v() = old_vel; + + p.image_box() = {2, 4, -1}; + p.lees_edwards_offset() = old_offset; + + LeesEdwardsBC le; + le.shear_direction = 2; + le.shear_plane_normal = 1; + le.pos_offset = 2.5; + le.shear_velocity = -3.1; + + BoxGeometry box; + box.set_type(BoxType::LEES_EDWARDS); + box.set_length({5., shear_normal_l, shear_l}); + box.set_lees_edwards_bc(le); + + // Test transition in one direction + Push(box, dt)(p); + auto expected_pos = old_pos - shear_direction(box) * le.pos_offset; + auto expected_vel = old_vel - shear_direction(box) * le.shear_velocity; + auto expected_offset = + old_offset + le.pos_offset - + le.shear_velocity * 0.5 * dt * p.image_box()[le.shear_plane_normal]; + BOOST_CHECK_SMALL((p.pos() - expected_pos).norm(), eps); + BOOST_CHECK_SMALL((p.v() - expected_vel).norm(), eps); + BOOST_CHECK_CLOSE(p.lees_edwards_offset(), expected_offset, tol); + + // Test transition in the other direction + p.pos()[le.shear_plane_normal] = -1; + Push(box, dt)(p); + expected_pos = {old_pos[0], -1., old_pos[2]}; + expected_vel = old_vel; + expected_offset = old_offset - le.shear_velocity * dt * + p.image_box()[le.shear_plane_normal]; + BOOST_CHECK_SMALL((p.pos() - expected_pos).norm(), eps); + BOOST_CHECK_SMALL((p.v() - expected_vel).norm(), eps); + BOOST_CHECK_CLOSE(p.lees_edwards_offset(), expected_offset, tol); +} + +BOOST_AUTO_TEST_CASE(protocol_off) { + auto off = Off(); + BOOST_CHECK_EQUAL(get_pos_offset(17.3, off), 0.0); + BOOST_CHECK_EQUAL(get_shear_velocity(17.3, off), 0.0); +} + +BOOST_AUTO_TEST_CASE(protocol_lin) { + auto const t0 = 1.2; + auto const x0 = -2.1; + auto const v = 2.6; + auto linear = LinearShear(x0, v, t0); + BOOST_CHECK_CLOSE(get_pos_offset(3.3, linear), x0 + v * (3.3 - t0), tol); + BOOST_CHECK_CLOSE(get_shear_velocity(17.3, linear), v, tol); +} + +BOOST_AUTO_TEST_CASE(protocol_osc) { + auto const t0 = 1.2; + auto const a = 3.1; + auto const o = 2.1; + auto osc = OscillatoryShear(a, o, t0); + BOOST_CHECK_CLOSE(get_pos_offset(3.3, osc), a * sin(o * (3.3 - t0)), tol); + BOOST_CHECK_CLOSE(get_shear_velocity(3.3, osc), a * o * cos(o * (3.3 - t0)), + tol); +} diff --git a/src/core/virtual_sites/VirtualSitesRelative.cpp b/src/core/virtual_sites/VirtualSitesRelative.cpp index b8bd7e766d1..b5477ce46d5 100644 --- a/src/core/virtual_sites/VirtualSitesRelative.cpp +++ b/src/core/virtual_sites/VirtualSitesRelative.cpp @@ -146,14 +146,27 @@ void VirtualSitesRelative::update() const { const Particle *p_ref = get_reference_particle(p.vs_relative()); - auto const new_pos = position(*p_ref, p.vs_relative()); + auto new_pos = position(*p_ref, p.vs_relative()); /* The shift has to respect periodic boundaries: if the reference * particles is not in the same image box, we potentially avoid shifting * to the other side of the box. */ - p.pos() += box_geo.get_mi_vector(new_pos, p.pos()); + auto shift = box_geo.get_mi_vector(new_pos, p.pos()); + p.pos() += shift; + Utils::Vector3i image_shift{}; + fold_position(shift, image_shift, box_geo); + p.image_box() = p_ref->image_box() - image_shift; p.v() = velocity(*p_ref, p.vs_relative()); + if (box_geo.type() == BoxType::LEES_EDWARDS) { + auto const &shear_dir = box_geo.clees_edwards_bc().shear_direction; + auto const &shear_normal = box_geo.clees_edwards_bc().shear_plane_normal; + auto const &le_vel = box_geo.lees_edwards_bc().shear_velocity; + Utils::Vector3i n_shifts{}; + fold_position(new_pos, n_shifts, box_geo); + p.v()[shear_dir] -= n_shifts[shear_normal] * le_vel; + } + if (have_quaternions()) p.quat() = p_ref->quat() * p.vs_relative().quat; } diff --git a/src/python/espressomd/grid.pxd b/src/python/espressomd/grid.pxd index a5953b6e96a..fc2dc4b53f1 100644 --- a/src/python/espressomd/grid.pxd +++ b/src/python/espressomd/grid.pxd @@ -27,6 +27,7 @@ cdef extern from "grid.hpp": const Vector3d & length() void set_length(Vector3d) Vector3d get_mi_vector(Vector3d, Vector3d) + Vector3d velocity_difference(Vector3d, Vector3d, Vector3d, Vector3d) BoxGeometry box_geo diff --git a/src/python/espressomd/lees_edwards.py b/src/python/espressomd/lees_edwards.py new file mode 100644 index 00000000000..8b64df06059 --- /dev/null +++ b/src/python/espressomd/lees_edwards.py @@ -0,0 +1,81 @@ +# +# Copyright (C) 2021-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 .script_interface import ScriptInterfaceHelper, script_interface_register + + +@script_interface_register +class LeesEdwards(ScriptInterfaceHelper): + + """Interface to the Lees-Edwards boundary conditions. + + See documentation. + + """ + + _so_name = "LeesEdwards::LeesEdwards" + + +@script_interface_register +class Off(ScriptInterfaceHelper): + + """Lees-Edwards protocol resulting in un-shifted boundaries.""" + _so_name = "LeesEdwards::Off" + + +@script_interface_register +class LinearShear(ScriptInterfaceHelper): + + """Lees-Edwards protocol for linear shear. + + Parameters + ---------- + shear_direction + Cartesian coordinate of the shear direction (0=x, 1=y, 2=z) + shear_plane_normal + Cartesian coordinate of the shear plane normal + initial_pos_offset + Positional offset at the Lees-Edwards boundary at t=0 + shear_velocity + Shear velocity (velocity jump) across the Lees-Edwards boundary + + """ + _so_name = "LeesEdwards::LinearShear" + + +@script_interface_register +class OscillatoryShear(ScriptInterfaceHelper): + + """Lees-Edwards protocol for oscillatory shear. + + Parameters + ---------- + shear_direction + Cartesian coordinate of the shear direction (0=x, 1=y, 2=z) + shear_plane_normal + Cartesian coordinate of the shear plane normal + amplitude + Maximum amplitude of the positional offset at the Lees-Edwards boundary + frequency + Frequency of the shear + time_0 + Time offset of the oscillation + + + """ + _so_name = "LeesEdwards::OscillatoryShear" diff --git a/src/python/espressomd/particle_data.pxd b/src/python/espressomd/particle_data.pxd index 0b095a27063..572cd945e1b 100644 --- a/src/python/espressomd/particle_data.pxd +++ b/src/python/espressomd/particle_data.pxd @@ -60,6 +60,8 @@ cdef extern from "particle_data.hpp": ctypedef struct particle_local "ParticleLocal": Vector3i i + double lees_edwards_offset + int lees_edwards_flag ctypedef struct particle "Particle": particle_properties p @@ -86,6 +88,7 @@ cdef extern from "particle_data.hpp": void set_particle_v(int part, const Vector3d & v) void set_particle_f(int part, const Vector3d & f) + void set_particle_lees_edwards_offset(int, const double) IF ROTATION: void set_particle_rotation(int part, int rot) diff --git a/src/python/espressomd/particle_data.pyx b/src/python/espressomd/particle_data.pyx index f467eaf1b2d..e2b43014ab4 100644 --- a/src/python/espressomd/particle_data.pyx +++ b/src/python/espressomd/particle_data.pyx @@ -73,7 +73,7 @@ cdef class ParticleHandle: """ pickle_attr = copy(particle_attributes) - for i in ["director", "dip", "image_box", "node"]: + for i in ["director", "dip", "image_box", "node", "lees_edwards_flag"]: if i in pickle_attr: pickle_attr.remove(i) IF MASS == 0: @@ -111,6 +111,8 @@ cdef class ParticleHandle: self.update_particle_data() return self._id + # The individual attributes of a particle are implemented as properties. + # Particle Type property type: """ @@ -233,6 +235,29 @@ cdef class ParticleHandle: self.particle_data.l.i[1], self.particle_data.l.i[2]]) + property lees_edwards_offset: + """Contains the accumulated Lees-Edwards offset to reconstruct + continuous trajectories. + + """ + + def __get__(self): + self.update_particle_data() + return self.particle_data.l.lees_edwards_offset + + def __set__(self, value): + set_particle_lees_edwards_offset(self._id, value) + + property lees_edwards_flag: + """Contains the accumulated Lees-Edwards flag to indicate + if the particle crossed the upper or lower boundary. + + """ + + def __get__(self): + self.update_particle_data() + return self.particle_data.l.lees_edwards_flag + # Velocity property v: """ diff --git a/src/python/espressomd/system.pyx b/src/python/espressomd/system.pyx index 9ad952757f8..c25c8bd279c 100644 --- a/src/python/espressomd/system.pyx +++ b/src/python/espressomd/system.pyx @@ -41,6 +41,7 @@ from .accumulators import AutoUpdateAccumulators if LB_BOUNDARIES or LB_BOUNDARIES_GPU: from .lbboundaries import LBBoundaries from .ekboundaries import EKBoundaries +from . import lees_edwards from .comfixed import ComFixed from .utils cimport check_type_or_throw_except from .utils import handle_errors, array_locked @@ -158,6 +159,8 @@ cdef class System: """:class:`espressomd.lbboundaries.LBBoundaries`""" ekboundaries """:class:`espressomd.ekboundaries.EKBoundaries`""" + lees_edwards + """:class:`espressomd.lees_edwards.LeesEdwards`""" collision_detection """:class:`espressomd.collision_detection.CollisionDetection`""" cuda_init_handle @@ -196,6 +199,7 @@ cdef class System: if LB_BOUNDARIES or LB_BOUNDARIES_GPU: self.lbboundaries = LBBoundaries() self.ekboundaries = EKBoundaries() + self.lees_edwards = lees_edwards.LeesEdwards() self.non_bonded_inter = interactions.NonBondedInteractions() self.part = particle_data.ParticleList() self.thermostat = Thermostat() @@ -401,14 +405,14 @@ cdef class System: cdef Vector3d pos1 if isinstance(p1, particle_data.ParticleHandle): - pos1 = make_Vector3d(p1.pos) + pos1 = make_Vector3d(p1.pos_folded) else: check_type_or_throw_except( p1, 3, float, "p1 must be a particle or 3 floats") pos1 = make_Vector3d(p1) cdef Vector3d pos2 if isinstance(p2, particle_data.ParticleHandle): - pos2 = make_Vector3d(p2.pos) + pos2 = make_Vector3d(p2.pos_folded) else: check_type_or_throw_except( p2, 3, float, "p2 must be a particle or 3 floats") @@ -416,6 +420,27 @@ cdef class System: return make_array_locked(box_geo.get_mi_vector(pos2, pos1)) + def velocity_difference(self, p1, p2): + """ + Return the velocity difference between two particles, + considering Lees-Edwards boundary conditions, if active. + + Parameters + ---------- + p1 : :class:`~espressomd.particle_data.ParticleHandle` + p2 : :class:`~espressomd.particle_data.ParticleHandle` + + """ + + cdef Vector3d pos1 = make_Vector3d(p1.pos_folded) + cdef Vector3d pos2 = make_Vector3d(p2.pos_folded) + + cdef Vector3d v1 = make_Vector3d(p1.v) + cdef Vector3d v2 = make_Vector3d(p2.v) + cdef Vector3d vd = box_geo.velocity_difference(pos2, pos1, v2, v1) + + return make_array_locked(vd) + def rotate_system(self, **kwargs): """Rotate the particles in the system about the center of mass. diff --git a/src/script_interface/CMakeLists.txt b/src/script_interface/CMakeLists.txt index cc2a9e1373d..7fcf6cf2665 100644 --- a/src/script_interface/CMakeLists.txt +++ b/src/script_interface/CMakeLists.txt @@ -10,6 +10,7 @@ add_subdirectory(constraints) add_subdirectory(cluster_analysis) add_subdirectory(interactions) add_subdirectory(lbboundaries) +add_subdirectory(lees_edwards) add_subdirectory(virtual_sites) add_subdirectory(observables) add_subdirectory(pair_criteria) diff --git a/src/script_interface/initialize.cpp b/src/script_interface/initialize.cpp index 2a55eb1e528..e0474ed2b9f 100644 --- a/src/script_interface/initialize.cpp +++ b/src/script_interface/initialize.cpp @@ -35,6 +35,7 @@ #include "collision_detection/initialize.hpp" #include "interactions/initialize.hpp" #include "lbboundaries/initialize.hpp" +#include "lees_edwards/initialize.hpp" #include "mpiio/initialize.hpp" #include "observables/initialize.hpp" #include "reaction_methods/initialize.hpp" @@ -53,6 +54,7 @@ void initialize(Utils::Factory *f) { ClusterAnalysis::initialize(f); Interactions::initialize(f); LBBoundaries::initialize(f); + LeesEdwards::initialize(f); PairCriteria::initialize(f); VirtualSites::initialize(f); MPIIO::initialize(f); diff --git a/src/script_interface/lees_edwards/CMakeLists.txt b/src/script_interface/lees_edwards/CMakeLists.txt new file mode 100644 index 00000000000..43c571ff9a2 --- /dev/null +++ b/src/script_interface/lees_edwards/CMakeLists.txt @@ -0,0 +1,2 @@ +target_sources(ScriptInterface + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/lees_edwards/LeesEdwards.hpp b/src/script_interface/lees_edwards/LeesEdwards.hpp new file mode 100644 index 00000000000..657a4d03bbc --- /dev/null +++ b/src/script_interface/lees_edwards/LeesEdwards.hpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2021-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 . + */ +#ifndef SCRIPT_INTERFACE_LEES_EDWARDS_LEES_EDWARDS_HPP +#define SCRIPT_INTERFACE_LEES_EDWARDS_LEES_EDWARDS_HPP + +#include "Protocol.hpp" + +#include "core/grid.hpp" +#include "core/lees_edwards.hpp" + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include + +namespace ScriptInterface { +namespace LeesEdwards { + +class LeesEdwards : public AutoParameters { +public: + LeesEdwards() : m_protocol{nullptr} { + add_parameters( + {{"protocol", + [this](Variant const &value) { + if (is_none(value)) { + m_protocol = nullptr; + ::LeesEdwards::unset_protocol(); + return; + } + m_protocol = get_value>(value); + ::LeesEdwards::set_protocol(m_protocol->protocol()); + }, + [this]() { + if (m_protocol) + return make_variant(m_protocol); + return make_variant(none); + }}, + {"shear_velocity", box_geo.lees_edwards_bc().shear_velocity}, + {"pos_offset", box_geo.lees_edwards_bc().pos_offset}, + {"shear_direction", box_geo.lees_edwards_bc().shear_direction}, + {"shear_plane_normal", box_geo.lees_edwards_bc().shear_plane_normal}}); + } + +private: + std::shared_ptr m_protocol; +}; + +} // namespace LeesEdwards +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/lees_edwards/LinearShear.hpp b/src/script_interface/lees_edwards/LinearShear.hpp new file mode 100644 index 00000000000..7be063b8585 --- /dev/null +++ b/src/script_interface/lees_edwards/LinearShear.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021-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 . + */ +#ifndef SCRIPT_INTERFACE_LEES_EDWARDS_LINEAR_SHEAR_HPP +#define SCRIPT_INTERFACE_LEES_EDWARDS_LINEAR_SHEAR_HPP + +#include "core/lees_edwards.hpp" + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include + +#include + +namespace ScriptInterface { +namespace LeesEdwards { + +class LinearShear : public Protocol { +public: + LinearShear() + : m_protocol{ + new ::LeesEdwards::ActiveProtocol{::LeesEdwards::LinearShear()}} { + 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::shared_ptr<::LeesEdwards::ActiveProtocol> protocol() override { + return m_protocol; + } + +private: + std::shared_ptr<::LeesEdwards::ActiveProtocol> m_protocol; +}; + +} // namespace LeesEdwards +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/lees_edwards/Off.hpp b/src/script_interface/lees_edwards/Off.hpp new file mode 100644 index 00000000000..5f102048765 --- /dev/null +++ b/src/script_interface/lees_edwards/Off.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2021-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 . + */ +#ifndef SCRIPT_INTERFACE_LEES_EDWARDS_OFF_HPP +#define SCRIPT_INTERFACE_LEES_EDWARDS_OFF_HPP + +#include "core/lees_edwards.hpp" + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include + +namespace ScriptInterface { +namespace LeesEdwards { + +class Off : public Protocol { +public: + Off() : m_protocol{new ::LeesEdwards::ActiveProtocol{::LeesEdwards::Off()}} {} + std::shared_ptr<::LeesEdwards::ActiveProtocol> protocol() override { + return m_protocol; + } + +private: + std::shared_ptr<::LeesEdwards::ActiveProtocol> m_protocol; +}; + +} // namespace LeesEdwards +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/lees_edwards/OscillatoryShear.hpp b/src/script_interface/lees_edwards/OscillatoryShear.hpp new file mode 100644 index 00000000000..3cae42c3682 --- /dev/null +++ b/src/script_interface/lees_edwards/OscillatoryShear.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021-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 . + */ +#ifndef SCRIPT_INTERFACE_LEES_EDWARDS_OSCILLATORY_SHEAR_HPP +#define SCRIPT_INTERFACE_LEES_EDWARDS_OSCILLATORY_SHEAR_HPP + +#include "core/lees_edwards.hpp" + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include + +#include + +namespace ScriptInterface { +namespace LeesEdwards { + +class OscillatoryShear : public Protocol { +public: + OscillatoryShear() + : m_protocol{new ::LeesEdwards::ActiveProtocol{ + ::LeesEdwards::OscillatoryShear()}} { + add_parameters( + {{"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}}); + } + std::shared_ptr<::LeesEdwards::ActiveProtocol> protocol() override { + return m_protocol; + } + +private: + std::shared_ptr<::LeesEdwards::ActiveProtocol> m_protocol; +}; + +} // namespace LeesEdwards +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/lees_edwards/Protocol.hpp b/src/script_interface/lees_edwards/Protocol.hpp new file mode 100644 index 00000000000..9f179aa6067 --- /dev/null +++ b/src/script_interface/lees_edwards/Protocol.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021-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 . + */ +#ifndef SCRIPT_INTERFACE_LEES_EDWARDS_PROTOCOL_HPP +#define SCRIPT_INTERFACE_LEES_EDWARDS_PROTOCOL_HPP + +#include "core/lees_edwards.hpp" + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include + +namespace ScriptInterface { +namespace LeesEdwards { + +class Protocol : public AutoParameters { +public: + virtual std::shared_ptr<::LeesEdwards::ActiveProtocol> protocol() = 0; +}; + +} // namespace LeesEdwards +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/lees_edwards/initialize.cpp b/src/script_interface/lees_edwards/initialize.cpp new file mode 100644 index 00000000000..a5729b46221 --- /dev/null +++ b/src/script_interface/lees_edwards/initialize.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021-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 . + */ +#include "initialize.hpp" + +#include "LeesEdwards.hpp" +#include "LinearShear.hpp" +#include "Off.hpp" +#include "OscillatoryShear.hpp" + +namespace ScriptInterface { +namespace LeesEdwards { +void initialize(Utils::Factory *om) { + om->register_new("LeesEdwards::LeesEdwards"); + om->register_new("LeesEdwards::Off"); + om->register_new("LeesEdwards::LinearShear"); + om->register_new("LeesEdwards::OscillatoryShear"); +} +} // namespace LeesEdwards +} // namespace ScriptInterface diff --git a/src/script_interface/lees_edwards/initialize.hpp b/src/script_interface/lees_edwards/initialize.hpp new file mode 100644 index 00000000000..8453bc62d11 --- /dev/null +++ b/src/script_interface/lees_edwards/initialize.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021-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 . + */ + +#ifndef SCRIPT_INTERFACE_LEES_EDWARDS_INITIALIZE_HPP +#define SCRIPT_INTERFACE_LEES_EDWARDS_INITIALIZE_HPP + +#include + +#include + +namespace ScriptInterface { +namespace LeesEdwards { + +void initialize(Utils::Factory *om); + +} // namespace LeesEdwards +} // namespace ScriptInterface + +#endif diff --git a/src/utils/include/utils/Vector.hpp b/src/utils/include/utils/Vector.hpp index ab3d5741cd2..2b0bab8b118 100644 --- a/src/utils/include/utils/Vector.hpp +++ b/src/utils/include/utils/Vector.hpp @@ -448,6 +448,16 @@ auto hadamard_division(T const &a, U const &b) { return a / b; } +template Vector unit_vector(int i) { + if (i == 0) + return {T{1}, T{0}, T{0}}; + if (i == 1) + return {T{0}, T{1}, T{0}}; + if (i == 2) + return {T{0}, T{0}, T{1}}; + throw std::domain_error("coordinate out of range"); +} + /** * @brief Meta function to turns a Vector<1, T> into T. */ diff --git a/src/utils/tests/Vector_test.cpp b/src/utils/tests/Vector_test.cpp index da66e53b182..c115a1b7221 100644 --- a/src/utils/tests/Vector_test.cpp +++ b/src/utils/tests/Vector_test.cpp @@ -105,6 +105,12 @@ BOOST_AUTO_TEST_CASE(range_constructor_test) { BOOST_CHECK(std::equal(v.begin(), v.end(), test_numbers)); } +BOOST_AUTO_TEST_CASE(unit_vector_test) { + BOOST_CHECK((Utils::unit_vector(2) == Utils::Vector3i{0, 0, 1})); + BOOST_CHECK_THROW(Utils::unit_vector(3), std::domain_error); + BOOST_CHECK_THROW(Utils::unit_vector(-1), std::domain_error); +} + BOOST_AUTO_TEST_CASE(test_norm2) { BOOST_CHECK(norm2<1>()); BOOST_CHECK(norm2<2>()); diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index be1a68c21e1..9c3d60980cb 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -143,6 +143,7 @@ python_test(FILE langevin_thermostat.py MAX_NUM_PROC 1) python_test(FILE langevin_thermostat_stats.py MAX_NUM_PROC 1 LABELS long) python_test(FILE brownian_dynamics.py MAX_NUM_PROC 1) python_test(FILE brownian_dynamics_stats.py MAX_NUM_PROC 1 LABELS long) +python_test(FILE lees_edwards.py MAX_NUM_PROC 4) python_test(FILE nsquare.py MAX_NUM_PROC 4) python_test(FILE virtual_sites_relative.py MAX_NUM_PROC 2) python_test(FILE virtual_sites_tracers.py MAX_NUM_PROC 2) diff --git a/testsuite/python/lees_edwards.py b/testsuite/python/lees_edwards.py new file mode 100644 index 00000000000..22b998069cb --- /dev/null +++ b/testsuite/python/lees_edwards.py @@ -0,0 +1,538 @@ +# +# Copyright (C) 2021-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 . +# +import espressomd +import espressomd.interactions +import espressomd.lees_edwards +import espressomd.virtual_sites + +import unittest as ut +import unittest_decorators as utx +import tests_common +import numpy as np +import itertools + + +np.random.seed(42) +params_lin = {'shear_velocity': 1.2, 'initial_pos_offset': 0.1, 'time_0': 0.1} +params_osc = {'amplitude': 2.3, 'omega': 2.51, 'time_0': -2.1} +lin_protocol = espressomd.lees_edwards.LinearShear(**params_lin) +osc_protocol = espressomd.lees_edwards.OscillatoryShear(**params_osc) +off_protocol = espressomd.lees_edwards.Off() + + +def axis(coord): + """Return Cartesian axis from coordinate index""" + res = np.zeros(3) + res[coord] = 1 + return res + + +class LeesEdwards(ut.TestCase): + + system = espressomd.System(box_l=[5.0, 5.0, 5.0]) + system.cell_system.skin = 0.0 + system.cell_system.set_n_square(use_verlet_lists=True) + + time_step = 0.5 + system.time_step = time_step + direction_permutations = list(itertools.permutations([0, 1, 2], 2)) + + def setUp(self): + self.system.time = 0.0 + + def tearDown(self): + system = self.system + system.part.clear() + system.lees_edwards.protocol = None + if espressomd.has_features("VIRTUAL_SITES"): + system.virtual_sites = espressomd.virtual_sites.VirtualSitesOff() + + def test_00_is_none_by_default(self): + + system = self.system + + # Protocol should be off by default + self.assertIsNone(system.lees_edwards.protocol) + self.assertEqual(system.lees_edwards.shear_velocity, 0) + self.assertEqual(system.lees_edwards.pos_offset, 0) + + def test_protocols(self): + """Test shear velocity and pos offset vs protocol and time""" + + # Linear shear + system = self.system + system.time = 3.15 + system.lees_edwards.protocol = lin_protocol + + # check protocol assignment + self.assertEqual(system.lees_edwards.protocol, lin_protocol) + + # check parameter setter/getter consistency + self.assertEqual( + system.lees_edwards.protocol.get_params(), params_lin) + + # Check pos offset and shear velocity + expected_pos = params_lin['initial_pos_offset'] + \ + params_lin['shear_velocity'] * (system.time - params_lin['time_0']) + expected_vel = params_lin['shear_velocity'] + self.assertAlmostEqual( + system.lees_edwards.shear_velocity, expected_vel) + self.assertAlmostEqual( + system.lees_edwards.pos_offset, expected_pos) + + # Check if the offset is determined correctly + + system.time = 0.0 + + # Oscillatory shear + system.lees_edwards.protocol = osc_protocol + + # check parameter setter/getter consistency + self.assertEqual(system.lees_edwards.protocol.get_params(), params_osc) + + # check pos offset and shear velocity at different times, + # check that LE offsets are recalculated on simulation time change + for time in [0., 2.3]: + system.time = time + expected_pos = params_osc['amplitude'] * \ + np.sin(params_osc['omega'] * (time - params_osc['time_0'])) + expected_vel = params_osc['amplitude'] * params_osc['omega'] * \ + np.cos(params_osc['omega'] * (time - params_osc['time_0'])) + self.assertAlmostEqual( + system.lees_edwards.shear_velocity, expected_vel) + self.assertAlmostEqual( + system.lees_edwards.pos_offset, expected_pos) + # Check that time change during integration updates offsets + system.integrator.run(1) + time = system.time + expected_pos = params_osc['amplitude'] * \ + np.sin(params_osc['omega'] * (time - params_osc['time_0'])) + expected_vel = params_osc['amplitude'] * params_osc['omega'] * \ + np.cos(params_osc['omega'] * (time - params_osc['time_0'])) + self.assertAlmostEqual( + system.lees_edwards.shear_velocity, + expected_vel) + self.assertAlmostEqual( + system.lees_edwards.pos_offset, expected_pos) + + # Check that LE is disabled correctly + system.lees_edwards.protocol = None + self.assertEqual(system.lees_edwards.shear_velocity, 0) + self.assertEqual(system.lees_edwards.pos_offset, 0) + + def test_boundary_crossing_lin(self): + """ + A particle crosses the upper and lower boundary with linear shear. + Check that position and velocity are updated correctly. + """ + + system = self.system + + system.lees_edwards.protocol = lin_protocol + box_l = np.copy(system.box_l) + tol = 1e-10 + + for shear_direction, shear_plane_normal in self.direction_permutations: + system.lees_edwards.shear_direction = shear_direction + system.lees_edwards.shear_plane_normal = shear_plane_normal + + system.time = 0.0 + shear_axis = axis(shear_direction) + pos = box_l - 0.01 + vel = np.array([1., 1., 1.]) + p1 = system.part.add(pos=pos) + p2 = system.part.add(pos=pos + box_l) + + # Test crossing the upper boundary + p1.v = vel + p2.v = vel + expected_pos = (pos + vel * system.time_step - + system.lees_edwards.pos_offset * shear_axis) + expected_vel = vel - params_lin['shear_velocity'] * shear_axis + + system.integrator.run(1) + + np.testing.assert_allclose(np.copy(p1.v), expected_vel, atol=tol) + np.testing.assert_allclose(np.copy(p2.v), expected_vel, atol=tol) + np.testing.assert_allclose(np.copy(p1.pos), expected_pos, atol=tol) + np.testing.assert_allclose( + np.copy(p2.pos), expected_pos + box_l, atol=tol) + np.testing.assert_allclose( + np.copy(p1.pos_folded), expected_pos - box_l, atol=tol) + np.testing.assert_allclose( + np.copy(p2.pos_folded), expected_pos - box_l, atol=tol) + + # Test crossing the lower boundary + p1.v = -vel + p2.v = -vel + expected_pos = p1.pos + p1.v * system.time_step + \ + system.lees_edwards.pos_offset * shear_axis + system.integrator.run(1) + np.testing.assert_allclose(np.copy(p1.v), -expected_vel, atol=tol) + np.testing.assert_allclose(np.copy(p2.v), -expected_vel, atol=tol) + np.testing.assert_allclose(np.copy(p1.pos), expected_pos, atol=tol) + np.testing.assert_allclose( + np.copy(p2.pos), expected_pos + box_l, atol=tol) + np.testing.assert_allclose( + np.copy(p1.pos_folded), np.mod(expected_pos - box_l, box_l), + atol=tol) + np.testing.assert_allclose( + np.copy(p2.pos_folded), np.mod(expected_pos - box_l, box_l), + atol=tol) + + def test_boundary_crossing_off(self): + """ + A particle crosses the upper and lower boundary without shear. + Check that position and velocity are unaffected. + """ + + system = self.system + system.lees_edwards.protocol = off_protocol + box_l = np.copy(system.box_l) + tol = 1e-10 + + for shear_direction, shear_plane_normal in self.direction_permutations: + system.lees_edwards.shear_direction = shear_direction + system.lees_edwards.shear_plane_normal = shear_plane_normal + + system.time = 0.0 + pos = box_l - 0.01 + vel = np.array([1., 1., 1.]) + p = system.part.add(pos=pos) + + # Test crossing the upper boundary + p.v = vel + expected_pos = pos + vel * system.time_step + expected_vel = vel + system.integrator.run(1) + np.testing.assert_allclose(np.copy(p.v), expected_vel, atol=tol) + np.testing.assert_allclose(np.copy(p.pos), expected_pos, atol=tol) + + # Test crossing the lower boundary + p.v = -vel + expected_pos = p.pos + p.v * system.time_step + system.integrator.run(1) + np.testing.assert_allclose(np.copy(p.v), -expected_vel, atol=tol) + np.testing.assert_allclose(np.copy(p.pos), expected_pos, atol=tol) + + def test_trajectory_reconstruction(self): + system = self.system + + system.lees_edwards.protocol = espressomd.lees_edwards.LinearShear( + shear_velocity=1., initial_pos_offset=0.0, time_0=0.0) + system.lees_edwards.shear_direction = 0 + system.lees_edwards.shear_plane_normal = 1 + + pos = system.box_l - 0.01 + vel = np.array([0, 1, 0]) + p = system.part.add(pos=pos, v=vel) + + system.integrator.run(1) + + np.testing.assert_almost_equal( + p.lees_edwards_flag * 1.0 * system.time_step * 0.5, + p.lees_edwards_offset) + np.testing.assert_almost_equal(p.lees_edwards_flag, -1) + + offset1 = p.lees_edwards_flag * 1.0 * system.time_step * 0.5 + + system.integrator.run(1) + + np.testing.assert_almost_equal( + offset1 - 1.0 * 0.5, p.lees_edwards_offset) + np.testing.assert_almost_equal(p.lees_edwards_flag, 0) + + @utx.skipIfMissingFeatures("EXTERNAL_FORCES") + def test_distance_vel_diff(self): + """ + Check distance and velocity difference calculation across LE boundary. + """ + + system = self.system + system.lees_edwards.protocol = lin_protocol + epsilon = 0.01 + + for shear_direction, shear_plane_normal in self.direction_permutations: + system.lees_edwards.shear_direction = shear_direction + system.lees_edwards.shear_plane_normal = shear_plane_normal + shear_axis = axis(shear_direction) + + system.lees_edwards.protocol = lin_protocol + p1 = system.part.add( + pos=[epsilon] * 3, v=np.random.random(3), fix=[True] * 3) + p2 = system.part.add( + pos=system.box_l - epsilon, v=np.random.random(3), fix=[True] * 3) + r_euclid = -2 * np.array([epsilon] * 3) + + # check distance + np.testing.assert_allclose( + np.copy(system.distance_vec(p1, p2)), + r_euclid + system.lees_edwards.pos_offset * shear_axis) + np.testing.assert_allclose( + np.copy(system.distance_vec(p1, p2)), + -np.copy(system.distance_vec(p2, p1))) + + # Check velocity difference + np.testing.assert_allclose( + np.copy(system.velocity_difference(p1, p2)), + np.copy(p2.v - p1.v) - system.lees_edwards.shear_velocity * shear_axis) + + @utx.skipIfMissingFeatures("EXTERNAL_FORCES") + def test_interactions(self): + """ + We place two particles crossing a boundary and connect them with an + unbonded and bonded interaction. We test with the resulting stress + tensor if the offset is included properly. + """ + + system = self.system + system.lees_edwards.protocol = lin_protocol + epsilon = 0.01 + + for shear_direction, shear_plane_normal in self.direction_permutations: + system.lees_edwards.shear_direction = shear_direction + system.lees_edwards.shear_plane_normal = shear_plane_normal + + system.lees_edwards.protocol = lin_protocol + p1 = system.part.add(pos=[epsilon] * 3, fix=[True] * 3) + p2 = system.part.add(pos=system.box_l - epsilon, fix=[True] * 3) + + # check bonded interaction + k_bond = 2.1 + r_cut = 1.5 + harm = espressomd.interactions.HarmonicBond(k=k_bond, r_0=0.0) + system.bonded_inter.add(harm) + p1.add_bond((harm, p2)) + + r_12 = np.copy(system.distance_vec(p1, p2)) + system.integrator.run(0) + + np.testing.assert_allclose(np.copy(p1.f), k_bond * r_12) + np.testing.assert_allclose(np.copy(p1.f), -np.copy(p2.f)) + + np.testing.assert_allclose( + np.copy(system.analysis.pressure_tensor()["bonded"]), + np.outer(r_12, np.copy(p2.f)) / system.volume()) + + np.testing.assert_almost_equal( + system.analysis.energy()["bonded"], + 0.5 * k_bond * np.copy(system.distance(p1, p2))**2) + p1.bonds = [] + + # Check non-bonded interaction + k_non_bonded = 3.2 + # NOTE: The force is k*n *distance, hence the 1/2 + system.non_bonded_inter[0, 0].soft_sphere.set_params( + a=k_non_bonded / 2, n=-2, cutoff=r_cut) + system.integrator.run(0) + r_12 = system.distance_vec(p1, p2) + + np.testing.assert_allclose( + k_non_bonded * r_12, np.copy(p1.f)) + np.testing.assert_allclose(np.copy(p1.f), -np.copy(p2.f)) + + np.testing.assert_allclose( + np.copy(system.analysis.pressure_tensor()["non_bonded"]), + np.outer(r_12, p2.f) / system.volume()) + + np.testing.assert_almost_equal( + system.analysis.energy()["non_bonded"], + 0.5 * k_non_bonded * np.copy(system.distance(p1, p2))**2) + + system.non_bonded_inter[0, 0].soft_sphere.set_params( + a=0, n=-2, cutoff=r_cut) + system.part.clear() + + @utx.skipIfMissingFeatures(["EXTERNAL_FORCES", "VIRTUAL_SITES_RELATIVE"]) + def test_virt_sites(self): + """ + Test placement and force transfer for virtual sites across LE + boundaries. + """ + system = self.system + system.min_global_cut = 2.5 + system.virtual_sites = espressomd.virtual_sites.VirtualSitesRelative() + tol = 1e-10 + + # Construct pair of VS across normal boundary + system.lees_edwards.protocol = None + p1 = system.part.add(pos=(2.5, 0.0, 2.5), rotation=[False] * 3, id=0) + p2 = system.part.add(pos=(2.5, 1.0, 2.5)) + p2.vs_auto_relate_to(p1) + p3 = system.part.add(pos=(2.5, 4.0, 2.5)) + p3.vs_auto_relate_to(p1) + system.integrator.run(1) + + system.lees_edwards.protocol = lin_protocol + system.lees_edwards.shear_direction = 0 + system.lees_edwards.shear_plane_normal = 1 + system.integrator.run(1) + np.testing.assert_allclose( + np.copy(system.distance_vec(p3, p2)), [0, 2, 0], atol=tol) + np.testing.assert_allclose( + np.copy(system.velocity_difference(p3, p2)), [0, 0, 0], atol=tol) + system.integrator.run(0) + np.testing.assert_allclose( + np.copy(system.distance_vec(p3, p2)), [0, 2, 0], atol=tol) + system.integrator.run(1) + np.testing.assert_allclose( + np.copy(system.distance_vec(p3, p2)), [0, 2, 0], atol=tol) + + # Check that force back-transfer matches distance + p2.ext_force = [1, 0, 0] + p3.ext_force = -p2.ext_force + system.integrator.run(1) + np.testing.assert_allclose( + np.copy(p1.torque_lab), [0, 0, -2], atol=tol) + + @utx.skipIfMissingFeatures(["VIRTUAL_SITES_RELATIVE", "ROTATION"]) + def test_virt_sites_interaction(self): + """ + A virtual site interacts with a real particle via a DPD interaction + to get a velocity-dependent force. First we measure a force within the + primary simulation box as reference. Then we compare it first with the + situation of the interaction across the Lees Edwards boundary and + second with the vector from the real particle to the virtual site + crossing the boundary. + """ + + system = self.system + system.virtual_sites = espressomd.virtual_sites.VirtualSitesRelative() + + system.thermostat.set_dpd(kT=0.0, seed=1) + system.non_bonded_inter[11, 11].dpd.set_params( + weight_function=0, gamma=1.75, r_cut=2., + trans_weight_function=0, trans_gamma=1.5, trans_r_cut=2.0) + + system.lees_edwards.protocol = espressomd.lees_edwards.LinearShear( + shear_velocity=2.0, initial_pos_offset=0.0) + system.lees_edwards.shear_direction = 0 + system.lees_edwards.shear_plane_normal = 1 + p1 = system.part.add( + pos=[2.5, 2.5, 2.5], rotation=(1, 1, 1), type=10, v=(0.0, -0.1, -0.25)) + p2 = system.part.add(pos=(2.5, 3.5, 2.5), type=11) + p2.vs_auto_relate_to(p1) + + p3 = system.part.add(pos=(2.5, 4.5, 2.5), type=11, v=(2.0, 1., 1.25)) + + system.integrator.run(0, recalc_forces=True) + + f_p1 = np.copy(p1.f) + f_p2 = np.copy(p2.f) + f_p3 = np.copy(p3.f) + + system.part.clear() + + p1 = system.part.add( + pos=[2.5, 3.75, 2.5], type=10, v=(0.0, -0.1, -0.25), + rotation=[True] * 3) + p2 = system.part.add(pos=(2.5, 4.75, 2.5), type=11) + p2.vs_auto_relate_to(p1) + + p3 = system.part.add(pos=(2.5, 5.75, 2.5), type=11, v=(0.0, 1., 1.25)) + + system.integrator.run(0, recalc_forces=True) + + np.testing.assert_array_almost_equal(np.copy(p1.f), f_p1) + np.testing.assert_array_almost_equal(np.copy(p2.f), f_p2) + np.testing.assert_array_almost_equal(np.copy(p3.f), f_p3) + + system.part.clear() + + p1 = system.part.add(pos=(2.5, 4.5, 2.5), type=10, v=(0.0, -0.1, -0.25), + rotation=3 * (True,)) + p2 = system.part.add(pos=(2.5, 5.5, 2.5), type=11) + p2.vs_auto_relate_to(p1) + + p3 = system.part.add(pos=(2.5, 6.5, 2.5), type=11, v=(0., 1., 1.25)) + + system.integrator.run(0, recalc_forces=True) + + np.testing.assert_array_almost_equal(np.copy(p1.f), f_p1) + np.testing.assert_array_almost_equal(np.copy(p2.f), f_p2) + np.testing.assert_array_almost_equal(np.copy(p3.f), f_p3) + + system.part.clear() + + system.non_bonded_inter[11, 11].dpd.set_params( + weight_function=0, gamma=0, r_cut=0, + trans_weight_function=0, trans_gamma=0, trans_r_cut=0) + + def setup_lj_liquid(self): + system = self.system + system.cell_system.set_n_square(use_verlet_lists=False) + # Parameters + n = 100 + phi = 0.4 + sigma = 1. + eps = 1 + cut = sigma * 2**(1 / 6) + + # box + l = (n / 6. * np.pi * sigma**3 / phi)**(1. / 3.) + + # Setup + system.box_l = [l, l, l] + system.lees_edwards.protocol = None + + system.time_step = 0.01 + system.thermostat.turn_off() + + np.random.seed(42) + system.part.add(pos=np.random.random((n, 3)) * l) + + # interactions + system.non_bonded_inter[0, 0].lennard_jones.set_params( + epsilon=eps, sigma=sigma, cutoff=cut, shift="auto") + # Remove overlap + system.integrator.set_steepest_descent( + f_max=0, gamma=0.05, max_displacement=0.05) + while system.analysis.energy()["total"] > 0.5 * n: + system.integrator.run(5) + + system.integrator.set_vv() + + @utx.skipIfMissingFeatures("LENNARD_JONES") + def test_zz_lj(self): + """ + Simulate an LJ liquid under linear shear and verify forces. + This is to make sure that no pairs get lost or are outdated + in the short range loop. To have deterministic forces, velocity + capping is used rather than a thermostat. + """ + system = self.system + self.setup_lj_liquid() + system.lees_edwards.protocol = espressomd.lees_edwards.LinearShear( + shear_velocity=0.3, initial_pos_offset=0.01) + system.lees_edwards.shear_direction = 2 + system.lees_edwards.shear_plane_normal = 0 + system.integrator.run(1, recalc_forces=True) + tests_common.check_non_bonded_loop_trace(system) + + # Rewind the clock to get back the LE offset applied during force calc + system.time = system.time - system.time_step + tests_common.verify_lj_forces(system, 1E-7) + + system.thermostat.set_langevin(kT=.1, gamma=5, seed=2) + system.integrator.run(50) + tests_common.check_non_bonded_loop_trace(system) + + +if __name__ == "__main__": + ut.main() From 1dba7aa6e42f849e0d2f0b09ca5b3d30e51ec3e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 28 Feb 2022 19:02:10 +0100 Subject: [PATCH 63/99] core: Rewrite Particle fix flag Remove the fourth bit in ext_flag, since the flag can be cast to bool directly. Remove macros COORD_FIXED and COORDS_FIX_MASK. --- src/core/Particle.hpp | 49 ++++---------- src/core/integrators/steepest_descent.cpp | 3 +- .../integrators/velocity_verlet_inline.hpp | 4 +- src/core/integrators/velocity_verlet_npt.cpp | 6 +- src/core/particle_data.cpp | 13 +++- src/core/particle_data.hpp | 13 ++-- src/core/thermostats/brownian_inline.hpp | 67 ++++--------------- src/python/espressomd/particle_data.pxd | 4 +- src/python/espressomd/particle_data.pyx | 28 +++----- 9 files changed, 63 insertions(+), 124 deletions(-) diff --git a/src/core/Particle.hpp b/src/core/Particle.hpp index a490354725d..f601ef90688 100644 --- a/src/core/Particle.hpp +++ b/src/core/Particle.hpp @@ -51,19 +51,6 @@ enum : uint8_t { ROTATION_Z = 4u }; -#ifdef EXTERNAL_FORCES -/** \ref ParticleProperties::ext_flag "ext_flag" value for fixed coordinate - * @c coord. - */ -#define COORD_FIXED(coord) (2u << (coord)) -/** \ref ParticleProperties::ext_flag "ext_flag" mask to check whether any of - * the coordinates is fixed. - */ -#define COORDS_FIX_MASK (COORD_FIXED(0) | COORD_FIXED(1) | COORD_FIXED(2)) -#else // EXTERNAL_FORCES -#define COORD_FIXED(coord) (0) -#endif // EXTERNAL_FORCES - /** Properties of a self-propelled particle. */ struct ParticleParametersSwimming { /** Is the particle a swimmer. */ @@ -190,14 +177,13 @@ struct ParticleProperties { #endif // THERMOSTAT_PER_PARTICLE #ifdef EXTERNAL_FORCES - /** Flag for fixed particle coordinates. + /** Bitfield for fixed particle coordinates. * Values: - * - 0: no fixed coordinates - * - 2: fix translation along the x axis - * - 4: fix translation along the y axis - * - 8: fix translation along the z axis + * - 1: fix translation along the x axis + * - 2: fix translation along the y axis + * - 4: fix translation along the z axis */ - uint8_t ext_flag = 0; + uint8_t ext_flag = static_cast(0b000u); /** External force. */ Utils::Vector3d ext_force = {0, 0, 0}; #ifdef ROTATION @@ -205,7 +191,8 @@ struct ParticleProperties { Utils::Vector3d ext_torque = {0, 0, 0}; #endif #else // EXTERNAL_FORCES - static constexpr const uint8_t ext_flag = 0; // no fixed coordinates + /** Bitfield for fixed particle coordinates. Coordinates cannot be fixed. */ + static constexpr const uint8_t ext_flag = static_cast(0b000u); #endif // EXTERNAL_FORCES #ifdef ENGINE @@ -481,9 +468,7 @@ struct Particle { // NOLINT(bugprone-exception-escape) constexpr auto &mass() const { return p.mass; } #endif #ifdef ROTATION - bool can_rotate() const { - return can_rotate_around(0) or can_rotate_around(1) or can_rotate_around(2); - } + bool can_rotate() const { return static_cast(p.rotation); } bool can_rotate_around(int const axis) const { detail::check_axis_idx_valid(axis); return detail::get_nth_bit(p.rotation, axis); @@ -553,25 +538,17 @@ struct Particle { // NOLINT(bugprone-exception-escape) #endif // ROTATION #endif // THERMOSTAT_PER_PARTICLE #ifdef EXTERNAL_FORCES - bool has_fixed_coordinates() const { - return detail::get_nth_bit(p.ext_flag, 0); - } + bool has_fixed_coordinates() const { return static_cast(p.ext_flag); } bool is_fixed_along(int const axis) const { detail::check_axis_idx_valid(axis); - return detail::get_nth_bit(p.ext_flag, axis + 1); + return detail::get_nth_bit(p.ext_flag, axis); } void set_fixed_along(int const axis, bool const fixed_flag) { // set new flag if (fixed_flag) { - p.ext_flag |= static_cast(1u << (axis + 1)); - } else { - p.ext_flag &= static_cast(~(1u << (axis + 1))); - } - // check if any flag is set and store that in the 0th bit - if (p.ext_flag >> 1) { - p.ext_flag |= static_cast(1u); + p.ext_flag |= static_cast(1u << axis); } else { - p.ext_flag &= static_cast(~1u); + p.ext_flag &= static_cast(~(1u << axis)); } } auto const &ext_force() const { return p.ext_force; } @@ -579,7 +556,7 @@ struct Particle { // NOLINT(bugprone-exception-escape) #else // EXTERNAL_FORCES constexpr bool has_fixed_coordinates() const { return false; } - constexpr bool is_fixed_along(int const axis) const { return false; } + constexpr bool is_fixed_along(int const) const { return false; } #endif // EXTERNAL_FORCES #ifdef ENGINE auto const &swimming() const { return p.swim; } diff --git a/src/core/integrators/steepest_descent.cpp b/src/core/integrators/steepest_descent.cpp index 3c1ebf767e6..08705399a10 100644 --- a/src/core/integrators/steepest_descent.cpp +++ b/src/core/integrators/steepest_descent.cpp @@ -54,7 +54,7 @@ bool steepest_descent_step(const ParticleRange &particles) { // For all Cartesian coordinates for (int j = 0; j < 3; j++) { // Skip, if coordinate is fixed - if (!(p.p.ext_flag & COORD_FIXED(j))) + if (!p.is_fixed_along(j)) { // Skip positional increments of virtual particles if (!p.p.is_virtual) { // Square of force on particle @@ -68,6 +68,7 @@ bool steepest_descent_step(const ParticleRange &particles) { // Move particle p.r.p[j] += dp; } + } } #ifdef ROTATION { diff --git a/src/core/integrators/velocity_verlet_inline.hpp b/src/core/integrators/velocity_verlet_inline.hpp index 2ea31122ad4..53f20705fd0 100644 --- a/src/core/integrators/velocity_verlet_inline.hpp +++ b/src/core/integrators/velocity_verlet_inline.hpp @@ -44,7 +44,7 @@ inline void velocity_verlet_propagate_vel_pos(const ParticleRange &particles, if (p.p.is_virtual) continue; for (int j = 0; j < 3; j++) { - if (!(p.p.ext_flag & COORD_FIXED(j))) { + if (!p.is_fixed_along(j)) { /* Propagate velocities: v(t+0.5*dt) = v(t) + 0.5 * dt * a(t) */ p.m.v[j] += 0.5 * time_step * p.f.f[j] / p.p.mass; @@ -68,7 +68,7 @@ inline void velocity_verlet_propagate_vel_final(const ParticleRange &particles, continue; for (int j = 0; j < 3; j++) { - if (!(p.p.ext_flag & COORD_FIXED(j))) { + if (!p.is_fixed_along(j)) { /* Propagate velocity: v(t+dt) = v(t+0.5*dt) + 0.5*dt * a(t+dt) */ p.m.v[j] += 0.5 * time_step * p.f.f[j] / p.p.mass; } diff --git a/src/core/integrators/velocity_verlet_npt.cpp b/src/core/integrators/velocity_verlet_npt.cpp index c56279b9031..1f90e159c09 100644 --- a/src/core/integrators/velocity_verlet_npt.cpp +++ b/src/core/integrators/velocity_verlet_npt.cpp @@ -53,7 +53,7 @@ void velocity_verlet_npt_propagate_vel_final(const ParticleRange &particles, continue; auto const noise = friction_therm0_nptiso<2>(npt_iso, p.m.v, p.p.identity); for (int j = 0; j < 3; j++) { - if (!(p.p.ext_flag & COORD_FIXED(j))) { + if (!p.is_fixed_along(j)) { if (nptiso.geometry & nptiso.nptgeom_dir[j]) { nptiso.p_vel[j] += Utils::sqr(p.m.v[j] * time_step) * p.p.mass; p.m.v[j] += (p.f.f[j] * time_step / 2.0 + noise[j]) / p.p.mass; @@ -123,7 +123,7 @@ void velocity_verlet_npt_propagate_pos(const ParticleRange &particles, if (p.p.is_virtual) continue; for (int j = 0; j < 3; j++) { - if (!(p.p.ext_flag & COORD_FIXED(j))) { + if (!p.is_fixed_along(j)) { if (nptiso.geometry & nptiso.nptgeom_dir[j]) { p.r.p[j] = scal[1] * (p.r.p[j] + scal[2] * p.m.v[j] * time_step); p.l.p_old[j] *= scal[1]; @@ -171,7 +171,7 @@ void velocity_verlet_npt_propagate_vel(const ParticleRange &particles, if (p.p.is_virtual) continue; for (int j = 0; j < 3; j++) { - if (!(p.p.ext_flag & COORD_FIXED(j))) { + if (!p.is_fixed_along(j)) { auto const noise = friction_therm0_nptiso<1>(npt_iso, p.m.v, p.p.identity); if (integ_switch == INTEG_METHOD_NPT_ISO && diff --git a/src/core/particle_data.cpp b/src/core/particle_data.cpp index 01cb703b551..34d9a391aa8 100644 --- a/src/core/particle_data.cpp +++ b/src/core/particle_data.cpp @@ -949,9 +949,16 @@ void set_particle_ext_force(int part, const Utils::Vector3d &force) { part, force); } -void set_particle_fix(int part, uint8_t flag) { - mpi_update_particle_property(part, - flag); +void set_particle_fix(int part, Utils::Vector3i const &flag) { + auto ext_flag = static_cast(0u); + if (flag[0]) + ext_flag |= static_cast(1u); + if (flag[1]) + ext_flag |= static_cast(2u); + if (flag[2]) + ext_flag |= static_cast(4u); + mpi_update_particle_property( + part, ext_flag); } #endif // EXTERNAL_FORCES diff --git a/src/core/particle_data.hpp b/src/core/particle_data.hpp index a9ab3ae5347..8ef223d048b 100644 --- a/src/core/particle_data.hpp +++ b/src/core/particle_data.hpp @@ -288,10 +288,10 @@ void set_particle_ext_force(int part, const Utils::Vector3d &force); /** Call only on the head node: set coordinate axes for which the particles * motion is fixed. * @param part the particle. - * @param flag new value for flagged coordinate axes to be fixed + * @param flag coordinates to be fixed. */ -void set_particle_fix(int part, uint8_t flag); -#endif +void set_particle_fix(int part, Utils::Vector3i const &flag); +#endif // EXTERNAL_FORCES /** Call only on the head node: remove bond from particle. * @param part identity of principal atom of the bond. @@ -425,8 +425,11 @@ inline Utils::Vector3d get_particle_ext_torque(Particle const *p) { return p->p.ext_torque; } #endif -inline uint8_t get_particle_fix(Particle const *p) { return p->p.ext_flag; } -#endif +inline Utils::Vector3i get_particle_fix(Particle const *p) { + return Utils::Vector3i{ + {p->is_fixed_along(0), p->is_fixed_along(1), p->is_fixed_along(2)}}; +} +#endif // EXTERNAL_FORCES #ifdef THERMOSTAT_PER_PARTICLE #ifdef PARTICLE_ANISOTROPY diff --git a/src/core/thermostats/brownian_inline.hpp b/src/core/thermostats/brownian_inline.hpp index 3b20d9da1d0..8bb01a83d51 100644 --- a/src/core/thermostats/brownian_inline.hpp +++ b/src/core/thermostats/brownian_inline.hpp @@ -70,25 +70,16 @@ inline Utils::Vector3d bd_drag(Thermostat::GammaType const &brownian_gamma, // Only a conservative part of the force is used here #ifdef PARTICLE_ANISOTROPY if (aniso_flag) { -#ifdef EXTERNAL_FORCES - if (!(p.p.ext_flag & COORD_FIXED(j))) -#endif - { + if (!p.is_fixed_along(j)) { position[j] = delta_pos_lab[j]; } } else { -#ifdef EXTERNAL_FORCES - if (!(p.p.ext_flag & COORD_FIXED(j))) -#endif - { + if (!p.is_fixed_along(j)) { position[j] = p.f.f[j] * dt / gamma[j]; } } #else -#ifdef EXTERNAL_FORCES - if (!(p.p.ext_flag & COORD_FIXED(j))) -#endif - { + if (!p.is_fixed_along(j)) { position[j] = p.f.f[j] * dt / gamma; } #endif // PARTICLE_ANISOTROPY @@ -133,25 +124,16 @@ inline Utils::Vector3d bd_drag_vel(Thermostat::GammaType const &brownian_gamma, // here. #ifdef PARTICLE_ANISOTROPY if (aniso_flag) { -#ifdef EXTERNAL_FORCES - if (!(p.p.ext_flag & COORD_FIXED(j))) -#endif - { + if (!p.is_fixed_along(j)) { velocity[j] = vel_lab[j]; } } else { -#ifdef EXTERNAL_FORCES - if (!(p.p.ext_flag & COORD_FIXED(j))) -#endif - { + if (!p.is_fixed_along(j)) { velocity[j] = p.f.f[j] / gamma[j]; } } -#else // PARTICLE_ANISOTROPY -#ifdef EXTERNAL_FORCES - if (!(p.p.ext_flag & COORD_FIXED(j))) -#endif - { +#else // PARTICLE_ANISOTROPY + if (!p.is_fixed_along(j)) { velocity[j] = p.f.f[j] / gamma; } #endif // PARTICLE_ANISOTROPY @@ -190,10 +172,7 @@ inline Utils::Vector3d bd_random_walk(BrownianThermostat const &brownian, auto const noise = Random::noise_gaussian( brownian.rng_counter(), brownian.rng_seed(), p.p.identity); for (int j = 0; j < 3; j++) { -#ifdef EXTERNAL_FORCES - if (!(p.p.ext_flag & COORD_FIXED(j))) -#endif - { + if (!p.is_fixed_along(j)) { #ifndef PARTICLE_ANISOTROPY if (sigma_pos > 0.0) { delta_pos_body[j] = sigma_pos * sqrt(dt) * noise[j]; @@ -222,10 +201,7 @@ inline Utils::Vector3d bd_random_walk(BrownianThermostat const &brownian, Utils::Vector3d position = {}; for (int j = 0; j < 3; j++) { -#ifdef EXTERNAL_FORCES - if (!(p.p.ext_flag & COORD_FIXED(j))) -#endif - { + if (!p.is_fixed_along(j)) { #ifdef PARTICLE_ANISOTROPY position[j] += aniso_flag ? delta_pos_lab[j] : delta_pos_body[j]; #else @@ -251,10 +227,7 @@ inline Utils::Vector3d bd_random_walk_vel(BrownianThermostat const &brownian, brownian.rng_counter(), brownian.rng_seed(), p.identity()); Utils::Vector3d velocity = {}; for (int j = 0; j < 3; j++) { -#ifdef EXTERNAL_FORCES - if (!(p.p.ext_flag & COORD_FIXED(j))) -#endif - { + if (!p.is_fixed_along(j)) { // Random (heat) velocity. See eq. (10.2.16) taking into account eq. // (10.2.18) and (10.2.29), Pottier2010. Note, that the Pottier2010 units // system (see Eq. (10.1.1) there) has been adapted towards the ESPResSo @@ -292,10 +265,7 @@ bd_drag_rot(Thermostat::GammaType const &brownian_gamma_rotation, Particle &p, Utils::Vector3d dphi = {}; for (int j = 0; j < 3; j++) { -#ifdef EXTERNAL_FORCES - if (!(p.p.ext_flag & COORD_FIXED(j))) -#endif - { + if (!p.is_fixed_along(j)) { // only a conservative part of the torque is used here #ifndef PARTICLE_ANISOTROPY dphi[j] = p.f.torque[j] * dt / gamma; @@ -334,10 +304,7 @@ bd_drag_vel_rot(Thermostat::GammaType const &brownian_gamma_rotation, Utils::Vector3d omega = {}; for (int j = 0; j < 3; j++) { -#ifdef EXTERNAL_FORCES - if (!(p.p.ext_flag & COORD_FIXED(j))) -#endif - { + if (!p.is_fixed_along(j)) { // only conservative part of the force is used here #ifndef PARTICLE_ANISOTROPY omega[j] = p.f.torque[j] / gamma; @@ -376,10 +343,7 @@ bd_random_walk_rot(BrownianThermostat const &brownian, Particle const &p, auto const noise = Random::noise_gaussian( brownian.rng_counter(), brownian.rng_seed(), p.p.identity); for (int j = 0; j < 3; j++) { -#ifdef EXTERNAL_FORCES - if (!(p.p.ext_flag & COORD_FIXED(j))) -#endif - { + if (!p.is_fixed_along(j)) { #ifndef PARTICLE_ANISOTROPY if (sigma_pos > 0.0) { dphi[j] = noise[j] * sigma_pos * sqrt(dt); @@ -414,10 +378,7 @@ bd_random_walk_vel_rot(BrownianThermostat const &brownian, Particle const &p) { auto const noise = Random::noise_gaussian( brownian.rng_counter(), brownian.rng_seed(), p.p.identity); for (int j = 0; j < 3; j++) { -#ifdef EXTERNAL_FORCES - if (!(p.p.ext_flag & COORD_FIXED(j))) -#endif - { + if (!p.is_fixed_along(j)) { domega[j] = sigma_vel * noise[j] / sqrt(p.p.rinertia[j]); } } diff --git a/src/python/espressomd/particle_data.pxd b/src/python/espressomd/particle_data.pxd index 572cd945e1b..268f09b314b 100644 --- a/src/python/espressomd/particle_data.pxd +++ b/src/python/espressomd/particle_data.pxd @@ -164,8 +164,8 @@ cdef extern from "particle_data.hpp": void set_particle_ext_force(int part, const Vector3d & force) Vector3d get_particle_ext_force(const particle * p) - void set_particle_fix(int part, stdint.uint8_t flag) - stdint.uint8_t get_particle_fix(const particle * p) + void set_particle_fix(int part, const Vector3i & flag) + Vector3i get_particle_fix(const particle * p) void delete_particle_bond(int part, Span[const int] bond) void delete_particle_bonds(int part) diff --git a/src/python/espressomd/particle_data.pyx b/src/python/espressomd/particle_data.pyx index e2b43014ab4..302703ce6b1 100644 --- a/src/python/espressomd/particle_data.pyx +++ b/src/python/espressomd/particle_data.pyx @@ -36,12 +36,6 @@ from .utils cimport make_Vector3d from .grid cimport box_geo, folded_position, unfolded_position -def _COORD_FIXED(coord): - return 2L << coord - - -COORDS_FIX_MASK = _COORD_FIXED(0) | _COORD_FIXED(1) | _COORD_FIXED(2) - # List of particle attributes for pickle and the like # Autogenerated from the class. Everything which is of the same # type as ParticleHandle.pos (getter_wrapper) @@ -814,24 +808,20 @@ cdef class ParticleHandle: """ - def __set__(self, _fixed_coord_flag): - cdef stdint.uint8_t ext_flag = 0 + def __set__(self, flag): + cdef Vector3i ext_flag check_type_or_throw_except( - _fixed_coord_flag, 3, int, "Fix has to be 3 bools.") - for i in map(long, range(3)): - if _fixed_coord_flag[i]: - ext_flag |= _COORD_FIXED(i) + flag, 3, int, "Property 'fix' has to be 3 bools.") + ext_flag[0] = int(flag[0]) + ext_flag[1] = int(flag[1]) + ext_flag[2] = int(flag[2]) set_particle_fix(self._id, ext_flag) def __get__(self): self.update_particle_data() - fixed_coord_flag = np.array([0, 0, 0], dtype=int) - cdef stdint.uint8_t ext_flag - ext_flag = get_particle_fix(self.particle_data) - for i in map(long, range(3)): - if ext_flag & _COORD_FIXED(i): - fixed_coord_flag[i] = 1 - return array_locked(fixed_coord_flag) + cdef Vector3i flag = get_particle_fix(self.particle_data) + ext_flag = np.array([flag[0], flag[1], flag[2]], dtype=int) + return array_locked(ext_flag) IF ROTATION: property ext_torque: From 27329f3a332f50590f153f4904b5e7eb74357ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 28 Feb 2022 19:12:03 +0100 Subject: [PATCH 64/99] core: Roll out new Particle interface --- src/core/AtomDecomposition.cpp | 4 +- src/core/CellStructure.hpp | 7 +- src/core/ComFixed.hpp | 14 +-- src/core/RegularDecomposition.cpp | 6 +- src/core/RegularDecomposition.hpp | 2 +- src/core/cluster_analysis/Cluster.hpp | 2 +- .../cluster_analysis/ClusterStructure.cpp | 2 +- src/core/constraints/ShapeBasedConstraint.hpp | 6 +- src/core/field_coupling/couplings/Charge.hpp | 2 +- src/core/field_coupling/couplings/Mass.hpp | 4 +- src/core/field_coupling/couplings/Viscous.hpp | 2 +- src/core/forcecap.cpp | 6 +- src/core/forces.cpp | 16 ++-- .../lb_particle_coupling.cpp | 4 +- src/core/integrators/brownian_inline.hpp | 20 ++-- src/core/integrators/steepest_descent.cpp | 12 +-- .../integrators/stokesian_dynamics_inline.hpp | 8 +- .../integrators/velocity_verlet_inline.hpp | 10 +- src/core/integrators/velocity_verlet_npt.cpp | 30 +++--- src/core/io/writer/h5md_core.cpp | 18 ++-- .../VerletCriterion.hpp | 4 +- src/core/observables/ParticleTraits.hpp | 2 +- src/core/particle_data.cpp | 2 +- src/core/particle_data.hpp | 38 ++++---- .../reaction_methods/ReactionAlgorithm.cpp | 6 +- .../tests/ReactionAlgorithm_test.cpp | 8 +- src/core/rotation.cpp | 94 +++++++++---------- src/core/rotation.hpp | 22 ++--- src/core/stokesian_dynamics/sd_interface.cpp | 12 +-- src/core/thermostats/brownian_inline.hpp | 62 ++++++------ src/core/thermostats/langevin_inline.hpp | 26 ++--- .../EspressoSystemStandAlone_test.cpp | 2 +- .../field_coupling_couplings_test.cpp | 28 ++---- src/core/unit_tests/thermostats_test.cpp | 2 +- .../virtual_sites/lb_inertialess_tracers.cpp | 2 +- .../utils/checks/charge_neutrality.hpp | 2 +- 36 files changed, 239 insertions(+), 248 deletions(-) diff --git a/src/core/AtomDecomposition.cpp b/src/core/AtomDecomposition.cpp index 737eb9aa6bf..ef069c72759 100644 --- a/src/core/AtomDecomposition.cpp +++ b/src/core/AtomDecomposition.cpp @@ -99,9 +99,9 @@ void AtomDecomposition::mark_cells() { void AtomDecomposition::resort(bool global_flag, std::vector &diff) { for (auto &p : local().particles()) { - fold_position(p.r.p, p.l.i, m_box); + fold_position(p.pos(), p.image_box(), m_box); - p.l.p_old = p.r.p; + p.pos_at_last_verlet_update() = p.pos(); } /* Local updates are a NoOp for this decomposition. */ diff --git a/src/core/CellStructure.hpp b/src/core/CellStructure.hpp index 395be36fbdf..fd7d77fcd09 100644 --- a/src/core/CellStructure.hpp +++ b/src/core/CellStructure.hpp @@ -375,8 +375,9 @@ struct CellStructure { Utils::Vector3d const &additional_offset = {}) const { auto const lim = Utils::sqr(skin / 2.) - additional_offset.norm2(); return std::any_of( - particles.begin(), particles.end(), - [lim](const auto &p) { return ((p.r.p - p.l.p_old).norm2() > lim); }); + particles.begin(), particles.end(), [lim](const auto &p) { + return ((p.pos() - p.pos_at_last_verlet_update()).norm2() > lim); + }); } auto get_le_pos_offset_at_last_resort() const { @@ -656,7 +657,7 @@ struct CellStructure { Cell *find_current_cell(const Particle &p) { assert(not get_resort_particles()); - if (p.l.ghost) { + if (p.is_ghost()) { return nullptr; } diff --git a/src/core/ComFixed.hpp b/src/core/ComFixed.hpp index 07a9b5ba220..4512242815f 100644 --- a/src/core/ComFixed.hpp +++ b/src/core/ComFixed.hpp @@ -41,9 +41,9 @@ template class ComFixed { for (auto const &p : particles) { /* Check if type is of interest */ - auto it = m_type_index.find(p.p.type); + auto it = m_type_index.find(p.type()); if (it != m_type_index.end()) { - ret[it->second] += Utils::Vector3d{p.f.f}; + ret[it->second] += p.force(); } } @@ -55,9 +55,9 @@ template class ComFixed { for (auto const &p : particles) { /* Check if type is of interest */ - auto it = m_type_index.find(p.p.type); + auto it = m_type_index.find(p.type()); if (it != m_type_index.end()) { - ret[it->second] += p.p.mass; + ret[it->second] += p.mass(); } } @@ -96,12 +96,12 @@ template class ComFixed { for (auto &p : particles) { /* Check if type is of interest */ - auto it = m_type_index.find(p.p.type); + auto it = m_type_index.find(p.type()); if (it != m_type_index.end()) { - auto const mass_frac = p.p.mass / masses[it->second]; + auto const mass_frac = p.mass() / masses[it->second]; auto const &type_force = forces[it->second]; for (int i = 0; i < 3; i++) { - p.f.f[i] -= mass_frac * type_force[i]; + p.force()[i] -= mass_frac * type_force[i]; } } } diff --git a/src/core/RegularDecomposition.cpp b/src/core/RegularDecomposition.cpp index 322d585018a..ab3df649bd5 100644 --- a/src/core/RegularDecomposition.cpp +++ b/src/core/RegularDecomposition.cpp @@ -80,7 +80,7 @@ void RegularDecomposition::move_if_local( ParticleList &src, ParticleList &rest, std::vector &modified_cells) { for (auto &part : src) { - auto target_cell = position_to_cell(part.r.p); + auto target_cell = position_to_cell(part.pos()); if (target_cell) { target_cell->particles().insert(std::move(part)); @@ -162,9 +162,9 @@ namespace { * @brief Fold coordinates to box and reset the old position. */ void fold_and_reset(Particle &p, BoxGeometry const &box_geo) { - fold_position(p.r.p, p.l.i, box_geo); + fold_position(p.pos(), p.image_box(), box_geo); - p.l.p_old = p.r.p; + p.pos_at_last_verlet_update() = p.pos(); } } // namespace diff --git a/src/core/RegularDecomposition.hpp b/src/core/RegularDecomposition.hpp index 419f099b680..c0ad3e14e45 100644 --- a/src/core/RegularDecomposition.hpp +++ b/src/core/RegularDecomposition.hpp @@ -106,7 +106,7 @@ struct RegularDecomposition : public ParticleDecomposition { } Cell *particle_to_cell(Particle const &p) override { - return position_to_cell(p.r.p); + return position_to_cell(p.pos()); } void resort(bool global, std::vector &diff) override; diff --git a/src/core/cluster_analysis/Cluster.hpp b/src/core/cluster_analysis/Cluster.hpp index 1d5d6cf6ce8..25c89f570aa 100644 --- a/src/core/cluster_analysis/Cluster.hpp +++ b/src/core/cluster_analysis/Cluster.hpp @@ -35,7 +35,7 @@ class Cluster { /** @brief Ids of the particles in the cluster */ std::vector particles; /** @brief add a particle to the cluster */ - void add_particle(const Particle &p) { particles.push_back(p.p.identity); } + void add_particle(const Particle &p) { particles.push_back(p.identity()); } /** @brief Calculate the center of mass of the cluster */ Utils::Vector3d center_of_mass_subcluster(std::vector const &particle_ids); diff --git a/src/core/cluster_analysis/ClusterStructure.cpp b/src/core/cluster_analysis/ClusterStructure.cpp index 180d923aa46..2a092519dfd 100644 --- a/src/core/cluster_analysis/ClusterStructure.cpp +++ b/src/core/cluster_analysis/ClusterStructure.cpp @@ -41,7 +41,7 @@ void ClusterStructure::clear() { } inline bool ClusterStructure::part_of_cluster(const Particle &p) { - return cluster_id.find(p.p.identity) != cluster_id.end(); + return cluster_id.find(p.identity()) != cluster_id.end(); } // Analyze the cluster structure of the given particles diff --git a/src/core/constraints/ShapeBasedConstraint.hpp b/src/core/constraints/ShapeBasedConstraint.hpp index 77484d2f263..044960a6e35 100644 --- a/src/core/constraints/ShapeBasedConstraint.hpp +++ b/src/core/constraints/ShapeBasedConstraint.hpp @@ -72,11 +72,11 @@ class ShapeBasedConstraint : public Constraint { bool &only_positive() { return m_only_positive; } bool &penetrable() { return m_penetrable; } - int &type() { return part_rep.p.type; } - Utils::Vector3d &velocity() { return part_rep.m.v; } + int &type() { return part_rep.type(); } + Utils::Vector3d &velocity() { return part_rep.v(); } void set_type(const int &type) { - part_rep.p.type = type; + part_rep.type() = type; make_particle_type_exist_local(type); } diff --git a/src/core/field_coupling/couplings/Charge.hpp b/src/core/field_coupling/couplings/Charge.hpp index 4f7deb3c00e..b5a2ad0ef79 100644 --- a/src/core/field_coupling/couplings/Charge.hpp +++ b/src/core/field_coupling/couplings/Charge.hpp @@ -27,7 +27,7 @@ class Charge { template T operator()(const Particle &p, const T &x) const { - return p.p.q * x; + return p.q() * x; } }; } // namespace Coupling diff --git a/src/core/field_coupling/couplings/Mass.hpp b/src/core/field_coupling/couplings/Mass.hpp index e94a82ef7f6..4d967fab6e1 100644 --- a/src/core/field_coupling/couplings/Mass.hpp +++ b/src/core/field_coupling/couplings/Mass.hpp @@ -27,8 +27,8 @@ class Mass { template T operator()(const Particle &p, const T &x) const { - if (not p.p.is_virtual) { - return p.p.mass * x; + if (not p.is_virtual()) { + return p.mass() * x; } return T{}; } diff --git a/src/core/field_coupling/couplings/Viscous.hpp b/src/core/field_coupling/couplings/Viscous.hpp index 1a4176b1047..92062aa429d 100644 --- a/src/core/field_coupling/couplings/Viscous.hpp +++ b/src/core/field_coupling/couplings/Viscous.hpp @@ -36,7 +36,7 @@ class Viscous { template Utils::Vector3d operator()(Particle const &p, Utils::Vector3d const &field) const { - return m_gamma * (field - p.m.v); + return m_gamma * (field - p.v()); } }; } // namespace Coupling diff --git a/src/core/forcecap.cpp b/src/core/forcecap.cpp index 977c58f5c3e..f0cc4ba83a9 100644 --- a/src/core/forcecap.cpp +++ b/src/core/forcecap.cpp @@ -45,9 +45,9 @@ void forcecap_cap(ParticleRange const &particles) { auto const force_cap_sq = Utils::sqr(force_cap); for (auto &p : particles) { - auto const force_sq = p.f.f.norm2(); + auto const force_sq = p.force().norm2(); if (force_sq > force_cap_sq) { - p.f.f *= force_cap / std::sqrt(force_sq); + p.force() *= force_cap / std::sqrt(force_sq); } } } @@ -61,4 +61,4 @@ REGISTER_CALLBACK(mpi_set_forcecap_local) void mpi_set_forcecap(double force_cap) { mpi_call_all(mpi_set_forcecap_local, force_cap); -} \ No newline at end of file +} diff --git a/src/core/forces.cpp b/src/core/forces.cpp index 3847f17d123..9b7623e4132 100644 --- a/src/core/forces.cpp +++ b/src/core/forces.cpp @@ -66,17 +66,17 @@ inline ParticleForce external_force(Particle const &p) { ParticleForce f = {}; #ifdef EXTERNAL_FORCES - f.f += p.p.ext_force; + f.f += p.ext_force(); #ifdef ROTATION - f.torque += p.p.ext_torque; + f.torque += p.ext_torque(); #endif #endif #ifdef ENGINE // apply a swimming force in the direction of // the particle's orientation axis - if (p.p.swim.swimming) { - f.f += p.p.swim.f_swim * p.r.calc_director(); + if (p.swimming().swimming) { + f.f += p.swimming().f_swim * p.calc_director(); } #endif @@ -92,10 +92,10 @@ inline ParticleForce thermostat_force(Particle const &p, double time_step, #ifdef ROTATION return {friction_thermo_langevin(langevin, p, time_step, kT), - p.p.rotation ? convert_vector_body_to_space( - p, friction_thermo_langevin_rotation( - langevin, p, time_step, kT)) - : Utils::Vector3d{}}; + 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 diff --git a/src/core/grid_based_algorithms/lb_particle_coupling.cpp b/src/core/grid_based_algorithms/lb_particle_coupling.cpp index 5e084386467..35d44f5bd4b 100644 --- a/src/core/grid_based_algorithms/lb_particle_coupling.cpp +++ b/src/core/grid_based_algorithms/lb_particle_coupling.cpp @@ -154,7 +154,7 @@ Utils::Vector3d lb_viscous_coupling(Particle const &p, Utils::Vector3d v_drift = interpolated_u; #ifdef ENGINE if (p.swimming().swimming) { - v_drift += p.swimming().v_swim * p.r.calc_director(); + v_drift += p.swimming().v_swim * p.calc_director(); } #endif @@ -229,7 +229,7 @@ void add_swimmer_force(Particle const &p, double time_step) { // calculate source position const double direction = double(p.swimming().push_pull) * p.swimming().dipole_length; - auto const director = p.r.calc_director(); + auto const director = p.calc_director(); auto const source_position = p.pos() + direction * director; if (not in_local_halo(source_position)) { diff --git a/src/core/integrators/brownian_inline.hpp b/src/core/integrators/brownian_inline.hpp index a0ff4bc7b6a..71eb4c3473a 100644 --- a/src/core/integrators/brownian_inline.hpp +++ b/src/core/integrators/brownian_inline.hpp @@ -37,19 +37,19 @@ inline void brownian_dynamics_propagator(BrownianThermostat const &brownian, double time_step, double kT) { for (auto &p : particles) { // Don't propagate translational degrees of freedom of vs - if (!(p.p.is_virtual) or thermo_virtual) { - p.r.p += bd_drag(brownian.gamma, p, time_step); - p.m.v = bd_drag_vel(brownian.gamma, p); - p.r.p += bd_random_walk(brownian, p, time_step, kT); - p.m.v += bd_random_walk_vel(brownian, p); + 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.p.rotation) + if (!p.can_rotate()) continue; convert_torque_to_body_frame_apply_fix(p); - p.r.quat = bd_drag_rot(brownian.gamma_rotation, p, time_step); - p.m.omega = bd_drag_vel_rot(brownian.gamma_rotation, p); - p.r.quat = bd_random_walk_rot(brownian, p, time_step, kT); - p.m.omega += bd_random_walk_vel_rot(brownian, 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 08705399a10..52aa78b821a 100644 --- a/src/core/integrators/steepest_descent.cpp +++ b/src/core/integrators/steepest_descent.cpp @@ -56,25 +56,25 @@ bool steepest_descent_step(const ParticleRange &particles) { // Skip, if coordinate is fixed if (!p.is_fixed_along(j)) { // Skip positional increments of virtual particles - if (!p.p.is_virtual) { + if (!p.is_virtual()) { // Square of force on particle - f += Utils::sqr(p.f.f[j]); + f += Utils::sqr(p.force()[j]); // Positional increment, crop to maximum allowed by user - auto const dp = boost::algorithm::clamp(params.gamma * p.f.f[j], + auto const dp = boost::algorithm::clamp(params.gamma * p.force()[j], -params.max_displacement, params.max_displacement); // Move particle - p.r.p[j] += dp; + p.pos()[j] += dp; } } } #ifdef ROTATION { // Rotational increment - auto const dq = params.gamma * p.f.torque; // Vector parallel to torque - auto const t = p.f.torque.norm2(); + auto const dq = params.gamma * p.torque(); // Vector parallel to torque + auto const t = p.torque().norm2(); // Normalize rotation axis and compute amount of rotation auto const l = dq.norm(); diff --git a/src/core/integrators/stokesian_dynamics_inline.hpp b/src/core/integrators/stokesian_dynamics_inline.hpp index c7b5e0ec1e0..a184ba07c7d 100644 --- a/src/core/integrators/stokesian_dynamics_inline.hpp +++ b/src/core/integrators/stokesian_dynamics_inline.hpp @@ -37,12 +37,12 @@ inline void stokesian_dynamics_propagate_vel_pos(const ParticleRange &particles, for (auto &p : particles) { // Translate particle - p.r.p += p.m.v * time_step; + p.pos() += p.v() * time_step; // Perform rotation - auto const norm = p.m.omega.norm(); - if (norm != 0) { - auto const omega_unit = (1. / norm) * p.m.omega; + auto const norm = p.omega().norm(); + if (norm != 0.) { + auto const omega_unit = (1. / norm) * p.omega(); local_rotate_particle(p, omega_unit, norm * time_step); } } diff --git a/src/core/integrators/velocity_verlet_inline.hpp b/src/core/integrators/velocity_verlet_inline.hpp index 53f20705fd0..04bbd0bffc3 100644 --- a/src/core/integrators/velocity_verlet_inline.hpp +++ b/src/core/integrators/velocity_verlet_inline.hpp @@ -41,16 +41,16 @@ inline void velocity_verlet_propagate_vel_pos(const ParticleRange &particles, #endif // Don't propagate translational degrees of freedom of vs - if (p.p.is_virtual) + if (p.is_virtual()) continue; for (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.m.v[j] += 0.5 * time_step * p.f.f[j] / p.p.mass; + 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.r.p[j] += time_step * p.m.v[j]; + p.pos()[j] += time_step * p.v()[j]; } } } @@ -64,13 +64,13 @@ inline void velocity_verlet_propagate_vel_final(const ParticleRange &particles, for (auto &p : particles) { // Virtual sites are not propagated during integration - if (p.p.is_virtual) + if (p.is_virtual()) continue; for (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.m.v[j] += 0.5 * time_step * p.f.f[j] / p.p.mass; + p.v()[j] += 0.5 * time_step * p.force()[j] / p.mass(); } } } diff --git a/src/core/integrators/velocity_verlet_npt.cpp b/src/core/integrators/velocity_verlet_npt.cpp index 1f90e159c09..6030793d4d6 100644 --- a/src/core/integrators/velocity_verlet_npt.cpp +++ b/src/core/integrators/velocity_verlet_npt.cpp @@ -49,17 +49,17 @@ void velocity_verlet_npt_propagate_vel_final(const ParticleRange &particles, for (auto &p : particles) { // Virtual sites are not propagated during integration - if (p.p.is_virtual) + if (p.is_virtual()) continue; - auto const noise = friction_therm0_nptiso<2>(npt_iso, p.m.v, p.p.identity); + auto const noise = friction_therm0_nptiso<2>(npt_iso, p.v(), p.identity()); for (int j = 0; j < 3; j++) { if (!p.is_fixed_along(j)) { if (nptiso.geometry & nptiso.nptgeom_dir[j]) { - nptiso.p_vel[j] += Utils::sqr(p.m.v[j] * time_step) * p.p.mass; - p.m.v[j] += (p.f.f[j] * time_step / 2.0 + noise[j]) / p.p.mass; + nptiso.p_vel[j] += Utils::sqr(p.v()[j] * time_step) * p.mass(); + p.v()[j] += (p.force()[j] * time_step / 2.0 + noise[j]) / p.mass(); } else { // Propagate velocity: v(t+dt) = v(t+0.5*dt) + 0.5*dt * a(t+dt) - p.m.v[j] += p.f.f[j] * time_step / 2.0 / p.p.mass; + p.v()[j] += p.force()[j] * time_step / 2.0 / p.mass(); } } } @@ -120,16 +120,16 @@ void velocity_verlet_npt_propagate_pos(const ParticleRange &particles, /* propagate positions while rescaling positions and velocities */ for (auto &p : particles) { - if (p.p.is_virtual) + if (p.is_virtual()) continue; for (int j = 0; j < 3; j++) { if (!p.is_fixed_along(j)) { if (nptiso.geometry & nptiso.nptgeom_dir[j]) { - p.r.p[j] = scal[1] * (p.r.p[j] + scal[2] * p.m.v[j] * time_step); - p.l.p_old[j] *= scal[1]; - p.m.v[j] *= scal[0]; + p.pos()[j] = scal[1] * (p.pos()[j] + scal[2] * p.v()[j] * time_step); + p.pos_at_last_verlet_update()[j] *= scal[1]; + p.v()[j] *= scal[0]; } else { - p.r.p[j] += p.m.v[j] * time_step; + p.pos()[j] += p.v()[j] * time_step; } } } @@ -168,19 +168,19 @@ void velocity_verlet_npt_propagate_vel(const ParticleRange &particles, #endif // Don't propagate translational degrees of freedom of vs - if (p.p.is_virtual) + if (p.is_virtual()) continue; for (int j = 0; j < 3; j++) { if (!p.is_fixed_along(j)) { auto const noise = - friction_therm0_nptiso<1>(npt_iso, p.m.v, p.p.identity); + friction_therm0_nptiso<1>(npt_iso, p.v(), p.identity()); if (integ_switch == INTEG_METHOD_NPT_ISO && (nptiso.geometry & nptiso.nptgeom_dir[j])) { - p.m.v[j] += (p.f.f[j] * time_step / 2.0 + noise[j]) / p.p.mass; - nptiso.p_vel[j] += Utils::sqr(p.m.v[j] * time_step) * p.p.mass; + p.v()[j] += (p.force()[j] * time_step / 2.0 + noise[j]) / p.mass(); + nptiso.p_vel[j] += Utils::sqr(p.v()[j] * time_step) * p.mass(); } else { // Propagate velocities: v(t+0.5*dt) = v(t) + 0.5*dt * a(t) - p.m.v[j] += p.f.f[j] * time_step / 2.0 / p.p.mass; + p.v()[j] += p.force()[j] * time_step / 2.0 / p.mass(); } } } diff --git a/src/core/io/writer/h5md_core.cpp b/src/core/io/writer/h5md_core.cpp index d2c0328062d..883e92808c3 100644 --- a/src/core/io/writer/h5md_core.cpp +++ b/src/core/io/writer/h5md_core.cpp @@ -322,7 +322,7 @@ void File::write(const ParticleRange &particles, double time, int step, write_td_particle_property<2>( prefix, n_part_global, particles, datasets["particles/atoms/id/value"], - [](auto const &p) { return Utils::Vector{p.p.identity}; }); + [](auto const &p) { return Utils::Vector{p.identity()}; }); write_dataset(Utils::Vector{time}, datasets["particles/atoms/id/time"], Vector1hs{1}, @@ -334,31 +334,31 @@ void File::write(const ParticleRange &particles, double time, int step, write_td_particle_property<2>( prefix, n_part_global, particles, datasets["particles/atoms/species/value"], - [](auto const &p) { return Utils::Vector{p.p.type}; }); + [](auto const &p) { return Utils::Vector{p.type()}; }); write_td_particle_property<2>( prefix, n_part_global, particles, datasets["particles/atoms/mass/value"], - [](auto const &p) { return Utils::Vector{p.p.mass}; }); + [](auto const &p) { return Utils::Vector{p.mass()}; }); write_td_particle_property<3>( prefix, n_part_global, particles, datasets["particles/atoms/position/value"], - [&](auto const &p) { return folded_position(p.r.p, geometry); }); + [&](auto const &p) { return folded_position(p.pos(), geometry); }); write_td_particle_property<3>(prefix, n_part_global, particles, datasets["particles/atoms/image/value"], - [](auto const &p) { return p.l.i; }); + [](auto const &p) { return p.image_box(); }); write_td_particle_property<3>(prefix, n_part_global, particles, datasets["particles/atoms/velocity/value"], - [](auto const &p) { return p.m.v; }); + [](auto const &p) { return p.v(); }); write_td_particle_property<3>(prefix, n_part_global, particles, datasets["particles/atoms/force/value"], - [](auto const &p) { return p.f.f; }); + [](auto const &p) { return p.force(); }); write_td_particle_property<2>( prefix, n_part_global, particles, datasets["particles/atoms/charge/value"], - [](auto const &p) { return Utils::Vector{p.p.q}; }); + [](auto const &p) { return Utils::Vector{p.q()}; }); } void File::write_connectivity(const ParticleRange &particles) { MultiArray3i bond(boost::extents[0][0][0]); @@ -369,7 +369,7 @@ void File::write_connectivity(const ParticleRange &particles) { auto const partner_ids = b.partner_ids(); if (partner_ids.size() == 1) { bond.resize(boost::extents[1][nbonds_local + 1][2]); - bond[0][nbonds_local][0] = p.p.identity; + bond[0][nbonds_local][0] = p.identity(); bond[0][nbonds_local][1] = partner_ids[0]; nbonds_local++; } diff --git a/src/core/nonbonded_interactions/VerletCriterion.hpp b/src/core/nonbonded_interactions/VerletCriterion.hpp index d6a62a40f03..c64ee87e90a 100644 --- a/src/core/nonbonded_interactions/VerletCriterion.hpp +++ b/src/core/nonbonded_interactions/VerletCriterion.hpp @@ -68,13 +68,13 @@ template class VerletCriterion { #ifdef ELECTROSTATICS // Within real space cutoff of electrostatics and both charged - if ((dist2 <= m_eff_coulomb_cut2) && (p1.p.q != 0) && (p2.p.q != 0)) + if (dist2 <= m_eff_coulomb_cut2 and p1.q() != 0. and p2.q() != 0.) return true; #endif #ifdef DIPOLES // Within dipolar cutoff and both carry magnetic moments - if ((dist2 <= m_eff_dipolar_cut2) && (p1.p.dipm != 0) && (p2.p.dipm != 0)) + if (dist2 <= m_eff_dipolar_cut2 and p1.dipm() != 0. and p2.dipm() != 0.) return true; #endif diff --git a/src/core/observables/ParticleTraits.hpp b/src/core/observables/ParticleTraits.hpp index ccc60cee2e0..761c729a2f7 100644 --- a/src/core/observables/ParticleTraits.hpp +++ b/src/core/observables/ParticleTraits.hpp @@ -30,7 +30,7 @@ namespace ParticleObservables { */ template <> struct traits { auto position(Particle const &p) const { return p.pos(); } - auto velocity(Particle const &p) const { return p.m.v; } + auto velocity(Particle const &p) const { return p.v(); } auto mass(Particle const &p) const { #ifdef VIRTUAL_SITES // we exclude virtual particles since their mass does not have a meaning diff --git a/src/core/particle_data.cpp b/src/core/particle_data.cpp index 34d9a391aa8..dcdf61cf22b 100644 --- a/src/core/particle_data.cpp +++ b/src/core/particle_data.cpp @@ -1190,7 +1190,7 @@ void init_type_map(int type) { auto &map_for_type = particle_type_map[type]; map_for_type.clear(); for (auto const &p : partCfg()) { - if (p.p.type == type) + if (p.type() == type) map_for_type.insert(p.id()); } } diff --git a/src/core/particle_data.hpp b/src/core/particle_data.hpp index 8ef223d048b..90640413fb6 100644 --- a/src/core/particle_data.hpp +++ b/src/core/particle_data.hpp @@ -375,54 +375,54 @@ int number_of_particles_with_type(int type); #ifdef LB_ELECTROHYDRODYNAMICS inline Utils::Vector3d get_particle_mu_E(Particle const *p) { - return p->p.mu_E; + return p->mu_E(); } #endif #ifdef ROTATION inline Utils::Vector3d get_particle_omega_body(Particle const *p) { - return p->m.omega; + return p->omega(); } inline Utils::Vector3d get_particle_torque_body(Particle const *p) { - return p->f.torque; + return p->torque(); } inline Utils::Quaternion get_particle_quat(Particle const *p) { - return p->r.quat; + return p->quat(); } #endif -inline double get_particle_q(Particle const *p) { return p->p.q; } +inline double get_particle_q(Particle const *p) { return p->q(); } #ifdef VIRTUAL_SITES -inline bool get_particle_virtual(Particle const *p) { return p->p.is_virtual; } +inline bool get_particle_virtual(Particle const *p) { return p->is_virtual(); } #endif #ifdef VIRTUAL_SITES_RELATIVE inline Utils::Quaternion get_particle_vs_quat(Particle const *p) { - return p->p.vs_relative.quat; + return p->vs_relative().quat; } inline Utils::Quaternion get_particle_vs_relative(Particle const *p, int &vs_relative_to, double &vs_distance) { - vs_relative_to = p->p.vs_relative.to_particle_id; - vs_distance = p->p.vs_relative.distance; - return p->p.vs_relative.rel_orientation; + vs_relative_to = p->vs_relative().to_particle_id; + vs_distance = p->vs_relative().distance; + return p->vs_relative().rel_orientation; } #endif #ifdef DIPOLES -inline double get_particle_dipm(Particle const *p) { return p->p.dipm; } +inline double get_particle_dipm(Particle const *p) { return p->dipm(); } #endif #ifdef EXTERNAL_FORCES inline Utils::Vector3d get_particle_ext_force(Particle const *p) { - return p->p.ext_force; + return p->ext_force(); } #ifdef ROTATION inline Utils::Vector3d get_particle_ext_torque(Particle const *p) { - return p->p.ext_torque; + return p->ext_torque(); } #endif inline Utils::Vector3i get_particle_fix(Particle const *p) { @@ -434,19 +434,19 @@ inline Utils::Vector3i get_particle_fix(Particle const *p) { #ifdef THERMOSTAT_PER_PARTICLE #ifdef PARTICLE_ANISOTROPY inline Utils::Vector3d get_particle_gamma(Particle const *p) { - return p->p.gamma; + return p->gamma(); } #else -inline double get_particle_gamma(Particle const *p) { return p->p.gamma; } +inline double get_particle_gamma(Particle const *p) { return p->gamma(); } #endif // PARTICLE_ANISOTROPY #ifdef ROTATION #ifdef PARTICLE_ANISOTROPY inline Utils::Vector3d get_particle_gamma_rot(Particle const *p) { - return p->p.gamma_rot; + return p->gamma_rot(); } #else inline double get_particle_gamma_rot(Particle const *p) { - return p->p.gamma_rot; + return p->gamma_rot(); } #endif // PARTICLE_ANISOTROPY #endif // ROTATION @@ -454,13 +454,13 @@ inline double get_particle_gamma_rot(Particle const *p) { #ifdef ENGINE inline ParticleParametersSwimming get_particle_swimming(Particle const *p) { - return p->p.swim; + return p->swimming(); } #endif #ifdef ROTATIONAL_INERTIA inline Utils::Vector3d get_particle_rotational_inertia(Particle const *p) { - return p->p.rinertia; + return p->rinertia(); } #endif diff --git a/src/core/reaction_methods/ReactionAlgorithm.cpp b/src/core/reaction_methods/ReactionAlgorithm.cpp index d6bea4bc8b6..4cf82bc7cab 100644 --- a/src/core/reaction_methods/ReactionAlgorithm.cpp +++ b/src/core/reaction_methods/ReactionAlgorithm.cpp @@ -382,7 +382,7 @@ void ReactionAlgorithm::check_exclusion_radius(int p_id) { return; } auto const &p = get_particle_data(p_id); - auto const d_min = distto(partCfg(), p.r.p, p_id); + auto const d_min = distto(partCfg(), p.pos(), p_id); if (d_min < exclusion_radius) particle_inside_exclusion_radius_touched = true; } @@ -527,9 +527,9 @@ ReactionAlgorithm::generate_new_particle_positions(int type, int n_particles) { drawn_pids.emplace_back(p_id); // store original position auto const &p = get_particle_data(p_id); - old_positions.emplace_back(std::pair{p_id, p.r.p}); + old_positions.emplace_back(std::pair{p_id, p.pos()}); // write new position - auto const prefactor = std::sqrt(kT / p.p.mass); + auto const prefactor = std::sqrt(kT / p.mass()); auto const new_pos = get_random_position_in_box(); move_particle(p_id, new_pos, prefactor); check_exclusion_radius(p_id); diff --git a/src/core/reaction_methods/tests/ReactionAlgorithm_test.cpp b/src/core/reaction_methods/tests/ReactionAlgorithm_test.cpp index 2c6fd66e3ee..a84a219160a 100644 --- a/src/core/reaction_methods/tests/ReactionAlgorithm_test.cpp +++ b/src/core/reaction_methods/tests/ReactionAlgorithm_test.cpp @@ -168,8 +168,8 @@ BOOST_AUTO_TEST_CASE(ReactionAlgorithm_test) { auto const ref_old_pos = ref_positions[pid].first; auto const ref_old_vel = ref_positions[pid].second; auto const &p = get_particle_data(pid); - auto const &new_pos = p.r.p; - auto const &new_vel = p.m.v; + auto const &new_pos = p.pos(); + auto const &new_vel = p.v(); BOOST_CHECK_EQUAL(item.second, ref_old_pos); BOOST_CHECK_GE(new_pos, Utils::Vector3d::broadcast(0.)); BOOST_CHECK_LE(new_pos, Utils::Vector3d::broadcast(box_l)); @@ -204,7 +204,7 @@ BOOST_AUTO_TEST_CASE(ReactionAlgorithm_test) { for (auto const pid : {0, 1}) { auto const ref_old_pos = ref_positions[pid]; auto const &p = get_particle_data(pid); - auto const &new_pos = p.r.p; + auto const &new_pos = p.pos(); BOOST_CHECK_LE((new_pos - ref_old_pos).norm(), tol); } // force a MC move to be accepted by using a constant Hamiltonian @@ -215,7 +215,7 @@ BOOST_AUTO_TEST_CASE(ReactionAlgorithm_test) { // check that only one particle moved for (auto const pid : {0, 1}) { auto const &p = get_particle_data(pid); - distances[pid] = (ref_positions[pid] - p.r.p).norm(); + distances[pid] = (ref_positions[pid] - p.pos()).norm(); } BOOST_CHECK_LE(std::min(distances[0], distances[1]), tol); BOOST_CHECK_GE(std::max(distances[0], distances[1]), 0.1); diff --git a/src/core/rotation.cpp b/src/core/rotation.cpp index cbda1e537f5..d402130f5a1 100644 --- a/src/core/rotation.cpp +++ b/src/core/rotation.cpp @@ -58,52 +58,52 @@ static void define_Qdd(Particle const &p, Utils::Quaternion &Qd, Utils::Vector3d &Wd) { /* calculate the first derivative of the quaternion */ /* Eq. (4) @cite sonnenschein85a */ - Qd[0] = 0.5 * (-p.r.quat[1] * p.m.omega[0] - p.r.quat[2] * p.m.omega[1] - - p.r.quat[3] * p.m.omega[2]); + Qd[0] = 0.5 * (-p.quat()[1] * p.omega()[0] - p.quat()[2] * p.omega()[1] - + p.quat()[3] * p.omega()[2]); - Qd[1] = 0.5 * (p.r.quat[0] * p.m.omega[0] - p.r.quat[3] * p.m.omega[1] + - p.r.quat[2] * p.m.omega[2]); + Qd[1] = 0.5 * (p.quat()[0] * p.omega()[0] - p.quat()[3] * p.omega()[1] + + p.quat()[2] * p.omega()[2]); - Qd[2] = 0.5 * (p.r.quat[3] * p.m.omega[0] + p.r.quat[0] * p.m.omega[1] - - p.r.quat[1] * p.m.omega[2]); + Qd[2] = 0.5 * (p.quat()[3] * p.omega()[0] + p.quat()[0] * p.omega()[1] - + p.quat()[1] * p.omega()[2]); - Qd[3] = 0.5 * (-p.r.quat[2] * p.m.omega[0] + p.r.quat[1] * p.m.omega[1] + - p.r.quat[0] * p.m.omega[2]); + Qd[3] = 0.5 * (-p.quat()[2] * p.omega()[0] + p.quat()[1] * p.omega()[1] + + p.quat()[0] * p.omega()[2]); /* Calculate the angular acceleration. */ /* Eq. (5) @cite sonnenschein85a */ - if (p.p.rotation & ROTATION_X) - Wd[0] = (p.f.torque[0] + p.m.omega[1] * p.m.omega[2] * - (p.p.rinertia[1] - p.p.rinertia[2])) / - p.p.rinertia[0]; - if (p.p.rotation & ROTATION_Y) - Wd[1] = (p.f.torque[1] + p.m.omega[2] * p.m.omega[0] * - (p.p.rinertia[2] - p.p.rinertia[0])) / - p.p.rinertia[1]; - if (p.p.rotation & ROTATION_Z) - Wd[2] = (p.f.torque[2] + p.m.omega[0] * p.m.omega[1] * - (p.p.rinertia[0] - p.p.rinertia[1])) / - p.p.rinertia[2]; + if (p.can_rotate_around(0)) + Wd[0] = (p.torque()[0] + p.omega()[1] * p.omega()[2] * + (p.rinertia()[1] - p.rinertia()[2])) / + p.rinertia()[0]; + if (p.can_rotate_around(1)) + Wd[1] = (p.torque()[1] + p.omega()[2] * p.omega()[0] * + (p.rinertia()[2] - p.rinertia()[0])) / + p.rinertia()[1]; + if (p.can_rotate_around(2)) + Wd[2] = (p.torque()[2] + p.omega()[0] * p.omega()[1] * + (p.rinertia()[0] - p.rinertia()[1])) / + p.rinertia()[2]; auto const S1 = Qd.norm2(); /* Calculate the second derivative of the quaternion. */ /* Eq. (8) @cite sonnenschein85a */ Qdd[0] = - 0.5 * (-p.r.quat[1] * Wd[0] - p.r.quat[2] * Wd[1] - p.r.quat[3] * Wd[2]) - - p.r.quat[0] * S1; + 0.5 * (-p.quat()[1] * Wd[0] - p.quat()[2] * Wd[1] - p.quat()[3] * Wd[2]) - + p.quat()[0] * S1; Qdd[1] = - 0.5 * (p.r.quat[0] * Wd[0] - p.r.quat[3] * Wd[1] + p.r.quat[2] * Wd[2]) - - p.r.quat[1] * S1; + 0.5 * (p.quat()[0] * Wd[0] - p.quat()[3] * Wd[1] + p.quat()[2] * Wd[2]) - + p.quat()[1] * S1; Qdd[2] = - 0.5 * (p.r.quat[3] * Wd[0] + p.r.quat[0] * Wd[1] - p.r.quat[1] * Wd[2]) - - p.r.quat[2] * S1; + 0.5 * (p.quat()[3] * Wd[0] + p.quat()[0] * Wd[1] - p.quat()[1] * Wd[2]) - + p.quat()[2] * S1; Qdd[3] = - 0.5 * (-p.r.quat[2] * Wd[0] + p.r.quat[1] * Wd[1] + p.r.quat[0] * Wd[2]) - - p.r.quat[3] * S1; + 0.5 * (-p.quat()[2] * Wd[0] + p.quat()[1] * Wd[1] + p.quat()[0] * Wd[2]) - + p.quat()[3] * S1; S[0] = S1; S[1] = Utils::dot(Qd, Qdd); @@ -116,7 +116,7 @@ static void define_Qdd(Particle const &p, Utils::Quaternion &Qd, * notation. * * For very high angular velocities (e.g. if the product of @p time_step - * with the largest component of @ref ParticleMomentum::omega "p.m.omega" + * with the largest component of @ref ParticleMomentum::omega "p.omega()" * is superior to ~2.0), the calculation might fail. * * \todo implement for fixed_coord_flag @@ -124,14 +124,14 @@ static void define_Qdd(Particle const &p, Utils::Quaternion &Qd, void propagate_omega_quat_particle(Particle &p, double time_step) { // If rotation for the particle is disabled entirely, return early. - if (p.p.rotation == ROTATION_FIXED) + if (!p.can_rotate()) return; Utils::Quaternion Qd{}, Qdd{}; Utils::Vector3d S{}, Wd{}; // Clear rotational velocity for blocked rotation axes. - p.m.omega = Utils::mask(p.p.rotation, p.m.omega); + p.omega() = Utils::mask(p.p.rotation, p.omega()); define_Qdd(p, Qd, Qdd, S, Wd); @@ -146,15 +146,15 @@ void propagate_omega_quat_particle(Particle &p, double time_step) { assert(square >= 0.); auto const lambda = 1 - S[0] * 0.5 * time_step_squared - sqrt(square); - p.m.omega += time_step_half * Wd; - p.r.quat += time_step * (Qd + time_step_half * Qdd) - lambda * p.r.quat; + p.omega() += time_step_half * Wd; + p.quat() += time_step * (Qd + time_step_half * Qdd) - lambda * p.quat(); /* and rescale quaternion, so it is exactly of unit length */ - auto const scale = p.r.quat.norm(); + auto const scale = p.quat().norm(); if (scale == 0) { - p.r.quat = Utils::Quaternion::identity(); + p.quat() = Utils::Quaternion::identity(); } else { - p.r.quat /= scale; + p.quat() /= scale; } } @@ -162,40 +162,40 @@ 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.p.rotation == ROTATION_FIXED) + if (!p.can_rotate()) continue; convert_torque_to_body_frame_apply_fix(p); // Propagation of angular velocities - p.m.omega += hadamard_division(0.5 * time_step * p.f.torque, p.p.rinertia); + p.omega() += hadamard_division(0.5 * time_step * p.torque(), p.rinertia()); // zeroth estimate of omega - Utils::Vector3d omega_0 = p.m.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 */ - const double rinertia_diff_01 = p.p.rinertia[0] - p.p.rinertia[1]; - const double rinertia_diff_12 = p.p.rinertia[1] - p.p.rinertia[2]; - const double rinertia_diff_20 = p.p.rinertia[2] - p.p.rinertia[0]; + 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.m.omega[1] * p.m.omega[2] * rinertia_diff_12 / p.p.rinertia[0]; - Wd[1] = p.m.omega[2] * p.m.omega[0] * rinertia_diff_20 / p.p.rinertia[1]; - Wd[2] = p.m.omega[0] * p.m.omega[1] * rinertia_diff_01 / p.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.m.omega = omega_0 + (0.5 * time_step) * Wd; + p.omega() = omega_0 + (0.5 * time_step) * Wd; } } } void convert_initial_torques(const ParticleRange &particles) { for (auto &p : particles) { - if (!p.p.rotation) + if (!p.can_rotate()) continue; convert_torque_to_body_frame_apply_fix(p); } diff --git a/src/core/rotation.hpp b/src/core/rotation.hpp index f08898b4776..5c8a5f78d2b 100644 --- a/src/core/rotation.hpp +++ b/src/core/rotation.hpp @@ -59,13 +59,13 @@ void convert_initial_torques(const ParticleRange &particles); // Frame conversion routines inline Utils::Vector3d convert_vector_body_to_space(const Particle &p, const Utils::Vector3d &vec) { - return p.r.quat * vec; + return p.quat() * vec; } inline Utils::Vector3d convert_vector_space_to_body(const Particle &p, const Utils::Vector3d &v) { - assert(p.r.quat.norm() > 0.0); - return rotation_matrix(p.r.quat).transposed() * v; + assert(p.quat().norm() > 0.0); + return rotation_matrix(p.quat()).transposed() * v; } /** @@ -87,7 +87,7 @@ inline Utils::Vector3d convert_vector_space_to_body(const Particle &p, */ template auto convert_body_to_space(const Particle &p, const Utils::Matrix &A) { - auto const O = rotation_matrix(p.r.quat); + auto const O = rotation_matrix(p.quat()); return O.transposed() * A * O; } @@ -110,12 +110,12 @@ local_rotate_particle_body(Particle const &p, const Utils::Vector3d &axis_body_frame, const double phi) { // Rotation turned off entirely? - if (!p.p.rotation) - return p.r.quat; + if (!p.can_rotate()) + return p.quat(); if (std::abs(phi) > std::numeric_limits::epsilon()) - return p.r.quat * + return p.quat() * boost::qvm::rot_quat(mask(p.p.rotation, axis_body_frame), phi); - return p.r.quat; + return p.quat(); } /** Rotate the particle p around the NORMALIZED axis aSpaceFrame by amount phi @@ -126,13 +126,13 @@ inline void local_rotate_particle(Particle &p, if (std::abs(phi) > std::numeric_limits::epsilon()) { // Convert rotation axis to body-fixed frame Utils::Vector3d axis = convert_vector_space_to_body(p, axis_space_frame); - p.r.quat = local_rotate_particle_body(p, axis, phi); + p.quat() = local_rotate_particle_body(p, axis, phi); } } inline void convert_torque_to_body_frame_apply_fix(Particle &p) { - auto const torque = convert_vector_space_to_body(p, p.f.torque); - p.f.torque = mask(p.p.rotation, torque); + auto const torque = convert_vector_space_to_body(p, p.torque()); + p.torque() = mask(p.p.rotation, torque); } #endif // ROTATION diff --git a/src/core/stokesian_dynamics/sd_interface.cpp b/src/core/stokesian_dynamics/sd_interface.cpp index 9b9c61e9a1b..6adea81e0cd 100644 --- a/src/core/stokesian_dynamics/sd_interface.cpp +++ b/src/core/stokesian_dynamics/sd_interface.cpp @@ -98,13 +98,13 @@ void sd_update_locally(ParticleRange const &parts) { } // Copy velocities - p.m.v[0] = v_sd[6 * i + 0]; - p.m.v[1] = v_sd[6 * i + 1]; - p.m.v[2] = v_sd[6 * i + 2]; + p.v()[0] = v_sd[6 * i + 0]; + p.v()[1] = v_sd[6 * i + 1]; + p.v()[2] = v_sd[6 * i + 2]; - p.m.omega[0] = v_sd[6 * i + 3]; - p.m.omega[1] = v_sd[6 * i + 4]; - p.m.omega[2] = v_sd[6 * i + 5]; + p.omega()[0] = v_sd[6 * i + 3]; + p.omega()[1] = v_sd[6 * i + 4]; + p.omega()[2] = v_sd[6 * i + 5]; i++; } diff --git a/src/core/thermostats/brownian_inline.hpp b/src/core/thermostats/brownian_inline.hpp index 8bb01a83d51..30cd9541893 100644 --- a/src/core/thermostats/brownian_inline.hpp +++ b/src/core/thermostats/brownian_inline.hpp @@ -45,8 +45,8 @@ inline Utils::Vector3d bd_drag(Thermostat::GammaType const &brownian_gamma, Thermostat::GammaType gamma; #ifdef THERMOSTAT_PER_PARTICLE - if (p.p.gamma >= Thermostat::GammaType{}) { - gamma = p.p.gamma; + if (p.gamma() >= Thermostat::GammaType{}) { + gamma = p.gamma(); } else #endif { @@ -58,7 +58,7 @@ inline Utils::Vector3d bd_drag(Thermostat::GammaType const &brownian_gamma, auto const aniso_flag = (gamma[0] != gamma[1]) || (gamma[1] != gamma[2]); Utils::Vector3d delta_pos_lab; if (aniso_flag) { - auto const force_body = convert_vector_space_to_body(p, p.f.f); + auto const force_body = convert_vector_space_to_body(p, p.force()); auto const delta_pos_body = hadamard_division(force_body * dt, gamma); delta_pos_lab = convert_vector_body_to_space(p, delta_pos_body); } @@ -75,12 +75,12 @@ inline Utils::Vector3d bd_drag(Thermostat::GammaType const &brownian_gamma, } } else { if (!p.is_fixed_along(j)) { - position[j] = p.f.f[j] * dt / gamma[j]; + position[j] = p.force()[j] * dt / gamma[j]; } } #else if (!p.is_fixed_along(j)) { - position[j] = p.f.f[j] * dt / gamma; + position[j] = p.force()[j] * dt / gamma; } #endif // PARTICLE_ANISOTROPY } @@ -98,8 +98,8 @@ inline Utils::Vector3d bd_drag_vel(Thermostat::GammaType const &brownian_gamma, Thermostat::GammaType gamma; #ifdef THERMOSTAT_PER_PARTICLE - if (p.p.gamma >= Thermostat::GammaType{}) { - gamma = p.p.gamma; + if (p.gamma() >= Thermostat::GammaType{}) { + gamma = p.gamma(); } else #endif { @@ -111,7 +111,7 @@ inline Utils::Vector3d bd_drag_vel(Thermostat::GammaType const &brownian_gamma, auto const aniso_flag = (gamma[0] != gamma[1]) || (gamma[1] != gamma[2]); Utils::Vector3d vel_lab; if (aniso_flag) { - auto const force_body = convert_vector_space_to_body(p, p.f.f); + auto const force_body = convert_vector_space_to_body(p, p.force()); auto const vel_body = hadamard_division(force_body, gamma); vel_lab = convert_vector_body_to_space(p, vel_body); } @@ -129,12 +129,12 @@ inline Utils::Vector3d bd_drag_vel(Thermostat::GammaType const &brownian_gamma, } } else { if (!p.is_fixed_along(j)) { - velocity[j] = p.f.f[j] / gamma[j]; + velocity[j] = p.force()[j] / gamma[j]; } } #else // PARTICLE_ANISOTROPY if (!p.is_fixed_along(j)) { - velocity[j] = p.f.f[j] / gamma; + velocity[j] = p.force()[j] / gamma; } #endif // PARTICLE_ANISOTROPY } @@ -151,15 +151,15 @@ 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.p.is_virtual && !thermo_virtual) + 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 - if (p.p.gamma >= Thermostat::GammaType{}) { + if (p.gamma() >= Thermostat::GammaType{}) { if (kT > 0.0) { - sigma_pos = BrownianThermostat::sigma(kT, p.p.gamma); + sigma_pos = BrownianThermostat::sigma(kT, p.gamma()); } else { sigma_pos = Thermostat::GammaType{}; } @@ -170,7 +170,7 @@ inline Utils::Vector3d bd_random_walk(BrownianThermostat const &brownian, // magnitude defined in the second eq. (14.38), Schlick2010. Utils::Vector3d delta_pos_body{}; auto const noise = Random::noise_gaussian( - brownian.rng_counter(), brownian.rng_seed(), p.p.identity); + brownian.rng_counter(), brownian.rng_seed(), p.identity()); for (int j = 0; j < 3; j++) { if (!p.is_fixed_along(j)) { #ifndef PARTICLE_ANISOTROPY @@ -220,7 +220,7 @@ 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.p.is_virtual && !thermo_virtual) + if (p.is_virtual() and !thermo_virtual) return {}; auto const noise = Random::noise_gaussian( @@ -235,7 +235,7 @@ inline Utils::Vector3d bd_random_walk_vel(BrownianThermostat const &brownian, // (14.31) of Schlick2010. A difference is the mass factor to the friction // tensor. The noise is Gaussian according to the convention at p. 237 // (last paragraph), Pottier2010. - velocity[j] += brownian.sigma_vel * noise[j] / sqrt(p.p.mass); + velocity[j] += brownian.sigma_vel * noise[j] / sqrt(p.mass()); } } return velocity; @@ -255,8 +255,8 @@ bd_drag_rot(Thermostat::GammaType const &brownian_gamma_rotation, Particle &p, Thermostat::GammaType gamma; #ifdef THERMOSTAT_PER_PARTICLE - if (p.p.gamma_rot >= Thermostat::GammaType{}) { - gamma = p.p.gamma_rot; + if (p.gamma_rot() >= Thermostat::GammaType{}) { + gamma = p.gamma_rot(); } else #endif { @@ -268,9 +268,9 @@ bd_drag_rot(Thermostat::GammaType const &brownian_gamma_rotation, Particle &p, if (!p.is_fixed_along(j)) { // only a conservative part of the torque is used here #ifndef PARTICLE_ANISOTROPY - dphi[j] = p.f.torque[j] * dt / gamma; + dphi[j] = p.torque()[j] * dt / gamma; #else - dphi[j] = p.f.torque[j] * dt / gamma[j]; + dphi[j] = p.torque()[j] * dt / gamma[j]; #endif // PARTICLE_ANISOTROPY } } @@ -280,7 +280,7 @@ bd_drag_rot(Thermostat::GammaType const &brownian_gamma_rotation, Particle &p, auto const dphi_u = dphi / dphi_m; return local_rotate_particle_body(p, dphi_u, dphi_m); } - return p.r.quat; + return p.quat(); } /** Set the terminal angular velocity driven by the conservative torques drag. @@ -294,8 +294,8 @@ bd_drag_vel_rot(Thermostat::GammaType const &brownian_gamma_rotation, Thermostat::GammaType gamma; #ifdef THERMOSTAT_PER_PARTICLE - if (p.p.gamma_rot >= Thermostat::GammaType{}) { - gamma = p.p.gamma_rot; + if (p.gamma_rot() >= Thermostat::GammaType{}) { + gamma = p.gamma_rot(); } else #endif { @@ -307,9 +307,9 @@ bd_drag_vel_rot(Thermostat::GammaType const &brownian_gamma_rotation, if (!p.is_fixed_along(j)) { // only conservative part of the force is used here #ifndef PARTICLE_ANISOTROPY - omega[j] = p.f.torque[j] / gamma; + omega[j] = p.torque()[j] / gamma; #else - omega[j] = p.f.torque[j] / gamma[j]; + omega[j] = p.torque()[j] / gamma[j]; #endif // PARTICLE_ANISOTROPY } } @@ -330,9 +330,9 @@ bd_random_walk_rot(BrownianThermostat const &brownian, Particle const &p, Thermostat::GammaType sigma_pos = brownian.sigma_pos_rotation; #ifdef THERMOSTAT_PER_PARTICLE // override default if particle-specific gamma - if (p.p.gamma_rot >= Thermostat::GammaType{}) { + if (p.gamma_rot() >= Thermostat::GammaType{}) { if (kT > 0.) { - sigma_pos = BrownianThermostat::sigma(kT, p.p.gamma_rot); + sigma_pos = BrownianThermostat::sigma(kT, p.gamma_rot()); } else { sigma_pos = {}; // just an indication of the infinity } @@ -341,7 +341,7 @@ bd_random_walk_rot(BrownianThermostat const &brownian, Particle const &p, Utils::Vector3d dphi = {}; auto const noise = Random::noise_gaussian( - brownian.rng_counter(), brownian.rng_seed(), p.p.identity); + brownian.rng_counter(), brownian.rng_seed(), p.identity()); for (int j = 0; j < 3; j++) { if (!p.is_fixed_along(j)) { #ifndef PARTICLE_ANISOTROPY @@ -362,7 +362,7 @@ bd_random_walk_rot(BrownianThermostat const &brownian, Particle const &p, auto const dphi_u = dphi / dphi_m; return local_rotate_particle_body(p, dphi_u, dphi_m); } - return p.r.quat; + return p.quat(); } /** Determine the angular velocities: random walk part. @@ -376,10 +376,10 @@ bd_random_walk_vel_rot(BrownianThermostat const &brownian, Particle const &p) { Utils::Vector3d domega{}; auto const noise = Random::noise_gaussian( - brownian.rng_counter(), brownian.rng_seed(), p.p.identity); + brownian.rng_counter(), brownian.rng_seed(), p.identity()); for (int j = 0; j < 3; j++) { if (!p.is_fixed_along(j)) { - domega[j] = sigma_vel * noise[j] / sqrt(p.p.rinertia[j]); + domega[j] = sigma_vel * noise[j] / sqrt(p.rinertia()[j]); } } return mask(p.p.rotation, domega); diff --git a/src/core/thermostats/langevin_inline.hpp b/src/core/thermostats/langevin_inline.hpp index 9ec4ca53398..f5c5d9884c1 100644 --- a/src/core/thermostats/langevin_inline.hpp +++ b/src/core/thermostats/langevin_inline.hpp @@ -45,7 +45,7 @@ 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.p.is_virtual && !thermo_virtual) { + if (p.is_virtual() and !thermo_virtual) { return {}; } @@ -54,9 +54,9 @@ friction_thermo_langevin(LangevinThermostat const &langevin, Particle const &p, Thermostat::GammaType pref_noise = langevin.pref_noise; #ifdef THERMOSTAT_PER_PARTICLE // override default if particle-specific gamma - if (p.p.gamma >= Thermostat::GammaType{}) { + if (p.gamma() >= Thermostat::GammaType{}) { auto const gamma = - p.p.gamma >= Thermostat::GammaType{} ? p.p.gamma : langevin.gamma; + p.gamma() >= Thermostat::GammaType{} ? p.gamma() : langevin.gamma; pref_friction = -gamma; pref_noise = LangevinThermostat::sigma(kT, time_step, gamma); } @@ -64,11 +64,11 @@ friction_thermo_langevin(LangevinThermostat const &langevin, Particle const &p, // Get effective velocity in the thermostatting #ifdef ENGINE - auto const &velocity = (p.p.swim.v_swim != 0) - ? p.m.v - p.p.swim.v_swim * p.r.calc_director() - : p.m.v; + auto const &velocity = (p.swimming().v_swim != 0) + ? p.v() - p.swimming().v_swim * p.calc_director() + : p.v(); #else - auto const &velocity = p.m.v; + auto const &velocity = p.v(); #endif // ENGINE #ifdef PARTICLE_ANISOTROPY // Particle frictional isotropy check @@ -90,7 +90,7 @@ friction_thermo_langevin(LangevinThermostat const &langevin, Particle const &p, return friction_op * velocity + noise_op * Random::noise_uniform( - langevin.rng_counter(), langevin.rng_seed(), p.p.identity); + langevin.rng_counter(), langevin.rng_seed(), p.identity()); } #ifdef ROTATION @@ -113,9 +113,9 @@ friction_thermo_langevin_rotation(LangevinThermostat const &langevin, #ifdef THERMOSTAT_PER_PARTICLE // override default if particle-specific gamma - if (p.p.gamma_rot >= Thermostat::GammaType{}) { - auto const gamma = p.p.gamma_rot >= Thermostat::GammaType{} - ? p.p.gamma_rot + if (p.gamma_rot() >= Thermostat::GammaType{}) { + auto const gamma = p.gamma_rot() >= Thermostat::GammaType{} + ? p.gamma_rot() : langevin.gamma_rotation; pref_friction = -gamma; pref_noise = LangevinThermostat::sigma(kT, time_step, gamma); @@ -123,8 +123,8 @@ friction_thermo_langevin_rotation(LangevinThermostat const &langevin, #endif // THERMOSTAT_PER_PARTICLE auto const noise = Random::noise_uniform( - langevin.rng_counter(), langevin.rng_seed(), p.p.identity); - return hadamard_product(pref_friction, p.m.omega) + + langevin.rng_counter(), langevin.rng_seed(), p.identity()); + return hadamard_product(pref_friction, p.omega()) + hadamard_product(pref_noise, noise); } diff --git a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp index 0498412943f..eae1982b545 100644 --- a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp +++ b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp @@ -316,7 +316,7 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, std::unordered_map expected; for (auto pid : pids) { auto p = get_particle_data(pid); - p.v() += 0.5 * time_step * p.force() / p.p.mass; + p.v() += 0.5 * time_step * p.force() / p.mass(); p.pos() += time_step * p.v(); expected[pid] = p.pos(); } diff --git a/src/core/unit_tests/field_coupling_couplings_test.cpp b/src/core/unit_tests/field_coupling_couplings_test.cpp index ed2f6b18d40..a72c8631bbb 100644 --- a/src/core/unit_tests/field_coupling_couplings_test.cpp +++ b/src/core/unit_tests/field_coupling_couplings_test.cpp @@ -37,34 +37,26 @@ BOOST_AUTO_TEST_CASE(charge) { static_assert(Charge::is_linear, ""); struct { - struct { - const double q = 1.23; - } p; + auto q() const { return 1.23; } } p; - BOOST_CHECK((p.p.q * 2.0) == Charge()(p, 2.0)); + BOOST_CHECK((p.q() * 2.0) == Charge()(p, 2.0)); } BOOST_AUTO_TEST_CASE(mass) { static_assert(Mass::is_linear, ""); struct { - struct { - const double mass = 1.23; - bool is_virtual = false; - } p; - + auto mass() const { return 1.23; } + auto is_virtual() const { return false; } } p; struct { - struct { - const double mass = 1.23; - bool is_virtual = true; - } p; - + auto mass() const { return 1.23; } + auto is_virtual() const { return true; } } p_virtual; - BOOST_CHECK((p.p.mass * 2.0) == Mass()(p, 2.0)); + BOOST_CHECK((p.mass() * 2.0) == Mass()(p, 2.0)); BOOST_CHECK((0.0 == Mass()(p_virtual, 2.0))); } @@ -114,13 +106,11 @@ BOOST_AUTO_TEST_CASE(viscous) { { struct { - struct { - const Utils::Vector3d v = {1., 2., 3.}; - } m; + auto v() const { return Utils::Vector3d{{1., 2., 3.}}; } } p; auto const u = Utils::Vector3d{4., 5., 6.}; - BOOST_CHECK((-gamma * (p.m.v - u)) == viscous_coupling(p, u)); + BOOST_CHECK((-gamma * (p.v() - u)) == viscous_coupling(p, u)); } } diff --git a/src/core/unit_tests/thermostats_test.cpp b/src/core/unit_tests/thermostats_test.cpp index 90124f370cc..0176780bb89 100644 --- a/src/core/unit_tests/thermostats_test.cpp +++ b/src/core/unit_tests/thermostats_test.cpp @@ -111,7 +111,7 @@ BOOST_AUTO_TEST_CASE(test_brownian_dynamics) { auto const p = particle_factory(); auto const sigma = sqrt(kT); auto const noise = Random::noise_gaussian(0, 0, 0); - auto const ref = sigma * noise / sqrt(p.p.mass); + auto const ref = sigma * noise / sqrt(p.mass()); auto const out = bd_random_walk_vel(brownian, p); BOOST_CHECK_CLOSE(out[0], ref[0], tol); BOOST_CHECK_CLOSE(out[1], ref[1], tol); diff --git a/src/core/virtual_sites/lb_inertialess_tracers.cpp b/src/core/virtual_sites/lb_inertialess_tracers.cpp index 3a171dc9c75..73fd0d27344 100644 --- a/src/core/virtual_sites/lb_inertialess_tracers.cpp +++ b/src/core/virtual_sites/lb_inertialess_tracers.cpp @@ -65,7 +65,7 @@ void IBM_ForcesIntoFluid_CPU() { // Loop over local cells for (auto &p : cell_structure.local_particles()) { - if (p.p.is_virtual) + if (p.is_virtual()) CoupleIBMParticleToFluid(p); } diff --git a/src/utils/include/utils/checks/charge_neutrality.hpp b/src/utils/include/utils/checks/charge_neutrality.hpp index 2a82a5eee02..ad1ae0204ee 100644 --- a/src/utils/include/utils/checks/charge_neutrality.hpp +++ b/src/utils/include/utils/checks/charge_neutrality.hpp @@ -37,7 +37,7 @@ bool check_charge_neutrality(ParticleRange &prange) { constexpr auto relative_tolerance = 2e-12; for (auto const &p : prange) { - auto const &q = p.p.q; + auto const &q = p.q(); if (q != 0.0) { q_sum(q); From 963cfbac331300954cf5f538a54f21e2ae8ab9d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 28 Feb 2022 21:18:47 +0100 Subject: [PATCH 65/99] core: Rewrite Particle rotation flag Remove the enum class. --- src/core/Particle.hpp | 27 +++++++++---------- src/core/particle_data.cpp | 13 ++++++--- src/core/particle_data.hpp | 11 ++++++-- src/core/unit_tests/thermostats_test.cpp | 10 ++++--- src/python/espressomd/particle_data.pxd | 13 ++------- src/python/espressomd/particle_data.pyx | 34 ++++++++---------------- 6 files changed, 51 insertions(+), 57 deletions(-) diff --git a/src/core/Particle.hpp b/src/core/Particle.hpp index f601ef90688..120cc8a433b 100644 --- a/src/core/Particle.hpp +++ b/src/core/Particle.hpp @@ -44,13 +44,6 @@ inline bool get_nth_bit(uint8_t const bitfield, int const bit_idx) { } } // namespace detail -enum : uint8_t { - ROTATION_FIXED = 0u, - ROTATION_X = 1u, - ROTATION_Y = 2u, - ROTATION_Z = 4u -}; - /** Properties of a self-propelled particle. */ struct ParticleParametersSwimming { /** Is the particle a swimmer. */ @@ -104,11 +97,18 @@ struct ParticleProperties { static constexpr Utils::Vector3d rinertia = {1., 1., 1.}; #endif - /** bitfield for the particle axes of rotation */ #ifdef ROTATION - uint8_t rotation = ROTATION_FIXED; + /** Bitfield for the particle axes of rotation. + * Values: + * - 1: allow rotation around the x axis + * - 2: allow rotation around the y axis + * - 4: allow rotation around the z axis + * By default, the particle cannot rotate. + */ + uint8_t rotation = static_cast(0b000u); #else - static constexpr uint8_t rotation = ROTATION_FIXED; + /** Bitfield for the particle axes of rotation. Particle cannot rotate. */ + static constexpr uint8_t rotation = static_cast(0b000u); #endif /** charge. */ @@ -481,10 +481,9 @@ struct Particle { // NOLINT(bugprone-exception-escape) p.rotation &= static_cast(~(1u << axis)); } } - void set_can_rotate_all_axes() { - for (int axis = 0; axis <= 2; axis++) { - set_can_rotate_around(axis, true); - } + void set_can_rotate_all_axes() { p.rotation = static_cast(0b111u); } + void set_cannot_rotate_all_axes() { + p.rotation = static_cast(0b000u); } auto const &quat() const { return r.quat; } auto &quat() { return r.quat; } diff --git a/src/core/particle_data.cpp b/src/core/particle_data.cpp index dcdf61cf22b..7653bf8c07b 100644 --- a/src/core/particle_data.cpp +++ b/src/core/particle_data.cpp @@ -780,9 +780,16 @@ constexpr Utils::Vector3d ParticleProperties::rinertia; #endif #ifdef ROTATION -void set_particle_rotation(int part, int rot) { - mpi_update_particle_property(part, - rot); +void set_particle_rotation(int part, Utils::Vector3i const &flag) { + auto rot_flag = static_cast(0u); + if (flag[0]) + rot_flag |= static_cast(1u); + if (flag[1]) + rot_flag |= static_cast(2u); + if (flag[2]) + rot_flag |= static_cast(4u); + mpi_update_particle_property( + part, rot_flag); } void rotate_particle(int part, const Utils::Vector3d &axis, double angle) { diff --git a/src/core/particle_data.hpp b/src/core/particle_data.hpp index 90640413fb6..7a875746949 100644 --- a/src/core/particle_data.hpp +++ b/src/core/particle_data.hpp @@ -154,9 +154,9 @@ void set_particle_rotational_inertia(int part, Utils::Vector3d const &rinertia); * degrees of freedom are integrated or not. If set to zero, the content of * the torque and omega variables are meaningless * @param part the particle. - * @param rot the degrees of freedom flag. + * @param flag the degrees of freedom flag. */ -void set_particle_rotation(int part, int rot); +void set_particle_rotation(int part, Utils::Vector3i const &flag); /** @brief rotate a particle around an axis * @@ -464,6 +464,13 @@ inline Utils::Vector3d get_particle_rotational_inertia(Particle const *p) { } #endif +#ifdef ROTATION +inline Utils::Vector3i get_particle_rotation(Particle const *p) { + return Utils::Vector3i{{p->can_rotate_around(0), p->can_rotate_around(1), + p->can_rotate_around(2)}}; +} +#endif + /** * @brief Check if particle exists. * diff --git a/src/core/unit_tests/thermostats_test.cpp b/src/core/unit_tests/thermostats_test.cpp index 0176780bb89..d9495fe5187 100644 --- a/src/core/unit_tests/thermostats_test.cpp +++ b/src/core/unit_tests/thermostats_test.cpp @@ -125,7 +125,8 @@ BOOST_AUTO_TEST_CASE(test_brownian_dynamics) { /* check rotation */ { auto p = particle_factory(); - p.p.rotation = ROTATION_X; + p.set_cannot_rotate_all_axes(); + p.set_can_rotate_around(0, true); auto const phi = time_step * dispersion_rotation[0]; auto const out = bd_drag_rot(brownian.gamma_rotation, p, time_step); BOOST_CHECK_CLOSE(out[0], std::cos(phi / 2), tol); @@ -137,7 +138,7 @@ BOOST_AUTO_TEST_CASE(test_brownian_dynamics) { /* check rotational velocity */ { auto p = particle_factory(); - p.p.rotation = ROTATION_X | ROTATION_Y | ROTATION_Z; + p.set_can_rotate_all_axes(); auto const ref = dispersion_rotation; auto const out = bd_drag_vel_rot(brownian.gamma_rotation, p); BOOST_CHECK_CLOSE(out[0], ref[0], tol); @@ -148,7 +149,8 @@ BOOST_AUTO_TEST_CASE(test_brownian_dynamics) { /* check walk rotation */ { auto p = particle_factory(); - p.p.rotation = ROTATION_X; + p.set_cannot_rotate_all_axes(); + p.set_can_rotate_around(0, true); auto const sigma = sqrt(brownian.gamma_rotation / (2.0 * kT)); auto const noise = Random::noise_gaussian(0, 0, 0); @@ -163,7 +165,7 @@ BOOST_AUTO_TEST_CASE(test_brownian_dynamics) { /* check walk rotational velocity */ { auto p = particle_factory(); - p.p.rotation = ROTATION_X | ROTATION_Y | ROTATION_Z; + p.set_can_rotate_all_axes(); auto const sigma = sqrt(kT); auto const noise = Random::noise_gaussian(0, 0, 0); diff --git a/src/python/espressomd/particle_data.pxd b/src/python/espressomd/particle_data.pxd index 268f09b314b..ea837c48f90 100644 --- a/src/python/espressomd/particle_data.pxd +++ b/src/python/espressomd/particle_data.pxd @@ -20,7 +20,6 @@ from .utils cimport Vector4d, Vector3d, Vector3i, Span, Quaternion from libcpp cimport bool from libcpp.vector cimport vector # import std::vector as vector -from libc cimport stdint include "myconfig.pxi" @@ -32,11 +31,6 @@ cdef extern from "particle_data.hpp": int bond_id() Span[const int] partner_ids() - # DATA STRUCTURES - stdint.uint8_t ROTATION_X - stdint.uint8_t ROTATION_Y - stdint.uint8_t ROTATION_Z - # Note: Conditional compilation is not possible within ctypedef blocks. # Therefore, only member variables are imported here, which are always compiled into ESPResSo. # For all other properties, getter-functions have to be used on the c @@ -46,7 +40,6 @@ cdef extern from "particle_data.hpp": int mol_id int type double mass - stdint.uint8_t rotation ctypedef struct particle_position "ParticlePosition": Vector3d p @@ -90,9 +83,6 @@ cdef extern from "particle_data.hpp": void set_particle_f(int part, const Vector3d & f) void set_particle_lees_edwards_offset(int, const double) - IF ROTATION: - void set_particle_rotation(int part, int rot) - IF MASS: void set_particle_mass(int part, double mass) @@ -101,7 +91,8 @@ cdef extern from "particle_data.hpp": Vector3d get_particle_rotational_inertia(const particle * p) IF ROTATION: - void set_particle_rotation(int part, int rot) + void set_particle_rotation(int part, const Vector3i & flag) + Vector3i get_particle_rotation(const particle * p) void set_particle_q(int part, double q) diff --git a/src/python/espressomd/particle_data.pyx b/src/python/espressomd/particle_data.pyx index 302703ce6b1..e433ea617b3 100644 --- a/src/python/espressomd/particle_data.pyx +++ b/src/python/espressomd/particle_data.pyx @@ -966,39 +966,27 @@ cdef class ParticleHandle: The default is not to integrate any rotational degrees of freedom. - rotation : (3,) array_like of :obj:`int` + rotation : (3,) array_like of :obj:`bool` .. note:: This needs the feature ``ROTATION``. """ - def __set__(self, _rot): - cdef int rot + def __set__(self, flag): + cdef Vector3i rot_flag check_type_or_throw_except( - _rot, 3, int, "The rotation flag has to be given as a tuple of 3 integers.") - - rot = 0 - if _rot[0]: - rot += ROTATION_X - if _rot[1]: - rot += ROTATION_Y - if _rot[2]: - rot += ROTATION_Z - set_particle_rotation(self._id, rot) + flag, 3, int, "Property 'rotation' has to be 3 bools.") + rot_flag[0] = int(flag[0]) + rot_flag[1] = int(flag[1]) + rot_flag[2] = int(flag[2]) + set_particle_rotation(self._id, rot_flag) def __get__(self): self.update_particle_data() - rot = self.particle_data.p.rotation - - res = np.zeros(3, dtype=int) - if rot & ROTATION_X: - res[0] = 1 - if rot & ROTATION_Y: - res[1] = 1 - if rot & ROTATION_Z: - res[2] = 1 - return array_locked(res) + cdef Vector3i flag = get_particle_rotation(self.particle_data) + rot_flag = np.array([flag[0], flag[1], flag[2]], dtype=int) + return array_locked(rot_flag) IF EXCLUSIONS: property exclusions: From 7cba1d17aeda643ab0bd0b7d79aa5156b93cee1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 1 Mar 2022 15:45:16 +0100 Subject: [PATCH 66/99] core: Modernize VirtualSitesRelative Use references instead of raw pointers. Remove unused functions. --- .../virtual_sites/VirtualSitesRelative.cpp | 82 ++++++------------- 1 file changed, 26 insertions(+), 56 deletions(-) diff --git a/src/core/virtual_sites/VirtualSitesRelative.cpp b/src/core/virtual_sites/VirtualSitesRelative.cpp index b5477ce46d5..741b2cbfc77 100644 --- a/src/core/virtual_sites/VirtualSitesRelative.cpp +++ b/src/core/virtual_sites/VirtualSitesRelative.cpp @@ -37,27 +37,13 @@ namespace { /** - * @brief Orientation of the virtual site. - * @param p_ref Reference particle for the virtual site. - * @param vs_rel Parameters for the virtual site. - * @return Orientation quaternion of the virtual site. - */ -Utils::Quaternion -orientation(const Particle &p_ref, - const ParticleProperties::VirtualSitesRelativeParameters &vs_rel) { - return p_ref.quat() * vs_rel.quat; -} - -/** - * @brief Vector pointing from the real particle - * to the virtual site. + * @brief Vector pointing from the real particle to the virtual site. * * @return Relative distance. */ -inline Utils::Vector3d connection_vector( - +auto connection_vector( Particle const &p_ref, - const ParticleProperties::VirtualSitesRelativeParameters &vs_rel) { + ParticleProperties::VirtualSitesRelativeParameters const &vs_rel) { // Calculate the quaternion defining the orientation of the vector connecting // the virtual site and the real particle // This is obtained, by multiplying the quaternion representing the director @@ -70,18 +56,6 @@ inline Utils::Vector3d connection_vector( return vs_rel.distance * director; } -/** - * @brief Position of the virtual site - * @param p_ref Reference particle for the virtual site. - * @param vs_rel Parameters for the virtual site. - * @return Position of the virtual site. - */ -Utils::Vector3d -position(Particle const &p_ref, - const ParticleProperties::VirtualSitesRelativeParameters &vs_rel) { - return p_ref.pos() + connection_vector(p_ref, vs_rel); -} - /** * @brief Velocity of the virtual site * @param p_ref Reference particle for the virtual site. @@ -89,8 +63,8 @@ position(Particle const &p_ref, * @return Velocity of the virtual site. */ Utils::Vector3d -velocity(const Particle &p_ref, - const ParticleProperties::VirtualSitesRelativeParameters &vs_rel) { +velocity(Particle const &p_ref, + ParticleProperties::VirtualSitesRelativeParameters const &vs_rel) { auto const d = connection_vector(p_ref, vs_rel); // Get omega of real particle in space-fixed frame @@ -107,14 +81,13 @@ velocity(const Particle &p_ref, * @param vs_rel Parameters to get the reference particle for. * @return Pointer to reference particle. */ -Particle *get_reference_particle( - const ParticleProperties::VirtualSitesRelativeParameters &vs_rel) { - auto p_ref = cell_structure.get_local_particle(vs_rel.to_particle_id); - if (!p_ref) { +Particle &get_reference_particle( + ParticleProperties::VirtualSitesRelativeParameters const &vs_rel) { + auto p_ref_ptr = cell_structure.get_local_particle(vs_rel.to_particle_id); + if (!p_ref_ptr) { throw std::runtime_error("No real particle associated with virtual site."); } - - return p_ref; + return *p_ref_ptr; } /** @@ -144,9 +117,9 @@ void VirtualSitesRelative::update() const { if (!p.is_virtual()) continue; - const Particle *p_ref = get_reference_particle(p.vs_relative()); + auto const &p_ref = get_reference_particle(p.vs_relative()); - auto new_pos = position(*p_ref, p.vs_relative()); + auto new_pos = p_ref.pos() + connection_vector(p_ref, p.vs_relative()); /* The shift has to respect periodic boundaries: if the reference * particles is not in the same image box, we potentially avoid shifting * to the other side of the box. */ @@ -154,9 +127,9 @@ void VirtualSitesRelative::update() const { p.pos() += shift; Utils::Vector3i image_shift{}; fold_position(shift, image_shift, box_geo); - p.image_box() = p_ref->image_box() - image_shift; + p.image_box() = p_ref.image_box() - image_shift; - p.v() = velocity(*p_ref, p.vs_relative()); + p.v() = velocity(p_ref, p.vs_relative()); if (box_geo.type() == BoxType::LEES_EDWARDS) { auto const &shear_dir = box_geo.clees_edwards_bc().shear_direction; @@ -168,7 +141,7 @@ void VirtualSitesRelative::update() const { } if (have_quaternions()) - p.quat() = p_ref->quat() * p.vs_relative().quat; + p.quat() = p_ref.quat() * p.vs_relative().quat; } if (cell_structure.check_resort_required(particles, skin)) { @@ -186,17 +159,15 @@ void VirtualSitesRelative::back_transfer_forces_and_torques() const { // Iterate over all the particles in the local cells for (auto &p : cell_structure.local_particles()) { // We only care about virtual particles - if (p.is_virtual()) { - // First obtain the real particle responsible for this virtual particle: - Particle *p_ref = get_reference_particle(p.vs_relative()); - - // Add forces and torques - p_ref->force() += p.force(); - p_ref->torque() += - vector_product(connection_vector(*p_ref, p.vs_relative()), - p.force()) + - p.torque(); - } + if (!p.is_virtual()) + continue; + auto &p_ref = get_reference_particle(p.vs_relative()); + + // Add forces and torques + p_ref.force() += p.force(); + p_ref.torque() += + vector_product(connection_vector(p_ref, p.vs_relative()), p.force()) + + p.torque(); } } @@ -208,10 +179,9 @@ Utils::Matrix VirtualSitesRelative::pressure_tensor() const { if (!p.is_virtual()) continue; - // First obtain the real particle responsible for this virtual particle: - const Particle *p_ref = get_reference_particle(p.vs_relative()); + auto const &p_ref = get_reference_particle(p.vs_relative()); - pressure_tensor += constraint_stress(p.force(), *p_ref, p.vs_relative()); + pressure_tensor += constraint_stress(p.force(), p_ref, p.vs_relative()); } return pressure_tensor; From bc8b2d1474ef40797004d2090c8c586cdc4793d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 1 Mar 2022 16:08:43 +0100 Subject: [PATCH 67/99] core: Cleanup header files --- src/core/MpiCallbacks.hpp | 1 + src/core/Particle.hpp | 1 - src/core/bond_breakage.cpp | 3 +++ src/core/lees_edwards_protocol.hpp | 2 ++ src/core/particle_data.cpp | 1 - src/core/unit_tests/lees_edwards_test.cpp | 1 + src/core/virtual_sites/VirtualSitesInertialessTracers.cpp | 2 ++ 7 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/core/MpiCallbacks.hpp b/src/core/MpiCallbacks.hpp index 21d7fd03269..477bd7ae915 100644 --- a/src/core/MpiCallbacks.hpp +++ b/src/core/MpiCallbacks.hpp @@ -46,6 +46,7 @@ #include #include +#include #include #include #include diff --git a/src/core/Particle.hpp b/src/core/Particle.hpp index 120cc8a433b..d67911c0e7f 100644 --- a/src/core/Particle.hpp +++ b/src/core/Particle.hpp @@ -29,7 +29,6 @@ #include -#include #include #include #include diff --git a/src/core/bond_breakage.cpp b/src/core/bond_breakage.cpp index 9549fa15417..bd24651fc08 100644 --- a/src/core/bond_breakage.cpp +++ b/src/core/bond_breakage.cpp @@ -25,6 +25,9 @@ #include #include +#include +#include +#include #include #include diff --git a/src/core/lees_edwards_protocol.hpp b/src/core/lees_edwards_protocol.hpp index ec88668afd5..b0cacac5bf2 100644 --- a/src/core/lees_edwards_protocol.hpp +++ b/src/core/lees_edwards_protocol.hpp @@ -23,6 +23,8 @@ #include +#include + namespace LeesEdwards { // Protocols determining shear rate and positional offset as a function of time diff --git a/src/core/particle_data.cpp b/src/core/particle_data.cpp index 7653bf8c07b..6d9ee298f04 100644 --- a/src/core/particle_data.cpp +++ b/src/core/particle_data.cpp @@ -34,7 +34,6 @@ #include "partCfg_global.hpp" #include "rotation.hpp" -#include #include #include #include diff --git a/src/core/unit_tests/lees_edwards_test.cpp b/src/core/unit_tests/lees_edwards_test.cpp index 3d31ea2cff9..4161f422279 100644 --- a/src/core/unit_tests/lees_edwards_test.cpp +++ b/src/core/unit_tests/lees_edwards_test.cpp @@ -31,6 +31,7 @@ #include #include +#include #include using namespace LeesEdwards; diff --git a/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp b/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp index a19dfae3e14..8bbb1c2a77c 100644 --- a/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp +++ b/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp @@ -19,7 +19,9 @@ #include "config.hpp" #ifdef VIRTUAL_SITES_INERTIALESS_TRACERS + #include "VirtualSitesInertialessTracers.hpp" + #include "cells.hpp" #include "communication.hpp" #include "errorhandling.hpp" From b94514c7482ca23dd49d2aebc1e1129fdbcd6c5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 1 Mar 2022 16:13:13 +0100 Subject: [PATCH 68/99] core: Cleanup comments --- src/core/cells.hpp | 5 +- src/core/event.hpp | 4 +- src/core/field_coupling/fields/PlaneWave.hpp | 2 +- src/core/forces.cpp | 4 +- .../grid_based_algorithms/lb_interface.hpp | 2 +- src/core/integrate.cpp | 21 +++---- src/core/io/writer/h5md_core.hpp | 5 +- .../VerletCriterion.hpp | 2 +- src/core/particle_data.cpp | 58 +++++++++---------- .../tests/particle_tracking_test.cpp | 2 +- src/core/virtual_sites.cpp | 4 +- .../virtual_sites/VirtualSitesRelative.cpp | 2 +- src/script_interface/h5md/h5md.hpp | 4 +- 13 files changed, 53 insertions(+), 62 deletions(-) diff --git a/src/core/cells.hpp b/src/core/cells.hpp index 7073069d010..52b708c3a6a 100644 --- a/src/core/cells.hpp +++ b/src/core/cells.hpp @@ -143,8 +143,9 @@ class PairInfo { int node; }; -/** @brief Returns pairs of particle ids, positions and distance as seen by the - * non-bonded loop. +/** + * @brief Returns pairs of particle ids, positions and distance as seen by the + * non-bonded loop. */ std::vector mpi_non_bonded_loop_trace(); diff --git a/src/core/event.hpp b/src/core/event.hpp index 477e9739518..1cf2fae0b0c 100644 --- a/src/core/event.hpp +++ b/src/core/event.hpp @@ -105,8 +105,8 @@ void on_skin_change(); */ void on_thermostat_param_change(); -/** @brief Called when the timestep changed. Internally calls @ref - * on_thermostat_param_change. +/** @brief Called when the timestep changed. + * Internally calls @ref on_thermostat_param_change. */ void on_timestep_change(); diff --git a/src/core/field_coupling/fields/PlaneWave.hpp b/src/core/field_coupling/fields/PlaneWave.hpp index 3be877e0305..1dd8a66b4b4 100644 --- a/src/core/field_coupling/fields/PlaneWave.hpp +++ b/src/core/field_coupling/fields/PlaneWave.hpp @@ -32,7 +32,7 @@ namespace Fields { /** * @brief A plane wave. * - * A time dependent plane wave, with a certain (vector-valued) + * A time-dependent plane wave, with a certain (vector-valued) * amplitude, wave vector frequency and phase. * * See https://en.wikipedia.org/wiki/Plane_wave diff --git a/src/core/forces.cpp b/src/core/forces.cpp index 9b7623e4132..1f0938b5e1b 100644 --- a/src/core/forces.cpp +++ b/src/core/forces.cpp @@ -238,12 +238,12 @@ void calc_long_range_forces(const ParticleRange &particles) { /* calculate k-space part of electrostatic interaction. */ Coulomb::calc_long_range_force(particles); -#endif /*ifdef ELECTROSTATICS */ +#endif // ELECTROSTATICS #ifdef DIPOLES /* calculate k-space part of the magnetostatic interaction. */ Dipole::calc_long_range_force(particles); -#endif /*ifdef DIPOLES */ +#endif // DIPOLES } #ifdef NPT diff --git a/src/core/grid_based_algorithms/lb_interface.hpp b/src/core/grid_based_algorithms/lb_interface.hpp index cbb6eb3c2ce..3d1d23f0aab 100644 --- a/src/core/grid_based_algorithms/lb_interface.hpp +++ b/src/core/grid_based_algorithms/lb_interface.hpp @@ -211,7 +211,7 @@ const Utils::Vector6d lb_lbnode_get_pressure_tensor_neq(const Utils::Vector3i &ind); /** @brief Calculate the average pressure tensor of all nodes by accumulating - * over all nodes and dividing by the @ref LB_parameters_gpu::number_of_nodes. + * over all nodes and dividing by the number of nodes. * Returns the lower triangle of the LB pressure tensor. */ const Utils::Vector6d lb_lbfluid_get_pressure_tensor(); diff --git a/src/core/integrate.cpp b/src/core/integrate.cpp index c09beaf21f6..35a94076f74 100644 --- a/src/core/integrate.cpp +++ b/src/core/integrate.cpp @@ -252,18 +252,14 @@ void integrator_step_2(ParticleRange &particles, double kT) { int integrate(int n_steps, int reuse_forces) { ESPRESSO_PROFILER_CXX_MARK_FUNCTION; - /* Prepare the integrator */ + // Prepare particle structure and run sanity checks of all active algorithms on_integration_start(time_step); - /* if any method vetoes (e.g. P3M not initialized), immediately bail out */ + // If any method vetoes (e.g. P3M not initialized), immediately bail out if (check_runtime_errors(comm_cart)) return 0; - /* Verlet list criterion */ - - /* Integration Step: Preparation for first integration step: - * Calculate forces F(t) as function of positions x(t) (and velocities v(t)) - */ + // Additional preparations for the first integration step if (reuse_forces == -1 || (recalc_forces && reuse_forces != 1)) { ESPRESSO_PROFILER_MARK_BEGIN("Initial Force Calculation"); lb_lbcoupling_deactivate(); @@ -291,13 +287,13 @@ int integrate(int n_steps, int reuse_forces) { if (check_runtime_errors(comm_cart)) return 0; - /* incremented if a Verlet update is done, aka particle resorting. */ + // Keep track of the number of Verlet updates (i.e. particle resorts) int n_verlet_updates = 0; #ifdef VALGRIND_INSTRUMENTATION CALLGRIND_START_INSTRUMENTATION; #endif - /* Integration loop */ + // Integration loop ESPRESSO_PROFILER_CXX_MARK_LOOP_BEGIN(integration_loop, "Integration loop"); int integrated_steps = 0; for (int step = 0; step < n_steps; step++) { @@ -324,12 +320,11 @@ int integrate(int n_steps, int reuse_forces) { resort_particles_if_needed(particles); } - /* Propagate philox rng counters */ + // Propagate philox RNG counters philox_counter_increment(); #ifdef BOND_CONSTRAINT - /* Correct those particle positions that participate in a rigid/constrained - * bond */ + // Correct particle positions that participate in a rigid/constrained bond if (n_rigidbonds) { correct_position_shake(cell_structure); } @@ -408,7 +403,7 @@ int integrate(int n_steps, int reuse_forces) { virtual_sites()->update(); #endif - /* verlet list statistics */ + // Verlet list statistics if (n_verlet_updates > 0) verlet_reuse = n_steps / (double)n_verlet_updates; else diff --git a/src/core/io/writer/h5md_core.hpp b/src/core/io/writer/h5md_core.hpp index c912019fbb7..30f4e4b15b4 100644 --- a/src/core/io/writer/h5md_core.hpp +++ b/src/core/io/writer/h5md_core.hpp @@ -89,8 +89,7 @@ class File { * @param particles Particle range for which to write data. * @param time Simulation time. * @param step Simulation step (monotonically increasing). - * @param geometry A BoxGeometry instance that carries the information of the - * box dimensions. + * @param geometry The box dimensions. */ void write(const ParticleRange &particles, double time, int step, BoxGeometry const &geometry); @@ -192,7 +191,7 @@ class File { */ void write_units(); /** - * @brief Create hard links for the time and step entries of time dependent + * @brief Create hard links for the time and step entries of time-dependent * datasets. */ void create_hard_links(); diff --git a/src/core/nonbonded_interactions/VerletCriterion.hpp b/src/core/nonbonded_interactions/VerletCriterion.hpp index c64ee87e90a..d1d6c65ec14 100644 --- a/src/core/nonbonded_interactions/VerletCriterion.hpp +++ b/src/core/nonbonded_interactions/VerletCriterion.hpp @@ -67,7 +67,7 @@ template class VerletCriterion { return false; #ifdef ELECTROSTATICS - // Within real space cutoff of electrostatics and both charged + // Within real space cutoff of electrostatics and both are charged if (dist2 <= m_eff_coulomb_cut2 and p1.q() != 0. and p2.q() != 0.) return true; #endif diff --git a/src/core/particle_data.cpp b/src/core/particle_data.cpp index 6d9ee298f04..ff011db2825 100644 --- a/src/core/particle_data.cpp +++ b/src/core/particle_data.cpp @@ -183,6 +183,7 @@ using UpdateForceMessage = boost::variant , UpdateForce #endif >; +// clang-format on /** * @brief Delete specific bond. @@ -192,39 +193,38 @@ struct RemoveBond { void operator()(Particle &p) const { assert(not bond.empty()); - auto const view = BondView(bond.front(), {bond.data() + 1, bond.size() - 1}); + auto const view = + BondView(bond.front(), {bond.data() + 1, bond.size() - 1}); auto it = boost::find(p.bonds(), view); if (it != p.bonds().end()) { - p.bonds().erase(it); + p.bonds().erase(it); } } - template - void serialize(Archive &ar, long int) { ar & bond; } + template void serialize(Archive &ar, long int) { ar &bond; } }; /** * @brief Delete pair bonds to a specific partner */ struct RemovePairBondsTo { - int other_pid; - - void operator()(Particle &p) const { - using Bond = std::vector; - std::vector to_delete; - for (auto b: p.bonds()) { - if (b.partner_ids().size() == 1 and b.partner_ids()[0] == other_pid) - to_delete.push_back(Bond{b.bond_id(),other_pid}); - } - for (auto b: to_delete) { - RemoveBond{b}(p); - } + int other_pid; + + void operator()(Particle &p) const { + using Bond = std::vector; + std::vector to_delete; + for (auto b : p.bonds()) { + if (b.partner_ids().size() == 1 and b.partner_ids()[0] == other_pid) + to_delete.push_back(Bond{b.bond_id(), other_pid}); } - template - void serialize(Archive &ar, long int) { - ar & other_pid; + for (auto b : to_delete) { + RemoveBond{b}(p); } + } + template void serialize(Archive &ar, long int) { + ar &other_pid; + } }; /** @@ -233,8 +233,7 @@ struct RemovePairBondsTo { struct RemoveBonds { void operator()(Particle &p) const { p.bonds().clear(); } - template - void serialize(Archive &, long int) {} + template void serialize(Archive &, long int) {} }; struct AddBond { @@ -246,34 +245,31 @@ struct AddBond { p.bonds().insert(view); } - template - void serialize(Archive &ar, long int) { - ar & bond; - } + template void serialize(Archive &ar, long int) { ar &bond; } }; +// clang-format off using UpdateBondMessage = boost::variant < RemoveBond , RemoveBonds , AddBond >; +// clang-format on #ifdef ROTATION struct UpdateOrientation { Utils::Vector3d axis; double angle; - void operator()(Particle &p) const { - local_rotate_particle(p, axis, angle); - } + void operator()(Particle &p) const { local_rotate_particle(p, axis, angle); } - template - void serialize(Archive &ar, long int) { - ar & axis & angle; + template void serialize(Archive &ar, long int) { + ar &axis ∠ } }; #endif +// clang-format off /** * @brief Top-level message. * diff --git a/src/core/reaction_methods/tests/particle_tracking_test.cpp b/src/core/reaction_methods/tests/particle_tracking_test.cpp index fdf80b802eb..df14f1e5f32 100644 --- a/src/core/reaction_methods/tests/particle_tracking_test.cpp +++ b/src/core/reaction_methods/tests/particle_tracking_test.cpp @@ -42,7 +42,7 @@ BOOST_FIXTURE_TEST_CASE(particle_type_map_test, ParticleFactory) { int const type = 10; int const pid = 1; - // exception for untracked particle ids + // exception for untracked particle types BOOST_CHECK_THROW(number_of_particles_with_type(type), std::runtime_error); // exception for negative particle ids diff --git a/src/core/virtual_sites.cpp b/src/core/virtual_sites.cpp index 66325a2c19c..e591277f0a2 100644 --- a/src/core/virtual_sites.cpp +++ b/src/core/virtual_sites.cpp @@ -151,5 +151,5 @@ void vs_relate_to(int part_num, int relate_to) { set_particle_virtual(part_num, true); } -#endif -#endif +#endif // VIRTUAL_SITES_RELATIVE +#endif // VIRTUAL_SITES diff --git a/src/core/virtual_sites/VirtualSitesRelative.cpp b/src/core/virtual_sites/VirtualSitesRelative.cpp index 741b2cbfc77..c88fb2a33fd 100644 --- a/src/core/virtual_sites/VirtualSitesRelative.cpp +++ b/src/core/virtual_sites/VirtualSitesRelative.cpp @@ -186,4 +186,4 @@ Utils::Matrix VirtualSitesRelative::pressure_tensor() const { return pressure_tensor; } -#endif +#endif // VIRTUAL_SITES_RELATIVE diff --git a/src/script_interface/h5md/h5md.hpp b/src/script_interface/h5md/h5md.hpp index b506e74d8cb..407b06530cc 100644 --- a/src/script_interface/h5md/h5md.hpp +++ b/src/script_interface/h5md/h5md.hpp @@ -63,8 +63,8 @@ class H5md : public AutoParameters { std::shared_ptr<::Writer::H5md::File> m_h5md; }; -} /* namespace Writer */ +} // namespace Writer } // namespace ScriptInterface -#endif // ESPRESSO_H5MD_HPP #endif // H5MD +#endif From 8cf3ce905534e4d82eddf291f8040640cfac7f7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 1 Mar 2022 16:33:52 +0100 Subject: [PATCH 69/99] core: Minor fixes Make private functions static and function arguments const whenever possible. Simplify code with utility functions. --- src/core/BoxGeometry.hpp | 4 ++-- src/core/field_coupling/fields/Interpolated.hpp | 4 +--- src/core/integrate.cpp | 6 +++--- src/core/observables/LBFluidPressureTensor.hpp | 5 +++-- src/python/espressomd/cellsystem.pxd | 3 +-- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/core/BoxGeometry.hpp b/src/core/BoxGeometry.hpp index 1a84330de2f..8a830240e8d 100644 --- a/src/core/BoxGeometry.hpp +++ b/src/core/BoxGeometry.hpp @@ -95,9 +95,9 @@ class BoxGeometry { /** Flags for all three dimensions whether pbc are applied (default). */ std::bitset<3> m_periodic = 0b111; /** Side lengths of the box */ - Utils::Vector3d m_length = {1, 1, 1}; + Utils::Vector3d m_length = {1., 1., 1.}; /** Inverse side lengths of the box */ - Utils::Vector3d m_length_inv = {1, 1, 1}; + Utils::Vector3d m_length_inv = {1., 1., 1.}; /** Half side lengths of the box */ Utils::Vector3d m_length_half = {0.5, 0.5, 0.5}; diff --git a/src/core/field_coupling/fields/Interpolated.hpp b/src/core/field_coupling/fields/Interpolated.hpp index 1c484e8fc87..ae9d3366027 100644 --- a/src/core/field_coupling/fields/Interpolated.hpp +++ b/src/core/field_coupling/fields/Interpolated.hpp @@ -136,9 +136,7 @@ template class Interpolated { bool fits_in_box(const Utils::Vector3d &box) const { auto const box_shape = shape(); - const Utils::Vector3d grid_size = {m_grid_spacing[0] * box_shape[0], - m_grid_spacing[1] * box_shape[1], - m_grid_spacing[2] * box_shape[2]}; + auto const grid_size = Utils::hadamard_product(m_grid_spacing, box_shape); return (m_origin < Utils::Vector3d::broadcast(0.)) && ((m_origin + grid_size) >= box); } diff --git a/src/core/integrate.cpp b/src/core/integrate.cpp index 35a94076f74..6e094988457 100644 --- a/src/core/integrate.cpp +++ b/src/core/integrate.cpp @@ -180,7 +180,7 @@ void integrator_sanity_checks() { } } -static void resort_particles_if_needed(ParticleRange &particles) { +static void resort_particles_if_needed(ParticleRange const &particles) { auto const offset = LeesEdwards::verlet_list_offset( box_geo, cell_structure.get_le_pos_offset_at_last_resort()); if (cell_structure.check_resort_required(particles, skin, offset)) { @@ -191,7 +191,7 @@ static void resort_particles_if_needed(ParticleRange &particles) { /** @brief Calls the hook for propagation kernels before the force calculation * @return whether or not to stop the integration loop early. */ -bool integrator_step_1(ParticleRange &particles) { +static bool integrator_step_1(ParticleRange const &particles) { bool early_exit = false; switch (integ_switch) { case INTEG_METHOD_STEEPEST_DESCENT: @@ -221,7 +221,7 @@ bool integrator_step_1(ParticleRange &particles) { } /** Calls the hook of the propagation kernels after force calculation */ -void integrator_step_2(ParticleRange &particles, double kT) { +static void integrator_step_2(ParticleRange const &particles, double kT) { switch (integ_switch) { case INTEG_METHOD_STEEPEST_DESCENT: // Nothing diff --git a/src/core/observables/LBFluidPressureTensor.hpp b/src/core/observables/LBFluidPressureTensor.hpp index c6759843ef6..e8dd7d7e24e 100644 --- a/src/core/observables/LBFluidPressureTensor.hpp +++ b/src/core/observables/LBFluidPressureTensor.hpp @@ -22,6 +22,8 @@ #include "Observable.hpp" #include "grid_based_algorithms/lb_interface.hpp" +#include + #include #include @@ -30,9 +32,8 @@ class LBFluidPressureTensor : public Observable { public: std::vector shape() const override { return {3, 3}; } std::vector operator()() const override { - auto const unit_conversion = - 1. / (lb_lbfluid_get_agrid() * pow(lb_lbfluid_get_tau(), 2)); + 1. / (lb_lbfluid_get_agrid() * Utils::sqr(lb_lbfluid_get_tau())); auto const lower_triangle = lb_lbfluid_get_pressure_tensor() * unit_conversion; return {lower_triangle[0], lower_triangle[1], lower_triangle[3], diff --git a/src/python/espressomd/cellsystem.pxd b/src/python/espressomd/cellsystem.pxd index 48092b5e1fe..c734e55d1dd 100644 --- a/src/python/espressomd/cellsystem.pxd +++ b/src/python/espressomd/cellsystem.pxd @@ -31,7 +31,6 @@ cdef extern from "cells.hpp": Vector3d vec21 int node - cdef extern from "communication.hpp": int n_nodes @@ -51,7 +50,7 @@ cdef extern from "cells.hpp": vector[pair[int, int]] mpi_get_pairs(double distance) except + vector[pair[int, int]] mpi_get_pairs_of_types(double distance, vector[int] types) except + - vector[PairInfo] mpi_non_bonded_loop_trace() + vector[PairInfo] mpi_non_bonded_loop_trace() vector[int] mpi_resort_particles(int global_flag) void mpi_bcast_cell_structure(int cs) void mpi_set_use_verlet_lists(bool use_verlet_lists) From 6203cfdd62390b3868bafb7a69d8dae25c06624a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 2 Mar 2022 12:35:04 +0100 Subject: [PATCH 70/99] core: Move files to submodules --- src/core/BoxGeometry.hpp | 2 +- src/core/CMakeLists.txt | 2 +- src/core/CellStructure.cpp | 2 +- src/core/bond_breakage/CMakeLists.txt | 2 + src/core/bond_breakage/actions.hpp | 78 +++++++++++++++++++ .../{ => bond_breakage}/bond_breakage.cpp | 63 +++------------ .../{ => bond_breakage}/bond_breakage.hpp | 8 +- src/core/forces.cpp | 2 +- src/core/forces_inline.hpp | 2 +- src/core/integrate.cpp | 4 +- src/core/{ => lees_edwards}/LeesEdwardsBC.hpp | 4 +- src/core/{ => lees_edwards}/lees_edwards.hpp | 6 +- .../protocols.hpp} | 4 +- src/core/unit_tests/lees_edwards_test.cpp | 4 +- .../bond_breakage/BreakageSpec.hpp | 2 +- .../bond_breakage/BreakageSpecs.hpp | 6 +- .../lees_edwards/LeesEdwards.hpp | 2 +- .../lees_edwards/LinearShear.hpp | 2 +- src/script_interface/lees_edwards/Off.hpp | 2 +- .../lees_edwards/OscillatoryShear.hpp | 2 +- .../lees_edwards/Protocol.hpp | 2 +- 21 files changed, 121 insertions(+), 80 deletions(-) create mode 100644 src/core/bond_breakage/CMakeLists.txt create mode 100644 src/core/bond_breakage/actions.hpp rename src/core/{ => bond_breakage}/bond_breakage.cpp (78%) rename src/core/{ => bond_breakage}/bond_breakage.hpp (87%) rename src/core/{ => lees_edwards}/LeesEdwardsBC.hpp (95%) rename src/core/{ => lees_edwards}/lees_edwards.hpp (97%) rename src/core/{lees_edwards_protocol.hpp => lees_edwards/protocols.hpp} (97%) diff --git a/src/core/BoxGeometry.hpp b/src/core/BoxGeometry.hpp index 8a830240e8d..e33326b974d 100644 --- a/src/core/BoxGeometry.hpp +++ b/src/core/BoxGeometry.hpp @@ -19,8 +19,8 @@ #ifndef CORE_BOX_GEOMETRY_HPP #define CORE_BOX_GEOMETRY_HPP -#include "LeesEdwardsBC.hpp" #include "algorithm/periodic_fold.hpp" +#include "lees_edwards/LeesEdwardsBC.hpp" #include #include diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c6fd3ecc3d5..1b632c873d6 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,6 +1,5 @@ set(EspressoCore_SRC accumulators.cpp - bond_breakage.cpp bond_error.cpp cells.cpp collision.cpp @@ -90,6 +89,7 @@ target_compile_definitions(EspressoCore PUBLIC $<$:H5XX_USE_MPI>) add_subdirectory(accumulators) add_subdirectory(actor) +add_subdirectory(bond_breakage) add_subdirectory(bonded_interactions) add_subdirectory(cluster_analysis) add_subdirectory(constraints) diff --git a/src/core/CellStructure.cpp b/src/core/CellStructure.cpp index 2b78d06cadd..a989244b40e 100644 --- a/src/core/CellStructure.cpp +++ b/src/core/CellStructure.cpp @@ -25,7 +25,7 @@ #include "CellStructureType.hpp" #include "RegularDecomposition.hpp" #include "grid.hpp" -#include "lees_edwards.hpp" +#include "lees_edwards/lees_edwards.hpp" #include diff --git a/src/core/bond_breakage/CMakeLists.txt b/src/core/bond_breakage/CMakeLists.txt new file mode 100644 index 00000000000..f356cb37f3b --- /dev/null +++ b/src/core/bond_breakage/CMakeLists.txt @@ -0,0 +1,2 @@ +target_sources(EspressoCore + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/bond_breakage.cpp) diff --git a/src/core/bond_breakage/actions.hpp b/src/core/bond_breakage/actions.hpp new file mode 100644 index 00000000000..009d22593ac --- /dev/null +++ b/src/core/bond_breakage/actions.hpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 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 . + */ + +#ifndef CORE_BOND_BREAKAGE_ACTIONS_HPP +#define CORE_BOND_BREAKAGE_ACTIONS_HPP + +#include +#include + +#include + +namespace BondBreakage { + +// Delete Actions +struct DeleteBond { + int particle_id; + int bond_partner_id; + int bond_type; + std::size_t hash_value() const { + std::size_t seed = 3875; + boost::hash_combine(seed, particle_id); + boost::hash_combine(seed, bond_partner_id); + boost::hash_combine(seed, bond_type); + return seed; + } + bool operator==(DeleteBond const &rhs) const { + return rhs.particle_id == particle_id and + rhs.bond_partner_id == bond_partner_id and + rhs.bond_type == bond_type; + } +}; + +struct DeleteAllBonds { + int particle_id_1; + int particle_id_2; + std::size_t hash_value() const { + std::size_t seed = 75; + boost::hash_combine(seed, particle_id_1); + boost::hash_combine(seed, particle_id_2); + return seed; + } + bool operator==(DeleteAllBonds const &rhs) const { + return rhs.particle_id_1 == particle_id_1 and + rhs.particle_id_2 == particle_id_2; + } +}; +} // namespace BondBreakage + +// Hash support for std::unordered_set +namespace boost { +template <> struct hash { + std::size_t operator()(BondBreakage::DeleteBond const &t) const noexcept { + return t.hash_value(); + } +}; +template <> struct hash { + std::size_t operator()(BondBreakage::DeleteAllBonds const &t) const noexcept { + return t.hash_value(); + } +}; +} // namespace boost +#endif diff --git a/src/core/bond_breakage.cpp b/src/core/bond_breakage/bond_breakage.cpp similarity index 78% rename from src/core/bond_breakage.cpp rename to src/core/bond_breakage/bond_breakage.cpp index bd24651fc08..2081577d882 100644 --- a/src/core/bond_breakage.cpp +++ b/src/core/bond_breakage/bond_breakage.cpp @@ -16,7 +16,9 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "bond_breakage.hpp" +#include "bond_breakage/bond_breakage.hpp" +#include "bond_breakage/actions.hpp" + #include "cells.hpp" #include "communication.hpp" #include "errorhandling.hpp" @@ -24,73 +26,28 @@ #include -#include #include #include #include #include #include -#include #include #include +#include #include namespace BondBreakage { // Bond breakage specifications -std::unordered_map> breakage_specs; +static std::unordered_map> breakage_specs; -// Delete Actions -struct DeleteBond { - int particle_id; - int bond_partner_id; - int bond_type; - std::size_t hash_value() const { - std::size_t seed = 3875; - boost::hash_combine(seed, particle_id); - boost::hash_combine(seed, bond_partner_id); - boost::hash_combine(seed, bond_type); - return seed; - } - bool operator==(DeleteBond const &rhs) const { - return rhs.particle_id == particle_id and - rhs.bond_partner_id == bond_partner_id and - rhs.bond_type == bond_type; - } -}; - -struct DeleteAllBonds { - int particle_id_1; - int particle_id_2; - std::size_t hash_value() const { - std::size_t seed = 75; - boost::hash_combine(seed, particle_id_1); - boost::hash_combine(seed, particle_id_2); - return seed; - } - bool operator==(DeleteAllBonds const &rhs) const { - return rhs.particle_id_1 == particle_id_1 and - rhs.particle_id_2 == particle_id_2; - } -}; -} // namespace BondBreakage +void insert_spec(int key, std::shared_ptr obj) { + breakage_specs.insert({key, std::move(obj)}); +} -// Hash support for std::unordered_set -namespace boost { -template <> struct hash { - std::size_t operator()(BondBreakage::DeleteBond const &t) const noexcept { - return t.hash_value(); - } -}; -template <> struct hash { - std::size_t operator()(BondBreakage::DeleteAllBonds const &t) const noexcept { - return t.hash_value(); - } -}; -} // namespace boost +void erase_spec(int key) { breakage_specs.erase(key); } -namespace BondBreakage { // Variant holding any of the actions using Action = boost::variant; @@ -103,7 +60,7 @@ struct QueueEntry { int bond_partner_id; int bond_type; - /// Serialization for synchronization across mpi ranks + // Serialization for synchronization across mpi ranks friend class boost::serialization::access; template void serialize(Archive &ar, const unsigned int version) { diff --git a/src/core/bond_breakage.hpp b/src/core/bond_breakage/bond_breakage.hpp similarity index 87% rename from src/core/bond_breakage.hpp rename to src/core/bond_breakage/bond_breakage.hpp index 5a6f0bcd689..7daa50f4049 100644 --- a/src/core/bond_breakage.hpp +++ b/src/core/bond_breakage/bond_breakage.hpp @@ -17,7 +17,8 @@ * along with this program. If not, see . */ -#pragma once +#ifndef CORE_BOND_BREAKAGE_BOND_BREAKAGE_HPP +#define CORE_BOND_BREAKAGE_BOND_BREAKAGE_HPP #include #include @@ -35,7 +36,9 @@ struct BreakageSpec { ActionType action_type; }; -extern std::unordered_map> breakage_specs; +void insert_spec(int key, std::shared_ptr obj); + +void erase_spec(int key); /** @brief Check if the bond between the particles should break, if yes, queue * it. @@ -48,3 +51,4 @@ void clear_queue(); void process_queue(); } // namespace BondBreakage +#endif diff --git a/src/core/forces.cpp b/src/core/forces.cpp index 1f0938b5e1b..ad5c976ab18 100644 --- a/src/core/forces.cpp +++ b/src/core/forces.cpp @@ -26,7 +26,7 @@ #include "EspressoSystemInterface.hpp" -#include "bond_breakage.hpp" +#include "bond_breakage/bond_breakage.hpp" #include "cells.hpp" #include "collision.hpp" #include "comfixed_global.hpp" diff --git a/src/core/forces_inline.hpp b/src/core/forces_inline.hpp index 6e781a390da..0a0f02123a5 100644 --- a/src/core/forces_inline.hpp +++ b/src/core/forces_inline.hpp @@ -28,7 +28,7 @@ #include "forces.hpp" -#include "bond_breakage.hpp" +#include "bond_breakage/bond_breakage.hpp" #include "bonded_interactions/bonded_interaction_data.hpp" #include "bonded_interactions/thermalized_bond_kernel.hpp" #include "immersed_boundary/ibm_tribend.hpp" diff --git a/src/core/integrate.cpp b/src/core/integrate.cpp index 6e094988457..d3777995dec 100644 --- a/src/core/integrate.cpp +++ b/src/core/integrate.cpp @@ -35,7 +35,7 @@ #include "ParticleRange.hpp" #include "accumulators.hpp" -#include "bond_breakage.hpp" +#include "bond_breakage/bond_breakage.hpp" #include "bonded_interactions/rigid_bond.hpp" #include "cells.hpp" #include "collision.hpp" @@ -47,7 +47,7 @@ #include "grid_based_algorithms/lb_interface.hpp" #include "grid_based_algorithms/lb_particle_coupling.hpp" #include "interactions.hpp" -#include "lees_edwards.hpp" +#include "lees_edwards/lees_edwards.hpp" #include "nonbonded_interactions/nonbonded_interaction_data.hpp" #include "npt.hpp" #include "rattle.hpp" diff --git a/src/core/LeesEdwardsBC.hpp b/src/core/lees_edwards/LeesEdwardsBC.hpp similarity index 95% rename from src/core/LeesEdwardsBC.hpp rename to src/core/lees_edwards/LeesEdwardsBC.hpp index f0d849e03fb..683642dc8bf 100644 --- a/src/core/LeesEdwardsBC.hpp +++ b/src/core/lees_edwards/LeesEdwardsBC.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 CORE_LEES_EDWARDS_BC_HPP -#define CORE_LEES_EDWARDS_BC_HPP +#ifndef CORE_LEES_EDWARDS_LEES_EDWARDS_BC_HPP +#define CORE_LEES_EDWARDS_LEES_EDWARDS_BC_HPP #include diff --git a/src/core/lees_edwards.hpp b/src/core/lees_edwards/lees_edwards.hpp similarity index 97% rename from src/core/lees_edwards.hpp rename to src/core/lees_edwards/lees_edwards.hpp index d20a3036bb5..1a938c5805b 100644 --- a/src/core/lees_edwards.hpp +++ b/src/core/lees_edwards/lees_edwards.hpp @@ -16,12 +16,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef LEES_EDWARDS_HPP -#define LEES_EDWARDS_HPP +#ifndef CORE_LEES_EDWARDS_LEES_EDWARDS_HPP +#define CORE_LEES_EDWARDS_LEES_EDWARDS_HPP #include "BoxGeometry.hpp" #include "Particle.hpp" -#include "lees_edwards_protocol.hpp" +#include "lees_edwards/protocols.hpp" #include #include diff --git a/src/core/lees_edwards_protocol.hpp b/src/core/lees_edwards/protocols.hpp similarity index 97% rename from src/core/lees_edwards_protocol.hpp rename to src/core/lees_edwards/protocols.hpp index b0cacac5bf2..65a275497b4 100644 --- a/src/core/lees_edwards_protocol.hpp +++ b/src/core/lees_edwards/protocols.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 LEES_EDWARDS_PROTOCOL_HPP -#define LEES_EDWARDS_PROTOCOL_HPP +#ifndef CORE_LEES_EDWARDS_PROTOCOLS_HPP +#define CORE_LEES_EDWARDS_PROTOCOLS_HPP #include diff --git a/src/core/unit_tests/lees_edwards_test.cpp b/src/core/unit_tests/lees_edwards_test.cpp index 4161f422279..0f9003f3eaf 100644 --- a/src/core/unit_tests/lees_edwards_test.cpp +++ b/src/core/unit_tests/lees_edwards_test.cpp @@ -22,9 +22,9 @@ #include #include "BoxGeometry.hpp" -#include "LeesEdwardsBC.hpp" #include "Particle.hpp" -#include "lees_edwards.hpp" +#include "lees_edwards/LeesEdwardsBC.hpp" +#include "lees_edwards/lees_edwards.hpp" #include diff --git a/src/script_interface/bond_breakage/BreakageSpec.hpp b/src/script_interface/bond_breakage/BreakageSpec.hpp index d44f9900816..0ab04da26d8 100644 --- a/src/script_interface/bond_breakage/BreakageSpec.hpp +++ b/src/script_interface/bond_breakage/BreakageSpec.hpp @@ -19,7 +19,7 @@ #pragma once -#include "bond_breakage.hpp" +#include "core/bond_breakage/bond_breakage.hpp" #include "script_interface/ScriptInterface.hpp" diff --git a/src/script_interface/bond_breakage/BreakageSpecs.hpp b/src/script_interface/bond_breakage/BreakageSpecs.hpp index 214ab7df2ac..38a359d3bfc 100644 --- a/src/script_interface/bond_breakage/BreakageSpecs.hpp +++ b/src/script_interface/bond_breakage/BreakageSpecs.hpp @@ -21,7 +21,7 @@ #include "BreakageSpec.hpp" -#include "core/bond_breakage.hpp" +#include "core/bond_breakage/bond_breakage.hpp" #include "script_interface/ObjectMap.hpp" #include "script_interface/ScriptInterface.hpp" @@ -49,10 +49,10 @@ class BreakageSpecs : public ObjectMap { void insert_in_core(key_type const &key, mapped_type const &obj_ptr) override { auto core_spec = obj_ptr->breakage_spec(); - ::BondBreakage::breakage_specs.insert({key, core_spec}); + ::BondBreakage::insert_spec(key, core_spec); } void erase_in_core(key_type const &key) override { - ::BondBreakage::breakage_specs.erase(key); + ::BondBreakage::erase_spec(key); } private: diff --git a/src/script_interface/lees_edwards/LeesEdwards.hpp b/src/script_interface/lees_edwards/LeesEdwards.hpp index 657a4d03bbc..ae0ef4722e8 100644 --- a/src/script_interface/lees_edwards/LeesEdwards.hpp +++ b/src/script_interface/lees_edwards/LeesEdwards.hpp @@ -22,7 +22,7 @@ #include "Protocol.hpp" #include "core/grid.hpp" -#include "core/lees_edwards.hpp" +#include "core/lees_edwards/lees_edwards.hpp" #include "script_interface/ScriptInterface.hpp" #include "script_interface/auto_parameters/AutoParameters.hpp" diff --git a/src/script_interface/lees_edwards/LinearShear.hpp b/src/script_interface/lees_edwards/LinearShear.hpp index 7be063b8585..9616858988d 100644 --- a/src/script_interface/lees_edwards/LinearShear.hpp +++ b/src/script_interface/lees_edwards/LinearShear.hpp @@ -19,7 +19,7 @@ #ifndef SCRIPT_INTERFACE_LEES_EDWARDS_LINEAR_SHEAR_HPP #define SCRIPT_INTERFACE_LEES_EDWARDS_LINEAR_SHEAR_HPP -#include "core/lees_edwards.hpp" +#include "core/lees_edwards/lees_edwards.hpp" #include "script_interface/ScriptInterface.hpp" #include "script_interface/auto_parameters/AutoParameters.hpp" diff --git a/src/script_interface/lees_edwards/Off.hpp b/src/script_interface/lees_edwards/Off.hpp index 5f102048765..3125ba3cfa2 100644 --- a/src/script_interface/lees_edwards/Off.hpp +++ b/src/script_interface/lees_edwards/Off.hpp @@ -19,7 +19,7 @@ #ifndef SCRIPT_INTERFACE_LEES_EDWARDS_OFF_HPP #define SCRIPT_INTERFACE_LEES_EDWARDS_OFF_HPP -#include "core/lees_edwards.hpp" +#include "core/lees_edwards/lees_edwards.hpp" #include "script_interface/ScriptInterface.hpp" #include "script_interface/auto_parameters/AutoParameters.hpp" diff --git a/src/script_interface/lees_edwards/OscillatoryShear.hpp b/src/script_interface/lees_edwards/OscillatoryShear.hpp index 3cae42c3682..242bc268eb3 100644 --- a/src/script_interface/lees_edwards/OscillatoryShear.hpp +++ b/src/script_interface/lees_edwards/OscillatoryShear.hpp @@ -19,7 +19,7 @@ #ifndef SCRIPT_INTERFACE_LEES_EDWARDS_OSCILLATORY_SHEAR_HPP #define SCRIPT_INTERFACE_LEES_EDWARDS_OSCILLATORY_SHEAR_HPP -#include "core/lees_edwards.hpp" +#include "core/lees_edwards/lees_edwards.hpp" #include "script_interface/ScriptInterface.hpp" #include "script_interface/auto_parameters/AutoParameters.hpp" diff --git a/src/script_interface/lees_edwards/Protocol.hpp b/src/script_interface/lees_edwards/Protocol.hpp index 9f179aa6067..faa09aa5a9e 100644 --- a/src/script_interface/lees_edwards/Protocol.hpp +++ b/src/script_interface/lees_edwards/Protocol.hpp @@ -19,7 +19,7 @@ #ifndef SCRIPT_INTERFACE_LEES_EDWARDS_PROTOCOL_HPP #define SCRIPT_INTERFACE_LEES_EDWARDS_PROTOCOL_HPP -#include "core/lees_edwards.hpp" +#include "core/lees_edwards/lees_edwards.hpp" #include "script_interface/ScriptInterface.hpp" #include "script_interface/auto_parameters/AutoParameters.hpp" From c9dd949ae29f18aa41b1342d958c5ccf636aa29f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 2 Mar 2022 13:14:57 +0100 Subject: [PATCH 71/99] core: Unit test bond breakage actions --- src/core/unit_tests/CMakeLists.txt | 2 + src/core/unit_tests/bond_breakage_test.cpp | 58 ++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/core/unit_tests/bond_breakage_test.cpp diff --git a/src/core/unit_tests/CMakeLists.txt b/src/core/unit_tests/CMakeLists.txt index 656f4ebfc36..95560b0931a 100644 --- a/src/core/unit_tests/CMakeLists.txt +++ b/src/core/unit_tests/CMakeLists.txt @@ -60,3 +60,5 @@ unit_test(NAME BondList_test SRC BondList_test.cpp DEPENDS EspressoCore) unit_test(NAME energy_test SRC energy_test.cpp DEPENDS EspressoCore) unit_test(NAME bonded_interactions_map_test SRC bonded_interactions_map_test.cpp DEPENDS EspressoCore) +unit_test(NAME bond_breakage_test SRC bond_breakage_test.cpp DEPENDS + EspressoCore) diff --git a/src/core/unit_tests/bond_breakage_test.cpp b/src/core/unit_tests/bond_breakage_test.cpp new file mode 100644 index 00000000000..72369a29e70 --- /dev/null +++ b/src/core/unit_tests/bond_breakage_test.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 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 . + */ + +#define BOOST_TEST_MODULE "Bond breakage" +#define BOOST_TEST_DYN_LINK +#include + +#include "bond_breakage/actions.hpp" + +BOOST_AUTO_TEST_CASE(test_actions_equality) { + { + using Action = BondBreakage::DeleteBond; + BOOST_CHECK((Action{1, 2, 3} == Action{1, 2, 3})); + BOOST_CHECK(!(Action{1, 2, 3} == Action{0, 2, 3})); + BOOST_CHECK(!(Action{1, 2, 3} == Action{1, 0, 3})); + BOOST_CHECK(!(Action{1, 2, 3} == Action{1, 2, 0})); + } + + { + using Action = BondBreakage::DeleteAllBonds; + BOOST_CHECK((Action{1, 2} == Action{1, 2})); + BOOST_CHECK(!(Action{1, 2} == Action{0, 2})); + BOOST_CHECK(!(Action{1, 2} == Action{1, 0})); + } +} + +BOOST_AUTO_TEST_CASE(test_actions_hash_value) { + { + using Action = BondBreakage::DeleteBond; + BOOST_CHECK((Action{1, 2, 3}.hash_value() == Action{1, 2, 3}.hash_value())); + BOOST_CHECK((Action{1, 2, 3}.hash_value() != Action{0, 2, 3}.hash_value())); + BOOST_CHECK((Action{1, 2, 3}.hash_value() != Action{1, 0, 3}.hash_value())); + BOOST_CHECK((Action{1, 2, 3}.hash_value() != Action{1, 2, 0}.hash_value())); + } + + { + using Action = BondBreakage::DeleteAllBonds; + BOOST_CHECK((Action{1, 2}.hash_value() == Action{1, 2}.hash_value())); + BOOST_CHECK((Action{1, 2}.hash_value() != Action{0, 2}.hash_value())); + BOOST_CHECK((Action{1, 2}.hash_value() != Action{1, 0}.hash_value())); + } +} From 60ea4bd6f7d0af2e8b8265bafeb56604e42978f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 4 Mar 2022 14:58:05 +0100 Subject: [PATCH 72/99] python: Remove code duplication in ScriptObjectMap --- src/python/espressomd/bond_breakage.py | 1 - src/python/espressomd/interactions.pyx | 87 +++++++--------------- src/python/espressomd/script_interface.pyx | 28 +++---- testsuite/python/bond_breakage.py | 4 +- 4 files changed, 42 insertions(+), 78 deletions(-) diff --git a/src/python/espressomd/bond_breakage.py b/src/python/espressomd/bond_breakage.py index ef7228a9ab4..f4a852ee322 100644 --- a/src/python/espressomd/bond_breakage.py +++ b/src/python/espressomd/bond_breakage.py @@ -27,4 +27,3 @@ class BreakageSpec(ScriptInterfaceHelper): @script_interface_register class BreakageSpecs(ScriptObjectMap): _so_name = "BondBreakage::BreakageSpecs" - _key_type = int diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 9b82e08cd61..03777e23ca3 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -25,7 +25,7 @@ include "myconfig.pxi" from . import utils from .utils import is_valid_type from .utils cimport check_type_or_throw_except -from .script_interface import ScriptObjectRegistry, ScriptInterfaceHelper, script_interface_register +from .script_interface import ScriptObjectMap, ScriptInterfaceHelper, script_interface_register cdef class NonBondedInteraction: @@ -2850,13 +2850,27 @@ def get_bonded_interaction_type_from_es_core(bond_id): @script_interface_register -class BondedInteractions(ScriptObjectRegistry): +class BondedInteractions(ScriptObjectMap): """ Represents the bonded interactions list. Individual interactions can be accessed using ``BondedInteractions[i]``, where ``i`` is the bond id. + + Methods + ------- + remove(): + Remove a bond from the list. + This is a noop if the bond does not exist. + + Parameters + ---------- + bond_id : :obj:`int` + + clear() + Remove all bonds. + """ _so_name = "Interactions::BondedInteractions" @@ -2880,36 +2894,11 @@ class BondedInteractions(ScriptObjectRegistry): bonded_ia = args[0] else: raise TypeError("A BondedInteraction object needs to be passed.") - bond_id = self._insert_bond(bonded_ia) + bond_id = self._insert_bond(None, bonded_ia) return bond_id - def remove(self, bond_id): - """ - Remove a bond from the list. This is a noop if the bond does not exist. - - Parameters - ---------- - bond_id : :obj:`int` - - """ - # type of key must be int - if not is_valid_type(bond_id, int): - raise ValueError( - "Index to BondedInteractions[] has to be an integer referring to a bond id") - - self.call_method("erase", key=bond_id) - - def clear(self): - """ - Remove all bonds. - - """ - self.call_method("clear") - - def _get_bond(self, bond_id): - if not is_valid_type(bond_id, int): - raise ValueError( - "Index to BondedInteractions[] has to be an integer referring to a bond id") + def __getitem__(self, bond_id): + self._assert_key_type(bond_id) if self.call_method('has_bond', bond_id=bond_id): bond_obj = self.call_method('get_bond', bond_id=bond_id) @@ -2930,16 +2919,10 @@ class BondedInteractions(ScriptObjectRegistry): # which links to the bonded interaction object return bond_class(bond_id) - def __getitem__(self, bond_id): - return self._get_bond(bond_id) - - def __setitem__(self, bond_id, value): - self._insert_bond(value, bond_id) - - def __delitem__(self, bond_id): - self.remove(bond_id) + def __setitem__(self, bond_id, bond_obj): + self._insert_bond(bond_id, bond_obj) - def _insert_bond(self, bond, bond_id=None): + def _insert_bond(self, bond_id, bond_obj): """ Inserts a new bond. If a ``bond_id`` is given, the bond is inserted at that id. If no id is given, a new id is generated. @@ -2950,29 +2933,26 @@ class BondedInteractions(ScriptObjectRegistry): """ # Validate arguments - if bond_id is not None: - if not is_valid_type(bond_id, int): - raise ValueError( - "Index to BondedInteractions[] has to be an integer referring to a bond id") - if not isinstance(bond, BondedInteraction): + if not isinstance(bond_obj, BondedInteraction): raise ValueError( "Only subclasses of BondedInteraction can be assigned.") # Send the script interface object pointer to the core if bond_id is None: - bond_id = self.call_method("insert", object=bond) + bond_id = self.call_method("insert", object=bond_obj) else: # Throw error if attempting to overwrite a bond of different type + self._assert_key_type(bond_id) if self.call_method("contains", key=bond_id): old_type = bonded_interaction_classes[ get_bonded_interaction_type_from_es_core(bond_id)] - if not type(bond) is old_type: + if not type(bond_obj) is old_type: raise ValueError( "Bonds can only be overwritten by bonds of equal type.") - self.call_method("insert", key=bond_id, object=bond) + self.call_method("insert", key=bond_id, object=bond_obj) # Save the bond id in the BondedInteraction instance - bond._bond_id = bond_id + bond_obj._bond_id = bond_id return bond_id @@ -2985,11 +2965,6 @@ class BondedInteractions(ScriptObjectRegistry): if get_bonded_interaction_type_from_es_core(bond_id): yield self[bond_id] - def __reduce__(self): - so_callback, (so_name, so_bytestring) = super().__reduce__() - return (_restore_bonded_interactions, - (so_callback, (so_name, so_bytestring), self.__getstate__())) - def __getstate__(self): params = {} for bond_id in self.call_method('get_bond_ids'): @@ -3004,9 +2979,3 @@ class BondedInteractions(ScriptObjectRegistry): for bond_id, (bond_params, bond_type) in params.items(): self[bond_id] = bonded_interaction_classes[bond_type]( **bond_params) - - -def _restore_bonded_interactions(so_callback, so_callback_args, state): - so = so_callback(*so_callback_args) - so.__setstate__(state) - return so diff --git a/src/python/espressomd/script_interface.pyx b/src/python/espressomd/script_interface.pyx index 531639c8f1c..78f2f38d303 100644 --- a/src/python/espressomd/script_interface.pyx +++ b/src/python/espressomd/script_interface.pyx @@ -406,14 +406,10 @@ class ScriptObjectMap(ScriptObjectRegistry): def remove(self, key): """ - Removes the element with the given key + Remove the element with the given key. + This is a noop if the key does not exist. """ - # Validate key type - if not is_valid_type(key, self._key_type): - raise ValueError( - f"Key has to be of type {self._key_type.__name__}") - - self.call_method("erase", key=key) + self.__delitem__(key) def clear(self): """ @@ -422,21 +418,17 @@ class ScriptObjectMap(ScriptObjectRegistry): """ self.call_method("clear") - def _get(self, key): - if not is_valid_type(key, self._key_type): - raise ValueError( - f"Key has to be of type {self._key_type.__name__}") - - return self.call_method("get", key=key) - def __getitem__(self, key): - return self._get(key) + self._assert_key_type(key) + return self.call_method("get", key=key) def __setitem__(self, key, value): + self._assert_key_type(key) self.call_method("insert", key=key, object=value) def __delitem__(self, key): - self.remove(key) + self._assert_key_type(key) + self.call_method("erase", key=key) def keys(self): return self.call_method("keys") @@ -447,6 +439,10 @@ class ScriptObjectMap(ScriptObjectRegistry): def items(self): for k in self.keys(): yield k, self[k] + def _assert_key_type(self, key): + if not is_valid_type(key, self._key_type): + raise TypeError(f"Key has to be of type {self._key_type.__name__}") + @classmethod def _restore_object(cls, so_callback, so_callback_args, state): so = so_callback(*so_callback_args) diff --git a/testsuite/python/bond_breakage.py b/testsuite/python/bond_breakage.py index ba538cb81c9..c85f89b6003 100644 --- a/testsuite/python/bond_breakage.py +++ b/testsuite/python/bond_breakage.py @@ -81,9 +81,9 @@ def test_00_interface(self): self.system.bond_breakage.clear() self.assertEqual(len(self.system.bond_breakage), 0) self.assertEqual(self.system.bond_breakage.keys(), []) - with self.assertRaisesRegex(ValueError, "Key has to be of type int"): + with self.assertRaisesRegex(TypeError, "Key has to be of type int"): self.system.bond_breakage[self.h1] - with self.assertRaisesRegex(ValueError, "Key has to be of type int"): + with self.assertRaisesRegex(TypeError, "Key has to be of type int"): self.system.bond_breakage.remove(self.h1) with self.assertRaisesRegex(RuntimeError, "Inserting breakage spec without a bond type is not permitted"): self.system.bond_breakage.call_method("insert", object=spec2) From 96b118d4469805e8d3747fcdeb1ba998e627ee06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 4 Mar 2022 15:04:43 +0100 Subject: [PATCH 73/99] python: Allow bond objects as key in bond breakage --- src/python/espressomd/bond_breakage.py | 15 +++++++++++++++ testsuite/python/bond_breakage.py | 22 +++++++++++++--------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/python/espressomd/bond_breakage.py b/src/python/espressomd/bond_breakage.py index f4a852ee322..9910299b4bc 100644 --- a/src/python/espressomd/bond_breakage.py +++ b/src/python/espressomd/bond_breakage.py @@ -17,6 +17,7 @@ # along with this program. If not, see . # from .script_interface import script_interface_register, ScriptObjectMap, ScriptInterfaceHelper +from . import interactions @script_interface_register @@ -27,3 +28,17 @@ class BreakageSpec(ScriptInterfaceHelper): @script_interface_register class BreakageSpecs(ScriptObjectMap): _so_name = "BondBreakage::BreakageSpecs" + + def _get_key(self, key): + """Convert a bond object to a bond id.""" + if isinstance(key, interactions.BondedInteraction): + key = key._bond_id + if key == -1: + raise ValueError("Bond needs to be added to the system first") + return key + + def __getitem__(self, key): + return super().__getitem__(self._get_key(key)) + + def __setitem__(self, key, value): + return super().__setitem__(self._get_key(key), value) diff --git a/testsuite/python/bond_breakage.py b/testsuite/python/bond_breakage.py index c85f89b6003..7405f94a0c9 100644 --- a/testsuite/python/bond_breakage.py +++ b/testsuite/python/bond_breakage.py @@ -82,9 +82,13 @@ def test_00_interface(self): self.assertEqual(len(self.system.bond_breakage), 0) self.assertEqual(self.system.bond_breakage.keys(), []) with self.assertRaisesRegex(TypeError, "Key has to be of type int"): - self.system.bond_breakage[self.h1] + self.system.bond_breakage[None] with self.assertRaisesRegex(TypeError, "Key has to be of type int"): - self.system.bond_breakage.remove(self.h1) + self.system.bond_breakage[None] = None + with self.assertRaisesRegex(TypeError, "Key has to be of type int"): + self.system.bond_breakage.remove(None) + with self.assertRaisesRegex(ValueError, "Bond needs to be added to the system first"): + self.system.bond_breakage[HarmonicBond(k=1, r_0=0)] with self.assertRaisesRegex(RuntimeError, "Inserting breakage spec without a bond type is not permitted"): self.system.bond_breakage.call_method("insert", object=spec2) @@ -92,7 +96,7 @@ def test_ignore(self): system = self.system # Particles closer than cutoff - system.bond_breakage[self.h1._bond_id] = BreakageSpec( + system.bond_breakage[self.h1] = BreakageSpec( breakage_length=2, action_type="revert_center_bond") self.p1.bonds = ((self.h1, self.p2)) @@ -104,7 +108,7 @@ def test_ignore(self): self.assertEqual(self.p2.bonds, ((self.h1, self.p1.id),)) # Different bond type - system.bond_breakage[self.h1._bond_id] = BreakageSpec( + system.bond_breakage[self.h1] = BreakageSpec( breakage_length=0.2, action_type="revert_center_bond") self.p1.bonds = [(self.h2, self.p2)] self.p2.bonds = [(self.h2, self.p1)] @@ -116,7 +120,7 @@ def test_delete_bond(self): system = self.system # Particles closer than cutoff - system.bond_breakage[self.h1._bond_id] = BreakageSpec( + system.bond_breakage[self.h1] = BreakageSpec( breakage_length=0, action_type="revert_center_bond") self.p1.bonds = [(self.h1, self.p2)] @@ -131,7 +135,7 @@ def test_revert_bind_at_point_of_collision(self): system = self.system # Particles closer than cutoff - system.bond_breakage[self.h1._bond_id] = BreakageSpec( + system.bond_breakage[self.h1] = BreakageSpec( breakage_length=0.5, action_type="revert_vs_bond") self.p1.bonds = [(self.h2, self.p2)] @@ -150,7 +154,7 @@ def test_exceptions(self): system = self.system # Particles closer than cutoff - system.bond_breakage[self.h2._bond_id] = BreakageSpec( + system.bond_breakage[self.h2] = BreakageSpec( breakage_length=0.5, action_type="revert_vs_bond") self.p1.bonds = [(self.h2, self.p2)] @@ -225,7 +229,7 @@ def test_center_bonds(self): self.system.integrator.run(1) self.system.collision_detection.set_params(mode="off") - self.system.bond_breakage[harm._bond_id] = BreakageSpec( + self.system.bond_breakage[harm] = BreakageSpec( breakage_length=crit, action_type="revert_center_bond") self.system.integrator.run(1) @@ -258,7 +262,7 @@ def test_vs_bonds(self): self.system.integrator.run(1) self.system.collision_detection.set_params(mode="off") - self.system.bond_breakage[harm._bond_id] = BreakageSpec( + self.system.bond_breakage[harm] = BreakageSpec( breakage_length=crit_vs, action_type="revert_vs_bond") self.system.integrator.run(1) From a8dfad13c1033685b0ceb9db157c1579b75d207d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 4 Mar 2022 15:14:29 +0100 Subject: [PATCH 74/99] docs: Bond breakage framework --- doc/sphinx/advanced_methods.rst | 57 ++++++++++++++++++++++++++ src/python/espressomd/bond_breakage.py | 13 ++++++ 2 files changed, 70 insertions(+) diff --git a/doc/sphinx/advanced_methods.rst b/doc/sphinx/advanced_methods.rst index cd8ff224c57..5a011df4a22 100644 --- a/doc/sphinx/advanced_methods.rst +++ b/doc/sphinx/advanced_methods.rst @@ -118,6 +118,63 @@ The following limitations currently apply for the collision detection: * The ``"bind at point of collision"`` approach cannot handle collisions between virtual sites +.. _Deleting bonds when particles are pulled: + +Deleting bonds when particles are pulled +---------------------------------------- + +With this feature, bonds between particles can be deleted automatically +when a critical bond extension is reached. This is useful to simulate +weak bonds between particles. + +The bond breakage action is specified for individual bonds via the system +:attr:`~espressomd.system.System.bond_breakage` attribute. + +Several modes are available: + +* ``"revert_center_bond"``: delete a bond from the first particle +* ``"revert_vs_bond"``: delete a virtual bond, can be used together with + the :ref:`collision detection` + feature (configured in mode ``"bind_at_point_of_collision"``) to + reversibly create and delete a bond +* ``"none"``: cancel an existing bond breakage specification + +Example:: + + import espressomd + import espressomd.interactions + import espressomd.bond_breakage + import numpy as np + + system = espressomd.System(box_l=[10] * 3) + system.cell_system.skin = 0.4 + system.time_step = 0.1 + system.min_global_cut = 2. + + h1 = espressomd.interactions.HarmonicBond(k=0.01, r_0=0.4) + h2 = espressomd.interactions.HarmonicBond(k=0.01, r_0=0.5) + system.bonded_inter.add(h1) + system.bonded_inter.add(h2) + system.bond_breakage[h1] = espressomd.bond_breakage.BreakageSpec( + breakage_length=0.5, action_type="revert_center_bond") + + p1 = system.part.add(id=1, pos=[0.00, 0.0, 0.0], v=[0.0, 0.0, 0.0]) + p2 = system.part.add(id=2, pos=[0.46, 0.0, 0.0], v=[0.1, 0.0, 0.0]) + p1.add_bond((h1, p2)) + p1.add_bond((h2, p2)) + for i in range(3): + system.integrator.run(2) + bond_length = np.linalg.norm(system.distance_vec(p1, p2)) + print(f"length = {bond_length:.2f}, bonds = {p1.bonds}") + +Output: + +.. code-block:: none + + length = 0.48, bonds = ((, 2), (, 2)) + length = 0.50, bonds = ((, 2), (, 2)) + length = 0.52, bonds = ((, 2),) + .. _Immersed Boundary Method for soft elastic objects: diff --git a/src/python/espressomd/bond_breakage.py b/src/python/espressomd/bond_breakage.py index 9910299b4bc..73423b550d2 100644 --- a/src/python/espressomd/bond_breakage.py +++ b/src/python/espressomd/bond_breakage.py @@ -22,6 +22,19 @@ @script_interface_register class BreakageSpec(ScriptInterfaceHelper): + """ + Specifications for bond breakage. + See :ref:`Deleting bonds when particles are pulled` for more details. + + Parameters + ---------- + breakage_length: :obj:`float` + Maximal bond extension until the bond breaks. + action_type: :obj:`str`, \{'revert_center_bond', 'revert_vs_bond', 'none'\} + Action triggered when the bond reaches its maximal extension. + + """ + _so_name = "BondBreakage::BreakageSpec" From bf7d972da6e11b04b2e77982904bb3f002dd8bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 4 Mar 2022 16:40:31 +0100 Subject: [PATCH 75/99] docs: Modeling reversible bonds Co-authored-by: Sebastian Bindgen --- doc/sphinx/advanced_methods.rst | 42 ++++++++++++++++++++------ src/python/espressomd/bond_breakage.py | 2 +- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/doc/sphinx/advanced_methods.rst b/doc/sphinx/advanced_methods.rst index 5a011df4a22..45c32cba2b4 100644 --- a/doc/sphinx/advanced_methods.rst +++ b/doc/sphinx/advanced_methods.rst @@ -118,14 +118,14 @@ The following limitations currently apply for the collision detection: * The ``"bind at point of collision"`` approach cannot handle collisions between virtual sites -.. _Deleting bonds when particles are pulled: +.. _Deleting bonds when particles are pulled apart: -Deleting bonds when particles are pulled ----------------------------------------- +Deleting bonds when particles are pulled apart +---------------------------------------------- With this feature, bonds between particles can be deleted automatically -when a critical bond extension is reached. This is useful to simulate -weak bonds between particles. +when the bond length exceeds a critical distance. This is used to model +breakable bonds. The bond breakage action is specified for individual bonds via the system :attr:`~espressomd.system.System.bond_breakage` attribute. @@ -133,10 +133,7 @@ The bond breakage action is specified for individual bonds via the system Several modes are available: * ``"revert_center_bond"``: delete a bond from the first particle -* ``"revert_vs_bond"``: delete a virtual bond, can be used together with - the :ref:`collision detection` - feature (configured in mode ``"bind_at_point_of_collision"``) to - reversibly create and delete a bond +* ``"revert_vs_bond"``: delete a bond between the virtual site * ``"none"``: cancel an existing bond breakage specification Example:: @@ -175,6 +172,33 @@ Output: length = 0.50, bonds = ((, 2), (, 2)) length = 0.52, bonds = ((, 2),) +Please note there is no special treatment for the energy released or consumed +by bond removal. This can lead to physical inconsistencies. + + +.. _Modeling reversible bonds: + +Modeling reversible bonds +------------------------- + +The :ref:`collision detection` +and :ref:`bond breakage` +features can be combined to model reversible bonds. + +Two combinations are possible: + +* ``"revert_center_bond"`` mode for breakable bonds together with + ``"bond_centers"`` mode for collision detection: + used to create or delete a bond between two real particles +* ``"revert_vs_bond"`` mode for breakable bonds together with + ``"bind_at_point_of_collision"`` mode for collision detection: + used to create or delete virtual sites (the implicitly created + bond between the real particles isn't affected) + +Please note that virtual sites are not automatically removed from the +simulation, therefore the particle number will increase. If you want to +remove virtual sites, you need to do so manually. + .. _Immersed Boundary Method for soft elastic objects: diff --git a/src/python/espressomd/bond_breakage.py b/src/python/espressomd/bond_breakage.py index 73423b550d2..f8ca6b327d1 100644 --- a/src/python/espressomd/bond_breakage.py +++ b/src/python/espressomd/bond_breakage.py @@ -24,7 +24,7 @@ class BreakageSpec(ScriptInterfaceHelper): """ Specifications for bond breakage. - See :ref:`Deleting bonds when particles are pulled` for more details. + See :ref:`Deleting bonds when particles are pulled apart` for more details. Parameters ---------- From e8054010f6ba81effdb98546f277b48d69f41757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 7 Mar 2022 19:42:01 +0100 Subject: [PATCH 76/99] CMake: Don't rename test files when possible When a test case doesn't need CMake variable substitution during configuration, there is no need to rename it. Renaming test files hinders Coverage.py's ability to map test files in the build directory to the corresponding test files in the root directory. Use FILE_SUFFIX to change the filepath during configuration, and SUFFIX to change the test name in CTest. --- testsuite/python/CMakeLists.txt | 17 ++++++++++------- testsuite/python/ek_eof_one_species.py | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index 9c3d60980cb..65f9f3106f4 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -1,11 +1,14 @@ function(PYTHON_TEST) - cmake_parse_arguments(TEST "" "FILE;MAX_NUM_PROC;SUFFIX" + cmake_parse_arguments(TEST "" "FILE;MAX_NUM_PROC;SUFFIX;FILE_SUFFIX" "DEPENDS;DEPENDENCIES;LABELS" ${ARGN}) get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE) + if(TEST_FILE_SUFFIX) + set(TEST_NAME "${TEST_NAME}_${TEST_FILE_SUFFIX}") + endif() + set(TEST_FILE_CONFIGURED "${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME}.py") if(TEST_SUFFIX) set(TEST_NAME "${TEST_NAME}_${TEST_SUFFIX}") endif() - set(TEST_FILE_CONFIGURED "${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME}.py") configure_file(${TEST_FILE} ${TEST_FILE_CONFIGURED}) foreach(dependency IN LISTS TEST_DEPENDENCIES) configure_file(${dependency} ${CMAKE_CURRENT_BINARY_DIR}/${dependency}) @@ -78,10 +81,10 @@ foreach( endif() foreach(TEST_BINARY ${TEST_BINARY_LIST}) python_test(FILE save_checkpoint.py MAX_NUM_PROC ${TEST_NPROCS} LABELS - ${TEST_LABELS} SUFFIX ${TEST_COMBINATION}_${TEST_BINARY}) + ${TEST_LABELS} FILE_SUFFIX ${TEST_COMBINATION}_${TEST_BINARY}) python_test( FILE test_checkpoint.py MAX_NUM_PROC ${TEST_NPROCS} LABELS ${TEST_LABELS} - SUFFIX ${TEST_COMBINATION}_${TEST_BINARY} DEPENDS + FILE_SUFFIX ${TEST_COMBINATION}_${TEST_BINARY} DEPENDS save_checkpoint_${TEST_COMBINATION}_${TEST_BINARY}) endforeach(TEST_BINARY) endforeach(TEST_COMBINATION) @@ -135,9 +138,9 @@ python_test(FILE lb_stokes_sphere.py MAX_NUM_PROC 4 LABELS gpu long) python_test(FILE lb_pressure_tensor.py MAX_NUM_PROC 1 LABELS gpu long) python_test(FILE ek_fluctuations.py MAX_NUM_PROC 1 LABELS gpu) python_test(FILE ek_charged_plate.py MAX_NUM_PROC 1 LABELS gpu) -python_test(FILE ek_eof_one_species.py MAX_NUM_PROC 1 LABELS gpu SUFFIX x) -python_test(FILE ek_eof_one_species.py MAX_NUM_PROC 1 LABELS gpu SUFFIX y) -python_test(FILE ek_eof_one_species.py MAX_NUM_PROC 1 LABELS gpu SUFFIX z) +python_test(FILE ek_eof_one_species.py MAX_NUM_PROC 1 LABELS gpu FILE_SUFFIX x) +python_test(FILE ek_eof_one_species.py MAX_NUM_PROC 1 LABELS gpu FILE_SUFFIX y) +python_test(FILE ek_eof_one_species.py MAX_NUM_PROC 1 LABELS gpu FILE_SUFFIX z) python_test(FILE exclusions.py MAX_NUM_PROC 2) python_test(FILE langevin_thermostat.py MAX_NUM_PROC 1) python_test(FILE langevin_thermostat_stats.py MAX_NUM_PROC 1 LABELS long) diff --git a/testsuite/python/ek_eof_one_species.py b/testsuite/python/ek_eof_one_species.py index 38af417e236..0c6c53e5619 100644 --- a/testsuite/python/ek_eof_one_species.py +++ b/testsuite/python/ek_eof_one_species.py @@ -63,7 +63,7 @@ params_base['density_counterions'] = -2.0 * \ params_base['sigma'] / params_base['width'] -axis = "@TEST_SUFFIX@" +axis = "@TEST_FILE_SUFFIX@" params = { "x": dict([ ('box_x', params_base['thickness']), From a9c607b702e08d2f0ea6b8ef90012ca9e4b94e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 7 Mar 2022 20:36:05 +0100 Subject: [PATCH 77/99] CMake: Remove FILE_SUFFIX The checkpoint and ek_eof_one_species tests no longer depend on CMake variables substitution. Instead, the test parameters are passed as arguments to the test file and a class factory generates the test class and parses the test parameters. --- .codecov.yml | 3 - testsuite/python/CMakeLists.txt | 86 ++++++++------- testsuite/python/ek_eof_one_species.py | 14 ++- testsuite/python/save_checkpoint.py | 57 +++++----- testsuite/python/test_checkpoint.py | 33 +++--- testsuite/python/unittest_generator.py | 144 +++++++++++++++++++++++++ 6 files changed, 254 insertions(+), 83 deletions(-) create mode 100644 testsuite/python/unittest_generator.py diff --git a/.codecov.yml b/.codecov.yml index 9fb0eec5b84..725210efbcb 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -48,6 +48,3 @@ ignore: - "doc/tutorials" - "samples" - "maintainer" -fixes: - - "testsuite/python/save_checkpoint*::testsuite/python/save_checkpoint.py" - - "testsuite/python/test_checkpoint*::testsuite/python/test_checkpoint.py" diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index 65f9f3106f4..d3ff5d73aa2 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -1,10 +1,7 @@ function(PYTHON_TEST) - cmake_parse_arguments(TEST "" "FILE;MAX_NUM_PROC;SUFFIX;FILE_SUFFIX" - "DEPENDS;DEPENDENCIES;LABELS" ${ARGN}) + cmake_parse_arguments(TEST "" "FILE;MAX_NUM_PROC;SUFFIX" + "DEPENDS;DEPENDENCIES;LABELS;ARGUMENTS" ${ARGN}) get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE) - if(TEST_FILE_SUFFIX) - set(TEST_NAME "${TEST_NAME}_${TEST_FILE_SUFFIX}") - endif() set(TEST_FILE_CONFIGURED "${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME}.py") if(TEST_SUFFIX) set(TEST_NAME "${TEST_NAME}_${TEST_SUFFIX}") @@ -33,10 +30,10 @@ function(PYTHON_TEST) ${MPIEXEC} ${MPIEXEC_OVERSUBSCRIBE} ${MPIEXEC_NUMPROC_FLAG} ${TEST_NUM_PROC} ${MPIEXEC_PREFLAGS} ${MPIEXEC_TMPDIR} ${CMAKE_BINARY_DIR}/pypresso ${PYPRESSO_OPTIONS} ${TEST_FILE} - ${MPIEXEC_POSTFLAGS}) + ${TEST_ARGUMENTS} ${MPIEXEC_POSTFLAGS}) else() add_test(${TEST_NAME} ${CMAKE_BINARY_DIR}/pypresso ${PYPRESSO_OPTIONS} - ${TEST_FILE}) + ${TEST_FILE} ${TEST_ARGUMENTS}) endif() set_tests_properties(${TEST_NAME} PROPERTIES PROCESSORS ${TEST_NUM_PROC} DEPENDS "${TEST_DEPENDS}") @@ -58,36 +55,45 @@ function(PYTHON_TEST) set(python_tests ${python_tests} ${TEST_FILE} PARENT_SCOPE) endfunction(PYTHON_TEST) -# Checkpointing tests: semicolon-separated list of mutually-compatible features. -# Separate features with hyphens, use a period to add an optional flag. -foreach( - TEST_COMBINATION - lb.cpu-p3m.cpu-lj-therm.lb;lb.gpu-p3m.elc-lj-therm.lb;lb.off-therm.npt-int.npt;lb.off-int.sd;lb.off-dp3m.cpu-therm.langevin-int.nvt;lb.off-therm.dpd-int.nvt;lb.off-scafacos-therm.bd-int.bd;lb.off-therm.sdm-int.sdm;lb.cpu-p3m.cpu-lj-therm.lb-mpi.1.core -) - if(${TEST_COMBINATION} MATCHES "mpi\\.1\\.core") - set(TEST_NPROCS 1) - else() - set(TEST_NPROCS 4) - endif() - if(${TEST_COMBINATION} MATCHES "\\.gpu") - set(TEST_LABELS "gpu") - else() - set(TEST_LABELS "") +function(CHECKPOINT_TEST) + cmake_parse_arguments(TEST "" "MODES;MAX_NUM_PROC;SUFFIX" "LABELS" ${ARGN}) + if(NOT DEFINED TEST_MAX_NUM_PROC) + set(TEST_MAX_NUM_PROC 4) endif() - if(${TEST_COMBINATION} MATCHES "lb\\.off") - set(TEST_BINARY_LIST "1") + if(TEST_SUFFIX) + set(TEST_ARGUMENTS "Test_suffix_${TEST_SUFFIX}__${TEST_MODES}") + set(TEST_SUFFIX "${TEST_MODES}_${TEST_SUFFIX}") else() - set(TEST_BINARY_LIST "1;0") + set(TEST_ARGUMENTS "Test__${TEST_MODES}") + set(TEST_SUFFIX "${TEST_MODES}") endif() - foreach(TEST_BINARY ${TEST_BINARY_LIST}) - python_test(FILE save_checkpoint.py MAX_NUM_PROC ${TEST_NPROCS} LABELS - ${TEST_LABELS} FILE_SUFFIX ${TEST_COMBINATION}_${TEST_BINARY}) - python_test( - FILE test_checkpoint.py MAX_NUM_PROC ${TEST_NPROCS} LABELS ${TEST_LABELS} - FILE_SUFFIX ${TEST_COMBINATION}_${TEST_BINARY} DEPENDS - save_checkpoint_${TEST_COMBINATION}_${TEST_BINARY}) - endforeach(TEST_BINARY) -endforeach(TEST_COMBINATION) + python_test( + FILE save_checkpoint.py MAX_NUM_PROC ${TEST_NPROCS} LABELS ${TEST_LABELS} + SUFFIX ${TEST_SUFFIX} ARGUMENTS "${TEST_ARGUMENTS}") + python_test( + FILE test_checkpoint.py MAX_NUM_PROC ${TEST_NPROCS} LABELS ${TEST_LABELS} + SUFFIX ${TEST_SUFFIX} ARGUMENTS "${TEST_ARGUMENTS}" DEPENDS + save_checkpoint_${TEST_SUFFIX}) +endfunction(CHECKPOINT_TEST) + +# Checkpoint tests run on 4 cores (can be overriden with MAX_NUM_PROC). The +# combination of modes to activate is stored in MODES. A mode consists of a +# feature with zero or more options; separate features with 2 underscores and +# options with 1 underscore (options can appear in any order). For example, +# "p3m_cpu__lb_cpu_ascii" generates modes P3M, P3M.CPU, LB, LB.CPU, LB.ASCII. +checkpoint_test(MODES therm_lb__p3m_cpu__lj__lb_cpu_ascii SUFFIX 1_core + MAX_NUM_PROC 1) +checkpoint_test(MODES therm_lb__p3m_cpu__lj__lb_cpu_ascii) +checkpoint_test(MODES therm_lb__elc_cpu__lj__lb_cpu_binary) +checkpoint_test(MODES therm_lb__elc_cpu__lj__lb_gpu_ascii LABELS gpu) +checkpoint_test(MODES therm_lb__p3m_gpu__lj__lb_gpu_binary LABELS gpu) +checkpoint_test(MODES therm_npt__int_npt) +checkpoint_test(MODES int_sd__lj) +checkpoint_test(MODES dp3m_cpu__therm_langevin__int_nvt) +checkpoint_test(MODES therm_dpd__int_nvt) +checkpoint_test(MODES scafacos__therm_bd__int_bd) +checkpoint_test(MODES therm_sdm__int_sdm) + python_test(FILE bond_breakage.py MAX_NUM_PROC 4) python_test(FILE cellsystem.py MAX_NUM_PROC 4) python_test(FILE tune_skin.py MAX_NUM_PROC 1) @@ -138,9 +144,12 @@ python_test(FILE lb_stokes_sphere.py MAX_NUM_PROC 4 LABELS gpu long) python_test(FILE lb_pressure_tensor.py MAX_NUM_PROC 1 LABELS gpu long) python_test(FILE ek_fluctuations.py MAX_NUM_PROC 1 LABELS gpu) python_test(FILE ek_charged_plate.py MAX_NUM_PROC 1 LABELS gpu) -python_test(FILE ek_eof_one_species.py MAX_NUM_PROC 1 LABELS gpu FILE_SUFFIX x) -python_test(FILE ek_eof_one_species.py MAX_NUM_PROC 1 LABELS gpu FILE_SUFFIX y) -python_test(FILE ek_eof_one_species.py MAX_NUM_PROC 1 LABELS gpu FILE_SUFFIX z) +python_test(FILE ek_eof_one_species.py MAX_NUM_PROC 1 LABELS gpu SUFFIX x + ARGUMENTS Test__axis_x) +python_test(FILE ek_eof_one_species.py MAX_NUM_PROC 1 LABELS gpu SUFFIX y + ARGUMENTS Test__axis_y) +python_test(FILE ek_eof_one_species.py MAX_NUM_PROC 1 LABELS gpu SUFFIX z + ARGUMENTS Test__axis_z) python_test(FILE exclusions.py MAX_NUM_PROC 2) python_test(FILE langevin_thermostat.py MAX_NUM_PROC 1) python_test(FILE langevin_thermostat_stats.py MAX_NUM_PROC 1 LABELS long) @@ -262,6 +271,9 @@ add_custom_target( COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/unittest_decorators.py ${CMAKE_CURRENT_BINARY_DIR} + COMMAND + ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/unittest_generator.py + ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/virtual_sites_tracers_common.py diff --git a/testsuite/python/ek_eof_one_species.py b/testsuite/python/ek_eof_one_species.py index 0c6c53e5619..0ff7de4025a 100644 --- a/testsuite/python/ek_eof_one_species.py +++ b/testsuite/python/ek_eof_one_species.py @@ -17,6 +17,7 @@ import unittest as ut import unittest_decorators as utx +import unittest_generator as utg import pathlib import sys @@ -36,6 +37,9 @@ import espressomd.shapes import ek_common +config = utg.TestGenerator() +modes = config.get_modes() + ########################################################################## # Set up the System # ########################################################################## @@ -63,7 +67,14 @@ params_base['density_counterions'] = -2.0 * \ params_base['sigma'] / params_base['width'] -axis = "@TEST_FILE_SUFFIX@" +if "AXIS.X" in modes: + axis = "x" +elif "AXIS.Y" in modes: + axis = "y" +else: + assert "AXIS.Z" in modes + axis = "z" + params = { "x": dict([ ('box_x', params_base['thickness']), @@ -459,4 +470,5 @@ def test_vtk(self): if __name__ == "__main__": + config.bind_test_class(ek_eof_one_species) ut.main() diff --git a/testsuite/python/save_checkpoint.py b/testsuite/python/save_checkpoint.py index 97862467dca..e9ac8140608 100644 --- a/testsuite/python/save_checkpoint.py +++ b/testsuite/python/save_checkpoint.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import unittest as ut +import unittest_generator as utg import numpy as np import os @@ -33,8 +34,8 @@ import espressomd.constraints import espressomd.bond_breakage -modes = {x for mode in set("@TEST_COMBINATION@".upper().split('-')) - for x in [mode, mode.split('.')[0]]} +config = utg.TestGenerator() +modes = config.get_modes() # use a box with 3 different dimensions, unless DipolarP3M is used system = espressomd.System(box_l=[12.0, 14.0, 16.0]) @@ -48,9 +49,8 @@ system.max_oif_objects = 5 # create checkpoint folder -idx = "mycheckpoint_@TEST_COMBINATION@_@TEST_BINARY@".replace(".", "__") checkpoint = espressomd.checkpointing.Checkpoint( - checkpoint_id=idx, checkpoint_path="@CMAKE_CURRENT_BINARY_DIR@") + **config.get_checkpoint_params()) # cleanup old checkpoint files if checkpoint.has_checkpoints(): @@ -60,14 +60,15 @@ n_nodes = system.cell_system.get_state()["n_nodes"] -LB_implementation = None +lbf_actor = None if 'LB.CPU' in modes: - LB_implementation = espressomd.lb.LBFluid + lbf_actor = espressomd.lb.LBFluid elif 'LB.GPU' in modes and espressomd.gpu_available(): - LB_implementation = espressomd.lb.LBFluidGPU -if LB_implementation: - lbf = LB_implementation(agrid=0.5, visc=1.3, dens=1.5, tau=0.01, - gamma_odd=0.2, gamma_even=0.3) + lbf_actor = espressomd.lb.LBFluidGPU +if lbf_actor: + lbf_cpt_mode = 0 if 'LB.ASCII' in modes else 1 + lbf = lbf_actor(agrid=0.5, visc=1.3, dens=1.5, tau=0.01, gamma_odd=0.2, + gamma_even=0.3) system.actors.add(lbf) if 'THERM.LB' in modes: system.thermostat.set_lb(LB_fluid=lbf, seed=23, gamma=2.0) @@ -96,8 +97,12 @@ p3 = system.part.add(id=3, pos=system.box_l / 2.0 - 1.0, type=1) p4 = system.part.add(id=4, pos=system.box_l / 2.0 + 1.0, type=1) -if espressomd.has_features('P3M') and 'P3M' in modes: - p3m = espressomd.electrostatics.P3M( +if espressomd.has_features('P3M') and ('P3M' in modes or 'ELC' in modes): + if espressomd.gpu_available() and 'P3M.GPU' in modes: + ActorP3M = espressomd.electrostatics.P3MGPU + else: + ActorP3M = espressomd.electrostatics.P3M + p3m = ActorP3M( prefactor=1.0, accuracy=0.1, mesh=10, @@ -106,9 +111,7 @@ r_cut=1.0, timings=15, tune=False) - if 'P3M.CPU' in modes: - system.actors.add(p3m) - elif 'P3M.ELC' in modes: + if 'ELC' in modes: elc = espressomd.electrostatics.ELC( p3m_actor=p3m, gap_size=6.0, @@ -116,6 +119,8 @@ delta_mid_top=0.9, delta_mid_bot=0.1) system.actors.add(elc) + else: + system.actors.add(p3m) # accumulators obs = espressomd.observables.ParticlePositions(ids=[0, 1]) @@ -164,7 +169,7 @@ system.constraints.add(espressomd.constraints.ElectricPlaneWave( E0=[1., -2., 3.], k=[-.1, .2, .3], omega=5., phi=1.4)) -if 'LB.OFF' in modes: +if 'LB' not in modes: # set thermostat if 'THERM.LANGEVIN' in modes: system.thermostat.set_langevin(kT=1.0, gamma=2.0, seed=42) @@ -296,7 +301,7 @@ "p2nfft_r_cut": "11", "p2nfft_alpha": "0.37"})) -if LB_implementation: +if lbf_actor: m = np.pi / 12 nx = int(np.round(system.box_l[0] / lbf.get_params()["agrid"])) ny = int(np.round(system.box_l[1] / lbf.get_params()["agrid"])) @@ -309,16 +314,15 @@ for j in range(ny): for k in range(nz): lbf[i, j, k].population = grid_3D[i, j, k] * np.arange(1, 20) - cpt_mode = int("@TEST_BINARY@") # save LB checkpoint file lbf_cpt_path = checkpoint.checkpoint_dir + "/lb.cpt" - lbf.save_checkpoint(lbf_cpt_path, cpt_mode) + lbf.save_checkpoint(lbf_cpt_path, lbf_cpt_mode) # save checkpoint file checkpoint.save(0) -class TestCheckpointLB(ut.TestCase): +class TestCheckpoint(ut.TestCase): def test_checkpointing(self): ''' @@ -331,12 +335,12 @@ def test_checkpointing(self): self.assertTrue(os.path.isfile(checkpoint_filepath), "checkpoint file not created") - if LB_implementation: + if lbf_actor: self.assertTrue(os.path.isfile(lbf_cpt_path), "LB checkpoint file not created") - self.check_lb_checkpointing() - def check_lb_checkpointing(self): + @ut.skipIf(lbf_actor is None, "Skipping test due to missing mode.") + def test_lb_checkpointing_exceptions(self): ''' Check the LB checkpointing exception mechanism. Write corrupted LB checkpoint files that will be tested in ``test_checkpoint.py``. @@ -346,10 +350,10 @@ def check_lb_checkpointing(self): with self.assertRaisesRegex(RuntimeError, 'could not open file'): dirname, filename = os.path.split(lbf_cpt_path) invalid_path = os.path.join(dirname, 'unknown_dir', filename) - lbf.save_checkpoint(invalid_path, cpt_mode) + lbf.save_checkpoint(invalid_path, lbf_cpt_mode) system.actors.remove(lbf) with self.assertRaisesRegex(RuntimeError, 'one needs to have already initialized the LB fluid'): - lbf.load_checkpoint(lbf_cpt_path, cpt_mode) + lbf.load_checkpoint(lbf_cpt_path, lbf_cpt_mode) # read the valid LB checkpoint file with open(lbf_cpt_path, "rb") as f: @@ -361,7 +365,7 @@ def check_lb_checkpointing(self): # write checkpoint file with extra data with open(cpt_path.format("-extra-data"), "wb") as f: f.write(lbf_cpt_str + lbf_cpt_str[-8:]) - if cpt_mode == 0: + if lbf_cpt_mode == 0: boxsize, data = lbf_cpt_str.split(b"\n", 1) # write checkpoint file with incorrectly formatted data with open(cpt_path.format("-wrong-format"), "wb") as f: @@ -372,4 +376,5 @@ def check_lb_checkpointing(self): if __name__ == '__main__': + config.bind_test_class(TestCheckpoint) ut.main() diff --git a/testsuite/python/test_checkpoint.py b/testsuite/python/test_checkpoint.py index 5eefa05cc53..4b9412dc280 100644 --- a/testsuite/python/test_checkpoint.py +++ b/testsuite/python/test_checkpoint.py @@ -18,6 +18,7 @@ # pylint: disable=undefined-variable import unittest as ut import unittest_decorators as utx +import unittest_generator as utg import numpy as np import espressomd @@ -30,18 +31,17 @@ import espressomd.shapes import espressomd.constraints -modes = {x for mode in set("@TEST_COMBINATION@".upper().split('-')) - for x in [mode, mode.split('.')[0]]} - -LB = ('LB.CPU' in modes or 'LB.GPU' in modes and espressomd.gpu_available()) +config = utg.TestGenerator() +is_gpu_available = espressomd.gpu_available() +modes = config.get_modes() +has_lb_mode = 'LB.CPU' in modes or 'LB.GPU' in modes and is_gpu_available +has_p3m_mode = 'P3M.CPU' in modes or 'P3M.GPU' in modes and is_gpu_available class CheckpointTest(ut.TestCase): checkpoint = espressomd.checkpointing.Checkpoint( - checkpoint_id="mycheckpoint_@TEST_COMBINATION@_@TEST_BINARY@".replace( - '.', '__'), - checkpoint_path="@CMAKE_CURRENT_BINARY_DIR@") + **config.get_checkpoint_params()) checkpoint.load(0) n_nodes = system.cell_system.get_state()["n_nodes"] @@ -62,7 +62,7 @@ def get_active_actor_of_type(self, actor_type): self.fail( f"system doesn't have an actor of type {actor_type.__name__}") - @ut.skipIf(not LB, "Skipping test due to missing mode.") + @ut.skipIf(not has_lb_mode, "Skipping test due to missing mode.") def test_lb_fluid(self): ''' Check serialization of the LB fluid. The checkpoint file only stores @@ -73,7 +73,7 @@ def test_lb_fluid(self): ''' lbf = self.get_active_actor_of_type( espressomd.lb.HydrodynamicInteraction) - cpt_mode = int("@TEST_BINARY@") + cpt_mode = 0 if 'LB.ASCII' in modes else 1 cpt_path = self.checkpoint.checkpoint_dir + "/lb{}.cpt" # check exception mechanism with corrupted LB checkpoint files @@ -423,10 +423,10 @@ def test_dp3m(self): err_msg=f'for parameter {key}') @utx.skipIfMissingFeatures('P3M') - @ut.skipIf('P3M.CPU' not in modes, - "Skipping test due to missing combination.") + @ut.skipIf(not has_p3m_mode, "Skipping test due to missing combination.") def test_p3m(self): - actor = self.get_active_actor_of_type(espressomd.electrostatics.P3M) + actor = self.get_active_actor_of_type( + espressomd.electrostatics.ElectrostaticInteraction) state = actor.get_params() reference = {'prefactor': 1.0, 'accuracy': 0.1, 'mesh': 3 * [10], 'cao': 1, 'alpha': 1.0, 'r_cut': 1.0, 'tune': False, @@ -437,8 +437,7 @@ def test_p3m(self): err_msg=f'for parameter {key}') @utx.skipIfMissingFeatures('P3M') - @ut.skipIf('P3M.ELC' not in modes, - "Skipping test due to missing combination.") + @ut.skipIf('ELC' not in modes, "Skipping test due to missing combination.") def test_elc(self): actor = self.get_active_actor_of_type(espressomd.electrostatics.ELC) elc_state = actor.get_params() @@ -510,8 +509,9 @@ def test_exclusions(self): self.assertEqual(list(system.part.by_id(1).exclusions), [2]) self.assertEqual(list(system.part.by_id(2).exclusions), [0, 1]) - @ut.skipIf(not LB or not (espressomd.has_features("LB_BOUNDARIES") - or espressomd.has_features("LB_BOUNDARIES_GPU")), "Missing features") + @ut.skipIf(not has_lb_mode or not (espressomd.has_features("LB_BOUNDARIES") + or espressomd.has_features("LB_BOUNDARIES_GPU")), + "Missing features") @ut.skipIf(n_nodes > 1, "only runs for 1 MPI rank") def test_lb_boundaries(self): # check boundary objects @@ -594,4 +594,5 @@ def test_constraints(self): if __name__ == '__main__': + config.bind_test_class(CheckpointTest) ut.main() diff --git a/testsuite/python/unittest_generator.py b/testsuite/python/unittest_generator.py new file mode 100644 index 00000000000..6e16257d772 --- /dev/null +++ b/testsuite/python/unittest_generator.py @@ -0,0 +1,144 @@ +# +# Copyright (C) 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 . +# + +import os +import sys +import inspect + + +class TestGenerator: + """ + Class factory to generate test classes on-the-fly. Parse the test name + from the command line arguments to create the corresponding test case. + + Use this class as follows:: + + import unittest as ut + import unittest_generator as utg + + config = utg.TestGenerator() + modes = config.get_modes() + + class Test(ut.TestCase): + def test_modes(self): + self.assertIn("P3M.CPU", modes) + + if __name__ == "__main__": + config.bind_test_class(Test) + ut.main() + + Invoke the test file with a test name of the form "Test_{suffix}__{modes}" + to dynamically bind to the ``Test`` class and populate the local variable + ``modes``. The suffix is optional. Example: + + .. code-block:: none + + $ python3 test.py Test__lb_cpu_ascii__p3m_cpu + . + ---------------------------------------------------------------------- + Ran 1 test in 0.000s + + OK + + To make the test fail, do: + + .. code-block:: none + + $ python3 test.py Test__lb_cpu_ascii__p3m_gpu + F + ====================================================================== + FAIL: test_modes (__main__.Test) + ---------------------------------------------------------------------- + AssertionError: 'P3M.CPU' not found in {'LB', 'LB.CPU', 'LB.ASCII', 'P3M', 'P3M.GPU'} + + ---------------------------------------------------------------------- + Ran 1 test in 0.000s + + FAILED (failures=1) + + Since the test name contains information that is required to run the test, + the test file can no longer be invoked without a test name as argument: + + .. code-block:: none + + $ python3 test.py + AssertionError: please provide a test name as argument, + like 'Test_lb_cpu__p3m_cpu' (got ['test.py']) + + At the CMake level, configure CTest to invoke the test file with a test + name as argument. An optional suffix can be added to disambiguate two + tests that take the same modes but run with e.g. a different number of + MPI ranks. This framework provides a solution to the Python ticket + `39283 `__. + + Although CMake provides a function ``configure_file()`` that substitutes + CMake variables into test files that are copied to the build folder, this + is only useful for variables that are known at build time. It also forces + us to copy the original test file multiple times with a different filepath + in the build folder, making it difficult for Python code coverage tools to + identify which file in the root folder matches the recorded coverage + information. + + """ + + def __init__(self): + self.main_module = inspect.getmodule(inspect.stack()[1][0]) + self.test_name = None + self.test_feat = None + prefix = 'Test_' + for arg in sys.argv: + if arg.startswith(prefix): + self.test_name = arg + self.test_feat = arg.split('__', 1)[1] + self.test_idx = arg.split('_', 1)[1].lstrip('_') + break + err_msg = f"please provide a test name as argument, like '{prefix}lb_cpu__p3m_cpu' (got {sys.argv})" + assert self.test_name is not None, err_msg + + def bind_test_class(self, base_class): + """ + Dynamically re-bind an existing test class in the main module + using the test name passed as command line argument. When running + the test file, the original test class name is displayed instead + of the new name. + """ + setattr(self.main_module, self.test_name, base_class) + + def get_modes(self): + """ + Generate the list of modes for the test name found in the command line. + A mode consists of a feature with zero or more options; separate + features with 2 underscores and options with 1 underscore (options + can appear in any order). For example, "p3m_cpu__lb_cpu_ascii" + generates modes P3M, P3M.CPU, LB, LB.CPU, LB.ASCII. + """ + modes = set() + for item in self.test_feat.upper().split('__'): + feature, *options = item.split('_') + for option in options: + modes.add(f"{feature}.{option}") + modes.add(feature) + return modes + + def get_checkpoint_params(self): + """ + Generate parameters to instantiate an ESPResSo checkpoint file. + """ + return {"checkpoint_id": f"checkpoint_{self.test_idx}", + "checkpoint_path": os.path.dirname(__file__)} From adcd8e11320810b2717c936baf07eafc868715d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 8 Mar 2022 14:05:25 +0100 Subject: [PATCH 78/99] CMake: Configure python test files with COPYONLY --- testsuite/python/CMakeLists.txt | 13 +++++++------ testsuite/python/sigint.py | 7 ++++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index d3ff5d73aa2..21171f022ec 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -6,9 +6,10 @@ function(PYTHON_TEST) if(TEST_SUFFIX) set(TEST_NAME "${TEST_NAME}_${TEST_SUFFIX}") endif() - configure_file(${TEST_FILE} ${TEST_FILE_CONFIGURED}) + configure_file(${TEST_FILE} ${TEST_FILE_CONFIGURED} COPYONLY) foreach(dependency IN LISTS TEST_DEPENDENCIES) - configure_file(${dependency} ${CMAKE_CURRENT_BINARY_DIR}/${dependency}) + configure_file(${dependency} ${CMAKE_CURRENT_BINARY_DIR}/${dependency} + COPYONLY) endforeach(dependency) set(TEST_FILE ${TEST_FILE_CONFIGURED}) @@ -29,11 +30,11 @@ function(PYTHON_TEST) COMMAND ${MPIEXEC} ${MPIEXEC_OVERSUBSCRIBE} ${MPIEXEC_NUMPROC_FLAG} ${TEST_NUM_PROC} ${MPIEXEC_PREFLAGS} ${MPIEXEC_TMPDIR} - ${CMAKE_BINARY_DIR}/pypresso ${PYPRESSO_OPTIONS} ${TEST_FILE} - ${TEST_ARGUMENTS} ${MPIEXEC_POSTFLAGS}) + ${CMAKE_BINARY_DIR}/pypresso ${PYPRESSO_OPTIONS} + ${TEST_FILE_CONFIGURED} ${TEST_ARGUMENTS} ${MPIEXEC_POSTFLAGS}) else() add_test(${TEST_NAME} ${CMAKE_BINARY_DIR}/pypresso ${PYPRESSO_OPTIONS} - ${TEST_FILE} ${TEST_ARGUMENTS}) + ${TEST_FILE_CONFIGURED} ${TEST_ARGUMENTS}) endif() set_tests_properties(${TEST_NAME} PROPERTIES PROCESSORS ${TEST_NUM_PROC} DEPENDS "${TEST_DEPENDS}") @@ -52,7 +53,7 @@ function(PYTHON_TEST) ${TEST_NAME} PROPERTIES LABELS "${TEST_LABELS};parallel;parallel_odd") endif() - set(python_tests ${python_tests} ${TEST_FILE} PARENT_SCOPE) + set(python_tests ${python_tests} ${TEST_FILE_CONFIGURED} PARENT_SCOPE) endfunction(PYTHON_TEST) function(CHECKPOINT_TEST) diff --git a/testsuite/python/sigint.py b/testsuite/python/sigint.py index b7fc0cd0d2c..1627fba0633 100644 --- a/testsuite/python/sigint.py +++ b/testsuite/python/sigint.py @@ -20,14 +20,15 @@ import signal import subprocess import time +import sys +import os class SigintTest(ut.TestCase): def setUp(self): - self.process = subprocess.Popen( - ['@CMAKE_BINARY_DIR@/pypresso', - '@CMAKE_CURRENT_BINARY_DIR@/sigint_child.py']) + script = os.path.join(os.path.dirname(__file__), 'sigint_child.py') + self.process = subprocess.Popen([sys.executable, script]) def test_signal_handling(self): self.process.send_signal(signal.SIGINT) From 33f750ccc5e5328cc7528c20c6cfa35c029ce69e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 8 Mar 2022 14:31:06 +0100 Subject: [PATCH 79/99] testsuite: Move utility functions to client code Move utility functions to test cases that use them, if they are used only once. --- testsuite/python/CMakeLists.txt | 2 - testsuite/python/ek_common.py | 74 ------ testsuite/python/ek_eof_one_species.py | 60 ++++- testsuite/python/interactions_bonded.py | 118 ++++++--- testsuite/python/interactions_non-bonded.py | 278 ++++++++++++++++++-- testsuite/python/pressure.py | 14 +- testsuite/python/tests_common.py | 277 ------------------- 7 files changed, 391 insertions(+), 432 deletions(-) delete mode 100644 testsuite/python/ek_common.py diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index 21171f022ec..d70df80dd52 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -279,8 +279,6 @@ add_custom_target( ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/virtual_sites_tracers_common.py ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/ek_common.py - ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/thermostats_common.py ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/testsuite/python/ek_common.py b/testsuite/python/ek_common.py deleted file mode 100644 index a030a9a215e..00000000000 --- a/testsuite/python/ek_common.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (C) 2010-2019 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 math - -# Some common functions used by the ek tests - -# root finding function - - -def solve(xi, d, bjerrum_length, sigma, valency): - el_char = 1.0 - return xi * math.tan(xi * d / 2.0) + 2.0 * math.pi * \ - bjerrum_length * sigma / (valency * el_char) - -# function to calculate the density - - -def density(x, xi, bjerrum_length): - return (xi * xi) / (2.0 * math.pi * bjerrum_length * - math.cos(xi * x) * math.cos(xi * x)) - -# function to calculate the velocity - - -def velocity( - x, - xi, - d, - bjerrum_length, - force, - viscosity_kinematic, - density_water): - return force * math.log(math.cos(xi * x) / math.cos(xi * d / 2.0)) / \ - (2.0 * math.pi * bjerrum_length * viscosity_kinematic * density_water) - -# function to calculate the nonzero component of the pressure tensor - - -def pressure_tensor_offdiagonal(x, xi, bjerrum_length, force): - return force * xi * math.tan(xi * x) / (2.0 * math.pi * bjerrum_length) - -# function to calculate the hydrostatic pressure - -# Technically, the LB simulates a compressible fluid, whose pressure -# tensor contains an additional term on the diagonal, proportional to -# the divergence of the velocity. We neglect this contribution, which -# creates a small error in the direction normal to the wall, which -# should decay with the simulation time. - - -def hydrostatic_pressure( - ek, - tensor_entry, - box_x, - box_y, - box_z, - agrid): - offset = ek[int(box_x / (2 * agrid)), int(box_y / (2 * agrid)), - int(box_z / (2 * agrid))].pressure_tensor[tensor_entry] - return 0.0 + offset diff --git a/testsuite/python/ek_eof_one_species.py b/testsuite/python/ek_eof_one_species.py index 0ff7de4025a..420b5d70ea6 100644 --- a/testsuite/python/ek_eof_one_species.py +++ b/testsuite/python/ek_eof_one_species.py @@ -35,11 +35,51 @@ import espressomd import espressomd.electrokinetics import espressomd.shapes -import ek_common config = utg.TestGenerator() modes = config.get_modes() + +########################################################################## +# Utility functions +########################################################################## + +def solve(xi, d, bjerrum_length, sigma, valency, el_char=1.0): + # root finding function + return xi * math.tan(xi * d / 2.0) + 2.0 * math.pi * \ + bjerrum_length * sigma / (valency * el_char) + + +def density(x, xi, bjerrum_length): + return (xi * xi) / (2.0 * math.pi * bjerrum_length * + math.cos(xi * x) * math.cos(xi * x)) + + +def velocity(x, xi, d, bjerrum_length, force, visc_kinematic, density_water): + return force * math.log(math.cos(xi * x) / math.cos(xi * d / 2.0)) / \ + (2.0 * math.pi * bjerrum_length * visc_kinematic * density_water) + + +def pressure_tensor_offdiagonal(x, xi, bjerrum_length, force): + # calculate the nonzero component of the pressure tensor + return force * xi * math.tan(xi * x) / (2.0 * math.pi * bjerrum_length) + + +def hydrostatic_pressure(ek, tensor_entry, box_x, box_y, box_z, agrid): + """ + Calculate the hydrostatic pressure. + + Technically, the LB simulates a compressible fluid, whose pressure + tensor contains an additional term on the diagonal, proportional to + the divergence of the velocity. We neglect this contribution, which + creates a small error in the direction normal to the wall, which + should decay with the simulation time. + """ + offset = ek[int(box_x / (2 * agrid)), int(box_y / (2 * agrid)), + int(box_z / (2 * agrid))].pressure_tensor[tensor_entry] + return 0.0 + offset + + ########################################################################## # Set up the System # ########################################################################## @@ -128,19 +168,19 @@ def bisection(): # the bisection scheme tol = 1.0e-08 while size > tol: - val0 = ek_common.solve( + val0 = solve( pnt0, params_base['width'], params_base['bjerrum_length'], params_base['sigma'], params_base['valency']) - val1 = ek_common.solve( + val1 = solve( pnt1, params_base['width'], params_base['bjerrum_length'], params_base['sigma'], params_base['valency']) - valm = ek_common.solve( + valm = solve( pntm, params_base['width'], params_base['bjerrum_length'], @@ -265,7 +305,7 @@ def test(self): (2 * params_base['agrid'])), i]) index = np.roll(index, params['n_roll_index']) measured_density = counterions[index].density - calculated_density = ek_common.density( + calculated_density = density( position, self.xi, params_base['bjerrum_length']) density_difference = abs(measured_density - calculated_density) total_density_difference += density_difference @@ -273,7 +313,7 @@ def test(self): # velocity measured_velocity = ek[index].velocity[int( np.nonzero(params['ext_force_density'])[0])] - calculated_velocity = ek_common.velocity( + calculated_velocity = velocity( position, self.xi, params_base['width'], @@ -288,7 +328,7 @@ def test(self): # diagonal pressure tensor measured_pressure_xx = ek[index].pressure_tensor[(0, 0)] - calculated_pressure_xx = ek_common.hydrostatic_pressure( + calculated_pressure_xx = hydrostatic_pressure( ek, (0, 0), system.box_l[params['periodic_dirs'][0]], @@ -296,7 +336,7 @@ def test(self): params['box_z'], params_base['agrid']) measured_pressure_yy = ek[index].pressure_tensor[(1, 1)] - calculated_pressure_yy = ek_common.hydrostatic_pressure( + calculated_pressure_yy = hydrostatic_pressure( ek, (1, 1), system.box_l[params['periodic_dirs'][0]], @@ -304,7 +344,7 @@ def test(self): params['box_z'], params_base['agrid']) measured_pressure_zz = ek[index].pressure_tensor[(2, 2)] - calculated_pressure_zz = ek_common.hydrostatic_pressure( + calculated_pressure_zz = hydrostatic_pressure( ek, (2, 2), system.box_l[params['periodic_dirs'][0]], @@ -326,7 +366,7 @@ def test(self): total_pressure_difference_zz = total_pressure_difference_zz + \ pressure_difference_zz - calculated_pressure_offdiagonal = ek_common.pressure_tensor_offdiagonal( + calculated_pressure_offdiagonal = pressure_tensor_offdiagonal( position, self.xi, params_base['bjerrum_length'], params_base['force']) # xy component pressure tensor measured_pressure_xy = ek[index].pressure_tensor[(0, 1)] diff --git a/testsuite/python/interactions_bonded.py b/testsuite/python/interactions_bonded.py index c9aac3aa6ee..a681a9617d3 100644 --- a/testsuite/python/interactions_bonded.py +++ b/testsuite/python/interactions_bonded.py @@ -23,8 +23,54 @@ import espressomd import espressomd.electrostatics import espressomd.interactions -import tests_common +# +# Analytical expressions for interactions +# + + +def harmonic_potential(scalar_r, k, r_0): + return 0.5 * k * (scalar_r - r_0)**2 + + +def harmonic_force(scalar_r, k, r_0): + return -k * (scalar_r - r_0) + + +def fene_potential(scalar_r, k, d_r_max, r_0): + return -0.5 * k * d_r_max**2 * np.log(1 - ((scalar_r - r_0) / d_r_max)**2) + + +def fene_force(scalar_r, k, d_r_max, r_0): + return k * (scalar_r - r_0) * d_r_max**2 / \ + ((scalar_r - r_0)**2 - d_r_max**2) + + +def coulomb_potential(scalar_r, k, q1, q2): + return k * q1 * q2 / scalar_r + + +def coulomb_force(scalar_r, k, q1, q2): + return k * q1 * q2 / scalar_r**2 + + +def quartic_force(k0, k1, r, r_cut, scalar_r): + force = 0. + if scalar_r <= r_cut: + force = - k0 * (scalar_r - r) - k1 * (scalar_r - r)**3 + return force + + +def quartic_potential(k0, k1, r, r_cut, scalar_r): + energy = 0. + if scalar_r <= r_cut: + energy = 0.5 * k0 * (scalar_r - r)**2 + 0.25 * k1 * (scalar_r - r)**4 + return energy + + +# +# Test case +# class InteractionsBondedTest(ut.TestCase): system = espressomd.System(box_l=[17.0, 9.0, 8.0]) @@ -52,33 +98,27 @@ def tearDown(self): # Test Harmonic Bond def test_harmonic(self): - hb_k = 5 - hb_r_0 = 1.5 - hb_r_cut = 3.355 + k = 5. + r_0 = 1.5 + r_cut = 3.355 - hb = espressomd.interactions.HarmonicBond( - k=hb_k, r_0=hb_r_0, r_cut=hb_r_cut) + hb = espressomd.interactions.HarmonicBond(k=k, r_0=r_0, r_cut=r_cut) self.run_test(hb, - lambda r: tests_common.harmonic_force( - scalar_r=r, k=hb_k, r_0=hb_r_0), - lambda r: tests_common.harmonic_potential( - scalar_r=r, k=hb_k, r_0=hb_r_0), - 0.01, hb_r_cut, True) + lambda r: harmonic_force(r, k, r_0), + lambda r: harmonic_potential(r, k, r_0), + 0.01, r_cut, True) # Test Fene Bond def test_fene(self): - fene_k = 23.15 - fene_d_r_max = 3.355 - fene_r_0 = 1.1 + k = 23.15 + d_r_max = 3.355 + r_0 = 1.1 - fene = espressomd.interactions.FeneBond( - k=fene_k, d_r_max=fene_d_r_max, r_0=fene_r_0) + fene = espressomd.interactions.FeneBond(k=k, d_r_max=d_r_max, r_0=r_0) self.run_test(fene, - lambda r: tests_common.fene_force( - scalar_r=r, k=fene_k, d_r_max=fene_d_r_max, r_0=fene_r_0), - lambda r: tests_common.fene_potential( - scalar_r=r, k=fene_k, d_r_max=fene_d_r_max, r_0=fene_r_0), - 0.01, fene_r_0 + fene_d_r_max, True) + lambda r: fene_force(r, k, d_r_max, r_0), + lambda r: fene_potential(r, k, d_r_max, r_0=r_0), + 0.01, r_0 + d_r_max, True) def test_virtual_bond(self): # add sentinel harmonic bond, otherwise short-range loop is skipped @@ -109,16 +149,16 @@ def test_rigid_bond(self): @utx.skipIfMissingFeatures(["ELECTROSTATICS"]) def test_coulomb(self): - coulomb_k = 1 - q1 = 1 - q2 = -1 + k = 1. + q1 = 1. + q2 = -1. p1, p2 = self.system.part.all() p1.q = q1 p2.q = q2 self.run_test( - espressomd.interactions.BondedCoulomb(prefactor=coulomb_k), - lambda r: tests_common.coulomb_force(r, coulomb_k, q1, q2), - lambda r: tests_common.coulomb_potential(r, coulomb_k, q1, q2), + espressomd.interactions.BondedCoulomb(prefactor=k), + lambda r: coulomb_force(r, k, q1, q2), + lambda r: coulomb_potential(r, k, q1, q2), 0.01, self.system.box_l[0] / 3) @utx.skipIfMissingFeatures(["ELECTROSTATICS"]) @@ -152,22 +192,20 @@ def test_quartic(self): """Tests the Quartic bonded interaction by comparing the potential and force against the analytic values""" - quartic_k0 = 2. - quartic_k1 = 5. - quartic_r = 0.5 - quartic_r_cut = self.system.box_l[0] / 3. + k0 = 2. + k1 = 5. + q_r = 0.5 + r_cut = self.system.box_l[0] / 3. - quartic = espressomd.interactions.QuarticBond(k0=quartic_k0, - k1=quartic_k1, - r=quartic_r, - r_cut=quartic_r_cut) + quartic = espressomd.interactions.QuarticBond(k0=k0, + k1=k1, + r=q_r, + r_cut=r_cut) self.run_test(quartic, - lambda r: tests_common.quartic_force( - k0=quartic_k0, k1=quartic_k1, r=quartic_r, r_cut=quartic_r_cut, scalar_r=r), - lambda r: tests_common.quartic_potential( - k0=quartic_k0, k1=quartic_k1, r=quartic_r, r_cut=quartic_r_cut, scalar_r=r), - 0.01, quartic_r_cut, True) + lambda r: quartic_force(k0, k1, q_r, r_cut, r), + lambda r: quartic_potential(k0, k1, q_r, r_cut, r), + 0.01, r_cut, True) def run_test(self, bond_instance, force_func, energy_func, min_dist, cutoff, test_breakage=False): diff --git a/testsuite/python/interactions_non-bonded.py b/testsuite/python/interactions_non-bonded.py index ebea83d6c0b..a564fab0a40 100644 --- a/testsuite/python/interactions_non-bonded.py +++ b/testsuite/python/interactions_non-bonded.py @@ -23,6 +23,240 @@ import tests_common +# +# Analytical expressions for interactions +# + + +# Lennard-Jones Cosine + + +def lj_cos_potential(r, epsilon, sigma, cutoff, offset): + V = 0. + r_min = offset + np.power(2., 1. / 6.) * sigma + r_cut = cutoff + offset + if r < r_min: + V = tests_common.lj_potential(r, epsilon=epsilon, sigma=sigma, + cutoff=cutoff, offset=offset, shift=0.) + elif r < r_cut: + alpha = np.pi / \ + (np.power(r_cut - offset, 2) - np.power(r_min - offset, 2)) + beta = np.pi - np.power(r_min - offset, 2) * alpha + V = 0.5 * epsilon * \ + (np.cos(alpha * np.power(r - offset, 2) + beta) - 1.) + return V + + +def lj_cos_force(espressomd, r, epsilon, sigma, cutoff, offset): + f = 0. + r_min = offset + np.power(2., 1. / 6.) * sigma + r_cut = cutoff + offset + if r < r_min: + f = tests_common.lj_force(espressomd, r, epsilon=epsilon, sigma=sigma, + cutoff=cutoff, offset=offset) + elif r < r_cut: + alpha = np.pi / \ + (np.power(r_cut - offset, 2) - np.power(r_min - offset, 2)) + beta = np.pi - np.power(r_min - offset, 2) * alpha + f = (r - offset) * alpha * epsilon * \ + np.sin(alpha * np.power(r - offset, 2) + beta) + return f + +# Lennard-Jones Cosine^2 + + +def lj_cos2_potential(r, epsilon, sigma, offset, width): + V = 0. + r_min = offset + np.power(2., 1. / 6.) * sigma + r_cut = r_min + width + if r < r_min: + V = tests_common.lj_potential(r, epsilon=epsilon, sigma=sigma, + offset=offset, cutoff=r_cut, shift=0.) + elif r < r_cut: + V = -epsilon * np.power(np.cos(np.pi / + (2. * width) * (r - r_min)), 2) + return V + + +def lj_cos2_force(espressomd, r, epsilon, sigma, offset, width): + f = 0. + r_min = offset + np.power(2., 1. / 6.) * sigma + r_cut = r_min + width + if r < r_min: + f = tests_common.lj_force(espressomd, r, epsilon=epsilon, sigma=sigma, + cutoff=r_cut, offset=offset) + elif r < r_cut: + f = - np.pi * epsilon * \ + np.sin(np.pi * (r - r_min) / width) / (2. * width) + return f + +# Smooth-Step + + +def smooth_step_potential(r, eps, sig, cutoff, d, n, k0): + V = 0. + if r < cutoff: + V = np.power(d / r, n) + eps / \ + (1 + np.exp(2 * k0 * (r - sig))) + return V + + +def smooth_step_force(r, eps, sig, cutoff, d, n, k0): + f = 0. + if r < cutoff: + f = n * d / r**2 * np.power(d / r, n - 1) + 2 * k0 * eps * np.exp( + 2 * k0 * (r - sig)) / (1 + np.exp(2 * k0 * (r - sig))**2) + return f + +# BMHTF + + +def bmhtf_potential(r, a, b, c, d, sig, cutoff): + V = 0. + if r == cutoff: + V = a * np.exp(b * (sig - r)) - c * np.power( + r, -6) - d * np.power(r, -8) + if r < cutoff: + V = a * np.exp(b * (sig - r)) - c * np.power( + r, -6) - d * np.power(r, -8) + V -= bmhtf_potential(cutoff, a, b, c, d, sig, cutoff) + return V + + +def bmhtf_force(r, a, b, c, d, sig, cutoff): + f = 0. + if r < cutoff: + f = a * b * np.exp(b * (sig - r)) - 6 * c * np.power( + r, -7) - 8 * d * np.power(r, -9) + return f + +# Morse + + +def morse_potential(r, eps, alpha, cutoff, rmin=0): + V = 0. + if r < cutoff: + V = eps * (np.exp(-2. * alpha * (r - rmin)) - + 2 * np.exp(-alpha * (r - rmin))) + V -= eps * (np.exp(-2. * alpha * (cutoff - rmin)) - + 2 * np.exp(-alpha * (cutoff - rmin))) + return V + + +def morse_force(r, eps, alpha, cutoff, rmin=0): + f = 0. + if r < cutoff: + f = 2. * np.exp((rmin - r) * alpha) * \ + (np.exp((rmin - r) * alpha) - 1) * alpha * eps + return f + +# Buckingham + + +def buckingham_potential(r, a, b, c, d, cutoff, discont, shift): + V = 0. + if r < discont: + m = - buckingham_force( + discont, a, b, c, d, cutoff, discont, shift) + c = buckingham_potential( + discont, a, b, c, d, cutoff, discont, shift) - m * discont + V = m * r + c + if (r >= discont) and (r < cutoff): + V = a * np.exp(- b * r) - c * np.power( + r, -6) - d * np.power(r, -4) + shift + return V + + +def buckingham_force(r, a, b, c, d, cutoff, discont, shift): + f = 0. + if r < discont: + f = buckingham_force( + discont, a, b, c, d, cutoff, discont, shift) + if (r >= discont) and (r < cutoff): + f = a * b * np.exp(- b * r) - 6 * c * np.power( + r, -7) - 4 * d * np.power(r, -5) + return f + +# Soft-sphere + + +def soft_sphere_potential(r, a, n, cutoff, offset=0): + V = 0. + if r < offset + cutoff: + V = a * np.power(r - offset, -n) + return V + + +def soft_sphere_force(r, a, n, cutoff, offset=0): + f = 0. + if (r > offset) and (r < offset + cutoff): + f = n * a * np.power(r - offset, -(n + 1)) + return f + +# Hertzian + + +def hertzian_potential(r, eps, sig): + V = 0. + if r < sig: + V = eps * np.power(1 - r / sig, 5. / 2.) + return V + + +def hertzian_force(r, eps, sig): + f = 0. + if r < sig: + f = 5. / 2. * eps / sig * np.power(1 - r / sig, 3. / 2.) + return f + +# Gaussian + + +def gaussian_potential(r, eps, sig, cutoff): + V = 0. + if r < cutoff: + V = eps * np.exp(-np.power(r / sig, 2) / 2) + return V + + +def gaussian_force(r, eps, sig, cutoff): + f = 0. + if r < cutoff: + f = eps * r / sig**2 * np.exp(-np.power(r / sig, 2) / 2) + return f + + +def gay_berne_potential(r_ij, u_i, u_j, epsilon_0, sigma_0, mu, nu, k_1, k_2): + r_normed = r_ij / np.linalg.norm(r_ij) + r_u_i = np.dot(r_normed, u_i) + r_u_j = np.dot(r_normed, u_j) + u_i_u_j = np.dot(u_i, u_j) + + chi = (k_1**2 - 1.) / (k_1**2 + 1.) + chi_d = (k_2**(1. / mu) - 1) / (k_2**(1. / mu) + 1) + + sigma = sigma_0 \ + / np.sqrt( + (1 - 0.5 * chi * ( + (r_u_i + r_u_j)**2 / (1 + chi * u_i_u_j) + + (r_u_i - r_u_j)**2 / (1 - chi * u_i_u_j)))) + + epsilon = epsilon_0 *\ + (1 - chi**2 * u_i_u_j**2)**(-nu / 2.) *\ + (1 - chi_d / 2. * ( + (r_u_i + r_u_j)**2 / (1 + chi_d * u_i_u_j) + + (r_u_i - r_u_j)**2 / (1 - chi_d * u_i_u_j)))**mu + + rr = np.linalg.norm((np.linalg.norm(r_ij) - sigma + sigma_0) / sigma_0) + + return 4. * epsilon * (rr**-12 - rr**-6) + + +# +# Test case +# + + class InteractionsNonBondedTest(ut.TestCase): system = espressomd.System(box_l=3 * [10.]) system.cell_system.skin = 0. @@ -53,10 +287,6 @@ def assertItemsFractionAlmostEqual(self, a, b): for i, ai in enumerate(a): self.assertFractionAlmostEqual(ai, b[i]) - # - # Tests - # - # Test Generic Lennard-Jones Potential @utx.skipIfMissingFeatures("LENNARD_JONES_GENERIC") def test_lj_generic(self): @@ -139,8 +369,8 @@ def test_lj_cos(self): "sigma": 0.73, "cutoff": 1.523, "offset": 0.223}, - force_kernel=tests_common.lj_cos_force, - energy_kernel=tests_common.lj_cos_potential, + force_kernel=lj_cos_force, + energy_kernel=lj_cos_potential, n_steps=175, force_kernel_needs_espressomd=True) @@ -153,8 +383,8 @@ def test_lj_cos2(self): "sigma": 0.73, "width": 1.523, "offset": 0.321}, - force_kernel=tests_common.lj_cos2_force, - energy_kernel=tests_common.lj_cos2_potential, + force_kernel=lj_cos2_force, + energy_kernel=lj_cos2_potential, n_steps=267, force_kernel_needs_espressomd=True) @@ -169,8 +399,8 @@ def test_smooth_step(self): "d": 2.52, "n": 11, "k0": 2.13}, - force_kernel=tests_common.smooth_step_force, - energy_kernel=tests_common.smooth_step_potential, + force_kernel=smooth_step_force, + energy_kernel=smooth_step_potential, n_steps=126) # Test BMHTF Potential @@ -184,8 +414,8 @@ def test_bmhtf(self): "d": 3.33, "sig": 0.123, "cutoff": 1.253}, - force_kernel=tests_common.bmhtf_force, - energy_kernel=tests_common.bmhtf_potential, + force_kernel=bmhtf_force, + energy_kernel=bmhtf_potential, n_steps=126) # Test Morse Potential @@ -197,8 +427,8 @@ def test_morse(self): "alpha": 3.03, "rmin": 0.123, "cutoff": 1.253}, - force_kernel=tests_common.morse_force, - energy_kernel=tests_common.morse_potential, + force_kernel=morse_force, + energy_kernel=morse_potential, n_steps=126) # Test Buckingham Potential @@ -213,8 +443,8 @@ def test_buckingham(self): "discont": 1.03, "cutoff": 2.253, "shift": 0.133}, - force_kernel=tests_common.buckingham_force, - energy_kernel=tests_common.buckingham_potential, + force_kernel=buckingham_force, + energy_kernel=buckingham_potential, n_steps=226, force_kernel_remove_shift=False) @@ -227,8 +457,8 @@ def test_soft_sphere(self): "n": 3.03, "cutoff": 1.123, "offset": 0.123}, - force_kernel=tests_common.soft_sphere_force, - energy_kernel=tests_common.soft_sphere_potential, + force_kernel=soft_sphere_force, + energy_kernel=soft_sphere_potential, n_steps=113, n_initial_steps=12) @@ -239,8 +469,8 @@ def test_hertzian(self): self.run_test("hertzian", {"eps": 6.92, "sig": 2.432}, - force_kernel=tests_common.hertzian_force, - energy_kernel=tests_common.hertzian_potential, + force_kernel=hertzian_force, + energy_kernel=hertzian_potential, n_steps=244) # Test Gaussian Potential @@ -251,8 +481,8 @@ def test_gaussian(self): {"eps": 6.92, "sig": 4.03, "cutoff": 1.243}, - force_kernel=tests_common.gaussian_force, - energy_kernel=tests_common.gaussian_potential, + force_kernel=gaussian_force, + energy_kernel=gaussian_potential, n_steps=125) # Test the Gay-Berne potential and the resulting force and torque @@ -310,10 +540,10 @@ def get_reference_energy(gb_params, r, director1, director2): k_1, k_2, mu, nu, sigma_0, epsilon_0, cut = gb_params r_cut = r * cut / np.linalg.norm(r) - E_ref = tests_common.gay_berne_potential( + E_ref = gay_berne_potential( r, director1, director2, epsilon_0, sigma_0, mu, nu, k_1, k_2) - E_ref -= tests_common.gay_berne_potential( + E_ref -= gay_berne_potential( r_cut, director1, director2, epsilon_0, sigma_0, mu, nu, k_1, k_2) return E_ref diff --git a/testsuite/python/pressure.py b/testsuite/python/pressure.py index 5a8bc3c2e35..57889e92226 100644 --- a/testsuite/python/pressure.py +++ b/testsuite/python/pressure.py @@ -22,14 +22,18 @@ import espressomd.interactions import espressomd.observables -import tests_common - import numpy as np # allowed deviation from analytical results tol = 1.0e-13 +def fene_force(bond_vector, k, d_r_max, r_0): + r = np.linalg.norm(bond_vector) + return k * (r - r_0) / (r * (1 - ((r - r_0) / d_r_max)**2)) * \ + np.array(bond_vector) + + def pressure_tensor_kinetic(vel): '''Analytical result for convective pressure tensor''' return np.einsum('ij,ik->jk', vel, vel) / system.volume() @@ -260,10 +264,10 @@ class PressureFENE(ut.TestCase): def tearDown(self): system.part.clear() - def get_anal_pressure_tensor_fene(self, pos_1, pos_2, k, d_r_max, r_0): + def get_analytic_pressure_tensor_fene(self, pos_1, pos_2, k, d_r_max, r_0): tensor = np.zeros([3, 3]) vec_r = pos_1 - pos_2 - f = -tests_common.fene_force2(vec_r, k, d_r_max, r_0) + f = -fene_force(vec_r, k, d_r_max, r_0) tensor += np.einsum('i,j', f, vec_r) / system.volume() return tensor @@ -302,7 +306,7 @@ def test_fene(self): for i in range(len(system.bonded_inter)): total_bonded_pressure_tensor += sim_pressure_tensor['bonded', i] - anal_pressure_tensor_fene = self.get_anal_pressure_tensor_fene( + anal_pressure_tensor_fene = self.get_analytic_pressure_tensor_fene( p0.pos, p1.pos, k, d_r_max, r_0) np.testing.assert_allclose( sim_pressure_tensor_bonded, anal_pressure_tensor_fene, atol=tol, diff --git a/testsuite/python/tests_common.py b/testsuite/python/tests_common.py index 6285c8b5221..2ebd058f131 100644 --- a/testsuite/python/tests_common.py +++ b/testsuite/python/tests_common.py @@ -277,60 +277,6 @@ def get_histogram(pos, obs_params, coord_system, **kwargs): raise ValueError(f"Unknown coord system '{coord_system}'") return np.histogramdd(pos, bins=bins, range=extent, **kwargs) -# -# Analytical Expressions for interactions -# - -# Harmonic bond - - -def harmonic_potential(scalar_r, k, r_0): - return 0.5 * k * (scalar_r - r_0)**2 - - -def harmonic_force(scalar_r, k, r_0): - return -k * (scalar_r - r_0) - -# FENE bond - - -def fene_potential(scalar_r, k, d_r_max, r_0): - return -0.5 * k * d_r_max**2 * np.log(1 - ((scalar_r - r_0) / d_r_max)**2) - - -def fene_force(scalar_r, k, d_r_max, r_0): - return k * (scalar_r - r_0) * d_r_max**2 / \ - ((scalar_r - r_0)**2 - d_r_max**2) - - -def fene_force2(bond_vector, k, d_r_max, r_0): - r = np.linalg.norm(bond_vector) - return k * (r - r_0) / (r * (1 - ((r - r_0) / d_r_max)**2)) * \ - np.array(bond_vector) - -# Coulomb bond - - -def coulomb_potential(scalar_r, k, q1, q2): - return k * q1 * q2 / scalar_r - - -def coulomb_force(scalar_r, k, q1, q2): - return k * q1 * q2 / scalar_r**2 - -# QUARTIC bond - - -def quartic_force(k0, k1, r, r_cut, scalar_r): - if scalar_r > r_cut: - return 0.0 - return - k0 * (scalar_r - r) - k1 * (scalar_r - r)**3 - - -def quartic_potential(k0, k1, r, r_cut, scalar_r): - if scalar_r > r_cut: - return 0.0 - return 0.5 * k0 * (scalar_r - r)**2 + 0.25 * k1 * (scalar_r - r)**4 # Generic Lennard-Jones @@ -376,229 +322,6 @@ def lj_force(espressomd, r, epsilon, sigma, cutoff, offset=0.): espressomd, r, epsilon, sigma, cutoff, offset=offset, generic=False) return f -# Lennard-Jones Cosine - - -def lj_cos_potential(r, epsilon, sigma, cutoff, offset): - V = 0. - r_min = offset + np.power(2., 1. / 6.) * sigma - r_cut = cutoff + offset - if r < r_min: - V = lj_potential(r, epsilon=epsilon, sigma=sigma, - cutoff=cutoff, offset=offset, shift=0.) - elif r < r_cut: - alpha = np.pi / \ - (np.power(r_cut - offset, 2) - np.power(r_min - offset, 2)) - beta = np.pi - np.power(r_min - offset, 2) * alpha - V = 0.5 * epsilon * \ - (np.cos(alpha * np.power(r - offset, 2) + beta) - 1.) - return V - - -def lj_cos_force(espressomd, r, epsilon, sigma, cutoff, offset): - f = 0. - r_min = offset + np.power(2., 1. / 6.) * sigma - r_cut = cutoff + offset - if r < r_min: - f = lj_force(espressomd, r, epsilon=epsilon, sigma=sigma, - cutoff=cutoff, offset=offset) - elif r < r_cut: - alpha = np.pi / \ - (np.power(r_cut - offset, 2) - np.power(r_min - offset, 2)) - beta = np.pi - np.power(r_min - offset, 2) * alpha - f = (r - offset) * alpha * epsilon * \ - np.sin(alpha * np.power(r - offset, 2) + beta) - return f - -# Lennard-Jones Cosine^2 - - -def lj_cos2_potential(r, epsilon, sigma, offset, width): - V = 0. - r_min = offset + np.power(2., 1. / 6.) * sigma - r_cut = r_min + width - if r < r_min: - V = lj_potential(r, epsilon=epsilon, sigma=sigma, - offset=offset, cutoff=r_cut, shift=0.) - elif r < r_cut: - V = -epsilon * np.power(np.cos(np.pi / - (2. * width) * (r - r_min)), 2) - return V - - -def lj_cos2_force(espressomd, r, epsilon, sigma, offset, width): - f = 0. - r_min = offset + np.power(2., 1. / 6.) * sigma - r_cut = r_min + width - if r < r_min: - f = lj_force(espressomd, r, epsilon=epsilon, - sigma=sigma, cutoff=r_cut, offset=offset) - elif r < r_cut: - f = - np.pi * epsilon * \ - np.sin(np.pi * (r - r_min) / width) / (2. * width) - return f - -# Smooth-Step - - -def smooth_step_potential(r, eps, sig, cutoff, d, n, k0): - V = 0. - if r < cutoff: - V = np.power(d / r, n) + eps / \ - (1 + np.exp(2 * k0 * (r - sig))) - return V - - -def smooth_step_force(r, eps, sig, cutoff, d, n, k0): - f = 0. - if r < cutoff: - f = n * d / r**2 * np.power(d / r, n - 1) + 2 * k0 * eps * np.exp( - 2 * k0 * (r - sig)) / (1 + np.exp(2 * k0 * (r - sig))**2) - return f - -# BMHTF - - -def bmhtf_potential(r, a, b, c, d, sig, cutoff): - V = 0. - if r == cutoff: - V = a * np.exp(b * (sig - r)) - c * np.power( - r, -6) - d * np.power(r, -8) - if r < cutoff: - V = a * np.exp(b * (sig - r)) - c * np.power( - r, -6) - d * np.power(r, -8) - V -= bmhtf_potential(cutoff, a, b, c, d, sig, cutoff) - return V - - -def bmhtf_force(r, a, b, c, d, sig, cutoff): - f = 0. - if r < cutoff: - f = a * b * np.exp(b * (sig - r)) - 6 * c * np.power( - r, -7) - 8 * d * np.power(r, -9) - return f - -# Morse - - -def morse_potential(r, eps, alpha, cutoff, rmin=0): - V = 0. - if r < cutoff: - V = eps * (np.exp(-2. * alpha * (r - rmin)) - - 2 * np.exp(-alpha * (r - rmin))) - V -= eps * (np.exp(-2. * alpha * (cutoff - rmin)) - - 2 * np.exp(-alpha * (cutoff - rmin))) - return V - - -def morse_force(r, eps, alpha, cutoff, rmin=0): - f = 0. - if r < cutoff: - f = 2. * np.exp((rmin - r) * alpha) * \ - (np.exp((rmin - r) * alpha) - 1) * alpha * eps - return f - -# Buckingham - - -def buckingham_potential(r, a, b, c, d, cutoff, discont, shift): - V = 0. - if r < discont: - m = - buckingham_force( - discont, a, b, c, d, cutoff, discont, shift) - c = buckingham_potential( - discont, a, b, c, d, cutoff, discont, shift) - m * discont - V = m * r + c - if (r >= discont) and (r < cutoff): - V = a * np.exp(- b * r) - c * np.power( - r, -6) - d * np.power(r, -4) + shift - return V - - -def buckingham_force(r, a, b, c, d, cutoff, discont, shift): - f = 0. - if r < discont: - f = buckingham_force( - discont, a, b, c, d, cutoff, discont, shift) - if (r >= discont) and (r < cutoff): - f = a * b * np.exp(- b * r) - 6 * c * np.power( - r, -7) - 4 * d * np.power(r, -5) - return f - -# Soft-sphere - - -def soft_sphere_potential(r, a, n, cutoff, offset=0): - V = 0. - if r < offset + cutoff: - V = a * np.power(r - offset, -n) - return V - - -def soft_sphere_force(r, a, n, cutoff, offset=0): - f = 0. - if (r > offset) and (r < offset + cutoff): - f = n * a * np.power(r - offset, -(n + 1)) - return f - -# Hertzian - - -def hertzian_potential(r, eps, sig): - V = 0. - if r < sig: - V = eps * np.power(1 - r / sig, 5. / 2.) - return V - - -def hertzian_force(r, eps, sig): - f = 0. - if r < sig: - f = 5. / 2. * eps / sig * np.power(1 - r / sig, 3. / 2.) - return f - -# Gaussian - - -def gaussian_potential(r, eps, sig, cutoff): - V = 0. - if r < cutoff: - V = eps * np.exp(-np.power(r / sig, 2) / 2) - return V - - -def gaussian_force(r, eps, sig, cutoff): - f = 0. - if r < cutoff: - f = eps * r / sig**2 * np.exp(-np.power(r / sig, 2) / 2) - return f - - -def gay_berne_potential(r_ij, u_i, u_j, epsilon_0, sigma_0, mu, nu, k_1, k_2): - r_normed = r_ij / np.linalg.norm(r_ij) - r_u_i = np.dot(r_normed, u_i) - r_u_j = np.dot(r_normed, u_j) - u_i_u_j = np.dot(u_i, u_j) - - chi = (k_1**2 - 1.) / (k_1**2 + 1.) - chi_d = (k_2**(1. / mu) - 1) / (k_2**(1. / mu) + 1) - - sigma = sigma_0 \ - / np.sqrt( - (1 - 0.5 * chi * ( - (r_u_i + r_u_j)**2 / (1 + chi * u_i_u_j) + - (r_u_i - r_u_j)**2 / (1 - chi * u_i_u_j)))) - - epsilon = epsilon_0 *\ - (1 - chi**2 * u_i_u_j**2)**(-nu / 2.) *\ - (1 - chi_d / 2. * ( - (r_u_i + r_u_j)**2 / (1 + chi_d * u_i_u_j) + - (r_u_i - r_u_j)**2 / (1 - chi_d * u_i_u_j)))**mu - - rr = np.linalg.norm((np.linalg.norm(r_ij) - sigma + sigma_0) / sigma_0) - - return 4. * epsilon * (rr**-12 - rr**-6) - def count_fluid_nodes(lbf): """Counts the non-boundary nodes in the passed lb fluid instance.""" From 41d632aadcd0fa0ea25c388ff17321c462408064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 9 Mar 2022 12:10:15 +0100 Subject: [PATCH 80/99] CMake: Cleanup python test dependencies Mark shared python files as explicit dependencies of client tests instead of passing them to `add_custom_target()`. --- testsuite/python/CMakeLists.txt | 37 ++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index d70df80dd52..35288a8f14b 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -70,10 +70,22 @@ function(CHECKPOINT_TEST) endif() python_test( FILE save_checkpoint.py MAX_NUM_PROC ${TEST_NPROCS} LABELS ${TEST_LABELS} - SUFFIX ${TEST_SUFFIX} ARGUMENTS "${TEST_ARGUMENTS}") + SUFFIX ${TEST_SUFFIX} ARGUMENTS ${TEST_ARGUMENTS} DEPENDENCIES + unittest_generator.py) python_test( - FILE test_checkpoint.py MAX_NUM_PROC ${TEST_NPROCS} LABELS ${TEST_LABELS} - SUFFIX ${TEST_SUFFIX} ARGUMENTS "${TEST_ARGUMENTS}" DEPENDS + FILE + test_checkpoint.py + MAX_NUM_PROC + ${TEST_NPROCS} + LABELS + ${TEST_LABELS} + SUFFIX + ${TEST_SUFFIX} + ARGUMENTS + ${TEST_ARGUMENTS} + DEPENDENCIES + unittest_generator.py + DEPENDS save_checkpoint_${TEST_SUFFIX}) endfunction(CHECKPOINT_TEST) @@ -146,11 +158,11 @@ python_test(FILE lb_pressure_tensor.py MAX_NUM_PROC 1 LABELS gpu long) python_test(FILE ek_fluctuations.py MAX_NUM_PROC 1 LABELS gpu) python_test(FILE ek_charged_plate.py MAX_NUM_PROC 1 LABELS gpu) python_test(FILE ek_eof_one_species.py MAX_NUM_PROC 1 LABELS gpu SUFFIX x - ARGUMENTS Test__axis_x) + ARGUMENTS Test__axis_x DEPENDENCIES unittest_generator.py) python_test(FILE ek_eof_one_species.py MAX_NUM_PROC 1 LABELS gpu SUFFIX y - ARGUMENTS Test__axis_y) + ARGUMENTS Test__axis_y DEPENDENCIES unittest_generator.py) python_test(FILE ek_eof_one_species.py MAX_NUM_PROC 1 LABELS gpu SUFFIX z - ARGUMENTS Test__axis_z) + ARGUMENTS Test__axis_z DEPENDENCIES unittest_generator.py) python_test(FILE exclusions.py MAX_NUM_PROC 2) python_test(FILE langevin_thermostat.py MAX_NUM_PROC 1) python_test(FILE langevin_thermostat_stats.py MAX_NUM_PROC 1 LABELS long) @@ -159,8 +171,10 @@ python_test(FILE brownian_dynamics_stats.py MAX_NUM_PROC 1 LABELS long) python_test(FILE lees_edwards.py MAX_NUM_PROC 4) python_test(FILE nsquare.py MAX_NUM_PROC 4) python_test(FILE virtual_sites_relative.py MAX_NUM_PROC 2) -python_test(FILE virtual_sites_tracers.py MAX_NUM_PROC 2) -python_test(FILE virtual_sites_tracers_gpu.py MAX_NUM_PROC 2 LABELS gpu) +python_test(FILE virtual_sites_tracers.py MAX_NUM_PROC 2 DEPENDENCIES + virtual_sites_tracers_common.py) +python_test(FILE virtual_sites_tracers_gpu.py MAX_NUM_PROC 2 LABELS gpu + DEPENDENCIES virtual_sites_tracers_common.py) python_test(FILE regular_decomposition.py MAX_NUM_PROC 4) python_test(FILE integrator_npt.py MAX_NUM_PROC 4) python_test(FILE integrator_npt_stats.py MAX_NUM_PROC 4 LABELS long) @@ -272,13 +286,6 @@ add_custom_target( COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/unittest_decorators.py ${CMAKE_CURRENT_BINARY_DIR} - COMMAND - ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/unittest_generator.py - ${CMAKE_CURRENT_BINARY_DIR} - COMMAND - ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_SOURCE_DIR}/virtual_sites_tracers_common.py - ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/thermostats_common.py ${CMAKE_CURRENT_BINARY_DIR}) From 7d8752b12771888065248b34a4f904c59203efd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 9 Mar 2022 12:25:39 +0100 Subject: [PATCH 81/99] CMake: Simplify tests LABELS --- testsuite/python/CMakeLists.txt | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index 35288a8f14b..77cd2112bec 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -43,15 +43,13 @@ function(PYTHON_TEST) set_tests_properties(${TEST_NAME} PROPERTIES RESOURCE_LOCK GPU) endif() - if(${TEST_MAX_NUM_PROC} EQUAL 1) - set_tests_properties(${TEST_NAME} PROPERTIES LABELS "${TEST_LABELS}") - elseif(${TEST_MAX_NUM_PROC} EQUAL 2) - set_tests_properties(${TEST_NAME} PROPERTIES LABELS - "${TEST_LABELS};parallel") - else() - set_tests_properties( - ${TEST_NAME} PROPERTIES LABELS "${TEST_LABELS};parallel;parallel_odd") + if(${TEST_MAX_NUM_PROC} EQUAL 2) + list(APPEND TEST_LABELS "parallel") + endif() + if(${TEST_MAX_NUM_PROC} EQUAL 3) + list(APPEND TEST_LABELS "parallel_odd") endif() + set_tests_properties(${TEST_NAME} PROPERTIES LABELS "${TEST_LABELS}") set(python_tests ${python_tests} ${TEST_FILE_CONFIGURED} PARENT_SCOPE) endfunction(PYTHON_TEST) From 258a3cd5198eefd719e8a2d5ae576b3ef76ee2e8 Mon Sep 17 00:00:00 2001 From: Christoph Lohrmann Date: Mon, 14 Mar 2022 10:20:06 +0100 Subject: [PATCH 82/99] throw error for bonded interactions if particles are at the same position --- src/core/bonded_interactions/fene.hpp | 6 +++++- src/core/bonded_interactions/harmonic.hpp | 6 +++++- src/core/bonded_interactions/quartic.hpp | 12 +++++++++++- testsuite/python/interactions_bonded.py | 12 ++++++++---- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/core/bonded_interactions/fene.hpp b/src/core/bonded_interactions/fene.hpp index a18f34322b6..1cd16adcd1f 100644 --- a/src/core/bonded_interactions/fene.hpp +++ b/src/core/bonded_interactions/fene.hpp @@ -27,6 +27,7 @@ */ #include "config.hpp" +#include "errorhandling.hpp" #include @@ -84,7 +85,10 @@ FeneBond::force(Utils::Vector3d const &dx) const { if (len > ROUND_ERROR_PREC) { fac /= len; } else { - fac = 0.0; + if (r0 > 0.) { + runtimeErrorMsg() << "FENE bond: Particles have zero distance. " + "This is most likely an error in the system setup."; + } } return fac * dx; diff --git a/src/core/bonded_interactions/harmonic.hpp b/src/core/bonded_interactions/harmonic.hpp index 77f8a4e06cb..c723e0669be 100644 --- a/src/core/bonded_interactions/harmonic.hpp +++ b/src/core/bonded_interactions/harmonic.hpp @@ -25,6 +25,7 @@ */ #include "config.hpp" +#include "errorhandling.hpp" #include #include @@ -79,7 +80,10 @@ HarmonicBond::force(Utils::Vector3d const &dx) const { if (dist > ROUND_ERROR_PREC) { /* Regular case */ fac /= dist; } else { - fac = 0; + if (r > 0.) { + runtimeErrorMsg() << "Harmonic bond: Particles have zero distance. " + "This is most likely an error in the system setup."; + } } return fac * dx; } diff --git a/src/core/bonded_interactions/quartic.hpp b/src/core/bonded_interactions/quartic.hpp index 86b777305fd..5795e39d70a 100644 --- a/src/core/bonded_interactions/quartic.hpp +++ b/src/core/bonded_interactions/quartic.hpp @@ -24,6 +24,7 @@ * Routines to calculate the quartic potential between particle pairs. */ +#include "errorhandling.hpp" #include #include #include @@ -73,7 +74,16 @@ QuarticBond::force(Utils::Vector3d const &dx) const { } auto const dr = dist - r; - auto const fac = (k0 * dr + k1 * Utils::int_pow<3>(dr)) / dist; + auto fac = (k0 * dr + k1 * Utils::int_pow<3>(dr)); + + if (dist > ROUND_ERROR_PREC) { /* Regular case */ + fac /= dist; + } else { + if (r > 0.) { + runtimeErrorMsg() << "Quartic bond: Particles have zero distance. " + "This is most likely an error in the system setup."; + } + } return -fac * dx; } diff --git a/testsuite/python/interactions_bonded.py b/testsuite/python/interactions_bonded.py index c9aac3aa6ee..7006c5bf6e7 100644 --- a/testsuite/python/interactions_bonded.py +++ b/testsuite/python/interactions_bonded.py @@ -63,7 +63,7 @@ def test_harmonic(self): scalar_r=r, k=hb_k, r_0=hb_r_0), lambda r: tests_common.harmonic_potential( scalar_r=r, k=hb_k, r_0=hb_r_0), - 0.01, hb_r_cut, True) + 0.01, hb_r_cut, True, test_same_pos_exception=True) # Test Fene Bond def test_fene(self): @@ -78,7 +78,7 @@ def test_fene(self): scalar_r=r, k=fene_k, d_r_max=fene_d_r_max, r_0=fene_r_0), lambda r: tests_common.fene_potential( scalar_r=r, k=fene_k, d_r_max=fene_d_r_max, r_0=fene_r_0), - 0.01, fene_r_0 + fene_d_r_max, True) + 0.01, fene_r_0 + fene_d_r_max, True, test_same_pos_exception=True) def test_virtual_bond(self): # add sentinel harmonic bond, otherwise short-range loop is skipped @@ -167,10 +167,10 @@ def test_quartic(self): k0=quartic_k0, k1=quartic_k1, r=quartic_r, r_cut=quartic_r_cut, scalar_r=r), lambda r: tests_common.quartic_potential( k0=quartic_k0, k1=quartic_k1, r=quartic_r, r_cut=quartic_r_cut, scalar_r=r), - 0.01, quartic_r_cut, True) + 0.01, quartic_r_cut, True, test_same_pos_exception=True) def run_test(self, bond_instance, force_func, energy_func, min_dist, - cutoff, test_breakage=False): + cutoff, test_breakage=False, test_same_pos_exception=False): self.system.bonded_inter.add(bond_instance) p1, p2 = self.system.part.all() p1.bonds = ((bond_instance, p2),) @@ -217,6 +217,10 @@ def run_test(self, bond_instance, force_func, energy_func, min_dist, p2.pos = p1.pos + self.axis * cutoff * 1.01 with self.assertRaisesRegex(Exception, "Encountered errors during integrate"): self.system.integrator.run(recalc_forces=True, steps=0) + if test_same_pos_exception: + p2.pos = p1.pos + with self.assertRaises(Exception): + self.system.integrator.run(recalc_forces=True, steps=0) if __name__ == '__main__': From 70fd0f7e05bfb752d925ce1138f8661f1970e397 Mon Sep 17 00:00:00 2001 From: Christoph Lohrmann Date: Mon, 14 Mar 2022 12:08:50 +0100 Subject: [PATCH 83/99] throw if lb boundaries do not fit lb algorithm --- .../grid_based_algorithms/lb_boundaries.cpp | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/core/grid_based_algorithms/lb_boundaries.cpp b/src/core/grid_based_algorithms/lb_boundaries.cpp index f140d06a95f..b7a71090c15 100644 --- a/src/core/grid_based_algorithms/lb_boundaries.cpp +++ b/src/core/grid_based_algorithms/lb_boundaries.cpp @@ -170,10 +170,11 @@ void ek_init_boundaries() { /** Initialize boundary conditions for all constraints in the system. */ void lb_init_boundaries() { if (lattice_switch == ActiveLB::GPU) { -#if defined(CUDA) && defined(LB_BOUNDARIES_GPU) if (this_node != 0) { return; } +#if defined(CUDA) +#if defined(LB_BOUNDARIES_GPU) ek_init_boundaries(); unsigned number_of_boundnodes = 0; std::vector host_boundary_node_list; @@ -228,7 +229,14 @@ void lb_init_boundaries() { host_boundary_index_list.data(), boundary_velocity.data()); -#endif /* defined (CUDA) && defined (LB_BOUNDARIES_GPU) */ +#else // defined (LB_BOUNDARIES_GPU) + if (not lbboundaries.empty()) { + runtimeErrorMsg() + << "LB boundaries not empty for GPU LB but LB_BOUNDARIES_GPU not " + "compiled in. Activate in myconfig.hpp."; + } +#endif // defined (LB_BOUNDARIES_GPU) +#endif // defined (CUDA) } else if (lattice_switch == ActiveLB::CPU) { #if defined(LB_BOUNDARIES) boost::for_each(lbfields, [](auto &f) { f.boundary = 0; }); @@ -260,7 +268,13 @@ void lb_init_boundaries() { } } } -#endif +#else // defined(LB_BOUNDARIES) + if (not lbboundaries.empty()) { + runtimeErrorMsg() + << "LB boundaries not empty for CPU LB but LB_BOUNDARIES not " + "compiled in. Activate in myconfig.hpp."; + } +#endif // defined(LB_BOUNDARIES) } } From 3cabd061a236ea1fcb156e28d3529beb2b4dd962 Mon Sep 17 00:00:00 2001 From: Christoph Lohrmann Date: Mon, 14 Mar 2022 14:39:40 +0100 Subject: [PATCH 84/99] particle getters for always-there-properties --- src/python/espressomd/particle_data.pxd | 29 +++++----------- src/python/espressomd/particle_data.pyx | 34 +++++++------------ .../espressomd/visualization_mayavi.pyx | 9 ++--- 3 files changed, 27 insertions(+), 45 deletions(-) diff --git a/src/python/espressomd/particle_data.pxd b/src/python/espressomd/particle_data.pxd index ea837c48f90..201ef913325 100644 --- a/src/python/espressomd/particle_data.pxd +++ b/src/python/espressomd/particle_data.pxd @@ -35,35 +35,24 @@ cdef extern from "particle_data.hpp": # Therefore, only member variables are imported here, which are always compiled into ESPResSo. # For all other properties, getter-functions have to be used on the c # level. - ctypedef struct particle_properties "ParticleProperties": - int identity - int mol_id - int type - double mass - - ctypedef struct particle_position "ParticlePosition": - Vector3d p - Vector3d calc_director() - - ctypedef struct particle_force "ParticleForce": - Vector3d f - - ctypedef struct particle_momentum "ParticleMomentum": - Vector3d v ctypedef struct particle_local "ParticleLocal": - Vector3i i double lees_edwards_offset int lees_edwards_flag ctypedef struct particle "Particle": - particle_properties p - particle_position r - particle_momentum m - particle_force f particle_local l vector[int] exclusions() except + Vector3d calc_dip() + int type() + int identity() + double mass() + int mol_id() + Vector3d pos() + Vector3d calc_director() + Vector3d force() + Vector3d v() + Vector3i image_box() IF ENGINE: ctypedef struct particle_parameters_swimming "ParticleParametersSwimming": diff --git a/src/python/espressomd/particle_data.pyx b/src/python/espressomd/particle_data.pyx index e433ea617b3..efc12c7f8f7 100644 --- a/src/python/espressomd/particle_data.pyx +++ b/src/python/espressomd/particle_data.pyx @@ -103,7 +103,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - return self._id + return self.particle_data.identity() # The individual attributes of a particle are implemented as properties. @@ -127,7 +127,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - return self.particle_data.p.type + return self.particle_data.type() # Particle MolId property mol_id: @@ -154,7 +154,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - return self.particle_data.p.mol_id + return self.particle_data.mol_id() # Position property pos: @@ -175,7 +175,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - return make_array_locked(unfolded_position(< Vector3d > self.particle_data.r.p, < Vector3i > self.particle_data.l.i, box_geo.length())) + return make_array_locked(self.particle_data.pos()) property pos_folded: """ @@ -213,7 +213,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() return make_array_locked(folded_position( - Vector3d(self.particle_data.r.p), box_geo)) + Vector3d(self.particle_data.pos()), box_geo)) property image_box: """ @@ -225,9 +225,9 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - return array_locked([self.particle_data.l.i[0], - self.particle_data.l.i[1], - self.particle_data.l.i[2]]) + return array_locked([self.particle_data.image_box()[0], + self.particle_data.image_box()[1], + self.particle_data.image_box()[2]]) property lees_edwards_offset: """Contains the accumulated Lees-Edwards offset to reconstruct @@ -271,7 +271,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - return make_array_locked(self.particle_data.m.v) + return make_array_locked(self.particle_data.v()) # Force property f: @@ -293,7 +293,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - return make_array_locked(self.particle_data.f.f) + return make_array_locked(self.particle_data.force()) # Bonds property bonds: @@ -380,7 +380,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - return self.particle_data.p.mass + return self.particle_data.mass() IF ROTATION: property omega_lab: @@ -468,7 +468,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - return make_array_locked(self.particle_data.r.calc_director()) + return make_array_locked(self.particle_data.calc_director()) property omega_body: """ @@ -627,7 +627,7 @@ cdef class ParticleHandle: "To make a particle virtual, VIRTUAL_SITES has to be defined in myconfig.hpp") def __get__(self): - # Note: the pointoer_to... mechanism doesn't work if virtual sites + # Note: the pointer_to... mechanism doesn't work if virtual sites # are not compiled in, because then, in the core, p.p.is_virtual # is constexpr. IF VIRTUAL_SITES: @@ -1212,7 +1212,6 @@ cdef class ParticleHandle: if self._id in bond[1:]: raise Exception( f"Bond partners {bond[1:]} include the particle {self._id} itself.") - add_particle_bond(self._id, make_const_span[int](bond_info, len(bond))) def delete_verified_bond(self, bond): @@ -1263,30 +1262,25 @@ cdef class ParticleHandle: "Bond needs to be a tuple or list containing bond type and partners.") bond = list(bond) - # Bond type or numerical bond id if is_valid_type(bond[0], int): bond[0] = BondedInteractions()[bond[0]] elif not isinstance(bond[0], BondedInteraction): raise Exception( f"1st element of Bond has to be of type BondedInteraction or int, got {type(bond[0])}.") - # Check the bond is in the list of active bonded interactions if bond[0]._bond_id == -1: raise Exception( "The bonded interaction has not yet been added to the list of active bonds in ESPResSo.") - # Validity of the numeric id if not bonded_ia_params_zero_based_type(bond[0]._bond_id): raise ValueError( f"The bond type {bond[0]._bond_id} does not exist.") - # Number of partners expected_num_partners = bond[0].call_method('get_num_partners') if len(bond) - 1 != expected_num_partners: raise ValueError( f"Bond {bond[0]} needs {expected_num_partners} partners.") - # Type check on partners for i in range(1, len(bond)): if isinstance(bond[i], ParticleHandle): @@ -1295,7 +1289,6 @@ cdef class ParticleHandle: elif not is_valid_type(bond[i], int): raise ValueError( "Bond partners have to be of type integer or ParticleHandle.") - return tuple(bond) def add_bond(self, bond): @@ -1333,7 +1326,6 @@ cdef class ParticleHandle: >>> p1.add_bond((0, p2)) """ - _bond = self.normalize_and_check_bond_or_throw_exception(bond) if _bond in self.bonds: raise RuntimeError( diff --git a/src/python/espressomd/visualization_mayavi.pyx b/src/python/espressomd/visualization_mayavi.pyx index f4116818165..56fe68d83d4 100644 --- a/src/python/espressomd/visualization_mayavi.pyx +++ b/src/python/espressomd/visualization_mayavi.pyx @@ -213,8 +213,8 @@ cdef class mayaviLive: if not p: continue - coords[j, :] = numpy.array([p.r.p[0], p.r.p[1], p.r.p[2]]) - t = p.p.type + coords[j, :] = numpy.array([p.pos()[0], p.pos()[1], p.pos()[2]]) + t = p.type() types[j] = t + 1 radii[j] = self._determine_radius(t) @@ -244,8 +244,9 @@ cdef class mayaviLive: t = bonds[3 * n + 2] p1 = &get_particle_data(i) p2 = &get_particle_data(j) - bond_coords[n, :3] = numpy.array([p1.r.p[0], p1.r.p[1], p1.r.p[2]]) - bond_coords[n, 3:6] = make_array_locked( < const Vector3d > box_geo.get_mi_vector(Vector3d(p2.r.p), Vector3d(p1.r.p))) + bond_coords[n, :3] = numpy.array( + [p1.pos()[0], p1.pos()[1], p1.pos()[2]]) + bond_coords[n, 3:6] = make_array_locked(< const Vector3d > box_geo.get_mi_vector(Vector3d(p2.pos()), Vector3d(p1.pos()))) bond_coords[n, 6] = t boxl = self.system.box_l From 008235954f1eb80013523dbce30b8b4577528294 Mon Sep 17 00:00:00 2001 From: Christoph Lohrmann Date: Mon, 14 Mar 2022 16:45:58 +0100 Subject: [PATCH 85/99] more getters --- src/core/particle_data.hpp | 52 +------------------------ src/python/espressomd/particle_data.pxd | 49 +++++++++++------------ src/python/espressomd/particle_data.pyx | 44 ++++++++------------- 3 files changed, 40 insertions(+), 105 deletions(-) diff --git a/src/core/particle_data.hpp b/src/core/particle_data.hpp index 7a875746949..7b39c92f154 100644 --- a/src/core/particle_data.hpp +++ b/src/core/particle_data.hpp @@ -373,32 +373,6 @@ int number_of_particles_with_type(int type); // This is needed, because cython does not support conditional compilation // within a ctypedef definition -#ifdef LB_ELECTROHYDRODYNAMICS -inline Utils::Vector3d get_particle_mu_E(Particle const *p) { - return p->mu_E(); -} -#endif - -#ifdef ROTATION -inline Utils::Vector3d get_particle_omega_body(Particle const *p) { - return p->omega(); -} - -inline Utils::Vector3d get_particle_torque_body(Particle const *p) { - return p->torque(); -} - -inline Utils::Quaternion get_particle_quat(Particle const *p) { - return p->quat(); -} -#endif - -inline double get_particle_q(Particle const *p) { return p->q(); } - -#ifdef VIRTUAL_SITES -inline bool get_particle_virtual(Particle const *p) { return p->is_virtual(); } -#endif - #ifdef VIRTUAL_SITES_RELATIVE inline Utils::Quaternion get_particle_vs_quat(Particle const *p) { return p->vs_relative().quat; @@ -412,19 +386,7 @@ inline Utils::Quaternion get_particle_vs_relative(Particle const *p, } #endif -#ifdef DIPOLES -inline double get_particle_dipm(Particle const *p) { return p->dipm(); } -#endif - #ifdef EXTERNAL_FORCES -inline Utils::Vector3d get_particle_ext_force(Particle const *p) { - return p->ext_force(); -} -#ifdef ROTATION -inline Utils::Vector3d get_particle_ext_torque(Particle const *p) { - return p->ext_torque(); -} -#endif inline Utils::Vector3i get_particle_fix(Particle const *p) { return Utils::Vector3i{ {p->is_fixed_along(0), p->is_fixed_along(1), p->is_fixed_along(2)}}; @@ -452,24 +414,12 @@ inline double get_particle_gamma_rot(Particle const *p) { #endif // ROTATION #endif // THERMOSTAT_PER_PARTICLE -#ifdef ENGINE -inline ParticleParametersSwimming get_particle_swimming(Particle const *p) { - return p->swimming(); -} -#endif - -#ifdef ROTATIONAL_INERTIA -inline Utils::Vector3d get_particle_rotational_inertia(Particle const *p) { - return p->rinertia(); -} -#endif - #ifdef ROTATION inline Utils::Vector3i get_particle_rotation(Particle const *p) { return Utils::Vector3i{{p->can_rotate_around(0), p->can_rotate_around(1), p->can_rotate_around(2)}}; } -#endif +#endif // ROTATION /** * @brief Check if particle exists. diff --git a/src/python/espressomd/particle_data.pxd b/src/python/espressomd/particle_data.pxd index 201ef913325..99b076af16f 100644 --- a/src/python/espressomd/particle_data.pxd +++ b/src/python/espressomd/particle_data.pxd @@ -25,23 +25,26 @@ include "myconfig.pxi" from .utils cimport Span + # Import particle data structures and setter functions from particle_data.hpp cdef extern from "particle_data.hpp": cppclass BondView: int bond_id() Span[const int] partner_ids() + ctypedef struct particle_parameters_swimming "ParticleParametersSwimming": + bool swimming + double f_swim + double v_swim + int push_pull + double dipole_length + # Note: Conditional compilation is not possible within ctypedef blocks. # Therefore, only member variables are imported here, which are always compiled into ESPResSo. # For all other properties, getter-functions have to be used on the c - # level. - - ctypedef struct particle_local "ParticleLocal": - double lees_edwards_offset - int lees_edwards_flag + # level. ctypedef struct particle "Particle": - particle_local l vector[int] exclusions() except + Vector3d calc_dip() int type() @@ -53,14 +56,20 @@ cdef extern from "particle_data.hpp": Vector3d force() Vector3d v() Vector3i image_box() - - IF ENGINE: - ctypedef struct particle_parameters_swimming "ParticleParametersSwimming": - bool swimming - double f_swim - double v_swim - int push_pull - double dipole_length + double lees_edwards_offset() + int lees_edwards_flag() + + Vector3d rinertia() + Vector3d mu_E() + double q() + Vector3d omega() + Vector3d torque() + Quaternion[double] quat() + double dipm() + bint is_virtual() + Vector3d ext_force() + Vector3d ext_torque() + particle_parameters_swimming swimming() # Setter/getter/modifier functions functions void prefetch_particle_data(vector[int] ids) @@ -77,7 +86,6 @@ cdef extern from "particle_data.hpp": IF ROTATIONAL_INERTIA: void set_particle_rotational_inertia(int part, const Vector3d & rinertia) - Vector3d get_particle_rotational_inertia(const particle * p) IF ROTATION: void set_particle_rotation(int part, const Vector3i & flag) @@ -87,7 +95,6 @@ cdef extern from "particle_data.hpp": IF LB_ELECTROHYDRODYNAMICS: void set_particle_mu_E(int part, const Vector3d & mu_E) - Vector3d get_particle_mu_E(const particle * p) void set_particle_type(int part, int type) @@ -95,22 +102,17 @@ cdef extern from "particle_data.hpp": IF ROTATION: void set_particle_quat(int part, const Quaternion[double] & quat) - Quaternion[double] get_particle_quat(const particle * p) void set_particle_director(int part, const Vector3d & director) void set_particle_omega_lab(int part, const Vector3d & omega) void set_particle_omega_body(int part, const Vector3d & omega) void set_particle_torque_lab(int part, const Vector3d & torque) - Vector3d get_particle_omega_body(const particle * p) - Vector3d get_particle_torque_body(const particle * p) IF DIPOLES: void set_particle_dip(int part, const Vector3d & dip) void set_particle_dipm(int part, double dipm) - double get_particle_dipm(const particle * p) IF VIRTUAL_SITES: void set_particle_virtual(int part, int isVirtual) - bint get_particle_virtual(const particle * p) IF THERMOSTAT_PER_PARTICLE: IF PARTICLE_ANISOTROPY: @@ -134,15 +136,11 @@ cdef extern from "particle_data.hpp": void set_particle_vs_relative(int part, int vs_relative_to, double vs_distance, const Quaternion[double] & rel_ori) void set_particle_vs_quat(int part, const Quaternion[double] & vs_quat) - double get_particle_q(const particle * p) - IF EXTERNAL_FORCES: IF ROTATION: void set_particle_ext_torque(int part, const Vector3d & torque) - Vector3d get_particle_ext_torque(const particle * p) void set_particle_ext_force(int part, const Vector3d & force) - Vector3d get_particle_ext_force(const particle * p) void set_particle_fix(int part, const Vector3i & flag) Vector3i get_particle_fix(const particle * p) @@ -157,7 +155,6 @@ cdef extern from "particle_data.hpp": IF ENGINE: void set_particle_swimming(int part, particle_parameters_swimming swim) - particle_parameters_swimming get_particle_swimming(const particle * p) int remove_particle(int part) except + diff --git a/src/python/espressomd/particle_data.pyx b/src/python/espressomd/particle_data.pyx index efc12c7f8f7..623ef1e439f 100644 --- a/src/python/espressomd/particle_data.pyx +++ b/src/python/espressomd/particle_data.pyx @@ -175,7 +175,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - return make_array_locked(self.particle_data.pos()) + return make_array_locked(unfolded_position( < Vector3d > self.particle_data.pos(), < Vector3i > self.particle_data.image_box(), box_geo.length())) property pos_folded: """ @@ -237,7 +237,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - return self.particle_data.l.lees_edwards_offset + return self.particle_data.lees_edwards_offset() def __set__(self, value): set_particle_lees_edwards_offset(self._id, value) @@ -250,7 +250,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - return self.particle_data.l.lees_edwards_flag + return self.particle_data.lees_edwards_flag() # Velocity property v: @@ -295,7 +295,6 @@ cdef class ParticleHandle: self.update_particle_data() return make_array_locked(self.particle_data.force()) - # Bonds property bonds: """ The bonds stored by this particle. Note that bonds are only stored by @@ -322,7 +321,7 @@ cdef class ParticleHandle: # i.e., we delete all existing bonds delete_particle_bonds(self._id) - # Empty list? ony delete + # Empty list? only delete if _bonds: nlvl = nesting_level(_bonds) if nlvl == 1: # Single item @@ -336,7 +335,6 @@ cdef class ParticleHandle: def __get__(self): bonds = [] - part_bonds = get_particle_bonds(self._id) # Go through the bond list of the particle for part_bond in part_bonds: @@ -436,7 +434,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - cdef Quaternion[double] q = get_particle_quat(self.particle_data) + cdef Quaternion[double] q = self.particle_data.quat() return array_locked([q[0], q[1], q[2], q[3]]) property director: @@ -491,9 +489,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - cdef Vector3d omega_body - omega_body = get_particle_omega_body(self.particle_data) - return make_array_locked(omega_body) + return make_array_locked(self.particle_data.omega()) property torque_lab: """ @@ -521,7 +517,7 @@ cdef class ParticleHandle: self.update_particle_data() cdef Vector3d torque_body cdef Vector3d torque_space - torque_body = get_particle_torque_body(self.particle_data) + torque_body = self.particle_data.torque() torque_space = convert_vector_body_to_space( dereference(self.particle_data), torque_body) @@ -553,8 +549,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - return make_array_locked( - get_particle_rotational_inertia(self.particle_data)) + return make_array_locked(self.particle_data.rinertia()) # Charge property q: @@ -575,7 +570,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - return get_particle_q(self.particle_data) + return self.particle_data.q() IF LB_ELECTROHYDRODYNAMICS: property mu_E: @@ -600,7 +595,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - return make_array_locked(get_particle_mu_E(self.particle_data)) + return make_array_locked(self.particle_data.mu_E()) property virtual: """Virtual flag. @@ -627,14 +622,8 @@ cdef class ParticleHandle: "To make a particle virtual, VIRTUAL_SITES has to be defined in myconfig.hpp") def __get__(self): - # Note: the pointer_to... mechanism doesn't work if virtual sites - # are not compiled in, because then, in the core, p.p.is_virtual - # is constexpr. - IF VIRTUAL_SITES: - self.update_particle_data() - return get_particle_virtual(self.particle_data) - ELSE: - return False + self.update_particle_data() + return self.particle_data.is_virtual() IF VIRTUAL_SITES_RELATIVE: property vs_quat: @@ -765,7 +754,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - return get_particle_dipm(self.particle_data) + return self.particle_data.dipm() IF EXTERNAL_FORCES: property ext_force: @@ -787,7 +776,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() return make_array_locked( - get_particle_ext_force(self.particle_data)) + self.particle_data.ext_force()) property fix: """ @@ -843,8 +832,7 @@ cdef class ParticleHandle: def __get__(self): self.update_particle_data() - return make_array_locked( - get_particle_ext_torque(self.particle_data)) + return make_array_locked(self.particle_data.ext_torque()) IF THERMOSTAT_PER_PARTICLE: IF PARTICLE_ANISOTROPY: @@ -1163,7 +1151,7 @@ cdef class ParticleHandle: swim = {} mode = "N/A" cdef particle_parameters_swimming _swim - _swim = get_particle_swimming(self.particle_data) + _swim = self.particle_data.swimming() if _swim.push_pull == -1: mode = 'pusher' From 4163d4c4219bcdc59533e3352f70b47ae2a6dec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 15 Mar 2022 17:22:43 +0100 Subject: [PATCH 86/99] core: Fix regressions std::unordered_map objects std::unordered_map::emplace(std::pair &&value) and std::unordered_map::insert(std::pair &&value) actually discard the new value if a value with the same key already exists. --- src/core/PartCfg.hpp | 9 ++++----- src/core/bond_breakage/bond_breakage.cpp | 2 +- src/core/statistics.cpp | 5 ++--- src/core/statistics.hpp | 5 +++-- .../EspressoSystemStandAlone_test.cpp | 2 +- src/script_interface/GlobalContext.cpp | 2 +- .../auto_parameters/AutoParameters.hpp | 5 ++++- .../tests/AutoParameters_test.cpp | 19 ++++++++++++++----- src/utils/include/utils/Cache.hpp | 10 +++++++++- src/utils/include/utils/Factory.hpp | 4 ++-- .../include/utils/NumeratedContainer.hpp | 6 +++--- 11 files changed, 44 insertions(+), 25 deletions(-) diff --git a/src/core/PartCfg.hpp b/src/core/PartCfg.hpp index 3f05c87f7bd..6436bd448cc 100644 --- a/src/core/PartCfg.hpp +++ b/src/core/PartCfg.hpp @@ -29,12 +29,11 @@ * * This class implements cached access to all particles in a * particle range on the head node. - * This implementation fetches all particles to - * the head node on first access. Updates of the particle data are - * triggered automatically on access. The data in the cache - * is invalidated automatically on_particle_change, and then + * This implementation fetches all particles to the head node on first access, + * which invalidates all existing particle pointers. Updates of the particle + * data are triggered automatically on access. The data in the cache + * is invalidated automatically by @ref on_particle_change, and then * updated on the next access. - * */ class PartCfg { /** The particle data */ diff --git a/src/core/bond_breakage/bond_breakage.cpp b/src/core/bond_breakage/bond_breakage.cpp index 2081577d882..f63072afdef 100644 --- a/src/core/bond_breakage/bond_breakage.cpp +++ b/src/core/bond_breakage/bond_breakage.cpp @@ -43,7 +43,7 @@ namespace BondBreakage { static std::unordered_map> breakage_specs; void insert_spec(int key, std::shared_ptr obj) { - breakage_specs.insert({key, std::move(obj)}); + breakage_specs[key] = std::move(obj); } void erase_spec(int key) { breakage_specs.erase(key); } diff --git a/src/core/statistics.cpp b/src/core/statistics.cpp index b6563441352..b50dedb2457 100644 --- a/src/core/statistics.cpp +++ b/src/core/statistics.cpp @@ -188,13 +188,12 @@ std::vector nbhood(PartCfg &partCfg, const Utils::Vector3d &pos, return ids; } -double distto(PartCfg &partCfg, const Utils::Vector3d &pos, int pid) { +double distto(PartCfg &partCfg, const Utils::Vector3d pos, int pid) { auto mindist = std::numeric_limits::infinity(); for (auto const &part : partCfg) { if (pid != part.id()) { - auto const d = - box_geo.get_mi_vector({pos[0], pos[1], pos[2]}, part.pos()); + auto const d = box_geo.get_mi_vector(pos, part.pos()); mindist = std::min(mindist, d.norm2()); } } diff --git a/src/core/statistics.hpp b/src/core/statistics.hpp index 598542bee4b..7990e27436c 100644 --- a/src/core/statistics.hpp +++ b/src/core/statistics.hpp @@ -52,14 +52,15 @@ std::vector nbhood(PartCfg &partCfg, const Utils::Vector3d &pos, double r_catch, const Utils::Vector3i &planedims); /** Calculate minimal distance to point. + * Note: Particle handles may be invalided! * @param partCfg particle selection - * @param pos point + * @param pos point (must be a copy to avoid use-after-free errors) * @param pid if a valid particle id, this particle is omitted from * minimization (this is a good idea if @p pos is the * position of a particle). * @return the minimal distance of a particle to coordinates @p pos */ -double distto(PartCfg &partCfg, const Utils::Vector3d &pos, int pid = -1); +double distto(PartCfg &partCfg, Utils::Vector3d pos, int pid = -1); /** Calculate the distribution of particles around others. * diff --git a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp index eae1982b545..4eb46a4761a 100644 --- a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp +++ b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp @@ -147,7 +147,6 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, mpi_kill_particle_motion(0); for (int i = 0; i < 5; ++i) { set_particle_v(pid2, {static_cast(i), 0., 0.}); - auto const &p = get_particle_data(pid2); acc.update(); auto const time_series = acc.time_series(); @@ -155,6 +154,7 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, auto const acc_value = time_series.back(); auto const obs_value = (*obs)(); + auto const &p = get_particle_data(pid2); BOOST_TEST(obs_value == p.v(), boost::test_tools::per_element()); BOOST_TEST(acc_value == p.v(), boost::test_tools::per_element()); } diff --git a/src/script_interface/GlobalContext.cpp b/src/script_interface/GlobalContext.cpp index 10480695e31..d9a762550ed 100644 --- a/src/script_interface/GlobalContext.cpp +++ b/src/script_interface/GlobalContext.cpp @@ -49,7 +49,7 @@ void GlobalContext::make_handle(ObjectId id, const std::string &name, ObjectRef so = m_node_local_context->make_shared( name, unpack(parameters, m_local_objects)); - m_local_objects.emplace(std::make_pair(id, std::move(so))); + m_local_objects[id] = std::move(so); } catch (Exception const &) { } } diff --git a/src/script_interface/auto_parameters/AutoParameters.hpp b/src/script_interface/auto_parameters/AutoParameters.hpp index 341eeee2a6c..e30e1701161 100644 --- a/src/script_interface/auto_parameters/AutoParameters.hpp +++ b/src/script_interface/auto_parameters/AutoParameters.hpp @@ -112,7 +112,10 @@ class AutoParameters : public Base { void add_parameters(std::vector &¶ms) { for (auto const &p : params) { - m_parameters.emplace(std::make_pair(p.name, p)); + if (m_parameters.count(p.name)) { + m_parameters.erase(p.name); + } + m_parameters.emplace(std::make_pair(p.name, std::move(p))); } } diff --git a/src/script_interface/tests/AutoParameters_test.cpp b/src/script_interface/tests/AutoParameters_test.cpp index 21c97479977..f5e5869f667 100644 --- a/src/script_interface/tests/AutoParameters_test.cpp +++ b/src/script_interface/tests/AutoParameters_test.cpp @@ -52,16 +52,25 @@ BOOST_AUTO_TEST_CASE(basic) { } struct B : public A { - B(int i_, int j_, int k_) : A(i_, j_), k(k_) { add_parameters({{"k", k}}); } + B(int i_, int j_, int k_, int l_) : A(i_, j_), k(k_), l(l_) { + add_parameters({{"k", k}, {"i", l} /* override i accessor */}); + } int k; + int l; }; BOOST_AUTO_TEST_CASE(add_parameters) { - B b{1, 2, 3}; - - BOOST_CHECK(3 == boost::get(b.get_parameter("k"))); + B b{1, 2, 3, 4}; + A &a = b; + + BOOST_CHECK_EQUAL(a.i, 1); + BOOST_CHECK_EQUAL(b.i, 1); + BOOST_CHECK_EQUAL(boost::get(b.get_parameter("j")), 2); + BOOST_CHECK_EQUAL(boost::get(b.get_parameter("k")), 3); + BOOST_CHECK_EQUAL(boost::get(b.get_parameter("i")), 4); + BOOST_CHECK_EQUAL(boost::get(a.get_parameter("i")), 4); b.set_parameter("k", 12); - BOOST_CHECK(12 == boost::get(b.get_parameter("k"))); + BOOST_CHECK_EQUAL(boost::get(b.get_parameter("k")), 12); } BOOST_AUTO_TEST_CASE(exceptions) { diff --git a/src/utils/include/utils/Cache.hpp b/src/utils/include/utils/Cache.hpp index 1f6e88ed7d8..b16a18186ee 100644 --- a/src/utils/include/utils/Cache.hpp +++ b/src/utils/include/utils/Cache.hpp @@ -110,11 +110,19 @@ template class Cache { * maximal size, a random element is removed before * putting the new one. */ template Value const *put(Key const &k, ValueRef &&v) { + auto check_for_k = true; + /* If there already is a value for k, overwriting it * will not increase the size, so we don't have to * make room. */ - if ((m_cache.size() >= m_max_size) && !has(k)) + if ((m_cache.size() >= m_max_size) && !has(k)) { drop_random_element(); + check_for_k = false; + } + + if (check_for_k && has(k)) { + m_cache.erase(k); + } typename map_type::const_iterator it; std::tie(it, std::ignore) = m_cache.emplace(k, std::forward(v)); diff --git a/src/utils/include/utils/Factory.hpp b/src/utils/include/utils/Factory.hpp index 4393c7a58f6..fc59d972d10 100644 --- a/src/utils/include/utils/Factory.hpp +++ b/src/utils/include/utils/Factory.hpp @@ -111,8 +111,8 @@ class Factory { * @param name Given name for the type, has to be unique in this Factory. */ template void register_new(const std::string &name) { - m_map.insert({name, []() { return pointer_type(new Derived()); }}); - m_type_map.insert({typeid(Derived), name}); + m_map[name] = []() { return pointer_type(new Derived()); }; + m_type_map[typeid(Derived)] = name; } /** diff --git a/src/utils/include/utils/NumeratedContainer.hpp b/src/utils/include/utils/NumeratedContainer.hpp index 9155027ed83..a6b37714be9 100644 --- a/src/utils/include/utils/NumeratedContainer.hpp +++ b/src/utils/include/utils/NumeratedContainer.hpp @@ -57,7 +57,7 @@ template class NumeratedContainer { explicit NumeratedContainer(std::initializer_list l) : NumeratedContainer() { for (auto const &e : l) { - m_container.insert(e); + m_container[e.first] = e.second; /* Remove the index from the index set if it exists. */ m_free_indices.erase(m_free_indices.find(e.first), m_free_indices.end()); } @@ -79,14 +79,14 @@ template class NumeratedContainer { */ index_type add(const T &c) { const index_type ind = get_index(); - m_container.emplace(std::make_pair(ind, c)); + m_container[ind] = c; return ind; } /** @overload */ index_type add(T &&c) { const index_type ind = get_index(); - m_container.emplace(std::make_pair(ind, std::move(c))); + m_container[ind] = std::move(c); return ind; } From e1f5729e2e5ad395aa6177046723d87db51eba28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 15 Mar 2022 17:41:25 +0100 Subject: [PATCH 87/99] python: Rename bond breakage actions --- doc/sphinx/advanced_methods.rst | 15 +++++++------ src/python/espressomd/bond_breakage.py | 2 +- .../bond_breakage/BreakageSpec.hpp | 8 +++---- testsuite/python/bond_breakage.py | 21 +++++++++---------- testsuite/python/save_checkpoint.py | 2 +- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/doc/sphinx/advanced_methods.rst b/doc/sphinx/advanced_methods.rst index 45c32cba2b4..4140e432cb0 100644 --- a/doc/sphinx/advanced_methods.rst +++ b/doc/sphinx/advanced_methods.rst @@ -132,8 +132,8 @@ The bond breakage action is specified for individual bonds via the system Several modes are available: -* ``"revert_center_bond"``: delete a bond from the first particle -* ``"revert_vs_bond"``: delete a bond between the virtual site +* ``"delete_bond"``: delete a bond from the first particle +* ``"revert_bind_at_point_of_collision"``: delete a bond between the virtual site * ``"none"``: cancel an existing bond breakage specification Example:: @@ -187,17 +187,20 @@ features can be combined to model reversible bonds. Two combinations are possible: -* ``"revert_center_bond"`` mode for breakable bonds together with +* ``"delete_bond"`` mode for breakable bonds together with ``"bond_centers"`` mode for collision detection: used to create or delete a bond between two real particles -* ``"revert_vs_bond"`` mode for breakable bonds together with - ``"bind_at_point_of_collision"`` mode for collision detection: +* ``"revert_bind_at_point_of_collision"`` mode for breakable bonds together + with ``"bind_at_point_of_collision"`` mode for collision detection: used to create or delete virtual sites (the implicitly created bond between the real particles isn't affected) Please note that virtual sites are not automatically removed from the simulation, therefore the particle number will increase. If you want to -remove virtual sites, you need to do so manually. +remove virtual sites, you need to do so manually, either by tracking which +virtual sites were introduced by collision detection, or by periodically +looping over the particle list and removing virtual sites which have no +corresponding bond. .. _Immersed Boundary Method for soft elastic objects: diff --git a/src/python/espressomd/bond_breakage.py b/src/python/espressomd/bond_breakage.py index f8ca6b327d1..3b16b727979 100644 --- a/src/python/espressomd/bond_breakage.py +++ b/src/python/espressomd/bond_breakage.py @@ -30,7 +30,7 @@ class BreakageSpec(ScriptInterfaceHelper): ---------- breakage_length: :obj:`float` Maximal bond extension until the bond breaks. - action_type: :obj:`str`, \{'revert_center_bond', 'revert_vs_bond', 'none'\} + action_type: :obj:`str`, \{'delete_bond', 'revert_bind_at_point_of_collision', 'none'\} Action triggered when the bond reaches its maximal extension. """ diff --git a/src/script_interface/bond_breakage/BreakageSpec.hpp b/src/script_interface/bond_breakage/BreakageSpec.hpp index d44f9900816..a16959e47c7 100644 --- a/src/script_interface/bond_breakage/BreakageSpec.hpp +++ b/src/script_interface/bond_breakage/BreakageSpec.hpp @@ -54,14 +54,14 @@ class BreakageSpec : public AutoParameters { std::unordered_map<::BondBreakage::ActionType, std::string> m_breakage_enum_to_str = { {::BondBreakage::ActionType::NONE, "none"}, - {::BondBreakage::ActionType::DELETE_BOND, "revert_center_bond"}, + {::BondBreakage::ActionType::DELETE_BOND, "delete_bond"}, {::BondBreakage::ActionType::REVERT_BIND_AT_POINT_OF_COLLISION, - "revert_vs_bond"}}; + "revert_bind_at_point_of_collision"}}; std::unordered_map m_breakage_str_to_enum = { {"none", ::BondBreakage::ActionType::NONE}, - {"revert_center_bond", ::BondBreakage::ActionType::DELETE_BOND}, - {"revert_vs_bond", + {"delete_bond", ::BondBreakage::ActionType::DELETE_BOND}, + {"revert_bind_at_point_of_collision", ::BondBreakage::ActionType::REVERT_BIND_AT_POINT_OF_COLLISION}}; }; diff --git a/testsuite/python/bond_breakage.py b/testsuite/python/bond_breakage.py index 7405f94a0c9..0ffb1f4ec85 100644 --- a/testsuite/python/bond_breakage.py +++ b/testsuite/python/bond_breakage.py @@ -64,10 +64,9 @@ def tearDown(self): def test_00_interface(self): self.assertEqual(len(self.system.bond_breakage), 0) - spec2 = BreakageSpec( - breakage_length=1.2, - action_type="revert_center_bond") - spec4 = BreakageSpec(breakage_length=0.2, action_type="revert_vs_bond") + spec2 = BreakageSpec(breakage_length=1.2, action_type="delete_bond") + spec4 = BreakageSpec(breakage_length=0.2, + action_type="revert_bind_at_point_of_collision") self.system.bond_breakage[2] = spec2 self.system.bond_breakage[4] = spec4 self.assertEqual(self.system.bond_breakage[2], spec2) @@ -97,7 +96,7 @@ def test_ignore(self): # Particles closer than cutoff system.bond_breakage[self.h1] = BreakageSpec( - breakage_length=2, action_type="revert_center_bond") + breakage_length=2, action_type="delete_bond") self.p1.bonds = ((self.h1, self.p2)) system.integrator.run(1) @@ -109,7 +108,7 @@ def test_ignore(self): # Different bond type system.bond_breakage[self.h1] = BreakageSpec( - breakage_length=0.2, action_type="revert_center_bond") + breakage_length=0.2, action_type="delete_bond") self.p1.bonds = [(self.h2, self.p2)] self.p2.bonds = [(self.h2, self.p1)] system.integrator.run(1) @@ -121,7 +120,7 @@ def test_delete_bond(self): # Particles closer than cutoff system.bond_breakage[self.h1] = BreakageSpec( - breakage_length=0, action_type="revert_center_bond") + breakage_length=0, action_type="delete_bond") self.p1.bonds = [(self.h1, self.p2)] system.integrator.run(1) @@ -136,7 +135,7 @@ def test_revert_bind_at_point_of_collision(self): # Particles closer than cutoff system.bond_breakage[self.h1] = BreakageSpec( - breakage_length=0.5, action_type="revert_vs_bond") + breakage_length=0.5, action_type="revert_bind_at_point_of_collision") self.p1.bonds = [(self.h2, self.p2)] self.p1v.bonds = [(self.h1, self.p2v)] @@ -155,7 +154,7 @@ def test_exceptions(self): # Particles closer than cutoff system.bond_breakage[self.h2] = BreakageSpec( - breakage_length=0.5, action_type="revert_vs_bond") + breakage_length=0.5, action_type="revert_bind_at_point_of_collision") self.p1.bonds = [(self.h2, self.p2)] self.p1v.bonds = [(self.h1, self.p2v)] @@ -230,7 +229,7 @@ def test_center_bonds(self): self.system.collision_detection.set_params(mode="off") self.system.bond_breakage[harm] = BreakageSpec( - breakage_length=crit, action_type="revert_center_bond") + breakage_length=crit, action_type="delete_bond") self.system.integrator.run(1) bonds_dist = 0 @@ -263,7 +262,7 @@ def test_vs_bonds(self): self.system.collision_detection.set_params(mode="off") self.system.bond_breakage[harm] = BreakageSpec( - breakage_length=crit_vs, action_type="revert_vs_bond") + breakage_length=crit_vs, action_type="revert_bind_at_point_of_collision") self.system.integrator.run(1) bonds_dist = 0 diff --git a/testsuite/python/save_checkpoint.py b/testsuite/python/save_checkpoint.py index 97862467dca..266b3ee6aec 100644 --- a/testsuite/python/save_checkpoint.py +++ b/testsuite/python/save_checkpoint.py @@ -233,7 +233,7 @@ ind1=p1.id, ind2=p2.id, ind3=p3.id, k1=1.1, k2=1.2, maxDist=1.6, elasticLaw="NeoHookean") break_spec = espressomd.bond_breakage.BreakageSpec( - breakage_length=5., action_type="revert_center_bond") + breakage_length=5., action_type="delete_bond") system.bond_breakage[strong_harmonic_bond._bond_id] = break_spec checkpoint.register("system") From 4d2d8b194a4e2b2bd26337cb1c8a699237ade08e Mon Sep 17 00:00:00 2001 From: pmblanco Date: Tue, 15 Mar 2022 11:45:07 +0100 Subject: [PATCH 88/99] reaction_methods: new optional keyword exclusion_radius_per_type --- doc/sphinx/advanced_methods.rst | 21 +++++- doc/tutorials/constant_pH/constant_pH.ipynb | 6 +- .../benchmarks/mc_acid_base_reservoir.py | 4 +- samples/grand_canonical.py | 2 +- samples/reaction_ensemble.py | 5 +- samples/reaction_ensemble_complex_reaction.py | 2 +- .../reaction_methods/ConstantpHEnsemble.hpp | 7 +- .../reaction_methods/ReactionAlgorithm.cpp | 71 +++++++++++++----- .../reaction_methods/ReactionAlgorithm.hpp | 33 ++++++--- .../reaction_methods/ReactionEnsemble.hpp | 7 +- src/core/reaction_methods/WidomInsertion.hpp | 7 +- .../tests/ConstantpHEnsemble_test.cpp | 2 +- .../tests/ReactionAlgorithm_test.cpp | 74 ++++++++++++++++--- .../tests/ReactionEnsemble_test.cpp | 5 +- src/python/espressomd/reaction_ensemble.py | 31 ++++---- .../reaction_methods/ConstantpHEnsemble.hpp | 6 +- .../reaction_methods/ReactionAlgorithm.hpp | 31 ++++---- .../reaction_methods/ReactionEnsemble.hpp | 5 +- .../reaction_methods/WidomInsertion.hpp | 4 +- testsuite/python/constant_pH.py | 2 +- testsuite/python/constant_pH_stats.py | 2 +- testsuite/python/reaction_ensemble.py | 4 +- testsuite/python/reaction_methods.py | 66 +++++++++++++---- 23 files changed, 290 insertions(+), 107 deletions(-) diff --git a/doc/sphinx/advanced_methods.rst b/doc/sphinx/advanced_methods.rst index cd8ff224c57..0500bd6ebb1 100644 --- a/doc/sphinx/advanced_methods.rst +++ b/doc/sphinx/advanced_methods.rst @@ -1772,6 +1772,25 @@ be converted to :math:`K_c` as where :math:`p^{\ominus}=1\,\mathrm{atm}` is the standard pressure. Consider using the python module pint for unit conversion. +Coupling Reaction Methods to Molecular Dynamics +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The Monte Carlo (MC) sampling of the reaction can be coupled with a configurational sampling using Molecular Dynamics (MD). +For non-interacting systems this coupling is not an issue, but for interacting systems the insertion of new particles +can lead to instabilities in the MD integration ultimately leading to a crash of the simulation. + +This integration instabilities can be avoided by defining a distance around the particles which already exist in the system +where new particles will not be inserted, which is defined by the required keyword ``exclusion_range``. +This prevents big overlaps with the newly inserted particles, avoiding too big forces between particles, which prevents the MD integration from crashing. +The value of the exclusion range does not affect the limiting result and it only affects the convergence and the stability of the integration. For interacting systems, +it is usually a good practice to choose the exclusion range such that it is comparable to the diameter of the particles. + +If particles with significantly different sizes are present, it is desired to define a different exclusion range for each pair of particle types. This can be done by +defining an exclusion radius per particle type by using the optional argument ``exclusion_radius_per_type``. Then, their exclusion range is calculated using +the Lorentz-Berthelot combination rule, ` i.e. ` ``exclusion_range = exclusion_radius_per_type[particle_type_1] + exclusion_radius_per_type[particle_type_2]``. +If the exclusion radius of one particle type is not defined, the value of the parameter provided in ``exclusion_range`` is used by default. +If the value in ``exclusion_radius_per_type`` is equal to 0, then the exclusion range of that particle type with any other particle is 0. + .. _Grand canonical ensemble simulation using the Reaction Ensemble: Grand canonical ensemble simulation @@ -1813,7 +1832,7 @@ As before in the Reaction Ensemble one can define multiple reactions (e.g. for a .. code-block:: python cpH=reaction_ensemble.ConstantpHEnsemble( - temperature=1, exclusion_radius=1, seed=77) + temperature=1, exclusion_range=1, seed=77) cpH.add_reaction(gamma=K_diss, reactant_types=[0], reactant_coefficients=[1], product_types=[1, 2], product_coefficients=[1, 1], default_charges={0: 0, 1: -1, 2: +1}) diff --git a/doc/tutorials/constant_pH/constant_pH.ipynb b/doc/tutorials/constant_pH/constant_pH.ipynb index 0aa3e62cca6..141d77107f2 100644 --- a/doc/tutorials/constant_pH/constant_pH.ipynb +++ b/doc/tutorials/constant_pH/constant_pH.ipynb @@ -561,7 +561,7 @@ "After the particles have been added to the system we initialize the `espressomd.reaction_ensemble`. The parameters to set are:\n", "\n", "* `kT` specifies the $k_\\mathrm{B}T$ value which is used as the inverse-temperature in the Boltzmann-factor to calculate the probabilities for the insertion.\n", - "* `exclusion_radius` specifies the distance around particles already existing in the system, where new particles will not be inserted. This ensures that there are no big overlaps with the newly inserted particles, so that MD integration does not crash. The value of the exclusion radius does not affect the limiting result, however, it affects the convergence and the stability of the integration. For interacting systems, exclusion radius should be comparable to the particle size. For non-interacting systems, we can set the exclusion radius to $0.0$.\n", + "* `exclusion_range` specifies the distance around particles already existing in the system, where new particles will not be inserted. This ensures that there are no big overlaps with the newly inserted particles, so that MD integration does not crash. The value of the exclusion range does not affect the limiting result, however, it affects the convergence and the stability of the integration. For interacting systems, exclusion range should be comparable to the particle size. For non-interacting systems, we can set the exclusion range to $0.0$.\n", "\n", "* `seed` for the random number generator (RNG) is an arbitrary number that defines the starting point in the RNG sequence. Using the same seed should yield reproducible simualtion results if the same RNG implementation is used. When running multiple copies of the same simulation, each of them should use a different seed.\n", "\n", @@ -577,10 +577,10 @@ }, "source": [ "```python\n", - "exclusion_radius = PARTICLE_SIZE_REDUCED if USE_WCA else 0.0\n", + "exclusion_range = PARTICLE_SIZE_REDUCED if USE_WCA else 0.0\n", "RE = espressomd.reaction_ensemble.ConstantpHEnsemble(\n", " kT=KT_REDUCED,\n", - " exclusion_radius=exclusion_radius,\n", + " exclusion_range=exclusion_range,\n", " seed=77,\n", " constant_pH=2 # temporary value\n", ")\n", diff --git a/maintainer/benchmarks/mc_acid_base_reservoir.py b/maintainer/benchmarks/mc_acid_base_reservoir.py index 7798fc33796..c656348f047 100644 --- a/maintainer/benchmarks/mc_acid_base_reservoir.py +++ b/maintainer/benchmarks/mc_acid_base_reservoir.py @@ -227,10 +227,10 @@ def calc_donnan_coefficient(c_acid, I_res, charge=-1): system.actors.add(coulomb) # ### Set up the constant pH ensemble using the reaction ensemble module -exclusion_radius = PARTICLE_SIZE_REDUCED +exclusion_range = PARTICLE_SIZE_REDUCED RE = espressomd.reaction_ensemble.ReactionEnsemble( kT=KT_REDUCED, - exclusion_radius=exclusion_radius, + exclusion_range=exclusion_range, seed=77 ) # this parameter helps speed up the calculation in an interacting system diff --git a/samples/grand_canonical.py b/samples/grand_canonical.py index c0b0a77f82b..8326e9c8d76 100644 --- a/samples/grand_canonical.py +++ b/samples/grand_canonical.py @@ -89,7 +89,7 @@ epsilon=wca_eps, sigma=wca_sig) RE = espressomd.reaction_ensemble.ReactionEnsemble( - kT=temperature, exclusion_radius=wca_sig, seed=3) + kT=temperature, exclusion_range=wca_sig, seed=3) RE.add_reaction( gamma=cs_bulk**2 * np.exp(excess_chemical_potential_pair / temperature), reactant_types=[], reactant_coefficients=[], product_types=[1, 2], diff --git a/samples/reaction_ensemble.py b/samples/reaction_ensemble.py index 1f1c0e91a3a..5d10e30f819 100644 --- a/samples/reaction_ensemble.py +++ b/samples/reaction_ensemble.py @@ -80,7 +80,7 @@ if args.mode == "reaction_ensemble": RE = espressomd.reaction_ensemble.ReactionEnsemble( kT=1, - exclusion_radius=1, + exclusion_range=1, seed=77) RE.add_reaction(gamma=K_diss, reactant_types=[types["HA"]], @@ -90,7 +90,7 @@ default_charges=charge_dict) elif args.mode == "constant_pH_ensemble": RE = espressomd.reaction_ensemble.ConstantpHEnsemble( - kT=1, exclusion_radius=1, seed=77, constant_pH=2) + kT=1, exclusion_range=1, seed=77, constant_pH=2) RE.add_reaction(gamma=K_diss, reactant_types=[types["HA"]], product_types=[types["A-"], types["H+"]], default_charges=charge_dict) @@ -106,6 +106,7 @@ # up the simulation RE.set_non_interacting_type(type=max(types.values()) + 1) + for i in range(10000): RE.reaction(reaction_steps=1) if i % 100 == 0: diff --git a/samples/reaction_ensemble_complex_reaction.py b/samples/reaction_ensemble_complex_reaction.py index 33de01ca339..1f82d0bf2d6 100644 --- a/samples/reaction_ensemble_complex_reaction.py +++ b/samples/reaction_ensemble_complex_reaction.py @@ -82,7 +82,7 @@ # use an exclusion radius of 0 to simulate an ideal gas RE = espressomd.reaction_ensemble.ReactionEnsemble( - kT=1, exclusion_radius=0, seed=4) + kT=1, exclusion_range=0, seed=4) RE.add_reaction( diff --git a/src/core/reaction_methods/ConstantpHEnsemble.hpp b/src/core/reaction_methods/ConstantpHEnsemble.hpp index 0fe32c23980..ee2f759c0e4 100644 --- a/src/core/reaction_methods/ConstantpHEnsemble.hpp +++ b/src/core/reaction_methods/ConstantpHEnsemble.hpp @@ -40,9 +40,10 @@ namespace ReactionMethods { */ class ConstantpHEnsemble : public ReactionAlgorithm { public: - ConstantpHEnsemble(int seed, double kT, double exclusion_radius, - double constant_pH) - : ReactionAlgorithm(seed, kT, exclusion_radius), + ConstantpHEnsemble( + int seed, double kT, double exclusion_range, double constant_pH, + const std::unordered_map &exclusion_radius_per_type) + : ReactionAlgorithm(seed, kT, exclusion_range, exclusion_radius_per_type), m_constant_pH(constant_pH) {} double m_constant_pH; diff --git a/src/core/reaction_methods/ReactionAlgorithm.cpp b/src/core/reaction_methods/ReactionAlgorithm.cpp index 4cf82bc7cab..61a5a1b5fc5 100644 --- a/src/core/reaction_methods/ReactionAlgorithm.cpp +++ b/src/core/reaction_methods/ReactionAlgorithm.cpp @@ -185,7 +185,7 @@ ReactionAlgorithm::make_reaction_attempt( current_reaction.reactant_coefficients[i]; j++) { int p_id = create_particle(current_reaction.product_types[i]); - check_exclusion_radius(p_id); + check_exclusion_range(p_id); p_ids_created_particles.push_back(p_id); } } else if (current_reaction.reactant_coefficients[i] - @@ -196,7 +196,7 @@ ReactionAlgorithm::make_reaction_attempt( j++) { append_particle_property_of_random_particle( current_reaction.reactant_types[i], hidden_particles_properties); - check_exclusion_radius(hidden_particles_properties.back().p_id); + check_exclusion_range(hidden_particles_properties.back().p_id); hide_particle(hidden_particles_properties.back().p_id); } } @@ -213,14 +213,14 @@ ReactionAlgorithm::make_reaction_attempt( for (int j = 0; j < current_reaction.reactant_coefficients[i]; j++) { append_particle_property_of_random_particle( current_reaction.reactant_types[i], hidden_particles_properties); - check_exclusion_radius(hidden_particles_properties.back().p_id); + check_exclusion_range(hidden_particles_properties.back().p_id); hide_particle(hidden_particles_properties.back().p_id); } } else { // create additional product_types particles for (int j = 0; j < current_reaction.product_coefficients[i]; j++) { int p_id = create_particle(current_reaction.product_types[i]); - check_exclusion_radius(p_id); + check_exclusion_range(p_id); p_ids_created_particles.push_back(p_id); } } @@ -270,7 +270,7 @@ void ReactionAlgorithm::generic_oneway_reaction( SingleReaction ¤t_reaction, double &E_pot_old) { current_reaction.tried_moves += 1; - particle_inside_exclusion_radius_touched = false; + particle_inside_exclusion_range_touched = false; if (!all_reactant_particles_exist(current_reaction)) { // makes sure, no incomplete reaction is performed -> only need to consider // rollback of complete reactions @@ -292,7 +292,7 @@ void ReactionAlgorithm::generic_oneway_reaction( hidden_particles_properties) = make_reaction_attempt(current_reaction); - auto const E_pot_new = (particle_inside_exclusion_radius_touched) + auto const E_pot_new = (particle_inside_exclusion_range_touched) ? std::numeric_limits::max() : calculate_current_potential_energy_of_system(); @@ -375,16 +375,53 @@ void ReactionAlgorithm::hide_particle(int p_id) const { } /** - * Check if the modified particle is too close to neighboring particles. + * Check if the inserted particle is too close to neighboring particles. */ -void ReactionAlgorithm::check_exclusion_radius(int p_id) { - if (exclusion_radius == 0.) { - return; +void ReactionAlgorithm::check_exclusion_range(int inserted_particle_id) { + + auto const &inserted_particle = get_particle_data(inserted_particle_id); + + /* Check the excluded radius of the inserted particle */ + + if (exclusion_radius_per_type.count(inserted_particle.type()) != 0) { + if (exclusion_radius_per_type[inserted_particle.type()] == 0) { + return; + } + } + + auto particle_ids = get_particle_ids(); + /* remove the inserted particle id*/ + particle_ids.erase(std::remove(particle_ids.begin(), particle_ids.end(), + inserted_particle_id), + particle_ids.end()); + + /* Check if the inserted particle within the excluded_range of any other + * particle*/ + double excluded_distance; + for (const auto &particle_id : particle_ids) { + auto const &already_present_particle = get_particle_data(particle_id); + if (exclusion_radius_per_type.count(inserted_particle.type()) == 0 || + exclusion_radius_per_type.count(inserted_particle.type()) == 0) { + excluded_distance = exclusion_range; + } else if (exclusion_radius_per_type[already_present_particle.type()] == + 0.) { + continue; + } else { + excluded_distance = + exclusion_radius_per_type[inserted_particle.type()] + + exclusion_radius_per_type[already_present_particle.type()]; + } + + auto const d_min = + box_geo + .get_mi_vector(already_present_particle.r.p, inserted_particle.r.p) + .norm(); + + if (d_min < excluded_distance) { + particle_inside_exclusion_range_touched = true; + return; + } } - auto const &p = get_particle_data(p_id); - auto const d_min = distto(partCfg(), p.pos(), p_id); - if (d_min < exclusion_radius) - particle_inside_exclusion_radius_touched = true; } /** @@ -532,7 +569,7 @@ ReactionAlgorithm::generate_new_particle_positions(int type, int n_particles) { auto const prefactor = std::sqrt(kT / p.mass()); auto const new_pos = get_random_position_in_box(); move_particle(p_id, new_pos, prefactor); - check_exclusion_radius(p_id); + check_exclusion_range(p_id); } return old_positions; @@ -544,7 +581,7 @@ ReactionAlgorithm::generate_new_particle_positions(int type, int n_particles) { bool ReactionAlgorithm::do_global_mc_move_for_particles_of_type( int type, int particle_number_of_type_to_be_changed) { m_tried_configurational_MC_moves += 1; - particle_inside_exclusion_radius_touched = false; + particle_inside_exclusion_range_touched = false; int particle_number_of_type = number_of_particles_with_type(type); if (particle_number_of_type == 0 or @@ -558,7 +595,7 @@ bool ReactionAlgorithm::do_global_mc_move_for_particles_of_type( auto const original_positions = generate_new_particle_positions( type, particle_number_of_type_to_be_changed); - auto const E_pot_new = (particle_inside_exclusion_radius_touched) + auto const E_pot_new = (particle_inside_exclusion_range_touched) ? std::numeric_limits::max() : calculate_current_potential_energy_of_system(); diff --git a/src/core/reaction_methods/ReactionAlgorithm.hpp b/src/core/reaction_methods/ReactionAlgorithm.hpp index a089cdae3d5..c99a1109ef2 100644 --- a/src/core/reaction_methods/ReactionAlgorithm.hpp +++ b/src/core/reaction_methods/ReactionAlgorithm.hpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -47,17 +48,31 @@ struct StoredParticleProperty { class ReactionAlgorithm { public: - ReactionAlgorithm(int seed, double kT, double exclusion_radius) + ReactionAlgorithm( + int seed, double kT, double exclusion_range, + const std::unordered_map &exclusion_radius_per_type) : m_generator(Random::mt19937(std::seed_seq({seed, seed, seed}))), m_normal_distribution(0.0, 1.0), m_uniform_real_distribution(0.0, 1.0) { if (kT < 0.) { throw std::domain_error("Invalid value for 'kT'"); } - if (exclusion_radius < 0.) { - throw std::domain_error("Invalid value for 'exclusion_radius'"); + if (exclusion_range < 0.) { + throw std::domain_error("Invalid value for 'exclusion_range'"); } this->kT = kT; - this->exclusion_radius = exclusion_radius; + this->exclusion_range = exclusion_range; + for (auto const &item : exclusion_radius_per_type) { + auto type = item.first; + auto exclusion_radius = item.second; + if (exclusion_radius < 0) { + std::stringstream ss; + ss << "Invalid excluded_radius value for type " << type + << " value: " << exclusion_radius; + std::string error_message = ss.str(); + throw std::domain_error(error_message); + } + } + this->exclusion_radius_per_type = exclusion_radius_per_type; update_volume(); } @@ -72,7 +87,8 @@ class ReactionAlgorithm { * infinite, therefore these configurations do not contribute * to the partition function and ensemble averages. */ - double exclusion_radius; + double exclusion_range; + std::unordered_map exclusion_radius_per_type; double volume; int non_interacting_type = 100; @@ -84,7 +100,7 @@ class ReactionAlgorithm { } auto get_kT() const { return kT; } - auto get_exclusion_radius() const { return exclusion_radius; } + auto get_exclusion_range() const { return exclusion_range; } auto get_volume() const { return volume; } void set_volume(double new_volume) { if (new_volume <= 0.) { @@ -114,7 +130,7 @@ class ReactionAlgorithm { bool do_global_mc_move_for_particles_of_type(int type, int particle_number_of_type); - bool particle_inside_exclusion_radius_touched = false; + bool particle_inside_exclusion_range_touched = false; protected: std::vector m_empty_p_ids_smaller_than_max_seen_particle; @@ -146,7 +162,6 @@ class ReactionAlgorithm { generate_new_particle_positions(int type, int n_particles); void restore_properties(std::vector const &property_list); - /** * @brief draws a random integer from the uniform distribution in the range * [0,maxint-1] @@ -178,7 +193,7 @@ class ReactionAlgorithm { void replace_particle(int p_id, int desired_type) const; int create_particle(int desired_type); void hide_particle(int p_id) const; - void check_exclusion_radius(int p_id); + void check_exclusion_range(int inserted_particle_id); void move_particle(int p_id, Utils::Vector3d const &new_pos, double velocity_prefactor); diff --git a/src/core/reaction_methods/ReactionEnsemble.hpp b/src/core/reaction_methods/ReactionEnsemble.hpp index 4bfa2dcef6c..8b385dfb8a8 100644 --- a/src/core/reaction_methods/ReactionEnsemble.hpp +++ b/src/core/reaction_methods/ReactionEnsemble.hpp @@ -36,8 +36,11 @@ namespace ReactionMethods { */ class ReactionEnsemble : public ReactionAlgorithm { public: - ReactionEnsemble(int seed, double kT, double exclusion_radius) - : ReactionAlgorithm(seed, kT, exclusion_radius) {} + ReactionEnsemble( + int seed, double kT, double exclusion_radius, + const std::unordered_map &exclusion_radius_per_type) + : ReactionAlgorithm(seed, kT, exclusion_radius, + exclusion_radius_per_type) {} protected: double calculate_acceptance_probability( diff --git a/src/core/reaction_methods/WidomInsertion.hpp b/src/core/reaction_methods/WidomInsertion.hpp index 5196f950245..3c586d66ded 100644 --- a/src/core/reaction_methods/WidomInsertion.hpp +++ b/src/core/reaction_methods/WidomInsertion.hpp @@ -28,8 +28,11 @@ namespace ReactionMethods { /** Widom insertion method */ class WidomInsertion : public ReactionAlgorithm { public: - WidomInsertion(int seed, double kT, double exclusion_radius) - : ReactionAlgorithm(seed, kT, exclusion_radius) {} + WidomInsertion( + int seed, double kT, double exclusion_range, + const std::unordered_map &exclusion_radius_per_type) + : ReactionAlgorithm(seed, kT, exclusion_range, + exclusion_radius_per_type) {} double calculate_particle_insertion_potential_energy( SingleReaction ¤t_reaction); }; diff --git a/src/core/reaction_methods/tests/ConstantpHEnsemble_test.cpp b/src/core/reaction_methods/tests/ConstantpHEnsemble_test.cpp index ec9a6eb930d..b483424276d 100644 --- a/src/core/reaction_methods/tests/ConstantpHEnsemble_test.cpp +++ b/src/core/reaction_methods/tests/ConstantpHEnsemble_test.cpp @@ -50,7 +50,7 @@ BOOST_AUTO_TEST_CASE(ConstantpHEnsemble_test) { }; constexpr double tol = 100 * std::numeric_limits::epsilon(); - ConstantpHEnsembleTest r_algo(42, 20., 0., 1.); + ConstantpHEnsembleTest r_algo(42, 20., 0., 1., {}); // exception if no reaction was added BOOST_CHECK_THROW(r_algo.check_reaction_method(), std::runtime_error); diff --git a/src/core/reaction_methods/tests/ReactionAlgorithm_test.cpp b/src/core/reaction_methods/tests/ReactionAlgorithm_test.cpp index a84a219160a..6ffab4d522e 100644 --- a/src/core/reaction_methods/tests/ReactionAlgorithm_test.cpp +++ b/src/core/reaction_methods/tests/ReactionAlgorithm_test.cpp @@ -59,7 +59,7 @@ BOOST_AUTO_TEST_CASE(ReactionAlgorithm_test) { constexpr double tol = 100 * std::numeric_limits::epsilon(); // check acceptance rate - ReactionAlgorithmTest r_algo(42, 1., 0.); + ReactionAlgorithmTest r_algo(42, 1., 0., {}); for (int tried_moves = 1; tried_moves < 5; ++tried_moves) { for (int accepted_moves = 0; accepted_moves < 5; ++accepted_moves) { r_algo.m_tried_configurational_MC_moves = tried_moves; @@ -156,11 +156,11 @@ BOOST_AUTO_TEST_CASE(ReactionAlgorithm_test) { set_particle_type(0, type_A); set_particle_type(1, type_A); // update particle positions and velocities - BOOST_CHECK(!r_algo.particle_inside_exclusion_radius_touched); - r_algo.particle_inside_exclusion_radius_touched = false; - r_algo.exclusion_radius = box_l; + BOOST_CHECK(!r_algo.particle_inside_exclusion_range_touched); + r_algo.particle_inside_exclusion_range_touched = false; + r_algo.exclusion_range = box_l; auto const bookkeeping = r_algo.generate_new_particle_positions(0, 2); - BOOST_CHECK(r_algo.particle_inside_exclusion_radius_touched); + BOOST_CHECK(r_algo.particle_inside_exclusion_range_touched); // check moves and bookkeeping for (auto const &item : bookkeeping) { auto const pid = item.first; @@ -197,8 +197,8 @@ BOOST_AUTO_TEST_CASE(ReactionAlgorithm_test) { BOOST_REQUIRE(!r_algo.do_global_mc_move_for_particles_of_type(type_A, 0)); // force all MC moves to be rejected by picking particles inside // their exclusion radius - r_algo.exclusion_radius = box_l; - r_algo.particle_inside_exclusion_radius_touched = false; + r_algo.exclusion_range = box_l; + r_algo.particle_inside_exclusion_range_touched = false; BOOST_REQUIRE(!r_algo.do_global_mc_move_for_particles_of_type(type_A, 2)); // check none of the particles moved for (auto const pid : {0, 1}) { @@ -208,8 +208,8 @@ BOOST_AUTO_TEST_CASE(ReactionAlgorithm_test) { BOOST_CHECK_LE((new_pos - ref_old_pos).norm(), tol); } // force a MC move to be accepted by using a constant Hamiltonian - r_algo.exclusion_radius = 0.; - r_algo.particle_inside_exclusion_radius_touched = false; + r_algo.exclusion_range = 0.; + r_algo.particle_inside_exclusion_range_touched = false; BOOST_REQUIRE(r_algo.do_global_mc_move_for_particles_of_type(type_A, 1)); std::vector distances(2); // check that only one particle moved @@ -275,6 +275,62 @@ BOOST_AUTO_TEST_CASE(ReactionAlgorithm_test) { BOOST_CHECK_THROW(r_algo.set_cyl_constraint(0.5, 0.5, -1.0), exception); r_algo.remove_constraint(); } + + { + + // domain error if negative exclusion_range is provided + + BOOST_CHECK_THROW(ReactionAlgorithmTest r_algo(40, 1., -1, {}), + std::domain_error); + + // domain error if a negative value is provided in exclusion_radius_per_type + std::unordered_map exclusion_radius_per_type; + exclusion_radius_per_type[type_A] = 1; + exclusion_radius_per_type[type_B] = -1; + + BOOST_CHECK_THROW( + ReactionAlgorithmTest r_algo(40, 1., 1, exclusion_radius_per_type), + std::domain_error); + + auto const box_l = Utils::Vector3d{1, 1, 1}; + espresso::system->set_box_l(box_l); + + // set up particle + place_particle(0, {0.5, 0.5, 0.5}); + set_particle_type(0, type_A); + place_particle(1, {0.7, 0.7, 0.7}); + set_particle_type(1, type_B); + exclusion_radius_per_type[type_A] = 0.1; + exclusion_radius_per_type[type_B] = 1; + ReactionAlgorithmTest r_algo(40, 1., 0, exclusion_radius_per_type); + + // the new position will always be in the excluded range since the sum of + // the radii of both particle types is larger than box length. The exclusion + // range value should be ignored + + r_algo.generate_new_particle_positions(type_B, 1); + + BOOST_REQUIRE(r_algo.particle_inside_exclusion_range_touched); + + // the new position will never be in the excluded range because the + // exclusion_radius of the particle is 0 + + r_algo.exclusion_radius_per_type[type_B] = 0; + r_algo.particle_inside_exclusion_range_touched = false; + r_algo.generate_new_particle_positions(type_B, 1); + + BOOST_REQUIRE(!r_algo.particle_inside_exclusion_range_touched); + // the new position will never accepted since the value in exclusion_range + // will be used if the particle does not have a defined excluded radius + + r_algo.exclusion_range = 1; + r_algo.exclusion_radius_per_type = {{type_A, 0}}; + r_algo.generate_new_particle_positions(type_B, 1); + + BOOST_REQUIRE(r_algo.particle_inside_exclusion_range_touched); + + // + } } int main(int argc, char **argv) { diff --git a/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp b/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp index d115fb424e8..a50457d57b2 100644 --- a/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp +++ b/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp @@ -46,7 +46,6 @@ namespace espresso { // ESPResSo system instance std::unique_ptr system; } // namespace espresso - // Check the Monte Carlo algorithm where moves depend on the system // configuration and energy. BOOST_AUTO_TEST_CASE(ReactionEnsemble_test) { @@ -61,7 +60,7 @@ BOOST_AUTO_TEST_CASE(ReactionEnsemble_test) { // check basic interface { - ReactionEnsembleTest r_algo(42, 20., 0.); + ReactionEnsembleTest r_algo(42, 20., 0., {}); r_algo.set_volume(10.); // exception if no reaction was added @@ -97,7 +96,7 @@ BOOST_AUTO_TEST_CASE(ReactionEnsemble_test) { // check that the system energy is updated after a succesful reaction { - ReactionEnsembleTest test_reaction(42, 1., 0.); + ReactionEnsembleTest test_reaction(42, 1., 0., {}); test_reaction.set_volume(1.); // create a generic identity exchange reaction D <-> E diff --git a/src/python/espressomd/reaction_ensemble.py b/src/python/espressomd/reaction_ensemble.py index 1f694b905b5..15632dc61b3 100644 --- a/src/python/espressomd/reaction_ensemble.py +++ b/src/python/espressomd/reaction_ensemble.py @@ -54,7 +54,7 @@ class ReactionAlgorithm(ScriptInterfaceHelper): This class provides the base class for Reaction Algorithms like the Reaction Ensemble algorithm and the constant pH method. Initialize the reaction algorithm by setting the - standard pressure, temperature, and the exclusion radius. + standard pressure, temperature, and the exclusion range. Note: When creating particles the velocities of the new particles are set according the Maxwell-Boltzmann distribution. In this step the mass of the @@ -65,16 +65,13 @@ class ReactionAlgorithm(ScriptInterfaceHelper): ---------- kT : :obj:`float` Thermal energy of the system in simulation units - exclusion_radius : :obj:`float` - Minimal distance from any particle, within which new particle will not - be inserted. This is useful to avoid integrator failures if particles - are too close and there is a diverging repulsive interaction, or to - prevent two oppositely charged particles from being placed on top of - each other. The Boltzmann factor :math:`\\exp(-\\beta E)` gives these - configurations a small contribution to the partition function, - therefore they can be neglected. + exclusion_range : :obj:`float` + Minimal distance from any particle, within which new particles will not + be inserted. seed : :obj:`int` Initial counter value (or seed) of the Mersenne Twister RNG. + exclusion_radius_per_type : :obj:`dict`, optional + Mapping of particle types to exclusion radii. Methods ------- @@ -133,7 +130,7 @@ class ReactionAlgorithm(ScriptInterfaceHelper): >>> elc = espressomd.electrostatics.ELC(p3m_actor=p3m, maxPWerror=1.0, gap_size=elc_gap) >>> system.actors.add(elc) >>> # add constant pH method - >>> RE = espressomd.reaction_ensemble.ConstantpHEnsemble(kT=1, exclusion_radius=1, seed=77) + >>> RE = espressomd.reaction_ensemble.ConstantpHEnsemble(kT=1, exclusion_range=1, seed=77) >>> RE.constant_pH = 2 >>> RE.add_reaction(gamma=0.0088, reactant_types=[types["HA"]], ... product_types=[types["A-"], types["H+"]], @@ -281,15 +278,18 @@ class ReactionAlgorithm(ScriptInterfaceHelper): ) def __init__(self, **kwargs): + if 'exclusion_radius' in kwargs: + raise KeyError( + 'the keyword `exclusion_radius` is obsolete. Currently, the equivalent keyword is `exclusion_range`') super().__init__(**kwargs) if not 'sip' in kwargs: utils.check_valid_keys(self.valid_keys(), kwargs.keys()) def valid_keys(self): - return {"kT", "exclusion_radius", "seed"} + return {"kT", "exclusion_range", "seed", "exclusion_radius_per_type"} def required_keys(self): - return {"kT", "exclusion_radius", "seed"} + return {"kT", "exclusion_range", "seed"} def add_reaction(self, **kwargs): """ @@ -376,7 +376,7 @@ def get_status(self): reactions_list.append(reaction) return {"reactions": reactions_list, "kT": self.kT, - "exclusion_radius": self.exclusion_radius} + "exclusion_range": self.exclusion_range} @script_interface_register @@ -408,10 +408,11 @@ class ConstantpHEnsemble(ReactionAlgorithm): _so_creation_policy = "LOCAL" def valid_keys(self): - return {"kT", "exclusion_radius", "seed", "constant_pH"} + return {"kT", "exclusion_range", "seed", + "constant_pH", "exclusion_radius_per_type"} def required_keys(self): - return {"kT", "exclusion_radius", "seed", "constant_pH"} + return {"kT", "exclusion_range", "seed", "constant_pH"} def add_reaction(self, *args, **kwargs): warn_msg = ( diff --git a/src/script_interface/reaction_methods/ConstantpHEnsemble.hpp b/src/script_interface/reaction_methods/ConstantpHEnsemble.hpp index 5016c8e7504..834bf651062 100644 --- a/src/script_interface/reaction_methods/ConstantpHEnsemble.hpp +++ b/src/script_interface/reaction_methods/ConstantpHEnsemble.hpp @@ -51,8 +51,10 @@ class ConstantpHEnsemble : public ReactionAlgorithm { void do_construct(VariantMap const ¶ms) override { m_re = std::make_shared<::ReactionMethods::ConstantpHEnsemble>( get_value(params, "seed"), get_value(params, "kT"), - get_value(params, "exclusion_radius"), - get_value(params, "constant_pH")); + get_value(params, "exclusion_range"), + get_value(params, "constant_pH"), + get_map(get_value_or>( + params, "exclusion_radius_per_type", {}))); } private: diff --git a/src/script_interface/reaction_methods/ReactionAlgorithm.hpp b/src/script_interface/reaction_methods/ReactionAlgorithm.hpp index f6028b70e09..1b5868fe461 100644 --- a/src/script_interface/reaction_methods/ReactionAlgorithm.hpp +++ b/src/script_interface/reaction_methods/ReactionAlgorithm.hpp @@ -56,19 +56,24 @@ class ReactionAlgorithm : public AutoParameters { virtual std::shared_ptr<::ReactionMethods::ReactionAlgorithm> RE() = 0; ReactionAlgorithm() { - add_parameters({ - {"reactions", AutoParameter::read_only, - [this]() { - std::vector out; - for (auto const &e : m_reactions) { - out.emplace_back(e); - } - return out; - }}, - {"kT", AutoParameter::read_only, [this]() { return RE()->get_kT(); }}, - {"exclusion_radius", AutoParameter::read_only, - [this]() { return RE()->get_exclusion_radius(); }}, - }); + add_parameters( + {{"reactions", AutoParameter::read_only, + [this]() { + std::vector out; + for (auto const &e : m_reactions) { + out.emplace_back(e); + } + return out; + }}, + {"kT", AutoParameter::read_only, [this]() { return RE()->get_kT(); }}, + {"exclusion_range", AutoParameter::read_only, + [this]() { return RE()->get_exclusion_range(); }}, + {"exclusion_radius_per_type", + [this](Variant const &v) { + RE()->exclusion_radius_per_type = get_map( + get_value>(v)); + }, + [this]() { return make_map(RE()->exclusion_radius_per_type); }}}); } Variant do_call_method(std::string const &name, diff --git a/src/script_interface/reaction_methods/ReactionEnsemble.hpp b/src/script_interface/reaction_methods/ReactionEnsemble.hpp index cdd98e1aaa6..03b66128420 100644 --- a/src/script_interface/reaction_methods/ReactionEnsemble.hpp +++ b/src/script_interface/reaction_methods/ReactionEnsemble.hpp @@ -39,9 +39,12 @@ class ReactionEnsemble : public ReactionAlgorithm { } void do_construct(VariantMap const ¶ms) override { + m_re = std::make_shared<::ReactionMethods::ReactionEnsemble>( get_value(params, "seed"), get_value(params, "kT"), - get_value(params, "exclusion_radius")); + get_value(params, "exclusion_range"), + get_map(get_value_or>( + params, "exclusion_radius_per_type", {}))); } private: diff --git a/src/script_interface/reaction_methods/WidomInsertion.hpp b/src/script_interface/reaction_methods/WidomInsertion.hpp index f60045507df..f563047bd9e 100644 --- a/src/script_interface/reaction_methods/WidomInsertion.hpp +++ b/src/script_interface/reaction_methods/WidomInsertion.hpp @@ -41,8 +41,10 @@ class WidomInsertion : public ReactionAlgorithm { } void do_construct(VariantMap const ¶ms) override { + std::unordered_map exclusion_radius_per_type; m_re = std::make_shared<::ReactionMethods::WidomInsertion>( - get_value(params, "seed"), get_value(params, "kT"), 0.); + get_value(params, "seed"), get_value(params, "kT"), 0., + exclusion_radius_per_type); } Variant do_call_method(std::string const &name, diff --git a/testsuite/python/constant_pH.py b/testsuite/python/constant_pH.py index 37d25fe5320..129255127a9 100644 --- a/testsuite/python/constant_pH.py +++ b/testsuite/python/constant_pH.py @@ -53,7 +53,7 @@ def test_ideal_alpha(self): RE = espressomd.reaction_ensemble.ConstantpHEnsemble( kT=1.0, - exclusion_radius=1, + exclusion_range=1, seed=44, constant_pH=pH) RE.add_reaction( diff --git a/testsuite/python/constant_pH_stats.py b/testsuite/python/constant_pH_stats.py index f7d05385c45..907b27c7330 100644 --- a/testsuite/python/constant_pH_stats.py +++ b/testsuite/python/constant_pH_stats.py @@ -52,7 +52,7 @@ class ReactionEnsembleTest(ut.TestCase): system.cell_system.skin = 0.4 system.time_step = 0.01 RE = espressomd.reaction_ensemble.ConstantpHEnsemble( - kT=1.0, exclusion_radius=1, seed=44, constant_pH=pH) + kT=1.0, exclusion_range=1, seed=44, constant_pH=pH) @classmethod def setUpClass(cls): diff --git a/testsuite/python/reaction_ensemble.py b/testsuite/python/reaction_ensemble.py index 96f160fd9e9..e56e11fc6a9 100644 --- a/testsuite/python/reaction_ensemble.py +++ b/testsuite/python/reaction_ensemble.py @@ -51,7 +51,7 @@ class ReactionEnsembleTest(ut.TestCase): # the exact sequence of random numbers and does not require hard-coded # output values temperature = 1.0 - exclusion_radius = 1.0 + exclusion_range = 1.0 # could be in this test for example anywhere in the range 0.000001 ... 9, reactant_types = [types["HA"]] reactant_coefficients = [1] @@ -69,7 +69,7 @@ class ReactionEnsembleTest(ut.TestCase): gamma = target_alpha**2 / (1. - target_alpha) * N0 / (volume**nubar) RE = espressomd.reaction_ensemble.ReactionEnsemble( kT=temperature, - exclusion_radius=exclusion_radius, seed=12) + exclusion_range=exclusion_range, seed=12) @classmethod def setUpClass(cls): diff --git a/testsuite/python/reaction_methods.py b/testsuite/python/reaction_methods.py index 0a29f2eb4d5..4a568aea974 100644 --- a/testsuite/python/reaction_methods.py +++ b/testsuite/python/reaction_methods.py @@ -33,7 +33,8 @@ class ReactionMethods(ut.TestCase): def tearDown(self): self.system.part.clear() - def check_interface(self, method, kT, exclusion_radius, gamma): + def check_interface(self, method, kT, exclusion_range, + gamma, exclusion_radius_per_type): def check_reaction_parameters(reactions, parameters): for reaction, params in zip(reactions, parameters): for key in reaction.required_keys(): @@ -68,9 +69,27 @@ def check_reaction_parameters(reactions, parameters): # check getters and setters self.assertAlmostEqual(method.kT, kT, delta=1e-10) self.assertAlmostEqual( - method.exclusion_radius, - exclusion_radius, + method.exclusion_range, + exclusion_range, delta=1e-10) + if exclusion_radius_per_type: # Avoid this test for widom method + self.assertEqual( + list( + method.exclusion_radius_per_type.keys()), + [1]) + self.assertAlmostEqual( + method.exclusion_radius_per_type[1], + exclusion_radius_per_type[1], + delta=1e-10) + method.exclusion_radius_per_type = {2: 0.2} + self.assertEqual( + list( + method.exclusion_radius_per_type.keys()), + [2]) + self.assertAlmostEqual( + method.exclusion_radius_per_type[2], + 0.2, + delta=1e-10) self.assertAlmostEqual( method.get_volume(), self.system.volume(), @@ -96,7 +115,7 @@ def check_reaction_parameters(reactions, parameters): # check status status = method.get_status() self.assertEqual(status['kT'], kT) - self.assertEqual(status['exclusion_radius'], exclusion_radius) + self.assertEqual(status['exclusion_range'], exclusion_range) self.assertEqual(len(status['reactions']), 2) for reaction_flat, params in zip( status['reactions'], reaction_parameters): @@ -148,17 +167,34 @@ def check_reaction_parameters(reactions, parameters): def test_interface(self): # reaction ensemble method = espressomd.reaction_ensemble.ReactionEnsemble( - kT=1.5, exclusion_radius=0.8, seed=12) - self.check_interface(method, kT=1.5, exclusion_radius=0.8, gamma=1.2) + kT=1.5, exclusion_range=0.8, seed=12, exclusion_radius_per_type={1: 0.1}) + self.check_interface( + method, + kT=1.5, + exclusion_range=0.8, + gamma=1.2, + exclusion_radius_per_type={ + 1: 0.1}) # constant pH ensemble method = espressomd.reaction_ensemble.ConstantpHEnsemble( - kT=1.5, exclusion_radius=0.8, seed=12, constant_pH=10) - self.check_interface(method, kT=1.5, exclusion_radius=0.8, gamma=1.2) + kT=1.5, exclusion_range=0.8, seed=12, constant_pH=10, exclusion_radius_per_type={1: 0.1}) + self.check_interface( + method, + kT=1.5, + exclusion_range=0.8, + gamma=1.2, + exclusion_radius_per_type={ + 1: 0.1}) # Widom insertion method = espressomd.reaction_ensemble.WidomInsertion(kT=1.6, seed=12) - self.check_interface(method, kT=1.6, exclusion_radius=0., gamma=1.) + self.check_interface( + method, + kT=1.6, + exclusion_range=0., + gamma=1., + exclusion_radius_per_type={}) def test_exceptions(self): single_reaction_params = { @@ -174,7 +210,7 @@ def test_exceptions(self): } widom = espressomd.reaction_ensemble.WidomInsertion(kT=1., seed=12) method = espressomd.reaction_ensemble.ReactionEnsemble( - kT=1.5, exclusion_radius=0.8, seed=12) + kT=1.5, exclusion_range=0.8, seed=12, exclusion_radius_per_type={1: 0.1}) method.add_reaction(**reaction_params) widom.add_reaction(**reaction_params) @@ -254,19 +290,19 @@ def test_exceptions(self): x=1, **single_reaction_params) with self.assertRaisesRegex(ValueError, err_msg): espressomd.reaction_ensemble.ReactionEnsemble( - kT=1., exclusion_radius=1., seed=12, x=1) + kT=1., exclusion_range=1., seed=12, x=1, exclusion_radius_per_type={1: 0.1}) with self.assertRaisesRegex(ValueError, err_msg): espressomd.reaction_ensemble.ConstantpHEnsemble( - kT=1., exclusion_radius=1., seed=12, x=1, constant_pH=2) + kT=1., exclusion_range=1., seed=12, x=1, constant_pH=2, exclusion_radius_per_type={1: 0.1}) with self.assertRaisesRegex(ValueError, err_msg): espressomd.reaction_ensemble.WidomInsertion( kT=1., seed=12, x=1) with self.assertRaisesRegex(ValueError, "Invalid value for 'kT'"): espressomd.reaction_ensemble.ReactionEnsemble( - kT=-1., exclusion_radius=1., seed=12) - with self.assertRaisesRegex(ValueError, "Invalid value for 'exclusion_radius'"): + kT=-1., exclusion_range=1., seed=12, exclusion_radius_per_type={1: 0.1}) + with self.assertRaisesRegex(ValueError, "Invalid value for 'exclusion_range'"): espressomd.reaction_ensemble.ReactionEnsemble( - kT=1., exclusion_radius=-1., seed=12) + kT=1., exclusion_range=-1., seed=12, exclusion_radius_per_type={1: 0.1}) if __name__ == "__main__": From ca4b75d5fa3fcccbd3c66b3be0d2c26e7c5e66b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 17 Mar 2022 17:34:52 +0100 Subject: [PATCH 89/99] testsuite: Fix regressions in test cases Check Particle setters/getters on empty myconfig files. Fix incorrect position of the LB boundary in the VTK test. Make VirtualSitesRelative tests independent from each other and faster using less particles (runtime scales with n_part^2). Fix MAX_NUM_PROC regression in the checkpoint test CMake logic. --- samples/grand_canonical.py | 2 +- src/core/unit_tests/Particle_test.cpp | 20 +++++--- testsuite/python/CMakeLists.txt | 14 ++--- testsuite/python/field_test.py | 12 ++--- .../interactions_non-bonded_interface.py | 7 ++- testsuite/python/lb_vtk.py | 2 +- testsuite/python/particle.py | 46 ++++++++--------- testsuite/python/random_pairs.py | 2 +- testsuite/python/reaction_methods.py | 51 ++++++------------- testsuite/python/virtual_sites_relative.py | 27 +++++----- 10 files changed, 83 insertions(+), 100 deletions(-) diff --git a/samples/grand_canonical.py b/samples/grand_canonical.py index 8326e9c8d76..7fe1c2b4ad1 100644 --- a/samples/grand_canonical.py +++ b/samples/grand_canonical.py @@ -38,7 +38,7 @@ import espressomd.reaction_ensemble import espressomd.electrostatics -required_features = ["P3M", "EXTERNAL_FORCES", "WCA"] +required_features = ["P3M", "WCA"] espressomd.assert_features(required_features) parser = argparse.ArgumentParser(epilog=__doc__ + epilog) diff --git a/src/core/unit_tests/Particle_test.cpp b/src/core/unit_tests/Particle_test.cpp index af08ba21579..2726a7b7621 100644 --- a/src/core/unit_tests/Particle_test.cpp +++ b/src/core/unit_tests/Particle_test.cpp @@ -243,8 +243,6 @@ BOOST_AUTO_TEST_CASE(rattle_constructors) { } #endif // BOND_CONSTRAINT -#ifdef EXTERNAL_FORCES -#ifdef ROTATION BOOST_AUTO_TEST_CASE(particle_bitfields) { auto p = Particle(); @@ -255,24 +253,32 @@ BOOST_AUTO_TEST_CASE(particle_bitfields) { BOOST_CHECK(not p.can_rotate_around(1)); // check setting of one axis +#ifdef EXTERNAL_FORCES p.set_fixed_along(1, true); - p.set_can_rotate_around(1, true); BOOST_CHECK(p.is_fixed_along(1)); - BOOST_CHECK(p.can_rotate_around(1)); BOOST_CHECK(p.has_fixed_coordinates()); +#endif +#ifdef ROTATION + p.set_can_rotate_around(1, true); + BOOST_CHECK(p.can_rotate_around(1)); BOOST_CHECK(p.can_rotate()); +#endif // check that unsetting is properly registered +#ifdef EXTERNAL_FORCES p.set_fixed_along(1, false); - p.set_can_rotate_around(1, false); BOOST_CHECK(not p.has_fixed_coordinates()); +#endif +#ifdef ROTATION + p.set_can_rotate_around(1, false); BOOST_CHECK(not p.can_rotate()); +#endif // check setting of all flags at once +#ifdef ROTATION p.set_can_rotate_all_axes(); BOOST_CHECK(p.can_rotate_around(0)); BOOST_CHECK(p.can_rotate_around(1)); BOOST_CHECK(p.can_rotate_around(2)); +#endif } -#endif // ROTATION -#endif // EXTERNAL_FORCES \ No newline at end of file diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index 77cd2112bec..38a9b15527f 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -14,7 +14,7 @@ function(PYTHON_TEST) set(TEST_FILE ${TEST_FILE_CONFIGURED}) if(NOT DEFINED TEST_MAX_NUM_PROC) - set(TEST_MAX_NUM_PROC 1) + set(TEST_MAX_NUM_PROC 4) endif() if(${TEST_MAX_NUM_PROC} GREATER ${TEST_NP}) @@ -61,20 +61,20 @@ function(CHECKPOINT_TEST) endif() if(TEST_SUFFIX) set(TEST_ARGUMENTS "Test_suffix_${TEST_SUFFIX}__${TEST_MODES}") - set(TEST_SUFFIX "${TEST_MODES}_${TEST_SUFFIX}") + set(TEST_SUFFIX "_${TEST_MODES}_${TEST_SUFFIX}") else() set(TEST_ARGUMENTS "Test__${TEST_MODES}") - set(TEST_SUFFIX "${TEST_MODES}") + set(TEST_SUFFIX "_${TEST_MODES}") endif() python_test( - FILE save_checkpoint.py MAX_NUM_PROC ${TEST_NPROCS} LABELS ${TEST_LABELS} - SUFFIX ${TEST_SUFFIX} ARGUMENTS ${TEST_ARGUMENTS} DEPENDENCIES - unittest_generator.py) + FILE save_checkpoint.py MAX_NUM_PROC ${TEST_MAX_NUM_PROC} LABELS + ${TEST_LABELS} SUFFIX ${TEST_SUFFIX} ARGUMENTS ${TEST_ARGUMENTS} + DEPENDENCIES unittest_generator.py) python_test( FILE test_checkpoint.py MAX_NUM_PROC - ${TEST_NPROCS} + ${TEST_MAX_NUM_PROC} LABELS ${TEST_LABELS} SUFFIX diff --git a/testsuite/python/field_test.py b/testsuite/python/field_test.py index 327b8ebaaf9..27b5aa13e11 100644 --- a/testsuite/python/field_test.py +++ b/testsuite/python/field_test.py @@ -149,11 +149,10 @@ def test_homogeneous_flow_field(self): def test_potential_field(self): h = np.array([.2, .2, .2]) - box = np.array([10., 10., 10.]) scaling = 2.6 field_data = espressomd.constraints.PotentialField.field_from_fn( - box, h, self.potential) + self.system.box_l, h, self.potential) F = espressomd.constraints.PotentialField( field=field_data, @@ -186,10 +185,9 @@ def test_potential_field(self): @utx.skipIfMissingFeatures("ELECTROSTATICS") def test_electric_potential_field(self): h = np.array([.2, .2, .2]) - box = np.array([10., 10., 10.]) field_data = espressomd.constraints.ElectricPotential.field_from_fn( - box, h, self.potential) + self.system.box_l, h, self.potential) F = espressomd.constraints.ElectricPotential( field=field_data, grid_spacing=h) @@ -213,11 +211,10 @@ def test_electric_potential_field(self): def test_force_field(self): h = np.array([.8, .8, .8]) - box = np.array([10., 10., 10.]) scaling = 2.6 field_data = espressomd.constraints.ForceField.field_from_fn( - box, h, self.force) + self.system.box_l, h, self.force) F = espressomd.constraints.ForceField( field=field_data, @@ -245,11 +242,10 @@ def test_force_field(self): def test_flow_field(self): h = np.array([.8, .8, .8]) - box = np.array([10., 10., 10.]) gamma = 2.6 field_data = espressomd.constraints.FlowField.field_from_fn( - box, h, self.force) + self.system.box_l, h, self.force) F = espressomd.constraints.FlowField( field=field_data, diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index d893f28ef79..b842fb8394b 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -25,7 +25,12 @@ class Non_bonded_interactionsTests(ut.TestCase): - system = espressomd.System(box_l=[20.0, 20.0, 20.0]) + system = espressomd.System(box_l=[30.0, 30.0, 30.0]) + + def tearDown(self): + if espressomd.has_features(["LENNARD_JONES"]): + self.system.non_bonded_inter[0, 0].lennard_jones.set_params( + epsilon=0., sigma=0., cutoff=0., shift=0.) def intersMatch(self, inType, outInter, inParams, outParams, msg_long): """Check, if the interaction type set and gotten back as well as the diff --git a/testsuite/python/lb_vtk.py b/testsuite/python/lb_vtk.py index 7ed32ebef82..8cd8e065c02 100644 --- a/testsuite/python/lb_vtk.py +++ b/testsuite/python/lb_vtk.py @@ -56,7 +56,7 @@ def set_lbf(self): self.system.lbboundaries.add(espressomd.lbboundaries.LBBoundary( shape=espressomd.shapes.Wall(normal=[1, 0, 0], dist=1.5))) self.system.lbboundaries.add(espressomd.lbboundaries.LBBoundary( - shape=espressomd.shapes.Wall(normal=[-1, 0, 0], dist=-10.5))) + shape=espressomd.shapes.Wall(normal=[-1, 0, 0], dist=-8.5))) return lbf def parse_vtk(self, filepath, name, shape): diff --git a/testsuite/python/particle.py b/testsuite/python/particle.py index 812b60e0d0c..43f704c105e 100644 --- a/testsuite/python/particle.py +++ b/testsuite/python/particle.py @@ -182,29 +182,29 @@ def test_gamma_rot_single(self): if espressomd.has_features(["VIRTUAL_SITES"]): test_virtual = generateTestForScalarProperty("virtual", 1) - if espressomd.has_features(["VIRTUAL_SITES_RELATIVE"]): - def test_yy_vs_relative(self): - self.system.part.add(id=0, pos=(0, 0, 0)) - p1 = self.system.part.add(id=1, pos=(0, 0, 0)) - p1.vs_relative = (0, 5.0, (0.5, -0.5, -0.5, -0.5)) - p1.vs_quat = [1, 2, 3, 4] - np.testing.assert_array_equal( - p1.vs_quat, [1, 2, 3, 4]) - res = p1.vs_relative - self.assertEqual(res[0], 0, "vs_relative: " + res.__str__()) - self.assertEqual(res[1], 5.0, "vs_relative: " + res.__str__()) - np.testing.assert_allclose( - res[2], np.array((0.5, -0.5, -0.5, -0.5)), - err_msg="vs_relative: " + res.__str__(), atol=self.tol) - # check exceptions - with self.assertRaisesRegex(ValueError, "needs input in the form"): - p1.vs_relative = (0, 5.0) - with self.assertRaisesRegex(ValueError, "particle id has to be given as an int"): - p1.vs_relative = ('0', 5.0, (1, 0, 0, 0)) - with self.assertRaisesRegex(ValueError, "distance has to be given as a float"): - p1.vs_relative = (0, '5', (1, 0, 0, 0)) - with self.assertRaisesRegex(ValueError, "quaternion has to be given as a tuple of 4 floats"): - p1.vs_relative = (0, 5.0, (1, 0, 0)) + + @utx.skipIfMissingFeatures(["VIRTUAL_SITES_RELATIVE"]) + def test_vs_relative(self): + self.system.part.add(id=0, pos=(0, 0, 0)) + p1 = self.system.part.add(id=1, pos=(0, 0, 0)) + p1.vs_relative = (0, 5.0, (0.5, -0.5, -0.5, -0.5)) + p1.vs_quat = [1, 2, 3, 4] + np.testing.assert_array_equal(p1.vs_quat, [1, 2, 3, 4]) + res = p1.vs_relative + self.assertEqual(res[0], 0, f"vs_relative: {res}") + self.assertEqual(res[1], 5.0, f"vs_relative: {res}") + np.testing.assert_allclose( + res[2], np.array((0.5, -0.5, -0.5, -0.5)), + err_msg=f"vs_relative: {res}", atol=self.tol) + # check exceptions + with self.assertRaisesRegex(ValueError, "needs input in the form"): + p1.vs_relative = (0, 5.0) + with self.assertRaisesRegex(ValueError, "particle id has to be given as an int"): + p1.vs_relative = ('0', 5.0, (1, 0, 0, 0)) + with self.assertRaisesRegex(ValueError, "distance has to be given as a float"): + p1.vs_relative = (0, '5', (1, 0, 0, 0)) + with self.assertRaisesRegex(ValueError, "quaternion has to be given as a tuple of 4 floats"): + p1.vs_relative = (0, 5.0, (1, 0, 0)) @utx.skipIfMissingFeatures("DIPOLES") def test_contradicting_properties_dip_dipm(self): diff --git a/testsuite/python/random_pairs.py b/testsuite/python/random_pairs.py index 5c4b19d2e7c..2d9a31354ef 100644 --- a/testsuite/python/random_pairs.py +++ b/testsuite/python/random_pairs.py @@ -91,7 +91,7 @@ def check_n_squared(self, n2_pairs): def test(self): periods = [0, 1] - self.system.periodicity = True, True, True + self.system.periodicity = [True, True, True] check_non_bonded_loop_trace(self.system) for periodicity in itertools.product(periods, periods, periods): diff --git a/testsuite/python/reaction_methods.py b/testsuite/python/reaction_methods.py index 4a568aea974..d82a5119698 100644 --- a/testsuite/python/reaction_methods.py +++ b/testsuite/python/reaction_methods.py @@ -72,28 +72,20 @@ def check_reaction_parameters(reactions, parameters): method.exclusion_range, exclusion_range, delta=1e-10) - if exclusion_radius_per_type: # Avoid this test for widom method + if not isinstance(method, espressomd.reaction_ensemble.WidomInsertion): self.assertEqual( - list( - method.exclusion_radius_per_type.keys()), - [1]) + list(method.exclusion_radius_per_type.keys()), [1]) self.assertAlmostEqual( method.exclusion_radius_per_type[1], exclusion_radius_per_type[1], delta=1e-10) method.exclusion_radius_per_type = {2: 0.2} self.assertEqual( - list( - method.exclusion_radius_per_type.keys()), - [2]) + list(method.exclusion_radius_per_type.keys()), [2]) self.assertAlmostEqual( - method.exclusion_radius_per_type[2], - 0.2, - delta=1e-10) + method.exclusion_radius_per_type[2], 0.2, delta=1e-10) self.assertAlmostEqual( - method.get_volume(), - self.system.volume(), - delta=1e-10) + method.get_volume(), self.system.volume(), delta=1e-10) method.set_volume(volume=1.) self.assertAlmostEqual(method.get_volume(), 1., delta=1e-10) self.assertEqual(method.get_non_interacting_type(), 100) @@ -165,36 +157,23 @@ def check_reaction_parameters(reactions, parameters): self.assertEqual(len(method.reactions), 0) def test_interface(self): + params = {'exclusion_range': 0.8, + 'exclusion_radius_per_type': {1: 0.1}} + # reaction ensemble method = espressomd.reaction_ensemble.ReactionEnsemble( - kT=1.5, exclusion_range=0.8, seed=12, exclusion_radius_per_type={1: 0.1}) - self.check_interface( - method, - kT=1.5, - exclusion_range=0.8, - gamma=1.2, - exclusion_radius_per_type={ - 1: 0.1}) + kT=1.4, seed=12, **params) + self.check_interface(method, kT=1.4, gamma=1.2, **params) # constant pH ensemble method = espressomd.reaction_ensemble.ConstantpHEnsemble( - kT=1.5, exclusion_range=0.8, seed=12, constant_pH=10, exclusion_radius_per_type={1: 0.1}) - self.check_interface( - method, - kT=1.5, - exclusion_range=0.8, - gamma=1.2, - exclusion_radius_per_type={ - 1: 0.1}) + kT=1.5, seed=14, constant_pH=10, **params) + self.check_interface(method, kT=1.5, gamma=1.2, **params) # Widom insertion - method = espressomd.reaction_ensemble.WidomInsertion(kT=1.6, seed=12) - self.check_interface( - method, - kT=1.6, - exclusion_range=0., - gamma=1., - exclusion_radius_per_type={}) + method = espressomd.reaction_ensemble.WidomInsertion(kT=1.6, seed=16) + self.check_interface(method, kT=1.6, gamma=1., exclusion_range=0., + exclusion_radius_per_type={}) def test_exceptions(self): single_reaction_params = { diff --git a/testsuite/python/virtual_sites_relative.py b/testsuite/python/virtual_sites_relative.py index cefc2d2b86c..d48fb5f16eb 100644 --- a/testsuite/python/virtual_sites_relative.py +++ b/testsuite/python/virtual_sites_relative.py @@ -24,14 +24,21 @@ import tests_common -@utx.skipIfMissingFeatures("VIRTUAL_SITES_RELATIVE") +@utx.skipIfMissingFeatures(["VIRTUAL_SITES_RELATIVE", "LENNARD_JONES"]) class VirtualSites(ut.TestCase): system = espressomd.System(box_l=[1.0, 1.0, 1.0]) np.random.seed(42) + def setUp(self): + self.system.box_l = [10.0, 10.0, 10.0] + def tearDown(self): self.system.part.clear() + self.system.thermostat.turn_off() + self.system.integrator.set_vv() + self.system.non_bonded_inter[0, 0].lennard_jones.set_params( + epsilon=0., sigma=0., cutoff=0., shift=0.) def multiply_quaternions(self, a, b): return np.array( @@ -85,8 +92,10 @@ def test_aa_method_switching(self): espressomd.virtual_sites.VirtualSitesRelative) def test_vs_quat(self): + self.system.time_step = 0.01 + self.system.min_global_cut = 0.23 # First check that quaternion of virtual particle is unchanged if - # have_quaterion is false. + # have_quaternion is false. self.system.virtual_sites = espressomd.virtual_sites.VirtualSitesRelative( have_quaternion=False) self.assertFalse(self.system.virtual_sites.have_quaternion) @@ -129,20 +138,12 @@ def test_vs_quat(self): # Check exceptions. with self.assertRaisesRegex(ValueError, "Argument of vs_auto_relate_to has to be of type ParticleHandle or int"): p2.vs_auto_relate_to('0') - try: - p2.vs_auto_relate_to(p1) - except BaseException: - self.fail('Failed to set a vs from a particle handle') def test_pos_vel_forces(self): system = self.system system.cell_system.skin = 0.3 system.virtual_sites = espressomd.virtual_sites.VirtualSitesRelative() - system.box_l = [10, 10, 10] system.time_step = 0.004 - system.thermostat.turn_off() - system.non_bonded_inter[0, 0].lennard_jones.set_params( - epsilon=0, sigma=0, cutoff=0, shift=0) # Check setting of min_global_cut system.min_global_cut = 0.23 @@ -215,7 +216,7 @@ def run_test_lj(self): system = self.system system.virtual_sites = espressomd.virtual_sites.VirtualSitesRelative() # Parameters - n = 90 + n = 40 phi = 0.6 sigma = 1. eps = .025 @@ -296,10 +297,6 @@ def run_test_lj(self): self.assertNotAlmostEqual(enegry_pre_change, enegry_post_change) self.assertNotAlmostEqual(pressure_pre_change, pressure_post_change) - # Turn off lj interaction - system.non_bonded_inter[0, 0].lennard_jones.set_params( - epsilon=0, sigma=0, cutoff=0, shift=0) - def test_lj(self): """Run LJ fluid test for different cell systems.""" system = self.system From 0e09fbf733d737fed45dc31d0d7dcc2bebd26f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 17 Mar 2022 17:46:51 +0100 Subject: [PATCH 90/99] testsuite: Simplify python tests Use a temporary directory in I/O tests to automate file cleanup. Improve testing of importlib_wrapper and reduce code duplication. --- testsuite/python/h5md.py | 64 +++++++++------------ testsuite/scripts/test_importlib_wrapper.py | 15 +++++ testsuite/scripts/tutorials/test_convert.py | 49 ++++++---------- 3 files changed, 60 insertions(+), 68 deletions(-) diff --git a/testsuite/python/h5md.py b/testsuite/python/h5md.py index de8a7427e89..57ede3d2ff2 100644 --- a/testsuite/python/h5md.py +++ b/testsuite/python/h5md.py @@ -28,6 +28,7 @@ import espressomd import espressomd.interactions import espressomd.io.writer +import tempfile try: import h5py # h5py has to be imported *after* espressomd (MPI) skipIfMissingPythonPackage = utx.no_skip @@ -46,18 +47,15 @@ class H5mdTests(ut.TestCase): Test the core implementation of writing hdf5 files. """ - system = espressomd.System(box_l=[1.0, 1.0, 1.0]) - # avoid particles to be set outside of the main box, otherwise particle - # positions are folded in the core when writing out and we cannot directly - # compare positions in the dataset and where particles were set. One would - # need to unfold the positions of the hdf5 file. - box_l = N_PART / 2.0 - system.box_l = [box_l, box_l, box_l] + box_l = N_PART // 2 + box_l = [box_l, box_l + 1, box_l + 2] + system = espressomd.System(box_l=box_l) system.cell_system.skin = 0.4 system.time_step = 0.01 + # set particles outside the main box to verify position folding for i in range(N_PART): - p = system.part.add(id=i, pos=np.array(3 * [i], dtype=float), + p = system.part.add(id=i, pos=np.array(3 * [i - 4], dtype=float), v=np.array([1.0, 2.0, 3.0]), type=23) if espressomd.has_features(['MASS']): p.mass = 2.3 @@ -77,17 +75,17 @@ class H5mdTests(ut.TestCase): @classmethod def setUpClass(cls): - if os.path.isfile('test.h5'): - os.remove('test.h5') + cls.temp_dir = tempfile.TemporaryDirectory() + cls.temp_file = os.path.join(cls.temp_dir.name, 'test.h5') h5_units = espressomd.io.writer.h5md.UnitSystem( time='ps', mass='u', length='m', charge='e') h5 = espressomd.io.writer.h5md.H5md( - file_path="test.h5", unit_system=h5_units) + file_path=cls.temp_file, unit_system=h5_units) h5.write() h5.write() h5.flush() h5.close() - cls.py_file = h5py.File("test.h5", 'r') + cls.py_file = h5py.File(cls.temp_file, 'r') cls.py_pos = cls.py_file['particles/atoms/position/value'][1] cls.py_img = cls.py_file['particles/atoms/image/value'][1] cls.py_mass = cls.py_file['particles/atoms/mass/value'][1] @@ -102,10 +100,10 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): - os.remove("test.h5") + cls.temp_dir.cleanup() def test_opening(self): - h5 = espressomd.io.writer.h5md.H5md(file_path="test.h5") + h5 = espressomd.io.writer.h5md.H5md(file_path=self.temp_file) h5.close() def test_box(self): @@ -126,9 +124,10 @@ def test_metadata(self): def test_pos(self): """Test if positions have been written properly.""" - np.testing.assert_allclose( - np.array([3 * [float(i) % self.box_l] for i in range(N_PART)]), - np.array([x for (_, x) in sorted(zip(self.py_id, self.py_pos))])) + pos_ref = np.outer(np.arange(N_PART) - 4, [1, 1, 1]) + pos_ref = np.mod(pos_ref, self.box_l) + pos_read = [x for (_, x) in sorted(zip(self.py_id, self.py_pos))] + np.testing.assert_allclose(pos_read, pos_ref) def test_time(self): """Test for time dataset.""" @@ -136,11 +135,10 @@ def test_time(self): def test_img(self): """Test if images have been written properly.""" - images = np.append(np.zeros((int(N_PART / 2), 3)), - np.ones((int(N_PART / 2), 3))) - images = images.reshape(N_PART, 3) - np.testing.assert_allclose( - [x for (_, x) in sorted(zip(self.py_id, self.py_img))], images) + pos_ref = np.outer(np.arange(N_PART) - 4, [1, 1, 1]) + images_ref = np.floor_divide(pos_ref, self.box_l) + images_read = [x for (_, x) in sorted(zip(self.py_id, self.py_img))] + np.testing.assert_allclose(images_read, images_ref) @utx.skipIfMissingFeatures("MASS") def test_mass(self): @@ -185,22 +183,16 @@ def test_script(self): self.assertEqual(data, ref) def test_units(self): - self.assertEqual( - self.py_file['particles/atoms/id/time'].attrs['unit'], b'ps') - self.assertEqual( - self.py_file['particles/atoms/position/value'].attrs['unit'], b'm') + def get_unit(path): + return self.py_file[path].attrs['unit'] + self.assertEqual(get_unit('particles/atoms/id/time'), b'ps') + self.assertEqual(get_unit('particles/atoms/position/value'), b'm') if espressomd.has_features(['ELECTROSTATICS']): - self.assertEqual( - self.py_file['particles/atoms/charge/value'].attrs['unit'], b'e') + self.assertEqual(get_unit('particles/atoms/charge/value'), b'e') if espressomd.has_features(['MASS']): - self.assertEqual( - self.py_file['particles/atoms/mass/value'].attrs['unit'], b'u') - self.assertEqual( - self.py_file['particles/atoms/force/value'].attrs['unit'], - b'm u ps-2') - self.assertEqual( - self.py_file['particles/atoms/velocity/value'].attrs['unit'], - b'm ps-1') + self.assertEqual(get_unit('particles/atoms/mass/value'), b'u') + self.assertEqual(get_unit('particles/atoms/force/value'), b'm u ps-2') + self.assertEqual(get_unit('particles/atoms/velocity/value'), b'm ps-1') def test_links(self): time_ref = self.py_id_time diff --git a/testsuite/scripts/test_importlib_wrapper.py b/testsuite/scripts/test_importlib_wrapper.py index 9d959edae26..e21904ecb6f 100644 --- a/testsuite/scripts/test_importlib_wrapper.py +++ b/testsuite/scripts/test_importlib_wrapper.py @@ -380,6 +380,21 @@ def test_delimit_statements(self): linenos_out = iw.delimit_statements(source_code) self.assertEqual(linenos_out, linenos_exp) + def test_ipython_magics(self): + lines = [ + '%matplotlib inline', + '%%matplotlib notebook', + 'import matplotlib.pyplot as plt', + '__doc__="%matplotlib inline"', + ] + code = '\n'.join(lines) + code_protected = iw.protect_ipython_magics(code) + code_protected_ref = f'\n{code}'.replace( + '\n%', '\n#_IPYTHON_MAGIC_%')[1:] + self.assertEqual(code_protected, code_protected_ref) + code_deprotected = iw.deprotect_ipython_magics(code_protected) + self.assertEqual(code_deprotected, code) + if __name__ == "__main__": ut.main() diff --git a/testsuite/scripts/tutorials/test_convert.py b/testsuite/scripts/tutorials/test_convert.py index 1386ab93826..0f24519cdc2 100644 --- a/testsuite/scripts/tutorials/test_convert.py +++ b/testsuite/scripts/tutorials/test_convert.py @@ -85,11 +85,18 @@ class HtmlRunner(ut.TestCase): "version": ".".join(map(str, sys.version_info[:3]))} } - def failed_to_run(self, cmd): - traceback.print_exc() - self.fail('Could not run @CMAKE_BINARY_DIR@/pypresso ' - '@CMAKE_BINARY_DIR@/doc/tutorials/convert.py ' + - ' '.join(cmd)) + def run_command(self, cmd, output=None): + error_msg = ("Could not run @CMAKE_BINARY_DIR@/pypresso " + "@CMAKE_BINARY_DIR@/doc/tutorials/convert.py " + f"{' '.join(cmd)}") + try: + args = convert.parser.parse_args(cmd) + args.callback(args) + except BaseException: + traceback.print_exc() + self.fail(error_msg) + if output is not None: + self.assertTrue(os.path.isfile(output), f"{output} not created") def test_html_wrapper(self): f_input = '@CMAKE_CURRENT_BINARY_DIR@/test_convert_notebook.ipynb' @@ -114,12 +121,7 @@ def test_html_wrapper(self): '--scripts', f_script, '--substitutions', 'global_var=20', '--execute'] - try: - args = convert.parser.parse_args(cmd) - args.callback(args) - except BaseException: - self.failed_to_run(cmd) - self.assertTrue(os.path.isfile(f_output), f_output + ' not created') + self.run_command(cmd, f_output) # read processed notebook with open(f_output, encoding='utf-8') as f: nb_output = nbformat.read(f, as_version=4) @@ -193,12 +195,7 @@ def test_exercise2_plugin(self): '--output', f_output, '--substitutions', 'global_var=20', '--exercise2', '--remove-empty-cells'] - try: - args = convert.parser.parse_args(cmd) - args.callback(args) - except BaseException: - self.failed_to_run(cmd) - self.assertTrue(os.path.isfile(f_output), f_output + ' not created') + self.run_command(cmd, f_output) # read processed notebook with open(f_output, encoding='utf-8') as f: nb_output = nbformat.read(f, as_version=4) @@ -246,11 +243,7 @@ def test_exercise2_conversion(self): nbformat.write(nb, f) # run command and check for errors cmd = ['exercise2', '--to-py', f_input] - try: - args = convert.parser.parse_args(cmd) - args.callback(args) - except BaseException: - self.failed_to_run(cmd) + self.run_command(cmd, f_input) # read processed notebook with open(f_input, encoding='utf-8') as f: nb_output = nbformat.read(f, as_version=4) @@ -267,11 +260,7 @@ def test_exercise2_conversion(self): self.assertEqual(next(cells, 'EOF'), 'EOF') # run command and check for errors cmd = ['exercise2', '--to-md', f_input] - try: - args = convert.parser.parse_args(cmd) - args.callback(args) - except BaseException: - self.failed_to_run(cmd) + self.run_command(cmd, f_input) # read processed notebook with open(f_input, encoding='utf-8') as f: nb_output = nbformat.read(f, as_version=4) @@ -305,11 +294,7 @@ def test_exercise2_autopep8(self): nbformat.write(nb, f) # run command and check for errors cmd = ['exercise2', '--pep8', f_input] - try: - args = convert.parser.parse_args(cmd) - args.callback(args) - except BaseException: - self.failed_to_run(cmd) + self.run_command(cmd) # read processed notebook with open(f_input, encoding='utf-8') as f: nb_output = nbformat.read(f, as_version=4) From aa3fc5d04cec7c2d07cbfbbd35421b36f1f82931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 17 Mar 2022 18:23:04 +0100 Subject: [PATCH 91/99] core: Unit test the rotation code --- src/core/rotation.cpp | 3 +- src/core/unit_tests/CMakeLists.txt | 2 + src/core/unit_tests/Particle_test.cpp | 2 + src/core/unit_tests/rotation_test.cpp | 219 ++++++++++++++++++++++++++ 4 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 src/core/unit_tests/rotation_test.cpp diff --git a/src/core/rotation.cpp b/src/core/rotation.cpp index d402130f5a1..f9adc15c32c 100644 --- a/src/core/rotation.cpp +++ b/src/core/rotation.cpp @@ -117,7 +117,8 @@ static void define_Qdd(Particle const &p, Utils::Quaternion &Qd, * * For very high angular velocities (e.g. if the product of @p time_step * with the largest component of @ref ParticleMomentum::omega "p.omega()" - * is superior to ~2.0), the calculation might fail. + * is superior to ~2.0) and for @p time_step superior or equal to unity, + * the calculation might fail. * * \todo implement for fixed_coord_flag */ diff --git a/src/core/unit_tests/CMakeLists.txt b/src/core/unit_tests/CMakeLists.txt index 95560b0931a..fdc28c5c5c7 100644 --- a/src/core/unit_tests/CMakeLists.txt +++ b/src/core/unit_tests/CMakeLists.txt @@ -37,6 +37,8 @@ unit_test(NAME p3m_test SRC p3m_test.cpp DEPENDS EspressoUtils) unit_test(NAME link_cell_test SRC link_cell_test.cpp DEPENDS EspressoUtils) unit_test(NAME Particle_test SRC Particle_test.cpp DEPENDS EspressoUtils Boost::serialization) +unit_test(NAME rotation_test SRC rotation_test.cpp DEPENDS EspressoUtils + EspressoCore) unit_test(NAME field_coupling_couplings SRC field_coupling_couplings_test.cpp DEPENDS EspressoUtils) unit_test(NAME field_coupling_fields SRC field_coupling_fields_test.cpp DEPENDS diff --git a/src/core/unit_tests/Particle_test.cpp b/src/core/unit_tests/Particle_test.cpp index 2726a7b7621..86a0af10410 100644 --- a/src/core/unit_tests/Particle_test.cpp +++ b/src/core/unit_tests/Particle_test.cpp @@ -280,5 +280,7 @@ BOOST_AUTO_TEST_CASE(particle_bitfields) { BOOST_CHECK(p.can_rotate_around(0)); BOOST_CHECK(p.can_rotate_around(1)); BOOST_CHECK(p.can_rotate_around(2)); + p.set_cannot_rotate_all_axes(); + BOOST_CHECK(not p.can_rotate()); #endif } diff --git a/src/core/unit_tests/rotation_test.cpp b/src/core/unit_tests/rotation_test.cpp new file mode 100644 index 00000000000..5ff5d98cb6c --- /dev/null +++ b/src/core/unit_tests/rotation_test.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (C) 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 . + */ + +#define BOOST_TEST_MODULE rotation test +#define BOOST_TEST_DYN_LINK +#include + +#include "Particle.hpp" +#include "config.hpp" +#include "rotation.hpp" + +#include +#include +#include + +#include +#include +#include + +#ifdef ROTATION + +auto constexpr tol = 5. * 100. * std::numeric_limits::epsilon(); + +namespace Testing { +std::tuple, Utils::Vector3d> +setup_trivial_quat(int i, Utils::Vector3d const &v_in) { + auto quat = Utils::Quaternion{{0., 0., 0., 0.}}; + quat[i] = 1.; + auto v_ref = v_in; + if (i) { + v_ref *= -1.; + v_ref[i - 1] *= -1.; + } + return std::make_tuple(quat, v_ref); +} +} // namespace Testing + +BOOST_AUTO_TEST_CASE(convert_vector_space_to_body_test) { + auto const t_in = Utils::Vector3d{{1., 2., 3.}}; + for (int i : {0, 1, 2, 3}) { + auto p = Particle(); + Utils::Vector3d t_ref; + std::tie(p.quat(), t_ref) = Testing::setup_trivial_quat(i, t_in); + auto const t_out = convert_vector_space_to_body(p, t_in); + for (int j : {0, 1, 2}) { + BOOST_CHECK_CLOSE(t_out[j], t_ref[j], tol); + } + } +} + +BOOST_AUTO_TEST_CASE(convert_torque_to_body_frame_apply_fix_test) { + auto const t_in = Utils::Vector3d{{1., 2., 3.}}; + { + // test particle torque conversion + for (int i : {0, 1, 2, 3}) { + auto p = Particle(); + p.set_can_rotate_all_axes(); + Utils::Vector3d t_ref; + std::tie(p.quat(), t_ref) = Testing::setup_trivial_quat(i, t_in); + p.torque() = t_in; + convert_torque_to_body_frame_apply_fix(p); + auto const t_out = p.torque(); + for (int j : {0, 1, 2}) { + BOOST_CHECK_CLOSE(t_out[j], t_ref[j], tol); + } + } + } + { + // torque is set to zero for axes without rotation + for (int j : {0, 1, 2}) { + auto p = Particle(); + p.set_can_rotate_all_axes(); + p.set_can_rotate_around(j, false); + p.quat() = Utils::Quaternion::identity(); + p.torque() = t_in; + auto t_ref = t_in; + t_ref[j] = 0.; + convert_torque_to_body_frame_apply_fix(p); + BOOST_TEST(p.torque() == t_ref, boost::test_tools::per_element()); + } + } + { + // torque is always zero for non-rotatable particles + auto p = Particle(); + p.set_cannot_rotate_all_axes(); + p.quat() = Utils::Quaternion::identity(); + p.torque() = t_in; + convert_torque_to_body_frame_apply_fix(p); + auto const t_ref = Utils::Vector3d{}; + BOOST_TEST(p.torque() == t_ref, boost::test_tools::per_element()); + } +} + +BOOST_AUTO_TEST_CASE(rotate_particle_body_test) { + auto p = Particle(); + p.quat() = {1., 2., 3., 4.}; + { + // fixed particles are unaffected, quaternion is identical to original + p.set_cannot_rotate_all_axes(); + auto const phi = 2.; + auto const quat = local_rotate_particle_body(p, {0., 0., 1.}, phi); + BOOST_TEST((quat == p.quat())); + } + { + // edge case: null rotation throws an exception + p.set_can_rotate_around(2, true); + auto const phi = 2.; + BOOST_CHECK_THROW(local_rotate_particle_body(p, {1., 1., 0.}, phi), + std::exception); + } + { + // an angle of zero has no effect, quaternion is identical to original + p.set_can_rotate_all_axes(); + auto const phi = 0.; + auto const quat = local_rotate_particle_body(p, {1., 2., 3.}, phi); + BOOST_TEST((quat == p.quat())); + } + { + // an angle of pi around the z-axis flips the quaternion sequence + p.set_can_rotate_all_axes(); + auto const phi = Utils::pi(); + auto const quat = local_rotate_particle_body(p, {0., 0., 1.}, phi); + auto const quat_ref = Utils::Vector4d{{-4., 3., -2., 1.}}; + for (int i : {0, 1, 2, 3}) { + BOOST_CHECK_CLOSE(quat[i], quat_ref[i], tol); + } + } + { + // an angle of 2 pi around the z-axis flips the quaternion sign + p.set_can_rotate_all_axes(); + auto const phi = 2. * Utils::pi(); + auto const quat = local_rotate_particle_body(p, {0., 0., 1.}, phi); + auto const quat_ref = Utils::Vector4d{{-1., -2., -3., -4.}}; + for (int i : {0, 1, 2, 3}) { + BOOST_CHECK_CLOSE(quat[i], quat_ref[i], tol); + } + } +} + +BOOST_AUTO_TEST_CASE(propagate_omega_quat_particle_test) { + auto p = Particle(); + p.set_can_rotate_all_axes(); + { + // test edge case: null quaternion and no rotation + p.quat() = {0., 0., 0., 0.}; + p.omega() = {0., 0., 0.}; + propagate_omega_quat_particle(p, 0.01); + auto const quat = p.quat(); + auto const quat_ref = Utils::Quaternion::identity(); + for (int i : {0, 1, 2, 3}) { + BOOST_CHECK_CLOSE(quat[i], quat_ref[i], tol); + } + } + { + // test trivial cases with parameters extremely close to the limit: + // time step almost 1.0 and product of time step with omega almost 2.0 + auto const time_step = 0.99; + for (int j : {0, 1, 2}) { + p.quat() = Utils::Quaternion::identity(); + p.omega() = {0., 0., 0.}; + p.omega()[j] = 2.; + propagate_omega_quat_particle(p, time_step); + auto const quat = p.quat(); + auto quat_ref = Utils::Quaternion::identity(); + quat_ref[1 + j] = time_step; + quat_ref[0] = std::sqrt(1. - time_step * time_step); + for (int i : {0, 1, 2, 3}) { + BOOST_CHECK_CLOSE(quat[i], quat_ref[i], tol); + } + } + } +} + +#ifdef DIPOLES +BOOST_AUTO_TEST_CASE(convert_dip_to_quat_test) { + auto const quat_to_vector4d = [](Utils::Quaternion const &quat) { + return Utils::Vector4d{quat.data(), quat.data() + 4}; + }; + auto p = Particle(); + p.quat() = {1., 2., 3., 4.}; + { + auto const dipm = 0.8; + auto const pair = convert_dip_to_quat({0., 0., dipm}); + auto const quat = quat_to_vector4d(pair.first); + auto const quat_ref = Utils::Vector4d{{1., 0., 0., 0.}}; + for (int i : {0, 1, 2, 3}) { + BOOST_CHECK_CLOSE(quat[i], quat_ref[i], tol); + } + BOOST_CHECK_CLOSE(pair.second, dipm, tol); + } + { + auto const dipm = 1.6; + auto const pair = convert_dip_to_quat({dipm, 0., 0.}); + auto const quat = quat_to_vector4d(pair.first); + auto const quat_ref = Utils::Vector4d{{0.5, -0.5, 0.5, -0.5}}; + for (int i : {0, 1, 2, 3}) { + BOOST_CHECK_CLOSE(quat[i], quat_ref[i], tol); + } + BOOST_CHECK_CLOSE(pair.second, dipm, tol); + } +} +#endif // DIPOLES +#endif // ROTATION From 11939cce7a2e31d30f9a57857d7124d06178c317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 17 Mar 2022 18:55:59 +0100 Subject: [PATCH 92/99] core: MPI-safe virtual sites relative exceptions When a real particle is not found, queue a runtime error instead of throwing a fatal error. --- .../virtual_sites/VirtualSitesRelative.cpp | 84 +++++++++---------- testsuite/python/virtual_sites_relative.py | 14 +++- 2 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/core/virtual_sites/VirtualSitesRelative.cpp b/src/core/virtual_sites/VirtualSitesRelative.cpp index c88fb2a33fd..b80996bbef9 100644 --- a/src/core/virtual_sites/VirtualSitesRelative.cpp +++ b/src/core/virtual_sites/VirtualSitesRelative.cpp @@ -23,6 +23,7 @@ #include "Particle.hpp" #include "cells.hpp" +#include "errorhandling.hpp" #include "forces.hpp" #include "grid.hpp" #include "integrate.hpp" @@ -33,80 +34,74 @@ #include #include -#include - -namespace { /** * @brief Vector pointing from the real particle to the virtual site. * * @return Relative distance. */ -auto connection_vector( - Particle const &p_ref, - ParticleProperties::VirtualSitesRelativeParameters const &vs_rel) { +static auto connection_vector(Particle const &p_ref, Particle const &p_vs) { // Calculate the quaternion defining the orientation of the vector connecting // the virtual site and the real particle // This is obtained, by multiplying the quaternion representing the director // of the real particle with the quaternion of the virtual particle, which // specifies the relative orientation. auto const director = Utils::convert_quaternion_to_director( - p_ref.quat() * vs_rel.rel_orientation) + p_ref.quat() * p_vs.vs_relative().rel_orientation) .normalize(); - return vs_rel.distance * director; + return p_vs.vs_relative().distance * director; } /** * @brief Velocity of the virtual site * @param p_ref Reference particle for the virtual site. - * @param vs_rel Parameters for the virtual site. + * @param p_vs Virtual site. * @return Velocity of the virtual site. */ -Utils::Vector3d -velocity(Particle const &p_ref, - ParticleProperties::VirtualSitesRelativeParameters const &vs_rel) { - auto const d = connection_vector(p_ref, vs_rel); +static Utils::Vector3d velocity(Particle const &p_ref, Particle const &p_vs) { + auto const d = connection_vector(p_ref, p_vs); // Get omega of real particle in space-fixed frame auto const omega_space_frame = convert_vector_body_to_space(p_ref, p_ref.omega()); - // Obtain velocity from v=v_real particle + omega_real_particle \times - // director + // Obtain velocity from v = v_real particle + omega_real_particle * director return vector_product(omega_space_frame, d) + p_ref.v(); } /** - * @brief Get reference particle. + * @brief Get real particle tracked by a virtual site. * - * @param vs_rel Parameters to get the reference particle for. - * @return Pointer to reference particle. + * @param p Virtual site. + * @return Pointer to real particle if @p p is a virtual site, + * otherwise nullptr. If the real particle is not found, + * queue a runtime error. */ -Particle &get_reference_particle( - ParticleProperties::VirtualSitesRelativeParameters const &vs_rel) { +static Particle *get_reference_particle(Particle const &p) { + if (!p.is_virtual()) { + return nullptr; + } + auto const &vs_rel = p.vs_relative(); auto p_ref_ptr = cell_structure.get_local_particle(vs_rel.to_particle_id); if (!p_ref_ptr) { - throw std::runtime_error("No real particle associated with virtual site."); + runtimeErrorMsg() << "No real particle with id " << vs_rel.to_particle_id + << " for virtual site with id " << p.identity(); } - return *p_ref_ptr; + return p_ref_ptr; } /** * @brief Constraint force to hold the particles at its prescribed position. * - * @param f Force on the virtual site. * @param p_ref Reference particle. - * @param vs_rel Parameters. + * @param p_vs Virtual site. * @return Constraint force. */ -auto constraint_stress( - const Utils::Vector3d &f, const Particle &p_ref, - const ParticleProperties::VirtualSitesRelativeParameters &vs_rel) { +static auto constraint_stress(Particle const &p_ref, Particle const &p_vs) { /* The constraint force is minus the force on the particle, make it force * free. The counter force is translated by the connection vector to the * real particle, hence the virial stress is */ - return tensor_product(-f, connection_vector(p_ref, vs_rel)); + return tensor_product(-p_vs.force(), connection_vector(p_ref, p_vs)); } -} // namespace void VirtualSitesRelative::update() const { cell_structure.ghosts_update(Cells::DATA_PART_POSITION | @@ -114,12 +109,12 @@ void VirtualSitesRelative::update() const { auto const particles = cell_structure.local_particles(); for (auto &p : particles) { - if (!p.is_virtual()) + auto const *p_ref_ptr = get_reference_particle(p); + if (!p_ref_ptr) continue; - auto const &p_ref = get_reference_particle(p.vs_relative()); - - auto new_pos = p_ref.pos() + connection_vector(p_ref, p.vs_relative()); + auto const &p_ref = *p_ref_ptr; + auto new_pos = p_ref.pos() + connection_vector(p_ref, p); /* The shift has to respect periodic boundaries: if the reference * particles is not in the same image box, we potentially avoid shifting * to the other side of the box. */ @@ -129,7 +124,7 @@ void VirtualSitesRelative::update() const { fold_position(shift, image_shift, box_geo); p.image_box() = p_ref.image_box() - image_shift; - p.v() = velocity(p_ref, p.vs_relative()); + p.v() = velocity(p_ref, p); if (box_geo.type() == BoxType::LEES_EDWARDS) { auto const &shear_dir = box_geo.clees_edwards_bc().shear_direction; @@ -158,16 +153,15 @@ void VirtualSitesRelative::back_transfer_forces_and_torques() const { // Iterate over all the particles in the local cells for (auto &p : cell_structure.local_particles()) { - // We only care about virtual particles - if (!p.is_virtual()) + auto *p_ref_ptr = get_reference_particle(p); + if (!p_ref_ptr) continue; - auto &p_ref = get_reference_particle(p.vs_relative()); // Add forces and torques + auto &p_ref = *p_ref_ptr; p_ref.force() += p.force(); p_ref.torque() += - vector_product(connection_vector(p_ref, p.vs_relative()), p.force()) + - p.torque(); + vector_product(connection_vector(p_ref, p), p.force()) + p.torque(); } } @@ -175,13 +169,11 @@ void VirtualSitesRelative::back_transfer_forces_and_torques() const { Utils::Matrix VirtualSitesRelative::pressure_tensor() const { Utils::Matrix pressure_tensor = {}; - for (auto &p : cell_structure.local_particles()) { - if (!p.is_virtual()) - continue; - - auto const &p_ref = get_reference_particle(p.vs_relative()); - - pressure_tensor += constraint_stress(p.force(), p_ref, p.vs_relative()); + 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); + } } return pressure_tensor; diff --git a/testsuite/python/virtual_sites_relative.py b/testsuite/python/virtual_sites_relative.py index d48fb5f16eb..14657efbac1 100644 --- a/testsuite/python/virtual_sites_relative.py +++ b/testsuite/python/virtual_sites_relative.py @@ -39,6 +39,7 @@ def tearDown(self): self.system.integrator.set_vv() self.system.non_bonded_inter[0, 0].lennard_jones.set_params( epsilon=0., sigma=0., cutoff=0., shift=0.) + self.system.virtual_sites = espressomd.virtual_sites.VirtualSitesOff() def multiply_quaternions(self, a, b): return np.array( @@ -135,9 +136,20 @@ def test_vs_quat(self): self.system.integrator.run(1) self.assertAlmostEqual(np.dot(p1.director, p2.director), 0.0) - # Check exceptions. + def test_vs_exceptions(self): + system = self.system + system.virtual_sites = espressomd.virtual_sites.VirtualSitesRelative() + system.time_step = 0.01 + p2 = system.part.add(pos=[1.0, 1.0, 1.0], rotation=[1, 1, 1], id=2) + p3 = system.part.add(pos=[1.0, 1.0, 1.0], rotation=[1, 1, 1], id=3) + # relating to anything else other than a particle or id is not allowed with self.assertRaisesRegex(ValueError, "Argument of vs_auto_relate_to has to be of type ParticleHandle or int"): p2.vs_auto_relate_to('0') + # relating to a deleted particle is not allowed + with self.assertRaisesRegex(Exception, "No real particle with id 3 for virtual site with id 2"): + p2.vs_auto_relate_to(p3) + p3.remove() + system.integrator.run(0, recalc_forces=True) def test_pos_vel_forces(self): system = self.system From 6e3cf3235be1b591348c211e8a570c8931c8f4e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 17 Mar 2022 22:31:15 +0100 Subject: [PATCH 93/99] Improve exception mechanism Add missing calls to handle_errors() and remove superfluous ones. When calling a script interface method, call handle_errors() with a clear error message. Catch null quaternions in the script interface to avoid triggering a fatal error in boost::qvm. Prevent relative virtual sites from tracking themselves. --- src/core/integrate.cpp | 12 ++--- src/core/io/writer/h5md_core.hpp | 5 +- .../reaction_methods/ReactionAlgorithm.hpp | 27 ++++++----- .../tests/particle_tracking_test.cpp | 3 ++ src/core/virtual_sites.cpp | 14 +++--- .../virtual_sites/VirtualSitesRelative.cpp | 4 +- src/python/espressomd/cellsystem.pyx | 2 +- src/python/espressomd/integrate.pyx | 3 +- src/python/espressomd/interactions.pyx | 15 +++--- src/python/espressomd/particle_data.pxd | 2 +- src/python/espressomd/particle_data.pyx | 15 ++++-- src/python/espressomd/script_interface.pyx | 2 +- src/python/espressomd/system.pyx | 4 +- .../reaction_methods/ReactionAlgorithm.hpp | 4 +- testsuite/python/CMakeLists.txt | 1 + testsuite/python/field_test.py | 17 +++++++ testsuite/python/h5md.py | 31 +++++++++++++ testsuite/python/integrator_exceptions.py | 45 +++++++++++++++++- .../python/interactions_bonded_interface.py | 46 +++++++++++++++++++ .../interactions_non-bonded_interface.py | 13 ++++++ testsuite/python/particle.py | 10 ++++ testsuite/python/reaction_methods.py | 18 ++++++-- testsuite/python/virtual_sites_relative.py | 9 ++++ 23 files changed, 247 insertions(+), 55 deletions(-) diff --git a/src/core/integrate.cpp b/src/core/integrate.cpp index d3777995dec..b56f36c8b3a 100644 --- a/src/core/integrate.cpp +++ b/src/core/integrate.cpp @@ -61,6 +61,7 @@ #include #include +#include #include #include #include @@ -419,6 +420,9 @@ int integrate(int n_steps, int reuse_forces) { int python_integrate(int n_steps, bool recalc_forces_par, bool reuse_forces_par) { + + assert(n_steps >= 0); + // Override the signal handler so that the integrator obeys Ctrl+C SignalHandler sa(SIGINT, [](int) { ctrl_C = 1; }); @@ -431,12 +435,6 @@ int python_integrate(int n_steps, bool recalc_forces_par, reuse_forces = -1; } - /* go on with integrate */ - if (n_steps < 0) { - runtimeErrorMsg() << "illegal number of steps (must be >0)"; - return ES_ERROR; - } - /* if skin wasn't set, do an educated guess now */ if (!skin_set) { auto const max_cut = maximal_cutoff(); @@ -552,7 +550,7 @@ REGISTER_CALLBACK(mpi_set_time_step_local) void mpi_set_time_step(double time_s) { if (time_s <= 0.) - throw std::invalid_argument("time_step must be > 0."); + throw std::domain_error("time_step must be > 0."); if (lb_lbfluid_get_lattice_switch() != ActiveLB::NONE) check_tau_time_step_consistency(lb_lbfluid_get_tau(), time_s); mpi_call_all(mpi_set_time_step_local, time_s); diff --git a/src/core/io/writer/h5md_core.hpp b/src/core/io/writer/h5md_core.hpp index 30f4e4b15b4..31c08497375 100644 --- a/src/core/io/writer/h5md_core.hpp +++ b/src/core/io/writer/h5md_core.hpp @@ -217,9 +217,8 @@ struct incompatible_h5mdfile : public std::exception { struct left_backupfile : public std::exception { const char *what() const noexcept override { - return "A backup of the .h5 file exists. This usually means \ -that either you forgot to call the 'close' method or your simulation \ -crashed."; + return "A backup of the .h5 file exists. This usually means that either " + "you forgot to call the 'close' method or your simulation crashed."; } }; diff --git a/src/core/reaction_methods/ReactionAlgorithm.hpp b/src/core/reaction_methods/ReactionAlgorithm.hpp index c99a1109ef2..2444fe6112e 100644 --- a/src/core/reaction_methods/ReactionAlgorithm.hpp +++ b/src/core/reaction_methods/ReactionAlgorithm.hpp @@ -61,18 +61,7 @@ class ReactionAlgorithm { } this->kT = kT; this->exclusion_range = exclusion_range; - for (auto const &item : exclusion_radius_per_type) { - auto type = item.first; - auto exclusion_radius = item.second; - if (exclusion_radius < 0) { - std::stringstream ss; - ss << "Invalid excluded_radius value for type " << type - << " value: " << exclusion_radius; - std::string error_message = ss.str(); - throw std::domain_error(error_message); - } - } - this->exclusion_radius_per_type = exclusion_radius_per_type; + set_exclusion_radius_per_type(exclusion_radius_per_type); update_volume(); } @@ -109,6 +98,20 @@ class ReactionAlgorithm { volume = new_volume; } void update_volume(); + void + set_exclusion_radius_per_type(std::unordered_map const &map) { + for (auto const &item : map) { + auto const type = item.first; + auto const exclusion_radius = item.second; + if (exclusion_radius < 0.) { + throw std::domain_error("Invalid excluded_radius value for type " + + std::to_string(type) + ": radius " + + std::to_string(exclusion_radius)); + } + } + exclusion_radius_per_type = map; + } + virtual int do_reaction(int reaction_steps); void check_reaction_method() const; void remove_constraint() { m_reaction_constraint = ReactionConstraint::NONE; } diff --git a/src/core/reaction_methods/tests/particle_tracking_test.cpp b/src/core/reaction_methods/tests/particle_tracking_test.cpp index df14f1e5f32..b792b9e1761 100644 --- a/src/core/reaction_methods/tests/particle_tracking_test.cpp +++ b/src/core/reaction_methods/tests/particle_tracking_test.cpp @@ -57,6 +57,9 @@ BOOST_FIXTURE_TEST_CASE(particle_type_map_test, ParticleFactory) { // exception for random index that exceeds the number of particles BOOST_CHECK_THROW(get_random_p_id(type, 10), std::runtime_error); + // exception for untracked particle types + BOOST_CHECK_THROW(get_random_p_id(type + 1, 0), std::runtime_error); + // check particle selection BOOST_CHECK_EQUAL(get_random_p_id(type, 0), pid); } diff --git a/src/core/virtual_sites.cpp b/src/core/virtual_sites.cpp index e591277f0a2..629468a9b00 100644 --- a/src/core/virtual_sites.cpp +++ b/src/core/virtual_sites.cpp @@ -38,6 +38,7 @@ #include #include +#include #include namespace { @@ -66,12 +67,10 @@ calculate_vs_relate_to_params(Particle const &p_current, if (dist > min_global_cut && n_nodes > 1) { runtimeErrorMsg() << "Warning: The distance between virtual and non-virtual particle (" - << dist << ") is\nlarger than the minimum global cutoff (" - << min_global_cut - << "). This may lead to incorrect simulations\nunder certain " - "conditions. Set the \"System()\" " - "class property \"min_global_cut\" to\nincrease the minimum " - "cutoff.\n"; + << dist << ") is larger than the minimum global cutoff (" + << min_global_cut << "). This may lead to incorrect simulations under " + << "certain conditions. Adjust the property system.min_global_cut to " + << "increase the minimum cutoff."; } // Now, calculate the quaternions which specify the angle between @@ -136,6 +135,9 @@ void local_vs_relate_to(Particle &p_current, Particle const &p_relate_to) { } void vs_relate_to(int part_num, int relate_to) { + if (part_num == relate_to) { + throw std::invalid_argument("A virtual site cannot relate to itself"); + } // Get the data for the particle we act on and the one we want to relate // it to. auto const &p_current = get_particle_data(part_num); diff --git a/src/core/virtual_sites/VirtualSitesRelative.cpp b/src/core/virtual_sites/VirtualSitesRelative.cpp index b80996bbef9..943c1c8f25a 100644 --- a/src/core/virtual_sites/VirtualSitesRelative.cpp +++ b/src/core/virtual_sites/VirtualSitesRelative.cpp @@ -72,9 +72,7 @@ static Utils::Vector3d velocity(Particle const &p_ref, Particle const &p_vs) { * @brief Get real particle tracked by a virtual site. * * @param p Virtual site. - * @return Pointer to real particle if @p p is a virtual site, - * otherwise nullptr. If the real particle is not found, - * queue a runtime error. + * @return Pointer to real particle. */ static Particle *get_reference_particle(Particle const &p) { if (!p.is_virtual()) { diff --git a/src/python/espressomd/cellsystem.pyx b/src/python/espressomd/cellsystem.pyx index 3c023ea39f7..8a76ea2f759 100644 --- a/src/python/espressomd/cellsystem.pyx +++ b/src/python/espressomd/cellsystem.pyx @@ -126,7 +126,7 @@ cdef class CellSystem: pairs = mpi_get_pairs(distance) else: pairs = self._get_pairs_of_types(distance, types) - handle_errors("") + handle_errors("Error in get_pairs()") return pairs def _get_pairs_of_types(self, distance, types): diff --git a/src/python/espressomd/integrate.pyx b/src/python/espressomd/integrate.pyx index 9851626ab2f..7cad1ef1861 100644 --- a/src/python/espressomd/integrate.pyx +++ b/src/python/espressomd/integrate.pyx @@ -210,11 +210,12 @@ cdef class Integrator: """ check_type_or_throw_except(steps, 1, int, "steps must be an int") - assert steps >= 0, "steps has to be positive" check_type_or_throw_except( recalc_forces, 1, bool, "recalc_forces has to be a bool") check_type_or_throw_except( reuse_forces, 1, bool, "reuse_forces has to be a bool") + if steps < 0: + raise ValueError("steps must be positive") _integrate(steps, recalc_forces, reuse_forces) diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 9b82e08cd61..2a411679c22 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -24,7 +24,7 @@ import collections include "myconfig.pxi" from . import utils from .utils import is_valid_type -from .utils cimport check_type_or_throw_except +from .utils cimport check_type_or_throw_except, handle_errors from .script_interface import ScriptObjectRegistry, ScriptInterfaceHelper, script_interface_register @@ -73,9 +73,7 @@ cdef class NonBondedInteraction: """ temp_params = self._get_params_from_es_core() - if self._params != temp_params: - return False - return True + return self._params == temp_params def get_params(self): """Get interaction parameters. @@ -116,14 +114,15 @@ cdef class NonBondedInteraction: # If this instance refers to an interaction defined in the ESPResSo core, # load the parameters from there + is_valid_ia = self._part_types[0] >= 0 and self._part_types[1] >= 0 - if self._part_types[0] >= 0 and self._part_types[1] >= 0: + if is_valid_ia: self._params = self._get_params_from_es_core() # Put in values given by the user self._params.update(p) - if self._part_types[0] >= 0 and self._part_types[1] >= 0: + if is_valid_ia: self._set_params_in_es_core() # update interaction dict when user sets interaction @@ -137,6 +136,10 @@ cdef class NonBondedInteraction: self.user_interactions[self._part_types[0]][ self._part_types[1]]['type_name'] = self.type_name() + # defer exception (core and interface must always agree on parameters) + if is_valid_ia: + handle_errors(f'setting {self.type_name()} raised an error') + def validate_params(self): """Check that parameters are valid. diff --git a/src/python/espressomd/particle_data.pxd b/src/python/espressomd/particle_data.pxd index 99b076af16f..b5c3f9d17bc 100644 --- a/src/python/espressomd/particle_data.pxd +++ b/src/python/espressomd/particle_data.pxd @@ -175,7 +175,7 @@ cdef extern from "particle_data.hpp": cdef extern from "virtual_sites.hpp": IF VIRTUAL_SITES_RELATIVE == 1: - void vs_relate_to(int part_num, int relate_to) + void vs_relate_to(int part_num, int relate_to) except + cdef extern from "rotation.hpp": Vector3d convert_vector_body_to_space(const particle & p, const Vector3d & v) diff --git a/src/python/espressomd/particle_data.pyx b/src/python/espressomd/particle_data.pyx index 623ef1e439f..de82bebd3ba 100644 --- a/src/python/espressomd/particle_data.pyx +++ b/src/python/espressomd/particle_data.pyx @@ -29,7 +29,7 @@ from .analyze cimport max_seen_particle_type from copy import copy import collections import functools -from .utils import nesting_level, array_locked, is_valid_type +from .utils import nesting_level, array_locked, is_valid_type, handle_errors from .utils cimport make_array_locked, make_const_span, check_type_or_throw_except from .utils cimport Vector3i, Vector3d, Vector4d from .utils cimport make_Vector3d @@ -373,8 +373,8 @@ cdef class ParticleHandle: _mass, 1, float, "Mass has to be 1 float") set_particle_mass(self._id, _mass) ELSE: - raise AttributeError("You are trying to set the particle mass \ - but the mass feature is not compiled in.") + raise AttributeError("You are trying to set the particle mass " + "but the MASS feature is not compiled in.") def __get__(self): self.update_particle_data() @@ -427,6 +427,8 @@ cdef class ParticleHandle: cdef Quaternion[double] q check_type_or_throw_except( _q, 4, float, "Quaternions has to be 4 floats.") + if np.linalg.norm(_q) == 0.: + raise ValueError("quaternion is zero") for i in range(4): q[i] = _q[i] set_particle_quat(self._id, q) @@ -642,6 +644,8 @@ cdef class ParticleHandle: def __set__(self, q): check_type_or_throw_except( q, 4, float, "vs_quat has to be an array-like of length 4") + if np.linalg.norm(q) == 0.: + raise ValueError("quaternion is zero") cdef Quaternion[double] _q for i in range(4): _q[i] = q[i] @@ -680,6 +684,8 @@ cdef class ParticleHandle: dist, 1, float, "The distance has to be given as a float.") check_type_or_throw_except( quat, 4, float, "The quaternion has to be given as a tuple of 4 floats.") + if np.linalg.norm(quat) == 0.: + raise ValueError("quaternion is zero") cdef Quaternion[double] q for i in range(4): q[i] = quat[i] @@ -698,7 +704,7 @@ cdef class ParticleHandle: def vs_auto_relate_to(self, rel_to): """ Setup this particle as virtual site relative to the particle - in argument ``rel_to``. + in argument ``rel_to``. A particle cannot relate to itself. Parameters ----------- @@ -713,6 +719,7 @@ cdef class ParticleHandle: check_type_or_throw_except( rel_to, 1, int, "Argument of vs_auto_relate_to has to be of type ParticleHandle or int.") vs_relate_to(self._id, rel_to) + handle_errors('vs_auto_relate_to') IF DIPOLES: property dip: diff --git a/src/python/espressomd/script_interface.pyx b/src/python/espressomd/script_interface.pyx index 531639c8f1c..42746780e47 100644 --- a/src/python/espressomd/script_interface.pyx +++ b/src/python/espressomd/script_interface.pyx @@ -150,7 +150,7 @@ cdef class PScriptInterface: res = variant_to_python_object( self.sip.get().call_method(to_char_pointer(method), parameters)) - handle_errors("") + handle_errors(f'while calling method {method}()') return res def name(self): diff --git a/src/python/espressomd/system.pyx b/src/python/espressomd/system.pyx index c25c8bd279c..e4a46691ea5 100644 --- a/src/python/espressomd/system.pyx +++ b/src/python/espressomd/system.pyx @@ -511,6 +511,4 @@ cdef class System: """ check_type_or_throw_except(type, 1, int, "type must be 1 int") - number = number_of_particles_with_type(type) - handle_errors("") - return int(number) + return number_of_particles_with_type(type) diff --git a/src/script_interface/reaction_methods/ReactionAlgorithm.hpp b/src/script_interface/reaction_methods/ReactionAlgorithm.hpp index 1b5868fe461..a3e5fcd8b86 100644 --- a/src/script_interface/reaction_methods/ReactionAlgorithm.hpp +++ b/src/script_interface/reaction_methods/ReactionAlgorithm.hpp @@ -70,8 +70,8 @@ class ReactionAlgorithm : public AutoParameters { [this]() { return RE()->get_exclusion_range(); }}, {"exclusion_radius_per_type", [this](Variant const &v) { - RE()->exclusion_radius_per_type = get_map( - get_value>(v)); + RE()->set_exclusion_radius_per_type(get_map( + get_value>(v))); }, [this]() { return make_map(RE()->exclusion_radius_per_type); }}}); } diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index 38a9b15527f..c5197445c75 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -264,6 +264,7 @@ python_test(FILE elc_vs_analytic.py MAX_NUM_PROC 2) python_test(FILE rotation.py MAX_NUM_PROC 1) python_test(FILE shapes.py MAX_NUM_PROC 1) python_test(FILE h5md.py MAX_NUM_PROC 2) +python_test(FILE h5md.py MAX_NUM_PROC 1 SUFFIX 1_core) python_test(FILE mdanalysis.py MAX_NUM_PROC 2) python_test(FILE p3m_fft.py MAX_NUM_PROC 6) if(${TEST_NP} GREATER_EQUAL 8) diff --git a/testsuite/python/field_test.py b/testsuite/python/field_test.py index 27b5aa13e11..7f80073756c 100644 --- a/testsuite/python/field_test.py +++ b/testsuite/python/field_test.py @@ -267,6 +267,23 @@ def test_flow_field(self): np.testing.assert_allclose( -gamma * (p.v - f_val), np.copy(p.f), atol=1e-12) + def test_invalid_interpolated_field(self): + h = np.array([1., 1., 1.]) + Field = espressomd.constraints.FlowField + + grid = espressomd.constraints.FlowField.field_from_fn( + self.system.box_l, h, self.force) + + err_msg = "Constraint not compatible with box size" + with self.assertRaisesRegex(RuntimeError, err_msg): + field = Field(field=grid[:, :, :-2], grid_spacing=h, gamma=1.) + self.system.constraints.add(field) + with self.assertRaisesRegex(RuntimeError, err_msg): + field = Field(field=grid[:, 2:, :], grid_spacing=h, gamma=1.) + self.system.constraints.add(field) + + self.assertEqual(len(self.system.constraints), 0) + if __name__ == "__main__": ut.main() diff --git a/testsuite/python/h5md.py b/testsuite/python/h5md.py index 57ede3d2ff2..8bf74639585 100644 --- a/testsuite/python/h5md.py +++ b/testsuite/python/h5md.py @@ -72,6 +72,7 @@ class H5mdTests(ut.TestCase): system.integrator.run(steps=0) system.time = 12.3 + n_nodes = system.cell_system.get_state()["n_nodes"] @classmethod def setUpClass(cls): @@ -85,6 +86,7 @@ def setUpClass(cls): h5.write() h5.flush() h5.close() + cls.h5_params = h5.get_params() cls.py_file = h5py.File(cls.temp_file, 'r') cls.py_pos = cls.py_file['particles/atoms/position/value'][1] cls.py_img = cls.py_file['particles/atoms/image/value'][1] @@ -106,6 +108,23 @@ def test_opening(self): h5 = espressomd.io.writer.h5md.H5md(file_path=self.temp_file) h5.close() + @ut.skipIf(n_nodes > 1, "only runs for 1 MPI rank") + def test_exceptions(self): + h5md = espressomd.io.writer.h5md + h5_units = h5md.UnitSystem(time='ps', mass='u', length='m', charge='e') + temp_file = os.path.join(self.temp_dir.name, 'exceptions.h5') + # write a non-compliant file + with open(temp_file, 'w') as _: + pass + with self.assertRaisesRegex(RuntimeError, 'not a valid HDF5 file'): + h5md.H5md(file_path=temp_file, unit_system=h5_units) + # write a leftover backup file + os.remove(temp_file) + with open(temp_file + '.bak', 'w') as _: + pass + with self.assertRaisesRegex(RuntimeError, 'A backup of the .h5 file exists'): + h5md.H5md(file_path=temp_file, unit_system=h5_units) + def test_box(self): np.testing.assert_allclose(self.py_box, self.box_l) @@ -194,6 +213,18 @@ def get_unit(path): self.assertEqual(get_unit('particles/atoms/force/value'), b'm u ps-2') self.assertEqual(get_unit('particles/atoms/velocity/value'), b'm ps-1') + def test_getters(self): + self.assertEqual(self.h5_params['file_path'], self.temp_file) + self.assertEqual(os.path.abspath(self.h5_params['script_path']), + os.path.abspath(__file__)) + self.assertEqual(self.h5_params['time_unit'], 'ps') + if espressomd.has_features(['ELECTROSTATICS']): + self.assertEqual(self.h5_params['charge_unit'], 'e') + if espressomd.has_features(['MASS']): + self.assertEqual(self.h5_params['mass_unit'], 'u') + self.assertEqual(self.h5_params['force_unit'], 'm u ps-2') + self.assertEqual(self.h5_params['velocity_unit'], 'm ps-1') + def test_links(self): time_ref = self.py_id_time step_ref = self.py_id_step diff --git a/testsuite/python/integrator_exceptions.py b/testsuite/python/integrator_exceptions.py index 08360150939..1f697854bfe 100644 --- a/testsuite/python/integrator_exceptions.py +++ b/testsuite/python/integrator_exceptions.py @@ -17,6 +17,9 @@ # along with this program. If not, see . # import espressomd +import espressomd.interactions +import espressomd.lees_edwards +import espressomd.shapes import unittest as ut import unittest_decorators as utx @@ -25,7 +28,6 @@ class Integrator_test(ut.TestCase): system = espressomd.System(box_l=[1., 1., 1.]) system.time_step = 0.01 - system.cell_system.skin = 0.4 msg = 'Encountered errors during integrate: ERROR: ' def setUp(self): @@ -36,27 +38,67 @@ def setUp(self): def tearDown(self): self.system.thermostat.turn_off() self.system.part.clear() + self.system.constraints.clear() + self.system.lees_edwards.protocol = None + + def test_00_common_interface(self): + self.system.integrator.set_vv() + with self.assertRaisesRegex(ValueError, 'time_step must be > 0.'): + self.system.time_step = -2. + with self.assertRaisesRegex(ValueError, 'time_step must be > 0.'): + self.system.time_step = 0. + with self.assertRaisesRegex(ValueError, 'steps must be positive'): + self.system.integrator.run(steps=-1) + with self.assertRaisesRegex(Exception, self.msg + 'cannot automatically determine skin, please set it manually'): + self.system.integrator.run() + self.system.cell_system.skin = 0.4 + with self.assertRaisesRegex(Exception, self.msg + 'cannot reuse old forces and recalculate forces'): + self.system.integrator.run(recalc_forces=True, reuse_forces=True) + if espressomd.has_features("WCA"): + wca = self.system.non_bonded_inter[0, 0].wca + wca.set_params(epsilon=1., sigma=0.01) + wall = espressomd.shapes.Wall(normal=[0., 0., 1.], dist=100.) + self.system.constraints.add( + shape=wall, particle_type=0, penetrable=False) + with self.assertRaisesRegex(Exception, self.msg + 'Constraint violated by particle 0 dist -100'): + self.system.integrator.run(0) + with self.assertRaisesRegex(Exception, 'Constraint violated by particle 0'): + self.system.analysis.energy() + wca.set_params(epsilon=0., sigma=0.) def test_vv_integrator(self): + self.system.cell_system.skin = 0.4 self.system.thermostat.set_brownian(kT=1.0, gamma=1.0, seed=42) self.system.integrator.set_vv() with self.assertRaisesRegex(Exception, self.msg + 'The VV integrator is incompatible with the currently active combination of thermostats'): self.system.integrator.run(0) def test_brownian_integrator(self): + self.system.cell_system.skin = 0.4 self.system.integrator.set_brownian_dynamics() with self.assertRaisesRegex(Exception, self.msg + 'The BD integrator requires the BD thermostat'): self.system.integrator.run(0) @utx.skipIfMissingFeatures("NPT") def test_npt_integrator(self): + self.system.cell_system.skin = 0.4 self.system.thermostat.set_brownian(kT=1.0, gamma=1.0, seed=42) self.system.integrator.set_isotropic_npt(ext_pressure=1.0, piston=1.0) with self.assertRaisesRegex(Exception, self.msg + 'The NpT integrator requires the NpT thermostat'): self.system.integrator.run(0) + self.system.thermostat.turn_off() + self.system.thermostat.set_npt(kT=1.0, gamma0=2., gammav=0.04, seed=42) + with self.assertRaisesRegex(Exception, self.msg + 'The NpT integrator cannot use Lees-Edwards'): + self.system.lees_edwards.protocol = espressomd.lees_edwards.LinearShear( + shear_velocity=1., initial_pos_offset=0., time_0=0.) + self.system.integrator.run(0) + with self.assertRaisesRegex(Exception, self.msg + 'The NpT integrator cannot use Lees-Edwards'): + self.system.lees_edwards.protocol = espressomd.lees_edwards.Off() + self.system.integrator.run(0) @utx.skipIfMissingFeatures("STOKESIAN_DYNAMICS") def test_stokesian_integrator(self): + self.system.cell_system.skin = 0.4 self.system.periodicity = 3 * [False] self.system.thermostat.set_langevin(kT=1.0, gamma=1.0, seed=42) self.system.integrator.set_stokesian_dynamics( @@ -65,6 +107,7 @@ def test_stokesian_integrator(self): self.system.integrator.run(0) def test_steepest_descent_integrator(self): + self.system.cell_system.skin = 0.4 with self.assertRaisesRegex(ValueError, r"The following keys have to be given as keyword arguments: " r"\['f_max', 'gamma', 'max_displacement'\], got " r"\['f_max', 'gamma', 'max_d'\] \(missing \['max_displacement'\]\)"): diff --git a/testsuite/python/interactions_bonded_interface.py b/testsuite/python/interactions_bonded_interface.py index dda1e79f3f4..d01069639e3 100644 --- a/testsuite/python/interactions_bonded_interface.py +++ b/testsuite/python/interactions_bonded_interface.py @@ -279,6 +279,52 @@ def test_exceptions(self): with self.assertRaisesRegex(ValueError, error_msg_not_yet_defined): self.system.bonded_inter[0] + # check cutoff exceptions + skin = self.system.cell_system.skin + box_l = self.system.box_l + node_grid = self.system.cell_system.node_grid + n_nodes = self.system.cell_system.get_state()['n_nodes'] + max_ia_cutoff = min(box_l / node_grid) - skin * (n_nodes > 1) + err_msg = r"while calling method insert\(\): ERROR: " + if n_nodes == 1: + safe_cut = 0.5 * max_ia_cutoff + err_msg += r"number of cells 1 is smaller than minimum 8" + else: + safe_cut = 1.0 * max_ia_cutoff + err_msg += r"interaction range .+ in direction [0-2] is larger than the local box size" + + # a bond with exactly the right cutoff can be added + h0 = espressomd.interactions.HarmonicBond(k=1., r_0=0., r_cut=safe_cut) + self.system.bonded_inter.add(h0) + self.assertEqual(len(self.system.bonded_inter), 1) + self.system.bonded_inter.clear() + + # a bond with a cutoff larger than the box can be added, but throws + big_cut = 1.001 * safe_cut + h1 = espressomd.interactions.HarmonicBond(k=1., r_0=0., r_cut=big_cut) + with self.assertRaisesRegex(Exception, err_msg): + self.system.bonded_inter.add(h1) + self.assertEqual(len(self.system.bonded_inter), 1) + self.system.bonded_inter.clear() + + # a dihedral halves the cutoff + safe_cut /= 2. + h2 = espressomd.interactions.HarmonicBond(k=1., r_0=0., r_cut=safe_cut) + self.system.bonded_inter.add(h2) + dihe = espressomd.interactions.Dihedral(bend=1., mult=1, phase=0.) + self.system.bonded_inter.add(dihe) + self.assertEqual(len(self.system.bonded_inter), 2) + self.system.bonded_inter.clear() + + # a dihedral halves the cutoff, safe cutoffs become unsafe + half_cut = big_cut / 2. + h3 = espressomd.interactions.HarmonicBond(k=1., r_0=0., r_cut=half_cut) + self.system.bonded_inter.add(h3) + with self.assertRaisesRegex(Exception, err_msg): + self.system.bonded_inter.add(dihe) + self.assertEqual(len(self.system.bonded_inter), 2) + self.system.bonded_inter.clear() + if __name__ == "__main__": ut.main() diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index b842fb8394b..59e9ebdb14d 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -183,6 +183,19 @@ def test_exceptions(self): self.system.non_bonded_inter[0, 0].lennard_jones.set_params( epsilon=1., sigma=2., cutoff=3., shift=4., unknown=5.) + skin = self.system.cell_system.skin + box_l = self.system.box_l + node_grid = self.system.cell_system.node_grid + n_nodes = self.system.cell_system.get_state()['n_nodes'] + max_ia_cutoff = min(box_l / node_grid) - skin * (n_nodes > 1) + wrong_cutoff = 1.01 * max_ia_cutoff + lennard_jones = self.system.non_bonded_inter[0, 0].lennard_jones + with self.assertRaisesRegex(Exception, "setting LennardJones raised an error: ERROR: interaction range .+ in direction [0-2] is larger than the local box size"): + lennard_jones.set_params( + epsilon=1., sigma=1., cutoff=wrong_cutoff, shift="auto") + self.assertAlmostEqual( + lennard_jones.get_params()['cutoff'], wrong_cutoff, delta=1e-10) + if __name__ == "__main__": ut.main() diff --git a/testsuite/python/particle.py b/testsuite/python/particle.py index 43f704c105e..bf182147949 100644 --- a/testsuite/python/particle.py +++ b/testsuite/python/particle.py @@ -205,6 +205,10 @@ def test_vs_relative(self): p1.vs_relative = (0, '5', (1, 0, 0, 0)) with self.assertRaisesRegex(ValueError, "quaternion has to be given as a tuple of 4 floats"): p1.vs_relative = (0, 5.0, (1, 0, 0)) + with self.assertRaisesRegex(ValueError, "quaternion is zero"): + p1.vs_relative = (0, 5.0, (0, 0, 0, 0)) + with self.assertRaisesRegex(ValueError, "quaternion is zero"): + p1.vs_quat = [0, 0, 0, 0] @utx.skipIfMissingFeatures("DIPOLES") def test_contradicting_properties_dip_dipm(self): @@ -217,6 +221,12 @@ def test_contradicting_properties_dip_quat(self): self.system.part.add(pos=[0, 0, 0], dip=[1, 1, 1], quat=[1.0, 1.0, 1.0, 1.0]) + @utx.skipIfMissingFeatures(["ROTATION"]) + def test_invalid_quat(self): + system = self.system + with self.assertRaisesRegex(ValueError, "quaternion is zero"): + system.part.add(pos=[0., 0., 0.], quat=[0., 0., 0., 0.]) + @utx.skipIfMissingFeatures("ELECTROSTATICS") def test_particle_selection(self): self.system.part.clear() diff --git a/testsuite/python/reaction_methods.py b/testsuite/python/reaction_methods.py index d82a5119698..f9076579d53 100644 --- a/testsuite/python/reaction_methods.py +++ b/testsuite/python/reaction_methods.py @@ -269,19 +269,29 @@ def test_exceptions(self): x=1, **single_reaction_params) with self.assertRaisesRegex(ValueError, err_msg): espressomd.reaction_ensemble.ReactionEnsemble( - kT=1., exclusion_range=1., seed=12, x=1, exclusion_radius_per_type={1: 0.1}) + kT=1., exclusion_range=1., seed=12, x=1) with self.assertRaisesRegex(ValueError, err_msg): espressomd.reaction_ensemble.ConstantpHEnsemble( - kT=1., exclusion_range=1., seed=12, x=1, constant_pH=2, exclusion_radius_per_type={1: 0.1}) + kT=1., exclusion_range=1., seed=12, x=1, constant_pH=2) with self.assertRaisesRegex(ValueError, err_msg): espressomd.reaction_ensemble.WidomInsertion( kT=1., seed=12, x=1) with self.assertRaisesRegex(ValueError, "Invalid value for 'kT'"): espressomd.reaction_ensemble.ReactionEnsemble( - kT=-1., exclusion_range=1., seed=12, exclusion_radius_per_type={1: 0.1}) + kT=-1., exclusion_range=1., seed=12) + + # check invalid exclusion ranges and radii with self.assertRaisesRegex(ValueError, "Invalid value for 'exclusion_range'"): espressomd.reaction_ensemble.ReactionEnsemble( - kT=1., exclusion_range=-1., seed=12, exclusion_radius_per_type={1: 0.1}) + kT=1., seed=12, exclusion_range=-1.) + with self.assertRaisesRegex(ValueError, "Invalid excluded_radius value for type 1: radius -0.10"): + espressomd.reaction_ensemble.ReactionEnsemble( + kT=1., seed=12, exclusion_range=1., exclusion_radius_per_type={1: -0.1}) + method = espressomd.reaction_ensemble.ReactionEnsemble( + kT=1., exclusion_range=1., seed=12, exclusion_radius_per_type={1: 0.1}) + with self.assertRaisesRegex(ValueError, "Invalid excluded_radius value for type 2: radius -0.10"): + method.exclusion_radius_per_type = {2: -0.1} + self.assertEqual(list(method.exclusion_radius_per_type.keys()), [1]) if __name__ == "__main__": diff --git a/testsuite/python/virtual_sites_relative.py b/testsuite/python/virtual_sites_relative.py index 14657efbac1..1d2dd0c64d8 100644 --- a/testsuite/python/virtual_sites_relative.py +++ b/testsuite/python/virtual_sites_relative.py @@ -140,16 +140,25 @@ def test_vs_exceptions(self): system = self.system system.virtual_sites = espressomd.virtual_sites.VirtualSitesRelative() system.time_step = 0.01 + system.cell_system.skin = 0.1 + system.min_global_cut = 0.1 + p1 = system.part.add(pos=[0.0, 0.0, 0.0], rotation=[1, 1, 1], id=1) p2 = system.part.add(pos=[1.0, 1.0, 1.0], rotation=[1, 1, 1], id=2) p3 = system.part.add(pos=[1.0, 1.0, 1.0], rotation=[1, 1, 1], id=3) # relating to anything else other than a particle or id is not allowed with self.assertRaisesRegex(ValueError, "Argument of vs_auto_relate_to has to be of type ParticleHandle or int"): p2.vs_auto_relate_to('0') + # relating to itself is not allowed + with self.assertRaisesRegex(ValueError, "A virtual site cannot relate to itself"): + p2.vs_auto_relate_to(p2) # relating to a deleted particle is not allowed with self.assertRaisesRegex(Exception, "No real particle with id 3 for virtual site with id 2"): p2.vs_auto_relate_to(p3) p3.remove() system.integrator.run(0, recalc_forces=True) + if system.cell_system.get_state()["n_nodes"] > 1: + with self.assertRaisesRegex(Exception, r"The distance between virtual and non-virtual particle \([0-9\.]+\) is larger than the minimum global cutoff"): + p2.vs_auto_relate_to(p1) def test_pos_vel_forces(self): system = self.system From 61e025abbac2637a85bd8f5b3ca0d19617ee15d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 18 Mar 2022 02:32:26 +0100 Subject: [PATCH 94/99] script_interface: Refactor get_value() framework Rename `make_map()` to `make_unordered_map_of_variants()` to better reflect its role. Remove `get_map()` and instead implement getter `template get_value>(Variant)`. Make Variant runtime error messages human-readable by substituting the Variant demangled name by the string "ScriptInterface::Variant". Reduce code duplication. --- src/script_interface/ObjectMap.hpp | 7 +- .../constraints/couplings.hpp | 10 +- src/script_interface/get_value.hpp | 146 ++++++++++++------ .../reaction_methods/ConstantpHEnsemble.hpp | 5 +- .../reaction_methods/ReactionAlgorithm.hpp | 10 +- .../reaction_methods/ReactionEnsemble.hpp | 6 +- .../reaction_methods/WidomInsertion.hpp | 3 +- src/script_interface/tests/get_value_test.cpp | 130 +++++++++++----- 8 files changed, 204 insertions(+), 113 deletions(-) diff --git a/src/script_interface/ObjectMap.hpp b/src/script_interface/ObjectMap.hpp index d6af7da67df..a41d7ddc902 100644 --- a/src/script_interface/ObjectMap.hpp +++ b/src/script_interface/ObjectMap.hpp @@ -130,12 +130,7 @@ class ObjectMap : public BaseType { } if (method == "get_map") { - std::unordered_map ret; - - for (auto const &kv : m_elements) - ret[kv.first] = kv.second; - - return ret; + return make_unordered_map_of_variants(m_elements); } if (method == "keys") { diff --git a/src/script_interface/constraints/couplings.hpp b/src/script_interface/constraints/couplings.hpp index c1ceab0de49..b96831bd883 100644 --- a/src/script_interface/constraints/couplings.hpp +++ b/src/script_interface/constraints/couplings.hpp @@ -74,8 +74,9 @@ template <> struct coupling_parameters_impl { AutoParameter::read_only, [this_]() { return this_().default_scale(); }, }, - {"particle_scales", AutoParameter::read_only, - [this_]() { return make_map(this_().particle_scales()); }}}; + {"particle_scales", AutoParameter::read_only, [this_]() { + return make_unordered_map_of_variants(this_().particle_scales()); + }}}; } }; @@ -90,9 +91,8 @@ template <> inline Viscous make_coupling(const VariantMap ¶ms) { } template <> inline Scaled make_coupling(const VariantMap ¶ms) { - auto const particle_scales = get_value_or>( - params, "particle_scales", {}); - return Scaled{get_map(particle_scales), + return Scaled{get_value_or>( + params, "particle_scales", {}), get_value(params, "default_scale")}; } } // namespace detail diff --git a/src/script_interface/get_value.hpp b/src/script_interface/get_value.hpp index acff18edf23..904230b8079 100644 --- a/src/script_interface/get_value.hpp +++ b/src/script_interface/get_value.hpp @@ -38,16 +38,64 @@ namespace ScriptInterface { namespace detail { + +/** + * @brief Demangle the symbol of a container @c value_type or @c mapped_type. + * Only for recursive variant container types used in @ref Variant. + * Other container types and non-container objects return an empty string. + */ +template struct demangle_container_value_type { +private: + template struct is_std_vector : std::false_type {}; + template + struct is_std_vector> : std::true_type {}; + + template struct is_std_unordered_map : std::false_type {}; + template + struct is_std_unordered_map> : std::true_type {}; + +public: + template ::value and + !is_std_unordered_map::value, + bool> = true> + auto operator()() const { + return std::string(""); + } + + template ::value, bool> = true> + auto operator()() const { + return Utils::demangle(); + } + + template ::value, bool> = true> + auto operator()() const { + return Utils::demangle(); + } +}; + +/** + * @brief Demangle the data type of an object wrapped inside a @ref Variant. + * When the data type involves a recursive variant (e.g. containers), the + * demangled symbol name is processed to replace all occurences of the + * variant symbol name by a human-readable string "ScriptInterface::Variant". + */ struct type_label_visitor : boost::static_visitor { template std::string operator()(const T &) const { - return Utils::demangle(); + auto const symbol_for_variant = Utils::demangle(); + auto const name_for_variant = std::string("ScriptInterface::Variant"); + auto symbol = Utils::demangle(); + for (std::string::size_type pos{}; + (pos = symbol.find(symbol_for_variant, pos)) != symbol.npos; + pos += name_for_variant.length()) { + symbol.replace(pos, symbol_for_variant.length(), name_for_variant); + } + return symbol; } }; -inline std::string type_label(const Variant &v) { - return boost::apply_visitor(type_label_visitor{}, v); -} - /* * Allows * T -> T, @@ -137,6 +185,9 @@ struct GetVectorOrEmpty : boost::static_visitor> { /* Standard case, correct type */ std::vector operator()(std::vector const &v) const { return v; } + + template ::value, bool> = true> std::vector operator()(std::vector const &vv) const { std::vector ret(vv.size()); @@ -147,17 +198,10 @@ struct GetVectorOrEmpty : boost::static_visitor> { } }; -/* std::vector cases - * We implicitly transform an empty vector into an empty vector. */ -template <> struct get_value_helper, void> { - std::vector operator()(Variant const &v) const { - return boost::apply_visitor(GetVectorOrEmpty{}, v); - } -}; - -template <> struct get_value_helper, void> { - std::vector operator()(Variant const &v) const { - return boost::apply_visitor(GetVectorOrEmpty{}, v); +/* std::vector cases */ +template struct get_value_helper, void> { + std::vector operator()(Variant const &v) const { + return boost::apply_visitor(GetVectorOrEmpty{}, v); } }; @@ -172,12 +216,24 @@ struct GetMapOrEmpty : boost::static_visitor> { std::unordered_map operator()(std::unordered_map const &v) const { return v; } + + template ::value, bool> = true> + std::unordered_map + operator()(std::unordered_map const &v) const { + std::unordered_map ret; + for (auto it = v.begin(); it != v.end(); ++it) { + ret.insert({it->first, get_value_helper{}(it->second)}); + } + return ret; + } }; /* std::unordered_map cases */ -template <> struct get_value_helper, void> { - std::unordered_map operator()(Variant const &v) const { - return boost::apply_visitor(GetMapOrEmpty{}, v); +template +struct get_value_helper, void> { + std::unordered_map operator()(Variant const &v) const { + return boost::apply_visitor(GetMapOrEmpty{}, v); } }; @@ -213,24 +269,27 @@ struct get_value_helper< * @brief Re-throw a @c boost::bad_get exception wrapped in an @ref Exception. * Write a custom error message for invalid conversions due to type mismatch * and due to nullptr values, possibly with context information if the variant - * is in a container. - * @tparam T Type which the variant was supposed to convert to - * @tparam U Type of the container the variant is contained in + * is a container. + * @tparam T Which type the variant was supposed to convert to */ -template -inline void handle_bad_get(Variant const &v) { - using Utils::demangle; - auto const what = "Provided argument of type " + type_label(v); - auto const context = - (std::is_void::value) - ? "" - : " (raised during the creation of a " + demangle() + ")"; +template inline void handle_bad_get(Variant const &v) { + auto const object_symbol_name = boost::apply_visitor(type_label_visitor{}, v); + auto const element_symbol_name = demangle_container_value_type{}(); + auto const is_container = !element_symbol_name.empty(); + auto const what = "Provided argument of type " + object_symbol_name; try { throw; } catch (bad_get_nullptr const &) { - throw Exception(what + " is a null pointer" + context); + auto const item_error = (is_container) ? " contains a value that" : ""; + throw Exception(what + item_error + " is a null pointer"); } catch (boost::bad_get const &) { - throw Exception(what + " is not convertible to " + demangle() + context); + auto const non_convertible = " is not convertible to "; + auto item_error = std::string(""); + if (is_container) { + item_error += " because it contains a value that"; + item_error += non_convertible + element_symbol_name; + } + throw Exception(what + non_convertible + Utils::demangle() + item_error); } } @@ -241,9 +300,9 @@ inline void handle_bad_get(Variant const &v) { * * This is a wrapper around boost::get that allows us to * customize the behavior for different types. This is - * needed e.g. to deal with Vector types that are not - * explicitly contained in Variant, but can easily - * be converted. + * needed e.g. to deal with containers whose elements + * have mixed types that are implicitly convertible + * to a requested type. */ template T get_value(Variant const &v) { try { @@ -255,22 +314,7 @@ template T get_value(Variant const &v) { } template -std::unordered_map get_map(std::unordered_map const &v) { - std::unordered_map ret; - auto it = v.begin(); - try { - for (; it != v.end(); ++it) { - ret.insert({it->first, detail::get_value_helper{}(it->second)}); - } - } catch (...) { - detail::handle_bad_get>(v); - throw; - } - return ret; -} - -template -std::unordered_map make_map(std::unordered_map const &v) { +auto make_unordered_map_of_variants(std::unordered_map const &v) { std::unordered_map ret; for (auto const &it : v) { ret.insert({it.first, Variant(it.second)}); diff --git a/src/script_interface/reaction_methods/ConstantpHEnsemble.hpp b/src/script_interface/reaction_methods/ConstantpHEnsemble.hpp index 834bf651062..47c0a672e3f 100644 --- a/src/script_interface/reaction_methods/ConstantpHEnsemble.hpp +++ b/src/script_interface/reaction_methods/ConstantpHEnsemble.hpp @@ -28,6 +28,7 @@ #include "core/reaction_methods/ReactionAlgorithm.hpp" #include +#include namespace ScriptInterface { namespace ReactionMethods { @@ -53,8 +54,8 @@ class ConstantpHEnsemble : public ReactionAlgorithm { get_value(params, "seed"), get_value(params, "kT"), get_value(params, "exclusion_range"), get_value(params, "constant_pH"), - get_map(get_value_or>( - params, "exclusion_radius_per_type", {}))); + get_value_or>( + params, "exclusion_radius_per_type", {})); } private: diff --git a/src/script_interface/reaction_methods/ReactionAlgorithm.hpp b/src/script_interface/reaction_methods/ReactionAlgorithm.hpp index a3e5fcd8b86..13f990207ea 100644 --- a/src/script_interface/reaction_methods/ReactionAlgorithm.hpp +++ b/src/script_interface/reaction_methods/ReactionAlgorithm.hpp @@ -29,6 +29,7 @@ #include #include #include +#include #include namespace ScriptInterface { @@ -70,10 +71,13 @@ class ReactionAlgorithm : public AutoParameters { [this]() { return RE()->get_exclusion_range(); }}, {"exclusion_radius_per_type", [this](Variant const &v) { - RE()->set_exclusion_radius_per_type(get_map( - get_value>(v))); + RE()->set_exclusion_radius_per_type( + get_value>(v)); }, - [this]() { return make_map(RE()->exclusion_radius_per_type); }}}); + [this]() { + return make_unordered_map_of_variants( + RE()->exclusion_radius_per_type); + }}}); } Variant do_call_method(std::string const &name, diff --git a/src/script_interface/reaction_methods/ReactionEnsemble.hpp b/src/script_interface/reaction_methods/ReactionEnsemble.hpp index 03b66128420..20478e1e8b6 100644 --- a/src/script_interface/reaction_methods/ReactionEnsemble.hpp +++ b/src/script_interface/reaction_methods/ReactionEnsemble.hpp @@ -28,6 +28,7 @@ #include "core/reaction_methods/ReactionEnsemble.hpp" #include +#include namespace ScriptInterface { namespace ReactionMethods { @@ -39,12 +40,11 @@ class ReactionEnsemble : public ReactionAlgorithm { } void do_construct(VariantMap const ¶ms) override { - m_re = std::make_shared<::ReactionMethods::ReactionEnsemble>( get_value(params, "seed"), get_value(params, "kT"), get_value(params, "exclusion_range"), - get_map(get_value_or>( - params, "exclusion_radius_per_type", {}))); + get_value_or>( + params, "exclusion_radius_per_type", {})); } private: diff --git a/src/script_interface/reaction_methods/WidomInsertion.hpp b/src/script_interface/reaction_methods/WidomInsertion.hpp index f563047bd9e..343679a0184 100644 --- a/src/script_interface/reaction_methods/WidomInsertion.hpp +++ b/src/script_interface/reaction_methods/WidomInsertion.hpp @@ -41,10 +41,9 @@ class WidomInsertion : public ReactionAlgorithm { } void do_construct(VariantMap const ¶ms) override { - std::unordered_map exclusion_radius_per_type; m_re = std::make_shared<::ReactionMethods::WidomInsertion>( get_value(params, "seed"), get_value(params, "kT"), 0., - exclusion_radius_per_type); + std::unordered_map{}); } Variant do_call_method(std::string const &name, diff --git a/src/script_interface/tests/get_value_test.cpp b/src/script_interface/tests/get_value_test.cpp index d6d346ace08..3410e3fd672 100644 --- a/src/script_interface/tests/get_value_test.cpp +++ b/src/script_interface/tests/get_value_test.cpp @@ -24,6 +24,7 @@ #include "script_interface/ObjectHandle.hpp" #include "script_interface/get_value.hpp" +#include #include #include #include @@ -127,67 +128,112 @@ BOOST_AUTO_TEST_CASE(get_value_from_map) { BOOST_CHECK_THROW((get_value(map, "unknown")), std::exception); } -BOOST_AUTO_TEST_CASE(get_map_value) { - using ScriptInterface::get_map; - using ScriptInterface::Variant; - - std::unordered_map const map_variant{{1, 1.5}, {2, 2.5}}; - std::unordered_map const map = get_map(map_variant); - BOOST_CHECK_EQUAL(map.at(1), 1.5); - BOOST_CHECK_EQUAL(map.at(2), 2.5); -} - -BOOST_AUTO_TEST_CASE(get_unordered_map) { +BOOST_AUTO_TEST_CASE(unordered_map) { using ScriptInterface::get_value; + using ScriptInterface::make_unordered_map_of_variants; using ScriptInterface::Variant; auto const var = Variant{std::unordered_map{{1, 1}, {2, 2.5}}}; - auto const map = get_value>(var); - BOOST_CHECK_EQUAL(get_value(map.at(1)), 1); - BOOST_CHECK_EQUAL(get_value(map.at(2)), 2.5); + { + auto const map_var = get_value>(var); + BOOST_CHECK_EQUAL(get_value(map_var.at(1)), 1); + BOOST_CHECK_EQUAL(get_value(map_var.at(2)), 2.5); + } + { + auto const map_dbl = get_value>(var); + BOOST_CHECK_EQUAL(map_dbl.at(1), 1.0); + BOOST_CHECK_EQUAL(map_dbl.at(2), 2.5); + } + { + auto const map_dbl_input = get_value>(var); + auto const map_var = make_unordered_map_of_variants(map_dbl_input); + auto const map_dbl = + get_value>(Variant{map_var}); + BOOST_CHECK_EQUAL(map_dbl.at(1), 1.0); + BOOST_CHECK_EQUAL(map_dbl.at(2), 2.5); + } +} + +auto exception_message_predicate(std::string const &pattern) { + return [=](std::exception const &ex) { + boost::test_tools::predicate_result result = true; + std::string const what = ex.what(); + std::smatch match; + if (!std::regex_search(what, match, std::regex(pattern))) { + result = false; + result.message() << "Error message \"" << what << "\" " + << "doesn't match pattern \"" << pattern << "\""; + } + return result; + }; } -BOOST_AUTO_TEST_CASE(exceptions) { - using ScriptInterface::get_map; +BOOST_AUTO_TEST_CASE(check_exceptions) { using ScriptInterface::get_value; using ScriptInterface::Variant; + assert(!!exception_message_predicate("A")(std::runtime_error("A"))); + assert(!exception_message_predicate("A")(std::runtime_error("B"))); + using so_ptr_t = std::shared_ptr; auto const so_obj = so_ptr_t(); - auto const so_ptr_tn = Utils::demangle(); + auto const msg_prefix = std::string("Provided argument of type "); + auto const variant_name = std::string("ScriptInterface::Variant"); { - auto const predicate = [](std::string const &type, std::string const &why) { - auto const message = "Provided argument of type " + type + " is " + why; - return [=](std::exception const &ex) { return ex.what() == message; }; - }; - auto const so_variant = Variant(so_obj); - BOOST_CHECK_EXCEPTION((get_value(so_variant)), std::exception, - predicate(so_ptr_tn, "a null pointer")); - BOOST_CHECK_EXCEPTION((get_value(so_variant)), std::exception, - predicate(so_ptr_tn, "not convertible to int")); + // basic types + auto const obj_variant = Variant{so_obj}; + auto const obj_variant_pattern = Utils::demangle(); + auto const what = msg_prefix + obj_variant_pattern; + auto const predicate_nullptr = + exception_message_predicate(what + " is a null pointer"); + auto const predicate_conversion = + exception_message_predicate(what + " is not convertible to int"); + BOOST_CHECK_EXCEPTION(get_value(obj_variant), std::exception, + predicate_nullptr); + BOOST_CHECK_EXCEPTION(get_value(obj_variant), std::exception, + predicate_conversion); + } + { + // vectors + auto const vec_variant = Variant{std::vector{{so_obj}}}; + auto const vec_variant_pattern = + "std::(__1::)?vector<" + variant_name + ", .*?>"; + auto const what = msg_prefix + vec_variant_pattern; + auto const predicate_nullptr = exception_message_predicate( + what + " contains a value that is a null pointer"); + auto const predicate_conversion = exception_message_predicate( + what + " is not convertible to std::(__1::)?vector because " + "it contains a value that is not convertible to int"); + BOOST_CHECK_EXCEPTION(get_value>(vec_variant), + std::exception, predicate_nullptr); + BOOST_CHECK_EXCEPTION(get_value>(vec_variant), + std::exception, predicate_conversion); } { - auto const predicate = [](std::string const &why, std::string const &type) { - auto const msg = "during the creation of a std::(__1::)?unordered_map"; - auto const pattern = why + " \\(raised " + msg + "{{1, so_obj}}; - BOOST_CHECK_EXCEPTION((get_map(so_map)), std::exception, - predicate("is a null pointer", so_ptr_tn)); - BOOST_CHECK_EXCEPTION((get_map(so_map)), std::exception, - predicate("is not convertible to int", "int")); + // unordered maps + auto const map_variant = + Variant{std::unordered_map{{1, so_obj}}}; + auto const map_variant_pattern = + "std::(__1::)?unordered_map"; + auto const what = msg_prefix + map_variant_pattern; + auto const predicate_nullptr = exception_message_predicate( + what + " contains a value that is a null pointer"); + auto const predicate_conversion = exception_message_predicate( + what + + " is not convertible to std::(__1::)?unordered_map " + "because it contains a value that is not convertible to int"); + BOOST_CHECK_EXCEPTION( + (get_value>(map_variant)), + std::exception, predicate_nullptr); + BOOST_CHECK_EXCEPTION( + (get_value>(map_variant)), std::exception, + predicate_conversion); } { using Utils::Vector3d; std::unordered_map const mixed{{1, 1}, {2, std::string("2")}}; - BOOST_CHECK_THROW((get_map(mixed)), std::exception); std::vector const v_var = {1., 2.}; std::vector const v_dbl = {1., 2.}; BOOST_CHECK_THROW((get_value(Variant{v_var})), std::exception); @@ -199,5 +245,7 @@ BOOST_AUTO_TEST_CASE(exceptions) { BOOST_CHECK_THROW((get_value(Variant{v_dbl})), std::exception); BOOST_CHECK_THROW((get_value>(Variant{})), std::exception); + BOOST_CHECK_THROW((get_value>(Variant{mixed})), + std::exception); } } From 78a22db3104a881f4df7b6318fbc5514c32da18d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 18 Mar 2022 17:15:08 +0100 Subject: [PATCH 95/99] core: Fix undefined behavior When LB_BOUNDARIES is not compiled in, the return type changes and the function pointer stored in the MpiCallbacks framework accesses the wrong data type. --- src/core/grid_based_algorithms/lb_collective_interface.cpp | 4 ++-- testsuite/python/lb_vtk.py | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/grid_based_algorithms/lb_collective_interface.cpp b/src/core/grid_based_algorithms/lb_collective_interface.cpp index 4ea9ddcd5ef..2638f091602 100644 --- a/src/core/grid_based_algorithms/lb_collective_interface.cpp +++ b/src/core/grid_based_algorithms/lb_collective_interface.cpp @@ -109,14 +109,14 @@ auto mpi_lb_get_populations(Utils::Vector3i const &index) { REGISTER_CALLBACK_ONE_RANK(mpi_lb_get_populations) -auto mpi_lb_get_boundary_flag(Utils::Vector3i const &index) { +boost::optional mpi_lb_get_boundary_flag(Utils::Vector3i const &index) { return detail::lb_calc(index, [&](auto index) { #ifdef LB_BOUNDARIES auto const linear_index = get_linear_index(lblattice.local_index(index), lblattice.halo_grid); return lbfields[linear_index].boundary; #else - return false; + return 0; #endif }); } diff --git a/testsuite/python/lb_vtk.py b/testsuite/python/lb_vtk.py index 8cd8e065c02..2e4e28f8088 100644 --- a/testsuite/python/lb_vtk.py +++ b/testsuite/python/lb_vtk.py @@ -142,6 +142,8 @@ def test_vtk(self): vtk_boundary = self.parse_vtk( 'vtk_out/boundary.vtk', 'boundary', shape) np.testing.assert_equal(vtk_boundary, node_boundary.astype(int)) + if self.system.lbboundaries is None: + np.testing.assert_equal(np.sum(node_boundary), 0.) def test_print(self): ''' From 9ad55b9003e8a113b02c990e3382b56b5f631ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 18 Mar 2022 22:58:00 +0100 Subject: [PATCH 96/99] python: Fix OpenGL visualization --- samples/lj-demo.py | 2 +- src/python/espressomd/visualization_opengl.py | 23 ++++++++----------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/samples/lj-demo.py b/samples/lj-demo.py index 46173f31cbf..eeab45f217a 100644 --- a/samples/lj-demo.py +++ b/samples/lj-demo.py @@ -454,7 +454,7 @@ def main_loop(): new_box = np.ones(3) * controls.volume**(1. / 3.) if np.any(np.array(system.box_l) != new_box): for p in system.part: - p.pos *= new_box / system.box_l[0] + p.pos = p.pos * new_box / system.box_l[0] print("volume changed") system.force_cap = lj_cap system.box_l = new_box diff --git a/src/python/espressomd/visualization_opengl.py b/src/python/espressomd/visualization_opengl.py index 3984349bc66..9b4b7588fe4 100644 --- a/src/python/espressomd/visualization_opengl.py +++ b/src/python/espressomd/visualization_opengl.py @@ -1587,22 +1587,17 @@ def _init_espresso_visualization(self): self.depth = 0 # LOOK FOR LB ACTOR - if self.specs['LB_draw_velocity_plane'] or \ - self.specs['LB_draw_nodes'] or \ - self.specs['LB_draw_node_boundaries']: - lb_types = [espressomd.lb.LBFluid] - if espressomd.has_features('CUDA'): - lb_types.append(espressomd.lb.LBFluidGPU) - for a in self.system.actors: - if isinstance(a, tuple(lb_types)): - # if 'agrid' in pa: - self.lb_params = a.get_params() - self.lb = a - self.lb_is_cpu = isinstance(a, espressomd.lb.LBFluid) - break + lb_types = [espressomd.lb.LBFluid] + if espressomd.has_features('CUDA'): + lb_types.append(espressomd.lb.LBFluidGPU) + for actor in self.system.actors: + if isinstance(actor, tuple(lb_types)): + self.lb_params = actor.get_params() + self.lb = actor + self.lb_is_cpu = isinstance(actor, espressomd.lb.LBFluid) + break if self.specs['LB_draw_velocity_plane']: - if self.specs['LB_plane_axis'] == 0: pn = [1.0, 0.0, 0.0] self.lb_plane_b1 = [0.0, 1.0, 0.0] From 4d319c37584ae4499a5a9d9d79295a03218dec95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 18 Mar 2022 23:04:40 +0100 Subject: [PATCH 97/99] testsuite: Fix regressions --- src/core/unit_tests/rotation_test.cpp | 10 +++++++--- testsuite/python/CMakeLists.txt | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/core/unit_tests/rotation_test.cpp b/src/core/unit_tests/rotation_test.cpp index 5ff5d98cb6c..336f16cd75f 100644 --- a/src/core/unit_tests/rotation_test.cpp +++ b/src/core/unit_tests/rotation_test.cpp @@ -19,10 +19,14 @@ #define BOOST_TEST_MODULE rotation test #define BOOST_TEST_DYN_LINK + +#include "config.hpp" + +#ifdef ROTATION + #include #include "Particle.hpp" -#include "config.hpp" #include "rotation.hpp" #include @@ -33,8 +37,6 @@ #include #include -#ifdef ROTATION - auto constexpr tol = 5. * 100. * std::numeric_limits::epsilon(); namespace Testing { @@ -216,4 +218,6 @@ BOOST_AUTO_TEST_CASE(convert_dip_to_quat_test) { } } #endif // DIPOLES +#else // ROTATION +int main(int argc, char **argv) {} #endif // ROTATION diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index c5197445c75..6b91021da26 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -127,6 +127,7 @@ python_test(FILE mass-and-rinertia_per_particle.py MAX_NUM_PROC 2 LABELS long) python_test(FILE integrate.py MAX_NUM_PROC 4) python_test(FILE interactions_bond_angle.py MAX_NUM_PROC 4) python_test(FILE interactions_bonded_interface.py MAX_NUM_PROC 4) +python_test(FILE interactions_bonded_interface.py MAX_NUM_PROC 1 SUFFIX 1_core) python_test(FILE interactions_bonded.py MAX_NUM_PROC 2) python_test(FILE interactions_dihedral.py MAX_NUM_PROC 4) python_test(FILE interactions_non-bonded_interface.py MAX_NUM_PROC 4) From db253de9126b060ccf00cae7bfd2f9127a0df5af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 18 Mar 2022 23:37:15 +0100 Subject: [PATCH 98/99] Improve particle exception handling Convert fatal errors into ValueError for invalid particle ids. Make python code for particle creation more readable. --- src/core/particle_data.cpp | 8 ++- src/python/espressomd/particle_data.pxd | 2 +- src/python/espressomd/particle_data.pyx | 65 ++++++++++++------------- testsuite/python/particle.py | 14 +++++- 4 files changed, 51 insertions(+), 38 deletions(-) diff --git a/src/core/particle_data.cpp b/src/core/particle_data.cpp index ff011db2825..17d8864a258 100644 --- a/src/core/particle_data.cpp +++ b/src/core/particle_data.cpp @@ -487,8 +487,9 @@ void build_particle_node() { mpi_who_has(); } * @brief Get the mpi rank which owns the particle with id. */ int get_particle_node(int id) { - if (id < 0) - throw std::runtime_error("Invalid particle id!"); + if (id < 0) { + throw std::domain_error("Invalid particle id: " + std::to_string(id)); + } if (particle_node.empty()) build_particle_node(); @@ -724,6 +725,9 @@ void mpi_place_particle(int node, int p_id, const Utils::Vector3d &pos) { } int place_particle(int p_id, Utils::Vector3d const &pos) { + if (p_id < 0) { + throw std::domain_error("Invalid particle id: " + std::to_string(p_id)); + } if (particle_exists(p_id)) { mpi_place_particle(get_particle_node(p_id), p_id, pos); diff --git a/src/python/espressomd/particle_data.pxd b/src/python/espressomd/particle_data.pxd index b5c3f9d17bc..695047b4090 100644 --- a/src/python/espressomd/particle_data.pxd +++ b/src/python/espressomd/particle_data.pxd @@ -74,7 +74,7 @@ cdef extern from "particle_data.hpp": # Setter/getter/modifier functions functions void prefetch_particle_data(vector[int] ids) - int place_particle(int part, const Vector3d & p) + int place_particle(int part, const Vector3d & p) except + void set_particle_v(int part, const Vector3d & v) diff --git a/src/python/espressomd/particle_data.pyx b/src/python/espressomd/particle_data.pyx index de82bebd3ba..28b8afc9b56 100644 --- a/src/python/espressomd/particle_data.pyx +++ b/src/python/espressomd/particle_data.pyx @@ -1747,84 +1747,83 @@ cdef class ParticleList: # Did we get a dictionary if len(args) == 1: if hasattr(args[0], "__getitem__"): - P = args[0] + particles_dict = args[0] else: - if len(args) == 0 and len(kwargs.keys()) != 0: - P = kwargs + if len(args) == 0 and len(kwargs) != 0: + particles_dict = kwargs else: raise ValueError( "add() takes either a dictionary or a bunch of keyword args.") # Check for presence of pos attribute - if "pos" not in P: + if "pos" not in particles_dict: raise ValueError( "pos attribute must be specified for new particle") - if len(np.array(P["pos"]).shape) == 2: - return self._place_new_particles(P) + if len(np.array(particles_dict["pos"]).shape) == 2: + return self._place_new_particles(particles_dict) else: - return self._place_new_particle(P) + return self._place_new_particle(particles_dict) - def _place_new_particle(self, P): + def _place_new_particle(self, p_dict): # Handling of particle id - if "id" not in P: + if "id" not in p_dict: # Generate particle id - P["id"] = get_maximal_particle_id() + 1 + p_dict["id"] = get_maximal_particle_id() + 1 else: - if particle_exists(P["id"]): - raise Exception(f"Particle {P['id']} already exists.") + if particle_exists(p_dict["id"]): + raise Exception(f"Particle {p_dict['id']} already exists.") # Prevent setting of contradicting attributes IF DIPOLES: - if 'dip' in P and 'dipm' in P: + if 'dip' in p_dict and 'dipm' in p_dict: raise ValueError("Contradicting attributes: dip and dipm. Setting \ dip is sufficient as the length of the vector defines the scalar dipole moment.") IF ROTATION: - if 'dip' in P and 'quat' in P: + if 'dip' in p_dict and 'quat' in p_dict: raise ValueError("Contradicting attributes: dip and quat. \ Setting dip overwrites the rotation of the particle around the dipole axis. \ Set quat and scalar dipole moment (dipm) instead.") # The ParticleList can not be used yet, as the particle # doesn't yet exist. Hence, the setting of position has to be - # done here. the code is from the pos:property of ParticleHandle + # done here. check_type_or_throw_except( - P["pos"], 3, float, "Position must be 3 floats.") - if place_particle(P["id"], make_Vector3d(P["pos"])) == -1: + p_dict["pos"], 3, float, "Position must be 3 floats.") + error_code = place_particle(p_dict["id"], make_Vector3d(p_dict["pos"])) + if error_code == -1: raise Exception("particle could not be set.") - # Pos is taken care of - del P["pos"] - pid = P["id"] - del P["id"] + # position is taken care of + del p_dict["pos"] + pid = p_dict.pop("id") - if P != {}: - self.by_id(pid).update(P) + if p_dict != {}: + self.by_id(pid).update(p_dict) return self.by_id(pid) - def _place_new_particles(self, Ps): + def _place_new_particles(self, p_list_dict): # Check if all entries have the same length - n_parts = len(Ps["pos"]) - if not all(np.shape(Ps[k]) and len(Ps[k]) == n_parts for k in Ps): + n_parts = len(p_list_dict["pos"]) + if not all(np.shape(v) and len(v) == + n_parts for v in p_list_dict.values()): raise ValueError( "When adding several particles at once, all lists of attributes have to have the same size") # If particle ids haven't been provided, use free ones # beyond the highest existing one - if not "id" in Ps: + if "id" not in p_list_dict: first_id = get_maximal_particle_id() + 1 - Ps["id"] = range(first_id, first_id + n_parts) + p_list_dict["id"] = range(first_id, first_id + n_parts) # Place the particles for i in range(n_parts): - P = {} - for k in Ps: - P[k] = Ps[k][i] - self._place_new_particle(P) + p_dict = {k: v[i] for k, v in p_list_dict.items()} + self._place_new_particle(p_dict) # Return slice of added particles - return self.by_ids(Ps["id"]) + return self.by_ids(p_list_dict["id"]) # Iteration over all existing particles def __iter__(self): diff --git a/testsuite/python/particle.py b/testsuite/python/particle.py index bf182147949..e812848bcac 100644 --- a/testsuite/python/particle.py +++ b/testsuite/python/particle.py @@ -274,11 +274,21 @@ def test_image_box(self): np.testing.assert_equal(np.copy(p.image_box), [1, 1, 1]) - def test_accessing_invalid_id_raises(self): + def test_invalid_particle_ids_exceptions(self): self.system.part.clear() handle_to_non_existing_particle = self.system.part.by_id(42) - with self.assertRaises(RuntimeError): + with self.assertRaisesRegex(RuntimeError, "Particle node for id 42 not found"): handle_to_non_existing_particle.id + p = self.system.part.add(pos=[0., 0., 0.], id=0) + with self.assertRaisesRegex(RuntimeError, "Particle node for id 42 not found"): + p._id = 42 + p.node + for i in range(1, 10): + with self.assertRaisesRegex(ValueError, f"Invalid particle id: {-i}"): + p._id = -i + p.node + with self.assertRaisesRegex(ValueError, f"Invalid particle id: {-i}"): + self.system.part.add(pos=[0., 0., 0.], id=-i) def test_parallel_property_setters(self): s = self.system From 87bd0f9813d4e636b52e1492577cf334b93caf16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 22 Mar 2022 12:53:45 +0100 Subject: [PATCH 99/99] doc: Apply code review suggestions Co-authored-by: Ingo Tischler --- doc/sphinx/advanced_methods.rst | 2 +- src/python/espressomd/interactions.pyx | 2 +- src/python/espressomd/script_interface.pyx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/sphinx/advanced_methods.rst b/doc/sphinx/advanced_methods.rst index 4140e432cb0..e2565235580 100644 --- a/doc/sphinx/advanced_methods.rst +++ b/doc/sphinx/advanced_methods.rst @@ -153,7 +153,7 @@ Example:: system.bonded_inter.add(h1) system.bonded_inter.add(h2) system.bond_breakage[h1] = espressomd.bond_breakage.BreakageSpec( - breakage_length=0.5, action_type="revert_center_bond") + breakage_length=0.5, action_type="delete_bond") p1 = system.part.add(id=1, pos=[0.00, 0.0, 0.0], v=[0.0, 0.0, 0.0]) p2 = system.part.add(id=2, pos=[0.46, 0.0, 0.0], v=[0.1, 0.0, 0.0]) diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 03777e23ca3..c1d20fa0334 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -2862,7 +2862,7 @@ class BondedInteractions(ScriptObjectMap): ------- remove(): Remove a bond from the list. - This is a noop if the bond does not exist. + This is a no-op if the bond does not exist. Parameters ---------- diff --git a/src/python/espressomd/script_interface.pyx b/src/python/espressomd/script_interface.pyx index 78f2f38d303..c8a76871c9b 100644 --- a/src/python/espressomd/script_interface.pyx +++ b/src/python/espressomd/script_interface.pyx @@ -407,7 +407,7 @@ class ScriptObjectMap(ScriptObjectRegistry): def remove(self, key): """ Remove the element with the given key. - This is a noop if the key does not exist. + This is a no-op if the key does not exist. """ self.__delitem__(key)