ras-commander 0.45.0__py3-none-any.whl → 0.46.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.
@@ -9,7 +9,7 @@ The file has been forked and modified for use in RAS Commander.
9
9
  """
10
10
 
11
11
  from pathlib import Path
12
- from typing import Union, Optional, List
12
+ from typing import Union, Optional, List, Dict, Tuple
13
13
 
14
14
  import h5py
15
15
  import numpy as np
@@ -37,251 +37,90 @@ class HdfResultsXsec:
37
37
  Attributes:
38
38
  None
39
39
 
40
- Methods:
41
- steady_profile_xs_output: Extract steady profile cross-section output for a specified variable.
42
- cross_sections_wsel: Get water surface elevation data for cross-sections.
43
- cross_sections_flow: Get flow data for cross-sections.
44
- cross_sections_energy_grade: Get energy grade data for cross-sections.
45
- cross_sections_additional_enc_station_left: Get left encroachment station data for cross-sections.
46
- cross_sections_additional_enc_station_right: Get right encroachment station data for cross-sections.
47
- cross_sections_additional_area_total: Get total ineffective area data for cross-sections.
48
- cross_sections_additional_velocity_total: Get total velocity data for cross-sections.
40
+
49
41
  """
50
42
 
51
- @staticmethod
52
- @standardize_input(file_type='plan_hdf')
53
- def steady_profile_xs_output(hdf_path: Path, var: str, round_to: int = 2) -> pd.DataFrame:
54
- """
55
- Create a DataFrame from steady cross section results based on the specified variable.
56
43
 
57
- Parameters:
58
- ----------
59
- hdf_path : Path
60
- Path to the HEC-RAS plan HDF file.
61
- var : str
62
- The variable to extract from the steady cross section results.
63
- round_to : int, optional
64
- Number of decimal places to round the results to (default is 2).
65
44
 
66
- Returns:
67
- -------
68
- pd.DataFrame
69
- DataFrame containing the steady cross section results for the specified variable.
70
- """
71
- XS_STEADY_OUTPUT_ADDITIONAL = [
72
- "Additional Encroachment Station Left",
73
- "Additional Encroachment Station Right",
74
- "Additional Area Ineffective Total",
75
- "Additional Velocity Total",
76
- ]
77
-
78
- try:
79
- with h5py.File(hdf_path, 'r') as hdf_file:
80
- # Determine the correct path based on the variable
81
- if var in XS_STEADY_OUTPUT_ADDITIONAL:
82
- path = f"/Results/Steady/Cross Sections/Additional Output/{var}"
83
- else:
84
- path = f"/Results/Steady/Cross Sections/{var}"
85
-
86
- # Check if the path exists in the HDF file
87
- if path not in hdf_file:
88
- return pd.DataFrame()
89
45
 
90
- # Get the profile names
91
- profiles = HdfBase.steady_flow_names(hdf_path)
92
-
93
- # Extract the steady data
94
- steady_data = hdf_file[path]
95
-
96
- # Create a DataFrame with profiles as index
97
- df = pd.DataFrame(steady_data, index=profiles)
98
-
99
- # Transpose the DataFrame and round values
100
- df_t = df.T.copy()
101
- for p in profiles:
102
- df_t[p] = df_t[p].apply(lambda x: round(x, round_to))
103
46
 
104
- return df_t
105
- except Exception as e:
106
- HdfUtils.logger.error(f"Failed to get steady profile cross section output: {str(e)}")
107
- return pd.DataFrame()
108
47
 
109
- @staticmethod
110
- @standardize_input(file_type='plan_hdf')
111
- def cross_sections_wsel(hdf_path: Path) -> pd.DataFrame:
112
- """
113
- Return the water surface elevation information for each 1D Cross Section.
114
48
 
115
- Parameters:
116
- ----------
117
- hdf_path : Path
118
- Path to the HEC-RAS plan HDF file.
119
49
 
120
- Returns:
121
- -------
122
- pd.DataFrame
123
- A DataFrame containing the water surface elevations for each cross section and event.
124
- """
125
- return HdfResultsXsec.steady_profile_xs_output(hdf_path, "Water Surface")
126
50
 
