1
1
"""HEC-RAS Plan HDF class."""
2
2
3
3
from .geom import RasGeomHdf
4
- from .utils import df_datetimes_to_str , ras_timesteps_to_datetimes
4
+ from .utils import (
5
+ df_datetimes_to_str ,
6
+ ras_timesteps_to_datetimes ,
7
+ parse_ras_datetime_ms ,
8
+ )
5
9
6
10
from geopandas import GeoDataFrame
7
11
import h5py
8
12
import numpy as np
9
13
from pandas import DataFrame
10
14
import pandas as pd
15
+ import xarray as xr
11
16
12
17
from datetime import datetime
13
18
from enum import Enum
@@ -46,6 +51,83 @@ class SummaryOutputVar(Enum):
46
51
]
47
52
48
53
54
+ class TimeSeriesOutputVar (Enum ):
55
+ """Time series output variables."""
56
+
57
+ # Default Outputs
58
+ WATER_SURFACE = "Water Surface"
59
+ FACE_VELOCITY = "Face Velocity"
60
+
61
+ # Optional Outputs
62
+ CELL_COURANT = "Cell Courant"
63
+ CELL_CUMULATIVE_PRECIPITATION_DEPTH = "Cell Cumulative Precipitation Depth"
64
+ CELL_DIVERGENCE_TERM = "Cell Divergence Term"
65
+ CELL_EDDY_VISCOSITY_X = "Cell Eddy Viscosity - Eddy Viscosity X"
66
+ CELL_EDDY_VISCOSITY_Y = "Cell Eddy Viscosity - Eddy Viscosity Y"
67
+ CELL_FLOW_BALANCE = "Cell Flow Balance"
68
+ CELL_HYDRAULIC_DEPTH = "Cell Hydraulic Depth"
69
+ CELL_INVERT_DEPTH = "Cell Invert Depth"
70
+ CELL_STORAGE_TERM = "Cell Storage Term"
71
+ CELL_VELOCITY_X = "Cell Velocity - Velocity X"
72
+ CELL_VELOCITY_Y = "Cell Velocity - Velocity Y"
73
+ CELL_VOLUME = "Cell Volume"
74
+ CELL_VOLUME_ERROR = "Cell Volume Error"
75
+ CELL_WATER_SOURCE_TERM = "Cell Water Source Term"
76
+ CELL_WATER_SURFACE_ERROR = "Cell Water Surface Error"
77
+
78
+ FACE_COURANT = "Face Courant"
79
+ FACE_CUMULATIVE_VOLUME = "Face Cumulative Volume"
80
+ FACE_EDDY_VISCOSITY = "Face Eddy Viscosity"
81
+ FACE_FLOW = "Face Flow"
82
+ FACE_FLOW_PERIOD_AVERAGE = "Face Flow Period Average"
83
+ FACE_FRICTION_TERM = "Face Friction Term"
84
+ FACE_PRESSURE_GRADIENT_TERM = "Face Pressure Gradient Term"
85
+ FACE_SHEAR_STRESS = "Face Shear Stress"
86
+ FACE_TANGENTIAL_VELOCITY = "Face Tangential Velocity"
87
+ FACE_WATER_SURFACE = "Face Water Surface"
88
+ FACE_WIND_TERM = "Face Wind Term"
89
+
90
+
91
+ TIME_SERIES_OUTPUT_VARS_CELLS = [
92
+ TimeSeriesOutputVar .WATER_SURFACE ,
93
+ TimeSeriesOutputVar .CELL_COURANT ,
94
+ TimeSeriesOutputVar .CELL_CUMULATIVE_PRECIPITATION_DEPTH ,
95
+ TimeSeriesOutputVar .CELL_DIVERGENCE_TERM ,
96
+ TimeSeriesOutputVar .CELL_EDDY_VISCOSITY_X ,
97
+ TimeSeriesOutputVar .CELL_EDDY_VISCOSITY_Y ,
98
+ TimeSeriesOutputVar .CELL_FLOW_BALANCE ,
99
+ TimeSeriesOutputVar .CELL_HYDRAULIC_DEPTH ,
100
+ TimeSeriesOutputVar .CELL_INVERT_DEPTH ,
101
+ TimeSeriesOutputVar .CELL_STORAGE_TERM ,
102
+ TimeSeriesOutputVar .CELL_VELOCITY_X ,
103
+ TimeSeriesOutputVar .CELL_VELOCITY_Y ,
104
+ TimeSeriesOutputVar .CELL_VOLUME ,
105
+ TimeSeriesOutputVar .CELL_VOLUME_ERROR ,
106
+ TimeSeriesOutputVar .CELL_WATER_SOURCE_TERM ,
107
+ TimeSeriesOutputVar .CELL_WATER_SURFACE_ERROR ,
108
+ ]
109
+
110
+ TIME_SERIES_OUTPUT_VARS_FACES = [
111
+ TimeSeriesOutputVar .FACE_COURANT ,
112
+ TimeSeriesOutputVar .FACE_CUMULATIVE_VOLUME ,
113
+ TimeSeriesOutputVar .FACE_EDDY_VISCOSITY ,
114
+ TimeSeriesOutputVar .FACE_FLOW ,
115
+ TimeSeriesOutputVar .FACE_FLOW_PERIOD_AVERAGE ,
116
+ TimeSeriesOutputVar .FACE_FRICTION_TERM ,
117
+ TimeSeriesOutputVar .FACE_PRESSURE_GRADIENT_TERM ,
118
+ TimeSeriesOutputVar .FACE_SHEAR_STRESS ,
119
+ TimeSeriesOutputVar .FACE_TANGENTIAL_VELOCITY ,
120
+ TimeSeriesOutputVar .FACE_VELOCITY ,
121
+ TimeSeriesOutputVar .FACE_WATER_SURFACE ,
122
+ # TimeSeriesOutputVar.FACE_WIND_TERM,
123
+ ]
124
+
125
+ TIME_SERIES_OUTPUT_VARS_DEFAULT = [
126
+ TimeSeriesOutputVar .WATER_SURFACE ,
127
+ TimeSeriesOutputVar .FACE_VELOCITY ,
128
+ ]
129
+
130
+
49
131
class RasPlanHdf (RasGeomHdf ):
50
132
"""HEC-RAS Plan HDF class."""
51
133
@@ -55,7 +137,11 @@ class RasPlanHdf(RasGeomHdf):
55
137
RESULTS_UNSTEADY_PATH = "Results/Unsteady"
56
138
RESULTS_UNSTEADY_SUMMARY_PATH = f"{ RESULTS_UNSTEADY_PATH } /Summary"
57
139
VOLUME_ACCOUNTING_PATH = f"{ RESULTS_UNSTEADY_PATH } /Volume Accounting"
58
- SUMMARY_OUTPUT_2D_FLOW_AREAS_PATH = f"{ RESULTS_UNSTEADY_PATH } /Output/Output Blocks/Base Output/Summary Output/2D Flow Areas"
140
+ BASE_OUTPUT_PATH = f"{ RESULTS_UNSTEADY_PATH } /Output/Output Blocks/Base Output"
141
+ SUMMARY_OUTPUT_2D_FLOW_AREAS_PATH = (
142
+ f"{ BASE_OUTPUT_PATH } /Summary Output/2D Flow Areas"
143
+ )
144
+ UNSTEADY_TIME_SERIES_PATH = f"{ BASE_OUTPUT_PATH } /Unsteady Time Series"
59
145
60
146
def __init__ (self , name : str , ** kwargs ):
61
147
"""Open a HEC-RAS Plan HDF file.
@@ -674,6 +760,134 @@ def mesh_cell_faces(
674
760
datetime_to_str = datetime_to_str ,
675
761
)
676
762
763
+ def unsteady_datetimes (self ) -> List [datetime ]:
764
+ """Return the unsteady timeseries datetimes from the plan file.
765
+
766
+ Returns
767
+ -------
768
+ List[datetime]
769
+ A list of datetimes for the unsteady timeseries data.
770
+ """
771
+ group_path = f"{ self .UNSTEADY_TIME_SERIES_PATH } /Time Date Stamp (ms)"
772
+ raw_datetimes = self [group_path ][:]
773
+ dt = [parse_ras_datetime_ms (x .decode ("utf-8" )) for x in raw_datetimes ]
774
+ return dt
775
+
776
+ def _mesh_timeseries_output_values_units (
777
+ self ,
778
+ mesh_name : str ,
779
+ var : TimeSeriesOutputVar ,
780
+ ) -> Tuple [np .ndarray , str ]:
781
+ path = f"{ self .UNSTEADY_TIME_SERIES_PATH } /2D Flow Areas/{ mesh_name } /{ var .value } "
782
+ group = self .get (path )
783
+ try :
784
+ import dask .array as da
785
+
786
+ # TODO: user-specified chunks?
787
+ values = da .from_array (group , chunks = group .chunks )
788
+ except ImportError :
789
+ values = group [:]
790
+ units = group .attrs .get ("Units" )
791
+ if units is not None :
792
+ units = units .decode ("utf-8" )
793
+ return values , units
794
+
795
+ def mesh_timeseries_output (
796
+ self ,
797
+ mesh_name : str ,
798
+ var : Union [str , TimeSeriesOutputVar ],
799
+ ) -> xr .DataArray :
800
+ """Return the time series output data for a given variable.
801
+
802
+ Parameters
803
+ ----------
804
+ mesh_name : str
805
+ The name of the 2D flow area mesh.
806
+ var : TimeSeriesOutputVar
807
+ The time series output variable to retrieve.
808
+
809
+ Returns
810
+ -------
811
+ xr.DataArray
812
+ An xarray DataArray with dimensions 'time' and 'cell_id'.
813
+ """
814
+ times = self .unsteady_datetimes ()
815
+ mesh_names_counts = {
816
+ name : count for name , count in self ._2d_flow_area_names_and_counts ()
817
+ }
818
+ if mesh_name not in mesh_names_counts :
819
+ raise ValueError (f"Mesh '{ mesh_name } ' not found in the Plan HDF file." )
820
+ if isinstance (var , str ):
821
+ var = TimeSeriesOutputVar (var )
822
+ values , units = self ._mesh_timeseries_output_values_units (mesh_name , var )
823
+ if var in TIME_SERIES_OUTPUT_VARS_CELLS :
824
+ cell_count = mesh_names_counts [mesh_name ]
825
+ values = values [:, :cell_count ]
826
+ id_coord = "cell_id"
827
+ elif var in TIME_SERIES_OUTPUT_VARS_FACES :
828
+ id_coord = "face_id"
829
+ else :
830
+ raise ValueError (f"Invalid time series output variable: { var .value } " )
831
+ da = xr .DataArray (
832
+ values ,
833
+ dims = ["time" , id_coord ],
834
+ coords = {
835
+ "time" : times ,
836
+ id_coord : range (values .shape [1 ]),
837
+ },
838
+ attrs = {
839
+ "mesh_name" : mesh_name ,
840
+ "variable" : var .value ,
841
+ "units" : units ,
842
+ },
843
+ )
844
+ return da
845
+
846
+ def _mesh_timeseries_outputs (
847
+ self , mesh_name : str , vars : List [TimeSeriesOutputVar ]
848
+ ) -> xr .Dataset :
849
+ datasets = {}
850
+ for var in vars :
851
+ var_path = f"{ self .UNSTEADY_TIME_SERIES_PATH } /2D Flow Areas/{ mesh_name } /{ var .value } "
852
+ if self .get (var_path ) is None :
853
+ continue
854
+ da = self .mesh_timeseries_output (mesh_name , var )
855
+ datasets [var .value ] = da
856
+ ds = xr .Dataset (datasets )
857
+ return ds
858
+
859
+ def mesh_timeseries_output_cells (self , mesh_name : str ) -> xr .Dataset :
860
+ """Return the time series output data for cells in a 2D flow area mesh.
861
+
862
+ Parameters
863
+ ----------
864
+ mesh_name : str
865
+ The name of the 2D flow area mesh.
866
+
867
+ Returns
868
+ -------
869
+ xr.Dataset
870
+ An xarray Dataset with DataArrays for each time series output variable.
871
+ """
872
+ ds = self ._mesh_timeseries_outputs (mesh_name , TIME_SERIES_OUTPUT_VARS_CELLS )
873
+ return ds
874
+
875
+ def mesh_timeseries_output_faces (self , mesh_name : str ) -> xr .Dataset :
876
+ """Return the time series output data for faces in a 2D flow area mesh.
877
+
878
+ Parameters
879
+ ----------
880
+ mesh_name : str
881
+ The name of the 2D flow area mesh.
882
+
883
+ Returns
884
+ -------
885
+ xr.Dataset
886
+ An xarray Dataset with DataArrays for each time series output variable.
887
+ """
888
+ ds = self ._mesh_timeseries_outputs (mesh_name , TIME_SERIES_OUTPUT_VARS_FACES )
889
+ return ds
890
+
677
891
def get_plan_info_attrs (self ) -> Dict :
678
892
"""Return plan information attributes from a HEC-RAS HDF plan file.
679
893
0 commit comments