Skip to main content

Crate hydra_common

Crate hydra_common 

Source
Expand description

§Hydra Common — Foundation Contract

Status: v1.3 — ratified 2026-07-28 (v1.1 added opaque per-block options to the production contract, §3.4; v1.2 added the chart fragment item, §3.3; v1.3 added engine availability and import formats, §2.1–2.3). This file is the module documentation of the hydra-common crate and follows the same spec-first workflow as the engine specs: implementation changes flow from changes here, never the reverse.


§1. Purpose and Scope

The common layer is the foundation every engine and every application may depend on. It depends on nothing else in the workspace. It exists so that applications can host any Hydra engine — present or future — through one uniform surface instead of per-engine hardcoded knowledge.

v1 is deliberately slim. It defines exactly two contracts:

  1. Engine identity — what an engine is (§2).
  2. Reportable output — what an engine can contribute to a report (§3).

Explicit non-goals for v1 (ratified 2026-07-28): a shared element schema (node / link / subcatchment / …), a unified unit system, and any cross-engine simulation contract. These are deferred until at least one additional engine implementation exists to exercise them; abstracting them from a single implementation risks baking water-distribution assumptions into the foundation. Nothing in v1 may presuppose the shape those future contracts will take.


§2. Engine Identity

§2.1 Engine descriptor

Every engine publishes one immutable descriptor:

FieldMeaningConstraints
keyStable machine identifierLowercase ASCII, short domain-umbrella abbreviation (wds, uds, och). Never changes once released — it is persisted in project metadata and report templates.
labelHuman-facing product namePractitioner-familiar term (e.g. “Water Distribution”). May be revised between releases.
pillTwo-character badgeUppercase, exactly 2 characters (e.g. “WD”).
accentBrand color for this engine#rrggbb hex string.
summaryOne-sentence description of the engine’s domainPlain text, no markup.
statusWhether this distribution can actually run the engineavailable or planned (§2.3).
importSource-model formats the engine importsOrdered list of import-format descriptors (§2.2); may be empty for an engine with no import path.

The key and the label/pill pair are two deliberately separate naming systems: the key carries the accurate domain umbrella; the label carries the familiar practitioner term. They are allowed to diverge and must not be derived from one another.

§2.2 Import formats

An engine’s models originate in some external tool’s file format. The descriptor names those formats so applications can offer a correctly filtered file picker for any engine without hardcoding per-engine file knowledge:

FieldMeaningConstraints
labelHuman-facing format namePlain text, e.g. “EPANET input file”.
extensionsFilename extensions the format usesOne or more, lowercase ASCII, no leading dot.

This is deliberately the only file knowledge in the foundation layer. It names formats; it says nothing about their contents, and nothing here may be used to decide whether a given file is valid. Validating that a file really is a model of the named format is the owning engine’s job — extensions are a picker filter and a first-pass hint, never a check. Two engines legitimately share the inp extension (EPANET and SWMM both use it) with entirely incompatible contents, so an application that trusted the extension would hand a stormwater model to a water-distribution solver.

§2.3 Availability

A registered engine is either:

  • available — implemented in this distribution and usable;
  • planned — registered so applications can present it (and so its key is reserved), but carrying no implementation.

Planned engines are registered rather than hidden because a user choosing a modelling domain deserves to see what Hydra covers and what is coming, and because the key must be reserved before anything persists it.

Applications must present planned engines as explicitly unavailable and must refuse to create projects, run simulations, or import models for them. Refusing is a hard requirement, not a UI nicety: a persisted project naming a planned engine would be indistinguishable from one whose engine was removed.

Resolving a planned engine’s key is not an error and must not be conflated with the unknown-key case (§2.4) — the descriptor exists and its identity fields are valid; only its implementation is absent.

§2.4 Registry

The registry is the ordered collection of descriptors for every engine compiled into a distribution. It supports:

  • Enumeration in a stable, deliberate order (the order engines are presented to users), available and planned engines alike.
  • Lookup by key, which either yields the descriptor or a typed “unknown engine” error.

Applications must treat an unknown key (e.g. a project created by a newer Hydra carrying an engine this build lacks) as an explicit unsupported state, never as a fallback to a default engine.

v1 ships three registered engines — wds (available), uds (planned), and och (planned) — in that order.


§3. Reportable-Output Contract

The contract by which an engine describes — and produces — the content blocks a report can include. Presentation (layout, styling, output formats, templates) is not part of this contract; it belongs to the report layer, which consumes this contract and knows nothing engine-specific.

§3.1 Concepts

TermMeaning
BlockOne self-contained unit of reportable content an engine can produce (e.g. a pressure summary, a pump energy table).
CatalogThe engine’s complete list of block descriptors. Queryable statically — without any simulation having run.
FragmentThe materialized content of one block for one completed simulation.

§3.2 Block descriptor

FieldMeaningConstraints
idStable block identifierNamespaced by engine key: <engine>.<name> (e.g. wds.pressure-summary). Never changes once released — report templates reference it.
titleDefault human-facing headingPlain text.
summaryWhat this block contains, for the template-builder UIOne or two sentences, plain text.

The descriptor deliberately carries no result-class or prerequisite vocabulary — what a block needs from a simulation is the producing engine’s internal concern, expressed only through the production error contract (§3.4). Encoding result taxonomies (hydraulic vs. quality vs. anything else) here would bake one engine family’s domain into the foundation layer.

