ras-commander 0.1.0__py2.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/__init__.py +24 -0
- ras_commander/execution.py +315 -0
- ras_commander/file_operations.py +173 -0
- ras_commander/geometry_operations.py +184 -0
- ras_commander/plan_operations.py +307 -0
- ras_commander/project_config.py +64 -0
- ras_commander/project_init.py +174 -0
- ras_commander/project_management.py +227 -0
- ras_commander/project_setup.py +15 -0
- ras_commander/unsteady_operations.py +172 -0
- ras_commander/utilities.py +195 -0
- ras_commander-0.1.0.dist-info/LICENSE +5 -0
- ras_commander-0.1.0.dist-info/METADATA +135 -0
- ras_commander-0.1.0.dist-info/RECORD +16 -0
- ras_commander-0.1.0.dist-info/WHEEL +6 -0
- ras_commander-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,184 @@
|
|
1
|
+
"""
|
2
|
+
Operations for handling geometry files in HEC-RAS projects.
|
3
|
+
"""
|
4
|
+
from pathlib import Path
|
5
|
+
from .plan_operations import PlanOperations
|
6
|
+
from .file_operations import FileOperations
|
7
|
+
from .project_config import ProjectConfig
|
8
|
+
import re
|
9
|
+
|
10
|
+
class GeometryOperations:
|
11
|
+
"""
|
12
|
+
A class for operations on HEC-RAS geometry files.
|
13
|
+
"""
|
14
|
+
@staticmethod
|
15
|
+
def clear_geometry_preprocessor_files(plan_file):
|
16
|
+
"""
|
17
|
+
Clear HEC-RAS geometry preprocessor files for a given plan file.
|
18
|
+
|
19
|
+
Parameters:
|
20
|
+
plan_file (str): Full path to the HEC-RAS plan file (.p*)
|
21
|
+
|
22
|
+
Returns:
|
23
|
+
None
|
24
|
+
"""
|
25
|
+
config = ProjectConfig()
|
26
|
+
config.check_initialized()
|
27
|
+
|
28
|
+
plan_path = Path(plan_file)
|
29
|
+
geom_preprocessor_file = plan_path.with_suffix('.c' + plan_path.suffix[2:])
|
30
|
+
if geom_preprocessor_file.exists():
|
31
|
+
print(f"Deleting geometry preprocessor file: {geom_preprocessor_file}")
|
32
|
+
geom_preprocessor_file.unlink()
|
33
|
+
print("File deletion completed successfully.")
|
34
|
+
else:
|
35
|
+
print(f"No geometry preprocessor file found for: {plan_file}")
|
36
|
+
|
37
|
+
@staticmethod
|
38
|
+
def clear_geometry_preprocessor_files_for_all_plans():
|
39
|
+
"""
|
40
|
+
Clear HEC-RAS geometry preprocessor files for all plan files in the project directory.
|
41
|
+
|
42
|
+
Returns:
|
43
|
+
None
|
44
|
+
"""
|
45
|
+
config = ProjectConfig()
|
46
|
+
config.check_initialized()
|
47
|
+
|
48
|
+
for plan_file in config.project_folder.glob("*.p[0-9][0-9]"):
|
49
|
+
GeometryOperations.clear_geometry_preprocessor_files(plan_file)
|
50
|
+
|
51
|
+
@staticmethod
|
52
|
+
def copy_geometry_files(dst_folder, template_geom):
|
53
|
+
"""
|
54
|
+
Copy geometry files from a template to the next available geometry number
|
55
|
+
and update the destination folder accordingly.
|
56
|
+
|
57
|
+
Parameters:
|
58
|
+
dst_folder (str): Destination folder path
|
59
|
+
template_geom (str): Template geometry number (e.g., 'g01')
|
60
|
+
|
61
|
+
Returns:
|
62
|
+
str: New geometry number (e.g., 'g03')
|
63
|
+
"""
|
64
|
+
config = ProjectConfig()
|
65
|
+
config.check_initialized()
|
66
|
+
|
67
|
+
src_folder = config.project_folder
|
68
|
+
dst_folder = Path(dst_folder)
|
69
|
+
dst_folder.mkdir(parents=True, exist_ok=True)
|
70
|
+
|
71
|
+
# Determine the next available geometry number
|
72
|
+
existing_numbers = []
|
73
|
+
geom_file_pattern = re.compile(r'^Geom File=g(\d+)', re.IGNORECASE)
|
74
|
+
|
75
|
+
for plan_file in src_folder.glob("*.p[0-9][0-9]"):
|
76
|
+
with open(plan_file, 'r') as file:
|
77
|
+
lines = file.readlines()
|
78
|
+
for line in lines:
|
79
|
+
match = geom_file_pattern.match(line.strip())
|
80
|
+
if match:
|
81
|
+
existing_numbers.append(int(match.group(1)))
|
82
|
+
|
83
|
+
if existing_numbers:
|
84
|
+
existing_numbers.sort()
|
85
|
+
next_number = 1
|
86
|
+
for num in existing_numbers:
|
87
|
+
if num == next_number:
|
88
|
+
next_number += 1
|
89
|
+
else:
|
90
|
+
break
|
91
|
+
else:
|
92
|
+
next_number = 1
|
93
|
+
|
94
|
+
next_geom_number = f"{next_number:02d}"
|
95
|
+
|
96
|
+
# Copy the template geometry file to the new geometry file
|
97
|
+
template_geom_filename = f"{config.project_name}.g{template_geom}"
|
98
|
+
src_g_file = src_folder / template_geom_filename
|
99
|
+
if src_g_file.exists():
|
100
|
+
new_geom_filename = f"{config.project_name}.g{next_geom_number}"
|
101
|
+
dst_g_file = dst_folder / new_geom_filename
|
102
|
+
dst_g_file.write_bytes(src_g_file.read_bytes())
|
103
|
+
print(f"Copied {src_g_file} to {dst_g_file}")
|
104
|
+
else:
|
105
|
+
raise FileNotFoundError(f"Template geometry file '{src_g_file}' does not exist.")
|
106
|
+
|
107
|
+
# Copy the corresponding .hdf file
|
108
|
+
src_hdf_file = src_folder / f"{template_geom_filename}.hdf"
|
109
|
+
if src_hdf_file.exists():
|
110
|
+
new_hdf_filename = f"{new_geom_filename}.hdf"
|
111
|
+
dst_hdf_file = dst_folder / new_hdf_filename
|
112
|
+
dst_hdf_file.write_bytes(src_hdf_file.read_bytes())
|
113
|
+
print(f"Copied {src_hdf_file} to {dst_hdf_file}")
|
114
|
+
else:
|
115
|
+
raise FileNotFoundError(f"Template geometry .hdf file '{src_hdf_file}' does not exist.")
|
116
|
+
config = ProjectConfig()
|
117
|
+
return f"g{next_geom_number}"
|
118
|
+
|
119
|
+
@staticmethod
|
120
|
+
def rename_geometry_files(old_number, new_number):
|
121
|
+
"""
|
122
|
+
Rename geometry files in the project folder.
|
123
|
+
|
124
|
+
Parameters:
|
125
|
+
old_number (str): Old geometry number (e.g., 'g01')
|
126
|
+
new_number (str): New geometry number (e.g., 'g02')
|
127
|
+
|
128
|
+
Returns:
|
129
|
+
None
|
130
|
+
"""
|
131
|
+
config = ProjectConfig()
|
132
|
+
config.check_initialized()
|
133
|
+
|
134
|
+
folder = config.project_folder
|
135
|
+
old_g_file = next(folder.glob(f"*.{old_number}"), None)
|
136
|
+
if old_g_file:
|
137
|
+
new_g_file = folder / old_g_file.name.replace(old_number, new_number)
|
138
|
+
old_g_file.rename(new_g_file)
|
139
|
+
print(f"Renamed {old_g_file} to {new_g_file}")
|
140
|
+
else:
|
141
|
+
print(f"No .{old_number} file found in {folder}")
|
142
|
+
|
143
|
+
old_hdf_file = next(folder.glob(f"*.{old_number}.hdf"), None)
|
144
|
+
if old_hdf_file:
|
145
|
+
new_hdf_file = folder / old_hdf_file.name.replace(old_number, new_number)
|
146
|
+
old_hdf_file.rename(new_hdf_file)
|
147
|
+
print(f"Renamed {old_hdf_file} to {new_hdf_file}")
|
148
|
+
else:
|
149
|
+
print(f"No .{old_number}.hdf file found in {folder}")
|
150
|
+
|
151
|
+
@staticmethod
|
152
|
+
def update_geometry_reference_in_plan(plan_file, new_geometry_number):
|
153
|
+
"""
|
154
|
+
Update the geometry reference in a plan file.
|
155
|
+
|
156
|
+
Parameters:
|
157
|
+
plan_file (str): Full path to the plan file
|
158
|
+
new_geometry_number (str): New geometry number (e.g., 'g02')
|
159
|
+
|
160
|
+
Returns:
|
161
|
+
None
|
162
|
+
"""
|
163
|
+
config = ProjectConfig()
|
164
|
+
config.check_initialized()
|
165
|
+
|
166
|
+
geometry_full_path = next(config.project_folder.glob(f"*.g{new_geometry_number}"), None)
|
167
|
+
if not geometry_full_path:
|
168
|
+
raise FileNotFoundError(f"Geometry file '*.g{new_geometry_number}' not found in the project folder.")
|
169
|
+
|
170
|
+
plan_path = Path(plan_file)
|
171
|
+
with open(plan_path, 'r') as file:
|
172
|
+
lines = file.readlines()
|
173
|
+
updated = False
|
174
|
+
for index, line in enumerate(lines):
|
175
|
+
if line.startswith("Geom File="):
|
176
|
+
lines[index] = f"Geom File=g{new_geometry_number}\n"
|
177
|
+
updated = True
|
178
|
+
break
|
179
|
+
if updated:
|
180
|
+
with open(plan_path, 'w') as file:
|
181
|
+
file.writelines(lines)
|
182
|
+
print(f"Updated geometry reference in {plan_file} to {new_geometry_number}")
|
183
|
+
else:
|
184
|
+
print(f"No geometry reference found in {plan_file}")
|
@@ -0,0 +1,307 @@
|
|
1
|
+
"""
|
2
|
+
Operations for modifying and updating HEC-RAS plan files.
|
3
|
+
"""
|
4
|
+
import re
|
5
|
+
from pathlib import Path
|
6
|
+
|
7
|
+
|
8
|
+
from .project_config import ProjectConfig
|
9
|
+
|
10
|
+
class PlanOperations:
|
11
|
+
"""
|
12
|
+
A class for operations on HEC-RAS plan files.
|
13
|
+
"""
|
14
|
+
@staticmethod
|
15
|
+
def apply_geometry_to_plan(plan_file, geometry_number):
|
16
|
+
"""
|
17
|
+
Apply a geometry file to a plan file.
|
18
|
+
|
19
|
+
Parameters:
|
20
|
+
plan_file (str): Full path to the HEC-RAS plan file (.pXX)
|
21
|
+
geometry_number (str): Geometry number to apply (e.g., '01')
|
22
|
+
|
23
|
+
Returns:
|
24
|
+
None
|
25
|
+
|
26
|
+
Raises:
|
27
|
+
ValueError: If the specified geometry number is not found in the project file
|
28
|
+
"""
|
29
|
+
config = ProjectConfig()
|
30
|
+
config.check_initialized()
|
31
|
+
|
32
|
+
if f"g{geometry_number}" not in config.ras_geom_entries['geom_number'].values:
|
33
|
+
raise ValueError(f"Geometry number g{geometry_number} not found in project file.")
|
34
|
+
with open(plan_file, 'r') as f:
|
35
|
+
lines = f.readlines()
|
36
|
+
with open(plan_file, 'w') as f:
|
37
|
+
for line in lines:
|
38
|
+
if line.startswith("Geom File="):
|
39
|
+
f.write(f"Geom File=g{geometry_number}\n")
|
40
|
+
print(f"Updated Geom File in {plan_file} to g{geometry_number}")
|
41
|
+
else:
|
42
|
+
f.write(line)
|
43
|
+
|
44
|
+
@staticmethod
|
45
|
+
def apply_flow_to_plan(plan_file, flow_number):
|
46
|
+
"""
|
47
|
+
Apply a steady flow file to a plan file.
|
48
|
+
|
49
|
+
Parameters:
|
50
|
+
plan_file (str): Full path to the HEC-RAS plan file (.pXX)
|
51
|
+
flow_number (str): Flow number to apply (e.g., '02')
|
52
|
+
|
53
|
+
Returns:
|
54
|
+
None
|
55
|
+
|
56
|
+
Raises:
|
57
|
+
ValueError: If the specified flow number is not found in the project file
|
58
|
+
"""
|
59
|
+
config = ProjectConfig()
|
60
|
+
config.check_initialized()
|
61
|
+
|
62
|
+
if f"{flow_number}" not in config.ras_flow_entries['flow_number'].values:
|
63
|
+
raise ValueError(f"Flow number f{flow_number} not found in project file.")
|
64
|
+
with open(plan_file, 'r') as f:
|
65
|
+
lines = f.readlines()
|
66
|
+
with open(plan_file, 'w') as f:
|
67
|
+
for line in lines:
|
68
|
+
if line.startswith("Flow File="):
|
69
|
+
f.write(f"Flow File=f{flow_number}\n")
|
70
|
+
print(f"Updated Flow File in {plan_file} to f{flow_number}")
|
71
|
+
else:
|
72
|
+
f.write(line)
|
73
|
+
|
74
|
+
@staticmethod
|
75
|
+
def copy_plan_from_template(template_plan):
|
76
|
+
"""
|
77
|
+
Create a new plan file based on a template and update the project file.
|
78
|
+
|
79
|
+
Parameters:
|
80
|
+
|
81
|
+
template_plan (str): Plan file to use as template (e.g., '01')
|
82
|
+
project_folder, project_name,
|
83
|
+
Returns:
|
84
|
+
str: New plan number
|
85
|
+
"""
|
86
|
+
config = ProjectConfig()
|
87
|
+
config.check_initialized()
|
88
|
+
|
89
|
+
# Read existing plan numbers
|
90
|
+
ras_plan_entries = config.ras_plan_entries
|
91
|
+
|
92
|
+
# Get next available plan number
|
93
|
+
new_plan_num = PlanOperations.get_next_available_number(ras_plan_entries['plan_number'])
|
94
|
+
|
95
|
+
# Copy template plan file to new plan file
|
96
|
+
template_plan_path = Path(config.project_folder) / f"{config.project_name}.p{template_plan}"
|
97
|
+
new_plan_path = Path(config.project_folder) / f"{config.project_name}.p{new_plan_num}"
|
98
|
+
new_plan_path.write_bytes(template_plan_path.read_bytes())
|
99
|
+
print(f"Copied {template_plan_path} to {new_plan_path}")
|
100
|
+
|
101
|
+
# Update project file with new plan
|
102
|
+
project_file = config.project_file
|
103
|
+
with open(project_file, 'a') as f:
|
104
|
+
f.write(f"\nPlan File=p{new_plan_num}")
|
105
|
+
print(f"Updated {project_file} with new plan p{new_plan_num}")
|
106
|
+
config = ProjectConfig()
|
107
|
+
return f"{new_plan_num}"
|
108
|
+
|
109
|
+
@staticmethod
|
110
|
+
def get_next_available_number(existing_numbers):
|
111
|
+
"""
|
112
|
+
Determine the next available number from a list of existing numbers.
|
113
|
+
|
114
|
+
Parameters:
|
115
|
+
existing_numbers (list): List of existing numbers as strings
|
116
|
+
|
117
|
+
Returns:
|
118
|
+
str: Next available number as a zero-padded string
|
119
|
+
"""
|
120
|
+
existing_numbers = sorted(int(num) for num in existing_numbers)
|
121
|
+
next_number = 1
|
122
|
+
for num in existing_numbers:
|
123
|
+
if num == next_number:
|
124
|
+
next_number += 1
|
125
|
+
else:
|
126
|
+
break
|
127
|
+
return f"{next_number:02d}"
|
128
|
+
|
129
|
+
|
130
|
+
|
131
|
+
@staticmethod
|
132
|
+
def apply_unsteady_to_plan(plan_file, unsteady_number):
|
133
|
+
"""
|
134
|
+
Apply an unsteady flow file to a plan file.
|
135
|
+
|
136
|
+
Parameters:
|
137
|
+
plan_file (str): Full path to the HEC-RAS plan file (.pXX)
|
138
|
+
unsteady_number (str): Unsteady flow number to apply (e.g., '01')
|
139
|
+
|
140
|
+
Returns:
|
141
|
+
None
|
142
|
+
|
143
|
+
Raises:
|
144
|
+
ValueError: If the specified unsteady number is not found in the project file
|
145
|
+
"""
|
146
|
+
config = ProjectConfig()
|
147
|
+
config.check_initialized()
|
148
|
+
|
149
|
+
if f"u{unsteady_number}" not in config.ras_unsteady_entries['unsteady_number'].values:
|
150
|
+
raise ValueError(f"Unsteady number u{unsteady_number} not found in project file.")
|
151
|
+
with open(plan_file, 'r') as f:
|
152
|
+
lines = f.readlines()
|
153
|
+
with open(plan_file, 'w') as f:
|
154
|
+
for line in lines:
|
155
|
+
if line.startswith("Unsteady File=") or line.startswith("Flow File=u"):
|
156
|
+
f.write(f"Unsteady File=u{unsteady_number}\n")
|
157
|
+
print(f"Updated Unsteady File in {plan_file} to u{unsteady_number}")
|
158
|
+
else:
|
159
|
+
f.write(line)
|
160
|
+
|
161
|
+
@staticmethod
|
162
|
+
def set_num_cores(plan_file, num_cores):
|
163
|
+
"""
|
164
|
+
Update the maximum number of cores to use in the HEC-RAS plan file.
|
165
|
+
|
166
|
+
Parameters:
|
167
|
+
plan_file (str): Full path to the plan file
|
168
|
+
num_cores (int): Maximum number of cores to use
|
169
|
+
|
170
|
+
Returns:
|
171
|
+
None
|
172
|
+
"""
|
173
|
+
config = ProjectConfig()
|
174
|
+
config.check_initialized()
|
175
|
+
|
176
|
+
cores_pattern = re.compile(r"(UNET D1 Cores= )\d+")
|
177
|
+
with open(plan_file, 'r') as file:
|
178
|
+
content = file.read()
|
179
|
+
new_content = cores_pattern.sub(rf"\g<1>{num_cores}", content)
|
180
|
+
with open(plan_file, 'w') as file:
|
181
|
+
file.write(new_content)
|
182
|
+
print(f"Updated {plan_file} with {num_cores} cores.")
|
183
|
+
|
184
|
+
@staticmethod
|
185
|
+
def update_geompre_flags(file_path, run_htab_value, use_ib_tables_value):
|
186
|
+
"""
|
187
|
+
Update the simulation plan file to modify the `Run HTab` and `UNET Use Existing IB Tables` settings.
|
188
|
+
|
189
|
+
Parameters:
|
190
|
+
file_path (str): Path to the simulation plan file (.p06 or similar) that you want to modify.
|
191
|
+
run_htab_value (int): Value for the `Run HTab` setting:
|
192
|
+
- `0` : Do not run the geometry preprocessor, use existing geometry tables.
|
193
|
+
- `-1` : Run the geometry preprocessor, forcing a recomputation of the geometry tables.
|
194
|
+
use_ib_tables_value (int): Value for the `UNET Use Existing IB Tables` setting:
|
195
|
+
- `0` : Use existing interpolation/boundary (IB) tables without recomputing them.
|
196
|
+
- `-1` : Do not use existing IB tables, force a recomputation.
|
197
|
+
|
198
|
+
Returns:
|
199
|
+
None
|
200
|
+
|
201
|
+
Raises:
|
202
|
+
ValueError: If `run_htab_value` or `use_ib_tables_value` are not integers or not within the accepted values (`0` or `-1`).
|
203
|
+
FileNotFoundError: If the specified file does not exist.
|
204
|
+
IOError: If there is an error reading or writing the file.
|
205
|
+
"""
|
206
|
+
config = ProjectConfig()
|
207
|
+
config.check_initialized()
|
208
|
+
|
209
|
+
if run_htab_value not in [-1, 0]:
|
210
|
+
raise ValueError("Invalid value for `Run HTab`. Expected `0` or `-1`.")
|
211
|
+
if use_ib_tables_value not in [-1, 0]:
|
212
|
+
raise ValueError("Invalid value for `UNET Use Existing IB Tables`. Expected `0` or `-1`.")
|
213
|
+
try:
|
214
|
+
print(f"Reading the file: {file_path}")
|
215
|
+
with open(file_path, 'r') as file:
|
216
|
+
lines = file.readlines()
|
217
|
+
print("Updating the file with new settings...")
|
218
|
+
updated_lines = []
|
219
|
+
for line in lines:
|
220
|
+
if line.strip().startswith("Run HTab="):
|
221
|
+
updated_line = f"Run HTab= {run_htab_value} \n"
|
222
|
+
updated_lines.append(updated_line)
|
223
|
+
print(f"Updated 'Run HTab' to {run_htab_value}")
|
224
|
+
elif line.strip().startswith("UNET Use Existing IB Tables="):
|
225
|
+
updated_line = f"UNET Use Existing IB Tables= {use_ib_tables_value} \n"
|
226
|
+
updated_lines.append(updated_line)
|
227
|
+
print(f"Updated 'UNET Use Existing IB Tables' to {use_ib_tables_value}")
|
228
|
+
else:
|
229
|
+
updated_lines.append(line)
|
230
|
+
print(f"Writing the updated settings back to the file: {file_path}")
|
231
|
+
with open(file_path, 'w') as file:
|
232
|
+
file.writelines(updated_lines)
|
233
|
+
print("File update completed successfully.")
|
234
|
+
except FileNotFoundError:
|
235
|
+
raise FileNotFoundError(f"The file '{file_path}' does not exist.")
|
236
|
+
except IOError as e:
|
237
|
+
raise IOError(f"An error occurred while reading or writing the file: {e}")
|
238
|
+
|
239
|
+
@staticmethod
|
240
|
+
def get_plan_full_path(plan_number: str) -> str:
|
241
|
+
"""Return the full path for a given plan number."""
|
242
|
+
config = ProjectConfig()
|
243
|
+
config.check_initialized()
|
244
|
+
|
245
|
+
plan_path = config.ras_plan_entries[config.ras_plan_entries['plan_number'] == plan_number]
|
246
|
+
if not plan_path.empty:
|
247
|
+
return plan_path['full_path'].iloc[0]
|
248
|
+
else:
|
249
|
+
raise ValueError(f"Plan number {plan_number} not found.")
|
250
|
+
|
251
|
+
@staticmethod
|
252
|
+
def get_results_full_path(plan_number: str) -> str:
|
253
|
+
"""Return the results path for a given plan number."""
|
254
|
+
config = ProjectConfig()
|
255
|
+
config.check_initialized()
|
256
|
+
|
257
|
+
results_path = config.ras_plan_entries[config.ras_plan_entries['plan_number'] == plan_number]
|
258
|
+
if not results_path.empty:
|
259
|
+
results_file_path = Path(results_path['results_path'].iloc[0])
|
260
|
+
if results_file_path.exists():
|
261
|
+
print(f"Results file for Plan number {plan_number} exists at: {results_file_path}")
|
262
|
+
return str(results_file_path)
|
263
|
+
else:
|
264
|
+
print(f"Error: Results file for Plan number {plan_number} does not exist at the expected location: {results_file_path}")
|
265
|
+
return None
|
266
|
+
else:
|
267
|
+
print(f"Error: Results file for Plan number {plan_number} not found in the entries.")
|
268
|
+
return None
|
269
|
+
|
270
|
+
|
271
|
+
|
272
|
+
|
273
|
+
@staticmethod
|
274
|
+
def get_flow_full_path(flow_number: str) -> str:
|
275
|
+
"""Return the full path for a given flow number."""
|
276
|
+
config = ProjectConfig()
|
277
|
+
config.check_initialized()
|
278
|
+
|
279
|
+
flow_path = config.ras_flow_entries[config.ras_flow_entries['flow_number'] == flow_number]
|
280
|
+
if not flow_path.empty:
|
281
|
+
return flow_path['full_path'].iloc[0]
|
282
|
+
else:
|
283
|
+
raise ValueError(f"Flow number {flow_number} not found.")
|
284
|
+
|
285
|
+
@staticmethod
|
286
|
+
def get_unsteady_full_path(unsteady_number: str) -> str:
|
287
|
+
"""Return the full path for a given unsteady number."""
|
288
|
+
config = ProjectConfig()
|
289
|
+
config.check_initialized()
|
290
|
+
|
291
|
+
unsteady_path = config.ras_unsteady_entries[config.ras_unsteady_entries['unsteady_number'] == unsteady_number]
|
292
|
+
if not unsteady_path.empty:
|
293
|
+
return unsteady_path['full_path'].iloc[0]
|
294
|
+
else:
|
295
|
+
raise ValueError(f"Unsteady number {unsteady_number} not found.")
|
296
|
+
|
297
|
+
@staticmethod
|
298
|
+
def get_geom_full_path(geometry_number: str) -> str:
|
299
|
+
"""Return the full path for a given geometry number."""
|
300
|
+
config = ProjectConfig()
|
301
|
+
config.check_initialized()
|
302
|
+
|
303
|
+
geom_path = config.ras_geom_entries[config.ras_geom_entries['geom_number'] == geometry_number]
|
304
|
+
if not geom_path.empty:
|
305
|
+
return geom_path['full_path'].iloc[0]
|
306
|
+
else:
|
307
|
+
raise ValueError(f"Geometry number {geometry_number} not found.")
|
@@ -0,0 +1,64 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
from .project_setup import find_hecras_project_file, load_project_data
|
3
|
+
|
4
|
+
# Notes (do note delete):
|
5
|
+
# This ProjectConfig class is a singleton, so it will have the same values for all instances.
|
6
|
+
# The class is very important for the project setup, as it will be used to store all the project's basic information.
|
7
|
+
# Instead of having to pass around the project folder, project file, etc., we can just pass around the ProjectConfig class instance.
|
8
|
+
# The class will need to be initialized with the project folder, which will then be used to set all the other project information.
|
9
|
+
# The project information will be stored in the instance as class variables.
|
10
|
+
|
11
|
+
# Documentation Notes:
|
12
|
+
# print each variable name and value after it is defined, so the user can easily reference the project's basic information using the self. class variables
|
13
|
+
# example: print(f"self.project_file: {self.project_file}")
|
14
|
+
|
15
|
+
class ProjectConfig:
|
16
|
+
_instance = None
|
17
|
+
|
18
|
+
def __new__(cls):
|
19
|
+
if cls._instance is None:
|
20
|
+
cls._instance = super(ProjectConfig, cls).__new__(cls)
|
21
|
+
cls._instance.initialized = False
|
22
|
+
return cls._instance
|
23
|
+
|
24
|
+
def initialize(self, project_folder, hecras_exe_path):
|
25
|
+
self.project_folder = Path(project_folder)
|
26
|
+
print(f"self.project_folder: {self.project_folder}")
|
27
|
+
|
28
|
+
self.project_file = find_hecras_project_file(self.project_folder)
|
29
|
+
print(f"self.project_file: {self.project_file}")
|
30
|
+
|
31
|
+
if self.project_file is None:
|
32
|
+
raise ValueError(f"No HEC-RAS project file found in {self.project_folder}")
|
33
|
+
|
34
|
+
self.project_name = self.project_file.stem
|
35
|
+
print(f"self.project_name: {self.project_name}")
|
36
|
+
|
37
|
+
self.hecras_exe_path = hecras_exe_path
|
38
|
+
print(f"self.hecras_exe_path: {self.hecras_exe_path}")
|
39
|
+
|
40
|
+
self._load_project_data()
|
41
|
+
self.initialized = True
|
42
|
+
print(f"self.initialized: {self.initialized}")
|
43
|
+
|
44
|
+
def _load_project_data(self):
|
45
|
+
data = load_project_data(self.project_file)
|
46
|
+
self.ras_plan_entries = data['ras_plan_entries']
|
47
|
+
print(f"self.ras_plan_entries: {self.ras_plan_entries}")
|
48
|
+
|
49
|
+
self.ras_flow_entries = data['ras_flow_entries']
|
50
|
+
print(f"self.ras_flow_entries: {self.ras_flow_entries}")
|
51
|
+
|
52
|
+
self.ras_unsteady_entries = data['ras_unsteady_entries']
|
53
|
+
print(f"self.ras_unsteady_entries: {self.ras_unsteady_entries}")
|
54
|
+
|
55
|
+
self.ras_geom_entries = data['ras_geom_entries']
|
56
|
+
print(f"self.ras_geom_entries: {self.ras_geom_entries}")
|
57
|
+
|
58
|
+
@property
|
59
|
+
def is_initialized(self):
|
60
|
+
return self.initialized
|
61
|
+
|
62
|
+
def check_initialized(self):
|
63
|
+
if not self.initialized:
|
64
|
+
raise RuntimeError("Project not initialized. Call init_ras_project() first.")
|