easysewer 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
easysewer/Area.py ADDED
@@ -0,0 +1,294 @@
1
+ """
2
+ Subcatchment Area Management Module
3
+
4
+ This module handles subcatchment areas in the drainage network, including their
5
+ physical characteristics, infiltration parameters, and routing behavior.
6
+ """
7
+ from .utils import *
8
+
9
+
10
+ class InfiltrationHorton:
11
+ def __init__(self):
12
+ self.maximum_rate = 50 # mm/h
13
+ self.minimum_rate = 5 # mm/h
14
+ self.decay_rate = 5 # 1/h
15
+ self.dry_time = 7 # day
16
+ self.maximum_infiltration_volume = 0 # mm, 0 if not applicable
17
+
18
+
19
+ class InfiltrationGreenAmpt:
20
+ def __init__(self):
21
+ self.soil_capillary_suction = 0
22
+ self.soil_saturated_hydraulic_conductivity = 0
23
+ self.initial_soil_moisture_deficit = 0
24
+
25
+
26
+ class InfiltrationCurveNumber:
27
+ def __init__(self):
28
+ self.curve_number = 0
29
+ self.dry_time = 0
30
+ self.soil_saturated_hydraulic_conductivity = 0
31
+
32
+
33
+ class Infiltration:
34
+ def __init__(self):
35
+ self.horton = InfiltrationHorton()
36
+ self.green_ampt = InfiltrationGreenAmpt()
37
+ self.curve_number = InfiltrationCurveNumber()
38
+
39
+
40
+ class Polygon:
41
+ def __init__(self):
42
+ self.area_name = None
43
+ self.x = []
44
+ self.y = []
45
+
46
+
47
+ class Area:
48
+ """
49
+ Represents a subcatchment area in the drainage system.
50
+
51
+ Models a land area that generates runoff and routes it to a specific outlet point.
52
+ Includes properties for surface characteristics, infiltration, and routing.
53
+
54
+ Attributes:
55
+ name (str): Unique identifier for the subcatchment
56
+ rain_gage (str): Associated rain gage name
57
+ outlet (str): Outlet node name
58
+ area (float): Subcatchment area
59
+ impervious_ratio (float): Fraction of impervious area
60
+ width (float): Characteristic width of overland flow
61
+ slope (float): Average surface slope
62
+ curb_length (float): Length of curbs (for pollutant buildup)
63
+ snow_pack (str): Name of snow pack parameter set
64
+ manning_impervious (float): Manning's n for impervious area
65
+ manning_pervious (float): Manning's n for pervious area
66
+ depression_impervious (float): Depression storage for impervious area
67
+ depression_pervious (float): Depression storage for pervious area
68
+ impervious_without_depression (float): % of impervious area with no depression storage
69
+ route_type (str): Internal routing method
70
+ route_type_ratio (float): Fraction routed between subareas
71
+ infiltration (dict): Infiltration parameters
72
+ """
73
+ def __init__(self):
74
+ self.name = ''
75
+ self.rain_gage = ''
76
+ self.outlet = ''
77
+ #
78
+ self.area = 0.0
79
+ self.impervious_ratio = 0
80
+ self.width = 0
81
+ self.slope = 0
82
+ #
83
+ self.curb_length = 0
84
+ self.snow_pack = ''
85
+ #
86
+ self.manning_impervious = 0
87
+ self.manning_pervious = 0
88
+ self.depression_impervious = 0
89
+ self.depression_pervious = 0
90
+ self.impervious_without_depression = 0
91
+ #
92
+ self.route_type = 'OUTLET'
93
+ self.route_type_ratio = 100
94
+ #
95
+ self.infiltration = Infiltration()
96
+ #
97
+ self.polygon = Polygon()
98
+
99
+ def __repr__(self):
100
+ return f'Subcatchment<{self.name}>'
101
+
102
+
103
+ class AreaList:
104
+ def __init__(self):
105
+ self.data = []
106
+
107
+ def __repr__(self):
108
+ return f'{len(self.data)} Areas'
109
+
110
+ def __len__(self):
111
+ return len(self.data)
112
+
113
+ def __getitem__(self, key):
114
+ if isinstance(key, int):
115
+ return self.data[key]
116
+ elif isinstance(key, str):
117
+ for item in self.data:
118
+ if item.name == key:
119
+ return item
120
+ raise KeyError(f"No item found with name '{key}'")
121
+ else:
122
+ raise TypeError("Key must be an integer or a string")
123
+
124
+ def __iter__(self):
125
+ return iter(self.data)
126
+
127
+ def __contains__(self, item):
128
+ return item in self.data
129
+
130
+ def add_area(self, area_information):
131
+ new_area = Area()
132
+ if 'name' in area_information:
133
+ new_area.name = area_information['name']
134
+ if 'rain_gage' in area_information:
135
+ new_area.rain_gage = area_information['rain_gage']
136
+ if 'outlet' in area_information:
137
+ new_area.outlet = area_information['outlet']
138
+ #
139
+ if 'area' in area_information:
140
+ new_area.area = area_information['area']
141
+ if 'impervious_ratio' in area_information:
142
+ new_area.impervious_ratio = area_information['impervious_ratio']
143
+ if 'width' in area_information:
144
+ new_area.width = area_information['width']
145
+ if 'slope' in area_information:
146
+ new_area.slope = area_information['slope']
147
+ #
148
+ if 'curb_length' in area_information:
149
+ new_area.curb_length = area_information['curb_length']
150
+ if 'snow_pack' in area_information:
151
+ new_area.snow_pack = area_information['snow_pack']
152
+ #
153
+ if 'manning_impervious' in area_information:
154
+ new_area.manning_impervious = area_information['manning_impervious']
155
+ if 'manning_pervious' in area_information:
156
+ new_area.manning_pervious = area_information['manning_pervious']
157
+ if 'depression_impervious' in area_information:
158
+ new_area.depression_impervious = area_information['depression_impervious']
159
+ if 'depression_pervious' in area_information:
160
+ new_area.depression_pervious = area_information['depression_pervious']
161
+ if 'impervious_without_depression' in area_information:
162
+ new_area.impervious_without_depression = area_information['impervious_without_depression']
163
+ #
164
+ if 'route_type' in area_information:
165
+ new_area.route_type = area_information['route_type']
166
+ if 'route_type_ratio' in area_information:
167
+ new_area.route_type_ratio = area_information['route_type_ratio']
168
+ #
169
+ if 'infiltration' in area_information:
170
+ new_area.infiltration = area_information['infiltration']
171
+ #
172
+ #
173
+ self.data.append(new_area)
174
+
175
+ def read_from_swmm_inp(self, filename, infiltration_type='Horton'):
176
+ sub_contents = get_swmm_inp_content(filename, '[SUBCATCHMENTS]')
177
+ # fill in default values
178
+ for index, line in enumerate(sub_contents):
179
+ if len(line.split()) == 8:
180
+ sub_contents[index] += ' VOID'
181
+ #
182
+ subarea_contents = get_swmm_inp_content(filename, '[SUBAREAS]')
183
+ # fill in default values
184
+ for index, line in enumerate(subarea_contents):
185
+ if len(line.split()) == 7:
186
+ subarea_contents[index] += ' 100'
187
+ content = combine_swmm_inp_contents(sub_contents, subarea_contents)
188
+ #
189
+ infiltration_contents = get_swmm_inp_content(filename, '[INFILTRATION]')
190
+ content = combine_swmm_inp_contents(content, infiltration_contents)
191
+
192
+ for line in content:
193
+ pair = line.split()
194
+ dic = {'name': pair[0],
195
+ 'rain_gage': pair[1],
196
+ 'outlet': pair[2],
197
+ 'area': float(pair[3]),
198
+ 'impervious_ratio': float(pair[4]),
199
+ 'width': float(pair[5]),
200
+ 'slope': float(pair[6]),
201
+ 'curb_length': float(pair[7]),
202
+ 'manning_impervious': float(pair[9]),
203
+ 'manning_pervious': float(pair[10]),
204
+ 'depression_impervious': float(pair[11]),
205
+ 'depression_pervious': float(pair[12]),
206
+ 'impervious_without_depression': float(pair[13]),
207
+ 'route_type': pair[14]
208
+ }
209
+ if dic['curb_length'] < 10e-5:
210
+ dic['curb_length'] = int(0)
211
+ #
212
+ if pair[8] != 'VOID':
213
+ dic['snow_pack'] = pair[8]
214
+ if pair[15] != '100':
215
+ dic['route_type_ratio'] = float(pair[15])
216
+ #
217
+ new_infiltration = Infiltration()
218
+
219
+ match infiltration_type:
220
+ case 'Horton':
221
+ new_infiltration.horton.maximum_rate = float(pair[16])
222
+ new_infiltration.horton.minimum_rate = float(pair[17])
223
+ new_infiltration.horton.decay_rate = float(pair[18])
224
+ new_infiltration.horton.dry_time = float(pair[19])
225
+ new_infiltration.horton.maximum_infiltration_volume = float(pair[20])
226
+ case 'GreenAmpt':
227
+ new_infiltration.green_ampt.soil_capillary_suction = float(pair[16])
228
+ new_infiltration.green_ampt.soil_saturated_hydraulic_conductivity = float(pair[17])
229
+ new_infiltration.green_ampt.initial_soil_moisture_deficit = float(pair[18])
230
+ case 'CurveNumber':
231
+ new_infiltration.curve_number.curve_number = float(pair[16])
232
+ new_infiltration.curve_number.soil_saturated_hydraulic_conductivity = float(pair[17])
233
+ new_infiltration.curve_number.dry_time = float(pair[18])
234
+
235
+ dic['infiltration'] = new_infiltration
236
+ #
237
+ self.add_area(dic)
238
+
239
+ #
240
+ polygon_contents = get_swmm_inp_content(filename, '[Polygons]')
241
+ for line in polygon_contents:
242
+ pair = line.split()
243
+ for area in self.data:
244
+ if area.name == pair[0]:
245
+ area.polygon.x.append(float(pair[1]))
246
+ area.polygon.y.append(float(pair[2]))
247
+ area.polygon.area_name = pair[0]
248
+ return 0
249
+
250
+ def write_to_swmm_inp(self, filename, infiltration_type='Horton'):
251
+ with open(filename, 'a', encoding='utf-8') as f:
252
+ f.write('\n\n[SUBCATCHMENTS]\n')
253
+ f.write(
254
+ ';;Name RainGage Outlet Area %Imperv Width %Slope CurbLen (SnowPack)\n')
255
+ for area in self.data:
256
+ f.write(
257
+ f'{area.name} {area.rain_gage} {area.outlet} {area.area:8.3f} {area.impervious_ratio:8.2f} {area.width:8.3f} {area.slope:8.2f} {area.curb_length:8} {area.snow_pack}\n')
258
+ #
259
+ f.write('\n\n[SUBAREAS]\n')
260
+ f.write(';;Subcatchment N-Imperv N-Perv S-Imperv S-Perv PctZero RouteTo (PctRouted)\n')
261
+ for area in self.data:
262
+ if area.route_type_ratio != 100:
263
+ f.write(
264
+ f'{area.name} {area.manning_impervious:8.3f} {area.manning_pervious:8.2f} {area.depression_impervious:8.2f} {area.depression_pervious:8.2f} {area.impervious_without_depression:8.2f} {area.route_type:8} {area.route_type_ratio:8}\n')
265
+ else:
266
+ f.write(
267
+ f'{area.name} {area.manning_impervious:8.3f} {area.manning_pervious:8.2f} {area.depression_impervious:8.2f} {area.depression_pervious:8.2f} {area.impervious_without_depression:8.2f} {area.route_type:8}\n')
268
+ #
269
+ f.write('\n\n[INFILTRATION]\n')
270
+ match infiltration_type:
271
+ case 'Horton':
272
+ f.write(';;;;Subcatchment MaxRate MinRate Decay DryTime MaxInfil \n')
273
+ for area in self.data:
274
+ f.write(
275
+ f'{area.name} {area.infiltration.horton.maximum_rate:8.1f} {area.infiltration.horton.minimum_rate:8.1f} {area.infiltration.horton.decay_rate:8.1f} {area.infiltration.horton.dry_time:8.1f} {area.infiltration.horton.maximum_infiltration_volume:8.1f}\n')
276
+ case 'GreenAmpt':
277
+ f.write(';;;;Subcatchment \n')
278
+ for area in self.data:
279
+ f.write(
280
+ f'{area.name} {area.infiltration.green_ampt.soil_capillary_suction:8} {area.infiltration.green_ampt.soil_saturated_hydraulic_conductivity:8} {area.infiltration.green_ampt.initial_soil_moisture_deficit:8}\n')
281
+ case 'CurveNumber':
282
+ f.write(';;;;Subcatchment \n')
283
+ for area in self.data:
284
+ f.write(
285
+ f'{area.name} {area.infiltration.curve_number.curve_number:8} {area.infiltration.curve_number.soil_saturated_hydraulic_conductivity:8} {area.infiltration.curve_number.dry_time:8}\n')
286
+ #
287
+ f.write('\n\n[Polygons]\n')
288
+ f.write(';;Subcatchment X-Coord Y-Coord\n')
289
+ for area in self.data:
290
+ if area.polygon.area_name is not None:
291
+ for xi, yi in zip(area.polygon.x, area.polygon.y):
292
+ f.write(f'{area.polygon.area_name} {xi} {yi}\n')
293
+ return 0
294
+
easysewer/Curve.py ADDED
@@ -0,0 +1,119 @@
1
+ """
2
+ Curve and Pattern Management Module
3
+
4
+ This module handles various types of curves and patterns used in the drainage model,
5
+ including rating curves, shape curves, and time patterns for different parameters.
6
+ """
7
+ from .utils import *
8
+
9
+
10
+ class Curve:
11
+ def __init__(self):
12
+ self.name = ''
13
+ self.type = ''
14
+ self.x = []
15
+ self.y = []
16
+
17
+
18
+ class Pattern:
19
+ def __init__(self):
20
+ self.name = ''
21
+ self.type = ''
22
+ self.value = []
23
+
24
+
25
+ class ValueList:
26
+ """
27
+ Container for curves and patterns used in the model.
28
+
29
+ Manages collections of curves (rating curves, shape curves) and patterns
30
+ (time patterns) used throughout the drainage model.
31
+
32
+ Attributes:
33
+ curve_list (list): Collection of curve objects
34
+ pattern_list (list): Collection of pattern objects
35
+ """
36
+ def __init__(self):
37
+ self.curve_list = []
38
+ self.pattern_list = []
39
+
40
+ def __repr__(self):
41
+ return 'ValueList'
42
+
43
+ def add_curve(self, new_curve):
44
+ self.curve_list.append(new_curve)
45
+
46
+ def add_pattern(self, new_pattern):
47
+ self.pattern_list.append(new_pattern)
48
+
49
+ def read_from_swmm_inp(self, filename):
50
+ #
51
+ content = get_swmm_inp_content(filename, '[CURVES]')
52
+ this_curve = Curve()
53
+ this_curve.name = 'initial'
54
+ for line in content:
55
+ pair = line.split()
56
+ name = pair[0]
57
+ if this_curve.name == 'initial':
58
+ this_curve.name = name
59
+ if this_curve.name != name:
60
+ self.add_curve(this_curve)
61
+ this_curve = Curve()
62
+ this_curve.name = name
63
+ if len(pair) % 2 == 0:
64
+ this_curve.type = pair[1]
65
+ x_list = [float(i) for index, i in enumerate(pair[2::]) if index % 2 == 0]
66
+ y_list = [float(i) for index, i in enumerate(pair[2::]) if index % 2 == 1]
67
+ else:
68
+ x_list = [float(i) for index, i in enumerate(pair[1::]) if index % 2 == 0]
69
+ y_list = [float(i) for index, i in enumerate(pair[1::]) if index % 2 == 1]
70
+ for x, y in zip(x_list, y_list):
71
+ this_curve.x.append(x)
72
+ this_curve.y.append(y)
73
+ if this_curve.name != 'initial':
74
+ self.add_curve(this_curve)
75
+ #
76
+ content = get_swmm_inp_content(filename, '[PATTERNS]')
77
+ this_pattern = Pattern()
78
+ this_pattern.name = 'initial'
79
+ for line in content:
80
+ pair = line.split()
81
+ name = pair[0]
82
+ if this_pattern.name == 'initial':
83
+ this_pattern.name = name
84
+ if this_pattern.name != name:
85
+ self.add_pattern(this_pattern)
86
+ this_pattern = Pattern()
87
+ this_pattern.name = name
88
+ if pair[1].isalpha():
89
+ this_pattern.type = pair[1]
90
+ for factor in pair[2::]:
91
+ this_pattern.value.append(factor)
92
+ else:
93
+ for factor in pair[1::]:
94
+ this_pattern.value.append(factor)
95
+ if this_pattern.name != 'initial':
96
+ self.add_pattern(this_pattern)
97
+ return 0
98
+
99
+ def write_to_swmm_inp(self, filename):
100
+ with open(filename, 'a', encoding='utf-8') as f:
101
+ f.write('\n\n[CURVES]\n')
102
+ f.write(';;Name Type X-Value Y-Value \n')
103
+ for curve in self.curve_list:
104
+ flag = 0
105
+ for x, y in zip(curve.x, curve.y):
106
+ if flag == 0:
107
+ f.write(f'{curve.name} {curve.type:8} {x} {y}\n')
108
+ flag = 1
109
+ else:
110
+ f.write(f'{curve.name} {x} {y}\n')
111
+ f.write(';\n')
112
+ #
113
+ f.write('\n\n[PATTERNS]\n')
114
+ f.write(';;Name Type Multipliers\n')
115
+ for pattern in self.pattern_list:
116
+ string = ' '.join(pattern.value)
117
+ f.write(f'{pattern.name} {pattern.type} {string}\n')
118
+ f.write(';\n')
119
+ return 0