Skip to content

Commit 192a69d

Browse files
authored
Merge pull request #1 from jngrad/easyblock
ESPResSo 5.0-dev
2 parents bf0ac9a + d879c93 commit 192a69d

File tree

5 files changed

+320
-1
lines changed

5 files changed

+320
-1
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
# dev.eessi.io-espresso
1+
# dev.eessi.io-espresso
2+
3+
Repository for pre-release builds of [ESPResSo](https://github.com/espressomd/espresso).
4+
Builds are deployed to the [`dev.eessi.io`](https://www.eessi.io/docs/repositories/dev.eessi.io/) repository.
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
easyblock = 'EB_ESPResSo'
2+
3+
name = 'ESPResSo'
4+
version = '%(software_commit)s'
5+
6+
homepage = 'https://espressomd.org/wordpress'
7+
description = """A software package for Molecular Dynamics and Monte Carlo
8+
simulations of bead-spring models, with electrostatics, magnetostatics,
9+
hydrodynamics and reaction-diffusion-advection solvers."""
10+
11+
sources = [
12+
{
13+
'source_urls': ['https://github.com/espressomd/espresso/archive/'],
14+
'filename': 'espresso-%(software_commit)s.tar.gz',
15+
'download_filename': '%(software_commit)s.tar.gz',
16+
},
17+
{
18+
'source_urls': ['https://i10git.cs.fau.de/api/v4/projects/walberla%2Fwalberla/repository/archive?sha=59c9b8b1#'],
19+
'filename': 'walberla-59c9b8b1.tar.gz',
20+
},
21+
{
22+
'source_urls': ['https://github.com/icl-utk-edu/heffte/archive/'],
23+
'filename': 'heffte-2.4.1.tar.gz',
24+
'download_filename': 'v2.4.1.tar.gz',
25+
},
26+
{
27+
'source_urls': ['https://github.com/kokkos/kokkos/archive/'],
28+
'filename': 'kokkos-18b830e3360dff9f44a9a9c729ca9e74c037e354.tar.gz',
29+
'download_filename': '18b830e3360dff9f44a9a9c729ca9e74c037e354.tar.gz',
30+
},
31+
{
32+
'source_urls': ['https://github.com/ECP-copa/Cabana/archive/'],
33+
'filename': 'Cabana-ebfaa51.tar.gz',
34+
'download_filename': 'ebfaa51.tar.gz',
35+
},
36+
{
37+
'source_urls': ['https://github.com/highfive-devs/highfive/archive/'],
38+
'filename': 'highfive-0103467c3b609628fe3b892c6a85915610f2f3d2.tar.gz',
39+
'download_filename': '0103467.tar.gz',
40+
},
41+
]
42+
checksums = [{
43+
'espresso-%(software_commit)s.tar.gz': '73d71511eca932adcff4f33ac778c77279e97cec91ea1ac53987ba488fc1288d',
44+
'walberla-59c9b8b1.tar.gz': 'a709d7299f2c06143946d9bb8033d4ddbb0cf8b9142e4bbaa15fecc78463038f',
45+
'heffte-2.4.1.tar.gz': 'de2cf26df5d61baac7841525db3f393cb007f79612ac7534fd4757f154ba3e6c',
46+
'kokkos-18b830e.tar.gz': 'dc0127134f47752f61e74c77237bd9ec560535c4283fef8c9643f947b3733063',
47+
'Cabana-ebfaa51.tar.gz': 'fe5b1b1d419662b29a80cbd6703994b28c942ccbee3b201eca87e48a13836350',
48+
'highfive-0103467.tar.gz': 'd0ce87abd07cdd676c6d6069d3b05f869138d115d8e22b935c9bd43c872c1f3d',
49+
}]
50+
patches = [
51+
'espresso.patch',
52+
]
53+
54+
toolchain = {'name': 'foss', 'version': '2023b'}
55+
toolchainopts = {'usempi': True, 'pic': True}
56+
57+
builddependencies = [
58+
('CMake', '3.27.6'),
59+
('Ninja', '1.11.1'),
60+
]
61+
62+
dependencies = [
63+
('Python', '3.11.5'),
64+
('SciPy-bundle', '2023.11'),
65+
('Boost.MPI', '1.83.0'),
66+
('Mesa', '23.1.9'),
67+
('GSL', '2.7'),
68+
('IPython', '8.17.2'),
69+
('Pint', '0.24'),
70+
('HDF5', '1.14.3'),
71+
#('VTK', '9.3.0'), # error: libpython3.10.so.1.0: file not found
72+
#('PFFT', '20181230'), # not available in this toolchain
73+
#('CUDA', '12.1.1', '', SYSTEM), # deferred for now
74+
]
75+
76+
# default CUDA compute capabilities to use (override via --cuda-compute-capabilities)
77+
if any(x[0] == 'CUDA' for x in dependencies):
78+
cuda_compute_capabilities = ['5.2', '6.0', '7.0', '7.5', '8.0', '8.6', '9.0']
79+
80+
configopts = f' -DESPRESSO_BUILD_TESTS=ON '
81+
# make sure the right Python is used (note: -DPython3_EXECUTABLE or -DPython_EXECUTABLE does not work!)
82+
configopts += ' -D PYTHON_EXECUTABLE=$EBROOTPYTHON/bin/python '
83+
configopts += ' -DCMAKE_INSTALL_LIBDIR:PATH=lib '
84+
# workaround for https://gitlab.kitware.com/cmake/cmake/-/issues/22678
85+
# (this only affects testsuite executable files in the build folder)
86+
_exe_linker_flags = ':'.join(f'%(builddir)s/easybuild_obj/_deps/{path}'
87+
for path in ['kokkos-build/containers/src',
88+
'kokkos-build/core/src',
89+
'kokkos-build/simd/src',
90+
'heffte-build'])
91+
configopts += f' -DCMAKE_EXE_LINKER_FLAGS="-Wl,-rpath-link,{_exe_linker_flags}" '
92+
93+
test_cmd = 'ninja'
94+
runtest = f'check_unit_tests && {test_cmd} check_python'
95+
96+
modextrapaths = {'PYTHONPATH': ['lib/python%(pyshortver)s/site-packages']}
97+
98+
_binaries = ['ipypresso', 'pypresso']
99+
_libs = [
100+
# ESPResSo
101+
'espresso_core', 'espresso_shapes', 'espresso_walberla',
102+
'espresso_script_interface', 'script_interface', 'utils', '_init',
103+
# waLBerla
104+
'libwalberla_core', 'libwalberla_executiontree', 'libwalberla_timeloop',
105+
'libwalberla_field', 'libwalberla_blockforest', 'libwalberla_geometry',
106+
'libwalberla_lbm', 'libwalberla_vtk', 'libwalberla_domain_decomposition',
107+
'libwalberla_boundary', 'liblodepng',
108+
# Kokkos
109+
'libkokkoscontainers', 'libkokkoscore', 'libkokkossimd',
110+
# heFFte
111+
'libheffte',
112+
]
113+
_python_modules = [
114+
'__init__.py', 'system.py', 'version.py', 'collision_detection.py', 'lb.py',
115+
'accumulators.py', 'constraints.py', 'observables.py', 'particle_data.py',
116+
]
117+
if any(x[0] == 'PFFT' for x in dependencies):
118+
_libs.append('libwalberla_fft')
119+
if any(x[0] == 'HDF5' for x in dependencies):
120+
_libs.append('espresso_hdf5')
121+
_python_modules.append('io/writer/h5md.py')
122+
if any(x[0] == 'CUDA' for x in dependencies):
123+
_libs += [
124+
'espresso_cuda', 'espresso_walberla_cuda', 'libwalberla_gpu',
125+
]
126+
_python_modules.append('cuda_init.py')
127+
128+
_lib_path = 'lib/python%(pyshortver)s/site-packages/espressomd'
129+
sanity_check_paths = {
130+
'files': [f'bin/{x}' for x in _binaries] +
131+
[f'{_lib_path}/{x}.{SHLIB_EXT}' for x in _libs] +
132+
[f'{_lib_path}/{x}' for x in _python_modules],
133+
'dirs': ['bin', 'lib']
134+
}
135+
136+
sanity_check_commands = [
137+
'pypresso -h', 'ipypresso -h',
138+
'pypresso -c "import espressomd.version;print(espressomd.version.friendly())"',
139+
'python3 -c "import espressomd.version;print(espressomd.version.friendly())"',
140+
]
141+
142+
moduleclass = 'chem'
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Cython 3.0.4 is supported, although it generates many warnings
2+
--- CMakeLists.txt
3+
+++ CMakeLists.txt
4+
@@ -245,3 +245,3 @@
5+
find_package(Python 3.11 REQUIRED COMPONENTS Interpreter Development NumPy)
6+
- find_package(Cython 3.0.8...<3.1.0 REQUIRED)
7+
+ find_package(Cython 3.0.4...<3.1.0 REQUIRED)
8+
find_program(IPYTHON_EXECUTABLE NAMES jupyter ipython3 ipython)
9+
# skip fragile test which may crash hdf5 depending on how it was built
10+
--- testsuite/python/CMakeLists.txt
11+
+++ testsuite/python/CMakeLists.txt
12+
@@ -386,3 +386,2 @@
13+
python_test(FILE h5md.py MAX_NUM_PROC 2)
14+
-python_test(FILE h5md.py MAX_NUM_PROC 1 SUFFIX 1_core)
15+
python_test(FILE p3m_fft.py MAX_NUM_PROC 6)

easyconfigs/e/ESPResSo/espresso.py

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
#
2+
# Copyright 2025 Jean-Noël Grad
3+
#
4+
# This code is free software: you can redistribute it and/or modify
5+
# it under the terms of the GNU General Public License as published by
6+
# the Free Software Foundation, either version 2 of the License, or
7+
# (at your option) any later version.
8+
#
9+
# This code is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# GNU General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU General Public License
15+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
#
17+
18+
"""
19+
EasyBuild support for ESPResSo, implemented as an easyblock.
20+
21+
@author: Jean-Noël Grad (University of Stuttgart)
22+
"""
23+
24+
import os
25+
import re
26+
import shutil
27+
from easybuild.easyblocks.generic.cmakeninja import CMakeNinja
28+
from easybuild.tools.systemtools import get_cpu_architecture, get_cpu_features
29+
from easybuild.tools.systemtools import X86_64
30+
from easybuild.tools.utilities import trace_msg
31+
from easybuild.tools.build_log import print_error
32+
33+
34+
class EB_ESPResSo(CMakeNinja):
35+
"""Support for building and installing ESPResSo."""
36+
37+
def _get_extracted_tarball_paths(self):
38+
"""
39+
Locate the source code of all dependencies.
40+
"""
41+
extracted_paths = {}
42+
for src in self.src:
43+
name = src['name'].split('-', 1)[0]
44+
# process main software
45+
if name == 'espresso':
46+
extracted_paths['espresso'] = src['finalpath']
47+
continue
48+
# process dependencies
49+
tarball = src['name']
50+
if not tarball.endswith('.tar.gz'):
51+
raise ValueError(tarball + ' is not a tar.gz file')
52+
prefix = tarball.rsplit('.', 2)[0]
53+
matches = [x for x in os.listdir(src['finalpath']) if x.startswith(prefix)]
54+
if len(matches) == 0:
55+
raise RuntimeError(tarball + ' was not extracted')
56+
if len(matches) > 1:
57+
raise RuntimeError(tarball + ' matches multiple folders: ' + str(matches))
58+
extracted_paths[name] = os.path.join(src['finalpath'], matches[0])
59+
return extracted_paths
60+
61+
def _patch_fetchcontent(self):
62+
"""
63+
Modify CMake ``FetchContent_Declare`` blocks to point to the folders
64+
containing the already-downloaded dependencies rather than to URLs.
65+
This avoids a download step during configuration.
66+
"""
67+
extracted_paths = self._get_extracted_tarball_paths()
68+
cmakelists_path = os.path.join(extracted_paths['espresso'], 'CMakeLists.txt')
69+
with open(cmakelists_path, 'r') as f:
70+
content = f.read()
71+
for name, local_uri in extracted_paths.items():
72+
if name == 'espresso':
73+
continue
74+
pattern = fr'FetchContent_Declare\(\s*{name}\s+GIT_REPOSITORY\s+\S+\s+GIT_TAG\s+\S+(?=\s|\))'
75+
m = re.search(pattern, content, flags=re.IGNORECASE)
76+
if m is None:
77+
raise RuntimeError(f'{name} is not part of the ESPResSo FetchContent workflow')
78+
content = re.sub(pattern, f'FetchContent_Declare({name} URL {local_uri}', content, flags=re.IGNORECASE)
79+
with open(cmakelists_path, 'w') as f:
80+
f.write(content)
81+
82+
def configure_step(self):
83+
# patch FetchContent to avoid re-downloading dependencies
84+
self._patch_fetchcontent()
85+
86+
configopts = self.cfg.get('configopts', '')
87+
dependencies = self.cfg.get('dependencies', [])
88+
89+
cpu_features = get_cpu_features()
90+
with_cuda = any(x.get('name', '') == 'CUDA' for x in dependencies)
91+
with_pfft = any(x.get('name', '') == 'PFFT' for x in dependencies)
92+
with_hdf5 = any(x.get('name', '') == 'HDF5' for x in dependencies)
93+
with_gsl = any(x.get('name', '') == 'GSL' for x in dependencies)
94+
95+
if with_cuda:
96+
configopts += ' -DESPRESSO_BUILD_WITH_CUDA=ON'
97+
else:
98+
configopts += ' -DESPRESSO_BUILD_WITH_CUDA=OFF'
99+
if with_hdf5:
100+
configopts += ' -DESPRESSO_BUILD_WITH_HDF5=ON'
101+
else:
102+
configopts += ' -DESPRESSO_BUILD_WITH_HDF5=OFF'
103+
if with_gsl:
104+
configopts += ' -DESPRESSO_BUILD_WITH_GSL=ON'
105+
else:
106+
configopts += ' -DESPRESSO_BUILD_WITH_GSL=OFF'
107+
108+
configopts += ' -DESPRESSO_BUILD_WITH_WALBERLA=ON'
109+
if with_pfft:
110+
configopts += ' -DESPRESSO_BUILD_WITH_WALBERLA_FFT=ON'
111+
else:
112+
configopts += ' -DESPRESSO_BUILD_WITH_WALBERLA_FFT=OFF'
113+
if get_cpu_architecture() == X86_64 and 'avx2' in cpu_features:
114+
configopts += ' -DESPRESSO_BUILD_WITH_WALBERLA_AVX=ON'
115+
116+
configopts += ' -DESPRESSO_BUILD_WITH_SHARED_MEMORY_PARALLELISM=ON'
117+
configopts += ' -DESPRESSO_BUILD_WITH_FFTW=ON'
118+
119+
self.cfg['configopts'] = configopts
120+
121+
return super(EB_ESPResSo, self).configure_step()
122+
123+
def _cleanup_aux_files(self):
124+
"""
125+
Remove files automatically installed by CMake outside the ESPResSo
126+
main directory: header files, config files, duplicated shared objects.
127+
"""
128+
def delete_dir(path):
129+
if os.path.isdir(path):
130+
trace_msg(f'removing directory \'%s\'' % path.replace(f'{self.installdir}/', ''))
131+
shutil.rmtree(path)
132+
133+
def delete_file(path):
134+
if os.path.isfile(path) or os.path.islink(path):
135+
trace_msg(f'removing file \'%s\'' % path.replace(f'{self.installdir}/', ''))
136+
os.remove(path)
137+
138+
lib_dir = f'{self.installdir}/lib'
139+
if os.path.isdir(f'{self.installdir}/lib64'):
140+
lib_dir = f'{self.installdir}/lib64'
141+
delete_dir(f'{self.installdir}/include')
142+
delete_dir(f'{self.installdir}/share')
143+
delete_dir(f'{self.installdir}/walberla')
144+
delete_dir(f'{lib_dir}/cmake')
145+
for path in os.listdir(lib_dir):
146+
if '.so' in path:
147+
delete_file(f'{lib_dir}/{path}')
148+
149+
def post_processing_step(self):
150+
try:
151+
self._cleanup_aux_files()
152+
except Exception as err:
153+
print_error("Failed to remove some auxiliary files (easyblock: %s): %s" % (self.__class__.__name__, str(err)))
154+
return super(EB_ESPResSo, self).post_processing_step()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
easyconfigs:
2+
- ESPResSo-foss-2023b-software-commit.eb:
3+
options:
4+
software-commit: 8aa60cecd56cdd10ab62042c567552f347374f36 # waLBerla coupling and OpenMP support
5+
include-easyblocks: easyconfigs/*/*/*.py

0 commit comments

Comments
 (0)