Architecture
This page introduces HydroPilot's current architecture from the implementation point of view.
Two major chains
HydroPilot has two connected but distinct chains:
- Config chain: YAML config -> validation and template expansion ->
RunConfig - Runtime chain:
SimModel->Session->Workspace->Executor-> execution services -> reporting
The config chain turns user input into a validated runtime configuration. The runtime chain uses that configuration to execute model runs, extract outputs, evaluate metrics, and persist results.
Config chain
At a high level, configuration preparation follows this path:
YAML
-> prepare_config()
-> optional template expansion
-> general validation
-> RunConfig.from_raw()
-> load_config()Key behavior:
version: generalmeans the YAML already describes the runtime config directly.- Template modes such as
version: swat,version: swatplus, orversion: xajare expanded into the general runtime form before execution. - Validation happens on the expanded config, so model-template issues surface early.
- When runtime loading succeeds, HydroPilot can emit a resolved
_general.yamlbeside the source config for inspection.
This chain is shared across CLI validation and runtime loading. The difference is that CLI commands convert failures into user-facing diagnostics, while the Python API raises exceptions.
Main modules
The runtime-facing modules are organized around:
apifor public entry points such asSimModelclifor command-line interfacesconfigfor config loading, path resolution, and schema conversionevaluationfor objectives, constraints, diagnostics, and derived valuesiofor runners, readers, and writersmodelsfor template-specific model logicparamsfor parameter space and parameter applicationreportingfor archived outputs and run artifactsruntimefor session and workspace lifecycleseriesfor simulation and observation extraction planningvalidationfor user-facing config diagnostics
Runtime chain
The runtime path starts from SimModel and then delegates to a set of orchestration components:
SimModel
-> Session
-> Workspace
-> Executor
-> ExecutionServices
-> ParamSpace
-> ParamWritePlan
-> ParamApplier
-> SeriesPlan
-> ObsStore
-> SubprocessRunner
-> SeriesExtractor
-> Evaluator
-> RunReporterEach layer has a clear role:
SimModelis the public facade for Python users.Sessionowns lifecycle and cleanup for one loaded runtime.Workspacecreates isolated run directories and manages reusable model instances.Executorruns one or more evaluations sequentially or in parallel.ExecutionServicesassembles the concrete runtime dependencies fromRunConfig.RunReporterpersists artifacts, summaries, and archived outputs.
Workspace and execution
HydroPilot executes model runs inside isolated working directories under basic.workPath. For each evaluation path it can:
- create or acquire a prepared project copy
- transform design values into physical parameters
- write parameters into model input files
- run the configured model command
- extract simulation outputs and observation slices
- evaluate objectives, constraints, and diagnostics
- archive the results and release the instance
By default, temporary instance_* directories are cleaned after the session closes. If basic.keepInstances: true is enabled, those directories are preserved for debugging.
Why templates matter
Templates are one of HydroPilot's main architectural advantages. They let users work with model-facing concepts such as SWAT parameters or XAJ CSV mappings without writing every low-level reader and writer manually.
In practice, this means:
- model-specific knowledge stays close to the template layer
- the runtime still executes a single general-form config
- CLI, Python API, and UQPyL integration all share the same execution foundation
Why this matters
This split is what makes HydroPilot useful as an engineering layer:
- configuration stays declarative
- model-specific details stay close to templates and I/O handlers
- runtime orchestration stays reusable for CLI, Python API, and UQPyL integration
