easysewer 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- easysewer/Area.py +294 -0
- easysewer/Curve.py +119 -0
- easysewer/Link.py +294 -0
- easysewer/Node.py +346 -0
- easysewer/Options.py +373 -0
- easysewer/OutputAPI.py +179 -0
- easysewer/Rain.py +209 -0
- easysewer/SolverAPI.py +154 -0
- easysewer/UDM.py +126 -0
- easysewer/__init__.py +7 -0
- easysewer/libs/linux/libswmm5.so +0 -0
- easysewer/libs/linux/swmm-output.so +0 -0
- easysewer/libs/win/swmm-output.dll +0 -0
- easysewer/libs/win/swmm5.dll +0 -0
- easysewer/utils.py +97 -0
- easysewer-0.0.1.dist-info/METADATA +16 -0
- easysewer-0.0.1.dist-info/RECORD +19 -0
- easysewer-0.0.1.dist-info/WHEEL +5 -0
- easysewer-0.0.1.dist-info/top_level.txt +1 -0
easysewer/Options.py
ADDED
@@ -0,0 +1,373 @@
|
|
1
|
+
"""
|
2
|
+
Calculation Options Module
|
3
|
+
|
4
|
+
This module manages simulation options and parameters for the drainage model,
|
5
|
+
including time steps, routing methods, and other calculation settings.
|
6
|
+
"""
|
7
|
+
from .utils import *
|
8
|
+
|
9
|
+
|
10
|
+
class CalculationInformation:
|
11
|
+
"""
|
12
|
+
Controls simulation parameters and calculation options.
|
13
|
+
|
14
|
+
Manages various settings that control how the drainage simulation is performed,
|
15
|
+
including time steps, routing methods, and reporting options.
|
16
|
+
|
17
|
+
Attributes:
|
18
|
+
start_date (str): Simulation start date
|
19
|
+
start_time (str): Simulation start time
|
20
|
+
report_start_date (str): Report period start date
|
21
|
+
report_start_time (str): Report period start time
|
22
|
+
end_date (str): Simulation end date
|
23
|
+
end_time (str): Simulation end time
|
24
|
+
sweep_start (str): Street sweeping start date
|
25
|
+
sweep_end (str): Street sweeping end date
|
26
|
+
dry_days (float): Days with no rain prior to simulation
|
27
|
+
report_step (str): Reporting time step
|
28
|
+
wet_step (str): Wet weather time step
|
29
|
+
dry_step (str): Dry weather time step
|
30
|
+
routing_step (str): Flow routing time step
|
31
|
+
allow_ponding (bool): Whether ponding is allowed
|
32
|
+
inertial_damping (str): Type of inertial damping
|
33
|
+
normal_flow_limited (str): Normal flow limitation method
|
34
|
+
force_main_equation (str): Force main equation selection
|
35
|
+
variable_step (float): Variable step for dynamic wave routing
|
36
|
+
lengthening_step (float): Lengthening step for dynamic wave routing
|
37
|
+
min_surface_area (float): Minimum surface area for nodes
|
38
|
+
max_trials (int): Maximum trials per time step
|
39
|
+
head_tolerance (float): Head difference tolerance
|
40
|
+
sys_flow_tolerance (float): System flow tolerance
|
41
|
+
lat_flow_tolerance (float): Lateral flow tolerance
|
42
|
+
"""
|
43
|
+
def __init__(self):
|
44
|
+
# general option
|
45
|
+
self.flow_unit = 'CFS'
|
46
|
+
self.infiltration_method = 'HORTON'
|
47
|
+
self.flow_routing_method = 'KINWAVE'
|
48
|
+
self.link_offsets_type = 'DEPTH'
|
49
|
+
self.force_main_equation = 'H-W'
|
50
|
+
|
51
|
+
self.ignore_rainfall = False
|
52
|
+
self.ignore_snow_melt = False
|
53
|
+
self.ignore_ground_water = False
|
54
|
+
self.ignore_RDII = False
|
55
|
+
self.ignore_routing = False
|
56
|
+
self.ignore_water_quality = False
|
57
|
+
|
58
|
+
self.allow_ponding = True
|
59
|
+
self.skip_steady_state = False
|
60
|
+
self.system_flow_tol = 5
|
61
|
+
self.lateral_flow_tol = 5
|
62
|
+
|
63
|
+
self.simulation_start = {"year": 2023, "month": 4, "day": 28, "hour": 8, "minute": 0}
|
64
|
+
self.simulation_end = {"year": 2023, "month": 4, "day": 28, "hour": 17, "minute": 0}
|
65
|
+
self.report_start = {"year": 2023, "month": 4, "day": 28, "hour": 8, "minute": 0}
|
66
|
+
self.sweep_start = {"month": 1, "day": 1}
|
67
|
+
self.sweep_end = {"month": 12, "day": 31}
|
68
|
+
self.dry_days = 0
|
69
|
+
|
70
|
+
self.report_step = {"hour": 0, "minute": 15, "second": 0}
|
71
|
+
self.wet_step = {"hour": 0, "minute": 5, "second": 0} # runoff
|
72
|
+
self.dry_step = {"hour": 1, "minute": 0, "second": 0} # runoff
|
73
|
+
self.routing_step = 600 # in seconds
|
74
|
+
self.lengthening_step = 0 # in seconds
|
75
|
+
self.variable_step = 0
|
76
|
+
self.minimum_step = 0.5 # in seconds
|
77
|
+
|
78
|
+
self.inertial_damping = 'PARTIAL'
|
79
|
+
self.normal_flow_limited = 'BOTH'
|
80
|
+
|
81
|
+
self.minimum_surface_area = 0
|
82
|
+
self.minimum_slope = 0
|
83
|
+
self.max_trials = 8
|
84
|
+
self.head_tolerance = 0.0015 # in meters
|
85
|
+
|
86
|
+
self.threads = 1
|
87
|
+
self.temp_directory = None
|
88
|
+
|
89
|
+
# report section information
|
90
|
+
self.report_input = False
|
91
|
+
self.report_check_continuity = True
|
92
|
+
self.report_flow_statistics = True
|
93
|
+
self.report_controls = False
|
94
|
+
self.report_subcatchments = 'ALL'
|
95
|
+
self.report_nodes = 'ALL'
|
96
|
+
self.report_links = 'ALL'
|
97
|
+
|
98
|
+
# map section information
|
99
|
+
self.map_dimensions = [0, 0, 1000, 1000]
|
100
|
+
self.map_units = 'None'
|
101
|
+
|
102
|
+
# evaporation section information
|
103
|
+
self.evaporation_constant = 0
|
104
|
+
self.evaporation_dry_only = False
|
105
|
+
|
106
|
+
def __repr__(self):
|
107
|
+
return f'unit: {self.flow_unit}'
|
108
|
+
|
109
|
+
def write_to_swmm_inp(self, filename):
|
110
|
+
"""
|
111
|
+
|
112
|
+
:param filename:
|
113
|
+
"""
|
114
|
+
with open(filename, 'a', encoding='utf-8') as f:
|
115
|
+
f.write('[OPTIONS]\n')
|
116
|
+
f.write(f'FLOW_UNITS {self.flow_unit}\n')
|
117
|
+
f.write(f'INFILTRATION {self.infiltration_method}\n')
|
118
|
+
f.write(f'FLOW_ROUTING {self.flow_routing_method}\n')
|
119
|
+
f.write(f'LINK_OFFSETS {self.link_offsets_type}\n')
|
120
|
+
f.write(f'FORCE_MAIN_EQUATION {self.force_main_equation}\n')
|
121
|
+
f.write('\n')
|
122
|
+
f.write('IGNORE_RAINFALL ' + ('YES' if self.ignore_rainfall else 'NO') + '\n')
|
123
|
+
f.write('IGNORE_SNOWMELT ' + ('YES' if self.ignore_snow_melt else 'NO') + '\n')
|
124
|
+
f.write('IGNORE_GROUNDWATER ' + ('YES' if self.ignore_ground_water else 'NO') + '\n')
|
125
|
+
f.write('IGNORE_RDII ' + ('YES' if self.ignore_RDII else 'NO') + '\n')
|
126
|
+
f.write('IGNORE_ROUTING ' + ('YES' if self.ignore_routing else 'NO') + '\n')
|
127
|
+
f.write('IGNORE_QUALITY ' + ('YES' if self.ignore_water_quality else 'NO') + '\n')
|
128
|
+
f.write('\n')
|
129
|
+
f.write('ALLOW_PONDING ' + ('YES' if self.allow_ponding else 'NO') + '\n')
|
130
|
+
f.write('SKIP_STEADY_STATE ' + ('YES' if self.skip_steady_state else 'NO') + '\n')
|
131
|
+
f.write(f'SYS_FLOW_TOL {self.system_flow_tol}\n')
|
132
|
+
f.write(f'LAT_FLOW_TOL {self.lateral_flow_tol}\n')
|
133
|
+
|
134
|
+
f.write('\n')
|
135
|
+
f.write('START_DATE ')
|
136
|
+
f.write(str(self.simulation_start['month']).zfill(2) + '/')
|
137
|
+
f.write(str(self.simulation_start['day']).zfill(2) + '/')
|
138
|
+
f.write(str(self.simulation_start['year']) + '\n')
|
139
|
+
f.write('START_TIME ')
|
140
|
+
f.write(str(self.simulation_start['hour']).zfill(2) + ':')
|
141
|
+
f.write(str(self.simulation_start['minute']).zfill(2) + '\n')
|
142
|
+
f.write('END_DATE ')
|
143
|
+
f.write(str(self.simulation_end['month']).zfill(2) + '/')
|
144
|
+
f.write(str(self.simulation_end['day']).zfill(2) + '/')
|
145
|
+
f.write(str(self.simulation_end['year']) + '\n')
|
146
|
+
f.write('END_TIME ')
|
147
|
+
f.write(str(self.simulation_end['hour']).zfill(2) + ':')
|
148
|
+
f.write(str(self.simulation_end['minute']).zfill(2) + '\n')
|
149
|
+
f.write('REPORT_START_DATE ')
|
150
|
+
f.write(str(self.report_start['month']).zfill(2) + '/')
|
151
|
+
f.write(str(self.report_start['day']).zfill(2) + '/')
|
152
|
+
f.write(str(self.report_start['year']) + '\n')
|
153
|
+
f.write('REPORT_START_TIME ')
|
154
|
+
f.write(str(self.report_start['hour']).zfill(2) + ':')
|
155
|
+
f.write(str(self.report_start['minute']).zfill(2) + '\n')
|
156
|
+
f.write('SWEEP_START ')
|
157
|
+
f.write(str(self.sweep_start['month']).zfill(2) + '/')
|
158
|
+
f.write(str(self.sweep_start['day']).zfill(2) + '\n')
|
159
|
+
f.write('SWEEP_END ')
|
160
|
+
f.write(str(self.sweep_end['month']).zfill(2) + '/')
|
161
|
+
f.write(str(self.sweep_end['day']).zfill(2) + '\n')
|
162
|
+
|
163
|
+
f.write('\n')
|
164
|
+
f.write(f'DRY_DAYS {self.dry_days}\n')
|
165
|
+
f.write('REPORT_STEP ')
|
166
|
+
f.write(str(self.report_step['hour']).zfill(2) + ':')
|
167
|
+
f.write(str(self.report_step['minute']).zfill(2) + ':')
|
168
|
+
f.write(str(self.report_step['second']).zfill(2) + '\n')
|
169
|
+
f.write('WET_STEP ')
|
170
|
+
f.write(str(self.wet_step['hour']).zfill(2) + ':')
|
171
|
+
f.write(str(self.wet_step['minute']).zfill(2) + ':')
|
172
|
+
f.write(str(self.wet_step['second']).zfill(2) + '\n')
|
173
|
+
f.write('DRY_STEP ')
|
174
|
+
f.write(str(self.dry_step['hour']).zfill(2) + ':')
|
175
|
+
f.write(str(self.dry_step['minute']).zfill(2) + ':')
|
176
|
+
f.write(str(self.dry_step['second']).zfill(2) + '\n')
|
177
|
+
|
178
|
+
f.write('\n')
|
179
|
+
f.write(f'ROUTING_STEP {self.routing_step}\n')
|
180
|
+
f.write(f'LENGTHENING_STEP {self.lengthening_step}\n')
|
181
|
+
f.write(f'VARIABLE_STEP {self.variable_step}\n')
|
182
|
+
f.write(f'MINIMUM_STEP {self.minimum_step}\n')
|
183
|
+
|
184
|
+
f.write('\n')
|
185
|
+
f.write(f'INERTIAL_DAMPING {self.inertial_damping}\n')
|
186
|
+
f.write(f'NORMAL_FLOW_LIMITED {self.normal_flow_limited}\n')
|
187
|
+
f.write(f'MIN_SURFAREA {self.minimum_surface_area}\n')
|
188
|
+
f.write(f'MIN_SLOPE {self.minimum_slope}\n')
|
189
|
+
f.write(f'MAX_TRIALS {self.max_trials}\n')
|
190
|
+
f.write(f'HEAD_TOLERANCE {self.head_tolerance}\n')
|
191
|
+
f.write(f'THREADS {self.threads}\n')
|
192
|
+
if self.temp_directory is not None:
|
193
|
+
f.write(f'TEMPDIR {self.temp_directory}\n')
|
194
|
+
|
195
|
+
f.write('\n\n[REPORT]\n')
|
196
|
+
f.write('INPUT ' + ('YES' if self.report_input else 'NO') + '\n')
|
197
|
+
f.write('CONTINUITY ' + ('YES' if self.report_check_continuity else 'NO') + '\n')
|
198
|
+
f.write('FLOWSTATS ' + ('YES' if self.report_flow_statistics else 'NO') + '\n')
|
199
|
+
f.write('CONTROLS ' + ('YES' if self.report_controls else 'NO') + '\n')
|
200
|
+
f.write(f'SUBCATCHMENTS {self.report_subcatchments}\n')
|
201
|
+
f.write(f'NODES {self.report_nodes}\n')
|
202
|
+
f.write(f'LINKS {self.report_links}\n')
|
203
|
+
|
204
|
+
f.write('\n\n[MAP]\n')
|
205
|
+
f.write(
|
206
|
+
f'DIMENSIONS {self.map_dimensions[0]} {self.map_dimensions[1]} {self.map_dimensions[2]} {self.map_dimensions[3]}\n')
|
207
|
+
f.write(f'Units {self.map_units}\n')
|
208
|
+
|
209
|
+
f.write('\n\n[EVAPORATION]\n')
|
210
|
+
f.write(f'CONSTANT {self.evaporation_constant}\n')
|
211
|
+
f.write('DRY_ONLY ' + ('YES' if self.evaporation_dry_only else 'NO') + '\n')
|
212
|
+
|
213
|
+
def read_from_swmm_inp(self, filename):
|
214
|
+
"""
|
215
|
+
|
216
|
+
:param filename:
|
217
|
+
:return:
|
218
|
+
"""
|
219
|
+
contents = get_swmm_inp_content(filename, '[OPTIONS]')
|
220
|
+
for line in contents:
|
221
|
+
pair = line.split()
|
222
|
+
match pair[0]:
|
223
|
+
case 'FLOW_UNITS':
|
224
|
+
self.flow_unit = pair[1]
|
225
|
+
case 'INFILTRATION':
|
226
|
+
self.infiltration_method = pair[1]
|
227
|
+
case 'FLOW_ROUTING':
|
228
|
+
self.flow_routing_method = pair[1]
|
229
|
+
case 'LINK_OFFSETS':
|
230
|
+
self.link_offsets_type = pair[1]
|
231
|
+
case 'FORCE_MAIN_EQUATION':
|
232
|
+
self.force_main_equation = pair[1]
|
233
|
+
case 'IGNORE_RAINFALL':
|
234
|
+
self.ignore_rainfall = True if pair[1] == 'YES' else False
|
235
|
+
case 'IGNORE_SNOWMELT':
|
236
|
+
self.ignore_snow_melt = True if pair[1] == 'YES' else False
|
237
|
+
case 'IGNORE_GROUNDWATER':
|
238
|
+
self.ignore_ground_water = True if pair[1] == 'YES' else False
|
239
|
+
case 'IGNORE_RDII':
|
240
|
+
self.ignore_RDII = True if pair[1] == 'YES' else False
|
241
|
+
case 'IGNORE_ROUTING':
|
242
|
+
self.ignore_routing = True if pair[1] == 'YES' else False
|
243
|
+
case 'IGNORE_QUALITY':
|
244
|
+
self.ignore_water_quality = True if pair[1] == 'YES' else False
|
245
|
+
case 'ALLOW_PONDING':
|
246
|
+
self.allow_ponding = True if pair[1] == 'YES' else False
|
247
|
+
case 'SKIP_STEADY_STATE':
|
248
|
+
self.skip_steady_state = True if pair[1] == 'YES' else False
|
249
|
+
case 'SYS_FLOW_TOL':
|
250
|
+
self.system_flow_tol = int(pair[1])
|
251
|
+
case 'LAT_FLOW_TOL':
|
252
|
+
self.lateral_flow_tol = int(pair[1])
|
253
|
+
case 'START_DATE':
|
254
|
+
keys = [int(i) for i in pair[1].split('/')]
|
255
|
+
self.simulation_start['year'] = keys[2]
|
256
|
+
self.simulation_start['month'] = keys[0]
|
257
|
+
self.simulation_start['day'] = keys[1]
|
258
|
+
case 'START_TIME':
|
259
|
+
keys = [int(i) for i in pair[1].split(':')]
|
260
|
+
self.simulation_start['hour'] = keys[0]
|
261
|
+
self.simulation_start['minute'] = keys[1]
|
262
|
+
case 'END_DATE':
|
263
|
+
keys = [int(i) for i in pair[1].split('/')]
|
264
|
+
self.simulation_end['year'] = keys[2]
|
265
|
+
self.simulation_end['month'] = keys[0]
|
266
|
+
self.simulation_end['day'] = keys[1]
|
267
|
+
case 'END_TIME':
|
268
|
+
keys = [int(i) for i in pair[1].split(':')]
|
269
|
+
self.simulation_end['hour'] = keys[0]
|
270
|
+
self.simulation_end['minute'] = keys[1]
|
271
|
+
case 'REPORT_START_DATE':
|
272
|
+
keys = [int(i) for i in pair[1].split('/')]
|
273
|
+
self.report_start['year'] = keys[2]
|
274
|
+
self.report_start['month'] = keys[0]
|
275
|
+
self.report_start['day'] = keys[1]
|
276
|
+
case 'REPORT_START_TIME':
|
277
|
+
keys = [int(i) for i in pair[1].split(':')]
|
278
|
+
self.report_start['hour'] = keys[0]
|
279
|
+
self.report_start['minute'] = keys[1]
|
280
|
+
case 'SWEEP_START':
|
281
|
+
keys = [int(i) for i in pair[1].split('/')]
|
282
|
+
self.sweep_start['month'] = keys[0]
|
283
|
+
self.sweep_start['day'] = keys[1]
|
284
|
+
case 'SWEEP_END':
|
285
|
+
keys = [int(i) for i in pair[1].split('/')]
|
286
|
+
self.sweep_end['month'] = keys[0]
|
287
|
+
self.sweep_end['day'] = keys[1]
|
288
|
+
case 'DRY_DAYS':
|
289
|
+
self.dry_days = int(pair[1])
|
290
|
+
case 'REPORT_STEP':
|
291
|
+
keys = [int(i) for i in pair[1].split(':')]
|
292
|
+
self.report_step['hour'] = keys[0]
|
293
|
+
self.report_step['minute'] = keys[1]
|
294
|
+
self.report_step['second'] = keys[2]
|
295
|
+
case 'WET_STEP':
|
296
|
+
keys = [int(i) for i in pair[1].split(':')]
|
297
|
+
self.wet_step['hour'] = keys[0]
|
298
|
+
self.wet_step['minute'] = keys[1]
|
299
|
+
self.wet_step['second'] = keys[2]
|
300
|
+
case 'DRY_STEP':
|
301
|
+
keys = [int(i) for i in pair[1].split(':')]
|
302
|
+
self.dry_step['hour'] = keys[0]
|
303
|
+
self.dry_step['minute'] = keys[1]
|
304
|
+
self.dry_step['second'] = keys[2]
|
305
|
+
case 'ROUTING_STEP':
|
306
|
+
keys = [int(i) for i in pair[1].split(':')]
|
307
|
+
if len(keys) == 1:
|
308
|
+
self.routing_step = keys[0]
|
309
|
+
elif len(keys) == 2:
|
310
|
+
self.routing_step = keys[-1] + keys[-2] * 60
|
311
|
+
elif len(keys) == 3:
|
312
|
+
self.routing_step = keys[-1] + keys[-2] * 60 + keys[-3] * 3600
|
313
|
+
else:
|
314
|
+
pass
|
315
|
+
case 'LENGTHENING_STEP':
|
316
|
+
keys = [int(i) for i in pair[1].split(':')]
|
317
|
+
if len(keys) == 1:
|
318
|
+
self.lengthening_step = keys[0]
|
319
|
+
elif len(keys) == 2:
|
320
|
+
self.lengthening_step = keys[-1] + keys[-2] * 60
|
321
|
+
elif len(keys) == 3:
|
322
|
+
self.lengthening_step = keys[-1] + keys[-2] * 60 + keys[-3] * 3600
|
323
|
+
else:
|
324
|
+
pass
|
325
|
+
case 'VARIABLE_STEP':
|
326
|
+
self.variable_step = float(pair[1])
|
327
|
+
case 'MINIMUM_STEP':
|
328
|
+
self.minimum_step = float(pair[1])
|
329
|
+
case 'INERTIAL_DAMPING':
|
330
|
+
self.inertial_damping = pair[1]
|
331
|
+
case 'NORMAL_FLOW_LIMITED':
|
332
|
+
self.normal_flow_limited = pair[1]
|
333
|
+
case 'MIN_SURFAREA':
|
334
|
+
self.minimum_surface_area = float(pair[1])
|
335
|
+
case 'MIN_SLOPE':
|
336
|
+
self.minimum_slope = float(pair[1])
|
337
|
+
case 'MAX_TRIALS':
|
338
|
+
self.max_trials = int(pair[1])
|
339
|
+
case 'HEAD_TOLERANCE':
|
340
|
+
self.head_tolerance = float(pair[1])
|
341
|
+
case 'THREADS':
|
342
|
+
self.threads = int(pair[1])
|
343
|
+
case 'TEMPDIR':
|
344
|
+
self.temp_directory = pair[1]
|
345
|
+
case _:
|
346
|
+
pass
|
347
|
+
contents = get_swmm_inp_content(filename, '[REPORT]')
|
348
|
+
for line in contents:
|
349
|
+
pair = line.split()
|
350
|
+
match pair[0]:
|
351
|
+
case 'INPUT':
|
352
|
+
self.report_input = True if pair[1] == 'YES' else False
|
353
|
+
case 'CONTINUITY':
|
354
|
+
self.report_check_continuity = True if pair[1] == 'YES' else False
|
355
|
+
case 'FLOWSTATS':
|
356
|
+
self.report_flow_statistics = True if pair[1] == 'YES' else False
|
357
|
+
case 'CONTROLS':
|
358
|
+
self.report_controls = True if pair[1] == 'YES' else False
|
359
|
+
case 'SUBCATCHMENTS':
|
360
|
+
self.report_subcatchments = pair[1]
|
361
|
+
case 'NODES':
|
362
|
+
self.report_nodes = pair[1]
|
363
|
+
case 'LINKS':
|
364
|
+
self.report_links = pair[1]
|
365
|
+
contents = get_swmm_inp_content(filename, '[MAP]')
|
366
|
+
for line in contents:
|
367
|
+
pair = line.split()
|
368
|
+
match pair[0]:
|
369
|
+
case 'DIMENSIONS':
|
370
|
+
self.map_dimensions = [float(pair[1]), float(pair[2]), float(pair[3]), float(pair[4])]
|
371
|
+
case 'Units':
|
372
|
+
self.map_units = pair[1]
|
373
|
+
return 0
|
easysewer/OutputAPI.py
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
"""
|
2
|
+
pass
|
3
|
+
"""
|
4
|
+
import ctypes
|
5
|
+
import platform
|
6
|
+
import os
|
7
|
+
|
8
|
+
|
9
|
+
class SWMMOutputAPI:
|
10
|
+
def __init__(self):
|
11
|
+
#
|
12
|
+
system = platform.system()
|
13
|
+
if system == 'Windows':
|
14
|
+
lib_path = os.path.join(os.path.dirname(__file__), 'libs', 'win', 'swmm-output.dll')
|
15
|
+
elif system == 'Linux':
|
16
|
+
lib_path = os.path.join(os.path.dirname(__file__), 'libs', 'linux', 'swmm-output.so')
|
17
|
+
else:
|
18
|
+
raise OSError('Unsupported operating system')
|
19
|
+
#
|
20
|
+
# Load the shared library
|
21
|
+
self.lib = ctypes.CDLL(lib_path)
|
22
|
+
#
|
23
|
+
self._set_prototypes()
|
24
|
+
|
25
|
+
def _set_prototypes(self):
|
26
|
+
# Define the function prototypes
|
27
|
+
self.lib.SMO_init.argtypes = [ctypes.POINTER(ctypes.c_void_p)]
|
28
|
+
self.lib.SMO_init.restype = ctypes.c_int
|
29
|
+
|
30
|
+
self.lib.SMO_close.argtypes = [ctypes.POINTER(ctypes.c_void_p)]
|
31
|
+
self.lib.SMO_close.restype = ctypes.c_int
|
32
|
+
|
33
|
+
self.lib.SMO_open.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
|
34
|
+
self.lib.SMO_open.restype = ctypes.c_int
|
35
|
+
|
36
|
+
self.lib.SMO_getVersion.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_int)]
|
37
|
+
self.lib.SMO_getVersion.restype = ctypes.c_int
|
38
|
+
|
39
|
+
self.lib.SMO_getFlowUnits.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_int)]
|
40
|
+
self.lib.SMO_getFlowUnits.restype = ctypes.c_int
|
41
|
+
|
42
|
+
self.lib.SMO_getStartDate.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_double)]
|
43
|
+
self.lib.SMO_getStartDate.restype = ctypes.c_int
|
44
|
+
|
45
|
+
self.lib.SMO_getTimes.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.POINTER(ctypes.c_int)]
|
46
|
+
self.lib.SMO_getTimes.restype = ctypes.c_int
|
47
|
+
|
48
|
+
self.lib.SMO_getSubcatchSeries.argtypes = [
|
49
|
+
ctypes.c_void_p, # Handle
|
50
|
+
ctypes.c_int, # Subcatch index
|
51
|
+
ctypes.c_int, # Subcatch attribute
|
52
|
+
ctypes.c_int, # Start period
|
53
|
+
ctypes.c_int, # End period
|
54
|
+
ctypes.POINTER(ctypes.POINTER(ctypes.c_float)), # Output value array
|
55
|
+
ctypes.POINTER(ctypes.c_int) # Length
|
56
|
+
]
|
57
|
+
self.lib.SMO_getSubcatchSeries.restype = ctypes.c_int
|
58
|
+
|
59
|
+
self.lib.SMO_getNodeSeries.argtypes = [
|
60
|
+
ctypes.c_void_p, # Handle
|
61
|
+
ctypes.c_int, # Node index
|
62
|
+
ctypes.c_int, # Node attribute
|
63
|
+
ctypes.c_int, # Start period
|
64
|
+
ctypes.c_int, # End period
|
65
|
+
ctypes.POINTER(ctypes.POINTER(ctypes.c_float)), # Output value array
|
66
|
+
ctypes.POINTER(ctypes.c_int) # Length
|
67
|
+
]
|
68
|
+
self.lib.SMO_getNodeSeries.restype = ctypes.c_int
|
69
|
+
|
70
|
+
self.lib.SMO_getLinkSeries.argtypes = [
|
71
|
+
ctypes.c_void_p, # Handle
|
72
|
+
ctypes.c_int, # Link index
|
73
|
+
ctypes.c_int, # Link attribute
|
74
|
+
ctypes.c_int, # Start period
|
75
|
+
ctypes.c_int, # End period
|
76
|
+
ctypes.POINTER(ctypes.POINTER(ctypes.c_float)), # Output value array
|
77
|
+
ctypes.POINTER(ctypes.c_int) # Length
|
78
|
+
]
|
79
|
+
self.lib.SMO_getLinkSeries.restype = ctypes.c_int
|
80
|
+
|
81
|
+
self.lib.SMO_getSystemSeries.argtypes = [
|
82
|
+
ctypes.c_void_p, # Handle
|
83
|
+
ctypes.c_int, # System attribute
|
84
|
+
ctypes.c_int, # Start period
|
85
|
+
ctypes.c_int, # End period
|
86
|
+
ctypes.POINTER(ctypes.POINTER(ctypes.c_float)), # Output value array
|
87
|
+
ctypes.POINTER(ctypes.c_int) # Length
|
88
|
+
]
|
89
|
+
self.lib.SMO_getSystemSeries.restype = ctypes.c_int
|
90
|
+
|
91
|
+
self.lib.SMO_free.argtypes = [ctypes.POINTER(ctypes.POINTER(ctypes.c_float))]
|
92
|
+
self.lib.SMO_free.restype = None
|
93
|
+
|
94
|
+
# Initialize the API
|
95
|
+
self.handle = ctypes.c_void_p()
|
96
|
+
if self.lib.SMO_init(ctypes.byref(self.handle)) != 0:
|
97
|
+
raise Exception("Failed to initialize SWMM Output API")
|
98
|
+
|
99
|
+
def __enter__(self):
|
100
|
+
if self.lib.SMO_init(ctypes.byref(self.handle)) != 0:
|
101
|
+
raise Exception("Failed to initialize SWMM Output API")
|
102
|
+
self.initialized = True
|
103
|
+
return self
|
104
|
+
|
105
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
106
|
+
if self.initialized and self.handle:
|
107
|
+
if self.lib.SMO_close(ctypes.byref(self.handle)) != 0:
|
108
|
+
raise Exception("Failed to close SWMM Output API")
|
109
|
+
self.initialized = False
|
110
|
+
|
111
|
+
def open(self, file_path):
|
112
|
+
if self.lib.SMO_open(self.handle, file_path.encode('utf-8')) != 0:
|
113
|
+
raise Exception("Failed to open SWMM output file")
|
114
|
+
|
115
|
+
def get_flow_units(self):
|
116
|
+
unit_flag = ctypes.c_int()
|
117
|
+
if self.lib.SMO_getFlowUnits(self.handle, ctypes.byref(unit_flag)) != 0:
|
118
|
+
raise Exception("Failed to get flow units")
|
119
|
+
return unit_flag.value
|
120
|
+
|
121
|
+
def get_start_date(self):
|
122
|
+
start_date = ctypes.c_double()
|
123
|
+
if self.lib.SMO_getStartDate(self.handle, ctypes.byref(start_date)) != 0:
|
124
|
+
raise Exception("Failed to get start date")
|
125
|
+
return start_date.value
|
126
|
+
|
127
|
+
def get_times(self, code):
|
128
|
+
time = ctypes.c_int()
|
129
|
+
if self.lib.SMO_getTimes(self.handle, code, ctypes.byref(time)) != 0:
|
130
|
+
raise Exception(f"Failed to get times for code {code}")
|
131
|
+
return time.value
|
132
|
+
|
133
|
+
def get_subcatch_series(self, subcatch_index, attr, start_period, end_period):
|
134
|
+
value_array = ctypes.POINTER(ctypes.c_float)()
|
135
|
+
length = ctypes.c_int()
|
136
|
+
if self.lib.SMO_getSubcatchSeries(
|
137
|
+
self.handle, subcatch_index, attr, start_period, end_period,
|
138
|
+
ctypes.byref(value_array), ctypes.byref(length)
|
139
|
+
) != 0:
|
140
|
+
raise Exception("Failed to get subcatch series")
|
141
|
+
values = [value_array[i] for i in range(length.value)]
|
142
|
+
self.lib.SMO_free(ctypes.byref(value_array))
|
143
|
+
return values
|
144
|
+
|
145
|
+
def get_node_series(self, node_index, attr, start_period, end_period):
|
146
|
+
value_array = ctypes.POINTER(ctypes.c_float)()
|
147
|
+
length = ctypes.c_int()
|
148
|
+
if self.lib.SMO_getNodeSeries(
|
149
|
+
self.handle, node_index, attr, start_period, end_period,
|
150
|
+
ctypes.byref(value_array), ctypes.byref(length)
|
151
|
+
) != 0:
|
152
|
+
raise Exception("Failed to get node series")
|
153
|
+
values = [value_array[i] for i in range(length.value)]
|
154
|
+
self.lib.SMO_free(ctypes.byref(value_array))
|
155
|
+
return values
|
156
|
+
|
157
|
+
def get_link_series(self, link_index, attr, start_period, end_period):
|
158
|
+
value_array = ctypes.POINTER(ctypes.c_float)()
|
159
|
+
length = ctypes.c_int()
|
160
|
+
if self.lib.SMO_getLinkSeries(
|
161
|
+
self.handle, link_index, attr, start_period, end_period,
|
162
|
+
ctypes.byref(value_array), ctypes.byref(length)
|
163
|
+
) != 0:
|
164
|
+
raise Exception("Failed to get link series")
|
165
|
+
values = [value_array[i] for i in range(length.value)]
|
166
|
+
self.lib.SMO_free(ctypes.byref(value_array))
|
167
|
+
return values
|
168
|
+
|
169
|
+
def get_system_series(self, attr, start_period, end_period):
|
170
|
+
value_array = ctypes.POINTER(ctypes.c_float)()
|
171
|
+
length = ctypes.c_int()
|
172
|
+
if self.lib.SMO_getSystemSeries(
|
173
|
+
self.handle, attr, start_period, end_period,
|
174
|
+
ctypes.byref(value_array), ctypes.byref(length)
|
175
|
+
) != 0:
|
176
|
+
raise Exception("Failed to get system series")
|
177
|
+
values = [value_array[i] for i in range(length.value)]
|
178
|
+
self.lib.SMO_free(ctypes.byref(value_array))
|
179
|
+
return values
|