Skip to content

Investigate Parsl as Python-native workflow manager for build/simulate orchestration #16

Description

@maxscheurer

Summary

Compute-heavy tasks in MDFactory (building systems, running simulations) are currently handed off to Nextflow (workflows/simulate.nf, workflows/build.nf) and require manual orchestration via shell scripts (workflows/run_full_pipeline.sh). Nextflow will be removed completely. There is no smooth Python-native integration of compute workflow management. For analysis execution, we use submitit to wrap lightweight Python functions for trajectory analyses (mdfactory/analysis/submit.py), but this doesn't extend to the heavier build/simulate workloads.

This issue tracks a proof-of-concept investigation of Parsl as a Python-native workflow manager for MDFactory, particularly for handling GROMACS MD runs and system builds through its bash_app, SLURM provider, retry functionality, and checkpoint/restart support.

Current Behavior

  • Simulation orchestration is done via Nextflow processes (minimization → nvt → npt → production) defined in workflows/simulate.nf with SLURM config in workflows/simulate.config.
  • Build orchestration uses workflows/build.nf to run mdfactory prepare-build then mdfactory build per hash.
  • Analysis dispatch uses submitit (mdfactory/analysis/submit.py) — one SLURM job per simulation path.
  • There is no Python-level MD run orchestration; the user must manually invoke Nextflow and adapt SLURM configuration.
  • No unified status tracking, retry handling, or restart capability across build/simulate/analysis.

Proposed Behavior

A Parsl-based execution layer inside MDFactory that:

  1. Replaces Nextflow entirely for both build and simulation orchestration.
  2. Runs system builds (mdfactory build) on GPU nodes via SLURM — builds are compute-heavy (OpenMM compression) and belong on HPC, not login nodes.
  3. Chains GROMACS simulation stages (EM → NVT → NPT → production) via @bash_app + futures.
  4. Submits work to SLURM via HighThroughputExecutor + SlurmProvider (pilot-job model).
  5. Supports custom workflow composition on the fly — e.g., packing multiple simulations onto a single node/job, or composing arbitrary build→simulate→analysis pipelines from Python.
  6. Closely integrates with and reuses existing MDFactory building blocks (build.py, run_schedules/, Pydantic models) rather than reimplementing logic.
  7. Handles embarrassingly parallel campaigns (hundreds of independent builds/simulations).
  8. Provides retry, checkpoint/restart, and monitoring without external tooling.

