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
@@ -17,7 +17,7 @@ import math
17
17
  import uuid
18
18
  import numpy as np
19
19
  from tqdm import tqdm
20
- from epyt.epanet import ToolkitConstants
20
+ from epanet_plus import EPyT, EpanetConstants
21
21
 
22
22
  from .scenario_config import ScenarioConfig
23
23
  from .sensor_config import SensorConfig, areaunit_to_id, massunit_to_id, qualityunit_to_id, \
@@ -31,7 +31,7 @@ from ..uncertainty import ModelUncertainty, SensorNoise
31
31
  from .events import SystemEvent, Leakage, ActuatorEvent, SensorFault, SensorReadingAttack, \
32
32
  SensorReadingEvent
33
33
  from .scada import ScadaData, CustomControlModule, SimpleControlModule, ComplexControlModule, \
34
- RuleCondition, RuleAction, ActuatorConstants, EN_R_ACTION_SETTING
34
+ RuleCondition, RuleAction, ActuatorConstants, EN_R_ACTION_SETTING, RULESTATUS
35
35
  from ..topology import NetworkTopology, UNITS_SIMETRIC, UNITS_USCUSTOM
36
36
  from ..utils import get_temp_folder
37
37
 
@@ -136,23 +136,6 @@ class ScenarioSimulator():
136
136
  self.__running_simulation = False
137
137
  self.__uncertainties_applied = False
138
138
 
139
- # Check availability of custom EPANET libraries
140
- custom_epanet_lib = None
141
- custom_epanetmsx_lib = None
142
- if sys.platform.startswith("linux") or sys.platform.startswith("darwin") :
143
- path_to_custom_libs = os.path.join(pathlib.Path(__file__).parent.resolve(),
144
- "..", "customlibs")
145
-
146
- libepanet_name = "libepanet2_2.so" if sys.platform.startswith("linux") \
147
- else "libepanet2_2.dylib"
148
- libepanetmsx_name = "libepanetmsx2_2_0.so" if sys.platform.startswith("linux") \
149
- else "libepanetmsx2_2_0.dylib"
150
-
151
- if os.path.isfile(os.path.join(path_to_custom_libs, libepanet_name)):
152
- custom_epanet_lib = os.path.join(path_to_custom_libs, libepanet_name)
153
- if os.path.isfile(os.path.join(path_to_custom_libs, libepanetmsx_name)):
154
- custom_epanetmsx_lib = os.path.join(path_to_custom_libs, libepanetmsx_name)
155
-
156
139
  # Workaround for EPyT bug concerning parallel simulations (see EPyT issue #54):
157
140
  # 1. Create random tmp folder (make sure it is unique!)
158
141
  # 2. Copy .inp and .msx file there
@@ -183,14 +166,11 @@ class ScenarioSimulator():
183
166
  else:
184
167
  my_f_msx_in = None
185
168
 
186
- from .backend import EPyT # Workaround: Sphinx autodoc "importlib.import_module TypeError: __mro_entries__"
187
- self.epanet_api = EPyT(my_f_inp_in, ph=self.__f_msx_in is None,
188
- customlib=custom_epanet_lib, loadfile=True,
189
- display_msg=epanet_verbose,
190
- display_warnings=False)
169
+ from epanet_plus import EPyT # Workaround: Sphinx autodoc "importlib.import_module TypeError: __mro_entries__"
170
+ self.epanet_api = EPyT(my_f_inp_in, use_project=self.__f_msx_in is None)
191
171
 
192
172
  if self.__f_msx_in is not None:
193
- self.epanet_api.loadMSXFile(my_f_msx_in, customMSXlib=custom_epanetmsx_lib)
173
+ self.epanet_api.load_msx_file(my_f_msx_in)
194
174
 
195
175
  # Do not raise exceptions in the case of EPANET warnings and errors
196
176
  self.epanet_api.set_error_handling(raise_exception_on_error=raise_exception_on_error,
@@ -225,8 +205,8 @@ class ScenarioSimulator():
225
205
  valve_id_to_idx: dict = None, pump_id_to_idx: dict = None,
226
206
  tank_id_to_idx: dict = None, bulkspecies_id_to_idx: dict = None,
227
207
  surfacespecies_id_to_idx: dict = None) -> SensorConfig:
228
- flow_unit = self.epanet_api.api.ENgetflowunits()
229
- quality_unit = qualityunit_to_id(self.epanet_api.getQualityInfo().QualityChemUnits)
208
+ flow_unit = self.epanet_api.getflowunits()
209
+ quality_unit = qualityunit_to_id(self.epanet_api.get_quality_info()["chemUnits"])
230
210
  bulk_species = []
231
211
  surface_species = []
232
212
  bulk_species_mass_unit = []
@@ -234,23 +214,22 @@ class ScenarioSimulator():
234
214
  surface_species_area_unit = None
235
215
 
236
216
  if self.__f_msx_in is not None:
237
- surface_species_area_unit = areaunit_to_id(self.epanet_api.getMSXAreaUnits())
217
+ surface_species_area_unit = areaunit_to_id("FT2")#self.epanet_api.get_msx_options()["areaUnits"])
238
218
 
239
- for species_id, species_type, mass_unit in zip(self.epanet_api.getMSXSpeciesNameID(),
240
- self.epanet_api.getMSXSpeciesType(),
241
- self.epanet_api.getMSXSpeciesUnits()):
242
- if species_type == "BULK":
219
+ for species_id, species_info in zip(self.epanet_api.get_all_msx_species_id(),
220
+ self.epanet_api.get_all_msx_species_info()):
221
+ if species_info["type"] == EpanetConstants.MSX_BULK:
243
222
  bulk_species.append(species_id)
244
- bulk_species_mass_unit.append(massunit_to_id(mass_unit))
245
- elif species_type == "WALL":
223
+ bulk_species_mass_unit.append(massunit_to_id(species_info["units"]))
224
+ elif species_info["type"] == EpanetConstants.MSX_WALL:
246
225
  surface_species.append(species_id)
247
- surface_species_mass_unit.append(massunit_to_id(mass_unit))
226
+ surface_species_mass_unit.append(massunit_to_id(species_info["units"]))
248
227
 
