epyt-flow 0.7.2__tar.gz → 0.8.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.8.0/CITATION.cff +61 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/PKG-INFO +20 -19
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/README.md +17 -18
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/REQUIREMENTS.txt +2 -0
- epyt_flow-0.8.0/epyt_flow/VERSION +1 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/gym/scenario_control_env.py +129 -10
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/serialization.py +10 -3
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/events/__init__.py +1 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/events/leakages.py +55 -12
- epyt_flow-0.8.0/epyt_flow/simulation/events/quality_events.py +194 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/events/system_event.py +5 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/scada/scada_data.py +512 -64
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/scada/scada_data_export.py +7 -5
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/scenario_config.py +13 -2
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/scenario_simulator.py +275 -187
- epyt_flow-0.8.0/epyt_flow/simulation/scenario_visualizer.py +1307 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/sensor_config.py +21 -10
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow.egg-info/PKG-INFO +20 -19
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow.egg-info/SOURCES.txt +1 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow.egg-info/requires.txt +2 -0
- epyt_flow-0.7.2/CITATION.cff +0 -37
- epyt_flow-0.7.2/epyt_flow/VERSION +0 -1
- epyt_flow-0.7.2/epyt_flow/simulation/scenario_visualizer.py +0 -61
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/CODE_OF_CONDUCT.md +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/LICENSE +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/MANIFEST.in +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/AUTHORS +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/LICENSE +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/Readme_SRC_Engines.txt +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/enumstxt.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/epanet.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/epanet2.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/epanet2.def +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/errors.dat +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/funcs.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/genmmd.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/hash.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/hash.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydcoeffs.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydraul.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydsolver.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydstatus.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_2.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_enums.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/inpfile.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/input1.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/input2.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/input3.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/main.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/mempool.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/mempool.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/output.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/project.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/quality.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/qualreact.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/qualroute.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/report.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/rules.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/smatrix.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/text.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/types.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/MSX_Updates.txt +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/dispersion.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/hash.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/hash.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx_export.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/mempool.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/mempool.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxchem.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxcompiler.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxdict.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxdispersion.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxerr.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxfile.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxinp.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxout.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxproj.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxqual.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxrpt.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxtank.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxtoolkit.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxtypes.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxutils.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxutils.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/newton.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/newton.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/rk5.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/rk5.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/ros2.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/ros2.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/smatrix.c +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/smatrix.h +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/readme.txt +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/compile_linux.sh +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/compile_macos.sh +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/__init__.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/__init__.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/benchmarks/__init__.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/benchmarks/batadal.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/benchmarks/batadal_data.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/benchmarks/battledim.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/benchmarks/battledim_data.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/benchmarks/gecco_water_quality.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/benchmarks/leakdb.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/benchmarks/leakdb_data.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/benchmarks/water_usage.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/networks.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/gym/__init__.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/gym/control_gyms.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/metrics.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/models/__init__.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/models/event_detector.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/models/sensor_interpolation_detector.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/__init__.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/base_handler.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/res_manager.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/scada_data/__init__.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/scada_data/data_handlers.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/scada_data/export_handlers.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/scada_data/handlers.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/scenario/__init__.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/scenario/event_handlers.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/scenario/handlers.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/scenario/simulation_handlers.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/scenario/uncertainty_handlers.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/server.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/__init__.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/events/actuator_events.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/events/event.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/events/sensor_faults.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/events/sensor_reading_attack.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/events/sensor_reading_event.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/parallel_simulation.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/scada/__init__.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/scada/advanced_control.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/topology.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/uncertainty/__init__.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/uncertainty/model_uncertainty.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/uncertainty/sensor_noise.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/uncertainty/uncertainties.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/uncertainty/utils.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/utils.py +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow.egg-info/dependency_links.txt +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow.egg-info/top_level.txt +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/pyproject.toml +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/setup.cfg +0 -0
- {epyt_flow-0.7.2 → epyt_flow-0.8.0}/setup.py +0 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
cff-version: "1.2.0"
|
|
2
|
+
authors:
|
|
3
|
+
- family-names: Artelt
|
|
4
|
+
given-names: André
|
|
5
|
+
orcid: "https://orcid.org/0000-0002-2426-3126"
|
|
6
|
+
- family-names: Kyriakou
|
|
7
|
+
given-names: Marios S.
|
|
8
|
+
orcid: "https://orcid.org/0000-0002-2324-8661"
|
|
9
|
+
- family-names: Vrachimis
|
|
10
|
+
given-names: Stelios G.
|
|
11
|
+
orcid: "https://orcid.org/0000-0001-8862-5205"
|
|
12
|
+
- family-names: Eliades
|
|
13
|
+
given-names: Demetrios G.
|
|
14
|
+
orcid: "https://orcid.org/0000-0001-6184-6366"
|
|
15
|
+
- family-names: Hammer
|
|
16
|
+
given-names: Barbara
|
|
17
|
+
orcid: "https://orcid.org/0000-0002-0935-5591"
|
|
18
|
+
- family-names: Polycarpou
|
|
19
|
+
given-names: Marios M.
|
|
20
|
+
orcid: "https://orcid.org/0000-0001-6495-9171"
|
|
21
|
+
contact:
|
|
22
|
+
- family-names: Artelt
|
|
23
|
+
given-names: André
|
|
24
|
+
orcid: "https://orcid.org/0000-0002-2426-3126"
|
|
25
|
+
message: If you use this software, please cite our article in the
|
|
26
|
+
Journal of Open Source Software.
|
|
27
|
+
preferred-citation:
|
|
28
|
+
authors:
|
|
29
|
+
- family-names: Artelt
|
|
30
|
+
given-names: André
|
|
31
|
+
orcid: "https://orcid.org/0000-0002-2426-3126"
|
|
32
|
+
- family-names: Kyriakou
|
|
33
|
+
given-names: Marios S.
|
|
34
|
+
orcid: "https://orcid.org/0000-0002-2324-8661"
|
|
35
|
+
- family-names: Vrachimis
|
|
36
|
+
given-names: Stelios G.
|
|
37
|
+
orcid: "https://orcid.org/0000-0001-8862-5205"
|
|
38
|
+
- family-names: Eliades
|
|
39
|
+
given-names: Demetrios G.
|
|
40
|
+
orcid: "https://orcid.org/0000-0001-6184-6366"
|
|
41
|
+
- family-names: Hammer
|
|
42
|
+
given-names: Barbara
|
|
43
|
+
orcid: "https://orcid.org/0000-0002-0935-5591"
|
|
44
|
+
- family-names: Polycarpou
|
|
45
|
+
given-names: Marios M.
|
|
46
|
+
orcid: "https://orcid.org/0000-0001-6495-9171"
|
|
47
|
+
date-published: 2024-11-12
|
|
48
|
+
doi: 10.21105/joss.07104
|
|
49
|
+
issn: 2475-9066
|
|
50
|
+
issue: 103
|
|
51
|
+
journal: Journal of Open Source Software
|
|
52
|
+
publisher:
|
|
53
|
+
name: Open Journals
|
|
54
|
+
start: 7104
|
|
55
|
+
title: "EPyT-Flow: A Toolkit for Generating Water Distribution Network
|
|
56
|
+
Data"
|
|
57
|
+
type: article
|
|
58
|
+
url: "https://joss.theoj.org/papers/10.21105/joss.07104"
|
|
59
|
+
volume: 9
|
|
60
|
+
title: "EPyT-Flow: A Toolkit for Generating Water Distribution Network
|
|
61
|
+
Data"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: epyt-flow
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.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
|
|
@@ -31,6 +31,8 @@ Requires-Dist: openpyxl>=3.1.2
|
|
|
31
31
|
Requires-Dist: falcon>=3.1.3
|
|
32
32
|
Requires-Dist: multiprocess>=0.70.16
|
|
33
33
|
Requires-Dist: geopandas>=0.14.4
|
|
34
|
+
Requires-Dist: svgpath2mpl>=1.0.0
|
|
35
|
+
Requires-Dist: Deprecated>=1.2.14
|
|
34
36
|
Requires-Dist: psutil
|
|
35
37
|
|
|
36
38
|
[](https://pypi.org/project/epyt-flow/)
|
|
@@ -40,6 +42,7 @@ Requires-Dist: psutil
|
|
|
40
42
|
[](https://epyt-flow.readthedocs.io/en/stable/?badge=stable)
|
|
41
43
|
[](https://pepy.tech/project/epyt-flow)
|
|
42
44
|
[](https://pepy.tech/project/epyt-flow)
|
|
45
|
+
[](https://doi.org/10.21105/joss.07104)
|
|
43
46
|
|
|
44
47
|
# EPyT-Flow -- EPANET Python Toolkit - Flow
|
|
45
48
|
|
|
@@ -121,7 +124,7 @@ pip install .
|
|
|
121
124
|
```python
|
|
122
125
|
from epyt_flow.data.benchmarks import load_leakdb_scenarios
|
|
123
126
|
from epyt_flow.simulation import ScenarioSimulator
|
|
124
|
-
from epyt_flow.utils import to_seconds
|
|
127
|
+
from epyt_flow.utils import to_seconds
|
|
125
128
|
|
|
126
129
|
|
|
127
130
|
if __name__ == "__main__":
|
|
@@ -144,16 +147,10 @@ if __name__ == "__main__":
|
|
|
144
147
|
|
|
145
148
|
# Print & plot sensor readings over the entire simulation
|
|
146
149
|
print(f"Pressure readings: {scada_data.get_data_pressures()}")
|
|
147
|
-
|
|
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$")
|
|
150
|
+
scada_data.plot_pressures()
|
|
152
151
|
|
|
153
152
|
print(f"Flow readings: {scada_data.get_data_flows()}")
|
|
154
|
-
|
|
155
|
-
x_axis_label="Time (30min steps)",
|
|
156
|
-
y_axis_label="Flow rate in $m^3/h$")
|
|
153
|
+
scada_data.plot_flows()
|
|
157
154
|
```
|
|
158
155
|
### Generated plots
|
|
159
156
|
|
|
@@ -194,15 +191,19 @@ MIT license -- see [LICENSE](LICENSE)
|
|
|
194
191
|
|
|
195
192
|
If you use this software, please cite it as follows:
|
|
196
193
|
|
|
197
|
-
```
|
|
198
|
-
@
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
194
|
+
```bibtex
|
|
195
|
+
@article{Artelt2024,
|
|
196
|
+
doi = {10.21105/joss.07104},
|
|
197
|
+
url = {https://doi.org/10.21105/joss.07104},
|
|
198
|
+
year = {2024},
|
|
199
|
+
publisher = {The Open Journal},
|
|
200
|
+
volume = {9},
|
|
201
|
+
number = {103},
|
|
202
|
+
pages = {7104},
|
|
203
|
+
author = {André Artelt and Marios S. Kyriakou and Stelios G. Vrachimis and Demetrios G. Eliades and Barbara Hammer and Marios M. Polycarpou},
|
|
204
|
+
title = {EPyT-Flow: A Toolkit for Generating Water Distribution Network Data},
|
|
205
|
+
journal = {Journal of Open Source Software}
|
|
206
|
+
}
|
|
206
207
|
```
|
|
207
208
|
|
|
208
209
|
## How to get Support?
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
[](https://epyt-flow.readthedocs.io/en/stable/?badge=stable)
|
|
6
6
|
[](https://pepy.tech/project/epyt-flow)
|
|
7
7
|
[](https://pepy.tech/project/epyt-flow)
|
|
8
|
+
[](https://doi.org/10.21105/joss.07104)
|
|
8
9
|
|
|
9
10
|
# EPyT-Flow -- EPANET Python Toolkit - Flow
|
|
10
11
|
|
|
@@ -86,7 +87,7 @@ pip install .
|
|
|
86
87
|
```python
|
|
87
88
|
from epyt_flow.data.benchmarks import load_leakdb_scenarios
|
|
88
89
|
from epyt_flow.simulation import ScenarioSimulator
|
|
89
|
-
from epyt_flow.utils import to_seconds
|
|
90
|
+
from epyt_flow.utils import to_seconds
|
|
90
91
|
|
|
91
92
|
|
|
92
93
|
if __name__ == "__main__":
|
|
@@ -109,16 +110,10 @@ if __name__ == "__main__":
|
|
|
109
110
|
|
|
110
111
|
# Print & plot sensor readings over the entire simulation
|
|
111
112
|
print(f"Pressure readings: {scada_data.get_data_pressures()}")
|
|
112
|
-
|
|
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$")
|
|
113
|
+
scada_data.plot_pressures()
|
|
117
114
|
|
|
118
115
|
print(f"Flow readings: {scada_data.get_data_flows()}")
|
|
119
|
-
|
|
120
|
-
x_axis_label="Time (30min steps)",
|
|
121
|
-
y_axis_label="Flow rate in $m^3/h$")
|
|
116
|
+
scada_data.plot_flows()
|
|
122
117
|
```
|
|
123
118
|
### Generated plots
|
|
124
119
|
|
|
@@ -159,15 +154,19 @@ MIT license -- see [LICENSE](LICENSE)
|
|
|
159
154
|
|
|
160
155
|
If you use this software, please cite it as follows:
|
|
161
156
|
|
|
162
|
-
```
|
|
163
|
-
@
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
157
|
+
```bibtex
|
|
158
|
+
@article{Artelt2024,
|
|
159
|
+
doi = {10.21105/joss.07104},
|
|
160
|
+
url = {https://doi.org/10.21105/joss.07104},
|
|
161
|
+
year = {2024},
|
|
162
|
+
publisher = {The Open Journal},
|
|
163
|
+
volume = {9},
|
|
164
|
+
number = {103},
|
|
165
|
+
pages = {7104},
|
|
166
|
+
author = {André Artelt and Marios S. Kyriakou and Stelios G. Vrachimis and Demetrios G. Eliades and Barbara Hammer and Marios M. Polycarpou},
|
|
167
|
+
title = {EPyT-Flow: A Toolkit for Generating Water Distribution Network Data},
|
|
168
|
+
journal = {Journal of Open Source Software}
|
|
169
|
+
}
|
|
171
170
|
```
|
|
172
171
|
|
|
173
172
|
## How to get Support?
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.8.0
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Module provides a base class for control environments.
|
|
3
3
|
"""
|
|
4
|
+
import os
|
|
5
|
+
import uuid
|
|
4
6
|
from abc import abstractmethod, ABC
|
|
5
7
|
from typing import Union
|
|
6
8
|
import warnings
|
|
7
9
|
import numpy as np
|
|
8
10
|
|
|
9
|
-
from ..simulation import ScenarioSimulator, ScenarioConfig, ScadaData
|
|
11
|
+
from ..simulation import ScenarioSimulator, ScenarioConfig, ScadaData, ToolkitConstants
|
|
12
|
+
from ..utils import get_temp_folder
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
class ScenarioControlEnv(ABC):
|
|
@@ -21,12 +24,32 @@ class ScenarioControlEnv(ABC):
|
|
|
21
24
|
If True, environment is automatically reset if terminated.
|
|
22
25
|
|
|
23
26
|
The default is False.
|
|
27
|
+
|
|
28
|
+
Attributes
|
|
29
|
+
----------
|
|
30
|
+
_scenario_sim : :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`, protected
|
|
31
|
+
Scenario simulator of the control scenario.
|
|
32
|
+
_scenario_config : :class:`~epyt_flow.simulation.scenario_config.ScenarioConfig`
|
|
33
|
+
Scenario configuration.
|
|
34
|
+
_sim_generator : Generator[Union[:class:`~epyt_flow.simulation.scada.scada_data.ScadaData`, dict], bool, None], protected
|
|
35
|
+
Generator for running the step-wise simulation.
|
|
36
|
+
_hydraulic_scada_data : :class:`~epyt_flow.simulation.scada.scada_data.ScadaData`, protected
|
|
37
|
+
SCADA data from the hydraulic simulation -- only used if EPANET-MSX is used in the control scenario.
|
|
24
38
|
"""
|
|
25
39
|
def __init__(self, scenario_config: ScenarioConfig, autoreset: bool = False, **kwds):
|
|
26
|
-
|
|
40
|
+
if not isinstance(scenario_config, ScenarioConfig):
|
|
41
|
+
raise TypeError("'scenario_config' must be an instance of " +
|
|
42
|
+
"'epyt_flow.simulation.ScenarioConfig' " +
|
|
43
|
+
"but not of '{type(scenario_config)}'")
|
|
44
|
+
if not isinstance(autoreset, bool):
|
|
45
|
+
raise TypeError("'autoreset' must be an instance of 'bool' " +
|
|
46
|
+
f"but not of '{type(autoreset)}'")
|
|
47
|
+
|
|
48
|
+
self._scenario_config = scenario_config
|
|
27
49
|
self._scenario_sim = None
|
|
28
50
|
self._sim_generator = None
|
|
29
51
|
self.__autoreset = autoreset
|
|
52
|
+
self._hydraulic_scada_data = None
|
|
30
53
|
|
|
31
54
|
super().__init__(**kwds)
|
|
32
55
|
|
|
@@ -54,15 +77,27 @@ class ScenarioControlEnv(ABC):
|
|
|
54
77
|
"""
|
|
55
78
|
try:
|
|
56
79
|
if self._sim_generator is not None:
|
|
57
|
-
self._sim_generator.send(True)
|
|
58
80
|
next(self._sim_generator)
|
|
81
|
+
self._sim_generator.send(True)
|
|
59
82
|
except StopIteration:
|
|
60
83
|
pass
|
|
61
84
|
|
|
62
85
|
if self._scenario_sim is not None:
|
|
63
86
|
self._scenario_sim.close()
|
|
64
87
|
|
|
65
|
-
def
|
|
88
|
+
def contains_events(self) -> bool:
|
|
89
|
+
"""
|
|
90
|
+
Check if the scenario contains any events.
|
|
91
|
+
|
|
92
|
+
Returns
|
|
93
|
+
-------
|
|
94
|
+
`bool`
|
|
95
|
+
True is the scenario contains any events, False otherwise.
|
|
96
|
+
"""
|
|
97
|
+
return len(self._scenario_config.system_events) != 0 or \
|
|
98
|
+
len(self._scenario_config.sensor_reading_events) != 0
|
|
99
|
+
|
|
100
|
+
def reset(self) -> Union[tuple[ScadaData, bool], ScadaData]:
|
|
66
101
|
"""
|
|
67
102
|
Resets the environment (i.e. simulation).
|
|
68
103
|
|
|
@@ -75,20 +110,38 @@ class ScenarioControlEnv(ABC):
|
|
|
75
110
|
self._scenario_sim.close()
|
|
76
111
|
|
|
77
112
|
self._scenario_sim = ScenarioSimulator(
|
|
78
|
-
scenario_config=self.
|
|
79
|
-
|
|
113
|
+
scenario_config=self._scenario_config)
|
|
114
|
+
|
|
115
|
+
if self._scenario_sim.f_msx_in is not None:
|
|
116
|
+
# Run hydraulic simulation first
|
|
117
|
+
hyd_export = os.path.join(get_temp_folder(), f"epytflow_env_MSX_{uuid.uuid4()}.hyd")
|
|
118
|
+
sim = self._scenario_sim.run_hydraulic_simulation
|
|
119
|
+
self._hydraulic_scada_data = sim(hyd_export=hyd_export)
|
|
120
|
+
|
|
121
|
+
# Run advanced quality analysis (EPANET-MSX) on top of the computed hydraulics
|
|
122
|
+
gen = self._scenario_sim.run_advanced_quality_simulation_as_generator
|
|
123
|
+
self._sim_generator = gen(hyd_export, support_abort=True)
|
|
124
|
+
else:
|
|
125
|
+
gen = self._scenario_sim.run_hydraulic_simulation_as_generator
|
|
126
|
+
self._sim_generator = gen(support_abort=True)
|
|
80
127
|
|
|
81
128
|
return self._next_sim_itr()
|
|
82
129
|
|
|
83
|
-
def _next_sim_itr(self) -> ScadaData:
|
|
130
|
+
def _next_sim_itr(self) -> Union[tuple[ScadaData, bool], ScadaData]:
|
|
84
131
|
try:
|
|
85
132
|
next(self._sim_generator)
|
|
86
|
-
|
|
133
|
+
scada_data = self._sim_generator.send(False)
|
|
134
|
+
|
|
135
|
+
if self._scenario_sim.f_msx_in is not None:
|
|
136
|
+
cur_time = int(scada_data.sensor_readings_time[0])
|
|
137
|
+
cur_hyd_scada_data = self._hydraulic_scada_data.\
|
|
138
|
+
extract_time_window(cur_time, cur_time)
|
|
139
|
+
scada_data.join(cur_hyd_scada_data)
|
|
87
140
|
|
|
88
141
|
if self.autoreset is True:
|
|
89
|
-
return
|
|
142
|
+
return scada_data
|
|
90
143
|
else:
|
|
91
|
-
return
|
|
144
|
+
return scada_data, False
|
|
92
145
|
except StopIteration:
|
|
93
146
|
if self.__autoreset is True:
|
|
94
147
|
return self.reset()
|
|
@@ -112,6 +165,10 @@ class ScenarioControlEnv(ABC):
|
|
|
112
165
|
- EN_CLOSED = 0
|
|
113
166
|
- EN_OPEN = 1
|
|
114
167
|
"""
|
|
168
|
+
if self._scenario_sim.f_msx_in is not None:
|
|
169
|
+
raise RuntimeError("Can not execute actions affecting the hydraulics "+
|
|
170
|
+
"when running EPANET-MSX")
|
|
171
|
+
|
|
115
172
|
pump_idx = self._scenario_sim.epanet_api.getLinkPumpNameID().index(pump_id)
|
|
116
173
|
pump_link_idx = self._scenario_sim.epanet_api.getLinkPumpIndex(pump_idx + 1)
|
|
117
174
|
self._scenario_sim.epanet_api.setLinkStatus(pump_link_idx, status)
|
|
@@ -127,6 +184,10 @@ class ScenarioControlEnv(ABC):
|
|
|
127
184
|
speed : `float`
|
|
128
185
|
New pump speed.
|
|
129
186
|
"""
|
|
187
|
+
if self._scenario_sim.f_msx_in is not None:
|
|
188
|
+
raise RuntimeError("Can not execute actions affecting the hydraulics "+
|
|
189
|
+
"when running EPANET-MSX")
|
|
190
|
+
|
|
130
191
|
pump_idx = self._scenario_sim.epanet_api.getLinkPumpNameID().index(pump_id)
|
|
131
192
|
pattern_idx = self._scenario_sim.epanet_api.getLinkPumpPatternIndex(pump_idx + 1)
|
|
132
193
|
|
|
@@ -154,6 +215,10 @@ class ScenarioControlEnv(ABC):
|
|
|
154
215
|
- EN_CLOSED = 0
|
|
155
216
|
- EN_OPEN = 1
|
|
156
217
|
"""
|
|
218
|
+
if self._scenario_sim.f_msx_in is not None:
|
|
219
|
+
raise RuntimeError("Can not execute actions affecting the hydraulics "+
|
|
220
|
+
"when running EPANET-MSX")
|
|
221
|
+
|
|
157
222
|
valve_idx = self._scenario_sim.epanet_api.getLinkValveNameID().index(valve_id)
|
|
158
223
|
valve_link_idx = self._scenario_sim.epanet_api.getLinkValveIndex()[valve_idx]
|
|
159
224
|
self._scenario_sim.epanet_api.setLinkStatus(valve_link_idx, status)
|
|
@@ -173,11 +238,65 @@ class ScenarioControlEnv(ABC):
|
|
|
173
238
|
qual_value : `float`
|
|
174
239
|
New quality source value.
|
|
175
240
|
"""
|
|
241
|
+
if self._scenario_sim.f_msx_in is not None:
|
|
242
|
+
raise RuntimeError("Can not execute actions affecting the hydraulics "+
|
|
243
|
+
"when running EPANET-MSX")
|
|
244
|
+
|
|
176
245
|
node_idx = self._scenario_sim.epanet_api.getNodeIndex(node_id)
|
|
177
246
|
pattern_idx = self._scenario_sim.epanet_api.getPatternIndex(pattern_id)
|
|
178
247
|
self._scenario_sim.epanet_api.setNodeSourceQuality(node_idx, 1)
|
|
179
248
|
self._scenario_sim.epanet_api.setPattern(pattern_idx, np.array([qual_value]))
|
|
180
249
|
|
|
250
|
+
def set_node_species_source_value(self, species_id: str, node_id: str, source_type: int,
|
|
251
|
+
pattern_id: str, source_strength: float) -> None:
|
|
252
|
+
"""
|
|
253
|
+
Sets the species source at a particular node to a specific value -- i.e.
|
|
254
|
+
setting the species injection amount at a particular location.
|
|
255
|
+
|
|
256
|
+
Parameters
|
|
257
|
+
----------
|
|
258
|
+
species_id : `str`
|
|
259
|
+
ID of the species.
|
|
260
|
+
node_id : `str`
|
|
261
|
+
ID of the node.
|
|
262
|
+
source_type : `int`
|
|
263
|
+
Type of the external species injection source -- must be one of
|
|
264
|
+
the following EPANET toolkit constants:
|
|
265
|
+
|
|
266
|
+
- EN_CONCEN = 0
|
|
267
|
+
- EN_MASS = 1
|
|
268
|
+
- EN_SETPOINT = 2
|
|
269
|
+
- EN_FLOWPACED = 3
|
|
270
|
+
|
|
271
|
+
Description:
|
|
272
|
+
|
|
273
|
+
- E_CONCEN Sets the concentration of external inflow entering a node
|
|
274
|
+
- EN_MASS Injects a given mass/minute into a node
|
|
275
|
+
- EN_SETPOINT Sets the concentration leaving a node to a given value
|
|
276
|
+
- EN_FLOWPACED Adds a given value to the concentration leaving a node
|
|
277
|
+
pattern_id : `str`
|
|
278
|
+
ID of the source pattern.
|
|
279
|
+
source_strength : `float`
|
|
280
|
+
Amount of the injected species (source strength) --
|
|
281
|
+
i.e. interpreation of this number depends on `source_type`
|
|
282
|
+
"""
|
|
283
|
+
if self._scenario_sim.f_msx_in is None:
|
|
284
|
+
raise RuntimeError("You are not running EPANET-MSX")
|
|
285
|
+
|
|
286
|
+
source_type_ = "None"
|
|
287
|
+
if source_type == ToolkitConstants.EN_CONCEN:
|
|
288
|
+
source_type_ = "CONCEN"
|
|
289
|
+
elif source_type == ToolkitConstants.EN_MASS:
|
|
290
|
+
source_type_ = "MASS"
|
|
291
|
+
elif source_type == ToolkitConstants.EN_SETPOINT:
|
|
292
|
+
source_type_ = "SETPOINT"
|
|
293
|
+
elif source_type == ToolkitConstants.EN_FLOWPACED:
|
|
294
|
+
source_type_ = "FLOWPACED"
|
|
295
|
+
|
|
296
|
+
self._scenario_sim.epanet_api.setMSXPattern(pattern_id, [1])
|
|
297
|
+
self._scenario_sim.epanet_api.setMSXSources(node_id, species_id, source_type_,
|
|
298
|
+
source_strength, pattern_id)
|
|
299
|
+
|
|
181
300
|
@abstractmethod
|
|
182
301
|
def step(self, *actions) -> Union[tuple[ScadaData, float, bool], tuple[ScadaData, float]]:
|
|
183
302
|
"""
|
|
@@ -47,6 +47,7 @@ NETWORK_TOPOLOGY_ID = 26
|
|
|
47
47
|
PUMP_STATE_EVENT_ID = 28
|
|
48
48
|
PUMP_SPEED_EVENT_ID = 29
|
|
49
49
|
VALVE_STATE_EVENT_ID = 30
|
|
50
|
+
SPECIESINJECTION_EVENT_ID = 31
|
|
50
51
|
|
|
51
52
|
|
|
52
53
|
def my_packb(data: Any) -> bytes:
|
|
@@ -452,9 +453,15 @@ def save_to_file(f_out: str, data: Any, use_compression: bool = True) -> None:
|
|
|
452
453
|
def __encode_bsr_array(array: scipy.sparse.bsr_array
|
|
453
454
|
) -> tuple[tuple[int, int], tuple[list[float], tuple[list[int], list[int]]]]:
|
|
454
455
|
shape = array.shape
|
|
455
|
-
data =
|
|
456
|
-
rows =
|
|
457
|
-
cols =
|
|
456
|
+
data = []
|
|
457
|
+
rows = []
|
|
458
|
+
cols = []
|
|
459
|
+
|
|
460
|
+
array_ = array.tocsr() # Bug workaround: BSR arrays do not implement __getitem__
|
|
461
|
+
for i, j in zip(*array_.nonzero()):
|
|
462
|
+
rows.append(int(i))
|
|
463
|
+
cols.append(int(j))
|
|
464
|
+
data.append(float(array_[i, j]))
|
|
458
465
|
|
|
459
466
|
return shape, (data, (rows, cols))
|
|
460
467
|
|
|
@@ -60,8 +60,8 @@ class Leakage(SystemEvent, JsonSerializable):
|
|
|
60
60
|
if area is None and diameter is None:
|
|
61
61
|
raise ValueError("Either 'diameter' or 'area' must be given")
|
|
62
62
|
if area is not None and diameter is not None:
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
raise ValueError("Either 'diameter' or 'area' must be given, " +
|
|
64
|
+
"but not both at the same time")
|
|
65
65
|
if diameter is not None:
|
|
66
66
|
if not isinstance(diameter, float):
|
|
67
67
|
raise TypeError("'diameter must be an instance of 'float' but " +
|
|
@@ -92,7 +92,7 @@ class Leakage(SystemEvent, JsonSerializable):
|
|
|
92
92
|
self.__area = area
|
|
93
93
|
self.__profile = profile
|
|
94
94
|
|
|
95
|
-
self.
|
|
95
|
+
self.__leaky_node_idx = None
|
|
96
96
|
self.__leak_emitter_coef = None
|
|
97
97
|
self.__time_pattern_idx = 0
|
|
98
98
|
|
|
@@ -174,7 +174,7 @@ class Leakage(SystemEvent, JsonSerializable):
|
|
|
174
174
|
def get_attributes(self) -> dict:
|
|
175
175
|
return super().get_attributes() | {"link_id": self.__link_id, "diameter": self.__diameter,
|
|
176
176
|
"area": self.__area, "profile": self.__profile,
|
|
177
|
-
"node_id": self.
|
|
177
|
+
"node_id": self.__node_id
|
|
178
178
|
if self.__link_id is None else None}
|
|
179
179
|
|
|
180
180
|
def __eq__(self, other) -> bool:
|
|
@@ -238,10 +238,16 @@ class Leakage(SystemEvent, JsonSerializable):
|
|
|
238
238
|
g = 32.17405 # feet/s^2
|
|
239
239
|
else:
|
|
240
240
|
raise ValueError("Leakages are only implemented for the following flow units:\n" +
|
|
241
|
-
" EN_CMH (cubic
|
|
241
|
+
" EN_CMH (cubic meter/hr)\n EN_CFS (foot/sec)")
|
|
242
242
|
|
|
243
243
|
return discharge_coef * area * np.sqrt(2. * g)
|
|
244
244
|
|
|
245
|
+
def _get_new_link_id(self) -> str:
|
|
246
|
+
return f"leak_pipe_{self.__link_id}"
|
|
247
|
+
|
|
248
|
+
def _get_new_node_id(self) -> str:
|
|
249
|
+
return f"leak_node_{self.__link_id}"
|
|
250
|
+
|
|
245
251
|
def init(self, epanet_api: epyt.epanet) -> None:
|
|
246
252
|
super().init(epanet_api)
|
|
247
253
|
|
|
@@ -250,35 +256,72 @@ class Leakage(SystemEvent, JsonSerializable):
|
|
|
250
256
|
if self.__link_id not in self._epanet_api.getLinkNameID():
|
|
251
257
|
raise ValueError(f"Unknown link/pipe '{self.__link_id}'")
|
|
252
258
|
|
|
253
|
-
new_link_id =
|
|
254
|
-
new_node_id =
|
|
259
|
+
new_link_id = self._get_new_link_id()
|
|
260
|
+
new_node_id = self._get_new_node_id()
|
|
255
261
|
|
|
256
262
|
all_nodes_id = self._epanet_api.getNodeNameID()
|
|
257
263
|
if new_node_id in all_nodes_id:
|
|
258
264
|
raise ValueError(f"There is already a leak at pipe {self.link_id}")
|
|
259
265
|
|
|
260
266
|
self._epanet_api.splitPipe(self.link_id, new_link_id, new_node_id)
|
|
261
|
-
self.
|
|
267
|
+
self.__leaky_node_idx = self._epanet_api.getNodeIndex(new_node_id)
|
|
262
268
|
else:
|
|
263
269
|
if self.__node_id not in self._epanet_api.getNodeNameID():
|
|
264
270
|
raise ValueError(f"Unknown node '{self.__node_id}'")
|
|
265
271
|
|
|
266
|
-
self.
|
|
272
|
+
self.__leaky_node_idx = self._epanet_api.getNodeIndex(self.__node_id)
|
|
267
273
|
|
|
268
|
-
self._epanet_api.setNodeEmitterCoeff(self.
|
|
274
|
+
self._epanet_api.setNodeEmitterCoeff(self.__leaky_node_idx, 0.)
|
|
269
275
|
|
|
270
276
|
# Compute leak emitter coefficient
|
|
271
277
|
self.__leak_emitter_coef = self.compute_leak_emitter_coefficient(
|
|
272
278
|
self.compute_leak_area(self.area))
|
|
273
279
|
|
|
280
|
+
def cleanup(self) -> None:
|
|
281
|
+
if self.__link_id is not None:
|
|
282
|
+
pipe_idx = self._epanet_api.getLinkIndex(self.__link_id)
|
|
283
|
+
link_prop = self._epanet_api.getLinksInfo()
|
|
284
|
+
link_diameter = link_prop.LinkDiameter[pipe_idx - 1]
|
|
285
|
+
link_length = link_prop.LinkLength[pipe_idx - 1] * 2.
|
|
286
|
+
link_roughness_coeff = link_prop.LinkRoughnessCoeff[pipe_idx - 1]
|
|
287
|
+
link_minor_loss_coeff = link_prop.LinkMinorLossCoeff[pipe_idx - 1]
|
|
288
|
+
link_initial_status = link_prop.LinkInitialStatus[pipe_idx - 1]
|
|
289
|
+
link_initial_setting = link_prop.LinkInitialSetting[pipe_idx - 1]
|
|
290
|
+
link_bulk_reaction_coeff = link_prop.LinkBulkReactionCoeff[pipe_idx - 1]
|
|
291
|
+
link_wall_reaction_coeff = link_prop.LinkWallReactionCoeff[pipe_idx - 1]
|
|
292
|
+
|
|
293
|
+
node_a_idx = int(self._epanet_api.getLinkNodesIndex(pipe_idx)[0])
|
|
294
|
+
node_b_idx = int(self._epanet_api.getLinkNodesIndex(self._get_new_link_id())[1])
|
|
295
|
+
|
|
296
|
+
self._epanet_api.deleteLink(self._get_new_link_id())
|
|
297
|
+
self._epanet_api.deleteLink(self.__link_id)
|
|
298
|
+
self._epanet_api.deleteNode(self._get_new_node_id())
|
|
299
|
+
|
|
300
|
+
self._epanet_api.addLinkPipe(self.__link_id,
|
|
301
|
+
self._epanet_api.getNodeNameID(node_a_idx),
|
|
302
|
+
self._epanet_api.getNodeNameID(node_b_idx))
|
|
303
|
+
link_idx = self._epanet_api.getLinkIndex(self.__link_id)
|
|
304
|
+
self._epanet_api.setLinkNodesIndex(link_idx,
|
|
305
|
+
node_a_idx, node_b_idx)
|
|
306
|
+
self._epanet_api.setLinkPipeData(link_idx, link_length, link_diameter,
|
|
307
|
+
link_roughness_coeff,
|
|
308
|
+
link_minor_loss_coeff)
|
|
309
|
+
if link_minor_loss_coeff != 0:
|
|
310
|
+
self._epanet_api.setLinklinkMinorLossCoeff(link_idx, link_minor_loss_coeff)
|
|
311
|
+
self._epanet_api.setLinkInitialStatus(link_idx, link_initial_status)
|
|
312
|
+
self._epanet_api.setLinkInitialSetting(link_idx, link_initial_setting)
|
|
313
|
+
self._epanet_api.setLinkBulkReactionCoeff(link_idx, link_bulk_reaction_coeff)
|
|
314
|
+
self._epanet_api.setLinkWallReactionCoeff(link_idx, link_wall_reaction_coeff)
|
|
315
|
+
self._epanet_api.setLinkTypePipe(link_idx)
|
|
316
|
+
|
|
274
317
|
def reset(self) -> None:
|
|
275
318
|
self.__time_pattern_idx = 0
|
|
276
319
|
|
|
277
320
|
def exit(self, cur_time) -> None:
|
|
278
|
-
self._epanet_api.setNodeEmitterCoeff(self.
|
|
321
|
+
self._epanet_api.setNodeEmitterCoeff(self.__leaky_node_idx, 0.)
|
|
279
322
|
|
|
280
323
|
def apply(self, cur_time: int) -> None:
|
|
281
|
-
self._epanet_api.setNodeEmitterCoeff(self.
|
|
324
|
+
self._epanet_api.setNodeEmitterCoeff(self.__leaky_node_idx,
|
|
282
325
|
self.__leak_emitter_coef *
|
|
283
326
|
self.__profile[self.__time_pattern_idx])
|
|
284
327
|
self.__time_pattern_idx += 1
|