Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 77 additions & 31 deletions easybuild/easyblocks/g/gromacs.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ def extra_options():
'ignore_plumed_version_check': [False, "Ignore the version compatibility check for PLUMED", CUSTOM],
'plumed': [None, "Try to apply PLUMED patches. None (default) is auto-detect. " +
"True or False forces behaviour.", CUSTOM],
'parallel_test': [None, "Number of tests to run in parallel. Can help with hanging tests caused by " +
"oversubscribed MPI processes. With None (default) the value of the parallel value " +
"is used.", CUSTOM],
})
return extra_vars

Expand Down Expand Up @@ -155,6 +158,73 @@ def is_double_precision_cuda_build(self):
cuda = get_software_root('CUDA')
return cuda and self.double_prec_pattern in self.cfg['configopts']

def is_using_plumed_kernel(self):
gromacs_version = LooseVersion(self.version)

return not gromacs_version < '2025'

def is_cmake_shared_lib_plumed_patch_supported(self):
gromacs_version = LooseVersion(self.version)

return gromacs_version >= '5.1'

def configure_runtime_loading_of_plumed(self):
# Since 2025.0 GROMACS can use a subset of PLUMED interface by loading the PLUMED kernel library at runtime

# 1. Enable building with the nativelly supported PLUMED functions:
# https://manual.gromacs.org/documentation/2025.0/install-guide/index.html#building-with-plumed-support
self.cfg.update('configopts', "-DGMX_USE_PLUMED=ON")

# 2. Set the location of the runtime loaded PLUMED library:
# https://manual.gromacs.org/documentation/2025.0/reference-manual/special/plumed.html
self.cfg.update('modextrapaths', {
'PLUMED_KERNEL': {
'paths': '$EBROOTPLUMED/lib/libplumedKernel.so',
'prepend': False
}
})

def check_plumed_version_supports_patching_gromacs_engine(self, engine):
res = run_shell_cmd("plumed-patch -l")
if not re.search(engine, res.output):
plumed_ver = get_software_version('PLUMED')
msg = "There is no support in PLUMED version %s for GROMACS %s: %s" % (plumed_ver, self.version,
res.output)
if self.cfg['ignore_plumed_version_check']:
self.log.warning(msg)
else:
raise EasyBuildError(msg)

def set_plumed_cmd(self):
if self.is_using_plumed_kernel():
self.configure_runtime_loading_of_plumed()
return

engine = 'gromacs-%s' % self.version

self.check_plumed_version_supports_patching_gromacs_engine(engine)

# PLUMED patching must be done at different stages depending on
# version of GROMACS. Just prepare first part of cmd here
plumed_cmd = "plumed-patch -p -e %s" % engine

if self.is_cmake_shared_lib_plumed_patch_supported():
# Use shared or static patch depending on
# setting of self.cfg['build_shared_libs']
# and adapt cmake flags accordingly as per instructions
# from "plumed patch -i"
if self.cfg['build_shared_libs']:
mode = 'shared'
else:
mode = 'static'
plumed_cmd = plumed_cmd + ' -m %s' % mode

self.plumed_cmd = plumed_cmd

def run_plumed_cmd(self):
if self.plumed_cmd:
run_shell_cmd(self.plumed_cmd)

def prepare_step(self, *args, **kwargs):
"""Custom prepare step for GROMACS."""

Expand Down Expand Up @@ -229,23 +299,7 @@ def configure_step(self):

if plumed_root:
self.log.info('PLUMED support has been enabled.')

# Need to check if PLUMED has an engine for this version
engine = 'gromacs-%s' % self.version

res = run_shell_cmd("plumed-patch -l")
if not re.search(engine, res.output):
plumed_ver = get_software_version('PLUMED')
msg = "There is no support in PLUMED version %s for GROMACS %s: %s" % (plumed_ver, self.version,
res.output)
if self.cfg['ignore_plumed_version_check']:
self.log.warning(msg)
else:
raise EasyBuildError(msg)

# PLUMED patching must be done at different stages depending on
# version of GROMACS. Just prepare first part of cmd here
plumed_cmd = "plumed-patch -p -e %s" % engine
self.set_plumed_cmd()

# Ensure that the GROMACS log files report how the code was patched
# during the build, so that any problems are easier to diagnose.
Expand Down Expand Up @@ -294,7 +348,7 @@ def configure_step(self):

# Now patch GROMACS for PLUMED between configure and build
if plumed_root:
run_shell_cmd(plumed_cmd)
self.run_plumed_cmd()

else:
if '-DGMX_MPI=ON' in self.cfg['configopts']:
Expand Down Expand Up @@ -354,18 +408,7 @@ def configure_step(self):

# Now patch GROMACS for PLUMED before cmake
if plumed_root:
if gromacs_version >= '5.1':
# Use shared or static patch depending on
# setting of self.cfg['build_shared_libs']
# and adapt cmake flags accordingly as per instructions
# from "plumed patch -i"
if self.cfg['build_shared_libs']:
mode = 'shared'
else:
mode = 'static'
plumed_cmd = plumed_cmd + ' -m %s' % mode

run_shell_cmd(plumed_cmd)
self.run_plumed_cmd()

# prefer static libraries, if available
if self.cfg['build_shared_libs']:
Expand Down Expand Up @@ -524,7 +567,10 @@ def test_step(self):

# run 'make check' or whatever the easyconfig specifies
# in parallel since it involves more compilation
self.cfg.update('runtest', f"-j {self.cfg.parallel}")
if self.parallel_test:
self.cfg.update('runtest', f"-j {self.parallel_test}")
else:
self.cfg.update('runtest', f"-j {self.cfg.parallel}")
super().test_step()

if build_option('rpath'):
Expand Down