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/ModelAPI.py
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
"""
|
2
|
+
API extensions for Urban Drainage Modeling system
|
3
|
+
|
4
|
+
This module enhances the base UrbanDrainageModel with:
|
5
|
+
- Advanced simulation control with progress tracking
|
6
|
+
- Automated output file management
|
7
|
+
- Enhanced error checking and rpting
|
8
|
+
"""
|
9
|
+
|
10
|
+
import datetime
|
11
|
+
import uuid
|
12
|
+
import os
|
13
|
+
from .UDM import UrbanDrainageModel
|
14
|
+
from .SolverAPI import SWMMSolverAPI
|
15
|
+
|
16
|
+
|
17
|
+
class Model(UrbanDrainageModel):
|
18
|
+
"""
|
19
|
+
Enhanced SWMM model with advanced simulation capabilities.
|
20
|
+
|
21
|
+
This class extends the base easysewer.Model (UrbanDrainageModel) with additional
|
22
|
+
functionality for running simulations with progress tracking and error rpting.
|
23
|
+
It provides both a fast simulation mode and a detailed step-by-step simulation
|
24
|
+
with visual progress feedback.
|
25
|
+
|
26
|
+
Attributes:
|
27
|
+
Inherits all attributes from easysewer.Model (UrbanDrainageModel)
|
28
|
+
"""
|
29
|
+
|
30
|
+
def __init__(self, model_path: str | None = None):
|
31
|
+
"""
|
32
|
+
Initialize a Model instance with optional inp file.
|
33
|
+
|
34
|
+
Args:
|
35
|
+
model_path (str | None): Path to SWMM .inp file to load. If None,
|
36
|
+
creates an empty model. Defaults to None.
|
37
|
+
"""
|
38
|
+
super().__init__(model_path)
|
39
|
+
|
40
|
+
def simulation(
|
41
|
+
self,
|
42
|
+
inp_file: str | None = None,
|
43
|
+
rpt_file: str | None = None,
|
44
|
+
out_file: str | None = None,
|
45
|
+
mode: str = "normal"
|
46
|
+
) -> tuple[str, str, str]:
|
47
|
+
"""
|
48
|
+
Execute SWMM simulation with progress tracking and error checking.
|
49
|
+
|
50
|
+
Args:
|
51
|
+
inp_file: Path for inp .inp file. Auto-generated if None.
|
52
|
+
rpt_file: Path for rpt .rpt file. Auto-generated if None.
|
53
|
+
out_file: Path for output .out file. Auto-generated if None.
|
54
|
+
mode: Execution mode ("fast" for quick run, "normal" for progress tracking)
|
55
|
+
|
56
|
+
Returns:
|
57
|
+
tuple: Paths to generated (inp_file, rpt_file, out_file)
|
58
|
+
|
59
|
+
Raises:
|
60
|
+
SystemExit: If fatal errors occur during simulation setup/execution
|
61
|
+
|
62
|
+
"""
|
63
|
+
|
64
|
+
# Get current datetime as a filename-safe string
|
65
|
+
now = datetime.datetime.now()
|
66
|
+
date_string = now.strftime("%Y%m%d_%H%M%S") # Format: YYYYMMDD_HHMMSS
|
67
|
+
# Generate a UUID
|
68
|
+
unique_id = str(uuid.uuid4())
|
69
|
+
# Define output directory
|
70
|
+
output_dir = 'simulation_output'
|
71
|
+
# Check if directory exists, create it if not
|
72
|
+
if not os.path.exists(output_dir):
|
73
|
+
os.makedirs(output_dir)
|
74
|
+
# Combine datetime and UUID for a unique filename
|
75
|
+
model_name = f"{date_string}_{unique_id}"
|
76
|
+
# Set default file paths if not provided
|
77
|
+
if inp_file is None:
|
78
|
+
inp_file = os.path.join(output_dir, f"{model_name}.inp")
|
79
|
+
if rpt_file is None:
|
80
|
+
rpt_file = os.path.join(output_dir, f"{model_name}.rpt")
|
81
|
+
if out_file is None:
|
82
|
+
out_file = os.path.join(output_dir, f"{model_name}.out")
|
83
|
+
|
84
|
+
# Export model to desired path
|
85
|
+
self.to_inp(inp_file)
|
86
|
+
|
87
|
+
# Initialize swmm solver
|
88
|
+
solver = SWMMSolverAPI()
|
89
|
+
|
90
|
+
# If using fast mode, then use run() method to execute the simulation
|
91
|
+
if mode == "fast":
|
92
|
+
solver.run(inp_file, rpt_file, out_file)
|
93
|
+
solver.close()
|
94
|
+
return inp_file, rpt_file, out_file
|
95
|
+
|
96
|
+
# Open the model
|
97
|
+
err = solver.open(inp_file, rpt_file, out_file)
|
98
|
+
if err:
|
99
|
+
print(f"Error opening SWMM project: {err}")
|
100
|
+
print(solver.get_error())
|
101
|
+
exit(1)
|
102
|
+
|
103
|
+
# Start simulation
|
104
|
+
err = solver.start(1)
|
105
|
+
if err:
|
106
|
+
print(f"Error starting SWMM simulation: {err}")
|
107
|
+
print(solver.get_error())
|
108
|
+
exit(1)
|
109
|
+
|
110
|
+
# Prepare for simulation progress
|
111
|
+
print("Simulation progress:")
|
112
|
+
bar_length = 50 # Length of progress bar
|
113
|
+
last_progress_int = -1
|
114
|
+
start_time = datetime.datetime(
|
115
|
+
year=self.calc.simulation_start['year'],
|
116
|
+
month=self.calc.simulation_start['month'],
|
117
|
+
day=self.calc.simulation_start['day'],
|
118
|
+
hour=self.calc.simulation_start['hour'],
|
119
|
+
minute=self.calc.simulation_start['minute']
|
120
|
+
)
|
121
|
+
end_time = datetime.datetime(
|
122
|
+
year=self.calc.simulation_end['year'],
|
123
|
+
month=self.calc.simulation_end['month'],
|
124
|
+
day=self.calc.simulation_end['day'],
|
125
|
+
hour=self.calc.simulation_end['hour'],
|
126
|
+
minute=self.calc.simulation_end['minute']
|
127
|
+
)
|
128
|
+
total_seconds = (end_time - start_time).total_seconds()
|
129
|
+
|
130
|
+
# Simulation iteration
|
131
|
+
while True:
|
132
|
+
result, elapsed_time = solver.step()
|
133
|
+
if elapsed_time <= 0:
|
134
|
+
break
|
135
|
+
|
136
|
+
# Progress bar - Convert elapsed time to percentage of total simulation time
|
137
|
+
# Multiply by 24*60*60 to convert days to seconds (SWMM uses days as time unit)
|
138
|
+
progress = (elapsed_time * 24 * 60 * 60 / total_seconds) * 100
|
139
|
+
progress_int = int(progress)
|
140
|
+
|
141
|
+
# Only update progress when it changes by at least 1%
|
142
|
+
if progress_int > last_progress_int:
|
143
|
+
# Calculate the number of characters to fill
|
144
|
+
filled_length = int(bar_length * progress / 100)
|
145
|
+
bar = '█' * filled_length + '-' * (bar_length - filled_length)
|
146
|
+
|
147
|
+
# Print the entire progress bar each time (overwriting previous one)
|
148
|
+
print(f"\r[{bar}] {progress_int}%", end='', flush=True)
|
149
|
+
last_progress_int = progress_int
|
150
|
+
|
151
|
+
# Complete the progress bar when finished
|
152
|
+
print(f"\r[{'█' * bar_length}] 100%")
|
153
|
+
|
154
|
+
# End the simulation
|
155
|
+
err = solver.end()
|
156
|
+
if err:
|
157
|
+
print(f"Error ending SWMM simulation: {err}")
|
158
|
+
print(solver.get_error())
|
159
|
+
|
160
|
+
# Check simulation mass balance errors (continuity errors)
|
161
|
+
# These errors indicate the accuracy of the simulation results
|
162
|
+
# Values under 5% are generally acceptable for most applications
|
163
|
+
runoff_error_percent, flow_error_percent, quality_error_percent = solver.get_mass_bal_err()
|
164
|
+
|
165
|
+
def _check_error(error_type: str, error_percent: float) -> None:
|
166
|
+
"""
|
167
|
+
Validate simulation error percentages against acceptability threshold.
|
168
|
+
|
169
|
+
Args:
|
170
|
+
error_type: Category of error being checked from:
|
171
|
+
- Runoff: Rainfall runoff calculation errors
|
172
|
+
- Flow: Hydraulic flow continuity errors
|
173
|
+
- Quality: Water quality simulation errors
|
174
|
+
error_percent: Calculated percentage error (positive/negative)
|
175
|
+
|
176
|
+
Note:
|
177
|
+
Prints warning message to stderr when exceeding 5% threshold
|
178
|
+
Does not interrupt simulation execution
|
179
|
+
"""
|
180
|
+
ERROR_THRESHOLD = 5
|
181
|
+
if abs(error_percent) > ERROR_THRESHOLD:
|
182
|
+
print(f"WARNING: {error_type} error percentage ({error_percent:.2f}%) exceeds {ERROR_THRESHOLD}%")
|
183
|
+
|
184
|
+
# Check for errors over 5%
|
185
|
+
_check_error("Runoff", runoff_error_percent)
|
186
|
+
_check_error("Flow", flow_error_percent)
|
187
|
+
_check_error("Quality", quality_error_percent)
|
188
|
+
|
189
|
+
# Close the solver
|
190
|
+
err = solver.close()
|
191
|
+
if err:
|
192
|
+
print(f"Error closing SWMM project: {err}")
|
193
|
+
print(solver.get_error())
|
194
|
+
return inp_file, rpt_file, out_file
|