Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation updates including column/field names for GeoJSON and Shapefiles #372

Merged
merged 11 commits into from
Aug 29, 2023
55 changes: 53 additions & 2 deletions documentation/controls.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ EPANET uses "controls" and "rules" to define conditions [Ross00]_. WNTR replicat

**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).
Unlike EPANET simple controls which are evaluated based on the order in which they are defined in the INP file,
controls in WNTR can be prioritized to set the order of operation.
If controls with conflicting actions should occur at the same time, the control with the highest priority will override all others.
Controls are evaluated after each simulation timestep.
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 simple 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 operate on a rule timestep, which can be different from the simulation timestep.
Rules in WNTR emulate EPANET rule-based controls.

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.
Expand Down Expand Up @@ -91,6 +94,20 @@ repeat conditions that are defined with :class:`~wntr.network.controls.TimeOfDay
not repeat conditions that are defined within :class:`~wntr.network.controls.SimTimeCondition`.
The WNTRSimulator can handle repeat or not repeat options for both of these conditions.

Priority
-----------

Priority levels are defined in the :class:`~wntr.network.controls.ControlPriority` class and
include the following options.

* :class:`~wntr.network.controls.ControlPriority.very_low` or 0
* :class:`~wntr.network.controls.ControlPriority.low` or 1
* :class:`~wntr.network.controls.ControlPriority.medium_low` or 2
* :class:`~wntr.network.controls.ControlPriority.medium` or 3
* :class:`~wntr.network.controls.ControlPriority.medium_high` or 4
* :class:`~wntr.network.controls.ControlPriority.high` or 5
* :class:`~wntr.network.controls.ControlPriority.very_high` or 6

Controls
---------------------

Expand Down Expand Up @@ -212,3 +229,37 @@ The control or rule should be named so that it can be retrieved and modified if
ValueError: The name provided for the control is already used. Please either remove the control with that name first or use a different name for this control.
>>> wn.remove_control('NewTimeControl')
>>> wn.add_control('NewTimeControl', ctrl2) # doctest: +SKIP

Accessing and modifying controls/rules
---------------------------------------

Controls and rules can be accessed and modified in several ways.
For example, the following example returns a list of control names that are included in the model.

.. doctest::

>>> control_name_list = wn.control_name_list
>>> print(control_name_list)
['control 1', 'control 2', 'control 3', 'control 4', 'control 5', 'control 6', 'control 7', 'control 8', 'control 9', 'control 10', 'control 11', 'control 12', 'control 13', 'control 14', 'control 15', 'control 16', 'control 17', 'control 18']

The following example loops through all controls in the model and identifies controls that require pipe '330'.

.. doctest::

>>> pipe = wn.get_link('330')
>>> for name, control in wn.controls():
... if pipe in control.requires():
... print(name, control)
control 17 IF TANK 1 LEVEL BELOW 5.21208 THEN PIPE 330 STATUS IS CLOSED PRIORITY 3
control 18 IF TANK 1 LEVEL ABOVE 5.821680000000001 THEN PIPE 330 STATUS IS OPEN PRIORITY 3

The following example changes the priority of 'control 5' from medium (3) to low (1).

.. doctest::

>>> control = wn.get_control('control 5')
>>> print(control)
IF SYSTEM TIME IS 49:00:00 THEN PUMP 10 STATUS IS OPEN PRIORITY 3
>>> control.update_priority(1) # low
>>> print(control)
IF SYSTEM TIME IS 49:00:00 THEN PUMP 10 STATUS IS OPEN PRIORITY 1
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 11 additions & 4 deletions documentation/gis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,12 @@ The following examples use a water network generated from Net1.inp.
The :class:`~wntr.gis.geospatial.snap` and :class:`~wntr.gis.geospatial.intersect` examples
also use additional GIS data stored in the
`examples/data <https://github.com/USEPA/WNTR/blob/main/examples/data>`_ directory.
For simplicity, the examples assume that all data coordinates are in

For simplicity, the examples in this section assume that all network and data coordinates are in
the EPSG:4326 coordinate reference system (CRS).
**Note that EPANET does not have a standard or default coordinate reference system.**
More information on setting and transforming CRS is included in :ref:`crs`.


