Skip to content

Commit 42a3e20

Browse files
committed
added functions for retrieving input observed data from the hdf
1 parent 6b45130 commit 42a3e20

File tree

1 file changed

+113
-0
lines changed

1 file changed

+113
-0
lines changed

src/rashdf/plan.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ class RasPlanHdf(RasGeomHdf):
156156
PLAN_INFO_PATH = "Plan Data/Plan Information"
157157
PLAN_PARAMS_PATH = "Plan Data/Plan Parameters"
158158
PRECIP_PATH = "Event Conditions/Meteorology/Precipitation"
159+
OBS_DATA_PATH = "Event Conditions/Observed Data"
159160
RESULTS_UNSTEADY_PATH = "Results/Unsteady"
160161
RESULTS_UNSTEADY_SUMMARY_PATH = f"{RESULTS_UNSTEADY_PATH}/Summary"
161162
VOLUME_ACCOUNTING_PATH = f"{RESULTS_UNSTEADY_PATH}/Volume Accounting"
@@ -166,6 +167,8 @@ class RasPlanHdf(RasGeomHdf):
166167
UNSTEADY_TIME_SERIES_PATH = f"{BASE_OUTPUT_PATH}/Unsteady Time Series"
167168
REFERENCE_LINES_OUTPUT_PATH = f"{UNSTEADY_TIME_SERIES_PATH}/Reference Lines"
168169
REFERENCE_POINTS_OUTPUT_PATH = f"{UNSTEADY_TIME_SERIES_PATH}/Reference Points"
170+
OBS_FLOW_OUTPUT_PATH = f"{OBS_DATA_PATH}/Flow"
171+
OBS_STAGE_OUTPUT_PATH = f"{OBS_DATA_PATH}/Stage"
169172

170173
RESULTS_STEADY_PATH = "Results/Steady"
171174
BASE_STEADY_PATH = f"{RESULTS_STEADY_PATH}/Output/Output Blocks/Base Output"
@@ -1117,6 +1120,106 @@ def reference_lines_timeseries_output(self) -> xr.Dataset:
11171120
"""
11181121
return self.reference_timeseries_output(reftype="lines")
11191122

1123+
def observed_timeseries_output(self, vartype: str = "Flow") -> xr.Dataset:
1124+
"""Return observed timeseries output data for reference lines and points from a HEC-RAS HDF plan file.
1125+
1126+
Parameters
1127+
----------
1128+
1129+
Returns
1130+
-------
1131+
xr.Dataset
1132+
An xarray Dataset with reference line timeseries data.
1133+
"""
1134+
1135+
# Decode the contents of the DataFrame from utf-8
1136+
def decode_bytes(val):
1137+
if isinstance(val, bytes):
1138+
return val.decode('utf-8')
1139+
return val
1140+
1141+
# Function to adjust invalid hour values
1142+
def adjust_invalid_hour(date_str):
1143+
if '24:00:00' in date_str:
1144+
# Split the date and time parts
1145+
date_part, time_part = date_str.split()
1146+
# Replace '24:00:00' with '00:00:00'
1147+
new_time_part = '00:00:00'
1148+
# Convert the date part to a datetime object and add one day
1149+
new_date_part = (pd.to_datetime(date_part, format='%d%b%Y') + pd.Timedelta(days=1)).strftime('%d%b%Y')
1150+
# Combine the new date and time parts
1151+
return f'{new_date_part} {new_time_part}'
1152+
return date_str
1153+
1154+
if vartype == "Flow":
1155+
output_path = self.OBS_FLOW_OUTPUT_PATH
1156+
elif vartype == "Stage":
1157+
output_path = self.OBS_STAGE_OUTPUT_PATH
1158+
else:
1159+
raise ValueError('vartype must be either "Flow" or "Stage".')
1160+
1161+
observed_group = self.get(output_path)
1162+
if observed_group is None:
1163+
raise RasPlanHdfError(
1164+
f"Could not find HDF group at path '{output_path}'."
1165+
f" Does the Plan HDF file contain reference {vartype[:-1]} output data?"
1166+
)
1167+
1168+
for var in observed_group.keys():
1169+
var_path = observed_group[var]
1170+
var_keys = var_path.keys()
1171+
if 'Attributes' in var_keys:
1172+
attrs_df = pd.DataFrame(var_path['Attributes'][:])
1173+
# Apply the decoding function to each element in the DataFrame
1174+
attrs_df = attrs_df.map(decode_bytes)
1175+
if var == 'Flow':
1176+
attrs_df['Units'] = 'cfs'
1177+
elif var == 'Stage':
1178+
attrs_df['Units'] = 'ft'
1179+
else:
1180+
attrs_df['Units'] = 'Unknown'
1181+
for site in var_keys:
1182+
if site != 'Attributes':
1183+
# Site Ex: 'Ref Point: Grapevine_Lake_RP'
1184+
prefix = site.split(":")[0]
1185+
suffix = site.split(":")[1][1:]
1186+
data_df = pd.DataFrame(var_path[site][:])
1187+
# Apply the decoding function to each element in the DataFrame
1188+
data_df = data_df.map(decode_bytes)
1189+
# Assign data types to the columns
1190+
data_df['Date'] = data_df['Date'].apply(adjust_invalid_hour)
1191+
data_df['Date'] = pd.to_datetime(data_df['Date'], format='%d%b%Y %H:%M:%S')
1192+
data_df['Value'] = data_df['Value'].astype(float)
1193+
# Determine the site type
1194+
site_type = 'reference_line' if 'Ref Line' in site else 'reference_point'
1195+
# Package into an xarray DataArray
1196+
da = xr.DataArray(
1197+
data_df['Value'].values,
1198+
name=suffix,
1199+
dims=["time"],
1200+
coords={
1201+
"time": data_df['Date'].values,
1202+
},
1203+
attrs={"units": attrs_df['Units'][0], "hdf_path": f"{output_path}/{var}/{site}"}
1204+
)
1205+
da_list.append(da.to_dataset(name=var))
1206+
site_list.append(suffix)
1207+
1208+
# Combine into an xarray dataset with 'site' as a dimension
1209+
ds = xr.concat(da_list, dim=pd.Index(site_list, name='site'))
1210+
1211+
return ds
1212+
1213+
def observed_data_timeseries_output(self) -> xr.Dataset:
1214+
"""Return observed data timeseries output data for reference lines or points from a HEC-RAS HDF plan file.
1215+
1216+
Returns
1217+
-------
1218+
xr.Dataset
1219+
An xarray Dataset with observed timeseries output data for reference lines or points.
1220+
"""
1221+
return self.observed_timeseries_output(vartype="Flow")
1222+
11201223
def reference_points_timeseries_output(self) -> xr.Dataset:
11211224
"""Return timeseries output data for reference points from a HEC-RAS HDF plan file.
11221225
@@ -1279,6 +1382,16 @@ def get_meteorology_precip_attrs(self) -> Dict:
12791382
"""
12801383
return self.get_attrs(self.PRECIP_PATH)
12811384

1385+
def get_obs_data_attrs(self) -> Dict:
1386+
"""Return observed data attributes from a HEC-RAS HDF plan file.
1387+
1388+
Returns
1389+
-------
1390+
dict
1391+
Dictionary of observed data attributes.
1392+
"""
1393+
return self.get_attrs(self.OBS_DATA_PATH)
1394+
12821395
def get_results_unsteady_attrs(self) -> Dict:
12831396
"""Return unsteady attributes from a HEC-RAS HDF plan file.
12841397

0 commit comments

Comments
 (0)