Skip to content
Merged
Show file tree
Hide file tree
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
12 changes: 4 additions & 8 deletions src/sire/mol/_dynamics.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,10 @@ def _exit_dynamics_block(
self._omm_state = self._omm_mols.getState(getEnergy=True)
self._omm_state_has_cv = (False, False)

# dynamics has advanced the positions without going through
# setPositions(), so the context's energy cache is stale
self._omm_mols.clear_energy_cache()

current_time = (
self._omm_state.getTime().value_in_unit(openmm.unit.nanosecond) * nanosecond
)
Expand Down Expand Up @@ -481,10 +485,6 @@ def _exit_dynamics_block(
nrg_sim_lambda_value = nrg

if lambda_windows is not None:
# Positions have just changed (dynamics completed), so
# invalidate all cached per-group energies before the scan.
self._omm_mols.clear_energy_cache()

# get the index of the simulation lambda value in the
# lambda windows list
try:
Expand Down Expand Up @@ -544,10 +544,6 @@ def _exit_dynamics_block(
self._nrgs = nrgs
self._nrgs_array = nrgs_array

# Repex synchronisation point: a peer replica may push new
# positions into this context, so the cache must be invalidated.
self._omm_mols.clear_energy_cache()

# update the interpolation lambda value
if self._is_interpolate:
if delta_lambda:
Expand Down
39 changes: 39 additions & 0 deletions tests/mol/test_dynamics.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,42 @@ def test_crash_report(merged_ethane_methanol, openmm_platform):
finally:
# Change back to the old directory.
os.chdir(old_dir)


@pytest.mark.skipif(
"openmm" not in sr.convert.supported_formats(),
reason="openmm support is not available",
)
def test_energy_cache_cleared_after_dynamics(ala_mols):
"""
The context's energy cache must be invalidated after every dynamics block,
not just one that saved energies. The integrator advances the positions
without going through setPositions(), so nothing else clears it.
"""
import openmm

mols = ala_mols

d = mols.dynamics(timestep="1fs", temperature="300K", platform="Reference")

def direct():
return (
d.context()
.getState(getEnergy=True)
.getPotentialEnergy()
.value_in_unit(openmm.unit.kilocalorie_per_mole)
)

assert d.current_potential_energy().value() == pytest.approx(direct())

# A block that doesn't record an energy.
d.run("50fs")
assert d.current_potential_energy().value() == pytest.approx(direct())

# A block that does.
d.run("50fs", energy_frequency="10fs")
assert d.current_potential_energy().value() == pytest.approx(direct())

# A block that doesn't, again, now that a trajectory exists.
d.run("50fs")
assert d.current_potential_energy().value() == pytest.approx(direct())