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.
Files changed (153) hide show
  1. epyt_flow-0.8.0/CITATION.cff +61 -0
  2. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/PKG-INFO +20 -19
  3. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/README.md +17 -18
  4. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/REQUIREMENTS.txt +2 -0
  5. epyt_flow-0.8.0/epyt_flow/VERSION +1 -0
  6. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/gym/scenario_control_env.py +129 -10
  7. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/serialization.py +10 -3
  8. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/events/__init__.py +1 -0
  9. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/events/leakages.py +55 -12
  10. epyt_flow-0.8.0/epyt_flow/simulation/events/quality_events.py +194 -0
  11. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/events/system_event.py +5 -0
  12. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/scada/scada_data.py +512 -64
  13. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/scada/scada_data_export.py +7 -5
  14. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/scenario_config.py +13 -2
  15. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/scenario_simulator.py +275 -187
  16. epyt_flow-0.8.0/epyt_flow/simulation/scenario_visualizer.py +1307 -0
  17. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/sensor_config.py +21 -10
  18. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow.egg-info/PKG-INFO +20 -19
  19. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow.egg-info/SOURCES.txt +1 -0
  20. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow.egg-info/requires.txt +2 -0
  21. epyt_flow-0.7.2/CITATION.cff +0 -37
  22. epyt_flow-0.7.2/epyt_flow/VERSION +0 -1
  23. epyt_flow-0.7.2/epyt_flow/simulation/scenario_visualizer.py +0 -61
  24. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/CODE_OF_CONDUCT.md +0 -0
  25. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/LICENSE +0 -0
  26. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/MANIFEST.in +0 -0
  27. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/AUTHORS +0 -0
  28. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/LICENSE +0 -0
  29. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/Readme_SRC_Engines.txt +0 -0
  30. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/enumstxt.h +0 -0
  31. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/epanet.c +0 -0
  32. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/epanet2.c +0 -0
  33. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/epanet2.def +0 -0
  34. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/errors.dat +0 -0
  35. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/funcs.h +0 -0
  36. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/genmmd.c +0 -0
  37. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/hash.c +0 -0
  38. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/hash.h +0 -0
  39. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydcoeffs.c +0 -0
  40. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydraul.c +0 -0
  41. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydsolver.c +0 -0
  42. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydstatus.c +0 -0
  43. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2.h +0 -0
  44. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_2.h +0 -0
  45. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_enums.h +0 -0
  46. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/inpfile.c +0 -0
  47. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/input1.c +0 -0
  48. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/input2.c +0 -0
  49. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/input3.c +0 -0
  50. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/main.c +0 -0
  51. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/mempool.c +0 -0
  52. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/mempool.h +0 -0
  53. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/output.c +0 -0
  54. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/project.c +0 -0
  55. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/quality.c +0 -0
  56. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/qualreact.c +0 -0
  57. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/qualroute.c +0 -0
  58. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/report.c +0 -0
  59. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/rules.c +0 -0
  60. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/smatrix.c +0 -0
  61. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/text.h +0 -0
  62. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET/SRC_engines/types.h +0 -0
  63. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/MSX_Updates.txt +0 -0
  64. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/dispersion.h +0 -0
  65. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/hash.c +0 -0
  66. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/hash.h +0 -0
  67. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx.h +0 -0
  68. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx_export.h +0 -0
  69. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.c +0 -0
  70. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.h +0 -0
  71. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/mempool.c +0 -0
  72. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/mempool.h +0 -0
  73. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxchem.c +0 -0
  74. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxcompiler.c +0 -0
  75. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxdict.h +0 -0
  76. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxdispersion.c +0 -0
  77. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxerr.c +0 -0
  78. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxfile.c +0 -0
  79. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.c +0 -0
  80. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.h +0 -0
  81. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxinp.c +0 -0
  82. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxout.c +0 -0
  83. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxproj.c +0 -0
  84. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxqual.c +0 -0
  85. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxrpt.c +0 -0
  86. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxtank.c +0 -0
  87. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxtoolkit.c +0 -0
  88. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxtypes.h +0 -0
  89. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxutils.c +0 -0
  90. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxutils.h +0 -0
  91. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/newton.c +0 -0
  92. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/newton.h +0 -0
  93. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/rk5.c +0 -0
  94. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/rk5.h +0 -0
  95. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/ros2.c +0 -0
  96. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/ros2.h +0 -0
  97. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/smatrix.c +0 -0
  98. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/Src/smatrix.h +0 -0
  99. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/EPANET-MSX/readme.txt +0 -0
  100. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/compile_linux.sh +0 -0
  101. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/EPANET/compile_macos.sh +0 -0
  102. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/__init__.py +0 -0
  103. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/__init__.py +0 -0
  104. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/benchmarks/__init__.py +0 -0
  105. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/benchmarks/batadal.py +0 -0
  106. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/benchmarks/batadal_data.py +0 -0
  107. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/benchmarks/battledim.py +0 -0
  108. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/benchmarks/battledim_data.py +0 -0
  109. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/benchmarks/gecco_water_quality.py +0 -0
  110. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/benchmarks/leakdb.py +0 -0
  111. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/benchmarks/leakdb_data.py +0 -0
  112. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/benchmarks/water_usage.py +0 -0
  113. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/data/networks.py +0 -0
  114. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/gym/__init__.py +0 -0
  115. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/gym/control_gyms.py +0 -0
  116. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/metrics.py +0 -0
  117. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/models/__init__.py +0 -0
  118. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/models/event_detector.py +0 -0
  119. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/models/sensor_interpolation_detector.py +0 -0
  120. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/__init__.py +0 -0
  121. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/base_handler.py +0 -0
  122. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/res_manager.py +0 -0
  123. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/scada_data/__init__.py +0 -0
  124. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/scada_data/data_handlers.py +0 -0
  125. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/scada_data/export_handlers.py +0 -0
  126. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/scada_data/handlers.py +0 -0
  127. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/scenario/__init__.py +0 -0
  128. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/scenario/event_handlers.py +0 -0
  129. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/scenario/handlers.py +0 -0
  130. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/scenario/simulation_handlers.py +0 -0
  131. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/scenario/uncertainty_handlers.py +0 -0
  132. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/rest_api/server.py +0 -0
  133. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/__init__.py +0 -0
  134. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/events/actuator_events.py +0 -0
  135. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/events/event.py +0 -0
  136. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/events/sensor_faults.py +0 -0
  137. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/events/sensor_reading_attack.py +0 -0
  138. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/events/sensor_reading_event.py +0 -0
  139. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/parallel_simulation.py +0 -0
  140. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/scada/__init__.py +0 -0
  141. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/simulation/scada/advanced_control.py +0 -0
  142. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/topology.py +0 -0
  143. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/uncertainty/__init__.py +0 -0
  144. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/uncertainty/model_uncertainty.py +0 -0
  145. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/uncertainty/sensor_noise.py +0 -0
  146. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/uncertainty/uncertainties.py +0 -0
  147. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/uncertainty/utils.py +0 -0
  148. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow/utils.py +0 -0
  149. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow.egg-info/dependency_links.txt +0 -0
  150. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/epyt_flow.egg-info/top_level.txt +0 -0
  151. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/pyproject.toml +0 -0
  152. {epyt_flow-0.7.2 → epyt_flow-0.8.0}/setup.cfg +0 -0
  153. {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.7.2
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
  [![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.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
- 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 *
@@ -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
- raise ValueError("Either 'diameter' or 'area' must be given, " +
64
- "but not both at the same time")
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.__leaky_node_id = None
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.__leaky_node_id
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 foot/sec)\n EN_CFS (cubic meter/hr)")
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 = f"leak_pipe_{self.link_id}"
254
- new_node_id = f"leak_node_{self.link_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.__leaky_node_id = self._epanet_api.getNodeIndex(new_node_id)
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.__leaky_node_id = self._epanet_api.getNodeIndex(self.__node_id)
272
+ self.__leaky_node_idx = self._epanet_api.getNodeIndex(self.__node_id)
267
273
 
268
- self._epanet_api.setNodeEmitterCoeff(self.__leaky_node_id, 0.)
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.__leaky_node_id, 0.)
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.__leaky_node_id,
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