Skip to content

Commit

Permalink
Bump Python requirements
Browse files Browse the repository at this point in the history
  • Loading branch information
jngrad committed Feb 7, 2025
1 parent 9973192 commit 912507b
Show file tree
Hide file tree
Showing 14 changed files with 74 additions and 60 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ endif()

# Python interpreter and Cython interface library
if(ESPRESSO_BUILD_WITH_PYTHON)
find_package(Python 3.10 REQUIRED COMPONENTS Interpreter Development NumPy)
find_package(Python 3.11 REQUIRED COMPONENTS Interpreter Development NumPy)
find_package(Cython 0.29.28...<3.0.12 REQUIRED)
find_program(IPYTHON_EXECUTABLE NAMES jupyter ipython3 ipython)
endif()
Expand Down
4 changes: 2 additions & 2 deletions doc/sphinx/appendix.rst
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ 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
For an infinite 2D or 3D ionic crystal lattice, the Madelung constant can be
obtained in a numerical simulation with the Evjen method :cite:`evjen32a` or
the Ewald method :cite:`ewald21a`.

Expand All @@ -527,6 +527,6 @@ with :math:`M` the orientation-dependent 1D Madelung constant,
: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
For an infinite 2D or 3D magnetic crystal lattice, the Madelung constant for
the maximal energy and minimal energy dipole orientation can be estimated
in a numerical simulation :cite:`batle20a`.
2 changes: 1 addition & 1 deletion doc/sphinx/inter_non-bonded.rst
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ the normal LJ potential is recovered for :math:`b_1=b_2=4`,
The optional ``LJGEN_SOFTCORE`` feature activates a softcore version of
the potential, where the following transformations apply:
:math:`\epsilon \rightarrow \lambda \epsilon` and
:math:`r-r_\mathrm{off} \rightarrow \sqrt{(r-r_\mathrm{off})^2 +
:math:`(r-r_\mathrm{off}) \rightarrow \sqrt{(r-r_\mathrm{off})^2 +
(1-\lambda) \delta \sigma^2}`. :math:`\lambda` allows to tune the strength of the
interaction, while :math:`\delta` varies how smoothly the potential goes to zero as
:math:`\lambda\rightarrow 0`. Such a feature allows one to perform
Expand Down
2 changes: 1 addition & 1 deletion doc/tutorials/langevin_dynamics/langevin_dynamics.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@
"source": [
"We find that the velocity-autocorrelation function quickly decays towards zero. However, owing to the relatively short overall sampling time, only the first part of the correlation function is well-sampled and a lot of noise is found in the tail of the autocorrelation function already early on. The obvious solution would be to increase the sampling time and in a production setting one would definitely have to do so in order to smoothly resolve at least several relaxation times. However, depending on a system's characteristics, under certain conditions it might still be necessary to replace a noisy long-time tail with an analytical expression, fitted to the short-time part of the autocorrelation function (again over at least several decay times; typically one would smoothly transition between numerical short-time data and the analytical tail-fit).\n",
"\n",
"A perfect smoothly sampled autocorrelation function could be integrated numerically, using e.g. [<tt>numpy.trapz</tt>](https://numpy.org/doc/stable/reference/generated/numpy.trapz.html).\n",
"A perfect smoothly sampled autocorrelation function could be integrated numerically, using e.g. [<tt>scipy.integrate.trapezoid</tt>](https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.trapezoid.html).\n",
"Here, however, we will use the initial part of the velocity-autocorrelation function to obtain a fully analytic data representation. For a Brownian particle the velocity-autocorrelation is expected to follow a simple exponential decay.\n",
"\n",
"Write a Python-function for the exponential decay. Fit your decay-function to the (short-time) correlation data and create a plot to visually verify that the analytic fits are indeed good representations of the data (the exponential decay should be a perfect match in the smooth region of the correlation function). You can copy and modify the plot script given above.\n",
Expand Down
19 changes: 3 additions & 16 deletions testsuite/python/constraint_shape_based.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,6 @@ def test_cylinder(self):
self.assertAlmostEqual(
-1.0 * outer_cylinder_wall.total_force()[1],
tests_common.lj_force(
espressomd,
cutoff=2.0,
offset=0.,
epsilon=1.0,
Expand All @@ -445,7 +444,6 @@ def test_cylinder(self):
outer_cylinder_wall.total_normal_force(),
2 *
tests_common.lj_force(
espressomd,
cutoff=2.0,
offset=0.,
epsilon=1.0,
Expand Down Expand Up @@ -545,7 +543,6 @@ def test_spherocylinder(self):
self.assertAlmostEqual(
-1.0 * outer_cylinder_constraint.total_force()[1],
tests_common.lj_force(
espressomd,
cutoff=2.0,
offset=0.,
epsilon=1.0,
Expand All @@ -563,8 +560,8 @@ def test_spherocylinder(self):
self.assertAlmostEqual(outer_cylinder_constraint.total_force()[2], 0.0)
self.assertAlmostEqual(outer_cylinder_constraint.total_normal_force(),
2 * tests_common.lj_force(
espressomd, cutoff=2.0, offset=0.,
epsilon=1.0, sigma=1.0, r=dist_part2))
cutoff=2.0, offset=0., epsilon=1.0,
sigma=1.0, r=dist_part2))

# Reset
system.part.clear()
Expand Down Expand Up @@ -662,7 +659,6 @@ def test_wall_forces(self):
self.assertAlmostEqual(
p.f[1],
tests_common.lj_force(
espressomd,
cutoff=2.0,
offset=0.,
epsilon=1.0,
Expand All @@ -672,7 +668,6 @@ def test_wall_forces(self):
self.assertAlmostEqual(
p.f[2],
tests_common.lj_force(
espressomd,
cutoff=2.0,
offset=0.,
epsilon=1.5,
Expand All @@ -684,7 +679,6 @@ def test_wall_forces(self):
self.assertAlmostEqual(
-1.0 * wall_xz.total_force()[1],
tests_common.lj_force(
espressomd,
cutoff=2.0,
offset=0.,
epsilon=1.0,
Expand All @@ -694,7 +688,6 @@ def test_wall_forces(self):
self.assertAlmostEqual(
-1.0 * wall_xy.total_force()[2],
tests_common.lj_force(
espressomd,
cutoff=2.0,
offset=0.,
epsilon=1.5,
Expand All @@ -706,7 +699,6 @@ def test_wall_forces(self):
self.assertAlmostEqual(
wall_xy.total_normal_force(),
tests_common.lj_force(
espressomd,
cutoff=2.0,
offset=0.,
epsilon=1.5,
Expand Down Expand Up @@ -818,7 +810,6 @@ def test_rhomboid(self):
self.assertAlmostEqual(
-p.f[2],
tests_common.lj_force(
espressomd,
cutoff=2.,
offset=0.,
epsilon=1.,
Expand All @@ -828,7 +819,6 @@ def test_rhomboid(self):
self.assertAlmostEqual(
rhomboid_constraint.total_normal_force(),
tests_common.lj_force(
espressomd,
cutoff=2.,
offset=0.,
epsilon=1.,
Expand Down Expand Up @@ -918,7 +908,6 @@ def test_rhomboid(self):
self.assertAlmostEqual(
rhomboid_constraint.total_normal_force(),
tests_common.lj_force(
espressomd,
cutoff=2.,
offset=0.,
epsilon=1.,
Expand All @@ -933,7 +922,6 @@ def test_rhomboid(self):
self.assertAlmostEqual(
rhomboid_constraint.total_normal_force(),
tests_common.lj_force(
espressomd,
cutoff=2.,
offset=0.,
epsilon=1.,
Expand Down Expand Up @@ -980,7 +968,6 @@ def test_torus(self):
self.assertAlmostEqual(
torus_wall.total_force()[1],
tests_common.lj_force(
espressomd,
cutoff=2.0,
offset=0.,
epsilon=1.0,
Expand All @@ -996,7 +983,7 @@ def test_torus(self):

self.assertAlmostEqual(torus_wall.total_force()[1], 0.0)
self.assertAlmostEqual(torus_wall.total_normal_force(), 2 * tests_common.lj_force(
espressomd, cutoff=2.0, offset=0., epsilon=1.0, sigma=1.0,
cutoff=2.0, offset=0., epsilon=1.0, sigma=1.0,
r=radius - tube_radius - part_offset))

# Test the geometry of the shape directly
Expand Down
3 changes: 2 additions & 1 deletion testsuite/python/integrator_langevin_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import unittest as ut
import unittest_decorators as utx
import numpy as np
import scipy.integrate

import espressomd
import espressomd.accumulators
Expand Down Expand Up @@ -123,7 +124,7 @@ def verify_diffusion(self, p, corr, kT, gamma):

# Integrate with trapezoidal rule
for i in range(3):
I = np.trapz(acf[:, p.id, i], tau)
I = scipy.integrate.trapezoid(acf[:, p.id, i], tau)
ratio = I / (kT / gamma[i])
self.assertAlmostEqual(ratio, 1., delta=0.07)

Expand Down
2 changes: 2 additions & 0 deletions testsuite/python/interactions_bonded.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,8 @@ def run_test(self, bond_instance, force_func, energy_func, min_dist,
p2.pos = p1.pos + self.axis * cutoff * 1.01
with self.assertRaisesRegex(Exception, r"while calling method integrate\(\)"):
self.system.integrator.run(recalc_forces=True, steps=0)
with self.assertRaisesRegex(Exception, r"while calling method calculate_energy\(\)"):
self.system.analysis.energy()["total"]
if test_same_pos_exception:
p2.pos = p1.pos
with self.assertRaises(Exception):
Expand Down
69 changes: 45 additions & 24 deletions testsuite/python/interactions_non-bonded.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ def lj_cos_potential(r, epsilon, sigma, cutoff, offset):
return V


def lj_cos_force(espressomd, r, epsilon, sigma, cutoff, offset):
def lj_cos_force(r, epsilon, sigma, cutoff, offset):
f = 0.
r_min = offset + np.power(2., 1. / 6.) * sigma
r_cut = cutoff + offset
if r < r_min:
f = tests_common.lj_force(espressomd, r, epsilon=epsilon, sigma=sigma,
f = tests_common.lj_force(r, epsilon=epsilon, sigma=sigma,
cutoff=cutoff, offset=offset)
elif r < r_cut:
alpha = np.pi / \
Expand All @@ -78,12 +78,12 @@ def lj_cos2_potential(r, epsilon, sigma, offset, width):
return V


def lj_cos2_force(espressomd, r, epsilon, sigma, offset, width):
def lj_cos2_force(r, epsilon, sigma, offset, width):
f = 0.
r_min = offset + np.power(2., 1. / 6.) * sigma
r_cut = r_min + width
if r < r_min:
f = tests_common.lj_force(espressomd, r, epsilon=epsilon, sigma=sigma,
f = tests_common.lj_force(r, epsilon=epsilon, sigma=sigma,
cutoff=r_cut, offset=offset)
elif r < r_cut:
f = - np.pi * epsilon * \
Expand Down Expand Up @@ -304,8 +304,7 @@ def test_lj_generic(self):
params,
force_kernel=tests_common.lj_generic_force,
energy_kernel=tests_common.lj_generic_potential,
n_steps=231,
force_kernel_needs_espressomd=True)
n_steps=231)

params["shift"] = "auto"
obj = espressomd.interactions.GenericLennardJonesInteraction(**params)
Expand All @@ -317,6 +316,31 @@ def test_lj_generic(self):
obj = espressomd.interactions.GenericLennardJonesInteraction(**params)
self.assertEqual(obj.shift, 0.)

def test_lj_generic_reference_potential(self):

class HasFeatures:
"""Mock class to override ``espressomd.has_features()``."""

def __init__(self, feature_name, has_feature):
self.feature_name = feature_name
self.has_feature = has_feature

def has_features(self, feature_name):
assert isinstance(feature_name, str)
result = espressomd.has_features(feature_name)
if feature_name == self.feature_name:
result = self.has_feature
return result

params = {
"r": 1., "epsilon": 1., "sigma": 1., "cutoff": 5., "offset": 2.}
lj_gen_force_with_softcore = tests_common.lj_generic_force(
espressomd=HasFeatures("LJGEN_SOFTCORE", True), **params)
lj_gen_force_without_softcore = tests_common.lj_generic_force(
espressomd=HasFeatures("LJGEN_SOFTCORE", False), **params)
self.assertAlmostEqual(lj_gen_force_with_softcore, -24., delta=1e-7)
self.assertAlmostEqual(lj_gen_force_without_softcore, 24., delta=1e-7)

# Test WCA Potential
@utx.skipIfMissingFeatures("WCA")
def test_wca(self):
Expand All @@ -333,8 +357,7 @@ def test_wca(self):
espressomd, r, epsilon=epsilon, sigma=sigma, cutoff=wca_cutoff),
energy_kernel=lambda r, epsilon, sigma: tests_common.lj_generic_potential(
r, epsilon=epsilon, sigma=sigma, cutoff=wca_cutoff, shift=4. * wca_shift),
n_steps=231,
force_kernel_needs_espressomd=True)
n_steps=231)

# Test Generic Lennard-Jones Softcore Potential
@utx.skipIfMissingFeatures("LJGEN_SOFTCORE")
Expand All @@ -354,8 +377,7 @@ def test_lj_generic_softcore(self):
"lam": 0.34},
force_kernel=tests_common.lj_generic_force,
energy_kernel=tests_common.lj_generic_potential,
n_steps=231,
force_kernel_needs_espressomd=True)
n_steps=231)

# Test Lennard-Jones Potential
@utx.skipIfMissingFeatures("LENNARD_JONES")
Expand All @@ -368,8 +390,7 @@ def test_lj(self):
"shift": 0.92},
force_kernel=tests_common.lj_force,
energy_kernel=tests_common.lj_potential,
n_steps=113,
force_kernel_needs_espressomd=True)
n_steps=113)

# Test Lennard-Jones Cosine Potential
@utx.skipIfMissingFeatures("LJCOS")
Expand All @@ -382,8 +403,7 @@ def test_lj_cos(self):
"offset": 0.223},
force_kernel=lj_cos_force,
energy_kernel=lj_cos_potential,
n_steps=175,
force_kernel_needs_espressomd=True)
n_steps=175)

# Test Lennard-Jones Cosine^2 Potential
@utx.skipIfMissingFeatures("LJCOS2")
Expand All @@ -396,8 +416,7 @@ def test_lj_cos2(self):
"offset": 0.321},
force_kernel=lj_cos2_force,
energy_kernel=lj_cos2_potential,
n_steps=267,
force_kernel_needs_espressomd=True)
n_steps=267)

# Test Smooth-step Potential
@utx.skipIfMissingFeatures("SMOOTH_STEP")
Expand Down Expand Up @@ -456,8 +475,7 @@ def test_buckingham(self):
"shift": 0.133},
force_kernel=buckingham_force,
energy_kernel=buckingham_potential,
n_steps=226,
force_kernel_remove_shift=False)
n_steps=226)

# Test Soft-sphere Potential
@utx.skipIfMissingFeatures("SOFT_SPHERE")
Expand Down Expand Up @@ -640,20 +658,23 @@ def get_reference_torque(gb_params, r, dir1, dir2):
self.assertEqual(self.system.analysis.energy()["non_bonded"], 0.0)

def run_test(self, name, parameters, force_kernel,
energy_kernel, n_steps, n_initial_steps=0,
force_kernel_needs_espressomd=False,
force_kernel_remove_shift=True):
energy_kernel, n_steps, n_initial_steps=0):

getattr(self.system.non_bonded_inter[0, 0], name).set_params(
**parameters)
p0, p1 = self.system.part.all()
p1.pos = p0.pos + self.step * n_initial_steps

force_parameters = parameters.copy()
if "shift" in force_parameters and force_kernel_remove_shift:
energy_parameters = parameters.copy()
force_kernel_varnames = force_kernel.__code__.co_varnames
energy_kernel_varnames = energy_kernel.__code__.co_varnames
if "shift" in parameters and "shift" not in force_kernel_varnames:
del force_parameters["shift"]
if force_kernel_needs_espressomd:
if "espressomd" in force_kernel_varnames:
force_parameters["espressomd"] = espressomd
if "espressomd" in energy_kernel_varnames:
energy_parameters["espressomd"] = espressomd

for _ in range(n_steps):
p1.pos = p1.pos + self.step
Expand All @@ -662,7 +683,7 @@ def run_test(self, name, parameters, force_kernel,

# Calculate energies
E_sim = self.system.analysis.energy()["non_bonded"]
E_ref = energy_kernel(r=d, **parameters)
E_ref = energy_kernel(r=d, **energy_parameters)

# Calculate forces
f0_sim = np.copy(p0.f)
Expand Down
5 changes: 3 additions & 2 deletions testsuite/python/lb_pressure_tensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@
import unittest as ut
import unittest_decorators as utx
import numpy as np
# import scipy.optimize
# import scipy.integrate

import espressomd
import espressomd.lb
# import scipy.optimize

N_CELLS = 12

Expand Down Expand Up @@ -195,7 +196,7 @@ def test_gk_viscosity(self):
# integrate first part numerically, fit exponential to tail
t_max_fit = 50 * tau
ts = np.arange(0, t_max_fit, 2 * tau)
numeric_integral = np.trapz(acf[:len(ts)], dx=2 * self.params["tau"])
numeric_integral = scipy.integrate.trapezoid(acf[:len(ts)], dx=2 * self.params["tau"])
# fit tail
def fit(x, a, b): return a * np.exp(-b * x)
Expand Down
2 changes: 1 addition & 1 deletion testsuite/python/p3m_madelung.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
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
lattices. See user guide sections :ref:`Madelung electrostatics` and
:ref:`Madelung magnetostatics` for more details.
"""

Expand Down
Loading

0 comments on commit 912507b

Please sign in to comment.