From 59ae1ec7800cc07f048345211e91850b5d51d64d Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Wed, 22 Jul 2026 11:30:51 +0100 Subject: [PATCH] Restrict energy decomposition to force groups used for integration. --- CHANGELOG.md | 1 + src/somd2/runner/_base.py | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9681d0..3fcfe2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Changelog * Persist alchemical ion identity across restarts so the same molecule is reused regardless of GCMC state [#172](https://github.com/OpenBioSim/somd2/pull/172). * Use perisistent `ThreadPoolExector` objects within the main replica exchange dynamics block [#175](https://github.com/OpenBioSim/somd2/pull/175). * Allow `oversubscription_factor` to change on restart [#177](https://github.com/OpenBioSim/somd2/pull/177). +* Restrict energy component decomposition to force groups that are used for integration [#180](https://github.com/OpenBioSim/somd2/pull/180). [2026.1.0](https://github.com/openbiosim/somd2/compare/2025.1.0...2026.1.0) - Jun 2026 -------------------------------------------------------------------------------------- diff --git a/src/somd2/runner/_base.py b/src/somd2/runner/_base.py index 26487b8..1c915e6 100644 --- a/src/somd2/runner/_base.py +++ b/src/somd2/runner/_base.py @@ -615,6 +615,9 @@ def __init__(self, system, config): # used to skip duplicate rows on restart. self._last_ec_time = {} + # Per-window cache of the integrator's integration force groups bitmask. + self._integration_groups = {} + # Store the current system as a reference. self._reference_system = self._system.clone() @@ -2514,10 +2517,19 @@ def _save_energy_components(self, index, context, time_ns): if time_ns <= self._last_ec_time[index]: return + if index not in self._integration_groups: + self._integration_groups[index] = ( + context.getIntegrator().getIntegrationForceGroups() + ) + integration_groups = self._integration_groups[index] + # Use the named force groups already assigned by sire_to_openmm_system, # sorted alphabetically for a consistent column order across runs. + # Skip any group not actually used for integration. energies = {} for name, grp in sorted(context._force_group_map.items()): + if not integration_groups & (1 << grp): + continue state = context.getState(getEnergy=True, groups=(1 << grp)) energies[name] = state.getPotentialEnergy().value_in_unit( openmm.unit.kilocalories_per_mole