ras-commander 0.48.0__py3-none-any.whl → 0.50.0__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.
- ras_commander/Decorators.py +18 -1
- ras_commander/HdfBase.py +307 -197
- ras_commander/HdfBndry.py +94 -287
- ras_commander/HdfFluvialPluvial.py +256 -273
- ras_commander/HdfInfiltration.py +410 -0
- ras_commander/HdfMesh.py +222 -114
- ras_commander/HdfPipe.py +127 -175
- ras_commander/HdfPlan.py +144 -58
- ras_commander/HdfPlot.py +104 -0
- ras_commander/HdfPump.py +76 -28
- ras_commander/HdfResultsMesh.py +190 -183
- ras_commander/HdfResultsPlan.py +76 -220
- ras_commander/HdfResultsPlot.py +182 -0
- ras_commander/HdfResultsXsec.py +185 -145
- ras_commander/HdfStruc.py +65 -35
- ras_commander/HdfUtils.py +435 -518
- ras_commander/HdfXsec.py +137 -127
- ras_commander/LoggingConfig.py +13 -3
- ras_commander/RasCmdr.py +13 -0
- ras_commander/RasExamples.py +14 -0
- ras_commander/RasGeo.py +11 -0
- ras_commander/RasGpt.py +8 -0
- ras_commander/RasMapper.py +105 -0
- ras_commander/RasPlan.py +30 -0
- ras_commander/RasPrj.py +34 -0
- ras_commander/RasToGo.py +16 -0
- ras_commander/RasUnsteady.py +15 -0
- ras_commander/RasUtils.py +31 -0
- ras_commander/__init__.py +10 -0
- {ras_commander-0.48.0.dist-info → ras_commander-0.50.0.dist-info}/METADATA +77 -9
- ras_commander-0.50.0.dist-info/RECORD +34 -0
- ras_commander-0.48.0.dist-info/RECORD +0 -30
- {ras_commander-0.48.0.dist-info → ras_commander-0.50.0.dist-info}/LICENSE +0 -0
- {ras_commander-0.48.0.dist-info → ras_commander-0.50.0.dist-info}/WHEEL +0 -0
- {ras_commander-0.48.0.dist-info → ras_commander-0.50.0.dist-info}/top_level.txt +0 -0
ras_commander/HdfResultsMesh.py
CHANGED
@@ -6,6 +6,61 @@ from the https://github.com/fema-ffrd/rashdf library,
|
|
6
6
|
released under MIT license and Copyright (c) 2024 fema-ffrd
|
7
7
|
|
8
8
|
The file has been forked and modified for use in RAS Commander.
|
9
|
+
|
10
|
+
-----
|
11
|
+
|
12
|
+
All methods in this class are static and designed to be used without instantiation.
|
13
|
+
|
14
|
+
Public Functions:
|
15
|
+
- get_mesh_summary(): Get summary output data for a variable
|
16
|
+
- get_mesh_timeseries(): Get timeseries output for a mesh and variable
|
17
|
+
- get_mesh_faces_timeseries(): Get timeseries for all face-based variables
|
18
|
+
- get_mesh_cells_timeseries(): Get timeseries for mesh cells
|
19
|
+
- get_mesh_last_iter(): Get last iteration count for cells
|
20
|
+
- get_mesh_max_ws(): Get maximum water surface elevation at each cell
|
21
|
+
- get_mesh_min_ws(): Get minimum water surface elevation at each cell
|
22
|
+
- get_mesh_max_face_v(): Get maximum face velocity at each face
|
23
|
+
- get_mesh_min_face_v(): Get minimum face velocity at each face
|
24
|
+
- get_mesh_max_ws_err(): Get maximum water surface error at each cell
|
25
|
+
- get_mesh_max_iter(): Get maximum iteration count at each cell
|
26
|
+
|
27
|
+
Private Functions:
|
28
|
+
- _get_mesh_timeseries_output_path(): Get HDF path for timeseries output #REDUNDANT??
|
29
|
+
- _get_mesh_cells_timeseries_output(): Internal handler for cell timeseries #REDUNDANT??
|
30
|
+
- _get_mesh_timeseries_output(): Internal handler for mesh timeseries # FACES??
|
31
|
+
- _get_mesh_timeseries_output_values_units(): Get values and units for timeseries
|
32
|
+
- _get_available_meshes(): Get list of available meshes in HDF #USE HDFBASE OR HDFUTIL
|
33
|
+
- get_mesh_summary_output(): Internal handler for summary output
|
34
|
+
- get_mesh_summary_output_group(): Get HDF group for summary output #REDUNDANT?? Include in Above
|
35
|
+
|
36
|
+
The class works with HEC-RAS version 6.0+ plan HDF files and uses HdfBase and
|
37
|
+
HdfUtils for common operations. Methods use @log_call decorator for logging and
|
38
|
+
@standardize_input decorator to handle different input types.
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
REVISIONS MADE:
|
46
|
+
|
47
|
+
Use get_ prefix for functions that return data.
|
48
|
+
BUT, we will never set results data, so we should use get_ for results data.
|
49
|
+
|
50
|
+
Renamed functions:
|
51
|
+
- mesh_summary_output() to get_mesh_summary()
|
52
|
+
- mesh_timeseries_output() to get_mesh_timeseries()
|
53
|
+
- mesh_faces_timeseries_output() to get_mesh_faces_timeseries()
|
54
|
+
- mesh_cells_timeseries_output() to get_mesh_cells_timeseries()
|
55
|
+
- mesh_last_iter() to get_mesh_last_iter()
|
56
|
+
- mesh_max_ws() to get_mesh_max_ws()
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
|
9
64
|
"""
|
10
65
|
|
11
66
|
import numpy as np
|
@@ -19,81 +74,81 @@ from .HdfBase import HdfBase
|
|
19
74
|
from .HdfUtils import HdfUtils
|
20
75
|
from .Decorators import log_call, standardize_input
|
21
76
|
from .LoggingConfig import setup_logging, get_logger
|
77
|
+
import geopandas as gpd
|
22
78
|
|
23
79
|
logger = get_logger(__name__)
|
24
80
|
|
25
81
|
class HdfResultsMesh:
|
26
82
|
"""
|
27
|
-
|
28
|
-
|
29
|
-
This class provides methods to extract and analyze mesh summary outputs,
|
30
|
-
timeseries data, and various mesh-specific results such as water surface
|
31
|
-
elevations, velocities, and errors.
|
32
|
-
|
33
|
-
The class works with HEC-RAS plan HDF files and uses HdfBase and HdfUtils
|
34
|
-
for common operations and utilities.
|
83
|
+
Handles mesh-related results from HEC-RAS HDF files.
|
35
84
|
|
36
|
-
|
37
|
-
|
38
|
-
|
85
|
+
Provides methods to extract and analyze:
|
86
|
+
- Mesh summary outputs
|
87
|
+
- Timeseries data
|
88
|
+
- Water surface elevations
|
89
|
+
- Velocities
|
90
|
+
- Error metrics
|
39
91
|
|
40
|
-
|
41
|
-
None
|
42
|
-
|
43
|
-
Note:
|
44
|
-
This class is designed to work with HEC-RAS version 6.0 and later.
|
92
|
+
Works with HEC-RAS 6.0+ plan HDF files.
|
45
93
|
"""
|
46
94
|
|
47
95
|
@staticmethod
|
48
96
|
@log_call
|
49
97
|
@standardize_input(file_type='plan_hdf')
|
50
|
-
def
|
98
|
+
def get_mesh_summary(hdf_path: Path, var: str, round_to: str = "100ms") -> pd.DataFrame:
|
51
99
|
"""
|
52
|
-
|
100
|
+
Get timeseries output for a specific mesh and variable.
|
53
101
|
|
54
102
|
Args:
|
55
|
-
hdf_path (Path): Path to the
|
56
|
-
|
57
|
-
|
103
|
+
hdf_path (Path): Path to the HDF file
|
104
|
+
mesh_name (str): Name of the mesh
|
105
|
+
var (str): Variable to retrieve (see valid options below)
|
106
|
+
truncate (bool): Whether to truncate trailing zeros (default True)
|
58
107
|
|
59
108
|
Returns:
|
60
|
-
|
109
|
+
xr.DataArray: DataArray with dimensions:
|
110
|
+
- time: Timestamps
|
111
|
+
- face_id/cell_id: IDs for faces/cells
|
112
|
+
And attributes:
|
113
|
+
- units: Variable units
|
114
|
+
- mesh_name: Name of mesh
|
115
|
+
- variable: Variable name
|
61
116
|
|
62
|
-
|
63
|
-
|
117
|
+
Valid variables include:
|
118
|
+
"Water Surface", "Face Velocity", "Cell Velocity X"...
|
64
119
|
"""
|
65
120
|
try:
|
66
121
|
with h5py.File(hdf_path, 'r') as hdf_file:
|
67
|
-
return HdfResultsMesh.
|
122
|
+
return HdfResultsMesh.get_mesh_summary_output(hdf_file, var, round_to)
|
68
123
|
except Exception as e:
|
69
|
-
logger.error(f"Error in
|
124
|
+
logger.error(f"Error in get_mesh_summary: {str(e)}")
|
70
125
|
logger.error(f"Variable: {var}")
|
71
126
|
raise ValueError(f"Failed to get summary output: {str(e)}")
|
72
127
|
|
73
128
|
@staticmethod
|
74
129
|
@log_call
|
75
130
|
@standardize_input(file_type='plan_hdf')
|
76
|
-
def
|
131
|
+
def get_mesh_timeseries(hdf_path: Path, mesh_name: str, var: str, truncate: bool = True) -> xr.DataArray:
|
77
132
|
"""
|
78
133
|
Get timeseries output for a specific mesh and variable.
|
79
134
|
|
80
135
|
Args:
|
81
|
-
hdf_path (Path): Path to the HDF file
|
82
|
-
mesh_name (str): Name of the mesh
|
83
|
-
var (str): Variable to retrieve
|
84
|
-
|
85
|
-
"Face Flow", "Face Water Surface", "Cell Volume", "Cell Volume Error",
|
86
|
-
"Cell Water Surface Error", "Cell Courant", "Face Courant",
|
87
|
-
"Cell Hydraulic Depth", "Cell Invert Depth",
|
88
|
-
"Cell Cumulative Precipitation Depth", "Cell Divergence Term",
|
89
|
-
"Cell Eddy Viscosity X", "Cell Eddy Viscosity Y", "Cell Flow Balance",
|
90
|
-
"Cell Storage Term", "Cell Water Source Term", "Face Cumulative Volume",
|
91
|
-
"Face Eddy Viscosity", "Face Flow Period Average", "Face Friction Term",
|
92
|
-
"Face Pressure Gradient Term", "Face Shear Stress", "Face Tangential Velocity"
|
93
|
-
truncate (bool): Whether to truncate the output (default True).
|
136
|
+
hdf_path (Path): Path to the HDF file
|
137
|
+
mesh_name (str): Name of the mesh
|
138
|
+
var (str): Variable to retrieve (see valid options below)
|
139
|
+
truncate (bool): Whether to truncate trailing zeros (default True)
|
94
140
|
|
95
141
|
Returns:
|
96
|
-
xr.DataArray: DataArray
|
142
|
+
xr.DataArray: DataArray with dimensions:
|
143
|
+
- time: Timestamps
|
144
|
+
- face_id/cell_id: IDs for faces/cells
|
145
|
+
And attributes:
|
146
|
+
- units: Variable units
|
147
|
+
- mesh_name: Name of mesh
|
148
|
+
- variable: Variable name
|
149
|
+
|
150
|
+
Valid variables include:
|
151
|
+
"Water Surface", "Face Velocity", "Cell Velocity X"...
|
97
152
|
"""
|
98
153
|
with h5py.File(hdf_path, 'r') as hdf_file:
|
99
154
|
return HdfResultsMesh._get_mesh_timeseries_output(hdf_file, mesh_name, var, truncate)
|
@@ -101,7 +156,7 @@ class HdfResultsMesh:
|
|
101
156
|
@staticmethod
|
102
157
|
@log_call
|
103
158
|
@standardize_input(file_type='plan_hdf')
|
104
|
-
def
|
159
|
+
def get_mesh_faces_timeseries(hdf_path: Path, mesh_name: str) -> xr.Dataset:
|
105
160
|
"""
|
106
161
|
Get timeseries output for all face-based variables of a specific mesh.
|
107
162
|
|
@@ -117,7 +172,7 @@ class HdfResultsMesh:
|
|
117
172
|
|
118
173
|
for var in face_vars:
|
119
174
|
try:
|
120
|
-
da = HdfResultsMesh.
|
175
|
+
da = HdfResultsMesh.get_mesh_timeseries(hdf_path, mesh_name, var)
|
121
176
|
# Assign the variable name as the DataArray name
|
122
177
|
da.name = var.lower().replace(' ', '_')
|
123
178
|
datasets.append(da)
|
@@ -137,34 +192,34 @@ class HdfResultsMesh:
|
|
137
192
|
@staticmethod
|
138
193
|
@log_call
|
139
194
|
@standardize_input(file_type='plan_hdf')
|
140
|
-
def
|
195
|
+
def get_mesh_cells_timeseries(hdf_path: Path, mesh_names: Optional[Union[str, List[str]]] = None, var: Optional[str] = None, truncate: bool = False, ras_object: Optional[Any] = None) -> Dict[str, xr.Dataset]:
|
141
196
|
"""
|
142
|
-
Get mesh cells timeseries output
|
197
|
+
Get mesh cells timeseries output.
|
143
198
|
|
144
199
|
Args:
|
145
|
-
hdf_path (
|
146
|
-
mesh_names (
|
147
|
-
var (
|
148
|
-
truncate (bool):
|
149
|
-
ras_object (
|
200
|
+
hdf_path (Path): Path to HDF file
|
201
|
+
mesh_names (str|List[str], optional): Mesh name(s). If None, processes all meshes
|
202
|
+
var (str, optional): Variable name. If None, retrieves all variables
|
203
|
+
truncate (bool): Remove trailing zeros if True
|
204
|
+
ras_object (Any, optional): RAS object if available
|
150
205
|
|
151
206
|
Returns:
|
152
|
-
Dict[str, xr.Dataset]:
|
153
|
-
|
154
|
-
|
155
|
-
|
207
|
+
Dict[str, xr.Dataset]: Dictionary mapping mesh names to datasets containing:
|
208
|
+
- Time-indexed variables
|
209
|
+
- Cell/face IDs
|
210
|
+
- Variable metadata
|
156
211
|
"""
|
157
212
|
try:
|
158
213
|
with h5py.File(hdf_path, 'r') as hdf_file:
|
159
|
-
return HdfResultsMesh.
|
214
|
+
return HdfResultsMesh._get_mesh_cells_timeseries_output(hdf_file, mesh_names, var, truncate)
|
160
215
|
except Exception as e:
|
161
|
-
logger.error(f"Error in
|
216
|
+
logger.error(f"Error in get_mesh_cells_timeseries: {str(e)}")
|
162
217
|
raise ValueError(f"Error processing timeseries output data: {e}")
|
163
218
|
|
164
219
|
@staticmethod
|
165
220
|
@log_call
|
166
221
|
@standardize_input(file_type='plan_hdf')
|
167
|
-
def
|
222
|
+
def get_mesh_last_iter(hdf_path: Path) -> pd.DataFrame:
|
168
223
|
"""
|
169
224
|
Get last iteration count for each mesh cell.
|
170
225
|
|
@@ -174,33 +229,28 @@ class HdfResultsMesh:
|
|
174
229
|
Returns:
|
175
230
|
pd.DataFrame: DataFrame containing last iteration counts.
|
176
231
|
"""
|
177
|
-
return HdfResultsMesh.
|
232
|
+
return HdfResultsMesh.get_mesh_summary_output(hdf_path, "Cell Last Iteration")
|
178
233
|
|
179
234
|
|
180
235
|
@staticmethod
|
181
236
|
@log_call
|
182
237
|
@standardize_input(file_type='plan_hdf')
|
183
|
-
def
|
238
|
+
def get_mesh_max_ws(hdf_path: Path, round_to: str = "100ms") -> gpd.GeoDataFrame:
|
184
239
|
"""
|
185
|
-
Get maximum
|
240
|
+
Get maximum water surface elevation for each mesh cell.
|
186
241
|
|
187
242
|
Args:
|
188
243
|
hdf_path (Path): Path to the HDF file.
|
189
244
|
round_to (str): Time rounding specification (default "100ms").
|
190
245
|
|
191
246
|
Returns:
|
192
|
-
|
193
|
-
|
194
|
-
Raises:
|
195
|
-
ValueError: If there's an error processing the maximum iteration data.
|
196
|
-
|
197
|
-
Note: The Maximum Iteration is labeled as "Cell Last Iteration" in the HDF file
|
247
|
+
gpd.GeoDataFrame: GeoDataFrame containing maximum water surface elevations with geometry.
|
198
248
|
"""
|
199
249
|
try:
|
200
250
|
with h5py.File(hdf_path, 'r') as hdf_file:
|
201
|
-
return HdfResultsMesh.
|
251
|
+
return HdfResultsMesh.get_mesh_summary_output(hdf_file, "Maximum Water Surface", round_to)
|
202
252
|
except Exception as e:
|
203
|
-
logger.error(f"Error in
|
253
|
+
logger.error(f"Error in get_mesh_max_ws: {str(e)}")
|
204
254
|
raise ValueError(f"Failed to get maximum water surface: {str(e)}")
|
205
255
|
|
206
256
|
|
@@ -210,7 +260,7 @@ class HdfResultsMesh:
|
|
210
260
|
@staticmethod
|
211
261
|
@log_call
|
212
262
|
@standardize_input(file_type='plan_hdf')
|
213
|
-
def
|
263
|
+
def get_mesh_min_ws(hdf_path: Path, round_to: str = "100ms") -> gpd.GeoDataFrame:
|
214
264
|
"""
|
215
265
|
Get minimum water surface elevation for each mesh cell.
|
216
266
|
|
@@ -219,19 +269,19 @@ class HdfResultsMesh:
|
|
219
269
|
round_to (str): Time rounding specification (default "100ms").
|
220
270
|
|
221
271
|
Returns:
|
222
|
-
|
272
|
+
gpd.GeoDataFrame: GeoDataFrame containing minimum water surface elevations with geometry.
|
223
273
|
"""
|
224
274
|
try:
|
225
275
|
with h5py.File(hdf_path, 'r') as hdf_file:
|
226
|
-
return HdfResultsMesh.
|
276
|
+
return HdfResultsMesh.get_mesh_summary_output(hdf_file, "Minimum Water Surface", round_to)
|
227
277
|
except Exception as e:
|
228
|
-
logger.error(f"Error in
|
278
|
+
logger.error(f"Error in get_mesh_min_ws: {str(e)}")
|
229
279
|
raise ValueError(f"Failed to get minimum water surface: {str(e)}")
|
230
280
|
|
231
281
|
@staticmethod
|
232
282
|
@log_call
|
233
283
|
@standardize_input(file_type='plan_hdf')
|
234
|
-
def
|
284
|
+
def get_mesh_max_face_v(hdf_path: Path, round_to: str = "100ms") -> pd.DataFrame:
|
235
285
|
"""
|
236
286
|
Get maximum face velocity for each mesh face.
|
237
287
|
|
@@ -241,21 +291,18 @@ class HdfResultsMesh:
|
|
241
291
|
|
242
292
|
Returns:
|
243
293
|
pd.DataFrame: DataFrame containing maximum face velocities.
|
244
|
-
|
245
|
-
Raises:
|
246
|
-
ValueError: If there's an error processing the maximum face velocity data.
|
247
294
|
"""
|
248
295
|
try:
|
249
296
|
with h5py.File(hdf_path, 'r') as hdf_file:
|
250
|
-
return HdfResultsMesh.
|
297
|
+
return HdfResultsMesh.get_mesh_summary_output(hdf_file, "Maximum Face Velocity", round_to)
|
251
298
|
except Exception as e:
|
252
|
-
logger.error(f"Error in
|
299
|
+
logger.error(f"Error in get_mesh_max_face_v: {str(e)}")
|
253
300
|
raise ValueError(f"Failed to get maximum face velocity: {str(e)}")
|
254
301
|
|
255
302
|
@staticmethod
|
256
303
|
@log_call
|
257
304
|
@standardize_input(file_type='plan_hdf')
|
258
|
-
def
|
305
|
+
def get_mesh_min_face_v(hdf_path: Path, round_to: str = "100ms") -> pd.DataFrame:
|
259
306
|
"""
|
260
307
|
Get minimum face velocity for each mesh cell.
|
261
308
|
|
@@ -271,15 +318,15 @@ class HdfResultsMesh:
|
|
271
318
|
"""
|
272
319
|
try:
|
273
320
|
with h5py.File(hdf_path, 'r') as hdf_file:
|
274
|
-
return HdfResultsMesh.
|
321
|
+
return HdfResultsMesh.get_mesh_summary_output(hdf_file, "Minimum Face Velocity", round_to)
|
275
322
|
except Exception as e:
|
276
|
-
logger.error(f"Error in
|
323
|
+
logger.error(f"Error in get_mesh_min_face_v: {str(e)}")
|
277
324
|
raise ValueError(f"Failed to get minimum face velocity: {str(e)}")
|
278
325
|
|
279
326
|
@staticmethod
|
280
327
|
@log_call
|
281
328
|
@standardize_input(file_type='plan_hdf')
|
282
|
-
def
|
329
|
+
def get_mesh_max_ws_err(hdf_path: Path, round_to: str = "100ms") -> pd.DataFrame:
|
283
330
|
"""
|
284
331
|
Get maximum water surface error for each mesh cell.
|
285
332
|
|
@@ -295,16 +342,16 @@ class HdfResultsMesh:
|
|
295
342
|
"""
|
296
343
|
try:
|
297
344
|
with h5py.File(hdf_path, 'r') as hdf_file:
|
298
|
-
return HdfResultsMesh.
|
345
|
+
return HdfResultsMesh.get_mesh_summary_output(hdf_file, "Cell Maximum Water Surface Error", round_to)
|
299
346
|
except Exception as e:
|
300
|
-
logger.error(f"Error in
|
347
|
+
logger.error(f"Error in get_mesh_max_ws_err: {str(e)}")
|
301
348
|
raise ValueError(f"Failed to get maximum water surface error: {str(e)}")
|
302
349
|
|
303
350
|
|
304
351
|
@staticmethod
|
305
352
|
@log_call
|
306
353
|
@standardize_input(file_type='plan_hdf')
|
307
|
-
def
|
354
|
+
def get_mesh_max_iter(hdf_path: Path, round_to: str = "100ms") -> gpd.GeoDataFrame:
|
308
355
|
"""
|
309
356
|
Get maximum iteration count for each mesh cell.
|
310
357
|
|
@@ -313,22 +360,22 @@ class HdfResultsMesh:
|
|
313
360
|
round_to (str): Time rounding specification (default "100ms").
|
314
361
|
|
315
362
|
Returns:
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
363
|
+
gpd.GeoDataFrame: GeoDataFrame containing maximum iteration counts with geometry.
|
364
|
+
Includes columns:
|
365
|
+
- mesh_name: Name of the mesh
|
366
|
+
- cell_id: ID of the cell
|
367
|
+
- cell_last_iteration: Maximum number of iterations
|
368
|
+
- cell_last_iteration_time: Time when max iterations occurred
|
369
|
+
- geometry: Point geometry representing cell center
|
322
370
|
"""
|
323
371
|
try:
|
324
372
|
with h5py.File(hdf_path, 'r') as hdf_file:
|
325
|
-
return HdfResultsMesh.
|
373
|
+
return HdfResultsMesh.get_mesh_summary_output(hdf_file, "Cell Last Iteration", round_to)
|
326
374
|
except Exception as e:
|
327
|
-
logger.error(f"Error in
|
375
|
+
logger.error(f"Error in get_mesh_max_iter: {str(e)}")
|
328
376
|
raise ValueError(f"Failed to get maximum iteration count: {str(e)}")
|
329
377
|
|
330
378
|
|
331
|
-
|
332
379
|
|
333
380
|
|
334
381
|
@staticmethod
|
@@ -347,10 +394,13 @@ class HdfResultsMesh:
|
|
347
394
|
|
348
395
|
|
349
396
|
@staticmethod
|
350
|
-
def
|
397
|
+
def _get_mesh_cells_timeseries_output(hdf_file: h5py.File,
|
398
|
+
mesh_names: Optional[Union[str, List[str]]] = None,
|
399
|
+
var: Optional[str] = None,
|
400
|
+
truncate: bool = False) -> Dict[str, xr.Dataset]:
|
351
401
|
"""
|
352
402
|
Get mesh cells timeseries output for specified meshes and variables.
|
353
|
-
|
403
|
+
|
354
404
|
Args:
|
355
405
|
hdf_file (h5py.File): Open HDF file object.
|
356
406
|
mesh_names (Optional[Union[str, List[str]]]): Name(s) of the mesh(es). If None, processes all available meshes.
|
@@ -381,8 +431,8 @@ class HdfResultsMesh:
|
|
381
431
|
}
|
382
432
|
|
383
433
|
try:
|
384
|
-
start_time = HdfBase.
|
385
|
-
time_stamps = HdfBase.
|
434
|
+
start_time = HdfBase.get_simulation_start_time(hdf_file)
|
435
|
+
time_stamps = HdfBase.get_unsteady_timestamps(hdf_file)
|
386
436
|
|
387
437
|
if mesh_names is None:
|
388
438
|
mesh_names = HdfResultsMesh._get_available_meshes(hdf_file)
|
@@ -470,19 +520,26 @@ class HdfResultsMesh:
|
|
470
520
|
dataset = hdf_file[path]
|
471
521
|
values = dataset[:]
|
472
522
|
units = dataset.attrs.get("Units", "").decode("utf-8")
|
473
|
-
|
523
|
+
|
524
|
+
# Get start time and timesteps
|
525
|
+
start_time = HdfBase.get_simulation_start_time(hdf_file)
|
526
|
+
# Updated to use the new function name from HdfUtils
|
527
|
+
timesteps = HdfUtils.convert_timesteps_to_datetimes(
|
528
|
+
np.array(hdf_file["Results/Unsteady/Output/Output Blocks/Base Output/Unsteady Time Series/Time"][:]),
|
529
|
+
start_time
|
530
|
+
)
|
474
531
|
|
475
532
|
if truncate:
|
476
533
|
non_zero = np.nonzero(values)[0]
|
477
534
|
if len(non_zero) > 0:
|
478
535
|
start, end = non_zero[0], non_zero[-1] + 1
|
479
536
|
values = values[start:end]
|
480
|
-
|
537
|
+
timesteps = timesteps[start:end]
|
481
538
|
|
482
539
|
# Determine if this is a face-based or cell-based variable
|
483
540
|
id_dim = "face_id" if "Face" in var else "cell_id"
|
484
541
|
dims = ["time", id_dim] if values.ndim == 2 else ["time"]
|
485
|
-
coords = {"time":
|
542
|
+
coords = {"time": timesteps}
|
486
543
|
if values.ndim == 2:
|
487
544
|
coords[id_dim] = np.arange(values.shape[1])
|
488
545
|
|
@@ -530,16 +587,13 @@ class HdfResultsMesh:
|
|
530
587
|
Returns:
|
531
588
|
List[str]: A list of mesh names.
|
532
589
|
"""
|
533
|
-
|
534
|
-
|
535
|
-
if base_path in hdf_file:
|
536
|
-
for name in hdf_file[base_path]:
|
537
|
-
if isinstance(hdf_file[f"{base_path}/{name}"], h5py.Group):
|
538
|
-
mesh_names.append(name)
|
539
|
-
return mesh_names
|
590
|
+
return HdfMesh.get_mesh_area_names(hdf_file)
|
591
|
+
|
540
592
|
|
541
593
|
@staticmethod
|
542
|
-
|
594
|
+
@log_call
|
595
|
+
@standardize_input(file_type='plan_hdf')
|
596
|
+
def get_mesh_summary_output(hdf_file: h5py.File, var: str, round_to: str = "100ms") -> gpd.GeoDataFrame:
|
543
597
|
"""
|
544
598
|
Get the summary output data for a given variable from the HDF file.
|
545
599
|
|
@@ -554,8 +608,8 @@ class HdfResultsMesh:
|
|
554
608
|
|
555
609
|
Returns
|
556
610
|
-------
|
557
|
-
|
558
|
-
A
|
611
|
+
gpd.GeoDataFrame
|
612
|
+
A GeoDataFrame containing the summary output data with attributes as metadata.
|
559
613
|
|
560
614
|
Raises
|
561
615
|
------
|
@@ -564,12 +618,18 @@ class HdfResultsMesh:
|
|
564
618
|
"""
|
565
619
|
try:
|
566
620
|
dfs = []
|
567
|
-
start_time = HdfBase.
|
621
|
+
start_time = HdfBase.get_simulation_start_time(hdf_file)
|
568
622
|
|
569
623
|
logger.info(f"Processing summary output for variable: {var}")
|
570
|
-
|
624
|
+
d2_flow_areas = hdf_file.get("Geometry/2D Flow Areas/Attributes")
|
625
|
+
if d2_flow_areas is None:
|
626
|
+
return gpd.GeoDataFrame()
|
627
|
+
|
628
|
+
for d2_flow_area in d2_flow_areas[:]:
|
629
|
+
mesh_name = HdfUtils.convert_ras_string(d2_flow_area[0])
|
630
|
+
cell_count = d2_flow_area[-1]
|
571
631
|
logger.debug(f"Processing mesh: {mesh_name} with {cell_count} cells")
|
572
|
-
group = HdfResultsMesh.
|
632
|
+
group = HdfResultsMesh.get_mesh_summary_output_group(hdf_file, mesh_name, var)
|
573
633
|
|
574
634
|
data = group[:]
|
575
635
|
logger.debug(f"Data shape for {var} in {mesh_name}: {data.shape}")
|
@@ -585,7 +645,7 @@ class HdfResultsMesh:
|
|
585
645
|
"mesh_name": [mesh_name] * data.shape[1],
|
586
646
|
"cell_id" if "Face" not in var else "face_id": range(data.shape[1]),
|
587
647
|
f"{var.lower().replace(' ', '_')}": data[0, :],
|
588
|
-
f"{var.lower().replace(' ', '_')}_time": HdfUtils.
|
648
|
+
f"{var.lower().replace(' ', '_')}_time": HdfUtils.convert_timesteps_to_datetimes(
|
589
649
|
data[1, :], start_time, time_unit="days", round_to=round_to
|
590
650
|
)
|
591
651
|
})
|
@@ -604,13 +664,13 @@ class HdfResultsMesh:
|
|
604
664
|
|
605
665
|
# Add geometry based on variable type
|
606
666
|
if "Face" in var:
|
607
|
-
face_df = HdfMesh.
|
667
|
+
face_df = HdfMesh.get_mesh_cell_faces(hdf_file)
|
608
668
|
if not face_df.empty:
|
609
669
|
df = df.merge(face_df[['mesh_name', 'face_id', 'geometry']],
|
610
670
|
on=['mesh_name', 'face_id'],
|
611
671
|
how='left')
|
612
672
|
else:
|
613
|
-
cell_df = HdfMesh.
|
673
|
+
cell_df = HdfMesh.get_mesh_cell_points(hdf_file)
|
614
674
|
if not cell_df.empty:
|
615
675
|
df = df.merge(cell_df[['mesh_name', 'cell_id', 'geometry']],
|
616
676
|
on=['mesh_name', 'cell_id'],
|
@@ -628,10 +688,18 @@ class HdfResultsMesh:
|
|
628
688
|
dfs.append(df)
|
629
689
|
|
630
690
|
if not dfs:
|
631
|
-
return
|
691
|
+
return gpd.GeoDataFrame()
|
632
692
|
|
633
693
|
result = pd.concat(dfs, ignore_index=True)
|
634
694
|
|
695
|
+
# Convert to GeoDataFrame
|
696
|
+
gdf = gpd.GeoDataFrame(result, geometry='geometry')
|
697
|
+
|
698
|
+
# Get CRS from HdfUtils
|
699
|
+
crs = HdfBase.get_projection(hdf_file)
|
700
|
+
if crs:
|
701
|
+
gdf.set_crs(crs, inplace=True)
|
702
|
+
|
635
703
|
# Combine attributes from all meshes
|
636
704
|
combined_attrs = {}
|
637
705
|
for df in dfs:
|
@@ -641,18 +709,17 @@ class HdfResultsMesh:
|
|
641
709
|
elif combined_attrs[key] != value:
|
642
710
|
combined_attrs[key] = f"Multiple values: {combined_attrs[key]}, {value}"
|
643
711
|
|
644
|
-
|
712
|
+
gdf.attrs.update(combined_attrs)
|
645
713
|
|
646
|
-
logger.info(f"Processed {len(
|
647
|
-
return
|
714
|
+
logger.info(f"Processed {len(gdf)} rows of summary output data")
|
715
|
+
return gdf
|
648
716
|
|
649
717
|
except Exception as e:
|
650
718
|
logger.error(f"Error processing summary output data: {e}")
|
651
719
|
raise ValueError(f"Error processing summary output data: {e}")
|
652
|
-
|
653
720
|
|
654
721
|
@staticmethod
|
655
|
-
def
|
722
|
+
def get_mesh_summary_output_group(hdf_file: h5py.File, mesh_name: str, var: str) -> Union[h5py.Group, h5py.Dataset]:
|
656
723
|
"""
|
657
724
|
Return the HDF group for a given mesh and summary output variable.
|
658
725
|
|
@@ -673,63 +740,3 @@ class HdfResultsMesh:
|
|
673
740
|
raise ValueError(f"Could not find HDF group or dataset at path '{output_path}'")
|
674
741
|
return output_item
|
675
742
|
|
676
|
-
@staticmethod
|
677
|
-
def plot_mesh_variable(variable_df: pd.DataFrame, variable_name: str, colormap: str = 'viridis', point_size: int = 10) -> None:
|
678
|
-
"""
|
679
|
-
Plot any mesh variable with consistent styling.
|
680
|
-
|
681
|
-
Args:
|
682
|
-
variable_df (pd.DataFrame): DataFrame containing the variable data
|
683
|
-
variable_name (str): Name of the variable (for labels)
|
684
|
-
colormap (str): Matplotlib colormap to use. Default: 'viridis'
|
685
|
-
point_size (int): Size of the scatter points. Default: 10
|
686
|
-
|
687
|
-
Returns:
|
688
|
-
None
|
689
|
-
|
690
|
-
Raises:
|
691
|
-
ImportError: If matplotlib is not installed
|
692
|
-
ValueError: If required columns are missing from variable_df
|
693
|
-
"""
|
694
|
-
try:
|
695
|
-
import matplotlib.pyplot as plt
|
696
|
-
except ImportError:
|
697
|
-
logger.error("matplotlib is required for plotting. Please install it with 'pip install matplotlib'")
|
698
|
-
raise ImportError("matplotlib is required for plotting")
|
699
|
-
|
700
|
-
# Get cell coordinates if not in variable_df
|
701
|
-
if 'geometry' not in variable_df.columns:
|
702
|
-
cell_coords = HdfMesh.mesh_cell_points(plan_hdf_path)
|
703
|
-
merged_df = pd.merge(variable_df, cell_coords, on=['mesh_name', 'cell_id'])
|
704
|
-
else:
|
705
|
-
merged_df = variable_df
|
706
|
-
|
707
|
-
# Extract coordinates, handling None values
|
708
|
-
merged_df = merged_df.dropna(subset=['geometry'])
|
709
|
-
merged_df['x'] = merged_df['geometry'].apply(lambda geom: geom.x if geom is not None else None)
|
710
|
-
merged_df['y'] = merged_df['geometry'].apply(lambda geom: geom.y if geom is not None else None)
|
711
|
-
|
712
|
-
# Drop any rows with None coordinates
|
713
|
-
merged_df = merged_df.dropna(subset=['x', 'y'])
|
714
|
-
|
715
|
-
if len(merged_df) == 0:
|
716
|
-
logger.error("No valid coordinates found for plotting")
|
717
|
-
raise ValueError("No valid coordinates found for plotting")
|
718
|
-
|
719
|
-
# Create plot
|
720
|
-
fig, ax = plt.subplots(figsize=(12, 8))
|
721
|
-
scatter = ax.scatter(merged_df['x'], merged_df['y'],
|
722
|
-
c=merged_df[variable_name],
|
723
|
-
cmap=colormap,
|
724
|
-
s=point_size)
|
725
|
-
|
726
|
-
# Customize plot
|
727
|
-
ax.set_title(f'{variable_name} per Cell')
|
728
|
-
ax.set_xlabel('X Coordinate')
|
729
|
-
ax.set_ylabel('Y Coordinate')
|
730
|
-
plt.colorbar(scatter, label=variable_name)
|
731
|
-
ax.grid(True, linestyle='--', alpha=0.7)
|
732
|
-
plt.rcParams.update({'font.size': 12})
|
733
|
-
plt.tight_layout()
|
734
|
-
plt.show()
|
735
|
-
|