.. doctest::
:skipif: gpd is None
Expand All @@ -66,6 +69,8 @@ More information on setting and transforming CRS is included in :ref:`crs`.
>>> wn = wntr.network.WaterNetworkModel('networks/Net1.inp') # doctest: +SKIP


.. _gis_data:

Water network GIS data
------------------------

Expand Down Expand Up @@ -188,10 +193,12 @@ Geometry
Valve LineString or Point
============================== ===============================

A WaterNetworkGIS object can also be written to GeoJSON and Shapefile files using
A WaterNetworkGIS object can also be written to GeoJSON and Shapefiles using
the object's :class:`~wntr.gis.network.WaterNetworkGIS.write_geojson` and
:class:`~wntr.gis.network.WaterNetworkGIS.write_shapefile` methods.
The GeoJSON and Shapefile files can be loaded into GIS platforms for further analysis and visualization.
See :ref:`shapefile_format` for more information on Shapefile format.

The GeoJSON and Shapefiles can be loaded into GIS platforms for further analysis and visualization.
An example of creating GeoJSON files from a WaterNetworkModel using the function :class:`~wntr.gis.network.WaterNetworkGIS.write_geojson`
is shown below.

Expand Down Expand Up @@ -234,7 +241,7 @@ WNTR to add attributes to the water network model and analysis. Examples of thes
The snap and intersect examples below used additional GIS data stored in the
`examples/data <https://github.com/USEPA/WNTR/blob/main/examples/data>`_ directory.

Note, the GeoPandas ``read_file`` and ``to_file`` functions can be used to read/write external GeoJSON and Shapefile files in Python.
Note, the GeoPandas ``read_file`` and ``to_file`` functions can be used to read/write external GeoJSON and Shapefiles in Python.

.. _crs:

Expand Down
60 changes: 59 additions & 1 deletion documentation/graphics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
>>> import wntr
>>> import numpy as np
>>> import matplotlib.pylab as plt
>>> import pandas as pd
>>> pd.set_option("display.precision", 3)
>>> try:
... wn = wntr.network.model.WaterNetworkModel('../examples/networks/Net3.inp')
... except:
Expand All @@ -19,7 +21,7 @@ Graphics
======================================

WNTR includes several functions to plot water network models and to plot
fragility, pump curves, tank curves, and valve layers.
fragility curves, pump curves, tank curves, and valve layers.

Networks
--------------------
Expand Down Expand Up @@ -69,6 +71,62 @@ which can be further customized by the user.

Basic network graphic.

Additional network plot examples are included below (:numref:`fig-network-3`).
This includes the use of data stored as
a Pandas Series (pipe velocity from simulation results),
a dictionary (the length of the five longest pipes), and
a list of strings (tank names).
The example also combines multiple images into one figure using subplots and
changes the colormap from the default `Spectral_r` to `viridis` in one plot.
See https://matplotlib.org for more colormap options.

.. doctest::

>>> sim = wntr.sim.EpanetSimulator(wn)
>>> results = sim.run_sim()
>>> velocity = results.link['velocity'].loc[3600,:]
>>> print(velocity.head())
name
20 0.039
40 0.013
50 0.004
60 2.824
101 1.320
Name: 3600, dtype: float32

>>> length = wn.query_link_attribute('length')
>>> length_top5 = length.sort_values(ascending=False)[0:5]
>>> length_top5 = length_top5.round(2).to_dict()
>>> print(length_top5)
{'329': 13868.4, '101': 4328.16, '137': 1975.1, '169': 1389.89, '204': 1380.74}

>>> tank_names = wn.tank_name_list
>>> print(tank_names)
['1', '2', '3']

>>> fig, axes = plt.subplots(1, 3, figsize=(15, 5))
>>> ax = wntr.graphics.plot_network(wn, link_attribute=velocity,
... title='Pipe velocity at hour 1', link_colorbar_label='Velocity (m/s)', ax=axes[0])
>>> ax = wntr.graphics.plot_network(wn, link_attribute=length_top5, link_width=2,
... title='Longest 5 pipes', link_cmap = plt.cm.viridis,
... link_colorbar_label='Pipe length (m)', ax=axes[1])
>>> ax = wntr.graphics.plot_network(wn, node_attribute=tank_names,
... title='Location of tanks', ax=axes[2])