249
- return SensorConfig(nodes=self.epanet_api.getNodeNameID(),
250
- links=self.epanet_api.getLinkNameID(),
251
- valves=self.epanet_api.getLinkValveNameID(),
252
- pumps=self.epanet_api.getLinkPumpNameID(),
253
- tanks=self.epanet_api.getNodeTankNameID(),
228
+ return SensorConfig(nodes=self.epanet_api.get_all_nodes_id(),
229
+ links=self.epanet_api.get_all_links_id(),
230
+ valves=self.epanet_api.get_all_valves_id(),
231
+ pumps=self.epanet_api.get_all_pumps_id(),
232
+ tanks=self.epanet_api.get_all_tanks_id(),
254
233
  bulk_species=bulk_species,
255
234
  surface_species=surface_species,
256
235
  flow_unit=flow_unit,
@@ -490,31 +469,18 @@ class ScenarioSimulator():
490
469
  def _parse_simple_control_rules(self) -> list[SimpleControlModule]:
491
470
  controls = []
492
471
 
493
- for idx in self.epanet_api.getControls():
494
- control = self.epanet_api.getControls(idx)
472
+ for idx in range(self.epanet_api.get_num_controls()):
473
+ cond_type, link_idx, link_status, node_idx, level = \
474
+ self.epanet_api.getcontrol(idx + 1)
495
475
 
496
- if control.Setting == "OPEN":
497
- link_status = ActuatorConstants.EN_OPEN
498
- else:
499
- link_status = ActuatorConstants.EN_CLOSED
500
-
501
- if control.Type == "LOWLEVEL":
502
- cond_type = ToolkitConstants.EN_LOWLEVEL
503
- elif control.Type == "HIGHLEVEL":
504
- cond_type = ToolkitConstants.EN_HILEVEL
505
- elif control.Type == "TIMER":
506
- cond_type = ToolkitConstants.EN_TIMER
507
- elif control.Type == "TIMEOFDAY":
508
- cond_type = ToolkitConstants.EN_TIMEOFDAY
509
-
510
- if control.NodeID is not None:
511
- cond_var_value = control.NodeID
512
- cond_comp_value = control.Value
476
+ if node_idx != 0:
477
+ cond_var_value = self.epanet_api.get_node_id(node_idx)
478
+ cond_comp_value = level
513
479
  else:
514
- if cond_type == ToolkitConstants.EN_TIMER:
515
- cond_var_value = int(control.Value / 3600)
516
- elif cond_type == ToolkitConstants.EN_TIMEOFDAY:
517
- sec = control.Value
480
+ if cond_type == EpanetConstants.EN_TIMER:
481
+ cond_var_value = int(level / 3600)
482
+ elif cond_type == EpanetConstants.EN_TIMEOFDAY:
483
+ sec = level
518
484
  if sec <= 43200:
519
485
  cond_var_value = \
520
486
  f"{':'.join(str(timedelta(seconds=sec)).split(':')[:2])} AM"
@@ -524,7 +490,7 @@ class ScenarioSimulator():
524
490
  f"{':'.join(str(timedelta(seconds=sec)).split(':')[:2])} PM"
525
491
  cond_comp_value = None
526
492
 
527
- controls.append(SimpleControlModule(link_id=control.LinkID,
493
+ controls.append(SimpleControlModule(link_id=self.epanet_api.get_link_id(link_idx),
528
494
  link_status=link_status,
529
495
  cond_type=cond_type,
530
496
  cond_var_value=cond_var_value,
@@ -535,35 +501,30 @@ class ScenarioSimulator():
535
501
  def _parse_complex_control_rules(self) -> list[ComplexControlModule]:
536
502
  controls = []
537
503
 
538
- rules = self.epanet_api.getRules()
539
- for rule_idx, rule in rules.items():
540
- rule_info = self.epanet_api.getRuleInfo(rule_idx)
541
-
542
- rule_id = rule["Rule_ID"]
543
- rule_priority, *_ = rule_info.Priority
544
-
545
- # Parse conditions
546
- n_rule_premises, *_ = rule_info.Premises
504
+ all_rules_id = self.epanet_api.get_all_rules_id()
505
+ for rule_idx, rule_id in enumerate(all_rules_id, start=1):
506
+ n_rule_premises, n_rule_then_actions, n_rule_else_actions, rule_priority = \
507
+ self.epanet_api.getrule(rule_idx)
547
508
 
548
509
  condition_1 = None
549
510
  additional_conditions = []
550
511
  for j in range(1, n_rule_premises + 1):
551
512
  [logop, object_type_id, obj_idx, variable_type_id, relop, status, value_premise] = \
552
- self.epanet_api.api.ENgetpremise(rule_idx, j)
513
+ self.epanet_api.getpremise(rule_idx, j)
553
514
 
554
515
  object_id = None
555
- if object_type_id == ToolkitConstants.EN_R_NODE:
556
- object_id = self.epanet_api.getNodeNameID(obj_idx)
557
- elif object_type_id == ToolkitConstants.EN_R_LINK:
558
- object_id = self.epanet_api.getLinkNameID(obj_idx)
559
- elif object_type_id == ToolkitConstants.EN_R_SYSTEM:
516
+ if object_type_id == EpanetConstants.EN_R_NODE:
517
+ object_id = self.epanet_api.get_node_id(obj_idx)
518
+ elif object_type_id == EpanetConstants.EN_R_LINK:
519
+ object_id = self.epanet_api.get_link_id(obj_idx)
520
+ elif object_type_id == EpanetConstants.EN_R_SYSTEM:
560
521
  object_id = ""
561
522
 
562
- if variable_type_id >= ToolkitConstants.EN_R_TIME:
523
+ if variable_type_id >= EpanetConstants.EN_R_TIME:
563
524
  value_premise = datetime.fromtimestamp(value_premise)\
564
525
  .strftime("%I:%M %p")
565
526
  if status != 0:
566
- value_premise = self.epanet_api.RULESTATUS[status - 1]
527
+ value_premise = RULESTATUS[status - 1]
567
528
 
568
529
  condition = RuleCondition(object_type_id, object_id, variable_type_id,
569
530
  relop, value_premise)
@@ -573,14 +534,13 @@ class ScenarioSimulator():
573
534
  additional_conditions.append((logop, condition))
574
535
 
575
536
  # Parse actions
576
- n_rule_then_actions, *_ = rule_info.ThenActions
577
537
  actions = []
578
538
  for j in range(1, n_rule_then_actions + 1):
579
539
  [link_idx, link_status, link_setting] = \
580
- self.epanet_api.api.ENgetthenaction(rule_idx, j)
540
+ self.epanet_api.getthenaction(rule_idx, j)
581
541
 
582
- link_type_id = self.epanet_api.getLinkTypeIndex(link_idx)
583
- link_id = self.epanet_api.getLinkNameID(link_idx)
542
+ link_type_id = self.epanet_api.getlinktype(link_idx)
543
+ link_id = self.epanet_api.get_link_id(link_idx)
584
544
  if link_status >= 0:
585
545
  action_type_id = link_status
586
546
  action_value = link_status
@@ -590,14 +550,13 @@ class ScenarioSimulator():
590
550
 
591
551
  actions.append(RuleAction(link_type_id, link_id, action_type_id, action_value))
592
552
 
593
- n_rule_else_actions, *_ = rule_info.ElseActions
594
553
  else_actions = []
595
554
  for j in range(1, n_rule_else_actions + 1):
596
555
  [link_idx, link_status, link_setting] = \
597
- self.epanet_api.api.ENgetelseaction(rule_idx, j)
556
+ self.epanet_api.getelseaction(rule_idx, j)
598
557
 
599
- link_type_id = self.epanet_api.getLinkType(link_idx)
600
- link_id = self.epanet_api.getLinkNameID(link_idx)
558
+ link_type_id = self.epanet_api.getlinktype(link_idx)
559
+ link_id = self.epanet_api.get_link_id(link_idx)
601
560
  if link_status <= 3:
602
561
  action_type_id = link_status
603
562
  action_value = link_status
@@ -614,11 +573,11 @@ class ScenarioSimulator():
614
573
  return controls
615
574
 
616
575
  def _adapt_to_network_changes(self):
617
- nodes = self.epanet_api.getNodeNameID()
618
- links = self.epanet_api.getLinkNameID()
576
+ nodes = self.epanet_api.get_all_nodes_id()
577
+ links = self.epanet_api.get_all_links_id()
619
578
 
620
- node_id_to_idx = {node_id: self.epanet_api.getNodeIndex(node_id) - 1 for node_id in nodes}
621
- link_id_to_idx = {link_id: self.epanet_api.getLinkIndex(link_id) - 1 for link_id in links}
579
+ node_id_to_idx = {node_id: self.epanet_api.get_node_idx(node_id) - 1 for node_id in nodes}
580
+ link_id_to_idx = {link_id: self.epanet_api.get_link_idx(link_id) - 1 for link_id in links}
622
581
  valve_id_to_idx = None # {valve_id: self.epanet_api.getLinkValveIndex(valve_id) for valve_id in valves}
623
582
  pump_id_to_idx = None # {pump_id: self.epanet_api.getLinkPumpIndex(pump_id) - 1 for pump_id in pumps}
624
583
  tank_id_to_idx = None # {tank_id: self.epanet_api.getNodeTankIndex(tank_id) - 1 for tank_id in tanks}
@@ -653,10 +612,7 @@ class ScenarioSimulator():
653
612
 
654
613
  Call this function after the simulation is done -- do not call this function before!
655
614
  """
656
- if self.__f_msx_in is not None:
657
- self.epanet_api.unloadMSX()
658
-
659
- self.epanet_api.unload()
615
+ self.epanet_api.close()
660
616
 
661
617
  if self.__my_f_inp_in is not None:
662
618
  shutil.rmtree(pathlib.Path(self.__my_f_inp_in).parent)
@@ -737,7 +693,7 @@ class ScenarioSimulator():
737
693
  event.cleanup()
738
694
 
739
695
  if inp_file_path is not None:
740
- self.epanet_api.saveInputFile(inp_file_path)
696
+ self.epanet_api.saveinpfile(inp_file_path)
741
697
  self.__f_inp_in = inp_file_path
742
698
 
743
699
  if export_sensor_config is True:
@@ -792,7 +748,7 @@ class ScenarioSimulator():
792
748
  __override_report_section(inp_file_path, report_desc)
793
749
 
794
750
  if self.__f_msx_in is not None and msx_file_path is not None:
795
- self.epanet_api.saveMSXFile(msx_file_path)
751
+ self.epanet_api.MSXsavemsxfile(msx_file_path)
796
752
  self.__f_msx_in = msx_file_path
797
753
 
798
754
  if export_sensor_config is True:
@@ -845,7 +801,7 @@ class ScenarioSimulator():
845
801
  """
846
802
  Gets the flow units.
847
803
 
848
- Will be one of the following EPANET toolkit constants:
804
+ Will be one of the following EPANET constants:
849
805
 
850
806
  - EN_CFS = 0 (cu foot/sec)
851
807
  - EN_GPM = 1 (gal/min)
@@ -863,7 +819,7 @@ class ScenarioSimulator():
863
819
  `int`
864
820
  Flow units.
865
821
  """
866
- return self.epanet_api.api.ENgetflowunits()
822
+ return self.epanet_api.getflowunits()
867
823
 
868
824
  def get_units_category(self) -> int:
869
825
  """
@@ -879,9 +835,9 @@ class ScenarioSimulator():
879
835
  `int`
880
836
  Units category.
881
837
  """
882
- if self.get_flow_units() in [ToolkitConstants.EN_CFS, ToolkitConstants.EN_GPM,
883
- ToolkitConstants.EN_MGD, ToolkitConstants.EN_IMGD,
884
- ToolkitConstants.EN_AFD]:
838
+ if self.get_flow_units() in [EpanetConstants.EN_CFS, EpanetConstants.EN_GPM,
839
+ EpanetConstants.EN_MGD, EpanetConstants.EN_IMGD,
840
+ EpanetConstants.EN_AFD]:
885
841
  return UNITS_USCUSTOM
886
842
  else:
887
843
  return UNITS_SIMETRIC
@@ -895,7 +851,7 @@ class ScenarioSimulator():
895
851
  `int`
896
852
  Hydraulic time step in seconds.
897
853
  """
898
- return self.epanet_api.getTimeHydraulicStep()
854
+ return self.epanet_api.get_hydraulic_time_step()
899
855
 
900
856
  def get_quality_time_step(self) -> int:
901
857
  """
@@ -906,7 +862,7 @@ class ScenarioSimulator():
906
862
  `int`
907
863
  Quality time step in seconds.
908
864
  """
909
- return self.epanet_api.getTimeQualityStep()
865
+ return self.epanet_api.get_quality_time_step()
910
866
 
911
867
  def get_simulation_duration(self) -> int:
912
868
  """
@@ -917,7 +873,7 @@ class ScenarioSimulator():
917
873
  `int`
918
874
  Simulation duration in seconds.
919
875
  """
920
- return self.epanet_api.getTimeSimulationDuration()
876
+ return self.epanet_api.get_simulation_duration()
921
877
 
922
878
  def get_demand_model(self) -> dict:
923
879
  """
@@ -928,12 +884,12 @@ class ScenarioSimulator():
928
884
  `dict`
929
885
  Demand model.
930
886
  """
931
- demand_info = self.epanet_api.getDemandModel()
887
+ demand_info = self.epanet_api.get_demand_model()
932
888
 
933
- return {"type": "PDA" if demand_info.DemandModelCode == 1 else "DDA",
934
- "pressure_min": demand_info.DemandModelPmin,
935
- "pressure_required": demand_info.DemandModelPreq,
936
- "pressure_exponent": demand_info.DemandModelPexp}
889
+ return {"type": demand_info["type"],
890
+ "pressure_min": demand_info["pmin"],
891
+ "pressure_required": demand_info["preq"],
892
+ "pressure_exponent": demand_info["pexp"]}
937
893
 
938
894
  def get_quality_model(self) -> dict:
939
895
  """
@@ -947,13 +903,13 @@ class ScenarioSimulator():
947
903
  `dict`
948
904
  Quality model.
949
905
  """
950
- qual_info = self.epanet_api.getQualityInfo()
906
+ qual_info = self.epanet_api.get_quality_info()
951
907
 
952
- return {"code": qual_info.QualityCode,
953
- "type": qual_info.QualityType,
954
- "chemical_name": qual_info.QualityChemName,
955
- "units": qualityunit_to_id(qual_info.QualityChemUnits),
956
- "trace_node_id": qual_info.TraceNode}
908
+ return {"code": self.epanet_api.getqualtype(),
909
+ "type": qual_info["qualType"],
910
+ "chemical_name": qual_info["chemName"],
911
+ "units": qualityunit_to_id(qual_info["chemUnits"]),
912
+ "trace_node_id": qual_info["traceNode"]}
957
913
 
958
914
  def get_reporting_time_step(self) -> int:
959
915
  """
@@ -966,7 +922,7 @@ class ScenarioSimulator():
966
922
  `int`
967
923
  Reporting time steps in seconds.
968
924
  """
969
- return self.epanet_api.getTimeReportingStep()
925
+ return self.epanet_api.get_reporting_time_step()
970
926
 
971
927
  def get_scenario_config(self) -> ScenarioConfig:
972
928
  """
@@ -1011,14 +967,15 @@ class ScenarioSimulator():
1011
967
  """
1012
968
  self._adapt_to_network_changes()
1013
969
 
1014
- n_time_steps = int(self.epanet_api.getTimeSimulationDuration() /
1015
- self.epanet_api.getTimeReportingStep())
1016
- n_quantities = self.epanet_api.getNodeCount() * 3 + self.epanet_api.getNodeTankCount() + \
1017
- self.epanet_api.getLinkValveCount() + self.epanet_api.getLinkPumpCount() + \
1018
- self.epanet_api.getLinkCount() * 2
970
+ n_time_steps = int(self.epanet_api.get_simulation_duration() /
971
+ self.epanet_api.get_reporting_time_step())
972
+ n_quantities = self.epanet_api.get_num_nodes() * 3 + self.epanet_api.get_num_tanks() + \
973
+ self.epanet_api.get_num_valves() + self.epanet_api.get_num_pumps() + \
974
+ self.epanet_api.get_num_links() * 2
1019
975
 
1020
976
  if self.__f_msx_in is not None:
1021
- n_quantities += self.epanet_api.getLinkCount() * 2 + self.epanet_api.getNodeCount()
977
+ n_quantities += self.epanet_api.get_num_links() * 2 * self.epanet_api.get_num_msx_species() + \
978
+ self.epanet_api.get_num_nodes() * self.epanet_api.get_num_msx_species()
1022
979
 
1023
980
  n_bytes_per_quantity = 64
1024
981
 
@@ -1036,28 +993,39 @@ class ScenarioSimulator():
1036
993
  self._adapt_to_network_changes()
1037
994
 
1038
995
  # Collect information about the topology of the water distribution network
1039
- nodes_id = self.epanet_api.getNodeNameID()
1040
- nodes_elevation = self.epanet_api.getNodeElevations()
1041
- nodes_type = [self.epanet_api.TYPENODE[i] for i in self.epanet_api.getNodeTypeIndex()]
1042
- nodes_coord = [self.epanet_api.api.ENgetcoord(node_idx)
1043
- for node_idx in self.epanet_api.getNodeIndex()]
1044
- nodes_comments = self.epanet_api.getNodeComment()
1045
- node_tank_names = self.epanet_api.getNodeTankNameID()
1046
-
1047
- links_id = self.epanet_api.getLinkNameID()
1048
- links_type = self.epanet_api.getLinkType()
1049
- links_data = self.epanet_api.getNodesConnectingLinksID()
1050
- links_diameter = self.epanet_api.getLinkDiameter()
1051
- links_length = self.epanet_api.getLinkLength()
1052
- links_roughness_coeff = self.epanet_api.getLinkRoughnessCoeff()
1053
- links_bulk_coeff = self.epanet_api.getLinkBulkReactionCoeff()
1054
- links_wall_coeff = self.epanet_api.getLinkWallReactionCoeff()
1055
- links_loss_coeff = self.epanet_api.getLinkMinorLossCoeff()
1056
-
1057
- pumps_id = self.epanet_api.getLinkPumpNameID()
1058
- pumps_type = self.epanet_api.getLinkPumpType()
1059
-
1060
- valves_id = self.epanet_api.getLinkValveNameID()
996
+ nodes_id = self.epanet_api.get_all_nodes_id()
997
+ nodes_elevation = [self.epanet_api.get_node_elevation(node_idx)
998
+ for node_idx in self.epanet_api.get_all_nodes_idx()]
999
+ nodes_type = [self.epanet_api.get_node_type(node_idx)
1000
+ for node_idx in self.epanet_api.get_all_nodes_idx()]
1001
+ nodes_coord = [self.epanet_api.getcoord(node_idx)
1002
+ for node_idx in self.epanet_api.get_all_nodes_idx()]
1003
+ nodes_comments = [self.epanet_api.get_node_comment(node_idx)
1004
+ for node_idx in self.epanet_api.get_all_nodes_idx()]
1005
+ node_tank_names = self.epanet_api.get_all_tanks_id()
1006
+
1007
+ links_id = self.epanet_api.get_all_links_id()
1008
+ links_type = [self.epanet_api.get_link_type(link_idx)
1009
+ for link_idx in self.epanet_api.get_all_links_idx()]
1010
+ links_data = self.epanet_api.get_all_links_connecting_nodes_id()
1011
+ links_diameter = [self.epanet_api.get_link_diameter(link_idx)
1012
+ for link_idx in self.epanet_api.get_all_links_idx()]
1013
+ links_length = [self.epanet_api.get_link_length(link_idx)
1014
+ for link_idx in self.epanet_api.get_all_links_idx()]
1015
+ links_roughness_coeff = [self.epanet_api.get_link_roughness(link_idx)
1016
+ for link_idx in self.epanet_api.get_all_links_idx()]
1017
+ links_bulk_coeff = [self.epanet_api.get_link_bulk_raction_coeff(link_idx)
1018
+ for link_idx in self.epanet_api.get_all_links_idx()]
1019
+ links_wall_coeff = [self.epanet_api.get_link_wall_raction_coeff(link_idx)
1020
+ for link_idx in self.epanet_api.get_all_links_idx()]
1021
+ links_loss_coeff = [self.epanet_api.get_link_minorloss(link_idx)
1022
+ for link_idx in self.epanet_api.get_all_links_idx()]
1023
+
1024
+ pumps_id = self.epanet_api.get_all_pumps_id()
1025
+ pumps_type = [self.epanet_api.get_pump_type(pump_idx)
1026
+ for pump_idx in self.epanet_api.get_all_pumps_idx()]
1027
+
1028
+ valves_id = self.epanet_api.get_all_valves_id()
1061
1029
 
1062
1030
  # Build graph describing the topology
1063
1031
  nodes = []
@@ -1068,14 +1036,15 @@ class ScenarioSimulator():
1068
1036
  "coord": node_coord,
1069
1037
  "comment": node_comment,
1070
1038
  "type": node_type}
1071
- if node_type == "TANK":
1072
- node_tank_idx = node_tank_names.index(node_id) + 1
1073
- node_info["diameter"] = float(self.epanet_api.getNodeTankDiameter(node_tank_idx))
1074
- node_info["volume"] = float(self.epanet_api.getNodeTankVolume(node_tank_idx))
1075
- node_info["max_level"] = float(self.epanet_api.getNodeTankMaximumWaterLevel(node_tank_idx))
1076
- node_info["min_level"] = float(self.epanet_api.getNodeTankMinimumWaterLevel(node_tank_idx))
1077
- node_info["mixing_fraction"] = float(self.epanet_api.getNodeTankMixingFraction(node_tank_idx))
1078
- #node_info["mixing_model"] = int(self.epanet_api.getNodeTankMixingModelCode(node_tank_idx)[0])
1039
+
1040
+ if node_type == EpanetConstants.EN_TANK:
1041
+ node_tank_idx = self.epanet_api.get_node_idx(node_id)
1042
+ node_info["diameter"] = float(self.epanet_api.get_tank_diameter(node_tank_idx))
1043
+ node_info["volume"] = float(self.epanet_api.get_tank_volume(node_tank_idx))
1044
+ node_info["max_level"] = float(self.epanet_api.get_tank_max_level(node_tank_idx))
1045
+ node_info["min_level"] = float(self.epanet_api.get_tank_min_level(node_tank_idx))
1046
+ node_info["mixing_fraction"] = float(self.epanet_api.get_tank_mix_fraction(node_tank_idx))
1047
+ node_info["mixing_model"] = int(self.epanet_api.get_tank_mix_model(node_tank_idx))
1079
1048
 
1080
1049
  nodes.append((node_id, node_info))
1081
1050
 
@@ -1132,23 +1101,21 @@ class ScenarioSimulator():
1132
1101
  self._adapt_to_network_changes()
1133
1102
 
1134
1103
  # Get all demand patterns
1135
- demand_patterns_idx = self.epanet_api.getNodeDemandPatternIndex()
1136
- demand_patterns_id = np.unique([idx for _, idx in demand_patterns_idx.items()])
1104
+ demand_patterns_idx = [self.epanet_api.get_node_demand_patterns_idx(node_idx)
1105
+ for node_idx in self.epanet_api.get_all_nodes_idx()]
1106
+ demand_patterns_idx = list(set(itertools.chain.from_iterable(demand_patterns_idx)))
1137
1107
 
1138
1108
  # Process each pattern separately
1139
- for pattern_id in demand_patterns_id:
1140
- if pattern_id == 0:
1141
- continue
1142
-
1143
- pattern_length = self.epanet_api.getPatternLengths(pattern_id)
1109
+ for pattern_idx in demand_patterns_idx:
1110
+ pattern_length = self.epanet_api.getpatternlen(pattern_idx)
1144
1111
  pattern = []
1145
1112
  for t in range(pattern_length): # Get pattern
1146
- pattern.append(self.epanet_api.getPatternValue(pattern_id, t + 1))
1113
+ pattern.append(self.epanet_api.getpatternvalue(pattern_idx, t + 1))
1147
1114
 
1148
1115
  random.shuffle(pattern) # Shuffle pattern
1149
1116
 
1150
1117
  for t in range(pattern_length): # Set shuffled/randomized pattern
1151
- self.epanet_api.setPatternValue(pattern_id, t + 1, pattern[t])
1118
+ self.epanet_api.setpatternvalue(pattern_idx, t + 1, pattern[t])
1152
1119
 
1153
1120
  def get_pattern(self, pattern_id: str) -> np.ndarray:
1154
1121
  """
@@ -1168,13 +1135,11 @@ class ScenarioSimulator():
1168
1135
  raise TypeError("'pattern_id' must be an instance of 'str' " +
1169
1136
  f"but not of '{type(pattern_id)}'")
1170
1137
 
1171
- pattern_idx = self.epanet_api.getPatternIndex(pattern_id)
1138
+ pattern_idx = self.epanet_api.getpatternindex(pattern_id)
1172
1139
  if pattern_idx == 0:
1173
1140
  raise ValueError(f"Unknown pattern '{pattern_id}'")
1174
1141
 
1175
- pattern_length = self.epanet_api.getPatternLengths(pattern_idx)
1176
- return np.array([self.epanet_api.getPatternValue(pattern_idx, t+1)
1177
- for t in range(pattern_length)])
1142
+ return np.array(self.epanet_api.get_pattern(pattern_idx))
1178
1143
 
1179
1144
  def add_pattern(self, pattern_id: str, pattern: np.ndarray) -> None:
1180
1145
  """
@@ -1199,7 +1164,7 @@ class ScenarioSimulator():
1199
1164
  raise ValueError(f"Inconsistent pattern shape '{pattern.shape}' " +
1200
1165
  "detected. Expected a one dimensional array!")
1201
1166
 
1202
- pattern_idx = self.epanet_api.addPattern(pattern_id, pattern)
1167
+ pattern_idx = self.epanet_api.add_pattern(pattern_id, pattern.tolist())
1203
1168
  if pattern_idx == 0:
1204
1169
  raise RuntimeError("Failed to add pattern! " +
1205
1170
  "Maybe pattern name contains invalid characters or is too long?")
@@ -1223,21 +1188,21 @@ class ScenarioSimulator():
1223
1188
  if node_id not in self._sensor_config.nodes:
1224
1189
  raise ValueError(f"Unknown node '{node_id}'")
1225
1190
 
1226
- node_idx = self.epanet_api.getNodeIndex(node_id)
1227
- n_demand_categories = self.epanet_api.getNodeDemandCategoriesNumber(node_idx)
1191
+ node_idx = self.epanet_api.get_node_idx(node_id)
1192
+ n_demand_categories = self.epanet_api.getnumdemands(node_idx)
1228
1193
 
1229
1194
  if n_demand_categories == 0:
1230
1195
  return None
1231
1196
  else:
1232
1197
  base_demand = 0
1233
- for demand_category in range(n_demand_categories):
1234
- base_demand += self.epanet_api.getNodeBaseDemands(node_idx)[demand_category + 1]
1198
+ for demand_idx in range(n_demand_categories):
1199
+ base_demand += self.epanet_api.getbasedemand(node_idx, demand_idx + 1)
1235
1200
 
1236
1201
  return base_demand
1237
1202
 
1238
1203
  def get_node_demand_pattern(self, node_id: str) -> np.ndarray:
1239
1204
  """
1240
- Returns the values of the demand pattern of a given node --
1205
+ Returns the values of the primary demand pattern of a given node --
1241
1206
  i.e. multiplier factors that are applied to the base demand.
1242
1207
 
1243
1208
  Parameters
@@ -1256,10 +1221,14 @@ class ScenarioSimulator():
1256
1221
  if node_id not in self._sensor_config.nodes:
1257
1222
  raise ValueError(f"Unknown node '{node_id}'")
1258
1223
 
1259
- node_idx = self.epanet_api.getNodeIndex(node_id)
1260
- demand_category = self.epanet_api.getNodeDemandCategoriesNumber()[node_idx]
1261
- demand_pattern_id = self.epanet_api.getNodeDemandPatternNameID()[demand_category][node_idx - 1]
1262
- return self.get_pattern(demand_pattern_id)
1224
+ node_idx = self.epanet_api.get_node_idx(node_id)
1225
+
1226
+ if self.epanet_api.getnodetype(node_idx) != EpanetConstants.EN_RESERVOIR:
1227
+ demand_pattern_idx = self.epanet_api.getdemandpattern(node_idx)
1228
+ else:
1229
+ demand_pattern_idx = self.epanet_api.getnodevalue(node_idx, EpanetConstants.EN_PATTERN)
1230
+
1231
+ return self.get_pattern(self.epanet_api.getpatternid(demand_pattern_idx))
1263
1232
 
1264
1233
  def set_node_demand_pattern(self, node_id: str, base_demand: float, demand_pattern_id: str,
1265
1234
  demand_pattern: np.ndarray = None) -> None:
@@ -1298,20 +1267,20 @@ class ScenarioSimulator():
1298
1267
  raise ValueError(f"Inconsistent demand pattern shape '{demand_pattern.shape}' " +
1299
1268
  "detected. Expected a one dimensional array!")
1300
1269
 
1301
- node_idx = self.epanet_api.getNodeIndex(node_id)
1270
+ node_idx = self.epanet_api.get_node_idx(node_id)
1302
1271
 
1303
- if demand_pattern_id not in self.epanet_api.getPatternNameID():
1272
+ if demand_pattern_id not in self.epanet_api.get_all_patterns_id():
1304
1273
  if demand_pattern is None:
1305
1274
  raise ValueError("'demand_pattern' can not be None if " +
1306
1275
  "'demand_pattern_id' does not already exist.")
1307
- self.epanet_api.addPattern(demand_pattern_id, demand_pattern)
1276
+ self.epanet_api.add_pattern(demand_pattern_id, demand_pattern.tolist())
1308
1277
  else:
1309
1278
  if demand_pattern is not None:
1310
- pattern_idx = self.epanet_api.getPatternIndex(demand_pattern_id)
1311
- self.epanet_api.setPattern(pattern_idx, demand_pattern)
1279
+ pattern_idx = self.epanet_api.get_node_pattern_idx(demand_pattern_id)
1280
+ self.epanet_api.set_pattern(pattern_idx, demand_pattern.tolist())
1312
1281
 
1313
- self.epanet_api.setNodeJunctionData(node_idx, self.epanet_api.getNodeElevations(node_idx),
1314
- base_demand, demand_pattern_id)
1282
+ self.epanet_api.setjuncdata(node_idx, self.epanet_api.get_node_elevation(node_idx),
1283
+ base_demand, demand_pattern_id)
1315
1284
 
1316
1285
  def add_custom_control(self, control: CustomControlModule) -> None:
1317
1286
  """
@@ -1349,13 +1318,20 @@ class ScenarioSimulator():
1349
1318
 
1350
1319
  if not any(c == control for c in self._simple_controls):
1351
1320
  self._simple_controls.append(control)
1352
- self.epanet_api.addControls(str(control))
1321
+
1322
+ link_idx = self.epanet_api.get_link_idx(control.link_id)
1323
+ cond_var_value = control.cond_var_value
1324
+ if control.cond_type == EpanetConstants.EN_LOWLEVEL or \
1325
+ control.cond_type == EpanetConstants.EN_HILEVEL:
1326
+ cond_var_value = self.epanet_api.get_node_idx(cond_var_value)
1327
+ self.epanet_api.addcontrol(control.cond_type, link_idx, control.link_status,
1328
+ cond_var_value, control.cond_comp_value)
1353
1329
 
1354
1330
  def remove_all_simple_controls(self) -> None:
1355
1331
  """
1356
1332
  Removes all simple EPANET controls from the scenario.
1357
1333
  """
1358
- self.epanet_api.deleteControls()
1334
+ self.epanet_api.remove_all_controls()
1359
1335
  self._simple_controls = []
1360
1336
 
1361
1337
  def remove_simple_control(self, control: SimpleControlModule) -> None:
@@ -1382,7 +1358,7 @@ class ScenarioSimulator():
1382
1358
  if control_idx is None:
1383
1359
  raise ValueError("Invalid/Unknown control module.")
1384
1360
 
1385
- self.epanet_api.deleteControls(control_idx)
1361
+ self.epanet_api.deletecontrol(control_idx)
1386
1362
  self._simple_controls.remove(control)
1387
1363
 
1388
1364
  def add_complex_control(self, control: ComplexControlModule) -> None:
@@ -1403,13 +1379,13 @@ class ScenarioSimulator():
1403
1379
 
1404
1380
  if not any(c == control for c in self._complex_controls):
1405
1381
  self._complex_controls.append(control)
1406
- self.epanet_api.addRules(str(control))
1382
+ self.epanet_api.addrule(str(control))
1407
1383
 
1408
1384
  def remove_all_complex_controls(self) -> None:
1409
1385
  """
1410
1386
  Removes all complex EPANET controls from the scenario.
1411
1387
  """
1412
- self.epanet_api.deleteRules()
1388
+ self.epanet_api.remove_all_rules()
1413
1389
  self._complex_controls = []
1414
1390
 
1415
1391
  def remove_complex_control(self, control: ComplexControlModule) -> None:
@@ -1428,12 +1404,13 @@ class ScenarioSimulator():
1428
1404
  "'epyt_flow.simulation.scada.ComplexControlModule' not of " +
1429
1405
  f"'{type(control)}'")
1430
1406
 
1431
- if control.rule_id not in self.epanet_api.getRuleID():
1407
+ all_rules_id = self.epanet_api.get_all_rules_id()
1408
+ if control.rule_id not in all_rules_id:
1432
1409
  raise ValueError("Invalid/Unknown control module. " +
1433
1410
  f"Can not find rule ID '{control.rule_id}'")
1434
1411
 
1435
- rule_idx = self.epanet_api.getRuleID().index(control.rule_id) + 1
1436
- self.epanet_api.deleteRules(rule_idx)
1412
+ rule_idx = all_rules_id.index(control.rule_id) + 1
1413
+ self.epanet_api.deleterule(rule_idx)
1437
1414
  self._complex_controls.remove(control)
1438
1415
 
1439
1416
  def add_leakage(self, leakage_event: Leakage) -> None:
@@ -1643,7 +1620,7 @@ class ScenarioSimulator():
1643
1620
  The default is False.
1644
1621
  """
1645
1622
  if junctions_only is True:
1646
- self.set_pressure_sensors(self.epanet_api.getNodeJunctionNameID())
1623
+ self.set_pressure_sensors(self.epanet_api.get_all_junctions_id())
1647
1624
  else:
1648
1625
  self.set_pressure_sensors(self._sensor_config.nodes)
1649
1626
 
@@ -2112,42 +2089,43 @@ class ScenarioSimulator():
2112
2089
  self._prepare_simulation(reapply_uncertainties)
2113
2090
 
2114
2091
  # Load pre-computed hydraulics
2115
- self.epanet_api.useMSXHydraulicFile(hyd_file_in)
2092
+ self.epanet_api.MSXusehydfile(hyd_file_in)
2116
2093
 
2117
2094
  # Initialize simulation
2118
- n_nodes = self.epanet_api.getNodeCount()
2119
- n_links = self.epanet_api.getLinkCount()
2095
+ n_nodes = self.epanet_api.get_num_nodes()
2096
+ n_links = self.epanet_api.get_num_links()
2120
2097
 
2121
- reporting_time_start = self.epanet_api.getTimeReportingStart()
2122
- reporting_time_step = self.epanet_api.getTimeReportingStep()
2123
- hyd_time_step = self.epanet_api.getTimeHydraulicStep()
2098
+ reporting_time_start = self.epanet_api.get_reporting_start_time()
2099
+ reporting_time_step = self.epanet_api.get_reporting_time_step()
2100
+ hyd_time_step = self.epanet_api.get_hydraulic_time_step()
2124
2101
 
2125
2102
  network_topo = self.get_topology()
2126
2103
 
2127
2104
  if use_quality_time_step_as_reporting_time_step is True:
2128
- quality_time_step = self.epanet_api.getMSXTimeStep()
2105
+ quality_time_step = self.epanet_api.get_msx_time_step()
2129
2106
  reporting_time_step = quality_time_step
2130
2107
  hyd_time_step = quality_time_step
2131
2108
 
2132
- self.epanet_api.initializeMSXQualityAnalysis(ToolkitConstants.EN_NOSAVE)
2109
+ self.epanet_api.MSXinit(EpanetConstants.EN_NOSAVE)
2133
2110
 
2134
2111
  self.__running_simulation = True
2135
2112
 
2136
- bulk_species_idx = self.epanet_api.getMSXSpeciesIndex(self._sensor_config.bulk_species)
2137
- surface_species_idx = self.epanet_api.getMSXSpeciesIndex(
2138
- self._sensor_config.surface_species)
2113
+ bulk_species_idx = [self.epanet_api.get_msx_species_idx(species_id)
2114
+ for species_id in self._sensor_config.bulk_species]
2115
+ surface_species_idx = [self.epanet_api.get_msx_species_idx(species_id)
2116
+ for species_id in self._sensor_config.surface_species]
2139
2117
 
2140
2118
  if verbose is True:
2141
2119
  print("Running EPANET-MSX ...")
2142
- n_iterations = math.ceil(self.epanet_api.getTimeSimulationDuration() /
2120
+ n_iterations = math.ceil(self.epanet_api.get_simulation_duration() /
2143
2121
  hyd_time_step)
2144
2122
  progress_bar = iter(tqdm(range(n_iterations + 1), ascii=True, desc="Time steps"))
2145
2123
 
2146
2124
  def __get_concentrations(init_qual=False):
2147
2125
  if init_qual is True:
2148
- msx_get_cur_value = self.epanet_api.msx.MSXgetinitqual
2126
+ msx_get_cur_value = self.epanet_api.MSXgetinitqual
2149
2127
  else:
2150
- msx_get_cur_value = self.epanet_api.getMSXSpeciesConcentration
2128
+ msx_get_cur_value = self.epanet_api.get_msx_species_concentration
2151
2129
 
2152
2130
  # Bulk species
2153
2131
  bulk_species_node_concentrations = []
@@ -2155,13 +2133,13 @@ class ScenarioSimulator():
2155
2133
  for species_idx in bulk_species_idx:
2156
2134
  cur_species_concentrations = []
2157
2135
  for node_idx in range(1, n_nodes + 1):
2158
- concen = msx_get_cur_value(0, node_idx, species_idx)
2136
+ concen = msx_get_cur_value(EpanetConstants.MSX_NODE, node_idx, species_idx)
2159
2137
  cur_species_concentrations.append(concen)
2160
2138
  bulk_species_node_concentrations.append(cur_species_concentrations)
2161
2139
 
2162
2140
  cur_species_concentrations = []
2163
2141
  for link_idx in range(1, n_links + 1):
2164
- concen = msx_get_cur_value(1, link_idx, species_idx)
2142
+ concen = msx_get_cur_value(EpanetConstants.MSX_LINK, link_idx, species_idx)
2165
2143
  cur_species_concentrations.append(concen)
2166
2144
  bulk_species_link_concentrations.append(cur_species_concentrations)
2167
2145
 
@@ -2183,7 +2161,7 @@ class ScenarioSimulator():
2183
2161
  cur_species_concentrations = []
2184
2162
 
2185
2163
  for link_idx in range(1, n_links + 1):
2186
- concen = msx_get_cur_value(1, link_idx, species_idx)
2164
+ concen = msx_get_cur_value(EpanetConstants.MSX_LINK, link_idx, species_idx)
2187
2165
  cur_species_concentrations.append(concen)
2188
2166
 
2189
2167
  surface_species_concentrations.append(cur_species_concentrations)
@@ -2208,7 +2186,7 @@ class ScenarioSimulator():
2208
2186
  pass
2209
2187
 
2210
2188
  if reporting_time_start == 0:
2211
- msx_error_code = self.epanet_api.msx.get_last_error_code()
2189
+ msx_error_code = self.epanet_api.get_last_error_code()
2212
2190
 
2213
2191
  if return_as_dict is True:
2214
2192
  data = {"bulk_species_node_concentration_raw": bulk_species_node_concentrations,
@@ -2241,8 +2219,8 @@ class ScenarioSimulator():
2241
2219
  last_msx_error_code = 0
2242
2220
  while tleft > 0:
2243
2221
  # Compute current time step
2244
- total_time, tleft = self.epanet_api.stepMSXQualityAnalysisTimeLeft()
2245
- msx_error_code = self.epanet_api.msx.get_last_error_code()
2222
+ total_time, tleft = self.epanet_api.MSXstep()
2223
+ msx_error_code = self.epanet_api.get_last_error_code()
2246
2224
  if last_msx_error_code == 0:
2247
2225
  last_msx_error_code = msx_error_code
2248
2226
 
@@ -2404,26 +2382,26 @@ class ScenarioSimulator():
2404
2382
  if self.__running_simulation is True:
2405
2383
  raise RuntimeError("A simulation is already running.")
2406
2384
 
2407
- requested_total_time = self.epanet_api.getTimeSimulationDuration()
2408
- requested_time_step = self.epanet_api.getTimeHydraulicStep()
2409
- reporting_time_start = self.epanet_api.getTimeReportingStart()
2410
- reporting_time_step = self.epanet_api.getTimeReportingStep()
2385
+ requested_total_time = self.epanet_api.get_simulation_duration()
2386
+ requested_time_step = self.epanet_api.get_hydraulic_time_step()
2387
+ reporting_time_start = self.epanet_api.get_reporting_start_time()
2388
+ reporting_time_step = self.epanet_api.get_reporting_time_step()
2411
2389
 
2412
2390
  if use_quality_time_step_as_reporting_time_step is True:
2413
- quality_time_step = self.epanet_api.getTimeQualityStep()
2391
+ quality_time_step = self.epanet_api.get_quality_time_step()
2414
2392
  requested_time_step = quality_time_step
2415
2393
  reporting_time_step = quality_time_step
2416
2394
 
2417
2395
  network_topo = self.get_topology()
2418
2396
 
2419
- self.epanet_api.useHydraulicFile(hyd_file_in)
2397
+ self.epanet_api.usehydfile(hyd_file_in)
2420
2398
 
2421
- self.epanet_api.openQualityAnalysis()
2422
- self.epanet_api.initializeQualityAnalysis(ToolkitConstants.EN_NOSAVE)
2399
+ self.epanet_api.openQ()
2400
+ self.epanet_api.initQ(EpanetConstants.EN_NOSAVE)
2423
2401
 
2424
2402
  if verbose is True:
2425
2403
  print("Running basic quality analysis using EPANET ...")
2426
- n_iterations = math.ceil(self.epanet_api.getTimeSimulationDuration() /
2404
+ n_iterations = math.ceil(self.epanet_api.get_simulation_duration() /
2427
2405
  requested_time_step)
2428
2406
  progress_bar = iter(tqdm(range(n_iterations + 1), ascii=True, desc="Time steps"))
2429
2407
 
@@ -2445,15 +2423,15 @@ class ScenarioSimulator():
2445
2423
  pass
2446
2424
 
2447
2425
  # Compute current time step
2448
- t = self.epanet_api.api.ENrunQ()
2426
+ t = self.epanet_api.runQ()
2449
2427
  total_time = t
2450
2428
 
2451
2429
  # Fetch data
2452
2430
  error_code = self.epanet_api.get_last_error_code()
2453
2431
  if last_error_code == 0:
2454
2432
  last_error_code = error_code
2455
- quality_node_data = self.epanet_api.getNodeActualQuality().reshape(1, -1)
2456
- quality_link_data = self.epanet_api.getLinkActualQuality().reshape(1, -1)
2433
+ quality_node_data = np.array(self.epanet_api.getnodevalues(EpanetConstants.EN_QUALITY)).reshape(1, -1)
2434
+ quality_link_data = np.array(self.epanet_api.getlinkvalues(EpanetConstants.EN_QUALITY)).reshape(1, -1)
2457
2435
 
2458
2436
  # Yield results in a regular time interval only!
2459
2437
  if total_time % reporting_time_step == 0 and total_time >= reporting_time_start:
@@ -2481,12 +2459,12 @@ class ScenarioSimulator():
2481
2459
  yield (data, total_time >= requested_total_time)
2482
2460
 
2483
2461
  # Next
2484
- tstep = self.epanet_api.api.ENstepQ()
2462
+ tstep = self.epanet_api.stepQ()
2485
2463
  error_code = self.epanet_api.get_last_error_code()
2486
2464
  if last_error_code == 0:
2487
2465
  last_error_code = error_code
2488
2466
 
2489
- self.epanet_api.closeQualityAnalysis()
2467
+ self.epanet_api.closeQ()
2490
2468
 
2491
2469
  def run_hydraulic_simulation(self, hyd_export: str = None, verbose: bool = False,
2492
2470
  frozen_sensor_config: bool = False,
@@ -2541,13 +2519,18 @@ class ScenarioSimulator():
2541
2519
  if result is None:
2542
2520
  result = {}
2543
2521
  for data_type, data in scada_data.items():
2544
- result[data_type] = [data]
2522
+ if data is None:
2523
+ result[data_type] = None
2524
+ else:
2525
+ result[data_type] = [data]
2545
2526
  else:
2546
2527
  for data_type, data in scada_data.items():
2547
- result[data_type].append(data)
2528
+ if result[data_type] is not None:
2529
+ result[data_type].append(data)
2548
2530
 
2549
2531
  for data_type in result:
2550
- result[data_type] = np.concatenate(result[data_type], axis=0)
2532
+ if result[data_type] is not None:
2533
+ result[data_type] = np.concatenate(result[data_type], axis=0)
2551
2534
 
2552
2535
  result = ScadaData(**result,
2553
2536
  network_topo=self.get_topology(),
@@ -2619,21 +2602,21 @@ class ScenarioSimulator():
2619
2602
 
2620
2603
  self.__running_simulation = True
2621
2604
 
2622
- self.epanet_api.api.ENopenH()
2623
- self.epanet_api.api.ENopenQ()
2624
- self.epanet_api.initializeHydraulicAnalysis(ToolkitConstants.EN_SAVE)
2625
- self.epanet_api.initializeQualityAnalysis(ToolkitConstants.EN_SAVE)
2605
+ self.epanet_api.openH()
2606
+ self.epanet_api.openQ()
2607
+ self.epanet_api.initH(EpanetConstants.EN_SAVE)
2608
+ self.epanet_api.initQ(EpanetConstants.EN_SAVE)
2626
2609
 
2627
- requested_total_time = self.epanet_api.getTimeSimulationDuration()
2628
- requested_time_step = self.epanet_api.getTimeHydraulicStep()
2629
- reporting_time_start = self.epanet_api.getTimeReportingStart()
2630
- reporting_time_step = self.epanet_api.getTimeReportingStep()
2610
+ requested_total_time = self.epanet_api.get_simulation_duration()
2611
+ requested_time_step = self.epanet_api.get_hydraulic_time_step()
2612
+ reporting_time_start = self.epanet_api.get_reporting_start_time()
2613
+ reporting_time_step = self.epanet_api.get_reporting_time_step()
2631
2614
 
2632
2615
  network_topo = self.get_topology()
2633
2616
 
2634
2617
  if verbose is True:
2635
2618
  print("Running EPANET ...")
2636
- n_iterations = math.ceil(self.epanet_api.getTimeSimulationDuration() /
2619
+ n_iterations = math.ceil(self.epanet_api.get_simulation_duration() /
2637
2620
  requested_time_step)
2638
2621
  progress_bar = iter(tqdm(range(n_iterations + 1), ascii=True, desc="Time steps"))
2639
2622
 
@@ -2661,9 +2644,9 @@ class ScenarioSimulator():
2661
2644
  event.step(total_time + tstep)
2662
2645
 
2663
2646
  # Compute current time step
2664
- t = self.epanet_api.api.ENrunH()
2647
+ t = self.epanet_api.runH()
2665
2648
  error_code = self.epanet_api.get_last_error_code()
2666
- self.epanet_api.api.ENrunQ()
2649
+ self.epanet_api.runQ()
2667
2650
  if error_code == 0:
2668
2651
  error_code = self.epanet_api.get_last_error_code()
2669
2652
  total_time = t
@@ -2671,20 +2654,32 @@ class ScenarioSimulator():
2671
2654
  last_error_code = error_code
2672
2655
 
2673
2656
  # Fetch data
2674
- pressure_data = self.epanet_api.getNodePressure().reshape(1, -1)
2675
- flow_data = self.epanet_api.getLinkFlows().reshape(1, -1)
2676
- demand_data = self.epanet_api.getNodeActualDemand().reshape(1, -1)
2677
- quality_node_data = self.epanet_api.getNodeActualQuality().reshape(1, -1)
2678
- quality_link_data = self.epanet_api.getLinkActualQuality().reshape(1, -1)
2679
- pumps_state_data = self.epanet_api.getLinkPumpState().reshape(1, -1)
2680
- tanks_volume_data = self.epanet_api.getNodeTankVolume().reshape(1, -1)
2681
-
2682
- pump_idx = self.epanet_api.getLinkPumpIndex()
2683
- pumps_energy_usage_data = self.epanet_api.getLinkEnergy(pump_idx).reshape(1, -1)
2684
- pumps_efficiency_data = self.epanet_api.getLinkPumpEfficiency().reshape(1, -1)
2685
-
2686
- link_valve_idx = self.epanet_api.getLinkValveIndex()
2687
- valves_state_data = self.epanet_api.getLinkStatus(link_valve_idx).reshape(1, -1)
2657
+ pressure_data = np.array(self.epanet_api.getnodevalues(EpanetConstants.EN_PRESSURE)).reshape(1, -1)
2658
+ flow_data = np.array(self.epanet_api.getlinkvalues(EpanetConstants.EN_FLOW)).reshape(1, -1)
2659
+ demand_data = np.array(self.epanet_api.getnodevalues(EpanetConstants.EN_DEMAND)).reshape(1, -1)
2660
+ quality_node_data = np.array(self.epanet_api.getnodevalues(EpanetConstants.EN_QUALITY)).reshape(1, -1)
2661
+ quality_link_data = np.array(self.epanet_api.getlinkvalues(EpanetConstants.EN_QUALITY)).reshape(1, -1)
2662
+
2663
+ tanks_volume_data = None
2664
+ if len(self.epanet_api.get_all_tanks_idx()) > 0:
2665
+ tanks_volume_data = np.array([self.epanet_api.get_tank_volume(tank_idx)
2666
+ for tank_idx in self.epanet_api.get_all_tanks_idx()]).reshape(1, -1)
2667
+
2668
+ pumps_state_data = None
2669
+ pumps_energy_usage_data = None
2670
+ pumps_efficiency_data = None
2671
+ if len(self.epanet_api.get_all_pumps_idx()) > 0:
2672
+ pumps_state_data = np.array([self.epanet_api.getlinkvalue(link_idx, EpanetConstants.EN_PUMP_STATE)
2673
+ for link_idx in self.epanet_api.get_all_pumps_idx()]).reshape(1, -1)
2674
+ pumps_energy_usage_data = np.array([self.epanet_api.get_pump_energy_usage(pump_idx)
2675
+ for pump_idx in self.epanet_api.get_all_pumps_idx()]).reshape(1, -1)
2676
+ pumps_efficiency_data = np.array([self.epanet_api.get_pump_efficiency(pump_idx)
2677
+ for pump_idx in self.epanet_api.get_all_pumps_idx()]).reshape(1, -1)
2678
+
2679
+ valves_state_data = None
2680
+ if len(self.epanet_api.get_all_valves_idx()) > 0:
2681
+ valves_state_data = np.array([self.epanet_api.getlinkvalue(link_valve_idx, EpanetConstants.EN_STATUS)
2682
+ for link_valve_idx in self.epanet_api.get_all_valves_idx()]).reshape(1, -1)
2688
2683
 
2689
2684
  scada_data = ScadaData(network_topo=network_topo,
2690
2685
  sensor_config=self._sensor_config,
@@ -2736,23 +2731,23 @@ class ScenarioSimulator():
2736
2731
  control.step(scada_data)
2737
2732
 
2738
2733
  # Next
2739
- tstep = self.epanet_api.api.ENnextH()
2734
+ tstep = self.epanet_api.nextH()
2740
2735
  error_code = self.epanet_api.get_last_error_code()
2741
2736
  if last_error_code == 0:
2742
2737
  last_error_code = error_code
2743
2738
 
2744
- self.epanet_api.api.ENnextQ()
2739
+ self.epanet_api.nextQ()
2745
2740
  error_code = self.epanet_api.get_last_error_code()
2746
2741
  if last_error_code == 0:
2747
2742
  last_error_code = error_code
2748
2743
 
2749
- self.epanet_api.api.ENcloseQ()
2750
- self.epanet_api.api.ENcloseH()
2744
+ self.epanet_api.closeQ()
2745
+ self.epanet_api.closeH()
2751
2746
 
2752
2747
  self.__running_simulation = False
2753
2748
 
2754
2749
  if hyd_export is not None:
2755
- self.epanet_api.saveHydraulicFile(hyd_export)
2750
+ self.epanet_api.savehydfile(hyd_export)
2756
2751
  except Exception as ex:
2757
2752
  self.__running_simulation = False
2758
2753
  raise ex
@@ -2929,7 +2924,7 @@ class ScenarioSimulator():
2929
2924
  Specifies the flow units -- i.e. all flows will be reported in these units.
2930
2925
  If None, the units from the .inp file will be used.
2931
2926
 
2932
- Must be one of the following EPANET toolkit constants:
2927
+ Must be one of the following EPANET constants:
2933
2928
 
2934
2929
  - EN_CFS = 0 (cubic foot/sec)
2935
2930
  - EN_GPM = 1 (gal/min)
@@ -2956,39 +2951,39 @@ class ScenarioSimulator():
2956
2951
  self._adapt_to_network_changes()
2957
2952
 
2958
2953
  if flow_units_id is not None:
2959
- if flow_units_id == ToolkitConstants.EN_CFS:
2960
- self.epanet_api.setFlowUnitsCFS()
2961
- elif flow_units_id == ToolkitConstants.EN_GPM:
2962
- self.epanet_api.setFlowUnitsGPM()
2963
- elif flow_units_id == ToolkitConstants.EN_MGD:
2964
- self.epanet_api.setFlowUnitsMGD()
2965
- elif flow_units_id == ToolkitConstants.EN_IMGD:
2966
- self.epanet_api.setFlowUnitsIMGD()
2967
- elif flow_units_id == ToolkitConstants.EN_AFD:
2968
- self.epanet_api.setFlowUnitsAFD()
2969
- elif flow_units_id == ToolkitConstants.EN_LPS:
2970
- self.epanet_api.setFlowUnitsLPS()
2971
- elif flow_units_id == ToolkitConstants.EN_LPM:
2972
- self.epanet_api.setFlowUnitsLPM()
2973
- elif flow_units_id == ToolkitConstants.EN_MLD:
2974
- self.epanet_api.setFlowUnitsMLD()
2975
- elif flow_units_id == ToolkitConstants.EN_CMH:
2976
- self.epanet_api.setFlowUnitsCMH()
2977
- elif flow_units_id == ToolkitConstants.EN_CMD:
2978
- self.epanet_api.setFlowUnitsCMD()
2954
+ if flow_units_id == EpanetConstants.EN_CFS:
2955
+ self.epanet_api.setflowunits(flow_units_id)
2956
+ elif flow_units_id == EpanetConstants.EN_GPM:
2957
+ self.epanet_api.setflowunits(flow_units_id)
2958
+ elif flow_units_id == EpanetConstants.EN_MGD:
2959
+ self.epanet_api.setflowunits(flow_units_id)
2960
+ elif flow_units_id == EpanetConstants.EN_IMGD:
2961
+ self.epanet_api.setflowunits(flow_units_id)
2962
+ elif flow_units_id == EpanetConstants.EN_AFD:
2963
+ self.epanet_api.setflowunits(flow_units_id)
2964
+ elif flow_units_id == EpanetConstants.EN_LPS:
2965
+ self.epanet_api.setflowunits(flow_units_id)
2966
+ elif flow_units_id == EpanetConstants.EN_LPM:
2967
+ self.epanet_api.setflowunits(flow_units_id)
2968
+ elif flow_units_id == EpanetConstants.EN_MLD:
2969
+ self.epanet_api.setflowunits(flow_units_id)
2970
+ elif flow_units_id == EpanetConstants.EN_CMH:
2971
+ self.epanet_api.setflowunits(flow_units_id)
2972
+ elif flow_units_id == EpanetConstants.EN_CMD:
2973
+ self.epanet_api.setflowunits(flow_units_id)
2979
2974
  else:
2980
2975
  raise ValueError(f"Unknown flow units '{flow_units_id}'")
2981
2976
 
2982
2977
  if demand_model is not None:
2983
- self.epanet_api.setDemandModel(demand_model["type"], demand_model["pressure_min"],
2984
- demand_model["pressure_required"],
2985
- demand_model["pressure_exponent"])
2978
+ self.epanet_api.set_demand_model(demand_model["type"], demand_model["pressure_min"],
2979
+ demand_model["pressure_required"],
2980
+ demand_model["pressure_exponent"])
2986
2981
 
2987
2982
  if simulation_duration is not None:
2988
2983
  if not isinstance(simulation_duration, int) or simulation_duration <= 0:
2989
2984
  raise ValueError("'simulation_duration' must be a positive integer specifying " +
2990
2985
  "the number of seconds to simulate")
2991
- self.epanet_api.setTimeSimulationDuration(simulation_duration)
2986
+ self.epanet_api.set_simulation_duration(simulation_duration)
2992
2987
 
2993
2988
  if hydraulic_time_step is not None:
2994
2989
  if not isinstance(hydraulic_time_step, int) or hydraulic_time_step <= 0:
@@ -2997,52 +2992,47 @@ class ScenarioSimulator():
2997
2992
  if len(self._system_events) != 0:
2998
2993
  raise RuntimeError("Hydraulic time step cannot be changed after system events " +
2999
2994
  "such as leakages have been added to the scenario")
3000
- self.epanet_api.setTimeHydraulicStep(hydraulic_time_step)
2995
+ self.epanet_api.set_hydraulic_time_step(hydraulic_time_step)
3001
2996
  if reporting_time_step is None:
3002
2997
  warnings.warn("No report time steps specified -- using 'hydraulic_time_step'")
3003
- self.epanet_api.setTimeReportingStep(hydraulic_time_step)
2998
+ self.epanet_api.set_reporting_time_step(hydraulic_time_step)
3004
2999
 
3005
3000
  if reporting_time_step is not None:
3006
- hydraulic_time_step = self.epanet_api.getTimeHydraulicStep()
3001
+ hydraulic_time_step = self.epanet_api.get_hydraulic_time_step()
3007
3002
  if not isinstance(reporting_time_step, int) or \
3008
3003
  reporting_time_step % hydraulic_time_step != 0:
3009
3004
  raise ValueError("'reporting_time_step' must be a positive integer " +
3010
3005
  "and a multiple of 'hydraulic_time_step'")
3011
- self.epanet_api.setTimeReportingStep(reporting_time_step)
3006
+ self.epanet_api.set_reporting_time_step(reporting_time_step)
3012
3007
 
3013
3008
  if reporting_time_start is not None:
3014
3009
  if not isinstance(reporting_time_start, int) or reporting_time_start <= 0:
3015
3010
  raise ValueError("'reporting_time_start' must be a positive integer specifying " +
3016
3011
  "the time at which reporting starts")
3017
- self.epanet_api.setTimeReportingStart(reporting_time_start)
3012
+ self.epanet_api.set_reporting_start_time(reporting_time_start)
3018
3013
 
3019
3014
  if quality_time_step is not None:
3020
3015
  if not isinstance(quality_time_step, int) or quality_time_step <= 0 or \
3021
- quality_time_step > self.epanet_api.getTimeHydraulicStep():
3016
+ quality_time_step > self.epanet_api.get_hydraulic_time_step():
3022
3017
  raise ValueError("'quality_time_step' must be a positive integer that is not " +
3023
3018
  "greater than the hydraulic time step")
3024
- self.epanet_api.setTimeQualityStep(quality_time_step)
3019
+ self.epanet_api.set_quality_time_step(quality_time_step)
3025
3020
 
3026
3021
  if advanced_quality_time_step is not None:
3027
3022
  if not isinstance(advanced_quality_time_step, int) or \
3028
3023
  advanced_quality_time_step <= 0 or \
3029
- advanced_quality_time_step > self.epanet_api.getTimeHydraulicStep():
3024
+ advanced_quality_time_step > self.epanet_api.get_hydraulic_time_step():
3030
3025
  raise ValueError("'advanced_quality_time_step' must be a positive integer " +
3031
3026
  "that is not greater than the hydraulic time step")
3032
- self.epanet_api.setMSXTimeStep(advanced_quality_time_step)
3027
+ self.epanet_api.set_msx_time_step(advanced_quality_time_step)
3033
3028
 
3034
3029
  if quality_model is not None:
3035
- if quality_model["type"] == "NONE":
3036
- self.epanet_api.setQualityType("none")
3037
- elif quality_model["type"] == "AGE":
3038
- self.epanet_api.setQualityType("age")
3039
- elif quality_model["type"] == "CHEM":
3040
- self.epanet_api.setQualityType("chem", quality_model["chemical_name"],
3041
- qualityunit_to_str(quality_model["units"]))
3042
- elif quality_model["type"] == "TRACE":
3043
- self.epanet_api.setQualityType("trace", quality_model["trace_node_id"])
3044
- else:
3045
- raise ValueError(f"Unknown quality type: {quality_model['type']}")
3030
+ chem_name = quality_model["chem_name"] if "chem_name" in quality_model else ""
3031
+ chem_units = quality_model["chem_units"] if "chem_units" in quality_model else ""
3032
+ trace_node_id = quality_model["trace_node_id"] \
3033
+ if "trace_node_id" in quality_model else ""
3034
+ self.epanet_api.set_quality_type(quality_model["type"], chem_name, chem_units,
3035
+ trace_node_id)
3046
3036
 
3047
3037
  def get_events_active_time_points(self) -> list[int]:
3048
3038
  """
@@ -3056,7 +3046,7 @@ class ScenarioSimulator():
3056
3046
  """
3057
3047
  events_times = []
3058
3048
 
3059
- hyd_time_step = self.epanet_api.getTimeHydraulicStep()
3049
+ hyd_time_step = self.epanet_api.get_hydraulic_time_step()
3060
3050
 
3061
3051
  def __process_event(event) -> None:
3062
3052
  cur_time = event.start_time
@@ -3107,18 +3097,18 @@ class ScenarioSimulator():
3107
3097
  else:
3108
3098
  pattern_id = f"energy_price_{pump_id}"
3109
3099
 
3110
- pattern_idx = self.epanet_api.getPatternIndex(pattern_id)
3100
+ pattern_idx = self.epanet_api.getpatternindex(pattern_id)
3111
3101
  if pattern_idx != 0:
3112
3102
  warnings.warn(f"Overwriting existing pattern '{pattern_id}'")
3113
3103
 
3114
- pump_idx = self.epanet_api.getLinkIndex(pump_id)
3115
- pattern_idx = self.epanet_api.getLinkPumpEPat(pump_idx)
3104
+ pump_idx = self.epanet_api.get_link_idx(pump_id)
3105
+ pattern_idx = self.epanet_api.getlinkvalue(pump_idx, EpanetConstants.EN_PUMP_EPAT)
3116
3106
  if pattern_idx != 0:
3117
3107
  warnings.warn(f"Overwriting existing energy price pattern of pump '{pump_id}'")
3118
3108
 
3119
- self.add_pattern(pattern_id, pattern)
3120
- pattern_idx = self.epanet_api.getPatternIndex(pattern_id)
3121
- self.epanet_api.setLinkPumpEPat(pattern_idx)
3109
+ self.add_pattern(pattern_id, pattern.tolist())
3110
+ pattern_idx = self.epanet_api.getpatternindex(pattern_id)
3111
+ self.epanet_api.setlinkvalue(pump_idx, EpanetConstants.PUMP_EPAT, pattern_idx)
3122
3112
 
3123
3113
  def get_pump_energy_price_pattern(self, pump_id: str) -> np.ndarray:
3124
3114
  """
@@ -3139,14 +3129,12 @@ class ScenarioSimulator():
3139
3129
  if pump_id not in self._sensor_config.pumps:
3140
3130
  raise ValueError(f"Unknown pump '{pump_id}'")
3141
3131
 
3142
- pump_idx = self.epanet_api.getLinkIndex(pump_id)
3143
- pattern_idx = self.epanet_api.getLinkPumpEPat(pump_idx)
3132
+ pump_idx = self.epanet_api.get_link_idx(pump_id)
3133
+ pattern_idx = self.epanet_api.getlinkvalue(pump_idx, EpanetConstants.EN_PUMP_EPAT)
3144
3134
  if pattern_idx == 0:
3145
3135
  return None
3146
3136
  else:
3147
- pattern_length = self.epanet_api.getPatternLengths(pattern_idx)
3148
- return np.array([self.epanet_api.getPatternValue(pattern_idx, t+1)
3149
- for t in range(pattern_length)])
3137
+ return np.array(self.epanet_api.get_pattern(pattern_idx))
3150
3138
 
3151
3139
  def get_pump_energy_price(self, pump_id: str) -> float:
3152
3140
  """
@@ -3167,8 +3155,8 @@ class ScenarioSimulator():
3167
3155
  if pump_id not in self._sensor_config.pumps:
3168
3156
  raise ValueError(f"Unknown pump '{pump_id}'")
3169
3157
 
3170
- pump_idx = self.epanet_api.getLinkIndex(pump_id)
3171
- return self.epanet_api.getLinkPumpECost(pump_idx)
3158
+ pump_idx = self.epanet_api.get_link_idx(pump_id)
3159
+ return self.epanet_api.get_pump_avg_energy_price(pump_idx)
3172
3160
 
3173
3161
  def set_pump_energy_price(self, pump_id, price: float) -> None:
3174
3162
  """
@@ -3190,11 +3178,8 @@ class ScenarioSimulator():
3190
3178
  if price <= 0:
3191
3179
  raise ValueError("'price' must be positive")
3192
3180
 
3193
- pump_idx = self._sensor_config.pumps.index(pump_id) + 1
3194
- pumps_energy_price = self.epanet_api.getLinkPumpECost()
3195
- pumps_energy_price[pump_idx - 1] = price
3196
-
3197
- self.epanet_api.setLinkPumpECost(pumps_energy_price)
3181
+ pump_idx = self.epanet_api.get_link_idx(pump_id)
3182
+ self.epanet_api.setlinkvalue(pump_idx, EpanetConstants.EN_PUMP_ECOST, price)
3198
3183
 
3199
3184
  def set_initial_link_status(self, link_id: str, status: int) -> None:
3200
3185
  """
@@ -3219,8 +3204,8 @@ class ScenarioSimulator():
3219
3204
  if status not in [ActuatorConstants.EN_CLOSED, ActuatorConstants.EN_OPEN]:
3220
3205
  raise ValueError(f"Invalid link status '{status}'")
3221
3206
 
3222
- link_idx = self.epanet_api.getLinkIndex(link_id)
3223
- self.epanet_api.setLinkInitialStatus(link_idx, status)
3207
+ link_idx = self.epanet_api.get_link_idx(link_id)
3208
+ self.epanet_api.setlinkvalue(link_idx, EpanetConstants.EN_INITSTATUS, status)
3224
3209
 
3225
3210
  def set_initial_pump_speed(self, pump_id: str, speed: float) -> None:
3226
3211
  """
@@ -3242,8 +3227,8 @@ class ScenarioSimulator():
3242
3227
  if speed < 0:
3243
3228
  raise ValueError("'speed' can not be negative")
3244
3229
 
3245
- pump_idx = self.epanet_api.getLinkIndex(pump_id)
3246
- self.epanet_api.setLinkInitialSetting(pump_idx, speed)
3230
+ pump_idx = self.epanet_api.get_link_idx(pump_id)
3231
+ self.epanet_api.setlinkvalue(pump_idx, EpanetConstants.EN_INITSETTING, speed)
3247
3232
 
3248
3233
  def set_initial_tank_level(self, tank_id, level: int) -> None:
3249
3234
  """
@@ -3265,14 +3250,14 @@ class ScenarioSimulator():
3265
3250
  if level < 0:
3266
3251
  raise ValueError("'level' can not be negative")
3267
3252
 
3268
- tank_idx = self.epanet_api.getNodeIndex(tank_id)
3269
- self.epanet_api.setNodeTankInitialLevel(tank_idx, level)
3253
+ tank_idx = self.epanet_api.get_node_idx(tank_id)
3254
+ self.epanet_api.setnodevalue(tank_idx, EpanetConstants.EN_TANKLEVEL, level)
3270
3255
 
3271
3256
  def __warn_if_quality_set(self):
3272
- qual_info = self.epanet_api.getQualityInfo()
3273
- if qual_info.QualityCode != ToolkitConstants.EN_NONE:
3257
+ qual_code = self.epanet_api.getqualinfo()[0]
3258
+ if qual_code != EpanetConstants.EN_NONE:
3274
3259
  warnings.warn("You are overriding current quality settings " +
3275
- f"'{qual_info.QualityType}'")
3260
+ f"'{qual_code}'")
3276
3261
 
3277
3262
  def enable_waterage_analysis(self) -> None:
3278
3263
  """
@@ -3285,7 +3270,7 @@ class ScenarioSimulator():
3285
3270
  self._adapt_to_network_changes()
3286
3271
 
3287
3272
  self.__warn_if_quality_set()
3288
- self.set_general_parameters(quality_model={"type": "AGE"})
3273
+ self.set_general_parameters(quality_model={"type": EpanetConstants.EN_AGE})
3289
3274
 
3290
3275
  def enable_chemical_analysis(self, chemical_name: str = "Chlorine",
3291
3276
  chemical_units: int = MASS_UNIT_MG) -> None:
@@ -3316,8 +3301,9 @@ class ScenarioSimulator():
3316
3301
  self._adapt_to_network_changes()
3317
3302
 
3318
3303
  self.__warn_if_quality_set()
3319
- self.set_general_parameters(quality_model={"type": "CHEM", "chemical_name": chemical_name,
3320
- "units": chemical_units})
3304
+ self.set_general_parameters(quality_model={"type": EpanetConstants.EN_CHEM,
3305
+ "chem_name": chemical_name,
3306
+ "chem_units": qualityunit_to_str(chemical_units)})
3321
3307
 
3322
3308
  def add_quality_source(self, node_id: str, source_type: int, pattern: np.ndarray = None,
3323
3309
  pattern_id: str = None, source_strength: int = 1.) -> None:
@@ -3330,7 +3316,7 @@ class ScenarioSimulator():
3330
3316
  ID of the node at which this external water quality source is placed.
3331
3317
  source_type : `int`,
3332
3318
  Types of the external water quality source -- must be of the following
3333
- EPANET toolkit constants:
3319
+ EPANET constants:
3334
3320
 
3335
3321
  - EN_CONCEN = 0
3336
3322
  - EN_MASS = 1
@@ -3366,7 +3352,7 @@ class ScenarioSimulator():
3366
3352
 
3367
3353
  self._adapt_to_network_changes()
3368
3354
 
3369
- if self.epanet_api.getQualityInfo().QualityCode != ToolkitConstants.EN_CHEM:
3355
+ if self.epanet_api.getqualinfo()[0] != EpanetConstants.EN_CHEM:
3370
3356
  raise RuntimeError("Chemical analysis is not enabled -- " +
3371
3357
  "call 'enable_chemical_analysis()' before calling this function.")
3372
3358
  if node_id not in self._sensor_config.nodes:
@@ -3382,19 +3368,20 @@ class ScenarioSimulator():
3382
3368
  if pattern_id is None:
3383
3369
  pattern_id = f"qual_src_pat_{node_id}"
3384
3370
 
3385
- node_idx = self.epanet_api.getNodeIndex(node_id)
3371
+ node_idx = self.epanet_api.get_node_idx(node_id)
3386
3372
 
3387
3373
  if pattern is None:
3388
- pattern_idx = self.epanet_api.getPatternIndex(pattern_id)
3374
+ pattern_idx = self.epanet_api.getpatternindex(pattern_id)
3389
3375
  else:
3390
- pattern_idx = self.epanet_api.addPattern(pattern_id, pattern)
3376
+ self.epanet_api.add_pattern(pattern_id, pattern.tolist())
3377
+ pattern_idx = self.epanet_api.getpatternindex(pattern_id)
3391
3378
  if pattern_idx == 0:
3392
3379
  raise RuntimeError("Failed to add/get pattern! " +
3393
3380
  "Maybe pattern name contains invalid characters or is too long?")
3394
3381
 
3395
- self.epanet_api.api.ENsetnodevalue(node_idx, ToolkitConstants.EN_SOURCETYPE, source_type)
3396
- self.epanet_api.setNodeSourceQuality(node_idx, source_strength)
3397
- self.epanet_api.setNodeSourcePatternIndex(node_idx, pattern_idx)
3382
+ self.epanet_api.setnodevalue(node_idx, EpanetConstants.EN_SOURCETYPE, source_type)
3383
+ self.epanet_api.setnodevalue(node_idx, EpanetConstants.EN_SOURCEQUAL, source_strength)
3384
+ self.epanet_api.setnodevalue(node_idx, EpanetConstants.EN_SOURCEPAT, pattern_idx)
3398
3385
 
3399
3386
  def set_initial_node_quality(self, node_id: str, initial_quality: float) -> None:
3400
3387
  """
@@ -3486,12 +3473,8 @@ class ScenarioSimulator():
3486
3473
  if node_init_qual < 0:
3487
3474
  raise ValueError(f"{node_id}: Initial node quality can not be negative")
3488
3475
 
3489
- init_qual = self.epanet_api.getNodeInitialQuality()
3490
- for node_id, node_init_qual in initial_quality:
3491
- node_idx = self.epanet_api.getNodeIndex(node_id) - 1
3492
- init_qual[node_idx] = node_init_qual
3493
-
3494
- self.epanet_api.setNodeInitialQuality(init_qual)
3476
+ for node_idx in self.epanet_api.get_all_nodes_idx():
3477
+ self.epanet_api.set_node_init_quality(node_idx, node_init_qual)
3495
3478
 
3496
3479
  if order_wall is not None:
3497
3480
  if not isinstance(order_wall, int):
@@ -3500,7 +3483,7 @@ class ScenarioSimulator():
3500
3483
  if order_wall not in [0, 1]:
3501
3484
  raise ValueError(f"Invalid value '{order_wall}' for order_wall")
3502
3485
 
3503
- self.epanet_api.setOptionsPipeWallReactionOrder(order_wall)
3486
+ self.epanet_api.setoption(EpanetConstants.EN_WALLORDER, order_wall)
3504
3487
 
3505
3488
  if order_bulk is not None:
3506
3489
  if not isinstance(order_bulk, int):
@@ -3509,7 +3492,7 @@ class ScenarioSimulator():
3509
3492
  if order_bulk not in [0, 1]:
3510
3493
  raise ValueError(f"Invalid value '{order_bulk}' for order_bulk")
3511
3494
 
3512
- self.epanet_api.setOptionsPipeBulkReactionOrder(order_bulk)
3495
+ self.epanet_api.setoption(EpanetConstants.EN_BULKORDER, order_bulk)
3513
3496
 
3514
3497
  if order_tank is not None:
3515
3498
  if not isinstance(order_tank, int):
@@ -3518,29 +3501,25 @@ class ScenarioSimulator():
3518
3501
  if order_tank not in [0, 1]:
3519
3502
  raise ValueError(f"Invalid value '{order_tank}' for order_wall")
3520
3503
 
3521
- self.epanet_api.setOptionsTankBulkReactionOrder(order_tank)
3504
+ self.epanet_api.setoption(EpanetConstants.EN_TANKORDER, order_tank)
3522
3505
 
3523
3506
  if global_wall_reaction_coefficient is not None:
3524
3507
  if not isinstance(global_wall_reaction_coefficient, float):
3525
3508
  raise TypeError("'global_wall_reaction_coefficient' must be an instance of " +
3526
3509
  f"'float' but not of '{type(global_wall_reaction_coefficient)}'")
3527
3510
 
3528
- wall_reaction_coeff = self.epanet_api.getLinkWallReactionCoeff()
3529
- for i in range(len(wall_reaction_coeff)):
3530
- wall_reaction_coeff[i] = global_wall_reaction_coefficient
3531
-
3532
- self.epanet_api.setLinkWallReactionCoeff(wall_reaction_coeff)
3511
+ for link_idx in self.epanet_api.get_all_links_idx():
3512
+ self.epanet_api.setlinkvalue(link_idx, EpanetConstants.EN_KWALL,
3513
+ global_wall_reaction_coefficient)
3533
3514
 
3534
3515
  if global_bulk_reaction_coefficient is not None:
3535
3516
  if not isinstance(global_bulk_reaction_coefficient, float):
3536
3517
  raise TypeError("'global_bulk_reaction_coefficient' must be an instance of " +
3537
3518
  f"'float' but not of '{type(global_bulk_reaction_coefficient)}'")
3538
3519
 
3539
- bulk_reaction_coeff = self.epanet_api.getLinkBulkReactionCoeff()
3540
- for i in range(len(bulk_reaction_coeff)):
3541
- bulk_reaction_coeff[i] = global_bulk_reaction_coefficient
3542
-
3543
- self.epanet_api.setLinkBulkReactionCoeff(bulk_reaction_coeff)
3520
+ for link_idx in self.epanet_api.get_all_links_idx():
3521
+ self.epanet_api.setlinkvalue(link_idx, EpanetConstants.EN_KBULK,
3522
+ global_bulk_reaction_coefficient)
3544
3523
 
3545
3524
  if local_wall_reaction_coefficient is not None:
3546
3525
  if not isinstance(local_wall_reaction_coefficient, dict):
@@ -3556,8 +3535,8 @@ class ScenarioSimulator():
3556
3535
  raise ValueError(f"Invalid link ID '{link_id}'")
3557
3536
 
3558
3537
  for link_id, link_reaction_coeff in local_wall_reaction_coefficient:
3559
- link_idx = self.epanet_api.getLinkIndex(link_id)
3560
- self.epanet_api.setLinkWallReactionCoeff(link_idx, link_reaction_coeff)
3538
+ link_idx = self.epanet_api.get_link_idx(link_id)
3539
+ self.epanet_api.setlinkvalue(link_idx, EpanetConstants.EN_KWALL, link_reaction_coeff)
3561
3540
 
3562
3541
  if local_bulk_reaction_coefficient is not None:
3563
3542
  if not isinstance(local_bulk_reaction_coefficient, dict):
@@ -3573,8 +3552,8 @@ class ScenarioSimulator():
3573
3552
  raise ValueError(f"Invalid link ID '{link_id}'")
3574
3553
 
3575
3554
  for link_id, link_reaction_coeff in local_bulk_reaction_coefficient:
3576
- link_idx = self.epanet_api.getLinkIndex(link_id)
3577
- self.epanet_api.setLinkBulkReactionCoeff(link_idx, link_reaction_coeff)
3555
+ link_idx = self.epanet_api.get_link_idx(link_id)
3556
+ self.epanet_api.setlinkvalue(link_idx, EpanetConstants.EN_KBULK, link_reaction_coeff)
3578
3557
 
3579
3558
  if local_tank_reaction_coefficient is not None:
3580
3559
  if not isinstance(local_tank_reaction_coefficient, dict):
@@ -3590,8 +3569,9 @@ class ScenarioSimulator():
3590
3569
  raise ValueError(f"Invalid tank ID '{tank_id}'")
3591
3570
 
3592
3571
  for tank_id, tank_reaction_coeff in local_tank_reaction_coefficient:
3593
- tank_idx = self.epanet_api.getNodeTankIndex(tank_id)
3594
- self.epanet_api.setNodeTankBulkReactionCoeff(tank_idx, tank_reaction_coeff)
3572
+ tank_idx = self.epanet_api.get_node_idx(tank_id)
3573
+ self.epanet_api.setnodevalue(tank_idx, EpanetConstants.EN_TANK_KBULK,
3574
+ tank_reaction_coeff)
3595
3575
 
3596
3576
  if limiting_potential is not None:
3597
3577
  if not isinstance(limiting_potential, float):
@@ -3600,7 +3580,7 @@ class ScenarioSimulator():
3600
3580
  if limiting_potential < 0:
3601
3581
  raise ValueError("'limiting_potential' can not be negative")
3602
3582
 
3603
- self.epanet_api.setOptionsLimitingConcentration(limiting_potential)
3583
+ self.epanet_api.setoption(EpanetConstants.EN_CONCENLIMIT, limiting_potential)
3604
3584
 
3605
3585
  def enable_sourcetracing_analysis(self, trace_node_id: str) -> None:
3606
3586
  """
@@ -3621,7 +3601,7 @@ class ScenarioSimulator():
3621
3601
  raise ValueError(f"Invalid node ID '{trace_node_id}'")
3622
3602
 
3623
3603
  self.__warn_if_quality_set()
3624
- self.set_general_parameters(quality_model={"type": "TRACE",
3604
+ self.set_general_parameters(quality_model={"type": EpanetConstants.EN_TRACE,
3625
3605
  "trace_node_id": trace_node_id})
3626
3606
 
3627
3607
  def add_species_injection_source(self, species_id: str, node_id: str, pattern: np.ndarray,
@@ -3645,7 +3625,7 @@ class ScenarioSimulator():
3645
3625
  Note that the pattern time step is equivalent to the EPANET pattern time step.
3646
3626
  source_type : `int`,
3647
3627
  Type of the external (bulk or surface) species injection source -- must be one of
3648
- the following EPANET toolkit constants:
3628
+ the following EPANET constants:
3649
3629
 
3650
3630
  - EN_CONCEN = 0
3651
3631
  - EN_MASS = 1
@@ -3670,25 +3650,18 @@ class ScenarioSimulator():
3670
3650
 
3671
3651
  The default is 1.
3672
3652
  """
3673
- source_type_ = "None"
3674
- if source_type == ToolkitConstants.EN_CONCEN:
3675
- source_type_ = "CONCEN"
3676
- elif source_type == ToolkitConstants.EN_MASS:
3677
- source_type_ = "MASS"
3678
- elif source_type == ToolkitConstants.EN_SETPOINT:
3679
- source_type_ = "SETPOINT"
3680
- elif source_type == ToolkitConstants.EN_FLOWPACED:
3681
- source_type_ = "FLOWPACED"
3682
-
3683
3653
  if pattern_id is None:
3684
3654
  pattern_id = f"{species_id}_{node_id}"
3685
- if pattern_id in self.epanet_api.getMSXPatternsNameID():
3655
+ if pattern_id in self.epanet_api.get_all_msx_pattern_id():
3686
3656
  raise ValueError("Invalid 'pattern_id' -- " +
3687
3657
  f"there already exists a pattern with ID '{pattern_id}'")
3688
3658
 
3689
- self.epanet_api.addMSXPattern(pattern_id, pattern)
3690
- self.epanet_api.setMSXSources(node_id, species_id, source_type_, source_strength,
3691
- pattern_id)
3659
+ self.epanet_api.MSXaddpattern(pattern_id)
3660
+ pattern_idx = self.epanet_api.MSXgetindex(EpanetConstants.MSX_PATTERN, pattern_id)
3661
+ self.epanet_api.MSXsetpattern(pattern_idx, pattern.tolist(), len(pattern))
3662
+ self.epanet_api.MSXsetsource(self.epanet_api.get_node_idx(node_id),
3663
+ self.epanet_api.get_msx_species_idx(species_id),
3664
+ source_type, source_strength, pattern_idx)
3692
3665
 
3693
3666
  def set_bulk_species_node_initial_concentrations(self,
3694
3667
  inital_conc: dict[str, list[tuple[str, float]]]
@@ -3723,12 +3696,12 @@ class ScenarioSimulator():
3723
3696
  raise ValueError("Initial node concentration can not be negative")
3724
3697
 
3725
3698
  for species_id, node_initial_conc in inital_conc.items():
3726
- species_idx, = self.epanet_api.getMSXSpeciesIndex([species_id])
3699
+ species_idx = self.epanet_api.get_msx_species_idx(species_id)
3727
3700
 
3728
3701
  for node_id, initial_conc in node_initial_conc:
3729
- node_idx = self.epanet_api.getNodeIndex(node_id)
3730
- self.epanet_api.msx.MSXsetinitqual(ToolkitConstants.MSX_NODE, node_idx, species_idx,
3731
- initial_conc)
3702
+ node_idx = self.epanet_api.get_node_idx(node_id)
3703
+ self.epanet_api.MSXsetinitqual(EpanetConstants.MSX_NODE, node_idx, species_idx,
3704
+ initial_conc)
3732
3705
 
3733
3706
  def set_species_link_initial_concentrations(self,
3734
3707
  inital_conc: dict[str, list[tuple[str, float]]]
@@ -3762,9 +3735,9 @@ class ScenarioSimulator():
3762
3735
  raise ValueError("Initial link concentration can not be negative")
3763
3736
 
3764
3737
  for species_id, link_initial_conc in inital_conc.items():
3765
- species_idx, = self.epanet_api.getMSXSpeciesIndex([species_id])
3738
+ species_idx = self.epanet_api.get_msx_species_idx(species_id)
3766
3739
 
3767
3740
  for link_id, initial_conc in link_initial_conc:
3768
- link_idx = self.epanet_api.getLinkIndex(link_id)
3769
- self.epanet_api.msx.MSXsetinitqual(ToolkitConstants.MSX_LINK, link_idx, species_idx,
3770
- initial_conc)
3741
+ link_idx = self.epanet_api.get_link_idx(link_id)
3742
+ self.epanet_api.MSXsetinitqual(EpanetConstants.MSX_LINK, link_idx, species_idx,
3743
+ initial_conc)