127
- @staticmethod
128
- @standardize_input(file_type='plan_hdf')
129
- def cross_sections_flow(hdf_path: Path) -> pd.DataFrame:
130
- """
131
- Return the Flow information for each 1D Cross Section.
132
51
 
133
- Parameters:
134
- ----------
135
- hdf_path : Path
136
- Path to the HEC-RAS plan HDF file.
137
52
 
138
- Returns:
139
- -------
140
- pd.DataFrame
141
- A DataFrame containing the flow for each cross section and event.
142
- """
143
- return HdfResultsXsec.steady_profile_xs_output(hdf_path, "Flow")
144
53
 
145
54
  @staticmethod
55
+ @log_call
146
56
  @standardize_input(file_type='plan_hdf')
147
- def cross_sections_energy_grade(hdf_path: Path) -> pd.DataFrame:
57
+ def get_pump_station_profile_output(hdf_path: Path) -> pd.DataFrame:
148
58
  """
149
- Return the energy grade information for each 1D Cross Section.
59
+ Extract pump station profile output data from the HDF file.
150
60
 
151
- Parameters:
152
- ----------
153
- hdf_path : Path
154
- Path to the HEC-RAS plan HDF file.
61
+ Args:
62
+ hdf_path (Path): Path to the HDF file.
155
63
 
156
64
  Returns:
157
- -------
158
- pd.DataFrame
159
- A DataFrame containing the energy grade for each cross section and event.
160
- """
161
- return HdfResultsXsec.steady_profile_xs_output(hdf_path, "Energy Grade")
65
+ pd.DataFrame: DataFrame containing pump station profile output data.
162
66
 
163
- @staticmethod
164
- @standardize_input(file_type='plan_hdf')
165
- def cross_sections_additional_enc_station_left(hdf_path: Path) -> pd.DataFrame:
67
+ Raises:
68
+ KeyError: If the required datasets are not found in the HDF file.
166
69
  """
167
- Return the left side encroachment information for a floodway plan hdf.
70
+ try:
71
+ with h5py.File(hdf_path, 'r') as hdf:
72
+ # Extract profile output data
73
+ profile_path = "/Results/Unsteady/Output/Output Blocks/DSS Profile Output/Unsteady Time Series/Pumping Stations"
74
+ if profile_path not in hdf:
75
+ logger.warning("Pump Station profile output data not found in HDF file")
76
+ return pd.DataFrame()
168
77
 
169
- Parameters:
170
- ----------
171
- hdf_path : Path
172
- Path to the HEC-RAS plan HDF file.
78
+ # Initialize an empty list to store data from all pump stations
79
+ all_data = []
173
80
 
174
- Returns:
175
- -------
176
- pd.DataFrame
177
- A DataFrame containing the cross sections left side encroachment stations.
178
- """
179
- return HdfResultsXsec.steady_profile_xs_output(
180
- hdf_path, "Encroachment Station Left"
181
- )
81
+ # Iterate through all pump stations
82
+ for station in hdf[profile_path].keys():
83
+ station_path = f"{profile_path}/{station}/Structure Variables"
84
+
85
+ data = hdf[station_path][()]
86
+
87
+ # Create a DataFrame for this pump station
88
+ df = pd.DataFrame(data, columns=['Flow', 'Stage HW', 'Stage TW', 'Pump Station', 'Pumps on'])
89
+ df['Station'] = station
90
+
91
+ all_data.append(df)
182
92
 
183
- @staticmethod
184
- @standardize_input(file_type='plan_hdf')
185
- def cross_sections_additional_enc_station_right(hdf_path: Path) -> pd.DataFrame:
186
- """
187
- Return the right side encroachment information for a floodway plan hdf.
93
+ # Concatenate all DataFrames
94
+ result_df = pd.concat(all_data, ignore_index=True)
188
95
 
189
- Parameters:
190
- ----------
191
- hdf_path : Path
192
- Path to the HEC-RAS plan HDF file.
96
+ # Add time information
97
+ time = HdfBase._get_unsteady_datetimes(hdf)
98
+ result_df['Time'] = [time[i] for i in result_df.index]
193
99
 
194
- Returns:
195
- -------
196
- pd.DataFrame
197
- A DataFrame containing the cross sections right side encroachment stations.
198
- """
199
- return HdfResultsXsec.steady_profile_xs_output(
200
- hdf_path, "Encroachment Station Right"
201
- )
100
+ return result_df
101
+
102
+ except KeyError as e:
103
+ logger.error(f"Required dataset not found in HDF file: {e}")
104
+ raise
105
+ except Exception as e:
106
+ logger.error(f"Error extracting pump station profile output data: {e}")
107
+ raise
202
108
 
