ras-commander 0.44.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.
- ras_commander/HdfFluvialPluvial.py +317 -0
- ras_commander/HdfMesh.py +62 -15
- ras_commander/HdfPipe.py +771 -0
- ras_commander/HdfPlan.py +5 -0
- ras_commander/HdfPump.py +269 -0
- ras_commander/HdfResultsMesh.py +135 -62
- ras_commander/HdfResultsPlan.py +3 -0
- ras_commander/HdfResultsXsec.py +192 -157
- ras_commander/HdfStruc.py +148 -50
- ras_commander/HdfUtils.py +51 -0
- ras_commander/HdfXsec.py +467 -136
- ras_commander/RasPlan.py +298 -45
- ras_commander/RasToGo.py +21 -0
- ras_commander/RasUnsteady.py +615 -14
- ras_commander/__init__.py +7 -1
- {ras_commander-0.44.0.dist-info → ras_commander-0.46.0.dist-info}/METADATA +1 -1
- ras_commander-0.46.0.dist-info/RECORD +30 -0
- {ras_commander-0.44.0.dist-info → ras_commander-0.46.0.dist-info}/WHEEL +1 -1
- ras_commander-0.44.0.dist-info/RECORD +0 -26
- {ras_commander-0.44.0.dist-info → ras_commander-0.46.0.dist-info}/LICENSE +0 -0
- {ras_commander-0.44.0.dist-info → ras_commander-0.46.0.dist-info}/top_level.txt +0 -0
ras_commander/RasPlan.py
CHANGED
@@ -34,6 +34,8 @@ from .RasPrj import RasPrj, ras
|
|
34
34
|
from .RasUtils import RasUtils
|
35
35
|
from pathlib import Path
|
36
36
|
from typing import Union, Any
|
37
|
+
from datetime import datetime
|
38
|
+
|
37
39
|
import logging
|
38
40
|
import re
|
39
41
|
from .LoggingConfig import get_logger
|
@@ -216,6 +218,8 @@ class RasPlan:
|
|
216
218
|
@staticmethod
|
217
219
|
def _update_unsteady_in_file(lines, new_unsteady_flow_number):
|
218
220
|
return [f"Unsteady File=u{new_unsteady_flow_number}\n" if line.startswith("Unsteady File=u") else line for line in lines]
|
221
|
+
|
222
|
+
|
219
223
|
@staticmethod
|
220
224
|
@log_call
|
221
225
|
def set_num_cores(plan_number, num_cores, ras_object=None):
|
@@ -870,47 +874,174 @@ class RasPlan:
|
|
870
874
|
logger.error(f"Key '{key}' not found in the plan file.")
|
871
875
|
return None
|
872
876
|
|
877
|
+
|
878
|
+
# NEW FUNCTIONS THAT NEED TESTING AND EXAMPLES
|
879
|
+
|
880
|
+
|
873
881
|
@staticmethod
|
874
882
|
@log_call
|
875
|
-
def
|
883
|
+
def update_run_flags(
|
876
884
|
plan_number_or_path: Union[str, Path],
|
877
|
-
|
878
|
-
|
885
|
+
geometry_preprocessor: bool = None,
|
886
|
+
unsteady_flow_simulation: bool = None,
|
887
|
+
run_sediment: bool = None,
|
888
|
+
post_processor: bool = None,
|
889
|
+
floodplain_mapping: bool = None,
|
879
890
|
ras_object=None
|
880
891
|
) -> None:
|
881
892
|
"""
|
882
|
-
Update
|
893
|
+
Update the run flags in a HEC-RAS plan file.
|
883
894
|
|
884
895
|
Parameters:
|
885
896
|
plan_number_or_path (Union[str, Path]): The plan number (1 to 99) or full path to the plan file
|
886
|
-
|
887
|
-
|
897
|
+
geometry_preprocessor (bool, optional): Flag for Geometry Preprocessor
|
898
|
+
unsteady_flow_simulation (bool, optional): Flag for Unsteady Flow Simulation
|
899
|
+
run_sediment (bool, optional): Flag for run_sediment
|
900
|
+
post_processor (bool, optional): Flag for Post Processor
|
901
|
+
floodplain_mapping (bool, optional): Flag for Floodplain Mapping
|
888
902
|
ras_object (RasPrj, optional): Specific RAS object to use. If None, uses the global ras instance.
|
889
903
|
|
890
904
|
Raises:
|
891
905
|
ValueError: If the plan file is not found
|
892
906
|
IOError: If there's an error reading or writing the plan file
|
893
907
|
|
894
|
-
|
908
|
+
Example:
|
909
|
+
>>> RasPlan.update_run_flags("01", geometry_preprocessor=True, unsteady_flow_simulation=True, run_sediment=False, post_processor=True, floodplain_mapping=False)
|
910
|
+
"""
|
911
|
+
ras_obj = ras_object or ras
|
912
|
+
ras_obj.check_initialized()
|
913
|
+
|
914
|
+
plan_file_path = Path(plan_number_or_path)
|
915
|
+
if not plan_file_path.is_file():
|
916
|
+
plan_file_path = RasPlan.get_plan_path(plan_number_or_path, ras_object=ras_obj)
|
917
|
+
if plan_file_path is None or not Path(plan_file_path).exists():
|
918
|
+
raise ValueError(f"Plan file not found: {plan_file_path}")
|
919
|
+
|
920
|
+
flag_mapping = {
|
921
|
+
'geometry_preprocessor': ('Run HTab', geometry_preprocessor),
|
922
|
+
'unsteady_flow_simulation': ('Run UNet', unsteady_flow_simulation),
|
923
|
+
'run_sediment': ('Run run_sediment', run_sediment),
|
924
|
+
'post_processor': ('Run PostProcess', post_processor),
|
925
|
+
'floodplain_mapping': ('Run RASMapper', floodplain_mapping)
|
926
|
+
}
|
927
|
+
|
928
|
+
try:
|
929
|
+
with open(plan_file_path, 'r') as file:
|
930
|
+
lines = file.readlines()
|
931
|
+
|
932
|
+
for i, line in enumerate(lines):
|
933
|
+
for key, (file_key, value) in flag_mapping.items():
|
934
|
+
if value is not None and line.strip().startswith(file_key):
|
935
|
+
lines[i] = f"{file_key}= {1 if value else 0}\n"
|
936
|
+
|
937
|
+
with open(plan_file_path, 'w') as file:
|
938
|
+
file.writelines(lines)
|
939
|
+
|
940
|
+
logger = logging.getLogger(__name__)
|
941
|
+
logger.info(f"Successfully updated run flags in plan file: {plan_file_path}")
|
942
|
+
|
943
|
+
except IOError as e:
|
944
|
+
logger = logging.getLogger(__name__)
|
945
|
+
logger.error(f"Error updating run flags in plan file {plan_file_path}: {e}")
|
946
|
+
raise
|
947
|
+
|
948
|
+
|
949
|
+
|
950
|
+
@staticmethod
|
951
|
+
@log_call
|
952
|
+
def update_plan_intervals(
|
953
|
+
plan_number_or_path: Union[str, Path],
|
954
|
+
computation_interval: Optional[str] = None,
|
955
|
+
output_interval: Optional[str] = None,
|
956
|
+
instantaneous_interval: Optional[str] = None,
|
957
|
+
mapping_interval: Optional[str] = None,
|
958
|
+
ras_object=None
|
959
|
+
) -> None:
|
960
|
+
"""
|
961
|
+
Update the computation and output intervals in a HEC-RAS plan file.
|
962
|
+
|
963
|
+
Parameters:
|
964
|
+
plan_number_or_path (Union[str, Path]): The plan number (1 to 99) or full path to the plan file
|
965
|
+
computation_interval (Optional[str]): The new computation interval. Valid entries include:
|
966
|
+
'1SEC', '2SEC', '3SEC', '4SEC', '5SEC', '6SEC', '10SEC', '15SEC', '20SEC', '30SEC',
|
967
|
+
'1MIN', '2MIN', '3MIN', '4MIN', '5MIN', '6MIN', '10MIN', '15MIN', '20MIN', '30MIN',
|
968
|
+
'1HOUR', '2HOUR', '3HOUR', '4HOUR', '6HOUR', '8HOUR', '12HOUR', '1DAY'
|
969
|
+
output_interval (Optional[str]): The new output interval. Valid entries are the same as computation_interval.
|
970
|
+
instantaneous_interval (Optional[str]): The new instantaneous interval. Valid entries are the same as computation_interval.
|
971
|
+
mapping_interval (Optional[str]): The new mapping interval. Valid entries are the same as computation_interval.
|
972
|
+
ras_object (RasPrj, optional): Specific RAS object to use. If None, uses the global ras instance.
|
973
|
+
|
974
|
+
Raises:
|
975
|
+
ValueError: If the plan file is not found or if an invalid interval is provided
|
976
|
+
IOError: If there's an error reading or writing the plan file
|
977
|
+
|
978
|
+
Note: This function does not check if the intervals are equal divisors. Ensure you use valid values from HEC-RAS.
|
895
979
|
|
896
980
|
Example:
|
897
|
-
>>> RasPlan.
|
898
|
-
>>> RasPlan.
|
899
|
-
>>> RasPlan.update_plan_value("01", "run_rasmapper", 0) # Turn on Floodplain Mapping
|
981
|
+
>>> RasPlan.update_plan_intervals("01", computation_interval="5SEC", output_interval="1MIN", instantaneous_interval="1HOUR", mapping_interval="5MIN")
|
982
|
+
>>> RasPlan.update_plan_intervals("/path/to/plan.p01", computation_interval="10SEC", output_interval="30SEC")
|
900
983
|
"""
|
901
984
|
ras_obj = ras_object or ras
|
902
985
|
ras_obj.check_initialized()
|
903
986
|
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
987
|
+
plan_file_path = Path(plan_number_or_path)
|
988
|
+
if not plan_file_path.is_file():
|
989
|
+
plan_file_path = RasPlan.get_plan_path(plan_number_or_path, ras_object=ras_obj)
|
990
|
+
if plan_file_path is None or not Path(plan_file_path).exists():
|
991
|
+
raise ValueError(f"Plan file not found: {plan_file_path}")
|
992
|
+
|
993
|
+
valid_intervals = [
|
994
|
+
'1SEC', '2SEC', '3SEC', '4SEC', '5SEC', '6SEC', '10SEC', '15SEC', '20SEC', '30SEC',
|
995
|
+
'1MIN', '2MIN', '3MIN', '4MIN', '5MIN', '6MIN', '10MIN', '15MIN', '20MIN', '30MIN',
|
996
|
+
'1HOUR', '2HOUR', '3HOUR', '4HOUR', '6HOUR', '8HOUR', '12HOUR', '1DAY'
|
997
|
+
]
|
998
|
+
|
999
|
+
interval_mapping = {
|
1000
|
+
'Computation Interval': computation_interval,
|
1001
|
+
'Output Interval': output_interval,
|
1002
|
+
'Instantaneous Interval': instantaneous_interval,
|
1003
|
+
'Mapping Interval': mapping_interval
|
910
1004
|
}
|
1005
|
+
|
1006
|
+
try:
|
1007
|
+
with open(plan_file_path, 'r') as file:
|
1008
|
+
lines = file.readlines()
|
1009
|
+
|
1010
|
+
for i, line in enumerate(lines):
|
1011
|
+
for key, value in interval_mapping.items():
|
1012
|
+
if value is not None:
|
1013
|
+
if value.upper() not in valid_intervals:
|
1014
|
+
raise ValueError(f"Invalid {key}: {value}. Must be one of {valid_intervals}")
|
1015
|
+
if line.strip().startswith(key):
|
1016
|
+
lines[i] = f"{key}={value.upper()}\n"
|
1017
|
+
|
1018
|
+
with open(plan_file_path, 'w') as file:
|
1019
|
+
file.writelines(lines)
|
1020
|
+
|
1021
|
+
logger = logging.getLogger(__name__)
|
1022
|
+
logger.info(f"Successfully updated intervals in plan file: {plan_file_path}")
|
1023
|
+
|
1024
|
+
except IOError as e:
|
1025
|
+
logger = logging.getLogger(__name__)
|
1026
|
+
logger.error(f"Error updating intervals in plan file {plan_file_path}: {e}")
|
1027
|
+
raise
|
1028
|
+
|
1029
|
+
|
1030
|
+
@log_call
|
1031
|
+
def update_plan_description(plan_number_or_path: Union[str, Path], description: str, ras_object: Optional['RasPrj'] = None) -> None:
|
1032
|
+
"""
|
1033
|
+
Update the description in the plan file.
|
1034
|
+
|
1035
|
+
Args:
|
1036
|
+
plan_number_or_path (Union[str, Path]): The plan number or path to the plan file.
|
1037
|
+
description (str): The new description to be written to the plan file.
|
1038
|
+
ras_object (Optional[RasPrj]): The RAS project object. If None, uses the global 'ras' object.
|
1039
|
+
|
1040
|
+
Raises:
|
1041
|
+
ValueError: If the plan file is not found.
|
1042
|
+
IOError: If there's an error reading from or writing to the plan file.
|
1043
|
+
"""
|
911
1044
|
logger = logging.getLogger(__name__)
|
912
|
-
if key not in supported_plan_keys:
|
913
|
-
logger.warning(f"Unknown key: {key}. Valid keys are: {', '.join(supported_plan_keys)}")
|
914
1045
|
|
915
1046
|
plan_file_path = Path(plan_number_or_path)
|
916
1047
|
if not plan_file_path.is_file():
|
@@ -925,43 +1056,165 @@ class RasPlan:
|
|
925
1056
|
logger.error(f"Error reading plan file {plan_file_path}: {e}")
|
926
1057
|
raise
|
927
1058
|
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
1059
|
+
start_index = None
|
1060
|
+
end_index = None
|
1061
|
+
comp_interval_index = None
|
1062
|
+
for i, line in enumerate(lines):
|
1063
|
+
if line.strip() == "BEGIN DESCRIPTION:":
|
1064
|
+
start_index = i
|
1065
|
+
elif line.strip() == "END DESCRIPTION:":
|
1066
|
+
end_index = i
|
1067
|
+
elif line.strip().startswith("Computation Interval="):
|
1068
|
+
comp_interval_index = i
|
1069
|
+
|
1070
|
+
if start_index is not None and end_index is not None:
|
1071
|
+
# Description exists, update it
|
1072
|
+
new_lines = lines[:start_index + 1]
|
1073
|
+
if description:
|
1074
|
+
new_lines.extend(description.split('\n'))
|
940
1075
|
else:
|
941
|
-
|
1076
|
+
new_lines.append('\n')
|
1077
|
+
new_lines.extend(lines[end_index:])
|
942
1078
|
else:
|
943
|
-
#
|
944
|
-
|
1079
|
+
# Description doesn't exist, insert before Computation Interval
|
1080
|
+
if comp_interval_index is None:
|
1081
|
+
logger.warning("Neither description tags nor Computation Interval found in plan file. Appending to end of file.")
|
1082
|
+
comp_interval_index = len(lines)
|
1083
|
+
|
1084
|
+
new_lines = lines[:comp_interval_index]
|
1085
|
+
new_lines.append("BEGIN DESCRIPTION:\n")
|
1086
|
+
if description:
|
1087
|
+
new_lines.extend(f"{line}\n" for line in description.split('\n'))
|
1088
|
+
else:
|
1089
|
+
new_lines.append('\n')
|
1090
|
+
new_lines.append("END DESCRIPTION:\n")
|
1091
|
+
new_lines.extend(lines[comp_interval_index:])
|
1092
|
+
|
1093
|
+
try:
|
1094
|
+
with open(plan_file_path, 'w') as file:
|
1095
|
+
file.writelines(new_lines)
|
1096
|
+
logger.info(f"Updated description in plan file: {plan_file_path}")
|
1097
|
+
except IOError as e:
|
1098
|
+
logger.error(f"Error writing to plan file {plan_file_path}: {e}")
|
1099
|
+
raise
|
1100
|
+
|
1101
|
+
# Refresh RasPrj dataframes
|
1102
|
+
if ras_object:
|
1103
|
+
ras_object.plan_df = ras_object.get_plan_entries()
|
1104
|
+
ras_object.geom_df = ras_object.get_geom_entries()
|
1105
|
+
ras_object.flow_df = ras_object.get_flow_entries()
|
1106
|
+
ras_object.unsteady_df = ras_object.get_unsteady_entries()
|
1107
|
+
|
1108
|
+
@staticmethod
|
1109
|
+
@log_call
|
1110
|
+
def read_plan_description(plan_number_or_path: Union[str, Path], ras_object: Optional['RasPrj'] = None) -> str:
|
1111
|
+
"""
|
1112
|
+
Read the description from the plan file.
|
1113
|
+
|
1114
|
+
Args:
|
1115
|
+
plan_number_or_path (Union[str, Path]): The plan number or path to the plan file.
|
1116
|
+
ras_object (Optional[RasPrj]): The RAS project object. If None, uses the global 'ras' object.
|
1117
|
+
|
1118
|
+
Returns:
|
1119
|
+
str: The description from the plan file.
|
1120
|
+
|
1121
|
+
Raises:
|
1122
|
+
ValueError: If the plan file is not found.
|
1123
|
+
IOError: If there's an error reading from the plan file.
|
1124
|
+
"""
|
1125
|
+
logger = logging.getLogger(__name__)
|
1126
|
+
|
1127
|
+
plan_file_path = Path(plan_number_or_path)
|
1128
|
+
if not plan_file_path.is_file():
|
1129
|
+
plan_file_path = RasPlan.get_plan_path(plan_number_or_path, ras_object)
|
1130
|
+
if plan_file_path is None or not Path(plan_file_path).exists():
|
1131
|
+
raise ValueError(f"Plan file not found: {plan_file_path}")
|
1132
|
+
|
1133
|
+
try:
|
1134
|
+
with open(plan_file_path, 'r') as file:
|
1135
|
+
lines = file.readlines()
|
1136
|
+
except IOError as e:
|
1137
|
+
logger.error(f"Error reading plan file {plan_file_path}: {e}")
|
1138
|
+
raise
|
1139
|
+
|
1140
|
+
description_lines = []
|
1141
|
+
in_description = False
|
1142
|
+
description_found = False
|
1143
|
+
for line in lines:
|
1144
|
+
if line.strip() == "BEGIN DESCRIPTION:":
|
1145
|
+
in_description = True
|
1146
|
+
description_found = True
|
1147
|
+
elif line.strip() == "END DESCRIPTION:":
|
1148
|
+
break
|
1149
|
+
elif in_description:
|
1150
|
+
description_lines.append(line.strip())
|
1151
|
+
|
1152
|
+
if not description_found:
|
1153
|
+
logger.warning(f"No description found in plan file: {plan_file_path}")
|
1154
|
+
return ""
|
1155
|
+
|
1156
|
+
description = '\n'.join(description_lines)
|
1157
|
+
logger.info(f"Read description from plan file: {plan_file_path}")
|
1158
|
+
return description
|
1159
|
+
|
1160
|
+
|
1161
|
+
|
1162
|
+
|
1163
|
+
@staticmethod
|
1164
|
+
@log_call
|
1165
|
+
def update_simulation_date(plan_number_or_path: Union[str, Path], start_date: datetime, end_date: datetime, ras_object: Optional['RasPrj'] = None) -> None:
|
1166
|
+
"""
|
1167
|
+
Update the simulation date for a given plan.
|
1168
|
+
|
1169
|
+
Args:
|
1170
|
+
plan_number_or_path (Union[str, Path]): The plan number or path to the plan file.
|
1171
|
+
start_date (datetime): The start date and time for the simulation.
|
1172
|
+
end_date (datetime): The end date and time for the simulation.
|
1173
|
+
ras_object (Optional['RasPrj']): The RAS project object. Defaults to None.
|
1174
|
+
|
1175
|
+
Raises:
|
1176
|
+
ValueError: If the plan file is not found or if there's an error updating the file.
|
1177
|
+
"""
|
1178
|
+
|
1179
|
+
# Get the plan file path
|
1180
|
+
plan_file_path = Path(plan_number_or_path)
|
1181
|
+
if not plan_file_path.is_file():
|
1182
|
+
plan_file_path = RasPlan.get_plan_path(plan_number_or_path, ras_object)
|
1183
|
+
if plan_file_path is None or not Path(plan_file_path).exists():
|
1184
|
+
raise ValueError(f"Plan file not found: {plan_file_path}")
|
1185
|
+
|
1186
|
+
# Format the dates
|
1187
|
+
formatted_date = f"{start_date.strftime('%d%b%Y').upper()},{start_date.strftime('%H%M')},{end_date.strftime('%d%b%Y').upper()},{end_date.strftime('%H%M')}"
|
1188
|
+
|
1189
|
+
try:
|
1190
|
+
# Read the file
|
1191
|
+
with open(plan_file_path, 'r') as file:
|
1192
|
+
lines = file.readlines()
|
1193
|
+
|
1194
|
+
# Update the Simulation Date line
|
945
1195
|
updated = False
|
946
1196
|
for i, line in enumerate(lines):
|
947
|
-
if line.startswith(
|
948
|
-
lines[i] = f"{
|
1197
|
+
if line.startswith("Simulation Date="):
|
1198
|
+
lines[i] = f"Simulation Date={formatted_date}\n"
|
949
1199
|
updated = True
|
950
1200
|
break
|
1201
|
+
|
1202
|
+
# If Simulation Date line not found, add it at the end
|
951
1203
|
if not updated:
|
952
|
-
|
953
|
-
return
|
1204
|
+
lines.append(f"Simulation Date={formatted_date}\n")
|
954
1205
|
|
955
|
-
|
1206
|
+
# Write the updated content back to the file
|
956
1207
|
with open(plan_file_path, 'w') as file:
|
957
1208
|
file.writelines(lines)
|
958
|
-
|
1209
|
+
|
1210
|
+
logger.info(f"Updated simulation date in plan file: {plan_file_path}")
|
1211
|
+
|
959
1212
|
except IOError as e:
|
960
|
-
logger.error(f"Error
|
961
|
-
raise
|
1213
|
+
logger.error(f"Error updating simulation date in plan file {plan_file_path}: {e}")
|
1214
|
+
raise ValueError(f"Error updating simulation date: {e}")
|
962
1215
|
|
963
1216
|
# Refresh RasPrj dataframes
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
1217
|
+
if ras_object:
|
1218
|
+
ras_object.plan_df = ras_object.get_plan_entries()
|
1219
|
+
ras_object.unsteady_df = ras_object.get_unsteady_entries()
|
1220
|
+
|
ras_commander/RasToGo.py
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
"""
|
2
|
+
RasToGo module provides functions to interface HEC-RAS with go-consequences.
|
3
|
+
This module helps prepare and format RAS data for use with go-consequences.
|
4
|
+
"""
|
5
|
+
|
6
|
+
import logging
|
7
|
+
from pathlib import Path
|
8
|
+
from typing import Dict, List, Optional, Any, Tuple, Union
|
9
|
+
import pandas as pd
|
10
|
+
import numpy as np
|
11
|
+
|
12
|
+
from .Decorators import log_call, standardize_input
|
13
|
+
from .LoggingConfig import setup_logging, get_logger
|
14
|
+
|
15
|
+
logger = get_logger(__name__)
|
16
|
+
|
17
|
+
class RasToGo:
|
18
|
+
"""Class containing functions to interface HEC-RAS with go-consequences."""
|
19
|
+
|
20
|
+
#@staticmethod
|
21
|
+
#@log_call
|