Water network model#
The water network model includes
junctions, tanks, reservoirs, pipes, pumps, valves,
patterns,
curves,
controls,
sources,
simulation options,
and node coordinates.
Water network models can be built from scratch or built directly from an EPANET INP file.
Sections of the EPANET INP file that are not compatible with WNTR are described in Limitations.
For more information on the water network model, see
WaterNetworkModel
in the API documentation.
Build a model from an INP file#
A water network model can be created directly from EPANET INP files using EPANET 2.00.12 or 2.2.0 format. The following example builds a water network model.
>>> import wntr
>>> wn = wntr.network.WaterNetworkModel('networks/Net3.inp')
Note
Unless otherwise noted, examples in the WNTR documentation use Net3.inp to build the
water network model, named wn
.
Add elements#
The water network model contains methods to add junctions, tanks, reservoirs, pipes, pumps, valves, patterns, curves, sources, and controls. When an element is added to the model, it is added to the model’s registry. Within the registry, junctions, tanks, and reservoirs share a namespace (e.g., those elements cannot share names) and pipes, pumps, and valves share a namespace.
For each method that adds an element to the model, there is a related object. For example, the
add_junction
method adds a
Junction
object to the model.
Generally, the object is not added to the model directly.
The example below adds a junction and pipe to a water network model.
>>> wn.add_junction('new_junction', base_demand=10, demand_pattern='1', elevation=10,
... coordinates=(6, 25))
>>> wn.add_pipe('new_pipe', start_node_name='new_junction', end_node_name='101',
... length=10, diameter=0.5, roughness=100, minor_loss=0)
Remove elements#
The water network model registry tracks when elements are used by other elements in the model. An element can only be removed if all elements that rely on it are removed or modified. For example, if a valve is used in a control, the valve cannot be removed until the control is removed or modified. Similarly, a node cannot be removed until the pipes connected to that node are removed. The following example removes a link and node from the model. If the element being removed is used by another element, an error message is printed to the screen and the element is not removed.
>>> wn.remove_link('new_pipe')
>>> wn.remove_node('new_junction')
Modify options#
Water network model options are divided into the following categories: time, hydraulics, quality, solver, results, graphics, and energy. The following example returns model options, which all have default values, and then modifies the simulation duration.
>>> wn.options
Time options:
duration : 604800
hydraulic_timestep : 900
quality_timestep : 900
rule_timestep : 360.0
pattern_timestep : 3600
...
>>> wn.options.time.duration = 10*3600
Modify element attributes#
To modify element attributes, the element object is first obtained using the
get_node
or
get_link
methods.
The following example changes junction elevation, pipe diameter, and size for a constant diameter tank.
>>> junction = wn.get_node('121')
>>> junction.elevation = 5
>>> pipe = wn.get_link('122')
>>> pipe.diameter = pipe.diameter*0.5
>>> tank = wn.get_node('1')
>>> tank.diameter = tank.diameter*1.1
The following shows how to add an additional demand to the junction 121.
>>> print(junction.demand_timeseries_list)
<Demands: [<TimeSeries: base_value=0.002626444876132, pattern_name='1', category='None'>]>
>>> junction.add_demand(base=1.0, pattern_name='1')
>>> print(junction.demand_timeseries_list)
<Demands: [<TimeSeries: base_value=0.002626444876132, pattern_name='1', category='None'>, <TimeSeries: base_value=1.0, pattern_name='1', category='None'>]>
To remove the demand, use the Python del
as with an array element.
>>> del junction.demand_timeseries_list[1]
>>> print(junction.demand_timeseries_list)
<Demands: [<TimeSeries: base_value=0.002626444876132, pattern_name='1', category='None'>]>
Modify time series#
Several network attributes are stored as a time series, including junction demand, reservoir head, and pump speed. A time series contains a base value, a pattern, and a category. Time series are added to the water network model when the junction, reservoir, or pump is added. Since junctions can have multiple demands, junction demands are stored as a list of time series. The following examples modify time series.
Change reservoir supply:
>>> reservoir = wn.get_node('River')
>>> reservoir.head_timeseries.base_value = reservoir.head_timeseries.base_value*0.9
Change junction demand base value:
>>> junction = wn.get_node('121')
>>> junction.demand_timeseries_list[0].base_value = 0.005
Add a new demand time series to the junction:
>>> pat = wn.get_pattern('3')
>>> junction.demand_timeseries_list.append((0.001, pat))
Add custom element attributes#
New attributes can be added to model elements simply by defining a new attribute name and value. These attributes can be used in custom analysis and graphics. While this is similar to using external datasets directly, there can be benefits to adding custom attributes to model objects.
The following example uses a dataset that defines pipe material as polyvinyl chloride (PVC), cast iron, steel, or high-density polyethylene (HDPE). The dataset is indexed by pipe name. The first 10 lines of the dataset are shown below.
>>> print(material.head(10))
20 Steel
40 Cast iron
50 HDPE
60 PVC
101 Cast iron
103 PVC
105 Steel
107 Steel
109 Cast iron
111 Steel
dtype: object
The data can be used to create a custom material attribute for each pipe object.
>>> for name, pipe in wn.pipes():
... pipe.material = material[name]
The custom attribute can be used in analysis in several ways. For example, the following example closes the pipe if pipe material is ‘Steel.’
>>> for name, pipe in wn.pipes():
... if pipe.material == 'Steel':
... pipe.initial_status = 'Closed'
A complete list of custom attributes can also be obtained using a query, as shown below.
>>> material = wn.query_link_attribute('material')
Iterate over elements#
Iterators are available for junctions, tanks, reservoirs, pipes, pumps, and valves. Each iterator returns the element’s name and the element’s object. The following example iterates over all pipes to modify pipe diameter.
>>> for pipe_name, pipe in wn.pipes():
... pipe.diameter = pipe.diameter*0.9
Get element names and counts#
Several methods are available to return a list of element names and the number of elements, as shown in the example below. The list of element names can be used as an iterator, especially in cases where the element object is not needed.
>>> node_names = wn.node_name_list
>>> num_nodes = wn.num_nodes
>>> wn.describe(level=0)
{'Nodes': 97, 'Links': 119, 'Patterns': 5, 'Curves': 2, 'Sources': 0, 'Controls': 18}
Query element attributes#
The water network model contains methods to query node and link attributes. These methods can return attributes for all nodes or links, or for a subset using arguments that specify a node or link type (i.e., junction or pipe), or by specifying a threshold (i.e., >= 10 m). The query methods return a pandas Series with the element name and value. The following example returns node elevation, junction elevation, and junction elevations greater than 10 m (using a NumPy operator).
>>> import numpy as np
>>> node_elevation = wn.query_node_attribute('elevation')
>>> junction_elevation = wn.query_node_attribute('elevation',
... node_type=wntr.network.model.Junction)
>>> junction_elevation_10 = wn.query_node_attribute('elevation', np.greater_equal,
... 10, node_type=wntr.network.model.Junction)
In a similar manner, link attributes can be queried, as shown below.
>>> link_length = wn.query_link_attribute('length', np.less, 50)
Reset initial conditions#
When using the same water network model to run multiple simulations using the WNTRSimulator, initial conditions need to be reset between simulations. Initial conditions include simulation time, tank head, reservoir head, pipe status, pump status, and valve status. When using the EpanetSimulator, this step is not needed since EPANET starts at the initial conditions each time it is run.
>>> wn.reset_initial_values()
Build a model from scratch#
A water network model can also be created from scratch by adding elements to an empty model. Elements must be added before they are used in a simulation. For example, demand patterns are added to the model before they are used within a junction. The section below includes additional information on adding elements to a water network model.
>>> wn = wntr.network.WaterNetworkModel()
>>> wn.add_pattern('pat1', [1])
>>> wn.add_pattern('pat2', [1,2,3,4,5,6,7,8,9,10])
>>> wn.add_junction('node1', base_demand=0.01, demand_pattern='pat1', elevation=100,
... coordinates=(1,2))
>>> wn.add_junction('node2', base_demand=0.02, demand_pattern='pat2', elevation=50,
... coordinates=(1,3))
>>> wn.add_pipe('pipe1', 'node1', 'node2', length=304.8, diameter=0.3048,
... roughness=100, minor_loss=0.0, initial_status='OPEN')
>>> wn.add_reservoir('res', base_head=125, head_pattern='pat1', coordinates=(0,2))
>>> wn.add_pipe('pipe2', 'node1', 'res', length=100, diameter=0.3048, roughness=100,
... minor_loss=0.0, initial_status='OPEN')
>>> ax = wntr.graphics.plot_network(wn)