epyt-flow 0.5.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.5.0 → epyt_flow-0.7.0}/PKG-INFO +49 -6
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/README.md +47 -4
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/REQUIREMENTS.txt +1 -1
- epyt_flow-0.7.0/epyt_flow/VERSION +1 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/data/benchmarks/battledim.py +6 -2
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/data/benchmarks/leakdb.py +11 -9
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/data/networks.py +1 -2
- epyt_flow-0.7.0/epyt_flow/gym/scenario_control_env.py +196 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/metrics.py +66 -4
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/serialization.py +46 -3
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/simulation/scada/advanced_control.py +1 -1
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/simulation/scada/scada_data.py +160 -62
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/simulation/scenario_config.py +6 -2
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/simulation/scenario_simulator.py +362 -65
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/simulation/sensor_config.py +186 -15
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/topology.py +93 -5
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/uncertainty/uncertainties.py +8 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/utils.py +69 -2
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow.egg-info/PKG-INFO +49 -6
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow.egg-info/requires.txt +1 -1
- epyt_flow-0.7.0/epyt_flow.egg-info/top_level.txt +6 -0
- epyt_flow-0.5.0/epyt_flow/VERSION +0 -1
- epyt_flow-0.5.0/epyt_flow/gym/scenario_control_env.py +0 -101
- epyt_flow-0.5.0/epyt_flow.egg-info/top_level.txt +0 -12
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/CITATION.cff +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/CODE_OF_CONDUCT.md +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/LICENSE +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/MANIFEST.in +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/AUTHORS +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/LICENSE +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/Readme_SRC_Engines.txt +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/enumstxt.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/epanet.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/epanet2.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/epanet2.def +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/errors.dat +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/funcs.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/genmmd.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/hash.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/hash.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydcoeffs.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydraul.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydsolver.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydstatus.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_2.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_enums.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/inpfile.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/input1.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/input2.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/input3.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/main.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/mempool.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/mempool.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/output.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/project.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/quality.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/qualreact.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/qualroute.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/report.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/rules.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/smatrix.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/text.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET/SRC_engines/types.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/MSX_Updates.txt +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/dispersion.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/hash.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/hash.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx_export.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/mempool.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/mempool.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxchem.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxcompiler.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxdict.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxdispersion.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxerr.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxfile.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxinp.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxout.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxproj.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxqual.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxrpt.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxtank.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxtoolkit.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxtypes.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxutils.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxutils.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/newton.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/newton.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/rk5.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/rk5.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/ros2.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/ros2.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/smatrix.c +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/Src/smatrix.h +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/EPANET-MSX/readme.txt +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/compile_linux.sh +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/EPANET/compile_macos.sh +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/__init__.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/data/__init__.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/data/benchmarks/__init__.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/data/benchmarks/batadal.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/data/benchmarks/batadal_data.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/data/benchmarks/battledim_data.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/data/benchmarks/gecco_water_quality.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/data/benchmarks/leakdb_data.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/data/benchmarks/water_usage.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/gym/__init__.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/gym/control_gyms.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/models/__init__.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/models/event_detector.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/models/sensor_interpolation_detector.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/__init__.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/base_handler.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/res_manager.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/scada_data/__init__.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/scada_data/data_handlers.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/scada_data/export_handlers.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/scada_data/handlers.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/scenario/__init__.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/scenario/event_handlers.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/scenario/handlers.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/scenario/simulation_handlers.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/scenario/uncertainty_handlers.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/rest_api/server.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/simulation/__init__.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/simulation/events/__init__.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/simulation/events/actuator_events.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/simulation/events/event.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/simulation/events/leakages.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/simulation/events/sensor_faults.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/simulation/events/sensor_reading_attack.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/simulation/events/sensor_reading_event.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/simulation/events/system_event.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/simulation/parallel_simulation.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/simulation/scada/__init__.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/simulation/scada/scada_data_export.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/simulation/scenario_visualizer.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/uncertainty/__init__.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/uncertainty/model_uncertainty.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/uncertainty/sensor_noise.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow/uncertainty/utils.py +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow.egg-info/SOURCES.txt +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/epyt_flow.egg-info/dependency_links.txt +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/pyproject.toml +0 -0
- {epyt_flow-0.5.0 → epyt_flow-0.7.0}/setup.cfg +0 -0
- {epyt_flow-0.5.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
|
|
@@ -458,10 +458,14 @@ def load_scenario(return_test_scenario: bool, download_dir: str = None,
|
|
|
458
458
|
ltown_config = load_ltown(use_realistic_demands=True, include_default_sensor_placement=True,
|
|
459
459
|
verbose=verbose)
|
|
460
460
|
|
|
461
|
-
# Set simulation duration
|
|
461
|
+
# Set simulation duration and other general parameters such as the demand model
|
|
462
462
|
general_params = {"simulation_duration": to_seconds(days=365), # One year
|
|
463
463
|
"hydraulic_time_step": to_seconds(minutes=5), # 5min time steps
|
|
464
|
-
"reporting_time_step": to_seconds(minutes=5)
|
|
464
|
+
"reporting_time_step": to_seconds(minutes=5),
|
|
465
|
+
"demand_model": {"type": "PDA", "pressure_min": 0,
|
|
466
|
+
"pressure_required": 0.1,
|
|
467
|
+
"pressure_exponent": 0.5}
|
|
468
|
+
} | ltown_config.general_params
|
|
465
469
|
|
|
466
470
|
# Add events
|
|
467
471
|
start_time = START_TIME_TEST if return_test_scenario is True else START_TIME_TRAIN
|
|
@@ -30,7 +30,7 @@ from .leakdb_data import NET1_LEAKAGES, HANOI_LEAKAGES
|
|
|
30
30
|
from ...utils import get_temp_folder, to_seconds, unpack_zip_archive, create_path_if_not_exist, \
|
|
31
31
|
download_if_necessary
|
|
32
32
|
from ...metrics import f1_score, true_positive_rate, true_negative_rate
|
|
33
|
-
from ...simulation import ScenarioSimulator
|
|
33
|
+
from ...simulation import ScenarioSimulator, ToolkitConstants
|
|
34
34
|
from ...simulation.events import AbruptLeakage, IncipientLeakage
|
|
35
35
|
from ...simulation import ScenarioConfig
|
|
36
36
|
from ...simulation.scada import ScadaData
|
|
@@ -424,14 +424,19 @@ def load_scenarios(scenarios_id: list[int], use_net1: bool = True,
|
|
|
424
424
|
download_dir = download_dir if download_dir is not None else get_temp_folder()
|
|
425
425
|
network_config = load_network(download_dir)
|
|
426
426
|
|
|
427
|
-
# Set simulation duration
|
|
427
|
+
# Set simulation duration and other general parameters such as the demand model and flow units
|
|
428
428
|
hydraulic_time_step = to_seconds(minutes=30) # 30min time steps
|
|
429
429
|
general_params = {"simulation_duration": to_seconds(days=365), # One year
|
|
430
430
|
"hydraulic_time_step": hydraulic_time_step,
|
|
431
|
-
"reporting_time_step": hydraulic_time_step
|
|
431
|
+
"reporting_time_step": hydraulic_time_step,
|
|
432
|
+
"flow_units_id": ToolkitConstants.EN_CMH,
|
|
433
|
+
"demand_model": {"type": "PDA", "pressure_min": 0,
|
|
434
|
+
"pressure_required": 0.1,
|
|
435
|
+
"pressure_exponent": 0.5}
|
|
436
|
+
} | network_config.general_params
|
|
432
437
|
|
|
433
438
|
# Add demand patterns
|
|
434
|
-
def gen_dem(download_dir
|
|
439
|
+
def gen_dem(download_dir):
|
|
435
440
|
# Taken from https://github.com/KIOS-Research/LeakDB/blob/master/CCWI-WDSA2018/Dataset_Generator_Py3/demandGenerator.py
|
|
436
441
|
week_pat = scipy.io.loadmat(os.path.join(download_dir, "weekPat_30min.mat"))
|
|
437
442
|
a_w = week_pat['Aw']
|
|
@@ -503,10 +508,7 @@ def load_scenarios(scenarios_id: list[int], use_net1: bool = True,
|
|
|
503
508
|
|
|
504
509
|
if not os.path.exists(f_inp_in):
|
|
505
510
|
with ScenarioSimulator(f_inp_in=network_config.f_inp_in) as wdn:
|
|
506
|
-
wdn.
|
|
507
|
-
wdn.epanet_api.setTimeSimulationDuration(general_params["simulation_duration"])
|
|
508
|
-
wdn.epanet_api.setTimePatternStep(general_params["hydraulic_time_step"])
|
|
509
|
-
wdn.epanet_api.setFlowUnitsCMH()
|
|
511
|
+
wdn.set_general_parameters(**general_params)
|
|
510
512
|
|
|
511
513
|
wdn.epanet_api.deletePatternsAll()
|
|
512
514
|
|
|
@@ -519,7 +521,7 @@ def load_scenarios(scenarios_id: list[int], use_net1: bool = True,
|
|
|
519
521
|
node_idx = wdn.epanet_api.getNodeIndex(node_id)
|
|
520
522
|
base_demand = wdn.epanet_api.getNodeBaseDemands(node_idx)[1][0]
|
|
521
523
|
|
|
522
|
-
my_demand_pattern = np.array(gen_dem(download_dir
|
|
524
|
+
my_demand_pattern = np.array(gen_dem(download_dir))
|
|
523
525
|
|
|
524
526
|
wdn.set_node_demand_pattern(node_id=node_id, base_demand=base_demand,
|
|
525
527
|
demand_pattern_id=f"demand_{node_id}",
|
|
@@ -56,8 +56,7 @@ def get_default_hydraulic_options(flow_units_id: int = None) -> dict:
|
|
|
56
56
|
Dictionary with default hydraulics options that can be passed to
|
|
57
57
|
:func:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator.set_general_parameters`.
|
|
58
58
|
"""
|
|
59
|
-
params = {
|
|
60
|
-
"pressure_exponent": 0.5}}
|
|
59
|
+
params = {}
|
|
61
60
|
if flow_units_id is not None:
|
|
62
61
|
params |= {"flow_units_id": flow_units_id}
|
|
63
62
|
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module provides a base class for control environments.
|
|
3
|
+
"""
|
|
4
|
+
from abc import abstractmethod, ABC
|
|
5
|
+
from typing import Union
|
|
6
|
+
import warnings
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from ..simulation import ScenarioSimulator, ScenarioConfig, ScadaData
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ScenarioControlEnv(ABC):
|
|
13
|
+
"""
|
|
14
|
+
Base class for a control environment challenge.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
scenario_config : :class:`~epyt_flow.simulation.scenario_config.ScenarioConfig`
|
|
19
|
+
Scenario configuration.
|
|
20
|
+
autoreset : `bool`, optional
|
|
21
|
+
If True, environment is automatically reset if terminated.
|
|
22
|
+
|
|
23
|
+
The default is False.
|
|
24
|
+
"""
|
|
25
|
+
def __init__(self, scenario_config: ScenarioConfig, autoreset: bool = False, **kwds):
|
|
26
|
+
self.__scenario_config = scenario_config
|
|
27
|
+
self._scenario_sim = None
|
|
28
|
+
self._sim_generator = None
|
|
29
|
+
self.__autoreset = autoreset
|
|
30
|
+
|
|
31
|
+
super().__init__(**kwds)
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def autoreset(self) -> bool:
|
|
35
|
+
"""
|
|
36
|
+
True, if environment automatically resets after it terminated.
|
|
37
|
+
|
|
38
|
+
Returns
|
|
39
|
+
-------
|
|
40
|
+
`bool`
|
|
41
|
+
True, if environment automatically resets after it terminated.
|
|
42
|
+
"""
|
|
43
|
+
return self.__autoreset
|
|
44
|
+
|
|
45
|
+
def __enter__(self):
|
|
46
|
+
return self
|
|
47
|
+
|
|
48
|
+
def __exit__(self, *args):
|
|
49
|
+
self.close()
|
|
50
|
+
|
|
51
|
+
def close(self) -> None:
|
|
52
|
+
"""
|
|
53
|
+
Frees all resources.
|
|
54
|
+
"""
|
|
55
|
+
try:
|
|
56
|
+
if self._sim_generator is not None:
|
|
57
|
+
self._sim_generator.send(True)
|
|
58
|
+
next(self._sim_generator)
|
|
59
|
+
except StopIteration:
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
if self._scenario_sim is not None:
|
|
63
|
+
self._scenario_sim.close()
|
|
64
|
+
|
|
65
|
+
def reset(self) -> ScadaData:
|
|
66
|
+
"""
|
|
67
|
+
Resets the environment (i.e. simulation).
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
:class:`~epyt_flow.simulation.scada.scada_data.ScadaData`
|
|
72
|
+
Current SCADA data (i.e. sensor readings).
|
|
73
|
+
"""
|
|
74
|
+
if self._scenario_sim is not None:
|
|
75
|
+
self._scenario_sim.close()
|
|
76
|
+
|
|
77
|
+
self._scenario_sim = ScenarioSimulator(
|
|
78
|
+
scenario_config=self.__scenario_config)
|
|
79
|
+
self._sim_generator = self._scenario_sim.run_simulation_as_generator(support_abort=True)
|
|
80
|
+
|
|
81
|
+
return self._next_sim_itr()
|
|
82
|
+
|
|
83
|
+
def _next_sim_itr(self) -> ScadaData:
|
|
84
|
+
try:
|
|
85
|
+
next(self._sim_generator)
|
|
86
|
+
r = self._sim_generator.send(False)
|
|
87
|
+
|
|
88
|
+
if self.autoreset is True:
|
|
89
|
+
return r
|
|
90
|
+
else:
|
|
91
|
+
return r, False
|
|
92
|
+
except StopIteration:
|
|
93
|
+
if self.__autoreset is True:
|
|
94
|
+
return self.reset()
|
|
95
|
+
else:
|
|
96
|
+
return None, True
|
|
97
|
+
|
|
98
|
+
def set_pump_status(self, pump_id: str, status: int) -> None:
|
|
99
|
+
"""
|
|
100
|
+
Sets the status of a pump.
|
|
101
|
+
|
|
102
|
+
Parameters
|
|
103
|
+
----------
|
|
104
|
+
pump_id : `str`
|
|
105
|
+
ID of the pump for which the status is set.
|
|
106
|
+
status : `int`
|
|
107
|
+
New status of the pump -- either active (i.e. open) or inactive (i.e. closed).
|
|
108
|
+
|
|
109
|
+
Must be one of the following constants defined in
|
|
110
|
+
:class:`~epyt_flow.simulation.events.actuator_events.ActuatorConstants`:
|
|
111
|
+
|
|
112
|
+
- EN_CLOSED = 0
|
|
113
|
+
- EN_OPEN = 1
|
|
114
|
+
"""
|
|
115
|
+
pump_idx = self._scenario_sim.epanet_api.getLinkPumpNameID().index(pump_id)
|
|
116
|
+
pump_link_idx = self._scenario_sim.epanet_api.getLinkPumpIndex(pump_idx + 1)
|
|
117
|
+
self._scenario_sim.epanet_api.setLinkStatus(pump_link_idx, status)
|
|
118
|
+
|
|
119
|
+
def set_pump_speed(self, pump_id: str, speed: float) -> None:
|
|
120
|
+
"""
|
|
121
|
+
Sets the speed of a pump.
|
|
122
|
+
|
|
123
|
+
Parameters
|
|
124
|
+
----------
|
|
125
|
+
pump_id : `str`
|
|
126
|
+
ID of the pump for which the pump speed is set.
|
|
127
|
+
speed : `float`
|
|
128
|
+
New pump speed.
|
|
129
|
+
"""
|
|
130
|
+
pump_idx = self._scenario_sim.epanet_api.getLinkPumpNameID().index(pump_id)
|
|
131
|
+
pattern_idx = self._scenario_sim.epanet_api.getLinkPumpPatternIndex(pump_idx + 1)
|
|
132
|
+
|
|
133
|
+
if pattern_idx == 0:
|
|
134
|
+
warnings.warn(f"No pattern for pump '{pump_id}' found -- a new pattern is created")
|
|
135
|
+
pattern_idx = self._scenario_sim.epanet_api.addPattern(f"pump_speed_{pump_id}")
|
|
136
|
+
self._scenario_sim.epanet_api.setLinkPumpPatternIndex(pattern_idx)
|
|
137
|
+
|
|
138
|
+
self._scenario_sim.epanet_api.setPattern(pattern_idx, np.array([speed]))
|
|
139
|
+
|
|
140
|
+
def set_valve_status(self, valve_id: str, status: int) -> None:
|
|
141
|
+
"""
|
|
142
|
+
Sets the status of a valve.
|
|
143
|
+
|
|
144
|
+
Parameters
|
|
145
|
+
----------
|
|
146
|
+
valve_id : `str`
|
|
147
|
+
ID of the valve for which the status is set.
|
|
148
|
+
status : `int`
|
|
149
|
+
New status of the valve -- either open or closed.
|
|
150
|
+
|
|
151
|
+
Must be one of the following constants defined in
|
|
152
|
+
:class:`~epyt_flow.simulation.events.actuator_events.ActuatorConstants`:
|
|
153
|
+
|
|
154
|
+
- EN_CLOSED = 0
|
|
155
|
+
- EN_OPEN = 1
|
|
156
|
+
"""
|
|
157
|
+
valve_idx = self._scenario_sim.epanet_api.getLinkValveNameID().index(valve_id)
|
|
158
|
+
valve_link_idx = self._scenario_sim.epanet_api.getLinkValveIndex()[valve_idx]
|
|
159
|
+
self._scenario_sim.epanet_api.setLinkStatus(valve_link_idx, status)
|
|
160
|
+
|
|
161
|
+
def set_node_quality_source_value(self, node_id: str, pattern_id: str,
|
|
162
|
+
qual_value: float) -> None:
|
|
163
|
+
"""
|
|
164
|
+
Sets the quality source at a particular node to a specific value -- e.g.
|
|
165
|
+
setting the chlorine concentration injection to a specified value.
|
|
166
|
+
|
|
167
|
+
Parameters
|
|
168
|
+
----------
|
|
169
|
+
node_id : `str`
|
|
170
|
+
ID of the node.
|
|
171
|
+
pattern_id : `str`
|
|
172
|
+
ID of the quality pattern at the specific node.
|
|
173
|
+
qual_value : `float`
|
|
174
|
+
New quality source value.
|
|
175
|
+
"""
|
|
176
|
+
node_idx = self._scenario_sim.epanet_api.getNodeIndex(node_id)
|
|
177
|
+
pattern_idx = self._scenario_sim.epanet_api.getPatternIndex(pattern_id)
|
|
178
|
+
self._scenario_sim.epanet_api.setNodeSourceQuality(node_idx, 1)
|
|
179
|
+
self._scenario_sim.epanet_api.setPattern(pattern_idx, np.array([qual_value]))
|
|
180
|
+
|
|
181
|
+
@abstractmethod
|
|
182
|
+
def step(self, *actions) -> Union[tuple[ScadaData, float, bool], tuple[ScadaData, float]]:
|
|
183
|
+
"""
|
|
184
|
+
Performs the next step by applying an action and observing
|
|
185
|
+
the consequences (SCADA data, reward, terminated).
|
|
186
|
+
|
|
187
|
+
Note that `terminated` is only returned if `autoreset=False` otherwise
|
|
188
|
+
only the current SCADA data and reward are returned.
|
|
189
|
+
|
|
190
|
+
Returns
|
|
191
|
+
-------
|
|
192
|
+
`(` :class:`~epyt_flow.simulation.scada.scada_data.ScadaData` `, float, bool)` or `(` :class:`~epyt_flow.simulation.scada.scada_data.ScadaData` `, float)`
|
|
193
|
+
Triple or tuple of observations (:class:`~epyt_flow.simulation.scada.scada_data.ScadaData`),
|
|
194
|
+
reward (`float`), and terminated (`bool`).
|
|
195
|
+
"""
|
|
196
|
+
raise NotImplementedError()
|
|
@@ -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
|
|
|
@@ -4,6 +4,7 @@ Module provides functions and classes for serialization.
|
|
|
4
4
|
from typing import Any, Union
|
|
5
5
|
from abc import abstractmethod, ABC
|
|
6
6
|
from io import BufferedIOBase
|
|
7
|
+
import pathlib
|
|
7
8
|
import importlib
|
|
8
9
|
import json
|
|
9
10
|
import gzip
|
|
@@ -97,7 +98,9 @@ class Serializable(ABC):
|
|
|
97
98
|
Base class for a serializable class -- must be used in conjunction with the
|
|
98
99
|
:func:`~epyt_flow.serialization.serializable` decorator.
|
|
99
100
|
"""
|
|
100
|
-
def __init__(self, **kwds):
|
|
101
|
+
def __init__(self, _parent_path: str = "", **kwds):
|
|
102
|
+
self._parent_path = _parent_path
|
|
103
|
+
|
|
101
104
|
super().__init__(**kwds)
|
|
102
105
|
|
|
103
106
|
@abstractmethod
|
|
@@ -306,6 +309,39 @@ class JsonSerializable(Serializable):
|
|
|
306
309
|
"""
|
|
307
310
|
return my_load_from_json(data)
|
|
308
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
|
+
|
|
309
345
|
|
|
310
346
|
def load(data: Union[bytes, BufferedIOBase]) -> Any:
|
|
311
347
|
"""
|
|
@@ -376,12 +412,19 @@ def load_from_file(f_in: str, use_compression: bool = True) -> Any:
|
|
|
376
412
|
`Any`
|
|
377
413
|
Deserialized data.
|
|
378
414
|
"""
|
|
415
|
+
inst = None
|
|
416
|
+
|
|
379
417
|
if use_compression is False:
|
|
380
418
|
with open(f_in, "rb") as f:
|
|
381
|
-
|
|
419
|
+
inst = load(f.read())
|
|
382
420
|
else:
|
|
383
421
|
with gzip.open(f_in, "rb") as f:
|
|
384
|
-
|
|
422
|
+
inst = load(f.read())
|
|
423
|
+
|
|
424
|
+
if isinstance(inst, Serializable):
|
|
425
|
+
inst._parent_path = pathlib.Path(f_in).parent.resolve()
|
|
426
|
+
|
|
427
|
+
return inst
|
|
385
428
|
|
|
386
429
|
|
|
387
430
|
def save_to_file(f_out: str, data: Any, use_compression: bool = True) -> None:
|
|
@@ -98,7 +98,7 @@ class AdvancedControlModule(ABC):
|
|
|
98
98
|
- EN_OPEN = 1
|
|
99
99
|
"""
|
|
100
100
|
valve_idx = self._epanet_api.getLinkValveNameID().index(valve_id)
|
|
101
|
-
valve_link_idx = self._epanet_api.getLinkValveIndex(valve_idx
|
|
101
|
+
valve_link_idx = self._epanet_api.getLinkValveIndex()[valve_idx]
|
|
102
102
|
self._epanet_api.setLinkStatus(valve_link_idx, status)
|
|
103
103
|
|
|
104
104
|
def set_node_quality_source_value(self, node_id: str, pattern_id: str,
|