From 677237ca1fb174ad68c0e3390c3e3eba87bda25d Mon Sep 17 00:00:00 2001 From: Michele Invernizzi Date: Thu, 15 Oct 2026 11:21:03 +0200 Subject: [PATCH 1/3] GROMACS 2020-2023 patches: count PLUMED as CPU force work also when it runs after do_force() In GROMACS 2020-2023, do_force() sets up the domain workload on the pair-search steps, and it read plumedswitch, which md.cpp sets to 0 while PLUMED runs after do_force() (on the virial and energy steps, and in the HREX energy evaluation). Step 0 is always such a step, so with GPU force buffer operations PLUMED's forces could still be lost until the next pair search, or in the whole run when every pair-search step computes the virial. A flag that is never switched off, plumedIsUsed, now decides it. GROMACS 2024 sets up the workload in md.cpp, before the switch, and was already right. Co-Authored-By: Claude Opus 5.5 --- patches/gromacs-2020.7.diff/src/gromacs/mdlib/force.cpp | 1 + patches/gromacs-2020.7.diff/src/gromacs/mdlib/sim_util.cpp | 4 ++-- .../src/gromacs/mdrun/legacymdrunoptions.cpp | 1 + .../src/gromacs/mdrun/legacymdrunoptions.h | 1 + patches/gromacs-2021.7.diff/src/gromacs/mdlib/sim_util.cpp | 3 ++- .../src/gromacs/mdrun/legacymdrunoptions.cpp | 1 + .../src/gromacs/mdrun/legacymdrunoptions.h | 1 + patches/gromacs-2021.7.diff/src/gromacs/mdrun/runner.cpp | 1 + patches/gromacs-2022.5.diff/src/gromacs/mdlib/sim_util.cpp | 3 ++- .../src/gromacs/mdrun/legacymdrunoptions.cpp | 1 + .../src/gromacs/mdrun/legacymdrunoptions.h | 1 + patches/gromacs-2022.5.diff/src/gromacs/mdrun/runner.cpp | 1 + patches/gromacs-2023.5.diff/src/gromacs/mdlib/sim_util.cpp | 3 ++- .../src/gromacs/mdrun/legacymdrunoptions.cpp | 1 + .../src/gromacs/mdrun/legacymdrunoptions.h | 1 + patches/gromacs-2023.5.diff/src/gromacs/mdrun/runner.cpp | 1 + 16 files changed, 20 insertions(+), 5 deletions(-) diff --git a/patches/gromacs-2020.7.diff/src/gromacs/mdlib/force.cpp b/patches/gromacs-2020.7.diff/src/gromacs/mdlib/force.cpp index 7349417bfd..627c82980e 100644 --- a/patches/gromacs-2020.7.diff/src/gromacs/mdlib/force.cpp +++ b/patches/gromacs-2020.7.diff/src/gromacs/mdlib/force.cpp @@ -76,6 +76,7 @@ /* PLUMED */ #include "../../../Plumed.h" int plumedswitch=0; +int plumedIsUsed=0; /* unlike plumedswitch, not switched off while PLUMED runs after do_force() */ plumed plumedmain; /* END PLUMED */ diff --git a/patches/gromacs-2020.7.diff/src/gromacs/mdlib/sim_util.cpp b/patches/gromacs-2020.7.diff/src/gromacs/mdlib/sim_util.cpp index ccd84a9a7f..91fadd3e4d 100644 --- a/patches/gromacs-2020.7.diff/src/gromacs/mdlib/sim_util.cpp +++ b/patches/gromacs-2020.7.diff/src/gromacs/mdlib/sim_util.cpp @@ -115,7 +115,7 @@ #include "gromacs/utility/sysinfo.h" /* PLUMED */ -extern int plumedswitch; +extern int plumedIsUsed; /* END PLUMED */ using gmx::AtomLocality; @@ -800,7 +800,7 @@ static DomainLifetimeWorkload setupDomainLifetimeWorkload(const t_inputrec& domainWork.haveSpecialForces || domainWork.haveCpuListedForceWork || domainWork.haveFreeEnergyWork || simulationWork.useCpuNonbonded || simulationWork.useCpuPme || simulationWork.haveEwaldSurfaceContribution || inputrec.nwall > 0 - || plumedswitch; /* PLUMED: it adds its forces to the CPU force buffer */ + || plumedIsUsed; /* PLUMED: it adds its forces to the CPU force buffer */ return domainWork; } diff --git a/patches/gromacs-2020.7.diff/src/gromacs/mdrun/legacymdrunoptions.cpp b/patches/gromacs-2020.7.diff/src/gromacs/mdrun/legacymdrunoptions.cpp index a6ff208900..1343f73d1b 100644 --- a/patches/gromacs-2020.7.diff/src/gromacs/mdrun/legacymdrunoptions.cpp +++ b/patches/gromacs-2020.7.diff/src/gromacs/mdrun/legacymdrunoptions.cpp @@ -177,6 +177,7 @@ int LegacyMdrunOptions::updateFromCommandLine(int argc, char** argv, ArrayRef(filenames.size()), filenames.data())); plumedswitch=1; + plumedIsUsed=1; } /* PLUMED HREX*/ if(getenv("PLUMED_HREX")) plumed_hrex=1; diff --git a/patches/gromacs-2020.7.diff/src/gromacs/mdrun/legacymdrunoptions.h b/patches/gromacs-2020.7.diff/src/gromacs/mdrun/legacymdrunoptions.h index 966e5a41a8..eac77318b4 100644 --- a/patches/gromacs-2020.7.diff/src/gromacs/mdrun/legacymdrunoptions.h +++ b/patches/gromacs-2020.7.diff/src/gromacs/mdrun/legacymdrunoptions.h @@ -60,6 +60,7 @@ /* PLUMED */ #include "../../../Plumed.h" extern int plumedswitch; +extern int plumedIsUsed; extern plumed plumedmain; /* END PLUMED */ diff --git a/patches/gromacs-2021.7.diff/src/gromacs/mdlib/sim_util.cpp b/patches/gromacs-2021.7.diff/src/gromacs/mdlib/sim_util.cpp index c4e6803e06..a5d5d3612a 100644 --- a/patches/gromacs-2021.7.diff/src/gromacs/mdlib/sim_util.cpp +++ b/patches/gromacs-2021.7.diff/src/gromacs/mdlib/sim_util.cpp @@ -138,6 +138,7 @@ using gmx::StepWorkload; /* PLUMED */ #include "../../../Plumed.h" extern int plumedswitch; +extern int plumedIsUsed; extern plumed plumedmain; /* END PLUMED */ @@ -882,7 +883,7 @@ static DomainLifetimeWorkload setupDomainLifetimeWorkload(const t_inputrec& domainWork.haveSpecialForces || domainWork.haveCpuListedForceWork || domainWork.haveFreeEnergyWork || simulationWork.useCpuNonbonded || simulationWork.useCpuPme || simulationWork.haveEwaldSurfaceContribution || inputrec.nwall > 0 - || plumedswitch; /* PLUMED: it adds its forces to the CPU force buffer */ + || plumedIsUsed; /* PLUMED: it adds its forces to the CPU force buffer */ return domainWork; } diff --git a/patches/gromacs-2021.7.diff/src/gromacs/mdrun/legacymdrunoptions.cpp b/patches/gromacs-2021.7.diff/src/gromacs/mdrun/legacymdrunoptions.cpp index 8706772915..9376c0333e 100644 --- a/patches/gromacs-2021.7.diff/src/gromacs/mdrun/legacymdrunoptions.cpp +++ b/patches/gromacs-2021.7.diff/src/gromacs/mdrun/legacymdrunoptions.cpp @@ -178,6 +178,7 @@ int LegacyMdrunOptions::updateFromCommandLine(int argc, char** argv, ArrayRef(filenames.size()), filenames.data())); plumedswitch=1; + plumedIsUsed=1; } /* PLUMED HREX*/ if(getenv("PLUMED_HREX")) plumed_hrex=1; diff --git a/patches/gromacs-2021.7.diff/src/gromacs/mdrun/legacymdrunoptions.h b/patches/gromacs-2021.7.diff/src/gromacs/mdrun/legacymdrunoptions.h index 13ee9b89f9..f27c702446 100644 --- a/patches/gromacs-2021.7.diff/src/gromacs/mdrun/legacymdrunoptions.h +++ b/patches/gromacs-2021.7.diff/src/gromacs/mdrun/legacymdrunoptions.h @@ -60,6 +60,7 @@ /* PLUMED */ #include "../../../Plumed.h" extern int plumedswitch; +extern int plumedIsUsed; extern plumed plumedmain; /* END PLUMED */ diff --git a/patches/gromacs-2021.7.diff/src/gromacs/mdrun/runner.cpp b/patches/gromacs-2021.7.diff/src/gromacs/mdrun/runner.cpp index 7e9461d029..c3ae63a09e 100644 --- a/patches/gromacs-2021.7.diff/src/gromacs/mdrun/runner.cpp +++ b/patches/gromacs-2021.7.diff/src/gromacs/mdrun/runner.cpp @@ -175,6 +175,7 @@ /* PLUMED */ #include "../../../Plumed.h" int plumedswitch; +int plumedIsUsed=0; /* unlike plumedswitch, not switched off while PLUMED runs after do_force() */ plumed plumedmain; /* END PLUMED */ diff --git a/patches/gromacs-2022.5.diff/src/gromacs/mdlib/sim_util.cpp b/patches/gromacs-2022.5.diff/src/gromacs/mdlib/sim_util.cpp index b1b4607852..b9a15038c8 100644 --- a/patches/gromacs-2022.5.diff/src/gromacs/mdlib/sim_util.cpp +++ b/patches/gromacs-2022.5.diff/src/gromacs/mdlib/sim_util.cpp @@ -137,6 +137,7 @@ using gmx::StepWorkload; /* PLUMED */ #include "../../../Plumed.h" extern int plumedswitch; +extern int plumedIsUsed; extern plumed plumedmain; /* END PLUMED */ @@ -939,7 +940,7 @@ static DomainLifetimeWorkload setupDomainLifetimeWorkload(const t_inputrec& domainWork.haveSpecialForces || domainWork.haveCpuListedForceWork || domainWork.haveFreeEnergyWork || simulationWork.useCpuNonbonded || simulationWork.useCpuPme || simulationWork.haveEwaldSurfaceContribution || inputrec.nwall > 0 - || plumedswitch; /* PLUMED: it adds its forces to the CPU force buffer */ + || plumedIsUsed; /* PLUMED: it adds its forces to the CPU force buffer */ domainWork.haveLocalForceContribInCpuBuffer = domainWork.haveCpuLocalForceWork || simulationWork.havePpDomainDecomposition; domainWork.haveNonLocalForceContribInCpuBuffer = diff --git a/patches/gromacs-2022.5.diff/src/gromacs/mdrun/legacymdrunoptions.cpp b/patches/gromacs-2022.5.diff/src/gromacs/mdrun/legacymdrunoptions.cpp index 024919022a..014dd57b63 100644 --- a/patches/gromacs-2022.5.diff/src/gromacs/mdrun/legacymdrunoptions.cpp +++ b/patches/gromacs-2022.5.diff/src/gromacs/mdrun/legacymdrunoptions.cpp @@ -185,6 +185,7 @@ int LegacyMdrunOptions::updateFromCommandLine(int argc, char** argv, ArrayRef(filenames.size()), filenames.data())); plumedswitch=1; + plumedIsUsed=1; } /* PLUMED HREX*/ if(getenv("PLUMED_HREX")) plumed_hrex=1; diff --git a/patches/gromacs-2022.5.diff/src/gromacs/mdrun/legacymdrunoptions.h b/patches/gromacs-2022.5.diff/src/gromacs/mdrun/legacymdrunoptions.h index 26a9b7d9c0..4668ccba91 100644 --- a/patches/gromacs-2022.5.diff/src/gromacs/mdrun/legacymdrunoptions.h +++ b/patches/gromacs-2022.5.diff/src/gromacs/mdrun/legacymdrunoptions.h @@ -57,6 +57,7 @@ /* PLUMED */ #include "../../../Plumed.h" extern int plumedswitch; +extern int plumedIsUsed; extern plumed plumedmain; /* END PLUMED */ diff --git a/patches/gromacs-2022.5.diff/src/gromacs/mdrun/runner.cpp b/patches/gromacs-2022.5.diff/src/gromacs/mdrun/runner.cpp index f0e8228936..6048ae5ca9 100644 --- a/patches/gromacs-2022.5.diff/src/gromacs/mdrun/runner.cpp +++ b/patches/gromacs-2022.5.diff/src/gromacs/mdrun/runner.cpp @@ -178,6 +178,7 @@ /* PLUMED */ #include "../../../Plumed.h" int plumedswitch; +int plumedIsUsed=0; /* unlike plumedswitch, not switched off while PLUMED runs after do_force() */ plumed plumedmain; /* END PLUMED */ diff --git a/patches/gromacs-2023.5.diff/src/gromacs/mdlib/sim_util.cpp b/patches/gromacs-2023.5.diff/src/gromacs/mdlib/sim_util.cpp index 9aec5daf46..35f252fdd6 100644 --- a/patches/gromacs-2023.5.diff/src/gromacs/mdlib/sim_util.cpp +++ b/patches/gromacs-2023.5.diff/src/gromacs/mdlib/sim_util.cpp @@ -137,6 +137,7 @@ using gmx::StepWorkload; /* PLUMED */ #include "../../../Plumed.h" extern int plumedswitch; +extern int plumedIsUsed; extern plumed plumedmain; /* END PLUMED */ @@ -968,7 +969,7 @@ static DomainLifetimeWorkload setupDomainLifetimeWorkload(const t_inputrec& domainWork.haveSpecialForces || domainWork.haveCpuListedForceWork || domainWork.haveFreeEnergyWork || simulationWork.useCpuNonbonded || simulationWork.useCpuPme || simulationWork.haveEwaldSurfaceContribution || inputrec.nwall > 0 - || plumedswitch; /* PLUMED: it adds its forces to the CPU force buffer */ + || plumedIsUsed; /* PLUMED: it adds its forces to the CPU force buffer */ domainWork.haveCpuNonLocalForceWork = domainWork.haveCpuBondedWork || domainWork.haveFreeEnergyWork; domainWork.haveLocalForceContribInCpuBuffer = domainWork.haveCpuLocalForceWork || simulationWork.havePpDomainDecomposition; diff --git a/patches/gromacs-2023.5.diff/src/gromacs/mdrun/legacymdrunoptions.cpp b/patches/gromacs-2023.5.diff/src/gromacs/mdrun/legacymdrunoptions.cpp index 024919022a..014dd57b63 100644 --- a/patches/gromacs-2023.5.diff/src/gromacs/mdrun/legacymdrunoptions.cpp +++ b/patches/gromacs-2023.5.diff/src/gromacs/mdrun/legacymdrunoptions.cpp @@ -185,6 +185,7 @@ int LegacyMdrunOptions::updateFromCommandLine(int argc, char** argv, ArrayRef(filenames.size()), filenames.data())); plumedswitch=1; + plumedIsUsed=1; } /* PLUMED HREX*/ if(getenv("PLUMED_HREX")) plumed_hrex=1; diff --git a/patches/gromacs-2023.5.diff/src/gromacs/mdrun/legacymdrunoptions.h b/patches/gromacs-2023.5.diff/src/gromacs/mdrun/legacymdrunoptions.h index cf894001b5..6cd6e3a993 100644 --- a/patches/gromacs-2023.5.diff/src/gromacs/mdrun/legacymdrunoptions.h +++ b/patches/gromacs-2023.5.diff/src/gromacs/mdrun/legacymdrunoptions.h @@ -57,6 +57,7 @@ /* PLUMED */ #include "../../../Plumed.h" extern int plumedswitch; +extern int plumedIsUsed; extern plumed plumedmain; /* END PLUMED */ diff --git a/patches/gromacs-2023.5.diff/src/gromacs/mdrun/runner.cpp b/patches/gromacs-2023.5.diff/src/gromacs/mdrun/runner.cpp index 5342fc6868..0c552edb08 100644 --- a/patches/gromacs-2023.5.diff/src/gromacs/mdrun/runner.cpp +++ b/patches/gromacs-2023.5.diff/src/gromacs/mdrun/runner.cpp @@ -183,6 +183,7 @@ /* PLUMED */ #include "../../../Plumed.h" int plumedswitch; +int plumedIsUsed=0; /* unlike plumedswitch, not switched off while PLUMED runs after do_force() */ plumed plumedmain; /* END PLUMED */ From 7639018b22d1ff1415d3d9688cfdde6e048615c6 Mon Sep 17 00:00:00 2001 From: Michele Invernizzi Date: Thu, 15 Oct 2026 11:29:55 +0200 Subject: [PATCH 2/3] GROMACS 2021-2024 patches: with MTS, add PLUMED's forces also to the normal force buffer On the slow MTS steps when PLUMED runs after do_force() (the virial and energy steps), it applies its forces to the combined MTS buffer, which the update uses. GROMACS computes the constraint virial from the normal buffer (and writes it to the trajectory), which then lacked the part of PLUMED's forces that the constraints pass on: e.g. 2.7, -0.5 and 3.8 kJ/mol on the virial of a restraint between two water oxygens, against the GROMACS pull code. The difference PLUMED made to the combined buffer is now added to the normal one too. With a bias on ENERGY the slow forces in the normal buffer are then rescaled as in the combined one, which only affects the constraint virial, not the dynamics. Co-Authored-By: Claude Opus 5.5 --- CHANGES/v2.9.md | 5 +++-- patches/gromacs-2021.7.diff/src/gromacs/mdrun/md.cpp | 9 ++++++++- patches/gromacs-2022.5.diff/src/gromacs/mdrun/md.cpp | 9 ++++++++- patches/gromacs-2023.5.diff/src/gromacs/mdrun/md.cpp | 9 ++++++++- patches/gromacs-2024.3.diff/src/gromacs/mdrun/md.cpp | 9 ++++++++- 5 files changed, 35 insertions(+), 6 deletions(-) diff --git a/CHANGES/v2.9.md b/CHANGES/v2.9.md index 3d6306ddd8..dcd147998c 100644 --- a/CHANGES/v2.9.md +++ b/CHANGES/v2.9.md @@ -111,8 +111,9 @@ Changes from version 2.8 which are relevant for users: on virtual sites were lost, in MD and in energy minimization. They are now spread on the constructing atoms. - GROMACS 2021, 2022, 2023 and 2024 patches: with multiple time stepping, the forces that PLUMED applied when it needed the potential energy were lost on the slow steps, and the potential energy passed to PLUMED - on the fast steps lacked the slow terms. The forces are now applied to the buffer used by the update, - and mdrun stops with an error if PLUMED needs the energy on a fast step. + on the fast steps lacked the slow terms. The forces are now applied to the buffer used by the update (and to + the one that GROMACS uses for the constraint virial and writes to the trajectory), and mdrun stops with an error + if PLUMED needs the energy on a fast step. - GROMACS patches: when PLUMED asked to stop the simulation (e.g. with \ref COMMITTOR), multiple simulations (e.g. multiple walkers) hung, and with GROMACS 2022-2024 no checkpoint was written at the last step. The request now goes through the GROMACS stop handler, so that it reaches all the ranks and the simulations, and mdrun diff --git a/patches/gromacs-2021.7.diff/src/gromacs/mdrun/md.cpp b/patches/gromacs-2021.7.diff/src/gromacs/mdrun/md.cpp index 9b6b813eab..1140372caa 100644 --- a/patches/gromacs-2021.7.diff/src/gromacs/mdrun/md.cpp +++ b/patches/gromacs-2021.7.diff/src/gromacs/mdrun/md.cpp @@ -1206,10 +1206,13 @@ void gmx::LegacySimulator::do_md() if(plumedswitch){ if(plumedAfterForces){ ArrayRef forceForPlumed=f.view().force(); + std::vector forceBeforePlumed; if(fr->useMts && step % ir->mtsLevels[1].stepFactor == 0){ - /* On the slow steps the update uses the combined MTS force buffer */ + /* On the slow steps the update uses the combined MTS force buffer, and the constraint virial + the normal one: PLUMED's forces are added to the latter below */ forceForPlumed=f.view().forceMtsCombined(); plumed_cmd(plumedmain,"setForces",&forceForPlumed[0][0]); + forceBeforePlumed.assign(forceForPlumed.begin(),forceForPlumed.begin()+mdatoms->homenr); } if(plumedNeedsEnergy){ msmul(force_vir,2.0,plumed_vir); @@ -1230,6 +1233,10 @@ void gmx::LegacySimulator::do_md() VirtualSitesHandler::VirialHandling::NonLinear,{},vsiteVir,nrnb,state->box,wcycle); m_add(force_vir,vsiteVir,force_vir); } + if(!forceBeforePlumed.empty()){ + ArrayRef forceNormal=f.view().force(); + for(int i=0;ihomenr;i++) forceNormal[i]+=forceForPlumed[i]-forceBeforePlumed[i]; + } } else { msmul(plumed_vir,0.5,plumed_vir); m_add(force_vir,plumed_vir,force_vir); diff --git a/patches/gromacs-2022.5.diff/src/gromacs/mdrun/md.cpp b/patches/gromacs-2022.5.diff/src/gromacs/mdrun/md.cpp index 73d102ea32..1c12c335d5 100644 --- a/patches/gromacs-2022.5.diff/src/gromacs/mdrun/md.cpp +++ b/patches/gromacs-2022.5.diff/src/gromacs/mdrun/md.cpp @@ -1530,10 +1530,13 @@ void gmx::LegacySimulator::do_md() if(plumedswitch){ if(plumedAfterForces){ ArrayRef forceForPlumed=f.view().force(); + std::vector forceBeforePlumed; if(simulationWork.useMts && step % ir->mtsLevels[1].stepFactor == 0){ - /* On the slow steps the update uses the combined MTS force buffer */ + /* On the slow steps the update uses the combined MTS force buffer, and the constraint virial + the normal one: PLUMED's forces are added to the latter below */ forceForPlumed=f.view().forceMtsCombined(); plumed_cmd(plumedmain,"setForces",&forceForPlumed[0][0]); + forceBeforePlumed.assign(forceForPlumed.begin(),forceForPlumed.begin()+md->homenr); } if(plumedNeedsEnergy){ msmul(force_vir,2.0,plumed_vir); @@ -1554,6 +1557,10 @@ void gmx::LegacySimulator::do_md() VirtualSitesHandler::VirialHandling::NonLinear,{},vsiteVir,nrnb,state->box,wcycle); m_add(force_vir,vsiteVir,force_vir); } + if(!forceBeforePlumed.empty()){ + ArrayRef forceNormal=f.view().force(); + for(int i=0;ihomenr;i++) forceNormal[i]+=forceForPlumed[i]-forceBeforePlumed[i]; + } } else { msmul(plumed_vir,0.5,plumed_vir); m_add(force_vir,plumed_vir,force_vir); diff --git a/patches/gromacs-2023.5.diff/src/gromacs/mdrun/md.cpp b/patches/gromacs-2023.5.diff/src/gromacs/mdrun/md.cpp index 5ba9ee34e0..7a87164c67 100644 --- a/patches/gromacs-2023.5.diff/src/gromacs/mdrun/md.cpp +++ b/patches/gromacs-2023.5.diff/src/gromacs/mdrun/md.cpp @@ -1566,10 +1566,13 @@ void gmx::LegacySimulator::do_md() if(plumedswitch){ if(plumedAfterForces){ ArrayRef forceForPlumed=f.view().force(); + std::vector forceBeforePlumed; if(simulationWork.useMts && step % ir->mtsLevels[1].stepFactor == 0){ - /* On the slow steps the update uses the combined MTS force buffer */ + /* On the slow steps the update uses the combined MTS force buffer, and the constraint virial + the normal one: PLUMED's forces are added to the latter below */ forceForPlumed=f.view().forceMtsCombined(); plumed_cmd(plumedmain,"setForces",&forceForPlumed[0][0]); + forceBeforePlumed.assign(forceForPlumed.begin(),forceForPlumed.begin()+md->homenr); } if(plumedNeedsEnergy){ msmul(force_vir,2.0,plumed_vir); @@ -1590,6 +1593,10 @@ void gmx::LegacySimulator::do_md() VirtualSitesHandler::VirialHandling::NonLinear,{},vsiteVir,nrnb,state->box,wcycle); m_add(force_vir,vsiteVir,force_vir); } + if(!forceBeforePlumed.empty()){ + ArrayRef forceNormal=f.view().force(); + for(int i=0;ihomenr;i++) forceNormal[i]+=forceForPlumed[i]-forceBeforePlumed[i]; + } } else { msmul(plumed_vir,0.5,plumed_vir); m_add(force_vir,plumed_vir,force_vir); diff --git a/patches/gromacs-2024.3.diff/src/gromacs/mdrun/md.cpp b/patches/gromacs-2024.3.diff/src/gromacs/mdrun/md.cpp index ee049c81f2..7f39232cfe 100644 --- a/patches/gromacs-2024.3.diff/src/gromacs/mdrun/md.cpp +++ b/patches/gromacs-2024.3.diff/src/gromacs/mdrun/md.cpp @@ -1617,10 +1617,13 @@ void gmx::LegacySimulator::do_md() if(plumedswitch){ if(plumedAfterForces){ ArrayRef forceForPlumed=f.view().force(); + std::vector forceBeforePlumed; if(simulationWork.useMts && step % ir->mtsLevels[1].stepFactor == 0){ - /* On the slow steps the update uses the combined MTS force buffer */ + /* On the slow steps the update uses the combined MTS force buffer, and the constraint virial + the normal one: PLUMED's forces are added to the latter below */ forceForPlumed=f.view().forceMtsCombined(); plumed_cmd(plumedmain,"setForces",&forceForPlumed[0][0]); + forceBeforePlumed.assign(forceForPlumed.begin(),forceForPlumed.begin()+md->homenr); } if(plumedNeedsEnergy){ msmul(force_vir,2.0,plumed_vir); @@ -1641,6 +1644,10 @@ void gmx::LegacySimulator::do_md() VirtualSitesHandler::VirialHandling::NonLinear,{},vsiteVir,nrnb_,state_->box,wallCycleCounters_); m_add(force_vir,vsiteVir,force_vir); } + if(!forceBeforePlumed.empty()){ + ArrayRef forceNormal=f.view().force(); + for(int i=0;ihomenr;i++) forceNormal[i]+=forceForPlumed[i]-forceBeforePlumed[i]; + } } else { msmul(plumed_vir,0.5,plumed_vir); m_add(force_vir,plumed_vir,force_vir); From d0d68578b95e577247ef866fa38be845ec04b54d Mon Sep 17 00:00:00 2001 From: Michele Invernizzi Date: Thu, 15 Oct 2026 11:29:55 +0200 Subject: [PATCH 3/3] GROMACS patches: clearer error messages, a stale comment, and corrections to CHANGES - The error for shells also fires with flexible constraints, which use the same relaxation: say so. The error for thread-MPI suggests -ntmpi 1. - The forces on virtual sites are spread whenever PLUMED runs after do_force(), not only in the energy path. - CHANGES: the virial was counted twice also with the patches for GROMACS older than 2019; after a stop request, mdrun runs one more step after the next communication of signals, which between simulations happens every 200 steps or more. Co-Authored-By: Claude Opus 5.5 --- CHANGES/v2.9.md | 15 ++++++++------- .../gromacs-2020.7.diff/src/gromacs/mdrun/md.cpp | 6 +++--- .../src/gromacs/mdrun/rerun.cpp | 4 ++-- .../src/gromacs/mdrun/runner.cpp | 2 +- .../gromacs-2021.7.diff/src/gromacs/mdrun/md.cpp | 6 +++--- .../src/gromacs/mdrun/rerun.cpp | 4 ++-- .../src/gromacs/mdrun/runner.cpp | 2 +- .../gromacs-2022.5.diff/src/gromacs/mdrun/md.cpp | 6 +++--- .../src/gromacs/mdrun/rerun.cpp | 4 ++-- .../src/gromacs/mdrun/runner.cpp | 2 +- .../gromacs-2023.5.diff/src/gromacs/mdrun/md.cpp | 6 +++--- .../src/gromacs/mdrun/rerun.cpp | 4 ++-- .../src/gromacs/mdrun/runner.cpp | 2 +- .../gromacs-2024.3.diff/src/gromacs/mdrun/md.cpp | 6 +++--- .../src/gromacs/mdrun/rerun.cpp | 4 ++-- .../src/gromacs/mdrun/runner.cpp | 2 +- 16 files changed, 38 insertions(+), 37 deletions(-) diff --git a/CHANGES/v2.9.md b/CHANGES/v2.9.md index dcd147998c..dbe80862c3 100644 --- a/CHANGES/v2.9.md +++ b/CHANGES/v2.9.md @@ -117,8 +117,8 @@ Changes from version 2.8 which are relevant for users: - GROMACS patches: when PLUMED asked to stop the simulation (e.g. with \ref COMMITTOR), multiple simulations (e.g. multiple walkers) hung, and with GROMACS 2022-2024 no checkpoint was written at the last step. The request now goes through the GROMACS stop handler, so that it reaches all the ranks and the simulations, and mdrun - stops at the next step at which GROMACS communicates signals (every nstglobalcomm steps, and at least every 200 - steps between multiple simulations), writing a checkpoint there. + runs one more step after the next step at which GROMACS communicates signals (every nstglobalcomm steps, and + between multiple simulations every 200 steps or more), writing a checkpoint at that last step. - GROMACS 2020 and 2024 patches: mdrun -rerun with -plumed stopped with an error, as PLUMED was never initialized. The rerun is now patched as for the other GROMACS versions. - GROMACS patches: in energy minimization with domain decomposition, PLUMED was not told when the atoms were @@ -127,12 +127,13 @@ Changes from version 2.8 which are relevant for users: - GROMACS patches: with -hrex, PLUMED calculated and updated a second time, for the previous step, when GROMACS computed the energy of the swapped coordinates: e.g. \ref METAD deposited a second hill, and \ref PRINT printed a second line, on the step before an exchange whenever their stride divided it. - - GROMACS patches: with shell (polarizable) particles, PLUMED was silently not applied in MD and in reruns. + - GROMACS patches: with shell (polarizable) particles or flexible constraints, PLUMED was silently not applied + in MD and in reruns. mdrun now stops with an error. - - GROMACS patches (since the GROMACS 2019 patch): the virial of the forces applied by PLUMED was counted twice, - so the pressure was wrong, e.g. in NPT simulations, with any bias that applies forces, unless the PLUMED input - needed the potential energy. On the steps when GROMACS computes the virial, PLUMED now runs after do_force(), - as it does when it needs the energy. + - GROMACS patches (and those for older GROMACS versions, at least since 4.6): the virial of the forces applied + by PLUMED was counted twice, so the pressure was wrong, e.g. in NPT simulations, with any bias that applies + forces, unless the PLUMED input needed the potential energy. On the steps when GROMACS computes the virial, + PLUMED now runs after do_force(), as it does when it needs the energy. - GROMACS 2024 patch: with -hrex, the energy of the swapped coordinates was computed with the settings of the previous step (often without the energy), so the exchanges were almost never accepted. - GROMACS 2024 patch: in expanded ensemble simulations, the `lambda` passed to PLUMED lagged one expanded diff --git a/patches/gromacs-2020.7.diff/src/gromacs/mdrun/md.cpp b/patches/gromacs-2020.7.diff/src/gromacs/mdrun/md.cpp index 2ab4e4b4c0..5ab4b4ca57 100644 --- a/patches/gromacs-2020.7.diff/src/gromacs/mdrun/md.cpp +++ b/patches/gromacs-2020.7.diff/src/gromacs/mdrun/md.cpp @@ -703,8 +703,8 @@ void gmx::LegacySimulator::do_md() if(plumedswitch){ if(shellfc){ /* relax_shell_flexcon() calls do_force() without setting PLUMED up for the step */ - gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles with this version of GROMACS: " - "PLUMED would not be applied."); + gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles or flexible constraints with this version " + "of GROMACS: PLUMED would not be applied."); } /* detect plumed API version */ int pversion=0; @@ -750,7 +750,7 @@ void gmx::LegacySimulator::do_md() }); } if(vsite){ - /* The forces that PLUMED applies on virtual sites in the energy path are spread below */ + /* When PLUMED runs after do_force(), the forces it applies on virtual sites are spread below */ plumedIsVirtualSite.assign(top_global->natoms,false); for(const AtomProxy atomP : AtomRange(*top_global)){ plumedIsVirtualSite[atomP.globalAtomNumber()]=(atomP.atom().ptype==eptVSite); diff --git a/patches/gromacs-2020.7.diff/src/gromacs/mdrun/rerun.cpp b/patches/gromacs-2020.7.diff/src/gromacs/mdrun/rerun.cpp index 0137808e67..3a5b7282a3 100644 --- a/patches/gromacs-2020.7.diff/src/gromacs/mdrun/rerun.cpp +++ b/patches/gromacs-2020.7.diff/src/gromacs/mdrun/rerun.cpp @@ -418,8 +418,8 @@ void gmx::LegacySimulator::do_rerun() if(plumedswitch){ if(shellfc){ /* relax_shell_flexcon() calls do_force() without setting PLUMED up for the step */ - gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles with this version of GROMACS: " - "PLUMED would not be applied."); + gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles or flexible constraints with this version " + "of GROMACS: PLUMED would not be applied."); } /* detect plumed API version */ int pversion=0; diff --git a/patches/gromacs-2020.7.diff/src/gromacs/mdrun/runner.cpp b/patches/gromacs-2020.7.diff/src/gromacs/mdrun/runner.cpp index 4e3353c4ca..909616ec97 100644 --- a/patches/gromacs-2020.7.diff/src/gromacs/mdrun/runner.cpp +++ b/patches/gromacs-2020.7.diff/src/gromacs/mdrun/runner.cpp @@ -1599,7 +1599,7 @@ int Mdrunner::mdrunner() if(useModularSimulator) gmx_fatal(FARGS, "PLUMED is not yet compatible with GROMACS new modular simulator"); #if GMX_THREAD_MPI /* all the thread-MPI ranks would share the same PLUMED object */ - if(PAR(cr)) gmx_fatal(FARGS, "PLUMED cannot be used with more than one thread-MPI rank: use GROMACS built with MPI"); + if(PAR(cr)) gmx_fatal(FARGS, "PLUMED cannot be used with more than one thread-MPI rank: run with -ntmpi 1, or use GROMACS built with MPI"); #endif /* detect plumed API version */ int pversion=0; diff --git a/patches/gromacs-2021.7.diff/src/gromacs/mdrun/md.cpp b/patches/gromacs-2021.7.diff/src/gromacs/mdrun/md.cpp index 1140372caa..4933e54bb2 100644 --- a/patches/gromacs-2021.7.diff/src/gromacs/mdrun/md.cpp +++ b/patches/gromacs-2021.7.diff/src/gromacs/mdrun/md.cpp @@ -720,8 +720,8 @@ void gmx::LegacySimulator::do_md() if(plumedswitch){ if(shellfc){ /* relax_shell_flexcon() calls do_force() without setting PLUMED up for the step */ - gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles with this version of GROMACS: " - "PLUMED would not be applied."); + gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles or flexible constraints with this version " + "of GROMACS: PLUMED would not be applied."); } /* detect plumed API version */ int pversion=0; @@ -767,7 +767,7 @@ void gmx::LegacySimulator::do_md() }); } if(vsite){ - /* The forces that PLUMED applies on virtual sites in the energy path are spread below */ + /* When PLUMED runs after do_force(), the forces it applies on virtual sites are spread below */ plumedIsVirtualSite.assign(top_global->natoms,false); for(const AtomProxy atomP : AtomRange(*top_global)){ plumedIsVirtualSite[atomP.globalAtomNumber()]=(atomP.atom().ptype==eptVSite); diff --git a/patches/gromacs-2021.7.diff/src/gromacs/mdrun/rerun.cpp b/patches/gromacs-2021.7.diff/src/gromacs/mdrun/rerun.cpp index 28ff670f9d..4f594f36fb 100644 --- a/patches/gromacs-2021.7.diff/src/gromacs/mdrun/rerun.cpp +++ b/patches/gromacs-2021.7.diff/src/gromacs/mdrun/rerun.cpp @@ -396,8 +396,8 @@ void gmx::LegacySimulator::do_rerun() if(plumedswitch){ if(shellfc){ /* relax_shell_flexcon() calls do_force() without setting PLUMED up for the step */ - gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles with this version of GROMACS: " - "PLUMED would not be applied."); + gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles or flexible constraints with this version " + "of GROMACS: PLUMED would not be applied."); } /* detect plumed API version */ int pversion=0; diff --git a/patches/gromacs-2021.7.diff/src/gromacs/mdrun/runner.cpp b/patches/gromacs-2021.7.diff/src/gromacs/mdrun/runner.cpp index c3ae63a09e..a6ef5cee92 100644 --- a/patches/gromacs-2021.7.diff/src/gromacs/mdrun/runner.cpp +++ b/patches/gromacs-2021.7.diff/src/gromacs/mdrun/runner.cpp @@ -1770,7 +1770,7 @@ int Mdrunner::mdrunner() if(useModularSimulator) gmx_fatal(FARGS, "PLUMED is not yet compatible with GROMACS new modular simulator"); #if GMX_THREAD_MPI /* all the thread-MPI ranks would share the same PLUMED object */ - if(PAR(cr)) gmx_fatal(FARGS, "PLUMED cannot be used with more than one thread-MPI rank: use GROMACS built with MPI"); + if(PAR(cr)) gmx_fatal(FARGS, "PLUMED cannot be used with more than one thread-MPI rank: run with -ntmpi 1, or use GROMACS built with MPI"); #endif /* detect plumed API version */ int pversion=0; diff --git a/patches/gromacs-2022.5.diff/src/gromacs/mdrun/md.cpp b/patches/gromacs-2022.5.diff/src/gromacs/mdrun/md.cpp index 1c12c335d5..0b742a8d69 100644 --- a/patches/gromacs-2022.5.diff/src/gromacs/mdrun/md.cpp +++ b/patches/gromacs-2022.5.diff/src/gromacs/mdrun/md.cpp @@ -823,8 +823,8 @@ void gmx::LegacySimulator::do_md() if(plumedswitch){ if(shellfc){ /* relax_shell_flexcon() calls do_force() without setting PLUMED up for the step */ - gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles with this version of GROMACS: " - "PLUMED would not be applied."); + gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles or flexible constraints with this version " + "of GROMACS: PLUMED would not be applied."); } /* detect plumed API version */ int pversion=0; @@ -870,7 +870,7 @@ void gmx::LegacySimulator::do_md() }); } if(vsite){ - /* The forces that PLUMED applies on virtual sites in the energy path are spread below */ + /* When PLUMED runs after do_force(), the forces it applies on virtual sites are spread below */ plumedIsVirtualSite.assign(top_global.natoms,false); for(const AtomProxy atomP : AtomRange(top_global)){ plumedIsVirtualSite[atomP.globalAtomNumber()]=(atomP.atom().ptype==ParticleType::VSite); diff --git a/patches/gromacs-2022.5.diff/src/gromacs/mdrun/rerun.cpp b/patches/gromacs-2022.5.diff/src/gromacs/mdrun/rerun.cpp index 6f4e2e1fd2..6e8ad48b2b 100644 --- a/patches/gromacs-2022.5.diff/src/gromacs/mdrun/rerun.cpp +++ b/patches/gromacs-2022.5.diff/src/gromacs/mdrun/rerun.cpp @@ -444,8 +444,8 @@ void gmx::LegacySimulator::do_rerun() if(plumedswitch){ if(shellfc){ /* relax_shell_flexcon() calls do_force() without setting PLUMED up for the step */ - gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles with this version of GROMACS: " - "PLUMED would not be applied."); + gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles or flexible constraints with this version " + "of GROMACS: PLUMED would not be applied."); } /* detect plumed API version */ int pversion=0; diff --git a/patches/gromacs-2022.5.diff/src/gromacs/mdrun/runner.cpp b/patches/gromacs-2022.5.diff/src/gromacs/mdrun/runner.cpp index 6048ae5ca9..8cebdeec35 100644 --- a/patches/gromacs-2022.5.diff/src/gromacs/mdrun/runner.cpp +++ b/patches/gromacs-2022.5.diff/src/gromacs/mdrun/runner.cpp @@ -2134,7 +2134,7 @@ int Mdrunner::mdrunner() if(useModularSimulator) gmx_fatal(FARGS, "PLUMED is not yet compatible with GROMACS new modular simulator"); #if GMX_THREAD_MPI /* all the thread-MPI ranks would share the same PLUMED object */ - if(PAR(cr)) gmx_fatal(FARGS, "PLUMED cannot be used with more than one thread-MPI rank: use GROMACS built with MPI"); + if(PAR(cr)) gmx_fatal(FARGS, "PLUMED cannot be used with more than one thread-MPI rank: run with -ntmpi 1, or use GROMACS built with MPI"); #endif /* detect plumed API version */ int pversion=0; diff --git a/patches/gromacs-2023.5.diff/src/gromacs/mdrun/md.cpp b/patches/gromacs-2023.5.diff/src/gromacs/mdrun/md.cpp index 7a87164c67..80ac6fcb09 100644 --- a/patches/gromacs-2023.5.diff/src/gromacs/mdrun/md.cpp +++ b/patches/gromacs-2023.5.diff/src/gromacs/mdrun/md.cpp @@ -813,8 +813,8 @@ void gmx::LegacySimulator::do_md() if(plumedswitch){ if(shellfc){ /* relax_shell_flexcon() calls do_force() without setting PLUMED up for the step */ - gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles with this version of GROMACS: " - "PLUMED would not be applied."); + gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles or flexible constraints with this version " + "of GROMACS: PLUMED would not be applied."); } /* detect plumed API version */ int pversion=0; @@ -860,7 +860,7 @@ void gmx::LegacySimulator::do_md() }); } if(vsite){ - /* The forces that PLUMED applies on virtual sites in the energy path are spread below */ + /* When PLUMED runs after do_force(), the forces it applies on virtual sites are spread below */ plumedIsVirtualSite.assign(top_global.natoms,false); for(const AtomProxy atomP : AtomRange(top_global)){ plumedIsVirtualSite[atomP.globalAtomNumber()]=(atomP.atom().ptype==ParticleType::VSite); diff --git a/patches/gromacs-2023.5.diff/src/gromacs/mdrun/rerun.cpp b/patches/gromacs-2023.5.diff/src/gromacs/mdrun/rerun.cpp index db03341b4e..af848243fc 100644 --- a/patches/gromacs-2023.5.diff/src/gromacs/mdrun/rerun.cpp +++ b/patches/gromacs-2023.5.diff/src/gromacs/mdrun/rerun.cpp @@ -439,8 +439,8 @@ void gmx::LegacySimulator::do_rerun() if(plumedswitch){ if(shellfc){ /* relax_shell_flexcon() calls do_force() without setting PLUMED up for the step */ - gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles with this version of GROMACS: " - "PLUMED would not be applied."); + gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles or flexible constraints with this version " + "of GROMACS: PLUMED would not be applied."); } /* detect plumed API version */ int pversion=0; diff --git a/patches/gromacs-2023.5.diff/src/gromacs/mdrun/runner.cpp b/patches/gromacs-2023.5.diff/src/gromacs/mdrun/runner.cpp index 0c552edb08..757989a849 100644 --- a/patches/gromacs-2023.5.diff/src/gromacs/mdrun/runner.cpp +++ b/patches/gromacs-2023.5.diff/src/gromacs/mdrun/runner.cpp @@ -2248,7 +2248,7 @@ int Mdrunner::mdrunner() if(useModularSimulator) gmx_fatal(FARGS, "PLUMED is not yet compatible with GROMACS new modular simulator"); #if GMX_THREAD_MPI /* all the thread-MPI ranks would share the same PLUMED object */ - if(PAR(cr)) gmx_fatal(FARGS, "PLUMED cannot be used with more than one thread-MPI rank: use GROMACS built with MPI"); + if(PAR(cr)) gmx_fatal(FARGS, "PLUMED cannot be used with more than one thread-MPI rank: run with -ntmpi 1, or use GROMACS built with MPI"); #endif /* detect plumed API version */ int pversion=0; diff --git a/patches/gromacs-2024.3.diff/src/gromacs/mdrun/md.cpp b/patches/gromacs-2024.3.diff/src/gromacs/mdrun/md.cpp index 7f39232cfe..b803972755 100644 --- a/patches/gromacs-2024.3.diff/src/gromacs/mdrun/md.cpp +++ b/patches/gromacs-2024.3.diff/src/gromacs/mdrun/md.cpp @@ -818,8 +818,8 @@ void gmx::LegacySimulator::do_md() if(plumedswitch){ if(shellfc){ /* relax_shell_flexcon() calls do_force() without setting PLUMED up for the step */ - gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles with this version of GROMACS: " - "PLUMED would not be applied."); + gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles or flexible constraints with this version " + "of GROMACS: PLUMED would not be applied."); } /* detect plumed API version */ int pversion=0; @@ -865,7 +865,7 @@ void gmx::LegacySimulator::do_md() }); } if(virtualSites_){ - /* The forces that PLUMED applies on virtual sites in the energy path are spread below */ + /* When PLUMED runs after do_force(), the forces it applies on virtual sites are spread below */ plumedIsVirtualSite.assign(topGlobal_.natoms,false); for(const AtomProxy atomP : AtomRange(topGlobal_)){ plumedIsVirtualSite[atomP.globalAtomNumber()]=(atomP.atom().ptype==ParticleType::VSite); diff --git a/patches/gromacs-2024.3.diff/src/gromacs/mdrun/rerun.cpp b/patches/gromacs-2024.3.diff/src/gromacs/mdrun/rerun.cpp index ca09f7d512..0af63e39a3 100644 --- a/patches/gromacs-2024.3.diff/src/gromacs/mdrun/rerun.cpp +++ b/patches/gromacs-2024.3.diff/src/gromacs/mdrun/rerun.cpp @@ -443,8 +443,8 @@ void gmx::LegacySimulator::do_rerun() if(plumedswitch){ if(shellfc){ /* relax_shell_flexcon() calls do_force() without setting PLUMED up for the step */ - gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles with this version of GROMACS: " - "PLUMED would not be applied."); + gmx_fatal(FARGS,"PLUMED cannot be used with shell (polarizable) particles or flexible constraints with this version " + "of GROMACS: PLUMED would not be applied."); } /* detect plumed API version */ int pversion=0; diff --git a/patches/gromacs-2024.3.diff/src/gromacs/mdrun/runner.cpp b/patches/gromacs-2024.3.diff/src/gromacs/mdrun/runner.cpp index 59e72da05f..70a1906f05 100644 --- a/patches/gromacs-2024.3.diff/src/gromacs/mdrun/runner.cpp +++ b/patches/gromacs-2024.3.diff/src/gromacs/mdrun/runner.cpp @@ -2330,7 +2330,7 @@ int Mdrunner::mdrunner() if(useModularSimulator) gmx_fatal(FARGS, "PLUMED is not yet compatible with GROMACS new modular simulator"); #if GMX_THREAD_MPI /* all the thread-MPI ranks would share the same PLUMED object */ - if(PAR(cr)) gmx_fatal(FARGS, "PLUMED cannot be used with more than one thread-MPI rank: use GROMACS built with MPI"); + if(PAR(cr)) gmx_fatal(FARGS, "PLUMED cannot be used with more than one thread-MPI rank: run with -ntmpi 1, or use GROMACS built with MPI"); #endif /* detect plumed API version */ int pversion=0;