Skip to content

Close a stage's LAMMPS session when the stage raises - #286

Merged
srmnitc merged 2 commits into
ICAMS:mainfrom
pmrv:runner-context-manager
Oct 9, 2026
Merged

srmnitc merged 2 commits into
ICAMS:mainfrom
pmrv:runner-context-manager

Conversation

@pmrv

@pmrv pmrv commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Each stage method (run_averaging, run_integration, reversible_scaling, temperature_scaling, pressure_scaling, scan_temperature_range) creates its own LAMMPS session and closes it as its last statement. So if anything in the stage raises, the session stays open. In library mode that keeps the pylammpsmpi/executorlib workers alive until the process exits.

Changes

  • BaseRunner gets __enter__/__exit__, which close the session on the way out, with or without an exception (calphy/runner.py).
  • Phase gets the same: __exit__ closes the session opened by the stage it wraps. Stages now create their session with lmp = self._open_lammps() instead of ph.create_object(...), so the job remembers it. Stage bodies are otherwise unchanged.
  • In routines.py (and the averaging call in dcci.py), each stage call becomes
    with job:
        job.run_averaging()

On success the stage has already closed the session itself, and the second close does nothing on both backends: LibraryRunner checks _closed, the executable runner's buffer is already empty, and the restart-file removal checks that the file exists.

__exit__ never suppresses the exception. If close() itself fails while an exception is already propagating, the close error is logged and the original exception is re-raised. A dead LAMMPS worker can easily make the close fail, but the error that matters is the one that got us there.

Setup failures are covered too. create_object emits the init commands after it has constructed the runner, and LibraryRunner.__init__ activates ML-IAP after the executor already exists. If either step raises, the runner closes itself (through __exit__) before the error propagates, because at that point nobody else holds a reference to it.

The DCCI driver's own two-cell sessions (dcci.py, lmps[phase]) are not touched here.

Testing

  • Full suite: 444 passed, 26 skipped. This includes the golden command-stream tests, and the emitted LAMMPS command streams are unchanged.
  • New tests: the runner context manager closes on success and on exception, a failing close() raises when no exception is in flight and does not mask one that is, with job: closes the session of a failed stage, and a failure in ML-IAP activation or in the init commands closes the executor. Both of those fail without the fix.
  • No end-to-end run against a real LAMMPS binary.

🤖 Generated with Claude Code

pmrv and others added 2 commits October 8, 2026 16:43
Every stage method (run_averaging, run_integration, reversible_scaling,
temperature_scaling, pressure_scaling, scan_temperature_range) creates its
own LAMMPS session and closes it as its last statement, so an exception
anywhere in the stage leaves the session open. In library mode that keeps
the pylammpsmpi/executorlib workers alive until the process exits.

`BaseRunner` gains `__enter__`/`__exit__`, and `Phase` gains a context that
closes the session opened by the stage it wraps. The routines now run each
stage call as `with job: job.stage(...)`. Stage bodies are untouched apart
from creating the session via `self._open_lammps()`, which remembers it.

On success the stage has already closed the session and the second close is
a no-op on both backends (`LibraryRunner` guards on `_closed`; the
executable runner's buffer is already empty and the restart-file removal is
guarded). If `close()` itself fails while an exception is propagating, the
close error is logged and the original exception wins.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`Phase._open_lammps()` only learns about the runner once
`ph.create_object()` returns, so a failure while setting the session up
left it open: `create_object` emits the init commands after constructing
the runner, and `LibraryRunner.__init__` activates ML-IAP after the
executor already exists. Close the runner in both places before
re-raising, through its `__exit__` so a failing close cannot mask the
setup error.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@pmrv

pmrv commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

@srmnitc I had issues before with calphy/executorlib when lammps errors during a run, e.g. on exploding 'Domain too large for neighbor bins'. This should help.

@srmnitc
srmnitc merged commit 67caa5b into ICAMS:main Oct 9, 2026
6 checks passed
@pmrv
pmrv deleted the runner-context-manager branch October 9, 2026 20:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants