epanet-plus 0.0.1__cp310-cp310-musllinux_1_2_x86_64.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.

Potentially problematic release.


This version of epanet-plus might be problematic. Click here for more details.

Files changed (105) hide show
  1. docs/conf.py +67 -0
  2. epanet-msx-src/dispersion.h +27 -0
  3. epanet-msx-src/hash.c +107 -0
  4. epanet-msx-src/hash.h +28 -0
  5. epanet-msx-src/include/epanetmsx.h +104 -0
  6. epanet-msx-src/include/epanetmsx_export.h +42 -0
  7. epanet-msx-src/mathexpr.c +937 -0
  8. epanet-msx-src/mathexpr.h +39 -0
  9. epanet-msx-src/mempool.c +204 -0
  10. epanet-msx-src/mempool.h +24 -0
  11. epanet-msx-src/msxchem.c +1285 -0
  12. epanet-msx-src/msxcompiler.c +368 -0
  13. epanet-msx-src/msxdict.h +42 -0
  14. epanet-msx-src/msxdispersion.c +586 -0
  15. epanet-msx-src/msxerr.c +116 -0
  16. epanet-msx-src/msxfile.c +260 -0
  17. epanet-msx-src/msxfuncs.c +175 -0
  18. epanet-msx-src/msxfuncs.h +35 -0
  19. epanet-msx-src/msxinp.c +1504 -0
  20. epanet-msx-src/msxout.c +398 -0
  21. epanet-msx-src/msxproj.c +791 -0
  22. epanet-msx-src/msxqual.c +2011 -0
  23. epanet-msx-src/msxrpt.c +400 -0
  24. epanet-msx-src/msxtank.c +422 -0
  25. epanet-msx-src/msxtoolkit.c +1164 -0
  26. epanet-msx-src/msxtypes.h +551 -0
  27. epanet-msx-src/msxutils.c +524 -0
  28. epanet-msx-src/msxutils.h +56 -0
  29. epanet-msx-src/newton.c +158 -0
  30. epanet-msx-src/newton.h +34 -0
  31. epanet-msx-src/rk5.c +287 -0
  32. epanet-msx-src/rk5.h +39 -0
  33. epanet-msx-src/ros2.c +293 -0
  34. epanet-msx-src/ros2.h +35 -0
  35. epanet-msx-src/smatrix.c +816 -0
  36. epanet-msx-src/smatrix.h +29 -0
  37. epanet-src/AUTHORS +60 -0
  38. epanet-src/LICENSE +21 -0
  39. epanet-src/enumstxt.h +151 -0
  40. epanet-src/epanet.c +5937 -0
  41. epanet-src/epanet2.c +961 -0
  42. epanet-src/epanet2.def +131 -0
  43. epanet-src/errors.dat +79 -0
  44. epanet-src/flowbalance.c +186 -0
  45. epanet-src/funcs.h +219 -0
  46. epanet-src/genmmd.c +1000 -0
  47. epanet-src/hash.c +177 -0
  48. epanet-src/hash.h +28 -0
  49. epanet-src/hydcoeffs.c +1303 -0
  50. epanet-src/hydraul.c +1164 -0
  51. epanet-src/hydsolver.c +781 -0
  52. epanet-src/hydstatus.c +442 -0
  53. epanet-src/include/epanet2.h +466 -0
  54. epanet-src/include/epanet2_2.h +1962 -0
  55. epanet-src/include/epanet2_enums.h +518 -0
  56. epanet-src/inpfile.c +884 -0
  57. epanet-src/input1.c +672 -0
  58. epanet-src/input2.c +970 -0
  59. epanet-src/input3.c +2265 -0
  60. epanet-src/leakage.c +527 -0
  61. epanet-src/mempool.c +146 -0
  62. epanet-src/mempool.h +24 -0
  63. epanet-src/output.c +853 -0
  64. epanet-src/project.c +1691 -0
  65. epanet-src/quality.c +695 -0
  66. epanet-src/qualreact.c +800 -0
  67. epanet-src/qualroute.c +696 -0
  68. epanet-src/report.c +1559 -0
  69. epanet-src/rules.c +1500 -0
  70. epanet-src/smatrix.c +871 -0
  71. epanet-src/text.h +508 -0
  72. epanet-src/types.h +928 -0
  73. epanet-src/util/cstr_helper.c +59 -0
  74. epanet-src/util/cstr_helper.h +38 -0
  75. epanet-src/util/errormanager.c +92 -0
  76. epanet-src/util/errormanager.h +39 -0
  77. epanet-src/util/filemanager.c +212 -0
  78. epanet-src/util/filemanager.h +81 -0
  79. epanet-src/validate.c +408 -0
  80. epanet.cpython-310-x86_64-linux-gnu.so +0 -0
  81. epanet_plus/VERSION +1 -0
  82. epanet_plus/__init__.py +8 -0
  83. epanet_plus/epanet_plus.c +118 -0
  84. epanet_plus/epanet_toolkit.py +2730 -0
  85. epanet_plus/epanet_wrapper.py +2414 -0
  86. epanet_plus/include/epanet_plus.h +9 -0
  87. epanet_plus-0.0.1.dist-info/METADATA +152 -0
  88. epanet_plus-0.0.1.dist-info/RECORD +105 -0
  89. epanet_plus-0.0.1.dist-info/WHEEL +5 -0
  90. epanet_plus-0.0.1.dist-info/licenses/LICENSE +21 -0
  91. epanet_plus-0.0.1.dist-info/top_level.txt +11 -0
  92. examples/basic_usage.py +35 -0
  93. python-extension/ext.c +344 -0
  94. python-extension/pyepanet.c +2133 -0
  95. python-extension/pyepanet.h +143 -0
  96. python-extension/pyepanet2.c +1823 -0
  97. python-extension/pyepanet2.h +141 -0
  98. python-extension/pyepanet_plus.c +37 -0
  99. python-extension/pyepanet_plus.h +4 -0
  100. python-extension/pyepanetmsx.c +388 -0
  101. python-extension/pyepanetmsx.h +35 -0
  102. tests/test_epanet.py +16 -0
  103. tests/test_epanetmsx.py +36 -0
  104. tests/test_epyt.py +114 -0
  105. tests/test_load_inp_from_buffer.py +18 -0
