epanet-plus 0.0.1__cp313-cp313-macosx_11_0_arm64.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.
- docs/conf.py +67 -0
- epanet-msx-src/dispersion.h +27 -0
- epanet-msx-src/hash.c +107 -0
- epanet-msx-src/hash.h +28 -0
- epanet-msx-src/include/epanetmsx.h +104 -0
- epanet-msx-src/include/epanetmsx_export.h +42 -0
- epanet-msx-src/mathexpr.c +937 -0
- epanet-msx-src/mathexpr.h +39 -0
- epanet-msx-src/mempool.c +204 -0
- epanet-msx-src/mempool.h +24 -0
- epanet-msx-src/msxchem.c +1285 -0
- epanet-msx-src/msxcompiler.c +368 -0
- epanet-msx-src/msxdict.h +42 -0
- epanet-msx-src/msxdispersion.c +586 -0
- epanet-msx-src/msxerr.c +116 -0
- epanet-msx-src/msxfile.c +260 -0
- epanet-msx-src/msxfuncs.c +175 -0
- epanet-msx-src/msxfuncs.h +35 -0
- epanet-msx-src/msxinp.c +1504 -0
- epanet-msx-src/msxout.c +398 -0
- epanet-msx-src/msxproj.c +791 -0
- epanet-msx-src/msxqual.c +2011 -0
- epanet-msx-src/msxrpt.c +400 -0
- epanet-msx-src/msxtank.c +422 -0
- epanet-msx-src/msxtoolkit.c +1164 -0
- epanet-msx-src/msxtypes.h +551 -0
- epanet-msx-src/msxutils.c +524 -0
- epanet-msx-src/msxutils.h +56 -0
- epanet-msx-src/newton.c +158 -0
- epanet-msx-src/newton.h +34 -0
- epanet-msx-src/rk5.c +287 -0
- epanet-msx-src/rk5.h +39 -0
- epanet-msx-src/ros2.c +293 -0
- epanet-msx-src/ros2.h +35 -0
- epanet-msx-src/smatrix.c +816 -0
- epanet-msx-src/smatrix.h +29 -0
- epanet-src/AUTHORS +60 -0
- epanet-src/LICENSE +21 -0
- epanet-src/enumstxt.h +151 -0
- epanet-src/epanet.c +5937 -0
- epanet-src/epanet2.c +961 -0
- epanet-src/epanet2.def +131 -0
- epanet-src/errors.dat +79 -0
- epanet-src/flowbalance.c +186 -0
- epanet-src/funcs.h +219 -0
- epanet-src/genmmd.c +1000 -0
- epanet-src/hash.c +177 -0
- epanet-src/hash.h +28 -0
- epanet-src/hydcoeffs.c +1303 -0
- epanet-src/hydraul.c +1164 -0
- epanet-src/hydsolver.c +781 -0
- epanet-src/hydstatus.c +442 -0
- epanet-src/include/epanet2.h +466 -0
- epanet-src/include/epanet2_2.h +1962 -0
- epanet-src/include/epanet2_enums.h +518 -0
- epanet-src/inpfile.c +884 -0
- epanet-src/input1.c +672 -0
- epanet-src/input2.c +970 -0
- epanet-src/input3.c +2265 -0
- epanet-src/leakage.c +527 -0
- epanet-src/mempool.c +146 -0
- epanet-src/mempool.h +24 -0
- epanet-src/output.c +853 -0
- epanet-src/project.c +1691 -0
- epanet-src/quality.c +695 -0
- epanet-src/qualreact.c +800 -0
- epanet-src/qualroute.c +696 -0
- epanet-src/report.c +1559 -0
- epanet-src/rules.c +1500 -0
- epanet-src/smatrix.c +871 -0
- epanet-src/text.h +508 -0
- epanet-src/types.h +928 -0
- epanet-src/util/cstr_helper.c +59 -0
- epanet-src/util/cstr_helper.h +38 -0
- epanet-src/util/errormanager.c +92 -0
- epanet-src/util/errormanager.h +39 -0
- epanet-src/util/filemanager.c +212 -0
- epanet-src/util/filemanager.h +81 -0
- epanet-src/validate.c +408 -0
- epanet.cpython-313-darwin.so +0 -0
- epanet_plus/VERSION +1 -0
- epanet_plus/__init__.py +8 -0
- epanet_plus/epanet_plus.c +118 -0
- epanet_plus/epanet_toolkit.py +2730 -0
- epanet_plus/epanet_wrapper.py +2414 -0
- epanet_plus/include/epanet_plus.h +9 -0
- epanet_plus-0.0.1.dist-info/METADATA +152 -0
- epanet_plus-0.0.1.dist-info/RECORD +105 -0
- epanet_plus-0.0.1.dist-info/WHEEL +6 -0
- epanet_plus-0.0.1.dist-info/licenses/LICENSE +21 -0
- epanet_plus-0.0.1.dist-info/top_level.txt +11 -0
- examples/basic_usage.py +35 -0
- python-extension/ext.c +344 -0
- python-extension/pyepanet.c +2133 -0
- python-extension/pyepanet.h +143 -0
- python-extension/pyepanet2.c +1823 -0
- python-extension/pyepanet2.h +141 -0
- python-extension/pyepanet_plus.c +37 -0
- python-extension/pyepanet_plus.h +4 -0
- python-extension/pyepanetmsx.c +388 -0
- python-extension/pyepanetmsx.h +35 -0
- tests/test_epanet.py +16 -0
- tests/test_epanetmsx.py +36 -0
- tests/test_epyt.py +114 -0
- 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
|