203
- @staticmethod
204
- @standardize_input(file_type='plan_hdf')
205
- def cross_sections_additional_area_total(hdf_path: Path) -> pd.DataFrame:
206
- """
207
- Return the 1D cross section area for each profile.
208
109
 
209
- Parameters:
210
- ----------
211
- hdf_path : Path
212
- Path to the HEC-RAS plan HDF file.
213
110
 
214
- Returns:
215
- -------
216
- pd.DataFrame
217
- A DataFrame containing the wet area inside the cross sections.
218
- """
219
- return HdfResultsXsec.steady_profile_xs_output(hdf_path, "Area Ineffective Total")
220
111
 
221
- @staticmethod
222
- @standardize_input(file_type='plan_hdf')
223
- def cross_sections_additional_velocity_total(hdf_path: Path) -> pd.DataFrame:
224
- """
225
- Return the 1D cross section velocity for each profile.
226
112
 
227
- Parameters:
228
- ----------
229
- hdf_path : Path
230
- Path to the HEC-RAS plan HDF file.
231
113
 
232
- Returns:
233
- -------
234
- pd.DataFrame
235
- A DataFrame containing the velocity inside the cross sections.
236
- """
237
- return HdfResultsXsec.steady_profile_xs_output(hdf_path, "Velocity Total")
238
114
 
239
115
 
240
- @staticmethod
241
- @log_call
242
- @standardize_input(file_type='plan_hdf')
243
- def get_pipe_network_summary(hdf_path: Path) -> pd.DataFrame:
244
- """
245
- Extract summary data for pipe networks from the HDF file.
246
116
 
247
- Args:
248
- hdf_path (Path): Path to the HDF file.
249
117
 
250
- Returns:
251
- pd.DataFrame: DataFrame containing pipe network summary data.
252
118
 
253
- Raises:
254
- KeyError: If the required datasets are not found in the HDF file.
255
- """
256
- try:
257
- with h5py.File(hdf_path, 'r') as hdf:
258
- # Extract summary data
259
- summary_path = "/Results/Unsteady/Summary/Pipe Network"
260
- if summary_path not in hdf:
261
- logger.warning("Pipe Network summary data not found in HDF file")
262
- return pd.DataFrame()
263
119
 
264
- summary_data = hdf[summary_path][()]
265
-
266
- # Create DataFrame
267
- df = pd.DataFrame(summary_data)
268
120
 
269
- # Convert column names
270
- df.columns = [col.decode('utf-8') if isinstance(col, bytes) else col for col in df.columns]
271
121
 
272
- # Convert byte string values to regular strings
273
- for col in df.columns:
274
- if df[col].dtype == object:
275
- df[col] = df[col].apply(lambda x: x.decode('utf-8') if isinstance(x, bytes) else x)
276
122
 
277
- return df
278
123
 
279
- except KeyError as e:
280
- logger.error(f"Required dataset not found in HDF file: {e}")
281
- raise
282
- except Exception as e:
283
- logger.error(f"Error extracting pipe network summary data: {e}")
284
- raise
285
124
 
286
125
  @staticmethod
287
126
  @log_call
@@ -329,115 +168,105 @@ class HdfResultsXsec:
329
168
  logger.error(f"Error extracting pump station summary data: {e}")
330
169
  raise
331
170
 
332
- @staticmethod
333
- @log_call
334
- @standardize_input(file_type='plan_hdf')
335
- def get_pipe_network_profile_output(hdf_path: Path) -> pd.DataFrame:
336
- """
337
- Extract pipe network profile output data from the HDF file.
338
-
339
- Args:
340
- hdf_path (Path): Path to the HDF file.
341
171
 
342
- Returns:
343
- pd.DataFrame: DataFrame containing pipe network profile output data.
344
172
 
345
- Raises:
346
- KeyError: If the required datasets are not found in the HDF file.
347
- """
348
- try:
349
- with h5py.File(hdf_path, 'r') as hdf:
350
- # Extract profile output data
351
- profile_path = "/Results/Unsteady/Output/Output Blocks/DSS Profile Output/Unsteady Time Series/Pipe Networks"
352
- if profile_path not in hdf:
353
- logger.warning("Pipe Network profile output data not found in HDF file")
354
- return pd.DataFrame()
355
173
 
