easysewer 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- easysewer/Area.py +294 -0
- easysewer/Curve.py +119 -0
- easysewer/Link.py +294 -0
- easysewer/Node.py +346 -0
- easysewer/Options.py +373 -0
- easysewer/OutputAPI.py +179 -0
- easysewer/Rain.py +209 -0
- easysewer/SolverAPI.py +154 -0
- easysewer/UDM.py +126 -0
- easysewer/__init__.py +7 -0
- easysewer/libs/linux/libswmm5.so +0 -0
- easysewer/libs/linux/swmm-output.so +0 -0
- easysewer/libs/win/swmm-output.dll +0 -0
- easysewer/libs/win/swmm5.dll +0 -0
- easysewer/utils.py +97 -0
- easysewer-0.0.1.dist-info/METADATA +16 -0
- easysewer-0.0.1.dist-info/RECORD +19 -0
- easysewer-0.0.1.dist-info/WHEEL +5 -0
- easysewer-0.0.1.dist-info/top_level.txt +1 -0
easysewer/Rain.py
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
"""
|
2
|
+
Rainfall Data Management Module
|
3
|
+
|
4
|
+
This module handles rainfall data input and processing, including rain gages,
|
5
|
+
time series data, and rainfall patterns for the drainage model.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from .utils import *
|
9
|
+
|
10
|
+
|
11
|
+
def parse_swmm_datetime(date_str=None, time_str=None):
|
12
|
+
"""Convert SWMM date and time strings to minutes since start of day
|
13
|
+
|
14
|
+
Args:
|
15
|
+
date_str: Optional date string in format 'MM/DD/YYYY'
|
16
|
+
time_str: Time string in format 'HH:MM' or 'H:MM'
|
17
|
+
|
18
|
+
Returns:
|
19
|
+
minutes: Integer minutes since start of day
|
20
|
+
"""
|
21
|
+
if date_str is None:
|
22
|
+
# Just parse time when no date provided
|
23
|
+
hours, minutes = time_str.split(':')
|
24
|
+
return int(minutes) + 60 * int(hours)
|
25
|
+
|
26
|
+
# Parse date and time when both provided
|
27
|
+
from datetime import datetime
|
28
|
+
dt = datetime.strptime(f"{date_str} {time_str}", "%m/%d/%Y %H:%M")
|
29
|
+
return dt.hour * 60 + dt.minute
|
30
|
+
|
31
|
+
|
32
|
+
class TimeSeries:
|
33
|
+
def __init__(self):
|
34
|
+
self.name = ''
|
35
|
+
self.time = [] # in minutes
|
36
|
+
self.value = [] # in mm
|
37
|
+
self.has_date = False # whether the timeseries includes date information
|
38
|
+
self.start_date = None # store the start date if available
|
39
|
+
|
40
|
+
def __repr__(self):
|
41
|
+
if len(self.time) == 0:
|
42
|
+
return 'None'
|
43
|
+
else:
|
44
|
+
interval = self.time[1] - self.time[0]
|
45
|
+
total = sum([(v * interval / 60) for v in self.value])
|
46
|
+
total = round(total, 2)
|
47
|
+
return f'{self.name}: {self.time[-1]}min - {total}mm'
|
48
|
+
|
49
|
+
|
50
|
+
class RainGage:
|
51
|
+
"""
|
52
|
+
Represents a rain gage station in the model.
|
53
|
+
|
54
|
+
Defines characteristics of rainfall measurement points including data format,
|
55
|
+
time intervals, and data source.
|
56
|
+
|
57
|
+
Attributes:
|
58
|
+
name (str): Unique identifier for the rain gage
|
59
|
+
format (str): Format of the rainfall data (INTENSITY/VOLUME/CUMULATIVE)
|
60
|
+
interval (float): Recording time interval
|
61
|
+
snow_catch (float): Snow catch deficiency correction
|
62
|
+
data_source (str): Source of the rainfall data
|
63
|
+
time_series (str): Name of associated time series data
|
64
|
+
"""
|
65
|
+
def __init__(self):
|
66
|
+
self.name = ''
|
67
|
+
self.form = '' # INTENSIFY: mm/h
|
68
|
+
self.interval = ''
|
69
|
+
self.SCF = 1 # snow catch deficiency correction factor (use 1.0 for no adjustment)
|
70
|
+
self.source = '' # timeseries name
|
71
|
+
|
72
|
+
def __repr__(self):
|
73
|
+
return f'RainGage<{self.name}>: {self.source}'
|
74
|
+
|
75
|
+
|
76
|
+
class Rain:
|
77
|
+
"""
|
78
|
+
Container class for managing rainfall data.
|
79
|
+
|
80
|
+
Manages collection of rain gages and their associated time series data
|
81
|
+
for the drainage model.
|
82
|
+
|
83
|
+
Attributes:
|
84
|
+
gage_list (list): Collection of RainGage objects
|
85
|
+
ts_list (list): Collection of time series data
|
86
|
+
"""
|
87
|
+
def __init__(self):
|
88
|
+
self.ts_list = []
|
89
|
+
self.gage_list = []
|
90
|
+
|
91
|
+
def __repr__(self):
|
92
|
+
if len(self.gage_list) == 0:
|
93
|
+
return 'None'
|
94
|
+
elif len(self.gage_list) == 1:
|
95
|
+
return f'{self.gage_list[0]}'
|
96
|
+
else:
|
97
|
+
return 'Gages'
|
98
|
+
|
99
|
+
def add_ts(self, new_ts):
|
100
|
+
self.ts_list.append(new_ts)
|
101
|
+
|
102
|
+
def add_gage(self, new_gage):
|
103
|
+
self.gage_list.append(new_gage)
|
104
|
+
|
105
|
+
def read_from_swmm_inp(self, filename):
|
106
|
+
from datetime import datetime
|
107
|
+
|
108
|
+
content = get_swmm_inp_content(filename, '[TIMESERIES]')
|
109
|
+
this_timeseries = TimeSeries()
|
110
|
+
this_timeseries.name = 'initial'
|
111
|
+
|
112
|
+
for line in content:
|
113
|
+
parts = line.split()
|
114
|
+
|
115
|
+
# Skip empty lines or invalid formats
|
116
|
+
if len(parts) < 3:
|
117
|
+
continue
|
118
|
+
|
119
|
+
# Determine if this line has date information
|
120
|
+
has_date = len(parts) == 4
|
121
|
+
|
122
|
+
if has_date:
|
123
|
+
name, date, time, value = parts
|
124
|
+
minutes = parse_swmm_datetime(date_str=date, time_str=time)
|
125
|
+
else:
|
126
|
+
name, time, value = parts
|
127
|
+
minutes = parse_swmm_datetime(time_str=time)
|
128
|
+
|
129
|
+
value = float(value)
|
130
|
+
|
131
|
+
# Handle first timeseries
|
132
|
+
if this_timeseries.name == 'initial':
|
133
|
+
this_timeseries.name = name
|
134
|
+
this_timeseries.has_date = has_date
|
135
|
+
if has_date:
|
136
|
+
this_timeseries.start_date = datetime.strptime(date, "%m/%d/%Y").date()
|
137
|
+
|
138
|
+
# If we encounter a new timeseries name
|
139
|
+
if this_timeseries.name != name:
|
140
|
+
# Save the current timeseries
|
141
|
+
self.add_ts(this_timeseries)
|
142
|
+
|
143
|
+
# Start a new timeseries
|
144
|
+
this_timeseries = TimeSeries()
|
145
|
+
this_timeseries.name = name
|
146
|
+
this_timeseries.has_date = has_date
|
147
|
+
if has_date:
|
148
|
+
this_timeseries.start_date = datetime.strptime(date, "%m/%d/%Y").date()
|
149
|
+
|
150
|
+
# Add the data point
|
151
|
+
this_timeseries.time.append(minutes)
|
152
|
+
this_timeseries.value.append(value)
|
153
|
+
|
154
|
+
# Add the last timeseries if it exists
|
155
|
+
if this_timeseries.name != 'initial':
|
156
|
+
self.add_ts(this_timeseries)
|
157
|
+
# rain gage section
|
158
|
+
content = get_swmm_inp_content(filename, '[RAINGAGES]')
|
159
|
+
for line in content:
|
160
|
+
name, form, interval, SCF, _, tise = line.split()
|
161
|
+
this_gage = RainGage()
|
162
|
+
this_gage.name = name
|
163
|
+
this_gage.form = form
|
164
|
+
this_gage.interval = interval
|
165
|
+
this_gage.SCF = SCF
|
166
|
+
this_gage.source = tise
|
167
|
+
self.add_gage(this_gage)
|
168
|
+
return 0
|
169
|
+
|
170
|
+
def write_to_swmm_inp(self, filename):
|
171
|
+
from datetime import datetime, timedelta
|
172
|
+
|
173
|
+
def time_minute2text(minutes):
|
174
|
+
minutes = int(minutes)
|
175
|
+
hours, left = divmod(minutes, 60)
|
176
|
+
text = f'{hours}:{left:02}'
|
177
|
+
return text
|
178
|
+
|
179
|
+
def get_date_for_minutes(start_date, minutes):
|
180
|
+
"""Convert minutes to date string, handling day rollovers"""
|
181
|
+
days, remaining_minutes = divmod(minutes, 24 * 60)
|
182
|
+
date = start_date + timedelta(days=days)
|
183
|
+
return date.strftime("%m/%d/%Y")
|
184
|
+
|
185
|
+
with open(filename, 'a', encoding='utf-8') as f:
|
186
|
+
f.write('\n\n[TIMESERIES]\n')
|
187
|
+
if any(ts.has_date for ts in self.ts_list):
|
188
|
+
f.write(';;Name Date Time Value\n')
|
189
|
+
f.write(';;-------------- ---------- ---------- ----------\n')
|
190
|
+
else:
|
191
|
+
f.write(';;Name Time Value\n')
|
192
|
+
f.write(';;---------- ---------- ----------\n')
|
193
|
+
|
194
|
+
for ts in self.ts_list:
|
195
|
+
for time, value in zip(ts.time, ts.value):
|
196
|
+
if ts.has_date:
|
197
|
+
date_str = get_date_for_minutes(ts.start_date, time)
|
198
|
+
time_str = time_minute2text(time % (24 * 60)) # Get time within the day
|
199
|
+
f.write(f'{ts.name:<14} {date_str} {time_str} {value:>.2f}\n')
|
200
|
+
else:
|
201
|
+
f.write(f'{ts.name} {time_minute2text(time)} {value:>.2f}\n')
|
202
|
+
f.write(';;\n')
|
203
|
+
|
204
|
+
f.write('\n\n[RAINGAGES]\n')
|
205
|
+
f.write(';;Name Format Interval SCF Source \n')
|
206
|
+
f.write(';;----- -------- --------- ---- ----------\n')
|
207
|
+
for gage in self.gage_list:
|
208
|
+
f.write(f'{gage.name} {gage.form} {gage.interval} {gage.SCF} TIMESERIES {gage.source}\n')
|
209
|
+
return 0
|
easysewer/SolverAPI.py
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
"""
|
2
|
+
pass
|
3
|
+
"""
|
4
|
+
import platform
|
5
|
+
import os
|
6
|
+
from ctypes import CDLL, c_char_p, c_int, c_double, c_float, byref, POINTER, create_string_buffer
|
7
|
+
|
8
|
+
|
9
|
+
class SWMMSolverAPI:
|
10
|
+
def __init__(self):
|
11
|
+
#
|
12
|
+
system = platform.system()
|
13
|
+
if system == 'Windows':
|
14
|
+
lib_path = os.path.join(os.path.dirname(__file__), 'libs', 'win', 'swmm5.dll')
|
15
|
+
elif system == 'Linux':
|
16
|
+
lib_path = os.path.join(os.path.dirname(__file__), 'libs', 'linux', 'libswmm5.so')
|
17
|
+
else:
|
18
|
+
raise OSError('Unsupported operating system')
|
19
|
+
|
20
|
+
self.swmm = CDLL(lib_path)
|
21
|
+
self._set_prototypes()
|
22
|
+
|
23
|
+
def _set_prototypes(self):
|
24
|
+
self.swmm.swmm_run.argtypes = [c_char_p, c_char_p, c_char_p]
|
25
|
+
self.swmm.swmm_run.restype = c_int
|
26
|
+
|
27
|
+
self.swmm.swmm_open.argtypes = [c_char_p, c_char_p, c_char_p]
|
28
|
+
self.swmm.swmm_open.restype = c_int
|
29
|
+
|
30
|
+
self.swmm.swmm_start.argtypes = [c_int]
|
31
|
+
self.swmm.swmm_start.restype = c_int
|
32
|
+
|
33
|
+
self.swmm.swmm_step.argtypes = [POINTER(c_double)]
|
34
|
+
self.swmm.swmm_step.restype = c_int
|
35
|
+
|
36
|
+
self.swmm.swmm_end.argtypes = []
|
37
|
+
self.swmm.swmm_end.restype = c_int
|
38
|
+
|
39
|
+
self.swmm.swmm_report.argtypes = []
|
40
|
+
self.swmm.swmm_report.restype = c_int
|
41
|
+
|
42
|
+
self.swmm.swmm_close.argtypes = []
|
43
|
+
self.swmm.swmm_close.restype = c_int
|
44
|
+
|
45
|
+
self.swmm.swmm_getMassBalErr.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float)]
|
46
|
+
self.swmm.swmm_getMassBalErr.restype = c_int
|
47
|
+
|
48
|
+
self.swmm.swmm_getVersion.argtypes = []
|
49
|
+
self.swmm.swmm_getVersion.restype = c_int
|
50
|
+
|
51
|
+
self.swmm.swmm_getError.argtypes = [c_char_p, c_int]
|
52
|
+
self.swmm.swmm_getError.restype = c_int
|
53
|
+
|
54
|
+
self.swmm.swmm_getWarnings.argtypes = []
|
55
|
+
self.swmm.swmm_getWarnings.restype = c_int
|
56
|
+
|
57
|
+
self.swmm.swmm_getCount.argtypes = [c_int]
|
58
|
+
self.swmm.swmm_getCount.restype = c_int
|
59
|
+
|
60
|
+
self.swmm.swmm_getName.argtypes = [c_int, c_int, c_char_p, c_int]
|
61
|
+
self.swmm.swmm_getName.restype = None
|
62
|
+
|
63
|
+
self.swmm.swmm_getIndex.argtypes = [c_int, c_char_p]
|
64
|
+
self.swmm.swmm_getIndex.restype = c_int
|
65
|
+
|
66
|
+
self.swmm.swmm_getValue.argtypes = [c_int, c_int]
|
67
|
+
self.swmm.swmm_getValue.restype = c_double
|
68
|
+
|
69
|
+
self.swmm.swmm_setValue.argtypes = [c_int, c_int, c_double]
|
70
|
+
self.swmm.swmm_setValue.restype = None
|
71
|
+
|
72
|
+
self.swmm.swmm_getSavedValue.argtypes = [c_int, c_int, c_int]
|
73
|
+
self.swmm.swmm_getSavedValue.restype = c_double
|
74
|
+
|
75
|
+
self.swmm.swmm_writeLine.argtypes = [c_char_p]
|
76
|
+
self.swmm.swmm_writeLine.restype = None
|
77
|
+
|
78
|
+
self.swmm.swmm_decodeDate.argtypes = [c_double, POINTER(c_int), POINTER(c_int), POINTER(c_int), POINTER(c_int), POINTER(c_int), POINTER(c_int), POINTER(c_int)]
|
79
|
+
self.swmm.swmm_decodeDate.restype = None
|
80
|
+
|
81
|
+
def run(self, input_file, report_file, output_file):
|
82
|
+
return self.swmm.swmm_run(input_file.encode('utf-8'), report_file.encode('utf-8'), output_file.encode('utf-8'))
|
83
|
+
|
84
|
+
def open(self, input_file, report_file, output_file):
|
85
|
+
return self.swmm.swmm_open(input_file.encode('utf-8'), report_file.encode('utf-8'), output_file.encode('utf-8'))
|
86
|
+
|
87
|
+
def start(self, save_flag):
|
88
|
+
return self.swmm.swmm_start(save_flag)
|
89
|
+
|
90
|
+
def step(self):
|
91
|
+
elapsed_time = c_double()
|
92
|
+
result = self.swmm.swmm_step(byref(elapsed_time))
|
93
|
+
return result, elapsed_time.value
|
94
|
+
|
95
|
+
def end(self):
|
96
|
+
return self.swmm.swmm_end()
|
97
|
+
|
98
|
+
def report(self):
|
99
|
+
return self.swmm.swmm_report()
|
100
|
+
|
101
|
+
def close(self):
|
102
|
+
return self.swmm.swmm_close()
|
103
|
+
|
104
|
+
def get_mass_bal_err(self):
|
105
|
+
runoff_err = c_float()
|
106
|
+
flow_err = c_float()
|
107
|
+
qual_err = c_float()
|
108
|
+
self.swmm.swmm_getMassBalErr(byref(runoff_err), byref(flow_err), byref(qual_err))
|
109
|
+
return runoff_err.value, flow_err.value, qual_err.value
|
110
|
+
|
111
|
+
def get_version(self):
|
112
|
+
return self.swmm.swmm_getVersion()
|
113
|
+
|
114
|
+
def get_error(self, msg_len=256):
|
115
|
+
err_msg = create_string_buffer(msg_len)
|
116
|
+
self.swmm.swmm_getError(err_msg, msg_len)
|
117
|
+
return err_msg.value.decode('utf-8')
|
118
|
+
|
119
|
+
def get_warnings(self):
|
120
|
+
return self.swmm.swmm_getWarnings()
|
121
|
+
|
122
|
+
def get_count(self, obj_type):
|
123
|
+
return self.swmm.swmm_getCount(obj_type)
|
124
|
+
|
125
|
+
def get_name(self, obj_type, index, size=256):
|
126
|
+
name = create_string_buffer(size)
|
127
|
+
self.swmm.swmm_getName(obj_type, index, name, size)
|
128
|
+
return name.value.decode('utf-8')
|
129
|
+
|
130
|
+
def get_index(self, obj_type, name):
|
131
|
+
return self.swmm.swmm_getIndex(obj_type, name.encode('utf-8'))
|
132
|
+
|
133
|
+
def get_value(self, property, index):
|
134
|
+
return self.swmm.swmm_getValue(property, index)
|
135
|
+
|
136
|
+
def set_value(self, property, index, value):
|
137
|
+
self.swmm.swmm_setValue(property, index, value)
|
138
|
+
|
139
|
+
def get_saved_value(self, property, index, period):
|
140
|
+
return self.swmm.swmm_getSavedValue(property, index, period)
|
141
|
+
|
142
|
+
def write_line(self, line):
|
143
|
+
self.swmm.swmm_writeLine(line.encode('utf-8'))
|
144
|
+
|
145
|
+
def decode_date(self, date):
|
146
|
+
year = c_int()
|
147
|
+
month = c_int()
|
148
|
+
day = c_int()
|
149
|
+
hour = c_int()
|
150
|
+
minute = c_int()
|
151
|
+
second = c_int()
|
152
|
+
day_of_week = c_int()
|
153
|
+
self.swmm.swmm_decodeDate(date, byref(year), byref(month), byref(day), byref(hour), byref(minute), byref(second), byref(day_of_week))
|
154
|
+
return (year.value, month.value, day.value, hour.value, minute.value, second.value, day_of_week.value)
|
easysewer/UDM.py
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
"""
|
2
|
+
Urban Drainage Model (UDM) - Main Model Class
|
3
|
+
|
4
|
+
This module implements the core Urban Drainage Model functionality for hydraulic simulations.
|
5
|
+
It serves as the main interface for creating and managing drainage system models including
|
6
|
+
nodes (junctions, outfalls), links (conduits), subcatchment areas, and rainfall data.
|
7
|
+
|
8
|
+
The model supports:
|
9
|
+
- Reading/writing SWMM .inp files
|
10
|
+
- Managing network elements (nodes, links, areas)
|
11
|
+
- Handling rainfall and calculation settings
|
12
|
+
- Supporting various hydraulic elements like conduits and junctions
|
13
|
+
"""
|
14
|
+
|
15
|
+
from .Options import CalculationInformation
|
16
|
+
from .Link import LinkList
|
17
|
+
from .Node import NodeList
|
18
|
+
from .Area import AreaList
|
19
|
+
from .Rain import Rain
|
20
|
+
from .Curve import ValueList
|
21
|
+
import json
|
22
|
+
from .utils import get_swmm_inp_content
|
23
|
+
|
24
|
+
|
25
|
+
class UrbanDrainageModel:
|
26
|
+
"""
|
27
|
+
Main class for managing an Urban Drainage Model.
|
28
|
+
|
29
|
+
This class serves as the central point for managing all aspects of an urban drainage
|
30
|
+
model including network topology, hydraulic elements, and simulation settings.
|
31
|
+
|
32
|
+
Attributes:
|
33
|
+
calc (CalculationInformation): Calculation and simulation settings
|
34
|
+
link (LinkList): Collection of conduits and other hydraulic links
|
35
|
+
node (NodeList): Collection of junctions, outfalls and other nodes
|
36
|
+
area (AreaList): Collection of subcatchment areas
|
37
|
+
rain (Rain): Rainfall data and settings
|
38
|
+
value (ValueList): Curves and patterns for various model parameters
|
39
|
+
label (dict): Model metadata and labeling information
|
40
|
+
|
41
|
+
Args:
|
42
|
+
model_path (str, optional): Path to SWMM .inp file to load. Defaults to None.
|
43
|
+
"""
|
44
|
+
|
45
|
+
def __init__(self, model_path=None):
|
46
|
+
# calculation related information
|
47
|
+
self.calc = CalculationInformation()
|
48
|
+
|
49
|
+
# entity related information
|
50
|
+
self.link = LinkList()
|
51
|
+
self.node = NodeList()
|
52
|
+
self.area = AreaList()
|
53
|
+
|
54
|
+
# rain related information
|
55
|
+
self.rain = Rain()
|
56
|
+
self.value = ValueList()
|
57
|
+
|
58
|
+
# label information
|
59
|
+
self.label = {}
|
60
|
+
|
61
|
+
# read model from the file if provided
|
62
|
+
if model_path is not None:
|
63
|
+
self.read_inp(model_path)
|
64
|
+
|
65
|
+
def __repr__(self):
|
66
|
+
"""Returns a string representation of the model showing key components"""
|
67
|
+
return f'{self.link}, {self.node}, {self.area}'
|
68
|
+
|
69
|
+
def to_inp(self, filename):
|
70
|
+
"""
|
71
|
+
Writes the model to a SWMM .inp file.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
filename (str): Path to the output .inp file
|
75
|
+
|
76
|
+
Returns:
|
77
|
+
int: 0 on success
|
78
|
+
"""
|
79
|
+
with open(filename, 'w', encoding='utf-8') as f:
|
80
|
+
# Write TITLE section first
|
81
|
+
f.write('[TITLE]\n')
|
82
|
+
if self.label:
|
83
|
+
try:
|
84
|
+
f.write(json.dumps(self.label, indent=2))
|
85
|
+
f.write('\n\n')
|
86
|
+
except:
|
87
|
+
f.write(str(self.label.get('TITLE', '')) + '\n\n')
|
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
|
97
|
+
|
98
|
+
def read_inp(self, filename):
|
99
|
+
"""
|
100
|
+
Reads a SWMM .inp file and populates the model.
|
101
|
+
|
102
|
+
Args:
|
103
|
+
filename (str): Path to the input .inp file
|
104
|
+
|
105
|
+
Returns:
|
106
|
+
int: 0 on success
|
107
|
+
"""
|
108
|
+
# Read TITLE section
|
109
|
+
title_content = get_swmm_inp_content(filename, '[TITLE]')
|
110
|
+
if title_content:
|
111
|
+
try:
|
112
|
+
# Try to parse as JSON
|
113
|
+
json_text = '\n'.join(title_content)
|
114
|
+
self.label = json.loads(json_text)
|
115
|
+
except json.JSONDecodeError:
|
116
|
+
# If not JSON, store as plain text
|
117
|
+
self.label = {'TITLE': '\n'.join(title_content)}
|
118
|
+
|
119
|
+
# Continue with other sections
|
120
|
+
self.calc.read_from_swmm_inp(filename)
|
121
|
+
self.node.read_from_swmm_inp(filename)
|
122
|
+
self.link.read_from_swmm_inp(filename)
|
123
|
+
self.area.read_from_swmm_inp(filename)
|
124
|
+
self.rain.read_from_swmm_inp(filename)
|
125
|
+
self.value.read_from_swmm_inp(filename)
|
126
|
+
return 0
|
easysewer/__init__.py
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
easysewer/utils.py
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
"""
|
2
|
+
Utility Functions Module
|
3
|
+
|
4
|
+
This module provides helper functions for file I/O operations and data processing
|
5
|
+
in the urban drainage model, particularly for SWMM input/output file handling.
|
6
|
+
"""
|
7
|
+
|
8
|
+
|
9
|
+
def get_swmm_inp_content(filename, flag):
|
10
|
+
"""
|
11
|
+
Extracts content from a specific section of a SWMM input file.
|
12
|
+
|
13
|
+
Args:
|
14
|
+
filename (str): Path to the SWMM input file
|
15
|
+
flag (str): Section identifier (e.g., '[TITLE]', '[JUNCTIONS]')
|
16
|
+
|
17
|
+
Returns:
|
18
|
+
list: Lines of content from the specified section
|
19
|
+
"""
|
20
|
+
flag += '\n'
|
21
|
+
result = []
|
22
|
+
|
23
|
+
with open(filename, 'r', encoding='utf-8') as f:
|
24
|
+
# getting to the flag line
|
25
|
+
for line in f:
|
26
|
+
if line == flag:
|
27
|
+
break
|
28
|
+
# adding related lines to results
|
29
|
+
for line in f:
|
30
|
+
# finish when getting to another section
|
31
|
+
if line[0] == '[':
|
32
|
+
break
|
33
|
+
# skip if this line is blank or annotation
|
34
|
+
if line == '\n' or line[0] == ';':
|
35
|
+
continue
|
36
|
+
result.append(line[0:-1])
|
37
|
+
|
38
|
+
return result
|
39
|
+
|
40
|
+
|
41
|
+
def combine_swmm_inp_contents(content1, content2):
|
42
|
+
"""
|
43
|
+
Combines two sections of SWMM input content based on matching identifiers.
|
44
|
+
|
45
|
+
Args:
|
46
|
+
content1 (list): Primary content lines
|
47
|
+
content2 (list): Secondary content lines to merge
|
48
|
+
|
49
|
+
Returns:
|
50
|
+
list: Combined content with merged information
|
51
|
+
"""
|
52
|
+
# generate a name list of content1
|
53
|
+
index_dic = []
|
54
|
+
for line in content1:
|
55
|
+
pair = line.split()
|
56
|
+
index_dic.append(pair[0])
|
57
|
+
#
|
58
|
+
for line in content2:
|
59
|
+
pair = line.split()
|
60
|
+
index = index_dic.index(pair[0])
|
61
|
+
content1[index] = content1[index] + ' ' + ' '.join(pair[1::])
|
62
|
+
#
|
63
|
+
return content1
|
64
|
+
|
65
|
+
|
66
|
+
def get_swmm_rpt_content(filename, flag):
|
67
|
+
"""
|
68
|
+
Extracts content from a specific section of a SWMM report file.
|
69
|
+
|
70
|
+
Args:
|
71
|
+
filename (str): Path to the SWMM report file
|
72
|
+
flag (str): Section identifier
|
73
|
+
|
74
|
+
Returns:
|
75
|
+
list: Lines of content from the specified report section
|
76
|
+
"""
|
77
|
+
# example:
|
78
|
+
# res = ut.get_swmm_rpt_content('calculate_temp/test.rpt', 'Node G80F425')
|
79
|
+
flag = f' <<< {flag} >>>\n'
|
80
|
+
result = []
|
81
|
+
with open(filename, 'r', encoding='utf-8') as f:
|
82
|
+
# getting to the flag line
|
83
|
+
for line in f:
|
84
|
+
if line == flag:
|
85
|
+
break
|
86
|
+
# adding related lines to results
|
87
|
+
i = 0
|
88
|
+
for line in f:
|
89
|
+
# skip title bar ( four lines )
|
90
|
+
if i < 4:
|
91
|
+
i += 1
|
92
|
+
continue
|
93
|
+
# finish when getting to another section
|
94
|
+
if line == ' \n':
|
95
|
+
break
|
96
|
+
result.append(line[0:-1])
|
97
|
+
return result
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: easysewer
|
3
|
+
Version: 0.0.1
|
4
|
+
Summary: An urban drainage modeling toolkit
|
5
|
+
Author-email: Yiran Ji <yiranji@zju.edu.cn>
|
6
|
+
License-Expression: AGPL-3.0-only
|
7
|
+
Project-URL: Homepage, https://github.com/yourusername/easysewer
|
8
|
+
Project-URL: Documentation, https://github.com/yourusername/easysewer#readme
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
10
|
+
Classifier: Operating System :: OS Independent
|
11
|
+
Requires-Python: >=3.10
|
12
|
+
Description-Content-Type: text/markdown
|
13
|
+
|
14
|
+
# EasySewer
|
15
|
+
🚀 An urban drainage modeling toolkit
|
16
|
+
> Note: Project under active development
|
@@ -0,0 +1,19 @@
|
|
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,,
|
@@ -0,0 +1 @@
|
|
1
|
+
easysewer
|