Skip to content

HPC hardening: single-GPU saturation, downstream-WE parallelism, and cleanups#10

Merged
namus merged 10 commits into
mainfrom
claude/hpc-single-gpu-saturation
Jul 8, 2026
Merged

HPC hardening: single-GPU saturation, downstream-WE parallelism, and cleanups#10
namus merged 10 commits into
mainfrom
claude/hpc-single-gpu-saturation

Conversation

@namus

@namus namus commented Jul 8, 2026

Copy link
Copy Markdown
Member

Summary

Hardens PathGennie for HPC testing on CPU and GPU nodes. The headline change lets the OpenMM backend saturate a single GPU by running many swarm walkers concurrently, and makes the downstream Weighted Ensemble spread across a node's GPUs/cores. It also fixes an executable-resolution bug that broke module load binaries, speeds up on-the-fly SPIB, and removes the non-functional MPI/Dask executors. The path-refinement review fixes and repo hygiene ride along.

10 commits; 107 unit tests pass and the HPC self-check is 7/7.

What changed & why

HPC scaling (the focus of this branch)

  • Single-GPU saturation (OpenMM). OpenMMEngine now backs a pool of interchangeable Contexts on one card, handed out one-per-thread via a queue with a shared, lock-guarded State cache (States are portable across Contexts of the same System). PathGennieMD sizes the pool from workers_per_device — an int, or auto (CPU cores capped by free GPU memory, probed while the pool is built). n_workers=1 is exactly the old single-Context path (no regression). Previously trials ran one at a time, leaving the GPU idle for small systems.
  • Downstream WE parallelism. The backend's device pool is now forwarded through run_downstreammake_stageWeightedEnsembleStage, so WE walker propagation spreads across GPUs/cores instead of silently running serially. No algorithm change.
  • Executable resolution. AMBER/GROMACS runners resolve executable with shutil.which, so a bare module load name (gmx, pmemd.cuda, sander) works instead of failing Path.exists(); example configs use bare names.
  • SPIB performance/memory. SPIBProgress caches features incrementally (was re-featurizing the whole growing buffer each refresh, ~O(N²)) with an optional bounded sliding window.
  • Removed MPIExecutor/DaskExecutor and the [hpc] extra. They never worked (per-cycle work closes over live, unpicklable engine state) and were wired to no backend.
  • tau2 on the winning device. The commit segment runs on the device of the softmax-selected sampler.

Path refinement (review fixes)

  • Snap refined NN path nodes to the nearest physical trajectory frame (project_to_real); fix the converged flag so a walker-generation failure isn't reported as convergence; use a true discrete Fréchet distance for path convergence (was mislabeled Hausdorff); renumber the example pipelines.

Repo hygiene

  • Ignore Manuscripts/ and .DS_Store; remove the superseded amberengine.py and unused standalone we/ scratch scripts.

Scaling model (reviewer note)

Path discovery is single-GPU, saturated by concurrent walkers. Multi-GPU/CPU belongs to the downstream WE via the device pool. Multi-node is Slurm/PBS array jobs over independent pathways/replicates (how the path-resolved-kinetics workflow already runs channels). There is deliberately no in-process multi-node executor; a work-queue manager for a single tightly-coupled run stays on the roadmap.

Testing

  • pytest -q tests --ignore tests/hpc107 passed (torch installed so the SPIB lane runs).
  • python tests/hpc/hpc_selfcheck.py7/7, including the new executable_resolution and concurrent_openmm checks.
  • OpenMM concurrent pool verified on the CPU platform: a 4-worker run reproduces the serial result exactly with a deterministic integrator; WE walker spread verified across a mock device pool.
  • Real multi-GPU spread and OpenMM single-GPU saturation are to be validated on the cluster via the tests/hpc/ Slurm/PBS job scripts and DEBUGGING.md.

Not included / follow-ups

  • Real-GPU throughput numbers (run on the cluster).
  • The OpenMM example still ships platform: CPU; switch to CUDA + workers_per_device: auto for the saturation test.
  • No version bump (changes recorded under CHANGELOG.md [Unreleased]).

🤖 Generated with Claude Code

