Water age ([WATER_AGE_SOURCES])#

Note

Engine: OpenSWMM 6 — refactored.

solver.water_age is the editable view over the [WATER_AGE_SOURCES] table of the model.age component: a global age per source pathway, plus per-node overrides for the two pathways that take node scope. All values are hours, the config file’s own unit.

Reference: openswmm_water_age.h.


Quickstart#

from openswmm.engine import Solver, WaterAgeSource

with Solver("model.inp") as s:
    print(s.water_age.enabled)              # [OPTIONS] WATER_AGE

    s.water_age.globals[WaterAgeSource.RAINFALL] = 0.0
    s.water_age.globals[WaterAgeSource.GW] = 720.0        # hours
    s.water_age.globals[WaterAgeSource.EXTERNAL_INFLOW] = -6.0

    s.water_age.node_overrides.set(WaterAgeSource.DWF, "J1", 12.0)
    for row in s.water_age.node_overrides:
        print(row.source, row.node_index, row.hours)

    s.water_age.save("model.age")

Note

Edits are live: the loaders re-read the table every step, so a mid-simulation write takes effect on the next routing step.


Global source ages#

WaterAge.globals maps WaterAgeSource to float hours and supports len(), iteration, in, and int | str keys.

Source

Pathway

RAINFALL

Rainfall-derived water.

DWF

Dry-weather flow. Takes node scope.

GW

Groundwater.

RDII

Rainfall-derived infiltration and inflow.

EXTERNAL_INFLOW

[INFLOWS]. Takes node scope.

IFACE

Interface file.

INITIAL_STATE

Water present at t = 0.

Important

Negative values are legal. A negative source age extracts age-volume rather than adding it, and the result is clamped so the computed age never goes below zero. Do not add a client-side non-negative guard — it would reject a modelling construct the engine supports deliberately. (Contrast Initial quality ([INITIAL_QUALITY]), where pollutant values must be non-negative.)

The C enum’s trailing COUNT = 7 sentinel is deliberately not mirrored in Python: len(WaterAgeSource) is the count.


Node overrides#

WaterAge.node_overrides is a row-indexed sequence of WaterAgeOverride named tuples (source, node_index, hours). Row order is stable across edits within a session.

Operation

Behaviour

len(s.water_age.node_overrides)

Number of override rows.

s.water_age.node_overrides[row]

One WaterAgeOverride by row index.

.set(source, node, hours)

Add or update the row for (source, node).

.remove(source, node)

Delete by key, not by row index.

Warning

Only WaterAgeSource.DWF and WaterAgeSource.EXTERNAL_INFLOW accept node scope — the parser’s own rule. Any other source is refused, not silently folded into the global value.

Setting the same (source, node) pair twice is an update, so an editor can change a value it just wrote. Negative hours are legal here too.


Saving#

WaterAge.save() writes the current table as a [WATER_AGE_SOURCES] component file — the model.age format the water-age component parses on the next open. It accepts str or any os.PathLike.

from pathlib import Path

s.water_age.save(Path("scenarios") / "baseline.age")

See also#