356
- # Initialize an empty list to store data from all pipe networks
357
- all_data = []
174
+ # Tested functions from AWS webinar where the code was developed
175
+ # Need to add examples
358
176
 
359
- # Iterate through all pipe networks
360
- for network in hdf[profile_path].keys():
361
- network_path = f"{profile_path}/{network}"
362
-
363
- # Extract data for each variable
364
- for var in hdf[network_path].keys():
365
- data = hdf[f"{network_path}/{var}"][()]
366
-
367
- # Create a DataFrame for this variable
368
- df = pd.DataFrame(data)
369
- df['Network'] = network
370
- df['Variable'] = var
371
-
372
- all_data.append(df)
373
-
374
- # Concatenate all DataFrames
375
- result_df = pd.concat(all_data, ignore_index=True)
376
-
377
- # Add time information
378
- time = HdfBase._get_unsteady_datetimes(hdf)
379
- result_df['Time'] = [time[i] for i in result_df.index]
380
-
381
- return result_df
382
-
383
- except KeyError as e:
384
- logger.error(f"Required dataset not found in HDF file: {e}")
385
- raise
386
- except Exception as e:
387
- logger.error(f"Error extracting pipe network profile output data: {e}")
388
- raise
389
177
 
390
178
  @staticmethod
391
179
  @log_call
392
180
  @standardize_input(file_type='plan_hdf')
393
- def get_pump_station_profile_output(hdf_path: Path) -> pd.DataFrame:
181
+ def extract_cross_section_results(hdf_path: Path) -> xr.Dataset:
394
182
  """
395
- Extract pump station profile output data from the HDF file.
183
+ Extract Water Surface, Velocity Total, Velocity Channel, Flow Lateral, and Flow data from HEC-RAS HDF file.
184
+ Includes Cross Section Only and Cross Section Attributes as coordinates in the xarray.Dataset.
185
+ Also calculates maximum values for key parameters.
396
186
 
397
- Args:
398
- hdf_path (Path): Path to the HDF file.
187
+ Parameters:
188
+ -----------
189
+ hdf_path : Path
190
+ Path to the HEC-RAS results HDF file
399
191
 
400
192
  Returns:
401
- pd.DataFrame: DataFrame containing pump station profile output data.
402
-
403
- Raises:
404
- KeyError: If the required datasets are not found in the HDF file.
193
+ --------
194
+ xr.Dataset
195
+ Xarray Dataset containing the extracted cross-section results with appropriate coordinates and attributes.
196
+ Includes maximum values for Water Surface, Flow, Channel Velocity, Total Velocity, and Lateral Flow.
405
197
  """
406
198
  try:
