Simulation Orchestration Class Diagram#

Scope#

This diagram documents the static orchestration layer that connects the public Project facade to simulation planning and solver execution.

It focuses on:

  • Project as the top-level orchestrator that the CLI (hmp run) and Python users instantiate,

  • SimulationPlanner and the immutable SimulationPlan / ProcessRun objects it builds,

  • SimulationRunner as the runtime dispatcher,

  • the project-owned runtime state exchanged between orchestration phases,

  • process runtimes (Flow, Transport) and the solver adapters they feed.

Code map#

  • hydromodpy/project/facade.py: the public Project facade. Holds the mutable runtime state and wires the pipeline phases.

  • hydromodpy/cli/commands/run.py and hydromodpy/cli/workflows.py: the hmp run <toml> entry point. Loads the TOML, builds a HydroModPyConfig, and delegates to Project.

  • hydromodpy/simulation/planning/planner.py and plan.py: planner boundary and immutable planning result.

  • hydromodpy/simulation/execution/runner.py: runtime dispatcher over ProcessRun entries.

  • hydromodpy/physics/ and hydromodpy/solver/: downstream runtime objects and concrete backends.

Diagram source#

@startuml
title Simulation Orchestration Class Diagram

package "hydromodpy" {
  class Project {
    +cfg: HydroModPyConfig
    +run()
    +prepare(**overrides) : sim_id
    +execute(sim_id) : runtime
    +ingest(sim_id)
    +render(sim_id)
    +build_geographic()
    +load_data()
    +build_mesh()
  }
}

package "hydromodpy.simulation" {
  class SimulationPlanner {
    +build(config)
  }

  class SimulationPlan {
    +name: str
    +description: str
    +runs: tuple[ProcessRun, ...]
  }

  class ProcessRun {
    +id: str
    +process_id: str
    +process_type: str
    +solver: str
    +depends_on: tuple[str, ...]
  }

  class SimulationRunner {
    +execute(plan, runtime_state)
  }
}

package "hydromodpy.physics" {
  class Flow
  class Transport
}

package "hydromodpy.solver" {
  interface "Flow Solver Adapter" as FlowSolver
  class ModflowNwt
  class Modflow6
  class Boussinesq

  interface "Transport Solver Adapter" as TransportSolver
  class Modpath
  class Mt3dms
  class Modflow6Transport
}

Project --> SimulationPlanner : builds
Project --> SimulationRunner : delegates execution

SimulationPlanner ..> SimulationPlan : returns
SimulationPlanner ..> ProcessRun : produces

SimulationPlan *-- "0..*" ProcessRun : runs

Project --> SimulationPlan : stores active plan
Project --> ProcessRun : indexes runs by id
Project --> Flow : prepares runtime process
Project --> Transport : prepares runtime process

SimulationRunner ..> SimulationPlan : consumes ordered runs
SimulationRunner ..> ProcessRun : dispatches
SimulationRunner ..> Flow : uses runtime process
SimulationRunner ..> Transport : uses runtime process
SimulationRunner ..> FlowSolver : instantiates from run.solver
SimulationRunner ..> TransportSolver : instantiates from run.solver

FlowSolver <|.. ModflowNwt
FlowSolver <|.. Modflow6
FlowSolver <|.. Boussinesq

TransportSolver <|.. Modpath
TransportSolver <|.. Mt3dms
TransportSolver <|.. Modflow6Transport
@enduml

Notes#

  • SimulationPlan is intentionally immutable. The planner produces it once, then the runner consumes it without rewriting scheduling decisions.

  • The mutable execution state stays owned by Project, which gradually accumulates runtime objects and produced models across phases (setup_workspace, build_geographic, load_data, build_mesh, prepare, execute, ingest, render).

  • SimulationRunner does not decide the schedule. It only dispatches the already-resolved ProcessRun entries to the right solver adapter.

  • Solver adapters are selected from ProcessRun.solver, which keeps solver choice explicit and traceable in the plan itself.