@@ -0,0 +1,2730 @@
1
+ """
2
+ This module contains a Python toolkit with higher-level functions for working with
3
+ EPANET and EPANET-MSX.
4
+ """
5
+ import os
6
+ import re
7
+ import tempfile
8
+
9
+ from .epanet_wrapper import EpanetAPI
10
+
11
+
12
+ class EpanetConstants:
13
+ """
14
+ EPANET and EPANET-MSX constants.
15
+ """
16
+ EN_MAXID = 31
17
+ EN_MAXMSG = 255
18
+ EN_ELEVATION = 0 # Elevation
19
+ EN_BASEDEMAND = 1 # Primary demand baseline value
20
+ EN_PATTERN = 2 # Primary demand time pattern index
21
+ EN_EMITTER = 3 # Emitter flow coefficient
22
+ EN_INITQUAL = 4 # Initial quality
23
+ EN_SOURCEQUAL = 5 # Quality source strength
24
+ EN_SOURCEPAT = 6 # Quality source pattern index
25
+ EN_SOURCETYPE = 7 # Quality source type (see @ref EN_SourceType)
26
+ EN_TANKLEVEL = 8 # Current computed tank water level (read only)
27
+ EN_DEMAND = 9 # Current computed demand (read only)
28
+ EN_HEAD = 10 # Current computed hydraulic head (read only)
29
+ EN_PRESSURE = 11 # Current computed pressure (read only)
30
+ EN_QUALITY = 12 # Current computed quality (read only)
31
+ EN_SOURCEMASS = 13 # Current computed quality source mass inflow (read only)
32
+ EN_INITVOLUME = 14 # Tank initial volume (read only)
33
+ EN_MIXMODEL = 15 # Tank mixing model (see @ref EN_MixingModel)
34
+ EN_MIXZONEVOL = 16 # Tank mixing zone volume (read only)
35
+ EN_TANKDIAM = 17 # Tank diameter
36
+ EN_MINVOLUME = 18 # Tank minimum volume
37
+ EN_VOLCURVE = 19 # Tank volume curve index
38
+ EN_MINLEVEL = 20 # Tank minimum level
39
+ EN_MAXLEVEL = 21 # Tank maximum level
40
+ EN_MIXFRACTION = 22 # Tank mixing fraction
41
+ EN_TANK_KBULK = 23 # Tank bulk decay coefficient
42
+ EN_TANKVOLUME = 24 # Current computed tank volume (read only)
43
+ EN_MAXVOLUME = 25 # Tank maximum volume (read only)
44
+ EN_CANOVERFLOW = 26 # Tank can overflow (= 1) or not (= 0)
45
+ EN_DEMANDDEFICIT = 27
46
+
47
+ EN_DIAMETER = 0 # Pipe/valve diameter
48
+ EN_LENGTH = 1 # Pipe length
49
+ EN_ROUGHNESS = 2 # Pipe roughness coefficient
50
+ EN_MINORLOSS = 3 # Pipe/valve minor loss coefficient
51
+ EN_INITSTATUS = 4 # Initial status (see @ref EN_LinkStatusType)
52
+ EN_INITSETTING = 5 # Initial pump speed or valve setting
53
+ EN_KBULK = 6 # Bulk chemical reaction coefficient
54
+ EN_KWALL = 7 # Pipe wall chemical reaction coefficient
55
+ EN_FLOW = 8 # Current computed flow rate (read only)
56
+ EN_VELOCITY = 9 # Current computed flow velocity (read only)
57
+ EN_HEADLOSS = 10 # Current computed head loss (read only)
58
+ EN_STATUS = 11 # Current link status (see @ref EN_LinkStatusType)
59
+ EN_SETTING = 12 # Current link setting
60
+ EN_ENERGY = 13 # Current computed pump energy usage (read only)
61
+ EN_LINKQUAL = 14 # Current computed link quality (read only)
62
+ EN_LINKPATTERN = 15 # Pump speed time pattern index
63
+ EN_PUMP_STATE = 16 # Current computed pump state (read only) (see @ref EN_PumpStateType)
64
+ EN_PUMP_EFFIC = 17 # Current computed pump efficiency (read only)
65
+ EN_PUMP_POWER = 18 # Pump constant power rating
66
+ EN_PUMP_HCURVE = 19 # Pump head v. flow curve index
67
+ EN_PUMP_ECURVE = 20 # Pump efficiency v. flow curve index
68
+ EN_PUMP_ECOST = 21 # Pump average energy price
69
+ EN_PUMP_EPAT = 22
70
+
71
+ EN_DURATION = 0 # Total simulation duration
72
+ EN_HYDSTEP = 1 # Hydraulic time step
73
+ EN_QUALSTEP = 2 # Water quality time step
74
+ EN_PATTERNSTEP = 3 # Time pattern period
75
+ EN_PATTERNSTART = 4 # Time when time patterns begin
76
+ EN_REPORTSTEP = 5 # Reporting time step
77
+ EN_REPORTSTART = 6 # Time when reporting starts
78
+ EN_RULESTEP = 7 # Rule-based control evaluation time step
79
+ EN_STATISTIC = 8 # Reporting statistic code (see @ref EN_StatisticType)
80
+ EN_PERIODS = 9 # Number of reporting time periods (read only)
81
+ EN_STARTTIME = 10 # Simulation starting time of day
82
+ EN_HTIME = 11 # Elapsed time of current hydraulic solution (read only)
83
+ EN_QTIME = 12 # Elapsed time of current quality solution (read only)
84
+ EN_HALTFLAG = 13 # Flag indicating if the simulation was halted (read only)
85
+ EN_NEXTEVENT = 14 # Shortest time until a tank becomes empty or full (read only)
86
+ EN_NEXTEVENTTANK = 15
87
+
88
+ EN_ITERATIONS = 0 # Number of hydraulic iterations taken
89
+ EN_RELATIVEERROR = 1 # Sum of link flow changes / sum of link flows
90
+ EN_MAXHEADERROR = 2 # Largest head loss error for links
91
+ EN_MAXFLOWCHANGE = 3 # Largest flow change in links
92
+ EN_MASSBALANCE = 4 # Cumulative water quality mass balance ratio
93
+ EN_DEFICIENTNODES = 5 # Number of pressure deficient nodes
94
+ EN_DEMANDREDUCTION = 6
95
+
96
+ EN_NODE = 0 # Nodes
97
+ EN_LINK = 1 # Links
98
+ EN_TIMEPAT = 2 # Time patterns
99
+ EN_CURVE = 3 # Data curves
100
+ EN_CONTROL = 4 # Simple controls
101
+ EN_RULE = 5
102
+
103
+ EN_NODECOUNT = 0 # Number of nodes (junctions + tanks + reservoirs)
104
+ EN_TANKCOUNT = 1 # Number of tanks and reservoirs
105
+ EN_LINKCOUNT = 2 # Number of links (pipes + pumps + valves)
106
+ EN_PATCOUNT = 3 # Number of time patterns
107
+ EN_CURVECOUNT = 4 # Number of data curves
108
+ EN_CONTROLCOUNT = 5 # Number of simple controls
109
+ EN_RULECOUNT = 6
110
+
111
+ EN_JUNCTION = 0 # Junction node
112
+ EN_RESERVOIR = 1 # Reservoir node
113
+ EN_TANK = 2
114
+
115
+ EN_CVPIPE = 0 # Pipe with check valve
116
+ EN_PIPE = 1 # Pipe
117
+ EN_PUMP = 2 # Pump
118
+ EN_PRV = 3 # Pressure reducing valve
119
+ EN_PSV = 4 # Pressure sustaining valve
120
+ EN_PBV = 5 # Pressure breaker valve
121
+ EN_FCV = 6 # Flow control valve
122
+ EN_TCV = 7 # Throttle control valve
123
+ EN_GPV = 8
124
+
125
+ EN_CLOSED = 0
126
+ EN_OPEN = 1
127
+
128
+ EN_PUMP_XHEAD = 0 # Pump closed - cannot supply head
129
+ EN_PUMP_CLOSED = 2 # Pump closed
130
+ EN_PUMP_OPEN = 3 # Pump open
131
+ EN_PUMP_XFLOW = 5
132
+
133
+ EN_NONE = 0 # No quality analysis
134
+ EN_CHEM = 1 # Chemical fate and transport
135
+ EN_AGE = 2 # Water age analysis
136
+ EN_TRACE = 3
137
+
138
+ EN_CONCEN = 0 # Sets the concentration of external inflow entering a node
139
+ EN_MASS = 1 # Injects a given mass/minute into a node
140
+ EN_SETPOINT = 2 # Sets the concentration leaving a node to a given value
141
+ EN_FLOWPACED = 3
142
+
143
+ EN_HW = 0 # Hazen-Williams
144
+ EN_DW = 1 # Darcy-Weisbach
145
+ EN_CM = 2
146
+
147
+ EN_CFS = 0 # Cubic feet per second
148
+ EN_GPM = 1 # Gallons per minute
149
+ EN_MGD = 2 # Million gallons per day
150
+ EN_IMGD = 3 # Imperial million gallons per day
151
+ EN_AFD = 4 # Acre-feet per day
152
+ EN_LPS = 5 # Liters per second
153
+ EN_LPM = 6 # Liters per minute
154
+ EN_MLD = 7 # Million liters per day
155
+ EN_CMH = 8 # Cubic meters per hour
156
+ EN_CMD = 9
157
+
158
+ EN_DDA = 0 # Demand driven analysis
159
+ EN_PDA = 1
160
+
161
+ EN_TRIALS = 0 # Maximum trials allowed for hydraulic convergence
162
+ EN_ACCURACY = 1 # Total normalized flow change for hydraulic convergence
163
+ EN_TOLERANCE = 2 # Water quality tolerance
164
+ EN_EMITEXPON = 3 # Exponent in emitter discharge formula
165
+ EN_DEMANDMULT = 4 # Global demand multiplier
166
+ EN_HEADERROR = 5 # Maximum head loss error for hydraulic convergence
167
+ EN_FLOWCHANGE = 6 # Maximum flow change for hydraulic convergence
168
+ EN_HEADLOSSFORM = 7 # Head loss formula (see @ref EN_HeadLossType)
169
+ EN_GLOBALEFFIC = 8 # Global pump efficiency (percent)
170
+ EN_GLOBALPRICE = 9 # Global energy price per KWH
171
+ EN_GLOBALPATTERN = 10 # Index of a global energy price pattern
172
+ EN_DEMANDCHARGE = 11 # Energy charge per max. KW usage
173
+ EN_SP_GRAVITY = 12 # Specific gravity
174
+ EN_SP_VISCOS = 13 # Specific viscosity (relative to water at 20 deg C)
175
+ EN_UNBALANCED = 14 # Extra trials allowed if hydraulics don't converge
176
+ EN_CHECKFREQ = 15 # Frequency of hydraulic status checks
177
+ EN_MAXCHECK = 16 # Maximum trials for status checking
178
+ EN_DAMPLIMIT = 17 # Accuracy level where solution damping begins
179
+ EN_SP_DIFFUS = 18 # Specific diffusivity (relative to chlorine at 20 deg C)
180
+ EN_BULKORDER = 19 # Bulk water reaction order for pipes
181
+ EN_WALLORDER = 20 # Wall reaction order for pipes (either 0 or 1)
182
+ EN_TANKORDER = 21 # Bulk water reaction order for tanks
183
+ EN_CONCENLIMIT = 22
184
+
185
+ EN_LOWLEVEL = 0 # Act when pressure or tank level drops below a setpoint
186
+ EN_HILEVEL = 1 # Act when pressure or tank level rises above a setpoint
187
+ EN_TIMER = 2 # Act at a prescribed elapsed amount of time
188
+ EN_TIMEOFDAY = 3
189
+
190
+ EN_SERIES = 0 # Report all time series points
191
+ EN_AVERAGE = 1 # Report average value over simulation period
192
+ EN_MINIMUM = 2 # Report minimum value over simulation period
193
+ EN_MAXIMUM = 3 # Report maximum value over simulation period
194
+ EN_RANGE = 4
195
+
196
+ EN_MIX1 = 0 # Complete mix model
197
+ EN_MIX2 = 1 # 2-compartment model
198
+ EN_FIFO = 2 # First in, first out model
199
+ EN_LIFO = 3
200
+
201
+ EN_NOSAVE = 0 # Don't save hydraulics; don't re-initialize flows
202
+ EN_SAVE = 1 # Save hydraulics to file, don't re-initialize flows
203
+ EN_INITFLOW = 10 # Don't save hydraulics; re-initialize flows
204
+ EN_SAVE_AND_INIT = 11
205
+
206
+ EN_CONST_HP = 0 # Constant horsepower
207
+ EN_POWER_FUNC = 1 # Power function
208
+ EN_CUSTOM = 2 # User-defined custom curve
209
+ EN_NOCURVE = 3
210
+
211
+ EN_VOLUME_CURVE = 0 # Tank volume v. depth curve
212
+ EN_PUMP_CURVE = 1 # Pump head v. flow curve
213
+ EN_EFFIC_CURVE = 2 # Pump efficiency v. flow curve
214
+ EN_HLOSS_CURVE = 3 # Valve head loss v. flow curve
215
+ EN_GENERIC_CURVE = 4
216
+
217
+ EN_UNCONDITIONAL = 0 # Delete all controls and connecing links
218
+ EN_CONDITIONAL = 1
219
+
220
+ N_NO_REPORT = 0 # No status reporting
221
+ EN_NORMAL_REPORT = 1 # Normal level of status reporting
222
+ EN_FULL_REPORT = 2
223
+
224
+ EN_R_NODE = 6 # Clause refers to a node
225
+ EN_R_LINK = 7 # Clause refers to a link
226
+ EN_R_SYSTEM = 8
227
+
228
+ EN_R_DEMAND = 0 # Nodal demand
229
+ EN_R_HEAD = 1 # Nodal hydraulic head
230
+ EN_R_GRADE = 2 # Nodal hydraulic grade
231
+ EN_R_LEVEL = 3 # Tank water level
232
+ EN_R_PRESSURE = 4 # Nodal pressure
233
+ EN_R_FLOW = 5 # Link flow rate
234
+ EN_R_STATUS = 6 # Link status
235
+ EN_R_SETTING = 7 # Link setting
236
+ EN_R_POWER = 8 # Pump power output
237
+ EN_R_TIME = 9 # Elapsed simulation time
238
+ EN_R_CLOCKTIME = 10 # Time of day
239
+ EN_R_FILLTIME = 11 # Time to fill a tank
240
+ EN_R_DRAINTIME = 12
241
+
242
+ EN_R_EQ = 0 # Equal to
243
+ EN_R_NE = 1 # Not equal
244
+ EN_R_LE = 2 # Less than or equal to
245
+ EN_R_GE = 3 # Greater than or equal to
246
+ EN_R_LT = 4 # Less than
247
+ EN_R_GT = 5 # Greater than
248
+ EN_R_IS = 6 # Is equal to
249
+ EN_R_NOT = 7 # Is not equal to
250
+ EN_R_BELOW = 8 # Is below
251
+ EN_R_ABOVE = 9
252
+
253
+ EN_R_IS_OPEN = 1 # Link is open
254
+ EN_R_IS_CLOSED = 2 # Link is closed
255
+ EN_R_IS_ACTIVE = 3
256
+
257
+ EN_MISSING = -1.E10
258
+
259
+ MSX_NODE = 0
260
+ MSX_LINK = 1
261
+ MSX_TANK = 2
262
+ MSX_SPECIES = 3
263
+ MSX_TERM = 4
264
+ MSX_PARAMETER = 5
265
+ MSX_CONSTANT = 6
266
+ MSX_PATTERN = 7
267
+ MSX_BULK = 0
268
+ MSX_WALL = 1
269
+ MSX_NOSOURCE = -1
270
+ MSX_CONCEN = 0
271
+ MSX_MASS = 1
272
+ MSX_SETPOINT = 2
273
+ MSX_FLOWPACED = 3
274
+
275
+
276
+ class EPyT(EpanetAPI):
277
+ """
278
+ Python toolkit for EPANET and EPANET-MSX.
279
+
280
+ Parameters
281
+ ----------
282
+ inp_file_in : `str`
283
+ Path to .inp file.
284
+ use_project : `bool`, optional
285
+ If True, projects will be used when calling EPANET functions (default in EPANET >= 2.2).
286
+ Note that this is incompatible with EPANET-MSX. Please set to False when using EPANET-MSX.
287
+
288
+ The default is False.
289
+ """
290
+ def __init__(self, inp_file_in: str, use_project: bool = False, **kwds):
291
+ super().__init__(use_project=use_project, **kwds)
292
+
293
+ if use_project is True:
294
+ self.createproject()
295
+ self.open(inp_file_in, inp_file_in + ".rpt", "")
296
+
297
+ self._inp_file = inp_file_in
298
+ self._msx_file = None
299
+
300
+ def __enter__(self):
301
+ return self
302
+
303
+ def __exit__(self, *args):
304
+ self.close()
305
+
306
+ def load_msx_file(self, msx_file_in: str) -> None:
307
+ """
308
+ Loads an EPANET-MSX file.
309
+
310
+ Parameters
311
+ ----------
312
+ msx_file_in : `str`
313
+ Path to .msx file.
314
+ """
315
+ if self._use_project is True:
316
+ raise ValueError("EPANET-MSX can not be used with EPANET projects")
317
+
318
+ self.MSXopen(msx_file_in)
319
+ self._msx_file = msx_file_in
320
+
321
+ def close(self) -> None:
322
+ """
323
+ Closes EPANET and EPANET-MSX, and deletes all temprorary files.
324
+ """
325
+ if self._msx_file is not None:
326
+ self.MSXclose()
327
+
328
+ super().close()
329
+
330
+ if self._use_project is True:
331
+ self.deleteproject()
332
+
333
+ @property
334
+ def msx_file(self) -> str:
335
+ """
336
+ Returns the file path to the .msx file.
337
+
338
+ Returns
339
+ -------
340
+ `str`
341
+ File path to .msx file.
342
+ """
343
+ return self._msx_file
344
+
345
+ def get_all_nodes_id(self) -> list[str]:
346
+ """
347
+ Returns all node IDs.
348
+
349
+ Returns
350
+ -------
351
+ `list[str]`
352
+ List of node IDs.
353
+ """
354
+ return [self.getnodeid(i + 1) for i in range(self.getcount(EpanetConstants.EN_NODECOUNT))]
355
+
356
+ def get_num_nodes(self) -> int:
357
+ """
358
+ Returns the number of nodes in the network.
359
+
360
+ Returns
361
+ -------
362
+ `int`
363
+ Number of nodes.
364
+ """
365
+ return self.getcount(EpanetConstants.EN_NODECOUNT)
366
+
367
+ def get_all_nodes_idx(self) -> list[int]:
368
+ """
369
+ Returns all node indices.
370
+
371
+ Returns
372
+ -------
373
+ `list[int]`
374
+ List of node indices:
375
+ """
376
+ return list(range(1, self.getcount(EpanetConstants.EN_NODECOUNT) + 1))
377
+
378
+ def get_all_junctions_id(self) -> list[str]:
379
+ """
380
+ Returns all junction IDs -- i.e., IDs of nodes that are neither a reservoir
381
+ nor a tank.
382
+
383
+ Returns
384
+ -------
385
+ `list[str]`
386
+ List of all junction IDs.
387
+ """
388
+ r = []
389
+
390
+ for i in range(self.getcount(EpanetConstants.EN_NODECOUNT)):
391
+ if self.getnodetype(i + 1) == EpanetConstants.EN_JUNCTION:
392
+ r.append(self.getnodeid(i + 1))
393
+
394
+ return []
395
+
396
+ def get_all_junctions_idx(self) -> list[int]:
397
+ """
398
+ Returns all junction indices -- i.e., indices of nodes that are neither a reservoir
399
+ nor a tank.
400
+
401
+ Returns
402
+ -------
403
+ `list[int]`
404
+ List of all junction indices.
405
+ """
406
+ r = []
407
+
408
+ for i in range(self.getcount(EpanetConstants.EN_NODECOUNT)):
409
+ if self.getnodetype(i + 1) == EpanetConstants.EN_JUNCTION:
410
+ r.append(i + 1)
411
+
412
+ return []
413
+
414
+ def get_num_junctions(self) -> int:
415
+ """
416
+ Returns the number of junctions -- i.e., number of nodes that are neither a tank nor
417
+ a reservoir.
418
+
419
+ Returns
420
+ -------
421
+ `int`
422
+ Number of junctions.
423
+ """
424
+ return len(self.get_all_junctions_idx())
425
+
426
+ def get_all_links_id(self) -> list[str]:
427
+ """
428
+ Returns all link IDs.
429
+
430
+ Returns
431
+ -------
432
+ `list[str]`
433
+ List of all link IDs.
434
+ """
435
+ return [self.getlinkid(i + 1) for i in range(self.getcount(EpanetConstants.EN_LINKCOUNT))]
436
+
437
+ def get_all_links_idx(self) -> list[int]:
438
+ """
439
+ Returns all link indcies.
440
+
441
+ Returns
442
+ -------
443
+ `list[int]`
444
+ List of all link indices.
445
+ """
446
+ return list(range(1, self.getcount(EpanetConstants.EN_LINKCOUNT) + 1))
447
+
448
+ def get_num_links(self) -> int:
449
+ """
450
+ Returns the number of links in the network.
451
+
452
+ Returns
453
+ -------
454
+ `int`
455
+ Number of links.
456
+ """
457
+ return self.getcount(EpanetConstants.EN_LINKCOUNT)
458
+
459
+ def get_all_pipes_idx(self) -> list[int]:
460
+ """
461
+ Return the indices of all pipes in the network.
462
+
463
+ Returns
464
+ -------
465
+ `list[int]`
466
+ List of pipe indices.
467
+ """
468
+ r = []
469
+
470
+ for i in range(self.getcount(EpanetConstants.EN_LINKCOUNT)):
471
+ link_type = self.getlinktype(i + 1)
472
+ if link_type == EpanetConstants.EN_PIPE:
473
+ r.append(i + 1)
474
+
475
+ return r
476
+
477
+ def get_all_pipes_id(self) -> list[str]:
478
+ """
479
+ Returns the IDs of all pipes in the network.
480
+
481
+ Returns
482
+ -------
483
+ `list[str]`
484
+ List of pipe IDs.
485
+ """
486
+ r = []
487
+
488
+ for i in range(self.getcount(EpanetConstants.EN_LINKCOUNT)):
489
+ link_type = self.getlinktype(i + 1)
490
+ if link_type == EpanetConstants.EN_PIPE:
491
+ r.append(self.getlinkid(i + 1))
492
+
493
+ return r
494
+
495
+ def get_num_pipes(self) -> int:
496
+ """
497
+ Returns the number of pipes in the network.
498
+
499
+ Returns
500
+ -------
501
+ `int`
502
+ Returns the maximum number of pipes.
503
+ """
504
+ return len(self.get_all_pipes_idx())
505
+
506
+ def get_all_valves_id(self) -> list[str]:
507
+ """
508
+ Returns a list of all valve IDs.
509
+
510
+ Returns
511
+ -------
512
+ `list[str]`
513
+ List of all valve IDs.
514
+ """
515
+ r = []
516
+
517
+ for i in range(self.getcount(EpanetConstants.EN_LINKCOUNT)):
518
+ link_type = self.getlinktype(i + 1)
519
+ if link_type != EpanetConstants.EN_PIPE and link_type != EpanetConstants.EN_PUMP:
520
+ r.append(self.getlinkid(i + 1))
521
+
522
+ return r
523
+
524
+ def get_num_valves(self) -> int:
525
+ """
526
+ Returns the number of valves.
527
+
528
+ Returns
529
+ -------
530
+ `int`
531
+ Number of valves.
532
+ """
533
+ return len(self.get_all_valves_idx())
534
+
535
+ def get_all_valves_idx(self) -> list[int]:
536
+ """
537
+ Returns all valve indices.
538
+
539
+ Returns
540
+ -------
541
+ `list[int]`
542
+ List of all valve indices.
543
+ """
544
+ r = []
545
+
546
+ for i in range(self.getcount(EpanetConstants.EN_LINKCOUNT)):
547
+ link_type = self.getlinktype(i + 1)
548
+ if link_type != EpanetConstants.EN_PIPE and link_type != EpanetConstants.EN_PUMP:
549
+ r.append(i + 1)
550
+
551
+ return r
552
+
553
+ def get_all_pumps_id(self) -> list[str]:
554
+ """
555
+ Returns all pump IDs.
556
+
557
+ Returns
558
+ -------
559
+ `list[str]`
560
+ List of all pump IDs.
561
+ """
562
+ r = []
563
+
564
+ for i in range(self.getcount(EpanetConstants.EN_LINKCOUNT)):
565
+ if self.getlinktype(i + 1) == EpanetConstants.EN_PUMP:
566
+ r.append(self.getlinkid(i + 1))
567
+
568
+ return r
569
+
570
+ def get_num_pumps(self) -> int:
571
+ """
572
+ Returns the number of pumps in the network.
573
+
574
+ Returns
575
+ -------
576
+ `int`
577
+ Number of pumps.
578
+ """
579
+ return len(self.get_all_pumps_idx())
580
+
581
+ def get_all_pumps_idx(self) -> list[int]:
582
+ """
583
+ Returns all pump_indices.
584
+
585
+ Returns
586
+ -------
587
+ `list[int]`
588
+ List of all pump indices.
589
+ """
590
+ r = []
591
+
592
+ for i in range(self.getcount(EpanetConstants.EN_LINKCOUNT)):
593
+ if self.getlinktype(i + 1) == EpanetConstants.EN_PUMP:
594
+ r.append(i + 1)
595
+
596
+ return r
597
+
598
+ def get_all_tanks_id(self) -> list[str]:
599
+ """
600
+ Returns all tank IDs.
601
+
602
+ Returns
603
+ -------
604
+ `list[str]`
605
+ List of all tank IDs.
606
+ """
607
+ r = []
608
+
609
+ for i in range(self.getcount(EpanetConstants.EN_NODECOUNT)):
610
+ if self.getnodetype(i + 1) == EpanetConstants.EN_TANK:
611
+ r.append(self.getnodeid(i + 1))
612
+
613
+ return r
614
+
615
+ def get_num_tanks(self) -> int:
616
+ """
617
+ Returns the number of tanks in the network.
618
+
619
+ Returns
620
+ -------
621
+ `int`
622
+ Number of tanks.
623
+ """
624
+ return self.getcount(EpanetConstants.EN_TANKCOUNT)
625
+
626
+ def get_all_tanks_idx(self) -> list[int]:
627
+ """
628
+ Returns all tank indices.
629
+
630
+ Returns
631
+ -------
632
+ `list[int]`
633
+ List of all tank indices.
634
+ """
635
+ r = []
636
+
637
+ for i in range(self.getcount(EpanetConstants.EN_NODECOUNT)):
638
+ if self.getnodetype(i + 1) == EpanetConstants.EN_TANK:
639
+ r.append(i + 1)
640
+
641
+ return r
642
+
643
+ def get_all_reservoirs_id(self) -> list[str]:
644
+ """
645
+ Returns all reservoir IDs.
646
+
647
+ Returns
648
+ -------
649
+ `list[str]`
650
+ List of all reservoir IDs.
651
+ """
652
+ r = []
653
+
654
+ for i in range(self.getcount(EpanetConstants.EN_NODECOUNT)):
655
+ if self.getnodetype(i + 1) == EpanetConstants.EN_RESERVOIR:
656
+ r.append(self.getnodeid(i + 1))
657
+
658
+ return r
659
+
660
+ def get_num_reservoirs(self) -> int:
661
+ """
662
+ Returns the number of reservoirs in the network.
663
+
664
+ Returns
665
+ -------
666
+ `int`
667
+ Number of reservoirs.
668
+ """
669
+ return len(self.get_all_reservoirs_idx())
670
+
671
+ def get_all_reservoirs_idx(self) -> list[int]:
672
+ """
673
+ Returns all reservoir indices.
674
+
675
+ Returns
676
+ -------
677
+ `list[int]`
678
+ List of all reservoir indices.
679
+ """
680
+ r = []
681
+
682
+ for i in range(self.getcount(EpanetConstants.EN_NODECOUNT)):
683
+ if self.getnodetype(i + 1) == EpanetConstants.EN_RESERVOIR:
684
+ r.append(i + 1)
685
+
686
+ return r
687
+
688
+ def get_node_idx(self, node_id: str) -> int:
689
+ """
690
+ Returns the index of a given node.
691
+
692
+ Parameters
693
+ ----------
694
+ node_id : `str`
695
+ ID of the node.
696
+
697
+ Returns
698
+ -------
699
+ `int`
700
+ Index of the node.
701
+ """
702
+ return self.getnodeindex(node_id)
703
+
704
+ def get_link_idx(self, link_id: str) -> int:
705
+ """
706
+ Returns the index of a given link.
707
+
708
+ Parameters
709
+ ----------
710
+ link_id : `str`
711
+ ID of the link.
712
+
713
+ Returns
714
+ -------
715
+ `int`
716
+ Index of the link.
717
+ """
718
+ return self.getlinkindex(link_id)
719
+
720
+ def get_node_id(self, node_idx) -> str:
721
+ """
722
+ Returns the ID of a given node.
723
+
724
+ Parameters
725
+ ----------
726
+ node_idx : `int`
727
+ Index of the node.
728
+
729
+ Returns
730
+ -------
731
+ `str`
732
+ ID of the node.
733
+ """
734
+ return self.getnodeid(node_idx)
735
+
736
+ def get_link_id(self, link_idx) -> str:
737
+ """
738
+ Returns the ID of a given link.
739
+
740
+ Parameters
741
+ ----------
742
+ link_idx : `int`
743
+ Index of the link.
744
+
745
+ Returns
746
+ -------
747
+ `str`
748
+ ID of the link.
749
+ """
750
+ return self.getlinkid(link_idx)
751
+
752
+ def get_node_type(self, node_idx: int) -> int:
753
+ """
754
+ Returns the type of a given node.
755
+
756
+ Parameters
757
+ ----------
758
+ node_idx : `int`
759
+ Index of the node.
760
+
761
+ Returns
762
+ -------
763
+ `int`
764
+ Type of the node. Will be one of the following:
765
+
766
+ - EN_JUNCTION
767
+ - EN_TANK
768
+ - EN_RESERVOIR
769
+ """
770
+ return self.getnodetype(node_idx)
771
+
772
+ def get_link_type(self, link_idx: int) -> int:
773
+ """
774
+ Returns the type of a given link.
775
+
776
+ Parameters
777
+ ----------
778
+ link_idx : `int`
779
+ Index of the link.
780
+
781
+ Returns
782
+ -------
783
+ `int`
784
+ Type of the link. Will be one of the following:
785
+
786
+ - EN_CVPIPE
787
+ - EN_PIPE
788
+ - EN_PUMP
789
+ - EN_PRV
790
+ - EN_PSV
791
+ - EN_PBV
792
+ - EN_FCV
793
+ - EN_TCV
794
+ - EN_GPV
795
+ """
796
+ return self.getlinktype(link_idx)
797
+
798
+ def get_quality_info(self) -> dict:
799
+ """
800
+ Returns the water quality analysis parameters.
801
+
802
+ Returns
803
+ -------
804
+ `dict`
805
+ Water quality analysis information as a dictionary with the following entries:
806
+
807
+ - 'qualType': type of quality analysis (EN_NONE, EN_CHEM, EN_AGE, or EN_TRACE);
808
+ - 'chemName': name of chemical constituent;
809
+ - 'chemUnits': concentration units of constituent;
810
+ - 'traceNode': ID of node being traced (if applicable,
811
+ only if 'qualType' = EN_TRACE);
812
+ """
813
+ r = dict(zip(["qualType", "chemName", "chemUnits", "traceNode"], self.getqualinfo()))
814
+ if r["qualType"] == EpanetConstants.EN_AGE:
815
+ r["chemUnits"] = "hrs"
816
+
817
+ if r["qualType"] == EpanetConstants.EN_TRACE:
818
+ r["traceNode"] = self.get_node_id(r["traceNode"])
819
+ else:
820
+ r["traceNode"] = ""
821
+
822
+ return r
823
+
824
+ def get_quality_type(self) -> dict:
825
+ """
826
+ Returns the type of quality analysis.
827
+
828
+ Returns
829
+ -------
830
+ `dict`
831
+ Dictioanry containing the type of quality analysis and the
832
+ ID of the node being traced (if applicable):
833
+
834
+ - 'qualType': type of quality analysis (EN_NONE, EN_CHEM, EN_AGE, or EN_TRACE);
835
+ - 'traceNode': ID of node being traced (if applicable, only if 'qualType' = EN_TRACE);
836
+ """
837
+ r = dict(zip(["qualType", "traceNode"], self.getqualtype()))
838
+ if r["qualType"] == EpanetConstants.EN_TRACE:
839
+ r["traceNode"] = self.get_node_id(r["traceNode"])
840
+ else:
841
+ r["traceNode"] = ""
842
+
843
+ return r
844
+
845
+ def set_quality_type(self, qual_code: int, chem_name: str, chem_units: str,
846
+ tracenode_id: str) -> None:
847
+ """
848
+ Specifies the water quality analysis parameters.
849
+
850
+ Parameters
851
+ ----------
852
+ qual_code : `int`
853
+ Type of quality analysis. Must be one of the following:
854
+
855
+ - EN_NONE
856
+ - EN_CHEM
857
+ - EN_AGE
858
+ - EN_TRACE
859
+ chem_name : `str`
860
+ Name of chemical constituent
861
+ chem_units : `str`
862
+ Concentration units of constituent
863
+ tracenode_id : `str`
864
+ ID of node being traced (if applicable, only if 'qualType' = EN_TRACE).
865
+ """
866
+ self.setqualtype(qual_code, chem_name, chem_units, tracenode_id)
867
+
868
+ def get_num_controls(self) -> int:
869
+ """
870
+ Returns the number of controls.
871
+
872
+ Returns
873
+ -------
874
+ `int`
875
+ Number of controls.
876
+ """
877
+ return self.getcount(EpanetConstants.EN_CONTROLCOUNT)
878
+
879
+ def add_control(self, control_type: int, link_index: int, setting: float, node_index: int,
880
+ level: float) -> None:
881
+ """
882
+ Adds a control.
883
+
884
+ Parameters
885
+ ----------
886
+ control_type : `int`
887
+ Type of control. Must be one of the following:
888
+
889
+ - EN_LOWLEVEL
890
+ - EN_HILEVEL
891
+ - EN_TIMER
892
+ - EN_TIMEOFDAY
893
+ link_index : `int`
894
+ Index of the link (i.e., valve or pump) that is being controlled.
895
+ setting : `float`
896
+ Link control setting (e.g., pump speed).
897
+ node_index : `int`
898
+ Index of the node that is controlling the link.
899
+ level : `float`
900
+ Control activation level -- pressure for junction nodes, water level for tank nodes
901
+ or time value for time-based control.
902
+ """
903
+ self.addcontrol(control_type, link_index, setting, node_index, level)
904
+
905
+ def remove_all_controls(self) -> None:
906
+ """
907
+ Removes all controls.
908
+ """
909
+ while self.getcount(EpanetConstants.EN_CONTROLCOUNT) > 0:
910
+ self.deletecontrol(1)
911
+
912
+ def get_num_rules(self) -> int:
913
+ """
914
+ Returns the numer of rules.
915
+
916
+ Returns
917
+ -------
918
+ `int`
919
+ Number of rules.
920
+ """
921
+ return self.getcount(EpanetConstants.EN_RULECOUNT)
922
+
923
+ def get_all_rules_id(self) -> list[str]:
924
+ """
925
+ Returns the IDs of all rules.
926
+
927
+ Returns
928
+ -------
929
+ `list[str]`
930
+ List of rule IDs -- ordered by their index.
931
+ """
932
+ return [self.getruleid(i + 1) for i in range(self.getcount(EpanetConstants.EN_RULECOUNT))]
933
+
934
+ def remove_all_rules(self) -> None:
935
+ """
936
+ Removes all rules.
937
+ """
938
+ while self.getcount(EpanetConstants.EN_RULECOUNT) > 0:
939
+ self.deleterule(1)
940
+
941
+ def get_hydraulic_time_step(self) -> int:
942
+ """
943
+ Returns the hydraulic time step in seconds.
944
+
945
+ Returns
946
+ -------
947
+ `int`
948
+ Hydraulic time step.
949
+ """
950
+ return self.gettimeparam(EpanetConstants.EN_HYDSTEP)
951
+
952
+ def set_hydraulic_time_step(self, time_step: int) -> None:
953
+ """
954
+ Specifies the hydraulic time step.
955
+
956
+ Parameters
957
+ ----------
958
+ time_step : `int`
959
+ Hydraulic time step in seconds.
960
+ """
961
+ self.settimeparam(EpanetConstants.EN_HYDSTEP, time_step)
962
+
963
+ def get_quality_time_step(self) -> int:
964
+ """
965
+ Returns the quality time step in seconds.
966
+
967
+ Returns
968
+ -------
969
+ `int`
970
+ Quality time step.
971
+ """
972
+ return self.gettimeparam(EpanetConstants.EN_QUALSTEP)
973
+
974
+ def set_quality_time_step(self, time_step: int) -> None:
975
+ """
976
+ Specifies the water quality time step.
977
+
978
+ Parameters
979
+ ----------
980
+ time_step : `int`
981
+ Water quality time step in seconds.
982
+ """
983
+ self.settimeparam(EpanetConstants.EN_QUALSTEP, time_step)
984
+
985
+ def get_reporting_time_step(self) -> int:
986
+ """
987
+ Returns the reporting time step in seconds.
988
+
989
+ Returns
990
+ -------
991
+ `int`
992
+ Reporting time step.
993
+ """
994
+ return self.gettimeparam(EpanetConstants.EN_REPORTSTEP)
995
+
996
+ def set_reporting_time_step(self, time_step: int) -> None:
997
+ """
998
+ Specifies the reporting time step.
999
+
1000
+ Parameters
1001
+ ----------
1002
+ time_step : `int`
1003
+ Reporting time step in seconds.
1004
+ """
1005
+ self.settimeparam(EpanetConstants.EN_REPORTSTEP, time_step)
1006
+
1007
+ def get_reporting_start_time(self) -> int:
1008
+ """
1009
+ Returns the reporting start time in seconds since the simulation start.
1010
+
1011
+ Returns
1012
+ -------
1013
+ `int`
1014
+ Reporting start time.
1015
+ """
1016
+ return self.gettimeparam(EpanetConstants.EN_REPORTSTART)
1017
+
1018
+ def set_reporting_start_time(self, start_time: int) -> None:
1019
+ """
1020
+ Specifies the start time of reporting.
1021
+
1022
+ Parameters
1023
+ ----------
1024
+ time_step : `int`
1025
+ Reporting start time step in seconds since simulation start.
1026
+ """
1027
+ self.settimeparam(EpanetConstants.EN_REPORTSTART, start_time)
1028
+
1029
+ def get_simulation_duration(self) -> int:
1030
+ """
1031
+ Returns the simulation duration in seconds.
1032
+
1033
+ Returns
1034
+ -------
1035
+ `int`
1036
+ Simulation duration.
1037
+ """
1038
+ return self.gettimeparam(EpanetConstants.EN_DURATION)
1039
+
1040
+ def set_simulation_duration(self, duration: int) -> None:
1041
+ """
1042
+ Sets the simulation duration.
1043
+
1044
+ Parameters
1045
+ ----------
1046
+ duration : `int`
1047
+ Simulation duration in seconds.
1048
+ """
1049
+ self.settimeparam(EpanetConstants.EN_DURATION, duration)
1050
+
1051
+ def get_demand_model(self) -> dict:
1052
+ """
1053
+ Returns the specifications of the demand model.
1054
+
1055
+ Returns
1056
+ -------
1057
+ `dict`
1058
+ Dictionary contains the specifications of the demand model:
1059
+
1060
+ - 'type': type of demand model (either EN_DDA or EN_PDA);
1061
+ - 'pmin': minimum pressure for any demand;
1062
+ - 'preq': required pressure for full demand;
1063
+ - 'pexp': exponent in pressure dependent demand formula;
1064
+ """
1065
+ return dict(zip(["type", "pmin", "preq", "pexp"], self.getdemandmodel()))
1066
+
1067
+ def set_demand_model(self, model_type: int, pmin: float, preq: float, pexp: float) -> None:
1068
+ """
1069
+ Specifies the demand model.
1070
+
1071
+ Parameters
1072
+ ----------
1073
+ model_type : `int`
1074
+ Type of demand model. Must be one of the following:
1075
+
1076
+ - EN_DDA
1077
+ - EN_PDA
1078
+ pmin : `float`
1079
+ Minimum pressure for any demand.
1080
+ preq : `float`
1081
+ Required pressure for full demand.
1082
+ pexp : `float`
1083
+ Exponent in pressure dependent demand formula.
1084
+ """
1085
+ self.setdemandmodel(model_type, pmin, preq, pexp)
1086
+
1087
+ def get_link_diameter(self, link_idx: int) -> float:
1088
+ """
1089
+ Returns the diameter of a given link.
1090
+
1091
+ Parameters
1092
+ ----------
1093
+ link_idx : `int`
1094
+ Index of the link.
1095
+
1096
+ Returns
1097
+ -------
1098
+ `float`
1099
+ Diameter of the link.
1100
+ """
1101
+ return self.getlinkvalue(link_idx, EpanetConstants.EN_DIAMETER)
1102
+
1103
+ def get_link_length(self, link_idx: int) -> float:
1104
+ """
1105
+ Returns the length of a given link.
1106
+
1107
+ Parameters
1108
+ ----------
1109
+ link_idx : `int`
1110
+ Index of the link.
1111
+
1112
+ Returns
1113
+ -------
1114
+ `float`
1115
+ Length of the link.
1116
+ """
1117
+ return self.getlinkvalue(link_idx, EpanetConstants.EN_LENGTH)
1118
+
1119
+ def get_link_roughness(self, link_idx: int) -> dict:
1120
+ """
1121
+ Returns the roughness coefficient of a given link.
1122
+
1123
+ Parameters
1124
+ ----------
1125
+ link_idx : `int`
1126
+ Index of the link.
1127
+
1128
+ Returns
1129
+ -------
1130
+ `float`
1131
+ Roughness coefficient of the link.
1132
+ """
1133
+ return self.getlinkvalue(link_idx, EpanetConstants.EN_ROUGHNESS)
1134
+
1135
+ def get_link_minorloss(self, link_idx: int) -> float:
1136
+ """
1137
+ Returns the minor loss coefficient of a given link.
1138
+
1139
+ Parameters
1140
+ ----------
1141
+ link_idx : `int`
1142
+ Index of the link.
1143
+
1144
+ Returns
1145
+ -------
1146
+ `float`
1147
+ Minor loss coefficient of the link.
1148
+ """
1149
+ return self.getlinkvalue(link_idx, EpanetConstants.EN_MINORLOSS)
1150
+
1151
+ def get_link_init_status(self, link_idx: int) -> int:
1152
+ """
1153
+ Returns the initial status (open or closed) of a given link.
1154
+
1155
+ Parameters
1156
+ ----------
1157
+ link_idx : `int`
1158
+ Index of the link.
1159
+
1160
+ Returns
1161
+ -------
1162
+ `int`
1163
+ Initial status of the link. Will be one of the following:
1164
+
1165
+ - EN_CLOSED
1166
+ - EN_OPEN
1167
+ """
1168
+ return int(self.getlinkvalue(link_idx, EpanetConstants.EN_INITSTATUS))
1169
+
1170
+ def get_link_init_setting(self, link_idx: int) -> float:
1171
+ """
1172
+ Returns the initial setting of a given link.
1173
+
1174
+ Parameters
1175
+ ----------
1176
+ link_idx : `int`
1177
+ Index of the link.
1178
+
1179
+ Returns
1180
+ -------
1181
+ `float`
1182
+ Initial setting.
1183
+ """
1184
+ return self.getlinkvalue(link_idx, EpanetConstants.EN_INITSETTING)
1185
+
1186
+ def get_link_bulk_deacy(self, link_idx: int) -> float:
1187
+ """
1188
+ Returns the bulk decay rate at a given link.
1189
+
1190
+ Parameters
1191
+ ----------
1192
+ link_idx : `int`
1193
+ Index of the link.
1194
+
1195
+ Returns
1196
+ -------
1197
+ `float`
1198
+ Bulk decay rate.
1199
+ """
1200
+ return self.getlinkvalue(link_idx, EpanetConstants.EN_KBULK)
1201
+
1202
+ def get_link_wall_decay(self, link_idx: int) -> float:
1203
+ """
1204
+ Returns the wall decay rate at a given link.
1205
+
1206
+ Parameters
1207
+ ----------
1208
+ link_idx : `int`
1209
+ Index of the link.
1210
+
1211
+ Returns
1212
+ -------
1213
+ `float`
1214
+ Wall decay rate.
1215
+ """
1216
+ return self.getlinkvalue(link_idx, EpanetConstants.EN_KWALL)
1217
+
1218
+ def get_node_comment(self, node_idx: int) -> str:
1219
+ """
1220
+ Returns the comment of a given node.
1221
+
1222
+ Parameters
1223
+ ----------
1224
+ node_idx : `int`
1225
+ Index of the node.
1226
+
1227
+ Returns
1228
+ -------
1229
+ `str`
1230
+ Comment.
1231
+ """
1232
+ return self.getcomment(EpanetConstants.EN_NODE, node_idx)
1233
+
1234
+ def get_node_elevation(self, node_idx: int) -> float:
1235
+ """
1236
+ Returns the evelvation of a given node.
1237
+
1238
+ Parameters
1239
+ ----------
1240
+ node_idx : `int`
1241
+ Index of the node.
1242
+
1243
+ Returns
1244
+ -------
1245
+ `float`
1246
+ Elevation.
1247
+ """
1248
+ return self.getnodevalue(node_idx, EpanetConstants.EN_ELEVATION)
1249
+
1250
+ def get_node_emitter_coeff(self, node_idx: int) -> float:
1251
+ """
1252
+ Returns the roughness coefficient of a given node.
1253
+
1254
+ Parameters
1255
+ ----------
1256
+ node_idx : `int`
1257
+ Index of the node.
1258
+
1259
+ Returns
1260
+ -------
1261
+ `float`
1262
+ Emitter coefficient of the node.
1263
+ """
1264
+ return self.getnodevalue(node_idx, EpanetConstants.EN_EMITTER)
1265
+
1266
+ def get_node_init_qual(self, node_idx: int) -> float:
1267
+ """
1268
+ Returns the initial quality value/state of a given node.
1269
+
1270
+ Parameters
1271
+ ----------
1272
+ node_idx : `int`
1273
+ Index of the node.
1274
+
1275
+ Returns
1276
+ -------
1277
+ `float`
1278
+ Initial quality state/value.
1279
+ """
1280
+ return self.getnodevalue(node_idx, EpanetConstants.EN_INITQUAL)
1281
+
1282
+ def get_node_source_qual(self, node_idx: int) -> float:
1283
+ """
1284
+ Returns the current quality state/value at a given node.
1285
+
1286
+ Parameters
1287
+ ----------
1288
+ node_idx : `int`
1289
+ Index of the node.
1290
+
1291
+ Returns
1292
+ -------
1293
+ `float`
1294
+ Current quality state/value.
1295
+ """
1296
+ return self.getnodevalue(node_idx, EpanetConstants.EN_SOURCEQUAL)
1297
+
1298
+ def get_node_source_type(self, node_idx: int) -> int:
1299
+ """
1300
+ Returns the type of the water quality source at a given node.
1301
+
1302
+ Parameters
1303
+ ----------
1304
+ node_idx : `int`
1305
+ Index of the node.
1306
+
1307
+ Returns
1308
+ -------
1309
+ `int`
1310
+ Type of the water quality source. Will be one of the following:
1311
+
1312
+ - EN_CONCEN
1313
+ - EN_MASS
1314
+ - EN_SETPOINT
1315
+ - EN_FLOWPACED
1316
+ """
1317
+ return int(self.getnodevalue(node_idx, EpanetConstants.EN_SOURCETYPE))
1318
+
1319
+ def get_node_source_pattern_idx(self, node_idx: int) -> int:
1320
+ """
1321
+ Returns the index of the quality source pattern at a given node.
1322
+
1323
+ Parameters
1324
+ ----------
1325
+ node_idx : `int`
1326
+ Index of the node.
1327
+
1328
+ Returns
1329
+ -------
1330
+ `int`
1331
+ Index of the pattern.
1332
+ """
1333
+ return int(self.getnodevalue(node_idx, EpanetConstants.EN_SOURCEPAT))
1334
+
1335
+ def get_node_pattern_idx(self, node_idx: int) -> float:
1336
+ """
1337
+ Returns the index of the primary demand pattern of a given node.
1338
+
1339
+ Parameters
1340
+ ----------
1341
+ node_idx : `int`
1342
+ Index of the node.
1343
+
1344
+ Returns
1345
+ -------
1346
+ `int`
1347
+ Index of the primary demand pattern.
1348
+ """
1349
+ return int(self.getnodevalue(node_idx, EpanetConstants.EN_PATTERN))
1350
+
1351
+ def get_node_base_demand(self, node_idx: int) -> float:
1352
+ """
1353
+ Returns the primary base demand of a given node.
1354
+
1355
+ Parameters
1356
+ ----------
1357
+ node_idx : `int`
1358
+ Index of the node.
1359
+
1360
+ Returns
1361
+ -------
1362
+ `int`
1363
+ Primary base demand.
1364
+ """
1365
+ return self.getnodevalue(node_idx, EpanetConstants.EN_BASEDEMAND)
1366
+
1367
+ def get_node_base_demands(self, node_idx: int) -> list[float]:
1368
+ """
1369
+ Returns all base demands of a given node.
1370
+
1371
+ Parameters
1372
+ ----------
1373
+ node_idx : `int`
1374
+ Index of the node.
1375
+
1376
+ Returns
1377
+ -------
1378
+ `list[int]`
1379
+ List of base demands.
1380
+ """
1381
+ r = []
1382
+
1383
+ for i in range(self.getnumdemands(node_idx)):
1384
+ r.append(self.getbasedemand(node_idx, i + 1))
1385
+
1386
+ return r
1387
+
1388
+ def get_node_demand_patterns_idx(self, node_idx: int) -> list[int]:
1389
+ """
1390
+ Returns the index of all demand patterns of a given node.
1391
+
1392
+ Parameters
1393
+ ----------
1394
+ node_idx : `int`
1395
+ Index of the node.
1396
+
1397
+ Returns
1398
+ -------
1399
+ `list[int]`
1400
+ List of indices of the demand patterns.
1401
+ """
1402
+ r = []
1403
+
1404
+ for i in range(self.getnumdemands(node_idx)):
1405
+ r.append(self.getdemandpattern(node_idx, i + 1))
1406
+
1407
+ return r
1408
+
1409
+ def get_tank_init_vol(self, tank_idx) -> float:
1410
+ """
1411
+ Return the inital water volume in a given tank.
1412
+
1413
+ Parameters
1414
+ ----------
1415
+ tank_idx : `int`
1416
+ Index of the tank.
1417
+
1418
+ Returns
1419
+ -------
1420
+ `float`
1421
+ Initial water volume.
1422
+ """
1423
+ return self.getnodevalue(tank_idx, EpanetConstants.EN_INITVOLUME)
1424
+
1425
+ def get_tank_level(self, tank_idx: int) -> float:
1426
+ """
1427
+ Returns the current water level in a given tank.
1428
+
1429
+ Parameters
1430
+ ----------
1431
+ tank_idx : `int`
1432
+ Index of the tank.
1433
+
1434
+ Returns
1435
+ -------
1436
+ `float`
1437
+ Current water level.
1438
+ """
1439
+ return self.getnodevalue(tank_idx, EpanetConstants.EN_TANKLEVEL)
1440
+
1441
+ def get_tank_volume(self, tank_idx: int) -> float:
1442
+ """
1443
+ Returns the current water volume inside a given tank.
1444
+
1445
+ Parameters
1446
+ ----------
1447
+ tank_idx : `int`
1448
+ Index of the tank.
1449
+
1450
+ Returns
1451
+ -------
1452
+ `float`
1453
+ Current water volume.
1454
+ """
1455
+ return self.getnodevalue(tank_idx, EpanetConstants.EN_TANKVOLUME)
1456
+
1457
+ def get_tank_mix_model(self, tank_idx: int) -> int:
1458
+ """
1459
+ Returns the mixing model of a given tank.
1460
+
1461
+ Parameters
1462
+ ----------
1463
+ tank_idx : `int`
1464
+ Index of the tank.
1465
+
1466
+ Returns
1467
+ -------
1468
+ `int`
1469
+ Type of mixing model. Will be one of the following:
1470
+
1471
+ - EN_MIX1
1472
+ - EN_MIX2
1473
+ - EN_FIFO
1474
+ - EN_LIFO
1475
+ """
1476
+ return int(self.getnodevalue(tank_idx, EpanetConstants.EN_MIXMODEL))
1477
+
1478
+ def get_tank_mix_zone_vol(self, tank_idx: int) -> float:
1479
+ """
1480
+ Returns the mixing zone volume of a given tank.
1481
+
1482
+ Parameters
1483
+ ----------
1484
+ tank_idx : `int`
1485
+ Index of the tank.
1486
+
1487
+ Returns
1488
+ -------
1489
+ `float`
1490
+ Tank mixing zone valume.
1491
+ """
1492
+ return self.getnodevalue(tank_idx, EpanetConstants.EN_MIXZONEVOL)
1493
+
1494
+ def get_tank_diameter(self, tank_idx: int) -> float:
1495
+ """
1496
+ Return the diameter of given tank.
1497
+
1498
+ Parameters
1499
+ ----------
1500
+ tank_idx : `int`
1501
+ Index of the tank.
1502
+
1503
+ Returns
1504
+ -------
1505
+ `float`
1506
+ Diameter of the tank.
1507
+ """
1508
+ return self.getnodevalue(tank_idx, EpanetConstants.EN_TANKDIAM)
1509
+
1510
+ def get_tank_min_vol(self, tank_idx: int) -> float:
1511
+ """
1512
+ Return the minimum volume of given tank.
1513
+
1514
+ Parameters
1515
+ ----------
1516
+ tank_idx : `int`
1517
+ Index of the tank.
1518
+
1519
+ Returns
1520
+ -------
1521
+ `float`
1522
+ Minimum volume.
1523
+ """
1524
+ return self.getnodevalue(tank_idx, EpanetConstants.EN_MINVOLUME)
1525
+
1526
+ def get_tank_max_vol(self, tank_idx: int) -> float:
1527
+ """
1528
+ Return the maxmium volume of given tank.
1529
+
1530
+ Parameters
1531
+ ----------
1532
+ tank_idx : `int`
1533
+ Index of the tank.
1534
+
1535
+ Returns
1536
+ -------
1537
+ `float`
1538
+ Maximum volume.
1539
+ """
1540
+ return self.getnodevalue(tank_idx, EpanetConstants.EN_MAXVOLUME)
1541
+
1542
+ def get_tank_vol_curve_idx(self, tank_idx: int) -> int:
1543
+ """
1544
+ Returns the index of the volume curve of a given tank.
1545
+
1546
+ Parameters
1547
+ ----------
1548
+ tank_idx : `int`
1549
+ Index of the tank.
1550
+
1551
+ Returns
1552
+ -------
1553
+ `int`
1554
+ Index of the volume curve.
1555
+ """
1556
+ return int(self.getnodevalue(tank_idx, EpanetConstants.EN_VOLCURVE))
1557
+
1558
+ def get_tank_min_level(self, tank_idx: int) -> float:
1559
+ """
1560
+ Returns the minimum water level of a given tank.
1561
+
1562
+ Parameters
1563
+ ----------
1564
+ tank_idx : `int`
1565
+ Index of the tank.
1566
+
1567
+ Returns
1568
+ -------
1569
+ `float`
1570
+ Minimum water level.
1571
+ """
1572
+ return self.getnodevalue(tank_idx, EpanetConstants.EN_MINLEVEL)
1573
+
1574
+ def get_tank_max_level(self, tank_idx: int) -> float:
1575
+ """
1576
+ Returns the maximum water level of a given tank.
1577
+
1578
+ Parameters
1579
+ ----------
1580
+ tank_idx : `int`
1581
+ Index of the tank.
1582
+
1583
+ Returns
1584
+ -------
1585
+ `float`
1586
+ Maximum water level.
1587
+ """
1588
+ return self.getnodevalue(tank_idx, EpanetConstants.EN_MAXLEVEL)
1589
+
1590
+ def get_tank_mix_fraction(self, tank_idx: int) -> float:
1591
+ """
1592
+ Returns the mixing fraction of a given tank.
1593
+
1594
+ Parameters
1595
+ ----------
1596
+ tank_idx : `int`
1597
+ Index of the tank.
1598
+
1599
+ Returns
1600
+ -------
1601
+ `float`
1602
+ Mixing fraction.
1603
+ """
1604
+ return self.getnodevalue(tank_idx, EpanetConstants.EN_MIXFRACTION)
1605
+
1606
+ def get_tank_bulk_decacy(self, tank_idx: int) -> float:
1607
+ """
1608
+ Returns the bulk decay rate in a given tank.
1609
+
1610
+ Parameters
1611
+ ----------
1612
+ tank_idx : `int`
1613
+ Index of the tank.
1614
+
1615
+ Returns
1616
+ -------
1617
+ `float`
1618
+ Bulk decay rate.
1619
+ """
1620
+ return self.getnodevalue(tank_idx, EpanetConstants.EN_TANK_KBULK)
1621
+
1622
+ def can_tank_overflow(self, tank_idx: int) -> bool:
1623
+ """
1624
+ Checks if a given tank can overflow or not.
1625
+
1626
+ Parameters
1627
+ ----------
1628
+ tank_idx : `int`
1629
+ Index of the tank.
1630
+
1631
+ Returns
1632
+ -------
1633
+ `bool`
1634
+ True if the tank can overflow, False otherwise.
1635
+ """
1636
+ return bool(self.getnodevalue(tank_idx, EpanetConstants.EN_CANOVERFLOW))
1637
+
1638
+ def get_pump_type(self, pump_idx: int) -> int:
1639
+ """
1640
+ Returns the type (type of pump curve) of a given pump.
1641
+
1642
+ Parameters
1643
+ ----------
1644
+ pump_idx : `int`
1645
+ Index of the pump.
1646
+
1647
+ Returns
1648
+ -------
1649
+ `int`
1650
+ Pump curve type. Will be one of the following:
1651
+
1652
+ - EN_CONST_HP
1653
+ - EN_POWER_FUNC
1654
+ - EN_CUSTOM
1655
+ - EN_NOCURVE
1656
+ """
1657
+ return self.getpumptype(pump_idx)
1658
+
1659
+ def get_pump_energy_price_pattern(self, pump_idx) -> int:
1660
+ """
1661
+ Returns the index of the energy price pattern of a given pump.
1662
+
1663
+ Parameters
1664
+ ----------
1665
+ pump_idx : `int`
1666
+ Index of the pump.
1667
+
1668
+ Returns
1669
+ -------
1670
+ `int`
1671
+ Pattern index.
1672
+ """
1673
+ return int(self.getlinkvalue(pump_idx, EpanetConstants.EN_PUMP_EPAT))
1674
+
1675
+ def set_pump_energy_price_pattern(self, pump_idx: int, pattern_idx: int) -> None:
1676
+ """
1677
+ Sets the energy price pattern of a given pump.
1678
+
1679
+ Parameters
1680
+ ----------
1681
+ pump_idx : `int`
1682
+ Index of the pump.
1683
+ pattern_idx : `int`
1684
+ Index of the pattern.
1685
+ """
1686
+ self.setlinkvalue(pump_idx, EpanetConstants.EN_PUMP_EPAT, pattern_idx)
1687
+
1688
+ def get_all_patterns_id(self) -> list[str]:
1689
+ """
1690
+ Returns a list of all pattern IDs.
1691
+
1692
+ Returns
1693
+ -------
1694
+ `list[str]`
1695
+ List of IDs.
1696
+ """
1697
+ r = []
1698
+
1699
+ for idx in range(1, self.getcount(EpanetConstants.EN_PATCOUNT) + 1):
1700
+ r.append(self.getpatternid(idx))
1701
+
1702
+ return r
1703
+
1704
+ def get_pattern(self, pattern_idx: int) -> list[float]:
1705
+ """
1706
+ Returns the values of a given pattern.
1707
+
1708
+ Parameters
1709
+ ----------
1710
+ pattern_idx : `int`
1711
+ Index of the pattern.
1712
+
1713
+ Returns
1714
+ -------
1715
+ `list[float]`
1716
+ Pattern values.
1717
+ """
1718
+ r = []
1719
+
1720
+ for t in range(self.getpatternlen(pattern_idx)):
1721
+ r.append(self.getpatternvalue(pattern_idx, t + 1))
1722
+
1723
+ return r
1724
+
1725
+ def set_pattern(self, pattern_idx: int, pattern_values: list[float]) -> None:
1726
+ """
1727
+ Set the values of a given pattern.
1728
+
1729
+ Parameters
1730
+ ----------
1731
+ pattern_idx : `int`
1732
+ Index of the pattern.
1733
+ pattern_values : `list[float]`
1734
+ New pattern values.
1735
+ """
1736
+ self.setpattern(pattern_idx, pattern_values, len(pattern_values))
1737
+
1738
+ def add_pattern(self, pattern_id: str, pattern_values: list[float]) -> None:
1739
+ """
1740
+ Adds a new pattern.
1741
+
1742
+ Parameters
1743
+ ----------
1744
+ pattern_id : `str`
1745
+ ID of the pattern.
1746
+ pattern_values : `list[float]`
1747
+ Pattern values.
1748
+ """
1749
+ self.addpattern(pattern_id)
1750
+
1751
+ idx = self.getpatternindex(pattern_id)
1752
+ self.setpattern(idx, pattern_values, len(pattern_values))
1753
+
1754
+ def get_all_links_connecting_nodes_id(self) -> list[tuple[str]]:
1755
+ """
1756
+ Returns a list of all connecting node IDs for each link in the network.
1757
+
1758
+ Returns
1759
+ -------
1760
+ `list[tuple[str]]`
1761
+ List of tuple of connecting node IDs of all links.
1762
+ """
1763
+ r = []
1764
+
1765
+ for link_idx in range(self.getcount(EpanetConstants.EN_LINKCOUNT)):
1766
+ node1_idx, node2_idx = self.getlinknodes(link_idx + 1)
1767
+ r.append((self.get_node_id(node1_idx), self.get_node_id(node2_idx)))
1768
+
1769
+ return r
1770
+
1771
+ def get_pump_avg_energy_price(self, pump_idx: int) -> float:
1772
+ """
1773
+ Returns the average energy price of a given pump.
1774
+
1775
+ Parameters
1776
+ ----------
1777
+ pump_idx : `int`
1778
+ Index of the pump
1779
+
1780
+ Returns
1781
+ -------
1782
+ `float`
1783
+ Average energy price.
1784
+ """
1785
+ return self.getlinkvalue(pump_idx, EpanetConstants.EN_PUMP_ECOST)
1786
+
1787
+ def set_pump_avg_energy_price(self, pump_idx: int, price: float) -> float:
1788
+ """
1789
+ Specifies the average energy price of a given pump.
1790
+
1791
+ Parameters
1792
+ ----------
1793
+ pump_idx : `int`
1794
+ Index of the pump
1795
+ price : `float`
1796
+ Average energy price.
1797
+ """
1798
+ self.setlinkvalue(pump_idx, EpanetConstants.EN_PUMP_ECOST, price)
1799
+
1800
+ def get_pump_pattern(self, pump_idx: int) -> int:
1801
+ """
1802
+ Returns the pattern of a given pump.
1803
+
1804
+ Parameters
1805
+ ----------
1806
+ pump_idx : `int`
1807
+ Index of the pump
1808
+
1809
+ Returns
1810
+ -------
1811
+ `int`
1812
+ Index of the pump pattern.
1813
+ """
1814
+ return int(self.getlinkvalue(pump_idx, EpanetConstants.EN_LINKPATTERN))
1815
+
1816
+ def set_pump_pattern(self, pump_idx: int, pattern_idx: int) -> None:
1817
+ """
1818
+ Specifies the pattern of a given pump.
1819
+
1820
+ Parameters
1821
+ ----------
1822
+ pump_idx : `int`
1823
+ Index of the pump
1824
+ pattern_idx : `int`
1825
+ Index of the pattern.
1826
+ """
1827
+ self.setlinkvalue(pump_idx, EpanetConstants.EN_LINKPATTERN, pattern_idx)
1828
+
1829
+ def set_node_data(self, node_idx: int, elev: float, base_demand: float,
1830
+ demand_pattern_id: str) -> None:
1831
+ """
1832
+ Specifies some properties, such as elevation and demand, of a given node.
1833
+
1834
+ Parameters
1835
+ ----------
1836
+ node_idx : `int`
1837
+ Index of the node.
1838
+ elev : `float`
1839
+ Eleveation of the node.
1840
+ base_demand : `float`
1841
+ Base demand of the node.
1842
+ demand_pattern_id : `str`
1843
+ ID of the primary demand pattern of the node.
1844
+ """
1845
+ self.setjuncdata(node_idx, elev, base_demand, demand_pattern_id)
1846
+
1847
+ def set_node_source(self, node_idx: int, source_type: int, source_strengh: float,
1848
+ pattern_idx: int) -> None:
1849
+ """
1850
+ Specifies the quality source of a given node.
1851
+
1852
+ Parameters
1853
+ ----------
1854
+ node_idx : `int`
1855
+ Index of the node.
1856
+ source_type : `int`
1857
+ Type of the source. Must be one of the following:
1858
+
1859
+ - EN_CONCEN
1860
+ - EN_MASS
1861
+ - EN_SETPOINT
1862
+ - EN_FLOWPACED
1863
+ source_strength : `float`
1864
+ Source strength.
1865
+ pattern_idx : `int`
1866
+ Index of the source pattern.
1867
+ """
1868
+ self.setnodevalue(node_idx, EpanetConstants.EN_SOURCETYPE, source_type)
1869
+ self.setnodevalue(node_idx, EpanetConstants.EN_SOURCEQUAL, source_strengh)
1870
+
1871
+ if pattern_idx is not None:
1872
+ self.setnodevalue(node_idx, EpanetConstants.EN_SOURCEPAT, pattern_idx)
1873
+
1874
+ def set_node_source_quality(self, node_idx, source_strength: float) -> None:
1875
+ """
1876
+ Specifies the strength of a node quality source.
1877
+
1878
+ Parameters
1879
+ ----------
1880
+ node_idx : `int`
1881
+ Index of the node.
1882
+ source_strength : `float`
1883
+ Source strength.
1884
+ """
1885
+ self.setnodevalue(node_idx, EpanetConstants.EN_SOURCEQUAL, source_strength)
1886
+
1887
+ def get_node_init_quality(self, node_idx: int) -> float:
1888
+ """
1889
+ Returns the initial quality (e.g., concentration) of a given node.
1890
+
1891
+ Parameters
1892
+ ----------
1893
+ node_idx : `int`
1894
+ Index of the node.
1895
+
1896
+ Returns
1897
+ -------
1898
+ `float`
1899
+ Initial quality.
1900
+ """
1901
+ return self.getnodevalue(node_idx, EpanetConstants.EN_INITQUAL)
1902
+
1903
+ def set_node_init_quality(self, node_idx: int, init_qual: float) -> None:
1904
+ """
1905
+ Specifies the initial quality (e.g., concentration) of a given node.
1906
+
1907
+ Parameters
1908
+ ----------
1909
+ node_idx : `int`
1910
+ Index of the node.
1911
+ init_qual : `float`
1912
+ Initial quality.
1913
+ """
1914
+ self.setnodevalue(node_idx, EpanetConstants.EN_INITQUAL, init_qual)
1915
+
1916
+ def get_pipe_wall_reaction_order(self) -> int:
1917
+ """
1918
+ Returns the pipe wall reaction order.
1919
+
1920
+ Returns
1921
+ -------
1922
+ `int`
1923
+ Reaction oder.
1924
+ """
1925
+ return int(self.getoption(EpanetConstants.EN_WALLORDER))
1926
+
1927
+ def get_pipe_bulk_reaction_order(self) -> int:
1928
+ """
1929
+ Returns the the pipe bulk reaction order.
1930
+
1931
+ Returns
1932
+ -------
1933
+ `int`
1934
+ Reaction order.
1935
+ """
1936
+ return int(self.getoption(EpanetConstants.EN_BULKORDER))
1937
+
1938
+ def get_link_wall_raction_coeff(self, link_idx: int) -> float:
1939
+ """
1940
+ Returns the wall reaction coefficient of a given link.
1941
+
1942
+ Parameters
1943
+ ----------
1944
+ link_idx : `int`
1945
+ Index of the link.
1946
+
1947
+ Returns
1948
+ -------
1949
+ `float`
1950
+ Wall reaction coefficient.
1951
+ """
1952
+ return self.getlinkvalue(link_idx, EpanetConstants.EN_KWALL)
1953
+
1954
+ def get_link_bulk_raction_coeff(self, link_idx: int) -> float:
1955
+ """
1956
+ Returns the bulk reaction coefficient of a link.
1957
+
1958
+ Parameters
1959
+ ----------
1960
+ link_idx : `int`
1961
+ Index of the link.
1962
+
1963
+ Returns
1964
+ -------
1965
+ `float`
1966
+ Bulk reaction coefficient.
1967
+ """
1968
+ return self.getlinkvalue(link_idx, EpanetConstants.EN_KBULK)
1969
+
1970
+ def get_tank_bulk_reaction_coeff(self, tank_idx: int) -> float:
1971
+ """
1972
+ Returns the bulk reaction coefficient of a given tank.
1973
+
1974
+ Parameters
1975
+ ----------
1976
+ tank_idx : `int`
1977
+ Index of the tank.
1978
+
1979
+ Returns
1980
+ -------
1981
+ `float`
1982
+ Bulk reaction coefficient.
1983
+ """
1984
+ return self.getnodevalue(tank_idx, EpanetConstants.EN_TANK_KBULK)
1985
+
1986
+ def get_limiting_concentration(self) -> float:
1987
+ """
1988
+ Returns the limiting concentration in reactions.
1989
+
1990
+ Returns
1991
+ -------
1992
+ `float`
1993
+ Limiting concentration.
1994
+ """
1995
+ return self.getoption(EpanetConstants.EN_CONCENLIMIT)
1996
+
1997
+ def get_quality_tolerance(self) -> float:
1998
+ """
1999
+ Returns the water quality tolerance.
2000
+
2001
+ Returns
2002
+ -------
2003
+ `float`
2004
+ Water quality tolerance.
2005
+ """
2006
+ return self.getoption(EpanetConstants.EN_TOLERANCE)
2007
+
2008
+ def get_specific_diffusivity(self) -> float:
2009
+ """
2010
+ Returns the specific diffusivity.
2011
+
2012
+ Returns
2013
+ -------
2014
+ `float`
2015
+ Specific diffusivity.
2016
+ """
2017
+ return self.getoption(EpanetConstants.EN_SP_DIFFUS)
2018
+
2019
+ def get_specific_gravity(self) -> float:
2020
+ """
2021
+ Returns the specific gravity.
2022
+
2023
+ Returns
2024
+ -------
2025
+ `float`
2026
+ Specific gravity.
2027
+ """
2028
+ return self.getoption(EpanetConstants.EN_SP_GRAVITY)
2029
+
2030
+ def get_specific_viscosity(self) -> float:
2031
+ """
2032
+ Returns the specific viscosity.
2033
+
2034
+ Returns
2035
+ -------
2036
+ `float`
2037
+ Specific viscosity.
2038
+ """
2039
+ return self.getoption(EpanetConstants.EN_SP_VISCOS)
2040
+
2041
+ def get_tank_bulk_reaction_order(self) -> int:
2042
+ """
2043
+ Returns the bulk reaction order in tanks.
2044
+
2045
+ Returns
2046
+ -------
2047
+ `int`
2048
+ Bulk reaction order.
2049
+ """
2050
+ return int(self.getoption(EpanetConstants.EN_TANKORDER))
2051
+
2052
+ def get_node_quality(self, node_idx: int) -> float:
2053
+ """
2054
+ Returns the current quality value at a given node.
2055
+
2056
+ Parameters
2057
+ ----------
2058
+ node_idx : `int`
2059
+ Index of the node.
2060
+
2061
+ Returns
2062
+ -------
2063
+ `float`
2064
+ Current node quality value.
2065
+ """
2066
+ return self.getnodevalue(node_idx, EpanetConstants.EN_QUALITY)
2067
+
2068
+ def get_link_quality(self, link_idx: int) -> float:
2069
+ """
2070
+ Returns the current quality value (e.g., concentration, age, ...) at a given link.
2071
+
2072
+ Parameters
2073
+ ----------
2074
+ `link_idx`
2075
+ Index of the link.
2076
+
2077
+ Returns
2078
+ -------
2079
+ `float`
2080
+ Current link quality value.
2081
+ """
2082
+ return self.getlinkvalue(link_idx, EpanetConstants.EN_LINKQUAL)
2083
+
2084
+ def get_node_pressure(self, node_idx: int) -> float:
2085
+ """
2086
+ Returns the current pressure at a given node.
2087
+
2088
+ Parameters
2089
+ ----------
2090
+ node_idx : `int`
2091
+ Index of the node.
2092
+
2093
+ Returns
2094
+ -------
2095
+ `float`
2096
+ Current pressure.
2097
+ """
2098
+ return self.getnodevalue(node_idx, EpanetConstants.EN_PRESSURE)
2099
+
2100
+ def get_node_head(self, node_idx: int) -> float:
2101
+ """
2102
+ Returns the current hydraulic head at a given node.
2103
+
2104
+ Parameters
2105
+ ----------
2106
+ node_idx : `int`
2107
+ Index of the node.
2108
+
2109
+ Returns
2110
+ -------
2111
+ `float`
2112
+ Current hydraulic head.
2113
+ """
2114
+ return self.getnodevalue(node_idx, EpanetConstants.EN_HEAD)
2115
+
2116
+ def get_node_demand(self, node_idx: int) -> float:
2117
+ """
2118
+ Returns the current demand at a given node.
2119
+
2120
+ Parameters
2121
+ ----------
2122
+ node_idx : `int`
2123
+ Index of the node.
2124
+
2125
+ Returns
2126
+ -------
2127
+ `float`
2128
+ Current demand.
2129
+ """
2130
+ return self.getnodevalue(node_idx, EpanetConstants.EN_DEMAND)
2131
+
2132
+ def get_link_flow(self, link_idx: int) -> float:
2133
+ """
2134
+ Returns the current flow rate at a given link.
2135
+
2136
+ Parameters
2137
+ ----------
2138
+ `link_idx`
2139
+ Index of the link.
2140
+
2141
+ Returns
2142
+ -------
2143
+ `float`
2144
+ Current flow rate.
2145
+ """
2146
+ return self.getlinkvalue(link_idx, EpanetConstants.EN_FLOW)
2147
+
2148
+ def get_pump_status(self, pump_idx: int) -> int:
2149
+ """
2150
+ Returns the current pump status.
2151
+
2152
+ Parameters
2153
+ ----------
2154
+ pump_idx : `int`
2155
+ Index of the pump.
2156
+
2157
+ Returns
2158
+ -------
2159
+ `int`
2160
+ Current pump status. Will be one of the following:
2161
+
2162
+ - EN_PUMP_XHEAD
2163
+ - EN_PUMP_CLOSED
2164
+ - EN_PUMP_OPEN
2165
+ - EN_PUMP_XFLOW
2166
+ """
2167
+ return int(self.getlinkvalue(pump_idx, EpanetConstants.EN_PUMP_STATE))
2168
+
2169
+ def set_pump_status(self, pump_idx: int, status: int) -> None:
2170
+ """
2171
+ Sets the the current status of a given pump.
2172
+
2173
+ Parameters
2174
+ ----------
2175
+ pump_idx : `int`
2176
+ Index of the pump
2177
+ status : `int`
2178
+ Pump status. Must be one of the following:
2179
+
2180
+ - EN_CLOSED
2181
+ - EN_OPEN
2182
+ """
2183
+ self.setlinkvalue(pump_idx, EpanetConstants.EN_STATUS, status)
2184
+
2185
+ def get_pump_energy_usage(self, pump_idx: int) -> float:
2186
+ """
2187
+ Returns the current energy usage of a given pump.
2188
+
2189
+ Parameters
2190
+ ----------
2191
+ pump_idx : `int`
2192
+ Index of the pump
2193
+
2194
+ Returns
2195
+ -------
2196
+ `float`
2197
+ Current energy usage.
2198
+ """
2199
+ return self.getlinkvalue(pump_idx, EpanetConstants.EN_ENERGY)
2200
+
2201
+ def get_pump_efficiency(self, pump_idx: int) -> float:
2202
+ """
2203
+ Returns the current effciency of a given pump.
2204
+
2205
+ Parameters
2206
+ ----------
2207
+ pump_idx : `int`
2208
+ Index of the pump
2209
+
2210
+ Returns
2211
+ -------
2212
+ `float`
2213
+ Current pump effciency.
2214
+ """
2215
+ return self.getlinkvalue(pump_idx, EpanetConstants.EN_PUMP_EFFIC)
2216
+
2217
+ def get_valve_status(self, valve_idx: int) -> int:
2218
+ """
2219
+ Returns the current status of a given valve.
2220
+
2221
+ Parameters
2222
+ ----------
2223
+ valve_idx : `int`
2224
+ Index of the valve.
2225
+
2226
+ Returns
2227
+ -------
2228
+ `int`
2229
+ Current status. Will be one of the following:
2230
+
2231
+ - EN_CLOSED
2232
+ - EN_OPEN
2233
+ """
2234
+ return int(self.getlinkvalue(valve_idx, EpanetConstants.EN_STATUS))
2235
+
2236
+ def set_valve_status(self, valve_idx, status: int):
2237
+ """
2238
+ Sets the current status of a given valve.
2239
+
2240
+ Parameters
2241
+ ----------
2242
+ valve_idx : `int`
2243
+ Index of the valve.
2244
+ status : `int`
2245
+ New status of the valve. Must be one of the following:
2246
+
2247
+ - EN_CLOSED
2248
+ - EN_OPEN
2249
+ """
2250
+ self.setlinkvalue(valve_idx, EpanetConstants.EN_STATUS, status)
2251
+
2252
+ def split_pipe(self, pipe_id: str, new_pipe_id: str, new_node_id: str) -> None:
2253
+ """
2254
+ Splits a pipe (pipeID), creating two new pipes (pipeID and newPipeID) and adds a
2255
+ junction/node (newNodeID) in between. If the pipe is linear
2256
+ the pipe is splitted in half, otherwisw the middle point of
2257
+ the vertice array elemnts is taken as the split point.
2258
+ The two new pipes have the same properties as the one which is splitted.
2259
+ The new node's properties are the same with the nodes on the left and right
2260
+ and New Node Elevation and Initial quality is the average of the two.
2261
+
2262
+ Note that this code is taken from EPyT -- slightly modified to fit into this toolkit.
2263
+
2264
+ Parameters
2265
+ ----------
2266
+ pipe_id : `str`
2267
+ ID of the pipe to be split.
2268
+ new_pipe_id : `str`
2269
+ ID of the new pipe.
2270
+ new_node_id : `str`
2271
+ ID of the new node, placed in the middle of the splitted pipe.
2272
+ """
2273
+ # Find the coordinates of the Nodes connected with the link/pipe
2274
+ pipeIndex = self.get_link_idx(pipe_id)
2275
+ nodesIndex = self.getlinknodes(pipeIndex)
2276
+ leftNodeIndex = nodesIndex[0]
2277
+ rightNodeIndex = nodesIndex[1]
2278
+ coordNode1 = self.getcoord(leftNodeIndex)
2279
+ coordNode2 = self.getcoord(rightNodeIndex)
2280
+
2281
+ if coordNode1[0] == 0 and coordNode1[1] == 0 \
2282
+ and coordNode2[0] == 0 and coordNode2[1] == 0:
2283
+ raise ValueError('Some nodes have zero values for coordinates')
2284
+
2285
+ if self.getvertexcount(pipeIndex) == 0:
2286
+ # Calculate mid position of the link/pipe based on nodes
2287
+ midX = (coordNode1[0] + coordNode2[0]) / 2
2288
+ midY = (coordNode1[1] + coordNode2[1]) / 2
2289
+ else:
2290
+ # Calculate mid position based on vertices pick midpoint of vertices
2291
+ xVert = []
2292
+ yVert = []
2293
+ for i in range(self.getvertexcount(pipeIndex)):
2294
+ x, y = self.getvertex(pipeIndex, i)
2295
+ xVert.append(x)
2296
+ yVert.append(y)
2297
+
2298
+ xMidPos = int(len(xVert) / 2)
2299
+ midX = xVert[xMidPos]
2300
+ midY = yVert[xMidPos]
2301
+
2302
+ # Add the new node between the link/pipe and add the same properties
2303
+ # as the left node (the elevation is the average of left-right nodes)
2304
+ index = self.addnode(new_node_id, EpanetConstants.EN_JUNCTION)
2305
+ self.setcoord(index, midX, midY)
2306
+
2307
+ newNodeIndex = self.get_node_idx(new_node_id)
2308
+ midElev = (self.get_node_elevation(leftNodeIndex) +
2309
+ self.get_node_elevation(rightNodeIndex)) / 2
2310
+ self.setjuncdata(newNodeIndex, midElev, 0, "")
2311
+ self.setnodevalue(newNodeIndex, EpanetConstants.EN_EMITTER,
2312
+ self.get_node_emitter_coeff(leftNodeIndex))
2313
+ if self.getqualtype()[0] > 0:
2314
+ midInitQual = (self.get_node_init_quality(leftNodeIndex) +
2315
+ self.get_node_init_quality(rightNodeIndex)) / 2
2316
+ self.set_node_init_quality(newNodeIndex, midInitQual)
2317
+ self.set_node_source_quality(newNodeIndex, self.get_node_source_qual(leftNodeIndex))
2318
+ self.setnodevalue(newNodeIndex, EpanetConstants.EN_SOURCEPAT,
2319
+ self.getnodevalue(leftNodeIndex, EpanetConstants.EN_SOURCEPAT))
2320
+ if self.getnodevalue(leftNodeIndex, EpanetConstants.EN_SOURCETYPE) != 0:
2321
+ self.setnodevalue(newNodeIndex, EpanetConstants.EN_SOURCETYPE,
2322
+ self.getnodevalue(leftNodeIndex, EpanetConstants.EN_SOURCETYPE))
2323
+
2324
+ # Access link properties
2325
+ linkDia = self.get_link_diameter(pipeIndex)
2326
+ linkLength = self.get_link_length(pipeIndex)
2327
+ linkRoughnessCoeff = self.get_link_roughness(pipeIndex)
2328
+ linkMinorLossCoeff = self.get_link_minorloss(pipeIndex)
2329
+ linkInitialStatus = self.get_link_init_status(pipeIndex)
2330
+ linkInitialSetting = self.get_link_init_setting(pipeIndex)
2331
+ linkBulkReactionCoeff = self.get_link_bulk_raction_coeff(pipeIndex)
2332
+ linkWallReactionCoeff = self.get_link_wall_raction_coeff(pipeIndex)
2333
+
2334
+ # Delete the link/pipe that is splitted
2335
+ self.deletelink(pipeIndex, 0)
2336
+
2337
+ # Add two new pipes
2338
+ # d.addLinkPipe(pipeID, fromNode, toNode)
2339
+ # Add the Left Pipe and add the same properties as the deleted link
2340
+ leftNodeID = self.get_node_id(leftNodeIndex)
2341
+ leftPipeIndex = self.addlink(pipe_id, EpanetConstants.EN_PIPE, leftNodeID, new_node_id)
2342
+ self.setlinknodes(leftPipeIndex, leftNodeIndex, newNodeIndex)
2343
+ self.setpipedata(leftPipeIndex, linkLength, linkDia, linkRoughnessCoeff, linkMinorLossCoeff)
2344
+ self.setlinkvalue(leftPipeIndex, EpanetConstants.EN_INITSTATUS, linkInitialStatus)
2345
+ self.setlinkvalue(leftPipeIndex, EpanetConstants.EN_INITSETTING, linkInitialSetting)
2346
+ self.setlinkvalue(leftPipeIndex, EpanetConstants.EN_KBULK, linkBulkReactionCoeff)
2347
+ self.setlinkvalue(leftPipeIndex, EpanetConstants.EN_KWALL, linkWallReactionCoeff)
2348
+
2349
+ # Add the Right Pipe and add the same properties as the deleted link
2350
+ rightNodeID = self.get_node_id(rightNodeIndex)
2351
+ rightPipeIndex = self.addlink(new_pipe_id, EpanetConstants.EN_PIPE, new_node_id, rightNodeID)
2352
+ self.setlinknodes(rightPipeIndex, newNodeIndex, rightNodeIndex)
2353
+ self.setpipedata(rightPipeIndex, linkLength, linkDia, linkRoughnessCoeff,
2354
+ linkMinorLossCoeff)
2355
+ self.setlinkvalue(rightPipeIndex, EpanetConstants.EN_INITSTATUS, linkInitialStatus)
2356
+ self.setlinkvalue(rightPipeIndex, EpanetConstants.EN_INITSETTING, linkInitialSetting)
2357
+ self.setlinkvalue(rightPipeIndex, EpanetConstants.EN_KBULK, linkBulkReactionCoeff)
2358
+ self.setlinkvalue(rightPipeIndex, EpanetConstants.EN_KWALL, linkWallReactionCoeff)
2359
+
2360
+ def _parse_msx_file(self) -> dict:
2361
+ if self._msx_file is None:
2362
+ raise ValueError("No .msx file loaded")
2363
+
2364
+ # Code for parsing .msx files taken from EPyT
2365
+ keys = ["AREA_UNITS", "RATE_UNITS", "SOLVER", "COUPLING", "TIMESTEP", "ATOL", "RTOL",
2366
+ "COMPILER", "SEGMENTS", "PECLET"]
2367
+ float_values = ["TIMESTEP", "ATOL", "RTOL", "SEGMENTS", "PECLET"]
2368
+ values = {key: None for key in keys}
2369
+
2370
+ # Flag to determine if we're in the [OPTIONS] section
2371
+ in_options = False
2372
+
2373
+ # Open and read the file
2374
+ with open(self._msx_file, 'r') as file:
2375
+ for line in file:
2376
+ # Check for [OPTIONS] section
2377
+ if "[OPTIONS]" in line:
2378
+ in_options = True
2379
+ elif "[" in line and "]" in line:
2380
+ in_options = False # We've reached a new section
2381
+
2382
+ if in_options:
2383
+ # Pattern to match the keys and extract values, ignoring comments and whitespace
2384
+ pattern = re.compile(r'^\s*(' + '|'.join(keys) + r')\s+(.*?)\s*(?:;.*)?$')
2385
+ match = pattern.search(line)
2386
+ if match:
2387
+ key, value = match.groups()
2388
+ if key in float_values:
2389
+ values[key] = float(value)
2390
+ else:
2391
+ values[key] = value
2392
+ return values
2393
+
2394
+ def get_msx_time_step(self) -> int:
2395
+ """
2396
+ Returns the MSX time step.
2397
+
2398
+ Returns
2399
+ -------
2400
+ `int`
2401
+ Time step.
2402
+ """
2403
+ return int(self._parse_msx_file()["TIMESTEP"])
2404
+
2405
+ def set_msx_time_step(self, time_step: int) -> None:
2406
+ """
2407
+ Specifies the MSX time step.
2408
+
2409
+ Parameters
2410
+ ----------
2411
+ time_step : `int`
2412
+ New MSX time step.
2413
+ """
2414
+ temp_folder = tempfile.gettempdir()
2415
+ file_name = os.path.basename(self._msx_file)
2416
+ temp_file = os.path.join(temp_folder, file_name)
2417
+
2418
+ self.MSXsavemsxfile(temp_file)
2419
+ self.MSXclose()
2420
+
2421
+ with open(temp_file, 'r+') as f: # Code taken from EPyT -- workaround for missing functions
2422
+ lines = f.readlines()
2423
+ options_index = -1
2424
+ flag = 0
2425
+ for i, line in enumerate(lines):
2426
+ if line.strip() == '[OPTIONS]':
2427
+ options_index = i
2428
+ elif line.strip().startswith("TIMESTEP"):
2429
+ lines[i] = "TIMESTEP" + "\t" + str(time_step) + "\n"
2430
+ flag = 1
2431
+ if flag == 0 and options_index != -1:
2432
+ lines.insert(options_index + 1, "TIMESTEP" + "\t" + str(time_step) + "\n")
2433
+ f.seek(0)
2434
+ f.writelines(lines)
2435
+ f.truncate()
2436
+
2437
+ self.MSXopen(temp_file)
2438
+ self._msx_file = temp_file
2439
+
2440
+ def get_msx_options(self) -> dict:
2441
+ """
2442
+ Returns the MSX options as specified in the .msx file.
2443
+
2444
+ Returns
2445
+ -------
2446
+ `dict`
2447
+ Dictionary of MSX options as specified in the .msx file -- note that not all
2448
+ options might be specified.
2449
+ Possible options (dictinary keys) are: REA_UNITS, RATE_UNITS, SOLVER, COUPLING,
2450
+ TIMESTEP, ATOL, RTOL, COMPILER, SEGMENTS, PECLET
2451
+ """
2452
+ return self._parse_msx_file()
2453
+
2454
+ def add_msx_pattern(self, pattern_id: str, pattern_mult: list[float]) -> None:
2455
+ """
2456
+ Adds a new MSX pattern.
2457
+
2458
+ Parameters
2459
+ ----------
2460
+ pattern_id : `str`
2461
+ ID of the new pattern.
2462
+ pattern_mult : `list[float]`
2463
+ Pattern values (i.e., multipliers).
2464
+ """
2465
+ self.MSXaddpattern(pattern_id)
2466
+ pattern_idx = self.MSXgetindex(EpanetConstants.MSX_PATTERN, pattern_id)
2467
+ self.MSXsetpattern(pattern_idx, pattern_mult, len(pattern_mult))
2468
+
2469
+ def set_msx_source(self, node_id: str, species_id: str, source_type: int,
2470
+ source_concentration: float, msx_pattern_id: str) -> None:
2471
+ """
2472
+ Adds a species source (i.e., injection of a given species) at a given node.
2473
+
2474
+ Parameters
2475
+ ----------
2476
+ node_id : `str`
2477
+ ID of the node where the species in injected into the network.
2478
+ species_id : `str`
2479
+ ID of the species to be injected.
2480
+ source_type : `int`
2481
+ Type of injection/source. Must be one of the following:
2482
+
2483
+ - MSX_NOSOURCE = -1 for no source,
2484
+ - MSX_CONCEN = 0 for a concentration source,
2485
+ - MSX_MASS = 1 for a mass booster source,
2486
+ - MSX_SETPOINT = 2 for a setpoint source,
2487
+ - MSX_FLOWPACED = 3 for a flow paced source;
2488
+ source_concentration : `float`
2489
+ Injetion concentration -- can change over time according the the pattern of multiplies.
2490
+ msx_pattern_id : `str`
2491
+ ID of the injection pattern -- i.e., multipliers.
2492
+ """
2493
+ node_idx = self.get_node_idx(node_id)
2494
+ species_idx = self.get_msx_species_idx(species_id)
2495
+ msx_pattern_idx = self.MSXgetindex(EpanetConstants.MSX_PATTERN, msx_pattern_id)
2496
+
2497
+ self.MSXsetsource(node_idx, species_idx, source_type, source_concentration, msx_pattern_idx)
2498
+
2499
+ def get_msx_species_init_concentration(self, obj_type: int, obj_index: int,
2500
+ species_idx: int) -> float:
2501
+ """
2502
+ Returns the initial concentration of a given species at a given location in the network.
2503
+
2504
+ Parameters
2505
+ ----------
2506
+ obj_type : `int`
2507
+ Type of the location (i.e., node or link). Must be one of the following:
2508
+
2509
+ - MSX_NODE
2510
+ - MSX_LINK
2511
+ obj_index : `int`
2512
+ Index of the link or node.
2513
+ species_idx : `int`
2514
+ Index of the species.
2515
+
2516
+ Returns
2517
+ -------
2518
+ `float`
2519
+ Initial concentration.
2520
+ """
2521
+ return self.MSXgetinitqual(obj_type, obj_index, species_idx)
2522
+
2523
+ def get_msx_species_concentration(self, obj_type: int, obj_index: int,
2524
+ species_idx: int) -> float:
2525
+ """
2526
+ Returns the current concentration of a given species at a given location in the network.
2527
+
2528
+ Parameters
2529
+ ----------
2530
+ obj_type : `int`
2531
+ Type of the location (i.e., node or link). Must be one of the following:
2532
+
2533
+ - MSX_NODE
2534
+ - MSX_LINK
2535
+ obj_index : `int`
2536
+ Index of the link or node.
2537
+ species_idx : `int`
2538
+ Index of the species.
2539
+
2540
+ Returns
2541
+ -------
2542
+ `float`
2543
+ Species concentration.
2544
+ """
2545
+ return self.MSXgetqual(obj_type, obj_index, species_idx)
2546
+
2547
+ def get_all_msx_species_id(self) -> list[str]:
2548
+ """
2549
+ Returns a list of all species IDs.
2550
+
2551
+ Returns
2552
+ -------
2553
+ `list[str]`
2554
+ List of all species IDs.
2555
+ """
2556
+ return [self.MSXgetID(EpanetConstants.MSX_SPECIES, i + 1)
2557
+ for i in range(self.MSXgetcount(EpanetConstants.MSX_SPECIES))]
2558
+
2559
+ def get_msx_species_idx(self, species_id) -> int:
2560
+ """
2561
+ Returns the index of a given species.
2562
+
2563
+ Parameters
2564
+ ----------
2565
+ species_id : `str`
2566
+ ID of the species.
2567
+
2568
+ Returns
2569
+ -------
2570
+ `int`
2571
+ Index of the species.
2572
+ """
2573
+ return self.MSXgetindex(EpanetConstants.MSX_SPECIES, species_id)
2574
+
2575
+ def get_num_msx_species(self) -> int:
2576
+ """
2577
+ Returns the total number of bulk and wall species.
2578
+
2579
+ Returns
2580
+ -------
2581
+ `int`
2582
+ Number of species.
2583
+ """
2584
+ return self.MSXgetcount(EpanetConstants.MSX_SPECIES)
2585
+
2586
+ def get_msx_species_info(self, species_idx: int) -> dict:
2587
+ """
2588
+ Returns information about a given species.
2589
+
2590
+ Parameters
2591
+ ----------
2592
+ species_idx : `int`
2593
+ Index of the species.
2594
+
2595
+ Returns
2596
+ -------
2597
+ `dict`
2598
+ Information as a dictionary. Will contains the following entries:
2599
+
2600
+ - 'type': MSX_BULK for a bulk flow species or MSX_WALL for a surface species;
2601
+ - 'units': mass units;
2602
+ - 'atol': absolute concentration tolerance (concentration units);
2603
+ - 'rtol': relative concentration tolerance (unitless);
2604
+ """
2605
+ return dict(zip(["type", "units", "atol", "rtol"], self.MSXgetspecies(species_idx)))
2606
+
2607
+ def get_all_msx_species_info(self) -> list[dict]:
2608
+ """
2609
+ Returns information about all species.
2610
+
2611
+ Returns
2612
+ -------
2613
+ `list[dict]`
2614
+ List of species information -- ordered by species index.#
2615
+ Each entry in the list contains a dictionary with the following entries:
2616
+
2617
+ - 'type': MSX_BULK for a bulk flow species or MSX_WALL for a surface species;
2618
+ - 'units': mass units;
2619
+ - 'atol': absolute concentration tolerance (concentration units);
2620
+ - 'rtol': relative concentration tolerance (unitless);
2621
+ """
2622
+ return [self.get_msx_species_info(i + 1)
2623
+ for i in range(self.MSXgetcount(EpanetConstants.MSX_SPECIES))]
2624
+
2625
+ def get_all_bulk_species_id(self) -> list[str]:
2626
+ """
2627
+ Returns the IDs of all bulk species.
2628
+
2629
+ Returns
2630
+ -------
2631
+ `list[int]`
2632
+ List of IDs.
2633
+ """
2634
+ r = []
2635
+
2636
+ for i in range(self.MSXgetcount(EpanetConstants.MSX_SPECIES)):
2637
+ if self.MSXgetspecies(i + 1)[0] == EpanetConstants.MSX_BULK:
2638
+ r.append(self.get_msx_species_idx(i + 1))
2639
+
2640
+ return r
2641
+
2642
+ def get_all_bulk_species_idx(self) -> list[int]:
2643
+ """
2644
+ Returns the indices of all bulk species.
2645
+
2646
+ Returns
2647
+ -------
2648
+ `list[int]`
2649
+ List of indices.
2650
+ """
2651
+ r = []
2652
+
2653
+ for i in range(self.MSXgetcount(EpanetConstants.MSX_SPECIES)):
2654
+ if self.MSXgetspecies(i + 1)[0] == EpanetConstants.MSX_BULK:
2655
+ r.append(i + 1)
2656
+
2657
+ return r
2658
+
2659
+ def get_all_wall_species_id(self) -> list[str]:
2660
+ """
2661
+ Returns the IDs of all wall species.
2662
+
2663
+ Returns
2664
+ -------
2665
+ `list[int]`
2666
+ List of IDs.
2667
+ """
2668
+ r = []
2669
+
2670
+ for i in range(self.MSXgetcount(EpanetConstants.MSX_SPECIES)):
2671
+ if self.MSXgetspecies(i + 1)[0] == EpanetConstants.MSX_WALL:
2672
+ r.append(self.get_msx_species_idx(i + 1))
2673
+
2674
+ return r
2675
+
2676
+ def get_all_wall_species_idx(self) -> list[int]:
2677
+ """
2678
+ Returns the indices of all wall species.
2679
+
2680
+ Returns
2681
+ -------
2682
+ `list[int]`
2683
+ List of indices.
2684
+ """
2685
+ r = []
2686
+
2687
+ for i in range(self.MSXgetcount(EpanetConstants.MSX_SPECIES)):
2688
+ if self.MSXgetspecies(i + 1)[0] == EpanetConstants.MSX_WALL:
2689
+ r.append(i + 1)
2690
+
2691
+ return r
2692
+
2693
+ def get_msx_pattern(self, pattern_idx: int) -> list[float]:
2694
+ """
2695
+ Returns a particular MSX pattern -- i.e., returns the multipliers.
2696
+
2697
+ Parameters
2698
+ ----------
2699
+ pattern_idx: `int`
2700
+ Index of the pattern.
2701
+
2702
+ Returns
2703
+ -------
2704
+ `list[float]`
2705
+ Pattern multipliers.
2706
+ """
2707
+ r = []
2708
+
2709
+ pattern_length = self.MSXgetpatternlen(pattern_idx)
2710
+ for idx in range(1, pattern_length + 1):
2711
+ r.append(self.MSXgetpatternvalue(pattern_idx, idx))
2712
+
2713
+ return r
2714
+
2715
+ def get_all_msx_pattern_id(self) -> list[str]:
2716
+ """
2717
+ Returns a list of the IDs of all MSX patterns.
2718
+
2719
+ Returns
2720
+ -------
2721
+ `list[str]`
2722
+ List of patterns (IDs).
2723
+ """
2724
+ r = []
2725
+
2726
+ n_msx_patterns = self.MSXgetcount(EpanetConstants.MSX_PATTERN)
2727
+ for pattern_idx in range(1, n_msx_patterns + 1):
2728
+ r.append(self.MSXgetID(EpanetConstants.MSX_PATTERN, pattern_idx))
2729
+
2730
+ return r