407
- with h5py.File(hdf_path, 'r') as hdf:
408
- # Extract profile output data
409
- profile_path = "/Results/Unsteady/Output/Output Blocks/DSS Profile Output/Unsteady Time Series/Pumping Stations"
410
- if profile_path not in hdf:
411
- logger.warning("Pump Station profile output data not found in HDF file")
412
- return pd.DataFrame()
413
-
414
- # Initialize an empty list to store data from all pump stations
415
- all_data = []
416
-
417
- # Iterate through all pump stations
418
- for station in hdf[profile_path].keys():
419
- station_path = f"{profile_path}/{station}/Structure Variables"
420
-
421
- data = hdf[station_path][()]
422
-
423
- # Create a DataFrame for this pump station
424
- df = pd.DataFrame(data, columns=['Flow', 'Stage HW', 'Stage TW', 'Pump Station', 'Pumps on'])
425
- df['Station'] = station
426
-
427
- all_data.append(df)
428
-
429
- # Concatenate all DataFrames
430
- result_df = pd.concat(all_data, ignore_index=True)
431
-
432
- # Add time information
433
- time = HdfBase._get_unsteady_datetimes(hdf)
434
- result_df['Time'] = [time[i] for i in result_df.index]
435
-
436
- return result_df
199
+ with h5py.File(hdf_path, 'r') as hdf_file:
200
+ # Define base paths
201
+ base_output_path = "/Results/Unsteady/Output/Output Blocks/Base Output/Unsteady Time Series/Cross Sections/"
202
+ time_stamp_path = "/Results/Unsteady/Output/Output Blocks/Base Output/Unsteady Time Series/Time Date Stamp (ms)"
203
+
204
+ # Extract Cross Section Attributes
205
+ attrs_dataset = hdf_file[f"{base_output_path}Cross Section Attributes"][:]
206
+ rivers = [attr['River'].decode('utf-8').strip() for attr in attrs_dataset]
207
+ reaches = [attr['Reach'].decode('utf-8').strip() for attr in attrs_dataset]
208
+ stations = [attr['Station'].decode('utf-8').strip() for attr in attrs_dataset]
209
+ names = [attr['Name'].decode('utf-8').strip() for attr in attrs_dataset]
210
+
211
+ # Extract Cross Section Only (Unique Names)
212
+ cross_section_only_dataset = hdf_file[f"{base_output_path}Cross Section Only"][:]
213
+ cross_section_names = [cs.decode('utf-8').strip() for cs in cross_section_only_dataset]
214
+
215
+ # Extract Time Stamps and convert to datetime
216
+ time_stamps = hdf_file[time_stamp_path][:]
217
+ if any(isinstance(ts, bytes) for ts in time_stamps):
218
+ time_stamps = [ts.decode('utf-8') for ts in time_stamps]
219
+ # Convert RAS format timestamps to datetime
220
+ times = pd.to_datetime(time_stamps, format='%d%b%Y %H:%M:%S:%f')
221
+
222
+ # Extract Required Datasets
223
+ water_surface = hdf_file[f"{base_output_path}Water Surface"][:]
224
+ velocity_total = hdf_file[f"{base_output_path}Velocity Total"][:]
225
+ velocity_channel = hdf_file[f"{base_output_path}Velocity Channel"][:]
226
+ flow_lateral = hdf_file[f"{base_output_path}Flow Lateral"][:]
227
+ flow = hdf_file[f"{base_output_path}Flow"][:]
228
+
229
+ # Calculate maximum values along time axis
230
+ max_water_surface = np.max(water_surface, axis=0)
231
+ max_flow = np.max(flow, axis=0)
232
+ max_velocity_channel = np.max(velocity_channel, axis=0)
233
+ max_velocity_total = np.max(velocity_total, axis=0)
234
+ max_flow_lateral = np.max(flow_lateral, axis=0)
235
+
236
+ # Create Xarray Dataset
237
+ ds = xr.Dataset(
238
+ {
239
+ 'Water_Surface': (['time', 'cross_section'], water_surface),
240
+ 'Velocity_Total': (['time', 'cross_section'], velocity_total),
241
+ 'Velocity_Channel': (['time', 'cross_section'], velocity_channel),
242
+ 'Flow_Lateral': (['time', 'cross_section'], flow_lateral),
243
+ 'Flow': (['time', 'cross_section'], flow),
244
+ },
245
+ coords={
246
+ 'time': times,
247
+ 'cross_section': cross_section_names,
248
+ 'River': ('cross_section', rivers),
249
+ 'Reach': ('cross_section', reaches),
250
+ 'Station': ('cross_section', stations),
251
+ 'Name': ('cross_section', names),
252
+ 'Maximum_Water_Surface': ('cross_section', max_water_surface),
253
+ 'Maximum_Flow': ('cross_section', max_flow),
254
+ 'Maximum_Channel_Velocity': ('cross_section', max_velocity_channel),
255
+ 'Maximum_Velocity_Total': ('cross_section', max_velocity_total),
256
+ 'Maximum_Flow_Lateral': ('cross_section', max_flow_lateral)
257
+ },
258
+ attrs={
259
+ 'description': 'Cross-section results extracted from HEC-RAS HDF file',
260
+ 'source_file': str(hdf_path)
261
+ }
262
+ )
263
+
264
+ return ds
437
265
 
438
266
  except KeyError as e:
439
267
  logger.error(f"Required dataset not found in HDF file: {e}")
440
268
  raise
441
269
  except Exception as e:
442
- logger.error(f"Error extracting pump station profile output data: {e}")
443
- raise
270
+ logger.error(f"Error extracting cross section results: {e}")
271
+ raise
272
+