Skip to content

Demand pattern library #492

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

Merged
merged 17 commits into from
Apr 4, 2025
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ include wntr/sim/network_isolation/numpy.i
include wntr/tests/networks_for_testing/*.inp
include wntr/library/msx/*.json
include wntr/library/msx/*.msx
include wntr/library/DemandPatternLibrary.json
Binary file added documentation/figures/demand_library.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions documentation/framework.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ run simulations are described in more detail below, followed by a list of softwa
:class:`~wntr.network` Contains classes and methods to define a water network model, network controls, model options, and graph representation of the network.
:class:`~wntr.scenario` Contains classes and methods to define disaster scenarios and fragility/survival curves.
:class:`~wntr.sim` Contains classes and methods to run hydraulic and water quality simulations using the water network model.
:class:`~wntr.library` Contains classes and methods to help build water network models.
:class:`~wntr.metrics` Contains functions to compute resilience, including topographic, hydraulic, water quality, water security, and economic metrics.
:class:`~wntr.morph` Contains methods to modify water network model morphology, including network skeletonization, modifying node coordinates, and splitting or breaking pipes.
:class:`~wntr.gis` Contains geospatial capabilities, including a function to convert the water network model to GeoDataFrames.
Expand Down
2 changes: 1 addition & 1 deletion documentation/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Key Features in WNTR
:widths: 65 35
:header-rows: 0

* - .. _fig-fragility:
* - .. _fig_wntr_features:
.. figure:: figures/wntr_features.png
:width: 750
- .. include:: attention.rst
Expand Down
254 changes: 254 additions & 0 deletions documentation/libraries.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
.. raw:: latex

\clearpage

.. _options:

.. doctest::
:hide:

>>> import matplotlib.pylab as plt
>>> import wntr
>>> try:
... wn = wntr.network.model.WaterNetworkModel('../examples/networks/Net1.inp')
... except:
... wn = wntr.network.model.WaterNetworkModel('examples/networks/Net1.inp')

Libraries
================================

WNTR includes the following libraries to help create water network models.
Libraries reside in the :class:`wntr.library` module.

* :ref:`demand_pattern_library`
* :ref:`msx_library`

.. _demand_pattern_library:

Demand pattern library
----------------------

The :class:`~wntr.library.demand_library.DemandPatternLibrary` class contains demand patterns
and methods to help create and modify patterns.
The demand pattern library can be used to add or modify patterns in a :class:`~wntr.network.model.WaterNetworkModel`.

The demand pattern library includes the following capabilities:

* Load a library of patterns from a JSON file
* Add a pattern to the library
* Create a pulse pattern (on/off sequence)
* Create a pattern that follows a gaussian or triangular distribution
* Combine patterns
* Create a copy of a pattern
* Modify the pattern timestep or start clocktime
* Add gaussian random noise to a pattern
* Normalize the pattern to have a mean of 1
* Filter patterns by category
* Create a :class:`~wntr.network.elements.Pattern` object to add the pattern to a :class:`~wntr.network.model.WaterNetworkModel`
* Create a Pandas Series (pattern indexed by time in seconds) to easily analyze or further modify the pattern
* Plot patterns
* Save and load custom libraries for use in subsequent projects

Each library entry is defined as a dictionary with the following keys:

* **name**: Pattern name (string)
* **category**: Pattern category (string, optional)
* **description**: Pattern description (string, optional)
* **citation**: Pattern citation (string, optional)
* **start_clocktime**: Time of day (in seconds from midnight) at which pattern begins (integer)
* **pattern_timestep**: Pattern timestep in seconds (integer)
* **wrap**: Indicates if the sequence of pattern values repeats (True or False)
* **multipliers**: Pattern values (list of floats)

Note that the pattern duration is not explicitly defined. Duration is inferred from the list of multipliers and the pattern timestep.
Several methods include duration as a optional input argument to change how long multipliers are repeated.
If wrap = False, the pattern values are set to 0 after the final multiplier value.

A default demand pattern library loads a JSON file that contains patterns from Net1, Net2, Net3, and Micropolis water network models.
Additional patterns could be added to the default library.
A sample entry from the default demand pattern library is shown below::

{
"name": "Micropolis_2",
"category": "Residential",
"description": "Residential",
"citation": "Brumbelow, Kelly, 02 Micropolis (2021). Synthetic Systems. 4. https://uknowledge.uky.edu/wdst_synthetic/4",
"start_clocktime": 0,
"pattern_timestep": 3600,
"wrap": true,
"multipliers": [
0.55, 0.55, 0.58, 0.67, 0.85, 1.05,
1.16, 1.12, 1.15, 1.1, 1.02, 1.0,
1.02, 1.1, 1.2, 1.35, 1.45, 1.5,
1.5, 1.35, 1.0, 0.8, 0.7, 0.6]
}

The following example illustrates functionality of the demand pattern library.
Note, methods that add or modify patterns return a pandas Series of the pattern. This can be helpful when developing new patterns.

Load the default demand pattern library, print names of the library entries, and plot patterns.

.. doctest::

>>> from wntr.library import DemandPatternLibrary

>>> demand_library = DemandPatternLibrary()
>>> print(demand_library.pattern_name_list)
['Null', 'Constant', 'Net1_1', 'Net2_1', 'Net3_1', 'KY_1', 'Micropolis_1', 'Micropolis_2', 'Micropolis_3', 'Micropolis_4', 'Micropolis_5']
>>> ax = demand_library.plot_patterns()

.. doctest::
:hide:

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

.. _fig_demand_library:
.. figure:: figures/demand_library.png
:width: 640
:alt: Demand library patterns

Demand library patterns.

Add a pulse and gaussian pattern.

.. doctest::

>>> on_off_sequence=[3*3600,6*3600,14*3600,20*3600]
>>> series = demand_library.add_pulse_pattern('Pulse', on_off_sequence)
>>> series = demand_library.add_gaussian_pattern('Gaussian', mean=12*3600,
... std=5*3600, duration=24*3600, pattern_timestep=3600,
... start_clocktime=0, normalize=True)

Add noise to a pattern.

.. doctest::

>>> demand_library.copy_pattern('Gaussian', 'Gaussian_with_noise')
>>> series = demand_library.apply_noise('Gaussian_with_noise', 0.1, normalize=True,
... seed=123)
>>> ax = demand_library.plot_patterns(names=['Gaussian', 'Gaussian_with_noise'])

.. doctest::
:hide:

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

.. _fig_demand_library_gaussian:
.. figure:: figures/demand_library_gaussian.png
:width: 640
:alt: New demand library patterns

Demand patterns, with and without noise.

Return a Pandas Series of the pattern.

.. doctest::

>>> series = demand_library.to_Series('Gaussian_with_noise', duration=48*3600)
>>> print(series.head())
0 7.474e-04
3600 2.676e-01
7200 2.862e-01
10800 2.302e-01
14400 4.742e-01
dtype: float64

Create a library of only commercial patterns.

.. doctest::

>>> commercial_patterns = demand_library.filter_by_category('Commercial')
>>> commercial_demand_library = DemandPatternLibrary(commercial_patterns)
>>> print(commercial_demand_library.pattern_name_list)
['Micropolis_1', 'Micropolis_4', 'Micropolis_5']

Resample a pattern with new time parameters. This is useful when applying patterns to a network with different start clocktime and/or pattern timestep.
For example, pattern "Net2_1", which has a start clocktime of 28800 seconds and pattern timestep of 3600 seconds,
can be resampled so it can be used in Net1, which has a start clocktime of 0 seconds and pattern timestep of 7200 seconds.

.. doctest::

>>> demand_library.copy_pattern('Net2_1', 'Net2_1_resampled')
>>> series = demand_library.resample_multipliers('Net2_1_resampled', duration=3*24*3600,
... pattern_timestep=7200, start_clocktime=0)
>>> ax = demand_library.plot_patterns(names=['Net2_1', 'Net2_1_resampled'])

.. doctest::
:hide:

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

.. _fig_demand_library_resampled:
.. figure:: figures/demand_library_resampled.png
:width: 640
:alt: New demand library patterns

Demand patterns, with and without resampling to match the start clocktime and pattern timestep of Net1.

Add the new pattern to a :class:`~wntr.network.model.WaterNetworkModel` of Net1.

.. doctest::

>>> import wntr
>>> wn = wntr.network.WaterNetworkModel('networks/Net1.inp') # doctest: +SKIP
>>> junction = wn.get_node('11')

>>> pattern = demand_library.to_Pattern('Net2_1_resampled')
>>> category = demand_library.library['Net2_1_resampled']['category']

>>> wn.add_pattern('from_Net2', pattern)
>>> junction.add_demand(base=5e-5, pattern_name='from_Net2', category=category)
>>> print(junction.demand_timeseries_list)
<Demands: [<TimeSeries: base_value=0.00946352946, pattern_name='1', category='None'>, <TimeSeries: base_value=5e-05, pattern_name='from_Net2', category='None'>]>

Write the new pattern library to a file.

.. doctest::

>>> demand_library.write_json("Custom_demand_pattern_library.json")

Load an existing demand pattern library for use in subsequent projects.

.. doctest::

>>> custom_demand_library = DemandPatternLibrary("Custom_demand_pattern_library.json")
>>> print(custom_demand_library.pattern_name_list)
['Null', 'Constant', 'Net1_1', 'Net2_1', 'Net3_1', 'KY_1', 'Micropolis_1', 'Micropolis_2', 'Micropolis_3', 'Micropolis_4', 'Micropolis_5', 'Pulse', 'Gaussian', 'Gaussian_with_noise', 'Net2_1_resampled']

.. _msx_library:

Multispecies model library
---------------------------

The :class:`~wntr.library.msx.MsxLibrary` class contains a library of MSX models that can be used in
multispecies reaction simulations.
See :ref:`msx_water_quality` for more information on simulating multispecies reactions in WNTR.

The multispecies model library includes the following models:

* `Arsenic oxidation/adsorption <https://github.com/USEPA/WNTR/blob/msx/wntr/msx/_library_data/arsenic_chloramine.json>`_ :cite:p:`shang2023`
* `Batch chloramine decay <https://github.com/USEPA/WNTR/blob/msx/wntr/msx/_library_data/batch_chloramine_decay.json>`_
* `Lead plumbosolvency <https://github.com/USEPA/WNTR/blob/msx/wntr/msx/_library_data/lead_ppm.json>`_ :cite:p:`bwms20`
* `Nicotine/chlorine reaction <https://github.com/USEPA/WNTR/blob/msx/wntr/msx/_library_data/nicotine.json>`_
* `Nicotine/chlorine reaction with reactive intermediate <https://github.com/USEPA/WNTR/blob/msx/wntr/msx/_library_data/nicotine_ri.json>`_

The models are stored in JSON format.
Additional models can be loaded into the library by setting a user specified path.
Additional models could also be added directly to the WNTR Reactions library.

The following example loads the Lead plumbosolvency model (lead_ppm) from the MsxLibrary.

.. doctest::

>>> import wntr.library.msx
>>> reaction_library = wntr.library.msx.MsxLibrary()

>>> print(reaction_library.model_name_list()) # doctest: +SKIP
['arsenic_chloramine', 'batch_chloramine_decay', 'lead_ppm', 'nicotine', 'nicotine_ri']

>>> lead_ppm = reaction_library.get_model("lead_ppm")
>>> print(lead_ppm)
MsxModel(name='lead_ppm')
1 change: 1 addition & 0 deletions documentation/userguide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ U.S. Department of Energy's National Nuclear Security Administration under contr
networkxgraph
layers
options
libraries

.. toctree::
:maxdepth: 1
Expand Down
31 changes: 1 addition & 30 deletions documentation/waterquality_msx.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Multi-species water quality simulation

The EpanetSimulator can use EPANET-MSX 2.0 :cite:p:`shang2023` to run
multi-species water quality simulations.
WNTR also includes a library of MSX models, see :ref:`msx_library` for more information.
Additional multi-species simulation options are discussed in :ref:`advanced_simulation`.

A multi-species analysis is run if a :class:`~wntr.msx.model.MsxModel` is added to the
Expand Down Expand Up @@ -73,33 +74,3 @@ All species must have two reactions defined for the model to be run successfully
One is a **pipe reaction**, the other is a **tank reaction**.

Examples that illustrate how to build MSX models in WNTR are included in :ref:`advanced_simulation`.

Reaction library
-----------------
WNTR also contains a library of MSX models that are accessed through the
:class:`~wntr.library.msx.MsxLibrary`.
This includes the following models:

* `Arsenic oxidation/adsorption <https://github.com/USEPA/WNTR/blob/msx/wntr/msx/_library_data/arsenic_chloramine.json>`_ :cite:p:`shang2023`
* `Batch chloramine decay <https://github.com/USEPA/WNTR/blob/msx/wntr/msx/_library_data/batch_chloramine_decay.json>`_
* `Lead plumbosolvency <https://github.com/USEPA/WNTR/blob/msx/wntr/msx/_library_data/lead_ppm.json>`_ :cite:p:`bwms20`
* `Nicotine/chlorine reaction <https://github.com/USEPA/WNTR/blob/msx/wntr/msx/_library_data/nicotine.json>`_
* `Nicotine/chlorine reaction with reactive intermediate <https://github.com/USEPA/WNTR/blob/msx/wntr/msx/_library_data/nicotine_ri.json>`_

The models are stored in JSON format.
Additional models can be loaded into the library by setting a user specified path.
Additional models could also be added directly to the WNTR Reactions library.

The following example loads the Lead plumbosolvency model (lead_ppm) from the MsxLibrary.

.. doctest::

>>> import wntr.library.msx
>>> reaction_library = wntr.library.msx.MsxLibrary()

>>> print(reaction_library.model_name_list()) # doctest: +SKIP
['arsenic_chloramine', 'batch_chloramine_decay', 'lead_ppm', 'nicotine', 'nicotine_ri']

>>> lead_ppm = reaction_library.get_model("lead_ppm")
>>> print(lead_ppm)
MsxModel(name='lead_ppm')
1 change: 1 addition & 0 deletions documentation/wntr-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ API documentation
wntr.epanet
wntr.gis
wntr.graphics
wntr.library
wntr.metrics
wntr.morph
wntr.msx
Expand Down
1 change: 1 addition & 0 deletions wntr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from wntr import scenario
from wntr import graphics
from wntr import gis
from wntr import library
from wntr import utils
from wntr import msx

Expand Down
Loading
Loading