epyt-flow 0.14.1__py3-none-any.whl → 0.15.0__py3-none-any.whl

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 (108) hide show
  1. epyt_flow/VERSION +1 -1
  2. epyt_flow/__init__.py +0 -37
  3. epyt_flow/data/benchmarks/battledim.py +2 -2
  4. epyt_flow/data/benchmarks/leakdb.py +12 -9
  5. epyt_flow/gym/scenario_control_env.py +32 -33
  6. epyt_flow/simulation/events/actuator_events.py +24 -18
  7. epyt_flow/simulation/events/leakages.py +59 -57
  8. epyt_flow/simulation/events/quality_events.py +21 -30
  9. epyt_flow/simulation/events/system_event.py +3 -3
  10. epyt_flow/simulation/scada/complex_control.py +14 -12
  11. epyt_flow/simulation/scada/custom_control.py +22 -21
  12. epyt_flow/simulation/scada/scada_data.py +108 -105
  13. epyt_flow/simulation/scada/simple_control.py +38 -31
  14. epyt_flow/simulation/scenario_simulator.py +368 -395
  15. epyt_flow/simulation/sensor_config.py +31 -32
  16. epyt_flow/topology.py +11 -10
  17. epyt_flow/uncertainty/model_uncertainty.py +146 -122
  18. epyt_flow/utils.py +66 -0
  19. epyt_flow/visualization/visualization_utils.py +4 -2
  20. {epyt_flow-0.14.1.dist-info → epyt_flow-0.15.0.dist-info}/METADATA +14 -19
  21. epyt_flow-0.15.0.dist-info/RECORD +65 -0
  22. epyt_flow/EPANET/EPANET/SRC_engines/AUTHORS +0 -60
  23. epyt_flow/EPANET/EPANET/SRC_engines/LICENSE +0 -21
  24. epyt_flow/EPANET/EPANET/SRC_engines/enumstxt.h +0 -151
  25. epyt_flow/EPANET/EPANET/SRC_engines/epanet.c +0 -5930
  26. epyt_flow/EPANET/EPANET/SRC_engines/epanet2.c +0 -961
  27. epyt_flow/EPANET/EPANET/SRC_engines/errors.dat +0 -79
  28. epyt_flow/EPANET/EPANET/SRC_engines/flowbalance.c +0 -186
  29. epyt_flow/EPANET/EPANET/SRC_engines/funcs.h +0 -219
  30. epyt_flow/EPANET/EPANET/SRC_engines/genmmd.c +0 -1000
  31. epyt_flow/EPANET/EPANET/SRC_engines/hash.c +0 -177
  32. epyt_flow/EPANET/EPANET/SRC_engines/hash.h +0 -28
  33. epyt_flow/EPANET/EPANET/SRC_engines/hydcoeffs.c +0 -1303
  34. epyt_flow/EPANET/EPANET/SRC_engines/hydraul.c +0 -1172
  35. epyt_flow/EPANET/EPANET/SRC_engines/hydsolver.c +0 -781
  36. epyt_flow/EPANET/EPANET/SRC_engines/hydstatus.c +0 -442
  37. epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2.h +0 -464
  38. epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_2.h +0 -1960
  39. epyt_flow/EPANET/EPANET/SRC_engines/include/epanet2_enums.h +0 -518
  40. epyt_flow/EPANET/EPANET/SRC_engines/inpfile.c +0 -884
  41. epyt_flow/EPANET/EPANET/SRC_engines/input1.c +0 -672
  42. epyt_flow/EPANET/EPANET/SRC_engines/input2.c +0 -735
  43. epyt_flow/EPANET/EPANET/SRC_engines/input3.c +0 -2265
  44. epyt_flow/EPANET/EPANET/SRC_engines/leakage.c +0 -527
  45. epyt_flow/EPANET/EPANET/SRC_engines/mempool.c +0 -146
  46. epyt_flow/EPANET/EPANET/SRC_engines/mempool.h +0 -24
  47. epyt_flow/EPANET/EPANET/SRC_engines/output.c +0 -853
  48. epyt_flow/EPANET/EPANET/SRC_engines/project.c +0 -1691
  49. epyt_flow/EPANET/EPANET/SRC_engines/quality.c +0 -695
  50. epyt_flow/EPANET/EPANET/SRC_engines/qualreact.c +0 -800
  51. epyt_flow/EPANET/EPANET/SRC_engines/qualroute.c +0 -696
  52. epyt_flow/EPANET/EPANET/SRC_engines/report.c +0 -1557
  53. epyt_flow/EPANET/EPANET/SRC_engines/rules.c +0 -1500
  54. epyt_flow/EPANET/EPANET/SRC_engines/smatrix.c +0 -871
  55. epyt_flow/EPANET/EPANET/SRC_engines/text.h +0 -508
  56. epyt_flow/EPANET/EPANET/SRC_engines/types.h +0 -928
  57. epyt_flow/EPANET/EPANET/SRC_engines/util/cstr_helper.c +0 -59
  58. epyt_flow/EPANET/EPANET/SRC_engines/util/cstr_helper.h +0 -38
  59. epyt_flow/EPANET/EPANET/SRC_engines/util/errormanager.c +0 -92
  60. epyt_flow/EPANET/EPANET/SRC_engines/util/errormanager.h +0 -39
  61. epyt_flow/EPANET/EPANET/SRC_engines/util/filemanager.c +0 -212
  62. epyt_flow/EPANET/EPANET/SRC_engines/util/filemanager.h +0 -81
  63. epyt_flow/EPANET/EPANET/SRC_engines/validate.c +0 -408
  64. epyt_flow/EPANET/EPANET-MSX/MSX_Updates.txt +0 -53
  65. epyt_flow/EPANET/EPANET-MSX/Src/dispersion.h +0 -27
  66. epyt_flow/EPANET/EPANET-MSX/Src/hash.c +0 -107
  67. epyt_flow/EPANET/EPANET-MSX/Src/hash.h +0 -28
  68. epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx.h +0 -102
  69. epyt_flow/EPANET/EPANET-MSX/Src/include/epanetmsx_export.h +0 -42
  70. epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.c +0 -937
  71. epyt_flow/EPANET/EPANET-MSX/Src/mathexpr.h +0 -39
  72. epyt_flow/EPANET/EPANET-MSX/Src/mempool.c +0 -204
  73. epyt_flow/EPANET/EPANET-MSX/Src/mempool.h +0 -24
  74. epyt_flow/EPANET/EPANET-MSX/Src/msxchem.c +0 -1285
  75. epyt_flow/EPANET/EPANET-MSX/Src/msxcompiler.c +0 -368
  76. epyt_flow/EPANET/EPANET-MSX/Src/msxdict.h +0 -42
  77. epyt_flow/EPANET/EPANET-MSX/Src/msxdispersion.c +0 -586
  78. epyt_flow/EPANET/EPANET-MSX/Src/msxerr.c +0 -116
  79. epyt_flow/EPANET/EPANET-MSX/Src/msxfile.c +0 -260
  80. epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.c +0 -175
  81. epyt_flow/EPANET/EPANET-MSX/Src/msxfuncs.h +0 -35
  82. epyt_flow/EPANET/EPANET-MSX/Src/msxinp.c +0 -1504
  83. epyt_flow/EPANET/EPANET-MSX/Src/msxout.c +0 -401
  84. epyt_flow/EPANET/EPANET-MSX/Src/msxproj.c +0 -791
  85. epyt_flow/EPANET/EPANET-MSX/Src/msxqual.c +0 -2010
  86. epyt_flow/EPANET/EPANET-MSX/Src/msxrpt.c +0 -400
  87. epyt_flow/EPANET/EPANET-MSX/Src/msxtank.c +0 -422
  88. epyt_flow/EPANET/EPANET-MSX/Src/msxtoolkit.c +0 -1164
  89. epyt_flow/EPANET/EPANET-MSX/Src/msxtypes.h +0 -551
  90. epyt_flow/EPANET/EPANET-MSX/Src/msxutils.c +0 -524
  91. epyt_flow/EPANET/EPANET-MSX/Src/msxutils.h +0 -56
  92. epyt_flow/EPANET/EPANET-MSX/Src/newton.c +0 -158
  93. epyt_flow/EPANET/EPANET-MSX/Src/newton.h +0 -34
  94. epyt_flow/EPANET/EPANET-MSX/Src/rk5.c +0 -287
  95. epyt_flow/EPANET/EPANET-MSX/Src/rk5.h +0 -39
  96. epyt_flow/EPANET/EPANET-MSX/Src/ros2.c +0 -293
  97. epyt_flow/EPANET/EPANET-MSX/Src/ros2.h +0 -35
  98. epyt_flow/EPANET/EPANET-MSX/Src/smatrix.c +0 -816
  99. epyt_flow/EPANET/EPANET-MSX/Src/smatrix.h +0 -29
  100. epyt_flow/EPANET/EPANET-MSX/readme.txt +0 -14
  101. epyt_flow/EPANET/compile_linux.sh +0 -4
  102. epyt_flow/EPANET/compile_macos.sh +0 -4
  103. epyt_flow/simulation/backend/__init__.py +0 -1
  104. epyt_flow/simulation/backend/my_epyt.py +0 -1101
  105. epyt_flow-0.14.1.dist-info/RECORD +0 -148
  106. {epyt_flow-0.14.1.dist-info → epyt_flow-0.15.0.dist-info}/WHEEL +0 -0
  107. {epyt_flow-0.14.1.dist-info → epyt_flow-0.15.0.dist-info}/licenses/LICENSE +0 -0
  108. {epyt_flow-0.14.1.dist-info → epyt_flow-0.15.0.dist-info}/top_level.txt +0 -0
