rashdf 0.2.2__py3-none-any.whl → 0.4.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.
- cli.py +14 -8
- rashdf/geom.py +142 -26
- rashdf/plan.py +1019 -1
- rashdf/utils.py +42 -0
- {rashdf-0.2.2.dist-info → rashdf-0.4.0.dist-info}/METADATA +3 -2
- rashdf-0.4.0.dist-info/RECORD +12 -0
- {rashdf-0.2.2.dist-info → rashdf-0.4.0.dist-info}/WHEEL +1 -1
- rashdf-0.2.2.dist-info/RECORD +0 -12
- {rashdf-0.2.2.dist-info → rashdf-0.4.0.dist-info}/LICENSE +0 -0
- {rashdf-0.2.2.dist-info → rashdf-0.4.0.dist-info}/entry_points.txt +0 -0
- {rashdf-0.2.2.dist-info → rashdf-0.4.0.dist-info}/top_level.txt +0 -0
rashdf/plan.py
CHANGED
|
@@ -1,8 +1,152 @@
|
|
|
1
1
|
"""HEC-RAS Plan HDF class."""
|
|
2
2
|
|
|
3
3
|
from .geom import RasGeomHdf
|
|
4
|
-
from
|
|
4
|
+
from .utils import (
|
|
5
|
+
df_datetimes_to_str,
|
|
6
|
+
ras_timesteps_to_datetimes,
|
|
7
|
+
parse_ras_datetime_ms,
|
|
8
|
+
)
|
|
9
|
+
|
|
5
10
|
from geopandas import GeoDataFrame
|
|
11
|
+
import h5py
|
|
12
|
+
import numpy as np
|
|
13
|
+
from pandas import DataFrame
|
|
14
|
+
import pandas as pd
|
|
15
|
+
import xarray as xr
|
|
16
|
+
|
|
17
|
+
from datetime import datetime
|
|
18
|
+
from enum import Enum
|
|
19
|
+
from typing import Dict, List, Optional, Tuple, Union
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class RasPlanHdfError(Exception):
|
|
23
|
+
"""HEC-RAS Plan HDF error class."""
|
|
24
|
+
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class XsSteadyOutputVar(Enum):
|
|
29
|
+
"""Summary of steady cross section output variables."""
|
|
30
|
+
|
|
31
|
+
ENERGY_GRADE = "Energy Grade"
|
|
32
|
+
FLOW = "Flow"
|
|
33
|
+
WATER_SURFACE = "Water Surface"
|
|
34
|
+
ENCROACHMENT_STATION_LEFT = "Encroachment Station Left"
|
|
35
|
+
ENCROACHMENT_STATION_RIGHT = "Encroachment Station Right"
|
|
36
|
+
AREA_INEFFECTIVE_TOTAL = "Area including Ineffective Total"
|
|
37
|
+
VELOCITY_TOTAL = "Velocity Total"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
XS_STEADY_OUTPUT_ADDITIONAL = [
|
|
41
|
+
XsSteadyOutputVar.ENCROACHMENT_STATION_LEFT,
|
|
42
|
+
XsSteadyOutputVar.ENCROACHMENT_STATION_RIGHT,
|
|
43
|
+
XsSteadyOutputVar.AREA_INEFFECTIVE_TOTAL,
|
|
44
|
+
XsSteadyOutputVar.VELOCITY_TOTAL,
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class SummaryOutputVar(Enum):
|
|
49
|
+
"""Summary output variables."""
|
|
50
|
+
|
|
51
|
+
MAXIMUM_WATER_SURFACE = "Maximum Water Surface"
|
|
52
|
+
MINIMUM_WATER_SURFACE = "Minimum Water Surface"
|
|
53
|
+
MAXIMUM_FACE_VELOCITY = "Maximum Face Velocity"
|
|
54
|
+
MINIMUM_FACE_VELOCITY = "Minimum Face Velocity"
|
|
55
|
+
CELL_MAXIMUM_WATER_SURFACE_ERROR = "Cell Maximum Water Surface Error"
|
|
56
|
+
CELL_CUMULATIVE_ITERATION = "Cell Cumulative Iteration"
|
|
57
|
+
CELL_LAST_ITERATION = "Cell Last Iteration"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
SUMMARY_OUTPUT_VARS_CELLS = [
|
|
61
|
+
SummaryOutputVar.MAXIMUM_WATER_SURFACE,
|
|
62
|
+
SummaryOutputVar.MINIMUM_WATER_SURFACE,
|
|
63
|
+
SummaryOutputVar.CELL_MAXIMUM_WATER_SURFACE_ERROR,
|
|
64
|
+
SummaryOutputVar.CELL_CUMULATIVE_ITERATION,
|
|
65
|
+
SummaryOutputVar.CELL_LAST_ITERATION,
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
SUMMARY_OUTPUT_VARS_FACES = [
|
|
69
|
+
SummaryOutputVar.MAXIMUM_FACE_VELOCITY,
|
|
70
|
+
SummaryOutputVar.MINIMUM_FACE_VELOCITY,
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class TimeSeriesOutputVar(Enum):
|
|
75
|
+
"""Time series output variables."""
|
|
76
|
+
|
|
77
|
+
# Default Outputs
|
|
78
|
+
WATER_SURFACE = "Water Surface"
|
|
79
|
+
FACE_VELOCITY = "Face Velocity"
|
|
80
|
+
|
|
81
|
+
# Optional Outputs
|
|
82
|
+
CELL_COURANT = "Cell Courant"
|
|
83
|
+
CELL_CUMULATIVE_PRECIPITATION_DEPTH = "Cell Cumulative Precipitation Depth"
|
|
84
|
+
CELL_DIVERGENCE_TERM = "Cell Divergence Term"
|
|
85
|
+
CELL_EDDY_VISCOSITY_X = "Cell Eddy Viscosity - Eddy Viscosity X"
|
|
86
|
+
CELL_EDDY_VISCOSITY_Y = "Cell Eddy Viscosity - Eddy Viscosity Y"
|
|
87
|
+
CELL_FLOW_BALANCE = "Cell Flow Balance"
|
|
88
|
+
CELL_HYDRAULIC_DEPTH = "Cell Hydraulic Depth"
|
|
89
|
+
CELL_INVERT_DEPTH = "Cell Invert Depth"
|
|
90
|
+
CELL_STORAGE_TERM = "Cell Storage Term"
|
|
91
|
+
CELL_VELOCITY_X = "Cell Velocity - Velocity X"
|
|
92
|
+
CELL_VELOCITY_Y = "Cell Velocity - Velocity Y"
|
|
93
|
+
CELL_VOLUME = "Cell Volume"
|
|
94
|
+
CELL_VOLUME_ERROR = "Cell Volume Error"
|
|
95
|
+
CELL_WATER_SOURCE_TERM = "Cell Water Source Term"
|
|
96
|
+
CELL_WATER_SURFACE_ERROR = "Cell Water Surface Error"
|
|
97
|
+
|
|
98
|
+
FACE_COURANT = "Face Courant"
|
|
99
|
+
FACE_CUMULATIVE_VOLUME = "Face Cumulative Volume"
|
|
100
|
+
FACE_EDDY_VISCOSITY = "Face Eddy Viscosity"
|
|
101
|
+
FACE_FLOW = "Face Flow"
|
|
102
|
+
FACE_FLOW_PERIOD_AVERAGE = "Face Flow Period Average"
|
|
103
|
+
FACE_FRICTION_TERM = "Face Friction Term"
|
|
104
|
+
FACE_PRESSURE_GRADIENT_TERM = "Face Pressure Gradient Term"
|
|
105
|
+
FACE_SHEAR_STRESS = "Face Shear Stress"
|
|
106
|
+
FACE_TANGENTIAL_VELOCITY = "Face Tangential Velocity"
|
|
107
|
+
FACE_WATER_SURFACE = "Face Water Surface"
|
|
108
|
+
FACE_WIND_TERM = "Face Wind Term"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
TIME_SERIES_OUTPUT_VARS_CELLS = [
|
|
112
|
+
TimeSeriesOutputVar.WATER_SURFACE,
|
|
113
|
+
TimeSeriesOutputVar.CELL_COURANT,
|
|
114
|
+
TimeSeriesOutputVar.CELL_CUMULATIVE_PRECIPITATION_DEPTH,
|
|
115
|
+
TimeSeriesOutputVar.CELL_DIVERGENCE_TERM,
|
|
116
|
+
TimeSeriesOutputVar.CELL_EDDY_VISCOSITY_X,
|
|
117
|
+
TimeSeriesOutputVar.CELL_EDDY_VISCOSITY_Y,
|
|
118
|
+
TimeSeriesOutputVar.CELL_FLOW_BALANCE,
|
|
119
|
+
TimeSeriesOutputVar.CELL_HYDRAULIC_DEPTH,
|
|
120
|
+
TimeSeriesOutputVar.CELL_INVERT_DEPTH,
|
|
121
|
+
TimeSeriesOutputVar.CELL_STORAGE_TERM,
|
|
122
|
+
TimeSeriesOutputVar.CELL_VELOCITY_X,
|
|
123
|
+
TimeSeriesOutputVar.CELL_VELOCITY_Y,
|
|
124
|
+
TimeSeriesOutputVar.CELL_VOLUME,
|
|
125
|
+
TimeSeriesOutputVar.CELL_VOLUME_ERROR,
|
|
126
|
+
TimeSeriesOutputVar.CELL_WATER_SOURCE_TERM,
|
|
127
|
+
TimeSeriesOutputVar.CELL_WATER_SURFACE_ERROR,
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
TIME_SERIES_OUTPUT_VARS_FACES = [
|
|
131
|
+
TimeSeriesOutputVar.FACE_COURANT,
|
|
132
|
+
TimeSeriesOutputVar.FACE_CUMULATIVE_VOLUME,
|
|
133
|
+
TimeSeriesOutputVar.FACE_EDDY_VISCOSITY,
|
|
134
|
+
TimeSeriesOutputVar.FACE_FLOW,
|
|
135
|
+
TimeSeriesOutputVar.FACE_FLOW_PERIOD_AVERAGE,
|
|
136
|
+
TimeSeriesOutputVar.FACE_FRICTION_TERM,
|
|
137
|
+
TimeSeriesOutputVar.FACE_PRESSURE_GRADIENT_TERM,
|
|
138
|
+
TimeSeriesOutputVar.FACE_SHEAR_STRESS,
|
|
139
|
+
TimeSeriesOutputVar.FACE_TANGENTIAL_VELOCITY,
|
|
140
|
+
TimeSeriesOutputVar.FACE_VELOCITY,
|
|
141
|
+
TimeSeriesOutputVar.FACE_WATER_SURFACE,
|
|
142
|
+
# TODO: investigate why "Face Wind Term" data gets written as a 1D array
|
|
143
|
+
# TimeSeriesOutputVar.FACE_WIND_TERM,
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
TIME_SERIES_OUTPUT_VARS_DEFAULT = [
|
|
147
|
+
TimeSeriesOutputVar.WATER_SURFACE,
|
|
148
|
+
TimeSeriesOutputVar.FACE_VELOCITY,
|
|
149
|
+
]
|
|
6
150
|
|
|
7
151
|
|
|
8
152
|
class RasPlanHdf(RasGeomHdf):
|
|
@@ -14,6 +158,17 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
14
158
|
RESULTS_UNSTEADY_PATH = "Results/Unsteady"
|
|
15
159
|
RESULTS_UNSTEADY_SUMMARY_PATH = f"{RESULTS_UNSTEADY_PATH}/Summary"
|
|
16
160
|
VOLUME_ACCOUNTING_PATH = f"{RESULTS_UNSTEADY_PATH}/Volume Accounting"
|
|
161
|
+
BASE_OUTPUT_PATH = f"{RESULTS_UNSTEADY_PATH}/Output/Output Blocks/Base Output"
|
|
162
|
+
SUMMARY_OUTPUT_2D_FLOW_AREAS_PATH = (
|
|
163
|
+
f"{BASE_OUTPUT_PATH}/Summary Output/2D Flow Areas"
|
|
164
|
+
)
|
|
165
|
+
UNSTEADY_TIME_SERIES_PATH = f"{BASE_OUTPUT_PATH}/Unsteady Time Series"
|
|
166
|
+
|
|
167
|
+
RESULTS_STEADY_PATH = "Results/Steady"
|
|
168
|
+
BASE_STEADY_PATH = f"{RESULTS_STEADY_PATH}/Output/Output Blocks/Base Output"
|
|
169
|
+
STEADY_PROFILES_PATH = f"{BASE_STEADY_PATH}/Steady Profiles"
|
|
170
|
+
STEADY_XS_PATH = f"{STEADY_PROFILES_PATH}/Cross Sections"
|
|
171
|
+
STEADY_XS_ADDITIONAL_PATH = f"{STEADY_XS_PATH}/Additional Variables"
|
|
17
172
|
|
|
18
173
|
def __init__(self, name: str, **kwargs):
|
|
19
174
|
"""Open a HEC-RAS Plan HDF file.
|
|
@@ -27,6 +182,745 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
27
182
|
"""
|
|
28
183
|
super().__init__(name, **kwargs)
|
|
29
184
|
|
|
185
|
+
def _simulation_start_time(self) -> datetime:
|
|
186
|
+
"""Return the simulation start time from the plan file.
|
|
187
|
+
|
|
188
|
+
Returns
|
|
189
|
+
-------
|
|
190
|
+
datetime
|
|
191
|
+
The simulation start time.
|
|
192
|
+
"""
|
|
193
|
+
plan_info = self.get_plan_info_attrs()
|
|
194
|
+
return plan_info["Simulation Start Time"]
|
|
195
|
+
|
|
196
|
+
def _2d_flow_area_names_and_counts(self) -> List[Tuple[str, int]]:
|
|
197
|
+
"""
|
|
198
|
+
Return a list of 2D flow area names and cell counts.
|
|
199
|
+
|
|
200
|
+
Returns
|
|
201
|
+
-------
|
|
202
|
+
List[Tuple[str, int]]
|
|
203
|
+
A list of tuples, where each tuple contains a 2D flow area name
|
|
204
|
+
and the number of cells in that area.
|
|
205
|
+
"""
|
|
206
|
+
d2_flow_areas = self[f"{self.FLOW_AREA_2D_PATH}/Attributes"][:]
|
|
207
|
+
return [
|
|
208
|
+
(d2_flow_area[0].decode("utf-8"), d2_flow_area[-1])
|
|
209
|
+
for d2_flow_area in d2_flow_areas
|
|
210
|
+
]
|
|
211
|
+
|
|
212
|
+
def _mesh_summary_output_group(
|
|
213
|
+
self, mesh_name: str, output_var: SummaryOutputVar
|
|
214
|
+
) -> h5py.Group:
|
|
215
|
+
"""Return the HDF group for a 2D flow area summary output variable.
|
|
216
|
+
|
|
217
|
+
Parameters
|
|
218
|
+
----------
|
|
219
|
+
mesh_name : str
|
|
220
|
+
The name of the 2D flow area mesh.
|
|
221
|
+
output_var : str
|
|
222
|
+
The name of the output variable.
|
|
223
|
+
|
|
224
|
+
Returns
|
|
225
|
+
-------
|
|
226
|
+
h5py.Group
|
|
227
|
+
The HDF group for the output variable.
|
|
228
|
+
"""
|
|
229
|
+
output_path = (
|
|
230
|
+
f"{self.SUMMARY_OUTPUT_2D_FLOW_AREAS_PATH}/{mesh_name}/{output_var.value}"
|
|
231
|
+
)
|
|
232
|
+
output_group = self.get(output_path)
|
|
233
|
+
if output_group is None:
|
|
234
|
+
raise RasPlanHdfError(
|
|
235
|
+
f"Could not find HDF group at path '{output_path}'."
|
|
236
|
+
" Does the Plan HDF file contain 2D output data?"
|
|
237
|
+
)
|
|
238
|
+
return output_group
|
|
239
|
+
|
|
240
|
+
def _mesh_summary_output_min_max_values(
|
|
241
|
+
self, mesh_name: str, var: SummaryOutputVar
|
|
242
|
+
) -> np.ndarray:
|
|
243
|
+
"""Return values for a "Maximum"/"Minimum" summary output variable.
|
|
244
|
+
|
|
245
|
+
Parameters
|
|
246
|
+
----------
|
|
247
|
+
mesh_name : str
|
|
248
|
+
The name of the 2D flow area mesh.
|
|
249
|
+
var : SummaryOutputVar
|
|
250
|
+
The summary output variable to retrieve.
|
|
251
|
+
|
|
252
|
+
Returns
|
|
253
|
+
-------
|
|
254
|
+
np.ndarray
|
|
255
|
+
An array of maximum water surface elevation values.
|
|
256
|
+
"""
|
|
257
|
+
max_ws_group = self._mesh_summary_output_group(mesh_name, var)
|
|
258
|
+
max_ws_raw = max_ws_group[:]
|
|
259
|
+
max_ws_values = max_ws_raw[0]
|
|
260
|
+
return max_ws_values
|
|
261
|
+
|
|
262
|
+
def _summary_output_min_max_time_unit(self, dataset: h5py.Dataset) -> str:
|
|
263
|
+
"""Return the time unit for "Maximum"/"Minimum" summary output datasets.
|
|
264
|
+
|
|
265
|
+
I.e., for summary output such as "Maximum Water Surface", "Minimum Water Surface", etc.
|
|
266
|
+
|
|
267
|
+
Should normally return the string: "days".
|
|
268
|
+
|
|
269
|
+
Parameters
|
|
270
|
+
----------
|
|
271
|
+
mesh_name : str
|
|
272
|
+
The name of the 2D flow area mesh.
|
|
273
|
+
|
|
274
|
+
Returns
|
|
275
|
+
-------
|
|
276
|
+
str
|
|
277
|
+
The time unit for the maximum water surface elevation data.
|
|
278
|
+
"""
|
|
279
|
+
if "Units per row" in dataset.attrs:
|
|
280
|
+
units = dataset.attrs["Units per row"]
|
|
281
|
+
else:
|
|
282
|
+
units = dataset.attrs["Units"]
|
|
283
|
+
# expect an array of size 2, with the first element being length or velocity units
|
|
284
|
+
# and the second element being time units (e.g., ["ft", "days"])
|
|
285
|
+
time_unit = units[1]
|
|
286
|
+
return time_unit.decode("utf-8")
|
|
287
|
+
|
|
288
|
+
def _mesh_summary_output_min_max_times(
|
|
289
|
+
self,
|
|
290
|
+
mesh_name: str,
|
|
291
|
+
var: SummaryOutputVar,
|
|
292
|
+
time_unit: str = "days",
|
|
293
|
+
round_to: str = "0.1 s",
|
|
294
|
+
) -> np.ndarray[np.datetime64]:
|
|
295
|
+
"""Return an array of times for min/max summary output data.
|
|
296
|
+
|
|
297
|
+
Parameters
|
|
298
|
+
----------
|
|
299
|
+
mesh_name : str
|
|
300
|
+
The name of the 2D flow area mesh.
|
|
301
|
+
var : SummaryOutputVar
|
|
302
|
+
The summary output variable to retrieve.
|
|
303
|
+
time_unit : str, optional
|
|
304
|
+
The time unit for the maximum water surface elevation data.
|
|
305
|
+
Default: "days".
|
|
306
|
+
round_to : str, optional
|
|
307
|
+
The time unit to round the datetimes to. Default: "0.1 s" (seconds).
|
|
308
|
+
|
|
309
|
+
Returns
|
|
310
|
+
-------
|
|
311
|
+
np.ndarray[np.datetime64]
|
|
312
|
+
An array of times for the maximum water surface elevation data.
|
|
313
|
+
"""
|
|
314
|
+
start_time = self._simulation_start_time()
|
|
315
|
+
max_ws_group = self._mesh_summary_output_group(mesh_name, var)
|
|
316
|
+
time_unit = self._summary_output_min_max_time_unit(max_ws_group)
|
|
317
|
+
max_ws_raw = max_ws_group[:]
|
|
318
|
+
max_ws_times_raw = max_ws_raw[1]
|
|
319
|
+
# we get weirdly specific datetime values if we don't round to e.g., 0.1 seconds;
|
|
320
|
+
# otherwise datetimes don't align with the actual timestep values in the plan file
|
|
321
|
+
max_ws_times = ras_timesteps_to_datetimes(
|
|
322
|
+
max_ws_times_raw, start_time, time_unit=time_unit, round_to=round_to
|
|
323
|
+
)
|
|
324
|
+
return max_ws_times
|
|
325
|
+
|
|
326
|
+
def _mesh_summary_output_min_max(
|
|
327
|
+
self,
|
|
328
|
+
var: SummaryOutputVar,
|
|
329
|
+
value_col: str = "value",
|
|
330
|
+
time_col: str = "time",
|
|
331
|
+
round_to: str = "0.1 s",
|
|
332
|
+
) -> DataFrame:
|
|
333
|
+
"""Return the min/max values and times for a summary output variable.
|
|
334
|
+
|
|
335
|
+
Valid for:
|
|
336
|
+
- Maximum Water Surface
|
|
337
|
+
- Minimum Water Surface
|
|
338
|
+
- Maximum Face Velocity
|
|
339
|
+
- Minimum Face Velocity
|
|
340
|
+
- Cell Maximum Water Surface Error
|
|
341
|
+
|
|
342
|
+
Parameters
|
|
343
|
+
----------
|
|
344
|
+
var : SummaryOutputVar
|
|
345
|
+
The summary output variable to retrieve.
|
|
346
|
+
round_to : str, optional
|
|
347
|
+
The time unit to round the datetimes to. Default: "0.1 s" (seconds).
|
|
348
|
+
|
|
349
|
+
Returns
|
|
350
|
+
-------
|
|
351
|
+
DataFrame
|
|
352
|
+
A DataFrame with columns 'mesh_name', 'cell_id' or 'face_id', 'value', and 'time'.
|
|
353
|
+
"""
|
|
354
|
+
dfs = []
|
|
355
|
+
for mesh_name, cell_count in self._2d_flow_area_names_and_counts():
|
|
356
|
+
values = self._mesh_summary_output_min_max_values(mesh_name, var)
|
|
357
|
+
times = self._mesh_summary_output_min_max_times(
|
|
358
|
+
mesh_name, var, round_to=round_to
|
|
359
|
+
)
|
|
360
|
+
if var in [
|
|
361
|
+
SummaryOutputVar.MAXIMUM_FACE_VELOCITY,
|
|
362
|
+
SummaryOutputVar.MINIMUM_FACE_VELOCITY,
|
|
363
|
+
]:
|
|
364
|
+
geom_id_col = "face_id"
|
|
365
|
+
else:
|
|
366
|
+
geom_id_col = "cell_id"
|
|
367
|
+
# The 2D mesh output data contains values for more cells than are actually
|
|
368
|
+
# in the mesh. The the true number of cells for a mesh is found in the table:
|
|
369
|
+
# "/Geometry/2D Flow Areas/Attributes". The number of cells in the 2D output
|
|
370
|
+
# data instead matches the number of cells in the "Cells Center Coordinate"
|
|
371
|
+
# array, which contains extra points along the perimeter of the mesh. These
|
|
372
|
+
# extra points are appended to the end of the mesh data and contain bogus
|
|
373
|
+
# output values (e.g., 0.0, NaN). We need to filter out these bogus values.
|
|
374
|
+
values = values[:cell_count]
|
|
375
|
+
times = times[:cell_count]
|
|
376
|
+
df = DataFrame(
|
|
377
|
+
{
|
|
378
|
+
"mesh_name": [mesh_name] * len(values),
|
|
379
|
+
geom_id_col: range(len(values)),
|
|
380
|
+
value_col: values,
|
|
381
|
+
time_col: times,
|
|
382
|
+
}
|
|
383
|
+
)
|
|
384
|
+
dfs.append(df)
|
|
385
|
+
df = pd.concat(dfs, ignore_index=True)
|
|
386
|
+
return df
|
|
387
|
+
|
|
388
|
+
def _mesh_summary_output_basic(
|
|
389
|
+
self, var: SummaryOutputVar, value_col: str = "value"
|
|
390
|
+
) -> DataFrame:
|
|
391
|
+
"""Return values and times for a summary output variable.
|
|
392
|
+
|
|
393
|
+
Valid for:
|
|
394
|
+
- Cell Cumulative Iteration (i.e. Cumulative Max Iterations)
|
|
395
|
+
- Cell Last Iteration
|
|
396
|
+
|
|
397
|
+
Parameters
|
|
398
|
+
----------
|
|
399
|
+
var : SummaryOutputVar
|
|
400
|
+
The summary output variable to retrieve.
|
|
401
|
+
|
|
402
|
+
Returns
|
|
403
|
+
-------
|
|
404
|
+
DataFrame
|
|
405
|
+
A DataFrame with columns 'mesh_name', 'cell_id' or 'face_id', 'value', and 'time'.
|
|
406
|
+
"""
|
|
407
|
+
dfs = []
|
|
408
|
+
for mesh_name, cell_count in self._2d_flow_area_names_and_counts():
|
|
409
|
+
group = self._mesh_summary_output_group(mesh_name, var)
|
|
410
|
+
values = group[:][:cell_count]
|
|
411
|
+
df = DataFrame(
|
|
412
|
+
{
|
|
413
|
+
"mesh_name": [mesh_name] * len(values),
|
|
414
|
+
"cell_id": range(len(values)),
|
|
415
|
+
value_col: values,
|
|
416
|
+
}
|
|
417
|
+
)
|
|
418
|
+
dfs.append(df)
|
|
419
|
+
df = pd.concat(dfs, ignore_index=True)
|
|
420
|
+
return df
|
|
421
|
+
|
|
422
|
+
def mesh_max_iter(self) -> DataFrame:
|
|
423
|
+
"""Return the number of times each cell in the mesh reached the max number of iterations.
|
|
424
|
+
|
|
425
|
+
Returns
|
|
426
|
+
-------
|
|
427
|
+
DataFrame
|
|
428
|
+
A DataFrame with columns 'mesh_name', 'cell_id', and 'max_iterations'.
|
|
429
|
+
"""
|
|
430
|
+
df = self._mesh_summary_output_basic(
|
|
431
|
+
SummaryOutputVar.CELL_CUMULATIVE_ITERATION, value_col="max_iter"
|
|
432
|
+
)
|
|
433
|
+
return df
|
|
434
|
+
|
|
435
|
+
def mesh_last_iter(self) -> DataFrame:
|
|
436
|
+
"""Return the number of times each cell in the mesh was the last cell to converge.
|
|
437
|
+
|
|
438
|
+
Returns
|
|
439
|
+
-------
|
|
440
|
+
DataFrame
|
|
441
|
+
A DataFrame with columns 'mesh_name', 'cell_id', and 'last_iter'.
|
|
442
|
+
"""
|
|
443
|
+
df = self._mesh_summary_output_basic(
|
|
444
|
+
SummaryOutputVar.CELL_LAST_ITERATION, value_col="last_iter"
|
|
445
|
+
)
|
|
446
|
+
return df
|
|
447
|
+
|
|
448
|
+
def mesh_max_ws(self, round_to: str = "0.1 s") -> DataFrame:
|
|
449
|
+
"""Return the max water surface elevation for each cell in the mesh.
|
|
450
|
+
|
|
451
|
+
Includes the corresponding time of max water surface elevation.
|
|
452
|
+
|
|
453
|
+
Parameters
|
|
454
|
+
----------
|
|
455
|
+
round_to : str, optional
|
|
456
|
+
The time unit to round the datetimes to. Default: "0.1 s" (seconds).
|
|
457
|
+
See Pandas documentation for valid time units:
|
|
458
|
+
https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases
|
|
459
|
+
|
|
460
|
+
Returns
|
|
461
|
+
-------
|
|
462
|
+
DataFrame
|
|
463
|
+
A DataFrame with columns 'mesh_name', 'cell_id', 'max_ws', and 'max_ws_time'.
|
|
464
|
+
"""
|
|
465
|
+
df = self._mesh_summary_output_min_max(
|
|
466
|
+
SummaryOutputVar.MAXIMUM_WATER_SURFACE,
|
|
467
|
+
value_col="max_ws",
|
|
468
|
+
time_col="max_ws_time",
|
|
469
|
+
round_to=round_to,
|
|
470
|
+
)
|
|
471
|
+
return df
|
|
472
|
+
|
|
473
|
+
def mesh_min_ws(self, round_to: str = "0.1 s") -> DataFrame:
|
|
474
|
+
"""Return the min water surface elevation for each cell in the mesh.
|
|
475
|
+
|
|
476
|
+
Includes the corresponding time of min water surface elevation.
|
|
477
|
+
|
|
478
|
+
Parameters
|
|
479
|
+
----------
|
|
480
|
+
round_to : str, optional
|
|
481
|
+
The time unit to round the datetimes to. Default: "0.1 s" (seconds).
|
|
482
|
+
See Pandas documentation for valid time units:
|
|
483
|
+
https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html
|
|
484
|
+
|
|
485
|
+
Returns
|
|
486
|
+
-------
|
|
487
|
+
DataFrame
|
|
488
|
+
A DataFrame with columns 'mesh_name', 'cell_id', 'min_ws', and 'min_ws_time'.
|
|
489
|
+
"""
|
|
490
|
+
df = self._mesh_summary_output_min_max(
|
|
491
|
+
SummaryOutputVar.MINIMUM_WATER_SURFACE,
|
|
492
|
+
value_col="min_ws",
|
|
493
|
+
time_col="min_ws_time",
|
|
494
|
+
round_to=round_to,
|
|
495
|
+
)
|
|
496
|
+
return df
|
|
497
|
+
|
|
498
|
+
def mesh_max_face_v(self, round_to: str = "0.1 s") -> DataFrame:
|
|
499
|
+
"""Return the max face velocity for each face in the mesh.
|
|
500
|
+
|
|
501
|
+
Includes the corresponding time of max face velocity.
|
|
502
|
+
|
|
503
|
+
Parameters
|
|
504
|
+
----------
|
|
505
|
+
round_to : str, optional
|
|
506
|
+
The time unit to round the datetimes to. Default: "0.1 s" (seconds).
|
|
507
|
+
See Pandas documentation for valid time units:
|
|
508
|
+
https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html
|
|
509
|
+
|
|
510
|
+
Returns
|
|
511
|
+
-------
|
|
512
|
+
DataFrame
|
|
513
|
+
A DataFrame with columns 'mesh_name', 'face_id', 'max_v', and 'max_v_time'.
|
|
514
|
+
"""
|
|
515
|
+
df = self._mesh_summary_output_min_max(
|
|
516
|
+
SummaryOutputVar.MAXIMUM_FACE_VELOCITY,
|
|
517
|
+
value_col="max_v",
|
|
518
|
+
time_col="max_v_time",
|
|
519
|
+
round_to=round_to,
|
|
520
|
+
)
|
|
521
|
+
return df
|
|
522
|
+
|
|
523
|
+
def mesh_min_face_v(self, round_to: str = "0.1 s") -> DataFrame:
|
|
524
|
+
"""Return the min face velocity for each face in the mesh.
|
|
525
|
+
|
|
526
|
+
Includes the corresponding time of min face velocity.
|
|
527
|
+
|
|
528
|
+
Parameters
|
|
529
|
+
----------
|
|
530
|
+
round_to : str, optional
|
|
531
|
+
The time unit to round the datetimes to. Default: "0.1 s" (seconds).
|
|
532
|
+
See Pandas documentation for valid time units:
|
|
533
|
+
https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html
|
|
534
|
+
|
|
535
|
+
Returns
|
|
536
|
+
-------
|
|
537
|
+
DataFrame
|
|
538
|
+
A DataFrame with columns 'mesh_name', 'face_id', 'min_v', and 'min_v_time'.
|
|
539
|
+
"""
|
|
540
|
+
df = self._mesh_summary_output_min_max(
|
|
541
|
+
SummaryOutputVar.MINIMUM_FACE_VELOCITY,
|
|
542
|
+
value_col="min_v",
|
|
543
|
+
time_col="min_v_time",
|
|
544
|
+
round_to=round_to,
|
|
545
|
+
)
|
|
546
|
+
return df
|
|
547
|
+
|
|
548
|
+
def mesh_max_ws_err(self, round_to: str = "0.1 s") -> DataFrame:
|
|
549
|
+
"""Return the max water surface error for each cell in the mesh.
|
|
550
|
+
|
|
551
|
+
Includes the corresponding time of max water surface error.
|
|
552
|
+
|
|
553
|
+
Parameters
|
|
554
|
+
----------
|
|
555
|
+
round_to : str, optional
|
|
556
|
+
The time unit to round the datetimes to. Default: "0.1 s" (seconds).
|
|
557
|
+
See Pandas documentation for valid time units:
|
|
558
|
+
https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html
|
|
559
|
+
|
|
560
|
+
Returns
|
|
561
|
+
-------
|
|
562
|
+
DataFrame
|
|
563
|
+
A DataFrame with columns 'mesh_name', 'cell_id', 'max_ws_err', and 'max_ws_err_time'.
|
|
564
|
+
"""
|
|
565
|
+
df = self._mesh_summary_output_min_max(
|
|
566
|
+
SummaryOutputVar.CELL_MAXIMUM_WATER_SURFACE_ERROR,
|
|
567
|
+
value_col="max_ws_err",
|
|
568
|
+
time_col="max_ws_err_time",
|
|
569
|
+
round_to=round_to,
|
|
570
|
+
)
|
|
571
|
+
return df
|
|
572
|
+
|
|
573
|
+
def mesh_summary_output(
|
|
574
|
+
self, var: SummaryOutputVar, round_to: str = "0.1 s"
|
|
575
|
+
) -> DataFrame:
|
|
576
|
+
"""Return the summary output data for a given variable.
|
|
577
|
+
|
|
578
|
+
Parameters
|
|
579
|
+
----------
|
|
580
|
+
var : SummaryOutputVar
|
|
581
|
+
The summary output variable to retrieve.
|
|
582
|
+
|
|
583
|
+
Returns
|
|
584
|
+
-------
|
|
585
|
+
DataFrame
|
|
586
|
+
A DataFrame with columns 'mesh_name', 'cell_id' or 'face_id', a value column, and a time column.
|
|
587
|
+
"""
|
|
588
|
+
methods_with_times = {
|
|
589
|
+
SummaryOutputVar.MAXIMUM_WATER_SURFACE: self.mesh_max_ws,
|
|
590
|
+
SummaryOutputVar.MINIMUM_WATER_SURFACE: self.mesh_min_ws,
|
|
591
|
+
SummaryOutputVar.MAXIMUM_FACE_VELOCITY: self.mesh_max_face_v,
|
|
592
|
+
SummaryOutputVar.MINIMUM_FACE_VELOCITY: self.mesh_min_face_v,
|
|
593
|
+
SummaryOutputVar.CELL_MAXIMUM_WATER_SURFACE_ERROR: self.mesh_max_ws_err,
|
|
594
|
+
}
|
|
595
|
+
other_methods = {
|
|
596
|
+
SummaryOutputVar.CELL_CUMULATIVE_ITERATION: self.mesh_max_iter,
|
|
597
|
+
SummaryOutputVar.CELL_LAST_ITERATION: self.mesh_last_iter,
|
|
598
|
+
}
|
|
599
|
+
if var in methods_with_times:
|
|
600
|
+
df = methods_with_times[var](round_to=round_to)
|
|
601
|
+
else:
|
|
602
|
+
df = other_methods[var]()
|
|
603
|
+
return df
|
|
604
|
+
|
|
605
|
+
def _summary_output_vars(
|
|
606
|
+
self, cells_or_faces: Optional[str] = None
|
|
607
|
+
) -> List[SummaryOutputVar]:
|
|
608
|
+
"""Return a list of available summary output variables from the Plan HDF file.
|
|
609
|
+
|
|
610
|
+
Returns
|
|
611
|
+
-------
|
|
612
|
+
List[SummaryOutputVar]
|
|
613
|
+
A list of summary output variables.
|
|
614
|
+
"""
|
|
615
|
+
mesh_names_counts = self._2d_flow_area_names_and_counts()
|
|
616
|
+
mesh_names = [mesh_name for mesh_name, _ in mesh_names_counts]
|
|
617
|
+
vars = set()
|
|
618
|
+
for mesh_name in mesh_names:
|
|
619
|
+
path = f"{self.SUMMARY_OUTPUT_2D_FLOW_AREAS_PATH}/{mesh_name}"
|
|
620
|
+
datasets = self[path].keys()
|
|
621
|
+
for dataset in datasets:
|
|
622
|
+
try:
|
|
623
|
+
var = SummaryOutputVar(dataset)
|
|
624
|
+
except ValueError:
|
|
625
|
+
continue
|
|
626
|
+
vars.add(var)
|
|
627
|
+
if cells_or_faces == "cells":
|
|
628
|
+
vars = vars.intersection(SUMMARY_OUTPUT_VARS_CELLS)
|
|
629
|
+
elif cells_or_faces == "faces":
|
|
630
|
+
vars = vars.intersection(SUMMARY_OUTPUT_VARS_FACES)
|
|
631
|
+
return sorted(list(vars), key=lambda x: x.value)
|
|
632
|
+
|
|
633
|
+
def _mesh_summary_outputs_gdf(
|
|
634
|
+
self,
|
|
635
|
+
geom_func: str,
|
|
636
|
+
cells_or_faces: str = "cells",
|
|
637
|
+
include_output: Union[bool, List[SummaryOutputVar]] = True,
|
|
638
|
+
round_to: str = "0.1 s",
|
|
639
|
+
datetime_to_str: bool = False,
|
|
640
|
+
) -> GeoDataFrame:
|
|
641
|
+
"""Return a GeoDataFrame with mesh geometry and summary output data.
|
|
642
|
+
|
|
643
|
+
Parameters
|
|
644
|
+
----------
|
|
645
|
+
geom_func : str
|
|
646
|
+
The method name to call to get the mesh geometry.
|
|
647
|
+
cells_or_faces : str, optional
|
|
648
|
+
The type of geometry to include in the GeoDataFrame.
|
|
649
|
+
Must be either "cells" or "faces". (default: "cells")
|
|
650
|
+
include_output : Union[bool, List[SummaryOutputVar]], optional
|
|
651
|
+
If True, include all available summary output data in the GeoDataFrame.
|
|
652
|
+
If a list of SummaryOutputVar values, include only the specified summary output data.
|
|
653
|
+
If False, do not include any summary output data.
|
|
654
|
+
(default: True)
|
|
655
|
+
round_to : str, optional
|
|
656
|
+
The time unit to round the datetimes to. Default: "0.1 s" (seconds).
|
|
657
|
+
datetime_to_str : bool, optional
|
|
658
|
+
If True, convert datetime columns to strings. (default: False)
|
|
659
|
+
"""
|
|
660
|
+
gdf = getattr(super(), geom_func)()
|
|
661
|
+
if include_output is False:
|
|
662
|
+
return gdf
|
|
663
|
+
if include_output is True:
|
|
664
|
+
summary_output_vars = self._summary_output_vars(
|
|
665
|
+
cells_or_faces=cells_or_faces
|
|
666
|
+
)
|
|
667
|
+
elif isinstance(include_output, list):
|
|
668
|
+
summary_output_vars = []
|
|
669
|
+
for var in include_output:
|
|
670
|
+
if not isinstance(var, SummaryOutputVar):
|
|
671
|
+
var = SummaryOutputVar(var)
|
|
672
|
+
summary_output_vars.append(var)
|
|
673
|
+
else:
|
|
674
|
+
raise ValueError(
|
|
675
|
+
"include_output must be a boolean or a list of SummaryOutputVar values."
|
|
676
|
+
)
|
|
677
|
+
if cells_or_faces == "cells":
|
|
678
|
+
geom_id_col = "cell_id"
|
|
679
|
+
elif cells_or_faces == "faces":
|
|
680
|
+
geom_id_col = "face_id"
|
|
681
|
+
else:
|
|
682
|
+
raise ValueError('cells_or_faces must be either "cells" or "faces".')
|
|
683
|
+
for var in summary_output_vars:
|
|
684
|
+
df = self.mesh_summary_output(var, round_to=round_to)
|
|
685
|
+
gdf = gdf.merge(df, on=["mesh_name", geom_id_col], how="left")
|
|
686
|
+
if datetime_to_str:
|
|
687
|
+
gdf = df_datetimes_to_str(gdf)
|
|
688
|
+
return gdf
|
|
689
|
+
|
|
690
|
+
def mesh_cell_points(
|
|
691
|
+
self,
|
|
692
|
+
include_output: Union[bool, List[SummaryOutputVar], List[str]] = True,
|
|
693
|
+
round_to: str = "0.1 s",
|
|
694
|
+
datetime_to_str: bool = False,
|
|
695
|
+
) -> GeoDataFrame:
|
|
696
|
+
"""Return the cell points for each cell in the mesh, including summary output.
|
|
697
|
+
|
|
698
|
+
Parameters
|
|
699
|
+
----------
|
|
700
|
+
include_output : Union[bool, List[SummaryOutputVar], List[str]], optional
|
|
701
|
+
If True, include all available summary output data in the GeoDataFrame.
|
|
702
|
+
If a list of SummaryOutputVar values, include only the specified summary output data.
|
|
703
|
+
If a list of strings, include only the specified summary output data by name.
|
|
704
|
+
If False, do not include any summary output data.
|
|
705
|
+
(default: True)
|
|
706
|
+
round_to : str, optional
|
|
707
|
+
The time unit to round the datetimes to. Default: "0.1 s" (seconds).
|
|
708
|
+
datetime_to_str : bool, optional
|
|
709
|
+
If True, convert datetime columns to strings. (default: False)
|
|
710
|
+
|
|
711
|
+
Returns
|
|
712
|
+
-------
|
|
713
|
+
GeoDataFrame
|
|
714
|
+
A GeoDataFrame with columns 'mesh_name', 'cell_id', 'geometry', and columns for each
|
|
715
|
+
summary output variable.
|
|
716
|
+
"""
|
|
717
|
+
return self._mesh_summary_outputs_gdf(
|
|
718
|
+
"mesh_cell_points",
|
|
719
|
+
"cells",
|
|
720
|
+
include_output=include_output,
|
|
721
|
+
round_to=round_to,
|
|
722
|
+
datetime_to_str=datetime_to_str,
|
|
723
|
+
)
|
|
724
|
+
|
|
725
|
+
def mesh_cell_polygons(
|
|
726
|
+
self,
|
|
727
|
+
include_output: Union[bool, List[SummaryOutputVar], List[str]] = True,
|
|
728
|
+
round_to: str = "0.1 s",
|
|
729
|
+
datetime_to_str: bool = False,
|
|
730
|
+
) -> GeoDataFrame:
|
|
731
|
+
"""Return the cell polygons for each cell in the mesh, including output.
|
|
732
|
+
|
|
733
|
+
Parameters
|
|
734
|
+
----------
|
|
735
|
+
include_output : Union[bool, List[SummaryOutputVar], List[str]], optional
|
|
736
|
+
If True, include all available summary output data in the GeoDataFrame.
|
|
737
|
+
If a list of SummaryOutputVar values, include only the specified summary output data.
|
|
738
|
+
If a list of strings, include only the specified summary output data by name.
|
|
739
|
+
If False, do not include any summary output data.
|
|
740
|
+
(default: True)
|
|
741
|
+
round_to : str, optional
|
|
742
|
+
The time unit to round the datetimes to. Default: "0.1 s" (seconds).
|
|
743
|
+
datetime_to_str : bool, optional
|
|
744
|
+
If True, convert datetime columns to strings. (default: False)
|
|
745
|
+
|
|
746
|
+
Returns
|
|
747
|
+
-------
|
|
748
|
+
GeoDataFrame
|
|
749
|
+
A GeoDataFrame with columns 'mesh_name', 'cell_id', 'geometry', and columns for each
|
|
750
|
+
summary output variable.
|
|
751
|
+
"""
|
|
752
|
+
return self._mesh_summary_outputs_gdf(
|
|
753
|
+
"mesh_cell_polygons",
|
|
754
|
+
"cells",
|
|
755
|
+
include_output=include_output,
|
|
756
|
+
round_to=round_to,
|
|
757
|
+
datetime_to_str=datetime_to_str,
|
|
758
|
+
)
|
|
759
|
+
|
|
760
|
+
def mesh_cell_faces(
|
|
761
|
+
self,
|
|
762
|
+
include_output: Union[bool, List[SummaryOutputVar], List[str]] = True,
|
|
763
|
+
round_to: str = "0.1 s",
|
|
764
|
+
datetime_to_str: bool = False,
|
|
765
|
+
) -> GeoDataFrame:
|
|
766
|
+
"""Return the cell faces for each cell in the mesh, including output.
|
|
767
|
+
|
|
768
|
+
Parameters
|
|
769
|
+
----------
|
|
770
|
+
include_output : Union[bool, List[SummaryOutputVar], List[str]], optional
|
|
771
|
+
If True, include all available summary output data in the GeoDataFrame.
|
|
772
|
+
If a list of SummaryOutputVar values, include only the specified summary output data.
|
|
773
|
+
If a list of strings, include only the specified summary output data by name.
|
|
774
|
+
If False, do not include any summary output data.
|
|
775
|
+
(default: True)
|
|
776
|
+
round_to : str, optional
|
|
777
|
+
The time unit to round the datetimes to. Default: "0.1 s" (seconds).
|
|
778
|
+
datetime_to_str : bool, optional
|
|
779
|
+
If True, convert datetime columns to strings. (default: False)
|
|
780
|
+
|
|
781
|
+
Returns
|
|
782
|
+
-------
|
|
783
|
+
GeoDataFrame
|
|
784
|
+
A GeoDataFrame with columns 'mesh_name', 'cell_id', 'geometry', and columns for each
|
|
785
|
+
summary output variable.
|
|
786
|
+
"""
|
|
787
|
+
return self._mesh_summary_outputs_gdf(
|
|
788
|
+
"mesh_cell_faces",
|
|
789
|
+
"faces",
|
|
790
|
+
include_output=include_output,
|
|
791
|
+
round_to=round_to,
|
|
792
|
+
datetime_to_str=datetime_to_str,
|
|
793
|
+
)
|
|
794
|
+
|
|
795
|
+
def unsteady_datetimes(self) -> List[datetime]:
|
|
796
|
+
"""Return the unsteady timeseries datetimes from the plan file.
|
|
797
|
+
|
|
798
|
+
Returns
|
|
799
|
+
-------
|
|
800
|
+
List[datetime]
|
|
801
|
+
A list of datetimes for the unsteady timeseries data.
|
|
802
|
+
"""
|
|
803
|
+
group_path = f"{self.UNSTEADY_TIME_SERIES_PATH}/Time Date Stamp (ms)"
|
|
804
|
+
raw_datetimes = self[group_path][:]
|
|
805
|
+
dt = [parse_ras_datetime_ms(x.decode("utf-8")) for x in raw_datetimes]
|
|
806
|
+
return dt
|
|
807
|
+
|
|
808
|
+
def _mesh_timeseries_output_values_units(
|
|
809
|
+
self,
|
|
810
|
+
mesh_name: str,
|
|
811
|
+
var: TimeSeriesOutputVar,
|
|
812
|
+
) -> Tuple[np.ndarray, str]:
|
|
813
|
+
path = f"{self.UNSTEADY_TIME_SERIES_PATH}/2D Flow Areas/{mesh_name}/{var.value}"
|
|
814
|
+
group = self.get(path)
|
|
815
|
+
try:
|
|
816
|
+
import dask.array as da
|
|
817
|
+
|
|
818
|
+
# TODO: user-specified chunks?
|
|
819
|
+
values = da.from_array(group, chunks=group.chunks)
|
|
820
|
+
except ImportError:
|
|
821
|
+
values = group[:]
|
|
822
|
+
units = group.attrs.get("Units")
|
|
823
|
+
if units is not None:
|
|
824
|
+
units = units.decode("utf-8")
|
|
825
|
+
return values, units
|
|
826
|
+
|
|
827
|
+
def mesh_timeseries_output(
|
|
828
|
+
self,
|
|
829
|
+
mesh_name: str,
|
|
830
|
+
var: Union[str, TimeSeriesOutputVar],
|
|
831
|
+
) -> xr.DataArray:
|
|
832
|
+
"""Return the time series output data for a given variable.
|
|
833
|
+
|
|
834
|
+
Parameters
|
|
835
|
+
----------
|
|
836
|
+
mesh_name : str
|
|
837
|
+
The name of the 2D flow area mesh.
|
|
838
|
+
var : TimeSeriesOutputVar
|
|
839
|
+
The time series output variable to retrieve.
|
|
840
|
+
|
|
841
|
+
Returns
|
|
842
|
+
-------
|
|
843
|
+
xr.DataArray
|
|
844
|
+
An xarray DataArray with dimensions 'time' and 'cell_id'.
|
|
845
|
+
"""
|
|
846
|
+
times = self.unsteady_datetimes()
|
|
847
|
+
mesh_names_counts = {
|
|
848
|
+
name: count for name, count in self._2d_flow_area_names_and_counts()
|
|
849
|
+
}
|
|
850
|
+
if mesh_name not in mesh_names_counts:
|
|
851
|
+
raise ValueError(f"Mesh '{mesh_name}' not found in the Plan HDF file.")
|
|
852
|
+
if isinstance(var, str):
|
|
853
|
+
var = TimeSeriesOutputVar(var)
|
|
854
|
+
values, units = self._mesh_timeseries_output_values_units(mesh_name, var)
|
|
855
|
+
if var in TIME_SERIES_OUTPUT_VARS_CELLS:
|
|
856
|
+
cell_count = mesh_names_counts[mesh_name]
|
|
857
|
+
values = values[:, :cell_count]
|
|
858
|
+
id_coord = "cell_id"
|
|
859
|
+
elif var in TIME_SERIES_OUTPUT_VARS_FACES:
|
|
860
|
+
id_coord = "face_id"
|
|
861
|
+
else:
|
|
862
|
+
raise ValueError(f"Invalid time series output variable: {var.value}")
|
|
863
|
+
da = xr.DataArray(
|
|
864
|
+
values,
|
|
865
|
+
name=var.value,
|
|
866
|
+
dims=["time", id_coord],
|
|
867
|
+
coords={
|
|
868
|
+
"time": times,
|
|
869
|
+
id_coord: range(values.shape[1]),
|
|
870
|
+
},
|
|
871
|
+
attrs={
|
|
872
|
+
"mesh_name": mesh_name,
|
|
873
|
+
"variable": var.value,
|
|
874
|
+
"units": units,
|
|
875
|
+
},
|
|
876
|
+
)
|
|
877
|
+
return da
|
|
878
|
+
|
|
879
|
+
def _mesh_timeseries_outputs(
|
|
880
|
+
self, mesh_name: str, vars: List[TimeSeriesOutputVar]
|
|
881
|
+
) -> xr.Dataset:
|
|
882
|
+
datasets = {}
|
|
883
|
+
for var in vars:
|
|
884
|
+
var_path = f"{self.UNSTEADY_TIME_SERIES_PATH}/2D Flow Areas/{mesh_name}/{var.value}"
|
|
885
|
+
if self.get(var_path) is None:
|
|
886
|
+
continue
|
|
887
|
+
da = self.mesh_timeseries_output(mesh_name, var)
|
|
888
|
+
datasets[var.value] = da
|
|
889
|
+
ds = xr.Dataset(datasets, attrs={"mesh_name": mesh_name})
|
|
890
|
+
return ds
|
|
891
|
+
|
|
892
|
+
def mesh_timeseries_output_cells(self, mesh_name: str) -> xr.Dataset:
|
|
893
|
+
"""Return the time series output data for cells in a 2D flow area mesh.
|
|
894
|
+
|
|
895
|
+
Parameters
|
|
896
|
+
----------
|
|
897
|
+
mesh_name : str
|
|
898
|
+
The name of the 2D flow area mesh.
|
|
899
|
+
|
|
900
|
+
Returns
|
|
901
|
+
-------
|
|
902
|
+
xr.Dataset
|
|
903
|
+
An xarray Dataset with DataArrays for each time series output variable.
|
|
904
|
+
"""
|
|
905
|
+
ds = self._mesh_timeseries_outputs(mesh_name, TIME_SERIES_OUTPUT_VARS_CELLS)
|
|
906
|
+
return ds
|
|
907
|
+
|
|
908
|
+
def mesh_timeseries_output_faces(self, mesh_name: str) -> xr.Dataset:
|
|
909
|
+
"""Return the time series output data for faces in a 2D flow area mesh.
|
|
910
|
+
|
|
911
|
+
Parameters
|
|
912
|
+
----------
|
|
913
|
+
mesh_name : str
|
|
914
|
+
The name of the 2D flow area mesh.
|
|
915
|
+
|
|
916
|
+
Returns
|
|
917
|
+
-------
|
|
918
|
+
xr.Dataset
|
|
919
|
+
An xarray Dataset with DataArrays for each time series output variable.
|
|
920
|
+
"""
|
|
921
|
+
ds = self._mesh_timeseries_outputs(mesh_name, TIME_SERIES_OUTPUT_VARS_FACES)
|
|
922
|
+
return ds
|
|
923
|
+
|
|
30
924
|
def get_plan_info_attrs(self) -> Dict:
|
|
31
925
|
"""Return plan information attributes from a HEC-RAS HDF plan file.
|
|
32
926
|
|
|
@@ -89,3 +983,127 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
89
983
|
|
|
90
984
|
def enroachment_points(self) -> GeoDataFrame: # noqa: D102
|
|
91
985
|
raise NotImplementedError
|
|
986
|
+
|
|
987
|
+
def steady_flow_names(self) -> list:
|
|
988
|
+
"""Return the profile information for each steady flow event.
|
|
989
|
+
|
|
990
|
+
Returns
|
|
991
|
+
-------
|
|
992
|
+
DataFrame
|
|
993
|
+
A Dataframe containing the profile names for each event
|
|
994
|
+
"""
|
|
995
|
+
if self.STEADY_PROFILES_PATH not in self:
|
|
996
|
+
return pd.DataFrame()
|
|
997
|
+
|
|
998
|
+
profile_data = self[self.STEADY_PROFILES_PATH]
|
|
999
|
+
profile_attrs = profile_data["Profile Names"][()]
|
|
1000
|
+
|
|
1001
|
+
return [x.decode("utf-8") for x in profile_attrs]
|
|
1002
|
+
|
|
1003
|
+
def steady_profile_xs_output(
|
|
1004
|
+
self, var: XsSteadyOutputVar, round_to: int = 2
|
|
1005
|
+
) -> DataFrame:
|
|
1006
|
+
"""Create a Dataframe from steady cross section results based on path.
|
|
1007
|
+
|
|
1008
|
+
Parameters
|
|
1009
|
+
----------
|
|
1010
|
+
var : XsSteadyOutputVar
|
|
1011
|
+
The name of the table in the steady results that information is to be retireved from.
|
|
1012
|
+
|
|
1013
|
+
round_to : int, optional
|
|
1014
|
+
Number of decimal places to round output data to.
|
|
1015
|
+
|
|
1016
|
+
Returns
|
|
1017
|
+
-------
|
|
1018
|
+
Dataframe with desired hdf data.
|
|
1019
|
+
"""
|
|
1020
|
+
if var in XS_STEADY_OUTPUT_ADDITIONAL:
|
|
1021
|
+
path = f"{self.STEADY_XS_ADDITIONAL_PATH}/{var.value}"
|
|
1022
|
+
else:
|
|
1023
|
+
path = f"{self.STEADY_XS_PATH}/{var.value}"
|
|
1024
|
+
if path not in self:
|
|
1025
|
+
return DataFrame()
|
|
1026
|
+
|
|
1027
|
+
profiles = self.steady_flow_names()
|
|
1028
|
+
|
|
1029
|
+
steady_data = self[path]
|
|
1030
|
+
df = DataFrame(steady_data, index=profiles)
|
|
1031
|
+
df_t = df.T.copy()
|
|
1032
|
+
for p in profiles:
|
|
1033
|
+
df_t[p] = df_t[p].apply(lambda x: round(x, round_to))
|
|
1034
|
+
|
|
1035
|
+
return df_t
|
|
1036
|
+
|
|
1037
|
+
def cross_sections_wsel(self) -> DataFrame:
|
|
1038
|
+
"""Return the water surface elevation information for each 1D Cross Section.
|
|
1039
|
+
|
|
1040
|
+
Returns
|
|
1041
|
+
-------
|
|
1042
|
+
DataFrame
|
|
1043
|
+
A Dataframe containing the water surface elevations for each cross section and event
|
|
1044
|
+
"""
|
|
1045
|
+
return self.steady_profile_xs_output(XsSteadyOutputVar.WATER_SURFACE)
|
|
1046
|
+
|
|
1047
|
+
def cross_sections_flow(self) -> DataFrame:
|
|
1048
|
+
"""Return the Flow information for each 1D Cross Section.
|
|
1049
|
+
|
|
1050
|
+
Returns
|
|
1051
|
+
-------
|
|
1052
|
+
DataFrame
|
|
1053
|
+
A Dataframe containing the flow for each cross section and event
|
|
1054
|
+
"""
|
|
1055
|
+
return self.steady_profile_xs_output(XsSteadyOutputVar.FLOW)
|
|
1056
|
+
|
|
1057
|
+
def cross_sections_energy_grade(self) -> DataFrame:
|
|
1058
|
+
"""Return the energy grade information for each 1D Cross Section.
|
|
1059
|
+
|
|
1060
|
+
Returns
|
|
1061
|
+
-------
|
|
1062
|
+
DataFrame
|
|
1063
|
+
A Dataframe containing the water surface elevations for each cross section and event
|
|
1064
|
+
"""
|
|
1065
|
+
return self.steady_profile_xs_output(XsSteadyOutputVar.ENERGY_GRADE)
|
|
1066
|
+
|
|
1067
|
+
def cross_sections_additional_enc_station_left(self) -> DataFrame:
|
|
1068
|
+
"""Return the left side encroachment information for a floodway plan hdf.
|
|
1069
|
+
|
|
1070
|
+
Returns
|
|
1071
|
+
-------
|
|
1072
|
+
DataFrame
|
|
1073
|
+
A DataFrame containing the cross sections left side encroachment stations
|
|
1074
|
+
"""
|
|
1075
|
+
return self.steady_profile_xs_output(
|
|
1076
|
+
XsSteadyOutputVar.ENCROACHMENT_STATION_LEFT
|
|
1077
|
+
)
|
|
1078
|
+
|
|
1079
|
+
def cross_sections_additional_enc_station_right(self) -> DataFrame:
|
|
1080
|
+
"""Return the right side encroachment information for a floodway plan hdf.
|
|
1081
|
+
|
|
1082
|
+
Returns
|
|
1083
|
+
-------
|
|
1084
|
+
DataFrame
|
|
1085
|
+
A DataFrame containing the cross sections right side encroachment stations
|
|
1086
|
+
"""
|
|
1087
|
+
return self.steady_profile_xs_output(
|
|
1088
|
+
XsSteadyOutputVar.ENCROACHMENT_STATION_RIGHT
|
|
1089
|
+
)
|
|
1090
|
+
|
|
1091
|
+
def cross_sections_additional_area_total(self) -> DataFrame:
|
|
1092
|
+
"""Return the 1D cross section area for each profile.
|
|
1093
|
+
|
|
1094
|
+
Returns
|
|
1095
|
+
-------
|
|
1096
|
+
DataFrame
|
|
1097
|
+
A DataFrame containing the wet area inside the cross sections
|
|
1098
|
+
"""
|
|
1099
|
+
return self.steady_profile_xs_output(XsSteadyOutputVar.AREA_INEFFECTIVE_TOTAL)
|
|
1100
|
+
|
|
1101
|
+
def cross_sections_additional_velocity_total(self) -> DataFrame:
|
|
1102
|
+
"""Return the 1D cross section velocity for each profile.
|
|
1103
|
+
|
|
1104
|
+
Returns
|
|
1105
|
+
-------
|
|
1106
|
+
DataFrame
|
|
1107
|
+
A DataFrame containing the velocity inside the cross sections
|
|
1108
|
+
"""
|
|
1109
|
+
return self.steady_profile_xs_output(XsSteadyOutputVar.VELOCITY_TOTAL)
|