epyt-flow 0.6.0__tar.gz → 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/PKG-INFO +49 -6
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/README.md +47 -4
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/REQUIREMENTS.txt +1 -1
- epyt_flow-0.7.0/epyt_flow/VERSION +1 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/metrics.py +66 -4
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/serialization.py +33 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/simulation/scada/scada_data.py +1 -1
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/simulation/scenario_simulator.py +94 -7
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/simulation/sensor_config.py +1 -1
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/topology.py +3 -3
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/uncertainty/uncertainties.py +8 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/utils.py +69 -2
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow.egg-info/PKG-INFO +49 -6
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow.egg-info/requires.txt +1 -1
- epyt_flow-0.6.0/epyt_flow/VERSION +0 -1
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/CITATION.cff +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/CODE_OF_CONDUCT.md +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/LICENSE +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/MANIFEST.in +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/AUTHORS +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/LICENSE +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/Readme_SRC_Engines.txt +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/enumstxt.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/epanet.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/epanet2.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/epanet2.def +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/errors.dat +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/funcs.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/genmmd.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/hash.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/hash.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydcoeffs.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydraul.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydsolver.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydstatus.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_2.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_enums.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/inpfile.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/input1.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/input2.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/input3.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/main.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/mempool.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/mempool.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/output.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/project.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/quality.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/qualreact.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/qualroute.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/report.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/rules.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/smatrix.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/text.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/types.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/MSX_Updates.txt +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/dispersion.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/hash.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/hash.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx_export.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/mempool.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/mempool.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxchem.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxcompiler.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxdict.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxdispersion.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxerr.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxfile.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxinp.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxout.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxproj.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxqual.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxrpt.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxtank.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxtoolkit.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxtypes.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxutils.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxutils.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/newton.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/newton.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/rk5.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/rk5.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/ros2.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/ros2.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/smatrix.c +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/smatrix.h +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/readme.txt +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/compile_linux.sh +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/compile_macos.sh +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/__init__.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/data/__init__.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/data/benchmarks/__init__.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/data/benchmarks/batadal.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/data/benchmarks/batadal_data.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/data/benchmarks/battledim.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/data/benchmarks/battledim_data.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/data/benchmarks/gecco_water_quality.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/data/benchmarks/leakdb.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/data/benchmarks/leakdb_data.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/data/benchmarks/water_usage.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/data/networks.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/gym/__init__.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/gym/control_gyms.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/gym/scenario_control_env.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/models/__init__.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/models/event_detector.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/models/sensor_interpolation_detector.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/__init__.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/base_handler.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/res_manager.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/scada_data/__init__.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/scada_data/data_handlers.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/scada_data/export_handlers.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/scada_data/handlers.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/scenario/__init__.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/scenario/event_handlers.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/scenario/handlers.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/scenario/simulation_handlers.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/scenario/uncertainty_handlers.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/server.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/simulation/__init__.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/simulation/events/__init__.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/simulation/events/actuator_events.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/simulation/events/event.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/simulation/events/leakages.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/simulation/events/sensor_faults.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/simulation/events/sensor_reading_attack.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/simulation/events/sensor_reading_event.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/simulation/events/system_event.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/simulation/parallel_simulation.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/simulation/scada/__init__.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/simulation/scada/advanced_control.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/simulation/scada/scada_data_export.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/simulation/scenario_config.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/simulation/scenario_visualizer.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/uncertainty/__init__.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/uncertainty/model_uncertainty.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/uncertainty/sensor_noise.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow/uncertainty/utils.py +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow.egg-info/SOURCES.txt +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow.egg-info/dependency_links.txt +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/epyt_flow.egg-info/top_level.txt +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/pyproject.toml +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/setup.cfg +0 -0
- {epyt_flow-0.6.0 → epyt_flow-0.7.0}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: epyt-flow
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: EPyT-Flow -- EPANET Python Toolkit - Flow
|
|
5
5
|
Author-email: André Artelt <aartelt@techfak.uni-bielefeld.de>, "Marios S. Kyriakou" <kiriakou.marios@ucy.ac.cy>, "Stelios G. Vrachimis" <vrachimis.stelios@ucy.ac.cy>
|
|
6
6
|
License: MIT License
|
|
@@ -20,7 +20,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
20
20
|
Requires-Python: >=3.9
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
|
-
Requires-Dist: epyt>=1.
|
|
23
|
+
Requires-Dist: epyt>=1.2.0
|
|
24
24
|
Requires-Dist: requests>=2.31.0
|
|
25
25
|
Requires-Dist: scipy>=1.11.4
|
|
26
26
|
Requires-Dist: u-msgpack-python>=2.8.0
|
|
@@ -121,7 +121,7 @@ pip install .
|
|
|
121
121
|
```python
|
|
122
122
|
from epyt_flow.data.benchmarks import load_leakdb_scenarios
|
|
123
123
|
from epyt_flow.simulation import ScenarioSimulator
|
|
124
|
-
from epyt_flow.utils import to_seconds
|
|
124
|
+
from epyt_flow.utils import to_seconds, plot_timeseries_data
|
|
125
125
|
|
|
126
126
|
|
|
127
127
|
if __name__ == "__main__":
|
|
@@ -142,14 +142,49 @@ if __name__ == "__main__":
|
|
|
142
142
|
# Run entire simulation
|
|
143
143
|
scada_data = sim.run_simulation()
|
|
144
144
|
|
|
145
|
-
#
|
|
145
|
+
# Print & plot sensor readings over the entire simulation
|
|
146
146
|
print(f"Pressure readings: {scada_data.get_data_pressures()}")
|
|
147
|
+
plot_timeseries_data(scada_data.get_data_pressures().T,
|
|
148
|
+
labels=[f"Node {n_id}" for n_id in
|
|
149
|
+
scada_data.sensor_config.pressure_sensors],
|
|
150
|
+
x_axis_label="Time (30min steps)",
|
|
151
|
+
y_axis_label="Pressure in $m$")
|
|
152
|
+
|
|
147
153
|
print(f"Flow readings: {scada_data.get_data_flows()}")
|
|
154
|
+
plot_timeseries_data(scada_data.get_data_flows().T,
|
|
155
|
+
x_axis_label="Time (30min steps)",
|
|
156
|
+
y_axis_label="Flow rate in $m^3/h$")
|
|
148
157
|
```
|
|
158
|
+
### Generated plots
|
|
159
|
+
|
|
160
|
+
<div>
|
|
161
|
+
<img src="https://github.com/WaterFutures/EPyT-Flow/blob/dev/docs/_static/examples_basic_usage_pressure.png?raw=true" width="49%"/>
|
|
162
|
+
<img src="https://github.com/WaterFutures/EPyT-Flow/blob/dev/docs/_static/examples_basic_usage_flow.png?raw=true" width="49%"/>
|
|
163
|
+
</div>
|
|
149
164
|
|
|
150
165
|
## Documentation
|
|
151
166
|
|
|
152
|
-
Documentation is available on readthedocs:[https://epyt-flow.readthedocs.io/en/latest/](https://epyt-flow.readthedocs.io/en/stable)
|
|
167
|
+
Documentation is available on readthedocs: [https://epyt-flow.readthedocs.io/en/latest/](https://epyt-flow.readthedocs.io/en/stable)
|
|
168
|
+
|
|
169
|
+
## How to Get Started?
|
|
170
|
+
|
|
171
|
+
EPyT-Flow is accompanied by an extensive documentation
|
|
172
|
+
[https://epyt-flow.readthedocs.io/en/latest/](https://epyt-flow.readthedocs.io/en/stable)
|
|
173
|
+
(including many [examples](https://epyt-flow.readthedocs.io/en/stable/#examples)).
|
|
174
|
+
|
|
175
|
+
If you are new to water distribution networks, we recommend first to read the chapter on
|
|
176
|
+
[Modeling of Water Distribution Networks](https://epyt-flow.readthedocs.io/en/stable/tut.intro.html).
|
|
177
|
+
You might also want to check out some lecture notes on
|
|
178
|
+
[Smart Water Systems](https://github.com/KIOS-Research/ece808-smart-water-systems).
|
|
179
|
+
|
|
180
|
+
If you are already familiar with WDNs (and software such as EPANET), we recommend checking out
|
|
181
|
+
our [WDSA CCWI 2024 tutorial](https://github.com/WaterFutures/EPyT-and-EPyT-Flow-Tutorial) which
|
|
182
|
+
not only teaches you how to use EPyT and EPyT-Flow but also contains some examples of applying
|
|
183
|
+
Machine Learning in WDNs.
|
|
184
|
+
Besides that, you can read in-depth about the different functionalities of EPyT-Flow in the
|
|
185
|
+
[In-depth Tutorial](https://epyt-flow.readthedocs.io/en/stable/tutorial.html) of the documentation --
|
|
186
|
+
we recommend reading the chapters in the order in which they are presented;
|
|
187
|
+
you might decide to skip some of the last chapters if their content is not relevant to you.
|
|
153
188
|
|
|
154
189
|
## License
|
|
155
190
|
|
|
@@ -170,6 +205,14 @@ If you use this software, please cite it as follows:
|
|
|
170
205
|
}
|
|
171
206
|
```
|
|
172
207
|
|
|
208
|
+
## How to get Support?
|
|
209
|
+
|
|
210
|
+
If you come across any bug or need assistance please feel free to open a new
|
|
211
|
+
[issue](https://github.com/WaterFutures/EPyT-Flow/issues/)
|
|
212
|
+
if non of the existing issues answers your questions.
|
|
213
|
+
|
|
173
214
|
## How to Contribute?
|
|
174
215
|
|
|
175
|
-
Contributions (e.g. creating issues, pull-requests, etc.) are welcome --
|
|
216
|
+
Contributions (e.g. creating issues, pull-requests, etc.) are welcome --
|
|
217
|
+
please make sure to read the [code of conduct](CODE_OF_CONDUCT.md) and
|
|
218
|
+
follow the [developers' guidelines](DEVELOPERS.md).
|
|
@@ -86,7 +86,7 @@ pip install .
|
|
|
86
86
|
```python
|
|
87
87
|
from epyt_flow.data.benchmarks import load_leakdb_scenarios
|
|
88
88
|
from epyt_flow.simulation import ScenarioSimulator
|
|
89
|
-
from epyt_flow.utils import to_seconds
|
|
89
|
+
from epyt_flow.utils import to_seconds, plot_timeseries_data
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
if __name__ == "__main__":
|
|
@@ -107,14 +107,49 @@ if __name__ == "__main__":
|
|
|
107
107
|
# Run entire simulation
|
|
108
108
|
scada_data = sim.run_simulation()
|
|
109
109
|
|
|
110
|
-
#
|
|
110
|
+
# Print & plot sensor readings over the entire simulation
|
|
111
111
|
print(f"Pressure readings: {scada_data.get_data_pressures()}")
|
|
112
|
+
plot_timeseries_data(scada_data.get_data_pressures().T,
|
|
113
|
+
labels=[f"Node {n_id}" for n_id in
|
|
114
|
+
scada_data.sensor_config.pressure_sensors],
|
|
115
|
+
x_axis_label="Time (30min steps)",
|
|
116
|
+
y_axis_label="Pressure in $m$")
|
|
117
|
+
|
|
112
118
|
print(f"Flow readings: {scada_data.get_data_flows()}")
|
|
119
|
+
plot_timeseries_data(scada_data.get_data_flows().T,
|
|
120
|
+
x_axis_label="Time (30min steps)",
|
|
121
|
+
y_axis_label="Flow rate in $m^3/h$")
|
|
113
122
|
```
|
|
123
|
+
### Generated plots
|
|
124
|
+
|
|
125
|
+
<div>
|
|
126
|
+
<img src="https://github.com/WaterFutures/EPyT-Flow/blob/dev/docs/_static/examples_basic_usage_pressure.png?raw=true" width="49%"/>
|
|
127
|
+
<img src="https://github.com/WaterFutures/EPyT-Flow/blob/dev/docs/_static/examples_basic_usage_flow.png?raw=true" width="49%"/>
|
|
128
|
+
</div>
|
|
114
129
|
|
|
115
130
|
## Documentation
|
|
116
131
|
|
|
117
|
-
Documentation is available on readthedocs:[https://epyt-flow.readthedocs.io/en/latest/](https://epyt-flow.readthedocs.io/en/stable)
|
|
132
|
+
Documentation is available on readthedocs: [https://epyt-flow.readthedocs.io/en/latest/](https://epyt-flow.readthedocs.io/en/stable)
|
|
133
|
+
|
|
134
|
+
## How to Get Started?
|
|
135
|
+
|
|
136
|
+
EPyT-Flow is accompanied by an extensive documentation
|
|
137
|
+
[https://epyt-flow.readthedocs.io/en/latest/](https://epyt-flow.readthedocs.io/en/stable)
|
|
138
|
+
(including many [examples](https://epyt-flow.readthedocs.io/en/stable/#examples)).
|
|
139
|
+
|
|
140
|
+
If you are new to water distribution networks, we recommend first to read the chapter on
|
|
141
|
+
[Modeling of Water Distribution Networks](https://epyt-flow.readthedocs.io/en/stable/tut.intro.html).
|
|
142
|
+
You might also want to check out some lecture notes on
|
|
143
|
+
[Smart Water Systems](https://github.com/KIOS-Research/ece808-smart-water-systems).
|
|
144
|
+
|
|
145
|
+
If you are already familiar with WDNs (and software such as EPANET), we recommend checking out
|
|
146
|
+
our [WDSA CCWI 2024 tutorial](https://github.com/WaterFutures/EPyT-and-EPyT-Flow-Tutorial) which
|
|
147
|
+
not only teaches you how to use EPyT and EPyT-Flow but also contains some examples of applying
|
|
148
|
+
Machine Learning in WDNs.
|
|
149
|
+
Besides that, you can read in-depth about the different functionalities of EPyT-Flow in the
|
|
150
|
+
[In-depth Tutorial](https://epyt-flow.readthedocs.io/en/stable/tutorial.html) of the documentation --
|
|
151
|
+
we recommend reading the chapters in the order in which they are presented;
|
|
152
|
+
you might decide to skip some of the last chapters if their content is not relevant to you.
|
|
118
153
|
|
|
119
154
|
## License
|
|
120
155
|
|
|
@@ -135,6 +170,14 @@ If you use this software, please cite it as follows:
|
|
|
135
170
|
}
|
|
136
171
|
```
|
|
137
172
|
|
|
173
|
+
## How to get Support?
|
|
174
|
+
|
|
175
|
+
If you come across any bug or need assistance please feel free to open a new
|
|
176
|
+
[issue](https://github.com/WaterFutures/EPyT-Flow/issues/)
|
|
177
|
+
if non of the existing issues answers your questions.
|
|
178
|
+
|
|
138
179
|
## How to Contribute?
|
|
139
180
|
|
|
140
|
-
Contributions (e.g. creating issues, pull-requests, etc.) are welcome --
|
|
181
|
+
Contributions (e.g. creating issues, pull-requests, etc.) are welcome --
|
|
182
|
+
please make sure to read the [code of conduct](CODE_OF_CONDUCT.md) and
|
|
183
|
+
follow the [developers' guidelines](DEVELOPERS.md).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.7.0
|
|
@@ -3,12 +3,74 @@ This module provides different metrics for evaluation.
|
|
|
3
3
|
"""
|
|
4
4
|
import numpy as np
|
|
5
5
|
from sklearn.metrics import roc_auc_score as skelarn_roc_auc_score, f1_score as skelarn_f1_scpre, \
|
|
6
|
-
mean_absolute_error
|
|
6
|
+
mean_absolute_error, root_mean_squared_error, r2_score as sklearn_r2_score
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def
|
|
9
|
+
def r2_score(y_pred: np.ndarray, y: np.ndarray) -> float:
|
|
10
10
|
"""
|
|
11
|
-
Computes the
|
|
11
|
+
Computes the R^2 score (also called the coefficient of determination).
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
y_pred : `numpy.ndarray`
|
|
16
|
+
Predicted outputs.
|
|
17
|
+
y : `numpy.ndarray`
|
|
18
|
+
Ground truth outputs.
|
|
19
|
+
|
|
20
|
+
Returns
|
|
21
|
+
-------
|
|
22
|
+
`float`
|
|
23
|
+
R^2 score.
|
|
24
|
+
"""
|
|
25
|
+
return sklearn_r2_score(y, y_pred)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def running_r2_score(y_pred: np.ndarray, y: np.ndarray) -> list[float]:
|
|
29
|
+
"""
|
|
30
|
+
Computes and returns the running R^2 score -- i.e. the R^2 score for every point in time.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
y_pred : `numpy.ndarray`
|
|
35
|
+
Predicted outputs.
|
|
36
|
+
y : `numpy.ndarray`
|
|
37
|
+
Ground truth outputs.
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
`list[float]`
|
|
42
|
+
The running R^2 score.
|
|
43
|
+
"""
|
|
44
|
+
r = []
|
|
45
|
+
|
|
46
|
+
for t in range(2, len(y_pred)):
|
|
47
|
+
r.append(r2_score(y_pred[:t], y[:t]))
|
|
48
|
+
|
|
49
|
+
return r
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def mean_squared_error(y_pred: np.ndarray, y: np.ndarray) -> float:
|
|
53
|
+
"""
|
|
54
|
+
Computes the Mean Squared Error (MSE).
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
y_pred : `numpy.ndarray`
|
|
59
|
+
Predicted outputs.
|
|
60
|
+
y : `numpy.ndarray`
|
|
61
|
+
Ground truth outputs.
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
`float`
|
|
66
|
+
MSE.
|
|
67
|
+
"""
|
|
68
|
+
return root_mean_squared_error(y, y_pred)**2
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def running_mse(y_pred: np.ndarray, y: np.ndarray) -> list[float]:
|
|
72
|
+
"""
|
|
73
|
+
Computes the running Mean Squared Error (MSE) -- i.e. the MSE for every point in time.
|
|
12
74
|
|
|
13
75
|
Parameters
|
|
14
76
|
----------
|
|
@@ -39,7 +101,7 @@ def running_mse(y_pred: np.ndarray, y: np.ndarray):
|
|
|
39
101
|
r_mse = list(esq for esq in e_sq)
|
|
40
102
|
|
|
41
103
|
for i in range(1, len(y)):
|
|
42
|
-
r_mse[i] = ((i * r_mse[i - 1]) / (i + 1)) + (r_mse[i] / (i + 1))
|
|
104
|
+
r_mse[i] = float((i * r_mse[i - 1]) / (i + 1)) + (r_mse[i] / (i + 1))
|
|
43
105
|
|
|
44
106
|
return r_mse
|
|
45
107
|
|
|
@@ -309,6 +309,39 @@ class JsonSerializable(Serializable):
|
|
|
309
309
|
"""
|
|
310
310
|
return my_load_from_json(data)
|
|
311
311
|
|
|
312
|
+
@staticmethod
|
|
313
|
+
def load_from_json_file(f_in: str) -> Any:
|
|
314
|
+
"""
|
|
315
|
+
Deserializes an instance of this class from a JSON file.
|
|
316
|
+
|
|
317
|
+
Parameters
|
|
318
|
+
----------
|
|
319
|
+
f_in : `str`
|
|
320
|
+
Path to the JSON file from which to deserialize the object.
|
|
321
|
+
|
|
322
|
+
Returns
|
|
323
|
+
-------
|
|
324
|
+
`Any`
|
|
325
|
+
Deserialized object.
|
|
326
|
+
"""
|
|
327
|
+
with open(f_in, "r", encoding="utf-8") as f:
|
|
328
|
+
return my_load_from_json(f.read())
|
|
329
|
+
|
|
330
|
+
def save_to_json_file(self, f_out: str) -> None:
|
|
331
|
+
"""
|
|
332
|
+
Serializes this instance and stores it in a JSON file.
|
|
333
|
+
|
|
334
|
+
Parameters
|
|
335
|
+
----------
|
|
336
|
+
f_in : `str`
|
|
337
|
+
Path to the JSON file where this serialized object will be stored.
|
|
338
|
+
"""
|
|
339
|
+
if not f_out.endswith(self.file_ext()):
|
|
340
|
+
f_out += self.file_ext()
|
|
341
|
+
|
|
342
|
+
with open(f_out, "w", encoding="utf-8") as f:
|
|
343
|
+
f.write(self.to_json())
|
|
344
|
+
|
|
312
345
|
|
|
313
346
|
def load(data: Union[bytes, BufferedIOBase]) -> Any:
|
|
314
347
|
"""
|
|
@@ -2150,7 +2150,7 @@ class ScadaData(Serializable):
|
|
|
2150
2150
|
raise TypeError("'bulk_species_sensor_locations' must be an instance of 'dict'" +
|
|
2151
2151
|
f" but not of '{type(bulk_species_sensor_locations)}'")
|
|
2152
2152
|
for species_id in bulk_species_sensor_locations:
|
|
2153
|
-
if species_id not in self.__sensor_config.
|
|
2153
|
+
if species_id not in self.__sensor_config.bulk_species_node_sensors:
|
|
2154
2154
|
raise ValueError(f"Species '{species_id}' is not included in the " +
|
|
2155
2155
|
"sensor configuration")
|
|
2156
2156
|
|
|
@@ -94,6 +94,7 @@ class ScenarioSimulator():
|
|
|
94
94
|
self.__controls = []
|
|
95
95
|
self.__system_events = []
|
|
96
96
|
self.__sensor_reading_events = []
|
|
97
|
+
self.__running_simulation = False
|
|
97
98
|
|
|
98
99
|
custom_epanet_lib = None
|
|
99
100
|
custom_epanetmsx_lib = None
|
|
@@ -150,7 +151,8 @@ class ScenarioSimulator():
|
|
|
150
151
|
|
|
151
152
|
self.epanet_api = epanet(my_f_inp_in, ph=self.__f_msx_in is None,
|
|
152
153
|
customlib=custom_epanet_lib, loadfile=True,
|
|
153
|
-
display_msg=epanet_verbose
|
|
154
|
+
display_msg=epanet_verbose,
|
|
155
|
+
display_warnings=False)
|
|
154
156
|
|
|
155
157
|
if self.__f_msx_in is not None:
|
|
156
158
|
self.epanet_api.loadMSXFile(my_f_msx_in, customMSXlib=custom_epanetmsx_lib)
|
|
@@ -432,7 +434,7 @@ class ScenarioSimulator():
|
|
|
432
434
|
new_sensor_config.quality_node_sensors = self.__sensor_config.quality_node_sensors
|
|
433
435
|
new_sensor_config.quality_link_sensors = self.__sensor_config.quality_link_sensors
|
|
434
436
|
new_sensor_config.pump_state_sensors = self.__sensor_config.pump_state_sensors
|
|
435
|
-
new_sensor_config.
|
|
437
|
+
new_sensor_config.pump_efficiency_sensors = self.__sensor_config.pump_efficiency_sensors
|
|
436
438
|
new_sensor_config.pump_energyconsumption_sensors = self.__sensor_config.\
|
|
437
439
|
pump_energyconsumption_sensors
|
|
438
440
|
new_sensor_config.valve_state_sensors = self.__sensor_config.valve_state_sensors
|
|
@@ -456,8 +458,6 @@ class ScenarioSimulator():
|
|
|
456
458
|
|
|
457
459
|
if self.__my_f_inp_in is not None:
|
|
458
460
|
shutil.rmtree(pathlib.Path(self.__my_f_inp_in).parent)
|
|
459
|
-
if self.__my_f_msx_in is not None:
|
|
460
|
-
shutil.rmtree(pathlib.Path(self.__my_f_msx_in).parent)
|
|
461
461
|
|
|
462
462
|
def __enter__(self):
|
|
463
463
|
return self
|
|
@@ -744,6 +744,19 @@ class ScenarioSimulator():
|
|
|
744
744
|
"units": qualityunit_to_id(qual_info.QualityChemUnits),
|
|
745
745
|
"trace_node_id": qual_info.TraceNode}
|
|
746
746
|
|
|
747
|
+
def get_reporting_time_step(self) -> int:
|
|
748
|
+
"""
|
|
749
|
+
Gets the reporting time steps -- i.e. time steps at which sensor readings are provided.
|
|
750
|
+
|
|
751
|
+
Is always a multiple of the hydraulic time step.
|
|
752
|
+
|
|
753
|
+
Returns
|
|
754
|
+
-------
|
|
755
|
+
`int`
|
|
756
|
+
Reporting time steps in seconds.
|
|
757
|
+
"""
|
|
758
|
+
return self.epanet_api.getTimeReportingStep()
|
|
759
|
+
|
|
747
760
|
def get_scenario_config(self) -> ScenarioConfig:
|
|
748
761
|
"""
|
|
749
762
|
Gets the configuration of this scenario -- i.e. all information & elements
|
|
@@ -758,7 +771,7 @@ class ScenarioSimulator():
|
|
|
758
771
|
|
|
759
772
|
general_params = {"hydraulic_time_step": self.get_hydraulic_time_step(),
|
|
760
773
|
"quality_time_step": self.get_quality_time_step(),
|
|
761
|
-
"reporting_time_step": self.
|
|
774
|
+
"reporting_time_step": self.get_reporting_time_step(),
|
|
762
775
|
"simulation_duration": self.get_simulation_duration(),
|
|
763
776
|
"flow_units_id": self.get_flow_units(),
|
|
764
777
|
"quality_model": self.get_quality_model(),
|
|
@@ -874,6 +887,9 @@ class ScenarioSimulator():
|
|
|
874
887
|
"""
|
|
875
888
|
Randomizes all demand patterns.
|
|
876
889
|
"""
|
|
890
|
+
if self.__running_simulation is True:
|
|
891
|
+
raise RuntimeError("Can not change general parameters when simulation is running.")
|
|
892
|
+
|
|
877
893
|
self.__adapt_to_network_changes()
|
|
878
894
|
|
|
879
895
|
# Get all demand patterns
|
|
@@ -996,6 +1012,9 @@ class ScenarioSimulator():
|
|
|
996
1012
|
event : :class:`~epyt_flow.simulation.events.system_event.SystemEvent`
|
|
997
1013
|
System event.
|
|
998
1014
|
"""
|
|
1015
|
+
if self.__running_simulation is True:
|
|
1016
|
+
raise RuntimeError("Can not add events when simulation is running.")
|
|
1017
|
+
|
|
999
1018
|
self.__adapt_to_network_changes()
|
|
1000
1019
|
|
|
1001
1020
|
if not isinstance(event, SystemEvent):
|
|
@@ -1015,6 +1034,9 @@ class ScenarioSimulator():
|
|
|
1015
1034
|
sensor_fault_event : :class:`~epyt_flow.simulation.events.sensor_faults.SensorFault`
|
|
1016
1035
|
Sensor fault specifications.
|
|
1017
1036
|
"""
|
|
1037
|
+
if self.__running_simulation is True:
|
|
1038
|
+
raise RuntimeError("Can not add events when simulation is running.")
|
|
1039
|
+
|
|
1018
1040
|
self.__adapt_to_network_changes()
|
|
1019
1041
|
|
|
1020
1042
|
sensor_fault_event.validate(self.__sensor_config)
|
|
@@ -1035,6 +1057,9 @@ class ScenarioSimulator():
|
|
|
1035
1057
|
sensor_reading_attack : :class:`~epyt_flow.simulation.events.sensor_reading_attack.SensorReadingAttack`
|
|
1036
1058
|
Sensor fault specifications.
|
|
1037
1059
|
"""
|
|
1060
|
+
if self.__running_simulation is True:
|
|
1061
|
+
raise RuntimeError("Can not add events when simulation is running.")
|
|
1062
|
+
|
|
1038
1063
|
self.__adapt_to_network_changes()
|
|
1039
1064
|
|
|
1040
1065
|
sensor_reading_attack.validate(self.__sensor_config)
|
|
@@ -1055,6 +1080,9 @@ class ScenarioSimulator():
|
|
|
1055
1080
|
event : :class:`~epyt_flow.simulation.events.sensor_reading_event.SensorReadingEvent`
|
|
1056
1081
|
Sensor reading event.
|
|
1057
1082
|
"""
|
|
1083
|
+
if self.__running_simulation is True:
|
|
1084
|
+
raise RuntimeError("Can not add events when simulation is running.")
|
|
1085
|
+
|
|
1058
1086
|
self.__adapt_to_network_changes()
|
|
1059
1087
|
|
|
1060
1088
|
event.validate(self.__sensor_config)
|
|
@@ -1134,11 +1162,21 @@ class ScenarioSimulator():
|
|
|
1134
1162
|
"""
|
|
1135
1163
|
self.set_sensors(SENSOR_TYPE_NODE_PRESSURE, sensor_locations)
|
|
1136
1164
|
|
|
1137
|
-
def place_pressure_sensors_everywhere(self) -> None:
|
|
1165
|
+
def place_pressure_sensors_everywhere(self, junctions_only: bool = False) -> None:
|
|
1138
1166
|
"""
|
|
1139
1167
|
Places a pressure sensor at every node in the network.
|
|
1168
|
+
|
|
1169
|
+
Parameters
|
|
1170
|
+
----------
|
|
1171
|
+
junctions_only : `bool`, optional
|
|
1172
|
+
If True, pressure sensors are only placed at junctions but not at tanks and reservoirs.
|
|
1173
|
+
|
|
1174
|
+
The default is False.
|
|
1140
1175
|
"""
|
|
1141
|
-
|
|
1176
|
+
if junctions_only is True:
|
|
1177
|
+
self.set_pressure_sensors(self.epanet_api.getNodeJunctionNameID())
|
|
1178
|
+
else:
|
|
1179
|
+
self.set_pressure_sensors(self.__sensor_config.nodes)
|
|
1142
1180
|
|
|
1143
1181
|
def set_flow_sensors(self, sensor_locations: list[str]) -> None:
|
|
1144
1182
|
"""
|
|
@@ -1442,6 +1480,9 @@ class ScenarioSimulator():
|
|
|
1442
1480
|
:class:`~epyt_flow.simulation.scada.scada_data.ScadaData`
|
|
1443
1481
|
Quality simulation results as SCADA data.
|
|
1444
1482
|
"""
|
|
1483
|
+
if self.__running_simulation is True:
|
|
1484
|
+
raise RuntimeError("A simulation is already running.")
|
|
1485
|
+
|
|
1445
1486
|
if self.__f_msx_in is None:
|
|
1446
1487
|
raise ValueError("No .msx file specified")
|
|
1447
1488
|
|
|
@@ -1506,6 +1547,9 @@ class ScenarioSimulator():
|
|
|
1506
1547
|
Generator containing the current EPANET-MSX simulation results as SCADA data
|
|
1507
1548
|
(i.e. species concentrations).
|
|
1508
1549
|
"""
|
|
1550
|
+
if self.__running_simulation is True:
|
|
1551
|
+
raise RuntimeError("A simulation is already running.")
|
|
1552
|
+
|
|
1509
1553
|
if self.__f_msx_in is None:
|
|
1510
1554
|
raise ValueError("No .msx file specified")
|
|
1511
1555
|
|
|
@@ -1522,6 +1566,8 @@ class ScenarioSimulator():
|
|
|
1522
1566
|
|
|
1523
1567
|
self.epanet_api.initializeMSXQualityAnalysis(ToolkitConstants.EN_NOSAVE)
|
|
1524
1568
|
|
|
1569
|
+
self.__running_simulation = True
|
|
1570
|
+
|
|
1525
1571
|
bulk_species_idx = self.epanet_api.getMSXSpeciesIndex(self.__sensor_config.bulk_species)
|
|
1526
1572
|
surface_species_idx = self.epanet_api.getMSXSpeciesIndex(
|
|
1527
1573
|
self.__sensor_config.surface_species)
|
|
@@ -1614,6 +1660,7 @@ class ScenarioSimulator():
|
|
|
1614
1660
|
|
|
1615
1661
|
# Run step-by-step simulation
|
|
1616
1662
|
tleft = 1
|
|
1663
|
+
total_time = 0
|
|
1617
1664
|
while tleft > 0:
|
|
1618
1665
|
if support_abort is True: # Can the simulation be aborted? If so, handle it.
|
|
1619
1666
|
abort = yield
|
|
@@ -1656,6 +1703,8 @@ class ScenarioSimulator():
|
|
|
1656
1703
|
sensor_noise=self.__sensor_noise,
|
|
1657
1704
|
frozen_sensor_config=frozen_sensor_config)
|
|
1658
1705
|
|
|
1706
|
+
self.__running_simulation = False
|
|
1707
|
+
|
|
1659
1708
|
def run_basic_quality_simulation(self, hyd_file_in: str, verbose: bool = False,
|
|
1660
1709
|
frozen_sensor_config: bool = False) -> ScadaData:
|
|
1661
1710
|
"""
|
|
@@ -1681,6 +1730,9 @@ class ScenarioSimulator():
|
|
|
1681
1730
|
:class:`~epyt_flow.simulation.scada.scada_data.ScadaData`
|
|
1682
1731
|
Quality simulation results as SCADA data.
|
|
1683
1732
|
"""
|
|
1733
|
+
if self.__running_simulation is True:
|
|
1734
|
+
raise RuntimeError("A simulation is already running.")
|
|
1735
|
+
|
|
1684
1736
|
result = None
|
|
1685
1737
|
|
|
1686
1738
|
# Run simulation step-by-step
|
|
@@ -1741,6 +1793,9 @@ class ScenarioSimulator():
|
|
|
1741
1793
|
:class:`~epyt_flow.simulation.scada.scada_data.ScadaData`
|
|
1742
1794
|
Generator with the current simulation results/states as SCADA data.
|
|
1743
1795
|
"""
|
|
1796
|
+
if self.__running_simulation is True:
|
|
1797
|
+
raise RuntimeError("A simulation is already running.")
|
|
1798
|
+
|
|
1744
1799
|
requested_time_step = self.epanet_api.getTimeHydraulicStep()
|
|
1745
1800
|
reporting_time_start = self.epanet_api.getTimeReportingStart()
|
|
1746
1801
|
reporting_time_step = self.epanet_api.getTimeReportingStep()
|
|
@@ -1834,6 +1889,9 @@ class ScenarioSimulator():
|
|
|
1834
1889
|
:class:`~epyt_flow.simulation.scada.scada_data.ScadaData`
|
|
1835
1890
|
Simulation results as SCADA data (i.e. sensor readings).
|
|
1836
1891
|
"""
|
|
1892
|
+
if self.__running_simulation is True:
|
|
1893
|
+
raise RuntimeError("A simulation is already running.")
|
|
1894
|
+
|
|
1837
1895
|
self.__adapt_to_network_changes()
|
|
1838
1896
|
|
|
1839
1897
|
result = None
|
|
@@ -1928,10 +1986,15 @@ class ScenarioSimulator():
|
|
|
1928
1986
|
Generator with the current simulation results/states as SCADA data
|
|
1929
1987
|
(i.e. sensor readings).
|
|
1930
1988
|
"""
|
|
1989
|
+
if self.__running_simulation is True:
|
|
1990
|
+
raise RuntimeError("A simulation is already running.")
|
|
1991
|
+
|
|
1931
1992
|
self.__adapt_to_network_changes()
|
|
1932
1993
|
|
|
1933
1994
|
self.__prepare_simulation()
|
|
1934
1995
|
|
|
1996
|
+
self.__running_simulation = True
|
|
1997
|
+
|
|
1935
1998
|
self.epanet_api.openHydraulicAnalysis()
|
|
1936
1999
|
self.epanet_api.openQualityAnalysis()
|
|
1937
2000
|
self.epanet_api.initializeHydraulicAnalysis(ToolkitConstants.EN_SAVE)
|
|
@@ -2041,9 +2104,12 @@ class ScenarioSimulator():
|
|
|
2041
2104
|
self.epanet_api.closeQualityAnalysis()
|
|
2042
2105
|
self.epanet_api.closeHydraulicAnalysis()
|
|
2043
2106
|
|
|
2107
|
+
self.__running_simulation = False
|
|
2108
|
+
|
|
2044
2109
|
if hyd_export is not None:
|
|
2045
2110
|
self.epanet_api.saveHydraulicFile(hyd_export)
|
|
2046
2111
|
except Exception as ex:
|
|
2112
|
+
self.__running_simulation = False
|
|
2047
2113
|
raise ex
|
|
2048
2114
|
|
|
2049
2115
|
def set_model_uncertainty(self, model_uncertainty: ModelUncertainty) -> None:
|
|
@@ -2055,6 +2121,9 @@ class ScenarioSimulator():
|
|
|
2055
2121
|
model_uncertainty : :class:`~epyt_flow.uncertainty.model_uncertainty.ModelUncertainty`
|
|
2056
2122
|
Model uncertainty specifications.
|
|
2057
2123
|
"""
|
|
2124
|
+
if self.__running_simulation is True:
|
|
2125
|
+
raise RuntimeError("Can not set uncertainties when simulation is running.")
|
|
2126
|
+
|
|
2058
2127
|
self.__adapt_to_network_changes()
|
|
2059
2128
|
|
|
2060
2129
|
if not isinstance(model_uncertainty, ModelUncertainty):
|
|
@@ -2073,6 +2142,9 @@ class ScenarioSimulator():
|
|
|
2073
2142
|
sensor_noise : :class:`~epyt_flow.uncertainties.sensor_noise.SensorNoise`
|
|
2074
2143
|
Sensor noise specification.
|
|
2075
2144
|
"""
|
|
2145
|
+
if self.__running_simulation is True:
|
|
2146
|
+
raise RuntimeError("Can not set sensor noise when simulation is running.")
|
|
2147
|
+
|
|
2076
2148
|
self.__adapt_to_network_changes()
|
|
2077
2149
|
|
|
2078
2150
|
if not isinstance(sensor_noise, SensorNoise):
|
|
@@ -2155,6 +2227,9 @@ class ScenarioSimulator():
|
|
|
2155
2227
|
|
|
2156
2228
|
The default is None.
|
|
2157
2229
|
"""
|
|
2230
|
+
if self.__running_simulation is True:
|
|
2231
|
+
raise RuntimeError("Can not change general parameters when simulation is running.")
|
|
2232
|
+
|
|
2158
2233
|
self.__adapt_to_network_changes()
|
|
2159
2234
|
|
|
2160
2235
|
if flow_units_id is not None:
|
|
@@ -2277,6 +2352,9 @@ class ScenarioSimulator():
|
|
|
2277
2352
|
Sets water age analysis -- i.e. estimates the water age (in hours) at
|
|
2278
2353
|
all places in the network.
|
|
2279
2354
|
"""
|
|
2355
|
+
if self.__running_simulation is True:
|
|
2356
|
+
raise RuntimeError("Can not change general parameters when simulation is running.")
|
|
2357
|
+
|
|
2280
2358
|
self.__adapt_to_network_changes()
|
|
2281
2359
|
|
|
2282
2360
|
self.__warn_if_quality_set()
|
|
@@ -2305,6 +2383,9 @@ class ScenarioSimulator():
|
|
|
2305
2383
|
|
|
2306
2384
|
The default is MASS_UNIT_MG.
|
|
2307
2385
|
"""
|
|
2386
|
+
if self.__running_simulation is True:
|
|
2387
|
+
raise RuntimeError("Can not change general parameters when simulation is running.")
|
|
2388
|
+
|
|
2308
2389
|
self.__adapt_to_network_changes()
|
|
2309
2390
|
|
|
2310
2391
|
self.__warn_if_quality_set()
|
|
@@ -2349,6 +2430,9 @@ class ScenarioSimulator():
|
|
|
2349
2430
|
|
|
2350
2431
|
The default is 1.
|
|
2351
2432
|
"""
|
|
2433
|
+
if self.__running_simulation is True:
|
|
2434
|
+
raise RuntimeError("Can not change general parameters when simulation is running.")
|
|
2435
|
+
|
|
2352
2436
|
self.__adapt_to_network_changes()
|
|
2353
2437
|
|
|
2354
2438
|
if self.epanet_api.getQualityInfo().QualityCode != ToolkitConstants.EN_CHEM:
|
|
@@ -2385,6 +2469,9 @@ class ScenarioSimulator():
|
|
|
2385
2469
|
trace_node_id : `str`
|
|
2386
2470
|
ID of the node traced in the source tracing analysis.
|
|
2387
2471
|
"""
|
|
2472
|
+
if self.__running_simulation is True:
|
|
2473
|
+
raise RuntimeError("Can not change general parameters when simulation is running.")
|
|
2474
|
+
|
|
2388
2475
|
self.__adapt_to_network_changes()
|
|
2389
2476
|
|
|
2390
2477
|
if trace_node_id not in self.__sensor_config.nodes:
|
|
@@ -284,7 +284,7 @@ def is_flowunit_simetric(unit_id: int) -> bool:
|
|
|
284
284
|
`bool`
|
|
285
285
|
True if the fiven unit is a SI metric unit, False otherwise.
|
|
286
286
|
"""
|
|
287
|
-
return unit_id in [ToolkitConstants.EN_LPM, ToolkitConstants.EN_MLD,
|
|
287
|
+
return unit_id in [ToolkitConstants.EN_LPS, ToolkitConstants.EN_LPM, ToolkitConstants.EN_MLD,
|
|
288
288
|
ToolkitConstants.EN_CMH, ToolkitConstants.EN_CMD]
|
|
289
289
|
|
|
290
290
|
|
|
@@ -507,11 +507,11 @@ class NetworkTopology(nx.Graph, JsonSerializable):
|
|
|
507
507
|
|
|
508
508
|
return super().__eq__(other) and \
|
|
509
509
|
self.get_all_nodes() == other.get_all_nodes() \
|
|
510
|
-
and all(link_a[0] == link_b[0] and
|
|
510
|
+
and all(link_a[0] == link_b[0] and link_a[1] == link_b[1]
|
|
511
511
|
for link_a, link_b in zip(self.get_all_links(), other.get_all_links())) \
|
|
512
512
|
and self.__units == other.units \
|
|
513
|
-
and self.
|
|
514
|
-
and self.
|
|
513
|
+
and self.get_all_pumps() == other.get_all_pumps() \
|
|
514
|
+
and self.get_all_valves() == other.get_all_valves()
|
|
515
515
|
|
|
516
516
|
def __str__(self) -> str:
|
|
517
517
|
return f"f_inp: {self.name} nodes: {self.__nodes} links: {self.__links} " +\
|
|
@@ -334,8 +334,16 @@ class PercentageDeviationUncertainty(UniformUncertainty, JsonSerializable):
|
|
|
334
334
|
if not 0 < deviation_percentage < 1:
|
|
335
335
|
raise ValueError("'deviation_percentage' must be in (0,1)")
|
|
336
336
|
|
|
337
|
+
if "low" in kwds:
|
|
338
|
+
del kwds["low"]
|
|
339
|
+
if "high" in kwds:
|
|
340
|
+
del kwds["high"]
|
|
341
|
+
|
|
337
342
|
super().__init__(low=1. - deviation_percentage, high=1. + deviation_percentage, **kwds)
|
|
338
343
|
|
|
344
|
+
def get_attributes(self) -> dict:
|
|
345
|
+
return super().get_attributes() | {"deviation_percentage": self.high - 1.}
|
|
346
|
+
|
|
339
347
|
def apply(self, data: float) -> float:
|
|
340
348
|
data *= np.random.uniform(low=self.low, high=self.high)
|
|
341
349
|
|