Note: SLURM autodetection (detecting whether we're on a SLURM cluster) is out of scope — handled separately in another PR.

Requirements Matrix

Capability Parsl Mechanism PoC Scope
Python-native orchestration @bash_app/@python_app decorators, futures-based DAG ✅ Core
CLI integration Config objects generated from MDFactory models ✅ Core
System-building workflow support @python_app or @bash_app invoking mdfactory build on GPU nodes ✅ Core
Simulation workflow support @bash_app chaining EM→NVT→NPT→production ✅ Core
SLURM submission SlurmProvider + HighThroughputExecutor ✅ Core
Embarrassingly parallel execution Loop of app calls → auto-parallel futures ✅ Core
External command execution @bash_app returns shell command string (with module loads, cd, etc.) ✅ Core
Dynamic workflow generation Plain Python generating app calls from Pydantic models ✅ Core
Pydantic-compatible planning MDFactory models → Parsl config/task generation ✅ Core
Reusable command/task templates Parameterized @bash_app functions reusing MDFactory building blocks ✅ Core
Custom workflow composition Composing tasks flexibly (multi-sim per node, arbitrary pipelines) ✅ Core
Monitoring / job status tracking MonitoringHub → SQLite DB ✅ Core
User-facing status command Query monitoring.db or MDFactory state 🔲 Stretch
Retries Config(retries=N, retry_handler=...) ✅ Core
Restart / resume support checkpoint_mode='task_exit' + get_all_checkpoints() ✅ Core
GROMACS checkpoint restart @bash_app generating gmx mdrun -cpi ... -append 🔲 Stretch
Output-aware completion checks Pre-check output files before submitting task 🔲 Stretch
MDFactory-owned persistent state Sync Parsl monitoring.db to MDFactory registry 🔲 Future
Dry-run / inspectable plan Generate tasks without parsl.load() ✅ Core
Multi-stage workflow composition Future-chaining: step2(step1(x)) ✅ Core
Many-simulation campaign execution Loop over hashes/replicas ✅ Core
Minimal boilerplate Single Config object, no external files ✅ Core
Low conceptual overhead Hidden behind MDFactory CLI/API ✅ Core
Backend abstraction LocalProviderSlurmProvider swap ✅ Core
Future extensibility Multiple executors, @join_app for dynamic sub-workflows 🔲 Future

Acceptance Criteria

  • A working @bash_app that runs gmx grompp + gmx mdrun for a single simulation stage, including proper HPC context (module loads, working directory, environment setup)
  • System build (mdfactory build) running on GPU nodes via SLURM through Parsl
  • Demonstration of chaining multiple stages (at minimum EM → production) via futures
  • SLURM submission via SlurmProvider on the HPC cluster
  • Embarrassingly parallel execution of multiple independent simulations (≥10 concurrent)
  • Custom workflow composition demonstrated (e.g., multiple simulations packed onto a single node/job)
  • Deferred (see also Investigate Parsl as Python-native workflow manager for build/simulate orchestration #16 (comment))
  • Retry behavior validated (task fails → automatic resubmission)
  • Checkpoint/restart demonstrated (re-run skips completed tasks)
  • Integration with existing MDFactory building blocks (reuses build.py, Pydantic models, run schedules — not reimplementing)
  • Dry-run mode that prints resolved commands without submitting
  • Brief write-up of findings: what works, what doesn't, gaps vs. requirements

Technical Notes

Relevant existing code to reuse/integrate with:

  • mdfactory/build.py — build pipeline (build_mixedbox, build_bilayer, build_lnp) — should be callable from Parsl tasks
  • mdfactory/workflows.pyrun_build_from_file() / run_build_from_dict() dispatch
  • mdfactory/run_schedules/gromacs/ — MDP templates for EM/NVT/NPT/production
  • mdfactory/models/input.pyBuildInput Pydantic model with SHA-1 hash
  • mdfactory/analysis/submit.py — existing submitit pattern (SlurmConfig dataclass, path resolution)
  • mdfactory/cli.py — cyclopts CLI (integration point for workflow commands)
  • workflows/simulate.nf — current Nextflow workflow (reference for what to replace)
  • workflows/simulate.config — SLURM config (maps to SlurmProvider params)

Parsl architecture for this use case:

  • HighThroughputExecutor with SlurmProvider + SrunLauncher for GPU nodes
  • worker_init for environment setup: module load gromacs-gpu, conda/pixi activation, etc.
  • scheduler_options='#SBATCH --gres=gpu:1' for GPU allocation
  • Pilot-job model: few SLURM jobs hosting many tasks (reduces scheduler pressure vs. submitit's 1-job-per-task)
  • MonitoringHub for SQLite-backed status tracking

HPC-aware bash_app patterns to validate:

@bash_app
def grompp(work_dir, mdp, gro, top, tpr_out, stdout=parsl.AUTO_LOGNAME):
    # Real HPC apps need: module loads, cd to workdir, proper env
    return f'''
    module load gromacs-gpu/2024
    cd {work_dir}
    gmx grompp -f {mdp} -c {gro} -p {top} -o {tpr_out} -maxwarn 1
    '''

@bash_app
def mdrun(work_dir, tpr, deffnm, ntomp=8, stdout=parsl.AUTO_LOGNAME):
    return f'''
    module load gromacs-gpu/2024
    cd {work_dir}
    gmx mdrun -s {tpr} -deffnm {deffnm} -ntomp {ntomp} -nb gpu -pme gpu
    '''

@python_app
def build_system(build_input_dict):
    """Run mdfactory build on a GPU node — reuses existing building blocks."""
    from mdfactory.workflows import run_build_from_dict
    return run_build_from_dict(build_input_dict)

# Chain: grompp → mdrun, implicit dependency via futures
tpr = grompp('/scratch/sim001', 'em.mdp', 'system.gro', 'topology.top', 'em.tpr')
em_result = mdrun('/scratch/sim001', tpr, 'min')

# Custom workflow: multiple sims on one node
@bash_app
def multi_sim_node(work_dirs, deffnms, stdout=parsl.AUTO_LOGNAME):
    """Pack multiple short simulations onto a single GPU node."""
    commands = []
    for wd, name in zip(work_dirs, deffnms):
        commands.append(f'cd {wd} && gmx mdrun -deffnm {name} -ntomp 4 &')
    commands.append('wait')
    return '\n'.join(['module load gromacs-gpu/2024'] + commands)

Risks to investigate:

  • Working directory management: Parsl workers may not cd to the expected simulation directory — must be explicit in bash_app commands
  • Environment setup: module load and pixi/conda activation must work inside worker processes
  • GPU allocation: ensuring correct GPU assignment when multiple tasks share a node
  • File staging: whether parsl.File objects are needed or if absolute paths suffice on shared filesystems
  • Custom node packing: whether HighThroughputExecutor supports multiple tasks per node cleanly, or if WorkQueueExecutor / TaskVineExecutor is better
  • Overhead of monitoring for large campaigns (1000+ tasks)
  • Serialization of MDFactory objects (Pydantic models, OpenFF molecules) when using @python_app

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions