Fix 1205 gromacs2026 energy - #1446
Open
invemichele-peptone wants to merge 3 commits into
Open
Conversation
There is no patch for GROMACS 2026, so `plumed patch -e` offers nothing newer than
gromacs-2025.0 and there is no supported way to build a PLUMED-enabled GROMACS 2026.
Applying the 2025.0 patch to 2026.4 does not work. GROMACS replaced the t_commrec
plumbing with the MpiComm abstraction in db61f87e0a ("Use MpiComm in t_commrec"), so
PlumedOptions now carries `const MpiComm* mpiComm_` instead of `const t_commrec* cr_`:
$ plumed patch -p -e gromacs-2025.0 # on a 2026.4 tree
patching file ./src/gromacs/applied_forces/plumed/plumedforceprovider.cpp
Hunk plumed#3 FAILED at 113.
1 out of 4 hunks FAILED -- saving rejects to ...plumedforceprovider.cpp.rej
patching file ./src/gromacs/CMakeLists.txt
Reversed (or previously applied) patch detected! Skipping patch.
2 out of 2 hunks ignored -- saving rejects to ./src/gromacs/CMakeLists.txt.rej
and six further files apply only with fuzz 5. The failing hunk is the one that hands
PLUMED the multi-replica communicators, which is the most damaging thing to lose
silently: `GREX setMPIIntercomm` is what populates PlumedMain::multi_sim_comm
(GREX.cpp), so without it every multi-replica action degrades to a single replica
without any error.
The new patch is the 2025.0 one carried onto the 2026 API:
* the GREX/multisim block uses MpiComm::isMainRank() and MpiComm::comm()
* src/gromacs/CMakeLists.txt is dropped entirely -- GROMACS 2026 already calls
gmx_manage_plumed() early, which is why those hunks reported as already applied
* setNumOMPthreads is carried over from plumed#1435
* the replex_ plumbing, the PLUMED_LOG_FILE override and forcing GMX_USE_PLUMED=ON
are unchanged in substance
It also fixes one bug in GROMACS's own code. The same MpiComm commit rewrote
- plumed_->cmd("setMPIComm", &options.cr_->mpi_comm_mygroup);
+ plumed_->cmd("setMPIComm", options.mpiComm_->comm());
dropping the address-of. MpiComm::comm() returns MPI_Comm by value, but PLUMED reads
that argument as `*(const MPI_Comm*)` (Communicator::Set_comm), so where MPI_Comm is a
pointer type this compiles and silently passes the wrong handle. The patch passes the
address of a named local instead. This is worth reporting to GROMACS separately; until
it is fixed there, any domain-decomposed PLUMED run on 2026 is affected.
Verified on GROMACS 2026.4 with clang 20.1.1, OpenMPI 5.0.7 and libc++:
* `plumed patch -p -e gromacs-2026.0` applies with no fuzz and no rejects, and
`plumed patch -r` restores the tree byte-for-byte
* the patched GROMACS builds clean (GMX_MPI=ON, GMX_THREAD_MPI=OFF, GMX_OPENMP=ON);
the four changed translation units compile with no errors and no warnings
* a two-replica `mdrun -multidir` run expands `@replicas:` to a different value per
replica, which is only possible when multi_sim_comm has been set up
Scope for 2027: the plumed module sources on GROMACS main are currently identical to
release-2026, and the runner.cpp and mdmodulesnotifiers.h edits apply there unchanged;
only the stored .preplumed snapshots would need regenerating once 2027 branches.
Builds on the gromacs-2026.0 patch: the native GROMACS PLUMED module never calls setEnergy, so ENERGY silently stayed 0 for the whole run. Nothing could request GMX_FORCE_ENERGY per step, and calculateForces() runs before accumulatePotentialEnergies(), so neither the energy nor the complete force buffer exist yet. Add two defaulted IForceProvider hooks: requestsPotentialEnergy(step), called before setupStepWorkload() so the energy can still be scheduled, and applyAfterPotentialEnergy(), called once the potential energy is accumulated and the total force is assembled. PLUMED answers the first by running the prepareDependencies() half of prepareCalc(), which needs only the step number, and the shareData() half later from calculateForces(). Energy biasing then rescales the total force in the late hook, using the same virial convention as the legacy patches. See plumed#1205 Co-authored-by: Cursor <cursoragent@cursor.com>
Energy minimisation and mdrun -rerun do reach applyAfterPotentialEnergy() with a valid potential energy, so listing them as unsupported was wrong. The only path that cannot supply the energy is the modular simulator, which bypasses the md.cpp loop where the energy is requested, and which is the default for the md-vv integrator. Point users at GMX_DISABLE_MODULAR_SIMULATOR=1 instead. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Support ENERGY in the GROMACS 2026 patch
Branch:
fix-1205-gromacs2026-energy→ basev2.10· Stacked on: #1439, please merge that firstRelated: #1205, #1384
Description
#1439 brings
patches/gromacs-2026.0into the tree, which is what makes a PLUMED-enabled GROMACS2026 buildable at all. This PR adds one thing on top: the
ENERGYcollective variable and energybiasing, which the native GROMACS PLUMED module does not support on any version from 2025 onwards.
On stock GROMACS 2025/2026 an input containing
ENERGYruns to completion and produces0.0forthe whole trajectory with no warning:
plumedforceprovider.cppnever issuessetEnergy, and onthe PLUMED side
ActionToPutData::wait()returns early whenwasset == false. SoOPES_EXPANDEDwith
ECV_MULTITHERMAL, the well-tempered ensemble, PT-WTE and energy reweighting are silentlywrong rather than unavailable. The GROMACS 2026 manual lists "interaction with GROMACS energy"
under the interface Limitations.
This branch starts from #1439 rather than
v2.10because it editspatches/gromacs-2026.0.diff/.../plumedforceprovider.{cpp,h}, which #1439 introduces. It does notre-do anything #1439 covers — the
setMPICommaddress-of fix, the GREX/multisim block,setNumOMPthreads,PLUMED_LOG_FILEand thereplexguard are taken as-is. The replica-exchangeguard is factored into a small
checkReplicaExchangeBias()helper so the new deferred pathperforms the same check; its behaviour is unchanged. The companion PR against
v2.10does thesame for
patches/gromacs-2025.0and also fixes the GROMACS 2024 regression (#1205), which hasthe same visible symptom.
What blocks energy biasing in the ForceProvider model. Nobody can ask GROMACS to compute the
energy:
stepWork.computeEnergyis only true onnstcalcenergysteps and a force provider cannotinfluence the step workload. At force-provider time the energy does not exist yet:
calculateForces()is called fromcomputeSpecialForces(), several hundred lines beforeaccumulatePotentialEnergies()fills the potential-energy term. And the force-rescaling trick isnot expressible: biasing the energy multiplies the entire force array by
1 - ∂V/∂E(PLUMED doesthis in
Energy::apply()viarescaleForces), while a provider only owns a separate additiveForceWithVirialbuffer and the total force is not assembled at that point.Design. Two additions to
IForceProvider, both defaulting to no-ops so no other module isaffected, forwarded by
ForceProvidersto every registered provider:md.cppcalls the first immediately beforesetupStepWorkload(), the last moment the workload canstill change, and ORs in
GMX_FORCE_ENERGY | GMX_FORCE_VIRIAL. The answer has to be exact forthis step rather than inherited from the previous one, so
PlumedForceProvider::requestsPotentialEnergy()splitsprepareCalc()in two: whether PLUMEDneeds the energy depends on which actions are active, which is known after
prepareDependencies(),and that half needs only the step number (
setStepLong→prepareDependencies→isEnergyNeeded). This is the sequence thePLUMEDaction already uses insrc/generic/Plumed.cpp, so it needs no new PLUMED command and works with any 2.10 kernel;prepareDependencies()only activates actions, with all atom and domain-decomposition handlingstaying inside
shareData(), so splitting there is safe.calculateForces()runs the second half(
shareData) once positions are available and falls back to the fullprepareCalc()when theearly hook did not run for this step — that fallback is what makes energy minimisation and
mdrun -rerunwork, since neither goes through themd.cpploop but both always compute theenergy.
sim_util.cppcalls the late hook afteraccumulatePotentialEnergies()andpostProcessForces(), where the energy is valid and the total force buffer is complete; therePLUMED does
setEnergy/setForceson the total force /setVirial/performCalc, with thesame
2 ×/0.5 ×virial convention and virial replacement as the legacy 2023 and 2024patches. When the energy is not needed the existing additive path is untouched. If a step that
never asked for the energy reaches the late hook needing it, PLUMED throws a
NotImplementedErrorpointing at the modular simulator and
GMX_DISABLE_MODULAR_SIMULATOR=1rather than biasing on zero.Four files are new to
patches/gromacs-2026.0.diff(iforceprovider.{h,cpp},md.cpp,sim_util.cpp), two are edits to files #1439 already carries (plumedforceprovider.{cpp,h}), andthe
.configblurb gains a line. As #1439 explains, every patched file is stored twice in full, soGitHub reports ~10.7k added lines for what is 76 changed lines of GROMACS source.
Verification
GROMACS 2026.3, mixed precision, 216 SPC waters (648 atoms), OPLS-AA, 0.8 nm cut-offs,
dt = 2 fs, v-rescale at 300 K, LINCS on h-bonds,nstcalcenergy = 100.GROMACS 2023.5 with the legacy patch is the reference, since it predates the whole problem.
plumed patch -p -e gromacs-2026.0applies with no fuzz and no rejects,plumed patch -rrestoresthe tree byte-for-byte, and the patched tree builds with no errors or warnings in any of the six
changed translation units.
ENERGYevery step over 250 steps gives 251 / 251 finite non-zerovalues, where unpatched the same input yields
0.0on every step.PACE=25,OBSERVATION_STEPS=8, 1500 stepsENERGY == 0DeltaFat 350 K after 3 ps (kJ/mol)The temperature count is the sharpest check, because OPES derives the grid from the energy
fluctuations observed during the first 0.4 ps: with the energy stuck at zero it collapses to 2
temperatures and every
DeltaFdegenerates. Trajectories are not bit-identical across GROMACSversions, so the residual spread is MD noise. Also checked:
PRINT ARG=ene STRIDE=7againstnstcalcenergy = 100, a stride sharing no factor with the energy period and precisely the case aninherited, one-step-late request cannot serve; restart via
-cpifrom step 150, not a multiple ofnstcalcenergy, with the energy continuous across it; andDISTANCE+RESTRAINTunchanged,confirming the additive
ForceWithVirialpath is untouched.GPU. Rebuilt with
-DGMX_GPU=CUDA(NVIDIA GB10, one rank) and re-run with PME so the GPU hasreal work.
ENERGYevery step is correctunder
-nb cpu(reference),-nb gpu,-nb gpu -pme gpu, fully GPU-resident-nb gpu -pme gpu -update gpu, and the last of those withGMX_CUDA_GRAPH=1: 121 / 121 in everycase, no zeros, no
NaN. OPES multithermal gives 11–12 temperatures andDeltaF1437–1438 acrossCPU,
-nb gpuand GPU-resident, matching the CPU baseline. The strongest check is against GROMACSitself rather than another run: on every step written to the
.edrthe value PLUMED receivesmatches GROMACS' own
Potentialterm to ≤ 5 × 10⁻⁸ relative in all five configurations,including fully GPU-resident stepping. Restarting across a non-
nstcalcenergystep with-update gpuis clean. Note that GROMACS only takes the CUDA-graph path when nothing computesforces on the CPU, which PLUMED always does, so
GMX_CUDA_GRAPH=1exercises the surroundingbookkeeping rather than graph capture itself.
MPI and domain decomposition. Rebuilt with
-DGMX_MPI=ON(OpenMPI) and run on 1728 SPC waters(5184 atoms) on 1, 2 and 4 ranks, DD grids
2 x 1 x 1and4 x 1 x 1. The deferred hook behaves:ENERGYis finite and non-zero on all 1001 frames at every rank count, OPES multithermal runsclean and
DeltaFat 350 K agrees between 1 and 4 ranks to 1 kJ/mol, and the value PLUMED receivesmatches GROMACS' own
Potentialterm to ≤ 1.5 × 10⁻⁷ relative. This also exercises thesetMPICommaddress-of fix that #1439 brings in, without which the run aborts before it starts.Remaining gaps. The modular simulator is the one
unsupported path: it bypasses the
md.cpploop, so the early hook never runs and the energy isnever scheduled. It is the default for the
md-vvintegrator, where energy-dependent input nowfails with the
NotImplementedErrorabove; withGMX_DISABLE_MODULAR_SIMULATOR=1,md-vvworks.PLUMED input that does not use the energy is unaffected and still runs under the modular simulator.
Energy minimisation and
mdrun -rerundo work. Under MTS the total force is taken from thecombined buffer only on slow steps. The two hooks are the minimal shape of what would need to go
upstream; GROMACS issue #4939 does not list
energy biasing among the requirements for the PLUMED interface, which is probably why it was never
designed in.
Target release
I would like my code to appear in release 2.10, alongside #1439 and
patches/gromacs-2025.0.diff.Type of contribution
Copyright
Tests
No regtest is added, for the reason #1439 gives: nothing under
patches/is compiled by PLUMED, sothe test suite cannot exercise it. It is consumed only when a user runs
plumed patchagainst aGROMACS source tree, at which point GROMACS compiles it. The GROMACS builds and MD runs above are
the substitute. This branch touches no core code, so the existing suite is unaffected.