WNTR Drinking Water Model¶
The WNTR drinking water model ("drinking_water") wraps the
WNTR hydraulic simulator to provide an
idiomatic Movici interface for running drinking water network simulations.
The model processes networks containing Pipes, Junctions, Tanks, Reservoirs,
Valves and Pumps, with all data described in terms of Movici entities and
attributes.
Use cases include:
Simulating hydraulic behavior of drinking water distribution networks
Analyzing pressure and flow under varying demand scenarios
Modeling transient tank filling/emptying dynamics
Evaluating the impact of pump or valve failures on network performance
Goals¶
The data model is as much as possible described in terms of entities and attributes
Data-related options (headloss formula, viscosity, etc.) are stored in the dataset’s
"general"sectionSolver-related options (trials, accuracy, etc.) are stored in the model config under
"options"It is a nice-to-have if the data model is compatible with other movici network tooling, such as the
shortest_pathmodel
Limitations¶
The initial implementation does not have support for chemical reactions
EPANET
.inpfiles are not supported directly. External tooling can convert.inpfiles to Movici datasetsPatterns are not included in the model. Values that change over time in a predictable manner (e.g. a pump rate goes to 70% at t=x) should be produced by one or more different models, such as a tape player
Model Characteristics¶
The WNTR model is a transient state dependent model. Its output depends both on an input state that may change over time and it has an inherent transient behaviour. Even if its input state doesn’t change, it may still change its output state. For WNTR this behaviour may stem from tanks that may fill or empty over time. Compare this to steady state models which only change their output state based on changes in their input state: if there are no input state changes, the output state also doesn’t change.
It also means that the output state of the model is dependent on some kind of initial state. In case of WNTR, the tank level at \(t_{1}\) requires a tank level at \(t_{0}\). In general it can be stated that the output \(Y\) of the model can be described as
with:
Warning
This results in a possible circular dependency where the model both subscribes and publishes to an attribute. While the orchestrator prevents a self-dependency, ie it will not call a model on its own update, even when it technically subscribes to its own data, the user must still be careful. When configuring a scenario containing this model, the initial state should be provided in the dataset and not a result of another model’s calculation. At all times there must only be one publisher of a certain attribute in an entity group in a dataset.
Since the model can act both on internal changes and external changes, it cannot do a full run at once. It must be able to react to changes from the outside. When it receives an outside change, it must incorporate those changes into its internal state and then calculate from there on.
Note
Pause and Restart
The WNTR simulator instance is kept alive across timesteps. Each call to run_sim() advances
the simulation time cumulatively, preserving internal state such as tank levels. Between calls,
the model can modify the WNTR network (e.g. changing demands or closing valves) and the next
run_sim() picks up from where it left off. This allows external changes to be integrated
between WNTR simulation steps.
How It Works¶
At initialization, the model builds a WNTR network from the Movici entity groups (junctions, tanks, reservoirs, pipes, pumps, valves)
Data options from the dataset’s
"general"section and solver options from the model config are merged and applied to the WNTR networkAt each simulation step, WNTR runs a hydraulic simulation with the current state
Any external state changes received at this timestep (e.g. demand updates, valve closures) are applied to the WNTR network for the next step
Results (pressures, heads, flows, velocities, tank levels) are written back to the corresponding Movici entity attributes
Data Model¶
The drinking water network data model can be described as follows.
Junctions¶
water_junction_entities
Junctions are nodes in the drinking water network. They connect pipes and can be used as demand
nodes. Junctions derive from PointEntity.
Attribute |
Flags |
Description |
|---|---|---|
|
INIT |
Location x coordinate (from |
|
INIT |
Location y coordinate (from |
|
INIT |
Elevation (from |
|
INIT |
Base demand on this node, multiplied by the demand factor to obtain the actual demand |
|
OPT |
Scaling factor for the demand |
|
OPT |
Per-junction minimum pressure for PDD analysis.
Overrides the global |
|
OPT |
Per-junction required (nominal) pressure for PDD
analysis. Overrides the global
|
|
OPT |
Per-junction pressure exponent for PDD analysis.
Overrides the global |
|
PUB |
Effective demand (base_demand * demand_factor) |
|
PUB |
Dynamic pressure at the node |
|
PUB |
Total head at the node (elevation + pressure) |
Tip
Calculating Demand Deficit
In Pressure-Dependent Demand ("PDA") simulations, junctions may receive less water than
requested when pressure is insufficient. The model publishes the actual delivered
drinking_water.demand but does not publish a demand deficit. To obtain the shortfall,
compare the effective demand (base_demand * demand_factor) with the published
drinking_water.demand for each junction.
Tanks¶
water_tank_entities
Tanks are buffers for drinking water. They are one of the transient elements of the drinking water
model — as a simulation progresses, tanks may fill up or empty over time. Tanks derive from
PointEntity.
Attribute |
Flags |
Description |
|---|---|---|
|
INIT |
Location x coordinate (from |
|
INIT |
Location y coordinate (from |
|
INIT |
Elevation at tank bottom (from |
|
OPT |
Whether a tank can overflow when full
(max_level reached). Default: |
|
OPT |
Tank diameter for a cylindrical tank |
|
OPT |
Minimum level to be able to drain (Default: 0) |
|
INIT |
Maximum level. If a tank is overflowable, it will overflow beyond this level |
|
OPT |
Curve of (x,y) pairs defining the tank volume over depth. Data type shape: (2,)-csr. Used for non-cylindrical tanks |
|
INIT, PUB |
Water level (m) in the tank. Required as an initial value |
|
PUB |
Dynamic pressure in the tank |
|
PUB |
Total head in the tank (elevation + pressure) |
The shape and volume of the tank can either be of constant diameter for cylindrical tanks, or the
volume can be defined by a volume curve. These are mutually exclusive — when a volume curve is
defined, the diameter attribute is ignored. Either is valid, so they must be OPT attributes.
If neither a diameter nor a volume curve is provided, WNTR will use default values (diameter=0).
Warning
Tank Overflow Not Supported
The drinking_water.overflow attribute exists but has no effect — WNTR does not support
tank overflow. Regardless of the overflow setting, when a tank reaches max_level all inflow
links are closed. The network will find a new equilibrium where inflow matches outflow,
potentially causing backpressure or pump shutoffs.
Note
Tank Level Boundaries
WNTR enforces tank level boundaries in both directions: when a tank reaches max_level,
inflow is blocked; when it reaches min_level, outflow is blocked. In both cases, the tank
acts as a closed boundary for the restricted direction while flow in the other direction
remains permitted.
Note
Tank Level vs Volume Attributes
WNTR uses min_level and max_level for all tank types. For volume curve tanks,
min_vol can also be specified and WNTR will derive the corresponding min_level from
the curve. The max_level is always used as the upper bound. If both min_vol and
min_level are specified, the higher effective level takes precedence.
Reservoirs¶
water_reservoir_entities
A reservoir is a tank that never empties. It has a fixed head (which can change by varying the
head factor). Reservoirs derive from PointEntity.
Attribute |
Flags |
Description |
|---|---|---|
|
INIT |
Location x coordinate (from |
|
INIT |
Location y coordinate (from |
|
INIT |
Base head of the reservoir |
|
OPT |
Head multiplier (Default: 1) |
|
PUB |
Calculated as base_head * head_factor |
|
PUB |
Dynamic pressure at the reservoir |
|
PUB |
Demand at the reservoir |
|
PUB |
Total flow rate out of the reservoir |
|
PUB |
Absolute flow rate |
Note
Reservoirs are not calculated using an elevation, so geometry.z is not used.
Note
Reservoirs as Sources and Drains
Reservoirs can act as both sources (water flows out) and drains (water flows in), depending on the hydraulic conditions. A reservoir with lower head than connected nodes will act as a drain. Reservoirs can connect directly to pipes — no pump is required. Flow direction is determined by head differences.
Pipes¶
water_pipe_entities
Pipes are links that transport water from one node (Junction, Tank, Reservoir) at a high head to
another node at a lower head and experience a pressure drop (head loss) while doing so. Pipes
derive from LinkEntity.
Attribute |
Flags |
Description |
|---|---|---|
|
INIT |
Node id on the from side (from |
|
INIT |
Node id on the to side (from |
|
INIT |
Pipe diameter |
|
INIT |
Pipe roughness coefficient (Hazen-Williams C-factor). Typical range 100-150. Higher values indicate smoother pipes |
|
OPT |
Pipe length (Default: 100 m) |
|
OPT |
Minor loss coefficient (Default: 0). Additional head loss from curves and bends, proportional to flow velocity squared |
|
OPT |
Whether the pipe is open ( |
|
OPT |
Restricts flow to from_node → to_node direction
only. Default: |
|
PUB |
Water flow rate through the pipe |
|
PUB |
Absolute flow rate |
|
PUB |
Water velocity (always positive, use flow sign for direction) |
|
PUB |
WNTR link status (0=Closed, 1=Open, 2=Active, 3=CV) |
Note
The dataset’s "general" section stores data-related WNTR hydraulic options
(headloss formula, viscosity, specific gravity, demand model, etc.). These are read from
TrackedState during initialization and applied to the WNTR network via
NetworkWrapper.configure_options(). See the Configuration Options section below for the
full split between dataset general and model config.
Note
Check Valve Direction
Flow direction is indicated by the sign of flow (positive = from_node to to_node, negative =
to_node to from_node). Velocity is always positive (absolute value of flow divided by pipe area).
Check valves (drinking_water.check_valve = True) restrict flow to the from_node → to_node
direction only. Reverse flow (from to_node back to from_node) is prevented.
Pumps¶
water_pump_entities
Pumps are links in the network. They have a direction and pump water (increase the head) from one
node (reservoir, tank, junction) to another. Pumps derive from LinkEntity.
Attribute |
Flags |
Description |
|---|---|---|
|
INIT |
Node id on the from side (from |
|
INIT |
Node id on the to side (from |
|
INIT |
Pump type: |
|
OPT |
Fixed power for a |
|
OPT |
Head/flow curve for a |
|
OPT |
Whether the pump is open ( |
|
PUB |
Pump flow rate |
|
PUB |
Absolute flow rate |
|
PUB |
WNTR link status (0=Closed, 1=Open, 2=Active, 3=CV) |
Note
Pump Status
We model pump status as a boolean (operational.status): True = open/active, False = closed.
The WNTR “active” status (open with specific speed) is handled by combining operational.status = True
with a drinking_water.speed value. This simplifies the interface while maintaining full functionality.
Note
Pump power / speed
power pumps can operate at a fixed power, although this power can be updated during a simulation.
Speed is ignored for power pumps — the WNTRSimulator always reports speed=1.
head pumps can only be turned on or off. When on, they operate according to their head curve.
Variable speed (speed != 1.0) is not supported by the WNTRSimulator and will raise a
NotImplementedError. Note that the WNTR Pump class does expose base_speed and
speed_timeseries attributes, but these cannot be used with the WNTRSimulator.
Note
Head pump curve fitting
WNTR fits head pump curves to the equation H = A - B * Q^C. The coefficients depend on
the number of points in the curve:
1-point curve:
C = 2, givingH = A - B * Q^2(parabolic)2-point curve:
C = 1, givingH = A - B * Q(linear in flow)3+ point curve: all three coefficients are fitted using least-squares optimization
The equation naturally extends beyond the defined curve domain — there is no clamping. If flow exceeds the maximum defined flow, WNTR extrapolates using the same equation, which can produce negative head values. A warning is issued after the fact but the simulation is not stopped.
Valves¶
water_valve_entities
Valves are links that reduce flow in a controlled manner. There are many types of valves that
each operate in their own way. Valves derive from LinkEntity.
Attribute |
Flags |
Description |
|---|---|---|
|
INIT |
Node id on the from side (from |
|
INIT |
Node id on the to side (from |
|
INIT |
Valve type: |
|
INIT |
Valve diameter |
|
OPT |
Pressure setting for |
|
OPT |
Flow setting for |
|
OPT |
Loss coefficient for |
|
OPT |
Minor loss coefficient (Default: 0). Head loss when the valve is fully open, proportional to flow velocity squared |
|
OPT |
Active ( |
|
PUB |
Valve flow rate |
|
PUB |
Absolute flow rate |
|
PUB |
WNTR link status (0=Closed, 1=Open, 2=Active, 3=CV) |
Note
GPV (General Purpose Valve) and PBV (Pressure Breaker Valve) are not supported by the WNTRSimulator.
Note
Valve Types Explained
PRV (Pressure Reducing): Limits downstream pressure to the set value
PSV (Pressure Sustaining): Maintains upstream pressure at the set value
FCV (Flow Control): Limits flow to the set value
TCV (Throttle Control): Simulates partially closed valve via loss coefficient. When the valve is Active, the
valve_loss_coefficientis used as the head loss coefficient. When the valve is Open (fully open),minor_lossis used instead. These are independent — WNTR does not enforce that the loss coefficient must be larger than the minor loss
Note
Valve Status
Valves support operational.status to set the initial state: True = Active (valve
regulates normally using its setting), False = Closed (no flow). The WNTR “Active”
status means the valve actively enforces its setting (pressure, flow, or coefficient).
Setting operational.status to True restores normal regulation; it does not set
the valve to “Open” (which in WNTR terminology means the valve acts as a fully open pipe,
ignoring its setting).
Warning
Valve Placement Rules
WNTR enforces that PRV, PSV, and FCV valves cannot connect directly to a reservoir or tank — an intermediate pipe must be placed between them. This is validated at network construction and raises an error if violated.
Additionally, the EPANET specification defines these topology guidelines (not enforced by WNTR, but violating them may produce unexpected simulation results):
PRVs should not share the same downstream node or be daisy-chained
PSVs should not share the same upstream node or be connected in series
A PSV should not connect to the downstream node of a PRV
Controls¶
Controls are not handled by the drinking water model directly, but are instead handed over to
the Rules Model. EPANET .inp files contain [CONTROLS] and [RULES] sections
that define time-based or conditional operations (e.g. closing a pump when a tank level exceeds
a threshold). In Movici, these are expressed as rules in the Rules Model.
For example, an EPANET control that closes a pump when a tank level reaches 23 m translates to:
{
"from_reference": "some tank",
"if": "drinking_water.level >= 23",
"to_reference": "some pump",
"output": "operational.status",
"value": false,
"else_value": true
}
See the Rules Model documentation for the full condition syntax, including support for
<simtime>, <clocktime>, boolean operators, and attribute comparisons.
Configuration Options¶
WNTR options are split between two sources. Both are merged at initialization and applied to the
WNTR network via NetworkWrapper.configure_options(). They contribute disjoint keys to the same
WNTR options structure.
Data Options¶
Stored in the dataset’s "general" section. These describe physical properties of the water
network:
Option |
Type |
Description |
|---|---|---|
|
string |
Headloss formula: |
|
number |
Kinematic viscosity. Default: 1.0 |
|
number |
Specific gravity of the fluid. Default: 1.0 |
|
string |
Demand model: |
|
number |
Global demand multiplier. Default: 1.0 |
|
number |
Global minimum pressure for PDD analysis |
|
number |
Global required (nominal) pressure for PDD analysis |
|
number |
Global pressure exponent for PDD analysis |
Solver Options¶
Stored in the model config under the "options" key. These control the WNTR solver behavior:
Option |
Type |
Description |
|---|---|---|
|
integer |
Maximum number of solver trials. Default: 200 |
|
number |
Convergence accuracy. Default: 0.001 |
|
number |
Maximum head error for convergence |
|
number |
Maximum flow change for convergence |
|
number |
Accuracy limit for damping |
|
integer |
Frequency of status checks |
|
integer |
Maximum number of status checks |
|
string |
Action if simulation is unbalanced |
|
number |
Value for unbalanced option |
Note
The WNTRSimulator only supports the Hazen-Williams ("H-W") headloss formula.
Example Configuration¶
Dataset general section with data options:
{
"general": {
"hydraulic": {
"headloss": "H-W",
"viscosity": 1.0,
"specific_gravity": 1.0,
"demand_model": "PDA",
"minimum_pressure": 0.0,
"required_pressure": 20.0,
"pressure_exponent": 0.5
}
}
}
Model config with solver options:
{
"name": "water_simulation",
"type": "drinking_water",
"dataset": "water_network",
"options": {
"hydraulic_timestep": 3600,
"hydraulic": {
"trials": 200,
"accuracy": 0.001
}
}
}
Other Considerations¶
Names, IDs and References¶
WNTR internally works with names for objects. For consistency, entity IDs (cast to strings) are
used as WNTR object names. It may be tempting to use the reference field instead of the ID,
but there is no guarantee that every object will have a reference. Mixing reference and
id (e.g. falling back to the id when an entity does not have a reference) creates a
risk of name collisions.
Units¶
Units must be in SI or SI-derived. Suggested values:
length: m
pressure (head): m
diameter: m
See WNTR Units.
When converting data from EPANET sources, units must be converted to SI properly.
Notes¶
Headloss is not available as a published attribute. The WNTRSimulator has removed headloss from its results output for performance reasons.
Controls (rule-based or conditional operations) are delegated to the Rules Model and not handled within this model.
The headloss formula is stored in the dataset
"general"section under"hydraulic"→"headloss". Only"H-W"(Hazen-Williams) is supported.
Config Schema Reference¶
DrinkingWaterConfig¶
type: objectproperties:dataset:stringName of the drinking water network dataset (required)options: DrinkingWaterOptions Solver and timing options for the WNTR simulator
DrinkingWaterOptions¶
type: objectproperties:hydraulic_timestep:integerHydraulic simulation timestep in seconds (default: 3600)report_timestep:integerReport timestep in seconds (default: same as hydraulic_timestep)hydraulic: DrinkingWaterHydraulicOptions Hydraulic solver settings
DrinkingWaterHydraulicOptions¶
type: objectproperties:trials:integerMaximum number of solver trials (default: 200)accuracy:numberConvergence accuracy (default: 0.001)headerror:numberMaximum head error for convergenceflowchange:numberMaximum flow change for convergencedamplimit:numberAccuracy limit for dampingcheckfreq:integerFrequency of status checksmaxcheck:integerMaximum number of status checksunbalanced:stringAction if simulation is unbalancedunbalanced_value:numberValue for unbalanced option