.. doctest::
:hide:

>>> plt.tight_layout()
>>> plt.savefig('plot_subplot_basic_network.png', dpi=300)

.. _fig-network-3:
.. figure:: figures/plot_subplot_basic_network.png
:width: 800
:alt: Network

Additional network graphics.


Interactive plotly networks
---------------------------------

Expand Down
107 changes: 99 additions & 8 deletions documentation/model_io.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
:hide:

>>> import wntr
>>> import pandas as pd
>>> try:
... import geopandas as gpd
... except ModuleNotFoundError:
Expand All @@ -29,6 +30,8 @@ EPANET INP file

The :class:`~wntr.network.io.read_inpfile` function builds a WaterNetworkModel from an EPANET INP file.
The EPANET INP file can be in the EPANET 2.00.12 or 2.2.0 format.
See https://epanet22.readthedocs.io for more information on EPANET INP file format.

The function can also be used to append information from an EPANET INP file into an existing WaterNetworkModel.

.. doctest::
Expand All @@ -53,7 +56,9 @@ EPANET INP files can be saved in the EPANET 2.00.12 or 2.2.0 format.
.. doctest::

>>> wntr.network.write_inpfile(wn, 'filename.inp', version=2.2)


.. _dictionary_representation:

Dictionary representation
-------------------------

Expand Down Expand Up @@ -172,8 +177,12 @@ a NetworkX graph could be added in a future version of WNTR.
JSON file
---------------------------------------------------------

JSON (JavaScript Object Notation) files store a collection of name/value pairs that is easy to read in text format.
More information on JSON files is available at https://www.json.org.
The format of JSON files in WNTR is based on the :ref:`dictionary_representation` of the WaterNetworkModel.

The :class:`~wntr.network.io.write_json` function writes a
JSON (JavaScript Object Notation) file from a WaterNetworkModel.
JSON file from a WaterNetworkModel.
The JSON file is a formatted version of the dictionary representation.

.. doctest::
Expand All @@ -194,6 +203,33 @@ They simply ignore extraneous or invalid dictionary keys.
GeoJSON files
-------------

GeoJSON files are commonly used to store geographic data structures.
More information on GeoJSON files can be found at https://geojson.org.

To use GeoJSON files in WNTR, a set of valid base column names are required.
Valid base GeoJSON column names can be obtained using the
:class:`~wntr.network.io.valid_gis_names` function.
The following example returns valid base GeoJSON column names for junctions.

.. doctest::
:skipif: gpd is None

>>> geojson_column_names = wntr.network.io.valid_gis_names()
>>> print(geojson_column_names['junctions'])
['name', 'base_demand', 'pattern_name', 'elevation', 'coordinates', 'demand_category', 'emitter_coefficient', 'initial_quality', 'minimum_pressure', 'required_pressure', 'pressure_exponent', 'tag']

A minimal list of valid column names can also be obtained by setting ``complete_list`` to False.
Column names that are optional (i.e., ``initial_quality``) and not included in the GeoJSON file are defined using default values.

.. doctest::
:skipif: gpd is None

>>> geojson_column_names = wntr.network.io.valid_gis_names(complete_list=False)
>>> print(geojson_column_names['junctions'])
['name', 'base_demand', 'pattern_name', 'elevation', 'coordinates', 'demand_category']

Note that GeoJSON files can contain additional custom column names that are assigned to WaterNetworkModel objects.

The :class:`~wntr.network.io.write_geojson` function writes a collection of
GeoJSON files from a WaterNetworkModel.
The GeoJSON files can be loaded into geographic information
Expand All @@ -204,7 +240,7 @@ system (GIS) platforms for further analysis and visualization.

>>> wntr.network.write_geojson(wn, 'Net3')

This creates the following GeoJSON files for junctions, tanks, reservoirs, pipes, and pumps:
This creates the following GeoJSON files for junctions, tanks, reservoirs, pipes, and pumps:

* Net3_junctions.geojson
* Net3_tanks.geojson
Expand All @@ -216,7 +252,8 @@ A GeoJSON file for valves, Net3_valves.geojson, is not created since Net3 has no
Note that patterns, curves, sources, controls, and options are not stored in the GeoJSON files.

The :class:`~wntr.network.io.read_geojson` function creates a WaterNetworkModel from a
dictionary of GeoJSON files.
dictionary of GeoJSON files.
Valid base column names and additional custom attributes are added to the model.
The function can also be used to append information from GeoJSON files into an existing WaterNetworkModel.

.. doctest::
Expand All @@ -233,12 +270,65 @@ The function can also be used to append information from GeoJSON files into an e
:class:`~wntr.gis.network.WaterNetworkGIS.write_geojson` and
:class:`~wntr.gis.network.WaterNetworkGIS.read_geojson`
are also methods on the WaterNetworkGIS object.

Shapefile files


.. _shapefile_format:

Shapefile
-------------------

A Shapefile is a collection of vector data storage files used to store geographic data.
The file format is developed and regulated by Esri.
For more information on Shapefiles, see https://www.esri.com.

To use Esri Shapefiles in WNTR, several formatting requirements are enforced:

* Geospatial data containing junction, tank, reservoir, pipe, pump, and valve data
are stored in separate Shapefile directories.

* The namespace for Node names (which includes junctions, tanks, and reservoirs)
must be unique. Likewise, the namespace for Links (which includes pipes,
pumps, and valves) must be unique. For example, this means that a junction
cannot have the same name as a tank.

* The Shapefile geometry is in a format compatible with GeoPandas, namely a
Point, LineString, or MultiLineString. See :ref:`gis_data` for
more information on geometries.

* Shapefiles truncate field names to 10 characters, while WaterNetworkModel
node and link attribute names are often longer. For this reason, it is
assumed that the first 10 characters of each attribute are unique.

* To create WaterNetworkModel from Shapefiles, a set of valid field names are required.
Valid base Shapefiles field names can be obtained using the
:class:`~wntr.network.io.valid_gis_names` function.
For Shapefiles, the `truncate` input parameter should be set to 10 (characters).
The following example returns valid base Shapefile field names for junctions.
Note that attributes like ``base_demand`` are truncated to ``base_deman``.

.. doctest::
:skipif: gpd is None

>>> shapefile_field_names = wntr.network.io.valid_gis_names(truncate_names=10)
>>> print(shapefile_field_names['junctions'])
['name', 'base_deman', 'pattern_na', 'elevation', 'coordinate', 'demand_cat', 'emitter_co', 'initial_qu', 'minimum_pr', 'required_p', 'pressure_e', 'tag']

A minimal list of valid field names can also be obtained by setting ``complete_list`` to False.
Field names that are optional (i.e., ``initial_quality``) and not included in the Shapefile are defined using default values.

.. doctest::
:skipif: gpd is None

>>> shapefile_field_names = wntr.network.io.valid_gis_names(complete_list=False,
... truncate_names=10)
>>> print(shapefile_field_names['junctions'])
['name', 'base_deman', 'pattern_na', 'elevation', 'coordinate', 'demand_cat']

* Shapefiles can contain additional custom field names that are assigned to WaterNetworkModel objects.


The :class:`~wntr.network.io.write_shapefile` function creates
Shapefile files from a WaterNetworkModel.
Shapefiles from a WaterNetworkModel.
The Shapefiles can be loaded into GIS platforms for further analysis and visualization.

.. doctest::
Expand All @@ -255,10 +345,11 @@ This creates the following Shapefile directories for junctions, tanks, reservoir
* Net3_pumps

A Shapefile for valves, Net3_valves, is not created since Net3 has no valves.
Note that patterns, curves, sources, controls, and options are not stored in the Shapefile files.
Note that patterns, curves, sources, controls, and options are not stored in the Shapefiles.

The :class:`~wntr.network.io.read_shapefile` function creates a WaterNetworkModel from a dictionary of
Shapefile directories.
Valid base field names and additional custom field names are added to the model.
The function can also be used to append information from Shapefiles into an existing WaterNetworkModel.

.. doctest::
Expand Down