ras-commander 0.71.0__py3-none-any.whl → 0.73.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/HdfInfiltration.py +1530 -416
- ras_commander/HdfResultsMesh.py +245 -0
- ras_commander/RasGeo.py +538 -380
- ras_commander/RasMap.py +252 -0
- ras_commander/RasPrj.py +19 -1
- ras_commander/__init__.py +3 -2
- {ras_commander-0.71.0.dist-info → ras_commander-0.73.0.dist-info}/METADATA +22 -3
- {ras_commander-0.71.0.dist-info → ras_commander-0.73.0.dist-info}/RECORD +11 -10
- {ras_commander-0.71.0.dist-info → ras_commander-0.73.0.dist-info}/WHEEL +0 -0
- {ras_commander-0.71.0.dist-info → ras_commander-0.73.0.dist-info}/licenses/LICENSE +0 -0
- {ras_commander-0.71.0.dist-info → ras_commander-0.73.0.dist-info}/top_level.txt +0 -0
ras_commander/HdfResultsMesh.py
CHANGED
@@ -750,3 +750,248 @@ class HdfResultsMesh:
|
|
750
750
|
raise ValueError(f"Dataset not found at path '{output_path}'")
|
751
751
|
return output_item
|
752
752
|
|
753
|
+
@staticmethod
|
754
|
+
@log_call
|
755
|
+
@standardize_input(file_type='plan_hdf')
|
756
|
+
def get_boundary_conditions_timeseries(hdf_path: Path) -> xr.Dataset:
|
757
|
+
"""
|
758
|
+
Get timeseries output for all boundary conditions as a single combined xarray Dataset.
|
759
|
+
|
760
|
+
Args:
|
761
|
+
hdf_path (Path): Path to the HDF file.
|
762
|
+
|
763
|
+
Returns:
|
764
|
+
xr.Dataset: Dataset containing all boundary condition data with:
|
765
|
+
- Dimensions: time, bc_name (boundary condition name), face_id
|
766
|
+
- Variables: stage, flow, flow_per_face, stage_per_face
|
767
|
+
- Coordinates and attributes preserving original metadata
|
768
|
+
|
769
|
+
Example:
|
770
|
+
>>> bc_data = HdfResultsMesh.get_boundary_conditions_timeseries_combined(hdf_path)
|
771
|
+
>>> print(bc_data)
|
772
|
+
>>> # Plot flow for all boundary conditions
|
773
|
+
>>> bc_data.flow.plot(x='time', hue='bc_name')
|
774
|
+
>>> # Extract data for a specific boundary condition
|
775
|
+
>>> upstream_data = bc_data.sel(bc_name='Upstream Inflow')
|
776
|
+
"""
|
777
|
+
try:
|
778
|
+
with h5py.File(hdf_path, 'r') as hdf_file:
|
779
|
+
# Get the base path and check if boundary conditions exist
|
780
|
+
base_path = "Results/Unsteady/Output/Output Blocks/Base Output/Unsteady Time Series"
|
781
|
+
bc_base_path = f"{base_path}/Boundary Conditions"
|
782
|
+
|
783
|
+
if bc_base_path not in hdf_file:
|
784
|
+
logger.warning(f"No boundary conditions found in HDF file")
|
785
|
+
return xr.Dataset()
|
786
|
+
|
787
|
+
# Get timestamps
|
788
|
+
start_time = HdfBase.get_simulation_start_time(hdf_file)
|
789
|
+
time_data = hdf_file[f"{base_path}/Time"][:]
|
790
|
+
timestamps = HdfUtils.convert_timesteps_to_datetimes(time_data, start_time)
|
791
|
+
|
792
|
+
# Get all boundary condition names (excluding those with " - Flow per Face" or " - Stage per Face" suffix)
|
793
|
+
bc_names = [name for name in hdf_file[bc_base_path].keys()
|
794
|
+
if " - Flow per Face" not in name and " - Stage per Face" not in name]
|
795
|
+
|
796
|
+
if not bc_names:
|
797
|
+
logger.warning(f"No boundary conditions found in HDF file")
|
798
|
+
return xr.Dataset()
|
799
|
+
|
800
|
+
# Initialize arrays for main stage and flow data
|
801
|
+
num_timesteps = len(timestamps)
|
802
|
+
num_bcs = len(bc_names)
|
803
|
+
|
804
|
+
stage_data = np.full((num_timesteps, num_bcs), np.nan)
|
805
|
+
flow_data = np.full((num_timesteps, num_bcs), np.nan)
|
806
|
+
|
807
|
+
# Dictionary to store face-specific data
|
808
|
+
face_data = {
|
809
|
+
'flow_per_face': {},
|
810
|
+
'stage_per_face': {}
|
811
|
+
}
|
812
|
+
|
813
|
+
# Extract metadata from all boundary conditions
|
814
|
+
bc_metadata = {}
|
815
|
+
|
816
|
+
# Process each boundary condition
|
817
|
+
for bc_idx, bc_name in enumerate(bc_names):
|
818
|
+
bc_path = f"{bc_base_path}/{bc_name}"
|
819
|
+
|
820
|
+
try:
|
821
|
+
# Extract main boundary data
|
822
|
+
bc_data = hdf_file[bc_path][:]
|
823
|
+
bc_attrs = dict(hdf_file[bc_path].attrs)
|
824
|
+
|
825
|
+
# Store metadata
|
826
|
+
bc_metadata[bc_name] = {
|
827
|
+
k: v.decode('utf-8') if isinstance(v, bytes) else v
|
828
|
+
for k, v in bc_attrs.items()
|
829
|
+
}
|
830
|
+
|
831
|
+
# Get column indices for Stage and Flow
|
832
|
+
if 'Columns' in bc_attrs:
|
833
|
+
columns = [col.decode('utf-8') if isinstance(col, bytes) else col
|
834
|
+
for col in bc_attrs['Columns']]
|
835
|
+
|
836
|
+
stage_idx = columns.index('Stage') if 'Stage' in columns else None
|
837
|
+
flow_idx = columns.index('Flow') if 'Flow' in columns else None
|
838
|
+
|
839
|
+
if stage_idx is not None:
|
840
|
+
stage_data[:, bc_idx] = bc_data[:, stage_idx]
|
841
|
+
if flow_idx is not None:
|
842
|
+
flow_data[:, bc_idx] = bc_data[:, flow_idx]
|
843
|
+
|
844
|
+
# Extract Flow per Face data
|
845
|
+
flow_face_path = f"{bc_path} - Flow per Face"
|
846
|
+
if flow_face_path in hdf_file:
|
847
|
+
flow_face_data = hdf_file[flow_face_path][:]
|
848
|
+
flow_face_attrs = dict(hdf_file[flow_face_path].attrs)
|
849
|
+
|
850
|
+
# Get face IDs
|
851
|
+
face_ids = flow_face_attrs.get('Faces', [])
|
852
|
+
if isinstance(face_ids, np.ndarray):
|
853
|
+
face_ids = face_ids.tolist()
|
854
|
+
else:
|
855
|
+
face_ids = list(range(flow_face_data.shape[1]))
|
856
|
+
|
857
|
+
face_data['flow_per_face'][bc_name] = {
|
858
|
+
'data': flow_face_data,
|
859
|
+
'faces': face_ids,
|
860
|
+
'attrs': {
|
861
|
+
k: v.decode('utf-8') if isinstance(v, bytes) else v
|
862
|
+
for k, v in flow_face_attrs.items()
|
863
|
+
}
|
864
|
+
}
|
865
|
+
|
866
|
+
# Extract Stage per Face data
|
867
|
+
stage_face_path = f"{bc_path} - Stage per Face"
|
868
|
+
if stage_face_path in hdf_file:
|
869
|
+
stage_face_data = hdf_file[stage_face_path][:]
|
870
|
+
stage_face_attrs = dict(hdf_file[stage_face_path].attrs)
|
871
|
+
|
872
|
+
# Get face IDs
|
873
|
+
face_ids = stage_face_attrs.get('Faces', [])
|
874
|
+
if isinstance(face_ids, np.ndarray):
|
875
|
+
face_ids = face_ids.tolist()
|
876
|
+
else:
|
877
|
+
face_ids = list(range(stage_face_data.shape[1]))
|
878
|
+
|
879
|
+
face_data['stage_per_face'][bc_name] = {
|
880
|
+
'data': stage_face_data,
|
881
|
+
'faces': face_ids,
|
882
|
+
'attrs': {
|
883
|
+
k: v.decode('utf-8') if isinstance(v, bytes) else v
|
884
|
+
for k, v in stage_face_attrs.items()
|
885
|
+
}
|
886
|
+
}
|
887
|
+
|
888
|
+
except Exception as e:
|
889
|
+
logger.warning(f"Error processing boundary condition '{bc_name}': {str(e)}")
|
890
|
+
continue
|
891
|
+
|
892
|
+
# Create base dataset with stage and flow data
|
893
|
+
ds = xr.Dataset(
|
894
|
+
data_vars={
|
895
|
+
'stage': xr.DataArray(
|
896
|
+
stage_data,
|
897
|
+
dims=['time', 'bc_name'],
|
898
|
+
coords={
|
899
|
+
'time': timestamps,
|
900
|
+
'bc_name': bc_names
|
901
|
+
},
|
902
|
+
attrs={'description': 'Water surface elevation at boundary condition'}
|
903
|
+
),
|
904
|
+
'flow': xr.DataArray(
|
905
|
+
flow_data,
|
906
|
+
dims=['time', 'bc_name'],
|
907
|
+
coords={
|
908
|
+
'time': timestamps,
|
909
|
+
'bc_name': bc_names
|
910
|
+
},
|
911
|
+
attrs={'description': 'Flow at boundary condition'}
|
912
|
+
)
|
913
|
+
},
|
914
|
+
attrs={
|
915
|
+
'source': 'HEC-RAS HDF Boundary Conditions',
|
916
|
+
'start_time': start_time
|
917
|
+
}
|
918
|
+
)
|
919
|
+
|
920
|
+
# Add metadata as coordinates
|
921
|
+
for key in bc_metadata[bc_names[0]]:
|
922
|
+
if key != 'Columns': # Skip Columns attribute as it's used for Stage/Flow
|
923
|
+
try:
|
924
|
+
values = [bc_metadata[bc].get(key, '') for bc in bc_names]
|
925
|
+
ds = ds.assign_coords({f'{key.lower()}': ('bc_name', values)})
|
926
|
+
except Exception as e:
|
927
|
+
logger.debug(f"Could not add metadata coordinate '{key}': {str(e)}")
|
928
|
+
|
929
|
+
# Add face-specific data variables if available
|
930
|
+
if face_data['flow_per_face']:
|
931
|
+
# First determine the maximum number of faces across all BCs
|
932
|
+
all_flow_faces = set()
|
933
|
+
for bc_name in face_data['flow_per_face']:
|
934
|
+
all_flow_faces.update(face_data['flow_per_face'][bc_name]['faces'])
|
935
|
+
|
936
|
+
# Create a merged array with NaN values for missing faces
|
937
|
+
all_flow_faces = sorted(list(all_flow_faces))
|
938
|
+
flow_face_data = np.full((num_timesteps, num_bcs, len(all_flow_faces)), np.nan)
|
939
|
+
|
940
|
+
# Fill in the data where available
|
941
|
+
for bc_idx, bc_name in enumerate(bc_names):
|
942
|
+
if bc_name in face_data['flow_per_face']:
|
943
|
+
bc_faces = face_data['flow_per_face'][bc_name]['faces']
|
944
|
+
bc_data = face_data['flow_per_face'][bc_name]['data']
|
945
|
+
|
946
|
+
for face_idx, face_id in enumerate(bc_faces):
|
947
|
+
if face_id in all_flow_faces:
|
948
|
+
target_idx = all_flow_faces.index(face_id)
|
949
|
+
flow_face_data[:, bc_idx, target_idx] = bc_data[:, face_idx]
|
950
|
+
|
951
|
+
# Add to the dataset
|
952
|
+
ds['flow_per_face'] = xr.DataArray(
|
953
|
+
flow_face_data,
|
954
|
+
dims=['time', 'bc_name', 'face_id'],
|
955
|
+
coords={
|
956
|
+
'time': timestamps,
|
957
|
+
'bc_name': bc_names,
|
958
|
+
'face_id': all_flow_faces
|
959
|
+
},
|
960
|
+
attrs={'description': 'Flow per face at boundary condition'}
|
961
|
+
)
|
962
|
+
|
963
|
+
# Similar approach for stage per face
|
964
|
+
if face_data['stage_per_face']:
|
965
|
+
all_stage_faces = set()
|
966
|
+
for bc_name in face_data['stage_per_face']:
|
967
|
+
all_stage_faces.update(face_data['stage_per_face'][bc_name]['faces'])
|
968
|
+
|
969
|
+
all_stage_faces = sorted(list(all_stage_faces))
|
970
|
+
stage_face_data = np.full((num_timesteps, num_bcs, len(all_stage_faces)), np.nan)
|
971
|
+
|
972
|
+
for bc_idx, bc_name in enumerate(bc_names):
|
973
|
+
if bc_name in face_data['stage_per_face']:
|
974
|
+
bc_faces = face_data['stage_per_face'][bc_name]['faces']
|
975
|
+
bc_data = face_data['stage_per_face'][bc_name]['data']
|
976
|
+
|
977
|
+
for face_idx, face_id in enumerate(bc_faces):
|
978
|
+
if face_id in all_stage_faces:
|
979
|
+
target_idx = all_stage_faces.index(face_id)
|
980
|
+
stage_face_data[:, bc_idx, target_idx] = bc_data[:, face_idx]
|
981
|
+
|
982
|
+
ds['stage_per_face'] = xr.DataArray(
|
983
|
+
stage_face_data,
|
984
|
+
dims=['time', 'bc_name', 'face_id'],
|
985
|
+
coords={
|
986
|
+
'time': timestamps,
|
987
|
+
'bc_name': bc_names,
|
988
|
+
'face_id': all_stage_faces
|
989
|
+
},
|
990
|
+
attrs={'description': 'Water surface elevation per face at boundary condition'}
|
991
|
+
)
|
992
|
+
|
993
|
+
return ds
|
994
|
+
|
995
|
+
except Exception as e:
|
996
|
+
logger.error(f"Error getting all boundary conditions timeseries: {str(e)}")
|
997
|
+
return xr.Dataset()
|