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/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
- format (str): Format of the rainfall data (INTENSITY/VOLUME/CUMULATIVE)
119
+ form (str): Format of the rainfall data (INTENSITY/VOLUME/CUMULATIVE)
60
120
  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
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
- 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
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(';;----- -------- --------- ---- ----------\n')
288
+ f.write(';;Name Format Interval SCF SourceType Source [Unit]\n')
289
+ f.write(';;----- -------- --------- ---- ---------- ---------- -------\n')
207
290
  for gage in self.gage_list:
208
- f.write(f'{gage.name} {gage.form} {gage.interval} {gage.SCF} TIMESERIES {gage.source}\n')
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
- with open(filename, 'w', encoding='utf-8') as f:
80
- # Write TITLE section first
81
- f.write('[TITLE]\n')
82
- if self.label:
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
- f.write(json.dumps(self.label, indent=2))
85
- f.write('\n\n')
118
+ filepath.unlink()
86
119
  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
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
@@ -2,6 +2,6 @@
2
2
  pass
3
3
  """
4
4
  from .UDM import UrbanDrainageModel
5
- from .UDM import UrbanDrainageModel as Model
5
+ from .ModelAPI import Model
6
6
  from .OutputAPI import SWMMOutputAPI
7
7
  from .SolverAPI import SWMMSolverAPI
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easysewer
3
- Version: 0.0.1
3
+ Version: 0.0.3
4
4
  Summary: An urban drainage modeling toolkit
5
5
  Author-email: Yiran Ji <yiranji@zju.edu.cn>
6
6
  License-Expression: AGPL-3.0-only
@@ -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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (80.3.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -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,,