From 5097bd00233b9f43b32ae6ebd08774efa17a8e81 Mon Sep 17 00:00:00 2001 From: Oliver Stueker Date: Fri, 31 Oct 2025 15:40:32 -0230 Subject: [PATCH 1/6] EB_GROMACS: finer control over PLUMED support (internal or patching) --- easybuild/easyblocks/g/gromacs.py | 53 +++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/easybuild/easyblocks/g/gromacs.py b/easybuild/easyblocks/g/gromacs.py index 39959335531..225ad22587d 100644 --- a/easybuild/easyblocks/g/gromacs.py +++ b/easybuild/easyblocks/g/gromacs.py @@ -72,7 +72,8 @@ def extra_options(): 'mpiexec_numproc_flag': ['-np', "Flag to introduce the number of MPI tasks when running tests", CUSTOM], 'mpi_numprocs': [0, "Number of MPI tasks to use when running tests", CUSTOM], '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. " + + 'plumed': [None, "Try to enable PLUMED support. None (default) is auto-detect. " + + "'patch' apply PLUMED patches even for GROMACS 2025 and newer." + "True or False forces behaviour.", CUSTOM], }) return extra_vars @@ -217,33 +218,50 @@ def configure_step(self): # PLUMED detection # enable PLUMED support if PLUMED is listed as a dependency # and PLUMED support is either explicitly enabled (plumed = True) or unspecified ('plumed' not defined) + # For GROMACS 2025 and newer that means enabling GROMACS' native PLUMED support ('-DGMX_USE_PLUMED=ON') + # unless PLUMED patches are requested with plumed = 'patch'. + # For older versions of GROMACS patches are applied to enable PLUMED support. plumed_root = get_software_root('PLUMED') + plumed_patches = False if self.cfg['plumed'] and not plumed_root: msg = "PLUMED support has been requested but PLUMED is not listed as a dependency." raise EasyBuildError(msg) elif plumed_root and self.cfg['plumed'] is False: self.log.info('PLUMED was found, but compilation without PLUMED has been requested.') plumed_root = None + elif plumed_root and gromacs_version >= '2025' and self.cfg['plumed'] == 'patch': + self.log.info('PLUMED was found, and PLUMED patching has been requested.') + plumed_patches = True + elif plumed_root and gromacs_version >= '2025' and self.cfg['plumed'] is True: + self.log.info('PLUMED was found, and PLUMED support has been requested.') + elif plumed_root and gromacs_version < '2025' and self.cfg['plumed'] is True: + self.log.info('PLUMED was found, and PLUMED support has been requested.') + plumed_patches = True 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 + if gromacs_version >= '2025' and plumed_patches is False: + self.log.info('Native PLUMED support has been enabled.') + self.cfg.update('configopts', '-DGMX_USE_PLUMED=ON') - 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) + else: + # 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 + # 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 # Ensure that the GROMACS log files report how the code was patched # during the build, so that any problems are easier to diagnose. @@ -251,7 +269,7 @@ def configure_step(self): if (gromacs_version >= '2020' and '-DGMX_VERSION_STRING_OF_FORK=' not in self.cfg['configopts']): gromacs_version_string_suffix = 'EasyBuild-%s' % EASYBUILD_VERSION - if plumed_root: + if plumed_root and plumed_patches: gromacs_version_string_suffix += '-PLUMED-%s' % get_software_version('PLUMED') self.cfg.update('configopts', '-DGMX_VERSION_STRING_OF_FORK=%s' % gromacs_version_string_suffix) @@ -291,7 +309,8 @@ def configure_step(self): ConfigureMake.configure_step(self) # Now patch GROMACS for PLUMED between configure and build - if plumed_root: + if plumed_root and plumed_patches: + self.log.info('Applying PLUMED patches to GROMACS.') run_shell_cmd(plumed_cmd) else: From c394b2fb981ad512a45ee45058d94beaca90d6d4 Mon Sep 17 00:00:00 2001 From: Oliver Stueker Date: Tue, 4 Nov 2025 16:10:29 -0330 Subject: [PATCH 2/6] GROMACS: improve PLUMED patching logic --- easybuild/easyblocks/g/gromacs.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/easybuild/easyblocks/g/gromacs.py b/easybuild/easyblocks/g/gromacs.py index 225ad22587d..0bb3a4e0755 100644 --- a/easybuild/easyblocks/g/gromacs.py +++ b/easybuild/easyblocks/g/gromacs.py @@ -229,14 +229,20 @@ def configure_step(self): elif plumed_root and self.cfg['plumed'] is False: self.log.info('PLUMED was found, but compilation without PLUMED has been requested.') plumed_root = None - elif plumed_root and gromacs_version >= '2025' and self.cfg['plumed'] == 'patch': + elif plumed_root and self.cfg['plumed'] == 'patch': self.log.info('PLUMED was found, and PLUMED patching has been requested.') + print('PLUMED was found, and PLUMED patching has been requested.') plumed_patches = True - elif plumed_root and gromacs_version >= '2025' and self.cfg['plumed'] is True: - self.log.info('PLUMED was found, and PLUMED support has been requested.') - elif plumed_root and gromacs_version < '2025' and self.cfg['plumed'] is True: - self.log.info('PLUMED was found, and PLUMED support has been requested.') - plumed_patches = True + elif plumed_root and self.cfg['plumed'] is True: + msg = 'PLUMED was found, and PLUMED support has been requested.' + if gromacs_version >= '2025': + msg += ' Will use native PLUMED support.' + plumed_patches = False + else: + msg += ' Will apply PLUMED patches.' + plumed_patches = True + self.log.info(msg) + print(msg) if plumed_root: self.log.info('PLUMED support has been enabled.') @@ -269,7 +275,7 @@ def configure_step(self): if (gromacs_version >= '2020' and '-DGMX_VERSION_STRING_OF_FORK=' not in self.cfg['configopts']): gromacs_version_string_suffix = 'EasyBuild-%s' % EASYBUILD_VERSION - if plumed_root and plumed_patches: + if plumed_patches: gromacs_version_string_suffix += '-PLUMED-%s' % get_software_version('PLUMED') self.cfg.update('configopts', '-DGMX_VERSION_STRING_OF_FORK=%s' % gromacs_version_string_suffix) From 03bbe6503288b72e49935eb40de291376e9aef4c Mon Sep 17 00:00:00 2001 From: Oliver Stueker Date: Tue, 4 Nov 2025 16:20:27 -0330 Subject: [PATCH 3/6] remove print statements added for debugging --- easybuild/easyblocks/g/gromacs.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/easybuild/easyblocks/g/gromacs.py b/easybuild/easyblocks/g/gromacs.py index 0bb3a4e0755..6c1592012ac 100644 --- a/easybuild/easyblocks/g/gromacs.py +++ b/easybuild/easyblocks/g/gromacs.py @@ -231,7 +231,6 @@ def configure_step(self): plumed_root = None elif plumed_root and self.cfg['plumed'] == 'patch': self.log.info('PLUMED was found, and PLUMED patching has been requested.') - print('PLUMED was found, and PLUMED patching has been requested.') plumed_patches = True elif plumed_root and self.cfg['plumed'] is True: msg = 'PLUMED was found, and PLUMED support has been requested.' @@ -242,7 +241,6 @@ def configure_step(self): msg += ' Will apply PLUMED patches.' plumed_patches = True self.log.info(msg) - print(msg) if plumed_root: self.log.info('PLUMED support has been enabled.') From 82a0da515f5209471c9bdcf2b59e04f4fcdbcc62 Mon Sep 17 00:00:00 2001 From: Oliver Stueker Date: Wed, 19 Nov 2025 13:41:17 -0330 Subject: [PATCH 4/6] GROMACS: add option plumed = 'native' --- easybuild/easyblocks/g/gromacs.py | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/easybuild/easyblocks/g/gromacs.py b/easybuild/easyblocks/g/gromacs.py index 6c1592012ac..2338432d7c9 100644 --- a/easybuild/easyblocks/g/gromacs.py +++ b/easybuild/easyblocks/g/gromacs.py @@ -73,8 +73,9 @@ def extra_options(): 'mpi_numprocs': [0, "Number of MPI tasks to use when running tests", CUSTOM], 'ignore_plumed_version_check': [False, "Ignore the version compatibility check for PLUMED", CUSTOM], 'plumed': [None, "Try to enable PLUMED support. None (default) is auto-detect. " + - "'patch' apply PLUMED patches even for GROMACS 2025 and newer." + - "True or False forces behaviour.", CUSTOM], + "'native' enables native PLUMED support for GROMACS 2025 and newer." + + "'patch' (or True) applies PLUMED patches." + + "False disables PLUMED support.", CUSTOM], }) return extra_vars @@ -216,11 +217,12 @@ def configure_step(self): self.cfg.update('configopts', "-DGMX_GPU=OFF") # PLUMED detection - # enable PLUMED support if PLUMED is listed as a dependency - # and PLUMED support is either explicitly enabled (plumed = True) or unspecified ('plumed' not defined) - # For GROMACS 2025 and newer that means enabling GROMACS' native PLUMED support ('-DGMX_USE_PLUMED=ON') - # unless PLUMED patches are requested with plumed = 'patch'. - # For older versions of GROMACS patches are applied to enable PLUMED support. + # enable PLUMED support if PLUMED is listed as a dependency. + # plumed = 'native' will enable GROMACS' native PLUMED support ('-DGMX_USE_PLUMED=ON') + # for GROMACS 2025 and newer. plumed = 'patch' specifically requests PLUMED patches. + # In auto-detect ('plumed = None' or not defined) will prefer native support for 2025 + # and newer. Older versions of GROMACS patches are applied to enable PLUMED support. + # plumed = True behaves like plumed = 'patch' for backwards compatibility. plumed_root = get_software_root('PLUMED') plumed_patches = False if self.cfg['plumed'] and not plumed_root: @@ -232,8 +234,22 @@ def configure_step(self): elif plumed_root and self.cfg['plumed'] == 'patch': self.log.info('PLUMED was found, and PLUMED patching has been requested.') plumed_patches = True + elif plumed_root and self.cfg['plumed'] == 'native': + msg = 'PLUMED was found, and native PLUMED support has been requested.' + if gromacs_version >= '2025': + msg += ' Will use native PLUMED support.' + plumed_patches = False + self.log.info(msg) + else: + msg += " Native PLUMED support is only available with GROMACS 2025 and newer." + raise EasyBuildError(msg) elif plumed_root and self.cfg['plumed'] is True: msg = 'PLUMED was found, and PLUMED support has been requested.' + msg += ' Will apply PLUMED patches.' + plumed_patches = True + self.log.info(msg) + elif plumed_root and self.cfg['plumed'] is None: + msg = 'PLUMED was found.' if gromacs_version >= '2025': msg += ' Will use native PLUMED support.' plumed_patches = False From e2df627cba4ba92998cb36fe731821c8801d1359 Mon Sep 17 00:00:00 2001 From: Oliver Stueker Date: Wed, 10 Dec 2025 11:44:05 -0330 Subject: [PATCH 5/6] use native PLUMED by default with GROMACS 2026+ GROMACS 2025 was released on Feb 11, 2025. Changing EB's default behavior of whether to apply patches or use GROMACS' native PLUMED support would be surprising to users. Changing the default with the yet unreleased GROMACS 2026 is safer. In any case PLUMED patches for GROMACS 2025 and newer are based on GROMACS' native support, which means that some PLUMED features that were available with older GROMACS releases are not supported until GROMACS' MDModule and the plugin mechansims have caught up again. See also discussion in easybuilders/easybuild-easyblocks#3984 --- easybuild/easyblocks/g/gromacs.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/easybuild/easyblocks/g/gromacs.py b/easybuild/easyblocks/g/gromacs.py index 2338432d7c9..9957973bf06 100644 --- a/easybuild/easyblocks/g/gromacs.py +++ b/easybuild/easyblocks/g/gromacs.py @@ -220,7 +220,7 @@ def configure_step(self): # enable PLUMED support if PLUMED is listed as a dependency. # plumed = 'native' will enable GROMACS' native PLUMED support ('-DGMX_USE_PLUMED=ON') # for GROMACS 2025 and newer. plumed = 'patch' specifically requests PLUMED patches. - # In auto-detect ('plumed = None' or not defined) will prefer native support for 2025 + # In auto-detect ('plumed = None' or not defined) will prefer native support for 2026 # and newer. Older versions of GROMACS patches are applied to enable PLUMED support. # plumed = True behaves like plumed = 'patch' for backwards compatibility. plumed_root = get_software_root('PLUMED') @@ -250,7 +250,9 @@ def configure_step(self): self.log.info(msg) elif plumed_root and self.cfg['plumed'] is None: msg = 'PLUMED was found.' - if gromacs_version >= '2025': + if gromacs_version >= '2026': + # Even though native support is available since GROMACS 2025, we'll only use + # it as default with 2026 and newer to avoid a sudden change in behaviour. msg += ' Will use native PLUMED support.' plumed_patches = False else: From f96f3524b4cb94e481a7ad3a7143d7c5cc51f9d6 Mon Sep 17 00:00:00 2001 From: Oliver Stueker Date: Tue, 16 Dec 2025 15:55:33 -0330 Subject: [PATCH 6/6] GROMACS: fix native PLUMED support instead of patching --- easybuild/easyblocks/g/gromacs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/g/gromacs.py b/easybuild/easyblocks/g/gromacs.py index 9957973bf06..08a9e4c8ed7 100644 --- a/easybuild/easyblocks/g/gromacs.py +++ b/easybuild/easyblocks/g/gromacs.py @@ -385,7 +385,7 @@ def configure_step(self): self.cfg.update('configopts', "-DPython3_FIND_VIRTUALENV=STANDARD") # Now patch GROMACS for PLUMED before cmake - if plumed_root: + if plumed_root and plumed_patches: if gromacs_version >= '5.1': # Use shared or static patch depending on # setting of self.cfg['build_shared_libs']