From fdcd9912716eb55992d182d1724f89490eb0fdc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 14 Jul 2022 16:33:06 +0200 Subject: [PATCH 01/85] python: Use consistent variable types --- doc/sphinx/integration.rst | 4 +-- doc/sphinx/magnetostatics.rst | 4 +-- doc/sphinx/particles.rst | 2 +- doc/sphinx/visualization.rst | 4 +-- .../ferrofluid/ferrofluid_part1.ipynb | 3 +- maintainer/benchmarks/ferrofluid.py | 2 +- samples/dancing.py | 6 ++-- samples/drude_bmimpf6.py | 2 +- samples/rigid_body.py | 3 +- src/core/electrostatics/elc.cpp | 2 +- src/core/electrostatics/mmm1d.cpp | 2 +- src/core/electrostatics/mmm1d_gpu.cpp | 2 +- src/core/electrostatics/p3m.cpp | 3 +- src/core/magnetostatics/dlc.cpp | 2 +- src/core/magnetostatics/dp3m.cpp | 3 +- testsuite/python/bond_breakage.py | 2 +- testsuite/python/brownian_dynamics.py | 4 +-- testsuite/python/brownian_dynamics_stats.py | 2 +- .../constraint_homogeneous_magnetic_field.py | 4 +-- testsuite/python/coulomb_interface.py | 11 +++--- testsuite/python/coulomb_mixed_periodicity.py | 4 +-- testsuite/python/dawaanr-and-bh-gpu.py | 2 +- testsuite/python/dawaanr-and-dds-gpu.py | 2 +- testsuite/python/dipolar_direct_summation.py | 4 +-- testsuite/python/dipolar_interface.py | 2 +- .../dipolar_mdlc_p3m_scafacos_p2nfft.py | 2 +- testsuite/python/drude.py | 8 ++--- testsuite/python/ek_charged_plate.py | 2 +- testsuite/python/elc_vs_analytic.py | 2 +- testsuite/python/engine_langevin.py | 2 +- testsuite/python/interactions_non-bonded.py | 4 +-- testsuite/python/langevin_thermostat.py | 6 ++-- testsuite/python/langevin_thermostat_stats.py | 4 +-- testsuite/python/lb.py | 6 ++-- testsuite/python/lb_stats.py | 2 +- testsuite/python/lb_switch.py | 2 +- testsuite/python/lees_edwards.py | 4 +-- testsuite/python/long_range_actors.py | 14 ++++---- .../python/mass-and-rinertia_per_particle.py | 8 ++--- testsuite/python/mmm1d.py | 4 +-- testsuite/python/npt_thermostat.py | 2 +- testsuite/python/observable_chain.py | 2 +- testsuite/python/p3m_fft.py | 2 +- testsuite/python/p3m_tuning_exceptions.py | 8 ++--- testsuite/python/pair_criteria.py | 8 ++--- testsuite/python/pairs.py | 8 ++--- testsuite/python/particle.py | 36 +++++++++---------- testsuite/python/particle_slice.py | 10 +++--- testsuite/python/pressure.py | 8 ++--- testsuite/python/random_pairs.py | 3 +- testsuite/python/rotation_per_particle.py | 10 +++--- .../python/rotational-diffusion-aniso.py | 7 ++-- testsuite/python/rotational_inertia.py | 4 +-- testsuite/python/save_checkpoint.py | 4 +-- testsuite/python/scafacos_dipoles_1d_2d.py | 8 ++--- testsuite/python/stokesian_dynamics.py | 12 +++---- testsuite/python/stokesian_thermostat.py | 8 ++--- testsuite/python/thermalized_bond.py | 4 +-- testsuite/python/thermostats_common.py | 2 +- testsuite/python/thole.py | 8 ++--- testsuite/python/virtual_sites_relative.py | 14 ++++---- 61 files changed, 159 insertions(+), 160 deletions(-) diff --git a/doc/sphinx/integration.rst b/doc/sphinx/integration.rst index dfe66078b02..fac5beef1b0 100644 --- a/doc/sphinx/integration.rst +++ b/doc/sphinx/integration.rst @@ -352,7 +352,7 @@ The following minimal example illustrates how to use the SDM in |es|:: system.periodicity = [False, False, False] system.time_step = 0.01 system.cell_system.skin = 0.4 - system.part.add(pos=[0, 0, 0], rotation=[1, 0, 0]) + system.part.add(pos=[0, 0, 0], rotation=[True, False, False]) system.integrator.set_stokesian_dynamics(viscosity=1.0, radii={0: 1.0}) system.integrator.run(100) @@ -690,7 +690,7 @@ needs to be activated via:: system.periodicity = [False, False, False] system.time_step = 0.01 system.cell_system.skin = 0.4 - system.part.add(pos=[0, 0, 0], rotation=[1, 0, 0], ext_force=[0, 0, -1]) + system.part.add(pos=[0, 0, 0], rotation=[True, False, False], ext_force=[0, 0, -1]) system.thermostat.set_stokesian(kT=1.0, seed=43) system.integrator.set_stokesian_dynamics(viscosity=1.0, radii={0: 1.0}) system.integrator.run(100) diff --git a/doc/sphinx/magnetostatics.rst b/doc/sphinx/magnetostatics.rst index 3afc02ba3d1..1732cb4a449 100644 --- a/doc/sphinx/magnetostatics.rst +++ b/doc/sphinx/magnetostatics.rst @@ -32,8 +32,8 @@ Magnetostatic interactions are activated via the actor framework:: system = espressomd.System(box_l=[10, 10, 10]) system.time_step = 0.01 - system.part.add(pos=[[0, 0, 0], [1, 1, 1]], - rotation=2 * [(1, 1, 1)], dip=2 * [(1, 0, 0)]) + system.part.add(pos=[[0, 0, 0], [1, 1, 1]], dip=2 * [(1, 0, 0)], + rotation=2 * [(True, True, True)]) actor = espressomd.magnetostatics.DipolarDirectSumCpu(prefactor=1.) system.actors.add(actor) diff --git a/doc/sphinx/particles.rst b/doc/sphinx/particles.rst index 405ddb41817..3d97a0462e2 100644 --- a/doc/sphinx/particles.rst +++ b/doc/sphinx/particles.rst @@ -654,7 +654,7 @@ Lattice-Boltzmann swimmers import espressomd system = espressomd.System(box_l=[1, 1, 1]) - system.part.add(pos=[2, 0, 0], rotation=[1, 1, 1], swimming={ + system.part.add(pos=[2, 0, 0], rotation=[True, True, True], swimming={ 'f_swim': 0.01, 'mode': 'pusher', 'dipole_length': 2.0}) For an explanation of the parameters ``v_swim`` and ``f_swim`` see the previous diff --git a/doc/sphinx/visualization.rst b/doc/sphinx/visualization.rst index 3416d336316..8f994cf56ff 100644 --- a/doc/sphinx/visualization.rst +++ b/doc/sphinx/visualization.rst @@ -209,12 +209,12 @@ feature):: for i in range(10): system.part.add(pos=np.random.random(3) * box_l, - rotation=[1, 1, 1], + rotation=[True, True, True], ext_torque=[5, 0, 0], v=[10, 0, 0], type=0) system.part.add(pos=np.random.random(3) * box_l, - rotation=[1, 1, 1], + rotation=[True, True, True], ext_torque=[0, 5, 0], v=[-10, 0, 0], type=1) diff --git a/doc/tutorials/ferrofluid/ferrofluid_part1.ipynb b/doc/tutorials/ferrofluid/ferrofluid_part1.ipynb index b7c8b21f7cb..42863f2a7e0 100644 --- a/doc/tutorials/ferrofluid/ferrofluid_part1.ipynb +++ b/doc/tutorials/ferrofluid/ferrofluid_part1.ipynb @@ -432,7 +432,8 @@ "outputs": [], "source": [ "# Add particles\n", - "system.part.add(pos=pos, rotation=N_PART * [(1, 1, 1)], dip=dip, fix=N_PART * [(0, 0, 1)])" + "system.part.add(pos=pos, rotation=N_PART * [(True, True, True)],\n", + " dip=dip, fix=N_PART * [(False, False, True)])" ] }, { diff --git a/maintainer/benchmarks/ferrofluid.py b/maintainer/benchmarks/ferrofluid.py index a580fe3f806..5b43da0a022 100644 --- a/maintainer/benchmarks/ferrofluid.py +++ b/maintainer/benchmarks/ferrofluid.py @@ -97,7 +97,7 @@ ############################################################# system.part.add( pos=np.random.random((n_part, 3)) * system.box_l, - rotation=n_part * [(1, 1, 1)], + rotation=n_part * [(True, True, True)], dipm=n_part * [args.dipole_moment]) # Warmup Integration diff --git a/samples/dancing.py b/samples/dancing.py index 458367bfcad..3404d1bd7eb 100644 --- a/samples/dancing.py +++ b/samples/dancing.py @@ -52,9 +52,9 @@ system.integrator.set_stokesian_dynamics( viscosity=1.0, radii={0: 1.0}, approximation_method=sd_method) -system.part.add(pos=[-5, 0, 0], rotation=[1, 1, 1]) -system.part.add(pos=[0, 0, 0], rotation=[1, 1, 1]) -system.part.add(pos=[7, 0, 0], rotation=[1, 1, 1]) +system.part.add(pos=[-5, 0, 0], rotation=[True, True, True]) +system.part.add(pos=[0, 0, 0], rotation=[True, True, True]) +system.part.add(pos=[7, 0, 0], rotation=[True, True, True]) gravity = espressomd.constraints.Gravity(g=[0, -1, 0]) system.constraints.add(gravity) diff --git a/samples/drude_bmimpf6.py b/samples/drude_bmimpf6.py index 6401f90cbdc..c689d43e267 100644 --- a/samples/drude_bmimpf6.py +++ b/samples/drude_bmimpf6.py @@ -211,7 +211,7 @@ def combination_rule_sigma(rule, sig1, sig2): cation_com = system.part.add( type=types["BMIM_COM"], pos=pos_com, mass=masses["BMIM_COM"], rinertia=[646.284, 585.158, 61.126], - gamma=0, rotation=[1, 1, 1]) + gamma=0, rotation=[True, True, True]) cation_c1 = system.part.add(type=types["BMIM_C1"], pos=pos_com + [0, -0.527, 1.365], q=charges["BMIM_C1"]) diff --git a/samples/rigid_body.py b/samples/rigid_body.py index 9e2db2a2096..b7b8edcd353 100644 --- a/samples/rigid_body.py +++ b/samples/rigid_body.py @@ -106,7 +106,8 @@ def rotate_vector(vector, axis, angle): # place center bead p_center = system.part.add( pos=center_of_mass, mass=branch_len * 6 + 1, rinertia=principal_moments, - rotation=[1, 1, 1], type=ParticleTypes.CENTER.value, quat=espressomd.rotation.matrix_to_quat(principal_axes)) + rotation=[True, True, True], type=ParticleTypes.CENTER.value, + quat=espressomd.rotation.matrix_to_quat(principal_axes)) # Relate the particles that make up the rigid body to the central particle. # This will also mark them as `virtual = True` diff --git a/src/core/electrostatics/elc.cpp b/src/core/electrostatics/elc.cpp index 1f437388aa1..445460f2c53 100644 --- a/src/core/electrostatics/elc.cpp +++ b/src/core/electrostatics/elc.cpp @@ -961,7 +961,7 @@ static auto calc_total_charge() { void ElectrostaticLayerCorrection::sanity_checks_periodicity() const { if (!box_geo.periodic(0) || !box_geo.periodic(1) || !box_geo.periodic(2)) { - throw std::runtime_error("ELC: requires periodicity (1 1 1)"); + throw std::runtime_error("ELC: requires periodicity (True, True, True)"); } } diff --git a/src/core/electrostatics/mmm1d.cpp b/src/core/electrostatics/mmm1d.cpp index 059645db24a..c32647c3f7a 100644 --- a/src/core/electrostatics/mmm1d.cpp +++ b/src/core/electrostatics/mmm1d.cpp @@ -131,7 +131,7 @@ CoulombMMM1D::CoulombMMM1D(double prefactor, double maxPWerror, void CoulombMMM1D::sanity_checks_periodicity() const { if (box_geo.periodic(0) || box_geo.periodic(1) || !box_geo.periodic(2)) { - throw std::runtime_error("MMM1D requires periodicity (0, 0, 1)"); + throw std::runtime_error("MMM1D requires periodicity (False, False, True)"); } } diff --git a/src/core/electrostatics/mmm1d_gpu.cpp b/src/core/electrostatics/mmm1d_gpu.cpp index eabba8ffc4d..0f83e4f3b5e 100644 --- a/src/core/electrostatics/mmm1d_gpu.cpp +++ b/src/core/electrostatics/mmm1d_gpu.cpp @@ -54,7 +54,7 @@ CoulombMMM1DGpu::CoulombMMM1DGpu(double prefactor, double maxPWerror, void CoulombMMM1DGpu::sanity_checks_periodicity() const { if (box_geo.periodic(0) || box_geo.periodic(1) || !box_geo.periodic(2)) { - throw std::runtime_error("MMM1D requires periodicity (0, 0, 1)"); + throw std::runtime_error("MMM1D requires periodicity (False, False, True)"); } } diff --git a/src/core/electrostatics/p3m.cpp b/src/core/electrostatics/p3m.cpp index 221c2640df9..d0d059157f1 100644 --- a/src/core/electrostatics/p3m.cpp +++ b/src/core/electrostatics/p3m.cpp @@ -758,7 +758,8 @@ void CoulombP3M::sanity_checks_boxl() const { void CoulombP3M::sanity_checks_periodicity() const { if (!box_geo.periodic(0) || !box_geo.periodic(1) || !box_geo.periodic(2)) { - throw std::runtime_error("CoulombP3M: requires periodicity (1 1 1)"); + throw std::runtime_error( + "CoulombP3M: requires periodicity (True, True, True)"); } } diff --git a/src/core/magnetostatics/dlc.cpp b/src/core/magnetostatics/dlc.cpp index 5374733840a..79ba4542df5 100644 --- a/src/core/magnetostatics/dlc.cpp +++ b/src/core/magnetostatics/dlc.cpp @@ -474,7 +474,7 @@ void DipolarLayerCorrection::adapt_solver() { void DipolarLayerCorrection::sanity_checks_node_grid() const { if (!box_geo.periodic(0) || !box_geo.periodic(1) || !box_geo.periodic(2)) { - throw std::runtime_error("DLC: requires periodicity (1 1 1)"); + throw std::runtime_error("DLC: requires periodicity (True, True, True)"); } } diff --git a/src/core/magnetostatics/dp3m.cpp b/src/core/magnetostatics/dp3m.cpp index 338c51cd2bd..e9e64276583 100644 --- a/src/core/magnetostatics/dp3m.cpp +++ b/src/core/magnetostatics/dp3m.cpp @@ -879,7 +879,8 @@ void DipolarP3M::sanity_checks_boxl() const { void DipolarP3M::sanity_checks_periodicity() const { if (!box_geo.periodic(0) || !box_geo.periodic(1) || !box_geo.periodic(2)) { - throw std::runtime_error("DipolarP3M: requires periodicity (1 1 1)"); + throw std::runtime_error( + "DipolarP3M: requires periodicity (True, True, True)"); } } diff --git a/testsuite/python/bond_breakage.py b/testsuite/python/bond_breakage.py index 92da7e1ec5f..5ae4e6c9c96 100644 --- a/testsuite/python/bond_breakage.py +++ b/testsuite/python/bond_breakage.py @@ -207,7 +207,7 @@ def setUp(self): self.system.integrator.set_vv() for i in range(part_num): - self.system.part.by_id(i).fix = [1, 1, 1] + self.system.part.by_id(i).fix = [True, True, True] self.system.thermostat.set_langevin(kT=0.0, gamma=1.0, seed=41) diff --git a/testsuite/python/brownian_dynamics.py b/testsuite/python/brownian_dynamics.py index e49eb3f643b..b99b78034cf 100644 --- a/testsuite/python/brownian_dynamics.py +++ b/testsuite/python/brownian_dynamics.py @@ -28,7 +28,7 @@ class BrownianThermostat(ut.TestCase): system = espressomd.System(box_l=[1.0, 1.0, 1.0]) system.cell_system.set_regular_decomposition(use_verlet_lists=True) system.cell_system.skin = 0 - system.periodicity = [0, 0, 0] + system.periodicity = [False, False, False] def setUp(self): np.random.seed(42) @@ -50,7 +50,7 @@ def reset_particle(): self.system.part.clear() p = system.part.add(pos=[0, 0, 0]) if espressomd.has_features("ROTATION"): - p.rotation = [1, 1, 1] + p.rotation = [True, True, True] if per_particle_gamma: assert espressomd.has_features("THERMOSTAT_PER_PARTICLE") if espressomd.has_features("PARTICLE_ANISOTROPY"): diff --git a/testsuite/python/brownian_dynamics_stats.py b/testsuite/python/brownian_dynamics_stats.py index 1e4941cf9be..1a39ce844c1 100644 --- a/testsuite/python/brownian_dynamics_stats.py +++ b/testsuite/python/brownian_dynamics_stats.py @@ -32,7 +32,7 @@ class BrownianThermostat(ut.TestCase, thermostats_common.ThermostatsCommon): system = espressomd.System(box_l=[1.0, 1.0, 1.0]) system.cell_system.set_regular_decomposition(use_verlet_lists=True) system.cell_system.skin = 0 - system.periodicity = [0, 0, 0] + system.periodicity = [False, False, False] def setUp(self): np.random.seed(42) diff --git a/testsuite/python/constraint_homogeneous_magnetic_field.py b/testsuite/python/constraint_homogeneous_magnetic_field.py index 0d30cf9d940..2d4d1773ffb 100644 --- a/testsuite/python/constraint_homogeneous_magnetic_field.py +++ b/testsuite/python/constraint_homogeneous_magnetic_field.py @@ -71,11 +71,11 @@ def test_add_energy_and_forces(self): # check dipolar energy when adding dipole moments p0 = self.system.part.add( - pos=[0, 0, 0], dip=dip_mom0, rotation=(1, 1, 1)) + pos=[0, 0, 0], dip=dip_mom0, rotation=(True, True, True)) self.assertEqual(self.system.analysis.energy()["dipolar"], -1.0 * np.dot(H_field, dip_mom0)) p1 = self.system.part.add( - pos=[1, 1, 1], dip=dip_mom1, rotation=(1, 1, 1)) + pos=[1, 1, 1], dip=dip_mom1, rotation=(True, True, True)) self.assertEqual(self.system.analysis.energy()["dipolar"], -(np.dot(H_field, dip_mom0) + np.dot(H_field, dip_mom1))) diff --git a/testsuite/python/coulomb_interface.py b/testsuite/python/coulomb_interface.py index 9dfa043c63b..bd1496b563a 100644 --- a/testsuite/python/coulomb_interface.py +++ b/testsuite/python/coulomb_interface.py @@ -203,13 +203,14 @@ def test_elc_p3m_exceptions(self): elc = espressomd.electrostatics.ELC( gap_size=4., maxPWerror=1., actor=p3m) self.system.actors.add(elc) - with self.assertRaisesRegex(Exception, r"ELC: requires periodicity \(1 1 1\)"): + periodicity_err_msg = r"requires periodicity \(True, True, True\)" + with self.assertRaisesRegex(Exception, periodicity_err_msg): self.system.periodicity = [False, False, False] - with self.assertRaisesRegex(Exception, r"requires periodicity \(1 1 1\)"): + with self.assertRaisesRegex(Exception, periodicity_err_msg): self.system.integrator.run(0, recalc_forces=True) - with self.assertRaisesRegex(Exception, r"requires periodicity \(1 1 1\)"): + with self.assertRaisesRegex(Exception, periodicity_err_msg): self.system.analysis.energy() - with self.assertRaisesRegex(Exception, r"requires periodicity \(1 1 1\)"): + with self.assertRaisesRegex(Exception, periodicity_err_msg): self.system.analysis.pressure() self.system.periodicity = [True, True, True] n_nodes = self.system.cell_system.get_state()["n_nodes"] @@ -239,7 +240,7 @@ def test_elc_p3m_exceptions(self): self.assertEqual(len(self.system.actors), 0) self.system.box_l = [10., 10., 10.] self.system.periodicity = [True, True, False] - with self.assertRaisesRegex(RuntimeError, r"ELC: requires periodicity \(1 1 1\)"): + with self.assertRaisesRegex(RuntimeError, periodicity_err_msg): elc = espressomd.electrostatics.ELC( gap_size=2., maxPWerror=1., actor=p3m) self.system.actors.add(elc) diff --git a/testsuite/python/coulomb_mixed_periodicity.py b/testsuite/python/coulomb_mixed_periodicity.py index cf96974c0a2..9a792c3eabc 100644 --- a/testsuite/python/coulomb_mixed_periodicity.py +++ b/testsuite/python/coulomb_mixed_periodicity.py @@ -84,7 +84,7 @@ def setup_elc_system(self): 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] + self.system.periodicity = [True, True, True] @utx.skipIfMissingFeatures(["P3M"]) def test_elc_cpu(self): @@ -117,7 +117,7 @@ def test_elc_gpu(self): @utx.skipIfMissingScafacosMethod("p2nfft") def test_scafacos_p2nfft(self): self.system.box_l = [10., 10., 10.] - self.system.periodicity = [1, 1, 0] + self.system.periodicity = [True, True, False] self.system.cell_system.set_regular_decomposition() scafacos = espressomd.electrostatics.Scafacos( diff --git a/testsuite/python/dawaanr-and-bh-gpu.py b/testsuite/python/dawaanr-and-bh-gpu.py index e64ff60abb3..5f7d0fbacd3 100644 --- a/testsuite/python/dawaanr-and-bh-gpu.py +++ b/testsuite/python/dawaanr-and-bh-gpu.py @@ -50,7 +50,7 @@ def test(self): ratio_dawaanr_bh_gpu = pf_dawaanr / pf_bh_gpu system = self.system system.box_l = 3 * [15] - system.periodicity = [0, 0, 0] + system.periodicity = [False, False, False] system.time_step = 1E-4 system.cell_system.skin = 0.1 diff --git a/testsuite/python/dawaanr-and-dds-gpu.py b/testsuite/python/dawaanr-and-dds-gpu.py index b1948a736e5..37f386bbc8d 100644 --- a/testsuite/python/dawaanr-and-dds-gpu.py +++ b/testsuite/python/dawaanr-and-dds-gpu.py @@ -42,7 +42,7 @@ def test(self): pf_dawaanr = 3.524 ratio_dawaanr_dds_gpu = pf_dawaanr / pf_dds_gpu self.system.box_l = 3 * [15] - self.system.periodicity = [0, 0, 0] + self.system.periodicity = [False, False, False] self.system.time_step = 1E-4 self.system.cell_system.skin = 0.1 diff --git a/testsuite/python/dipolar_direct_summation.py b/testsuite/python/dipolar_direct_summation.py index 5d98bf4ecb5..b39f9d9466d 100644 --- a/testsuite/python/dipolar_direct_summation.py +++ b/testsuite/python/dipolar_direct_summation.py @@ -142,7 +142,7 @@ def gen_reference_data(self, filepath_energy=OPEN_BOUNDARIES_REF_ENERGY, part_pos = np.random.random((N, 3)) * system.box_l part_dip = dipole_modulus * tests_common.random_dipoles(N) self.particles = system.part.add(pos=part_pos, dip=part_dip, - rotation=N * [(1, 1, 1)]) + rotation=N * [(True, True, True)]) # minimize system system.non_bonded_inter[0, 0].lennard_jones.set_params( @@ -182,7 +182,7 @@ def check_open_bc(self, method_func=None, energy_tol=None, ref_t = array_data[:, 9:12] self.particles = system.part.add( - pos=pos, dip=dip, rotation=[[1, 1, 1]] * len(pos)) + pos=pos, dip=dip, rotation=[[True, True, True]] * len(pos)) dds_e, dds_f, dds_t = method_func() self.assertAlmostEqual(dds_e, ref_e, delta=energy_tol) np.testing.assert_allclose(dds_f, ref_f, atol=force_tol) diff --git a/testsuite/python/dipolar_interface.py b/testsuite/python/dipolar_interface.py index 0b6edbcd3f3..93ebb1ad50b 100644 --- a/testsuite/python/dipolar_interface.py +++ b/testsuite/python/dipolar_interface.py @@ -136,7 +136,7 @@ def test_exceptions_serial(self): # run sanity checks self.system.periodicity = [True, True, False] ddsr = DDSR(prefactor=1., n_replica=1) - with self.assertRaisesRegex(Exception, r"DLC: requires periodicity \(1 1 1\)"): + with self.assertRaisesRegex(Exception, r"DLC: requires periodicity \(True, True, True\)"): mdlc = MDLC(gap_size=1., maxPWerror=1e-5, actor=ddsr) self.system.actors.add(mdlc) self.assertEqual(len(self.system.actors), 0) diff --git a/testsuite/python/dipolar_mdlc_p3m_scafacos_p2nfft.py b/testsuite/python/dipolar_mdlc_p3m_scafacos_p2nfft.py index 522448c670a..c262c71e57a 100644 --- a/testsuite/python/dipolar_mdlc_p3m_scafacos_p2nfft.py +++ b/testsuite/python/dipolar_mdlc_p3m_scafacos_p2nfft.py @@ -43,7 +43,7 @@ class Dipolar_p3m_mdlc_p2nfft(ut.TestCase): system = espressomd.System(box_l=[1.0, 1.0, 1.0]) system.time_step = 0.01 system.cell_system.skin = .4 - system.periodicity = [1, 1, 1] + system.periodicity = [True, True, True] def tearDown(self): self.system.part.clear() diff --git a/testsuite/python/drude.py b/testsuite/python/drude.py index 1d8cbd33e7e..99fef04bc1f 100644 --- a/testsuite/python/drude.py +++ b/testsuite/python/drude.py @@ -137,19 +137,19 @@ def test(self): pos_pf6 = box_center + np.array([0, dmol, 0]) part0 = system.part.add( type=types["PF6"], pos=pos_pf6, q=charges["PF6"], - mass=masses["PF6"], fix=[1, 1, 1]) + mass=masses["PF6"], fix=3 * [True]) # cation pos_com = box_center - np.array([0, dmol, 0]) part2 = system.part.add(id=2, type=types["BMIM_C1"], pos=pos_com + [0, -0.527, 1.365], q=charges["BMIM_C1"], - mass=masses["BMIM_C1"], fix=[1, 1, 1]) + mass=masses["BMIM_C1"], fix=3 * [True]) part4 = system.part.add(id=4, type=types["BMIM_C2"], pos=pos_com + [0, 1.641, 2.987], q=charges["BMIM_C2"], - mass=masses["BMIM_C2"], fix=[1, 1, 1]) + mass=masses["BMIM_C2"], fix=3 * [True]) part6 = system.part.add(id=6, type=types["BMIM_C3"], pos=pos_com + [0, 0.187, -2.389], q=charges["BMIM_C3"], - mass=masses["BMIM_C3"], fix=[1, 1, 1]) + mass=masses["BMIM_C3"], fix=3 * [True]) system.thermostat.set_langevin( kT=temperature_com, diff --git a/testsuite/python/ek_charged_plate.py b/testsuite/python/ek_charged_plate.py index 41137ea6419..6feba520d9e 100644 --- a/testsuite/python/ek_charged_plate.py +++ b/testsuite/python/ek_charged_plate.py @@ -45,7 +45,7 @@ def test(self): system.box_l = [box_x, box_y, box_z] system.cell_system.skin = 0.2 system.time_step = 0.1 - system.periodicity = [1, 1, 1] + system.periodicity = [True, True, True] bjerrum_length = 2.13569 agrid = 0.5 diff --git a/testsuite/python/elc_vs_analytic.py b/testsuite/python/elc_vs_analytic.py index 8c96ab10dc3..1cc1bbb1ca2 100644 --- a/testsuite/python/elc_vs_analytic.py +++ b/testsuite/python/elc_vs_analytic.py @@ -60,7 +60,7 @@ def test_elc(self): self.system.box_l = [self.box_l, self.box_l, self.box_l + self.elc_gap] self.system.cell_system.set_regular_decomposition( use_verlet_lists=True) - self.system.periodicity = [1, 1, 1] + self.system.periodicity = [True, True, True] prefactor = 2.0 p3m = self.p3m_class(prefactor=prefactor, accuracy=self.accuracy, mesh=[58, 58, 70], cao=4) diff --git a/testsuite/python/engine_langevin.py b/testsuite/python/engine_langevin.py index eabfb38b93a..14af967eb36 100644 --- a/testsuite/python/engine_langevin.py +++ b/testsuite/python/engine_langevin.py @@ -53,7 +53,7 @@ def z_v(t, z0): p0 = system.part.add(pos=pos_0, swimming={"v_swim": v_swim}) p1 = system.part.add(pos=pos_1, swimming={"f_swim": f_swim}) - system.part.all().rotation = (1, 1, 1) + system.part.all().rotation = (True, True, True) system.thermostat.set_langevin(kT=temp, gamma=gamma, seed=42) diff --git a/testsuite/python/interactions_non-bonded.py b/testsuite/python/interactions_non-bonded.py index 33bd14658a8..b5eaec6ec55 100644 --- a/testsuite/python/interactions_non-bonded.py +++ b/testsuite/python/interactions_non-bonded.py @@ -520,9 +520,9 @@ def setup_system(gb_params): self.system.part.clear() self.system.part.add( - pos=(1, 2, 3), rotation=(1, 1, 1), type=0) + pos=(1, 2, 3), rotation=3 * [True], type=0) self.system.part.add( - pos=(2.2, 2.1, 2.9), rotation=(1, 1, 1), type=0) + pos=(2.2, 2.1, 2.9), rotation=3 * [True], type=0) self.system.non_bonded_inter[0, 0].gay_berne.set_params( sig=sigma_0, cut=cut, eps=epsilon_0, k1=k_1, k2=k_2, mu=mu, diff --git a/testsuite/python/langevin_thermostat.py b/testsuite/python/langevin_thermostat.py index 2ec0940180a..1d701e0eef6 100644 --- a/testsuite/python/langevin_thermostat.py +++ b/testsuite/python/langevin_thermostat.py @@ -28,7 +28,7 @@ class LangevinThermostat(ut.TestCase): system = espressomd.System(box_l=[1.0, 1.0, 1.0]) system.cell_system.set_regular_decomposition(use_verlet_lists=True) system.cell_system.skin = 0 - system.periodicity = [0, 0, 0] + system.periodicity = [False, False, False] def setUp(self): np.random.seed(42) @@ -50,7 +50,7 @@ def reset_particle(): self.system.part.clear() p = system.part.add(pos=[0, 0, 0]) if espressomd.has_features("ROTATION"): - p.rotation = [1, 1, 1] + p.rotation = [True, True, True] if per_particle_gamma: assert espressomd.has_features("THERMOSTAT_PER_PARTICLE") if espressomd.has_features("PARTICLE_ANISOTROPY"): @@ -190,7 +190,7 @@ def test_03__friction_rot(self): system.time = 0 system.time_step = 0.0001 - p = system.part.add(pos=(0, 0, 0), omega_body=o0, rotation=(1, 1, 1)) + p = system.part.add(pos=(0, 0, 0), omega_body=o0, rotation=3 * [True]) if espressomd.has_features("ROTATIONAL_INERTIA"): rinertia = np.array((2, 2, 2)) p.rinertia = rinertia diff --git a/testsuite/python/langevin_thermostat_stats.py b/testsuite/python/langevin_thermostat_stats.py index e3848491eb6..6b4f5cf6be9 100644 --- a/testsuite/python/langevin_thermostat_stats.py +++ b/testsuite/python/langevin_thermostat_stats.py @@ -32,7 +32,7 @@ class LangevinThermostat(ut.TestCase, thermostats_common.ThermostatsCommon): system = espressomd.System(box_l=[1.0, 1.0, 1.0]) system.cell_system.set_regular_decomposition(use_verlet_lists=True) system.cell_system.skin = 0 - system.periodicity = [0, 0, 0] + system.periodicity = [False, False, False] def setUp(self): np.random.seed(42) @@ -100,7 +100,7 @@ def setup_diff_mass_rinertia(self, p): if espressomd.has_features("MASS"): p.mass = 0.5 if espressomd.has_features("ROTATION"): - p.rotation = [1, 1, 1] + p.rotation = [True, True, True] # Make sure rinertia does not change diff coeff if espressomd.has_features("ROTATIONAL_INERTIA"): p.rinertia = [0.4, 0.4, 0.4] diff --git a/testsuite/python/lb.py b/testsuite/python/lb.py index b15f9d090f6..bc32ae7b99b 100644 --- a/testsuite/python/lb.py +++ b/testsuite/python/lb.py @@ -49,7 +49,7 @@ class TestLB: 'temp': 1.5, 'gamma': 1.5} - system.periodicity = [1, 1, 1] + system.periodicity = [True, True, True] system.time_step = params['time_step'] system.cell_system.skin = 1.0 interpolation = False @@ -377,7 +377,7 @@ def test_viscous_coupling(self): self.system.thermostat.set_lb( LB_fluid=lbf, seed=3, gamma=self.params['friction']) p = self.system.part.add( - pos=[0.5 * self.params['agrid']] * 3, v=v_part, fix=[1, 1, 1]) + pos=[0.5 * self.params['agrid']] * 3, v=v_part, fix=3 * [True]) lbf[0, 0, 0].velocity = v_fluid if self.interpolation: v_fluid = lbf.get_interpolated_velocity(p.pos) @@ -414,7 +414,7 @@ def test_unequal_time_step(self): where particles don't move. """ - p = self.system.part.add(pos=[0.1, 0.2, 0.3], fix=[1, 1, 1]) + p = self.system.part.add(pos=[0.1, 0.2, 0.3], fix=3 * [True]) ext_force_density = [2.3, 1.2, 0.1] lbf = self.lb_class( visc=self.params['viscosity'], diff --git a/testsuite/python/lb_stats.py b/testsuite/python/lb_stats.py index 8ddc1333df4..863977a00d6 100644 --- a/testsuite/python/lb_stats.py +++ b/testsuite/python/lb_stats.py @@ -41,7 +41,7 @@ class TestLB: 'temp': 1.5, 'gamma': 1.5} - system.periodicity = [1, 1, 1] + system.periodicity = [True, True, True] system.time_step = 0.01 system.cell_system.skin = 1.0 dof = 3. diff --git a/testsuite/python/lb_switch.py b/testsuite/python/lb_switch.py index 41d03818695..728dd908f57 100644 --- a/testsuite/python/lb_switch.py +++ b/testsuite/python/lb_switch.py @@ -35,7 +35,7 @@ class LBSwitchActor(ut.TestCase): def switch_test(self, GPU=False): system = self.system system.actors.clear() - p = system.part.add(pos=[1., 1., 1.], v=[1., 0, 0], fix=[1, 1, 1]) + p = system.part.add(pos=[1., 1., 1.], v=[1., 0, 0], fix=3 * [True]) lb_fluid_params = {'agrid': 2.0, 'dens': 1.0, 'visc': 1.0, 'tau': 0.03} friction_1 = 1.5 diff --git a/testsuite/python/lees_edwards.py b/testsuite/python/lees_edwards.py index fb6c64f0435..b635724284b 100644 --- a/testsuite/python/lees_edwards.py +++ b/testsuite/python/lees_edwards.py @@ -472,8 +472,8 @@ def test_virt_sites_interaction(self): shear_velocity=2.0, initial_pos_offset=0.0) system.lees_edwards.set_boundary_conditions( shear_direction="x", shear_plane_normal="y", protocol=protocol) - p1 = system.part.add( - pos=[2.5, 2.5, 2.5], rotation=(1, 1, 1), type=10, v=(0.0, -0.1, -0.25)) + p1 = system.part.add(pos=[2.5, 2.5, 2.5], type=10, + rotation=3 * (True,), 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) diff --git a/testsuite/python/long_range_actors.py b/testsuite/python/long_range_actors.py index 1682c16059b..c7c2f57947d 100644 --- a/testsuite/python/long_range_actors.py +++ b/testsuite/python/long_range_actors.py @@ -44,7 +44,7 @@ def tearDown(self): self.system.actors.clear() self.system.thermostat.turn_off() self.system.integrator.set_vv() - self.system.periodicity = [1, 1, 1] + self.system.periodicity = [True, True, True] self.system.cell_system.set_regular_decomposition() def add_charged_particles(self): @@ -309,26 +309,26 @@ def check_mmm1d_exceptions(self, mmm1d_class): self.system.cell_system.set_n_square() # check periodicity exceptions - for periodicity in itertools.product(range(2), range(2), range(2)): - if periodicity == (0, 0, 1): + for periodicity in itertools.product((True, False), repeat=3): + if periodicity == (False, False, True): continue self.system.periodicity = periodicity - with self.assertRaisesRegex(Exception, r"MMM1D requires periodicity \(0, 0, 1\)"): + with self.assertRaisesRegex(Exception, r"MMM1D requires periodicity \(False, False, True\)"): mmm1d = mmm1d_class(prefactor=1., maxPWerror=1e-2) self.system.actors.add(mmm1d) self.assertEqual(len(self.system.actors), 0) self.assertFalse(mmm1d.is_tuned) - self.system.periodicity = (0, 0, 1) + self.system.periodicity = (False, False, True) @utx.skipIfMissingFeatures(["ELECTROSTATICS"]) def test_mmm1d_cpu_exceptions(self): - self.system.periodicity = (0, 0, 1) + self.system.periodicity = (False, False, True) self.check_mmm1d_exceptions(espressomd.electrostatics.MMM1D) @utx.skipIfMissingGPU() @utx.skipIfMissingFeatures(["MMM1D_GPU"]) def test_mmm1d_gpu_exceptions(self): - self.system.periodicity = (0, 0, 1) + self.system.periodicity = (False, False, True) self.check_mmm1d_exceptions(espressomd.electrostatics.MMM1DGPU) with self.assertRaisesRegex(ValueError, "switching radius must not be larger than box length"): diff --git a/testsuite/python/mass-and-rinertia_per_particle.py b/testsuite/python/mass-and-rinertia_per_particle.py index 26eb2f7c725..b2ed0d3fe75 100644 --- a/testsuite/python/mass-and-rinertia_per_particle.py +++ b/testsuite/python/mass-and-rinertia_per_particle.py @@ -111,7 +111,7 @@ def dissipation_param_setup(self, n): # Space self.system.box_l = 3 * [1.0] - self.system.periodicity = [0, 0, 0] + self.system.periodicity = [False, False, False] # NVT thermostat self.kT = 0.0 @@ -155,7 +155,7 @@ def dissipation_param_setup(self, n): for k in range(2): ind = i + k * n partcl = self.system.part.add( - rotation=(1, 1, 1), pos=(0.0, 0.0, 0.0), id=ind) + rotation=3 * [True], pos=(0.0, 0.0, 0.0), id=ind) partcl.v = [1.0, 1.0, 1.0] if espressomd.has_features("ROTATION"): partcl.omega_body = [1.0, 1.0, 1.0] @@ -179,7 +179,7 @@ def fluctuation_dissipation_param_setup(self, n): # Space box = 10.0 self.system.box_l = 3 * [box] - self.system.periodicity = [0, 0, 0] + self.system.periodicity = [False, False, False] # NVT thermostat # Just some temperature range to cover by the test: @@ -218,7 +218,7 @@ def fluctuation_dissipation_param_setup(self, n): part_v = [0.0, 0.0, 0.0] part_omega_body = [0.0, 0.0, 0.0] partcl = self.system.part.add( - rotation=(1, 1, 1), + rotation=3 * [True], id=ind, mass=self.mass, rinertia=self.J, diff --git a/testsuite/python/mmm1d.py b/testsuite/python/mmm1d.py index b270fe01ad9..bb69adfc257 100644 --- a/testsuite/python/mmm1d.py +++ b/testsuite/python/mmm1d.py @@ -27,7 +27,7 @@ class ElectrostaticInteractionsTests: # Handle to espresso system system = espressomd.System(box_l=[10.0] * 3) - system.periodicity = [0, 0, 1] + system.periodicity = [False, False, True] system.time_step = 0.01 system.cell_system.skin = 0.4 system.cell_system.set_n_square() @@ -41,7 +41,7 @@ class ElectrostaticInteractionsTests: def setUp(self): self.system.box_l = [10.0] * 3 - self.system.periodicity = [0, 0, 1] + self.system.periodicity = [False, False, True] self.system.cell_system.set_n_square() def tearDown(self): diff --git a/testsuite/python/npt_thermostat.py b/testsuite/python/npt_thermostat.py index d9e039c7255..70bdd088c1e 100644 --- a/testsuite/python/npt_thermostat.py +++ b/testsuite/python/npt_thermostat.py @@ -29,7 +29,7 @@ class NPTThermostat(ut.TestCase): """Test NpT dynamics""" system = espressomd.System(box_l=[1.0, 1.0, 1.0]) system.cell_system.skin = 0. - system.periodicity = [1, 1, 1] + system.periodicity = [True, True, True] def setUp(self): np.random.seed(42) diff --git a/testsuite/python/observable_chain.py b/testsuite/python/observable_chain.py index 02d9f63c694..d30a44ebd4e 100644 --- a/testsuite/python/observable_chain.py +++ b/testsuite/python/observable_chain.py @@ -44,7 +44,7 @@ class ObservableTests(ut.TestCase): n_parts = 5 box_l = 5. system = espressomd.System(box_l=3 * [box_l]) - system.periodicity = [1, 1, 1] + system.periodicity = [True, True, True] system.time_step = 0.01 system.cell_system.skin = 0.2 * box_l diff --git a/testsuite/python/p3m_fft.py b/testsuite/python/p3m_fft.py index db3beab520c..481fc865c4f 100644 --- a/testsuite/python/p3m_fft.py +++ b/testsuite/python/p3m_fft.py @@ -81,7 +81,7 @@ def add_magnetic_particles(self): positions = np.random.random((num_part, 3)) dipoles = tests_common.random_dipoles(num_part) self.system.part.add(pos=positions * self.system.box_l, - dip=dipoles, rotation=num_part * [(1, 1, 1)]) + dip=dipoles, rotation=num_part * [3 * [True]]) self.minimize() @ut.skipIf(n_nodes not in FFT_PLANS, f"no FFT plan for {n_nodes} threads") diff --git a/testsuite/python/p3m_tuning_exceptions.py b/testsuite/python/p3m_tuning_exceptions.py index 4748da63ca6..9f60452d09b 100644 --- a/testsuite/python/p3m_tuning_exceptions.py +++ b/testsuite/python/p3m_tuning_exceptions.py @@ -223,12 +223,12 @@ def check_invalid_params(self, class_solver, **custom_params): def check_invalid_cell_systems(self): # check periodicity exceptions - for periodicity in itertools.product(range(2), range(2), range(2)): - if periodicity == (1, 1, 1): + for periodicity in itertools.product((True, False), repeat=3): + if periodicity == (True, True, True): continue - with self.assertRaisesRegex(Exception, r"P3M: requires periodicity \(1 1 1\)"): + with self.assertRaisesRegex(Exception, r"P3M: requires periodicity \(True, True, True\)"): self.system.periodicity = periodicity - self.system.periodicity = (1, 1, 1) + self.system.periodicity = (True, True, True) # check cell system exceptions with self.assertRaisesRegex(Exception, "P3M: requires the regular decomposition cell system"): diff --git a/testsuite/python/pair_criteria.py b/testsuite/python/pair_criteria.py index 31648265bd7..57d903e5cde 100644 --- a/testsuite/python/pair_criteria.py +++ b/testsuite/python/pair_criteria.py @@ -45,7 +45,7 @@ def test_distance_crit_periodic(self): # Decisions # Periodic system. Particles in range via minimum image convention - self.system.periodicity = 3 * [True] + self.system.periodicity = (True, True, True) self.assertTrue(dc.decide(self.p1, self.p2)) self.assertTrue(dc.decide(self.p1.id, self.p2.id)) @@ -53,7 +53,7 @@ def test_distance_crit_non_periodic(self): dc = espressomd.pair_criteria.DistanceCriterion(cut_off=0.1) # Non-periodic system. Particles out of range - self.system.periodicity = (0, 0, 0) + self.system.periodicity = (False, False, False) self.assertTrue(not dc.decide(self.p1, self.p2)) self.assertTrue(not dc.decide(self.p1.id, self.p2.id)) @@ -74,7 +74,7 @@ def test_energy_crit(self): # Decisions # Periodic system. Particles in range via minimum image convention - self.system.periodicity = (1, 1, 1) + self.system.periodicity = (True, True, True) self.assertTrue(ec.decide(self.p1, self.p2)) self.assertTrue(ec.decide(self.p1.id, self.p2.id)) @@ -89,7 +89,7 @@ def test_energy_crit_non_periodic(self): self.assertTrue(abs(ec.get_params()["cut_off"] - 0.001) < self.epsilon) # Non-periodic system. Particles out of range - self.system.periodicity = (0, 0, 0) + self.system.periodicity = (False, False, False) self.assertTrue(not ec.decide(self.p1, self.p2)) self.assertTrue(not ec.decide(self.p1.id, self.p2.id)) diff --git a/testsuite/python/pairs.py b/testsuite/python/pairs.py index b9505c4afc4..9fa89f2aacf 100644 --- a/testsuite/python/pairs.py +++ b/testsuite/python/pairs.py @@ -102,23 +102,23 @@ def run_and_check(self, n_steps=100): def test_nsquare(self): self.system.cell_system.set_n_square() - self.system.periodicity = [1, 1, 1] + self.system.periodicity = [True, True, True] self.run_and_check() def test_nsquare_partial_z(self): self.system.cell_system.set_n_square() - self.system.periodicity = [1, 1, 0] + self.system.periodicity = [True, True, False] self.run_and_check() def test_dd(self): self.system.cell_system.set_regular_decomposition() - self.system.periodicity = [1, 1, 1] + self.system.periodicity = [True, True, True] self.run_and_check() self.check_range_exception() def test_dd_partial_z(self): self.system.cell_system.set_regular_decomposition() - self.system.periodicity = [1, 1, 0] + self.system.periodicity = [True, True, False] self.run_and_check() self.check_range_exception() diff --git a/testsuite/python/particle.py b/testsuite/python/particle.py index 5f735e89626..eb1002fa594 100644 --- a/testsuite/python/particle.py +++ b/testsuite/python/particle.py @@ -22,6 +22,7 @@ import espressomd.interactions import numpy as np import collections +import itertools class ParticleProperties(ut.TestCase): @@ -49,7 +50,7 @@ def setUp(self): def tearDown(self): self.system.part.clear() - def generateTestForVectorProperty(_propName, _value): + def generateTestForVectorProperty(_propName, *_values): """Generates test cases for vectorial particle properties such as position, velocity... 1st arg: name of the property (e.g., "pos"), @@ -57,21 +58,22 @@ def generateTestForVectorProperty(_propName, _value): """ # This is executed, when generateTestForVectorProperty() is called propName = _propName - value = _value + values = _values def func(self): # This code is run at the execution of the generated function. # It will use the state of the variables in the outer function, # which was there, when the outer function was called - setattr(self.partcl, propName, value) - np.testing.assert_allclose( - np.array(getattr(self.partcl, propName)), value, - err_msg=propName + ": value set and value gotten back differ.", - atol=self.tol) + for value in values: + setattr(self.partcl, propName, value) + np.testing.assert_allclose( + np.array(getattr(self.partcl, propName)), value, + err_msg=propName + ": value set and value gotten back differ.", + atol=self.tol) return func - def generateTestForScalarProperty(_propName, _value): + def generateTestForScalarProperty(_propName, *_values): """Generates test cases for scalar particle properties such as type, mass, charge... 1st arg: name of the property (e.g., "type"), @@ -79,15 +81,16 @@ def generateTestForScalarProperty(_propName, _value): """ # This is executed, when generateTestForVectorProperty() is called propName = _propName - value = _value + values = _values def func(self): # This code is run at the execution of the generated function. # It will use the state of the variables in the outer function, # which was there, when the outer function was called - setattr(self.partcl, propName, value) - self.assertEqual(getattr(self.partcl, propName), - value, propName + ": value set and value gotten back differ.") + for value in values: + setattr(self.partcl, propName, value) + self.assertEqual(getattr(self.partcl, propName), + value, propName + ": value set and value gotten back differ.") return func @@ -104,13 +107,8 @@ def func(self): test_mass = generateTestForScalarProperty("mass", 1.3) if espressomd.has_features(["ROTATION"]): - - for x in 0, 1: - for y in 0, 1: - for z in 0, 1: - test_rotation = generateTestForVectorProperty( - "rotation", np.array([x, y, z], dtype=int)) - + test_rotation = generateTestForVectorProperty( + "rotation", *itertools.product((True, False), repeat=3)) test_omega_lab = generateTestForVectorProperty( "omega_lab", np.array([4., 2., 1.])) test_omega_body = generateTestForVectorProperty( diff --git a/testsuite/python/particle_slice.py b/testsuite/python/particle_slice.py index 7885eb3977f..7b2561ba5ae 100644 --- a/testsuite/python/particle_slice.py +++ b/testsuite/python/particle_slice.py @@ -52,23 +52,23 @@ def tearDown(self): @utx.skipIfMissingFeatures(["EXTERNAL_FORCES"]) def test_1_set_different_values(self): - self.fix_flags[0] = [1, 0, 0] - self.fix_flags[1] = [1, 0, 0] + self.fix_flags[0] = [True, False, False] + self.fix_flags[1] = [True, False, False] self.p0p1.fix = self.fix_flags np.testing.assert_array_equal( np.copy(self.p0p1.fix), self.fix_flags) @utx.skipIfMissingFeatures(["EXTERNAL_FORCES"]) def test_2_set_same_value(self): - self.fix_flags[0] = [0, 1, 0] - self.fix_flags[1] = [0, 1, 0] + self.fix_flags[0] = [False, True, False] + self.fix_flags[1] = [False, True, False] self.p0p1.fix = self.fix_flags[1] np.testing.assert_array_equal( np.copy(self.p0p1.fix), self.fix_flags) @utx.skipIfMissingFeatures(["EXTERNAL_FORCES"]) def test_3_set_one_value(self): - self.fix_flags[1] = [0, 0, 1] + self.fix_flags[1] = [False, False, True] self.p1.fix = self.fix_flags[1] np.testing.assert_array_equal( np.copy(self.p0p1.fix), self.fix_flags) diff --git a/testsuite/python/pressure.py b/testsuite/python/pressure.py index 3e7d3b31538..005d1a241d2 100644 --- a/testsuite/python/pressure.py +++ b/testsuite/python/pressure.py @@ -107,7 +107,7 @@ def test(self): # thermostat and cell system system.thermostat.set_langevin(kT=0.0, gamma=1.0, seed=41) system.cell_system.skin = skin - system.periodicity = [1, 1, 1] + system.periodicity = [True, True, True] # particles and bond p0 = system.part.add(pos=[9.9, 9.75, 9.9], type=0, mol_id=0) @@ -282,13 +282,13 @@ def test_fene(self): # thermostat and cell system system.cell_system.skin = skin - system.periodicity = [1, 1, 1] + system.periodicity = [True, True, True] # particles and bond p0 = system.part.add( - pos=[9.9, 9.75, 9.9], type=0, mol_id=0, fix=[1, 1, 1]) + pos=[9.9, 9.75, 9.9], type=0, mol_id=0, fix=3 * [True]) p1 = system.part.add( - pos=[9.9, 10.25, 9.9], type=0, mol_id=0, fix=[1, 1, 1]) + pos=[9.9, 10.25, 9.9], type=0, mol_id=0, fix=3 * [True]) k = 1e4 d_r_max = 1.5 diff --git a/testsuite/python/random_pairs.py b/testsuite/python/random_pairs.py index ffc66eb291d..fcad71bfbf6 100644 --- a/testsuite/python/random_pairs.py +++ b/testsuite/python/random_pairs.py @@ -92,11 +92,10 @@ def check_n_squared(self, n2_pairs): self.check_pairs(n2_pairs) def test(self): - periods = [0, 1] self.system.periodicity = [True, True, True] tests_common.check_non_bonded_loop_trace(self, self.system) - for periodicity in itertools.product(periods, periods, periods): + for periodicity in itertools.product((False, True), repeat=3): self.system.periodicity = periodicity n2_pairs = self.pairs_n2(1.5) diff --git a/testsuite/python/rotation_per_particle.py b/testsuite/python/rotation_per_particle.py index 56526558189..d8bb7c58c21 100644 --- a/testsuite/python/rotation_per_particle.py +++ b/testsuite/python/rotation_per_particle.py @@ -67,8 +67,8 @@ def test_axes_changes(self): p.quat = [1, 0, 0, 0] # Enable rotation in a single direction - rot = [0, 0, 0] - rot[direction] = 1 + rot = [False, False, False] + rot[direction] = True p.rotation = rot system.integrator.run(130) @@ -86,7 +86,7 @@ def test_axes_changes(self): np.dot(axis, p.convert_vector_body_to_space(axis)), 0.95) def test_frame_conversion_and_rotation(self): - p = self.system.part.add(pos=np.random.random(3), rotation=(1, 1, 1)) + p = self.system.part.add(pos=np.random.random(3), rotation=3 * [True]) # Space and body frame co-incide? np.testing.assert_allclose( @@ -127,12 +127,12 @@ def test_frame_conversion_and_rotation(self): def test_rotation_mpi_communication(self): system = self.system # place particle in cell with MPI rank 0 - p = system.part.add(pos=0.01 * system.box_l, rotation=(1, 1, 1)) + p = system.part.add(pos=0.01 * system.box_l, rotation=3 * [True]) p.rotate((1, 0, 0), -np.pi / 2) np.testing.assert_array_almost_equal( np.copy(p.director), [0, 1, 0], decimal=10) # place particle in cell with MPI rank N-1 - p = system.part.add(pos=0.99 * system.box_l, rotation=(1, 1, 1)) + p = system.part.add(pos=0.99 * system.box_l, rotation=3 * [True]) p.rotate((1, 0, 0), -np.pi / 2) np.testing.assert_array_almost_equal( np.copy(p.director), [0, 1, 0], decimal=10) diff --git a/testsuite/python/rotational-diffusion-aniso.py b/testsuite/python/rotational-diffusion-aniso.py index 22c2b07ef54..c580d0b60b7 100644 --- a/testsuite/python/rotational-diffusion-aniso.py +++ b/testsuite/python/rotational-diffusion-aniso.py @@ -32,7 +32,7 @@ class RotDiffAniso(ut.TestCase): # Handle for espresso system system = espressomd.System(box_l=3 * [10.]) system.cell_system.skin = 5.0 - system.periodicity = [0, 0, 0] + system.periodicity = [False, False, False] # The time step should be less than t0 ~ mass / gamma system.time_step = 3E-3 @@ -58,8 +58,9 @@ def tearDown(self): def add_particles(self): positions = np.random.random((self.n_part, 3)) * self.system.box_l[0] - self.partcls = self.system.part.add(pos=positions, rotation=self.n_part * [(1, 1, 1)], - rinertia=self.n_part * [self.J]) + self.partcls = self.system.part.add( + pos=positions, rotation=self.n_part * [3 * [True]], + rinertia=self.n_part * [self.J]) if espressomd.has_features("ROTATION"): self.partcls.omega_body = [0.0, 0.0, 0.0] diff --git a/testsuite/python/rotational_inertia.py b/testsuite/python/rotational_inertia.py index 871aa82bb76..8549b3b62ee 100644 --- a/testsuite/python/rotational_inertia.py +++ b/testsuite/python/rotational_inertia.py @@ -51,7 +51,7 @@ def set_L(self, p): self.L_lab = tests_common.convert_vec_body_to_space(p, L_body) def test_stability(self): - p = self.system.part.add(pos=[0.0, 0.0, 0.0], rotation=(1, 1, 1)) + p = self.system.part.add(pos=[0.0, 0.0, 0.0], rotation=3 * [True]) # Inertial motion around the stable and unstable axes @@ -137,7 +137,7 @@ def momentum(self, p): def test_energy_and_momentum_conservation(self): system = self.system p = system.part.add(pos=(0, 0, 0), rinertia=(1.1, 1.3, 1.5), - rotation=(1, 1, 1), omega_body=(2, 1, 4)) + rotation=3 * [True], omega_body=(2, 1, 4)) E0 = self.energy(p) m0 = self.momentum(p) system.time_step = 0.001 diff --git a/testsuite/python/save_checkpoint.py b/testsuite/python/save_checkpoint.py index a60d66674a6..591d4e2d072 100644 --- a/testsuite/python/save_checkpoint.py +++ b/testsuite/python/save_checkpoint.py @@ -198,7 +198,7 @@ elif 'THERM.DPD' in modes and espressomd.has_features('DPD'): system.thermostat.set_dpd(kT=1.0, seed=42) elif 'THERM.SDM' in modes and espressomd.has_features('STOKESIAN_DYNAMICS'): - system.periodicity = [0, 0, 0] + system.periodicity = [False, False, False] system.thermostat.set_stokesian(kT=1.0, seed=42) # set integrator if 'INT.NPT' in modes and espressomd.has_features('NPT'): @@ -212,7 +212,7 @@ elif 'INT.BD' in modes: system.integrator.set_brownian_dynamics() elif 'INT.SDM' in modes and espressomd.has_features('STOKESIAN_DYNAMICS'): - system.periodicity = [0, 0, 0] + system.periodicity = [False, False, False] system.integrator.set_stokesian_dynamics( approximation_method='ft', viscosity=0.5, radii={0: 1.5}, pair_mobility=False, self_mobility=True) diff --git a/testsuite/python/scafacos_dipoles_1d_2d.py b/testsuite/python/scafacos_dipoles_1d_2d.py index 6502d6bb88f..4bb939e3101 100644 --- a/testsuite/python/scafacos_dipoles_1d_2d.py +++ b/testsuite/python/scafacos_dipoles_1d_2d.py @@ -36,12 +36,12 @@ class Scafacos1d2d(ut.TestCase): system = espressomd.System(box_l=[1.0, 1.0, 1.0]) system.time_step = 0.01 system.cell_system.skin = 0.5 - system.periodicity = [1, 1, 1] + system.periodicity = [True, True, True] def tearDown(self): self.system.part.clear() self.system.actors.clear() - self.system.periodicity = [1, 1, 1] + self.system.periodicity = [True, True, True] def test_scafacos(self): system = self.system @@ -61,9 +61,9 @@ def test_scafacos(self): # Read reference data if dim == 2: file_prefix = "mdlc" - system.periodicity = [1, 1, 0] + system.periodicity = [True, True, False] else: - system.periodicity = [1, 0, 0] + system.periodicity = [True, False, False] file_prefix = "scafacos_dipoles_1d" ref_e_path = tests_common.data_path( diff --git a/testsuite/python/stokesian_dynamics.py b/testsuite/python/stokesian_dynamics.py index 869afb05335..250e606a6bc 100644 --- a/testsuite/python/stokesian_dynamics.py +++ b/testsuite/python/stokesian_dynamics.py @@ -39,7 +39,7 @@ class StokesianDynamicsTest(ut.TestCase): def setUp(self): self.system.box_l = [10] * 3 - self.system.periodicity = [0, 0, 0] + self.system.periodicity = [False, False, False] self.system.cell_system.skin = 0.4 def tearDown(self): @@ -48,9 +48,9 @@ def tearDown(self): def falling_spheres(self, time_step, l_factor, t_factor, sd_method='fts'): self.system.time_step = time_step - self.system.part.add(pos=[-5 * l_factor, 0, 0], rotation=[1, 1, 1]) - self.system.part.add(pos=[0 * l_factor, 0, 0], rotation=[1, 1, 1]) - self.system.part.add(pos=[7 * l_factor, 0, 0], rotation=[1, 1, 1]) + self.system.part.add(pos=[-5 * l_factor, 0, 0], rotation=3 * [True]) + self.system.part.add(pos=[0 * l_factor, 0, 0], rotation=3 * [True]) + self.system.part.add(pos=[7 * l_factor, 0, 0], rotation=3 * [True]) self.system.integrator.set_stokesian_dynamics( viscosity=1.0 / (t_factor * l_factor), @@ -116,7 +116,7 @@ class StokesianDiffusionTest(ut.TestCase): def setUp(self): self.system.box_l = [10] * 3 - self.system.periodicity = [0, 0, 0] + self.system.periodicity = [False, False, False] self.system.time_step = 1.0 self.system.cell_system.skin = 0.4 @@ -127,7 +127,7 @@ def tearDown(self): self.system.thermostat.set_stokesian(kT=0) def test(self): - p = self.system.part.add(pos=[0, 0, 0], rotation=[1, 1, 1]) + p = self.system.part.add(pos=[0, 0, 0], rotation=3 * [True]) self.system.integrator.set_stokesian_dynamics( viscosity=self.eta, radii={0: self.R}) self.system.thermostat.set_stokesian(kT=self.kT, seed=42) diff --git a/testsuite/python/stokesian_thermostat.py b/testsuite/python/stokesian_thermostat.py index c33b9c77bae..ca3113a704a 100644 --- a/testsuite/python/stokesian_thermostat.py +++ b/testsuite/python/stokesian_thermostat.py @@ -28,7 +28,7 @@ class StokesianThermostat(ut.TestCase): """Test Stokesian thermostat""" system = espressomd.System(box_l=[1.0, 1.0, 1.0]) system.cell_system.skin = 0 - system.periodicity = [0, 0, 0] + system.periodicity = [False, False, False] def setUp(self): np.random.seed(42) @@ -111,17 +111,17 @@ def test_integrator_exceptions(self): # invalid PBC should throw exceptions self.system.integrator.set_vv() - self.system.periodicity = [0, 0, 1] + self.system.periodicity = [False, False, True] with self.assertRaises(RuntimeError): self.system.integrator.set_stokesian_dynamics( viscosity=1.0, radii={0: 1.0}) - self.system.periodicity = [0, 0, 0] + self.system.periodicity = [False, False, False] self.system.integrator.set_stokesian_dynamics( viscosity=1.0, radii={0: 1.0}) with self.assertRaises(Exception): - self.system.periodicity = [0, 1, 0] + self.system.periodicity = [False, True, False] if __name__ == "__main__": diff --git a/testsuite/python/thermalized_bond.py b/testsuite/python/thermalized_bond.py index 5cc983e674d..a89717b8060 100644 --- a/testsuite/python/thermalized_bond.py +++ b/testsuite/python/thermalized_bond.py @@ -48,7 +48,7 @@ def test_com_langevin(self): N_half = int(N / 2) self.system.part.clear() self.system.time_step = 0.02 - self.system.periodicity = [0, 0, 0] + self.system.periodicity = [False, False, False] m1 = 1.0 m2 = 10.0 @@ -100,7 +100,7 @@ def test_dist_langevin(self): N_half = int(N / 2) self.system.part.clear() self.system.time_step = 0.02 - self.system.periodicity = [1, 1, 1] + self.system.periodicity = [True, True, True] m1 = 1.0 m2 = 10.0 diff --git a/testsuite/python/thermostats_common.py b/testsuite/python/thermostats_common.py index 74430e60f0f..212b6948804 100644 --- a/testsuite/python/thermostats_common.py +++ b/testsuite/python/thermostats_common.py @@ -142,7 +142,7 @@ def check_per_particle(self, N, kT, gamma_local, steps, v_minmax, n_bins, system = self.system partcls = system.part.add(pos=np.random.random((N, 3))) if espressomd.has_features("ROTATION"): - partcls.rotation = [1, 1, 1] + partcls.rotation = [True, True, True] if espressomd.has_features("PARTICLE_ANISOTROPY"): gamma_local = 3 * [gamma_local] diff --git a/testsuite/python/thole.py b/testsuite/python/thole.py index eb2179d8da1..de20b768bc5 100644 --- a/testsuite/python/thole.py +++ b/testsuite/python/thole.py @@ -47,13 +47,9 @@ def setUp(self): self.system.time_step = 0.01 self.system.cell_system.skin = 0.4 self.p0 = self.system.part.add( - pos=[ - 0, 0, 0], type=0, fix=[ - 1, 1, 1], q=self.q1) + pos=[0, 0, 0], type=0, fix=3 * [True], q=self.q1) self.p1 = self.system.part.add( - pos=[ - 2, 0, 0], type=0, fix=[ - 1, 1, 1], q=self.q2) + pos=[2, 0, 0], type=0, fix=3 * [True], q=self.q2) p3m = espressomd.electrostatics.P3M( prefactor=COULOMB_PREFACTOR, accuracy=1e-6, mesh=3 * [52], cao=4) diff --git a/testsuite/python/virtual_sites_relative.py b/testsuite/python/virtual_sites_relative.py index 106bc630437..747349398c0 100644 --- a/testsuite/python/virtual_sites_relative.py +++ b/testsuite/python/virtual_sites_relative.py @@ -100,9 +100,9 @@ def test_vs_quat(self): self.system.virtual_sites = espressomd.virtual_sites.VirtualSitesRelative( have_quaternion=False) self.assertFalse(self.system.virtual_sites.have_quaternion) - p1 = self.system.part.add(pos=[1, 1, 1], rotation=[1, 1, 1], + p1 = self.system.part.add(pos=[1, 1, 1], rotation=3 * [True], omega_lab=[1, 1, 1]) - p2 = self.system.part.add(pos=[1, 1, 1], rotation=[1, 1, 1]) + p2 = self.system.part.add(pos=[1, 1, 1], rotation=3 * [True]) p2.vs_auto_relate_to(p1) np.testing.assert_array_equal(np.copy(p2.quat), [1, 0, 0, 0]) self.system.integrator.run(1) @@ -142,9 +142,9 @@ def test_vs_exceptions(self): 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) + p1 = system.part.add(pos=[0.0, 0.0, 0.0], rotation=3 * [True], id=1) + p2 = system.part.add(pos=[1.0, 1.0, 1.0], rotation=3 * [True], id=2) + p3 = system.part.add(pos=[1.0, 1.0, 1.0], rotation=3 * [True], 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') @@ -171,13 +171,13 @@ def test_pos_vel_forces(self): self.assertEqual(system.min_global_cut, 0.23) # Place central particle + 3 vs - p1 = system.part.add(rotation=(1, 1, 1), pos=(0.5, 0.5, 0.5), id=1, + p1 = system.part.add(rotation=3 * [True], pos=(0.5, 0.5, 0.5), id=1, quat=(1, 0, 0, 0), omega_lab=(1, 2, 3)) pos2 = (0.5, 0.4, 0.5) pos3 = (0.3, 0.5, 0.4) pos4 = (0.5, 0.5, 0.5) for pos in (pos2, pos3, pos4): - p = system.part.add(rotation=(1, 1, 1), pos=pos) + p = system.part.add(rotation=3 * [True], pos=pos) p.vs_auto_relate_to(p1) # Was the particle made virtual self.assertTrue(p.virtual) From 4a5c3a7f7415abca33e76aebfabc3bae338c59e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 14 Jul 2022 17:11:49 +0200 Subject: [PATCH 02/85] python: Improve type safety, use keyword arguments --- samples/grand_canonical.py | 2 +- samples/lj_liquid_structurefactor.py | 4 ++-- samples/reaction_methods.py | 2 +- samples/widom_insertion.py | 2 +- testsuite/python/analyze_chains.py | 13 +++++-------- testsuite/python/analyze_distance.py | 4 ++-- testsuite/python/cell_system.py | 6 ++++-- testsuite/python/coulomb_interface.py | 4 ++-- .../python/interactions_bonded_interface.py | 8 +++++++- testsuite/python/lb_stats.py | 3 ++- testsuite/python/linear_momentum_lb.py | 4 ++-- testsuite/python/p3m_tuning_exceptions.py | 4 ++-- testsuite/python/reaction_methods_interface.py | 8 ++++++-- testsuite/python/thole.py | 17 +++-------------- 14 files changed, 40 insertions(+), 41 deletions(-) diff --git a/samples/grand_canonical.py b/samples/grand_canonical.py index 9d28c6acd7d..150050e849f 100644 --- a/samples/grand_canonical.py +++ b/samples/grand_canonical.py @@ -95,7 +95,7 @@ reactant_types=[], reactant_coefficients=[], product_types=[1, 2], product_coefficients=[1, 1], default_charges={1: -1, 2: +1}) print(RE.get_status()) -system.setup_type_map([0, 1, 2]) +system.setup_type_map(type_list=[0, 1, 2]) # Set the hidden particle type to the lowest possible number to speed # up the simulation diff --git a/samples/lj_liquid_structurefactor.py b/samples/lj_liquid_structurefactor.py index 4d67990339e..3791883259b 100644 --- a/samples/lj_liquid_structurefactor.py +++ b/samples/lj_liquid_structurefactor.py @@ -78,7 +78,7 @@ structurefactor_type_list = [0, 1] structurefactor_order = 20 structurefactor_bins = len(system.analysis.structure_factor( - [0], structurefactor_order)[0]) + sf_types=[0], sf_order=structurefactor_order)[0]) structurefactor_k = np.zeros(structurefactor_bins) structurefactor_Sk = np.zeros(structurefactor_bins) @@ -153,7 +153,7 @@ system.integrator.run(int_steps) structurefactor_k, structurefactor_Sk = system.analysis.structure_factor( - structurefactor_type_list, structurefactor_order) + sf_types=structurefactor_type_list, sf_order=structurefactor_order) energies = system.analysis.energy() print(energies['total']) diff --git a/samples/reaction_methods.py b/samples/reaction_methods.py index a53e1c445c3..220e2675b00 100644 --- a/samples/reaction_methods.py +++ b/samples/reaction_methods.py @@ -98,7 +98,7 @@ assert RE is not None, "Please choose a reaction ensemble from the command line" print(RE.get_status()) -system.setup_type_map(list(types.values())) +system.setup_type_map(type_list=list(types.values())) # Set the hidden particle type to the lowest possible number to speed diff --git a/samples/widom_insertion.py b/samples/widom_insertion.py index 29159d1c0e2..d0ff627b348 100644 --- a/samples/widom_insertion.py +++ b/samples/widom_insertion.py @@ -111,7 +111,7 @@ reactant_coefficients=[], product_types=[1, 2], product_coefficients=[1, 1], default_charges={1: -1, 2: +1}) print(widom.get_status()) -system.setup_type_map([0, 1, 2]) +system.setup_type_map(type_list=[0, 1, 2]) # Set the hidden particle type to the lowest possible number to speed diff --git a/testsuite/python/analyze_chains.py b/testsuite/python/analyze_chains.py index 5ee2db748a7..073b1015c37 100644 --- a/testsuite/python/analyze_chains.py +++ b/testsuite/python/analyze_chains.py @@ -78,8 +78,9 @@ def calc_rg(self): tail_ids = head_ids + self.num_mono rg2 = [] for p in range(self.num_poly): - rg2.append( - np.var(self.system.part.by_ids(range(head_ids[p], tail_ids[p])).pos, axis=0)) + ids = range(head_ids[p], tail_ids[p]) + r = np.copy(self.system.part.by_ids(ids).pos) + rg2.append(np.var(r, axis=0)) rg2 = np.array(rg2) rg2 = np.sum(rg2, axis=1) return np.mean(np.sqrt(rg2)), np.std( @@ -92,12 +93,8 @@ def calc_rh(self): tail_ids = head_ids + self.num_mono - 1 rh = [] for p in range(self.num_poly): - r = np.array( - self.system.part.by_ids( - range( - head_ids[p], - tail_ids[p] + - 1)).pos) + ids = range(head_ids[p], tail_ids[p] + 1) + r = np.copy(self.system.part.by_ids(ids).pos) # this generates indices for all i Date: Thu, 14 Jul 2022 18:14:07 +0200 Subject: [PATCH 03/85] script_interface: Cleanup --- src/script_interface/CMakeLists.txt | 10 +++---- src/script_interface/Variant.hpp | 2 +- src/script_interface/get_value.hpp | 2 +- src/script_interface/h5md/h5md.cpp | 6 ++-- src/script_interface/h5md/h5md.hpp | 4 +-- src/script_interface/initialize.cpp | 30 +++++++++---------- .../reaction_methods/ReactionAlgorithm.hpp | 2 +- 7 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/script_interface/CMakeLists.txt b/src/script_interface/CMakeLists.txt index a7995717085..5b06f982758 100644 --- a/src/script_interface/CMakeLists.txt +++ b/src/script_interface/CMakeLists.txt @@ -26,22 +26,22 @@ add_library(Espresso::script_interface ALIAS Espresso_script_interface) add_subdirectory(accumulators) add_subdirectory(bond_breakage) add_subdirectory(cell_system) +add_subdirectory(cluster_analysis) add_subdirectory(collision_detection) add_subdirectory(constraints) -add_subdirectory(cluster_analysis) add_subdirectory(electrostatics) +add_subdirectory(h5md) add_subdirectory(interactions) add_subdirectory(lbboundaries) add_subdirectory(lees_edwards) add_subdirectory(magnetostatics) -add_subdirectory(virtual_sites) +add_subdirectory(mpiio) add_subdirectory(observables) add_subdirectory(pair_criteria) -add_subdirectory(mpiio) +add_subdirectory(reaction_methods) add_subdirectory(scafacos) add_subdirectory(shapes) -add_subdirectory(h5md) -add_subdirectory(reaction_methods) +add_subdirectory(virtual_sites) install(TARGETS Espresso_script_interface LIBRARY DESTINATION ${PYTHON_INSTDIR}/espressomd) diff --git a/src/script_interface/Variant.hpp b/src/script_interface/Variant.hpp index 5afff0e7d4f..b3ffa371e9a 100644 --- a/src/script_interface/Variant.hpp +++ b/src/script_interface/Variant.hpp @@ -104,6 +104,6 @@ template bool is_type(Variant const &v) { } inline bool is_none(Variant const &v) { return is_type(v); } -} /* namespace ScriptInterface */ +} // namespace ScriptInterface #endif diff --git a/src/script_interface/get_value.hpp b/src/script_interface/get_value.hpp index e26f99da33f..2d18f9af01c 100644 --- a/src/script_interface/get_value.hpp +++ b/src/script_interface/get_value.hpp @@ -408,6 +408,6 @@ template void set_from_args(T &dst, VariantMap const &vals, const char *name) { dst = get_value(vals, name); } -} /* namespace ScriptInterface */ +} // namespace ScriptInterface #endif diff --git a/src/script_interface/h5md/h5md.cpp b/src/script_interface/h5md/h5md.cpp index 2c01743e330..ca8399c8492 100644 --- a/src/script_interface/h5md/h5md.cpp +++ b/src/script_interface/h5md/h5md.cpp @@ -18,8 +18,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef ESPRESSO_SCRIPTINTERFACE_H5MD_CPP -#define ESPRESSO_SCRIPTINTERFACE_H5MD_CPP #include "config.hpp" @@ -36,6 +34,7 @@ namespace ScriptInterface { namespace Writer { + Variant H5md::do_call_method(const std::string &name, const VariantMap ¶meters) { if (name == "write") @@ -52,8 +51,7 @@ Variant H5md::do_call_method(const std::string &name, return {}; } -} /* namespace Writer */ +} // namespace Writer } // namespace ScriptInterface #endif // H5MD -#endif diff --git a/src/script_interface/h5md/h5md.hpp b/src/script_interface/h5md/h5md.hpp index 21e16bba28b..7609d6b9361 100644 --- a/src/script_interface/h5md/h5md.hpp +++ b/src/script_interface/h5md/h5md.hpp @@ -19,8 +19,8 @@ * along with this program. If not, see . */ -#ifndef ESPRESSO_SCRIPTINTERFACE_H5MD_HPP -#define ESPRESSO_SCRIPTINTERFACE_H5MD_HPP +#ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_H5MD_H5MD_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_H5MD_H5MD_HPP #include "config.hpp" diff --git a/src/script_interface/initialize.cpp b/src/script_interface/initialize.cpp index dc7352b67b1..5da10ccf079 100644 --- a/src/script_interface/initialize.cpp +++ b/src/script_interface/initialize.cpp @@ -21,55 +21,53 @@ #include "config.hpp" -#include "cluster_analysis/initialize.hpp" -#include "constraints/initialize.hpp" -#include "pair_criteria/initialize.hpp" -#include "shapes/initialize.hpp" -#ifdef H5MD -#include "h5md/initialize.hpp" -#endif #include "ComFixed.hpp" #include "CylindricalTransformationParameters.hpp" #include "accumulators/initialize.hpp" #include "bond_breakage/initialize.hpp" #include "cell_system/initialize.hpp" +#include "cluster_analysis/initialize.hpp" #include "collision_detection/initialize.hpp" +#include "constraints/initialize.hpp" #include "electrostatics/initialize.hpp" +#include "h5md/initialize.hpp" #include "interactions/initialize.hpp" #include "lbboundaries/initialize.hpp" #include "lees_edwards/initialize.hpp" #include "magnetostatics/initialize.hpp" #include "mpiio/initialize.hpp" #include "observables/initialize.hpp" +#include "pair_criteria/initialize.hpp" #include "reaction_methods/initialize.hpp" +#include "shapes/initialize.hpp" #include "virtual_sites/initialize.hpp" namespace ScriptInterface { void initialize(Utils::Factory *f) { - Shapes::initialize(f); - Constraints::initialize(f); -#ifdef H5MD - Writer::initialize(f); -#endif Accumulators::initialize(f); BondBreakage::initialize(f); CellSystem::initialize(f); - Observables::initialize(f); ClusterAnalysis::initialize(f); + CollisionDetection::initialize(f); + Constraints::initialize(f); Coulomb::initialize(f); Dipoles::initialize(f); Interactions::initialize(f); LBBoundaries::initialize(f); LeesEdwards::initialize(f); + MPIIO::initialize(f); + Observables::initialize(f); PairCriteria::initialize(f); + Shapes::initialize(f); VirtualSites::initialize(f); - MPIIO::initialize(f); - CollisionDetection::initialize(f); ReactionMethods::initialize(f); +#ifdef H5MD + Writer::initialize(f); +#endif f->register_new("ComFixed"); f->register_new( "CylindricalTransformationParameters"); } -} /* namespace ScriptInterface */ +} // namespace ScriptInterface diff --git a/src/script_interface/reaction_methods/ReactionAlgorithm.hpp b/src/script_interface/reaction_methods/ReactionAlgorithm.hpp index 96fdfa89eb6..9f6b9939437 100644 --- a/src/script_interface/reaction_methods/ReactionAlgorithm.hpp +++ b/src/script_interface/reaction_methods/ReactionAlgorithm.hpp @@ -160,7 +160,7 @@ class ReactionAlgorithm : public AutoParameters { throw std::runtime_error(("unknown method '" + name + "()'").c_str()); } return {}; - }; + } private: void delete_reaction(int reaction_id) { From 42ad39ad65ee5041f0a473fa6e9f99408f3c38c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 15 Jul 2022 21:26:50 +0200 Subject: [PATCH 04/85] script_interface: Modernize interface code Return std::vector objects as numpy arrays and Utils::Vector objects as array_locked. Add support for Utils::Vector3i and Utils::Vector3b. Improve get_value() exception mechanism to print the types contained in a container, e.g. `std::unordered_map` is printed as `std::unordered_map` to help understand why a type conversion failed. --- src/python/espressomd/script_interface.pxd | 1 - src/python/espressomd/script_interface.pyx | 48 +++++++++++-- src/python/espressomd/utils.pxd | 8 ++- src/python/espressomd/utils.pyx | 35 --------- src/script_interface/Variant.hpp | 26 ++++++- .../cell_system/CellSystem.hpp | 15 ++-- src/script_interface/get_value.hpp | 71 +++++++++++++------ src/script_interface/packed_variant.hpp | 5 +- src/script_interface/tests/get_value_test.cpp | 9 +-- testsuite/python/cell_system.py | 2 +- 10 files changed, 132 insertions(+), 88 deletions(-) diff --git a/src/python/espressomd/script_interface.pxd b/src/python/espressomd/script_interface.pxd index bd2b0c2860b..112a05de941 100644 --- a/src/python/espressomd/script_interface.pxd +++ b/src/python/espressomd/script_interface.pxd @@ -18,7 +18,6 @@ from libcpp.unordered_map cimport unordered_map -from libcpp.vector cimport vector from libcpp.string cimport string from libcpp.memory cimport shared_ptr from libcpp cimport bool diff --git a/src/python/espressomd/script_interface.pyx b/src/python/espressomd/script_interface.pyx index ad591e8a445..67a9151e658 100644 --- a/src/python/espressomd/script_interface.pyx +++ b/src/python/espressomd/script_interface.pyx @@ -16,11 +16,13 @@ # along with this program. If not, see . import numpy as np from . import utils -from .utils cimport Vector2d, Vector3d, Vector4d, make_array_locked +from .utils cimport Vector3b, Vector3i, Vector2d, Vector3d, Vector4d cimport cpython.object from libcpp.memory cimport shared_ptr, make_shared +from libcpp.vector cimport vector from libcpp.utility cimport pair +from libcpp.unordered_map cimport unordered_map cdef shared_ptr[ContextManager] _om @@ -191,7 +193,9 @@ cdef class PScriptInterface: cdef Variant python_object_to_variant(value) except *: """Convert Python objects to C++ Variant objects.""" - cdef vector[Variant] vec + cdef vector[Variant] vec_variant + cdef vector[int] vec_int + cdef vector[double] vec_double cdef unordered_map[int, Variant] map_int2var cdef unordered_map[string, Variant] map_str2var cdef PObjectRef oref @@ -225,10 +229,30 @@ cdef Variant python_object_to_variant(value) except *: elif type(value) in (str, np.str_): return make_variant[string](utils.to_char_pointer(str(value))) elif hasattr(value, '__iter__'): + if len(value) == 0: + return make_variant[vector[Variant]](vec_variant) + if isinstance(value, np.ndarray) and value.ndim == 1: + if np.issubdtype(value.dtype, np.floating): + for e in value: + vec_double.push_back(e) + return make_variant[vector[double]](vec_double) + elif np.issubdtype(value.dtype, np.signedinteger): + for e in value: + vec_int.push_back(e) + return make_variant[vector[int]](vec_int) + if all(map(lambda x: isinstance(x, (float, np.floating)), value)): + for e in value: + vec_double.push_back(e) + return make_variant[vector[double]](vec_double) + if all(map(lambda x: isinstance(x, (int, np.integer)) + and not isinstance(x, type(True)), value)): + for e in value: + vec_int.push_back(e) + return make_variant[vector[int]](vec_int) for e in value: - vec.push_back(python_object_to_variant(e)) - return make_variant[vector[Variant]](vec) - elif type(value) == type(True): + vec_variant.push_back(python_object_to_variant(e)) + return make_variant[vector[Variant]](vec_variant) + elif isinstance(value, (type(True), np.bool_)): return make_variant[bool](value) elif np.issubdtype(np.dtype(type(value)), np.signedinteger): return make_variant[int](value) @@ -247,7 +271,10 @@ cdef variant_to_python_object(const Variant & value) except +: cdef pair[int, Variant] pair_int2var cdef pair[string, Variant] pair_str2var cdef shared_ptr[ObjectHandle] ptr + cdef Vector3b vec3b + cdef Vector3i vec3i cdef Vector2d vec2d + cdef Vector3d vec3d cdef Vector4d vec4d if is_none(value): return None @@ -262,12 +289,19 @@ cdef variant_to_python_object(const Variant & value) except +: if is_type[vector[int]](value): return get_value[vector[int]](value) if is_type[vector[double]](value): - return get_value[vector[double]](value) + return np.array(get_value[vector[double]](value)) + if is_type[Vector3b](value): + vec3b = get_value[Vector3b](value) + return utils.array_locked([vec3b[0], vec3b[1], vec3b[2]]) + if is_type[Vector3i](value): + vec3i = get_value[Vector3i](value) + return utils.array_locked([vec3i[0], vec3i[1], vec3i[2]]) if is_type[Vector4d](value): vec4d = get_value[Vector4d](value) return utils.array_locked([vec4d[0], vec4d[1], vec4d[2], vec4d[3]]) if is_type[Vector3d](value): - return make_array_locked(get_value[Vector3d](value)) + vec3d = get_value[Vector3d](value) + return utils.array_locked([vec3d[0], vec3d[1], vec3d[2]]) if is_type[Vector2d](value): vec2d = get_value[Vector2d](value) return utils.array_locked([vec2d[0], vec2d[1]]) diff --git a/src/python/espressomd/utils.pxd b/src/python/espressomd/utils.pxd index 746ff4f5609..0f68c8ecde3 100644 --- a/src/python/espressomd/utils.pxd +++ b/src/python/espressomd/utils.pxd @@ -21,6 +21,7 @@ cimport numpy as np from libcpp.string cimport string # import std::string as string from libcpp.vector cimport vector # import std::vector as vector +from libcpp cimport bool as cbool cdef extern from "utils/Span.hpp" namespace "Utils": cppclass Span[T]: @@ -41,7 +42,6 @@ cdef np.ndarray create_nparray_from_double_array(double * x, int n) cdef np.ndarray create_nparray_from_double_span(Span[double] x) cpdef check_array_type_or_throw_except(x, n, t, msg) cpdef check_type_or_throw_except(x, n, t, msg) -cdef check_range_or_except(D, x, v_min, incl_min, v_max, incl_max) cdef extern from "error_handling/RuntimeError.hpp" namespace "ErrorHandling::RuntimeError": cdef cppclass ErrorLevel: @@ -66,10 +66,15 @@ cdef extern from "utils/Vector.hpp" namespace "Utils": cppclass Vector2d: double & operator[](int i) double * data() + cppclass Vector4d: double & operator[](int i) double * data() + cppclass Vector3b: + cbool & operator[](int i) + cbool * data() + cppclass Vector3i: int & operator[](int i) int * data() @@ -104,7 +109,6 @@ cdef extern from "utils/quaternion.hpp" namespace "Utils": T & operator[](int i) cdef make_array_locked(Vector3d) -cdef make_array_locked_vector(vector[Vector3d] v) cdef Vector3d make_Vector3d(a) cdef Vector3i make_Vector3i(a) diff --git a/src/python/espressomd/utils.pyx b/src/python/espressomd/utils.pyx index 5c10cf40d4d..779f9d46ae2 100644 --- a/src/python/espressomd/utils.pyx +++ b/src/python/espressomd/utils.pyx @@ -18,7 +18,6 @@ # cimport numpy as np import numpy as np -from libcpp.vector cimport vector cdef _check_type_or_throw_except_assertion(x, t): @@ -95,32 +94,6 @@ cdef np.ndarray create_nparray_from_double_span(Span[double] x): """ return create_nparray_from_double_array(x.data(), x.size()) -cdef check_range_or_except(D, name, v_min, incl_min, v_max, incl_max): - """ - Checks that x is in range [v_min,v_max] (include boundaries via - incl_min/incl_max = true) or throws a ValueError. v_min/v_max = 'inf' to - disable limit. - - """ - x = D[name] - - # Array/list/tuple - if hasattr(x, "__len__"): - if (v_min != "inf" and ((incl_min and not all(v >= v_min for v in x)) - or (not incl_min and not all(v > v_min for v in x)))) or \ - (v_max != "inf" and ((incl_max and not all(v <= v_max for v in x)) - or (not incl_max and not all(v < v_max for v in x)))): - raise ValueError(f"In {name}: Some values in {x} are out of" - f" range {'[' if incl_min else ']'}{v_min}," - f"{v_max}{']' if incl_max else '['}") - # Single Value - else: - if (v_min != "inf" and ((incl_min and x < v_min) or (not incl_min and x <= v_min)) or - v_max != "inf" and ((incl_max and x > v_max) or (not incl_max and x >= v_max))): - raise ValueError(f"In {name}: Value {x} is out of" - f" range {'[' if incl_min else ']'}{v_min}," - f"{v_max}{']' if incl_max else '['}") - def to_char_pointer(s): """ @@ -233,14 +206,6 @@ cdef make_array_locked(Vector3d v): return array_locked([v[0], v[1], v[2]]) -cdef make_array_locked_vector(vector[Vector3d] v): - ret = np.empty((v.size(), 3)) - for i in range(v.size()): - for j in range(3): - ret[i][j] = v[i][j] - return array_locked(ret) - - cdef Vector3d make_Vector3d(a): cdef Vector3d v for i, ai in enumerate(a): diff --git a/src/script_interface/Variant.hpp b/src/script_interface/Variant.hpp index b3ffa371e9a..ffd723ce61d 100644 --- a/src/script_interface/Variant.hpp +++ b/src/script_interface/Variant.hpp @@ -50,6 +50,10 @@ #include #include +namespace Utils { +using Vector3b = Utils::Vector; +} + namespace ScriptInterface { class ObjectHandle; using ObjectRef = std::shared_ptr; @@ -69,8 +73,9 @@ constexpr const None none{}; */ using Variant = boost::make_recursive_variant< None, bool, int, std::size_t, double, std::string, ObjectRef, - Utils::Vector2d, Utils::Vector3d, Utils::Vector4d, std::vector, - std::vector, std::vector, + Utils::Vector3b, Utils::Vector3i, Utils::Vector2d, Utils::Vector3d, + Utils::Vector4d, std::vector, std::vector, + std::vector, std::unordered_map, std::unordered_map>::type; @@ -84,6 +89,23 @@ using VariantMap = std::unordered_map; */ template Variant make_variant(const T &x) { return Variant(x); } +template +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)}); + } + return ret; +} + +template auto make_vector_of_variants(std::vector const &v) { + std::vector ret; + for (auto const &item : v) { + ret.emplace_back(item); + } + return ret; +} + namespace detail { template struct is_type_visitor : boost::static_visitor { template constexpr bool operator()(const U &) const { diff --git a/src/script_interface/cell_system/CellSystem.hpp b/src/script_interface/cell_system/CellSystem.hpp index 152fec48ab4..56757ed5390 100644 --- a/src/script_interface/cell_system/CellSystem.hpp +++ b/src/script_interface/cell_system/CellSystem.hpp @@ -54,9 +54,6 @@ namespace ScriptInterface { namespace CellSystem { namespace { -Variant pack_vector(Utils::Vector3i const &vec) { - return std::vector(vec.begin(), vec.end()); -} auto const &get_regular_decomposition() { return dynamic_cast( Utils::as_const(cell_structure).decomposition()); @@ -89,12 +86,8 @@ class CellSystem : public AutoParameters { [this](Variant const &v) { context()->parallel_try_catch([&v]() { auto const error_msg = std::string("Parameter 'node_grid'"); - auto const vec = get_value>(v); - if (vec.size() != 3ul) { - throw std::invalid_argument(error_msg + " must be 3 ints"); - } auto const old_node_grid = ::node_grid; - auto const new_node_grid = Utils::Vector3i{vec.begin(), vec.end()}; + auto const new_node_grid = get_value(v); auto const n_nodes_old = Utils::product(old_node_grid); auto const n_nodes_new = Utils::product(new_node_grid); if (n_nodes_new != n_nodes_old) { @@ -113,7 +106,7 @@ class CellSystem : public AutoParameters { } }); }, - []() { return pack_vector(::node_grid); }}, + []() { return ::node_grid; }}, {"skin", [this](Variant const &v) { auto const new_skin = get_value(v); @@ -186,11 +179,11 @@ class CellSystem : public AutoParameters { auto const cs_type = cell_structure.decomposition_type(); if (cs_type == CellStructureType::CELL_STRUCTURE_REGULAR) { auto const rd = get_regular_decomposition(); - state["cell_grid"] = pack_vector(rd.cell_grid); + state["cell_grid"] = Variant{rd.cell_grid}; state["cell_size"] = Variant{rd.cell_size}; } else if (cs_type == CellStructureType::CELL_STRUCTURE_HYBRID) { auto const hd = get_hybrid_decomposition(); - state["cell_grid"] = pack_vector(hd.get_cell_grid()); + state["cell_grid"] = Variant{hd.get_cell_grid()}; state["cell_size"] = Variant{hd.get_cell_size()}; mpi_resort_particles(true); // needed to get correct particle counts state["parts_per_decomposition"] = diff --git a/src/script_interface/get_value.hpp b/src/script_interface/get_value.hpp index 2d18f9af01c..9142abc89dd 100644 --- a/src/script_interface/get_value.hpp +++ b/src/script_interface/get_value.hpp @@ -26,10 +26,12 @@ #include +#include #include #include #include +#include #include #include #include @@ -43,10 +45,13 @@ namespace detail { * @brief Convert a demangled symbol into a human-readable name that omits * container allocators, key hashes and implementation-specific namespaces. * When the data type involves the @ref Variant type, it is recursively - * replaced by the string "ScriptInterface::Variant". + * replaced by the string "Variant{type1,type2,...}" based on the actual + * contents of the variant. */ namespace demangle { +inline std::string simplify_symbol_variant(Variant const &v); + /** @brief Simplify the demangled symbol of an object. */ template auto simplify_symbol(T const *) { auto constexpr is_string = std::is_same::value; @@ -61,12 +66,33 @@ template auto simplify_symbol(T const *) { return name; } +/** @overload */ +template +auto simplify_symbol(Utils::Vector const *) { + auto const name_val = simplify_symbol(static_cast(nullptr)); + return "Utils::Vector<" + Utils::demangle() + ", " + std::to_string(N) + + ">"; +} + /** @overload */ template auto simplify_symbol(std::vector const *) { auto const name_val = simplify_symbol(static_cast(nullptr)); return "std::vector<" + name_val + ">"; } +/** @overload */ +inline auto simplify_symbol(std::vector const *vec) { + auto value_type_name = std::string("ScriptInterface::Variant"); + if (vec) { + std::set types = {}; + for (auto const &v : *vec) { + types.insert(simplify_symbol_variant(v)); + } + value_type_name += "{" + boost::algorithm::join(types, ", ") + "}"; + } + return "std::vector<" + value_type_name + ">"; +} + /** @overload */ template auto simplify_symbol(std::unordered_map const *) { @@ -75,14 +101,29 @@ auto simplify_symbol(std::unordered_map const *) { return "std::unordered_map<" + name_key + ", " + name_val + ">"; } +/** @overload */ +template +auto simplify_symbol(std::unordered_map const *map) { + auto const name_key = simplify_symbol(static_cast(nullptr)); + auto value_type_name = std::string("ScriptInterface::Variant"); + if (map) { + std::set types = {}; + for (auto const &kv : *map) { + types.insert(simplify_symbol_variant(kv.second)); + } + value_type_name += "{" + boost::algorithm::join(types, ", ") + "}"; + } + return "std::unordered_map<" + name_key + ", " + value_type_name + ">"; +} + struct simplify_symbol_visitor : boost::static_visitor { - template std::string operator()(const T &) const { - return simplify_symbol(static_cast(nullptr)); + template std::string operator()(T const &t) const { + return simplify_symbol(&t); } }; /** @brief Simplify the demangled symbol of an object wrapped in a variant. */ -inline auto simplify_symbol_variant(Variant const &v) { +inline std::string simplify_symbol_variant(Variant const &v) { return boost::apply_visitor(simplify_symbol_visitor{}, v); } @@ -180,8 +221,9 @@ struct vector_conversion_visitor : boost::static_visitor> { return ret; } - Utils::Vector - operator()(std::vector> const &v) const { + template + std::enable_if_t::value, Utils::Vector> + operator()(std::vector> const &v) const { if (N != v.size()) { throw boost::bad_get{}; } @@ -348,23 +390,6 @@ template T get_value(Variant const &v) { } } -template -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)}); - } - return ret; -} - -template auto make_vector_of_variants(std::vector const &v) { - std::vector ret; - for (auto const &item : v) { - ret.emplace_back(item); - } - return ret; -} - /** * @brief Get a value from a VariantMap by name, or throw * if it does not exist or is not convertible to diff --git a/src/script_interface/packed_variant.hpp b/src/script_interface/packed_variant.hpp index 041cbe72c82..2a8ba229a10 100644 --- a/src/script_interface/packed_variant.hpp +++ b/src/script_interface/packed_variant.hpp @@ -53,8 +53,9 @@ inline ObjectId object_id(const ObjectHandle *p) { */ using PackedVariant = boost::make_recursive_variant< None, bool, int, std::size_t, double, std::string, ObjectId, - Utils::Vector2d, Utils::Vector3d, Utils::Vector4d, std::vector, - std::vector, std::vector, + Utils::Vector3b, Utils::Vector3i, Utils::Vector2d, Utils::Vector3d, + Utils::Vector4d, std::vector, std::vector, + std::vector, std::unordered_map, std::unordered_map>::type; diff --git a/src/script_interface/tests/get_value_test.cpp b/src/script_interface/tests/get_value_test.cpp index f16245d58db..3286dc97f66 100644 --- a/src/script_interface/tests/get_value_test.cpp +++ b/src/script_interface/tests/get_value_test.cpp @@ -186,7 +186,8 @@ BOOST_AUTO_TEST_CASE(check_exceptions) { auto const so_obj = so_ptr_t(); auto const msg_prefix = std::string("Provided argument of type "); - auto const variant_name = std::string("ScriptInterface::Variant"); + auto const variant_sip_name = + "ScriptInterface::Variant\\{" + Utils::demangle() + "\\}"; { // basic types @@ -206,7 +207,7 @@ BOOST_AUTO_TEST_CASE(check_exceptions) { // vectors auto const int_variant = Variant{1.5}; auto const vec_variant = Variant{std::vector{{so_obj}}}; - auto const vec_variant_pattern = "std::vector<" + variant_name + ">"; + auto const vec_variant_pattern = "std::vector<" + variant_sip_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"); @@ -227,7 +228,7 @@ BOOST_AUTO_TEST_CASE(check_exceptions) { auto const map_variant = Variant{std::unordered_map{{1, so_obj}}}; auto const map_variant_pattern = - "std::unordered_map"; + "std::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"); @@ -247,7 +248,7 @@ BOOST_AUTO_TEST_CASE(check_exceptions) { auto const map_variant = Variant{std::unordered_map{{"key", so_obj}}}; auto const map_variant_pattern = - "std::unordered_map"; + "std::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"); diff --git a/testsuite/python/cell_system.py b/testsuite/python/cell_system.py index 07c87230c29..afe950b8cce 100644 --- a/testsuite/python/cell_system.py +++ b/testsuite/python/cell_system.py @@ -62,7 +62,7 @@ def test_exceptions(self): self.assertAlmostEqual(system.cell_system.skin, 0.1, delta=1e-12) node_grid = system.cell_system.node_grid - with self.assertRaisesRegex(ValueError, "Parameter 'node_grid' must be 3 ints"): + with self.assertRaisesRegex(RuntimeError, "Provided argument of type .+ is not convertible to 'Utils::Vector'"): system.cell_system.node_grid = [1, 2, 3, 4] np.testing.assert_array_equal(np.copy(system.cell_system.node_grid), np.copy(node_grid)) From bb36893a0f5d4a0e2676fc1438363d3595a73c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 15 Jul 2022 22:01:47 +0200 Subject: [PATCH 05/85] python: Break circular dependency Move the features checking logic to a dedicated file. --- src/python/espressomd/__init__.py | 54 +------------- src/python/espressomd/code_features.py | 72 +++++++++++++++++++ src/python/espressomd/collision_detection.py | 5 +- src/python/espressomd/drude_helpers.py | 2 +- src/python/espressomd/ekboundaries.py | 2 +- .../espressomd/electrostatic_extensions.py | 2 +- src/python/espressomd/electrostatics.py | 2 +- src/python/espressomd/io/writer/h5md.py | 3 +- src/python/espressomd/lbboundaries.py | 2 +- src/python/espressomd/magnetostatics.py | 2 +- src/python/espressomd/virtual_sites.py | 5 +- testsuite/python/unittest_decorators.py | 4 +- testsuite/scripts/importlib_wrapper.py | 3 +- 13 files changed, 95 insertions(+), 63 deletions(-) create mode 100644 src/python/espressomd/code_features.py diff --git a/src/python/espressomd/__init__.py b/src/python/espressomd/__init__.py index 12a2ed3661e..892db16909a 100644 --- a/src/python/espressomd/__init__.py +++ b/src/python/espressomd/__init__.py @@ -1,3 +1,4 @@ +# # Copyright (C) 2016-2022 The ESPResSo project # Copyright (C) 2014 Olaf Lenz # @@ -22,56 +23,5 @@ from .system import System from .code_info import features, all_features +from .code_features import has_features, assert_features from .cuda_init import gpu_available -from . import code_info -from . import utils - - -class FeaturesError(Exception): - - def __init__(self, missing_features): - super().__init__(f"Missing features {', '.join(missing_features)}") - - -def has_features(*args): - """ - Check whether a list of features is a subset of the compiled-in features. - """ - - lvl = utils.nesting_level(args) - assert lvl in [1, 2], "has_features() takes strings as argument" - if lvl == 2: - check_set = set(args[0]) - else: - check_set = set(args) - - if not check_set <= code_info.all_features(): - unknown_features = check_set - code_info.all_features() - raise RuntimeError(f"unknown features {','.join(unknown_features)}") - - return check_set <= set(code_info.features()) - - -def missing_features(*args): - """ - Return a list of the missing features in the arguments. - """ - - lvl = utils.nesting_level(args) - assert lvl in [1, 2], "missing_features() takes strings as argument" - if lvl == 2: - features = set(args[0]) - else: - features = set(args) - - return sorted(features - set(code_info.features())) - - -def assert_features(*args): - """ - Raise an exception when a list of features is not a subset of the - compiled-in features. - """ - - if not has_features(*args): - raise FeaturesError(missing_features(*args)) diff --git a/src/python/espressomd/code_features.py b/src/python/espressomd/code_features.py new file mode 100644 index 00000000000..3a0d8a5ff99 --- /dev/null +++ b/src/python/espressomd/code_features.py @@ -0,0 +1,72 @@ +# +# Copyright (C) 2016-2022 The ESPResSo project +# Copyright (C) 2014 Olaf Lenz +# +# 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 . import code_info +from . import utils + + +class FeaturesError(Exception): + + def __init__(self, missing_features): + super().__init__(f"Missing features {', '.join(missing_features)}") + + +def has_features(*args): + """ + Check whether a list of features is a subset of the compiled-in features. + """ + + lvl = utils.nesting_level(args) + assert lvl in [1, 2], "has_features() takes strings as argument" + if lvl == 2: + check_set = set(args[0]) + else: + check_set = set(args) + + if not check_set <= code_info.all_features(): + unknown_features = check_set - code_info.all_features() + raise RuntimeError(f"unknown features {','.join(unknown_features)}") + + return check_set <= set(code_info.features()) + + +def missing_features(*args): + """ + Return a list of the missing features in the arguments. + """ + + lvl = utils.nesting_level(args) + assert lvl in [1, 2], "missing_features() takes strings as argument" + if lvl == 2: + features = set(args[0]) + else: + features = set(args) + + return sorted(features - set(code_info.features())) + + +def assert_features(*args): + """ + Raise an exception when a list of features is not a subset of the + compiled-in features. + """ + + if not has_features(*args): + raise FeaturesError(missing_features(*args)) diff --git a/src/python/espressomd/collision_detection.py b/src/python/espressomd/collision_detection.py index 426da182b7f..2e6906bca88 100644 --- a/src/python/espressomd/collision_detection.py +++ b/src/python/espressomd/collision_detection.py @@ -1,3 +1,4 @@ +# # Copyright (C) 2010-2022 The ESPResSo project # # This file is part of ESPResSo. @@ -14,9 +15,11 @@ # # 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 from .interactions import BondedInteraction, BondedInteractions -from .__init__ import assert_features +from .code_features import assert_features @script_interface_register diff --git a/src/python/espressomd/drude_helpers.py b/src/python/espressomd/drude_helpers.py index 40337476ab7..07c8c729ccf 100644 --- a/src/python/espressomd/drude_helpers.py +++ b/src/python/espressomd/drude_helpers.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . from . import interactions -from .__init__ import has_features +from .code_features import has_features class DrudeHelpers: diff --git a/src/python/espressomd/ekboundaries.py b/src/python/espressomd/ekboundaries.py index f36dea8e25d..7389239b226 100644 --- a/src/python/espressomd/ekboundaries.py +++ b/src/python/espressomd/ekboundaries.py @@ -15,7 +15,7 @@ # 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 -from .__init__ import has_features +from .code_features import has_features import espressomd.lbboundaries diff --git a/src/python/espressomd/electrostatic_extensions.py b/src/python/espressomd/electrostatic_extensions.py index e1ea4d96bbe..a1e34957b12 100644 --- a/src/python/espressomd/electrostatic_extensions.py +++ b/src/python/espressomd/electrostatic_extensions.py @@ -19,7 +19,7 @@ from . import utils from .script_interface import ScriptInterfaceHelper, script_interface_register -from .__init__ import has_features +from .code_features import has_features import numpy as np diff --git a/src/python/espressomd/electrostatics.py b/src/python/espressomd/electrostatics.py index 3b795f58ad2..6b91ec6d355 100644 --- a/src/python/espressomd/electrostatics.py +++ b/src/python/espressomd/electrostatics.py @@ -19,7 +19,7 @@ from . import utils from .script_interface import ScriptInterfaceHelper, script_interface_register -from .__init__ import has_features +from .code_features import has_features class ElectrostaticInteraction(ScriptInterfaceHelper): diff --git a/src/python/espressomd/io/writer/h5md.py b/src/python/espressomd/io/writer/h5md.py index 816a6cf231c..a8089d5c3c2 100644 --- a/src/python/espressomd/io/writer/h5md.py +++ b/src/python/espressomd/io/writer/h5md.py @@ -1,3 +1,4 @@ +# # Copyright (C) 2010-2022 The ESPResSo project # # This file is part of ESPResSo. @@ -20,7 +21,7 @@ import pathlib from ...script_interface import script_interface_register, ScriptInterfaceHelper # pylint: disable=import -from ...__init__ import assert_features +from ...code_features import assert_features from ... import utils diff --git a/src/python/espressomd/lbboundaries.py b/src/python/espressomd/lbboundaries.py index cd51f57ccfe..5ddea5dcc0a 100644 --- a/src/python/espressomd/lbboundaries.py +++ b/src/python/espressomd/lbboundaries.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . from .script_interface import ScriptObjectList, ScriptInterfaceHelper, script_interface_register -from .__init__ import has_features +from .code_features import has_features if any(has_features(i) for i in ["LB_BOUNDARIES", "LB_BOUNDARIES_GPU"]): diff --git a/src/python/espressomd/magnetostatics.py b/src/python/espressomd/magnetostatics.py index c7f2d102979..384562a8626 100644 --- a/src/python/espressomd/magnetostatics.py +++ b/src/python/espressomd/magnetostatics.py @@ -19,7 +19,7 @@ from . import utils from .script_interface import ScriptInterfaceHelper, script_interface_register -from .__init__ import has_features +from .code_features import has_features class MagnetostaticInteraction(ScriptInterfaceHelper): diff --git a/src/python/espressomd/virtual_sites.py b/src/python/espressomd/virtual_sites.py index 295bf5aaaa6..ba047c11b44 100644 --- a/src/python/espressomd/virtual_sites.py +++ b/src/python/espressomd/virtual_sites.py @@ -1,3 +1,4 @@ +# # Copyright (C) 2010-2022 The ESPResSo project # # This file is part of ESPResSo. @@ -14,7 +15,9 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from .__init__ import has_features +# + +from .code_features import has_features from .script_interface import ScriptInterfaceHelper, script_interface_register if has_features("VIRTUAL_SITES"): diff --git a/testsuite/python/unittest_decorators.py b/testsuite/python/unittest_decorators.py index ee831abe3f4..109f89cc8d7 100644 --- a/testsuite/python/unittest_decorators.py +++ b/testsuite/python/unittest_decorators.py @@ -23,6 +23,8 @@ import unittest import espressomd +import espressomd.code_info +import espressomd.code_features def no_skip(x): @@ -32,7 +34,7 @@ def no_skip(x): def skipIfMissingFeatures(*args): """Unittest skipIf decorator for missing Espresso features.""" if not espressomd.has_features(*args): - missing_features = espressomd.missing_features(*args) + missing_features = espressomd.code_features.missing_features(*args) return unittest.skip("Skipping test: missing feature{} {}".format( 's' if missing_features else '', ', '.join(missing_features))) return no_skip diff --git a/testsuite/scripts/importlib_wrapper.py b/testsuite/scripts/importlib_wrapper.py index 6010945b8aa..9921a342ae1 100644 --- a/testsuite/scripts/importlib_wrapper.py +++ b/testsuite/scripts/importlib_wrapper.py @@ -26,6 +26,7 @@ import importlib import pathlib import espressomd +import espressomd.code_features # global variable: if one import failed, all subsequent imports will be skipped, @@ -115,7 +116,7 @@ def configure_and_import(filepath, module_name = output_filepath.stem try: module = importlib.import_module(module_name) - except espressomd.FeaturesError as err: + except espressomd.code_features.FeaturesError as err: skip_future_imports_dependency(filepath) skipIfMissingFeatures = unittest.skip(f"{err}, skipping test!") module = unittest.mock.MagicMock() From 59b47c060336321bb2be7f2e631fee2a91a0d5df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 19 Jul 2022 14:04:41 +0200 Subject: [PATCH 06/85] script_interface: Rewrite Analysis module --- src/core/CMakeLists.txt | 3 +- src/core/analysis/CMakeLists.txt | 3 + src/core/{ => analysis}/statistics.cpp | 167 ++--- src/core/{ => analysis}/statistics.hpp | 48 +- src/core/{ => analysis}/statistics_chain.cpp | 3 +- src/core/{ => analysis}/statistics_chain.hpp | 9 +- src/core/dpd.hpp | 7 +- .../reaction_methods/ReactionAlgorithm.cpp | 2 +- src/python/espressomd/analyze.pxd | 56 +- src/python/espressomd/analyze.pyx | 641 ++++++------------ src/python/espressomd/polymer.pxd | 8 +- src/python/espressomd/polymer.pyx | 2 +- src/python/espressomd/system.pyx | 2 +- src/python/espressomd/utils.pxd | 2 - src/python/espressomd/utils.pyx | 27 - src/script_interface/CMakeLists.txt | 1 + src/script_interface/analysis/Analysis.cpp | 225 ++++++ src/script_interface/analysis/Analysis.hpp | 39 ++ src/script_interface/analysis/CMakeLists.txt | 3 + src/script_interface/analysis/initialize.cpp | 32 + src/script_interface/analysis/initialize.hpp | 35 + src/script_interface/initialize.cpp | 2 + testsuite/python/analyze_chains.py | 10 +- testsuite/python/analyze_distribution.py | 31 +- testsuite/python/analyze_mass_related.py | 5 +- 25 files changed, 726 insertions(+), 637 deletions(-) create mode 100644 src/core/analysis/CMakeLists.txt rename src/core/{ => analysis}/statistics.cpp (64%) rename src/core/{ => analysis}/statistics.hpp (74%) rename src/core/{ => analysis}/statistics_chain.cpp (99%) rename src/core/{ => analysis}/statistics_chain.hpp (93%) create mode 100644 src/script_interface/analysis/Analysis.cpp create mode 100644 src/script_interface/analysis/Analysis.hpp create mode 100644 src/script_interface/analysis/CMakeLists.txt create mode 100644 src/script_interface/analysis/initialize.cpp create mode 100644 src/script_interface/analysis/initialize.hpp diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a864eae8ea0..e5a79c08b49 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -50,8 +50,6 @@ set(Espresso_core_SRC rotate_system.cpp rotation.cpp Observable_stat.cpp - statistics_chain.cpp - statistics.cpp SystemInterface.cpp thermostat.cpp tuning.cpp @@ -107,6 +105,7 @@ target_include_directories( target_compile_definitions(Espresso_core PUBLIC $<$:H5XX_USE_MPI>) add_subdirectory(accumulators) +add_subdirectory(analysis) add_subdirectory(bond_breakage) add_subdirectory(bonded_interactions) add_subdirectory(cell_system) diff --git a/src/core/analysis/CMakeLists.txt b/src/core/analysis/CMakeLists.txt new file mode 100644 index 00000000000..b4519a3e97a --- /dev/null +++ b/src/core/analysis/CMakeLists.txt @@ -0,0 +1,3 @@ +target_sources( + Espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/statistics.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/statistics_chain.cpp) diff --git a/src/core/statistics.cpp b/src/core/analysis/statistics.cpp similarity index 64% rename from src/core/statistics.cpp rename to src/core/analysis/statistics.cpp index 6d1506892ac..683684eaaa0 100644 --- a/src/core/statistics.cpp +++ b/src/core/analysis/statistics.cpp @@ -24,7 +24,7 @@ * The corresponding header file is statistics.hpp. */ -#include "statistics.hpp" +#include "analysis/statistics.hpp" #include "Particle.hpp" #include "cells.hpp" @@ -45,12 +45,8 @@ #include #include -/**************************************************************************************** - * basic observables calculation - ****************************************************************************************/ - -double mindist(PartCfg &partCfg, const std::vector &set1, - const std::vector &set2) { +double mindist(PartCfg &partCfg, std::vector const &set1, + std::vector const &set2) { using Utils::contains; auto mindist_sq = std::numeric_limits::infinity(); @@ -105,63 +101,54 @@ Utils::Vector3d calc_linear_momentum(int include_particles, return linear_momentum; } -Utils::Vector3d centerofmass(PartCfg &partCfg, int type) { +Utils::Vector3d center_of_mass(PartCfg &partCfg, int p_type) { Utils::Vector3d com{}; double mass = 0.0; for (auto const &p : partCfg) { - if ((p.type() == type) || (type == -1)) - if (not p.is_virtual()) { - com += p.pos() * p.mass(); - mass += p.mass(); - } + if ((p.type() == p_type or p_type == -1) and not p.is_virtual()) { + com += p.pos() * p.mass(); + mass += p.mass(); + } } com /= mass; return com; } -Utils::Vector3d angularmomentum(PartCfg &partCfg, int type) { +Utils::Vector3d angular_momentum(PartCfg &partCfg, int p_type) { Utils::Vector3d am{}; for (auto const &p : partCfg) { - if ((p.type() == type) || (type == -1)) - if (not p.is_virtual()) { - am += p.mass() * vector_product(p.pos(), p.v()); - } + if ((p.type() == p_type or p_type == -1) and not p.is_virtual()) { + am += p.mass() * vector_product(p.pos(), p.v()); + } } return am; } -void momentofinertiamatrix(PartCfg &partCfg, int type, double *MofImatrix) { - int i, count; - double massi; - Utils::Vector3d p1{}; - count = 0; - - for (i = 0; i < 9; i++) - MofImatrix[i] = 0.; - - auto const com = centerofmass(partCfg, type); +Utils::Vector9d moment_of_inertia_matrix(PartCfg &partCfg, int p_type) { + Utils::Vector9d mat{}; + auto const com = center_of_mass(partCfg, p_type); for (auto const &p : partCfg) { - if (type == p.type() and (not p.is_virtual())) { - count++; - 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]); - MofImatrix[1] -= massi * (p1[0] * p1[1]); - MofImatrix[2] -= massi * (p1[0] * p1[2]); - MofImatrix[5] -= massi * (p1[1] * p1[2]); + if (p.type() == p_type and (not p.is_virtual())) { + auto const p1 = p.pos() - com; + auto const mass = p.mass(); + mat[0] += mass * (p1[1] * p1[1] + p1[2] * p1[2]); + mat[4] += mass * (p1[0] * p1[0] + p1[2] * p1[2]); + mat[8] += mass * (p1[0] * p1[0] + p1[1] * p1[1]); + mat[1] -= mass * (p1[0] * p1[1]); + mat[2] -= mass * (p1[0] * p1[2]); + mat[5] -= mass * (p1[1] * p1[2]); } } /* use symmetry */ - MofImatrix[3] = MofImatrix[1]; - MofImatrix[6] = MofImatrix[2]; - MofImatrix[7] = MofImatrix[5]; + mat[3] = mat[1]; + mat[6] = mat[2]; + mat[7] = mat[5]; + return mat; } -std::vector nbhood(PartCfg &partCfg, const Utils::Vector3d &pos, +std::vector nbhood(PartCfg &partCfg, Utils::Vector3d const &pos, double dist) { std::vector ids; auto const dist_sq = dist * dist; @@ -176,30 +163,25 @@ std::vector nbhood(PartCfg &partCfg, const Utils::Vector3d &pos, return ids; } -void calc_part_distribution(PartCfg &partCfg, std::vector const &p1_types, - std::vector const &p2_types, double r_min, - double r_max, int r_bins, bool log_flag, - double *low, double *dist) { - int ind, cnt = 0; - double inv_bin_width = 0.0; - double min_dist, min_dist2 = 0.0, start_dist2; +std::vector> +calc_part_distribution(PartCfg &partCfg, std::vector const &p1_types, + std::vector const &p2_types, double r_min, + double r_max, int r_bins, bool log_flag, bool int_flag) { auto const r_max2 = Utils::sqr(r_max); auto const r_min2 = Utils::sqr(r_min); - start_dist2 = Utils::sqr(r_max + 1.); - /* bin preparation */ - *low = 0.0; - for (int i = 0; i < r_bins; i++) - dist[i] = 0.0; - if (log_flag) - inv_bin_width = (double)r_bins / (log(r_max / r_min)); - else - inv_bin_width = (double)r_bins / (r_max - r_min); - - /* particle loop: p1_types */ + auto const start_dist2 = Utils::sqr(r_max + 1.); + auto const inv_bin_width = + (log_flag) ? static_cast(r_bins) / std::log(r_max / r_min) + : static_cast(r_bins) / (r_max - r_min); + + long cnt = 0; + double low = 0.0; + std::vector distribution(r_bins); + for (auto const &p1 : partCfg) { if (Utils::contains(p1_types, p1.type())) { - min_dist2 = start_dist2; + auto min_dist2 = start_dist2; /* particle loop: p2_types */ for (auto const &p2 : partCfg) { if (p1 != p2) { @@ -214,41 +196,63 @@ void calc_part_distribution(PartCfg &partCfg, std::vector const &p1_types, } if (min_dist2 <= r_max2) { if (min_dist2 >= r_min2) { - min_dist = sqrt(min_dist2); + auto const min_dist = std::sqrt(min_dist2); /* calculate bin index */ - if (log_flag) - ind = (int)((log(min_dist / r_min)) * inv_bin_width); - else - ind = (int)((min_dist - r_min) * inv_bin_width); - if (ind >= 0 && ind < r_bins) { - dist[ind] += 1.0; + auto const ind = static_cast( + ((log_flag) ? std::log(min_dist / r_min) : (min_dist - r_min)) * + inv_bin_width); + if (ind >= 0 and ind < r_bins) { + distribution[ind] += 1.0; } } else { - *low += 1.0; + low += 1.0; } } cnt++; } } - if (cnt == 0) - return; - /* normalization */ - *low /= (double)cnt; - for (int i = 0; i < r_bins; i++) - dist[i] /= (double)cnt; + if (cnt != 0) { + // normalization + low /= static_cast(cnt); + for (int i = 0; i < r_bins; i++) { + distribution[i] /= static_cast(cnt); + } + + // integration + if (int_flag) { + distribution[0] += low; + for (int i = 0; i < r_bins - 1; i++) + distribution[i + 1] += distribution[i]; + } + } + + std::vector radii(r_bins); + if (log_flag) { + auto const log_fac = std::pow(r_max / r_min, 1. / r_bins); + radii[0] = r_min * std::sqrt(log_fac); + for (int i = 1; i < r_bins; ++i) { + radii[i] = radii[i - 1] * log_fac; + } + } else { + auto const bin_width = (r_max - r_min) / static_cast(r_bins); + for (int i = 0; i < r_bins; ++i) { + radii[i] = r_min + bin_width / 2. + static_cast(i) * bin_width; + } + } + + return {radii, distribution}; } -void calc_structurefactor(PartCfg &partCfg, std::vector const &p_types, - int order, std::vector &wavevectors, - std::vector &intensities) { +std::vector> +structure_factor(PartCfg &partCfg, std::vector const &p_types, int order) { if (order < 1) throw std::domain_error("order has to be a strictly positive number"); auto const order_sq = Utils::sqr(static_cast(order)); + auto const twoPI_L = 2. * Utils::pi() * box_geo.length_inv()[0]; std::vector ff(2 * order_sq + 1); - auto const twoPI_L = 2 * Utils::pi() * box_geo.length_inv()[0]; for (int i = 0; i <= order; i++) { for (int j = -order; j <= order; j++) { @@ -285,8 +289,8 @@ void calc_structurefactor(PartCfg &partCfg, std::vector const &p_types, } } - wavevectors.resize(length); - intensities.resize(length); + std::vector wavevectors(length); + std::vector intensities(length); int cnt = 0; for (std::size_t i = 0; i < order_sq; i++) { @@ -296,4 +300,5 @@ void calc_structurefactor(PartCfg &partCfg, std::vector const &p_types, cnt++; } } + return {std::move(wavevectors), std::move(intensities)}; } diff --git a/src/core/statistics.hpp b/src/core/analysis/statistics.hpp similarity index 74% rename from src/core/statistics.hpp rename to src/core/analysis/statistics.hpp index d2123ba7043..d86e7ca1ff4 100644 --- a/src/core/statistics.hpp +++ b/src/core/analysis/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 CORE_STATISTICS_HPP -#define CORE_STATISTICS_HPP +#ifndef ESPRESSO_SRC_CORE_ANALYSIS_STATISTICS_HPP +#define ESPRESSO_SRC_CORE_ANALYSIS_STATISTICS_HPP /** \file * Statistical tools to analyze simulations. * @@ -28,6 +28,8 @@ #include "PartCfg.hpp" +#include + #include /** Calculate the minimal distance of two particles with types in set1 resp. @@ -37,8 +39,8 @@ * @param set2 types of particles * @return the minimal distance of two particles */ -double mindist(PartCfg &partCfg, const std::vector &set1, - const std::vector &set2); +double mindist(PartCfg &partCfg, std::vector const &set1, + std::vector const &set2); /** Find all particles within a given radius @p r_catch around a position. * @param partCfg @copybrief PartCfg @@ -47,7 +49,7 @@ double mindist(PartCfg &partCfg, const std::vector &set1, * * @return List of ids close to @p pos. */ -std::vector nbhood(PartCfg &partCfg, const Utils::Vector3d &pos, +std::vector nbhood(PartCfg &partCfg, Utils::Vector3d const &pos, double dist); /** Calculate the distribution of particles around others. @@ -66,13 +68,13 @@ std::vector nbhood(PartCfg &partCfg, const Utils::Vector3d &pos, * @param r_max Maximal distance for the distribution. * @param r_bins Number of bins. * @param log_flag Whether the bins are (logarithmically) equidistant. - * @param low particles closer than @p r_min - * @param dist Array to store the result (size: @p r_bins). + * @param int_flag Whether the distribution should be cumulative. + * @return Radii and distance distribution. */ -void calc_part_distribution(PartCfg &partCfg, std::vector const &p1_types, - std::vector const &p2_types, double r_min, - double r_max, int r_bins, bool log_flag, - double *low, double *dist); +std::vector> +calc_part_distribution(PartCfg &partCfg, std::vector const &p1_types, + std::vector const &p2_types, double r_min, + double r_max, int r_bins, bool log_flag, bool int_flag); /** Calculate the spherically averaged structure factor. * @@ -90,30 +92,28 @@ void calc_part_distribution(PartCfg &partCfg, std::vector const &p1_types, * @param[in] partCfg particle collection * @param[in] p_types list with types of particles to be analyzed * @param[in] order the maximum wave vector length in units of 2PI/L - * @param[out] wavevectors the scattering vectors q - * @param[out] intensities the structure factor S(q) + * @return The scattering vectors q and structure factors S(q). */ -void calc_structurefactor(PartCfg &partCfg, std::vector const &p_types, - int order, std::vector &wavevectors, - std::vector &intensities); +std::vector> +structure_factor(PartCfg &partCfg, std::vector const &p_types, int order); /** Calculate the center of mass of a special type of the current configuration. - * \param part_type type of the particle + * @param p_type type of the particle */ -Utils::Vector3d centerofmass(PartCfg &, int part_type); +Utils::Vector3d center_of_mass(PartCfg &partCfg, int p_type); /** Calculate the angular momentum of a special type of the current * configuration. - * \param type type of the particle + * @param partCfg @copybrief PartCfg + * @param p_type type of the particle */ -Utils::Vector3d angularmomentum(PartCfg &, int type); +Utils::Vector3d angular_momentum(PartCfg &partCfg, int p_type); /** Calculate the center of mass of a special type of a saved configuration. - * \param partCfg @copybrief PartCfg - * \param type type of the particle, -1 for all - * \param MofImatrix Center of mass + * @param partCfg @copybrief PartCfg + * @param p_type type of the particle */ -void momentofinertiamatrix(PartCfg &partCfg, int type, double *MofImatrix); +Utils::Vector9d moment_of_inertia_matrix(PartCfg &partCfg, int p_type); /** Calculate total momentum of the system (particles & LB fluid). * Inputs are bools to include particles and fluid in the linear momentum diff --git a/src/core/statistics_chain.cpp b/src/core/analysis/statistics_chain.cpp similarity index 99% rename from src/core/statistics_chain.cpp rename to src/core/analysis/statistics_chain.cpp index f5122bd005d..5f4e3e3bec8 100644 --- a/src/core/statistics_chain.cpp +++ b/src/core/analysis/statistics_chain.cpp @@ -21,7 +21,8 @@ /** \file * Implementation of \ref statistics_chain.hpp "statistics_chain.hpp". */ -#include "statistics_chain.hpp" + +#include "analysis/statistics_chain.hpp" #include "Particle.hpp" #include "grid.hpp" diff --git a/src/core/statistics_chain.hpp b/src/core/analysis/statistics_chain.hpp similarity index 93% rename from src/core/statistics_chain.hpp rename to src/core/analysis/statistics_chain.hpp index 63647ae711d..41626ea1122 100644 --- a/src/core/statistics_chain.hpp +++ b/src/core/analysis/statistics_chain.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_CHAIN_H -#define STATISTICS_CHAIN_H +#ifndef ESPRESSO_SRC_CORE_ANALYSIS_STATISTICS_CHAIN_HPP +#define ESPRESSO_SRC_CORE_ANALYSIS_STATISTICS_CHAIN_HPP /** \file * * This file contains the code for statistics on chains. @@ -27,10 +27,6 @@ #include -/** \name Exported Functions */ -/************************************************************/ -/**@{*/ - /** * @brief Calculate the end-to-end-distance. * @@ -69,6 +65,5 @@ std::array calc_rg(int chain_start, int chain_n_chains, */ std::array calc_rh(int chain_start, int chain_n_chains, int chain_length); -/**@}*/ #endif diff --git a/src/core/dpd.hpp b/src/core/dpd.hpp index f1125ed7d2e..091ca91cde7 100644 --- a/src/core/dpd.hpp +++ b/src/core/dpd.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 CORE_DPD_HPP -#define CORE_DPD_HPP +#ifndef ESPRESSO_SRC_CORE_DPD_HPP +#define ESPRESSO_SRC_CORE_DPD_HPP /** \file * Routines to use DPD as thermostat or pair force @cite soddemann03a * @@ -29,6 +29,7 @@ #include "config.hpp" #ifdef DPD + #include "Particle.hpp" #include @@ -45,5 +46,5 @@ Utils::Vector3d dpd_pair_force(Particle const &p1, Particle const &p2, double dist2); Utils::Vector9d dpd_stress(); -#endif +#endif // DPD #endif diff --git a/src/core/reaction_methods/ReactionAlgorithm.cpp b/src/core/reaction_methods/ReactionAlgorithm.cpp index cbbec7f5132..90eaa102778 100644 --- a/src/core/reaction_methods/ReactionAlgorithm.cpp +++ b/src/core/reaction_methods/ReactionAlgorithm.cpp @@ -21,13 +21,13 @@ #include "reaction_methods/ReactionAlgorithm.hpp" +#include "analysis/statistics.hpp" #include "cells.hpp" #include "energy.hpp" #include "grid.hpp" #include "partCfg_global.hpp" #include "particle_data.hpp" #include "particle_node.hpp" -#include "statistics.hpp" #include #include diff --git a/src/python/espressomd/analyze.pxd b/src/python/espressomd/analyze.pxd index 9cef33f7c8e..7cc9e290a05 100644 --- a/src/python/espressomd/analyze.pxd +++ b/src/python/espressomd/analyze.pxd @@ -17,13 +17,9 @@ # along with this program. If not, see . # -# For C-extern Analysis - cimport numpy as np import numpy as np -from .utils cimport Vector3i, Vector3d, Vector9d, Span -from .utils cimport create_nparray_from_double_span -from libcpp.vector cimport vector # import std::vector as vector +from .utils cimport Span from libcpp cimport bool as cbool from libcpp.memory cimport shared_ptr from .interactions cimport bonded_ia_params_zero_based_type @@ -31,26 +27,6 @@ from .interactions cimport enum_bonded_interaction from .interactions cimport bonded_ia_params_next_key include "myconfig.pxi" -cdef extern from "" namespace "std" nogil: - cdef cppclass array4 "std::array": - array4() except+ - double & operator[](size_t) - - cdef cppclass array2 "std::array": - array2() except+ - double & operator[](size_t) - -cdef extern from "particle_data.hpp": - ctypedef struct particle "Particle" - const particle & get_particle_data(int pid) except + - -cdef extern from "PartCfg.hpp": - cppclass PartCfg: - pass - -cdef extern from "partCfg_global.hpp": - PartCfg & partCfg() - cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": int max_seen_particle_type @@ -69,37 +45,11 @@ cdef extern from "Observable_stat.hpp": Span[double] non_bonded_inter_contribution(int type1, int type2) size_t get_chunk_size() -cdef extern from "statistics.hpp": - cdef void calc_structurefactor(PartCfg & , const vector[int] & p_types, int order, vector[double] & wavevectors, vector[double] & intensities) except + - cdef double mindist(PartCfg & , const vector[int] & set1, const vector[int] & set2) - cdef vector[int] nbhood(PartCfg & , const Vector3d & pos, double dist) - cdef vector[double] calc_linear_momentum(int include_particles, int include_lbfluid) - cdef vector[double] centerofmass(PartCfg & , int part_type) - - Vector3d angularmomentum(PartCfg & , int p_type) - - void momentofinertiamatrix(PartCfg & , int p_type, double * MofImatrix) - - void calc_part_distribution( - PartCfg &, const vector[int] & p1_types, const vector[int] & p2_types, - double r_min, double r_max, int r_bins, bint log_flag, double * low, - double * dist) - -cdef extern from "statistics_chain.hpp": - array4 calc_re(int, int, int) - array4 calc_rg(int, int, int) except + - array2 calc_rh(int, int, int) - cdef extern from "pressure.hpp": cdef shared_ptr[Observable_stat] calculate_pressure() cdef extern from "energy.hpp": cdef shared_ptr[Observable_stat] calculate_energy() - double calculate_current_potential_energy_of_system() - double particle_short_range_energy_contribution(int pid) - -cdef extern from "dpd.hpp": - Vector9d dpd_stress() cdef inline get_obs_contribs(Span[double] contributions, cbool calc_scalar_pressure): @@ -119,7 +69,9 @@ cdef inline get_obs_contribs(Span[double] contributions, """ cdef np.ndarray value - value = create_nparray_from_double_span(contributions) + value = np.empty(contributions.size()) + for i in range(contributions.size()): + value[i] = contributions[i] if contributions.size() >= 9: assert contributions.size() % 3 == 0 if calc_scalar_pressure: diff --git a/src/python/espressomd/analyze.pyx b/src/python/espressomd/analyze.pyx index 4dabe1b2c59..70126b90fae 100644 --- a/src/python/espressomd/analyze.pyx +++ b/src/python/espressomd/analyze.pyx @@ -16,17 +16,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -include "myconfig.pxi" -from . cimport analyze -from libcpp.vector cimport vector # import std::vector as vector import numpy as np -cimport numpy as np -from .grid cimport box_geo -from .system import System from . import utils -from . cimport utils -from .utils cimport Vector9d +from . cimport analyze +from .code_features import assert_features +from .script_interface import script_interface_register, ScriptInterfaceHelper def autocorrelation(time_series): @@ -86,75 +81,138 @@ def autocorrelation(time_series): f"are supported, got shape {time_series.shape}") -class Analysis: +@script_interface_register +class Analysis(ScriptInterfaceHelper): + """ + Methods + ------- + linear_momentum(): + Calculates the system's linear momentum. - def __init__(self, system): - if not isinstance(system, System): - raise TypeError("An instance of System is required as argument") - self._system = system + Parameters + ---------- + include_particles : :obj:`bool`, optional + Whether to include the particles contribution to the linear + momentum. True by default. + include_lbfluid : :obj:`bool`, optional + whether to include the lattice-Boltzmann fluid contribution + to the linear momentum. True by default. - def min_dist(self, p1='default', p2='default'): - """Minimal distance between two sets of particle types. + Returns + ------- + (3,) array_like of :obj:`float` + The linear momentum of the system. + + center_of_mass(): + Calculate the center of mass of particles of a given type. + + Note that virtual sites are not included, as they do not have a + meaningful mass. Parameters ---------- - p1, p2 : lists of :obj:`int` - Particle :attr:`~espressomd.particle_data.ParticleHandle.type` in - both sets. If both are set to ``'default'``, the minimum distance - of all pairs is returned. + p_type : :obj:`int` + Particle :attr:`~espressomd.particle_data.ParticleHandle.type` + for which to calculate the center of mass. - """ + Returns + ------- + (3,) array_like of :obj:`float` + The center of mass of the particles. - if p1 == 'default' and p2 == 'default': - return analyze.mindist(analyze.partCfg(), [], []) - elif p1 == 'default' or p2 == 'default': - raise ValueError("Both p1 and p2 have to be specified") - else: - for i in range(len(p1)): - if not utils.is_valid_type(p1[i], int): - raise TypeError( - f"Particle types in p1 have to be of type int, got: {repr(p1[i])}") + nbhood(): + Get all particles in a defined neighborhood. - for i in range(len(p2)): - if not utils.is_valid_type(p2[i], int): - raise TypeError( - f"Particle types in p2 have to be of type int, got: {repr(p2[i])}") + Parameters + ---------- + pos : array of :obj:`float` + Reference position for the neighborhood. + r_catch : :obj:`float` + Radius of the region. - return analyze.mindist(analyze.partCfg(), p1, p2) + Returns + ------- + (N,) array_like of :obj:`int` + The neighbouring particle ids. - # - # Analyze Linear Momentum - # + calc_re(): + Calculate the mean end-to-end distance of chains and its + standard deviation, as well as mean square end-to-end distance of + chains and its standard deviation. - def linear_momentum(self, include_particles=True, - include_lbfluid=True): - """ - Calculates the system's linear momentum. + This requires that a set of chains of equal length which start + with the particle number ``chain_start`` and are consecutively + numbered, the last particle in that topology having id number + ``chain_start + number_of_chains * chain_length - 1``. Parameters ---------- - include_particles : :obj:`bool`, optional - whether to include the particles contribution to the linear - momentum. - include_lbfluid : :obj:`bool`, optional - whether to include the lattice-Boltzmann fluid contribution - to the linear momentum. + chain_start : :obj:`int` + The id of the first monomer of the first chain. + number_of_chains : :obj:`int` + Number of chains contained in the range. + chain_length : :obj:`int` + The length of every chain. Returns ------- - :obj:`float` - The linear momentum of the system. + (4,) array_like of :obj:`float` + Where [0] is the mean end-to-end distance of chains and [1] its + standard deviation, [2] the mean square end-to-end distance and + [3] its standard deviation. - """ - return analyze.calc_linear_momentum(include_particles, include_lbfluid) + calc_rg(): + Calculate the mean radius of gyration of chains and its standard + deviation, as well as the mean square radius of gyration of chains + and its standard deviation. - # - # Analyze center of mass - # + This requires that a set of chains of equal length which start + with the particle number ``chain_start`` and are consecutively + numbered, the last particle in that topology having id number + ``chain_start + number_of_chains * chain_length - 1``. - def center_of_mass(self, p_type=None): - """ - Calculate the system's center of mass. + Parameters + ---------- + chain_start : :obj:`int` + The id of the first monomer of the first chain. + number_of_chains : :obj:`int` + Number of chains contained in the range. + chain_length : :obj:`int` + The length of every chain. + + Returns + ------- + (4,) array_like of :obj:`float` + Where [0] is the mean radius of gyration of the chains and [1] its + standard deviation, [2] the mean square radius of gyration and [3] + its standard deviation. + + calc_rh(): + Calculate the hydrodynamic mean radius of chains and its standard + deviation. + + This requires that a set of chains of equal length which start + with the particle number ``chain_start`` and are consecutively + numbered, the last particle in that topology having id number + ``chain_start + number_of_chains * chain_length - 1``. + + Parameters + ---------- + chain_start : :obj:`int` + The id of the first monomer of the first chain + number_of_chains : :obj:`int` + Number of chains contained in the range. + chain_length : :obj:`int` + The length of every chain. + + Returns + ------- + (2,) array_like of :obj:`float`: + Where [0] is the mean hydrodynamic radius of the chains + and [1] its standard deviation. + + angular_momentum(): + Calculate the system's angular momentum with respect to the origin. Note that virtual sites are not included, as they do not have a meaningful mass. @@ -163,48 +221,110 @@ class Analysis: ---------- p_type : :obj:`int` Particle :attr:`~espressomd.particle_data.ParticleHandle.type` for - which to calculate the center of mass. + which to calculate the center of mass, or ``-1`` for all particles. Returns ------- - array of :obj:`float` - The center of mass of the system. + (3,) array_like of :obj:`float` + The center of mass of the system. - """ - if p_type is None: - raise ValueError( - "The p_type keyword argument must be provided (particle type)") - utils.check_type_or_throw_except( - p_type, 1, int, "p_type has to be an int") - if p_type < 0 or p_type >= analyze.max_seen_particle_type: - raise ValueError(f"Particle type {p_type} does not exist!") + structure_factor(): + Calculate the structure factor for given types. Returns the + spherically averaged structure factor of particles specified in + ``sf_types``. The structure factor is calculated for all possible wave + vectors q up to ``sf_order``. Do not choose parameter ``sf_order`` too + large because the number of calculations grows as ``sf_order`` to the + third power. + + Parameters + ---------- + sf_types : list of :obj:`int` + Particle :attr:`~espressomd.particle_data.ParticleHandle.type` + which should be considered. + sf_order : :obj:`int` + Specifies the maximum wavevector. - return analyze.centerofmass(analyze.partCfg(), p_type) + Returns + ------- + :obj:`ndarray` + Where [0] contains the wave vectors q + and [1] contains the structure factors S(q) - def nbhood(self, pos=None, r_catch=None): - """ - Get all particles in a defined neighborhood. + distribution(): + Calculates the distance distribution of particles (probability of + finding a particle of type A at a certain distance around a particle of + type B, disregarding the fact that a spherical shell of a larger radius + covers a larger volume). The distance is defined as the minimal distance + between a particle of group ``type_list_a`` to any of the group + ``type_list_b``. Returns two arrays, the bins and the (normalized) + distribution. Parameters ---------- - pos : array of :obj:`float` - Reference position for the neighborhood. - r_catch : :obj:`float` - Radius of the region. + type_list_a : list of :obj:`int` + List of particle :attr:`~espressomd.particle_data.ParticleHandle.type`, + only consider distances from these types. + type_list_b : list of :obj:`int` + List of particle :attr:`~espressomd.particle_data.ParticleHandle.type`, + only consider distances to these types. + r_min : :obj:`float` + Minimum distance. Default is 0. + r_max : :obj:`float` + Maximum distance. By default, it is half the box size. + A value larger than half the box size is allowed for systems + with :ref:`open boundary conditions `. + r_bins : :obj:`int` + Number of bins. Default is 100. + log_flag : :obj:`bool` + When set to ``False``, the bins are linearly equidistant; when set + to ``True``, the bins are logarithmically equidistant. + int_flag : :obj:`bool` + When set to ``True``, the result is an integrated distribution. Returns ------- - array of :obj:`int` - The neighbouring particle ids. + :obj:`ndarray` + Where [0] contains the midpoints of the bins, + and [1] contains the values of the rdf. + """ + _so_name = "ScriptInterface::Analysis::Analysis" + _so_creation_policy = "LOCAL" + _so_bind_methods = ( + "linear_momentum", + "center_of_mass", + "nbhood", + "calc_re", + "calc_rg", + "calc_rh", + "angular_momentum", + "structure_factor", + "distribution") + + def min_dist(self, p1='default', p2='default'): """ + Minimal distance between two sets of particle types. + + Parameters + ---------- + p1, p2 : lists of :obj:`int` + Particle :attr:`~espressomd.particle_data.ParticleHandle.type` in + both sets. If both are set to ``'default'``, the minimum distance + of all pairs is returned. - utils.check_type_or_throw_except(pos, 3, float, "pos must be 3 floats") - utils.check_type_or_throw_except( - r_catch, 1, float, "r_catch must be a float") + Returns + ------- + :obj:`float` + Minimal distance. + + """ - return analyze.nbhood( - analyze.partCfg(), utils.make_Vector3d(pos), r_catch) + if p1 == 'default' and p2 == 'default': + p1 = [] + p2 = [] + elif p1 == 'default' or p2 == 'default': + raise ValueError("Both p1 and p2 have to be specified") + return self.call_method("min_dist", p_types1=p1, p_types2=p2) def pressure(self): """ @@ -293,18 +413,6 @@ class Analysis: utils.handle_errors("calculate_pressure() failed") return obs - IF DPD == 1: - def dpd_stress(self): - cdef Vector9d p = dpd_stress() - return utils.array_locked(( - p[0], p[1], p[2], - p[3], p[4], p[5], - p[6], p[7], p[8])).reshape((3, 3)) - - # - # Energy analysis - # - def energy(self): """ Calculate the system energy in parallel. @@ -365,301 +473,24 @@ class Analysis: Returns ------- :obj: `float` - non-bonded energy of that particle - - """ - energy_contribution = particle_short_range_energy_contribution( - particle.id) - return energy_contribution - - def calc_re(self, chain_start=None, number_of_chains=None, - chain_length=None): - """ - Calculate the mean end-to-end distance of chains and its - standard deviation, as well as mean square end-to-end distance of - chains and its standard deviation. - - This requires that a set of chains of equal length which start - with the particle number ``chain_start`` and are consecutively - numbered, the last particle in that topology having id number - ``chain_start + number_of_chains * chain_length - 1``. - - Parameters - ---------- - chain_start : :obj:`int` - The id of the first monomer of the first chain. - number_of_chains : :obj:`int` - Number of chains contained in the range. - chain_length : :obj:`int` - The length of every chain. - - Returns - ------- - (4,) array_like of :obj:`float` - Where [0] is the mean end-to-end distance of chains and [1] its - standard deviation, [2] the mean square end-to-end distance and - [3] its standard deviation. - - """ - self.check_topology(chain_start, number_of_chains, chain_length) - re = analyze.calc_re(chain_start, number_of_chains, chain_length) - return np.array([re[0], re[1], re[2], re[3]]) - - def calc_rg(self, chain_start=None, number_of_chains=None, - chain_length=None): - """ - Calculate the mean radius of gyration of chains and its standard - deviation, as well as the mean square radius of gyration of chains - and its standard deviation. - - This requires that a set of chains of equal length which start - with the particle number ``chain_start`` and are consecutively - numbered, the last particle in that topology having id number - ``chain_start + number_of_chains * chain_length - 1``. - - Parameters - ---------- - chain_start : :obj:`int` - The id of the first monomer of the first chain. - number_of_chains : :obj:`int` - Number of chains contained in the range. - chain_length : :obj:`int` - The length of every chain. - - Returns - ------- - (4,) array_like of :obj:`float` - Where [0] is the mean radius of gyration of the chains and [1] its - standard deviation, [2] the mean square radius of gyration and [3] - its standard deviation. - - """ - self.check_topology(chain_start, number_of_chains, chain_length) - rg = analyze.calc_rg(chain_start, number_of_chains, chain_length) - return np.array([rg[0], rg[1], rg[2], rg[3]]) - - def calc_rh(self, chain_start=None, number_of_chains=None, - chain_length=None): - """ - Calculate the hydrodynamic mean radius of chains and its standard - deviation. - - This requires that a set of chains of equal length which start - with the particle number ``chain_start`` and are consecutively - numbered, the last particle in that topology having id number - ``chain_start + number_of_chains * chain_length - 1``. - - Parameters - ---------- - chain_start : :obj:`int` - The id of the first monomer of the first chain - number_of_chains : :obj:`int` - Number of chains contained in the range. - chain_length : :obj:`int` - The length of every chain. - - Returns - ------- - (2,) array_like of :obj:`float`: - Where [0] is the mean hydrodynamic radius of the chains - and [1] its standard deviation. - - """ - - self.check_topology(chain_start, number_of_chains, chain_length) - rh = analyze.calc_rh(chain_start, number_of_chains, chain_length) - return np.array([rh[0], rh[1]]) - - def check_topology(self, chain_start=None, number_of_chains=None, - chain_length=None): - utils.check_type_or_throw_except( - chain_start, 1, int, "chain_start=int is a required argument") - utils.check_type_or_throw_except( - number_of_chains, 1, int, "number_of_chains=int is a required argument") - utils.check_type_or_throw_except( - chain_length, 1, int, "chain_length=int is a required argument") - id_min = chain_start - id_max = chain_start + chain_length * number_of_chains - for i in range(id_min, id_max): - if not self._system.part.exists(i): - raise ValueError(f'particle with id {i} does not exist\n' - f'cannot perform analysis on the range ' - f'chain_start={chain_start}, number_of_chains=' - f'{number_of_chains}, chain_length={chain_length}\n' - f'please provide a contiguous range of particle ids') - - # - # Structure factor - # - - def structure_factor(self, sf_types=None, sf_order=None): - """ - Calculate the structure factor for given types. Returns the - spherically averaged structure factor of particles specified in - ``sf_types``. The structure factor is calculated for all possible wave - vectors q up to ``sf_order``. Do not choose parameter ``sf_order`` too - large because the number of calculations grows as ``sf_order`` to the - third power. - - Parameters - ---------- - sf_types : list of :obj:`int` - Specifies which particle :attr:`~espressomd.particle_data.ParticleHandle.type` - should be considered. - sf_order : :obj:`int` - Specifies the maximum wavevector. - - Returns - ------- - :obj:`ndarray` - Where [0] contains q - and [1] contains the structure factor s(q) - - """ - - if sf_types is None or not hasattr(sf_types, '__iter__'): - raise ValueError("sf_types has to be a list!") - utils.check_type_or_throw_except( - sf_order, 1, int, "sf_order has to be an int!") - - cdef vector[double] wavevectors - cdef vector[double] intensities - analyze.calc_structurefactor( - analyze.partCfg(), sf_types, sf_order, wavevectors, intensities) - - return np.vstack([wavevectors, intensities]) - - # - # distribution - # - - def distribution(self, type_list_a=None, type_list_b=None, - r_min=0.0, r_max=None, r_bins=100, log_flag=0, int_flag=0): - """ - Calculates the distance distribution of particles (probability of - finding a particle of type A at a certain distance around a particle of - type B, disregarding the fact that a spherical shell of a larger radius - covers a larger volume). The distance is defined as the minimal distance - between a particle of group ``type_list_a`` to any of the group - ``type_list_b`` (excluding the self-contribution). Returns two arrays, - the bins and the (normalized) distribution. - - Parameters - ---------- - type_list_a : list of :obj:`int` - List of particle :attr:`~espressomd.particle_data.ParticleHandle.type`, - only consider distances from these types. - type_list_b : list of :obj:`int` - List of particle :attr:`~espressomd.particle_data.ParticleHandle.type`, - only consider distances to these types. - r_min : :obj:`float` - Minimum distance. - r_max : :obj:`float` - Maximum distance. By default, it is half the box size. - A value larger than half the box size is allowed for systems - with :ref:`open boundary conditions `. - r_bins : :obj:`int` - Number of bins. - log_flag : :obj:`bool` - When set to ``False``, the bins are linearly equidistant; when set - to ``True``, the bins are logarithmically equidistant. - int_flag : :obj:`bool` - When set to ``True``, the result is an integrated distribution. - - Returns - ------- - :obj:`ndarray` - Where [0] contains the midpoints of the bins, - and [1] contains the values of the rdf. + Non-bonded energy of that particle """ + return self.call_method("particle_energy", pid=particle.id) - if (type_list_a is None) or (not hasattr(type_list_a, '__iter__')): - raise ValueError("type_list_a has to be a list!") - if (type_list_b is None) or (not hasattr(type_list_b, '__iter__')): - raise ValueError("type_list_b has to be a list!") - - if r_max is None: - box_l = utils.make_array_locked(box_geo.length()) - r_max = min(box_l) / 2 - - assert r_min >= 0.0, "r_min was chosen too small!" - assert not log_flag or r_min != 0.0, "r_min cannot include zero" - assert r_max > r_min, "r_max has to be greater than r_min!" - assert r_bins >= 1, "r_bins has to be greater than zero!" - - cdef double low - cdef vector[double] distribution - distribution.resize(r_bins) - - analyze.calc_part_distribution( - analyze.partCfg(), type_list_a, type_list_b, - r_min, r_max, r_bins, < bint > log_flag, & low, distribution.data()) - - np_distribution = utils.create_nparray_from_double_array( - distribution.data(), r_bins) - - if int_flag: - np_distribution[0] += low - for i in xrange(r_bins - 1): - np_distribution[i + 1] += np_distribution[i] - - r = np.zeros(r_bins) - - if log_flag: - log_fac = (r_max / r_min)**(1.0 / r_bins) - r[0] = r_min * np.sqrt(log_fac) - for i in xrange(1, r_bins): - r[i] = r[i - 1] * log_fac - else: - bin_width = (r_max - r_min) / float(r_bins) - r = np.linspace(r_min + bin_width / 2.0, - r_max - bin_width / 2.0, r_bins) - - return np.array([r, np_distribution]) - - # - # angularmomentum - # - - def angular_momentum(self, p_type=None): - """ - Calculates the system's angular momentum with respect to the origin. - - Note that virtual sites are not included, as they do not have a meaningful mass. - - Parameters - ---------- - p_type : :obj:`int` - Particle :attr:`~espressomd.particle_data.ParticleHandle.type` for - which to calculate the center of mass. - - Returns - ------- - (3,) :obj:`ndarray` of :obj:`float` - The center of mass of the system. - - """ - utils.check_type_or_throw_except( - p_type, 1, int, "p_type has to be an int") - - return np.array(utils.make_array_locked( - analyze.angularmomentum(analyze.partCfg(), p_type))) - - # - # gyration_tensor - # + def dpd_stress(self): + assert_features("DPD") + return np.reshape(self.call_method("dpd_stress"), (3, 3)) def gyration_tensor(self, p_type=None): """ - Analyze the gyration tensor of particles of a given type or of all - particles in the system if no type is given. + Analyze the gyration tensor of particles of a given type. Parameters ---------- - p_type : list of :obj:`int`, optional + p_type : list of :obj:`int` A particle :attr:`~espressomd.particle_data.ParticleHandle.type`, - or list of all particle types to be considered. + or list of particle types to be considered. Returns ------- @@ -680,37 +511,22 @@ class Analysis: "The p_type keyword argument must be provided (particle type)") if not hasattr(p_type, '__iter__'): p_type = [p_type] - for ptype in p_type: - utils.check_type_or_throw_except( - ptype, 1, int, "particle type has to be an int") - if ptype < 0 or ptype >= analyze.max_seen_particle_type: - raise ValueError(f"Particle type {ptype} does not exist!") - selection = self._system.part.select(lambda p: (p.type in p_type)) - cm = np.mean(selection.pos, axis=0) - mat = np.zeros(shape=(3, 3)) - for i, j in np.ndindex((3, 3)): - mat[i, j] = np.mean(((selection.pos)[:, i] - cm[i]) * ( - (selection.pos)[:, j] - cm[j])) + vec = self.call_method("gyration_tensor", p_types=p_type) + mat = np.reshape(vec, (3, 3)) w, v = np.linalg.eig(mat) # return eigenvalue/vector tuples in order of increasing eigenvalues order = np.argsort(np.abs(w))[::-1] - rad_gyr_sqr = mat[0, 0] + mat[1, 1] + mat[2, 2] + rad_gyr_sqr = np.trace(mat) aspheric = w[order[0]] - 0.5 * (w[order[1]] + w[order[2]]) acylindric = w[order[1]] - w[order[2]] rel_shape_anis = (aspheric**2 + 0.75 * acylindric**2) / rad_gyr_sqr**2 return { "Rg^2": rad_gyr_sqr, - "shape": [aspheric, - acylindric, - rel_shape_anis], + "shape": [aspheric, acylindric, rel_shape_anis], "eva0": (w[order[0]], v[:, order[0]]), "eva1": (w[order[1]], v[:, order[1]]), "eva2": (w[order[2]], v[:, order[2]])} - # - # momentofinertiamatrix - # - def moment_of_inertia_matrix(self, p_type=None): """ Returns the 3x3 moment of inertia matrix for particles of a given type. @@ -718,30 +534,13 @@ class Analysis: Parameters ---------- p_type : :obj:`int` - A particle :attr:`~espressomd.particle_data.ParticleHandle.type` + A particle :attr:`~espressomd.particle_data.ParticleHandle.type`. Returns ------- - :obj:`ndarray` - 3x3 moment of inertia matrix. + (3,3) array_like of :obj:`float` + Moment of inertia matrix. """ - - cdef double[9] MofImatrix - - if p_type is None: - raise ValueError( - "The p_type keyword argument must be provided (particle type)") - utils.check_type_or_throw_except( - p_type, 1, int, "p_type has to be an int") - if p_type < 0 or p_type >= analyze.max_seen_particle_type: - raise ValueError(f"Particle type {p_type} does not exist!") - - analyze.momentofinertiamatrix( - analyze.partCfg(), p_type, MofImatrix) - - MofImatrix_np = np.empty((9)) - for i in range(9): - MofImatrix_np[i] = MofImatrix[i] - - return MofImatrix_np.reshape((3, 3)) + vec = self.call_method("moment_of_inertia_matrix", p_type=p_type) + return np.reshape(vec, (3, 3)) diff --git a/src/python/espressomd/polymer.pxd b/src/python/espressomd/polymer.pxd index c33de5b3f35..8f81e8f279d 100644 --- a/src/python/espressomd/polymer.pxd +++ b/src/python/espressomd/polymer.pxd @@ -19,7 +19,13 @@ from libcpp.vector cimport vector from .utils cimport Vector3d -from .analyze cimport PartCfg + +cdef extern from "PartCfg.hpp": + cppclass PartCfg: + pass + +cdef extern from "partCfg_global.hpp": + PartCfg & partCfg() cdef extern from "polymer.hpp": vector[vector[Vector3d]] draw_polymer_positions(PartCfg &, int n_polymers, int beads_per_polymer, double bond_length, vector[Vector3d] & start_positions, double min_distance, int max_tries, int use_bond_angle, double bond_angle, int respect_constraints, int seed) except + diff --git a/src/python/espressomd/polymer.pyx b/src/python/espressomd/polymer.pyx index 34063a2d317..9bbbc8e4c21 100644 --- a/src/python/espressomd/polymer.pyx +++ b/src/python/espressomd/polymer.pyx @@ -24,7 +24,7 @@ from .system import System from .interactions import BondedInteraction from .utils cimport make_Vector3d, check_type_or_throw_except from .utils import array_locked -from .analyze cimport partCfg +from .polymer cimport partCfg def validate_params(_params, default): diff --git a/src/python/espressomd/system.pyx b/src/python/espressomd/system.pyx index 3bb08f381a5..2a40a19ed7e 100644 --- a/src/python/espressomd/system.pyx +++ b/src/python/espressomd/system.pyx @@ -178,7 +178,7 @@ cdef class System: f"Property '{arg}' can not be set via argument to System class.") System.__setattr__(self, arg, kwargs.get(arg)) self.actors = actors.Actors() - self.analysis = analyze.Analysis(self) + self.analysis = analyze.Analysis() self.auto_update_accumulators = accumulators.AutoUpdateAccumulators() self.bonded_inter = interactions.BondedInteractions() self.cell_system = cell_system.CellSystem() diff --git a/src/python/espressomd/utils.pxd b/src/python/espressomd/utils.pxd index 0f68c8ecde3..855799ced20 100644 --- a/src/python/espressomd/utils.pxd +++ b/src/python/espressomd/utils.pxd @@ -38,8 +38,6 @@ cdef extern from "utils/Span.hpp" namespace "Utils": Span[const T] make_const_span[T](T *, size_t) -cdef np.ndarray create_nparray_from_double_array(double * x, int n) -cdef np.ndarray create_nparray_from_double_span(Span[double] x) cpdef check_array_type_or_throw_except(x, n, t, msg) cpdef check_type_or_throw_except(x, n, t, msg) diff --git a/src/python/espressomd/utils.pyx b/src/python/espressomd/utils.pyx index 779f9d46ae2..8900b2a6e0a 100644 --- a/src/python/espressomd/utils.pyx +++ b/src/python/espressomd/utils.pyx @@ -68,33 +68,6 @@ cpdef check_type_or_throw_except(x, n, t, msg): raise ValueError(msg + f" -- Got an {type(x).__name__}") -cdef np.ndarray create_nparray_from_double_array(double * x, int len_x): - """ - Returns a numpy array from double array. - - Parameters - ---------- - x : C-style array of type double which is to be converted - len_x: len of array - - """ - numpyArray = np.zeros(len_x) - for i in range(len_x): - numpyArray[i] = x[i] - return numpyArray - -cdef np.ndarray create_nparray_from_double_span(Span[double] x): - """ - Returns a numpy array from double span. - - Parameters - ---------- - x : Span of type double which is to be converted - - """ - return create_nparray_from_double_array(x.data(), x.size()) - - def to_char_pointer(s): """ Returns a char pointer which contains the information of the provided python string. diff --git a/src/script_interface/CMakeLists.txt b/src/script_interface/CMakeLists.txt index 5b06f982758..9c9a19ce1ab 100644 --- a/src/script_interface/CMakeLists.txt +++ b/src/script_interface/CMakeLists.txt @@ -24,6 +24,7 @@ add_library( add_library(Espresso::script_interface ALIAS Espresso_script_interface) add_subdirectory(accumulators) +add_subdirectory(analysis) add_subdirectory(bond_breakage) add_subdirectory(cell_system) add_subdirectory(cluster_analysis) diff --git a/src/script_interface/analysis/Analysis.cpp b/src/script_interface/analysis/Analysis.cpp new file mode 100644 index 00000000000..2f3862e925c --- /dev/null +++ b/src/script_interface/analysis/Analysis.cpp @@ -0,0 +1,225 @@ +/* + * 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 . + */ + +#include "Analysis.hpp" + +#include "core/analysis/statistics.hpp" +#include "core/analysis/statistics_chain.hpp" +#include "core/dpd.hpp" +#include "core/energy.hpp" +#include "core/grid.hpp" +#include "core/nonbonded_interactions/nonbonded_interaction_data.hpp" +#include "core/partCfg_global.hpp" +#include "core/particle_node.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace ScriptInterface { +namespace Analysis { + +/** @brief Check if a contiguous range of particle ids exists. */ +static void check_topology(int chain_start, int chain_length, int n_chains) { + for (int i = 0; i < chain_length * n_chains; ++i) { + auto const pid = chain_start + i; + if (not particle_exists(pid)) { + std::stringstream error_msg; + error_msg << "Particle with id " << pid << " does not exist; " + << "cannot perform analysis on the range chain_start=" + << chain_start << ", number_of_chains=" << n_chains + << ", chain_length=" << chain_length << ". " + << "Please provide a contiguous range of particle ids."; + throw std::runtime_error(error_msg.str()); + } + } +} + +/** @brief Check if a particle type exists. */ +static void check_particle_type(int p_type) { + if (p_type < 0 or p_type >= ::max_seen_particle_type) { + std::stringstream error_msg; + error_msg << "Particle type " << p_type << " does not exist"; + throw std::invalid_argument(error_msg.str()); + } +} + +Variant Analysis::do_call_method(std::string const &name, + VariantMap const ¶meters) { + if (name == "min_dist") { + auto const p_types1 = get_value>(parameters, "p_types1"); + auto const p_types2 = get_value>(parameters, "p_types2"); + for (auto const p_type : p_types1) { + check_particle_type(p_type); + } + for (auto const p_type : p_types2) { + check_particle_type(p_type); + } + return mindist(partCfg(), p_types1, p_types2); + } + if (name == "center_of_mass") { + auto const p_type = get_value(parameters, "p_type"); + check_particle_type(p_type); + return center_of_mass(partCfg(), p_type).as_vector(); + } + if (name == "angular_momentum") { + auto const p_type = get_value(parameters, "p_type"); + auto const result = angular_momentum(partCfg(), p_type); + return result.as_vector(); + } + if (name == "linear_momentum") { + auto const result = calc_linear_momentum( + get_value_or(parameters, "include_particles", true), + get_value_or(parameters, "include_lbfluid", true)); + return result.as_vector(); + } + if (name == "nbhood") { + auto const pos = get_value(parameters, "pos"); + auto const radius = get_value(parameters, "r_catch"); + auto const result = nbhood(partCfg(), pos, radius); + return result; + } + if (name == "dpd_stress") { + auto const result = dpd_stress(); + return result.as_vector(); + } + if (name == "particle_energy") { + auto const pid = get_value(parameters, "pid"); + return particle_short_range_energy_contribution(pid); + } + if (name == "calc_re") { + auto const chain_start = get_value(parameters, "chain_start"); + auto const chain_length = get_value(parameters, "chain_length"); + auto const n_chains = get_value(parameters, "number_of_chains"); + check_topology(chain_start, chain_length, n_chains); + auto const result = calc_re(chain_start, n_chains, chain_length); + return std::vector(result.begin(), result.end()); + } + if (name == "calc_rg") { + auto const chain_start = get_value(parameters, "chain_start"); + auto const chain_length = get_value(parameters, "chain_length"); + auto const n_chains = get_value(parameters, "number_of_chains"); + check_topology(chain_start, chain_length, n_chains); + auto const result = calc_rg(chain_start, n_chains, chain_length); + return std::vector(result.begin(), result.end()); + } + if (name == "calc_rh") { + auto const chain_start = get_value(parameters, "chain_start"); + auto const chain_length = get_value(parameters, "chain_length"); + auto const n_chains = get_value(parameters, "number_of_chains"); + check_topology(chain_start, chain_length, n_chains); + auto const result = calc_rh(chain_start, n_chains, chain_length); + return std::vector(result.begin(), result.end()); + } + if (name == "gyration_tensor") { + auto const p_types = get_value>(parameters, "p_types"); + for (auto const p_type : p_types) { + check_particle_type(p_type); + } + std::vector positions{}; + for (const auto &p : partCfg()) { + if (Utils::contains(p_types, p.type())) { + positions.push_back(p.pos()); + } + } + auto const com = + std::accumulate(positions.begin(), positions.end(), Utils::Vector3d{}) / + static_cast(positions.size()); + // compute covariance matrix + Utils::Vector9d mat{}; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + if (i > j) { + mat[i * 3 + j] = mat[j * 3 + i]; + } else { + mat[i * 3 + j] = std::accumulate( + positions.begin(), positions.end(), 0., + [i, j, &com](double acc, Utils::Vector3d const &pos) { + return acc + (pos[i] - com[i]) * (pos[j] - com[j]); + }); + } + } + } + mat /= static_cast(positions.size()); + return std::vector(mat.begin(), mat.end()); + } + if (name == "moment_of_inertia_matrix") { + auto const p_type = get_value(parameters, "p_type"); + check_particle_type(p_type); + auto const result = moment_of_inertia_matrix(partCfg(), p_type); + return result.as_vector(); + } + if (name == "structure_factor") { + auto const order = get_value(parameters, "sf_order"); + auto const p_types = get_value>(parameters, "sf_types"); + for (auto const p_type : p_types) { + check_particle_type(p_type); + } + auto const result = structure_factor(partCfg(), p_types, order); + return make_vector_of_variants(result); + } + if (name == "distribution") { + auto const r_max_limit = + 0.5 * std::min(std::min(::box_geo.length()[0], ::box_geo.length()[1]), + ::box_geo.length()[2]); + auto const r_min = get_value_or(parameters, "r_min", 0.); + auto const r_max = get_value_or(parameters, "r_max", r_max_limit); + auto const r_bins = get_value_or(parameters, "r_bins", 100); + auto const log_flag = get_value_or(parameters, "log_flag", false); + auto const int_flag = get_value_or(parameters, "int_flag", false); + if (log_flag and r_min <= 0.) { + throw std::domain_error("Parameter 'r_min' must be > 0"); + } + if (r_min < 0.) { + throw std::domain_error("Parameter 'r_min' must be >= 0"); + } + if (r_min >= r_max) { + throw std::domain_error("Parameter 'r_max' must be > 'r_min'"); + } + if (r_max > r_max_limit) { + throw std::domain_error("Parameter 'r_max' must be <= box_l / 2"); + } + if (r_bins <= 0) { + throw std::domain_error("Parameter 'r_bins' must be >= 1"); + } + auto const p_types1 = + get_value>(parameters, "type_list_a"); + auto const p_types2 = + get_value>(parameters, "type_list_b"); + for (auto const p_type : p_types1) { + check_particle_type(p_type); + } + for (auto const p_type : p_types2) { + check_particle_type(p_type); + } + return make_vector_of_variants( + calc_part_distribution(partCfg(), p_types1, p_types2, r_min, r_max, + r_bins, log_flag, int_flag)); + } + return {}; +} + +} // namespace Analysis +} // namespace ScriptInterface diff --git a/src/script_interface/analysis/Analysis.hpp b/src/script_interface/analysis/Analysis.hpp new file mode 100644 index 00000000000..ef4bce4fea7 --- /dev/null +++ b/src/script_interface/analysis/Analysis.hpp @@ -0,0 +1,39 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_ANALYSIS_ANALYSIS_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_ANALYSIS_ANALYSIS_HPP + +#include "script_interface/ScriptInterface.hpp" + +#include + +namespace ScriptInterface { +namespace Analysis { + +class Analysis : public ObjectHandle { +public: + Variant do_call_method(std::string const &name, + VariantMap const ¶meters) override; +}; + +} // namespace Analysis +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/analysis/CMakeLists.txt b/src/script_interface/analysis/CMakeLists.txt new file mode 100644 index 00000000000..59bf25d5912 --- /dev/null +++ b/src/script_interface/analysis/CMakeLists.txt @@ -0,0 +1,3 @@ +target_sources( + Espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Analysis.cpp) diff --git a/src/script_interface/analysis/initialize.cpp b/src/script_interface/analysis/initialize.cpp new file mode 100644 index 00000000000..d0fa36a8ed4 --- /dev/null +++ b/src/script_interface/analysis/initialize.cpp @@ -0,0 +1,32 @@ +/* + * 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 "Analysis.hpp" + +namespace ScriptInterface { +namespace Analysis { + +void initialize(Utils::Factory *om) { + om->register_new("ScriptInterface::Analysis::Analysis"); +} + +} // namespace Analysis +} // namespace ScriptInterface diff --git a/src/script_interface/analysis/initialize.hpp b/src/script_interface/analysis/initialize.hpp new file mode 100644 index 00000000000..26760cb6430 --- /dev/null +++ b/src/script_interface/analysis/initialize.hpp @@ -0,0 +1,35 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_ANALYSIS_INITIALIZE_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_ANALYSIS_INITIALIZE_HPP + +#include "script_interface/ObjectHandle.hpp" + +#include + +namespace ScriptInterface { +namespace Analysis { + +void initialize(Utils::Factory *om); + +} // namespace Analysis +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/initialize.cpp b/src/script_interface/initialize.cpp index 5da10ccf079..afa76e1df45 100644 --- a/src/script_interface/initialize.cpp +++ b/src/script_interface/initialize.cpp @@ -24,6 +24,7 @@ #include "ComFixed.hpp" #include "CylindricalTransformationParameters.hpp" #include "accumulators/initialize.hpp" +#include "analysis/initialize.hpp" #include "bond_breakage/initialize.hpp" #include "cell_system/initialize.hpp" #include "cluster_analysis/initialize.hpp" @@ -45,6 +46,7 @@ namespace ScriptInterface { void initialize(Utils::Factory *f) { Accumulators::initialize(f); + Analysis::initialize(f); BondBreakage::initialize(f); CellSystem::initialize(f); ClusterAnalysis::initialize(f); diff --git a/testsuite/python/analyze_chains.py b/testsuite/python/analyze_chains.py index 073b1015c37..c328d4a779b 100644 --- a/testsuite/python/analyze_chains.py +++ b/testsuite/python/analyze_chains.py @@ -134,14 +134,16 @@ def test_radii(self): all_partcls.pos = old_pos def test_exceptions(self): - err_msg = """particle with id 10 does not exist -cannot perform analysis on the range chain_start=0, number_of_chains=2, chain_length=10 -please provide a contiguous range of particle ids""" + err_msg = ("Particle with id 10 does not exist; cannot perform " + "analysis on the range chain_start=0, number_of_chains=2, " + "chain_length=10. Please provide a contiguous range of " + "particle ids.") analysis = self.system.analysis for method in (analysis.calc_re, analysis.calc_rg, analysis.calc_rh): - with self.assertRaisesRegex(ValueError, err_msg): + with self.assertRaisesRegex(RuntimeError, err_msg): method(chain_start=0, number_of_chains=self.num_poly, chain_length=2 * self.num_mono) + self.assertIsNone(analysis.call_method("unknown")) if __name__ == "__main__": diff --git a/testsuite/python/analyze_distribution.py b/testsuite/python/analyze_distribution.py index 650ee6fe7f3..df6c8814d0c 100644 --- a/testsuite/python/analyze_distribution.py +++ b/testsuite/python/analyze_distribution.py @@ -44,33 +44,52 @@ def calc_min_distribution(self, bins, int_flag): def test_distribution_lin(self): r_min = 0.0 - r_max = 100. + r_max = 20. r_bins = 100 edges = np.linspace(r_min, r_max, num=r_bins + 1, endpoint=True) ref_bins = (edges[1:] + edges[:-1]) / 2. - for int_flag in (0, 1): + for int_flag in (False, True): ref_rdf = self.calc_min_distribution(edges, int_flag) core_rdf = self.system.analysis.distribution( type_list_a=[0], type_list_b=[0], r_min=r_min, r_max=r_max, - r_bins=r_bins, log_flag=0, int_flag=int_flag) + r_bins=r_bins, log_flag=False, int_flag=int_flag) np.testing.assert_allclose(core_rdf[0], ref_bins) np.testing.assert_allclose(core_rdf[1], ref_rdf) def test_distribution_log(self): r_min = 0.01 - r_max = 100. + r_max = 20. r_bins = 100 edges = np.geomspace(r_min, r_max, num=r_bins + 1) - for int_flag in (0, 1): + for int_flag in (False, True): ref_rdf = self.calc_min_distribution(edges, int_flag) core_rdf = self.system.analysis.distribution( type_list_a=[0], type_list_b=[0], r_min=r_min, r_max=r_max, - r_bins=r_bins, log_flag=1, int_flag=int_flag) + r_bins=r_bins, log_flag=True, int_flag=int_flag) ref_bins = np.geomspace( core_rdf[0][0], core_rdf[0][-1], r_bins, endpoint=True) np.testing.assert_allclose(core_rdf[0], ref_bins) np.testing.assert_allclose(core_rdf[1], ref_rdf) + def test_exceptions(self): + valid_params = { + "type_list_a": [0], "type_list_b": [0], "r_min": 0.01, "r_max": 20., + "r_bins": 100, "log_flag": False, "int_flag": False + } + exceptions = [ + ("Parameter 'r_min' must be >= 0", {"r_min": -0.1}), + ("Parameter 'r_min' must be > 0", {"r_min": 0., "log_flag": True}), + ("Parameter 'r_max' must be > 'r_min'", + {"r_min": 2., "r_max": 1.}), + ("Parameter 'r_max' must be <= box_l / 2", {"r_max": 20.0001}), + ("Parameter 'r_bins' must be >= 1", {"r_bins": 0}), + ("Particle type 99 does not exist", {"type_list_a": [99]}), + ("Particle type -1 does not exist", {"type_list_b": [-1]}), + ] + for err_msg, kwargs in exceptions: + with self.assertRaisesRegex(ValueError, err_msg): + self.system.analysis.distribution(**{**valid_params, **kwargs}) + if __name__ == "__main__": ut.main() diff --git a/testsuite/python/analyze_mass_related.py b/testsuite/python/analyze_mass_related.py index 5a270893089..f62eb173bd9 100644 --- a/testsuite/python/analyze_mass_related.py +++ b/testsuite/python/analyze_mass_related.py @@ -130,8 +130,7 @@ def test_kinetic_energy(self): def test_kinetic_pressure(self): no_virtual = self.system.part.select(virtual=False) P_kin = np.sum( - no_virtual.mass * np.sum(no_virtual.v**2, - axis=1), + no_virtual.mass * np.sum(no_virtual.v**2, axis=1), axis=0) / (3 * self.system.volume()) np.testing.assert_allclose( P_kin, self.system.analysis.pressure()["kinetic"]) @@ -145,7 +144,7 @@ def test_kinetic_pressure(self): def test_gyration_radius(self): if self.system.part.select(virtual=True): - with self.assertRaisesRegex(Exception, "not well-defined"): + with self.assertRaisesRegex(RuntimeError, "not well-defined"): self.system.analysis.calc_rg(chain_start=0, number_of_chains=1, chain_length=len(self.system.part)) From 5add7f87025c9f6df0ca7fac9e897011afed1e3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 19 Jul 2022 23:50:43 +0200 Subject: [PATCH 07/85] script_interface: Rewrite System module --- src/core/EspressoSystemStandAlone.cpp | 12 +- src/core/event.cpp | 6 +- src/core/grid.cpp | 61 +-- src/core/grid.hpp | 18 +- src/core/integrate.cpp | 3 +- .../nonbonded_interaction_data.cpp | 14 +- .../nonbonded_interaction_data.hpp | 5 +- src/core/rotate_system.cpp | 17 +- src/core/rotate_system.hpp | 8 +- src/python/espressomd/system.pxd | 46 -- .../espressomd/{system.pyx => system.py} | 515 ++++++++---------- src/script_interface/CMakeLists.txt | 1 + src/script_interface/initialize.cpp | 2 + src/script_interface/system/CMakeLists.txt | 5 + src/script_interface/system/Globals.cpp | 81 +++ src/script_interface/system/Globals.hpp | 62 +++ src/script_interface/system/System.cpp | 127 +++++ src/script_interface/system/System.hpp | 41 ++ src/script_interface/system/initialize.cpp | 34 ++ src/script_interface/system/initialize.hpp | 35 ++ testsuite/python/box_geometry.py | 14 +- testsuite/python/coulomb_interface.py | 2 +- testsuite/python/rescale.py | 8 + testsuite/python/save_checkpoint.py | 2 +- testsuite/python/stokesian_thermostat.py | 8 +- 25 files changed, 680 insertions(+), 447 deletions(-) delete mode 100644 src/python/espressomd/system.pxd rename src/python/espressomd/{system.pyx => system.py} (51%) create mode 100644 src/script_interface/system/CMakeLists.txt create mode 100644 src/script_interface/system/Globals.cpp create mode 100644 src/script_interface/system/Globals.hpp create mode 100644 src/script_interface/system/System.cpp create mode 100644 src/script_interface/system/System.hpp create mode 100644 src/script_interface/system/initialize.cpp create mode 100644 src/script_interface/system/initialize.hpp diff --git a/src/core/EspressoSystemStandAlone.cpp b/src/core/EspressoSystemStandAlone.cpp index 90788c3c023..1f948319ed4 100644 --- a/src/core/EspressoSystemStandAlone.cpp +++ b/src/core/EspressoSystemStandAlone.cpp @@ -20,7 +20,9 @@ #include "config.hpp" #include "EspressoSystemStandAlone.hpp" +#include "MpiCallbacks.hpp" #include "communication.hpp" +#include "event.hpp" #include "grid.hpp" #include "integrate.hpp" #include "virtual_sites.hpp" @@ -50,6 +52,14 @@ EspressoSystemStandAlone::EspressoSystemStandAlone(int argc, char **argv) { mpi_loop(); } +static void mpi_set_node_grid_local(Utils::Vector3i const &node_grid) { + ::node_grid = node_grid; + grid_changed_n_nodes(); + on_node_grid_change(); +} + +REGISTER_CALLBACK(mpi_set_node_grid_local) + void EspressoSystemStandAlone::set_box_l(Utils::Vector3d const &box_l) const { if (!head_node) return; @@ -60,7 +70,7 @@ void EspressoSystemStandAlone::set_node_grid( Utils::Vector3i const &node_grid) const { if (!head_node) return; - mpi_set_node_grid(node_grid); + mpi_call_all(mpi_set_node_grid_local, node_grid); } void EspressoSystemStandAlone::set_time_step(double time_step) const { diff --git a/src/core/event.cpp b/src/core/event.cpp index 986c3fcad50..66ee97814bb 100644 --- a/src/core/event.cpp +++ b/src/core/event.cpp @@ -320,10 +320,8 @@ void on_periodicity_change() { #ifdef STOKESIAN_DYNAMICS if (integ_switch == INTEG_METHOD_SD) { if (box_geo.periodic(0) || box_geo.periodic(1) || box_geo.periodic(2)) - runtimeErrorMsg() << "Illegal box periodicity for Stokesian Dynamics: " - << box_geo.periodic(0) << " " << box_geo.periodic(1) - << " " << box_geo.periodic(2) << "\n" - << " Required: 0 0 0\n"; + runtimeErrorMsg() << "Stokesian Dynamics requires periodicity " + << "(False, False, False)\n"; } #endif on_skin_change(); diff --git a/src/core/grid.cpp b/src/core/grid.cpp index f4e7e8e1e5f..2cdf51742b6 100644 --- a/src/core/grid.cpp +++ b/src/core/grid.cpp @@ -106,66 +106,13 @@ void grid_changed_n_nodes() { grid_changed_box_l(box_geo); } -void rescale_boxl(int dir, double d_new) { - double scale = (dir - 3) ? d_new * box_geo.length_inv()[dir] - : d_new * box_geo.length_inv()[0]; - - /* If shrinking, rescale the particles first. */ - if (scale <= 1.) { - mpi_rescale_particles(dir, scale); - } - - if (dir < 3) { - auto box_l = box_geo.length(); - box_l[dir] = d_new; - mpi_set_box_length(box_l); - } else { - mpi_set_box_length({d_new, d_new, d_new}); - } - - if (scale > 1.) { - mpi_rescale_particles(dir, scale); - } -} - -void mpi_set_box_length_local(const Utils::Vector3d &length) { - box_geo.set_length(length); +static void mpi_set_box_length_local(Utils::Vector3d const &box_l) { + box_geo.set_length(box_l); on_boxl_change(); } REGISTER_CALLBACK(mpi_set_box_length_local) -void mpi_set_box_length(const Utils::Vector3d &length) { - if (boost::algorithm::any_of(length, - [](double value) { return value <= 0; })) { - throw std::domain_error("Box length must be >0"); - } - - mpi_call_all(mpi_set_box_length_local, length); -} - -void mpi_set_periodicity_local(bool x, bool y, bool z) { - box_geo.set_periodic(0, x); - box_geo.set_periodic(1, y); - box_geo.set_periodic(2, z); - - on_periodicity_change(); -} - -REGISTER_CALLBACK(mpi_set_periodicity_local) - -void mpi_set_periodicity(bool x, bool y, bool z) { - mpi_call_all(mpi_set_periodicity_local, x, y, z); -} - -void mpi_set_node_grid_local(const Utils::Vector3i &node_grid) { - ::node_grid = node_grid; - grid_changed_n_nodes(); - on_node_grid_change(); -} - -REGISTER_CALLBACK(mpi_set_node_grid_local) - -void mpi_set_node_grid(const Utils::Vector3i &node_grid) { - mpi_call_all(mpi_set_node_grid_local, node_grid); +void mpi_set_box_length(Utils::Vector3d const &box_l) { + mpi_call_all(mpi_set_box_length_local, box_l); } diff --git a/src/core/grid.hpp b/src/core/grid.hpp index 20f27fb972d..2a865f313c1 100644 --- a/src/core/grid.hpp +++ b/src/core/grid.hpp @@ -72,11 +72,6 @@ void grid_changed_n_nodes(); void grid_changed_box_l(const BoxGeometry &box); -/** @brief Rescale box in dimension @p dir to the new value @p d_new and - * rescale the particles accordingly. - */ -void rescale_boxl(int dir, double d_new); - /** @brief Calculate image box shift vector. * @param image_box image box offset * @param box box parameters (side lengths) @@ -111,17 +106,6 @@ LocalBox regular_decomposition(const BoxGeometry &box, Utils::Vector3i const &node_pos, Utils::Vector3i const &node_grid); -/** @brief Set and broadcast the box length. - * @param length new box length - */ -void mpi_set_box_length(const Utils::Vector3d &length); - -/** @brief Set and broadcast the periodicity. - * @param x periodicity in x direction - * @param y periodicity in y direction - * @param z periodicity in z direction - */ -void mpi_set_periodicity(bool x, bool y, bool z); +void mpi_set_box_length(Utils::Vector3d const &box_l); -void mpi_set_node_grid(const Utils::Vector3i &node_grid); #endif diff --git a/src/core/integrate.cpp b/src/core/integrate.cpp index 945ef986eb5..8c44353b3c0 100644 --- a/src/core/integrate.cpp +++ b/src/core/integrate.cpp @@ -512,7 +512,8 @@ void integrate_set_bd() { mpi_set_integ_switch(INTEG_METHOD_BD); } void integrate_set_sd() { if (box_geo.periodic(0) || box_geo.periodic(1) || box_geo.periodic(2)) { - throw std::runtime_error("Stokesian Dynamics requires periodicity 0 0 0"); + throw std::runtime_error( + "Stokesian Dynamics requires periodicity (False, False, False)"); } mpi_set_integ_switch(INTEG_METHOD_SD); } diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index 3784071a81e..cb3025f8785 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -81,7 +81,7 @@ static void mpi_realloc_ia_params_local(int new_size) { REGISTER_CALLBACK(mpi_realloc_ia_params_local) /** Increase the size of the @ref nonbonded_ia_params vector. */ -inline void mpi_realloc_ia_params(int new_size) { +static void mpi_realloc_ia_params(int new_size) { mpi_call_all(mpi_realloc_ia_params_local, new_size); } @@ -92,7 +92,7 @@ static void mpi_bcast_all_ia_params_local() { REGISTER_CALLBACK(mpi_bcast_all_ia_params_local) /** Broadcast @ref nonbonded_ia_params to all nodes. */ -inline void mpi_bcast_all_ia_params() { +static void mpi_bcast_all_ia_params() { mpi_call_all(mpi_bcast_all_ia_params_local); } @@ -233,15 +233,9 @@ void make_particle_type_exist_local(int type) { mpi_realloc_ia_params_local(type + 1); } -void mpi_set_min_global_cut_local(double min_global_cut) { +void set_min_global_cut(double min_global_cut) { ::min_global_cut = min_global_cut; on_skin_change(); } -REGISTER_CALLBACK(mpi_set_min_global_cut_local) - -void mpi_set_min_global_cut(double min_global_cut) { - mpi_call_all(mpi_set_min_global_cut_local, min_global_cut); -} - -double get_min_global_cut() { return min_global_cut; } +double get_min_global_cut() { return ::min_global_cut; } diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index 112b99ba49e..eb415c59fc3 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -337,10 +337,7 @@ inline bool checkIfInteraction(IA_parameters const &data) { return data.max_cut != INACTIVE_CUTOFF; } -/** @brief Set and broadcast the min_global_cut - * @param min_global_cut minimum global cutoff - */ -void mpi_set_min_global_cut(double min_global_cut); +void set_min_global_cut(double min_global_cut); double get_min_global_cut(); #endif diff --git a/src/core/rotate_system.cpp b/src/core/rotate_system.cpp index 895c5497361..67e5d1a017e 100644 --- a/src/core/rotate_system.cpp +++ b/src/core/rotate_system.cpp @@ -16,6 +16,9 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + +#include "rotate_system.hpp" + #include "Particle.hpp" #include "ParticleRange.hpp" #include "cells.hpp" @@ -34,7 +37,7 @@ #include #include -static void mpi_rotate_system_local(double phi, double theta, double alpha) { +void rotate_system(double phi, double theta, double alpha) { auto const particles = cell_structure.local_particles(); // Calculate center of mass @@ -55,9 +58,9 @@ static void mpi_rotate_system_local(double phi, double theta, double alpha) { // Rotation axis in Cartesian coordinates Utils::Vector3d axis; - axis[0] = sin(theta) * cos(phi); - axis[1] = sin(theta) * sin(phi); - axis[2] = cos(theta); + axis[0] = std::sin(theta) * std::cos(phi); + axis[1] = std::sin(theta) * std::sin(phi); + axis[2] = std::cos(theta); // Rotate particle coordinates for (auto &p : particles) { @@ -72,9 +75,3 @@ static void mpi_rotate_system_local(double phi, double theta, double alpha) { on_particle_change(); update_dependent_particles(); } - -REGISTER_CALLBACK(mpi_rotate_system_local) - -void mpi_rotate_system(double phi, double theta, double alpha) { - mpi_call_all(mpi_rotate_system_local, phi, theta, alpha); -} diff --git a/src/core/rotate_system.hpp b/src/core/rotate_system.hpp index a5096fc53ea..d126342bf77 100644 --- a/src/core/rotate_system.hpp +++ b/src/core/rotate_system.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 ROTATE_SYSTEM_HPP -#define ROTATE_SYSTEM_HPP +#ifndef ESPRESSO_SRC_CORE_ROTATE_SYSTEM_HPP +#define ESPRESSO_SRC_CORE_ROTATE_SYSTEM_HPP /** Rotate all particle coordinates around an axis given by phi,theta through - * the center of mass by an angle alpha + * the center of mass by an angle alpha. */ -void mpi_rotate_system(double phi, double theta, double alpha); +void rotate_system(double phi, double theta, double alpha); #endif diff --git a/src/python/espressomd/system.pxd b/src/python/espressomd/system.pxd deleted file mode 100644 index 75f23e67890..00000000000 --- a/src/python/espressomd/system.pxd +++ /dev/null @@ -1,46 +0,0 @@ -# -# 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 . -# -include "myconfig.pxi" -from libcpp cimport bool - -from .utils cimport Vector3d - -cdef extern from "grid.hpp": - void mpi_set_box_length(Vector3d length) except + - void mpi_set_periodicity(bool x, bool y, bool z) - void rescale_boxl(int dir, double d_new) - -cdef extern from "rotate_system.hpp": - void mpi_rotate_system(double phi, double theta, double alpha) - -IF EXCLUSIONS: - cdef extern from "particle_data.hpp": - void auto_exclusions(int distance) - -cdef extern from "particle_node.hpp": - int init_type_map(int type) except + - int number_of_particles_with_type(int type) except + - -cdef extern from "object-in-fluid/oif_global_forces.hpp": - int max_oif_objects - void mpi_set_max_oif_objects(int max_oif_objects) - -cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": - double get_min_global_cut() - void mpi_set_min_global_cut(double min_global_cut) diff --git a/src/python/espressomd/system.pyx b/src/python/espressomd/system.py similarity index 51% rename from src/python/espressomd/system.pyx rename to src/python/espressomd/system.py index 2a40a19ed7e..c2156d16d88 100644 --- a/src/python/espressomd/system.pyx +++ b/src/python/espressomd/system.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -include "myconfig.pxi" import numpy as np import collections @@ -40,17 +39,14 @@ from . import thermostat from . import virtual_sites -from .__init__ import has_features, assert_features -from .grid cimport box_geo -from .utils cimport Vector3d -from . cimport utils +from .code_features import has_features, assert_features from . import utils +from .script_interface import script_interface_register, ScriptInterfaceHelper -_system_created = False - -cdef class _BoxGeometry: +@script_interface_register +class _Globals(ScriptInterfaceHelper): """ Wrapper class required for technical reasons only. @@ -60,108 +56,163 @@ System class, and adding that object as the first element of the ordered dict that is used during serialization. When the System class is reloaded, the ordered dict is walked through and objects are deserialized in the same - order. Since many objects depend on the box length, the `_BoxGeometry` has + order. Since many objects depend on the box length, the `_Globals` has to be deserialized first. This guarantees the box geometry is already set in the core before e.g. particles and bonds are deserialized. """ + _so_name = "System::Globals" + _so_creation_policy = "GLOBAL" - def __getstate__(self): - return {'box_l': self.box_l, - 'periodicity': self.periodicity, - 'min_global_cut': self.min_global_cut} + def __setattr__(self, attr, value): + if attr == "periodicity": + utils.check_type_or_throw_except( + value, 3, bool, "Attribute 'periodicity' must be a list of 3 bools") + if attr == "box_l": + utils.check_type_or_throw_except( + value, 3, float, "Attribute 'box_l' must be a list of 3 floats") + super().__setattr__(attr, value) + utils.handle_errors(f"while assigning system parameter '{attr}'") - def __setstate__(self, params): - self.box_l = params['box_l'] - self.periodicity = params['periodicity'] - self.min_global_cut = params['min_global_cut'] - property box_l: - def __set__(self, box_l): - utils.check_type_or_throw_except( - box_l, 3, float, "box_l must be an array_like of 3 floats") - mpi_set_box_length(utils.make_Vector3d(box_l)) - utils.handle_errors("Exception while updating the box length") +@script_interface_register +class System(ScriptInterfaceHelper): + """ + The ESPResSo system class. - def __get__(self): - return utils.make_array_locked(box_geo.length()) + Attributes + ---------- + box_l: (3,) array_like of :obj:`float` + Dimensions of the simulation box - property periodicity: - def __set__(self, periodic): - utils.check_type_or_throw_except( - periodic, 3, type(True), "periodicity must be an array_like of 3 bools") - mpi_set_periodicity(periodic[0], periodic[1], periodic[2]) - utils.handle_errors("Exception while assigning system periodicity") + periodicity: (3,) array_like of :obj:`bool` + System periodicity in ``[x, y, z]``, ``False`` for no periodicity + in this direction, ``True`` for periodicity + + force_cap: :obj:`float` + If > 0, the magnitude of the force on the particles + are capped to this value. - def __get__(self): - periodicity = np.empty(3, dtype=type(True)) - for i in range(3): - periodicity[i] = box_geo.periodic(i) - return utils.array_locked(periodicity) + virtual_sites: + Set the virtual site implementation. + + Requires feature ``VIRTUAL_SITES``. + + max_cut_bonded: :obj:`float` + Maximal cutoff for bonded interactions. + + max_cut_nonbonded: :obj:`float` + Maximal cutoff for non-bonded interactions. + + min_global_cut: :obj:`float` + Minimal interaction cutoff. + + time: :obj:`float` + Total simulation time. + + time_step: :obj:`float` + MD time step. + + actors: :class:`espressomd.actors.Actors` + analysis: :class:`espressomd.analyze.Analysis` + auto_update_accumulators: :class:`espressomd.accumulators.AutoUpdateAccumulators` + bond_breakage: :class:`espressomd.bond_breakage.BreakageSpecs` + bonded_inter: :class:`espressomd.interactions.BondedInteractions` + cell_system: :class:`espressomd.cell_system.CellSystem` + collision_detection: :class:`espressomd.collision_detection.CollisionDetection` + comfixed: :class:`espressomd.comfixed.ComFixed` + constraints: :class:`espressomd.constraints.Constraints` + cuda_init_handle: :class:`espressomd.cuda_init.CudaInitHandle` + ekboundaries: :class:`espressomd.ekboundaries.EKBoundaries` + galilei: :class:`espressomd.galilei.GalileiTransform` + integrator: :class:`espressomd.integrate.IntegratorHandle` + lbboundaries: :class:`espressomd.lbboundaries.LBBoundaries` + lees_edwards: :class:`espressomd.lees_edwards.LeesEdwards` + non_bonded_inter: :class:`espressomd.interactions.NonBondedInteractions` + part: :class:`espressomd.particle_data.ParticleList` + thermostat: :class:`espressomd.thermostat.Thermostat` + + Methods + ------- + setup_type_map() + Requires setting the volume using :meth:`set_volume`. + For using ESPResSo conveniently for simulations in the grand canonical + ensemble, or other purposes, when particles of certain types are created + and deleted frequently. Particle ids can be stored in lists for each + individual type and so random ids of particles of a certain type can be + drawn. If you want ESPResSo to keep track of particle ids of a certain type + you have to initialize the method by calling the setup function. After that + ESPResSo will keep track of particle ids of that type. - property min_global_cut: - def __set__(self, min_global_cut): - mpi_set_min_global_cut(min_global_cut) + Parameters + ---------- + type_list : array_like of :obj:`int` + Types to track. - def __get__(self): - return get_min_global_cut() + number_of_particles(): + Count the number of particles of a given type. + Parameters + ---------- + type : :obj:`int` (:attr:`~espressomd.particle_data.ParticleHandle.type`) + Particle type to count the number for. -cdef class System: - """The ESPResSo system class. + Returns + ------- + :obj:`int` + The number of particles which have the given type. - .. note:: every attribute has to be declared at the class level. - This means that methods cannot define an attribute by using - ``self.new_attr = somevalue`` without declaring it inside this - indentation level, either as method, property or reference. + Raises + ------ + RuntimeError + If the particle ``type`` is not currently tracked by the system. + To select which particle types are tracked, call :meth:`setup_type_map`. + + rotate_system(): + Rotate the particles in the system about the center of mass. + + If ``ROTATION`` is activated, the internal rotation degrees of + freedom are rotated accordingly. + + Parameters + ---------- + phi : :obj:`float` + Angle between the z-axis and the rotation axis. + theta : :obj:`float` + Rotation of the axis around the y-axis. + alpha : :obj:`float` + How much to rotate """ + _so_name = "System::System" + _so_creation_policy = "GLOBAL" + _so_bind_methods = ( + "setup_type_map", + "number_of_particles", + "rotate_system") + + def __getattr__(self, attr): + if attr in self.__dict__.get("_globals_parameters", []): + return self._globals.__getattr__(attr) + else: + return super().__getattr__(attr) - cdef public: - _box_geo - part - """:class:`espressomd.particle_data.ParticleList`""" - non_bonded_inter - """:class:`espressomd.interactions.NonBondedInteractions`""" - bonded_inter - """:class:`espressomd.interactions.BondedInteractions`""" - cell_system - """:class:`espressomd.cell_system.CellSystem`""" - thermostat - """:class:`espressomd.thermostat.Thermostat`""" - actors - """:class:`espressomd.actors.Actors`""" - analysis - """:class:`espressomd.analyze.Analysis`""" - bond_breakage - """:class:`espressomd.bond_breakage.BreakageSpecs`""" - galilei - """:class:`espressomd.galilei.GalileiTransform`""" - integrator - """:class:`espressomd.integrate.IntegratorHandle`""" - auto_update_accumulators - """:class:`espressomd.accumulators.AutoUpdateAccumulators`""" - constraints - """:class:`espressomd.constraints.Constraints`""" - lbboundaries - """: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 - """:class:`espressomd.cuda_init.CudaInitHandle`""" - comfixed - """:class:`espressomd.comfixed.ComFixed`""" - _active_virtual_sites_handle + def __setattr__(self, attr, value): + if attr in self.__dict__.get("_globals_parameters", []): + self._globals.__setattr__(attr, value) + else: + super().__setattr__(attr, value) def __init__(self, **kwargs): - if _system_created: + if "sip" in kwargs: + super().__init__(**kwargs) + return + super().__init__() + + if self.call_method("is_system_created"): raise RuntimeError( "You can only have one instance of the system class at a time.") - if 'box_l' not in kwargs: + if "box_l" not in kwargs: raise ValueError("Required argument 'box_l' not provided.") setable_properties = ["box_l", "min_global_cut", "periodicity", "time", @@ -169,9 +220,10 @@ def __init__(self, **kwargs): if has_features("VIRTUAL_SITES"): setable_properties.append("_active_virtual_sites_handle") - self._box_geo = _BoxGeometry() + self._globals = _Globals() + self._globals_parameters = self._globals._valid_parameters() self.integrator = integrate.IntegratorHandle() - System.__setattr__(self, "box_l", kwargs.pop("box_l")) + self.box_l = kwargs.pop("box_l") for arg in kwargs: if arg not in setable_properties: raise ValueError( @@ -203,19 +255,29 @@ def __init__(self, **kwargs): implementation=virtual_sites.VirtualSitesOff()) # lock class - global _system_created - _system_created = True + self.call_method("lock_system_creation") + + def __reduce__(self): + so_callback, so_callback_args = super().__reduce__() + return (System._restore_object, + (so_callback, so_callback_args, self.__getstate__())) + + @classmethod + def _restore_object(cls, so_callback, so_callback_args, state): + so = so_callback(*so_callback_args) + so.__setstate__(state) + so._globals_parameters = so._globals._valid_parameters() + return so - # __getstate__ and __setstate__ define the pickle interaction def __getstate__(self): - checkpointable_properties = ["_box_geo", "integrator"] + checkpointable_properties = ["_globals", "integrator"] if has_features("VIRTUAL_SITES"): checkpointable_properties.append("_active_virtual_sites_handle") checkpointable_properties += [ "non_bonded_inter", "bonded_inter", "cell_system", "lees_edwards", "part", "actors", "analysis", "auto_update_accumulators", "comfixed", "constraints", "galilei", "thermostat", - "bond_breakage", "max_oif_objects" + "bond_breakage" ] if has_features("LB_BOUNDARIES") or has_features("LB_BOUNDARIES_GPU"): checkpointable_properties.append("lbboundaries") @@ -228,115 +290,76 @@ def __getstate__(self): return odict def __setstate__(self, params): + # note: this class is initialized twice by pickle for property_name in params.keys(): System.__setattr__(self, property_name, params[property_name]) + self.call_method("lock_system_creation") - property box_l: - """ - (3,) array_like of :obj:`float`: - Dimensions of the simulation box - - """ - - def __set__(self, value): - self._box_geo.box_l = value - - def __get__(self): - return self._box_geo.box_l - - property force_cap: - """ - :obj:`float`: - If > 0, the magnitude of the force on the particles - are capped to this value. - - """ + @property + def box_l(self): + return self._globals.box_l - def __get__(self): - return self.integrator.force_cap + @box_l.setter + def box_l(self, value): + self._globals.box_l = value - def __set__(self, cap): - self.integrator.force_cap = cap + @property + def periodicity(self): + return self._globals.periodicity - property periodicity: - """ - (3,) array_like of :obj:`bool`: - System periodicity in ``[x, y, z]``, ``False`` for no periodicity - in this direction, ``True`` for periodicity + @periodicity.setter + def periodicity(self, value): + self._globals.periodicity = value - """ + @property + def min_global_cut(self): + return self._globals.min_global_cut - def __set__(self, value): - self._box_geo.periodicity = value + @min_global_cut.setter + def min_global_cut(self, value): + self._globals.min_global_cut = value - def __get__(self): - return self._box_geo.periodicity + @property + def force_cap(self): + return self.integrator.force_cap - property time: - """ - Set the time in the simulation. + @force_cap.setter + def force_cap(self, value): + self.integrator.force_cap = value - """ + @property + def time(self): + return self.integrator.time - def __set__(self, double sim_time): - self.integrator.time = sim_time + @time.setter + def time(self, value): + self.integrator.time = value - def __get__(self): - return self.integrator.time + @property + def time_step(self): + return self.integrator.time_step - property time_step: - """ - Set the time step for the integrator. + @time_step.setter + def time_step(self, value): + self.integrator.time_step = value - """ + @property + def max_cut_nonbonded(self): + return self.cell_system.max_cut_nonbonded - def __set__(self, double time_step): - self.integrator.time_step = time_step + @property + def max_cut_bonded(self): + return self.cell_system.max_cut_bonded - def __get__(self): - return self.integrator.time_step + @property + def virtual_sites(self): + assert_features("VIRTUAL_SITES") + return self._active_virtual_sites_handle.implementation - property max_cut_nonbonded: - def __get__(self): - return self.cell_system.max_cut_nonbonded - - property max_cut_bonded: - def __get__(self): - return self.cell_system.max_cut_bonded - - property min_global_cut: - def __set__(self, value): - self._box_geo.min_global_cut = value - - def __get__(self): - return self._box_geo.min_global_cut - - property virtual_sites: - """ - Set the virtual site implementation. - - Requires feature ``VIRTUAL_SITES``. - - """ - - def __set__(self, v): - assert_features("VIRTUAL_SITES") - self._active_virtual_sites_handle.implementation = v - - def __get__(self): - assert_features("VIRTUAL_SITES") - return self._active_virtual_sites_handle.implementation - - property max_oif_objects: - """Maximum number of objects as per the object_in_fluid method. - - """ - - def __get__(self): - return max_oif_objects - - def __set__(self, v): - mpi_set_max_oif_objects(v) + @virtual_sites.setter + def virtual_sites(self, value): + assert_features("VIRTUAL_SITES") + self._active_virtual_sites_handle.implementation = value def change_volume_and_rescale_particles(self, d_new, dir="xyz"): """Change box size and rescale particle coordinates. @@ -351,26 +374,18 @@ def change_volume_and_rescale_particles(self, d_new, dir="xyz"): """ - if d_new < 0: - raise ValueError("No negative lengths") - if dir == "xyz": - rescale_boxl(3, d_new) - elif dir == "x" or dir == 0: - rescale_boxl(0, d_new) - elif dir == "y" or dir == 1: - rescale_boxl(1, d_new) - elif dir == "z" or dir == 2: - rescale_boxl(2, d_new) - else: + coord = {"x": 0, "y": 1, "z": 2, 0: 0, 1: 1, 2: 2, "xyz": 3}.get(dir) + if coord is None: raise ValueError( 'Usage: change_volume_and_rescale_particles(, [{ "x" | "y" | "z" | "xyz" }])') + self.call_method("rescale_boxl", length=d_new, coord=coord) def volume(self): - """Return box volume of the cuboid box. + """Return volume of the cuboid box. """ - return self.box_l[0] * self.box_l[1] * self.box_l[2] + return float(np.prod(self.box_l)) def distance(self, p1, p2): """Return the scalar distance between particles, between a particle @@ -378,14 +393,13 @@ def distance(self, p1, p2): Parameters ---------- - p1 : :class:`~espressomd.particle_data.ParticleHandle` or (3,) array of :obj:`float` + p1 : :class:`~espressomd.particle_data.ParticleHandle` or (3,) array_like of :obj:`float` First particle or position. - p2 : :class:`~espressomd.particle_data.ParticleHandle` or (3,) array of :obj:`float` + p2 : :class:`~espressomd.particle_data.ParticleHandle` or (3,) array_like of :obj:`float` Second particle or position. """ - res = self.distance_vec(p1, p2) - return np.linalg.norm(res) + return np.linalg.norm(self.distance_vec(p1, p2)) def distance_vec(self, p1, p2): """Return the distance vector between particles, between a particle @@ -393,29 +407,27 @@ def distance_vec(self, p1, p2): Parameters ---------- - p1 : :class:`~espressomd.particle_data.ParticleHandle` or (3,) array of :obj:`float` + p1 : :class:`~espressomd.particle_data.ParticleHandle` or (3,) array_like of :obj:`float` First particle or position. - p2 : :class:`~espressomd.particle_data.ParticleHandle` or (3,) array of :obj:`float` + p2 : :class:`~espressomd.particle_data.ParticleHandle` or (3,) array_like of :obj:`float` Second particle or position. """ - cdef Vector3d pos1 if isinstance(p1, particle_data.ParticleHandle): - pos1 = utils.make_Vector3d(p1.pos_folded) + pos1 = p1.pos_folded else: utils.check_type_or_throw_except( p1, 3, float, "p1 must be a particle or 3 floats") - pos1 = utils.make_Vector3d(p1) - cdef Vector3d pos2 + pos1 = p1 if isinstance(p2, particle_data.ParticleHandle): - pos2 = utils.make_Vector3d(p2.pos_folded) + pos2 = p2.pos_folded else: utils.check_type_or_throw_except( p2, 3, float, "p2 must be a particle or 3 floats") - pos2 = utils.make_Vector3d(p2) + pos2 = p2 - return utils.make_array_locked(box_geo.get_mi_vector(pos2, pos1)) + return self.call_method("distance_vec", pos1=pos1, pos2=pos2) def velocity_difference(self, p1, p2): """ @@ -429,36 +441,12 @@ def velocity_difference(self, p1, p2): """ - cdef Vector3d pos1 = utils.make_Vector3d(p1.pos_folded) - cdef Vector3d pos2 = utils.make_Vector3d(p2.pos_folded) - - cdef Vector3d v1 = utils.make_Vector3d(p1.v) - cdef Vector3d v2 = utils.make_Vector3d(p2.v) - cdef Vector3d vd = box_geo.velocity_difference(pos2, pos1, v2, v1) - - return utils.make_array_locked(vd) - - def rotate_system(self, **kwargs): - """Rotate the particles in the system about the center of mass. - - If ``ROTATION`` is activated, the internal rotation degrees of - freedom are rotated accordingly. - - Parameters - ---------- - phi : :obj:`float` - Angle between the z-axis and the rotation axis. - theta : :obj:`float` - Rotation of the axis around the y-axis. - alpha : :obj:`float` - How much to rotate - - """ - mpi_rotate_system(kwargs['phi'], kwargs['theta'], kwargs['alpha']) + return self.call_method("velocity_difference", pos1=p1.pos_folded, + pos2=p2.pos_folded, v1=p1.v, v2=p2.v) def auto_exclusions(self, distance): - """Automatically adds exclusions between particles - that are bonded. + """ + Add exclusions between particles that are bonded. This only considers pair bonds. @@ -467,49 +455,8 @@ def auto_exclusions(self, distance): Parameters ---------- distance : :obj:`int` - Bond distance upto which the exclusions should be added. - - """ - IF EXCLUSIONS: - auto_exclusions(distance) - ELSE: - assert_features("EXCLUSIONS") - - def setup_type_map(self, type_list=None): - """ - For using ESPResSo conveniently for simulations in the grand canonical - ensemble, or other purposes, when particles of certain types are created - and deleted frequently. Particle ids can be stored in lists for each - individual type and so random ids of particles of a certain type can be - drawn. If you want ESPResSo to keep track of particle ids of a certain type - you have to initialize the method by calling the setup function. After that - ESPResSo will keep track of particle ids of that type. - - """ - if not hasattr(type_list, "__iter__"): - raise ValueError("type_list has to be iterable.") - - for current_type in type_list: - init_type_map(current_type) - - def number_of_particles(self, type=None): - """ - Parameters - ---------- - type : :obj:`int` (:attr:`~espressomd.particle_data.ParticleHandle.type`) - Particle type to count the number for. - - Returns - ------- - :obj:`int` - The number of particles which have the given type. - - Raises - ------ - RuntimeError - If the particle ``type`` is not currently tracked by the system. - To select which particle types are tracked, call :meth:`setup_type_map`. + Bond distance up to which the exclusions should be added. """ - utils.check_type_or_throw_except(type, 1, int, "type must be 1 int") - return number_of_particles_with_type(type) + assert_features("EXCLUSIONS") + self.call_method("auto_exclusions", distance=distance) diff --git a/src/script_interface/CMakeLists.txt b/src/script_interface/CMakeLists.txt index 9c9a19ce1ab..5f8666d136f 100644 --- a/src/script_interface/CMakeLists.txt +++ b/src/script_interface/CMakeLists.txt @@ -42,6 +42,7 @@ add_subdirectory(pair_criteria) add_subdirectory(reaction_methods) add_subdirectory(scafacos) add_subdirectory(shapes) +add_subdirectory(system) add_subdirectory(virtual_sites) install(TARGETS Espresso_script_interface diff --git a/src/script_interface/initialize.cpp b/src/script_interface/initialize.cpp index afa76e1df45..9e58b0fc1d7 100644 --- a/src/script_interface/initialize.cpp +++ b/src/script_interface/initialize.cpp @@ -41,6 +41,7 @@ #include "pair_criteria/initialize.hpp" #include "reaction_methods/initialize.hpp" #include "shapes/initialize.hpp" +#include "system/initialize.hpp" #include "virtual_sites/initialize.hpp" namespace ScriptInterface { @@ -61,6 +62,7 @@ void initialize(Utils::Factory *f) { Observables::initialize(f); PairCriteria::initialize(f); Shapes::initialize(f); + System::initialize(f); VirtualSites::initialize(f); ReactionMethods::initialize(f); #ifdef H5MD diff --git a/src/script_interface/system/CMakeLists.txt b/src/script_interface/system/CMakeLists.txt new file mode 100644 index 00000000000..8506546385f --- /dev/null +++ b/src/script_interface/system/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources( + Espresso_script_interface + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Globals.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/System.cpp) diff --git a/src/script_interface/system/Globals.cpp b/src/script_interface/system/Globals.cpp new file mode 100644 index 00000000000..c1bf5f186c2 --- /dev/null +++ b/src/script_interface/system/Globals.cpp @@ -0,0 +1,81 @@ +/* + * 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 . + */ + +#include "Globals.hpp" + +#include "core/event.hpp" +#include "core/grid.hpp" +#include "core/nonbonded_interactions/nonbonded_interaction_data.hpp" + +#include + +#include +#include +#include + +namespace ScriptInterface { +namespace System { + +static Utils::Vector3b get_periodicity() { + return {::box_geo.periodic(0), ::box_geo.periodic(1), ::box_geo.periodic(2)}; +} + +static void set_periodicity(Utils::Vector3b const &value) { + for (int i = 0; i < 3; ++i) { + ::box_geo.set_periodic(i, value[i]); + } + on_periodicity_change(); +} + +Globals::Globals() { + add_parameters({ + {"box_l", + [this](Variant const &v) { + context()->parallel_try_catch([&]() { + auto const new_value = get_value(v); + if (not(new_value > Utils::Vector3d::broadcast(0.))) { + throw std::domain_error("Attribute 'box_l' must be > 0"); + } + box_geo.set_length(new_value); + on_boxl_change(); + }); + }, + []() { return ::box_geo.length(); }}, + {"min_global_cut", + [this](Variant const &v) { + context()->parallel_try_catch([&]() { + auto const new_value = get_value(v); + if (new_value < 0. and new_value != INACTIVE_CUTOFF) { + throw std::domain_error("Attribute 'min_global_cut' must be >= 0"); + } + set_min_global_cut(new_value); + }); + }, + []() { return ::get_min_global_cut(); }}, + {"periodicity", + [this](Variant const &v) { + context()->parallel_try_catch( + [&]() { set_periodicity(get_value(v)); }); + }, + []() { return get_periodicity(); }}, + }); +} + +} // namespace System +} // namespace ScriptInterface diff --git a/src/script_interface/system/Globals.hpp b/src/script_interface/system/Globals.hpp new file mode 100644 index 00000000000..0d31911bd10 --- /dev/null +++ b/src/script_interface/system/Globals.hpp @@ -0,0 +1,62 @@ +/* + * 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 . + */ + +#ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_SYSTEM_GLOBALS_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_SYSTEM_GLOBALS_HPP + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include +#include +#include + +namespace ScriptInterface { +namespace System { + +class Globals : public AutoParameters { +public: + Globals(); + +private: + void do_construct(VariantMap const ¶ms) override { + /* When reloading the system state from a checkpoint file, + * the order of global variables instantiation matters. + * The @c box_l must be set before any other global variable. + * All these globals re-initialize the cell system, and we + * cannot use the default-constructed @c box_geo when e.g. + * long-range interactions exist in the system, otherwise + * runtime errors about the local geometry being smaller + * than the interaction range would be raised. + */ + if (not params.empty()) { + auto const keys = + std::vector{"box_l", "periodicity", "min_global_cut"}; + assert(params.size() == keys.size()); + for (auto const &key : keys) { + do_set_parameter(key, params.at(key)); + } + } + } +}; + +} // namespace System +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/system/System.cpp b/src/script_interface/system/System.cpp new file mode 100644 index 00000000000..aa11cbe7bcc --- /dev/null +++ b/src/script_interface/system/System.cpp @@ -0,0 +1,127 @@ +/* + * 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 . + */ + +#include "System.hpp" + +#include "config.hpp" + +#include "core/grid.hpp" +#include "core/object-in-fluid/oif_global_forces.hpp" +#include "core/particle_data.hpp" +#include "core/particle_node.hpp" +#include "core/rotate_system.hpp" + +#include + +#include +#include + +namespace ScriptInterface { +namespace System { + +static bool system_created = false; + +System::System() { + add_parameters({ + {"max_oif_objects", ::max_oif_objects}, + }); +} + +Variant System::do_call_method(std::string const &name, + VariantMap const ¶meters) { + if (name == "is_system_created") { + return system_created; + } + if (name == "lock_system_creation") { + system_created = true; + return {}; + } + if (name == "rescale_boxl") { + if (context()->is_head_node()) { + auto const coord = get_value(parameters, "coord"); + auto const length = get_value(parameters, "length"); + auto const scale = (coord == 3) ? length * ::box_geo.length_inv()[0] + : length * ::box_geo.length_inv()[coord]; + if (length <= 0.) { + throw std::domain_error("Parameter 'd_new' be > 0"); + } + auto new_value = Utils::Vector3d{}; + if (coord == 3) { + new_value = Utils::Vector3d::broadcast(length); + } else { + new_value = ::box_geo.length(); + new_value[coord] = length; + } + // when shrinking, rescale the particles first + if (scale <= 1.) { + mpi_rescale_particles(coord, scale); + } + mpi_set_box_length(new_value); + if (scale > 1.) { + mpi_rescale_particles(coord, scale); + } + } + return {}; + } +#ifdef EXCLUSIONS + if (name == "auto_exclusions") { + auto const distance = get_value(parameters, "distance"); + auto_exclusions(distance); + return {}; + } +#endif + if (name == "setup_type_map") { + if (context()->is_head_node()) { + auto const types = get_value>(parameters, "type_list"); + for (auto const type : types) { + init_type_map(type); + } + } + return {}; + } + if (name == "number_of_particles") { + if (context()->is_head_node()) { + auto const type = get_value(parameters, "type"); + return number_of_particles_with_type(type); + } + return {}; + } + if (name == "velocity_difference") { + auto const pos1 = get_value(parameters, "pos1"); + auto const pos2 = get_value(parameters, "pos2"); + auto const v1 = get_value(parameters, "v1"); + auto const v2 = get_value(parameters, "v2"); + return ::box_geo.velocity_difference(pos2, pos1, v2, v1); + } + if (name == "distance_vec") { + auto const pos1 = get_value(parameters, "pos1"); + auto const pos2 = get_value(parameters, "pos2"); + return ::box_geo.get_mi_vector(pos2, pos1); + } + if (name == "rotate_system") { + rotate_system(get_value(parameters, "phi"), + get_value(parameters, "theta"), + get_value(parameters, "alpha")); + return {}; + } + return {}; +} + +} // namespace System +} // namespace ScriptInterface diff --git a/src/script_interface/system/System.hpp b/src/script_interface/system/System.hpp new file mode 100644 index 00000000000..13ab4a44fe4 --- /dev/null +++ b/src/script_interface/system/System.hpp @@ -0,0 +1,41 @@ +/* + * 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 . + */ + +#ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_SYSTEM_SYSTEM_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_SYSTEM_SYSTEM_HPP + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include + +namespace ScriptInterface { +namespace System { + +class System : public AutoParameters { +public: + System(); + Variant do_call_method(std::string const &name, + VariantMap const ¶meters) override; +}; + +} // namespace System +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/system/initialize.cpp b/src/script_interface/system/initialize.cpp new file mode 100644 index 00000000000..5959bdd0e76 --- /dev/null +++ b/src/script_interface/system/initialize.cpp @@ -0,0 +1,34 @@ +/* + * 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 "Globals.hpp" +#include "System.hpp" + +namespace ScriptInterface { +namespace System { + +void initialize(Utils::Factory *om) { + om->register_new("System::Globals"); + om->register_new("System::System"); +} + +} // namespace System +} // namespace ScriptInterface diff --git a/src/script_interface/system/initialize.hpp b/src/script_interface/system/initialize.hpp new file mode 100644 index 00000000000..8a1f574ea5a --- /dev/null +++ b/src/script_interface/system/initialize.hpp @@ -0,0 +1,35 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_SYSTEM_INITIALIZE_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_SYSTEM_INITIALIZE_HPP + +#include "script_interface/ObjectHandle.hpp" + +#include + +namespace ScriptInterface { +namespace System { + +void initialize(Utils::Factory *om); + +} // namespace System +} // namespace ScriptInterface + +#endif diff --git a/testsuite/python/box_geometry.py b/testsuite/python/box_geometry.py index c709ddb247b..c27c4880d03 100644 --- a/testsuite/python/box_geometry.py +++ b/testsuite/python/box_geometry.py @@ -30,13 +30,13 @@ def test_box_length_interface(self): local_box_l = self.box_l.copy() local_box_l[i] = -1. - with self.assertRaisesRegex(Exception, 'Box length must be >0'): + with self.assertRaisesRegex(ValueError, "Attribute 'box_l' must be > 0"): self.system.box_l = local_box_l # the box length should not be updated np.testing.assert_equal(self.box_l, np.copy(self.system.box_l)) - with self.assertRaisesRegex(ValueError, 'box_l must be an array_like of 3 floats'): + with self.assertRaisesRegex(ValueError, "Attribute 'box_l' must be a list of 3 floats"): self.system.box_l = self.box_l[:2] def test_periodicity(self): @@ -50,13 +50,21 @@ def test_periodicity(self): default_periodicity = (True, True, True) self.system.periodicity = default_periodicity - with self.assertRaisesRegex(ValueError, 'periodicity must be an array_like of 3 bools'): + with self.assertRaisesRegex(ValueError, "Attribute 'periodicity' must be a list of 3 bools"): self.system.periodicity = (True, True) # the periodicity should not be updated np.testing.assert_equal(np.copy(self.system.periodicity), default_periodicity) + def test_min_global_cut(self): + self.system.min_global_cut = 0.01 + with self.assertRaisesRegex(ValueError, "Attribute 'min_global_cut' must be >= 0"): + self.system.min_global_cut = -2. + + # the min global cutoff should not be updated + np.testing.assert_equal(self.system.min_global_cut, 0.01) + if __name__ == "__main__": ut.main() diff --git a/testsuite/python/coulomb_interface.py b/testsuite/python/coulomb_interface.py index 72ab62729b7..eff174e6e08 100644 --- a/testsuite/python/coulomb_interface.py +++ b/testsuite/python/coulomb_interface.py @@ -220,7 +220,7 @@ def test_elc_p3m_exceptions(self): self.assertEqual( list(self.system.cell_system.node_grid), list(self.original_node_grid)) - with self.assertRaisesRegex(Exception, "Exception while updating the box length: ERROR: ELC gap size .+ larger than box length in z-direction"): + with self.assertRaisesRegex(Exception, "while assigning system parameter 'box_l': ERROR: ELC gap size .+ larger than box length in z-direction"): self.system.box_l = [10., 10., 2.5] self.system.box_l = [10., 10., 10.] self.system.actors.clear() diff --git a/testsuite/python/rescale.py b/testsuite/python/rescale.py index e271ba181e2..f94c66e07d7 100644 --- a/testsuite/python/rescale.py +++ b/testsuite/python/rescale.py @@ -80,6 +80,14 @@ def test_y(self): def test_z(self): self.dir_test(2) + def test_exceptions(self): + with self.assertRaisesRegex(ValueError, "Parameter 'd_new' be > 0"): + self.system.change_volume_and_rescale_particles(d_new=0.) + with self.assertRaisesRegex(ValueError, "Parameter 'd_new' be > 0"): + self.system.change_volume_and_rescale_particles(d_new=-1.) + with self.assertRaisesRegex(ValueError, "Usage: change_volume_and_rescale_particles"): + self.system.change_volume_and_rescale_particles(d_new=1., dir=5) + if __name__ == "__main__": ut.main() diff --git a/testsuite/python/save_checkpoint.py b/testsuite/python/save_checkpoint.py index 591d4e2d072..9a01aca1476 100644 --- a/testsuite/python/save_checkpoint.py +++ b/testsuite/python/save_checkpoint.py @@ -47,7 +47,7 @@ # use a box with 3 different dimensions, unless DipolarP3M is used system = espressomd.System(box_l=[12.0, 14.0, 16.0]) if 'DP3M' in modes: - system.box_l = 3 * [np.max(system.box_l)] + system.box_l = 3 * [float(np.max(system.box_l))] system.cell_system.skin = 0.1 system.time_step = 0.01 system.time = 1.5 diff --git a/testsuite/python/stokesian_thermostat.py b/testsuite/python/stokesian_thermostat.py index ca3113a704a..c70ab92d8b4 100644 --- a/testsuite/python/stokesian_thermostat.py +++ b/testsuite/python/stokesian_thermostat.py @@ -102,17 +102,17 @@ def reset_particle(): def test_integrator_exceptions(self): # invalid parameters should throw exceptions - with self.assertRaises(RuntimeError): + with self.assertRaisesRegex(RuntimeError, "Particle radius for type 0 has an invalid value"): self.system.integrator.set_stokesian_dynamics( viscosity=1.0, radii={0: -1}) - with self.assertRaises(RuntimeError): + with self.assertRaisesRegex(RuntimeError, "Viscosity has an invalid value"): self.system.integrator.set_stokesian_dynamics( viscosity=-1, radii={0: 1.0}) # invalid PBC should throw exceptions self.system.integrator.set_vv() self.system.periodicity = [False, False, True] - with self.assertRaises(RuntimeError): + with self.assertRaisesRegex(RuntimeError, r"Stokesian Dynamics requires periodicity \(False, False, False\)"): self.system.integrator.set_stokesian_dynamics( viscosity=1.0, radii={0: 1.0}) @@ -120,7 +120,7 @@ def test_integrator_exceptions(self): self.system.integrator.set_stokesian_dynamics( viscosity=1.0, radii={0: 1.0}) - with self.assertRaises(Exception): + with self.assertRaisesRegex(Exception, r"Stokesian Dynamics requires periodicity \(False, False, False\)"): self.system.periodicity = [False, True, False] From b82a6953cd38754263c2ef47d6bebfd74bd0a071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 20 Jul 2022 13:47:44 +0200 Subject: [PATCH 08/85] script_interface: Rewrite ObservableStat --- src/python/espressomd/analyze.pxd | 235 ------------------ .../espressomd/{analyze.pyx => analyze.py} | 79 +++++- src/python/espressomd/interactions.pxd | 3 - src/python/espressomd/particle_data.pxd | 3 + src/python/espressomd/particle_data.pyx | 1 - src/script_interface/analysis/CMakeLists.txt | 6 +- .../analysis/ObservableStat.cpp | 154 ++++++++++++ .../analysis/ObservableStat.hpp | 39 +++ src/script_interface/analysis/initialize.cpp | 2 + src/script_interface/interactions/bonded.hpp | 5 - 10 files changed, 270 insertions(+), 257 deletions(-) delete mode 100644 src/python/espressomd/analyze.pxd rename src/python/espressomd/{analyze.pyx => analyze.py} (89%) create mode 100644 src/script_interface/analysis/ObservableStat.cpp create mode 100644 src/script_interface/analysis/ObservableStat.hpp diff --git a/src/python/espressomd/analyze.pxd b/src/python/espressomd/analyze.pxd deleted file mode 100644 index 7cc9e290a05..00000000000 --- a/src/python/espressomd/analyze.pxd +++ /dev/null @@ -1,235 +0,0 @@ -# -# 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 . -# - -cimport numpy as np -import numpy as np -from .utils cimport Span -from libcpp cimport bool as cbool -from libcpp.memory cimport shared_ptr -from .interactions cimport bonded_ia_params_zero_based_type -from .interactions cimport enum_bonded_interaction -from .interactions cimport bonded_ia_params_next_key -include "myconfig.pxi" - -cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": - int max_seen_particle_type - -cdef extern from "Observable_stat.hpp": - cdef cppclass Observable_stat: - Span[double] kinetic - Span[double] coulomb - Span[double] dipolar - Span[double] virtual_sites - Span[double] external_fields - double accumulate(...) - double accumulate2 "accumulate"(double acc, size_t column) - double accumulate1 "accumulate"(double acc) - Span[double] bonded_contribution(int bond_id) - Span[double] non_bonded_intra_contribution(int type1, int type2) - Span[double] non_bonded_inter_contribution(int type1, int type2) - size_t get_chunk_size() - -cdef extern from "pressure.hpp": - cdef shared_ptr[Observable_stat] calculate_pressure() - -cdef extern from "energy.hpp": - cdef shared_ptr[Observable_stat] calculate_energy() - -cdef inline get_obs_contribs(Span[double] contributions, - cbool calc_scalar_pressure): - """ - Convert an Observable_stat range of contributions into a correctly - shaped numpy array. - - Parameters - ---------- - contributions : (N,) array_like of :obj:`float` - Flattened array of energy/pressure contributions from an observable. - size : :obj:`int`, \{1, 9\} - Dimensionality of the data. - calc_scalar_pressure : :obj:`bool` - Whether to calculate a scalar pressure (only relevant when - ``contributions`` is a pressure tensor). - - """ - cdef np.ndarray value - value = np.empty(contributions.size()) - for i in range(contributions.size()): - value[i] = contributions[i] - if contributions.size() >= 9: - assert contributions.size() % 3 == 0 - if calc_scalar_pressure: - return np.einsum('...ii', value.reshape((-1, 3, 3))) / 3 - else: - return value.reshape((-1, 3, 3)) - else: - return value - -cdef inline get_obs_contrib(Span[double] contribution, - cbool calc_scalar_pressure): - """ - Convert an Observable_stat contribution into a correctly - shaped numpy array. If the size is 1, decay to a float. - - Parameters - ---------- - contributions : (N,) array_like of :obj:`float` - Flattened array of energy/pressure contributions from an observable. - calc_scalar_pressure : :obj:`bool` - Whether to calculate a scalar pressure (only relevant when - ``contributions`` is a pressure tensor). - - """ - cdef np.ndarray value - value = get_obs_contribs(contribution, calc_scalar_pressure) - if value.shape[0] == 1: - return value[0] - return value - -cdef inline observable_stat_matrix(size_t size, cbool calc_scalar_pressure): - if size == 9 and not calc_scalar_pressure: - return np.zeros((3, 3), dtype=float) - else: - return 0.0 - -cdef inline Observable_stat_to_dict(Observable_stat * obs, cbool calc_sp): - """Transform an ``Observable_stat`` object to a python dict. - - Parameters - ---------- - obs : - Core observable. - calc_sp : :obj:`bool` - Whether to calculate a scalar pressure (only relevant when - ``obs`` is a pressure tensor observable). - - Returns - ------- - :obj:`dict` - A dictionary with the following keys: - - * ``"total"``: total contribution - * ``"kinetic"``: kinetic contribution - * ``"bonded"``: total bonded contribution - * ``"bonded", ``: bonded contribution which arises from the given bond_type - * ``"non_bonded"``: total non-bonded contribution - * ``"non_bonded", , ``: non-bonded contribution which arises from the interactions between type_i and type_j - * ``"non_bonded_intra", , ``: non-bonded contribution between short ranged forces between type i and j and with the same mol_id - * ``"non_bonded_inter", , ``: non-bonded contribution between short ranged forces between type i and j and different mol_ids - * ``"coulomb"``: Coulomb contribution, how it is calculated depends on the method - * ``"coulomb", ``: Coulomb contribution from particle pairs (``i=0``), electrostatics solvers (``i=1``) - * ``"dipolar"``: dipolar contribution, how it is calculated depends on the method - * ``"dipolar", ``: dipolar contribution from particle pairs and magnetic field constraints (``i=0``), magnetostatics solvers (``i=1``) - * ``"virtual_sites"``: virtual sites contribution - * ``"virtual_sites", ``: contribution from virtual site i - * ``"external_fields"``: external fields contribution - - """ - - cdef size_t i - cdef size_t j - cdef size_t obs_dim = obs.get_chunk_size() - cdef size_t n_bonded = bonded_ia_params_next_key() - cdef size_t n_nonbonded = max_seen_particle_type - cdef double[9] total - - # numpy array shape - shape = (1,) - if obs_dim == 9 and not calc_sp: - shape = (3, 3) - - # Dict to store the results - p = {} - - # Total contribution - for i in range(obs_dim): - total[i] = obs.accumulate(0.0, i) - p["total"] = get_obs_contrib(Span[double](total, obs_dim), calc_sp) - - # Kinetic - p["kinetic"] = get_obs_contrib(obs.kinetic, calc_sp) - - # External - p["external_fields"] = get_obs_contrib(obs.external_fields, calc_sp) - - # Bonded - total_bonded = observable_stat_matrix(obs_dim, calc_sp) - for i in range(n_bonded): - if bonded_ia_params_zero_based_type( - i) != enum_bonded_interaction.BONDED_IA_NONE: - val = get_obs_contrib(obs.bonded_contribution(i), calc_sp) - p["bonded", i] = val - total_bonded += val - p["bonded"] = total_bonded - - # Non-Bonded interactions, total as well as intra and inter molecular - total_intra = observable_stat_matrix(obs_dim, calc_sp) - total_inter = observable_stat_matrix(obs_dim, calc_sp) - for i in range(n_nonbonded): - for j in range(i, n_nonbonded): - intra = get_obs_contrib( - obs.non_bonded_intra_contribution(i, j), calc_sp) - total_intra += intra - p["non_bonded_intra", i, j] = intra - inter = get_obs_contrib( - obs.non_bonded_inter_contribution(i, j), calc_sp) - total_inter += inter - p["non_bonded_inter", i, j] = inter - p["non_bonded", i, j] = intra + inter - - p["non_bonded_intra"] = total_intra - p["non_bonded_inter"] = total_inter - p["non_bonded"] = total_intra + total_inter - - # Electrostatics - IF ELECTROSTATICS == 1: - cdef np.ndarray coulomb - coulomb = get_obs_contribs(obs.coulomb, calc_sp) - coulomb = coulomb.reshape((-1, *shape)).squeeze() - for i in range(coulomb.shape[0]): - p["coulomb", i] = coulomb[i] - p["coulomb"] = np.sum(coulomb, axis=0) - - # Dipoles - IF DIPOLES == 1: - cdef np.ndarray dipolar - dipolar = get_obs_contribs(obs.dipolar, calc_sp) - dipolar = dipolar.reshape((-1, *shape)).squeeze() - for i in range(dipolar.shape[0]): - p["dipolar", i] = dipolar[i] - p["dipolar"] = np.sum(dipolar, axis=0) - - # virtual sites - IF VIRTUAL_SITES == 1: - cdef np.ndarray virtual_sites - virtual_sites = get_obs_contribs(obs.virtual_sites, calc_sp) - for i in range(virtual_sites.shape[0]): - p["virtual_sites", i] = virtual_sites[i] - p["virtual_sites"] = np.sum(virtual_sites, axis=0) - - return p - -cdef inline get_scalar_pressure(): - return Observable_stat_to_dict(calculate_pressure().get(), True) - -cdef inline get_pressure_tensor(): - return Observable_stat_to_dict(calculate_pressure().get(), False) - -cdef inline get_energy(): - return Observable_stat_to_dict(calculate_energy().get(), False) diff --git a/src/python/espressomd/analyze.pyx b/src/python/espressomd/analyze.py similarity index 89% rename from src/python/espressomd/analyze.pyx rename to src/python/espressomd/analyze.py index 70126b90fae..4a0f2f3119b 100644 --- a/src/python/espressomd/analyze.pyx +++ b/src/python/espressomd/analyze.py @@ -19,8 +19,7 @@ import numpy as np from . import utils -from . cimport analyze -from .code_features import assert_features +from .code_features import assert_features, has_features from .script_interface import script_interface_register, ScriptInterfaceHelper @@ -81,6 +80,64 @@ def acf_1d(signal, n_with_padding, n): f"are supported, got shape {time_series.shape}") +@script_interface_register +class _ObservableStat(ScriptInterfaceHelper): + _so_name = "ScriptInterface::Analysis::ObservableStat" + _so_creation_policy = "LOCAL" + + def _generate_summary(self, obj, dim, calc_sp): + """ + Compute derived quantities and reshape pressure tensors as 3x3 matrices. + """ + + def zero(): + if dim == 1 or calc_sp: + return 0. + return np.zeros(9, dtype=float) + + def reduction(obj, key): + total = zero() + for k in obj.keys(): + if isinstance(k, tuple) and k[0] == key: + total += obj[k] + obj[key] = total + + out = {} + out["bonded"] = zero() + out["non_bonded_intra"] = zero() + out["non_bonded_inter"] = zero() + for k, v in obj.items(): + if "," not in k: + out[k] = v + else: + k = k.split(",") + k = (k[0], *map(int, k[1:])) + out[k] = v + if k[0] == "bonded": + out["bonded"] += v + elif k[0].startswith("non_bonded_"): + if k[0] == "non_bonded_intra": + out["non_bonded_intra"] += v + else: + out["non_bonded_inter"] += v + k = ("non_bonded", *k[1:]) + if k not in out: + out[k] = zero() + out[k] += v + + out["non_bonded"] = out["non_bonded_intra"] + out["non_bonded_inter"] + if has_features("ELECTROSTATICS"): + reduction(out, "coulomb") + if has_features("DIPOLES"): + reduction(out, "dipolar") + if has_features("VIRTUAL_SITES"): + reduction(out, "virtual_sites") + + if dim == 1 or calc_sp: + return out + return {k: np.reshape(v, (3, 3)) for k, v in out.items()} + + @script_interface_register class Analysis(ScriptInterfaceHelper): """ @@ -365,10 +422,10 @@ def pressure(self): * ``"external_fields"``: external fields contribution """ - - obs = analyze.get_scalar_pressure() + obs_stat = _ObservableStat() + observable = obs_stat.call_method("calculate_scalar_pressure") utils.handle_errors("calculate_pressure() failed") - return obs + return obs_stat._generate_summary(observable, 9, True) def pressure_tensor(self): """ @@ -408,10 +465,10 @@ def pressure_tensor(self): * ``"external_fields"``: external fields contribution """ - - obs = analyze.get_pressure_tensor() + obs_stat = _ObservableStat() + observable = obs_stat.call_method("calculate_pressure_tensor") utils.handle_errors("calculate_pressure() failed") - return obs + return obs_stat._generate_summary(observable, 9, False) def energy(self): """ @@ -457,10 +514,10 @@ def energy(self): >>> print(energy["external_fields"]) """ - - obs = analyze.get_energy() + obs_stat = _ObservableStat() + observable = obs_stat.call_method("calculate_energy") utils.handle_errors("calculate_energy() failed") - return obs + return obs_stat._generate_summary(observable, 1, False) def particle_energy(self, particle): """ diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index 8fb07037e1f..4026e35e20c 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -22,7 +22,6 @@ from libcpp.memory cimport shared_ptr from libcpp.string cimport string from libcpp.vector cimport vector -from libcpp cimport bool as cbool from libc cimport stdint from .thermostat cimport thermalized_bond @@ -305,8 +304,6 @@ IF TABULATED: cdef extern from "script_interface/interactions/bonded.hpp": int bonded_ia_params_zero_based_type(int bond_id) except + - int bonded_ia_params_size() - int bonded_ia_params_next_key() # Map the boost::variant type indices to python type identifiers. These enum # values must be in the same order as in the definition of the boost::variant. diff --git a/src/python/espressomd/particle_data.pxd b/src/python/espressomd/particle_data.pxd index 69ef3fa7a94..cca355004fb 100644 --- a/src/python/espressomd/particle_data.pxd +++ b/src/python/espressomd/particle_data.pxd @@ -184,6 +184,9 @@ cdef extern from "rotation.hpp": Vector3d convert_vector_space_to_body(const particle & p, const Vector3d & v) void rotate_particle(int pid, const Vector3d & axis, double angle) +cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": + int max_seen_particle_type + cdef extern from "bonded_interactions/rigid_bond.hpp": extern int n_rigidbonds diff --git a/src/python/espressomd/particle_data.pyx b/src/python/espressomd/particle_data.pyx index 3dc80634be9..e128f312911 100644 --- a/src/python/espressomd/particle_data.pyx +++ b/src/python/espressomd/particle_data.pyx @@ -25,7 +25,6 @@ from . cimport particle_data from .interactions import BondedInteraction from .interactions import BondedInteractions from .interactions cimport bonded_ia_params_zero_based_type -from .analyze cimport max_seen_particle_type from copy import copy import collections import functools diff --git a/src/script_interface/analysis/CMakeLists.txt b/src/script_interface/analysis/CMakeLists.txt index 59bf25d5912..0fc64ff3ccb 100644 --- a/src/script_interface/analysis/CMakeLists.txt +++ b/src/script_interface/analysis/CMakeLists.txt @@ -1,3 +1,5 @@ target_sources( - Espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Analysis.cpp) + Espresso_script_interface + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Analysis.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ObservableStat.cpp) diff --git a/src/script_interface/analysis/ObservableStat.cpp b/src/script_interface/analysis/ObservableStat.cpp new file mode 100644 index 00000000000..1d0c59036a8 --- /dev/null +++ b/src/script_interface/analysis/ObservableStat.cpp @@ -0,0 +1,154 @@ +/* + * 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 . + */ + +#include "config.hpp" + +#include "ObservableStat.hpp" + +#include "script_interface/interactions/bonded.hpp" + +#include "core/nonbonded_interactions/nonbonded_interaction_data.hpp" + +#include "core/energy.hpp" +#include "core/pressure.hpp" + +#include + +#include +#include +#include + +namespace ScriptInterface { +namespace Analysis { + +/** + * @brief Generate an observable summary. + * @param[in] obs The observable handle. + * @param[in] calc_sp Whether to compute a scalar pressure. + */ +static auto get_summary(Observable_stat const &obs, bool const calc_sp) { + auto const obs_dim = obs.get_chunk_size(); + + auto const get_obs_contribs = + [obs_dim, + calc_sp](Utils::Span const views) -> std::vector { + if (obs_dim == 1) { + return std::vector(views.begin(), views.end()); + } + assert(obs_dim == 9ul); + assert(views.size() % 9ul == 0ul); + std::vector out; + for (std::size_t i = 0ul; i < views.size() / 9ul; ++i) { + auto const view = Utils::Span{views.data() + i * 9ul, 9ul}; + if (calc_sp) { + auto const trace = view[0] + view[4] + view[8]; + out.emplace_back(trace / 3.); + } else { + auto const flat_matrix = std::vector(view.begin(), view.end()); + out.emplace_back(flat_matrix); + } + } + return out; + }; + + auto const get_obs_contrib = + [&get_obs_contribs](Utils::Span const views) -> Variant { + return get_obs_contribs(views)[0]; + }; + + std::unordered_map dict; + dict["kinetic"] = get_obs_contrib(obs.kinetic); + dict["external_fields"] = get_obs_contrib(obs.external_fields); + + { + auto values = std::vector(obs_dim); + for (std::size_t i = 0; i < obs_dim; ++i) { + values[i] = obs.accumulate(0., i); + } + dict["total"] = get_obs_contrib({values.data(), obs_dim}); + } + + auto const n_bonds = ::bonded_ia_params.get_next_key(); + for (std::size_t bond_id = 0; bond_id < n_bonds; ++bond_id) { + if (bonded_ia_params_zero_based_type(bond_id) != 0) { + dict["bonded," + std::to_string(bond_id)] = + get_obs_contrib(obs.bonded_contribution(bond_id)); + } + } + + auto const n_nonbonded = static_cast(::max_seen_particle_type); + for (std::size_t i = 0; i < n_nonbonded; ++i) { + for (std::size_t j = i; j < n_nonbonded; ++j) { + auto const indices = std::to_string(i) + "," + std::to_string(j); + dict["non_bonded_intra," + indices] = + get_obs_contrib(obs.non_bonded_intra_contribution(i, j)); + dict["non_bonded_inter," + indices] = + get_obs_contrib(obs.non_bonded_inter_contribution(i, j)); + } + } + +#ifdef ELECTROSTATICS + { + auto const values = get_obs_contribs(obs.coulomb); + for (std::size_t i = 0; i < values.size(); ++i) { + dict["coulomb," + std::to_string(i)] = values[i]; + } + } +#endif // ELECTROSTATICS + +#ifdef DIPOLES + { + auto const values = get_obs_contribs(obs.dipolar); + for (std::size_t i = 0; i < values.size(); ++i) { + dict["dipolar," + std::to_string(i)] = values[i]; + } + } +#endif // DIPOLES + +#ifdef VIRTUAL_SITES + { + auto const values = get_obs_contribs(obs.virtual_sites); + for (std::size_t i = 0; i < values.size(); ++i) { + dict["virtual_sites," + std::to_string(i)] = values[i]; + } + } +#endif // VIRTUAL_SITES + + return dict; +} + +Variant ObservableStat::do_call_method(std::string const &name, + VariantMap const ¶meters) { + if (name == "calculate_energy") { + auto const obs = calculate_energy(); + return get_summary(*obs, false); + } + if (name == "calculate_scalar_pressure") { + auto const obs = calculate_pressure(); + return get_summary(*obs, true); + } + if (name == "calculate_pressure_tensor") { + auto const obs = calculate_pressure(); + return get_summary(*obs, false); + } + return {}; +} + +} // namespace Analysis +} // namespace ScriptInterface diff --git a/src/script_interface/analysis/ObservableStat.hpp b/src/script_interface/analysis/ObservableStat.hpp new file mode 100644 index 00000000000..cad05fe24f5 --- /dev/null +++ b/src/script_interface/analysis/ObservableStat.hpp @@ -0,0 +1,39 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_ANALYSIS_OBSERVABLE_STAT_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_ANALYSIS_OBSERVABLE_STAT_HPP + +#include "script_interface/ScriptInterface.hpp" + +#include + +namespace ScriptInterface { +namespace Analysis { + +class ObservableStat : public ObjectHandle { +public: + Variant do_call_method(std::string const &name, + VariantMap const ¶meters) override; +}; + +} // namespace Analysis +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/analysis/initialize.cpp b/src/script_interface/analysis/initialize.cpp index d0fa36a8ed4..fa1438370a9 100644 --- a/src/script_interface/analysis/initialize.cpp +++ b/src/script_interface/analysis/initialize.cpp @@ -20,12 +20,14 @@ #include "initialize.hpp" #include "Analysis.hpp" +#include "ObservableStat.hpp" namespace ScriptInterface { namespace Analysis { void initialize(Utils::Factory *om) { om->register_new("ScriptInterface::Analysis::Analysis"); + om->register_new("ScriptInterface::Analysis::ObservableStat"); } } // namespace Analysis diff --git a/src/script_interface/interactions/bonded.hpp b/src/script_interface/interactions/bonded.hpp index 9fb1ab3f238..6370468965b 100644 --- a/src/script_interface/interactions/bonded.hpp +++ b/src/script_interface/interactions/bonded.hpp @@ -35,9 +35,4 @@ inline int bonded_ia_params_zero_based_type(int bond_id) { return 0; } -/** Return the next key. */ -inline int bonded_ia_params_next_key() { - return bonded_ia_params.get_next_key(); -} - #endif From 6f34349534fc3cc710443d29fde99ea5c2473f9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 20 Jul 2022 13:59:47 +0200 Subject: [PATCH 09/85] script_interface: Rewrite Galilei module --- src/core/CMakeLists.txt | 2 +- src/core/galilei.cpp | 150 ------------------ src/core/galilei/CMakeLists.txt | 1 + src/core/galilei/Galilei.cpp | 99 ++++++++++++ src/core/{galilei.hpp => galilei/Galilei.hpp} | 36 +++-- .../EspressoSystemStandAlone_test.cpp | 18 ++- src/python/espressomd/galilei.pxd | 26 --- src/python/espressomd/galilei.py | 80 ++++++++++ src/python/espressomd/galilei.pyx | 84 ---------- src/script_interface/CMakeLists.txt | 1 + src/script_interface/galilei/CMakeLists.txt | 2 + src/script_interface/galilei/Galilei.hpp | 67 ++++++++ src/script_interface/galilei/initialize.cpp | 32 ++++ src/script_interface/galilei/initialize.hpp | 35 ++++ src/script_interface/initialize.cpp | 2 + 15 files changed, 354 insertions(+), 281 deletions(-) delete mode 100644 src/core/galilei.cpp create mode 100644 src/core/galilei/CMakeLists.txt create mode 100644 src/core/galilei/Galilei.cpp rename src/core/{galilei.hpp => galilei/Galilei.hpp} (54%) delete mode 100644 src/python/espressomd/galilei.pxd create mode 100644 src/python/espressomd/galilei.py delete mode 100644 src/python/espressomd/galilei.pyx create mode 100644 src/script_interface/galilei/CMakeLists.txt create mode 100644 src/script_interface/galilei/Galilei.hpp create mode 100644 src/script_interface/galilei/initialize.cpp create mode 100644 src/script_interface/galilei/initialize.hpp diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e5a79c08b49..673d7605754 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -33,7 +33,6 @@ set(Espresso_core_SRC EspressoSystemInterface.cpp forcecap.cpp forces.cpp - galilei.cpp ghosts.cpp grid.cpp immersed_boundaries.cpp @@ -113,6 +112,7 @@ add_subdirectory(cluster_analysis) add_subdirectory(constraints) add_subdirectory(electrostatics) add_subdirectory(error_handling) +add_subdirectory(galilei) add_subdirectory(grid_based_algorithms) add_subdirectory(immersed_boundary) add_subdirectory(integrators) diff --git a/src/core/galilei.cpp b/src/core/galilei.cpp deleted file mode 100644 index ab6bd3fea20..00000000000 --- a/src/core/galilei.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2010-2022 The ESPResSo project - * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 - * Max-Planck-Institute for Polymer Research, Theory Group - * - * This file is part of ESPResSo. - * - * ESPResSo is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ESPResSo is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "galilei.hpp" -#include "Particle.hpp" -#include "ParticleRange.hpp" -#include "cells.hpp" -#include "communication.hpp" -#include "event.hpp" -#include "grid.hpp" - -#include -#include - -#include - -#include -#include - -/** Stop particle motion by setting the velocity of each particle to zero. */ -void local_kill_particle_motion(int omega, const ParticleRange &particles) { - for (auto &p : particles) { - if (omega) { - p.m = {}; - } else { - p.v() = {}; - } - } -} - -/** Set all the forces acting on the particles to zero */ -void local_kill_particle_forces(int torque, const ParticleRange &particles) { - for (auto &p : particles) { - if (torque) { - p.f = {}; - } else { - p.force() = {}; - } - } -} - -/** Calculate the CMS of the system */ -std::pair local_system_CMS() { - return boost::accumulate( - cell_structure.local_particles(), std::pair{}, - [](auto sum, const Particle &p) { - if (not p.is_virtual()) { - return std::pair{ - 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}; - }); -} - -/** Calculate the CMS velocity of the system */ -std::pair local_system_CMS_velocity() { - return boost::accumulate( - cell_structure.local_particles(), std::pair{}, - [](auto sum, const Particle &p) { - if (not p.is_virtual()) { - return std::pair{ - sum.first + p.mass() * p.v(), sum.second + p.mass()}; - } - return std::pair{sum.first, sum.second}; - }); -} - -/** Remove the CMS velocity */ -void local_galilei_transform(const Utils::Vector3d &cmsvel) { - for (auto &p : cell_structure.local_particles()) { - p.v() -= cmsvel; - } -} - -void mpi_kill_particle_motion_local(int rotation) { - local_kill_particle_motion(rotation, cell_structure.local_particles()); - on_particle_change(); -} - -REGISTER_CALLBACK(mpi_kill_particle_motion_local) - -void mpi_kill_particle_motion(int rotation) { - mpi_call_all(mpi_kill_particle_motion_local, rotation); -} - -void mpi_kill_particle_forces_local(int torque) { - local_kill_particle_forces(torque, cell_structure.local_particles()); - on_particle_change(); -} - -REGISTER_CALLBACK(mpi_kill_particle_forces_local) - -void mpi_kill_particle_forces(int torque) { - mpi_call_all(mpi_kill_particle_forces_local, torque); -} - -struct pair_sum { - template - auto operator()(std::pair l, std::pair r) const { - return std::pair{l.first + r.first, l.second + r.second}; - } -}; - -Utils::Vector3d mpi_system_CMS() { - auto const data = - mpi_call(Communication::Result::reduction, pair_sum{}, local_system_CMS); - return data.first / data.second; -} - -REGISTER_CALLBACK_REDUCTION(local_system_CMS_velocity, pair_sum{}) - -Utils::Vector3d mpi_system_CMS_velocity() { - auto const data = mpi_call(Communication::Result::reduction, pair_sum{}, - local_system_CMS_velocity); - return data.first / data.second; -} - -REGISTER_CALLBACK_REDUCTION(local_system_CMS, pair_sum{}) - -void mpi_galilei_transform_local(Utils::Vector3d const &cmsvel) { - local_galilei_transform(cmsvel); - on_particle_change(); -} - -REGISTER_CALLBACK(mpi_galilei_transform_local) - -void mpi_galilei_transform() { - auto const cmsvel = mpi_system_CMS_velocity(); - mpi_call_all(mpi_galilei_transform_local, cmsvel); -} diff --git a/src/core/galilei/CMakeLists.txt b/src/core/galilei/CMakeLists.txt new file mode 100644 index 00000000000..87285e22124 --- /dev/null +++ b/src/core/galilei/CMakeLists.txt @@ -0,0 +1 @@ +target_sources(Espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Galilei.cpp) diff --git a/src/core/galilei/Galilei.cpp b/src/core/galilei/Galilei.cpp new file mode 100644 index 00000000000..0ead5f60e55 --- /dev/null +++ b/src/core/galilei/Galilei.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2010-2022 The ESPResSo project + * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 + * Max-Planck-Institute for Polymer Research, Theory Group + * + * This file is part of ESPResSo. + * + * ESPResSo is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ESPResSo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "galilei/Galilei.hpp" + +#include "Particle.hpp" +#include "ParticleRange.hpp" +#include "cells.hpp" +#include "communication.hpp" +#include "config.hpp" +#include "event.hpp" +#include "grid.hpp" + +#include + +#include + +void Galilei::kill_particle_motion(bool omega) const { + static_cast(omega); + for (auto &p : cell_structure.local_particles()) { + p.v() = {}; +#ifdef ROTATION + if (omega) { + p.omega() = {}; + } +#endif // ROTATION + } + on_particle_change(); +} + +void Galilei::kill_particle_forces(bool torque) const { + static_cast(torque); + for (auto &p : cell_structure.local_particles()) { + p.force() = {}; +#ifdef ROTATION + if (torque) { + p.torque() = {}; + } +#endif // ROTATION + } + on_particle_change(); +} + +Utils::Vector3d Galilei::calc_system_cms_position() const { + auto total_mass = 0.; + auto cms_pos = Utils::Vector3d{}; + for (auto const &p : cell_structure.local_particles()) { + if (not p.is_virtual()) { + total_mass += p.mass(); + cms_pos += p.mass() * + unfolded_position(p.pos(), p.image_box(), box_geo.length()); + } + } + total_mass = boost::mpi::all_reduce(comm_cart, total_mass, std::plus<>()); + cms_pos = boost::mpi::all_reduce(comm_cart, cms_pos, std::plus<>()); + cms_pos /= total_mass; + return cms_pos; +} + +Utils::Vector3d Galilei::calc_system_cms_velocity() const { + auto total_mass = 0.; + auto cms_vel = Utils::Vector3d{}; + for (auto const &p : cell_structure.local_particles()) { + if (not p.is_virtual()) { + total_mass += p.mass(); + cms_vel += p.mass() * p.v(); + } + } + total_mass = boost::mpi::all_reduce(comm_cart, total_mass, std::plus<>()); + cms_vel = boost::mpi::all_reduce(comm_cart, cms_vel, std::plus<>()); + cms_vel /= total_mass; + return cms_vel; +} + +void Galilei::galilei_transform() const { + auto const cms_vel = calc_system_cms_velocity(); + for (auto &p : cell_structure.local_particles()) { + p.v() -= cms_vel; + } + on_particle_change(); +} diff --git a/src/core/galilei.hpp b/src/core/galilei/Galilei.hpp similarity index 54% rename from src/core/galilei.hpp rename to src/core/galilei/Galilei.hpp index 26596f4e17f..72ff4b39d96 100644 --- a/src/core/galilei.hpp +++ b/src/core/galilei/Galilei.hpp @@ -18,28 +18,32 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef GALILEI_H -#define GALILEI_H + +#ifndef ESPRESSO_SRC_CORE_GALILEI_GALILEI_HPP +#define ESPRESSO_SRC_CORE_GALILEI_GALILEI_HPP #include -/** Stop particle motion by setting the velocity of each particle to zero. - * @param rotation if true, also set particle angular velocities to zero - */ -void mpi_kill_particle_motion(int rotation); +class Galilei { +public: + /** Stop particle motion by setting the velocity of each particle to zero. + * @param omega if true, also set particle angular velocities to zero + */ + void kill_particle_motion(bool omega) const; -/** Set all the forces acting on the particles to zero. - * @param torque if true, also set particle torques to zero - */ -void mpi_kill_particle_forces(int torque); + /** Set all the forces acting on the particles to zero. + * @param torque if true, also set particle torques to zero + */ + void kill_particle_forces(bool torque) const; -/** Calculate the CMS of the system */ -Utils::Vector3d mpi_system_CMS(); + /** Calculate the CMS of the system */ + Utils::Vector3d calc_system_cms_position() const; -/** Calculate the CMS velocity of the system */ -Utils::Vector3d mpi_system_CMS_velocity(); + /** Calculate the CMS velocity of the system */ + Utils::Vector3d calc_system_cms_velocity() const; -/** Remove the CMS velocity */ -void mpi_galilei_transform(); + /** Remove the CMS velocity */ + void galilei_transform() const; +}; #endif diff --git a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp index f2cdabff335..3f69a1764c1 100644 --- a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp +++ b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp @@ -37,7 +37,7 @@ namespace utf = boost::unit_test; #include "electrostatics/p3m.hpp" #include "electrostatics/registration.hpp" #include "energy.hpp" -#include "galilei.hpp" +#include "galilei/Galilei.hpp" #include "integrate.hpp" #include "nonbonded_interactions/lj.hpp" #include "observables/ParticleVelocities.hpp" @@ -94,6 +94,16 @@ static void mpi_create_bonds(int harm_bond_id, int fene_bond_id) { mpi_call_all(mpi_create_bonds_local, harm_bond_id, fene_bond_id); } +static void mpi_remove_translational_motion_local() { + Galilei{}.kill_particle_motion(false); +} + +REGISTER_CALLBACK(mpi_remove_translational_motion_local) + +static void mpi_remove_translational_motion() { + mpi_call_all(mpi_remove_translational_motion_local); +} + #ifdef P3M static void mpi_set_tuned_p3m_local(double prefactor) { auto p3m = P3MParameters{false, @@ -168,7 +178,7 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, BOOST_REQUIRE_EQUAL_COLLECTIONS(obs_shape.begin(), obs_shape.end(), ref_shape.begin(), ref_shape.end()); - mpi_kill_particle_motion(0); + mpi_remove_translational_motion(); for (int i = 0; i < 5; ++i) { set_particle_v(pid2, {static_cast(i), 0., 0.}); @@ -186,7 +196,7 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, // check kinetic energy { - mpi_kill_particle_motion(0); + mpi_remove_translational_motion(); for (int i = 0; i < 5; ++i) { set_particle_v(pid2, {static_cast(i), 0., 0.}); auto const &p = get_particle_data(pid2); @@ -301,7 +311,7 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, integrate_set_nvt(); // reset system - mpi_kill_particle_motion(0); + mpi_remove_translational_motion(); reset_particle_positions(); // recalculate forces without propagating the system diff --git a/src/python/espressomd/galilei.pxd b/src/python/espressomd/galilei.pxd deleted file mode 100644 index a80dc3b2ab7..00000000000 --- a/src/python/espressomd/galilei.pxd +++ /dev/null @@ -1,26 +0,0 @@ -# -# 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 . -# -from .utils cimport Vector3d - -cdef extern from "galilei.hpp": - void mpi_kill_particle_motion(int rotation) - void mpi_kill_particle_forces(int torque) - Vector3d mpi_system_CMS() - Vector3d mpi_system_CMS_velocity() - void mpi_galilei_transform() diff --git a/src/python/espressomd/galilei.py b/src/python/espressomd/galilei.py new file mode 100644 index 00000000000..1a81750b3f6 --- /dev/null +++ b/src/python/espressomd/galilei.py @@ -0,0 +1,80 @@ +# +# 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 . +# + +from .script_interface import script_interface_register, ScriptInterfaceHelper + + +@script_interface_register +class GalileiTransform(ScriptInterfaceHelper): + """ + Collective operations on particles. + + Methods + ------- + kill_particle_motion(): + Stop the motion of all particles. + + Parameters + ---------- + rotation : :obj:`bool`, optional + Whether or not to stop the angular momentum too. Defaults to false. + + kill_particle_forces(): + Set the forces on the particles to zero. + + Parameters + ---------- + torque : :obj:`bool`, optional + Whether or not to set the torques to zero too. Defaults to false. + + system_CMS(): + Calculate the center of mass of the system. Assumes equal unit mass + if the ``MASS`` feature is not compiled in. + + Returns + ------- + (3,) array_like of :obj:`float` + The center of mass position. + + system_CMS_velocity(): + Calculate the center of mass velocity of the system. Assumes equal unit + mass if the ``MASS`` feature is not compiled in. + + Returns + ------- + (3,) array_like of :obj:`float` + The of the center of mass velocity vector. + + galilei_transform(): + Remove the center of mass velocity of the system. Assumes equal unit + mass if the ``MASS`` feature is not compiled in. This is often used + when switching from Langevin Dynamics to lattice-Boltzmann. This is + due to the random nature of LD that yield a non-zero net system + momentum at any given time. + + """ + _so_name = "Galilei::Galilei" + _so_creation_policy = "GLOBAL" + _so_bind_methods = ( + "kill_particle_motion", + "kill_particle_forces", + "system_CMS", + "system_CMS_velocity", + "galilei_transform", + ) diff --git a/src/python/espressomd/galilei.pyx b/src/python/espressomd/galilei.pyx deleted file mode 100644 index 7bcd90872a6..00000000000 --- a/src/python/espressomd/galilei.pyx +++ /dev/null @@ -1,84 +0,0 @@ -# -# 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 . -# - -from . cimport galilei -from .utils cimport make_array_locked - -cdef class GalileiTransform: - - def kill_particle_motion(self, rotation=False): - """ - Stop the motion of the particles. - - Parameters - ---------- - rotation : :obj:`bool`, optional - Whether or not to kill the rotations too. - - """ - mpi_kill_particle_motion(int(rotation)) - - def kill_particle_forces(self, torque=False): - """ - Set the forces on the particles to zero. - - Parameters - ---------- - torque : :obj:`bool`, optional - Whether or not to kill the torques on all particles too. - - """ - mpi_kill_particle_forces(int(torque)) - - def system_CMS(self): - """ - Calculate the center of mass of the system. Assumes equal unit mass if the mass feature is not used. - - Returns - ------- - cms : (3,) array_like of :obj:`float` - The of the center of mass position vector. - - """ - return make_array_locked(mpi_system_CMS()) - - def system_CMS_velocity(self): - """ - Calculate the center of mass velocity of the system. Assumes equal unit - mass if the mass feature is not used. - - Returns - ------- - cms_vel : (3,) array_like of :obj:`float` - The of the center of mass velocity vector. - - """ - - return make_array_locked(mpi_system_CMS_velocity()) - - def galilei_transform(self): - """ - Remove the center of mass velocity of the system. Assumes equal unit - mass if the mass feature is not used. This is often used when switching - from Langevin Dynamics to lattice-Boltzmann. This is due to the random - nature of LD that yield a non-zero net system momentum at any given - time. - - """ - mpi_galilei_transform() diff --git a/src/script_interface/CMakeLists.txt b/src/script_interface/CMakeLists.txt index 5f8666d136f..6fac648520d 100644 --- a/src/script_interface/CMakeLists.txt +++ b/src/script_interface/CMakeLists.txt @@ -31,6 +31,7 @@ add_subdirectory(cluster_analysis) add_subdirectory(collision_detection) add_subdirectory(constraints) add_subdirectory(electrostatics) +add_subdirectory(galilei) add_subdirectory(h5md) add_subdirectory(interactions) add_subdirectory(lbboundaries) diff --git a/src/script_interface/galilei/CMakeLists.txt b/src/script_interface/galilei/CMakeLists.txt new file mode 100644 index 00000000000..36dbb5a513c --- /dev/null +++ b/src/script_interface/galilei/CMakeLists.txt @@ -0,0 +1,2 @@ +target_sources(Espresso_script_interface + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/galilei/Galilei.hpp b/src/script_interface/galilei/Galilei.hpp new file mode 100644 index 00000000000..2d15aa0c853 --- /dev/null +++ b/src/script_interface/galilei/Galilei.hpp @@ -0,0 +1,67 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_GALILEI_GALILEI_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_GALILEI_GALILEI_HPP + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include "core/galilei/Galilei.hpp" + +#include + +namespace ScriptInterface { +namespace Galilei { + +class Galilei : public AutoParameters { + std::shared_ptr<::Galilei> m_galilei; + + void do_construct(VariantMap const ¶ms) override { + m_galilei = std::make_shared<::Galilei>(); + } + +public: + Variant do_call_method(std::string const &name, + VariantMap const ¶ms) override { + if (name == "kill_particle_motion") { + auto const rotation = get_value_or(params, "rotation", false); + m_galilei->kill_particle_motion(rotation); + } + if (name == "kill_particle_forces") { + auto const torque = get_value_or(params, "torque", false); + m_galilei->kill_particle_forces(torque); + } + if (name == "galilei_transform") { + m_galilei->galilei_transform(); + } + if (name == "system_CMS") { + return m_galilei->calc_system_cms_position().as_vector(); + } + if (name == "system_CMS_velocity") { + return m_galilei->calc_system_cms_velocity().as_vector(); + } + return {}; + } +}; + +} // namespace Galilei +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/galilei/initialize.cpp b/src/script_interface/galilei/initialize.cpp new file mode 100644 index 00000000000..061ffcae8fb --- /dev/null +++ b/src/script_interface/galilei/initialize.cpp @@ -0,0 +1,32 @@ +/* + * 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 "Galilei.hpp" + +namespace ScriptInterface { +namespace Galilei { + +void initialize(Utils::Factory *om) { + om->register_new("Galilei::Galilei"); +} + +} // namespace Galilei +} // namespace ScriptInterface diff --git a/src/script_interface/galilei/initialize.hpp b/src/script_interface/galilei/initialize.hpp new file mode 100644 index 00000000000..248d609d705 --- /dev/null +++ b/src/script_interface/galilei/initialize.hpp @@ -0,0 +1,35 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_GALILEI_INITIALIZE_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_GALILEI_INITIALIZE_HPP + +#include + +#include + +namespace ScriptInterface { +namespace Galilei { + +void initialize(Utils::Factory *om); + +} // namespace Galilei +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/initialize.cpp b/src/script_interface/initialize.cpp index 9e58b0fc1d7..13ac41e2854 100644 --- a/src/script_interface/initialize.cpp +++ b/src/script_interface/initialize.cpp @@ -31,6 +31,7 @@ #include "collision_detection/initialize.hpp" #include "constraints/initialize.hpp" #include "electrostatics/initialize.hpp" +#include "galilei/initialize.hpp" #include "h5md/initialize.hpp" #include "interactions/initialize.hpp" #include "lbboundaries/initialize.hpp" @@ -55,6 +56,7 @@ void initialize(Utils::Factory *f) { Constraints::initialize(f); Coulomb::initialize(f); Dipoles::initialize(f); + Galilei::initialize(f); Interactions::initialize(f); LBBoundaries::initialize(f); LeesEdwards::initialize(f); From 47c3d699edef0d7a89ef2418c3c436d7d9b5d71d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 20 Jul 2022 14:37:20 +0200 Subject: [PATCH 10/85] script_interface: Rewrite CellSystem module --- src/core/cell_system/CellStructure.hpp | 39 ++- src/python/espressomd/tuning.pxd | 20 -- .../cell_system/CMakeLists.txt | 5 +- .../cell_system/CellSystem.cpp | 262 ++++++++++++++++++ .../cell_system/CellSystem.hpp | 233 +--------------- 5 files changed, 290 insertions(+), 269 deletions(-) delete mode 100644 src/python/espressomd/tuning.pxd create mode 100644 src/script_interface/cell_system/CellSystem.cpp diff --git a/src/core/cell_system/CellStructure.hpp b/src/core/cell_system/CellStructure.hpp index a094f652c18..cfee3c2fa72 100644 --- a/src/core/cell_system/CellStructure.hpp +++ b/src/core/cell_system/CellStructure.hpp @@ -33,6 +33,7 @@ #include "bond_error.hpp" #include "cell_system/Cell.hpp" #include "cell_system/CellStructureType.hpp" +#include "config.hpp" #include "ghosts.hpp" #include @@ -256,7 +257,6 @@ struct CellStructure { [this](int id) { return get_local_particle(id); }); } -public: CellStructureType decomposition_type() const { return m_type; } /** Maximal cutoff supported by current cell system. */ @@ -265,10 +265,6 @@ struct CellStructure { /** Maximal pair range supported by current cell system. */ Utils::Vector3d max_range() const; -private: - Utils::Span local_cells(); - -public: ParticleRange local_particles(); ParticleRange ghost_particles(); @@ -281,6 +277,8 @@ struct CellStructure { */ Cell *particle_to_cell(const Particle &p); + Utils::Span local_cells(); + public: /** * @brief Add a particle. @@ -346,7 +344,7 @@ struct CellStructure { * * @return The active particle decomposition. */ - const ParticleDecomposition &decomposition() const { + ParticleDecomposition const &decomposition() const { return assert(m_decomposition), *m_decomposition; } @@ -418,6 +416,7 @@ struct CellStructure { * @brief Add forces from ghost particles to real particles. */ void ghosts_reduce_forces(); + #ifdef BOND_CONSTRAINT /** * @brief Add rattle corrections from ghost particles to real particles. @@ -425,6 +424,11 @@ struct CellStructure { void ghosts_reduce_rattle_correction(); #endif + /** + * @brief Resort particles. + */ + void resort_particles(bool global_flag, BoxGeometry const &box); + private: /** * @brief Resolve ids to particles. @@ -491,13 +495,6 @@ struct CellStructure { } } -public: - /** - * @brief Resort particles. - */ - void resort_particles(bool global_flag, BoxGeometry const &box); - -private: /** @brief Set the particle decomposition, keeping the particles. */ void set_particle_decomposition( std::unique_ptr &&decomposition) { @@ -550,13 +547,6 @@ struct CellStructure { LocalBox &local_geo, std::set n_square_types); -public: - template void bond_loop(BondKernel const &bond_kernel) { - for (auto &p : local_particles()) { - execute_bond_handler(p, bond_kernel); - } - } - private: /** * @brief Run link_cell algorithm for local cells. @@ -630,6 +620,15 @@ struct CellStructure { } public: + /** Bonded pair loop. + * @param bond_kernel Kernel to apply + */ + template void bond_loop(BondKernel const &bond_kernel) { + for (auto &p : local_particles()) { + execute_bond_handler(p, bond_kernel); + } + } + /** Non-bonded pair loop. * @param pair_kernel Kernel to apply */ diff --git a/src/python/espressomd/tuning.pxd b/src/python/espressomd/tuning.pxd deleted file mode 100644 index b8893ccb288..00000000000 --- a/src/python/espressomd/tuning.pxd +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (C) 2010-2022 The ESPResSo project -# -# This file is part of ESPResSo. -# -# ESPResSo is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# ESPResSo is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -from libcpp cimport bool - -cdef extern from "tuning.hpp": - cdef void tune_skin(double min_skin, double max_skin, double tol, int int_steps, bool adjust_max_skin) diff --git a/src/script_interface/cell_system/CMakeLists.txt b/src/script_interface/cell_system/CMakeLists.txt index 36dbb5a513c..4b997aa6dc4 100644 --- a/src/script_interface/cell_system/CMakeLists.txt +++ b/src/script_interface/cell_system/CMakeLists.txt @@ -1,2 +1,3 @@ -target_sources(Espresso_script_interface - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) +target_sources( + Espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/CellSystem.cpp) diff --git a/src/script_interface/cell_system/CellSystem.cpp b/src/script_interface/cell_system/CellSystem.cpp new file mode 100644 index 00000000000..6392e6aeedb --- /dev/null +++ b/src/script_interface/cell_system/CellSystem.cpp @@ -0,0 +1,262 @@ +/* + * 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 "CellSystem.hpp" + +#include "script_interface/ScriptInterface.hpp" + +#include "core/bonded_interactions/bonded_interaction_data.hpp" +#include "core/cell_system/HybridDecomposition.hpp" +#include "core/cell_system/RegularDecomposition.hpp" +#include "core/cells.hpp" +#include "core/communication.hpp" +#include "core/event.hpp" +#include "core/grid.hpp" +#include "core/integrate.hpp" +#include "core/nonbonded_interactions/nonbonded_interaction_data.hpp" +#include "core/particle_node.hpp" +#include "core/tuning.hpp" + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ScriptInterface { +namespace CellSystem { + +static auto const &get_regular_decomposition() { + return dynamic_cast( + Utils::as_const(::cell_structure).decomposition()); +} + +static auto const &get_hybrid_decomposition() { + return dynamic_cast( + Utils::as_const(::cell_structure).decomposition()); +} + +CellSystem::CellSystem() { + add_parameters({ + {"use_verlet_lists", ::cell_structure.use_verlet_list}, + {"node_grid", + [this](Variant const &v) { + context()->parallel_try_catch([&v]() { + auto const error_msg = std::string("Parameter 'node_grid'"); + auto const old_node_grid = ::node_grid; + auto const new_node_grid = get_value(v); + auto const n_nodes_old = Utils::product(old_node_grid); + auto const n_nodes_new = Utils::product(new_node_grid); + if (n_nodes_new != n_nodes_old) { + std::stringstream reason; + reason << ": MPI world size " << n_nodes_old << " incompatible " + << "with new node grid [" << new_node_grid << "]"; + throw std::invalid_argument(error_msg + reason.str()); + } + try { + ::node_grid = new_node_grid; + on_node_grid_change(); + } catch (...) { + ::node_grid = old_node_grid; + on_node_grid_change(); + throw; + } + }); + }, + []() { return ::node_grid; }}, + {"skin", + [this](Variant const &v) { + auto const new_skin = get_value(v); + if (new_skin < 0.) { + if (context()->is_head_node()) { + throw std::domain_error("Parameter 'skin' must be >= 0"); + } + throw Exception(""); + } + mpi_set_skin_local(new_skin); + }, + []() { return ::skin; }}, + {"decomposition_type", AutoParameter::read_only, + [this]() { + return cs_type_to_name.at(::cell_structure.decomposition_type()); + }}, + {"n_square_types", AutoParameter::read_only, + []() { + if (::cell_structure.decomposition_type() != + CellStructureType::CELL_STRUCTURE_HYBRID) { + return Variant{none}; + } + auto const hd = get_hybrid_decomposition(); + auto const ns_types = hd.get_n_square_types(); + return Variant{std::vector(ns_types.begin(), ns_types.end())}; + }}, + {"cutoff_regular", AutoParameter::read_only, + []() { + if (::cell_structure.decomposition_type() != + CellStructureType::CELL_STRUCTURE_HYBRID) { + return Variant{none}; + } + auto const hd = get_hybrid_decomposition(); + return Variant{hd.get_cutoff_regular()}; + }}, + {"max_cut_nonbonded", AutoParameter::read_only, maximal_cutoff_nonbonded}, + {"max_cut_bonded", AutoParameter::read_only, maximal_cutoff_bonded}, + {"interaction_range", AutoParameter::read_only, interaction_range}, + }); +} + +Variant CellSystem::do_call_method(std::string const &name, + VariantMap const ¶ms) { + if (name == "initialize") { + auto const cs_name = get_value(params, "name"); + auto const cs_type = cs_name_to_type.at(cs_name); + initialize(cs_type, params); + return {}; + } + if (name == "resort") { + auto const global_flag = get_value_or(params, "global_flag", true); + return mpi_resort_particles(global_flag); + } + if (name == "get_state") { + auto state = get_parameters(); + auto const cs_type = ::cell_structure.decomposition_type(); + if (cs_type == CellStructureType::CELL_STRUCTURE_REGULAR) { + auto const rd = get_regular_decomposition(); + state["cell_grid"] = Variant{rd.cell_grid}; + state["cell_size"] = Variant{rd.cell_size}; + } else if (cs_type == CellStructureType::CELL_STRUCTURE_HYBRID) { + auto const hd = get_hybrid_decomposition(); + state["cell_grid"] = Variant{hd.get_cell_grid()}; + state["cell_size"] = Variant{hd.get_cell_size()}; + mpi_resort_particles(true); // needed to get correct particle counts + state["parts_per_decomposition"] = + Variant{std::unordered_map{ + {"regular", hd.count_particles_in_regular()}, + {"n_square", hd.count_particles_in_n_square()}}}; + } + state["verlet_reuse"] = get_verlet_reuse(); + state["n_nodes"] = ::n_nodes; + return state; + } + if (name == "get_pairs") { + std::vector out; + context()->parallel_try_catch([¶ms, &out]() { + std::vector> pair_list; + auto const distance = get_value(params, "distance"); + if (boost::get(¶ms.at("types")) != nullptr) { + auto const key = get_value(params, "types"); + if (key != "all") { + throw std::invalid_argument("Unknown argument types='" + key + "'"); + } + pair_list = get_pairs(distance); + } else { + auto const types = get_value>(params, "types"); + pair_list = get_pairs_of_types(distance, types); + } + std::transform(pair_list.begin(), pair_list.end(), + std::back_inserter(out), + [](std::pair const &pair) { + return std::vector{pair.first, pair.second}; + }); + }); + return out; + } + if (name == "get_neighbors") { + std::vector> neighbors_global; + context()->parallel_try_catch([&neighbors_global, ¶ms]() { + auto const dist = get_value(params, "distance"); + auto const pid = get_value(params, "pid"); + auto const ret = mpi_get_short_range_neighbors_local(pid, dist, true); + std::vector neighbors_local; + if (ret) { + neighbors_local = *ret; + } + boost::mpi::gather(::comm_cart, neighbors_local, neighbors_global, 0); + }); + std::vector neighbors; + for (auto const &neighbors_local : neighbors_global) { + if (not neighbors_local.empty()) { + neighbors = neighbors_local; + break; + } + } + return neighbors; + } + if (name == "non_bonded_loop_trace") { + std::vector out; + auto const pair_list = non_bonded_loop_trace(); + std::transform(pair_list.begin(), pair_list.end(), std::back_inserter(out), + [](PairInfo const &pair) { + return std::vector{pair.id1, pair.id2, + pair.pos1, pair.pos2, + pair.vec21, pair.node}; + }); + return out; + } + if (name == "tune_skin") { + if (context()->is_head_node()) { + tune_skin(get_value(params, "min_skin"), + get_value(params, "max_skin"), + get_value(params, "tol"), + get_value(params, "int_steps"), + get_value_or(params, "adjust_max_skin", false)); + } + return ::skin; + } + return {}; +} + +std::vector CellSystem::mpi_resort_particles(bool global_flag) const { + ::cell_structure.resort_particles(global_flag, box_geo); + if (context()->is_head_node()) { + clear_particle_node(); + } + auto const size = static_cast(::cell_structure.local_particles().size()); + std::vector n_part_per_node; + boost::mpi::gather(::comm_cart, size, n_part_per_node, 0); + return n_part_per_node; +} + +void CellSystem::initialize(CellStructureType const &cs_type, + VariantMap const ¶ms) const { + auto const verlet = get_value_or(params, "use_verlet_lists", true); + ::cell_structure.use_verlet_list = verlet; + if (cs_type == CellStructureType::CELL_STRUCTURE_HYBRID) { + auto const cutoff_regular = get_value(params, "cutoff_regular"); + auto const ns_types = + get_value_or>(params, "n_square_types", {}); + auto n_square_types = std::set{ns_types.begin(), ns_types.end()}; + set_hybrid_decomposition(std::move(n_square_types), cutoff_regular); + } else { + cells_re_init(cs_type); + } +} + +} // namespace CellSystem +} // namespace ScriptInterface diff --git a/src/script_interface/cell_system/CellSystem.hpp b/src/script_interface/cell_system/CellSystem.hpp index 56757ed5390..efc878e7c12 100644 --- a/src/script_interface/cell_system/CellSystem.hpp +++ b/src/script_interface/cell_system/CellSystem.hpp @@ -21,50 +21,16 @@ #define ESPRESSO_SRC_SCRIPT_INTERFACE_CELL_SYSTEM_CELL_SYSTEM_HPP #include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" -#include "core/bonded_interactions/bonded_interaction_data.hpp" -#include "core/cell_system/HybridDecomposition.hpp" -#include "core/cell_system/RegularDecomposition.hpp" -#include "core/cells.hpp" -#include "core/communication.hpp" -#include "core/event.hpp" -#include "core/grid.hpp" -#include "core/integrate.hpp" -#include "core/nonbonded_interactions/nonbonded_interaction_data.hpp" -#include "core/particle_node.hpp" -#include "core/tuning.hpp" +#include "cell_system/CellStructureType.hpp" -#include -#include - -#include -#include - -#include -#include -#include -#include -#include #include #include -#include -#include namespace ScriptInterface { namespace CellSystem { -namespace { -auto const &get_regular_decomposition() { - return dynamic_cast( - Utils::as_const(cell_structure).decomposition()); -} - -auto const &get_hybrid_decomposition() { - return dynamic_cast( - Utils::as_const(cell_structure).decomposition()); -} -} // namespace - class CellSystem : public AutoParameters { std::unordered_map const cs_type_to_name = { {CellStructureType::CELL_STRUCTURE_REGULAR, "regular_decomposition"}, @@ -79,75 +45,7 @@ class CellSystem : public AutoParameters { }; public: - CellSystem() { - add_parameters({ - {"use_verlet_lists", cell_structure.use_verlet_list}, - {"node_grid", - [this](Variant const &v) { - context()->parallel_try_catch([&v]() { - auto const error_msg = std::string("Parameter 'node_grid'"); - auto const old_node_grid = ::node_grid; - auto const new_node_grid = get_value(v); - auto const n_nodes_old = Utils::product(old_node_grid); - auto const n_nodes_new = Utils::product(new_node_grid); - if (n_nodes_new != n_nodes_old) { - std::stringstream reason; - reason << ": MPI world size " << n_nodes_old << " incompatible " - << "with new node grid [" << new_node_grid << "]"; - throw std::invalid_argument(error_msg + reason.str()); - } - try { - ::node_grid = new_node_grid; - on_node_grid_change(); - } catch (...) { - ::node_grid = old_node_grid; - on_node_grid_change(); - throw; - } - }); - }, - []() { return ::node_grid; }}, - {"skin", - [this](Variant const &v) { - auto const new_skin = get_value(v); - if (new_skin < 0.) { - if (context()->is_head_node()) { - throw std::domain_error("Parameter 'skin' must be >= 0"); - } - throw Exception(""); - } - mpi_set_skin_local(new_skin); - }, - []() { return ::skin; }}, - {"decomposition_type", AutoParameter::read_only, - [this]() { - return cs_type_to_name.at(cell_structure.decomposition_type()); - }}, - {"n_square_types", AutoParameter::read_only, - []() { - if (cell_structure.decomposition_type() != - CellStructureType::CELL_STRUCTURE_HYBRID) { - return Variant{none}; - } - auto const hd = get_hybrid_decomposition(); - auto const ns_types = hd.get_n_square_types(); - return Variant{std::vector(ns_types.begin(), ns_types.end())}; - }}, - {"cutoff_regular", AutoParameter::read_only, - []() { - if (cell_structure.decomposition_type() != - CellStructureType::CELL_STRUCTURE_HYBRID) { - return Variant{none}; - } - auto const hd = get_hybrid_decomposition(); - return Variant{hd.get_cutoff_regular()}; - }}, - {"max_cut_nonbonded", AutoParameter::read_only, - maximal_cutoff_nonbonded}, - {"max_cut_bonded", AutoParameter::read_only, maximal_cutoff_bonded}, - {"interaction_range", AutoParameter::read_only, interaction_range}, - }); - } + CellSystem(); void do_construct(VariantMap const ¶ms) override { // the core cell structure has a default constructor; params is empty @@ -163,105 +61,7 @@ class CellSystem : public AutoParameters { } Variant do_call_method(std::string const &name, - VariantMap const ¶ms) override { - if (name == "initialize") { - auto const cs_name = get_value(params, "name"); - auto const cs_type = cs_name_to_type.at(cs_name); - initialize(cs_type, params); - return {}; - } - if (name == "resort") { - auto const global_flag = get_value_or(params, "global_flag", true); - return mpi_resort_particles(global_flag); - } - if (name == "get_state") { - auto state = get_parameters(); - auto const cs_type = cell_structure.decomposition_type(); - if (cs_type == CellStructureType::CELL_STRUCTURE_REGULAR) { - auto const rd = get_regular_decomposition(); - state["cell_grid"] = Variant{rd.cell_grid}; - state["cell_size"] = Variant{rd.cell_size}; - } else if (cs_type == CellStructureType::CELL_STRUCTURE_HYBRID) { - auto const hd = get_hybrid_decomposition(); - state["cell_grid"] = Variant{hd.get_cell_grid()}; - state["cell_size"] = Variant{hd.get_cell_size()}; - mpi_resort_particles(true); // needed to get correct particle counts - state["parts_per_decomposition"] = - Variant{std::unordered_map{ - {"regular", hd.count_particles_in_regular()}, - {"n_square", hd.count_particles_in_n_square()}}}; - } - state["verlet_reuse"] = get_verlet_reuse(); - state["n_nodes"] = ::n_nodes; - return state; - } - if (name == "get_pairs") { - std::vector out; - context()->parallel_try_catch([¶ms, &out]() { - std::vector> pair_list; - auto const distance = get_value(params, "distance"); - if (boost::get(¶ms.at("types")) != nullptr) { - auto const key = get_value(params, "types"); - if (key != "all") { - throw std::invalid_argument("Unknown argument types='" + key + "'"); - } - pair_list = get_pairs(distance); - } else { - auto const types = get_value>(params, "types"); - pair_list = get_pairs_of_types(distance, types); - } - std::transform(pair_list.begin(), pair_list.end(), - std::back_inserter(out), - [](std::pair const &pair) { - return std::vector{pair.first, pair.second}; - }); - }); - return out; - } - if (name == "get_neighbors") { - std::vector> neighbors_global; - context()->parallel_try_catch([&neighbors_global, ¶ms]() { - auto const dist = get_value(params, "distance"); - auto const pid = get_value(params, "pid"); - auto const ret = mpi_get_short_range_neighbors_local(pid, dist, true); - std::vector neighbors_local; - if (ret) { - neighbors_local = *ret; - } - boost::mpi::gather(comm_cart, neighbors_local, neighbors_global, 0); - }); - std::vector neighbors; - for (auto const &neighbors_local : neighbors_global) { - if (not neighbors_local.empty()) { - neighbors = neighbors_local; - break; - } - } - return neighbors; - } - if (name == "non_bonded_loop_trace") { - std::vector out; - auto const pair_list = non_bonded_loop_trace(); - std::transform(pair_list.begin(), pair_list.end(), - std::back_inserter(out), [](PairInfo const &pair) { - return std::vector{pair.id1, pair.id2, - pair.pos1, pair.pos2, - pair.vec21, pair.node}; - }); - return out; - } - if (name == "tune_skin") { - if (context()->is_head_node()) { - tune_skin(get_value(params, "min_skin"), - get_value(params, "max_skin"), - get_value(params, "tol"), - get_value(params, "int_steps"), - get_value_or(params, "adjust_max_skin", false)); - } - return ::skin; - } - return {}; - } + VariantMap const ¶ms) override; private: /** @@ -273,31 +73,10 @@ class CellSystem : public AutoParameters { * are only exchanges between neighbors. * @return The number of particles on the nodes after the resort. */ - std::vector mpi_resort_particles(bool global_flag) const { - cell_structure.resort_particles(global_flag, box_geo); - if (context()->is_head_node()) { - clear_particle_node(); - } - auto const size = static_cast(cell_structure.local_particles().size()); - std::vector n_part_per_node; - boost::mpi::gather(comm_cart, size, n_part_per_node, 0); - return n_part_per_node; - } + std::vector mpi_resort_particles(bool global_flag) const; void initialize(CellStructureType const &cs_type, - VariantMap const ¶ms) const { - auto const verlet = get_value_or(params, "use_verlet_lists", true); - cell_structure.use_verlet_list = verlet; - if (cs_type == CellStructureType::CELL_STRUCTURE_HYBRID) { - auto const cutoff_regular = get_value(params, "cutoff_regular"); - auto const ns_types = - get_value_or>(params, "n_square_types", {}); - auto n_square_types = std::set{ns_types.begin(), ns_types.end()}; - set_hybrid_decomposition(std::move(n_square_types), cutoff_regular); - } else { - cells_re_init(cs_type); - } - } + VariantMap const ¶ms) const; }; } // namespace CellSystem From ceb7ed75b174274bb787342f28301a87405ad520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 20 Jul 2022 15:03:44 +0200 Subject: [PATCH 11/85] script_interface: Rewrite ComFixed module --- src/core/CMakeLists.txt | 1 - src/core/comfixed_global.cpp | 25 ----------------- src/core/comfixed_global.hpp | 27 ------------------ src/core/forces.cpp | 7 +++-- src/core/forces.hpp | 5 ++++ src/core/{ => galilei}/ComFixed.hpp | 27 +++++++++--------- src/python/espressomd/comfixed.py | 5 +++- .../{ => galilei}/ComFixed.hpp | 28 +++++++++++++++---- src/script_interface/galilei/initialize.cpp | 2 ++ src/script_interface/initialize.cpp | 2 -- testsuite/python/save_checkpoint.py | 2 ++ testsuite/python/test_checkpoint.py | 3 ++ 12 files changed, 57 insertions(+), 77 deletions(-) delete mode 100644 src/core/comfixed_global.cpp delete mode 100644 src/core/comfixed_global.hpp rename src/core/{ => galilei}/ComFixed.hpp (85%) rename src/script_interface/{ => galilei}/ComFixed.hpp (57%) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 673d7605754..5b57b88be1b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -24,7 +24,6 @@ set(Espresso_core_SRC bond_error.cpp cells.cpp collision.cpp - comfixed_global.cpp communication.cpp constraints.cpp dpd.cpp diff --git a/src/core/comfixed_global.cpp b/src/core/comfixed_global.cpp deleted file mode 100644 index 57304c40aa3..00000000000 --- a/src/core/comfixed_global.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2017-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 . - */ -/** \file - * - * Implementation of \ref comfixed_global.hpp - */ -#include "comfixed_global.hpp" - -ComFixed comfixed; diff --git a/src/core/comfixed_global.hpp b/src/core/comfixed_global.hpp deleted file mode 100644 index 17a079466e3..00000000000 --- a/src/core/comfixed_global.hpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2017-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_COMFIXED_GLOBAL_HPP -#define CORE_COMFIXED_GLOBAL_HPP - -#include "ComFixed.hpp" -#include "ParticleRange.hpp" - -extern ComFixed comfixed; - -#endif diff --git a/src/core/forces.cpp b/src/core/forces.cpp index 5e6c4d17db5..d2d247d8557 100644 --- a/src/core/forces.cpp +++ b/src/core/forces.cpp @@ -30,13 +30,13 @@ #include "cell_system/CellStructure.hpp" #include "cells.hpp" #include "collision.hpp" -#include "comfixed_global.hpp" #include "communication.hpp" #include "constraints.hpp" #include "electrostatics/icc.hpp" #include "electrostatics/p3m_gpu.hpp" #include "forcecap.hpp" #include "forces_inline.hpp" +#include "galilei/ComFixed.hpp" #include "grid_based_algorithms/electrokinetics.hpp" #include "grid_based_algorithms/lb_interface.hpp" #include "grid_based_algorithms/lb_particle_coupling.hpp" @@ -58,6 +58,9 @@ #include #include +#include + +std::shared_ptr comfixed = std::make_shared(); /** Initialize the forces for a ghost particle */ inline ParticleForce init_ghost_force(Particle const &) { return {}; } @@ -234,7 +237,7 @@ void force_calc(CellStructure &cell_structure, double time_step, double kT) { cell_structure.ghosts_reduce_forces(); // should be pretty late, since it needs to zero out the total force - comfixed.apply(comm_cart, particles); + comfixed->apply(comm_cart, particles); // Needs to be the last one to be effective forcecap_cap(particles); diff --git a/src/core/forces.hpp b/src/core/forces.hpp index 1f2bccd7eb8..8db114a9ece 100644 --- a/src/core/forces.hpp +++ b/src/core/forces.hpp @@ -32,9 +32,14 @@ #include "ParticleRange.hpp" #include "cell_system/CellStructure.hpp" +#include "galilei/ComFixed.hpp" #include +#include + +extern std::shared_ptr comfixed; + /** initialize real particle forces with thermostat forces and ghost particle forces with zero. */ void init_forces(const ParticleRange &particles, double time_step); diff --git a/src/core/ComFixed.hpp b/src/core/galilei/ComFixed.hpp similarity index 85% rename from src/core/ComFixed.hpp rename to src/core/galilei/ComFixed.hpp index a779f3aa4f8..86feee7e73b 100644 --- a/src/core/ComFixed.hpp +++ b/src/core/galilei/ComFixed.hpp @@ -16,8 +16,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef CORE_COMFIXED_HPP -#define CORE_COMFIXED_HPP + +#ifndef ESPRESSO_SRC_CORE_GALILEI_COMFIXED_HPP +#define ESPRESSO_SRC_CORE_GALILEI_COMFIXED_HPP + +#include "ParticleRange.hpp" #include #include @@ -28,15 +31,12 @@ #include #include -template class ComFixed { -public: - using TypeIndex = std::unordered_map; - +class ComFixed { private: - TypeIndex m_type_index; + std::unordered_map m_type_index; std::vector - local_type_forces(ParticleRange &particles) const { + local_type_forces(ParticleRange const &particles) const { std::vector ret(m_type_index.size(), Utils::Vector3d{}); for (auto const &p : particles) { @@ -50,7 +50,7 @@ template class ComFixed { return ret; } - std::vector local_type_masses(ParticleRange &particles) const { + std::vector local_type_masses(ParticleRange const &particles) const { std::vector ret(m_type_index.size(), 0.); for (auto const &p : particles) { @@ -65,18 +65,19 @@ template class ComFixed { } public: - template void set_fixed_types(Container const &c) { + void set_fixed_types(std::vector const &p_types) { m_type_index.clear(); int i = 0; - for (auto const &t : c) { - m_type_index[t] = i++; + for (auto const &p_type : p_types) { + m_type_index[p_type] = i++; } } + std::vector get_fixed_types() const { return Utils::keys(m_type_index); } void apply(boost::mpi::communicator const &comm, - ParticleRange &particles) const { + ParticleRange const &particles) const { /* Bail out early if there is nothing to do. */ if (m_type_index.empty()) return; diff --git a/src/python/espressomd/comfixed.py b/src/python/espressomd/comfixed.py index 4b6181702da..431268d771d 100644 --- a/src/python/espressomd/comfixed.py +++ b/src/python/espressomd/comfixed.py @@ -1,3 +1,4 @@ +# # Copyright (C) 2010-2022 The ESPResSo project # # This file is part of ESPResSo. @@ -14,6 +15,8 @@ # # 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 @@ -34,5 +37,5 @@ class ComFixed(ScriptInterfaceHelper): List of types for which the center of mass should be fixed. """ - _so_name = "ComFixed" + _so_name = "Galilei::ComFixed" _so_creation_policy = "GLOBAL" diff --git a/src/script_interface/ComFixed.hpp b/src/script_interface/galilei/ComFixed.hpp similarity index 57% rename from src/script_interface/ComFixed.hpp rename to src/script_interface/galilei/ComFixed.hpp index 967d7bbaa5d..ca1ea531616 100644 --- a/src/script_interface/ComFixed.hpp +++ b/src/script_interface/galilei/ComFixed.hpp @@ -19,26 +19,42 @@ * along with this program. If not, see . */ -#ifndef SCRIPT_INTERFACE_COM_FIXED_HPP -#define SCRIPT_INTERFACE_COM_FIXED_HPP +#ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_GALILEI_COMFIXED_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_GALILEI_COMFIXED_HPP #include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" -#include "core/comfixed_global.hpp" +#include "core/forces.hpp" +#include "core/galilei/ComFixed.hpp" +#include #include namespace ScriptInterface { +namespace Galilei { class ComFixed : public AutoParameters { + std::shared_ptr<::ComFixed> m_comfixed; + + void do_construct(VariantMap const ¶ms) override { + ::comfixed = m_comfixed = std::make_shared<::ComFixed>(); + for (auto const &p : params) { + do_set_parameter(p.first, p.second); + } + } + public: ComFixed() { add_parameters({{"types", - [](Variant const &v) { - comfixed.set_fixed_types(get_value>(v)); + [this](Variant const &v) { + auto const p_types = get_value>(v); + m_comfixed->set_fixed_types(p_types); }, - []() { return comfixed.get_fixed_types(); }}}); + [this]() { return m_comfixed->get_fixed_types(); }}}); } }; + +} // namespace Galilei } // namespace ScriptInterface #endif diff --git a/src/script_interface/galilei/initialize.cpp b/src/script_interface/galilei/initialize.cpp index 061ffcae8fb..c82232dc056 100644 --- a/src/script_interface/galilei/initialize.cpp +++ b/src/script_interface/galilei/initialize.cpp @@ -19,6 +19,7 @@ #include "initialize.hpp" +#include "ComFixed.hpp" #include "Galilei.hpp" namespace ScriptInterface { @@ -26,6 +27,7 @@ namespace Galilei { void initialize(Utils::Factory *om) { om->register_new("Galilei::Galilei"); + om->register_new("Galilei::ComFixed"); } } // namespace Galilei diff --git a/src/script_interface/initialize.cpp b/src/script_interface/initialize.cpp index 13ac41e2854..064c36191ca 100644 --- a/src/script_interface/initialize.cpp +++ b/src/script_interface/initialize.cpp @@ -21,7 +21,6 @@ #include "config.hpp" -#include "ComFixed.hpp" #include "CylindricalTransformationParameters.hpp" #include "accumulators/initialize.hpp" #include "analysis/initialize.hpp" @@ -71,7 +70,6 @@ void initialize(Utils::Factory *f) { Writer::initialize(f); #endif - f->register_new("ComFixed"); f->register_new( "CylindricalTransformationParameters"); } diff --git a/testsuite/python/save_checkpoint.py b/testsuite/python/save_checkpoint.py index 9a01aca1476..03b6f18f717 100644 --- a/testsuite/python/save_checkpoint.py +++ b/testsuite/python/save_checkpoint.py @@ -110,6 +110,8 @@ 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) +system.comfixed.types = [0, 2] + 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 diff --git a/testsuite/python/test_checkpoint.py b/testsuite/python/test_checkpoint.py index ff378330615..cece3cff4d8 100644 --- a/testsuite/python/test_checkpoint.py +++ b/testsuite/python/test_checkpoint.py @@ -566,6 +566,9 @@ def test_scafacos_dipoles(self): self.assertIn(key, state) self.assertEqual(state[key], reference[key], msg=f'for {key}') + def test_comfixed(self): + self.assertEqual(list(system.comfixed.types), [0, 2]) + @utx.skipIfMissingFeatures('COLLISION_DETECTION') def test_collision_detection(self): coldet = system.collision_detection From a7be06239280b240fdf6ed4494174c454e12129f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 20 Jul 2022 15:24:04 +0200 Subject: [PATCH 12/85] script_interface: Move CTP to math submodule --- src/python/espressomd/math.py | 8 +++-- src/script_interface/CMakeLists.txt | 1 + src/script_interface/initialize.cpp | 6 ++-- src/script_interface/math/CMakeLists.txt | 2 ++ .../CylindricalTransformationParameters.hpp | 23 ++++++------ src/script_interface/math/initialize.cpp | 33 +++++++++++++++++ src/script_interface/math/initialize.hpp | 35 +++++++++++++++++++ .../CylindricalLBProfileObservable.hpp | 4 +-- .../CylindricalPidProfileObservable.hpp | 5 +-- .../shapes/HollowConicalFrustum.hpp | 8 +++-- 10 files changed, 103 insertions(+), 22 deletions(-) create mode 100644 src/script_interface/math/CMakeLists.txt rename src/script_interface/{ => math}/CylindricalTransformationParameters.hpp (83%) create mode 100644 src/script_interface/math/initialize.cpp create mode 100644 src/script_interface/math/initialize.hpp diff --git a/src/python/espressomd/math.py b/src/python/espressomd/math.py index c6e068ec9e7..96ececc9cc3 100644 --- a/src/python/espressomd/math.py +++ b/src/python/espressomd/math.py @@ -1,3 +1,4 @@ +# # Copyright (C) 2010-2022 The ESPResSo project # # This file is part of ESPResSo. @@ -14,6 +15,7 @@ # # 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 @@ -36,6 +38,8 @@ class CylindricalTransformationParameters(ScriptInterfaceHelper): Notes ----- If you provide no arguments, the defaults above are set. - If you provide only a ``center`` and an ``axis``, an ``orientation`` will be automatically generated that is orthogonal to ``axis``. + If you provide only a ``center`` and an ``axis``, an ``orientation`` + will be automatically generated that is orthogonal to ``axis``. + """ - _so_name = "CylindricalTransformationParameters" + _so_name = "Math::CylindricalTransformationParameters" diff --git a/src/script_interface/CMakeLists.txt b/src/script_interface/CMakeLists.txt index 6fac648520d..6f17bf1bba2 100644 --- a/src/script_interface/CMakeLists.txt +++ b/src/script_interface/CMakeLists.txt @@ -37,6 +37,7 @@ add_subdirectory(interactions) add_subdirectory(lbboundaries) add_subdirectory(lees_edwards) add_subdirectory(magnetostatics) +add_subdirectory(math) add_subdirectory(mpiio) add_subdirectory(observables) add_subdirectory(pair_criteria) diff --git a/src/script_interface/initialize.cpp b/src/script_interface/initialize.cpp index 064c36191ca..0a27b04cdc3 100644 --- a/src/script_interface/initialize.cpp +++ b/src/script_interface/initialize.cpp @@ -21,7 +21,6 @@ #include "config.hpp" -#include "CylindricalTransformationParameters.hpp" #include "accumulators/initialize.hpp" #include "analysis/initialize.hpp" #include "bond_breakage/initialize.hpp" @@ -36,6 +35,7 @@ #include "lbboundaries/initialize.hpp" #include "lees_edwards/initialize.hpp" #include "magnetostatics/initialize.hpp" +#include "math/initialize.hpp" #include "mpiio/initialize.hpp" #include "observables/initialize.hpp" #include "pair_criteria/initialize.hpp" @@ -59,6 +59,7 @@ void initialize(Utils::Factory *f) { Interactions::initialize(f); LBBoundaries::initialize(f); LeesEdwards::initialize(f); + Math::initialize(f); MPIIO::initialize(f); Observables::initialize(f); PairCriteria::initialize(f); @@ -69,9 +70,6 @@ void initialize(Utils::Factory *f) { #ifdef H5MD Writer::initialize(f); #endif - - f->register_new( - "CylindricalTransformationParameters"); } } // namespace ScriptInterface diff --git a/src/script_interface/math/CMakeLists.txt b/src/script_interface/math/CMakeLists.txt new file mode 100644 index 00000000000..36dbb5a513c --- /dev/null +++ b/src/script_interface/math/CMakeLists.txt @@ -0,0 +1,2 @@ +target_sources(Espresso_script_interface + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/CylindricalTransformationParameters.hpp b/src/script_interface/math/CylindricalTransformationParameters.hpp similarity index 83% rename from src/script_interface/CylindricalTransformationParameters.hpp rename to src/script_interface/math/CylindricalTransformationParameters.hpp index 5cfbef164c3..3087026d332 100644 --- a/src/script_interface/CylindricalTransformationParameters.hpp +++ b/src/script_interface/math/CylindricalTransformationParameters.hpp @@ -19,18 +19,20 @@ * along with this program. If not, see . */ -#ifndef SCRIPT_INTERFACE_CYL_TRANSFORM_PARAMS_HPP -#define SCRIPT_INTERFACE_CYL_TRANSFORM_PARAMS_HPP +#ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_MATH_CYL_TRANSFORM_PARAMS_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_MATH_CYL_TRANSFORM_PARAMS_HPP #include "script_interface/auto_parameters/AutoParameters.hpp" #include "script_interface/get_value.hpp" +#include #include #include #include namespace ScriptInterface { +namespace Math { class CylindricalTransformationParameters : public AutoParameters { @@ -43,26 +45,25 @@ class CylindricalTransformationParameters {"orientation", AutoParameter::read_only, [this]() { return m_transform_params->orientation(); }}}); } - std::shared_ptr<::Utils::CylindricalTransformationParameters> - cyl_transform_params() const { - return m_transform_params; - } + + auto cyl_transform_params() const { return m_transform_params; } + void do_construct(VariantMap const ¶ms) override { auto n_params = params.size(); switch (n_params) { case 0: m_transform_params = - std::make_shared(); + std::make_shared<::Utils::CylindricalTransformationParameters>(); break; case 2: m_transform_params = - std::make_shared( + std::make_shared<::Utils::CylindricalTransformationParameters>( get_value(params, "center"), get_value(params, "axis")); break; case 3: m_transform_params = - std::make_shared( + std::make_shared<::Utils::CylindricalTransformationParameters>( get_value(params, "center"), get_value(params, "axis"), get_value(params, "orientation")); @@ -74,8 +75,10 @@ class CylindricalTransformationParameters } private: - std::shared_ptr + std::shared_ptr<::Utils::CylindricalTransformationParameters> m_transform_params; }; + +} // namespace Math } // namespace ScriptInterface #endif diff --git a/src/script_interface/math/initialize.cpp b/src/script_interface/math/initialize.cpp new file mode 100644 index 00000000000..266e60c8c78 --- /dev/null +++ b/src/script_interface/math/initialize.cpp @@ -0,0 +1,33 @@ +/* + * 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 "CylindricalTransformationParameters.hpp" + +namespace ScriptInterface { +namespace Math { + +void initialize(Utils::Factory *om) { + om->register_new( + "Math::CylindricalTransformationParameters"); +} + +} // namespace Math +} // namespace ScriptInterface diff --git a/src/script_interface/math/initialize.hpp b/src/script_interface/math/initialize.hpp new file mode 100644 index 00000000000..32d945ca3a1 --- /dev/null +++ b/src/script_interface/math/initialize.hpp @@ -0,0 +1,35 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_MATH_INITIALIZE_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_MATH_INITIALIZE_HPP + +#include + +#include + +namespace ScriptInterface { +namespace Math { + +void initialize(Utils::Factory *om); + +} // namespace Math +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/observables/CylindricalLBProfileObservable.hpp b/src/script_interface/observables/CylindricalLBProfileObservable.hpp index 9f9e64edae8..b59e2bc31a1 100644 --- a/src/script_interface/observables/CylindricalLBProfileObservable.hpp +++ b/src/script_interface/observables/CylindricalLBProfileObservable.hpp @@ -28,7 +28,7 @@ #include "core/observables/CylindricalLBProfileObservable.hpp" #include "script_interface/get_value.hpp" -#include "script_interface/CylindricalTransformationParameters.hpp" +#include "script_interface/math/CylindricalTransformationParameters.hpp" #include @@ -143,7 +143,7 @@ class CylindricalLBProfileObservable private: std::shared_ptr m_observable; - std::shared_ptr m_transform_params; + std::shared_ptr m_transform_params; }; } /* namespace Observables */ diff --git a/src/script_interface/observables/CylindricalPidProfileObservable.hpp b/src/script_interface/observables/CylindricalPidProfileObservable.hpp index 0d0244bfeef..b459e3a6d09 100644 --- a/src/script_interface/observables/CylindricalPidProfileObservable.hpp +++ b/src/script_interface/observables/CylindricalPidProfileObservable.hpp @@ -27,7 +27,8 @@ #include "Observable.hpp" #include "core/observables/CylindricalPidProfileObservable.hpp" -#include +#include "script_interface/math/CylindricalTransformationParameters.hpp" + #include #include @@ -141,7 +142,7 @@ class CylindricalPidProfileObservable private: std::shared_ptr m_observable; - std::shared_ptr m_transform_params; + std::shared_ptr m_transform_params; }; } /* namespace Observables */ diff --git a/src/script_interface/shapes/HollowConicalFrustum.hpp b/src/script_interface/shapes/HollowConicalFrustum.hpp index 21c646765db..3f95907500b 100644 --- a/src/script_interface/shapes/HollowConicalFrustum.hpp +++ b/src/script_interface/shapes/HollowConicalFrustum.hpp @@ -19,8 +19,11 @@ #ifndef ESPRESSO_HOLLOW_CONICAL_FRUSTUM_HPP #define ESPRESSO_HOLLOW_CONICAL_FRUSTUM_HPP + #include "Shape.hpp" -#include + +#include "script_interface/math/CylindricalTransformationParameters.hpp" + #include namespace ScriptInterface { @@ -85,7 +88,8 @@ class HollowConicalFrustum : public Shape { private: std::shared_ptr<::Shapes::HollowConicalFrustum> m_hollow_conical_frustum; - std::shared_ptr m_cyl_transform_params; + std::shared_ptr + m_cyl_transform_params; }; } /* namespace Shapes */ From 74ec1fe029ab54acb8d4ff7b86eeb177165e34d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 22 Jul 2022 14:01:03 +0200 Subject: [PATCH 13/85] script_interface: Rewrite ParticleHandle --- maintainer/benchmarks/lj.py | 6 +- src/core/constraints/ShapeBasedConstraint.cpp | 1 + src/core/particle_data.cpp | 8 +- src/core/particle_data.hpp | 68 +- src/core/particle_node.cpp | 7 + src/python/espressomd/grid.pxd | 34 - src/python/espressomd/particle_data.pxd | 152 +- src/python/espressomd/particle_data.pyx | 1527 +++++------------ src/python/espressomd/utils.pxd | 4 - src/python/espressomd/visualization.py | 10 +- src/script_interface/CMakeLists.txt | 1 + src/script_interface/analysis/Analysis.cpp | 2 + src/script_interface/initialize.cpp | 2 + .../particle_data/CMakeLists.txt | 4 + .../particle_data/ParticleHandle.cpp | 490 ++++++ .../particle_data/ParticleHandle.hpp | 52 + .../particle_data/initialize.cpp | 32 + .../particle_data/initialize.hpp | 35 + testsuite/python/engine_lb.py | 7 - testsuite/python/mdanalysis.py | 29 +- testsuite/python/particle.py | 67 +- testsuite/python/particle_slice.py | 33 +- testsuite/python/polymer_diamond.py | 2 +- testsuite/python/rotational_dynamics.py | 2 +- testsuite/python/virtual_sites_relative.py | 2 +- 25 files changed, 1162 insertions(+), 1415 deletions(-) delete mode 100644 src/python/espressomd/grid.pxd create mode 100644 src/script_interface/particle_data/CMakeLists.txt create mode 100644 src/script_interface/particle_data/ParticleHandle.cpp create mode 100644 src/script_interface/particle_data/ParticleHandle.hpp create mode 100644 src/script_interface/particle_data/initialize.cpp create mode 100644 src/script_interface/particle_data/initialize.hpp diff --git a/maintainer/benchmarks/lj.py b/maintainer/benchmarks/lj.py index a32737e02e2..083aee9f440 100644 --- a/maintainer/benchmarks/lj.py +++ b/maintainer/benchmarks/lj.py @@ -105,9 +105,11 @@ system.bonded_inter.add(hb) for _ in range(0, n_part, 2): pos = np.random.random(3) * system.box_l + vec = np.random.random(3) + vec /= np.linalg.norm(vec) p1 = system.part.add(pos=pos) - p2 = system.part.add(pos=pos + np.random.random(3) / np.sqrt(3)) - system.part[p1].add_bond((hb, p2)) + p2 = system.part.add(pos=pos + vec * hb.r_0) + p1.add_bond((hb, p2)) # Warmup Integration ############################################################# diff --git a/src/core/constraints/ShapeBasedConstraint.cpp b/src/core/constraints/ShapeBasedConstraint.cpp index 50e85159c38..9e46ce5066b 100644 --- a/src/core/constraints/ShapeBasedConstraint.cpp +++ b/src/core/constraints/ShapeBasedConstraint.cpp @@ -154,6 +154,7 @@ void ShapeBasedConstraint::add_energy(const Particle &p, runtimeErrorMsg() << "Constraint violated by particle " << p.id(); } } + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) if (part_rep.type() >= 0) obs_energy.add_non_bonded_contribution(p.type(), part_rep.type(), energy); } diff --git a/src/core/particle_data.cpp b/src/core/particle_data.cpp index e388ecda0b5..94aac35a134 100644 --- a/src/core/particle_data.cpp +++ b/src/core/particle_data.cpp @@ -424,7 +424,7 @@ void set_particle_f(int part, const Utils::Vector3d &f) { &ParticleForce::f>(part, f); } -#if defined(MASS) +#ifdef MASS void set_particle_mass(int part, double mass) { mpi_update_particle_property(part, mass); } @@ -506,13 +506,11 @@ void set_particle_vs_relative(int part, int vs_relative_to, double vs_distance, } #endif -void set_particle_q(int part, double q) { #ifdef ELECTROSTATICS +void set_particle_q(int part, double q) { mpi_update_particle_property(part, q); -#endif } - -#ifndef ELECTROSTATICS +#else const constexpr double ParticleProperties::q; #endif diff --git a/src/core/particle_data.hpp b/src/core/particle_data.hpp index aa935abe2ac..03aec9e1717 100644 --- a/src/core/particle_data.hpp +++ b/src/core/particle_data.hpp @@ -21,14 +21,9 @@ #ifndef CORE_PARTICLE_DATA_HPP #define CORE_PARTICLE_DATA_HPP /** \file - * Particles property access. + * Particles property update. * - * This file contains everything related to particle properties. If you want to - * add a new property to the particles, it is probably a good idea to modify - * Particle to give scripts access to that property. You always have to modify - * two positions: first the print section, where you should add your new - * data at the end, and second the read section where you have to find a nice - * and short name for your property to appear in the Python code. + * This file contains everything related to updating particle properties. */ #include "config.hpp" @@ -67,11 +62,13 @@ void set_particle_swimming(int part, ParticleParametersSwimming swim); */ void set_particle_f(int part, const Utils::Vector3d &F); +#ifdef MASS /** Call only on the head node: set particle mass. * @param part the particle. * @param mass its new mass. */ void set_particle_mass(int part, double mass); +#endif #ifdef ROTATIONAL_INERTIA /** Call only on the head node: set particle rotational inertia. @@ -81,6 +78,7 @@ void set_particle_mass(int part, double mass); void set_particle_rotational_inertia(int part, Utils::Vector3d const &rinertia); #endif +#ifdef ROTATION /** Call only on the head node: Specifies whether a particle's rotational * degrees of freedom are integrated or not. If set to zero, the content of * the torque and omega variables are meaningless @@ -96,12 +94,15 @@ void set_particle_rotation(int part, Utils::Vector3i const &flag); * @param angle rotation angle */ void rotate_particle(int part, const Utils::Vector3d &axis, double angle); +#endif +#ifdef ELECTROSTATICS /** Call only on the head node: set particle charge. * @param part the particle. * @param q its new charge. */ void set_particle_q(int part, double q); +#endif #ifdef LB_ELECTROHYDRODYNAMICS /** Call only on the head node: set particle electrophoretic mobility. @@ -287,57 +288,4 @@ void auto_exclusions(int distance); /** Rescale all particle positions in direction @p dir by a factor @p scale. */ void mpi_rescale_particles(int dir, double scale); -// The following functions are used by the python interface to obtain -// properties of a particle, which are only compiled in in some configurations -// This is needed, because cython does not support conditional compilation -// within a ctypedef definition - -#ifdef VIRTUAL_SITES_RELATIVE -inline Utils::Quaternion get_particle_vs_quat(Particle const *p) { - 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->vs_relative().to_particle_id; - vs_distance = p->vs_relative().distance; - return p->vs_relative().rel_orientation; -} -#endif - -#ifdef EXTERNAL_FORCES -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 -inline Utils::Vector3d get_particle_gamma(Particle const *p) { - return p->gamma(); -} -#else -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->gamma_rot(); -} -#else -inline double get_particle_gamma_rot(Particle const *p) { - return p->gamma_rot(); -} -#endif // PARTICLE_ANISOTROPY -#endif // ROTATION -#endif // THERMOSTAT_PER_PARTICLE - -#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 // ROTATION - #endif diff --git a/src/core/particle_node.cpp b/src/core/particle_node.cpp index d112f1ac90c..96d1768b3bd 100644 --- a/src/core/particle_node.cpp +++ b/src/core/particle_node.cpp @@ -43,6 +43,7 @@ #include #include +#include #include #include #include @@ -435,6 +436,12 @@ void 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)); } +#ifndef __FAST_MATH__ + if (std::isnan(pos[0]) or std::isnan(pos[1]) or std::isnan(pos[2]) or + std::isinf(pos[0]) or std::isinf(pos[1]) or std::isinf(pos[2])) { + throw std::domain_error("Particle position must be finite"); + } +#endif // __FAST_MATH__ if (particle_exists(p_id)) { mpi_place_particle(get_particle_node(p_id), p_id, pos); } else { diff --git a/src/python/espressomd/grid.pxd b/src/python/espressomd/grid.pxd deleted file mode 100644 index f2129ddef66..00000000000 --- a/src/python/espressomd/grid.pxd +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (C) 2010-2022 The ESPResSo project -# -# This file is part of ESPResSo. -# -# ESPResSo is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# ESPResSo is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -from libcpp cimport bool - -from .utils cimport Vector3i, Vector3d - -cdef extern from "grid.hpp": - - cppclass BoxGeometry: - void set_periodic(unsigned coord, bool value) - bool periodic(unsigned coord) - const Vector3d & length() - void set_length(Vector3d) - Vector3d get_mi_vector(Vector3d, Vector3d) - Vector3d velocity_difference(Vector3d, Vector3d, Vector3d, Vector3d) - - BoxGeometry box_geo - - Vector3d folded_position(Vector3d, const BoxGeometry &) - Vector3d unfolded_position(Vector3d, Vector3i, const Vector3d & ) diff --git a/src/python/espressomd/particle_data.pxd b/src/python/espressomd/particle_data.pxd index cca355004fb..1a10e7d064c 100644 --- a/src/python/espressomd/particle_data.pxd +++ b/src/python/espressomd/particle_data.pxd @@ -17,147 +17,15 @@ # along with this program. If not, see . # # Here we create something to handle particles -from .utils cimport Vector4d, Vector3d, Vector3i, Span, Quaternion +from .utils cimport Vector4d, Vector3d, Vector3i from libcpp cimport bool from libcpp.vector cimport vector # import std::vector as vector include "myconfig.pxi" -from .utils cimport Span - - -cdef extern from "Particle.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 "Particle": - Vector3d calc_dip() - int type() - int id() - double mass() - int mol_id() - Vector3d pos() - Vector3d calc_director() - Vector3d force() - Vector3d v() - Vector3i image_box() - 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() - vector[int] exclusions_as_vector() except + - bool has_exclusion(int pid) except + - particle_parameters_swimming swimming() - -cdef extern from "particle_data.hpp": - # Setter/getter/modifier functions functions +cdef extern from "particle_node.hpp": void prefetch_particle_data(vector[int] ids) - 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 MASS: - void set_particle_mass(int part, double mass) - - IF ROTATIONAL_INERTIA: - void set_particle_rotational_inertia(int part, const Vector3d & rinertia) - - IF ROTATION: - void set_particle_rotation(int part, const Vector3i & flag) - Vector3i get_particle_rotation(const particle * p) - - void set_particle_q(int part, double q) - - IF LB_ELECTROHYDRODYNAMICS: - void set_particle_mu_E(int part, const Vector3d & mu_E) - - void set_particle_type(int part, int type) - - void set_particle_mol_id(int part, int mid) - - IF ROTATION: - void set_particle_quat(int part, const Quaternion[double] & quat) - 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) - - IF DIPOLES: - void set_particle_dip(int part, const Vector3d & dip) - void set_particle_dipm(int part, double dipm) - - IF VIRTUAL_SITES: - void set_particle_virtual(int part, int isVirtual) - - IF THERMOSTAT_PER_PARTICLE: - IF PARTICLE_ANISOTROPY: - void set_particle_gamma(int part, const Vector3d & gamma) - Vector3d get_particle_gamma(const particle * p) - ELSE: - void set_particle_gamma(int part, double gamma) - double get_particle_gamma(const particle * p) - - IF ROTATION: - IF PARTICLE_ANISOTROPY: - void set_particle_gamma_rot(int part, const Vector3d & gamma_rot) - Vector3d get_particle_gamma_rot(const particle * p) - ELSE: - void set_particle_gamma_rot(int part, double gamma) - double get_particle_gamma_rot(const particle * p) - - IF VIRTUAL_SITES_RELATIVE: - Quaternion[double] get_particle_vs_relative(const particle * p, int & vs_relative_to, double & vs_distance) - Quaternion[double] get_particle_vs_quat(const particle * p) - 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) - - IF EXTERNAL_FORCES: - IF ROTATION: - void set_particle_ext_torque(int part, const Vector3d & torque) - - void set_particle_ext_force(int part, const Vector3d & force) - - 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) - void add_particle_bond(int part, Span[const int] bond) - const vector[BondView] & get_particle_bonds(int part) - - IF EXCLUSIONS: - void remove_particle_exclusion(int part1, int part2) except + - void add_particle_exclusion(int part1, int part2) except + - - IF ENGINE: - void set_particle_swimming(int part, particle_parameters_swimming swim) - - void remove_all_bonds_to(int part) - -cdef extern from "particle_node.hpp": void place_particle(int p_id, const Vector3d & pos) except + void remove_particle(int p_id) except + @@ -168,33 +36,17 @@ cdef extern from "particle_node.hpp": int get_particle_node(int p_id) except + - const particle & get_particle_data(int p_id) except + - vector[int] get_particle_ids() except + int get_maximal_particle_id() int get_n_part() -cdef extern from "virtual_sites.hpp": - IF VIRTUAL_SITES_RELATIVE == 1: - 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) - Vector3d convert_vector_space_to_body(const particle & p, const Vector3d & v) - void rotate_particle(int pid, const Vector3d & axis, double angle) - cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": int max_seen_particle_type cdef extern from "bonded_interactions/rigid_bond.hpp": extern int n_rigidbonds -cdef class ParticleHandle: - cdef public int _id - cdef const particle * particle_data - cdef int update_particle_data(self) except -1 - cdef class _ParticleSliceImpl: cdef public id_selection cdef int _chunk_size diff --git a/src/python/espressomd/particle_data.pyx b/src/python/espressomd/particle_data.pyx index e128f312911..fc7fe9d4f0d 100644 --- a/src/python/espressomd/particle_data.pyx +++ b/src/python/espressomd/particle_data.pyx @@ -29,112 +29,29 @@ from copy import copy import collections import functools 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 make_array_locked, check_type_or_throw_except from .utils cimport Vector3i, Vector3d, Vector4d from .utils cimport make_Vector3d -from .utils cimport make_Vector3i -from .grid cimport box_geo, folded_position, unfolded_position +from .code_features import assert_features, has_features +from .script_interface import script_interface_register, ScriptInterfaceHelper -# 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) -particle_attributes = [] -for d in dir(ParticleHandle): - if type(getattr(ParticleHandle, d)) == type(ParticleHandle.pos): - if d != "pos_folded": - particle_attributes.append(d) - -cdef class ParticleHandle: - def __cinit__(self, int _id): - self._id = _id - - cdef int update_particle_data(self) except -1: - self.particle_data = &get_particle_data(self._id) - - def to_dict(self): - """ - Returns the particle's attributes as a dictionary. - - It includes the content of ``particle_attributes``, minus a few exceptions: - - - :attr:`~ParticleHandle.dip`, :attr:`~ParticleHandle.director`: - Setting only the director will overwrite the orientation of the - particle around the axis parallel to dipole moment/director. - Quaternions contain the full info. - - :attr:`~ParticleHandle.image_box`, :attr:`~ParticleHandle.node` - - """ - - pickle_attr = copy(particle_attributes) - for i in ["director", "dip", "image_box", "node", "lees_edwards_flag"]: - if i in pickle_attr: - pickle_attr.remove(i) - IF MASS == 0: - pickle_attr.remove("mass") - pdict = {} - - for property_ in pickle_attr: - pdict[property_] = ParticleHandle( - self.id).__getattribute__(property_) - return pdict - - def __str__(self): - res = collections.OrderedDict() - # Id and pos first, then the rest - res["id"] = self.id - res["pos"] = self.pos - for a in particle_attributes: - tmp = getattr(self, a) - # Remove array type names from output - if isinstance(tmp, array_locked): - res[a] = tuple(tmp) - else: - res[a] = tmp - - # Get rid of OrderedDict in output - return str(res).replace("OrderedDict(", "ParticleHandle(") - - # The individual attributes of a particle are implemented as properties. - property id: - """Integer particle id - - """ - - def __get__(self): - self.update_particle_data() - return self.particle_data.id() - - # The individual attributes of a particle are implemented as properties. - - # Particle Type - property type: - """ - The particle type for nonbonded interactions. +@script_interface_register +class ParticleHandle(ScriptInterfaceHelper): + """ + Attributes + ---------- + id: :obj:`int` + Particle identifier. - type : :obj:`int` + type: :obj:`int` + The particle type for non-bonded interactions. .. note:: The value of ``type`` has to be an integer >= 0. - """ - - def __set__(self, _type): - if is_valid_type(_type, int) and _type >= 0: - set_particle_type(self._id, _type) - else: - raise ValueError("type must be an integer >= 0") - - def __get__(self): - self.update_particle_data() - return self.particle_data.type() - - # Particle MolId - property mol_id: - """ - The molecule id of the Particle. - - mol_id : :obj:`int` + mol_id: :obj:`int` + The molecule id of the particle. The particle ``mol_id`` is used to differentiate between particles belonging to different molecules, e.g. when virtual @@ -144,49 +61,15 @@ cdef class ParticleHandle: .. note:: The value of ``mol_id`` has to be an integer >= 0. - """ - - def __set__(self, _mol_id): - if is_valid_type(_mol_id, int) and _mol_id >= 0: - set_particle_mol_id(self._id, _mol_id) - else: - raise ValueError("mol_id must be an integer >= 0") - - def __get__(self): - self.update_particle_data() - return self.particle_data.mol_id() - - # Position - property pos: - """ + pos: (3,) array_like of :obj:`float` The unwrapped (not folded into central box) particle position. - pos : (3,) array_like of :obj:`float` - - """ - - def __set__(self, _pos): - if np.isnan(_pos).any() or np.isinf(_pos).any(): - raise ValueError("invalid particle position") - check_type_or_throw_except( - _pos, 3, float, "Position must be 3 floats") - place_particle(self._id, make_Vector3d(_pos)) - - def __get__(self): - self.update_particle_data() - return make_array_locked(unfolded_position( - self.particle_data.pos(), - self.particle_data.image_box(), - box_geo.length())) - - property pos_folded: - """ + pos_folded: (3,) array_like of :obj:`float` The wrapped (folded into central box) position vector of a particle. - pos : (3,) array_like of :obj:`float` - .. note:: - Setting the folded position is ambiguous and is thus not possible, please use ``pos``. + Setting the folded position is ambiguous and is thus not possible, + please use ``pos`` instead. Examples -------- @@ -206,110 +89,40 @@ cdef class ParticleHandle: [0.0, 0.0, 0.0] [5.0, 0.0, 0.0] - """ - - def __set__(self, pos_folded): - raise AttributeError( - "setting a folded position is not implemented") - - def __get__(self): - self.update_particle_data() - return make_array_locked(folded_position( - self.particle_data.pos(), box_geo)) - - property image_box: - """ + image_box: (3,) array_like of :obj:`int` The image box the particles is in. This is the number of times the particle position has been folded by the box length in each direction. - """ - def __get__(self): - self.update_particle_data() - cdef Vector3i image_box = self.particle_data.image_box() - return array_locked([image_box[0], image_box[1], image_box[2]]) - - property lees_edwards_offset: - """ + lees_edwards_offset: :obj:`float` The accumulated Lees-Edwards offset. Can be used to reconstruct continuous trajectories. - offset : (3,) array_like of :obj:`float` - - """ - - def __get__(self): - self.update_particle_data() - return self.particle_data.lees_edwards_offset() - - def __set__(self, value): - set_particle_lees_edwards_offset(self._id, value) - - property lees_edwards_flag: - """ + lees_edwards_flag: :obj:`int` The Lees-Edwards flag that indicate if the particle crossed the upper or lower boundary. - """ - - def __get__(self): - self.update_particle_data() - return self.particle_data.lees_edwards_flag() - - # Velocity - property v: - """ + v: (3,) array_like of :obj:`float` The particle velocity in the lab frame. - v : (3,) array_like of :obj:`float` - .. note:: - The velocity remains variable and will be changed during integration. - - """ - - def __set__(self, _v): - check_type_or_throw_except( - _v, 3, float, "Velocity has to be floats") - set_particle_v(self._id, make_Vector3d(_v)) - - def __get__(self): - self.update_particle_data() - return make_array_locked(self.particle_data.v()) + The velocity will be updated during integration. - # Force - property f: - """ + f: (3,) array_like of :obj:`float` The instantaneous force acting on this particle. - f : (3,) array_like of :obj:`float` - .. note:: - Whereas the velocity is modified with respect to the velocity you set - upon integration, the force it recomputed during the integration step and any - force set in this way is immediately lost at the next integration step. + The force is recomputed during the integration step and any force + set in this way is immediately lost at the next integration step. - """ - - def __set__(self, _f): - check_type_or_throw_except(_f, 3, float, "Force has to be floats") - set_particle_f(self._id, make_Vector3d(_f)) - - def __get__(self): - self.update_particle_data() - return make_array_locked(self.particle_data.force()) - - property bonds: - """ + bonds: The bonds stored by this particle. Note that bonds are only stored by one partner. You need to define a bonded interaction. - bonds : list/tuple of tuples/lists - A bond tuple is specified as a bond identifier associated with - a particle ``(bond_ID, part_ID)``. A single particle may contain - multiple such tuples. + a particle ``(bond_ID, (*part_ID,))``. A single particle may contain + multiple bonds. See Also -------- @@ -319,889 +132,440 @@ cdef class ParticleHandle: .. note:: Bond ids have to be an integer >= 0. - """ - - def __set__(self, _bonds): - # Assigning to the bond property means replacing the existing value - # i.e., we delete all existing bonds - delete_particle_bonds(self._id) - - # Empty list? only delete - if _bonds: - nlvl = nesting_level(_bonds) - if nlvl == 1: # Single item - self.add_bond(_bonds) - elif nlvl == 2: # List of items - for bond in _bonds: - self.add_bond(bond) - else: - raise ValueError( - "Bonds have to specified as lists of tuples/lists or a single list.") - - 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: - bond_id = part_bond.bond_id() - partners = part_bond.partner_ids() - partner_ids = [partners[i] for i in range(partners.size())] - bonds.append((BondedInteractions()[bond_id], *partner_ids)) - - return tuple(bonds) - - property node: - """ + node: (3,) array_like of :obj:`int` The node the particle is on, identified by its MPI rank. - """ - - def __get__(self): - return get_particle_node(self._id) - # Properties that exist only when certain features are activated - # MASS - property mass: - """ + mass: :obj:`float` Particle mass. - mass : :obj:`float` - - See Also - -------- - :meth:`espressomd.thermostat.Thermostat.set_langevin` : Setting the parameters of the Langevin thermostat - - """ - - def __set__(self, _mass): - IF MASS == 1: - check_type_or_throw_except( - _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.") - - def __get__(self): - self.update_particle_data() - return self.particle_data.mass() - - IF ROTATION: - property omega_lab: - """ - The particle angular velocity the lab frame. - - omega_lab : (3,) array_like of :obj:`float` - - .. note:: - This needs the feature ``ROTATION``. - - If you set the angular velocity of the particle in the lab - frame, the orientation of the particle - (:attr:`~espressomd.particle_data.ParticleHandle.quat`) must be - set before setting ``omega_lab``, otherwise the conversion from - lab to body frame will not be handled properly. - - See Also - --------- - :attr:`~espressomd.particle_data.ParticleHandle.omega_body` - - """ - - def __set__(self, _o): - check_type_or_throw_except( - _o, 3, float, "Omega_lab has to be 3 floats.") - set_particle_omega_lab(self._id, make_Vector3d(_o)) - - def __get__(self): - self.update_particle_data() - return array_locked( - self.convert_vector_body_to_space(self.omega_body)) - - property quat: - """ - Quaternion representation of the particle rotational position. - - quat : (4,) array_like of :obj:`float` - - .. note:: - This needs the feature ``ROTATION``. - - """ - - def __set__(self, _q): - 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) - - def __get__(self): - self.update_particle_data() - - cdef Quaternion[double] q = self.particle_data.quat() - return array_locked([q[0], q[1], q[2], q[3]]) - - property director: - """ - The particle director. - - The ``director`` defines the the z-axis in the body-fixed frame. - If particle rotations happen, the director, i.e., the body-fixed - coordinate system co-rotates. Properties such as the angular - velocity :attr:`espressomd.particle_data.ParticleHandle.omega_body` - are evaluated in this body-fixed coordinate system. - When using particle dipoles, the dipole moment is co-aligned with - the particle director. Setting the director thus modifies the - dipole moment orientation (:attr:`espressomd.particle_data.ParticleHandle.dip`) - and vice versa. - See also :ref:`Rotational degrees of freedom and particle anisotropy`. - - director : (3,) array_like of :obj:`float` - - .. note:: - This needs the feature ``ROTATION``. - - """ - - def __set__(self, _d): - check_type_or_throw_except( - _d, 3, float, "Particle director has to be 3 floats.") - set_particle_director(self._id, make_Vector3d(_d)) - - def __get__(self): - self.update_particle_data() - return make_array_locked(self.particle_data.calc_director()) - - property omega_body: - """ - The particle angular velocity in body frame. - - omega_body : (3,) array_like of :obj:`float` - - This property sets the angular momentum of this particle in the - particles co-rotating frame (or body frame). - - .. note:: - This needs the feature ``ROTATION``. - - """ - - def __set__(self, _o): - check_type_or_throw_except( - _o, 3, float, "Omega_body has to be 3 floats.") - set_particle_omega_body(self._id, make_Vector3d(_o)) + omega_lab: (3,) array_like of :obj:`float` + The particle angular velocity the lab frame. - def __get__(self): - self.update_particle_data() - return make_array_locked(self.particle_data.omega()) - - property torque_lab: - """ - The particle torque in the lab frame. + .. note:: + This needs the feature ``ROTATION``. - torque_lab : (3,) array_like of :obj:`float` + If you set the angular velocity of the particle in the lab + frame, the orientation of the particle + (:attr:`~espressomd.particle_data.ParticleHandle.quat`) must be + set before setting ``omega_lab``, otherwise the conversion from + lab to body frame will not be handled properly. - This property defines the torque of this particle - in the fixed frame (or laboratory frame). + See Also + --------- + :attr:`~espressomd.particle_data.ParticleHandle.omega_body` - .. note:: - The orientation of the particle - (:attr:`~espressomd.particle_data.ParticleHandle.quat`) must be - set before setting this property, otherwise the conversion from - lab to body frame will not be handled properly. + quat: (4,) array_like of :obj:`float` + Quaternion representation of the particle rotational position. - """ + .. note:: + This needs the feature ``ROTATION``. + + director: (3,) array_like of :obj:`float` + The particle director. + + The ``director`` defines the the z-axis in the body-fixed frame. + If particle rotations happen, the director, i.e., the body-fixed + coordinate system co-rotates. Properties such as the angular + velocity :attr:`espressomd.particle_data.ParticleHandle.omega_body` + are evaluated in this body-fixed coordinate system. + When using particle dipoles, the dipole moment is co-aligned with + the particle director. Setting the director thus modifies the + dipole moment orientation (:attr:`espressomd.particle_data.ParticleHandle.dip`) + and vice versa. + See also :ref:`Rotational degrees of freedom and particle anisotropy`. - def __set__(self, _t): - check_type_or_throw_except( - _t, 3, float, "Torque has to be 3 floats.") - set_particle_torque_lab(self._id, make_Vector3d(_t)) + .. note:: + This needs the feature ``ROTATION``. - def __get__(self): - self.update_particle_data() - cdef Vector3d torque_body - cdef Vector3d torque_space - torque_body = self.particle_data.torque() - torque_space = convert_vector_body_to_space( - dereference(self.particle_data), torque_body) + omega_body: (3,) array_like of :obj:`float` + The particle angular velocity in body frame. - return make_array_locked(torque_space) + This property sets the angular momentum of this particle in the + particles co-rotating frame (or body frame). - IF ROTATIONAL_INERTIA: - property rinertia: - """ - The particle rotational inertia. + .. note:: + This needs the feature ``ROTATION``. - rinertia : (3,) array_like of :obj:`float` + torque_lab: (3,) array_like of :obj:`float` + The particle torque in the lab frame. - Sets the diagonal elements of this particles rotational inertia - tensor. These correspond with the inertial moments along the - coordinate axes in the particle's co-rotating coordinate system. - When the particle's quaternions are set to ``[1, 0, 0, 0,]``, the - co-rotating and the fixed (lab) frames are co-aligned. + This property defines the torque of this particle + in the fixed frame (or laboratory frame). - .. note:: - This needs the feature ``ROTATIONAL_INERTIA``. + .. note:: + The orientation of the particle + (:attr:`~espressomd.particle_data.ParticleHandle.quat`) must be + set before setting this property, otherwise the conversion from + lab to body frame will not be handled properly. - """ + rinertia: (3,) array_like of :obj:`float` + The particle rotational inertia. - def __set__(self, _rinertia): - check_type_or_throw_except( - _rinertia, 3, float, "Rotation_inertia has to be 3 floats.") - set_particle_rotational_inertia( - self._id, make_Vector3d(_rinertia)) + Sets the diagonal elements of this particles rotational inertia + tensor. These correspond with the inertial moments along the + coordinate axes in the particle's co-rotating coordinate system. + When the particle's quaternions are set to ``[1, 0, 0, 0,]``, the + co-rotating and the fixed (lab) frames are co-aligned. - def __get__(self): - self.update_particle_data() - return make_array_locked(self.particle_data.rinertia()) + .. note:: + This needs the feature ``ROTATIONAL_INERTIA``. - # Charge - property q: - """ + q: :obj:`float` Particle charge. - q : :obj:`float` - .. note:: This needs the feature ``ELECTROSTATICS``. - """ - - def __set__(self, q): - check_type_or_throw_except( - q, 1, float, "Charge has to be a float.") - set_particle_q(self._id, q) - - def __get__(self): - self.update_particle_data() - return self.particle_data.q() - - IF LB_ELECTROHYDRODYNAMICS: - property mu_E: - """ - Particle electrophoretic velocity. - - mu_E : :obj:`float` - - This effectively acts as a velocity offset between - a lattice-Boltzmann fluid and the particle. Has only - an effect if LB is turned on. - - .. note:: - This needs the feature ``LB_ELECTROHYDRODYNAMICS``. - - """ + mu_E: :obj:`float` + Particle electrophoretic velocity. - def __set__(self, mu_E): - check_type_or_throw_except( - mu_E, 3, float, "mu_E has to be 3 floats.") - set_particle_mu_E(self._id, make_Vector3d(mu_E)) + This effectively acts as a velocity offset between + a lattice-Boltzmann fluid and the particle. Has only + an effect if LB is turned on. - def __get__(self): - self.update_particle_data() - return make_array_locked(self.particle_data.mu_E()) + .. note:: + This needs the feature ``LB_ELECTROHYDRODYNAMICS``. - property virtual: - """Virtual flag. + virtual: :obj:`bool` + Virtual flag. Declares the particles as virtual (``True``) or non-virtual (``False``, default). - virtual : :obj:`bool` - .. note:: This needs the feature ``VIRTUAL_SITES`` - """ - - def __set__(self, _v): - IF VIRTUAL_SITES: - if is_valid_type(_v, int): - set_particle_virtual(self._id, < bint > _v) - else: - raise ValueError("virtual must be a boolean.") - ELSE: - if _v: - raise AttributeError( - "To make a particle virtual, VIRTUAL_SITES has to be defined in myconfig.hpp") - - def __get__(self): - self.update_particle_data() - return self.particle_data.is_virtual() - - IF VIRTUAL_SITES_RELATIVE: - property vs_quat: - """ Virtual site quaternion. - - This quaternion describes the virtual particles orientation in the - body fixed frame of the related real particle. - - vs_quat : (4,) array_like of :obj:`float` - - .. note:: - This needs the feature ``VIRTUAL_SITES_RELATIVE``. - - """ - - 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] - set_particle_vs_quat(self._id, _q) - - def __get__(self): - self.update_particle_data() - cdef Quaternion[double] q - q = get_particle_vs_quat(self.particle_data) - return array_locked([q[0], q[1], q[2], q[3]]) - - property vs_relative: - """ - Virtual sites relative parameters. - - Allows for manual access to the attributes of virtual sites in the "relative" - implementation. PID denotes the id of the particle to which this virtual site - is related and distance the distance between non-virtual and virtual particle. - The relative orientation is specified as a quaternion of 4 floats. - - vs_relative : tuple (PID, distance, quaternion) - - .. note:: - This needs the feature ``VIRTUAL_SITES_RELATIVE`` - - """ - - def __set__(self, x): - if len(x) != 3: - raise ValueError( - "vs_relative needs input in the form [id, distance, quaternion].") - rel_to, dist, quat = x - check_type_or_throw_except( - rel_to, 1, int, "The particle id has to be given as an int.") - check_type_or_throw_except( - 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] - - set_particle_vs_relative(self._id, rel_to, dist, q) - - def __get__(self): - self.update_particle_data() - cdef int rel_to = -1 - cdef double dist = 0. - cdef Quaternion[double] q - q = get_particle_vs_relative(self.particle_data, rel_to, dist) - return (rel_to, dist, array_locked([q[0], q[1], q[2], q[3]])) - - # vs_auto_relate_to - def vs_auto_relate_to(self, rel_to): - """ - Setup this particle as virtual site relative to the particle - in argument ``rel_to``. A particle cannot relate to itself. - - Parameters - ----------- - rel_to : :obj:`int` or :obj:`ParticleHandle` - Particle to relate to (either particle id or particle object). - - """ - # If rel_to is of type ParticleHandle, - # resolve id of particle which to relate to - if isinstance(rel_to, ParticleHandle): - rel_to = rel_to.id - 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: - """ - The orientation of the dipole axis. - - dip : (3,) array_like of :obj:`float` + vs_quat: (4,) array_like of :obj:`float` + Virtual site quaternion. - .. note:: - This needs the feature ``DIPOLES``. + This quaternion describes the virtual particles orientation in the + body fixed frame of the related real particle. - """ - - def __set__(self, _dip): - check_type_or_throw_except( - _dip, 3, float, "Dipole moment vector has to be 3 floats.") - set_particle_dip(self._id, make_Vector3d(_dip)) - - def __get__(self): - self.update_particle_data() - return make_array_locked(self.particle_data.calc_dip()) + .. note:: + This needs the feature ``VIRTUAL_SITES_RELATIVE``. - # Scalar magnitude of dipole moment - property dipm: - """ - The magnitude of the dipole moment. + vs_relative: :obj:`tuple` + Virtual sites relative parameters. - dipm : :obj:`float` + Allows for manual access to the attributes of virtual sites in the + "relative" implementation. Format: ``(PID, distance, quaternion)``. + PID denotes the id of the particle to which this virtual site is + related and distance the distance between non-virtual and virtual particle. + The relative orientation is specified as a quaternion. - .. note:: - This needs the feature ``DIPOLES``. + .. note:: + This needs the feature ``VIRTUAL_SITES_RELATIVE`` - """ + dip: (3,) array_like of :obj:`float` + The orientation of the dipole axis. - def __set__(self, dipm): - check_type_or_throw_except( - dipm, 1, float, "Magnitude of dipole moment has to be 1 float.") - set_particle_dipm(self._id, dipm) + .. note:: + This needs the feature ``DIPOLES``. - def __get__(self): - self.update_particle_data() - return self.particle_data.dipm() + dipm: :obj:`float` + The magnitude of the dipole moment. - IF EXTERNAL_FORCES: - property ext_force: - """ - An additional external force applied to the particle. + .. note:: + This needs the feature ``DIPOLES``. - ext_force : (3,) array_like of :obj:`float` + ext_force: (3,) array_like of :obj:`float` + An additional external force applied to the particle. - .. note:: - This needs the feature ``EXTERNAL_FORCES``. + .. note:: + This needs the feature ``EXTERNAL_FORCES``. - """ + fix: (3,) array_like of :obj:`bool` + Fixes the particle motion in the specified cartesian directions. - def __set__(self, _ext_f): - check_type_or_throw_except( - _ext_f, 3, float, "External force vector has to be 3 floats.") - set_particle_ext_force(self._id, make_Vector3d(_ext_f)) + Fixes the particle in space. It is possible to fix motion in the + x-, y-, or z-direction independently. For example:: - def __get__(self): - self.update_particle_data() - return make_array_locked( - self.particle_data.ext_force()) + part.by_id(1).fix = [False, False, True] - property fix: - """ - Fixes the particle motion in the specified cartesian directions. + will fix motion for particle with index 1 only in the z-direction. - fix : (3,) array_like of :obj:`bool` + .. note:: + This needs the feature ``EXTERNAL_FORCES``. - Fixes the particle in space. By supplying a set of 3 bools as - arguments it is possible to fix motion in x, y, or z coordinates - independently. For example:: + ext_torque: (3,) array_like of :obj:`float` + An additional external torque is applied to the particle. - part[].fix = [False, False, True] + .. note:: + * This torque is specified in the laboratory frame! + * This needs features ``EXTERNAL_FORCES`` and ``ROTATION``. - will fix motion for particle with index ``INDEX`` only in z. + gamma: :obj:`float` or (3,) array_like of :obj:`float` + The translational frictional coefficient used in the Langevin + and Brownian thermostats. - .. note:: - This needs the feature ``EXTERNAL_FORCES``. + .. note:: + This needs feature ``THERMOSTAT_PER_PARTICLE`` and + optionally ``PARTICLE_ANISOTROPY``. - """ + See Also + ---------- + :meth:`espressomd.thermostat.Thermostat.set_langevin` : Setting the parameters of the Langevin thermostat - def __set__(self, flag): - check_type_or_throw_except( - flag, 3, int, "Property 'fix' has to be 3 bools.") - set_particle_fix(self._id, make_Vector3i(flag)) + gamma_rot: :obj:`float` or (3,) array_like of :obj:`float` + The particle rotational frictional coefficient used in + the Langevin and Brownian thermostats. - def __get__(self): - self.update_particle_data() - 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) + gamma_rot : :obj:`float` or (3,) array_like of :obj:`float` - IF ROTATION: - property ext_torque: - """ - An additional external torque is applied to the particle. + .. note:: + This needs features ``THERMOSTAT_PER_PARTICLE``, ``ROTATION`` and + optionally ``PARTICLE_ANISOTROPY``. - ext_torque : (3,) array_like of :obj:`float` + rotation: (3,) array_like of :obj:`bool` + Switches the particle's rotational degrees of freedom in the + Cartesian axes in the body-fixed frame. The content of the torque + and omega variables are meaningless for the co-ordinates for which + rotation is disabled. - .. note:: - * This torque is specified in the laboratory frame! - * This needs features ``EXTERNAL_FORCES`` and ``ROTATION``. + The default is not to integrate any rotational degrees of freedom. - """ + rotation : (3,) array_like of :obj:`bool` - def __set__(self, _ext_t): - check_type_or_throw_except( - _ext_t, 3, float, "External force vector has to be 3 floats.") - set_particle_ext_torque(self._id, make_Vector3d(_ext_t)) + .. note:: + This needs the feature ``ROTATION``. - def __get__(self): - self.update_particle_data() - return make_array_locked(self.particle_data.ext_torque()) + exclusions: (N,) array_like of :obj:`int` + The exclusion list of particles where non-bonded interactions are ignored. - IF THERMOSTAT_PER_PARTICLE: - IF PARTICLE_ANISOTROPY: - property gamma: - """ - The body-fixed frictional coefficient used in the Langevin - and Brownian thermostats. + .. note:: + This needs the feature ``EXCLUSIONS``. + + swimming: + Set swimming parameters. + + This property takes a dictionary with a different number of entries + depending whether there is an implicit fluid (i.e. with the Langevin + thermostat) of an explicit fluid (with LB). + + Swimming enables the particle to be self-propelled in the direction + determined by its quaternion. For setting the quaternion of the + particle see :attr:`~espressomd.particle_data.ParticleHandle.quat`. The + self-propulsion speed will relax to a constant velocity, that is specified by + ``v_swim``. Alternatively it is possible to achieve a constant velocity by + imposing a constant force term ``f_swim`` that is balanced by friction of a + (Langevin) thermostat. The way the velocity of the particle decays to the + constant terminal velocity in either of these methods is completely + determined by the friction coefficient. You may only set one of the + possibilities ``v_swim`` *or* ``f_swim`` as you cannot relax to constant force + *and* constant velocity at the same time. Setting both ``v_swim`` and + ``f_swim`` to 0.0 disables swimming. This option applies to all + non-lattice-Boltzmann thermostats. Note that there is no real difference + between ``v_swim`` and ``f_swim`` since the latter may always be chosen such that + the same terminal velocity is achieved for a given friction coefficient. - gamma : :obj:`float` or (3,) array_like of :obj:`float` + Parameters + ---------- + f_swim : :obj:`float` + Achieve a constant velocity by imposing a constant + force term ``f_swim`` that is balanced by friction of a + (Langevin) thermostat. This excludes the option ``v_swim``. + v_swim : :obj:`float` + Achieve a constant velocity by imposing a constant terminal + velocity ``v_swim``. This excludes the option ``f_swim``. + mode : :obj:`str`, \{'pusher', 'puller', 'N/A'\} + The LB flow field can be generated by a pushing or a + pulling mechanism, leading to change in the sign of the + dipolar flow field with respect to the direction of motion. + dipole_length : :obj:`float` + This determines the distance of the source of + propulsion from the particle's center. + + Notes + ----- + This needs the feature ``ENGINE``. The keys ``'mode'``, + and ``'dipole_length'`` are only available if ``ENGINE`` + is used with LB or ``CUDA``. - .. note:: - This needs features ``PARTICLE_ANISOTROPY`` and - ``THERMOSTAT_PER_PARTICLE``. + Examples + -------- + >>> import espressomd + >>> system = espressomd.System(box_l=[10, 10, 10]) + >>> # Langevin swimmer + >>> system.part.add(pos=[1, 0, 0], swimming={'f_swim': 0.03}) + >>> # LB swimmer + >>> system.part.add(pos=[2, 0, 0], swimming={'f_swim': 0.01, + ... 'mode': 'pusher', 'dipole_length': 2.0}) + + Methods + ------- + delete_all_bonds(): + Delete all bonds from the particle. - See Also - ---------- - :meth:`espressomd.thermostat.Thermostat.set_langevin` : Setting the parameters of the Langevin thermostat - - """ - - def __set__(self, _gamma): - # We accept a single number by just repeating it - if not isinstance(_gamma, collections.abc.Iterable): - _gamma = 3 * [_gamma] - check_type_or_throw_except( - _gamma, 3, float, "Friction has to be 3 floats.") - set_particle_gamma(self._id, make_Vector3d(_gamma)) + See Also + ---------- + delete_bond : Delete an unverified bond held by the particle. + bonds : ``Particle`` property containing a list of all current bonds held by ``Particle``. - def __get__(self): - self.update_particle_data() - return make_array_locked( - get_particle_gamma(self.particle_data)) + """ - ELSE: - property gamma: - """ - The translational frictional coefficient used in the Langevin - and Brownian thermostats. + _so_name = "Particles::ParticleHandle" + _so_creation_policy = "LOCAL" + _so_bind_methods = ( + "delete_all_bonds", + ) - gamma : :obj:`float` + def remove(self): + """ + Delete the particle. - .. note:: - This needs the feature ``THERMOSTAT_PER_PARTICLE``. + See Also + -------- + espressomd.particle_data.ParticleList.add + espressomd.particle_data.ParticleList.clear - See Also - ---------- - :meth:`espressomd.thermostat.Thermostat.set_langevin.set_langevin` : Setting the parameters of the Langevin thermostat + """ + self.call_method("remove_particle") + del self - """ + def to_dict(self): + """ + Returns the particle's attributes as a dictionary. - def __set__(self, _gamma): - check_type_or_throw_except( - _gamma, 1, float, "Gamma has to be a float.") - set_particle_gamma(self._id, _gamma) + It includes the content of ``particle_attributes``, minus a few exceptions: - def __get__(self): - self.update_particle_data() - return get_particle_gamma(self.particle_data) + - :attr:`~ParticleHandle.dip`, :attr:`~ParticleHandle.director`: + Setting only the director will overwrite the orientation of the + particle around the axis parallel to dipole moment/director. + Quaternions contain the full info. + - :attr:`~ParticleHandle.image_box`, :attr:`~ParticleHandle.node` - IF ROTATION: - IF PARTICLE_ANISOTROPY: - property gamma_rot: - """ - The particle translational frictional coefficient used in - the Langevin and Brownian thermostats. - - gamma_rot : :obj:`float` or (3,) array_like of :obj:`float` - - .. note:: - This needs features ``ROTATION``, ``PARTICLE_ANISOTROPY`` - and ``THERMOSTAT_PER_PARTICLE``. - - """ - - def __set__(self, _gamma_rot): - # We accept a single number by just repeating it - if not isinstance( - _gamma_rot, collections.abc.Iterable): - _gamma_rot = 3 * [_gamma_rot] - check_type_or_throw_except( - _gamma_rot, 3, float, "Rotational friction has to be 3 floats.") - set_particle_gamma_rot( - self._id, make_Vector3d(_gamma_rot)) - - def __get__(self): - self.update_particle_data() - return make_array_locked( - get_particle_gamma_rot(self.particle_data)) - ELSE: - property gamma_rot: - """ - The particle rotational frictional coefficient used in the - Langevin and Brownian thermostats. - - gamma_rot : :obj:`float` - - .. note:: - This needs features ``ROTATION`` and - ``THERMOSTAT_PER_PARTICLE``. - - """ - - def __set__(self, _gamma_rot): - check_type_or_throw_except( - _gamma_rot, 1, float, "gamma_rot has to be a float.") - set_particle_gamma_rot(self._id, _gamma_rot) - - def __get__(self): - self.update_particle_data() - return get_particle_gamma_rot(self.particle_data) - - IF ROTATION: - property rotation: - """ - Switches the particle's rotational degrees of freedom in the - Cartesian axes in the body-fixed frame. The content of the torque - and omega variables are meaningless for the co-ordinates for which - rotation is disabled. - - The default is not to integrate any rotational degrees of freedom. - - rotation : (3,) array_like of :obj:`bool` - - .. note:: - This needs the feature ``ROTATION``. - - """ - - def __set__(self, flag): - check_type_or_throw_except( - flag, 3, int, "Property 'rotation' has to be 3 bools.") - set_particle_rotation(self._id, make_Vector3i(flag)) + """ - def __get__(self): - self.update_particle_data() - 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) + pdict = self.get_params() + for k in ["director", "dip", "pos_folded", + "image_box", "node", "lees_edwards_flag"]: + if k in pdict: + del pdict[k] + if has_features("EXCLUSIONS"): + pdict["exclusions"] = self.exclusions + pdict["bonds"] = self.bonds + return pdict - IF EXCLUSIONS: - property exclusions: - """ - The exclusion list of particles where non-bonded interactions are ignored. + def __str__(self): + res = collections.OrderedDict() + # Id and pos first, then the rest + res["id"] = self.id + res["pos"] = self.pos + for attr in particle_attributes: + tmp = getattr(self, attr) + # Remove array type names from output + if isinstance(tmp, array_locked): + res[attr] = tuple(tmp) + else: + res[attr] = tmp - .. note:: - This needs the feature ``EXCLUSIONS``. + # Get rid of OrderedDict in output + return str(res).replace("OrderedDict(", "ParticleHandle(") - exclusions : (N,) array_like of :obj:`int` + def add_exclusion(self, partner): + """ + Exclude non-bonded interactions with the given partner. - """ + .. note:: + This needs the feature ``EXCLUSIONS``. - def __set__(self, partners): - # Delete all - for e in self.exclusions: - self.delete_exclusion(e) + Parameters + ----------- + partner : :class:`~espressomd.particle_data.ParticleHandle` or :obj:`int` + Particle to exclude. - nlvl = nesting_level(partners) + """ + if isinstance(partner, ParticleHandle): + p_id = partner.id + else: + p_id = partner + check_type_or_throw_except( + p_id, 1, int, "Argument 'partner' has to be a ParticleHandle or int.") + if self.call_method("has_exclusion", pid=p_id): + raise RuntimeError( + f"Particle with id {p_id} is already in exclusion list of particle with id {self.id}") + self.call_method("add_exclusion", pid=p_id) - if nlvl == 0: # Single item - self.add_exclusion(partners) - elif nlvl == 1: # List of items - for partner in partners: - self.add_exclusion(partner) - else: - raise ValueError( - "Exclusions have to be specified as a lists of partners or a single item.") + def delete_exclusion(self, partner): + """ + Remove exclusion of non-bonded interactions with the given partner. - def __get__(self): - self.update_particle_data() - return array_locked(self.particle_data.exclusions_as_vector()) + .. note:: + This needs the feature ``EXCLUSIONS``. - def add_exclusion(self, partner): - """ - Exclude non-bonded interactions with the given partner. + Parameters + ----------- + partner : :class:`~espressomd.particle_data.ParticleHandle` or :obj:`int` + Particle to remove from exclusions. - .. note:: - This needs the feature ``EXCLUSIONS``. + """ + if isinstance(partner, ParticleHandle): + p_id = partner.id + else: + p_id = partner + check_type_or_throw_except( + p_id, 1, int, "Argument 'partner' has to be a ParticleHandle or int.") + if not self.call_method("has_exclusion", pid=p_id): + raise RuntimeError( + f"Particle with id {p_id} is not in exclusion list of particle with id {self.id}") + self.call_method("del_exclusion", pid=p_id) + + @property + def bonds(self): + bonds = [] + for bond_view in self.call_method("get_bonds_view"): + bond_id = bond_view[0] + partner_ids = bond_view[1:] + bonds.append((BondedInteractions()[bond_id], *partner_ids)) + + return tuple(bonds) + + @bonds.setter + def bonds(self, bonds): + # Assigning to the bond property means replacing the existing value + # i.e., we delete all existing bonds + self.delete_all_bonds() + + if bonds: + nlvl = nesting_level(bonds) + if nlvl == 1: + self.add_bond(bonds) + elif nlvl == 2: + for bond in bonds: + self.add_bond(bond) + else: + raise ValueError( + "Bonds have to specified as lists of tuples/lists or a single list.") - Parameters - ----------- - partner : :class:`~espressomd.particle_data.ParticleHandle` or :obj:`int` - Particle to exclude. + @property + def exclusions(self): + assert_features("EXCLUSIONS") + return array_locked( + np.array(self.call_method("get_exclusions"), dtype=int)) - """ - if isinstance(partner, ParticleHandle): - p_id = partner.id - else: - p_id = partner - check_type_or_throw_except( - p_id, 1, int, "Argument 'partner' has to be a ParticleHandle or int.") - self.update_particle_data() - if self.particle_data.has_exclusion(p_id): - raise RuntimeError( - f"Particle with id {p_id} is already in exclusion list of particle with id {self._id}") - add_particle_exclusion(self._id, p_id) - - def delete_exclusion(self, partner): - """ - Remove exclusion of non-bonded interactions with the given partner. - - .. note:: - This needs the feature ``EXCLUSIONS``. - - Parameters - ----------- - partner : :class:`~espressomd.particle_data.ParticleHandle` or :obj:`int` - Particle to remove from exclusions. - - """ - if isinstance(partner, ParticleHandle): - p_id = partner.id - else: - p_id = partner - check_type_or_throw_except( - p_id, 1, int, "Argument 'partner' has to be a ParticleHandle or int.") - self.update_particle_data() - if not self.particle_data.has_exclusion(p_id): - raise RuntimeError( - f"Particle with id {p_id} is not in exclusion list of particle with id {self._id}") - remove_particle_exclusion(self._id, p_id) - - IF ENGINE: - property swimming: - """ - Set swimming parameters. - - This property takes a dictionary with a different number of - entries depending whether there is an implicit fluid (i.e. with the - Langevin thermostat) of an explicit fluid (with LB). - - Swimming enables the particle to be self-propelled in the direction - determined by its quaternion. For setting the quaternion of the - particle see :attr:`~espressomd.particle_data.ParticleHandle.quat`. The - self-propulsion speed will relax to a constant velocity, that is specified by - ``v_swim``. Alternatively it is possible to achieve a constant velocity by - imposing a constant force term ``f_swim`` that is balanced by friction of a - (Langevin) thermostat. The way the velocity of the particle decays to the - constant terminal velocity in either of these methods is completely - determined by the friction coefficient. You may only set one of the - possibilities ``v_swim`` *or* ``f_swim`` as you cannot relax to constant force - *and* constant velocity at the same time. Setting both ``v_swim`` and - ``f_swim`` to 0.0 disables swimming. This option applies to all - non-lattice-Boltzmann thermostats. Note that there is no real difference - between ``v_swim`` and ``f_swim`` since the latter may always be chosen such that - the same terminal velocity is achieved for a given friction coefficient. - - - Parameters - ---------- - f_swim : :obj:`float` - Achieve a constant velocity by imposing a constant - force term ``f_swim`` that is balanced by friction of a - (Langevin) thermostat. This excludes the option ``v_swim``. - v_swim : :obj:`float` - Achieve a constant velocity by imposing a constant terminal - velocity ``v_swim``. This excludes the option ``f_swim``. - mode : :obj:`str`, \{'pusher', 'puller', 'N/A'\} - The LB flow field can be generated by a pushing or a - pulling mechanism, leading to change in the sign of the - dipolar flow field with respect to the direction of motion. - dipole_length : :obj:`float` - This determines the distance of the source of - propulsion from the particle's center. - - Notes - ----- - This needs the feature ``ENGINE``. The keys ``'mode'``, - and ``'dipole_length'`` are only - available if ``ENGINE`` is used with LB or ``CUDA``. - - Examples - -------- - >>> import espressomd - >>> system = espressomd.System(box_l=[10, 10, 10]) - >>> # Langevin swimmer - >>> system.part.add(pos=[1, 0, 0], swimming={'f_swim': 0.03}) - >>> # LB swimmer - >>> system.part.add(pos=[2, 0, 0], swimming={'f_swim': 0.01, - ... 'mode': 'pusher', 'dipole_length': 2.0}) - - """ - - def __set__(self, _params): - cdef particle_parameters_swimming swim - - swim.swimming = True - swim.v_swim = 0.0 - swim.f_swim = 0.0 - swim.push_pull = 0 - swim.dipole_length = 0.0 - - if type(_params) == type(True): - if _params: - raise Exception( - "To enable swimming supply a dictionary of parameters.") - else: - if 'f_swim' in _params and 'v_swim' in _params: - if _params["f_swim"] == 0 or _params["v_swim"] == 0: - pass - else: - raise Exception( - "You can't set v_swim and f_swim at the same time.") - if 'f_swim' in _params: - check_type_or_throw_except( - _params['f_swim'], 1, float, "f_swim has to be a float.") - swim.f_swim = _params['f_swim'] - if 'v_swim' in _params: - check_type_or_throw_except( - _params['v_swim'], 1, float, "v_swim has to be a float.") - swim.v_swim = _params['v_swim'] - - if 'mode' in _params: - if _params['mode'] == "pusher": - swim.push_pull = -1 - elif _params['mode'] == "puller": - swim.push_pull = 1 - elif _params['mode'] == "N/A": - swim.push_pull = 0 - else: - raise Exception( - "'mode' has to be either 'pusher', 'puller' or 'N/A'.") - - if 'dipole_length' in _params: - check_type_or_throw_except( - _params['dipole_length'], 1, float, "dipole_length has to be a float.") - swim.dipole_length = _params['dipole_length'] - - set_particle_swimming(self._id, swim) - - def __get__(self): - self.update_particle_data() - swim = {} - mode = "N/A" - cdef particle_parameters_swimming _swim - _swim = self.particle_data.swimming() - - if _swim.push_pull == -1: - mode = 'pusher' - elif _swim.push_pull == 1: - mode = 'puller' - swim = { - 'v_swim': _swim.v_swim, - 'f_swim': _swim.f_swim, - 'mode': mode, - 'dipole_length': _swim.dipole_length - } - - return swim + @exclusions.setter + def exclusions(self, p_ids): + assert_features("EXCLUSIONS") + self.call_method("set_exclusions", p_ids=p_ids) - def remove(self): + def vs_auto_relate_to(self, rel_to): """ - Delete the particle. + Setup this particle as virtual site relative to the particle + in argument ``rel_to``. A particle cannot relate to itself. - See Also - -------- - espressomd.particle_data.ParticleList.add - espressomd.particle_data.ParticleList.clear + Parameters + ----------- + rel_to : :obj:`int` or :obj:`ParticleHandle` + Particle to relate to (either particle id or particle object). """ - remove_particle(self._id) - del self + if isinstance(rel_to, ParticleHandle): + rel_to = rel_to.id + else: + check_type_or_throw_except( + rel_to, 1, int, "Argument of 'vs_auto_relate_to' has to be of type ParticleHandle or int") + self.call_method("vs_relate_to", pid=rel_to) + handle_errors("vs_auto_relate_to") def add_verified_bond(self, bond): """ @@ -1213,17 +577,12 @@ cdef class ParticleHandle: bonds : ``Particle`` property containing a list of all current bonds held by ``Particle``. """ - - # If someone adds bond types with more than four partners, this has to - # be changed - cdef int bond_info[5] - bond_info[0] = bond[0]._bond_id - for i in range(1, len(bond)): - bond_info[i] = bond[i] - if self._id in bond[1:]: + 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))) + f"Bond partners {bond[1:]} include the particle {self.id} itself") + self.call_method("add_bond", + bond_id=bond[0]._bond_id, + part_id=bond[1:]) def delete_verified_bond(self, bond): """ @@ -1242,14 +601,9 @@ cdef class ParticleHandle: bonds : ``Particle`` property containing a list of all current bonds held by ``Particle``. """ - - cdef int bond_info[5] - bond_info[0] = bond[0]._bond_id - for i in range(1, len(bond)): - bond_info[i] = bond[i] - - delete_particle_bond( - self._id, make_const_span[int](bond_info, len(bond))) + self.call_method("del_bond", + bond_id=bond[0]._bond_id, + part_id=bond[1:]) def normalize_and_check_bond_or_throw_exception(self, bond): """ @@ -1278,20 +632,20 @@ cdef class ParticleHandle: 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])}.") + 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.") + "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): + if not self.call_method("is_valid_bond_id", bond_id=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') + 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.") + 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): @@ -1340,7 +694,7 @@ cdef class ParticleHandle: _bond = self.normalize_and_check_bond_or_throw_exception(bond) if _bond in self.bonds: raise RuntimeError( - f"Bond {_bond} already exists on particle {self._id}.") + f"Bond {_bond} already exists on particle {self.id}") self.add_verified_bond(_bond) def delete_bond(self, bond): @@ -1390,22 +744,9 @@ cdef class ParticleHandle: _bond = self.normalize_and_check_bond_or_throw_exception(bond) if _bond not in self.bonds: raise RuntimeError( - f"Bond {_bond} doesn't exist on particle {self._id}.") + f"Bond {_bond} doesn't exist on particle {self.id}") self.delete_verified_bond(_bond) - def delete_all_bonds(self): - """ - Delete all bonds from the particle. - - See Also - ---------- - delete_bond : Delete an unverified bond held by the particle. - bonds : ``Particle`` property containing a list of all current bonds held by ``Particle``. - - """ - - delete_particle_bonds(self._id) - def update(self, new_properties): """ Update properties of a particle. @@ -1430,35 +771,45 @@ cdef class ParticleHandle: """ if "id" in new_properties: - raise Exception("Cannot change particle id.") + raise RuntimeError("Cannot change particle id.") for k, v in new_properties.items(): setattr(self, k, v) - IF ROTATION: - def convert_vector_body_to_space(self, vec): - """Converts the given vector from the particle's body frame to the space frame""" - self.update_particle_data() - return np.array(make_array_locked(convert_vector_body_to_space( - dereference(self.particle_data), make_Vector3d(vec)))) + def convert_vector_body_to_space(self, vec): + """ + Convert the given vector from the particle's body frame to the space frame. + """ + assert_features("ROTATION") + return self.call_method("convert_vector_body_to_space", vec=vec) - def convert_vector_space_to_body(self, vec): - """Converts the given vector from the space frame to the particle's body frame""" - self.update_particle_data() - return np.array(make_array_locked(convert_vector_space_to_body( - dereference(self.particle_data), make_Vector3d(vec)))) + def convert_vector_space_to_body(self, vec): + """ + Convert the given vector from the space frame to the particle's body frame. + """ + assert_features("ROTATION") + return self.call_method("convert_vector_space_to_body", vec=vec) - def rotate(self, axis, angle): - """Rotates the particle around the given axis + def rotate(self, axis, angle): + """ + Rotate the particle around the given axis. - Parameters - ---------- - axis : (3,) array_like of :obj:`float` + Parameters + ---------- + axis : (3,) array_like of :obj:`float` - angle : :obj:`float` + angle : :obj:`float` + + """ + assert_features("ROTATION") + self.call_method("rotate_particle", axis=axis, angle=angle) + + +particle_attributes = set(ParticleHandle(id=0)._valid_parameters()) +if has_features("EXCLUSIONS"): + particle_attributes.add("exclusions") +particle_attributes.add("bonds") - """ - rotate_particle(self._id, make_Vector3d(axis), angle) cdef class _ParticleSliceImpl: """Handles slice inputs. @@ -1533,7 +884,7 @@ cdef class _ParticleSliceImpl: for chunk in self.chunks(self.id_selection, self._chunk_size): prefetch_particle_data(chunk) for i in chunk: - yield ParticleHandle(i) + yield ParticleHandle(id=i) def chunks(self, l, n): """Generator returning chunks of length n from l. @@ -1554,17 +905,20 @@ cdef class _ParticleSliceImpl: pos_array = np.zeros((len(self.id_selection), 3)) for i in range(len(self.id_selection)): pos_array[i, :] = ParticleHandle( - self.id_selection[i]).pos_folded + id=self.id_selection[i]).pos_folded return pos_array + def __set__(self, value): + raise RuntimeError("Parameter 'pos_folded' is read-only.") + IF EXCLUSIONS: def add_exclusion(self, _partner): - for i in self.id_selection: - ParticleHandle(i).add_exclusion(_partner) + for p_id in self.id_selection: + ParticleHandle(id=p_id).add_exclusion(_partner) def delete_exclusion(self, _partner): - for i in self.id_selection: - ParticleHandle(i).delete_exclusion(_partner) + for p_id in self.id_selection: + ParticleHandle(id=p_id).delete_exclusion(_partner) def __str__(self): res = "" @@ -1588,20 +942,20 @@ cdef class _ParticleSliceImpl: Add a single bond to the particles. """ - for i in self.id_selection: - ParticleHandle(i).add_bond(_bond) + for p_id in self.id_selection: + ParticleHandle(id=p_id).add_bond(_bond) def delete_bond(self, _bond): """ Delete a single bond from the particles. """ - for i in self.id_selection: - ParticleHandle(i).delete_bond(_bond) + for p_id in self.id_selection: + ParticleHandle(id=p_id).delete_bond(_bond) def delete_all_bonds(self): - for i in self.id_selection: - ParticleHandle(i).delete_all_bonds() + for p_id in self.id_selection: + ParticleHandle(id=p_id).delete_all_bonds() def remove(self): """ @@ -1612,8 +966,8 @@ cdef class _ParticleSliceImpl: :meth:`espressomd.particle_data.ParticleList.add` """ - for id in self.id_selection: - ParticleHandle(id).remove() + for p_id in self.id_selection: + ParticleHandle(id=p_id).remove() class ParticleSlice(_ParticleSliceImpl): @@ -1625,7 +979,7 @@ class ParticleSlice(_ParticleSliceImpl): """ def __setattr__(self, name, value): - if name != "_chunk_size" and not hasattr(ParticleHandle, name): + if name != "_chunk_size" and name not in particle_attributes: raise AttributeError( f"ParticleHandle does not have the attribute {name}.") super().__setattr__(name, value) @@ -1651,9 +1005,8 @@ class ParticleSlice(_ParticleSliceImpl): """ odict = {} - key_list = [p.id for p in self] - for particle_number in key_list: - pdict = ParticleHandle(particle_number).to_dict() + for p in self: + pdict = ParticleHandle(id=p.id).to_dict() for p_key, p_value in pdict.items(): if p_key in odict: odict[p_key].append(p_value) @@ -1668,11 +1021,11 @@ cdef class ParticleList: """ - def by_id(self, id): + def by_id(self, p_id): """ Access a particle by its integer id. """ - return ParticleHandle(id) + return ParticleHandle(id=p_id) def by_ids(self, ids): """ @@ -2026,12 +1379,12 @@ Set quat and scalar dipole moment (dipm) instead.") def set_slice_one_for_all(particle_slice, attribute, values): for i in particle_slice.id_selection: - setattr(ParticleHandle(i), attribute, values) + setattr(ParticleHandle(id=i), attribute, values) def set_slice_one_for_each(particle_slice, attribute, values): for i, v in zip(particle_slice.id_selection, values): - setattr(ParticleHandle(i), attribute, v) + setattr(ParticleHandle(id=i), attribute, v) def _add_particle_slice_properties(): @@ -2093,7 +1446,7 @@ def _add_particle_slice_properties(): else: target = getattr( - ParticleHandle(particle_slice.id_selection[0]), attribute) + ParticleHandle(id=particle_slice.id_selection[0]), attribute) target_shape = np.shape(target) if not target_shape: # scalar quantity @@ -2132,7 +1485,7 @@ def _add_particle_slice_properties(): # get first slice member to determine its type target = getattr(ParticleHandle( - particle_slice.id_selection[0]), attribute) + id=particle_slice.id_selection[0]), attribute) if type(target) is array_locked: # vectorial quantity target_type = target.dtype else: # scalar quantity @@ -2159,7 +1512,7 @@ def _add_particle_slice_properties(): new_property = property( functools.partial(get_attribute, attribute=attribute_name), functools.partial(set_attribute, attribute=attribute_name), - doc=getattr(ParticleHandle, attribute_name).__doc__) + doc="") # attach the property to ParticleSlice setattr(ParticleSlice, attribute_name, new_property) diff --git a/src/python/espressomd/utils.pxd b/src/python/espressomd/utils.pxd index 855799ced20..e3770dfe9c6 100644 --- a/src/python/espressomd/utils.pxd +++ b/src/python/espressomd/utils.pxd @@ -102,10 +102,6 @@ cdef extern from "utils/Vector.hpp" namespace "Utils": double & operator[](int i) double * data() -cdef extern from "utils/quaternion.hpp" namespace "Utils": - cppclass Quaternion[T]: - T & operator[](int i) - cdef make_array_locked(Vector3d) cdef Vector3d make_Vector3d(a) cdef Vector3i make_Vector3i(a) diff --git a/src/python/espressomd/visualization.py b/src/python/espressomd/visualization.py index 2c9f8c5184a..c4da2a1b962 100644 --- a/src/python/espressomd/visualization.py +++ b/src/python/espressomd/visualization.py @@ -404,12 +404,10 @@ def __init__(self, system, **kwargs): # particle info of highlighted particle: collect particle attributes self.highlighted_particle = {} self.particle_attributes = [] - for d in dir(espressomd.particle_data.ParticleHandle): - if isinstance(getattr(espressomd.particle_data.ParticleHandle, d), - type(espressomd.particle_data.ParticleHandle.pos)): - if d not in ["pos_folded"]: - self.particle_attributes.append(d) - self.max_len_attr = max([len(a) for a in self.particle_attributes]) + for attr in espressomd.particle_data.particle_attributes: + if attr not in ["pos_folded"]: + self.particle_attributes.append(attr) + self.max_len_attr = max(map(len, self.particle_attributes)) # fixed colors from inverse background color for good contrast self.inverse_bg_color = \ diff --git a/src/script_interface/CMakeLists.txt b/src/script_interface/CMakeLists.txt index 6f17bf1bba2..9718a160841 100644 --- a/src/script_interface/CMakeLists.txt +++ b/src/script_interface/CMakeLists.txt @@ -41,6 +41,7 @@ add_subdirectory(math) add_subdirectory(mpiio) add_subdirectory(observables) add_subdirectory(pair_criteria) +add_subdirectory(particle_data) add_subdirectory(reaction_methods) add_subdirectory(scafacos) add_subdirectory(shapes) diff --git a/src/script_interface/analysis/Analysis.cpp b/src/script_interface/analysis/Analysis.cpp index 2f3862e925c..5ebebd25802 100644 --- a/src/script_interface/analysis/Analysis.cpp +++ b/src/script_interface/analysis/Analysis.cpp @@ -101,10 +101,12 @@ Variant Analysis::do_call_method(std::string const &name, auto const result = nbhood(partCfg(), pos, radius); return result; } +#ifdef DPD if (name == "dpd_stress") { auto const result = dpd_stress(); return result.as_vector(); } +#endif // DPD if (name == "particle_energy") { auto const pid = get_value(parameters, "pid"); return particle_short_range_energy_contribution(pid); diff --git a/src/script_interface/initialize.cpp b/src/script_interface/initialize.cpp index 0a27b04cdc3..eca5538128c 100644 --- a/src/script_interface/initialize.cpp +++ b/src/script_interface/initialize.cpp @@ -39,6 +39,7 @@ #include "mpiio/initialize.hpp" #include "observables/initialize.hpp" #include "pair_criteria/initialize.hpp" +#include "particle_data/initialize.hpp" #include "reaction_methods/initialize.hpp" #include "shapes/initialize.hpp" #include "system/initialize.hpp" @@ -63,6 +64,7 @@ void initialize(Utils::Factory *f) { MPIIO::initialize(f); Observables::initialize(f); PairCriteria::initialize(f); + Particles::initialize(f); Shapes::initialize(f); System::initialize(f); VirtualSites::initialize(f); diff --git a/src/script_interface/particle_data/CMakeLists.txt b/src/script_interface/particle_data/CMakeLists.txt new file mode 100644 index 00000000000..88c0788304e --- /dev/null +++ b/src/script_interface/particle_data/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources( + Espresso_script_interface + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ParticleHandle.cpp) diff --git a/src/script_interface/particle_data/ParticleHandle.cpp b/src/script_interface/particle_data/ParticleHandle.cpp new file mode 100644 index 00000000000..f327b52e63e --- /dev/null +++ b/src/script_interface/particle_data/ParticleHandle.cpp @@ -0,0 +1,490 @@ +/* + * 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 "config.hpp" + +#include "ParticleHandle.hpp" + +#include "script_interface/get_value.hpp" +#include "script_interface/interactions/bonded.hpp" + +#include "core/grid.hpp" +#include "core/particle_data.hpp" +#include "core/particle_node.hpp" +#include "core/rotation.hpp" +#include "core/virtual_sites.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace ScriptInterface { +namespace Particles { + +static auto error_msg(std::string const &name, std::string const &reason) { + std::stringstream msg; + msg << "attribute '" << name << "' of 'ParticleHandle' " << reason; + return msg.str(); +} + +static auto quat2vector(Utils::Quaternion const &q) { + return Utils::Vector4d{{q[0], q[1], q[2], q[3]}}; +} + +static auto get_quaternion_safe(std::string const &name, Variant const &value) { + auto const q = get_value(value); + if (q.norm2() == 0.) { + throw std::domain_error(error_msg(name, "must be non-zero")); + } + return Utils::Quaternion{{q[0], q[1], q[2], q[3]}}; +} + +#ifdef THERMOSTAT_PER_PARTICLE +static auto get_gamma_safe(Variant const &value) { +#ifdef PARTICLE_ANISOTROPY + try { + return Utils::Vector3d::broadcast(get_value(value)); + } catch (...) { + return get_value(value); + } +#else // PARTICLE_ANISOTROPY + return get_value(value); +#endif // PARTICLE_ANISOTROPY +} +#endif // THERMOSTAT_PER_PARTICLE + +static auto get_bond_vector(VariantMap const ¶ms) { + auto const bond_id = get_value(params, "bond_id"); + auto const part_id = get_value>(params, "part_id"); + std::vector bond_view; + bond_view.emplace_back(bond_id); + for (auto const pid : part_id) { + bond_view.emplace_back(pid); + } + return bond_view; +} + +ParticleHandle::ParticleHandle() { + add_parameters({ + {"id", AutoParameter::read_only, [this]() { return m_pid; }}, + {"type", + [this](Variant const &value) { + auto const p_type = get_value(value); + if (p_type < 0) { + throw std::domain_error( + error_msg("type", "must be an integer >= 0")); + } + set_particle_type(m_pid, p_type); + }, + [this]() { return particle().type(); }}, + {"pos", + [this](Variant const &value) { + place_particle(m_pid, get_value(value)); + }, + [this]() { + auto const &p = particle(); + return unfolded_position(p.pos(), p.image_box(), ::box_geo.length()); + }}, + {"v", + [this](Variant const &value) { + set_particle_v(m_pid, get_value(value)); + }, + [this]() { return particle().v(); }}, + {"f", + [this](Variant const &value) { + set_particle_f(m_pid, get_value(value)); + }, + [this]() { return particle().force(); }}, + {"mass", +#ifdef MASS + [this](Variant const &value) { + set_particle_mass(m_pid, get_value(value)); + }, +#else // MASS + [](Variant const &value) { + if (std::abs(get_value(value) - 1.) > 1e-10) { + throw std::runtime_error("Feature MASS not compiled in"); + } + }, +#endif // MASS + [this]() { return particle().mass(); }}, + {"q", +#ifdef ELECTROSTATICS + [this](Variant const &value) { + set_particle_q(m_pid, get_value(value)); + }, +#else // ELECTROSTATICS + [](Variant const &value) { + if (get_value(value) != 0.) { + throw std::runtime_error("Feature ELECTROSTATICS not compiled in"); + } + }, +#endif // ELECTROSTATICS + [this]() { return particle().q(); }}, + {"virtual", +#ifdef VIRTUAL_SITES + [this](Variant const &value) { + set_particle_virtual(m_pid, get_value(value)); + }, +#else // VIRTUAL_SITES + [](Variant const &value) { + if (get_value(value)) { + throw std::runtime_error("Feature VIRTUAL_SITES not compiled in"); + } + }, +#endif // VIRTUAL_SITES + [this]() { return particle().is_virtual(); }}, +#ifdef ROTATION + {"director", + [this](Variant const &value) { + set_particle_director(m_pid, get_value(value)); + }, + [this]() { return particle().calc_director(); }}, + {"quat", + [this](Variant const &value) { + set_particle_quat(m_pid, get_quaternion_safe("quat", value)); + }, + [this]() { return quat2vector(particle().quat()); }}, + {"omega_body", + [this](Variant const &value) { + set_particle_omega_body(m_pid, get_value(value)); + }, + [this]() { return particle().omega(); }}, + {"rotation", + [this](Variant const &value) { + set_particle_rotation( + m_pid, Utils::Vector3i{get_value(value)}); + }, + [this]() { + auto const &p = particle(); + return Utils::Vector3b{{p.can_rotate_around(0), p.can_rotate_around(1), + p.can_rotate_around(2)}}; + }}, + {"omega_lab", + [this](Variant const &value) { + set_particle_omega_lab(m_pid, get_value(value)); + }, + [this]() { + auto const &p = particle(); + return convert_vector_body_to_space(p, p.omega()); + }}, + {"torque_lab", + [this](Variant const &value) { + set_particle_torque_lab(m_pid, get_value(value)); + }, + [this]() { + auto const &p = particle(); + return convert_vector_body_to_space(p, p.torque()); + }}, +#endif // ROTATION +#ifdef DIPOLES + {"dip", + [this](Variant const &value) { + set_particle_dip(m_pid, get_value(value)); + }, + [this]() { return particle().calc_dip(); }}, + {"dipm", + [this](Variant const &value) { + set_particle_dipm(m_pid, get_value(value)); + }, + [this]() { return particle().dipm(); }}, +#endif // DIPOLES +#ifdef ROTATIONAL_INERTIA + {"rinertia", + [this](Variant const &value) { + set_particle_rotational_inertia(m_pid, + get_value(value)); + }, + [this]() { return particle().rinertia(); }}, +#endif // ROTATIONAL_INERTIA +#ifdef LB_ELECTROHYDRODYNAMICS + {"mu_E", + [this](Variant const &value) { + set_particle_mu_E(m_pid, get_value(value)); + }, + [this]() { return particle().mu_E(); }}, +#endif // LB_ELECTROHYDRODYNAMICS +#ifdef EXTERNAL_FORCES + {"fix", + [this](Variant const &value) { + set_particle_fix(m_pid, + Utils::Vector3i{get_value(value)}); + }, + [this]() { + auto const &p = particle(); + return Utils::Vector3b{ + {p.is_fixed_along(0), p.is_fixed_along(1), p.is_fixed_along(2)}}; + }}, + {"ext_force", + [this](Variant const &value) { + set_particle_ext_force(m_pid, get_value(value)); + }, + [this]() { return particle().ext_force(); }}, +#ifdef ROTATION + {"ext_torque", + [this](Variant const &value) { + set_particle_ext_torque(m_pid, get_value(value)); + }, + [this]() { return particle().ext_torque(); }}, +#endif // ROTATION +#endif // EXTERNAL_FORCES +#ifdef THERMOSTAT_PER_PARTICLE + {"gamma", + [this](Variant const &value) { + set_particle_gamma(m_pid, get_gamma_safe(value)); + }, + [this]() { return particle().gamma(); }}, +#ifdef ROTATION + {"gamma_rot", + [this](Variant const &value) { + set_particle_gamma_rot(m_pid, get_gamma_safe(value)); + }, + [this]() { return particle().gamma_rot(); }}, +#endif // ROTATION +#endif // THERMOSTAT_PER_PARTICLE + {"pos_folded", AutoParameter::read_only, + [this]() { return folded_position(particle().pos(), ::box_geo); }}, + + {"lees_edwards_offset", + [this](Variant const &value) { + set_particle_lees_edwards_offset(m_pid, get_value(value)); + }, + [this]() { return particle().lees_edwards_offset(); }}, + {"lees_edwards_flag", AutoParameter::read_only, + [this]() { return particle().lees_edwards_flag(); }}, + {"image_box", AutoParameter::read_only, + [this]() { return particle().image_box(); }}, + {"node", AutoParameter::read_only, + [this]() { return get_particle_node(m_pid); }}, + {"mol_id", + [this](Variant const &value) { + auto const mol_id = get_value(value); + if (mol_id < 0) { + throw std::domain_error( + error_msg("mol_id", "must be an integer >= 0")); + } + set_particle_mol_id(m_pid, mol_id); + }, + [this]() { return particle().mol_id(); }}, +#ifdef VIRTUAL_SITES_RELATIVE + {"vs_quat", + [this](Variant const &value) { + set_particle_vs_quat(m_pid, get_quaternion_safe("vs_quat", value)); + }, + [this]() { return quat2vector(particle().vs_relative().quat); }}, + {"vs_relative", + [this](Variant const &value) { + ParticleProperties::VirtualSitesRelativeParameters vs_relative{}; + try { + auto const array = get_value>(value); + if (array.size() != 3) { + throw 0; + } + vs_relative.distance = get_value(array[1]); + vs_relative.to_particle_id = get_value(array[0]); + vs_relative.rel_orientation = + get_quaternion_safe("vs_relative", array[2]); + } catch (...) { + throw std::invalid_argument(error_msg( + "vs_relative", "must take the form [id, distance, quaternion]")); + } + set_particle_vs_relative(m_pid, vs_relative.to_particle_id, + vs_relative.distance, + vs_relative.rel_orientation); + }, + [this]() { + auto const &p = particle(); + return std::vector{ + {p.vs_relative().to_particle_id, p.vs_relative().distance, + quat2vector(p.vs_relative().rel_orientation)}}; + }}, +#endif // VIRTUAL_SITES_RELATIVE +#ifdef ENGINE + {"swimming", + [this](Variant const &value) { + ParticleParametersSwimming swim{}; + swim.swimming = true; + auto const dict = get_value(value); + if (dict.count("f_swim") != 0) { + swim.f_swim = get_value(dict.at("f_swim")); + } + if (dict.count("v_swim") != 0) { + swim.v_swim = get_value(dict.at("v_swim")); + } + if (swim.f_swim != 0. and swim.v_swim != 0.) { + throw std::invalid_argument(error_msg( + "swimming", + "cannot be set with 'v_swim' and 'f_swim' at the same time")); + } + if (dict.count("mode") != 0) { + auto const mode = get_value(dict.at("mode")); + if (mode == "pusher") { + swim.push_pull = -1; + } else if (mode == "puller") { + swim.push_pull = +1; + } else if (mode == "N/A") { + swim.push_pull = 0; + } else { + throw std::invalid_argument( + error_msg("swimming.mode", + "has to be either 'pusher', 'puller' or 'N/A'")); + } + } + if (dict.count("dipole_length") != 0) { + swim.dipole_length = get_value(dict.at("dipole_length")); + } + set_particle_swimming(m_pid, swim); + }, + [this]() { + auto const &p = particle(); + auto const &swim = p.swimming(); + std::string mode; + if (swim.push_pull == -1) { + mode = "pusher"; + } else if (swim.push_pull == 1) { + mode = "puller"; + } else { + mode = "N/A"; + } + return VariantMap{{{"mode", mode}, + {"v_swim", swim.v_swim}, + {"f_swim", swim.f_swim}, + {"dipole_length", swim.dipole_length}}}; + }}, +#endif // ENGINE + }); +} + +Variant ParticleHandle::do_call_method(std::string const &name, + VariantMap const ¶ms) { + if (name == "get_bonds_view") { + std::vector> bonds_flat; + for (auto const &bond_view : get_particle_bonds(m_pid)) { + std::vector bond_flat; + bond_flat.emplace_back(bond_view.bond_id()); + for (auto const pid : bond_view.partner_ids()) { + bond_flat.emplace_back(pid); + } + bonds_flat.emplace_back(std::move(bond_flat)); + } + return make_vector_of_variants(bonds_flat); + } + if (name == "add_bond") { + add_particle_bond(m_pid, get_bond_vector(params)); + } else if (name == "del_bond") { + delete_particle_bond(m_pid, get_bond_vector(params)); + } else if (name == "delete_all_bonds") { + delete_particle_bonds(m_pid); + } else if (name == "is_valid_bond_id") { + auto const bond_id = get_value(params, "bond_id"); + return bonded_ia_params_zero_based_type(bond_id) != 0; + } + if (name == "remove_particle") { + remove_particle(m_pid); +#ifdef VIRTUAL_SITES_RELATIVE + } else if (name == "vs_relate_to") { + vs_relate_to(m_pid, get_value(params, "pid")); +#endif // VIRTUAL_SITES_RELATIVE +#ifdef EXCLUSIONS + } else if (name == "has_exclusion") { + auto const &p = get_particle_data(m_pid); + return p.has_exclusion(get_value(params, "pid")); + } + if (name == "add_exclusion") { + add_particle_exclusion(m_pid, get_value(params, "pid")); + } else if (name == "del_exclusion") { + remove_particle_exclusion(m_pid, get_value(params, "pid")); + } else if (name == "set_exclusions") { + auto const &p = particle(); + for (auto const pid : p.exclusions_as_vector()) { + remove_particle_exclusion(m_pid, pid); + } + std::vector exclusion_list; + try { + auto const pid = get_value(params, "p_ids"); + exclusion_list.push_back(pid); + } catch (...) { + exclusion_list = get_value>(params, "p_ids"); + } + for (auto const pid : exclusion_list) { + if (!p.has_exclusion(pid)) { + add_particle_exclusion(m_pid, pid); + } + } + } else if (name == "get_exclusions") { + return particle().exclusions_as_vector(); +#endif // EXCLUSIONS +#ifdef ROTATION + } + if (name == "rotate_particle") { + rotate_particle(m_pid, get_value(params, "axis"), + get_value(params, "angle")); + } + if (name == "convert_vector_body_to_space") { + auto const &p = get_particle_data(m_pid); + return convert_vector_body_to_space( + p, get_value(params, "vec")) + .as_vector(); + } + if (name == "convert_vector_space_to_body") { + auto const &p = get_particle_data(m_pid); + return convert_vector_space_to_body( + p, get_value(params, "vec")) + .as_vector(); +#endif // ROTATION + } + return {}; +} + +void ParticleHandle::do_construct(VariantMap const ¶ms) { + m_pid = (params.count("id")) ? get_value(params, "id") + : get_maximal_particle_id() + 1; + + // create a new particle if extra arguments were passed + if ((params.size() - params.count("id")) > 0) { + if (particle_exists(m_pid)) { + throw std::invalid_argument("Particle " + std::to_string(m_pid) + + " already exists"); + } + + // create a default-constructed particle + auto const pos = get_value(params, "pos"); + place_particle(m_pid, pos); + + // set particle properties (filter out read-only and deferred properties) + std::vector skip = { + "id", "pos", "dipm", "pos_folded", "lees_edwards_flag", + "image_box", "node", "bonds", "exclusions", + }; + for (auto const &kv : params) { + if (std::find(skip.begin(), skip.end(), kv.first) == skip.end()) { + do_set_parameter(kv.first, kv.second); + } + } + } +} + +} // namespace Particles +} // namespace ScriptInterface diff --git a/src/script_interface/particle_data/ParticleHandle.hpp b/src/script_interface/particle_data/ParticleHandle.hpp new file mode 100644 index 00000000000..b5f184c133e --- /dev/null +++ b/src/script_interface/particle_data/ParticleHandle.hpp @@ -0,0 +1,52 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_PARTICLE_DATA_PARTICLE_HANDLE_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_PARTICLE_DATA_PARTICLE_HANDLE_HPP + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include "core/Particle.hpp" + +#include + +Particle const &get_particle_data(int p_id); + +namespace ScriptInterface { +namespace Particles { + +class ParticleHandle : public AutoParameters { + int m_pid; + + Particle const &particle() const { return get_particle_data(m_pid); }; + +public: + ParticleHandle(); + + Variant do_call_method(std::string const &name, + VariantMap const ¶ms) override; + + void do_construct(VariantMap const ¶ms) override; +}; + +} // namespace Particles +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/particle_data/initialize.cpp b/src/script_interface/particle_data/initialize.cpp new file mode 100644 index 00000000000..b79d041f9f2 --- /dev/null +++ b/src/script_interface/particle_data/initialize.cpp @@ -0,0 +1,32 @@ +/* + * 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 "ParticleHandle.hpp" + +namespace ScriptInterface { +namespace Particles { + +void initialize(Utils::Factory *om) { + om->register_new("Particles::ParticleHandle"); +} + +} // namespace Particles +} // namespace ScriptInterface diff --git a/src/script_interface/particle_data/initialize.hpp b/src/script_interface/particle_data/initialize.hpp new file mode 100644 index 00000000000..5b239d2068e --- /dev/null +++ b/src/script_interface/particle_data/initialize.hpp @@ -0,0 +1,35 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_PARTICLE_DATA_INITIALIZE_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_PARTICLE_DATA_INITIALIZE_HPP + +#include + +#include + +namespace ScriptInterface { +namespace Particles { + +void initialize(Utils::Factory *om); + +} // namespace Particles +} // namespace ScriptInterface + +#endif diff --git a/testsuite/python/engine_lb.py b/testsuite/python/engine_lb.py index f7ed95ca8db..c10d8d8b813 100644 --- a/testsuite/python/engine_lb.py +++ b/testsuite/python/engine_lb.py @@ -84,13 +84,6 @@ def tearDown(self): self.system.actors.clear() self.system.thermostat.turn_off() - def test_conflicting_parameters(self): - """v_swim and f_swim can't be set at the same time - """ - swimmer = self.system.part.add(pos=[3] * 3) - with self.assertRaises(Exception): - swimmer.swimming = {"v_swim": 0.3, "f_swim": 0.6} - def test_momentum_conservation(self): """friction as well as 'active' forces apply to particles and to the fluid, so total momentum is conserved diff --git a/testsuite/python/mdanalysis.py b/testsuite/python/mdanalysis.py index 4fbee40113c..3fde9a219ad 100644 --- a/testsuite/python/mdanalysis.py +++ b/testsuite/python/mdanalysis.py @@ -33,24 +33,29 @@ @utx.skipIfMissingModules("MDAnalysis") +@utx.skipIfMissingFeatures(["ELECTROSTATICS"]) class TestMDAnalysis(ut.TestCase): system = espressomd.System(box_l=[10.0, 10.0, 10.0]) system.time_step = 0.001 system.cell_system.skin = 0.1 - for i in range(10): - system.part.add(id=i, pos=[i, i % 2, 0], v=[0, i, -i], f=[1, 2 * i, 0], - type=i % 2, q=i % 3 - 1) + def setUp(self): + system = self.system + for i in range(10): + system.part.add(id=i, pos=[i, i % 2, 0], v=[0, i, -i], f=[1, 2 * i, 0], + type=i % 2, q=i % 3 - 1) - bond = espressomd.interactions.HarmonicBond(k=1., r_0=1.2, r_cut=2.0) - angle = espressomd.interactions.AngleCosine(bend=1., phi0=2 * np.pi / 3) - dihe = espressomd.interactions.Dihedral(bend=1., mult=2, phase=np.pi / 3) - system.bonded_inter.add(bond) - system.bonded_inter.add(angle) - system.bonded_inter.add(dihe) - system.part.by_id(1).add_bond((bond, 0)) - system.part.by_id(3).add_bond((angle, 2, 4)) - system.part.by_id(6).add_bond((dihe, 5, 7, 8)) + bond = espressomd.interactions.HarmonicBond(k=1., r_0=1.2, r_cut=2.0) + angle = espressomd.interactions.AngleCosine( + bend=1., phi0=2 * np.pi / 3) + dihe = espressomd.interactions.Dihedral( + bend=1., mult=2, phase=np.pi / 3) + system.bonded_inter.add(bond) + system.bonded_inter.add(angle) + system.bonded_inter.add(dihe) + system.part.by_id(1).add_bond((bond, 0)) + system.part.by_id(3).add_bond((angle, 2, 4)) + system.part.by_id(6).add_bond((dihe, 5, 7, 8)) def test_universe(self): system = self.system diff --git a/testsuite/python/particle.py b/testsuite/python/particle.py index eb1002fa594..d7fb75f551d 100644 --- a/testsuite/python/particle.py +++ b/testsuite/python/particle.py @@ -20,6 +20,7 @@ import unittest_decorators as utx import espressomd import espressomd.interactions +import espressomd.particle_data import numpy as np import collections import itertools @@ -44,8 +45,7 @@ class ParticleProperties(ut.TestCase): system.bonded_inter.add(f2) def setUp(self): - if not self.system.part.exists(self.pid): - self.partcl = self.system.part.add(id=self.pid, pos=(0, 0, 0)) + self.partcl = self.system.part.add(id=self.pid, pos=(0, 0, 0)) def tearDown(self): self.system.part.clear() @@ -180,7 +180,7 @@ def test_gamma_rot_single(self): test_dipm = generateTestForScalarProperty("dipm", -9.7) if espressomd.has_features(["VIRTUAL_SITES"]): - test_virtual = generateTestForScalarProperty("virtual", 1) + test_virtual = generateTestForScalarProperty("virtual", True) @utx.skipIfMissingFeatures(["VIRTUAL_SITES_RELATIVE"]) def test_vs_relative(self): @@ -196,17 +196,18 @@ def test_vs_relative(self): 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"): + error_msg = r"attribute 'vs_relative' of 'ParticleHandle' must take the form \[id, distance, quaternion\]" + with self.assertRaisesRegex(ValueError, error_msg): p1.vs_relative = (0, 5.0) - with self.assertRaisesRegex(ValueError, "particle id has to be given as an int"): + with self.assertRaisesRegex(ValueError, error_msg): p1.vs_relative = ('0', 5.0, (1, 0, 0, 0)) - with self.assertRaisesRegex(ValueError, "distance has to be given as a float"): + with self.assertRaisesRegex(ValueError, error_msg): p1.vs_relative = (0, '5', (1, 0, 0, 0)) - with self.assertRaisesRegex(ValueError, "quaternion has to be given as a tuple of 4 floats"): + with self.assertRaisesRegex(ValueError, error_msg): p1.vs_relative = (0, 5.0, (1, 0, 0)) - with self.assertRaisesRegex(ValueError, "quaternion is zero"): + with self.assertRaisesRegex(ValueError, error_msg): p1.vs_relative = (0, 5.0, (0, 0, 0, 0)) - with self.assertRaisesRegex(ValueError, "quaternion is zero"): + with self.assertRaisesRegex(ValueError, "attribute 'vs_quat' of 'ParticleHandle' must be non-zero"): p1.vs_quat = [0, 0, 0, 0] @utx.skipIfMissingFeatures(["EXCLUSIONS"]) @@ -241,7 +242,7 @@ def test_contradicting_properties_dip_quat(self): @utx.skipIfMissingFeatures(["ROTATION"]) def test_invalid_quat(self): system = self.system - with self.assertRaisesRegex(ValueError, "quaternion is zero"): + with self.assertRaisesRegex(ValueError, "attribute 'quat' of 'ParticleHandle' must be non-zero"): system.part.add(pos=[0., 0., 0.], quat=[0., 0., 0., 0.]) @utx.skipIfMissingFeatures("ELECTROSTATICS") @@ -331,13 +332,9 @@ def test_invalid_particle_ids_exceptions(self): self.system.part.clear() handle_to_non_existing_particle = self.system.part.by_id(42) 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 + handle_to_non_existing_particle.type for i in range(1, 10): - p._id = -i + p = espressomd.particle_data.ParticleHandle(id=-i) with self.assertRaisesRegex(ValueError, f"Invalid particle id: {-i}"): p.node with self.assertRaisesRegex(ValueError, f"Invalid particle id: {-i}"): @@ -356,26 +353,40 @@ def test_invalid_particle_creation(self): with self.assertRaisesRegex(ValueError, err_msg): self.system.part.add([1, 2]) + def test_invalid_particle_attributes(self): + p = self.partcl + err_msg = "attribute '{}' of 'ParticleHandle' {}" + with self.assertRaisesRegex(ValueError, err_msg.format("type", "must be an integer >= 0")): + p.type = -1 + with self.assertRaisesRegex(ValueError, err_msg.format("mol_id", "must be an integer >= 0")): + p.mol_id = -1 + if espressomd.has_features("ENGINE"): + with self.assertRaisesRegex(ValueError, err_msg.format("swimming", "cannot be set with 'v_swim' and 'f_swim' at the same time")): + p.swimming = {"v_swim": 0.3, "f_swim": 0.6} + with self.assertRaisesRegex(ValueError, err_msg.format("swimming.mode", "has to be either 'pusher', 'puller' or 'N/A'")): + p.swimming = {"v_swim": 0.3, "mode": "invalid"} + def test_parallel_property_setters(self): - s = self.system - s.part.clear() - partcls = s.part.add(pos=s.box_l * np.random.random((100, 3))) + system = self.system + system.part.clear() + partcls = system.part.add( + pos=system.box_l * np.random.random((100, 3))) # Copy individual properties of particle 0 print( "If this test hangs, there is an mpi deadlock in a particle property setter.") - for p in espressomd.particle_data.particle_attributes: + for attr in espressomd.particle_data.particle_attributes: # Uncomment to identify guilty property - # print( p) + # print(attr) - assert hasattr(s.part.by_id(0), p), \ + assert hasattr(system.part.by_id(0), attr), \ "Inconsistency between ParticleHandle and particle_data.particle_attributes" try: - setattr(partcls, p, getattr(s.part.by_id(0), p)) - except AttributeError: - print("Skipping read-only", p) + setattr(partcls, attr, getattr(system.part.by_id(0), attr)) + except RuntimeError as err: + self.assertEqual(str(err), f"Parameter '{attr}' is read-only.") # Cause a different mpi callback to uncover deadlock immediately - _ = getattr(partcls, p) + _ = getattr(partcls, attr) def test_remove_particle(self): """Tests that if a particle is removed, @@ -523,7 +534,7 @@ def test_update(self): self.system.part.clear() p = self.system.part.add(pos=0.5 * self.system.box_l) # cannot change id (to avoid corrupting caches in the core) - with self.assertRaisesRegex(Exception, "Cannot change particle id."): + with self.assertRaisesRegex(RuntimeError, "Cannot change particle id"): p.update({'id': 1}) # check value change new_pos = [1., 2., 3.] @@ -532,11 +543,9 @@ def test_update(self): # updating self should not change anything pdict = p.to_dict() del pdict['id'] - del pdict['_id'] p.update(pdict) new_pdict = p.to_dict() del new_pdict['id'] - del new_pdict['_id'] self.assertEqual(str(new_pdict), str(pdict)) diff --git a/testsuite/python/particle_slice.py b/testsuite/python/particle_slice.py index 7b2561ba5ae..cfff6076a4d 100644 --- a/testsuite/python/particle_slice.py +++ b/testsuite/python/particle_slice.py @@ -76,9 +76,10 @@ def test_3_set_one_value(self): @utx.skipIfMissingFeatures(["EXTERNAL_FORCES"]) def test_4_str(self): self.assertEqual(repr(self.p0.fix), - repr(np.array([0, 0, 0]))) + repr(np.array([False, False, False]))) self.assertEqual(repr(self.p0p1.fix), - repr(np.array([[0, 0, 0], [0, 0, 1]]))) + repr(np.array([[False, False, False], + [False, False, True]]))) def test_pos_str(self): self.p0.pos = [0, 0, 0] @@ -113,7 +114,7 @@ def test_bonds(self): # Setter # tuple - b = fene, 0 + b = (fene, 0) self.p2.bonds = b self.assertEqual(self.all_partcls.bonds, [(), (), ((fene, 0),), ()]) @@ -171,7 +172,7 @@ def test_bonds(self): # tuple for all self.all_partcls.bonds = [] - b = fene, 0 + b = (fene, 0) self.p2p3.bonds = b self.assertEqual(self.all_partcls.bonds, [(), (), ((fene, 0),), ((fene, 0),)]) @@ -334,18 +335,18 @@ def test_vs_relative(self): all_partcls = self.system.part.all() self.assertEqual(repr(all_partcls.vs_relative), - repr([(1, 1.0, np.array([1., 1., 1., 1.])), - (0, 0.0, np.array([1., 0., 0., 0.])), - (0, 0.0, np.array([1., 0., 0., 0.])), - (0, 0.0, np.array([1., 0., 0., 0.]))])) + repr([[1, 1.0, np.array([1., 1., 1., 1.])], + [0, 0.0, np.array([1., 0., 0., 0.])], + [0, 0.0, np.array([1., 0., 0., 0.])], + [0, 0.0, np.array([1., 0., 0., 0.])]])) all_partcls.vs_relative = [1, 1.0, (1.0, 1.0, 1.0, 1.0)] self.assertEqual(repr(all_partcls.vs_relative), - repr([(1, 1.0, np.array([1., 1., 1., 1.])), - (1, 1.0, np.array([1., 1., 1., 1.])), - (1, 1.0, np.array([1., 1., 1., 1.])), - (1, 1.0, np.array([1., 1., 1., 1.]))])) + repr([[1, 1.0, np.array([1., 1., 1., 1.])], + [1, 1.0, np.array([1., 1., 1., 1.])], + [1, 1.0, np.array([1., 1., 1., 1.])], + [1, 1.0, np.array([1., 1., 1., 1.])]])) all_partcls.vs_relative = [[1, 1.0, (1.0, 1.0, 1.0, 1.0)], [1, 2.0, (1.0, 1.0, 1.0, 1.0)], @@ -353,10 +354,10 @@ def test_vs_relative(self): [1, 4.0, (1.0, 1.0, 1.0, 1.0)]] self.assertEqual(repr(all_partcls.vs_relative), - repr([(1, 1.0, np.array([1., 1., 1., 1.])), - (1, 2.0, np.array([1., 1., 1., 1.])), - (1, 3.0, np.array([1., 1., 1., 1.])), - (1, 4.0, np.array([1., 1., 1., 1.]))])) + repr([[1, 1.0, np.array([1., 1., 1., 1.])], + [1, 2.0, np.array([1., 1., 1., 1.])], + [1, 3.0, np.array([1., 1., 1., 1.])], + [1, 4.0, np.array([1., 1., 1., 1.])]])) def test_multiadd(self): self.system.part.clear() diff --git a/testsuite/python/polymer_diamond.py b/testsuite/python/polymer_diamond.py index f719e572787..093c1313cb3 100644 --- a/testsuite/python/polymer_diamond.py +++ b/testsuite/python/polymer_diamond.py @@ -25,6 +25,7 @@ import espressomd.interactions +@utx.skipIfMissingFeatures(["ELECTROSTATICS"]) class DiamondPolymer(ut.TestCase): """ Test the functionality of espressomd.polymer.setup_diamond_polymer() @@ -66,7 +67,6 @@ def setUp(self): def tearDown(self): self.system.part.clear() - @utx.skipIfMissingFeatures(["ELECTROSTATICS"]) def test_particle_properties(self): """ checks if the particles created have the right type and charge diff --git a/testsuite/python/rotational_dynamics.py b/testsuite/python/rotational_dynamics.py index e376a558e37..e60ff865c1c 100644 --- a/testsuite/python/rotational_dynamics.py +++ b/testsuite/python/rotational_dynamics.py @@ -22,7 +22,7 @@ import unittest_decorators as utx -@utx.skipIfMissingFeatures("ROTATION") +@utx.skipIfMissingFeatures(["ROTATION", "ROTATIONAL_INERTIA"]) class Integrate(ut.TestCase): system = espressomd.System(box_l=[1., 1., 1.]) diff --git a/testsuite/python/virtual_sites_relative.py b/testsuite/python/virtual_sites_relative.py index 747349398c0..78c5c4a3743 100644 --- a/testsuite/python/virtual_sites_relative.py +++ b/testsuite/python/virtual_sites_relative.py @@ -146,7 +146,7 @@ def test_vs_exceptions(self): p2 = system.part.add(pos=[1.0, 1.0, 1.0], rotation=3 * [True], id=2) p3 = system.part.add(pos=[1.0, 1.0, 1.0], rotation=3 * [True], 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"): + 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"): From dd90d0e2f0e9eb7b047f7049c1056f39ce552bf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 22 Jul 2022 14:12:50 +0200 Subject: [PATCH 14/85] script_interface: Rewrite ParticleList --- src/python/espressomd/particle_data.pxd | 18 -- src/python/espressomd/particle_data.pyx | 180 +++++------------- .../particle_data/CMakeLists.txt | 3 +- .../particle_data/ParticleList.cpp | 161 ++++++++++++++++ .../particle_data/ParticleList.hpp | 46 +++++ .../particle_data/initialize.cpp | 2 + testsuite/python/particle.py | 7 +- 7 files changed, 269 insertions(+), 148 deletions(-) create mode 100644 src/script_interface/particle_data/ParticleList.cpp create mode 100644 src/script_interface/particle_data/ParticleList.hpp diff --git a/src/python/espressomd/particle_data.pxd b/src/python/espressomd/particle_data.pxd index 1a10e7d064c..751aa243fa7 100644 --- a/src/python/espressomd/particle_data.pxd +++ b/src/python/espressomd/particle_data.pxd @@ -17,7 +17,6 @@ # along with this program. If not, see . # # Here we create something to handle particles -from .utils cimport Vector4d, Vector3d, Vector3i from libcpp cimport bool from libcpp.vector cimport vector # import std::vector as vector @@ -26,26 +25,9 @@ include "myconfig.pxi" cdef extern from "particle_node.hpp": void prefetch_particle_data(vector[int] ids) - void place_particle(int p_id, const Vector3d & pos) except + - - void remove_particle(int p_id) except + - - void remove_all_particles() except + - bool particle_exists(int p_id) - int get_particle_node(int p_id) except + - - vector[int] get_particle_ids() except + - int get_maximal_particle_id() - int get_n_part() - -cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": - int max_seen_particle_type - -cdef extern from "bonded_interactions/rigid_bond.hpp": - extern int n_rigidbonds cdef class _ParticleSliceImpl: cdef public id_selection diff --git a/src/python/espressomd/particle_data.pyx b/src/python/espressomd/particle_data.pyx index fc7fe9d4f0d..28ff820775c 100644 --- a/src/python/espressomd/particle_data.pyx +++ b/src/python/espressomd/particle_data.pyx @@ -1015,11 +1015,31 @@ class ParticleSlice(_ParticleSliceImpl): return odict -cdef class ParticleList: +@script_interface_register +class ParticleList(ScriptInterfaceHelper): """ Provides access to the particles. + Attributes + ---------- + highest_particle_id: :obj:`int` + Largest particle id. + + Methods + ------- + clear(): + Remove all particles. + + See Also + -------- + :meth:`espressomd.particle_data.ParticleHandle.remove` + """ + _so_name = "Particles::ParticleList" + _so_creation_policy = "LOCAL" + _so_bind_methods = ( + "clear", + ) def by_id(self, p_id): """ @@ -1037,49 +1057,15 @@ cdef class ParticleList: """ Get a slice containing all particles. """ - all_ids = get_particle_ids() + all_ids = self.call_method("get_particle_ids") return self.by_ids(all_ids) - # __getstate__ and __setstate__ define the pickle interaction - def __getstate__(self): - """Attributes to pickle. - - Content of ``particle_attributes``, minus a few exceptions: - - - :attr:`~ParticleHandle.dip`, :attr:`~ParticleHandle.director`: - Setting only the director will overwrite the orientation of the - particle around the axis parallel to dipole moment/director. - Quaternions contain the full info. - - :attr:`~ParticleHandle.id`: The particle id is used as the - storage key when pickling all particles via :class:`ParticleList`, - and the interface (rightly) does not support changing of the id - after the particle was created. - - :attr:`~ParticleHandle.image_box`, :attr:`~ParticleHandle.node` - - """ - - odict = {} - for p in self: - pdict = p.to_dict() - del pdict["id"] - odict[p.id] = pdict - return odict - - def __setstate__(self, params): - exclusions = collections.OrderedDict() - for particle_number in params.keys(): - params[particle_number]["id"] = particle_number - IF EXCLUSIONS: - exclusions[particle_number] = params[particle_number][ - "exclusions"] - del params[particle_number]["exclusions"] - self._place_new_particle(params[particle_number]) - IF EXCLUSIONS: - for pid in exclusions: - self.by_id(pid).exclusions = exclusions[pid] - def __len__(self): - return get_n_part() + return self.call_method("get_n_part") + + @property + def highest_particle_id(self): + return self.call_method("get_highest_particle_id") def add(self, *args, **kwargs): """ @@ -1135,40 +1121,18 @@ cdef class ParticleList: return self._place_new_particle(particles_dict) def _place_new_particle(self, p_dict): - # Handling of particle id - if "id" not in p_dict: - # Generate particle id - p_dict["id"] = get_maximal_particle_id() + 1 - else: - 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_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_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. - check_type_or_throw_except( - p_dict["pos"], 3, float, "Position must be 3 floats.") - place_particle(p_dict["id"], make_Vector3d(p_dict["pos"])) - - # position is taken care of - del p_dict["pos"] - pid = p_dict.pop("id") - - if p_dict != {}: - self.by_id(pid).update(p_dict) - - return self.by_id(pid) + bonds = [] + if "bonds" in p_dict: + bonds = p_dict.pop("bonds") + if nesting_level(bonds) == 1: + bonds = [bonds] + p_id = self.call_method("add_particle", **p_dict) + p = self.by_id(p_id) + for bond in bonds: + if len(bond): + bond = p.normalize_and_check_bond_or_throw_exception(bond) + p.add_verified_bond(bond) + return p def _place_new_particles(self, p_list_dict): # Check if all entries have the same length @@ -1181,8 +1145,8 @@ Set quat and scalar dipole moment (dipm) instead.") # If particle ids haven't been provided, use free ones # beyond the highest existing one if "id" not in p_list_dict: - first_id = get_maximal_particle_id() + 1 - p_list_dict["id"] = range(first_id, first_id + n_parts) + first_id = self.highest_particle_id + 1 + p_list_dict["id"] = np.arange(first_id, first_id + n_parts) # Place the particles for i in range(n_parts): @@ -1194,35 +1158,21 @@ Set quat and scalar dipole moment (dipm) instead.") # Iteration over all existing particles def __iter__(self): - ids = get_particle_ids() - - for i in ids: - yield self.by_id(i) + for p_id in self.call_method("get_particle_ids"): + yield self.by_id(p_id) def exists(self, idx): if is_valid_type(idx, int): - return particle_exists(idx) + return self.call_method("particle_exists", p_id=idx) if isinstance(idx, (slice, tuple, list, np.ndarray)): tf_array = np.zeros(len(idx), dtype=type(True)) for i in range(len(idx)): - tf_array[i] = particle_exists(idx[i]) + tf_array[i] = self.call_method("particle_exists", p_id=idx[i]) return tf_array - def clear(self): - """ - Removes all particles. - - See Also - -------- - add - :meth:`espressomd.particle_data.ParticleHandle.remove` - - """ - - remove_all_particles() - def __str__(self): - return "ParticleList([" + ",".join(get_particle_ids()) + "])" + return "ParticleList([" + \ + ",".join(map(str, self.call_method("get_particle_ids"))) + "])" def writevtk(self, fname, types='all'): """ @@ -1255,7 +1205,6 @@ Set quat and scalar dipole moment (dipm) instead.") """ - global box_l if not hasattr(types, '__iter__'): types = [types] @@ -1281,47 +1230,22 @@ Set quat and scalar dipole moment (dipm) instead.") if types == 'all' or p.type in types: vtk.write("{} {} {}\n".format(*p.v)) - property highest_particle_id: - """ - Largest particle id. - - """ - - def __get__(self): - return get_maximal_particle_id() - - property n_part_types: - """ - Number of particle types. - - """ - - def __get__(self): - return max_seen_particle_type - - property n_rigidbonds: - """ - Number of rigid bonds. - - """ - - def __get__(self): - return n_rigidbonds - def pairs(self): """ - Generator returns all pairs of particles. + Generate all pairs of particles. """ - ids = get_particle_ids() + ids = self.call_method("get_particle_ids") for i in ids: for j in ids[i + 1:]: yield (self.by_id(i), self.by_id(j)) def select(self, *args, **kwargs): - """Generates a particle slice by filtering particles via a user-defined criterion + """ + Generate a particle slice by filtering particles via a user-defined + criterion. Parameters: diff --git a/src/script_interface/particle_data/CMakeLists.txt b/src/script_interface/particle_data/CMakeLists.txt index 88c0788304e..b8b1386f6fc 100644 --- a/src/script_interface/particle_data/CMakeLists.txt +++ b/src/script_interface/particle_data/CMakeLists.txt @@ -1,4 +1,5 @@ target_sources( Espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/ParticleHandle.cpp) + ${CMAKE_CURRENT_SOURCE_DIR}/ParticleHandle.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ParticleList.cpp) diff --git a/src/script_interface/particle_data/ParticleList.cpp b/src/script_interface/particle_data/ParticleList.cpp new file mode 100644 index 00000000000..515343600f9 --- /dev/null +++ b/src/script_interface/particle_data/ParticleList.cpp @@ -0,0 +1,161 @@ +/* + * 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 "ParticleList.hpp" +#include "ParticleHandle.hpp" + +#include "script_interface/ObjectState.hpp" +#include "script_interface/ScriptInterface.hpp" + +#include "core/particle_node.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace ScriptInterface { +namespace Particles { + +#ifdef EXCLUSIONS +static void set_exclusions(ParticleHandle &p, Variant const &exclusions) { + p.do_call_method("set_exclusions", {{"p_ids", exclusions}}); +} +#endif // EXCLUSIONS + +static void set_bonds(ParticleHandle &p, Variant const &bonds) { + auto const bond_list_flat = get_value>>(bonds); + for (auto const &bond_flat : bond_list_flat) { + auto const bond_id = bond_flat[0]; + auto const part_id = + std::vector{bond_flat.begin() + 1, bond_flat.end()}; + p.do_call_method("add_bond", + {{"bond_id", bond_id}, {"part_id", std::move(part_id)}}); + } +} + +std::string ParticleList::get_internal_state() const { + auto const p_ids = get_particle_ids(); + std::vector object_states(p_ids.size()); + + boost::transform(p_ids, object_states.begin(), [](auto const p_id) { + ParticleHandle p_handle{}; + p_handle.do_construct({{"id", p_id}}); + auto const packed_state = p_handle.serialize(); + // custom particle serialization + auto state = Utils::unpack(packed_state); + state.name = "Particles::ParticleHandle"; + auto const bonds_view = p_handle.call_method("get_bonds_view", {}); + state.params.emplace_back( + std::pair{"bonds", pack(bonds_view)}); +#ifdef EXCLUSIONS + auto const exclusions = p_handle.call_method("get_exclusions", {}); + state.params.emplace_back( + std::pair{"exclusions", pack(exclusions)}); +#endif // EXCLUSIONS + return Utils::pack(state); + }); + + return Utils::pack(object_states); +} + +void ParticleList::set_internal_state(std::string const &state) { + auto const object_states = Utils::unpack>(state); +#ifdef EXCLUSIONS + std::unordered_map exclusions = {}; +#endif // EXCLUSIONS + std::unordered_map bonds = {}; + + for (auto const &packed_object : object_states) { + auto const state = Utils::unpack(packed_object); + auto o = std::dynamic_pointer_cast( + ObjectHandle::deserialize(packed_object, *ObjectHandle::context())); + auto const p_id = get_value(o->get_parameter("id")); + for (auto const &kv : state.params) { + if (kv.first == "bonds") { + bonds[p_id] = unpack(kv.second, {}); + } +#ifdef EXCLUSIONS + else if (kv.first == "exclusions") { + exclusions[p_id] = unpack(kv.second, {}); + } +#endif // EXCLUSIONS + } + } + + for (auto const p_id : get_particle_ids()) { + ParticleHandle p_handle{}; + p_handle.do_construct({{"id", p_id}}); + set_bonds(p_handle, bonds[p_id]); +#ifdef EXCLUSIONS + set_exclusions(p_handle, exclusions[p_id]); +#endif // EXCLUSIONS + } +} + +Variant ParticleList::do_call_method(std::string const &name, + VariantMap const ¶ms) { + if (name == "get_n_part") { + return get_n_part(); + } + if (name == "get_particle_ids") { + return get_particle_ids(); + } + if (name == "particle_exists") { + return particle_exists(get_value(params, "p_id")); + } + if (name == "clear") { + remove_all_particles(); + } else if (name == "add_particle") { + assert(params.count("bonds") == 0); + // sanitize particle properties +#ifdef DIPOLES + if (params.count("dip") and params.count("dipm")) { + throw std::invalid_argument("Contradicting attributes: 'dip' and 'dipm'. \ +Setting 'dip' is sufficient as the length of the vector defines the scalar \ +dipole moment."); + } + if (params.count("dip") and params.count("quat")) { + throw std::invalid_argument("Contradicting attributes: 'dip' and 'quat'. \ +Setting 'dip' overwrites the rotation of the particle around the dipole axis. \ +Set attribute 'quat' together with 'dipm' (scalar dipole moment) instead."); + } +#endif // DIPOLES + ParticleHandle p_handle{}; + p_handle.do_construct(params); +#ifdef EXCLUSIONS + if (params.count("exclusions")) { + set_exclusions(p_handle, params.at("exclusions")); + } +#endif // EXCLUSIONS + return p_handle.get_parameter("id"); + } + if (name == "get_highest_particle_id") { + return get_maximal_particle_id(); + } + return {}; +} + +} // namespace Particles +} // namespace ScriptInterface diff --git a/src/script_interface/particle_data/ParticleList.hpp b/src/script_interface/particle_data/ParticleList.hpp new file mode 100644 index 00000000000..9506506141c --- /dev/null +++ b/src/script_interface/particle_data/ParticleList.hpp @@ -0,0 +1,46 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_PARTICLE_DATA_PARTICLE_LIST_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_PARTICLE_DATA_PARTICLE_LIST_HPP + +#include "script_interface/ScriptInterface.hpp" + +#include + +namespace ScriptInterface { +namespace Particles { + +class ParticleList : public ObjectHandle { + +public: + Variant do_call_method(std::string const &name, + VariantMap const ¶ms) override; + + void do_construct(VariantMap const ¶ms) override {} + +private: + std::string get_internal_state() const override; + void set_internal_state(std::string const &state) override; +}; + +} // namespace Particles +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/particle_data/initialize.cpp b/src/script_interface/particle_data/initialize.cpp index b79d041f9f2..8a8f7d37338 100644 --- a/src/script_interface/particle_data/initialize.cpp +++ b/src/script_interface/particle_data/initialize.cpp @@ -20,12 +20,14 @@ #include "initialize.hpp" #include "ParticleHandle.hpp" +#include "ParticleList.hpp" namespace ScriptInterface { namespace Particles { void initialize(Utils::Factory *om) { om->register_new("Particles::ParticleHandle"); + om->register_new("Particles::ParticleList"); } } // namespace Particles diff --git a/testsuite/python/particle.py b/testsuite/python/particle.py index d7fb75f551d..53d8bd01888 100644 --- a/testsuite/python/particle.py +++ b/testsuite/python/particle.py @@ -328,7 +328,9 @@ def test_particle_numbering(self): p3 = system.part.add(pos=offset) self.assertEqual(p3.id, p0.id + 1) - def test_invalid_particle_ids_exceptions(self): + def test_invalid_particle_ids(self): + with self.assertRaisesRegex(ValueError, f"Particle {self.partcl.id} already exists"): + self.system.part.add(id=self.partcl.id, pos=self.partcl.pos) self.system.part.clear() handle_to_non_existing_particle = self.system.part.by_id(42) with self.assertRaisesRegex(RuntimeError, "Particle node for id 42 not found"): @@ -471,6 +473,9 @@ def test_coord_fold_corner_cases(self): self.assertGreaterEqual(p.pos_folded[i], 0) self.assertLess(p.pos_folded[i], system.box_l[i]) + def test_particle_list(self): + self.assertEqual(str(self.system.part), "ParticleList([17])") + def test_particle_slice(self): """Tests operations on slices of particles""" From 0cc3b590bcb793f25ff2ca3a1cb3d67dad3938f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 22 Jul 2022 14:33:54 +0200 Subject: [PATCH 15/85] script_interface: Rewrite ParticleSlice --- src/python/espressomd/cluster_analysis.py | 2 +- src/python/espressomd/particle_data.pxd | 34 ---- .../{particle_data.pyx => particle_data.py} | 169 ++++++------------ .../particle_data/CMakeLists.txt | 3 +- .../particle_data/ParticleSlice.cpp | 51 ++++++ .../particle_data/ParticleSlice.hpp | 57 ++++++ .../particle_data/initialize.cpp | 2 + testsuite/python/particle.py | 22 +-- testsuite/python/particle_slice.py | 3 +- testsuite/python/save_checkpoint.py | 2 + testsuite/python/test_checkpoint.py | 5 + 11 files changed, 185 insertions(+), 165 deletions(-) delete mode 100644 src/python/espressomd/particle_data.pxd rename src/python/espressomd/{particle_data.pyx => particle_data.py} (90%) create mode 100644 src/script_interface/particle_data/ParticleSlice.cpp create mode 100644 src/script_interface/particle_data/ParticleSlice.hpp diff --git a/src/python/espressomd/cluster_analysis.py b/src/python/espressomd/cluster_analysis.py index 6935292e96a..af279897041 100644 --- a/src/python/espressomd/cluster_analysis.py +++ b/src/python/espressomd/cluster_analysis.py @@ -73,7 +73,7 @@ def particles(self): :class:`espressomd.particle_data.ParticleSlice` """ - return ParticleSlice(self.particle_ids()) + return ParticleSlice(id_selection=self.particle_ids()) @script_interface_register diff --git a/src/python/espressomd/particle_data.pxd b/src/python/espressomd/particle_data.pxd deleted file mode 100644 index 751aa243fa7..00000000000 --- a/src/python/espressomd/particle_data.pxd +++ /dev/null @@ -1,34 +0,0 @@ -# -# 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 . -# -# Here we create something to handle particles -from libcpp cimport bool -from libcpp.vector cimport vector # import std::vector as vector - -include "myconfig.pxi" - -cdef extern from "particle_node.hpp": - void prefetch_particle_data(vector[int] ids) - - bool particle_exists(int p_id) - - int get_maximal_particle_id() - -cdef class _ParticleSliceImpl: - cdef public id_selection - cdef int _chunk_size diff --git a/src/python/espressomd/particle_data.pyx b/src/python/espressomd/particle_data.py similarity index 90% rename from src/python/espressomd/particle_data.pyx rename to src/python/espressomd/particle_data.py index 28ff820775c..b61c1b8eeaf 100644 --- a/src/python/espressomd/particle_data.pyx +++ b/src/python/espressomd/particle_data.py @@ -16,22 +16,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -include "myconfig.pxi" -cimport numpy as np import numpy as np -from cython.operator cimport dereference -from . cimport particle_data -from .interactions import BondedInteraction -from .interactions import BondedInteractions -from .interactions cimport bonded_ia_params_zero_based_type -from copy import copy import collections import functools +from .interactions import BondedInteraction +from .interactions import BondedInteractions from .utils import nesting_level, array_locked, is_valid_type, handle_errors -from .utils cimport make_array_locked, check_type_or_throw_except -from .utils cimport Vector3i, Vector3d, Vector4d -from .utils cimport make_Vector3d +from .utils import check_type_or_throw_except from .code_features import assert_features, has_features from .script_interface import script_interface_register, ScriptInterfaceHelper @@ -811,83 +803,38 @@ def rotate(self, axis, angle): particle_attributes.add("bonds") -cdef class _ParticleSliceImpl: - """Handles slice inputs. - - This base class should not be used directly. Use - :class:`espressomd.particle_data.ParticleSlice` instead, which contains - all the particle properties. +@script_interface_register +class ParticleSlice(ScriptInterfaceHelper): + """ + Handle slice inputs. Set values for selected slices or + return values as a single list. """ + _so_name = "Particles::ParticleSlice" + _so_creation_policy = "LOCAL" - def __cinit__(self, slice_, prefetch_chunk_size=10000): - # Chunk size for pre-fetch cache - self._chunk_size = prefetch_chunk_size - - # We distinguish two cases: - # * ranges and slices only specify lower and upper bounds for particle - # ids and optionally a step. Gaps in the id range are tolerated. - # * Explicit list/tuple/ndarray containing particle ids. Here - # all particles have to exist and the result maintains the order - # specified in the list. - if isinstance(slice_, (slice, range)): - self.id_selection = self._id_selection_from_slice(slice_) - elif isinstance(slice_, (list, tuple, np.ndarray)): - self._validate_pid_list(slice_) - self.id_selection = np.array(slice_, dtype=int) - else: - raise TypeError( - f"ParticleSlice must be initialized with an instance of " - f"slice or range, or with a list, tuple, or ndarray of ints, " - f"but got {slice_} of type {type(slice_)}") - - def _id_selection_from_slice(self, slice_): - """Returns an ndarray of particle ids to be included in the - ParticleSlice for a given range or slice object. - """ - # Prevent negative bounds - if (slice_.start is not None and slice_.start < 0) or \ - (slice_.stop is not None and slice_.stop < 0): - raise IndexError( - "Negative start and end ids are not supported on ParticleSlice") - - # We start with a full list of possible particle ids and then - # remove ids of non-existing particles - id_list = np.arange(get_maximal_particle_id() + 1, dtype=int) - id_list = id_list[slice_] - - # Generate a mask which will remove ids of non-existing particles - mask = np.empty(len(id_list), dtype=type(True)) - mask[:] = True - for i, id in enumerate(id_list): - if not particle_exists(id): - mask[i] = False - # Return the id list filtered by the mask - return id_list[mask] - - def _validate_pid_list(self, pid_list): - """Check that all entries are integers and the corresponding particles exist. Throw, otherwise.""" - # Check that all entries are some flavor of integer - for pid in pid_list: - if not is_valid_type(pid, int): - raise TypeError( - f"Particle id must be an integer but got {pid}") - if not particle_exists(pid): - raise IndexError(f"Particle does not exist {pid}") + def __init__(self, **kwargs): + super().__init__(**kwargs) + if "sip" not in kwargs: + for p_id in self.id_selection: + if not self.call_method("particle_exists", p_id=p_id): + raise IndexError(f"Particle does not exist: {p_id}") def __iter__(self): return self._id_gen() def _id_gen(self): - """Generator for chunked and prefetched iteration of particles. """ - for chunk in self.chunks(self.id_selection, self._chunk_size): - prefetch_particle_data(chunk) + Generator for chunked and prefetched iteration of particles. + """ + for chunk in self.chunks(self.id_selection, self.chunk_size): + self.call_method("prefetch_particle_data", chunk=chunk) for i in chunk: yield ParticleHandle(id=i) def chunks(self, l, n): - """Generator returning chunks of length n from l. + """ + Generator returning chunks of length n from l. """ for i in range(0, len(l), n): yield l[i:i + n] @@ -895,43 +842,40 @@ def chunks(self, l, n): def __len__(self): return len(self.id_selection) - property pos_folded: + @property + def pos_folded(self): """ Particle position (folded into central image). """ + pos_array = np.zeros((len(self.id_selection), 3)) + for i in range(len(self.id_selection)): + pos_array[i, :] = ParticleHandle( + id=self.id_selection[i]).pos_folded + return pos_array + + @pos_folded.setter + def pos_folded(self, value): + raise RuntimeError("Parameter 'pos_folded' is read-only.") + + def add_exclusion(self, _partner): + assert_features(["EXCLUSIONS"]) + for p_id in self.id_selection: + ParticleHandle(id=p_id).add_exclusion(_partner) - def __get__(self): - pos_array = np.zeros((len(self.id_selection), 3)) - for i in range(len(self.id_selection)): - pos_array[i, :] = ParticleHandle( - id=self.id_selection[i]).pos_folded - return pos_array - - def __set__(self, value): - raise RuntimeError("Parameter 'pos_folded' is read-only.") - - IF EXCLUSIONS: - def add_exclusion(self, _partner): - for p_id in self.id_selection: - ParticleHandle(id=p_id).add_exclusion(_partner) - - def delete_exclusion(self, _partner): - for p_id in self.id_selection: - ParticleHandle(id=p_id).delete_exclusion(_partner) + def delete_exclusion(self, _partner): + assert_features(["EXCLUSIONS"]) + for p_id in self.id_selection: + ParticleHandle(id=p_id).delete_exclusion(_partner) def __str__(self): - res = "" - pl = ParticleList() - for i in self.id_selection: - if pl.exists(i): - res += f"{pl.by_id(i)}, " - # Remove final comma - return f"ParticleSlice([{res[:-2]}])" + return "ParticleSlice([" + \ + ", ".join(str(ParticleHandle(id=i)) + for i in self.id_selection) + "])" def update(self, new_properties): if "id" in new_properties: - raise Exception("Cannot change particle id.") + raise RuntimeError("Cannot change particle id.") for k, v in new_properties.items(): setattr(self, k, v) @@ -969,17 +913,8 @@ def remove(self): for p_id in self.id_selection: ParticleHandle(id=p_id).remove() - -class ParticleSlice(_ParticleSliceImpl): - - """ - Handles slice inputs e.g. ``part[0:2]``. Sets values for selected slices or - returns values as a single list. - - """ - def __setattr__(self, name, value): - if name != "_chunk_size" and name not in particle_attributes: + if name != "chunk_size" and name != "id_selection" and name not in particle_attributes: raise AttributeError( f"ParticleHandle does not have the attribute {name}.") super().__setattr__(name, value) @@ -1051,7 +986,7 @@ def by_ids(self, ids): """ Get a slice of particles by their integer ids. """ - return ParticleSlice(ids) + return ParticleSlice(id_selection=ids) def all(self): """ @@ -1274,7 +1209,7 @@ def select(self, *args, **kwargs): for p in self: if args[0](p): ids.append(p.id) - return ParticleSlice(ids) + return ParticleSlice(id_selection=ids) # Did we get a set of keyword args? elif len(args) == 0: @@ -1295,7 +1230,7 @@ def select(self, *args, **kwargs): break if select: ids.append(p.id) - return ParticleSlice(ids) + return ParticleSlice(id_selection=ids) else: raise Exception( "select() takes either selection function as positional argument or a set of keyword arguments.") @@ -1410,7 +1345,7 @@ def get_attribute(particle_slice, attribute): # get first slice member to determine its type target = getattr(ParticleHandle( id=particle_slice.id_selection[0]), attribute) - if type(target) is array_locked: # vectorial quantity + if isinstance(target, array_locked): # vectorial quantity target_type = target.dtype else: # scalar quantity target_type = type(target) @@ -1428,7 +1363,7 @@ def get_attribute(particle_slice, attribute): return values - for attribute_name in particle_attributes: + for attribute_name in sorted(particle_attributes): if attribute_name in dir(ParticleSlice): continue diff --git a/src/script_interface/particle_data/CMakeLists.txt b/src/script_interface/particle_data/CMakeLists.txt index b8b1386f6fc..486011fa0ee 100644 --- a/src/script_interface/particle_data/CMakeLists.txt +++ b/src/script_interface/particle_data/CMakeLists.txt @@ -2,4 +2,5 @@ target_sources( Espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ParticleHandle.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/ParticleList.cpp) + ${CMAKE_CURRENT_SOURCE_DIR}/ParticleList.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ParticleSlice.cpp) diff --git a/src/script_interface/particle_data/ParticleSlice.cpp b/src/script_interface/particle_data/ParticleSlice.cpp new file mode 100644 index 00000000000..eef27f91f85 --- /dev/null +++ b/src/script_interface/particle_data/ParticleSlice.cpp @@ -0,0 +1,51 @@ +/* + * 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 "ParticleSlice.hpp" + +#include "script_interface/ScriptInterface.hpp" + +#include "core/particle_node.hpp" + +#include + +#include +#include + +namespace ScriptInterface { +namespace Particles { + +void ParticleSlice::do_construct(VariantMap const ¶ms) { + m_id_selection = get_value>(params, "id_selection"); + m_chunk_size = get_value_or(params, "prefetch_chunk_size", 10000); +} + +Variant ParticleSlice::do_call_method(std::string const &name, + VariantMap const ¶ms) { + if (name == "prefetch_particle_data") { + auto p_ids = get_value>(params, "chunk"); + prefetch_particle_data(Utils::Span(p_ids)); + } else if (name == "particle_exists") { + return particle_exists(get_value(params, "p_id")); + } + return {}; +} + +} // namespace Particles +} // namespace ScriptInterface diff --git a/src/script_interface/particle_data/ParticleSlice.hpp b/src/script_interface/particle_data/ParticleSlice.hpp new file mode 100644 index 00000000000..b6c2ae57bdb --- /dev/null +++ b/src/script_interface/particle_data/ParticleSlice.hpp @@ -0,0 +1,57 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_PARTICLE_DATA_PARTICLE_SLICE_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_PARTICLE_DATA_PARTICLE_SLICE_HPP + +#include "ParticleHandle.hpp" + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include +#include + +namespace ScriptInterface { +namespace Particles { + +class ParticleSlice : public AutoParameters { + std::vector m_id_selection; + int m_chunk_size; + +public: + ParticleSlice() { + add_parameters({ + {"chunk_size", AutoParameter::read_only, + [this]() { return m_chunk_size; }}, + {"id_selection", AutoParameter::read_only, + [this]() { return m_id_selection; }}, + }); + } + + void do_construct(VariantMap const ¶ms) override; + + Variant do_call_method(std::string const &name, + VariantMap const ¶ms) override; +}; + +} // namespace Particles +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/particle_data/initialize.cpp b/src/script_interface/particle_data/initialize.cpp index 8a8f7d37338..db308fcd2db 100644 --- a/src/script_interface/particle_data/initialize.cpp +++ b/src/script_interface/particle_data/initialize.cpp @@ -21,6 +21,7 @@ #include "ParticleHandle.hpp" #include "ParticleList.hpp" +#include "ParticleSlice.hpp" namespace ScriptInterface { namespace Particles { @@ -28,6 +29,7 @@ namespace Particles { void initialize(Utils::Factory *om) { om->register_new("Particles::ParticleHandle"); om->register_new("Particles::ParticleList"); + om->register_new("Particles::ParticleSlice"); } } // namespace Particles diff --git a/testsuite/python/particle.py b/testsuite/python/particle.py index 53d8bd01888..e9fdd5a2857 100644 --- a/testsuite/python/particle.py +++ b/testsuite/python/particle.py @@ -491,7 +491,7 @@ def test_particle_slice(self): all_partcls_empty.pos = ((1, 2, 3,),) # Slice containing particles - ids = [1, 4, 6, 3, 8, 9] + ids = [1, 4, 6, 3, 8, 9, 5] pos = np.random.random((len(ids), 3)) system.part.add(id=ids, pos=pos) @@ -502,24 +502,24 @@ def test_particle_slice(self): np.testing.assert_equal(all_partcls.pos, pos[np.argsort(ids)]) # Access via slicing - np.testing.assert_equal(system.part.by_ids(range(4, 9)).id, - [i for i in sorted(ids) if i >= 4 and i < 9]) - np.testing.assert_equal(system.part.by_ids(range(9, 4, -1)).id, - [i for i in sorted(ids, key=lambda i:-i) if i > 4 and i <= 9]) + np.testing.assert_equal(system.part.by_ids(range(3, 6)).id, + [i for i in sorted(ids) if i >= 3 and i < 6]) + np.testing.assert_equal(system.part.by_ids(range(6, 3, -1)).id, + [i for i in sorted(ids, key=lambda i:-i) if i > 3 and i <= 6]) # Setting particle properties on a slice - system.part.by_ids(range(5)).pos = 0, 0, 0 + system.part.by_ids(range(9, 10)).pos = (0, 0, 0) np.testing.assert_equal(system.part.all().pos, - [pos[i] if ids[i] >= 5 else [0, 0, 0] for i in np.argsort(ids)]) + [pos[i] if ids[i] != 9 else [0, 0, 0] for i in np.argsort(ids)]) # Slice access via explicit list of ids np.testing.assert_equal(system.part.by_ids(ids[1:4]).id, ids[1:4]) # Check that ids passed in an explicit list must exist - with self.assertRaises(IndexError): + with self.assertRaisesRegex(IndexError, "Particle does not exist: 99"): system.part.by_ids([99, 3]) # Check that wrong types are not accepted - with self.assertRaises(TypeError): - system.part.by_ids([[ids[0], 1.2]]) + with self.assertRaisesRegex(RuntimeError, "it contains a value that is not convertible to 'int'"): + system.part.by_ids([ids[0], 1.2]) def test_to_dict(self): self.system.part.clear() @@ -541,6 +541,8 @@ def test_update(self): # cannot change id (to avoid corrupting caches in the core) with self.assertRaisesRegex(RuntimeError, "Cannot change particle id"): p.update({'id': 1}) + with self.assertRaisesRegex(RuntimeError, "Cannot change particle id"): + self.system.part.all().update({'id': 1}) # check value change new_pos = [1., 2., 3.] p.update({'pos': new_pos}) diff --git a/testsuite/python/particle_slice.py b/testsuite/python/particle_slice.py index cfff6076a4d..4cd2aaf0a8a 100644 --- a/testsuite/python/particle_slice.py +++ b/testsuite/python/particle_slice.py @@ -381,8 +381,7 @@ def test_multiadd(self): def test_empty(self): np.testing.assert_array_equal( - self.system.part.by_ids( - []).pos, np.empty(0)) + self.system.part.by_ids([]).pos, np.empty(0)) def test_len(self): self.assertEqual(len(self.system.part.by_ids([])), 0) diff --git a/testsuite/python/save_checkpoint.py b/testsuite/python/save_checkpoint.py index 03b6f18f717..09ebda6f002 100644 --- a/testsuite/python/save_checkpoint.py +++ b/testsuite/python/save_checkpoint.py @@ -111,6 +111,7 @@ p4 = system.part.add(id=4, pos=system.box_l / 2.0 + 1.0, type=1) system.comfixed.types = [0, 2] +p_slice = system.part.by_ids([4, 1]) if espressomd.has_features('P3M') and ('P3M' in modes or 'ELC' in modes): if espressomd.gpu_available() and 'P3M.GPU' in modes: @@ -280,6 +281,7 @@ checkpoint.register("ibm_tribend_bond") checkpoint.register("ibm_triel_bond") checkpoint.register("break_spec") +checkpoint.register("p_slice") if espressomd.has_features("H5MD"): checkpoint.register("h5") checkpoint.register("h5_units") diff --git a/testsuite/python/test_checkpoint.py b/testsuite/python/test_checkpoint.py index cece3cff4d8..a90de4a52ef 100644 --- a/testsuite/python/test_checkpoint.py +++ b/testsuite/python/test_checkpoint.py @@ -161,6 +161,11 @@ def test_part(self): np.testing.assert_allclose(np.copy(p1.f), particle_force0) np.testing.assert_allclose(np.copy(p2.f), particle_force1) + def test_part_slice(self): + np.testing.assert_allclose(np.copy(p_slice.id), [4, 1]) + np.testing.assert_allclose(np.copy(p_slice.pos), + np.copy(system.part.by_ids([4, 1]).pos)) + def test_bonded_interactions_serialization(self): ''' Check that particles at the interface between two MPI nodes still From 45054fe803687ef606e127a575a3bc8f98e7b8e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 22 Jul 2022 15:55:01 +0200 Subject: [PATCH 16/85] script_interface: Rewrite Polymer module --- src/core/polymer.cpp | 2 +- src/python/espressomd/polymer.pxd | 31 ---- .../espressomd/{polymer.pyx => polymer.py} | 153 +++++------------- .../particle_data/CMakeLists.txt | 3 +- .../particle_data/Polymer.cpp | 60 +++++++ .../particle_data/Polymer.hpp | 39 +++++ .../particle_data/initialize.cpp | 2 + testsuite/python/particle.py | 6 + testsuite/python/polymer_linear.py | 8 +- 9 files changed, 155 insertions(+), 149 deletions(-) delete mode 100644 src/python/espressomd/polymer.pxd rename src/python/espressomd/{polymer.pyx => polymer.py} (65%) create mode 100644 src/script_interface/particle_data/Polymer.cpp create mode 100644 src/script_interface/particle_data/Polymer.hpp diff --git a/src/core/polymer.cpp b/src/core/polymer.cpp index f41404d4e5d..d17e2f5ea5c 100644 --- a/src/core/polymer.cpp +++ b/src/core/polymer.cpp @@ -20,7 +20,7 @@ */ /** \file * This file contains everything needed to create a start-up configuration - * of (partially charged) polymer chains with counterions and salt molecules, + * of (possibly charged) polymer chains with counterions and salt molecules, * assigning velocities to the particles and cross-linking the polymers if * necessary. * diff --git a/src/python/espressomd/polymer.pxd b/src/python/espressomd/polymer.pxd deleted file mode 100644 index 8f81e8f279d..00000000000 --- a/src/python/espressomd/polymer.pxd +++ /dev/null @@ -1,31 +0,0 @@ -# -# 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 . -# - -from libcpp.vector cimport vector -from .utils cimport Vector3d - -cdef extern from "PartCfg.hpp": - cppclass PartCfg: - pass - -cdef extern from "partCfg_global.hpp": - PartCfg & partCfg() - -cdef extern from "polymer.hpp": - vector[vector[Vector3d]] draw_polymer_positions(PartCfg &, int n_polymers, int beads_per_polymer, double bond_length, vector[Vector3d] & start_positions, double min_distance, int max_tries, int use_bond_angle, double bond_angle, int respect_constraints, int seed) except + diff --git a/src/python/espressomd/polymer.pyx b/src/python/espressomd/polymer.py similarity index 65% rename from src/python/espressomd/polymer.pyx rename to src/python/espressomd/polymer.py index 9bbbc8e4c21..f7ea28b90aa 100644 --- a/src/python/espressomd/polymer.pyx +++ b/src/python/espressomd/polymer.py @@ -17,52 +17,23 @@ # along with this program. If not, see . # -from . cimport polymer -from . import utils import numpy as np -from .system import System +from .utils import check_type_or_throw_except from .interactions import BondedInteraction -from .utils cimport make_Vector3d, check_type_or_throw_except -from .utils import array_locked -from .polymer cimport partCfg +from .script_interface import script_interface_register, ScriptInterfaceHelper -def validate_params(_params, default): - if _params["n_polymers"] <= 0: - raise ValueError( - "n_polymers has to be a positive integer") - if _params["beads_per_chain"] <= 0: - raise ValueError( - "beads_per_chain has to be a positive integer") - if _params["bond_length"] < 0: - raise ValueError( - "bond_length has to be a positive float") - if not ((np.size(_params["start_positions"]) == 0) - or np.shape(_params["start_positions"]) == (_params["n_polymers"], 3)): - raise ValueError( - "start_positions has to be a numpy array with shape (n_polymers, 3)") - if _params["min_distance"] < 0: - raise ValueError( - "min_distance has to be a positive float") - if _params["max_tries"] < 0: - raise ValueError( - "max_tries has to be a positive integer") - if _params["use_bond_angle"] and np.isnan(_params["bond_angle"]): - raise ValueError( - "bond_angle has to be a positive float") - if type(_params["respect_constraints"]) != bool: - raise ValueError( - "respect_constraints has to be either True or False") - if type(_params["seed"]) != int: - raise ValueError( - "seed has to be an integer") +@script_interface_register +class _Polymer(ScriptInterfaceHelper): + _so_name = "Particles::Polymer" + _so_creation_policy = "LOCAL" -# wrapper function to expose to the user interface - -def linear_polymer_positions(**kwargs): +def linear_polymer_positions( + start_positions=(), min_distance=0., respect_constraints=False, + max_tries=1000, **kwargs): """ - Generates particle positions for polymer creation. + Generate particle positions for polymer creation. Parameters ---------- @@ -75,21 +46,21 @@ def linear_polymer_positions(**kwargs): seed : :obj:`int`, required Seed for the RNG used to generate the particle positions. bond_angle : :obj:`float`, optional - If set, this parameter defines the angle between adjacent bonds - within a polymer. - start_positions : array_like :obj:`float`. + Angle in radians between adjacent bonds within a polymer. + Must be in the range :math:`[0, \\pi]`. If omitted, random + angles are drawn. + start_positions : (N, 3) array_like :obj:`float`, optional If set, this vector defines the start positions for the polymers, i.e., the position of each polymer's first monomer bead. - Here, a numpy array of shape (n_polymers, 3) is expected. + Here, a numpy array of shape ``(n_polymers, 3)`` is expected. min_distance : :obj:`float`, optional - Minimum distance between all generated positions. Defaults to 0 + Minimum distance between all generated positions. respect_constraints : :obj:`bool`, optional If True, the particle setup tries to obey previously defined constraints. - Default value is False. max_tries : :obj:`int`, optional Maximal number of attempts to generate every monomer position, as well as maximal number of retries per polymer, if choosing - suitable monomer positions fails. Default value is 1000. + suitable monomer positions fails. Depending on the total number of beads and constraints, this value needs to be adapted. @@ -100,67 +71,34 @@ def linear_polymer_positions(**kwargs): coordinates of the respective monomers. """ - params = dict() - default_params = dict() - default_params["n_polymers"] = 0 - default_params["beads_per_chain"] = 0 - default_params["bond_length"] = 0 - default_params["start_positions"] = np.array([]) - default_params["min_distance"] = 0 - default_params["max_tries"] = 1000 - default_params["bond_angle"] = -1 - default_params["respect_constraints"] = False - default_params["seed"] = None - - params = default_params - - # use bond_angle if set via kwargs - params["use_bond_angle"] = "bond_angle" in kwargs - - valid_keys = [ - "n_polymers", - "beads_per_chain", - "bond_length", - "start_positions", - "min_distance", - "max_tries", - "bond_angle", - "respect_constraints", - "seed"] - - required_keys = ["n_polymers", "beads_per_chain", "bond_length", "seed"] - - 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 - if (params["start_positions"].size > 0): - for i in range(len(params["start_positions"])): - start_positions.push_back( - make_Vector3d(params["start_positions"][i])) + kwargs["start_positions"] = start_positions + kwargs["min_distance"] = min_distance + kwargs["respect_constraints"] = respect_constraints + kwargs["max_tries"] = max_tries + kwargs["use_bond_angle"] = "bond_angle" in kwargs + if kwargs.get("n_polymers", 0) <= 0: + raise ValueError( + "Parameter 'n_polymers' has to be a positive integer") + if kwargs.get("beads_per_chain", 0) <= 0: + raise ValueError( + "Parameter 'beads_per_chain' has to be a positive integer") + if kwargs.get("bond_length", -1.) < 0.: + raise ValueError("Parameter 'bond_length' has to be a positive float") + if not ((np.size(kwargs["start_positions"]) == 0) + or np.shape(kwargs["start_positions"]) == (kwargs["n_polymers"], 3)): + raise ValueError( + "Parameter 'start_positions' has to be an array_like of floats with shape (n_polymers, 3)") + if kwargs["min_distance"] < 0.: + raise ValueError("Parameter 'min_distance' has to be a positive float") + if kwargs["max_tries"] < 0: + raise ValueError("Parameter 'max_tries' has to be a positive integer") + if kwargs["use_bond_angle"] and not (0. <= kwargs["bond_angle"] <= np.pi): + raise ValueError( + "Parameter 'bond_angle' has to be a float between 0 and pi") - data = draw_polymer_positions( - partCfg(), - params["n_polymers"], - params["beads_per_chain"], - params["bond_length"], - start_positions, - params["min_distance"], - params["max_tries"], - int(params["use_bond_angle"]), - params["bond_angle"], - int(params["respect_constraints"]), - params["seed"]) - positions = [] - for polymer in data: - p = [] - for monomer in polymer: - m = array_locked([monomer[0], monomer[1], monomer[2]]) - p.append(m) - positions.append(p) - return np.array(positions) + pos = _Polymer().call_method("draw_polymer_positions", **kwargs) + return np.reshape( + pos, (kwargs["n_polymers"], kwargs["beads_per_chain"], 3)) def setup_diamond_polymer(system=None, bond=None, MPC=0, @@ -210,9 +148,6 @@ def setup_diamond_polymer(system=None, bond=None, MPC=0, if not no_bonds and not isinstance(bond, BondedInteraction): raise TypeError( "bond argument must be an instance of espressomd.interactions.BondedInteraction") - if not isinstance(system, System): - raise TypeError( - "System argument must be an instance of espressomd.system.System") check_type_or_throw_except( MPC, 1, int, "MPC must be one int") diff --git a/src/script_interface/particle_data/CMakeLists.txt b/src/script_interface/particle_data/CMakeLists.txt index 486011fa0ee..cc7e8b8d031 100644 --- a/src/script_interface/particle_data/CMakeLists.txt +++ b/src/script_interface/particle_data/CMakeLists.txt @@ -3,4 +3,5 @@ target_sources( PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ParticleHandle.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ParticleList.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/ParticleSlice.cpp) + ${CMAKE_CURRENT_SOURCE_DIR}/ParticleSlice.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Polymer.cpp) diff --git a/src/script_interface/particle_data/Polymer.cpp b/src/script_interface/particle_data/Polymer.cpp new file mode 100644 index 00000000000..3c61ce4c889 --- /dev/null +++ b/src/script_interface/particle_data/Polymer.cpp @@ -0,0 +1,60 @@ +/* + * 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 . + */ + +#include "Polymer.hpp" + +#include "script_interface/Variant.hpp" +#include "script_interface/get_value.hpp" + +#include "core/partCfg_global.hpp" +#include "core/polymer.hpp" + +#include + +#include +#include + +namespace ScriptInterface { +namespace Particles { + +Variant Polymer::do_call_method(std::string const &name, + VariantMap const ¶meters) { + if (name == "draw_polymer_positions") { + auto const positions = draw_polymer_positions( + partCfg(), get_value(parameters, "n_polymers"), + get_value(parameters, "beads_per_chain"), + get_value(parameters, "bond_length"), + get_value>(parameters, "start_positions"), + get_value(parameters, "min_distance"), + get_value(parameters, "max_tries"), + get_value(parameters, "use_bond_angle"), + get_value_or(parameters, "bond_angle", 0.), + get_value(parameters, "respect_constraints"), + get_value(parameters, "seed")); + std::vector pack; + for (auto const &chain : positions) { + pack.emplace_back(make_vector_of_variants(chain)); + } + return pack; + } + return {}; +} + +} // namespace Particles +} // namespace ScriptInterface diff --git a/src/script_interface/particle_data/Polymer.hpp b/src/script_interface/particle_data/Polymer.hpp new file mode 100644 index 00000000000..504d568ac01 --- /dev/null +++ b/src/script_interface/particle_data/Polymer.hpp @@ -0,0 +1,39 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_PARTICLE_DATA_POLYMER_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_PARTICLE_DATA_POLYMER_HPP + +#include "script_interface/ScriptInterface.hpp" + +#include + +namespace ScriptInterface { +namespace Particles { + +class Polymer : public AutoParameters { +public: + Variant do_call_method(std::string const &name, + VariantMap const ¶meters) override; +}; + +} // namespace Particles +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/particle_data/initialize.cpp b/src/script_interface/particle_data/initialize.cpp index db308fcd2db..f3b28ccf959 100644 --- a/src/script_interface/particle_data/initialize.cpp +++ b/src/script_interface/particle_data/initialize.cpp @@ -22,6 +22,7 @@ #include "ParticleHandle.hpp" #include "ParticleList.hpp" #include "ParticleSlice.hpp" +#include "Polymer.hpp" namespace ScriptInterface { namespace Particles { @@ -30,6 +31,7 @@ void initialize(Utils::Factory *om) { om->register_new("Particles::ParticleHandle"); om->register_new("Particles::ParticleList"); om->register_new("Particles::ParticleSlice"); + om->register_new("Particles::Polymer"); } } // namespace Particles diff --git a/testsuite/python/particle.py b/testsuite/python/particle.py index e9fdd5a2857..0f64e685432 100644 --- a/testsuite/python/particle.py +++ b/testsuite/python/particle.py @@ -354,6 +354,12 @@ def test_invalid_particle_creation(self): self.system.part.add(1, id=2) with self.assertRaisesRegex(ValueError, err_msg): self.system.part.add([1, 2]) + for coord in (0, 1, 2): + for value in (float('nan'), float('inf'), -float('inf')): + with self.assertRaisesRegex(ValueError, "Particle position must be finite"): + pos = [0., 0., 0.] + pos[coord] = value + self.system.part.add(pos=pos) def test_invalid_particle_attributes(self): p = self.partcl diff --git a/testsuite/python/polymer_linear.py b/testsuite/python/polymer_linear.py index a9262dad468..11a9955baa8 100644 --- a/testsuite/python/polymer_linear.py +++ b/testsuite/python/polymer_linear.py @@ -209,15 +209,9 @@ def test_exceptions(self): Check runtime error messages. """ - with self.assertRaisesRegex(ValueError, r"The following keys have to be given as keyword arguments: " - r"\[.+\], got \[.+\] \(missing \['seed'\]\)"): + with self.assertRaisesRegex(RuntimeError, "Parameter 'seed' is missing"): 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(RuntimeError, "Failed to create polymer positions"): espressomd.polymer.linear_polymer_positions( n_polymers=1, beads_per_chain=10, From d1ea8851b237d8525f95079356c01b6ea75d7000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 22 Jul 2022 18:07:16 +0200 Subject: [PATCH 17/85] doc: Fix Sphinx regressions in generated methods --- src/python/espressomd/analyze.py | 18 ++--- src/python/espressomd/galilei.py | 10 +-- src/python/espressomd/interactions.pyx | 2 +- src/python/espressomd/particle_data.py | 62 ++++++++------- src/python/espressomd/reaction_methods.py | 2 +- src/python/espressomd/system.py | 94 +++++++++++++++-------- 6 files changed, 109 insertions(+), 79 deletions(-) diff --git a/src/python/espressomd/analyze.py b/src/python/espressomd/analyze.py index 4a0f2f3119b..69168368145 100644 --- a/src/python/espressomd/analyze.py +++ b/src/python/espressomd/analyze.py @@ -143,7 +143,7 @@ class Analysis(ScriptInterfaceHelper): """ Methods ------- - linear_momentum(): + linear_momentum() Calculates the system's linear momentum. Parameters @@ -160,7 +160,7 @@ class Analysis(ScriptInterfaceHelper): (3,) array_like of :obj:`float` The linear momentum of the system. - center_of_mass(): + center_of_mass() Calculate the center of mass of particles of a given type. Note that virtual sites are not included, as they do not have a @@ -177,7 +177,7 @@ class Analysis(ScriptInterfaceHelper): (3,) array_like of :obj:`float` The center of mass of the particles. - nbhood(): + nbhood() Get all particles in a defined neighborhood. Parameters @@ -192,7 +192,7 @@ class Analysis(ScriptInterfaceHelper): (N,) array_like of :obj:`int` The neighbouring particle ids. - calc_re(): + calc_re() Calculate the mean end-to-end distance of chains and its standard deviation, as well as mean square end-to-end distance of chains and its standard deviation. @@ -218,7 +218,7 @@ class Analysis(ScriptInterfaceHelper): standard deviation, [2] the mean square end-to-end distance and [3] its standard deviation. - calc_rg(): + calc_rg() Calculate the mean radius of gyration of chains and its standard deviation, as well as the mean square radius of gyration of chains and its standard deviation. @@ -244,7 +244,7 @@ class Analysis(ScriptInterfaceHelper): standard deviation, [2] the mean square radius of gyration and [3] its standard deviation. - calc_rh(): + calc_rh() Calculate the hydrodynamic mean radius of chains and its standard deviation. @@ -268,7 +268,7 @@ class Analysis(ScriptInterfaceHelper): Where [0] is the mean hydrodynamic radius of the chains and [1] its standard deviation. - angular_momentum(): + angular_momentum() Calculate the system's angular momentum with respect to the origin. Note that virtual sites are not included, as they do not have a @@ -285,7 +285,7 @@ class Analysis(ScriptInterfaceHelper): (3,) array_like of :obj:`float` The center of mass of the system. - structure_factor(): + structure_factor() Calculate the structure factor for given types. Returns the spherically averaged structure factor of particles specified in ``sf_types``. The structure factor is calculated for all possible wave @@ -307,7 +307,7 @@ class Analysis(ScriptInterfaceHelper): Where [0] contains the wave vectors q and [1] contains the structure factors S(q) - distribution(): + distribution() Calculates the distance distribution of particles (probability of finding a particle of type A at a certain distance around a particle of type B, disregarding the fact that a spherical shell of a larger radius diff --git a/src/python/espressomd/galilei.py b/src/python/espressomd/galilei.py index 1a81750b3f6..68de294f800 100644 --- a/src/python/espressomd/galilei.py +++ b/src/python/espressomd/galilei.py @@ -27,7 +27,7 @@ class GalileiTransform(ScriptInterfaceHelper): Methods ------- - kill_particle_motion(): + kill_particle_motion() Stop the motion of all particles. Parameters @@ -35,7 +35,7 @@ class GalileiTransform(ScriptInterfaceHelper): rotation : :obj:`bool`, optional Whether or not to stop the angular momentum too. Defaults to false. - kill_particle_forces(): + kill_particle_forces() Set the forces on the particles to zero. Parameters @@ -43,7 +43,7 @@ class GalileiTransform(ScriptInterfaceHelper): torque : :obj:`bool`, optional Whether or not to set the torques to zero too. Defaults to false. - system_CMS(): + system_CMS() Calculate the center of mass of the system. Assumes equal unit mass if the ``MASS`` feature is not compiled in. @@ -52,7 +52,7 @@ class GalileiTransform(ScriptInterfaceHelper): (3,) array_like of :obj:`float` The center of mass position. - system_CMS_velocity(): + system_CMS_velocity() Calculate the center of mass velocity of the system. Assumes equal unit mass if the ``MASS`` feature is not compiled in. @@ -61,7 +61,7 @@ class GalileiTransform(ScriptInterfaceHelper): (3,) array_like of :obj:`float` The of the center of mass velocity vector. - galilei_transform(): + galilei_transform() Remove the center of mass velocity of the system. Assumes equal unit mass if the ``MASS`` feature is not compiled in. This is often used when switching from Langevin Dynamics to lattice-Boltzmann. This is diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 8b35e7141f5..6cf62e85a05 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -2850,7 +2850,7 @@ class BondedInteractions(ScriptObjectMap): Methods ------- - remove(): + remove() Remove a bond from the list. This is a no-op if the bond does not exist. diff --git a/src/python/espressomd/particle_data.py b/src/python/espressomd/particle_data.py index b61c1b8eeaf..96e6a58df7c 100644 --- a/src/python/espressomd/particle_data.py +++ b/src/python/espressomd/particle_data.py @@ -108,22 +108,6 @@ class ParticleHandle(ScriptInterfaceHelper): The force is recomputed during the integration step and any force set in this way is immediately lost at the next integration step. - bonds: - The bonds stored by this particle. Note that bonds are only stored by - one partner. You need to define a bonded interaction. - - A bond tuple is specified as a bond identifier associated with - a particle ``(bond_ID, (*part_ID,))``. A single particle may contain - multiple bonds. - - See Also - -------- - espressomd.particle_data.ParticleHandle.add_bond : Method to add bonds to a ``Particle`` - espressomd.particle_data.ParticleHandle.delete_bond : Method to remove bonds from a ``Particle`` - - .. note:: - Bond ids have to be an integer >= 0. - node: (3,) array_like of :obj:`int` The node the particle is on, identified by its MPI rank. @@ -321,12 +305,6 @@ class ParticleHandle(ScriptInterfaceHelper): .. note:: This needs the feature ``ROTATION``. - exclusions: (N,) array_like of :obj:`int` - The exclusion list of particles where non-bonded interactions are ignored. - - .. note:: - This needs the feature ``EXCLUSIONS``. - swimming: Set swimming parameters. @@ -385,7 +363,7 @@ class ParticleHandle(ScriptInterfaceHelper): Methods ------- - delete_all_bonds(): + delete_all_bonds() Delete all bonds from the particle. See Also @@ -504,6 +482,25 @@ def delete_exclusion(self, partner): @property def bonds(self): + """ + The bonds stored by this particle. Note that bonds are only stored by + one partner. You need to define a bonded interaction. + + A bond tuple is specified as a bond identifier associated with + a particle ``(bond_ID, (*part_ID,))``. A single particle may contain + multiple bonds. + + Type: Ragged array. + + .. note:: + Bond ids have to be an integer >= 0. + + See Also + -------- + espressomd.particle_data.ParticleHandle.add_bond : Method to add bonds to a ``Particle`` + espressomd.particle_data.ParticleHandle.delete_bond : Method to remove bonds from a ``Particle`` + + """ bonds = [] for bond_view in self.call_method("get_bonds_view"): bond_id = bond_view[0] @@ -531,6 +528,15 @@ def bonds(self, bonds): @property def exclusions(self): + """ + The exclusion list of particles where non-bonded interactions are ignored. + + .. note:: + This needs the feature ``EXCLUSIONS``. + + Type: (N,) array_like of :obj:`int` + + """ assert_features("EXCLUSIONS") return array_locked( np.array(self.call_method("get_exclusions"), dtype=int)) @@ -955,14 +961,9 @@ class ParticleList(ScriptInterfaceHelper): """ Provides access to the particles. - Attributes - ---------- - highest_particle_id: :obj:`int` - Largest particle id. - Methods ------- - clear(): + clear() Remove all particles. See Also @@ -1000,6 +1001,9 @@ def __len__(self): @property def highest_particle_id(self): + """ + Largest particle id. + """ return self.call_method("get_highest_particle_id") def add(self, *args, **kwargs): diff --git a/src/python/espressomd/reaction_methods.py b/src/python/espressomd/reaction_methods.py index 18460038189..1cf361ebfc7 100644 --- a/src/python/espressomd/reaction_methods.py +++ b/src/python/espressomd/reaction_methods.py @@ -168,7 +168,7 @@ class ReactionAlgorithm(ScriptInterfaceHelper): Get the volume to be used in the acceptance probability of the reaction ensemble. - get_acceptance_rate_configurational_moves(): + get_acceptance_rate_configurational_moves() Returns the acceptance rate for the configuration moves. get_acceptance_rate_reaction() diff --git a/src/python/espressomd/system.py b/src/python/espressomd/system.py index c2156d16d88..f2124133fa9 100644 --- a/src/python/espressomd/system.py +++ b/src/python/espressomd/system.py @@ -82,37 +82,6 @@ class System(ScriptInterfaceHelper): Attributes ---------- - box_l: (3,) array_like of :obj:`float` - Dimensions of the simulation box - - periodicity: (3,) array_like of :obj:`bool` - System periodicity in ``[x, y, z]``, ``False`` for no periodicity - in this direction, ``True`` for periodicity - - force_cap: :obj:`float` - If > 0, the magnitude of the force on the particles - are capped to this value. - - virtual_sites: - Set the virtual site implementation. - - Requires feature ``VIRTUAL_SITES``. - - max_cut_bonded: :obj:`float` - Maximal cutoff for bonded interactions. - - max_cut_nonbonded: :obj:`float` - Maximal cutoff for non-bonded interactions. - - min_global_cut: :obj:`float` - Minimal interaction cutoff. - - time: :obj:`float` - Total simulation time. - - time_step: :obj:`float` - MD time step. - actors: :class:`espressomd.actors.Actors` analysis: :class:`espressomd.analyze.Analysis` auto_update_accumulators: :class:`espressomd.accumulators.AutoUpdateAccumulators` @@ -135,7 +104,6 @@ class System(ScriptInterfaceHelper): Methods ------- setup_type_map() - Requires setting the volume using :meth:`set_volume`. For using ESPResSo conveniently for simulations in the grand canonical ensemble, or other purposes, when particles of certain types are created and deleted frequently. Particle ids can be stored in lists for each @@ -149,7 +117,7 @@ class System(ScriptInterfaceHelper): type_list : array_like of :obj:`int` Types to track. - number_of_particles(): + number_of_particles() Count the number of particles of a given type. Parameters @@ -168,7 +136,7 @@ class System(ScriptInterfaceHelper): If the particle ``type`` is not currently tracked by the system. To select which particle types are tracked, call :meth:`setup_type_map`. - rotate_system(): + rotate_system() Rotate the particles in the system about the center of mass. If ``ROTATION`` is activated, the internal rotation degrees of @@ -297,6 +265,12 @@ def __setstate__(self, params): @property def box_l(self): + """ + Dimensions of the simulation box. + + Type: (3,) array_like of :obj:`float` + + """ return self._globals.box_l @box_l.setter @@ -305,6 +279,13 @@ def box_l(self, value): @property def periodicity(self): + """ + System periodicity in ``[x, y, z]``, ``False`` for no periodicity + in this direction, ``True`` for periodicity + + Type: (3,) array_like of :obj:`bool` + + """ return self._globals.periodicity @periodicity.setter @@ -313,6 +294,12 @@ def periodicity(self, value): @property def min_global_cut(self): + """ + Minimal interaction cutoff. + + Type: :obj:`float` + + """ return self._globals.min_global_cut @min_global_cut.setter @@ -321,6 +308,13 @@ def min_global_cut(self, value): @property def force_cap(self): + """ + If > 0, the magnitude of the force on the particles + are capped to this value. + + Type: :obj:`float` + + """ return self.integrator.force_cap @force_cap.setter @@ -329,6 +323,12 @@ def force_cap(self, value): @property def time(self): + """ + Total simulation time. + + Type: :obj:`float` + + """ return self.integrator.time @time.setter @@ -337,6 +337,12 @@ def time(self, value): @property def time_step(self): + """ + MD time step. + + Type: :obj:`float` + + """ return self.integrator.time_step @time_step.setter @@ -345,14 +351,34 @@ def time_step(self, value): @property def max_cut_nonbonded(self): + """ + Maximal cutoff for non-bonded interactions. + + Type: :obj:`float` + + """ return self.cell_system.max_cut_nonbonded @property def max_cut_bonded(self): + """ + Maximal cutoff for bonded interactions. + + Type: :obj:`float` + + """ return self.cell_system.max_cut_bonded @property def virtual_sites(self): + """ + Set the virtual site implementation. + + Requires feature ``VIRTUAL_SITES``. + + Type: :obj:`espressomd.virtual_sites.ActiveVirtualSitesHandle` + + """ assert_features("VIRTUAL_SITES") return self._active_virtual_sites_handle.implementation From c7a81fb11cb90047fb7594fef74ed1f19ffdec6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 25 Jul 2022 15:27:24 +0200 Subject: [PATCH 18/85] CMake: Fix broken test target for label parallel_odd Regression introduced in 7d8752b12771888065248b34a4f904c59203efd0. --- testsuite/python/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index 6180038001c..c711e34ef90 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -62,10 +62,10 @@ function(PYTHON_TEST) set_tests_properties(${TEST_NAME} PROPERTIES RESOURCE_LOCK GPU) endif() - if(${TEST_MAX_NUM_PROC} EQUAL 2) + if(${TEST_NUM_PROC} EQUAL 2) list(APPEND TEST_LABELS "parallel") endif() - if(${TEST_MAX_NUM_PROC} EQUAL 3) + if(${TEST_NUM_PROC} EQUAL 3) list(APPEND TEST_LABELS "parallel_odd") endif() set_tests_properties(${TEST_NAME} PROPERTIES LABELS "${TEST_LABELS}") From 2dd41559c0fbb277f2976f1b5f6c75c10ff52d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 26 Jul 2022 13:54:25 +0200 Subject: [PATCH 19/85] tests: Unit test P3M/DP3M against simple lattices --- doc/bibliography.bib | 35 ++++ doc/sphinx/appendix.rst | 42 ++++- testsuite/python/CMakeLists.txt | 1 + testsuite/python/p3m_madelung.py | 294 +++++++++++++++++++++++++++++++ 4 files changed, 371 insertions(+), 1 deletion(-) create mode 100644 testsuite/python/p3m_madelung.py diff --git a/doc/bibliography.bib b/doc/bibliography.bib index b86abae22fd..2039fcd7b05 100644 --- a/doc/bibliography.bib +++ b/doc/bibliography.bib @@ -132,6 +132,18 @@ @Article{banchio03a doi = {10.1063/1.1571819}, } +@Article{batle20a, + author = {Batle, Josep and Ciftja, Orion}, + title = {Minimum and maximum energy for crystals of magnetic dipoles}, + journal = {Scientific Reports}, + year = {2020}, + volume = {10}, + number = {1}, + pages = {19113}, + issn = {2045-2322}, + doi = {10.1038/s41598-020-76029-x}, +} + @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}, @@ -201,6 +213,17 @@ @Article{cerda08d doi = {10.1063/1.3000389}, } +@Article{ciftja19a, + author = {Ciftja, Orion}, + title = {Equivalence of an infinite one-dimensional ionic crystal to a simple electrostatic model}, + journal = {Results in Physics}, + year = {2019}, + volume = {13}, + pages = {102325}, + issn = {2211-3797}, + doi = {10.1016/j.rinp.2019.102325}, +} + @ARTICLE{cimrak12a, author = {Cimr\'{a}k, I. and Gusenbauer, M. and Schrefl, T.}, title = {Modelling and simulation of processes in microfluidic devices for biomedical applications}, @@ -396,6 +419,18 @@ @article{essmann95a publisher={AIP} } +@Article{evjen32a, + author = {Evjen, H. M.}, + title = {On the stability of certain heteropolar crystals}, + journal = {Physical Review}, + year = {1932}, + volume = {39}, + number = {4}, + pages = {675--687}, + doi = {10.1103/PhysRev.39.675}, + publisher = {American Physical Society}, +} + @ARTICLE{ewald21a, author = {Ewald, P. P.}, title = {Die {B}erechnung optischer und elektrostatischer {G}itterpotentiale}, diff --git a/doc/sphinx/appendix.rst b/doc/sphinx/appendix.rst index e3f17d05606..9def06e14f0 100644 --- a/doc/sphinx/appendix.rst +++ b/doc/sphinx/appendix.rst @@ -453,7 +453,7 @@ where The implementation is very similar to MMM2D, except that the separation between slices close by, and above and below is not necessary. -.. _Errors: +.. _MMM Errors: Errors ~~~~~~ @@ -490,3 +490,43 @@ MMM1D the error distribution is less homogeneous, however, also here it is always better to have some extra precision, especially since it is computationally cheap. +.. _MMM Testing: + +Testing +~~~~~~~ + +The accuracy of MMM algorithms can be tested against the Madelung constants +of simple crystal lattices. Although the constants don't have a closed form +in 2D and 3D, their infinite sum converges rapidly to well-known values. + +.. _Madelung electrostatics: + +Ionic crystals +^^^^^^^^^^^^^^ + +For an infinite wire, the energy per ion is :math:`MC\frac{q}{a}` with +:math:`M = -2\ln{2}` the 1D Madelung constant, :math:`C` the electrostatics +prefactor, :math:`q` the electric charge and :math:`a` the lattice constant. +Likewise, the pressure per ion can be derived as :math:`MC\frac{q}{aV}` +with :math:`V` the simulation box volume. For details, see :cite:`ciftja19a`. + +For an infinite 2D or 3D NaCl crystal lattice, the Madelung constant can be +obtained in a numerical simulation with the Evjen method :cite:`evjen32a` or +the Ewald method :cite:`ewald21a`. + +.. _Madelung magnetostatics: + +Magnetic dipoles crystals +^^^^^^^^^^^^^^^^^^^^^^^^^ + +For an infinite wire, the energy per dipole is :math:`MC\frac{|\mu|^2}{a^3}` +with :math:`M` the orientation-dependent 1D Madelung constant, +:math:`M^{\mathrm{min}} = - 2 \zeta(3)` for parallel dipoles and +:math:`M^{\mathrm{max}} = +3/2 \zeta(3)` for anti-parallel dipoles, +:math:`\zeta(s)` the Riemann zeta function, +:math:`C` the magnetostatics prefactor, :math:`\mu` the dipole moment and +:math:`a` the lattice constant :cite:`batle20a`. + +For an infinite 2D or 3D NaCl crystal lattice, the Madelung constant for +the maximal energy and minimal energy dipole orientation can be estimated +in a numerical simulation :cite:`batle20a`. diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index c711e34ef90..18c3e32b7d8 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -279,6 +279,7 @@ python_test(FILE lb_momentum_conservation.py MAX_NUM_PROC 4 LABELS gpu) python_test(FILE lb_momentum_conservation.py MAX_NUM_PROC 1 LABELS gpu SUFFIX n_square) python_test(FILE p3m_electrostatic_pressure.py MAX_NUM_PROC 2 LABELS gpu) +python_test(FILE p3m_madelung.py MAX_NUM_PROC 1 LABELS gpu long) 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) diff --git a/testsuite/python/p3m_madelung.py b/testsuite/python/p3m_madelung.py new file mode 100644 index 00000000000..ed6f6ae0cb2 --- /dev/null +++ b/testsuite/python/p3m_madelung.py @@ -0,0 +1,294 @@ +# +# 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 numpy as np +import scipy.special +import itertools +import unittest as ut +import unittest_decorators as utx +import espressomd.electrostatics +import espressomd.magnetostatics + + +class Test(ut.TestCase): + """ + Check all P3M algorithms against the Madelung constant of 1D, 2D and 3D + NaCl lattices. See user guide sections :ref:`Madelung electrostatics` and + :ref:`Madelung magnetostatics` for more details. + """ + + system = espressomd.System(box_l=[1., 1., 1.]) + system.time_step = 0.01 + system.cell_system.skin = 0.4 + + def tearDown(self): + self.system.part.clear() + self.system.actors.clear() + + def get_normalized_obs_per_ion(self, pressure=True): + energy = self.system.analysis.energy()["coulomb"] + if pressure: + p_scalar = self.system.analysis.pressure()["coulomb"] + p_tensor = self.system.analysis.pressure_tensor()["coulomb"] + else: + p_scalar = 0. + p_tensor = np.zeros((3, 3)) + N = len(self.system.part) + V = self.system.volume() + return 2. * energy / N, 2. * p_scalar * V / N, 2. * p_tensor * V / N + + def get_reference_obs_per_ion(self, madelung, base_vector): + base_tensor = base_vector * np.eye(3) + ref_energy = madelung + ref_pressure = madelung * base_tensor / np.trace(base_tensor) + return ref_energy, ref_pressure + + def get_normalized_obs_per_dipole(self, dipm, spacing): + energy = self.system.analysis.energy()["dipolar"] + return energy / len(self.system.part) / dipm**2 * spacing**3 + + @utx.skipIfMissingFeatures(["DP3M"]) + def test_infinite_magnetic_wire(self): + n_part = 64 + spacing = 1.2 + dipm = 1.05 + self.system.box_l = 3 * [n_part * spacing] + + dipoles_parallel = [] + dipoles_antiparallel = [] + for j in range(n_part): + dipoles_antiparallel.append([(-1)**j, 0., 0.]) + dipoles_parallel.append([1., 0., 0.]) + pos = np.array([j, 0, 0]) + self.system.part.add(pos=spacing * pos, dip=[1., 0., 0.]) + + dp3m_params = dict(prefactor=1., accuracy=1e-9, mesh=48, r_cut=28., + cao=6, tuning=False) + mdlc_params = {'maxPWerror': 1e-9, 'gap_size': 16.} + + def check(): + # minimal energy configuration + self.system.part.all().dip = dipm * np.array(dipoles_parallel) + mc = -2. * scipy.special.zeta(3) + energy_per_dip = self.get_normalized_obs_per_dipole(dipm, spacing) + np.testing.assert_allclose(energy_per_dip, mc, atol=0., rtol=2e-9) + + # maximal energy configuration + self.system.part.all().dip = dipm * np.array(dipoles_antiparallel) + mc = 3. / 2. * scipy.special.zeta(3) + energy_per_dip = self.get_normalized_obs_per_dipole(dipm, spacing) + np.testing.assert_allclose(energy_per_dip, mc, atol=0., rtol=2e-9) + + dp3m = espressomd.magnetostatics.DipolarP3M(**dp3m_params) + self.system.actors.add(dp3m) + check() + self.system.actors.remove(dp3m) + mdlc = espressomd.magnetostatics.DLC(actor=dp3m, **mdlc_params) + self.system.actors.add(mdlc) + check() + + @utx.skipIfMissingFeatures(["DP3M"]) + def test_infinite_magnetic_sheet(self): + n_part = 64 + spacing = 1.2 + dipm = 1.05 + self.system.box_l = 3 * [n_part * spacing] + + dipoles_parallel = [] + dipoles_antiparallel = [] + for j, k in itertools.product(range(n_part), repeat=2): + if j % 2 == 0: + theta = (-1)**(k + 0) * np.pi / 4. + np.pi + else: + theta = (-1)**(k + 1) * np.pi / 4. + dipoles_antiparallel.append([np.cos(theta), np.sin(theta), 0.]) + dipoles_parallel.append([0., (-1)**j, 0.]) + pos = np.array([j, k, 0]) + self.system.part.add(pos=spacing * pos, dip=[0., 0., 1.]) + + dp3m_params = dict(prefactor=1., accuracy=1e-9, mesh=48, r_cut=33.6, + cao=7, tuning=False) + mdlc_params = {'maxPWerror': 1e-9, 'gap_size': 16.} + + def check(): + # minimal energy configuration + self.system.part.all().dip = dipm * np.array(dipoles_parallel) + mc = -2.54944 + energy_per_dip = self.get_normalized_obs_per_dipole(dipm, spacing) + np.testing.assert_allclose(energy_per_dip, mc, atol=0., rtol=4e-6) + + # maximal energy configuration + self.system.part.all().dip = dipm * np.array(dipoles_antiparallel) + mc = +3.01716 + energy_per_dip = self.get_normalized_obs_per_dipole(dipm, spacing) + np.testing.assert_allclose(energy_per_dip, mc, atol=0., rtol=4e-6) + + dp3m = espressomd.magnetostatics.DipolarP3M(**dp3m_params) + self.system.actors.add(dp3m) + check() + self.system.actors.remove(dp3m) + mdlc = espressomd.magnetostatics.DLC(actor=dp3m, **mdlc_params) + self.system.actors.add(mdlc) + check() + + @utx.skipIfMissingFeatures(["DP3M"]) + def test_infinite_magnetic_cube(self): + n_part = 16 + spacing = 1.2 + dipm = 1.05 + self.system.box_l = 3 * [n_part * spacing] + + dipoles_parallel = [] + dipoles_antiparallel = [] + for i, j, k in itertools.product(range(n_part), repeat=3): + if i % 2 == 0: + theta = np.pi + (-1)**j * np.pi / 4. + else: + theta = (-1)**(j + 1) * np.pi / 4. + dipoles_antiparallel.append([np.cos(theta), np.sin(theta), 0.]) + dipoles_parallel.append([0., 0., (-1)**(i + j)]) + pos = np.array([i, j, k]) + self.system.part.add(pos=spacing * pos, dip=[0., 0., 1.]) + + dp3m_params = dict( + prefactor=1., accuracy=5e-8, mesh=64, r_cut=8.2, cao=7, tuning=False) + + def check(): + # minimal energy configuration + self.system.part.all().dip = dipm * np.array(dipoles_parallel) + mc = -2.67679 + energy_per_dip = self.get_normalized_obs_per_dipole(dipm, spacing) + np.testing.assert_allclose(energy_per_dip, mc, atol=0., rtol=1e-6) + + # maximal energy configuration + self.system.part.all().dip = dipm * np.array(dipoles_antiparallel) + mc = +4.84473 # imprecise + energy_per_dip = self.get_normalized_obs_per_dipole(dipm, spacing) + np.testing.assert_allclose(energy_per_dip, mc, atol=0., rtol=3e-4) + + dp3m = espressomd.magnetostatics.DipolarP3M(**dp3m_params) + self.system.actors.add(dp3m) + check() + + @utx.skipIfMissingFeatures(["P3M"]) + def test_infinite_ionic_wire(self): + n_pairs = 64 + self.system.box_l = [n_pairs / 2, n_pairs / 2, 2 * n_pairs] + for i in range(n_pairs): + self.system.part.add(pos=[0., 0., 2. * i + 0.], q=+1.) + self.system.part.add(pos=[0., 0., 2. * i + 1.], q=-1.) + + mc = -2. * np.log(2.) + base = [0., 0., 1.] + ref_energy, ref_pressure = self.get_reference_obs_per_ion(mc, base) + p3m_params = dict(prefactor=1., accuracy=1e-8, mesh=32, r_cut=14., + cao=7, tuning=False) + + def check(): + energy, p_scalar, p_tensor = self.get_normalized_obs_per_ion() + np.testing.assert_allclose(energy, ref_energy, atol=0., rtol=5e-7) + np.testing.assert_allclose(p_scalar, np.trace(ref_pressure) / 3., + atol=1e-12, rtol=1e-6) + np.testing.assert_allclose(p_tensor, ref_pressure, atol=1e-12, + rtol=1e-6) + + p3m = espressomd.electrostatics.P3M(**p3m_params) + self.system.actors.add(p3m) + check() + if espressomd.has_features("CUDA"): + self.system.actors.clear() + p3m = espressomd.electrostatics.P3MGPU(**p3m_params) + self.system.actors.add(p3m) + check() + + @utx.skipIfMissingFeatures(["P3M"]) + def test_infinite_ionic_sheet(self): + n_pairs = 32 + self.system.box_l = [2 * n_pairs, 2 * n_pairs, 4 * n_pairs] + for j, k in itertools.product(range(2 * n_pairs), repeat=2): + self.system.part.add(pos=[j, k, 0.], q=(-1)**(j + k)) + + mc = -1.6155426267128247 + base = [1., 1., 0.] + ref_energy, ref_pressure = self.get_reference_obs_per_ion(mc, base) + p3m_params = dict(prefactor=1., accuracy=1e-8, mesh=48, r_cut=26., + cao=7, tuning=False) + elc_params = dict(maxPWerror=1e-6, gap_size=16) + + def check(pressure=True): + energy, p_scalar, p_tensor = self.get_normalized_obs_per_ion( + pressure) + np.testing.assert_allclose(energy, ref_energy, atol=0., rtol=1e-7) + if not pressure: + return + np.testing.assert_allclose(p_scalar, np.trace(ref_pressure) / 3., + atol=1e-12, rtol=5e-7) + np.testing.assert_allclose(p_tensor, ref_pressure, atol=1e-12, + rtol=5e-7) + + p3m = espressomd.electrostatics.P3M(**p3m_params) + self.system.actors.add(p3m) + check(pressure=True) + self.system.actors.remove(p3m) + elc = espressomd.electrostatics.ELC(actor=p3m, **elc_params) + self.system.actors.add(elc) + check(pressure=False) + if espressomd.has_features("CUDA"): + self.system.actors.clear() + p3m = espressomd.electrostatics.P3MGPU(**p3m_params) + self.system.actors.add(p3m) + check(pressure=True) + self.system.actors.remove(p3m) + elc = espressomd.electrostatics.ELC(actor=p3m, **elc_params) + self.system.actors.add(elc) + check(pressure=False) + + @utx.skipIfMissingFeatures(["P3M"]) + def test_infinite_ionic_cube(self): + n_pairs = 8 + self.system.box_l = 3 * [2 * n_pairs] + for j, k, l in itertools.product(range(2 * n_pairs), repeat=3): + self.system.part.add(pos=[j, k, l], q=(-1)**(j + k + l)) + + mc = -1.74756459463318219 + base = [1., 1., 1.] + ref_energy, ref_pressure = self.get_reference_obs_per_ion(mc, base) + p3m_params = dict(prefactor=1., accuracy=3e-7, mesh=44, r_cut=7., cao=7, + tuning=False) + + def check(): + energy, p_scalar, p_tensor = self.get_normalized_obs_per_ion() + np.testing.assert_allclose(energy, ref_energy, atol=0., rtol=1e-6) + np.testing.assert_allclose(p_scalar, np.trace(ref_pressure) / 3., + atol=1e-12, rtol=5e-6) + np.testing.assert_allclose(p_tensor, ref_pressure, atol=1e-12, + rtol=5e-6) + + p3m = espressomd.electrostatics.P3M(**p3m_params) + self.system.actors.add(p3m) + check() + if espressomd.has_features("CUDA"): + self.system.actors.clear() + p3m = espressomd.electrostatics.P3MGPU(**p3m_params) + self.system.actors.add(p3m) + check() + + +if __name__ == "__main__": + ut.main() From cf322d53e137d05d7b5d181992451e41609922ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 27 Jul 2022 16:06:09 +0200 Subject: [PATCH 20/85] tests: Fine-tune long-range test cases Adjust r_cut in tests running on 3 cores. Add missing feature guards. Reduce computational cost of Madelung tests and run them in parallel. --- testsuite/python/CMakeLists.txt | 2 +- testsuite/python/coulomb_mixed_periodicity.py | 4 ++-- testsuite/python/p3m_madelung.py | 18 +++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index 18c3e32b7d8..6cd2992030d 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -279,7 +279,7 @@ python_test(FILE lb_momentum_conservation.py MAX_NUM_PROC 4 LABELS gpu) python_test(FILE lb_momentum_conservation.py MAX_NUM_PROC 1 LABELS gpu SUFFIX n_square) python_test(FILE p3m_electrostatic_pressure.py MAX_NUM_PROC 2 LABELS gpu) -python_test(FILE p3m_madelung.py MAX_NUM_PROC 1 LABELS gpu long) +python_test(FILE p3m_madelung.py MAX_NUM_PROC 2 LABELS gpu long) 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) diff --git a/testsuite/python/coulomb_mixed_periodicity.py b/testsuite/python/coulomb_mixed_periodicity.py index cf96974c0a2..fddc602ba5a 100644 --- a/testsuite/python/coulomb_mixed_periodicity.py +++ b/testsuite/python/coulomb_mixed_periodicity.py @@ -92,7 +92,7 @@ def test_elc_cpu(self): self.setup_elc_system() p3m = espressomd.electrostatics.P3M( - prefactor=1., accuracy=1e-6, mesh=[42, 42, 50], r_cut=3.5) + prefactor=1., accuracy=1e-6, mesh=[42, 42, 50], r_cut=3.) elc = espressomd.electrostatics.ELC( actor=p3m, maxPWerror=1E-6, gap_size=3) @@ -106,7 +106,7 @@ def test_elc_gpu(self): self.setup_elc_system() p3m = espressomd.electrostatics.P3M( - prefactor=1., accuracy=1e-6, mesh=[42, 42, 50], r_cut=3.5) + prefactor=1., accuracy=1e-6, mesh=[42, 42, 50], r_cut=3.) elc = espressomd.electrostatics.ELC( actor=p3m, maxPWerror=1E-6, gap_size=3.) diff --git a/testsuite/python/p3m_madelung.py b/testsuite/python/p3m_madelung.py index ed6f6ae0cb2..0c795ba95d3 100644 --- a/testsuite/python/p3m_madelung.py +++ b/testsuite/python/p3m_madelung.py @@ -105,8 +105,8 @@ def check(): @utx.skipIfMissingFeatures(["DP3M"]) def test_infinite_magnetic_sheet(self): - n_part = 64 - spacing = 1.2 + n_part = 32 + spacing = 2.4 dipm = 1.05 self.system.box_l = 3 * [n_part * spacing] @@ -149,8 +149,8 @@ def check(): @utx.skipIfMissingFeatures(["DP3M"]) def test_infinite_magnetic_cube(self): - n_part = 16 - spacing = 1.2 + n_part = 8 + spacing = 2.4 dipm = 1.05 self.system.box_l = 3 * [n_part * spacing] @@ -211,7 +211,7 @@ def check(): p3m = espressomd.electrostatics.P3M(**p3m_params) self.system.actors.add(p3m) check() - if espressomd.has_features("CUDA"): + if espressomd.has_features("CUDA") and espressomd.gpu_available(): self.system.actors.clear() p3m = espressomd.electrostatics.P3MGPU(**p3m_params) self.system.actors.add(p3m) @@ -219,7 +219,7 @@ def check(): @utx.skipIfMissingFeatures(["P3M"]) def test_infinite_ionic_sheet(self): - n_pairs = 32 + n_pairs = 24 self.system.box_l = [2 * n_pairs, 2 * n_pairs, 4 * n_pairs] for j, k in itertools.product(range(2 * n_pairs), repeat=2): self.system.part.add(pos=[j, k, 0.], q=(-1)**(j + k)) @@ -227,7 +227,7 @@ def test_infinite_ionic_sheet(self): mc = -1.6155426267128247 base = [1., 1., 0.] ref_energy, ref_pressure = self.get_reference_obs_per_ion(mc, base) - p3m_params = dict(prefactor=1., accuracy=1e-8, mesh=48, r_cut=26., + p3m_params = dict(prefactor=1., accuracy=1e-8, mesh=48, r_cut=22., cao=7, tuning=False) elc_params = dict(maxPWerror=1e-6, gap_size=16) @@ -249,7 +249,7 @@ def check(pressure=True): elc = espressomd.electrostatics.ELC(actor=p3m, **elc_params) self.system.actors.add(elc) check(pressure=False) - if espressomd.has_features("CUDA"): + if espressomd.has_features("CUDA") and espressomd.gpu_available(): self.system.actors.clear() p3m = espressomd.electrostatics.P3MGPU(**p3m_params) self.system.actors.add(p3m) @@ -283,7 +283,7 @@ def check(): p3m = espressomd.electrostatics.P3M(**p3m_params) self.system.actors.add(p3m) check() - if espressomd.has_features("CUDA"): + if espressomd.has_features("CUDA") and espressomd.gpu_available(): self.system.actors.clear() p3m = espressomd.electrostatics.P3MGPU(**p3m_params) self.system.actors.add(p3m) From 2111342b02e68d9b22ffcaa69234f676f62cd268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 28 Jul 2022 23:11:47 +0200 Subject: [PATCH 21/85] config: Rewrite config generation script Make the script fully reproducible by sorting feature sets and removing the absolute file path and timestamp. This makes object file caching much more efficient. Cleanup outdated comments. --- src/config/gen_featureconfig.py | 139 ++++++++++++++++++-------------- 1 file changed, 80 insertions(+), 59 deletions(-) diff --git a/src/config/gen_featureconfig.py b/src/config/gen_featureconfig.py index 59cf81068d3..b8d713e9479 100644 --- a/src/config/gen_featureconfig.py +++ b/src/config/gen_featureconfig.py @@ -1,3 +1,4 @@ +# # Copyright (C) 2013-2022 The ESPResSo project # Copyright (C) 2012 Olaf Lenz # @@ -16,9 +17,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# This script generates the files featureconfig.h and featureconfig.c. -# -import time + import string import inspect import sys @@ -32,67 +31,78 @@ print(f"Usage: {sys.argv[0]} DEFFILE HPPFILE CPPFILE", file=sys.stderr) exit(2) -deffilename, hfilename, cfilename = sys.argv[1:5] - -print("Reading definitions from " + deffilename + "...") -defs = featuredefs.defs(deffilename) -print("Done.") - -print("Writing " + hfilename + "...") -hfile = open(hfilename, 'w') - -hfile.write("""/* -WARNING: This file was autogenerated by +path_def, path_hpp, path_cpp = sys.argv[1:5] - %s on %s +print(f"Reading definitions from {path_def}") +defs = featuredefs.defs(path_def) - Do not modify it or your changes will be overwritten! - Modify features.def instead. +disclaimer = f"""/* + WARNING: This file was generated automatically. + Do not modify it or your changes will be overwritten! + Modify features.def instead. */ -#ifndef ESPRESSO_FEATURECONFIG_HPP -#define ESPRESSO_FEATURECONFIG_HPP - -#include "cmake_config.hpp" -#include "myconfig-final.hpp" +""" -""" % (sys.argv[0], time.asctime())) +print(f"Writing {path_hpp}") +hfile = open(path_hpp, 'w') +hfile.write(disclaimer) +hfile.write(""" +#ifndef ESPRESSO_SRC_CONFIG_CONFIG_FEATURES_HPP +#define ESPRESSO_SRC_CONFIG_CONFIG_FEATURES_HPP +""") # external features can only be set by the build # system, so in case the user has defined some of # them, we undef all external features and include # the config from the build system again, to make # sure only the detected ones are set. -hfile.write('/* Guards for externals */') +hfile.write(""" +/*********************************/ +/* Handle definitions from CMake */ +/*********************************/ + +#include "cmake_config.hpp" +#include "myconfig-final.hpp" +""") external_template = string.Template(""" // $feature is external #if defined($feature) #undef $feature #endif """) -for feature in defs.externals: +for feature in sorted(defs.externals): hfile.write(external_template.substitute(feature=feature)) # Include definitions from CMake hfile.write(""" -/* Definitions from CMake */ #include "cmake_config.hpp" """) # handle implications -hfile.write('/* Handle implications */') +hfile.write("""\ +/***********************/ +/* Handle implications */ +/***********************/ +""") implication_template = string.Template(""" // $feature implies $implied #if defined($feature) && !defined($implied) #define $implied #endif """) -for feature, implied in defs.implications: +for feature, implied in sorted(defs.implications): hfile.write(implication_template.substitute( feature=feature, implied=implied)) +hfile.write("\n") + # output warnings if internal features are set manually -hfile.write('/* Warn when derived switches are specified manually */') +hfile.write("""\ +/*****************************************************/ +/* Warn when derived switches are specified manually */ +/*****************************************************/ +""") derivation_template = string.Template(""" // $feature equals $expr #ifdef $feature @@ -101,58 +111,52 @@ #define $feature #endif """) -for feature, expr, cppexpr in defs.derivations: +for feature, expr, cppexpr in sorted(defs.derivations): hfile.write(derivation_template.substitute( feature=feature, cppexpr=cppexpr, expr=expr)) -# write footer -# define external FEATURES and NUM_FEATURES hfile.write(""" -extern const char* FEATURES[]; -extern const int NUM_FEATURES; -#endif /* of _FEATURECONFIG_HPP */""") -hfile.close() -print("Done.") +extern char const *const FEATURES[]; +extern char const *const FEATURES_ALL[]; +extern unsigned int const NUM_FEATURES; +extern unsigned int const NUM_FEATURES_ALL; -print("Writing " + cfilename + "...") -cfile = open(cfilename, 'w') - -# handle requirements - -cfile.write(f"""/* -WARNING: This file was autogenerated by +#endif +""") - {sys.argv[0]} - on - {time.asctime()} +hfile.close() - Do not modify it or your changes will be overwritten! - Modify features.def instead. -*/ +print(f"Writing {path_cpp}") +cfile = open(path_cpp, "w") -/* config.hpp includes config-features.hpp and myconfig.hpp */ +cfile.write(disclaimer) +cfile.write(f""" +#include "config-features.hpp" #include "config.hpp" +/***********************/ +/* Handle requirements */ +/***********************/ """) -cfile.write('/* Handle requirements */') - requirement_string = """ // {feature} requires {expr} #if defined({feature}) && !({cppexpr}) #error Feature {feature} requires {expr} #endif """ -for feature, expr, cppexpr in defs.requirements: +for feature, expr, cppexpr in sorted(defs.requirements): cfile.write( requirement_string.format( feature=feature, cppexpr=cppexpr, expr=expr)) cfile.write(""" - +/****************/ /* Feature list */ -const char* FEATURES[] = { +/****************/ + +char const *const FEATURES[] = { """) feature_string = """ @@ -161,14 +165,31 @@ #endif """ -for feature in defs.externals.union(defs.features, defs.derived): +for feature in sorted(defs.externals.union(defs.features, defs.derived)): cfile.write(feature_string.format(feature=feature)) cfile.write(""" }; +unsigned int const NUM_FEATURES = sizeof(FEATURES) / sizeof(char*); +""") -const int NUM_FEATURES = sizeof(FEATURES)/sizeof(char*); +cfile.write(""" +/*********************/ +/* Feature full list */ +/*********************/ + +char const *const FEATURES_ALL[] = {\ +""") + +feature_string = """ + "{feature}",""" + +for feature in sorted(defs.allfeatures): + cfile.write(feature_string.format(feature=feature)) + +cfile.write(""" +}; +unsigned int const NUM_FEATURES_ALL = sizeof(FEATURES_ALL) / sizeof(char*); """) cfile.close() -print("Done.") From b6b36d090093511fa5de415575a232a3a3d3f1c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 1 Aug 2022 15:10:16 +0200 Subject: [PATCH 22/85] script_interface: Rewrite code_info module --- src/python/espressomd/CMakeLists.txt | 9 -- src/python/espressomd/code_features.py | 5 +- src/python/espressomd/code_info.py | 52 ++++++++++ src/python/espressomd/gen_code_info.py | 95 ------------------- src/script_interface/CMakeLists.txt | 1 + src/script_interface/code_info/CMakeLists.txt | 5 + src/script_interface/code_info/CodeInfo.cpp | 58 +++++++++++ src/script_interface/code_info/CodeInfo.hpp | 39 ++++++++ src/script_interface/code_info/initialize.cpp | 32 +++++++ src/script_interface/code_info/initialize.hpp | 35 +++++++ src/script_interface/initialize.cpp | 2 + testsuite/python/CMakeLists.txt | 1 + testsuite/python/code_info.py | 47 +++++++++ testsuite/python/features.py | 10 +- testsuite/scripts/test_importlib_wrapper.py | 3 +- 15 files changed, 283 insertions(+), 111 deletions(-) create mode 100644 src/python/espressomd/code_info.py delete mode 100644 src/python/espressomd/gen_code_info.py create mode 100644 src/script_interface/code_info/CMakeLists.txt create mode 100644 src/script_interface/code_info/CodeInfo.cpp create mode 100644 src/script_interface/code_info/CodeInfo.hpp create mode 100644 src/script_interface/code_info/initialize.cpp create mode 100644 src/script_interface/code_info/initialize.hpp create mode 100644 testsuite/python/code_info.py diff --git a/src/python/espressomd/CMakeLists.txt b/src/python/espressomd/CMakeLists.txt index f67a0e60ae5..8070b1e5753 100644 --- a/src/python/espressomd/CMakeLists.txt +++ b/src/python/espressomd/CMakeLists.txt @@ -34,14 +34,6 @@ add_custom_command( ${CMAKE_CURRENT_BINARY_DIR}/myconfig.pxi DEPENDS gen_pxiconfig) add_custom_target(espressomd) -add_custom_command( - OUTPUT code_info.pyx - COMMAND - ${PYTHON_EXECUTABLE} gen_code_info.py - ${CMAKE_SOURCE_DIR}/src/config/features.def - ${CMAKE_CURRENT_BINARY_DIR}/code_info.pyx - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/myconfig.pxi) # Make the cython_SRC, cython_HEADER and cython_AUX a cached variable to be able # to extend it in the subdirectories. @@ -56,7 +48,6 @@ set(cython_AUX "${cython_AUX}" CACHE INTERNAL "cython_AUX") add_subdirectory(io) -list(APPEND cython_SRC ${CMAKE_CURRENT_BINARY_DIR}/code_info.pyx) list(REMOVE_DUPLICATES cython_SRC) add_library(Espresso_pyx_flags INTERFACE) diff --git a/src/python/espressomd/code_features.py b/src/python/espressomd/code_features.py index 3a0d8a5ff99..1172d81d9e8 100644 --- a/src/python/espressomd/code_features.py +++ b/src/python/espressomd/code_features.py @@ -40,8 +40,9 @@ def has_features(*args): else: check_set = set(args) - if not check_set <= code_info.all_features(): - unknown_features = check_set - code_info.all_features() + all_features_set = set(code_info.all_features()) + if not check_set <= all_features_set: + unknown_features = check_set - all_features_set raise RuntimeError(f"unknown features {','.join(unknown_features)}") return check_set <= set(code_info.features()) diff --git a/src/python/espressomd/code_info.py b/src/python/espressomd/code_info.py new file mode 100644 index 00000000000..54773651296 --- /dev/null +++ b/src/python/espressomd/code_info.py @@ -0,0 +1,52 @@ +# +# 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, ScriptInterfaceHelper + + +@script_interface_register +class _CodeInfo(ScriptInterfaceHelper): + _so_name = "CodeInfo::CodeInfo" + _so_creation_policy = "LOCAL" + _so_bind_methods = ( + "build_type", "features", "all_features", "scafacos_methods" + ) + + +def build_type(): + """ + Get the CMake build type of this build of ESPResSo. + Can be e.g. Debug, Release, RelWithAssert, RelWithDebInfo, Coverage, etc. + """ + return _CodeInfo().build_type() + + +def features(): + """Get the list of features available in this build of ESPResSo.""" + return _CodeInfo().features() + + +def all_features(): + """Get the list of all features that can be activated in ESPResSo.""" + return _CodeInfo().all_features() + + +def scafacos_methods(): + """Lists long-range methods available in the ScaFaCoS library.""" + return _CodeInfo().scafacos_methods() diff --git a/src/python/espressomd/gen_code_info.py b/src/python/espressomd/gen_code_info.py deleted file mode 100644 index 4314d5e3cb6..00000000000 --- a/src/python/espressomd/gen_code_info.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright (C) 2016-2022 The ESPResSo project -# Copyright (C) 2014 Olaf Lenz -# -# 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 . -# -# This script generates code_info.pyx -# -import inspect -import sys -import os -# find featuredefs.py -moduledir = os.path.dirname(inspect.getfile(inspect.currentframe())) -sys.path.append(os.path.join(moduledir, '..', '..', 'config')) -import featuredefs - -if len(sys.argv) != 3: - print(f"Usage: {sys.argv[0]} DEFFILE PYXFILE", file=sys.stderr) - exit(2) - -deffilename, cfilename = sys.argv[1:3] - -print(f"Reading definitions from {deffilename}...") -defs = featuredefs.defs(deffilename) -print("Done.") - -# generate cpp-file -print(f"Writing {cfilename}...") -cfile = open(cfilename, 'w') - -cfile.write(""" -# This file is autogenerated by gen_code_info.py. -# DO NOT EDIT MANUALLY, CHANGES WILL BE LOST - -include "myconfig.pxi" -from . import utils - -def features(): - \"\"\"Returns list of features compiled into ESPResSo core\"\"\" - - f = [] -""") - -for feature in defs.allfeatures: - cfile.write(f"\n IF {feature} == 1:\n f.append(\"{feature}\")\n") - -cfile.write(f""" - return sorted(f) - -def all_features(): - return {defs.allfeatures} - - -cdef extern from "version.hpp": - cdef const char * ESPRESSO_BUILD_TYPE - - -def build_type(): - \"\"\"Prints the CMake build type. - Can be e.g. Debug, Release, RelWithAssert, RelWithDebInfo, Coverage, etc. - \"\"\" - return utils.to_str(ESPRESSO_BUILD_TYPE) # pylint: disable=undefined-variable - - -from libcpp.string cimport string -from libcpp.vector cimport vector - -IF SCAFACOS: - cdef extern from "script_interface/scafacos/scafacos.hpp" namespace "ScriptInterface::Scafacos": - vector[string] available_methods() - -def scafacos_methods(): - \"\"\"Lists long-range methods available in the ScaFaCoS library.\"\"\" - scafacos_features = [] - IF SCAFACOS == 1: - method_names = available_methods() - for method_name in method_names: - scafacos_features.append(method_name.decode('ascii')) - return scafacos_features -""") - -cfile.close() -print("Done.") diff --git a/src/script_interface/CMakeLists.txt b/src/script_interface/CMakeLists.txt index 9718a160841..95b4692391d 100644 --- a/src/script_interface/CMakeLists.txt +++ b/src/script_interface/CMakeLists.txt @@ -28,6 +28,7 @@ add_subdirectory(analysis) add_subdirectory(bond_breakage) add_subdirectory(cell_system) add_subdirectory(cluster_analysis) +add_subdirectory(code_info) add_subdirectory(collision_detection) add_subdirectory(constraints) add_subdirectory(electrostatics) diff --git a/src/script_interface/code_info/CMakeLists.txt b/src/script_interface/code_info/CMakeLists.txt new file mode 100644 index 00000000000..394f8a926e5 --- /dev/null +++ b/src/script_interface/code_info/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources( + Espresso_script_interface + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/CodeInfo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/CodeInfo.cpp) diff --git a/src/script_interface/code_info/CodeInfo.cpp b/src/script_interface/code_info/CodeInfo.cpp new file mode 100644 index 00000000000..5b3796214a6 --- /dev/null +++ b/src/script_interface/code_info/CodeInfo.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 . + */ + +#include "CodeInfo.hpp" + +#include "config-features.hpp" +#include "script_interface/scafacos/scafacos.hpp" +#include "version.hpp" + +#include +#include + +namespace ScriptInterface { +namespace CodeInfo { + +static Variant get_feature_list(char const *const ptr[], unsigned int len) { + return make_vector_of_variants(std::vector{ptr, ptr + len}); +} + +Variant CodeInfo::do_call_method(std::string const &name, + VariantMap const ¶meters) { + if (name == "features") { + return get_feature_list(FEATURES, NUM_FEATURES); + } + if (name == "all_features") { + return get_feature_list(FEATURES_ALL, NUM_FEATURES_ALL); + } + if (name == "build_type") { + return std::string(ESPRESSO_BUILD_TYPE); + } + if (name == "scafacos_methods") { +#ifdef SCAFACOS + return make_vector_of_variants(Scafacos::available_methods()); +#else // SCAFACOS + return make_vector_of_variants(std::vector(0)); +#endif // SCAFACOS + } + return {}; +} + +} // namespace CodeInfo +} // namespace ScriptInterface diff --git a/src/script_interface/code_info/CodeInfo.hpp b/src/script_interface/code_info/CodeInfo.hpp new file mode 100644 index 00000000000..70f80c2471e --- /dev/null +++ b/src/script_interface/code_info/CodeInfo.hpp @@ -0,0 +1,39 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_CODE_INFO_CODE_INFO_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_CODE_INFO_CODE_INFO_HPP + +#include "script_interface/ScriptInterface.hpp" + +#include + +namespace ScriptInterface { +namespace CodeInfo { + +class CodeInfo : public ObjectHandle { +public: + Variant do_call_method(std::string const &name, + VariantMap const ¶meters) override; +}; + +} // namespace CodeInfo +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/code_info/initialize.cpp b/src/script_interface/code_info/initialize.cpp new file mode 100644 index 00000000000..e63c526d8d0 --- /dev/null +++ b/src/script_interface/code_info/initialize.cpp @@ -0,0 +1,32 @@ +/* + * 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 "CodeInfo.hpp" + +namespace ScriptInterface { +namespace CodeInfo { + +void initialize(Utils::Factory *om) { + om->register_new("CodeInfo::CodeInfo"); +} + +} // namespace CodeInfo +} // namespace ScriptInterface diff --git a/src/script_interface/code_info/initialize.hpp b/src/script_interface/code_info/initialize.hpp new file mode 100644 index 00000000000..ae31eec14a7 --- /dev/null +++ b/src/script_interface/code_info/initialize.hpp @@ -0,0 +1,35 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_CODE_INFO_INITIALIZE_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_CODE_INFO_INITIALIZE_HPP + +#include "script_interface/ObjectHandle.hpp" + +#include + +namespace ScriptInterface { +namespace CodeInfo { + +void initialize(Utils::Factory *om); + +} // namespace CodeInfo +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/initialize.cpp b/src/script_interface/initialize.cpp index eca5538128c..f231e34156b 100644 --- a/src/script_interface/initialize.cpp +++ b/src/script_interface/initialize.cpp @@ -26,6 +26,7 @@ #include "bond_breakage/initialize.hpp" #include "cell_system/initialize.hpp" #include "cluster_analysis/initialize.hpp" +#include "code_info/initialize.hpp" #include "collision_detection/initialize.hpp" #include "constraints/initialize.hpp" #include "electrostatics/initialize.hpp" @@ -52,6 +53,7 @@ void initialize(Utils::Factory *f) { BondBreakage::initialize(f); CellSystem::initialize(f); ClusterAnalysis::initialize(f); + CodeInfo::initialize(f); CollisionDetection::initialize(f); Constraints::initialize(f); Coulomb::initialize(f); diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index bb057cd7a01..38e8ad49f13 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -129,6 +129,7 @@ python_test(FILE cell_system.py MAX_NUM_PROC 4) python_test(FILE get_neighbors.py MAX_NUM_PROC 4) python_test(FILE get_neighbors.py MAX_NUM_PROC 3 SUFFIX 3_cores) python_test(FILE tune_skin.py MAX_NUM_PROC 1) +python_test(FILE code_info.py MAX_NUM_PROC 1) python_test(FILE constraint_homogeneous_magnetic_field.py MAX_NUM_PROC 4) python_test(FILE cutoffs.py MAX_NUM_PROC 4) python_test(FILE cutoffs.py MAX_NUM_PROC 1 SUFFIX 1_core) diff --git a/testsuite/python/code_info.py b/testsuite/python/code_info.py new file mode 100644 index 00000000000..585d1457f0d --- /dev/null +++ b/testsuite/python/code_info.py @@ -0,0 +1,47 @@ +# +# 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 unittest as ut +import espressomd +import espressomd.code_info + + +class Test(ut.TestCase): + + def test_code_info(self): + # check CMake build type + build_types = { + "Debug", "Release", "RelWithDebInfo", "MinSizeRel", "Coverage", + "RelWithAssert"} + self.assertIn(espressomd.code_info.build_type(), build_types) + + # check features + features = espressomd.code_info.features() + all_features = espressomd.code_info.all_features() + self.assertTrue(set(features).issubset(all_features)) + + # check arrays are sorted + scafacos_methods = espressomd.code_info.scafacos_methods() + self.assertEqual(features, sorted(features)) + self.assertEqual(all_features, sorted(all_features)) + self.assertEqual(scafacos_methods, sorted(scafacos_methods)) + + +if __name__ == "__main__": + ut.main() diff --git a/testsuite/python/features.py b/testsuite/python/features.py index f21ddb64043..38cb07f8a35 100644 --- a/testsuite/python/features.py +++ b/testsuite/python/features.py @@ -24,14 +24,16 @@ class Features(ut.TestCase): def test_has_features(self): - for feature in espressomd.code_info.features(): + all_features = set(espressomd.code_info.all_features()) + features = set(espressomd.code_info.features()) + + for feature in features: self.assertTrue(espressomd.has_features(feature)) - for feature in espressomd.code_info.all_features( - ) - set(espressomd.code_info.features()): + for feature in all_features - features: self.assertFalse(espressomd.has_features(feature)) - with self.assertRaises(RuntimeError) as _: + with self.assertRaises(RuntimeError): espressomd.has_features("NotAFeature") diff --git a/testsuite/scripts/test_importlib_wrapper.py b/testsuite/scripts/test_importlib_wrapper.py index f325ff2dd40..364a9cf2c65 100644 --- a/testsuite/scripts/test_importlib_wrapper.py +++ b/testsuite/scripts/test_importlib_wrapper.py @@ -360,7 +360,8 @@ def test_configure_and_import(self): path_out.read_text()) # test importing a sample that relies on features not compiled in - inactive_features = espressomd.all_features() - set(espressomd.features()) + inactive_features = set( + espressomd.all_features()) - set(espressomd.features()) if inactive_features: path_features.write_text(f""" import espressomd From 1eaa7a631c64ec23b642953bc4809e133621d355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 2 Aug 2022 14:25:28 +0200 Subject: [PATCH 23/85] script_interface: Rewrite version module --- src/python/espressomd/version.pxd | 22 ------ src/python/espressomd/version.py | 73 +++++++++++++++++++ src/python/espressomd/version.pyx | 59 --------------- src/script_interface/code_info/CMakeLists.txt | 2 +- src/script_interface/code_info/Version.cpp | 72 ++++++++++++++++++ src/script_interface/code_info/Version.hpp | 39 ++++++++++ src/script_interface/code_info/initialize.cpp | 2 + testsuite/python/code_info.py | 21 ++++++ 8 files changed, 208 insertions(+), 82 deletions(-) delete mode 100644 src/python/espressomd/version.pxd create mode 100644 src/python/espressomd/version.py delete mode 100644 src/python/espressomd/version.pyx create mode 100644 src/script_interface/code_info/Version.cpp create mode 100644 src/script_interface/code_info/Version.hpp diff --git a/src/python/espressomd/version.pxd b/src/python/espressomd/version.pxd deleted file mode 100644 index 81b2e3d91ef..00000000000 --- a/src/python/espressomd/version.pxd +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (C) 2010-2022 The ESPResSo project -# -# This file is part of ESPResSo. -# -# ESPResSo is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# ESPResSo is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -cdef extern from "version.hpp": - cdef int ESPRESSO_VERSION_MAJOR - cdef int ESPRESSO_VERSION_MINOR - cdef const char * GIT_BRANCH - cdef const char * GIT_COMMIT_HASH - cdef const char * GIT_STATE diff --git a/src/python/espressomd/version.py b/src/python/espressomd/version.py new file mode 100644 index 00000000000..94877b94dd1 --- /dev/null +++ b/src/python/espressomd/version.py @@ -0,0 +1,73 @@ +# +# Copyright (C) 2010-2022 The ESPResSo project +# +# This file is part of ESPResSo. +# +# ESPResSo is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# ESPResSo is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +from .script_interface import script_interface_register, ScriptInterfaceHelper + + +@script_interface_register +class _Version(ScriptInterfaceHelper): + _so_name = "CodeInfo::Version" + _so_creation_policy = "LOCAL" + _so_bind_methods = ( + "version", + "version_major", + "version_minor", + "version_friendly", + "git_branch", + "git_commit", + "git_state", + ) + + +def version(): + """Get the version of ESPResSo.""" + return tuple(_Version().version()) + + +def major(): + """Get the major version of ESPResSo.""" + return _Version().version_major() + + +def minor(): + """Get the minor version of ESPResSo.""" + return _Version().version_minor() + + +def friendly(): + """Dot version of the version.""" + return _Version().version_friendly() + + +def git_branch(): + """Git branch of the build if known, otherwise empty.""" + return _Version().git_branch() + + +def git_commit(): + """Git commit of the build if known, otherwise empty.""" + return _Version().git_commit() + + +def git_state(): + """ + Git state of the build if known, otherwise empty. State is "CLEAN" if the + repository was not changed from :meth:`git_commit()`, "DIRTY" otherwise. + """ + return _Version().git_state() diff --git a/src/python/espressomd/version.pyx b/src/python/espressomd/version.pyx deleted file mode 100644 index e42a7d6f712..00000000000 --- a/src/python/espressomd/version.pyx +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (C) 2010-2022 The ESPResSo project -# -# This file is part of ESPResSo. -# -# ESPResSo is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# ESPResSo is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from . import utils - - -def major(): - """Prints the major version of ESPResSo. - """ - return ESPRESSO_VERSION_MAJOR # pylint: disable=undefined-variable - - -def minor(): - """Prints the minor version of ESPResSo. - """ - return ESPRESSO_VERSION_MINOR # pylint: disable=undefined-variable - - -def friendly(): - """Dot version of the version. - """ - return f"{major()}.{minor()}" - - -def git_branch(): - """Git branch of the build if known, otherwise - empty. - """ - return utils.to_str(GIT_BRANCH) # pylint: disable=undefined-variable - - -def git_commit(): - """Git commit of the build if known, otherwise - empty. - """ - return utils.to_str(GIT_COMMIT_HASH) # pylint: disable=undefined-variable - - -def git_state(): - """Git state of the build if known, otherwise - empty. State is "CLEAN" if the repository - was not changed from :meth:`git_commit()`, - "DIRTY" otherwise. - """ - return utils.to_str(GIT_STATE) # pylint: disable=undefined-variable diff --git a/src/script_interface/code_info/CMakeLists.txt b/src/script_interface/code_info/CMakeLists.txt index 394f8a926e5..40b7d3dd568 100644 --- a/src/script_interface/code_info/CMakeLists.txt +++ b/src/script_interface/code_info/CMakeLists.txt @@ -2,4 +2,4 @@ target_sources( Espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CodeInfo.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/CodeInfo.cpp) + ${CMAKE_CURRENT_SOURCE_DIR}/Version.cpp) diff --git a/src/script_interface/code_info/Version.cpp b/src/script_interface/code_info/Version.cpp new file mode 100644 index 00000000000..22c09b10d94 --- /dev/null +++ b/src/script_interface/code_info/Version.cpp @@ -0,0 +1,72 @@ +/* + * 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 "Version.hpp" + +#include "version.hpp" + +#include + +#include +#include + +namespace ScriptInterface { +namespace CodeInfo { + +static auto get_version_tuple_as_string() { + std::vector version; + boost::split(version, std::string{ESPRESSO_VERSION}, boost::is_any_of("-")); + return version[0]; +} + +Variant Version::do_call_method(std::string const &name, + VariantMap const ¶meters) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) + if (name == "version_major") { + return ESPRESSO_VERSION_MAJOR; + } + if (name == "version_minor") { + return ESPRESSO_VERSION_MINOR; + } + if (name == "version_friendly") { + return get_version_tuple_as_string(); + } + if (name == "version") { + std::vector version; + boost::split(version, get_version_tuple_as_string(), boost::is_any_of(".")); + std::vector version_tuple; + for (auto const &x : version) { + version_tuple.emplace_back(std::stoi(x)); + } + return version_tuple; + } + if (name == "git_branch") { + return std::string{GIT_BRANCH}; + } + if (name == "git_commit") { + return std::string{GIT_COMMIT_HASH}; + } + if (name == "git_state") { + return std::string{GIT_STATE}; + } + return {}; +} + +} // namespace CodeInfo +} // namespace ScriptInterface diff --git a/src/script_interface/code_info/Version.hpp b/src/script_interface/code_info/Version.hpp new file mode 100644 index 00000000000..ce63495b58a --- /dev/null +++ b/src/script_interface/code_info/Version.hpp @@ -0,0 +1,39 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_CODE_INFO_VERSION_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_CODE_INFO_VERSION_HPP + +#include "script_interface/ScriptInterface.hpp" + +#include + +namespace ScriptInterface { +namespace CodeInfo { + +class Version : public ObjectHandle { +public: + Variant do_call_method(std::string const &name, + VariantMap const ¶meters) override; +}; + +} // namespace CodeInfo +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/code_info/initialize.cpp b/src/script_interface/code_info/initialize.cpp index e63c526d8d0..72730ece45f 100644 --- a/src/script_interface/code_info/initialize.cpp +++ b/src/script_interface/code_info/initialize.cpp @@ -20,12 +20,14 @@ #include "initialize.hpp" #include "CodeInfo.hpp" +#include "Version.hpp" namespace ScriptInterface { namespace CodeInfo { void initialize(Utils::Factory *om) { om->register_new("CodeInfo::CodeInfo"); + om->register_new("CodeInfo::Version"); } } // namespace CodeInfo diff --git a/testsuite/python/code_info.py b/testsuite/python/code_info.py index 585d1457f0d..878c6d72c64 100644 --- a/testsuite/python/code_info.py +++ b/testsuite/python/code_info.py @@ -20,6 +20,7 @@ import unittest as ut import espressomd import espressomd.code_info +import espressomd.version class Test(ut.TestCase): @@ -42,6 +43,26 @@ def test_code_info(self): self.assertEqual(all_features, sorted(all_features)) self.assertEqual(scafacos_methods, sorted(scafacos_methods)) + def test_version(self): + version_full = espressomd.version.version() + version_major_minor = (espressomd.version.major(), + espressomd.version.minor()) + self.assertTrue(all(x >= 0 for x in version_full)) + self.assertIn(len(version_full), (2, 3)) + self.assertEqual(version_full[:2], version_major_minor) + self.assertEqual(".".join(map(str, espressomd.version.version())), + espressomd.version.friendly()) + + def test_git_info(self): + git_states = {"CLEAN", "DIRTY"} + commit_charset = set("abcdef0123456789") + self.assertIn(espressomd.version.git_state(), git_states) + self.assertIsInstance(espressomd.version.git_branch(), str) + self.assertIsInstance(espressomd.version.git_commit(), str) + git_commit = espressomd.version.git_commit() + self.assertLessEqual(len(git_commit), 40) + self.assertTrue(set(git_commit).issubset(commit_charset)) + if __name__ == "__main__": ut.main() From 606247de636ea8e9921fab6b3cd883727f8be709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 2 Aug 2022 14:37:28 +0200 Subject: [PATCH 24/85] script_interface: Rewrite cuda_init module --- src/python/espressomd/cuda_init.pxd | 37 ------ src/python/espressomd/cuda_init.py | 69 ++++++++++ src/python/espressomd/cuda_init.pyx | 122 ------------------ src/script_interface/system/CMakeLists.txt | 1 + .../system/CudaInitHandle.cpp | 110 ++++++++++++++++ .../system/CudaInitHandle.hpp | 41 ++++++ src/script_interface/system/initialize.cpp | 2 + testsuite/python/gpu_availability.py | 21 ++- 8 files changed, 238 insertions(+), 165 deletions(-) delete mode 100644 src/python/espressomd/cuda_init.pxd create mode 100644 src/python/espressomd/cuda_init.py delete mode 100644 src/python/espressomd/cuda_init.pyx create mode 100644 src/script_interface/system/CudaInitHandle.cpp create mode 100644 src/script_interface/system/CudaInitHandle.hpp diff --git a/src/python/espressomd/cuda_init.pxd b/src/python/espressomd/cuda_init.pxd deleted file mode 100644 index 1336553123e..00000000000 --- a/src/python/espressomd/cuda_init.pxd +++ /dev/null @@ -1,37 +0,0 @@ -# -# 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 . -# - -from libcpp.vector cimport vector - -cdef extern from "cuda_init.hpp": - cdef struct EspressoGpuDevice: - int id - char name[64] - char proc_name[64] - int node - int compute_capability_major - int compute_capability_minor - size_t total_memory - int n_cores - - void cuda_set_device(int dev) except + - int cuda_get_device() except + - int cuda_get_n_gpus() except + - void cuda_get_gpu_name(int dev, char name[64]) except + - vector[EspressoGpuDevice] cuda_gather_gpus() diff --git a/src/python/espressomd/cuda_init.py b/src/python/espressomd/cuda_init.py new file mode 100644 index 00000000000..6cfd844fe16 --- /dev/null +++ b/src/python/espressomd/cuda_init.py @@ -0,0 +1,69 @@ +# +# 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 . +# + +from .script_interface import script_interface_register, ScriptInterfaceHelper + + +@script_interface_register +class CudaInitHandle(ScriptInterfaceHelper): + """ + Attributes + ---------- + device: :obj:`int` + Device id to use. + + Methods + ------- + list_devices() + List devices. + + Returns + ------- + :obj:`dict` : + Available CUDA devices sorted by device id. + + """ + _so_name = "System::CudaInitHandle" + _so_creation_policy = "LOCAL" + _so_bind_methods = ("list_devices",) + + def list_devices_properties(self): + """ + List devices with their properties on each host machine. + + Returns + ------- + :obj:`dict` : + Available CUDA devices with their properties sorted by hostname + and device id. + + """ + out = self.call_method("list_devices_properties") + for listing in out.values(): + for dev in listing.values(): + dev["compute_capability"] = tuple(dev["compute_capability"]) + return out + + +def gpu_available(): + """ + Check whether there is at least one compatible GPU available. + """ + n_compatible_gpus = CudaInitHandle().call_method("get_n_gpus") + return n_compatible_gpus > 0 diff --git a/src/python/espressomd/cuda_init.pyx b/src/python/espressomd/cuda_init.pyx deleted file mode 100644 index 1a34f025790..00000000000 --- a/src/python/espressomd/cuda_init.pyx +++ /dev/null @@ -1,122 +0,0 @@ -# -# 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 . -# -include "myconfig.pxi" -from . cimport cuda_init -from . import utils - -cdef class CudaInitHandle: - def __init__(self): - IF CUDA != 1: - raise Exception("CUDA is not compiled in") - - IF CUDA == 1: - @property - def device(self): - """ - Get device. - - Returns - ------- - :obj:`int` : - Id of current set device. - - """ - dev = cuda_get_device() - return dev - - @device.setter - def device(self, int dev): - """ - Specify which device to use. - - Parameters - ---------- - dev : :obj:`int` - Set the device id of the graphics card to use. - - """ - cuda_set_device(dev) - - def list_devices(self): - """ - List devices. - - Returns - ------- - :obj:`dict` : - List of available CUDA devices. - - """ - cdef char gpu_name_buffer[4 + 64] - n_gpus = 0 - try: - n_gpus = cuda_get_n_gpus() - except RuntimeError: - pass - devices = dict() - for i in range(n_gpus): - try: - cuda_get_gpu_name(i, gpu_name_buffer) - except RuntimeError: - continue - devices[i] = utils.to_str(gpu_name_buffer) - return devices - - def list_devices_properties(self): - """ - List devices with their properties on each host machine. - - Returns - ------- - :obj:`dict` : - List of available CUDA devices with their properties. - - """ - cdef vector[EspressoGpuDevice] devices - cdef EspressoGpuDevice dev - try: - devices = cuda_gather_gpus() - except RuntimeError: - pass - resources = dict() - for i in range(devices.size()): - dev = devices[i] - hostname = utils.to_str(dev.proc_name) - if hostname not in resources: - resources[hostname] = {} - resources[hostname][dev.id] = { - 'name': utils.to_str(dev.name), - 'compute_capability': ( - dev.compute_capability_major, - dev.compute_capability_minor - ), - 'cores': dev.n_cores, - 'total_memory': dev.total_memory, - } - return resources - -IF CUDA: - def gpu_available(): - try: - return cuda_get_n_gpus() > 0 - except RuntimeError: - return False -ELSE: - def gpu_available(): - return False diff --git a/src/script_interface/system/CMakeLists.txt b/src/script_interface/system/CMakeLists.txt index 8506546385f..3a66434c73f 100644 --- a/src/script_interface/system/CMakeLists.txt +++ b/src/script_interface/system/CMakeLists.txt @@ -1,5 +1,6 @@ target_sources( Espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/CudaInitHandle.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Globals.cpp ${CMAKE_CURRENT_SOURCE_DIR}/System.cpp) diff --git a/src/script_interface/system/CudaInitHandle.cpp b/src/script_interface/system/CudaInitHandle.cpp new file mode 100644 index 00000000000..afbb9e8971a --- /dev/null +++ b/src/script_interface/system/CudaInitHandle.cpp @@ -0,0 +1,110 @@ +/* + * 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 . + */ + +#include "CudaInitHandle.hpp" + +#include "config.hpp" + +#include "core/cuda_init.hpp" +#include "core/cuda_utils.hpp" + +#include +#include +#include +#include + +namespace ScriptInterface { +namespace System { + +CudaInitHandle::CudaInitHandle() { + add_parameters({ +#ifdef CUDA + {"device", [](Variant const &v) { cuda_set_device(get_value(v)); }, + []() { return cuda_get_device(); }}, +#endif // CUDA + }); +} + +#ifdef CUDA +/** + * @brief Silently ignore CUDA exceptions. + * This is useful when querying the properties of CUDA devices that may + * not have a suitable CUDA version, or when there is no compatible CUDA + * device available. + */ +template static void skip_cuda_errors(F &&fun) { + try { + fun(); + } catch (cuda_runtime_error const &) { + } +} +#endif // CUDA + +Variant CudaInitHandle::do_call_method(std::string const &name, + VariantMap const ¶meters) { + if (name == "list_devices") { + std::unordered_map devices{}; +#ifdef CUDA + auto n_gpus = 0; + skip_cuda_errors([&n_gpus]() { n_gpus = cuda_get_n_gpus(); }); + for (int i = 0; i < n_gpus; ++i) { + skip_cuda_errors([&devices, i]() { + char gpu_name_buffer[4 + 64]; + cuda_get_gpu_name(i, gpu_name_buffer); + devices[i] = std::string{gpu_name_buffer}; + }); + } +#endif // CUDA + return make_unordered_map_of_variants(devices); + } + if (name == "list_devices_properties") { + std::unordered_map> dict{}; +#ifdef CUDA + std::vector devices; + skip_cuda_errors([&devices]() { devices = cuda_gather_gpus(); }); + for (auto const &dev : devices) { + auto const hostname = std::string{dev.proc_name}; + if (dict.count(hostname) == 0) { + dict[hostname] = {}; + } + std::unordered_map dev_properties = { + {"name", std::string{dev.name}}, + {"compute_capability", + Variant{std::vector{ + {dev.compute_capability_major, dev.compute_capability_minor}}}}, + {"cores", dev.n_cores}, + {"total_memory", dev.total_memory}, + }; + dict[hostname][dev.id] = std::move(dev_properties); + } +#endif // CUDA + return make_unordered_map_of_variants(dict); + } + if (name == "get_n_gpus") { + auto n_gpus = 0; +#ifdef CUDA + skip_cuda_errors([&n_gpus]() { n_gpus = cuda_get_n_gpus(); }); +#endif // CUDA + return n_gpus; + } + return {}; +} + +} // namespace System +} // namespace ScriptInterface diff --git a/src/script_interface/system/CudaInitHandle.hpp b/src/script_interface/system/CudaInitHandle.hpp new file mode 100644 index 00000000000..0bf8d3c898d --- /dev/null +++ b/src/script_interface/system/CudaInitHandle.hpp @@ -0,0 +1,41 @@ +/* + * 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 . + */ + +#ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_SYSTEM_CUDA_INIT_HANDLE_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_SYSTEM_CUDA_INIT_HANDLE_HPP + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include + +namespace ScriptInterface { +namespace System { + +class CudaInitHandle : public AutoParameters { +public: + CudaInitHandle(); + Variant do_call_method(std::string const &name, + VariantMap const ¶meters) override; +}; + +} // namespace System +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/system/initialize.cpp b/src/script_interface/system/initialize.cpp index 5959bdd0e76..f83a34b0a74 100644 --- a/src/script_interface/system/initialize.cpp +++ b/src/script_interface/system/initialize.cpp @@ -19,6 +19,7 @@ #include "initialize.hpp" +#include "CudaInitHandle.hpp" #include "Globals.hpp" #include "System.hpp" @@ -26,6 +27,7 @@ namespace ScriptInterface { namespace System { void initialize(Utils::Factory *om) { + om->register_new("System::CudaInitHandle"); om->register_new("System::Globals"); om->register_new("System::System"); } diff --git a/testsuite/python/gpu_availability.py b/testsuite/python/gpu_availability.py index 3bd8d8b4e23..3cba5a7a06f 100644 --- a/testsuite/python/gpu_availability.py +++ b/testsuite/python/gpu_availability.py @@ -19,6 +19,7 @@ import unittest as ut import unittest_decorators as utx import espressomd +import espressomd.cuda_init class GPUAvailability(ut.TestCase): @@ -27,14 +28,21 @@ class GPUAvailability(ut.TestCase): system = espressomd.System(box_l=[1, 1, 1]) def test(self): + cuda_init_handle = espressomd.cuda_init.CudaInitHandle() + devices = cuda_init_handle.list_devices() + devices_prop = cuda_init_handle.list_devices_properties() + has_gpu = espressomd.gpu_available() + self.assertIsInstance(devices, dict) + self.assertIsInstance(devices_prop, dict) if espressomd.has_features("CUDA"): - self.assertEqual(self.system.cuda_init_handle.list_devices() != {}, - espressomd.gpu_available()) - self.assertEqual( - self.system.cuda_init_handle.list_devices_properties() != {}, - espressomd.gpu_available()) + self.assertEqual(devices != {}, has_gpu) + self.assertEqual(devices_prop != {}, has_gpu) else: - self.assertFalse(espressomd.gpu_available()) + self.assertFalse(has_gpu) + self.assertEqual(devices, {}) + self.assertEqual(devices_prop, {}) + with self.assertRaisesRegex(AttributeError, "Object 'CudaInitHandle' has no attribute 'device'"): + cuda_init_handle.device @utx.skipIfMissingFeatures("CUDA") def test_exceptions(self): @@ -68,6 +76,7 @@ def test_list_devices(self): dev_id = self.system.cuda_init_handle.device self.assertIn(dev_id, device_list_p_head) device = device_list_p_head[dev_id] + self.assertIsInstance(device['compute_capability'], tuple) self.assertGreater(device['cores'], 0) self.assertGreater(device['total_memory'], 0) self.assertGreaterEqual(device['compute_capability'][0], 3) From 1fba163e86a49fe9e0bd46b132925eac91b160de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 5 Aug 2022 16:43:41 +0200 Subject: [PATCH 25/85] script_interface: Fix regressions revealed by CI --- src/python/espressomd/utils.pyx | 2 +- src/python/espressomd/version.py | 2 +- src/script_interface/code_info/CMakeLists.txt | 2 +- .../code_info/{Version.cpp => CodeVersion.cpp} | 6 +++--- .../code_info/{Version.hpp => CodeVersion.hpp} | 6 +++--- src/script_interface/code_info/initialize.cpp | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) rename src/script_interface/code_info/{Version.cpp => CodeVersion.cpp} (92%) rename src/script_interface/code_info/{Version.hpp => CodeVersion.hpp} (85%) diff --git a/src/python/espressomd/utils.pyx b/src/python/espressomd/utils.pyx index 8900b2a6e0a..987086c0baa 100644 --- a/src/python/espressomd/utils.pyx +++ b/src/python/espressomd/utils.pyx @@ -23,7 +23,7 @@ import numpy as np cdef _check_type_or_throw_except_assertion(x, t): return isinstance(x, t) or (t == int and is_valid_type(x, int)) or ( t == float and (is_valid_type(x, int) or is_valid_type(x, float))) or ( - t == bool and (is_valid_type(x, bool) or x in (0, 1))) + t == bool and is_valid_type(x, bool)) cpdef check_array_type_or_throw_except(x, n, t, msg): diff --git a/src/python/espressomd/version.py b/src/python/espressomd/version.py index 94877b94dd1..081b1bd114a 100644 --- a/src/python/espressomd/version.py +++ b/src/python/espressomd/version.py @@ -22,7 +22,7 @@ @script_interface_register class _Version(ScriptInterfaceHelper): - _so_name = "CodeInfo::Version" + _so_name = "CodeInfo::CodeVersion" _so_creation_policy = "LOCAL" _so_bind_methods = ( "version", diff --git a/src/script_interface/code_info/CMakeLists.txt b/src/script_interface/code_info/CMakeLists.txt index 40b7d3dd568..734ebd564c8 100644 --- a/src/script_interface/code_info/CMakeLists.txt +++ b/src/script_interface/code_info/CMakeLists.txt @@ -2,4 +2,4 @@ target_sources( Espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CodeInfo.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Version.cpp) + ${CMAKE_CURRENT_SOURCE_DIR}/CodeVersion.cpp) diff --git a/src/script_interface/code_info/Version.cpp b/src/script_interface/code_info/CodeVersion.cpp similarity index 92% rename from src/script_interface/code_info/Version.cpp rename to src/script_interface/code_info/CodeVersion.cpp index 22c09b10d94..1c6958339a7 100644 --- a/src/script_interface/code_info/Version.cpp +++ b/src/script_interface/code_info/CodeVersion.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "Version.hpp" +#include "CodeVersion.hpp" #include "version.hpp" @@ -35,8 +35,8 @@ static auto get_version_tuple_as_string() { return version[0]; } -Variant Version::do_call_method(std::string const &name, - VariantMap const ¶meters) { +Variant CodeVersion::do_call_method(std::string const &name, + VariantMap const ¶meters) { // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) if (name == "version_major") { return ESPRESSO_VERSION_MAJOR; diff --git a/src/script_interface/code_info/Version.hpp b/src/script_interface/code_info/CodeVersion.hpp similarity index 85% rename from src/script_interface/code_info/Version.hpp rename to src/script_interface/code_info/CodeVersion.hpp index ce63495b58a..1cbec83f64b 100644 --- a/src/script_interface/code_info/Version.hpp +++ b/src/script_interface/code_info/CodeVersion.hpp @@ -17,8 +17,8 @@ * along with this program. If not, see . */ -#ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_CODE_INFO_VERSION_HPP -#define ESPRESSO_SRC_SCRIPT_INTERFACE_CODE_INFO_VERSION_HPP +#ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_CODE_INFO_CODE_VERSION_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_CODE_INFO_CODE_VERSION_HPP #include "script_interface/ScriptInterface.hpp" @@ -27,7 +27,7 @@ namespace ScriptInterface { namespace CodeInfo { -class Version : public ObjectHandle { +class CodeVersion : public ObjectHandle { public: Variant do_call_method(std::string const &name, VariantMap const ¶meters) override; diff --git a/src/script_interface/code_info/initialize.cpp b/src/script_interface/code_info/initialize.cpp index 72730ece45f..f9df14eff32 100644 --- a/src/script_interface/code_info/initialize.cpp +++ b/src/script_interface/code_info/initialize.cpp @@ -20,14 +20,14 @@ #include "initialize.hpp" #include "CodeInfo.hpp" -#include "Version.hpp" +#include "CodeVersion.hpp" namespace ScriptInterface { namespace CodeInfo { void initialize(Utils::Factory *om) { om->register_new("CodeInfo::CodeInfo"); - om->register_new("CodeInfo::Version"); + om->register_new("CodeInfo::CodeVersion"); } } // namespace CodeInfo From 285aac0e41abaf5b5fd42b8a9d3f62222b99db03 Mon Sep 17 00:00:00 2001 From: Christoph Lohrmann Date: Mon, 8 Aug 2022 14:17:24 +0200 Subject: [PATCH 26/85] rotation - fix -interaction --- src/core/thermostats/brownian_inline.hpp | 15 +++++++------- testsuite/python/brownian_dynamics.py | 26 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/core/thermostats/brownian_inline.hpp b/src/core/thermostats/brownian_inline.hpp index a4afe0a90a2..70633dc2a5f 100644 --- a/src/core/thermostats/brownian_inline.hpp +++ b/src/core/thermostats/brownian_inline.hpp @@ -265,7 +265,7 @@ bd_drag_rot(Thermostat::GammaType const &brownian_gamma_rotation, Particle &p, Utils::Vector3d dphi = {}; for (int j = 0; j < 3; j++) { - if (!p.is_fixed_along(j)) { + if (p.can_rotate_around(j)) { // only a conservative part of the torque is used here #ifndef PARTICLE_ANISOTROPY dphi[j] = p.torque()[j] * dt / gamma; @@ -304,12 +304,11 @@ bd_drag_vel_rot(Thermostat::GammaType const &brownian_gamma_rotation, Utils::Vector3d omega = {}; for (int j = 0; j < 3; j++) { - if (!p.is_fixed_along(j)) { - // only conservative part of the force is used here -#ifndef PARTICLE_ANISOTROPY - omega[j] = p.torque()[j] / gamma; -#else + if (p.can_rotate_around(j)) { +#ifdef PARTICLE_ANISOTROPY omega[j] = p.torque()[j] / gamma[j]; +#else + omega[j] = p.torque()[j] / gamma; #endif // PARTICLE_ANISOTROPY } } @@ -343,7 +342,7 @@ bd_random_walk_rot(BrownianThermostat const &brownian, Particle const &p, auto const noise = Random::noise_gaussian( brownian.rng_counter(), brownian.rng_seed(), p.id()); for (int j = 0; j < 3; j++) { - if (!p.is_fixed_along(j)) { + if (p.can_rotate_around(j)) { #ifndef PARTICLE_ANISOTROPY if (sigma_pos > 0.0) { dphi[j] = noise[j] * sigma_pos * sqrt(dt); @@ -378,7 +377,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.id()); for (int j = 0; j < 3; j++) { - if (!p.is_fixed_along(j)) { + if (p.can_rotate_around(j)) { domega[j] = sigma_vel * noise[j] / sqrt(p.rinertia()[j]); } } diff --git a/testsuite/python/brownian_dynamics.py b/testsuite/python/brownian_dynamics.py index b99b78034cf..6c87186eff5 100644 --- a/testsuite/python/brownian_dynamics.py +++ b/testsuite/python/brownian_dynamics.py @@ -152,6 +152,32 @@ def test_07__virtual(self): np.testing.assert_almost_equal(np.copy(virtual.v), [0, 0, 0]) np.testing.assert_almost_equal(np.copy(physical.v), [0, 0, 0]) + @utx.skipIfMissingFeatures(["ROTATION", "EXTERNAL_FORCES"]) + def test_fix_rotation(self): + system = self.system + system.time_step = 0.01 + part = system.part.add( + pos=3 * [0.], + fix=3 * [True], + rotation=3 * [True]) + + # torque only + part.ext_torque = [0, 0, 1.3] + system.thermostat.set_brownian( + kT=0, gamma=1, gamma_rotation=1.5, act_on_virtual=False, seed=41) + system.integrator.set_brownian_dynamics() + system.integrator.run(3) + np.testing.assert_allclose( + part.omega_lab, [ + 0, 0, 1.3 / 1.5], atol=1e-14) + + # noise only + part.ext_torque = 3 * [0.] + system.thermostat.set_brownian( + kT=1, gamma=1, gamma_rotation=1.5, act_on_virtual=False, seed=41) + system.integrator.run(3) + self.assertGreater(np.linalg.norm(part.omega_lab), 0.) + if __name__ == "__main__": ut.main() From 381aac217f7793c22a52bad7602fe049b6069499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 8 Aug 2022 16:32:28 +0200 Subject: [PATCH 27/85] CMake: Modernize handling of Python dependencies --- CMakeLists.txt | 49 ++++++++++++++++++------- cmake/FindCython.cmake | 2 +- cmake/FindNumPy.cmake | 44 ---------------------- cmake/FindPythonHeaders.cmake | 35 ------------------ cmake/FindSphinx.cmake | 6 +-- doc/doxygen/CMakeLists.txt | 2 +- doc/sphinx/CMakeLists.txt | 2 +- src/CMakeLists.txt | 2 +- src/config/CMakeLists.txt | 8 ++-- src/core/CMakeLists.txt | 5 ++- src/particle_observables/CMakeLists.txt | 2 +- src/python/CMakeLists.txt | 6 +-- src/python/espressomd/CMakeLists.txt | 15 +++++--- src/python/pypresso.cmakein | 6 +-- src/scafacos/CMakeLists.txt | 3 +- src/script_interface/CMakeLists.txt | 2 +- src/shapes/CMakeLists.txt | 2 +- testsuite/cmake/CMakeLists.txt | 2 +- testsuite/cmake/test_install.sh | 10 ++--- 19 files changed, 75 insertions(+), 128 deletions(-) delete mode 100644 cmake/FindNumPy.cmake delete mode 100644 cmake/FindPythonHeaders.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index c7c9a21b016..8348a3f74e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,8 +54,8 @@ minimal_compiler_version("Intel" 18.0) minimal_compiler_version("IntelLLVM" 2021.0) include(FeatureSummary) -include(GNUInstallDirs) project(ESPResSo) +include(GNUInstallDirs) include(option_enum) if(POLICY CMP0074) # make find_package() use _ROOT variables @@ -162,7 +162,7 @@ foreach(func_name __PRETTY_FUNCTION__ __FUNCSIG__ __FUNCTION__) endforeach() # -# Libraries +# Interface libraries # # CUDA compiler @@ -188,25 +188,47 @@ if(WITH_CUDA) endif() endif(WITH_CUDA) -find_package(PythonInterp 3.8 REQUIRED) - +# Python interpreter and Cython interface library if(WITH_PYTHON) + find_package(Python 3.8 REQUIRED COMPONENTS Interpreter Development NumPy) find_package(Cython 0.29.14 REQUIRED) - find_package(PythonHeaders REQUIRED) - find_package(NumPy REQUIRED) find_program(IPYTHON_EXECUTABLE NAMES jupyter ipython3 ipython) endif(WITH_PYTHON) +# +# Installation folders +# + +string(REGEX REPLACE "/+$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# folder for binaries and wrapper scripts +set(ESPRESSO_INSTALL_BINDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}") + +# folder for C++ and CUDA shared objects +set(ESPRESSO_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") + +# python site-packages, can be overriden with CMake options +if(WITH_PYTHON) + if(NOT ESPRESSO_INSTALL_PYTHON) + if(CMAKE_INSTALL_PREFIX STREQUAL "/") + set(ESPRESSO_INSTALL_PYTHON "${Python_SITEARCH}") + else() + set(ESPRESSO_INSTALL_PYTHON "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/site-packages") + endif() + endif() + # override: package C++, CUDA and Cython shared objects together + set(ESPRESSO_INSTALL_LIBDIR "${ESPRESSO_INSTALL_PYTHON}/espressomd") +endif() + +# +# Libraries +# + find_package(FFTW3) if(FFTW3_FOUND) set(FFTW 3) endif(FFTW3_FOUND) -# If we build Python bindings, turn on script interface -if(WITH_PYTHON) - set(WITH_SCRIPT_INTERFACE ON) -endif() - # We need the parallel hdf5 version! if(WITH_HDF5) # The FindHDF5 function will fall back to the serial version if no parallel @@ -263,8 +285,7 @@ if(GSL_FOUND) endif(GSL_FOUND) if(WITH_STOKESIAN_DYNAMICS) - set(CMAKE_INSTALL_LIBDIR - "${CMAKE_INSTALL_PREFIX}/${PYTHON_INSTDIR}/espressomd") + set(CMAKE_INSTALL_LIBDIR "${ESPRESSO_INSTALL_LIBDIR}") include(FetchContent) FetchContent_Declare( stokesian_dynamics @@ -337,7 +358,7 @@ find_package(Boost 1.69.0 REQUIRED ${BOOST_COMPONENTS}) # Paths # -set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${PYTHON_INSTDIR}/espressomd") +set(CMAKE_INSTALL_RPATH "${ESPRESSO_INSTALL_LIBDIR}") # # Flags diff --git a/cmake/FindCython.cmake b/cmake/FindCython.cmake index f300257acc8..e2dd86004da 100644 --- a/cmake/FindCython.cmake +++ b/cmake/FindCython.cmake @@ -22,7 +22,7 @@ # limitations under the License. #============================================================================= -set(CYTHON_EXECUTABLE ${PYTHON_EXECUTABLE} -m cython) +set(CYTHON_EXECUTABLE ${Python_EXECUTABLE} -m cython) execute_process(COMMAND ${CYTHON_EXECUTABLE} -V ERROR_VARIABLE CYTHON_OUTPUT RESULT_VARIABLE CYTHON_STATUS OUTPUT_QUIET) if(CYTHON_STATUS EQUAL 0) diff --git a/cmake/FindNumPy.cmake b/cmake/FindNumPy.cmake deleted file mode 100644 index 97259211415..00000000000 --- a/cmake/FindNumPy.cmake +++ /dev/null @@ -1,44 +0,0 @@ -unset(NUMPY_VERSION) -unset(NUMPY_INCLUDE_DIR) - -if(PYTHONINTERP_FOUND) - execute_process( - COMMAND "${PYTHON_EXECUTABLE}" "-c" - "import numpy as n; print(n.__version__); print(n.get_include());" - RESULT_VARIABLE __result OUTPUT_VARIABLE __output - OUTPUT_STRIP_TRAILING_WHITESPACE) - - if(__result MATCHES 0) - string(REGEX REPLACE ";" "\\\\;" __values ${__output}) - string(REGEX REPLACE "\r?\n" ";" __values ${__values}) - list(GET __values 0 NUMPY_VERSION) - list(GET __values 1 NUMPY_INCLUDE_DIR) - - string(REGEX MATCH "^([0-9])+\\.([0-9])+\\.([0-9])+" __ver_check - "${NUMPY_VERSION}") - if(NOT "${__ver_check}" STREQUAL "") - set(NUMPY_VERSION_MAJOR ${CMAKE_MATCH_1}) - set(NUMPY_VERSION_MINOR ${CMAKE_MATCH_2}) - set(NUMPY_VERSION_PATCH ${CMAKE_MATCH_3}) - math( - EXPR - NUMPY_VERSION_DECIMAL - "(${NUMPY_VERSION_MAJOR} * 10000) + (${NUMPY_VERSION_MINOR} * 100) + ${NUMPY_VERSION_PATCH}" - ) - string(REGEX REPLACE "\\\\" "/" NUMPY_INCLUDE_DIR ${NUMPY_INCLUDE_DIR}) - else() - unset(NUMPY_VERSION) - unset(NUMPY_INCLUDE_DIR) - message( - STATUS - "Requested NumPy version and include path, but got instead:\n${__output}\n" - ) - endif() - endif() -else() - message(STATUS "To find NumPy, the Python interpreter must be found first.") -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(NumPy REQUIRED_VARS NUMPY_INCLUDE_DIR - NUMPY_VERSION VERSION_VAR NUMPY_VERSION) diff --git a/cmake/FindPythonHeaders.cmake b/cmake/FindPythonHeaders.cmake deleted file mode 100644 index 3e5f5700254..00000000000 --- a/cmake/FindPythonHeaders.cmake +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (C) 2020 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 Python C++ headers -execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c - "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 sysconfig; print(sysconfig.get_path('purelib', vars={'base': ''}).lstrip('/'))" - OUTPUT_VARIABLE PYTHON_INSTDIR OUTPUT_STRIP_TRAILING_WHITESPACE) -endif(NOT PYTHON_INSTDIR) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(PythonHeaders REQUIRED_VARS - PYTHON_INCLUDE_DIRS PYTHON_INSTDIR) diff --git a/cmake/FindSphinx.cmake b/cmake/FindSphinx.cmake index 353ead0beea..8317e43d8db 100644 --- a/cmake/FindSphinx.cmake +++ b/cmake/FindSphinx.cmake @@ -1,6 +1,6 @@ include(FindPackageHandleStandardArgs) -set(SPHINX_EXECUTABLE ${PYTHON_EXECUTABLE} -m sphinx) +set(SPHINX_EXECUTABLE ${Python_EXECUTABLE} -m sphinx) execute_process( COMMAND ${SPHINX_EXECUTABLE} --version OUTPUT_VARIABLE QUERY_VERSION_OUT @@ -19,9 +19,9 @@ if(NOT QUERY_VERSION_RESULT) string(REGEX MATCH "[0-9]+\.[0-9.]+" SPHINX_VERSION "${QUERY_VERSION}") if("${SPHINX_VERSION}" VERSION_LESS "1.7") - set(SPHINX_API_DOC_EXE ${PYTHON_EXECUTABLE} -m sphinx.apidoc) + set(SPHINX_API_DOC_EXE ${Python_EXECUTABLE} -m sphinx.apidoc) else() - set(SPHINX_API_DOC_EXE ${PYTHON_EXECUTABLE} -m sphinx.ext.apidoc) + set(SPHINX_API_DOC_EXE ${Python_EXECUTABLE} -m sphinx.ext.apidoc) endif() endif() diff --git a/doc/doxygen/CMakeLists.txt b/doc/doxygen/CMakeLists.txt index 6fa5ad127cf..7773ae536a6 100644 --- a/doc/doxygen/CMakeLists.txt +++ b/doc/doxygen/CMakeLists.txt @@ -21,7 +21,7 @@ find_package(Doxygen) if(DOXYGEN_FOUND) add_custom_command( OUTPUT doxy-features - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/gen_doxyconfig.py + COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/gen_doxyconfig.py ${CMAKE_SOURCE_DIR}/src/config doxy-features DEPENDS Espresso::config) diff --git a/doc/sphinx/CMakeLists.txt b/doc/sphinx/CMakeLists.txt index 5d60edaee58..02347b5bc46 100644 --- a/doc/sphinx/CMakeLists.txt +++ b/doc/sphinx/CMakeLists.txt @@ -84,7 +84,7 @@ if(SPHINX_FOUND) add_custom_target( sphinx - ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/samples.py" + ${Python_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/samples.py" COMMAND ${SPHINX_API_DOC_EXE} -f -o ${CMAKE_CURRENT_BINARY_DIR} ${SPHINX_PYTHON_DIR} ${EXCLUDE} COMMAND diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2b80d674d76..eb47e9409cd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,7 +39,7 @@ if(SCAFACOS) add_subdirectory(scafacos) endif(SCAFACOS) -if(WITH_SCRIPT_INTERFACE) +if(WITH_PYTHON) add_subdirectory(script_interface) endif() diff --git a/src/config/CMakeLists.txt b/src/config/CMakeLists.txt index cb2feefb1af..d4c337a07c0 100644 --- a/src/config/CMakeLists.txt +++ b/src/config/CMakeLists.txt @@ -4,7 +4,7 @@ add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/config-features.hpp ${CMAKE_CURRENT_BINARY_DIR}/config-features.cpp COMMAND - ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/gen_featureconfig.py + ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/gen_featureconfig.py ${CMAKE_CURRENT_SOURCE_DIR}/features.def ${CMAKE_CURRENT_BINARY_DIR}/config-features.hpp ${CMAKE_CURRENT_BINARY_DIR}/config-features.cpp @@ -18,13 +18,13 @@ add_custom_target( add_custom_target( check_myconfig COMMAND - ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/check_myconfig.py + ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/check_myconfig.py ${CMAKE_CXX_COMPILER} ${CMAKE_CURRENT_SOURCE_DIR}/features.def ${CMAKE_CURRENT_BINARY_DIR}/myconfig-final.hpp cmake_config.hpp DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/features.def myconfig) execute_process( - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/gen_sampleconfig.py + COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/gen_sampleconfig.py ${CMAKE_CURRENT_SOURCE_DIR}/features.def OUTPUT_FILE ${CMAKE_BINARY_DIR}/myconfig-sample.hpp) @@ -33,7 +33,7 @@ add_library(Espresso::config ALIAS Espresso_config) add_dependencies(Espresso_config myconfig check_myconfig generate_config_features) install(TARGETS Espresso_config - LIBRARY DESTINATION ${PYTHON_INSTDIR}/espressomd) + LIBRARY DESTINATION ${ESPRESSO_INSTALL_PYTHON}/espressomd) target_include_directories(Espresso_config PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index fdb39215d89..3c601a5c882 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -93,10 +93,11 @@ if(CUDA) ${CMAKE_CURRENT_SOURCE_DIR}/magnetostatics) target_link_libraries(Espresso_core PUBLIC Espresso::cuda) install(TARGETS Espresso_cuda - LIBRARY DESTINATION ${PYTHON_INSTDIR}/espressomd) + LIBRARY DESTINATION ${ESPRESSO_INSTALL_PYTHON}/espressomd) endif() -install(TARGETS Espresso_core LIBRARY DESTINATION ${PYTHON_INSTDIR}/espressomd) +install(TARGETS Espresso_core + LIBRARY DESTINATION ${ESPRESSO_INSTALL_PYTHON}/espressomd) target_link_libraries( Espresso_core PRIVATE Espresso::config Espresso::utils::mpi Espresso::shapes diff --git a/src/particle_observables/CMakeLists.txt b/src/particle_observables/CMakeLists.txt index b68b46876a5..70b961dc7c4 100644 --- a/src/particle_observables/CMakeLists.txt +++ b/src/particle_observables/CMakeLists.txt @@ -6,7 +6,7 @@ target_include_directories( $) install(TARGETS Espresso_particle_observables - LIBRARY DESTINATION ${PYTHON_INSTDIR}/espressomd) + LIBRARY DESTINATION ${ESPRESSO_INSTALL_PYTHON}/espressomd) if(WITH_TESTS) add_subdirectory(tests) diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 170f14123bc..fc47951f55e 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -1,7 +1,7 @@ # Configure pypresso for build dir set(PYTHON_DIR ${CMAKE_CURRENT_BINARY_DIR}) -set(PYTHON_FRONTEND ${PYTHON_EXECUTABLE}) +set(PYTHON_FRONTEND ${Python_EXECUTABLE}) if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") execute_process( COMMAND "${PYTHON_FRONTEND}" "-c" @@ -21,9 +21,9 @@ if(IPYTHON_EXECUTABLE) endif() # Configure pypresso for install dir -set(PYTHON_DIR ${CMAKE_INSTALL_PREFIX}/${PYTHON_INSTDIR}) +set(PYTHON_DIR ${ESPRESSO_INSTALL_PYTHON}) -set(PYTHON_FRONTEND ${PYTHON_EXECUTABLE}) +set(PYTHON_FRONTEND ${Python_EXECUTABLE}) configure_file(pypresso.cmakein ${CMAKE_CURRENT_BINARY_DIR}/pypresso @ONLY) if(IPYTHON_EXECUTABLE) diff --git a/src/python/espressomd/CMakeLists.txt b/src/python/espressomd/CMakeLists.txt index d92787b9491..d5433dd43f7 100644 --- a/src/python/espressomd/CMakeLists.txt +++ b/src/python/espressomd/CMakeLists.txt @@ -20,7 +20,7 @@ add_custom_command( OUTPUT gen_pxiconfig.cpp COMMAND - ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/gen_pxiconfig.py + ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/gen_pxiconfig.py ${CMAKE_SOURCE_DIR}/src/config/features.def ${CMAKE_CURRENT_BINARY_DIR}/gen_pxiconfig.cpp DEPENDS ${CMAKE_SOURCE_DIR}/src/config/features.def) @@ -111,10 +111,12 @@ foreach(cython_file ${cython_SRC}) Espresso::script_interface) target_link_libraries(${target} PRIVATE Espresso::cpp_flags) target_link_libraries(${target} PRIVATE Espresso::pyx_flags) - target_include_directories(${target} SYSTEM PRIVATE ${PYTHON_INCLUDE_DIRS} - ${NUMPY_INCLUDE_DIR}) + target_include_directories( + ${target} SYSTEM PRIVATE ${Python_INCLUDE_DIRS} + ${Python_NumPy_INCLUDE_DIRS}) add_dependencies(espressomd ${target}) - install(TARGETS ${target} LIBRARY DESTINATION ${PYTHON_INSTDIR}/espressomd) + install(TARGETS ${target} + LIBRARY DESTINATION ${ESPRESSO_INSTALL_PYTHON}/espressomd) endif() endforeach() @@ -132,5 +134,6 @@ foreach(auxfile ${cython_AUX}) endforeach(auxfile) # Install Python files -install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DESTINATION ${PYTHON_INSTDIR} - FILES_MATCHING PATTERN "*.py" PATTERN "CMakeFiles" EXCLUDE) +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DESTINATION ${ESPRESSO_INSTALL_PYTHON} FILES_MATCHING PATTERN "*.py" + PATTERN "CMakeFiles" EXCLUDE) diff --git a/src/python/pypresso.cmakein b/src/python/pypresso.cmakein index aab95c89e8f..f874fe14974 100755 --- a/src/python/pypresso.cmakein +++ b/src/python/pypresso.cmakein @@ -53,7 +53,7 @@ fi case "$1" in --gdb) shift - [ "@PYTHON_FRONTEND@" = "@IPYTHON_EXECUTABLE@" ] && exec gdb -ex "set print thread-events off" -ex "set exec-wrapper sh -c 'exec \"@IPYTHON_EXECUTABLE@\" \"\$@\"'" --args "@PYTHON_EXECUTABLE@" "$@" + [ "@PYTHON_FRONTEND@" = "@IPYTHON_EXECUTABLE@" ] && exec gdb -ex "set print thread-events off" -ex "set exec-wrapper sh -c 'exec \"@IPYTHON_EXECUTABLE@\" \"\$@\"'" --args "@Python_EXECUTABLE@" "$@" exec gdb --args "@PYTHON_FRONTEND@" "$@" ;; --lldb) @@ -62,7 +62,7 @@ case "$1" in ;; --coverage) shift - exec @PYTHON_EXECUTABLE@ -m coverage run --source=@CMAKE_BINARY_DIR@/testsuite/ --rcfile="@PROJECT_SOURCE_DIR@/.coveragerc" "$@" + exec @Python_EXECUTABLE@ -m coverage run --source=@CMAKE_BINARY_DIR@/testsuite/ --rcfile="@PROJECT_SOURCE_DIR@/.coveragerc" "$@" ;; --valgrind) shift @@ -79,7 +79,7 @@ case "$1" in --gdb=*) options="${1#*=}" shift - [ "@PYTHON_FRONTEND@" = "@IPYTHON_EXECUTABLE@" ] && exec gdb -ex "set print thread-events off" -ex "set exec-wrapper sh -c 'exec \"@IPYTHON_EXECUTABLE@\" \"\$@\"'" ${options} --args "@PYTHON_EXECUTABLE@" "$@" + [ "@PYTHON_FRONTEND@" = "@IPYTHON_EXECUTABLE@" ] && exec gdb -ex "set print thread-events off" -ex "set exec-wrapper sh -c 'exec \"@IPYTHON_EXECUTABLE@\" \"\$@\"'" ${options} --args "@Python_EXECUTABLE@" "$@" exec gdb ${options} --args "@PYTHON_FRONTEND@" "$@" ;; --lldb=*) diff --git a/src/scafacos/CMakeLists.txt b/src/scafacos/CMakeLists.txt index c0b83394258..d88d07891f8 100644 --- a/src/scafacos/CMakeLists.txt +++ b/src/scafacos/CMakeLists.txt @@ -16,4 +16,5 @@ target_include_directories( target_include_directories(Espresso_scafacos SYSTEM PUBLIC ${SCAFACOS_INCLUDE_DIRS}) -install(TARGETS Espresso_scafacos DESTINATION ${PYTHON_INSTDIR}/espressomd) +install(TARGETS Espresso_scafacos + DESTINATION ${ESPRESSO_INSTALL_PYTHON}/espressomd) diff --git a/src/script_interface/CMakeLists.txt b/src/script_interface/CMakeLists.txt index 08bf00c3138..7756688bfc1 100644 --- a/src/script_interface/CMakeLists.txt +++ b/src/script_interface/CMakeLists.txt @@ -50,7 +50,7 @@ add_subdirectory(system) add_subdirectory(virtual_sites) install(TARGETS Espresso_script_interface - LIBRARY DESTINATION ${PYTHON_INSTDIR}/espressomd) + LIBRARY DESTINATION ${ESPRESSO_INSTALL_PYTHON}/espressomd) target_link_libraries( Espresso_script_interface PRIVATE Espresso::config Espresso::core diff --git a/src/shapes/CMakeLists.txt b/src/shapes/CMakeLists.txt index 22d37048e4a..9048c20c920 100644 --- a/src/shapes/CMakeLists.txt +++ b/src/shapes/CMakeLists.txt @@ -12,7 +12,7 @@ target_include_directories( $) install(TARGETS Espresso_shapes - LIBRARY DESTINATION ${PYTHON_INSTDIR}/espressomd) + LIBRARY DESTINATION ${ESPRESSO_INSTALL_PYTHON}/espressomd) if(WITH_TESTS) add_subdirectory(unit_tests) diff --git a/testsuite/cmake/CMakeLists.txt b/testsuite/cmake/CMakeLists.txt index 3cbd1b1be9f..7535e71c043 100644 --- a/testsuite/cmake/CMakeLists.txt +++ b/testsuite/cmake/CMakeLists.txt @@ -12,7 +12,7 @@ function(CMAKE_TEST) set(cmake_tests ${cmake_tests} ${TEST_FILE} PARENT_SCOPE) endfunction(CMAKE_TEST) -set(PYTHON_DIR ${CMAKE_INSTALL_PREFIX}/${PYTHON_INSTDIR}) +set(PYTHON_DIR ${CMAKE_INSTALL_PREFIX}/${Python_STDARCH}) cmake_test(FILE test_install.sh DEPENDENCIES BashUnitTests.sh) diff --git a/testsuite/cmake/test_install.sh b/testsuite/cmake/test_install.sh index 00b1ad4c747..163b782093c 100755 --- a/testsuite/cmake/test_install.sh +++ b/testsuite/cmake/test_install.sh @@ -22,17 +22,17 @@ source BashUnitTests.sh # test installation and Python bindings function test_install() { # check Python files were installed in espressomd - local -r filepaths=("@CMAKE_INSTALL_FULL_BINDIR@/pypresso" \ - "@PYTHON_DIR@/espressomd/Espresso_core.so" \ - "@PYTHON_DIR@/espressomd/_init.so" \ - "@PYTHON_DIR@/espressomd/__init__.py" + local -r filepaths=("@ESPRESSO_INSTALL_BINDIR@/pypresso" \ + "@ESPRESSO_INSTALL_PYTHON@/espressomd/Espresso_core.so" \ + "@ESPRESSO_INSTALL_PYTHON@/espressomd/_init.so" \ + "@ESPRESSO_INSTALL_PYTHON@/espressomd/__init__.py" ) for filepath in "${filepaths[@]}"; do assert_file_exists "${filepath}" done # check no Python file was installed outside espressomd - paths=$(find "@CMAKE_INSTALL_PREFIX@" -path "@PYTHON_DIR@/espressomd" -prune -o \( -name '*.py' -o -name '*.so' \) -print) + paths=$(find "@CMAKE_INSTALL_PREFIX@" -path "@ESPRESSO_INSTALL_PYTHON@/espressomd" -prune -o \( -name '*.py' -o -name '*.so' \) -print) count=$(echo "${paths}" | wc -l) assert_string_equal "${paths}" "" "${count} files were installed in the wrong directories:"$'\n'"${paths}" From 16c695e93e523be8bf181f025221b52c30d7549a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 9 Aug 2022 14:29:37 +0200 Subject: [PATCH 28/85] CMake: Rename C++ targets --- CMakeLists.txt | 36 +++--- cmake/FindCUDACompilerClang.cmake | 14 +-- cmake/FindCUDACompilerNVCC.cmake | 8 +- cmake/unit_test.cmake | 2 +- doc/doxygen/CMakeLists.txt | 2 +- doc/sphinx/installation.rst | 4 +- src/config/CMakeLists.txt | 12 +- src/core/CMakeLists.txt | 30 ++--- src/core/accumulators/CMakeLists.txt | 2 +- src/core/analysis/CMakeLists.txt | 2 +- src/core/bond_breakage/CMakeLists.txt | 2 +- src/core/bonded_interactions/CMakeLists.txt | 2 +- src/core/cell_system/CMakeLists.txt | 2 +- src/core/cluster_analysis/CMakeLists.txt | 4 +- src/core/constraints/CMakeLists.txt | 2 +- src/core/electrostatics/CMakeLists.txt | 2 +- src/core/error_handling/CMakeLists.txt | 2 +- src/core/galilei/CMakeLists.txt | 2 +- src/core/grid_based_algorithms/CMakeLists.txt | 2 +- src/core/immersed_boundary/CMakeLists.txt | 2 +- src/core/integrators/CMakeLists.txt | 2 +- src/core/io/mpiio/CMakeLists.txt | 2 +- src/core/io/writer/CMakeLists.txt | 8 +- src/core/magnetostatics/CMakeLists.txt | 2 +- .../nonbonded_interactions/CMakeLists.txt | 2 +- src/core/object-in-fluid/CMakeLists.txt | 2 +- src/core/observables/CMakeLists.txt | 2 +- src/core/p3m/CMakeLists.txt | 4 +- src/core/reaction_methods/CMakeLists.txt | 2 +- .../reaction_methods/tests/CMakeLists.txt | 12 +- src/core/scafacos/CMakeLists.txt | 4 +- src/core/stokesian_dynamics/CMakeLists.txt | 8 +- src/core/unit_tests/CMakeLists.txt | 56 ++++----- src/core/virtual_sites/CMakeLists.txt | 2 +- src/particle_observables/CMakeLists.txt | 8 +- src/particle_observables/tests/CMakeLists.txt | 6 +- src/profiler/CMakeLists.txt | 10 +- src/python/espressomd/CMakeLists.txt | 18 +-- src/scafacos/CMakeLists.txt | 16 +-- src/script_interface/CMakeLists.txt | 14 +-- .../accumulators/CMakeLists.txt | 2 +- src/script_interface/analysis/CMakeLists.txt | 2 +- .../bond_breakage/CMakeLists.txt | 2 +- .../cell_system/CMakeLists.txt | 2 +- .../cluster_analysis/CMakeLists.txt | 2 +- src/script_interface/code_info/CMakeLists.txt | 2 +- .../collision_detection/CMakeLists.txt | 2 +- .../constraints/CMakeLists.txt | 2 +- .../electrostatics/CMakeLists.txt | 2 +- src/script_interface/galilei/CMakeLists.txt | 2 +- src/script_interface/h5md/CMakeLists.txt | 2 +- .../interactions/CMakeLists.txt | 2 +- .../lbboundaries/CMakeLists.txt | 2 +- .../lees_edwards/CMakeLists.txt | 2 +- .../magnetostatics/CMakeLists.txt | 2 +- src/script_interface/math/CMakeLists.txt | 2 +- src/script_interface/mpiio/CMakeLists.txt | 2 +- .../observables/CMakeLists.txt | 2 +- .../pair_criteria/CMakeLists.txt | 2 +- .../particle_data/CMakeLists.txt | 2 +- .../reaction_methods/CMakeLists.txt | 2 +- src/script_interface/scafacos/CMakeLists.txt | 2 +- src/script_interface/shapes/CMakeLists.txt | 2 +- src/script_interface/system/CMakeLists.txt | 2 +- src/script_interface/tests/CMakeLists.txt | 36 +++--- .../virtual_sites/CMakeLists.txt | 2 +- src/shapes/CMakeLists.txt | 12 +- src/shapes/unit_tests/CMakeLists.txt | 22 ++-- src/utils/CMakeLists.txt | 14 +-- src/utils/tests/CMakeLists.txt | 112 +++++++++--------- testsuite/cmake/test_install.sh | 2 +- 71 files changed, 281 insertions(+), 281 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8348a3f74e8..f960d948b51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -367,25 +367,25 @@ set(CMAKE_INSTALL_RPATH "${ESPRESSO_INSTALL_LIBDIR}") # drop 'lib' prefix from all libraries set(CMAKE_SHARED_LIBRARY_PREFIX "") -add_library(Espresso_coverage_flags INTERFACE) -add_library(Espresso::coverage_flags ALIAS Espresso_coverage_flags) +add_library(espresso_coverage_flags INTERFACE) +add_library(espresso::coverage_flags ALIAS espresso_coverage_flags) if(WITH_COVERAGE) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") target_compile_options( - Espresso_coverage_flags INTERFACE -g -fprofile-instr-generate + espresso_coverage_flags INTERFACE -g -fprofile-instr-generate -fcoverage-mapping) else() target_compile_options( - Espresso_coverage_flags INTERFACE -g --coverage -fprofile-arcs + espresso_coverage_flags INTERFACE -g --coverage -fprofile-arcs -ftest-coverage) - target_link_libraries(Espresso_coverage_flags INTERFACE gcov) + target_link_libraries(espresso_coverage_flags INTERFACE gcov) endif() endif() -add_library(Espresso_cpp_flags INTERFACE) -add_library(Espresso::cpp_flags ALIAS Espresso_cpp_flags) +add_library(espresso_cpp_flags INTERFACE) +add_library(espresso::cpp_flags ALIAS espresso_cpp_flags) target_compile_options( - Espresso_cpp_flags + espresso_cpp_flags INTERFACE -Wall -Wextra @@ -411,18 +411,18 @@ target_compile_options( # disable warning from -Wextra on ARM processors if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_PROCESSOR MATCHES "arm") - target_compile_options(Espresso_cpp_flags INTERFACE -Wno-psabi) + target_compile_options(espresso_cpp_flags INTERFACE -Wno-psabi) endif() # prevent 80-bit arithmetic on old Intel processors if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SIZEOF_VOID_P EQUAL 4 AND CMAKE_SYSTEM_PROCESSOR MATCHES "[xX]86") - target_compile_options(Espresso_cpp_flags INTERFACE -ffloat-store) + target_compile_options(espresso_cpp_flags INTERFACE -ffloat-store) endif() # enable boost::variant with more than 20 types target_compile_options( - Espresso_cpp_flags INTERFACE -DBOOST_MPL_CFG_NO_PREPROCESSED_HEADERS + espresso_cpp_flags INTERFACE -DBOOST_MPL_CFG_NO_PREPROCESSED_HEADERS -DBOOST_MPL_LIMIT_LIST_SIZE=30) set(CMAKE_MACOSX_RPATH TRUE) @@ -438,22 +438,22 @@ if(WITH_ASAN AND WITH_MSAN) endif() if(WITH_ASAN) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g -O1") - target_compile_options(Espresso_cpp_flags INTERFACE -fsanitize=address + target_compile_options(espresso_cpp_flags INTERFACE -fsanitize=address -fno-omit-frame-pointer) - target_link_libraries(Espresso_cpp_flags INTERFACE -fsanitize=address) + target_link_libraries(espresso_cpp_flags INTERFACE -fsanitize=address) endif() if(WITH_MSAN) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g -O1") - target_compile_options(Espresso_cpp_flags INTERFACE -fsanitize=memory + target_compile_options(espresso_cpp_flags INTERFACE -fsanitize=memory -fno-omit-frame-pointer) - target_link_libraries(Espresso_cpp_flags INTERFACE -fsanitize=memory) + target_link_libraries(espresso_cpp_flags INTERFACE -fsanitize=memory) endif() if(WITH_UBSAN) - target_compile_options(Espresso_cpp_flags INTERFACE -fsanitize=undefined) - target_link_libraries(Espresso_cpp_flags INTERFACE -fsanitize=undefined) + target_compile_options(espresso_cpp_flags INTERFACE -fsanitize=undefined) + target_link_libraries(espresso_cpp_flags INTERFACE -fsanitize=undefined) endif() -target_link_libraries(Espresso_cpp_flags INTERFACE Espresso::coverage_flags) +target_link_libraries(espresso_cpp_flags INTERFACE espresso::coverage_flags) # # Static analysis diff --git a/cmake/FindCUDACompilerClang.cmake b/cmake/FindCUDACompilerClang.cmake index 1a24bacf9fa..dff808ff2d6 100644 --- a/cmake/FindCUDACompilerClang.cmake +++ b/cmake/FindCUDACompilerClang.cmake @@ -28,8 +28,8 @@ if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") ) endif() -add_library(Espresso_cuda_flags INTERFACE) -add_library(Espresso::cuda_flags ALIAS Espresso_cuda_flags) +add_library(espresso_cuda_flags INTERFACE) +add_library(espresso::cuda_flags ALIAS espresso_cuda_flags) function(detect_clang_cuda_path) execute_process(COMMAND ${CMAKE_CUDA_COMPILER} ${CMAKE_CXX_FLAGS} --verbose @@ -47,7 +47,7 @@ function(detect_clang_cuda_path) if(CLANG_VERBOSE_OUTPUT MATCHES "Found CUDA installation") set(CLANG_VERBOSE_OUTPUT ${CLANG_VERBOSE_OUTPUT} PARENT_SCOPE) message(STATUS "Clang did not automatically detect a compatible CUDA library; adding compiler flag --cuda-path=${unix_cuda_path}") - target_compile_options(Espresso_cuda_flags INTERFACE "--cuda-path=${unix_cuda_path}") + target_compile_options(espresso_cuda_flags INTERFACE "--cuda-path=${unix_cuda_path}") return() endif() endif() @@ -66,7 +66,7 @@ string(REGEX REPLACE "^.*Found CUDA installation: .* version ([0-9\.]+|unknown). message(STATUS "Found CUDA-capable host compiler: ${CMAKE_CUDA_COMPILER}") if(NOT CLANG_VERBOSE_OUTPUT MATCHES "Found CUDA installation" OR CUDA_VERSION STREQUAL "unknown") message(STATUS "Clang did not automatically detect a compatible CUDA library; adding compiler flag -Wno-unknown-cuda-version") - target_compile_options(Espresso_cuda_flags INTERFACE -Wno-unknown-cuda-version) + target_compile_options(espresso_cuda_flags INTERFACE -Wno-unknown-cuda-version) message(STATUS "Found CUDA version: ${CUDAToolkit_VERSION}") message(STATUS "Found CUDA installation: ${CUDAToolkit_LIBRARY_DIR}") else() @@ -75,7 +75,7 @@ else() CUDA_VERSION VERSION_GREATER_EQUAL "11.0" AND CUDA_VERSION VERSION_LESS "12.0") message(STATUS "Clang ${CMAKE_CXX_COMPILER_VERSION} doesn't natively support CUDA ${CUDAToolkit_VERSION_MAJOR}.${CUDAToolkit_VERSION_MINOR}; adding compiler flag -Wno-unknown-cuda-version") - target_compile_options(Espresso_cuda_flags INTERFACE -Wno-unknown-cuda-version) + target_compile_options(espresso_cuda_flags INTERFACE -Wno-unknown-cuda-version) endif() message(STATUS "Found CUDA version: ${CUDAToolkit_VERSION} (recognized by Clang as ${CUDA_VERSION})") message(STATUS "Found CUDA installation: ${CUDA_DIR}") @@ -84,7 +84,7 @@ set(CUDA_VERSION ${CUDAToolkit_VERSION}) set(CUDA 1) target_compile_options( - Espresso_cuda_flags + espresso_cuda_flags INTERFACE $<$:-g> $<$:-O3 -DNDEBUG> @@ -107,7 +107,7 @@ function(add_gpu_library) set_source_files_properties(${TARGET_SOURCES} PROPERTIES LANGUAGE "CXX") add_library(${ARGV}) set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE "CXX") - target_link_libraries(${TARGET_NAME} PRIVATE Espresso::cuda_flags) + target_link_libraries(${TARGET_NAME} PRIVATE espresso::cuda_flags) endfunction() include(FindPackageHandleStandardArgs) diff --git a/cmake/FindCUDACompilerNVCC.cmake b/cmake/FindCUDACompilerNVCC.cmake index 9de20af6a5f..99cabb5abfa 100644 --- a/cmake/FindCUDACompilerNVCC.cmake +++ b/cmake/FindCUDACompilerNVCC.cmake @@ -47,10 +47,10 @@ set(CUDA_LINK_LIBRARIES_KEYWORD PUBLIC) set(CUDA_PROPAGATE_HOST_FLAGS OFF) -add_library(Espresso_cuda_flags INTERFACE) -add_library(Espresso::cuda_flags ALIAS Espresso_cuda_flags) +add_library(espresso_cuda_flags INTERFACE) +add_library(espresso::cuda_flags ALIAS espresso_cuda_flags) target_compile_options( - Espresso_cuda_flags + espresso_cuda_flags INTERFACE $<$:> $<$:-Xptxas=-O3 -Xcompiler=-O3 -DNDEBUG> @@ -67,7 +67,7 @@ function(add_gpu_library) add_library(${ARGV}) set(GPU_TARGET_NAME ${ARGV0}) set_property(TARGET ${GPU_TARGET_NAME} PROPERTY CUDA_SEPARABLE_COMPILATION ON) - target_link_libraries(${GPU_TARGET_NAME} PRIVATE Espresso::cuda_flags) + target_link_libraries(${GPU_TARGET_NAME} PRIVATE espresso::cuda_flags) list(APPEND cuda_archs 52) if(CMAKE_CUDA_COMPILER_VERSION LESS 11) list(APPEND cuda_archs 30) diff --git a/cmake/unit_test.cmake b/cmake/unit_test.cmake index 3e08b23e346..41c7577354a 100644 --- a/cmake/unit_test.cmake +++ b/cmake/unit_test.cmake @@ -9,7 +9,7 @@ function(UNIT_TEST) target_link_libraries(${TEST_NAME} PRIVATE ${TEST_DEPENDS}) endif() target_include_directories(${TEST_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src/core) - target_link_libraries(${TEST_NAME} PRIVATE Espresso::config Espresso::cpp_flags) + target_link_libraries(${TEST_NAME} PRIVATE espresso::config espresso::cpp_flags) # If NUM_PROC is given, set up MPI parallel test case if(TEST_NUM_PROC) diff --git a/doc/doxygen/CMakeLists.txt b/doc/doxygen/CMakeLists.txt index 7773ae536a6..2318cef9b73 100644 --- a/doc/doxygen/CMakeLists.txt +++ b/doc/doxygen/CMakeLists.txt @@ -23,7 +23,7 @@ if(DOXYGEN_FOUND) OUTPUT doxy-features COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/gen_doxyconfig.py ${CMAKE_SOURCE_DIR}/src/config doxy-features - DEPENDS Espresso::config) + DEPENDS espresso::config) set(DOXYGEN_BIB_IN ${CMAKE_SOURCE_DIR}/doc/bibliography.bib) set(DOXYGEN_BIB_OUT ${CMAKE_CURRENT_BINARY_DIR}/bibliography.bib) diff --git a/doc/sphinx/installation.rst b/doc/sphinx/installation.rst index b998e3e59df..6252a578bd7 100644 --- a/doc/sphinx/installation.rst +++ b/doc/sphinx/installation.rst @@ -779,12 +779,12 @@ The build type is controlled by ``-D CMAKE_BUILD_TYPE=`` where * ``Coverage``: for code coverage Cluster users and HPC developers may be interested in manually editing the -``Espresso_cpp_flags`` target in the top-level ``CMakeLists.txt`` file for +``espresso_cpp_flags`` target in the top-level ``CMakeLists.txt`` file for finer control over compiler flags. The variable declaration is followed by a series of conditionals to enable or disable compiler-specific flags. Compiler flags passed to CMake via the ``-DCMAKE_CXX_FLAGS`` option (such as ``cmake . -DCMAKE_CXX_FLAGS="-ffast-math -fno-finite-math-only"``) -will appear in the compiler command before the flags in ``Espresso_cpp_flags``, +will appear in the compiler command before the flags in ``espresso_cpp_flags``, and will therefore have lower precedence. Be aware that fast-math mode can break |es|. It is incompatible with the diff --git a/src/config/CMakeLists.txt b/src/config/CMakeLists.txt index d4c337a07c0..e48cf2390ec 100644 --- a/src/config/CMakeLists.txt +++ b/src/config/CMakeLists.txt @@ -28,13 +28,13 @@ execute_process( ${CMAKE_CURRENT_SOURCE_DIR}/features.def OUTPUT_FILE ${CMAKE_BINARY_DIR}/myconfig-sample.hpp) -add_library(Espresso_config SHARED config-features.cpp) -add_library(Espresso::config ALIAS Espresso_config) -add_dependencies(Espresso_config myconfig check_myconfig +add_library(espresso_config SHARED config-features.cpp) +add_library(espresso::config ALIAS espresso_config) +add_dependencies(espresso_config myconfig check_myconfig generate_config_features) -install(TARGETS Espresso_config +install(TARGETS espresso_config LIBRARY DESTINATION ${ESPRESSO_INSTALL_PYTHON}/espressomd) -target_include_directories(Espresso_config PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} +target_include_directories(espresso_config PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) find_package(Git) @@ -49,4 +49,4 @@ add_custom_target( ${PROJECT_SOURCE_DIR}/cmake/version.cmake) set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES version.hpp version.hpp.tmp) -add_dependencies(Espresso_config version) +add_dependencies(espresso_config version) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3c601a5c882..9426568be13 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -20,7 +20,7 @@ # add_library( - Espresso_core SHARED + espresso_core SHARED accumulators.cpp bond_error.cpp cells.cpp @@ -56,16 +56,16 @@ add_library( exclusions.cpp PartCfg.cpp EspressoSystemStandAlone.cpp) -add_library(Espresso::core ALIAS Espresso_core) +add_library(espresso::core ALIAS espresso_core) if(CUDA) target_sources( - Espresso_core + espresso_core PRIVATE cuda_init.cpp cuda_interface.cpp grid_based_algorithms/electrokinetics.cpp grid_based_algorithms/lbgpu.cpp) add_gpu_library( - Espresso_cuda + espresso_cuda SHARED cuda_common_cuda.cu cuda_init_cuda.cu @@ -80,32 +80,32 @@ if(CUDA) grid_based_algorithms/lbgpu_cuda.cu grid_based_algorithms/fd-electrostatics_cuda.cu virtual_sites/lb_inertialess_tracers_cuda.cu) - add_library(Espresso::cuda ALIAS Espresso_cuda) + add_library(espresso::cuda ALIAS espresso_cuda) target_link_libraries( - Espresso_cuda PRIVATE CUDA::cuda_driver CUDA::cudart CUDA::cufft - Espresso::config Espresso::utils Espresso::shapes) + espresso_cuda PRIVATE CUDA::cuda_driver CUDA::cudart CUDA::cufft + espresso::config espresso::utils espresso::shapes) target_include_directories( - Espresso_cuda + espresso_cuda PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/virtual_sites ${CMAKE_CURRENT_SOURCE_DIR}/grid_based_algorithms ${CMAKE_CURRENT_SOURCE_DIR}/electrostatics ${CMAKE_CURRENT_SOURCE_DIR}/magnetostatics) - target_link_libraries(Espresso_core PUBLIC Espresso::cuda) - install(TARGETS Espresso_cuda + target_link_libraries(espresso_core PUBLIC espresso::cuda) + install(TARGETS espresso_cuda LIBRARY DESTINATION ${ESPRESSO_INSTALL_PYTHON}/espressomd) endif() -install(TARGETS Espresso_core +install(TARGETS espresso_core LIBRARY DESTINATION ${ESPRESSO_INSTALL_PYTHON}/espressomd) target_link_libraries( - Espresso_core PRIVATE Espresso::config Espresso::utils::mpi Espresso::shapes - Espresso::profiler Espresso::cpp_flags - PUBLIC Espresso::utils MPI::MPI_CXX Random123 Espresso::particle_observables + espresso_core PRIVATE espresso::config espresso::utils::mpi espresso::shapes + espresso::profiler espresso::cpp_flags + PUBLIC espresso::utils MPI::MPI_CXX Random123 espresso::particle_observables Boost::serialization Boost::mpi) -target_include_directories(Espresso_core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(espresso_core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) add_subdirectory(accumulators) add_subdirectory(analysis) diff --git a/src/core/accumulators/CMakeLists.txt b/src/core/accumulators/CMakeLists.txt index 61799be4d7f..aa7ae804c53 100644 --- a/src/core/accumulators/CMakeLists.txt +++ b/src/core/accumulators/CMakeLists.txt @@ -1,5 +1,5 @@ target_sources( - Espresso_core + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Correlator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MeanVarianceCalculator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TimeSeries.cpp) diff --git a/src/core/analysis/CMakeLists.txt b/src/core/analysis/CMakeLists.txt index b4519a3e97a..4b6ee25351a 100644 --- a/src/core/analysis/CMakeLists.txt +++ b/src/core/analysis/CMakeLists.txt @@ -1,3 +1,3 @@ target_sources( - Espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/statistics.cpp + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/statistics.cpp ${CMAKE_CURRENT_SOURCE_DIR}/statistics_chain.cpp) diff --git a/src/core/bond_breakage/CMakeLists.txt b/src/core/bond_breakage/CMakeLists.txt index 58659571287..487cd85e105 100644 --- a/src/core/bond_breakage/CMakeLists.txt +++ b/src/core/bond_breakage/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_core +target_sources(espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/bond_breakage.cpp) diff --git a/src/core/bonded_interactions/CMakeLists.txt b/src/core/bonded_interactions/CMakeLists.txt index 92a22ffa958..87c9b422a41 100644 --- a/src/core/bonded_interactions/CMakeLists.txt +++ b/src/core/bonded_interactions/CMakeLists.txt @@ -1,5 +1,5 @@ target_sources( - Espresso_core + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/angle_cosine.cpp ${CMAKE_CURRENT_SOURCE_DIR}/angle_cossquare.cpp ${CMAKE_CURRENT_SOURCE_DIR}/bonded_interaction_data.cpp diff --git a/src/core/cell_system/CMakeLists.txt b/src/core/cell_system/CMakeLists.txt index 343f068ddc8..5077fc50d06 100644 --- a/src/core/cell_system/CMakeLists.txt +++ b/src/core/cell_system/CMakeLists.txt @@ -1,5 +1,5 @@ target_sources( - Espresso_core + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/AtomDecomposition.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CellStructure.cpp ${CMAKE_CURRENT_SOURCE_DIR}/HybridDecomposition.cpp diff --git a/src/core/cluster_analysis/CMakeLists.txt b/src/core/cluster_analysis/CMakeLists.txt index f576fa8c5b8..2db761f0f9a 100644 --- a/src/core/cluster_analysis/CMakeLists.txt +++ b/src/core/cluster_analysis/CMakeLists.txt @@ -1,7 +1,7 @@ target_sources( - Espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Cluster.cpp + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Cluster.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ClusterStructure.cpp) if(GSL) - target_link_libraries(Espresso_core PRIVATE GSL::gsl GSL::gslcblas) + target_link_libraries(espresso_core PRIVATE GSL::gsl GSL::gslcblas) endif() diff --git a/src/core/constraints/CMakeLists.txt b/src/core/constraints/CMakeLists.txt index 568456d4143..e2283e25a20 100644 --- a/src/core/constraints/CMakeLists.txt +++ b/src/core/constraints/CMakeLists.txt @@ -1,3 +1,3 @@ target_sources( - Espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/HomogeneousMagneticField.cpp + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/HomogeneousMagneticField.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ShapeBasedConstraint.cpp) diff --git a/src/core/electrostatics/CMakeLists.txt b/src/core/electrostatics/CMakeLists.txt index 818b11c0966..6ac70cecf30 100644 --- a/src/core/electrostatics/CMakeLists.txt +++ b/src/core/electrostatics/CMakeLists.txt @@ -17,7 +17,7 @@ # along with this program. If not, see . # target_sources( - Espresso_core + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/coulomb.cpp ${CMAKE_CURRENT_SOURCE_DIR}/elc.cpp ${CMAKE_CURRENT_SOURCE_DIR}/icc.cpp diff --git a/src/core/error_handling/CMakeLists.txt b/src/core/error_handling/CMakeLists.txt index 7d74728d0b2..6cc5c6a5315 100644 --- a/src/core/error_handling/CMakeLists.txt +++ b/src/core/error_handling/CMakeLists.txt @@ -17,7 +17,7 @@ # along with this program. If not, see . # target_sources( - Espresso_core + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/RuntimeErrorCollector.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RuntimeError.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RuntimeErrorStream.cpp) diff --git a/src/core/galilei/CMakeLists.txt b/src/core/galilei/CMakeLists.txt index 87285e22124..870772e503a 100644 --- a/src/core/galilei/CMakeLists.txt +++ b/src/core/galilei/CMakeLists.txt @@ -1 +1 @@ -target_sources(Espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Galilei.cpp) +target_sources(espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Galilei.cpp) diff --git a/src/core/grid_based_algorithms/CMakeLists.txt b/src/core/grid_based_algorithms/CMakeLists.txt index 28e4e4ad192..767fb758325 100644 --- a/src/core/grid_based_algorithms/CMakeLists.txt +++ b/src/core/grid_based_algorithms/CMakeLists.txt @@ -1,5 +1,5 @@ target_sources( - Espresso_core + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/halo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/lattice.cpp ${CMAKE_CURRENT_SOURCE_DIR}/lb_boundaries.cpp diff --git a/src/core/immersed_boundary/CMakeLists.txt b/src/core/immersed_boundary/CMakeLists.txt index 6c1dbbe4b20..f7bed58632e 100644 --- a/src/core/immersed_boundary/CMakeLists.txt +++ b/src/core/immersed_boundary/CMakeLists.txt @@ -1,5 +1,5 @@ target_sources( - Espresso_core + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ibm_tribend.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ibm_triel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ibm_volcons.cpp diff --git a/src/core/integrators/CMakeLists.txt b/src/core/integrators/CMakeLists.txt index 0d957a70f73..94d6c9f10b9 100644 --- a/src/core/integrators/CMakeLists.txt +++ b/src/core/integrators/CMakeLists.txt @@ -1,3 +1,3 @@ target_sources( - Espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/velocity_verlet_npt.cpp + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/velocity_verlet_npt.cpp ${CMAKE_CURRENT_SOURCE_DIR}/steepest_descent.cpp) diff --git a/src/core/io/mpiio/CMakeLists.txt b/src/core/io/mpiio/CMakeLists.txt index 7bdfa3f309c..e00b20a78c8 100644 --- a/src/core/io/mpiio/CMakeLists.txt +++ b/src/core/io/mpiio/CMakeLists.txt @@ -1 +1 @@ -target_sources(Espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/mpiio.cpp) +target_sources(espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/mpiio.cpp) diff --git a/src/core/io/writer/CMakeLists.txt b/src/core/io/writer/CMakeLists.txt index eb42087cb06..07cb71a5433 100644 --- a/src/core/io/writer/CMakeLists.txt +++ b/src/core/io/writer/CMakeLists.txt @@ -1,12 +1,12 @@ if(H5MD) - target_link_libraries(Espresso_core PUBLIC ${HDF5_LIBRARIES} + target_link_libraries(espresso_core PUBLIC ${HDF5_LIBRARIES} Boost::filesystem h5xx) - target_include_directories(Espresso_core PUBLIC ${CMAKE_CURRRENT_SOURCE_DIR} + target_include_directories(espresso_core PUBLIC ${CMAKE_CURRRENT_SOURCE_DIR} ${HDF5_INCLUDE_DIRS}) - target_compile_definitions(Espresso_core PUBLIC H5XX_USE_MPI) + target_compile_definitions(espresso_core PUBLIC H5XX_USE_MPI) target_sources( - Espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/h5md_core.cpp + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/h5md_core.cpp ${CMAKE_CURRENT_SOURCE_DIR}/h5md_specification.cpp) endif(H5MD) diff --git a/src/core/magnetostatics/CMakeLists.txt b/src/core/magnetostatics/CMakeLists.txt index a1388ef236d..6b6ab06dfc2 100644 --- a/src/core/magnetostatics/CMakeLists.txt +++ b/src/core/magnetostatics/CMakeLists.txt @@ -17,7 +17,7 @@ # along with this program. If not, see . # target_sources( - Espresso_core + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/dipoles.cpp ${CMAKE_CURRENT_SOURCE_DIR}/barnes_hut_gpu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dds.cpp diff --git a/src/core/nonbonded_interactions/CMakeLists.txt b/src/core/nonbonded_interactions/CMakeLists.txt index 32f2c5f8a71..66570208137 100644 --- a/src/core/nonbonded_interactions/CMakeLists.txt +++ b/src/core/nonbonded_interactions/CMakeLists.txt @@ -1,5 +1,5 @@ target_sources( - Espresso_core + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/bmhtf-nacl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/buckingham.cpp ${CMAKE_CURRENT_SOURCE_DIR}/gaussian.cpp diff --git a/src/core/object-in-fluid/CMakeLists.txt b/src/core/object-in-fluid/CMakeLists.txt index c602b77cdd0..6019502431c 100644 --- a/src/core/object-in-fluid/CMakeLists.txt +++ b/src/core/object-in-fluid/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_core +target_sources(espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/oif_global_forces.cpp) diff --git a/src/core/observables/CMakeLists.txt b/src/core/observables/CMakeLists.txt index 44c2937c82e..57793fe1f03 100644 --- a/src/core/observables/CMakeLists.txt +++ b/src/core/observables/CMakeLists.txt @@ -1,5 +1,5 @@ target_sources( - Espresso_core + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/CylindricalLBFluxDensityProfileAtParticlePositions.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CylindricalLBVelocityProfileAtParticlePositions.cpp diff --git a/src/core/p3m/CMakeLists.txt b/src/core/p3m/CMakeLists.txt index 1ac937ea20e..f05697c8378 100644 --- a/src/core/p3m/CMakeLists.txt +++ b/src/core/p3m/CMakeLists.txt @@ -18,11 +18,11 @@ # if(FFTW3_FOUND) - target_link_libraries(Espresso_core PUBLIC FFTW3::FFTW3) + target_link_libraries(espresso_core PUBLIC FFTW3::FFTW3) endif() target_sources( - Espresso_core + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/common.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TuningAlgorithm.cpp ${CMAKE_CURRENT_SOURCE_DIR}/send_mesh.cpp diff --git a/src/core/reaction_methods/CMakeLists.txt b/src/core/reaction_methods/CMakeLists.txt index 57dcb5defb9..6d6d34729a1 100644 --- a/src/core/reaction_methods/CMakeLists.txt +++ b/src/core/reaction_methods/CMakeLists.txt @@ -18,7 +18,7 @@ # target_sources( - Espresso_core + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ReactionAlgorithm.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ReactionEnsemble.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ConstantpHEnsemble.cpp diff --git a/src/core/reaction_methods/tests/CMakeLists.txt b/src/core/reaction_methods/tests/CMakeLists.txt index 39123a04f97..4993e5b8267 100644 --- a/src/core/reaction_methods/tests/CMakeLists.txt +++ b/src/core/reaction_methods/tests/CMakeLists.txt @@ -18,14 +18,14 @@ include(unit_test) unit_test(NAME SingleReaction_test SRC SingleReaction_test.cpp DEPENDS - Espresso::core) + espresso::core) unit_test(NAME ConstantpHEnsemble_test SRC ConstantpHEnsemble_test.cpp DEPENDS - Espresso::core Boost::mpi MPI::MPI_CXX) + espresso::core Boost::mpi MPI::MPI_CXX) unit_test(NAME ReactionAlgorithm_test SRC ReactionAlgorithm_test.cpp DEPENDS - Espresso::core Boost::mpi MPI::MPI_CXX) + espresso::core Boost::mpi MPI::MPI_CXX) unit_test(NAME ReactionEnsemble_test SRC ReactionEnsemble_test.cpp DEPENDS - Espresso::core Boost::mpi MPI::MPI_CXX) + espresso::core Boost::mpi MPI::MPI_CXX) unit_test(NAME particle_tracking_test SRC particle_tracking_test.cpp DEPENDS - Espresso::core Boost::mpi MPI::MPI_CXX) + espresso::core Boost::mpi MPI::MPI_CXX) unit_test(NAME reaction_methods_utils_test SRC reaction_methods_utils_test.cpp - DEPENDS Espresso::core) + DEPENDS espresso::core) diff --git a/src/core/scafacos/CMakeLists.txt b/src/core/scafacos/CMakeLists.txt index e36b354e3bb..1300b9d51a2 100644 --- a/src/core/scafacos/CMakeLists.txt +++ b/src/core/scafacos/CMakeLists.txt @@ -18,8 +18,8 @@ # if(SCAFACOS) - target_link_libraries(Espresso_core PRIVATE Espresso::scafacos) + target_link_libraries(espresso_core PRIVATE espresso::scafacos) target_sources( - Espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ScafacosContext.cpp + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ScafacosContext.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ScafacosContextBase.cpp) endif() diff --git a/src/core/stokesian_dynamics/CMakeLists.txt b/src/core/stokesian_dynamics/CMakeLists.txt index 60c851344ff..f0e20f310bf 100644 --- a/src/core/stokesian_dynamics/CMakeLists.txt +++ b/src/core/stokesian_dynamics/CMakeLists.txt @@ -18,10 +18,10 @@ # if(STOKESIAN_DYNAMICS) - target_sources(Espresso_core + target_sources(espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/sd_interface.cpp) - target_link_libraries(Espresso_core PRIVATE StokesianDynamics::sd_cpu) - target_include_directories(Espresso_core SYSTEM + target_link_libraries(espresso_core PRIVATE StokesianDynamics::sd_cpu) + target_include_directories(espresso_core SYSTEM PRIVATE ${stokesian_dynamics_SOURCE_DIR}/include) - target_include_directories(Espresso_core PRIVATE ${CMAKE_SOURCE_DIR}) + target_include_directories(espresso_core PRIVATE ${CMAKE_SOURCE_DIR}) endif() diff --git a/src/core/unit_tests/CMakeLists.txt b/src/core/unit_tests/CMakeLists.txt index c260d211ad9..274bcf5af84 100644 --- a/src/core/unit_tests/CMakeLists.txt +++ b/src/core/unit_tests/CMakeLists.txt @@ -23,48 +23,48 @@ 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 Espresso::core Boost::mpi MPI::MPI_CXX NUM_PROC 2) + DEPENDS espresso::core Boost::mpi MPI::MPI_CXX NUM_PROC 2) unit_test(NAME EspressoSystemStandAlone_test SRC - EspressoSystemStandAlone_test.cpp DEPENDS Espresso::core Boost::mpi + EspressoSystemStandAlone_test.cpp DEPENDS espresso::core Boost::mpi MPI::MPI_CXX NUM_PROC 2) unit_test(NAME EspressoSystemInterface_test SRC - EspressoSystemInterface_test.cpp DEPENDS Espresso::core Boost::mpi) + EspressoSystemInterface_test.cpp DEPENDS espresso::core Boost::mpi) unit_test(NAME MpiCallbacks_test SRC MpiCallbacks_test.cpp DEPENDS - Espresso::utils Boost::mpi MPI::MPI_CXX NUM_PROC 2) + espresso::utils Boost::mpi MPI::MPI_CXX NUM_PROC 2) unit_test(NAME ParticleIterator_test SRC ParticleIterator_test.cpp DEPENDS - Espresso::utils) -unit_test(NAME p3m_test SRC p3m_test.cpp DEPENDS Espresso::utils) -unit_test(NAME link_cell_test SRC link_cell_test.cpp DEPENDS Espresso::utils) -unit_test(NAME Particle_test SRC Particle_test.cpp DEPENDS Espresso::utils + espresso::utils) +unit_test(NAME p3m_test SRC p3m_test.cpp DEPENDS espresso::utils) +unit_test(NAME link_cell_test SRC link_cell_test.cpp DEPENDS espresso::utils) +unit_test(NAME Particle_test SRC Particle_test.cpp DEPENDS espresso::utils Boost::serialization) unit_test(NAME Particle_serialization_test SRC Particle_serialization_test.cpp - DEPENDS Espresso::utils Boost::serialization) -unit_test(NAME rotation_test SRC rotation_test.cpp DEPENDS Espresso::utils - Espresso::core) + DEPENDS espresso::utils Boost::serialization) +unit_test(NAME rotation_test SRC rotation_test.cpp DEPENDS espresso::utils + espresso::core) unit_test(NAME field_coupling_couplings SRC field_coupling_couplings_test.cpp - DEPENDS Espresso::utils) + DEPENDS espresso::utils) unit_test(NAME field_coupling_fields SRC field_coupling_fields_test.cpp DEPENDS - Espresso::utils) + espresso::utils) unit_test(NAME field_coupling_force_field SRC - field_coupling_force_field_test.cpp DEPENDS Espresso::utils) + field_coupling_force_field_test.cpp DEPENDS espresso::utils) unit_test(NAME periodic_fold_test SRC periodic_fold_test.cpp) -unit_test(NAME grid_test SRC grid_test.cpp DEPENDS Espresso::core) +unit_test(NAME grid_test SRC grid_test.cpp DEPENDS espresso::core) unit_test(NAME lees_edwards_test SRC lees_edwards_test.cpp DEPENDS - Espresso::core) -unit_test(NAME BoxGeometry_test SRC BoxGeometry_test.cpp DEPENDS Espresso::core) -unit_test(NAME LocalBox_test SRC LocalBox_test.cpp DEPENDS Espresso::core) -unit_test(NAME Lattice_test SRC Lattice_test.cpp DEPENDS Espresso::core) -unit_test(NAME lb_exceptions SRC lb_exceptions.cpp DEPENDS Espresso::core) -unit_test(NAME Verlet_list_test SRC Verlet_list_test.cpp DEPENDS Espresso::core + espresso::core) +unit_test(NAME BoxGeometry_test SRC BoxGeometry_test.cpp DEPENDS espresso::core) +unit_test(NAME LocalBox_test SRC LocalBox_test.cpp DEPENDS espresso::core) +unit_test(NAME Lattice_test SRC Lattice_test.cpp DEPENDS espresso::core) +unit_test(NAME lb_exceptions SRC lb_exceptions.cpp DEPENDS espresso::core) +unit_test(NAME Verlet_list_test SRC Verlet_list_test.cpp DEPENDS espresso::core NUM_PROC 4) unit_test(NAME VerletCriterion_test SRC VerletCriterion_test.cpp DEPENDS - Espresso::core) -unit_test(NAME thermostats_test SRC thermostats_test.cpp DEPENDS Espresso::core) -unit_test(NAME random_test SRC random_test.cpp DEPENDS Espresso::utils + espresso::core) +unit_test(NAME thermostats_test SRC thermostats_test.cpp DEPENDS espresso::core) +unit_test(NAME random_test SRC random_test.cpp DEPENDS espresso::utils Random123) -unit_test(NAME BondList_test SRC BondList_test.cpp DEPENDS Espresso::core) -unit_test(NAME energy_test SRC energy_test.cpp DEPENDS Espresso::core) +unit_test(NAME BondList_test SRC BondList_test.cpp DEPENDS espresso::core) +unit_test(NAME energy_test SRC energy_test.cpp DEPENDS espresso::core) unit_test(NAME bonded_interactions_map_test SRC - bonded_interactions_map_test.cpp DEPENDS Espresso::core) + bonded_interactions_map_test.cpp DEPENDS espresso::core) unit_test(NAME bond_breakage_test SRC bond_breakage_test.cpp DEPENDS - Espresso::core) + espresso::core) diff --git a/src/core/virtual_sites/CMakeLists.txt b/src/core/virtual_sites/CMakeLists.txt index d47d1ebe7ce..3207c93a2cf 100644 --- a/src/core/virtual_sites/CMakeLists.txt +++ b/src/core/virtual_sites/CMakeLists.txt @@ -1,5 +1,5 @@ target_sources( - Espresso_core + espresso_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/lb_inertialess_tracers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/lb_inertialess_tracers_cuda_interface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/VirtualSitesInertialessTracers.cpp diff --git a/src/particle_observables/CMakeLists.txt b/src/particle_observables/CMakeLists.txt index 70b961dc7c4..1ff9a47d04b 100644 --- a/src/particle_observables/CMakeLists.txt +++ b/src/particle_observables/CMakeLists.txt @@ -1,11 +1,11 @@ -add_library(Espresso_particle_observables INTERFACE) -add_library(Espresso::particle_observables ALIAS Espresso_particle_observables) +add_library(espresso_particle_observables INTERFACE) +add_library(espresso::particle_observables ALIAS espresso_particle_observables) target_include_directories( - Espresso_particle_observables SYSTEM + espresso_particle_observables SYSTEM INTERFACE $ $) -install(TARGETS Espresso_particle_observables +install(TARGETS espresso_particle_observables LIBRARY DESTINATION ${ESPRESSO_INSTALL_PYTHON}/espressomd) if(WITH_TESTS) diff --git a/src/particle_observables/tests/CMakeLists.txt b/src/particle_observables/tests/CMakeLists.txt index e66d2c71b1b..6c53b9917b1 100644 --- a/src/particle_observables/tests/CMakeLists.txt +++ b/src/particle_observables/tests/CMakeLists.txt @@ -1,8 +1,8 @@ include(unit_test) unit_test(NAME properties_test SRC properties.cpp DEPENDS - Espresso::particle_observables) + espresso::particle_observables) unit_test(NAME algorithms_test SRC algorithms.cpp DEPENDS - Espresso::particle_observables) + espresso::particle_observables) unit_test(NAME observables_test SRC observables.cpp DEPENDS - Espresso::particle_observables) + espresso::particle_observables) diff --git a/src/profiler/CMakeLists.txt b/src/profiler/CMakeLists.txt index df7c215a0d1..8a46db099f2 100644 --- a/src/profiler/CMakeLists.txt +++ b/src/profiler/CMakeLists.txt @@ -1,10 +1,10 @@ -add_library(Espresso_profiler INTERFACE) -add_library(Espresso::profiler ALIAS Espresso_profiler) -target_include_directories(Espresso_profiler INTERFACE "include") +add_library(espresso_profiler INTERFACE) +add_library(espresso::profiler ALIAS espresso_profiler) +target_include_directories(espresso_profiler INTERFACE "include") if(WITH_PROFILER) find_package(caliper REQUIRED) - target_link_libraries(Espresso_profiler INTERFACE caliper-mpi) - target_compile_definitions(Espresso_profiler INTERFACE HAVE_CALIPER) + target_link_libraries(espresso_profiler INTERFACE caliper-mpi) + target_compile_definitions(espresso_profiler INTERFACE HAVE_CALIPER) endif() diff --git a/src/python/espressomd/CMakeLists.txt b/src/python/espressomd/CMakeLists.txt index d5433dd43f7..382c4735574 100644 --- a/src/python/espressomd/CMakeLists.txt +++ b/src/python/espressomd/CMakeLists.txt @@ -26,7 +26,7 @@ add_custom_command( DEPENDS ${CMAKE_SOURCE_DIR}/src/config/features.def) add_executable(gen_pxiconfig gen_pxiconfig.cpp) -target_link_libraries(gen_pxiconfig Espresso::config) +target_link_libraries(gen_pxiconfig espresso::config) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/myconfig.pxi @@ -48,10 +48,10 @@ add_subdirectory(io) list(REMOVE_DUPLICATES cython_SRC) -add_library(Espresso_pyx_flags INTERFACE) -add_library(Espresso::pyx_flags ALIAS Espresso_pyx_flags) +add_library(espresso_pyx_flags INTERFACE) +add_library(espresso::pyx_flags ALIAS espresso_pyx_flags) target_compile_options( - Espresso_pyx_flags + espresso_pyx_flags INTERFACE $<$>:-Wno-pedantic> $<$>:-Wno-deprecated-declarations> @@ -107,10 +107,10 @@ foreach(cython_file ${cython_SRC}) "-undefined dynamic_lookup") endif() set_target_properties(${target} PROPERTIES CXX_CLANG_TIDY "") - target_link_libraries(${target} PRIVATE Espresso::config Espresso::core - Espresso::script_interface) - target_link_libraries(${target} PRIVATE Espresso::cpp_flags) - target_link_libraries(${target} PRIVATE Espresso::pyx_flags) + target_link_libraries(${target} PRIVATE espresso::config espresso::core + espresso::script_interface) + target_link_libraries(${target} PRIVATE espresso::cpp_flags) + target_link_libraries(${target} PRIVATE espresso::pyx_flags) target_include_directories( ${target} SYSTEM PRIVATE ${Python_INCLUDE_DIRS} ${Python_NumPy_INCLUDE_DIRS}) @@ -120,7 +120,7 @@ foreach(cython_file ${cython_SRC}) endif() endforeach() -target_link_libraries(espressomd_profiler PRIVATE Espresso::profiler) +target_link_libraries(espressomd_profiler PRIVATE espresso::profiler) # Configure Python files foreach(auxfile ${cython_AUX}) diff --git a/src/scafacos/CMakeLists.txt b/src/scafacos/CMakeLists.txt index d88d07891f8..b7948326295 100644 --- a/src/scafacos/CMakeLists.txt +++ b/src/scafacos/CMakeLists.txt @@ -1,20 +1,20 @@ -add_library(Espresso_scafacos SHARED src/Scafacos.cpp src/Coulomb.cpp +add_library(espresso_scafacos SHARED src/Scafacos.cpp src/Coulomb.cpp src/Dipoles.cpp) -add_library(Espresso::scafacos ALIAS Espresso_scafacos) +add_library(espresso::scafacos ALIAS espresso_scafacos) if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 10 AND INSIDE_DOCKER AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS_EQUAL 11 AND WITH_COVERAGE) - target_link_libraries(Espresso_scafacos + target_link_libraries(espresso_scafacos PRIVATE "-L/usr/lib/gcc/x86_64-linux-gnu/10") endif() -target_link_libraries(Espresso_scafacos PUBLIC MPI::MPI_CXX - PRIVATE ${SCAFACOS_LDFLAGS} Espresso::cpp_flags) +target_link_libraries(espresso_scafacos PUBLIC MPI::MPI_CXX + PRIVATE ${SCAFACOS_LDFLAGS} espresso::cpp_flags) target_include_directories( - Espresso_scafacos + espresso_scafacos PUBLIC $ $) -target_include_directories(Espresso_scafacos SYSTEM +target_include_directories(espresso_scafacos SYSTEM PUBLIC ${SCAFACOS_INCLUDE_DIRS}) -install(TARGETS Espresso_scafacos +install(TARGETS espresso_scafacos DESTINATION ${ESPRESSO_INSTALL_PYTHON}/espressomd) diff --git a/src/script_interface/CMakeLists.txt b/src/script_interface/CMakeLists.txt index 7756688bfc1..49c387d98b0 100644 --- a/src/script_interface/CMakeLists.txt +++ b/src/script_interface/CMakeLists.txt @@ -18,10 +18,10 @@ # add_library( - Espresso_script_interface SHARED + espresso_script_interface SHARED initialize.cpp ObjectHandle.cpp object_container_mpi_guard.cpp GlobalContext.cpp ContextManager.cpp ParallelExceptionHandler.cpp) -add_library(Espresso::script_interface ALIAS Espresso_script_interface) +add_library(espresso::script_interface ALIAS espresso_script_interface) add_subdirectory(accumulators) add_subdirectory(analysis) @@ -49,15 +49,15 @@ add_subdirectory(shapes) add_subdirectory(system) add_subdirectory(virtual_sites) -install(TARGETS Espresso_script_interface +install(TARGETS espresso_script_interface LIBRARY DESTINATION ${ESPRESSO_INSTALL_PYTHON}/espressomd) target_link_libraries( - Espresso_script_interface PRIVATE Espresso::config Espresso::core - PUBLIC Espresso::utils MPI::MPI_CXX Boost::mpi Espresso::shapes - PRIVATE Espresso::cpp_flags) + espresso_script_interface PRIVATE espresso::config espresso::core + PUBLIC espresso::utils MPI::MPI_CXX Boost::mpi espresso::shapes + PRIVATE espresso::cpp_flags) -target_include_directories(Espresso_script_interface +target_include_directories(espresso_script_interface PUBLIC ${CMAKE_SOURCE_DIR}/src) add_subdirectory(tests) diff --git a/src/script_interface/accumulators/CMakeLists.txt b/src/script_interface/accumulators/CMakeLists.txt index 36dbb5a513c..b74821d0b1e 100644 --- a/src/script_interface/accumulators/CMakeLists.txt +++ b/src/script_interface/accumulators/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/analysis/CMakeLists.txt b/src/script_interface/analysis/CMakeLists.txt index 0fc64ff3ccb..49612d7733c 100644 --- a/src/script_interface/analysis/CMakeLists.txt +++ b/src/script_interface/analysis/CMakeLists.txt @@ -1,5 +1,5 @@ target_sources( - Espresso_script_interface + espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Analysis.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ObservableStat.cpp) diff --git a/src/script_interface/bond_breakage/CMakeLists.txt b/src/script_interface/bond_breakage/CMakeLists.txt index 36dbb5a513c..b74821d0b1e 100644 --- a/src/script_interface/bond_breakage/CMakeLists.txt +++ b/src/script_interface/bond_breakage/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/cell_system/CMakeLists.txt b/src/script_interface/cell_system/CMakeLists.txt index 4b997aa6dc4..e936fa248fb 100644 --- a/src/script_interface/cell_system/CMakeLists.txt +++ b/src/script_interface/cell_system/CMakeLists.txt @@ -1,3 +1,3 @@ target_sources( - Espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp + espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CellSystem.cpp) diff --git a/src/script_interface/cluster_analysis/CMakeLists.txt b/src/script_interface/cluster_analysis/CMakeLists.txt index 36dbb5a513c..b74821d0b1e 100644 --- a/src/script_interface/cluster_analysis/CMakeLists.txt +++ b/src/script_interface/cluster_analysis/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/code_info/CMakeLists.txt b/src/script_interface/code_info/CMakeLists.txt index 734ebd564c8..6b0f891e5bf 100644 --- a/src/script_interface/code_info/CMakeLists.txt +++ b/src/script_interface/code_info/CMakeLists.txt @@ -1,5 +1,5 @@ target_sources( - Espresso_script_interface + espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CodeInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CodeVersion.cpp) diff --git a/src/script_interface/collision_detection/CMakeLists.txt b/src/script_interface/collision_detection/CMakeLists.txt index 36dbb5a513c..b74821d0b1e 100644 --- a/src/script_interface/collision_detection/CMakeLists.txt +++ b/src/script_interface/collision_detection/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/constraints/CMakeLists.txt b/src/script_interface/constraints/CMakeLists.txt index 36dbb5a513c..b74821d0b1e 100644 --- a/src/script_interface/constraints/CMakeLists.txt +++ b/src/script_interface/constraints/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/electrostatics/CMakeLists.txt b/src/script_interface/electrostatics/CMakeLists.txt index 36dbb5a513c..b74821d0b1e 100644 --- a/src/script_interface/electrostatics/CMakeLists.txt +++ b/src/script_interface/electrostatics/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/galilei/CMakeLists.txt b/src/script_interface/galilei/CMakeLists.txt index 36dbb5a513c..b74821d0b1e 100644 --- a/src/script_interface/galilei/CMakeLists.txt +++ b/src/script_interface/galilei/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/h5md/CMakeLists.txt b/src/script_interface/h5md/CMakeLists.txt index e45c193a7f0..5dea7226b1e 100644 --- a/src/script_interface/h5md/CMakeLists.txt +++ b/src/script_interface/h5md/CMakeLists.txt @@ -1,4 +1,4 @@ target_sources( - Espresso_script_interface + espresso_script_interface PRIVATE $<$:${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp> $<$:${CMAKE_CURRENT_SOURCE_DIR}/h5md.cpp>) diff --git a/src/script_interface/interactions/CMakeLists.txt b/src/script_interface/interactions/CMakeLists.txt index 36dbb5a513c..b74821d0b1e 100644 --- a/src/script_interface/interactions/CMakeLists.txt +++ b/src/script_interface/interactions/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/lbboundaries/CMakeLists.txt b/src/script_interface/lbboundaries/CMakeLists.txt index 36dbb5a513c..b74821d0b1e 100644 --- a/src/script_interface/lbboundaries/CMakeLists.txt +++ b/src/script_interface/lbboundaries/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/lees_edwards/CMakeLists.txt b/src/script_interface/lees_edwards/CMakeLists.txt index 36dbb5a513c..b74821d0b1e 100644 --- a/src/script_interface/lees_edwards/CMakeLists.txt +++ b/src/script_interface/lees_edwards/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/magnetostatics/CMakeLists.txt b/src/script_interface/magnetostatics/CMakeLists.txt index 36dbb5a513c..b74821d0b1e 100644 --- a/src/script_interface/magnetostatics/CMakeLists.txt +++ b/src/script_interface/magnetostatics/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/math/CMakeLists.txt b/src/script_interface/math/CMakeLists.txt index 36dbb5a513c..b74821d0b1e 100644 --- a/src/script_interface/math/CMakeLists.txt +++ b/src/script_interface/math/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/mpiio/CMakeLists.txt b/src/script_interface/mpiio/CMakeLists.txt index 36dbb5a513c..b74821d0b1e 100644 --- a/src/script_interface/mpiio/CMakeLists.txt +++ b/src/script_interface/mpiio/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/observables/CMakeLists.txt b/src/script_interface/observables/CMakeLists.txt index 36dbb5a513c..b74821d0b1e 100644 --- a/src/script_interface/observables/CMakeLists.txt +++ b/src/script_interface/observables/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/pair_criteria/CMakeLists.txt b/src/script_interface/pair_criteria/CMakeLists.txt index 36dbb5a513c..b74821d0b1e 100644 --- a/src/script_interface/pair_criteria/CMakeLists.txt +++ b/src/script_interface/pair_criteria/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/particle_data/CMakeLists.txt b/src/script_interface/particle_data/CMakeLists.txt index cc7e8b8d031..f131f61619c 100644 --- a/src/script_interface/particle_data/CMakeLists.txt +++ b/src/script_interface/particle_data/CMakeLists.txt @@ -1,5 +1,5 @@ target_sources( - Espresso_script_interface + espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ParticleHandle.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ParticleList.cpp diff --git a/src/script_interface/reaction_methods/CMakeLists.txt b/src/script_interface/reaction_methods/CMakeLists.txt index 36dbb5a513c..b74821d0b1e 100644 --- a/src/script_interface/reaction_methods/CMakeLists.txt +++ b/src/script_interface/reaction_methods/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/scafacos/CMakeLists.txt b/src/script_interface/scafacos/CMakeLists.txt index a856fb4088f..d1f9def68ad 100644 --- a/src/script_interface/scafacos/CMakeLists.txt +++ b/src/script_interface/scafacos/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/scafacos.cpp) diff --git a/src/script_interface/shapes/CMakeLists.txt b/src/script_interface/shapes/CMakeLists.txt index 36dbb5a513c..b74821d0b1e 100644 --- a/src/script_interface/shapes/CMakeLists.txt +++ b/src/script_interface/shapes/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/script_interface/system/CMakeLists.txt b/src/script_interface/system/CMakeLists.txt index 3a66434c73f..8cac65c9603 100644 --- a/src/script_interface/system/CMakeLists.txt +++ b/src/script_interface/system/CMakeLists.txt @@ -1,5 +1,5 @@ target_sources( - Espresso_script_interface + espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CudaInitHandle.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Globals.cpp diff --git a/src/script_interface/tests/CMakeLists.txt b/src/script_interface/tests/CMakeLists.txt index 67db65f6ffe..e65b4e9fef4 100644 --- a/src/script_interface/tests/CMakeLists.txt +++ b/src/script_interface/tests/CMakeLists.txt @@ -20,37 +20,37 @@ include(unit_test) unit_test(NAME ObjectHandle_test SRC ObjectHandle_test.cpp DEPENDS - Espresso::script_interface) + espresso::script_interface) unit_test(NAME AutoParameters_test SRC AutoParameters_test.cpp DEPENDS - Espresso::script_interface) + espresso::script_interface) unit_test(NAME AutoParameter_test SRC AutoParameter_test.cpp DEPENDS - Espresso::script_interface) + espresso::script_interface) unit_test(NAME Variant_test SRC Variant_test.cpp DEPENDS - Espresso::script_interface) + espresso::script_interface) unit_test(NAME get_value_test SRC get_value_test.cpp DEPENDS - Espresso::script_interface) -unit_test(NAME None_test SRC None_test.cpp DEPENDS Espresso::script_interface) + espresso::script_interface) +unit_test(NAME None_test SRC None_test.cpp DEPENDS espresso::script_interface) unit_test(NAME LocalContext_test SRC LocalContext_test.cpp DEPENDS - Espresso::script_interface Boost::mpi MPI::MPI_CXX NUM_PROC 1) + espresso::script_interface Boost::mpi MPI::MPI_CXX NUM_PROC 1) unit_test(NAME GlobalContext_test SRC GlobalContext_test.cpp DEPENDS - Espresso::script_interface Boost::mpi MPI::MPI_CXX NUM_PROC 2) + espresso::script_interface Boost::mpi MPI::MPI_CXX NUM_PROC 2) unit_test(NAME Exception_test SRC Exception_test.cpp DEPENDS - Espresso::script_interface) + espresso::script_interface) unit_test(NAME ParallelExceptionHandler_test SRC - ParallelExceptionHandler_test.cpp DEPENDS Espresso::script_interface - Espresso::core Boost::mpi MPI::MPI_CXX NUM_PROC 2) + ParallelExceptionHandler_test.cpp DEPENDS espresso::script_interface + espresso::core Boost::mpi MPI::MPI_CXX NUM_PROC 2) unit_test(NAME packed_variant_test SRC packed_variant_test.cpp DEPENDS - Espresso::script_interface) + espresso::script_interface) unit_test(NAME ObjectList_test SRC ObjectList_test.cpp DEPENDS - Espresso::script_interface Espresso::core) + espresso::script_interface espresso::core) unit_test(NAME ObjectMap_test SRC ObjectMap_test.cpp DEPENDS - Espresso::script_interface Espresso::core) + espresso::script_interface espresso::core) unit_test(NAME serialization_mpi_guard_test SRC - serialization_mpi_guard_test.cpp DEPENDS Espresso::script_interface + serialization_mpi_guard_test.cpp DEPENDS espresso::script_interface Boost::mpi MPI::MPI_CXX NUM_PROC 2) unit_test(NAME Accumulators_test SRC Accumulators_test.cpp DEPENDS - Espresso::script_interface Espresso::core) + espresso::script_interface espresso::core) unit_test(NAME Constraints_test SRC Constraints_test.cpp DEPENDS - Espresso::script_interface Espresso::core) + espresso::script_interface espresso::core) unit_test(NAME Actors_test SRC Actors_test.cpp DEPENDS - Espresso::script_interface Espresso::core) + espresso::script_interface espresso::core) diff --git a/src/script_interface/virtual_sites/CMakeLists.txt b/src/script_interface/virtual_sites/CMakeLists.txt index 36dbb5a513c..b74821d0b1e 100644 --- a/src/script_interface/virtual_sites/CMakeLists.txt +++ b/src/script_interface/virtual_sites/CMakeLists.txt @@ -1,2 +1,2 @@ -target_sources(Espresso_script_interface +target_sources(espresso_script_interface PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp) diff --git a/src/shapes/CMakeLists.txt b/src/shapes/CMakeLists.txt index 9048c20c920..cee3d6a3a8f 100644 --- a/src/shapes/CMakeLists.txt +++ b/src/shapes/CMakeLists.txt @@ -1,17 +1,17 @@ add_library( - Espresso_shapes SHARED + espresso_shapes SHARED src/HollowConicalFrustum.cpp src/Cylinder.cpp src/Ellipsoid.cpp src/Rhomboid.cpp src/SimplePore.cpp src/Slitpore.cpp src/Sphere.cpp src/SpheroCylinder.cpp src/Torus.cpp src/Wall.cpp) -add_library(Espresso::shapes ALIAS Espresso_shapes) +add_library(espresso::shapes ALIAS espresso_shapes) -target_link_libraries(Espresso_shapes PUBLIC Espresso::utils - PRIVATE Boost::boost Espresso::cpp_flags) +target_link_libraries(espresso_shapes PUBLIC espresso::utils + PRIVATE Boost::boost espresso::cpp_flags) target_include_directories( - Espresso_shapes PUBLIC $ + espresso_shapes PUBLIC $ $) -install(TARGETS Espresso_shapes +install(TARGETS espresso_shapes LIBRARY DESTINATION ${ESPRESSO_INSTALL_PYTHON}/espressomd) if(WITH_TESTS) diff --git a/src/shapes/unit_tests/CMakeLists.txt b/src/shapes/unit_tests/CMakeLists.txt index e9cd65d682c..3e10957d282 100644 --- a/src/shapes/unit_tests/CMakeLists.txt +++ b/src/shapes/unit_tests/CMakeLists.txt @@ -1,13 +1,13 @@ include(unit_test) -unit_test(NAME Wall_test SRC Wall_test.cpp DEPENDS Espresso::shapes - Espresso::utils) +unit_test(NAME Wall_test SRC Wall_test.cpp DEPENDS espresso::shapes + espresso::utils) unit_test(NAME HollowConicalFrustum_test SRC HollowConicalFrustum_test.cpp - DEPENDS Espresso::shapes Espresso::utils) -unit_test(NAME Union_test SRC Union_test.cpp DEPENDS Espresso::shapes - Espresso::utils) -unit_test(NAME Ellipsoid_test SRC Ellipsoid_test.cpp DEPENDS Espresso::shapes - Espresso::utils) -unit_test(NAME Sphere_test SRC Sphere_test.cpp DEPENDS Espresso::shapes - Espresso::utils) -unit_test(NAME NoWhere_test SRC NoWhere_test.cpp DEPENDS Espresso::shapes - Espresso::utils) + DEPENDS espresso::shapes espresso::utils) +unit_test(NAME Union_test SRC Union_test.cpp DEPENDS espresso::shapes + espresso::utils) +unit_test(NAME Ellipsoid_test SRC Ellipsoid_test.cpp DEPENDS espresso::shapes + espresso::utils) +unit_test(NAME Sphere_test SRC Sphere_test.cpp DEPENDS espresso::shapes + espresso::utils) +unit_test(NAME NoWhere_test SRC NoWhere_test.cpp DEPENDS espresso::shapes + espresso::utils) diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 0b97b475fae..f777723e9cf 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -1,12 +1,12 @@ -add_library(Espresso_utils INTERFACE) -add_library(Espresso::utils ALIAS Espresso_utils) -add_library(Espresso_utils_mpi INTERFACE) -add_library(Espresso::utils::mpi ALIAS Espresso_utils) +add_library(espresso_utils INTERFACE) +add_library(espresso::utils ALIAS espresso_utils) +add_library(espresso_utils_mpi INTERFACE) +add_library(espresso::utils::mpi ALIAS espresso_utils) target_include_directories( - Espresso_utils + espresso_utils INTERFACE $) -target_link_libraries(Espresso_utils INTERFACE Boost::serialization) -target_link_libraries(Espresso_utils_mpi INTERFACE Espresso::utils Boost::mpi +target_link_libraries(espresso_utils INTERFACE Boost::serialization) +target_link_libraries(espresso_utils_mpi INTERFACE espresso::utils Boost::mpi MPI::MPI_CXX) if(WITH_TESTS) diff --git a/src/utils/tests/CMakeLists.txt b/src/utils/tests/CMakeLists.txt index 55eae6bba76..cef8e0bfe04 100644 --- a/src/utils/tests/CMakeLists.txt +++ b/src/utils/tests/CMakeLists.txt @@ -1,89 +1,89 @@ include(unit_test) -unit_test(NAME abs_test SRC abs_test.cpp DEPENDS Espresso::utils) -unit_test(NAME Vector_test SRC Vector_test.cpp DEPENDS Espresso::utils) -unit_test(NAME Factory_test SRC Factory_test.cpp DEPENDS Espresso::utils) +unit_test(NAME abs_test SRC abs_test.cpp DEPENDS espresso::utils) +unit_test(NAME Vector_test SRC Vector_test.cpp DEPENDS espresso::utils) +unit_test(NAME Factory_test SRC Factory_test.cpp DEPENDS espresso::utils) unit_test(NAME NumeratedContainer_test SRC NumeratedContainer_test.cpp DEPENDS - Espresso::utils) -unit_test(NAME keys_test SRC keys_test.cpp DEPENDS Espresso::utils) -unit_test(NAME Cache_test SRC Cache_test.cpp DEPENDS Espresso::utils) -unit_test(NAME histogram SRC histogram.cpp DEPENDS Espresso::utils) -unit_test(NAME accumulator SRC accumulator.cpp DEPENDS Espresso::utils + espresso::utils) +unit_test(NAME keys_test SRC keys_test.cpp DEPENDS espresso::utils) +unit_test(NAME Cache_test SRC Cache_test.cpp DEPENDS espresso::utils) +unit_test(NAME histogram SRC histogram.cpp DEPENDS espresso::utils) +unit_test(NAME accumulator SRC accumulator.cpp DEPENDS espresso::utils Boost::serialization) -unit_test(NAME int_pow SRC int_pow_test.cpp DEPENDS Espresso::utils) -unit_test(NAME sgn SRC sgn_test.cpp DEPENDS Espresso::utils) -unit_test(NAME AS_erfc_part SRC AS_erfc_part_test.cpp DEPENDS Espresso::utils) -unit_test(NAME sinc SRC sinc_test.cpp DEPENDS Espresso::utils) -unit_test(NAME as_const SRC as_const_test.cpp DEPENDS Espresso::utils) +unit_test(NAME int_pow SRC int_pow_test.cpp DEPENDS espresso::utils) +unit_test(NAME sgn SRC sgn_test.cpp DEPENDS espresso::utils) +unit_test(NAME AS_erfc_part SRC AS_erfc_part_test.cpp DEPENDS espresso::utils) +unit_test(NAME sinc SRC sinc_test.cpp DEPENDS espresso::utils) +unit_test(NAME as_const SRC as_const_test.cpp DEPENDS espresso::utils) unit_test(NAME permute_ifield_test SRC permute_ifield_test.cpp DEPENDS - Espresso::utils) -unit_test(NAME vec_rotate SRC vec_rotate_test.cpp DEPENDS Espresso::utils) + espresso::utils) +unit_test(NAME vec_rotate SRC vec_rotate_test.cpp DEPENDS espresso::utils) unit_test(NAME tensor_product SRC tensor_product_test.cpp DEPENDS - Espresso::utils) + espresso::utils) unit_test(NAME linear_interpolation SRC linear_interpolation_test.cpp DEPENDS - Espresso::utils) + espresso::utils) unit_test(NAME interpolation_gradient SRC interpolation_gradient_test.cpp - DEPENDS Espresso::utils) -unit_test(NAME interpolation SRC interpolation_test.cpp DEPENDS Espresso::utils) -unit_test(NAME bspline_test SRC bspline_test.cpp DEPENDS Espresso::utils) -unit_test(NAME Span_test SRC Span_test.cpp DEPENDS Espresso::utils) + DEPENDS espresso::utils) +unit_test(NAME interpolation SRC interpolation_test.cpp DEPENDS espresso::utils) +unit_test(NAME bspline_test SRC bspline_test.cpp DEPENDS espresso::utils) +unit_test(NAME Span_test SRC Span_test.cpp DEPENDS espresso::utils) unit_test(NAME matrix_vector_product SRC matrix_vector_product.cpp DEPENDS - Espresso::utils) -unit_test(NAME index_test SRC index_test.cpp DEPENDS Espresso::utils) -unit_test(NAME tuple_test SRC tuple_test.cpp DEPENDS Espresso::utils) + espresso::utils) +unit_test(NAME index_test SRC index_test.cpp DEPENDS espresso::utils) +unit_test(NAME tuple_test SRC tuple_test.cpp DEPENDS espresso::utils) unit_test(NAME Array_test SRC Array_test.cpp DEPENDS Boost::serialization - Espresso::utils) -unit_test(NAME contains_test SRC contains_test.cpp DEPENDS Espresso::utils) -unit_test(NAME Counter_test SRC Counter_test.cpp DEPENDS Espresso::utils + espresso::utils) +unit_test(NAME contains_test SRC contains_test.cpp DEPENDS espresso::utils) +unit_test(NAME Counter_test SRC Counter_test.cpp DEPENDS espresso::utils Boost::serialization) unit_test(NAME RunningAverage_test SRC RunningAverage_test.cpp DEPENDS - Espresso::utils) + espresso::utils) unit_test(NAME for_each_pair_test SRC for_each_pair_test.cpp DEPENDS - Espresso::utils) -unit_test(NAME raster_test SRC raster_test.cpp DEPENDS Espresso::utils) + espresso::utils) +unit_test(NAME raster_test SRC raster_test.cpp DEPENDS espresso::utils) unit_test(NAME make_lin_space_test SRC make_lin_space_test.cpp DEPENDS - Espresso::utils) -unit_test(NAME sampling_test SRC sampling_test.cpp DEPENDS Espresso::utils) + espresso::utils) +unit_test(NAME sampling_test SRC sampling_test.cpp DEPENDS espresso::utils) unit_test(NAME coordinate_transformation_test SRC coordinate_transformation.cpp - DEPENDS Espresso::utils) + DEPENDS espresso::utils) unit_test(NAME cylindrical_transformation_test SRC - cylindrical_transformation.cpp DEPENDS Espresso::utils) + cylindrical_transformation.cpp DEPENDS espresso::utils) unit_test(NAME rotation_matrix_test SRC rotation_matrix_test.cpp DEPENDS - Espresso::utils) -unit_test(NAME quaternion_test SRC quaternion_test.cpp DEPENDS Espresso::utils) -unit_test(NAME mask_test SRC mask_test.cpp DEPENDS Espresso::utils) + espresso::utils) +unit_test(NAME quaternion_test SRC quaternion_test.cpp DEPENDS espresso::utils) +unit_test(NAME mask_test SRC mask_test.cpp DEPENDS espresso::utils) unit_test(NAME type_traits_test SRC type_traits_test.cpp DEPENDS - Espresso::utils) -unit_test(NAME uniform_test SRC uniform_test.cpp DEPENDS Espresso::utils) + espresso::utils) +unit_test(NAME uniform_test SRC uniform_test.cpp DEPENDS espresso::utils) unit_test(NAME memcpy_archive_test SRC memcpy_archive_test.cpp DEPENDS - Espresso::utils) + espresso::utils) unit_test(NAME triangle_functions_test SRC triangle_functions_test.cpp DEPENDS - Espresso::utils) -unit_test(NAME Bag_test SRC Bag_test.cpp DEPENDS Espresso::utils + espresso::utils) +unit_test(NAME Bag_test SRC Bag_test.cpp DEPENDS espresso::utils Boost::serialization) unit_test(NAME integral_parameter_test SRC integral_parameter_test.cpp DEPENDS - Espresso::utils) -unit_test(NAME flatten_test SRC flatten_test.cpp DEPENDS Espresso::utils) + espresso::utils) +unit_test(NAME flatten_test SRC flatten_test.cpp DEPENDS espresso::utils) unit_test(NAME pack_test SRC pack_test.cpp DEPENDS Boost::serialization - Espresso::utils) + espresso::utils) unit_test(NAME unordered_map_test SRC unordered_map_test.cpp DEPENDS - Boost::serialization Espresso::utils) -unit_test(NAME u32_to_u64_test SRC u32_to_u64_test.cpp DEPENDS Espresso::utils + Boost::serialization espresso::utils) +unit_test(NAME u32_to_u64_test SRC u32_to_u64_test.cpp DEPENDS espresso::utils NUM_PROC 1) unit_test(NAME gather_buffer_test SRC gather_buffer_test.cpp DEPENDS - Espresso::utils::mpi Boost::mpi MPI::MPI_CXX NUM_PROC 4) + espresso::utils::mpi Boost::mpi MPI::MPI_CXX NUM_PROC 4) unit_test(NAME scatter_buffer_test SRC scatter_buffer_test.cpp DEPENDS - Espresso::utils::mpi Boost::mpi MPI::MPI_CXX NUM_PROC 4) + espresso::utils::mpi Boost::mpi MPI::MPI_CXX NUM_PROC 4) unit_test(NAME all_compare_test SRC all_compare_test.cpp DEPENDS - Espresso::utils::mpi Boost::mpi MPI::MPI_CXX NUM_PROC 3) -unit_test(NAME gatherv_test SRC gatherv_test.cpp DEPENDS Espresso::utils::mpi + espresso::utils::mpi Boost::mpi MPI::MPI_CXX NUM_PROC 3) +unit_test(NAME gatherv_test SRC gatherv_test.cpp DEPENDS espresso::utils::mpi Boost::mpi MPI::MPI_CXX NUM_PROC 3) -unit_test(NAME sendrecv_test SRC sendrecv_test.cpp DEPENDS Espresso::utils::mpi - Boost::mpi MPI::MPI_CXX Espresso::utils NUM_PROC 3) +unit_test(NAME sendrecv_test SRC sendrecv_test.cpp DEPENDS espresso::utils::mpi + Boost::mpi MPI::MPI_CXX espresso::utils NUM_PROC 3) unit_test(NAME serialization_test SRC serialization_test.cpp DEPENDS - Espresso::utils Boost::serialization Boost::mpi MPI::MPI_CXX NUM_PROC + espresso::utils Boost::serialization Boost::mpi MPI::MPI_CXX NUM_PROC 1) -unit_test(NAME matrix_test SRC matrix_test.cpp DEPENDS Espresso::utils +unit_test(NAME matrix_test SRC matrix_test.cpp DEPENDS espresso::utils Boost::serialization NUM_PROC 1) unit_test(NAME orthonormal_vec_test SRC orthonormal_vec_test.cpp DEPENDS - Espresso::utils Boost::serialization NUM_PROC 1) + espresso::utils Boost::serialization NUM_PROC 1) diff --git a/testsuite/cmake/test_install.sh b/testsuite/cmake/test_install.sh index 163b782093c..5f5c906ba66 100755 --- a/testsuite/cmake/test_install.sh +++ b/testsuite/cmake/test_install.sh @@ -23,7 +23,7 @@ source BashUnitTests.sh function test_install() { # check Python files were installed in espressomd local -r filepaths=("@ESPRESSO_INSTALL_BINDIR@/pypresso" \ - "@ESPRESSO_INSTALL_PYTHON@/espressomd/Espresso_core.so" \ + "@ESPRESSO_INSTALL_PYTHON@/espressomd/espresso_core.so" \ "@ESPRESSO_INSTALL_PYTHON@/espressomd/_init.so" \ "@ESPRESSO_INSTALL_PYTHON@/espressomd/__init__.py" ) From 5a0b44059f53a6baa9edcaade11d0b4044db80df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 9 Aug 2022 14:33:31 +0200 Subject: [PATCH 29/85] CMake: Fix various regressions --- CMakeLists.txt | 4 +++- cmake/FindSphinx.cmake | 2 +- doc/doxygen/gen_doxyconfig.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f960d948b51..43e693316ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -213,7 +213,9 @@ if(WITH_PYTHON) if(CMAKE_INSTALL_PREFIX STREQUAL "/") set(ESPRESSO_INSTALL_PYTHON "${Python_SITEARCH}") else() - set(ESPRESSO_INSTALL_PYTHON "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/site-packages") + set(ESPRESSO_INSTALL_PYTHON + "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/site-packages" + ) endif() endif() # override: package C++, CUDA and Cython shared objects together diff --git a/cmake/FindSphinx.cmake b/cmake/FindSphinx.cmake index 8317e43d8db..85303fe1f7b 100644 --- a/cmake/FindSphinx.cmake +++ b/cmake/FindSphinx.cmake @@ -27,7 +27,7 @@ endif() set(SPHINX_VERSION_COMPATIBLE TRUE) # Blacklist broken versions -if("${SPHINX_VERSION}" VERSION_EQUAL "2.1.0" OR "${SPHINX_VERSION}" VERSION_EQUAL "3.0.0") +if("${SPHINX_VERSION}" VERSION_EQUAL "2.3.0" OR "${SPHINX_VERSION}" VERSION_EQUAL "3.0.0") message(WARNING "Sphinx version ${SPHINX_VERSION} is not compatible.") set(SPHINX_VERSION_COMPATIBLE FALSE) endif() diff --git a/doc/doxygen/gen_doxyconfig.py b/doc/doxygen/gen_doxyconfig.py index caca0c3f80f..9d5842febec 100755 --- a/doc/doxygen/gen_doxyconfig.py +++ b/doc/doxygen/gen_doxyconfig.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (C) 2013-2022 The ESPResSo project # Copyright (C) 2012 Olaf Lenz # From 0bb7b7a663635184466b01aeb659492b9c853d31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 10 Aug 2022 11:27:56 +0200 Subject: [PATCH 30/85] CMake: Update license headers --- cmake/FindCUDACompilerClang.cmake | 3 ++- cmake/FindCUDACompilerNVCC.cmake | 3 ++- cmake/FindClangTidy.cmake | 19 +++++++++++++++++++ cmake/FindFFTW3.cmake | 3 +++ cmake/FindSphinx.cmake | 19 +++++++++++++++++++ cmake/MyConfig.cmake | 3 ++- cmake/option_enum.cmake | 3 ++- cmake/unit_test.cmake | 19 +++++++++++++++++++ doc/doxygen/gen_doxyconfig.py | 3 ++- src/CMakeLists.txt | 1 + src/config/CMakeLists.txt | 19 +++++++++++++++++++ src/config/defines.py | 5 ++++- src/config/featuredefs.py | 1 + src/config/features.def | 20 ++++++++++++++++++++ src/config/gen_sampleconfig.py | 3 ++- src/core/unit_tests/CMakeLists.txt | 2 ++ src/python/CMakeLists.txt | 19 +++++++++++++++++++ src/scafacos/CMakeLists.txt | 19 +++++++++++++++++++ src/utils/CMakeLists.txt | 19 +++++++++++++++++++ src/utils/tests/CMakeLists.txt | 21 +++++++++++++++++++++ testsuite/CMakeLists.txt | 19 +++++++++++++++++++ testsuite/cmake/CMakeLists.txt | 19 +++++++++++++++++++ testsuite/scripts/CMakeLists.txt | 19 +++++++++++++++++++ testsuite/scripts/benchmarks/CMakeLists.txt | 19 +++++++++++++++++++ testsuite/scripts/samples/CMakeLists.txt | 19 +++++++++++++++++++ testsuite/scripts/tutorials/CMakeLists.txt | 19 +++++++++++++++++++ 26 files changed, 311 insertions(+), 7 deletions(-) diff --git a/cmake/FindCUDACompilerClang.cmake b/cmake/FindCUDACompilerClang.cmake index dff808ff2d6..75305d08ea4 100644 --- a/cmake/FindCUDACompilerClang.cmake +++ b/cmake/FindCUDACompilerClang.cmake @@ -1,4 +1,5 @@ -# Copyright (C) 2009-2020 The ESPResSo project +# +# Copyright (C) 2009-2022 The ESPResSo project # Copyright (C) 2009,2010 # Max-Planck-Institute for Polymer Research, Theory Group # diff --git a/cmake/FindCUDACompilerNVCC.cmake b/cmake/FindCUDACompilerNVCC.cmake index 99cabb5abfa..d274476707c 100644 --- a/cmake/FindCUDACompilerNVCC.cmake +++ b/cmake/FindCUDACompilerNVCC.cmake @@ -1,4 +1,5 @@ -# Copyright (C) 2009-2020 The ESPResSo project +# +# Copyright (C) 2009-2022 The ESPResSo project # Copyright (C) 2009,2010 # Max-Planck-Institute for Polymer Research, Theory Group # diff --git a/cmake/FindClangTidy.cmake b/cmake/FindClangTidy.cmake index ada02b5539f..39b48f96c87 100644 --- a/cmake/FindClangTidy.cmake +++ b/cmake/FindClangTidy.cmake @@ -1,3 +1,22 @@ +# +# Copyright (C) 2017-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 Clang-tidy # get Clang version diff --git a/cmake/FindFFTW3.cmake b/cmake/FindFFTW3.cmake index 54ede3fe470..0baad7cdddb 100644 --- a/cmake/FindFFTW3.cmake +++ b/cmake/FindFFTW3.cmake @@ -1,3 +1,5 @@ +# +# Copyright (C) 2010-2022 The ESPResSo project # Copyright (C) 2009,2010 Christoph Junghans # # This file is part of ESPResSo. @@ -15,6 +17,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # + # - Find FFTW3 # Find the native FFTW3 includes and library, double precision # diff --git a/cmake/FindSphinx.cmake b/cmake/FindSphinx.cmake index 85303fe1f7b..34ed2bc2c45 100644 --- a/cmake/FindSphinx.cmake +++ b/cmake/FindSphinx.cmake @@ -1,3 +1,22 @@ +# +# Copyright (C) 2016-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(FindPackageHandleStandardArgs) set(SPHINX_EXECUTABLE ${Python_EXECUTABLE} -m sphinx) diff --git a/cmake/MyConfig.cmake b/cmake/MyConfig.cmake index 32aac7ab4f6..c0fbb5c9097 100644 --- a/cmake/MyConfig.cmake +++ b/cmake/MyConfig.cmake @@ -1,5 +1,6 @@ +# +# Copyright (C) 2015-2022 The ESPResSo Project # Copyright (C) 2011 Olaf Lenz -# Copyright (C) 2015,2020 The ESPResSo Project # # This file is part of ESPResSo. # diff --git a/cmake/option_enum.cmake b/cmake/option_enum.cmake index ed2a7d51ee2..2a013bfe57d 100644 --- a/cmake/option_enum.cmake +++ b/cmake/option_enum.cmake @@ -1,4 +1,5 @@ -# Copyright (C) 2020 The ESPResSo project +# +# Copyright (C) 2020-2022 The ESPResSo project # # This file is part of ESPResSo. # diff --git a/cmake/unit_test.cmake b/cmake/unit_test.cmake index 41c7577354a..1ea3b6da4b4 100644 --- a/cmake/unit_test.cmake +++ b/cmake/unit_test.cmake @@ -1,3 +1,22 @@ +# +# Copyright (C) 2016-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 . +# + # unit_test function function(UNIT_TEST) cmake_parse_arguments(TEST "" "NAME;NUM_PROC" "SRC;DEPENDS" ${ARGN}) diff --git a/doc/doxygen/gen_doxyconfig.py b/doc/doxygen/gen_doxyconfig.py index 9d5842febec..3938b1c8f16 100755 --- a/doc/doxygen/gen_doxyconfig.py +++ b/doc/doxygen/gen_doxyconfig.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# # Copyright (C) 2013-2022 The ESPResSo project # Copyright (C) 2012 Olaf Lenz # @@ -17,8 +18,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # + # This script generates the file doxy-features -# import sys import os diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index eb47e9409cd..c996d06f17d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,3 +1,4 @@ +# # Copyright (C) 2009-2022 The ESPResSo project # Copyright (C) 2009,2010 # Max-Planck-Institute for Polymer Research, Theory Group diff --git a/src/config/CMakeLists.txt b/src/config/CMakeLists.txt index e48cf2390ec..223ddba14ed 100644 --- a/src/config/CMakeLists.txt +++ b/src/config/CMakeLists.txt @@ -1,3 +1,22 @@ +# +# Copyright (C) 2015-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 . +# + configure_file(${CMAKE_SOURCE_DIR}/cmake/cmake_config.cmakein cmake_config.hpp) add_custom_command( diff --git a/src/config/defines.py b/src/config/defines.py index d0fe6197ab9..04699aaafaf 100644 --- a/src/config/defines.py +++ b/src/config/defines.py @@ -1,4 +1,5 @@ -# Copyright (C) 2010-2022 The ESPResSo project +# +# Copyright (C) 2017-2022 The ESPResSo project # # This file is part of ESPResSo. # @@ -14,6 +15,8 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +# + import tempfile import subprocess from sys import argv diff --git a/src/config/featuredefs.py b/src/config/featuredefs.py index c9c458b374c..e3b2ddd8d5c 100644 --- a/src/config/featuredefs.py +++ b/src/config/featuredefs.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# # Copyright (C) 2013-2022 The ESPResSo project # Copyright (C) 2012 Olaf Lenz # diff --git a/src/config/features.def b/src/config/features.def index 2324a462219..bc40e264682 100644 --- a/src/config/features.def +++ b/src/config/features.def @@ -1,3 +1,23 @@ +# +# Copyright (C) 2013-2022 The ESPResSo project +# Copyright (C) 2012 Olaf Lenz +# +# 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 . +# + # Feature definitions # # The definitions are used for diff --git a/src/config/gen_sampleconfig.py b/src/config/gen_sampleconfig.py index 761da065d17..7df1de2b785 100644 --- a/src/config/gen_sampleconfig.py +++ b/src/config/gen_sampleconfig.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 -# Copyright (C) 2013,2014,2018-2022 The ESPResSo project +# +# Copyright (C) 2013-2022 The ESPResSo project # Copyright (C) 2012 Olaf Lenz # # This file is part of ESPResSo. diff --git a/src/core/unit_tests/CMakeLists.txt b/src/core/unit_tests/CMakeLists.txt index 274bcf5af84..82465c13b89 100644 --- a/src/core/unit_tests/CMakeLists.txt +++ b/src/core/unit_tests/CMakeLists.txt @@ -1,3 +1,4 @@ +# # Copyright (C) 2010-2022 The ESPResSo project # Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 # Max-Planck-Institute for Polymer Research, Theory Group @@ -16,6 +17,7 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +# include(unit_test) diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index fc47951f55e..18c8c87959b 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -1,3 +1,22 @@ +# +# Copyright (C) 2015-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 . +# + # Configure pypresso for build dir set(PYTHON_DIR ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/src/scafacos/CMakeLists.txt b/src/scafacos/CMakeLists.txt index b7948326295..eeb89fca3ae 100644 --- a/src/scafacos/CMakeLists.txt +++ b/src/scafacos/CMakeLists.txt @@ -1,3 +1,22 @@ +# +# 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 . +# + add_library(espresso_scafacos SHARED src/Scafacos.cpp src/Coulomb.cpp src/Dipoles.cpp) add_library(espresso::scafacos ALIAS espresso_scafacos) diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index f777723e9cf..e7fac538888 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -1,3 +1,22 @@ +# +# 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 . +# + add_library(espresso_utils INTERFACE) add_library(espresso::utils ALIAS espresso_utils) add_library(espresso_utils_mpi INTERFACE) diff --git a/src/utils/tests/CMakeLists.txt b/src/utils/tests/CMakeLists.txt index cef8e0bfe04..4ed534db166 100644 --- a/src/utils/tests/CMakeLists.txt +++ b/src/utils/tests/CMakeLists.txt @@ -1,3 +1,24 @@ +# +# Copyright (C) 2010-2022 The ESPResSo project +# Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 +# Max-Planck-Institute for Polymer Research, Theory Group +# +# This file is part of ESPResSo. +# +# ESPResSo is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# ESPResSo is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + include(unit_test) unit_test(NAME abs_test SRC abs_test.cpp DEPENDS espresso::utils) diff --git a/testsuite/CMakeLists.txt b/testsuite/CMakeLists.txt index a3d03c96947..e82797962ce 100644 --- a/testsuite/CMakeLists.txt +++ b/testsuite/CMakeLists.txt @@ -1,3 +1,22 @@ +# +# Copyright (C) 2015-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(unit_test) if(WITH_COVERAGE_PYTHON) diff --git a/testsuite/cmake/CMakeLists.txt b/testsuite/cmake/CMakeLists.txt index 7535e71c043..6df6aa59faa 100644 --- a/testsuite/cmake/CMakeLists.txt +++ b/testsuite/cmake/CMakeLists.txt @@ -1,3 +1,22 @@ +# +# Copyright (C) 2018-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 . +# + function(CMAKE_TEST) cmake_parse_arguments(TEST "" "FILE" "DEPENDENCIES" ${ARGN}) get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE) diff --git a/testsuite/scripts/CMakeLists.txt b/testsuite/scripts/CMakeLists.txt index f2cb58b42c1..367d01a0e28 100644 --- a/testsuite/scripts/CMakeLists.txt +++ b/testsuite/scripts/CMakeLists.txt @@ -1,3 +1,22 @@ +# +# 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 . +# + set(TEST_FILE_CONFIGURED_IMPORTLIB_WRAPPER ${CMAKE_CURRENT_BINARY_DIR}/test_importlib_wrapper.py) configure_file(importlib_wrapper.py diff --git a/testsuite/scripts/benchmarks/CMakeLists.txt b/testsuite/scripts/benchmarks/CMakeLists.txt index 80fe1eecef1..e6e77411dee 100644 --- a/testsuite/scripts/benchmarks/CMakeLists.txt +++ b/testsuite/scripts/benchmarks/CMakeLists.txt @@ -1,3 +1,22 @@ +# +# 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 . +# + function(BENCHMARK_TEST) python_scripts_test(${ARGV} TYPE benchmark) # forward arguments set(benchmarks_tests ${benchmarks_tests} ${TEST_FILE_CONFIGURED} PARENT_SCOPE) diff --git a/testsuite/scripts/samples/CMakeLists.txt b/testsuite/scripts/samples/CMakeLists.txt index 6e3940336a8..fd6d08779ac 100644 --- a/testsuite/scripts/samples/CMakeLists.txt +++ b/testsuite/scripts/samples/CMakeLists.txt @@ -1,3 +1,22 @@ +# +# 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 . +# + function(SAMPLE_TEST) python_scripts_test(${ARGV} TYPE sample) # forward arguments set(samples_tests ${samples_tests} ${TEST_FILE_CONFIGURED} PARENT_SCOPE) diff --git a/testsuite/scripts/tutorials/CMakeLists.txt b/testsuite/scripts/tutorials/CMakeLists.txt index 8855f3b4d21..6ea9dfb57b8 100644 --- a/testsuite/scripts/tutorials/CMakeLists.txt +++ b/testsuite/scripts/tutorials/CMakeLists.txt @@ -1,3 +1,22 @@ +# +# 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 . +# + function(TUTORIAL_TEST) python_scripts_test(${ARGV} TYPE tutorial) # forward arguments set(tutorials_tests ${tutorials_tests} ${TEST_FILE_CONFIGURED} PARENT_SCOPE) From 6f9832e46cb34162d42d79cb9864f3df4c5bafbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 10 Aug 2022 11:28:41 +0200 Subject: [PATCH 31/85] maintainer: Update rules in header detection tool --- maintainer/files_with_header.sh | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/maintainer/files_with_header.sh b/maintainer/files_with_header.sh index 2e50dce2319..0508710610c 100755 --- a/maintainer/files_with_header.sh +++ b/maintainer/files_with_header.sh @@ -19,17 +19,13 @@ # along with this program. If not, see . # - # List all files that are eligible for a copyright header. git ls-files --exclude-standard | grep -vE '\.(blk|gz|npz|data|dat|tab|chk|jpg|png|pdf|fig|gif|xcf|css|bib|vtf|vtk|svg|ico|eps|rst|ipynb)$' | -grep -vE '^testsuite/configs/|^old/|^cmake/|^libs/' | -grep -vE '(ChangeLog|AUTHORS|COPYING|NEWS|README|INSTALL|README\.md|CONTRIBUTING\.md)' | -grep -vE '(\.gitmodules|\.github|\.gitignore|\.codecov\.yml|\.gitlab-ci\.yml|kodiak\.toml)' | -grep -vE '(\.clang-format|\.cmake-format|\.clang-tidy|\.coveragerc|\.pylintrc|ubsan\.supp)' | -grep -vE '(\.lgtm\.yml|\.pre-commit-config\.yaml|requirements\.txt)' | -grep -vE '(Doxyfile|latexmk\.1|latexmkrc|assemble_quickref\.awk)' | -grep -vE '(src/config/features\.def)' | -grep -vE '(featurelist)' | -grep -vE '(\.cproject|\.project|\.settings)' +grep -vE '(^testsuite/python/data/|^old/|^libs/|^samples/immersed_boundary/tables/)' | +grep -vE '(^ChangeLog|^AUTHORS|^COPYING|^NEWS|^INSTALL|^Readme\.md|^CONTRIBUTING\.md)' | +grep -vE '(^\.gitmodules|^\.github|^\.gitignore|^\.codecov\.yml|^\.gitlab-ci\.yml|^\.kodiak\.toml)' | +grep -vE '(^\.clang-format|^\.cmake-format|^\.clang-tidy|^\.coveragerc|^\.pylintrc|ubsan\.supp)' | +grep -vE '(^\.lgtm\.yml|^\.gitpod\.yml|^\.cppcheck|^\.pre-commit-config\.yaml|^requirements\.txt)' | +grep -vE '(Doxyfile|latexmk\.1|latexmkrc|^doc/tutorials/.*/NotesForTutor\.md)' From 8e0f7d9884e30d0c67ce386521da7149e3936669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 11 Aug 2022 16:13:23 +0200 Subject: [PATCH 32/85] script_interface: Simplify Cython bindings --- src/python/espressomd/interactions.pxd | 46 ++++++++++++++------------ src/python/espressomd/interactions.pyx | 23 ------------- src/python/espressomd/thermostat.pxd | 9 ----- src/python/espressomd/thermostat.pyx | 3 -- 4 files changed, 24 insertions(+), 57 deletions(-) diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index 4026e35e20c..88b3a4b5b07 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -19,7 +19,6 @@ # Handling of interactions -from libcpp.memory cimport shared_ptr from libcpp.string cimport string from libcpp.vector cimport vector from libc cimport stdint @@ -188,21 +187,23 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": Thole_Parameters thole - cdef IA_parameters * get_ia_param(int i, int j) cdef IA_parameters * get_ia_param_safe(int i, int j) cdef string ia_params_get_state() cdef void ia_params_set_state(string) cdef void reset_ia_params() -cdef extern from "nonbonded_interactions/lj.hpp": - cdef int lennard_jones_set_params(int part_type_a, int part_type_b, - double eps, double sig, double cut, - double shift, double offset, - double min) +IF LENNARD_JONES: + cdef extern from "nonbonded_interactions/lj.hpp": + cdef int lennard_jones_set_params(int part_type_a, int part_type_b, + double eps, double sig, double cut, + double shift, double offset, + double min) + +IF WCA: + cdef extern from "nonbonded_interactions/wca.hpp": + cdef int wca_set_params(int part_type_a, int part_type_b, + double eps, double sig) -cdef extern from "nonbonded_interactions/wca.hpp": - cdef int wca_set_params(int part_type_a, int part_type_b, - double eps, double sig) IF LJCOS: cdef extern from "nonbonded_interactions/ljcos.hpp": cdef int ljcos_set_params(int part_type_a, int part_type_b, @@ -226,18 +227,19 @@ IF THOLE: cdef extern from "nonbonded_interactions/thole.hpp": int thole_set_params(int part_type_a, int part_type_b, double scaling_coeff, double q1q2) -cdef extern from "nonbonded_interactions/ljgen.hpp": - IF LJGEN_SOFTCORE: - cdef int ljgen_set_params(int part_type_a, int part_type_b, - double eps, double sig, double cut, - double shift, double offset, - double a1, double a2, double b1, double b2, - double genlj_lambda, double softrad) - ELSE: - cdef int ljgen_set_params(int part_type_a, int part_type_b, - double eps, double sig, double cut, - double shift, double offset, - double a1, double a2, double b1, double b2) +IF LENNARD_JONES_GENERIC: + cdef extern from "nonbonded_interactions/ljgen.hpp": + IF LJGEN_SOFTCORE: + cdef int ljgen_set_params(int part_type_a, int part_type_b, + double eps, double sig, double cut, + double shift, double offset, + double a1, double a2, double b1, double b2, + double genlj_lambda, double softrad) + ELSE: + cdef int ljgen_set_params(int part_type_a, int part_type_b, + double eps, double sig, double cut, + double shift, double offset, + double a1, double a2, double b1, double b2) IF SMOOTH_STEP: cdef extern from "nonbonded_interactions/smooth_step.hpp": diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 6cf62e85a05..2344513c14b 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -1535,33 +1535,10 @@ class NonBondedInteractionHandle: """ - type1 = -1 - type2 = -1 - - # Here, one line per non-bonded ia - lennard_jones = None - lennard_jones_cos = None - lennard_jones_cos2 = None - generic_lennard_jones = None - smooth_step = None - bmhtf = None - morse = None - buckingham = None - soft_sphere = None - hertzian = None - gaussian = None - tabulated = None - gay_berne = None - dpd = None - hat = None - thole = None - def __init__(self, _type1, _type2): if not (utils.is_valid_type(_type1, int) and utils.is_valid_type(_type2, int)): raise TypeError("The particle types have to be of type integer.") - self.type1 = _type1 - self.type2 = _type2 # Here, add one line for each nonbonded ia IF LENNARD_JONES: diff --git a/src/python/espressomd/thermostat.pxd b/src/python/espressomd/thermostat.pxd index 3f7e6c830a3..7ad36753057 100644 --- a/src/python/espressomd/thermostat.pxd +++ b/src/python/espressomd/thermostat.pxd @@ -114,12 +114,3 @@ cdef extern from "stokesian_dynamics/sd_interface.hpp": IF STOKESIAN_DYNAMICS: void set_sd_kT(double kT) except + double get_sd_kT() - -IF NPT: - cdef extern from "npt.hpp": - ctypedef struct NptIsoParameters: - double p_ext - double p_inst - double p_diff - double piston - extern NptIsoParameters nptiso diff --git a/src/python/espressomd/thermostat.pyx b/src/python/espressomd/thermostat.pyx index 2af6fb25d18..1b8e63c1d68 100644 --- a/src/python/espressomd/thermostat.pyx +++ b/src/python/espressomd/thermostat.pyx @@ -18,8 +18,6 @@ # import functools include "myconfig.pxi" -IF NPT: - from .thermostat cimport nptiso from . cimport utils from .lb import HydrodynamicInteraction from .lb cimport lb_lbcoupling_set_gamma @@ -217,7 +215,6 @@ cdef class Thermostat: npt_dict["counter"] = npt_iso.rng_counter() npt_dict["gamma0"] = npt_iso.gamma0 npt_dict["gammav"] = npt_iso.gammav - npt_dict.update(nptiso) thermo_list.append(npt_dict) if thermo_switch & THERMO_DPD: IF DPD: From 67719975823849e7e2e35225eed91869c7bcd170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 11 Aug 2022 21:45:39 +0200 Subject: [PATCH 33/85] script_interface: Make CUDA interface parallel --- src/core/EspressoSystemInterface.cpp | 10 ----- src/core/EspressoSystemInterface.hpp | 7 ---- src/core/cuda_init.cpp | 12 +----- src/core/cuda_interface.cpp | 16 +------- src/core/cuda_interface.hpp | 6 +-- src/core/electrostatics/mmm1d_gpu.cpp | 8 ++-- src/core/electrostatics/p3m_gpu.cpp | 2 +- src/core/grid_based_algorithms/lbgpu.cpp | 18 +++++---- src/core/magnetostatics/barnes_hut_gpu.cpp | 7 ++-- src/core/magnetostatics/dds_gpu.cpp | 3 -- src/python/espressomd/cuda_init.py | 2 +- .../system/CudaInitHandle.cpp | 38 ++++++++++++------- 12 files changed, 52 insertions(+), 77 deletions(-) diff --git a/src/core/EspressoSystemInterface.cpp b/src/core/EspressoSystemInterface.cpp index 0b130ce0452..f6930d7e4e5 100644 --- a/src/core/EspressoSystemInterface.cpp +++ b/src/core/EspressoSystemInterface.cpp @@ -50,16 +50,6 @@ void EspressoSystemInterface::enableParticleCommunication() { } } } - -void EspressoSystemInterface::enableParticleCommunicationParallel() { - if (m_gpu) { - if (!gpu_get_global_particle_vars_pointer_host()->communication_enabled) { - gpu_init_particle_comm(this_node); - cuda_bcast_global_part_params_parallel(); - reallocDeviceMemory(gpu_get_particle_pointer().size()); - } - } -} #endif void EspressoSystemInterface::init() { gatherParticles(); } diff --git a/src/core/EspressoSystemInterface.hpp b/src/core/EspressoSystemInterface.hpp index 368cfc45b76..9cfde74c391 100644 --- a/src/core/EspressoSystemInterface.hpp +++ b/src/core/EspressoSystemInterface.hpp @@ -90,12 +90,6 @@ class EspressoSystemInterface : public SystemInterface { enableParticleCommunication(); } - void requestParticleStructGpuParallel() { - m_needsParticleStructGpu = true; - m_gpu |= m_needsParticleStructGpu; - enableParticleCommunicationParallel(); - } - float *fGpuBegin() override { return gpu_get_particle_force_pointer(); } bool hasFGpu() override { return true; } void requestFGpu() override { @@ -139,7 +133,6 @@ class EspressoSystemInterface : public SystemInterface { void split_particle_struct(); #ifdef CUDA void enableParticleCommunication(); - void enableParticleCommunicationParallel(); void reallocDeviceMemory(std::size_t n); private: diff --git a/src/core/cuda_init.cpp b/src/core/cuda_init.cpp index 64ff562b6fb..975ac802c6f 100644 --- a/src/core/cuda_init.cpp +++ b/src/core/cuda_init.cpp @@ -27,8 +27,6 @@ #include "communication.hpp" #include "errorhandling.hpp" -#include - #include #include @@ -56,7 +54,7 @@ struct CompareDevices { * of the physical node, as opposed to the logical rank of which there can * be more than one per node. */ -static std::vector mpi_cuda_gather_gpus_local() { +std::vector cuda_gather_gpus() { /* List of local devices */ std::vector devices_local; /* Global unique device list (only relevant on the head node) */ @@ -124,10 +122,4 @@ static std::vector mpi_cuda_gather_gpus_local() { return devices_global; } -REGISTER_CALLBACK_MAIN_RANK(mpi_cuda_gather_gpus_local) - -std::vector cuda_gather_gpus() { - return mpi_call(Communication::Result::main_rank, mpi_cuda_gather_gpus_local); -} - -#endif /* CUDA */ +#endif // CUDA diff --git a/src/core/cuda_interface.cpp b/src/core/cuda_interface.cpp index 221bfb2fe0a..01ebcc3bd26 100644 --- a/src/core/cuda_interface.cpp +++ b/src/core/cuda_interface.cpp @@ -154,22 +154,10 @@ void cuda_mpi_send_forces(const ParticleRange &particles, } } -static void cuda_bcast_global_part_params_local() { - MPI_Bcast(gpu_get_global_particle_vars_pointer_host(), - sizeof(CUDA_global_part_vars), MPI_BYTE, 0, comm_cart); - EspressoSystemInterface::Instance().requestParticleStructGpu(); -} - -REGISTER_CALLBACK(cuda_bcast_global_part_params_local) - void cuda_bcast_global_part_params() { - mpi_call_all(cuda_bcast_global_part_params_local); -} - -void cuda_bcast_global_part_params_parallel() { MPI_Bcast(gpu_get_global_particle_vars_pointer_host(), sizeof(CUDA_global_part_vars), MPI_BYTE, 0, comm_cart); - EspressoSystemInterface::Instance().requestParticleStructGpuParallel(); + EspressoSystemInterface::Instance().requestParticleStructGpu(); } -#endif /* ifdef CUDA */ +#endif // CUDA diff --git a/src/core/cuda_interface.hpp b/src/core/cuda_interface.hpp index 99642ce9c13..d9320ae1e9a 100644 --- a/src/core/cuda_interface.hpp +++ b/src/core/cuda_interface.hpp @@ -137,8 +137,8 @@ void copy_part_data_to_gpu(ParticleRange particles, int this_node); void cuda_mpi_send_forces(const ParticleRange &particles, Utils::Span host_forces, Utils::Span host_torques); -void cuda_bcast_global_part_params_parallel(); + void cuda_bcast_global_part_params(); -#endif /* ifdef CUDA */ -#endif /* ifdef CORE_CUDA_INTERFACE_HPP */ +#endif // CUDA +#endif diff --git a/src/core/electrostatics/mmm1d_gpu.cpp b/src/core/electrostatics/mmm1d_gpu.cpp index 0f83e4f3b5e..452741f9028 100644 --- a/src/core/electrostatics/mmm1d_gpu.cpp +++ b/src/core/electrostatics/mmm1d_gpu.cpp @@ -43,11 +43,11 @@ CoulombMMM1DGpu::CoulombMMM1DGpu(double prefactor, double maxPWerror, "switching radius must not be larger than box length"); } + auto &system = EspressoSystemInterface::Instance(); + system.requestFGpu(); + system.requestRGpu(); + system.requestQGpu(); if (this_node == 0) { - auto &system = EspressoSystemInterface::Instance(); - system.requestFGpu(); - system.requestRGpu(); - system.requestQGpu(); modpsi_init(); } } diff --git a/src/core/electrostatics/p3m_gpu.cpp b/src/core/electrostatics/p3m_gpu.cpp index 5f65bc991e6..085ad69f1e5 100644 --- a/src/core/electrostatics/p3m_gpu.cpp +++ b/src/core/electrostatics/p3m_gpu.cpp @@ -50,7 +50,7 @@ void CoulombP3MGPU::init_cpu_kernels() { CoulombP3M::init(); } void CoulombP3MGPU::request_gpu() const { auto &system = EspressoSystemInterface::Instance(); - system.requestParticleStructGpuParallel(); + system.requestParticleStructGpu(); } #endif // CUDA diff --git a/src/core/grid_based_algorithms/lbgpu.cpp b/src/core/grid_based_algorithms/lbgpu.cpp index e4508254871..bdc614f25a7 100644 --- a/src/core/grid_based_algorithms/lbgpu.cpp +++ b/src/core/grid_based_algorithms/lbgpu.cpp @@ -148,16 +148,20 @@ void lb_reinit_parameters_gpu() { /** Performs a full initialization of the lattice Boltzmann system. * All derived parameters and the fluid are reset to their default values. */ -void lb_init_gpu() { - /* set parameters for transfer to gpu */ - lb_reinit_parameters_gpu(); - - lb_init_GPU(lbpar_gpu); - +void lb_init_gpu_local() { + if (this_node == 0) { + /* set parameters for transfer to gpu */ + lb_reinit_parameters_gpu(); + lb_init_GPU(lbpar_gpu); + } gpu_init_particle_comm(this_node); cuda_bcast_global_part_params(); } +REGISTER_CALLBACK(lb_init_gpu_local) + +void lb_init_gpu() { mpi_call_all(lb_init_gpu_local); } + void lb_GPU_sanity_checks() { if (this_node == 0) { if (lbpar_gpu.agrid < 0.f) { @@ -205,4 +209,4 @@ void lb_set_agrid_gpu(double agrid) { std::multiplies()); } -#endif /* CUDA */ +#endif // CUDA diff --git a/src/core/magnetostatics/barnes_hut_gpu.cpp b/src/core/magnetostatics/barnes_hut_gpu.cpp index 102c41e46df..997aca0c14b 100644 --- a/src/core/magnetostatics/barnes_hut_gpu.cpp +++ b/src/core/magnetostatics/barnes_hut_gpu.cpp @@ -33,15 +33,14 @@ DipolarBarnesHutGpu::DipolarBarnesHutGpu(double prefactor, double epssq, double itolsq) : prefactor{prefactor}, m_epssq{epssq}, m_itolsq{itolsq} { - if (this_node != 0) { - return; - } auto &system = EspressoSystemInterface::Instance(); system.requestFGpu(); system.requestTorqueGpu(); system.requestRGpu(); system.requestDipGpu(); - setBHPrecision(static_cast(m_epssq), static_cast(m_itolsq)); + if (this_node == 0) { + setBHPrecision(static_cast(m_epssq), static_cast(m_itolsq)); + } } template diff --git a/src/core/magnetostatics/dds_gpu.cpp b/src/core/magnetostatics/dds_gpu.cpp index 1d4950f2712..1184186f88d 100644 --- a/src/core/magnetostatics/dds_gpu.cpp +++ b/src/core/magnetostatics/dds_gpu.cpp @@ -38,9 +38,6 @@ static void get_simulation_box(float *box, int *per) { DipolarDirectSumGpu::DipolarDirectSumGpu(double prefactor) : prefactor{prefactor} { - if (this_node != 0) { - return; - } auto &system = EspressoSystemInterface::Instance(); system.requestFGpu(); system.requestTorqueGpu(); diff --git a/src/python/espressomd/cuda_init.py b/src/python/espressomd/cuda_init.py index 6cfd844fe16..ac3aec17182 100644 --- a/src/python/espressomd/cuda_init.py +++ b/src/python/espressomd/cuda_init.py @@ -40,7 +40,7 @@ class CudaInitHandle(ScriptInterfaceHelper): """ _so_name = "System::CudaInitHandle" - _so_creation_policy = "LOCAL" + _so_creation_policy = "GLOBAL" _so_bind_methods = ("list_devices",) def list_devices_properties(self): diff --git a/src/script_interface/system/CudaInitHandle.cpp b/src/script_interface/system/CudaInitHandle.cpp index afbb9e8971a..b231f8c056f 100644 --- a/src/script_interface/system/CudaInitHandle.cpp +++ b/src/script_interface/system/CudaInitHandle.cpp @@ -35,8 +35,15 @@ namespace System { CudaInitHandle::CudaInitHandle() { add_parameters({ #ifdef CUDA - {"device", [](Variant const &v) { cuda_set_device(get_value(v)); }, - []() { return cuda_get_device(); }}, + {"device", + [this](Variant const &v) { + if (context()->is_head_node()) { + cuda_set_device(get_value(v)); + } + }, + [this]() { + return (context()->is_head_node()) ? cuda_get_device() : 0; + }}, #endif // CUDA }); } @@ -61,14 +68,17 @@ Variant CudaInitHandle::do_call_method(std::string const &name, if (name == "list_devices") { std::unordered_map devices{}; #ifdef CUDA - auto n_gpus = 0; - skip_cuda_errors([&n_gpus]() { n_gpus = cuda_get_n_gpus(); }); - for (int i = 0; i < n_gpus; ++i) { - skip_cuda_errors([&devices, i]() { - char gpu_name_buffer[4 + 64]; - cuda_get_gpu_name(i, gpu_name_buffer); - devices[i] = std::string{gpu_name_buffer}; - }); + if (context()->is_head_node()) { + // only GPUs on the head node can be used + auto n_gpus = 0; + skip_cuda_errors([&n_gpus]() { n_gpus = cuda_get_n_gpus(); }); + for (int i = 0; i < n_gpus; ++i) { + skip_cuda_errors([&devices, i]() { + char gpu_name_buffer[4 + 64]; + cuda_get_gpu_name(i, gpu_name_buffer); + devices[i] = std::string{gpu_name_buffer}; + }); + } } #endif // CUDA return make_unordered_map_of_variants(devices); @@ -76,8 +86,7 @@ Variant CudaInitHandle::do_call_method(std::string const &name, if (name == "list_devices_properties") { std::unordered_map> dict{}; #ifdef CUDA - std::vector devices; - skip_cuda_errors([&devices]() { devices = cuda_gather_gpus(); }); + std::vector devices = cuda_gather_gpus(); for (auto const &dev : devices) { auto const hostname = std::string{dev.proc_name}; if (dict.count(hostname) == 0) { @@ -99,7 +108,10 @@ Variant CudaInitHandle::do_call_method(std::string const &name, if (name == "get_n_gpus") { auto n_gpus = 0; #ifdef CUDA - skip_cuda_errors([&n_gpus]() { n_gpus = cuda_get_n_gpus(); }); + if (context()->is_head_node()) { + // only GPUs on the head node can be used + skip_cuda_errors([&n_gpus]() { n_gpus = cuda_get_n_gpus(); }); + } #endif // CUDA return n_gpus; } From e513209a035bbf3754f9dcb44eaf5dd455ed955a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 12 Aug 2022 16:31:32 +0200 Subject: [PATCH 34/85] script_interface: Rewrite integrate module --- src/core/EspressoSystemStandAlone.cpp | 9 +- src/core/forcecap.cpp | 20 +- src/core/forcecap.hpp | 4 +- src/core/grid.hpp | 2 +- src/core/integrate.cpp | 74 +-- src/core/integrate.hpp | 51 +-- src/core/integrators/steepest_descent.cpp | 27 +- src/core/integrators/steepest_descent.hpp | 17 +- src/core/integrators/velocity_verlet_npt.cpp | 15 +- src/core/integrators/velocity_verlet_npt.hpp | 3 +- src/core/npt.cpp | 107 ++--- src/core/npt.hpp | 55 +-- src/core/stokesian_dynamics/sd_interface.cpp | 53 +-- src/core/stokesian_dynamics/sd_interface.hpp | 24 +- src/core/tuning.cpp | 6 +- .../EspressoSystemStandAlone_test.cpp | 2 +- src/core/unit_tests/Verlet_list_test.cpp | 28 +- src/python/espressomd/integrate.pxd | 36 -- src/python/espressomd/integrate.pyx | 428 ++++-------------- src/script_interface/CMakeLists.txt | 1 + src/script_interface/initialize.cpp | 2 + .../integrators/BrownianDynamics.cpp | 32 ++ .../integrators/BrownianDynamics.hpp | 38 ++ .../integrators/CMakeLists.txt | 9 + .../integrators/Integrator.hpp | 40 ++ .../integrators/IntegratorHandle.cpp | 95 ++++ .../integrators/IntegratorHandle.hpp | 46 ++ .../integrators/SteepestDescent.cpp | 63 +++ .../integrators/SteepestDescent.hpp | 53 +++ .../integrators/StokesianDynamics.cpp | 103 +++++ .../integrators/StokesianDynamics.hpp | 58 +++ .../integrators/VelocityVerlet.cpp | 32 ++ .../integrators/VelocityVerlet.hpp | 38 ++ .../integrators/VelocityVerletIsoNPT.cpp | 75 +++ .../integrators/VelocityVerletIsoNPT.hpp | 57 +++ .../integrators/initialize.cpp | 47 ++ .../integrators/initialize.hpp | 35 ++ testsuite/python/integrator_exceptions.py | 12 +- testsuite/python/integrator_npt.py | 4 +- .../python/integrator_steepest_descent.py | 8 +- testsuite/python/stokesian_thermostat.py | 11 +- testsuite/python/test_checkpoint.py | 16 +- 42 files changed, 1123 insertions(+), 713 deletions(-) create mode 100644 src/script_interface/integrators/BrownianDynamics.cpp create mode 100644 src/script_interface/integrators/BrownianDynamics.hpp create mode 100644 src/script_interface/integrators/CMakeLists.txt create mode 100644 src/script_interface/integrators/Integrator.hpp create mode 100644 src/script_interface/integrators/IntegratorHandle.cpp create mode 100644 src/script_interface/integrators/IntegratorHandle.hpp create mode 100644 src/script_interface/integrators/SteepestDescent.cpp create mode 100644 src/script_interface/integrators/SteepestDescent.hpp create mode 100644 src/script_interface/integrators/StokesianDynamics.cpp create mode 100644 src/script_interface/integrators/StokesianDynamics.hpp create mode 100644 src/script_interface/integrators/VelocityVerlet.cpp create mode 100644 src/script_interface/integrators/VelocityVerlet.hpp create mode 100644 src/script_interface/integrators/VelocityVerletIsoNPT.cpp create mode 100644 src/script_interface/integrators/VelocityVerletIsoNPT.hpp create mode 100644 src/script_interface/integrators/initialize.cpp create mode 100644 src/script_interface/integrators/initialize.hpp diff --git a/src/core/EspressoSystemStandAlone.cpp b/src/core/EspressoSystemStandAlone.cpp index 1f948319ed4..e0649516d0d 100644 --- a/src/core/EspressoSystemStandAlone.cpp +++ b/src/core/EspressoSystemStandAlone.cpp @@ -58,7 +58,12 @@ static void mpi_set_node_grid_local(Utils::Vector3i const &node_grid) { on_node_grid_change(); } +static void mpi_set_time_step_local(double const &value) { + set_time_step(value); +} + REGISTER_CALLBACK(mpi_set_node_grid_local) +REGISTER_CALLBACK(mpi_set_time_step_local) void EspressoSystemStandAlone::set_box_l(Utils::Vector3d const &box_l) const { if (!head_node) @@ -76,11 +81,11 @@ void EspressoSystemStandAlone::set_node_grid( void EspressoSystemStandAlone::set_time_step(double time_step) const { if (!head_node) return; - mpi_set_time_step(time_step); + mpi_call_all(mpi_set_time_step_local, time_step); } void EspressoSystemStandAlone::set_skin(double new_skin) const { if (!head_node) return; - mpi_set_skin(new_skin); + mpi_call_all(mpi_set_skin_local, new_skin); } diff --git a/src/core/forcecap.cpp b/src/core/forcecap.cpp index 7141c6a37a6..c532856e3fe 100644 --- a/src/core/forcecap.cpp +++ b/src/core/forcecap.cpp @@ -25,40 +25,32 @@ #include "forcecap.hpp" #include "Particle.hpp" -#include "communication.hpp" #include "event.hpp" -#include #include #include static double force_cap = 0.0; -double forcecap_get() { return force_cap; } +double get_force_cap() { return ::force_cap; } void forcecap_cap(ParticleRange const &particles) { - if (force_cap <= 0) { + if (::force_cap <= 0) { return; } - auto const force_cap_sq = Utils::sqr(force_cap); + auto const force_cap_sq = Utils::sqr(::force_cap); for (auto &p : particles) { auto const force_sq = p.force().norm2(); if (force_sq > force_cap_sq) { - p.force() *= force_cap / std::sqrt(force_sq); + p.force() *= ::force_cap / std::sqrt(force_sq); } } } -void mpi_set_forcecap_local(double force_cap) { - ::force_cap = force_cap; +void set_force_cap(double value) { + ::force_cap = value; on_forcecap_change(); } - -REGISTER_CALLBACK(mpi_set_forcecap_local) - -void mpi_set_forcecap(double force_cap) { - mpi_call_all(mpi_set_forcecap_local, force_cap); -} diff --git a/src/core/forcecap.hpp b/src/core/forcecap.hpp index a490cb1ad5b..471e2bd169a 100644 --- a/src/core/forcecap.hpp +++ b/src/core/forcecap.hpp @@ -23,10 +23,10 @@ #include "ParticleRange.hpp" -double forcecap_get(); +double get_force_cap(); void forcecap_cap(ParticleRange const &particles); -void mpi_set_forcecap(double force_cap); +void set_force_cap(double value); #endif diff --git a/src/core/grid.hpp b/src/core/grid.hpp index 2a865f313c1..c053b25f851 100644 --- a/src/core/grid.hpp +++ b/src/core/grid.hpp @@ -106,6 +106,6 @@ LocalBox regular_decomposition(const BoxGeometry &box, Utils::Vector3i const &node_pos, Utils::Vector3i const &node_grid); -void mpi_set_box_length(Utils::Vector3d const &box_l); +void mpi_set_box_length(Utils::Vector3d const &value); #endif diff --git a/src/core/integrate.cpp b/src/core/integrate.cpp index 8c44353b3c0..a424f7c24d4 100644 --- a/src/core/integrate.cpp +++ b/src/core/integrate.cpp @@ -58,6 +58,7 @@ #include +#include #include #include @@ -448,7 +449,7 @@ int python_integrate(int n_steps, bool recalc_forces_par, auto const new_skin = std::min(0.4 * max_cut, *boost::min_element(cell_structure.max_cutoff()) - max_cut); - mpi_set_skin(new_skin); + mpi_call_all(mpi_set_skin_local, new_skin); } using Accumulators::auto_update; @@ -500,34 +501,6 @@ int mpi_integrate(int n_steps, int reuse_forces) { mpi_integrate_local, n_steps, reuse_forces); } -void integrate_set_steepest_descent(const double f_max, const double gamma, - const double max_displacement) { - steepest_descent_init(f_max, gamma, max_displacement); - mpi_set_integ_switch(INTEG_METHOD_STEEPEST_DESCENT); -} - -void integrate_set_nvt() { mpi_set_integ_switch(INTEG_METHOD_NVT); } - -void integrate_set_bd() { mpi_set_integ_switch(INTEG_METHOD_BD); } - -void integrate_set_sd() { - if (box_geo.periodic(0) || box_geo.periodic(1) || box_geo.periodic(2)) { - throw std::runtime_error( - "Stokesian Dynamics requires periodicity (False, False, False)"); - } - mpi_set_integ_switch(INTEG_METHOD_SD); -} - -#ifdef NPT -void integrate_set_npt_isotropic(double ext_pressure, double piston, - bool xdir_rescale, bool ydir_rescale, - bool zdir_rescale, bool cubic_box) { - nptiso_init(ext_pressure, piston, xdir_rescale, ydir_rescale, zdir_rescale, - cubic_box); - mpi_set_integ_switch(INTEG_METHOD_NPT_ISO); -} -#endif - double interaction_range() { /* Consider skin only if there are actually interactions */ auto const max_cut = maximal_cutoff(n_nodes == 1); @@ -542,47 +515,30 @@ double get_sim_time() { return sim_time; } void increment_sim_time(double amount) { sim_time += amount; } -void mpi_set_time_step_local(double dt) { - time_step = dt; - on_timestep_change(); -} - -REGISTER_CALLBACK(mpi_set_time_step_local) - -void mpi_set_time_step(double time_s) { - if (time_s <= 0.) +void set_time_step(double value) { + if (value <= 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); + check_tau_time_step_consistency(lb_lbfluid_get_tau(), value); + ::time_step = value; + on_timestep_change(); } -void mpi_set_skin_local(double skin) { - ::skin = skin; +void mpi_set_skin_local(double value) { + ::skin = value; skin_set = true; on_skin_change(); } REGISTER_CALLBACK(mpi_set_skin_local) -void mpi_set_skin(double skin) { mpi_call_all(mpi_set_skin_local, skin); } - -void mpi_set_time_local(double time) { - sim_time = time; - recalc_forces = true; +void set_time(double value) { + ::sim_time = value; + ::recalc_forces = true; LeesEdwards::update_box_params(); } -REGISTER_CALLBACK(mpi_set_time_local) - -void mpi_set_time(double time) { mpi_call_all(mpi_set_time_local, time); } - -void mpi_set_integ_switch_local(int integ_switch) { - ::integ_switch = integ_switch; -} - -REGISTER_CALLBACK(mpi_set_integ_switch_local) - -void mpi_set_integ_switch(int integ_switch) { - mpi_call_all(mpi_set_integ_switch_local, integ_switch); +void set_integ_switch(int value) { + ::integ_switch = value; + ::recalc_forces = true; } diff --git a/src/core/integrate.hpp b/src/core/integrate.hpp index 06ce2b2519c..adfc53f0347 100644 --- a/src/core/integrate.hpp +++ b/src/core/integrate.hpp @@ -122,38 +122,6 @@ int mpi_integrate(int n_steps, int reuse_forces); */ int mpi_steepest_descent(int steps); -/** @brief Set the steepest descent integrator for energy minimization. */ -void integrate_set_steepest_descent(double f_max, double gamma, - double max_displacement); - -/** @brief Set the velocity Verlet integrator for the NVT ensemble. */ -void integrate_set_nvt(); - -/** @brief Set the Brownian Dynamics integrator. */ -void integrate_set_bd(); - -/** @brief Set the Stokesian Dynamics integrator. */ -void integrate_set_sd(); - -#ifdef NPT -/** @brief Set the velocity Verlet integrator modified for the NpT ensemble - * with isotropic rescaling. - * - * @param ext_pressure Reference pressure - * @param piston Piston mass - * @param xdir_rescale Enable box rescaling in the *x*-direction - * @param ydir_rescale Enable box rescaling in the *y*-direction - * @param zdir_rescale Enable box rescaling in the *z*-direction - * @param cubic_box Determines if the volume fluctuations should also - * apply to dimensions which are switched off by the - * above flags and which do not contribute to the - * pressure (3D) or tension (2D, 1D) - */ -void integrate_set_npt_isotropic(double ext_pressure, double piston, - bool xdir_rescale, bool ydir_rescale, - bool zdir_rescale, bool cubic_box); -#endif - /** Get @c verlet_reuse */ double get_verlet_reuse(); @@ -166,20 +134,15 @@ double get_sim_time(); /** Increase simulation time (only on head node) */ void increment_sim_time(double amount); -/** Send new \ref time_step and rescale the velocities accordingly. */ -void mpi_set_time_step(double time_step); +/** Set new @ref time_step. */ +void set_time_step(double value); -/** @brief Set and broadcast the skin - * @param skin skin value - */ -void mpi_set_skin(double skin); -void mpi_set_skin_local(double skin); +/** @brief Set new skin. */ +void mpi_set_skin_local(double value); -/** @brief Set and broadcast the time - * @param time time - */ -void mpi_set_time(double time); +/** @brief Set the simulation time. */ +void set_time(double value); -void mpi_set_integ_switch(int integ_switch); +void set_integ_switch(int value); #endif diff --git a/src/core/integrators/steepest_descent.cpp b/src/core/integrators/steepest_descent.cpp index afeb6315aa8..d696dacae3f 100644 --- a/src/core/integrators/steepest_descent.cpp +++ b/src/core/integrators/steepest_descent.cpp @@ -33,15 +33,15 @@ #include #include -#include #include #include #include #include +#include /** Currently active steepest descent instance */ -static SteepestDescentParameters params{}; +static SteepestDescentParameters params{0., 0., 0.}; bool steepest_descent_step(const ParticleRange &particles) { // Maximal force encountered on node @@ -97,26 +97,19 @@ bool steepest_descent_step(const ParticleRange &particles) { cell_structure.set_resort_particles(Cells::RESORT_LOCAL); // Synchronize maximum force/torque encountered - namespace mpi = boost::mpi; auto const f_max_global = - mpi::all_reduce(comm_cart, f_max, mpi::maximum()); + boost::mpi::all_reduce(comm_cart, f_max, boost::mpi::maximum()); return sqrt(f_max_global) < params.f_max; } -void mpi_bcast_steepest_descent_local() { - boost::mpi::broadcast(comm_cart, params, 0); +void register_integrator(SteepestDescentParameters const &obj) { + ::params = obj; } -REGISTER_CALLBACK(mpi_bcast_steepest_descent_local) - -/** Broadcast steepest descent parameters */ -void mpi_bcast_steepest_descent() { - mpi_call_all(mpi_bcast_steepest_descent_local); -} - -void steepest_descent_init(const double f_max, const double gamma, - const double max_displacement) { +SteepestDescentParameters::SteepestDescentParameters( + const double f_max, const double gamma, const double max_displacement) + : f_max{f_max}, gamma{gamma}, max_displacement{max_displacement} { if (f_max < 0.0) { throw std::runtime_error("The maximal force must be positive."); } @@ -126,8 +119,4 @@ void steepest_descent_init(const double f_max, const double gamma, if (max_displacement < 0.0) { throw std::runtime_error("The maximal displacement must be positive."); } - - params = SteepestDescentParameters{f_max, gamma, max_displacement}; - - mpi_bcast_steepest_descent(); } diff --git a/src/core/integrators/steepest_descent.hpp b/src/core/integrators/steepest_descent.hpp index 02b7bba1e09..beb33538c1f 100644 --- a/src/core/integrators/steepest_descent.hpp +++ b/src/core/integrators/steepest_descent.hpp @@ -24,8 +24,6 @@ #include "ParticleRange.hpp" -#include - /** Parameters for the steepest descent algorithm */ struct SteepestDescentParameters { /** Maximal particle force @@ -43,20 +41,11 @@ struct SteepestDescentParameters { */ double max_displacement; -private: - friend boost::serialization::access; - template void serialize(Archive &ar, long int /* version */) { - ar &f_max; - ar γ - ar &max_displacement; - } + SteepestDescentParameters(double f_max, double gamma, + double max_displacement); }; -/** Steepest descent initializer - * - * Sets the parameters in @ref SteepestDescentParameters - */ -void steepest_descent_init(double f_max, double gamma, double max_displacement); +void register_integrator(SteepestDescentParameters const &obj); /** Steepest descent integrator * @return whether the maximum force/torque encountered is below the user diff --git a/src/core/integrators/velocity_verlet_npt.cpp b/src/core/integrators/velocity_verlet_npt.cpp index c73c41aed20..cd2e71afce2 100644 --- a/src/core/integrators/velocity_verlet_npt.cpp +++ b/src/core/integrators/velocity_verlet_npt.cpp @@ -43,6 +43,8 @@ #include #include +static constexpr Utils::Vector3i nptgeom_dir{{1, 2, 4}}; + void velocity_verlet_npt_propagate_vel_final(const ParticleRange &particles, double time_step) { nptiso.p_vel = {}; @@ -54,7 +56,7 @@ void velocity_verlet_npt_propagate_vel_final(const ParticleRange &particles, auto const noise = friction_therm0_nptiso<2>(npt_iso, p.v(), p.id()); for (int j = 0; j < 3; j++) { if (!p.is_fixed_along(j)) { - if (nptiso.geometry & nptiso.nptgeom_dir[j]) { + if (nptiso.geometry & ::nptgeom_dir[j]) { 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 { @@ -71,7 +73,7 @@ void velocity_verlet_npt_finalize_p_inst(double time_step) { /* finalize derivation of p_inst */ nptiso.p_inst = 0.0; for (int i = 0; i < 3; i++) { - if (nptiso.geometry & nptiso.nptgeom_dir[i]) { + if (nptiso.geometry & ::nptgeom_dir[i]) { nptiso.p_vel[i] /= Utils::sqr(time_step); nptiso.p_inst += nptiso.p_vir[i] + nptiso.p_vel[i]; } @@ -124,7 +126,7 @@ void velocity_verlet_npt_propagate_pos(const ParticleRange &particles, continue; for (int j = 0; j < 3; j++) { if (!p.is_fixed_along(j)) { - if (nptiso.geometry & nptiso.nptgeom_dir[j]) { + if (nptiso.geometry & ::nptgeom_dir[j]) { 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]; @@ -145,7 +147,7 @@ void velocity_verlet_npt_propagate_pos(const ParticleRange &particles, new_box = box_geo.length(); for (int i = 0; i < 3; i++) { - if (nptiso.cubic_box || nptiso.geometry & nptiso.nptgeom_dir[i]) { + if (nptiso.cubic_box || nptiso.geometry & ::nptgeom_dir[i]) { new_box[i] = L_new; } } @@ -173,8 +175,7 @@ void velocity_verlet_npt_propagate_vel(const ParticleRange &particles, for (int j = 0; j < 3; j++) { if (!p.is_fixed_along(j)) { auto const noise = friction_therm0_nptiso<1>(npt_iso, p.v(), p.id()); - if (integ_switch == INTEG_METHOD_NPT_ISO && - (nptiso.geometry & nptiso.nptgeom_dir[j])) { + if (nptiso.geometry & ::nptgeom_dir[j]) { 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 { @@ -201,4 +202,4 @@ void velocity_verlet_npt_step_2(const ParticleRange &particles, #endif velocity_verlet_npt_finalize_p_inst(time_step); } -#endif +#endif // NPT diff --git a/src/core/integrators/velocity_verlet_npt.hpp b/src/core/integrators/velocity_verlet_npt.hpp index 7458dd34b42..338f7f2cedd 100644 --- a/src/core/integrators/velocity_verlet_npt.hpp +++ b/src/core/integrators/velocity_verlet_npt.hpp @@ -22,6 +22,7 @@ #include "config.hpp" #ifdef NPT + #include "ParticleRange.hpp" /** Special propagator for NpT isotropic. @@ -43,6 +44,6 @@ void velocity_verlet_npt_step_1(const ParticleRange &particles, */ void velocity_verlet_npt_step_2(const ParticleRange &particles, double time_step); -#endif +#endif // NPT #endif diff --git a/src/core/npt.cpp b/src/core/npt.cpp index 032951eecc9..85c85f7cded 100644 --- a/src/core/npt.cpp +++ b/src/core/npt.cpp @@ -36,19 +36,9 @@ #include #include -NptIsoParameters nptiso = {0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - {0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0}, - 0, - {NPTGEOM_XDIR, NPTGEOM_YDIR, NPTGEOM_ZDIR}, - 0, - false, - 0}; +static constexpr Utils::Vector3i nptgeom_dir{{1, 2, 4}}; + +NptIsoParameters nptiso = {}; void synchronize_npt_state() { boost::mpi::broadcast(comm_cart, nptiso.p_inst, 0); @@ -56,98 +46,69 @@ void synchronize_npt_state() { boost::mpi::broadcast(comm_cart, nptiso.volume, 0); } -void mpi_bcast_nptiso_geom_barostat_local() { - boost::mpi::broadcast(comm_cart, nptiso.geometry, 0); - boost::mpi::broadcast(comm_cart, nptiso.dimension, 0); - boost::mpi::broadcast(comm_cart, nptiso.cubic_box, 0); - boost::mpi::broadcast(comm_cart, nptiso.non_const_dim, 0); - boost::mpi::broadcast(comm_cart, nptiso.p_ext, 0); - boost::mpi::broadcast(comm_cart, nptiso.piston, 0); - on_thermostat_param_change(); -} - -REGISTER_CALLBACK(mpi_bcast_nptiso_geom_barostat_local) - -/** Broadcast nptiso geometry and barostat parameters to all nodes. */ -void mpi_bcast_nptiso_geom_barostat() { - mpi_call_all(mpi_bcast_nptiso_geom_barostat_local); -} - -void integrator_npt_coulomb_dipole_sanity_checks( - NptIsoParameters const ¶ms) { +void NptIsoParameters::coulomb_dipole_sanity_checks() const { #ifdef ELECTROSTATICS - if (params.dimension < 3 && !params.cubic_box && electrostatics_actor) { + if (dimension < 3 && !cubic_box && electrostatics_actor) { throw std::runtime_error("If electrostatics is being used you must " "use the cubic box NpT."); } #endif #ifdef DIPOLES - if (params.dimension < 3 && !params.cubic_box && magnetostatics_actor) { + if (dimension < 3 && !cubic_box && magnetostatics_actor) { throw std::runtime_error("If magnetostatics is being used you must " "use the cubic box NpT."); } #endif } -void nptiso_init(double ext_pressure, double piston, bool xdir_rescale, - bool ydir_rescale, bool zdir_rescale, bool cubic_box) { +NptIsoParameters::NptIsoParameters(double ext_pressure, double piston, + Utils::Vector const &rescale, + bool cubic_box) + : piston{piston}, p_ext{ext_pressure}, cubic_box{cubic_box} { if (ext_pressure < 0.0) { - throw std::runtime_error("The external pressure must be positive."); + throw std::runtime_error("The external pressure must be positive"); } if (piston <= 0.0) { - throw std::runtime_error("The piston mass must be positive."); + throw std::runtime_error("The piston mass must be positive"); } - NptIsoParameters new_nptiso = {piston, - nptiso.inv_piston, - nptiso.volume, - ext_pressure, - nptiso.p_inst, - nptiso.p_diff, - nptiso.p_vir, - nptiso.p_vel, - 0, - nptiso.nptgeom_dir, - 0, - cubic_box, - -1}; + inv_piston = ::nptiso.inv_piston; + volume = ::nptiso.volume; + p_inst = ::nptiso.p_inst; + p_diff = ::nptiso.p_diff; + p_vir = ::nptiso.p_vir; + p_vel = ::nptiso.p_vel; /* set the NpT geometry */ - if (xdir_rescale) { - new_nptiso.geometry |= NPTGEOM_XDIR; - new_nptiso.dimension += 1; - new_nptiso.non_const_dim = 0; - } - if (ydir_rescale) { - new_nptiso.geometry |= NPTGEOM_YDIR; - new_nptiso.dimension += 1; - new_nptiso.non_const_dim = 1; - } - if (zdir_rescale) { - new_nptiso.geometry |= NPTGEOM_ZDIR; - new_nptiso.dimension += 1; - new_nptiso.non_const_dim = 2; + for (auto const i : {0, 1, 2}) { + if (rescale[i]) { + geometry |= ::nptgeom_dir[i]; + dimension += 1; + non_const_dim = i; + } } - if (new_nptiso.dimension == 0 || new_nptiso.non_const_dim == -1) { + if (dimension == 0 or non_const_dim == -1) { throw std::runtime_error( "You must enable at least one of the x y z components " - "as fluctuating dimension(s) for box length motion!"); + "as fluctuating dimension(s) for box length motion"); } - integrator_npt_coulomb_dipole_sanity_checks(new_nptiso); - - nptiso = new_nptiso; + coulomb_dipole_sanity_checks(); +} - mpi_bcast_nptiso_geom_barostat(); +Utils::Vector NptIsoParameters::get_direction() const { + return {static_cast(geometry & ::nptgeom_dir[0]), + static_cast(geometry & ::nptgeom_dir[1]), + static_cast(geometry & ::nptgeom_dir[2])}; } void npt_ensemble_init(const BoxGeometry &box) { if (integ_switch == INTEG_METHOD_NPT_ISO) { /* prepare NpT-integration */ - nptiso.inv_piston = 1 / nptiso.piston; + nptiso.inv_piston = 1. / nptiso.piston; nptiso.volume = pow(box.length()[nptiso.non_const_dim], nptiso.dimension); if (recalc_forces) { nptiso.p_inst = 0.0; @@ -160,7 +121,7 @@ void npt_ensemble_init(const BoxGeometry &box) { void integrator_npt_sanity_checks() { if (integ_switch == INTEG_METHOD_NPT_ISO) { try { - integrator_npt_coulomb_dipole_sanity_checks(nptiso); + nptiso.coulomb_dipole_sanity_checks(); } catch (std::runtime_error const &err) { runtimeErrorMsg() << err.what(); } diff --git a/src/core/npt.hpp b/src/core/npt.hpp index 502892d60d3..3c99c8119a4 100644 --- a/src/core/npt.hpp +++ b/src/core/npt.hpp @@ -34,74 +34,53 @@ /** Parameters of the isotropic NpT-integration scheme. */ struct NptIsoParameters { + NptIsoParameters() = default; + NptIsoParameters(double ext_pressure, double piston, + Utils::Vector const &rescale, bool cubic_box); /** mass of a virtual piston representing the shaken box */ - double piston; + double piston = 0.; /** inverse of \ref piston */ - double inv_piston; + double inv_piston = 0.; /** isotropic volume. Note that we use the term volume throughout, * although for a 2d or 1d system we mean Area and Length respectively */ - double volume; + double volume = 0.; /** desired pressure to which the algorithm strives to */ - double p_ext; + double p_ext = 0.; /** instantaneous pressure the system currently has */ - double p_inst; + double p_inst = 0.; /** difference between \ref p_ext and \ref p_inst */ - double p_diff; + double p_diff = 0.; /** virial (short-range) components of \ref p_inst */ - Utils::Vector3d p_vir; + Utils::Vector3d p_vir = {0., 0., 0.}; /** ideal gas components of \ref p_inst, derived from the velocities */ - Utils::Vector3d p_vel; + Utils::Vector3d p_vel = {0., 0., 0.}; /** geometry information for the NpT integrator. Holds the vector * \< dir, dir, dir \> where a positive value for dir indicates that * box movement is allowed in that direction. To check whether a * given direction is turned on, use bitwise comparison with \ref * nptgeom_dir */ - int geometry; - /** bitwise comparison values corresponding to different directions */ - Utils::Vector3i nptgeom_dir; + int geometry = 0; /** The number of dimensions in which NpT boxlength motion is coupled to * particles */ - int dimension; + int dimension = 0; /** Set this flag if you want all box dimensions to be identical. Needed for * electrostatics and magnetostatics. If the value of \ref dimension is * less than 3, then box length motion in one or more directions will * be decoupled from the particle motion */ - bool cubic_box; + bool cubic_box = false; /** An index to one of the non-constant dimensions. Handy if you just want * the variable box_l */ - int non_const_dim; + int non_const_dim = -1; + void coulomb_dipole_sanity_checks() const; + Utils::Vector get_direction() const; }; extern NptIsoParameters nptiso; -/** @brief NpT initializer. - * - * @param ext_pressure Reference pressure - * @param piston Piston mass - * @param xdir_rescale Enable box rescaling in the *x*-direction - * @param ydir_rescale Enable box rescaling in the *y*-direction - * @param zdir_rescale Enable box rescaling in the *z*-direction - * @param cubic_box Determines if the volume fluctuations should also - * apply to dimensions which are switched off by the - * above flags and which do not contribute to the - * pressure (3D) or tension (2D, 1D) - */ -void nptiso_init(double ext_pressure, double piston, bool xdir_rescale, - bool ydir_rescale, bool zdir_rescale, bool cubic_box); - -/** @name NpT geometry bitmasks. - * Allowed values for @ref NptIsoParameters::geometry. - */ -/**@{*/ -#define NPTGEOM_XDIR 1 -#define NPTGEOM_YDIR 2 -#define NPTGEOM_ZDIR 4 -/**@}*/ - /** @brief Synchronizes NpT state such as instantaneous and average pressure */ void synchronize_npt_state(); diff --git a/src/core/stokesian_dynamics/sd_interface.cpp b/src/core/stokesian_dynamics/sd_interface.cpp index 8f67841882c..1b3ad925ba9 100644 --- a/src/core/stokesian_dynamics/sd_interface.cpp +++ b/src/core/stokesian_dynamics/sd_interface.cpp @@ -26,6 +26,7 @@ #include "Particle.hpp" #include "ParticleRange.hpp" +#include "grid.hpp" #include "thermostat.hpp" #include @@ -44,7 +45,6 @@ #include #include -namespace { /* type for particle data transfer between nodes */ struct SD_particle_data { SD_particle_data() = default; @@ -66,21 +66,23 @@ struct SD_particle_data { } }; -double sd_viscosity = -1.0; - -std::unordered_map radius_dict; +BOOST_IS_BITWISE_SERIALIZABLE(SD_particle_data) -double sd_kT = 0.0; +static StokesianDynamicsParameters params{0., {}, 0}; -int sd_flags = 0; +static double sd_kT = 0.0; /** Buffer that holds the (translational and angular) velocities of the local * particles on each node, used for returning results. */ -std::vector v_sd{}; - -} // namespace +static std::vector v_sd{}; -BOOST_IS_BITWISE_SERIALIZABLE(SD_particle_data) +void register_integrator(StokesianDynamicsParameters const &obj) { + if (::box_geo.periodic(0) or ::box_geo.periodic(1) or ::box_geo.periodic(2)) { + throw std::runtime_error( + "Stokesian Dynamics requires periodicity (False, False, False)"); + } + ::params = obj; +} /** Update translational and rotational velocities of all particles. */ void sd_update_locally(ParticleRange const &parts) { @@ -110,31 +112,26 @@ void sd_update_locally(ParticleRange const &parts) { } } -void set_sd_viscosity(double eta) { - if (eta < 0.0) { - throw std::runtime_error("Viscosity has an invalid value: " + - std::to_string(eta)); +StokesianDynamicsParameters::StokesianDynamicsParameters( + double viscosity, std::unordered_map radii, int flags) + : viscosity{viscosity}, radii{radii}, flags{flags} { + if (viscosity < 0.) { + throw std::domain_error("Viscosity has an invalid value: " + + std::to_string(viscosity)); } - - sd_viscosity = eta; -} - -void set_sd_radius_dict(std::unordered_map const &x) { /* Check that radii are positive */ - for (auto const &kv : x) { + for (auto const &kv : radii) { if (kv.second < 0.) { - throw std::runtime_error( + throw std::domain_error( "Particle radius for type " + std::to_string(kv.first) + " has an invalid value: " + std::to_string(kv.second)); } } - - radius_dict = x; } void set_sd_kT(double kT) { if (kT < 0.0) { - throw std::runtime_error("kT has an invalid value: " + std::to_string(kT)); + throw std::domain_error("kT has an invalid value: " + std::to_string(kT)); } sd_kT = kT; @@ -142,8 +139,6 @@ void set_sd_kT(double kT) { double get_sd_kT() { return sd_kT; } -void set_sd_flags(int flg) { sd_flags = flg; } - void propagate_vel_pos_sd(const ParticleRange &particles, const boost::mpi::communicator &comm, const double time_step) { @@ -185,17 +180,17 @@ void propagate_vel_pos_sd(const ParticleRange &particles, f_host[6 * i + 4] = p.ext_force.torque[1]; f_host[6 * i + 5] = p.ext_force.torque[2]; - double radius = radius_dict[p.type]; + double radius = params.radii[p.type]; a_host[i] = radius; ++i; } - v_sd = sd_cpu(x_host, f_host, a_host, n_part, sd_viscosity, + v_sd = sd_cpu(x_host, f_host, a_host, n_part, params.viscosity, std::sqrt(sd_kT / time_step), static_cast(stokesian.rng_counter()), - static_cast(stokesian.rng_seed()), sd_flags); + static_cast(stokesian.rng_seed()), params.flags); } else { // if (this_node == 0) v_sd.resize(particles.size() * 6); } // if (this_node == 0) {...} else diff --git a/src/core/stokesian_dynamics/sd_interface.hpp b/src/core/stokesian_dynamics/sd_interface.hpp index 422469118a2..73df6f2d8a3 100644 --- a/src/core/stokesian_dynamics/sd_interface.hpp +++ b/src/core/stokesian_dynamics/sd_interface.hpp @@ -28,21 +28,34 @@ #include "config.hpp" #ifdef STOKESIAN_DYNAMICS + #include "ParticleRange.hpp" #include #include -void set_sd_viscosity(double eta); +struct StokesianDynamicsParameters { + double viscosity; + std::unordered_map radii; + int flags; + StokesianDynamicsParameters(double viscosity, + std::unordered_map radii, int flags); +}; + +enum class sd_flags : int { + NONE = 0, + SELF_MOBILITY = 1 << 0, + PAIR_MOBILITY = 1 << 1, + LUBRICATION = 1 << 2, + FTS = 1 << 3 +}; -void set_sd_radius_dict(std::unordered_map const &x); +void register_integrator(StokesianDynamicsParameters const &obj); void set_sd_kT(double kT); double get_sd_kT(); -void set_sd_flags(int flg); - /** Takes the forces and torques on all particles and computes their * velocities. Acts globally on particles on all nodes; i.e. particle data * is gathered from all nodes and their velocities and angular velocities are @@ -53,5 +66,4 @@ void propagate_vel_pos_sd(const ParticleRange &particles, double time_step); #endif // STOKESIAN_DYNAMICS - -#endif // STOKESIAN_DYNAMICS_INTERFACE_H +#endif diff --git a/src/core/tuning.cpp b/src/core/tuning.cpp index cbdc6fdd2bd..18c61459815 100644 --- a/src/core/tuning.cpp +++ b/src/core/tuning.cpp @@ -136,10 +136,10 @@ void tune_skin(double min_skin, double max_skin, double tol, int int_steps, b = max_permissible_skin; while (fabs(a - b) > tol) { - mpi_set_skin(a); + mpi_call_all(mpi_set_skin_local, a); auto const time_a = time_calc(int_steps); - mpi_set_skin(b); + mpi_call_all(mpi_set_skin_local, b); auto const time_b = time_calc(int_steps); if (time_a > time_b) { @@ -149,5 +149,5 @@ void tune_skin(double min_skin, double max_skin, double tol, int int_steps, } } auto const new_skin = 0.5 * (a + b); - mpi_set_skin(new_skin); + mpi_call_all(mpi_set_skin_local, new_skin); } diff --git a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp index 3f69a1764c1..94cbc51a24d 100644 --- a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp +++ b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp @@ -308,7 +308,7 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, auto const skin = 0.4; espresso::system->set_time_step(time_step); espresso::system->set_skin(skin); - integrate_set_nvt(); + set_integ_switch(INTEG_METHOD_NVT); // reset system mpi_remove_translational_motion(); diff --git a/src/core/unit_tests/Verlet_list_test.cpp b/src/core/unit_tests/Verlet_list_test.cpp index 26377c7796f..ec9f840f13d 100644 --- a/src/core/unit_tests/Verlet_list_test.cpp +++ b/src/core/unit_tests/Verlet_list_test.cpp @@ -33,10 +33,14 @@ namespace utf = boost::unit_test; namespace bdata = boost::unit_test::data; #include "EspressoSystemStandAlone.hpp" +#include "MpiCallbacks.hpp" #include "Particle.hpp" #include "ParticleFactory.hpp" +#include "communication.hpp" #include "integrate.hpp" +#include "integrators/steepest_descent.hpp" #include "nonbonded_interactions/lj.hpp" +#include "npt.hpp" #include "particle_node.hpp" #include "thermostat.hpp" @@ -84,10 +88,17 @@ struct IntegratorHelper { } }; +void mpi_set_integrator_sd_local() { + register_integrator(SteepestDescentParameters(0., 0.01, 100.)); + set_integ_switch(INTEG_METHOD_STEEPEST_DESCENT); +} + +REGISTER_CALLBACK(mpi_set_integrator_sd_local) + struct : public IntegratorHelper { void set_integrator() const override { mpi_set_thermo_switch(THERMO_OFF); - integrate_set_steepest_descent(0., 0.01, 100.); + mpi_call_all(mpi_set_integrator_sd_local); } void set_particle_properties(int pid) const override { set_particle_ext_force(pid, {20., 0., 0.}); @@ -95,10 +106,14 @@ struct : public IntegratorHelper { char const *name() const override { return "SteepestDescent"; } } steepest_descent; +void mpi_set_integrator_vv_local() { set_integ_switch(INTEG_METHOD_NVT); } + +REGISTER_CALLBACK(mpi_set_integrator_vv_local) + struct : public IntegratorHelper { void set_integrator() const override { mpi_set_thermo_switch(THERMO_OFF); - integrate_set_nvt(); + mpi_call_all(mpi_set_integrator_vv_local); } void set_particle_properties(int pid) const override { set_particle_v(pid, {20., 0., 0.}); @@ -107,9 +122,16 @@ struct : public IntegratorHelper { } velocity_verlet; #ifdef NPT +void mpi_set_integrator_npt_local() { + ::nptiso = NptIsoParameters(1., 1e9, {true, true, true}, true); + set_integ_switch(INTEG_METHOD_NPT_ISO); +} + +REGISTER_CALLBACK(mpi_set_integrator_npt_local) + struct : public IntegratorHelper { void set_integrator() const override { - integrate_set_npt_isotropic(1., 1e9, true, true, true, true); + mpi_call_all(mpi_set_integrator_npt_local); mpi_set_temperature(1.); npt_iso_set_rng_seed(0); mpi_set_thermo_switch(thermo_switch | THERMO_NPT_ISO); diff --git a/src/python/espressomd/integrate.pxd b/src/python/espressomd/integrate.pxd index f2ec3dcbdc2..91c372f321d 100644 --- a/src/python/espressomd/integrate.pxd +++ b/src/python/espressomd/integrate.pxd @@ -17,52 +17,16 @@ # along with this program. If not, see . # from libcpp cimport bool as cbool -from libcpp.string cimport string -from libcpp.unordered_map cimport unordered_map include "myconfig.pxi" -cdef extern from "config.hpp": - pass - -cdef extern from "forcecap.hpp": - double forcecap_get() - void mpi_set_forcecap(double forcecap) - cdef extern from "integrate.hpp": double get_time_step() - double get_sim_time() - void mpi_set_time_step(double time_step) except + - void mpi_set_time(double time) cdef extern from "integrate.hpp" nogil: cdef int python_integrate(int n_steps, cbool recalc_forces, int reuse_forces) cdef int mpi_steepest_descent(int max_steps) - cdef void integrate_set_sd() except + - cdef void integrate_set_nvt() - cdef void integrate_set_steepest_descent(const double f_max, const double gamma, - const double max_displacement) except + cdef extern cbool set_py_interrupt - cdef void integrate_set_bd() - -IF NPT: - cdef extern from "integrate.hpp" nogil: - cdef void integrate_set_npt_isotropic(double ext_pressure, double piston, - cbool xdir_rescale, cbool ydir_rescale, - cbool zdir_rescale, cbool cubic_box) except + - -IF STOKESIAN_DYNAMICS: - cdef extern from "stokesian_dynamics/sd_interface.hpp": - void set_sd_viscosity(double eta) except + - void set_sd_radius_dict(const unordered_map[int, double] & radius_dict) except + - void set_sd_flags(int flg) - - cpdef enum flags: - NONE = 0, - SELF_MOBILITY = 1 << 0, - PAIR_MOBILITY = 1 << 1, - LUBRICATION = 1 << 2, - FTS = 1 << 3 cdef inline int _integrate(int nSteps, cbool recalc_forces, int reuse_forces): with nogil: diff --git a/src/python/espressomd/integrate.pyx b/src/python/espressomd/integrate.pyx index 31fbeb91f36..581f3148670 100644 --- a/src/python/espressomd/integrate.pyx +++ b/src/python/espressomd/integrate.pyx @@ -20,75 +20,41 @@ from cpython.exc cimport PyErr_CheckSignals, PyErr_SetInterrupt include "myconfig.pxi" from . import utils from . cimport integrate +from .script_interface import ScriptInterfaceHelper, script_interface_register +from .code_features import assert_features -cdef class IntegratorHandle: - """ - Provide access to all integrators. +@script_interface_register +class IntegratorHandle(ScriptInterfaceHelper): """ + Provide access to the currently active integrator. - cdef object _integrator - - # __getstate__ and __setstate__ define the pickle interaction - def __getstate__(self): - return {'integrator': self._integrator, 'time': self.time, - 'time_step': self.time_step, 'force_cap': self.force_cap} - - def __setstate__(self, state): - self._integrator = state['integrator'] - self._integrator._set_params_in_es_core() - self.time = state['time'] - self.time_step = state['time_step'] - self.force_cap = state['force_cap'] - - def get_state(self): - """ - Return the integrator. - - """ - return self.__getstate__() - - property time_step: - def __set__(self, time_step): - mpi_set_time_step(time_step) - - def __get__(self): - return get_time_step() - - property time: - def __set__(self, sim_time): - mpi_set_time(sim_time) - - def __get__(self): - return get_sim_time() - - property force_cap: - def __set__(self, cap): - mpi_set_forcecap(cap) - - def __get__(self): - return forcecap_get() + """ + _so_name = "Integrators::IntegratorHandle" + _so_creation_policy = "GLOBAL" - def __init__(self): - self.set_nvt() + def __init__(self, **kwargs): + if "sip" not in kwargs and "integrator" not in kwargs: + kwargs["integrator"] = VelocityVerlet() + super().__init__(**kwargs) def __str__(self): - return f'{self.__class__.__name__}({self._integrator.__class__.__name__})' + return f'{self.__class__.__name__}({self.integrator.__class__.__name__})' def run(self, *args, **kwargs): """ Run the integrator. """ - return self._integrator.run(*args, **kwargs) + return self.integrator.run(*args, **kwargs) - def set_steepest_descent(self, *args, **kwargs): + def set_steepest_descent(self, **kwargs): """ Set the integration method to steepest descent (:class:`SteepestDescent`). """ - self._integrator = SteepestDescent(*args, **kwargs) + self.integrator = SteepestDescent(**kwargs) def set_vv(self): """ @@ -96,7 +62,7 @@ cdef class IntegratorHandle: simulations in the NVT ensemble (:class:`VelocityVerlet`). """ - self._integrator = VelocityVerlet() + self.integrator = VelocityVerlet() def set_nvt(self): """ @@ -104,94 +70,36 @@ cdef class IntegratorHandle: simulations in the NVT ensemble (:class:`VelocityVerlet`). """ - self._integrator = VelocityVerlet() + self.integrator = VelocityVerlet() - def set_isotropic_npt(self, *args, **kwargs): + def set_isotropic_npt(self, **kwargs): """ Set the integration method to a modified velocity Verlet designed for simulations in the NpT ensemble (:class:`VelocityVerletIsotropicNPT`). """ - self._integrator = VelocityVerletIsotropicNPT(*args, **kwargs) + self.integrator = VelocityVerletIsotropicNPT(**kwargs) def set_brownian_dynamics(self): """ Set the integration method to BD. """ - self._integrator = BrownianDynamics() + self.integrator = BrownianDynamics() - def set_stokesian_dynamics(self, *args, **kwargs): + def set_stokesian_dynamics(self, **kwargs): """ Set the integration method to Stokesian Dynamics (:class:`StokesianDynamics`). """ - self._integrator = StokesianDynamics(*args, **kwargs) + self.integrator = StokesianDynamics(**kwargs) -cdef class Integrator: +class Integrator(ScriptInterfaceHelper): """ Integrator class. """ - cdef object _params - - def __getstate__(self): - return self._params - - def __setstate__(self, params): - self._params = params - self._set_params_in_es_core() - - def _set_params_in_es_core(self): - """Virtual method. - - """ - raise Exception( - "Subclasses of Integrator must define the _set_params_in_es_core() method.") - - def get_params(self): - """Get integrator parameters. - - """ - return self.__getstate__() - - def __init__(self, *args, **kwargs): - utils.check_required_keys(self.required_keys(), kwargs.keys()) - self._params = self.default_params() - self._params.update(kwargs) - self.validate_params() - self._set_params_in_es_core() - - def __str__(self): - return f'{self.__class__.__name__}({self.get_params()})' - - def validate_params(self): - """Check that parameters are valid. - - """ - pass - - def default_params(self): - """Virtual method. - - """ - raise Exception( - "Subclasses of Integrator must define the default_params() method.") - - def valid_keys(self): - """Virtual method. - - """ - raise Exception( - "Subclasses of Integrator must define the valid_keys() method.") - - def required_keys(self): - """Virtual method. - - """ - raise Exception( - "Subclasses of Integrator must define the required_keys() method.") def run(self, steps=1, recalc_forces=False, reuse_forces=False): """ @@ -225,7 +133,8 @@ cdef class Integrator: utils.handle_errors("Encountered errors during integrate") -cdef class SteepestDescent(Integrator): +@script_interface_register +class SteepestDescent(Integrator): """ Steepest descent algorithm for energy minimization. @@ -249,34 +158,8 @@ cdef class SteepestDescent(Integrator): are in the range of 0.1% to 10% of the particle sigma. """ - - def default_params(self): - return {} - - def valid_keys(self): - """All parameters that can be set. - - """ - return {"f_max", "gamma", "max_displacement"} - - def required_keys(self): - """Parameters that have to be set. - - """ - return {"f_max", "gamma", "max_displacement"} - - def validate_params(self): - utils.check_type_or_throw_except( - self._params["f_max"], 1, float, "f_max must be a float") - utils.check_type_or_throw_except( - self._params["gamma"], 1, float, "gamma must be a float") - utils.check_type_or_throw_except( - self._params["max_displacement"], 1, float, "max_displacement must be a float") - - def _set_params_in_es_core(self): - integrate_set_steepest_descent(self._params["f_max"], - self._params["gamma"], - self._params["max_displacement"]) + _so_name = "Integrators::SteepestDescent" + _so_creation_policy = "GLOBAL" def run(self, steps=1, **kwargs): """ @@ -303,215 +186,84 @@ cdef class SteepestDescent(Integrator): return integrated -cdef class VelocityVerlet(Integrator): +@script_interface_register +class VelocityVerlet(Integrator): """ Velocity Verlet integrator, suitable for simulations in the NVT ensemble. """ + _so_name = "Integrators::VelocityVerlet" + _so_creation_policy = "GLOBAL" - def default_params(self): - return {} - - def valid_keys(self): - """All parameters that can be set. - - """ - return set() - - def required_keys(self): - """Parameters that have to be set. - - """ - return set() - - def validate_params(self): - """Check that parameters are valid. - - """ - pass - - def _set_params_in_es_core(self): - integrate_set_nvt() +@script_interface_register +class VelocityVerletIsotropicNPT(Integrator): + """ + Modified velocity Verlet integrator, suitable for simulations in the + NpT ensemble with isotropic rescaling. Requires the NpT thermostat, + activated with :meth:`espressomd.thermostat.Thermostat.set_npt`. -IF NPT: - cdef class VelocityVerletIsotropicNPT(Integrator): - """ - Modified velocity Verlet integrator, suitable for simulations in the - NpT ensemble with isotropic rescaling. Requires the NpT thermostat, - activated with :meth:`espressomd.thermostat.Thermostat.set_npt`. - - Parameters - ---------- - ext_pressure : :obj:`float` - The external pressure. - piston : :obj:`float` - The mass of the applied piston. - direction : (3,) array_like of :obj:`bool`, optional - Select which dimensions are allowed to fluctuate by assigning - them to ``True``. - cubic_box : :obj:`bool`, optional - If ``True``, a cubic box is assumed and the value of ``direction`` - will be ignored when rescaling the box. This is required e.g. for - electrostatics and magnetostatics. - """ - - def default_params(self): - return {"direction": (True, True, True), "cubic_box": False} - - def valid_keys(self): - """All parameters that can be set. - - """ - return {"ext_pressure", "piston", "direction", "cubic_box"} - - def required_keys(self): - """Parameters that have to be set. - - """ - return {"ext_pressure", "piston"} - - def validate_params(self): - utils.check_type_or_throw_except( - self._params["ext_pressure"], 1, float, "ext_pressure must be a float") - utils.check_type_or_throw_except( - self._params["piston"], 1, float, "piston must be a float") - utils.check_type_or_throw_except( - self._params["direction"], 3, bool, "direction must be an array-like of 3 bools") - utils.check_type_or_throw_except( - self._params["cubic_box"], 1, int, "cubic_box must be a bool") + Parameters + ---------- + ext_pressure : :obj:`float` + The external pressure. + piston : :obj:`float` + The mass of the applied piston. + direction : (3,) array_like of :obj:`bool`, optional + Select which dimensions are allowed to fluctuate by assigning + them to ``True``. Default is all ``True``. + cubic_box : :obj:`bool`, optional + If ``True``, a cubic box is assumed and the value of ``direction`` + will be ignored when rescaling the box. This is required e.g. for + electrostatics and magnetostatics. Default is ``False``. - def _set_params_in_es_core(self): - integrate_set_npt_isotropic(self._params["ext_pressure"], - self._params["piston"], - self._params["direction"][0], - self._params["direction"][1], - self._params["direction"][2], - self._params["cubic_box"]) + """ + _so_name = "Integrators::VelocityVerletIsoNPT" + _so_creation_policy = "GLOBAL" -ELSE: - cdef class VelocityVerletIsotropicNPT(Integrator): - def __init__(self, *args, **kwargs): - raise Exception("NPT not compiled in.") + def __init__(self, **kwargs): + assert_features("NPT") + super().__init__(**kwargs) -cdef class BrownianDynamics(Integrator): +@script_interface_register +class BrownianDynamics(Integrator): """ Brownian Dynamics integrator. """ + _so_name = "Integrators::BrownianDynamics" + _so_creation_policy = "GLOBAL" - def default_params(self): - return {} - - def valid_keys(self): - """All parameters that can be set. - - """ - return set() - - def required_keys(self): - """Parameters that have to be set. - - """ - return set() - - def validate_params(self): - """Check that parameters are valid. - - """ - pass - def _set_params_in_es_core(self): - integrate_set_bd() - - -IF STOKESIAN_DYNAMICS: - cdef class StokesianDynamics(Integrator): - """ - Stokesian Dynamics integrator. - - Parameters - ---------- - viscosity : :obj:`float` - Bulk viscosity. - radii : :obj:`dict` - Dictionary that maps particle types to radii. - approximation_method : :obj:`str`, optional, \{'ft', 'fts'\} - Chooses the method of the mobility approximation. - ``'fts'`` is more accurate. Default is ``'fts'``. - self_mobility : :obj:`bool`, optional - Switches off or on the mobility terms for single particles. Default - is ``True``. - pair_mobility : :obj:`bool`, optional - Switches off or on the hydrodynamic interactions between particles. - Default is ``True``. +@script_interface_register +class StokesianDynamics(Integrator): + """ + Stokesian Dynamics integrator. - """ + Parameters + ---------- + viscosity : :obj:`float` + Bulk viscosity. + radii : :obj:`dict` + Dictionary that maps particle types to radii. + approximation_method : :obj:`str`, optional, \{'ft', 'fts'\} + Chooses the method of the mobility approximation. + ``'fts'`` is more accurate. Default is ``'fts'``. + self_mobility : :obj:`bool`, optional + Switches off or on the mobility terms for single particles. Default + is ``True``. + pair_mobility : :obj:`bool`, optional + Switches off or on the hydrodynamic interactions between particles. + Default is ``True``. - def default_params(self): - return {"lubrication": False, "approximation_method": "fts", - "self_mobility": True, "pair_mobility": True} - - def valid_keys(self): - """All parameters that can be set. - - """ - return {"radii", "viscosity", "lubrication", "approximation_method", - "self_mobility", "pair_mobility"} - - def required_keys(self): - """Parameters that have to be set. - - """ - return {"radii", "viscosity"} - - def validate_params(self): - """Check that parameters are valid. - - """ - utils.check_type_or_throw_except( - self._params["viscosity"], 1, float, - "viscosity must be a number") - utils.check_type_or_throw_except( - self._params["radii"], 1, dict, - "radii must be a dictionary") - utils.check_type_or_throw_except( - self._params["lubrication"], 1, bool, - "lubrication must be a bool") - if self._params["lubrication"]: - raise NotImplementedError( - "Stokesian Dynamics lubrication is not available yet") - utils.check_type_or_throw_except( - self._params["approximation_method"], 1, str, - "approximation_method must be a string") - if self._params["approximation_method"].lower() not in { - "ft", "fts"}: - raise ValueError( - "approximation_method must be either 'ft' or 'fts'") - utils.check_type_or_throw_except( - self._params["self_mobility"], 1, bool, - "self_mobility must be a bool") - utils.check_type_or_throw_except( - self._params["pair_mobility"], 1, bool, - "pair_mobility must be a bool") - - def _set_params_in_es_core(self): - integrate_set_sd() - set_sd_radius_dict(self._params["radii"]) - set_sd_viscosity(self._params["viscosity"]) - fl = flags.NONE - if self._params["lubrication"]: - fl = fl | flags.LUBRICATION - if self._params["approximation_method"].lower() == "fts": - fl = fl | flags.FTS - if self._params["self_mobility"]: - fl = fl | flags.SELF_MOBILITY - if self._params["pair_mobility"]: - fl = fl | flags.PAIR_MOBILITY - set_sd_flags(fl) - -ELSE: - cdef class StokesianDynamics(Integrator): - def __init__(self, *args, **kwargs): - raise Exception("Stokesian Dynamics not compiled in.") + """ + _so_name = "Integrators::StokesianDynamics" + _so_creation_policy = "GLOBAL" + + def __init__(self, **kwargs): + assert_features("STOKESIAN_DYNAMICS") + if kwargs.get("lubrication", False): + raise NotImplementedError( + "Stokesian Dynamics lubrication is not available yet") + super().__init__(**kwargs) diff --git a/src/script_interface/CMakeLists.txt b/src/script_interface/CMakeLists.txt index 49c387d98b0..1cbe9e8f208 100644 --- a/src/script_interface/CMakeLists.txt +++ b/src/script_interface/CMakeLists.txt @@ -34,6 +34,7 @@ add_subdirectory(constraints) add_subdirectory(electrostatics) add_subdirectory(galilei) add_subdirectory(h5md) +add_subdirectory(integrators) add_subdirectory(interactions) add_subdirectory(lbboundaries) add_subdirectory(lees_edwards) diff --git a/src/script_interface/initialize.cpp b/src/script_interface/initialize.cpp index f231e34156b..4d3e70fbf9d 100644 --- a/src/script_interface/initialize.cpp +++ b/src/script_interface/initialize.cpp @@ -32,6 +32,7 @@ #include "electrostatics/initialize.hpp" #include "galilei/initialize.hpp" #include "h5md/initialize.hpp" +#include "integrators/initialize.hpp" #include "interactions/initialize.hpp" #include "lbboundaries/initialize.hpp" #include "lees_edwards/initialize.hpp" @@ -59,6 +60,7 @@ void initialize(Utils::Factory *f) { Coulomb::initialize(f); Dipoles::initialize(f); Galilei::initialize(f); + Integrators::initialize(f); Interactions::initialize(f); LBBoundaries::initialize(f); LeesEdwards::initialize(f); diff --git a/src/script_interface/integrators/BrownianDynamics.cpp b/src/script_interface/integrators/BrownianDynamics.cpp new file mode 100644 index 00000000000..c61c8f1b3bd --- /dev/null +++ b/src/script_interface/integrators/BrownianDynamics.cpp @@ -0,0 +1,32 @@ +/* + * 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 "BrownianDynamics.hpp" + +#include "script_interface/ScriptInterface.hpp" + +#include "core/integrate.hpp" + +namespace ScriptInterface { +namespace Integrators { + +void BrownianDynamics::activate() const { set_integ_switch(INTEG_METHOD_BD); } + +} // namespace Integrators +} // namespace ScriptInterface diff --git a/src/script_interface/integrators/BrownianDynamics.hpp b/src/script_interface/integrators/BrownianDynamics.hpp new file mode 100644 index 00000000000..f7379332328 --- /dev/null +++ b/src/script_interface/integrators/BrownianDynamics.hpp @@ -0,0 +1,38 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_BROWNIAN_DYNAMICS_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_BROWNIAN_DYNAMICS_HPP + +#include "Integrator.hpp" + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +namespace ScriptInterface { +namespace Integrators { + +class BrownianDynamics : public AutoParameters { + void activate() const override; +}; + +} // namespace Integrators +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/integrators/CMakeLists.txt b/src/script_interface/integrators/CMakeLists.txt new file mode 100644 index 00000000000..3487c0079c2 --- /dev/null +++ b/src/script_interface/integrators/CMakeLists.txt @@ -0,0 +1,9 @@ +target_sources( + espresso_script_interface + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/initialize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/BrownianDynamics.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/IntegratorHandle.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/SteepestDescent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/StokesianDynamics.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/VelocityVerlet.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/VelocityVerletIsoNPT.cpp) diff --git a/src/script_interface/integrators/Integrator.hpp b/src/script_interface/integrators/Integrator.hpp new file mode 100644 index 00000000000..85f1f05af15 --- /dev/null +++ b/src/script_interface/integrators/Integrator.hpp @@ -0,0 +1,40 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_INTEGRATOR_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_INTEGRATOR_HPP + +#include "script_interface/ObjectHandle.hpp" +#include "script_interface/ScriptInterface.hpp" + +#include +#include + +namespace ScriptInterface { +namespace Integrators { + +class Integrator : public ObjectHandle { +public: + virtual void activate() const = 0; +}; + +} // namespace Integrators +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/integrators/IntegratorHandle.cpp b/src/script_interface/integrators/IntegratorHandle.cpp new file mode 100644 index 00000000000..7e838958c6a --- /dev/null +++ b/src/script_interface/integrators/IntegratorHandle.cpp @@ -0,0 +1,95 @@ +/* + * 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 "IntegratorHandle.hpp" + +#include "script_interface/ScriptInterface.hpp" + +#include "BrownianDynamics.hpp" +#include "SteepestDescent.hpp" +#include "StokesianDynamics.hpp" +#include "VelocityVerlet.hpp" +#include "VelocityVerletIsoNPT.hpp" + +#include "core/forcecap.hpp" +#include "core/integrate.hpp" + +#include +#include + +namespace ScriptInterface { +namespace Integrators { + +IntegratorHandle::IntegratorHandle() { + add_parameters({ + {"integrator", + [this](Variant const &v) { + m_instance = get_value>(v); + m_instance->activate(); + }, + [this]() { + switch (::integ_switch) { + case INTEG_METHOD_STEEPEST_DESCENT: + return Variant{ + std::dynamic_pointer_cast(m_instance)}; +#ifdef NPT + case INTEG_METHOD_NPT_ISO: + return Variant{ + std::dynamic_pointer_cast(m_instance)}; +#endif + case INTEG_METHOD_BD: + return Variant{ + std::dynamic_pointer_cast(m_instance)}; +#ifdef STOKESIAN_DYNAMICS + case INTEG_METHOD_SD: + return Variant{ + std::dynamic_pointer_cast(m_instance)}; +#endif // STOKESIAN_DYNAMICS + default: { + auto ptr = std::dynamic_pointer_cast(m_instance); + assert(ptr.get()); + return Variant{ptr}; + } + } + }}, + {"time_step", + [this](Variant const &v) { + context()->parallel_try_catch( + [&]() { set_time_step(get_value(v)); }); + }, + []() { return get_time_step(); }}, + {"time", [](Variant const &v) { set_time(get_value(v)); }, + []() { return get_sim_time(); }}, + {"force_cap", + [](Variant const &v) { set_force_cap(get_value(v)); }, + []() { return get_force_cap(); }}, + }); +} + +void IntegratorHandle::do_construct(VariantMap const ¶ms) { + for (auto const &kv : params) { + if (kv.first != "integrator") { + do_set_parameter(kv.first, kv.second); + } + } + do_set_parameter("integrator", params.at("integrator")); +} + +} // namespace Integrators +} // namespace ScriptInterface diff --git a/src/script_interface/integrators/IntegratorHandle.hpp b/src/script_interface/integrators/IntegratorHandle.hpp new file mode 100644 index 00000000000..732a49dc3b3 --- /dev/null +++ b/src/script_interface/integrators/IntegratorHandle.hpp @@ -0,0 +1,46 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_INTEGRATOR_HANDLE_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_INTEGRATOR_HANDLE_HPP + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include "Integrator.hpp" + +#include +#include + +namespace ScriptInterface { +namespace Integrators { + +class IntegratorHandle : public AutoParameters { + std::shared_ptr m_instance; + +public: + IntegratorHandle(); + + void do_construct(VariantMap const ¶ms) override; +}; + +} // namespace Integrators +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/integrators/SteepestDescent.cpp b/src/script_interface/integrators/SteepestDescent.cpp new file mode 100644 index 00000000000..18f8bb180b3 --- /dev/null +++ b/src/script_interface/integrators/SteepestDescent.cpp @@ -0,0 +1,63 @@ +/* + * 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 "SteepestDescent.hpp" + +#include "script_interface/ScriptInterface.hpp" + +#include "core/integrate.hpp" +#include "core/integrators/steepest_descent.hpp" + +#include + +#include +#include + +namespace ScriptInterface { +namespace Integrators { + +SteepestDescent::SteepestDescent() { + add_parameters({ + {"f_max", AutoParameter::read_only, + [this]() { return get_instance().f_max; }}, + {"gamma", AutoParameter::read_only, + [this]() { return get_instance().gamma; }}, + {"max_displacement", AutoParameter::read_only, + [this]() { return get_instance().max_displacement; }}, + }); +} + +void SteepestDescent::do_construct(VariantMap const ¶ms) { + auto const f_max = get_value(params, "f_max"); + auto const gamma = get_value(params, "gamma"); + auto const max_d = get_value(params, "max_displacement"); + + context()->parallel_try_catch([&]() { + m_instance = + std::make_shared<::SteepestDescentParameters>(f_max, gamma, max_d); + }); +} + +void SteepestDescent::activate() const { + register_integrator(get_instance()); + set_integ_switch(INTEG_METHOD_STEEPEST_DESCENT); +} + +} // namespace Integrators +} // namespace ScriptInterface diff --git a/src/script_interface/integrators/SteepestDescent.hpp b/src/script_interface/integrators/SteepestDescent.hpp new file mode 100644 index 00000000000..6e6aa6c80e3 --- /dev/null +++ b/src/script_interface/integrators/SteepestDescent.hpp @@ -0,0 +1,53 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_STEEPEST_DESCENT_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_STEEPEST_DESCENT_HPP + +#include "Integrator.hpp" + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include "core/integrators/steepest_descent.hpp" + +#include +#include + +namespace ScriptInterface { +namespace Integrators { + +class SteepestDescent : public AutoParameters { + std::shared_ptr<::SteepestDescentParameters> m_instance; + +public: + SteepestDescent(); + + void do_construct(VariantMap const ¶ms) override; + void activate() const override; + + ::SteepestDescentParameters const &get_instance() const { + return *m_instance; + } +}; + +} // namespace Integrators +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/integrators/StokesianDynamics.cpp b/src/script_interface/integrators/StokesianDynamics.cpp new file mode 100644 index 00000000000..bbddbd6187b --- /dev/null +++ b/src/script_interface/integrators/StokesianDynamics.cpp @@ -0,0 +1,103 @@ +/* + * 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 "config.hpp" + +#ifdef STOKESIAN_DYNAMICS + +#include "StokesianDynamics.hpp" + +#include "script_interface/ScriptInterface.hpp" + +#include "core/grid.hpp" +#include "core/integrate.hpp" +#include "core/stokesian_dynamics/sd_interface.hpp" + +#include +#include +#include + +namespace ScriptInterface { +namespace Integrators { + +StokesianDynamics::StokesianDynamics() { + add_parameters({ + {"viscosity", AutoParameter::read_only, + [this]() { return get_instance().viscosity; }}, + {"radii", AutoParameter::read_only, + [this]() { + return make_unordered_map_of_variants(get_instance().radii); + }}, + {"lubrication", AutoParameter::read_only, + [this]() { + return static_cast(get_instance().flags & + static_cast(sd_flags::LUBRICATION)); + }}, + {"self_mobility", AutoParameter::read_only, + [this]() { + return static_cast(get_instance().flags & + static_cast(sd_flags::SELF_MOBILITY)); + }}, + {"pair_mobility", AutoParameter::read_only, + [this]() { + return static_cast(get_instance().flags & + static_cast(sd_flags::PAIR_MOBILITY)); + }}, + {"approximation_method", AutoParameter::read_only, + [this]() { + return std::string( + (get_instance().flags & static_cast(sd_flags::FTS)) ? "fts" + : "ft"); + }}, + }); +} + +void StokesianDynamics::do_construct(VariantMap const ¶ms) { + context()->parallel_try_catch([&]() { + int bitfield = 0; + if (get_value_or(params, "self_mobility", true)) { + bitfield |= static_cast(sd_flags::SELF_MOBILITY); + } + if (get_value_or(params, "pair_mobility", true)) { + bitfield |= static_cast(sd_flags::PAIR_MOBILITY); + } + auto const approx = + get_value_or(params, "approximation_method", "fts"); + if (approx == "fts") { + bitfield |= static_cast(sd_flags::FTS); + } else if (approx != "ft") { + throw std::invalid_argument("Unknown approximation '" + approx + "'"); + } + m_instance = std::make_shared<::StokesianDynamicsParameters>( + get_value(params, "viscosity"), + get_value>(params, "radii"), bitfield); + }); +} + +void StokesianDynamics::activate() const { + context()->parallel_try_catch([&]() { + register_integrator(get_instance()); + set_integ_switch(INTEG_METHOD_SD); + }); +} + +} // namespace Integrators +} // namespace ScriptInterface + +#endif // STOKESIAN_DYNAMICS diff --git a/src/script_interface/integrators/StokesianDynamics.hpp b/src/script_interface/integrators/StokesianDynamics.hpp new file mode 100644 index 00000000000..804040d6b10 --- /dev/null +++ b/src/script_interface/integrators/StokesianDynamics.hpp @@ -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 . + */ + +#ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_STOKESIAN_DYNAMICS_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_STOKESIAN_DYNAMICS_HPP + +#include "config.hpp" + +#ifdef STOKESIAN_DYNAMICS + +#include "Integrator.hpp" + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include "core/stokesian_dynamics/sd_interface.hpp" + +#include +#include + +namespace ScriptInterface { +namespace Integrators { + +class StokesianDynamics : public AutoParameters { + std::shared_ptr<::StokesianDynamicsParameters> m_instance; + +public: + StokesianDynamics(); + + void do_construct(VariantMap const ¶ms) override; + void activate() const override; + + ::StokesianDynamicsParameters const &get_instance() const { + return *m_instance; + } +}; + +} // namespace Integrators +} // namespace ScriptInterface + +#endif // STOKESIAN_DYNAMICS +#endif diff --git a/src/script_interface/integrators/VelocityVerlet.cpp b/src/script_interface/integrators/VelocityVerlet.cpp new file mode 100644 index 00000000000..f32d0b11bd8 --- /dev/null +++ b/src/script_interface/integrators/VelocityVerlet.cpp @@ -0,0 +1,32 @@ +/* + * 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 "VelocityVerlet.hpp" + +#include "script_interface/ScriptInterface.hpp" + +#include "core/integrate.hpp" + +namespace ScriptInterface { +namespace Integrators { + +void VelocityVerlet::activate() const { set_integ_switch(INTEG_METHOD_NVT); } + +} // namespace Integrators +} // namespace ScriptInterface diff --git a/src/script_interface/integrators/VelocityVerlet.hpp b/src/script_interface/integrators/VelocityVerlet.hpp new file mode 100644 index 00000000000..30ae3dc3d25 --- /dev/null +++ b/src/script_interface/integrators/VelocityVerlet.hpp @@ -0,0 +1,38 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_VELOCITY_VERLET_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_VELOCITY_VERLET_HPP + +#include "Integrator.hpp" + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +namespace ScriptInterface { +namespace Integrators { + +class VelocityVerlet : public Integrator { + void activate() const override; +}; + +} // namespace Integrators +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/integrators/VelocityVerletIsoNPT.cpp b/src/script_interface/integrators/VelocityVerletIsoNPT.cpp new file mode 100644 index 00000000000..28ad7850b2b --- /dev/null +++ b/src/script_interface/integrators/VelocityVerletIsoNPT.cpp @@ -0,0 +1,75 @@ +/* + * 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 "config.hpp" + +#ifdef NPT + +#include "VelocityVerletIsoNPT.hpp" + +#include "script_interface/ScriptInterface.hpp" + +#include "core/event.hpp" +#include "core/integrate.hpp" +#include "core/npt.hpp" + +#include + +#include +#include + +namespace ScriptInterface { +namespace Integrators { + +VelocityVerletIsoNPT::VelocityVerletIsoNPT() { + add_parameters({ + {"ext_pressure", AutoParameter::read_only, + [this]() { return get_instance().p_ext; }}, + {"piston", AutoParameter::read_only, + [this]() { return get_instance().piston; }}, + {"direction", AutoParameter::read_only, + [this]() { return get_instance().get_direction(); }}, + {"cubic_box", AutoParameter::read_only, + [this]() { return get_instance().cubic_box; }}, + }); +} + +void VelocityVerletIsoNPT::do_construct(VariantMap const ¶ms) { + auto const ext_pressure = get_value(params, "ext_pressure"); + auto const piston = get_value(params, "piston"); + auto const cubic_box = get_value_or(params, "cubic_box", false); + auto const direction = get_value_or( + params, "direction", Utils::Vector3b::broadcast(true)); + + context()->parallel_try_catch([&]() { + m_instance = std::make_shared<::NptIsoParameters>(ext_pressure, piston, + direction, cubic_box); + }); +} + +void VelocityVerletIsoNPT::activate() const { + ::nptiso = get_instance(); + set_integ_switch(INTEG_METHOD_NPT_ISO); + on_thermostat_param_change(); +} + +} // namespace Integrators +} // namespace ScriptInterface + +#endif // NPT diff --git a/src/script_interface/integrators/VelocityVerletIsoNPT.hpp b/src/script_interface/integrators/VelocityVerletIsoNPT.hpp new file mode 100644 index 00000000000..a858113a8ca --- /dev/null +++ b/src/script_interface/integrators/VelocityVerletIsoNPT.hpp @@ -0,0 +1,57 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_NPT_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_NPT_HPP + +#include "config.hpp" + +#ifdef NPT + +#include "Integrator.hpp" + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include "core/npt.hpp" + +#include +#include + +namespace ScriptInterface { +namespace Integrators { + +class VelocityVerletIsoNPT + : public AutoParameters { + std::shared_ptr<::NptIsoParameters> m_instance; + +public: + VelocityVerletIsoNPT(); + + void do_construct(VariantMap const ¶ms) override; + void activate() const override; + + ::NptIsoParameters const &get_instance() const { return *m_instance; } +}; + +} // namespace Integrators +} // namespace ScriptInterface + +#endif // NPT +#endif diff --git a/src/script_interface/integrators/initialize.cpp b/src/script_interface/integrators/initialize.cpp new file mode 100644 index 00000000000..bdeeb5e63d6 --- /dev/null +++ b/src/script_interface/integrators/initialize.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015-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 "BrownianDynamics.hpp" +#include "IntegratorHandle.hpp" +#include "SteepestDescent.hpp" +#include "StokesianDynamics.hpp" +#include "VelocityVerlet.hpp" +#include "VelocityVerletIsoNPT.hpp" +#include "config.hpp" + +namespace ScriptInterface { +namespace Integrators { + +void initialize(Utils::Factory *om) { + om->register_new("Integrators::IntegratorHandle"); + om->register_new("Integrators::BrownianDynamics"); + om->register_new("Integrators::SteepestDescent"); +#ifdef STOKESIAN_DYNAMICS + om->register_new("Integrators::StokesianDynamics"); +#endif // STOKESIAN_DYNAMICS + om->register_new("Integrators::VelocityVerlet"); +#ifdef NPT + om->register_new("Integrators::VelocityVerletIsoNPT"); +#endif // NPT +} + +} // namespace Integrators +} // namespace ScriptInterface diff --git a/src/script_interface/integrators/initialize.hpp b/src/script_interface/integrators/initialize.hpp new file mode 100644 index 00000000000..706621966a2 --- /dev/null +++ b/src/script_interface/integrators/initialize.hpp @@ -0,0 +1,35 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_INITIALIZE_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_INITIALIZE_HPP + +#include + +#include + +namespace ScriptInterface { +namespace Integrators { + +void initialize(Utils::Factory *om); + +} // namespace Integrators +} // namespace ScriptInterface + +#endif diff --git a/testsuite/python/integrator_exceptions.py b/testsuite/python/integrator_exceptions.py index 66e9386b079..1e8a7c6b648 100644 --- a/testsuite/python/integrator_exceptions.py +++ b/testsuite/python/integrator_exceptions.py @@ -112,11 +112,13 @@ def test_stokesian_integrator(self): 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'\]\)"): - self.system.integrator.set_steepest_descent( - f_max=0, gamma=0.1, max_d=5) + params = {"f_max": 0., "gamma": 0.1, "max_displacement": 5.} + for key in params: + invalid_params = params.copy() + del invalid_params[key] + with self.assertRaisesRegex(RuntimeError, f"Parameter '{key}' is missing"): + self.system.integrator.set_steepest_descent( + **invalid_params) 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/integrator_npt.py b/testsuite/python/integrator_npt.py index ef26661726a..232e858d861 100644 --- a/testsuite/python/integrator_npt.py +++ b/testsuite/python/integrator_npt.py @@ -91,7 +91,7 @@ def test_00_integrator_recovery(self): system.integrator.set_steepest_descent( f_max=-10, gamma=0, max_displacement=0.1) # the interface state is unchanged - integrator_state = system.integrator.get_state() + integrator_state = system.integrator.get_params() self.assertIsInstance(integrator_state['integrator'], espressomd.integrate.VelocityVerletIsotropicNPT) params = integrator_state['integrator'].get_params() @@ -112,7 +112,7 @@ def test_00_integrator_recovery(self): with self.assertRaises(RuntimeError): system.integrator.set_isotropic_npt(ext_pressure=-1, piston=100) # the interface state is unchanged - self.assertIsInstance(system.integrator.get_state()['integrator'], + self.assertIsInstance(system.integrator.get_params()['integrator'], espressomd.integrate.VelocityVerlet) # the core state is unchanged system.integrator.run(1) diff --git a/testsuite/python/integrator_steepest_descent.py b/testsuite/python/integrator_steepest_descent.py index d8036dbb0bd..1574329d11f 100644 --- a/testsuite/python/integrator_steepest_descent.py +++ b/testsuite/python/integrator_steepest_descent.py @@ -188,10 +188,10 @@ def test_integrator_recovery(self): with self.assertRaises(RuntimeError): system.integrator.set_isotropic_npt(ext_pressure=1, piston=-1) # the interface state is unchanged - state = system.integrator.get_state() - self.assertIsInstance(state['integrator'], + state = system.integrator.get_params() + self.assertIsInstance(state["integrator"], espressomd.integrate.SteepestDescent) - params = state['integrator'].get_params() + params = state["integrator"].get_params() self.assertEqual(params["f_max"], sd_params["f_max"]) self.assertEqual(params["gamma"], sd_params["gamma"]) self.assertEqual( @@ -211,7 +211,7 @@ def test_integrator_recovery(self): system.integrator.set_steepest_descent( f_max=0, gamma=1, max_displacement=-1) # the interface state is unchanged - self.assertIsInstance(system.integrator.get_state()['integrator'], + self.assertIsInstance(system.integrator.get_params()["integrator"], espressomd.integrate.VelocityVerlet) # the core state is unchanged system.integrator.run(1) diff --git a/testsuite/python/stokesian_thermostat.py b/testsuite/python/stokesian_thermostat.py index c70ab92d8b4..3e43d1c17f1 100644 --- a/testsuite/python/stokesian_thermostat.py +++ b/testsuite/python/stokesian_thermostat.py @@ -55,7 +55,7 @@ def reset_particle(): viscosity = 2.4 # invalid parameters should throw exceptions - with self.assertRaises(RuntimeError): + with self.assertRaisesRegex(ValueError, "kT has an invalid value"): system.thermostat.set_stokesian(kT=-1) with self.assertRaises(ValueError): system.thermostat.set_stokesian(kT=1, seed=-1) @@ -102,12 +102,15 @@ def reset_particle(): def test_integrator_exceptions(self): # invalid parameters should throw exceptions - with self.assertRaisesRegex(RuntimeError, "Particle radius for type 0 has an invalid value"): + with self.assertRaisesRegex(ValueError, "Particle radius for type 0 has an invalid value"): self.system.integrator.set_stokesian_dynamics( viscosity=1.0, radii={0: -1}) - with self.assertRaisesRegex(RuntimeError, "Viscosity has an invalid value"): + with self.assertRaisesRegex(ValueError, "Viscosity has an invalid value"): self.system.integrator.set_stokesian_dynamics( - viscosity=-1, radii={0: 1.0}) + viscosity=-1.0, radii={0: 1.0}) + with self.assertRaisesRegex(ValueError, "Unknown approximation 'STS'"): + self.system.integrator.set_stokesian_dynamics( + viscosity=1.0, radii={0: 1.0}, approximation_method="STS") # invalid PBC should throw exceptions self.system.integrator.set_vv() diff --git a/testsuite/python/test_checkpoint.py b/testsuite/python/test_checkpoint.py index a90de4a52ef..b9f7d953ae9 100644 --- a/testsuite/python/test_checkpoint.py +++ b/testsuite/python/test_checkpoint.py @@ -260,7 +260,7 @@ def test_thermostat_SDM(self): self.assertEqual(thmst['counter'], 0) def test_integrator(self): - params = system.integrator.get_state() + params = system.integrator.get_params() self.assertAlmostEqual(params['force_cap'], 1e8, delta=1E-10) self.assertAlmostEqual(params['time_step'], 0.01, delta=1E-10) self.assertAlmostEqual(params['time'], 1.5, delta=1E-10) @@ -268,18 +268,18 @@ def test_integrator(self): @utx.skipIfMissingFeatures('NPT') @ut.skipIf('INT.NPT' not in modes, 'NPT integrator not in modes') def test_integrator_NPT(self): - integ = system.integrator.get_state() + integ = system.integrator.get_params() self.assertIsInstance( integ['integrator'], espressomd.integrate.VelocityVerletIsotropicNPT) params = integ['integrator'].get_params() self.assertEqual(params['ext_pressure'], 2.0) self.assertEqual(params['piston'], 0.01) - self.assertEqual(params['direction'], [1, 0, 0]) + self.assertEqual(list(params['direction']), [True, False, False]) self.assertEqual(params['cubic_box'], False) @ut.skipIf('INT.SD' not in modes, 'SD integrator not in modes') def test_integrator_SD(self): - integ = system.integrator.get_state() + integ = system.integrator.get_params() self.assertIsInstance( integ['integrator'], espressomd.integrate.SteepestDescent) @@ -290,7 +290,7 @@ def test_integrator_SD(self): @ut.skipIf('INT.NVT' not in modes, 'NVT integrator not in modes') def test_integrator_NVT(self): - integ = system.integrator.get_state() + integ = system.integrator.get_params() self.assertIsInstance( integ['integrator'], espressomd.integrate.VelocityVerlet) @@ -299,7 +299,7 @@ def test_integrator_NVT(self): @ut.skipIf('INT' in modes, 'VV integrator not the default') def test_integrator_VV(self): - integ = system.integrator.get_state() + integ = system.integrator.get_params() self.assertIsInstance( integ['integrator'], espressomd.integrate.VelocityVerlet) @@ -308,7 +308,7 @@ def test_integrator_VV(self): @ut.skipIf('INT.BD' not in modes, 'BD integrator not in modes') def test_integrator_BD(self): - integ = system.integrator.get_state() + integ = system.integrator.get_params() self.assertIsInstance( integ['integrator'], espressomd.integrate.BrownianDynamics) @@ -318,7 +318,7 @@ def test_integrator_BD(self): @utx.skipIfMissingFeatures('STOKESIAN_DYNAMICS') @ut.skipIf('INT.SDM' not in modes, 'SDM integrator not in modes') def test_integrator_SDM(self): - integ = system.integrator.get_state() + integ = system.integrator.get_params() self.assertIsInstance( integ['integrator'], espressomd.integrate.StokesianDynamics) From 4529d0c3f1c61c923753fb4e4c65df205f8fa882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 12 Aug 2022 21:06:19 +0200 Subject: [PATCH 35/85] core: Reduce usage of MPI callbacks --- src/core/EspressoSystemStandAlone.cpp | 13 +++--- src/core/collision.cpp | 15 +++---- src/core/constraints/ShapeBasedConstraint.cpp | 7 ++-- src/core/dpd.cpp | 6 +-- src/core/energy.cpp | 2 +- src/core/energy_inline.hpp | 2 +- src/core/event.cpp | 6 +-- src/core/forces_inline.hpp | 2 +- src/core/grid.cpp | 13 +++--- src/core/grid.hpp | 3 +- src/core/interactions.cpp | 2 +- .../VerletCriterion.hpp | 2 +- .../nonbonded_interaction_data.cpp | 4 +- .../nonbonded_interaction_data.hpp | 6 +-- src/core/pair_criteria/EnergyCriterion.hpp | 2 +- src/core/particle_data.cpp | 26 +----------- src/core/particle_data.hpp | 7 +++- src/core/pressure_inline.hpp | 2 +- src/core/tuning.cpp | 31 +++++++++----- .../cell_system/CellSystem.cpp | 18 ++++----- src/script_interface/system/System.cpp | 40 +++++++++---------- 21 files changed, 95 insertions(+), 114 deletions(-) diff --git a/src/core/EspressoSystemStandAlone.cpp b/src/core/EspressoSystemStandAlone.cpp index e0649516d0d..694818a1193 100644 --- a/src/core/EspressoSystemStandAlone.cpp +++ b/src/core/EspressoSystemStandAlone.cpp @@ -52,23 +52,26 @@ EspressoSystemStandAlone::EspressoSystemStandAlone(int argc, char **argv) { mpi_loop(); } -static void mpi_set_node_grid_local(Utils::Vector3i const &node_grid) { - ::node_grid = node_grid; - grid_changed_n_nodes(); - on_node_grid_change(); +static void mpi_set_box_length_local(Utils::Vector3d const &value) { + set_box_length(value); +} + +static void mpi_set_node_grid_local(Utils::Vector3i const &value) { + set_node_grid(value); } static void mpi_set_time_step_local(double const &value) { set_time_step(value); } +REGISTER_CALLBACK(mpi_set_box_length_local) REGISTER_CALLBACK(mpi_set_node_grid_local) REGISTER_CALLBACK(mpi_set_time_step_local) void EspressoSystemStandAlone::set_box_l(Utils::Vector3d const &box_l) const { if (!head_node) return; - mpi_set_box_length(box_l); + mpi_call_all(mpi_set_box_length_local, box_l); } void EspressoSystemStandAlone::set_node_grid( diff --git a/src/core/collision.cpp b/src/core/collision.cpp index fdcc075996a..486fae8cee8 100644 --- a/src/core/collision.cpp +++ b/src/core/collision.cpp @@ -195,8 +195,7 @@ void Collision_parameters::initialize() { throw std::domain_error("Collision detection particle type for virtual " "sites needs to be >=0"); } - if (this_node == 0) - make_particle_type_exist(collision_params.vs_particle_type); + make_particle_type_exist_local(collision_params.vs_particle_type); } if (collision_params.mode == CollisionModeType::GLUE_TO_SURF) { @@ -204,29 +203,25 @@ void Collision_parameters::initialize() { throw std::domain_error("Collision detection particle type for virtual " "sites needs to be >=0"); } - if (this_node == 0) - make_particle_type_exist(collision_params.vs_particle_type); + make_particle_type_exist_local(collision_params.vs_particle_type); if (collision_params.part_type_to_be_glued < 0) { throw std::domain_error("Collision detection particle type to be glued " "needs to be >=0"); } - if (this_node == 0) - make_particle_type_exist(collision_params.part_type_to_be_glued); + make_particle_type_exist_local(collision_params.part_type_to_be_glued); if (collision_params.part_type_to_attach_vs_to < 0) { throw std::domain_error("Collision detection particle type to attach " "the virtual site to needs to be >=0"); } - if (this_node == 0) - make_particle_type_exist(collision_params.part_type_to_attach_vs_to); + make_particle_type_exist_local(collision_params.part_type_to_attach_vs_to); if (collision_params.part_type_after_glueing < 0) { throw std::domain_error("Collision detection particle type after gluing " "needs to be >=0"); } - if (this_node == 0) - make_particle_type_exist(collision_params.part_type_after_glueing); + make_particle_type_exist_local(collision_params.part_type_after_glueing); } on_short_range_ia_change(); diff --git a/src/core/constraints/ShapeBasedConstraint.cpp b/src/core/constraints/ShapeBasedConstraint.cpp index 9e46ce5066b..197dd5cb57f 100644 --- a/src/core/constraints/ShapeBasedConstraint.cpp +++ b/src/core/constraints/ShapeBasedConstraint.cpp @@ -56,8 +56,7 @@ double ShapeBasedConstraint::min_dist(const ParticleRange &particles) { particles.begin(), particles.end(), std::numeric_limits::infinity(), [this](double min, Particle const &p) { - IA_parameters const &ia_params = - *get_ia_param(p.type(), part_rep.type()); + auto const &ia_params = get_ia_param(p.type(), part_rep.type()); if (checkIfInteraction(ia_params)) { double dist; Utils::Vector3d vec; @@ -75,7 +74,7 @@ ParticleForce ShapeBasedConstraint::force(Particle const &p, Utils::Vector3d const &folded_pos, double) { ParticleForce pf{}; - IA_parameters const &ia_params = *get_ia_param(p.type(), part_rep.type()); + auto const &ia_params = get_ia_param(p.type(), part_rep.type()); if (checkIfInteraction(ia_params)) { double dist = 0.; @@ -135,7 +134,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.type(), part_rep.type()); + auto const &ia_params = get_ia_param(p.type(), part_rep.type()); if (checkIfInteraction(ia_params)) { auto const coulomb_kernel = Coulomb::pair_energy_kernel(); diff --git a/src/core/dpd.cpp b/src/core/dpd.cpp index 1a871573004..d59b6007c12 100644 --- a/src/core/dpd.cpp +++ b/src/core/dpd.cpp @@ -66,7 +66,7 @@ Vector3d dpd_noise(int pid1, int pid2) { int dpd_set_params(int part_type_a, int part_type_b, double gamma, double k, double r_c, int wf, double tgamma, double tr_c, int twf) { - IA_parameters &ia_params = *get_ia_param_safe(part_type_a, part_type_b); + auto &ia_params = *get_ia_param_safe(part_type_a, part_type_b); ia_params.dpd_radial = DPDParameters{gamma, k, r_c, wf, -1.}; ia_params.dpd_trans = DPDParameters{tgamma, k, tr_c, twf, -1.}; @@ -80,7 +80,7 @@ int dpd_set_params(int part_type_a, int part_type_b, double gamma, double k, void dpd_init(double kT, double time_step) { for (int type_a = 0; type_a < max_seen_particle_type; type_a++) { for (int type_b = 0; type_b < max_seen_particle_type; type_b++) { - IA_parameters &ia_params = *get_ia_param(type_a, type_b); + IA_parameters &ia_params = get_ia_param(type_a, type_b); ia_params.dpd_radial.pref = sqrt(24.0 * kT * ia_params.dpd_radial.gamma / time_step); @@ -147,7 +147,7 @@ static auto dpd_viscous_stress_local() { auto const v21 = box_geo.velocity_difference(p1.pos(), p2.pos(), p1.v(), p2.v()); - IA_parameters const &ia_params = *get_ia_param(p1.type(), p2.type()); + auto const &ia_params = get_ia_param(p1.type(), p2.type()); auto const dist = std::sqrt(d.dist2); auto const f_r = dpd_pair_force(ia_params.dpd_radial, v21, dist, {}); diff --git a/src/core/energy.cpp b/src/core/energy.cpp index cce2271a7bf..aceabcfc33a 100644 --- a/src/core/energy.cpp +++ b/src/core/energy.cpp @@ -149,7 +149,7 @@ double particle_short_range_energy_contribution_local(int pid) { if (not do_nonbonded(p, p1)) return; #endif - auto const &ia_params = *get_ia_param(p.type(), p1.type()); + auto const &ia_params = get_ia_param(p.type(), p1.type()); // Add energy for current particle pair to result ret += calc_non_bonded_pair_energy(p, p1, ia_params, vec, vec.norm(), coulomb_kernel_ptr); diff --git a/src/core/energy_inline.hpp b/src/core/energy_inline.hpp index ba6d36b77ab..23921c6cd14 100644 --- a/src/core/energy_inline.hpp +++ b/src/core/energy_inline.hpp @@ -178,7 +178,7 @@ inline void add_non_bonded_pair_energy( Coulomb::ShortRangeEnergyKernel::kernel_type const *coulomb_kernel, Dipoles::ShortRangeEnergyKernel::kernel_type const *dipoles_kernel, Observable_stat &obs_energy) { - IA_parameters const &ia_params = *get_ia_param(p1.type(), p2.type()); + auto const &ia_params = get_ia_param(p1.type(), p2.type()); #ifdef EXCLUSIONS if (do_nonbonded(p1, p2)) diff --git a/src/core/event.cpp b/src/core/event.cpp index 66ee97814bb..3419a7cf206 100644 --- a/src/core/event.cpp +++ b/src/core/event.cpp @@ -96,10 +96,8 @@ void on_program_start() { /* initially go for regular decomposition */ cells_re_init(CellStructureType::CELL_STRUCTURE_REGULAR); - if (this_node == 0) { - /* make sure interaction 0<->0 always exists */ - make_particle_type_exist(0); - } + /* make sure interaction 0<->0 always exists */ + make_particle_type_exist_local(0); } void on_integration_start(double time_step) { diff --git a/src/core/forces_inline.hpp b/src/core/forces_inline.hpp index 24472c14c9d..874cc391c15 100644 --- a/src/core/forces_inline.hpp +++ b/src/core/forces_inline.hpp @@ -184,7 +184,7 @@ inline void add_non_bonded_pair_force( Coulomb::ShortRangeForceKernel::kernel_type const *coulomb_kernel, Dipoles::ShortRangeForceKernel::kernel_type const *dipoles_kernel, Coulomb::ShortRangeForceCorrectionsKernel::kernel_type const *elc_kernel) { - IA_parameters const &ia_params = *get_ia_param(p1.type(), p2.type()); + auto const &ia_params = get_ia_param(p1.type(), p2.type()); ParticleForce pf{}; /***********************************************/ diff --git a/src/core/grid.cpp b/src/core/grid.cpp index 2cdf51742b6..3653b866a1d 100644 --- a/src/core/grid.cpp +++ b/src/core/grid.cpp @@ -106,13 +106,12 @@ void grid_changed_n_nodes() { grid_changed_box_l(box_geo); } -static void mpi_set_box_length_local(Utils::Vector3d const &box_l) { - box_geo.set_length(box_l); - on_boxl_change(); +void set_node_grid(Utils::Vector3i const &value) { + ::node_grid = value; + on_node_grid_change(); } -REGISTER_CALLBACK(mpi_set_box_length_local) - -void mpi_set_box_length(Utils::Vector3d const &box_l) { - mpi_call_all(mpi_set_box_length_local, box_l); +void set_box_length(Utils::Vector3d const &value) { + ::box_geo.set_length(value); + on_boxl_change(); } diff --git a/src/core/grid.hpp b/src/core/grid.hpp index c053b25f851..a690695d7f6 100644 --- a/src/core/grid.hpp +++ b/src/core/grid.hpp @@ -106,6 +106,7 @@ LocalBox regular_decomposition(const BoxGeometry &box, Utils::Vector3i const &node_pos, Utils::Vector3i const &node_grid); -void mpi_set_box_length(Utils::Vector3d const &value); +void set_node_grid(Utils::Vector3i const &value); +void set_box_length(Utils::Vector3d const &value); #endif diff --git a/src/core/interactions.cpp b/src/core/interactions.cpp index 1ae9d9b561f..b050bedab4f 100644 --- a/src/core/interactions.cpp +++ b/src/core/interactions.cpp @@ -79,7 +79,7 @@ bool long_range_interactions_sanity_checks() { } void mpi_bcast_ia_params_local(int i, int j) { - boost::mpi::broadcast(comm_cart, *get_ia_param(i, j), 0); + boost::mpi::broadcast(comm_cart, get_ia_param(i, j), 0); on_short_range_ia_change(); } diff --git a/src/core/nonbonded_interactions/VerletCriterion.hpp b/src/core/nonbonded_interactions/VerletCriterion.hpp index 87770a7d110..050242c6c69 100644 --- a/src/core/nonbonded_interactions/VerletCriterion.hpp +++ b/src/core/nonbonded_interactions/VerletCriterion.hpp @@ -30,7 +30,7 @@ struct GetNonbondedCutoff { auto operator()(int type_i, int type_j) const { - return get_ia_param(type_i, type_j)->max_cut; + return get_ia_param(type_i, type_j).max_cut; } }; diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index f5d3a6167c1..5250e802c87 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -70,7 +70,7 @@ static void mpi_realloc_ia_params_local(int new_size) { for (int i = 0; i < max_seen_particle_type; i++) for (int j = i; j < max_seen_particle_type; j++) { new_params.at(Utils::upper_triangular(i, j, new_size)) = - std::move(*get_ia_param(i, j)); + std::move(get_ia_param(i, j)); } max_seen_particle_type = new_size; @@ -97,7 +97,7 @@ static void mpi_bcast_all_ia_params() { IA_parameters *get_ia_param_safe(int i, int j) { make_particle_type_exist(std::max(i, j)); - return get_ia_param(i, j); + return &get_ia_param(i, j); } std::string ia_params_get_state() { diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index eb415c59fc3..defb1d1f122 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -294,13 +294,13 @@ double maximal_cutoff_nonbonded(); * @param i First type, has to be smaller than @ref max_seen_particle_type. * @param j Second type, has to be smaller than @ref max_seen_particle_type. * - * @return Pointer to interaction parameters for the type pair. + * @return Reference to interaction parameters for the type pair. */ -inline IA_parameters *get_ia_param(int i, int j) { +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 &nonbonded_ia_params[Utils::upper_triangular( + return nonbonded_ia_params[Utils::upper_triangular( std::min(i, j), std::max(i, j), max_seen_particle_type)]; } diff --git a/src/core/pair_criteria/EnergyCriterion.hpp b/src/core/pair_criteria/EnergyCriterion.hpp index 21b912c1555..c84cba12d9a 100644 --- a/src/core/pair_criteria/EnergyCriterion.hpp +++ b/src/core/pair_criteria/EnergyCriterion.hpp @@ -34,7 +34,7 @@ class EnergyCriterion : public PairCriterion { auto const d = box_geo.get_mi_vector(p1.pos(), p2.pos()); // Interaction parameters for particle types - auto const &ia_params = *get_ia_param(p1.type(), p2.type()); + auto const &ia_params = get_ia_param(p1.type(), p2.type()); auto const coulomb_kernel = Coulomb::pair_energy_kernel(); auto const energy = calc_non_bonded_pair_energy( diff --git a/src/core/particle_data.cpp b/src/core/particle_data.cpp index 94aac35a134..064980062d1 100644 --- a/src/core/particle_data.cpp +++ b/src/core/particle_data.cpp @@ -641,11 +641,7 @@ const std::vector &get_particle_bonds(int part) { return ret; } -/** Locally rescale all particles on current node. - * @param dir direction to scale (0/1/2 = x/y/z, 3 = x+y+z isotropically) - * @param scale factor by which to rescale (>1: stretch, <1: contract) - */ -void local_rescale_particles(int dir, double scale) { +void rescale_particles(int dir, double scale) { for (auto &p : cell_structure.local_particles()) { if (dir < 3) p.pos()[dir] *= scale; @@ -653,26 +649,6 @@ void local_rescale_particles(int dir, double scale) { p.pos() *= scale; } } -} - -static void mpi_rescale_particles_local(int dir) { - double scale = 0.0; - comm_cart.recv(0, some_tag, scale); - local_rescale_particles(dir, scale); - on_particle_change(); -} - -REGISTER_CALLBACK(mpi_rescale_particles_local) - -void mpi_rescale_particles(int dir, double scale) { - mpi_call(mpi_rescale_particles_local, dir); - for (int pnode = 0; pnode < n_nodes; pnode++) { - if (pnode == this_node) { - local_rescale_particles(dir, scale); - } else { - comm_cart.send(pnode, some_tag, scale); - } - } on_particle_change(); } diff --git a/src/core/particle_data.hpp b/src/core/particle_data.hpp index 03aec9e1717..82c7f39268a 100644 --- a/src/core/particle_data.hpp +++ b/src/core/particle_data.hpp @@ -285,7 +285,10 @@ void add_particle_exclusion(int part1, int part2); void auto_exclusions(int distance); #endif -/** Rescale all particle positions in direction @p dir by a factor @p scale. */ -void mpi_rescale_particles(int dir, double scale); +/** Rescale all particle positions in direction @p dir by a factor @p scale. + * @param dir direction to scale (0/1/2 = x/y/z, 3 = x+y+z isotropically) + * @param scale factor by which to rescale (>1: stretch, <1: contract) + */ +void rescale_particles(int dir, double scale); #endif diff --git a/src/core/pressure_inline.hpp b/src/core/pressure_inline.hpp index a77b0aa3df3..66fa782621a 100644 --- a/src/core/pressure_inline.hpp +++ b/src/core/pressure_inline.hpp @@ -62,7 +62,7 @@ inline void add_non_bonded_pair_virials( if (do_nonbonded(p1, p2)) #endif { - IA_parameters const &ia_params = *get_ia_param(p1.type(), p2.type()); + auto const &ia_params = get_ia_param(p1.type(), p2.type()); auto const force = calc_non_bonded_pair_force(p1, p2, ia_params, d, dist, kernel_forces).f; auto const stress = Utils::tensor_product(d, force); diff --git a/src/core/tuning.cpp b/src/core/tuning.cpp index 18c61459815..bf9e198b681 100644 --- a/src/core/tuning.cpp +++ b/src/core/tuning.cpp @@ -31,6 +31,8 @@ #include +#include +#include #include #include @@ -66,7 +68,7 @@ static void check_statistics(Utils::Statistics::RunningAverage &acc) { static void throw_on_error() { auto const n_errors = check_runtime_errors_local(); - if (boost::mpi::all_reduce(comm_cart, n_errors, std::plus<>()) != 0) { + if (boost::mpi::all_reduce(::comm_cart, n_errors, std::plus<>()) != 0) { throw TuningFailed{}; } } @@ -92,10 +94,15 @@ double benchmark_integration_step(int int_steps) { /* MPI returns in seconds, returned value should be in ms. */ auto retval = 1000. * running_average.avg(); - boost::mpi::broadcast(comm_cart, retval, 0); + boost::mpi::broadcast(::comm_cart, retval, 0); return retval; } +static bool integration_failed() { + auto const count_local = check_runtime_errors_local(); + return boost::mpi::all_reduce(::comm_cart, count_local, std::plus<>()) > 0; +} + /** * \brief Time the integration. * This times the integration and @@ -105,16 +112,20 @@ double benchmark_integration_step(int int_steps) { * @return Time per integration in ms. */ static double time_calc(int int_steps) { - if (mpi_integrate(0, 0)) + integrate(0, 0); + if (integration_failed()) { return -1; + } /* perform force calculation test */ - const double tick = MPI_Wtime(); - if (mpi_integrate(int_steps, -1)) + auto const tick = MPI_Wtime(); + integrate(int_steps, -1); + auto const tock = MPI_Wtime(); + if (integration_failed()) { return -1; - const double tock = MPI_Wtime(); + } - /* MPI returns s, return value should be in ms. */ + /* MPI returns in seconds, returned value should be in ms. */ return 1000. * (tock - tick) / int_steps; } @@ -136,10 +147,10 @@ void tune_skin(double min_skin, double max_skin, double tol, int int_steps, b = max_permissible_skin; while (fabs(a - b) > tol) { - mpi_call_all(mpi_set_skin_local, a); + mpi_set_skin_local(a); auto const time_a = time_calc(int_steps); - mpi_call_all(mpi_set_skin_local, b); + mpi_set_skin_local(b); auto const time_b = time_calc(int_steps); if (time_a > time_b) { @@ -149,5 +160,5 @@ void tune_skin(double min_skin, double max_skin, double tol, int int_steps, } } auto const new_skin = 0.5 * (a + b); - mpi_call_all(mpi_set_skin_local, new_skin); + mpi_set_skin_local(new_skin); } diff --git a/src/script_interface/cell_system/CellSystem.cpp b/src/script_interface/cell_system/CellSystem.cpp index 6392e6aeedb..553af6321cb 100644 --- a/src/script_interface/cell_system/CellSystem.cpp +++ b/src/script_interface/cell_system/CellSystem.cpp @@ -80,11 +80,9 @@ CellSystem::CellSystem() { throw std::invalid_argument(error_msg + reason.str()); } try { - ::node_grid = new_node_grid; - on_node_grid_change(); + set_node_grid(new_node_grid); } catch (...) { - ::node_grid = old_node_grid; - on_node_grid_change(); + set_node_grid(old_node_grid); throw; } }); @@ -220,13 +218,11 @@ Variant CellSystem::do_call_method(std::string const &name, return out; } if (name == "tune_skin") { - if (context()->is_head_node()) { - tune_skin(get_value(params, "min_skin"), - get_value(params, "max_skin"), - get_value(params, "tol"), - get_value(params, "int_steps"), - get_value_or(params, "adjust_max_skin", false)); - } + tune_skin(get_value(params, "min_skin"), + get_value(params, "max_skin"), + get_value(params, "tol"), + get_value(params, "int_steps"), + get_value_or(params, "adjust_max_skin", false)); return ::skin; } return {}; diff --git a/src/script_interface/system/System.cpp b/src/script_interface/system/System.cpp index aa11cbe7bcc..203ea1b3662 100644 --- a/src/script_interface/system/System.cpp +++ b/src/script_interface/system/System.cpp @@ -53,29 +53,29 @@ Variant System::do_call_method(std::string const &name, return {}; } if (name == "rescale_boxl") { - if (context()->is_head_node()) { - auto const coord = get_value(parameters, "coord"); - auto const length = get_value(parameters, "length"); - auto const scale = (coord == 3) ? length * ::box_geo.length_inv()[0] - : length * ::box_geo.length_inv()[coord]; + auto const coord = get_value(parameters, "coord"); + auto const length = get_value(parameters, "length"); + auto const scale = (coord == 3) ? length * ::box_geo.length_inv()[0] + : length * ::box_geo.length_inv()[coord]; + context()->parallel_try_catch([&]() { if (length <= 0.) { throw std::domain_error("Parameter 'd_new' be > 0"); } - auto new_value = Utils::Vector3d{}; - if (coord == 3) { - new_value = Utils::Vector3d::broadcast(length); - } else { - new_value = ::box_geo.length(); - new_value[coord] = length; - } - // when shrinking, rescale the particles first - if (scale <= 1.) { - mpi_rescale_particles(coord, scale); - } - mpi_set_box_length(new_value); - if (scale > 1.) { - mpi_rescale_particles(coord, scale); - } + }); + auto new_value = Utils::Vector3d{}; + if (coord == 3) { + new_value = Utils::Vector3d::broadcast(length); + } else { + new_value = ::box_geo.length(); + new_value[coord] = length; + } + // when shrinking, rescale the particles first + if (scale <= 1.) { + rescale_particles(coord, scale); + } + set_box_length(new_value); + if (scale > 1.) { + rescale_particles(coord, scale); } return {}; } From 2e06a72eea7579d4eddee7267d96ad9037d185a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 16 Aug 2022 17:13:35 +0200 Subject: [PATCH 36/85] Invitation summer school 2022 --- Readme.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Readme.md b/Readme.md index 01dbe7d0638..2ac2d98d3c1 100644 --- a/Readme.md +++ b/Readme.md @@ -1,3 +1,9 @@ +# Invitation to the ESPResSo Summer School 2022 + +[![CECAM Flagship School registration link](https://img.shields.io/badge/CECAM%20Flagship%20School-Register%20Now-blue?style=for-the-badge)](https://www.cecam.org/workshop-details/1146) + +The summer school "Simulating the dynamics of soft matter with ESPResSo, PyStencils and LbmPy" will take place on October 10-14, 2022, in Stuttgart. Registration is now open on [CECAM](https://www.cecam.org/workshop-details/1146). + # ESPResSo [![GitLab CI](https://gitlab.icp.uni-stuttgart.de/espressomd/espresso/badges/python/pipeline.svg)](https://gitlab.icp.uni-stuttgart.de/espressomd/espresso/commits/python) From 9ac01dd728cefc4f15ac839c48af79d3fc013505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 17 Aug 2022 13:52:29 +0200 Subject: [PATCH 37/85] doc: Use https to avoid url redirection --- Readme.md | 6 +++--- doc/sphinx/conf.py.in | 2 +- doc/sphinx/introduction.rst | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Readme.md b/Readme.md index 2ac2d98d3c1..22d3f227dea 100644 --- a/Readme.md +++ b/Readme.md @@ -62,7 +62,7 @@ walk you through the basic usage of ESPResSo. Advanced simulation methods are extensively documented, with examples and links to the relevant literature. Additional resources such as tutorials and doxygen documentation can be found on https://espressomd.github.io. -The official website is http://espressomd.org/wordpress/. +The official website is https://espressomd.org/wordpress/. ## Installation @@ -80,7 +80,7 @@ to use 4.1.4. ### Join the community Please consider subscribing to our -[mailing list](http://espressomd.org/wordpress/community-and-support/mailing-lists/) +[mailing list](https://espressomd.org/wordpress/community-and-support/mailing-lists/) if you're actively using ESPResSo, as we occasionally need community feedback when making decisions on the future of specific features in upcoming releases. You'll also get notifications on bugfix releases. @@ -89,7 +89,7 @@ upcoming releases. You'll also get notifications on bugfix releases. If you use ESPResSo to publish scientific results, we would ask you to acknowledge this usage by mentioning the software with its version number and -[citing the relevant papers](http://espressomd.org/wordpress/about/please-cite-us/). +[citing the relevant papers](https://espressomd.org/wordpress/about/please-cite-us/). A number of algorithms in ESPResSo are fairly advanced and unique to ESPResSo. The authors of these contributions kindly ask you to cite the relevant publications, as indicated in the documentation. For detailed instructions, see diff --git a/doc/sphinx/conf.py.in b/doc/sphinx/conf.py.in index 50b681bc2f3..641a1dffccd 100644 --- a/doc/sphinx/conf.py.in +++ b/doc/sphinx/conf.py.in @@ -268,7 +268,7 @@ def setup(app): # -- Options for Cython --------------------------------------------------- -# see http://opendreamkit.org/2017/06/09/CythonSphinx/ why this is needed +# see https://opendreamkit.org/2017/06/09/CythonSphinx/ why this is needed def isfunction(obj): return hasattr(type(obj), "__code__") diff --git a/doc/sphinx/introduction.rst b/doc/sphinx/introduction.rst index af6a22a3b3d..a89e174c202 100644 --- a/doc/sphinx/introduction.rst +++ b/doc/sphinx/introduction.rst @@ -548,7 +548,7 @@ our policies on :ref:`bug reports ` and Releases from 4.0 onward can be found on `GitHub `_. Older releases from 2.1 to 3.3 can be found in -`GNU Savannah `_. +`GNU Savannah `_. See our policy on :ref:`API backward compatibility ` if you need more details. From d57998302a1cc16fad0c913259b33c3430fbfd84 Mon Sep 17 00:00:00 2001 From: Christoph Lohrmann Date: Tue, 23 Aug 2022 11:58:34 +0200 Subject: [PATCH 38/85] pitchforkize config --- cmake/MyConfig.cmake | 4 +-- cmake/version.cmake | 8 +++-- src/config/CMakeLists.txt | 34 ++++++++++++------- src/config/gen_featureconfig.py | 10 +++--- src/config/include/config/CMakeLists.txt | 1 + src/config/{ => include/config}/config.hpp | 2 +- .../{ => include/config}/version.hpp.in | 0 src/config/src/CMakeLists.txt | 1 + src/core/EspressoSystemInterface.hpp | 2 +- src/core/EspressoSystemStandAlone.cpp | 2 +- src/core/Observable_stat.cpp | 2 +- src/core/Particle.hpp | 2 +- src/core/SystemInterface.cpp | 2 +- src/core/SystemInterface.hpp | 2 +- src/core/bonded_interactions/angle_common.hpp | 2 +- .../bonded_interactions/bonded_coulomb.hpp | 2 +- .../bonded_interactions/bonded_coulomb_sr.hpp | 2 +- src/core/bonded_interactions/bonded_tab.hpp | 2 +- src/core/bonded_interactions/dihedral.hpp | 2 +- src/core/bonded_interactions/fene.hpp | 2 +- src/core/bonded_interactions/harmonic.hpp | 2 +- src/core/cell_system/CellStructure.hpp | 2 +- src/core/cluster_analysis/Cluster.cpp | 2 +- src/core/collision.hpp | 2 +- .../constraints/HomogeneousMagneticField.cpp | 2 +- src/core/constraints/ShapeBasedConstraint.cpp | 2 +- src/core/cuda_common_cuda.cu | 2 +- src/core/cuda_init.cpp | 2 +- src/core/cuda_init.hpp | 2 +- src/core/cuda_interface.cpp | 2 +- src/core/cuda_interface.hpp | 2 +- src/core/cuda_utils.hpp | 2 +- src/core/dpd.cpp | 2 +- src/core/dpd.hpp | 2 +- src/core/electrostatics/actor.hpp | 2 +- src/core/electrostatics/coulomb.cpp | 2 +- src/core/electrostatics/coulomb.hpp | 2 +- src/core/electrostatics/coulomb_inline.hpp | 2 +- src/core/electrostatics/debye_hueckel.hpp | 2 +- src/core/electrostatics/elc.cpp | 2 +- src/core/electrostatics/elc.hpp | 2 +- src/core/electrostatics/icc.cpp | 2 +- src/core/electrostatics/icc.hpp | 2 +- src/core/electrostatics/mmm-modpsi.cpp | 2 +- src/core/electrostatics/mmm1d.cpp | 2 +- src/core/electrostatics/mmm1d.hpp | 2 +- src/core/electrostatics/mmm1d_gpu.cpp | 2 +- src/core/electrostatics/mmm1d_gpu.hpp | 2 +- src/core/electrostatics/mmm1d_gpu_cuda.cu | 2 +- src/core/electrostatics/p3m.hpp | 2 +- src/core/electrostatics/p3m_gpu.cpp | 2 +- src/core/electrostatics/p3m_gpu.hpp | 2 +- src/core/electrostatics/p3m_gpu_cuda.cu | 2 +- src/core/electrostatics/p3m_gpu_error.hpp | 2 +- src/core/electrostatics/reaction_field.hpp | 2 +- src/core/electrostatics/registration.hpp | 2 +- src/core/electrostatics/scafacos.hpp | 2 +- src/core/electrostatics/scafacos_impl.cpp | 2 +- src/core/electrostatics/scafacos_impl.hpp | 2 +- src/core/electrostatics/specfunc.cuh | 2 +- src/core/energy_inline.hpp | 2 +- src/core/errorhandling.hpp | 2 +- src/core/event.cpp | 2 +- src/core/exclusions.cpp | 2 +- src/core/exclusions.hpp | 2 +- src/core/forces_inline.hpp | 2 +- src/core/galilei/Galilei.cpp | 2 +- .../grid_based_algorithms/electrokinetics.hpp | 2 +- .../electrokinetics_cuda.cu | 2 +- src/core/grid_based_algorithms/halo.cpp | 2 +- src/core/grid_based_algorithms/lb.hpp | 2 +- .../grid_based_algorithms/lb_boundaries.hpp | 2 +- .../lb_collective_interface.cpp | 2 +- .../grid_based_algorithms/lb_interface.cpp | 2 +- .../grid_based_algorithms/lb_interface.hpp | 2 +- .../lb_interpolation.cpp | 2 +- .../lb_particle_coupling.cpp | 2 +- .../lbboundaries/LBBoundary.hpp | 2 +- src/core/grid_based_algorithms/lbgpu.cpp | 2 +- src/core/grid_based_algorithms/lbgpu.cuh | 2 +- src/core/grid_based_algorithms/lbgpu.hpp | 2 +- src/core/grid_based_algorithms/lbgpu_cuda.cu | 2 +- src/core/immersed_boundaries.hpp | 2 +- .../immersed_boundary/ImmersedBoundaries.hpp | 2 +- src/core/immersed_boundary/ibm_tribend.hpp | 2 +- src/core/immersed_boundary/ibm_triel.hpp | 2 +- src/core/integrators/brownian_inline.hpp | 2 +- src/core/integrators/steepest_descent.cpp | 2 +- .../integrators/stokesian_dynamics_inline.hpp | 2 +- .../integrators/velocity_verlet_inline.hpp | 2 +- src/core/integrators/velocity_verlet_npt.cpp | 2 +- src/core/integrators/velocity_verlet_npt.hpp | 2 +- src/core/io/writer/h5md_core.cpp | 2 +- src/core/magnetostatics/barnes_hut_gpu.cpp | 2 +- src/core/magnetostatics/barnes_hut_gpu.hpp | 2 +- .../magnetostatics/barnes_hut_gpu_cuda.cu | 2 +- .../magnetostatics/barnes_hut_gpu_cuda.cuh | 2 +- src/core/magnetostatics/dds.cpp | 2 +- src/core/magnetostatics/dds.hpp | 2 +- src/core/magnetostatics/dds_gpu.cpp | 2 +- src/core/magnetostatics/dds_gpu.hpp | 2 +- src/core/magnetostatics/dds_gpu_cuda.cu | 2 +- src/core/magnetostatics/dds_gpu_cuda.cuh | 2 +- src/core/magnetostatics/dds_replica.cpp | 2 +- src/core/magnetostatics/dds_replica.hpp | 2 +- src/core/magnetostatics/dipoles.cpp | 2 +- src/core/magnetostatics/dipoles.hpp | 2 +- src/core/magnetostatics/dipoles_inline.hpp | 2 +- src/core/magnetostatics/dlc.cpp | 2 +- src/core/magnetostatics/dlc.hpp | 2 +- src/core/magnetostatics/dp3m.cpp | 2 +- src/core/magnetostatics/dp3m.hpp | 2 +- src/core/magnetostatics/registration.hpp | 2 +- src/core/magnetostatics/scafacos.hpp | 2 +- src/core/magnetostatics/scafacos_impl.cpp | 2 +- src/core/magnetostatics/scafacos_impl.hpp | 2 +- .../VerletCriterion.hpp | 2 +- .../nonbonded_interactions/bmhtf-nacl.hpp | 2 +- .../nonbonded_interactions/buckingham.hpp | 2 +- src/core/nonbonded_interactions/gaussian.hpp | 2 +- src/core/nonbonded_interactions/gay_berne.hpp | 2 +- src/core/nonbonded_interactions/hat.hpp | 2 +- src/core/nonbonded_interactions/hertzian.hpp | 2 +- src/core/nonbonded_interactions/lj.hpp | 2 +- src/core/nonbonded_interactions/ljcos.hpp | 2 +- src/core/nonbonded_interactions/ljcos2.hpp | 2 +- src/core/nonbonded_interactions/ljgen.hpp | 2 +- src/core/nonbonded_interactions/morse.hpp | 2 +- .../nonbonded_interaction_data.hpp | 2 +- .../nonbonded_interactions/nonbonded_tab.hpp | 2 +- .../nonbonded_interactions/smooth_step.hpp | 2 +- .../nonbonded_interactions/soft_sphere.hpp | 2 +- src/core/nonbonded_interactions/thole.hpp | 2 +- src/core/nonbonded_interactions/wca.hpp | 2 +- src/core/npt.cpp | 2 +- src/core/npt.hpp | 2 +- src/core/object-in-fluid/oif_local_forces.hpp | 2 +- src/core/observables/ParticleTraits.hpp | 2 +- src/core/p3m/TuningAlgorithm.cpp | 2 +- src/core/p3m/TuningAlgorithm.hpp | 2 +- src/core/p3m/TuningLogger.hpp | 2 +- src/core/p3m/common.cpp | 2 +- src/core/p3m/common.hpp | 2 +- src/core/p3m/data_struct.hpp | 2 +- src/core/p3m/fft.cpp | 2 +- src/core/p3m/fft.hpp | 2 +- src/core/p3m/influence_function_dipolar.hpp | 2 +- src/core/p3m/send_mesh.cpp | 2 +- src/core/p3m/send_mesh.hpp | 2 +- src/core/particle_data.cpp | 2 +- src/core/particle_data.hpp | 2 +- src/core/pressure.cpp | 2 +- src/core/pressure_inline.hpp | 2 +- src/core/rattle.hpp | 2 +- .../reaction_methods/ReactionAlgorithm.cpp | 2 +- .../reaction_methods/ReactionAlgorithm.hpp | 2 +- src/core/rotate_system.cpp | 2 +- src/core/rotation.hpp | 2 +- src/core/scafacos/ScafacosContext.cpp | 2 +- src/core/scafacos/ScafacosContext.hpp | 2 +- src/core/scafacos/ScafacosContextBase.cpp | 2 +- src/core/scafacos/ScafacosContextBase.hpp | 2 +- src/core/stokesian_dynamics/sd_interface.cpp | 2 +- src/core/stokesian_dynamics/sd_interface.hpp | 2 +- src/core/thermostat.cpp | 2 +- src/core/thermostat.hpp | 2 +- src/core/thermostats/brownian_inline.hpp | 2 +- src/core/thermostats/langevin_inline.hpp | 2 +- src/core/thermostats/npt_inline.hpp | 2 +- .../EspressoSystemInterface_test.cpp | 2 +- .../Particle_serialization_test.cpp | 2 +- src/core/unit_tests/Particle_test.cpp | 2 +- src/core/unit_tests/VerletCriterion_test.cpp | 2 +- src/core/unit_tests/Verlet_list_test.cpp | 2 +- src/core/unit_tests/rotation_test.cpp | 2 +- src/core/unit_tests/thermostats_test.cpp | 2 +- src/core/virtual_sites.cpp | 2 +- src/core/virtual_sites.hpp | 2 +- src/core/virtual_sites/VirtualSites.hpp | 2 +- .../VirtualSitesInertialessTracers.cpp | 2 +- .../VirtualSitesInertialessTracers.hpp | 2 +- src/core/virtual_sites/VirtualSitesOff.hpp | 2 +- .../virtual_sites/VirtualSitesRelative.hpp | 2 +- .../virtual_sites/lb_inertialess_tracers.cpp | 2 +- .../virtual_sites/lb_inertialess_tracers.hpp | 2 +- .../lb_inertialess_tracers_cuda.cu | 2 +- .../lb_inertialess_tracers_cuda_interface.cpp | 2 +- .../lb_inertialess_tracers_cuda_interface.hpp | 2 +- src/python/espressomd/gen_pxiconfig.py | 2 +- src/python/espressomd/interactions.pxd | 2 +- .../analysis/ObservableStat.cpp | 2 +- src/script_interface/code_info/CodeInfo.cpp | 4 +-- .../code_info/CodeVersion.cpp | 2 +- .../CollisionDetection.hpp | 2 +- .../collision_detection/initialize.cpp | 2 +- src/script_interface/electrostatics/Actor.hpp | 2 +- .../electrostatics/Actor_impl.hpp | 2 +- .../electrostatics/CoulombMMM1D.hpp | 2 +- .../electrostatics/CoulombMMM1DGpu.hpp | 2 +- .../electrostatics/CoulombP3M.hpp | 2 +- .../electrostatics/CoulombP3MGPU.hpp | 2 +- .../electrostatics/CoulombScafacos.hpp | 2 +- .../electrostatics/DebyeHueckel.hpp | 2 +- .../ElectrostaticLayerCorrection.hpp | 2 +- .../electrostatics/ICCStar.hpp | 2 +- .../electrostatics/ReactionField.hpp | 2 +- .../electrostatics/initialize.cpp | 2 +- src/script_interface/h5md/h5md.cpp | 2 +- src/script_interface/h5md/h5md.hpp | 2 +- src/script_interface/h5md/initialize.cpp | 2 +- src/script_interface/h5md/initialize.hpp | 2 +- src/script_interface/initialize.cpp | 2 +- .../integrators/StokesianDynamics.cpp | 2 +- .../integrators/StokesianDynamics.hpp | 2 +- .../integrators/VelocityVerletIsoNPT.cpp | 2 +- .../integrators/VelocityVerletIsoNPT.hpp | 2 +- .../integrators/initialize.cpp | 2 +- .../lbboundaries/LBBoundaries.hpp | 2 +- .../lbboundaries/LBBoundary.hpp | 2 +- src/script_interface/magnetostatics/Actor.hpp | 2 +- .../magnetostatics/Actor_impl.hpp | 2 +- .../magnetostatics/DipolarBarnesHutGpu.hpp | 2 +- .../magnetostatics/DipolarDirectSum.hpp | 2 +- .../magnetostatics/DipolarDirectSumGpu.hpp | 2 +- .../DipolarDirectSumWithReplica.hpp | 2 +- .../magnetostatics/DipolarLayerCorrection.hpp | 2 +- .../magnetostatics/DipolarP3M.hpp | 2 +- .../magnetostatics/DipolarScafacos.hpp | 2 +- .../magnetostatics/initialize.cpp | 2 +- .../observables/ParamlessObservable.hpp | 2 +- .../observables/initialize.cpp | 2 +- .../particle_data/ParticleHandle.cpp | 2 +- src/script_interface/scafacos/scafacos.cpp | 2 +- src/script_interface/scafacos/scafacos.hpp | 2 +- .../system/CudaInitHandle.cpp | 2 +- src/script_interface/system/System.cpp | 2 +- src/script_interface/tests/Actors_test.cpp | 2 +- .../ActiveVirtualSitesHandle.hpp | 2 +- .../virtual_sites/VirtualSites.hpp | 2 +- .../VirtualSitesInertialessTracers.hpp | 2 +- .../virtual_sites/VirtualSitesOff.hpp | 2 +- .../virtual_sites/VirtualSitesRelative.hpp | 2 +- .../virtual_sites/initialize.cpp | 2 +- 243 files changed, 272 insertions(+), 260 deletions(-) create mode 100644 src/config/include/config/CMakeLists.txt rename src/config/{ => include/config}/config.hpp (98%) rename src/config/{ => include/config}/version.hpp.in (100%) create mode 100644 src/config/src/CMakeLists.txt diff --git a/cmake/MyConfig.cmake b/cmake/MyConfig.cmake index c0fbb5c9097..ef81522c715 100644 --- a/cmake/MyConfig.cmake +++ b/cmake/MyConfig.cmake @@ -37,9 +37,9 @@ if(NOT MYCONFIG_FILE) endif() configure_file(${MYCONFIG_FILE} - ${CMAKE_BINARY_DIR}/src/config/myconfig-final.hpp COPYONLY) + ${CMAKE_BINARY_DIR}/src/config/include/config/myconfig-final.hpp COPYONLY) add_custom_target(myconfig - DEPENDS ${CMAKE_BINARY_DIR}/src/config/myconfig-final.hpp) + DEPENDS ${CMAKE_BINARY_DIR}/src/config/include/config/myconfig-final.hpp) message(STATUS "Config file: ${MYCONFIG_FILE}") # Clear variable, otherwise cmake must be run by hand to detect myconfig. # Also prevents find_file from skipping when variable is already set. diff --git a/cmake/version.cmake b/cmake/version.cmake index a29ec0be474..09ef3e3d2f9 100644 --- a/cmake/version.cmake +++ b/cmake/version.cmake @@ -25,6 +25,8 @@ if(GIT_EXECUTABLE) endif(GIT_EXECUTABLE) -configure_file(${PROJECT_SOURCE_DIR}/src/config/version.hpp.in version.hpp.tmp) -execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different version.hpp.tmp - version.hpp) +configure_file(${PROJECT_SOURCE_DIR}/src/config/include/config/version.hpp.in + ${CMAKE_BINARY_DIR}/include/config/version.hpp.tmp) +execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_BINARY_DIR}/include/config/version.hpp.tmp + ${CMAKE_BINARY_DIR}/include/config/version.hpp) diff --git a/src/config/CMakeLists.txt b/src/config/CMakeLists.txt index 223ddba14ed..b9edbbfd4a9 100644 --- a/src/config/CMakeLists.txt +++ b/src/config/CMakeLists.txt @@ -17,29 +17,34 @@ # along with this program. If not, see . # -configure_file(${CMAKE_SOURCE_DIR}/cmake/cmake_config.cmakein cmake_config.hpp) +add_subdirectory(include/config) +add_subdirectory(src) + +configure_file(${CMAKE_SOURCE_DIR}/cmake/cmake_config.cmakein + ${CMAKE_CURRENT_BINARY_DIR}/include/config/cmake_config.hpp) add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/config-features.hpp - ${CMAKE_CURRENT_BINARY_DIR}/config-features.cpp + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/include/config/config-features.hpp + ${CMAKE_CURRENT_BINARY_DIR}/src/config-features.cpp COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/gen_featureconfig.py ${CMAKE_CURRENT_SOURCE_DIR}/features.def - ${CMAKE_CURRENT_BINARY_DIR}/config-features.hpp - ${CMAKE_CURRENT_BINARY_DIR}/config-features.cpp + ${CMAKE_CURRENT_BINARY_DIR}/include/config/config-features.hpp + ${CMAKE_CURRENT_BINARY_DIR}/src/config-features.cpp DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/features.def ${CMAKE_CURRENT_SOURCE_DIR}/gen_featureconfig.py) add_custom_target( generate_config_features - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/config-features.hpp - ${CMAKE_CURRENT_BINARY_DIR}/config-features.cpp) + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/include/config/config-features.hpp + ${CMAKE_CURRENT_BINARY_DIR}/src/config-features.cpp) add_custom_target( check_myconfig COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/check_myconfig.py ${CMAKE_CXX_COMPILER} ${CMAKE_CURRENT_SOURCE_DIR}/features.def - ${CMAKE_CURRENT_BINARY_DIR}/myconfig-final.hpp cmake_config.hpp + ${CMAKE_CURRENT_BINARY_DIR}/include/config/myconfig-final.hpp + ${CMAKE_CURRENT_BINARY_DIR}/include/config/cmake_config.hpp DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/features.def myconfig) execute_process( @@ -47,14 +52,15 @@ execute_process( ${CMAKE_CURRENT_SOURCE_DIR}/features.def OUTPUT_FILE ${CMAKE_BINARY_DIR}/myconfig-sample.hpp) -add_library(espresso_config SHARED config-features.cpp) +add_library(espresso_config SHARED src/config-features.cpp) add_library(espresso::config ALIAS espresso_config) add_dependencies(espresso_config myconfig check_myconfig generate_config_features) install(TARGETS espresso_config LIBRARY DESTINATION ${ESPRESSO_INSTALL_PYTHON}/espressomd) -target_include_directories(espresso_config PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_BINARY_DIR}) +target_include_directories( + espresso_config PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include) find_package(Git) # Parse repository info from git if available Run this at build time to avoid @@ -66,6 +72,8 @@ add_custom_target( -DPROJECT_VERSION=${PROJECT_VERSION} -DGIT_EXECUTABLE=${GIT_EXECUTABLE} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -P ${PROJECT_SOURCE_DIR}/cmake/version.cmake) -set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES version.hpp - version.hpp.tmp) +set_property( + DIRECTORY APPEND + PROPERTY ADDITIONAL_MAKE_CLEAN_FILES include/config/version.hpp + include/config/version.hpp.tmp) add_dependencies(espresso_config version) diff --git a/src/config/gen_featureconfig.py b/src/config/gen_featureconfig.py index b8d713e9479..04edc08bf84 100644 --- a/src/config/gen_featureconfig.py +++ b/src/config/gen_featureconfig.py @@ -61,8 +61,8 @@ /* Handle definitions from CMake */ /*********************************/ -#include "cmake_config.hpp" -#include "myconfig-final.hpp" +#include "config/cmake_config.hpp" +#include "config/myconfig-final.hpp" """) external_template = string.Template(""" // $feature is external @@ -75,7 +75,7 @@ # Include definitions from CMake hfile.write(""" -#include "cmake_config.hpp" +#include "config/cmake_config.hpp" """) @@ -132,8 +132,8 @@ cfile.write(disclaimer) cfile.write(f""" -#include "config-features.hpp" -#include "config.hpp" +#include "config/config-features.hpp" +#include "config/config.hpp" /***********************/ /* Handle requirements */ diff --git a/src/config/include/config/CMakeLists.txt b/src/config/include/config/CMakeLists.txt new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/src/config/include/config/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/src/config/config.hpp b/src/config/include/config/config.hpp similarity index 98% rename from src/config/config.hpp rename to src/config/include/config/config.hpp index 03634264667..f392f9fff80 100644 --- a/src/config/config.hpp +++ b/src/config/include/config/config.hpp @@ -37,7 +37,7 @@ #define MPICH_SKIP_MPICXX #endif -#include "config-features.hpp" +#include "config/config-features.hpp" /** P3M: Default for offset of first mesh point from the origin (left * down corner of the simulation box). diff --git a/src/config/version.hpp.in b/src/config/include/config/version.hpp.in similarity index 100% rename from src/config/version.hpp.in rename to src/config/include/config/version.hpp.in diff --git a/src/config/src/CMakeLists.txt b/src/config/src/CMakeLists.txt new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/src/config/src/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/src/core/EspressoSystemInterface.hpp b/src/core/EspressoSystemInterface.hpp index 9cfde74c391..335390f52f7 100644 --- a/src/core/EspressoSystemInterface.hpp +++ b/src/core/EspressoSystemInterface.hpp @@ -20,7 +20,7 @@ #define ESPRESSOSYSTEMINTERFACE_H #include "SystemInterface.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "cuda_interface.hpp" #include diff --git a/src/core/EspressoSystemStandAlone.cpp b/src/core/EspressoSystemStandAlone.cpp index 694818a1193..cfa2aa0c5e6 100644 --- a/src/core/EspressoSystemStandAlone.cpp +++ b/src/core/EspressoSystemStandAlone.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #include "EspressoSystemStandAlone.hpp" #include "MpiCallbacks.hpp" diff --git a/src/core/Observable_stat.cpp b/src/core/Observable_stat.cpp index 96d9892b624..527bad5da96 100644 --- a/src/core/Observable_stat.cpp +++ b/src/core/Observable_stat.cpp @@ -21,7 +21,7 @@ #include "Observable_stat.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "bonded_interactions/bonded_interaction_data.hpp" #include "communication.hpp" diff --git a/src/core/Particle.hpp b/src/core/Particle.hpp index 5aea6ceacdd..4c9a21d5f0f 100644 --- a/src/core/Particle.hpp +++ b/src/core/Particle.hpp @@ -19,7 +19,7 @@ #ifndef ESPRESSO_CORE_PARTICLE_HPP #define ESPRESSO_CORE_PARTICLE_HPP -#include "config.hpp" +#include "config/config.hpp" #include "BondList.hpp" diff --git a/src/core/SystemInterface.cpp b/src/core/SystemInterface.cpp index b3014dcaef2..47df16f2115 100644 --- a/src/core/SystemInterface.cpp +++ b/src/core/SystemInterface.cpp @@ -17,4 +17,4 @@ * along with this program. If not, see . */ #include "SystemInterface.hpp" -#include "config.hpp" +#include "config/config.hpp" diff --git a/src/core/SystemInterface.hpp b/src/core/SystemInterface.hpp index 9ffd1174563..3dfbf014351 100644 --- a/src/core/SystemInterface.hpp +++ b/src/core/SystemInterface.hpp @@ -19,7 +19,7 @@ #ifndef SYSTEMINTERFACE_H #define SYSTEMINTERFACE_H -#include "config.hpp" +#include "config/config.hpp" #include diff --git a/src/core/bonded_interactions/angle_common.hpp b/src/core/bonded_interactions/angle_common.hpp index 63ca6df11a8..aa4cf70fdfe 100644 --- a/src/core/bonded_interactions/angle_common.hpp +++ b/src/core/bonded_interactions/angle_common.hpp @@ -24,7 +24,7 @@ * Common code for functions calculating angle forces. */ -#include "config.hpp" +#include "config/config.hpp" #include "grid.hpp" #include diff --git a/src/core/bonded_interactions/bonded_coulomb.hpp b/src/core/bonded_interactions/bonded_coulomb.hpp index 927715daa0d..9665bffe422 100644 --- a/src/core/bonded_interactions/bonded_coulomb.hpp +++ b/src/core/bonded_interactions/bonded_coulomb.hpp @@ -25,7 +25,7 @@ * particle pairs. */ -#include "config.hpp" +#include "config/config.hpp" #include diff --git a/src/core/bonded_interactions/bonded_coulomb_sr.hpp b/src/core/bonded_interactions/bonded_coulomb_sr.hpp index 9d60d7b4b74..d59f865c11e 100644 --- a/src/core/bonded_interactions/bonded_coulomb_sr.hpp +++ b/src/core/bonded_interactions/bonded_coulomb_sr.hpp @@ -26,7 +26,7 @@ * interactions in combination with Thole damping. */ -#include "config.hpp" +#include "config/config.hpp" #include "Particle.hpp" diff --git a/src/core/bonded_interactions/bonded_tab.hpp b/src/core/bonded_interactions/bonded_tab.hpp index 450189b937f..0725aad8fe1 100644 --- a/src/core/bonded_interactions/bonded_tab.hpp +++ b/src/core/bonded_interactions/bonded_tab.hpp @@ -28,7 +28,7 @@ * Implementation in \ref bonded_tab.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #include "TabulatedPotential.hpp" #include "angle_common.hpp" diff --git a/src/core/bonded_interactions/dihedral.hpp b/src/core/bonded_interactions/dihedral.hpp index 665ed59fc86..63c28fef1cd 100644 --- a/src/core/bonded_interactions/dihedral.hpp +++ b/src/core/bonded_interactions/dihedral.hpp @@ -28,7 +28,7 @@ */ #include "BoxGeometry.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "grid.hpp" #include diff --git a/src/core/bonded_interactions/fene.hpp b/src/core/bonded_interactions/fene.hpp index 944ce8b4221..68091784336 100644 --- a/src/core/bonded_interactions/fene.hpp +++ b/src/core/bonded_interactions/fene.hpp @@ -26,7 +26,7 @@ * Implementation in \ref fene.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #include "errorhandling.hpp" #include diff --git a/src/core/bonded_interactions/harmonic.hpp b/src/core/bonded_interactions/harmonic.hpp index 108d64ebcca..d4af01869f3 100644 --- a/src/core/bonded_interactions/harmonic.hpp +++ b/src/core/bonded_interactions/harmonic.hpp @@ -24,7 +24,7 @@ * Routines to calculate the harmonic bond potential between particle pairs. */ -#include "config.hpp" +#include "config/config.hpp" #include "errorhandling.hpp" #include diff --git a/src/core/cell_system/CellStructure.hpp b/src/core/cell_system/CellStructure.hpp index cfee3c2fa72..42777ba94a0 100644 --- a/src/core/cell_system/CellStructure.hpp +++ b/src/core/cell_system/CellStructure.hpp @@ -33,7 +33,7 @@ #include "bond_error.hpp" #include "cell_system/Cell.hpp" #include "cell_system/CellStructureType.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "ghosts.hpp" #include diff --git a/src/core/cluster_analysis/Cluster.cpp b/src/core/cluster_analysis/Cluster.cpp index 1256bcce05e..f691169c584 100644 --- a/src/core/cluster_analysis/Cluster.cpp +++ b/src/core/cluster_analysis/Cluster.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef GSL #include "gsl/gsl_fit.h" diff --git a/src/core/collision.hpp b/src/core/collision.hpp index 77fdd5247ff..e46a6d7baf2 100644 --- a/src/core/collision.hpp +++ b/src/core/collision.hpp @@ -19,7 +19,7 @@ #ifndef CORE_COLLISION_HPP #define CORE_COLLISION_HPP -#include "config.hpp" +#include "config/config.hpp" #include "BondList.hpp" #include "Particle.hpp" diff --git a/src/core/constraints/HomogeneousMagneticField.cpp b/src/core/constraints/HomogeneousMagneticField.cpp index c45927ec0d2..05fb65ba0ef 100644 --- a/src/core/constraints/HomogeneousMagneticField.cpp +++ b/src/core/constraints/HomogeneousMagneticField.cpp @@ -20,7 +20,7 @@ #include "Observable_stat.hpp" #include "Particle.hpp" -#include "config.hpp" +#include "config/config.hpp" #include diff --git a/src/core/constraints/ShapeBasedConstraint.cpp b/src/core/constraints/ShapeBasedConstraint.cpp index 197dd5cb57f..a8121142114 100644 --- a/src/core/constraints/ShapeBasedConstraint.cpp +++ b/src/core/constraints/ShapeBasedConstraint.cpp @@ -22,7 +22,7 @@ #include "BoxGeometry.hpp" #include "Observable_stat.hpp" #include "communication.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "dpd.hpp" #include "energy_inline.hpp" #include "errorhandling.hpp" diff --git a/src/core/cuda_common_cuda.cu b/src/core/cuda_common_cuda.cu index f9006da8264..40240add32f 100644 --- a/src/core/cuda_common_cuda.cu +++ b/src/core/cuda_common_cuda.cu @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #include "ParticleRange.hpp" #include "cuda_init.hpp" diff --git a/src/core/cuda_init.cpp b/src/core/cuda_init.cpp index 975ac802c6f..fba9c233cf8 100644 --- a/src/core/cuda_init.cpp +++ b/src/core/cuda_init.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef CUDA diff --git a/src/core/cuda_init.hpp b/src/core/cuda_init.hpp index 3e9d10eca65..a663224db86 100644 --- a/src/core/cuda_init.hpp +++ b/src/core/cuda_init.hpp @@ -19,7 +19,7 @@ #ifndef CORE_CUDA_INIT_H #define CORE_CUDA_INIT_H -#include "config.hpp" +#include "config/config.hpp" #ifdef CUDA diff --git a/src/core/cuda_interface.cpp b/src/core/cuda_interface.cpp index 01ebcc3bd26..e02be9867b8 100644 --- a/src/core/cuda_interface.cpp +++ b/src/core/cuda_interface.cpp @@ -23,7 +23,7 @@ #include "EspressoSystemInterface.hpp" #include "communication.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "grid.hpp" #include "nonbonded_interactions/nonbonded_interaction_data.hpp" #include "serialization/CUDA_particle_data.hpp" diff --git a/src/core/cuda_interface.hpp b/src/core/cuda_interface.hpp index d9320ae1e9a..f79cf46af9e 100644 --- a/src/core/cuda_interface.hpp +++ b/src/core/cuda_interface.hpp @@ -19,7 +19,7 @@ #ifndef CORE_CUDA_INTERFACE_HPP #define CORE_CUDA_INTERFACE_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef CUDA diff --git a/src/core/cuda_utils.hpp b/src/core/cuda_utils.hpp index 88ce020b887..f65c92d1164 100644 --- a/src/core/cuda_utils.hpp +++ b/src/core/cuda_utils.hpp @@ -19,7 +19,7 @@ #ifndef CORE_CUDA_UTILS_HPP #define CORE_CUDA_UTILS_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef CUDA diff --git a/src/core/dpd.cpp b/src/core/dpd.cpp index d59b6007c12..8a22f988d8c 100644 --- a/src/core/dpd.cpp +++ b/src/core/dpd.cpp @@ -21,7 +21,7 @@ /** \file * Implementation of dpd.hpp. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef DPD diff --git a/src/core/dpd.hpp b/src/core/dpd.hpp index 091ca91cde7..ea9e7a7e42c 100644 --- a/src/core/dpd.hpp +++ b/src/core/dpd.hpp @@ -26,7 +26,7 @@ * Implementation in @ref dpd.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef DPD diff --git a/src/core/electrostatics/actor.hpp b/src/core/electrostatics/actor.hpp index 138be4ed5c5..674c288ff0f 100644 --- a/src/core/electrostatics/actor.hpp +++ b/src/core/electrostatics/actor.hpp @@ -19,7 +19,7 @@ #ifndef ESPRESSO_SRC_CORE_ELECTROSTATICS_ACTOR_HPP #define ESPRESSO_SRC_CORE_ELECTROSTATICS_ACTOR_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef ELECTROSTATICS diff --git a/src/core/electrostatics/coulomb.cpp b/src/core/electrostatics/coulomb.cpp index 3cb15150262..378666ffab4 100644 --- a/src/core/electrostatics/coulomb.cpp +++ b/src/core/electrostatics/coulomb.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef ELECTROSTATICS diff --git a/src/core/electrostatics/coulomb.hpp b/src/core/electrostatics/coulomb.hpp index 03b1944debd..1d0b831251e 100644 --- a/src/core/electrostatics/coulomb.hpp +++ b/src/core/electrostatics/coulomb.hpp @@ -19,7 +19,7 @@ #ifndef ESPRESSO_SRC_CORE_ELECTROSTATICS_COULOMB_HPP #define ESPRESSO_SRC_CORE_ELECTROSTATICS_COULOMB_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef ELECTROSTATICS diff --git a/src/core/electrostatics/coulomb_inline.hpp b/src/core/electrostatics/coulomb_inline.hpp index 996e8c3e74e..b13c1e45b43 100644 --- a/src/core/electrostatics/coulomb_inline.hpp +++ b/src/core/electrostatics/coulomb_inline.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_CORE_ELECTROSTATICS_COULOMB_INLINE_HPP #define ESPRESSO_SRC_CORE_ELECTROSTATICS_COULOMB_INLINE_HPP -#include "config.hpp" +#include "config/config.hpp" #include "electrostatics/coulomb.hpp" diff --git a/src/core/electrostatics/debye_hueckel.hpp b/src/core/electrostatics/debye_hueckel.hpp index eecbc940c5d..c0c72710ef0 100644 --- a/src/core/electrostatics/debye_hueckel.hpp +++ b/src/core/electrostatics/debye_hueckel.hpp @@ -27,7 +27,7 @@ #ifndef ESPRESSO_SRC_CORE_ELECTROSTATICS_DEBYE_HUECKEL_HPP #define ESPRESSO_SRC_CORE_ELECTROSTATICS_DEBYE_HUECKEL_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef ELECTROSTATICS diff --git a/src/core/electrostatics/elc.cpp b/src/core/electrostatics/elc.cpp index 445460f2c53..9d811608573 100644 --- a/src/core/electrostatics/elc.cpp +++ b/src/core/electrostatics/elc.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef P3M diff --git a/src/core/electrostatics/elc.hpp b/src/core/electrostatics/elc.hpp index e17c0da1d78..4b1326790e6 100644 --- a/src/core/electrostatics/elc.hpp +++ b/src/core/electrostatics/elc.hpp @@ -31,7 +31,7 @@ #ifndef ESPRESSO_SRC_CORE_ELECTROSTATICS_ELC_HPP #define ESPRESSO_SRC_CORE_ELECTROSTATICS_ELC_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef P3M diff --git a/src/core/electrostatics/icc.cpp b/src/core/electrostatics/icc.cpp index a36e9e15943..ca23e179a04 100644 --- a/src/core/electrostatics/icc.cpp +++ b/src/core/electrostatics/icc.cpp @@ -26,7 +26,7 @@ * \ref icc.hpp. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef ELECTROSTATICS diff --git a/src/core/electrostatics/icc.hpp b/src/core/electrostatics/icc.hpp index de1862d6046..7d79be0656f 100644 --- a/src/core/electrostatics/icc.hpp +++ b/src/core/electrostatics/icc.hpp @@ -46,7 +46,7 @@ * data organisation schemes, this is performed differently. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef ELECTROSTATICS diff --git a/src/core/electrostatics/mmm-modpsi.cpp b/src/core/electrostatics/mmm-modpsi.cpp index 59767e364bb..4fb556e686b 100644 --- a/src/core/electrostatics/mmm-modpsi.cpp +++ b/src/core/electrostatics/mmm-modpsi.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #include "mmm-modpsi.hpp" #include "specfunc.hpp" diff --git a/src/core/electrostatics/mmm1d.cpp b/src/core/electrostatics/mmm1d.cpp index c32647c3f7a..53f2b84a021 100644 --- a/src/core/electrostatics/mmm1d.cpp +++ b/src/core/electrostatics/mmm1d.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef ELECTROSTATICS diff --git a/src/core/electrostatics/mmm1d.hpp b/src/core/electrostatics/mmm1d.hpp index 95b47f8afc2..7792ea2431c 100644 --- a/src/core/electrostatics/mmm1d.hpp +++ b/src/core/electrostatics/mmm1d.hpp @@ -31,7 +31,7 @@ #ifndef ESPRESSO_SRC_CORE_ELECTROSTATICS_MMM1D_HPP #define ESPRESSO_SRC_CORE_ELECTROSTATICS_MMM1D_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef ELECTROSTATICS diff --git a/src/core/electrostatics/mmm1d_gpu.cpp b/src/core/electrostatics/mmm1d_gpu.cpp index 452741f9028..d1359544be5 100644 --- a/src/core/electrostatics/mmm1d_gpu.cpp +++ b/src/core/electrostatics/mmm1d_gpu.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef MMM1D_GPU diff --git a/src/core/electrostatics/mmm1d_gpu.hpp b/src/core/electrostatics/mmm1d_gpu.hpp index 4f05d218ed0..e524c7f1b99 100644 --- a/src/core/electrostatics/mmm1d_gpu.hpp +++ b/src/core/electrostatics/mmm1d_gpu.hpp @@ -29,7 +29,7 @@ #ifndef ESPRESSO_SRC_CORE_ELECTROSTATICS_MMM1D_GPU_HPP #define ESPRESSO_SRC_CORE_ELECTROSTATICS_MMM1D_GPU_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef MMM1D_GPU diff --git a/src/core/electrostatics/mmm1d_gpu_cuda.cu b/src/core/electrostatics/mmm1d_gpu_cuda.cu index d95b597c409..78ae0fb30ed 100644 --- a/src/core/electrostatics/mmm1d_gpu_cuda.cu +++ b/src/core/electrostatics/mmm1d_gpu_cuda.cu @@ -22,7 +22,7 @@ * near formulas of MMM1D on GPU, as well as the force kernels. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef MMM1D_GPU diff --git a/src/core/electrostatics/p3m.hpp b/src/core/electrostatics/p3m.hpp index 1c7e3e92755..4f682bf7fa8 100644 --- a/src/core/electrostatics/p3m.hpp +++ b/src/core/electrostatics/p3m.hpp @@ -35,7 +35,7 @@ #ifndef ESPRESSO_SRC_CORE_ELECTROSTATICS_P3M_HPP #define ESPRESSO_SRC_CORE_ELECTROSTATICS_P3M_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef P3M diff --git a/src/core/electrostatics/p3m_gpu.cpp b/src/core/electrostatics/p3m_gpu.cpp index 085ad69f1e5..5922644fcdd 100644 --- a/src/core/electrostatics/p3m_gpu.cpp +++ b/src/core/electrostatics/p3m_gpu.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef P3M #ifdef CUDA diff --git a/src/core/electrostatics/p3m_gpu.hpp b/src/core/electrostatics/p3m_gpu.hpp index cb91512f17b..8cfdcd24da8 100644 --- a/src/core/electrostatics/p3m_gpu.hpp +++ b/src/core/electrostatics/p3m_gpu.hpp @@ -25,7 +25,7 @@ * P3M electrostatics on GPU. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef P3M #ifdef CUDA diff --git a/src/core/electrostatics/p3m_gpu_cuda.cu b/src/core/electrostatics/p3m_gpu_cuda.cu index 317a21a9520..3a7c005e047 100644 --- a/src/core/electrostatics/p3m_gpu_cuda.cu +++ b/src/core/electrostatics/p3m_gpu_cuda.cu @@ -25,7 +25,7 @@ * The corresponding header file is @ref p3m_gpu_cuda.cuh. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef ELECTROSTATICS diff --git a/src/core/electrostatics/p3m_gpu_error.hpp b/src/core/electrostatics/p3m_gpu_error.hpp index d8c8025744f..8ee70a8d3ee 100644 --- a/src/core/electrostatics/p3m_gpu_error.hpp +++ b/src/core/electrostatics/p3m_gpu_error.hpp @@ -26,7 +26,7 @@ * Implementation in p3m_gpu_error_cuda.cu. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef CUDA double p3m_k_space_error_gpu(double prefactor, const int *mesh, int cao, diff --git a/src/core/electrostatics/reaction_field.hpp b/src/core/electrostatics/reaction_field.hpp index 38cf95d4d84..c3a63c17fe4 100644 --- a/src/core/electrostatics/reaction_field.hpp +++ b/src/core/electrostatics/reaction_field.hpp @@ -28,7 +28,7 @@ #ifndef ESPRESSO_SRC_CORE_ELECTROSTATICS_REACTION_FIELD_HPP #define ESPRESSO_SRC_CORE_ELECTROSTATICS_REACTION_FIELD_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef ELECTROSTATICS diff --git a/src/core/electrostatics/registration.hpp b/src/core/electrostatics/registration.hpp index cfa1303077d..7e14fab16a0 100644 --- a/src/core/electrostatics/registration.hpp +++ b/src/core/electrostatics/registration.hpp @@ -19,7 +19,7 @@ #ifndef ESPRESSO_SRC_CORE_ELECTROSTATICS_COULOMB_REGISTRATION_HPP #define ESPRESSO_SRC_CORE_ELECTROSTATICS_COULOMB_REGISTRATION_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef ELECTROSTATICS diff --git a/src/core/electrostatics/scafacos.hpp b/src/core/electrostatics/scafacos.hpp index 47db47a19d4..a4a801debbc 100644 --- a/src/core/electrostatics/scafacos.hpp +++ b/src/core/electrostatics/scafacos.hpp @@ -22,7 +22,7 @@ #ifndef ESPRESSO_SRC_CORE_ELECTROSTATICS_SCAFACOS_HPP #define ESPRESSO_SRC_CORE_ELECTROSTATICS_SCAFACOS_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef SCAFACOS diff --git a/src/core/electrostatics/scafacos_impl.cpp b/src/core/electrostatics/scafacos_impl.cpp index d4cec083c9a..3a7ce9b1021 100644 --- a/src/core/electrostatics/scafacos_impl.cpp +++ b/src/core/electrostatics/scafacos_impl.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef SCAFACOS diff --git a/src/core/electrostatics/scafacos_impl.hpp b/src/core/electrostatics/scafacos_impl.hpp index cbe9a2798da..9a9015e92a4 100644 --- a/src/core/electrostatics/scafacos_impl.hpp +++ b/src/core/electrostatics/scafacos_impl.hpp @@ -22,7 +22,7 @@ #ifndef ESPRESSO_SRC_CORE_ELECTROSTATICS_SCAFACOS_IMPL_HPP #define ESPRESSO_SRC_CORE_ELECTROSTATICS_SCAFACOS_IMPL_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef SCAFACOS diff --git a/src/core/electrostatics/specfunc.cuh b/src/core/electrostatics/specfunc.cuh index 7b3a3ef3416..87aa18ed7f6 100644 --- a/src/core/electrostatics/specfunc.cuh +++ b/src/core/electrostatics/specfunc.cuh @@ -48,7 +48,7 @@ #ifndef ESPRESSO_SRC_CORE_ELECTROSTATICS_SPECFUNC_CUH #define ESPRESSO_SRC_CORE_ELECTROSTATICS_SPECFUNC_CUH -#include "config.hpp" +#include "config/config.hpp" #include diff --git a/src/core/energy_inline.hpp b/src/core/energy_inline.hpp index 23921c6cd14..20123643186 100644 --- a/src/core/energy_inline.hpp +++ b/src/core/energy_inline.hpp @@ -24,7 +24,7 @@ * Energy calculation. */ -#include "config.hpp" +#include "config/config.hpp" #include "energy.hpp" diff --git a/src/core/errorhandling.hpp b/src/core/errorhandling.hpp index 1d79a1a94e6..7dc23c793c8 100644 --- a/src/core/errorhandling.hpp +++ b/src/core/errorhandling.hpp @@ -26,7 +26,7 @@ #ifndef ESPRESSO_SRC_CORE_ERROR_HANDLING_HPP #define ESPRESSO_SRC_CORE_ERROR_HANDLING_HPP -#include "config.hpp" +#include "config/config.hpp" #include "error_handling/RuntimeError.hpp" #include "error_handling/RuntimeErrorStream.hpp" diff --git a/src/core/event.cpp b/src/core/event.cpp index 3419a7cf206..1e1ea80524c 100644 --- a/src/core/event.cpp +++ b/src/core/event.cpp @@ -30,7 +30,7 @@ #include "cells.hpp" #include "collision.hpp" #include "communication.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "cuda_init.hpp" #include "cuda_interface.hpp" #include "cuda_utils.hpp" diff --git a/src/core/exclusions.cpp b/src/core/exclusions.cpp index adf63b0df3d..6ac72674183 100644 --- a/src/core/exclusions.cpp +++ b/src/core/exclusions.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef EXCLUSIONS diff --git a/src/core/exclusions.hpp b/src/core/exclusions.hpp index 87f47b7b567..d377c9bad59 100644 --- a/src/core/exclusions.hpp +++ b/src/core/exclusions.hpp @@ -21,7 +21,7 @@ #ifndef ESPRESSO_EXCLUSIONS_HPP #define ESPRESSO_EXCLUSIONS_HPP -#include "config.hpp" +#include "config/config.hpp" #include "Particle.hpp" diff --git a/src/core/forces_inline.hpp b/src/core/forces_inline.hpp index 874cc391c15..691bf5ec967 100644 --- a/src/core/forces_inline.hpp +++ b/src/core/forces_inline.hpp @@ -24,7 +24,7 @@ * Force calculation. */ -#include "config.hpp" +#include "config/config.hpp" #include "forces.hpp" diff --git a/src/core/galilei/Galilei.cpp b/src/core/galilei/Galilei.cpp index 0ead5f60e55..e1bb4c690c9 100644 --- a/src/core/galilei/Galilei.cpp +++ b/src/core/galilei/Galilei.cpp @@ -25,7 +25,7 @@ #include "ParticleRange.hpp" #include "cells.hpp" #include "communication.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "event.hpp" #include "grid.hpp" diff --git a/src/core/grid_based_algorithms/electrokinetics.hpp b/src/core/grid_based_algorithms/electrokinetics.hpp index e315a8a39ee..b9f611c2148 100644 --- a/src/core/grid_based_algorithms/electrokinetics.hpp +++ b/src/core/grid_based_algorithms/electrokinetics.hpp @@ -20,7 +20,7 @@ #ifndef CORE_GRID_BASED_ALGORITHMS_ELECTROKINETICS_HPP #define CORE_GRID_BASED_ALGORITHMS_ELECTROKINETICS_HPP -#include "config.hpp" +#include "config/config.hpp" #include "grid_based_algorithms/lb_boundaries.hpp" // note that we need to declare the ek_parameters struct and instantiate it for diff --git a/src/core/grid_based_algorithms/electrokinetics_cuda.cu b/src/core/grid_based_algorithms/electrokinetics_cuda.cu index 6cf9736a3f7..8a401bf9688 100644 --- a/src/core/grid_based_algorithms/electrokinetics_cuda.cu +++ b/src/core/grid_based_algorithms/electrokinetics_cuda.cu @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef CUDA /* Terminates at end of file */ #ifdef ELECTROKINETICS /* Terminates at end of file */ diff --git a/src/core/grid_based_algorithms/halo.cpp b/src/core/grid_based_algorithms/halo.cpp index 13329bff45d..e800d2af3be 100644 --- a/src/core/grid_based_algorithms/halo.cpp +++ b/src/core/grid_based_algorithms/halo.cpp @@ -25,7 +25,7 @@ * */ -#include "config.hpp" +#include "config/config.hpp" #include "communication.hpp" #include "grid.hpp" diff --git a/src/core/grid_based_algorithms/lb.hpp b/src/core/grid_based_algorithms/lb.hpp index 3b641af9b17..dac4508134f 100644 --- a/src/core/grid_based_algorithms/lb.hpp +++ b/src/core/grid_based_algorithms/lb.hpp @@ -36,7 +36,7 @@ * Implementation in lb.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #include "grid_based_algorithms/lattice.hpp" #include "grid_based_algorithms/lb-d3q19.hpp" #include "grid_based_algorithms/lb_constants.hpp" diff --git a/src/core/grid_based_algorithms/lb_boundaries.hpp b/src/core/grid_based_algorithms/lb_boundaries.hpp index 772405807dd..3dcf7e35f60 100644 --- a/src/core/grid_based_algorithms/lb_boundaries.hpp +++ b/src/core/grid_based_algorithms/lb_boundaries.hpp @@ -34,7 +34,7 @@ #include "lbboundaries/LBBoundary.hpp" -#include "config.hpp" +#include "config/config.hpp" #include diff --git a/src/core/grid_based_algorithms/lb_collective_interface.cpp b/src/core/grid_based_algorithms/lb_collective_interface.cpp index bedc3088b73..f1a9765c5c1 100644 --- a/src/core/grid_based_algorithms/lb_collective_interface.cpp +++ b/src/core/grid_based_algorithms/lb_collective_interface.cpp @@ -19,7 +19,7 @@ #include "MpiCallbacks.hpp" #include "communication.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "grid.hpp" #include "lb.hpp" #include "lb_constants.hpp" diff --git a/src/core/grid_based_algorithms/lb_interface.cpp b/src/core/grid_based_algorithms/lb_interface.cpp index 9970949ee14..3630d31d47c 100644 --- a/src/core/grid_based_algorithms/lb_interface.cpp +++ b/src/core/grid_based_algorithms/lb_interface.cpp @@ -20,7 +20,7 @@ #include "BoxGeometry.hpp" #include "MpiCallbacks.hpp" #include "communication.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "electrokinetics.hpp" #include "errorhandling.hpp" #include "grid.hpp" diff --git a/src/core/grid_based_algorithms/lb_interface.hpp b/src/core/grid_based_algorithms/lb_interface.hpp index ba35f73edf3..3dc14ecc537 100644 --- a/src/core/grid_based_algorithms/lb_interface.hpp +++ b/src/core/grid_based_algorithms/lb_interface.hpp @@ -19,7 +19,7 @@ #ifndef CORE_LB_INTERFACE #define CORE_LB_INTERFACE -#include "config.hpp" +#include "config/config.hpp" #include "grid_based_algorithms/lattice.hpp" #include diff --git a/src/core/grid_based_algorithms/lb_interpolation.cpp b/src/core/grid_based_algorithms/lb_interpolation.cpp index a0c4b7a8846..baeaac1446d 100644 --- a/src/core/grid_based_algorithms/lb_interpolation.cpp +++ b/src/core/grid_based_algorithms/lb_interpolation.cpp @@ -20,7 +20,7 @@ #include "lb_interpolation.hpp" #include "communication.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "grid_based_algorithms/lattice.hpp" #include "lb.hpp" diff --git a/src/core/grid_based_algorithms/lb_particle_coupling.cpp b/src/core/grid_based_algorithms/lb_particle_coupling.cpp index a158d3c9252..63628ceb7ad 100644 --- a/src/core/grid_based_algorithms/lb_particle_coupling.cpp +++ b/src/core/grid_based_algorithms/lb_particle_coupling.cpp @@ -21,7 +21,7 @@ #include "Particle.hpp" #include "cells.hpp" #include "communication.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "errorhandling.hpp" #include "grid.hpp" #include "grid_based_algorithms/OptionalCounter.hpp" diff --git a/src/core/grid_based_algorithms/lbboundaries/LBBoundary.hpp b/src/core/grid_based_algorithms/lbboundaries/LBBoundary.hpp index 0b9d521d935..ead5c0d7497 100644 --- a/src/core/grid_based_algorithms/lbboundaries/LBBoundary.hpp +++ b/src/core/grid_based_algorithms/lbboundaries/LBBoundary.hpp @@ -19,7 +19,7 @@ #ifndef LBBOUNDARIES_LBBOUNDARY_HPP #define LBBOUNDARIES_LBBOUNDARY_HPP -#include "config.hpp" +#include "config/config.hpp" #include #include diff --git a/src/core/grid_based_algorithms/lbgpu.cpp b/src/core/grid_based_algorithms/lbgpu.cpp index bdc614f25a7..d0b075dad79 100644 --- a/src/core/grid_based_algorithms/lbgpu.cpp +++ b/src/core/grid_based_algorithms/lbgpu.cpp @@ -22,7 +22,7 @@ * The corresponding header file is lbgpu.hpp. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef CUDA diff --git a/src/core/grid_based_algorithms/lbgpu.cuh b/src/core/grid_based_algorithms/lbgpu.cuh index dbbfc966505..352bed60596 100644 --- a/src/core/grid_based_algorithms/lbgpu.cuh +++ b/src/core/grid_based_algorithms/lbgpu.cuh @@ -25,7 +25,7 @@ #ifndef LBGPU_CUH #define LBGPU_CUH -#include "config.hpp" +#include "config/config.hpp" #ifdef CUDA #include diff --git a/src/core/grid_based_algorithms/lbgpu.hpp b/src/core/grid_based_algorithms/lbgpu.hpp index 31ab7b404b8..7f3d27b7c4c 100644 --- a/src/core/grid_based_algorithms/lbgpu.hpp +++ b/src/core/grid_based_algorithms/lbgpu.hpp @@ -25,7 +25,7 @@ #ifndef LBGPU_HPP #define LBGPU_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef CUDA #include "OptionalCounter.hpp" diff --git a/src/core/grid_based_algorithms/lbgpu_cuda.cu b/src/core/grid_based_algorithms/lbgpu_cuda.cu index 8a99c3c8c00..bd291f646b7 100644 --- a/src/core/grid_based_algorithms/lbgpu_cuda.cu +++ b/src/core/grid_based_algorithms/lbgpu_cuda.cu @@ -22,7 +22,7 @@ * The corresponding header file is lbgpu.cuh. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef CUDA diff --git a/src/core/immersed_boundaries.hpp b/src/core/immersed_boundaries.hpp index 1b8e3c2aa14..441952b9d0a 100644 --- a/src/core/immersed_boundaries.hpp +++ b/src/core/immersed_boundaries.hpp @@ -18,7 +18,7 @@ */ #ifndef IMMERSED_BOUNDARIES_HPP #define IMMERSED_BOUNDARIES_HPP -#include "config.hpp" +#include "config/config.hpp" #include "immersed_boundary/ImmersedBoundaries.hpp" extern ImmersedBoundaries immersed_boundaries; diff --git a/src/core/immersed_boundary/ImmersedBoundaries.hpp b/src/core/immersed_boundary/ImmersedBoundaries.hpp index af4a3af284b..93d5d5923c5 100644 --- a/src/core/immersed_boundary/ImmersedBoundaries.hpp +++ b/src/core/immersed_boundary/ImmersedBoundaries.hpp @@ -19,7 +19,7 @@ #ifndef IMMERSED_BOUNDARY_IMMERSED_BOUNDARIES_HPP #define IMMERSED_BOUNDARY_IMMERSED_BOUNDARIES_HPP -#include "config.hpp" +#include "config/config.hpp" #include "cell_system/CellStructure.hpp" diff --git a/src/core/immersed_boundary/ibm_tribend.hpp b/src/core/immersed_boundary/ibm_tribend.hpp index 735da471ca6..1445f84702b 100644 --- a/src/core/immersed_boundary/ibm_tribend.hpp +++ b/src/core/immersed_boundary/ibm_tribend.hpp @@ -20,7 +20,7 @@ #ifndef IBM_TRIBEND_H #define IBM_TRIBEND_H -#include "config.hpp" +#include "config/config.hpp" #include "Particle.hpp" diff --git a/src/core/immersed_boundary/ibm_triel.hpp b/src/core/immersed_boundary/ibm_triel.hpp index eaf8a8241c8..9039f094890 100644 --- a/src/core/immersed_boundary/ibm_triel.hpp +++ b/src/core/immersed_boundary/ibm_triel.hpp @@ -21,7 +21,7 @@ #define IBM_TRIEL_H #include "Particle.hpp" -#include "config.hpp" +#include "config/config.hpp" #include diff --git a/src/core/integrators/brownian_inline.hpp b/src/core/integrators/brownian_inline.hpp index c8bab4ea58b..3396d7cabe3 100644 --- a/src/core/integrators/brownian_inline.hpp +++ b/src/core/integrators/brownian_inline.hpp @@ -22,7 +22,7 @@ #ifndef INTEGRATORS_BROWNIAN_INLINE_HPP #define INTEGRATORS_BROWNIAN_INLINE_HPP -#include "config.hpp" +#include "config/config.hpp" #include "ParticleRange.hpp" #include "integrate.hpp" diff --git a/src/core/integrators/steepest_descent.cpp b/src/core/integrators/steepest_descent.cpp index d696dacae3f..8b7f41cdd4c 100644 --- a/src/core/integrators/steepest_descent.cpp +++ b/src/core/integrators/steepest_descent.cpp @@ -25,7 +25,7 @@ #include "ParticleRange.hpp" #include "cells.hpp" #include "communication.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "rotation.hpp" #include diff --git a/src/core/integrators/stokesian_dynamics_inline.hpp b/src/core/integrators/stokesian_dynamics_inline.hpp index 6347cba6357..1127ff3e685 100644 --- a/src/core/integrators/stokesian_dynamics_inline.hpp +++ b/src/core/integrators/stokesian_dynamics_inline.hpp @@ -19,7 +19,7 @@ #ifndef STOKESIAN_DYNAMICS_INLINE_HPP #define STOKESIAN_DYNAMICS_INLINE_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef STOKESIAN_DYNAMICS #include "ParticleRange.hpp" diff --git a/src/core/integrators/velocity_verlet_inline.hpp b/src/core/integrators/velocity_verlet_inline.hpp index 0e819946b5e..fce60cf017e 100644 --- a/src/core/integrators/velocity_verlet_inline.hpp +++ b/src/core/integrators/velocity_verlet_inline.hpp @@ -19,7 +19,7 @@ #ifndef INTEGRATORS_VELOCITY_VERLET_HPP #define INTEGRATORS_VELOCITY_VERLET_HPP -#include "config.hpp" +#include "config/config.hpp" #include "Particle.hpp" #include "ParticleRange.hpp" diff --git a/src/core/integrators/velocity_verlet_npt.cpp b/src/core/integrators/velocity_verlet_npt.cpp index cd2e71afce2..38264cafe1c 100644 --- a/src/core/integrators/velocity_verlet_npt.cpp +++ b/src/core/integrators/velocity_verlet_npt.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef NPT #include "velocity_verlet_npt.hpp" diff --git a/src/core/integrators/velocity_verlet_npt.hpp b/src/core/integrators/velocity_verlet_npt.hpp index 338f7f2cedd..9b707b8a421 100644 --- a/src/core/integrators/velocity_verlet_npt.hpp +++ b/src/core/integrators/velocity_verlet_npt.hpp @@ -19,7 +19,7 @@ #ifndef INTEGRATORS_VELOCITY_VERLET_NPT_HPP #define INTEGRATORS_VELOCITY_VERLET_NPT_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef NPT diff --git a/src/core/io/writer/h5md_core.cpp b/src/core/io/writer/h5md_core.cpp index bce29e03fa8..2e2c69cbce6 100644 --- a/src/core/io/writer/h5md_core.cpp +++ b/src/core/io/writer/h5md_core.cpp @@ -23,9 +23,9 @@ #include "BoxGeometry.hpp" #include "Particle.hpp" +#include "config/version.hpp" #include "h5md_specification.hpp" #include "lees_edwards/LeesEdwardsBC.hpp" -#include "version.hpp" #include diff --git a/src/core/magnetostatics/barnes_hut_gpu.cpp b/src/core/magnetostatics/barnes_hut_gpu.cpp index 997aca0c14b..733f1b953f5 100644 --- a/src/core/magnetostatics/barnes_hut_gpu.cpp +++ b/src/core/magnetostatics/barnes_hut_gpu.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLAR_BARNES_HUT diff --git a/src/core/magnetostatics/barnes_hut_gpu.hpp b/src/core/magnetostatics/barnes_hut_gpu.hpp index 1b0230d9275..a4ff0a9c94f 100644 --- a/src/core/magnetostatics/barnes_hut_gpu.hpp +++ b/src/core/magnetostatics/barnes_hut_gpu.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_CORE_MAGNETOSTATICS_BARNES_HUT_GPU_HPP #define ESPRESSO_SRC_CORE_MAGNETOSTATICS_BARNES_HUT_GPU_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLAR_BARNES_HUT diff --git a/src/core/magnetostatics/barnes_hut_gpu_cuda.cu b/src/core/magnetostatics/barnes_hut_gpu_cuda.cu index f18a8fdd8dc..9c1d9e00692 100644 --- a/src/core/magnetostatics/barnes_hut_gpu_cuda.cu +++ b/src/core/magnetostatics/barnes_hut_gpu_cuda.cu @@ -22,7 +22,7 @@ * The method is based on @cite burtscher11a. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLAR_BARNES_HUT diff --git a/src/core/magnetostatics/barnes_hut_gpu_cuda.cuh b/src/core/magnetostatics/barnes_hut_gpu_cuda.cuh index 8fa4d0c4f51..7177d67ba83 100644 --- a/src/core/magnetostatics/barnes_hut_gpu_cuda.cuh +++ b/src/core/magnetostatics/barnes_hut_gpu_cuda.cuh @@ -21,7 +21,7 @@ #ifndef ESPRESSO_SRC_CORE_MAGNETOSTATICS_BARNES_HUT_GPU_CUH #define ESPRESSO_SRC_CORE_MAGNETOSTATICS_BARNES_HUT_GPU_CUH -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLAR_BARNES_HUT diff --git a/src/core/magnetostatics/dds.cpp b/src/core/magnetostatics/dds.cpp index 961e0d3cf8a..8b330f1be4b 100644 --- a/src/core/magnetostatics/dds.cpp +++ b/src/core/magnetostatics/dds.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLES diff --git a/src/core/magnetostatics/dds.hpp b/src/core/magnetostatics/dds.hpp index 129f2ab02de..007909dc7da 100644 --- a/src/core/magnetostatics/dds.hpp +++ b/src/core/magnetostatics/dds.hpp @@ -22,7 +22,7 @@ #ifndef ESPRESSO_SRC_CORE_MAGNETOSTATICS_DIPOLAR_DIRECT_SUM_HPP #define ESPRESSO_SRC_CORE_MAGNETOSTATICS_DIPOLAR_DIRECT_SUM_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLES diff --git a/src/core/magnetostatics/dds_gpu.cpp b/src/core/magnetostatics/dds_gpu.cpp index 1184186f88d..47145828289 100644 --- a/src/core/magnetostatics/dds_gpu.cpp +++ b/src/core/magnetostatics/dds_gpu.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLAR_DIRECT_SUM diff --git a/src/core/magnetostatics/dds_gpu.hpp b/src/core/magnetostatics/dds_gpu.hpp index aa09a41e21a..484fb23cb95 100644 --- a/src/core/magnetostatics/dds_gpu.hpp +++ b/src/core/magnetostatics/dds_gpu.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_CORE_MAGNETOSTATICS_DIPOLAR_DIRECT_SUM_GPU_HPP #define ESPRESSO_SRC_CORE_MAGNETOSTATICS_DIPOLAR_DIRECT_SUM_GPU_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLAR_DIRECT_SUM diff --git a/src/core/magnetostatics/dds_gpu_cuda.cu b/src/core/magnetostatics/dds_gpu_cuda.cu index 5b76e38ca9e..2f3f063fe05 100644 --- a/src/core/magnetostatics/dds_gpu_cuda.cu +++ b/src/core/magnetostatics/dds_gpu_cuda.cu @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLAR_DIRECT_SUM diff --git a/src/core/magnetostatics/dds_gpu_cuda.cuh b/src/core/magnetostatics/dds_gpu_cuda.cuh index 3d73bf386c5..547a3e9783e 100644 --- a/src/core/magnetostatics/dds_gpu_cuda.cuh +++ b/src/core/magnetostatics/dds_gpu_cuda.cuh @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_CORE_MAGNETOSTATICS_DIPOLAR_DIRECT_SUM_GPU_CUH #define ESPRESSO_SRC_CORE_MAGNETOSTATICS_DIPOLAR_DIRECT_SUM_GPU_CUH -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLAR_DIRECT_SUM diff --git a/src/core/magnetostatics/dds_replica.cpp b/src/core/magnetostatics/dds_replica.cpp index 227f0ac40b7..dbaca1099aa 100644 --- a/src/core/magnetostatics/dds_replica.cpp +++ b/src/core/magnetostatics/dds_replica.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLES diff --git a/src/core/magnetostatics/dds_replica.hpp b/src/core/magnetostatics/dds_replica.hpp index f54e09de23c..917c7662fc2 100644 --- a/src/core/magnetostatics/dds_replica.hpp +++ b/src/core/magnetostatics/dds_replica.hpp @@ -22,7 +22,7 @@ #ifndef ESPRESSO_SRC_CORE_MAGNETOSTATICS_DIPOLAR_DIRECT_SUM_REPLICA_HPP #define ESPRESSO_SRC_CORE_MAGNETOSTATICS_DIPOLAR_DIRECT_SUM_REPLICA_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLES diff --git a/src/core/magnetostatics/dipoles.cpp b/src/core/magnetostatics/dipoles.cpp index 084143422e2..59694f79fb5 100644 --- a/src/core/magnetostatics/dipoles.cpp +++ b/src/core/magnetostatics/dipoles.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLES diff --git a/src/core/magnetostatics/dipoles.hpp b/src/core/magnetostatics/dipoles.hpp index 73a8a84cd75..acbae513ffc 100644 --- a/src/core/magnetostatics/dipoles.hpp +++ b/src/core/magnetostatics/dipoles.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_CORE_MAGNETOSTATICS_DIPOLES_HPP #define ESPRESSO_SRC_CORE_MAGNETOSTATICS_DIPOLES_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLES diff --git a/src/core/magnetostatics/dipoles_inline.hpp b/src/core/magnetostatics/dipoles_inline.hpp index 130fcb8bf2c..d7261ecf7e5 100644 --- a/src/core/magnetostatics/dipoles_inline.hpp +++ b/src/core/magnetostatics/dipoles_inline.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_CORE_MAGNETOSTATICS_DIPOLES_INLINE_HPP #define ESPRESSO_SRC_CORE_MAGNETOSTATICS_DIPOLES_INLINE_HPP -#include "config.hpp" +#include "config/config.hpp" #include "Particle.hpp" diff --git a/src/core/magnetostatics/dlc.cpp b/src/core/magnetostatics/dlc.cpp index 79ba4542df5..b343f65aee8 100644 --- a/src/core/magnetostatics/dlc.cpp +++ b/src/core/magnetostatics/dlc.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLES diff --git a/src/core/magnetostatics/dlc.hpp b/src/core/magnetostatics/dlc.hpp index a032479ee88..da4e901e30b 100644 --- a/src/core/magnetostatics/dlc.hpp +++ b/src/core/magnetostatics/dlc.hpp @@ -22,7 +22,7 @@ #ifndef ESPRESSO_SRC_CORE_MAGNETOSTATICS_DLC_HPP #define ESPRESSO_SRC_CORE_MAGNETOSTATICS_DLC_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLES diff --git a/src/core/magnetostatics/dp3m.cpp b/src/core/magnetostatics/dp3m.cpp index e9e64276583..3e9c28cf689 100644 --- a/src/core/magnetostatics/dp3m.cpp +++ b/src/core/magnetostatics/dp3m.cpp @@ -25,7 +25,7 @@ * By default the magnetic epsilon is metallic = 0. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef DP3M diff --git a/src/core/magnetostatics/dp3m.hpp b/src/core/magnetostatics/dp3m.hpp index edc957daf14..1098ec04af5 100644 --- a/src/core/magnetostatics/dp3m.hpp +++ b/src/core/magnetostatics/dp3m.hpp @@ -32,7 +32,7 @@ #ifndef ESPRESSO_SRC_CORE_MAGNETOSTATICS_DIPOLAR_P3M_HPP #define ESPRESSO_SRC_CORE_MAGNETOSTATICS_DIPOLAR_P3M_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef DP3M diff --git a/src/core/magnetostatics/registration.hpp b/src/core/magnetostatics/registration.hpp index 8daa094eb5f..ce9513b6237 100644 --- a/src/core/magnetostatics/registration.hpp +++ b/src/core/magnetostatics/registration.hpp @@ -19,7 +19,7 @@ #ifndef ESPRESSO_SRC_CORE_MAGNETOSTATICS_DIPOLE_REGISTRATION_HPP #define ESPRESSO_SRC_CORE_MAGNETOSTATICS_DIPOLE_REGISTRATION_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLES diff --git a/src/core/magnetostatics/scafacos.hpp b/src/core/magnetostatics/scafacos.hpp index c189ad65107..baeead68150 100644 --- a/src/core/magnetostatics/scafacos.hpp +++ b/src/core/magnetostatics/scafacos.hpp @@ -22,7 +22,7 @@ #ifndef ESPRESSO_SRC_CORE_MAGNETOSTATICS_SCAFACOS_HPP #define ESPRESSO_SRC_CORE_MAGNETOSTATICS_SCAFACOS_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef SCAFACOS_DIPOLES diff --git a/src/core/magnetostatics/scafacos_impl.cpp b/src/core/magnetostatics/scafacos_impl.cpp index 803ed852528..c71f69af5f1 100644 --- a/src/core/magnetostatics/scafacos_impl.cpp +++ b/src/core/magnetostatics/scafacos_impl.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef SCAFACOS_DIPOLES diff --git a/src/core/magnetostatics/scafacos_impl.hpp b/src/core/magnetostatics/scafacos_impl.hpp index d6a86a72879..13264fe295c 100644 --- a/src/core/magnetostatics/scafacos_impl.hpp +++ b/src/core/magnetostatics/scafacos_impl.hpp @@ -22,7 +22,7 @@ #ifndef ESPRESSO_SRC_CORE_MAGNETOSTATICS_SCAFACOS_IMPL_HPP #define ESPRESSO_SRC_CORE_MAGNETOSTATICS_SCAFACOS_IMPL_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef SCAFACOS_DIPOLES diff --git a/src/core/nonbonded_interactions/VerletCriterion.hpp b/src/core/nonbonded_interactions/VerletCriterion.hpp index 050242c6c69..21eff25e41f 100644 --- a/src/core/nonbonded_interactions/VerletCriterion.hpp +++ b/src/core/nonbonded_interactions/VerletCriterion.hpp @@ -22,7 +22,7 @@ #define CORE_NB_IA_VERLETCRITERION_HPP #include "Particle.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "nonbonded_interactions/nonbonded_interaction_data.hpp" #include diff --git a/src/core/nonbonded_interactions/bmhtf-nacl.hpp b/src/core/nonbonded_interactions/bmhtf-nacl.hpp index c2a5b87d110..b5274c751ee 100644 --- a/src/core/nonbonded_interactions/bmhtf-nacl.hpp +++ b/src/core/nonbonded_interactions/bmhtf-nacl.hpp @@ -27,7 +27,7 @@ * Implementation in \ref bmhtf-nacl.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef BMHTF_NACL diff --git a/src/core/nonbonded_interactions/buckingham.hpp b/src/core/nonbonded_interactions/buckingham.hpp index 33a78a8625c..4bafa45b974 100644 --- a/src/core/nonbonded_interactions/buckingham.hpp +++ b/src/core/nonbonded_interactions/buckingham.hpp @@ -26,7 +26,7 @@ * Implementation in \ref buckingham.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef BUCKINGHAM diff --git a/src/core/nonbonded_interactions/gaussian.hpp b/src/core/nonbonded_interactions/gaussian.hpp index fd004b13cc6..9b345ab217c 100644 --- a/src/core/nonbonded_interactions/gaussian.hpp +++ b/src/core/nonbonded_interactions/gaussian.hpp @@ -27,7 +27,7 @@ * Implementation in \ref gaussian.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #include "nonbonded_interaction_data.hpp" diff --git a/src/core/nonbonded_interactions/gay_berne.hpp b/src/core/nonbonded_interactions/gay_berne.hpp index 4696f267af4..2d606a14dbc 100644 --- a/src/core/nonbonded_interactions/gay_berne.hpp +++ b/src/core/nonbonded_interactions/gay_berne.hpp @@ -31,7 +31,7 @@ * Implementation in \ref gay_berne.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef GAY_BERNE diff --git a/src/core/nonbonded_interactions/hat.hpp b/src/core/nonbonded_interactions/hat.hpp index 60d6fdc40e9..bc9785006d2 100644 --- a/src/core/nonbonded_interactions/hat.hpp +++ b/src/core/nonbonded_interactions/hat.hpp @@ -27,7 +27,7 @@ * Implementation in \ref hat.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef HAT diff --git a/src/core/nonbonded_interactions/hertzian.hpp b/src/core/nonbonded_interactions/hertzian.hpp index 6f45ce363d7..767c401dc6d 100644 --- a/src/core/nonbonded_interactions/hertzian.hpp +++ b/src/core/nonbonded_interactions/hertzian.hpp @@ -27,7 +27,7 @@ * Implementation in \ref hertzian.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #include "nonbonded_interaction_data.hpp" diff --git a/src/core/nonbonded_interactions/lj.hpp b/src/core/nonbonded_interactions/lj.hpp index 0cbee73cee2..99b5d6f7df5 100644 --- a/src/core/nonbonded_interactions/lj.hpp +++ b/src/core/nonbonded_interactions/lj.hpp @@ -21,7 +21,7 @@ #ifndef CORE_NB_IA_LJ_HPP #define CORE_NB_IA_LJ_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef LENNARD_JONES diff --git a/src/core/nonbonded_interactions/ljcos.hpp b/src/core/nonbonded_interactions/ljcos.hpp index 342c08c99c6..50e9abd824e 100644 --- a/src/core/nonbonded_interactions/ljcos.hpp +++ b/src/core/nonbonded_interactions/ljcos.hpp @@ -27,7 +27,7 @@ * Implementation in \ref ljcos.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef LJCOS diff --git a/src/core/nonbonded_interactions/ljcos2.hpp b/src/core/nonbonded_interactions/ljcos2.hpp index 3df298586fd..3d39d94c242 100644 --- a/src/core/nonbonded_interactions/ljcos2.hpp +++ b/src/core/nonbonded_interactions/ljcos2.hpp @@ -30,7 +30,7 @@ * Implementation in \ref ljcos2.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef LJCOS2 diff --git a/src/core/nonbonded_interactions/ljgen.hpp b/src/core/nonbonded_interactions/ljgen.hpp index b0a734ff514..23afed47a95 100644 --- a/src/core/nonbonded_interactions/ljgen.hpp +++ b/src/core/nonbonded_interactions/ljgen.hpp @@ -36,7 +36,7 @@ * Implementation in \ref ljgen.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef LENNARD_JONES_GENERIC diff --git a/src/core/nonbonded_interactions/morse.hpp b/src/core/nonbonded_interactions/morse.hpp index fab45cfe3a5..4515e2b0837 100644 --- a/src/core/nonbonded_interactions/morse.hpp +++ b/src/core/nonbonded_interactions/morse.hpp @@ -27,7 +27,7 @@ * Implementation in \ref morse.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef MORSE diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index defb1d1f122..2926e7ceb75 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -25,7 +25,7 @@ */ #include "TabulatedPotential.hpp" -#include "config.hpp" +#include "config/config.hpp" #include diff --git a/src/core/nonbonded_interactions/nonbonded_tab.hpp b/src/core/nonbonded_interactions/nonbonded_tab.hpp index 692d17ef604..85385b1ca05 100644 --- a/src/core/nonbonded_interactions/nonbonded_tab.hpp +++ b/src/core/nonbonded_interactions/nonbonded_tab.hpp @@ -29,7 +29,7 @@ * Needs feature TABULATED compiled in (see \ref config.hpp). */ -#include "config.hpp" +#include "config/config.hpp" #ifdef TABULATED diff --git a/src/core/nonbonded_interactions/smooth_step.hpp b/src/core/nonbonded_interactions/smooth_step.hpp index 50cf2152796..926be66665f 100644 --- a/src/core/nonbonded_interactions/smooth_step.hpp +++ b/src/core/nonbonded_interactions/smooth_step.hpp @@ -27,7 +27,7 @@ * Implementation in \ref smooth_step.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef SMOOTH_STEP diff --git a/src/core/nonbonded_interactions/soft_sphere.hpp b/src/core/nonbonded_interactions/soft_sphere.hpp index b7a0b0acc39..5a6d8a9291c 100644 --- a/src/core/nonbonded_interactions/soft_sphere.hpp +++ b/src/core/nonbonded_interactions/soft_sphere.hpp @@ -27,7 +27,7 @@ * Implementation in \ref soft_sphere.cpp */ -#include "config.hpp" +#include "config/config.hpp" #ifdef SOFT_SPHERE diff --git a/src/core/nonbonded_interactions/thole.hpp b/src/core/nonbonded_interactions/thole.hpp index 5c0eb75732b..4865850aeb4 100644 --- a/src/core/nonbonded_interactions/thole.hpp +++ b/src/core/nonbonded_interactions/thole.hpp @@ -27,7 +27,7 @@ * Implementation in \ref thole.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef THOLE #include "Particle.hpp" diff --git a/src/core/nonbonded_interactions/wca.hpp b/src/core/nonbonded_interactions/wca.hpp index 6c91b291afb..55ff743e0bd 100644 --- a/src/core/nonbonded_interactions/wca.hpp +++ b/src/core/nonbonded_interactions/wca.hpp @@ -25,7 +25,7 @@ * Implementation in \ref wca.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef WCA diff --git a/src/core/npt.cpp b/src/core/npt.cpp index 85c85f7cded..6b40edc3fc7 100644 --- a/src/core/npt.cpp +++ b/src/core/npt.cpp @@ -21,7 +21,7 @@ #ifdef NPT #include "communication.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "electrostatics/coulomb.hpp" #include "errorhandling.hpp" #include "event.hpp" diff --git a/src/core/npt.hpp b/src/core/npt.hpp index 3c99c8119a4..658526c033a 100644 --- a/src/core/npt.hpp +++ b/src/core/npt.hpp @@ -25,7 +25,7 @@ #ifndef NPT_H #define NPT_H -#include "config.hpp" +#include "config/config.hpp" #ifdef NPT #include "BoxGeometry.hpp" diff --git a/src/core/object-in-fluid/oif_local_forces.hpp b/src/core/object-in-fluid/oif_local_forces.hpp index 11f9cb1d9c1..18653cf9477 100644 --- a/src/core/object-in-fluid/oif_local_forces.hpp +++ b/src/core/object-in-fluid/oif_local_forces.hpp @@ -25,7 +25,7 @@ * See @cite dupin07a. */ -#include "config.hpp" +#include "config/config.hpp" #include "BoxGeometry.hpp" #include "Particle.hpp" diff --git a/src/core/observables/ParticleTraits.hpp b/src/core/observables/ParticleTraits.hpp index 604d766125b..bff9d86a1b0 100644 --- a/src/core/observables/ParticleTraits.hpp +++ b/src/core/observables/ParticleTraits.hpp @@ -20,7 +20,7 @@ #define OBSERVABLES_PARTICLE_TRAITS #include "Particle.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "rotation.hpp" namespace ParticleObservables { diff --git a/src/core/p3m/TuningAlgorithm.cpp b/src/core/p3m/TuningAlgorithm.cpp index 80845109b4f..9fa852e36ed 100644 --- a/src/core/p3m/TuningAlgorithm.cpp +++ b/src/core/p3m/TuningAlgorithm.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #if defined(P3M) || defined(DP3M) diff --git a/src/core/p3m/TuningAlgorithm.hpp b/src/core/p3m/TuningAlgorithm.hpp index dae20fc22a4..5ea7bae5034 100644 --- a/src/core/p3m/TuningAlgorithm.hpp +++ b/src/core/p3m/TuningAlgorithm.hpp @@ -22,7 +22,7 @@ #ifndef ESPRESSO_SRC_CORE_P3M_TUNING_ALGORITHM_HPP #define ESPRESSO_SRC_CORE_P3M_TUNING_ALGORITHM_HPP -#include "config.hpp" +#include "config/config.hpp" #if defined(P3M) || defined(DP3M) diff --git a/src/core/p3m/TuningLogger.hpp b/src/core/p3m/TuningLogger.hpp index b2b91da1d0b..dabd053a500 100644 --- a/src/core/p3m/TuningLogger.hpp +++ b/src/core/p3m/TuningLogger.hpp @@ -22,7 +22,7 @@ #ifndef ESPRESSO_SRC_CORE_P3M_TUNING_LOGGER_HPP #define ESPRESSO_SRC_CORE_P3M_TUNING_LOGGER_HPP -#include "config.hpp" +#include "config/config.hpp" #if defined(P3M) || defined(DP3M) diff --git a/src/core/p3m/common.cpp b/src/core/p3m/common.cpp index 406ddad5d43..b68a8972599 100644 --- a/src/core/p3m/common.cpp +++ b/src/core/p3m/common.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #if defined(P3M) || defined(DP3M) diff --git a/src/core/p3m/common.hpp b/src/core/p3m/common.hpp index b21f0ee8aeb..bea6f359fa8 100644 --- a/src/core/p3m/common.hpp +++ b/src/core/p3m/common.hpp @@ -35,7 +35,7 @@ #ifndef ESPRESSO_SRC_CORE_P3M_COMMON_HPP #define ESPRESSO_SRC_CORE_P3M_COMMON_HPP -#include "config.hpp" +#include "config/config.hpp" #include diff --git a/src/core/p3m/data_struct.hpp b/src/core/p3m/data_struct.hpp index cc10d9157bb..66657774c25 100644 --- a/src/core/p3m/data_struct.hpp +++ b/src/core/p3m/data_struct.hpp @@ -21,7 +21,7 @@ #ifndef CORE_P3M_DATA_STRUCT_HPP #define CORE_P3M_DATA_STRUCT_HPP -#include "config.hpp" +#include "config/config.hpp" #if defined(P3M) || defined(DP3M) diff --git a/src/core/p3m/fft.cpp b/src/core/p3m/fft.cpp index b2aa88fc8b3..55ff03c66d3 100644 --- a/src/core/p3m/fft.cpp +++ b/src/core/p3m/fft.cpp @@ -25,7 +25,7 @@ * */ -#include "config.hpp" +#include "config/config.hpp" #if defined(P3M) || defined(DP3M) diff --git a/src/core/p3m/fft.hpp b/src/core/p3m/fft.hpp index 18f57a0cc86..0744be24e6d 100644 --- a/src/core/p3m/fft.hpp +++ b/src/core/p3m/fft.hpp @@ -42,7 +42,7 @@ * For more information about FFT usage, see \ref fft.cpp "fft.cpp". */ -#include "config.hpp" +#include "config/config.hpp" #if defined(P3M) || defined(DP3M) diff --git a/src/core/p3m/influence_function_dipolar.hpp b/src/core/p3m/influence_function_dipolar.hpp index cb4fa9ba602..0bd8f8ef149 100644 --- a/src/core/p3m/influence_function_dipolar.hpp +++ b/src/core/p3m/influence_function_dipolar.hpp @@ -21,7 +21,7 @@ #ifndef ESPRESSO_CORE_P3M_INFLUENCE_FUNCTION_DIPOLAR_HPP #define ESPRESSO_CORE_P3M_INFLUENCE_FUNCTION_DIPOLAR_HPP -#include "config.hpp" +#include "config/config.hpp" #if defined(DP3M) diff --git a/src/core/p3m/send_mesh.cpp b/src/core/p3m/send_mesh.cpp index 51b08a8c33c..f08262317d4 100644 --- a/src/core/p3m/send_mesh.cpp +++ b/src/core/p3m/send_mesh.cpp @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #if defined(P3M) || defined(DP3M) diff --git a/src/core/p3m/send_mesh.hpp b/src/core/p3m/send_mesh.hpp index 8c6b5956c70..bcee38e72e5 100644 --- a/src/core/p3m/send_mesh.hpp +++ b/src/core/p3m/send_mesh.hpp @@ -21,7 +21,7 @@ #ifndef ESPRESSO_CORE_P3M_SEND_MESH_HPP #define ESPRESSO_CORE_P3M_SEND_MESH_HPP -#include "config.hpp" +#include "config/config.hpp" #if defined(P3M) || defined(DP3M) diff --git a/src/core/particle_data.cpp b/src/core/particle_data.cpp index 064980062d1..f2dcfbabf87 100644 --- a/src/core/particle_data.cpp +++ b/src/core/particle_data.cpp @@ -24,7 +24,7 @@ #include "Particle.hpp" #include "cells.hpp" #include "communication.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "event.hpp" #include "exclusions.hpp" #include "nonbonded_interactions/nonbonded_interaction_data.hpp" diff --git a/src/core/particle_data.hpp b/src/core/particle_data.hpp index 82c7f39268a..0418ce63340 100644 --- a/src/core/particle_data.hpp +++ b/src/core/particle_data.hpp @@ -26,7 +26,7 @@ * This file contains everything related to updating particle properties. */ -#include "config.hpp" +#include "config/config.hpp" #include "Particle.hpp" diff --git a/src/core/pressure.cpp b/src/core/pressure.cpp index f67defc2965..aa156f78858 100644 --- a/src/core/pressure.cpp +++ b/src/core/pressure.cpp @@ -29,7 +29,7 @@ #include "bonded_interactions/bonded_interaction_data.hpp" #include "cells.hpp" #include "communication.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "event.hpp" #include "grid.hpp" #include "interactions.hpp" diff --git a/src/core/pressure_inline.hpp b/src/core/pressure_inline.hpp index 66fa782621a..7e42ff2b455 100644 --- a/src/core/pressure_inline.hpp +++ b/src/core/pressure_inline.hpp @@ -21,7 +21,7 @@ #ifndef CORE_PRESSURE_INLINE_HPP #define CORE_PRESSURE_INLINE_HPP -#include "config.hpp" +#include "config/config.hpp" #include "pressure.hpp" diff --git a/src/core/rattle.hpp b/src/core/rattle.hpp index 8253575ef74..41ceb8cb037 100644 --- a/src/core/rattle.hpp +++ b/src/core/rattle.hpp @@ -27,7 +27,7 @@ * For more information see \ref rattle.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef BOND_CONSTRAINT diff --git a/src/core/reaction_methods/ReactionAlgorithm.cpp b/src/core/reaction_methods/ReactionAlgorithm.cpp index 90eaa102778..f5feb49a82c 100644 --- a/src/core/reaction_methods/ReactionAlgorithm.cpp +++ b/src/core/reaction_methods/ReactionAlgorithm.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #include "reaction_methods/ReactionAlgorithm.hpp" diff --git a/src/core/reaction_methods/ReactionAlgorithm.hpp b/src/core/reaction_methods/ReactionAlgorithm.hpp index 18202713b2c..8b559b6d3d4 100644 --- a/src/core/reaction_methods/ReactionAlgorithm.hpp +++ b/src/core/reaction_methods/ReactionAlgorithm.hpp @@ -19,7 +19,7 @@ #ifndef REACTION_METHODS_REACTION_ALGORITHM_HPP #define REACTION_METHODS_REACTION_ALGORITHM_HPP -#include "config.hpp" +#include "config/config.hpp" #include "SingleReaction.hpp" diff --git a/src/core/rotate_system.cpp b/src/core/rotate_system.cpp index 67e5d1a017e..aa57cc579f6 100644 --- a/src/core/rotate_system.cpp +++ b/src/core/rotate_system.cpp @@ -23,7 +23,7 @@ #include "ParticleRange.hpp" #include "cells.hpp" #include "communication.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "event.hpp" #include "rotation.hpp" diff --git a/src/core/rotation.hpp b/src/core/rotation.hpp index a12efa40e23..84d984be355 100644 --- a/src/core/rotation.hpp +++ b/src/core/rotation.hpp @@ -24,7 +24,7 @@ * This file contains all subroutines required to process rotational motion. */ -#include "config.hpp" +#include "config/config.hpp" #ifdef ROTATION diff --git a/src/core/scafacos/ScafacosContext.cpp b/src/core/scafacos/ScafacosContext.cpp index c870c593df3..eb9d1ae9f9d 100644 --- a/src/core/scafacos/ScafacosContext.cpp +++ b/src/core/scafacos/ScafacosContext.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #if defined(SCAFACOS) or defined(SCAFACOS_DIPOLES) diff --git a/src/core/scafacos/ScafacosContext.hpp b/src/core/scafacos/ScafacosContext.hpp index fc612b14488..1adc2b44d90 100644 --- a/src/core/scafacos/ScafacosContext.hpp +++ b/src/core/scafacos/ScafacosContext.hpp @@ -28,7 +28,7 @@ * It is further derived for the coulombic and dipolar versions of ScaFaCoS. */ -#include "config.hpp" +#include "config/config.hpp" #if defined(SCAFACOS) or defined(SCAFACOS_DIPOLES) diff --git a/src/core/scafacos/ScafacosContextBase.cpp b/src/core/scafacos/ScafacosContextBase.cpp index 991adedf4e0..c0cbbdb74c6 100644 --- a/src/core/scafacos/ScafacosContextBase.cpp +++ b/src/core/scafacos/ScafacosContextBase.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #if defined(SCAFACOS) or defined(SCAFACOS_DIPOLAR) diff --git a/src/core/scafacos/ScafacosContextBase.hpp b/src/core/scafacos/ScafacosContextBase.hpp index e153a930b70..3f754ea6403 100644 --- a/src/core/scafacos/ScafacosContextBase.hpp +++ b/src/core/scafacos/ScafacosContextBase.hpp @@ -30,7 +30,7 @@ #ifndef ESPRESSO_SRC_CORE_SCAFACOS_SCAFACOS_CONTEXT_BASE_HPP #define ESPRESSO_SRC_CORE_SCAFACOS_SCAFACOS_CONTEXT_BASE_HPP -#include "config.hpp" +#include "config/config.hpp" #if defined(SCAFACOS) or defined(SCAFACOS_DIPOLAR) diff --git a/src/core/stokesian_dynamics/sd_interface.cpp b/src/core/stokesian_dynamics/sd_interface.cpp index 1b3ad925ba9..b5ae2e98ec3 100644 --- a/src/core/stokesian_dynamics/sd_interface.cpp +++ b/src/core/stokesian_dynamics/sd_interface.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef STOKESIAN_DYNAMICS #include "sd_interface.hpp" diff --git a/src/core/stokesian_dynamics/sd_interface.hpp b/src/core/stokesian_dynamics/sd_interface.hpp index 73df6f2d8a3..bdc95116e0a 100644 --- a/src/core/stokesian_dynamics/sd_interface.hpp +++ b/src/core/stokesian_dynamics/sd_interface.hpp @@ -25,7 +25,7 @@ #ifndef STOKESIAN_DYNAMICS_INTERFACE_H #define STOKESIAN_DYNAMICS_INTERFACE_H -#include "config.hpp" +#include "config/config.hpp" #ifdef STOKESIAN_DYNAMICS diff --git a/src/core/thermostat.cpp b/src/core/thermostat.cpp index 511302ba118..bc56e228838 100644 --- a/src/core/thermostat.cpp +++ b/src/core/thermostat.cpp @@ -22,7 +22,7 @@ * Implementation of \ref thermostat.hpp. */ -#include "config.hpp" +#include "config/config.hpp" #include "bonded_interactions/thermalized_bond.hpp" #include "bonded_interactions/thermalized_bond_utils.hpp" diff --git a/src/core/thermostat.hpp b/src/core/thermostat.hpp index f5552203a35..14e294146bc 100644 --- a/src/core/thermostat.hpp +++ b/src/core/thermostat.hpp @@ -24,7 +24,7 @@ * Implementation in \ref thermostat.cpp. */ -#include "config.hpp" +#include "config/config.hpp" #include #include diff --git a/src/core/thermostats/brownian_inline.hpp b/src/core/thermostats/brownian_inline.hpp index 70633dc2a5f..162f7c209b6 100644 --- a/src/core/thermostats/brownian_inline.hpp +++ b/src/core/thermostats/brownian_inline.hpp @@ -22,7 +22,7 @@ #ifndef THERMOSTATS_BROWNIAN_INLINE_HPP #define THERMOSTATS_BROWNIAN_INLINE_HPP -#include "config.hpp" +#include "config/config.hpp" #include "Particle.hpp" #include "random.hpp" diff --git a/src/core/thermostats/langevin_inline.hpp b/src/core/thermostats/langevin_inline.hpp index 12359eb1299..561fafe46af 100644 --- a/src/core/thermostats/langevin_inline.hpp +++ b/src/core/thermostats/langevin_inline.hpp @@ -22,7 +22,7 @@ #ifndef THERMOSTATS_LANGEVIN_INLINE_HPP #define THERMOSTATS_LANGEVIN_INLINE_HPP -#include "config.hpp" +#include "config/config.hpp" #include "Particle.hpp" #include "random.hpp" diff --git a/src/core/thermostats/npt_inline.hpp b/src/core/thermostats/npt_inline.hpp index 9ce5615da92..e5969ddd8f3 100644 --- a/src/core/thermostats/npt_inline.hpp +++ b/src/core/thermostats/npt_inline.hpp @@ -22,7 +22,7 @@ #ifndef THERMOSTATS_NPT_INLINE_HPP #define THERMOSTATS_NPT_INLINE_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef NPT diff --git a/src/core/unit_tests/EspressoSystemInterface_test.cpp b/src/core/unit_tests/EspressoSystemInterface_test.cpp index ade8c8df16e..52c34ff0eeb 100644 --- a/src/core/unit_tests/EspressoSystemInterface_test.cpp +++ b/src/core/unit_tests/EspressoSystemInterface_test.cpp @@ -25,7 +25,7 @@ #include "EspressoSystemInterface.hpp" #include "communication.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "particle_data.hpp" #include "particle_node.hpp" #include "virtual_sites.hpp" diff --git a/src/core/unit_tests/Particle_serialization_test.cpp b/src/core/unit_tests/Particle_serialization_test.cpp index ac86edcfa1d..409e07f7f6b 100644 --- a/src/core/unit_tests/Particle_serialization_test.cpp +++ b/src/core/unit_tests/Particle_serialization_test.cpp @@ -22,7 +22,7 @@ #include #include "Particle.hpp" -#include "config.hpp" +#include "config/config.hpp" #include #include diff --git a/src/core/unit_tests/Particle_test.cpp b/src/core/unit_tests/Particle_test.cpp index b80af98c300..76214e2f7f1 100644 --- a/src/core/unit_tests/Particle_test.cpp +++ b/src/core/unit_tests/Particle_test.cpp @@ -24,7 +24,7 @@ #include #include "Particle.hpp" -#include "config.hpp" +#include "config/config.hpp" #include #include diff --git a/src/core/unit_tests/VerletCriterion_test.cpp b/src/core/unit_tests/VerletCriterion_test.cpp index 69fb4d193b0..e84e9b595a3 100644 --- a/src/core/unit_tests/VerletCriterion_test.cpp +++ b/src/core/unit_tests/VerletCriterion_test.cpp @@ -23,7 +23,7 @@ #include "Particle.hpp" #include "cell_system/CellStructure.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "nonbonded_interactions/VerletCriterion.hpp" #include "particle_data.hpp" diff --git a/src/core/unit_tests/Verlet_list_test.cpp b/src/core/unit_tests/Verlet_list_test.cpp index ec9f840f13d..74d8bab9d31 100644 --- a/src/core/unit_tests/Verlet_list_test.cpp +++ b/src/core/unit_tests/Verlet_list_test.cpp @@ -19,7 +19,7 @@ #define BOOST_TEST_MODULE Verlet list update test -#include "config.hpp" +#include "config/config.hpp" #ifdef LENNARD_JONES diff --git a/src/core/unit_tests/rotation_test.cpp b/src/core/unit_tests/rotation_test.cpp index 336f16cd75f..e1f417a29ad 100644 --- a/src/core/unit_tests/rotation_test.cpp +++ b/src/core/unit_tests/rotation_test.cpp @@ -20,7 +20,7 @@ #define BOOST_TEST_MODULE rotation test #define BOOST_TEST_DYN_LINK -#include "config.hpp" +#include "config/config.hpp" #ifdef ROTATION diff --git a/src/core/unit_tests/thermostats_test.cpp b/src/core/unit_tests/thermostats_test.cpp index c0cca417065..0edd7f47a44 100644 --- a/src/core/unit_tests/thermostats_test.cpp +++ b/src/core/unit_tests/thermostats_test.cpp @@ -24,7 +24,7 @@ #include #include "Particle.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "random.hpp" #include "random_test.hpp" #include "thermostat.hpp" diff --git a/src/core/virtual_sites.cpp b/src/core/virtual_sites.cpp index 31e7882080e..d32627b6bb9 100644 --- a/src/core/virtual_sites.cpp +++ b/src/core/virtual_sites.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef VIRTUAL_SITES diff --git a/src/core/virtual_sites.hpp b/src/core/virtual_sites.hpp index 496d39824cb..18a282a1723 100644 --- a/src/core/virtual_sites.hpp +++ b/src/core/virtual_sites.hpp @@ -19,7 +19,7 @@ #ifndef VIRTUAL_SITES_HPP #define VIRTUAL_SITES_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef VIRTUAL_SITES #include "Particle.hpp" diff --git a/src/core/virtual_sites/VirtualSites.hpp b/src/core/virtual_sites/VirtualSites.hpp index 17c8f478281..933fd149260 100644 --- a/src/core/virtual_sites/VirtualSites.hpp +++ b/src/core/virtual_sites/VirtualSites.hpp @@ -32,7 +32,7 @@ * - update virtual sites */ -#include +#include "config/config.hpp" #ifdef VIRTUAL_SITES #include diff --git a/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp b/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp index c9f8f3c137c..5c6a6e1f56d 100644 --- a/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp +++ b/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef VIRTUAL_SITES_INERTIALESS_TRACERS diff --git a/src/core/virtual_sites/VirtualSitesInertialessTracers.hpp b/src/core/virtual_sites/VirtualSitesInertialessTracers.hpp index c70b207430c..6011f536666 100644 --- a/src/core/virtual_sites/VirtualSitesInertialessTracers.hpp +++ b/src/core/virtual_sites/VirtualSitesInertialessTracers.hpp @@ -19,7 +19,7 @@ #ifndef VIRTUAL_SITES_VIRTUAL_SITES_INERTIALESS_TRACERS_HPP #define VIRTUAL_SITES_VIRTUAL_SITES_INERTIALESS_TRACERS_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef VIRTUAL_SITES #include "VirtualSites.hpp" diff --git a/src/core/virtual_sites/VirtualSitesOff.hpp b/src/core/virtual_sites/VirtualSitesOff.hpp index c90efabb22f..71bd1e9f71e 100644 --- a/src/core/virtual_sites/VirtualSitesOff.hpp +++ b/src/core/virtual_sites/VirtualSitesOff.hpp @@ -19,7 +19,7 @@ #ifndef VIRTUAL_SITES_VIRTUAL_SITES_OFF_HPP #define VIRTUAL_SITES_VIRTUAL_SITES_OFF_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef VIRTUAL_SITES #include "VirtualSites.hpp" diff --git a/src/core/virtual_sites/VirtualSitesRelative.hpp b/src/core/virtual_sites/VirtualSitesRelative.hpp index 144a914de22..e7050f9c348 100644 --- a/src/core/virtual_sites/VirtualSitesRelative.hpp +++ b/src/core/virtual_sites/VirtualSitesRelative.hpp @@ -20,7 +20,7 @@ #ifndef VIRTUAL_SITES_VIRTUAL_SITES_RELATIVE_HPP #define VIRTUAL_SITES_VIRTUAL_SITES_RELATIVE_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef VIRTUAL_SITES_RELATIVE #include "VirtualSites.hpp" diff --git a/src/core/virtual_sites/lb_inertialess_tracers.cpp b/src/core/virtual_sites/lb_inertialess_tracers.cpp index 113c21ca291..96b60daba01 100644 --- a/src/core/virtual_sites/lb_inertialess_tracers.cpp +++ b/src/core/virtual_sites/lb_inertialess_tracers.cpp @@ -19,7 +19,7 @@ /// \file /// \brief Main of the Bayreuth Immersed-Boundary implementation -#include "config.hpp" +#include "config/config.hpp" #ifdef VIRTUAL_SITES_INERTIALESS_TRACERS diff --git a/src/core/virtual_sites/lb_inertialess_tracers.hpp b/src/core/virtual_sites/lb_inertialess_tracers.hpp index de568c585b6..666cafa0432 100644 --- a/src/core/virtual_sites/lb_inertialess_tracers.hpp +++ b/src/core/virtual_sites/lb_inertialess_tracers.hpp @@ -22,7 +22,7 @@ #ifndef VIRTUAL_SITES_LB_INERTIALESS_TRACERS_HPP #define VIRTUAL_SITES_LB_INERTIALESS_TRACERS_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef VIRTUAL_SITES_INERTIALESS_TRACERS diff --git a/src/core/virtual_sites/lb_inertialess_tracers_cuda.cu b/src/core/virtual_sites/lb_inertialess_tracers_cuda.cu index c547b6044f9..a1385036be2 100644 --- a/src/core/virtual_sites/lb_inertialess_tracers_cuda.cu +++ b/src/core/virtual_sites/lb_inertialess_tracers_cuda.cu @@ -21,7 +21,7 @@ // It should not be included by any main ESPResSo routines // Functions to be exported for ESPResSo are in ibm_main.hpp -#include "config.hpp" +#include "config/config.hpp" #if defined(VIRTUAL_SITES_INERTIALESS_TRACERS) && defined(CUDA) 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 c14c1a2fc7c..53ab47eb9eb 100644 --- a/src/core/virtual_sites/lb_inertialess_tracers_cuda_interface.cpp +++ b/src/core/virtual_sites/lb_inertialess_tracers_cuda_interface.cpp @@ -21,7 +21,7 @@ // It should not be included by any main ESPResSo routines // Functions to be exported for ESPResSo are in ibm_main.hpp -#include "config.hpp" +#include "config/config.hpp" #ifdef VIRTUAL_SITES_INERTIALESS_TRACERS diff --git a/src/core/virtual_sites/lb_inertialess_tracers_cuda_interface.hpp b/src/core/virtual_sites/lb_inertialess_tracers_cuda_interface.hpp index 01fc0eed50f..ddac4997d20 100644 --- a/src/core/virtual_sites/lb_inertialess_tracers_cuda_interface.hpp +++ b/src/core/virtual_sites/lb_inertialess_tracers_cuda_interface.hpp @@ -25,7 +25,7 @@ #ifndef IBM_CUDA_INTERFACE_HPP #define IBM_CUDA_INTERFACE_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef VIRTUAL_SITES_INERTIALESS_TRACERS diff --git a/src/python/espressomd/gen_pxiconfig.py b/src/python/espressomd/gen_pxiconfig.py index 670b146acf0..ff2ee131a10 100644 --- a/src/python/espressomd/gen_pxiconfig.py +++ b/src/python/espressomd/gen_pxiconfig.py @@ -41,7 +41,7 @@ cfile = open(cfilename, 'w') cfile.write(""" -#include "config.hpp" +#include "config/config.hpp" #include using namespace std; int main() { diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index 88b3a4b5b07..009dc267158 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -28,7 +28,7 @@ from .thermostat cimport thermalized_bond include "myconfig.pxi" # force include of config.hpp -cdef extern from "config.hpp": +cdef extern from "config/config.hpp": pass cdef extern from "TabulatedPotential.hpp": diff --git a/src/script_interface/analysis/ObservableStat.cpp b/src/script_interface/analysis/ObservableStat.cpp index 1d0c59036a8..c5a0d61ae64 100644 --- a/src/script_interface/analysis/ObservableStat.cpp +++ b/src/script_interface/analysis/ObservableStat.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #include "ObservableStat.hpp" diff --git a/src/script_interface/code_info/CodeInfo.cpp b/src/script_interface/code_info/CodeInfo.cpp index 5b3796214a6..8cb57175d4b 100644 --- a/src/script_interface/code_info/CodeInfo.cpp +++ b/src/script_interface/code_info/CodeInfo.cpp @@ -19,9 +19,9 @@ #include "CodeInfo.hpp" -#include "config-features.hpp" +#include "config/config-features.hpp" +#include "config/version.hpp" #include "script_interface/scafacos/scafacos.hpp" -#include "version.hpp" #include #include diff --git a/src/script_interface/code_info/CodeVersion.cpp b/src/script_interface/code_info/CodeVersion.cpp index 1c6958339a7..a2641e3548b 100644 --- a/src/script_interface/code_info/CodeVersion.cpp +++ b/src/script_interface/code_info/CodeVersion.cpp @@ -19,7 +19,7 @@ #include "CodeVersion.hpp" -#include "version.hpp" +#include "config/version.hpp" #include diff --git a/src/script_interface/collision_detection/CollisionDetection.hpp b/src/script_interface/collision_detection/CollisionDetection.hpp index 0c31431025b..b320cd4a398 100644 --- a/src/script_interface/collision_detection/CollisionDetection.hpp +++ b/src/script_interface/collision_detection/CollisionDetection.hpp @@ -22,7 +22,7 @@ #ifndef SCRIPT_INTERFACE_COLLISION_DETECTION_COLLISION_DETECTION_HPP #define SCRIPT_INTERFACE_COLLISION_DETECTION_COLLISION_DETECTION_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef COLLISION_DETECTION diff --git a/src/script_interface/collision_detection/initialize.cpp b/src/script_interface/collision_detection/initialize.cpp index 45a23fee70b..daa06ed44a2 100644 --- a/src/script_interface/collision_detection/initialize.cpp +++ b/src/script_interface/collision_detection/initialize.cpp @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #include "initialize.hpp" diff --git a/src/script_interface/electrostatics/Actor.hpp b/src/script_interface/electrostatics/Actor.hpp index 090b6529794..4d9923249a6 100644 --- a/src/script_interface/electrostatics/Actor.hpp +++ b/src/script_interface/electrostatics/Actor.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_ACTOR_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_ACTOR_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef ELECTROSTATICS diff --git a/src/script_interface/electrostatics/Actor_impl.hpp b/src/script_interface/electrostatics/Actor_impl.hpp index b16c426afda..3310f633677 100644 --- a/src/script_interface/electrostatics/Actor_impl.hpp +++ b/src/script_interface/electrostatics/Actor_impl.hpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef ELECTROSTATICS diff --git a/src/script_interface/electrostatics/CoulombMMM1D.hpp b/src/script_interface/electrostatics/CoulombMMM1D.hpp index a872c33d42e..57f1aac6f37 100644 --- a/src/script_interface/electrostatics/CoulombMMM1D.hpp +++ b/src/script_interface/electrostatics/CoulombMMM1D.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_MMM1D_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_MMM1D_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef ELECTROSTATICS diff --git a/src/script_interface/electrostatics/CoulombMMM1DGpu.hpp b/src/script_interface/electrostatics/CoulombMMM1DGpu.hpp index ebd356fc974..f740121c327 100644 --- a/src/script_interface/electrostatics/CoulombMMM1DGpu.hpp +++ b/src/script_interface/electrostatics/CoulombMMM1DGpu.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_MMM1D_GPU_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_MMM1D_GPU_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef MMM1D_GPU diff --git a/src/script_interface/electrostatics/CoulombP3M.hpp b/src/script_interface/electrostatics/CoulombP3M.hpp index 14e0b3e7643..147ee7d0af7 100644 --- a/src/script_interface/electrostatics/CoulombP3M.hpp +++ b/src/script_interface/electrostatics/CoulombP3M.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_COULOMB_P3M_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_COULOMB_P3M_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef P3M diff --git a/src/script_interface/electrostatics/CoulombP3MGPU.hpp b/src/script_interface/electrostatics/CoulombP3MGPU.hpp index fec2219523c..d212a5e671f 100644 --- a/src/script_interface/electrostatics/CoulombP3MGPU.hpp +++ b/src/script_interface/electrostatics/CoulombP3MGPU.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_COULOMB_P3M_GPU_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_COULOMB_P3M_GPU_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef P3M #ifdef CUDA diff --git a/src/script_interface/electrostatics/CoulombScafacos.hpp b/src/script_interface/electrostatics/CoulombScafacos.hpp index 57c7ed059f3..e5f8ba05dc2 100644 --- a/src/script_interface/electrostatics/CoulombScafacos.hpp +++ b/src/script_interface/electrostatics/CoulombScafacos.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_COULOMB_SCAFACOS_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_COULOMB_SCAFACOS_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef SCAFACOS diff --git a/src/script_interface/electrostatics/DebyeHueckel.hpp b/src/script_interface/electrostatics/DebyeHueckel.hpp index f60b427f54f..89612b3691d 100644 --- a/src/script_interface/electrostatics/DebyeHueckel.hpp +++ b/src/script_interface/electrostatics/DebyeHueckel.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_DH_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_DH_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef ELECTROSTATICS diff --git a/src/script_interface/electrostatics/ElectrostaticLayerCorrection.hpp b/src/script_interface/electrostatics/ElectrostaticLayerCorrection.hpp index 44c4ef8f8ab..c867a61f926 100644 --- a/src/script_interface/electrostatics/ElectrostaticLayerCorrection.hpp +++ b/src/script_interface/electrostatics/ElectrostaticLayerCorrection.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_ELC_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_ELC_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef P3M diff --git a/src/script_interface/electrostatics/ICCStar.hpp b/src/script_interface/electrostatics/ICCStar.hpp index bf1734a1c16..0ddbbe85c88 100644 --- a/src/script_interface/electrostatics/ICCStar.hpp +++ b/src/script_interface/electrostatics/ICCStar.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_ICC_STAR_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_ICC_STAR_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef ELECTROSTATICS diff --git a/src/script_interface/electrostatics/ReactionField.hpp b/src/script_interface/electrostatics/ReactionField.hpp index f07dde735cd..9a1dd322777 100644 --- a/src/script_interface/electrostatics/ReactionField.hpp +++ b/src/script_interface/electrostatics/ReactionField.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_RF_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_ELECTROSTATICS_RF_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef ELECTROSTATICS diff --git a/src/script_interface/electrostatics/initialize.cpp b/src/script_interface/electrostatics/initialize.cpp index 743323670e5..be164f64034 100644 --- a/src/script_interface/electrostatics/initialize.cpp +++ b/src/script_interface/electrostatics/initialize.cpp @@ -19,7 +19,7 @@ #include "initialize.hpp" -#include "config.hpp" +#include "config/config.hpp" #ifdef ELECTROSTATICS diff --git a/src/script_interface/h5md/h5md.cpp b/src/script_interface/h5md/h5md.cpp index ca8399c8492..5efadfc7ae1 100644 --- a/src/script_interface/h5md/h5md.cpp +++ b/src/script_interface/h5md/h5md.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef H5MD diff --git a/src/script_interface/h5md/h5md.hpp b/src/script_interface/h5md/h5md.hpp index 7609d6b9361..8316bb8be7c 100644 --- a/src/script_interface/h5md/h5md.hpp +++ b/src/script_interface/h5md/h5md.hpp @@ -22,7 +22,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_H5MD_H5MD_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_H5MD_H5MD_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef H5MD diff --git a/src/script_interface/h5md/initialize.cpp b/src/script_interface/h5md/initialize.cpp index 059a9bb5212..df03041ded8 100644 --- a/src/script_interface/h5md/initialize.cpp +++ b/src/script_interface/h5md/initialize.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef H5MD #include "h5md.hpp" #include "initialize.hpp" diff --git a/src/script_interface/h5md/initialize.hpp b/src/script_interface/h5md/initialize.hpp index 644e411f9f0..7fcf05b3569 100644 --- a/src/script_interface/h5md/initialize.hpp +++ b/src/script_interface/h5md/initialize.hpp @@ -20,7 +20,7 @@ #ifndef SCRIPT_INTERFACE_H5MD_INITIALIZE_HPP #define SCRIPT_INTERFACE_H5MD_INITIALIZE_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef H5MD #include diff --git a/src/script_interface/initialize.cpp b/src/script_interface/initialize.cpp index 4d3e70fbf9d..b9625598722 100644 --- a/src/script_interface/initialize.cpp +++ b/src/script_interface/initialize.cpp @@ -19,7 +19,7 @@ #include "initialize.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "accumulators/initialize.hpp" #include "analysis/initialize.hpp" diff --git a/src/script_interface/integrators/StokesianDynamics.cpp b/src/script_interface/integrators/StokesianDynamics.cpp index bbddbd6187b..27bd7962b6e 100644 --- a/src/script_interface/integrators/StokesianDynamics.cpp +++ b/src/script_interface/integrators/StokesianDynamics.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef STOKESIAN_DYNAMICS diff --git a/src/script_interface/integrators/StokesianDynamics.hpp b/src/script_interface/integrators/StokesianDynamics.hpp index 804040d6b10..27f20281ee1 100644 --- a/src/script_interface/integrators/StokesianDynamics.hpp +++ b/src/script_interface/integrators/StokesianDynamics.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_STOKESIAN_DYNAMICS_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_STOKESIAN_DYNAMICS_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef STOKESIAN_DYNAMICS diff --git a/src/script_interface/integrators/VelocityVerletIsoNPT.cpp b/src/script_interface/integrators/VelocityVerletIsoNPT.cpp index 28ad7850b2b..dfa3cbee7dc 100644 --- a/src/script_interface/integrators/VelocityVerletIsoNPT.cpp +++ b/src/script_interface/integrators/VelocityVerletIsoNPT.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef NPT diff --git a/src/script_interface/integrators/VelocityVerletIsoNPT.hpp b/src/script_interface/integrators/VelocityVerletIsoNPT.hpp index a858113a8ca..414b16ddb9f 100644 --- a/src/script_interface/integrators/VelocityVerletIsoNPT.hpp +++ b/src/script_interface/integrators/VelocityVerletIsoNPT.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_NPT_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_INTEGRATORS_NPT_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef NPT diff --git a/src/script_interface/integrators/initialize.cpp b/src/script_interface/integrators/initialize.cpp index bdeeb5e63d6..bf69ce65c58 100644 --- a/src/script_interface/integrators/initialize.cpp +++ b/src/script_interface/integrators/initialize.cpp @@ -25,7 +25,7 @@ #include "StokesianDynamics.hpp" #include "VelocityVerlet.hpp" #include "VelocityVerletIsoNPT.hpp" -#include "config.hpp" +#include "config/config.hpp" namespace ScriptInterface { namespace Integrators { diff --git a/src/script_interface/lbboundaries/LBBoundaries.hpp b/src/script_interface/lbboundaries/LBBoundaries.hpp index d151ce1636e..c08d4df304c 100644 --- a/src/script_interface/lbboundaries/LBBoundaries.hpp +++ b/src/script_interface/lbboundaries/LBBoundaries.hpp @@ -19,7 +19,7 @@ #ifndef SCRIPT_INTERFACE_LBBOUNDARIES_LBBOUNDARIES_HPP #define SCRIPT_INTERFACE_LBBOUNDARIES_LBBOUNDARIES_HPP -#include "config.hpp" +#include "config/config.hpp" #include "LBBoundary.hpp" diff --git a/src/script_interface/lbboundaries/LBBoundary.hpp b/src/script_interface/lbboundaries/LBBoundary.hpp index f64df36c156..7dd3e090df8 100644 --- a/src/script_interface/lbboundaries/LBBoundary.hpp +++ b/src/script_interface/lbboundaries/LBBoundary.hpp @@ -19,7 +19,7 @@ #ifndef SCRIPT_INTERFACE_LBBOUNDARIES_LBBOUNDARY_HPP #define SCRIPT_INTERFACE_LBBOUNDARIES_LBBOUNDARY_HPP -#include "config.hpp" +#include "config/config.hpp" #include "script_interface/ScriptInterface.hpp" #include "script_interface/auto_parameters/AutoParameters.hpp" diff --git a/src/script_interface/magnetostatics/Actor.hpp b/src/script_interface/magnetostatics/Actor.hpp index f307293ae15..c6c7c124ec0 100644 --- a/src/script_interface/magnetostatics/Actor.hpp +++ b/src/script_interface/magnetostatics/Actor.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_MAGNETOSTATICS_ACTOR_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_MAGNETOSTATICS_ACTOR_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLES diff --git a/src/script_interface/magnetostatics/Actor_impl.hpp b/src/script_interface/magnetostatics/Actor_impl.hpp index 486cf4e78cd..61028a517ab 100644 --- a/src/script_interface/magnetostatics/Actor_impl.hpp +++ b/src/script_interface/magnetostatics/Actor_impl.hpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLES diff --git a/src/script_interface/magnetostatics/DipolarBarnesHutGpu.hpp b/src/script_interface/magnetostatics/DipolarBarnesHutGpu.hpp index 18bbf46f614..222f097c434 100644 --- a/src/script_interface/magnetostatics/DipolarBarnesHutGpu.hpp +++ b/src/script_interface/magnetostatics/DipolarBarnesHutGpu.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_MAGNETOSTATICS_DIPOLAR_BH_GPU_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_MAGNETOSTATICS_DIPOLAR_BH_GPU_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLAR_BARNES_HUT diff --git a/src/script_interface/magnetostatics/DipolarDirectSum.hpp b/src/script_interface/magnetostatics/DipolarDirectSum.hpp index ac69ba75f92..c3fdcff9e1d 100644 --- a/src/script_interface/magnetostatics/DipolarDirectSum.hpp +++ b/src/script_interface/magnetostatics/DipolarDirectSum.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_MAGNETOSTATICS_DIPOLAR_DIRECT_SUM_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_MAGNETOSTATICS_DIPOLAR_DIRECT_SUM_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLES diff --git a/src/script_interface/magnetostatics/DipolarDirectSumGpu.hpp b/src/script_interface/magnetostatics/DipolarDirectSumGpu.hpp index 226711e7445..814ebc6e256 100644 --- a/src/script_interface/magnetostatics/DipolarDirectSumGpu.hpp +++ b/src/script_interface/magnetostatics/DipolarDirectSumGpu.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_MAGNETOSTATICS_DIPOLAR_DIRECT_SUM_GPU_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_MAGNETOSTATICS_DIPOLAR_DIRECT_SUM_GPU_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLAR_DIRECT_SUM diff --git a/src/script_interface/magnetostatics/DipolarDirectSumWithReplica.hpp b/src/script_interface/magnetostatics/DipolarDirectSumWithReplica.hpp index d7965def6e9..d0789491432 100644 --- a/src/script_interface/magnetostatics/DipolarDirectSumWithReplica.hpp +++ b/src/script_interface/magnetostatics/DipolarDirectSumWithReplica.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_MAGNETOSTATICS_DDS_REPLICA_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_MAGNETOSTATICS_DDS_REPLICA_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLES diff --git a/src/script_interface/magnetostatics/DipolarLayerCorrection.hpp b/src/script_interface/magnetostatics/DipolarLayerCorrection.hpp index abed1215cce..34d172d3a6c 100644 --- a/src/script_interface/magnetostatics/DipolarLayerCorrection.hpp +++ b/src/script_interface/magnetostatics/DipolarLayerCorrection.hpp @@ -19,7 +19,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_DIPOLAR_LAYER_CORRECTION_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_DIPOLAR_LAYER_CORRECTION_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLES diff --git a/src/script_interface/magnetostatics/DipolarP3M.hpp b/src/script_interface/magnetostatics/DipolarP3M.hpp index 6923ded75b6..54ff728496a 100644 --- a/src/script_interface/magnetostatics/DipolarP3M.hpp +++ b/src/script_interface/magnetostatics/DipolarP3M.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_MAGNETOSTATICS_DIPOLAR_P3M_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_MAGNETOSTATICS_DIPOLAR_P3M_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef DP3M diff --git a/src/script_interface/magnetostatics/DipolarScafacos.hpp b/src/script_interface/magnetostatics/DipolarScafacos.hpp index ef53205ef87..796522095e5 100644 --- a/src/script_interface/magnetostatics/DipolarScafacos.hpp +++ b/src/script_interface/magnetostatics/DipolarScafacos.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_MAGNETOSTATICS_DIPOLAR_SCAFACOS_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_MAGNETOSTATICS_DIPOLAR_SCAFACOS_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef SCAFACOS_DIPOLES diff --git a/src/script_interface/magnetostatics/initialize.cpp b/src/script_interface/magnetostatics/initialize.cpp index 7fd32e36886..1c253beb512 100644 --- a/src/script_interface/magnetostatics/initialize.cpp +++ b/src/script_interface/magnetostatics/initialize.cpp @@ -18,7 +18,7 @@ */ #include "initialize.hpp" -#include "config.hpp" +#include "config/config.hpp" #ifdef DIPOLES diff --git a/src/script_interface/observables/ParamlessObservable.hpp b/src/script_interface/observables/ParamlessObservable.hpp index 4a515230a16..ba398757811 100644 --- a/src/script_interface/observables/ParamlessObservable.hpp +++ b/src/script_interface/observables/ParamlessObservable.hpp @@ -22,7 +22,7 @@ #ifndef SCRIPT_INTERFACE_OBSERVABLES_PARAMLESSOBSERVABLE_HPP #define SCRIPT_INTERFACE_OBSERVABLES_PARAMLESSOBSERVABLE_HPP -#include "config.hpp" +#include "config/config.hpp" #include "script_interface/ScriptInterface.hpp" diff --git a/src/script_interface/observables/initialize.cpp b/src/script_interface/observables/initialize.cpp index 13fd0ba8e3a..2814eed150a 100644 --- a/src/script_interface/observables/initialize.cpp +++ b/src/script_interface/observables/initialize.cpp @@ -26,7 +26,7 @@ #include "PidProfileObservable.hpp" #include "ProfileObservable.hpp" #include "RDF.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "core/observables/BondAngles.hpp" #include "core/observables/BondDihedrals.hpp" diff --git a/src/script_interface/particle_data/ParticleHandle.cpp b/src/script_interface/particle_data/ParticleHandle.cpp index f327b52e63e..e08ca7b884d 100644 --- a/src/script_interface/particle_data/ParticleHandle.cpp +++ b/src/script_interface/particle_data/ParticleHandle.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #include "ParticleHandle.hpp" diff --git a/src/script_interface/scafacos/scafacos.cpp b/src/script_interface/scafacos/scafacos.cpp index 130dcd29f72..b849d4c4a97 100644 --- a/src/script_interface/scafacos/scafacos.cpp +++ b/src/script_interface/scafacos/scafacos.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #if defined(SCAFACOS) or defined(SCAFACOS_DIPOLES) diff --git a/src/script_interface/scafacos/scafacos.hpp b/src/script_interface/scafacos/scafacos.hpp index 6f8f2d69282..c6d086af434 100644 --- a/src/script_interface/scafacos/scafacos.hpp +++ b/src/script_interface/scafacos/scafacos.hpp @@ -20,7 +20,7 @@ #ifndef ESPRESSO_SRC_SCRIPT_INTERFACE_SCAFACOS_SCAFACOS_HPP #define ESPRESSO_SRC_SCRIPT_INTERFACE_SCAFACOS_SCAFACOS_HPP -#include "config.hpp" +#include "config/config.hpp" #if defined(SCAFACOS) or defined(SCAFACOS_DIPOLAR) diff --git a/src/script_interface/system/CudaInitHandle.cpp b/src/script_interface/system/CudaInitHandle.cpp index b231f8c056f..2762e62b70c 100644 --- a/src/script_interface/system/CudaInitHandle.cpp +++ b/src/script_interface/system/CudaInitHandle.cpp @@ -19,7 +19,7 @@ #include "CudaInitHandle.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "core/cuda_init.hpp" #include "core/cuda_utils.hpp" diff --git a/src/script_interface/system/System.cpp b/src/script_interface/system/System.cpp index 203ea1b3662..aaa3ca554d0 100644 --- a/src/script_interface/system/System.cpp +++ b/src/script_interface/system/System.cpp @@ -19,7 +19,7 @@ #include "System.hpp" -#include "config.hpp" +#include "config/config.hpp" #include "core/grid.hpp" #include "core/object-in-fluid/oif_global_forces.hpp" diff --git a/src/script_interface/tests/Actors_test.cpp b/src/script_interface/tests/Actors_test.cpp index 9a4bd19e1c3..e24a33566b0 100644 --- a/src/script_interface/tests/Actors_test.cpp +++ b/src/script_interface/tests/Actors_test.cpp @@ -20,7 +20,7 @@ #define BOOST_TEST_MODULE "Long-range actors test" #define BOOST_TEST_DYN_LINK -#include "config.hpp" +#include "config/config.hpp" #if defined(ELECTROSTATICS) or defined(DIPOLES) or defined(SCAFACOS) or \ defined(SCAFACOS_DIPOLES) diff --git a/src/script_interface/virtual_sites/ActiveVirtualSitesHandle.hpp b/src/script_interface/virtual_sites/ActiveVirtualSitesHandle.hpp index 682f9453cd0..ce35e026b3b 100644 --- a/src/script_interface/virtual_sites/ActiveVirtualSitesHandle.hpp +++ b/src/script_interface/virtual_sites/ActiveVirtualSitesHandle.hpp @@ -22,7 +22,7 @@ #ifndef SCRIPT_INTERFACE_VIRTUAL_SITES_ACTIVE_VIRTUAL_SITES_HANDLE_HPP #define SCRIPT_INTERFACE_VIRTUAL_SITES_ACTIVE_VIRTUAL_SITES_HANDLE_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef VIRTUAL_SITES diff --git a/src/script_interface/virtual_sites/VirtualSites.hpp b/src/script_interface/virtual_sites/VirtualSites.hpp index 4bfb1ebf416..75d02c42d8b 100644 --- a/src/script_interface/virtual_sites/VirtualSites.hpp +++ b/src/script_interface/virtual_sites/VirtualSites.hpp @@ -22,7 +22,7 @@ #ifndef SCRIPT_INTERFACE_VIRTUAL_SITES_VIRTUAL_SITES_HPP #define SCRIPT_INTERFACE_VIRTUAL_SITES_VIRTUAL_SITES_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef VIRTUAL_SITES diff --git a/src/script_interface/virtual_sites/VirtualSitesInertialessTracers.hpp b/src/script_interface/virtual_sites/VirtualSitesInertialessTracers.hpp index 0ec880219fd..1fbe703c9e8 100644 --- a/src/script_interface/virtual_sites/VirtualSitesInertialessTracers.hpp +++ b/src/script_interface/virtual_sites/VirtualSitesInertialessTracers.hpp @@ -22,7 +22,7 @@ #ifndef SCRIPT_INTERFACE_VIRTUAL_SITES_VIRTUAL_SITES_INERTIALESS_TRACERS_HPP #define SCRIPT_INTERFACE_VIRTUAL_SITES_VIRTUAL_SITES_INERTIALESS_TRACERS_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef VIRTUAL_SITES_INERTIALESS_TRACERS diff --git a/src/script_interface/virtual_sites/VirtualSitesOff.hpp b/src/script_interface/virtual_sites/VirtualSitesOff.hpp index 32bcdc460f8..0987558aa55 100644 --- a/src/script_interface/virtual_sites/VirtualSitesOff.hpp +++ b/src/script_interface/virtual_sites/VirtualSitesOff.hpp @@ -22,7 +22,7 @@ #ifndef SCRIPT_INTERFACE_VIRTUAL_SITES_VIRTUAL_SITES_OFF_HPP #define SCRIPT_INTERFACE_VIRTUAL_SITES_VIRTUAL_SITES_OFF_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef VIRTUAL_SITES diff --git a/src/script_interface/virtual_sites/VirtualSitesRelative.hpp b/src/script_interface/virtual_sites/VirtualSitesRelative.hpp index e448247766e..0b70e3869e5 100644 --- a/src/script_interface/virtual_sites/VirtualSitesRelative.hpp +++ b/src/script_interface/virtual_sites/VirtualSitesRelative.hpp @@ -22,7 +22,7 @@ #ifndef SCRIPT_INTERFACE_VIRTUAL_SITES_VIRTUAL_SITES_RELATIVE_HPP #define SCRIPT_INTERFACE_VIRTUAL_SITES_VIRTUAL_SITES_RELATIVE_HPP -#include "config.hpp" +#include "config/config.hpp" #ifdef VIRTUAL_SITES_RELATIVE diff --git a/src/script_interface/virtual_sites/initialize.cpp b/src/script_interface/virtual_sites/initialize.cpp index 07792ae1a15..7d43ec56d2b 100644 --- a/src/script_interface/virtual_sites/initialize.cpp +++ b/src/script_interface/virtual_sites/initialize.cpp @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "config.hpp" +#include "config/config.hpp" #include "initialize.hpp" From 4241606a25ad8eedb0f6dc7c99688e85e3b6d78a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 18 Aug 2022 17:54:52 +0200 Subject: [PATCH 39/85] Write new non-bonded interaction interface --- src/core/event.cpp | 5 + src/core/event.hpp | 3 + .../nonbonded_interaction_data.cpp | 52 +++-- .../nonbonded_interaction_data.hpp | 21 +- src/python/espressomd/interactions.pyx | 170 +++++++++++++-- src/python/espressomd/script_interface.pyx | 8 +- src/script_interface/Context.hpp | 6 + src/script_interface/GlobalContext.cpp | 14 +- src/script_interface/GlobalContext.hpp | 3 + src/script_interface/LocalContext.hpp | 6 + .../interactions/NonBondedInteraction.hpp | 199 ++++++++++++++++++ .../interactions/NonBondedInteractions.hpp | 128 +++++++++++ .../interactions/initialize.cpp | 7 + .../tests/LocalContext_test.cpp | 10 + .../tests/ObjectHandle_test.cpp | 6 + .../interactions_non-bonded_interface.py | 39 +++- testsuite/python/save_checkpoint.py | 4 + testsuite/python/test_checkpoint.py | 4 + 18 files changed, 638 insertions(+), 47 deletions(-) create mode 100644 src/script_interface/interactions/NonBondedInteraction.hpp create mode 100644 src/script_interface/interactions/NonBondedInteractions.hpp diff --git a/src/core/event.cpp b/src/core/event.cpp index 3419a7cf206..0607f978408 100644 --- a/src/core/event.cpp +++ b/src/core/event.cpp @@ -251,6 +251,11 @@ void on_dipoles_change() { on_short_range_ia_change(); } +void on_non_bonded_ia_change() { + maximal_cutoff_nonbonded(); + on_short_range_ia_change(); +} + void on_short_range_ia_change() { cells_re_init(cell_structure.decomposition_type()); recalc_forces = true; diff --git a/src/core/event.hpp b/src/core/event.hpp index 6e39cbff4ab..8f1f56c6af6 100644 --- a/src/core/event.hpp +++ b/src/core/event.hpp @@ -79,6 +79,9 @@ void on_dipoles_change(); /** called every time short ranged interaction parameters are changed. */ void on_short_range_ia_change(); +/** called every time a non-bonded interaction parameters are changed. */ +void on_non_bonded_ia_change(); + /** called every time a constraint is changed. */ void on_constraint_change(); diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index 5250e802c87..8d782caee1e 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -48,7 +48,8 @@ * variables *****************************************/ int max_seen_particle_type = 0; -std::vector nonbonded_ia_params; +std::vector old_nonbonded_ia_params; +std::vector> nonbonded_ia_params; /** Minimal global interaction cutoff. Particles with a distance * smaller than this are guaranteed to be available on the same node @@ -60,21 +61,33 @@ static double min_global_cut = INACTIVE_CUTOFF; * general low-level functions *****************************************/ -static void mpi_realloc_ia_params_local(int new_size) { - if (new_size <= max_seen_particle_type) +void mpi_realloc_ia_params_local(int new_size) { + auto const old_size = ::max_seen_particle_type; + if (new_size <= old_size) return; - auto new_params = std::vector(new_size * (new_size + 1) / 2); + auto const n_pairs = new_size * (new_size + 1) / 2; + auto new_params_ = std::vector(n_pairs); + auto new_params = std::vector>(n_pairs); /* if there is an old field, move entries */ - for (int i = 0; i < max_seen_particle_type; i++) - for (int j = i; j < max_seen_particle_type; j++) { - new_params.at(Utils::upper_triangular(i, j, new_size)) = - std::move(get_ia_param(i, j)); + for (int i = 0; i < old_size; i++) { + for (int j = i; j < old_size; j++) { + auto const new_key = Utils::upper_triangular(i, j, new_size); + auto const old_key = Utils::upper_triangular(i, j, old_size); + new_params_.at(new_key) = std::move(old_nonbonded_ia_params[old_key]); + new_params[new_key] = std::move(nonbonded_ia_params[old_key]); } + } + for (auto &ia_params : new_params) { + if (ia_params == nullptr) { + ia_params = std::make_shared(); + } + } - max_seen_particle_type = new_size; - std::swap(nonbonded_ia_params, new_params); + ::max_seen_particle_type = new_size; + std::swap(::old_nonbonded_ia_params, new_params_); + std::swap(::nonbonded_ia_params, new_params); } REGISTER_CALLBACK(mpi_realloc_ia_params_local) @@ -85,12 +98,12 @@ static void mpi_realloc_ia_params(int new_size) { } static void mpi_bcast_all_ia_params_local() { - boost::mpi::broadcast(comm_cart, nonbonded_ia_params, 0); + boost::mpi::broadcast(comm_cart, old_nonbonded_ia_params, 0); } REGISTER_CALLBACK(mpi_bcast_all_ia_params_local) -/** Broadcast @ref nonbonded_ia_params to all nodes. */ +/** Broadcast @ref old_nonbonded_ia_params to all nodes. */ static void mpi_bcast_all_ia_params() { mpi_call_all(mpi_bcast_all_ia_params_local); } @@ -103,7 +116,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 << nonbonded_ia_params; + oa << old_nonbonded_ia_params; oa << max_seen_particle_type; return out.str(); } @@ -113,8 +126,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); - nonbonded_ia_params.clear(); - ia >> nonbonded_ia_params; + old_nonbonded_ia_params.clear(); + ia >> old_nonbonded_ia_params; ia >> max_seen_particle_type; mpi_realloc_ia_params(max_seen_particle_type); mpi_bcast_all_ia_params(); @@ -204,16 +217,21 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { double maximal_cutoff_nonbonded() { auto max_cut_nonbonded = INACTIVE_CUTOFF; - for (auto &data : nonbonded_ia_params) { + for (auto &data : old_nonbonded_ia_params) { data.max_cut = recalc_maximal_cutoff(data); max_cut_nonbonded = std::max(max_cut_nonbonded, data.max_cut); } + for (auto &data : nonbonded_ia_params) { + data->max_cut = recalc_maximal_cutoff(*data); + max_cut_nonbonded = std::max(max_cut_nonbonded, data->max_cut); + } + return max_cut_nonbonded; } void reset_ia_params() { - boost::fill(nonbonded_ia_params, IA_parameters{}); + boost::fill(old_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 defb1d1f122..fd85c9bbb3a 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -185,7 +186,7 @@ struct Thole_Parameters { struct DPDParameters { double gamma = 0.; double k = 1.; - double cutoff = -1.; + double cutoff = INACTIVE_CUTOFF; int wf = 0; double pref = 0.0; }; @@ -275,7 +276,8 @@ struct IA_parameters { #endif }; -extern std::vector nonbonded_ia_params; +extern std::vector old_nonbonded_ia_params; +extern std::vector> nonbonded_ia_params; /** Maximal particle type seen so far. */ extern int max_seen_particle_type; @@ -285,6 +287,13 @@ extern int max_seen_particle_type; */ double maximal_cutoff_nonbonded(); +inline int get_ia_param_key(int i, int j) { + assert(i >= 0 && i < ::max_seen_particle_type); + assert(j >= 0 && j < ::max_seen_particle_type); + return Utils::upper_triangular(std::min(i, j), std::max(i, j), + ::max_seen_particle_type); +} + /** * @brief Get interaction parameters between particle types i and j * @@ -297,11 +306,7 @@ double maximal_cutoff_nonbonded(); * @return Reference to interaction parameters for the type pair. */ 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 nonbonded_ia_params[Utils::upper_triangular( - std::min(i, j), std::max(i, j), max_seen_particle_type)]; + return ::old_nonbonded_ia_params[get_ia_param_key(i, j)]; } /** Get interaction parameters between particle types i and j. @@ -318,6 +323,8 @@ std::string ia_params_get_state(); */ void ia_params_set_state(std::string const &); +void mpi_realloc_ia_params_local(int new_size); + bool is_new_particle_type(int type); /** Make sure that ia_params is large enough to cover interactions * for this particle type. The interactions are initialized with values diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 2344513c14b..73b4dac197c 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -205,6 +205,85 @@ cdef class NonBondedInteraction: raise Exception( "Subclasses of NonBondedInteraction must define the required_keys() method.") + +class NewNonBondedInteraction(ScriptInterfaceHelper): + """ + Represents an instance of a non-bonded interaction, such as Lennard-Jones. + + """ + + def __init__(self, **kwargs): + if "sip" in kwargs: + super().__init__(**kwargs) + else: + self._validate(params=kwargs, check_required=True) + params = self.default_params() + params.update(kwargs) + super().__init__(**params) + + def __str__(self): + return f'{self.__class__.__name__}({self.get_params()})' + + def _validate(self, params, check_required=False): + # Check, if any key was passed, which is not known + keys = {x for x in params.keys() if x != "_types"} + utils.check_valid_keys(self.valid_keys(), keys) + # When an interaction is newly activated, all required keys must be + # given + if check_required: + utils.check_required_keys(self.required_keys(), params.keys()) + + def set_params(self, **kwargs): + """Update the given parameters. + + """ + self._validate(params=kwargs, check_required=not self.is_active()) + + params = self.get_params() + params.update(kwargs) + + err_msg = f"setting {self.type_name()} raised an error" + self.call_method("set_params", handle_errors_message=err_msg, **params) + + def _serialize(self): + return (self.__class__.__name__, self.get_params()) + + def default_params(self): + """Virtual method. + + """ + raise Exception( + "Subclasses of NonBondedInteraction must define the default_params() method.") + + def is_active(self): + """Virtual method. + + """ + raise Exception( + "Subclasses of NonBondedInteraction must define the is_active() method.") + + def type_name(self): + """Virtual method. + + """ + raise Exception( + "Subclasses of NonBondedInteraction must define the type_name() method.") + + def valid_keys(self): + """Virtual method. + + """ + raise Exception( + "Subclasses of NonBondedInteraction must define the valid_keys() method.") + + def required_keys(self): + """Virtual method. + + """ + raise Exception( + "Subclasses of NonBondedInteraction must define the required_keys() method.") + + IF LENNARD_JONES == 1: cdef class LennardJonesInteraction(NonBondedInteraction): @@ -1528,17 +1607,32 @@ IF GAUSSIAN == 1: return {"eps", "sig", "cutoff"} -class NonBondedInteractionHandle: +@script_interface_register +class NonBondedInteractionHandle(ScriptInterfaceHelper): """ Provides access to all non-bonded interactions between two particle types. """ + _so_name = "Interactions::NonBondedInteractionHandle" - def __init__(self, _type1, _type2): + def __getattr__(self, key): + obj = super().__getattr__(key) + return globals()[obj.__class__.__name__]( + _types=self.call_method("get_types"), **obj.get_params()) + + def __init__(self, *args, **kwargs): + if "sip" in kwargs: + super().__init__(**kwargs) + return + if len(args): + _type1, _type2 = args + else: + _type1, _type2 = kwargs.pop("_types") if not (utils.is_valid_type(_type1, int) and utils.is_valid_type(_type2, int)): raise TypeError("The particle types have to be of type integer.") + super().__init__(_types=[_type1, _type2], **kwargs) # Here, add one line for each nonbonded ia IF LENNARD_JONES: @@ -1578,40 +1672,78 @@ class NonBondedInteractionHandle: IF THOLE: self.thole = TholeInteraction(_type1, _type2) + def _serialize(self): + serialized = [] + for name, obj in self.get_params().items(): + serialized.append((name, *obj._serialize())) + return serialized + -cdef class NonBondedInteractions: +@script_interface_register +class NonBondedInteractions(ScriptInterfaceHelper): """ Access to non-bonded interaction parameters via ``[i,j]``, where ``i, j`` are particle types. Returns a :class:`NonBondedInteractionHandle` object. - Also: access to force capping. """ + _so_name = "Interactions::NonBondedInteractions" + _so_creation_policy = "GLOBAL" + # _so_bind_methods = ("reset",) - def __getitem__(self, key): - if not isinstance(key, tuple): - raise ValueError( - "NonBondedInteractions[] expects two particle types as indices.") - if len(key) != 2 or (not utils.is_valid_type(key[0], int)) or ( - not utils.is_valid_type(key[1], int)): - raise ValueError( + def keys(self): + return [tuple(x) for x in self.call_method("keys")] + + def _assert_key_type(self, key): + if not isinstance(key, tuple) or len(key) != 2 or \ + not utils.is_valid_type(key[0], int) or not utils.is_valid_type(key[1], int): + raise TypeError( "NonBondedInteractions[] expects two particle types as indices.") - return NonBondedInteractionHandle(key[0], key[1]) + + def __getitem__(self, key): + self._assert_key_type(key) + return NonBondedInteractionHandle(_types=key) + + def __setitem__(self, key, value): + self._assert_key_type(key) + self.call_method("insert", key=key, object=value) def __getstate__(self): - cdef string core_state - core_state = ia_params_get_state() - return core_state + cdef string core_state = ia_params_get_state() + n_types = self.call_method("get_n_types") + state = [] + for i in range(n_types): + for j in range(i, n_types): + handle = NonBondedInteractionHandle(_types=(i, j)) + state.append(((i, j), handle._serialize())) + return {"state": state, "core_state": core_state} - def __setstate__(self, core_state): - cdef string state = core_state - ia_params_set_state(state) + def __setstate__(self, params): + for types, kwargs in params["state"]: + objects = {} + for name, class_name, obj_params in kwargs: + objects[name] = globals()[class_name](**obj_params) + obj = NonBondedInteractionHandle(_types=types, **objects) + self.call_method("insert", key=types, object=obj) def reset(self): """ Reset all interaction parameters to their default values. """ - reset_ia_params() + self.call_method("reset") + + @classmethod + def _restore_object(cls, so_callback, so_callback_args, state): + cdef string core_state = state["core_state"] + ia_params_set_state(core_state) + so = so_callback(*so_callback_args) + so.__setstate__(state) + return so + + def __reduce__(self): + so_callback, (so_name, so_bytestring) = super().__reduce__() + return (NonBondedInteractions._restore_object, + (so_callback, (so_name, so_bytestring), self.__getstate__())) class BondedInteraction(ScriptInterfaceHelper): diff --git a/src/python/espressomd/script_interface.pyx b/src/python/espressomd/script_interface.pyx index 67a9151e658..176beea6185 100644 --- a/src/python/espressomd/script_interface.pyx +++ b/src/python/espressomd/script_interface.pyx @@ -136,7 +136,7 @@ cdef class PScriptInterface: self.sip = sip - def call_method(self, method, **kwargs): + def call_method(self, method, handle_errors_message=None, **kwargs): """ Call a method of the core class. @@ -144,6 +144,8 @@ cdef class PScriptInterface: ---------- method : Creation policy. Name of the core method. + handle_errors_message : :obj:`str`, optional + Custom error message for runtime errors raised in a MPI context. \*\*kwargs Arguments for the method. """ @@ -156,7 +158,9 @@ cdef class PScriptInterface: value = self.sip.get().call_method(utils.to_char_pointer(method), parameters) res = variant_to_python_object(value) - utils.handle_errors(f'while calling method {method}()') + if handle_errors_message is None: + handle_errors_message = f"while calling method {method}()" + utils.handle_errors(handle_errors_message) return res def name(self): diff --git a/src/script_interface/Context.hpp b/src/script_interface/Context.hpp index 5aace50f5a3..21717a46db1 100644 --- a/src/script_interface/Context.hpp +++ b/src/script_interface/Context.hpp @@ -81,6 +81,12 @@ class Context : public std::enable_shared_from_this { virtual std::shared_ptr make_shared(std::string const &name, const VariantMap ¶meters) = 0; + /** + * @copydoc Context::make_shared + */ + virtual std::shared_ptr + make_shared_local(std::string const &name, VariantMap const ¶meters) = 0; + protected: /** * @brief Set the context of an object to this. diff --git a/src/script_interface/GlobalContext.cpp b/src/script_interface/GlobalContext.cpp index ed22422e782..c4859b71be9 100644 --- a/src/script_interface/GlobalContext.cpp +++ b/src/script_interface/GlobalContext.cpp @@ -88,10 +88,22 @@ void GlobalContext::notify_call_method(const ObjectHandle *o, cb_call_method(object_id(o), name, pack(arguments)); } +std::shared_ptr +GlobalContext::make_shared_local(std::string const &name, + VariantMap const ¶meters) { + auto sp = m_node_local_context->factory().make(name); + set_context(sp.get()); + + sp->construct(parameters); + + return sp; +} + std::shared_ptr GlobalContext::make_shared(std::string const &name, const VariantMap ¶meters) { - std::unique_ptr sp = m_node_local_context->factory().make(name); + assert(is_head_node()); + auto sp = m_node_local_context->factory().make(name); set_context(sp.get()); auto const id = object_id(sp.get()); diff --git a/src/script_interface/GlobalContext.hpp b/src/script_interface/GlobalContext.hpp index d4de6ebbc02..53ac78cf88a 100644 --- a/src/script_interface/GlobalContext.hpp +++ b/src/script_interface/GlobalContext.hpp @@ -163,6 +163,9 @@ class GlobalContext : public Context { */ std::shared_ptr make_shared(std::string const &name, const VariantMap ¶meters) override; + std::shared_ptr + make_shared_local(std::string const &name, + VariantMap const ¶meters) override; boost::string_ref name(const ObjectHandle *o) const override; diff --git a/src/script_interface/LocalContext.hpp b/src/script_interface/LocalContext.hpp index dcb81be5edc..c12047f9002 100644 --- a/src/script_interface/LocalContext.hpp +++ b/src/script_interface/LocalContext.hpp @@ -69,6 +69,12 @@ class LocalContext : public Context { return sp; } + std::shared_ptr + make_shared_local(std::string const &name, + VariantMap const ¶meters) override { + return make_shared(name, parameters); + } + boost::string_ref name(const ObjectHandle *o) const override { assert(o); diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp new file mode 100644 index 00000000000..f810ec3ab02 --- /dev/null +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -0,0 +1,199 @@ +/* + * 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 . + */ + +/** @file + * The ScriptInterface counterparts of the non-bonded interactions parameters + * structs from the core are defined here. + */ + +#ifndef SCRIPT_INTERFACE_INTERACTIONS_NONBONDED_INTERACTION_HPP +#define SCRIPT_INTERFACE_INTERACTIONS_NONBONDED_INTERACTION_HPP + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" +#include "script_interface/get_value.hpp" + +#include "core/event.hpp" +#include "core/nonbonded_interactions/nonbonded_interaction_data.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace ScriptInterface { +namespace Interactions { + +template +class InteractionPotentialInterface + : public AutoParameters> { + std::array m_types = {-1, -1}; + +public: + using CoreInteraction = CoreIA; + +protected: + using AutoParameters>::context; + std::shared_ptr m_ia_si; + virtual CoreInteraction IA_parameters::*get_ptr_offset() const = 0; + virtual void make_new_instance(VariantMap const ¶ms) = 0; + + template + auto make_autoparameter(T CoreInteraction::*ptr, char const *name) { + return AutoParameter{name, AutoParameter::read_only, + [this, ptr]() { return m_ia_si.get()->*ptr; }}; + } + +public: + Variant do_call_method(std::string const &name, + VariantMap const ¶ms) override { + if (name == "set_params") { + context()->parallel_try_catch( + [this, ¶ms]() { make_new_instance(params); }); + if (m_types[0] != -1) { + copy_si_to_core(); + on_non_bonded_ia_change(); + } + return {}; + } + if (name == "bind_types") { + auto types = get_value>(params, "_types"); + if (types[0] > types[1]) { + std::swap(types[0], types[1]); + } + if (m_types[0] == -1 or + (m_types[0] == types[0] and m_types[1] == types[1])) { + m_types[0] = types[0]; + m_types[1] = types[1]; + } else { + context()->parallel_try_catch([this]() { + throw std::runtime_error( + "Non-bonded interaction is already bound to interaction pair [" + + std::to_string(m_types[0]) + ", " + std::to_string(m_types[1]) + + "]"); + }); + } + return {}; + } + return {}; + } + + void do_construct(VariantMap const ¶ms) final { + if (params.empty()) { + m_ia_si = std::make_shared(); + } else if (params.count("_types") != 0) { + do_call_method("bind_types", params); + m_ia_si = std::make_shared(); + copy_core_to_si(); + } else { + context()->parallel_try_catch( + [this, ¶ms]() { make_new_instance(params); }); + } + } + + auto const &get_ia() const { return *m_ia_si; } + + void copy_si_to_core() { + assert(m_ia_si != nullptr); + auto const key = get_ia_param_key(m_types[0], m_types[1]); + assert(key < ::nonbonded_ia_params.size()); + ::nonbonded_ia_params[key].get()->*get_ptr_offset() = *m_ia_si; + ::old_nonbonded_ia_params[key].*get_ptr_offset() = *m_ia_si; + } + + void copy_core_to_si() { + assert(m_ia_si != nullptr); + auto const key = get_ia_param_key(m_types[0], m_types[1]); + assert(key < ::nonbonded_ia_params.size()); + *m_ia_si = ::nonbonded_ia_params[key].get()->*get_ptr_offset(); + } +}; + +class NonBondedInteractionHandle + : public AutoParameters { + std::array m_types = {-1, -1}; + std::shared_ptr<::IA_parameters> m_interaction; + + template + auto make_autoparameter(std::shared_ptr &member, const char *key) const { + auto const setter = [this, &member](Variant const &v) { + member = get_value>(v); + if (m_types[0] != -1) { + auto const types = Variant{std::vector{{m_types[0], m_types[1]}}}; + member->do_call_method("bind_types", VariantMap{{"_types", types}}); + member->copy_si_to_core(); + on_non_bonded_ia_change(); + } + }; + return AutoParameter{key, setter, [&member]() { return member; }}; + } + +public: + NonBondedInteractionHandle() { + add_parameters({ + }); + } + +private: + template + void set_member(std::shared_ptr &member, std::string key, + std::string so_name, VariantMap const ¶ms) { + auto const ia_types = VariantMap{{"_types", params.at("_types")}}; + if (params.count(key) != 0) { + member = get_value>(params.at(key)); + member->do_call_method("bind_types", ia_types); + member->copy_si_to_core(); + } else { + auto so_object = context()->make_shared_local(so_name, ia_types); + member = std::dynamic_pointer_cast(so_object); + member->copy_core_to_si(); + } + } + +public: + Variant do_call_method(std::string const &name, + VariantMap const ¶ms) override { + if (name == "get_types") { + return std::vector{{m_types[0], m_types[1]}}; + } + return {}; + } + + void do_construct(VariantMap const ¶ms) override { + assert(params.count("_types") != 0); + auto const types = get_value>(params.at("_types")); + m_types[0] = std::min(types[0], types[1]); + m_types[1] = std::max(types[0], types[1]); + // make type exist + mpi_realloc_ia_params_local(m_types[1] + 1); + // create interface objects + auto const key = get_ia_param_key(m_types[0], m_types[1]); + m_interaction = ::nonbonded_ia_params[key]; + } + + auto get_ia() const { return m_interaction; } +}; + +} // namespace Interactions +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/interactions/NonBondedInteractions.hpp b/src/script_interface/interactions/NonBondedInteractions.hpp new file mode 100644 index 00000000000..0817210c4cf --- /dev/null +++ b/src/script_interface/interactions/NonBondedInteractions.hpp @@ -0,0 +1,128 @@ +/* + * 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_INTERACTIONS_NONBONDED_INTERACTIONS_HPP +#define SCRIPT_INTERFACE_INTERACTIONS_NONBONDED_INTERACTIONS_HPP + +#include "NonBondedInteraction.hpp" + +#include "core/event.hpp" +#include "core/nonbonded_interactions/nonbonded_interaction_data.hpp" + +#include "script_interface/ObjectMap.hpp" +#include "script_interface/ScriptInterface.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace ScriptInterface { +namespace Interactions { + +class NonBondedInteractions : public ObjectHandle { + using container_type = + std::unordered_map>; + + auto make_interaction(int i, int j) { + assert(i <= j); + auto const types = std::vector{{i, j}}; + return std::dynamic_pointer_cast( + context()->make_shared_local("Interactions::NonBondedInteractionHandle", + {{"_types", Variant{types}}})); + } + +public: + using key_type = typename container_type::key_type; + using mapped_type = typename container_type::mapped_type; + + void reset() { + auto const size = ::max_seen_particle_type; + for (int i = 0; i < size; i++) { + for (int j = i; j < size; j++) { + auto const key = Utils::upper_triangular(i, j, size); + ::nonbonded_ia_params[i] = std::make_shared<::IA_parameters>(); + ::old_nonbonded_ia_params[key] = ::IA_parameters{}; + m_nonbonded_ia_params[key] = make_interaction(i, j); + } + } + on_non_bonded_ia_change(); + } + + void do_construct(VariantMap const ¶ms) override { + auto const size = ::max_seen_particle_type; + { + // when reloading from a checkpoint file, need to resize IA lists + auto const new_size = ::max_seen_particle_type; + auto const n_pairs = new_size * (new_size + 1) / 2; + ::old_nonbonded_ia_params.resize(n_pairs); + ::nonbonded_ia_params.resize(n_pairs); + for (auto &ia_params : ::nonbonded_ia_params) { + if (ia_params == nullptr) { + ia_params = std::make_shared<::IA_parameters>(); + } + } + } + for (int i = 0; i < size; i++) { + for (int j = i; j < size; j++) { + auto const key = Utils::upper_triangular(i, j, size); + m_nonbonded_ia_params[key] = make_interaction(i, j); + } + } + } + + Variant do_call_method(std::string const &name, + VariantMap const ¶ms) override { + if (name == "get_n_types") { + return Variant{::max_seen_particle_type}; + } + if (name == "reset") { + reset(); + return {}; + } + if (name == "insert") { + auto const types = get_value>(params.at("key")); + auto const key = get_ia_param_key(std::min(types[0], types[1]), + std::max(types[0], types[1])); + auto obj_ptr = get_value>( + params.at("object")); + ::nonbonded_ia_params[key] = obj_ptr->get_ia(); + m_nonbonded_ia_params[key] = obj_ptr; + ::old_nonbonded_ia_params[key] = *(obj_ptr->get_ia()); + on_non_bonded_ia_change(); + return {}; + } + + return {}; + } + +private: + // disable serialization: pickling done by the python interface + std::string get_internal_state() const override { return {}; } + void set_internal_state(std::string const &state) override {} + container_type m_nonbonded_ia_params; +}; + +} // namespace Interactions +} // namespace ScriptInterface + +#endif diff --git a/src/script_interface/interactions/initialize.cpp b/src/script_interface/interactions/initialize.cpp index 77418ae1bff..47e9c8adaf7 100644 --- a/src/script_interface/interactions/initialize.cpp +++ b/src/script_interface/interactions/initialize.cpp @@ -20,6 +20,8 @@ #include "BondedInteraction.hpp" #include "BondedInteractions.hpp" +#include "NonBondedInteraction.hpp" +#include "NonBondedInteractions.hpp" #include @@ -49,6 +51,11 @@ void initialize(Utils::Factory *om) { om->register_new("Interactions::OifGlobalForcesBond"); om->register_new("Interactions::OifLocalForcesBond"); om->register_new("Interactions::VirtualBond"); + + om->register_new( + "Interactions::NonBondedInteractions"); + om->register_new( + "Interactions::NonBondedInteractionHandle"); } } // namespace Interactions } // namespace ScriptInterface diff --git a/src/script_interface/tests/LocalContext_test.cpp b/src/script_interface/tests/LocalContext_test.cpp index 60a8ed88df8..d678012c68e 100644 --- a/src/script_interface/tests/LocalContext_test.cpp +++ b/src/script_interface/tests/LocalContext_test.cpp @@ -70,6 +70,16 @@ BOOST_AUTO_TEST_CASE(LocalContext_make_shared) { BOOST_CHECK_EQUAL(ctx->name(res.get()), "Dummy"); } +BOOST_AUTO_TEST_CASE(LocalContext_make_shared_local) { + boost::mpi::communicator comm; + auto ctx = std::make_shared(factory, comm); + + auto res = ctx->make_shared_local("Dummy", {}); + BOOST_REQUIRE(res != nullptr); + BOOST_CHECK_EQUAL(res->context(), ctx.get()); + BOOST_CHECK_EQUAL(ctx->name(res.get()), "Dummy"); +} + BOOST_AUTO_TEST_CASE(LocalContext_serialization) { boost::mpi::communicator comm; auto ctx = std::make_shared(factory, comm); diff --git a/src/script_interface/tests/ObjectHandle_test.cpp b/src/script_interface/tests/ObjectHandle_test.cpp index ed24a0ec4a2..06e96a97aa3 100644 --- a/src/script_interface/tests/ObjectHandle_test.cpp +++ b/src/script_interface/tests/ObjectHandle_test.cpp @@ -188,6 +188,10 @@ struct LogContext : public Context { return it; } + std::shared_ptr + make_shared_local(std::string const &s, VariantMap const &v) override { + return make_shared(s, v); + } boost::string_ref name(const ObjectHandle *o) const override { return "Dummy"; @@ -247,7 +251,9 @@ BOOST_AUTO_TEST_CASE(interface_) { using namespace Testing; auto log_ctx = std::make_shared(); auto o = log_ctx->make_shared({}, {}); + auto l = log_ctx->make_shared_local({}, {}); BOOST_CHECK(log_ctx->is_head_node()); BOOST_CHECK_EQUAL(log_ctx->name(o.get()), "Dummy"); + BOOST_CHECK_EQUAL(log_ctx->name(l.get()), "Dummy"); static_cast(log_ctx->parallel_try_catch([]() {})); } diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index c0d236b7b0f..b4f9d8cdeb5 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -24,7 +24,7 @@ import espressomd.interactions -class Non_bonded_interactionsTests(ut.TestCase): +class Test(ut.TestCase): system = espressomd.System(box_l=[30.0, 30.0, 30.0]) def tearDown(self): @@ -162,6 +162,33 @@ def func(self): "k2": 5.0, "mu": 2.0, "nu": 1.0}, "gay_berne") + @utx.skipIfMissingFeatures(["LENNARD_JONES", "WCA"]) + def test_set_params(self): + self.system.non_bonded_inter[0, 0].lennard_jones.set_params( + epsilon=1., sigma=2., cutoff=3., shift="auto") + self.system.non_bonded_inter.reset() + self.assertEqual(self.system.non_bonded_inter[0, 0].lennard_jones.get_params(), + {'shift': 0., 'sigma': 0., 'epsilon': 0., + 'cutoff': -1., 'offset': 0., 'min': 0.}) + wca = espressomd.interactions.WCAInteraction(epsilon=1., sigma=2.) + wca.set_params(epsilon=2.) + self.assertEqual(wca.get_params()["epsilon"], 2.) + self.system.non_bonded_inter[0, 0].wca = wca + self.assertEqual( + self.system.non_bonded_inter[0, 0].wca.get_params()["epsilon"], 2.) + self.assertEqual(wca.get_params()["epsilon"], 2.) + wca.set_params(epsilon=3.) + self.assertEqual( + self.system.non_bonded_inter[0, 0].wca.get_params()["epsilon"], 3.) + self.assertEqual(wca.get_params()["epsilon"], 3.) + self.system.non_bonded_inter.reset() + wca.set_params(epsilon=4.) + self.assertEqual( + self.system.non_bonded_inter[0, 0].wca.get_params()["epsilon"], 4.) + self.assertEqual(wca.get_params()["epsilon"], 4.) + with self.assertRaisesRegex(RuntimeError, r"Non-bonded interaction is already bound to interaction pair \[0, 0\]"): + self.system.non_bonded_inter[0, 1].wca = wca + @utx.skipIfMissingFeatures("LENNARD_JONES") def test_exceptions(self): err_msg_required = (r"The following keys have to be given as keyword arguments: " @@ -182,6 +209,9 @@ def test_exceptions(self): 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.) + with self.assertRaisesRegex(ValueError, "Parameter 'shift' has to be 'auto' or a float"): + self.system.non_bonded_inter[0, 0].lennard_jones.set_params( + epsilon=1., sigma=2., cutoff=3., shift="automatic") skin = self.system.cell_system.skin box_l = self.system.box_l @@ -196,6 +226,13 @@ def test_exceptions(self): self.assertAlmostEqual( lennard_jones.get_params()['cutoff'], wrong_cutoff, delta=1e-10) + def check_potential_exceptions(self, ia_class, params, check_keys): + for key in check_keys: + with self.assertRaisesRegex(ValueError, f"parameter '{key}'"): + invalid_params = params.copy() + invalid_params[key] = -0.1 + ia_class(**invalid_params) + if __name__ == "__main__": ut.main() diff --git a/testsuite/python/save_checkpoint.py b/testsuite/python/save_checkpoint.py index 09ebda6f002..aef41515bc5 100644 --- a/testsuite/python/save_checkpoint.py +++ b/testsuite/python/save_checkpoint.py @@ -233,6 +233,10 @@ epsilon=1.2, sigma=1.7, cutoff=2.0, shift=0.1) system.non_bonded_inter[1, 17].lennard_jones.set_params( epsilon=1.2e5, sigma=1.7, cutoff=2.0, shift=0.1) + # add inactive interaction + system.non_bonded_inter[4, 4].lennard_jones.set_params( + epsilon=1.4, sigma=1.2, cutoff=1.5, shift=0.2, offset=0.1, min=0.2) + system.non_bonded_inter[4, 4].lennard_jones.set_params(cutoff=-2.) # bonded interactions harmonic_bond = espressomd.interactions.HarmonicBond(r_0=0.0, k=1.0) diff --git a/testsuite/python/test_checkpoint.py b/testsuite/python/test_checkpoint.py index b9f7d953ae9..0aaba75e277 100644 --- a/testsuite/python/test_checkpoint.py +++ b/testsuite/python/test_checkpoint.py @@ -333,12 +333,16 @@ def test_integrator_SDM(self): def test_non_bonded_inter(self): params1 = system.non_bonded_inter[0, 0].lennard_jones.get_params() params2 = system.non_bonded_inter[3, 0].lennard_jones.get_params() + params3 = system.non_bonded_inter[4, 4].lennard_jones.get_params() reference1 = {'shift': 0.1, 'sigma': 1.3, 'epsilon': 1.2, 'cutoff': 2.0, 'offset': 0.0, 'min': 0.0} reference2 = {'shift': 0.1, 'sigma': 1.7, 'epsilon': 1.2, 'cutoff': 2.0, 'offset': 0.0, 'min': 0.0} + reference3 = {'shift': 0.2, 'sigma': 1.2, 'epsilon': 1.4, + 'cutoff': -2.0, 'offset': 0.1, 'min': 0.2} self.assertEqual(params1, reference1) self.assertEqual(params2, reference2) + self.assertEqual(params3, reference3) def test_bonded_inter(self): # check the ObjectHandle was correctly initialized (including MPI) From a1ea710d3b244335f388156b06bfe9f3c583ab3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 19 Aug 2022 00:01:53 +0200 Subject: [PATCH 40/85] Rewrite WCA interaction interface --- .../nonbonded_interaction_data.cpp | 2 +- .../nonbonded_interaction_data.hpp | 3 + src/core/nonbonded_interactions/wca.cpp | 27 ++++---- src/core/nonbonded_interactions/wca.hpp | 2 - src/python/espressomd/interactions.pxd | 12 ---- src/python/espressomd/interactions.pyx | 63 ++++++------------- .../interactions/NonBondedInteraction.hpp | 41 ++++++++++++ .../interactions/initialize.cpp | 3 + .../interactions_non-bonded_interface.py | 7 +++ 9 files changed, 87 insertions(+), 73 deletions(-) diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index 8d782caee1e..70540c32b9d 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -141,7 +141,7 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { #endif #ifdef WCA - max_cut_current = std::max(max_cut_current, data.wca.cut); + max_cut_current = std::max(max_cut_current, data.wca.max_cutoff()); #endif #ifdef DPD diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index fd85c9bbb3a..2a4c971a382 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -55,6 +55,9 @@ struct WCA_Parameters { double eps = 0.0; double sig = 0.0; double cut = INACTIVE_CUTOFF; + WCA_Parameters() = default; + WCA_Parameters(double eps, double sig); + double max_cutoff() const { return cut; } }; /** Generic Lennard-Jones with shift */ diff --git a/src/core/nonbonded_interactions/wca.cpp b/src/core/nonbonded_interactions/wca.cpp index 29a4a2d14dd..389bc65544a 100644 --- a/src/core/nonbonded_interactions/wca.cpp +++ b/src/core/nonbonded_interactions/wca.cpp @@ -23,24 +23,21 @@ #include "wca.hpp" #ifdef WCA -#include "interactions.hpp" #include "nonbonded_interaction_data.hpp" -#include - #include - -int wca_set_params(int part_type_a, int part_type_b, double eps, double sig) { - IA_parameters *data = get_ia_param_safe(part_type_a, part_type_b); - - data->wca.eps = eps; - data->wca.sig = sig; - data->wca.cut = sig * std::pow(2., 1. / 6.); - - /* broadcast interaction parameters */ - mpi_bcast_ia_params(part_type_a, part_type_b); - - return ES_OK; +#include + +WCA_Parameters::WCA_Parameters(double eps, double sig) : eps{eps}, sig{sig} { + if (eps < 0.) { + throw std::domain_error("WCA parameter 'epsilon' has to be >= 0"); + } + if (sig < 0.) { + throw std::domain_error("WCA parameter 'sigma' has to be >= 0"); + } + if (sig != 0.) { + cut = sig * std::pow(2., 1. / 6.); + } } #endif /* ifdef WCA */ diff --git a/src/core/nonbonded_interactions/wca.hpp b/src/core/nonbonded_interactions/wca.hpp index 6c91b291afb..452496c8d3f 100644 --- a/src/core/nonbonded_interactions/wca.hpp +++ b/src/core/nonbonded_interactions/wca.hpp @@ -35,8 +35,6 @@ #include #include -int wca_set_params(int part_type_a, int part_type_b, double eps, double sig); - /** Calculate WCA force factor */ inline double wca_pair_force_factor(IA_parameters const &ia_params, double dist) { diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index 88b3a4b5b07..d96572265a2 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -47,11 +47,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": double offset double min - cdef struct WCA_Parameters: - double eps - double sig - double cut - cdef struct LJGen_Parameters: double eps double sig @@ -154,8 +149,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": cdef struct IA_parameters: LJ_Parameters lj - WCA_Parameters wca - LJcos_Parameters ljcos LJcos2_Parameters ljcos2 @@ -199,11 +192,6 @@ IF LENNARD_JONES: double shift, double offset, double min) -IF WCA: - cdef extern from "nonbonded_interactions/wca.hpp": - cdef int wca_set_params(int part_type_a, int part_type_b, - double eps, double sig) - IF LJCOS: cdef extern from "nonbonded_interactions/ljcos.hpp": cdef int ljcos_set_params(int part_type_a, int part_type_b, diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 73b4dac197c..1521b3218a9 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -389,57 +389,32 @@ IF LENNARD_JONES == 1: IF WCA == 1: - cdef class WCAInteraction(NonBondedInteraction): - - def validate_params(self): - """Check that parameters are valid. - - Raises - ------ - ValueError - If not true. - """ - if self._params["epsilon"] < 0: - raise ValueError("WCA eps has to be >=0") - if self._params["sigma"] < 0: - raise ValueError("WCA sigma has to be >=0") - - def _get_params_from_es_core(self): - cdef IA_parameters * ia_params - ia_params = get_ia_param_safe( - self._part_types[0], - self._part_types[1]) - return { - "epsilon": ia_params.wca.eps, - "sigma": ia_params.wca.sig, - "cutoff": ia_params.wca.cut} - - def is_active(self): - """Check if interaction is active. - - """ - return (self._params["epsilon"] > 0) + @script_interface_register + class WCAInteraction(NewNonBondedInteraction): + """ + Standard 6-12 Weeks-Chandler-Andersen potential. - def set_params(self, **kwargs): - """Set parameters for the WCA interaction. + Methods + ------- + set_params() + Set or update parameters for the interaction. + Parameters marked as required become optional once the + interaction has been activated for the first time; + subsequent calls to this method update the existing values. Parameters ---------- - epsilon : :obj:`float` Magnitude of the interaction. sigma : :obj:`float` Interaction length scale. - """ - super().set_params(**kwargs) + """ - def _set_params_in_es_core(self): - if wca_set_params( - self._part_types[0], self._part_types[1], - self._params["epsilon"], - self._params["sigma"]): - raise Exception("Could not set WCA parameters") + _so_name = "Interactions::InteractionWCA" + + def is_active(self): + return self.epsilon > 0. def default_params(self): """Python dictionary of default parameters. @@ -465,6 +440,10 @@ IF WCA == 1: """ return {"epsilon", "sigma"} + @property + def cutoff(self): + return self.call_method("get_cutoff") + IF LENNARD_JONES_GENERIC == 1: cdef class GenericLennardJonesInteraction(NonBondedInteraction): @@ -1637,8 +1616,6 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): # Here, add one line for each nonbonded ia IF LENNARD_JONES: self.lennard_jones = LennardJonesInteraction(_type1, _type2) - IF WCA: - self.wca = WCAInteraction(_type1, _type2) IF SOFT_SPHERE: self.soft_sphere = SoftSphereInteraction(_type1, _type2) IF LENNARD_JONES_GENERIC: diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index f810ec3ab02..aff6d6de50d 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -128,10 +128,44 @@ class InteractionPotentialInterface } }; +#ifdef WCA +class InteractionWCA : public InteractionPotentialInterface<::WCA_Parameters> { +protected: + CoreInteraction IA_parameters::*get_ptr_offset() const override { + return &::IA_parameters::wca; + } + +public: + InteractionWCA() { + add_parameters({ + make_autoparameter(&CoreInteraction::eps, "epsilon"), + make_autoparameter(&CoreInteraction::sig, "sigma"), + }); + } + + void make_new_instance(VariantMap const ¶ms) override { + m_ia_si = make_shared_from_args( + params, "epsilon", "sigma"); + } + + Variant do_call_method(std::string const &name, + VariantMap const ¶ms) override { + if (name == "get_cutoff") { + return m_ia_si.get()->cut; + } + return InteractionPotentialInterface::do_call_method( + name, params); + } +}; +#endif // WCA + class NonBondedInteractionHandle : public AutoParameters { std::array m_types = {-1, -1}; std::shared_ptr<::IA_parameters> m_interaction; +#ifdef WCA + std::shared_ptr m_wca; +#endif template auto make_autoparameter(std::shared_ptr &member, const char *key) const { @@ -150,6 +184,9 @@ class NonBondedInteractionHandle public: NonBondedInteractionHandle() { add_parameters({ +#ifdef WCA + make_autoparameter(m_wca, "wca"), +#endif }); } @@ -188,6 +225,10 @@ class NonBondedInteractionHandle // create interface objects auto const key = get_ia_param_key(m_types[0], m_types[1]); m_interaction = ::nonbonded_ia_params[key]; +#ifdef WCA + set_member(m_wca, "wca", "Interactions::InteractionWCA", + params); +#endif } auto get_ia() const { return m_interaction; } diff --git a/src/script_interface/interactions/initialize.cpp b/src/script_interface/interactions/initialize.cpp index 47e9c8adaf7..08fd863cbc3 100644 --- a/src/script_interface/interactions/initialize.cpp +++ b/src/script_interface/interactions/initialize.cpp @@ -56,6 +56,9 @@ void initialize(Utils::Factory *om) { "Interactions::NonBondedInteractions"); om->register_new( "Interactions::NonBondedInteractionHandle"); +#ifdef WCA + om->register_new("Interactions::InteractionWCA"); +#endif } } // namespace Interactions } // namespace ScriptInterface diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index b4f9d8cdeb5..8a60fb48e02 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -233,6 +233,13 @@ def check_potential_exceptions(self, ia_class, params, check_keys): invalid_params[key] = -0.1 ia_class(**invalid_params) + @utx.skipIfMissingFeatures("WCA") + def test_wca_exceptions(self): + self.check_potential_exceptions( + espressomd.interactions.WCAInteraction, + {"epsilon": 1., "sigma": 1.}, + ("epsilon", "sigma")) + if __name__ == "__main__": ut.main() From f56f5f358f20685127cb375f6faf84f3e1c806b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 19 Aug 2022 14:08:03 +0200 Subject: [PATCH 41/85] Rewrite LJ interaction interface --- src/core/nonbonded_interactions/lj.cpp | 40 +++++----- src/core/nonbonded_interactions/lj.hpp | 4 - .../nonbonded_interaction_data.cpp | 2 +- .../nonbonded_interaction_data.hpp | 5 ++ .../EspressoSystemStandAlone_test.cpp | 22 +++++- src/core/unit_tests/Verlet_list_test.cpp | 16 +++- src/python/espressomd/interactions.pxd | 16 ---- src/python/espressomd/interactions.pyx | 77 +++++-------------- .../interactions/NonBondedInteraction.hpp | 47 +++++++++++ .../interactions/initialize.cpp | 3 + .../interactions_non-bonded_interface.py | 9 ++- 11 files changed, 138 insertions(+), 103 deletions(-) diff --git a/src/core/nonbonded_interactions/lj.cpp b/src/core/nonbonded_interactions/lj.cpp index ebc988949be..f915e4acfba 100644 --- a/src/core/nonbonded_interactions/lj.cpp +++ b/src/core/nonbonded_interactions/lj.cpp @@ -25,31 +25,33 @@ #include "lj.hpp" #ifdef LENNARD_JONES -#include "interactions.hpp" #include "nonbonded_interaction_data.hpp" -#include +#include -int lennard_jones_set_params(int part_type_a, int part_type_b, double eps, - double sig, double cut, double shift, - double offset, double min) { - IA_parameters *data = get_ia_param_safe(part_type_a, part_type_b); +#include +#include - if (!data) - return ES_ERROR; - - data->lj.eps = eps; - data->lj.sig = sig; - data->lj.cut = cut; - data->lj.shift = shift; - data->lj.offset = offset; - if (min > 0) { - data->lj.min = min; +LJ_Parameters::LJ_Parameters(double eps, double sig, double cut, double offset, + double min) + : LJ_Parameters(eps, sig, cut, offset, min, 0.) { + if (cut != 0.) { + auto const sig_cut = sig / cut; + shift = Utils::int_pow<6>(sig_cut) - Utils::int_pow<12>(sig_cut); } - /* broadcast interaction parameters */ - mpi_bcast_ia_params(part_type_a, part_type_b); +} - return ES_OK; +LJ_Parameters::LJ_Parameters(double eps, double sig, double cut, double offset, + double min, double shift) + : eps{eps}, sig{sig}, cut{cut}, shift{shift}, offset{offset}, min{std::max( + min, + 0.)} { + if (eps < 0.) { + throw std::domain_error("LJ parameter 'epsilon' has to be >= 0"); + } + if (sig < 0.) { + throw std::domain_error("LJ parameter 'sigma' has to be >= 0"); + } } #endif /* ifdef LENNARD_JONES */ diff --git a/src/core/nonbonded_interactions/lj.hpp b/src/core/nonbonded_interactions/lj.hpp index 0cbee73cee2..b6651943e72 100644 --- a/src/core/nonbonded_interactions/lj.hpp +++ b/src/core/nonbonded_interactions/lj.hpp @@ -37,10 +37,6 @@ #include #include -int lennard_jones_set_params(int part_type_a, int part_type_b, double eps, - double sig, double cut, double shift, - double offset, double min); - /** Calculate Lennard-Jones force factor */ inline double lj_pair_force_factor(IA_parameters const &ia_params, double dist) { diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index 70540c32b9d..10d33891b5e 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -137,7 +137,7 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { auto max_cut_current = INACTIVE_CUTOFF; #ifdef LENNARD_JONES - max_cut_current = std::max(max_cut_current, (data.lj.cut + data.lj.offset)); + max_cut_current = std::max(max_cut_current, data.lj.max_cutoff()); #endif #ifdef WCA diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index 2a4c971a382..1c3f68a0ea8 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -48,6 +48,11 @@ struct LJ_Parameters { double shift = 0.0; double offset = 0.0; double min = 0.0; + LJ_Parameters() = default; + LJ_Parameters(double eps, double sig, double cut, double offset, double min); + LJ_Parameters(double eps, double sig, double cut, double offset, double min, + double shift); + double max_cutoff() const { return cut + offset; } }; /** WCA potential */ diff --git a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp index 94cbc51a24d..224f3703cd5 100644 --- a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp +++ b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp @@ -37,6 +37,7 @@ namespace utf = boost::unit_test; #include "electrostatics/p3m.hpp" #include "electrostatics/registration.hpp" #include "energy.hpp" +#include "event.hpp" #include "galilei/Galilei.hpp" #include "integrate.hpp" #include "nonbonded_interactions/lj.hpp" @@ -126,6 +127,18 @@ static void mpi_set_tuned_p3m(double prefactor) { } #endif // P3M +#ifdef LENNARD_JONES +void mpi_set_lj_local(int key, double eps, double sig, double cut, + double offset, double min, double shift) { + LJ_Parameters lj{eps, sig, cut, offset, min, shift}; + ::nonbonded_ia_params[key]->lj = lj; + ::old_nonbonded_ia_params[key].lj = lj; + on_non_bonded_ia_change(); +} + +REGISTER_CALLBACK(mpi_set_lj_local) +#endif // LENNARD_JONES + BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, *utf::precondition(if_head_node())) { constexpr auto tol = 100. * std::numeric_limits::epsilon(); @@ -219,9 +232,12 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, auto const offset = 0.1; auto const min = 0.0; auto const r_off = dist - offset; - auto const cut = r_off + 1e-3; // LJ for only 2 pairs: AA BB - lennard_jones_set_params(type_a, type_b, eps, sig, cut, shift, offset, min); - lennard_jones_set_params(type_b, type_b, eps, sig, cut, shift, offset, min); + auto const cut = r_off + 1e-3; // LJ for only 2 pairs: AB BB + make_particle_type_exist(std::max(type_a, type_b)); + auto const key_ab = get_ia_param_key(type_a, type_b); + auto const key_bb = get_ia_param_key(type_b, type_b); + mpi_call_all(mpi_set_lj_local, key_ab, eps, sig, cut, offset, min, shift); + mpi_call_all(mpi_set_lj_local, key_bb, eps, sig, cut, offset, min, shift); // matrix indices and reference energy value auto const max_type = type_b + 1; diff --git a/src/core/unit_tests/Verlet_list_test.cpp b/src/core/unit_tests/Verlet_list_test.cpp index ec9f840f13d..d006d5b9439 100644 --- a/src/core/unit_tests/Verlet_list_test.cpp +++ b/src/core/unit_tests/Verlet_list_test.cpp @@ -37,6 +37,7 @@ namespace bdata = boost::unit_test::data; #include "Particle.hpp" #include "ParticleFactory.hpp" #include "communication.hpp" +#include "event.hpp" #include "integrate.hpp" #include "integrators/steepest_descent.hpp" #include "nonbonded_interactions/lj.hpp" @@ -143,8 +144,19 @@ struct : public IntegratorHelper { char const *name() const override { return "VelocityVerletNpT"; } } velocity_verlet_npt; #endif // NPT + } // namespace Testing +void mpi_set_lj_local(int key, double eps, double sig, double cut, + double offset, double min, double shift) { + LJ_Parameters lj{eps, sig, cut, offset, min, shift}; + ::nonbonded_ia_params[key]->lj = lj; + ::old_nonbonded_ia_params[key].lj = lj; + on_non_bonded_ia_change(); +} + +REGISTER_CALLBACK(mpi_set_lj_local) + inline double get_dist_from_last_verlet_update(Particle const &p) { return (p.pos() - p.pos_at_last_verlet_update()).norm(); } @@ -188,7 +200,9 @@ BOOST_DATA_TEST_CASE_F(ParticleFactory, verlet_list_update, auto const min = 0.0; auto const r_off = dist - offset; auto const cut = r_off + 1e-3; - lennard_jones_set_params(0, 1, eps, sig, cut, shift, offset, min); + make_particle_type_exist(1); + auto const key = get_ia_param_key(0, 1); + mpi_call_all(mpi_set_lj_local, key, eps, sig, cut, offset, min, shift); // set up velocity-Verlet integrator auto const time_step = 0.01; diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index d96572265a2..6fd9f585b22 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -39,14 +39,6 @@ cdef extern from "TabulatedPotential.hpp": vector[double] force_tab cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": - cdef struct LJ_Parameters: - double eps - double sig - double cut - double shift - double offset - double min - cdef struct LJGen_Parameters: double eps double sig @@ -147,7 +139,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": double pref cdef struct IA_parameters: - LJ_Parameters lj LJcos_Parameters ljcos @@ -185,13 +176,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": cdef void ia_params_set_state(string) cdef void reset_ia_params() -IF LENNARD_JONES: - cdef extern from "nonbonded_interactions/lj.hpp": - cdef int lennard_jones_set_params(int part_type_a, int part_type_b, - double eps, double sig, double cut, - double shift, double offset, - double min) - IF LJCOS: cdef extern from "nonbonded_interactions/ljcos.hpp": cdef int ljcos_set_params(int part_type_a, int part_type_b, diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 1521b3218a9..2bb2d5aff61 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -286,48 +286,21 @@ class NewNonBondedInteraction(ScriptInterfaceHelper): IF LENNARD_JONES == 1: - cdef class LennardJonesInteraction(NonBondedInteraction): - - def validate_params(self): - """Check that parameters are valid. - - Raises - ------ - ValueError - If not true. - """ - if self._params["epsilon"] < 0: - raise ValueError("Lennard-Jones epsilon has to be >=0") - if self._params["sigma"] < 0: - raise ValueError("Lennard-Jones sigma has to be >=0") - if self._params["cutoff"] < 0: - raise ValueError("Lennard-Jones cutoff has to be >=0") - - def _get_params_from_es_core(self): - cdef IA_parameters * ia_params - ia_params = get_ia_param_safe( - self._part_types[0], - self._part_types[1]) - return { - "epsilon": ia_params.lj.eps, - "sigma": ia_params.lj.sig, - "cutoff": ia_params.lj.cut, - "shift": ia_params.lj.shift, - "offset": ia_params.lj.offset, - "min": ia_params.lj.min} - - def is_active(self): - """Check if interaction is active. - - """ - return (self._params["epsilon"] > 0) + @script_interface_register + class LennardJonesInteraction(NewNonBondedInteraction): + """ + Standard 6-12 Lennard-Jones potential. - def set_params(self, **kwargs): - """Set parameters for the Lennard-Jones interaction. + Methods + ------- + set_params() + Set or update parameters for the interaction. + Parameters marked as required become optional once the + interaction has been activated for the first time; + subsequent calls to this method update the existing values. Parameters ---------- - epsilon : :obj:`float` Magnitude of the interaction. sigma : :obj:`float` @@ -343,25 +316,15 @@ IF LENNARD_JONES == 1: min : :obj:`float`, optional Restricts the interaction to a minimal distance. - """ - super().set_params(**kwargs) + """ - def _set_params_in_es_core(self): - # Handle the case of shift="auto" - if self._params["shift"] == "auto": - self._params["shift"] = -(( - self._params["sigma"] / self._params["cutoff"])**12 - ( - self._params["sigma"] / self._params["cutoff"])**6) - - if lennard_jones_set_params( - self._part_types[0], self._part_types[1], - self._params["epsilon"], - self._params["sigma"], - self._params["cutoff"], - self._params["shift"], - self._params["offset"], - self._params["min"]): - raise Exception("Could not set Lennard-Jones parameters") + _so_name = "Interactions::InteractionLJ" + + def is_active(self): + """Check if interaction is active. + + """ + return self.epsilon > 0. def default_params(self): """Python dictionary of default parameters. @@ -1614,8 +1577,6 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): super().__init__(_types=[_type1, _type2], **kwargs) # Here, add one line for each nonbonded ia - IF LENNARD_JONES: - self.lennard_jones = LennardJonesInteraction(_type1, _type2) IF SOFT_SPHERE: self.soft_sphere = SoftSphereInteraction(_type1, _type2) IF LENNARD_JONES_GENERIC: diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index aff6d6de50d..7b961cfa2ad 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -159,6 +159,43 @@ class InteractionWCA : public InteractionPotentialInterface<::WCA_Parameters> { }; #endif // WCA +#ifdef LENNARD_JONES +class InteractionLJ : public InteractionPotentialInterface<::LJ_Parameters> { +protected: + CoreInteraction IA_parameters::*get_ptr_offset() const override { + return &::IA_parameters::lj; + } + +public: + InteractionLJ() { + add_parameters({ + make_autoparameter(&CoreInteraction::eps, "epsilon"), + make_autoparameter(&CoreInteraction::sig, "sigma"), + make_autoparameter(&CoreInteraction::cut, "cutoff"), + make_autoparameter(&CoreInteraction::shift, "shift"), + make_autoparameter(&CoreInteraction::offset, "offset"), + make_autoparameter(&CoreInteraction::min, "min"), + }); + } + + void make_new_instance(VariantMap const ¶ms) override { + if (auto const *shift = boost::get(¶ms.at("shift"))) { + if (*shift != "auto") { + throw std::invalid_argument( + "LJ parameter 'shift' has to be 'auto' or a float"); + } + m_ia_si = make_shared_from_args( + params, "epsilon", "sigma", "cutoff", "offset", "min"); + } else { + m_ia_si = make_shared_from_args( + params, "epsilon", "sigma", "cutoff", "offset", "min", "shift"); + } + } +}; +#endif // LENNARD_JONES + class NonBondedInteractionHandle : public AutoParameters { std::array m_types = {-1, -1}; @@ -166,6 +203,9 @@ class NonBondedInteractionHandle #ifdef WCA std::shared_ptr m_wca; #endif +#ifdef LENNARD_JONES + std::shared_ptr m_lj; +#endif template auto make_autoparameter(std::shared_ptr &member, const char *key) const { @@ -186,6 +226,9 @@ class NonBondedInteractionHandle add_parameters({ #ifdef WCA make_autoparameter(m_wca, "wca"), +#endif +#ifdef LENNARD_JONES + make_autoparameter(m_lj, "lennard_jones"), #endif }); } @@ -228,6 +271,10 @@ class NonBondedInteractionHandle #ifdef WCA set_member(m_wca, "wca", "Interactions::InteractionWCA", params); +#endif +#ifdef LENNARD_JONES + set_member(m_lj, "lennard_jones", + "Interactions::InteractionLJ", params); #endif } diff --git a/src/script_interface/interactions/initialize.cpp b/src/script_interface/interactions/initialize.cpp index 08fd863cbc3..03bba64c9ea 100644 --- a/src/script_interface/interactions/initialize.cpp +++ b/src/script_interface/interactions/initialize.cpp @@ -56,6 +56,9 @@ void initialize(Utils::Factory *om) { "Interactions::NonBondedInteractions"); om->register_new( "Interactions::NonBondedInteractionHandle"); +#ifdef LENNARD_JONES + om->register_new("Interactions::InteractionLJ"); +#endif #ifdef WCA om->register_new("Interactions::InteractionWCA"); #endif diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index 8a60fb48e02..82a45ab8e50 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -209,7 +209,7 @@ def test_exceptions(self): 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.) - with self.assertRaisesRegex(ValueError, "Parameter 'shift' has to be 'auto' or a float"): + with self.assertRaisesRegex(ValueError, "LJ parameter 'shift' has to be 'auto' or a float"): self.system.non_bonded_inter[0, 0].lennard_jones.set_params( epsilon=1., sigma=2., cutoff=3., shift="automatic") @@ -240,6 +240,13 @@ def test_wca_exceptions(self): {"epsilon": 1., "sigma": 1.}, ("epsilon", "sigma")) + @utx.skipIfMissingFeatures("LENNARD_JONES") + def test_lj_exceptions(self): + self.check_potential_exceptions( + espressomd.interactions.LennardJonesInteraction, + {"epsilon": 1., "sigma": 1., "cutoff": 1.5, "shift": 0.2}, + ("epsilon", "sigma")) + if __name__ == "__main__": ut.main() From d4b41071d1d7c4994a8517659c2da10de9fdc33d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 26 Aug 2022 15:22:54 +0200 Subject: [PATCH 42/85] core: Include what you use Add missing header files for builds with the empty myconfig file. Re-order header include statements based on which ESPResSo modules they originate from. --- src/core/io/writer/h5md_core.cpp | 3 ++- src/core/p3m/common.hpp | 5 +++-- src/core/particle_data.cpp | 3 ++- src/core/pressure.cpp | 9 ++++----- src/core/unit_tests/EspressoSystemInterface_test.cpp | 3 ++- src/core/unit_tests/energy_test.cpp | 2 +- src/utils/include/utils/u32_to_u64.hpp | 2 +- 7 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/core/io/writer/h5md_core.cpp b/src/core/io/writer/h5md_core.cpp index 2e2c69cbce6..aaed608bee4 100644 --- a/src/core/io/writer/h5md_core.cpp +++ b/src/core/io/writer/h5md_core.cpp @@ -23,10 +23,11 @@ #include "BoxGeometry.hpp" #include "Particle.hpp" -#include "config/version.hpp" #include "h5md_specification.hpp" #include "lees_edwards/LeesEdwardsBC.hpp" +#include "config/version.hpp" + #include #include diff --git a/src/core/p3m/common.hpp b/src/core/p3m/common.hpp index bea6f359fa8..e912f8d75b9 100644 --- a/src/core/p3m/common.hpp +++ b/src/core/p3m/common.hpp @@ -39,6 +39,9 @@ #include +#include +#include + /** This value indicates metallic boundary conditions. */ auto constexpr P3M_EPSILON_METALLIC = 0.0; @@ -46,10 +49,8 @@ auto constexpr P3M_EPSILON_METALLIC = 0.0; #include "LocalBox.hpp" -#include #include #include -#include namespace detail { /** @brief Index helpers for direct and reciprocal space. diff --git a/src/core/particle_data.cpp b/src/core/particle_data.cpp index f2dcfbabf87..8b357accf61 100644 --- a/src/core/particle_data.cpp +++ b/src/core/particle_data.cpp @@ -24,7 +24,6 @@ #include "Particle.hpp" #include "cells.hpp" #include "communication.hpp" -#include "config/config.hpp" #include "event.hpp" #include "exclusions.hpp" #include "nonbonded_interactions/nonbonded_interaction_data.hpp" @@ -32,6 +31,8 @@ #include "particle_node.hpp" #include "rotation.hpp" +#include "config/config.hpp" + #include #include #include diff --git a/src/core/pressure.cpp b/src/core/pressure.cpp index aa156f78858..3fa348ea420 100644 --- a/src/core/pressure.cpp +++ b/src/core/pressure.cpp @@ -29,18 +29,17 @@ #include "bonded_interactions/bonded_interaction_data.hpp" #include "cells.hpp" #include "communication.hpp" -#include "config/config.hpp" +#include "electrostatics/coulomb.hpp" #include "event.hpp" #include "grid.hpp" #include "interactions.hpp" +#include "magnetostatics/dipoles.hpp" #include "nonbonded_interactions/nonbonded_interaction_data.hpp" #include "pressure_inline.hpp" -#include "virtual_sites.hpp" - #include "short_range_loop.hpp" +#include "virtual_sites.hpp" -#include "electrostatics/coulomb.hpp" -#include "magnetostatics/dipoles.hpp" +#include "config/config.hpp" #include #include diff --git a/src/core/unit_tests/EspressoSystemInterface_test.cpp b/src/core/unit_tests/EspressoSystemInterface_test.cpp index 52c34ff0eeb..5f7f4d09220 100644 --- a/src/core/unit_tests/EspressoSystemInterface_test.cpp +++ b/src/core/unit_tests/EspressoSystemInterface_test.cpp @@ -25,12 +25,13 @@ #include "EspressoSystemInterface.hpp" #include "communication.hpp" -#include "config/config.hpp" #include "particle_data.hpp" #include "particle_node.hpp" #include "virtual_sites.hpp" #include "virtual_sites/VirtualSitesOff.hpp" +#include "config/config.hpp" + #include #include diff --git a/src/core/unit_tests/energy_test.cpp b/src/core/unit_tests/energy_test.cpp index 218687f97f9..f04aa0ace31 100644 --- a/src/core/unit_tests/energy_test.cpp +++ b/src/core/unit_tests/energy_test.cpp @@ -24,7 +24,7 @@ #include "Particle.hpp" #include "energy_inline.hpp" -#include "utils/Vector.hpp" +#include BOOST_AUTO_TEST_CASE(translational_kinetic_energy_) { // real particle diff --git a/src/utils/include/utils/u32_to_u64.hpp b/src/utils/include/utils/u32_to_u64.hpp index f33b2f0c21c..dd0892bfdda 100644 --- a/src/utils/include/utils/u32_to_u64.hpp +++ b/src/utils/include/utils/u32_to_u64.hpp @@ -19,7 +19,7 @@ #ifndef UTILS_U32_TO_U64_HPP #define UTILS_U32_TO_U64_HPP -#include +#include #include namespace Utils { From 63a3389e41f8ab039255a276f2b944e12550df4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 30 Aug 2022 20:51:25 +0200 Subject: [PATCH 43/85] core: Cleanup interface code Fix small regressions (wrong CRTP, incorrect function argument types) and use more expressive variable names. --- src/core/analysis/statistics.cpp | 4 ++-- src/core/analysis/statistics.hpp | 9 ++++---- src/core/constraints/Constraints.hpp | 22 +++++++++---------- src/core/magnetostatics/dds_gpu_cuda.cu | 2 +- .../electrostatics/ICCStar.hpp | 2 +- src/shapes/include/shapes/Union.hpp | 14 ++++++------ src/utils/include/utils/u32_to_u64.hpp | 2 +- 7 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/core/analysis/statistics.cpp b/src/core/analysis/statistics.cpp index 683684eaaa0..3db0b719511 100644 --- a/src/core/analysis/statistics.cpp +++ b/src/core/analysis/statistics.cpp @@ -87,8 +87,8 @@ static Utils::Vector3d mpi_particle_momentum_local() { REGISTER_CALLBACK_REDUCTION(mpi_particle_momentum_local, std::plus()) -Utils::Vector3d calc_linear_momentum(int include_particles, - int include_lbfluid) { +Utils::Vector3d calc_linear_momentum(bool include_particles, + bool include_lbfluid) { Utils::Vector3d linear_momentum{}; if (include_particles) { linear_momentum += diff --git a/src/core/analysis/statistics.hpp b/src/core/analysis/statistics.hpp index d86e7ca1ff4..b8d5e70076e 100644 --- a/src/core/analysis/statistics.hpp +++ b/src/core/analysis/statistics.hpp @@ -116,11 +116,10 @@ Utils::Vector3d angular_momentum(PartCfg &partCfg, int p_type); Utils::Vector9d moment_of_inertia_matrix(PartCfg &partCfg, int p_type); /** Calculate total momentum of the system (particles & LB fluid). - * Inputs are bools to include particles and fluid in the linear momentum - * calculation - * @return Result for this processor + * @param include_particles Add particles momentum + * @param include_lbfluid Add LB fluid momentum */ -Utils::Vector3d calc_linear_momentum(int include_particles, - int include_lbfluid); +Utils::Vector3d calc_linear_momentum(bool include_particles, + bool include_lbfluid); #endif diff --git a/src/core/constraints/Constraints.hpp b/src/core/constraints/Constraints.hpp index 11182f8f8c7..bfc9ca93178 100644 --- a/src/core/constraints/Constraints.hpp +++ b/src/core/constraints/Constraints.hpp @@ -41,29 +41,29 @@ template class Constraints { private: void reset_forces() const { - for (auto const &c : *this) { - c->reset_force(); + for (auto const &constraint : *this) { + constraint->reset_force(); } } container_type m_constraints; public: - void add(std::shared_ptr const &c) { - if (not c->fits_in_box(box_geo.length())) { + void add(std::shared_ptr const &constraint) { + if (not constraint->fits_in_box(box_geo.length())) { throw std::runtime_error("Constraint not compatible with box size."); } - assert(std::find(m_constraints.begin(), m_constraints.end(), c) == + assert(std::find(m_constraints.begin(), m_constraints.end(), constraint) == m_constraints.end()); - m_constraints.emplace_back(c); + m_constraints.emplace_back(constraint); on_constraint_change(); } - void remove(std::shared_ptr const &c) { - assert(std::find(m_constraints.begin(), m_constraints.end(), c) != + void remove(std::shared_ptr const &constraint) { + assert(std::find(m_constraints.begin(), m_constraints.end(), constraint) != m_constraints.end()); m_constraints.erase( - std::remove(m_constraints.begin(), m_constraints.end(), c), + std::remove(m_constraints.begin(), m_constraints.end(), constraint), m_constraints.end()); on_constraint_change(); } @@ -82,8 +82,8 @@ template class Constraints { for (auto &p : particles) { auto const pos = folded_position(p.pos(), box_geo); ParticleForce force{}; - for (auto const &c : *this) { - force += c->force(p, pos, t); + for (auto const &constraint : *this) { + force += constraint->force(p, pos, t); } p.f += force; diff --git a/src/core/magnetostatics/dds_gpu_cuda.cu b/src/core/magnetostatics/dds_gpu_cuda.cu index 2f3f063fe05..30812497fc3 100644 --- a/src/core/magnetostatics/dds_gpu_cuda.cu +++ b/src/core/magnetostatics/dds_gpu_cuda.cu @@ -152,7 +152,7 @@ __global__ void DipolarDirectSum_kernel_force(float pf, unsigned int n, // There is one thread per particle. Each thread computes interactions // with particles whose id is smaller than the thread id. // The force and torque of all the interaction partners of the current thread - // is atomically added to global results ad once. + // is atomically added to global results at once. // The result for the particle id equal to the thread id is atomically added // to global memory at the end. diff --git a/src/script_interface/electrostatics/ICCStar.hpp b/src/script_interface/electrostatics/ICCStar.hpp index 0ddbbe85c88..998d9e43238 100644 --- a/src/script_interface/electrostatics/ICCStar.hpp +++ b/src/script_interface/electrostatics/ICCStar.hpp @@ -41,7 +41,7 @@ namespace ScriptInterface { namespace Coulomb { -class ICCStar : public AutoParameters<::ICCStar> { +class ICCStar : public AutoParameters { using CoreActorClass = ::ICCStar; std::shared_ptr m_actor; diff --git a/src/shapes/include/shapes/Union.hpp b/src/shapes/include/shapes/Union.hpp index 55c5e5b85bc..adc62e51059 100644 --- a/src/shapes/include/shapes/Union.hpp +++ b/src/shapes/include/shapes/Union.hpp @@ -35,12 +35,12 @@ namespace Shapes { class Union : public Shape { public: - void add(std::shared_ptr const &s) { - m_shapes.emplace_back(s); + void add(std::shared_ptr const &shape) { + m_shapes.emplace_back(shape); } - void remove(std::shared_ptr const &s) { - m_shapes.erase(std::remove(m_shapes.begin(), m_shapes.end(), s), + void remove(std::shared_ptr const &shape) { + m_shapes.erase(std::remove(m_shapes.begin(), m_shapes.end(), shape), m_shapes.end()); } @@ -55,10 +55,10 @@ class Union : public Shape { void calculate_dist(Utils::Vector3d const &pos, double &dist, Utils::Vector3d &vec) const override { auto dist_compare = [&pos](std::pair const &res, - std::shared_ptr const &s) { + std::shared_ptr const &shape) { double d; Utils::Vector3d vec; - (*s).calculate_dist(pos, d, vec); + shape->calculate_dist(pos, d, vec); if (d < 0.0) throw std::domain_error( "Distance to Union not well-defined for given position!"); @@ -76,7 +76,7 @@ class Union : public Shape { bool is_inside(Utils::Vector3d const &pos) const override { return boost::algorithm::any_of( - m_shapes, [&pos](auto const &s) { return s->is_inside(pos); }); + m_shapes, [&pos](auto const &shape) { return shape->is_inside(pos); }); } private: diff --git a/src/utils/include/utils/u32_to_u64.hpp b/src/utils/include/utils/u32_to_u64.hpp index dd0892bfdda..f35d2369124 100644 --- a/src/utils/include/utils/u32_to_u64.hpp +++ b/src/utils/include/utils/u32_to_u64.hpp @@ -33,4 +33,4 @@ constexpr inline std::pair u64_to_u32(uint64_t in) { } // namespace Utils -#endif // ESPRESSO_U32_TO_U64_HPP +#endif From 9e3594d9090eb1d5d33f079a6e8769b28e6d4164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 31 Aug 2022 19:18:09 +0200 Subject: [PATCH 44/85] tests: Improve testing Fix various regressions, improve docstrings and reduce runtime of slow tests. --- .../tests/GlobalContext_test.cpp | 3 +- .../tests/LocalContext_test.cpp | 3 +- src/utils/tests/gatherv_test.cpp | 34 ++++++++----------- src/utils/tests/serialization_test.cpp | 19 +++++++++-- src/utils/tests/u32_to_u64_test.cpp | 2 +- testsuite/python/dawaanr-and-bh-gpu.py | 2 +- testsuite/python/dawaanr-and-dds-gpu.py | 2 +- testsuite/python/dds-and-bh-gpu.py | 5 ++- .../dipolar_mdlc_p3m_scafacos_p2nfft.py | 2 +- testsuite/python/integrator_npt.py | 2 +- testsuite/python/mmm1d.py | 15 +++----- testsuite/python/p3m_madelung.py | 19 ++++++----- testsuite/python/random_pairs.py | 33 +++++++++++------- testsuite/python/test_checkpoint.py | 1 + 14 files changed, 81 insertions(+), 61 deletions(-) diff --git a/src/script_interface/tests/GlobalContext_test.cpp b/src/script_interface/tests/GlobalContext_test.cpp index 19bee7ff1f0..b5556b2d18e 100644 --- a/src/script_interface/tests/GlobalContext_test.cpp +++ b/src/script_interface/tests/GlobalContext_test.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -49,7 +50,7 @@ struct Dummy : si::ObjectHandle { static const boost::string_ref parameter_names[] = {"id", "object_param"}; return Utils::make_const_span(parameter_names, - std::min(params.size(), 2lu)); + std::min(params.size(), std::size_t{2u})); } }; diff --git a/src/script_interface/tests/LocalContext_test.cpp b/src/script_interface/tests/LocalContext_test.cpp index 60a8ed88df8..9122ffa0fee 100644 --- a/src/script_interface/tests/LocalContext_test.cpp +++ b/src/script_interface/tests/LocalContext_test.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -49,7 +50,7 @@ struct Dummy : si::ObjectHandle { static const boost::string_ref parameter_names[] = {"id", "object_param"}; return Utils::make_const_span(parameter_names, - std::min(params.size(), 2lu)); + std::min(params.size(), std::size_t{2u})); } }; diff --git a/src/utils/tests/gatherv_test.cpp b/src/utils/tests/gatherv_test.cpp index 5244feb17e3..96ca48925fb 100644 --- a/src/utils/tests/gatherv_test.cpp +++ b/src/utils/tests/gatherv_test.cpp @@ -30,20 +30,16 @@ #include #include -using Utils::Mpi::gatherv; - -namespace mpi = boost::mpi; - /* * Check that implementation behaves - * like MPI_Gatherv with an mpi datatype. - * To test this we gather the rank from + * like @c MPI_Gatherv with an mpi datatype. + * To test this, we gather the rank from * every rank to one rank and then check * that the value was written to the * correct position in the output array. */ BOOST_AUTO_TEST_CASE(mpi_type) { - mpi::communicator world; + boost::mpi::communicator world; auto const rank = world.rank(); auto const size = world.size(); auto const root = world.size() - 1; @@ -54,10 +50,10 @@ BOOST_AUTO_TEST_CASE(mpi_type) { std::vector out(size, -1); std::vector sizes(size, 1); - gatherv(world, &rank, 1, out.data(), sizes.data(), root); + Utils::Mpi::gatherv(world, &rank, 1, out.data(), sizes.data(), root); for (int i = 0; i < size; i++) { - BOOST_CHECK_EQUAL(i, out.at(i)); + BOOST_CHECK_EQUAL(out.at(i), i); } } else { Utils::Mpi::gatherv(world, &rank, 1, root); @@ -71,10 +67,10 @@ BOOST_AUTO_TEST_CASE(mpi_type) { out[rank] = rank; std::vector sizes(size, 1); - gatherv(world, out.data(), 1, out.data(), sizes.data(), root); + Utils::Mpi::gatherv(world, out.data(), 1, out.data(), sizes.data(), root); for (int i = 0; i < size; i++) { - BOOST_CHECK_EQUAL(i, out.at(i)); + BOOST_CHECK_EQUAL(out.at(i), i); } } else { Utils::Mpi::gatherv(world, &rank, 1, root); @@ -84,14 +80,14 @@ BOOST_AUTO_TEST_CASE(mpi_type) { /* * Check that implementation behaves - * like MPI_Gatherv with an non-mpi datatype. - * To test this we gather a string containing the rank from + * like @c MPI_Gatherv with a non-mpi datatype. + * To test this, we gather a string containing the rank from * every rank to one rank and then check * that the value was written to the * correct position in the output array. */ BOOST_AUTO_TEST_CASE(non_mpi_type) { - mpi::communicator world; + boost::mpi::communicator world; auto const rank = world.rank(); auto const size = world.size(); auto const root = world.size() - 1; @@ -103,10 +99,10 @@ BOOST_AUTO_TEST_CASE(non_mpi_type) { std::vector out(size); std::vector sizes(size, 1); - gatherv(world, &in, 1, out.data(), sizes.data(), root); + Utils::Mpi::gatherv(world, &in, 1, out.data(), sizes.data(), root); for (int i = 0; i < size; i++) { - BOOST_CHECK_EQUAL(std::to_string(i), out.at(i)); + BOOST_CHECK_EQUAL(out.at(i), std::to_string(i)); } } else { Utils::Mpi::gatherv(world, &in, 1, root); @@ -120,10 +116,10 @@ BOOST_AUTO_TEST_CASE(non_mpi_type) { out[rank] = in; std::vector sizes(size, 1); - gatherv(world, out.data(), 1, out.data(), sizes.data(), root); + Utils::Mpi::gatherv(world, out.data(), 1, out.data(), sizes.data(), root); for (int i = 0; i < size; i++) { - BOOST_CHECK_EQUAL(std::to_string(i), out.at(i)); + BOOST_CHECK_EQUAL(out.at(i), std::to_string(i)); } } else { Utils::Mpi::gatherv(world, &in, 1, root); @@ -132,7 +128,7 @@ BOOST_AUTO_TEST_CASE(non_mpi_type) { } int main(int argc, char **argv) { - mpi::environment mpi_env(argc, 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/serialization_test.cpp b/src/utils/tests/serialization_test.cpp index 67fe91d5fb4..6f9dad3630e 100644 --- a/src/utils/tests/serialization_test.cpp +++ b/src/utils/tests/serialization_test.cpp @@ -208,11 +208,23 @@ auto sorted_view(InputIt const &buffer_it) { return subset; } +/** + * @brief Simplistic test for endianness. + * Replace with @c std::endian once ESPResSo becomes a C++20 project. + */ +bool is_big_endian() { +#ifdef __BYTE_ORDER__ + return __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__; +#else + return false; +#endif +} + BOOST_AUTO_TEST_CASE(serialization_level_test) { boost::mpi::communicator comm; auto const buffer = create_mpi_archive(comm); - /* Serialization should produce the following bytestring: + /* Serialization should produce the following bytestring (little-endian): * 3 0 0 0 0 0 0 0 * 4 0 0 0 * 5 0 0 0 @@ -267,7 +279,10 @@ BOOST_AUTO_TEST_CASE(mpi_archive_test) { BOOST_TEST(buffer_vector == buffer_ref, boost::test_tools::per_element()); BOOST_TEST(buffer_storage == buffer_ref, boost::test_tools::per_element()); BOOST_TEST(buffer_quat == buffer_ref, boost::test_tools::per_element()); - BOOST_TEST(buffer_cv[0] == Testing::N); + auto const index_lsb = (is_big_endian()) ? 1 : 0; + auto const index_hsb = (is_big_endian()) ? 0 : 1; + BOOST_TEST(buffer_cv[index_lsb] == Testing::N); + BOOST_TEST(buffer_cv[index_hsb] == 0); buffer_cv.erase(buffer_cv.begin()); buffer_cv.erase(buffer_cv.begin()); BOOST_TEST(buffer_cv == buffer_ref, boost::test_tools::per_element()); diff --git a/src/utils/tests/u32_to_u64_test.cpp b/src/utils/tests/u32_to_u64_test.cpp index 5e7c0bdf3a1..8daaeee7e70 100644 --- a/src/utils/tests/u32_to_u64_test.cpp +++ b/src/utils/tests/u32_to_u64_test.cpp @@ -25,7 +25,7 @@ #include BOOST_AUTO_TEST_CASE(u32_to_u64) { - constexpr const uint64_t expected = (4ul << 32) | (11ul); + constexpr const uint64_t expected = (uint64_t{4ul} << 32) | (11ul); BOOST_CHECK(expected == Utils::u32_to_u64(4u, 11u)); } diff --git a/testsuite/python/dawaanr-and-bh-gpu.py b/testsuite/python/dawaanr-and-bh-gpu.py index 5f7d0fbacd3..885ce47df99 100644 --- a/testsuite/python/dawaanr-and-bh-gpu.py +++ b/testsuite/python/dawaanr-and-bh-gpu.py @@ -49,7 +49,7 @@ def test(self): pf_dawaanr = 3.524 ratio_dawaanr_bh_gpu = pf_dawaanr / pf_bh_gpu system = self.system - system.box_l = 3 * [15] + system.box_l = [15., 15., 15.] system.periodicity = [False, False, False] system.time_step = 1E-4 system.cell_system.skin = 0.1 diff --git a/testsuite/python/dawaanr-and-dds-gpu.py b/testsuite/python/dawaanr-and-dds-gpu.py index 37f386bbc8d..73d36707e94 100644 --- a/testsuite/python/dawaanr-and-dds-gpu.py +++ b/testsuite/python/dawaanr-and-dds-gpu.py @@ -41,7 +41,7 @@ def test(self): pf_dds_gpu = 2.34 pf_dawaanr = 3.524 ratio_dawaanr_dds_gpu = pf_dawaanr / pf_dds_gpu - self.system.box_l = 3 * [15] + self.system.box_l = [15., 15., 15.] self.system.periodicity = [False, False, False] self.system.time_step = 1E-4 self.system.cell_system.skin = 0.1 diff --git a/testsuite/python/dds-and-bh-gpu.py b/testsuite/python/dds-and-bh-gpu.py index 4f3b19a8d10..6179b6d7ce1 100644 --- a/testsuite/python/dds-and-bh-gpu.py +++ b/testsuite/python/dds-and-bh-gpu.py @@ -45,9 +45,8 @@ def test(self): pf_bh_gpu = 2.34 pf_dds_gpu = 3.524 ratio_dawaanr_bh_gpu = pf_dds_gpu / pf_bh_gpu - l = 15 system = self.system - system.box_l = 3 * [l] + system.box_l = [15., 15., 15.] system.periodicity = 3 * [False] system.time_step = 1E-4 system.cell_system.skin = 0.1 @@ -56,7 +55,7 @@ def test(self): for n in [128, 541]: dipole_modulus = 1.3 - part_pos = np.random.random((n, 3)) * l + part_pos = np.random.random((n, 3)) * system.box_l[0] part_dip = dipole_modulus * tests_common.random_dipoles(n) system.part.add(pos=part_pos, dip=part_dip, v=n * [(0, 0, 0)], omega_body=n * [(0, 0, 0)]) diff --git a/testsuite/python/dipolar_mdlc_p3m_scafacos_p2nfft.py b/testsuite/python/dipolar_mdlc_p3m_scafacos_p2nfft.py index c262c71e57a..4d82afaceda 100644 --- a/testsuite/python/dipolar_mdlc_p3m_scafacos_p2nfft.py +++ b/testsuite/python/dipolar_mdlc_p3m_scafacos_p2nfft.py @@ -33,7 +33,7 @@ @utx.skipIfMissingFeatures(["DIPOLES", "FFTW"]) -class Dipolar_p3m_mdlc_p2nfft(ut.TestCase): +class Test(ut.TestCase): """Tests mdlc (2d) as well as dipolar p3m and dipolar p2nfft (3d) against stored data. Validity of the stored data: diff --git a/testsuite/python/integrator_npt.py b/testsuite/python/integrator_npt.py index 232e858d861..3007308a705 100644 --- a/testsuite/python/integrator_npt.py +++ b/testsuite/python/integrator_npt.py @@ -71,7 +71,7 @@ def test_00_integrator_recovery(self): # get the equilibrium box length for the chosen NpT parameters system.integrator.run(500) # catch unstable simulation early (when the DP3M test case ran first) - assert system.box_l[0] < 20. + assert system.box_l[0] < 20., "NpT simulation is unstable" system.integrator.run(1500) box_l_ref = system.box_l[0] diff --git a/testsuite/python/mmm1d.py b/testsuite/python/mmm1d.py index bb69adfc257..a8f3b7b7c42 100644 --- a/testsuite/python/mmm1d.py +++ b/testsuite/python/mmm1d.py @@ -111,14 +111,10 @@ def test_bjerrum_length_change(self): def test_infinite_wire(self): """ For an infinite wire, the energy per ion is :math:`MC\\frac{q}{a}` - with :math:`M = - \\ln{2}` the 1D Madelung constant, :math:`C` + with :math:`M = - 2\\ln{2}` the 1D Madelung constant, :math:`C` the electrostatics prefactor, :math:`q` the ion charge and - :math:`a` the lattice constant. Likewise, the pressure for - one ion can be derived as :math:`MC\\frac{q}{aV}` with - :math:`V` the simulation box volume. For more details, see - Orion Ciftja, "Equivalence of an infinite one-dimensional ionic - crystal to a simple electrostatic model", Results in Physics, - Volume 13, 2019, 102325, doi:10.1016/j.rinp.2019.102325 + :math:`a` the lattice constant. See user guide sections + :ref:`Madelung electrostatics` for more details. """ n_pairs = 128 n_part = 2 * n_pairs @@ -131,9 +127,8 @@ def test_infinite_wire(self): energy = self.system.analysis.energy()["coulomb"] p_scalar = self.system.analysis.pressure()["coulomb"] p_tensor = self.system.analysis.pressure_tensor()["coulomb"] - ref_energy = -np.log(2.) - np.testing.assert_allclose(energy / n_part, ref_energy, - atol=0., rtol=5e-7) + ref_energy = -np.log(2.) * n_part + np.testing.assert_allclose(energy, ref_energy, atol=0., rtol=5e-7) np.testing.assert_allclose(p_scalar, 0., atol=1e-12) np.testing.assert_allclose(p_tensor, 0., atol=1e-12) diff --git a/testsuite/python/p3m_madelung.py b/testsuite/python/p3m_madelung.py index 0c795ba95d3..de5bf298088 100644 --- a/testsuite/python/p3m_madelung.py +++ b/testsuite/python/p3m_madelung.py @@ -79,7 +79,7 @@ def test_infinite_magnetic_wire(self): self.system.part.add(pos=spacing * pos, dip=[1., 0., 0.]) dp3m_params = dict(prefactor=1., accuracy=1e-9, mesh=48, r_cut=28., - cao=6, tuning=False) + cao=6, alpha=0.137582516, tune=False) mdlc_params = {'maxPWerror': 1e-9, 'gap_size': 16.} def check(): @@ -123,7 +123,7 @@ def test_infinite_magnetic_sheet(self): self.system.part.add(pos=spacing * pos, dip=[0., 0., 1.]) dp3m_params = dict(prefactor=1., accuracy=1e-9, mesh=48, r_cut=33.6, - cao=7, tuning=False) + cao=7, alpha=0.1186918, tune=False) mdlc_params = {'maxPWerror': 1e-9, 'gap_size': 16.} def check(): @@ -167,7 +167,8 @@ def test_infinite_magnetic_cube(self): self.system.part.add(pos=spacing * pos, dip=[0., 0., 1.]) dp3m_params = dict( - prefactor=1., accuracy=5e-8, mesh=64, r_cut=8.2, cao=7, tuning=False) + prefactor=1., accuracy=5e-8, mesh=64, r_cut=8.2, cao=7, + alpha=0.5091135, tune=False) def check(): # minimal energy configuration @@ -198,7 +199,7 @@ def test_infinite_ionic_wire(self): base = [0., 0., 1.] ref_energy, ref_pressure = self.get_reference_obs_per_ion(mc, base) p3m_params = dict(prefactor=1., accuracy=1e-8, mesh=32, r_cut=14., - cao=7, tuning=False) + cao=7, alpha=0.27362958, tune=False) def check(): energy, p_scalar, p_tensor = self.get_normalized_obs_per_ion() @@ -228,7 +229,7 @@ def test_infinite_ionic_sheet(self): base = [1., 1., 0.] ref_energy, ref_pressure = self.get_reference_obs_per_ion(mc, base) p3m_params = dict(prefactor=1., accuracy=1e-8, mesh=48, r_cut=22., - cao=7, tuning=False) + cao=7, alpha=0.179719, tune=False) elc_params = dict(maxPWerror=1e-6, gap_size=16) def check(pressure=True): @@ -261,7 +262,7 @@ def check(pressure=True): @utx.skipIfMissingFeatures(["P3M"]) def test_infinite_ionic_cube(self): - n_pairs = 8 + n_pairs = 6 self.system.box_l = 3 * [2 * n_pairs] for j, k, l in itertools.product(range(2 * n_pairs), repeat=3): self.system.part.add(pos=[j, k, l], q=(-1)**(j + k + l)) @@ -269,8 +270,8 @@ def test_infinite_ionic_cube(self): mc = -1.74756459463318219 base = [1., 1., 1.] ref_energy, ref_pressure = self.get_reference_obs_per_ion(mc, base) - p3m_params = dict(prefactor=1., accuracy=3e-7, mesh=44, r_cut=7., cao=7, - tuning=False) + p3m_params = dict(prefactor=1., accuracy=3e-7, mesh=52, r_cut=4.4, cao=7, + alpha=0.8895166, tune=False) def check(): energy, p_scalar, p_tensor = self.get_normalized_obs_per_ion() @@ -285,6 +286,8 @@ def check(): check() if espressomd.has_features("CUDA") and espressomd.gpu_available(): self.system.actors.clear() + p3m_params = dict(prefactor=1., accuracy=3e-7, mesh=42, r_cut=5.5, + cao=7, alpha=0.709017, tune=False) p3m = espressomd.electrostatics.P3MGPU(**p3m_params) self.system.actors.add(p3m) check() diff --git a/testsuite/python/random_pairs.py b/testsuite/python/random_pairs.py index fcad71bfbf6..269b2d6ab4f 100644 --- a/testsuite/python/random_pairs.py +++ b/testsuite/python/random_pairs.py @@ -20,6 +20,7 @@ import unittest as ut import espressomd import numpy as np +import scipy.spatial import itertools import collections import tests_common @@ -38,23 +39,31 @@ class RandomPairTest(ut.TestCase): system = espressomd.System(box_l=[10., 15., 15.]) def setUp(self): - s = self.system - s.time_step = .1 - s.cell_system.skin = 0.0 - s.min_global_cut = 1.5 - n_part = 500 + system = self.system + system.time_step = .1 + system.cell_system.skin = 0.0 + system.min_global_cut = 1.5 + n_part = 400 np.random.seed(2) - s.part.add(pos=s.box_l * np.random.random((n_part, 3))) + positions = system.box_l * np.random.random((n_part, 3)) + system.part.add(pos=positions) self.all_pairs = [] - dist_func = self.system.distance - for pair in self.system.part.pairs(): - if dist_func(pair[0], pair[1]) < 1.5: - self.all_pairs.append((pair[0].id, pair[1].id)) - - self.all_pairs = set(self.all_pairs) + def euclidean_pbc(a, b, box_l): + vec = np.fmod(a - b + box_l, box_l) + for i in range(3): + if vec[i] > box_l[i] / 2.: + vec[i] -= box_l[i] + return np.linalg.norm(vec) + + dist_mat = scipy.spatial.distance.cdist( + positions, positions, metric=euclidean_pbc, + box_l=np.copy(system.box_l)) + diag_mask = np.logical_not(np.eye(n_part, dtype=bool)) + dist_crit = (dist_mat < 1.5) * diag_mask + self.all_pairs = list(zip(*np.nonzero(np.triu(dist_crit)))) self.assertGreater(len(self.all_pairs), 0) def tearDown(self): diff --git a/testsuite/python/test_checkpoint.py b/testsuite/python/test_checkpoint.py index b9f7d953ae9..17363085c3f 100644 --- a/testsuite/python/test_checkpoint.py +++ b/testsuite/python/test_checkpoint.py @@ -668,6 +668,7 @@ def test_constraints(self): if self.n_nodes == 1: union = c[7].shape self.assertIsInstance(union, espressomd.shapes.Union) + self.assertEqual(c[7].particle_type, 2) self.assertEqual(len(union), 2) wall1, wall2 = union.call_method('get_elements') self.assertIsInstance(wall1, espressomd.shapes.Wall) From 3a0512514b4dac927b274c2d35f30839f6a33931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 31 Aug 2022 19:43:22 +0200 Subject: [PATCH 45/85] core: Remove redundant feature guards --- .../constraints/HomogeneousMagneticField.cpp | 2 +- .../grid_based_algorithms/lb_boundaries.cpp | 19 ++++++++----------- src/core/observables/ParticleTraits.hpp | 2 +- .../VirtualSitesInertialessTracers.cpp | 5 ++--- .../VirtualSitesInertialessTracers.hpp | 8 ++++---- .../virtual_sites/lb_inertialess_tracers.cpp | 2 +- 6 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/core/constraints/HomogeneousMagneticField.cpp b/src/core/constraints/HomogeneousMagneticField.cpp index 05fb65ba0ef..6741ffd9652 100644 --- a/src/core/constraints/HomogeneousMagneticField.cpp +++ b/src/core/constraints/HomogeneousMagneticField.cpp @@ -28,7 +28,7 @@ namespace Constraints { ParticleForce HomogeneousMagneticField::force(const Particle &p, const Utils::Vector3d &, double) { -#if defined(ROTATION) && defined(DIPOLES) +#ifdef DIPOLES return {{}, vector_product(p.calc_dip(), m_field)}; #else return {}; diff --git a/src/core/grid_based_algorithms/lb_boundaries.cpp b/src/core/grid_based_algorithms/lb_boundaries.cpp index 818e2d2d416..6249e0dcb89 100644 --- a/src/core/grid_based_algorithms/lb_boundaries.cpp +++ b/src/core/grid_based_algorithms/lb_boundaries.cpp @@ -52,8 +52,6 @@ #include #include -using Utils::get_linear_index; - namespace LBBoundaries { std::vector> lbboundaries; @@ -87,8 +85,8 @@ bool sanity_check_mach_limit() { }); } -void ek_init_boundaries() { -#if defined(CUDA) && defined(EK_BOUNDARIES) +#if defined(EK_BOUNDARIES) +static void ek_init_boundaries() { int number_of_boundnodes = 0; std::vector host_wallcharge_species_density; @@ -164,8 +162,8 @@ void ek_init_boundaries() { ek_init_species_density_wallcharge(host_wallcharge_species_density.data(), wallcharge_species); } -#endif } +#endif // defined(EK_BOUNDARIES) /** Initialize boundary conditions for all constraints in the system. */ void lb_init_boundaries() { @@ -173,9 +171,10 @@ void lb_init_boundaries() { if (this_node != 0) { return; } -#if defined(CUDA) #if defined(LB_BOUNDARIES_GPU) +#if defined(EK_BOUNDARIES) ek_init_boundaries(); +#endif unsigned number_of_boundnodes = 0; std::vector host_boundary_node_list; std::vector host_boundary_index_list; @@ -234,9 +233,9 @@ void lb_init_boundaries() { "compiled in. Activate in myconfig.hpp."; } #endif // defined (LB_BOUNDARIES_GPU) -#endif // defined (CUDA) } else if (lattice_switch == ActiveLB::CPU) { #if defined(LB_BOUNDARIES) + using Utils::get_linear_index; boost::for_each(lbfields, [](auto &f) { f.boundary = 0; }); auto const node_pos = calc_node_pos(comm_cart); @@ -285,7 +284,6 @@ REGISTER_CALLBACK(lb_collect_boundary_forces_local) Utils::Vector3d lbboundary_get_force(LBBoundary const *lbb) { Utils::Vector3d force{}; -#if defined(LB_BOUNDARIES) || defined(LB_BOUNDARIES_GPU) auto const it = boost::find_if(lbboundaries, [lbb](std::shared_ptr const &i) { return i.get() == lbb; @@ -296,7 +294,7 @@ Utils::Vector3d lbboundary_get_force(LBBoundary const *lbb) { "system.lbboundaries."); std::vector forces(3 * lbboundaries.size()); if (lattice_switch == ActiveLB::GPU) { -#if defined(LB_BOUNDARIES_GPU) && defined(CUDA) +#if defined(LB_BOUNDARIES_GPU) lb_gpu_get_boundary_forces(forces); #endif } else if (lattice_switch == ActiveLB::CPU) { @@ -309,10 +307,9 @@ Utils::Vector3d lbboundary_get_force(LBBoundary const *lbb) { force[0] = forces[3 * container_index + 0]; force[1] = forces[3 * container_index + 1]; force[2] = forces[3 * container_index + 2]; -#endif return force; } -#endif /* LB_BOUNDARIES or LB_BOUNDARIES_GPU */ +#endif // defined(LB_BOUNDARIES) || defined(LB_BOUNDARIES_GPU) } // namespace LBBoundaries diff --git a/src/core/observables/ParticleTraits.hpp b/src/core/observables/ParticleTraits.hpp index bff9d86a1b0..e40f4a62a70 100644 --- a/src/core/observables/ParticleTraits.hpp +++ b/src/core/observables/ParticleTraits.hpp @@ -43,7 +43,7 @@ template <> struct traits { } auto charge(Particle const &p) const { return p.q(); } auto dipole_moment(Particle const &p) const { -#if defined(ROTATION) && defined(DIPOLES) +#ifdef DIPOLES return p.calc_dip(); #else return Utils::Vector3d{}; diff --git a/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp b/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp index 5c6a6e1f56d..30dd822599d 100644 --- a/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp +++ b/src/core/virtual_sites/VirtualSitesInertialessTracers.cpp @@ -27,6 +27,7 @@ #include "errorhandling.hpp" #include "grid_based_algorithms/lb_interface.hpp" #include "virtual_sites/lb_inertialess_tracers.hpp" + #include void VirtualSitesInertialessTracers::after_force_calc() { @@ -51,9 +52,7 @@ void VirtualSitesInertialessTracers::after_force_calc() { } void VirtualSitesInertialessTracers::after_lb_propagation(double time_step) { -#ifdef VIRTUAL_SITES_INERTIALESS_TRACERS IBM_UpdateParticlePositions(cell_structure.local_particles(), time_step, this_node); -#endif } -#endif +#endif // VIRTUAL_SITES_INERTIALESS_TRACERS diff --git a/src/core/virtual_sites/VirtualSitesInertialessTracers.hpp b/src/core/virtual_sites/VirtualSitesInertialessTracers.hpp index 6011f536666..92abc09d7b7 100644 --- a/src/core/virtual_sites/VirtualSitesInertialessTracers.hpp +++ b/src/core/virtual_sites/VirtualSitesInertialessTracers.hpp @@ -20,10 +20,11 @@ #define VIRTUAL_SITES_VIRTUAL_SITES_INERTIALESS_TRACERS_HPP #include "config/config.hpp" -#ifdef VIRTUAL_SITES -#include "VirtualSites.hpp" #ifdef VIRTUAL_SITES_INERTIALESS_TRACERS + +#include "VirtualSites.hpp" + /** @brief Virtual sites which are advected with an lb fluid. Forces on them are * instantaneously transferred to the fluid */ @@ -32,6 +33,5 @@ class VirtualSitesInertialessTracers : public VirtualSites { void after_lb_propagation(double time_step) override; }; -#endif -#endif +#endif // VIRTUAL_SITES_INERTIALESS_TRACERS #endif diff --git a/src/core/virtual_sites/lb_inertialess_tracers.cpp b/src/core/virtual_sites/lb_inertialess_tracers.cpp index 96b60daba01..5aae5af66c5 100644 --- a/src/core/virtual_sites/lb_inertialess_tracers.cpp +++ b/src/core/virtual_sites/lb_inertialess_tracers.cpp @@ -306,4 +306,4 @@ void ParticleVelocitiesFromLB_CPU() { } } } -#endif +#endif // VIRTUAL_SITES_INERTIALESS_TRACERS From 12b1c3a77060a0d402984ce88b230744fa31c79d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 1 Sep 2022 13:37:40 +0200 Subject: [PATCH 46/85] core: Fix broken FFT check Properly check the magnitude of complex residuals. Add a parameter to disable the check for debugging purposes. --- src/core/electrostatics/p3m.cpp | 8 +++++--- src/core/electrostatics/p3m.hpp | 3 ++- src/core/p3m/fft.cpp | 2 +- .../unit_tests/EspressoSystemStandAlone_test.cpp | 2 +- src/python/espressomd/electrostatics.py | 7 +++++++ src/script_interface/electrostatics/CoulombP3M.hpp | 6 ++++-- .../electrostatics/CoulombP3MGPU.hpp | 6 ++++-- testsuite/python/coulomb_interface.py | 6 ++++-- testsuite/python/icc_interface.py | 14 ++++++++++---- testsuite/python/save_checkpoint.py | 1 + testsuite/python/test_checkpoint.py | 2 ++ 11 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/core/electrostatics/p3m.cpp b/src/core/electrostatics/p3m.cpp index d0d059157f1..b06300fed9e 100644 --- a/src/core/electrostatics/p3m.cpp +++ b/src/core/electrostatics/p3m.cpp @@ -270,9 +270,11 @@ void CoulombP3M::init() { } CoulombP3M::CoulombP3M(P3MParameters &¶meters, double prefactor, - int tune_timings, bool tune_verbose) + int tune_timings, bool tune_verbose, + bool check_complex_residuals) : p3m{std::move(parameters)}, tune_timings{tune_timings}, - tune_verbose{tune_verbose} { + tune_verbose{tune_verbose}, check_complex_residuals{ + check_complex_residuals} { m_is_tuned = !p3m.params.tuning; p3m.params.tuning = false; @@ -490,7 +492,7 @@ double CoulombP3M::long_range_kernel(bool force_flag, bool energy_flag, } /* Back FFT force component mesh */ - auto const check_complex = !p3m.params.tuning; + auto const check_complex = !p3m.params.tuning and check_complex_residuals; for (int d = 0; d < 3; d++) { fft_perform_back(p3m.E_mesh[d].data(), check_complex, p3m.fft, comm_cart); } diff --git a/src/core/electrostatics/p3m.hpp b/src/core/electrostatics/p3m.hpp index 4f682bf7fa8..a8d02b055b4 100644 --- a/src/core/electrostatics/p3m.hpp +++ b/src/core/electrostatics/p3m.hpp @@ -89,13 +89,14 @@ struct CoulombP3M : public Coulomb::Actor { int tune_timings; bool tune_verbose; + bool check_complex_residuals; private: bool m_is_tuned; public: CoulombP3M(P3MParameters &¶meters, double prefactor, int tune_timings, - bool tune_verbose); + bool tune_verbose, bool check_complex_residuals); bool is_tuned() const { return m_is_tuned; } diff --git a/src/core/p3m/fft.cpp b/src/core/p3m/fft.cpp index 55ff03c66d3..2c1d193155c 100644 --- a/src/core/p3m/fft.cpp +++ b/src/core/p3m/fft.cpp @@ -738,7 +738,7 @@ void fft_perform_back(double *data, bool check_complex, fft_data_struct &fft, for (int i = 0; i < fft.plan[1].new_size; i++) { fft.data_buf[i] = data[2 * i]; /* real value */ // Vincent: - if (check_complex && (data[2 * i + 1] > 1e-5)) { + if (check_complex and std::abs(data[2 * i + 1]) > 1e-5) { printf("Complex value is not zero (i=%d,data=%g)!!!\n", i, data[2 * i + 1]); if (i > 100) diff --git a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp index 94cbc51a24d..ddf19a283ed 100644 --- a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp +++ b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp @@ -115,7 +115,7 @@ static void mpi_set_tuned_p3m_local(double prefactor) { 0.654, 1e-3}; auto solver = - std::make_shared(std::move(p3m), prefactor, 1, false); + std::make_shared(std::move(p3m), prefactor, 1, false, true); ::Coulomb::add_actor(solver); } diff --git a/src/python/espressomd/electrostatics.py b/src/python/espressomd/electrostatics.py index 6b91ec6d355..60a12cc4483 100644 --- a/src/python/espressomd/electrostatics.py +++ b/src/python/espressomd/electrostatics.py @@ -155,6 +155,7 @@ def default_params(self): "mesh_off": [-1., -1., -1.], "prefactor": 0., "check_neutrality": True, + "check_complex_residuals": True, "tune": True, "timings": 10, "verbose": True} @@ -231,6 +232,9 @@ class P3M(_P3MBase): check_neutrality : :obj:`bool`, optional Raise a warning if the system is not electrically neutral when set to ``True`` (default). + check_complex_residuals: :obj:`bool`, optional + Raise a warning if the backward Fourier transform has non-zero + complex residuals when set to ``True`` (default). """ _so_name = "Coulomb::CoulombP3M" @@ -281,6 +285,9 @@ class P3MGPU(_P3MBase): check_neutrality : :obj:`bool`, optional Raise a warning if the system is not electrically neutral when set to ``True`` (default). + check_complex_residuals: :obj:`bool`, optional + Raise a warning if the backward Fourier transform has non-zero + complex residuals when set to ``True`` (default). """ _so_name = "Coulomb::CoulombP3MGPU" diff --git a/src/script_interface/electrostatics/CoulombP3M.hpp b/src/script_interface/electrostatics/CoulombP3M.hpp index 147ee7d0af7..0f167cfa442 100644 --- a/src/script_interface/electrostatics/CoulombP3M.hpp +++ b/src/script_interface/electrostatics/CoulombP3M.hpp @@ -69,6 +69,8 @@ class CoulombP3M : public Actor { {"timings", AutoParameter::read_only, [this]() { return actor()->tune_timings; }}, {"tune", AutoParameter::read_only, [this]() { return m_tune; }}, + {"check_complex_residuals", AutoParameter::read_only, + [this]() { return actor()->check_complex_residuals; }}, }); } @@ -85,8 +87,8 @@ class CoulombP3M : public Actor { get_value(params, "accuracy")}; m_actor = std::make_shared( std::move(p3m), get_value(params, "prefactor"), - get_value(params, "timings"), - get_value(params, "verbose")); + get_value(params, "timings"), get_value(params, "verbose"), + get_value(params, "check_complex_residuals")); }); set_charge_neutrality_tolerance(params); } diff --git a/src/script_interface/electrostatics/CoulombP3MGPU.hpp b/src/script_interface/electrostatics/CoulombP3MGPU.hpp index d212a5e671f..3fe11a9de85 100644 --- a/src/script_interface/electrostatics/CoulombP3MGPU.hpp +++ b/src/script_interface/electrostatics/CoulombP3MGPU.hpp @@ -70,6 +70,8 @@ class CoulombP3MGPU : public Actor { {"timings", AutoParameter::read_only, [this]() { return actor()->tune_timings; }}, {"tune", AutoParameter::read_only, [this]() { return m_tune; }}, + {"check_complex_residuals", AutoParameter::read_only, + [this]() { return actor()->check_complex_residuals; }}, }); } @@ -86,8 +88,8 @@ class CoulombP3MGPU : public Actor { get_value(params, "accuracy")}; m_actor = std::make_shared( std::move(p3m), get_value(params, "prefactor"), - get_value(params, "timings"), - get_value(params, "verbose")); + get_value(params, "timings"), get_value(params, "verbose"), + get_value(params, "check_complex_residuals")); }); m_actor->request_gpu(); set_charge_neutrality_tolerance(params); diff --git a/testsuite/python/coulomb_interface.py b/testsuite/python/coulomb_interface.py index eff174e6e08..a5a1dc6b828 100644 --- a/testsuite/python/coulomb_interface.py +++ b/testsuite/python/coulomb_interface.py @@ -59,7 +59,8 @@ def tearDown(self): system, espressomd.electrostatics.P3M, dict(prefactor=2., epsilon=0., mesh_off=[0.6, 0.7, 0.8], r_cut=1.5, cao=2, mesh=[8, 10, 8], alpha=12., accuracy=0.01, tune=False, - check_neutrality=True, charge_neutrality_tolerance=7e-12)) + check_neutrality=True, charge_neutrality_tolerance=7e-12, + check_complex_residuals=False)) test_p3m_cpu_non_metallic = tests_common.generate_test_for_actor_class( system, espressomd.electrostatics.P3M, dict(prefactor=2., epsilon=3., mesh_off=[0.6, 0.7, 0.8], r_cut=1.5, @@ -78,7 +79,8 @@ def tearDown(self): system, espressomd.electrostatics.P3MGPU, dict(prefactor=2., epsilon=0., mesh_off=[0.6, 0.7, 0.8], r_cut=1.5, cao=2, mesh=[8, 10, 8], alpha=12., accuracy=0.01, tune=False, - check_neutrality=True, charge_neutrality_tolerance=7e-12)) + check_neutrality=True, charge_neutrality_tolerance=7e-12, + check_complex_residuals=False)) test_p3m_gpu_non_metallic = tests_common.generate_test_for_actor_class( system, espressomd.electrostatics.P3MGPU, dict(prefactor=2., epsilon=3., mesh_off=[0.6, 0.7, 0.8], r_cut=1.5, diff --git a/testsuite/python/icc_interface.py b/testsuite/python/icc_interface.py index 2b968d2432b..6eadca2d0e7 100644 --- a/testsuite/python/icc_interface.py +++ b/testsuite/python/icc_interface.py @@ -121,15 +121,21 @@ def test_exceptions_small_r_cut(self): @utx.skipIfMissingFeatures(["P3M"]) def test_exceptions_large_r_cut(self): - icc, (_, p) = self.setup_icc_particles_and_solver(max_iterations=1) - p3m = espressomd.electrostatics.P3M(**self.valid_p3m_parameters()) + icc, (_, p) = self.setup_icc_particles_and_solver( + max_iterations=1, convergence=10.) + p3m = espressomd.electrostatics.P3M( + check_complex_residuals=False, **self.valid_p3m_parameters()) self.system.actors.add(p3m) self.system.actors.add(icc) - with self.assertRaisesRegex(Exception, f"Particle with id {p.id} has a charge .+ that is too large for the ICC algorithm"): - p.q = 1e9 + p.q = 1e8 self.system.integrator.run(0) + + self.system.actors.remove(icc) + self.system.part.clear() + icc, (_, p) = self.setup_icc_particles_and_solver(max_iterations=1) + self.system.actors.add(icc) with self.assertRaisesRegex(Exception, "ICC failed to converge in the given number of maximal steps"): p.q = 0. self.system.integrator.run(0) diff --git a/testsuite/python/save_checkpoint.py b/testsuite/python/save_checkpoint.py index 09ebda6f002..55ab1b37b82 100644 --- a/testsuite/python/save_checkpoint.py +++ b/testsuite/python/save_checkpoint.py @@ -125,6 +125,7 @@ cao=1, alpha=1.0, r_cut=1.0, + check_complex_residuals=False, timings=15, tune=False) if 'ELC' in modes: diff --git a/testsuite/python/test_checkpoint.py b/testsuite/python/test_checkpoint.py index 17363085c3f..98f9959f68f 100644 --- a/testsuite/python/test_checkpoint.py +++ b/testsuite/python/test_checkpoint.py @@ -504,6 +504,7 @@ def test_p3m(self): reference = {'prefactor': 1.0, 'accuracy': 0.1, 'mesh': 3 * [10], 'cao': 1, 'alpha': 1.0, 'r_cut': 1.0, 'tune': False, 'timings': 15, 'check_neutrality': True, + 'check_complex_residuals': False, 'charge_neutrality_tolerance': 1e-12} for key in reference: self.assertIn(key, state) @@ -519,6 +520,7 @@ def test_elc(self): p3m_reference = {'prefactor': 1.0, 'accuracy': 0.1, 'mesh': 3 * [10], 'cao': 1, 'alpha': 1.0, 'r_cut': 1.0, 'tune': False, 'timings': 15, 'check_neutrality': True, + 'check_complex_residuals': False, 'charge_neutrality_tolerance': 7e-12} elc_reference = {'gap_size': 6.0, 'maxPWerror': 0.1, 'delta_mid_top': 0.9, 'delta_mid_bot': 0.1, From 39e7e122f397d4da1559e3c21ab9c220d490556a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 1 Sep 2022 16:48:28 +0200 Subject: [PATCH 47/85] python: Improve long-range parameter checks Raise an error when unknown parameters are passed to electrostatic or magnetostatic methods. Replace `valid_keys()` by the corresponding script interface method (several `valid_keys()` methods had diverged and no longer matched with the core). Move some range checks to the core, make error messages more homogeneous and systematically check them in the testsuite. --- src/core/electrostatics/mmm1d.cpp | 11 ++- src/core/electrostatics/mmm1d_gpu.cpp | 14 +++- src/core/electrostatics/p3m.cpp | 3 + src/core/magnetostatics/barnes_hut_gpu.cpp | 9 +++ .../magnetostatics/barnes_hut_gpu_cuda.cu | 26 +++--- src/core/magnetostatics/dp3m.cpp | 3 + src/python/espressomd/electrostatics.py | 80 ++++--------------- src/python/espressomd/magnetostatics.py | 75 ++++++++--------- .../magnetostatics/DipolarBarnesHutGpu.hpp | 10 ++- testsuite/python/coulomb_interface.py | 37 ++++++--- testsuite/python/dipolar_interface.py | 14 +++- .../python/electrostatic_interactions.py | 2 +- testsuite/python/long_range_actors.py | 2 +- 13 files changed, 143 insertions(+), 143 deletions(-) diff --git a/src/core/electrostatics/mmm1d.cpp b/src/core/electrostatics/mmm1d.cpp index 53f2b84a021..b29397b275e 100644 --- a/src/core/electrostatics/mmm1d.cpp +++ b/src/core/electrostatics/mmm1d.cpp @@ -123,10 +123,19 @@ CoulombMMM1D::CoulombMMM1D(double prefactor, double maxPWerror, : maxPWerror{maxPWerror}, far_switch_radius{switch_rad}, tune_timings{tune_timings}, tune_verbose{tune_verbose}, m_is_tuned{false}, far_switch_radius_sq{-1.}, uz2{0.}, prefuz2{0.}, prefL3_i{0.} { + set_prefactor(prefactor); + if (maxPWerror <= 0.) { + throw std::domain_error("Parameter 'maxPWerror' must be > 0"); + } + if (far_switch_radius <= 0. and far_switch_radius != -1.) { + throw std::domain_error("Parameter 'far_switch_radius' must be > 0"); + } if (far_switch_radius > 0.) { far_switch_radius_sq = Utils::sqr(far_switch_radius); } - set_prefactor(prefactor); + if (tune_timings <= 0) { + throw std::domain_error("Parameter 'timings' must be > 0"); + } } void CoulombMMM1D::sanity_checks_periodicity() const { diff --git a/src/core/electrostatics/mmm1d_gpu.cpp b/src/core/electrostatics/mmm1d_gpu.cpp index d1359544be5..ffe2047fd62 100644 --- a/src/core/electrostatics/mmm1d_gpu.cpp +++ b/src/core/electrostatics/mmm1d_gpu.cpp @@ -36,11 +36,19 @@ CoulombMMM1DGpu::CoulombMMM1DGpu(double prefactor, double maxPWerror, : maxPWerror{maxPWerror}, far_switch_radius{far_switch_radius}, far_switch_radius_sq{-1.}, bessel_cutoff{bessel_cutoff}, m_is_tuned{ false} { - set_prefactor(prefactor); - if (far_switch_radius >= 0. and far_switch_radius > box_geo.length()[2]) { + if (maxPWerror <= 0.) { + throw std::domain_error("Parameter 'maxPWerror' must be > 0"); + } + if (far_switch_radius <= 0. and far_switch_radius != -1.) { + throw std::domain_error("Parameter 'far_switch_radius' must be > 0"); + } + if (far_switch_radius > 0. and far_switch_radius > box_geo.length()[2]) { throw std::domain_error( - "switching radius must not be larger than box length"); + "Parameter 'far_switch_radius' must not be larger than box length"); + } + if (bessel_cutoff < 0 and bessel_cutoff != -1) { + throw std::domain_error("Parameter 'bessel_cutoff' must be > 0"); } auto &system = EspressoSystemInterface::Instance(); diff --git a/src/core/electrostatics/p3m.cpp b/src/core/electrostatics/p3m.cpp index b06300fed9e..530526f20ab 100644 --- a/src/core/electrostatics/p3m.cpp +++ b/src/core/electrostatics/p3m.cpp @@ -276,6 +276,9 @@ CoulombP3M::CoulombP3M(P3MParameters &¶meters, double prefactor, tune_verbose{tune_verbose}, check_complex_residuals{ check_complex_residuals} { + if (tune_timings <= 0) { + throw std::domain_error("Parameter 'timings' must be > 0"); + } m_is_tuned = !p3m.params.tuning; p3m.params.tuning = false; set_prefactor(prefactor); diff --git a/src/core/magnetostatics/barnes_hut_gpu.cpp b/src/core/magnetostatics/barnes_hut_gpu.cpp index 733f1b953f5..0f5011c1ecd 100644 --- a/src/core/magnetostatics/barnes_hut_gpu.cpp +++ b/src/core/magnetostatics/barnes_hut_gpu.cpp @@ -33,6 +33,15 @@ DipolarBarnesHutGpu::DipolarBarnesHutGpu(double prefactor, double epssq, double itolsq) : prefactor{prefactor}, m_epssq{epssq}, m_itolsq{itolsq} { + if (prefactor <= 0.) { + throw std::domain_error("Parameter 'prefactor' must be > 0"); + } + if (m_itolsq <= 0.) { + throw std::domain_error("Parameter 'itolsq' must be > 0"); + } + if (m_epssq <= 0.) { + throw std::domain_error("Parameter 'epssq' must be > 0"); + } auto &system = EspressoSystemInterface::Instance(); system.requestFGpu(); system.requestTorqueGpu(); diff --git a/src/core/magnetostatics/barnes_hut_gpu_cuda.cu b/src/core/magnetostatics/barnes_hut_gpu_cuda.cu index 9c1d9e00692..06bee2220f0 100644 --- a/src/core/magnetostatics/barnes_hut_gpu_cuda.cu +++ b/src/core/magnetostatics/barnes_hut_gpu_cuda.cu @@ -709,12 +709,12 @@ __global__ __launch_bounds__(THREADS5, FACTOR5) void forceCalculationKernel( // calculated within the array dq[i], which will // be compared later with squared distance between the particle // and the cell depending on a cell level. - // Original tree box edge (2*radiusd) should be divided *0.5 + // Original tree box edge (2*radiusd) should be halved // as much as the tree depth takes place. dq[0] = radiusd * radiusd * *itolsqd; for (i = 1; i < maxdepthd; i++) { - dq[i] = dq[i - 1] * 0.25f; - dq[i - 1] += *epssqd; + dq[i] = dq[i - 1] * 0.25f; // halving of the squared distance + dq[i - 1] += *epssqd; // increase thickness of previous cell } dq[i - 1] += *epssqd; @@ -820,7 +820,7 @@ __global__ __launch_bounds__(THREADS5, FACTOR5) void forceCalculationKernel( #endif if (n != i) { - auto const d1 = sqrtf(tmp /*, 0.5f*/); + auto const d1 = sqrtf(tmp); auto const dd5 = __fdividef(1.0f, tmp * tmp * d1); auto b = 0.0f; auto b2 = 0.0f; @@ -891,7 +891,7 @@ __global__ __launch_bounds__(THREADS5, FACTOR5) void forceCalculationKernel( __global__ __launch_bounds__(THREADS5, FACTOR5) void energyCalculationKernel( float pf, float *energySum) { // NOTE: the algorithm of this kernel is almost identical to - // forceCalculationKernel. See comments there. + // @ref forceCalculationKernel. See comments there. int i, n, t; float dr[3], h[3], u[3], uc[3]; @@ -962,20 +962,16 @@ __global__ __launch_bounds__(THREADS5, FACTOR5) void energyCalculationKernel( dr[l] = -bhpara->r[3 * n + l] + bhpara->r[3 * i + l]; tmp += dr[l] * dr[l]; } + // check if all threads agree that cell is far enough away + // (or is a body) #if defined(__CUDACC__) && CUDA_VERSION >= 9000 if ((n < bhpara->nbodies) || - __all_sync( - __activemask(), - tmp >= dq[depth])) { // check if all threads agree that cell - // is far enough away (or is a body) + __all_sync(__activemask(), tmp >= dq[depth])) { #else - if ((n < bhpara->nbodies) || - __all(tmp >= - dq[depth])) { // check if all threads agree that cell - // is far enough away (or is a body) + if ((n < bhpara->nbodies) || __all(tmp >= dq[depth])) { #endif if (n != i) { - auto const d1 = sqrtf(tmp /*, 0.5f*/); + auto const d1 = sqrtf(tmp); auto const dd5 = __fdividef(1.0f, tmp * tmp * d1); auto b = 0.0f; for (int l = 0; l < 3; l++) { @@ -1275,4 +1271,4 @@ void fill_bh_data(float const *r, float const *dip, BHData const *bh_data) { cuda_safe_mem(cudaMemcpy(bh_data->u, dip, size, cudaMemcpyDeviceToDevice)); } -#endif // BARNES_HUT +#endif // DIPOLAR_BARNES_HUT diff --git a/src/core/magnetostatics/dp3m.cpp b/src/core/magnetostatics/dp3m.cpp index 3e9c28cf689..9f2a15c21dd 100644 --- a/src/core/magnetostatics/dp3m.cpp +++ b/src/core/magnetostatics/dp3m.cpp @@ -154,6 +154,9 @@ DipolarP3M::DipolarP3M(P3MParameters &¶meters, double prefactor, if (prefactor <= 0.) { throw std::domain_error("Parameter 'prefactor' must be > 0"); } + if (tune_timings <= 0) { + throw std::domain_error("Parameter 'timings' must be > 0"); + } if (dp3m.params.mesh != Utils::Vector3i::broadcast(dp3m.params.mesh[0])) { throw std::domain_error("DipolarP3M requires a cubic mesh"); diff --git a/src/python/espressomd/electrostatics.py b/src/python/espressomd/electrostatics.py index 60a12cc4483..cbac5267a7a 100644 --- a/src/python/espressomd/electrostatics.py +++ b/src/python/espressomd/electrostatics.py @@ -38,10 +38,17 @@ def __init__(self, **kwargs): self._check_required_features() if 'sip' not in kwargs: + for key in self.required_keys(): + if key not in kwargs: + raise RuntimeError(f"Parameter '{key}' is missing") params = self.default_params() params.update(kwargs) self.validate_params(params) super().__init__(**params) + for key in params: + if key not in self._valid_parameters(): + raise RuntimeError( + f"Parameter '{key}' is not a valid parameter") else: super().__init__(**kwargs) @@ -53,14 +60,11 @@ def validate_params(self, params): """Check validity of given parameters. """ utils.check_type_or_throw_except( - params["prefactor"], 1, float, "prefactor should be a double") + params["prefactor"], 1, float, "Parameter 'prefactor' should be a float") def default_params(self): raise NotImplementedError("Derived classes must implement this method") - def valid_keys(self): - raise NotImplementedError("Derived classes must implement this method") - def required_keys(self): raise NotImplementedError("Derived classes must implement this method") @@ -93,9 +97,6 @@ class DH(ElectrostaticInteraction): _so_name = "Coulomb::DebyeHueckel" _so_creation_policy = "GLOBAL" - def valid_keys(self): - return {"prefactor", "kappa", "r_cut", "check_neutrality"} - def required_keys(self): return {"prefactor", "kappa", "r_cut"} @@ -126,10 +127,6 @@ class ReactionField(ElectrostaticInteraction): _so_name = "Coulomb::ReactionField" _so_creation_policy = "GLOBAL" - def valid_keys(self): - return {"prefactor", "kappa", "epsilon1", "epsilon2", "r_cut", - "check_neutrality"} - def required_keys(self): return {"prefactor", "kappa", "epsilon1", "epsilon2", "r_cut"} @@ -138,10 +135,6 @@ def default_params(self): class _P3MBase(ElectrostaticInteraction): - def valid_keys(self): - return {"mesh", "cao", "accuracy", "epsilon", "alpha", "r_cut", - "prefactor", "tune", "check_neutrality", "timings", - "verbose", "mesh_off"} def required_keys(self): return {"prefactor", "accuracy"} @@ -165,8 +158,9 @@ def validate_params(self, params): if utils.is_valid_type(params["mesh"], int): params["mesh"] = 3 * [params["mesh"]] - utils.check_type_or_throw_except(params["mesh"], 3, int, - "P3M mesh has to be an integer or integer list of length 3") + utils.check_type_or_throw_except( + params["mesh"], 3, int, + "Parameter 'mesh' has to be an integer or integer list of length 3") if (params["mesh"][0] % 2 != 0 and params["mesh"][0] != -1) or \ (params["mesh"][1] % 2 != 0 and params["mesh"][1] != -1) or \ (params["mesh"][2] % 2 != 0 and params["mesh"][2] != -1): @@ -178,18 +172,16 @@ def validate_params(self, params): utils.check_type_or_throw_except( params["epsilon"], 1, float, - "epsilon should be a double or 'metallic'") + "Parameter 'epsilon' has to be a float or 'metallic'") utils.check_type_or_throw_except( params["mesh_off"], 3, float, - "mesh_off should be a (3,) array_like of values between 0 and 1") + "Parameter 'mesh_off' has to be a (3,) array_like of values between 0.0 and 1.0") if not utils.is_valid_type(params["timings"], int): - raise TypeError("P3M timings has to be an integer") - if params["timings"] <= 0: - raise ValueError("P3M timings must be > 0") + raise TypeError("Parameter 'timings' has to be an integer") if not utils.is_valid_type(params["tune"], bool): - raise TypeError("P3M tune has to be a boolean") + raise TypeError("Parameter 'tune' has to be a boolean") @script_interface_register @@ -371,11 +363,6 @@ def validate_params(self, params): utils.check_type_or_throw_except( params["neutralize"], 1, bool, "neutralize has to be a bool") - def valid_keys(self): - return {"actor", "maxPWerror", "gap_size", "far_cut", - "neutralize", "delta_mid_top", "delta_mid_bot", - "const_pot", "pot_diff", "check_neutrality"} - def required_keys(self): return {"actor", "maxPWerror", "gap_size"} @@ -413,26 +400,12 @@ class MMM1D(ElectrostaticInteraction): _so_name = "Coulomb::CoulombMMM1D" _so_creation_policy = "GLOBAL" - def validate_params(self, params): - default_params = self.default_params() - if params["prefactor"] <= 0: - raise ValueError("prefactor should be a positive float") - if params["maxPWerror"] < 0 and params["maxPWerror"] != default_params["maxPWerror"]: - raise ValueError("maxPWerror should be a positive double") - if params["far_switch_radius"] < 0 and params["far_switch_radius"] != default_params["far_switch_radius"]: - raise ValueError("switch radius should be a positive double") - def default_params(self): return {"far_switch_radius": -1., "verbose": True, "timings": 15, - "tune": True, "check_neutrality": True} - def valid_keys(self): - return {"prefactor", "maxPWerror", "far_switch_radius", - "verbose", "timings", "tune", "check_neutrality"} - def required_keys(self): return {"prefactor", "maxPWerror"} @@ -462,27 +435,11 @@ def _check_required_features(self): if not has_features("MMM1D_GPU"): raise NotImplementedError("Feature MMM1D_GPU not compiled in") - def validate_params(self, params): - default_params = self.default_params() - if params["prefactor"] <= 0: - raise ValueError("prefactor should be a positive float") - if params["maxPWerror"] < 0 and params["maxPWerror"] != default_params["maxPWerror"]: - raise ValueError("maxPWerror should be a positive double") - if params["far_switch_radius"] < 0 and params["far_switch_radius"] != default_params["far_switch_radius"]: - raise ValueError("switch radius should be a positive double") - if params["bessel_cutoff"] < 0 and params["bessel_cutoff"] != default_params["bessel_cutoff"]: - raise ValueError("bessel_cutoff should be a positive integer") - def default_params(self): return {"far_switch_radius": -1., "bessel_cutoff": -1, - "tune": True, "check_neutrality": True} - def valid_keys(self): - return {"prefactor", "maxPWerror", "far_switch_radius", - "bessel_cutoff", "tune", "check_neutrality"} - def required_keys(self): return {"prefactor", "maxPWerror"} @@ -542,15 +499,8 @@ def _check_required_features(self): if not has_features("SCAFACOS"): raise NotImplementedError("Feature SCAFACOS not compiled in") - def validate_params(self, params): - pass - def default_params(self): return {"check_neutrality": True} - def valid_keys(self): - return {"method_name", "method_params", - "prefactor", "check_neutrality"} - def required_keys(self): return {"method_name", "method_params", "prefactor"} diff --git a/src/python/espressomd/magnetostatics.py b/src/python/espressomd/magnetostatics.py index 384562a8626..9846aeb8657 100644 --- a/src/python/espressomd/magnetostatics.py +++ b/src/python/espressomd/magnetostatics.py @@ -38,10 +38,18 @@ def __init__(self, **kwargs): self._check_required_features() if 'sip' not in kwargs: + for key in self.required_keys(): + if key not in kwargs: + raise RuntimeError(f"Parameter '{key}' is missing") + utils.check_required_keys(self.required_keys(), kwargs.keys()) params = self.default_params() params.update(kwargs) self.validate_params(params) super().__init__(**params) + for key in params: + if key not in self._valid_parameters(): + raise RuntimeError( + f"Parameter '{key}' is not a valid parameter") else: super().__init__(**kwargs) @@ -53,14 +61,11 @@ def validate_params(self, params): """Check validity of given parameters. """ utils.check_type_or_throw_except( - params["prefactor"], 1, float, "prefactor should be a double") + params["prefactor"], 1, float, "Parameter 'prefactor' should be a float") def default_params(self): raise NotImplementedError("Derived classes must implement this method") - def valid_keys(self): - raise NotImplementedError("Derived classes must implement this method") - def required_keys(self): raise NotImplementedError("Derived classes must implement this method") @@ -125,30 +130,25 @@ def validate_params(self, params): if utils.is_valid_type(params["mesh"], int): params["mesh"] = 3 * [params["mesh"]] else: - utils.check_type_or_throw_except(params["mesh"], 3, int, - "DipolarP3M mesh has to be an integer or integer list of length 3") + utils.check_type_or_throw_except( + params["mesh"], 3, int, + "Parameter 'mesh' has to be an integer or integer list of length 3") if params["epsilon"] == "metallic": params["epsilon"] = 0.0 utils.check_type_or_throw_except( params["epsilon"], 1, float, - "epsilon should be a double or 'metallic'") + "Parameter 'epsilon' has to be a float or 'metallic'") - utils.check_type_or_throw_except(params["mesh_off"], 3, float, - "mesh_off should be a (3,) array_like of values between 0.0 and 1.0") + utils.check_type_or_throw_except( + params["mesh_off"], 3, float, + "Parameter 'mesh_off' has to be a (3,) array_like of values between 0.0 and 1.0") if not utils.is_valid_type(params["timings"], int): - raise TypeError("DipolarP3M timings has to be an integer") - if params["timings"] <= 0: - raise ValueError("DipolarP3M timings must be > 0") + raise TypeError("Parameter 'timings' has to be an integer") if not utils.is_valid_type(params["tune"], bool): - raise TypeError("DipolarP3M tune has to be a boolean") - - def valid_keys(self): - return {"prefactor", "alpha_L", "r_cut_iL", "mesh", "mesh_off", - "cao", "accuracy", "epsilon", "cao_cut", "a", "ai", - "alpha", "r_cut", "cao3", "tune", "timings", "verbose"} + raise TypeError("Parameter 'tune' has to be a boolean") def required_keys(self): return {"accuracy"} @@ -190,9 +190,6 @@ def default_params(self): def required_keys(self): return set() - def valid_keys(self): - return {"prefactor"} - @script_interface_register class DipolarDirectSumWithReplicaCpu(MagnetostaticInteraction): @@ -219,9 +216,6 @@ def default_params(self): def required_keys(self): return {"n_replica"} - def valid_keys(self): - return {"prefactor", "n_replica"} - @script_interface_register class Scafacos(MagnetostaticInteraction): @@ -260,15 +254,9 @@ def _check_required_features(self): raise NotImplementedError( "Feature SCAFACOS_DIPOLES not compiled in") - def validate_params(self, params): - pass - def default_params(self): return {} - def valid_keys(self): - return {"method_name", "method_params", "prefactor"} - def required_keys(self): return {"method_name", "method_params", "prefactor"} @@ -306,9 +294,6 @@ def default_params(self): return {} def required_keys(self): - return set() - - def valid_keys(self): return {"prefactor"} @@ -325,6 +310,13 @@ class DipolarBarnesHutGpu(MagnetostaticInteraction): Requires feature ``DIPOLAR_BARNES_HUT``, which depends on ``DIPOLES`` and ``CUDA``. + Parameters + ---------- + epssq : :obj:`float`, optional + Squared skin of the octant cells. + itolsq : :obj:`float`, optional + Squared inverse fraction of the octant cells. + """ _so_name = "Dipoles::DipolarBarnesHutGpu" _so_creation_policy = "GLOBAL" @@ -338,10 +330,7 @@ def default_params(self): return {"epssq": 100.0, "itolsq": 4.0} def required_keys(self): - return set() - - def valid_keys(self): - return {"prefactor", "epssq", "itolsq"} + return {"prefactor"} @script_interface_register @@ -375,17 +364,17 @@ class DLC(MagnetostaticInteraction): def validate_params(self, params): utils.check_type_or_throw_except( - params["maxPWerror"], 1, float, "maxPWerror has to be a float") + params["maxPWerror"], 1, float, + "Parameter 'maxPWerror' has to be a float") utils.check_type_or_throw_except( - params["gap_size"], 1, float, "gap_size has to be a float") + params["gap_size"], 1, float, + "Parameter 'gap_size' has to be a float") utils.check_type_or_throw_except( - params["far_cut"], 1, float, "far_cut has to be a float") + params["far_cut"], 1, float, + "Parameter 'far_cut' has to be a float") def default_params(self): return {"far_cut": -1.} - def valid_keys(self): - return {"actor", "maxPWerror", "gap_size", "far_cut"} - def required_keys(self): return {"actor", "maxPWerror", "gap_size"} diff --git a/src/script_interface/magnetostatics/DipolarBarnesHutGpu.hpp b/src/script_interface/magnetostatics/DipolarBarnesHutGpu.hpp index 222f097c434..b12f99ce2b7 100644 --- a/src/script_interface/magnetostatics/DipolarBarnesHutGpu.hpp +++ b/src/script_interface/magnetostatics/DipolarBarnesHutGpu.hpp @@ -49,10 +49,12 @@ class DipolarBarnesHutGpu } void do_construct(VariantMap const ¶ms) override { - m_actor = - std::make_shared(get_value(params, "prefactor"), - get_value(params, "epssq"), - get_value(params, "itolsq")); + context()->parallel_try_catch([&]() { + m_actor = std::make_shared( + get_value(params, "prefactor"), + get_value(params, "epssq"), + get_value(params, "itolsq")); + }); } }; diff --git a/testsuite/python/coulomb_interface.py b/testsuite/python/coulomb_interface.py index a5a1dc6b828..fbfda219323 100644 --- a/testsuite/python/coulomb_interface.py +++ b/testsuite/python/coulomb_interface.py @@ -97,22 +97,37 @@ def tearDown(self): def test_mmm1d_cpu(self): self.system.periodicity = [False, False, True] self.system.cell_system.set_n_square() + valid_params = dict( + prefactor=1., maxPWerror=1e-3, far_switch_radius=1., + check_neutrality=True, charge_neutrality_tolerance=7e-12, + timings=5, verbose=False) tests_common.generate_test_for_actor_class( - self.system, espressomd.electrostatics.MMM1D, - dict(prefactor=1.0, maxPWerror=1e-3, far_switch_radius=1., - check_neutrality=True, charge_neutrality_tolerance=7e-12, - timings=5, verbose=False))(self) + self.system, espressomd.electrostatics.MMM1D, valid_params)(self) + + for key in ["prefactor", "maxPWerror", "far_switch_radius", "timings"]: + invalid_params = valid_params.copy() + invalid_params[key] = -2 + with self.assertRaisesRegex(ValueError, f"Parameter '{key}' must be > 0"): + espressomd.electrostatics.MMM1D(**invalid_params) @utx.skipIfMissingGPU() @utx.skipIfMissingFeatures(["CUDA", "MMM1D_GPU"]) def test_mmm1d_gpu(self): self.system.periodicity = [False, False, True] self.system.cell_system.set_n_square() + valid_params = dict( + prefactor=1., maxPWerror=1e-3, far_switch_radius=1., + check_neutrality=True, charge_neutrality_tolerance=7e-12, + bessel_cutoff=1) tests_common.generate_test_for_actor_class( - self.system, espressomd.electrostatics.MMM1DGPU, - dict(prefactor=1., maxPWerror=1e-3, far_switch_radius=1., - check_neutrality=True, charge_neutrality_tolerance=7e-12, - bessel_cutoff=1))(self) + self.system, espressomd.electrostatics.MMM1DGPU, valid_params)(self) + + for key in ["prefactor", "maxPWerror", + "far_switch_radius", "bessel_cutoff"]: + invalid_params = valid_params.copy() + invalid_params[key] = -2 + with self.assertRaisesRegex(ValueError, f"Parameter '{key}' must be > 0"): + espressomd.electrostatics.MMM1DGPU(**invalid_params) def test_charge_neutrality_check(self): self.system.part.add(pos=(0.0, 0.0, 0.0), q=1.) @@ -173,12 +188,16 @@ def test_elc_p3m_exceptions(self): # check runtime errors and input parameters with self.assertRaisesRegex(ValueError, "Parameter 'prefactor' must be > 0"): P3M(**{**p3m_params, 'prefactor': -2.}) - with self.assertRaisesRegex(ValueError, "P3M mesh has to be an integer or integer list of length 3"): + with self.assertRaisesRegex(ValueError, "Parameter 'timings' must be > 0"): + P3M(**{**p3m_params, 'timings': -2}) + with self.assertRaisesRegex(ValueError, "Parameter 'mesh' has to be an integer or integer list of length 3"): P3M(**{**p3m_params, 'mesh': [8, 8]}) with self.assertRaisesRegex(ValueError, "Parameter 'actor' of type Coulomb::ElectrostaticLayerCorrection isn't supported by ELC"): ELC(gap_size=2., maxPWerror=1., actor=elc) with self.assertRaisesRegex(ValueError, "Parameter 'actor' of type Coulomb::DebyeHueckel isn't supported by ELC"): ELC(gap_size=2., maxPWerror=1., actor=dh) + with self.assertRaisesRegex(RuntimeError, "Parameter 'accuracy' is not a valid parameter"): + ELC(gap_size=2., maxPWerror=1., actor=p3m, accuracy=1e-3) with self.assertRaisesRegex(RuntimeError, "Parameter 'actor' is missing"): ELC(gap_size=2., maxPWerror=1.) with self.assertRaisesRegex(ValueError, "Parameter 'const_pot' must be True when 'pot_diff' is non-zero"): diff --git a/testsuite/python/dipolar_interface.py b/testsuite/python/dipolar_interface.py index 93ebb1ad50b..cf7b4dcc27e 100644 --- a/testsuite/python/dipolar_interface.py +++ b/testsuite/python/dipolar_interface.py @@ -166,13 +166,25 @@ def test_exceptions_parallel(self): with self.assertRaisesRegex(ValueError, "Parameter 'prefactor' must be > 0"): espressomd.magnetostatics.DipolarP3M( **{**dp3m_params, 'prefactor': -2.}) - with self.assertRaisesRegex(ValueError, "DipolarP3M mesh has to be an integer or integer list of length 3"): + with self.assertRaisesRegex(ValueError, "Parameter 'mesh' has to be an integer or integer list of length 3"): espressomd.magnetostatics.DipolarP3M( **{**dp3m_params, 'mesh': [49, 49]}) dp3m = DP3M(**dp3m_params) mdlc = MDLC(gap_size=2., maxPWerror=0.1, actor=dp3m) with self.assertRaisesRegex(ValueError, "Parameter 'actor' of type Dipoles::DipolarLayerCorrection isn't supported by DLC"): MDLC(gap_size=2., maxPWerror=0.1, actor=mdlc) + with self.assertRaisesRegex(RuntimeError, "Parameter 'accuracy' is not a valid parameter"): + MDLC(gap_size=2., maxPWerror=0.1, actor=dp3m, accuracy=1e-3) + + @utx.skipIfMissingGPU() + @utx.skipIfMissingFeatures(["DIPOLAR_BARNES_HUT"]) + def test_exceptions_barnes_hut(self): + valid_params = dict(prefactor=2., epssq=200., itolsq=8.) + for key in valid_params.keys(): + invalid_params = valid_params.copy() + invalid_params[key] = -1. + with self.assertRaisesRegex(ValueError, f"Parameter '{key}' must be > 0"): + espressomd.magnetostatics.DipolarBarnesHutGpu(**invalid_params) if __name__ == "__main__": diff --git a/testsuite/python/electrostatic_interactions.py b/testsuite/python/electrostatic_interactions.py index bec0355b686..8b50b7094f9 100644 --- a/testsuite/python/electrostatic_interactions.py +++ b/testsuite/python/electrostatic_interactions.py @@ -246,7 +246,7 @@ def test_rf_exceptions(self): r_cut=1.0) for key in params: invalid_params = {**params, key: -1.0} - with self.assertRaisesRegex(ValueError, f"'{key}' must be >=? 0"): + with self.assertRaisesRegex(ValueError, f"Parameter '{key}' must be >=? 0"): espressomd.electrostatics.ReactionField(**invalid_params) diff --git a/testsuite/python/long_range_actors.py b/testsuite/python/long_range_actors.py index c7c2f57947d..705dedc5403 100644 --- a/testsuite/python/long_range_actors.py +++ b/testsuite/python/long_range_actors.py @@ -331,7 +331,7 @@ def test_mmm1d_gpu_exceptions(self): self.system.periodicity = (False, False, True) self.check_mmm1d_exceptions(espressomd.electrostatics.MMM1DGPU) - with self.assertRaisesRegex(ValueError, "switching radius must not be larger than box length"): + with self.assertRaisesRegex(ValueError, "Parameter 'far_switch_radius' must not be larger than box length"): espressomd.electrostatics.MMM1DGPU( prefactor=1., maxPWerror=1e-2, far_switch_radius=2. * self.system.box_l[2]) From 8151b55317426b85cf0fb041fa49a98c5bc2e57c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 2 Sep 2022 10:24:06 +0200 Subject: [PATCH 48/85] CI: Skip statistical tests on macOS --- .github/actions/build_and_check/action.yml | 6 +----- .github/workflows/push_pull.yml | 2 -- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/actions/build_and_check/action.yml b/.github/actions/build_and_check/action.yml index 2dcce230289..61ca0b49a88 100644 --- a/.github/actions/build_and_check/action.yml +++ b/.github/actions/build_and_check/action.yml @@ -9,10 +9,6 @@ inputs: description: 'Whether to build with undefined behavior sanitizer' required: true default: 'false' - check_skip_long: # id of input - description: 'Whether to skip long python tests' - required: true - default: 'false' runs: using: "composite" steps: @@ -22,7 +18,7 @@ runs: pip3 install numpy cython h5py scipy shell: bash - run: | - export myconfig=maxset with_cuda=false test_timeout=800 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=true bash maintainer/CI/build_cmake.sh shell: bash # This is a workaround for the unfortunate interaction of MacOS and OpenMPI 4 diff --git a/.github/workflows/push_pull.yml b/.github/workflows/push_pull.yml index 26dfebcad9f..1de0439032d 100644 --- a/.github/workflows/push_pull.yml +++ b/.github/workflows/push_pull.yml @@ -22,7 +22,6 @@ jobs: with: asan: false ubsan: false - check_skip_long: false sanitizer_check: runs-on: macos-latest @@ -39,7 +38,6 @@ jobs: with: asan: true ubsan: true - check_skip_long: true - name: Setting job link variable if: ${{ failure() }} run: | From c817352d7be69ce9548f187396a07029ac7ed550 Mon Sep 17 00:00:00 2001 From: Julian Hossbach Date: Wed, 24 Aug 2022 14:10:45 +0200 Subject: [PATCH 49/85] Rewrite LJcos interaction interface --- src/core/nonbonded_interactions/ljcos.cpp | 39 +++++------- src/core/nonbonded_interactions/ljcos.hpp | 4 -- .../nonbonded_interaction_data.cpp | 3 +- .../nonbonded_interaction_data.hpp | 3 + src/python/espressomd/interactions.pxd | 14 ----- src/python/espressomd/interactions.pyx | 61 +++++++------------ .../interactions/NonBondedInteraction.hpp | 36 +++++++++++ .../interactions/initialize.cpp | 3 + .../interactions_non-bonded_interface.py | 7 +++ 9 files changed, 88 insertions(+), 82 deletions(-) diff --git a/src/core/nonbonded_interactions/ljcos.cpp b/src/core/nonbonded_interactions/ljcos.cpp index 78c29d28a80..ba33702b446 100644 --- a/src/core/nonbonded_interactions/ljcos.cpp +++ b/src/core/nonbonded_interactions/ljcos.cpp @@ -25,34 +25,27 @@ #include "ljcos.hpp" #ifdef LJCOS -#include "interactions.hpp" #include "nonbonded_interaction_data.hpp" #include #include -int ljcos_set_params(int part_type_a, int part_type_b, double eps, double sig, - double cut, double offset) { - IA_parameters *data = get_ia_param_safe(part_type_a, part_type_b); - - if (!data) - return ES_ERROR; - - data->ljcos.eps = eps; - data->ljcos.sig = sig; - data->ljcos.cut = cut; - data->ljcos.offset = offset; - - /* Calculate dependent parameters */ +#include + +LJcos_Parameters::LJcos_Parameters(double eps, double sig, double cut, + double offset) + : eps{eps}, sig{sig}, cut{cut}, offset{offset} { + if (eps < 0.) { + throw std::domain_error("LJcos parameter 'epsilon' has to be >= 0"); + } + if (sig < 0.) { + throw std::domain_error("LJcos parameter 'sigma' has to be >= 0"); + } auto const facsq = Utils::cbrt_2() * Utils::sqr(sig); - data->ljcos.rmin = sqrt(Utils::cbrt_2()) * sig; - data->ljcos.alfa = Utils::pi() / (Utils::sqr(data->ljcos.cut) - facsq); - data->ljcos.beta = - Utils::pi() * (1. - (1. / (Utils::sqr(data->ljcos.cut) / facsq - 1.))); - /* broadcast interaction parameters */ - mpi_bcast_ia_params(part_type_a, part_type_b); - - return ES_OK; + rmin = sqrt(Utils::cbrt_2()) * sig; + alfa = Utils::pi() / (Utils::sqr(cut) - facsq); + beta = Utils::pi() * (1. - (1. / (Utils::sqr(cut) / facsq - 1.))); } -#endif + +#endif // LJCOS diff --git a/src/core/nonbonded_interactions/ljcos.hpp b/src/core/nonbonded_interactions/ljcos.hpp index 342c08c99c6..b75ce5544de 100644 --- a/src/core/nonbonded_interactions/ljcos.hpp +++ b/src/core/nonbonded_interactions/ljcos.hpp @@ -33,15 +33,11 @@ #include "nonbonded_interaction_data.hpp" -#include #include #include #include -int ljcos_set_params(int part_type_a, int part_type_b, double eps, double sig, - double cut, double offset); - /** Calculate Lennard-Jones cosine force factor */ inline double ljcos_pair_force_factor(IA_parameters const &ia_params, double dist) { diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index 10d33891b5e..05a49776e63 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -188,8 +188,7 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { #endif #ifdef LJCOS - max_cut_current = - std::max(max_cut_current, (data.ljcos.cut + data.ljcos.offset)); + max_cut_current = std::max(max_cut_current, data.ljcos.max_cutoff()); #endif #ifdef LJCOS2 diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index 1c3f68a0ea8..30b60202909 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -159,6 +159,9 @@ struct LJcos_Parameters { double alfa = 0.0; double beta = 0.0; double rmin = 0.0; + LJcos_Parameters() = default; + LJcos_Parameters(double eps, double sig, double cut, double offset); + double max_cutoff() const { return cut + offset; } }; /** Lennard-Jones with a different Cos potential */ diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index 6fd9f585b22..99a3c1cb61a 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -106,12 +106,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": double Fmax double r - cdef struct LJcos_Parameters: - double eps - double sig - double cut - double offset - cdef struct LJcos2_Parameters: double eps double sig @@ -140,8 +134,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": cdef struct IA_parameters: - LJcos_Parameters ljcos - LJcos2_Parameters ljcos2 LJGen_Parameters ljgen @@ -176,12 +168,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": cdef void ia_params_set_state(string) cdef void reset_ia_params() -IF LJCOS: - cdef extern from "nonbonded_interactions/ljcos.hpp": - cdef int ljcos_set_params(int part_type_a, int part_type_b, - double eps, double sig, - double cut, double offset) - IF LJCOS2: cdef extern from "nonbonded_interactions/ljcos2.hpp": cdef int ljcos2_set_params(int part_type_a, int part_type_b, diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 2bb2d5aff61..6753eb86e34 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -563,37 +563,22 @@ IF LENNARD_JONES_GENERIC == 1: return {"epsilon", "sigma", "cutoff", "shift", "offset", "e1", "e2", "b1", "b2"} -IF LJCOS: +IF LJCOS == 1: - cdef class LennardJonesCosInteraction(NonBondedInteraction): - - def validate_params(self): - if self._params["epsilon"] < 0: - raise ValueError("Lennard-Jones epsilon has to be >=0") - if self._params["sigma"] < 0: - raise ValueError("Lennard-Jones sigma has to be >=0") - - def _get_params_from_es_core(self): - cdef IA_parameters * ia_params - ia_params = get_ia_param_safe( - self._part_types[0], - self._part_types[1]) - return { - "epsilon": ia_params.ljcos.eps, - "sigma": ia_params.ljcos.sig, - "cutoff": ia_params.ljcos.cut, - "offset": ia_params.ljcos.offset, - } - - def is_active(self): - return(self._params["epsilon"] > 0) + @script_interface_register + class LennardJonesCosInteraction(NewNonBondedInteraction): + """Lennard-Jones cosine interaction. - def set_params(self, **kwargs): - """Set parameters for the Lennard-Jones cosine interaction. + Methods + ------- + set_params() + Set or update parameters for the interaction. + Parameters marked as required become optional once the + interaction has been activated for the first time; + subsequent calls to this method update the existing values. Parameters ---------- - epsilon : :obj:`float` Magnitude of the interaction. sigma : :obj:`float` @@ -602,20 +587,20 @@ IF LJCOS: Cutoff distance of the interaction. offset : :obj:`float`, optional Offset distance of the interaction. - """ - super().set_params(**kwargs) + """ - def _set_params_in_es_core(self): - if ljcos_set_params(self._part_types[0], - self._part_types[1], - self._params["epsilon"], - self._params["sigma"], - self._params["cutoff"], - self._params["offset"]): - raise Exception( - "Could not set Lennard-Jones Cosine parameters") + _so_name = "Interactions::InteractionLJcos" + + def is_active(self): + """Check if interactions is active. + + """ + return self.epsilon > 0. def default_params(self): + """Python dictionary of default parameters. + + """ return {"offset": 0.} def type_name(self): @@ -1582,8 +1567,6 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): IF LENNARD_JONES_GENERIC: self.generic_lennard_jones = GenericLennardJonesInteraction( _type1, _type2) - IF LJCOS: - self.lennard_jones_cos = LennardJonesCosInteraction(_type1, _type2) IF LJCOS2: self.lennard_jones_cos2 = LennardJonesCos2Interaction( _type1, _type2) diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index 7b961cfa2ad..5e3bb962652 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -196,6 +196,32 @@ class InteractionLJ : public InteractionPotentialInterface<::LJ_Parameters> { }; #endif // LENNARD_JONES +#ifdef LJCOS +class InteractionLJcos + : public InteractionPotentialInterface<::LJcos_Parameters> { +protected: + CoreInteraction IA_parameters::*get_ptr_offset() const override { + return &::IA_parameters::ljcos; + } + +public: + InteractionLJcos() { + add_parameters({ + make_autoparameter(&CoreInteraction::eps, "epsilon"), + make_autoparameter(&CoreInteraction::sig, "sigma"), + make_autoparameter(&CoreInteraction::cut, "cutoff"), + make_autoparameter(&CoreInteraction::offset, "offset"), + }); + } + + void make_new_instance(VariantMap const ¶ms) override { + m_ia_si = + make_shared_from_args( + params, "epsilon", "sigma", "cutoff", "offset"); + } +}; +#endif // LJCOS + class NonBondedInteractionHandle : public AutoParameters { std::array m_types = {-1, -1}; @@ -206,6 +232,9 @@ class NonBondedInteractionHandle #ifdef LENNARD_JONES std::shared_ptr m_lj; #endif +#ifdef LJCOS + std::shared_ptr m_ljcos; +#endif template auto make_autoparameter(std::shared_ptr &member, const char *key) const { @@ -229,6 +258,9 @@ class NonBondedInteractionHandle #endif #ifdef LENNARD_JONES make_autoparameter(m_lj, "lennard_jones"), +#endif +#ifdef LJCOS + make_autoparameter(m_ljcos, "lennard_jones_cos"), #endif }); } @@ -275,6 +307,10 @@ class NonBondedInteractionHandle #ifdef LENNARD_JONES set_member(m_lj, "lennard_jones", "Interactions::InteractionLJ", params); +#endif +#ifdef LJCOS + set_member(m_ljcos, "lennard_jones_cos", + "Interactions::InteractionLJcos", params); #endif } diff --git a/src/script_interface/interactions/initialize.cpp b/src/script_interface/interactions/initialize.cpp index 03bba64c9ea..2c5e0e60639 100644 --- a/src/script_interface/interactions/initialize.cpp +++ b/src/script_interface/interactions/initialize.cpp @@ -59,6 +59,9 @@ void initialize(Utils::Factory *om) { #ifdef LENNARD_JONES om->register_new("Interactions::InteractionLJ"); #endif +#ifdef LJCOS + om->register_new("Interactions::InteractionLJcos"); +#endif #ifdef WCA om->register_new("Interactions::InteractionWCA"); #endif diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index 82a45ab8e50..a45d7a08fbe 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -247,6 +247,13 @@ def test_lj_exceptions(self): {"epsilon": 1., "sigma": 1., "cutoff": 1.5, "shift": 0.2}, ("epsilon", "sigma")) + @utx.skipIfMissingFeatures("LJCOS") + def test_lj_cos_exceptions(self): + self.check_potential_exceptions( + espressomd.interactions.LennardJonesCosInteraction, + {"epsilon": 1., "sigma": 1., "cutoff": 1.5, "offset": 0.2}, + ("epsilon", "sigma")) + if __name__ == "__main__": ut.main() From 587b341721f33ce47bc01c3db32b67be41e85b78 Mon Sep 17 00:00:00 2001 From: Julian Hossbach Date: Fri, 26 Aug 2022 14:18:05 +0200 Subject: [PATCH 50/85] Rewrite LJcos2 interaction interface --- src/core/nonbonded_interactions/ljcos2.cpp | 39 ++++------- src/core/nonbonded_interactions/ljcos2.hpp | 4 -- .../nonbonded_interaction_data.cpp | 3 +- .../nonbonded_interaction_data.hpp | 3 + src/python/espressomd/interactions.pxd | 14 ---- src/python/espressomd/interactions.pyx | 69 ++++++++----------- .../interactions/NonBondedInteraction.hpp | 45 ++++++++++++ .../interactions/initialize.cpp | 3 + .../interactions_non-bonded_interface.py | 7 ++ 9 files changed, 101 insertions(+), 86 deletions(-) diff --git a/src/core/nonbonded_interactions/ljcos2.cpp b/src/core/nonbonded_interactions/ljcos2.cpp index 56237327934..31ccb14196b 100644 --- a/src/core/nonbonded_interactions/ljcos2.cpp +++ b/src/core/nonbonded_interactions/ljcos2.cpp @@ -25,33 +25,24 @@ #include "ljcos2.hpp" #ifdef LJCOS2 -#include "interactions.hpp" #include "nonbonded_interaction_data.hpp" -#include - #include - -int ljcos2_set_params(int part_type_a, int part_type_b, double eps, double sig, - double offset, double w) { - IA_parameters *data = get_ia_param_safe(part_type_a, part_type_b); - - if (!data) - return ES_ERROR; - - data->ljcos2.eps = eps; - data->ljcos2.sig = sig; - data->ljcos2.offset = offset; - data->ljcos2.w = w; - - /* calculate dependent parameters */ - data->ljcos2.rchange = pow(2, 1 / 6.) * sig; - data->ljcos2.cut = w + data->ljcos2.rchange; - - /* broadcast interaction parameters */ - mpi_bcast_ia_params(part_type_a, part_type_b); - - return ES_OK; +#include + +LJcos2_Parameters::LJcos2_Parameters(double eps, double sig, double offset, + double w) + : eps{eps}, sig{sig}, offset{offset}, w{w} { + if (eps < 0.) { + throw std::domain_error("LJcos2 parameter 'epsilon' has to be >= 0"); + } + if (sig < 0.) { + throw std::domain_error("LJcos2 parameter 'sigma' has to be >= 0"); + } + if (sig != 0.) { + rchange = std::pow(2., 1. / 6.) * sig; + cut = w + rchange; + } } #endif /* ifdef LJCOS2 */ diff --git a/src/core/nonbonded_interactions/ljcos2.hpp b/src/core/nonbonded_interactions/ljcos2.hpp index 3df298586fd..b04f6c64d88 100644 --- a/src/core/nonbonded_interactions/ljcos2.hpp +++ b/src/core/nonbonded_interactions/ljcos2.hpp @@ -36,16 +36,12 @@ #include "nonbonded_interaction_data.hpp" -#include #include #include #include #include -int ljcos2_set_params(int part_type_a, int part_type_b, double eps, double sig, - double offset, double w); - /** Calculate Lennard-Jones cosine squared force factor */ inline double ljcos2_pair_force_factor(IA_parameters const &ia_params, double dist) { diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index 05a49776e63..b2cf2747d14 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -192,8 +192,7 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { #endif #ifdef LJCOS2 - max_cut_current = - std::max(max_cut_current, (data.ljcos2.cut + data.ljcos2.offset)); + max_cut_current = std::max(max_cut_current, data.ljcos2.max_cutoff()); #endif #ifdef GAY_BERNE diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index 30b60202909..7e807f66d70 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -172,6 +172,9 @@ struct LJcos2_Parameters { double offset = 0.0; double w = 0.0; double rchange = 0.0; + LJcos2_Parameters() = default; + LJcos2_Parameters(double eps, double sig, double offset, double w); + double max_cutoff() const { return cut + offset; } }; /** Gay-Berne potential */ diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index 99a3c1cb61a..926a8c782ba 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -106,12 +106,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": double Fmax double r - cdef struct LJcos2_Parameters: - double eps - double sig - double offset - double w - cdef struct GayBerne_Parameters: double eps double sig @@ -134,8 +128,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": cdef struct IA_parameters: - LJcos2_Parameters ljcos2 - LJGen_Parameters ljgen SoftSphere_Parameters soft_sphere @@ -168,12 +160,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": cdef void ia_params_set_state(string) cdef void reset_ia_params() -IF LJCOS2: - cdef extern from "nonbonded_interactions/ljcos2.hpp": - cdef int ljcos2_set_params(int part_type_a, int part_type_b, - double eps, double sig, double offset, - double w) - IF GAY_BERNE: cdef extern from "nonbonded_interactions/gay_berne.hpp": int gay_berne_set_params(int part_type_a, int part_type_b, diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 6753eb86e34..c21cafb3fe2 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -621,38 +621,22 @@ IF LJCOS == 1: """ return {"epsilon", "sigma", "cutoff"} -IF LJCOS2: +IF LJCOS2 == 1: - cdef class LennardJonesCos2Interaction(NonBondedInteraction): - - def validate_params(self): - if self._params["epsilon"] < 0: - raise ValueError("Lennard-Jones epsilon has to be >=0") - if self._params["sigma"] < 0: - raise ValueError("Lennard-Jones sigma has to be >=0") - if self._params["width"] < 0: - raise ValueError("Parameter width has to be >=0") - - def _get_params_from_es_core(self): - cdef IA_parameters * ia_params - ia_params = get_ia_param_safe( - self._part_types[0], - self._part_types[1]) - return{ - "epsilon": ia_params.ljcos2.eps, - "sigma": ia_params.ljcos2.sig, - "offset": ia_params.ljcos2.offset, - "width": ia_params.ljcos2.w} - - def is_active(self): - return (self._params["epsilon"] > 0) + @script_interface_register + class LennardJonesCos2Interaction(NewNonBondedInteraction): + """Second variant of the Lennard-Jones cosine interaction. - def set_params(self, **kwargs): - """Set parameters for the Lennard-Jones cosine squared interaction. + Methods + ------- + set_params() + Set or update parameters for the interaction. + Parameters marked as required become optional once the + interaction has been activated for the first time; + subsequent calls to this method update the existing values. Parameters ---------- - epsilon : :obj:`float` Magnitude of the interaction. sigma : :obj:`float` @@ -661,20 +645,20 @@ IF LJCOS2: Offset distance of the interaction. width : :obj:`float` Width of interaction. - """ - super().set_params(**kwargs) + """ - def _set_params_in_es_core(self): - if ljcos2_set_params(self._part_types[0], - self._part_types[1], - self._params["epsilon"], - self._params["sigma"], - self._params["offset"], - self._params["width"]): - raise Exception( - "Could not set Lennard-Jones Cosine2 parameters") + _so_name = "Interactions::InteractionLJcos2" + + def is_active(self): + """Check if interactions is active. + + """ + return self.epsilon > 0. def default_params(self): + """Python dictionary of default parameters. + + """ return {"offset": 0.} def type_name(self): @@ -687,7 +671,7 @@ IF LJCOS2: """All parameters that can be set. """ - return {"epsilon", "sigma", "offset", "width"} + return {"epsilon", "sigma", "width", "offset"} def required_keys(self): """Parameters that have to be set. @@ -695,6 +679,10 @@ IF LJCOS2: """ return {"epsilon", "sigma", "width"} + @property + def cutoff(self): + return self.call_method("get_cutoff") + IF HAT == 1: cdef class HatInteraction(NonBondedInteraction): @@ -1567,9 +1555,6 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): IF LENNARD_JONES_GENERIC: self.generic_lennard_jones = GenericLennardJonesInteraction( _type1, _type2) - IF LJCOS2: - self.lennard_jones_cos2 = LennardJonesCos2Interaction( - _type1, _type2) IF SMOOTH_STEP: self.smooth_step = SmoothStepInteraction(_type1, _type2) IF BMHTF_NACL: diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index 5e3bb962652..f78c09e6f9f 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -222,6 +222,41 @@ class InteractionLJcos }; #endif // LJCOS +#ifdef LJCOS2 +class InteractionLJcos2 + : public InteractionPotentialInterface<::LJcos2_Parameters> { +protected: + CoreInteraction IA_parameters::*get_ptr_offset() const override { + return &::IA_parameters::ljcos2; + } + +public: + InteractionLJcos2() { + add_parameters({ + make_autoparameter(&CoreInteraction::eps, "epsilon"), + make_autoparameter(&CoreInteraction::sig, "sigma"), + make_autoparameter(&CoreInteraction::offset, "offset"), + make_autoparameter(&CoreInteraction::w, "width"), + }); + } + + void make_new_instance(VariantMap const ¶ms) override { + m_ia_si = + make_shared_from_args( + params, "epsilon", "sigma", "offset", "width"); + } + + Variant do_call_method(std::string const &name, + VariantMap const ¶ms) override { + if (name == "get_cutoff") { + return m_ia_si.get()->cut; + } + return InteractionPotentialInterface::do_call_method( + name, params); + } +}; +#endif // LJCOS2 + class NonBondedInteractionHandle : public AutoParameters { std::array m_types = {-1, -1}; @@ -235,6 +270,9 @@ class NonBondedInteractionHandle #ifdef LJCOS std::shared_ptr m_ljcos; #endif +#ifdef LJCOS2 + std::shared_ptr m_ljcos2; +#endif template auto make_autoparameter(std::shared_ptr &member, const char *key) const { @@ -261,6 +299,9 @@ class NonBondedInteractionHandle #endif #ifdef LJCOS make_autoparameter(m_ljcos, "lennard_jones_cos"), +#endif +#ifdef LJCOS2 + make_autoparameter(m_ljcos2, "lennard_jones_cos2"), #endif }); } @@ -311,6 +352,10 @@ class NonBondedInteractionHandle #ifdef LJCOS set_member(m_ljcos, "lennard_jones_cos", "Interactions::InteractionLJcos", params); +#endif +#ifdef LJCOS2 + set_member(m_ljcos2, "lennard_jones_cos2", + "Interactions::InteractionLJcos2", params); #endif } diff --git a/src/script_interface/interactions/initialize.cpp b/src/script_interface/interactions/initialize.cpp index 2c5e0e60639..1e63b0136c1 100644 --- a/src/script_interface/interactions/initialize.cpp +++ b/src/script_interface/interactions/initialize.cpp @@ -62,6 +62,9 @@ void initialize(Utils::Factory *om) { #ifdef LJCOS om->register_new("Interactions::InteractionLJcos"); #endif +#ifdef LJCOS2 + om->register_new("Interactions::InteractionLJcos2"); +#endif #ifdef WCA om->register_new("Interactions::InteractionWCA"); #endif diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index a45d7a08fbe..c275e338973 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -254,6 +254,13 @@ def test_lj_cos_exceptions(self): {"epsilon": 1., "sigma": 1., "cutoff": 1.5, "offset": 0.2}, ("epsilon", "sigma")) + @utx.skipIfMissingFeatures("LJCOS2") + def test_lj_cos2_exceptions(self): + self.check_potential_exceptions( + espressomd.interactions.LennardJonesCos2Interaction, + {"epsilon": 1., "sigma": 1., "width": 1.5, "offset": 0.2}, + ("epsilon", "sigma")) + if __name__ == "__main__": ut.main() From 9cdf1d98b8dcbbdb9e525a3af9e7d4f1a1de8c5d Mon Sep 17 00:00:00 2001 From: Julian Hossbach Date: Tue, 30 Aug 2022 11:36:16 +0200 Subject: [PATCH 51/85] Rewrite Gaussian interaction interface --- src/core/nonbonded_interactions/gaussian.cpp | 29 +++----- src/core/nonbonded_interactions/gaussian.hpp | 4 -- .../nonbonded_interaction_data.cpp | 2 +- .../nonbonded_interaction_data.hpp | 3 + src/python/espressomd/interactions.pxd | 12 ---- src/python/espressomd/interactions.pyx | 69 ++++++------------- .../interactions/NonBondedInteraction.hpp | 33 +++++++++ .../interactions/initialize.cpp | 3 + .../interactions_non-bonded_interface.py | 8 +++ 9 files changed, 79 insertions(+), 84 deletions(-) diff --git a/src/core/nonbonded_interactions/gaussian.cpp b/src/core/nonbonded_interactions/gaussian.cpp index 51643bcfa27..97f0e8c20aa 100644 --- a/src/core/nonbonded_interactions/gaussian.cpp +++ b/src/core/nonbonded_interactions/gaussian.cpp @@ -25,26 +25,17 @@ #include "gaussian.hpp" #ifdef GAUSSIAN -#include "interactions.hpp" #include "nonbonded_interaction_data.hpp" -#include +#include -int gaussian_set_params(int part_type_a, int part_type_b, double eps, - double sig, double cut) { - IA_parameters *data = get_ia_param_safe(part_type_a, part_type_b); - - if (!data) - return ES_ERROR; - - data->gaussian.eps = eps; - data->gaussian.sig = sig; - data->gaussian.cut = cut; - - /* broadcast interaction parameters */ - mpi_bcast_ia_params(part_type_a, part_type_b); - - return ES_OK; +Gaussian_Parameters::Gaussian_Parameters(double eps, double sig, double cut) + : eps{eps}, sig{sig}, cut{cut} { + if (eps < 0.) { + throw std::domain_error("Gaussian parameter 'eps' has to be >= 0"); + } + if (sig < 0.) { + throw std::domain_error("Gaussian parameter 'sig' has to be >= 0"); + } } - -#endif +#endif // GAUSSIAN diff --git a/src/core/nonbonded_interactions/gaussian.hpp b/src/core/nonbonded_interactions/gaussian.hpp index fd004b13cc6..71f7daa369b 100644 --- a/src/core/nonbonded_interactions/gaussian.hpp +++ b/src/core/nonbonded_interactions/gaussian.hpp @@ -31,16 +31,12 @@ #include "nonbonded_interaction_data.hpp" -#include #include #include #ifdef GAUSSIAN -int gaussian_set_params(int part_type_a, int part_type_b, double eps, - double sig, double cut); - /** Calculate Gaussian force factor */ inline double gaussian_pair_force_factor(IA_parameters const &ia_params, double dist) { diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index b2cf2747d14..fa1bf31c599 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -163,7 +163,7 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { #endif #ifdef GAUSSIAN - max_cut_current = std::max(max_cut_current, data.gaussian.cut); + max_cut_current = std::max(max_cut_current, data.gaussian.max_cutoff()); #endif #ifdef BMHTF_NACL diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index 7e807f66d70..cecab1f511b 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -101,6 +101,9 @@ struct Gaussian_Parameters { double eps = 0.0; double sig = 1.0; double cut = INACTIVE_CUTOFF; + Gaussian_Parameters() = default; + Gaussian_Parameters(double eps, double sig, double cut); + double max_cutoff() const { return cut; } }; /** BMHTF NaCl potential */ diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index 926a8c782ba..f2cdbfd9c72 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -64,11 +64,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": double eps double sig - cdef struct Gaussian_Parameters: - double eps - double sig - double cut - cdef struct BMHTF_Parameters: double A double B @@ -146,8 +141,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": Hertzian_Parameters hertzian - Gaussian_Parameters gaussian - DPDParameters dpd_radial DPDParameters dpd_trans @@ -219,11 +212,6 @@ IF HERTZIAN: int hertzian_set_params(int part_type_a, int part_type_b, double eps, double sig) -IF GAUSSIAN: - cdef extern from "nonbonded_interactions/gaussian.hpp": - int gaussian_set_params(int part_type_a, int part_type_b, - double eps, double sig, double cut) - IF DPD: cdef extern from "dpd.hpp": int dpd_set_params(int part_type_a, int part_type_b, diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index c21cafb3fe2..b3e008f03f7 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -1442,44 +1442,35 @@ IF HERTZIAN == 1: IF GAUSSIAN == 1: - cdef class GaussianInteraction(NonBondedInteraction): + @script_interface_register + class GaussianInteraction(NewNonBondedInteraction): + """Gaussian interaction. - def validate_params(self): - """Check that parameters are valid. + Methods + ------- + set_params() + Set or update parameters for the interaction. + Parameters marked as required become optional once the + interaction has been activated for the first time; + subsequent calls to this method update the existing values. - """ - if self._params["eps"] < 0: - raise ValueError("Gaussian eps a has to be >=0") - if self._params["sig"] < 0: - raise ValueError("Gaussian sig has to be >=0") - if self._params["offset"] < 0: - raise ValueError("Gaussian offset has to be >=0") + Parameters + ---------- + eps : :obj:`float` + Overlap energy epsilon. + sig : :obj:`float` + Variance sigma of the Gaussian interaction. + cutoff : :obj:`float` + Cutoff distance of the interaction. + """ - def _get_params_from_es_core(self): - cdef IA_parameters * ia_params - ia_params = get_ia_param_safe( - self._part_types[0], - self._part_types[1]) - return { - "eps": ia_params.gaussian.eps, - "sig": ia_params.gaussian.sig, - "cutoff": ia_params.gaussian.cut - } + _so_name = "Interactions::InteractionGaussian" def is_active(self): """Check if interaction is active. """ - return (self._params["eps"] > 0) - - def _set_params_in_es_core(self): - if gaussian_set_params(self._part_types[0], - self._part_types[1], - self._params["eps"], - self._params["sig"], - self._params["cutoff"]): - raise Exception( - "Could not set Gaussian interaction parameters") + return self.eps > 0. def default_params(self): """Python dictionary of default parameters. @@ -1493,22 +1484,6 @@ IF GAUSSIAN == 1: """ return "Gaussian" - def set_params(self, **kwargs): - """ - Set parameters for the Gaussian interaction. - - Parameters - ---------- - eps : :obj:`float` - Overlap energy epsilon. - sig : :obj:`float` - Variance sigma of the Gaussian interaction. - cutoff : :obj:`float` - Cutoff distance of the interaction. - - """ - super().set_params(**kwargs) - def valid_keys(self): """All parameters that can be set. @@ -1565,8 +1540,6 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): self.buckingham = BuckinghamInteraction(_type1, _type2) IF HERTZIAN: self.hertzian = HertzianInteraction(_type1, _type2) - IF GAUSSIAN: - self.gaussian = GaussianInteraction(_type1, _type2) IF TABULATED: self.tabulated = TabulatedNonBonded(_type1, _type2) IF GAY_BERNE: diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index f78c09e6f9f..704f35c97eb 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -257,6 +257,29 @@ class InteractionLJcos2 }; #endif // LJCOS2 +#ifdef GAUSSIAN +class InteractionGaussian + : public InteractionPotentialInterface<::Gaussian_Parameters> { +protected: + CoreInteraction IA_parameters::*get_ptr_offset() const override { + return &::IA_parameters::gaussian; + } + +public: + InteractionGaussian() { + add_parameters({ + make_autoparameter(&CoreInteraction::eps, "eps"), + make_autoparameter(&CoreInteraction::sig, "sig"), + make_autoparameter(&CoreInteraction::cut, "cutoff"), + }); + } + void make_new_instance(VariantMap const ¶ms) override { + m_ia_si = make_shared_from_args( + params, "eps", "sig", "cutoff"); + } +}; +#endif // GAUSSIAN + class NonBondedInteractionHandle : public AutoParameters { std::array m_types = {-1, -1}; @@ -273,6 +296,9 @@ class NonBondedInteractionHandle #ifdef LJCOS2 std::shared_ptr m_ljcos2; #endif +#ifdef GAUSSIAN + std::shared_ptr m_gaussian; +#endif template auto make_autoparameter(std::shared_ptr &member, const char *key) const { @@ -302,6 +328,9 @@ class NonBondedInteractionHandle #endif #ifdef LJCOS2 make_autoparameter(m_ljcos2, "lennard_jones_cos2"), +#endif +#ifdef GAUSSIAN + make_autoparameter(m_gaussian, "gaussian"), #endif }); } @@ -356,6 +385,10 @@ class NonBondedInteractionHandle #ifdef LJCOS2 set_member(m_ljcos2, "lennard_jones_cos2", "Interactions::InteractionLJcos2", params); +#endif +#ifdef GAUSSIAN + set_member( + m_gaussian, "gaussian", "Interactions::InteractionGaussian", params); #endif } diff --git a/src/script_interface/interactions/initialize.cpp b/src/script_interface/interactions/initialize.cpp index 1e63b0136c1..507858d0515 100644 --- a/src/script_interface/interactions/initialize.cpp +++ b/src/script_interface/interactions/initialize.cpp @@ -68,6 +68,9 @@ void initialize(Utils::Factory *om) { #ifdef WCA om->register_new("Interactions::InteractionWCA"); #endif +#ifdef GAUSSIAN + om->register_new("Interactions::InteractionGaussian"); +#endif } } // namespace Interactions } // namespace ScriptInterface diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index c275e338973..a22311dffc6 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -261,6 +261,14 @@ def test_lj_cos2_exceptions(self): {"epsilon": 1., "sigma": 1., "width": 1.5, "offset": 0.2}, ("epsilon", "sigma")) + @utx.skipIfMissingFeatures("GAUSSIAN") + def test_gaussian_exceptions(self): + self.check_potential_exceptions( + espressomd.interactions.GaussianInteraction, + {"eps": 1., "sig": 1., "cutoff": 1.5}, + ("eps", "sig") + ) + if __name__ == "__main__": ut.main() From a42a5864065ba2bc6c86b9685e8421815d9bda0a Mon Sep 17 00:00:00 2001 From: Julian Hossbach Date: Tue, 30 Aug 2022 16:14:18 +0200 Subject: [PATCH 52/85] Rewrite Hertzian interaction interface --- src/core/nonbonded_interactions/hertzian.cpp | 24 +++---- src/core/nonbonded_interactions/hertzian.hpp | 5 -- .../nonbonded_interaction_data.cpp | 2 +- .../nonbonded_interaction_data.hpp | 3 + src/python/espressomd/interactions.pxd | 11 ---- src/python/espressomd/interactions.pyx | 62 ++++++------------- .../interactions/NonBondedInteraction.hpp | 32 ++++++++++ .../interactions/initialize.cpp | 3 + .../interactions_non-bonded_interface.py | 8 +++ 9 files changed, 73 insertions(+), 77 deletions(-) diff --git a/src/core/nonbonded_interactions/hertzian.cpp b/src/core/nonbonded_interactions/hertzian.cpp index 025f513b9e5..4b3439f4982 100644 --- a/src/core/nonbonded_interactions/hertzian.cpp +++ b/src/core/nonbonded_interactions/hertzian.cpp @@ -25,25 +25,15 @@ #include "hertzian.hpp" #ifdef HERTZIAN -#include "interactions.hpp" #include "nonbonded_interaction_data.hpp" -#include +#include -int hertzian_set_params(int part_type_a, int part_type_b, double eps, - double sig) { - IA_parameters *data = get_ia_param_safe(part_type_a, part_type_b); - - if (!data) - return ES_ERROR; - - data->hertzian.eps = eps; - data->hertzian.sig = sig; - - /* broadcast interaction parameters */ - mpi_bcast_ia_params(part_type_a, part_type_b); - - return ES_OK; +Hertzian_Parameters::Hertzian_Parameters(double eps, double sig) + : eps{eps}, sig{sig} { + if (eps < 0.) { + throw std::domain_error("Hertzian parameter 'eps' has to be >= 0"); + } } -#endif +#endif // HERTZIAN diff --git a/src/core/nonbonded_interactions/hertzian.hpp b/src/core/nonbonded_interactions/hertzian.hpp index 6f45ce363d7..9b3698917a3 100644 --- a/src/core/nonbonded_interactions/hertzian.hpp +++ b/src/core/nonbonded_interactions/hertzian.hpp @@ -31,15 +31,10 @@ #include "nonbonded_interaction_data.hpp" -#include - #include #ifdef HERTZIAN -int hertzian_set_params(int part_type_a, int part_type_b, double eps, - double sig); - /** Calculate Hertzian force factor */ inline double hertzian_pair_force_factor(IA_parameters const &ia_params, double dist) { diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index fa1bf31c599..68ca1341910 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -159,7 +159,7 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { #endif #ifdef HERTZIAN - max_cut_current = std::max(max_cut_current, data.hertzian.sig); + max_cut_current = std::max(max_cut_current, data.hertzian.max_cutoff()); #endif #ifdef GAUSSIAN diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index cecab1f511b..c48b4823a82 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -94,6 +94,9 @@ struct SmoothStep_Parameters { struct Hertzian_Parameters { double eps = 0.0; double sig = INACTIVE_CUTOFF; + Hertzian_Parameters() = default; + Hertzian_Parameters(double eps, double sig); + double max_cutoff() const { return sig; } }; /** Gaussian potential */ diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index f2cdbfd9c72..53edb5645c4 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -60,10 +60,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": int n double k0 - cdef struct Hertzian_Parameters: - double eps - double sig - cdef struct BMHTF_Parameters: double A double B @@ -139,8 +135,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": Buckingham_Parameters buckingham - Hertzian_Parameters hertzian - DPDParameters dpd_radial DPDParameters dpd_trans @@ -207,11 +201,6 @@ IF SOFT_SPHERE: int soft_sphere_set_params(int part_type_a, int part_type_b, double a, double n, double cut, double offset) -IF HERTZIAN: - cdef extern from "nonbonded_interactions/hertzian.hpp": - int hertzian_set_params(int part_type_a, int part_type_b, - double eps, double sig) - IF DPD: cdef extern from "dpd.hpp": int dpd_set_params(int part_type_a, int part_type_b, diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index b3e008f03f7..a0c9673495a 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -1364,42 +1364,35 @@ IF SOFT_SPHERE == 1: """ return {"a", "n", "cutoff"} - IF HERTZIAN == 1: - cdef class HertzianInteraction(NonBondedInteraction): + @script_interface_register + class HertzianInteraction(NewNonBondedInteraction): + """Hertzian interaction. - def validate_params(self): - """Check that parameters are valid. + Methods + ------- + set_params() + Set or update parameters for the interaction. + Parameters marked as required become optional once the + interaction has been activated for the first time; + subsequent calls to this method update the existing values. - """ - if self._params["eps"] < 0: - raise ValueError("Hertzian eps a has to be >=0") - if self._params["sig"] < 0: - raise ValueError("Hertzian sig has to be >=0") + Parameters + ---------- + eps : :obj:`float` + Magnitude of the interaction. + sig : :obj:`float` + Interaction length scale. + """ - def _get_params_from_es_core(self): - cdef IA_parameters * ia_params - ia_params = get_ia_param_safe( - self._part_types[0], - self._part_types[1]) - return { - "eps": ia_params.hertzian.eps, - "sig": ia_params.hertzian.sig - } + _so_name = "Interactions::InteractionHertzian" def is_active(self): """Check if interaction is active. """ - return (self._params["eps"] > 0) - - def _set_params_in_es_core(self): - if hertzian_set_params(self._part_types[0], - self._part_types[1], - self._params["eps"], - self._params["sig"]): - raise Exception("Could not set Hertzian parameters") + return self.eps > 0. def default_params(self): """Python dictionary of default parameters. @@ -1413,21 +1406,6 @@ IF HERTZIAN == 1: """ return "Hertzian" - def set_params(self, **kwargs): - """ - Set parameters for the Hertzian interaction. - - Parameters - ---------- - eps : :obj:`float` - Magnitude of the interaction. - sig : :obj:`float` - Parameter sigma. Determines the length over which the potential - decays. - - """ - super().set_params(**kwargs) - def valid_keys(self): """All parameters that can be set. @@ -1538,8 +1516,6 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): self.morse = MorseInteraction(_type1, _type2) IF BUCKINGHAM: self.buckingham = BuckinghamInteraction(_type1, _type2) - IF HERTZIAN: - self.hertzian = HertzianInteraction(_type1, _type2) IF TABULATED: self.tabulated = TabulatedNonBonded(_type1, _type2) IF GAY_BERNE: diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index 704f35c97eb..4929a67f822 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -257,6 +257,28 @@ class InteractionLJcos2 }; #endif // LJCOS2 +#ifdef HERTZIAN +class InteractionHertzian + : public InteractionPotentialInterface<::Hertzian_Parameters> { +protected: + CoreInteraction IA_parameters::*get_ptr_offset() const override { + return &::IA_parameters::hertzian; + } + +public: + InteractionHertzian() { + add_parameters({ + make_autoparameter(&CoreInteraction::eps, "eps"), + make_autoparameter(&CoreInteraction::sig, "sig"), + }); + } + void make_new_instance(VariantMap const ¶ms) override { + m_ia_si = make_shared_from_args( + params, "eps", "sig"); + } +}; +#endif // HERTZIAN + #ifdef GAUSSIAN class InteractionGaussian : public InteractionPotentialInterface<::Gaussian_Parameters> { @@ -296,6 +318,9 @@ class NonBondedInteractionHandle #ifdef LJCOS2 std::shared_ptr m_ljcos2; #endif +#ifdef HERTZIAN + std::shared_ptr m_hertzian; +#endif #ifdef GAUSSIAN std::shared_ptr m_gaussian; #endif @@ -329,6 +354,9 @@ class NonBondedInteractionHandle #ifdef LJCOS2 make_autoparameter(m_ljcos2, "lennard_jones_cos2"), #endif +#ifdef HERTZIAN + make_autoparameter(m_hertzian, "hertzian"), +#endif #ifdef GAUSSIAN make_autoparameter(m_gaussian, "gaussian"), #endif @@ -386,6 +414,10 @@ class NonBondedInteractionHandle set_member(m_ljcos2, "lennard_jones_cos2", "Interactions::InteractionLJcos2", params); #endif +#ifdef HERTZIAN + set_member( + m_hertzian, "hertzian", "Interactions::InteractionHertzian", params); +#endif #ifdef GAUSSIAN set_member( m_gaussian, "gaussian", "Interactions::InteractionGaussian", params); diff --git a/src/script_interface/interactions/initialize.cpp b/src/script_interface/interactions/initialize.cpp index 507858d0515..bf1208c89c2 100644 --- a/src/script_interface/interactions/initialize.cpp +++ b/src/script_interface/interactions/initialize.cpp @@ -68,6 +68,9 @@ void initialize(Utils::Factory *om) { #ifdef WCA om->register_new("Interactions::InteractionWCA"); #endif +#ifdef HERTZIAN + om->register_new("Interactions::InteractionHertzian"); +#endif #ifdef GAUSSIAN om->register_new("Interactions::InteractionGaussian"); #endif diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index a22311dffc6..b128fb2b9f6 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -261,6 +261,14 @@ def test_lj_cos2_exceptions(self): {"epsilon": 1., "sigma": 1., "width": 1.5, "offset": 0.2}, ("epsilon", "sigma")) + @utx.skipIfMissingFeatures("HERTZIAN") + def test_hertzian_exceptions(self): + self.check_potential_exceptions( + espressomd.interactions.HertzianInteraction, + {"eps": 1., "sig": 1.}, + ("eps",) + ) + @utx.skipIfMissingFeatures("GAUSSIAN") def test_gaussian_exceptions(self): self.check_potential_exceptions( From 8576bf891fe4259f3081501b1268b8777b4cb7bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 5 Sep 2022 22:31:04 +0200 Subject: [PATCH 53/85] Rewrite generic Lennard-Jones interaction interface --- src/core/nonbonded_interactions/ljgen.cpp | 55 +++---- src/core/nonbonded_interactions/ljgen.hpp | 23 +-- .../nonbonded_interaction_data.cpp | 3 +- .../nonbonded_interaction_data.hpp | 20 ++- src/python/espressomd/interactions.pxd | 29 ---- src/python/espressomd/interactions.pyx | 140 +++++------------- .../interactions/NonBondedInteraction.hpp | 65 ++++++++ .../interactions/initialize.cpp | 3 + testsuite/python/interactions_non-bonded.py | 29 ++-- .../interactions_non-bonded_interface.py | 19 +++ 10 files changed, 191 insertions(+), 195 deletions(-) diff --git a/src/core/nonbonded_interactions/ljgen.cpp b/src/core/nonbonded_interactions/ljgen.cpp index 862d0dfef81..51feb1bc448 100644 --- a/src/core/nonbonded_interactions/ljgen.cpp +++ b/src/core/nonbonded_interactions/ljgen.cpp @@ -25,43 +25,36 @@ #include "ljgen.hpp" #ifdef LENNARD_JONES_GENERIC -#include "interactions.hpp" #include "nonbonded_interaction_data.hpp" -#include - -int ljgen_set_params(int part_type_a, int part_type_b, double eps, double sig, - double cut, double shift, double offset, double a1, - double a2, double b1, double b2 +#include +LJGen_Parameters::LJGen_Parameters(double epsilon, double sigma, double cutoff, + double shift, double offset, #ifdef LJGEN_SOFTCORE - , - double lambda, double softrad + double lam, double delta, #endif -) { - IA_parameters *data = get_ia_param_safe(part_type_a, part_type_b); - - if (!data) - return ES_ERROR; - - data->ljgen.eps = eps; - data->ljgen.sig = sig; - data->ljgen.cut = cut; - data->ljgen.shift = shift; - data->ljgen.offset = offset; - data->ljgen.a1 = a1; - data->ljgen.a2 = a2; - data->ljgen.b1 = b1; - data->ljgen.b2 = b2; + double e1, double e2, double b1, double b2) + : eps{epsilon}, sig{sigma}, cut{cutoff}, shift{shift}, offset{offset}, #ifdef LJGEN_SOFTCORE - data->ljgen.lambda1 = lambda; - data->ljgen.softrad = softrad; + lambda{lam}, softrad{delta}, #endif - - /* broadcast interaction parameters */ - mpi_bcast_ia_params(part_type_a, part_type_b); - - return ES_OK; + a1{e1}, a2{e2}, b1{b1}, b2{b2} { + if (epsilon < 0.) { + throw std::domain_error("Generic LJ parameter 'epsilon' has to be >= 0"); + } + if (sigma < 0.) { + throw std::domain_error("Generic LJ parameter 'sigma' has to be >= 0"); + } +#ifdef LJGEN_SOFTCORE + if (delta < 0.) { + throw std::domain_error("Generic LJ parameter 'delta' has to be >= 0"); + } + if (lam < 0. or lam > 1.) { + throw std::domain_error( + "Generic LJ parameter 'lam' has to be in the range [0, 1]"); + } +#endif // LJGEN_SOFTCORE } -#endif +#endif // LENNARD_JONES_GENERIC diff --git a/src/core/nonbonded_interactions/ljgen.hpp b/src/core/nonbonded_interactions/ljgen.hpp index b0a734ff514..c753ba1db20 100644 --- a/src/core/nonbonded_interactions/ljgen.hpp +++ b/src/core/nonbonded_interactions/ljgen.hpp @@ -47,24 +47,15 @@ #include -int ljgen_set_params(int part_type_a, int part_type_b, double eps, double sig, - double cut, double shift, double offset, double a1, - double a2, double b1, double b2 -#ifdef LJGEN_SOFTCORE - , - double lambda, double softrad -#endif -); - /** Calculate Lennard-Jones force factor */ inline double ljgen_pair_force_factor(IA_parameters const &ia_params, double dist) { - if (dist < (ia_params.ljgen.cut + ia_params.ljgen.offset)) { + if (dist < ia_params.ljgen.max_cutoff()) { auto r_off = dist - ia_params.ljgen.offset; #ifdef LJGEN_SOFTCORE r_off *= r_off; - r_off += Utils::sqr(ia_params.ljgen.sig) * (1.0 - ia_params.ljgen.lambda1) * + r_off += Utils::sqr(ia_params.ljgen.sig) * (1.0 - ia_params.ljgen.lambda) * ia_params.ljgen.softrad; r_off = sqrt(r_off); #else @@ -73,7 +64,7 @@ inline double ljgen_pair_force_factor(IA_parameters const &ia_params, auto const frac = ia_params.ljgen.sig / r_off; auto const fac = ia_params.ljgen.eps #ifdef LJGEN_SOFTCORE - * ia_params.ljgen.lambda1 * + * ia_params.ljgen.lambda * (dist - ia_params.ljgen.offset) / r_off #endif * (ia_params.ljgen.b1 * ia_params.ljgen.a1 * @@ -88,11 +79,11 @@ inline double ljgen_pair_force_factor(IA_parameters const &ia_params, /** Calculate Lennard-Jones energy */ inline double ljgen_pair_energy(IA_parameters const &ia_params, double dist) { - if (dist < (ia_params.ljgen.cut + ia_params.ljgen.offset)) { + if (dist < ia_params.ljgen.max_cutoff()) { auto r_off = dist - ia_params.ljgen.offset; #ifdef LJGEN_SOFTCORE r_off *= r_off; - r_off += pow(ia_params.ljgen.sig, 2) * (1.0 - ia_params.ljgen.lambda1) * + r_off += pow(ia_params.ljgen.sig, 2) * (1.0 - ia_params.ljgen.lambda) * ia_params.ljgen.softrad; r_off = sqrt(r_off); #else @@ -101,7 +92,7 @@ inline double ljgen_pair_energy(IA_parameters const &ia_params, double dist) { auto const frac = ia_params.ljgen.sig / r_off; auto const fac = ia_params.ljgen.eps #ifdef LJGEN_SOFTCORE - * ia_params.ljgen.lambda1 + * ia_params.ljgen.lambda #endif * (ia_params.ljgen.b1 * pow(frac, ia_params.ljgen.a1) - ia_params.ljgen.b2 * pow(frac, ia_params.ljgen.a2) + @@ -112,4 +103,4 @@ inline double ljgen_pair_energy(IA_parameters const &ia_params, double dist) { } #endif // LENNARD_JONES_GENERIC -#endif // CORE_NB_IA_LJGEN_HPP +#endif diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index 68ca1341910..c2801e512ba 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -150,8 +150,7 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { #endif #ifdef LENNARD_JONES_GENERIC - max_cut_current = - std::max(max_cut_current, (data.ljgen.cut + data.ljgen.offset)); + max_cut_current = std::max(max_cut_current, data.ljgen.max_cutoff()); #endif #ifdef SMOOTH_STEP diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index c48b4823a82..922638e3b43 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -72,12 +73,27 @@ struct LJGen_Parameters { double cut = INACTIVE_CUTOFF; double shift = 0.0; double offset = 0.0; + double lambda = 1.0; + double softrad = 0.0; double a1 = 0.0; double a2 = 0.0; double b1 = 0.0; double b2 = 0.0; - double lambda1 = 1.0; - double softrad = 0.0; + LJGen_Parameters() = default; + LJGen_Parameters(double epsilon, double sigma, double cutoff, double shift, + double offset, +#ifdef LJGEN_SOFTCORE + double lam, double delta, +#endif + double e1, double e2, double b1, double b2); + double get_auto_shift() const { + auto auto_shift = 0.; + if (cut != 0.) { + auto_shift = b2 * std::pow(sig / cut, a2) - b1 * std::pow(sig / cut, a1); + } + return auto_shift; + } + double max_cutoff() const { return cut + offset; } }; /** smooth step potential */ diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index 53edb5645c4..839de598f65 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -39,19 +39,6 @@ cdef extern from "TabulatedPotential.hpp": vector[double] force_tab cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": - cdef struct LJGen_Parameters: - double eps - double sig - double cut - double shift - double offset - double a1 - double a2 - double b1 - double b2 - double lambda1 - double softrad - cdef struct SmoothStep_Parameters: double eps double sig @@ -119,8 +106,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": cdef struct IA_parameters: - LJGen_Parameters ljgen - SoftSphere_Parameters soft_sphere TabulatedPotential tab @@ -158,20 +143,6 @@ IF THOLE: cdef extern from "nonbonded_interactions/thole.hpp": int thole_set_params(int part_type_a, int part_type_b, double scaling_coeff, double q1q2) -IF LENNARD_JONES_GENERIC: - cdef extern from "nonbonded_interactions/ljgen.hpp": - IF LJGEN_SOFTCORE: - cdef int ljgen_set_params(int part_type_a, int part_type_b, - double eps, double sig, double cut, - double shift, double offset, - double a1, double a2, double b1, double b2, - double genlj_lambda, double softrad) - ELSE: - cdef int ljgen_set_params(int part_type_a, int part_type_b, - double eps, double sig, double cut, - double shift, double offset, - double a1, double a2, double b1, double b2) - IF SMOOTH_STEP: cdef extern from "nonbonded_interactions/smooth_step.hpp": int smooth_step_set_params(int part_type_a, int part_type_b, diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index a0c9673495a..9fba81af9a6 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -409,109 +409,18 @@ IF WCA == 1: IF LENNARD_JONES_GENERIC == 1: - cdef class GenericLennardJonesInteraction(NonBondedInteraction): - - def validate_params(self): - """Check that parameters are valid. - - Raises - ------ - ValueError - If not true. - """ - if self._params["epsilon"] < 0: - raise ValueError("Generic Lennard-Jones epsilon has to be >=0") - if self._params["sigma"] < 0: - raise ValueError("Generic Lennard-Jones sigma has to be >=0") - if self._params["cutoff"] < 0: - raise ValueError("Generic Lennard-Jones cutoff has to be >=0") - IF LJGEN_SOFTCORE: - if self._params["delta"] < 0: - raise ValueError( - "Generic Lennard-Jones delta has to be >=0") - if self._params["lam"] < 0 or self._params["lam"] > 1: - raise ValueError( - "Generic Lennard-Jones lam has to be in the range [0,1]") - - def _get_params_from_es_core(self): - cdef IA_parameters * ia_params - ia_params = get_ia_param_safe( - self._part_types[0], - self._part_types[1]) - return { - "epsilon": ia_params.ljgen.eps, - "sigma": ia_params.ljgen.sig, - "cutoff": ia_params.ljgen.cut, - "shift": ia_params.ljgen.shift, - "offset": ia_params.ljgen.offset, - "e1": ia_params.ljgen.a1, - "e2": ia_params.ljgen.a2, - "b1": ia_params.ljgen.b1, - "b2": ia_params.ljgen.b2, - "lam": ia_params.ljgen.lambda1, - "delta": ia_params.ljgen.softrad - } - - def is_active(self): - """Check if interaction is active. - - """ - return (self._params["epsilon"] > 0) - - def _set_params_in_es_core(self): - # Handle the case of shift="auto" - if self._params["shift"] == "auto": - self._params["shift"] = -( - self._params["b1"] * (self._params["sigma"] / self._params["cutoff"])**self._params["e1"] - - self._params["b2"] * (self._params["sigma"] / self._params["cutoff"])**self._params["e2"]) - IF LJGEN_SOFTCORE: - if ljgen_set_params(self._part_types[0], self._part_types[1], - self._params["epsilon"], - self._params["sigma"], - self._params["cutoff"], - self._params["shift"], - self._params["offset"], - self._params["e1"], - self._params["e2"], - self._params["b1"], - self._params["b2"], - self._params["lam"], - self._params["delta"]): - raise Exception( - "Could not set Generic Lennard-Jones parameters") - ELSE: - if ljgen_set_params(self._part_types[0], self._part_types[1], - self._params["epsilon"], - self._params["sigma"], - self._params["cutoff"], - self._params["shift"], - self._params["offset"], - self._params["e1"], - self._params["e2"], - self._params["b1"], - self._params["b2"], - ): - raise Exception( - "Could not set Generic Lennard-Jones parameters") - - def default_params(self): - """Python dictionary of default parameters. - - """ - IF LJGEN_SOFTCORE: - return {"delta": 0., "lam": 1.} - ELSE: - return {} - - def type_name(self): - """Name of interaction type. - - """ - return "GenericLennardJones" + @script_interface_register + class GenericLennardJonesInteraction(NewNonBondedInteraction): + """ + Generalized Lennard-Jones potential. - def set_params(self, **kwargs): - """ - Set parameters for the generic Lennard-Jones interaction. + Methods + ------- + set_params() + Set or update parameters for the interaction. + Parameters marked as required become optional once the + interaction has been activated for the first time; + subsequent calls to this method update the existing values. Parameters ---------- @@ -542,8 +451,30 @@ IF LENNARD_JONES_GENERIC == 1: ``LJGEN_SOFTCORE`` parameter lambda. Tune the strength of the interaction. + """ + + _so_name = "Interactions::InteractionLJGen" + + def is_active(self): + """Check if interaction is active. + """ - super().set_params(**kwargs) + return self.epsilon > 0. + + def default_params(self): + """Python dictionary of default parameters. + + """ + IF LJGEN_SOFTCORE: + return {"delta": 0., "lam": 1.} + ELSE: + return {} + + def type_name(self): + """Name of interaction type. + + """ + return "GenericLennardJones" def valid_keys(self): """All parameters that can be set. @@ -1505,9 +1436,6 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): # Here, add one line for each nonbonded ia IF SOFT_SPHERE: self.soft_sphere = SoftSphereInteraction(_type1, _type2) - IF LENNARD_JONES_GENERIC: - self.generic_lennard_jones = GenericLennardJonesInteraction( - _type1, _type2) IF SMOOTH_STEP: self.smooth_step = SmoothStepInteraction(_type1, _type2) IF BMHTF_NACL: diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index 4929a67f822..7fe6a846c30 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -196,6 +196,61 @@ class InteractionLJ : public InteractionPotentialInterface<::LJ_Parameters> { }; #endif // LENNARD_JONES +#ifdef LENNARD_JONES_GENERIC +class InteractionLJGen + : public InteractionPotentialInterface<::LJGen_Parameters> { +protected: + CoreInteraction IA_parameters::*get_ptr_offset() const override { + return &::IA_parameters::ljgen; + } + +public: + InteractionLJGen() { + add_parameters({ + make_autoparameter(&CoreInteraction::eps, "epsilon"), + make_autoparameter(&CoreInteraction::sig, "sigma"), + make_autoparameter(&CoreInteraction::cut, "cutoff"), + make_autoparameter(&CoreInteraction::shift, "shift"), + make_autoparameter(&CoreInteraction::offset, "offset"), +#ifdef LJGEN_SOFTCORE + make_autoparameter(&CoreInteraction::lambda, "lam"), + make_autoparameter(&CoreInteraction::softrad, "delta"), +#endif + make_autoparameter(&CoreInteraction::a1, "e1"), + make_autoparameter(&CoreInteraction::a2, "e2"), + make_autoparameter(&CoreInteraction::b1, "b1"), + make_autoparameter(&CoreInteraction::b2, "b2"), + }); + } + + void make_new_instance(VariantMap const ¶ms) override { + auto new_params = params; + auto const *shift_string = boost::get(¶ms.at("shift")); + if (shift_string != nullptr) { + if (*shift_string != "auto") { + throw std::invalid_argument( + "Generic LJ parameter 'shift' has to be 'auto' or a float"); + } + new_params["shift"] = 0.; + } + m_ia_si = make_shared_from_args( + new_params, "epsilon", "sigma", "cutoff", "shift", "offset", +#ifdef LJGEN_SOFTCORE + "lam", "delta", +#endif + "e1", "e2", "b1", "b2"); + if (shift_string != nullptr) { + m_ia_si->shift = m_ia_si->get_auto_shift(); + } + } +}; +#endif // LENNARD_JONES_GENERIC + #ifdef LJCOS class InteractionLJcos : public InteractionPotentialInterface<::LJcos_Parameters> { @@ -312,6 +367,9 @@ class NonBondedInteractionHandle #ifdef LENNARD_JONES std::shared_ptr m_lj; #endif +#ifdef LENNARD_JONES_GENERIC + std::shared_ptr m_ljgen; +#endif #ifdef LJCOS std::shared_ptr m_ljcos; #endif @@ -348,6 +406,9 @@ class NonBondedInteractionHandle #ifdef LENNARD_JONES make_autoparameter(m_lj, "lennard_jones"), #endif +#ifdef LENNARD_JONES_GENERIC + make_autoparameter(m_ljgen, "generic_lennard_jones"), +#endif #ifdef LJCOS make_autoparameter(m_ljcos, "lennard_jones_cos"), #endif @@ -406,6 +467,10 @@ class NonBondedInteractionHandle set_member(m_lj, "lennard_jones", "Interactions::InteractionLJ", params); #endif +#ifdef LENNARD_JONES_GENERIC + set_member(m_ljgen, "generic_lennard_jones", + "Interactions::InteractionLJGen", params); +#endif #ifdef LJCOS set_member(m_ljcos, "lennard_jones_cos", "Interactions::InteractionLJcos", params); diff --git a/src/script_interface/interactions/initialize.cpp b/src/script_interface/interactions/initialize.cpp index bf1208c89c2..b4359575250 100644 --- a/src/script_interface/interactions/initialize.cpp +++ b/src/script_interface/interactions/initialize.cpp @@ -59,6 +59,9 @@ void initialize(Utils::Factory *om) { #ifdef LENNARD_JONES om->register_new("Interactions::InteractionLJ"); #endif +#ifdef LENNARD_JONES_GENERIC + om->register_new("Interactions::InteractionLJGen"); +#endif #ifdef LJCOS om->register_new("Interactions::InteractionLJcos"); #endif diff --git a/testsuite/python/interactions_non-bonded.py b/testsuite/python/interactions_non-bonded.py index b5eaec6ec55..ba55bfced47 100644 --- a/testsuite/python/interactions_non-bonded.py +++ b/testsuite/python/interactions_non-bonded.py @@ -291,21 +291,32 @@ def assertItemsFractionAlmostEqual(self, a, b): @utx.skipIfMissingFeatures("LENNARD_JONES_GENERIC") def test_lj_generic(self): + params = {"epsilon": 2.12, + "sigma": 1.37, + "cutoff": 2.122, + "offset": 0.185, + "b1": 4.22, + "b2": 3.63, + "e1": 10.32, + "e2": 5.81, + "shift": -0.13} self.run_test("generic_lennard_jones", - {"epsilon": 2.12, - "sigma": 1.37, - "cutoff": 2.122, - "offset": 0.185, - "b1": 4.22, - "b2": 3.63, - "e1": 10.32, - "e2": 5.81, - "shift": -0.13}, + params, force_kernel=tests_common.lj_generic_force, energy_kernel=tests_common.lj_generic_potential, n_steps=231, force_kernel_needs_espressomd=True) + params["shift"] = "auto" + obj = espressomd.interactions.GenericLennardJonesInteraction(**params) + ref_shift = obj.b2 * (obj.sigma / obj.cutoff)**obj.e2 - \ + obj.b1 * (obj.sigma / obj.cutoff)**obj.e1 + self.assertAlmostEqual(obj.shift, ref_shift, delta=1e-10) + + params["cutoff"] = 0. + obj = espressomd.interactions.GenericLennardJonesInteraction(**params) + self.assertEqual(obj.shift, 0.) + # Test WCA Potential @utx.skipIfMissingFeatures("WCA") def test_wca(self): diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index b128fb2b9f6..3efde27b40d 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -247,6 +247,25 @@ def test_lj_exceptions(self): {"epsilon": 1., "sigma": 1., "cutoff": 1.5, "shift": 0.2}, ("epsilon", "sigma")) + @utx.skipIfMissingFeatures("LENNARD_JONES_GENERIC") + def test_ljgen_exceptions(self): + check_keys = ("epsilon", "sigma") + params = {"epsilon": 1., "sigma": 1., "cutoff": 1.5, "shift": 0.2, + "offset": 0.1, "b1": 1., "b2": 1., "e1": 12., "e2": 6.} + if espressomd.has_features(["LJGEN_SOFTCORE"]): + params["delta"] = 0.1 + params["lam"] = 0.3 + check_keys = ("delta", "lam", *check_keys) + self.check_potential_exceptions( + espressomd.interactions.GenericLennardJonesInteraction, + params, + check_keys) + with self.assertRaisesRegex(ValueError, f"Generic LJ parameter 'shift' has to be 'auto' or a float"): + invalid_params = params.copy() + invalid_params["shift"] = "unknown" + espressomd.interactions.GenericLennardJonesInteraction( + **invalid_params) + @utx.skipIfMissingFeatures("LJCOS") def test_lj_cos_exceptions(self): self.check_potential_exceptions( From 53368cc7622c0ea9ca5f83bc209a099a53e96346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 5 Sep 2022 23:37:38 +0200 Subject: [PATCH 54/85] Rewrite BMHTF interaction interface --- .../nonbonded_interactions/bmhtf-nacl.cpp | 47 ++++------ .../nonbonded_interactions/bmhtf-nacl.hpp | 6 +- .../nonbonded_interaction_data.cpp | 2 +- .../nonbonded_interaction_data.hpp | 4 + src/python/espressomd/interactions.pxd | 17 ---- src/python/espressomd/interactions.pyx | 88 ++++++------------- .../interactions/NonBondedInteraction.hpp | 38 ++++++++ .../interactions/initialize.cpp | 3 + .../interactions_non-bonded_interface.py | 8 ++ 9 files changed, 101 insertions(+), 112 deletions(-) diff --git a/src/core/nonbonded_interactions/bmhtf-nacl.cpp b/src/core/nonbonded_interactions/bmhtf-nacl.cpp index 74c2d0df9d0..e89fbf193df 100644 --- a/src/core/nonbonded_interactions/bmhtf-nacl.cpp +++ b/src/core/nonbonded_interactions/bmhtf-nacl.cpp @@ -25,35 +25,26 @@ #include "bmhtf-nacl.hpp" #ifdef BMHTF_NACL -#include "interactions.hpp" #include "nonbonded_interactions/nonbonded_interaction_data.hpp" -#include - -int BMHTF_set_params(int part_type_a, int part_type_b, double A, double B, - double C, double D, double sig, double cut) { - double shift, dist2, pw6; - IA_parameters *data = get_ia_param_safe(part_type_a, part_type_b); - - if (!data) - return ES_ERROR; - - dist2 = cut * cut; - pw6 = dist2 * dist2 * dist2; - shift = -(A * exp(B * (sig - cut)) - C / pw6 - D / pw6 / dist2); - - data->bmhtf.A = A; - data->bmhtf.B = B; - data->bmhtf.C = C; - data->bmhtf.D = D; - data->bmhtf.sig = sig; - data->bmhtf.cut = cut; - data->bmhtf.computed_shift = shift; - - /* broadcast interaction parameters */ - mpi_bcast_ia_params(part_type_a, part_type_b); - - return ES_OK; +#include + +#include + +BMHTF_Parameters::BMHTF_Parameters(double a, double b, double c, double d, + double sig, double cutoff) + : A{a}, B{b}, C{c}, D{d}, sig{sig}, cut{cutoff} { + if (a < 0.) { + throw std::domain_error("BMHTF parameter 'a' has to be >= 0"); + } + if (c < 0.) { + throw std::domain_error("BMHTF parameter 'c' has to be >= 0"); + } + if (d < 0.) { + throw std::domain_error("BMHTF parameter 'd' has to be >= 0"); + } + computed_shift = C / Utils::int_pow<6>(cut) + D / Utils::int_pow<8>(cut) - + A * std::exp(B * (sig - cut)); } -#endif +#endif // BMHTF_NACL diff --git a/src/core/nonbonded_interactions/bmhtf-nacl.hpp b/src/core/nonbonded_interactions/bmhtf-nacl.hpp index c2a5b87d110..91ff2179d20 100644 --- a/src/core/nonbonded_interactions/bmhtf-nacl.hpp +++ b/src/core/nonbonded_interactions/bmhtf-nacl.hpp @@ -33,14 +33,10 @@ #include "nonbonded_interaction_data.hpp" -#include #include #include -int BMHTF_set_params(int part_type_a, int part_type_b, double A, double B, - double C, double D, double sig, double cut); - /** Calculate BMHTF force factor */ inline double BMHTF_pair_force_factor(IA_parameters const &ia_params, double dist) { @@ -67,5 +63,5 @@ inline double BMHTF_pair_energy(IA_parameters const &ia_params, double dist) { return 0.0; } -#endif +#endif // BMHTF_NACL #endif diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index c2801e512ba..2abad44a70f 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -166,7 +166,7 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { #endif #ifdef BMHTF_NACL - max_cut_current = std::max(max_cut_current, data.bmhtf.cut); + max_cut_current = std::max(max_cut_current, data.bmhtf.max_cutoff()); #endif #ifdef MORSE diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index 922638e3b43..a2fcc9343ed 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -134,6 +134,10 @@ struct BMHTF_Parameters { double sig = 0.0; double cut = INACTIVE_CUTOFF; double computed_shift = 0.0; + BMHTF_Parameters() = default; + BMHTF_Parameters(double A, double B, double C, double D, double sig, + double cut); + double max_cutoff() const { return cut; } }; /** Morse potential */ diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index 839de598f65..f30569e5f29 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -47,15 +47,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": int n double k0 - cdef struct BMHTF_Parameters: - double A - double B - double C - double D - double sig - double cut - double computed_shift - cdef struct Morse_Parameters: double eps double alpha @@ -114,8 +105,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": SmoothStep_Parameters smooth_step - BMHTF_Parameters bmhtf - Morse_Parameters morse Buckingham_Parameters buckingham @@ -149,12 +138,6 @@ IF SMOOTH_STEP: double d, int n, double eps, double k0, double sig, double cut) -IF BMHTF_NACL: - cdef extern from "nonbonded_interactions/bmhtf-nacl.hpp": - int BMHTF_set_params(int part_type_a, int part_type_b, - double A, double B, double C, - double D, double sig, double cut) - IF MORSE: cdef extern from "nonbonded_interactions/morse.hpp": int morse_set_params(int part_type_a, int part_type_b, diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 9fba81af9a6..20ad80c9662 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -938,51 +938,41 @@ IF SMOOTH_STEP == 1: IF BMHTF_NACL == 1: - cdef class BMHTFInteraction(NonBondedInteraction): + @script_interface_register + class BMHTFInteraction(NewNonBondedInteraction): + """BMHTF interaction. - def validate_params(self): - """Check that parameters are valid. + Methods + ------- + set_params() + Set or update parameters for the interaction. + Parameters marked as required become optional once the + interaction has been activated for the first time; + subsequent calls to this method update the existing values. - """ - if self._params["a"] < 0: - raise ValueError("BMHTF a has to be >=0") - if self._params["c"] < 0: - raise ValueError("BMHTF c has to be >=0") - if self._params["d"] < 0: - raise ValueError("BMHTF d has to be >=0") - if self._params["cutoff"] < 0: - raise ValueError("BMHTF cutoff has to be >=0") + Parameters + ---------- + a : :obj:`float` + Magnitude of exponential part of the interaction. + b : :obj:`float` + Exponential factor of the interaction. + c : :obj:`float` + Magnitude of the term decaying with the sixth power of r. + d : :obj:`float` + Magnitude of the term decaying with the eighth power of r. + sig : :obj:`float` + Shift in the exponent. + cutoff : :obj:`float` + Cutoff distance of the interaction. + """ - def _get_params_from_es_core(self): - cdef IA_parameters * ia_params - ia_params = get_ia_param_safe(self._part_types[0], - self._part_types[1]) - return { - "a": ia_params.bmhtf.A, - "b": ia_params.bmhtf.B, - "c": ia_params.bmhtf.C, - "d": ia_params.bmhtf.D, - "sig": ia_params.bmhtf.sig, - "cutoff": ia_params.bmhtf.cut, - } + _so_name = "Interactions::InteractionBMHTF" def is_active(self): """Check if interaction is active. """ - return (self._params["a"] > 0) and ( - self._params["c"] > 0) and (self._params["d"] > 0) - - def _set_params_in_es_core(self): - if BMHTF_set_params(self._part_types[0], - self._part_types[1], - self._params["a"], - self._params["b"], - self._params["c"], - self._params["d"], - self._params["sig"], - self._params["cutoff"]): - raise Exception("Could not set BMHTF parameters") + return self.a > 0. and self.c > 0. and self.d > 0. def default_params(self): """Python dictionary of default parameters. @@ -996,28 +986,6 @@ IF BMHTF_NACL == 1: """ return "BMHTF" - def set_params(self, **kwargs): - """ - Set parameters for the BMHTF interaction. - - Parameters - ---------- - a : :obj:`float` - Magnitude of exponential part of the interaction. - b : :obj:`float` - Exponential factor of the interaction. - c : :obj:`float` - Magnitude of the term decaying with the sixth power of r. - d : :obj:`float` - Magnitude of the term decaying with the eighth power of r. - sig : :obj:`float` - Shift in the exponent. - cutoff : :obj:`float` - Cutoff distance of the interaction. - - """ - super().set_params(**kwargs) - def valid_keys(self): """All parameters that can be set. @@ -1438,8 +1406,6 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): self.soft_sphere = SoftSphereInteraction(_type1, _type2) IF SMOOTH_STEP: self.smooth_step = SmoothStepInteraction(_type1, _type2) - IF BMHTF_NACL: - self.bmhtf = BMHTFInteraction(_type1, _type2) IF MORSE: self.morse = MorseInteraction(_type1, _type2) IF BUCKINGHAM: diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index 7fe6a846c30..ccf4569a8bc 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -357,6 +357,34 @@ class InteractionGaussian }; #endif // GAUSSIAN +#ifdef BMHTF_NACL +class InteractionBMHTF + : public InteractionPotentialInterface<::BMHTF_Parameters> { +protected: + CoreInteraction IA_parameters::*get_ptr_offset() const override { + return &::IA_parameters::bmhtf; + } + +public: + InteractionBMHTF() { + add_parameters({ + make_autoparameter(&CoreInteraction::A, "a"), + make_autoparameter(&CoreInteraction::B, "b"), + make_autoparameter(&CoreInteraction::C, "c"), + make_autoparameter(&CoreInteraction::D, "d"), + make_autoparameter(&CoreInteraction::sig, "sig"), + make_autoparameter(&CoreInteraction::cut, "cutoff"), + }); + } + + void make_new_instance(VariantMap const ¶ms) override { + m_ia_si = make_shared_from_args( + params, "a", "b", "c", "d", "sig", "cutoff"); + } +}; +#endif // BMHTF_NACL + class NonBondedInteractionHandle : public AutoParameters { std::array m_types = {-1, -1}; @@ -382,6 +410,9 @@ class NonBondedInteractionHandle #ifdef GAUSSIAN std::shared_ptr m_gaussian; #endif +#ifdef BMHTF_NACL + std::shared_ptr m_bmhtf; +#endif template auto make_autoparameter(std::shared_ptr &member, const char *key) const { @@ -420,6 +451,9 @@ class NonBondedInteractionHandle #endif #ifdef GAUSSIAN make_autoparameter(m_gaussian, "gaussian"), +#endif +#ifdef BMHTF_NACL + make_autoparameter(m_bmhtf, "bmhtf"), #endif }); } @@ -486,6 +520,10 @@ class NonBondedInteractionHandle #ifdef GAUSSIAN set_member( m_gaussian, "gaussian", "Interactions::InteractionGaussian", params); +#endif +#ifdef BMHTF_NACL + set_member(m_bmhtf, "bmhtf", + "Interactions::InteractionBMHTF", params); #endif } diff --git a/src/script_interface/interactions/initialize.cpp b/src/script_interface/interactions/initialize.cpp index b4359575250..ab4c766f575 100644 --- a/src/script_interface/interactions/initialize.cpp +++ b/src/script_interface/interactions/initialize.cpp @@ -77,6 +77,9 @@ void initialize(Utils::Factory *om) { #ifdef GAUSSIAN om->register_new("Interactions::InteractionGaussian"); #endif +#ifdef BMHTF_NACL + om->register_new("Interactions::InteractionBMHTF"); +#endif } } // namespace Interactions } // namespace ScriptInterface diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index 3efde27b40d..4dea913dc48 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -296,6 +296,14 @@ def test_gaussian_exceptions(self): ("eps", "sig") ) + @utx.skipIfMissingFeatures("BMHTF_NACL") + def test_bmhtf_exceptions(self): + self.check_potential_exceptions( + espressomd.interactions.BMHTFInteraction, + {"a": 3., "b": 2., "c": 1., "d": 4., "sig": 0.13, "cutoff": 1.2}, + ("a", "c", "d") + ) + if __name__ == "__main__": ut.main() From c4b458cc1791e8132b29f231e618b467876101ab Mon Sep 17 00:00:00 2001 From: Patrick Kreissl Date: Tue, 6 Sep 2022 01:40:33 +0200 Subject: [PATCH 55/85] Add missing guard for ext_torque. --- src/core/Particle.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/Particle.hpp b/src/core/Particle.hpp index 4c9a21d5f0f..53948fb857a 100644 --- a/src/core/Particle.hpp +++ b/src/core/Particle.hpp @@ -492,8 +492,10 @@ struct Particle { // NOLINT(bugprone-exception-escape) auto &torque() { return f.torque; } auto const &omega() const { return m.omega; } auto &omega() { return m.omega; } +#ifdef EXTERNAL_FORCES auto const &ext_torque() const { return p.ext_torque; } auto &ext_torque() { return p.ext_torque; } +#endif // EXTERNAL_FORCES auto calc_director() const { return r.calc_director(); } #else // ROTATION bool can_rotate() const { return false; } From d0be79c52c3891d75af1fe53c68a00440800e7f5 Mon Sep 17 00:00:00 2001 From: Patrick Kreissl Date: Tue, 6 Sep 2022 02:20:02 +0200 Subject: [PATCH 56/85] Add missing feature guards in tests. --- src/core/unit_tests/Verlet_list_test.cpp | 13 +++++++++++-- testsuite/python/bond_breakage.py | 2 +- testsuite/python/lees_edwards.py | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/core/unit_tests/Verlet_list_test.cpp b/src/core/unit_tests/Verlet_list_test.cpp index 74d8bab9d31..3edecd75d15 100644 --- a/src/core/unit_tests/Verlet_list_test.cpp +++ b/src/core/unit_tests/Verlet_list_test.cpp @@ -95,6 +95,7 @@ void mpi_set_integrator_sd_local() { REGISTER_CALLBACK(mpi_set_integrator_sd_local) +#ifdef EXTERNAL_FORCES struct : public IntegratorHelper { void set_integrator() const override { mpi_set_thermo_switch(THERMO_OFF); @@ -105,6 +106,7 @@ struct : public IntegratorHelper { } char const *name() const override { return "SteepestDescent"; } } steepest_descent; +#endif // EXTERNAL_FORCES void mpi_set_integrator_vv_local() { set_integ_switch(INTEG_METHOD_NVT); } @@ -159,8 +161,11 @@ auto const propagators = Testing::velocity_verlet, #ifdef NPT Testing::velocity_verlet_npt, -#endif - Testing::steepest_descent}; +#endif // NPT +#ifdef EXTERNAL_FORCES + Testing::steepest_descent +#endif // EXTERNAL_FORCES + }; BOOST_TEST_DECORATOR(*utf::precondition(if_head_node())) BOOST_DATA_TEST_CASE_F(ParticleFactory, verlet_list_update, @@ -227,12 +232,16 @@ BOOST_DATA_TEST_CASE_F(ParticleFactory, verlet_list_update, { mpi_integrate(1, 0); auto const &p1 = get_particle_data(pid1); +#ifdef EXTERNAL_FORCES auto const &p2 = get_particle_data(pid2); BOOST_CHECK_CLOSE(p1.force()[0] - p1.ext_force()[0], 480., 1e-9); +#endif // EXTERNAL_FORCES BOOST_CHECK_CLOSE(p1.force()[1], 0., tol); BOOST_CHECK_CLOSE(p1.force()[2], 0., tol); +#ifdef EXTERNAL_FORCES BOOST_TEST(p1.force() - p1.ext_force() == -p2.force(), boost::test_tools::per_element()); +#endif // EXTERNAL_FORCES BOOST_CHECK_LT(get_dist_from_last_verlet_update(p1), skin / 2.); } } diff --git a/testsuite/python/bond_breakage.py b/testsuite/python/bond_breakage.py index 5ae4e6c9c96..138d679980a 100644 --- a/testsuite/python/bond_breakage.py +++ b/testsuite/python/bond_breakage.py @@ -162,7 +162,7 @@ def test_exceptions(self): system.integrator.run(1) -@utx.skipIfMissingFeatures("LENNARD_JONES") +@utx.skipIfMissingFeatures(["LENNARD_JONES", "COLLISION_DETECTION"]) class NetworkBreakage(BondBreakageCommon, ut.TestCase): @classmethod diff --git a/testsuite/python/lees_edwards.py b/testsuite/python/lees_edwards.py index b635724284b..adb39de1c49 100644 --- a/testsuite/python/lees_edwards.py +++ b/testsuite/python/lees_edwards.py @@ -449,7 +449,7 @@ def test_virt_sites(self): np.testing.assert_allclose( np.copy(p1.torque_lab), [0, 0, -2], atol=tol) - @utx.skipIfMissingFeatures(["VIRTUAL_SITES_RELATIVE", "ROTATION"]) + @utx.skipIfMissingFeatures(["VIRTUAL_SITES_RELATIVE", "ROTATION", "DPD"]) def test_virt_sites_interaction(self): """ A virtual site interacts with a real particle via a DPD interaction From 6173439832c5578487521c13982ccb04f3ee17cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 6 Sep 2022 12:48:30 +0200 Subject: [PATCH 57/85] Rewrite Morse interaction interface --- src/core/nonbonded_interactions/morse.cpp | 39 ++++------ src/core/nonbonded_interactions/morse.hpp | 8 +- .../nonbonded_interaction_data.cpp | 2 +- .../nonbonded_interaction_data.hpp | 5 +- src/python/espressomd/interactions.pxd | 15 ---- src/python/espressomd/interactions.pyx | 74 ++++++------------- .../interactions/NonBondedInteraction.hpp | 36 +++++++++ .../interactions/initialize.cpp | 3 + .../interactions_non-bonded_interface.py | 8 ++ 9 files changed, 90 insertions(+), 100 deletions(-) diff --git a/src/core/nonbonded_interactions/morse.cpp b/src/core/nonbonded_interactions/morse.cpp index f31d2335be5..f4465491b54 100644 --- a/src/core/nonbonded_interactions/morse.cpp +++ b/src/core/nonbonded_interactions/morse.cpp @@ -25,33 +25,20 @@ #include "morse.hpp" #ifdef MORSE -#include "interactions.hpp" #include "nonbonded_interaction_data.hpp" -#include - -int morse_set_params(int part_type_a, int part_type_b, double eps, double alpha, - double rmin, double cut) { - double add1, add2; - IA_parameters *data = get_ia_param_safe(part_type_a, part_type_b); - - if (!data) - return ES_ERROR; - - data->morse.eps = eps; - data->morse.alpha = alpha; - data->morse.rmin = rmin; - data->morse.cut = cut; - - /* calculate dependent parameter */ - add1 = exp(-2.0 * data->morse.alpha * (data->morse.cut - data->morse.rmin)); - add2 = 2.0 * exp(-data->morse.alpha * (data->morse.cut - data->morse.rmin)); - data->morse.rest = data->morse.eps * (add1 - add2); - - /* broadcast interaction parameters */ - mpi_bcast_ia_params(part_type_a, part_type_b); - - return ES_OK; +#include +#include + +Morse_Parameters::Morse_Parameters(double eps, double alpha, double rmin, + double cutoff) + : eps{eps}, alpha{alpha}, rmin{rmin}, cut{cutoff} { + if (eps < 0.) { + throw std::domain_error("Morse parameter 'eps' has to be >= 0"); + } + auto const add1 = std::exp(-2.0 * alpha * (cut - rmin)); + auto const add2 = 2.0 * std::exp(-alpha * (cut - rmin)); + rest = eps * (add1 - add2); } -#endif +#endif // MORSE diff --git a/src/core/nonbonded_interactions/morse.hpp b/src/core/nonbonded_interactions/morse.hpp index fab45cfe3a5..41252931b5e 100644 --- a/src/core/nonbonded_interactions/morse.hpp +++ b/src/core/nonbonded_interactions/morse.hpp @@ -33,14 +33,10 @@ #include "nonbonded_interaction_data.hpp" -#include #include #include -int morse_set_params(int part_type_a, int part_type_b, double eps, double alpha, - double rmin, double cut); - /** Calculate Morse force factor */ inline double morse_pair_force_factor(IA_parameters const &ia_params, double dist) { @@ -64,5 +60,5 @@ inline double morse_pair_energy(IA_parameters const &ia_params, double dist) { return 0.0; } -#endif /* ifdef MORSE */ -#endif /* ifdef CORE_NB_IA_MORSE_HPP */ +#endif // MORSE +#endif diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index 2abad44a70f..341bb5cf1d7 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -170,7 +170,7 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { #endif #ifdef MORSE - max_cut_current = std::max(max_cut_current, data.morse.cut); + max_cut_current = std::max(max_cut_current, data.morse.max_cutoff()); #endif #ifdef BUCKINGHAM diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index a2fcc9343ed..9f803b85621 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -142,11 +142,14 @@ struct BMHTF_Parameters { /** Morse potential */ struct Morse_Parameters { - double eps = INACTIVE_CUTOFF; + double eps = 0.; double alpha = INACTIVE_CUTOFF; double rmin = INACTIVE_CUTOFF; double cut = INACTIVE_CUTOFF; double rest = INACTIVE_CUTOFF; + Morse_Parameters() = default; + Morse_Parameters(double eps, double alpha, double rmin, double cutoff); + double max_cutoff() const { return cut; } }; /** Buckingham potential */ diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index f30569e5f29..5523c237e8d 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -47,13 +47,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": int n double k0 - cdef struct Morse_Parameters: - double eps - double alpha - double rmin - double cut - double rest - cdef struct Buckingham_Parameters: double A double B @@ -105,8 +98,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": SmoothStep_Parameters smooth_step - Morse_Parameters morse - Buckingham_Parameters buckingham DPDParameters dpd_radial @@ -138,12 +129,6 @@ IF SMOOTH_STEP: double d, int n, double eps, double k0, double sig, double cut) -IF MORSE: - cdef extern from "nonbonded_interactions/morse.hpp": - int morse_set_params(int part_type_a, int part_type_b, - double eps, double alpha, - double rmin, double cut) - IF BUCKINGHAM: cdef extern from "nonbonded_interactions/buckingham.hpp": int buckingham_set_params(int part_type_a, int part_type_b, diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 20ad80c9662..691062413c5 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -1000,45 +1000,37 @@ IF BMHTF_NACL == 1: IF MORSE == 1: - cdef class MorseInteraction(NonBondedInteraction): + @script_interface_register + class MorseInteraction(NewNonBondedInteraction): + """Morse interaction. - def validate_params(self): - """Check that parameters are valid. + Methods + ------- + set_params() + Set or update parameters for the interaction. + Parameters marked as required become optional once the + interaction has been activated for the first time; + subsequent calls to this method update the existing values. - """ - if self._params["eps"] < 0: - raise ValueError("Morse eps has to be >=0") - if self._params["offset"] < 0: - raise ValueError("Morse offset has to be >=0") - if self._params["cutoff"] < 0: - raise ValueError("Morse cutoff has to be >=0") + Parameters + ---------- + eps : :obj:`float` + The magnitude of the interaction. + alpha : :obj:`float` + Stiffness of the Morse interaction. + rmin : :obj:`float` + Distance of potential minimum + cutoff : :obj:`float`, optional + Cutoff distance of the interaction. + """ - def _get_params_from_es_core(self): - cdef IA_parameters * ia_params - ia_params = get_ia_param_safe( - self._part_types[0], - self._part_types[1]) - return { - "eps": ia_params.morse.eps, - "alpha": ia_params.morse.alpha, - "rmin": ia_params.morse.rmin, - "cutoff": ia_params.morse.cut - } + _so_name = "Interactions::InteractionMorse" def is_active(self): """Check if interaction is active. """ - return (self._params["eps"] > 0) - - def _set_params_in_es_core(self): - if morse_set_params(self._part_types[0], - self._part_types[1], - self._params["eps"], - self._params["alpha"], - self._params["rmin"], - self._params["cutoff"]): - raise Exception("Could not set Morse parameters") + return self.eps > 0. def default_params(self): """Python dictionary of default parameters. @@ -1052,24 +1044,6 @@ IF MORSE == 1: """ return "Morse" - def set_params(self, **kwargs): - """ - Set parameters for the Morse interaction. - - Parameters - ---------- - eps : :obj:`float` - The magnitude of the interaction. - alpha : :obj:`float` - Stiffness of the Morse interaction. - rmin : :obj:`float` - Distance of potential minimum - cutoff : :obj:`float`, optional - Cutoff distance of the interaction. - - """ - super().set_params(**kwargs) - def valid_keys(self): """All parameters that can be set. @@ -1406,8 +1380,6 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): self.soft_sphere = SoftSphereInteraction(_type1, _type2) IF SMOOTH_STEP: self.smooth_step = SmoothStepInteraction(_type1, _type2) - IF MORSE: - self.morse = MorseInteraction(_type1, _type2) IF BUCKINGHAM: self.buckingham = BuckinghamInteraction(_type1, _type2) IF TABULATED: diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index ccf4569a8bc..9bbc72ddb6e 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -385,6 +385,32 @@ class InteractionBMHTF }; #endif // BMHTF_NACL +#ifdef MORSE +class InteractionMorse + : public InteractionPotentialInterface<::Morse_Parameters> { +protected: + CoreInteraction IA_parameters::*get_ptr_offset() const override { + return &::IA_parameters::morse; + } + +public: + InteractionMorse() { + add_parameters({ + make_autoparameter(&CoreInteraction::eps, "eps"), + make_autoparameter(&CoreInteraction::alpha, "alpha"), + make_autoparameter(&CoreInteraction::rmin, "rmin"), + make_autoparameter(&CoreInteraction::cut, "cutoff"), + }); + } + + void make_new_instance(VariantMap const ¶ms) override { + m_ia_si = + make_shared_from_args( + params, "eps", "alpha", "rmin", "cutoff"); + } +}; +#endif // MORSE + class NonBondedInteractionHandle : public AutoParameters { std::array m_types = {-1, -1}; @@ -413,6 +439,9 @@ class NonBondedInteractionHandle #ifdef BMHTF_NACL std::shared_ptr m_bmhtf; #endif +#ifdef MORSE + std::shared_ptr m_morse; +#endif template auto make_autoparameter(std::shared_ptr &member, const char *key) const { @@ -454,6 +483,9 @@ class NonBondedInteractionHandle #endif #ifdef BMHTF_NACL make_autoparameter(m_bmhtf, "bmhtf"), +#endif +#ifdef MORSE + make_autoparameter(m_morse, "morse"), #endif }); } @@ -524,6 +556,10 @@ class NonBondedInteractionHandle #ifdef BMHTF_NACL set_member(m_bmhtf, "bmhtf", "Interactions::InteractionBMHTF", params); +#endif +#ifdef MORSE + set_member(m_morse, "morse", + "Interactions::InteractionMorse", params); #endif } diff --git a/src/script_interface/interactions/initialize.cpp b/src/script_interface/interactions/initialize.cpp index ab4c766f575..57cd45770f7 100644 --- a/src/script_interface/interactions/initialize.cpp +++ b/src/script_interface/interactions/initialize.cpp @@ -80,6 +80,9 @@ void initialize(Utils::Factory *om) { #ifdef BMHTF_NACL om->register_new("Interactions::InteractionBMHTF"); #endif +#ifdef MORSE + om->register_new("Interactions::InteractionMorse"); +#endif } } // namespace Interactions } // namespace ScriptInterface diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index 4dea913dc48..631747c2ea9 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -304,6 +304,14 @@ def test_bmhtf_exceptions(self): ("a", "c", "d") ) + @utx.skipIfMissingFeatures("MORSE") + def test_morse_exceptions(self): + self.check_potential_exceptions( + espressomd.interactions.MorseInteraction, + {"eps": 1., "alpha": 3., "rmin": 0.1, "cutoff": 1.2}, + ("eps",) + ) + if __name__ == "__main__": ut.main() From b053cc50a5bdf358897c08626d99634045b71c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 6 Sep 2022 13:04:46 +0200 Subject: [PATCH 58/85] Rewrite Buckingham interaction interface --- .../nonbonded_interactions/buckingham.cpp | 48 +++++----- .../nonbonded_interactions/buckingham.hpp | 8 +- .../nonbonded_interaction_data.cpp | 2 +- .../nonbonded_interaction_data.hpp | 4 + src/python/espressomd/interactions.pxd | 19 ---- src/python/espressomd/interactions.pyx | 94 ++++++------------- .../interactions/NonBondedInteraction.hpp | 40 ++++++++ .../interactions/initialize.cpp | 4 + .../interactions_non-bonded_interface.py | 9 ++ 9 files changed, 109 insertions(+), 119 deletions(-) diff --git a/src/core/nonbonded_interactions/buckingham.cpp b/src/core/nonbonded_interactions/buckingham.cpp index b8b77565d01..e8febf5ff32 100644 --- a/src/core/nonbonded_interactions/buckingham.cpp +++ b/src/core/nonbonded_interactions/buckingham.cpp @@ -25,38 +25,32 @@ #include "buckingham.hpp" #ifdef BUCKINGHAM -#include "interactions.hpp" #include "nonbonded_interaction_data.hpp" -#include - -int buckingham_set_params(int part_type_a, int part_type_b, double A, double B, - double C, double D, double cut, double discont, - double shift) { - IA_parameters *data = get_ia_param_safe(part_type_a, part_type_b); - - if (!data) - return ES_ERROR; - - data->buckingham.A = A; - data->buckingham.B = B; - data->buckingham.C = C; - data->buckingham.D = D; - data->buckingham.cut = cut; - data->buckingham.discont = discont; - data->buckingham.shift = shift; +#include + +Buckingham_Parameters::Buckingham_Parameters(double a, double b, double c, + double d, double cutoff, + double discont, double shift) + : A{a}, B{b}, C{c}, D{d}, cut{cutoff}, discont{discont}, shift{shift} { + if (a < 0.) { + throw std::domain_error("Buckingham parameter 'a' has to be >= 0"); + } + if (b < 0.) { + throw std::domain_error("Buckingham parameter 'b' has to be >= 0"); + } + if (c < 0.) { + throw std::domain_error("Buckingham parameter 'c' has to be >= 0"); + } + if (d < 0.) { + throw std::domain_error("Buckingham parameter 'd' has to be >= 0"); + } /* Replace the Buckingham potential for interatomic distance less than or equal to discontinuity by a straight line (F1+F2*r) */ - auto const F = buck_force_r(A, B, C, D, discont); - data->buckingham.F1 = buck_energy_r(A, B, C, D, shift, discont) + discont * F; - data->buckingham.F2 = -F; - - /* broadcast interaction parameters */ - mpi_bcast_ia_params(part_type_a, part_type_b); - - return ES_OK; + F1 = buck_energy_r(A, B, C, D, shift, discont) + discont * F; + F2 = -F; } -#endif +#endif // BUCKINGHAM diff --git a/src/core/nonbonded_interactions/buckingham.hpp b/src/core/nonbonded_interactions/buckingham.hpp index 33a78a8625c..76fc3b1bd39 100644 --- a/src/core/nonbonded_interactions/buckingham.hpp +++ b/src/core/nonbonded_interactions/buckingham.hpp @@ -32,14 +32,8 @@ #include "nonbonded_interaction_data.hpp" -#include - #include -int buckingham_set_params(int part_type_a, int part_type_b, double A, double B, - double C, double D, double cut, double discont, - double shift); - /**Resultant Force due to a Buckingham potential between two particles at * interatomic separation r greater than or equal to discont*/ inline double buck_force_r(double A, double B, double C, double D, double r) { @@ -90,5 +84,5 @@ inline double buck_pair_energy(IA_parameters const &ia_params, double dist) { return 0.0; } -#endif +#endif // BUCKINGHAM #endif diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index 341bb5cf1d7..0ba701e8b56 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -174,7 +174,7 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { #endif #ifdef BUCKINGHAM - max_cut_current = std::max(max_cut_current, data.buckingham.cut); + max_cut_current = std::max(max_cut_current, data.buckingham.max_cutoff()); #endif #ifdef SOFT_SPHERE diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index 9f803b85621..eed08f6f359 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -163,6 +163,10 @@ struct Buckingham_Parameters { double shift = 0.0; double F1 = 0.0; double F2 = 0.0; + Buckingham_Parameters() = default; + Buckingham_Parameters(double a, double b, double c, double d, double cutoff, + double discont, double shift); + double max_cutoff() const { return cut; } }; /** soft-sphere potential */ diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index 5523c237e8d..6d57e62deaf 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -47,17 +47,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": int n double k0 - cdef struct Buckingham_Parameters: - double A - double B - double C - double D - double cut - double discont - double shift - double F1 - double F2 - cdef struct SoftSphere_Parameters: double a double n @@ -98,8 +87,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": SmoothStep_Parameters smooth_step - Buckingham_Parameters buckingham - DPDParameters dpd_radial DPDParameters dpd_trans @@ -129,12 +116,6 @@ IF SMOOTH_STEP: double d, int n, double eps, double k0, double sig, double cut) -IF BUCKINGHAM: - cdef extern from "nonbonded_interactions/buckingham.hpp": - int buckingham_set_params(int part_type_a, int part_type_b, - double A, double B, double C, double D, double cut, - double discont, double shift) - IF SOFT_SPHERE: cdef extern from "nonbonded_interactions/soft_sphere.hpp": int soft_sphere_set_params(int part_type_a, int part_type_b, diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 691062413c5..d3cdfae0377 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -1058,69 +1058,17 @@ IF MORSE == 1: IF BUCKINGHAM == 1: - cdef class BuckinghamInteraction(NonBondedInteraction): - - def validate_params(self): - """Check that parameters are valid. - - """ - if self._params["a"] < 0: - raise ValueError("Buckingham a has to be >=0") - if self._params["b"] < 0: - raise ValueError("Buckingham b has to be >=0") - if self._params["c"] < 0: - raise ValueError("Buckingham c has to be >=0") - if self._params["d"] < 0: - raise ValueError("Buckingham d has to be >=0") - - def _get_params_from_es_core(self): - cdef IA_parameters * ia_params - ia_params = get_ia_param_safe( - self._part_types[0], - self._part_types[1]) - return { - "a": ia_params.buckingham.A, - "b": ia_params.buckingham.B, - "c": ia_params.buckingham.C, - "d": ia_params.buckingham.D, - "cutoff": ia_params.buckingham.cut, - "discont": ia_params.buckingham.discont, - "shift": ia_params.buckingham.shift - } - - def is_active(self): - """Check if interaction is active. - - """ - return (self._params["a"] > 0) or (self._params["b"] > 0) or ( - self._params["d"] > 0) or (self._params["shift"] > 0) - - def _set_params_in_es_core(self): - if buckingham_set_params(self._part_types[0], self._part_types[1], - self._params["a"], - self._params["b"], - self._params["c"], - self._params["d"], - self._params["cutoff"], - self._params["discont"], - self._params["shift"]): - raise Exception("Could not set Buckingham parameters") - - def default_params(self): - """Python dictionary of default parameters. - - """ - return {"b": 0., "shift": 0.} - - def type_name(self): - """Name of interaction type. - - """ - return "Buckingham" + @script_interface_register + class BuckinghamInteraction(NewNonBondedInteraction): + """Buckingham interaction. - def set_params(self, **kwargs): - """ - Set parameters for the Buckingham interaction. + Methods + ------- + set_params() + Set or update parameters for the interaction. + Parameters marked as required become optional once the + interaction has been activated for the first time; + subsequent calls to this method update the existing values. Parameters ---------- @@ -1138,9 +1086,27 @@ IF BUCKINGHAM == 1: Cutoff distance of the interaction. shift: :obj:`float`, optional Constant potential shift. + """ + + _so_name = "Interactions::InteractionBuckingham" + + def is_active(self): + """Check if interaction is active. """ - super().set_params(**kwargs) + return self.a > 0. or self.b > 0. or self.d > 0. or self.shift > 0. + + def default_params(self): + """Python dictionary of default parameters. + + """ + return {"b": 0., "shift": 0.} + + def type_name(self): + """Name of interaction type. + + """ + return "Buckingham" def valid_keys(self): """All parameters that can be set. @@ -1380,8 +1346,6 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): self.soft_sphere = SoftSphereInteraction(_type1, _type2) IF SMOOTH_STEP: self.smooth_step = SmoothStepInteraction(_type1, _type2) - IF BUCKINGHAM: - self.buckingham = BuckinghamInteraction(_type1, _type2) IF TABULATED: self.tabulated = TabulatedNonBonded(_type1, _type2) IF GAY_BERNE: diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index 9bbc72ddb6e..719aafdba1d 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -411,6 +411,35 @@ class InteractionMorse }; #endif // MORSE +#ifdef BUCKINGHAM +class InteractionBuckingham + : public InteractionPotentialInterface<::Buckingham_Parameters> { +protected: + CoreInteraction IA_parameters::*get_ptr_offset() const override { + return &::IA_parameters::buckingham; + } + +public: + InteractionBuckingham() { + add_parameters({ + make_autoparameter(&CoreInteraction::A, "a"), + make_autoparameter(&CoreInteraction::B, "b"), + make_autoparameter(&CoreInteraction::C, "c"), + make_autoparameter(&CoreInteraction::D, "d"), + make_autoparameter(&CoreInteraction::cut, "cutoff"), + make_autoparameter(&CoreInteraction::discont, "discont"), + make_autoparameter(&CoreInteraction::shift, "shift"), + }); + } + + void make_new_instance(VariantMap const ¶ms) override { + m_ia_si = make_shared_from_args( + params, "a", "b", "c", "d", "cutoff", "discont", "shift"); + } +}; +#endif // BUCKINGHAM + class NonBondedInteractionHandle : public AutoParameters { std::array m_types = {-1, -1}; @@ -442,6 +471,9 @@ class NonBondedInteractionHandle #ifdef MORSE std::shared_ptr m_morse; #endif +#ifdef BUCKINGHAM + std::shared_ptr m_buckingham; +#endif template auto make_autoparameter(std::shared_ptr &member, const char *key) const { @@ -486,6 +518,9 @@ class NonBondedInteractionHandle #endif #ifdef MORSE make_autoparameter(m_morse, "morse"), +#endif +#ifdef BUCKINGHAM + make_autoparameter(m_buckingham, "buckingham"), #endif }); } @@ -560,6 +595,11 @@ class NonBondedInteractionHandle #ifdef MORSE set_member(m_morse, "morse", "Interactions::InteractionMorse", params); +#endif +#ifdef BUCKINGHAM + set_member(m_buckingham, "buckingham", + "Interactions::InteractionBuckingham", + params); #endif } diff --git a/src/script_interface/interactions/initialize.cpp b/src/script_interface/interactions/initialize.cpp index 57cd45770f7..17983855d75 100644 --- a/src/script_interface/interactions/initialize.cpp +++ b/src/script_interface/interactions/initialize.cpp @@ -83,6 +83,10 @@ void initialize(Utils::Factory *om) { #ifdef MORSE om->register_new("Interactions::InteractionMorse"); #endif +#ifdef BUCKINGHAM + om->register_new( + "Interactions::InteractionBuckingham"); +#endif } } // namespace Interactions } // namespace ScriptInterface diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index 631747c2ea9..342749f739a 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -312,6 +312,15 @@ def test_morse_exceptions(self): ("eps",) ) + @utx.skipIfMissingFeatures("BUCKINGHAM") + def test_buckingham_exceptions(self): + self.check_potential_exceptions( + espressomd.interactions.BuckinghamInteraction, + {"a": 2., "b": 3., "c": 5., "d": 4., "discont": 1., "cutoff": 2., + "shift": 0.1}, + ("a", "b", "c", "d") + ) + if __name__ == "__main__": ut.main() From 04db15c6bd5738202bb08be05b0c77615644daf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 6 Sep 2022 13:31:02 +0200 Subject: [PATCH 59/85] Rewrite SoftSphere interaction interface --- .../nonbonded_interaction_data.cpp | 3 +- .../nonbonded_interaction_data.hpp | 3 + .../nonbonded_interactions/soft_sphere.cpp | 32 +++----- .../nonbonded_interactions/soft_sphere.hpp | 11 +-- src/python/espressomd/interactions.pxd | 19 ----- src/python/espressomd/interactions.pyx | 73 ++++++------------- .../interactions/NonBondedInteraction.hpp | 37 ++++++++++ .../interactions/initialize.cpp | 4 + .../interactions_non-bonded_interface.py | 8 ++ 9 files changed, 91 insertions(+), 99 deletions(-) diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index 0ba701e8b56..bcff9a8bbd4 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -178,8 +178,7 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { #endif #ifdef SOFT_SPHERE - max_cut_current = std::max(max_cut_current, - (data.soft_sphere.cut + data.soft_sphere.offset)); + max_cut_current = std::max(max_cut_current, data.soft_sphere.max_cutoff()); #endif #ifdef HAT diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index eed08f6f359..e4fe1e0564e 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -175,6 +175,9 @@ struct SoftSphere_Parameters { double n = 0.0; double cut = INACTIVE_CUTOFF; double offset = 0.0; + SoftSphere_Parameters() = default; + SoftSphere_Parameters(double a, double n, double cutoff, double offset); + double max_cutoff() const { return cut + offset; } }; /** hat potential */ diff --git a/src/core/nonbonded_interactions/soft_sphere.cpp b/src/core/nonbonded_interactions/soft_sphere.cpp index ffc13dc9ad6..bfa8e44d928 100644 --- a/src/core/nonbonded_interactions/soft_sphere.cpp +++ b/src/core/nonbonded_interactions/soft_sphere.cpp @@ -26,27 +26,19 @@ #include "soft_sphere.hpp" #ifdef SOFT_SPHERE -#include "interactions.hpp" #include "nonbonded_interaction_data.hpp" -#include - -int soft_sphere_set_params(int part_type_a, int part_type_b, double a, double n, - double cut, double offset) { - IA_parameters *data = get_ia_param_safe(part_type_a, part_type_b); - - if (!data) - return ES_ERROR; - - data->soft_sphere.a = a; - data->soft_sphere.n = n; - data->soft_sphere.cut = cut; - data->soft_sphere.offset = offset; - - /* broadcast interaction parameters */ - mpi_bcast_ia_params(part_type_a, part_type_b); - - return ES_OK; +#include + +SoftSphere_Parameters::SoftSphere_Parameters(double a, double n, double cutoff, + double offset) + : a{a}, n{n}, cut{cutoff}, offset{offset} { + if (a < 0.) { + throw std::domain_error("SoftSphere parameter 'a' has to be >= 0"); + } + if (offset < 0.) { + throw std::domain_error("SoftSphere parameter 'offset' has to be >= 0"); + } } -#endif +#endif // SOFT_SPHERE diff --git a/src/core/nonbonded_interactions/soft_sphere.hpp b/src/core/nonbonded_interactions/soft_sphere.hpp index b7a0b0acc39..658e30020ed 100644 --- a/src/core/nonbonded_interactions/soft_sphere.hpp +++ b/src/core/nonbonded_interactions/soft_sphere.hpp @@ -33,17 +33,12 @@ #include "nonbonded_interaction_data.hpp" -#include - #include -int soft_sphere_set_params(int part_type_a, int part_type_b, double a, double n, - double cut, double offset); - /** Calculate soft-sphere force factor */ inline double soft_pair_force_factor(IA_parameters const &ia_params, double dist) { - if (dist < (ia_params.soft_sphere.cut + ia_params.soft_sphere.offset)) { + if (dist < ia_params.soft_sphere.max_cutoff()) { /* normal case: resulting force/energy smaller than zero. */ auto const r_off = dist - ia_params.soft_sphere.offset; if (r_off > 0.0) { @@ -56,7 +51,7 @@ inline double soft_pair_force_factor(IA_parameters const &ia_params, /** Calculate soft-sphere energy */ inline double soft_pair_energy(IA_parameters const &ia_params, double dist) { - if (dist < (ia_params.soft_sphere.cut + ia_params.soft_sphere.offset)) { + if (dist < ia_params.soft_sphere.max_cutoff()) { auto const r_off = dist - ia_params.soft_sphere.offset; /* normal case: resulting force/energy smaller than zero. */ return ia_params.soft_sphere.a / std::pow(r_off, ia_params.soft_sphere.n); @@ -64,5 +59,5 @@ inline double soft_pair_energy(IA_parameters const &ia_params, double dist) { return 0.0; } -#endif /* ifdef SOFT_SPHERE */ +#endif // SOFT_SPHERE #endif diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index 6d57e62deaf..37a5d00bad8 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -47,12 +47,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": int n double k0 - cdef struct SoftSphere_Parameters: - double a - double n - double cut - double offset - cdef struct Hat_Parameters: double Fmax double r @@ -79,8 +73,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": cdef struct IA_parameters: - SoftSphere_Parameters soft_sphere - TabulatedPotential tab GayBerne_Parameters gay_berne @@ -116,11 +108,6 @@ IF SMOOTH_STEP: double d, int n, double eps, double k0, double sig, double cut) -IF SOFT_SPHERE: - cdef extern from "nonbonded_interactions/soft_sphere.hpp": - int soft_sphere_set_params(int part_type_a, int part_type_b, - double a, double n, double cut, double offset) - IF DPD: cdef extern from "dpd.hpp": int dpd_set_params(int part_type_a, int part_type_b, @@ -132,12 +119,6 @@ IF HAT: int hat_set_params(int part_type_a, int part_type_b, double Fmax, double r) -IF SOFT_SPHERE: - cdef extern from "nonbonded_interactions/soft_sphere.hpp": - cdef int soft_sphere_set_params(int part_type_a, int part_type_b, - double a, double n, - double cut, double offset) - IF TABULATED: cdef extern from "nonbonded_interactions/nonbonded_tab.hpp": int tabulated_set_params(int part_type_a, int part_type_b, diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index d3cdfae0377..0a12522d926 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -1122,44 +1122,37 @@ IF BUCKINGHAM == 1: IF SOFT_SPHERE == 1: - cdef class SoftSphereInteraction(NonBondedInteraction): + @script_interface_register + class SoftSphereInteraction(NewNonBondedInteraction): + """Soft sphere interaction. - def validate_params(self): - """Check that parameters are valid. + Methods + ------- + set_params() + Set or update parameters for the interaction. + Parameters marked as required become optional once the + interaction has been activated for the first time; + subsequent calls to this method update the existing values. - """ - if self._params["a"] < 0: - raise ValueError("Soft-sphere a has to be >=0") - if self._params["offset"] < 0: - raise ValueError("Soft-sphere offset has to be >=0") - if self._params["cutoff"] < 0: - raise ValueError("Soft-sphere cutoff has to be >=0") + Parameters + ---------- + a : :obj:`float` + Magnitude of the interaction. + n : :obj:`float` + Exponent of the power law. + cutoff : :obj:`float` + Cutoff distance of the interaction. + offset : :obj:`float`, optional + Offset distance of the interaction. + """ - def _get_params_from_es_core(self): - cdef IA_parameters * ia_params - ia_params = get_ia_param_safe(self._part_types[0], - self._part_types[1]) - return { - "a": ia_params.soft_sphere.a, - "n": ia_params.soft_sphere.n, - "cutoff": ia_params.soft_sphere.cut, - "offset": ia_params.soft_sphere.offset - } + _so_name = "Interactions::InteractionSoftSphere" def is_active(self): """Check if interaction is active. """ - return (self._params["a"] > 0) - - def _set_params_in_es_core(self): - if soft_sphere_set_params(self._part_types[0], - self._part_types[1], - self._params["a"], - self._params["n"], - self._params["cutoff"], - self._params["offset"]): - raise Exception("Could not set Soft-sphere parameters") + return self.a > 0. def default_params(self): """Python dictionary of default parameters. @@ -1173,24 +1166,6 @@ IF SOFT_SPHERE == 1: """ return "SoftSphere" - def set_params(self, **kwargs): - """ - Set parameters for the Soft-sphere interaction. - - Parameters - ---------- - a : :obj:`float` - Magnitude of the interaction. - n : :obj:`float` - Exponent of the power law. - cutoff : :obj:`float` - Cutoff distance of the interaction. - offset : :obj:`float`, optional - Offset distance of the interaction. - - """ - super().set_params(**kwargs) - def valid_keys(self): """All parameters that can be set. @@ -1342,8 +1317,6 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): super().__init__(_types=[_type1, _type2], **kwargs) # Here, add one line for each nonbonded ia - IF SOFT_SPHERE: - self.soft_sphere = SoftSphereInteraction(_type1, _type2) IF SMOOTH_STEP: self.smooth_step = SmoothStepInteraction(_type1, _type2) IF TABULATED: diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index 719aafdba1d..8904cf03705 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -440,6 +440,32 @@ class InteractionBuckingham }; #endif // BUCKINGHAM +#ifdef SOFT_SPHERE +class InteractionSoftSphere + : public InteractionPotentialInterface<::SoftSphere_Parameters> { +protected: + CoreInteraction IA_parameters::*get_ptr_offset() const override { + return &::IA_parameters::soft_sphere; + } + +public: + InteractionSoftSphere() { + add_parameters({ + make_autoparameter(&CoreInteraction::a, "a"), + make_autoparameter(&CoreInteraction::n, "n"), + make_autoparameter(&CoreInteraction::cut, "cutoff"), + make_autoparameter(&CoreInteraction::offset, "offset"), + }); + } + + void make_new_instance(VariantMap const ¶ms) override { + m_ia_si = + make_shared_from_args( + params, "a", "n", "cutoff", "offset"); + } +}; +#endif // SOFT_SPHERE + class NonBondedInteractionHandle : public AutoParameters { std::array m_types = {-1, -1}; @@ -474,6 +500,9 @@ class NonBondedInteractionHandle #ifdef BUCKINGHAM std::shared_ptr m_buckingham; #endif +#ifdef SOFT_SPHERE + std::shared_ptr m_soft_sphere; +#endif template auto make_autoparameter(std::shared_ptr &member, const char *key) const { @@ -521,6 +550,9 @@ class NonBondedInteractionHandle #endif #ifdef BUCKINGHAM make_autoparameter(m_buckingham, "buckingham"), +#endif +#ifdef SOFT_SPHERE + make_autoparameter(m_soft_sphere, "soft_sphere"), #endif }); } @@ -600,6 +632,11 @@ class NonBondedInteractionHandle set_member(m_buckingham, "buckingham", "Interactions::InteractionBuckingham", params); +#endif +#ifdef SOFT_SPHERE + set_member(m_soft_sphere, "soft_sphere", + "Interactions::InteractionSoftSphere", + params); #endif } diff --git a/src/script_interface/interactions/initialize.cpp b/src/script_interface/interactions/initialize.cpp index 17983855d75..8174935106e 100644 --- a/src/script_interface/interactions/initialize.cpp +++ b/src/script_interface/interactions/initialize.cpp @@ -87,6 +87,10 @@ void initialize(Utils::Factory *om) { om->register_new( "Interactions::InteractionBuckingham"); #endif +#ifdef SOFT_SPHERE + om->register_new( + "Interactions::InteractionSoftSphere"); +#endif } } // namespace Interactions } // namespace ScriptInterface diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index 342749f739a..6edea52a3ed 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -321,6 +321,14 @@ def test_buckingham_exceptions(self): ("a", "b", "c", "d") ) + @utx.skipIfMissingFeatures("SOFT_SPHERE") + def test_soft_sphere_exceptions(self): + self.check_potential_exceptions( + espressomd.interactions.SoftSphereInteraction, + {"a": 1., "n": 3., "cutoff": 1.1, "offset": 0.1}, + ("a", "offset") + ) + if __name__ == "__main__": ut.main() From b67c93e28e83de93204d94243bcfaa3ab65da096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 7 Sep 2022 01:10:02 +0200 Subject: [PATCH 60/85] Add missing feature guards --- doc/sphinx/inter_non-bonded.rst | 15 --------------- src/core/Particle.hpp | 3 ++- .../grid_based_algorithms/lb_boundaries.cpp | 2 ++ src/core/magnetostatics/dipoles_inline.hpp | 8 ++++---- src/python/espressomd/drude_helpers.py | 17 ++++++++++------- testsuite/python/bond_breakage.py | 4 +++- testsuite/python/collision_detection.py | 2 +- testsuite/python/constraint_shape_based.py | 2 +- testsuite/python/dipolar_direct_summation.py | 1 + testsuite/python/dipolar_interface.py | 3 +++ testsuite/python/lees_edwards.py | 4 ++-- testsuite/python/long_range_actors.py | 4 ++-- testsuite/python/save_checkpoint.py | 5 +++-- testsuite/python/test_checkpoint.py | 7 ++++--- 14 files changed, 38 insertions(+), 39 deletions(-) diff --git a/doc/sphinx/inter_non-bonded.rst b/doc/sphinx/inter_non-bonded.rst index 4a0de03ef31..e7c07177d38 100644 --- a/doc/sphinx/inter_non-bonded.rst +++ b/doc/sphinx/inter_non-bonded.rst @@ -44,7 +44,6 @@ Tabulated interaction Feature ``TABULATED`` required. - The interface for tabulated interactions are implemented in the :class:`~espressomd.interactions.TabulatedNonBonded` class. They can be configured via the following syntax:: @@ -52,7 +51,6 @@ via the following syntax:: system.non_bonded_inter[type1, type2].tabulated.set_params( min='min', max='max', energy='energy', force='force') - This defines an interaction between particles of the types ``type1`` and ``type2`` according to an arbitrary tabulated pair potential by linear interpolation. ``force`` specifies the tabulated forces and ``energy`` the energies as a function of the @@ -646,19 +644,6 @@ and involved types. The samples folder contains the script :file:`/samples/drude_bmimpf6.py` with a fully polarizable, coarse grained ionic liquid where this approach is applied. -To use the script, compile espresso with the following features: - -.. code-block:: c++ - - #define EXTERNAL_FORCES - #define MASS - #define THERMOSTAT_PER_PARTICLE - #define ROTATION - #define ROTATIONAL_INERTIA - #define ELECTROSTATICS - #define VIRTUAL_SITES_RELATIVE - #define LENNARD_JONES - #define THOLE .. _Anisotropic non-bonded interactions: diff --git a/src/core/Particle.hpp b/src/core/Particle.hpp index 4c9a21d5f0f..a4c007617c4 100644 --- a/src/core/Particle.hpp +++ b/src/core/Particle.hpp @@ -492,8 +492,10 @@ struct Particle { // NOLINT(bugprone-exception-escape) auto &torque() { return f.torque; } auto const &omega() const { return m.omega; } auto &omega() { return m.omega; } +#ifdef EXTERNAL_FORCES auto const &ext_torque() const { return p.ext_torque; } auto &ext_torque() { return p.ext_torque; } +#endif // EXTERNAL_FORCES auto calc_director() const { return r.calc_director(); } #else // ROTATION bool can_rotate() const { return false; } @@ -556,7 +558,6 @@ struct Particle { // NOLINT(bugprone-exception-escape) } 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) const { return false; } diff --git a/src/core/grid_based_algorithms/lb_boundaries.cpp b/src/core/grid_based_algorithms/lb_boundaries.cpp index 6249e0dcb89..14a1ff43b59 100644 --- a/src/core/grid_based_algorithms/lb_boundaries.cpp +++ b/src/core/grid_based_algorithms/lb_boundaries.cpp @@ -171,6 +171,7 @@ void lb_init_boundaries() { if (this_node != 0) { return; } +#if defined(CUDA) #if defined(LB_BOUNDARIES_GPU) #if defined(EK_BOUNDARIES) ek_init_boundaries(); @@ -233,6 +234,7 @@ void lb_init_boundaries() { "compiled in. Activate in myconfig.hpp."; } #endif // defined (LB_BOUNDARIES_GPU) +#endif // defined (CUDA) } else if (lattice_switch == ActiveLB::CPU) { #if defined(LB_BOUNDARIES) using Utils::get_linear_index; diff --git a/src/core/magnetostatics/dipoles_inline.hpp b/src/core/magnetostatics/dipoles_inline.hpp index d7261ecf7e5..0f9a5ef356c 100644 --- a/src/core/magnetostatics/dipoles_inline.hpp +++ b/src/core/magnetostatics/dipoles_inline.hpp @@ -51,7 +51,7 @@ struct ShortRangeForceKernel return {}; } -#ifdef P3M +#ifdef DP3M result_type operator()(std::shared_ptr const &ptr) const { auto const &actor = *ptr; return kernel_type{[&actor](Particle const &p1, Particle const &p2, @@ -60,7 +60,7 @@ struct ShortRangeForceKernel return actor.pair_force(p1, p2, d, dist2, dist); }}; } -#endif // P3M +#endif // DP3M result_type operator()(std::shared_ptr const &ptr) const { @@ -82,7 +82,7 @@ struct ShortRangeEnergyKernel return {}; } -#ifdef P3M +#ifdef DP3M result_type operator()(std::shared_ptr const &ptr) const { auto const &actor = *ptr; return kernel_type{[&actor](Particle const &p1, Particle const &p2, @@ -91,7 +91,7 @@ struct ShortRangeEnergyKernel return actor.pair_energy(p1, p2, d, dist2, dist); }}; } -#endif // P3M +#endif // DP3M result_type operator()(std::shared_ptr const &ptr) const { diff --git a/src/python/espressomd/drude_helpers.py b/src/python/espressomd/drude_helpers.py index 07c8c729ccf..7e2255dd552 100644 --- a/src/python/espressomd/drude_helpers.py +++ b/src/python/espressomd/drude_helpers.py @@ -81,13 +81,16 @@ def add_drude_particle_to_core(self, system, harmonic_bond, k = harmonic_bond.params["k"] q_drude = -1.0 * pow(k * alpha / coulomb_prefactor, 0.5) - if has_features("PARTICLE_ANISOTROPY"): - gamma_off = [0.0, 0.0, 0.0] - else: - gamma_off = 0.0 + if has_features("THERMOSTAT_PER_PARTICLE"): + if has_features("PARTICLE_ANISOTROPY"): + gamma_off = [0.0, 0.0, 0.0] + else: + gamma_off = 0.0 drude_part = system.part.add(pos=p_core.pos, type=type_drude, - q=q_drude, mass=mass_drude, gamma=gamma_off) + q=q_drude, mass=mass_drude) + if has_features("THERMOSTAT_PER_PARTICLE"): + drude_part.gamma = gamma_off id_drude = drude_part.id if verbose: @@ -98,8 +101,8 @@ def add_drude_particle_to_core(self, system, harmonic_bond, p_core.mass -= mass_drude p_core.add_bond((harmonic_bond, id_drude)) p_core.add_bond((thermalized_bond, id_drude)) - - p_core.gamma = gamma_off + if has_features("THERMOSTAT_PER_PARTICLE"): + p_core.gamma = gamma_off if type_drude in self.drude_dict and not ( self.drude_dict[type_drude]["q"] == q_drude and diff --git a/testsuite/python/bond_breakage.py b/testsuite/python/bond_breakage.py index 5ae4e6c9c96..3dd26d613e8 100644 --- a/testsuite/python/bond_breakage.py +++ b/testsuite/python/bond_breakage.py @@ -216,6 +216,7 @@ def tearDown(self): self.system.bonded_inter.clear() self.system.thermostat.turn_off() + @utx.skipIfMissingFeatures(["COLLISION_DETECTION"]) def test_center_bonds(self): harm = espressomd.interactions.HarmonicBond(k=1.0, r_0=0.0, r_cut=5) @@ -244,7 +245,8 @@ def test_center_bonds(self): bonds_count = self.count_bonds(pairs) np.testing.assert_equal(bonds_dist, bonds_count) - @utx.skipIfMissingFeatures("VIRTUAL_SITES_RELATIVE") + @utx.skipIfMissingFeatures( + ["VIRTUAL_SITES_RELATIVE", "COLLISION_DETECTION"]) def test_vs_bonds(self): harm = espressomd.interactions.HarmonicBond(k=1.0, r_0=0.0, r_cut=5) diff --git a/testsuite/python/collision_detection.py b/testsuite/python/collision_detection.py index 16b4236d332..0c959c35857 100644 --- a/testsuite/python/collision_detection.py +++ b/testsuite/python/collision_detection.py @@ -464,7 +464,7 @@ def test_glue_to_surface(self): self.run_test_glue_to_surface_for_pos( np.array((0.2, 0, 0)), np.array((0.95, 0, 0)), np.array((0.7, 0, 0))) - @utx.skipIfMissingFeatures("VIRTUAL_SITES_RELATIVE") + @utx.skipIfMissingFeatures(["LENNARD_JONES", "VIRTUAL_SITES_RELATIVE"]) def test_glue_to_surface_random(self): """Integrate lj liquid and check that no double bonds are formed and the number of bonds fits the number of virtual sites diff --git a/testsuite/python/constraint_shape_based.py b/testsuite/python/constraint_shape_based.py index 48233a9b6df..a0ee72d187a 100644 --- a/testsuite/python/constraint_shape_based.py +++ b/testsuite/python/constraint_shape_based.py @@ -30,7 +30,7 @@ import tests_common -@utx.skipIfMissingFeatures(["LENNARD_JONES_GENERIC"]) +@utx.skipIfMissingFeatures(["LENNARD_JONES", "LENNARD_JONES_GENERIC"]) class ShapeBasedConstraintTest(ut.TestCase): box_l = 30. diff --git a/testsuite/python/dipolar_direct_summation.py b/testsuite/python/dipolar_direct_summation.py index b39f9d9466d..c6187e1ff2f 100644 --- a/testsuite/python/dipolar_direct_summation.py +++ b/testsuite/python/dipolar_direct_summation.py @@ -121,6 +121,7 @@ def fcs_data(self): @ut.skipIf(system.cell_system.get_state()["n_nodes"] > 1, "Skipping test: only runs for n_nodes == 1") + @utx.skipIfMissingFeatures(["LENNARD_JONES"]) def test_gen_reference_data(self): filepaths = ('dipolar_direct_summation_energy.npy', 'dipolar_direct_summation_arrays.npy') diff --git a/testsuite/python/dipolar_interface.py b/testsuite/python/dipolar_interface.py index cf7b4dcc27e..8f23750c1a7 100644 --- a/testsuite/python/dipolar_interface.py +++ b/testsuite/python/dipolar_interface.py @@ -166,6 +166,9 @@ def test_exceptions_parallel(self): with self.assertRaisesRegex(ValueError, "Parameter 'prefactor' must be > 0"): espressomd.magnetostatics.DipolarP3M( **{**dp3m_params, 'prefactor': -2.}) + with self.assertRaisesRegex(ValueError, "Parameter 'timings' must be > 0"): + espressomd.magnetostatics.DipolarP3M( + **{**dp3m_params, 'timings': -2}) with self.assertRaisesRegex(ValueError, "Parameter 'mesh' has to be an integer or integer list of length 3"): espressomd.magnetostatics.DipolarP3M( **{**dp3m_params, 'mesh': [49, 49]}) diff --git a/testsuite/python/lees_edwards.py b/testsuite/python/lees_edwards.py index b635724284b..699e2cd495c 100644 --- a/testsuite/python/lees_edwards.py +++ b/testsuite/python/lees_edwards.py @@ -343,7 +343,7 @@ def test_distance_vel_diff(self): np.copy(system.velocity_difference(p1, p2)), np.copy(p2.v - p1.v) - system.lees_edwards.shear_velocity * shear_axis) - @utx.skipIfMissingFeatures("EXTERNAL_FORCES") + @utx.skipIfMissingFeatures(["EXTERNAL_FORCES", "SOFT_SPHERE"]) def test_interactions(self): """ We place two particles crossing a boundary and connect them with an @@ -449,7 +449,7 @@ def test_virt_sites(self): np.testing.assert_allclose( np.copy(p1.torque_lab), [0, 0, -2], atol=tol) - @utx.skipIfMissingFeatures(["VIRTUAL_SITES_RELATIVE", "ROTATION"]) + @utx.skipIfMissingFeatures(["VIRTUAL_SITES_RELATIVE", "ROTATION", "DPD"]) def test_virt_sites_interaction(self): """ A virtual site interacts with a real particle via a DPD interaction diff --git a/testsuite/python/long_range_actors.py b/testsuite/python/long_range_actors.py index 705dedc5403..c3ef7d0e89f 100644 --- a/testsuite/python/long_range_actors.py +++ b/testsuite/python/long_range_actors.py @@ -284,13 +284,13 @@ def check_p3m_tuning_errors(self, p3m): self.assertFalse(p3m.is_tuned) self.assertEqual(len(self.system.actors), 0) - @utx.skipIfMissingFeatures(["P3M"]) + @utx.skipIfMissingFeatures(["P3M", "NPT"]) def test_p3m_cpu_tuning_errors(self): self.add_charged_particles() p3m = espressomd.electrostatics.P3M(prefactor=1., accuracy=1e-3) self.check_p3m_tuning_errors(p3m) - @utx.skipIfMissingFeatures(["DP3M"]) + @utx.skipIfMissingFeatures(["DP3M", "NPT"]) def test_dp3m_cpu_tuning_errors(self): self.add_magnetic_particles() dp3m = espressomd.magnetostatics.DipolarP3M( diff --git a/testsuite/python/save_checkpoint.py b/testsuite/python/save_checkpoint.py index 55ab1b37b82..9b502cf10e6 100644 --- a/testsuite/python/save_checkpoint.py +++ b/testsuite/python/save_checkpoint.py @@ -76,8 +76,10 @@ lbf_actor = None if 'LB.CPU' in modes: lbf_actor = espressomd.lb.LBFluid + has_lbb = espressomd.has_features("LB_BOUNDARIES") elif 'LB.GPU' in modes and espressomd.gpu_available(): lbf_actor = espressomd.lb.LBFluidGPU + has_lbb = espressomd.has_features("LB_BOUNDARIES_GPU") 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, @@ -85,8 +87,7 @@ system.actors.add(lbf) if 'THERM.LB' in modes: system.thermostat.set_lb(LB_fluid=lbf, seed=23, gamma=2.0) - if (espressomd.has_features( - "LB_BOUNDARIES") or espressomd.has_features("LB_BOUNDARIES_GPU")): + if has_lbb: system.lbboundaries.add(espressomd.lbboundaries.LBBoundary( shape=espressomd.shapes.Wall(normal=(1, 0, 0), dist=0.5), velocity=(1e-4, 1e-4, 0))) system.lbboundaries.add(espressomd.lbboundaries.LBBoundary( diff --git a/testsuite/python/test_checkpoint.py b/testsuite/python/test_checkpoint.py index 98f9959f68f..6de38133e73 100644 --- a/testsuite/python/test_checkpoint.py +++ b/testsuite/python/test_checkpoint.py @@ -44,6 +44,9 @@ 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 +has_lbb = ('LB.CPU' in modes and espressomd.has_features("LB_BOUNDARIES") or + 'LB.GPU' in modes and espressomd.has_features("LB_BOUNDARIES_GPU") + and espressomd.gpu_available()) class CheckpointTest(ut.TestCase): @@ -589,9 +592,7 @@ 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 has_lb_mode or not (espressomd.has_features("LB_BOUNDARIES") - or espressomd.has_features("LB_BOUNDARIES_GPU")), - "Missing features") + @ut.skipIf(not has_lbb, "Missing features") def test_lb_boundaries(self): # check boundary objects self.assertEqual(len(system.lbboundaries), 2) From d77ee0805906939e2b68aed484deb9d21e266f31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 7 Sep 2022 16:17:19 +0200 Subject: [PATCH 61/85] Rewrite Hat interaction interface --- src/core/nonbonded_interactions/hat.cpp | 23 +++------- src/core/nonbonded_interactions/hat.hpp | 8 +--- .../nonbonded_interaction_data.cpp | 2 +- .../nonbonded_interaction_data.hpp | 3 ++ src/python/espressomd/interactions.pxd | 11 ----- src/python/espressomd/interactions.pyx | 45 +++++++------------ .../interactions/NonBondedInteraction.hpp | 32 +++++++++++++ .../interactions/initialize.cpp | 3 ++ .../interactions_non-bonded_interface.py | 8 ++++ 9 files changed, 71 insertions(+), 64 deletions(-) diff --git a/src/core/nonbonded_interactions/hat.cpp b/src/core/nonbonded_interactions/hat.cpp index c861851a6d8..ede5d20066e 100644 --- a/src/core/nonbonded_interactions/hat.cpp +++ b/src/core/nonbonded_interactions/hat.cpp @@ -25,24 +25,15 @@ #include "hat.hpp" #ifdef HAT -#include "interactions.hpp" #include "nonbonded_interaction_data.hpp" -#include +#include -int hat_set_params(int part_type_a, int part_type_b, double Fmax, double r) { - IA_parameters *data = get_ia_param_safe(part_type_a, part_type_b); - - if (!data) - return ES_ERROR; - - data->hat.Fmax = Fmax; - data->hat.r = r; - - /* broadcast interaction parameters */ - mpi_bcast_ia_params(part_type_a, part_type_b); - - return ES_OK; +Hat_Parameters::Hat_Parameters(double F_max, double cutoff) + : Fmax{F_max}, r{cutoff} { + if (F_max < 0.) { + throw std::domain_error("Hat parameter 'F_max' has to be >= 0"); + } } -#endif +#endif // HAT diff --git a/src/core/nonbonded_interactions/hat.hpp b/src/core/nonbonded_interactions/hat.hpp index 60d6fdc40e9..e5c84a1c993 100644 --- a/src/core/nonbonded_interactions/hat.hpp +++ b/src/core/nonbonded_interactions/hat.hpp @@ -33,14 +33,10 @@ #include "nonbonded_interaction_data.hpp" -#include - -int hat_set_params(int part_type_a, int part_type_b, double Fmax, double r); - /** Calculate hat force factor */ inline double hat_pair_force_factor(IA_parameters const &ia_params, double dist) { - if (dist > 0. && dist < ia_params.hat.r) { + if (dist != 0. and dist < ia_params.hat.r) { return ia_params.hat.Fmax * (1.0 - dist / ia_params.hat.r) / dist; } return 0.0; @@ -55,5 +51,5 @@ inline double hat_pair_energy(IA_parameters const &ia_params, double dist) { return 0.0; } -#endif /* ifdef HAT */ +#endif // HAT #endif diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index bcff9a8bbd4..e844ab84553 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -182,7 +182,7 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { #endif #ifdef HAT - max_cut_current = std::max(max_cut_current, data.hat.r); + max_cut_current = std::max(max_cut_current, data.hat.max_cutoff()); #endif #ifdef LJCOS diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index e4fe1e0564e..ece89b974fa 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -184,6 +184,9 @@ struct SoftSphere_Parameters { struct Hat_Parameters { double Fmax = 0.0; double r = INACTIVE_CUTOFF; + Hat_Parameters() = default; + Hat_Parameters(double F_max, double cutoff); + double max_cutoff() const { return r; } }; /** Lennard-Jones+Cos potential */ diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index 37a5d00bad8..9fd85e33426 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -47,10 +47,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": int n double k0 - cdef struct Hat_Parameters: - double Fmax - double r - cdef struct GayBerne_Parameters: double eps double sig @@ -82,8 +78,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": DPDParameters dpd_radial DPDParameters dpd_trans - Hat_Parameters hat - Thole_Parameters thole cdef IA_parameters * get_ia_param_safe(int i, int j) @@ -114,11 +108,6 @@ IF DPD: double gamma, double k, double r_c, int wf, double tgamma, double tr_c, int twf) -IF HAT: - cdef extern from "nonbonded_interactions/hat.hpp": - int hat_set_params(int part_type_a, int part_type_b, - double Fmax, double r) - IF TABULATED: cdef extern from "nonbonded_interactions/nonbonded_tab.hpp": int tabulated_set_params(int part_type_a, int part_type_b, diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 0a12522d926..94756808bc9 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -616,28 +616,17 @@ IF LJCOS2 == 1: IF HAT == 1: - cdef class HatInteraction(NonBondedInteraction): - - def validate_params(self): - if self._params["F_max"] < 0: - raise ValueError("Hat max force has to be >=0") - if self._params["cutoff"] < 0: - raise ValueError("Hat cutoff has to be >=0") - - def _get_params_from_es_core(self): - cdef IA_parameters * ia_params - ia_params = get_ia_param_safe( - self._part_types[0], self._part_types[1]) - return { - "F_max": ia_params.hat.Fmax, - "cutoff": ia_params.hat.r, - } - - def is_active(self): - return (self._params["F_max"] > 0) + @script_interface_register + class HatInteraction(NewNonBondedInteraction): + """Hat interaction. - def set_params(self, **kwargs): - """Set parameters for the Hat interaction. + Methods + ------- + set_params() + Set or update parameters for the interaction. + Parameters marked as required become optional once the + interaction has been activated for the first time; + subsequent calls to this method update the existing values. Parameters ---------- @@ -646,14 +635,12 @@ IF HAT == 1: cutoff : :obj:`float` Cutoff distance of the interaction. - """ - super().set_params(**kwargs) + """ - def _set_params_in_es_core(self): - if hat_set_params(self._part_types[0], self._part_types[1], - self._params["F_max"], - self._params["cutoff"]): - raise Exception("Could not set Hat parameters") + _so_name = "Interactions::InteractionHat" + + def is_active(self): + return self.F_max > 0. def default_params(self): return {} @@ -1325,8 +1312,6 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): self.gay_berne = GayBerneInteraction(_type1, _type2) IF DPD: self.dpd = DPDInteraction(_type1, _type2) - IF HAT: - self.hat = HatInteraction(_type1, _type2) IF THOLE: self.thole = TholeInteraction(_type1, _type2) diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index 8904cf03705..a8aa0d667bf 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -466,6 +466,28 @@ class InteractionSoftSphere }; #endif // SOFT_SPHERE +#ifdef HAT +class InteractionHat : public InteractionPotentialInterface<::Hat_Parameters> { +protected: + CoreInteraction IA_parameters::*get_ptr_offset() const override { + return &::IA_parameters::hat; + } + +public: + InteractionHat() { + add_parameters({ + make_autoparameter(&CoreInteraction::Fmax, "F_max"), + make_autoparameter(&CoreInteraction::r, "cutoff"), + }); + } + + void make_new_instance(VariantMap const ¶ms) override { + m_ia_si = make_shared_from_args( + params, "F_max", "cutoff"); + } +}; +#endif // HAT + class NonBondedInteractionHandle : public AutoParameters { std::array m_types = {-1, -1}; @@ -503,6 +525,9 @@ class NonBondedInteractionHandle #ifdef SOFT_SPHERE std::shared_ptr m_soft_sphere; #endif +#ifdef HAT + std::shared_ptr m_hat; +#endif template auto make_autoparameter(std::shared_ptr &member, const char *key) const { @@ -553,6 +578,9 @@ class NonBondedInteractionHandle #endif #ifdef SOFT_SPHERE make_autoparameter(m_soft_sphere, "soft_sphere"), +#endif +#ifdef HAT + make_autoparameter(m_hat, "hat"), #endif }); } @@ -637,6 +665,10 @@ class NonBondedInteractionHandle set_member(m_soft_sphere, "soft_sphere", "Interactions::InteractionSoftSphere", params); +#endif +#ifdef HAT + set_member(m_hat, "hat", "Interactions::InteractionHat", + params); #endif } diff --git a/src/script_interface/interactions/initialize.cpp b/src/script_interface/interactions/initialize.cpp index 8174935106e..ad1f5c08b6b 100644 --- a/src/script_interface/interactions/initialize.cpp +++ b/src/script_interface/interactions/initialize.cpp @@ -91,6 +91,9 @@ void initialize(Utils::Factory *om) { om->register_new( "Interactions::InteractionSoftSphere"); #endif +#ifdef HAT + om->register_new("Interactions::InteractionHat"); +#endif } } // namespace Interactions } // namespace ScriptInterface diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index 6edea52a3ed..1483af4b18a 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -329,6 +329,14 @@ def test_soft_sphere_exceptions(self): ("a", "offset") ) + @utx.skipIfMissingFeatures("HAT") + def test_hat_exceptions(self): + self.check_potential_exceptions( + espressomd.interactions.HatInteraction, + {"F_max": 10., "cutoff": 1.}, + ("F_max",) + ) + if __name__ == "__main__": ut.main() From f4787cca1e59246ec8217a6872013bd8fbb0c7cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 7 Sep 2022 17:57:14 +0200 Subject: [PATCH 62/85] Rewrite Gay-Berne interaction interface --- src/core/nonbonded_interactions/gay_berne.cpp | 40 +++--------- src/core/nonbonded_interactions/gay_berne.hpp | 4 -- .../nonbonded_interaction_data.cpp | 2 +- .../nonbonded_interaction_data.hpp | 4 ++ src/python/espressomd/interactions.pxd | 18 ------ src/python/espressomd/interactions.pyx | 62 ++++++------------- .../interactions/NonBondedInteraction.hpp | 39 ++++++++++++ .../interactions/initialize.cpp | 3 + 8 files changed, 73 insertions(+), 99 deletions(-) diff --git a/src/core/nonbonded_interactions/gay_berne.cpp b/src/core/nonbonded_interactions/gay_berne.cpp index d5732832415..12a9a78b907 100644 --- a/src/core/nonbonded_interactions/gay_berne.cpp +++ b/src/core/nonbonded_interactions/gay_berne.cpp @@ -25,39 +25,15 @@ #include "gay_berne.hpp" #ifdef GAY_BERNE -#include "interactions.hpp" #include "nonbonded_interaction_data.hpp" -#include +#include -int gay_berne_set_params(int part_type_a, int part_type_b, double eps, - double sig, double cut, double k1, double k2, - double mu, double nu) { - IA_parameters *data = get_ia_param_safe(part_type_a, part_type_b); +GayBerne_Parameters::GayBerne_Parameters(double eps, double sig, double cut, + double k1, double k2, double mu, + double nu) + : eps{eps}, sig{sig}, cut{cut}, k1{k1}, k2{k2}, mu{mu}, nu{nu}, + chi1{((k1 * k1) - 1.) / ((k1 * k1) + 1.)}, + chi2{(std::pow(k2, 1. / mu) - 1.) / (std::pow(k2, 1. / mu) + 1.)} {} - if (!data) - return ES_ERROR; - - data->gay_berne.eps = eps; - data->gay_berne.sig = sig; - data->gay_berne.cut = cut; - data->gay_berne.k1 = k1; - data->gay_berne.k2 = k2; - data->gay_berne.mu = mu; - data->gay_berne.nu = nu; - - /* Calculate dependent parameters */ - - data->gay_berne.chi1 = ((data->gay_berne.k1 * data->gay_berne.k1) - 1) / - ((data->gay_berne.k1 * data->gay_berne.k1) + 1); - data->gay_berne.chi2 = - (pow(data->gay_berne.k2, (1 / data->gay_berne.mu)) - 1) / - (pow(data->gay_berne.k2, (1 / data->gay_berne.mu)) + 1); - - /* broadcast interaction parameters */ - mpi_bcast_ia_params(part_type_a, part_type_b); - - return ES_OK; -} - -#endif +#endif // GAY_BERNE diff --git a/src/core/nonbonded_interactions/gay_berne.hpp b/src/core/nonbonded_interactions/gay_berne.hpp index 4696f267af4..b667b83de01 100644 --- a/src/core/nonbonded_interactions/gay_berne.hpp +++ b/src/core/nonbonded_interactions/gay_berne.hpp @@ -44,10 +44,6 @@ #include -int gay_berne_set_params(int part_type_a, int part_type_b, double eps, - double sig, double cut, double k1, double k2, - double mu, double nu); - /** Calculate Gay-Berne force and torques */ inline ParticleForce gb_pair_force(Utils::Vector3d const &ui, Utils::Vector3d const &uj, diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index e844ab84553..1c0be99411f 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -194,7 +194,7 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { #endif #ifdef GAY_BERNE - max_cut_current = std::max(max_cut_current, data.gay_berne.cut); + max_cut_current = std::max(max_cut_current, data.gay_berne.max_cutoff()); #endif #ifdef TABULATED diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index ece89b974fa..a0bee47bb43 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -227,6 +227,10 @@ struct GayBerne_Parameters { double nu = 0.0; double chi1 = 0.0; double chi2 = 0.0; + GayBerne_Parameters() = default; + GayBerne_Parameters(double eps, double sig, double cut, double k1, double k2, + double mu, double nu); + double max_cutoff() const { return cut; } }; /** Thole potential */ diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index 9fd85e33426..da0312ff344 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -47,15 +47,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": int n double k0 - cdef struct GayBerne_Parameters: - double eps - double sig - double cut - double k1 - double k2 - double mu - double nu - cdef struct Thole_Parameters: double scaling_coeff double q1q2 @@ -71,8 +62,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": TabulatedPotential tab - GayBerne_Parameters gay_berne - SmoothStep_Parameters smooth_step DPDParameters dpd_radial @@ -85,13 +74,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": cdef void ia_params_set_state(string) cdef void reset_ia_params() -IF GAY_BERNE: - cdef extern from "nonbonded_interactions/gay_berne.hpp": - int gay_berne_set_params(int part_type_a, int part_type_b, - double eps, double sig, double cut, - double k1, double k2, - double mu, double nu) - IF THOLE: cdef extern from "nonbonded_interactions/thole.hpp": int thole_set_params(int part_type_a, int part_type_b, double scaling_coeff, double q1q2) diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 94756808bc9..65dcf99941e 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -656,36 +656,17 @@ IF HAT == 1: IF GAY_BERNE: - cdef class GayBerneInteraction(NonBondedInteraction): - - def validate_params(self): - """Check that parameters are valid. - - """ - pass - - def _get_params_from_es_core(self): - cdef IA_parameters * ia_params - ia_params = get_ia_param_safe( - self._part_types[0], - self._part_types[1]) - return { - "eps": ia_params.gay_berne.eps, - "sig": ia_params.gay_berne.sig, - "cut": ia_params.gay_berne.cut, - "k1": ia_params.gay_berne.k1, - "k2": ia_params.gay_berne.k2, - "mu": ia_params.gay_berne.mu, - "nu": ia_params.gay_berne.nu} - - def is_active(self): - """Check if interaction is active. - - """ - return (self._params["eps"] > 0) + @script_interface_register + class GayBerneInteraction(NewNonBondedInteraction): + """Gay--Berne interaction. - def set_params(self, **kwargs): - """Set parameters for the Gay-Berne interaction. + Methods + ------- + set_params() + Set or update parameters for the interaction. + Parameters marked as required become optional once the + interaction has been activated for the first time; + subsequent calls to this method update the existing values. Parameters ---------- @@ -705,20 +686,15 @@ IF GAY_BERNE: nu : :obj:`float`, optional Adjustable exponent. - """ - super().set_params(**kwargs) + """ - def _set_params_in_es_core(self): - if gay_berne_set_params(self._part_types[0], - self._part_types[1], - self._params["eps"], - self._params["sig"], - self._params["cut"], - self._params["k1"], - self._params["k2"], - self._params["mu"], - self._params["nu"]): - raise Exception("Could not set Gay-Berne parameters") + _so_name = "Interactions::InteractionGayBerne" + + def is_active(self): + """Check if interaction is active. + + """ + return self.eps > 0. def default_params(self): """Python dictionary of default parameters. @@ -1308,8 +1284,6 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): self.smooth_step = SmoothStepInteraction(_type1, _type2) IF TABULATED: self.tabulated = TabulatedNonBonded(_type1, _type2) - IF GAY_BERNE: - self.gay_berne = GayBerneInteraction(_type1, _type2) IF DPD: self.dpd = DPDInteraction(_type1, _type2) IF THOLE: diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index a8aa0d667bf..36fe39350c9 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -488,6 +488,35 @@ class InteractionHat : public InteractionPotentialInterface<::Hat_Parameters> { }; #endif // HAT +#ifdef GAY_BERNE +class InteractionGayBerne + : public InteractionPotentialInterface<::GayBerne_Parameters> { +protected: + CoreInteraction IA_parameters::*get_ptr_offset() const override { + return &::IA_parameters::gay_berne; + } + +public: + InteractionGayBerne() { + add_parameters({ + make_autoparameter(&CoreInteraction::eps, "eps"), + make_autoparameter(&CoreInteraction::sig, "sig"), + make_autoparameter(&CoreInteraction::cut, "cut"), + make_autoparameter(&CoreInteraction::k1, "k1"), + make_autoparameter(&CoreInteraction::k2, "k2"), + make_autoparameter(&CoreInteraction::mu, "mu"), + make_autoparameter(&CoreInteraction::nu, "nu"), + }); + } + + void make_new_instance(VariantMap const ¶ms) override { + m_ia_si = make_shared_from_args( + params, "eps", "sig", "cut", "k1", "k2", "mu", "nu"); + } +}; +#endif // GAY_BERNE + class NonBondedInteractionHandle : public AutoParameters { std::array m_types = {-1, -1}; @@ -528,6 +557,9 @@ class NonBondedInteractionHandle #ifdef HAT std::shared_ptr m_hat; #endif +#ifdef GAY_BERNE + std::shared_ptr m_gay_berne; +#endif template auto make_autoparameter(std::shared_ptr &member, const char *key) const { @@ -581,6 +613,9 @@ class NonBondedInteractionHandle #endif #ifdef HAT make_autoparameter(m_hat, "hat"), +#endif +#ifdef GAY_BERNE + make_autoparameter(m_gay_berne, "gay_berne"), #endif }); } @@ -669,6 +704,10 @@ class NonBondedInteractionHandle #ifdef HAT set_member(m_hat, "hat", "Interactions::InteractionHat", params); +#endif +#ifdef GAY_BERNE + set_member( + m_gay_berne, "gay_berne", "Interactions::InteractionGayBerne", params); #endif } diff --git a/src/script_interface/interactions/initialize.cpp b/src/script_interface/interactions/initialize.cpp index ad1f5c08b6b..00e0e121507 100644 --- a/src/script_interface/interactions/initialize.cpp +++ b/src/script_interface/interactions/initialize.cpp @@ -94,6 +94,9 @@ void initialize(Utils::Factory *om) { #ifdef HAT om->register_new("Interactions::InteractionHat"); #endif +#ifdef GAY_BERNE + om->register_new("Interactions::InteractionGayBerne"); +#endif } } // namespace Interactions } // namespace ScriptInterface From 23448f69a4c710c9f35734f69bd860767a9e0b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 7 Sep 2022 18:56:33 +0200 Subject: [PATCH 63/85] Rewrite Tabulated interaction interface --- src/core/CMakeLists.txt | 3 +- src/core/TabulatedPotential.cpp | 55 +++++++ src/core/TabulatedPotential.hpp | 6 +- src/core/bonded_interactions/bonded_tab.cpp | 15 +- .../nonbonded_interactions/CMakeLists.txt | 1 - .../nonbonded_interactions/nonbonded_tab.cpp | 58 ------- .../nonbonded_interactions/nonbonded_tab.hpp | 19 +-- src/python/espressomd/interactions.pxd | 17 --- src/python/espressomd/interactions.pyx | 142 ++++++++---------- .../interactions/NonBondedInteraction.hpp | 45 ++++++ .../interactions/initialize.cpp | 3 + testsuite/python/tabulated.py | 13 ++ 12 files changed, 188 insertions(+), 189 deletions(-) create mode 100644 src/core/TabulatedPotential.cpp delete mode 100644 src/core/nonbonded_interactions/nonbonded_tab.cpp diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9426568be13..852b30d5f4e 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -55,7 +55,8 @@ add_library( virtual_sites.cpp exclusions.cpp PartCfg.cpp - EspressoSystemStandAlone.cpp) + EspressoSystemStandAlone.cpp + TabulatedPotential.cpp) add_library(espresso::core ALIAS espresso_core) if(CUDA) diff --git a/src/core/TabulatedPotential.cpp b/src/core/TabulatedPotential.cpp new file mode 100644 index 00000000000..2ba8a6e61a1 --- /dev/null +++ b/src/core/TabulatedPotential.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010-2022 The ESPResSo project + * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 + * Max-Planck-Institute for Polymer Research, Theory Group + * + * This file is part of ESPResSo. + * + * ESPResSo is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ESPResSo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "TabulatedPotential.hpp" + +#include +#include + +TabulatedPotential::TabulatedPotential(double minval, double maxval, + std::vector const &force, + std::vector const &energy) + : minval{minval}, maxval{maxval} { + + if (minval > maxval) { + throw std::domain_error("TabulatedPotential parameter 'max' must be " + "larger than or equal to parameter 'min'"); + } + if (minval != -1.) { + if (minval == maxval and force.size() != 1) { + throw std::domain_error( + "TabulatedPotential parameter 'force' must contain 1 element"); + } + if (force.empty()) { + throw std::domain_error("TabulatedPotential parameter 'force' must " + "contain at least 1 element"); + } + if (force.size() != energy.size()) { + throw std::invalid_argument("TabulatedPotential parameter 'force' must " + "have the same size as parameter 'energy'"); + } + invstepsize = static_cast(force.size() - 1) / (maxval - minval); + } else { + invstepsize = 0.; + } + force_tab = force; + energy_tab = energy; +} diff --git a/src/core/TabulatedPotential.hpp b/src/core/TabulatedPotential.hpp index f05a4db85fe..819dee91060 100644 --- a/src/core/TabulatedPotential.hpp +++ b/src/core/TabulatedPotential.hpp @@ -25,7 +25,6 @@ #include #include -#include #include /** Evaluate forces and energies using a custom potential profile. @@ -46,6 +45,11 @@ struct TabulatedPotential { /** Tabulated energies. */ std::vector energy_tab; + TabulatedPotential() = default; + TabulatedPotential(double minval, double maxval, + std::vector const &force, + std::vector const &energy); + /** Evaluate the force at position @p x. * @param x Bond length/angle * @return Interpolated force. diff --git a/src/core/bonded_interactions/bonded_tab.cpp b/src/core/bonded_interactions/bonded_tab.cpp index ff86e49b0af..74762482f91 100644 --- a/src/core/bonded_interactions/bonded_tab.cpp +++ b/src/core/bonded_interactions/bonded_tab.cpp @@ -30,20 +30,7 @@ TabulatedBond::TabulatedBond(double min, double max, std::vector const &energy, std::vector const &force) { - assert(max >= min); - assert((max == min) || force.size() > 1); - assert(force.size() == energy.size()); - - auto tab_pot = this->pot = std::make_shared(); - - /* set table limits */ - tab_pot->minval = min; - tab_pot->maxval = max; - - tab_pot->invstepsize = static_cast(force.size() - 1) / (max - min); - - tab_pot->force_tab = force; - tab_pot->energy_tab = energy; + pot = std::make_shared(min, max, force, energy); } TabulatedDistanceBond::TabulatedDistanceBond(double min, double max, diff --git a/src/core/nonbonded_interactions/CMakeLists.txt b/src/core/nonbonded_interactions/CMakeLists.txt index 66570208137..d655a1b11f0 100644 --- a/src/core/nonbonded_interactions/CMakeLists.txt +++ b/src/core/nonbonded_interactions/CMakeLists.txt @@ -12,7 +12,6 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/ljgen.cpp ${CMAKE_CURRENT_SOURCE_DIR}/morse.cpp ${CMAKE_CURRENT_SOURCE_DIR}/nonbonded_interaction_data.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/nonbonded_tab.cpp ${CMAKE_CURRENT_SOURCE_DIR}/soft_sphere.cpp ${CMAKE_CURRENT_SOURCE_DIR}/smooth_step.cpp ${CMAKE_CURRENT_SOURCE_DIR}/thole.cpp diff --git a/src/core/nonbonded_interactions/nonbonded_tab.cpp b/src/core/nonbonded_interactions/nonbonded_tab.cpp deleted file mode 100644 index d9cbaef5ebe..00000000000 --- a/src/core/nonbonded_interactions/nonbonded_tab.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2010-2022 The ESPResSo project - * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 - * Max-Planck-Institute for Polymer Research, Theory Group - * - * This file is part of ESPResSo. - * - * ESPResSo is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ESPResSo is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/** \file - * - * Implementation of \ref nonbonded_tab.hpp - */ -#include "nonbonded_interactions/nonbonded_tab.hpp" - -#ifdef TABULATED -#include "interactions.hpp" - -#include - -#include -#include - -int tabulated_set_params(int part_type_a, int part_type_b, double min, - double max, std::vector const &energy, - std::vector const &force) { - auto data = get_ia_param_safe(part_type_a, part_type_b); - assert(max >= min); - assert((max == min) || force.size() > 1); - assert(force.size() == energy.size()); - - data->tab.maxval = max; - data->tab.minval = min; - if (max == min) - data->tab.invstepsize = 0; - else - data->tab.invstepsize = static_cast(force.size() - 1) / (max - min); - - data->tab.force_tab = force; - data->tab.energy_tab = energy; - - mpi_bcast_ia_params(part_type_a, part_type_b); - - return ES_OK; -} - -#endif diff --git a/src/core/nonbonded_interactions/nonbonded_tab.hpp b/src/core/nonbonded_interactions/nonbonded_tab.hpp index 692d17ef604..3b1d35a2939 100644 --- a/src/core/nonbonded_interactions/nonbonded_tab.hpp +++ b/src/core/nonbonded_interactions/nonbonded_tab.hpp @@ -25,7 +25,6 @@ * Routines to calculate the energy and/or force for particle pairs via * interpolation of lookup tables. * - * Implementation in \ref nonbonded_tab.cpp. * Needs feature TABULATED compiled in (see \ref config.hpp). */ @@ -40,21 +39,6 @@ #include -/** Set the parameters of a non-bonded tabulated potential. - * ia_params and force/energy tables are communicated to each node - * - * @param part_type_a particle type for which the interaction is defined - * @param part_type_b particle type for which the interaction is defined - * @param min @copybrief TabulatedPotential::minval - * @param max @copybrief TabulatedPotential::maxval - * @param energy @copybrief TabulatedPotential::energy_tab - * @param force @copybrief TabulatedPotential::force_tab - * @retval ES_OK - */ -int tabulated_set_params(int part_type_a, int part_type_b, double min, - double max, std::vector const &energy, - std::vector const &force); - /** Calculate a non-bonded pair force factor by linear interpolation from a * table. */ @@ -75,6 +59,5 @@ inline double tabulated_pair_energy(IA_parameters const &ia_params, return 0.0; } -#endif - +#endif // TABULATED #endif diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index da0312ff344..22ed81c6066 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -20,7 +20,6 @@ # Handling of interactions from libcpp.string cimport string -from libcpp.vector cimport vector from libc cimport stdint from .thermostat cimport thermalized_bond @@ -31,13 +30,6 @@ include "myconfig.pxi" cdef extern from "config.hpp": pass -cdef extern from "TabulatedPotential.hpp": - struct TabulatedPotential: - double maxval - double minval - vector[double] energy_tab - vector[double] force_tab - cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": cdef struct SmoothStep_Parameters: double eps @@ -60,8 +52,6 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": cdef struct IA_parameters: - TabulatedPotential tab - SmoothStep_Parameters smooth_step DPDParameters dpd_radial @@ -90,13 +80,6 @@ IF DPD: double gamma, double k, double r_c, int wf, double tgamma, double tr_c, int twf) -IF TABULATED: - cdef extern from "nonbonded_interactions/nonbonded_tab.hpp": - int tabulated_set_params(int part_type_a, int part_type_b, - double min, double max, - vector[double] energy, - vector[double] force) - cdef extern from "script_interface/interactions/bonded.hpp": int bonded_ia_params_zero_based_type(int bond_id) except + diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 65dcf99941e..e7a09e3b981 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -720,6 +720,69 @@ IF GAY_BERNE: """ return {"eps", "sig", "cut", "k1", "k2", "mu", "nu"} +IF TABULATED: + + @script_interface_register + class TabulatedNonBonded(NewNonBondedInteraction): + """Tabulated interaction. + + Methods + ------- + set_params() + Set or update parameters for the interaction. + Parameters marked as required become optional once the + interaction has been activated for the first time; + subsequent calls to this method update the existing values. + + Parameters + ---------- + min : :obj:`float`, + The minimal interaction distance. + max : :obj:`float`, + The maximal interaction distance. + energy: array_like of :obj:`float` + The energy table. + force: array_like of :obj:`float` + The force table. + + """ + + _so_name = "Interactions::InteractionTabulated" + + def is_active(self): + """Check if interaction is active. + + """ + return self.cutoff > 0 + + def default_params(self): + """Python dictionary of default parameters. + + """ + return {} + + def type_name(self): + """Name of the potential. + + """ + return "Tabulated" + + def valid_keys(self): + """All parameters that can be set. + + """ + return {"min", "max", "energy", "force"} + + def required_keys(self): + """Parameters that have to be set. + + """ + return {"min", "max", "energy", "force"} + + @property + def cutoff(self): + return self.call_method("get_cutoff") + IF DPD: cdef class DPDInteraction(NonBondedInteraction): @@ -1282,8 +1345,6 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): # Here, add one line for each nonbonded ia IF SMOOTH_STEP: self.smooth_step = SmoothStepInteraction(_type1, _type2) - IF TABULATED: - self.tabulated = TabulatedNonBonded(_type1, _type2) IF DPD: self.dpd = DPDInteraction(_type1, _type2) IF THOLE: @@ -2042,83 +2103,6 @@ IF TABULATED: raise ValueError(f"Tabulated dihedral expects forces/energies " f"within the range [0, 2*pi], got {phi}") - cdef class TabulatedNonBonded(NonBondedInteraction): - - cdef int state - - def __init__(self, *args, **kwargs): - self.state = -1 - super().__init__(*args, **kwargs) - - def type_number(self): - return "TABULATED_NONBONDED" - - def type_name(self): - """Name of the potential. - - """ - return "TABULATED" - - def valid_keys(self): - """All parameters that can be set. - - """ - return {"min", "max", "energy", "force"} - - def required_keys(self): - """Parameters that have to be set. - - """ - return {"min", "max", "energy", "force"} - - def set_params(self, **kwargs): - """Set parameters for the TabulatedNonBonded interaction. - - Parameters - ---------- - - min : :obj:`float`, - The minimal interaction distance. - max : :obj:`float`, - The maximal interaction distance. - energy: array_like of :obj:`float` - The energy table. - force: array_like of :obj:`float` - The force table. - - """ - super().set_params(**kwargs) - - def set_default_params(self): - """Set parameters that are not required to their default value. - - """ - self._params = {} - - def _get_params_from_es_core(self): - cdef IA_parameters * ia_params = get_ia_param_safe( - self._part_types[0], - self._part_types[1]) - - return {'min': ia_params.tab.minval, - 'max': ia_params.tab.maxval, - 'energy': ia_params.tab.energy_tab, - 'force': ia_params.tab.force_tab} - - def _set_params_in_es_core(self): - self.state = tabulated_set_params(self._part_types[0], - self._part_types[1], - self._params["min"], - self._params["max"], - self._params["energy"], - self._params["force"]) - - def is_active(self): - """Check if interaction is active. - - """ - return (self.state == 0) - @script_interface_register class Virtual(BondedInteraction): diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index 36fe39350c9..0cdc53b4523 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -517,6 +517,41 @@ class InteractionGayBerne }; #endif // GAY_BERNE +#ifdef TABULATED +class InteractionTabulated + : public InteractionPotentialInterface<::TabulatedPotential> { +protected: + CoreInteraction IA_parameters::*get_ptr_offset() const override { + return &::IA_parameters::tab; + } + +public: + InteractionTabulated() { + add_parameters({ + make_autoparameter(&CoreInteraction::minval, "min"), + make_autoparameter(&CoreInteraction::maxval, "max"), + make_autoparameter(&CoreInteraction::force_tab, "force"), + make_autoparameter(&CoreInteraction::energy_tab, "energy"), + }); + } + + void make_new_instance(VariantMap const ¶ms) override { + m_ia_si = make_shared_from_args, std::vector>( + params, "min", "max", "force", "energy"); + } + + Variant do_call_method(std::string const &name, + VariantMap const ¶ms) override { + if (name == "get_cutoff") { + return m_ia_si.get()->cutoff(); + } + return InteractionPotentialInterface::do_call_method( + name, params); + } +}; +#endif // TABULATED + class NonBondedInteractionHandle : public AutoParameters { std::array m_types = {-1, -1}; @@ -560,6 +595,9 @@ class NonBondedInteractionHandle #ifdef GAY_BERNE std::shared_ptr m_gay_berne; #endif +#ifdef TABULATED + std::shared_ptr m_tabulated; +#endif template auto make_autoparameter(std::shared_ptr &member, const char *key) const { @@ -616,6 +654,9 @@ class NonBondedInteractionHandle #endif #ifdef GAY_BERNE make_autoparameter(m_gay_berne, "gay_berne"), +#endif +#ifdef TABULATED + make_autoparameter(m_tabulated, "tabulated"), #endif }); } @@ -708,6 +749,10 @@ class NonBondedInteractionHandle #ifdef GAY_BERNE set_member( m_gay_berne, "gay_berne", "Interactions::InteractionGayBerne", params); +#endif +#ifdef TABULATED + set_member( + m_tabulated, "tabulated", "Interactions::InteractionTabulated", params); #endif } diff --git a/src/script_interface/interactions/initialize.cpp b/src/script_interface/interactions/initialize.cpp index 00e0e121507..5e33081b41d 100644 --- a/src/script_interface/interactions/initialize.cpp +++ b/src/script_interface/interactions/initialize.cpp @@ -97,6 +97,9 @@ void initialize(Utils::Factory *om) { #ifdef GAY_BERNE om->register_new("Interactions::InteractionGayBerne"); #endif +#ifdef TABULATED + om->register_new("Interactions::InteractionTabulated"); +#endif } } // namespace Interactions } // namespace ScriptInterface diff --git a/testsuite/python/tabulated.py b/testsuite/python/tabulated.py index 199d12528d1..198fdd34cc1 100644 --- a/testsuite/python/tabulated.py +++ b/testsuite/python/tabulated.py @@ -71,6 +71,19 @@ def test_non_bonded(self): self.system.non_bonded_inter[0, 0].tabulated.set_params( min=-1, max=-1, energy=[], force=[]) + with self.assertRaisesRegex(ValueError, "TabulatedPotential parameter 'max' must be larger than or equal to parameter 'min'"): + espressomd.interactions.TabulatedNonBonded( + min=1., max=0., energy=[0.], force=[0.]) + with self.assertRaisesRegex(ValueError, "TabulatedPotential parameter 'force' must contain 1 element"): + espressomd.interactions.TabulatedNonBonded( + min=1., max=1., energy=[0., 0.], force=[0., 0.]) + with self.assertRaisesRegex(ValueError, "TabulatedPotential parameter 'force' must contain at least 1 element"): + espressomd.interactions.TabulatedNonBonded( + min=1., max=2., energy=[], force=[]) + with self.assertRaisesRegex(ValueError, "TabulatedPotential parameter 'force' must have the same size as parameter 'energy'"): + espressomd.interactions.TabulatedNonBonded( + min=1., max=2., energy=[0.], force=[0., 0.]) + @utx.skipIfMissingFeatures("TABULATED") def test_bonded(self): tb = espressomd.interactions.TabulatedDistance( From 7314aea40dcc1f12b9ec4ac65a968cb40287564f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 8 Sep 2022 01:18:18 +0200 Subject: [PATCH 64/85] Rewrite DPD interaction interface --- src/core/dpd.cpp | 36 ++++-------- src/core/dpd.hpp | 2 - .../nonbonded_interaction_data.cpp | 3 +- .../nonbonded_interaction_data.hpp | 18 ++++-- src/python/espressomd/interactions.pxd | 15 ----- src/python/espressomd/interactions.pyx | 57 +++++-------------- .../interactions/NonBondedInteraction.hpp | 48 ++++++++++++++++ .../interactions/initialize.cpp | 3 + 8 files changed, 92 insertions(+), 90 deletions(-) diff --git a/src/core/dpd.cpp b/src/core/dpd.cpp index d59b6007c12..5b4488b17bb 100644 --- a/src/core/dpd.cpp +++ b/src/core/dpd.cpp @@ -64,28 +64,15 @@ Vector3d dpd_noise(int pid1, int pid2) { (pid1 < pid2) ? pid1 : pid2); } -int dpd_set_params(int part_type_a, int part_type_b, double gamma, double k, - double r_c, int wf, double tgamma, double tr_c, int twf) { - auto &ia_params = *get_ia_param_safe(part_type_a, part_type_b); - - ia_params.dpd_radial = DPDParameters{gamma, k, r_c, wf, -1.}; - ia_params.dpd_trans = DPDParameters{tgamma, k, tr_c, twf, -1.}; - - /* broadcast interaction parameters */ - mpi_bcast_ia_params(part_type_a, part_type_b); - - return ES_OK; -} - void dpd_init(double kT, double time_step) { for (int type_a = 0; type_a < max_seen_particle_type; type_a++) { for (int type_b = 0; type_b < max_seen_particle_type; type_b++) { IA_parameters &ia_params = get_ia_param(type_a, type_b); - ia_params.dpd_radial.pref = - sqrt(24.0 * kT * ia_params.dpd_radial.gamma / time_step); - ia_params.dpd_trans.pref = - sqrt(24.0 * kT * ia_params.dpd_trans.gamma / time_step); + ia_params.dpd.radial.pref = + sqrt(24.0 * kT * ia_params.dpd.radial.gamma / time_step); + ia_params.dpd.trans.pref = + sqrt(24.0 * kT * ia_params.dpd.trans.gamma / time_step); } } } @@ -116,19 +103,19 @@ Utils::Vector3d dpd_pair_force(Particle const &p1, Particle const &p2, IA_parameters const &ia_params, Utils::Vector3d const &d, double dist, double dist2) { - if (ia_params.dpd_radial.cutoff <= 0.0 && ia_params.dpd_trans.cutoff <= 0.0) { + if (ia_params.dpd.radial.cutoff <= 0.0 && ia_params.dpd.trans.cutoff <= 0.0) { return {}; } auto const v21 = box_geo.velocity_difference(p1.pos(), p2.pos(), p1.v(), p2.v()); auto const noise_vec = - (ia_params.dpd_radial.pref > 0.0 || ia_params.dpd_trans.pref > 0.0) + (ia_params.dpd.radial.pref > 0.0 || ia_params.dpd.trans.pref > 0.0) ? dpd_noise(p1.id(), p2.id()) : Vector3d{}; - auto const f_r = dpd_pair_force(ia_params.dpd_radial, v21, dist, noise_vec); - auto const f_t = dpd_pair_force(ia_params.dpd_trans, v21, dist, noise_vec); + auto const f_r = dpd_pair_force(ia_params.dpd.radial, v21, dist, noise_vec); + auto const f_t = dpd_pair_force(ia_params.dpd.trans, v21, dist, noise_vec); /* Projection operator to radial direction */ auto const P = tensor_product(d / dist2, d); @@ -150,8 +137,8 @@ static auto dpd_viscous_stress_local() { auto const &ia_params = get_ia_param(p1.type(), p2.type()); auto const dist = std::sqrt(d.dist2); - auto const f_r = dpd_pair_force(ia_params.dpd_radial, v21, dist, {}); - auto const f_t = dpd_pair_force(ia_params.dpd_trans, v21, dist, {}); + auto const f_r = dpd_pair_force(ia_params.dpd.radial, v21, dist, {}); + auto const f_t = dpd_pair_force(ia_params.dpd.trans, v21, dist, {}); /* Projection operator to radial direction */ auto const P = tensor_product(d.vec21 / d.dist2, d.vec21); @@ -188,4 +175,5 @@ Utils::Vector9d dpd_stress() { return Utils::flatten(stress) / volume; } -#endif + +#endif // DPD diff --git a/src/core/dpd.hpp b/src/core/dpd.hpp index 091ca91cde7..9ca2d1878ad 100644 --- a/src/core/dpd.hpp +++ b/src/core/dpd.hpp @@ -36,8 +36,6 @@ struct IA_parameters; -int dpd_set_params(int part_type_a, int part_type_b, double gamma, double k, - double r_c, int wf, double tgamma, double tr_c, int twf); void dpd_init(double kT, double time_step); Utils::Vector3d dpd_pair_force(Particle const &p1, Particle const &p2, diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index 1c0be99411f..c813a250fe8 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -145,8 +145,7 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { #endif #ifdef DPD - max_cut_current = std::max( - max_cut_current, std::max(data.dpd_radial.cutoff, data.dpd_trans.cutoff)); + max_cut_current = std::max(max_cut_current, data.dpd.max_cutoff()); #endif #ifdef LENNARD_JONES_GENERIC diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index a0bee47bb43..98d95a817ec 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -248,6 +248,18 @@ struct DPDParameters { double pref = 0.0; }; +struct DPD_Parameters { + DPDParameters radial; + DPDParameters trans; + DPD_Parameters() = default; + DPD_Parameters(double gamma, double k, double r_c, int wf, double tgamma, + double tr_c, int twf) { + radial = DPDParameters{gamma, k, r_c, wf, -1.}; + trans = DPDParameters{tgamma, k, tr_c, twf, -1.}; + } + double max_cutoff() const { return std::max(radial.cutoff, trans.cutoff); } +}; + /** Data structure containing the interaction parameters for non-bonded * interactions. * Access via get_ia_param(i, j) with @@ -321,11 +333,7 @@ struct IA_parameters { #endif #ifdef DPD - /** \name DPD as interaction */ - /**@{*/ - DPDParameters dpd_radial; - DPDParameters dpd_trans; - /**@}*/ + DPD_Parameters dpd; #endif #ifdef THOLE diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index 22ed81c6066..2a50bd6f596 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -43,20 +43,10 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": double scaling_coeff double q1q2 - cdef struct DPDParameters: - double gamma - double k - double cutoff - int wf - double pref - cdef struct IA_parameters: SmoothStep_Parameters smooth_step - DPDParameters dpd_radial - DPDParameters dpd_trans - Thole_Parameters thole cdef IA_parameters * get_ia_param_safe(int i, int j) @@ -74,11 +64,6 @@ IF SMOOTH_STEP: double d, int n, double eps, double k0, double sig, double cut) -IF DPD: - cdef extern from "dpd.hpp": - int dpd_set_params(int part_type_a, int part_type_b, - double gamma, double k, double r_c, int wf, - double tgamma, double tr_c, int twf) cdef extern from "script_interface/interactions/bonded.hpp": int bonded_ia_params_zero_based_type(int bond_id) except + diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index e7a09e3b981..7d8d5660a4a 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -785,34 +785,17 @@ IF TABULATED: IF DPD: - cdef class DPDInteraction(NonBondedInteraction): - - def validate_params(self): - """Check that parameters are valid. - - """ - pass - - def _get_params_from_es_core(self): - cdef IA_parameters * ia_params - ia_params = get_ia_param_safe( - self._part_types[0], self._part_types[1]) - return { - "weight_function": ia_params.dpd_radial.wf, - "gamma": ia_params.dpd_radial.gamma, - "k": ia_params.dpd_radial.k, - "r_cut": ia_params.dpd_radial.cutoff, - "trans_weight_function": ia_params.dpd_trans.wf, - "trans_gamma": ia_params.dpd_trans.gamma, - "trans_r_cut": ia_params.dpd_trans.cutoff - } - - def is_active(self): - return (self._params["r_cut"] > 0) or ( - self._params["trans_r_cut"] > 0) + @script_interface_register + class DPDInteraction(NewNonBondedInteraction): + """DPD interaction. - def set_params(self, **kwargs): - """Set parameters for the DPD interaction. + Methods + ------- + set_params() + Set or update parameters for the interaction. + Parameters marked as required become optional once the + interaction has been activated for the first time; + subsequent calls to this method update the existing values. Parameters ---------- @@ -833,20 +816,12 @@ IF DPD: trans_r_cut : :obj:`float` Cutoff of the orthogonal part - """ - super().set_params(**kwargs) + """ - def _set_params_in_es_core(self): - if dpd_set_params(self._part_types[0], - self._part_types[1], - self._params["gamma"], - self._params["k"], - self._params["r_cut"], - self._params["weight_function"], - self._params["trans_gamma"], - self._params["trans_r_cut"], - self._params["trans_weight_function"]): - raise Exception("Could not set DPD parameters") + _so_name = "Interactions::InteractionDPD" + + def is_active(self): + return self.r_cut > 0. or self.trans_r_cut > 0. def default_params(self): return { @@ -1345,8 +1320,6 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): # Here, add one line for each nonbonded ia IF SMOOTH_STEP: self.smooth_step = SmoothStepInteraction(_type1, _type2) - IF DPD: - self.dpd = DPDInteraction(_type1, _type2) IF THOLE: self.thole = TholeInteraction(_type1, _type2) diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index 0cdc53b4523..420a866ee80 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -38,6 +38,7 @@ #include #include #include +#include #include namespace ScriptInterface { @@ -552,6 +553,43 @@ class InteractionTabulated }; #endif // TABULATED +#ifdef DPD +class InteractionDPD : public InteractionPotentialInterface<::DPD_Parameters> { +protected: + CoreInteraction IA_parameters::*get_ptr_offset() const override { + return &::IA_parameters::dpd; + } + +public: + InteractionDPD() { + add_parameters({ + {"weight_function", AutoParameter::read_only, + [this]() { return m_ia_si.get()->radial.wf; }}, + {"gamma", AutoParameter::read_only, + [this]() { return m_ia_si.get()->radial.gamma; }}, + {"k", AutoParameter::read_only, + [this]() { return m_ia_si.get()->radial.k; }}, + {"r_cut", AutoParameter::read_only, + [this]() { return m_ia_si.get()->radial.cutoff; }}, + {"trans_weight_function", AutoParameter::read_only, + [this]() { return m_ia_si.get()->trans.wf; }}, + {"trans_gamma", AutoParameter::read_only, + [this]() { return m_ia_si.get()->trans.gamma; }}, + {"trans_r_cut", AutoParameter::read_only, + [this]() { return m_ia_si.get()->trans.cutoff; }}, + }); + std::ignore = get_ptr_offset(); // for code coverage + } + + void make_new_instance(VariantMap const ¶ms) override { + m_ia_si = make_shared_from_args( + params, "gamma", "k", "r_cut", "weight_function", "trans_gamma", + "trans_r_cut", "trans_weight_function"); + } +}; +#endif // DPD + class NonBondedInteractionHandle : public AutoParameters { std::array m_types = {-1, -1}; @@ -598,6 +636,9 @@ class NonBondedInteractionHandle #ifdef TABULATED std::shared_ptr m_tabulated; #endif +#ifdef DPD + std::shared_ptr m_dpd; +#endif template auto make_autoparameter(std::shared_ptr &member, const char *key) const { @@ -657,6 +698,9 @@ class NonBondedInteractionHandle #endif #ifdef TABULATED make_autoparameter(m_tabulated, "tabulated"), +#endif +#ifdef DPD + make_autoparameter(m_dpd, "dpd"), #endif }); } @@ -753,6 +797,10 @@ class NonBondedInteractionHandle #ifdef TABULATED set_member( m_tabulated, "tabulated", "Interactions::InteractionTabulated", params); +#endif +#ifdef DPD + set_member(m_dpd, "dpd", "Interactions::InteractionDPD", + params); #endif } diff --git a/src/script_interface/interactions/initialize.cpp b/src/script_interface/interactions/initialize.cpp index 5e33081b41d..02a63dab2f2 100644 --- a/src/script_interface/interactions/initialize.cpp +++ b/src/script_interface/interactions/initialize.cpp @@ -100,6 +100,9 @@ void initialize(Utils::Factory *om) { #ifdef TABULATED om->register_new("Interactions::InteractionTabulated"); #endif +#ifdef DPD + om->register_new("Interactions::InteractionDPD"); +#endif } } // namespace Interactions } // namespace ScriptInterface From 3a9102aa6322548ad4f58519403a39519b85b750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 8 Sep 2022 12:28:12 +0200 Subject: [PATCH 65/85] Rewrite Thole interaction interface --- .../nonbonded_interactions/CMakeLists.txt | 1 - .../nonbonded_interaction_data.cpp | 2 +- .../nonbonded_interaction_data.hpp | 7 +- src/core/nonbonded_interactions/thole.cpp | 47 -------- src/core/nonbonded_interactions/thole.hpp | 3 - src/python/espressomd/interactions.pxd | 10 -- src/python/espressomd/interactions.pyx | 106 ++++++++---------- .../interactions/NonBondedInteraction.hpp | 32 ++++++ .../interactions/initialize.cpp | 3 + 9 files changed, 87 insertions(+), 124 deletions(-) delete mode 100644 src/core/nonbonded_interactions/thole.cpp diff --git a/src/core/nonbonded_interactions/CMakeLists.txt b/src/core/nonbonded_interactions/CMakeLists.txt index d655a1b11f0..1e52b0fe4d0 100644 --- a/src/core/nonbonded_interactions/CMakeLists.txt +++ b/src/core/nonbonded_interactions/CMakeLists.txt @@ -14,5 +14,4 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/nonbonded_interaction_data.cpp ${CMAKE_CURRENT_SOURCE_DIR}/soft_sphere.cpp ${CMAKE_CURRENT_SOURCE_DIR}/smooth_step.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/thole.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wca.cpp) diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index c813a250fe8..6e000f81e99 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -202,7 +202,7 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { #ifdef THOLE // If THOLE is active, use p3m cutoff - if (data.thole.scaling_coeff != 0) + if (data.thole.scaling_coeff != 0.) max_cut_current = std::max(max_cut_current, Coulomb::cutoff()); #endif diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index 98d95a817ec..f4a9feac3d0 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -235,8 +235,11 @@ struct GayBerne_Parameters { /** Thole potential */ struct Thole_Parameters { - double scaling_coeff; - double q1q2; + double scaling_coeff = 0.; // inactive cutoff is 0 + double q1q2 = 0.; + Thole_Parameters() = default; + Thole_Parameters(double scaling_coeff, double q1q2) + : scaling_coeff{scaling_coeff}, q1q2{q1q2} {} }; /** DPD potential */ diff --git a/src/core/nonbonded_interactions/thole.cpp b/src/core/nonbonded_interactions/thole.cpp deleted file mode 100644 index fb750a9ac51..00000000000 --- a/src/core/nonbonded_interactions/thole.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2010-2022 The ESPResSo project - * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 - * Max-Planck-Institute for Polymer Research, Theory Group - * - * This file is part of ESPResSo. - * - * ESPResSo is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ESPResSo is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/** \file - * - * Implementation of \ref thole.hpp - */ -#include "thole.hpp" - -#ifdef THOLE -#include "interactions.hpp" - -#include - -int thole_set_params(int part_type_a, int part_type_b, double scaling_coeff, - double q1q2) { - IA_parameters *data = get_ia_param_safe(part_type_a, part_type_b); - - if (!data) - return ES_ERROR; - - data->thole.scaling_coeff = scaling_coeff; - data->thole.q1q2 = q1q2; - - /* broadcast interaction parameters */ - mpi_bcast_ia_params(part_type_a, part_type_b); - - return ES_OK; -} -#endif diff --git a/src/core/nonbonded_interactions/thole.hpp b/src/core/nonbonded_interactions/thole.hpp index 5c0eb75732b..9c9f4acdebb 100644 --- a/src/core/nonbonded_interactions/thole.hpp +++ b/src/core/nonbonded_interactions/thole.hpp @@ -41,9 +41,6 @@ #include -int thole_set_params(int part_type_a, int part_type_b, double scaling_coeff, - double q1q2); - /** Calculate Thole force */ inline Utils::Vector3d thole_pair_force(Particle const &p1, Particle const &p2, diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index 2a50bd6f596..3a30ad945af 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -39,25 +39,15 @@ cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": int n double k0 - cdef struct Thole_Parameters: - double scaling_coeff - double q1q2 - cdef struct IA_parameters: SmoothStep_Parameters smooth_step - Thole_Parameters thole - cdef IA_parameters * get_ia_param_safe(int i, int j) cdef string ia_params_get_state() cdef void ia_params_set_state(string) cdef void reset_ia_params() -IF THOLE: - cdef extern from "nonbonded_interactions/thole.hpp": - int thole_set_params(int part_type_a, int part_type_b, double scaling_coeff, double q1q2) - IF SMOOTH_STEP: cdef extern from "nonbonded_interactions/smooth_step.hpp": int smooth_step_set_params(int part_type_a, int part_type_b, diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 7d8d5660a4a..fa2859b2455 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -1289,6 +1289,52 @@ IF GAUSSIAN == 1: """ return {"eps", "sig", "cutoff"} +IF THOLE: + + @script_interface_register + class TholeInteraction(NewNonBondedInteraction): + """Thole interaction. + + Methods + ------- + set_params() + Set or update parameters for the interaction. + Parameters marked as required become optional once the + interaction has been activated for the first time; + subsequent calls to this method update the existing values. + + Parameters + ---------- + scaling_coeff : :obj:`float` + The factor used in the Thole damping function between + polarizable particles i and j. Usually calculated by + the polarizabilities :math:`\\alpha_i`, :math:`\\alpha_j` + and damping parameters :math:`a_i`, :math:`a_j` via + :math:`s_{ij} = \\frac{(a_i+a_j)/2}{((\\alpha_i\\cdot\\alpha_j)^{1/2})^{1/3}}` + q1q2: :obj:`float` + Charge factor of the involved charges. Has to be set because + it acts only on the portion of the Drude core charge that is + associated to the dipole of the atom. For charged, polarizable + atoms that charge is not equal to the particle charge property. + """ + + _so_name = "Interactions::InteractionThole" + + def is_active(self): + return self.scaling_coeff != 0. + + def default_params(self): + return {} + + def type_name(self): + return "Thole" + + def valid_keys(self): + return {"scaling_coeff", "q1q2"} + + def required_keys(self): + return {"scaling_coeff", "q1q2"} + @script_interface_register class NonBondedInteractionHandle(ScriptInterfaceHelper): @@ -1320,8 +1366,6 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): # Here, add one line for each nonbonded ia IF SMOOTH_STEP: self.smooth_step = SmoothStepInteraction(_type1, _type2) - IF THOLE: - self.thole = TholeInteraction(_type1, _type2) def _serialize(self): serialized = [] @@ -1771,64 +1815,6 @@ class ThermalizedBond(BondedInteraction): return {"r_cut": 0., "seed": None} -IF THOLE: - - cdef class TholeInteraction(NonBondedInteraction): - - def validate_params(self): - pass - - def _get_params_from_es_core(self): - cdef IA_parameters * ia_params - ia_params = get_ia_param_safe(self._part_types[0], - self._part_types[1]) - return { - "scaling_coeff": ia_params.thole.scaling_coeff, - "q1q2": ia_params.thole.q1q2 - } - - def is_active(self): - return (self._params["scaling_coeff"] != 0) - - def set_params(self, **kwargs): - """Set parameters for the Thole interaction. - - Parameters - ---------- - scaling_coeff : :obj:`float` - The factor used in the Thole damping function between - polarizable particles i and j. Usually calculated by - the polarizabilities :math:`\\alpha_i`, :math:`\\alpha_j` - and damping parameters :math:`a_i`, :math:`a_j` via - :math:`s_{ij} = \\frac{(a_i+a_j)/2}{((\\alpha_i\\cdot\\alpha_j)^{1/2})^{1/3}}` - q1q2: :obj:`float` - Charge factor of the involved charges. Has to be set because - it acts only on the portion of the Drude core charge that is - associated to the dipole of the atom. For charged, polarizable - atoms that charge is not equal to the particle charge property. - - """ - super().set_params(**kwargs) - - def _set_params_in_es_core(self): - if thole_set_params(self._part_types[0], self._part_types[1], - self._params["scaling_coeff"], - self._params["q1q2"]): - raise Exception("Could not set Thole parameters") - - def default_params(self): - return {} - - def type_name(self): - return "Thole" - - def valid_keys(self): - return {"scaling_coeff", "q1q2"} - - def required_keys(self): - return {"scaling_coeff", "q1q2"} - - IF BOND_CONSTRAINT == 1: @script_interface_register diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index 420a866ee80..b3b2f46db59 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -590,6 +590,28 @@ class InteractionDPD : public InteractionPotentialInterface<::DPD_Parameters> { }; #endif // DPD +#ifdef THOLE +class InteractionThole + : public InteractionPotentialInterface<::Thole_Parameters> { +protected: + CoreInteraction IA_parameters::*get_ptr_offset() const override { + return &::IA_parameters::thole; + } + +public: + InteractionThole() { + add_parameters({ + make_autoparameter(&CoreInteraction::scaling_coeff, "scaling_coeff"), + make_autoparameter(&CoreInteraction::q1q2, "q1q2"), + }); + } + void make_new_instance(VariantMap const ¶ms) override { + m_ia_si = make_shared_from_args( + params, "scaling_coeff", "q1q2"); + } +}; +#endif // THOLE + class NonBondedInteractionHandle : public AutoParameters { std::array m_types = {-1, -1}; @@ -639,6 +661,9 @@ class NonBondedInteractionHandle #ifdef DPD std::shared_ptr m_dpd; #endif +#ifdef THOLE + std::shared_ptr m_thole; +#endif template auto make_autoparameter(std::shared_ptr &member, const char *key) const { @@ -701,6 +726,9 @@ class NonBondedInteractionHandle #endif #ifdef DPD make_autoparameter(m_dpd, "dpd"), +#endif +#ifdef THOLE + make_autoparameter(m_thole, "thole"), #endif }); } @@ -801,6 +829,10 @@ class NonBondedInteractionHandle #ifdef DPD set_member(m_dpd, "dpd", "Interactions::InteractionDPD", params); +#endif +#ifdef THOLE + set_member(m_thole, "thole", + "Interactions::InteractionThole", params); #endif } diff --git a/src/script_interface/interactions/initialize.cpp b/src/script_interface/interactions/initialize.cpp index 02a63dab2f2..b1a2b09a1a8 100644 --- a/src/script_interface/interactions/initialize.cpp +++ b/src/script_interface/interactions/initialize.cpp @@ -103,6 +103,9 @@ void initialize(Utils::Factory *om) { #ifdef DPD om->register_new("Interactions::InteractionDPD"); #endif +#ifdef THOLE + om->register_new("Interactions::InteractionThole"); +#endif } } // namespace Interactions } // namespace ScriptInterface From d8cd0c33c195cafd8fb87a3f922ad0e04990c1bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 8 Sep 2022 13:07:59 +0200 Subject: [PATCH 66/85] Rewrite SmoothStep interaction interface --- .../nonbonded_interaction_data.cpp | 2 +- .../nonbonded_interaction_data.hpp | 4 + .../nonbonded_interactions/smooth_step.cpp | 30 ++---- .../nonbonded_interactions/smooth_step.hpp | 3 - src/python/espressomd/interactions.pxd | 17 +--- src/python/espressomd/interactions.pyx | 93 ++++++------------- .../interactions/NonBondedInteraction.hpp | 38 ++++++++ .../interactions/initialize.cpp | 4 + .../interactions_non-bonded_interface.py | 8 ++ 9 files changed, 94 insertions(+), 105 deletions(-) diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index 6e000f81e99..673407afcc4 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -153,7 +153,7 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { #endif #ifdef SMOOTH_STEP - max_cut_current = std::max(max_cut_current, data.smooth_step.cut); + max_cut_current = std::max(max_cut_current, data.smooth_step.max_cutoff()); #endif #ifdef HERTZIAN diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index f4a9feac3d0..10eb3e1c97e 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -104,6 +104,10 @@ struct SmoothStep_Parameters { double d = 0.0; int n = 0; double k0 = 0.0; + SmoothStep_Parameters() = default; + SmoothStep_Parameters(double eps, double sig, double cutoff, double d, int n, + double k0); + double max_cutoff() const { return cut; } }; /** Hertzian potential */ diff --git a/src/core/nonbonded_interactions/smooth_step.cpp b/src/core/nonbonded_interactions/smooth_step.cpp index 34996d91776..fd9d8f16e0d 100644 --- a/src/core/nonbonded_interactions/smooth_step.cpp +++ b/src/core/nonbonded_interactions/smooth_step.cpp @@ -25,29 +25,17 @@ #include "smooth_step.hpp" #ifdef SMOOTH_STEP -#include "interactions.hpp" #include "nonbonded_interaction_data.hpp" -#include +#include -int smooth_step_set_params(int part_type_a, int part_type_b, double d, int n, - double eps, double k0, double sig, double cut) { - IA_parameters *data = get_ia_param_safe(part_type_a, part_type_b); - - if (!data) - return ES_ERROR; - - data->smooth_step.eps = eps; - data->smooth_step.sig = sig; - data->smooth_step.cut = cut; - data->smooth_step.d = d; - data->smooth_step.n = n; - data->smooth_step.k0 = k0; - - /* broadcast interaction parameters */ - mpi_bcast_ia_params(part_type_a, part_type_b); - - return ES_OK; +SmoothStep_Parameters::SmoothStep_Parameters(double eps, double sig, + double cutoff, double d, int n, + double k0) + : eps{eps}, sig{sig}, cut{cutoff}, d{d}, n{n}, k0{k0} { + if (eps < 0.) { + throw std::domain_error("SmoothStep parameter 'eps' has to be >= 0"); + } } -#endif +#endif // SMOOTH_STEP diff --git a/src/core/nonbonded_interactions/smooth_step.hpp b/src/core/nonbonded_interactions/smooth_step.hpp index 50cf2152796..df3c858e20a 100644 --- a/src/core/nonbonded_interactions/smooth_step.hpp +++ b/src/core/nonbonded_interactions/smooth_step.hpp @@ -38,9 +38,6 @@ #include -int smooth_step_set_params(int part_type_a, int part_type_b, double d, int n, - double eps, double k0, double sig, double cut); - /** Calculate smooth step force factor */ inline double SmSt_pair_force_factor(IA_parameters const &ia_params, double dist) { diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index 3a30ad945af..2407460c749 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -31,30 +31,15 @@ cdef extern from "config.hpp": pass cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": - cdef struct SmoothStep_Parameters: - double eps - double sig - double cut - double d - int n - double k0 cdef struct IA_parameters: - - SmoothStep_Parameters smooth_step + pass cdef IA_parameters * get_ia_param_safe(int i, int j) cdef string ia_params_get_state() cdef void ia_params_set_state(string) cdef void reset_ia_params() -IF SMOOTH_STEP: - cdef extern from "nonbonded_interactions/smooth_step.hpp": - int smooth_step_set_params(int part_type_a, int part_type_b, - double d, int n, double eps, - double k0, double sig, - double cut) - cdef extern from "script_interface/interactions/bonded.hpp": int bonded_ia_params_zero_based_type(int bond_id) except + diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index fa2859b2455..c52124fc26e 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -845,51 +845,42 @@ IF DPD: IF SMOOTH_STEP == 1: - cdef class SmoothStepInteraction(NonBondedInteraction): + @script_interface_register + class SmoothStepInteraction(NewNonBondedInteraction): + """Smooth step interaction. - def validate_params(self): - """Check that parameters are valid. + Methods + ------- + set_params() + Set or update parameters for the interaction. + Parameters marked as required become optional once the + interaction has been activated for the first time; + subsequent calls to this method update the existing values. - """ - if self._params["eps"] < 0: - raise ValueError("Smooth-step eps has to be >=0") - if self._params["offset"] < 0: - raise ValueError("Smooth-step offset has to be >=0") - if self._params["cutoff"] < 0: - raise ValueError("Smooth-step cutoff has to be >=0") - if self._params["cap"] < 0: - raise ValueError("Smooth-step cap has to be >=0") - - def _get_params_from_es_core(self): - cdef IA_parameters * ia_params - ia_params = get_ia_param_safe( - self._part_types[0], - self._part_types[1]) - return { - "d": ia_params.smooth_step.d, - "n": ia_params.smooth_step.n, - "eps": ia_params.smooth_step.eps, - "k0": ia_params.smooth_step.k0, - "sig": ia_params.smooth_step.sig, - "cutoff": ia_params.smooth_step.cut - } + Parameters + ---------- + d : :obj:`float` + Short range repulsion parameter. + n : :obj:`int`, optional + Exponent of short range repulsion. + eps : :obj:`float` + Magnitude of the second (soft) repulsion. + k0 : :obj:`float`, optional + Exponential factor in second (soft) repulsion. + sig : :obj:`float`, optional + Length scale of second (soft) repulsion. + cutoff : :obj:`float` + Cutoff distance of the interaction. + + """ + + _so_name = "Interactions::InteractionSmoothStep" def is_active(self): """Check if interaction is active. """ - return ((self._params["eps"] > 0) and (self._params["sig"] > 0)) - - def _set_params_in_es_core(self): - if smooth_step_set_params(self._part_types[0], - self._part_types[1], - self._params["d"], - self._params["n"], - self._params["eps"], - self._params["k0"], - self._params["sig"], - self._params["cutoff"]): - raise Exception("Could not set smooth-step parameters") + return self.eps > 0. and self.sig > 0. def default_params(self): """Python dictionary of default parameters. @@ -903,28 +894,6 @@ IF SMOOTH_STEP == 1: """ return "SmoothStep" - def set_params(self, **kwargs): - """ - Set parameters for the smooth-step interaction. - - Parameters - ---------- - d : :obj:`float` - Short range repulsion parameter. - n : :obj:`int`, optional - Exponent of short range repulsion. - eps : :obj:`float` - Magnitude of the second (soft) repulsion. - k0 : :obj:`float`, optional - Exponential factor in second (soft) repulsion. - sig : :obj:`float`, optional - Length scale of second (soft) repulsion. - cutoff : :obj:`float` - Cutoff distance of the interaction. - - """ - super().set_params(**kwargs) - def valid_keys(self): """All parameters that can be set. @@ -1363,10 +1332,6 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): raise TypeError("The particle types have to be of type integer.") super().__init__(_types=[_type1, _type2], **kwargs) - # Here, add one line for each nonbonded ia - IF SMOOTH_STEP: - self.smooth_step = SmoothStepInteraction(_type1, _type2) - def _serialize(self): serialized = [] for name, obj in self.get_params().items(): diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index b3b2f46db59..076e95ef48b 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -612,6 +612,33 @@ class InteractionThole }; #endif // THOLE +#ifdef SMOOTH_STEP +class InteractionSmoothStep + : public InteractionPotentialInterface<::SmoothStep_Parameters> { +protected: + CoreInteraction IA_parameters::*get_ptr_offset() const override { + return &::IA_parameters::smooth_step; + } + +public: + InteractionSmoothStep() { + add_parameters({ + make_autoparameter(&CoreInteraction::eps, "eps"), + make_autoparameter(&CoreInteraction::sig, "sig"), + make_autoparameter(&CoreInteraction::cut, "cutoff"), + make_autoparameter(&CoreInteraction::d, "d"), + make_autoparameter(&CoreInteraction::n, "n"), + make_autoparameter(&CoreInteraction::k0, "k0"), + }); + } + void make_new_instance(VariantMap const ¶ms) override { + m_ia_si = make_shared_from_args( + params, "eps", "sig", "cutoff", "d", "n", "k0"); + } +}; +#endif // SMOOTH_STEP + class NonBondedInteractionHandle : public AutoParameters { std::array m_types = {-1, -1}; @@ -664,6 +691,9 @@ class NonBondedInteractionHandle #ifdef THOLE std::shared_ptr m_thole; #endif +#ifdef SMOOTH_STEP + std::shared_ptr m_smooth_step; +#endif template auto make_autoparameter(std::shared_ptr &member, const char *key) const { @@ -729,6 +759,9 @@ class NonBondedInteractionHandle #endif #ifdef THOLE make_autoparameter(m_thole, "thole"), +#endif +#ifdef SMOOTH_STEP + make_autoparameter(m_smooth_step, "smooth_step"), #endif }); } @@ -833,6 +866,11 @@ class NonBondedInteractionHandle #ifdef THOLE set_member(m_thole, "thole", "Interactions::InteractionThole", params); +#endif +#ifdef SMOOTH_STEP + set_member(m_smooth_step, "smooth_step", + "Interactions::InteractionSmoothStep", + params); #endif } diff --git a/src/script_interface/interactions/initialize.cpp b/src/script_interface/interactions/initialize.cpp index b1a2b09a1a8..57129779939 100644 --- a/src/script_interface/interactions/initialize.cpp +++ b/src/script_interface/interactions/initialize.cpp @@ -106,6 +106,10 @@ void initialize(Utils::Factory *om) { #ifdef THOLE om->register_new("Interactions::InteractionThole"); #endif +#ifdef SMOOTH_STEP + om->register_new( + "Interactions::InteractionSmoothStep"); +#endif } } // namespace Interactions } // namespace ScriptInterface diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index 1483af4b18a..e04c0b509f4 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -337,6 +337,14 @@ def test_hat_exceptions(self): ("F_max",) ) + @utx.skipIfMissingFeatures("SMOOTH_STEP") + def test_smooth_step_exceptions(self): + self.check_potential_exceptions( + espressomd.interactions.SmoothStepInteraction, + {"eps": 4., "sig": 3., "cutoff": 1., "d": 2., "n": 11, "k0": 2.}, + ("eps",) + ) + if __name__ == "__main__": ut.main() From 05ace34d1bd52d8abb9f2380f54e9b577d528d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 9 Sep 2022 11:24:01 +0200 Subject: [PATCH 67/85] Remove old non-bonded interaction interface --- src/core/TabulatedPotential.hpp | 11 - src/core/interactions.cpp | 16 +- src/core/interactions.hpp | 13 - .../nonbonded_interaction_data.cpp | 69 +---- .../nonbonded_interaction_data.hpp | 23 +- src/core/serialization/IA_parameters.hpp | 56 ----- .../EspressoSystemStandAlone_test.cpp | 1 - src/core/unit_tests/Verlet_list_test.cpp | 1 - src/python/espressomd/interactions.pxd | 11 - src/python/espressomd/interactions.pyx | 238 ++---------------- .../interactions/NonBondedInteraction.hpp | 1 - .../interactions/NonBondedInteractions.hpp | 3 - 12 files changed, 30 insertions(+), 413 deletions(-) delete mode 100644 src/core/serialization/IA_parameters.hpp diff --git a/src/core/TabulatedPotential.hpp b/src/core/TabulatedPotential.hpp index 819dee91060..e63a3bef9d4 100644 --- a/src/core/TabulatedPotential.hpp +++ b/src/core/TabulatedPotential.hpp @@ -71,17 +71,6 @@ struct TabulatedPotential { } double cutoff() const { return maxval; } - -private: - friend boost::serialization::access; - template - void serialize(Archive &ar, long int /* version */) { - ar &minval; - ar &maxval; - ar &invstepsize; - ar &force_tab; - ar &energy_tab; - } }; #endif diff --git a/src/core/interactions.cpp b/src/core/interactions.cpp index b050bedab4f..6ba4a77ee67 100644 --- a/src/core/interactions.cpp +++ b/src/core/interactions.cpp @@ -18,16 +18,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "communication.hpp" #include "bonded_interactions/bonded_interaction_data.hpp" #include "collision.hpp" +#include "communication.hpp" #include "electrostatics/coulomb.hpp" #include "errorhandling.hpp" #include "event.hpp" #include "magnetostatics/dipoles.hpp" - -#include "serialization/IA_parameters.hpp" +#include "nonbonded_interactions/nonbonded_interaction_data.hpp" #include @@ -77,14 +76,3 @@ bool long_range_interactions_sanity_checks() { } return false; } - -void mpi_bcast_ia_params_local(int i, int j) { - boost::mpi::broadcast(comm_cart, get_ia_param(i, j), 0); - on_short_range_ia_change(); -} - -REGISTER_CALLBACK(mpi_bcast_ia_params_local) - -void mpi_bcast_ia_params(int i, int j) { - mpi_call_all(mpi_bcast_ia_params_local, i, j); -} diff --git a/src/core/interactions.hpp b/src/core/interactions.hpp index 4399e5017bf..332891dc14a 100644 --- a/src/core/interactions.hpp +++ b/src/core/interactions.hpp @@ -32,17 +32,4 @@ double maximal_cutoff(bool single_node); */ bool long_range_interactions_sanity_checks(); -/** Send new IA params. - * Also calls \ref on_short_range_ia_change. - * - * Used for both bonded and non-bonded interaction parameters. Therefore - * @p i and @p j are used depending on their value: - * - * \param i particle type for non-bonded interaction parameters / - * bonded interaction type number. - * \param j if not negative: particle type for non-bonded interaction - * parameters / if negative: flag for bonded interaction - */ -void mpi_bcast_ia_params(int i, int j); - #endif diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index 673407afcc4..6673ee62feb 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -26,21 +26,11 @@ #include "communication.hpp" #include "electrostatics/coulomb.hpp" #include "event.hpp" -#include "serialization/IA_parameters.hpp" - -#include -#include -#include -#include -#include -#include -#include #include #include -#include -#include +#include #include #include @@ -48,7 +38,6 @@ * variables *****************************************/ int max_seen_particle_type = 0; -std::vector old_nonbonded_ia_params; std::vector> nonbonded_ia_params; /** Minimal global interaction cutoff. Particles with a distance @@ -67,7 +56,6 @@ void mpi_realloc_ia_params_local(int new_size) { return; auto const n_pairs = new_size * (new_size + 1) / 2; - auto new_params_ = std::vector(n_pairs); auto new_params = std::vector>(n_pairs); /* if there is an old field, move entries */ @@ -75,7 +63,6 @@ void mpi_realloc_ia_params_local(int new_size) { for (int j = i; j < old_size; j++) { auto const new_key = Utils::upper_triangular(i, j, new_size); auto const old_key = Utils::upper_triangular(i, j, old_size); - new_params_.at(new_key) = std::move(old_nonbonded_ia_params[old_key]); new_params[new_key] = std::move(nonbonded_ia_params[old_key]); } } @@ -86,53 +73,11 @@ void mpi_realloc_ia_params_local(int new_size) { } ::max_seen_particle_type = new_size; - std::swap(::old_nonbonded_ia_params, new_params_); std::swap(::nonbonded_ia_params, new_params); } REGISTER_CALLBACK(mpi_realloc_ia_params_local) -/** Increase the size of the @ref nonbonded_ia_params vector. */ -static 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, old_nonbonded_ia_params, 0); -} - -REGISTER_CALLBACK(mpi_bcast_all_ia_params_local) - -/** Broadcast @ref old_nonbonded_ia_params to all nodes. */ -static void mpi_bcast_all_ia_params() { - mpi_call_all(mpi_bcast_all_ia_params_local); -} - -IA_parameters *get_ia_param_safe(int i, int j) { - make_particle_type_exist(std::max(i, j)); - return &get_ia_param(i, j); -} - -std::string ia_params_get_state() { - std::stringstream out; - boost::archive::binary_oarchive oa(out); - oa << old_nonbonded_ia_params; - oa << max_seen_particle_type; - return out.str(); -} - -void ia_params_set_state(std::string const &state) { - namespace iostreams = boost::iostreams; - iostreams::array_source src(state.data(), state.size()); - iostreams::stream ss(src); - boost::archive::binary_iarchive ia(ss); - old_nonbonded_ia_params.clear(); - ia >> old_nonbonded_ia_params; - ia >> max_seen_particle_type; - mpi_realloc_ia_params(max_seen_particle_type); - mpi_bcast_all_ia_params(); -} - static double recalc_maximal_cutoff(const IA_parameters &data) { auto max_cut_current = INACTIVE_CUTOFF; @@ -212,11 +157,6 @@ static double recalc_maximal_cutoff(const IA_parameters &data) { double maximal_cutoff_nonbonded() { auto max_cut_nonbonded = INACTIVE_CUTOFF; - for (auto &data : old_nonbonded_ia_params) { - data.max_cut = recalc_maximal_cutoff(data); - max_cut_nonbonded = std::max(max_cut_nonbonded, data.max_cut); - } - for (auto &data : nonbonded_ia_params) { data->max_cut = recalc_maximal_cutoff(*data); max_cut_nonbonded = std::max(max_cut_nonbonded, data->max_cut); @@ -225,18 +165,13 @@ double maximal_cutoff_nonbonded() { return max_cut_nonbonded; } -void reset_ia_params() { - boost::fill(old_nonbonded_ia_params, IA_parameters{}); - mpi_bcast_all_ia_params(); -} - bool is_new_particle_type(int type) { return (type + 1) > max_seen_particle_type; } void make_particle_type_exist(int type) { if (is_new_particle_type(type)) - mpi_realloc_ia_params(type + 1); + mpi_call_all(mpi_realloc_ia_params_local, type + 1); } void make_particle_type_exist_local(int type) { diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index 10eb3e1c97e..b778530f051 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -33,7 +33,6 @@ #include #include #include -#include #include /** Cutoff for deactivated interactions. Must be negative, so that even @@ -348,7 +347,6 @@ struct IA_parameters { #endif }; -extern std::vector old_nonbonded_ia_params; extern std::vector> nonbonded_ia_params; /** Maximal particle type seen so far. */ @@ -378,23 +376,9 @@ inline int get_ia_param_key(int i, int j) { * @return Reference to interaction parameters for the type pair. */ inline IA_parameters &get_ia_param(int i, int j) { - return ::old_nonbonded_ia_params[get_ia_param_key(i, j)]; + return *::nonbonded_ia_params[get_ia_param_key(i, j)]; } -/** Get interaction parameters between particle types i and j. - * Slower than @ref get_ia_param, but can also be used on not - * yet present particle types - */ -IA_parameters *get_ia_param_safe(int i, int j); - -/** @brief Get the state of all non-bonded interactions. - */ -std::string ia_params_get_state(); - -/** @brief Set the state of all non-bonded interactions. - */ -void ia_params_set_state(std::string const &); - void mpi_realloc_ia_params_local(int new_size); bool is_new_particle_type(int type); @@ -406,11 +390,6 @@ void make_particle_type_exist(int type); void make_particle_type_exist_local(int type); -/** - * @brief Reset all interaction parameters to their defaults. - */ -void reset_ia_params(); - /** Check if a non-bonded interaction is defined */ inline bool checkIfInteraction(IA_parameters const &data) { return data.max_cut != INACTIVE_CUTOFF; diff --git a/src/core/serialization/IA_parameters.hpp b/src/core/serialization/IA_parameters.hpp deleted file mode 100644 index e56e8c1c03a..00000000000 --- a/src/core/serialization/IA_parameters.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2010-2022 The ESPResSo project - * - * This file is part of ESPResSo. - * - * ESPResSo is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ESPResSo is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#ifndef SERIALIZATION_IA_PARAMETERS_HPP -#define SERIALIZATION_IA_PARAMETERS_HPP - -#include "nonbonded_interactions/nonbonded_interaction_data.hpp" - -namespace boost { -namespace serialization { -template -void load(Archive &ar, IA_parameters &p, - const unsigned int /* file_version */) { - ar >> make_array(reinterpret_cast(&p), sizeof(IA_parameters)); - -#ifdef TABULATED - TabulatedPotential tab; - ar >> tab; - - new (&(p.tab)) TabulatedPotential(std::move(tab)); -#endif -} - -template -void save(Archive &ar, IA_parameters const &p, - const unsigned int /* file_version */) { - ar << make_array(reinterpret_cast(&p), sizeof(IA_parameters)); - -#ifdef TABULATED - ar << p.tab; -#endif -} - -template -void serialize(Archive &ar, IA_parameters &p, const unsigned int file_version) { - split_free(ar, p, file_version); -} -} // namespace serialization -} // namespace boost - -#endif diff --git a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp index 224f3703cd5..f632f3e0b05 100644 --- a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp +++ b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp @@ -132,7 +132,6 @@ void mpi_set_lj_local(int key, double eps, double sig, double cut, double offset, double min, double shift) { LJ_Parameters lj{eps, sig, cut, offset, min, shift}; ::nonbonded_ia_params[key]->lj = lj; - ::old_nonbonded_ia_params[key].lj = lj; on_non_bonded_ia_change(); } diff --git a/src/core/unit_tests/Verlet_list_test.cpp b/src/core/unit_tests/Verlet_list_test.cpp index d006d5b9439..170aad1e061 100644 --- a/src/core/unit_tests/Verlet_list_test.cpp +++ b/src/core/unit_tests/Verlet_list_test.cpp @@ -151,7 +151,6 @@ void mpi_set_lj_local(int key, double eps, double sig, double cut, double offset, double min, double shift) { LJ_Parameters lj{eps, sig, cut, offset, min, shift}; ::nonbonded_ia_params[key]->lj = lj; - ::old_nonbonded_ia_params[key].lj = lj; on_non_bonded_ia_change(); } diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index 2407460c749..6d5ca5f26fc 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -19,7 +19,6 @@ # Handling of interactions -from libcpp.string cimport string from libc cimport stdint from .thermostat cimport thermalized_bond @@ -30,16 +29,6 @@ include "myconfig.pxi" cdef extern from "config.hpp": pass -cdef extern from "nonbonded_interactions/nonbonded_interaction_data.hpp": - - cdef struct IA_parameters: - pass - - cdef IA_parameters * get_ia_param_safe(int i, int j) - cdef string ia_params_get_state() - cdef void ia_params_set_state(string) - cdef void reset_ia_params() - cdef extern from "script_interface/interactions/bonded.hpp": int bonded_ia_params_zero_based_type(int bond_id) except + diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index c52124fc26e..dda684e5983 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -16,197 +16,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -from libcpp.string cimport string cimport cpython.object -import collections include "myconfig.pxi" from . import utils from .script_interface import ScriptObjectMap, ScriptInterfaceHelper, script_interface_register -cdef class NonBondedInteraction: - """ - Represents an instance of a non-bonded interaction, such as Lennard-Jones. - Either called with two particle type id, in which case, the interaction - will represent the bonded interaction as it is defined in ESPResSo core, - or called with keyword arguments describing a new interaction. - - """ - - cdef public object _part_types - cdef public object _params - - # init dict to access all user defined nonbonded-inters via - # user_interactions[type1][type2][parameter] - cdef public object user_interactions - - def __init__(self, *args, **kwargs): - if self.user_interactions is None: - self.user_interactions = {} - # Interaction id as argument - if len(args) == 2 and utils.is_valid_type( - args[0], int) and utils.is_valid_type(args[1], int): - self._part_types = args - - # Load the parameters currently set in the ESPResSo core - self._params = self._get_params_from_es_core() - - # 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] - self._params.update(kwargs) - self.validate_params() - else: - raise Exception( - "The constructor has to be called either with two particle type ids (as integer), or with a set of keyword arguments describing a new interaction") - - def is_valid(self): - """Check, if the data stored in the instance still matches what is in ESPResSo. - - """ - temp_params = self._get_params_from_es_core() - return self._params == temp_params - - def get_params(self): - """Get interaction parameters. - - """ - # If this instance refers to an actual interaction defined in - # the es core, load current parameters from there - if self._part_types[0] >= 0 and self._part_types[1] >= 0: - self._params = self._get_params_from_es_core() - return self._params - - def __str__(self): - return f'{self.__class__.__name__}({self.get_params()})' - - def __getstate__(self): - odict = collections.OrderedDict() - odict['user_interactions'] = self.user_interactions - odict['_part_types'] = self._part_types - odict['params'] = self.get_params() - return odict - - def __setstate__(self, state): - self.user_interactions = state['user_interactions'] - self._part_types = state['_part_types'] - self._params = state['params'] - - def set_params(self, **p): - """Update the given parameters. - - """ - # Check, if any key was passed, which is not known - 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(): - 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 - is_valid_ia = 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 is_valid_ia: - self._set_params_in_es_core() - - # update interaction dict when user sets interaction - if self._part_types[0] not in self.user_interactions: - self.user_interactions[self._part_types[0]] = {} - self.user_interactions[self._part_types[0]][self._part_types[1]] = {} - new_params = self.get_params() - for p_key in new_params: - self.user_interactions[self._part_types[0]][ - self._part_types[1]][p_key] = new_params[p_key] - 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: - utils.handle_errors(f'setting {self.type_name()} raised an error') - - def validate_params(self): - """Check that parameters are valid. - - """ - pass - - def __getattribute__(self, name): - """Every time _set_params_in_es_core is called, the parameter dict is also updated. - - """ - attr = object.__getattribute__(self, name) - if hasattr( - attr, '__call__') and attr.__name__ == "_set_params_in_es_core": - def sync_params(*args, **kwargs): - result = attr(*args, **kwargs) - self._params.update(self._get_params_from_es_core()) - return result - return sync_params - else: - return attr - - def _get_params_from_es_core(self): - raise Exception( - "Subclasses of NonBondedInteraction must define the _get_params_from_es_core() method.") - - def _set_params_in_es_core(self): - raise Exception( - "Subclasses of NonBondedInteraction must define the _set_params_in_es_core() method.") - - def default_params(self): - """Virtual method. - - """ - raise Exception( - "Subclasses of NonBondedInteraction must define the default_params() method.") - - def is_active(self): - """Virtual method. - - """ - # If this instance refers to an actual interaction defined in - # the es core, load current parameters from there - if self._part_types[0] >= 0 and self._part_types[1] >= 0: - self._params = self._get_params_from_es_core() - raise Exception( - "Subclasses of NonBondedInteraction must define the is_active() method.") - - def type_name(self): - """Virtual method. - - """ - raise Exception( - "Subclasses of NonBondedInteraction must define the type_name() method.") - - def valid_keys(self): - """Virtual method. - - """ - raise Exception( - "Subclasses of NonBondedInteraction must define the valid_keys() method.") - - def required_keys(self): - """Virtual method. - - """ - raise Exception( - "Subclasses of NonBondedInteraction must define the required_keys() method.") - - -class NewNonBondedInteraction(ScriptInterfaceHelper): +class NonBondedInteraction(ScriptInterfaceHelper): """ Represents an instance of a non-bonded interaction, such as Lennard-Jones. @@ -287,7 +104,7 @@ class NewNonBondedInteraction(ScriptInterfaceHelper): IF LENNARD_JONES == 1: @script_interface_register - class LennardJonesInteraction(NewNonBondedInteraction): + class LennardJonesInteraction(NonBondedInteraction): """ Standard 6-12 Lennard-Jones potential. @@ -353,7 +170,7 @@ IF LENNARD_JONES == 1: IF WCA == 1: @script_interface_register - class WCAInteraction(NewNonBondedInteraction): + class WCAInteraction(NonBondedInteraction): """ Standard 6-12 Weeks-Chandler-Andersen potential. @@ -410,7 +227,7 @@ IF WCA == 1: IF LENNARD_JONES_GENERIC == 1: @script_interface_register - class GenericLennardJonesInteraction(NewNonBondedInteraction): + class GenericLennardJonesInteraction(NonBondedInteraction): """ Generalized Lennard-Jones potential. @@ -497,7 +314,7 @@ IF LENNARD_JONES_GENERIC == 1: IF LJCOS == 1: @script_interface_register - class LennardJonesCosInteraction(NewNonBondedInteraction): + class LennardJonesCosInteraction(NonBondedInteraction): """Lennard-Jones cosine interaction. Methods @@ -555,7 +372,7 @@ IF LJCOS == 1: IF LJCOS2 == 1: @script_interface_register - class LennardJonesCos2Interaction(NewNonBondedInteraction): + class LennardJonesCos2Interaction(NonBondedInteraction): """Second variant of the Lennard-Jones cosine interaction. Methods @@ -617,7 +434,7 @@ IF LJCOS2 == 1: IF HAT == 1: @script_interface_register - class HatInteraction(NewNonBondedInteraction): + class HatInteraction(NonBondedInteraction): """Hat interaction. Methods @@ -657,7 +474,7 @@ IF HAT == 1: IF GAY_BERNE: @script_interface_register - class GayBerneInteraction(NewNonBondedInteraction): + class GayBerneInteraction(NonBondedInteraction): """Gay--Berne interaction. Methods @@ -723,7 +540,7 @@ IF GAY_BERNE: IF TABULATED: @script_interface_register - class TabulatedNonBonded(NewNonBondedInteraction): + class TabulatedNonBonded(NonBondedInteraction): """Tabulated interaction. Methods @@ -786,7 +603,7 @@ IF TABULATED: IF DPD: @script_interface_register - class DPDInteraction(NewNonBondedInteraction): + class DPDInteraction(NonBondedInteraction): """DPD interaction. Methods @@ -846,7 +663,7 @@ IF DPD: IF SMOOTH_STEP == 1: @script_interface_register - class SmoothStepInteraction(NewNonBondedInteraction): + class SmoothStepInteraction(NonBondedInteraction): """Smooth step interaction. Methods @@ -909,7 +726,7 @@ IF SMOOTH_STEP == 1: IF BMHTF_NACL == 1: @script_interface_register - class BMHTFInteraction(NewNonBondedInteraction): + class BMHTFInteraction(NonBondedInteraction): """BMHTF interaction. Methods @@ -971,7 +788,7 @@ IF BMHTF_NACL == 1: IF MORSE == 1: @script_interface_register - class MorseInteraction(NewNonBondedInteraction): + class MorseInteraction(NonBondedInteraction): """Morse interaction. Methods @@ -1029,7 +846,7 @@ IF MORSE == 1: IF BUCKINGHAM == 1: @script_interface_register - class BuckinghamInteraction(NewNonBondedInteraction): + class BuckinghamInteraction(NonBondedInteraction): """Buckingham interaction. Methods @@ -1093,7 +910,7 @@ IF BUCKINGHAM == 1: IF SOFT_SPHERE == 1: @script_interface_register - class SoftSphereInteraction(NewNonBondedInteraction): + class SoftSphereInteraction(NonBondedInteraction): """Soft sphere interaction. Methods @@ -1151,7 +968,7 @@ IF SOFT_SPHERE == 1: IF HERTZIAN == 1: @script_interface_register - class HertzianInteraction(NewNonBondedInteraction): + class HertzianInteraction(NonBondedInteraction): """Hertzian interaction. Methods @@ -1205,7 +1022,7 @@ IF HERTZIAN == 1: IF GAUSSIAN == 1: @script_interface_register - class GaussianInteraction(NewNonBondedInteraction): + class GaussianInteraction(NonBondedInteraction): """Gaussian interaction. Methods @@ -1261,7 +1078,7 @@ IF GAUSSIAN == 1: IF THOLE: @script_interface_register - class TholeInteraction(NewNonBondedInteraction): + class TholeInteraction(NonBondedInteraction): """Thole interaction. Methods @@ -1345,10 +1162,15 @@ class NonBondedInteractions(ScriptInterfaceHelper): Access to non-bonded interaction parameters via ``[i,j]``, where ``i, j`` are particle types. Returns a :class:`NonBondedInteractionHandle` object. + Methods + ------- + reset() + Reset all interaction parameters to their default values. + """ _so_name = "Interactions::NonBondedInteractions" _so_creation_policy = "GLOBAL" - # _so_bind_methods = ("reset",) + _so_bind_methods = ("reset",) def keys(self): return [tuple(x) for x in self.call_method("keys")] @@ -1368,14 +1190,13 @@ class NonBondedInteractions(ScriptInterfaceHelper): self.call_method("insert", key=key, object=value) def __getstate__(self): - cdef string core_state = ia_params_get_state() n_types = self.call_method("get_n_types") state = [] for i in range(n_types): for j in range(i, n_types): handle = NonBondedInteractionHandle(_types=(i, j)) state.append(((i, j), handle._serialize())) - return {"state": state, "core_state": core_state} + return {"state": state} def __setstate__(self, params): for types, kwargs in params["state"]: @@ -1385,17 +1206,8 @@ class NonBondedInteractions(ScriptInterfaceHelper): obj = NonBondedInteractionHandle(_types=types, **objects) self.call_method("insert", key=types, object=obj) - def reset(self): - """ - Reset all interaction parameters to their default values. - """ - reset_ia_params() - self.call_method("reset") - @classmethod def _restore_object(cls, so_callback, so_callback_args, state): - cdef string core_state = state["core_state"] - ia_params_set_state(core_state) so = so_callback(*so_callback_args) so.__setstate__(state) return so diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index 076e95ef48b..fd76e16b4c3 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -118,7 +118,6 @@ class InteractionPotentialInterface auto const key = get_ia_param_key(m_types[0], m_types[1]); assert(key < ::nonbonded_ia_params.size()); ::nonbonded_ia_params[key].get()->*get_ptr_offset() = *m_ia_si; - ::old_nonbonded_ia_params[key].*get_ptr_offset() = *m_ia_si; } void copy_core_to_si() { diff --git a/src/script_interface/interactions/NonBondedInteractions.hpp b/src/script_interface/interactions/NonBondedInteractions.hpp index 0817210c4cf..9680d37419d 100644 --- a/src/script_interface/interactions/NonBondedInteractions.hpp +++ b/src/script_interface/interactions/NonBondedInteractions.hpp @@ -61,7 +61,6 @@ class NonBondedInteractions : public ObjectHandle { for (int j = i; j < size; j++) { auto const key = Utils::upper_triangular(i, j, size); ::nonbonded_ia_params[i] = std::make_shared<::IA_parameters>(); - ::old_nonbonded_ia_params[key] = ::IA_parameters{}; m_nonbonded_ia_params[key] = make_interaction(i, j); } } @@ -74,7 +73,6 @@ class NonBondedInteractions : public ObjectHandle { // when reloading from a checkpoint file, need to resize IA lists auto const new_size = ::max_seen_particle_type; auto const n_pairs = new_size * (new_size + 1) / 2; - ::old_nonbonded_ia_params.resize(n_pairs); ::nonbonded_ia_params.resize(n_pairs); for (auto &ia_params : ::nonbonded_ia_params) { if (ia_params == nullptr) { @@ -107,7 +105,6 @@ class NonBondedInteractions : public ObjectHandle { params.at("object")); ::nonbonded_ia_params[key] = obj_ptr->get_ia(); m_nonbonded_ia_params[key] = obj_ptr; - ::old_nonbonded_ia_params[key] = *(obj_ptr->get_ia()); on_non_bonded_ia_change(); return {}; } From f0ba1c2f4258c0119d5e413f3cecdfa72df0fcc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 9 Sep 2022 11:48:06 +0200 Subject: [PATCH 68/85] Refactor new non-bonded interface Interactions can now be reset. Skip inactive interactions when reloading from a checkpoint file to avoid triggering range checks. Re-introduce the original cutoff range checks. --- doc/sphinx/inter_non-bonded.rst | 34 +- .../nonbonded_interactions/bmhtf-nacl.cpp | 3 + .../nonbonded_interactions/buckingham.cpp | 3 + src/core/nonbonded_interactions/gaussian.cpp | 7 +- src/core/nonbonded_interactions/hat.cpp | 3 + src/core/nonbonded_interactions/hertzian.cpp | 3 + src/core/nonbonded_interactions/lj.cpp | 27 +- src/core/nonbonded_interactions/lj.hpp | 6 +- src/core/nonbonded_interactions/ljcos.cpp | 11 +- src/core/nonbonded_interactions/ljcos2.cpp | 19 +- src/core/nonbonded_interactions/ljgen.cpp | 3 + src/core/nonbonded_interactions/morse.cpp | 3 + .../nonbonded_interaction_data.hpp | 22 +- .../nonbonded_interactions/smooth_step.cpp | 6 + .../nonbonded_interactions/soft_sphere.cpp | 3 + src/core/nonbonded_interactions/wca.cpp | 11 +- src/python/espressomd/interactions.pyx | 424 ++---------------- .../interactions/NonBondedInteraction.hpp | 135 +++--- testsuite/python/collision_detection.py | 6 +- testsuite/python/constraint_shape_based.py | 3 +- testsuite/python/cutoffs.py | 5 +- testsuite/python/dawaanr-and-bh-gpu.py | 3 +- testsuite/python/dawaanr-and-dds-gpu.py | 3 +- testsuite/python/dds-and-bh-gpu.py | 3 +- testsuite/python/dipolar_direct_summation.py | 3 +- .../interactions_non-bonded_interface.py | 97 ++-- testsuite/python/p3m_fft.py | 2 +- testsuite/python/save_checkpoint.py | 8 +- testsuite/python/test_checkpoint.py | 6 +- testsuite/python/virtual_sites_relative.py | 3 +- 30 files changed, 305 insertions(+), 560 deletions(-) diff --git a/doc/sphinx/inter_non-bonded.rst b/doc/sphinx/inter_non-bonded.rst index 4a0de03ef31..877b07eb59d 100644 --- a/doc/sphinx/inter_non-bonded.rst +++ b/doc/sphinx/inter_non-bonded.rst @@ -18,23 +18,41 @@ explicitly set. A bonded interaction between a set of particles has to be specified explicitly by the command, while the command is used to define the interaction parameters. -.. _Isotropic non-bonded interactions: +Non-bonded interaction are configured via the +:class:`espressomd.interactions.NonBondedInteraction` class, +which is a member of :class:`espressomd.system.System`:: -Isotropic non-bonded interactions ---------------------------------- + system.non_bonded_inter[type1, type2] -Non-bonded interaction are configured via the :class:`espressomd.interactions.NonBondedInteraction` class, which is a member of :class:`espressomd.system.System`:: +This command defines an interaction between all particles of type ``type1`` +and ``type2``. All available interaction potentials and their parameters are +listed below. For example, the following adds a WCA potential between +particles of type 0:: - system.non_bonded_inter[type1, type2] + system.non_bonded_inter[0, 0].wca.set_params(epsilon=1., sigma=2.) + +Each type pair can have multiple potentials active at the same time. +To deactivate a specific potential for a given type pair, do:: + + system.non_bonded_inter[0, 0].wca.deactivate() + +To deactivate all potentials for a given type pair, do:: -This command defines an interaction between all particles of type ``type1`` and -``type2``. Possible interaction types and their parameters are -listed below. + system.non_bonded_inter[0, 0].reset() + +To deactivate all potentials between all type pairs, do:: + + system.non_bonded_inter.reset() For many non-bonded interactions, it is possible to artificially cap the forces, which often allows to equilibrate the system much faster. See the subsection :ref:`Capping the force during warmup` for more details. +.. _Isotropic non-bonded interactions: + +Isotropic non-bonded interactions +--------------------------------- + .. _Tabulated interaction: Tabulated interaction diff --git a/src/core/nonbonded_interactions/bmhtf-nacl.cpp b/src/core/nonbonded_interactions/bmhtf-nacl.cpp index e89fbf193df..df7975415d2 100644 --- a/src/core/nonbonded_interactions/bmhtf-nacl.cpp +++ b/src/core/nonbonded_interactions/bmhtf-nacl.cpp @@ -43,6 +43,9 @@ BMHTF_Parameters::BMHTF_Parameters(double a, double b, double c, double d, if (d < 0.) { throw std::domain_error("BMHTF parameter 'd' has to be >= 0"); } + if (cutoff < 0.) { + throw std::domain_error("BMHTF parameter 'cutoff' has to be >= 0"); + } computed_shift = C / Utils::int_pow<6>(cut) + D / Utils::int_pow<8>(cut) - A * std::exp(B * (sig - cut)); } diff --git a/src/core/nonbonded_interactions/buckingham.cpp b/src/core/nonbonded_interactions/buckingham.cpp index e8febf5ff32..7fd90cdad50 100644 --- a/src/core/nonbonded_interactions/buckingham.cpp +++ b/src/core/nonbonded_interactions/buckingham.cpp @@ -45,6 +45,9 @@ Buckingham_Parameters::Buckingham_Parameters(double a, double b, double c, if (d < 0.) { throw std::domain_error("Buckingham parameter 'd' has to be >= 0"); } + if (cutoff < 0.) { + throw std::domain_error("Buckingham parameter 'cutoff' has to be >= 0"); + } /* Replace the Buckingham potential for interatomic distance less than or equal to discontinuity by a straight line (F1+F2*r) */ diff --git a/src/core/nonbonded_interactions/gaussian.cpp b/src/core/nonbonded_interactions/gaussian.cpp index 97f0e8c20aa..76821cf4c11 100644 --- a/src/core/nonbonded_interactions/gaussian.cpp +++ b/src/core/nonbonded_interactions/gaussian.cpp @@ -29,13 +29,16 @@ #include -Gaussian_Parameters::Gaussian_Parameters(double eps, double sig, double cut) - : eps{eps}, sig{sig}, cut{cut} { +Gaussian_Parameters::Gaussian_Parameters(double eps, double sig, double cutoff) + : eps{eps}, sig{sig}, cut{cutoff} { if (eps < 0.) { throw std::domain_error("Gaussian parameter 'eps' has to be >= 0"); } if (sig < 0.) { throw std::domain_error("Gaussian parameter 'sig' has to be >= 0"); } + if (cutoff < 0.) { + throw std::domain_error("Gaussian parameter 'cutoff' has to be >= 0"); + } } #endif // GAUSSIAN diff --git a/src/core/nonbonded_interactions/hat.cpp b/src/core/nonbonded_interactions/hat.cpp index ede5d20066e..00dd4661ecc 100644 --- a/src/core/nonbonded_interactions/hat.cpp +++ b/src/core/nonbonded_interactions/hat.cpp @@ -34,6 +34,9 @@ Hat_Parameters::Hat_Parameters(double F_max, double cutoff) if (F_max < 0.) { throw std::domain_error("Hat parameter 'F_max' has to be >= 0"); } + if (cutoff < 0.) { + throw std::domain_error("Hat parameter 'cutoff' has to be >= 0"); + } } #endif // HAT diff --git a/src/core/nonbonded_interactions/hertzian.cpp b/src/core/nonbonded_interactions/hertzian.cpp index 4b3439f4982..9ee911d0757 100644 --- a/src/core/nonbonded_interactions/hertzian.cpp +++ b/src/core/nonbonded_interactions/hertzian.cpp @@ -34,6 +34,9 @@ Hertzian_Parameters::Hertzian_Parameters(double eps, double sig) if (eps < 0.) { throw std::domain_error("Hertzian parameter 'eps' has to be >= 0"); } + if (sig < 0.) { + throw std::domain_error("Hertzian parameter 'sig' has to be >= 0"); + } } #endif // HERTZIAN diff --git a/src/core/nonbonded_interactions/lj.cpp b/src/core/nonbonded_interactions/lj.cpp index f915e4acfba..afb8a63b341 100644 --- a/src/core/nonbonded_interactions/lj.cpp +++ b/src/core/nonbonded_interactions/lj.cpp @@ -27,31 +27,22 @@ #ifdef LENNARD_JONES #include "nonbonded_interaction_data.hpp" -#include - #include #include -LJ_Parameters::LJ_Parameters(double eps, double sig, double cut, double offset, - double min) - : LJ_Parameters(eps, sig, cut, offset, min, 0.) { - if (cut != 0.) { - auto const sig_cut = sig / cut; - shift = Utils::int_pow<6>(sig_cut) - Utils::int_pow<12>(sig_cut); - } -} - -LJ_Parameters::LJ_Parameters(double eps, double sig, double cut, double offset, - double min, double shift) - : eps{eps}, sig{sig}, cut{cut}, shift{shift}, offset{offset}, min{std::max( - min, - 0.)} { - if (eps < 0.) { +LJ_Parameters::LJ_Parameters(double epsilon, double sigma, double cutoff, + double offset, double min, double shift) + : eps{epsilon}, sig{sigma}, cut{cutoff}, shift{shift}, offset{offset}, + min{std::max(min, 0.)} { + if (epsilon < 0.) { throw std::domain_error("LJ parameter 'epsilon' has to be >= 0"); } - if (sig < 0.) { + if (sigma < 0.) { throw std::domain_error("LJ parameter 'sigma' has to be >= 0"); } + if (cutoff < 0.) { + throw std::domain_error("LJ parameter 'cutoff' has to be >= 0"); + } } #endif /* ifdef LENNARD_JONES */ diff --git a/src/core/nonbonded_interactions/lj.hpp b/src/core/nonbonded_interactions/lj.hpp index b6651943e72..d95aaaea3ae 100644 --- a/src/core/nonbonded_interactions/lj.hpp +++ b/src/core/nonbonded_interactions/lj.hpp @@ -40,8 +40,7 @@ /** Calculate Lennard-Jones force factor */ inline double lj_pair_force_factor(IA_parameters const &ia_params, double dist) { - if ((dist < ia_params.lj.cut + ia_params.lj.offset) && - (dist > ia_params.lj.min + ia_params.lj.offset)) { + if (dist < ia_params.lj.max_cutoff() and dist > ia_params.lj.min_cutoff()) { auto const r_off = dist - ia_params.lj.offset; auto const frac6 = Utils::int_pow<6>(ia_params.lj.sig / r_off); return 48.0 * ia_params.lj.eps * frac6 * (frac6 - 0.5) / (r_off * dist); @@ -51,8 +50,7 @@ inline double lj_pair_force_factor(IA_parameters const &ia_params, /** Calculate Lennard-Jones energy */ inline double lj_pair_energy(IA_parameters const &ia_params, double dist) { - if ((dist < ia_params.lj.cut + ia_params.lj.offset) && - (dist > ia_params.lj.min + ia_params.lj.offset)) { + if (dist < ia_params.lj.max_cutoff() and dist > ia_params.lj.min_cutoff()) { auto const r_off = dist - ia_params.lj.offset; auto const frac6 = Utils::int_pow<6>(ia_params.lj.sig / r_off); return 4.0 * ia_params.lj.eps * diff --git a/src/core/nonbonded_interactions/ljcos.cpp b/src/core/nonbonded_interactions/ljcos.cpp index ba33702b446..1fa4d75e266 100644 --- a/src/core/nonbonded_interactions/ljcos.cpp +++ b/src/core/nonbonded_interactions/ljcos.cpp @@ -32,15 +32,18 @@ #include -LJcos_Parameters::LJcos_Parameters(double eps, double sig, double cut, +LJcos_Parameters::LJcos_Parameters(double epsilon, double sigma, double cutoff, double offset) - : eps{eps}, sig{sig}, cut{cut}, offset{offset} { - if (eps < 0.) { + : eps{epsilon}, sig{sigma}, cut{cutoff}, offset{offset} { + if (epsilon < 0.) { throw std::domain_error("LJcos parameter 'epsilon' has to be >= 0"); } - if (sig < 0.) { + if (sigma < 0.) { throw std::domain_error("LJcos parameter 'sigma' has to be >= 0"); } + if (cutoff < 0.) { + throw std::domain_error("LJcos parameter 'cutoff' has to be >= 0"); + } auto const facsq = Utils::cbrt_2() * Utils::sqr(sig); rmin = sqrt(Utils::cbrt_2()) * sig; diff --git a/src/core/nonbonded_interactions/ljcos2.cpp b/src/core/nonbonded_interactions/ljcos2.cpp index 31ccb14196b..1f4539e26f0 100644 --- a/src/core/nonbonded_interactions/ljcos2.cpp +++ b/src/core/nonbonded_interactions/ljcos2.cpp @@ -30,18 +30,21 @@ #include #include -LJcos2_Parameters::LJcos2_Parameters(double eps, double sig, double offset, - double w) - : eps{eps}, sig{sig}, offset{offset}, w{w} { - if (eps < 0.) { +LJcos2_Parameters::LJcos2_Parameters(double epsilon, double sigma, + double offset, double width) + : eps{epsilon}, sig{sigma}, offset{offset}, w{width} { + if (epsilon < 0.) { throw std::domain_error("LJcos2 parameter 'epsilon' has to be >= 0"); } - if (sig < 0.) { + if (sigma < 0.) { throw std::domain_error("LJcos2 parameter 'sigma' has to be >= 0"); } - if (sig != 0.) { - rchange = std::pow(2., 1. / 6.) * sig; - cut = w + rchange; + if (width < 0.) { + throw std::domain_error("LJcos2 parameter 'width' has to be >= 0"); + } + if (sigma != 0.) { + rchange = std::pow(2., 1. / 6.) * sigma; + cut = width + rchange; } } diff --git a/src/core/nonbonded_interactions/ljgen.cpp b/src/core/nonbonded_interactions/ljgen.cpp index 51feb1bc448..d97e80f7258 100644 --- a/src/core/nonbonded_interactions/ljgen.cpp +++ b/src/core/nonbonded_interactions/ljgen.cpp @@ -46,6 +46,9 @@ LJGen_Parameters::LJGen_Parameters(double epsilon, double sigma, double cutoff, if (sigma < 0.) { throw std::domain_error("Generic LJ parameter 'sigma' has to be >= 0"); } + if (cutoff < 0.) { + throw std::domain_error("Generic LJ parameter 'cutoff' has to be >= 0"); + } #ifdef LJGEN_SOFTCORE if (delta < 0.) { throw std::domain_error("Generic LJ parameter 'delta' has to be >= 0"); diff --git a/src/core/nonbonded_interactions/morse.cpp b/src/core/nonbonded_interactions/morse.cpp index f4465491b54..a4e4cfe0212 100644 --- a/src/core/nonbonded_interactions/morse.cpp +++ b/src/core/nonbonded_interactions/morse.cpp @@ -36,6 +36,9 @@ Morse_Parameters::Morse_Parameters(double eps, double alpha, double rmin, if (eps < 0.) { throw std::domain_error("Morse parameter 'eps' has to be >= 0"); } + if (cutoff < 0.) { + throw std::domain_error("Morse parameter 'cutoff' has to be >= 0"); + } auto const add1 = std::exp(-2.0 * alpha * (cut - rmin)); auto const add2 = 2.0 * std::exp(-alpha * (cut - rmin)); rest = eps * (add1 - add2); diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp index b778530f051..48a89767b33 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.hpp @@ -28,6 +28,7 @@ #include "config.hpp" #include +#include #include #include @@ -49,10 +50,17 @@ struct LJ_Parameters { double offset = 0.0; double min = 0.0; LJ_Parameters() = default; - LJ_Parameters(double eps, double sig, double cut, double offset, double min); - LJ_Parameters(double eps, double sig, double cut, double offset, double min, - double shift); + LJ_Parameters(double epsilon, double sigma, double cutoff, double offset, + double min, double shift); + double get_auto_shift() const { + auto auto_shift = 0.; + if (cut != 0.) { + auto_shift = Utils::int_pow<6>(sig / cut) - Utils::int_pow<12>(sig / cut); + } + return auto_shift; + } double max_cutoff() const { return cut + offset; } + double min_cutoff() const { return min + offset; } }; /** WCA potential */ @@ -61,7 +69,7 @@ struct WCA_Parameters { double sig = 0.0; double cut = INACTIVE_CUTOFF; WCA_Parameters() = default; - WCA_Parameters(double eps, double sig); + WCA_Parameters(double epsilon, double sigma); double max_cutoff() const { return cut; } }; @@ -124,7 +132,7 @@ struct Gaussian_Parameters { double sig = 1.0; double cut = INACTIVE_CUTOFF; Gaussian_Parameters() = default; - Gaussian_Parameters(double eps, double sig, double cut); + Gaussian_Parameters(double eps, double sig, double cutoff); double max_cutoff() const { return cut; } }; @@ -202,7 +210,7 @@ struct LJcos_Parameters { double beta = 0.0; double rmin = 0.0; LJcos_Parameters() = default; - LJcos_Parameters(double eps, double sig, double cut, double offset); + LJcos_Parameters(double epsilon, double sigma, double cutoff, double offset); double max_cutoff() const { return cut + offset; } }; @@ -215,7 +223,7 @@ struct LJcos2_Parameters { double w = 0.0; double rchange = 0.0; LJcos2_Parameters() = default; - LJcos2_Parameters(double eps, double sig, double offset, double w); + LJcos2_Parameters(double epsilon, double sigma, double offset, double width); double max_cutoff() const { return cut + offset; } }; diff --git a/src/core/nonbonded_interactions/smooth_step.cpp b/src/core/nonbonded_interactions/smooth_step.cpp index fd9d8f16e0d..6c1b9ee7f93 100644 --- a/src/core/nonbonded_interactions/smooth_step.cpp +++ b/src/core/nonbonded_interactions/smooth_step.cpp @@ -36,6 +36,12 @@ SmoothStep_Parameters::SmoothStep_Parameters(double eps, double sig, if (eps < 0.) { throw std::domain_error("SmoothStep parameter 'eps' has to be >= 0"); } + if (sig < 0.) { + throw std::domain_error("SmoothStep parameter 'sig' has to be >= 0"); + } + if (cutoff < 0.) { + throw std::domain_error("SmoothStep parameter 'cutoff' has to be >= 0"); + } } #endif // SMOOTH_STEP diff --git a/src/core/nonbonded_interactions/soft_sphere.cpp b/src/core/nonbonded_interactions/soft_sphere.cpp index bfa8e44d928..84812c1f4f0 100644 --- a/src/core/nonbonded_interactions/soft_sphere.cpp +++ b/src/core/nonbonded_interactions/soft_sphere.cpp @@ -36,6 +36,9 @@ SoftSphere_Parameters::SoftSphere_Parameters(double a, double n, double cutoff, if (a < 0.) { throw std::domain_error("SoftSphere parameter 'a' has to be >= 0"); } + if (cutoff < 0.) { + throw std::domain_error("SoftSphere parameter 'cutoff' has to be >= 0"); + } if (offset < 0.) { throw std::domain_error("SoftSphere parameter 'offset' has to be >= 0"); } diff --git a/src/core/nonbonded_interactions/wca.cpp b/src/core/nonbonded_interactions/wca.cpp index 389bc65544a..4de3020b2ed 100644 --- a/src/core/nonbonded_interactions/wca.cpp +++ b/src/core/nonbonded_interactions/wca.cpp @@ -28,15 +28,16 @@ #include #include -WCA_Parameters::WCA_Parameters(double eps, double sig) : eps{eps}, sig{sig} { - if (eps < 0.) { +WCA_Parameters::WCA_Parameters(double epsilon, double sigma) + : eps{epsilon}, sig{sigma} { + if (epsilon < 0.) { throw std::domain_error("WCA parameter 'epsilon' has to be >= 0"); } - if (sig < 0.) { + if (sigma < 0.) { throw std::domain_error("WCA parameter 'sigma' has to be >= 0"); } - if (sig != 0.) { - cut = sig * std::pow(2., 1. / 6.); + if (sigma != 0.) { + cut = sigma * std::pow(2., 1. / 6.); } } diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index dda684e5983..36c2b25d1df 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -27,39 +27,50 @@ class NonBondedInteraction(ScriptInterfaceHelper): """ Represents an instance of a non-bonded interaction, such as Lennard-Jones. + Methods + ------- + deactivate() + Reset parameters for the interaction. + """ + _so_bind_methods = ("deactivate",) def __init__(self, **kwargs): if "sip" in kwargs: super().__init__(**kwargs) else: - self._validate(params=kwargs, check_required=True) + self._validate(kwargs) params = self.default_params() params.update(kwargs) super().__init__(**params) + self._validate_post(params) def __str__(self): return f'{self.__class__.__name__}({self.get_params()})' - def _validate(self, params, check_required=False): - # Check, if any key was passed, which is not known - keys = {x for x in params.keys() if x != "_types"} - utils.check_valid_keys(self.valid_keys(), keys) - # When an interaction is newly activated, all required keys must be - # given - if check_required: - utils.check_required_keys(self.required_keys(), params.keys()) + def _validate(self, params): + for key in self.required_keys(): + if key not in params: + raise RuntimeError( + f"{self.__class__.__name__} parameter '{key}' is missing") + + def _validate_post(self, params): + valid_parameters = self.valid_keys() + for key in params: + if key != "_types" and key not in valid_parameters: + raise RuntimeError( + f"Parameter '{key}' is not a valid {self.__class__.__name__} parameter") def set_params(self, **kwargs): - """Update the given parameters. + """Set new parameters. """ - self._validate(params=kwargs, check_required=not self.is_active()) - - params = self.get_params() + self._validate(kwargs) + params = self.default_params() params.update(kwargs) + self._validate_post(params) - err_msg = f"setting {self.type_name()} raised an error" + err_msg = f"setting {self.__class__.__name__} raised an error" self.call_method("set_params", handle_errors_message=err_msg, **params) def _serialize(self): @@ -72,26 +83,11 @@ class NonBondedInteraction(ScriptInterfaceHelper): raise Exception( "Subclasses of NonBondedInteraction must define the default_params() method.") - def is_active(self): - """Virtual method. - - """ - raise Exception( - "Subclasses of NonBondedInteraction must define the is_active() method.") - - def type_name(self): - """Virtual method. - - """ - raise Exception( - "Subclasses of NonBondedInteraction must define the type_name() method.") - def valid_keys(self): - """Virtual method. + """All parameters that can be set. """ - raise Exception( - "Subclasses of NonBondedInteraction must define the valid_keys() method.") + return set(self._valid_parameters()) def required_keys(self): """Virtual method. @@ -111,10 +107,7 @@ IF LENNARD_JONES == 1: Methods ------- set_params() - Set or update parameters for the interaction. - Parameters marked as required become optional once the - interaction has been activated for the first time; - subsequent calls to this method update the existing values. + Set new parameters for the interaction. Parameters ---------- @@ -137,30 +130,12 @@ IF LENNARD_JONES == 1: _so_name = "Interactions::InteractionLJ" - def is_active(self): - """Check if interaction is active. - - """ - return self.epsilon > 0. - def default_params(self): """Python dictionary of default parameters. """ return {"offset": 0., "min": 0.} - def type_name(self): - """Name of interaction type. - - """ - return "LennardJones" - - def valid_keys(self): - """All parameters that can be set. - - """ - return {"epsilon", "sigma", "cutoff", "shift", "offset", "min"} - def required_keys(self): """Parameters that have to be set. @@ -177,10 +152,7 @@ IF WCA == 1: Methods ------- set_params() - Set or update parameters for the interaction. - Parameters marked as required become optional once the - interaction has been activated for the first time; - subsequent calls to this method update the existing values. + Set new parameters for the interaction. Parameters ---------- @@ -193,27 +165,12 @@ IF WCA == 1: _so_name = "Interactions::InteractionWCA" - def is_active(self): - return self.epsilon > 0. - def default_params(self): """Python dictionary of default parameters. """ return {} - def type_name(self): - """Name of interaction type. - - """ - return "WCA" - - def valid_keys(self): - """All parameters that can be set. - - """ - return {"epsilon", "sigma"} - def required_keys(self): """Parameters that have to be set. @@ -234,10 +191,7 @@ IF LENNARD_JONES_GENERIC == 1: Methods ------- set_params() - Set or update parameters for the interaction. - Parameters marked as required become optional once the - interaction has been activated for the first time; - subsequent calls to this method update the existing values. + Set new parameters for the interaction. Parameters ---------- @@ -272,12 +226,6 @@ IF LENNARD_JONES_GENERIC == 1: _so_name = "Interactions::InteractionLJGen" - def is_active(self): - """Check if interaction is active. - - """ - return self.epsilon > 0. - def default_params(self): """Python dictionary of default parameters. @@ -287,23 +235,6 @@ IF LENNARD_JONES_GENERIC == 1: ELSE: return {} - def type_name(self): - """Name of interaction type. - - """ - return "GenericLennardJones" - - def valid_keys(self): - """All parameters that can be set. - - """ - IF LJGEN_SOFTCORE: - return {"epsilon", "sigma", "cutoff", "shift", - "offset", "e1", "e2", "b1", "b2", "delta", "lam"} - ELSE: - return {"epsilon", "sigma", "cutoff", - "shift", "offset", "e1", "e2", "b1", "b2"} - def required_keys(self): """Parameters that have to be set. @@ -339,30 +270,12 @@ IF LJCOS == 1: _so_name = "Interactions::InteractionLJcos" - def is_active(self): - """Check if interactions is active. - - """ - return self.epsilon > 0. - def default_params(self): """Python dictionary of default parameters. """ return {"offset": 0.} - def type_name(self): - """Name of interaction type. - - """ - return "LennardJonesCos" - - def valid_keys(self): - """All parameters that can be set. - - """ - return {"epsilon", "sigma", "cutoff", "offset"} - def required_keys(self): """Parameters that have to be set. @@ -378,10 +291,7 @@ IF LJCOS2 == 1: Methods ------- set_params() - Set or update parameters for the interaction. - Parameters marked as required become optional once the - interaction has been activated for the first time; - subsequent calls to this method update the existing values. + Set new parameters for the interaction. Parameters ---------- @@ -397,30 +307,12 @@ IF LJCOS2 == 1: _so_name = "Interactions::InteractionLJcos2" - def is_active(self): - """Check if interactions is active. - - """ - return self.epsilon > 0. - def default_params(self): """Python dictionary of default parameters. """ return {"offset": 0.} - def type_name(self): - """Name of interaction type. - - """ - return "LennardJonesCos2" - - def valid_keys(self): - """All parameters that can be set. - - """ - return {"epsilon", "sigma", "width", "offset"} - def required_keys(self): """Parameters that have to be set. @@ -440,10 +332,7 @@ IF HAT == 1: Methods ------- set_params() - Set or update parameters for the interaction. - Parameters marked as required become optional once the - interaction has been activated for the first time; - subsequent calls to this method update the existing values. + Set new parameters for the interaction. Parameters ---------- @@ -456,18 +345,9 @@ IF HAT == 1: _so_name = "Interactions::InteractionHat" - def is_active(self): - return self.F_max > 0. - def default_params(self): return {} - def type_name(self): - return "Hat" - - def valid_keys(self): - return {"F_max", "cutoff"} - def required_keys(self): return {"F_max", "cutoff"} @@ -480,10 +360,7 @@ IF GAY_BERNE: Methods ------- set_params() - Set or update parameters for the interaction. - Parameters marked as required become optional once the - interaction has been activated for the first time; - subsequent calls to this method update the existing values. + Set new parameters for the interaction. Parameters ---------- @@ -507,30 +384,12 @@ IF GAY_BERNE: _so_name = "Interactions::InteractionGayBerne" - def is_active(self): - """Check if interaction is active. - - """ - return self.eps > 0. - def default_params(self): """Python dictionary of default parameters. """ return {} - def type_name(self): - """Name of interaction type. - - """ - return "GayBerne" - - def valid_keys(self): - """All parameters that can be set. - - """ - return {"eps", "sig", "cut", "k1", "k2", "mu", "nu"} - def required_keys(self): """Parameters that have to be set. @@ -546,10 +405,7 @@ IF TABULATED: Methods ------- set_params() - Set or update parameters for the interaction. - Parameters marked as required become optional once the - interaction has been activated for the first time; - subsequent calls to this method update the existing values. + Set new parameters for the interaction. Parameters ---------- @@ -566,30 +422,12 @@ IF TABULATED: _so_name = "Interactions::InteractionTabulated" - def is_active(self): - """Check if interaction is active. - - """ - return self.cutoff > 0 - def default_params(self): """Python dictionary of default parameters. """ return {} - def type_name(self): - """Name of the potential. - - """ - return "Tabulated" - - def valid_keys(self): - """All parameters that can be set. - - """ - return {"min", "max", "energy", "force"} - def required_keys(self): """Parameters that have to be set. @@ -609,10 +447,7 @@ IF DPD: Methods ------- set_params() - Set or update parameters for the interaction. - Parameters marked as required become optional once the - interaction has been activated for the first time; - subsequent calls to this method update the existing values. + Set new parameters for the interaction. Parameters ---------- @@ -637,9 +472,6 @@ IF DPD: _so_name = "Interactions::InteractionDPD" - def is_active(self): - return self.r_cut > 0. or self.trans_r_cut > 0. - def default_params(self): return { "weight_function": 0, @@ -650,13 +482,6 @@ IF DPD: "trans_gamma": 0.0, "trans_r_cut": -1.0} - def type_name(self): - return "DPD" - - def valid_keys(self): - return {"weight_function", "gamma", "k", "r_cut", - "trans_weight_function", "trans_gamma", "trans_r_cut"} - def required_keys(self): return set() @@ -669,10 +494,7 @@ IF SMOOTH_STEP == 1: Methods ------- set_params() - Set or update parameters for the interaction. - Parameters marked as required become optional once the - interaction has been activated for the first time; - subsequent calls to this method update the existing values. + Set new parameters for the interaction. Parameters ---------- @@ -693,30 +515,12 @@ IF SMOOTH_STEP == 1: _so_name = "Interactions::InteractionSmoothStep" - def is_active(self): - """Check if interaction is active. - - """ - return self.eps > 0. and self.sig > 0. - def default_params(self): """Python dictionary of default parameters. """ return {"n": 10, "k0": 0., "sig": 0.} - def type_name(self): - """Name of interaction type. - - """ - return "SmoothStep" - - def valid_keys(self): - """All parameters that can be set. - - """ - return {"d", "n", "eps", "k0", "sig", "cutoff"} - def required_keys(self): """Parameters that have to be set. @@ -732,10 +536,7 @@ IF BMHTF_NACL == 1: Methods ------- set_params() - Set or update parameters for the interaction. - Parameters marked as required become optional once the - interaction has been activated for the first time; - subsequent calls to this method update the existing values. + Set new parameters for the interaction. Parameters ---------- @@ -755,30 +556,12 @@ IF BMHTF_NACL == 1: _so_name = "Interactions::InteractionBMHTF" - def is_active(self): - """Check if interaction is active. - - """ - return self.a > 0. and self.c > 0. and self.d > 0. - def default_params(self): """Python dictionary of default parameters. """ return {} - def type_name(self): - """Name of interaction type. - - """ - return "BMHTF" - - def valid_keys(self): - """All parameters that can be set. - - """ - return {"a", "b", "c", "d", "sig", "cutoff"} - def required_keys(self): """Parameters that have to be set. @@ -794,10 +577,7 @@ IF MORSE == 1: Methods ------- set_params() - Set or update parameters for the interaction. - Parameters marked as required become optional once the - interaction has been activated for the first time; - subsequent calls to this method update the existing values. + Set new parameters for the interaction. Parameters ---------- @@ -813,30 +593,12 @@ IF MORSE == 1: _so_name = "Interactions::InteractionMorse" - def is_active(self): - """Check if interaction is active. - - """ - return self.eps > 0. - def default_params(self): """Python dictionary of default parameters. """ return {"cutoff": 0.} - def type_name(self): - """Name of interaction type. - - """ - return "Morse" - - def valid_keys(self): - """All parameters that can be set. - - """ - return {"eps", "alpha", "rmin", "cutoff"} - def required_keys(self): """Parameters that have to be set. @@ -852,10 +614,7 @@ IF BUCKINGHAM == 1: Methods ------- set_params() - Set or update parameters for the interaction. - Parameters marked as required become optional once the - interaction has been activated for the first time; - subsequent calls to this method update the existing values. + Set new parameters for the interaction. Parameters ---------- @@ -877,30 +636,12 @@ IF BUCKINGHAM == 1: _so_name = "Interactions::InteractionBuckingham" - def is_active(self): - """Check if interaction is active. - - """ - return self.a > 0. or self.b > 0. or self.d > 0. or self.shift > 0. - def default_params(self): """Python dictionary of default parameters. """ return {"b": 0., "shift": 0.} - def type_name(self): - """Name of interaction type. - - """ - return "Buckingham" - - def valid_keys(self): - """All parameters that can be set. - - """ - return {"a", "b", "c", "d", "discont", "cutoff", "shift"} - def required_keys(self): """Parameters that have to be set. @@ -916,10 +657,7 @@ IF SOFT_SPHERE == 1: Methods ------- set_params() - Set or update parameters for the interaction. - Parameters marked as required become optional once the - interaction has been activated for the first time; - subsequent calls to this method update the existing values. + Set new parameters for the interaction. Parameters ---------- @@ -935,30 +673,12 @@ IF SOFT_SPHERE == 1: _so_name = "Interactions::InteractionSoftSphere" - def is_active(self): - """Check if interaction is active. - - """ - return self.a > 0. - def default_params(self): """Python dictionary of default parameters. """ return {"offset": 0.} - def type_name(self): - """Name of interaction type. - - """ - return "SoftSphere" - - def valid_keys(self): - """All parameters that can be set. - - """ - return {"a", "n", "cutoff", "offset"} - def required_keys(self): """Parameters that have to be set. @@ -974,10 +694,7 @@ IF HERTZIAN == 1: Methods ------- set_params() - Set or update parameters for the interaction. - Parameters marked as required become optional once the - interaction has been activated for the first time; - subsequent calls to this method update the existing values. + Set new parameters for the interaction. Parameters ---------- @@ -989,30 +706,12 @@ IF HERTZIAN == 1: _so_name = "Interactions::InteractionHertzian" - def is_active(self): - """Check if interaction is active. - - """ - return self.eps > 0. - def default_params(self): """Python dictionary of default parameters. """ return {} - def type_name(self): - """Name of interaction type. - - """ - return "Hertzian" - - def valid_keys(self): - """All parameters that can be set. - - """ - return {"eps", "sig"} - def required_keys(self): """Parameters that have to be set. @@ -1028,10 +727,7 @@ IF GAUSSIAN == 1: Methods ------- set_params() - Set or update parameters for the interaction. - Parameters marked as required become optional once the - interaction has been activated for the first time; - subsequent calls to this method update the existing values. + Set new parameters for the interaction. Parameters ---------- @@ -1045,30 +741,12 @@ IF GAUSSIAN == 1: _so_name = "Interactions::InteractionGaussian" - def is_active(self): - """Check if interaction is active. - - """ - return self.eps > 0. - def default_params(self): """Python dictionary of default parameters. """ return {} - def type_name(self): - """Name of interaction type. - - """ - return "Gaussian" - - def valid_keys(self): - """All parameters that can be set. - - """ - return {"eps", "sig", "cutoff"} - def required_keys(self): """Parameters that have to be set. @@ -1084,10 +762,7 @@ IF THOLE: Methods ------- set_params() - Set or update parameters for the interaction. - Parameters marked as required become optional once the - interaction has been activated for the first time; - subsequent calls to this method update the existing values. + Set new parameters for the interaction. Parameters ---------- @@ -1106,18 +781,9 @@ IF THOLE: _so_name = "Interactions::InteractionThole" - def is_active(self): - return self.scaling_coeff != 0. - def default_params(self): return {} - def type_name(self): - return "Thole" - - def valid_keys(self): - return {"scaling_coeff", "q1q2"} - def required_keys(self): return {"scaling_coeff", "q1q2"} @@ -1155,6 +821,10 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): serialized.append((name, *obj._serialize())) return serialized + def reset(self): + for key in self._valid_parameters(): + getattr(self, key).deactivate() + @script_interface_register class NonBondedInteractions(ScriptInterfaceHelper): diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index fd76e16b4c3..effd77a3d26 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -47,6 +47,7 @@ namespace Interactions { template class InteractionPotentialInterface : public AutoParameters> { + /** @brief Particle type pair. */ std::array m_types = {-1, -1}; public: @@ -54,9 +55,16 @@ class InteractionPotentialInterface protected: using AutoParameters>::context; + /** @brief Managed object. */ std::shared_ptr m_ia_si; + /** @brief Pointer to the corresponding member in a handle. */ virtual CoreInteraction IA_parameters::*get_ptr_offset() const = 0; + /** @brief Create a new instance using the constructor with range checks. */ virtual void make_new_instance(VariantMap const ¶ms) = 0; + /** @brief Which parameter indicates whether the potential is inactive. */ + virtual std::string inactive_parameter() const { return "cutoff"; } + /** @brief Which magic value indicates the potential is inactive. */ + virtual double inactive_cutoff() const { return INACTIVE_CUTOFF; } template auto make_autoparameter(T CoreInteraction::*ptr, char const *name) { @@ -76,6 +84,14 @@ class InteractionPotentialInterface } return {}; } + if (name == "deactivate") { + m_ia_si = std::make_shared(); + if (m_types[0] != -1) { + copy_si_to_core(); + on_non_bonded_ia_change(); + } + return {}; + } if (name == "bind_types") { auto types = get_value>(params, "_types"); if (types[0] > types[1]) { @@ -99,20 +115,21 @@ class InteractionPotentialInterface } void do_construct(VariantMap const ¶ms) final { - if (params.empty()) { - m_ia_si = std::make_shared(); - } else if (params.count("_types") != 0) { + if (params.count("_types") != 0) { do_call_method("bind_types", params); m_ia_si = std::make_shared(); copy_core_to_si(); } else { - context()->parallel_try_catch( - [this, ¶ms]() { make_new_instance(params); }); + if (std::abs(get_value(params, inactive_parameter()) - + inactive_cutoff()) < 1e-9) { + m_ia_si = std::make_shared(); + } else { + context()->parallel_try_catch( + [this, ¶ms]() { make_new_instance(params); }); + } } } - auto const &get_ia() const { return *m_ia_si; } - void copy_si_to_core() { assert(m_ia_si != nullptr); auto const key = get_ia_param_key(m_types[0], m_types[1]); @@ -143,6 +160,9 @@ class InteractionWCA : public InteractionPotentialInterface<::WCA_Parameters> { }); } + std::string inactive_parameter() const override { return "sigma"; } + double inactive_cutoff() const override { return 0.; } + void make_new_instance(VariantMap const ¶ms) override { m_ia_si = make_shared_from_args( params, "epsilon", "sigma"); @@ -179,18 +199,20 @@ class InteractionLJ : public InteractionPotentialInterface<::LJ_Parameters> { } void make_new_instance(VariantMap const ¶ms) override { - if (auto const *shift = boost::get(¶ms.at("shift"))) { - if (*shift != "auto") { + auto new_params = params; + auto const *shift_string = boost::get(¶ms.at("shift")); + if (shift_string != nullptr) { + if (*shift_string != "auto") { throw std::invalid_argument( "LJ parameter 'shift' has to be 'auto' or a float"); } - m_ia_si = make_shared_from_args( - params, "epsilon", "sigma", "cutoff", "offset", "min"); - } else { - m_ia_si = make_shared_from_args( - params, "epsilon", "sigma", "cutoff", "offset", "min", "shift"); + new_params["shift"] = 0.; + } + m_ia_si = make_shared_from_args( + new_params, "epsilon", "sigma", "cutoff", "offset", "min", "shift"); + if (shift_string != nullptr) { + m_ia_si->shift = m_ia_si->get_auto_shift(); } } }; @@ -295,6 +317,9 @@ class InteractionLJcos2 }); } + std::string inactive_parameter() const override { return "sigma"; } + double inactive_cutoff() const override { return 0.; } + void make_new_instance(VariantMap const ¶ms) override { m_ia_si = make_shared_from_args( @@ -327,6 +352,9 @@ class InteractionHertzian make_autoparameter(&CoreInteraction::sig, "sig"), }); } + + std::string inactive_parameter() const override { return "sig"; } + void make_new_instance(VariantMap const ¶ms) override { m_ia_si = make_shared_from_args( params, "eps", "sig"); @@ -350,6 +378,7 @@ class InteractionGaussian make_autoparameter(&CoreInteraction::cut, "cutoff"), }); } + void make_new_instance(VariantMap const ¶ms) override { m_ia_si = make_shared_from_args( params, "eps", "sig", "cutoff"); @@ -509,6 +538,8 @@ class InteractionGayBerne }); } + std::string inactive_parameter() const override { return "cut"; } + void make_new_instance(VariantMap const ¶ms) override { m_ia_si = make_shared_from_args( @@ -535,6 +566,8 @@ class InteractionTabulated }); } + std::string inactive_parameter() const override { return "max"; } + void make_new_instance(VariantMap const ¶ms) override { m_ia_si = make_shared_from_args, std::vector>( @@ -580,6 +613,8 @@ class InteractionDPD : public InteractionPotentialInterface<::DPD_Parameters> { std::ignore = get_ptr_offset(); // for code coverage } + std::string inactive_parameter() const override { return "r_cut"; } + void make_new_instance(VariantMap const ¶ms) override { m_ia_si = make_shared_from_args( @@ -604,6 +639,10 @@ class InteractionThole make_autoparameter(&CoreInteraction::q1q2, "q1q2"), }); } + + std::string inactive_parameter() const override { return "scaling_coeff"; } + double inactive_cutoff() const override { return 0.; } + void make_new_instance(VariantMap const ¶ms) override { m_ia_si = make_shared_from_args( params, "scaling_coeff", "q1q2"); @@ -801,75 +840,65 @@ class NonBondedInteractionHandle auto const key = get_ia_param_key(m_types[0], m_types[1]); m_interaction = ::nonbonded_ia_params[key]; #ifdef WCA - set_member(m_wca, "wca", "Interactions::InteractionWCA", - params); + set_member(m_wca, "wca", "Interactions::InteractionWCA", params); #endif #ifdef LENNARD_JONES - set_member(m_lj, "lennard_jones", - "Interactions::InteractionLJ", params); + set_member(m_lj, "lennard_jones", "Interactions::InteractionLJ", params); #endif #ifdef LENNARD_JONES_GENERIC - set_member(m_ljgen, "generic_lennard_jones", - "Interactions::InteractionLJGen", params); + set_member(m_ljgen, "generic_lennard_jones", + "Interactions::InteractionLJGen", params); #endif #ifdef LJCOS - set_member(m_ljcos, "lennard_jones_cos", - "Interactions::InteractionLJcos", params); + set_member(m_ljcos, "lennard_jones_cos", "Interactions::InteractionLJcos", + params); #endif #ifdef LJCOS2 - set_member(m_ljcos2, "lennard_jones_cos2", - "Interactions::InteractionLJcos2", params); + set_member(m_ljcos2, "lennard_jones_cos2", + "Interactions::InteractionLJcos2", params); #endif #ifdef HERTZIAN - set_member( - m_hertzian, "hertzian", "Interactions::InteractionHertzian", params); + set_member(m_hertzian, "hertzian", "Interactions::InteractionHertzian", + params); #endif #ifdef GAUSSIAN - set_member( - m_gaussian, "gaussian", "Interactions::InteractionGaussian", params); + set_member(m_gaussian, "gaussian", "Interactions::InteractionGaussian", + params); #endif #ifdef BMHTF_NACL - set_member(m_bmhtf, "bmhtf", - "Interactions::InteractionBMHTF", params); + set_member(m_bmhtf, "bmhtf", "Interactions::InteractionBMHTF", params); #endif #ifdef MORSE - set_member(m_morse, "morse", - "Interactions::InteractionMorse", params); + set_member(m_morse, "morse", "Interactions::InteractionMorse", params); #endif #ifdef BUCKINGHAM - set_member(m_buckingham, "buckingham", - "Interactions::InteractionBuckingham", - params); + set_member(m_buckingham, "buckingham", + "Interactions::InteractionBuckingham", params); #endif #ifdef SOFT_SPHERE - set_member(m_soft_sphere, "soft_sphere", - "Interactions::InteractionSoftSphere", - params); + set_member(m_soft_sphere, "soft_sphere", + "Interactions::InteractionSoftSphere", params); #endif #ifdef HAT - set_member(m_hat, "hat", "Interactions::InteractionHat", - params); + set_member(m_hat, "hat", "Interactions::InteractionHat", params); #endif #ifdef GAY_BERNE - set_member( - m_gay_berne, "gay_berne", "Interactions::InteractionGayBerne", params); + set_member(m_gay_berne, "gay_berne", "Interactions::InteractionGayBerne", + params); #endif #ifdef TABULATED - set_member( - m_tabulated, "tabulated", "Interactions::InteractionTabulated", params); + set_member(m_tabulated, "tabulated", "Interactions::InteractionTabulated", + params); #endif #ifdef DPD - set_member(m_dpd, "dpd", "Interactions::InteractionDPD", - params); + set_member(m_dpd, "dpd", "Interactions::InteractionDPD", params); #endif #ifdef THOLE - set_member(m_thole, "thole", - "Interactions::InteractionThole", params); + set_member(m_thole, "thole", "Interactions::InteractionThole", params); #endif #ifdef SMOOTH_STEP - set_member(m_smooth_step, "smooth_step", - "Interactions::InteractionSmoothStep", - params); + set_member(m_smooth_step, "smooth_step", + "Interactions::InteractionSmoothStep", params); #endif } diff --git a/testsuite/python/collision_detection.py b/testsuite/python/collision_detection.py index 16b4236d332..308dddf28d0 100644 --- a/testsuite/python/collision_detection.py +++ b/testsuite/python/collision_detection.py @@ -309,8 +309,7 @@ def test_bind_at_point_of_collision_random(self): self.assertIn(base_particles, bonds) # Tidy - system.non_bonded_inter[0, 0].lennard_jones.set_params( - epsilon=0, sigma=0, cutoff=0) + system.non_bonded_inter[0, 0].lennard_jones.deactivate() def run_test_glue_to_surface_for_pos(self, *positions): system = self.system @@ -578,8 +577,7 @@ def test_glue_to_surface_random(self): self.assertEqual(p.type, self.part_type_vs) # Tidy - system.non_bonded_inter[0, 0].lennard_jones.set_params( - epsilon=0, sigma=0, cutoff=0) + system.non_bonded_inter[0, 0].lennard_jones.deactivate() def test_bind_three_particles(self): system = self.system diff --git a/testsuite/python/constraint_shape_based.py b/testsuite/python/constraint_shape_based.py index 48233a9b6df..775669ef745 100644 --- a/testsuite/python/constraint_shape_based.py +++ b/testsuite/python/constraint_shape_based.py @@ -383,8 +383,7 @@ def test_ellipsoid(self): energy = system.analysis.energy() self.assertAlmostEqual(energy["total"], r - 1.) # Reset the interaction to zero - system.non_bonded_inter[0, 1].generic_lennard_jones.set_params( - epsilon=0., sigma=0., cutoff=0., shift=0., offset=0., e1=0, e2=0, b1=0., b2=0.) + system.non_bonded_inter[0, 1].generic_lennard_jones.deactivate() def test_cylinder(self): """Tests if shape based constraints can be added to a system both by diff --git a/testsuite/python/cutoffs.py b/testsuite/python/cutoffs.py index 8afacd6c0bd..51c4ca0522e 100644 --- a/testsuite/python/cutoffs.py +++ b/testsuite/python/cutoffs.py @@ -56,8 +56,6 @@ def test(self): self.assertEqual(system.cell_system.interaction_range, -1) if espressomd.has_features("LENNARD_JONES"): - lj_off_params = system.non_bonded_inter[0, - 0].lennard_jones.get_params() system.non_bonded_inter[0, 0].lennard_jones.set_params( sigma=1, epsilon=1, cutoff=2.5, shift="auto") self.assertEqual( @@ -66,8 +64,7 @@ def test(self): system.cell_system.max_cut_nonbonded + system.cell_system.skin) - system.non_bonded_inter[0, - 0].lennard_jones.set_params(**lj_off_params) + system.non_bonded_inter[0, 0].lennard_jones.deactivate() self.assertEqual(system.cell_system.max_cut_nonbonded, -1) self.assertEqual(system.cell_system.interaction_range, -1) diff --git a/testsuite/python/dawaanr-and-bh-gpu.py b/testsuite/python/dawaanr-and-bh-gpu.py index 5f7d0fbacd3..f79ac3db0cb 100644 --- a/testsuite/python/dawaanr-and-bh-gpu.py +++ b/testsuite/python/dawaanr-and-bh-gpu.py @@ -67,8 +67,7 @@ def test(self): g.kill_particle_motion(rotation=True) system.integrator.set_vv() - system.non_bonded_inter[0, 0].lennard_jones.set_params( - epsilon=0.0, sigma=0.0, cutoff=-1, shift=0.0) + system.non_bonded_inter[0, 0].lennard_jones.deactivate() system.cell_system.skin = 0.0 system.time_step = 0.01 diff --git a/testsuite/python/dawaanr-and-dds-gpu.py b/testsuite/python/dawaanr-and-dds-gpu.py index 37f386bbc8d..4e3a6499909 100644 --- a/testsuite/python/dawaanr-and-dds-gpu.py +++ b/testsuite/python/dawaanr-and-dds-gpu.py @@ -63,8 +63,7 @@ def test(self): g.kill_particle_motion(rotation=True) self.system.integrator.set_vv() - self.system.non_bonded_inter[0, 0].lennard_jones.set_params( - epsilon=0.0, sigma=0.0, cutoff=0.0, shift=0.0) + self.system.non_bonded_inter[0, 0].lennard_jones.deactivate() self.system.cell_system.skin = 0.0 self.system.time_step = 0.01 diff --git a/testsuite/python/dds-and-bh-gpu.py b/testsuite/python/dds-and-bh-gpu.py index 4f3b19a8d10..99af4c509b9 100644 --- a/testsuite/python/dds-and-bh-gpu.py +++ b/testsuite/python/dds-and-bh-gpu.py @@ -68,8 +68,7 @@ def test(self): g.kill_particle_motion(rotation=True) system.integrator.set_vv() - system.non_bonded_inter[0, 0].lennard_jones.set_params( - epsilon=0.0, sigma=0.0, cutoff=-1, shift=0.0) + system.non_bonded_inter[0, 0].lennard_jones.deactivate() system.cell_system.skin = 0.0 system.time_step = 0.01 diff --git a/testsuite/python/dipolar_direct_summation.py b/testsuite/python/dipolar_direct_summation.py index b39f9d9466d..3c2993502cd 100644 --- a/testsuite/python/dipolar_direct_summation.py +++ b/testsuite/python/dipolar_direct_summation.py @@ -150,8 +150,7 @@ def gen_reference_data(self, filepath_energy=OPEN_BOUNDARIES_REF_ENERGY, system.integrator.set_steepest_descent( f_max=1, gamma=0.001, max_displacement=0.01) system.integrator.run(100) - system.non_bonded_inter[0, 0].lennard_jones.set_params( - epsilon=0.0, sigma=0, cutoff=0, shift=0) + system.non_bonded_inter[0, 0].lennard_jones.deactivate() system.integrator.set_vv() assert system.analysis.energy()["total"] == 0 diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index e04c0b509f4..793dac07572 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -28,9 +28,7 @@ class Test(ut.TestCase): 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.) + self.system.non_bonded_inter.reset() def intersMatch(self, inType, outInter, inParams, outParams, msg_long): """Check, if the interaction type set and gotten back as well as the @@ -114,9 +112,9 @@ def func(self): self.intersMatch( interClass, outInter, params, outParams, - "{}: value set and value gotten back differ for particle types {} and {}: {} vs. {}" - .format(interClass(**params).type_name(), partType1, partType2, - params, outParams)) + f"{interClass.__name__}: value set and value gotten back " + f"differ for particle types {partType1} and {partType2}: " + f"{params} vs. {outParams}") self.parameterKeys(outInter) return func @@ -162,27 +160,42 @@ def func(self): "k2": 5.0, "mu": 2.0, "nu": 1.0}, "gay_berne") - @utx.skipIfMissingFeatures(["LENNARD_JONES", "WCA"]) - def test_set_params(self): + @utx.skipIfMissingFeatures(["LENNARD_JONES"]) + def test_reset(self): + lj_params_inactive = {"shift": 0., "sigma": 0., "epsilon": 0., + "cutoff": -1., "offset": 0., "min": 0.} + # global reset self.system.non_bonded_inter[0, 0].lennard_jones.set_params( epsilon=1., sigma=2., cutoff=3., shift="auto") self.system.non_bonded_inter.reset() self.assertEqual(self.system.non_bonded_inter[0, 0].lennard_jones.get_params(), - {'shift': 0., 'sigma': 0., 'epsilon': 0., - 'cutoff': -1., 'offset': 0., 'min': 0.}) + lj_params_inactive) + # handle reset + self.system.non_bonded_inter[0, 0].lennard_jones.set_params( + epsilon=1., sigma=2., cutoff=3., shift="auto") + self.system.non_bonded_inter[0, 0].reset() + self.assertEqual(self.system.non_bonded_inter[0, 0].lennard_jones.get_params(), + lj_params_inactive) + # potential reset + self.system.non_bonded_inter[0, 0].lennard_jones.set_params( + epsilon=1., sigma=2., cutoff=3., shift="auto") + self.system.non_bonded_inter[0, 0].lennard_jones.deactivate() + self.assertEqual(self.system.non_bonded_inter[0, 0].lennard_jones.get_params(), + lj_params_inactive) + + @utx.skipIfMissingFeatures(["WCA"]) + def test_set_params(self): wca = espressomd.interactions.WCAInteraction(epsilon=1., sigma=2.) - wca.set_params(epsilon=2.) + wca.set_params(epsilon=2., sigma=3.) self.assertEqual(wca.get_params()["epsilon"], 2.) + self.assertEqual(wca.get_params()["sigma"], 3.) self.system.non_bonded_inter[0, 0].wca = wca self.assertEqual( self.system.non_bonded_inter[0, 0].wca.get_params()["epsilon"], 2.) self.assertEqual(wca.get_params()["epsilon"], 2.) - wca.set_params(epsilon=3.) - self.assertEqual( - self.system.non_bonded_inter[0, 0].wca.get_params()["epsilon"], 3.) - self.assertEqual(wca.get_params()["epsilon"], 3.) + self.assertEqual(wca.get_params()["sigma"], 3.) self.system.non_bonded_inter.reset() - wca.set_params(epsilon=4.) + wca.set_params(epsilon=4., sigma=3.) self.assertEqual( self.system.non_bonded_inter[0, 0].wca.get_params()["epsilon"], 4.) self.assertEqual(wca.get_params()["epsilon"], 4.) @@ -191,22 +204,16 @@ def test_set_params(self): @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): + with self.assertRaisesRegex(RuntimeError, "LennardJonesInteraction parameter 'shift' is missing"): espressomd.interactions.LennardJonesInteraction( - epsilon=1., sigma=2.) - with self.assertRaisesRegex(ValueError, err_msg_required): + epsilon=1., sigma=2., cutoff=2.) + with self.assertRaisesRegex(RuntimeError, "LennardJonesInteraction parameter 'shift' is missing"): self.system.non_bonded_inter[0, 0].lennard_jones.set_params( - epsilon=1., sigma=2.) - with self.assertRaisesRegex(ValueError, err_msg_valid): + epsilon=1., sigma=2., cutoff=2.) + with self.assertRaisesRegex(RuntimeError, "Parameter 'unknown' is not a valid LennardJonesInteraction parameter"): espressomd.interactions.LennardJonesInteraction( epsilon=1., sigma=2., cutoff=3., shift=4., unknown=5.) - with self.assertRaisesRegex(ValueError, err_msg_valid): + with self.assertRaisesRegex(RuntimeError, "Parameter 'unknown' is not a valid LennardJonesInteraction parameter"): self.system.non_bonded_inter[0, 0].lennard_jones.set_params( epsilon=1., sigma=2., cutoff=3., shift=4., unknown=5.) with self.assertRaisesRegex(ValueError, "LJ parameter 'shift' has to be 'auto' or a float"): @@ -220,11 +227,14 @@ def test_exceptions(self): 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"): + with self.assertRaisesRegex(Exception, "setting LennardJonesInteraction 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) + inter_00_obj = self.system.non_bonded_inter[0, 0] + self.assertEqual(inter_00_obj.call_method("get_types"), [0, 0]) + self.assertIsNone(inter_00_obj.call_method("unknown")) def check_potential_exceptions(self, ia_class, params, check_keys): for key in check_keys: @@ -235,6 +245,7 @@ def check_potential_exceptions(self, ia_class, params, check_keys): @utx.skipIfMissingFeatures("WCA") def test_wca_exceptions(self): + self.assertEqual(self.system.non_bonded_inter[0, 0].wca.cutoff, -1.) self.check_potential_exceptions( espressomd.interactions.WCAInteraction, {"epsilon": 1., "sigma": 1.}, @@ -245,11 +256,11 @@ def test_lj_exceptions(self): self.check_potential_exceptions( espressomd.interactions.LennardJonesInteraction, {"epsilon": 1., "sigma": 1., "cutoff": 1.5, "shift": 0.2}, - ("epsilon", "sigma")) + ("epsilon", "sigma", "cutoff")) @utx.skipIfMissingFeatures("LENNARD_JONES_GENERIC") def test_ljgen_exceptions(self): - check_keys = ("epsilon", "sigma") + check_keys = ("epsilon", "sigma", "cutoff") params = {"epsilon": 1., "sigma": 1., "cutoff": 1.5, "shift": 0.2, "offset": 0.1, "b1": 1., "b2": 1., "e1": 12., "e2": 6.} if espressomd.has_features(["LJGEN_SOFTCORE"]): @@ -260,7 +271,7 @@ def test_ljgen_exceptions(self): espressomd.interactions.GenericLennardJonesInteraction, params, check_keys) - with self.assertRaisesRegex(ValueError, f"Generic LJ parameter 'shift' has to be 'auto' or a float"): + with self.assertRaisesRegex(ValueError, "Generic LJ parameter 'shift' has to be 'auto' or a float"): invalid_params = params.copy() invalid_params["shift"] = "unknown" espressomd.interactions.GenericLennardJonesInteraction( @@ -271,21 +282,23 @@ def test_lj_cos_exceptions(self): self.check_potential_exceptions( espressomd.interactions.LennardJonesCosInteraction, {"epsilon": 1., "sigma": 1., "cutoff": 1.5, "offset": 0.2}, - ("epsilon", "sigma")) + ("epsilon", "sigma", "cutoff")) @utx.skipIfMissingFeatures("LJCOS2") def test_lj_cos2_exceptions(self): + self.assertEqual( + self.system.non_bonded_inter[0, 0].lennard_jones_cos2.cutoff, -1.) self.check_potential_exceptions( espressomd.interactions.LennardJonesCos2Interaction, {"epsilon": 1., "sigma": 1., "width": 1.5, "offset": 0.2}, - ("epsilon", "sigma")) + ("epsilon", "sigma", "width")) @utx.skipIfMissingFeatures("HERTZIAN") def test_hertzian_exceptions(self): self.check_potential_exceptions( espressomd.interactions.HertzianInteraction, {"eps": 1., "sig": 1.}, - ("eps",) + ("eps", "sig") ) @utx.skipIfMissingFeatures("GAUSSIAN") @@ -293,7 +306,7 @@ def test_gaussian_exceptions(self): self.check_potential_exceptions( espressomd.interactions.GaussianInteraction, {"eps": 1., "sig": 1., "cutoff": 1.5}, - ("eps", "sig") + ("eps", "sig", "cutoff") ) @utx.skipIfMissingFeatures("BMHTF_NACL") @@ -301,7 +314,7 @@ def test_bmhtf_exceptions(self): self.check_potential_exceptions( espressomd.interactions.BMHTFInteraction, {"a": 3., "b": 2., "c": 1., "d": 4., "sig": 0.13, "cutoff": 1.2}, - ("a", "c", "d") + ("a", "c", "d", "cutoff") ) @utx.skipIfMissingFeatures("MORSE") @@ -309,7 +322,7 @@ def test_morse_exceptions(self): self.check_potential_exceptions( espressomd.interactions.MorseInteraction, {"eps": 1., "alpha": 3., "rmin": 0.1, "cutoff": 1.2}, - ("eps",) + ("eps", "cutoff") ) @utx.skipIfMissingFeatures("BUCKINGHAM") @@ -318,7 +331,7 @@ def test_buckingham_exceptions(self): espressomd.interactions.BuckinghamInteraction, {"a": 2., "b": 3., "c": 5., "d": 4., "discont": 1., "cutoff": 2., "shift": 0.1}, - ("a", "b", "c", "d") + ("a", "b", "c", "d", "cutoff") ) @utx.skipIfMissingFeatures("SOFT_SPHERE") @@ -326,7 +339,7 @@ def test_soft_sphere_exceptions(self): self.check_potential_exceptions( espressomd.interactions.SoftSphereInteraction, {"a": 1., "n": 3., "cutoff": 1.1, "offset": 0.1}, - ("a", "offset") + ("a", "offset", "cutoff") ) @utx.skipIfMissingFeatures("HAT") @@ -334,7 +347,7 @@ def test_hat_exceptions(self): self.check_potential_exceptions( espressomd.interactions.HatInteraction, {"F_max": 10., "cutoff": 1.}, - ("F_max",) + ("F_max", "cutoff") ) @utx.skipIfMissingFeatures("SMOOTH_STEP") @@ -342,7 +355,7 @@ def test_smooth_step_exceptions(self): self.check_potential_exceptions( espressomd.interactions.SmoothStepInteraction, {"eps": 4., "sig": 3., "cutoff": 1., "d": 2., "n": 11, "k0": 2.}, - ("eps",) + ("eps", "sig", "cutoff") ) diff --git a/testsuite/python/p3m_fft.py b/testsuite/python/p3m_fft.py index 481fc865c4f..21f63440e16 100644 --- a/testsuite/python/p3m_fft.py +++ b/testsuite/python/p3m_fft.py @@ -65,7 +65,7 @@ def minimize(self): self.system.integrator.run(100) self.system.integrator.set_vv() self.system.non_bonded_inter[0, 0].lennard_jones.set_params( - epsilon=0.0, sigma=1.0, cutoff=2) + epsilon=0.0, sigma=1.0, cutoff=2, shift="auto") def add_charged_particles(self): np.random.seed(seed=42) diff --git a/testsuite/python/save_checkpoint.py b/testsuite/python/save_checkpoint.py index aef41515bc5..1dd3047feef 100644 --- a/testsuite/python/save_checkpoint.py +++ b/testsuite/python/save_checkpoint.py @@ -161,7 +161,7 @@ # constraints system.constraints.add(shape=espressomd.shapes.Sphere(center=system.box_l / 2, radius=0.1), - particle_type=17) + particle_type=7) system.constraints.add( shape=espressomd.shapes.Wall( normal=[1. / np.sqrt(3)] * 3, dist=0.5)) @@ -231,12 +231,8 @@ epsilon=1.2, sigma=1.3, cutoff=2.0, shift=0.1) system.non_bonded_inter[3, 0].lennard_jones.set_params( epsilon=1.2, sigma=1.7, cutoff=2.0, shift=0.1) - system.non_bonded_inter[1, 17].lennard_jones.set_params( + system.non_bonded_inter[1, 7].lennard_jones.set_params( epsilon=1.2e5, sigma=1.7, cutoff=2.0, shift=0.1) - # add inactive interaction - system.non_bonded_inter[4, 4].lennard_jones.set_params( - epsilon=1.4, sigma=1.2, cutoff=1.5, shift=0.2, offset=0.1, min=0.2) - system.non_bonded_inter[4, 4].lennard_jones.set_params(cutoff=-2.) # bonded interactions harmonic_bond = espressomd.interactions.HarmonicBond(r_0=0.0, k=1.0) diff --git a/testsuite/python/test_checkpoint.py b/testsuite/python/test_checkpoint.py index 0aaba75e277..e9182c9bc7d 100644 --- a/testsuite/python/test_checkpoint.py +++ b/testsuite/python/test_checkpoint.py @@ -333,16 +333,12 @@ def test_integrator_SDM(self): def test_non_bonded_inter(self): params1 = system.non_bonded_inter[0, 0].lennard_jones.get_params() params2 = system.non_bonded_inter[3, 0].lennard_jones.get_params() - params3 = system.non_bonded_inter[4, 4].lennard_jones.get_params() reference1 = {'shift': 0.1, 'sigma': 1.3, 'epsilon': 1.2, 'cutoff': 2.0, 'offset': 0.0, 'min': 0.0} reference2 = {'shift': 0.1, 'sigma': 1.7, 'epsilon': 1.2, 'cutoff': 2.0, 'offset': 0.0, 'min': 0.0} - reference3 = {'shift': 0.2, 'sigma': 1.2, 'epsilon': 1.4, - 'cutoff': -2.0, 'offset': 0.1, 'min': 0.2} self.assertEqual(params1, reference1) self.assertEqual(params2, reference2) - self.assertEqual(params3, reference3) def test_bonded_inter(self): # check the ObjectHandle was correctly initialized (including MPI) @@ -630,7 +626,7 @@ def test_constraints(self): self.assertIsInstance(c[0].shape, espressomd.shapes.Sphere) self.assertAlmostEqual(c[0].shape.radius, 0.1, delta=1E-10) - self.assertEqual(c[0].particle_type, 17) + self.assertEqual(c[0].particle_type, 7) self.assertIsInstance(c[1].shape, espressomd.shapes.Wall) np.testing.assert_allclose(np.copy(c[1].shape.normal), diff --git a/testsuite/python/virtual_sites_relative.py b/testsuite/python/virtual_sites_relative.py index 78c5c4a3743..aa0a3b055af 100644 --- a/testsuite/python/virtual_sites_relative.py +++ b/testsuite/python/virtual_sites_relative.py @@ -37,8 +37,7 @@ 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.) + self.system.non_bonded_inter[0, 0].lennard_jones.deactivate() self.system.virtual_sites = espressomd.virtual_sites.VirtualSitesOff() def multiply_quaternions(self, a, b): From 2ef9dcfc4b4a429f367ffbdaad95a75b9eb777fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 9 Sep 2022 18:31:00 +0200 Subject: [PATCH 69/85] doc: Clarify CUDA installation instructions --- doc/sphinx/installation.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/sphinx/installation.rst b/doc/sphinx/installation.rst index 6252a578bd7..a13c09be344 100644 --- a/doc/sphinx/installation.rst +++ b/doc/sphinx/installation.rst @@ -105,20 +105,23 @@ CUDA SDK to make use of GPU computation: sudo apt install nvidia-cuda-toolkit +Later in the installation instructions, you will see CMake commands of the +form ``cmake ..`` with optional arguments, such as ``cmake .. -D WITH_CUDA=ON`` +to activate CUDA. These commands may need to be adapted depending on which +operating system and CUDA version you are using. + On Ubuntu 22.04, the default GCC compiler is too recent for nvcc and will fail to compile sources that rely on ``std::function``. You can either use GCC 10: .. code-block:: bash CC=gcc-10 CXX=g++-10 cmake .. -D WITH_CUDA=ON - make -j or alternatively install Clang 12 as a replacement for nvcc and GCC: .. code-block:: bash CC=clang-12 CXX=clang++-12 cmake .. -D WITH_CUDA=ON -D WITH_CUDA_COMPILER=clang - make -j On Ubuntu 20.04, the default GCC compiler is also too recent for nvcc and will generate compiler errors. You can either install an older version of GCC and @@ -133,7 +136,6 @@ specific one by providing custom paths to the compiler and toolkit: CUDACXX=/usr/local/cuda-11.0/bin/nvcc \ cmake .. -D WITH_CUDA=ON -D CUDA_TOOLKIT_ROOT_DIR=/usr/local/cuda-11.0 - make -j Alternatively for Clang: @@ -141,7 +143,6 @@ Alternatively for Clang: CC=clang-12 CXX=clang++-12 CUDACXX=clang++-12 CUDAToolkit_ROOT=/usr/local/cuda-11.0 \ cmake .. -DWITH_CUDA=ON -DWITH_CUDA_COMPILER=clang -DCMAKE_CXX_FLAGS=--cuda-path=/usr/local/cuda-11.0 - make -j .. _Requirements for building the documentation: From 912fc397175c21974d49d898365d8ae0e9cf0b77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 9 Sep 2022 19:29:30 +0200 Subject: [PATCH 70/85] core: Pass quaternions to Gay-Berne kernels --- src/core/energy_inline.hpp | 3 +-- src/core/forces_inline.hpp | 6 +----- src/core/nonbonded_interactions/gay_berne.hpp | 13 +++++++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/core/energy_inline.hpp b/src/core/energy_inline.hpp index 20123643186..eaa24054fd3 100644 --- a/src/core/energy_inline.hpp +++ b/src/core/energy_inline.hpp @@ -154,8 +154,7 @@ inline double calc_non_bonded_pair_energy( #ifdef GAY_BERNE /* Gay-Berne */ - ret += gb_pair_energy(p1.calc_director(), p2.calc_director(), ia_params, d, - dist); + ret += gb_pair_energy(p1.quat(), p2.quat(), ia_params, d, dist); #endif return ret; diff --git a/src/core/forces_inline.hpp b/src/core/forces_inline.hpp index 691bf5ec967..d17cc3f859e 100644 --- a/src/core/forces_inline.hpp +++ b/src/core/forces_inline.hpp @@ -143,11 +143,7 @@ inline ParticleForce calc_non_bonded_pair_force( #endif /* Gay-Berne */ #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.calc_director(), p2.calc_director(), ia_params, d, - dist); - } + pf += gb_pair_force(p1.quat(), p2.quat(), ia_params, d, dist); #endif pf.f += force_factor * d; return pf; diff --git a/src/core/nonbonded_interactions/gay_berne.hpp b/src/core/nonbonded_interactions/gay_berne.hpp index 2d606a14dbc..2804df031a0 100644 --- a/src/core/nonbonded_interactions/gay_berne.hpp +++ b/src/core/nonbonded_interactions/gay_berne.hpp @@ -41,6 +41,7 @@ #include #include #include +#include #include @@ -49,8 +50,8 @@ int gay_berne_set_params(int part_type_a, int part_type_b, double eps, double mu, double nu); /** Calculate Gay-Berne force and torques */ -inline ParticleForce gb_pair_force(Utils::Vector3d const &ui, - Utils::Vector3d const &uj, +inline ParticleForce gb_pair_force(Utils::Quaternion const &qi, + Utils::Quaternion const &qj, IA_parameters const &ia_params, Utils::Vector3d const &d, double dist) { using Utils::int_pow; @@ -60,6 +61,8 @@ inline ParticleForce gb_pair_force(Utils::Vector3d const &ui, return {}; } + auto const ui = Utils::convert_quaternion_to_director(qi); + auto const uj = Utils::convert_quaternion_to_director(qj); auto const e0 = ia_params.gay_berne.eps; auto const s0 = ia_params.gay_berne.sig; auto const chi1 = ia_params.gay_berne.chi1; @@ -132,8 +135,8 @@ inline ParticleForce gb_pair_force(Utils::Vector3d const &ui, } /** Calculate Gay-Berne energy */ -inline double gb_pair_energy(Utils::Vector3d const &ui, - Utils::Vector3d const &uj, +inline double gb_pair_energy(Utils::Quaternion const &qi, + Utils::Quaternion const &qj, IA_parameters const &ia_params, Utils::Vector3d const &d, double dist) { using Utils::int_pow; @@ -143,6 +146,8 @@ inline double gb_pair_energy(Utils::Vector3d const &ui, return 0.0; } + auto const ui = Utils::convert_quaternion_to_director(qi); + auto const uj = Utils::convert_quaternion_to_director(qj); auto const e0 = ia_params.gay_berne.eps; auto const s0 = ia_params.gay_berne.sig; auto const chi1 = ia_params.gay_berne.chi1; From 8078029bbf595ca6e337e4155e0bb18427a18c39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 12 Sep 2022 13:22:30 +0200 Subject: [PATCH 71/85] Decythonize non-bonded interactions --- .gitlab-ci.yml | 2 + src/core/nonbonded_interactions/thole.hpp | 2 - src/python/espressomd/interactions.pxd | 6 - src/python/espressomd/interactions.pyx | 1499 ++++++++--------- .../interactions/NonBondedInteraction.hpp | 73 +- .../interactions/NonBondedInteractions.hpp | 13 +- testsuite/python/dpd.py | 2 +- .../python/interactions_bonded_interface.py | 13 + .../interactions_non-bonded_interface.py | 70 +- testsuite/python/save_checkpoint.py | 9 + testsuite/python/tabulated.py | 4 + testsuite/python/test_checkpoint.py | 11 +- 12 files changed, 797 insertions(+), 907 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index eb0f5c136dd..5de0d50577a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -438,6 +438,8 @@ empty: with_static_analysis: 'true' with_scafacos: 'false' with_stokesian_dynamics: 'false' + with_coverage: 'false' + with_coverage_python: 'true' script: - bash maintainer/CI/build_cmake.sh tags: diff --git a/src/core/nonbonded_interactions/thole.hpp b/src/core/nonbonded_interactions/thole.hpp index 9c9f4acdebb..041840b8bb3 100644 --- a/src/core/nonbonded_interactions/thole.hpp +++ b/src/core/nonbonded_interactions/thole.hpp @@ -23,8 +23,6 @@ /** \file * Routines to calculate the Thole damping potential between particle pairs. * See @cite thole81a. - * - * Implementation in \ref thole.cpp. */ #include "config.hpp" diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd index 6d5ca5f26fc..e8dd0dd7013 100644 --- a/src/python/espressomd/interactions.pxd +++ b/src/python/espressomd/interactions.pxd @@ -23,12 +23,6 @@ from libc cimport stdint from .thermostat cimport thermalized_bond -include "myconfig.pxi" - -# force include of config.hpp -cdef extern from "config.hpp": - pass - cdef extern from "script_interface/interactions/bonded.hpp": int bonded_ia_params_zero_based_type(int bond_id) except + diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx index 36c2b25d1df..c1510cc96be 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.pyx @@ -18,8 +18,8 @@ # cimport cpython.object -include "myconfig.pxi" from . import utils +from . import code_features from .script_interface import ScriptObjectMap, ScriptInterfaceHelper, script_interface_register @@ -36,45 +36,34 @@ class NonBondedInteraction(ScriptInterfaceHelper): _so_bind_methods = ("deactivate",) def __init__(self, **kwargs): + code_features.assert_features(self.__class__.__dict__["_so_feature"]) if "sip" in kwargs: super().__init__(**kwargs) else: - self._validate(kwargs) params = self.default_params() params.update(kwargs) super().__init__(**params) - self._validate_post(params) def __str__(self): return f'{self.__class__.__name__}({self.get_params()})' - def _validate(self, params): - for key in self.required_keys(): - if key not in params: - raise RuntimeError( - f"{self.__class__.__name__} parameter '{key}' is missing") - - def _validate_post(self, params): - valid_parameters = self.valid_keys() - for key in params: - if key != "_types" and key not in valid_parameters: - raise RuntimeError( - f"Parameter '{key}' is not a valid {self.__class__.__name__} parameter") - def set_params(self, **kwargs): """Set new parameters. """ - self._validate(kwargs) params = self.default_params() params.update(kwargs) - self._validate_post(params) err_msg = f"setting {self.__class__.__name__} raised an error" self.call_method("set_params", handle_errors_message=err_msg, **params) - def _serialize(self): - return (self.__class__.__name__, self.get_params()) + def __reduce__(self): + return (NonBondedInteraction._restore_object, + (self.__class__, self.get_params())) + + @classmethod + def _restore_object(cls, derived_class, kwargs): + return derived_class(**kwargs) def default_params(self): """Virtual method. @@ -83,709 +72,607 @@ class NonBondedInteraction(ScriptInterfaceHelper): raise Exception( "Subclasses of NonBondedInteraction must define the default_params() method.") - def valid_keys(self): - """All parameters that can be set. - """ - return set(self._valid_parameters()) +@script_interface_register +class LennardJonesInteraction(NonBondedInteraction): + """ + Standard 6-12 Lennard-Jones potential. - def required_keys(self): - """Virtual method. + Methods + ------- + set_params() + Set new parameters for the interaction. - """ - raise Exception( - "Subclasses of NonBondedInteraction must define the required_keys() method.") + Parameters + ---------- + epsilon : :obj:`float` + Magnitude of the interaction. + sigma : :obj:`float` + Interaction length scale. + cutoff : :obj:`float` + Cutoff distance of the interaction. + shift : :obj:`float` or :obj:`str` \{'auto'\} + Constant shift of the potential. If ``'auto'``, a default value + is computed from ``sigma`` and ``cutoff``. The LJ potential + will be shifted by :math:`4\\epsilon\\cdot\\text{shift}`. + offset : :obj:`float`, optional + Offset distance of the interaction. + min : :obj:`float`, optional + Restricts the interaction to a minimal distance. + + """ + _so_name = "Interactions::InteractionLJ" + _so_feature = "LENNARD_JONES" -IF LENNARD_JONES == 1: + def default_params(self): + """Python dictionary of default parameters. - @script_interface_register - class LennardJonesInteraction(NonBondedInteraction): """ - Standard 6-12 Lennard-Jones potential. + return {"offset": 0., "min": 0.} - Methods - ------- - set_params() - Set new parameters for the interaction. - Parameters - ---------- - epsilon : :obj:`float` - Magnitude of the interaction. - sigma : :obj:`float` - Interaction length scale. - cutoff : :obj:`float` - Cutoff distance of the interaction. - shift : :obj:`float` or :obj:`str` \{'auto'\} - Constant shift of the potential. If ``'auto'``, a default value - is computed from ``sigma`` and ``cutoff``. The LJ potential - will be shifted by :math:`4\\epsilon\\cdot\\text{shift}`. - offset : :obj:`float`, optional - Offset distance of the interaction. - min : :obj:`float`, optional - Restricts the interaction to a minimal distance. +@script_interface_register +class WCAInteraction(NonBondedInteraction): + """ + Standard 6-12 Weeks-Chandler-Andersen potential. - """ + Methods + ------- + set_params() + Set new parameters for the interaction. - _so_name = "Interactions::InteractionLJ" + Parameters + ---------- + epsilon : :obj:`float` + Magnitude of the interaction. + sigma : :obj:`float` + Interaction length scale. - def default_params(self): - """Python dictionary of default parameters. + """ - """ - return {"offset": 0., "min": 0.} + _so_name = "Interactions::InteractionWCA" + _so_feature = "WCA" - def required_keys(self): - """Parameters that have to be set. + def default_params(self): + """Python dictionary of default parameters. - """ - return {"epsilon", "sigma", "cutoff", "shift"} + """ + return {} -IF WCA == 1: + @property + def cutoff(self): + return self.call_method("get_cutoff") - @script_interface_register - class WCAInteraction(NonBondedInteraction): - """ - Standard 6-12 Weeks-Chandler-Andersen potential. - Methods - ------- - set_params() - Set new parameters for the interaction. +@script_interface_register +class GenericLennardJonesInteraction(NonBondedInteraction): + """ + Generalized Lennard-Jones potential. - Parameters - ---------- - epsilon : :obj:`float` - Magnitude of the interaction. - sigma : :obj:`float` - Interaction length scale. + Methods + ------- + set_params() + Set new parameters for the interaction. - """ + Parameters + ---------- + epsilon : :obj:`float` + Magnitude of the interaction. + sigma : :obj:`float` + Interaction length scale. + cutoff : :obj:`float` + Cutoff distance of the interaction. + shift : :obj:`float` or :obj:`str` \{'auto'\} + Constant shift of the potential. If ``'auto'``, a default value + is computed from the other parameters. The LJ potential + will be shifted by :math:`\\epsilon\\cdot\\text{shift}`. + offset : :obj:`float` + Offset distance of the interaction. + e1 : :obj:`int` + Exponent of the repulsion term. + e2 : :obj:`int` + Exponent of the attraction term. + b1 : :obj:`float` + Prefactor of the repulsion term. + b2 : :obj:`float` + Prefactor of the attraction term. + delta : :obj:`float`, optional + ``LJGEN_SOFTCORE`` parameter delta. Allows control over how + smoothly the potential drops to zero as lambda approaches zero. + lam : :obj:`float`, optional + ``LJGEN_SOFTCORE`` parameter lambda. Tune the strength of the + interaction. - _so_name = "Interactions::InteractionWCA" + """ - def default_params(self): - """Python dictionary of default parameters. + _so_name = "Interactions::InteractionLJGen" + _so_feature = "LENNARD_JONES_GENERIC" - """ - return {} + def default_params(self): + """Python dictionary of default parameters. - def required_keys(self): - """Parameters that have to be set. + """ + if code_features.has_features("LJGEN_SOFTCORE"): + return {"delta": 0., "lam": 1.} + return {} - """ - return {"epsilon", "sigma"} - @property - def cutoff(self): - return self.call_method("get_cutoff") +@script_interface_register +class LennardJonesCosInteraction(NonBondedInteraction): + """Lennard-Jones cosine interaction. -IF LENNARD_JONES_GENERIC == 1: + Methods + ------- + set_params() + Set or update parameters for the interaction. + Parameters marked as required become optional once the + interaction has been activated for the first time; + subsequent calls to this method update the existing values. - @script_interface_register - class GenericLennardJonesInteraction(NonBondedInteraction): - """ - Generalized Lennard-Jones potential. + Parameters + ---------- + epsilon : :obj:`float` + Magnitude of the interaction. + sigma : :obj:`float` + Interaction length scale. + cutoff : :obj:`float` + Cutoff distance of the interaction. + offset : :obj:`float`, optional + Offset distance of the interaction. + """ - Methods - ------- - set_params() - Set new parameters for the interaction. + _so_name = "Interactions::InteractionLJcos" + _so_feature = "LJCOS" - Parameters - ---------- - epsilon : :obj:`float` - Magnitude of the interaction. - sigma : :obj:`float` - Interaction length scale. - cutoff : :obj:`float` - Cutoff distance of the interaction. - shift : :obj:`float` or :obj:`str` \{'auto'\} - Constant shift of the potential. If ``'auto'``, a default value - is computed from the other parameters. The LJ potential - will be shifted by :math:`\\epsilon\\cdot\\text{shift}`. - offset : :obj:`float` - Offset distance of the interaction. - e1 : :obj:`int` - Exponent of the repulsion term. - e2 : :obj:`int` - Exponent of the attraction term. - b1 : :obj:`float` - Prefactor of the repulsion term. - b2 : :obj:`float` - Prefactor of the attraction term. - delta : :obj:`float`, optional - ``LJGEN_SOFTCORE`` parameter delta. Allows control over how - smoothly the potential drops to zero as lambda approaches zero. - lam : :obj:`float`, optional - ``LJGEN_SOFTCORE`` parameter lambda. Tune the strength of the - interaction. + def default_params(self): + """Python dictionary of default parameters. """ + return {"offset": 0.} - _so_name = "Interactions::InteractionLJGen" - def default_params(self): - """Python dictionary of default parameters. +@script_interface_register +class LennardJonesCos2Interaction(NonBondedInteraction): + """Second variant of the Lennard-Jones cosine interaction. - """ - IF LJGEN_SOFTCORE: - return {"delta": 0., "lam": 1.} - ELSE: - return {} + Methods + ------- + set_params() + Set new parameters for the interaction. - def required_keys(self): - """Parameters that have to be set. + Parameters + ---------- + epsilon : :obj:`float` + Magnitude of the interaction. + sigma : :obj:`float` + Interaction length scale. + offset : :obj:`float`, optional + Offset distance of the interaction. + width : :obj:`float` + Width of interaction. + """ - """ - return {"epsilon", "sigma", "cutoff", - "shift", "offset", "e1", "e2", "b1", "b2"} + _so_name = "Interactions::InteractionLJcos2" + _so_feature = "LJCOS2" -IF LJCOS == 1: + def default_params(self): + """Python dictionary of default parameters. - @script_interface_register - class LennardJonesCosInteraction(NonBondedInteraction): - """Lennard-Jones cosine interaction. + """ + return {"offset": 0.} - Methods - ------- - set_params() - Set or update parameters for the interaction. - Parameters marked as required become optional once the - interaction has been activated for the first time; - subsequent calls to this method update the existing values. + @property + def cutoff(self): + return self.call_method("get_cutoff") - Parameters - ---------- - epsilon : :obj:`float` - Magnitude of the interaction. - sigma : :obj:`float` - Interaction length scale. - cutoff : :obj:`float` - Cutoff distance of the interaction. - offset : :obj:`float`, optional - Offset distance of the interaction. - """ - _so_name = "Interactions::InteractionLJcos" - - def default_params(self): - """Python dictionary of default parameters. - - """ - return {"offset": 0.} - - def required_keys(self): - """Parameters that have to be set. - - """ - return {"epsilon", "sigma", "cutoff"} - -IF LJCOS2 == 1: - - @script_interface_register - class LennardJonesCos2Interaction(NonBondedInteraction): - """Second variant of the Lennard-Jones cosine interaction. - - Methods - ------- - set_params() - Set new parameters for the interaction. - - Parameters - ---------- - epsilon : :obj:`float` - Magnitude of the interaction. - sigma : :obj:`float` - Interaction length scale. - offset : :obj:`float`, optional - Offset distance of the interaction. - width : :obj:`float` - Width of interaction. - """ +@script_interface_register +class HatInteraction(NonBondedInteraction): + """Hat interaction. - _so_name = "Interactions::InteractionLJcos2" + Methods + ------- + set_params() + Set new parameters for the interaction. - def default_params(self): - """Python dictionary of default parameters. + Parameters + ---------- + F_max : :obj:`float` + Magnitude of the interaction. + cutoff : :obj:`float` + Cutoff distance of the interaction. - """ - return {"offset": 0.} + """ - def required_keys(self): - """Parameters that have to be set. + _so_name = "Interactions::InteractionHat" + _so_feature = "HAT" - """ - return {"epsilon", "sigma", "width"} + def default_params(self): + return {} + + +@script_interface_register +class GayBerneInteraction(NonBondedInteraction): + """Gay--Berne interaction. - @property - def cutoff(self): - return self.call_method("get_cutoff") + Methods + ------- + set_params() + Set new parameters for the interaction. -IF HAT == 1: + Parameters + ---------- + eps : :obj:`float` + Potential well depth. + sig : :obj:`float` + Interaction range. + cut : :obj:`float` + Cutoff distance of the interaction. + k1 : :obj:`float` or :obj:`str` + Molecular elongation. + k2 : :obj:`float`, optional + Ratio of the potential well depths for the side-by-side + and end-to-end configurations. + mu : :obj:`float`, optional + Adjustable exponent. + nu : :obj:`float`, optional + Adjustable exponent. - @script_interface_register - class HatInteraction(NonBondedInteraction): - """Hat interaction. + """ - Methods - ------- - set_params() - Set new parameters for the interaction. + _so_name = "Interactions::InteractionGayBerne" + _so_feature = "GAY_BERNE" - Parameters - ---------- - F_max : :obj:`float` - Magnitude of the interaction. - cutoff : :obj:`float` - Cutoff distance of the interaction. + def default_params(self): + """Python dictionary of default parameters. """ + return {} - _so_name = "Interactions::InteractionHat" - def default_params(self): - return {} +@script_interface_register +class TabulatedNonBonded(NonBondedInteraction): + """Tabulated interaction. - def required_keys(self): - return {"F_max", "cutoff"} + Methods + ------- + set_params() + Set new parameters for the interaction. -IF GAY_BERNE: + Parameters + ---------- + min : :obj:`float`, + The minimal interaction distance. + max : :obj:`float`, + The maximal interaction distance. + energy: array_like of :obj:`float` + The energy table. + force: array_like of :obj:`float` + The force table. - @script_interface_register - class GayBerneInteraction(NonBondedInteraction): - """Gay--Berne interaction. + """ - Methods - ------- - set_params() - Set new parameters for the interaction. + _so_name = "Interactions::InteractionTabulated" + _so_feature = "TABULATED" - Parameters - ---------- - eps : :obj:`float` - Potential well depth. - sig : :obj:`float` - Interaction range. - cut : :obj:`float` - Cutoff distance of the interaction. - k1 : :obj:`float` or :obj:`str` - Molecular elongation. - k2 : :obj:`float`, optional - Ratio of the potential well depths for the side-by-side - and end-to-end configurations. - mu : :obj:`float`, optional - Adjustable exponent. - nu : :obj:`float`, optional - Adjustable exponent. + def default_params(self): + """Python dictionary of default parameters. """ + return {} - _so_name = "Interactions::InteractionGayBerne" + @property + def cutoff(self): + return self.call_method("get_cutoff") - def default_params(self): - """Python dictionary of default parameters. - """ - return {} +@script_interface_register +class DPDInteraction(NonBondedInteraction): + """DPD interaction. - def required_keys(self): - """Parameters that have to be set. + Methods + ------- + set_params() + Set new parameters for the interaction. - """ - return {"eps", "sig", "cut", "k1", "k2", "mu", "nu"} + Parameters + ---------- + weight_function : :obj:`int`, \{0, 1\} + The distance dependence of the parallel part, + either 0 (constant) or 1 (linear) + gamma : :obj:`float` + Friction coefficient of the parallel part + k : :obj:`float` + Exponent in the modified weight function + r_cut : :obj:`float` + Cutoff of the parallel part + trans_weight_function : :obj:`int`, \{0, 1\} + The distance dependence of the orthogonal part, + either 0 (constant) or 1 (linear) + trans_gamma : :obj:`float` + Friction coefficient of the orthogonal part + trans_r_cut : :obj:`float` + Cutoff of the orthogonal part -IF TABULATED: + """ - @script_interface_register - class TabulatedNonBonded(NonBondedInteraction): - """Tabulated interaction. + _so_name = "Interactions::InteractionDPD" + _so_feature = "DPD" - Methods - ------- - set_params() - Set new parameters for the interaction. + def default_params(self): + return { + "weight_function": 0, + "gamma": 0.0, + "k": 1.0, + "r_cut": -1.0, + "trans_weight_function": 0, + "trans_gamma": 0.0, + "trans_r_cut": -1.0} - Parameters - ---------- - min : :obj:`float`, - The minimal interaction distance. - max : :obj:`float`, - The maximal interaction distance. - energy: array_like of :obj:`float` - The energy table. - force: array_like of :obj:`float` - The force table. - """ +@script_interface_register +class SmoothStepInteraction(NonBondedInteraction): + """Smooth step interaction. - _so_name = "Interactions::InteractionTabulated" + Methods + ------- + set_params() + Set new parameters for the interaction. - def default_params(self): - """Python dictionary of default parameters. + Parameters + ---------- + d : :obj:`float` + Short range repulsion parameter. + n : :obj:`int`, optional + Exponent of short range repulsion. + eps : :obj:`float` + Magnitude of the second (soft) repulsion. + k0 : :obj:`float`, optional + Exponential factor in second (soft) repulsion. + sig : :obj:`float`, optional + Length scale of second (soft) repulsion. + cutoff : :obj:`float` + Cutoff distance of the interaction. - """ - return {} + """ - def required_keys(self): - """Parameters that have to be set. + _so_name = "Interactions::InteractionSmoothStep" + _so_feature = "SMOOTH_STEP" - """ - return {"min", "max", "energy", "force"} + def default_params(self): + """Python dictionary of default parameters. - @property - def cutoff(self): - return self.call_method("get_cutoff") + """ + return {"n": 10, "k0": 0., "sig": 0.} -IF DPD: - @script_interface_register - class DPDInteraction(NonBondedInteraction): - """DPD interaction. - - Methods - ------- - set_params() - Set new parameters for the interaction. - - Parameters - ---------- - weight_function : :obj:`int`, \{0, 1\} - The distance dependence of the parallel part, - either 0 (constant) or 1 (linear) - gamma : :obj:`float` - Friction coefficient of the parallel part - k : :obj:`float` - Exponent in the modified weight function - r_cut : :obj:`float` - Cutoff of the parallel part - trans_weight_function : :obj:`int`, \{0, 1\} - The distance dependence of the orthogonal part, - either 0 (constant) or 1 (linear) - trans_gamma : :obj:`float` - Friction coefficient of the orthogonal part - trans_r_cut : :obj:`float` - Cutoff of the orthogonal part +@script_interface_register +class BMHTFInteraction(NonBondedInteraction): + """BMHTF interaction. - """ + Methods + ------- + set_params() + Set new parameters for the interaction. - _so_name = "Interactions::InteractionDPD" + Parameters + ---------- + a : :obj:`float` + Magnitude of exponential part of the interaction. + b : :obj:`float` + Exponential factor of the interaction. + c : :obj:`float` + Magnitude of the term decaying with the sixth power of r. + d : :obj:`float` + Magnitude of the term decaying with the eighth power of r. + sig : :obj:`float` + Shift in the exponent. + cutoff : :obj:`float` + Cutoff distance of the interaction. - def default_params(self): - return { - "weight_function": 0, - "gamma": 0.0, - "k": 1.0, - "r_cut": -1.0, - "trans_weight_function": 0, - "trans_gamma": 0.0, - "trans_r_cut": -1.0} + """ - def required_keys(self): - return set() - -IF SMOOTH_STEP == 1: - - @script_interface_register - class SmoothStepInteraction(NonBondedInteraction): - """Smooth step interaction. - - Methods - ------- - set_params() - Set new parameters for the interaction. + _so_name = "Interactions::InteractionBMHTF" + _so_feature = "BMHTF_NACL" - Parameters - ---------- - d : :obj:`float` - Short range repulsion parameter. - n : :obj:`int`, optional - Exponent of short range repulsion. - eps : :obj:`float` - Magnitude of the second (soft) repulsion. - k0 : :obj:`float`, optional - Exponential factor in second (soft) repulsion. - sig : :obj:`float`, optional - Length scale of second (soft) repulsion. - cutoff : :obj:`float` - Cutoff distance of the interaction. + def default_params(self): + """Python dictionary of default parameters. """ + return {} - _so_name = "Interactions::InteractionSmoothStep" - def default_params(self): - """Python dictionary of default parameters. +@script_interface_register +class MorseInteraction(NonBondedInteraction): + """Morse interaction. - """ - return {"n": 10, "k0": 0., "sig": 0.} - - def required_keys(self): - """Parameters that have to be set. - - """ - return {"d", "eps", "cutoff"} - -IF BMHTF_NACL == 1: - - @script_interface_register - class BMHTFInteraction(NonBondedInteraction): - """BMHTF interaction. - - Methods - ------- - set_params() - Set new parameters for the interaction. - - Parameters - ---------- - a : :obj:`float` - Magnitude of exponential part of the interaction. - b : :obj:`float` - Exponential factor of the interaction. - c : :obj:`float` - Magnitude of the term decaying with the sixth power of r. - d : :obj:`float` - Magnitude of the term decaying with the eighth power of r. - sig : :obj:`float` - Shift in the exponent. - cutoff : :obj:`float` - Cutoff distance of the interaction. - """ - - _so_name = "Interactions::InteractionBMHTF" - - def default_params(self): - """Python dictionary of default parameters. + Methods + ------- + set_params() + Set new parameters for the interaction. - """ - return {} + Parameters + ---------- + eps : :obj:`float` + The magnitude of the interaction. + alpha : :obj:`float` + Stiffness of the Morse interaction. + rmin : :obj:`float` + Distance of potential minimum + cutoff : :obj:`float`, optional + Cutoff distance of the interaction. - def required_keys(self): - """Parameters that have to be set. + """ - """ - return {"a", "b", "c", "d", "sig", "cutoff"} + _so_name = "Interactions::InteractionMorse" + _so_feature = "MORSE" -IF MORSE == 1: - - @script_interface_register - class MorseInteraction(NonBondedInteraction): - """Morse interaction. - - Methods - ------- - set_params() - Set new parameters for the interaction. + def default_params(self): + """Python dictionary of default parameters. - Parameters - ---------- - eps : :obj:`float` - The magnitude of the interaction. - alpha : :obj:`float` - Stiffness of the Morse interaction. - rmin : :obj:`float` - Distance of potential minimum - cutoff : :obj:`float`, optional - Cutoff distance of the interaction. """ + return {"cutoff": 0.} - _so_name = "Interactions::InteractionMorse" - - def default_params(self): - """Python dictionary of default parameters. - """ - return {"cutoff": 0.} +@script_interface_register +class BuckinghamInteraction(NonBondedInteraction): + """Buckingham interaction. - def required_keys(self): - """Parameters that have to be set. + Methods + ------- + set_params() + Set new parameters for the interaction. - """ - return {"eps", "alpha", "rmin"} + Parameters + ---------- + a : :obj:`float` + Magnitude of the exponential part of the interaction. + b : :obj:`float`, optional + Exponent of the exponential part of the interaction. + c : :obj:`float` + Prefactor of term decaying with the sixth power of distance. + d : :obj:`float` + Prefactor of term decaying with the fourth power of distance. + discont : :obj:`float` + Distance below which the potential is linearly continued. + cutoff : :obj:`float` + Cutoff distance of the interaction. + shift: :obj:`float`, optional + Constant potential shift. -IF BUCKINGHAM == 1: + """ - @script_interface_register - class BuckinghamInteraction(NonBondedInteraction): - """Buckingham interaction. + _so_name = "Interactions::InteractionBuckingham" + _so_feature = "BUCKINGHAM" - Methods - ------- - set_params() - Set new parameters for the interaction. + def default_params(self): + """Python dictionary of default parameters. - Parameters - ---------- - a : :obj:`float` - Magnitude of the exponential part of the interaction. - b : :obj:`float`, optional - Exponent of the exponential part of the interaction. - c : :obj:`float` - Prefactor of term decaying with the sixth power of distance. - d : :obj:`float` - Prefactor of term decaying with the fourth power of distance. - discont : :obj:`float` - Distance below which the potential is linearly continued. - cutoff : :obj:`float` - Cutoff distance of the interaction. - shift: :obj:`float`, optional - Constant potential shift. """ + return {"b": 0., "shift": 0.} - _so_name = "Interactions::InteractionBuckingham" - - def default_params(self): - """Python dictionary of default parameters. - """ - return {"b": 0., "shift": 0.} +@script_interface_register +class SoftSphereInteraction(NonBondedInteraction): + """Soft sphere interaction. - def required_keys(self): - """Parameters that have to be set. + Methods + ------- + set_params() + Set new parameters for the interaction. - """ - return {"a", "c", "d", "discont", "cutoff"} + Parameters + ---------- + a : :obj:`float` + Magnitude of the interaction. + n : :obj:`float` + Exponent of the power law. + cutoff : :obj:`float` + Cutoff distance of the interaction. + offset : :obj:`float`, optional + Offset distance of the interaction. -IF SOFT_SPHERE == 1: + """ - @script_interface_register - class SoftSphereInteraction(NonBondedInteraction): - """Soft sphere interaction. + _so_name = "Interactions::InteractionSoftSphere" + _so_feature = "SOFT_SPHERE" - Methods - ------- - set_params() - Set new parameters for the interaction. + def default_params(self): + """Python dictionary of default parameters. - Parameters - ---------- - a : :obj:`float` - Magnitude of the interaction. - n : :obj:`float` - Exponent of the power law. - cutoff : :obj:`float` - Cutoff distance of the interaction. - offset : :obj:`float`, optional - Offset distance of the interaction. """ + return {"offset": 0.} - _so_name = "Interactions::InteractionSoftSphere" - - def default_params(self): - """Python dictionary of default parameters. - """ - return {"offset": 0.} +@script_interface_register +class HertzianInteraction(NonBondedInteraction): + """Hertzian interaction. - def required_keys(self): - """Parameters that have to be set. + Methods + ------- + set_params() + Set new parameters for the interaction. - """ - return {"a", "n", "cutoff"} + Parameters + ---------- + eps : :obj:`float` + Magnitude of the interaction. + sig : :obj:`float` + Interaction length scale. -IF HERTZIAN == 1: + """ - @script_interface_register - class HertzianInteraction(NonBondedInteraction): - """Hertzian interaction. + _so_name = "Interactions::InteractionHertzian" + _so_feature = "HERTZIAN" - Methods - ------- - set_params() - Set new parameters for the interaction. + def default_params(self): + """Python dictionary of default parameters. - Parameters - ---------- - eps : :obj:`float` - Magnitude of the interaction. - sig : :obj:`float` - Interaction length scale. """ + return {} - _so_name = "Interactions::InteractionHertzian" - - def default_params(self): - """Python dictionary of default parameters. - """ - return {} +@script_interface_register +class GaussianInteraction(NonBondedInteraction): + """Gaussian interaction. - def required_keys(self): - """Parameters that have to be set. + Methods + ------- + set_params() + Set new parameters for the interaction. - """ - return {"eps", "sig"} + Parameters + ---------- + eps : :obj:`float` + Overlap energy epsilon. + sig : :obj:`float` + Variance sigma of the Gaussian interaction. + cutoff : :obj:`float` + Cutoff distance of the interaction. -IF GAUSSIAN == 1: + """ - @script_interface_register - class GaussianInteraction(NonBondedInteraction): - """Gaussian interaction. + _so_name = "Interactions::InteractionGaussian" + _so_feature = "GAUSSIAN" - Methods - ------- - set_params() - Set new parameters for the interaction. + def default_params(self): + """Python dictionary of default parameters. - Parameters - ---------- - eps : :obj:`float` - Overlap energy epsilon. - sig : :obj:`float` - Variance sigma of the Gaussian interaction. - cutoff : :obj:`float` - Cutoff distance of the interaction. """ + return {} - _so_name = "Interactions::InteractionGaussian" - - def default_params(self): - """Python dictionary of default parameters. - - """ - return {} - - def required_keys(self): - """Parameters that have to be set. - - """ - return {"eps", "sig", "cutoff"} - -IF THOLE: - @script_interface_register - class TholeInteraction(NonBondedInteraction): - """Thole interaction. +@script_interface_register +class TholeInteraction(NonBondedInteraction): + """Thole interaction. - Methods - ------- - set_params() - Set new parameters for the interaction. + Methods + ------- + set_params() + Set new parameters for the interaction. - Parameters - ---------- - scaling_coeff : :obj:`float` - The factor used in the Thole damping function between - polarizable particles i and j. Usually calculated by - the polarizabilities :math:`\\alpha_i`, :math:`\\alpha_j` - and damping parameters :math:`a_i`, :math:`a_j` via - :math:`s_{ij} = \\frac{(a_i+a_j)/2}{((\\alpha_i\\cdot\\alpha_j)^{1/2})^{1/3}}` - q1q2: :obj:`float` - Charge factor of the involved charges. Has to be set because - it acts only on the portion of the Drude core charge that is - associated to the dipole of the atom. For charged, polarizable - atoms that charge is not equal to the particle charge property. - """ + Parameters + ---------- + scaling_coeff : :obj:`float` + The factor used in the Thole damping function between + polarizable particles i and j. Usually calculated by + the polarizabilities :math:`\\alpha_i`, :math:`\\alpha_j` + and damping parameters :math:`a_i`, :math:`a_j` via + :math:`s_{ij} = \\frac{(a_i+a_j)/2}{((\\alpha_i\\cdot\\alpha_j)^{1/2})^{1/3}}` + q1q2: :obj:`float` + Charge factor of the involved charges. Has to be set because + it acts only on the portion of the Drude core charge that is + associated to the dipole of the atom. For charged, polarizable + atoms that charge is not equal to the particle charge property. - _so_name = "Interactions::InteractionThole" + """ - def default_params(self): - return {} + _so_name = "Interactions::InteractionThole" + _so_feature = "THOLE" - def required_keys(self): - return {"scaling_coeff", "q1q2"} + def default_params(self): + return {} @script_interface_register @@ -802,29 +689,27 @@ class NonBondedInteractionHandle(ScriptInterfaceHelper): return globals()[obj.__class__.__name__]( _types=self.call_method("get_types"), **obj.get_params()) - def __init__(self, *args, **kwargs): - if "sip" in kwargs: - super().__init__(**kwargs) - return - if len(args): - _type1, _type2 = args - else: - _type1, _type2 = kwargs.pop("_types") - if not (utils.is_valid_type(_type1, int) - and utils.is_valid_type(_type2, int)): - raise TypeError("The particle types have to be of type integer.") - super().__init__(_types=[_type1, _type2], **kwargs) - def _serialize(self): serialized = [] for name, obj in self.get_params().items(): - serialized.append((name, *obj._serialize())) + serialized.append((name, obj.__reduce__()[1])) return serialized def reset(self): for key in self._valid_parameters(): getattr(self, key).deactivate() + @classmethod + def _restore_object(cls, types, kwargs): + objects = {} + for name, (obj_class, obj_params) in kwargs: + objects[name] = obj_class(**obj_params) + return NonBondedInteractionHandle(_types=types, **objects) + + def __reduce__(self): + return (NonBondedInteractionHandle._restore_object, + (self.call_method("get_types"), self._serialize())) + @script_interface_register class NonBondedInteractions(ScriptInterfaceHelper): @@ -870,10 +755,7 @@ class NonBondedInteractions(ScriptInterfaceHelper): def __setstate__(self, params): for types, kwargs in params["state"]: - objects = {} - for name, class_name, obj_params in kwargs: - objects[name] = globals()[class_name](**obj_params) - obj = NonBondedInteractionHandle(_types=types, **objects) + obj = NonBondedInteractionHandle._restore_object(types, kwargs) self.call_method("insert", key=types, object=obj) @classmethod @@ -908,6 +790,10 @@ class BondedInteraction(ScriptInterfaceHelper): kwargs = args[0] args = [] + feature = self.__class__.__dict__.get("_so_feature") + if feature is not None: + code_features.assert_features(feature) + if not 'sip' in kwargs: if len(args) == 1 and utils.is_valid_type(args[0], int): # create a new script interface object for a bond that already @@ -1019,46 +905,6 @@ class BondedInteraction(ScriptInterfaceHelper): raise NotImplementedError("only equality comparison is supported") -class BondedInteractionNotDefined: - - def __init__(self, *args, **kwargs): - raise Exception( - self.__class__.__name__ + " not compiled into ESPResSo core") - - def type_number(self): - raise Exception(f"{self.name} has to be defined in myconfig.hpp.") - - def type_name(self): - """Name of interaction type. - - """ - raise Exception(f"{self.name} has to be defined in myconfig.hpp.") - - def valid_keys(self): - """All parameters that can be set. - - """ - raise Exception(f"{self.name} has to be defined in myconfig.hpp.") - - def required_keys(self): - """Parameters that have to be set. - - """ - raise Exception(f"{self.name} has to be defined in myconfig.hpp.") - - def get_default_params(self): - """Gets default values of optional parameters. - - """ - raise Exception(f"{self.name} has to be defined in myconfig.hpp.") - - def _get_params_from_es_core(self): - raise Exception(f"{self.name} has to be defined in myconfig.hpp.") - - def _set_params_in_es_core(self): - raise Exception(f"{self.name} has to be defined in myconfig.hpp.") - - @script_interface_register class FeneBond(BondedInteraction): @@ -1129,67 +975,65 @@ class HarmonicBond(BondedInteraction): return {"r_cut": 0.} -if ELECTROSTATICS: - - @script_interface_register - class BondedCoulomb(BondedInteraction): - - """ - Bonded Coulomb bond. +@script_interface_register +class BondedCoulomb(BondedInteraction): - Parameters - ---------- + """ + Bonded Coulomb bond. - prefactor : :obj:`float` - Coulomb prefactor of the bonded Coulomb interaction. - """ + Parameters + ---------- - _so_name = "Interactions::BondedCoulomb" + prefactor : :obj:`float` + Coulomb prefactor of the bonded Coulomb interaction. + """ - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + _so_name = "Interactions::BondedCoulomb" + _so_feature = "ELECTROSTATICS" - def type_number(self): - return BONDED_IA_BONDED_COULOMB + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) - def type_name(self): - return "BONDED_COULOMB" + def type_number(self): + return BONDED_IA_BONDED_COULOMB - def get_default_params(self): - """Gets default values of optional parameters. + def type_name(self): + return "BONDED_COULOMB" - """ - return {} + def get_default_params(self): + """Gets default values of optional parameters. + """ + return {} -if ELECTROSTATICS: - @script_interface_register - class BondedCoulombSRBond(BondedInteraction): +@script_interface_register +class BondedCoulombSRBond(BondedInteraction): - """ - Bonded Coulomb short range bond. Calculates the short range part of - Coulomb interactions. + """ + Bonded Coulomb short range bond. Calculates the short range part of + Coulomb interactions. - Parameters - ---------- + Parameters + ---------- - q1q2 : :obj:`float` - Charge factor of the involved particle pair. Note the - particle charges are used to allow e.g. only partial subtraction - of the involved charges. - """ + q1q2 : :obj:`float` + Charge factor of the involved particle pair. Note the + particle charges are used to allow e.g. only partial subtraction + of the involved charges. + """ - _so_name = "Interactions::BondedCoulombSR" + _so_name = "Interactions::BondedCoulombSR" + _so_feature = "ELECTROSTATICS" - def type_number(self): - return BONDED_IA_BONDED_COULOMB_SR + def type_number(self): + return BONDED_IA_BONDED_COULOMB_SR - def type_name(self): - return "BONDED_COULOMB_SR" + def type_name(self): + return "BONDED_COULOMB_SR" - def get_default_params(self): - return {} + def get_default_params(self): + return {} @script_interface_register @@ -1262,49 +1106,44 @@ class ThermalizedBond(BondedInteraction): return {"r_cut": 0., "seed": None} -IF BOND_CONSTRAINT == 1: - - @script_interface_register - class RigidBond(BondedInteraction): - - """ - Rigid bond. +@script_interface_register +class RigidBond(BondedInteraction): - Parameters - ---------- - r : :obj:`float` - Bond length. - ptol : :obj:`float`, optional - Tolerance for positional deviations. - vtop : :obj:`float`, optional - Tolerance for velocity deviations. + """ + Rigid bond. - """ + Parameters + ---------- + r : :obj:`float` + Bond length. + ptol : :obj:`float`, optional + Tolerance for positional deviations. + vtop : :obj:`float`, optional + Tolerance for velocity deviations. - _so_name = "Interactions::RigidBond" + """ - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + _so_name = "Interactions::RigidBond" + _so_feature = "BOND_CONSTRAINT" - def type_number(self): - return BONDED_IA_RIGID_BOND + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) - def type_name(self): - """Name of interaction type. + def type_number(self): + return BONDED_IA_RIGID_BOND - """ - return "RIGID" + def type_name(self): + """Name of interaction type. - def get_default_params(self): - """Gets default values of optional parameters. + """ + return "RIGID" - """ - # TODO rationality of Default Parameters has to be checked - return {"ptol": 0.001, "vtol": 0.001} + def get_default_params(self): + """Gets default values of optional parameters. -ELSE: - class RigidBond(BondedInteractionNotDefined): - name = "RIGID" + """ + # TODO rationality of Default Parameters has to be checked + return {"ptol": 0.001, "vtol": 0.001} @script_interface_register @@ -1350,164 +1189,148 @@ class Dihedral(BondedInteraction): return {} -IF TABULATED: - class _TabulatedBase(BondedInteraction): - - """ - Parent class for tabulated bonds. +@script_interface_register +class TabulatedDistance(BondedInteraction): - Parameters - ---------- + """ + Tabulated bond length. - min : :obj:`float` - The minimal interaction distance. Has to be 0 for angles and dihedrals. - max : :obj:`float` - The maximal interaction distance. Has to be pi for angles and 2pi for - dihedrals. - energy: array_like of :obj:`float` - The energy table. - force: array_like of :obj:`float` - The force table. + Parameters + ---------- - """ + min : :obj:`float` + The minimal interaction distance. + max : :obj:`float` + The maximal interaction distance. + energy: array_like of :obj:`float` + The energy table. + force: array_like of :obj:`float` + The force table. - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + """ - def type_number(self): - return "BONDED_IA_TABULATED" + _so_name = "Interactions::TabulatedDistanceBond" + _so_feature = "TABULATED" - def type_name(self): - """Name of interaction type. + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) - """ - return "TABULATED_BOND" + def type_number(self): + return BONDED_IA_TABULATED_DISTANCE - def get_default_params(self): - """Gets default values of optional parameters. + def type_name(self): + """Name of interaction type. - """ - return {} + """ + return "TABULATED_DISTANCE" - @script_interface_register - class TabulatedDistance(_TabulatedBase): + def get_default_params(self): + """Gets default values of optional parameters. """ - Tabulated bond length. - - Parameters - ---------- + return {} - min : :obj:`float` - The minimal interaction distance. - max : :obj:`float` - The maximal interaction distance. - energy: array_like of :obj:`float` - The energy table. - force: array_like of :obj:`float` - The force table. - """ +@script_interface_register +class TabulatedAngle(BondedInteraction): - _so_name = "Interactions::TabulatedDistanceBond" + """ + Tabulated bond angle. - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + Parameters + ---------- - def type_number(self): - return BONDED_IA_TABULATED_DISTANCE + energy: array_like of :obj:`float` + The energy table for the range :math:`0-\\pi`. + force: array_like of :obj:`float` + The force table for the range :math:`0-\\pi`. - def type_name(self): - """Name of interaction type. + """ - """ - return "TABULATED_DISTANCE" + _so_name = "Interactions::TabulatedAngleBond" + _so_feature = "TABULATED" - @script_interface_register - class TabulatedAngle(_TabulatedBase): + pi = 3.14159265358979 - """ - Tabulated bond angle. + def __init__(self, *args, **kwargs): + if len(args) == 0: + kwargs.update({"min": 0., "max": self.pi}) + super().__init__(*args, **kwargs) - Parameters - ---------- + def type_number(self): + return BONDED_IA_TABULATED_ANGLE - energy: array_like of :obj:`float` - The energy table for the range :math:`0-\\pi`. - force: array_like of :obj:`float` - The force table for the range :math:`0-\\pi`. + def type_name(self): + """Name of interaction type. """ + return "TABULATED_ANGLE" - _so_name = "Interactions::TabulatedAngleBond" - - pi = 3.14159265358979 + def validate_params(self, params): + """Check that parameters are valid. - def __init__(self, *args, **kwargs): - if len(args) == 0: - kwargs.update({"min": 0., "max": self.pi}) - super().__init__(*args, **kwargs) + """ + phi = [params["min"], params["max"]] + if abs(phi[0] - 0.) > 1e-5 or abs(phi[1] - self.pi) > 1e-5: + raise ValueError(f"Tabulated angle expects forces/energies " + f"within the range [0, pi], got {phi}") - def type_number(self): - return BONDED_IA_TABULATED_ANGLE + def get_default_params(self): + """Gets default values of optional parameters. - def type_name(self): - """Name of interaction type. + """ + return {} - """ - return "TABULATED_ANGLE" - def validate_params(self, params): - """Check that parameters are valid. +@script_interface_register +class TabulatedDihedral(BondedInteraction): - """ - phi = [params["min"], params["max"]] - if abs(phi[0] - 0.) > 1e-5 or abs(phi[1] - self.pi) > 1e-5: - raise ValueError(f"Tabulated angle expects forces/energies " - f"within the range [0, pi], got {phi}") + """ + Tabulated bond dihedral. - @script_interface_register - class TabulatedDihedral(_TabulatedBase): + Parameters + ---------- - """ - Tabulated bond dihedral. + energy: array_like of :obj:`float` + The energy table for the range :math:`0-2\\pi`. + force: array_like of :obj:`float` + The force table for the range :math:`0-2\\pi`. - Parameters - ---------- + """ - energy: array_like of :obj:`float` - The energy table for the range :math:`0-2\\pi`. - force: array_like of :obj:`float` - The force table for the range :math:`0-2\\pi`. + _so_name = "Interactions::TabulatedDihedralBond" + _so_feature = "TABULATED" - """ + pi = 3.14159265358979 - _so_name = "Interactions::TabulatedDihedralBond" + def __init__(self, *args, **kwargs): + if len(args) == 0: + kwargs.update({"min": 0., "max": 2. * self.pi}) + super().__init__(*args, **kwargs) - pi = 3.14159265358979 + def type_number(self): + return BONDED_IA_TABULATED_DIHEDRAL - def __init__(self, *args, **kwargs): - if len(args) == 0: - kwargs.update({"min": 0., "max": 2. * self.pi}) - super().__init__(*args, **kwargs) + def type_name(self): + """Name of interaction type. - def type_number(self): - return BONDED_IA_TABULATED_DIHEDRAL + """ + return "TABULATED_DIHEDRAL" - def type_name(self): - """Name of interaction type. + def validate_params(self, params): + """Check that parameters are valid. - """ - return "TABULATED_DIHEDRAL" + """ + phi = [params["min"], params["max"]] + if abs(phi[0] - 0.) > 1e-5 or abs(phi[1] - 2 * self.pi) > 1e-5: + raise ValueError(f"Tabulated dihedral expects forces/energies " + f"within the range [0, 2*pi], got {phi}") - def validate_params(self, params): - """Check that parameters are valid. + def get_default_params(self): + """Gets default values of optional parameters. - """ - phi = [params["min"], params["max"]] - if abs(phi[0] - 0.) > 1e-5 or abs(phi[1] - 2 * self.pi) > 1e-5: - raise ValueError(f"Tabulated dihedral expects forces/energies " - f"within the range [0, 2*pi], got {phi}") + """ + return {} @script_interface_register @@ -1924,8 +1747,13 @@ class QuarticBond(BondedInteraction): bonded_interaction_classes = { int(BONDED_IA_FENE): FeneBond, int(BONDED_IA_HARMONIC): HarmonicBond, + int(BONDED_IA_BONDED_COULOMB): BondedCoulomb, + int(BONDED_IA_BONDED_COULOMB_SR): BondedCoulombSRBond, int(BONDED_IA_RIGID_BOND): RigidBond, int(BONDED_IA_DIHEDRAL): Dihedral, + int(BONDED_IA_TABULATED_DISTANCE): TabulatedDistance, + int(BONDED_IA_TABULATED_ANGLE): TabulatedAngle, + int(BONDED_IA_TABULATED_DIHEDRAL): TabulatedDihedral, int(BONDED_IA_VIRTUAL_BOND): Virtual, int(BONDED_IA_ANGLE_HARMONIC): AngleHarmonic, int(BONDED_IA_ANGLE_COSINE): AngleCosine, @@ -1938,17 +1766,6 @@ bonded_interaction_classes = { int(BONDED_IA_THERMALIZED_DIST): ThermalizedBond, int(BONDED_IA_QUARTIC): QuarticBond, } -IF ELECTROSTATICS: - bonded_interaction_classes[int(BONDED_IA_BONDED_COULOMB)] = BondedCoulomb - bonded_interaction_classes[ - int(BONDED_IA_BONDED_COULOMB_SR)] = BondedCoulombSRBond -IF TABULATED: - bonded_interaction_classes[ - int(BONDED_IA_TABULATED_DISTANCE)] = TabulatedDistance - bonded_interaction_classes[ - int(BONDED_IA_TABULATED_ANGLE)] = TabulatedAngle - bonded_interaction_classes[ - int(BONDED_IA_TABULATED_DIHEDRAL)] = TabulatedDihedral def get_bonded_interaction_type_from_es_core(bond_id): diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index effd77a3d26..6ed08ce4906 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -32,10 +32,13 @@ #include "core/event.hpp" #include "core/nonbonded_interactions/nonbonded_interaction_data.hpp" +#include #include #include #include +#include #include +#include #include #include #include @@ -55,6 +58,7 @@ class InteractionPotentialInterface protected: using AutoParameters>::context; + using AutoParameters>::valid_parameters; /** @brief Managed object. */ std::shared_ptr m_ia_si; /** @brief Pointer to the corresponding member in a handle. */ @@ -72,12 +76,39 @@ class InteractionPotentialInterface [this, ptr]() { return m_ia_si.get()->*ptr; }}; } +private: + std::set get_valid_parameters() const { + auto const vec = valid_parameters(); + auto valid_keys = std::set(); + std::transform(vec.begin(), vec.end(), + std::inserter(valid_keys, valid_keys.begin()), + [](auto const &key) { return std::string{key}; }); + return valid_keys; + } + + void check_valid_parameters(VariantMap const ¶ms) const { + auto const valid_keys = get_valid_parameters(); + for (auto const &key : valid_keys) { + if (params.count(std::string(key)) == 0) { + throw std::runtime_error("Parameter '" + key + "' is missing"); + } + } + for (auto const &kv : params) { + if (valid_keys.count(kv.first) == 0) { + throw std::runtime_error("Parameter '" + kv.first + + "' is not recognized"); + } + } + } + public: Variant do_call_method(std::string const &name, VariantMap const ¶ms) override { if (name == "set_params") { - context()->parallel_try_catch( - [this, ¶ms]() { make_new_instance(params); }); + context()->parallel_try_catch([this, ¶ms]() { + check_valid_parameters(params); + make_new_instance(params); + }); if (m_types[0] != -1) { copy_si_to_core(); on_non_bonded_ia_change(); @@ -92,6 +123,9 @@ class InteractionPotentialInterface } return {}; } + if (name == "is_registered") { + return m_types[0] != -1; + } if (name == "bind_types") { auto types = get_value>(params, "_types"); if (types[0] > types[1]) { @@ -124,8 +158,10 @@ class InteractionPotentialInterface inactive_cutoff()) < 1e-9) { m_ia_si = std::make_shared(); } else { - context()->parallel_try_catch( - [this, ¶ms]() { make_new_instance(params); }); + context()->parallel_try_catch([this, ¶ms]() { + check_valid_parameters(params); + make_new_instance(params); + }); } } } @@ -160,6 +196,7 @@ class InteractionWCA : public InteractionPotentialInterface<::WCA_Parameters> { }); } +private: std::string inactive_parameter() const override { return "sigma"; } double inactive_cutoff() const override { return 0.; } @@ -168,6 +205,7 @@ class InteractionWCA : public InteractionPotentialInterface<::WCA_Parameters> { params, "epsilon", "sigma"); } +public: Variant do_call_method(std::string const &name, VariantMap const ¶ms) override { if (name == "get_cutoff") { @@ -198,6 +236,7 @@ class InteractionLJ : public InteractionPotentialInterface<::LJ_Parameters> { }); } +private: void make_new_instance(VariantMap const ¶ms) override { auto new_params = params; auto const *shift_string = boost::get(¶ms.at("shift")); @@ -245,6 +284,7 @@ class InteractionLJGen }); } +private: void make_new_instance(VariantMap const ¶ms) override { auto new_params = params; auto const *shift_string = boost::get(¶ms.at("shift")); @@ -291,6 +331,7 @@ class InteractionLJcos }); } +private: void make_new_instance(VariantMap const ¶ms) override { m_ia_si = make_shared_from_args( @@ -317,6 +358,7 @@ class InteractionLJcos2 }); } +private: std::string inactive_parameter() const override { return "sigma"; } double inactive_cutoff() const override { return 0.; } @@ -326,6 +368,7 @@ class InteractionLJcos2 params, "epsilon", "sigma", "offset", "width"); } +public: Variant do_call_method(std::string const &name, VariantMap const ¶ms) override { if (name == "get_cutoff") { @@ -353,6 +396,7 @@ class InteractionHertzian }); } +private: std::string inactive_parameter() const override { return "sig"; } void make_new_instance(VariantMap const ¶ms) override { @@ -379,6 +423,7 @@ class InteractionGaussian }); } +private: void make_new_instance(VariantMap const ¶ms) override { m_ia_si = make_shared_from_args( params, "eps", "sig", "cutoff"); @@ -406,6 +451,7 @@ class InteractionBMHTF }); } +private: void make_new_instance(VariantMap const ¶ms) override { m_ia_si = make_shared_from_args( @@ -432,6 +478,7 @@ class InteractionMorse }); } +private: void make_new_instance(VariantMap const ¶ms) override { m_ia_si = make_shared_from_args( @@ -461,6 +508,7 @@ class InteractionBuckingham }); } +private: void make_new_instance(VariantMap const ¶ms) override { m_ia_si = make_shared_from_args( @@ -487,6 +535,7 @@ class InteractionSoftSphere }); } +private: void make_new_instance(VariantMap const ¶ms) override { m_ia_si = make_shared_from_args( @@ -510,6 +559,7 @@ class InteractionHat : public InteractionPotentialInterface<::Hat_Parameters> { }); } +private: void make_new_instance(VariantMap const ¶ms) override { m_ia_si = make_shared_from_args( params, "F_max", "cutoff"); @@ -538,6 +588,7 @@ class InteractionGayBerne }); } +private: std::string inactive_parameter() const override { return "cut"; } void make_new_instance(VariantMap const ¶ms) override { @@ -566,6 +617,7 @@ class InteractionTabulated }); } +private: std::string inactive_parameter() const override { return "max"; } void make_new_instance(VariantMap const ¶ms) override { @@ -574,6 +626,7 @@ class InteractionTabulated params, "min", "max", "force", "energy"); } +public: Variant do_call_method(std::string const &name, VariantMap const ¶ms) override { if (name == "get_cutoff") { @@ -613,6 +666,7 @@ class InteractionDPD : public InteractionPotentialInterface<::DPD_Parameters> { std::ignore = get_ptr_offset(); // for code coverage } +private: std::string inactive_parameter() const override { return "r_cut"; } void make_new_instance(VariantMap const ¶ms) override { @@ -620,6 +674,14 @@ class InteractionDPD : public InteractionPotentialInterface<::DPD_Parameters> { double, double, double, double>( params, "gamma", "k", "r_cut", "weight_function", "trans_gamma", "trans_r_cut", "trans_weight_function"); + if (m_ia_si->radial.wf != 0 and m_ia_si->radial.wf != 1) { + throw std::domain_error( + "DPDInteraction parameter 'weight_function' must be 0 or 1"); + } + if (m_ia_si->trans.wf != 0 and m_ia_si->trans.wf != 1) { + throw std::domain_error( + "DPDInteraction parameter 'trans_weight_function' must be 0 or 1"); + } } }; #endif // DPD @@ -640,6 +702,7 @@ class InteractionThole }); } +private: std::string inactive_parameter() const override { return "scaling_coeff"; } double inactive_cutoff() const override { return 0.; } @@ -669,6 +732,8 @@ class InteractionSmoothStep make_autoparameter(&CoreInteraction::k0, "k0"), }); } + +private: void make_new_instance(VariantMap const ¶ms) override { m_ia_si = make_shared_from_args( diff --git a/src/script_interface/interactions/NonBondedInteractions.hpp b/src/script_interface/interactions/NonBondedInteractions.hpp index 9680d37419d..945ac1be2f1 100644 --- a/src/script_interface/interactions/NonBondedInteractions.hpp +++ b/src/script_interface/interactions/NonBondedInteractions.hpp @@ -69,17 +69,7 @@ class NonBondedInteractions : public ObjectHandle { void do_construct(VariantMap const ¶ms) override { auto const size = ::max_seen_particle_type; - { - // when reloading from a checkpoint file, need to resize IA lists - auto const new_size = ::max_seen_particle_type; - auto const n_pairs = new_size * (new_size + 1) / 2; - ::nonbonded_ia_params.resize(n_pairs); - for (auto &ia_params : ::nonbonded_ia_params) { - if (ia_params == nullptr) { - ia_params = std::make_shared<::IA_parameters>(); - } - } - } + make_particle_type_exist_local(size); for (int i = 0; i < size; i++) { for (int j = i; j < size; j++) { auto const key = Utils::upper_triangular(i, j, size); @@ -99,6 +89,7 @@ class NonBondedInteractions : public ObjectHandle { } if (name == "insert") { auto const types = get_value>(params.at("key")); + make_particle_type_exist_local(std::max(types[0], types[1])); auto const key = get_ia_param_key(std::min(types[0], types[1]), std::max(types[0], types[1])); auto obj_ptr = get_value>( diff --git a/testsuite/python/dpd.py b/testsuite/python/dpd.py index 7d8ce3b6641..64f9dfc003a 100644 --- a/testsuite/python/dpd.py +++ b/testsuite/python/dpd.py @@ -59,7 +59,7 @@ def reset_particles(): gamma = 1.5 # No seed should throw exception - with self.assertRaises(ValueError): + with self.assertRaisesRegex(ValueError, "A seed has to be given as keyword argument on first activation of the thermostat"): system.thermostat.set_dpd(kT=kT) system.thermostat.set_dpd(kT=kT, seed=41) diff --git a/testsuite/python/interactions_bonded_interface.py b/testsuite/python/interactions_bonded_interface.py index c5b0c256232..240b9d551cc 100644 --- a/testsuite/python/interactions_bonded_interface.py +++ b/testsuite/python/interactions_bonded_interface.py @@ -22,6 +22,7 @@ import espressomd import espressomd.interactions +import espressomd.code_features import numpy as np @@ -336,6 +337,18 @@ def test_exceptions(self): self.assertEqual(len(self.system.bonded_inter), 2) self.system.bonded_inter.clear() + def test_feature_checks(self): + base_class = espressomd.interactions.BondedInteraction + FeaturesError = espressomd.code_features.FeaturesError + for class_bond in espressomd.interactions.__dict__.values(): + if isinstance(class_bond, type) and issubclass( + class_bond, base_class) and class_bond != base_class: + feature = getattr(class_bond, "_so_feature", None) + if feature is not None and not espressomd.code_features.has_features( + feature): + with self.assertRaisesRegex(FeaturesError, f"Missing features {feature}"): + class_bond() + if __name__ == "__main__": ut.main() diff --git a/testsuite/python/interactions_non-bonded_interface.py b/testsuite/python/interactions_non-bonded_interface.py index 793dac07572..1bdaf1f9a6c 100644 --- a/testsuite/python/interactions_non-bonded_interface.py +++ b/testsuite/python/interactions_non-bonded_interface.py @@ -22,6 +22,7 @@ import espressomd import espressomd.interactions +import espressomd.code_features class Test(ut.TestCase): @@ -38,40 +39,6 @@ def intersMatch(self, inType, outInter, inParams, outParams, msg_long): self.assertIsInstance(outInter, inType) tests_common.assert_params_match(self, inParams, outParams, msg_long) - def parameterKeys(self, interObject): - """ - Check :meth:`~espressomd.interactions.NonBondedInteraction.valid_keys` - and :meth:`~espressomd.interactions.NonBondedInteraction.required_keys` - return sets, and that - :meth:`~espressomd.interactions.NonBondedInteraction.default_params` - returns a dictionary with the correct keys. - - Parameters - ---------- - interObject: instance of a class derived from :class:`espressomd.interactions.NonBondedInteraction` - Object of the interaction to test, e.g. - :class:`~espressomd.interactions.LennardJonesInteraction` - """ - classname = interObject.__class__.__name__ - valid_keys = interObject.valid_keys() - required_keys = interObject.required_keys() - default_keys = set(interObject.default_params().keys()) - self.assertIsInstance(valid_keys, set, - "{}.valid_keys() must return a set".format( - classname)) - self.assertIsInstance(required_keys, set, - "{}.required_keys() must return a set".format( - classname)) - self.assertTrue(default_keys.issubset(valid_keys), - "{}.default_params() has unknown parameters: {}".format( - classname, default_keys.difference(valid_keys))) - self.assertTrue(default_keys.isdisjoint(required_keys), - "{}.default_params() has extra parameters: {}".format( - classname, default_keys.intersection(required_keys))) - self.assertSetEqual(default_keys, valid_keys - required_keys, - "{}.default_params() should have keys: {}, got: {}".format( - classname, valid_keys - required_keys, default_keys)) - def generateTestForNon_bonded_interaction( _partType1, _partType2, _interClass, _params, _interName): """Generates test cases for checking interaction parameters set and @@ -115,7 +82,6 @@ def func(self): f"{interClass.__name__}: value set and value gotten back " f"differ for particle types {partType1} and {partType2}: " f"{params} vs. {outParams}") - self.parameterKeys(outInter) return func @@ -204,16 +170,16 @@ def test_set_params(self): @utx.skipIfMissingFeatures("LENNARD_JONES") def test_exceptions(self): - with self.assertRaisesRegex(RuntimeError, "LennardJonesInteraction parameter 'shift' is missing"): + with self.assertRaisesRegex(RuntimeError, "Parameter 'shift' is missing"): espressomd.interactions.LennardJonesInteraction( epsilon=1., sigma=2., cutoff=2.) - with self.assertRaisesRegex(RuntimeError, "LennardJonesInteraction parameter 'shift' is missing"): + with self.assertRaisesRegex(RuntimeError, "Parameter 'shift' is missing"): self.system.non_bonded_inter[0, 0].lennard_jones.set_params( epsilon=1., sigma=2., cutoff=2.) - with self.assertRaisesRegex(RuntimeError, "Parameter 'unknown' is not a valid LennardJonesInteraction parameter"): + with self.assertRaisesRegex(RuntimeError, "Parameter 'unknown' is not recognized"): espressomd.interactions.LennardJonesInteraction( epsilon=1., sigma=2., cutoff=3., shift=4., unknown=5.) - with self.assertRaisesRegex(RuntimeError, "Parameter 'unknown' is not a valid LennardJonesInteraction parameter"): + with self.assertRaisesRegex(RuntimeError, "Parameter 'unknown' is not recognized"): self.system.non_bonded_inter[0, 0].lennard_jones.set_params( epsilon=1., sigma=2., cutoff=3., shift=4., unknown=5.) with self.assertRaisesRegex(ValueError, "LJ parameter 'shift' has to be 'auto' or a float"): @@ -236,11 +202,23 @@ def test_exceptions(self): self.assertEqual(inter_00_obj.call_method("get_types"), [0, 0]) self.assertIsNone(inter_00_obj.call_method("unknown")) - def check_potential_exceptions(self, ia_class, params, check_keys): + def test_feature_checks(self): + base_class = espressomd.interactions.NonBondedInteraction + FeaturesError = espressomd.code_features.FeaturesError + for class_ia in espressomd.interactions.__dict__.values(): + if isinstance(class_ia, type) and issubclass( + class_ia, base_class) and class_ia != base_class: + feature = class_ia._so_feature + if not espressomd.code_features.has_features(feature): + with self.assertRaisesRegex(FeaturesError, f"Missing features {feature}"): + class_ia() + + def check_potential_exceptions( + self, ia_class, params, check_keys, invalid_value=-0.1): for key in check_keys: with self.assertRaisesRegex(ValueError, f"parameter '{key}'"): invalid_params = params.copy() - invalid_params[key] = -0.1 + invalid_params[key] = invalid_value ia_class(**invalid_params) @utx.skipIfMissingFeatures("WCA") @@ -358,6 +336,16 @@ def test_smooth_step_exceptions(self): ("eps", "sig", "cutoff") ) + @utx.skipIfMissingFeatures("DPD") + def test_dpd_exceptions(self): + self.check_potential_exceptions( + espressomd.interactions.DPDInteraction, + {"weight_function": 1, "gamma": 2., "r_cut": 2., + "trans_weight_function": 1, "trans_gamma": 1., "trans_r_cut": 2.}, + ("weight_function", "trans_weight_function"), + invalid_value=-1 + ) + if __name__ == "__main__": ut.main() diff --git a/testsuite/python/save_checkpoint.py b/testsuite/python/save_checkpoint.py index 1dd3047feef..c6710cac1c1 100644 --- a/testsuite/python/save_checkpoint.py +++ b/testsuite/python/save_checkpoint.py @@ -233,6 +233,15 @@ epsilon=1.2, sigma=1.7, cutoff=2.0, shift=0.1) system.non_bonded_inter[1, 7].lennard_jones.set_params( epsilon=1.2e5, sigma=1.7, cutoff=2.0, shift=0.1) +if espressomd.has_features(['DPD']): + dpd_params = {"weight_function": 1, "gamma": 2., "trans_r_cut": 2., "k": 2., + "trans_weight_function": 0, "trans_gamma": 1., "r_cut": 2.} + dpd_ia = espressomd.interactions.DPDInteraction(**dpd_params) + handle_ia = espressomd.interactions.NonBondedInteractionHandle( + _types=(0, 0)) + checkpoint.register("dpd_ia") + checkpoint.register("dpd_params") + checkpoint.register("handle_ia") # bonded interactions harmonic_bond = espressomd.interactions.HarmonicBond(r_0=0.0, k=1.0) diff --git a/testsuite/python/tabulated.py b/testsuite/python/tabulated.py index 198fdd34cc1..73304f865af 100644 --- a/testsuite/python/tabulated.py +++ b/testsuite/python/tabulated.py @@ -59,6 +59,8 @@ def check(self): def test_non_bonded(self): self.system.non_bonded_inter[0, 0].tabulated.set_params( min=self.min_, max=self.max_, energy=self.energy, force=self.force) + self.assertEqual( + self.system.non_bonded_inter[0, 0].tabulated.cutoff, self.max_) params = self.system.non_bonded_inter[0, 0].tabulated.get_params() np.testing.assert_allclose(params['force'], self.force) @@ -70,6 +72,8 @@ def test_non_bonded(self): self.system.non_bonded_inter[0, 0].tabulated.set_params( min=-1, max=-1, energy=[], force=[]) + self.assertEqual( + self.system.non_bonded_inter[0, 0].tabulated.cutoff, -1.) with self.assertRaisesRegex(ValueError, "TabulatedPotential parameter 'max' must be larger than or equal to parameter 'min'"): espressomd.interactions.TabulatedNonBonded( diff --git a/testsuite/python/test_checkpoint.py b/testsuite/python/test_checkpoint.py index e9182c9bc7d..2d5b874722b 100644 --- a/testsuite/python/test_checkpoint.py +++ b/testsuite/python/test_checkpoint.py @@ -330,7 +330,9 @@ def test_integrator_SDM(self): @utx.skipIfMissingFeatures('LENNARD_JONES') @ut.skipIf('LJ' not in modes, "Skipping test due to missing mode.") - def test_non_bonded_inter(self): + def test_non_bonded_inter_lj(self): + self.assertTrue( + system.non_bonded_inter[0, 0].lennard_jones.call_method("is_registered")) params1 = system.non_bonded_inter[0, 0].lennard_jones.get_params() params2 = system.non_bonded_inter[3, 0].lennard_jones.get_params() reference1 = {'shift': 0.1, 'sigma': 1.3, 'epsilon': 1.2, @@ -339,6 +341,13 @@ def test_non_bonded_inter(self): 'cutoff': 2.0, 'offset': 0.0, 'min': 0.0} self.assertEqual(params1, reference1) self.assertEqual(params2, reference2) + self.assertTrue(handle_ia.lennard_jones.call_method("is_registered")) + self.assertEqual(handle_ia.lennard_jones.get_params(), reference1) + + @utx.skipIfMissingFeatures('DPD') + def test_non_bonded_inter_dpd(self): + self.assertEqual(dpd_ia.get_params(), dpd_params) + self.assertFalse(dpd_ia.call_method("is_registered")) def test_bonded_inter(self): # check the ObjectHandle was correctly initialized (including MPI) From 283e0c8e412608dc5a11a75f47c94d0d29bad4fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 12 Sep 2022 14:54:42 +0200 Subject: [PATCH 72/85] Decythonize bonded interactions --- .../bonded_interaction_data.hpp | 3 + .../bonded_interactions.dox | 32 +- src/python/espressomd/interactions.pxd | 62 --- .../{interactions.pyx => interactions.py} | 515 ++++-------------- .../analysis/ObservableStat.cpp | 5 +- .../interactions/BondedInteraction.hpp | 336 ++++++------ .../interactions/BondedInteractions.hpp | 5 + src/script_interface/interactions/bonded.hpp | 38 -- .../particle_data/ParticleHandle.cpp | 4 +- .../python/interactions_bonded_interface.py | 97 ++-- testsuite/python/save_checkpoint.py | 20 +- testsuite/python/test_checkpoint.py | 17 +- 12 files changed, 380 insertions(+), 754 deletions(-) delete mode 100644 src/python/espressomd/interactions.pxd rename src/python/espressomd/{interactions.pyx => interactions.py} (76%) delete mode 100644 src/script_interface/interactions/bonded.hpp diff --git a/src/core/bonded_interactions/bonded_interaction_data.hpp b/src/core/bonded_interactions/bonded_interaction_data.hpp index e25f403af06..2f006186cec 100644 --- a/src/core/bonded_interactions/bonded_interaction_data.hpp +++ b/src/core/bonded_interactions/bonded_interaction_data.hpp @@ -133,6 +133,9 @@ class BondedInteractionsMap { bool empty() const { return m_params.empty(); } auto size() const { return m_params.size(); } auto get_next_key() const { return next_key; } + auto get_zero_based_type(int bond_id) const { + return contains(bond_id) ? at(bond_id)->which() : 0; + } private: container_type m_params = {}; diff --git a/src/core/bonded_interactions/bonded_interactions.dox b/src/core/bonded_interactions/bonded_interactions.dox index 5685d6ed817..ab7eb29a285 100644 --- a/src/core/bonded_interactions/bonded_interactions.dox +++ b/src/core/bonded_interactions/bonded_interactions.dox @@ -243,37 +243,27 @@ * * @subsection bondedIA_new_interface Adding the interaction in the Python interface * - * Please note that the following is Cython code (www.cython.org), rather than - * pure Python. - * - * * In file src/python/espressomd/interactions.pxd: - * - Add the bonded interaction to \c enum_bonded_interaction. + * * In file src/python/espressomd/interactions.py: + * - Add the bonded interaction to \c BONDED_IA. * The order of the enum values must match the order of types in * @ref Bonded_IA_Parameters exactly: * @code{.py} - * cdef enum enum_bonded_interaction: - * BONDED_IA_NONE = 0, - * BONDED_IA_FENE, - * [...] + * class BONDED_IA(enum.IntEnum): + * NONE = 0 + * FENE = enum.auto() + * HARMONIC = enum.auto() + * # ... * @endcode - * * In file src/python/espressomd/interactions.pyx: - * - Implement the Cython class for the bonded interaction, using the one + * - Implement the Python class for the bonded interaction, using the one * for the FENE bond as template. Please use pep8 naming convention. - * - Set the class' member + * - Set the class' members * @code{.py} * _so_name = "Interactions::YourNewBond" + * _type_number = BONDED_IA.YOURNEWBOND * @endcode * where you put the name of your bond instead of \c YourNewBond. * This connects the %ScriptInterface class with the Python class. - * - Create a new entry in the dictionary \c bonded_interaction_classes to - * register the new class you have written: - * @code{.py} - * bonded_interaction_classes = { - * int(BONDED_IA_FENE): FeneBond, - * int(BONDED_IA_HARMONIC): HarmonicBond, - * [...] - * } - * @endcode + * The type number is the new enum value from \c BONDED_IA. * * In file testsuite/python/interactions_bonded_interface.py: * - Add a test case to verify that parameters set and gotten from the * interaction are consistent. diff --git a/src/python/espressomd/interactions.pxd b/src/python/espressomd/interactions.pxd deleted file mode 100644 index e8dd0dd7013..00000000000 --- a/src/python/espressomd/interactions.pxd +++ /dev/null @@ -1,62 +0,0 @@ -# -# 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 . -# - -# Handling of interactions - -from libc cimport stdint - -from .thermostat cimport thermalized_bond - -cdef extern from "script_interface/interactions/bonded.hpp": - int bonded_ia_params_zero_based_type(int bond_id) except + - -# Map the boost::variant type indices to python type identifiers. These enum -# values must be in the same order as in the definition of the boost::variant. -cdef enum enum_bonded_interaction: - BONDED_IA_NONE = 0, - BONDED_IA_FENE, - BONDED_IA_HARMONIC, - BONDED_IA_QUARTIC, - BONDED_IA_BONDED_COULOMB, - BONDED_IA_BONDED_COULOMB_SR, - BONDED_IA_ANGLE_HARMONIC, - BONDED_IA_ANGLE_COSINE, - BONDED_IA_ANGLE_COSSQUARE, - BONDED_IA_DIHEDRAL, - BONDED_IA_TABULATED_DISTANCE, - BONDED_IA_TABULATED_ANGLE, - BONDED_IA_TABULATED_DIHEDRAL, - BONDED_IA_THERMALIZED_DIST, - BONDED_IA_RIGID_BOND, - BONDED_IA_IBM_TRIEL, - BONDED_IA_IBM_VOLUME_CONSERVATION, - BONDED_IA_IBM_TRIBEND, - BONDED_IA_OIF_GLOBAL_FORCES, - BONDED_IA_OIF_LOCAL_FORCES, - BONDED_IA_VIRTUAL_BOND - -cdef extern from "thermostat.hpp": - void thermalized_bond_set_rng_counter(stdint.uint64_t counter) - -cdef extern from "immersed_boundary/ImmersedBoundaries.hpp": - cppclass ImmersedBoundaries: - double get_current_volume(int softID) - -cdef extern from "immersed_boundaries.hpp": - extern ImmersedBoundaries immersed_boundaries diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.py similarity index 76% rename from src/python/espressomd/interactions.pyx rename to src/python/espressomd/interactions.py index c1510cc96be..a84c67d1fb6 100644 --- a/src/python/espressomd/interactions.pyx +++ b/src/python/espressomd/interactions.py @@ -16,8 +16,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -cimport cpython.object +import enum from . import utils from . import code_features from .script_interface import ScriptObjectMap, ScriptInterfaceHelper, script_interface_register @@ -770,6 +770,30 @@ def __reduce__(self): (so_callback, (so_name, so_bytestring), self.__getstate__())) +class BONDED_IA(enum.IntEnum): + NONE = 0 + FENE = enum.auto() + HARMONIC = enum.auto() + QUARTIC = enum.auto() + BONDED_COULOMB = enum.auto() + BONDED_COULOMB_SR = enum.auto() + ANGLE_HARMONIC = enum.auto() + ANGLE_COSINE = enum.auto() + ANGLE_COSSQUARE = enum.auto() + DIHEDRAL = enum.auto() + TABULATED_DISTANCE = enum.auto() + TABULATED_ANGLE = enum.auto() + TABULATED_DIHEDRAL = enum.auto() + THERMALIZED_DIST = enum.auto() + RIGID_BOND = enum.auto() + IBM_TRIEL = enum.auto() + IBM_VOLUME_CONSERVATION = enum.auto() + IBM_TRIBEND = enum.auto() + OIF_GLOBAL_FORCES = enum.auto() + OIF_LOCAL_FORCES = enum.auto() + VIRTUAL_BOND = enum.auto() + + class BondedInteraction(ScriptInterfaceHelper): """ @@ -784,74 +808,69 @@ class BondedInteraction(ScriptInterfaceHelper): _so_name = "Interactions::BondedInteraction" _so_creation_policy = "GLOBAL" - def __init__(self, *args, **kwargs): - if len(args) == 1 and len(kwargs) == 0 and isinstance(args[0], dict): - # this branch is only visited by checkpointing constructor #2 - kwargs = args[0] - args = [] - + def __init__(self, **kwargs): feature = self.__class__.__dict__.get("_so_feature") if feature is not None: code_features.assert_features(feature) - if not 'sip' in kwargs: - if len(args) == 1 and utils.is_valid_type(args[0], int): + if "sip" not in kwargs: + if "bond_id" in kwargs: # create a new script interface object for a bond that already - # exists in the core via bond_id (checkpointing constructor #1) - bond_id = args[0] + # exists in the core via its id (BondedInteractions getter and + # checkpointing constructor #1) + bond_id = kwargs["bond_id"] + super().__init__(bond_id=bond_id) # Check if the bond type in ESPResSo core matches this class - if get_bonded_interaction_type_from_es_core( - bond_id) != self.type_number(): - raise Exception( + if self.call_method("get_zero_based_type", + bond_id=bond_id) != self._type_number: + raise RuntimeError( f"The bond with id {bond_id} is not defined as a " - f"{self.type_name()} bond in the ESPResSo core.") - super().__init__(bond_id=bond_id) + f"{self._type_number.name} bond in the ESPResSo core.") self._bond_id = bond_id - self._ctor_params = self._get_params_from_es_core() + self._ctor_params = self.get_params() else: - # create a bond from bond parameters + # create a new script interface object from bond parameters + # (normal bond creation and checkpointing constructor #2) params = self.get_default_params() params.update(kwargs) - self.validate_params(params) - super().__init__(*args, **params) - utils.check_valid_keys(self.valid_keys(), kwargs.keys()) + super().__init__(**params) self._ctor_params = params self._bond_id = -1 else: # create a new bond based on a bond in the script interface - # (checkpointing constructor #2 or BondedInteractions getter) + # (checkpointing constructor #3) super().__init__(**kwargs) self._bond_id = -1 - self._ctor_params = self._get_params_from_es_core() + self._ctor_params = self.get_params() def __reduce__(self): if self._bond_id != -1: # checkpointing constructor #1 - return (self.__class__, (self._bond_id,)) + return (BondedInteraction._restore_object, + (self.__class__, {"bond_id": self._bond_id})) else: # checkpointing constructor #2 - return (self.__class__, (self._ctor_params,)) + return (BondedInteraction._restore_object, + (self.__class__, self._serialize())) + + def _serialize(self): + return self._ctor_params.copy() + + @classmethod + def _restore_object(cls, derived_class, kwargs): + return derived_class(**kwargs) def __setattr__(self, attr, value): super().__setattr__(attr, value) @property def params(self): - return self._get_params_from_es_core() + return self.get_params() @params.setter def params(self, p): raise RuntimeError("Bond parameters are immutable.") - def validate_params(self, params): - """Check that parameters are valid. - - """ - pass - - def _get_params_from_es_core(self): - return {key: getattr(self, key) for key in self.valid_keys()} - def __str__(self): return f'{self.__class__.__name__}({self._ctor_params})' @@ -862,47 +881,15 @@ def get_default_params(self): raise Exception( "Subclasses of BondedInteraction must define the get_default_params() method.") - def type_number(self): - raise Exception( - "Subclasses of BondedInteraction must define the type_number() method.") - - def type_name(self): - """Name of interaction type. - - """ - raise Exception( - "Subclasses of BondedInteraction must define the type_name() method.") - - def valid_keys(self): - """All parameters that can be set. - - """ - return set(self._valid_parameters()) - - def required_keys(self): - """Parameters that have to be set. - - """ - return self.valid_keys().difference(self.get_default_params().keys()) - def __repr__(self): return f'<{self}>' def __eq__(self, other): - return self.__richcmp__(other, cpython.object.Py_EQ) + return self.__class__ == other.__class__ and self.call_method( + "is_same_bond", bond=other) def __ne__(self, other): - return self.__richcmp__(other, cpython.object.Py_NE) - - def __richcmp__(self, other, op): - are_equal = self.__class__ == other.__class__ and self.call_method( - "get_address") == other.call_method("get_address") - if op == cpython.object.Py_EQ: - return are_equal - elif op == cpython.object.Py_NE: - return not are_equal - else: - raise NotImplementedError("only equality comparison is supported") + return not self.__eq__(other) @script_interface_register @@ -923,15 +910,7 @@ class FeneBond(BondedInteraction): """ _so_name = "Interactions::FeneBond" - - def type_number(self): - return BONDED_IA_FENE - - def type_name(self): - """Name of interaction type. - - """ - return "FENE" + _type_number = BONDED_IA.FENE def get_default_params(self): """Gets default values of optional parameters. @@ -958,15 +937,7 @@ class HarmonicBond(BondedInteraction): """ _so_name = "Interactions::HarmonicBond" - - def type_number(self): - return BONDED_IA_HARMONIC - - def type_name(self): - """Name of interaction type. - - """ - return "HARMONIC" + _type_number = BONDED_IA.HARMONIC def get_default_params(self): """Gets default values of optional parameters. @@ -990,15 +961,7 @@ class BondedCoulomb(BondedInteraction): _so_name = "Interactions::BondedCoulomb" _so_feature = "ELECTROSTATICS" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def type_number(self): - return BONDED_IA_BONDED_COULOMB - - def type_name(self): - return "BONDED_COULOMB" + _type_number = BONDED_IA.BONDED_COULOMB def get_default_params(self): """Gets default values of optional parameters. @@ -1025,12 +988,7 @@ class BondedCoulombSRBond(BondedInteraction): _so_name = "Interactions::BondedCoulombSR" _so_feature = "ELECTROSTATICS" - - def type_number(self): - return BONDED_IA_BONDED_COULOMB_SR - - def type_name(self): - return "BONDED_COULOMB_SR" + _type_number = BONDED_IA.BONDED_COULOMB_SR def get_default_params(self): return {} @@ -1060,47 +1018,26 @@ class ThermalizedBond(BondedInteraction): r_cut: :obj:`float`, optional Maximum distance beyond which the bond is considered broken. seed : :obj:`int` - Initial counter value (or seed) of the philox RNG. - Required on the first thermalized bond in the system. Must be positive. - If prompted, it does not return the initially set counter value - (the seed) but the current state of the RNG. + Seed of the philox RNG. Must be positive. + Required for the first thermalized bond in the system. Subsequent + thermalized bonds don't need a seed; if one is provided nonetheless, + it will overwrite the seed of all previously defined thermalized bonds, + even if the new bond is not added to the system. """ _so_name = "Interactions::ThermalizedBond" + _type_number = BONDED_IA.THERMALIZED_DIST def __init__(self, *args, **kwargs): - counter = None - # Interaction id as argument - if len(args) == 2 and isinstance(args[0], (dict, int)): - counter = args[1] - args = (args[0],) + if kwargs and "sip" not in kwargs: + kwargs["rng_state"] = kwargs.get("rng_state") super().__init__(*args, **kwargs) - if counter is not None: - thermalized_bond_set_rng_counter(counter) - if self.params["seed"] is None and thermalized_bond.is_seed_required(): - raise ValueError( - "A seed has to be given as keyword argument on first activation of the thermalized bond") - - def __reduce__(self): - counter = thermalized_bond.rng_counter() - if self._bond_id != -1: - return (self.__class__, (self._bond_id, counter)) - else: - return (self.__class__, (self._ctor_params, counter)) - - def type_number(self): - return BONDED_IA_THERMALIZED_DIST - def type_name(self): - return "THERMALIZED_DIST" - - def validate_params(self, params): - if params["seed"] is not None: - utils.check_type_or_throw_except( - params["seed"], 1, int, "seed must be a positive integer") - if params["seed"] < 0: - raise ValueError("seed must be a positive integer") + def _serialize(self): + params = self._ctor_params.copy() + params["rng_state"] = self.call_method("get_rng_state") + return params def get_default_params(self): return {"r_cut": 0., "seed": None} @@ -1125,18 +1062,7 @@ class RigidBond(BondedInteraction): _so_name = "Interactions::RigidBond" _so_feature = "BOND_CONSTRAINT" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def type_number(self): - return BONDED_IA_RIGID_BOND - - def type_name(self): - """Name of interaction type. - - """ - return "RIGID" + _type_number = BONDED_IA.RIGID_BOND def get_default_params(self): """Gets default values of optional parameters. @@ -1164,23 +1090,7 @@ class Dihedral(BondedInteraction): """ _so_name = "Interactions::DihedralBond" - - def type_number(self): - return BONDED_IA_DIHEDRAL - - def type_name(self): - """Name of interaction type. - - """ - return "DIHEDRAL" - - def validate_params(self, params): - """Check that parameters are valid. - - """ - if params["mult"] is not None: - utils.check_type_or_throw_except( - params["mult"], 1, int, "mult must be a positive integer") + _type_number = BONDED_IA.DIHEDRAL def get_default_params(self): """Gets default values of optional parameters. @@ -1211,18 +1121,7 @@ class TabulatedDistance(BondedInteraction): _so_name = "Interactions::TabulatedDistanceBond" _so_feature = "TABULATED" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def type_number(self): - return BONDED_IA_TABULATED_DISTANCE - - def type_name(self): - """Name of interaction type. - - """ - return "TABULATED_DISTANCE" + _type_number = BONDED_IA.TABULATED_DISTANCE def get_default_params(self): """Gets default values of optional parameters. @@ -1249,32 +1148,15 @@ class TabulatedAngle(BondedInteraction): _so_name = "Interactions::TabulatedAngleBond" _so_feature = "TABULATED" + _type_number = BONDED_IA.TABULATED_ANGLE pi = 3.14159265358979 def __init__(self, *args, **kwargs): - if len(args) == 0: + if len(args) == 0 and "sip" not in kwargs: kwargs.update({"min": 0., "max": self.pi}) super().__init__(*args, **kwargs) - def type_number(self): - return BONDED_IA_TABULATED_ANGLE - - def type_name(self): - """Name of interaction type. - - """ - return "TABULATED_ANGLE" - - def validate_params(self, params): - """Check that parameters are valid. - - """ - phi = [params["min"], params["max"]] - if abs(phi[0] - 0.) > 1e-5 or abs(phi[1] - self.pi) > 1e-5: - raise ValueError(f"Tabulated angle expects forces/energies " - f"within the range [0, pi], got {phi}") - def get_default_params(self): """Gets default values of optional parameters. @@ -1300,32 +1182,15 @@ class TabulatedDihedral(BondedInteraction): _so_name = "Interactions::TabulatedDihedralBond" _so_feature = "TABULATED" + _type_number = BONDED_IA.TABULATED_DIHEDRAL pi = 3.14159265358979 def __init__(self, *args, **kwargs): - if len(args) == 0: + if len(args) == 0 and "sip" not in kwargs: kwargs.update({"min": 0., "max": 2. * self.pi}) super().__init__(*args, **kwargs) - def type_number(self): - return BONDED_IA_TABULATED_DIHEDRAL - - def type_name(self): - """Name of interaction type. - - """ - return "TABULATED_DIHEDRAL" - - def validate_params(self, params): - """Check that parameters are valid. - - """ - phi = [params["min"], params["max"]] - if abs(phi[0] - 0.) > 1e-5 or abs(phi[1] - 2 * self.pi) > 1e-5: - raise ValueError(f"Tabulated dihedral expects forces/energies " - f"within the range [0, 2*pi], got {phi}") - def get_default_params(self): """Gets default values of optional parameters. @@ -1341,18 +1206,7 @@ class Virtual(BondedInteraction): """ _so_name = "Interactions::VirtualBond" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def type_number(self): - return BONDED_IA_VIRTUAL_BOND - - def type_name(self): - """Name of interaction type. - - """ - return "VIRTUAL" + _type_number = BONDED_IA.VIRTUAL_BOND def get_default_params(self): """Gets default values of optional parameters. @@ -1377,15 +1231,7 @@ class AngleHarmonic(BondedInteraction): """ _so_name = "Interactions::AngleHarmonicBond" - - def type_number(self): - return BONDED_IA_ANGLE_HARMONIC - - def type_name(self): - """Name of interaction type. - - """ - return "ANGLE_HARMONIC" + _type_number = BONDED_IA.ANGLE_HARMONIC def get_default_params(self): """Gets default values of optional parameters. @@ -1410,15 +1256,7 @@ class AngleCosine(BondedInteraction): """ _so_name = "Interactions::AngleCosineBond" - - def type_number(self): - return BONDED_IA_ANGLE_COSINE - - def type_name(self): - """Name of interaction type. - - """ - return "ANGLE_COSINE" + _type_number = BONDED_IA.ANGLE_COSINE def get_default_params(self): """Gets default values of optional parameters. @@ -1443,15 +1281,7 @@ class AngleCossquare(BondedInteraction): """ _so_name = "Interactions::AngleCossquareBond" - - def type_number(self): - return BONDED_IA_ANGLE_COSSQUARE - - def type_name(self): - """Name of interaction type. - - """ - return "ANGLE_COSSQUARE" + _type_number = BONDED_IA.ANGLE_COSSQUARE def get_default_params(self): """Gets default values of optional parameters. @@ -1485,25 +1315,7 @@ class IBM_Triel(BondedInteraction): """ _so_name = "Interactions::IBMTriel" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def type_number(self): - return BONDED_IA_IBM_TRIEL - - def type_name(self): - return "IBM_Triel" - - def valid_keys(self): - return {"ind1", "ind2", "ind3", "k1", "k2", "maxDist", "elasticLaw"} - - def validate_params(self, params): - """Check that parameters are valid. - - """ - if params['elasticLaw'] not in {'NeoHookean', 'Skalak'}: - raise ValueError(f"Unknown elasticLaw: '{params['elasticLaw']}'") + _type_number = BONDED_IA.IBM_TRIEL def get_default_params(self): """Gets default values of optional parameters. @@ -1511,13 +1323,6 @@ def get_default_params(self): """ return {"k2": 0} - def _get_params_from_es_core(self): - return \ - {"maxDist": self.maxDist, - "k1": self.k1, - "k2": self.k2, - "elasticLaw": self.elasticLaw} - @script_interface_register class IBM_Tribend(BondedInteraction): @@ -1540,25 +1345,7 @@ class IBM_Tribend(BondedInteraction): """ _so_name = "Interactions::IBMTribend" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def type_number(self): - return BONDED_IA_IBM_TRIBEND - - def type_name(self): - return "IBM_Tribend" - - def valid_keys(self): - return {"ind1", "ind2", "ind3", "ind4", "kb", "refShape"} - - def validate_params(self, params): - """Check that parameters are valid. - - """ - if params['refShape'] not in {'Flat', 'Initial'}: - raise ValueError(f"Unknown refShape: '{params['refShape']}'") + _type_number = BONDED_IA.IBM_TRIBEND def get_default_params(self): """Gets default values of optional parameters. @@ -1566,10 +1353,6 @@ def get_default_params(self): """ return {"refShape": "Flat"} - def _get_params_from_es_core(self): - return {"kb": self.kb, "theta0": self.theta0, - "refShape": self.refShape} - @script_interface_register class IBM_VolCons(BondedInteraction): @@ -1588,18 +1371,18 @@ class IBM_VolCons(BondedInteraction): kappaV : :obj:`float` Modulus for volume force + Methods + ------- + current_volume() + Query the current volume of the soft object associated to this bond. + The volume is initialized once all :class:`IBM_Triel` bonds have + been added and the forces have been recalculated. + """ _so_name = "Interactions::IBMVolCons" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def type_number(self): - return BONDED_IA_IBM_VOLUME_CONSERVATION - - def type_name(self): - return "IBM_VolCons" + _so_bind_methods = ("current_volume",) + _type_number = BONDED_IA.IBM_VOLUME_CONSERVATION def get_default_params(self): """Gets default values of optional parameters. @@ -1607,14 +1390,6 @@ def get_default_params(self): """ return {} - def current_volume(self): - """ - Query the current volume of the soft object associated to this bond. - The volume is initialized once all :class:`IBM_Triel` bonds have - been added and the forces have been recalculated. - """ - return immersed_boundaries.get_current_volume(self.softID) - @script_interface_register class OifGlobalForces(BondedInteraction): @@ -1639,15 +1414,7 @@ class OifGlobalForces(BondedInteraction): """ _so_name = "Interactions::OifGlobalForcesBond" - - def type_number(self): - return BONDED_IA_OIF_GLOBAL_FORCES - - def type_name(self): - """Name of interaction type. - - """ - return "OIF_GLOBAL_FORCES" + _type_number = BONDED_IA.OIF_GLOBAL_FORCES def get_default_params(self): """Gets default values of optional parameters. @@ -1688,15 +1455,7 @@ class OifLocalForces(BondedInteraction): """ _so_name = "Interactions::OifLocalForcesBond" - - def type_number(self): - return BONDED_IA_OIF_LOCAL_FORCES - - def type_name(self): - """Name of interaction type. - - """ - return "OIF_LOCAL_FORCES" + _type_number = BONDED_IA.OIF_LOCAL_FORCES def get_default_params(self): """Gets default values of optional parameters. @@ -1724,18 +1483,7 @@ class QuarticBond(BondedInteraction): """ _so_name = "Interactions::QuarticBond" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def type_number(self): - return BONDED_IA_QUARTIC - - def type_name(self): - """Name of interaction type. - - """ - return "QUARTIC" + _type_number = BONDED_IA.QUARTIC def get_default_params(self): """Gets default values of optional parameters. @@ -1744,34 +1492,6 @@ def get_default_params(self): return {} -bonded_interaction_classes = { - int(BONDED_IA_FENE): FeneBond, - int(BONDED_IA_HARMONIC): HarmonicBond, - int(BONDED_IA_BONDED_COULOMB): BondedCoulomb, - int(BONDED_IA_BONDED_COULOMB_SR): BondedCoulombSRBond, - int(BONDED_IA_RIGID_BOND): RigidBond, - int(BONDED_IA_DIHEDRAL): Dihedral, - int(BONDED_IA_TABULATED_DISTANCE): TabulatedDistance, - int(BONDED_IA_TABULATED_ANGLE): TabulatedAngle, - int(BONDED_IA_TABULATED_DIHEDRAL): TabulatedDihedral, - int(BONDED_IA_VIRTUAL_BOND): Virtual, - int(BONDED_IA_ANGLE_HARMONIC): AngleHarmonic, - int(BONDED_IA_ANGLE_COSINE): AngleCosine, - int(BONDED_IA_ANGLE_COSSQUARE): AngleCossquare, - int(BONDED_IA_OIF_GLOBAL_FORCES): OifGlobalForces, - int(BONDED_IA_OIF_LOCAL_FORCES): OifLocalForces, - int(BONDED_IA_IBM_TRIEL): IBM_Triel, - int(BONDED_IA_IBM_TRIBEND): IBM_Tribend, - int(BONDED_IA_IBM_VOLUME_CONSERVATION): IBM_VolCons, - int(BONDED_IA_THERMALIZED_DIST): ThermalizedBond, - int(BONDED_IA_QUARTIC): QuarticBond, -} - - -def get_bonded_interaction_type_from_es_core(bond_id): - return < enum_bonded_interaction > bonded_ia_params_zero_based_type(bond_id) - - @script_interface_register class BondedInteractions(ScriptObjectMap): @@ -1798,8 +1518,12 @@ class BondedInteractions(ScriptObjectMap): _so_name = "Interactions::BondedInteractions" _so_creation_policy = "GLOBAL" + _bond_classes = { + cls._type_number: cls for cls in globals().values() + if isinstance(cls, type) and issubclass(cls, BondedInteraction) and cls != BondedInteraction + } - def add(self, *args, **kwargs): + def add(self, *args): """ Add a bond to the list. @@ -1807,9 +1531,6 @@ def add(self, *args, **kwargs): ---------- bond: :class:`espressomd.interactions.BondedInteraction` Either a bond object... - \*\*kwargs : any - ... or parameters to construct a - :class:`~espressomd.interactions.BondedInteraction` object """ @@ -1829,18 +1550,18 @@ def __getitem__(self, bond_id): return bond_obj # Find out the type of the interaction from ESPResSo - bond_type = get_bonded_interaction_type_from_es_core(bond_id) + bond_type = self.call_method("get_zero_based_type", bond_id=bond_id) # Check if the bonded interaction exists in ESPResSo core - if bond_type == BONDED_IA_NONE: + if bond_type == BONDED_IA.NONE: raise ValueError(f"The bond with id {bond_id} is not yet defined.") # Find the appropriate class representing such a bond - bond_class = bonded_interaction_classes[bond_type] + bond_class = self._bond_classes[bond_type] # Create a new script interface object (i.e. a copy of the shared_ptr) # which links to the bonded interaction object - return bond_class(bond_id) + return bond_class(bond_id=bond_id) def __setitem__(self, bond_id, bond_obj): self._insert_bond(bond_id, bond_obj) @@ -1867,9 +1588,9 @@ def _insert_bond(self, bond_id, bond_obj): # 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_obj) is old_type: + old_type = self._bond_classes[ + self.call_method("get_zero_based_type", bond_id=bond_id)] + if not isinstance(bond_obj, old_type): raise ValueError( "Bonds can only be overwritten by bonds of equal type.") self.call_method("insert", key=bond_id, object=bond_obj) @@ -1885,20 +1606,18 @@ def __len__(self): # Support iteration over active bonded interactions def __iter__(self): for bond_id in self.call_method('get_bond_ids'): - if get_bonded_interaction_type_from_es_core(bond_id): + if self.call_method("get_zero_based_type", bond_id=bond_id): yield self[bond_id] def __getstate__(self): params = {} for bond_id in self.call_method('get_bond_ids'): - if get_bonded_interaction_type_from_es_core(bond_id): - bond_obj = self[bond_id] - if hasattr(bond_obj, 'params'): - params[bond_id] = ( - bond_obj._ctor_params, bond_obj.type_number()) + if self.call_method("get_zero_based_type", bond_id=bond_id): + obj = self[bond_id] + if hasattr(obj, "params"): + params[bond_id] = (obj._type_number, obj._serialize()) return params def __setstate__(self, params): - for bond_id, (bond_params, bond_type) in params.items(): - self[bond_id] = bonded_interaction_classes[bond_type]( - **bond_params) + for bond_id, (type_number, bond_params) in params.items(): + self[bond_id] = self._bond_classes[type_number](**bond_params) diff --git a/src/script_interface/analysis/ObservableStat.cpp b/src/script_interface/analysis/ObservableStat.cpp index 1d0c59036a8..db055e27ee4 100644 --- a/src/script_interface/analysis/ObservableStat.cpp +++ b/src/script_interface/analysis/ObservableStat.cpp @@ -21,8 +21,7 @@ #include "ObservableStat.hpp" -#include "script_interface/interactions/bonded.hpp" - +#include "core/bonded_interactions/bonded_interaction_data.hpp" #include "core/nonbonded_interactions/nonbonded_interaction_data.hpp" #include "core/energy.hpp" @@ -86,7 +85,7 @@ static auto get_summary(Observable_stat const &obs, bool const calc_sp) { auto const n_bonds = ::bonded_ia_params.get_next_key(); for (std::size_t bond_id = 0; bond_id < n_bonds; ++bond_id) { - if (bonded_ia_params_zero_based_type(bond_id) != 0) { + if (::bonded_ia_params.get_zero_based_type(bond_id) != 0) { dict["bonded," + std::to_string(bond_id)] = get_obs_contrib(obs.bonded_contribution(bond_id)); } diff --git a/src/script_interface/interactions/BondedInteraction.hpp b/src/script_interface/interactions/BondedInteraction.hpp index 787a8b57a17..46c8b752d6c 100644 --- a/src/script_interface/interactions/BondedInteraction.hpp +++ b/src/script_interface/interactions/BondedInteraction.hpp @@ -27,7 +27,9 @@ #define SCRIPT_INTERFACE_INTERACTIONS_BONDED_INTERACTION_HPP #include "core/bonded_interactions/bonded_interaction_data.hpp" +#include "core/immersed_boundaries.hpp" #include "core/thermostat.hpp" + #include "script_interface/ScriptInterface.hpp" #include "script_interface/auto_parameters/AutoParameters.hpp" #include "script_interface/get_value.hpp" @@ -35,9 +37,14 @@ #include #include +#include +#include #include #include +#include +#include #include +#include #include #include #include @@ -45,7 +52,7 @@ namespace ScriptInterface { namespace Interactions { -template class BondedInteractionInterface { +class BondedInteraction : public AutoParameters { protected: std::shared_ptr<::Bonded_IA_Parameters> m_bonded_ia; @@ -54,45 +61,93 @@ template class BondedInteractionInterface { std::shared_ptr bonded_ia() const { return m_bonded_ia; } -}; -class BondedInteraction : public AutoParameters, - public BondedInteractionInterface { +protected: + using AutoParameters::context; + using AutoParameters::valid_parameters; + + virtual std::set get_valid_parameters() const { + auto const vec = valid_parameters(); + auto valid_keys = std::set(); + std::transform(vec.begin(), vec.end(), + std::inserter(valid_keys, valid_keys.begin()), + [](auto const &key) { return std::string{key}; }); + return valid_keys; + } + +private: + void check_valid_parameters(VariantMap const ¶ms) const { + auto const valid_keys = get_valid_parameters(); + for (auto const &key : valid_keys) { + if (params.count(std::string(key)) == 0) { + throw std::runtime_error("Parameter '" + key + "' is missing"); + } + } + for (auto const &kv : params) { + if (valid_keys.count(kv.first) == 0) { + throw std::runtime_error("Parameter '" + kv.first + + "' is not recognized"); + } + } + } + void do_construct(VariantMap const ¶ms) override { // Check if initialization "by id" or "by parameters" if (params.find("bond_id") != params.end()) { - m_bonded_ia = ::bonded_ia_params.at(get_value(params, "bond_id")); + auto const bond_id = get_value(params, "bond_id"); + context()->parallel_try_catch([&]() { + if (not::bonded_ia_params.contains(bond_id)) { + throw std::runtime_error("No bond with id " + + std::to_string(bond_id) + + " exists in the ESPResSo core"); + } + }); + m_bonded_ia = ::bonded_ia_params.at(bond_id); } else { - construct_bond(params); + context()->parallel_try_catch([&]() { + check_valid_parameters(params); + construct_bond(params); + }); } } -private: - virtual void construct_bond(VariantMap const ¶ms) {} + virtual void construct_bond(VariantMap const ¶ms) = 0; public: - template - bool operator==(const BondedInteractionInterface &other) { + bool operator==(BondedInteraction const &other) { return m_bonded_ia == other.m_bonded_ia; } Variant do_call_method(std::string const &name, VariantMap const ¶ms) override { // this feature is needed to compare bonds - if (name == "get_address") { - return reinterpret_cast(bonded_ia().get()); + if (name == "is_same_bond") { + auto const bond_so = + get_value>(params, "bond"); + return *this == *bond_so; } if (name == "get_num_partners") { return number_of_partners(*bonded_ia()); } + if (name == "get_zero_based_type") { + auto const bond_id = get_value(params, "bond_id"); + return ::bonded_ia_params.get_zero_based_type(bond_id); + } return {}; } }; -class FeneBond : public BondedInteraction { - using CoreBondedInteraction = ::FeneBond; +template class BondedInteractionImpl : public BondedInteraction { +public: + using CoreBondedInteraction = CoreIA; + CoreBondedInteraction &get_struct() { + return boost::get(*bonded_ia()); + } +}; + +class FeneBond : public BondedInteractionImpl<::FeneBond> { public: FeneBond() { add_parameters({ @@ -110,15 +165,9 @@ class FeneBond : public BondedInteraction { get_value(params, "d_r_max"), get_value(params, "r_0"))); } - - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); - } }; -class HarmonicBond : public BondedInteraction { - using CoreBondedInteraction = ::HarmonicBond; - +class HarmonicBond : public BondedInteractionImpl<::HarmonicBond> { public: HarmonicBond() { add_parameters({ @@ -136,15 +185,9 @@ class HarmonicBond : public BondedInteraction { get_value(params, "k"), get_value(params, "r_0"), get_value(params, "r_cut"))); } - - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); - } }; -class QuarticBond : public BondedInteraction { - using CoreBondedInteraction = ::QuarticBond; - +class QuarticBond : public BondedInteractionImpl<::QuarticBond> { public: QuarticBond() { add_parameters({ @@ -164,15 +207,9 @@ class QuarticBond : public BondedInteraction { get_value(params, "r"), get_value(params, "r_cut"))); } - - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); - } }; -class BondedCoulomb : public BondedInteraction { - using CoreBondedInteraction = ::BondedCoulomb; - +class BondedCoulomb : public BondedInteractionImpl<::BondedCoulomb> { public: BondedCoulomb() { add_parameters({ @@ -181,10 +218,6 @@ class BondedCoulomb : public BondedInteraction { }); } - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); - } - private: void construct_bond(VariantMap const ¶ms) override { m_bonded_ia = std::make_shared<::Bonded_IA_Parameters>( @@ -192,9 +225,7 @@ class BondedCoulomb : public BondedInteraction { } }; -class BondedCoulombSR : public BondedInteraction { - using CoreBondedInteraction = ::BondedCoulombSR; - +class BondedCoulombSR : public BondedInteractionImpl<::BondedCoulombSR> { public: BondedCoulombSR() { add_parameters({ @@ -203,10 +234,6 @@ class BondedCoulombSR : public BondedInteraction { }); } - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); - } - private: void construct_bond(VariantMap const ¶ms) override { m_bonded_ia = std::make_shared<::Bonded_IA_Parameters>( @@ -214,9 +241,7 @@ class BondedCoulombSR : public BondedInteraction { } }; -class AngleHarmonicBond : public BondedInteraction { - using CoreBondedInteraction = ::AngleHarmonicBond; - +class AngleHarmonicBond : public BondedInteractionImpl<::AngleHarmonicBond> { public: AngleHarmonicBond() { add_parameters({ @@ -227,10 +252,6 @@ class AngleHarmonicBond : public BondedInteraction { }); } - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); - } - private: void construct_bond(VariantMap const ¶ms) override { m_bonded_ia = std::make_shared<::Bonded_IA_Parameters>( @@ -239,9 +260,7 @@ class AngleHarmonicBond : public BondedInteraction { } }; -class AngleCosineBond : public BondedInteraction { - using CoreBondedInteraction = ::AngleCosineBond; - +class AngleCosineBond : public BondedInteractionImpl<::AngleCosineBond> { public: AngleCosineBond() { add_parameters({ @@ -252,10 +271,6 @@ class AngleCosineBond : public BondedInteraction { }); } - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); - } - private: void construct_bond(VariantMap const ¶ms) override { m_bonded_ia = std::make_shared<::Bonded_IA_Parameters>( @@ -264,9 +279,7 @@ class AngleCosineBond : public BondedInteraction { } }; -class AngleCossquareBond : public BondedInteraction { - using CoreBondedInteraction = ::AngleCossquareBond; - +class AngleCossquareBond : public BondedInteractionImpl<::AngleCossquareBond> { public: AngleCossquareBond() { add_parameters({ @@ -277,10 +290,6 @@ class AngleCossquareBond : public BondedInteraction { }); } - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); - } - private: void construct_bond(VariantMap const ¶ms) override { m_bonded_ia = std::make_shared<::Bonded_IA_Parameters>( @@ -289,9 +298,7 @@ class AngleCossquareBond : public BondedInteraction { } }; -class DihedralBond : public BondedInteraction { - using CoreBondedInteraction = ::DihedralBond; - +class DihedralBond : public BondedInteractionImpl<::DihedralBond> { public: DihedralBond() { add_parameters({ @@ -304,10 +311,6 @@ class DihedralBond : public BondedInteraction { }); } - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); - } - private: void construct_bond(VariantMap const ¶ms) override { m_bonded_ia = @@ -317,9 +320,8 @@ class DihedralBond : public BondedInteraction { } }; -class TabulatedDistanceBond : public BondedInteraction { - using CoreBondedInteraction = ::TabulatedDistanceBond; - +class TabulatedDistanceBond + : public BondedInteractionImpl<::TabulatedDistanceBond> { public: TabulatedDistanceBond() { add_parameters({ @@ -334,10 +336,6 @@ class TabulatedDistanceBond : public BondedInteraction { }); } - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); - } - private: void construct_bond(VariantMap const ¶ms) override { m_bonded_ia = @@ -348,9 +346,7 @@ class TabulatedDistanceBond : public BondedInteraction { } }; -class TabulatedAngleBond : public BondedInteraction { - using CoreBondedInteraction = ::TabulatedAngleBond; - +class TabulatedAngleBond : public BondedInteractionImpl<::TabulatedAngleBond> { public: TabulatedAngleBond() { add_parameters({ @@ -365,10 +361,6 @@ class TabulatedAngleBond : public BondedInteraction { }); } - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); - } - private: void construct_bond(VariantMap const ¶ms) override { m_bonded_ia = @@ -379,9 +371,8 @@ class TabulatedAngleBond : public BondedInteraction { } }; -class TabulatedDihedralBond : public BondedInteraction { - using CoreBondedInteraction = ::TabulatedDihedralBond; - +class TabulatedDihedralBond + : public BondedInteractionImpl<::TabulatedDihedralBond> { public: TabulatedDihedralBond() { add_parameters({ @@ -396,10 +387,6 @@ class TabulatedDihedralBond : public BondedInteraction { }); } - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); - } - private: void construct_bond(VariantMap const ¶ms) override { m_bonded_ia = @@ -410,9 +397,7 @@ class TabulatedDihedralBond : public BondedInteraction { } }; -class ThermalizedBond : public BondedInteraction { - using CoreBondedInteraction = ::ThermalizedBond; - +class ThermalizedBond : public BondedInteractionImpl<::ThermalizedBond> { public: ThermalizedBond() { add_parameters({ @@ -431,8 +416,15 @@ class ThermalizedBond : public BondedInteraction { }); } - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); + Variant do_call_method(std::string const &name, + VariantMap const ¶ms) override { + if (name == "get_rng_state") { + auto const state = ::thermalized_bond.rng_counter(); + // check it's safe to forward the current state as an integer + assert(state < static_cast(std::numeric_limits::max())); + return static_cast(state); + } + return BondedInteraction::do_call_method(name, params); } private: @@ -443,14 +435,37 @@ class ThermalizedBond : public BondedInteraction { get_value(params, "temp_distance"), get_value(params, "gamma_distance"), get_value(params, "r_cut"))); - thermalized_bond.rng_initialize( - static_cast(get_value(params, "seed"))); + + if (is_none(params.at("seed"))) { + if (::thermalized_bond.is_seed_required()) { + throw std::invalid_argument("A parameter 'seed' has to be given on " + "first activation of a thermalized bond"); + } + } else { + auto const seed = get_value(params, "seed"); + if (seed < 0) { + throw std::domain_error("Parameter 'seed' must be >= 0"); + } + ::thermalized_bond.rng_initialize(static_cast(seed)); + } + + // handle checkpointing + if (params.count("rng_state") and not is_none(params.at("rng_state"))) { + auto const state = get_value(params, "rng_state"); + assert(state >= 0); + ::thermalized_bond.set_rng_counter(static_cast(state)); + } } -}; -class RigidBond : public BondedInteraction { - using CoreBondedInteraction = ::RigidBond; + std::set get_valid_parameters() const override { + auto names = + BondedInteractionImpl::get_valid_parameters(); + names.insert("rng_state"); + return names; + } +}; +class RigidBond : public BondedInteractionImpl<::RigidBond> { public: RigidBond() { add_parameters({ @@ -463,10 +478,6 @@ class RigidBond : public BondedInteraction { }); } - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); - } - private: void construct_bond(VariantMap const ¶ms) override { m_bonded_ia = @@ -476,17 +487,7 @@ class RigidBond : public BondedInteraction { } }; -class IBMTriel : public BondedInteraction { - using CoreBondedInteraction = ::IBMTriel; - -private: - tElasticLaw str2elastic_law(std::string el) { - if (boost::iequals(el, "NeoHookean")) { - return tElasticLaw::NeoHookean; - } - return tElasticLaw::Skalak; - } - +class IBMTriel : public BondedInteractionImpl<::IBMTriel> { public: IBMTriel() { add_parameters({ @@ -504,25 +505,37 @@ class IBMTriel : public BondedInteraction { }); } - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); - } - private: void construct_bond(VariantMap const ¶ms) override { + auto const law_name = get_value(params, "elasticLaw"); + tElasticLaw elastic_law; + if (law_name == "NeoHookean") { + elastic_law = tElasticLaw::NeoHookean; + } else if (law_name == "Skalak") { + elastic_law = tElasticLaw::Skalak; + } else { + throw std::invalid_argument( + "Invalid value for parameter 'elasticLaw': '" + law_name + "'"); + } m_bonded_ia = std::make_shared<::Bonded_IA_Parameters>(CoreBondedInteraction( get_value(params, "ind1"), get_value(params, "ind2"), get_value(params, "ind3"), - get_value(params, "maxDist"), - str2elastic_law(get_value(params, "elasticLaw")), + get_value(params, "maxDist"), elastic_law, get_value(params, "k1"), get_value(params, "k2"))); } -}; -class IBMVolCons : public BondedInteraction { - using CoreBondedInteraction = ::IBMVolCons; + std::set get_valid_parameters() const override { + auto names = + BondedInteractionImpl::get_valid_parameters(); + names.insert("ind1"); + names.insert("ind2"); + names.insert("ind3"); + return names; + } +}; +class IBMVolCons : public BondedInteractionImpl<::IBMVolCons> { public: IBMVolCons() { add_parameters({ @@ -533,8 +546,12 @@ class IBMVolCons : public BondedInteraction { }); } - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); + Variant do_call_method(std::string const &name, + VariantMap const ¶ms) override { + if (name == "current_volume") { + return ::immersed_boundaries.get_current_volume(get_struct().softID); + } + return BondedInteraction::do_call_method(name, params); } private: @@ -545,9 +562,7 @@ class IBMVolCons : public BondedInteraction { } }; -class IBMTribend : public BondedInteraction { - using CoreBondedInteraction = ::IBMTribend; - +class IBMTribend : public BondedInteractionImpl<::IBMTribend> { public: IBMTribend() { add_parameters({ @@ -561,26 +576,39 @@ class IBMTribend : public BondedInteraction { }); } - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); - } - private: bool m_flat; void construct_bond(VariantMap const ¶ms) override { - auto const &refShape = get_value(params, "refShape"); - m_flat = boost::iequals(refShape, "Flat"); + auto const shape_name = get_value(params, "refShape"); + if (shape_name == "Flat") { + m_flat = true; + } else if (shape_name == "Initial") { + m_flat = false; + } else { + throw std::invalid_argument("Invalid value for parameter 'refShape': '" + + shape_name + "'"); + } m_bonded_ia = std::make_shared<::Bonded_IA_Parameters>(CoreBondedInteraction( get_value(params, "ind1"), get_value(params, "ind2"), get_value(params, "ind3"), get_value(params, "ind4"), get_value(params, "kb"), m_flat)); } -}; -class OifGlobalForcesBond : public BondedInteraction { - using CoreBondedInteraction = ::OifGlobalForcesBond; + std::set get_valid_parameters() const override { + auto names = + BondedInteractionImpl::get_valid_parameters(); + names.erase("theta0"); + names.insert("ind1"); + names.insert("ind2"); + names.insert("ind3"); + names.insert("ind4"); + return names; + } +}; +class OifGlobalForcesBond + : public BondedInteractionImpl<::OifGlobalForcesBond> { public: OifGlobalForcesBond() { add_parameters({ @@ -593,10 +621,6 @@ class OifGlobalForcesBond : public BondedInteraction { }); } - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); - } - private: void construct_bond(VariantMap const ¶ms) override { m_bonded_ia = @@ -607,9 +631,7 @@ class OifGlobalForcesBond : public BondedInteraction { } }; -class OifLocalForcesBond : public BondedInteraction { - using CoreBondedInteraction = ::OifLocalForcesBond; - +class OifLocalForcesBond : public BondedInteractionImpl<::OifLocalForcesBond> { public: OifLocalForcesBond() { add_parameters({ @@ -631,10 +653,6 @@ class OifLocalForcesBond : public BondedInteraction { }); } - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); - } - private: void construct_bond(VariantMap const ¶ms) override { m_bonded_ia = @@ -648,19 +666,15 @@ class OifLocalForcesBond : public BondedInteraction { } }; -class VirtualBond : public BondedInteraction { - using CoreBondedInteraction = ::VirtualBond; - +class VirtualBond : public BondedInteractionImpl<::VirtualBond> { public: VirtualBond() { m_bonded_ia = std::make_shared<::Bonded_IA_Parameters>(CoreBondedInteraction()); } -public: - CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); - } +private: + void construct_bond(VariantMap const &) override {} }; } // namespace Interactions diff --git a/src/script_interface/interactions/BondedInteractions.hpp b/src/script_interface/interactions/BondedInteractions.hpp index 047c13aaa7d..fe92ef1e196 100644 --- a/src/script_interface/interactions/BondedInteractions.hpp +++ b/src/script_interface/interactions/BondedInteractions.hpp @@ -96,6 +96,11 @@ class BondedInteractions : public ObjectMap { return {m_bonds.at(bond_id)}; } + if (name == "get_zero_based_type") { + auto const bond_id = get_value(params, "bond_id"); + return ::bonded_ia_params.get_zero_based_type(bond_id); + } + return ObjectMap::do_call_method(name, params); } diff --git a/src/script_interface/interactions/bonded.hpp b/src/script_interface/interactions/bonded.hpp deleted file mode 100644 index 6370468965b..00000000000 --- a/src/script_interface/interactions/bonded.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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_INTERACTIONS_BONDED_HPP -#define SCRIPT_INTERFACE_INTERACTIONS_BONDED_HPP - -/** @file - * Functions to interface with the core boost::variant. - */ - -#include "core/bonded_interactions/bonded_interaction_data.hpp" - -#include - -/** Return the 0-based type number of the specified bond. */ -inline int bonded_ia_params_zero_based_type(int bond_id) { - if (bonded_ia_params.contains(bond_id)) { - return (*bonded_ia_params.at(bond_id)).which(); - } - return 0; -} - -#endif diff --git a/src/script_interface/particle_data/ParticleHandle.cpp b/src/script_interface/particle_data/ParticleHandle.cpp index f327b52e63e..aa6c8843991 100644 --- a/src/script_interface/particle_data/ParticleHandle.cpp +++ b/src/script_interface/particle_data/ParticleHandle.cpp @@ -22,8 +22,8 @@ #include "ParticleHandle.hpp" #include "script_interface/get_value.hpp" -#include "script_interface/interactions/bonded.hpp" +#include "core/bonded_interactions/bonded_interaction_data.hpp" #include "core/grid.hpp" #include "core/particle_data.hpp" #include "core/particle_node.hpp" @@ -399,7 +399,7 @@ Variant ParticleHandle::do_call_method(std::string const &name, delete_particle_bonds(m_pid); } else if (name == "is_valid_bond_id") { auto const bond_id = get_value(params, "bond_id"); - return bonded_ia_params_zero_based_type(bond_id) != 0; + return ::bonded_ia_params.get_zero_based_type(bond_id) != 0; } if (name == "remove_particle") { remove_particle(m_pid); diff --git a/testsuite/python/interactions_bonded_interface.py b/testsuite/python/interactions_bonded_interface.py index 240b9d551cc..e8c27588611 100644 --- a/testsuite/python/interactions_bonded_interface.py +++ b/testsuite/python/interactions_bonded_interface.py @@ -44,47 +44,6 @@ def bondsMatch(self, inType, outBond, inParams, outParams, msg_long): self.assertEqual(outBond, inType, msg="Bonded interaction mismatch") tests_common.assert_params_match(self, inParams, outParams, msg_long) - def parameterKeys(self, bondObject): - """ - Check :meth:`~espressomd.interactions.BondedInteraction.valid_keys` - and :meth:`~espressomd.interactions.BondedInteraction.required_keys` - return sets, and that - :meth:`~espressomd.interactions.BondedInteraction.get_default_params` - returns a dictionary with the correct keys. - - Parameters - ---------- - bondObject: instance of a class derived from :class:`espressomd.interactions.BondedInteraction` - Object of the interaction to test, e.g. - :class:`~espressomd.interactions.FeneBond` - """ - classname = bondObject.__class__.__name__ - valid_keys = bondObject.valid_keys() - required_keys = bondObject.required_keys() - old_params = bondObject.params.copy() - default_keys = set(bondObject.get_default_params()) - self.assertIsInstance(valid_keys, set, - f"{classname}.valid_keys() must return a set") - self.assertIsInstance(required_keys, set, - f"{classname}.required_keys() must return a set") - self.assertTrue(default_keys.issubset(valid_keys), - f"{classname}.get_default_params() has unknown " - f"parameters: {default_keys.difference(valid_keys)}") - self.assertTrue(default_keys.isdisjoint(required_keys), - f"{classname}.get_default_params() has extra " - f"parameters: {default_keys.intersection(required_keys)}") - self.assertSetEqual(default_keys, valid_keys - required_keys, - f"{classname}.get_default_params() should have keys: " - f"{valid_keys - required_keys}, got: {default_keys}") - with self.assertRaisesRegex(RuntimeError, "Bond parameters are immutable"): - bondObject.params = {} - for key in (bondObject.params.keys() | old_params.keys()): - if isinstance(old_params[key], str): - self.assertEqual(bondObject.params[key], old_params[key]) - else: - np.testing.assert_allclose( - bondObject.params[key], old_params[key], atol=1e-10) - def generateTestForBondParams(_bondId, _bondClass, _params, _refs=None): """Generates test cases for checking bond parameters set and gotten back from the espresso core actually match those in the Python classes. @@ -145,14 +104,22 @@ def func(self): # check that parameters written and read back are identical outBond = self.system.bonded_inter[bondId] - tnIn = bondClass(**params).type_number() - tnOut = outBond.type_number() + tnIn = bondClass._type_number + tnOut = outBond._type_number outParams = outBond.params self.bondsMatch( tnIn, tnOut, outParamsRef, outParams, - "{}: value set and value gotten back differ for bond id {}: {} vs. {}" - .format(bondClass(**params).type_name(), bondId, outParamsRef, outParams)) - self.parameterKeys(outBond) + f"{bondClass.__name__}: value set and value gotten back " + f"differ for bond id {bondId}: {outParamsRef} vs. {outParams}") + with self.assertRaisesRegex(RuntimeError, "Bond parameters are immutable"): + outBond.params = {} + old_params = outBond.params.copy() + for key in (outBond.params.keys() | old_params.keys()): + if isinstance(old_params[key], str): + self.assertEqual(outBond.params[key], old_params[key]) + else: + np.testing.assert_allclose( + outBond.params[key], old_params[key], atol=1e-10) # check no-op self.assertIsNone(outBond.call_method('unknown')) @@ -176,6 +143,11 @@ def func(self): {"ind1": 0, "ind2": 1, "ind3": 2, "ind4": 3, "kb": 1.1, "refShape": "Initial"}, {"kb": 1.1, "theta0": 0.0}) + test_ibm_tribend_flat = generateTestForBondParams( + 0, espressomd.interactions.IBM_Tribend, + {"ind1": 0, "ind2": 1, "ind3": 2, "ind4": 3, + "kb": 1.1, "refShape": "Flat"}, + {"kb": 1.1, "theta0": 0.0}) test_ibm_triel = generateTestForBondParams( 0, espressomd.interactions.IBM_Triel, {"ind1": 0, "ind2": 1, "ind3": 2, "k1": 1.1, "k2": 1.2, @@ -231,8 +203,8 @@ def test_bonded_coulomb_sr(self): def test_exceptions(self): error_msg_not_yet_defined = 'The bond with id 0 is not yet defined' - bond_type = espressomd.interactions.get_bonded_interaction_type_from_es_core( - 5000) + bond_type = self.system.bonded_inter.call_method( + 'get_zero_based_type', bond_id=5000) self.assertEqual(bond_type, 0) has_bond = self.system.bonded_inter.call_method('has_bond', bond_id=0) self.assertFalse(has_bond) @@ -252,6 +224,19 @@ def test_exceptions(self): self.system.bonded_inter[0] = fene_bond with self.assertRaisesRegex(ValueError, 'Bonds can only be overwritten by bonds of equal type'): self.system.bonded_inter[0] = angle_bond + with self.assertRaisesRegex(RuntimeError, "No bond with id 8 exists in the ESPResSo core"): + espressomd.interactions.FeneBond(bond_id=8) + with self.assertRaisesRegex(RuntimeError, "The bond with id 0 is not defined as a FENE bond in the ESPResSo core"): + espressomd.interactions.FeneBond(bond_id=0) + + # bonds can only be compared for equality + self.assertEqual(angle_bond, angle_bond) + self.assertNotEqual(harm_bond1, harm_bond2) + self.assertNotEqual(angle_bond, fene_bond) + with self.assertRaises(NotImplementedError): + angle_bond > fene_bond + with self.assertRaises(NotImplementedError): + angle_bond <= fene_bond # bonds are immutable with self.assertRaisesRegex(RuntimeError, "Parameter 'r_0' is read-only"): @@ -260,16 +245,22 @@ 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'\]\)"): + with self.assertRaisesRegex(RuntimeError, f"Parameter 'rcut' is not recognized"): espressomd.interactions.HarmonicBond(k=1., r_0=1., rcut=2.) - with self.assertRaisesRegex(ValueError, "Unknown refShape: 'Unknown'"): + with self.assertRaisesRegex(ValueError, "Invalid value for parameter 'refShape': 'Unknown'"): espressomd.interactions.IBM_Tribend( ind1=0, ind2=1, ind3=2, ind4=3, kb=1.1, refShape='Unknown') - with self.assertRaisesRegex(ValueError, "Unknown elasticLaw: 'Unknown'"): + with self.assertRaisesRegex(ValueError, "Invalid value for parameter 'elasticLaw': 'Unknown'"): espressomd.interactions.IBM_Triel( ind1=0, ind2=1, ind3=2, k1=1.1, k2=1.2, maxDist=1.6, elasticLaw='Unknown') + with self.assertRaisesRegex(ValueError, "A parameter 'seed' has to be given on first activation of a thermalized bond"): + espressomd.interactions.ThermalizedBond( + temp_com=1., gamma_com=1., temp_distance=1., gamma_distance=1., + r_cut=2.) + with self.assertRaisesRegex(ValueError, "Parameter 'seed' must be >= 0"): + espressomd.interactions.ThermalizedBond( + temp_com=1., gamma_com=1., temp_distance=1., gamma_distance=1., + r_cut=2., seed=-1) # sanity checks when removing bonds self.system.bonded_inter.clear() diff --git a/testsuite/python/save_checkpoint.py b/testsuite/python/save_checkpoint.py index c6710cac1c1..0f9986ca0c9 100644 --- a/testsuite/python/save_checkpoint.py +++ b/testsuite/python/save_checkpoint.py @@ -248,14 +248,22 @@ system.bonded_inter.add(harmonic_bond) p2.add_bond((harmonic_bond, p1)) if 'THERM.LB' not in modes: - thermalized_bond = espressomd.interactions.ThermalizedBond( - temp_com=0.0, gamma_com=0.0, temp_distance=0.2, gamma_distance=0.5, - r_cut=2, seed=51) - system.bonded_inter.add(thermalized_bond) - p2.add_bond((thermalized_bond, p1)) + # create 3 thermalized bonds that will overwrite each other's seed + thermalized_bond_params = dict(temp_com=0.1, temp_distance=0.2, + gamma_com=0.3, gamma_distance=0.5, r_cut=2.) + thermalized_bond1 = espressomd.interactions.ThermalizedBond( + seed=1, **thermalized_bond_params) + thermalized_bond2 = espressomd.interactions.ThermalizedBond( + seed=2, **thermalized_bond_params) + thermalized_bond3 = espressomd.interactions.ThermalizedBond( + seed=3, **thermalized_bond_params) + system.bonded_inter.add(thermalized_bond1) + p2.add_bond((thermalized_bond1, p1)) + checkpoint.register("thermalized_bond2") + checkpoint.register("thermalized_bond_params") if espressomd.has_features(['ELECTROSTATICS', 'MASS', 'ROTATION']): dh = espressomd.drude_helpers.DrudeHelpers() - dh.add_drude_particle_to_core(system, harmonic_bond, thermalized_bond, + dh.add_drude_particle_to_core(system, harmonic_bond, thermalized_bond1, p2, 10, 1., 4.6, 0.8, 2.) checkpoint.register("dh") strong_harmonic_bond = espressomd.interactions.HarmonicBond(r_0=0.0, k=5e5) diff --git a/testsuite/python/test_checkpoint.py b/testsuite/python/test_checkpoint.py index 2d5b874722b..fe0a97f580a 100644 --- a/testsuite/python/test_checkpoint.py +++ b/testsuite/python/test_checkpoint.py @@ -355,18 +355,15 @@ def test_bonded_inter(self): self.assertEqual(len(bond_ids), len(system.bonded_inter)) # check bonded interactions partcl_1 = system.part.by_id(1) - state = partcl_1.bonds[0][0].params reference = {'r_0': 0.0, 'k': 1.0, 'r_cut': 0.0} - self.assertEqual(state, reference) - state = partcl_1.bonds[0][0].params - self.assertEqual(state, reference) + self.assertEqual(partcl_1.bonds[0][0].params, reference) + self.assertEqual(system.bonded_inter[0].params, reference) if 'THERM.LB' not in modes: - state = partcl_1.bonds[1][0].params - reference = {'temp_com': 0., 'gamma_com': 0., 'temp_distance': 0.2, - 'gamma_distance': 0.5, 'r_cut': 2.0, 'seed': 51} - self.assertEqual(state, reference) - state = partcl_1.bonds[1][0].params - self.assertEqual(state, reference) + # all thermalized bonds should be identical + reference = {**thermalized_bond_params, 'seed': 3} + self.assertEqual(partcl_1.bonds[1][0].params, reference) + self.assertEqual(system.bonded_inter[1].params, reference) + self.assertEqual(thermalized_bond2.params, reference) # immersed boundary bonds self.assertEqual( ibm_volcons_bond.params, {'softID': 15, 'kappaV': 0.01}) From bde47e88dbcb6cabedf2171b311315ee06327ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 13 Sep 2022 18:07:20 +0200 Subject: [PATCH 73/85] core: Remove MPI dependency from OIF code --- src/core/forces.cpp | 8 +++-- .../object-in-fluid/oif_global_forces.cpp | 33 ++++--------------- .../object-in-fluid/oif_global_forces.hpp | 1 - 3 files changed, 13 insertions(+), 29 deletions(-) diff --git a/src/core/forces.cpp b/src/core/forces.cpp index dfbb17019ed..2c4b2ef85ee 100644 --- a/src/core/forces.cpp +++ b/src/core/forces.cpp @@ -211,9 +211,13 @@ void force_calc(CellStructure &cell_structure, double time_step, double kT) { // There are two global quantities that need to be evaluated: // object's surface and object's volume. for (int i = 0; i < max_oif_objects; i++) { - auto const area_volume = calc_oif_global(i, cell_structure); - if (fabs(area_volume[0]) < 1e-100 && fabs(area_volume[1]) < 1e-100) + auto const area_volume = boost::mpi::all_reduce( + comm_cart, calc_oif_global(i, cell_structure), std::plus<>()); + auto const oif_part_area = std::abs(area_volume[0]); + auto const oif_part_vol = std::abs(area_volume[1]); + if (oif_part_area < 1e-100 and oif_part_vol < 1e-100) { break; + } add_oif_global_forces(area_volume, i, cell_structure); } } diff --git a/src/core/object-in-fluid/oif_global_forces.cpp b/src/core/object-in-fluid/oif_global_forces.cpp index ef09ccedfc9..6cdc35d3a13 100644 --- a/src/core/object-in-fluid/oif_global_forces.cpp +++ b/src/core/object-in-fluid/oif_global_forces.cpp @@ -22,7 +22,6 @@ #include "BoxGeometry.hpp" #include "Particle.hpp" #include "cell_system/CellStructure.hpp" -#include "communication.hpp" #include "grid.hpp" #include "bonded_interactions/bonded_interaction_data.hpp" @@ -32,12 +31,7 @@ #include #include -#include - -#include - -using Utils::area_triangle; -using Utils::get_n_triangle; +int max_oif_objects = 0; Utils::Vector2d calc_oif_global(int molType, CellStructure &cs) { // first-fold-then-the-same approach @@ -60,20 +54,19 @@ Utils::Vector2d calc_oif_global(int molType, CellStructure &cs) { auto const p33 = p11 + box_geo.get_mi_vector(partners[1]->pos(), p11); // unfolded positions correct - auto const VOL_A = area_triangle(p11, p22, p33); + auto const VOL_A = Utils::area_triangle(p11, p22, p33); partArea += VOL_A; - auto const VOL_norm = get_n_triangle(p11, p22, p33); + auto const VOL_norm = Utils::get_n_triangle(p11, p22, p33); auto const VOL_dn = VOL_norm.norm(); auto const VOL_hz = 1.0 / 3.0 * (p11[2] + p22[2] + p33[2]); - VOL_partVol += VOL_A * -1 * VOL_norm[2] / VOL_dn * VOL_hz; + VOL_partVol -= VOL_A * VOL_norm[2] / VOL_dn * VOL_hz; } return false; }); - auto const area_volume_local = Utils::Vector2d{{partArea, VOL_partVol}}; - return boost::mpi::all_reduce(comm_cart, area_volume_local, std::plus<>()); + return {{partArea, VOL_partVol}}; } void add_oif_global_forces(Utils::Vector2d const &area_volume, int molType, @@ -96,8 +89,8 @@ void add_oif_global_forces(Utils::Vector2d const &area_volume, int molType, // unfolded positions correct // starting code from volume force - auto const VOL_norm = get_n_triangle(p11, p22, p33).normalize(); - auto const VOL_A = area_triangle(p11, p22, p33); + auto const VOL_norm = Utils::get_n_triangle(p11, p22, p33).normalize(); + auto const VOL_A = Utils::area_triangle(p11, p22, p33); auto const VOL_vv = (VOL_volume - iaparams->V0) / iaparams->V0; auto const VOL_force = @@ -127,15 +120,3 @@ void add_oif_global_forces(Utils::Vector2d const &area_volume, int molType, return false; }); } - -int max_oif_objects = 0; - -void mpi_set_max_oif_objects_local(int max_oif_objects) { - ::max_oif_objects = max_oif_objects; -} - -REGISTER_CALLBACK(mpi_set_max_oif_objects_local) - -void mpi_set_max_oif_objects(int max_oif_objects) { - mpi_call_all(mpi_set_max_oif_objects_local, max_oif_objects); -} diff --git a/src/core/object-in-fluid/oif_global_forces.hpp b/src/core/object-in-fluid/oif_global_forces.hpp index 685c457b4be..a0bd8924350 100644 --- a/src/core/object-in-fluid/oif_global_forces.hpp +++ b/src/core/object-in-fluid/oif_global_forces.hpp @@ -43,5 +43,4 @@ void add_oif_global_forces(Utils::Vector2d const &area_volume, int molType, extern int max_oif_objects; -void mpi_set_max_oif_objects(int max_oif_objects); #endif From 67eeeadf0341d7c8fb895809f2832ca84b2144a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 14 Sep 2022 22:58:01 +0200 Subject: [PATCH 74/85] script_interface: Make MPI communicator public The MPI communicator is now accessible through the object context. --- src/script_interface/Context.hpp | 7 ++++ src/script_interface/GlobalContext.hpp | 5 ++- src/script_interface/LocalContext.hpp | 3 ++ src/script_interface/ObjectList.hpp | 3 +- src/script_interface/ObjectMap.hpp | 3 +- .../ParallelExceptionHandler.cpp | 2 - .../cell_system/CellSystem.cpp | 10 ++--- .../object_container_mpi_guard.cpp | 7 ++-- .../object_container_mpi_guard.hpp | 4 +- .../tests/ObjectHandle_test.cpp | 14 ++++++- .../tests/serialization_mpi_guard_test.cpp | 42 ++++++++++++------- 11 files changed, 69 insertions(+), 31 deletions(-) diff --git a/src/script_interface/Context.hpp b/src/script_interface/Context.hpp index 5aace50f5a3..6a979517c1c 100644 --- a/src/script_interface/Context.hpp +++ b/src/script_interface/Context.hpp @@ -35,6 +35,12 @@ #include #include +namespace boost { +namespace mpi { +class communicator; +} // namespace mpi +} // namespace boost + namespace ScriptInterface { /** * @brief Context of an object handle. @@ -99,6 +105,7 @@ class Context : public std::enable_shared_from_this { virtual bool is_head_node() const = 0; virtual void parallel_try_catch(std::function const &cb) const = 0; + virtual boost::mpi::communicator const &get_comm() const = 0; virtual ~Context() = default; }; diff --git a/src/script_interface/GlobalContext.hpp b/src/script_interface/GlobalContext.hpp index d4de6ebbc02..03f2d0dc329 100644 --- a/src/script_interface/GlobalContext.hpp +++ b/src/script_interface/GlobalContext.hpp @@ -37,6 +37,7 @@ #include +#include #include #include @@ -71,6 +72,7 @@ class GlobalContext : public Context { std::shared_ptr m_node_local_context; bool m_is_head_node; + boost::mpi::communicator const &m_comm; ParallelExceptionHandler m_parallel_exception_handler; @@ -89,7 +91,7 @@ class GlobalContext : public Context { GlobalContext(Communication::MpiCallbacks &callbacks, std::shared_ptr node_local_context) : m_local_objects(), m_node_local_context(std::move(node_local_context)), - m_is_head_node(callbacks.comm().rank() == 0), + m_is_head_node(callbacks.comm().rank() == 0), m_comm(callbacks.comm()), // NOLINTNEXTLINE(bugprone-throw-keyword-missing) m_parallel_exception_handler(callbacks.comm()), cb_make_handle(&callbacks, @@ -170,6 +172,7 @@ class GlobalContext : public Context { void parallel_try_catch(std::function const &cb) const override { m_parallel_exception_handler.parallel_try_catch(cb); } + boost::mpi::communicator const &get_comm() const override { return m_comm; } }; } // namespace ScriptInterface diff --git a/src/script_interface/LocalContext.hpp b/src/script_interface/LocalContext.hpp index dcb81be5edc..11a79a7a2fb 100644 --- a/src/script_interface/LocalContext.hpp +++ b/src/script_interface/LocalContext.hpp @@ -43,12 +43,14 @@ namespace ScriptInterface { class LocalContext : public Context { Utils::Factory m_factory; bool m_is_head_node; + boost::mpi::communicator const &m_comm; ParallelExceptionHandler m_parallel_exception_handler; public: LocalContext(Utils::Factory factory, boost::mpi::communicator const &comm) : m_factory(std::move(factory)), m_is_head_node(comm.rank() == 0), + m_comm(comm), // NOLINTNEXTLINE(bugprone-throw-keyword-missing) m_parallel_exception_handler(comm) {} @@ -79,6 +81,7 @@ class LocalContext : public Context { void parallel_try_catch(std::function const &cb) const override { m_parallel_exception_handler.parallel_try_catch(cb); } + boost::mpi::communicator const &get_comm() const override { return m_comm; } }; } // namespace ScriptInterface diff --git a/src/script_interface/ObjectList.hpp b/src/script_interface/ObjectList.hpp index e43506afd15..3bcad1caa57 100644 --- a/src/script_interface/ObjectList.hpp +++ b/src/script_interface/ObjectList.hpp @@ -133,7 +133,8 @@ class ObjectList : public BaseType { private: std::string get_internal_state() const override { - object_container_mpi_guard(BaseType::name(), m_elements.size()); + object_container_mpi_guard(BaseType::name(), m_elements.size(), + BaseType::context()->get_comm().size()); std::vector object_states(m_elements.size()); diff --git a/src/script_interface/ObjectMap.hpp b/src/script_interface/ObjectMap.hpp index 2556a4e198e..cfd222f6241 100644 --- a/src/script_interface/ObjectMap.hpp +++ b/src/script_interface/ObjectMap.hpp @@ -164,7 +164,8 @@ class ObjectMap : public BaseType { private: std::string get_internal_state() const override { - object_container_mpi_guard(BaseType::name(), m_elements.size()); + object_container_mpi_guard(BaseType::name(), m_elements.size(), + BaseType::context()->get_comm().size()); using packed_type = std::pair; std::vector object_states(m_elements.size()); diff --git a/src/script_interface/ParallelExceptionHandler.cpp b/src/script_interface/ParallelExceptionHandler.cpp index e70feff9da4..b694cccea6f 100644 --- a/src/script_interface/ParallelExceptionHandler.cpp +++ b/src/script_interface/ParallelExceptionHandler.cpp @@ -21,8 +21,6 @@ #include "Exception.hpp" -#include "core/MpiCallbacks.hpp" -#include "core/communication.hpp" #include "core/error_handling/RuntimeError.hpp" #include "core/errorhandling.hpp" diff --git a/src/script_interface/cell_system/CellSystem.cpp b/src/script_interface/cell_system/CellSystem.cpp index 553af6321cb..688da7bc598 100644 --- a/src/script_interface/cell_system/CellSystem.cpp +++ b/src/script_interface/cell_system/CellSystem.cpp @@ -25,7 +25,6 @@ #include "core/cell_system/HybridDecomposition.hpp" #include "core/cell_system/RegularDecomposition.hpp" #include "core/cells.hpp" -#include "core/communication.hpp" #include "core/event.hpp" #include "core/grid.hpp" #include "core/integrate.hpp" @@ -159,7 +158,7 @@ Variant CellSystem::do_call_method(std::string const &name, {"n_square", hd.count_particles_in_n_square()}}}; } state["verlet_reuse"] = get_verlet_reuse(); - state["n_nodes"] = ::n_nodes; + state["n_nodes"] = context()->get_comm().size(); return state; } if (name == "get_pairs") { @@ -187,7 +186,7 @@ Variant CellSystem::do_call_method(std::string const &name, } if (name == "get_neighbors") { std::vector> neighbors_global; - context()->parallel_try_catch([&neighbors_global, ¶ms]() { + context()->parallel_try_catch([this, &neighbors_global, ¶ms]() { auto const dist = get_value(params, "distance"); auto const pid = get_value(params, "pid"); auto const ret = mpi_get_short_range_neighbors_local(pid, dist, true); @@ -195,7 +194,8 @@ Variant CellSystem::do_call_method(std::string const &name, if (ret) { neighbors_local = *ret; } - boost::mpi::gather(::comm_cart, neighbors_local, neighbors_global, 0); + boost::mpi::gather(context()->get_comm(), neighbors_local, + neighbors_global, 0); }); std::vector neighbors; for (auto const &neighbors_local : neighbors_global) { @@ -235,7 +235,7 @@ std::vector CellSystem::mpi_resort_particles(bool global_flag) const { } auto const size = static_cast(::cell_structure.local_particles().size()); std::vector n_part_per_node; - boost::mpi::gather(::comm_cart, size, n_part_per_node, 0); + boost::mpi::gather(context()->get_comm(), size, n_part_per_node, 0); return n_part_per_node; } diff --git a/src/script_interface/object_container_mpi_guard.cpp b/src/script_interface/object_container_mpi_guard.cpp index 4f1e680e280..da62287b3f2 100644 --- a/src/script_interface/object_container_mpi_guard.cpp +++ b/src/script_interface/object_container_mpi_guard.cpp @@ -19,8 +19,7 @@ #include "script_interface/object_container_mpi_guard.hpp" -#include "core/communication.hpp" - +#include #include #include @@ -28,8 +27,8 @@ #include void object_container_mpi_guard(boost::string_ref const &name, - std::size_t n_elements) { - if (comm_cart.size() > 1 and n_elements) { + std::size_t n_elements, int world_size) { + if (world_size > 1 and n_elements) { std::stringstream error_msg; error_msg << "Non-empty object containers do not support checkpointing in " << "MPI environments. Container " << name << " contains " diff --git a/src/script_interface/object_container_mpi_guard.hpp b/src/script_interface/object_container_mpi_guard.hpp index 90eededb143..bb9723c5e6f 100644 --- a/src/script_interface/object_container_mpi_guard.hpp +++ b/src/script_interface/object_container_mpi_guard.hpp @@ -19,6 +19,7 @@ #ifndef ESPRESSO_OBJECT_CONTAINER_MPI_GUARD_HPP #define ESPRESSO_OBJECT_CONTAINER_MPI_GUARD_HPP +#include #include #include @@ -39,8 +40,9 @@ * * @param name Name of the object container * @param n_elements Number of elements in the container + * @param world_size MPI world size */ void object_container_mpi_guard(boost::string_ref const &name, - std::size_t n_elements); + std::size_t n_elements, int world_size); #endif diff --git a/src/script_interface/tests/ObjectHandle_test.cpp b/src/script_interface/tests/ObjectHandle_test.cpp index ed24a0ec4a2..5ea001e084a 100644 --- a/src/script_interface/tests/ObjectHandle_test.cpp +++ b/src/script_interface/tests/ObjectHandle_test.cpp @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -165,6 +166,12 @@ BOOST_AUTO_TEST_CASE(do_call_method_) { MockCall::CallMethod{&name, ¶ms})); } +namespace boost { +namespace mpi { +class communicator {}; +} // namespace mpi +} // namespace boost + namespace Testing { /** * Logging mock for Context. @@ -195,6 +202,10 @@ struct LogContext : public Context { bool is_head_node() const override { return true; } void parallel_try_catch(std::function const &) const override {} + boost::mpi::communicator const &get_comm() const override { return m_comm; } + +private: + boost::mpi::communicator m_comm; }; } // namespace Testing @@ -249,5 +260,6 @@ BOOST_AUTO_TEST_CASE(interface_) { auto o = log_ctx->make_shared({}, {}); BOOST_CHECK(log_ctx->is_head_node()); BOOST_CHECK_EQUAL(log_ctx->name(o.get()), "Dummy"); - static_cast(log_ctx->parallel_try_catch([]() {})); + log_ctx->parallel_try_catch([]() {}); + std::ignore = log_ctx->get_comm(); } diff --git a/src/script_interface/tests/serialization_mpi_guard_test.cpp b/src/script_interface/tests/serialization_mpi_guard_test.cpp index 88fea91e851..72749046f97 100644 --- a/src/script_interface/tests/serialization_mpi_guard_test.cpp +++ b/src/script_interface/tests/serialization_mpi_guard_test.cpp @@ -22,6 +22,7 @@ #define BOOST_TEST_DYN_LINK #include +#include "script_interface/GlobalContext.hpp" #include "script_interface/ObjectList.hpp" #include @@ -51,24 +52,35 @@ struct ObjectContainer : ScriptInterface::ObjectList { 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; - }; + Utils::Factory factory; + factory.register_new("ObjectContainer"); + Communication::MpiCallbacks cb{world}; + auto ctx = std::make_shared( + cb, std::make_shared(factory, world)); - Testing::ObjectContainer list; - BOOST_CHECK_NO_THROW(list.serialize()); + if (world.rank() == 0) { + 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 ObjectContainer contains 1 elements."; + return ex.what() == message; + }; - list.add(obj_ptr); - if (world.size() > 1) { - BOOST_CHECK_EXCEPTION(list.serialize(), std::runtime_error, predicate); - } + auto list_so = ctx->make_shared("ObjectContainer", {}); + auto &list = dynamic_cast(*list_so); + 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()); + list.remove(obj_ptr); + BOOST_CHECK_NO_THROW(list.serialize()); + } else { + cb.loop(); + } } int main(int argc, char **argv) { From 59b5bea019a956b34e2f14b863596c5bd18cfbeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 16 Sep 2022 14:51:17 +0200 Subject: [PATCH 75/85] script_interface: Make main analysis methods parallel --- src/core/analysis/statistics.cpp | 29 ++++--------- src/core/energy.cpp | 30 ++++--------- src/core/energy.hpp | 6 +-- src/core/grid_based_algorithms/lb.cpp | 13 +++--- src/core/grid_based_algorithms/lb.hpp | 7 ++-- .../grid_based_algorithms/lb_interface.cpp | 17 +++----- src/core/observables/EnergyObservable.hpp | 2 +- src/core/observables/PressureObservable.hpp | 2 +- src/core/observables/PressureTensor.hpp | 2 +- src/core/pressure.cpp | 15 +++---- src/core/pressure.hpp | 2 +- .../reaction_methods/ReactionAlgorithm.cpp | 8 ++-- src/core/reaction_methods/WidomInsertion.cpp | 4 +- .../EspressoSystemStandAlone_test.cpp | 14 ++++--- src/python/espressomd/analyze.py | 4 +- src/script_interface/analysis/Analysis.cpp | 26 +++++++----- src/script_interface/communication.hpp | 42 +++++++++++++++++++ 17 files changed, 123 insertions(+), 100 deletions(-) create mode 100644 src/script_interface/communication.hpp diff --git a/src/core/analysis/statistics.cpp b/src/core/analysis/statistics.cpp index 3db0b719511..9ad044ca7f7 100644 --- a/src/core/analysis/statistics.cpp +++ b/src/core/analysis/statistics.cpp @@ -73,32 +73,21 @@ double mindist(PartCfg &partCfg, std::vector const &set1, return std::sqrt(mindist_sq); } -static Utils::Vector3d mpi_particle_momentum_local() { - auto const particles = cell_structure.local_particles(); - auto const momentum = - std::accumulate(particles.begin(), particles.end(), Utils::Vector3d{}, - [](Utils::Vector3d &m, Particle const &p) { - return m + p.mass() * p.v(); - }); - - return momentum; -} - -REGISTER_CALLBACK_REDUCTION(mpi_particle_momentum_local, - std::plus()) - Utils::Vector3d calc_linear_momentum(bool include_particles, bool include_lbfluid) { - Utils::Vector3d linear_momentum{}; + Utils::Vector3d momentum{}; if (include_particles) { - linear_momentum += - mpi_call(::Communication::Result::reduction, - std::plus(), mpi_particle_momentum_local); + auto const particles = cell_structure.local_particles(); + momentum = + std::accumulate(particles.begin(), particles.end(), Utils::Vector3d{}, + [](Utils::Vector3d &m, Particle const &p) { + return m + p.mass() * p.v(); + }); } if (include_lbfluid) { - linear_momentum += lb_lbfluid_calc_fluid_momentum(); + momentum += lb_lbfluid_calc_fluid_momentum(); } - return linear_momentum; + return momentum; } Utils::Vector3d center_of_mass(PartCfg &partCfg, int p_type) { diff --git a/src/core/energy.cpp b/src/core/energy.cpp index aceabcfc33a..1bec058ccb1 100644 --- a/src/core/energy.cpp +++ b/src/core/energy.cpp @@ -43,7 +43,7 @@ #include -static std::shared_ptr calculate_energy_local() { +std::shared_ptr calculate_energy() { auto obs_energy_ptr = std::make_shared(1); @@ -117,23 +117,19 @@ static std::shared_ptr calculate_energy_local() { // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) } -REGISTER_CALLBACK_MAIN_RANK(calculate_energy_local) +REGISTER_CALLBACK_MAIN_RANK(calculate_energy) -std::shared_ptr calculate_energy() { - return mpi_call(Communication::Result::main_rank, calculate_energy_local); +double mpi_calculate_potential_energy() { + auto const obs = mpi_call(Communication::Result::main_rank, calculate_energy); + return obs->accumulate(-obs->kinetic[0]); } -double calculate_current_potential_energy_of_system() { - auto const obs_energy = calculate_energy(); - return obs_energy->accumulate(-obs_energy->kinetic[0]); +double mpi_observable_compute_energy() { + auto const obs = mpi_call(Communication::Result::main_rank, calculate_energy); + return obs->accumulate(0); } -double observable_compute_energy() { - auto const obs_energy = calculate_energy(); - return obs_energy->accumulate(0); -} - -double particle_short_range_energy_contribution_local(int pid) { +double particle_short_range_energy_contribution(int pid) { double ret = 0.0; if (cell_structure.get_resort_particles()) { @@ -158,11 +154,3 @@ double particle_short_range_energy_contribution_local(int pid) { } return ret; } - -REGISTER_CALLBACK_REDUCTION(particle_short_range_energy_contribution_local, - std::plus()) - -double particle_short_range_energy_contribution(int pid) { - return mpi_call(Communication::Result::reduction, std::plus(), - particle_short_range_energy_contribution_local, pid); -} diff --git a/src/core/energy.hpp b/src/core/energy.hpp index 63380c6c200..91f5e4176ef 100644 --- a/src/core/energy.hpp +++ b/src/core/energy.hpp @@ -34,16 +34,16 @@ std::shared_ptr calculate_energy(); /** Calculate the total energy of the system. */ -double calculate_current_potential_energy_of_system(); +double mpi_calculate_potential_energy(); /** Helper function for @ref Observables::Energy. */ -double observable_compute_energy(); +double mpi_observable_compute_energy(); /** * @brief Compute short-range energy of a particle. * * Iterates through particles inside cell and neighboring cells and computes - * energy contribution for a specificparticle. + * energy contribution for a specific particle. * * @param pid Particle id * @return Non-bonded energy of the particle. diff --git a/src/core/grid_based_algorithms/lb.cpp b/src/core/grid_based_algorithms/lb.cpp index 6de37da6819..b9c60828073 100644 --- a/src/core/grid_based_algorithms/lb.cpp +++ b/src/core/grid_based_algorithms/lb.cpp @@ -1316,15 +1316,15 @@ Utils::Vector3d lb_calc_local_momentum_density(Lattice::index_t index, } /** Calculate momentum of the LB fluid. - * @param[out] result Fluid momentum in MD units * @param[in] lb_parameters LB parameters * @param[in] lb_fields Hydrodynamic fields of the fluid * @param[in] lb_lattice The underlying lattice */ -void lb_calc_fluid_momentum(double *result, const LB_Parameters &lb_parameters, - const std::vector &lb_fields, - const Lattice &lb_lattice) { - Utils::Vector3d momentum_density{}, momentum{}; +Utils::Vector3d +mpi_lb_calc_fluid_momentum_local(LB_Parameters const &lb_parameters, + std::vector const &lb_fields, + Lattice const &lb_lattice) { + Utils::Vector3d momentum_density{}, momentum{}, result{}; for (int x = 1; x <= lb_lattice.grid[0]; x++) { for (int y = 1; y <= lb_lattice.grid[1]; y++) { @@ -1338,7 +1338,8 @@ void lb_calc_fluid_momentum(double *result, const LB_Parameters &lb_parameters, } momentum *= lb_parameters.agrid / lb_parameters.tau; - boost::mpi::reduce(comm_cart, momentum.data(), 3, result, std::plus<>(), 0); + boost::mpi::reduce(::comm_cart, momentum, result, std::plus<>(), 0); + return result; } void lb_collect_boundary_forces(double *result) { diff --git a/src/core/grid_based_algorithms/lb.hpp b/src/core/grid_based_algorithms/lb.hpp index dac4508134f..f7dc8eae44d 100644 --- a/src/core/grid_based_algorithms/lb.hpp +++ b/src/core/grid_based_algorithms/lb.hpp @@ -250,9 +250,10 @@ void lb_bounce_back(LB_Fluid &lbfluid, const LB_Parameters &lb_parameters, #endif /* LB_BOUNDARIES */ -void lb_calc_fluid_momentum(double *result, const LB_Parameters &lb_parameters, - const std::vector &lb_fields, - const Lattice &lb_lattice); +Utils::Vector3d +mpi_lb_calc_fluid_momentum_local(LB_Parameters const &lb_parameters, + std::vector const &lb_fields, + Lattice const &lb_lattice); void lb_collect_boundary_forces(double *result); void lb_initialize_fields(std::vector &fields, LB_Parameters const &lb_parameters, diff --git a/src/core/grid_based_algorithms/lb_interface.cpp b/src/core/grid_based_algorithms/lb_interface.cpp index 3630d31d47c..662a1c59b93 100644 --- a/src/core/grid_based_algorithms/lb_interface.cpp +++ b/src/core/grid_based_algorithms/lb_interface.cpp @@ -1099,23 +1099,18 @@ void lb_lbnode_set_pop(const Utils::Vector3i &ind, } } -static void mpi_lb_lbfluid_calc_fluid_momentum_local() { - lb_calc_fluid_momentum(nullptr, lbpar, lbfields, lblattice); -} - -REGISTER_CALLBACK(mpi_lb_lbfluid_calc_fluid_momentum_local) - Utils::Vector3d lb_lbfluid_calc_fluid_momentum() { - Utils::Vector3d fluid_momentum{}; + Utils::Vector3d momentum{}; if (lattice_switch == ActiveLB::GPU) { #ifdef CUDA - lb_calc_fluid_momentum_GPU(fluid_momentum.data()); + if (::comm_cart.rank() == 0) { + lb_calc_fluid_momentum_GPU(momentum.data()); + } #endif } else if (lattice_switch == ActiveLB::CPU) { - mpi_call(mpi_lb_lbfluid_calc_fluid_momentum_local); - lb_calc_fluid_momentum(fluid_momentum.data(), lbpar, lbfields, lblattice); + momentum = mpi_lb_calc_fluid_momentum_local(lbpar, lbfields, lblattice); } - return fluid_momentum; + return momentum; } const Utils::Vector3d diff --git a/src/core/observables/EnergyObservable.hpp b/src/core/observables/EnergyObservable.hpp index 8f0a906971c..40132153c01 100644 --- a/src/core/observables/EnergyObservable.hpp +++ b/src/core/observables/EnergyObservable.hpp @@ -32,7 +32,7 @@ class Energy : public Observable { std::vector shape() const override { return {1}; } std::vector operator()() const override { std::vector res{1}; - res[0] = observable_compute_energy(); + res[0] = mpi_observable_compute_energy(); return res; } }; diff --git a/src/core/observables/PressureObservable.hpp b/src/core/observables/PressureObservable.hpp index f9d50f09b45..1f9ec9cc8eb 100644 --- a/src/core/observables/PressureObservable.hpp +++ b/src/core/observables/PressureObservable.hpp @@ -30,7 +30,7 @@ class Pressure : public Observable { public: std::vector shape() const override { return {1}; } std::vector operator()() const override { - auto const ptensor = observable_compute_pressure_tensor(); + auto const ptensor = mpi_observable_compute_pressure_tensor(); std::vector res{1}; res[0] = (ptensor[0] + ptensor[4] + ptensor[8]) / 3.; return res; diff --git a/src/core/observables/PressureTensor.hpp b/src/core/observables/PressureTensor.hpp index 485dd4b7a95..d9fc5eabcde 100644 --- a/src/core/observables/PressureTensor.hpp +++ b/src/core/observables/PressureTensor.hpp @@ -30,7 +30,7 @@ class PressureTensor : public Observable { public: std::vector shape() const override { return {3, 3}; } std::vector operator()() const override { - return observable_compute_pressure_tensor().as_vector(); + return mpi_observable_compute_pressure_tensor().as_vector(); } }; diff --git a/src/core/pressure.cpp b/src/core/pressure.cpp index 3fa348ea420..5b770bf8ae1 100644 --- a/src/core/pressure.cpp +++ b/src/core/pressure.cpp @@ -52,7 +52,7 @@ #include #include -static std::shared_ptr calculate_pressure_local() { +std::shared_ptr calculate_pressure() { auto obs_pressure_ptr = std::make_shared(9); @@ -125,17 +125,14 @@ static std::shared_ptr calculate_pressure_local() { return obs_pressure_ptr; } -REGISTER_CALLBACK_MAIN_RANK(calculate_pressure_local) - -std::shared_ptr calculate_pressure() { - return mpi_call(Communication::Result::main_rank, calculate_pressure_local); -} +REGISTER_CALLBACK_MAIN_RANK(calculate_pressure) -Utils::Vector9d observable_compute_pressure_tensor() { - auto const obs_pressure = calculate_pressure(); +Utils::Vector9d mpi_observable_compute_pressure_tensor() { + auto const obs = + mpi_call(Communication::Result::main_rank, calculate_pressure); Utils::Vector9d pressure_tensor{}; for (std::size_t j = 0; j < 9; j++) { - pressure_tensor[j] = obs_pressure->accumulate(0, j); + pressure_tensor[j] = obs->accumulate(0, j); } return pressure_tensor; } diff --git a/src/core/pressure.hpp b/src/core/pressure.hpp index 9ab357126b0..7fd55d37841 100644 --- a/src/core/pressure.hpp +++ b/src/core/pressure.hpp @@ -35,6 +35,6 @@ std::shared_ptr calculate_pressure(); /** Helper function for @ref Observables::PressureTensor. */ -Utils::Vector9d observable_compute_pressure_tensor(); +Utils::Vector9d mpi_observable_compute_pressure_tensor(); #endif diff --git a/src/core/reaction_methods/ReactionAlgorithm.cpp b/src/core/reaction_methods/ReactionAlgorithm.cpp index f5feb49a82c..1f8330318be 100644 --- a/src/core/reaction_methods/ReactionAlgorithm.cpp +++ b/src/core/reaction_methods/ReactionAlgorithm.cpp @@ -54,7 +54,7 @@ int ReactionAlgorithm::do_reaction(int reaction_steps) { // calculate potential energy; only consider potential energy since we // assume that the kinetic part drops out in the process of calculating // ensemble averages (kinetic part may be separated and crossed out) - auto current_E_pot = calculate_current_potential_energy_of_system(); + auto current_E_pot = mpi_calculate_potential_energy(); 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); @@ -295,7 +295,7 @@ void ReactionAlgorithm::generic_oneway_reaction( auto const E_pot_new = (particle_inside_exclusion_range_touched) ? std::numeric_limits::max() - : calculate_current_potential_energy_of_system(); + : mpi_calculate_potential_energy(); auto const bf = calculate_acceptance_probability( current_reaction, E_pot_old, E_pot_new, old_particle_numbers); @@ -592,14 +592,14 @@ bool ReactionAlgorithm::do_global_mc_move_for_particles_of_type( return false; } - auto const E_pot_old = calculate_current_potential_energy_of_system(); + auto const E_pot_old = mpi_calculate_potential_energy(); auto const original_positions = generate_new_particle_positions( type, particle_number_of_type_to_be_changed); auto const E_pot_new = (particle_inside_exclusion_range_touched) ? std::numeric_limits::max() - : calculate_current_potential_energy_of_system(); + : mpi_calculate_potential_energy(); auto const beta = 1.0 / kT; diff --git a/src/core/reaction_methods/WidomInsertion.cpp b/src/core/reaction_methods/WidomInsertion.cpp index 783557ca75a..0945b735716 100644 --- a/src/core/reaction_methods/WidomInsertion.cpp +++ b/src/core/reaction_methods/WidomInsertion.cpp @@ -36,7 +36,7 @@ double WidomInsertion::calculate_particle_insertion_potential_energy( throw std::runtime_error("Trying to remove some non-existing particles " "from the system via the inverse Widom scheme."); - auto const E_pot_old = calculate_current_potential_energy_of_system(); + auto const E_pot_old = mpi_calculate_potential_energy(); // make reaction attempt std::vector p_ids_created_particles; @@ -46,7 +46,7 @@ double WidomInsertion::calculate_particle_insertion_potential_energy( hidden_particles_properties) = make_reaction_attempt(current_reaction); - auto const E_pot_new = calculate_current_potential_energy_of_system(); + auto const E_pot_new = mpi_calculate_potential_energy(); // reverse reaction attempt // reverse reaction // 1) delete created product particles diff --git a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp index ddf19a283ed..0366ac86be9 100644 --- a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp +++ b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp @@ -104,6 +104,10 @@ static void mpi_remove_translational_motion() { mpi_call_all(mpi_remove_translational_motion_local); } +static auto mpi_calculate_energy() { + return mpi_call(Communication::Result::main_rank, calculate_energy); +} + #ifdef P3M static void mpi_set_tuned_p3m_local(double prefactor) { auto p3m = P3MParameters{false, @@ -201,9 +205,9 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, set_particle_v(pid2, {static_cast(i), 0., 0.}); auto const &p = get_particle_data(pid2); auto const kinetic_energy = 0.5 * p.mass() * p.v().norm2(); - auto const obs_energy = calculate_energy(); + auto const obs_energy = mpi_calculate_energy(); BOOST_CHECK_CLOSE(obs_energy->kinetic[0], kinetic_energy, tol); - BOOST_CHECK_CLOSE(observable_compute_energy(), kinetic_energy, tol); + BOOST_CHECK_CLOSE(mpi_observable_compute_energy(), kinetic_energy, tol); } } @@ -232,7 +236,7 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, auto const lj_energy = 4.0 * eps * (Utils::sqr(frac6) - frac6 + shift); // measure energies - auto const obs_energy = calculate_energy(); + auto const obs_energy = mpi_calculate_energy(); for (int i = 0; i < n_pairs; ++i) { auto const ref_inter = (i == lj_pair_ab) ? lj_energy : 0.; auto const ref_intra = (i == lj_pair_bb) ? lj_energy : 0.; @@ -259,7 +263,7 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, add_particle_bond(pid2, std::vector{fene_bond_id, pid3}); // measure energies - auto const obs_energy = calculate_energy(); + auto const obs_energy = mpi_calculate_energy(); 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 = @@ -291,7 +295,7 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory, place_particle(pid2, pos2); auto const r = (pos2 - pos1).norm(); // check P3M energy - auto const obs_energy = calculate_energy(); + auto const obs_energy = mpi_calculate_energy(); // at very short distances, the real-space contribution to // the energy is much larger than the k-space contribution auto const energy_ref = -prefactor / r; diff --git a/src/python/espressomd/analyze.py b/src/python/espressomd/analyze.py index 69168368145..83f9c140e09 100644 --- a/src/python/espressomd/analyze.py +++ b/src/python/espressomd/analyze.py @@ -83,7 +83,7 @@ def acf_1d(signal, n_with_padding, n): @script_interface_register class _ObservableStat(ScriptInterfaceHelper): _so_name = "ScriptInterface::Analysis::ObservableStat" - _so_creation_policy = "LOCAL" + _so_creation_policy = "GLOBAL" def _generate_summary(self, obj, dim, calc_sp): """ @@ -346,7 +346,7 @@ class Analysis(ScriptInterfaceHelper): """ _so_name = "ScriptInterface::Analysis::Analysis" - _so_creation_policy = "LOCAL" + _so_creation_policy = "GLOBAL" _so_bind_methods = ( "linear_momentum", "center_of_mass", diff --git a/src/script_interface/analysis/Analysis.cpp b/src/script_interface/analysis/Analysis.cpp index 5ebebd25802..14c1b0bdf7d 100644 --- a/src/script_interface/analysis/Analysis.cpp +++ b/src/script_interface/analysis/Analysis.cpp @@ -28,6 +28,8 @@ #include "core/partCfg_global.hpp" #include "core/particle_node.hpp" +#include "script_interface/communication.hpp" + #include #include @@ -68,6 +70,20 @@ static void check_particle_type(int p_type) { Variant Analysis::do_call_method(std::string const &name, VariantMap const ¶meters) { + if (name == "linear_momentum") { + auto const local = calc_linear_momentum( + get_value_or(parameters, "include_particles", true), + get_value_or(parameters, "include_lbfluid", true)); + return mpi_reduce_sum(context()->get_comm(), local).as_vector(); + } + if (name == "particle_energy") { + auto const pid = get_value(parameters, "pid"); + auto const local = particle_short_range_energy_contribution(pid); + return mpi_reduce_sum(context()->get_comm(), local); + } + if (not context()->is_head_node()) { + return {}; + } if (name == "min_dist") { auto const p_types1 = get_value>(parameters, "p_types1"); auto const p_types2 = get_value>(parameters, "p_types2"); @@ -89,12 +105,6 @@ Variant Analysis::do_call_method(std::string const &name, auto const result = angular_momentum(partCfg(), p_type); return result.as_vector(); } - if (name == "linear_momentum") { - auto const result = calc_linear_momentum( - get_value_or(parameters, "include_particles", true), - get_value_or(parameters, "include_lbfluid", true)); - return result.as_vector(); - } if (name == "nbhood") { auto const pos = get_value(parameters, "pos"); auto const radius = get_value(parameters, "r_catch"); @@ -107,10 +117,6 @@ Variant Analysis::do_call_method(std::string const &name, return result.as_vector(); } #endif // DPD - if (name == "particle_energy") { - auto const pid = get_value(parameters, "pid"); - return particle_short_range_energy_contribution(pid); - } if (name == "calc_re") { auto const chain_start = get_value(parameters, "chain_start"); auto const chain_length = get_value(parameters, "chain_length"); diff --git a/src/script_interface/communication.hpp b/src/script_interface/communication.hpp new file mode 100644 index 00000000000..d66dbeef1b3 --- /dev/null +++ b/src/script_interface/communication.hpp @@ -0,0 +1,42 @@ +/* + * 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 ESPRESSO_SRC_SCRIPT_INTERFACE_COMMUNICATION_HPP +#define ESPRESSO_SRC_SCRIPT_INTERFACE_COMMUNICATION_HPP + +#include +#include + +#include + +namespace ScriptInterface { + +/** + * @brief Reduce object by sum on the head node. + * Worker nodes get a default-constructed object. + */ +template +T mpi_reduce_sum(boost::mpi::communicator const &comm, T const &result) { + T out{}; + boost::mpi::reduce(comm, result, out, std::plus{}, 0); + return out; +} + +} // namespace ScriptInterface + +#endif From 7962c4d9d9400fe858819b1b935259ea93a24598 Mon Sep 17 00:00:00 2001 From: Alexander Reinauer <38552369+reinaual@users.noreply.github.com> Date: Fri, 16 Sep 2022 22:53:08 +0200 Subject: [PATCH 76/85] Small typos in the reaction ensemble tutorial --- doc/tutorials/constant_pH/constant_pH.ipynb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/tutorials/constant_pH/constant_pH.ipynb b/doc/tutorials/constant_pH/constant_pH.ipynb index dc3f8519e0c..e10fbf4ca14 100644 --- a/doc/tutorials/constant_pH/constant_pH.ipynb +++ b/doc/tutorials/constant_pH/constant_pH.ipynb @@ -103,7 +103,7 @@ "source": [ "### The constant pH method\n", "\n", - "The constant $\\mathrm{pH}$ method [Reed1992] has been designed to simulate an acid-base ionization reaction at a given $\\mathrm{pH}$. It assumes that $\\mathrm{pH}$ in the system is constant, independent of the ionization degree. In a real solution, the ionization reaction generates an $\\mathrm{H^+}$ ion, that immediately reacts with the the buffer that has been added to the solution in order to keep the $\\mathrm{pH}$ (approximately) constant. Effectively, the net reaction that can be written as \n", + "The constant $\\mathrm{pH}$ method [Reed1992] has been designed to simulate an acid-base ionization reaction at a given $\\mathrm{pH}$. It assumes that $\\mathrm{pH}$ in the system is constant, independent of the ionization degree. In a real solution, the ionization reaction generates an $\\mathrm{H^+}$ ion, that immediately reacts with the buffer that has been added to the solution in order to keep the $\\mathrm{pH}$ (approximately) constant. Effectively, the net reaction can be written as \n", "$$\\mathrm{HA} \\Leftrightarrow \\mathrm{A}^- + \\mathrm{B}^+ ,$$\n", "where we use $\\mathrm{B}^+$ to denote a generic neutralizing counterion that could be either the $\\mathrm{H}^+$ ion, or another cation that is generated by the reaction of the $\\mathrm{H}^+$ ion with the buffer.\n", "In the literature, this counterion is often denoted as $\\mathrm{H^+}$, however, this may lead to confusion because then the actual number of $\\mathrm{H^+}$ ions in the box does not correspond to their concentration implied by the fixed value of the $\\mathrm{pH}$.\n", @@ -240,7 +240,7 @@ "### Set the key physical parameters that uniquely define the system\n", "\n", "We want to simulate a solution of polyelectrolyte chains composed of weak acidic groups. \n", - "The chains are is characterized by the number of acidic groups, their $\\mathrm{p}K_{\\mathrm{A}}$ and their concentration in the solution.\n", + "The chains are characterized by the number of acidic groups, their $\\mathrm{p}K_{\\mathrm{A}}$ and their concentration in the solution.\n", "The polyelectrolytes are dissolved in an aqueous solution of NaCl, further characterized by the salt concentration and by the $\\mathrm{pH}$ value.\n", "\n", "To specify the system properties, we to define the total number of titratable units `N_ACID`, their concentration `C_ACID` and the salt concentration `C_SALT`. We set the dissociation constant of the acid to $\\mathrm{p}K_\\mathrm{A}=4.88$, that is the acidity constant of propionic acid. The structure of propionic acid resembles the repeating unit of poly(acrylic acid), the most commonly used weak polyacid." @@ -256,7 +256,7 @@ "N_ACID = 20\n", "C_ACID = 1e-3 * ureg.molar\n", "C_SALT = 2 * C_ACID\n", - "pKa = 4.88 # acidity constant\n", + "pKa = 4.88 # acidity constant\n", "pKw = 14.0 # autoprotolysis constant of water" ] }, @@ -294,7 +294,7 @@ "\n", "To be able to compare our results to the analytical solutions for ideal systems and to obtain results very quickly, we begin with all non-bonded interactions turned off. In the next runs, we will add the steric repulsion and electrostatic interactions to observe their effect on the ionization.\n", "\n", - "Because it is an interactive tutorial, we want it to be completed within few minutes. Therefore, we choose the Debye-Hückel potential for electrostatics by default. It produces results that are similar to the full electrostatics calculated by the P3M method but we gain about a factor of 5 in the simulation runtime. In this way, we sacrifice accuracy for speed. Such thing is definitely not recommended for production simulations." + "Because it is an interactive tutorial, we want it to be completed within few minutes. Therefore, we choose the Debye-Hückel potential for electrostatics by default. It produces results that are similar to the full electrostatics calculated by the P3M method but we gain about a factor of 5 in the simulation runtime. In this way, we sacrifice accuracy for speed. Is's definitely not recommended for production simulations." ] }, { @@ -321,7 +321,7 @@ "source": [ "#### Set the number of samples to be collected\n", "\n", - "For error analysis we specify the number of blocks `N_BLOCKS` and the desired number of samples per block `DESIRED_BLOCK_SIZE`. From that we can estimate the total number of statistically independent samples that we want to collect `NUM_SAMPLES`. Note that samples collected in our simulations are not really statistically independent, so the actual number of independent samples is always lower than `NUM_SAMPLES`. \n", + "For the error analysis we specify the number of blocks `N_BLOCKS` and the desired number of samples per block `DESIRED_BLOCK_SIZE`. From that we can estimate the total number of statistically independent samples that we want to collect `NUM_SAMPLES`. Note that samples collected in our simulations are not really statistically independent, so the actual number of independent samples is always lower than `NUM_SAMPLES`. \n", "Therefore, the actual number of independent samples depends on the properties of the simulated system and needs to be estimated at the end of the simulation.\n", "We keep the number of samples rather low, so that the tutorial completes fast. If you want to obtain data with a better statistical quality, we encourage you to increase the number of samples." ] From 9536521fc650b1fb1bf5c9d0b911157331d876ab Mon Sep 17 00:00:00 2001 From: Alexander Reinauer <38552369+reinaual@users.noreply.github.com> Date: Sat, 17 Sep 2022 10:35:46 +0200 Subject: [PATCH 77/85] Update doc/tutorials/constant_pH/constant_pH.ipynb 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorials/constant_pH/constant_pH.ipynb b/doc/tutorials/constant_pH/constant_pH.ipynb index e10fbf4ca14..9f78b64d30f 100644 --- a/doc/tutorials/constant_pH/constant_pH.ipynb +++ b/doc/tutorials/constant_pH/constant_pH.ipynb @@ -294,7 +294,7 @@ "\n", "To be able to compare our results to the analytical solutions for ideal systems and to obtain results very quickly, we begin with all non-bonded interactions turned off. In the next runs, we will add the steric repulsion and electrostatic interactions to observe their effect on the ionization.\n", "\n", - "Because it is an interactive tutorial, we want it to be completed within few minutes. Therefore, we choose the Debye-Hückel potential for electrostatics by default. It produces results that are similar to the full electrostatics calculated by the P3M method but we gain about a factor of 5 in the simulation runtime. In this way, we sacrifice accuracy for speed. Is's definitely not recommended for production simulations." + "Because it is an interactive tutorial, we want it to be completed within few minutes. Therefore, we choose the Debye-Hückel potential for electrostatics by default. It produces results that are similar to the full electrostatics calculated by the P3M method but we gain about a factor of 5 in the simulation runtime. In this way, we sacrifice accuracy for speed. It's definitely not recommended for production simulations." ] }, { From 2d279f5c0cbc6823e765d81248a630a6bae282f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 19 Sep 2022 18:36:47 +0200 Subject: [PATCH 78/85] Apply code review suggestions Co-authored-by: Alexander Reinauer --- .../nonbonded_interaction_data.cpp | 2 +- src/python/espressomd/interactions.py | 22 +++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp index 6673ee62feb..8b1925ba306 100644 --- a/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp +++ b/src/core/nonbonded_interactions/nonbonded_interaction_data.cpp @@ -73,7 +73,7 @@ void mpi_realloc_ia_params_local(int new_size) { } ::max_seen_particle_type = new_size; - std::swap(::nonbonded_ia_params, new_params); + ::nonbonded_ia_params = std::move(new_params); } REGISTER_CALLBACK(mpi_realloc_ia_params_local) diff --git a/src/python/espressomd/interactions.py b/src/python/espressomd/interactions.py index a84c67d1fb6..7ffe575bd62 100644 --- a/src/python/espressomd/interactions.py +++ b/src/python/espressomd/interactions.py @@ -17,13 +17,14 @@ # along with this program. If not, see . # +import abc import enum from . import utils from . import code_features from .script_interface import ScriptObjectMap, ScriptInterfaceHelper, script_interface_register -class NonBondedInteraction(ScriptInterfaceHelper): +class NonBondedInteraction(ScriptInterfaceHelper, metaclass=abc.ABCMeta): """ Represents an instance of a non-bonded interaction, such as Lennard-Jones. @@ -65,12 +66,9 @@ def __reduce__(self): def _restore_object(cls, derived_class, kwargs): return derived_class(**kwargs) + @abc.abstractmethod def default_params(self): - """Virtual method. - - """ - raise Exception( - "Subclasses of NonBondedInteraction must define the default_params() method.") + pass @script_interface_register @@ -794,7 +792,7 @@ class BONDED_IA(enum.IntEnum): VIRTUAL_BOND = enum.auto() -class BondedInteraction(ScriptInterfaceHelper): +class BondedInteraction(ScriptInterfaceHelper, metaclass=abc.ABCMeta): """ Base class for bonded interactions. @@ -874,12 +872,12 @@ def params(self, p): def __str__(self): return f'{self.__class__.__name__}({self._ctor_params})' + @abc.abstractmethod def get_default_params(self): """Gets default values of optional parameters. """ - raise Exception( - "Subclasses of BondedInteraction must define the get_default_params() method.") + pass def __repr__(self): return f'<{self}>' @@ -991,6 +989,9 @@ class BondedCoulombSRBond(BondedInteraction): _type_number = BONDED_IA.BONDED_COULOMB_SR def get_default_params(self): + """Gets default values of optional parameters. + + """ return {} @@ -1040,6 +1041,9 @@ def _serialize(self): return params def get_default_params(self): + """Gets default values of optional parameters. + + """ return {"r_cut": 0., "seed": None} From b6059ce13aa68f716fea111acadc60e78d0d3590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Wed, 21 Sep 2022 17:31:15 +0200 Subject: [PATCH 79/85] python: Fix checkpointing code and documentation Allow checkpointing a system that doesn't have a time_step yet. --- doc/sphinx/io.rst | 9 ++++++++- .../integrators/IntegratorHandle.cpp | 12 +++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/doc/sphinx/io.rst b/doc/sphinx/io.rst index cf5b3781e7b..2d73dcd809c 100644 --- a/doc/sphinx/io.rst +++ b/doc/sphinx/io.rst @@ -113,7 +113,7 @@ Be aware of the following limitations: * Checkpointing only supports recursion on the head node. It is therefore impossible to checkpoint a :class:`espressomd.system.System` instance that - contains LB boundaries, constraints or auto-update accumulators when the + contains LB boundaries, constraint unions or auto-update accumulators when the simulation is running with 2 or more MPI nodes. * The active actors, i.e., the content of ``system.actors``, are checkpointed. @@ -140,6 +140,13 @@ Be aware of the following limitations: * Checkpoints may depend on the presence of other Python modules at specific versions. It may therefore not be possible to load a checkpoint in a different environment than where it was written. + In particular, all |es| modules whose classes have been used to + instantiate objects in the checkpoint file also need to be imported + in the script that loads the checkpoint (because importing an |es| + module does more than just making classes visibles: it also registers + them as script interface classes). + Loading a checkpoint without the proper |es| module imports will generally + raise an exception indicating which module is missing. For additional methods of the checkpointing class, see :class:`espressomd.checkpointing.Checkpoint`. diff --git a/src/script_interface/integrators/IntegratorHandle.cpp b/src/script_interface/integrators/IntegratorHandle.cpp index 7e838958c6a..2a485360572 100644 --- a/src/script_interface/integrators/IntegratorHandle.cpp +++ b/src/script_interface/integrators/IntegratorHandle.cpp @@ -82,9 +82,19 @@ IntegratorHandle::IntegratorHandle() { }); } +static bool checkpoint_filter(typename VariantMap::value_type const &kv) { + /* When loading from a checkpoint file, defer the integrator object to last, + * and skip the time_step if it is -1 to avoid triggering sanity checks. + */ + return kv.first != "integrator" and + not(kv.first == "time_step" and ::integ_switch == INTEG_METHOD_NVT and + get_time_step() == -1. and is_type(kv.second) and + get_value(kv.second) == -1.); +} + void IntegratorHandle::do_construct(VariantMap const ¶ms) { for (auto const &kv : params) { - if (kv.first != "integrator") { + if (checkpoint_filter(kv)) { do_set_parameter(kv.first, kv.second); } } From 0929c29da022ca3fd9b2ba76fce2696547ebafff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 22 Sep 2022 13:41:29 +0200 Subject: [PATCH 80/85] python: Document how to checkpoint local objects Co-authored-by: Christoph Lohrmann --- doc/sphinx/io.rst | 24 ++++++++++++++++++++++++ testsuite/python/save_checkpoint.py | 5 +++++ 2 files changed, 29 insertions(+) diff --git a/doc/sphinx/io.rst b/doc/sphinx/io.rst index 2d73dcd809c..401642a444c 100644 --- a/doc/sphinx/io.rst +++ b/doc/sphinx/io.rst @@ -148,6 +148,30 @@ Be aware of the following limitations: Loading a checkpoint without the proper |es| module imports will generally raise an exception indicating which module is missing. +* It is only possible to checkpoint objects at global scope. + When wrapping the checkpointing logic in a function, objects local to + that function won't be checkpointed, since their origin cannot be safely + stored in the checkpoint file. To circumvent this limitation, make any + local object explicitly global, so that it belongs to the global scope:: + + import espressomd + import espressomd.checkpointing + + def setup_system(): + global system # attach 'system' to global scope for checkpointing + checkpoint = espressomd.checkpointing.Checkpoint(checkpoint_id="mycheckpoint") + if not checkpoint.has_checkpoints(): + system = espressomd.System(box_l=[1., 1., 1.]) + system.part.add(pos=[0.1, 0.2, 0.3]) + checkpoint.register("system") + checkpoint.save() + else: + checkpoint.load() + print(system.part.by_id(0).pos) + return system + + system = setup_system() + For additional methods of the checkpointing class, see :class:`espressomd.checkpointing.Checkpoint`. diff --git a/testsuite/python/save_checkpoint.py b/testsuite/python/save_checkpoint.py index ca001c2e374..2bea13d10a9 100644 --- a/testsuite/python/save_checkpoint.py +++ b/testsuite/python/save_checkpoint.py @@ -393,6 +393,11 @@ def test_checkpointing(self): self.assertTrue(lbf_cpt_path.is_file(), "LB checkpoint file not created") + # only objects at global scope can be checkpointed + with self.assertRaisesRegex(KeyError, "The given object 'local_obj' was not found in the current scope"): + local_obj = "local" # pylint: disable=unused-variable + checkpoint.register("local_obj") + @ut.skipIf(lbf_actor is None, "Skipping test due to missing mode.") def test_lb_checkpointing_exceptions(self): ''' From 6db87687e2c6b387fdad8ea1e1e3c016456f788f Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 25 Sep 2022 16:54:48 +0200 Subject: [PATCH 81/85] build: harden deploy.yml permissions Signed-off-by: Alex --- .github/workflows/deploy.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 082a28fc791..58653e4d88f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,6 +4,9 @@ on: schedule: - cron: '0 7 * * *' +permissions: + contents: read # to fetch code (actions/checkout) + jobs: deploy_docs: runs-on: ubuntu-20.04 From 77f3d664942529d3e4a33d19d9d71a042c2379c0 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 25 Sep 2022 16:56:53 +0200 Subject: [PATCH 82/85] build: harden push_pull.yml permissions Signed-off-by: Alex --- .github/workflows/push_pull.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/push_pull.yml b/.github/workflows/push_pull.yml index 1de0439032d..fec06489853 100644 --- a/.github/workflows/push_pull.yml +++ b/.github/workflows/push_pull.yml @@ -6,6 +6,9 @@ on: schedule: - cron: '0 3 * * *' +permissions: + contents: read # to fetch code (actions/checkout) + jobs: regular_check: runs-on: macos-latest @@ -24,6 +27,10 @@ jobs: ubsan: false sanitizer_check: + permissions: + contents: read # to fetch code (actions/checkout) + issues: write # to create an issue + runs-on: macos-latest if: (github.event_name == 'schedule' && github.repository == 'espressomd/espresso') steps: From cf46adbdf0a650ea088c2c7e5c3ccd569e1b7412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 26 Sep 2022 19:47:23 +0200 Subject: [PATCH 83/85] cmake: Bump CUDA architectures --- cmake/FindCUDACompilerClang.cmake | 5 +++-- cmake/FindCUDACompilerNVCC.cmake | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cmake/FindCUDACompilerClang.cmake b/cmake/FindCUDACompilerClang.cmake index 75305d08ea4..e67853375ce 100644 --- a/cmake/FindCUDACompilerClang.cmake +++ b/cmake/FindCUDACompilerClang.cmake @@ -93,8 +93,9 @@ target_compile_options( $<$:-O2 -g -DNDEBUG> $<$:-O3 -g> $<$:-O3 -g> - $<$:--cuda-gpu-arch=sm_30> - --cuda-gpu-arch=sm_52 + $<$:--cuda-gpu-arch=sm_52> # GTX-900 series (Maxwell) + $<$:--cuda-gpu-arch=sm_61> # GTX-1000 series (Pascal) + $<$:--cuda-gpu-arch=sm_75> # RTX-2000 series (Turing) ) function(add_gpu_library) diff --git a/cmake/FindCUDACompilerNVCC.cmake b/cmake/FindCUDACompilerNVCC.cmake index d274476707c..0b9bda3e343 100644 --- a/cmake/FindCUDACompilerNVCC.cmake +++ b/cmake/FindCUDACompilerNVCC.cmake @@ -69,9 +69,10 @@ function(add_gpu_library) set(GPU_TARGET_NAME ${ARGV0}) set_property(TARGET ${GPU_TARGET_NAME} PROPERTY CUDA_SEPARABLE_COMPILATION ON) target_link_libraries(${GPU_TARGET_NAME} PRIVATE espresso::cuda_flags) - list(APPEND cuda_archs 52) + list(APPEND cuda_archs 75) # RTX-2000 series (Turing) + list(APPEND cuda_archs 61) # GTX-1000 series (Pascal) if(CMAKE_CUDA_COMPILER_VERSION LESS 11) - list(APPEND cuda_archs 30) + list(APPEND cuda_archs 52) # GTX-900 series (Maxwell) endif() set_target_properties(${GPU_TARGET_NAME} PROPERTIES CUDA_ARCHITECTURES "${cuda_archs}") endfunction() From 8cde26d755cc94ff37232ebc0f4fa0122e39dc3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 27 Sep 2022 14:55:52 +0200 Subject: [PATCH 84/85] core: Address Clang 12 diagnostics Fix most of the following diagnostics: return-std-move-in-c++11, missing-variable-declarations, double-promotion, shorten-64-to-32, old-style-cast, extra-semi, missing-noreturn, non-virtual-dtor, unused-exception-parameter, unused-macros, documentation. --- CMakeLists.txt | 5 +++- src/core/SystemInterface.hpp | 24 +++++++++---------- src/core/bond_breakage/bond_breakage.cpp | 2 +- src/core/cell_system/AtomDecomposition.hpp | 2 +- src/core/cell_system/HybridDecomposition.hpp | 2 +- src/core/cell_system/RegularDecomposition.hpp | 2 +- src/core/cells.cpp | 2 +- src/core/communication.cpp | 10 ++++---- src/core/cuda_init.cpp | 4 ++-- src/core/cuda_interface.cpp | 4 ++-- src/core/electrostatics/icc.cpp | 7 +++--- src/core/electrostatics/specfunc.cpp | 12 +++++----- src/core/energy.cpp | 4 ++-- src/core/errorhandling.cpp | 6 ++--- src/core/errorhandling.hpp | 2 +- src/core/event.cpp | 2 +- src/core/galilei/ComFixed.hpp | 10 ++++---- src/core/grid_based_algorithms/halo.cpp | 4 ++-- src/core/grid_based_algorithms/lb.cpp | 2 +- .../grid_based_algorithms/lb_boundaries.cpp | 4 ++-- .../grid_based_algorithms/lb_interface.cpp | 14 ++++++----- .../lb_particle_coupling.cpp | 2 +- src/core/grid_based_algorithms/lbgpu.cpp | 2 ++ src/core/immersed_boundary/ibm_triel.cpp | 2 +- src/core/integrate.cpp | 2 +- src/core/io/writer/h5md_core.cpp | 2 +- src/core/p3m/common.cpp | 3 ++- src/core/p3m/fft.cpp | 2 +- src/core/p3m/interpolation.hpp | 2 +- .../tests/ReactionAlgorithm_test.cpp | 2 +- .../tests/ReactionEnsemble_test.cpp | 3 ++- .../EspressoSystemInterface_test.cpp | 4 ++-- .../EspressoSystemStandAlone_test.cpp | 2 +- .../Particle_serialization_test.cpp | 8 +++---- src/core/unit_tests/Verlet_list_test.cpp | 2 +- .../field_coupling_couplings_test.cpp | 2 +- src/core/unit_tests/grid_test.cpp | 2 +- .../analysis/ObservableStat.cpp | 10 ++++---- .../auto_parameters/AutoParameters.hpp | 6 ++--- .../CylindricalPidProfileObservable.hpp | 2 +- .../observables/initialize.cpp | 7 ------ .../particle_data/ParticleHandle.hpp | 2 +- .../tests/LocalContext_test.cpp | 2 +- .../tests/ParallelExceptionHandler_test.cpp | 8 +++---- src/utils/include/utils/Histogram.hpp | 6 +++-- .../cylindrical_transformation_parameters.hpp | 13 ++++++---- src/utils/include/utils/mpi/gather_buffer.hpp | 4 ++-- src/utils/tests/matrix_vector_product.cpp | 2 +- src/utils/tests/quaternion_test.cpp | 6 ++--- 49 files changed, 120 insertions(+), 114 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 43e693316ea..37b891d74ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -397,8 +397,11 @@ target_compile_options( $<$:-Wextern-initializer> $<$:-Wrange-loop-analysis> -Wfloat-conversion - $<$:-Wimplicit-float-conversion> + $<$:-Wimplicit-float-conversion> + $<$:-Wunused-exception-parameter> + $<$:-Wmissing-variable-declarations> $<$,$,11.0.0>>:-Wnon-c-typedef-for-linkage> + $<$>:-Wdelete-non-virtual-dtor> # disable warnings from -Wextra -Wno-sign-compare -Wno-unused-function diff --git a/src/core/SystemInterface.hpp b/src/core/SystemInterface.hpp index 3dfbf014351..1b68444ba60 100644 --- a/src/core/SystemInterface.hpp +++ b/src/core/SystemInterface.hpp @@ -62,39 +62,39 @@ class SystemInterface { virtual void init() = 0; virtual void update() = 0; - virtual float *rGpuBegin() { return nullptr; }; - virtual bool hasRGpu() { return false; }; + virtual float *rGpuBegin() { return nullptr; } + virtual bool hasRGpu() { return false; } virtual void requestRGpu() { throw std::runtime_error(error_message("positions")); } - virtual float *dipGpuBegin() { return nullptr; }; - virtual bool hasDipGpu() { return false; }; + virtual float *dipGpuBegin() { return nullptr; } + virtual bool hasDipGpu() { return false; } virtual void requestDipGpu() { throw std::runtime_error(error_message("dipoles")); } - virtual float *torqueGpuBegin() { return nullptr; }; - virtual bool hasTorqueGpu() { return false; }; + virtual float *torqueGpuBegin() { return nullptr; } + virtual bool hasTorqueGpu() { return false; } virtual void requestTorqueGpu() { throw std::runtime_error(error_message("torques")); } - virtual float *qGpuBegin() { return nullptr; }; - virtual bool hasQGpu() { return false; }; + virtual float *qGpuBegin() { return nullptr; } + virtual bool hasQGpu() { return false; } virtual void requestQGpu() { throw std::runtime_error(error_message("charges")); } - virtual float *fGpuBegin() { return nullptr; }; - virtual bool hasFGpu() { return false; }; + virtual float *fGpuBegin() { return nullptr; } + virtual bool hasFGpu() { return false; } virtual void requestFGpu() { throw std::runtime_error(error_message("forces")); } - virtual float *eGpu() { return nullptr; }; + virtual float *eGpu() { return nullptr; } - virtual std::size_t npart_gpu() const { return 0; }; + virtual std::size_t npart_gpu() const { return 0; } virtual Utils::Vector3d box() const = 0; private: diff --git a/src/core/bond_breakage/bond_breakage.cpp b/src/core/bond_breakage/bond_breakage.cpp index f3773467d57..df061ecf387 100644 --- a/src/core/bond_breakage/bond_breakage.cpp +++ b/src/core/bond_breakage/bond_breakage.cpp @@ -72,7 +72,7 @@ struct QueueEntry { /** @brief Queue to record bonds broken during a time step */ using Queue = std::vector; -Queue queue; +static Queue queue; /** @brief Retrieve breakage specification for the bond type */ boost::optional get_breakage_spec(int bond_type) { diff --git a/src/core/cell_system/AtomDecomposition.hpp b/src/core/cell_system/AtomDecomposition.hpp index 7b549aa9045..934a91bccfb 100644 --- a/src/core/cell_system/AtomDecomposition.hpp +++ b/src/core/cell_system/AtomDecomposition.hpp @@ -108,7 +108,7 @@ class AtomDecomposition : public ParticleDecomposition { return m_box; } - BoxGeometry const &box() const override { return m_box; }; + BoxGeometry const &box() const override { return m_box; } private: /** diff --git a/src/core/cell_system/HybridDecomposition.hpp b/src/core/cell_system/HybridDecomposition.hpp index 2713a901164..6db52dbc5eb 100644 --- a/src/core/cell_system/HybridDecomposition.hpp +++ b/src/core/cell_system/HybridDecomposition.hpp @@ -127,7 +127,7 @@ class HybridDecomposition : public ParticleDecomposition { return m_box; } - BoxGeometry const &box() const override { return m_box; }; + BoxGeometry const &box() const override { return m_box; } /** @brief Count particles in child regular decompositions. */ std::size_t count_particles_in_regular() const { diff --git a/src/core/cell_system/RegularDecomposition.hpp b/src/core/cell_system/RegularDecomposition.hpp index 7c0f64eb2af..77ccc09aa3b 100644 --- a/src/core/cell_system/RegularDecomposition.hpp +++ b/src/core/cell_system/RegularDecomposition.hpp @@ -121,7 +121,7 @@ struct RegularDecomposition : public ParticleDecomposition { return {m_box}; } - BoxGeometry const &box() const override { return m_box; }; + BoxGeometry const &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 35bf33b02a2..e52b612deb6 100644 --- a/src/core/cells.cpp +++ b/src/core/cells.cpp @@ -149,7 +149,7 @@ mpi_get_short_range_neighbors_local(int const pid, double const distance, } }; cell_structure.run_on_particle_short_range_neighbors(*p, kernel); - return ret; + return {ret}; } REGISTER_CALLBACK_ONE_RANK(mpi_get_short_range_neighbors_local) diff --git a/src/core/communication.cpp b/src/core/communication.cpp index 20b7d052637..5173ea727a7 100644 --- a/src/core/communication.cpp +++ b/src/core/communication.cpp @@ -39,15 +39,13 @@ #include #include -namespace Communication { -auto const &mpi_datatype_cache = boost::mpi::detail::mpi_datatype_cache(); -std::shared_ptr mpi_env; -} // namespace Communication - boost::mpi::communicator comm_cart; namespace Communication { -std::unique_ptr m_callbacks; +static auto const &mpi_datatype_cache = + boost::mpi::detail::mpi_datatype_cache(); +static std::shared_ptr mpi_env; +static std::unique_ptr m_callbacks; /* We use a singleton callback class for now. */ MpiCallbacks &mpiCallbacks() { diff --git a/src/core/cuda_init.cpp b/src/core/cuda_init.cpp index fba9c233cf8..49fbeb1f023 100644 --- a/src/core/cuda_init.cpp +++ b/src/core/cuda_init.cpp @@ -63,7 +63,7 @@ std::vector cuda_gather_gpus() { int n_devices; try { n_devices = cuda_get_n_gpus(); - } catch (cuda_runtime_error const &err) { + } catch (cuda_runtime_error const &) { n_devices = 0; } @@ -80,7 +80,7 @@ std::vector cuda_gather_gpus() { device.proc_name[63] = '\0'; device.node = this_node; devices_local.push_back(device); - } catch (cuda_runtime_error const &err) { + } catch (cuda_runtime_error const &) { // pass } } diff --git a/src/core/cuda_interface.cpp b/src/core/cuda_interface.cpp index e02be9867b8..9fb68dc90bb 100644 --- a/src/core/cuda_interface.cpp +++ b/src/core/cuda_interface.cpp @@ -118,9 +118,9 @@ static void add_forces_and_torques(ParticleRange particles, int i = 0; for (auto &p : particles) { for (int j = 0; j < 3; j++) { - p.force()[j] += forces[3 * i + j]; + p.force()[j] += static_cast(forces[3 * i + j]); #ifdef ROTATION - p.torque()[j] += torques[3 * i + j]; + p.torque()[j] += static_cast(torques[3 * i + j]); #endif } i++; diff --git a/src/core/electrostatics/icc.cpp b/src/core/electrostatics/icc.cpp index ca23e179a04..72efc29679d 100644 --- a/src/core/electrostatics/icc.cpp +++ b/src/core/electrostatics/icc.cpp @@ -236,7 +236,8 @@ struct SanityChecksICC : public boost::static_visitor { void operator()(std::shared_ptr const &actor) const {} #ifdef P3M #ifdef CUDA - void operator()(std::shared_ptr const &actor) const { + [[noreturn]] void + operator()(std::shared_ptr const &actor) const { throw std::runtime_error("ICC does not work with P3MGPU"); } #endif // CUDA @@ -248,10 +249,10 @@ struct SanityChecksICC : public boost::static_visitor { boost::apply_visitor(*this, actor->base_solver); } #endif // P3M - void operator()(std::shared_ptr const &) const { + [[noreturn]] void operator()(std::shared_ptr const &) const { throw std::runtime_error("ICC does not work with DebyeHueckel."); } - void operator()(std::shared_ptr const &) const { + [[noreturn]] void operator()(std::shared_ptr const &) const { throw std::runtime_error("ICC does not work with ReactionField."); } }; diff --git a/src/core/electrostatics/specfunc.cpp b/src/core/electrostatics/specfunc.cpp index 5f2220a3fd4..79057995562 100644 --- a/src/core/electrostatics/specfunc.cpp +++ b/src/core/electrostatics/specfunc.cpp @@ -353,11 +353,11 @@ double LPK1(double x) { auto const tmp = exp(-x) / sqrt(x), xx = (16. / 3.) / x - 5. / 3.; return tmp * (xx * ak1_cs[1] + 0.5 * ak1_cs[0]); } - if (x > 2) { - int j = ak01_orders[((int)x) - 2]; + if (x > 2.) { + int j = ak01_orders[static_cast(x) - 2]; double x2; double *s1; - if (x <= 8) { + if (x <= 8.) { s1 = ak1_cs; x2 = (2. * 16. / 3.) / x - 2. * 5. / 3.; } else { @@ -416,11 +416,11 @@ std::pair LPK01(double x) { 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]; + if (x > 2.) { + int j = ak01_orders[static_cast(x) - 2]; double x2; double *s0, *s1; - if (x <= 8) { + if (x <= 8.) { s0 = ak0_cs; s1 = ak1_cs; x2 = (2. * 16. / 3.) / x - 2. * 5. / 3.; diff --git a/src/core/energy.cpp b/src/core/energy.cpp index 1bec058ccb1..6edd616a38d 100644 --- a/src/core/energy.cpp +++ b/src/core/energy.cpp @@ -107,9 +107,9 @@ std::shared_ptr calculate_energy() { #ifdef CUDA auto const energy_host = copy_energy_from_GPU(); if (!obs_energy.coulomb.empty()) - obs_energy.coulomb[1] += energy_host.coulomb; + obs_energy.coulomb[1] += static_cast(energy_host.coulomb); if (!obs_energy.dipolar.empty()) - obs_energy.dipolar[1] += energy_host.dipolar; + obs_energy.dipolar[1] += static_cast(energy_host.dipolar); #endif obs_energy.mpi_reduce(); diff --git a/src/core/errorhandling.cpp b/src/core/errorhandling.cpp index 6a50495282f..f6370dd11d0 100644 --- a/src/core/errorhandling.cpp +++ b/src/core/errorhandling.cpp @@ -37,16 +37,14 @@ #include namespace ErrorHandling { -namespace { /** RuntimeErrorCollector instance. * This is a unique pointer so we don't * leak on repeated calls of @ref init_error_handling. */ -std::unique_ptr runtimeErrorCollector; +static std::unique_ptr runtimeErrorCollector; /** The callback loop we are on. */ -Communication::MpiCallbacks *m_callbacks = nullptr; -} // namespace +static Communication::MpiCallbacks *m_callbacks = nullptr; void init_error_handling(Communication::MpiCallbacks &cb) { m_callbacks = &cb; diff --git a/src/core/errorhandling.hpp b/src/core/errorhandling.hpp index 7dc23c793c8..c2026a55419 100644 --- a/src/core/errorhandling.hpp +++ b/src/core/errorhandling.hpp @@ -53,7 +53,7 @@ class communicator; * @brief exit ungracefully, * core dump if switched on. */ -void errexit(); +[[noreturn]] void errexit(); /** * @brief Count runtime errors on all nodes. diff --git a/src/core/event.cpp b/src/core/event.cpp index 0c5a750158d..ae9071ac391 100644 --- a/src/core/event.cpp +++ b/src/core/event.cpp @@ -85,7 +85,7 @@ void on_program_start() { if (this_node == 0) { try { cuda_init(); - } catch (cuda_runtime_error const &err) { + } catch (cuda_runtime_error const &) { // pass } } diff --git a/src/core/galilei/ComFixed.hpp b/src/core/galilei/ComFixed.hpp index 86feee7e73b..6eee9135c57 100644 --- a/src/core/galilei/ComFixed.hpp +++ b/src/core/galilei/ComFixed.hpp @@ -90,10 +90,12 @@ class ComFixed { std::vector masses(m_type_index.size(), 0.0); /* Add contributions from all nodes and redistribute them to all. */ - boost::mpi::all_reduce(comm, local_forces.data(), local_forces.size(), - forces.data(), std::plus{}); - boost::mpi::all_reduce(comm, local_masses.data(), local_masses.size(), - masses.data(), std::plus{}); + boost::mpi::all_reduce(comm, local_forces.data(), + static_cast(local_forces.size()), forces.data(), + std::plus{}); + boost::mpi::all_reduce(comm, local_masses.data(), + static_cast(local_masses.size()), masses.data(), + std::plus{}); for (auto &p : particles) { /* Check if type is of interest */ diff --git a/src/core/grid_based_algorithms/halo.cpp b/src/core/grid_based_algorithms/halo.cpp index e800d2af3be..03291c95b18 100644 --- a/src/core/grid_based_algorithms/halo.cpp +++ b/src/core/grid_based_algorithms/halo.cpp @@ -218,8 +218,8 @@ void halo_communication(const HaloCommunicator &hc, char *const base) { for (int n = 0; n < hc.num; n++) { int s_node, r_node; int comm_type = hc.halo_info[n].type; - char *s_buffer = (char *)base + hc.halo_info[n].s_offset; - char *r_buffer = (char *)base + hc.halo_info[n].r_offset; + char *s_buffer = static_cast(base) + hc.halo_info[n].s_offset; + char *r_buffer = static_cast(base) + hc.halo_info[n].r_offset; switch (comm_type) { diff --git a/src/core/grid_based_algorithms/lb.cpp b/src/core/grid_based_algorithms/lb.cpp index b9c60828073..504f3088fb9 100644 --- a/src/core/grid_based_algorithms/lb.cpp +++ b/src/core/grid_based_algorithms/lb.cpp @@ -185,7 +185,7 @@ static LB_FluidData lbfluid_b; LB_Fluid lbfluid; /** Span of the velocity populations of the fluid (post-collision populations). */ -LB_Fluid lbfluid_post; +static LB_Fluid lbfluid_post; std::vector lbfields; diff --git a/src/core/grid_based_algorithms/lb_boundaries.cpp b/src/core/grid_based_algorithms/lb_boundaries.cpp index 14a1ff43b59..12a19f9d423 100644 --- a/src/core/grid_based_algorithms/lb_boundaries.cpp +++ b/src/core/grid_based_algorithms/lb_boundaries.cpp @@ -101,14 +101,14 @@ static void ek_init_boundaries() { if (ek_initialized) { host_wallcharge_species_density.resize(ek_parameters.number_of_nodes); for (auto &lbboundary : lbboundaries) { - if (lbboundary->charge_density() != 0.0) { + if (lbboundary->charge_density() != 0.0f) { charged_boundaries = 1; break; } } for (int n = 0; n < int(ek_parameters.number_of_species); n++) - if (ek_parameters.valency[n] != 0.0) { + if (ek_parameters.valency[n] != 0.0f) { wallcharge_species = n; break; } diff --git a/src/core/grid_based_algorithms/lb_interface.cpp b/src/core/grid_based_algorithms/lb_interface.cpp index 662a1c59b93..00f2ca356dd 100644 --- a/src/core/grid_based_algorithms/lb_interface.cpp +++ b/src/core/grid_based_algorithms/lb_interface.cpp @@ -399,7 +399,8 @@ void lb_lbfluid_set_tau(double tau) { } void check_tau_time_step_consistency(double tau, double time_step) { - auto const eps = std::numeric_limits::epsilon(); + // use float epsilon since tau may be a float (GPU LB) + auto const eps = static_cast(std::numeric_limits::epsilon()); if ((tau - time_step) / (tau + time_step) < -eps) throw std::invalid_argument("LB tau (" + std::to_string(tau) + ") must be >= MD time_step (" + @@ -654,7 +655,8 @@ void lb_lbfluid_print_velocity(const std::string &filename) { std::vector host_values(lbpar_gpu.number_of_nodes); lb_get_values_GPU(host_values.data()); auto const agrid = lb_lbfluid_get_agrid(); - auto const lattice_speed = lb_lbfluid_get_lattice_speed(); + auto const lattice_speed = + static_cast(lb_lbfluid_get_lattice_speed()); Utils::Vector3d pos; for (unsigned int j = 0; j < lbpar_gpu.number_of_nodes; ++j) { auto const k = j / lbpar_gpu.dim[0]; @@ -790,10 +792,10 @@ void lb_lbfluid_save_checkpoint(const std::string &filename, bool binary) { } } } - } catch (std::ios_base::failure const &fail) { + } catch (std::ios_base::failure const &) { cpfile.stream.close(); throw std::runtime_error(err_msg + "could not write data to " + filename); - } catch (std::runtime_error const &fail) { + } catch (std::runtime_error const &) { cpfile.stream.close(); throw; } @@ -862,14 +864,14 @@ void lb_lbfluid_load_checkpoint(const std::string &filename, bool binary) { if (cpfile.stream.peek() != EOF) { throw std::runtime_error(err_msg + "extra data found, expected EOF."); } - } catch (std::ios_base::failure const &fail) { + } catch (std::ios_base::failure const &) { auto const eof_error = cpfile.stream.eof(); cpfile.stream.close(); if (eof_error) { throw std::runtime_error(err_msg + "EOF found."); } throw std::runtime_error(err_msg + "incorrectly formatted data."); - } catch (std::runtime_error const &fail) { + } catch (std::runtime_error const &) { cpfile.stream.close(); throw; } diff --git a/src/core/grid_based_algorithms/lb_particle_coupling.cpp b/src/core/grid_based_algorithms/lb_particle_coupling.cpp index 63628ceb7ad..5e3b60f5bc3 100644 --- a/src/core/grid_based_algorithms/lb_particle_coupling.cpp +++ b/src/core/grid_based_algorithms/lb_particle_coupling.cpp @@ -43,7 +43,7 @@ #include #include -LB_Particle_Coupling lb_particle_coupling; +static LB_Particle_Coupling lb_particle_coupling; void mpi_bcast_lb_particle_coupling_local() { boost::mpi::broadcast(comm_cart, lb_particle_coupling, 0); diff --git a/src/core/grid_based_algorithms/lbgpu.cpp b/src/core/grid_based_algorithms/lbgpu.cpp index d0b075dad79..083327d0ce1 100644 --- a/src/core/grid_based_algorithms/lbgpu.cpp +++ b/src/core/grid_based_algorithms/lbgpu.cpp @@ -82,7 +82,9 @@ LB_parameters_gpu lbpar_gpu = { /** this is the array that stores the hydrodynamic fields for the output */ std::vector host_values(0); +#ifdef ELECTROKINETICS bool ek_initialized = false; +#endif /** (Re-)initialize the fluid according to the given value of rho. */ void lb_reinit_fluid_gpu() { diff --git a/src/core/immersed_boundary/ibm_triel.cpp b/src/core/immersed_boundary/ibm_triel.cpp index f15ff690e26..63426c01875 100644 --- a/src/core/immersed_boundary/ibm_triel.cpp +++ b/src/core/immersed_boundary/ibm_triel.cpp @@ -192,7 +192,7 @@ IBMTriel::calc_forces(Particle const &p1, Particle const &p2, // Rotate forces back into original position of triangle auto forces = RotateForces(f1_rot, f2_rot, vec1, vec2); - return forces; + return {forces}; } IBMTriel::IBMTriel(const int ind1, const int ind2, const int ind3, diff --git a/src/core/integrate.cpp b/src/core/integrate.cpp index a424f7c24d4..a14f1662ebf 100644 --- a/src/core/integrate.cpp +++ b/src/core/integrate.cpp @@ -407,7 +407,7 @@ int integrate(int n_steps, int reuse_forces) { // Verlet list statistics if (n_verlet_updates > 0) - verlet_reuse = n_steps / (double)n_verlet_updates; + verlet_reuse = n_steps / static_cast(n_verlet_updates); else verlet_reuse = 0; diff --git a/src/core/io/writer/h5md_core.cpp b/src/core/io/writer/h5md_core.cpp index aaed608bee4..a5763b786cc 100644 --- a/src/core/io/writer/h5md_core.cpp +++ b/src/core/io/writer/h5md_core.cpp @@ -60,7 +60,7 @@ static void backup_file(const std::string &from, const std::string &to) { try { boost::filesystem::copy_file( pfrom, pto, boost::filesystem::copy_option::fail_if_exists); - } catch (const boost::filesystem::filesystem_error &e) { + } catch (const boost::filesystem::filesystem_error &) { throw left_backupfile(); } } diff --git a/src/core/p3m/common.cpp b/src/core/p3m/common.cpp index b68a8972599..7d010576bd8 100644 --- a/src/core/p3m/common.cpp +++ b/src/core/p3m/common.cpp @@ -37,7 +37,8 @@ #include double p3m_analytic_cotangent_sum(int n, double mesh_i, int cao) { - auto const c = Utils::sqr(std::cos(Utils::pi() * mesh_i * (double)n)); + auto const c = + Utils::sqr(std::cos(Utils::pi() * mesh_i * static_cast(n))); auto res = 0.0; switch (cao) { diff --git a/src/core/p3m/fft.cpp b/src/core/p3m/fft.cpp index 2c1d193155c..aab0fb4e7ba 100644 --- a/src/core/p3m/fft.cpp +++ b/src/core/p3m/fft.cpp @@ -174,7 +174,7 @@ find_comm_groups(Utils::Vector3i const &grid1, Utils::Vector3i const &grid2, group[0] = n; c_pos--; } - return group; + return {group}; } /** Calculate the local fft mesh. Calculate the local mesh (@p loc_mesh) diff --git a/src/core/p3m/interpolation.hpp b/src/core/p3m/interpolation.hpp index ecb6334d082..a25b0d8f66b 100644 --- a/src/core/p3m/interpolation.hpp +++ b/src/core/p3m/interpolation.hpp @@ -153,7 +153,7 @@ p3m_calculate_interpolation_weights(const Utils::Vector3d &position, /* particle position in mesh coordinates */ auto const pos = ((position[d] - local_mesh.ld_pos[d]) * ai[d]) - pos_shift; - nmp[d] = (int)pos; + nmp[d] = static_cast(pos); /* distance to nearest mesh point */ dist[d] = (pos - nmp[d]) - 0.5; diff --git a/src/core/reaction_methods/tests/ReactionAlgorithm_test.cpp b/src/core/reaction_methods/tests/ReactionAlgorithm_test.cpp index 1ff6d22fbaf..adc96b10990 100644 --- a/src/core/reaction_methods/tests/ReactionAlgorithm_test.cpp +++ b/src/core/reaction_methods/tests/ReactionAlgorithm_test.cpp @@ -44,7 +44,7 @@ namespace espresso { // ESPResSo system instance -std::unique_ptr system; +static std::unique_ptr system; } // namespace espresso // Check the base class for all Monte Carlo algorithms. diff --git a/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp b/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp index 1c6934a87ba..7cda7dbead1 100644 --- a/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp +++ b/src/core/reaction_methods/tests/ReactionEnsemble_test.cpp @@ -45,8 +45,9 @@ namespace espresso { // ESPResSo system instance -std::unique_ptr system; +static 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) { diff --git a/src/core/unit_tests/EspressoSystemInterface_test.cpp b/src/core/unit_tests/EspressoSystemInterface_test.cpp index 5f7f4d09220..b9c45851bb3 100644 --- a/src/core/unit_tests/EspressoSystemInterface_test.cpp +++ b/src/core/unit_tests/EspressoSystemInterface_test.cpp @@ -38,7 +38,7 @@ #include namespace espresso { -auto &system = EspressoSystemInterface::Instance(); +static auto &system = EspressoSystemInterface::Instance(); } inline void check_uninitialized_device_pointers() { @@ -61,7 +61,7 @@ boost::test_tools::assertion_result has_gpu(boost::unit_test::test_unit_id) { try { cuda_check_device(); has_compatible_gpu = true; - } catch (cuda_runtime_error const &err) { + } catch (cuda_runtime_error const &) { } return has_compatible_gpu; } diff --git a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp index 617918717a3..5be9b7a2ec8 100644 --- a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp +++ b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp @@ -65,7 +65,7 @@ namespace utf = boost::unit_test; namespace espresso { // ESPResSo system instance -std::unique_ptr system; +static std::unique_ptr system; } // namespace espresso /** Decorator to run a unit test only on the head node. */ diff --git a/src/core/unit_tests/Particle_serialization_test.cpp b/src/core/unit_tests/Particle_serialization_test.cpp index 409e07f7f6b..53f7879fc6e 100644 --- a/src/core/unit_tests/Particle_serialization_test.cpp +++ b/src/core/unit_tests/Particle_serialization_test.cpp @@ -114,7 +114,7 @@ template