easysewer 0.0.1__py3-none-any.whl → 0.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- easysewer/Area.py +617 -159
- easysewer/Link.py +461 -151
- easysewer/ModelAPI.py +194 -0
- easysewer/Node.py +971 -190
- easysewer/Rain.py +108 -19
- easysewer/UDM.py +47 -22
- easysewer/__init__.py +1 -1
- {easysewer-0.0.1.dist-info → easysewer-0.0.3.dist-info}/METADATA +1 -1
- easysewer-0.0.3.dist-info/RECORD +20 -0
- {easysewer-0.0.1.dist-info → easysewer-0.0.3.dist-info}/WHEEL +1 -1
- easysewer-0.0.1.dist-info/RECORD +0 -19
- {easysewer-0.0.1.dist-info → easysewer-0.0.3.dist-info}/top_level.txt +0 -0
easysewer/Rain.py
CHANGED
@@ -4,10 +4,70 @@ Rainfall Data Management Module
|
|
4
4
|
This module handles rainfall data input and processing, including rain gages,
|
5
5
|
time series data, and rainfall patterns for the drainage model.
|
6
6
|
"""
|
7
|
-
|
7
|
+
from warnings import warn
|
8
8
|
from .utils import *
|
9
9
|
|
10
10
|
|
11
|
+
class NamedList:
|
12
|
+
"""A list-like collection that allows access by index or name.
|
13
|
+
|
14
|
+
This class implements common list methods and adds the ability to access items
|
15
|
+
by their name attribute.
|
16
|
+
|
17
|
+
Attributes:
|
18
|
+
data (list): The underlying list of items
|
19
|
+
"""
|
20
|
+
|
21
|
+
def __init__(self, data=None):
|
22
|
+
self.data = data if data is not None else []
|
23
|
+
|
24
|
+
def __len__(self):
|
25
|
+
return len(self.data)
|
26
|
+
|
27
|
+
def __getitem__(self, key):
|
28
|
+
if isinstance(key, int):
|
29
|
+
return self.data[key]
|
30
|
+
elif isinstance(key, str):
|
31
|
+
for item in self.data:
|
32
|
+
if item.name == key:
|
33
|
+
return item
|
34
|
+
raise KeyError(f"No item found with name '{key}'")
|
35
|
+
else:
|
36
|
+
raise TypeError("Key must be an integer or a string")
|
37
|
+
|
38
|
+
def __iter__(self):
|
39
|
+
return iter(self.data)
|
40
|
+
|
41
|
+
def __contains__(self, item):
|
42
|
+
return item in self.data
|
43
|
+
|
44
|
+
def append(self, item):
|
45
|
+
"""Add an item to the collection.
|
46
|
+
|
47
|
+
Args:
|
48
|
+
item: The item to add
|
49
|
+
"""
|
50
|
+
self.data.append(item)
|
51
|
+
|
52
|
+
|
53
|
+
class TimeSeriesList(NamedList):
|
54
|
+
"""A specialized collection for TimeSeries objects.
|
55
|
+
|
56
|
+
Inherits all functionality from NamedList and may add TimeSeries-specific
|
57
|
+
methods in the future.
|
58
|
+
"""
|
59
|
+
pass
|
60
|
+
|
61
|
+
|
62
|
+
class RainGageList(NamedList):
|
63
|
+
"""A specialized collection for RainGage objects.
|
64
|
+
|
65
|
+
Inherits all functionality from NamedList and may add RainGage-specific
|
66
|
+
methods in the future.
|
67
|
+
"""
|
68
|
+
pass
|
69
|
+
|
70
|
+
|
11
71
|
def parse_swmm_datetime(date_str=None, time_str=None):
|
12
72
|
"""Convert SWMM date and time strings to minutes since start of day
|
13
73
|
|
@@ -56,21 +116,23 @@ class RainGage:
|
|
56
116
|
|
57
117
|
Attributes:
|
58
118
|
name (str): Unique identifier for the rain gage
|
59
|
-
|
119
|
+
form (str): Format of the rainfall data (INTENSITY/VOLUME/CUMULATIVE)
|
60
120
|
interval (float): Recording time interval
|
61
|
-
|
62
|
-
|
63
|
-
time_series (str): Name of associated time series data
|
121
|
+
source_type (str): Source type (TIMESERIES or FILE)
|
122
|
+
unit (str): Unit for FILE source (e.g., mm)
|
64
123
|
"""
|
65
124
|
def __init__(self):
|
66
125
|
self.name = ''
|
67
126
|
self.form = '' # INTENSIFY: mm/h
|
68
127
|
self.interval = ''
|
69
128
|
self.SCF = 1 # snow catch deficiency correction factor (use 1.0 for no adjustment)
|
70
|
-
self.source = '' # timeseries name
|
129
|
+
self.source = '' # timeseries name or file name
|
130
|
+
self.source_type = 'TIMESERIES' # TIMESERIES or FILE
|
131
|
+
self.station_id = None # Only for FILE source
|
132
|
+
self.unit = None # Only for FILE source
|
71
133
|
|
72
134
|
def __repr__(self):
|
73
|
-
return f'RainGage<{self.name}>: {self.source}'
|
135
|
+
return f'RainGage<{self.name}>: {self.source} ({self.source_type})'
|
74
136
|
|
75
137
|
|
76
138
|
class Rain:
|
@@ -85,8 +147,8 @@ class Rain:
|
|
85
147
|
ts_list (list): Collection of time series data
|
86
148
|
"""
|
87
149
|
def __init__(self):
|
88
|
-
self.ts_list =
|
89
|
-
self.gage_list =
|
150
|
+
self.ts_list = TimeSeriesList()
|
151
|
+
self.gage_list = RainGageList()
|
90
152
|
|
91
153
|
def __repr__(self):
|
92
154
|
if len(self.gage_list) == 0:
|
@@ -157,13 +219,34 @@ class Rain:
|
|
157
219
|
# rain gage section
|
158
220
|
content = get_swmm_inp_content(filename, '[RAINGAGES]')
|
159
221
|
for line in content:
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
222
|
+
parts = line.split()
|
223
|
+
if len(parts) < 6:
|
224
|
+
continue # skip malformed lines
|
225
|
+
source_type = parts[4]
|
226
|
+
if source_type == 'TIMESERIES' and len(parts) == 6:
|
227
|
+
name, form, interval, SCF, source_type, tise = parts
|
228
|
+
this_gage = RainGage()
|
229
|
+
this_gage.name = name
|
230
|
+
this_gage.form = form
|
231
|
+
this_gage.interval = interval
|
232
|
+
this_gage.SCF = SCF
|
233
|
+
this_gage.source_type = source_type
|
234
|
+
this_gage.source = tise
|
235
|
+
# unit remains default
|
236
|
+
elif source_type == 'FILE' and len(parts) == 8:
|
237
|
+
name, form, interval, SCF, source_type, filepath, station_id, unit = parts
|
238
|
+
this_gage = RainGage()
|
239
|
+
this_gage.name = name
|
240
|
+
this_gage.form = form
|
241
|
+
this_gage.interval = interval
|
242
|
+
this_gage.SCF = SCF
|
243
|
+
this_gage.source_type = source_type
|
244
|
+
this_gage.source = filepath
|
245
|
+
this_gage.station_id = station_id
|
246
|
+
this_gage.unit = unit
|
247
|
+
else:
|
248
|
+
warn(f'Failed to add rain gauge for content "{line}".')
|
249
|
+
continue # skip malformed lines
|
167
250
|
self.add_gage(this_gage)
|
168
251
|
return 0
|
169
252
|
|
@@ -202,8 +285,14 @@ class Rain:
|
|
202
285
|
f.write(';;\n')
|
203
286
|
|
204
287
|
f.write('\n\n[RAINGAGES]\n')
|
205
|
-
f.write(';;Name Format Interval SCF Source \n')
|
206
|
-
f.write(';;----- -------- --------- ----
|
288
|
+
f.write(';;Name Format Interval SCF SourceType Source [Unit]\n')
|
289
|
+
f.write(';;----- -------- --------- ---- ---------- ---------- -------\n')
|
207
290
|
for gage in self.gage_list:
|
208
|
-
|
291
|
+
if gage.source_type == 'TIMESERIES':
|
292
|
+
f.write(f'{gage.name} {gage.form} {gage.interval} {gage.SCF} TIMESERIES {gage.source}\n')
|
293
|
+
elif gage.source_type == 'FILE':
|
294
|
+
f.write(f'{gage.name} {gage.form} {gage.interval} {gage.SCF} FILE {gage.source} {gage.station_id} {gage.unit}\n')
|
295
|
+
else:
|
296
|
+
# fallback for unknown type
|
297
|
+
raise ValueError(f"Unknown source type: {gage.source_type}")
|
209
298
|
return 0
|
easysewer/UDM.py
CHANGED
@@ -11,6 +11,8 @@ The model supports:
|
|
11
11
|
- Handling rainfall and calculation settings
|
12
12
|
- Supporting various hydraulic elements like conduits and junctions
|
13
13
|
"""
|
14
|
+
import json
|
15
|
+
from pathlib import Path
|
14
16
|
|
15
17
|
from .Options import CalculationInformation
|
16
18
|
from .Link import LinkList
|
@@ -18,7 +20,6 @@ from .Node import NodeList
|
|
18
20
|
from .Area import AreaList
|
19
21
|
from .Rain import Rain
|
20
22
|
from .Curve import ValueList
|
21
|
-
import json
|
22
23
|
from .utils import get_swmm_inp_content
|
23
24
|
|
24
25
|
|
@@ -68,32 +69,56 @@ class UrbanDrainageModel:
|
|
68
69
|
|
69
70
|
def to_inp(self, filename):
|
70
71
|
"""
|
71
|
-
Writes the model to a SWMM .inp file.
|
72
|
-
|
72
|
+
Writes the model to a SWMM .inp file, creating parent directories if needed.
|
73
73
|
Args:
|
74
|
-
filename (str): Path to the output .inp file
|
75
|
-
|
74
|
+
filename (str or Path): Path to the output .inp file
|
76
75
|
Returns:
|
77
|
-
int: 0 on success
|
76
|
+
int: 0 on success, raises exceptions on failure
|
77
|
+
Raises:
|
78
|
+
OSError: If file operations fail
|
79
|
+
TypeError: If JSON serialization fails
|
78
80
|
"""
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
81
|
+
# Convert to Path object if it isn't already
|
82
|
+
filepath = Path(filename)
|
83
|
+
|
84
|
+
# Create parent directories if they don't exist
|
85
|
+
filepath.parent.mkdir(parents=True, exist_ok=True)
|
86
|
+
|
87
|
+
try:
|
88
|
+
with open(filepath, 'w', encoding='utf-8') as f:
|
89
|
+
# Write TITLE section first
|
90
|
+
f.write('[TITLE]\n')
|
91
|
+
if self.label:
|
92
|
+
try:
|
93
|
+
# Only attempt JSON if it's a dictionary
|
94
|
+
if isinstance(self.label, dict):
|
95
|
+
f.write(json.dumps(self.label, indent=2))
|
96
|
+
else:
|
97
|
+
f.write(str(self.label))
|
98
|
+
f.write('\n\n')
|
99
|
+
except (TypeError, ValueError) as e:
|
100
|
+
# Fallback to simple string representation
|
101
|
+
f.write(str(self.label.get('TITLE', '')) if isinstance(self.label, dict) else str(self.label))
|
102
|
+
f.write('\n\n')
|
103
|
+
|
104
|
+
# Continue with other sections - now using the same filepath
|
105
|
+
self.calc.write_to_swmm_inp(filepath)
|
106
|
+
self.node.write_to_swmm_inp(filepath)
|
107
|
+
self.link.write_to_swmm_inp(filepath)
|
108
|
+
self.area.write_to_swmm_inp(filepath)
|
109
|
+
self.rain.write_to_swmm_inp(filepath)
|
110
|
+
self.value.write_to_swmm_inp(filepath)
|
111
|
+
|
112
|
+
return 0
|
113
|
+
|
114
|
+
except Exception as e:
|
115
|
+
# Clean up partially written file if there was an error
|
116
|
+
if filepath.exists():
|
83
117
|
try:
|
84
|
-
|
85
|
-
f.write('\n\n')
|
118
|
+
filepath.unlink()
|
86
119
|
except:
|
87
|
-
|
88
|
-
|
89
|
-
# Continue with other sections
|
90
|
-
self.calc.write_to_swmm_inp(filename)
|
91
|
-
self.node.write_to_swmm_inp(filename)
|
92
|
-
self.link.write_to_swmm_inp(filename)
|
93
|
-
self.area.write_to_swmm_inp(filename)
|
94
|
-
self.rain.write_to_swmm_inp(filename)
|
95
|
-
self.value.write_to_swmm_inp(filename)
|
96
|
-
return 0
|
120
|
+
pass # Don't mask the original error
|
121
|
+
raise # Re-raise the original exception
|
97
122
|
|
98
123
|
def read_inp(self, filename):
|
99
124
|
"""
|
easysewer/__init__.py
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
easysewer/Area.py,sha256=Hw3xr-XiqAiTGVsG-fH71ovIBNnsRYBwmJrWG9l3YLA,32703
|
2
|
+
easysewer/Curve.py,sha256=C5X_CJ9CtuYcUFqdClFkxWYZOYHhQFBxENYg9jC6Dqs,4241
|
3
|
+
easysewer/Link.py,sha256=7hyQPme-ibnsgHO1VCjA0WgvPYflMgz3zCF8r5lhZ3g,23749
|
4
|
+
easysewer/ModelAPI.py,sha256=qJe8YroVostJ_Xwnlu0P9EdBy8FEFJ6vjev_Jn9sECs,7557
|
5
|
+
easysewer/Node.py,sha256=Cbexul4OdPWRhLW9h2fGjeMhLLwtDl0vGwmbACg_Hjs,45547
|
6
|
+
easysewer/Options.py,sha256=zsmyDpiQO0Swsrqkzw_vow4lNKbiLsLX_0YQ6XWsSBI,18425
|
7
|
+
easysewer/OutputAPI.py,sha256=tD6-6VjBpG-DBlmvVhb9siZ_zgEe1lD1d0h2xPr72Qw,7519
|
8
|
+
easysewer/Rain.py,sha256=Bb-SD-j7J0F6lWENsdU6Qc6Nr4ZcrCMOQcbJW3ZcbaQ,10865
|
9
|
+
easysewer/SolverAPI.py,sha256=H1KZkS_03Mv2ruSNX6-Rg_fesTwL9O6R5TRCil2eC7o,5612
|
10
|
+
easysewer/UDM.py,sha256=A2SCLhiM9LD05VVn0L8S8vIv6WgWxpl6_ckM_5JmRCI,5655
|
11
|
+
easysewer/__init__.py,sha256=8i9amFHLOrXYo3vjg6hCQPZ676d650UTCxPkblgyJvk,158
|
12
|
+
easysewer/utils.py,sha256=dkK5YwwaE2eLdiaY3YAEg78xv74TU6ff7Q3Rl3ltatI,2847
|
13
|
+
easysewer/libs/linux/libswmm5.so,sha256=UI5v1fCZ7LU9zkjLQMJqqJtzDefkFnL1iy3jmAlFsVo,839664
|
14
|
+
easysewer/libs/linux/swmm-output.so,sha256=248JHb0PUGyeo3tLj9fL4L114ySTSLBrUUA_k3VHnCQ,30928
|
15
|
+
easysewer/libs/win/swmm-output.dll,sha256=tN0K2ZRk7rlQZ-cLxlGLYZ6w269uvc5JuH1oVumADD0,71168
|
16
|
+
easysewer/libs/win/swmm5.dll,sha256=7LjGQXUyj8bdVuFzzY8-pObQV0VcUDI5KsAz-hstWls,915968
|
17
|
+
easysewer-0.0.3.dist-info/METADATA,sha256=G8-hiKnEacVIA20nHHrMwYbJvBI25EFoe70fpHg6kC4,586
|
18
|
+
easysewer-0.0.3.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
|
19
|
+
easysewer-0.0.3.dist-info/top_level.txt,sha256=YJi065ohgpDZhPb0XuXfE6ExHMhHPlwveH40BgZ7kt4,10
|
20
|
+
easysewer-0.0.3.dist-info/RECORD,,
|
easysewer-0.0.1.dist-info/RECORD
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
easysewer/Area.py,sha256=Y7SjlcA9byN4A3fk70D4M4hG8MulE0LLVJLqZ8FDt6c,13010
|
2
|
-
easysewer/Curve.py,sha256=C5X_CJ9CtuYcUFqdClFkxWYZOYHhQFBxENYg9jC6Dqs,4241
|
3
|
-
easysewer/Link.py,sha256=tZWGPg5EMcii6hfswNPp5MMz3wLdZPzuil9Sjv6OqXc,11802
|
4
|
-
easysewer/Node.py,sha256=trtXGPLDk1gkdKMXXwJ1hUOmsS6x2mVr4wh61Lxisqk,14092
|
5
|
-
easysewer/Options.py,sha256=zsmyDpiQO0Swsrqkzw_vow4lNKbiLsLX_0YQ6XWsSBI,18425
|
6
|
-
easysewer/OutputAPI.py,sha256=tD6-6VjBpG-DBlmvVhb9siZ_zgEe1lD1d0h2xPr72Qw,7519
|
7
|
-
easysewer/Rain.py,sha256=Hu_XtEXreIquf4uLxMgAiG_eRNc0OzuansDnZ1ozGAQ,7597
|
8
|
-
easysewer/SolverAPI.py,sha256=H1KZkS_03Mv2ruSNX6-Rg_fesTwL9O6R5TRCil2eC7o,5612
|
9
|
-
easysewer/UDM.py,sha256=DfEOFRyFR5guy7yEU_ve_XsRypXQ3k2nn5Slc5VAzOc,4385
|
10
|
-
easysewer/__init__.py,sha256=XD2zR0vua7-FMz5brRDY9FCzF_QNkPozfrOl7dvYALc,175
|
11
|
-
easysewer/utils.py,sha256=dkK5YwwaE2eLdiaY3YAEg78xv74TU6ff7Q3Rl3ltatI,2847
|
12
|
-
easysewer/libs/linux/libswmm5.so,sha256=UI5v1fCZ7LU9zkjLQMJqqJtzDefkFnL1iy3jmAlFsVo,839664
|
13
|
-
easysewer/libs/linux/swmm-output.so,sha256=248JHb0PUGyeo3tLj9fL4L114ySTSLBrUUA_k3VHnCQ,30928
|
14
|
-
easysewer/libs/win/swmm-output.dll,sha256=tN0K2ZRk7rlQZ-cLxlGLYZ6w269uvc5JuH1oVumADD0,71168
|
15
|
-
easysewer/libs/win/swmm5.dll,sha256=7LjGQXUyj8bdVuFzzY8-pObQV0VcUDI5KsAz-hstWls,915968
|
16
|
-
easysewer-0.0.1.dist-info/METADATA,sha256=GGX1eS11v3_hpeMxS1QsuqwJQi9wfJzEVZfVWTja_es,586
|
17
|
-
easysewer-0.0.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
18
|
-
easysewer-0.0.1.dist-info/top_level.txt,sha256=YJi065ohgpDZhPb0XuXfE6ExHMhHPlwveH40BgZ7kt4,10
|
19
|
-
easysewer-0.0.1.dist-info/RECORD,,
|
File without changes
|