openswmm 5.3.0.dev0__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.
@@ -0,0 +1,1646 @@
1
+ # cython: language_level=3str
2
+ # Description: Cython module for openswmmcore solver
3
+ # Created by: Caleb Buahin (EPA/ORD/CESER/WID)
4
+ # Created on: 2024-11-19
5
+
6
+ # python and cython imports
7
+ from enum import Enum
8
+ from warnings import warn
9
+ from typing import List, Tuple, Union, Dict, Set, Callable
10
+ from cpython.datetime cimport datetime as cython_datetime
11
+ from datetime import datetime, timedelta
12
+ from libc.stdlib cimport free, malloc
13
+ from functools import partialmethod
14
+
15
+ # external imports
16
+ from .solver cimport (
17
+ PyObject_CallObject,
18
+ clock_t,
19
+ clock,
20
+ swmm_Object,
21
+ swmm_NodeType,
22
+ swmm_LinkType,
23
+ swmm_GageProperty,
24
+ swmm_SubcatchProperty,
25
+ swmm_NodeProperty,
26
+ swmm_LinkProperty,
27
+ swmm_SystemProperty,
28
+ swmm_FlowUnitsProperty,
29
+ swmm_API_Errors,
30
+ progress_callback,
31
+ swmm_run,
32
+ swmm_run_with_callback,
33
+ swmm_open,
34
+ swmm_start,
35
+ swmm_step,
36
+ swmm_stride,
37
+ swmm_useHotStart,
38
+ swmm_saveHotStart,
39
+ swmm_end,
40
+ swmm_report,
41
+ swmm_close,
42
+ swmm_getMassBalErr,
43
+ swmm_getVersion,
44
+ swmm_getError,
45
+ swmm_getErrorFromCode,
46
+ swmm_getWarnings,
47
+ swmm_getCount,
48
+ swmm_getName,
49
+ swmm_getIndex,
50
+ swmm_getValue,
51
+ swmm_getValueExpanded,
52
+ swmm_setValue,
53
+ swmm_setValueExpanded,
54
+ swmm_getSavedValue,
55
+ swmm_writeLine,
56
+ swmm_decodeDate,
57
+ swmm_encodeDate
58
+ )
59
+
60
+ class SWMMObjects(Enum):
61
+ """
62
+ Enumeration of SWMM objects.
63
+
64
+ :ivar RAIN_GAGE: Raingage object
65
+ :type RAIN_GAGE: int
66
+ :ivar SUBCATCHMENT: Subcatchment object
67
+ :type SUBCATCHMENT: int
68
+ :ivar NODE: Node object
69
+ :type NODE: int
70
+ :ivar LINK: Link object
71
+ :type LINK: int
72
+ :ivar AQUIFER: Aquifer object
73
+ :type AQUIFER: int
74
+ :ivar SNOWPACK: Snowpack object
75
+ :type SNOWPACK: int
76
+ :ivar UNIT_HYDROGRAPH: Unit hydrograph object
77
+ :type UNIT_HYDROGRAPH: int
78
+ :ivar LID: LID object
79
+ :type LID: int
80
+ :ivar STREET: Street object
81
+ :type STREET: int
82
+ :ivar INLET: Inlet object
83
+ :type INLET: int
84
+ :ivar TRANSECT: Transect object
85
+ :type TRANSECT: int
86
+ :ivar XSECTION_SHAPE: Cross-section shape object
87
+ :type XSECTION_SHAPE: int
88
+ :ivar CONTROL_RULE: Control rule object
89
+ :type CONTROL_RULE: int
90
+ :ivar POLLUTANT: Pollutant object
91
+ :type POLLUTANT: int
92
+ :ivar LANDUSE: Land use object
93
+ :type LANDUSE: int
94
+ :ivar CURVE: Curve object
95
+ :type CURVE: int
96
+ :ivar TIMESERIES: Time series object
97
+ :type TIMESERIES: int
98
+ :ivar TIME_PATTERN: Time pattern object
99
+ :type TIME_PATTERN: int
100
+ :ivar SYSTEM: System object
101
+ :type SYSTEM: int
102
+ """
103
+ RAIN_GAGE = swmm_Object.swmm_GAGE
104
+ SUBCATCHMENT = swmm_Object.swmm_SUBCATCH
105
+ NODE = swmm_Object.swmm_NODE
106
+ LINK = swmm_Object.swmm_LINK
107
+ AQUIFER = swmm_Object.swmm_AQUIFER
108
+ SNOWPACK = swmm_Object.swmm_SNOWPACK
109
+ UNIT_HYDROGRAPH = swmm_Object.swmm_UNIT_HYDROGRAPH
110
+ LID = swmm_Object.swmm_LID
111
+ STREET = swmm_Object.swmm_STREET
112
+ INLET = swmm_Object.swmm_INLET
113
+ TRANSECT = swmm_Object.swmm_TRANSECT
114
+ XSECTION_SHAPE = swmm_Object.smmm_XSECTION_SHAPE
115
+ CONTROL_RULE = swmm_Object.swmm_CONTROL_RULE
116
+ POLLUTANT = swmm_Object.swmm_POLLUTANT
117
+ LANDUSE = swmm_Object.swmm_LANDUSE
118
+ CURVE = swmm_Object.swmm_CURVE
119
+ TIMESERIES = swmm_Object.swmm_TIMESERIES
120
+ TIME_PATTERN = swmm_Object.swmm_TIME_PATTERN
121
+ SYSTEM = swmm_Object.swmm_SYSTEM
122
+
123
+ class SWMMNodeTypes(Enum):
124
+ """
125
+ Enumeration of SWMM node types.
126
+
127
+ :ivar JUNCTION: Junction node
128
+ :type JUNCTION: int
129
+ :ivar OUTFALL: Outfall node
130
+ :type OUTFALL: int
131
+ :ivar STORAGE: Storage node
132
+ :type STORAGE: int
133
+ :ivar DIVIDER: Divider node
134
+ :type DIVIDER: int
135
+ """
136
+ JUNCTION = swmm_NodeType.swmm_JUNCTION
137
+ OUTFALL = swmm_NodeType.swmm_OUTFALL
138
+ STORAGE = swmm_NodeType.swmm_STORAGE
139
+ DIVIDER = swmm_NodeType.swmm_DIVIDER
140
+
141
+ class SWMMRainGageProperties(Enum):
142
+ """
143
+ Enumeration of SWMM raingage properties.
144
+
145
+ :ivar GAGE_TOTAL_PRECIPITATION: Total precipitation
146
+ :type GAGE_TOTAL_PRECIPITATION: int
147
+ :ivar GAGE_RAINFALL: Rainfall
148
+ :type GAGE_RAINFALL: int
149
+ :ivar GAGE_SNOWFALL: Snowfall
150
+ :type GAGE_SNOWFALL: int
151
+ """
152
+ GAGE_TOTAL_PRECIPITATION = swmm_GageProperty.swmm_GAGE_TOTAL_PRECIPITATION # Total precipitation
153
+ GAGE_RAINFALL = swmm_GageProperty.swmm_GAGE_RAINFALL # Rainfall
154
+ GAGE_SNOWFALL = swmm_GageProperty.swmm_GAGE_SNOWFALL # Snowfall
155
+
156
+ class SWMMSubcatchmentProperties(Enum):
157
+ """
158
+ Enumeration of SWMM subcatchment properties.
159
+
160
+ :ivar AREA: Area
161
+ :type AREA: int
162
+ :ivar RAINGAGE: Raingage
163
+ :type RAINGAGE: int
164
+ :ivar RAINFALL: Rainfall
165
+ :type RAINFALL: int
166
+ :ivar EVAPORATION: Evaporation
167
+ :type EVAPORATION: int
168
+ :ivar INFILTRATION: Infiltration
169
+ :type INFILTRATION: int
170
+ :ivar RUNOFF: Runoff
171
+ :type RUNOFF: int
172
+ :ivar REPORT_FLAG: Report flag
173
+ :type REPORT_FLAG: int
174
+ :ivar WIDTH: Width
175
+ :type WIDTH: int
176
+ :ivar SLOPE: Slope
177
+ :type SLOPE: int
178
+ :ivar CURB_LENGTH: Curb length
179
+ :type CURB_LENGTH: int
180
+ :ivar API_RAINFALL: API Rainfall
181
+ :type API_RAINFALL: int
182
+ :ivar API_SNOWFALL: API Snowfall
183
+ :type API_SNOWFALL: int
184
+ :ivar POLLUTANT_BUILDUP: Pollutant buildup
185
+ :type POLLUTANT_BUILDUP: int
186
+ :ivar EXTERNAL_POLLUTANT_BUILDUP: External pollutant buildup
187
+ :type EXTERNAL_POLLUTANT_BUILDUP: int
188
+ :ivar POLLUTANT_RUNOFF_CONCENTRATION: Pollutant runoff concentration
189
+ :type POLLUTANT_RUNOFF_CONCENTRATION: int
190
+ :ivar POLLUTANT_PONDED_CONCENTRATION: Pollutant ponded concentration
191
+ :type POLLUTANT_PONDED_CONCENTRATION: int
192
+ :ivar POLLUTANT_TOTAL_LOAD: Pollutant total load
193
+ :type POLLUTANT_TOTAL_LOAD: int
194
+ """
195
+ AREA = swmm_SubcatchProperty.swmm_SUBCATCH_AREA
196
+ RAINGAGE = swmm_SubcatchProperty.swmm_SUBCATCH_RAINGAGE
197
+ RAINFALL = swmm_SubcatchProperty.swmm_SUBCATCH_RAINFALL
198
+ EVAPORATION = swmm_SubcatchProperty.swmm_SUBCATCH_EVAP
199
+ INFILTRATION = swmm_SubcatchProperty.swmm_SUBCATCH_INFIL
200
+ RUNOFF = swmm_SubcatchProperty.swmm_SUBCATCH_RUNOFF
201
+ REPORT_FLAG = swmm_SubcatchProperty.swmm_SUBCATCH_RPTFLAG
202
+ WIDTH = swmm_SubcatchProperty.swmm_SUBCATCH_WIDTH
203
+ SLOPE = swmm_SubcatchProperty.swmm_SUBCATCH_SLOPE
204
+ CURB_LENGTH = swmm_SubcatchProperty.swmm_SUBCATCH_CURB_LENGTH
205
+ API_RAINFALL = swmm_SubcatchProperty.swmm_SUBCATCH_API_RAINFALL
206
+ API_SNOWFALL = swmm_SubcatchProperty.swmm_SUBCATCH_API_SNOWFALL
207
+ POLLUTANT_BUILDUP = swmm_SubcatchProperty.swmm_SUBCATCH_POLLUTANT_BUILDUP
208
+ EXTERNAL_POLLUTANT_BUILDUP = swmm_SubcatchProperty.swmm_SUBCATCH_EXTERNAL_POLLUTANT_BUILDUP
209
+ POLLUTANT_RUNOFF_CONCENTRATION = swmm_SubcatchProperty.swmm_SUBCATCH_POLLUTANT_RUNOFF_CONCENTRATION
210
+ POLLUTANT_PONDED_CONCENTRATION = swmm_SubcatchProperty.swmm_SUBCATCH_POLLUTANT_PONDED_CONCENTRATION
211
+ POLLUTANT_TOTAL_LOAD = swmm_SubcatchProperty.swmm_SUBCATCH_POLLUTANT_TOTAL_LOAD
212
+
213
+ class SWMMNodeProperties(Enum):
214
+ """
215
+ Enumeration of SWMM node properties.
216
+
217
+ :ivar TYPE: Node type
218
+ :type TYPE: int
219
+ :ivar INVERT_ELEVATION: Invert elevation
220
+ :type INVERT_ELEVATION: int
221
+ :ivar MAX_DEPTH: Maximum depth
222
+ :type MAX_DEPTH: int
223
+ :ivar DEPTH: Depth
224
+ :type DEPTH: int
225
+ :ivar HYDRAULIC_HEAD: Hydraulic head
226
+ :type HYDRAULIC_HEAD: int
227
+ :ivar VOLUME: Volume
228
+ :type VOLUME: int
229
+ :ivar LATERAL_INFLOW: Lateral inflow
230
+ :type LATERAL_INFLOW: int
231
+ :ivar TOTAL_INFLOW: Total inflow
232
+ :type TOTAL_INFLOW: int
233
+ :ivar FLOODING: Flooding
234
+ :type FLOODING: int
235
+ :ivar REPORT_FLAG: Report flag
236
+ :type REPORT_FLAG: int
237
+ :ivar SURCHARGE_DEPTH: Surcharge depth
238
+ :type SURCHARGE_DEPTH: int
239
+ :ivar PONDING_AREA: Ponding area
240
+ :type PONDING_AREA: int
241
+ :ivar INITIAL_DEPTH: Initial depth
242
+ :type INITIAL_DEPTH: int
243
+ :ivar POLLUTANT_CONCENTRATION: Pollutant concentration
244
+ :type POLLUTANT_CONCENTRATION: int
245
+ :ivar POLLUTANT_LATERAL_MASS_FLUX: Pollutant lateral mass flux
246
+ :type POLLUTANT_LATERAL_MASS_FLUX: int
247
+ """
248
+ TYPE = swmm_NodeProperty.swmm_NODE_TYPE
249
+ INVERT_ELEVATION = swmm_NodeProperty.swmm_NODE_ELEV
250
+ MAX_DEPTH = swmm_NodeProperty.swmm_NODE_MAXDEPTH
251
+ DEPTH = swmm_NodeProperty.swmm_NODE_DEPTH
252
+ HYDRAULIC_HEAD = swmm_NodeProperty.swmm_NODE_HEAD
253
+ VOLUME = swmm_NodeProperty.swmm_NODE_VOLUME
254
+ LATERAL_INFLOW = swmm_NodeProperty.swmm_NODE_LATFLOW
255
+ TOTAL_INFLOW = swmm_NodeProperty.swmm_NODE_INFLOW
256
+ FLOODING = swmm_NodeProperty.swmm_NODE_OVERFLOW
257
+ REPORT_FLAG = swmm_NodeProperty.swmm_NODE_RPTFLAG
258
+ SURCHARGE_DEPTH = swmm_NodeProperty.swmm_NODE_SURCHARGE_DEPTH
259
+ PONDING_AREA = swmm_NodeProperty.swmm_NODE_PONDED_AREA
260
+ INITIAL_DEPTH = swmm_NodeProperty.swmm_NODE_INITIAL_DEPTH
261
+ POLLUTANT_CONCENTRATION = swmm_NodeProperty.swmm_NODE_POLLUTANT_CONCENTRATION # Pollutant concentration
262
+ POLLUTANT_LATERAL_MASS_FLUX = swmm_NodeProperty.swmm_NODE_POLLUTANT_LATMASS_FLUX # Pollutant inflow concentration
263
+
264
+ class SWMMLinkProperties(Enum):
265
+ """
266
+ Enumeration of SWMM link properties.
267
+
268
+ :ivar TYPE: Link type
269
+ :type TYPE: int
270
+ :ivar START_NODE: Start node
271
+ :type START_NODE: int
272
+ :ivar END_NODE: End node
273
+ :type END_NODE: int
274
+ :ivar LENGTH: Length
275
+ :type LENGTH: int
276
+ :ivar SLOPE: Slope
277
+ :type SLOPE: int
278
+ :ivar FULL_DEPTH: Full depth
279
+ :type FULL_DEPTH: int
280
+ :ivar FULL_FLOW: Full flow
281
+ :type FULL_FLOW: int
282
+ :ivar SETTING: Setting
283
+ :type SETTING: int
284
+ :ivar TIME_OPEN: Time open
285
+ :type TIME_OPEN: int
286
+ :ivar TIME_CLOSED: Time closed
287
+ :type TIME_CLOSED: int
288
+ :ivar FLOW: Flow
289
+ :type FLOW: int
290
+ :ivar DEPTH: Depth
291
+ :type DEPTH: int
292
+ :ivar VELOCITY: Velocity
293
+ :type VELOCITY: int
294
+ :ivar TOP_WIDTH: Top width
295
+ :type TOP_WIDTH: int
296
+ :ivar REPORT_FLAG: Report flag
297
+ :type REPORT_FLAG: int
298
+ :ivar START_NODE_OFFSET: Start node offset
299
+ :type START_NODE_OFFSET: int
300
+ :ivar END_NODE_OFFSET: End node offset
301
+ :type END_NODE_OFFSET: int
302
+ :ivar INITIAL_FLOW: Initial flow
303
+ :type INITIAL_FLOW: int
304
+ :ivar FLOW_LIMIT: Flow limit
305
+ :type FLOW_LIMIT: int
306
+ :ivar INLET_LOSS: Inlet loss
307
+ :type INLET_LOSS: int
308
+ :ivar OUTLET_LOSS: Outlet loss
309
+ :type OUTLET_LOSS: int
310
+ :ivar AVERAGE_LOSS: Average loss
311
+ :type AVERAGE_LOSS: int
312
+ :ivar SEEPAGE_RATE: Seepage rate
313
+ :type SEEPAGE_RATE: int
314
+ :ivar HAS_FLAPGATE: Has flapgate
315
+ :type HAS_FLAPGATE: int
316
+ :ivar POLLUTANT_CONCENTRATION: Pollutant concentration
317
+ :type POLLUTANT_CONCENTRATION: int
318
+ :ivar POLLUTANT_LOAD: Pollutant load
319
+ :type POLLUTANT_LOAD: int
320
+ :ivar POLLUTANT_LATERAL_MASS_FLUX: Pollutant lateral mass flux
321
+ :type POLLUTANT_LATERAL_MASS_FLUX: int
322
+ """
323
+ TYPE = swmm_LinkProperty.swmm_LINK_TYPE
324
+ START_NODE = swmm_LinkProperty.swmm_LINK_NODE1
325
+ END_NODE = swmm_LinkProperty.swmm_LINK_NODE2
326
+ LENGTH = swmm_LinkProperty.swmm_LINK_LENGTH
327
+ SLOPE = swmm_LinkProperty.swmm_LINK_SLOPE
328
+ FULL_DEPTH = swmm_LinkProperty.swmm_LINK_FULLDEPTH
329
+ FULL_FLOW = swmm_LinkProperty.swmm_LINK_FULLFLOW
330
+ SETTING = swmm_LinkProperty.swmm_LINK_SETTING
331
+ TIME_OPEN = swmm_LinkProperty.swmm_LINK_TIMEOPEN
332
+ TIME_CLOSED = swmm_LinkProperty.swmm_LINK_TIMECLOSED
333
+ FLOW = swmm_LinkProperty.swmm_LINK_FLOW
334
+ DEPTH = swmm_LinkProperty.swmm_LINK_DEPTH
335
+ VELOCITY = swmm_LinkProperty.swmm_LINK_VELOCITY
336
+ TOP_WIDTH = swmm_LinkProperty.swmm_LINK_TOPWIDTH
337
+ VOLUME = swmm_LinkProperty.swmm_LINK_VOLUME
338
+ CAPACITY = swmm_LinkProperty.swmm_LINK_CAPACITY
339
+ REPORT_FLAG = swmm_LinkProperty.swmm_LINK_RPTFLAG
340
+ START_NODE_OFFSET = swmm_LinkProperty.swmm_LINK_OFFSET1
341
+ END_NODE_OFFSET = swmm_LinkProperty.swmm_LINK_OFFSET2
342
+ INITIAL_FLOW = swmm_LinkProperty.swmm_LINK_INITIAL_FLOW
343
+ FLOW_LIMIT = swmm_LinkProperty.swmm_LINK_FLOW_LIMIT
344
+ INLET_LOSS = swmm_LinkProperty.swmm_LINK_INLET_LOSS
345
+ OUTLET_LOSS = swmm_LinkProperty.swmm_LINK_OUTLET_LOSS
346
+ AVERAGE_LOSS = swmm_LinkProperty.swmm_LINK_AVERAGE_LOSS
347
+ SEEPAGE_RATE = swmm_LinkProperty.swmm_LINK_SEEPAGE_RATE
348
+ HAS_FLAPGATE = swmm_LinkProperty.swmm_LINK_HAS_FLAPGATE
349
+ POLLUTANT_CONCENTRATION = swmm_LinkProperty.swmm_LINK_POLLUTANT_CONCENTRATION # Pollutant concentration
350
+ POLLUTANT_LOAD = swmm_LinkProperty.swmm_LINK_POLLUTANT_LOAD # Pollutant load
351
+ POLLUTANT_LATERAL_MASS_FLUX = swmm_LinkProperty.swmm_LINK_POLLUTANT_LATMASS_FLUX # Pollutant lateral mass flux
352
+
353
+ class SWMMLinkTypes(Enum):
354
+ """
355
+ Enumeration of SWMM link types.
356
+
357
+ :ivar CONDUIT: Conduit link
358
+ :type CONDUIT: int
359
+ :ivar PUMP: Pump link
360
+ :type PUMP: int
361
+ :ivar ORIFICE: Orifice link
362
+ :type ORIFICE: int
363
+ :ivar WEIR: Weir link
364
+ :type WEIR: int
365
+ :ivar OUTLET: Outlet link
366
+ :type OUTLET: int
367
+ """
368
+ CONDUIT = swmm_LinkType.swmm_CONDUIT
369
+ PUMP = swmm_LinkType.swmm_PUMP
370
+ ORIFICE = swmm_LinkType.swmm_ORIFICE
371
+ WEIR = swmm_LinkType.swmm_WEIR
372
+ OUTLET = swmm_LinkType.swmm_OUTLET
373
+
374
+ class SWMMSystemProperties(Enum):
375
+ """
376
+ Enumeration of SWMM system properties.
377
+
378
+ :ivar START_DATE: Start date for the simulation
379
+ :type START_DATE: int
380
+ :ivar CURRENT_DATE: Current date for the simulation
381
+ :type CURRENT_DATE: int
382
+ :ivar ELAPSED_TIME: Elapsed time for the simulation
383
+ :type ELAPSED_TIME: int
384
+ :ivar ROUTING_STEP: Routing time step
385
+ :type ROUTING_STEP: int
386
+ :ivar MAX_ROUTING_STEP: Maximum routing time step
387
+ :type MAX_ROUTING_STEP: int
388
+ :ivar REPORT_STEP: Report time step
389
+ :type REPORT_STEP: int
390
+ :ivar TOTAL_STEPS: Total number of steps
391
+ :type TOTAL_STEPS: int
392
+ :ivar NO_REPORT_FLAG: No report flag
393
+ :type NO_REPORT_FLAG: int
394
+ :ivar FLOW_UNITS: Flow units
395
+ :type FLOW_UNITS: int
396
+ :ivar END_DATE: End date for the simulation
397
+ :type END_DATE: int
398
+ :ivar REPORT_START_DATE: Report start date
399
+ :type REPORT_START_DATE: int
400
+ :ivar UNIT_SYSTEM: Unit system
401
+ :type UNIT_SYSTEM: int
402
+ :ivar SURCHARGE_METHOD: Surcharge method
403
+ :type SURCHARGE_METHOD: int
404
+ :ivar ALLOW_PONDING: Allow ponding
405
+ :type ALLOW_PONDING: int
406
+ :ivar INTERTIAL_DAMPING: Inertial damping
407
+ :type INTERTIAL_DAMPING: int
408
+ :ivar NORMAL_FLOW_LIMITED: Normal flow limited
409
+ :type NORMAL_FLOW_LIMITED: int
410
+ :ivar SKIP_STEADY_STATE: Skip steady state
411
+ :type SKIP_STEADY_STATE: int
412
+ :ivar IGNORE_RAINFALL: Ignore rainfall
413
+ :type IGNORE_RAINFALL: int
414
+ :ivar IGNORE_RDII: Ignore RDII
415
+ :type IGNORE_RDII: int
416
+ :ivar IGNORE_SNOWMELT: Ignore snowmelt
417
+ :type IGNORE_SNOWMELT: int
418
+ :ivar IGNORE_GROUNDWATER: Ignore groundwater
419
+ :type IGNORE_GROUNDWATER: int
420
+ :ivar IGNORE_ROUTING: Ignore routing
421
+ :type IGNORE_ROUTING: int
422
+ :ivar IGNORE_QUALITY: Ignore quality
423
+ :type IGNORE_QUALITY: int
424
+ :ivar RULE_STEP: Rule step
425
+ :type RULE_STEP: int
426
+ :ivar SWEEP_START: Sweep start
427
+ :type SWEEP_START: int
428
+ :ivar SWEEP_END: Sweep end
429
+ :type SWEEP_END: int
430
+ :ivar MAX_TRIALS: Maximum trials
431
+ :type MAX_TRIALS: int
432
+ :ivar NUM_THREADS: Number of threads
433
+ :type NUM_THREADS: int
434
+ :ivar MIN_ROUTE_STEP: Minimum routing step
435
+ :type MIN_ROUTE_STEP: int
436
+ :ivar LENGTHENING_STEP: Lengthening step
437
+ :type LENGTHENING_STEP: int
438
+ :ivar START_DRY_DAYS: Start dry days
439
+ :type START_DRY_DAYS: int
440
+ :ivar COURANT_FACTOR: Courant factor
441
+ :type COURANT_FACTOR: int
442
+ :ivar MIN_SURF_AREA: Minimum surface area
443
+ :type MIN_SURF_AREA: int
444
+ :ivar MIN_SLOPE: Minimum slope
445
+ :type MIN_SLOPE: int
446
+ :ivar RUNOFF_ERROR: Runoff error
447
+ :type RUNOFF_ERROR: int
448
+ :ivar FLOW_ERROR: Flow error
449
+ :type FLOW_ERROR: int
450
+ :ivar QUAL_ERROR: Quality error
451
+ :type QUAL_ERROR: int
452
+ :ivar HEAD_TOL: Head tolerance
453
+ :type HEAD_TOL: int
454
+ :ivar SYS_FLOW_TOL: System flow tolerance
455
+ :type SYS_FLOW_TOL: int
456
+ :ivar LAT_FLOW_TOL: Lateral flow tolerance
457
+ :type LAT_FLOW_TOL: int
458
+
459
+ """
460
+ START_DATE = swmm_SystemProperty.swmm_STARTDATE
461
+ CURRENT_DATE = swmm_SystemProperty.swmm_CURRENTDATE
462
+ ELAPSED_TIME = swmm_SystemProperty.swmm_ELAPSEDTIME
463
+ ROUTING_STEP = swmm_SystemProperty.swmm_ROUTESTEP
464
+ MAX_ROUTING_STEP = swmm_SystemProperty.swmm_MAXROUTESTEP
465
+ REPORT_STEP = swmm_SystemProperty.swmm_REPORTSTEP
466
+ TOTAL_STEPS = swmm_SystemProperty.swmm_TOTALSTEPS
467
+ NO_REPORT_FLAG = swmm_SystemProperty.swmm_NOREPORT
468
+ FLOW_UNITS = swmm_SystemProperty.swmm_FLOWUNITS
469
+ END_DATE = swmm_SystemProperty.swmm_ENDDATE
470
+ REPORT_START_DATE = swmm_SystemProperty.swmm_REPORTSTART
471
+ UNIT_SYSTEM = swmm_SystemProperty.swmm_UNITSYSTEM
472
+ SURCHARGE_METHOD = swmm_SystemProperty.swmm_SURCHARGEMETHOD
473
+ ALLOW_PONDING = swmm_SystemProperty.swmm_ALLOWPONDING
474
+ INTERTIAL_DAMPING = swmm_SystemProperty.swmm_INERTIADAMPING
475
+ NORMAL_FLOW_LIMITED = swmm_SystemProperty.swmm_NORMALFLOWLTD
476
+ SKIP_STEADY_STATE = swmm_SystemProperty.swmm_SKIPSTEADYSTATE
477
+ IGNORE_RAINFALL = swmm_SystemProperty.swmm_IGNORERAINFALL
478
+ IGNORE_RDII = swmm_SystemProperty.swmm_IGNORERDII
479
+ IGNORE_SNOWMELT = swmm_SystemProperty.swmm_IGNORESNOWMELT
480
+ IGNORE_GROUNDWATER = swmm_SystemProperty.swmm_IGNOREGROUNDWATER
481
+ IGNORE_ROUTING = swmm_SystemProperty.swmm_IGNOREROUTING
482
+ IGNORE_QUALITY = swmm_SystemProperty.swmm_IGNOREQUALITY
483
+ ERROR_CODE = swmm_SystemProperty.swmm_ERROR_CODE
484
+ RULE_STEP = swmm_SystemProperty.swmm_RULESTEP
485
+ SWEEP_START = swmm_SystemProperty.swmm_SWEEPSTART
486
+ SWEEP_END = swmm_SystemProperty.swmm_SWEEPEND
487
+ MAX_TRIALS = swmm_SystemProperty.swmm_MAXTRIALS
488
+ NUM_THREADS = swmm_SystemProperty.swmm_NUMTHREADS
489
+ MIN_ROUTE_STEP = swmm_SystemProperty.swmm_MINROUTESTEP
490
+ LENGTHENING_STEP = swmm_SystemProperty.swmm_LENGTHENINGSTEP
491
+ START_DRY_DAYS = swmm_SystemProperty.swmm_STARTDRYDAYS
492
+ COURANT_FACTOR = swmm_SystemProperty.swmm_COURANTFACTOR
493
+ MIN_SURF_AREA = swmm_SystemProperty.swmm_MINSURFAREA
494
+ MIN_SLOPE = swmm_SystemProperty.swmm_MINSLOPE
495
+ RUNOFF_ERROR = swmm_SystemProperty.swmm_RUNOFFERROR
496
+ FLOW_ERROR = swmm_SystemProperty.swmm_FLOWERROR
497
+ QUAL_ERROR = swmm_SystemProperty.swmm_QUALERROR
498
+ HEAD_TOL = swmm_SystemProperty.swmm_HEADTOL
499
+ SYS_FLOW_TOL = swmm_SystemProperty.swmm_SYSFLOWTOL
500
+ LAT_FLOW_TOL = swmm_SystemProperty.swmm_LATFLOWTOL
501
+
502
+ class SWMMFlowUnits(Enum):
503
+ """
504
+ Enumeration of SWMM flow units.
505
+
506
+ :ivar CFS: Cubic feet per second
507
+ :type CFS: int
508
+ :ivar GPM: Gallons per minute
509
+ :type GPM: int
510
+ :ivar MGD: Million gallons per day
511
+ :type MGD: int
512
+ :ivar CMS: Cubic meters per second
513
+ :type CMS: int
514
+ :ivar LPS: Liters per second
515
+ :type LPS: int
516
+ :ivar MLD: Million liters per day
517
+ :type MLD: int
518
+ """
519
+ CFS = swmm_FlowUnitsProperty.swmm_CFS
520
+ GPM = swmm_FlowUnitsProperty.swmm_GPM
521
+ MGD = swmm_FlowUnitsProperty.swmm_MGD
522
+ CMS = swmm_FlowUnitsProperty.swmm_CMS
523
+ LPS = swmm_FlowUnitsProperty.swmm_LPS
524
+ MLD = swmm_FlowUnitsProperty.swmm_MLD
525
+
526
+ class SWMMAPIErrors(Enum):
527
+ """
528
+ Enumeration of SWMM API errors.
529
+
530
+ :ivar PROJECT_NOT_OPENED: Project not opened
531
+ :type PROJECT_NOT_OPENED: int
532
+ :ivar SIMULATION_NOT_STARTED: Simulation not started
533
+ :type SIMULATION_NOT_STARTED: int
534
+ :ivar SIMULATION_NOT_ENDED: Simulation not ended
535
+ :type SIMULATION_NOT_ENDED: int
536
+ """
537
+ PROJECT_NOT_OPENED = swmm_API_Errors.ERR_API_NOT_OPEN # API not open
538
+ SIMULATION_NOT_STARTED = swmm_API_Errors.ERR_API_NOT_STARTED # API not started
539
+ SIMULATION_NOT_ENDED = swmm_API_Errors.ERR_API_NOT_ENDED # API not ended
540
+ OBJECT_TYPE = swmm_API_Errors.ERR_API_OBJECT_TYPE # Invalid object type
541
+ OBJECT_INDEX = swmm_API_Errors.ERR_API_OBJECT_INDEX # Invalid object index
542
+ OBJECT_NAME = swmm_API_Errors.ERR_API_OBJECT_NAME # Invalid object name
543
+ PROPERTY_TYPE = swmm_API_Errors.ERR_API_PROPERTY_TYPE # Invalid property type
544
+ PROPERTY_VALUE = swmm_API_Errors.ERR_API_PROPERTY_VALUE # Invalid property value
545
+ TIME_PERIOD = swmm_API_Errors.ERR_API_TIME_PERIOD # Invalid time period
546
+ HOTSTART_FILE_OPEN = swmm_API_Errors.ERR_API_HOTSTART_FILE_OPEN # Error opening hotstart file
547
+ HOTSTART_FILE_FORMAT = swmm_API_Errors.ERR_API_HOTSTART_FILE_FORMAT # Invalid hotstart file format
548
+ SIMULATION_IS_RUNNING = swmm_API_Errors.ERR_API_IS_RUNNING # Simulation is running
549
+
550
+ cdef void c_wrapper_function(double x):
551
+ """
552
+ Wrapper function to call a Python function.
553
+
554
+ :param x: Input value
555
+ :type x: double
556
+ """
557
+ global py_progress_callback
558
+ cdef tuple args = (x,)
559
+ PyObject_CallObject(py_progress_callback, args)
560
+
561
+ cdef progress_callback wrap_python_function_as_callback(object py_func):
562
+ """
563
+ Wrap a Python function as a callback.
564
+
565
+ :param py_func: Python function
566
+ :type py_func: callable
567
+ :return: Callback function
568
+ :rtype: progress_callback
569
+ """
570
+ global py_progress_callback
571
+ py_progress_callback = py_func
572
+ return <progress_callback>c_wrapper_function
573
+
574
+ cdef object global_solver = None
575
+
576
+ cdef void progress_callback_wrapper(double progress):
577
+ """
578
+ Wrapper function to call the instance method.
579
+
580
+ :param progress: Progress percentage
581
+ :type progress: double
582
+
583
+ """
584
+ global solver_instance
585
+
586
+ if solver_instance is not None:
587
+ solver_instance.__progress_callback(progress)
588
+
589
+ def run_solver(
590
+ inp_file: str,
591
+ rpt_file: str = None,
592
+ out_file: str = None,
593
+ swmm_progress_callback: Callable[[float], None] = None
594
+ ) -> int:
595
+ """
596
+ Run a SWMM simulation with a progress callback.
597
+
598
+ :param inp_file: Input file name
599
+ :rtype inp_file: str
600
+ :param rpt_file: Report file name
601
+ :rtype rpt_file: str
602
+ :param out_file: Output file name
603
+ :rtype out_file: str
604
+ :param swmm_progress_callback: Progress callback function
605
+ :type swmm_progress_callback: callable
606
+ :return: Error code (0 if successful)
607
+ """
608
+ cdef int error_code = 0
609
+ cdef bytes c_inp_file_bytes = inp_file.encode('utf-8')
610
+ cdef progress_callback c_swm_progress_callback
611
+
612
+ if rpt_file is not None:
613
+ rpt_file = inp_file.replace('.inp', '.rpt')
614
+
615
+ if out_file is not None:
616
+ out_file = inp_file.replace('.inp', '.out')
617
+
618
+ cdef bytes c_rpt_file_bytes = rpt_file.encode('utf-8')
619
+ cdef bytes c_out_file_bytes = out_file.encode('utf-8')
620
+
621
+ cdef const char* c_inp_file = c_inp_file_bytes
622
+ cdef const char* c_rpt_file = c_rpt_file_bytes
623
+ cdef const char* c_out_file = c_out_file_bytes
624
+
625
+ if swmm_progress_callback is not None:
626
+ c_swm_progress_callback = <progress_callback>wrap_python_function_as_callback(swmm_progress_callback)
627
+ error_code = swmm_run_with_callback(c_inp_file, c_rpt_file, c_out_file, c_swm_progress_callback)
628
+ else:
629
+ error_code = swmm_run(c_inp_file, c_rpt_file, c_out_file)
630
+
631
+ if error_code != 0:
632
+ raise SWMMSolverException(f'Run failed with message: {get_error_message(error_code)}')
633
+
634
+ return error_code
635
+
636
+ cpdef cython_datetime decode_swmm_datetime(double swmm_datetime):
637
+ """
638
+ Decode a SWMM datetime into a datetime object.
639
+
640
+ :param swmm_datetime: SWMM datetime float value
641
+ :type swmm_datetime: float
642
+
643
+ :return: datetime object
644
+ :rtype: datetime
645
+ """
646
+ cdef int year, month, day, hour, minute, second, day_of_week
647
+ swmm_decodeDate(swmm_datetime, &year, &month, &day, &hour, &minute, &second, &day_of_week)
648
+
649
+ return datetime(year, month, day, hour, minute, second)
650
+
651
+ cpdef double encode_swmm_datetime(cython_datetime dt):
652
+ """
653
+ Encode a datetime object into a SWMM datetime float value.
654
+
655
+ :param dt: datetime object
656
+ :type dt: datetime
657
+ :return: SWMM datetime float value
658
+ :rtype: float
659
+ """
660
+ cdef int year = dt.year
661
+ cdef int month = dt.month
662
+ cdef int day = dt.day
663
+ cdef int hour = dt.hour
664
+ cdef int minute = dt.minute
665
+ cdef int second = dt.second
666
+
667
+ return swmm_encodeDate(year, month, day, hour, minute, second)
668
+
669
+ cpdef int version():
670
+ """
671
+ Get the SWMM version.
672
+
673
+ :return: SWMM version
674
+ :rtype: str
675
+ """
676
+ cdef int swmm_version = swmm_getVersion()
677
+
678
+ return swmm_version
679
+
680
+ cpdef str get_error_message(int error_code):
681
+ """
682
+ Get the error message for a SWMM error code.
683
+
684
+ :param error_code: Error code
685
+ :type error_code: int
686
+ :return: Error message
687
+ :rtype: str
688
+ """
689
+ cdef char* c_error_message = <char*>malloc(1024*sizeof(char))
690
+
691
+ swmm_getErrorFromCode(error_code, &c_error_message)
692
+
693
+ error_message = c_error_message.decode('utf-8')
694
+
695
+ free(c_error_message)
696
+
697
+ return error_message
698
+
699
+ class SolverState(Enum):
700
+ """
701
+ An enumeration to represent the state of the solver.
702
+ """
703
+ CREATED = 0
704
+ OPEN = 1
705
+ STARTED = 2
706
+ FINISHED = 3
707
+ ENDED = 4
708
+ REPORTED = 5
709
+ CLOSED = 6
710
+
711
+ class CallbackType(Enum):
712
+ """
713
+ An enumeration to represent the type of callback.
714
+ """
715
+ BEFORE_INITIALIZE = 0
716
+ BEFORE_OPEN = 1
717
+ AFTER_OPEN = 2
718
+ BEFORE_START = 3
719
+ AFTER_START = 4
720
+ BEFORE_STEP = 5
721
+ AFTER_STEP = 6
722
+ BEFORE_END = 7
723
+ AFTER_END = 8
724
+ BEFORE_REPORT = 9
725
+ AFTER_REPORT = 10
726
+ BEFORE_CLOSE = 11
727
+ AFTER_CLOSE = 12
728
+
729
+ class SWMMSolverException(Exception):
730
+ """
731
+ Exception class for SWMM output file processing errors.
732
+ """
733
+ def __init__(self, message: str) -> None:
734
+ """
735
+ Constructor to initialize the exception message.
736
+
737
+ :param message: Error message.
738
+ :type message: str
739
+ """
740
+ super().__init__(message)
741
+
742
+ cdef class Solver:
743
+ """
744
+ A class to represent a SWMM solver.
745
+ """
746
+ cdef str _inp_file
747
+ cdef str _rpt_file
748
+ cdef str _out_file
749
+ cdef bint _save_results
750
+ cdef int _stride_step
751
+ cdef dict _callbacks
752
+ cdef int _progress_callbacks_per_second
753
+ cdef list _progress_callbacks
754
+ cdef clock_t _clock
755
+ cdef double _total_duration
756
+ cdef object _solver_state
757
+ cdef object _partial_step_function
758
+
759
+ def __cinit__(
760
+ self,
761
+ str inp_file,
762
+ str rpt_file = None,
763
+ str out_file = None,
764
+ int stride_step = 300,
765
+ bint save_results=True
766
+ ):
767
+ """
768
+ Constructor to create a new SWMM solver.
769
+
770
+ :param inp_file: Input file name
771
+ :param rpt_file: Report file name
772
+ :param out_file: Output file name
773
+ """
774
+ global global_solver
775
+ self._save_results = save_results
776
+ self._inp_file = inp_file
777
+ self._progress_callbacks_per_second = 2
778
+ self._stride_step = stride_step
779
+ self._clock = clock()
780
+ global_solver = self
781
+
782
+ if rpt_file is not None:
783
+ self._rpt_file = rpt_file
784
+ else:
785
+ self._rpt_file = inp_file.replace('.inp', '.rpt')
786
+
787
+ if out_file is not None:
788
+ self._out_file = out_file
789
+ else:
790
+ self._out_file = inp_file.replace('.inp', '.out')
791
+
792
+ self._callbacks = {
793
+ CallbackType.BEFORE_INITIALIZE: [],
794
+ CallbackType.BEFORE_OPEN: [],
795
+ CallbackType.AFTER_OPEN: [],
796
+ CallbackType.BEFORE_START: [],
797
+ CallbackType.AFTER_START: [],
798
+ CallbackType.BEFORE_STEP: [],
799
+ CallbackType.AFTER_STEP: [],
800
+ CallbackType.BEFORE_END: [],
801
+ CallbackType.AFTER_END: [],
802
+ CallbackType.BEFORE_REPORT: [],
803
+ CallbackType.AFTER_REPORT: [],
804
+ CallbackType.BEFORE_CLOSE: [],
805
+ CallbackType.AFTER_CLOSE: []
806
+ }
807
+
808
+ self._progress_callbacks = []
809
+
810
+ self._solver_state = SolverState.CREATED
811
+
812
+ def __enter__(self):
813
+ """
814
+ Enter method for context manager.
815
+ """
816
+ self.__execute_callbacks(CallbackType.BEFORE_INITIALIZE)
817
+ self.open()
818
+ return self
819
+
820
+ def __exit__(self, exc_type, exc_value, traceback):
821
+ """
822
+ Exit method for context manager.
823
+ """
824
+ self.finalize()
825
+
826
+ def __dealloc__(self):
827
+ """
828
+ Destructor to free the solver.
829
+ """
830
+ self.finalize()
831
+
832
+ def __iter__(self):
833
+ """
834
+ Iterator method for the solver.
835
+ """
836
+ return self
837
+
838
+ def __next__(self):
839
+ """
840
+ Next method for the solver.
841
+ """
842
+ if self._solver_state == SolverState.FINISHED:
843
+ raise StopIteration
844
+ else:
845
+ return self.step()
846
+
847
+ @property
848
+ def start_datetime(self) -> datetime:
849
+ """
850
+ Get the start date of the simulation.
851
+
852
+ :return: Start date
853
+ :rtype: datetime
854
+ """
855
+ cdef double start_date = swmm_getValueExpanded(
856
+ objType=SWMMObjects.SYSTEM.value,
857
+ property=SWMMSystemProperties.START_DATE.value,
858
+ index=0,
859
+ subIndex=0,
860
+ pollutantIndex=0
861
+ )
862
+
863
+ return decode_swmm_datetime(start_date)
864
+
865
+ @start_datetime.setter
866
+ def start_datetime(self, sim_start_datetime: datetime) -> None:
867
+ """
868
+ Initialize the solver.
869
+
870
+ :param sim_start_datetime: Start date of the simulation
871
+ :return: Error code (0 if successful)
872
+ """
873
+ cdef double start_date = encode_swmm_datetime(dt=sim_start_datetime)
874
+ cdef int error_code = swmm_setValueExpanded(
875
+ objType=SWMMObjects.SYSTEM.value,
876
+ property=SWMMSystemProperties.START_DATE.value,
877
+ index=0,
878
+ subindex=0,
879
+ pollutantIndex=0,
880
+ value=start_date
881
+ )
882
+
883
+ self.__validate_error(error_code)
884
+
885
+ @property
886
+ def end_datetime(self) -> datetime:
887
+ """
888
+ Get the end date of the simulation.
889
+
890
+ :return: End date
891
+ :rtype: datetime
892
+ """
893
+ cdef double end_date = swmm_getValueExpanded(
894
+ objType=SWMMObjects.SYSTEM.value,
895
+ property=SWMMSystemProperties.END_DATE.value,
896
+ index=0,
897
+ subIndex=0,
898
+ pollutantIndex=0
899
+ )
900
+
901
+ return decode_swmm_datetime(end_date)
902
+
903
+ @end_datetime.setter
904
+ def end_datetime(self, sim_end_datetime: datetime) -> None:
905
+ """
906
+ Set the end date of the simulation.
907
+
908
+ :param sim_end_datetime: End date of the simulation
909
+ :return: Error code (0 if successful)
910
+ """
911
+ cdef double end_date = encode_swmm_datetime(dt=sim_end_datetime)
912
+ cdef int error_code = swmm_setValueExpanded(
913
+ objType=SWMMObjects.SYSTEM.value,
914
+ property=SWMMSystemProperties.END_DATE.value,
915
+ index=0,
916
+ subindex=0,
917
+ value=end_date,
918
+ pollutantIndex=0,
919
+ )
920
+
921
+ self.__validate_error(error_code)
922
+
923
+ @property
924
+ def routing_step(self) -> float:
925
+ """
926
+ Get the routing time step of the simulation in seconds.
927
+
928
+ :return: Routing time step
929
+ :rtype: double
930
+ """
931
+ cdef double routing_step = swmm_getValueExpanded(
932
+ objType=SWMMObjects.SYSTEM.value,
933
+ property=SWMMSystemProperties.ROUTING_STEP.value,
934
+ index=0,
935
+ subIndex=0,
936
+ pollutantIndex=0
937
+ )
938
+
939
+ return routing_step
940
+
941
+ @routing_step.setter
942
+ def routing_step(self, value: float) -> None:
943
+ """
944
+ Set the routing time step of the simulation in seconds.
945
+
946
+ :param value: Routing time step in seconds
947
+ :type value: float
948
+ """
949
+ cdef int error_code = swmm_setValueExpanded(
950
+ objType=SWMMObjects.SYSTEM.value,
951
+ property=SWMMSystemProperties.ROUTING_STEP.value,
952
+ index=0,
953
+ subindex=0,
954
+ pollutantIndex=0,
955
+ value=value
956
+ )
957
+
958
+ self.__validate_error(error_code)
959
+
960
+ @property
961
+ def reporting_step(self) -> float:
962
+ """
963
+ Get the reporting time step of the simulation in seconds.
964
+
965
+ :return: Reporting time step
966
+ :rtype: double
967
+ """
968
+ cdef double reporting_step = swmm_getValueExpanded(
969
+ objType=SWMMObjects.SYSTEM.value,
970
+ property=SWMMSystemProperties.REPORT_STEP.value,
971
+ index=0,
972
+ subIndex=0,
973
+ pollutantIndex=0
974
+ )
975
+
976
+ return reporting_step
977
+
978
+ @reporting_step.setter
979
+ def reporting_step(self, value: float) -> None:
980
+ """
981
+ Set the reporting time step of the simulation in seconds.
982
+
983
+ :param value: Reporting time step in seconds
984
+ :type value: float
985
+ """
986
+ cdef int error_code = swmm_setValueExpanded(
987
+ objType=SWMMObjects.SYSTEM.value,
988
+ property=SWMMSystemProperties.REPORT_STEP.value,
989
+ index=0,
990
+ subindex=0,
991
+ pollutantIndex=0,
992
+ value=value
993
+ )
994
+
995
+ self.__validate_error(error_code)
996
+
997
+ @property
998
+ def report_start_datetime(self) -> datetime:
999
+ """
1000
+ Get the report start date of the simulation.
1001
+
1002
+ :return: Report start date
1003
+ :rtype: datetime
1004
+ """
1005
+ cdef double report_start_date = swmm_getValueExpanded(
1006
+ objType=SWMMObjects.SYSTEM.value,
1007
+ property=SWMMSystemProperties.REPORT_START_DATE.value,
1008
+ index=0,
1009
+ subIndex=0,
1010
+ pollutantIndex=0
1011
+ )
1012
+
1013
+ return decode_swmm_datetime(report_start_date)
1014
+
1015
+ @report_start_datetime.setter
1016
+ def report_start_datetime(self, report_start_datetime: datetime) -> None:
1017
+ """
1018
+ Set the report start date of the simulation.
1019
+
1020
+ :param report_start_datetime: Report start date
1021
+ :type report_start_datetime: datetime
1022
+ """
1023
+ cdef double report_start_date = encode_swmm_datetime(report_start_datetime)
1024
+ cdef int error_code = swmm_setValueExpanded(
1025
+ objType=SWMMObjects.SYSTEM.value,
1026
+ property=SWMMSystemProperties.REPORT_START_DATE.value,
1027
+ index=0,
1028
+ subindex=0,
1029
+ pollutantIndex=0,
1030
+ value=report_start_date
1031
+ )
1032
+
1033
+ self.__validate_error(error_code)
1034
+
1035
+ @property
1036
+ def current_datetime(self) -> datetime:
1037
+ """
1038
+ Get the current date of the simulation.
1039
+
1040
+ :return: Current date
1041
+ :rtype: datetime
1042
+ """
1043
+ cdef double current_date = swmm_getValueExpanded(
1044
+ objType=SWMMObjects.SYSTEM.value,
1045
+ property=SWMMSystemProperties.CURRENT_DATE.value,
1046
+ index=0,
1047
+ subIndex=0,
1048
+ pollutantIndex=0
1049
+ )
1050
+
1051
+ return decode_swmm_datetime(current_date)
1052
+
1053
+ @property
1054
+ def progress_callbacks_per_second(self) -> int:
1055
+ """
1056
+ Get the number of progress callbacks per second.
1057
+
1058
+ :return: Progress callbacks per second
1059
+ :rtype: int
1060
+ """
1061
+ return self._progress_callbacks_per_second
1062
+
1063
+ @progress_callbacks_per_second.setter
1064
+ def progress_callbacks_per_second(self, value: int) -> None:
1065
+ """
1066
+ Set the number of progress callbacks per second.
1067
+
1068
+ :param value: Progress callbacks per second
1069
+ :type value: int
1070
+ """
1071
+ self._progress_callbacks_per_second = max(1, value)
1072
+
1073
+ def get_object_count(self, object_type: SWMMObjects) -> int:
1074
+ """
1075
+ Get the count of a SWMM object type.
1076
+
1077
+ :param object_type: SWMM object type
1078
+ :type object_type: SWMMObjects
1079
+ :return: Object count
1080
+ :rtype: int
1081
+ """
1082
+ cdef int count = swmm_getCount(objType=object_type.value)
1083
+
1084
+ self.__validate_error(count)
1085
+
1086
+ return count
1087
+
1088
+ def get_object_name(self, object_type: SWMMObjects, index: int) -> str:
1089
+ """
1090
+ Get the name of a SWMM object.
1091
+
1092
+ :param object_type: SWMM object type
1093
+ :type object_type: SWMMObjects
1094
+ :param index: Object index
1095
+ :type index: int
1096
+ :return: Object name
1097
+ :rtype: str
1098
+ """
1099
+ cdef char* c_object_name = <char*>malloc(1024*sizeof(char))
1100
+
1101
+ cdef int error_code = swmm_getName(
1102
+ objType=object_type.value,
1103
+ index=index,
1104
+ name=c_object_name,
1105
+ size=1024
1106
+ )
1107
+
1108
+ self.__validate_error(error_code)
1109
+
1110
+ object_name = c_object_name.decode('utf-8')
1111
+
1112
+ free(c_object_name)
1113
+
1114
+ return object_name
1115
+
1116
+ def get_object_names(self, object_type: SWMMObjects) -> List[str]:
1117
+ """
1118
+ Get the names of all SWMM objects of a given type.
1119
+
1120
+ :param object_type: SWMM object type
1121
+ :type object_type: SWMMObjects
1122
+ :return: Object names
1123
+ :rtype: List[str]
1124
+ """
1125
+ cdef char* c_object_name = <char*>malloc(1024*sizeof(char))
1126
+ cdef list object_names = []
1127
+ cdef int count = self.get_object_count(object_type=object_type)
1128
+
1129
+ for i in range(count):
1130
+
1131
+ error_code = swmm_getName(
1132
+ objType=object_type.value,
1133
+ index=i,
1134
+ name=c_object_name,
1135
+ size=1024
1136
+ )
1137
+
1138
+ self.__validate_error(error_code)
1139
+
1140
+ object_name = c_object_name.decode('utf-8')
1141
+ object_names.append(object_name)
1142
+
1143
+
1144
+ free(c_object_name)
1145
+
1146
+ return object_names
1147
+
1148
+ def get_object_index(self, object_type: SWMMObjects, object_name: str) -> int:
1149
+ """
1150
+ Get the index of a SWMM object.
1151
+
1152
+ :param object_type: SWMM object type
1153
+ :type object_type: SWMMObjects
1154
+ :param object_name: Object name
1155
+ :type object_name: str
1156
+ :return: Object index
1157
+ :rtype: int
1158
+ """
1159
+ cdef int index = swmm_getIndex(
1160
+ objType=object_type.value,
1161
+ name=object_name.encode('utf-8')
1162
+ )
1163
+
1164
+ return index
1165
+
1166
+ def set_value(
1167
+ self,
1168
+ object_type: SWMMObjects,
1169
+ property_type: Union[
1170
+ SWMMRainGageProperties,
1171
+ SWMMSubcatchmentProperties,
1172
+ SWMMNodeProperties,
1173
+ SWMMLinkProperties,
1174
+ SWMMSystemProperties
1175
+ ],
1176
+ index: Union[int, str],
1177
+ value: float,
1178
+ sub_index: int = -1,
1179
+ pollutant_index: int = -1
1180
+ ) -> None:
1181
+ """
1182
+ Set a SWMM system property value.
1183
+
1184
+ :param object_type: SWMM object type (e.g., SWMMObjects.NODE)
1185
+ :type object_type: SWMMObjects
1186
+ :param property_type: SWMM system property type (e.g., SWMMSystemProperties.START_DATE)
1187
+ :type property_type: Union[SWMMRainGageProperties, SWMMSubcatchmentProperties, SWMMNodeProperties, SWMMLinkProperties, SWMMSystemProperties]
1188
+ :param index: Object index (e.g., 0, 'J1')
1189
+ :type index: int or str
1190
+ :param value: Property value (e.g., 10.0)
1191
+ :type value: double
1192
+ :param sub_index: Sub-index (e.g., 0) for properties with sub-indexes. For example pollutant index for POLLUTANT properties.
1193
+ :type sub_index: int
1194
+ :param pollutant_index: Pollutant index (e.g., 0) for POLLUTANT properties.
1195
+ :type pollutant_index: int
1196
+ """
1197
+ cdef int element_index = -1
1198
+
1199
+ if isinstance(index, str):
1200
+ element_index = self.get_object_index(object_type, index)
1201
+ self.__validate_error(element_index)
1202
+ else:
1203
+ element_index = index
1204
+
1205
+ cdef int error_code = swmm_setValueExpanded(
1206
+ objType=<int>object_type.value,
1207
+ property=<int>property_type.value,
1208
+ index=element_index,
1209
+ subindex=sub_index,
1210
+ pollutantIndex=<int>pollutant_index,
1211
+ value=value
1212
+ )
1213
+
1214
+ self.__validate_error(error_code)
1215
+
1216
+ def get_value(
1217
+ self,
1218
+ object_type: SWMMObjects,
1219
+ property_type: Union[
1220
+ SWMMRainGageProperties,
1221
+ SWMMSubcatchmentProperties,
1222
+ SWMMNodeProperties,
1223
+ SWMMLinkProperties,
1224
+ SWMMSystemProperties
1225
+ ],
1226
+ index: Union[int, str],
1227
+ sub_index: int = -1,
1228
+ pollutant_index: int = -1
1229
+ ) -> float:
1230
+ """
1231
+ Get a SWMM system property value.
1232
+
1233
+ :param object_type: SWMM object type (e.g., SWMMObjects.NODE)
1234
+ :type object_type: SWMMObjects
1235
+ :param property_type: SWMM system property type (e.g., SWMMSystemProperties.START_DATE)
1236
+ :type property_type: Union[SWMMRainGageProperties, SWMMSubcatchmentProperties, SWMMNodeProperties, SWMMLinkProperties, SWMMSystemProperties]
1237
+ :param index: Object index (e.g., 0, 'J1')
1238
+ :type index: int or str
1239
+ :param sub_index: Sub-index (e.g., 0) for properties with sub-indexes. For example pollutant index for POLLUTANT properties.
1240
+ :type sub_index: int
1241
+ :param pollutant_index: Pollutant index (e.g., 0) for POLLUTANT properties.
1242
+ :type pollutant_index: int
1243
+ :return: Property value
1244
+ :rtype: double
1245
+ """
1246
+
1247
+ cdef int element_index = -1
1248
+
1249
+ if isinstance(index, str):
1250
+ element_index = self.get_object_index(object_type, index)
1251
+ self.__validate_error(element_index)
1252
+ else:
1253
+ element_index = index
1254
+
1255
+ cdef double value = swmm_getValueExpanded(
1256
+ objType=<int>object_type.value,
1257
+ property=<int>property_type.value,
1258
+ index=element_index,
1259
+ subIndex=sub_index,
1260
+ pollutantIndex=pollutant_index
1261
+ )
1262
+
1263
+ self.__validate_error(<int>value)
1264
+
1265
+ return value
1266
+
1267
+ @property
1268
+ def stride_step(self) -> int:
1269
+ """
1270
+ Get the stride step of the simulation.
1271
+
1272
+ :return: Stride step
1273
+ :rtype: int
1274
+ """
1275
+ return self._stride_step
1276
+
1277
+ @stride_step.setter
1278
+ def stride_step(self, value: int):
1279
+ """
1280
+ Set the stride time step of the simulation.
1281
+
1282
+ :param value: Stride step in seconds
1283
+ :type value: int
1284
+ """
1285
+ self._stride_step = value
1286
+
1287
+ @property
1288
+ def solver_state(self) -> SolverState:
1289
+ """
1290
+ Get the state of the solver.
1291
+
1292
+ :return: Solver state
1293
+ :rtype: SolverState
1294
+ """
1295
+ return self._solver_state
1296
+
1297
+ def add_callback(self, callback_type: CallbackType, callback: Callable[[Solver], None]) -> None:
1298
+ """
1299
+ Add a callback to the solver.
1300
+
1301
+ :param callback_type: Type of callback
1302
+ :type callback_type: CallbackType
1303
+ :param callback: Callback function
1304
+ :type callback: callable
1305
+ """
1306
+ self._callbacks[callback_type].append(callback)
1307
+
1308
+ def add_progress_callback(self, callback: Callable[[float], None]) -> None:
1309
+ """
1310
+ Add a progress callback to the solver.
1311
+
1312
+ :param callback: Progress callback function
1313
+ :type callback: callable
1314
+ """
1315
+ self._progress_callbacks.append(callback)
1316
+
1317
+ cpdef void open(self):
1318
+ """
1319
+ Opens the SWMM solver by calling the open method in the SWMM API.
1320
+ """
1321
+ cdef int error_code = 0
1322
+ cdef bytes c_inp_file_bytes = self._inp_file.encode('utf-8')
1323
+ cdef bytes c_rpt_file_bytes = self._rpt_file.encode('utf-8')
1324
+ cdef bytes c_out_file_bytes = self._out_file.encode('utf-8')
1325
+
1326
+ cdef const char* c_inp_file = c_inp_file_bytes
1327
+ cdef const char* c_rpt_file = c_rpt_file_bytes
1328
+ cdef const char* c_out_file = c_out_file_bytes
1329
+
1330
+ if self._solver_state == SolverState.OPEN:
1331
+ pass
1332
+ elif self._solver_state == SolverState.CREATED or self._solver_state == SolverState.CLOSED:
1333
+ self.__execute_callbacks(CallbackType.BEFORE_OPEN)
1334
+
1335
+ error_code = swmm_open(
1336
+ inp_file=c_inp_file,
1337
+ rpt_file=c_rpt_file,
1338
+ out_file=c_out_file
1339
+ )
1340
+
1341
+ self.__validate_error(error_code)
1342
+ self._solver_state = SolverState.OPEN
1343
+ self.__execute_callbacks(CallbackType.AFTER_OPEN)
1344
+ self._clock = clock()
1345
+ else:
1346
+ raise SWMMSolverException(f'Open failed: Solver is not in a valid state: {self._solver_state}')
1347
+
1348
+ self._total_duration = swmm_getValue(
1349
+ property=SWMMSystemProperties.END_DATE.value,
1350
+ index=0
1351
+ ) - swmm_getValue(
1352
+ property=SWMMSystemProperties.START_DATE.value,
1353
+ index=0
1354
+ )
1355
+
1356
+ cpdef void start(self):
1357
+ """
1358
+ Starts the SWMM solver.
1359
+ """
1360
+ cdef int error_code = 0
1361
+
1362
+ if self._solver_state == SolverState.STARTED:
1363
+ pass
1364
+ elif self._solver_state == SolverState.OPEN:
1365
+ self.__execute_callbacks(CallbackType.BEFORE_START)
1366
+ error_code = swmm_start(save_flag=self._save_results)
1367
+ self.__validate_error(error_code)
1368
+ self._solver_state = SolverState.STARTED
1369
+ self.__execute_callbacks(CallbackType.AFTER_START)
1370
+ else:
1371
+ raise SWMMSolverException(f'Start failed: Solver is not in a valid state: {self._solver_state}')
1372
+
1373
+ cpdef void initialize(self):
1374
+ """
1375
+ Initializes the solver and starts the simulation. Calls the open and start methods in
1376
+ the SWMM API.
1377
+ """
1378
+ self.__execute_callbacks(CallbackType.BEFORE_INITIALIZE)
1379
+ self.open()
1380
+ self.start()
1381
+
1382
+ cpdef tuple step(self, int steps = 0):
1383
+ """
1384
+ Step a SWMM simulation.
1385
+
1386
+ :param steps: Number of steps to run. Overrides internal stride step if greater than 0.
1387
+ :type steps: int
1388
+ :return: elapsed_time, current_date
1389
+ :rtype: Tuple[float, datetime]
1390
+ """
1391
+ cdef double elapsed_time = 0.0
1392
+ cdef double progress = 0.0
1393
+
1394
+ error_code = swmm_stride(strideStep=steps if steps > 0 else self._stride_step, elapsedTime=&elapsed_time)
1395
+
1396
+ if error_code < 0:
1397
+ self.__validate_error(error_code)
1398
+
1399
+ progress = (
1400
+ swmm_getValue(
1401
+ property=SWMMSystemProperties.CURRENT_DATE.value,
1402
+ index=0
1403
+ ) - swmm_getValue(
1404
+ property=SWMMSystemProperties.START_DATE.value,
1405
+ index=0
1406
+ )
1407
+ ) / self._total_duration
1408
+
1409
+ self.__execute_progress_callbacks(progress)
1410
+
1411
+ if elapsed_time <= 0.0:
1412
+ self._solver_state = SolverState.FINISHED
1413
+
1414
+ return elapsed_time, decode_swmm_datetime(
1415
+ swmm_datetime=swmm_getValue(
1416
+ property=SWMMSystemProperties.CURRENT_DATE.value,
1417
+ index=0
1418
+ )
1419
+ )
1420
+ # else:
1421
+ # raise SWMMSolverException(f'Step failed: Solver is not in a valid state: {self._solver_state}')
1422
+
1423
+ cpdef void end(self):
1424
+ """
1425
+ Ends the SWMM simulation.
1426
+ """
1427
+ cdef int error_code = 0
1428
+
1429
+ if self._solver_state == SolverState.ENDED or \
1430
+ self._solver_state == SolverState.CREATED:
1431
+ pass
1432
+ elif self._solver_state == SolverState.OPEN or \
1433
+ self._solver_state == SolverState.STARTED or \
1434
+ self._solver_state == SolverState.FINISHED:
1435
+
1436
+ self.__execute_callbacks(CallbackType.BEFORE_END)
1437
+ error_code = swmm_end()
1438
+ self.__validate_error(error_code)
1439
+ self._solver_state = SolverState.ENDED
1440
+ self.__execute_callbacks(CallbackType.AFTER_END)
1441
+ else:
1442
+ raise SWMMSolverException(f'End failed: Solver is not in a valid state: {self._solver_state}')
1443
+
1444
+ cpdef void report(self):
1445
+ """
1446
+ Reports the results of the SWMM simulation.
1447
+ """
1448
+ cdef int error_code = 0
1449
+
1450
+ if self._solver_state == SolverState.REPORTED or self._solver_state == SolverState.CREATED:
1451
+ pass
1452
+ elif self._solver_state == SolverState.ENDED:
1453
+ self.__execute_callbacks(CallbackType.BEFORE_REPORT)
1454
+ error_code = swmm_report()
1455
+ self.__validate_error(error_code)
1456
+ self._solver_state = SolverState.REPORTED
1457
+ self.__execute_callbacks(CallbackType.AFTER_REPORT)
1458
+ else:
1459
+ raise SWMMSolverException(f'Report failed: Solver is not in a valid state: {self._solver_state}')
1460
+
1461
+ cpdef void close(self):
1462
+ """
1463
+ Close the solver.
1464
+ """
1465
+ cdef int error_code = 0
1466
+
1467
+ if self._solver_state == SolverState.CREATED:
1468
+ pass
1469
+ elif self._solver_state == SolverState.REPORTED:
1470
+ self.__execute_callbacks(CallbackType.BEFORE_CLOSE)
1471
+ error_code = swmm_close()
1472
+ self.__validate_error(error_code)
1473
+ self._solver_state = SolverState.CLOSED
1474
+ self.__execute_callbacks(CallbackType.AFTER_CLOSE)
1475
+ else:
1476
+ raise SWMMSolverException(f'Close failed: Solver is not in a valid state: {self._solver_state}')
1477
+
1478
+ cpdef void finalize(self):
1479
+ """
1480
+ Finalize the solver. Ends simulation, reports the results, and dispose objects.
1481
+ """
1482
+ cdef int error_code = 0
1483
+
1484
+ if self._solver_state == SolverState.OPEN or \
1485
+ self._solver_state == SolverState.STARTED or \
1486
+ self._solver_state == SolverState.FINISHED:
1487
+
1488
+ self.end()
1489
+ self.report()
1490
+ self.close()
1491
+ elif self._solver_state == SolverState.ENDED:
1492
+ self.report()
1493
+ self.close()
1494
+ elif self._solver_state == SolverState.REPORTED:
1495
+ self.close()
1496
+ elif self._solver_state == SolverState.CREATED or self._solver_state == SolverState.CLOSED:
1497
+ pass
1498
+ else:
1499
+ raise SWMMSolverException(f'Finalize failed: Solver is not in a valid state: {self._solver_state}')
1500
+
1501
+ cpdef void execute(self):
1502
+ """
1503
+ Run the solver to completion.
1504
+
1505
+ :return: Error code (0 if successful)
1506
+ """
1507
+ cdef int error_code = 0
1508
+ cdef progress_callback swmm_progress_callback = <progress_callback>progress_callback_wrapper
1509
+ cdef bytes c_inp_file_bytes = self._inp_file.encode('utf-8')
1510
+ cdef bytes c_rpt_file_bytes = self._rpt_file.encode('utf-8')
1511
+ cdef bytes c_out_file_bytes = self._out_file.encode('utf-8')
1512
+
1513
+ cdef const char* c_inp_file = c_inp_file_bytes
1514
+ cdef const char* c_rpt_file = c_rpt_file_bytes
1515
+ cdef const char* c_out_file = c_out_file_bytes
1516
+
1517
+ if (self._solver_state != SolverState.CREATED or self._solver_state != SolverState.CLOSED):
1518
+ if len(self._progress_callbacks) > 0:
1519
+ error_code = swmm_run_with_callback(
1520
+ inp_file=c_inp_file,
1521
+ rpt_file=c_rpt_file,
1522
+ out_file=c_out_file,
1523
+ progress=swmm_progress_callback
1524
+ )
1525
+ else:
1526
+ error_code = swmm_run(
1527
+ inp_file=c_inp_file,
1528
+ rpt_file=c_rpt_file,
1529
+ out_file=c_out_file
1530
+ )
1531
+ else:
1532
+ raise SWMMSolverException(f'Solver is not in a valid state: {self._solver_state}')
1533
+
1534
+ cpdef void use_hotstart(self, str hotstart_file):
1535
+ """
1536
+ Use a hotstart file.
1537
+
1538
+ :param hotstart_file: Hotstart file name
1539
+ """
1540
+ cdef bytes c_hotstart_file = hotstart_file.encode('utf-8')
1541
+ cdef const char* cc_hotstart_file = c_hotstart_file
1542
+ cdef int error_code = swmm_useHotStart(hotStartFile=cc_hotstart_file)
1543
+
1544
+ self.__validate_error(error_code)
1545
+
1546
+ cpdef void save_hotstart(self, str hotstart_file):
1547
+ """
1548
+ Save a hotstart file.
1549
+
1550
+ :param hotstart_file: Hotstart file name
1551
+ """
1552
+ cdef bytes c_hotstart_file = hotstart_file.encode('utf-8')
1553
+ cdef const char* cc_hotstart_file = c_hotstart_file
1554
+ cdef int error_code = swmm_saveHotStart(hotStartFile=cc_hotstart_file)
1555
+
1556
+ self.__validate_error(error_code)
1557
+
1558
+ def get_mass_balance_error(self) -> Tuple[float, float, float]:
1559
+ """
1560
+ Get the mass balance error.
1561
+
1562
+ :return: Mass balance error
1563
+ :rtype: Tuple[float, float, float]
1564
+ """
1565
+ cdef int error_code = 0
1566
+ cdef float runoffErr, flowErr, qualErr
1567
+
1568
+ swmm_getMassBalErr(
1569
+ runoffErr=&runoffErr,
1570
+ flowErr=&flowErr,
1571
+ qualErr=&qualErr
1572
+ )
1573
+
1574
+ self.__validate_error(error_code)
1575
+
1576
+ def __execute_callbacks(self, callback_type: CallbackType) -> None:
1577
+ """
1578
+ Execute the callbacks for the given type.
1579
+
1580
+ :param callback_type: Type of callback
1581
+ :type callback_type: CallbackType
1582
+ """
1583
+ for callback in self._callbacks[callback_type]:
1584
+ callback(self)
1585
+
1586
+ cpdef void __execute_progress_callbacks(self, double percent_complete):
1587
+ """
1588
+ Execute the progress callbacks.
1589
+
1590
+ :param percent_complete: Percent complete
1591
+ :type percent_complete: float
1592
+ """
1593
+ for callback in self._progress_callbacks:
1594
+ callback(percent_complete)
1595
+
1596
+ cdef void __progress_callback(self, double percent_complete):
1597
+ """
1598
+ Progress callback for the solver.
1599
+
1600
+ :param percent_complete: Percent complete
1601
+ :type percent_complete: float
1602
+ """
1603
+ cdef clock_t elapsed_time = clock() - self._clock
1604
+
1605
+ if elapsed_time > 1.0 / self._progress_callbacks_per_second:
1606
+ self.__execute_progress_callbacks(
1607
+ percent_complete=percent_complete
1608
+ )
1609
+
1610
+ self._clock = clock()
1611
+
1612
+ cdef void __validate_error(self, error_code: int) :
1613
+ """
1614
+ Validate the error code and raise an exception if it is not 0.
1615
+
1616
+ :param error_code: Error code to validate
1617
+ :type error_code: int
1618
+ """
1619
+ cdef int internal_error_code = <int>swmm_getValue(
1620
+ property=SWMMObjects.SYSTEM.value,
1621
+ index=SWMMSystemProperties.ERROR_CODE.value
1622
+ )
1623
+
1624
+ if error_code < 0:
1625
+ if internal_error_code > 0:
1626
+ raise SWMMSolverException(f'SWMM failed with message: {internal_error_code}, {self.__get_error()}')
1627
+ else:
1628
+ raise SWMMSolverException(f'SWMM failed with message: {error_code}, {get_error_message(error_code)}')
1629
+
1630
+ cdef str __get_error(self):
1631
+ """
1632
+ Get the error code from the solver.
1633
+
1634
+ :return: Error code
1635
+ :rtype: int
1636
+ """
1637
+ cdef char* c_error_message = <char*>malloc(1024*sizeof(char))
1638
+ swmm_getError(c_error_message, 1024)
1639
+
1640
+ error_message = c_error_message.decode('utf-8')
1641
+
1642
+ free(c_error_message)
1643
+
1644
+ return error_message
1645
+
1646
+