Removing a block id, or changing the meaning of an existing id, is a breaking change to every saved template that references it and must be treated with the same gravity as a file-format break.

§3.3 Fragment model

Fragments are neutral data — no colors, fonts, page geometry, or format hints. A fragment is a titled sequence of items; each item is one of:

ItemShapeNotes
Key-value listOrdered pairs of (label, value)For scalar summaries (“Total demand”, “Simulation duration”).
TableColumn descriptors + row-major valuesColumn descriptor: name, optional unit text, value kind.
NotePlain text paragraphFor caveats and methodological remarks (e.g. “Convergence relaxed at 3 timesteps”).
ChartAxis labels/units + chart data (below)Declarative data only — engines describe what is charted, never colors, geometry, or layout.

Chart data is one of:

  • Bar — parallel category labels and values (distributions, rankings). Single-series in this revision.
  • Line — one or more named series of (x, y) points over a continuous x axis (time series).

Every chart must be table-derivable: renderers without graphics support present the chart as a data table derived mechanically from its data (bar → category/value rows; line → x column plus one column per series, absent where a series lacks that x). A chart therefore never gates information behind a graphics-capable format.

Values are typed: number (with optional unit text), integer, boolean, text, timestamp, or absent. Unit strings are display text in v1; a structured unit system in common is an explicit non-goal (§1). Nested sections and images are deferred to a later revision.

§3.4 Production

An engine produces a fragment given:

  • a block id from its catalog,
  • the artifacts of one completed simulation (results and derived analytics — the engine defines internally what it needs), and
  • an optional options value: JSON-shaped structured data whose meaning is defined entirely by the producing engine (thresholds, top-N counts, tolerances). The foundation layer and the report layer treat it as fully opaque — carrying it, never interpreting it. An absent options value means the engine’s documented defaults; a malformed options value fails production with the failed error naming the problem. No option vocabulary may be defined in this layer.

Production is read-only and deterministic: the same simulation artifacts, block id, and options always yield the same fragment. Production fails with one of three neutral, typed errors:

  • unknown block — the id is not in this engine’s catalog;
  • unavailable — the block does not apply to this run, with a human-readable reason supplied by the engine (e.g. “the run has no water-quality results”); an expected condition, not a fault;
  • failed — reading or deriving from the simulation artifacts failed.

The report layer decides how an unavailable or failed block renders (placeholder, omission) — the engine never does, and the contract carries no engine vocabulary for why beyond the engine-authored reason text.

Block options arrived in v1.1 as production inputs only — the descriptor still carries no options schema. How an engine’s options are discovered and edited by a template-builder UI is deferred; until then, options are authored knowingly (documentation, or app-side knowledge of specific ids).

§3.5 Consumers and dependency rules

LayerMay depend onMust not depend on
commonnothing in the workspace
engine-*commonthe report layer, applications
report layercommonany engine-*, applications
applications (CLI, GUI)everything above, via the umbrella

Applications are the composition root: they obtain catalogs and fragments from engines and hand fragments to the report layer for rendering. The report layer never invokes an engine; engines never render.


§4. Evolution

  • All v1 contracts evolve additively; fields are added, never repurposed.
  • The deferred contracts (element schema, units, simulation surface) will arrive as new modules of this layer with their own spec sections, gated on a second engine implementation existing to validate them. Their arrival must not require changes to the v1 identity or report contracts.
  • If a future revision must break a v1 contract, the break follows the library release track’s semver discipline.

Structs§

BlockDescriptor
Descriptor of one block in an engine’s catalog (spec §3.2).
Chart
A declarative chart (spec §3.3): data plus axis labels only — engines describe what is charted, never colors, geometry, or layout. Every chart is table-derivable so it never gates information behind a graphics-capable format.
Column
Column descriptor of a table (spec §3.3).
EngineDescriptor
Immutable identity of one Hydra engine (spec §2.1).
Fragment
The materialized content of one block for one completed simulation (spec §3.1): a titled sequence of items.
ImportFormat
One source-model file format an engine imports (spec §2.2).
KeyValue
One (label, value) pair in a key-value list (spec §3.3).
LineSeries
One named series of (x, y) points in x order (spec §3.3).
Table
Column descriptors plus row-major values (spec §3.3).
UnknownEngineError
Lookup failure for engine_by_key.

Enums§

BlockError
Failure producing a block (spec §3.4). The report layer decides how an unavailable or failed block renders (placeholder, omission) — the engine never does, and the contract carries no engine vocabulary for why beyond the engine-authored reason text.
ChartData
Chart data (spec §3.3).
EngineStatus
Whether a registered engine is implemented in this distribution (spec §2.3).
FragmentItem
One item of a fragment (spec §3.3).
Value
One typed value inside a fragment (spec §3.3). Unit strings are display text — a structured unit system in this layer is an explicit non-goal (spec §1).
ValueKind
Kind of a Value, used in column descriptors (spec §3.3).

Constants§

ENGINES
Every engine compiled into this distribution, in presentation order (spec §2.4) — planned engines included, so applications can present the full modelling scope rather than only what ships today.
HYDRA_COMMON_VERSION
The crate version, taken from Cargo.toml at compile time.

Functions§

engine_by_key
Resolve an engine key to its descriptor (spec §2.2).