epyt-flow 0.7.3__tar.gz → 0.8.1__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.
Files changed (153) hide show
  1. epyt_flow-0.8.1/CITATION.cff +61 -0
  2. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/PKG-INFO +20 -19
  3. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/README.md +17 -18
  4. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/REQUIREMENTS.txt +2 -0
  5. epyt_flow-0.8.1/epyt_flow/VERSION +1 -0
  6. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/data/benchmarks/leakdb.py +2 -2
  7. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/data/networks.py +14 -26
  8. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/gym/scenario_control_env.py +129 -10
  9. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/serialization.py +10 -3
  10. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/simulation/events/__init__.py +1 -0
  11. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/simulation/events/leakages.py +55 -12
  12. epyt_flow-0.8.1/epyt_flow/simulation/events/quality_events.py +194 -0
  13. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/simulation/events/system_event.py +5 -0
  14. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/simulation/scada/scada_data.py +512 -64
  15. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/simulation/scada/scada_data_export.py +7 -5
  16. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/simulation/scenario_config.py +13 -2
  17. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/simulation/scenario_simulator.py +275 -187
  18. epyt_flow-0.8.1/epyt_flow/simulation/scenario_visualizer.py +1307 -0
  19. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow.egg-info/PKG-INFO +20 -19
  20. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow.egg-info/SOURCES.txt +1 -0
  21. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow.egg-info/requires.txt +2 -0
  22. epyt_flow-0.7.3/CITATION.cff +0 -37
  23. epyt_flow-0.7.3/epyt_flow/VERSION +0 -1
  24. epyt_flow-0.7.3/epyt_flow/simulation/scenario_visualizer.py +0 -61
  25. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/CODE_OF_CONDUCT.md +0 -0
  26. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/LICENSE +0 -0
  27. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/MANIFEST.in +0 -0
  28. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/AUTHORS +0 -0
  29. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/LICENSE +0 -0
  30. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/Readme_SRC_Engines.txt +0 -0
  31. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/enumstxt.h +0 -0
  32. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/epanet.c +0 -0
  33. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/epanet2.c +0 -0
  34. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/epanet2.def +0 -0
  35. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/errors.dat +0 -0
  36. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/funcs.h +0 -0
  37. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/genmmd.c +0 -0
  38. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/hash.c +0 -0
  39. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/hash.h +0 -0
  40. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/hydcoeffs.c +0 -0
  41. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/hydraul.c +0 -0
  42. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/hydsolver.c +0 -0
  43. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/hydstatus.c +0 -0
  44. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2.h +0 -0
  45. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_2.h +0 -0
  46. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_enums.h +0 -0
  47. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/inpfile.c +0 -0
  48. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/input1.c +0 -0
  49. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/input2.c +0 -0
  50. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/input3.c +0 -0
  51. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/main.c +0 -0
  52. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/mempool.c +0 -0
  53. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/mempool.h +0 -0
  54. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/output.c +0 -0
  55. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/project.c +0 -0
  56. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/quality.c +0 -0
  57. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/qualreact.c +0 -0
  58. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/qualroute.c +0 -0
  59. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/report.c +0 -0
  60. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/rules.c +0 -0
  61. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/smatrix.c +0 -0
  62. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/text.h +0 -0
  63. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET/SRC_engines/types.h +0 -0
  64. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/MSX_Updates.txt +0 -0
  65. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/dispersion.h +0 -0
  66. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/hash.c +0 -0
  67. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/hash.h +0 -0
  68. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx.h +0 -0
  69. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx_export.h +0 -0
  70. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.c +0 -0
  71. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.h +0 -0
  72. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/mempool.c +0 -0
  73. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/mempool.h +0 -0
  74. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/msxchem.c +0 -0
  75. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/msxcompiler.c +0 -0
  76. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/msxdict.h +0 -0
  77. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/msxdispersion.c +0 -0
  78. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/msxerr.c +0 -0
  79. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/msxfile.c +0 -0
  80. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.c +0 -0
  81. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.h +0 -0
  82. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/msxinp.c +0 -0
  83. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/msxout.c +0 -0
  84. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/msxproj.c +0 -0
  85. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/msxqual.c +0 -0
  86. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/msxrpt.c +0 -0
  87. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/msxtank.c +0 -0
  88. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/msxtoolkit.c +0 -0
  89. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/msxtypes.h +0 -0
  90. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/msxutils.c +0 -0
  91. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/msxutils.h +0 -0
  92. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/newton.c +0 -0
  93. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/newton.h +0 -0
  94. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/rk5.c +0 -0
  95. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/rk5.h +0 -0
  96. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/ros2.c +0 -0
  97. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/ros2.h +0 -0
  98. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/smatrix.c +0 -0
  99. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/Src/smatrix.h +0 -0
  100. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/EPANET-MSX/readme.txt +0 -0
  101. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/compile_linux.sh +0 -0
  102. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/EPANET/compile_macos.sh +0 -0
  103. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/__init__.py +0 -0
  104. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/data/__init__.py +0 -0
  105. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/data/benchmarks/__init__.py +0 -0
  106. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/data/benchmarks/batadal.py +0 -0
  107. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/data/benchmarks/batadal_data.py +0 -0
  108. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/data/benchmarks/battledim.py +0 -0
  109. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/data/benchmarks/battledim_data.py +0 -0
  110. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/data/benchmarks/gecco_water_quality.py +0 -0
  111. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/data/benchmarks/leakdb_data.py +0 -0
  112. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/data/benchmarks/water_usage.py +0 -0
  113. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/gym/__init__.py +0 -0
  114. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/gym/control_gyms.py +0 -0
  115. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/metrics.py +0 -0
  116. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/models/__init__.py +0 -0
  117. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/models/event_detector.py +0 -0
  118. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/models/sensor_interpolation_detector.py +0 -0
  119. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/rest_api/__init__.py +0 -0
  120. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/rest_api/base_handler.py +0 -0
  121. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/rest_api/res_manager.py +0 -0
  122. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/rest_api/scada_data/__init__.py +0 -0
  123. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/rest_api/scada_data/data_handlers.py +0 -0
  124. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/rest_api/scada_data/export_handlers.py +0 -0
  125. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/rest_api/scada_data/handlers.py +0 -0
  126. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/rest_api/scenario/__init__.py +0 -0
  127. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/rest_api/scenario/event_handlers.py +0 -0
  128. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/rest_api/scenario/handlers.py +0 -0
  129. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/rest_api/scenario/simulation_handlers.py +0 -0
  130. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/rest_api/scenario/uncertainty_handlers.py +0 -0
  131. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/rest_api/server.py +0 -0
  132. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/simulation/__init__.py +0 -0
  133. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/simulation/events/actuator_events.py +0 -0
  134. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/simulation/events/event.py +0 -0
  135. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/simulation/events/sensor_faults.py +0 -0
  136. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/simulation/events/sensor_reading_attack.py +0 -0
  137. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/simulation/events/sensor_reading_event.py +0 -0
  138. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/simulation/parallel_simulation.py +0 -0
  139. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/simulation/scada/__init__.py +0 -0
  140. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/simulation/scada/advanced_control.py +0 -0
  141. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/simulation/sensor_config.py +0 -0
  142. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/topology.py +0 -0
  143. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/uncertainty/__init__.py +0 -0
  144. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/uncertainty/model_uncertainty.py +0 -0
  145. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/uncertainty/sensor_noise.py +0 -0
  146. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/uncertainty/uncertainties.py +0 -0
  147. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/uncertainty/utils.py +0 -0
  148. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow/utils.py +0 -0
  149. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow.egg-info/dependency_links.txt +0 -0
  150. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/epyt_flow.egg-info/top_level.txt +0 -0
  151. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/pyproject.toml +0 -0
  152. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/setup.cfg +0 -0
  153. {epyt_flow-0.7.3 → epyt_flow-0.8.1}/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.7.3