epyt_flow/VERSION CHANGED
@@ -1 +1 @@
1
- 0.14.1
1
+ 0.15.0
epyt_flow/__init__.py CHANGED
@@ -1,42 +1,5 @@
1
- import subprocess
2
- import warnings
3
- import shutil
4
- import sys
5
1
  import os
6
2
 
7
3
  with open(os.path.join(os.path.dirname(__file__), 'VERSION'), encoding="utf-8") as f:
8
4
  VERSION = f.read().strip()
9
5
  __version__ = VERSION
10
-
11
-
12
- def compile_libraries_unix(lib_epanet_name: str, compile_script_name: str,
13
- gcc_name: str = "gcc") -> None:
14
- """Compile EPANET and EPANET-MSX libraries if needed."""
15
- path_to_custom_libs = os.path.join(os.path.dirname(__file__), "customlibs")
16
- path_to_lib_epanet = os.path.join(path_to_custom_libs, lib_epanet_name)
17
- path_to_epanet = os.path.join(os.path.dirname(__file__), "EPANET")
18
-
19
- update = False
20
- if os.path.isfile(path_to_lib_epanet):
21
- if os.path.getmtime(__file__) > os.path.getmtime(path_to_lib_epanet):
22
- update = True
23
-
24
- if not os.path.isfile(path_to_lib_epanet) or update:
25
- if shutil.which(gcc_name) is not None:
26
- print("Compiling EPANET and EPANET-MSX...")
27
- try:
28
- subprocess.check_call(f"cd \"{path_to_epanet}\"; bash {compile_script_name}",
29
- shell=True)
30
- print("Done")
31
- except subprocess.CalledProcessError as ex:
32
- print(f"Compilation failed\n{ex}")
33
- warnings.warn("Falling back to pre-compiled library shipped by EPyT.")
34
- else:
35
- warnings.warn("GCC is not available to compile the required libraries.\n" +
36
- "Falling back to pre-compiled library shipped by EPyT.")
37
-
38
-
39
- if sys.platform.startswith("linux"):
40
- compile_libraries_unix("libepanet2_2.so", "compile_linux.sh")
41
- elif sys.platform.startswith("darwin"):
42
- compile_libraries_unix("libepanet2_2.dylib", "compile_macos.sh", gcc_name="gcc-15")
@@ -32,7 +32,7 @@ from .battledim_data import START_TIME_TEST, START_TIME_TRAIN, LEAKS_CONFIG_TEST
32
32
  LEAKS_CONFIG_TRAIN
