Water network controls¶
One of the key features of water network models is the ability to control pipes, pumps, and valves using simple and complex conditions. EPANET uses “controls” and “rules” to define conditions [Ross00]. WNTR replicates EPANET functionality, and includes additional options, as described below.
Controls are defined using an “IF condition; THEN action” format. Controls use a single action (i.e., closing/opening a link or changing the setting) based on a single condition (i.e., time based or tank level based). If a time based or tank level condition is not exactly matched at a simulation timestep, controls make use of partial timesteps to match the condition before the control is deployed. Controls in WNTR emulate EPANET controls.
Rules are more complex; rules are defined using an “IF condition; THEN action1; ELSE action2” format, where the ELSE block is optional. Rules can use multiple conditions and multiple actions in each of the logical blocks. Rules can also be prioritized to set the order of operation. If rules with conflicting actions should occur at the same time, the rule with the highest priority will override all others. Rules operate on a rule timestep specified by the user, which can be different from the simulation timestep. Rules in WNTR emulate EPANET rules.
When generating a water network model from an EPANET INP file, WNTR generates controls and rules based on input from the [CONTROLS] and [RULES] sections. These controls and rules are then used when simulating hydraulics with either the EpanetSimulator or the WNTRSimulator. Controls and rules can also be defined directly in WNTR using the API described below. WNTR includes additional options to define controls and rules that can be used by the WNTRSimulator.
The basic steps to define a control or rule are:
- Define the action(s)
- Define condition(s) (i.e., define what should cause the action to occur)
- Define the control or rule using the action(s) and condition(s)
- Add the control or rule to the water network model
These steps are defined below. Examples use the “Net3.inp” EPANET INP file to generate the water network model object, called wn.
Control and rule actions tell the simulator what to do when a condition becomes “true.”
Actions are created using the
An action is defined by a target link, the attribute to change, and the value to change it to.
The following example creates an action that opens pipe 330:
>>> import wntr.network.controls as controls >>> pipe = wn.get_link('330') >>> act1 = controls.ControlAction(pipe, 'status', 1) >>> print(act1) set Pipe('330').status to Open
Conditions define when an action should occur. The condition classes are listed in Table 6.
||Time-of-day or “clocktime” based condition statement|
||Condition based on time since start of the simulation|
||Compare a network element attribute to a set value|
||Compare attributes of two different objects (e.g., levels from tanks 1 and 2)|
||Combine two WNTR Conditions with an OR|
||Combine two WNTR Conditions with an AND|
All of the above conditions are valid EpanetSimulator conditions except
The EpanetSimulator is also limited to always
repeat conditions that are defined with
not repeat conditions that are defined with in
The WNTRSimulator can handle repeat or not repeat options for both of these conditions.
A control is created in WNTR with the
Control class, which takes an instance
of any of the above conditions, and an action that should occur when the condition is true.
In the following example, a conditional control is defined that opens pipe 330 if the level of tank 1 goes above 46.0248 m. The target is the tank and the attribute is the tanks level. To specify that the condition should be true when the level is greater than the threshold, the operation is set to > and the threshold is set to 46.0248. The action act1 from above is used in the control.
>>> tank = wn.get_node('1') >>> cond1 = controls.ValueCondition(tank, 'level', '>', 46.0248) >>> print(cond1) Tank('1').level > 46.0248 >>> ctrl1 = controls.Control(cond1, act1, name='control1') >>> print(ctrl1) Control control1 := if Tank('1').level > 46.0248 then set Pipe('330').status to Open with priority 3
In the following example, a time-based control is defined that opens Pump 10 at hour 121. A new action is defined that opens the pump.
>>> pump = wn.get_link('10') >>> act2 = controls.ControlAction(pump, 'status', 1) >>> cond2 = controls.SimTimeCondition(wn, '=', '121:00:00') >>> print(cond2) sim_time = 435600 sec >>> ctrl2 = controls.Control(cond2, act2, name='control2') >>> print(ctrl2) Control control2 := if sim_time = 435600 sec then set HeadPump('10').status to Open with priority 3
A rule is created in WNTR with the
Rule class, which takes any of the above conditions,
a list of actions that should occur when the condition is true, and an optional list of actions that should occur
when the condition is false.
Rules also take an optional priority.
If rules with conflicting actions should occur at the same time, the rule with the highest priority will override
all others. The priority argument should be an element of the
ControlPriority enum. The default
priority is medium (3).
The following examples illustrate the creation of a rules, using conditions and actions similar to those defined above.
>>> cond2 = controls.SimTimeCondition(wn, controls.Comparison.ge, '121:00:00') >>> rule1 = controls.Rule(cond1, [act1], name='rule1') >>> print(rule1) Rule rule1 := if Tank('1').level > 46.0248 then set Pipe('330').status to Open with priority 3 >>> rule2 = controls.Rule(cond2, [act2], name='rule2') >>> print(rule2) Rule rule2 := if sim_time >= 435600 sec then set HeadPump('10').status to Open with priority 3
Since rules operate on a different timestep than controls, these rules might behave differently than the controls defined above.
More complex rules can be written using one of the Boolean logic condition classes. The following example creates a new rule that will open pipe 330 if both conditions are true, and otherwise it will open pipe 10.
>>> cond3 = controls.AndCondition(cond1, cond2) >>> print(cond3) ( Tank('1').level > 46.0248 && sim_time >= 435600 sec ) >>> rule3 = controls.Rule(cond3, [act1], [act2], priority=3, name='complex_rule') >>> print(rule3) Rule complex_rule := if ( Tank('1').level > 46.0248 && sim_time >= 435600 sec ) then set Pipe('330').status to Open else set HeadPump('10').status to Open with priority 3
Actions can also be combined, as shown in the following example.
>>> cond4 = controls.OrCondition(cond1, cond2) >>> rule4 = controls.Rule(cond4, [act1, act2]) >>> print(rule4) Rule := if ( Tank('1').level > 46.0248 || sim_time >= 435600 sec ) then set Pipe('330').status to Open and set HeadPump('10').status to Open with priority 3
The flexibility of rules provides an extremely powerful tool for defining complex network operations.
Adding controls/rules to a network¶
Once a control or rule is created, it can be added to the network.
This is accomplished using the
add_control method of the water network model object.
The control or rule should be named so that it can be retrieved and modified if desired.
>>> wn.add_control('NewTimeControl', ctrl2) >>> wn.get_control('NewTimeControl') <Control: 'control2', <SimTimeCondition: model, 'Is', '5-01:00:00', False, 0>, [<ControlAction: 10, status, Open>], , priority=3>