3
+ Version: 0.8.1
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
  [![pypi](https://img.shields.io/pypi/v/epyt-flow.svg)](https://pypi.org/project/epyt-flow/)
@@ -40,6 +42,7 @@ Requires-Dist: psutil
40
42
  [![Documentation Status](https://readthedocs.org/projects/epyt-flow/badge/?version=stable)](https://epyt-flow.readthedocs.io/en/stable/?badge=stable)
41
43
  [![Downloads](https://static.pepy.tech/badge/epyt-flow)](https://pepy.tech/project/epyt-flow)
42
44
  [![Downloads](https://static.pepy.tech/badge/epyt-flow/month)](https://pepy.tech/project/epyt-flow)
45
+ [![DOI](https://joss.theoj.org/papers/10.21105/joss.07104/status.svg)](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, plot_timeseries_data
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
- 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$")
150
+ scada_data.plot_pressures()
152
151
 
153
152
  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$")
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
- @misc{github:epytflow,
199
- author = {André Artelt, Marios S. Kyriakou, Stelios G. Vrachimis, Demetrios G. Eliades, Barbara Hammer, Marios M. Polycarpou},
200
- title = {EPyT-Flow -- EPANET Python Toolkit - Flow},
201
- year = {2024},
202
- publisher = {GitHub},
203
- journal = {GitHub repository},
204
- howpublished = {\url{https://github.com/WaterFutures/EPyT-Flow}}
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
  [![Documentation Status](https://readthedocs.org/projects/epyt-flow/badge/?version=stable)](https://epyt-flow.readthedocs.io/en/stable/?badge=stable)
6
6
  [![Downloads](https://static.pepy.tech/badge/epyt-flow)](https://pepy.tech/project/epyt-flow)
7
7
  [![Downloads](https://static.pepy.tech/badge/epyt-flow/month)](https://pepy.tech/project/epyt-flow)
8
+ [![DOI](https://joss.theoj.org/papers/10.21105/joss.07104/status.svg)](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, plot_timeseries_data
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
- 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$")
113
+ scada_data.plot_pressures()
117
114
 
118
115
  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$")
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
- @misc{github:epytflow,
164
- author = {André Artelt, Marios S. Kyriakou, Stelios G. Vrachimis, Demetrios G. Eliades, Barbara Hammer, Marios M. Polycarpou},
165
- title = {EPyT-Flow -- EPANET Python Toolkit - Flow},
166
- year = {2024},
167
- publisher = {GitHub},
168
- journal = {GitHub repository},
169
- howpublished = {\url{https://github.com/WaterFutures/EPyT-Flow}}
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?
@@ -9,4 +9,6 @@ openpyxl>=3.1.2
9
9
  falcon>=3.1.3
10
10
  multiprocess>=0.70.16
11
11
  geopandas>=0.14.4
12
+ svgpath2mpl>=1.0.0
13
+ Deprecated>=1.2.14
12
14
  psutil
@@ -0,0 +1 @@
1
+ 0.8.1
@@ -491,9 +491,9 @@ def load_scenarios(scenarios_id: list[int], use_net1: bool = True,
491
491
 
492
492
  return dem_final
493
493
 
494
- week_pattern_url = "https://github.com/KIOS-Research/LeakDB/raw/master/CCWI-WDSA2018/" +\
494
+ week_pattern_url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/CCWI-WDSA2018/" +\
495
495
  "Dataset_Generator_Py3/weekPat_30min.mat"
496
- year_offset_url = "https://github.com/KIOS-Research/LeakDB/raw/master/CCWI-WDSA2018/" +\
496
+ year_offset_url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/CCWI-WDSA2018/" +\
497
497
  "Dataset_Generator_Py3/yearOffset_30min.mat"
498
498
 
499
499
  download_if_necessary(os.path.join(download_dir, "weekPat_30min.mat"),
@@ -153,8 +153,7 @@ def load_net1(download_dir: str = get_temp_folder(), verbose: bool = True,
153
153
  :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`.
154
154
  """
155
155
  f_in = os.path.join(download_dir, "Net1.inp")
156
- url = "https://raw.githubusercontent.com/OpenWaterAnalytics/EPyT/main/epyt/networks/" + \
157
- "asce-tf-wdst/Net1.inp"
156
+ url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/Net1.inp"
158
157
 
159
158
  download_if_necessary(f_in, url, verbose)
160
159
  return load_inp(f_in, flow_units_id=flow_units_id)
@@ -201,8 +200,7 @@ def load_net2(download_dir: str = get_temp_folder(), verbose: bool = True,
201
200
  :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`.
202
201
  """
203
202
  f_in = os.path.join(download_dir, "Net2.inp")
204
- url = "https://raw.githubusercontent.com/OpenWaterAnalytics/EPyT/main/epyt/networks/" + \
205
- "asce-tf-wdst/Net2.inp"
203
+ url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/Net2.inp"
206
204
 
207
205
  download_if_necessary(f_in, url, verbose)
208
206
  return load_inp(f_in, flow_units_id=flow_units_id)
@@ -249,8 +247,7 @@ def load_net3(download_dir: str = get_temp_folder(), verbose: bool = True,
249
247
  :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`.
250
248
  """
251
249
  f_in = os.path.join(download_dir, "Net3.inp")
252
- url = "https://raw.githubusercontent.com/OpenWaterAnalytics/EPyT/main/epyt/networks/" + \
253
- "asce-tf-wdst/Net3.inp"
250
+ url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/Net3.inp"
254
251
 
255
252
  download_if_necessary(f_in, url, verbose)
256
253
  return load_inp(f_in, flow_units_id=flow_units_id)
@@ -297,7 +294,7 @@ def load_net6(download_dir: str = get_temp_folder(), verbose: bool = True,
297
294
  :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`.
298
295
  """
299
296
  f_in = os.path.join(download_dir, "Net6.inp")
300
- url = "https://github.com/OpenWaterAnalytics/WNTR/raw/main/examples/networks/Net6.inp"
297
+ url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/Net6.inp"
301
298
 
302
299
  download_if_necessary(f_in, url, verbose)
303
300
  return load_inp(f_in, flow_units_id=flow_units_id)
@@ -344,8 +341,7 @@ def load_richmond(download_dir: str = get_temp_folder(), verbose: bool = True,
344
341
  :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`.
345
342
  """
346
343
  f_in = os.path.join(download_dir, "Richmond_standard.inp")
347
- url = "https://raw.githubusercontent.com/OpenWaterAnalytics/EPyT/main/epyt/networks/" + \
348
- "exeter-benchmarks/Richmond_standard.inp"
344
+ url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/Richmond_standard.inp"
349
345
 
350
346
  download_if_necessary(f_in, url, verbose)
351
347
  return load_inp(f_in, flow_units_id=flow_units_id)
@@ -392,8 +388,7 @@ def load_micropolis(download_dir: str = get_temp_folder(), verbose: bool = True,
392
388
  :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`.
393
389
  """
394
390
  f_in = os.path.join(download_dir, "MICROPOLIS_v1.inp")
395
- url = "https://github.com/OpenWaterAnalytics/EPyT/raw/main/epyt/networks/asce-tf-wdst/" + \
396
- "MICROPOLIS_v1.inp"
391
+ url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/MICROPOLIS_v1.inp"
397
392
 
398
393
  download_if_necessary(f_in, url, verbose)
399
394
  return load_inp(f_in, flow_units_id=flow_units_id)
@@ -440,8 +435,7 @@ def load_balerma(download_dir: str = get_temp_folder(), verbose: bool = True,
440
435
  :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`.
441
436
  """
442
437
  f_in = os.path.join(download_dir, "Balerma.inp")
443
- url = "https://github.com/OpenWaterAnalytics/EPyT/raw/main/epyt/networks/" + \
444
- "asce-tf-wdst/Balerma.inp"
438
+ url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/Balerma.inp"
445
439
 
446
440
  download_if_necessary(f_in, url, verbose)
447
441
  return load_inp(f_in, flow_units_id=flow_units_id)
@@ -488,8 +482,7 @@ def load_rural(download_dir: str = get_temp_folder(), verbose: bool = True,
488
482
  :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`.
489
483
  """
490
484
  f_in = os.path.join(download_dir, "RuralNetwork.inp")
491
- url = "https://github.com/OpenWaterAnalytics/EPyT/raw/main/epyt/networks/" + \
492
- "asce-tf-wdst/RuralNetwork.inp"
485
+ url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/RuralNetwork.inp"
493
486
 
494
487
  download_if_necessary(f_in, url, verbose)
495
488
  return load_inp(f_in, flow_units_id=flow_units_id)
@@ -536,8 +529,7 @@ def load_bwsn1(download_dir: str = get_temp_folder(), verbose: bool = True,
536
529
  :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`.
537
530
  """
538
531
  f_in = os.path.join(download_dir, "BWSN_Network_1.inp")
539
- url = "https://github.com/OpenWaterAnalytics/EPyT/raw/main/epyt/networks/" + \
540
- "asce-tf-wdst/BWSN_Network_1.inp"
532
+ url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/BWSN_Network_1.inp"
541
533
 
542
534
  download_if_necessary(f_in, url, verbose)
543
535
  return load_inp(f_in, flow_units_id=flow_units_id)
@@ -584,8 +576,7 @@ def load_bwsn2(download_dir: str = get_temp_folder(), verbose: bool = True,
584
576
  :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`.
585
577
  """
586
578
  f_in = os.path.join(download_dir, "BWSN_Network_2.inp")
587
- url = "https://github.com/OpenWaterAnalytics/EPyT/raw/main/epyt/networks/" + \
588
- "asce-tf-wdst/BWSN_Network_2.inp"
579
+ url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/BWSN_Network_2.inp"
589
580
 
590
581
  download_if_necessary(f_in, url, verbose)
591
582
  return load_inp(f_in, flow_units_id=flow_units_id)
@@ -632,8 +623,7 @@ def load_anytown(download_dir: str = get_temp_folder(), verbose: bool = True,
632
623
  :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`.
633
624
  """
634
625
  f_in = os.path.join(download_dir, "Anytown.inp")
635
- url = "https://raw.githubusercontent.com/OpenWaterAnalytics/EPyT/main/epyt/networks/" + \
636
- "asce-tf-wdst/Anytown.inp"
626
+ url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/Anytown.inp"
637
627
 
638
628
  download_if_necessary(f_in, url, verbose)
639
629
  return load_inp(f_in, flow_units_id=flow_units_id)
@@ -727,7 +717,7 @@ def load_ctown(download_dir: str = get_temp_folder(), verbose: bool = True,
727
717
  :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`.
728
718
  """
729
719
  f_in = os.path.join(download_dir, "CTOWN.INP")
730
- url = "https://github.com/scy-phy/www.batadal.net/raw/master/data/CTOWN.INP"
720
+ url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/CTOWN.INP"
731
721
 
732
722
  download_if_necessary(f_in, url, verbose)
733
723
  return load_inp(f_in, flow_units_id=flow_units_id)
@@ -783,8 +773,7 @@ def load_kentucky(wdn_id: int = 1, download_dir: str = get_temp_folder(),
783
773
  raise ValueError(f"Unknown network 'ky{wdn_id}.inp'")
784
774
 
785
775
  f_in = os.path.join(download_dir, f"ky{wdn_id}.inp")
786
- url = "https://raw.githubusercontent.com/OpenWaterAnalytics/EPyT/main/epyt/networks/" + \
787
- f"asce-tf-wdst/ky{wdn_id}.inp"
776
+ url = f"https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/ky{wdn_id}.inp"
788
777
 
789
778
  download_if_necessary(f_in, url, verbose)
790
779
  return load_inp(f_in, flow_units_id=flow_units_id)
@@ -836,8 +825,7 @@ def load_hanoi(download_dir: str = get_temp_folder(),
836
825
  :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`.
837
826
  """
838
827
  f_in = os.path.join(download_dir, "Hanoi.inp")
839
- url = "https://raw.githubusercontent.com/OpenWaterAnalytics/EPyT/main/epyt/networks/" + \
840
- "asce-tf-wdst/Hanoi.inp"
828
+ url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/Hanoi.inp"
841
829
 
842
830
  download_if_necessary(f_in, url, verbose)
843
831
  config = load_inp(f_in, flow_units_id=flow_units_id)
@@ -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
- self.__scenario_config = scenario_config
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 reset(self) -> ScadaData:
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.__scenario_config)
79
- self._sim_generator = self._scenario_sim.run_simulation_as_generator(support_abort=True)
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
- r = self._sim_generator.send(False)
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 r
142
+ return scada_data
90
143
  else:
91
- return r, False
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 = array.data.flatten().tolist()
456
- rows = array.nonzero()[0].tolist()
457
- cols = array.nonzero()[1].tolist()
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
 
@@ -1,5 +1,6 @@
1
1
  from .system_event import *
2
2
  from .leakages import *
3
+ from .quality_events import *
3
4
  from .sensor_reading_event import *
4
5
  from .sensor_faults import *
5
6
  from .sensor_reading_attack import *