33
33
  from ..networks import load_ltown
34
34
  from ...simulation.events import AbruptLeakage, IncipientLeakage, Leakage
35
- from ...simulation import ScenarioConfig
35
+ from ...simulation import ScenarioConfig, EpanetConstants
36
36
  from ...topology import NetworkTopology
37
37
  from ...simulation.scada import ScadaData
38
38
  from ...utils import get_temp_folder, to_seconds, create_path_if_not_exist, download_if_necessary
@@ -463,7 +463,7 @@ def load_scenario(return_test_scenario: bool, download_dir: str = None,
463
463
  general_params = {"simulation_duration": to_seconds(days=365), # One year
464
464
  "hydraulic_time_step": to_seconds(minutes=5), # 5min time steps
465
465
  "reporting_time_step": to_seconds(minutes=5),
466
- "demand_model": {"type": "PDA", "pressure_min": 0,
466
+ "demand_model": {"type": EpanetConstants.EN_PDA, "pressure_min": 0,
467
467
  "pressure_required": 0.1,
468
468
  "pressure_exponent": 0.5}
469
469
  } | ltown_config.general_params
@@ -28,7 +28,7 @@ from ..networks import load_net1, load_hanoi
28
28
  from .leakdb_data import NET1_LEAKAGES, HANOI_LEAKAGES
29
29
  from ...utils import get_temp_folder, to_seconds, unpack_zip_archive, create_path_if_not_exist, \
30
30
  download_if_necessary
31
- from ...simulation import ScenarioSimulator, ToolkitConstants
31
+ from ...simulation import ScenarioSimulator, EpanetConstants
32
32
  from ...simulation.events import AbruptLeakage, IncipientLeakage
33
33
  from ...simulation import ScenarioConfig
34
34
  from ...simulation.scada import ScadaData
@@ -275,6 +275,8 @@ def load_data(scenarios_id: list[int], use_net1: bool, download_dir: str = None,
275
275
  create_path_if_not_exist(scenario_data_folder_in)
276
276
  unpack_zip_archive(scenario_data_file_in, scenario_data_folder_in)
277
277
 
278
+ scenario_data_folder_in = os.path.join(scenario_data_folder_in, f"Scenario-{s_id}")
279
+
278
280
  # Load and parse data
279
281
  pressure_files = list(filter(lambda d: d.endswith(".csv"),
280
282
  os.listdir(os.path.join(scenario_data_folder_in,
@@ -464,8 +466,8 @@ def load_scenarios(scenarios_id: list[int], use_net1: bool = True,
464
466
  general_params = {"simulation_duration": to_seconds(days=365), # One year
465
467
  "hydraulic_time_step": hydraulic_time_step,
466
468
  "reporting_time_step": hydraulic_time_step,
467
- "flow_units_id": ToolkitConstants.EN_CMH,
468
- "demand_model": {"type": "PDA", "pressure_min": 0,
469
+ "flow_units_id": EpanetConstants.EN_CMH,
470
+ "demand_model": {"type": EpanetConstants.EN_PDA, "pressure_min": 0,
469
471
  "pressure_required": 0.1,
470
472
  "pressure_exponent": 0.5}
471
473
  } | network_config.general_params
@@ -544,18 +546,19 @@ def load_scenarios(scenarios_id: list[int], use_net1: bool = True,
544
546
  if not os.path.exists(f_inp_in):
545
547
  with ScenarioSimulator(f_inp_in=network_config.f_inp_in) as wdn:
546
548
  wdn.set_general_parameters(**general_params)
547
- wdn.epanet_api.setTimePatternStep(hydraulic_time_step)
549
+ wdn.epanet_api.set_hydraulic_time_step(hydraulic_time_step)
548
550
 
549
- wdn.epanet_api.deletePatternsAll()
551
+ for idx in range(1, wdn.epanet_api.getcount(EpanetConstants.EN_PATCOUNT) + 1):
552
+ wdn.epanet_api.deletepattern(idx)
550
553
 
551
- reservoir_nodes_id = wdn.epanet_api.getNodeReservoirNameID()
554
+ reservoir_nodes_id = wdn.epanet_api.get_all_reservoirs_id()
552
555
  for node_id in network_config.sensor_config.nodes:
553
556
  if node_id in network_config.sensor_config.tanks or\
554
557
  node_id in reservoir_nodes_id:
555
558
  continue
556
559
 
557
- node_idx = wdn.epanet_api.getNodeIndex(node_id)
558
- base_demand = wdn.epanet_api.getNodeBaseDemands(node_idx)[1][0]
560
+ node_idx = wdn.epanet_api.get_node_idx(node_id)
561
+ base_demand = wdn.epanet_api.get_node_base_demand(node_idx)
559
562
 
560
563
  my_demand_pattern = np.array(gen_dem(download_dir))
561
564
 
@@ -563,7 +566,7 @@ def load_scenarios(scenarios_id: list[int], use_net1: bool = True,
563
566
  demand_pattern_id=f"demand_{node_id}",
564
567
  demand_pattern=my_demand_pattern)
565
568
 
566
- wdn.epanet_api.saveInputFile(f_inp_in)
569
+ wdn.epanet_api.saveinpfile(f_inp_in)
567
570
 
568
571
  # Create uncertainties
569
572
  class MyUniformUncertainty(UniformUncertainty):
@@ -6,9 +6,9 @@ import uuid
6
6
  from abc import abstractmethod, ABC
7
7
  from typing import Union
8
8
  import warnings
9
- import numpy as np
9
+ from epanet_plus import EpanetConstants
10
10
 
11
- from ..simulation import ScenarioSimulator, ScenarioConfig, ScadaData, ToolkitConstants
11
+ from ..simulation import ScenarioSimulator, ScenarioConfig, ScadaData
12
12
  from ..utils import get_temp_folder
13
13
 
14
14
 
@@ -193,14 +193,14 @@ class ScenarioControlEnv(ABC):
193
193
  raise RuntimeError("Can not execute actions affecting the hydraulics "+
194
194
  "when running EPANET-MSX")
195
195
 
196
- pump_idx = self._scenario_sim.epanet_api.getLinkPumpNameID().index(pump_id) + 1
197
- pump_link_idx = self._scenario_sim.epanet_api.getLinkPumpIndex(pump_idx)
198
-
199
- pattern_idx = self._scenario_sim.epanet_api.getLinkPumpPatternIndex(pump_idx)
196
+ pump_link_idx = self._scenario_sim.epanet_api.get_link_idx(pump_id)
197
+ pattern_idx = self._scenario_sim.epanet_api.getlinkvalue(pump_link_idx,
198
+ EpanetConstants.EN_LINKPATTERN)
200
199
  if pattern_idx != 0:
201
200
  warnings.warn(f"Can not set pump state of pump {pump_id} because a pump pattern exists")
202
201
  else:
203
- self._scenario_sim.epanet_api.setLinkStatus(pump_link_idx, status)
202
+ self._scenario_sim.epanet_api.setlinkvalue(pump_link_idx, EpanetConstants.EN_STATUS,
203
+ status)
204
204
 
205
205
  def set_pump_speed(self, pump_id: str, speed: float) -> None:
206
206
  """
@@ -217,15 +217,19 @@ class ScenarioControlEnv(ABC):
217
217
  raise RuntimeError("Can not execute actions affecting the hydraulics "+
218
218
  "when running EPANET-MSX")
219
219
 
220
- pump_idx = self._scenario_sim.epanet_api.getLinkPumpNameID().index(pump_id)
221
- pattern_idx = self._scenario_sim.epanet_api.getLinkPumpPatternIndex(pump_idx + 1)
220
+ pump_idx = self._scenario_sim.epanet_api.get_link_idx(pump_id)
221
+ pattern_idx = int(self._scenario_sim.epanet_api.getlinkvalue(pump_idx,
222
+ EpanetConstants.EN_LINKPATTERN))
222
223
 
223
224
  if pattern_idx == 0:
224
225
  warnings.warn(f"No pattern for pump '{pump_id}' found -- a new pattern is created")
225
- pattern_idx = self._scenario_sim.epanet_api.addPattern(f"pump_speed_{pump_id}")
226
- self._scenario_sim.epanet_api.setLinkPumpPatternIndex(pump_idx + 1, pattern_idx)
226
+ pattern_id = f"pump_speed_{pump_id}"
227
+ self._scenario_sim.epanet_api.add_pattern(pattern_id, [speed])
228
+ pattern_idx = self._scenario_sim.epanet_api.getpatternindex(pattern_id)
229
+ self._scenario_sim.epanet_api.setlinkvalue(pump_idx, EpanetConstants.EN_LINKPATTERN,
230
+ pattern_idx)
227
231
 
228
- self._scenario_sim.epanet_api.setPattern(pattern_idx, np.array([speed]))
232
+ self._scenario_sim.epanet_api.setpattern(pattern_idx, [speed], 1)
229
233
 
230
234
  def set_valve_status(self, valve_id: str, status: int) -> None:
231
235
  """
@@ -248,9 +252,9 @@ class ScenarioControlEnv(ABC):
248
252
  raise RuntimeError("Can not execute actions affecting the hydraulics "+
249
253
  "when running EPANET-MSX")
250
254
 
251
- valve_idx = self._scenario_sim.epanet_api.getLinkValveNameID().index(valve_id)
252
- valve_link_idx = self._scenario_sim.epanet_api.getLinkValveIndex()[valve_idx]
253
- self._scenario_sim.epanet_api.setLinkStatus(valve_link_idx, status)
255
+ valve_link_idx = self._scenario_sim.epanet_api.get_link_idx(valve_id)
256
+ self._scenario_sim.epanet_api.setlinkvalue(valve_link_idx, EpanetConstants.EN_STATUS,
257
+ status)
254
258
 
255
259
  def set_node_quality_source_value(self, node_id: str, pattern_id: str,
256
260
  qual_value: float) -> None:
@@ -271,10 +275,10 @@ class ScenarioControlEnv(ABC):
271
275
  raise RuntimeError("Can not execute actions affecting the hydraulics "+
272
276
  "when running EPANET-MSX")
273
277
 
274
- node_idx = self._scenario_sim.epanet_api.getNodeIndex(node_id)
275
- pattern_idx = self._scenario_sim.epanet_api.getPatternIndex(pattern_id)
276
- self._scenario_sim.epanet_api.setNodeSourceQuality(node_idx, 1)
277
- self._scenario_sim.epanet_api.setPattern(pattern_idx, np.array([qual_value]))
278
+ node_idx = self._scenario_sim.epanet_api.get_node_idx(node_id)
279
+ pattern_idx = self._scenario_sim.epanet_api.getpatternindex(pattern_id)
280
+ self._scenario_sim.epanet_api.setnodevalue(node_idx, EpanetConstants.EN_SOURCEQUAL, 1)
281
+ self._scenario_sim.epanet_api.set_pattern(pattern_idx, [qual_value])
278
282
 
279
283
  def set_node_species_source_value(self, species_id: str, node_id: str, source_type: int,
280
284
  pattern_id: str, source_strength: float) -> None:
@@ -290,7 +294,7 @@ class ScenarioControlEnv(ABC):
290
294
  ID of the node.
291
295
  source_type : `int`
292
296
  Type of the external species injection source -- must be one of
293
- the following EPANET toolkit constants:
297
+ the following EPANET constants:
294
298
 
295
299
  - EN_CONCEN = 0
296
300
  - EN_MASS = 1
@@ -312,19 +316,14 @@ class ScenarioControlEnv(ABC):
312
316
  if self._scenario_sim.f_msx_in is None:
313
317
  raise RuntimeError("You are not running EPANET-MSX")
314
318
 
315
- source_type_ = "None"
316
- if source_type == ToolkitConstants.EN_CONCEN:
317
- source_type_ = "CONCEN"
318
- elif source_type == ToolkitConstants.EN_MASS:
319
- source_type_ = "MASS"
320
- elif source_type == ToolkitConstants.EN_SETPOINT:
321
- source_type_ = "SETPOINT"
322
- elif source_type == ToolkitConstants.EN_FLOWPACED:
323
- source_type_ = "FLOWPACED"
324
-
325
- self._scenario_sim.epanet_api.setMSXPattern(pattern_id, [1])
326
- self._scenario_sim.epanet_api.setMSXSources(node_id, species_id, source_type_,
327
- source_strength, pattern_id)
319
+ pattern_idx = self._scenario_sim.epanet_api.MSXgetindex(EpanetConstants.MSX_PATTERN,
320
+ pattern_id)
321
+ self._scenario_sim.epanet_api.MSXsetpattern(pattern_idx, [1], 1)
322
+
323
+ node_idx = self._scenario_sim.epanet_api.get_node_idx(node_id)
324
+ species_idx = self._scenario_sim.epanet_api.get_msx_species_idx(species_id)
325
+ self._scenario_sim.epanet_api.MSXsetsource(node_idx, species_idx, source_type,
326
+ source_strength, pattern_idx)
328
327
 
329
328
  @abstractmethod
330
329
  def step(self, *actions) -> Union[tuple[ScadaData, float, bool], tuple[ScadaData, float]]:
@@ -2,8 +2,7 @@
2
2
  Module provides implementations of different types of actuator events.
3
3
  """
4
4
  import warnings
5
- from epyt.epanet import epanet
6
- import numpy as np
5
+ from epanet_plus import EPyT, EpanetConstants
7
6
 
8
7
  from .system_event import SystemEvent
9
8
  from ...serialization import serializable, JsonSerializable, PUMP_STATE_EVENT_ID, \
@@ -20,9 +19,15 @@ class ActuatorConstants:
20
19
  Valve or pump is closed.
21
20
  EN_OPEN
22
21
  Valve or pump is open -- i.e. active.
22
+ EN_SET_CLOSED
23
+ Link set closed indicator
24
+ EN_SET_OPEN
25
+ Link set open indicator
23
26
  """
24
27
  EN_CLOSED = 0
25
28
  EN_OPEN = 1
29
+ EN_SET_CLOSED = -1e10
30
+ EN_SET_OPEN = 1e10
26
31
 
27
32
 
28
33
  class ActuatorEvent(SystemEvent):
@@ -59,8 +64,8 @@ class PumpEvent(ActuatorEvent):
59
64
 
60
65
  super().__init__(**kwds)
61
66
 
62
- def init(self, epanet_api: epanet) -> None:
63
- if self.__pump_id not in epanet_api.getLinkPumpNameID():
67
+ def init(self, epanet_api: EPyT) -> None:
68
+ if self.__pump_id not in epanet_api.get_all_pumps_id():
64
69
  raise ValueError(f"Invalid pump ID '{self.__pump_id}'")
65
70
 
66
71
  super().init(epanet_api)
@@ -132,15 +137,15 @@ class PumpStateEvent(PumpEvent, JsonSerializable):
132
137
  return self.__pump_state
133
138
 
134
139
  def apply(self, cur_time: int) -> None:
135
- pump_idx = self._epanet_api.getLinkPumpNameID().index(self.pump_id) + 1
136
- pump_link_idx = self._epanet_api.getLinkPumpIndex(pump_idx)
140
+ pump_link_idx = self._epanet_api.getlinkindex(self.pump_id)
137
141
 
138
- pattern_idx = self._epanet_api.getLinkPumpPatternIndex(pump_idx)
142
+ pattern_idx = self._epanet_api.getlinkvalue(pump_link_idx, EpanetConstants.EN_LINKPATTERN)
139
143
  if pattern_idx != 0:
140
144
  warnings.warn(f"Can not set pump state of pump {self.pump_id} " +
141
145
  "because a pump pattern exists")
142
146
  else:
143
- self._epanet_api.setLinkStatus(pump_link_idx, self.__pump_state)
147
+ self._epanet_api.setlinkvalue(pump_link_idx, EpanetConstants.EN_STATUS,
148
+ self.__pump_state)
144
149
 
145
150
 
146
151
  @serializable(PUMP_SPEED_EVENT_ID, ".epytflow_pump_speed_event")
@@ -180,15 +185,17 @@ class PumpSpeedEvent(PumpEvent, JsonSerializable):
180
185
  return self.__pump_speed
181
186
 
182
187
  def apply(self, cur_time: int) -> None:
183
- pump_idx = self._epanet_api.getLinkPumpNameID().index(self.pump_id) + 1
184
- pattern_idx = self._epanet_api.getLinkPumpPatternIndex(pump_idx)
188
+ pump_idx = self._epanet_api.get_link_idx(self.pump_id)
189
+ pattern_idx = self._epanet_api.getlinkvalue(pump_idx, EpanetConstants.EN_LINKPATTERN)
185
190
 
186
191
  if pattern_idx == 0:
187
192
  warnings.warn(f"No pattern for pump '{self.pump_id}' found -- a new pattern is created")
188
- pattern_idx = self._epanet_api.addPattern(f"pump_speed_{self.pump_id}")
189
- self._epanet_api.setLinkPumpPatternIndex(pump_idx, pattern_idx)
193
+ pattern_id = f"pump_speed_{self.pump_id}"
194
+ self._epanet_api.add_pattern(pattern_id, [self.__pump_speed])
195
+ pattern_idx = self._epanet_api.getpatternindex(pattern_id)
196
+ self._epanet_api.setlinkvalue(pump_idx, EpanetConstants.EN_LINKPATTERN, pattern_idx)
190
197
 
191
- self._epanet_api.setPattern(pattern_idx, np.array([self.__pump_speed]))
198
+ self._epanet_api.setpattern(pattern_idx, [self.__pump_speed], 1)
192
199
 
193
200
 
194
201
  @serializable(VALVE_STATE_EVENT_ID, ".epytflow_valve_state_event")
@@ -222,8 +229,8 @@ class ValveStateEvent(ActuatorEvent, JsonSerializable):
222
229
 
223
230
  super().__init__(**kwds)
224
231
 
225
- def init(self, epanet_api: epanet) -> None:
226
- if self.__valve_id not in epanet_api.getLinkValveNameID():
232
+ def init(self, epanet_api: EPyT) -> None:
233
+ if self.__valve_id not in epanet_api.get_all_valves_id():
227
234
  raise ValueError(f"Invalid valve ID '{self.__valve_id}'")
228
235
 
229
236
  super().init(epanet_api)
@@ -260,6 +267,5 @@ class ValveStateEvent(ActuatorEvent, JsonSerializable):
260
267
  return self.__valve_state
261
268
 
262
269
  def apply(self, cur_time: int) -> None:
263
- valve_idx = self._epanet_api.getLinkValveNameID().index(self.__valve_id) + 1
264
- valve_link_idx = self._epanet_api.getLinkValveIndex(valve_idx)
265
- self._epanet_api.setLinkStatus(valve_link_idx, self.__valve_state)
270
+ valve_link_idx = self._epanet_api.get_link_idx(self.__valve_id)
271
+ self._epanet_api.setlinkvalue(valve_link_idx, EpanetConstants.EN_STATUS, self.__valve_state)
@@ -4,8 +4,7 @@ Module provides classes for implementing leakages.
4
4
  from copy import deepcopy
5
5
  import math
6
6
  import numpy as np
7
- import epyt
8
- from epyt.epanet import ToolkitConstants
7
+ from epanet_plus import EPyT, EpanetConstants
9
8
 
10
9
  from .system_event import SystemEvent
11
10
  from ...serialization import serializable, JsonSerializable, \
@@ -231,10 +230,10 @@ class Leakage(SystemEvent, JsonSerializable):
231
230
  `float`
232
231
  Leak emitter coefficient.
233
232
  """
234
- flow_unit = self._epanet_api.api.ENgetflowunits()
235
- if flow_unit == ToolkitConstants.EN_CMH:
233
+ flow_unit = self._epanet_api.getflowunits()
234
+ if flow_unit == EpanetConstants.EN_CMH:
236
235
  g = 127137600 # m/h^2
237
- elif flow_unit == ToolkitConstants.EN_CFS:
236
+ elif flow_unit == EpanetConstants.EN_CFS:
238
237
  g = 32.17405 # feet/s^2
239
238
  else:
240
239
  raise ValueError("Leakages are only implemented for the following flow units:\n" +
@@ -248,30 +247,30 @@ class Leakage(SystemEvent, JsonSerializable):
248
247
  def _get_new_node_id(self) -> str:
249
248
  return f"leak_node_{self.__link_id}"
250
249
 
251
- def init(self, epanet_api: epyt.epanet) -> None:
250
+ def init(self, epanet_api: EPyT) -> None:
252
251
  super().init(epanet_api)
253
252
 
254
253
  # Split pipe if leak is placed at a link/pipe
255
254
  if self.__link_id is not None:
256
- if self.__link_id not in self._epanet_api.getLinkNameID():
255
+ if self.__link_id not in self._epanet_api.get_all_links_id():
257
256
  raise ValueError(f"Unknown link/pipe '{self.__link_id}'")
258
257
 
259
258
  new_link_id = self._get_new_link_id()
260
259
  new_node_id = self._get_new_node_id()
261
260
 
262
- all_nodes_id = self._epanet_api.getNodeNameID()
261
+ all_nodes_id = self._epanet_api.get_all_nodes_id()
263
262
  if new_node_id in all_nodes_id:
264
263
  raise ValueError(f"There is already a leak at pipe {self.link_id}")
265
264
 
266
- self._epanet_api.splitPipe(self.link_id, new_link_id, new_node_id)
267
- self.__leaky_node_idx = self._epanet_api.getNodeIndex(new_node_id)
265
+ self._epanet_api.split_pipe(self.link_id, new_link_id, new_node_id)
266
+ self.__leaky_node_idx = self._epanet_api.get_node_idx(new_node_id)
268
267
  else:
269
- if self.__node_id not in self._epanet_api.getNodeNameID():
268
+ if self.__node_id not in self._epanet_api.get_all_nodes_id():
270
269
  raise ValueError(f"Unknown node '{self.__node_id}'")
271
270
 
272
- self.__leaky_node_idx = self._epanet_api.getNodeIndex(self.__node_id)
271
+ self.__leaky_node_idx = self._epanet_api.get_node_idx(self.__node_id)
273
272
 
274
- self._epanet_api.setNodeEmitterCoeff(self.__leaky_node_idx, 0.)
273
+ self._epanet_api.setnodevalue(self.__leaky_node_idx, EpanetConstants.EN_EMITTER, 0.)
275
274
 
276
275
  # Compute leak emitter coefficient
277
276
  self.__leak_emitter_coef = self.compute_leak_emitter_coefficient(
@@ -279,51 +278,54 @@ class Leakage(SystemEvent, JsonSerializable):
279
278
 
280
279
  def cleanup(self) -> None:
281
280
  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)
281
+ pipe_idx = self._epanet_api.get_link_idx(self.__link_id)
282
+ link_diameter = self._epanet_api.get_link_diameter(pipe_idx)
283
+ link_length = self._epanet_api.get_link_length(pipe_idx)
284
+ link_roughness_coeff = self._epanet_api.get_link_roughness(pipe_idx)
285
+ link_minor_loss_coeff = self._epanet_api.get_link_minorloss(pipe_idx)
286
+ link_initial_status = self._epanet_api.get_link_init_status(pipe_idx)
287
+ link_initial_setting = self._epanet_api.get_link_init_setting(pipe_idx)
288
+ link_bulk_reaction_coeff = self._epanet_api.get_link_bulk_raction_coeff(pipe_idx)
289
+ link_wall_reaction_coeff = self._epanet_api.get_link_wall_raction_coeff(pipe_idx)
290
+
291
+ node_a_idx = int(self._epanet_api.getlinknodes(pipe_idx)[0])
292
+ node_b_idx = int(self._epanet_api.getlinknodes(self._epanet_api.get_link_idx(self._get_new_link_id()))[1])
293
+
294
+ self._epanet_api.deletelink(self._epanet_api.get_link_idx(self._get_new_link_id()),
295
+ EpanetConstants.EN_UNCONDITIONAL)
296
+ self._epanet_api.deletelink(self._epanet_api.get_link_idx(self.__link_id),
297
+ EpanetConstants.EN_UNCONDITIONAL)
298
+ self._epanet_api.deletenode(self._epanet_api.get_node_idx(self._get_new_node_id()),
299
+ EpanetConstants.EN_UNCONDITIONAL)
300
+
301
+ self._epanet_api.addlink(self.__link_id, EpanetConstants.EN_PIPE,
302
+ self._epanet_api.get_node_id(node_a_idx),
303
+ self._epanet_api.get_node_id(node_b_idx))
304
+ link_idx = self._epanet_api.get_link_idx(self.__link_id)
305
+ self._epanet_api.setlinknodes(link_idx, node_a_idx, node_b_idx)
306
+ self._epanet_api.setlinktype(link_idx, EpanetConstants.EN_PIPE,
307
+ EpanetConstants.EN_UNCONDITIONAL)
308
+ self._epanet_api.setpipedata(link_idx, link_length, link_diameter, link_roughness_coeff,
309
+ link_minor_loss_coeff)
310
+ self._epanet_api.setlinkvalue(link_idx, EpanetConstants.EN_INITSTATUS,
311
+ link_initial_status)
312
+ self._epanet_api.setlinkvalue(link_idx, EpanetConstants.EN_INITSETTING,
313
+ link_initial_setting)
314
+ self._epanet_api.setlinkvalue(link_idx, EpanetConstants.EN_KBULK,
315
+ link_bulk_reaction_coeff)
316
+ self._epanet_api.setlinkvalue(link_idx, EpanetConstants.EN_KWALL,
317
+ link_wall_reaction_coeff)
316
318
 
317
319
  def reset(self) -> None:
318
320
  self.__time_pattern_idx = 0
319
321
 
320
322
  def exit(self, cur_time) -> None:
321
- self._epanet_api.setNodeEmitterCoeff(self.__leaky_node_idx, 0.)
323
+ self._epanet_api.setnodevalue(self.__leaky_node_idx, EpanetConstants.EN_EMITTER, 0.)
322
324
 
323
325
  def apply(self, cur_time: int) -> None:
324
- self._epanet_api.setNodeEmitterCoeff(self.__leaky_node_idx,
325
- self.__leak_emitter_coef *
326
- self.__profile[self.__time_pattern_idx])
326
+ self._epanet_api.setnodevalue(self.__leaky_node_idx, EpanetConstants.EN_EMITTER,
327
+ self.__leak_emitter_coef *
328
+ self.__profile[self.__time_pattern_idx])
327
329
  self.__time_pattern_idx += 1
328
330
 
329
331
 
@@ -357,12 +359,12 @@ class AbruptLeakage(Leakage):
357
359
  else:
358
360
  super().__init__(link_id=link_id, diameter=diameter, area=area, **kwds)
359
361
 
360
- def init(self, epanet_api: epyt.epanet) -> None:
362
+ def init(self, epanet_api: EPyT) -> None:
361
363
  super().init(epanet_api)
362
364
 
363
365
  # Set pattern
364
- total_sim_duration = self._epanet_api.getTimeSimulationDuration()
365
- time_step = self._epanet_api.getTimeHydraulicStep()
366
+ total_sim_duration = self._epanet_api.get_simulation_duration()
367
+ time_step = self._epanet_api.get_hydraulic_time_step()
366
368
 
367
369
  if self.end_time is not None:
368
370
  n_leaky_time_points = math.ceil((self.end_time - self.start_time) / time_step)
@@ -436,12 +438,12 @@ class IncipientLeakage(Leakage):
436
438
  def __str__(self) -> str:
437
439
  return f"{super().__str__()} peak_time: {self.peak_time}"
438
440
 
439
- def init(self, epanet_api: epyt.epanet) -> None:
441
+ def init(self, epanet_api: EPyT) -> None:
440
442
  super().init(epanet_api)
441
443
 
442
444
  # Set pattern
443
- total_sim_duration = self._epanet_api.getTimeSimulationDuration()
444
- time_step = self._epanet_api.getTimeHydraulicStep()
445
+ total_sim_duration = self._epanet_api.get_simulation_duration()
446
+ time_step = self._epanet_api.get_hydraulic_time_step()
445
447
 
446
448
  if self.end_time is not None:
447
449
  n_leaky_time_points = math.ceil((self.end_time - self.start_time) / time_step)
@@ -5,8 +5,7 @@ from copy import deepcopy
5
5
  import warnings
6
6
  import math
7
7
  import numpy as np
8
- import epyt
9
- from epyt.epanet import ToolkitConstants
8
+ from epanet_plus import EPyT, EpanetConstants
10
9
 
11
10
  from .system_event import SystemEvent
12
11
  from ...serialization import serializable, JsonSerializable, \
@@ -31,7 +30,7 @@ class SpeciesInjectionEvent(SystemEvent, JsonSerializable):
31
30
  Note that the pattern time step is equivalent to the EPANET pattern time step.
32
31
  source_type : `int`
33
32
  Type of the bulk species injection source -- must be one of
34
- the following EPANET toolkit constants:
33
+ the following EPANET constants:
35
34
 
36
35
  - EN_CONCEN = 0
37
36
  - EN_MASS = 1
@@ -140,18 +139,18 @@ class SpeciesInjectionEvent(SystemEvent, JsonSerializable):
140
139
  def _get_pattern_id(self) -> str:
141
140
  return f"{self.__species_id}_{self.__node_id}"
142
141
 
143
- def init(self, epanet_api: epyt.epanet) -> None:
142
+ def init(self, epanet_api: EPyT) -> None:
144
143
  super().init(epanet_api)
145
144
 
146
145
  # Check parameters
147
- if self.__species_id not in self._epanet_api.getMSXSpeciesNameID():
146
+ if self.__species_id not in self._epanet_api.get_all_msx_species_id():
148
147
  raise ValueError(f"Unknown species '{self.__species_id}'")
149
- if self.__node_id not in self._epanet_api.getNodeNameID():
148
+ if self.__node_id not in self._epanet_api.get_all_nodes_id():
150
149
  raise ValueError(f"Unknown node '{self.__node_id}'")
151
150
 
152
151
  # Create final injection strength pattern
153
- total_sim_duration = self._epanet_api.getTimeSimulationDuration()
154
- time_step = self._epanet_api.getTimeHydraulicStep()
152
+ total_sim_duration = self._epanet_api.get_simulation_duration()
153
+ time_step = self._epanet_api.get_hydraulic_time_step()
155
154
 
156
155
  pattern = np.zeros(math.ceil(total_sim_duration / time_step))
157
156
 
@@ -170,34 +169,26 @@ class SpeciesInjectionEvent(SystemEvent, JsonSerializable):
170
169
  injection_time_start_idx + injection_pattern_length] = injection_pattern
171
170
 
172
171
  # Create injection
173
- source_type_ = "None"
174
- if self.__source_type == ToolkitConstants.EN_CONCEN:
175
- source_type_ = "CONCEN"
176
- elif self.__source_type == ToolkitConstants.EN_MASS:
177
- source_type_ = "MASS"
178
- elif self.__source_type == ToolkitConstants.EN_SETPOINT:
179
- source_type_ = "SETPOINT"
180
- elif self.__source_type == ToolkitConstants.EN_FLOWPACED:
181
- source_type_ = "FLOWPACED"
182
-
183
172
  pattern_id = self._get_pattern_id()
184
- if pattern_id in self._epanet_api.getMSXPatternsNameID():
185
- node_idx = self._epanet_api.getNodeIndex(self.__node_id)
186
- species_idx, = self._epanet_api.getMSXSpeciesIndex([self.__species_id])
187
- cur_source_type = self._epanet_api.msx.MSXgetsource(node_idx, species_idx)
188
- if cur_source_type[0] != source_type_:
173
+ if pattern_id in [self._epanet_api.MSXgetID(EpanetConstants.MSX_PATTERN, pattern_idx + 1)
174
+ for pattern_idx in
175
+ range(self._epanet_api.MSXgetcount(EpanetConstants.MSX_PATTERN))]:
176
+ node_idx = self._epanet_api.get_node_idx(self.__node_id)
177
+ species_idx = self._epanet_api.get_msx_species_idx(self.__species_id)
178
+ cur_source_type = self._epanet_api.MSXgetsource(node_idx, species_idx)
179
+ if cur_source_type[0] != self.__source_type:
189
180
  raise ValueError("Source type does not match existing source type")
190
181
 
191
182
  # Add new injection amount to existing injection --
192
183
  # i.e. two injection events at the same node
193
- pattern_idx, = self._epanet_api.getMSXPatternsIndex([pattern_id])
194
- cur_pattern = self._epanet_api.getMSXPattern()[pattern_idx - 1]
195
- cur_pattern += pattern
196
- self._epanet_api.setMSXPattern(pattern_idx, cur_pattern)
184
+ pattern_idx = self._epanet_api.MSXgetindex(EpanetConstants.MSX_PATTERN, pattern_id)
185
+ cur_pattern = self._epanet_api.get_msx_pattern(pattern_idx)
186
+ cur_pattern = np.array(cur_pattern) + pattern
187
+ self._epanet_api.MSXsetpattern(pattern_idx, cur_pattern.tolist(), len(cur_pattern))
197
188
  else:
198
- self._epanet_api.addMSXPattern(pattern_id, pattern)
199
- self._epanet_api.setMSXSources(self.__node_id, self.__species_id, source_type_, 1,
200
- pattern_id)
189
+ self._epanet_api.add_msx_pattern(pattern_id, pattern.tolist())
190
+ self._epanet_api.set_msx_source(self.__node_id, self.__species_id, self.__source_type, 1,
191
+ pattern_id)
201
192
 
202
193
  def cleanup(self) -> None:
203
194
  warnings.warn("Can not undo SpeciesInjectionEvent -- " +