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.
- openswmm/CMakeLists.txt +31 -0
- openswmm/__init__.py +42 -0
- openswmm/_openswmm.pyx +12 -0
- openswmm/data/__init__.py +0 -0
- openswmm/gym/__init__.py +0 -0
- openswmm/openswmm.pxd +10 -0
- openswmm/output/CMakeLists.txt +45 -0
- openswmm/output/__init__.pxd +1 -0
- openswmm/output/__init__.py +13 -0
- openswmm/output/_output.pyi +732 -0
- openswmm/output/_output.pyx +1557 -0
- openswmm/output/output.pxd +368 -0
- openswmm/solver/CMakeLists.txt +50 -0
- openswmm/solver/__init__.pxd +1 -0
- openswmm/solver/__init__.py +22 -0
- openswmm/solver/_solver.pyi +1012 -0
- openswmm/solver/_solver.pyx +1646 -0
- openswmm/solver/solver.pxd +356 -0
- openswmm-5.3.0.dev0.dist-info/METADATA +228 -0
- openswmm-5.3.0.dev0.dist-info/RECORD +36 -0
- openswmm-5.3.0.dev0.dist-info/WHEEL +5 -0
- openswmm-5.3.0.dev0.dist-info/licenses/LICENSE +12 -0
- openswmm-5.3.0.dev0.dist-info/top_level.txt +2 -0
- tests/.DS_Store +0 -0
- tests/__init__.py +3 -0
- tests/data/.DS_Store +0 -0
- tests/data/__init__.py +3 -0
- tests/data/output/__init__.py +22 -0
- tests/data/output/example_output_1.out +0 -0
- tests/data/output/json_time_series.pickle +0 -0
- tests/data/solver/__init__.py +17 -0
- tests/data/solver/non_existent_input_file.rpt +2387 -0
- tests/data/solver/site_drainage_example.inp +499 -0
- tests/data/solver/site_drainage_example.rpt +354 -0
- tests/test_swmm_solver.py +716 -0
- tests/test_swwm_output.py +1048 -0
|
@@ -0,0 +1,1557 @@
|
|
|
1
|
+
# cython: language_level=3str
|
|
2
|
+
# Description: Cython module for openswmmcore output file processing and data extraction functions for the openswmmcore python package.
|
|
3
|
+
# Created by: Caleb Buahin (EPA/ORD/CESER/WID)
|
|
4
|
+
# Created on: 2024-11-19
|
|
5
|
+
|
|
6
|
+
# python and cython imports
|
|
7
|
+
import os
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from typing import List, Tuple, Union, Optional, Dict, Set
|
|
10
|
+
from cpython.datetime cimport datetime, timedelta
|
|
11
|
+
from libc.stdlib cimport free, malloc
|
|
12
|
+
|
|
13
|
+
# external imports
|
|
14
|
+
|
|
15
|
+
# local python and cython imports
|
|
16
|
+
from ..solver import (
|
|
17
|
+
decode_swmm_datetime,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from .output cimport (
|
|
21
|
+
SMO_unitSystem,
|
|
22
|
+
SMO_flowUnits,
|
|
23
|
+
SMO_concUnits,
|
|
24
|
+
SMO_elementType,
|
|
25
|
+
SMO_time,
|
|
26
|
+
SMO_subcatchAttribute,
|
|
27
|
+
SMO_nodeAttribute,
|
|
28
|
+
SMO_linkAttribute,
|
|
29
|
+
SMO_systemAttribute,
|
|
30
|
+
SMO_Handle,
|
|
31
|
+
MAXFILENAME,
|
|
32
|
+
MAXELENAME,
|
|
33
|
+
SMO_init,
|
|
34
|
+
SMO_open,
|
|
35
|
+
SMO_close,
|
|
36
|
+
SMO_getVersion,
|
|
37
|
+
SMO_getProjectSize,
|
|
38
|
+
SMO_getUnits,
|
|
39
|
+
SMO_getFlowUnits,
|
|
40
|
+
SMO_getPollutantUnits,
|
|
41
|
+
SMO_getStartDate,
|
|
42
|
+
SMO_getTimes,
|
|
43
|
+
SMO_getElementName,
|
|
44
|
+
SMO_getNumVars,
|
|
45
|
+
SMO_getVarCode,
|
|
46
|
+
SMO_getVarCodes,
|
|
47
|
+
SMO_getNumProperties,
|
|
48
|
+
SMO_getPropertyCode,
|
|
49
|
+
SMO_getPropertyCodes,
|
|
50
|
+
SMO_getPropertyValue,
|
|
51
|
+
SMO_getPropertyValues,
|
|
52
|
+
SMO_getSubcatchSeries,
|
|
53
|
+
SMO_getNodeSeries,
|
|
54
|
+
SMO_getLinkSeries,
|
|
55
|
+
SMO_getSystemSeries,
|
|
56
|
+
SMO_getSubcatchAttribute,
|
|
57
|
+
SMO_getNodeAttribute,
|
|
58
|
+
SMO_getLinkAttribute,
|
|
59
|
+
SMO_getLinkAttribute,
|
|
60
|
+
SMO_getSystemAttribute,
|
|
61
|
+
SMO_getSubcatchResult,
|
|
62
|
+
SMO_getNodeResult,
|
|
63
|
+
SMO_getLinkResult,
|
|
64
|
+
SMO_getSystemResult,
|
|
65
|
+
SMO_free,
|
|
66
|
+
SMO_clearError,
|
|
67
|
+
SMO_checkError
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
class UnitSystem(Enum):
|
|
71
|
+
"""
|
|
72
|
+
Enumeration of the unit system used in the output file.
|
|
73
|
+
|
|
74
|
+
:ivar US: US customary units.
|
|
75
|
+
:type US: int
|
|
76
|
+
:ivar SI: SI metric units.
|
|
77
|
+
:type SI: int
|
|
78
|
+
"""
|
|
79
|
+
US = SMO_unitSystem.SMO_US #: US customary units.
|
|
80
|
+
SI = SMO_unitSystem.SMO_SI #: SI metric units.
|
|
81
|
+
|
|
82
|
+
class FlowUnits(Enum):
|
|
83
|
+
"""
|
|
84
|
+
Enumeration of the flow units used in the simulation.
|
|
85
|
+
|
|
86
|
+
:ivar CFS: Cubic feet per second.
|
|
87
|
+
:type CFS: int
|
|
88
|
+
:ivar GPM: Gallons per minute.
|
|
89
|
+
:type GPM: int
|
|
90
|
+
:ivar MGD: Million gallons per day.
|
|
91
|
+
:type MGD: int
|
|
92
|
+
:ivar CMS: Cubic meters per second.
|
|
93
|
+
:type CMS: int
|
|
94
|
+
:ivar LPS: Liters per second.
|
|
95
|
+
:type LPS: int
|
|
96
|
+
:ivar MLD: Million liters per day.
|
|
97
|
+
:type MLD: int
|
|
98
|
+
"""
|
|
99
|
+
CFS = SMO_flowUnits.SMO_CFS #: Cubic feet per second.
|
|
100
|
+
GPM = SMO_flowUnits.SMO_GPM #: Gallons per minute.
|
|
101
|
+
MGD = SMO_flowUnits.SMO_MGD #: Million gallons per day.
|
|
102
|
+
CMS = SMO_flowUnits.SMO_CMS #: Cubic meters per second.
|
|
103
|
+
LPS = SMO_flowUnits.SMO_LPS #: Liters per second.
|
|
104
|
+
MLD = SMO_flowUnits.SMO_MLD #: Million liters per day.
|
|
105
|
+
|
|
106
|
+
class ConcentrationUnits(Enum):
|
|
107
|
+
"""
|
|
108
|
+
Enumeration of the concentration units used in the simulation.
|
|
109
|
+
|
|
110
|
+
:ivar MG: Milligrams per liter.
|
|
111
|
+
:type MG: int
|
|
112
|
+
:ivar UG: Micrograms per liter.
|
|
113
|
+
:type UG: int
|
|
114
|
+
:ivar COUNT: Counts per liter.
|
|
115
|
+
:type COUNT: int
|
|
116
|
+
:ivar NONE: No units.
|
|
117
|
+
:type NONE: int
|
|
118
|
+
"""
|
|
119
|
+
MG = SMO_concUnits.SMO_MG #: Milligrams per liter.
|
|
120
|
+
UG = SMO_concUnits.SMO_UG #: Micrograms per liter.
|
|
121
|
+
COUNT = SMO_concUnits.SMO_COUNT #: Counts per liter.
|
|
122
|
+
NONE = SMO_concUnits.SMO_NONE #: No units.
|
|
123
|
+
|
|
124
|
+
class ElementType(Enum):
|
|
125
|
+
"""
|
|
126
|
+
Enumeration of the SWMM element types.
|
|
127
|
+
|
|
128
|
+
:ivar SUBCATCHMENT: Subcatchment.
|
|
129
|
+
:type SUBCATCHMENT: int
|
|
130
|
+
:ivar NODE: Node.
|
|
131
|
+
:type NODE: int
|
|
132
|
+
:ivar LINK: Link.
|
|
133
|
+
:type LINK: int
|
|
134
|
+
:ivar SYS: System.
|
|
135
|
+
:type SYS: int
|
|
136
|
+
:ivar POLLUTANT: Pollutant.
|
|
137
|
+
:type POLLUTANT: int
|
|
138
|
+
"""
|
|
139
|
+
SUBCATCHMENT = SMO_elementType.SMO_subcatch #: Subcatchment.
|
|
140
|
+
NODE = SMO_elementType.SMO_node #: Node.
|
|
141
|
+
LINK = SMO_elementType.SMO_link #: Link.
|
|
142
|
+
SYSTEM = SMO_elementType.SMO_sys #: System.
|
|
143
|
+
POLLUTANT = SMO_elementType.SMO_pollut #: Pollutant.
|
|
144
|
+
|
|
145
|
+
class TimeAttribute(Enum):
|
|
146
|
+
"""
|
|
147
|
+
Enumeration of the report time related attributes.
|
|
148
|
+
|
|
149
|
+
:ivar REPORT_STEP: Report step size (seconds).
|
|
150
|
+
:type REPORT_STEP: int
|
|
151
|
+
:ivar NUM_PERIODS: Number of reporting periods.
|
|
152
|
+
:type NUM_PERIODS: int
|
|
153
|
+
"""
|
|
154
|
+
REPORT_STEP = SMO_time.SMO_reportStep #: Report step size (seconds).
|
|
155
|
+
NUM_PERIODS = SMO_time.SMO_numPeriods #: Number of reporting periods.
|
|
156
|
+
|
|
157
|
+
class SubcatchAttribute(Enum):
|
|
158
|
+
"""
|
|
159
|
+
Enumeration of the subcatchment attributes.
|
|
160
|
+
|
|
161
|
+
:ivar RAINFALL: Subcatchment rainfall (in/hr or mm/hr).
|
|
162
|
+
:type RAINFALL: int
|
|
163
|
+
:ivar SNOW_DEPTH: Subcatchment snow depth (in or mm).
|
|
164
|
+
:type SNOW_DEPTH: int
|
|
165
|
+
:ivar EVAPORATION_LOSS: Subcatchment evaporation loss (in/hr or mm/hr).
|
|
166
|
+
:type EVAPORATION_LOSS: int
|
|
167
|
+
:ivar INFILTRATION_LOSS: Subcatchment infiltration loss (in/hr or mm/hr).
|
|
168
|
+
:type INFILTRATION_LOSS: int
|
|
169
|
+
:ivar RUNOFF_RATE: Subcatchment runoff flow (flow units).
|
|
170
|
+
:type RUNOFF_RATE: int
|
|
171
|
+
:ivar GROUNDWATER_OUTFLOW: Subcatchment groundwater flow (flow units).
|
|
172
|
+
:type GROUNDWATER_OUTFLOW: int
|
|
173
|
+
:ivar GW_TABLE: Subcatchment groundwater elevation (ft or m).
|
|
174
|
+
:type GW_TABLE: int
|
|
175
|
+
:ivar SOIL_MOISTURE: Subcatchment soil moisture content (-).
|
|
176
|
+
:type SOIL_MOISTURE: int
|
|
177
|
+
:ivar POLLUTANT_CONCENTRATION: Subcatchment pollutant concentration (-).
|
|
178
|
+
:type POLLUTANT_CONCENTRATION: int
|
|
179
|
+
"""
|
|
180
|
+
RAINFALL = SMO_subcatchAttribute.SMO_rainfall_subcatch #: Subcatchment rainfall (in/hr or mm/hr).
|
|
181
|
+
SNOW_DEPTH = SMO_subcatchAttribute.SMO_snow_depth_subcatch #: Subcatchment snow depth (in or mm).
|
|
182
|
+
EVAPORATION_LOSS = SMO_subcatchAttribute.SMO_evap_loss #: Subcatchment evaporation loss (in/hr or mm/hr).
|
|
183
|
+
INFILTRATION_LOSS = SMO_subcatchAttribute.SMO_infil_loss #: Subcatchment infiltration loss (in/hr or mm/hr).
|
|
184
|
+
RUNOFF_RATE = SMO_subcatchAttribute.SMO_runoff_rate #: Subcatchment runoff flow (flow units).
|
|
185
|
+
GROUNDWATER_OUTFLOW = SMO_subcatchAttribute.SMO_gwoutflow_rate #: Subcatchment groundwater flow (flow units).
|
|
186
|
+
GROUNDWATER_TABLE_ELEVATION = SMO_subcatchAttribute.SMO_gwtable_elev #: Subcatchment groundwater elevation (ft or m).
|
|
187
|
+
SOIL_MOISTURE = SMO_subcatchAttribute.SMO_soil_moisture #: Subcatchment soil moisture content (-).
|
|
188
|
+
POLLUTANT_CONCENTRATION = SMO_subcatchAttribute.SMO_pollutant_conc_subcatch #: Subcatchment pollutant concentration (-).
|
|
189
|
+
|
|
190
|
+
class NodeAttribute(Enum):
|
|
191
|
+
"""
|
|
192
|
+
Enumeration of the node attributes.
|
|
193
|
+
|
|
194
|
+
:ivar INVERT_DEPTH: Node depth above invert (ft or m).
|
|
195
|
+
:type INVERT_DEPTH: int
|
|
196
|
+
:ivar HYDRAULIC_HEAD: Node hydraulic head (ft or m).
|
|
197
|
+
:type HYDRAULIC_HEAD: int
|
|
198
|
+
:ivar STORED_VOLUME: Node volume stored (ft3 or m3).
|
|
199
|
+
:type STORED_VOLUME: int
|
|
200
|
+
:ivar LATERAL_INFLOW: Node lateral inflow (flow units).
|
|
201
|
+
:type LATERAL_INFLOW: int
|
|
202
|
+
:ivar TOTAL_INFLOW: Node total inflow (flow units).
|
|
203
|
+
:type TOTAL_INFLOW: int
|
|
204
|
+
:ivar FLOODING_LOSSES: Node flooding losses (flow units).
|
|
205
|
+
:type FLOODING_LOSSES: int
|
|
206
|
+
:ivar POLLUTANT_CONCENTRATION: Node pollutant concentration (-).
|
|
207
|
+
:type POLLUTANT_CONCENTRATION: int
|
|
208
|
+
"""
|
|
209
|
+
INVERT_DEPTH = SMO_nodeAttribute.SMO_invert_depth #: Node depth above invert (ft or m).
|
|
210
|
+
HYDRAULIC_HEAD = SMO_nodeAttribute.SMO_hydraulic_head #: Node hydraulic head (ft or m).
|
|
211
|
+
STORED_VOLUME = SMO_nodeAttribute.SMO_stored_ponded_volume #: Node volume stored (ft3 or m3).
|
|
212
|
+
LATERAL_INFLOW = SMO_nodeAttribute.SMO_lateral_inflow #: Node lateral inflow (flow units).
|
|
213
|
+
TOTAL_INFLOW = SMO_nodeAttribute.SMO_total_inflow #: Node total inflow (flow units).
|
|
214
|
+
FLOODING_LOSSES = SMO_nodeAttribute.SMO_flooding_losses #: Node flooding losses (flow units).
|
|
215
|
+
POLLUTANT_CONCENTRATION = SMO_nodeAttribute.SMO_pollutant_conc_node #: Node pollutant concentration (-).
|
|
216
|
+
|
|
217
|
+
class LinkAttribute(Enum):
|
|
218
|
+
"""
|
|
219
|
+
Enumeration of the link attributes.
|
|
220
|
+
|
|
221
|
+
:ivar FLOW_RATE: Link flow rate (flow units).
|
|
222
|
+
:type FLOW_RATE: int
|
|
223
|
+
:ivar FLOW_DEPTH: Link flow depth (ft or m).
|
|
224
|
+
:type FLOW_DEPTH: int
|
|
225
|
+
:ivar FLOW_VELOCITY: Link flow velocity (ft/s or m/s).
|
|
226
|
+
:type FLOW_VELOCITY: int
|
|
227
|
+
:ivar FLOW_VOLUME: Link flow volume (ft3 or m3).
|
|
228
|
+
:type FLOW_VOLUME: int
|
|
229
|
+
:ivar CAPACITY: Link capacity (fraction of conduit filled).
|
|
230
|
+
:type CAPACITY: int
|
|
231
|
+
:ivar POLLUTANT_CONCENTRATION: Link pollutant concentration (-).
|
|
232
|
+
:type POLLUTANT_CONCENTRATION: int
|
|
233
|
+
"""
|
|
234
|
+
FLOW_RATE = SMO_linkAttribute.SMO_flow_rate_link #: Link flow rate (flow units).
|
|
235
|
+
FLOW_DEPTH = SMO_linkAttribute.SMO_flow_depth #: Link flow depth (ft or m).
|
|
236
|
+
FLOW_VELOCITY = SMO_linkAttribute.SMO_flow_velocity #: Link flow velocity (ft/s or m/s).
|
|
237
|
+
FLOW_VOLUME = SMO_linkAttribute.SMO_flow_volume #: Link flow volume (ft3 or m3).
|
|
238
|
+
CAPACITY = SMO_linkAttribute.SMO_capacity #: Link capacity (fraction of conduit filled).
|
|
239
|
+
POLLUTANT_CONCENTRATION = SMO_linkAttribute.SMO_pollutant_conc_link #: Link pollutant concentration (-).
|
|
240
|
+
|
|
241
|
+
class SystemAttribute(Enum):
|
|
242
|
+
"""
|
|
243
|
+
Enumeration of the system attributes.
|
|
244
|
+
|
|
245
|
+
:ivar AIR_TEMP: Air temperature (deg. F or deg. C).
|
|
246
|
+
:type AIR_TEMP: int
|
|
247
|
+
:ivar RAINFALL: Rainfall intensity (in/hr or mm/hr).
|
|
248
|
+
:type RAINFALL: int
|
|
249
|
+
:ivar SNOW_DEPTH: Snow depth (in or mm).
|
|
250
|
+
:type SNOW_DEPTH: int
|
|
251
|
+
:ivar EVAP_INFIL_LOSS: Evaporation and infiltration loss rate (in/day or mm/day).
|
|
252
|
+
:type EVAP_INFIL_LOSS: int
|
|
253
|
+
:ivar RUNOFF_FLOW: Runoff flow (flow units).
|
|
254
|
+
:type RUNOFF_FLOW: int
|
|
255
|
+
:ivar DRY_WEATHER_INFLOW: Dry weather inflow (flow units).
|
|
256
|
+
:type DRY_WEATHER_INFLOW: int
|
|
257
|
+
:ivar GROUNDWATER_INFLOW: Groundwater inflow (flow units).
|
|
258
|
+
:type GROUNDWATER_INFLOW: int
|
|
259
|
+
:ivar RDII_INFLOW: Rainfall Derived Infiltration and Inflow (RDII) (flow units).
|
|
260
|
+
:type RDII_INFLOW: int
|
|
261
|
+
:ivar DIRECT_INFLOW: Direct inflow (flow units).
|
|
262
|
+
:type DIRECT_INFLOW: int
|
|
263
|
+
:ivar TOTAL_LATERAL_INFLOW: Total lateral inflow; sum of variables 4 to 8 (flow units).
|
|
264
|
+
:type TOTAL_LATERAL_INFLOW: int
|
|
265
|
+
:ivar FLOOD_LOSSES: Flooding losses (flow units).
|
|
266
|
+
:type FLOOD_LOSSES: int
|
|
267
|
+
:ivar OUTFALL_FLOWS: Outfall flow (flow units).
|
|
268
|
+
:type OUTFALL_FLOWS: int
|
|
269
|
+
:ivar VOLUME_STORED: Volume stored in storage nodes (ft3 or m3).
|
|
270
|
+
:type VOLUME_STORED: int
|
|
271
|
+
:ivar EVAPORATION_RATE: Evaporation rate (in/day or mm/day).
|
|
272
|
+
:type EVAPORATION_RATE: int
|
|
273
|
+
"""
|
|
274
|
+
AIR_TEMP = SMO_systemAttribute.SMO_air_temp #: Air temperature (deg. F or deg. C).
|
|
275
|
+
RAINFALL = SMO_systemAttribute.SMO_rainfall_system #: Rainfall intensity (in/hr or mm/hr).
|
|
276
|
+
SNOW_DEPTH = SMO_systemAttribute.SMO_snow_depth_system #: Snow depth (in or mm).
|
|
277
|
+
EVAP_INFIL_LOSS = SMO_systemAttribute.SMO_evap_infil_loss #: Evaporation and infiltration loss rate (in/day or mm/day).
|
|
278
|
+
RUNOFF_FLOW = SMO_systemAttribute.SMO_runoff_flow #: Runoff flow (flow units).
|
|
279
|
+
DRY_WEATHER_INFLOW = SMO_systemAttribute.SMO_dry_weather_inflow #: Dry weather inflow (flow units).
|
|
280
|
+
GROUNDWATER_INFLOW = SMO_systemAttribute.SMO_groundwater_inflow #: Groundwater inflow (flow units).
|
|
281
|
+
RDII_INFLOW = SMO_systemAttribute.SMO_RDII_inflow #: Rainfall Derived Infiltration and Inflow (RDII) (flow units).
|
|
282
|
+
DIRECT_INFLOW = SMO_systemAttribute.SMO_direct_inflow #: Direct inflow (flow units).
|
|
283
|
+
TOTAL_LATERAL_INFLOW = SMO_systemAttribute.SMO_total_lateral_inflow #: Total lateral inflow; sum of variables 4 to 8 (flow units).
|
|
284
|
+
FLOOD_LOSSES = SMO_systemAttribute.SMO_flood_losses #: Flooding losses (flow units).
|
|
285
|
+
OUTFALL_FLOWS = SMO_systemAttribute.SMO_outfall_flows #: Outfall flow (flow units).
|
|
286
|
+
VOLUME_STORED = SMO_systemAttribute.SMO_volume_stored #: Volume stored in storage nodes (ft3 or m3).
|
|
287
|
+
EVAPORATION_RATE = SMO_systemAttribute.SMO_evap_rate #: Evaporation rate (in/day or mm/day).
|
|
288
|
+
|
|
289
|
+
class SWMMOutputException(Exception):
|
|
290
|
+
"""
|
|
291
|
+
Exception class for SWMM output file processing errors.
|
|
292
|
+
"""
|
|
293
|
+
def __init__(self, message: str) -> None:
|
|
294
|
+
"""
|
|
295
|
+
Constructor to initialize the exception message.
|
|
296
|
+
|
|
297
|
+
:param message: Error message.
|
|
298
|
+
:type message: str
|
|
299
|
+
"""
|
|
300
|
+
super().__init__(message)
|
|
301
|
+
|
|
302
|
+
cdef class Output:
|
|
303
|
+
"""
|
|
304
|
+
Class to read and process the output file generated by the SWMM simulation.
|
|
305
|
+
|
|
306
|
+
:cvar _output_file_handle: Handle to the SWMM output file.
|
|
307
|
+
:cvar _version: Version of the SWMM output file.
|
|
308
|
+
:cvar _units: Unit system used in the SWMM output file.
|
|
309
|
+
:cvar _flow_units: Flow units used in the SWMM output file.
|
|
310
|
+
:cvar _output_size: Size of the project in the SWMM output file.
|
|
311
|
+
:cvar _pollutant_units: Pollutant units used in the SWMM output file.
|
|
312
|
+
:cvar _start_date: Start date of the simulation in the SWMM output file.
|
|
313
|
+
:cvar _report_step: Report step size in seconds.
|
|
314
|
+
:cvar _num_periods: Number of reporting periods.
|
|
315
|
+
:cvar _times: Times of the simulation in the SWMM output file.
|
|
316
|
+
"""
|
|
317
|
+
cdef SMO_Handle _output_file_handle
|
|
318
|
+
cdef int _version
|
|
319
|
+
cdef int* _units
|
|
320
|
+
cdef int _units_length
|
|
321
|
+
cdef int _flow_units
|
|
322
|
+
cdef int* _output_size
|
|
323
|
+
cdef int _output_size_length
|
|
324
|
+
cdef list _pollutant_units
|
|
325
|
+
cdef dict _element_name_indexes
|
|
326
|
+
cdef datetime _start_date
|
|
327
|
+
cdef int _report_step
|
|
328
|
+
cdef int _num_periods
|
|
329
|
+
cdef list _times
|
|
330
|
+
|
|
331
|
+
def __cinit__(self, str output_file):
|
|
332
|
+
"""
|
|
333
|
+
Constructor to open the SWMM output file.
|
|
334
|
+
|
|
335
|
+
:param output_file: Path to the SWMM output file.
|
|
336
|
+
:type output_file: str
|
|
337
|
+
"""
|
|
338
|
+
cdef int i = 0
|
|
339
|
+
cdef int error_code = 0
|
|
340
|
+
cdef bytes path_bytes = output_file.encode('utf-8')
|
|
341
|
+
cdef const char* c_output_file = path_bytes
|
|
342
|
+
self._output_file_handle = NULL
|
|
343
|
+
|
|
344
|
+
self._output_size = NULL
|
|
345
|
+
self._units = NULL
|
|
346
|
+
|
|
347
|
+
error_code = SMO_init(&self._output_file_handle)
|
|
348
|
+
|
|
349
|
+
# get error message if error code is not 0 and print it and prevent any memory leaks
|
|
350
|
+
if error_code != 0:
|
|
351
|
+
# create a buffer to store the error message
|
|
352
|
+
error_message = self.check_error()
|
|
353
|
+
raise SWMMOutputException(f"Error initializing the SWMM output file {output_file}. Error code: {error_code}: {error_message}")
|
|
354
|
+
|
|
355
|
+
# Check if the output file exists
|
|
356
|
+
if not os.path.exists(output_file):
|
|
357
|
+
raise FileNotFoundError(f"Error opening the SWMM output file {output_file}. Error code: 434: The output file does not exist.")
|
|
358
|
+
|
|
359
|
+
error_code = SMO_open(self._output_file_handle, c_output_file)
|
|
360
|
+
|
|
361
|
+
# get error message if error code is not 0 and print it and prevent any memory leaks
|
|
362
|
+
if error_code != 0:
|
|
363
|
+
# create a buffer to store the error message
|
|
364
|
+
error_message = self.check_error()
|
|
365
|
+
|
|
366
|
+
if error_code > 400:
|
|
367
|
+
self._output_file_handle = NULL
|
|
368
|
+
|
|
369
|
+
if error_code == 434:
|
|
370
|
+
raise FileNotFoundError(f"Error opening the SWMM output file {output_file}. Error code: {error_code}: {error_message}. The output file may not exist or may be locked by another process.")
|
|
371
|
+
else:
|
|
372
|
+
raise SWMMOutputException(f"Error opening the SWMM output file {output_file}. Error code: {error_code}: {error_message}")
|
|
373
|
+
|
|
374
|
+
# Read and cache output attributes for faster access
|
|
375
|
+
self._version = self.__get_version()
|
|
376
|
+
self._units, self._units_length = self.__get_units()
|
|
377
|
+
self._flow_units = self.__get_flow_units()
|
|
378
|
+
self._output_size, self._output_size_length = self.__get_output_size()
|
|
379
|
+
self._pollutant_units = [ConcentrationUnits(i) for i in self.__get_pollutant_units()]
|
|
380
|
+
self._element_name_indexes = self.__get_element_name_indexes()
|
|
381
|
+
self._start_date = self.__get_start_date()
|
|
382
|
+
self._report_step = self.get_time_attribute(TimeAttribute.REPORT_STEP)
|
|
383
|
+
self._num_periods = self.get_time_attribute(TimeAttribute.NUM_PERIODS)
|
|
384
|
+
self._times = [self._start_date + timedelta(seconds=self._report_step) * i for i in range(1, self._num_periods + 1)]
|
|
385
|
+
|
|
386
|
+
def __enter__(self):
|
|
387
|
+
"""
|
|
388
|
+
Method to return the SWMM output file instance.
|
|
389
|
+
"""
|
|
390
|
+
return self
|
|
391
|
+
|
|
392
|
+
def __close(self):
|
|
393
|
+
"""
|
|
394
|
+
Method to close the SWMM output file instance.
|
|
395
|
+
"""
|
|
396
|
+
if self._output_file_handle != NULL:
|
|
397
|
+
SMO_close(&self._output_file_handle)
|
|
398
|
+
self._output_file_handle = NULL
|
|
399
|
+
|
|
400
|
+
if self._output_size != NULL:
|
|
401
|
+
free(self._output_size)
|
|
402
|
+
self._output_size = NULL
|
|
403
|
+
|
|
404
|
+
if self._units != NULL:
|
|
405
|
+
free(self._units)
|
|
406
|
+
self._units = NULL
|
|
407
|
+
|
|
408
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
409
|
+
"""
|
|
410
|
+
Method to close the SWMM output file instance.
|
|
411
|
+
"""
|
|
412
|
+
self.__close()
|
|
413
|
+
|
|
414
|
+
def __dealloc__(self):
|
|
415
|
+
"""
|
|
416
|
+
Destructor to close the SWMM output file.
|
|
417
|
+
"""
|
|
418
|
+
self.__close()
|
|
419
|
+
|
|
420
|
+
@property
|
|
421
|
+
def version(self) -> int:
|
|
422
|
+
"""
|
|
423
|
+
Method to get the version of the SWMM output file.
|
|
424
|
+
|
|
425
|
+
:return: Version of the SWMM output file.
|
|
426
|
+
:rtype: str
|
|
427
|
+
"""
|
|
428
|
+
return self._version
|
|
429
|
+
|
|
430
|
+
cdef int __get_version(self):
|
|
431
|
+
"""
|
|
432
|
+
Method to get the version of the SWMM output file.
|
|
433
|
+
|
|
434
|
+
:return: Version of the SWMM output file.
|
|
435
|
+
:rtype: str
|
|
436
|
+
"""
|
|
437
|
+
cdef int error_code = 0
|
|
438
|
+
cdef int version = 0
|
|
439
|
+
|
|
440
|
+
error_code = SMO_getVersion(self._output_file_handle, &version)
|
|
441
|
+
self.__validate_error_code(error_code)
|
|
442
|
+
|
|
443
|
+
return version
|
|
444
|
+
|
|
445
|
+
@property
|
|
446
|
+
def output_size(self) -> Dict[str, int]:
|
|
447
|
+
"""
|
|
448
|
+
Method to get the size of the project in the SWMM output file.
|
|
449
|
+
|
|
450
|
+
:return: Size of the project in the SWMM output file.
|
|
451
|
+
:rtype: int
|
|
452
|
+
"""
|
|
453
|
+
cdef dict output_size_dict = {
|
|
454
|
+
'subcatchments': self._output_size[0],
|
|
455
|
+
'nodes': self._output_size[1],
|
|
456
|
+
'links': self._output_size[2],
|
|
457
|
+
'system': self._output_size[3],
|
|
458
|
+
'pollutants': self._output_size[4]
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return output_size_dict
|
|
462
|
+
|
|
463
|
+
cdef (int*, int) __get_output_size(self):
|
|
464
|
+
"""
|
|
465
|
+
Method to get the size of the project in the SWMM output file.
|
|
466
|
+
|
|
467
|
+
:return: Size of the project in the SWMM output file.
|
|
468
|
+
:rtype: int
|
|
469
|
+
"""
|
|
470
|
+
cdef int error_code = 0
|
|
471
|
+
cdef int *project_size = NULL
|
|
472
|
+
cdef int length = 0
|
|
473
|
+
|
|
474
|
+
error_code = SMO_getProjectSize(self._output_file_handle, &project_size, &length)
|
|
475
|
+
self.__validate_error_code(error_code)
|
|
476
|
+
|
|
477
|
+
return project_size, length
|
|
478
|
+
|
|
479
|
+
@property
|
|
480
|
+
def units(self) -> Tuple[UnitSystem, FlowUnits, Optional[List[ConcentrationUnits]]]:
|
|
481
|
+
"""
|
|
482
|
+
Method to get the unit system used in the SWMM output file.
|
|
483
|
+
|
|
484
|
+
:return: Tuple of the unit system, flow units, and pollutant units used in the SWMM output file.
|
|
485
|
+
:rtype: Tuple[UnitSystem, FlowUnits, Optional[List[ConcentrationUnits]]]
|
|
486
|
+
"""
|
|
487
|
+
return (
|
|
488
|
+
UnitSystem(self._units[0]),
|
|
489
|
+
FlowUnits(self._units[1]),
|
|
490
|
+
[ConcentrationUnits(self._units[i]) for i in range(2, self._units_length)]
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
cdef (int*, int) __get_units(self):
|
|
494
|
+
"""
|
|
495
|
+
Method to get the unit system used in the SWMM output file.
|
|
496
|
+
|
|
497
|
+
:return: Tuple of comprised of an integer array of unit systems used and its length
|
|
498
|
+
:rtype: Tuple[int*, int]
|
|
499
|
+
"""
|
|
500
|
+
cdef int error_code = 0
|
|
501
|
+
cdef int *units = NULL
|
|
502
|
+
cdef int length = 0
|
|
503
|
+
|
|
504
|
+
error_code = SMO_getUnits(self._output_file_handle, &units, &length)
|
|
505
|
+
self.__validate_error_code(error_code)
|
|
506
|
+
|
|
507
|
+
return units, length
|
|
508
|
+
|
|
509
|
+
@property
|
|
510
|
+
def flow_units(self) -> FlowUnits:
|
|
511
|
+
"""
|
|
512
|
+
Method to get the flow units used in the SWMM output file.
|
|
513
|
+
|
|
514
|
+
:return: Flow units used in the SWMM output file.
|
|
515
|
+
:rtype: FlowUnits
|
|
516
|
+
"""
|
|
517
|
+
return FlowUnits(self._flow_units)
|
|
518
|
+
|
|
519
|
+
cdef int __get_flow_units(self):
|
|
520
|
+
"""
|
|
521
|
+
Method to get the flow units used in the SWMM output file.
|
|
522
|
+
|
|
523
|
+
:return: Flow units used in the SWMM output file.
|
|
524
|
+
:rtype: FlowUnits
|
|
525
|
+
"""
|
|
526
|
+
cdef int error_code = 0
|
|
527
|
+
cdef int flow_units = 0
|
|
528
|
+
|
|
529
|
+
error_code = SMO_getFlowUnits(self._output_file_handle, &flow_units)
|
|
530
|
+
self.__validate_error_code(error_code)
|
|
531
|
+
|
|
532
|
+
return flow_units
|
|
533
|
+
|
|
534
|
+
@property
|
|
535
|
+
def pollutant_units(self) -> List[ConcentrationUnits]:
|
|
536
|
+
"""
|
|
537
|
+
Method to get the pollutant units used in the SWMM output file.
|
|
538
|
+
|
|
539
|
+
:return: Pollutant units used in the SWMM output file.
|
|
540
|
+
:rtype: List[ConcentrationUnits]
|
|
541
|
+
"""
|
|
542
|
+
return self._pollutant_units
|
|
543
|
+
|
|
544
|
+
cdef list __get_pollutant_units(self):
|
|
545
|
+
"""
|
|
546
|
+
Method to get the pollutant units used in the SWMM output file.
|
|
547
|
+
|
|
548
|
+
:return: Pollutant units used in the SWMM output file.
|
|
549
|
+
:rtype: List[ConcentrationUnits]
|
|
550
|
+
"""
|
|
551
|
+
cdef int i = 0
|
|
552
|
+
cdef int error_code = 0
|
|
553
|
+
cdef int *pollutant_units = NULL
|
|
554
|
+
cdef int length = 0
|
|
555
|
+
cdef list pollutant_units_list = []
|
|
556
|
+
|
|
557
|
+
error_code = SMO_getPollutantUnits(self._output_file_handle, &pollutant_units, &length)
|
|
558
|
+
self.__validate_error_code(error_code)
|
|
559
|
+
|
|
560
|
+
pollutant_units_list = [pollutant_units[i] for i in range(length)]
|
|
561
|
+
|
|
562
|
+
if pollutant_units != NULL:
|
|
563
|
+
free(pollutant_units)
|
|
564
|
+
|
|
565
|
+
return pollutant_units_list
|
|
566
|
+
|
|
567
|
+
cdef dict __get_element_name_indexes(self):
|
|
568
|
+
"""
|
|
569
|
+
Method to get the indexes of all elements of a given type and name in the SWMM output file.
|
|
570
|
+
:rtype: Dict[str, Dict[str, int]]
|
|
571
|
+
"""
|
|
572
|
+
cdef dict element_name_indexes = {}
|
|
573
|
+
|
|
574
|
+
for element_type in ElementType:
|
|
575
|
+
if element_type.value == SMO_elementType.SMO_sys:
|
|
576
|
+
continue
|
|
577
|
+
|
|
578
|
+
num_elements = self._output_size[element_type.value]
|
|
579
|
+
element_names = self.get_element_names(element_type)
|
|
580
|
+
|
|
581
|
+
element_name_indexes[element_type.name] = {element_name: i for i , element_name in enumerate(element_names)}
|
|
582
|
+
|
|
583
|
+
return element_name_indexes
|
|
584
|
+
|
|
585
|
+
@property
|
|
586
|
+
def start_date(self) -> datetime:
|
|
587
|
+
"""
|
|
588
|
+
Method to get the start date of the simulation in the SWMM output file.
|
|
589
|
+
|
|
590
|
+
:return: Start date of the simulation in the SWMM output file.
|
|
591
|
+
:rtype: datetime
|
|
592
|
+
"""
|
|
593
|
+
return self._start_date
|
|
594
|
+
|
|
595
|
+
cdef datetime __get_start_date(self):
|
|
596
|
+
"""
|
|
597
|
+
Method to get the start date of the simulation in the SWMM output file.
|
|
598
|
+
|
|
599
|
+
:return: Start date of the simulation in the SWMM output file.
|
|
600
|
+
:rtype: datetime
|
|
601
|
+
"""
|
|
602
|
+
cdef double swmm_datetime = 0
|
|
603
|
+
|
|
604
|
+
error_code = SMO_getStartDate(self._output_file_handle, &swmm_datetime)
|
|
605
|
+
self.__validate_error_code(error_code)
|
|
606
|
+
|
|
607
|
+
return decode_swmm_datetime(swmm_datetime)
|
|
608
|
+
|
|
609
|
+
@property
|
|
610
|
+
def times(self) -> List[datetime]:
|
|
611
|
+
"""
|
|
612
|
+
Method to get the times of the simulation in the SWMM output file.
|
|
613
|
+
|
|
614
|
+
:return: Times of the simulation in the SWMM output file.
|
|
615
|
+
:rtype: List[datetime]
|
|
616
|
+
"""
|
|
617
|
+
return self._times
|
|
618
|
+
|
|
619
|
+
def get_num_variables(self, element_type: ElementType) -> int:
|
|
620
|
+
"""
|
|
621
|
+
Method to get the number of variables for an element type in the SWMM output file.
|
|
622
|
+
|
|
623
|
+
:param element_type: Type of the element.
|
|
624
|
+
:type element_type: int
|
|
625
|
+
|
|
626
|
+
:return: Number of variables for the element type.
|
|
627
|
+
:rtype: int
|
|
628
|
+
"""
|
|
629
|
+
cdef int error_code = 0
|
|
630
|
+
cdef int num_vars = 0
|
|
631
|
+
|
|
632
|
+
error_code = SMO_getNumVars(
|
|
633
|
+
p_handle=self._output_file_handle,
|
|
634
|
+
type=<SMO_elementType>element_type.value,
|
|
635
|
+
count=&num_vars
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
self.__validate_error_code(error_code)
|
|
639
|
+
|
|
640
|
+
return num_vars
|
|
641
|
+
|
|
642
|
+
def get_variable_code(self, element_type: ElementType, variable_index: int) -> int:
|
|
643
|
+
"""
|
|
644
|
+
Method to get the variable code for an element type in the SWMM output file.
|
|
645
|
+
|
|
646
|
+
:param element_type: Type of the element.
|
|
647
|
+
:type element_type: int
|
|
648
|
+
|
|
649
|
+
:param variable_index: Index of the variable.
|
|
650
|
+
:type variable_index: int
|
|
651
|
+
|
|
652
|
+
:return: Variable code for the element type.
|
|
653
|
+
:rtype: int
|
|
654
|
+
"""
|
|
655
|
+
cdef int error_code = 0
|
|
656
|
+
cdef int var_code = 0
|
|
657
|
+
|
|
658
|
+
error_code = SMO_getVarCode(
|
|
659
|
+
p_handle=self._output_file_handle,
|
|
660
|
+
type=<SMO_elementType>element_type.value,
|
|
661
|
+
varIndex=variable_index,
|
|
662
|
+
varCode=&var_code
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
self.__validate_error_code(error_code)
|
|
666
|
+
|
|
667
|
+
return var_code
|
|
668
|
+
|
|
669
|
+
def get_variable_codes(self, element_type: ElementType) -> List[int]:
|
|
670
|
+
"""
|
|
671
|
+
Method to get the variable codes for an element type in the SWMM output file.
|
|
672
|
+
|
|
673
|
+
:param element_type: Type of the element.
|
|
674
|
+
:type element_type: int
|
|
675
|
+
|
|
676
|
+
:return: Variable codes for the element type.
|
|
677
|
+
:rtype: List[int]
|
|
678
|
+
"""
|
|
679
|
+
cdef int error_code = 0
|
|
680
|
+
cdef int num_vars = 0
|
|
681
|
+
cdef int* var_codes = NULL
|
|
682
|
+
cdef list variable_codes
|
|
683
|
+
|
|
684
|
+
error_code = SMO_getVarCodes(
|
|
685
|
+
p_handle=self._output_file_handle,
|
|
686
|
+
type=<SMO_elementType>element_type.value,
|
|
687
|
+
varCodes=&var_codes,
|
|
688
|
+
size=&num_vars
|
|
689
|
+
)
|
|
690
|
+
|
|
691
|
+
self.__validate_error_code(error_code)
|
|
692
|
+
|
|
693
|
+
variable_codes = [var_codes[i] for i in range(num_vars)]
|
|
694
|
+
|
|
695
|
+
if var_codes != NULL:
|
|
696
|
+
free(var_codes)
|
|
697
|
+
|
|
698
|
+
return variable_codes
|
|
699
|
+
|
|
700
|
+
def get_num_properties(self, element_type: ElementType) -> int:
|
|
701
|
+
"""
|
|
702
|
+
Method to get the number of properties for an element type in the SWMM output file.
|
|
703
|
+
|
|
704
|
+
:param element_type: Type of the element.
|
|
705
|
+
:type element_type: int
|
|
706
|
+
|
|
707
|
+
:return: Number of properties for the element type.
|
|
708
|
+
:rtype: int
|
|
709
|
+
"""
|
|
710
|
+
cdef int error_code = 0
|
|
711
|
+
cdef int num_properties = 0
|
|
712
|
+
|
|
713
|
+
error_code = SMO_getNumProperties(
|
|
714
|
+
p_handle=self._output_file_handle,
|
|
715
|
+
type=<SMO_elementType>element_type.value,
|
|
716
|
+
count=&num_properties
|
|
717
|
+
)
|
|
718
|
+
|
|
719
|
+
self.__validate_error_code(error_code)
|
|
720
|
+
|
|
721
|
+
return num_properties
|
|
722
|
+
|
|
723
|
+
def get_property_code(self, element_type: ElementType, property_index: int) -> int:
|
|
724
|
+
"""
|
|
725
|
+
Method to get the property code for an element type in the SWMM output file.
|
|
726
|
+
|
|
727
|
+
:param element_type: Type of the element.
|
|
728
|
+
:type element_type: int
|
|
729
|
+
|
|
730
|
+
:param property_index: Index of the property.
|
|
731
|
+
:type property_index: int
|
|
732
|
+
|
|
733
|
+
:return: Property code for the element type.
|
|
734
|
+
:rtype: int
|
|
735
|
+
"""
|
|
736
|
+
cdef int error_code = 0
|
|
737
|
+
cdef int property_code = 0
|
|
738
|
+
|
|
739
|
+
error_code = SMO_getPropertyCode(
|
|
740
|
+
p_handle=self._output_file_handle,
|
|
741
|
+
type=<SMO_elementType>element_type.value,
|
|
742
|
+
propertyIndex=property_index,
|
|
743
|
+
propertyCode=&property_code
|
|
744
|
+
)
|
|
745
|
+
|
|
746
|
+
self.__validate_error_code(error_code)
|
|
747
|
+
|
|
748
|
+
return property_code
|
|
749
|
+
|
|
750
|
+
def get_property_codes(self, element_type: ElementType) -> List[int]:
|
|
751
|
+
"""
|
|
752
|
+
Method to get the property codes for an element type in the SWMM output file.
|
|
753
|
+
|
|
754
|
+
:param element_type: Type of the element.
|
|
755
|
+
:type element_type: int
|
|
756
|
+
|
|
757
|
+
:return: Property codes for the element type.
|
|
758
|
+
:rtype: List[int]
|
|
759
|
+
"""
|
|
760
|
+
cdef int error_code = 0
|
|
761
|
+
cdef int num_properties = 0
|
|
762
|
+
cdef int* property_codes = NULL
|
|
763
|
+
|
|
764
|
+
error_code = SMO_getPropertyCodes(
|
|
765
|
+
p_handle=self._output_file_handle,
|
|
766
|
+
type=<SMO_elementType>element_type.value,
|
|
767
|
+
propertyCodes=&property_codes,
|
|
768
|
+
size=&num_properties
|
|
769
|
+
)
|
|
770
|
+
|
|
771
|
+
self.__validate_error_code(error_code)
|
|
772
|
+
|
|
773
|
+
property_codes_list = [property_codes[i] for i in range(num_properties)]
|
|
774
|
+
|
|
775
|
+
if property_codes != NULL:
|
|
776
|
+
free(property_codes)
|
|
777
|
+
|
|
778
|
+
return property_codes_list
|
|
779
|
+
|
|
780
|
+
def get_property_value(self, element_type: ElementType, element_index: Union[int, str], property_code: int) -> float:
|
|
781
|
+
"""
|
|
782
|
+
Method to get the property value for an element in the SWMM output file.
|
|
783
|
+
|
|
784
|
+
:param element_type: Type of the element.
|
|
785
|
+
:type element_type: int
|
|
786
|
+
|
|
787
|
+
:param element_index: Index of the element.
|
|
788
|
+
:type element_index: int
|
|
789
|
+
|
|
790
|
+
:param property_code: Property code.
|
|
791
|
+
:type property_code: int
|
|
792
|
+
|
|
793
|
+
:return: Property value for the element.
|
|
794
|
+
:rtype: float
|
|
795
|
+
"""
|
|
796
|
+
cdef int error_code = 0
|
|
797
|
+
cdef float property_value = 0
|
|
798
|
+
cdef int l_element_index = element_index if isinstance(element_index, int) \
|
|
799
|
+
else self._element_name_indexes[ElementType.NODE.name][element_index]
|
|
800
|
+
|
|
801
|
+
error_code = SMO_getPropertyValue(
|
|
802
|
+
p_handle=self._output_file_handle,
|
|
803
|
+
type=<SMO_elementType>element_type.value,
|
|
804
|
+
propertyIndex=property_code,
|
|
805
|
+
elementIndex=l_element_index,
|
|
806
|
+
value=&property_value
|
|
807
|
+
)
|
|
808
|
+
|
|
809
|
+
self.__validate_error_code(error_code)
|
|
810
|
+
|
|
811
|
+
return property_value
|
|
812
|
+
|
|
813
|
+
def get_property_values(self, element_type: ElementType, element_index: Union[int, str]) -> List[float]:
|
|
814
|
+
"""
|
|
815
|
+
Method to get the property values for an element type in the SWMM output file.
|
|
816
|
+
|
|
817
|
+
:param element_type: Type of the element.
|
|
818
|
+
:type element_type: int
|
|
819
|
+
|
|
820
|
+
:param element_index: Element index.
|
|
821
|
+
:type element_index: int
|
|
822
|
+
|
|
823
|
+
:return: Property values for the element type.
|
|
824
|
+
:rtype: List[float]
|
|
825
|
+
"""
|
|
826
|
+
cdef int error_code = 0
|
|
827
|
+
cdef int num_elements = 0
|
|
828
|
+
cdef int i = 0
|
|
829
|
+
cdef float* property_values = NULL
|
|
830
|
+
cdef list property_values_list
|
|
831
|
+
cdef int l_element_index = element_index if isinstance(element_index, int) \
|
|
832
|
+
else self._element_name_indexes[ElementType.NODE.name][element_index]
|
|
833
|
+
|
|
834
|
+
error_code = SMO_getPropertyValues(
|
|
835
|
+
p_handle=self._output_file_handle,
|
|
836
|
+
type=<SMO_elementType>element_type.value,
|
|
837
|
+
elementIndex=l_element_index,
|
|
838
|
+
outValueArray=&property_values,
|
|
839
|
+
length=&num_elements
|
|
840
|
+
)
|
|
841
|
+
|
|
842
|
+
self.__validate_error_code(error_code)
|
|
843
|
+
property_values_list = [*<float[:num_elements]>property_values]
|
|
844
|
+
|
|
845
|
+
if property_values != NULL:
|
|
846
|
+
free(property_values)
|
|
847
|
+
|
|
848
|
+
return property_values_list
|
|
849
|
+
|
|
850
|
+
def get_time_attribute(self, time_attribute: TimeAttribute) -> int:
|
|
851
|
+
"""
|
|
852
|
+
Method to get the temporal attributes of the simulation in the SWMM output file.
|
|
853
|
+
|
|
854
|
+
:param time_attribute: Temporal attribute.
|
|
855
|
+
:type time_attribute: TimeAttribute
|
|
856
|
+
|
|
857
|
+
:return: Temporal attributes of the simulation in the SWMM output file.
|
|
858
|
+
:rtype: int
|
|
859
|
+
|
|
860
|
+
"""
|
|
861
|
+
cdef int error_code = 0
|
|
862
|
+
cdef int temporal_attribute = -1
|
|
863
|
+
|
|
864
|
+
error_code = SMO_getTimes(self._output_file_handle, <SMO_time>time_attribute.value, &temporal_attribute)
|
|
865
|
+
self.__validate_error_code(error_code)
|
|
866
|
+
|
|
867
|
+
return temporal_attribute
|
|
868
|
+
|
|
869
|
+
def get_element_name(self, element_type: ElementType, element_index: int) -> str:
|
|
870
|
+
"""
|
|
871
|
+
Method to get the name of an element in the SWMM output file.
|
|
872
|
+
|
|
873
|
+
:param element_type: Type of the element.
|
|
874
|
+
:type element_type: int
|
|
875
|
+
|
|
876
|
+
:param indelement_indexex: Index of the element.
|
|
877
|
+
:type element_index: int
|
|
878
|
+
|
|
879
|
+
:return: Name of the element.
|
|
880
|
+
:rtype: str
|
|
881
|
+
"""
|
|
882
|
+
cdef int error_code = 0
|
|
883
|
+
cdef int strlen = 0
|
|
884
|
+
cdef char* element_name = NULL
|
|
885
|
+
|
|
886
|
+
error_code = SMO_getElementName(self._output_file_handle, <SMO_elementType>element_type.value, element_index, &element_name, &strlen)
|
|
887
|
+
self.__validate_error_code(error_code)
|
|
888
|
+
|
|
889
|
+
# Convert the C string to a Python string and delete the C string
|
|
890
|
+
element_name_str = element_name.decode('utf-8')
|
|
891
|
+
|
|
892
|
+
if element_name != NULL:
|
|
893
|
+
free(element_name)
|
|
894
|
+
|
|
895
|
+
return element_name_str
|
|
896
|
+
|
|
897
|
+
def get_element_names(self, element_type: ElementType) -> List[str]:
|
|
898
|
+
"""
|
|
899
|
+
Method to get the names of all elements of a given type in the SWMM output file.
|
|
900
|
+
|
|
901
|
+
:param element_type: Type of the element.
|
|
902
|
+
:type element_type: int
|
|
903
|
+
|
|
904
|
+
:return: Names of all elements of the given type.
|
|
905
|
+
:rtype: List[str]
|
|
906
|
+
"""
|
|
907
|
+
cdef int error_code = 0
|
|
908
|
+
cdef int num_elements = 0
|
|
909
|
+
cdef int i = 0
|
|
910
|
+
cdef int strlen = 0
|
|
911
|
+
cdef char** c_element_names = NULL
|
|
912
|
+
cdef list element_names
|
|
913
|
+
|
|
914
|
+
if element_type.value == SMO_elementType.SMO_sys:
|
|
915
|
+
raise SWMMOutputException(f"Cannot get element names for the system element type {ElementType.SYSTEM}.")
|
|
916
|
+
elif element_type.value > SMO_elementType.SMO_pollut:
|
|
917
|
+
raise SWMMOutputException("Invalid element type.")
|
|
918
|
+
|
|
919
|
+
num_elements = self._output_size[element_type.value]
|
|
920
|
+
|
|
921
|
+
c_element_names = <char**>malloc(num_elements * sizeof(char*))
|
|
922
|
+
|
|
923
|
+
for i in range(num_elements):
|
|
924
|
+
error_code = SMO_getElementName(self._output_file_handle, <SMO_elementType>element_type.value, i, &c_element_names[i], &strlen)
|
|
925
|
+
self.__validate_error_code(error_code)
|
|
926
|
+
|
|
927
|
+
element_names = [c_element_names[i].decode('utf-8') for i in range(num_elements)]
|
|
928
|
+
|
|
929
|
+
if c_element_names != NULL:
|
|
930
|
+
for i in range(num_elements):
|
|
931
|
+
if c_element_names[i] != NULL:
|
|
932
|
+
free(c_element_names[i])
|
|
933
|
+
c_element_names[i] = NULL
|
|
934
|
+
|
|
935
|
+
free(c_element_names)
|
|
936
|
+
|
|
937
|
+
return element_names
|
|
938
|
+
|
|
939
|
+
def get_subcatchment_timeseries(
|
|
940
|
+
self, element_index: Union[int, str],
|
|
941
|
+
attribute: SubcatchAttribute,
|
|
942
|
+
start_date_index: int = 0,
|
|
943
|
+
end_date_index: int = -1,
|
|
944
|
+
sub_index: int = 0
|
|
945
|
+
) -> Dict[datetime, float]:
|
|
946
|
+
"""
|
|
947
|
+
Method to get the time series data for a subcatchment attribute in the SWMM output file.
|
|
948
|
+
|
|
949
|
+
:param element_index: Index of the subcatchment.
|
|
950
|
+
:type element_index: int or str
|
|
951
|
+
|
|
952
|
+
:param attribute: Subcatchment attribute.
|
|
953
|
+
:type attribute: SubcatchAttribute
|
|
954
|
+
|
|
955
|
+
:param start_date_index: Start date index. Default is 0.
|
|
956
|
+
:type start_date_index: int
|
|
957
|
+
|
|
958
|
+
:param end_date_index: End date index. Default is the last date index.
|
|
959
|
+
:type end_date_index: int
|
|
960
|
+
|
|
961
|
+
:param sub_index: Attribute index for the subcatchment non enumerated attributes primarily for the pollutants
|
|
962
|
+
:type sub_index: int
|
|
963
|
+
|
|
964
|
+
:return: Time series data for the subcatchment attribute.
|
|
965
|
+
:rtype: Dict[datetime, double]
|
|
966
|
+
"""
|
|
967
|
+
cdef int error_code = 0
|
|
968
|
+
cdef float* values = NULL
|
|
969
|
+
cdef int length = 0
|
|
970
|
+
cdef int attribute_index = attribute.value + sub_index
|
|
971
|
+
cdef int l_element_index = element_index if isinstance(element_index, int) \
|
|
972
|
+
else self._element_name_indexes[ElementType.SUBCATCHMENT.name][element_index]
|
|
973
|
+
|
|
974
|
+
if end_date_index == -1:
|
|
975
|
+
end_date_index = self._num_periods
|
|
976
|
+
|
|
977
|
+
error_code = SMO_getSubcatchSeries(
|
|
978
|
+
p_handle=self._output_file_handle,
|
|
979
|
+
subcatchIndex=l_element_index,
|
|
980
|
+
attr=<SMO_subcatchAttribute>attribute_index,
|
|
981
|
+
startPeriod=start_date_index,
|
|
982
|
+
endPeriod=end_date_index,
|
|
983
|
+
outValueArray=&values,
|
|
984
|
+
length=&length
|
|
985
|
+
)
|
|
986
|
+
self.__validate_error_code(error_code)
|
|
987
|
+
|
|
988
|
+
results = dict(zip(self._times[start_date_index:end_date_index], <float[:length]>values))
|
|
989
|
+
|
|
990
|
+
if values != NULL:
|
|
991
|
+
free(values)
|
|
992
|
+
|
|
993
|
+
return results
|
|
994
|
+
|
|
995
|
+
def get_node_timeseries(
|
|
996
|
+
self,
|
|
997
|
+
element_index: Union[int, str],
|
|
998
|
+
attribute: NodeAttribute,
|
|
999
|
+
start_date_index: int = 0,
|
|
1000
|
+
end_date_index: int = -1,
|
|
1001
|
+
sub_index: int = 0
|
|
1002
|
+
) -> Dict[datetime, float]:
|
|
1003
|
+
"""
|
|
1004
|
+
Method to get the time series data for a node attribute in the SWMM output file.
|
|
1005
|
+
|
|
1006
|
+
:param element_index: Index of the node.
|
|
1007
|
+
:type element_index: int or str
|
|
1008
|
+
|
|
1009
|
+
:param attribute: Node attribute.
|
|
1010
|
+
:type attribute: NodeAttribute
|
|
1011
|
+
|
|
1012
|
+
:param start_date_index: Start date index. Default is 0.
|
|
1013
|
+
:type start_date_index: int
|
|
1014
|
+
|
|
1015
|
+
:param end_date_index: End date index. Default is the last date index.
|
|
1016
|
+
:type end_date_index: int
|
|
1017
|
+
|
|
1018
|
+
:param sub_index: Attribute index for the subcatchment non enumerated attributes primarily for the pollutants
|
|
1019
|
+
:type sub_index: int
|
|
1020
|
+
|
|
1021
|
+
:return: Time series data for the node attribute.
|
|
1022
|
+
:rtype: Dict[datetime, double]
|
|
1023
|
+
"""
|
|
1024
|
+
cdef int error_code = 0
|
|
1025
|
+
cdef float* values = NULL
|
|
1026
|
+
cdef int length = 0
|
|
1027
|
+
cdef int attribute_index = attribute.value + sub_index
|
|
1028
|
+
cdef int l_element_index = element_index if isinstance(element_index, int) \
|
|
1029
|
+
else self._element_name_indexes[ElementType.NODE.name][element_index]
|
|
1030
|
+
|
|
1031
|
+
if end_date_index == -1:
|
|
1032
|
+
end_date_index = self._num_periods
|
|
1033
|
+
|
|
1034
|
+
error_code = SMO_getNodeSeries(
|
|
1035
|
+
p_handle=self._output_file_handle,
|
|
1036
|
+
nodeIndex=l_element_index,
|
|
1037
|
+
attr=<SMO_nodeAttribute>attribute_index,
|
|
1038
|
+
startPeriod=start_date_index,
|
|
1039
|
+
endPeriod=end_date_index,
|
|
1040
|
+
outValueArray=&values,
|
|
1041
|
+
length=&length
|
|
1042
|
+
)
|
|
1043
|
+
|
|
1044
|
+
self.__validate_error_code(error_code)
|
|
1045
|
+
|
|
1046
|
+
results = dict(zip(self._times[start_date_index:end_date_index], <float[:length]>values))
|
|
1047
|
+
|
|
1048
|
+
if values != NULL:
|
|
1049
|
+
free(values)
|
|
1050
|
+
|
|
1051
|
+
return results
|
|
1052
|
+
|
|
1053
|
+
def get_link_timeseries(
|
|
1054
|
+
self,
|
|
1055
|
+
element_index: Union[int, str],
|
|
1056
|
+
attribute: LinkAttribute,
|
|
1057
|
+
start_date_index: int = 0,
|
|
1058
|
+
end_date_index: int = -1,
|
|
1059
|
+
sub_index: int = 0
|
|
1060
|
+
) -> Dict[datetime, float]:
|
|
1061
|
+
"""
|
|
1062
|
+
Method to get the time series data for a link attribute in the SWMM output file.
|
|
1063
|
+
|
|
1064
|
+
:param element_index: Index of the link.
|
|
1065
|
+
:type element_index: int
|
|
1066
|
+
|
|
1067
|
+
:param attribute: Link attribute.
|
|
1068
|
+
:type attribute: LinkAttribute
|
|
1069
|
+
|
|
1070
|
+
:param start_date_index: Start date index. Default is 0.
|
|
1071
|
+
:type start_date_index: int
|
|
1072
|
+
|
|
1073
|
+
:param end_date_index: End date index. Default is the last date index.
|
|
1074
|
+
:type end_date_index: int
|
|
1075
|
+
|
|
1076
|
+
:param sub_index: Attribute index for the subcatchment non enumerated attributes primarily for the pollutants
|
|
1077
|
+
:type sub_index: int
|
|
1078
|
+
|
|
1079
|
+
:return: Time series data for the link attribute.
|
|
1080
|
+
:rtype: Dict[datetime, double]
|
|
1081
|
+
"""
|
|
1082
|
+
cdef int error_code = 0
|
|
1083
|
+
cdef float* values = NULL
|
|
1084
|
+
cdef int length = 0
|
|
1085
|
+
cdef int attribute_index = attribute.value + sub_index
|
|
1086
|
+
cdef int l_element_index = element_index if isinstance(element_index, int) \
|
|
1087
|
+
else self._element_name_indexes[ElementType.LINK.name][element_index]
|
|
1088
|
+
|
|
1089
|
+
if end_date_index == -1:
|
|
1090
|
+
end_date_index = self._num_periods
|
|
1091
|
+
|
|
1092
|
+
error_code = SMO_getLinkSeries(
|
|
1093
|
+
p_handle=self._output_file_handle,
|
|
1094
|
+
linkIndex=l_element_index,
|
|
1095
|
+
attr=<SMO_linkAttribute>(attribute_index),
|
|
1096
|
+
startPeriod=start_date_index,
|
|
1097
|
+
endPeriod=end_date_index,
|
|
1098
|
+
outValueArray=&values,
|
|
1099
|
+
length=&length
|
|
1100
|
+
)
|
|
1101
|
+
|
|
1102
|
+
self.__validate_error_code(error_code)
|
|
1103
|
+
|
|
1104
|
+
results = dict(zip(self._times[start_date_index:end_date_index], <float[:length]>values))
|
|
1105
|
+
|
|
1106
|
+
if values != NULL:
|
|
1107
|
+
free(values)
|
|
1108
|
+
|
|
1109
|
+
return results
|
|
1110
|
+
|
|
1111
|
+
def get_system_timeseries(
|
|
1112
|
+
self,
|
|
1113
|
+
attribute: SystemAttribute,
|
|
1114
|
+
start_date_index: int = 0,
|
|
1115
|
+
end_date_index: int = -1,
|
|
1116
|
+
sub_index: int = 0
|
|
1117
|
+
) -> Dict[datetime, float]:
|
|
1118
|
+
"""
|
|
1119
|
+
Method to get the time series data for a system attribute in the SWMM output file.
|
|
1120
|
+
|
|
1121
|
+
:param attribute: System attribute.
|
|
1122
|
+
:type attribute: SystemAttribute
|
|
1123
|
+
|
|
1124
|
+
:param start_date_index: Start date index. Default is 0.
|
|
1125
|
+
:type start_date_index: int
|
|
1126
|
+
|
|
1127
|
+
:param end_date_index: End date index. Default is the last date index.
|
|
1128
|
+
:type end_date_index: int
|
|
1129
|
+
|
|
1130
|
+
:param sub_index: Attribute index for the subcatchment non enumerated attributes primarily for the pollutants
|
|
1131
|
+
:type sub_index: int
|
|
1132
|
+
|
|
1133
|
+
:return: Time series data for the system attribute.
|
|
1134
|
+
:rtype: Dict[datetime, double]
|
|
1135
|
+
"""
|
|
1136
|
+
cdef int error_code = 0
|
|
1137
|
+
cdef float* values = NULL
|
|
1138
|
+
cdef int length = 0
|
|
1139
|
+
cdef int attribute_index = attribute.value + sub_index
|
|
1140
|
+
|
|
1141
|
+
if end_date_index == -1:
|
|
1142
|
+
end_date_index = self._num_periods
|
|
1143
|
+
|
|
1144
|
+
error_code = SMO_getSystemSeries(
|
|
1145
|
+
p_handle=self._output_file_handle,
|
|
1146
|
+
attr=<SMO_systemAttribute>attribute_index,
|
|
1147
|
+
startPeriod=start_date_index,
|
|
1148
|
+
endPeriod=end_date_index,
|
|
1149
|
+
outValueArray=&values,
|
|
1150
|
+
length=&length
|
|
1151
|
+
)
|
|
1152
|
+
|
|
1153
|
+
self.__validate_error_code(error_code)
|
|
1154
|
+
|
|
1155
|
+
results = dict(zip(self._times[start_date_index:end_date_index], <float[:length]>values))
|
|
1156
|
+
|
|
1157
|
+
if values != NULL:
|
|
1158
|
+
free(values)
|
|
1159
|
+
|
|
1160
|
+
return results
|
|
1161
|
+
|
|
1162
|
+
def get_subcatchment_values_by_time_and_attribute(
|
|
1163
|
+
self,
|
|
1164
|
+
time_index: int,
|
|
1165
|
+
attribute: SubcatchAttribute,
|
|
1166
|
+
sub_index: int = 0
|
|
1167
|
+
) -> Dict[str, float]:
|
|
1168
|
+
"""
|
|
1169
|
+
Method to get the subcatchment values for all subcatchments for a given time index and attribute.
|
|
1170
|
+
|
|
1171
|
+
:param time_index: Time index.
|
|
1172
|
+
:type time_index: int
|
|
1173
|
+
|
|
1174
|
+
:param attribute: Subcatchment attribute.
|
|
1175
|
+
:type attribute: SubcatchAttribute
|
|
1176
|
+
|
|
1177
|
+
:param sub_index: Attribute index for the subcatchment non enumerated attributes primarily for the pollutants
|
|
1178
|
+
:type sub_index: int
|
|
1179
|
+
|
|
1180
|
+
:return: Subcatchment values for all subcatchments.
|
|
1181
|
+
:rtype: Dict[str, float]
|
|
1182
|
+
"""
|
|
1183
|
+
|
|
1184
|
+
cdef int error_code = 0
|
|
1185
|
+
cdef float* values = NULL
|
|
1186
|
+
cdef int length = 0
|
|
1187
|
+
cdef int attribute_index = attribute.value + sub_index
|
|
1188
|
+
|
|
1189
|
+
error_code = SMO_getSubcatchAttribute(
|
|
1190
|
+
p_handle=self._output_file_handle,
|
|
1191
|
+
timeIndex=time_index,
|
|
1192
|
+
attr=<SMO_subcatchAttribute>attribute_index,
|
|
1193
|
+
outValueArray=&values,
|
|
1194
|
+
length=&length
|
|
1195
|
+
)
|
|
1196
|
+
|
|
1197
|
+
self.__validate_error_code(error_code)
|
|
1198
|
+
|
|
1199
|
+
subcatchment_values = dict(zip(self.get_element_names(ElementType.SUBCATCHMENT), <float[:length]>values))
|
|
1200
|
+
|
|
1201
|
+
if values != NULL:
|
|
1202
|
+
free(values)
|
|
1203
|
+
|
|
1204
|
+
return subcatchment_values
|
|
1205
|
+
|
|
1206
|
+
def get_node_values_by_time_and_attribute(
|
|
1207
|
+
self,
|
|
1208
|
+
time_index: int,
|
|
1209
|
+
attribute: NodeAttribute,
|
|
1210
|
+
sub_index: int = 0
|
|
1211
|
+
) -> Dict[str, float]:
|
|
1212
|
+
"""
|
|
1213
|
+
Method to get the node values for all nodes for a given time index and attribute.
|
|
1214
|
+
|
|
1215
|
+
:param time_index: Time index.
|
|
1216
|
+
:type time_index: int
|
|
1217
|
+
|
|
1218
|
+
:param attribute: Node attribute.
|
|
1219
|
+
:type attribute: NodeAttribute
|
|
1220
|
+
|
|
1221
|
+
:param sub_index: Attribute index for the subcatchment non enumerated attributes primarily for the pollutants
|
|
1222
|
+
:type sub_index: int
|
|
1223
|
+
|
|
1224
|
+
:return: Node values for all nodes.
|
|
1225
|
+
:rtype: Dict[str, float]
|
|
1226
|
+
"""
|
|
1227
|
+
|
|
1228
|
+
cdef int error_code = 0
|
|
1229
|
+
cdef float* values = NULL
|
|
1230
|
+
cdef int length = 0
|
|
1231
|
+
cdef int attribute_index = attribute.value + sub_index
|
|
1232
|
+
|
|
1233
|
+
error_code = SMO_getNodeAttribute(
|
|
1234
|
+
p_handle=self._output_file_handle,
|
|
1235
|
+
timeIndex=time_index,
|
|
1236
|
+
attr=<SMO_nodeAttribute>attribute_index,
|
|
1237
|
+
outValueArray=&values,
|
|
1238
|
+
length=&length
|
|
1239
|
+
)
|
|
1240
|
+
|
|
1241
|
+
self.__validate_error_code(error_code)
|
|
1242
|
+
|
|
1243
|
+
node_values = dict(zip(self.get_element_names(ElementType.NODE), <float[:length]>values))
|
|
1244
|
+
|
|
1245
|
+
if values != NULL:
|
|
1246
|
+
free(values)
|
|
1247
|
+
|
|
1248
|
+
return node_values
|
|
1249
|
+
|
|
1250
|
+
def get_link_values_by_time_and_attribute(
|
|
1251
|
+
self,
|
|
1252
|
+
time_index: int,
|
|
1253
|
+
attribute: LinkAttribute,
|
|
1254
|
+
sub_index: int = 0
|
|
1255
|
+
) -> Dict[str, float]:
|
|
1256
|
+
"""
|
|
1257
|
+
Method to get the link values for all links for a given time index and attribute.
|
|
1258
|
+
|
|
1259
|
+
:param time_index: Time index.
|
|
1260
|
+
:type time_index: int
|
|
1261
|
+
|
|
1262
|
+
:param attribute: Link attribute.
|
|
1263
|
+
:type attribute: LinkAttribute
|
|
1264
|
+
|
|
1265
|
+
:param sub_index: Attribute index for the subcatchment non enumerated attributes primarily for the pollutants
|
|
1266
|
+
:type sub_index: int
|
|
1267
|
+
|
|
1268
|
+
:return: Link values for all links.
|
|
1269
|
+
:rtype: Dict[str, float]
|
|
1270
|
+
"""
|
|
1271
|
+
|
|
1272
|
+
cdef int error_code = 0
|
|
1273
|
+
cdef float* values = NULL
|
|
1274
|
+
cdef int length = 0
|
|
1275
|
+
cdef int attribute_index = attribute.value + sub_index
|
|
1276
|
+
|
|
1277
|
+
error_code = SMO_getLinkAttribute(
|
|
1278
|
+
p_handle=self._output_file_handle,
|
|
1279
|
+
timeIndex=time_index,
|
|
1280
|
+
attr=<SMO_linkAttribute>attribute_index,
|
|
1281
|
+
outValueArray=&values,
|
|
1282
|
+
length=&length
|
|
1283
|
+
)
|
|
1284
|
+
|
|
1285
|
+
self.__validate_error_code(error_code)
|
|
1286
|
+
|
|
1287
|
+
link_values = dict(zip(self.get_element_names(ElementType.LINK), <float[:length]>values))
|
|
1288
|
+
|
|
1289
|
+
if values != NULL:
|
|
1290
|
+
free(values)
|
|
1291
|
+
|
|
1292
|
+
return link_values
|
|
1293
|
+
|
|
1294
|
+
def get_system_values_by_time_and_attribute(
|
|
1295
|
+
self,
|
|
1296
|
+
time_index: int,
|
|
1297
|
+
attribute: SystemAttribute,
|
|
1298
|
+
sub_index: int = 0
|
|
1299
|
+
) -> Dict[str, float]:
|
|
1300
|
+
"""
|
|
1301
|
+
Method to get the system values for a given time index and attribute.
|
|
1302
|
+
|
|
1303
|
+
:param time_index: Time index.
|
|
1304
|
+
:type time_index: int
|
|
1305
|
+
|
|
1306
|
+
:param attribute: System attribute.
|
|
1307
|
+
:type attribute: SystemAttribute
|
|
1308
|
+
|
|
1309
|
+
:param sub_index: Attribute index for the subcatchment non enumerated attributes primarily for the pollutants
|
|
1310
|
+
:type sub_index: int
|
|
1311
|
+
|
|
1312
|
+
:return: System values.
|
|
1313
|
+
:rtype: Dict[str, float]
|
|
1314
|
+
"""
|
|
1315
|
+
|
|
1316
|
+
cdef int error_code = 0
|
|
1317
|
+
cdef float* values = NULL
|
|
1318
|
+
cdef int length = 0
|
|
1319
|
+
cdef int attribute_index = attribute.value + sub_index
|
|
1320
|
+
|
|
1321
|
+
error_code = SMO_getSystemAttribute(
|
|
1322
|
+
p_handle=self._output_file_handle,
|
|
1323
|
+
timeIndex=time_index,
|
|
1324
|
+
attr=<SMO_systemAttribute>attribute_index,
|
|
1325
|
+
outValueArray=&values,
|
|
1326
|
+
length=&length
|
|
1327
|
+
)
|
|
1328
|
+
|
|
1329
|
+
self.__validate_error_code(error_code)
|
|
1330
|
+
|
|
1331
|
+
system_values = dict(zip([SystemAttribute(attribute).name], <float[:length]>values))
|
|
1332
|
+
|
|
1333
|
+
|
|
1334
|
+
if values != NULL:
|
|
1335
|
+
free(values)
|
|
1336
|
+
|
|
1337
|
+
return system_values
|
|
1338
|
+
|
|
1339
|
+
def get_subcatchment_values_by_time_and_element_index(
|
|
1340
|
+
self,
|
|
1341
|
+
time_index: int,
|
|
1342
|
+
element_index: Union[int, str]
|
|
1343
|
+
) -> Dict[str, float]:
|
|
1344
|
+
"""
|
|
1345
|
+
Method to get all attributes of a given subcatchment for specified time.
|
|
1346
|
+
|
|
1347
|
+
:param time_index: Time index.
|
|
1348
|
+
:type time_index: int
|
|
1349
|
+
|
|
1350
|
+
:param element_index: Index of the subcatchment.
|
|
1351
|
+
:type element_index: int or str
|
|
1352
|
+
|
|
1353
|
+
:return: Dictionary of subcatchment attributes.
|
|
1354
|
+
:rtype: Dict[str, float]
|
|
1355
|
+
"""
|
|
1356
|
+
cdef int error_code = 0
|
|
1357
|
+
cdef float* values = NULL
|
|
1358
|
+
cdef int length = 0
|
|
1359
|
+
cdef int enum_values_length = len(SubcatchAttribute) - 1
|
|
1360
|
+
cdef list pollutant_names = self.get_element_names(ElementType.POLLUTANT)
|
|
1361
|
+
cdef list attrib_names = []
|
|
1362
|
+
cdef int l_element_index = element_index if isinstance(element_index, int) \
|
|
1363
|
+
else self._element_name_indexes[ElementType.SUBCATCHMENT.name][element_index]
|
|
1364
|
+
|
|
1365
|
+
error_code = SMO_getSubcatchResult(
|
|
1366
|
+
p_handle=self._output_file_handle,
|
|
1367
|
+
timeIndex=time_index,
|
|
1368
|
+
subcatchIndex=l_element_index,
|
|
1369
|
+
outValueArray=&values,
|
|
1370
|
+
length=&length
|
|
1371
|
+
)
|
|
1372
|
+
|
|
1373
|
+
self.__validate_error_code(error_code)
|
|
1374
|
+
|
|
1375
|
+
for i in range(length):
|
|
1376
|
+
if i < enum_values_length:
|
|
1377
|
+
attrib_names.append(SubcatchAttribute(i).name)
|
|
1378
|
+
else:
|
|
1379
|
+
attrib_names.append(pollutant_names[i - enum_values_length])
|
|
1380
|
+
|
|
1381
|
+
subcatchment_values = dict(zip(attrib_names, <float[:length]>values))
|
|
1382
|
+
|
|
1383
|
+
if values != NULL:
|
|
1384
|
+
free(values)
|
|
1385
|
+
|
|
1386
|
+
return subcatchment_values
|
|
1387
|
+
|
|
1388
|
+
def get_node_values_by_time_and_element_index(
|
|
1389
|
+
self,
|
|
1390
|
+
time_index: int,
|
|
1391
|
+
element_index: Union[int, str]
|
|
1392
|
+
) -> Dict[str, float]:
|
|
1393
|
+
"""
|
|
1394
|
+
Method to get all attributes of a given node for specified time.
|
|
1395
|
+
|
|
1396
|
+
:param time_index: Time index.
|
|
1397
|
+
:type time_index: int
|
|
1398
|
+
|
|
1399
|
+
:param element_index: Index of the node.
|
|
1400
|
+
:type element_index: int
|
|
1401
|
+
|
|
1402
|
+
:return: Dictionary of node attributes.
|
|
1403
|
+
:rtype: Dict[str, float]
|
|
1404
|
+
"""
|
|
1405
|
+
|
|
1406
|
+
cdef int error_code = 0
|
|
1407
|
+
cdef float* values = NULL
|
|
1408
|
+
cdef int length = 0
|
|
1409
|
+
cdef int enum_values_length = len(NodeAttribute) - 1
|
|
1410
|
+
cdef list pollutant_names = self.get_element_names(ElementType.POLLUTANT)
|
|
1411
|
+
cdef list attrib_names = []
|
|
1412
|
+
cdef int l_element_index = element_index if isinstance(element_index, int) \
|
|
1413
|
+
else self._element_name_indexes[ElementType.NODE.name][element_index]
|
|
1414
|
+
|
|
1415
|
+
error_code = SMO_getNodeResult(
|
|
1416
|
+
p_handle=self._output_file_handle,
|
|
1417
|
+
timeIndex=time_index,
|
|
1418
|
+
nodeIndex=l_element_index,
|
|
1419
|
+
outValueArray=&values,
|
|
1420
|
+
length=&length
|
|
1421
|
+
)
|
|
1422
|
+
|
|
1423
|
+
self.__validate_error_code(error_code)
|
|
1424
|
+
|
|
1425
|
+
for i in range(length):
|
|
1426
|
+
if i < enum_values_length:
|
|
1427
|
+
attrib_names.append(NodeAttribute(i).name)
|
|
1428
|
+
else:
|
|
1429
|
+
attrib_names.append(pollutant_names[i - enum_values_length])
|
|
1430
|
+
|
|
1431
|
+
node_values = dict(zip(attrib_names, <float[:length]>values))
|
|
1432
|
+
|
|
1433
|
+
if values != NULL:
|
|
1434
|
+
free(values)
|
|
1435
|
+
|
|
1436
|
+
return node_values
|
|
1437
|
+
|
|
1438
|
+
def get_link_values_by_time_and_element_index(
|
|
1439
|
+
self,
|
|
1440
|
+
time_index: int,
|
|
1441
|
+
element_index: Union[int, str]
|
|
1442
|
+
) -> Dict[str, float]:
|
|
1443
|
+
"""
|
|
1444
|
+
Method to get all attributes of a given link for specified time.
|
|
1445
|
+
|
|
1446
|
+
:param time_index: Time index.
|
|
1447
|
+
:type time_index: int
|
|
1448
|
+
|
|
1449
|
+
:param element_index: Index of the link.
|
|
1450
|
+
:type element_index: int, str
|
|
1451
|
+
|
|
1452
|
+
:return: Dictionary of link attributes.
|
|
1453
|
+
:rtype: Dict[str, float]
|
|
1454
|
+
"""
|
|
1455
|
+
|
|
1456
|
+
cdef int error_code = 0
|
|
1457
|
+
cdef float* values = NULL
|
|
1458
|
+
cdef int length = 0
|
|
1459
|
+
cdef int enum_values_length = len(LinkAttribute) - 1
|
|
1460
|
+
cdef list pollutant_names = self.get_element_names(ElementType.POLLUTANT)
|
|
1461
|
+
cdef list attrib_names = []
|
|
1462
|
+
cdef int l_element_index = element_index if isinstance(element_index, int) \
|
|
1463
|
+
else self._element_name_indexes[ElementType.LINK.name][element_index]
|
|
1464
|
+
|
|
1465
|
+
error_code = SMO_getLinkResult(
|
|
1466
|
+
p_handle=self._output_file_handle,
|
|
1467
|
+
timeIndex=time_index,
|
|
1468
|
+
linkIndex=l_element_index,
|
|
1469
|
+
outValueArray=&values,
|
|
1470
|
+
length=&length
|
|
1471
|
+
)
|
|
1472
|
+
|
|
1473
|
+
self.__validate_error_code(error_code)
|
|
1474
|
+
|
|
1475
|
+
for i in range(length):
|
|
1476
|
+
if i < enum_values_length:
|
|
1477
|
+
attrib_names.append(LinkAttribute(i).name)
|
|
1478
|
+
else:
|
|
1479
|
+
attrib_names.append(pollutant_names[i - enum_values_length])
|
|
1480
|
+
|
|
1481
|
+
link_values = dict(zip(attrib_names, <float[:length]>values))
|
|
1482
|
+
|
|
1483
|
+
if values != NULL:
|
|
1484
|
+
free(values)
|
|
1485
|
+
|
|
1486
|
+
return link_values
|
|
1487
|
+
|
|
1488
|
+
def get_system_values_by_time(self, time_index: int) -> Dict[str, float]:
|
|
1489
|
+
"""
|
|
1490
|
+
Method to get all attributes of the system for specified time.
|
|
1491
|
+
|
|
1492
|
+
:param time_index: Time index.
|
|
1493
|
+
:type time_index: int
|
|
1494
|
+
|
|
1495
|
+
:return: Dictionary of system attributes.
|
|
1496
|
+
:rtype: Dict[str, float]
|
|
1497
|
+
"""
|
|
1498
|
+
|
|
1499
|
+
cdef int error_code = 0
|
|
1500
|
+
cdef float* values = NULL
|
|
1501
|
+
cdef int length = 0
|
|
1502
|
+
cdef int enum_values_length = len(SystemAttribute) - 1
|
|
1503
|
+
|
|
1504
|
+
error_code = SMO_getSystemResult(
|
|
1505
|
+
p_handle=self._output_file_handle,
|
|
1506
|
+
timeIndex=time_index,
|
|
1507
|
+
dummyIndex=0,
|
|
1508
|
+
outValueArray=&values,
|
|
1509
|
+
length=&length
|
|
1510
|
+
)
|
|
1511
|
+
|
|
1512
|
+
self.__validate_error_code(error_code)
|
|
1513
|
+
|
|
1514
|
+
system_values = dict(zip([SystemAttribute(i).name for i in range(enum_values_length)], <float[:length]>values))
|
|
1515
|
+
|
|
1516
|
+
if values != NULL:
|
|
1517
|
+
free(values)
|
|
1518
|
+
|
|
1519
|
+
return system_values
|
|
1520
|
+
|
|
1521
|
+
cdef str check_error(self):
|
|
1522
|
+
"""
|
|
1523
|
+
Method to check if there is an error in the SWMM output file instance reader.
|
|
1524
|
+
|
|
1525
|
+
:return: Error message if there is an error, otherwise None.
|
|
1526
|
+
:rtype: str
|
|
1527
|
+
"""
|
|
1528
|
+
cdef char* msg_buffer = NULL
|
|
1529
|
+
cdef int error_code
|
|
1530
|
+
|
|
1531
|
+
error_code = SMO_checkError(self._output_file_handle, &msg_buffer)
|
|
1532
|
+
|
|
1533
|
+
if error_code != 0 and msg_buffer != NULL:
|
|
1534
|
+
# Convert the C string to a Python string
|
|
1535
|
+
error_message = msg_buffer.decode('utf-8')
|
|
1536
|
+
# Free the allocated memory for the message buffer
|
|
1537
|
+
free(msg_buffer)
|
|
1538
|
+
return error_message
|
|
1539
|
+
else:
|
|
1540
|
+
return u""
|
|
1541
|
+
|
|
1542
|
+
cdef str __validate_error_code(self, int error_code):
|
|
1543
|
+
"""
|
|
1544
|
+
Method to validate the error code and return the error message if there is an error.
|
|
1545
|
+
|
|
1546
|
+
:param error_code: Error code to validate.
|
|
1547
|
+
:type error_code: int
|
|
1548
|
+
|
|
1549
|
+
:return: Error message if there is an error, otherwise None.
|
|
1550
|
+
:rtype: str
|
|
1551
|
+
"""
|
|
1552
|
+
|
|
1553
|
+
if error_code != 0:
|
|
1554
|
+
error_message = self.check_error()
|
|
1555
|
+
raise SWMMOutputException(f"Error code: {error_code}: {error_message}")
|
|
1556
|
+
else:
|
|
1557
|
+
return u""
|