epyt-flow 0.9.0__tar.gz → 0.11.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 (157) hide show
  1. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/LICENSE +1 -1
  2. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/PKG-INFO +18 -6
  3. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/README.md +14 -3
  4. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/REQUIREMENTS.txt +1 -1
  5. epyt_flow-0.11.0/epyt_flow/VERSION +1 -0
  6. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/data/networks.py +27 -14
  7. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/gym/control_gyms.py +8 -0
  8. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/gym/scenario_control_env.py +17 -4
  9. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/metrics.py +5 -0
  10. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/models/event_detector.py +5 -0
  11. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/models/sensor_interpolation_detector.py +5 -0
  12. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/serialization.py +5 -0
  13. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/simulation/__init__.py +0 -1
  14. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/simulation/events/actuator_events.py +7 -1
  15. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/simulation/events/sensor_reading_attack.py +16 -3
  16. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/simulation/events/sensor_reading_event.py +18 -3
  17. epyt_flow-0.11.0/epyt_flow/simulation/scada/__init__.py +5 -0
  18. epyt_flow-0.11.0/epyt_flow/simulation/scada/advanced_control.py +138 -0
  19. epyt_flow-0.11.0/epyt_flow/simulation/scada/complex_control.py +625 -0
  20. epyt_flow-0.9.0/epyt_flow/simulation/scada/advanced_control.py → epyt_flow-0.11.0/epyt_flow/simulation/scada/custom_control.py +3 -3
  21. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/simulation/scada/scada_data.py +547 -8
  22. epyt_flow-0.11.0/epyt_flow/simulation/scada/simple_control.py +317 -0
  23. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/simulation/scenario_config.py +87 -26
  24. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/simulation/scenario_simulator.py +865 -51
  25. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/simulation/sensor_config.py +34 -2
  26. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/topology.py +16 -0
  27. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/uncertainty/model_uncertainty.py +80 -62
  28. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/uncertainty/sensor_noise.py +15 -4
  29. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/uncertainty/uncertainties.py +71 -18
  30. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/uncertainty/utils.py +40 -13
  31. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/utils.py +15 -1
  32. epyt_flow-0.11.0/epyt_flow/visualization/__init__.py +2 -0
  33. {epyt_flow-0.9.0/epyt_flow/simulation → epyt_flow-0.11.0/epyt_flow/visualization}/scenario_visualizer.py +429 -586
  34. epyt_flow-0.11.0/epyt_flow/visualization/visualization_utils.py +611 -0
  35. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow.egg-info/PKG-INFO +18 -6
  36. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow.egg-info/SOURCES.txt +7 -2
  37. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow.egg-info/requires.txt +1 -1
  38. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow.egg-info/top_level.txt +1 -0
  39. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/pyproject.toml +2 -1
  40. epyt_flow-0.9.0/epyt_flow/VERSION +0 -1
  41. epyt_flow-0.9.0/epyt_flow/simulation/scada/__init__.py +0 -3
  42. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/CITATION.cff +0 -0
  43. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/CODE_OF_CONDUCT.md +0 -0
  44. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/MANIFEST.in +0 -0
  45. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/AUTHORS +0 -0
  46. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/LICENSE +0 -0
  47. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/Readme_SRC_Engines.txt +0 -0
  48. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/enumstxt.h +0 -0
  49. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/epanet.c +0 -0
  50. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/epanet2.c +0 -0
  51. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/epanet2.def +0 -0
  52. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/errors.dat +0 -0
  53. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/funcs.h +0 -0
  54. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/genmmd.c +0 -0
  55. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/hash.c +0 -0
  56. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/hash.h +0 -0
  57. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydcoeffs.c +0 -0
  58. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydraul.c +0 -0
  59. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydsolver.c +0 -0
  60. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/hydstatus.c +0 -0
  61. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2.h +0 -0
  62. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_2.h +0 -0
  63. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_enums.h +0 -0
  64. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/inpfile.c +0 -0
  65. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/input1.c +0 -0
  66. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/input2.c +0 -0
  67. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/input3.c +0 -0
  68. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/main.c +0 -0
  69. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/mempool.c +0 -0
  70. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/mempool.h +0 -0
  71. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/output.c +0 -0
  72. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/project.c +0 -0
  73. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/quality.c +0 -0
  74. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/qualreact.c +0 -0
  75. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/qualroute.c +0 -0
  76. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/report.c +0 -0
  77. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/rules.c +0 -0
  78. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/smatrix.c +0 -0
  79. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/text.h +0 -0
  80. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET/SRC_engines/types.h +0 -0
  81. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/MSX_Updates.txt +0 -0
  82. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/dispersion.h +0 -0
  83. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/hash.c +0 -0
  84. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/hash.h +0 -0
  85. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx.h +0 -0
  86. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx_export.h +0 -0
  87. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.c +0 -0
  88. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.h +0 -0
  89. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/mempool.c +0 -0
  90. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/mempool.h +0 -0
  91. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxchem.c +0 -0
  92. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxcompiler.c +0 -0
  93. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxdict.h +0 -0
  94. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxdispersion.c +0 -0
  95. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxerr.c +0 -0
  96. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxfile.c +0 -0
  97. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.c +0 -0
  98. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.h +0 -0
  99. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxinp.c +0 -0
  100. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxout.c +0 -0
  101. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxproj.c +0 -0
  102. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxqual.c +0 -0
  103. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxrpt.c +0 -0
  104. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxtank.c +0 -0
  105. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxtoolkit.c +0 -0
  106. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxtypes.h +0 -0
  107. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxutils.c +0 -0
  108. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/msxutils.h +0 -0
  109. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/newton.c +0 -0
  110. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/newton.h +0 -0
  111. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/rk5.c +0 -0
  112. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/rk5.h +0 -0
  113. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/ros2.c +0 -0
  114. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/ros2.h +0 -0
  115. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/smatrix.c +0 -0
  116. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/Src/smatrix.h +0 -0
  117. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/EPANET-MSX/readme.txt +0 -0
  118. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/compile_linux.sh +0 -0
  119. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/EPANET/compile_macos.sh +0 -0
  120. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/__init__.py +0 -0
  121. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/data/__init__.py +0 -0
  122. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/data/benchmarks/__init__.py +0 -0
  123. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/data/benchmarks/batadal.py +0 -0
  124. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/data/benchmarks/batadal_data.py +0 -0
  125. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/data/benchmarks/battledim.py +0 -0
  126. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/data/benchmarks/battledim_data.py +0 -0
  127. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/data/benchmarks/gecco_water_quality.py +0 -0
  128. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/data/benchmarks/leakdb.py +0 -0
  129. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/data/benchmarks/leakdb_data.py +0 -0
  130. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/data/benchmarks/water_usage.py +0 -0
  131. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/gym/__init__.py +0 -0
  132. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/models/__init__.py +0 -0
  133. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/rest_api/__init__.py +0 -0
  134. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/rest_api/base_handler.py +0 -0
  135. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/rest_api/res_manager.py +0 -0
  136. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/rest_api/scada_data/__init__.py +0 -0
  137. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/rest_api/scada_data/data_handlers.py +0 -0
  138. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/rest_api/scada_data/export_handlers.py +0 -0
  139. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/rest_api/scada_data/handlers.py +0 -0
  140. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/rest_api/scenario/__init__.py +0 -0
  141. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/rest_api/scenario/event_handlers.py +0 -0
  142. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/rest_api/scenario/handlers.py +0 -0
  143. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/rest_api/scenario/simulation_handlers.py +0 -0
  144. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/rest_api/scenario/uncertainty_handlers.py +0 -0
  145. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/rest_api/server.py +0 -0
  146. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/simulation/events/__init__.py +0 -0
  147. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/simulation/events/event.py +0 -0
  148. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/simulation/events/leakages.py +0 -0
  149. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/simulation/events/quality_events.py +0 -0
  150. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/simulation/events/sensor_faults.py +0 -0
  151. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/simulation/events/system_event.py +0 -0
  152. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/simulation/parallel_simulation.py +0 -0
  153. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/simulation/scada/scada_data_export.py +0 -0
  154. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow/uncertainty/__init__.py +0 -0
  155. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/epyt_flow.egg-info/dependency_links.txt +0 -0
  156. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/setup.cfg +0 -0
  157. {epyt_flow-0.9.0 → epyt_flow-0.11.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 EPyT-Flow Developers
3
+ Copyright (c) EPyT-Flow Developers
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: epyt-flow
3
- Version: 0.9.0
3
+ Version: 0.11.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
@@ -17,10 +17,11 @@ Classifier: Programming Language :: Python :: 3.9
17
17
  Classifier: Programming Language :: Python :: 3.10
18
18
  Classifier: Programming Language :: Python :: 3.11
19
19
  Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
20
21
  Requires-Python: >=3.9
21
22
  Description-Content-Type: text/markdown
22
23
  License-File: LICENSE
23
- Requires-Dist: epyt>=1.2.0
24
+ Requires-Dist: epyt>=1.2.1
24
25
  Requires-Dist: requests>=2.31.0
25
26
  Requires-Dist: scipy>=1.11.4
26
27
  Requires-Dist: u-msgpack-python>=2.8.0
@@ -66,8 +67,8 @@ Unique features of EPyT-Flow that make it superior to other (Python) toolboxes a
66
67
  - High- and low-level interface
67
68
  - Object-orientated design that is easy to extend and customize
68
69
  - Sensor configurations
69
- - Wide variety of pre-defined events (e.g. leakages, sensor faults, actuator events, cyber-attacks, etc.)
70
- - Wide variety of pre-defined types of uncertainties (e.g. model uncertainties)
70
+ - Wide variety of pre-defined events (e.g. leakages, sensor faults, actuator events, contamination, cyber-attacks, etc.)
71
+ - Wide variety of pre-defined types of global & local uncertainties (e.g. model uncertainties)
71
72
  - Step-wise simulation and environment for training and evaluating control strategies
72
73
  - Serialization module for easy exchange of data and (scenario) configurations
73
74
  - REST API to make EPyT-Flow accessible in other applications
@@ -76,7 +77,7 @@ Unique features of EPyT-Flow that make it superior to other (Python) toolboxes a
76
77
 
77
78
  ## Installation
78
79
 
79
- EPyT-Flow supports Python 3.9 - 3.12
80
+ EPyT-Flow supports Python 3.9 - 3.13
80
81
 
81
82
  Note that [EPANET and EPANET-MSX sources](epyt_flow/EPANET/) are compiled and overwrite the binaries
82
83
  shipped by EPyT **IF** EPyT-Flow is installed on a Unix system and the *gcc* compiler is available.
@@ -183,6 +184,17 @@ Besides that, you can read in-depth about the different functionalities of EPyT-
183
184
  we recommend reading the chapters in the order in which they are presented;
184
185
  you might decide to skip some of the last chapters if their content is not relevant to you.
185
186
 
187
+ ## More Networks and Benchmarks
188
+
189
+ More Water Distribution Networks (WDNs) and benchmarks are available on the
190
+ [WaterBenchmarkHub](https://waterfutures.github.io/WaterBenchmarkHub) platform.
191
+
192
+ ## More on Control
193
+
194
+ We recommend checking out [EPyT-Control](https://github.com/WaterFutures/EPyT-Control)
195
+ if you are intersted in (data-driven) control and relates tasks such as state estimation
196
+ and event diagnosis in Water Distribution Networks.
197
+
186
198
  ## License
187
199
 
188
200
  MIT license -- see [LICENSE](LICENSE)
@@ -29,8 +29,8 @@ Unique features of EPyT-Flow that make it superior to other (Python) toolboxes a
29
29
  - High- and low-level interface
30
30
  - Object-orientated design that is easy to extend and customize
31
31
  - Sensor configurations
32
- - Wide variety of pre-defined events (e.g. leakages, sensor faults, actuator events, cyber-attacks, etc.)
33
- - Wide variety of pre-defined types of uncertainties (e.g. model uncertainties)
32
+ - Wide variety of pre-defined events (e.g. leakages, sensor faults, actuator events, contamination, cyber-attacks, etc.)
33
+ - Wide variety of pre-defined types of global & local uncertainties (e.g. model uncertainties)
34
34
  - Step-wise simulation and environment for training and evaluating control strategies
35
35
  - Serialization module for easy exchange of data and (scenario) configurations
36
36
  - REST API to make EPyT-Flow accessible in other applications
@@ -39,7 +39,7 @@ Unique features of EPyT-Flow that make it superior to other (Python) toolboxes a
39
39
 
40
40
  ## Installation
41
41
 
42
- EPyT-Flow supports Python 3.9 - 3.12
42
+ EPyT-Flow supports Python 3.9 - 3.13
43
43
 
44
44
  Note that [EPANET and EPANET-MSX sources](epyt_flow/EPANET/) are compiled and overwrite the binaries
45
45
  shipped by EPyT **IF** EPyT-Flow is installed on a Unix system and the *gcc* compiler is available.
@@ -146,6 +146,17 @@ Besides that, you can read in-depth about the different functionalities of EPyT-
146
146
  we recommend reading the chapters in the order in which they are presented;
147
147
  you might decide to skip some of the last chapters if their content is not relevant to you.
148
148
 
149
+ ## More Networks and Benchmarks
150
+
151
+ More Water Distribution Networks (WDNs) and benchmarks are available on the
152
+ [WaterBenchmarkHub](https://waterfutures.github.io/WaterBenchmarkHub) platform.
153
+
154
+ ## More on Control
155
+
156
+ We recommend checking out [EPyT-Control](https://github.com/WaterFutures/EPyT-Control)
157
+ if you are intersted in (data-driven) control and relates tasks such as state estimation
158
+ and event diagnosis in Water Distribution Networks.
159
+
149
160
  ## License
150
161
 
151
162
  MIT license -- see [LICENSE](LICENSE)
@@ -1,4 +1,4 @@
1
- epyt>=1.2.0
1
+ epyt>=1.2.1
2
2
  requests>=2.31.0
3
3
  scipy>=1.11.4
4
4
  u-msgpack-python>=2.8.0
@@ -0,0 +1 @@
1
+ 0.11.0
@@ -154,8 +154,9 @@ def load_net1(download_dir: str = get_temp_folder(), verbose: bool = True,
154
154
  """
155
155
  f_in = os.path.join(download_dir, "Net1.inp")
156
156
  url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/Net1.inp"
157
+ backup_urls = ["https://raw.githubusercontent.com/OpenWaterAnalytics/EPyT/refs/heads/dev/epyt/networks/asce-tf-wdst/Net1.inp"]
157
158
 
158
- download_if_necessary(f_in, url, verbose)
159
+ download_if_necessary(f_in, url, verbose, backup_urls)
159
160
  return load_inp(f_in, flow_units_id=flow_units_id)
160
161
 
161
162
 
@@ -201,8 +202,9 @@ def load_net2(download_dir: str = get_temp_folder(), verbose: bool = True,
201
202
  """
202
203
  f_in = os.path.join(download_dir, "Net2.inp")
203
204
  url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/Net2.inp"
205
+ backup_urls = ["https://raw.githubusercontent.com/OpenWaterAnalytics/EPyT/refs/heads/dev/epyt/networks/asce-tf-wdst/Net2.inp"]
204
206
 
205
- download_if_necessary(f_in, url, verbose)
207
+ download_if_necessary(f_in, url, verbose, backup_urls)
206
208
  return load_inp(f_in, flow_units_id=flow_units_id)
207
209
 
208
210
 
@@ -248,8 +250,9 @@ def load_net3(download_dir: str = get_temp_folder(), verbose: bool = True,
248
250
  """
249
251
  f_in = os.path.join(download_dir, "Net3.inp")
250
252
  url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/Net3.inp"
253
+ backup_urls = ["https://raw.githubusercontent.com/OpenWaterAnalytics/EPyT/refs/heads/dev/epyt/networks/asce-tf-wdst/Net3.inp"]
251
254
 
252
- download_if_necessary(f_in, url, verbose)
255
+ download_if_necessary(f_in, url, verbose, backup_urls)
253
256
  return load_inp(f_in, flow_units_id=flow_units_id)
254
257
 
255
258
 
@@ -342,8 +345,9 @@ def load_richmond(download_dir: str = get_temp_folder(), verbose: bool = True,
342
345
  """
343
346
  f_in = os.path.join(download_dir, "Richmond_standard.inp")
344
347
  url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/Richmond_standard.inp"
348
+ backup_urls = ["https://raw.githubusercontent.com/KIOS-Research/EPANET-Benchmarks/refs/heads/master/collect-epanet-inp/Richmond_standard.inp"]
345
349
 
346
- download_if_necessary(f_in, url, verbose)
350
+ download_if_necessary(f_in, url, verbose, backup_urls)
347
351
  return load_inp(f_in, flow_units_id=flow_units_id)
348
352
 
349
353
 
@@ -389,8 +393,9 @@ def load_micropolis(download_dir: str = get_temp_folder(), verbose: bool = True,
389
393
  """
390
394
  f_in = os.path.join(download_dir, "MICROPOLIS_v1.inp")
391
395
  url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/MICROPOLIS_v1.inp"
396
+ backup_urls = ["https://raw.githubusercontent.com/KIOS-Research/EPANET-Benchmarks/refs/heads/master/collect-epanet-inp/MICROPOLIS_v1.inp"]
392
397
 
393
- download_if_necessary(f_in, url, verbose)
398
+ download_if_necessary(f_in, url, verbose, backup_urls)
394
399
  return load_inp(f_in, flow_units_id=flow_units_id)
395
400
 
396
401
 
@@ -436,8 +441,9 @@ def load_balerma(download_dir: str = get_temp_folder(), verbose: bool = True,
436
441
  """
437
442
  f_in = os.path.join(download_dir, "Balerma.inp")
438
443
  url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/Balerma.inp"
444
+ backup_urls = ["https://raw.githubusercontent.com/KIOS-Research/EPANET-Benchmarks/refs/heads/master/collect-epanet-inp/Balerma.inp"]
439
445
 
440
- download_if_necessary(f_in, url, verbose)
446
+ download_if_necessary(f_in, url, verbose, backup_urls)
441
447
  return load_inp(f_in, flow_units_id=flow_units_id)
442
448
 
443
449
 
@@ -483,8 +489,9 @@ def load_rural(download_dir: str = get_temp_folder(), verbose: bool = True,
483
489
  """
484
490
  f_in = os.path.join(download_dir, "RuralNetwork.inp")
485
491
  url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/RuralNetwork.inp"
492
+ backup_urls = ["https://raw.githubusercontent.com/KIOS-Research/EPANET-Benchmarks/refs/heads/master/collect-epanet-inp/RuralNetwork.inp"]
486
493
 
487
- download_if_necessary(f_in, url, verbose)
494
+ download_if_necessary(f_in, url, verbose, backup_urls)
488
495
  return load_inp(f_in, flow_units_id=flow_units_id)
489
496
 
490
497
 
@@ -530,8 +537,9 @@ def load_bwsn1(download_dir: str = get_temp_folder(), verbose: bool = True,
530
537
  """
531
538
  f_in = os.path.join(download_dir, "BWSN_Network_1.inp")
532
539
  url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/BWSN_Network_1.inp"
540
+ backup_urls = ["https://raw.githubusercontent.com/KIOS-Research/EPANET-Benchmarks/refs/heads/master/collect-epanet-inp/BWSN_Network_1.inp"]
533
541
 
534
- download_if_necessary(f_in, url, verbose)
542
+ download_if_necessary(f_in, url, verbose, backup_urls)
535
543
  return load_inp(f_in, flow_units_id=flow_units_id)
536
544
 
537
545
 
@@ -577,8 +585,9 @@ def load_bwsn2(download_dir: str = get_temp_folder(), verbose: bool = True,
577
585
  """
578
586
  f_in = os.path.join(download_dir, "BWSN_Network_2.inp")
579
587
  url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/BWSN_Network_2.inp"
588
+ backup_urls = ["https://raw.githubusercontent.com/KIOS-Research/EPANET-Benchmarks/refs/heads/master/collect-epanet-inp/BWSN_Network_2.inp"]
580
589
 
581
- download_if_necessary(f_in, url, verbose)
590
+ download_if_necessary(f_in, url, verbose, backup_urls)
582
591
  return load_inp(f_in, flow_units_id=flow_units_id)
583
592
 
584
593
 
@@ -624,8 +633,9 @@ def load_anytown(download_dir: str = get_temp_folder(), verbose: bool = True,
624
633
  """
625
634
  f_in = os.path.join(download_dir, "Anytown.inp")
626
635
  url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/Anytown.inp"
636
+ backup_urls = ["https://raw.githubusercontent.com/OpenWaterAnalytics/EPyT/refs/heads/dev/epyt/networks/asce-tf-wdst/Anytown.inp"]
627
637
 
628
- download_if_necessary(f_in, url, verbose)
638
+ download_if_necessary(f_in, url, verbose, backup_urls)
629
639
  return load_inp(f_in, flow_units_id=flow_units_id)
630
640
 
631
641
 
@@ -670,9 +680,10 @@ def load_dtown(download_dir: str = get_temp_folder(), verbose: bool = True,
670
680
  :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`.
671
681
  """
672
682
  f_in = os.path.join(download_dir, "d-town.inp")
673
- url = "https://www.exeter.ac.uk/media/universityofexeter/emps/research/cws/downloads/d-town.inp"
683
+ url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/d-town.inp"
684
+ backup_urls = ["https://raw.githubusercontent.com/KIOS-Research/EPANET-Benchmarks/refs/heads/master/collect-epanet-inp/d-town.inp"]
674
685
 
675
- download_if_necessary(f_in, url, verbose)
686
+ download_if_necessary(f_in, url, verbose, backup_urls)
676
687
  return load_inp(f_in, flow_units_id=flow_units_id)
677
688
 
678
689
 
@@ -774,8 +785,9 @@ def load_kentucky(wdn_id: int = 1, download_dir: str = get_temp_folder(),
774
785
 
775
786
  f_in = os.path.join(download_dir, f"ky{wdn_id}.inp")
776
787
  url = f"https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/ky{wdn_id}.inp"
788
+ backup_urls = [f"https://raw.githubusercontent.com/OpenWaterAnalytics/EPyT/refs/heads/dev/epyt/networks/asce-tf-wdst/ky{wdn_id}.inp"]
777
789
 
778
- download_if_necessary(f_in, url, verbose)
790
+ download_if_necessary(f_in, url, verbose, backup_urls)
779
791
  return load_inp(f_in, flow_units_id=flow_units_id)
780
792
 
781
793
 
@@ -826,8 +838,9 @@ def load_hanoi(download_dir: str = get_temp_folder(),
826
838
  """
827
839
  f_in = os.path.join(download_dir, "Hanoi.inp")
828
840
  url = "https://filedn.com/lumBFq2P9S74PNoLPWtzxG4/EPyT-Flow/Networks/Hanoi.inp"
841
+ backup_urls = ["https://raw.githubusercontent.com/OpenWaterAnalytics/EPyT/refs/heads/dev/epyt/networks/asce-tf-wdst/Hanoi.inp"]
829
842
 
830
- download_if_necessary(f_in, url, verbose)
843
+ download_if_necessary(f_in, url, verbose, backup_urls)
831
844
  config = load_inp(f_in, flow_units_id=flow_units_id)
832
845
 
833
846
  if include_default_sensor_placement is True:
@@ -1,6 +1,8 @@
1
1
  """
2
2
  Module provides functions for registering and creating control environments.
3
3
  """
4
+ import warnings
5
+
4
6
  from .scenario_control_env import ScenarioControlEnv
5
7
 
6
8
 
@@ -18,6 +20,9 @@ def register(env_name: str, env: ScenarioControlEnv) -> None:
18
20
  env : :class:`~epyt_flow.gym.scenario_control_env.ScenarioControlEnv`
19
21
  Environment.
20
22
  """
23
+ warnings.warn("'register()' is deprecated and will be removed in future releases -- " +
24
+ "please consider switching to the EPyT-Control package")
25
+
21
26
  if env_name in environments:
22
27
  raise ValueError(f"Environment '{env_name}' already exists.")
23
28
  if not issubclass(env, ScenarioControlEnv):
@@ -41,6 +46,9 @@ def make(env_name: str, **kwds) -> ScenarioControlEnv:
41
46
  :class:`~epyt_flow.gym.scenario_control_env.ScenarioControlEnv`
42
47
  Environment.
43
48
  """
49
+ warnings.warn("'make()' is deprecated and will be removed in future releases -- " +
50
+ "please consider switching to the EPyT-Control package")
51
+
44
52
  if env_name not in environments:
45
53
  raise ValueError(f"Unknown environment '{env_name}'.")
46
54
 
@@ -107,6 +107,14 @@ class ScenarioControlEnv(ABC):
107
107
  Current SCADA data (i.e. sensor readings).
108
108
  """
109
109
  if self._scenario_sim is not None:
110
+ # Abort current simulation if any is runing
111
+ try:
112
+ next(self._sim_generator)
113
+ self._sim_generator.send(True)
114
+ except StopIteration:
115
+ pass
116
+
117
+ # Close scenario
110
118
  self._scenario_sim.close()
111
119
 
112
120
  self._scenario_sim = ScenarioSimulator(
@@ -169,9 +177,14 @@ class ScenarioControlEnv(ABC):
169
177
  raise RuntimeError("Can not execute actions affecting the hydraulics "+
170
178
  "when running EPANET-MSX")
171
179
 
172
- pump_idx = self._scenario_sim.epanet_api.getLinkPumpNameID().index(pump_id)
173
- pump_link_idx = self._scenario_sim.epanet_api.getLinkPumpIndex(pump_idx + 1)
174
- self._scenario_sim.epanet_api.setLinkStatus(pump_link_idx, status)
180
+ pump_idx = self._scenario_sim.epanet_api.getLinkPumpNameID().index(pump_id) + 1
181
+ pump_link_idx = self._scenario_sim.epanet_api.getLinkPumpIndex(pump_idx)
182
+
183
+ pattern_idx = self._scenario_sim.epanet_api.getLinkPumpPatternIndex(pump_idx)
184
+ if pattern_idx != 0:
185
+ warnings.warn(f"Can not set pump state of pump {pump_id} because a pump pattern exists")
186
+ else:
187
+ self._scenario_sim.epanet_api.setLinkStatus(pump_link_idx, status)
175
188
 
176
189
  def set_pump_speed(self, pump_id: str, speed: float) -> None:
177
190
  """
@@ -194,7 +207,7 @@ class ScenarioControlEnv(ABC):
194
207
  if pattern_idx == 0:
195
208
  warnings.warn(f"No pattern for pump '{pump_id}' found -- a new pattern is created")
196
209
  pattern_idx = self._scenario_sim.epanet_api.addPattern(f"pump_speed_{pump_id}")
197
- self._scenario_sim.epanet_api.setLinkPumpPatternIndex(pump_idx, pattern_idx)
210
+ self._scenario_sim.epanet_api.setLinkPumpPatternIndex(pump_idx + 1, pattern_idx)
198
211
 
199
212
  self._scenario_sim.epanet_api.setPattern(pattern_idx, np.array([speed]))
200
213
 
@@ -1,11 +1,16 @@
1
1
  """
2
2
  This module provides different metrics for evaluation.
3
3
  """
4
+ import warnings
4
5
  import numpy as np
5
6
  from sklearn.metrics import roc_auc_score as skelarn_roc_auc_score, f1_score as skelarn_f1_scpre, \
6
7
  mean_absolute_error, root_mean_squared_error, r2_score as sklearn_r2_score
7
8
 
8
9
 
10
+ warnings.warn("'epyt_flow.metrics' is deprecated in favor of EPyT-Control " +
11
+ "and will be removed in future releases.")
12
+
13
+
9
14
  def r2_score(y_pred: np.ndarray, y: np.ndarray) -> float:
10
15
  """
11
16
  Computes the R^2 score (also called the coefficient of determination).
@@ -1,11 +1,16 @@
1
1
  """
2
2
  Module provides a base class for event detectors.
3
3
  """
4
+ import warnings
4
5
  from abc import abstractmethod, ABC
5
6
 
6
7
  from ..simulation.scada import ScadaData
7
8
 
8
9
 
10
+ warnings.warn("'epyt_flow.models' is deprecated in favor of EPyT-Control " +
11
+ "and will be removed in future releases.")
12
+
13
+
9
14
  class EventDetector(ABC):
10
15
  """
11
16
  Base class for event detectors.
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Module provides a simple residual-based event detector that performs sensor interpolation.
3
3
  """
4
+ import warnings
4
5
  from typing import Any, Union
5
6
  from copy import deepcopy
6
7
  import numpy as np
@@ -10,6 +11,10 @@ from .event_detector import EventDetector
10
11
  from ..simulation.scada import ScadaData
11
12
 
12
13
 
14
+ warnings.warn("'epyt_flow.models' is deprecated in favor of EPyT-Control " +
15
+ "and will be removed in future releases.")
16
+
17
+
13
18
  class SensorInterpolationDetector(EventDetector):
14
19
  """
15
20
  Class implementing a residual-based event detector based on sensor interpolation.
@@ -48,6 +48,11 @@ PUMP_STATE_EVENT_ID = 28
48
48
  PUMP_SPEED_EVENT_ID = 29
49
49
  VALVE_STATE_EVENT_ID = 30
50
50
  SPECIESINJECTION_EVENT_ID = 31
51
+ SIMPLE_CONTROL_ID = 32
52
+ COMPLEX_CONTROL_ID = 33
53
+ COMPLEX_CONTROL_CONDITION_ID = 34
54
+ COMPLEX_CONTROL_ACTION_ID = 35
55
+ COLOR_SCHEMES_ID = 36
51
56
 
52
57
 
53
58
  def my_packb(data: Any) -> bytes:
@@ -1,5 +1,4 @@
1
1
  from .scenario_config import *
2
2
  from .sensor_config import *
3
3
  from .scenario_simulator import *
4
- from .scenario_visualizer import *
5
4
  from .parallel_simulation import *
@@ -134,7 +134,13 @@ class PumpStateEvent(PumpEvent, JsonSerializable):
134
134
  def apply(self, cur_time: int) -> None:
135
135
  pump_idx = self._epanet_api.getLinkPumpNameID().index(self.pump_id) + 1
136
136
  pump_link_idx = self._epanet_api.getLinkPumpIndex(pump_idx)
137
- self._epanet_api.setLinkStatus(pump_link_idx, self.__pump_state)
137
+
138
+ pattern_idx = self._epanet_api.getLinkPumpPatternIndex(pump_idx)
139
+ if pattern_idx != 0:
140
+ warnings.warn(f"Can not set pump state of pump {self.pump_id} " +
141
+ "because a pump pattern exists")
142
+ else:
143
+ self._epanet_api.setLinkStatus(pump_link_idx, self.__pump_state)
138
144
 
139
145
 
140
146
  @serializable(PUMP_SPEED_EVENT_ID, ".epytflow_pump_speed_event")
@@ -46,7 +46,7 @@ class SensorOverrideAttack(SensorReadingAttack, JsonSerializable):
46
46
  @property
47
47
  def new_sensor_values(self) -> np.ndarray:
48
48
  """
49
- Get the new sensor reading values -- i.e. these values replace the
49
+ Returns the new sensor reading values -- i.e. these values replace the
50
50
  true sensor reading values.
51
51
 
52
52
  Returns
@@ -64,7 +64,7 @@ class SensorOverrideAttack(SensorReadingAttack, JsonSerializable):
64
64
  raise TypeError("Can not compare 'SensorOverrideAttack' instance " +
65
65
  f"with '{type(other)}' instance")
66
66
 
67
- return super().__eq__(other) and self.__new_sensor_values == other.new_sensor_values
67
+ return super().__eq__(other) and np.all(self.__new_sensor_values == other.new_sensor_values)
68
68
 
69
69
  def __str__(self) -> str:
70
70
  return f"{type(self).__name__} {super().__str__()} " +\
@@ -151,6 +151,19 @@ class SensorReplayAttack(SensorReadingAttack, JsonSerializable):
151
151
  """
152
152
  return self.__sensor_data_time_window_end
153
153
 
154
+ @property
155
+ def new_sensor_values(self) -> np.ndarray:
156
+ """
157
+ Returns the new sensor reading values -- i.e. these values replace the
158
+ true sensor reading values.
159
+
160
+ Returns
161
+ -------
162
+ `np.ndarray`
163
+ New sensor readings.
164
+ """
165
+ return deepcopy(self.__new_sensor_values)
166
+
154
167
  def get_attributes(self) -> dict:
155
168
  my_attributes = {"new_sensor_values": self.__new_sensor_values,
156
169
  "replay_data_time_window_start": self.__sensor_data_time_window_start,
@@ -163,7 +176,7 @@ class SensorReplayAttack(SensorReadingAttack, JsonSerializable):
163
176
  raise TypeError("Can not compare 'SensorReplayAttack' instance " +
164
177
  f"with '{type(other)}' instance")
165
178
 
166
- return super().__eq__(other) and self.__new_sensor_values == other.new_sensor_values
179
+ return super().__eq__(other) and np.all(self.__new_sensor_values == other.new_sensor_values)
167
180
 
168
181
  def __str__(self) -> str:
169
182
  return f"{type(self).__name__} {super().__str__()} " +\
@@ -95,13 +95,28 @@ class SensorReadingEvent(Event):
95
95
  if self.__sensor_id not in sensor_config.tank_volume_sensors:
96
96
  __show_warning()
97
97
  elif self.__sensor_type == SENSOR_TYPE_NODE_BULK_SPECIES:
98
- if self.__sensor_id not in sensor_config.bulk_species_node_sensors:
98
+ sensor_present = False
99
+ for _, sensors_id in sensor_config.bulk_species_node_sensors.items():
100
+ if self.__sensor_id in sensors_id:
101
+ sensor_present = True
102
+ break
103
+ if sensor_present is False:
99
104
  __show_warning()
100
105
  elif self.__sensor_type == SENSOR_TYPE_LINK_BULK_SPECIES:
101
- if self.__sensor_id not in sensor_config.bulk_species_link_sensors:
106
+ sensor_present = False
107
+ for _, sensors_id in sensor_config.bulk_species_link_sensors.items():
108
+ if self.__sensor_id in sensors_id:
109
+ sensor_present = True
110
+ break
111
+ if sensor_present is False:
102
112
  __show_warning()
103
113
  elif self.__sensor_type == SENSOR_TYPE_SURFACE_SPECIES:
104
- if self.__sensor_id not in sensor_config.surface_species_sensors:
114
+ sensor_present = False
115
+ for _, sensors_id in sensor_config.surface_species_sensors.items():
116
+ if self.__sensor_id in sensors_id:
117
+ sensor_present = True
118
+ break
119
+ if sensor_present is False:
105
120
  __show_warning()
106
121
 
107
122
  @property
@@ -0,0 +1,5 @@
1
+ from .scada_data import *
2
+ from .scada_data_export import *
3
+ from .custom_control import *
4
+ from .simple_control import *
5
+ from .complex_control import *
@@ -0,0 +1,138 @@
1
+ """
2
+ Deprecated -- use epyt_flow.simulation.scada.custom_control instead
3
+ """
4
+ import warnings
5
+ from abc import abstractmethod, ABC
6
+ import numpy as np
7
+ import epyt
8
+
9
+ from . import ScadaData
10
+
11
+
12
+ warnings.warn("'epyt_flow.simulation.scada.advanced_control' is deprecated and will be removed " +
13
+ "in future releases -- use 'epyt_flow.simulation.scada.custom_control' instead")
14
+
15
+
16
+ class AdvancedControlModule(ABC):
17
+ """
18
+ Base class for a control module.
19
+
20
+ Attributes
21
+ ----------
22
+ epanet_api : `epyt.epanet <https://epanet-python-toolkit-epyt.readthedocs.io/en/latest/api.html#epyt.epanet.epanet>`_
23
+ API to EPANET and EPANET-MSX. Is set in :func:`init`.
24
+ """
25
+ def __init__(self, **kwds):
26
+ self._epanet_api = None
27
+
28
+ super().__init__(**kwds)
29
+
30
+ def init(self, epanet_api: epyt.epanet) -> None:
31
+ """
32
+ Initializes the control module.
33
+
34
+ Parameters
35
+ ----------
36
+ epanet_api : `epyt.epanet <https://epanet-python-toolkit-epyt.readthedocs.io/en/latest/api.html#epyt.epanet.epanet>`_
37
+ API to EPANET for implementing the control module.
38
+ """
39
+ if not isinstance(epanet_api, epyt.epanet):
40
+ raise TypeError("'epanet_api' must be an instance of 'epyt.epanet' but not of " +
41
+ f"'{type(epanet_api)}'")
42
+
43
+ self._epanet_api = epanet_api
44
+
45
+ def set_pump_status(self, pump_id: str, status: int) -> None:
46
+ """
47
+ Sets the status of a pump.
48
+
49
+ Parameters
50
+ ----------
51
+ pump_id : `str`
52
+ ID of the pump for which the status is set.
53
+ status : `int`
54
+ New status of the pump -- either active (i.e. open) or inactive (i.e. closed).
55
+
56
+ Must be one of the following constants defined in
57
+ :class:`~epyt_flow.simulation.events.actuator_events.ActuatorConstants`:
58
+
59
+ - EN_CLOSED = 0
60
+ - EN_OPEN = 1
61
+ """
62
+ pump_idx = self._epanet_api.getLinkPumpNameID().index(pump_id)
63
+ pump_link_idx = self._epanet_api.getLinkPumpIndex(pump_idx + 1)
64
+ self._epanet_api.setLinkStatus(pump_link_idx, status)
65
+
66
+ def set_pump_speed(self, pump_id: str, speed: float) -> None:
67
+ """
68
+ Sets the speed of a pump.
69
+
70
+ Parameters
71
+ ----------
72
+ pump_id : `str`
73
+ ID of the pump for which the pump speed is set.
74
+ speed : `float`
75
+ New pump speed.
76
+ """
77
+ pump_idx = self._epanet_api.getLinkPumpNameID().index(pump_id)
78
+ pattern_idx = self._epanet_api.getLinkPumpPatternIndex(pump_idx + 1)
79
+
80
+ if pattern_idx == 0:
81
+ warnings.warn(f"No pattern for pump '{pump_id}' found -- a new pattern is created")
82
+ pattern_idx = self._epanet_api.addPattern(f"pump_speed_{pump_id}")
83
+ self._epanet_api.setLinkPumpPatternIndex(pattern_idx)
84
+
85
+ self._epanet_api.setPattern(pattern_idx, np.array([speed]))
86
+
87
+ def set_valve_status(self, valve_id: str, status: int) -> None:
88
+ """
89
+ Sets the status of a valve.
90
+
91
+ Parameters
92
+ ----------
93
+ valve_id : `str`
94
+ ID of the valve for which the status is set.
95
+ status : `int`
96
+ New status of the valve -- either open or closed.
97
+
98
+ Must be one of the following constants defined in
99
+ :class:`~epyt_flow.simulation.events.actuator_events.ActuatorConstants`:
100
+
101
+ - EN_CLOSED = 0
102
+ - EN_OPEN = 1
103
+ """
104
+ valve_idx = self._epanet_api.getLinkValveNameID().index(valve_id)
105
+ valve_link_idx = self._epanet_api.getLinkValveIndex()[valve_idx]
106
+ self._epanet_api.setLinkStatus(valve_link_idx, status)
107
+
108
+ def set_node_quality_source_value(self, node_id: str, pattern_id: str,
109
+ qual_value: float) -> None:
110
+ """
111
+ Sets the quality source at a particular node to a specific value -- e.g.
112
+ setting the chlorine concentration injection to a specified value.
113
+
114
+ Parameters
115
+ ----------
116
+ node_id : `str`
117
+ ID of the node.
118
+ pattern_id : `str`
119
+ ID of the quality pattern at the specific node.
120
+ qual_value : `float`
121
+ New quality source value.
122
+ """
123
+ node_idx = self._epanet_api.getNodeIndex(node_id)
124
+ pattern_idx = self._epanet_api.getPatternIndex(pattern_id)
125
+ self._epanet_api.setNodeSourceQuality(node_idx, 1)
126
+ self._epanet_api.setPattern(pattern_idx, np.array([qual_value]))
127
+
128
+ @abstractmethod
129
+ def step(self, scada_data: ScadaData) -> None:
130
+ """
131
+ Implements the control algorithm -- i.e. mapping of sensor reading to actions.
132
+
133
+ Parameters
134
+ ----------
135
+ scada_data : :class:`~epyt_flow.simulation.scada.scada_data.ScadaData`
136
+ Sensor readings.
137
+ """
138
+ raise NotImplementedError()