Suman Chakrabarty and others added 10 commits July 8, 2026 20:49
Keep large manuscript sources and macOS Finder metadata out of the tree.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SPIBProgress re-featurized the entire growing coordinate buffer on every
refresh (~O(N^2) over a long adaptive run) and held full coordinate arrays in
host memory. Featurize once when a frame is observed and cache the vectors
(features are far smaller than a solvated configuration); an optional
max_buffer keeps a most-recent sliding window while preserving the start frame.
Results are identical to the previous path when unbounded.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Record each trial's device on TrialResult and run the tau2 runner on the
device of the softmax-selected sampler, instead of always the pool's first
device. Keeps the commit segment on the same GPU that produced the winner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…refresh

- EnsemblePathRefinerFast.transform gains project_to_real: snap each refined
  node to the nearest physically realized trajectory frame (needed for 3D
  macromolecules so PathCV/WE seeds are not unphysical). CPU fallback when CUDA
  is requested but unavailable.
- refiner.py wires project_to_real through PathRefinementConfig and no longer
  reports converged=True when it breaks on a walker-generation failure.
- verify scripts use a true discrete Frechet distance (was mislabeled Hausdorff)
  for path-to-path convergence, matching the manuscript.
- renumber example pipelines (1_generate -> 2_run -> 3_analyze), rename the
  toy checker to verify_path_refinement.py, and refresh docs/README.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…(OpenMM)

Path generation stays single-GPU but no longer runs swarm trials one at a time.
OpenMMEngine now backs a pool of interchangeable Contexts on the one card,
handed out one-per-thread via a queue with a shared, lock-guarded State cache
(States are portable across Contexts of the same System). PathGennieMD sizes the
pool from workers_per_device: an int, or 'auto' (CPU cores capped by free GPU
memory, probed while the pool is built) and drives it with a ThreadDevicePool.
n_workers=1 is exactly the previous single-Context path (no regression);
resolve_worker_count and the config schema accept 'auto'. Verified on CPU: a
4-worker run reproduces the serial result with a deterministic integrator.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… the device pool

- AMBER/GROMACS runners resolve 'executable' with shutil.which so a bare
  module-load name (gmx, pmemd.cuda, sander) works instead of failing
  Path.exists(); example configs switch to bare names.
- Forward the backend's ThreadDevicePool into the downstream Weighted Ensemble
  (run_downstream -> make_stage -> WeightedEnsembleStage) so its walker
  propagation spreads across the same GPUs/cores the discovery swarm used
  instead of running serially. OpenMM reuses its concurrent-Context pool.
- Remove the superseded amberengine.py (replaced by the device-aware
  CoreAmberEngine in engine.py).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… tau2-device regression

hpc_selfcheck.py confirms the case's configured MD executable resolves the way the
backends do (shutil.which) and smoke-tests the OpenMM concurrent-Context pool on
the CPU platform; the HPC README documents the single-GPU-saturation model and the
new checks. Adds the tau2-runs-on-chosen-device regression to the parallel suite.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Delete unused main2.py / main_clean.py from the standalone we/ package.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… extra

MPIExecutor/DaskExecutor never worked — the driver's per-cycle work closes over
live engine state that cannot be pickled across nodes — and were wired to no
backend. Remove the two classes, the mpi4py/dask '[hpc]' optional dependency, and
their package probes.

The supported patterns cover every practical PathGennie workload and are now the
documented scaling model: path discovery saturates a single GPU with concurrent
walkers, the downstream Weighted Ensemble spreads across a node's GPUs/cores, and
multi-node throughput comes from running independent pathways/replicates as
Slurm/PBS array jobs. A work-queue manager for a single tightly-coupled
multi-node run stays on the roadmap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…w self-checks

The OpenMM 'gpu_spread' row no longer claims a SerialExecutor/one-Context path;
OpenMM now saturates a single card with a concurrent-Context pool. Add triage
rows for the executable_resolution and concurrent_openmm self-checks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@namus namus merged commit c95ead3 into main Jul 8, 2026
3 checks passed
@namus namus deleted the claude/hpc-single-gpu-saturation branch July 8, 2026 17:22
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.

1 participant