ras-commander 0.23.0.dev0__py2.py3-none-any.whl → 0.24.0.dev0__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/{RasCommander.py → RasCmdr.py} +246 -262
- ras_commander/RasExamples.py +4 -4
- ras_commander/RasPrj.py +1 -1
- ras_commander/RasUnsteady.py +12 -3
- ras_commander/RasUtils.py +4 -4
- ras_commander/__init__.py +6 -6
- ras_commander/_version.py +2 -2
- {ras_commander-0.23.0.dev0.dist-info → ras_commander-0.24.0.dev0.dist-info}/METADATA +59 -76
- ras_commander-0.24.0.dev0.dist-info/RECORD +14 -0
- ras_commander/README.md +0 -187
- ras_commander-0.23.0.dev0.dist-info/RECORD +0 -15
- {ras_commander-0.23.0.dev0.dist-info → ras_commander-0.24.0.dev0.dist-info}/LICENSE +0 -0
- {ras_commander-0.23.0.dev0.dist-info → ras_commander-0.24.0.dev0.dist-info}/WHEEL +0 -0
- {ras_commander-0.23.0.dev0.dist-info → ras_commander-0.24.0.dev0.dist-info}/top_level.txt +0 -0
@@ -25,64 +25,64 @@ import queue
|
|
25
25
|
from threading import Thread, Lock
|
26
26
|
import time
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
# TO DO:
|
29
|
+
# 1. Alternate Run Mode for compute_plan and compute_parallel: Using Powershell to execute the HEC-RAS command and hide the RAS window and all child windows.
|
30
|
+
# If this is implemented, and the plan has a popup, then the plan will not execute. This is a deal breaker for many scenarios, and should only be used
|
31
|
+
# as a special option for those who don't want to deal with the popups, or want to run in the background. This option should be limited to non-commercial use.
|
32
|
+
# 2. Implment compute_plan_remote to go along with compute_plan. This will be a compute_plan that is run on a remote machine via a psexec command.
|
33
|
+
# First, we will use the keyring package to securely store the remote machine username and password.
|
34
|
+
# Second, we will implement the psexec command to execute the HEC-RAS command on the remote machine.
|
35
|
+
# Each machine will need to be initialized as a remote_worker object, which will store the machine name, username, password, ras_exe_path, local folder path and other relevant info.
|
36
|
+
# A separate RasRemote class will be created to handle the creation of the remote_worker objects and the necessary abstractions.
|
37
|
+
# The compute_plan_remote function will live in RasCmdr, and will be a thin abstraction above the RasRemote class, since the functions will be simliar to the existing compute_plan functions, but specific to remote execution.
|
38
|
+
|
39
|
+
|
40
|
+
class RasCmdr:
|
30
41
|
@staticmethod
|
31
42
|
def compute_plan(
|
32
43
|
plan_number,
|
33
|
-
|
34
|
-
ras_object=None
|
35
|
-
|
44
|
+
dest_folder=None,
|
45
|
+
ras_object=None,
|
46
|
+
clear_geompre=False,
|
47
|
+
num_cores=None,
|
48
|
+
overwrite_dest=False
|
49
|
+
):
|
36
50
|
"""
|
37
51
|
Execute a HEC-RAS plan.
|
38
52
|
|
39
53
|
Args:
|
40
54
|
plan_number (str, Path): The plan number to execute (e.g., "01", "02") or the full path to the plan file.
|
41
|
-
|
55
|
+
dest_folder (str, Path, optional): Name of the folder or full path for computation.
|
42
56
|
If a string is provided, it will be created in the same parent directory as the project folder.
|
43
57
|
If a full path is provided, it will be used as is.
|
44
|
-
If the compute_folder already exists, a ValueError will be raised to prevent overwriting.
|
45
58
|
ras_object (RasPrj, optional): Specific RAS object to use. If None, uses the global ras instance.
|
59
|
+
clear_geompre (bool, optional): Whether to clear geometry preprocessor files. Defaults to False.
|
60
|
+
num_cores (int, optional): Number of cores to use for the plan execution. If None, the current setting is not changed.
|
61
|
+
overwrite_dest (bool, optional): If True, overwrite the destination folder if it exists. Defaults to False.
|
46
62
|
|
47
63
|
Returns:
|
48
64
|
bool: True if the execution was successful, False otherwise.
|
49
65
|
|
50
66
|
Raises:
|
51
|
-
ValueError: If the specified
|
52
|
-
|
53
|
-
Example:
|
54
|
-
# Execute plan "01" in the current project folder
|
55
|
-
RasCommander.compute_plan("01")
|
56
|
-
|
57
|
-
# Execute plan "02" in a new compute folder
|
58
|
-
RasCommander.compute_plan("02", compute_folder="ComputeRun1")
|
59
|
-
|
60
|
-
# Execute a specific plan file in a new compute folder
|
61
|
-
RasCommander.compute_plan(r"C:\path\to\plan.p01.hdf", compute_folder="ComputeRun2")
|
62
|
-
|
63
|
-
Notes:
|
64
|
-
When using a compute_folder:
|
65
|
-
- A new RasPrj object is created for the computation.
|
66
|
-
- The entire project is copied to the new folder before execution.
|
67
|
-
- Results will be stored in the new folder, preserving the original project.
|
67
|
+
ValueError: If the specified dest_folder already exists and is not empty, and overwrite_dest is False.
|
68
68
|
"""
|
69
|
-
# Initialize RasPrj object with the default "ras" object if no specific object is provided
|
70
69
|
ras_obj = ras_object or ras
|
71
70
|
ras_obj.check_initialized()
|
72
71
|
|
73
|
-
|
74
|
-
|
75
|
-
|
72
|
+
if dest_folder is not None:
|
73
|
+
dest_folder = Path(ras_obj.project_folder).parent / dest_folder if isinstance(dest_folder, str) else Path(dest_folder)
|
74
|
+
|
75
|
+
if dest_folder.exists():
|
76
|
+
if overwrite_dest:
|
77
|
+
shutil.rmtree(dest_folder)
|
78
|
+
elif any(dest_folder.iterdir()):
|
79
|
+
raise ValueError(f"Destination folder '{dest_folder}' exists and is not empty. Use overwrite_dest=True to overwrite.")
|
76
80
|
|
77
|
-
|
78
|
-
|
79
|
-
raise ValueError(f"Compute folder '{compute_folder}' exists and is not empty. Please ensure the compute folder is empty before proceeding.")
|
80
|
-
elif not compute_folder.exists():
|
81
|
-
shutil.copytree(ras_obj.project_folder, compute_folder)
|
81
|
+
dest_folder.mkdir(parents=True, exist_ok=True)
|
82
|
+
shutil.copytree(ras_obj.project_folder, dest_folder, dirs_exist_ok=True)
|
82
83
|
|
83
|
-
# Initialize a new RAS project in the compute folder
|
84
84
|
compute_ras = RasPrj()
|
85
|
-
compute_ras.initialize(
|
85
|
+
compute_ras.initialize(dest_folder, ras_obj.ras_exe_path)
|
86
86
|
compute_prj_path = compute_ras.prj_file
|
87
87
|
else:
|
88
88
|
compute_ras = ras_obj
|
@@ -95,6 +95,22 @@ class RasCommander:
|
|
95
95
|
print(f"Error: Could not find project file or plan file for plan {plan_number}")
|
96
96
|
return False
|
97
97
|
|
98
|
+
# Clear geometry preprocessor files if requested
|
99
|
+
if clear_geompre:
|
100
|
+
try:
|
101
|
+
RasGeo.clear_geompre_files(compute_plan_path, ras_object=compute_ras)
|
102
|
+
print(f"Cleared geometry preprocessor files for plan: {plan_number}")
|
103
|
+
except Exception as e:
|
104
|
+
print(f"Error clearing geometry preprocessor files for plan {plan_number}: {str(e)}")
|
105
|
+
|
106
|
+
# Set the number of cores if specified
|
107
|
+
if num_cores is not None:
|
108
|
+
try:
|
109
|
+
RasPlan.set_num_cores(compute_plan_path, num_cores=num_cores, ras_object=compute_ras)
|
110
|
+
print(f"Set number of cores to {num_cores} for plan: {plan_number}")
|
111
|
+
except Exception as e:
|
112
|
+
print(f"Error setting number of cores for plan {plan_number}: {str(e)}")
|
113
|
+
|
98
114
|
# Prepare the command for HEC-RAS execution
|
99
115
|
cmd = f'"{compute_ras.ras_exe_path}" -c "{compute_prj_path}" "{compute_plan_path}"'
|
100
116
|
print("Running HEC-RAS from the Command Line:")
|
@@ -124,164 +140,16 @@ class RasCommander:
|
|
124
140
|
ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
|
125
141
|
|
126
142
|
|
127
|
-
def compute_test_mode(
|
128
|
-
plan_numbers=None,
|
129
|
-
folder_suffix="[Test]",
|
130
|
-
clear_geompre=False,
|
131
|
-
max_cores=None,
|
132
|
-
ras_object=None
|
133
|
-
):
|
134
|
-
"""
|
135
|
-
Execute HEC-RAS plans in test mode.
|
136
|
-
|
137
|
-
This function creates a separate test folder, copies the project there, and executes the specified plans.
|
138
|
-
It allows for isolated testing without affecting the original project files.
|
139
|
-
|
140
|
-
Args:
|
141
|
-
plan_numbers (list of str, optional): List of plan numbers to execute.
|
142
|
-
If None, all plans will be executed. Default is None.
|
143
|
-
folder_suffix (str, optional): Suffix to append to the test folder name.
|
144
|
-
Defaults to "[Test]".
|
145
|
-
clear_geompre (bool, optional): Whether to clear geometry preprocessor files.
|
146
|
-
Defaults to False.
|
147
|
-
max_cores (int, optional): Maximum number of cores to use for each plan.
|
148
|
-
If None, the current setting is not changed. Default is None.
|
149
|
-
ras_object (RasPrj, optional): Specific RAS object to use. If None, uses the global ras instance.
|
150
|
-
|
151
|
-
Returns:
|
152
|
-
None
|
153
|
-
|
154
|
-
Example:
|
155
|
-
Run all plans: RasCommander.compute_test_mode()
|
156
|
-
Run specific plans: RasCommander.compute_test_mode(plan_numbers=["01", "03", "05"])
|
157
|
-
Run plans with a custom folder suffix: RasCommander.compute_test_mode(folder_suffix="[TestRun]")
|
158
|
-
Run plans and clear geometry preprocessor files: RasCommander.compute_test_mode(clear_geompre=True)
|
159
|
-
Run plans with a specific number of cores: RasCommander.compute_test_mode(max_cores=4)
|
160
|
-
|
161
|
-
Notes:
|
162
|
-
- This function executes plans in a separate folder for isolated testing.
|
163
|
-
- If plan_numbers is not provided, all plans in the project will be executed.
|
164
|
-
- The function does not change the geometry preprocessor and IB tables settings.
|
165
|
-
- To force recomputing of geometry preprocessor and IB tables, use the clear_geompre=True option.
|
166
|
-
- Plans are executed sequentially.
|
167
|
-
"""
|
168
|
-
|
169
|
-
# This line of code is used to initialize the RasPrj object with the default "ras" object if no specific object is provided.
|
170
|
-
ras_obj = ras_object or ras
|
171
|
-
# This line of code is used to check if the RasPrj object is initialized.
|
172
|
-
ras_obj.check_initialized()
|
173
|
-
|
174
|
-
print("Starting the compute_test_mode...")
|
175
|
-
|
176
|
-
# Use the project folder from the ras object
|
177
|
-
project_folder = ras_obj.project_folder
|
178
|
-
|
179
|
-
# Check if the project folder exists
|
180
|
-
if not project_folder.exists():
|
181
|
-
print(f"Error: Project folder '{project_folder}' does not exist.")
|
182
|
-
return
|
183
|
-
|
184
|
-
# Create test folder with the specified suffix in the same directory as the project folder
|
185
|
-
compute_folder = project_folder.parent / f"{project_folder.name} {folder_suffix}"
|
186
|
-
print(f"Creating the test folder: {compute_folder}...")
|
187
|
-
|
188
|
-
# Check if the compute folder exists and is empty
|
189
|
-
if compute_folder.exists():
|
190
|
-
if any(compute_folder.iterdir()):
|
191
|
-
raise ValueError(
|
192
|
-
f"Compute folder '{compute_folder}' exists and is not empty. "
|
193
|
-
"Please ensure the compute folder is empty before proceeding."
|
194
|
-
)
|
195
|
-
else:
|
196
|
-
try:
|
197
|
-
shutil.copytree(project_folder, compute_folder)
|
198
|
-
except FileNotFoundError:
|
199
|
-
print(f"Error: Unable to copy project folder. Source folder '{project_folder}' not found.")
|
200
|
-
return
|
201
|
-
except PermissionError:
|
202
|
-
print(f"Error: Permission denied when trying to create or copy to '{compute_folder}'.")
|
203
|
-
return
|
204
|
-
except Exception as e:
|
205
|
-
print(f"Error occurred while copying project folder: {str(e)}")
|
206
|
-
return
|
207
|
-
|
208
|
-
# Initialize a new RAS project in the compute folder
|
209
|
-
try:
|
210
|
-
compute_ras = RasPrj()
|
211
|
-
compute_ras.initialize(compute_folder, ras_obj.ras_exe_path)
|
212
|
-
compute_prj_path = compute_ras.prj_file
|
213
|
-
except Exception as e:
|
214
|
-
print(f"Error initializing RAS project in compute folder: {str(e)}")
|
215
|
-
return
|
216
|
-
|
217
|
-
if not compute_prj_path:
|
218
|
-
print("Project file not found.")
|
219
|
-
return
|
220
|
-
print(f"Project file found: {compute_prj_path}")
|
221
|
-
|
222
|
-
# Get plan entries
|
223
|
-
print("Getting plan entries...")
|
224
|
-
try:
|
225
|
-
ras_compute_plan_entries = compute_ras.plan_df
|
226
|
-
print("Retrieved plan entries successfully.")
|
227
|
-
except Exception as e:
|
228
|
-
print(f"Error retrieving plan entries: {str(e)}")
|
229
|
-
return
|
230
|
-
|
231
|
-
# Filter plans if plan_numbers is provided
|
232
|
-
if plan_numbers:
|
233
|
-
ras_compute_plan_entries = ras_compute_plan_entries[
|
234
|
-
ras_compute_plan_entries['plan_number'].isin(plan_numbers)
|
235
|
-
]
|
236
|
-
print(f"Filtered plans to execute: {plan_numbers}")
|
237
|
-
|
238
|
-
# Optimize by iterating once to clear geompre files and set max cores
|
239
|
-
if clear_geompre or max_cores is not None:
|
240
|
-
print("Processing geometry preprocessor files and core settings...")
|
241
|
-
for plan_file in ras_compute_plan_entries['full_path']:
|
242
|
-
if clear_geompre:
|
243
|
-
try:
|
244
|
-
RasGeo.clear_geompre_files(plan_file)
|
245
|
-
print(f"Cleared geometry preprocessor files for {plan_file}")
|
246
|
-
except Exception as e:
|
247
|
-
print(f"Error clearing geometry preprocessor files for {plan_file}: {str(e)}")
|
248
|
-
if max_cores is not None:
|
249
|
-
try:
|
250
|
-
RasPlan.set_num_cores(plan_file, num_cores=max_cores)
|
251
|
-
print(f"Set max cores to {max_cores} for {plan_file}")
|
252
|
-
except Exception as e:
|
253
|
-
print(f"Error setting max cores for {plan_file}: {str(e)}")
|
254
|
-
print("Geometry preprocessor files and core settings processed successfully.")
|
255
|
-
|
256
|
-
# Run plans sequentially
|
257
|
-
print("Running selected plans sequentially...")
|
258
|
-
for _, plan in ras_compute_plan_entries.iterrows():
|
259
|
-
plan_number = plan["plan_number"]
|
260
|
-
start_time = time.time()
|
261
|
-
try:
|
262
|
-
RasCommander.compute_plan(plan_number, ras_object=compute_ras)
|
263
|
-
except Exception as e:
|
264
|
-
print(f"Error computing plan {plan_number}: {str(e)}")
|
265
|
-
end_time = time.time()
|
266
|
-
run_time = end_time - start_time
|
267
|
-
print(f"Total run time for plan {plan_number}: {run_time:.2f} seconds")
|
268
|
-
|
269
|
-
print("All selected plans have been executed.")
|
270
|
-
print("compute_test_mode completed.")
|
271
|
-
|
272
|
-
ras_obj = ras_object or ras
|
273
|
-
ras_obj.plan_df = ras_obj.get_plan_entries()
|
274
|
-
ras_obj.geom_df = ras_obj.get_geom_entries()
|
275
|
-
ras_obj.flow_df = ras_obj.get_flow_entries()
|
276
|
-
ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
|
277
143
|
|
278
144
|
@staticmethod
|
279
145
|
def compute_parallel(
|
280
|
-
|
146
|
+
plan_number: str | list[str] | None = None,
|
281
147
|
max_workers: int = 2,
|
282
|
-
|
148
|
+
num_cores: int = 2,
|
149
|
+
clear_geompre: bool = False,
|
283
150
|
ras_object: RasPrj | None = None,
|
284
|
-
dest_folder: str | Path | None = None
|
151
|
+
dest_folder: str | Path | None = None,
|
152
|
+
overwrite_dest: bool = False
|
285
153
|
) -> dict[str, bool]:
|
286
154
|
"""
|
287
155
|
Execute HEC-RAS plans in parallel using multiple worker threads.
|
@@ -290,21 +158,20 @@ class RasCommander:
|
|
290
158
|
in parallel. It allows for isolated and concurrent execution of multiple plans.
|
291
159
|
|
292
160
|
Args:
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
cores_per_run (int, optional): Number of cores to use for each plan execution.
|
298
|
-
Default is 2.
|
161
|
+
plan_number (str | list[str] | None): Plan number, list of plan numbers, or None to execute all plans.
|
162
|
+
max_workers (int, optional): Maximum number of worker threads to use. Default is 2.
|
163
|
+
num_cores (int, optional): Number of cores to use for each plan execution. Default is 2.
|
164
|
+
clear_geompre (bool, optional): Whether to clear geometry preprocessor files. Defaults to False.
|
299
165
|
ras_object (RasPrj, optional): Specific RAS object to use. If None, uses the global ras instance.
|
300
166
|
dest_folder (str | Path, optional): Destination folder for the final computed results.
|
301
167
|
If None, results will be stored in a "[Computed]" folder next to the original project.
|
168
|
+
overwrite_dest (bool, optional): If True, overwrite the destination folder if it exists. Defaults to False.
|
302
169
|
|
303
170
|
Returns:
|
304
171
|
dict[str, bool]: A dictionary with plan numbers as keys and boolean values indicating success (True) or failure (False).
|
305
172
|
|
306
173
|
Raises:
|
307
|
-
ValueError: If the destination folder exists and is not empty.
|
174
|
+
ValueError: If the destination folder exists and is not empty, and overwrite_dest is False.
|
308
175
|
FileNotFoundError: If a plan file is not found.
|
309
176
|
|
310
177
|
Notes:
|
@@ -314,10 +181,9 @@ class RasCommander:
|
|
314
181
|
- The function automatically handles cleanup and consolidation of results after execution.
|
315
182
|
|
316
183
|
Revision Notes:
|
317
|
-
-
|
318
|
-
-
|
319
|
-
-
|
320
|
-
- Removed debug print statements for cleaner execution logs.
|
184
|
+
- Added support for clear_geompre flag as a pass-through to compute_plan.
|
185
|
+
- Simplified worker thread logic by removing redundant operations.
|
186
|
+
- Removed duplicate RAS object initialization in worker threads.
|
321
187
|
"""
|
322
188
|
ras_obj = ras_object or ras
|
323
189
|
ras_obj.check_initialized()
|
@@ -327,65 +193,42 @@ class RasCommander:
|
|
327
193
|
if dest_folder is not None:
|
328
194
|
dest_folder_path = Path(dest_folder)
|
329
195
|
if dest_folder_path.exists():
|
330
|
-
if
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
else:
|
343
|
-
try:
|
344
|
-
dest_folder_path.mkdir(parents=True, exist_ok=True)
|
345
|
-
except PermissionError:
|
346
|
-
raise PermissionError(f"Unable to create destination folder '{dest_folder_path}'. Permission denied.")
|
347
|
-
try:
|
348
|
-
shutil.copytree(project_folder, dest_folder_path, dirs_exist_ok=True)
|
349
|
-
except shutil.Error as e:
|
350
|
-
raise IOError(f"Error copying project to destination folder: {str(e)}")
|
351
|
-
project_folder = dest_folder_path # Update project_folder to the new destination
|
352
|
-
|
353
|
-
if plan_numbers:
|
354
|
-
if isinstance(plan_numbers, str):
|
355
|
-
plan_numbers = [plan_numbers]
|
356
|
-
ras_obj.plan_df = ras_obj.plan_df[ras_obj.plan_df['plan_number'].isin(plan_numbers)]
|
196
|
+
if overwrite_dest:
|
197
|
+
shutil.rmtree(dest_folder_path)
|
198
|
+
elif any(dest_folder_path.iterdir()):
|
199
|
+
raise ValueError(f"Destination folder '{dest_folder_path}' exists and is not empty. Use overwrite_dest=True to overwrite.")
|
200
|
+
dest_folder_path.mkdir(parents=True, exist_ok=True)
|
201
|
+
shutil.copytree(project_folder, dest_folder_path, dirs_exist_ok=True)
|
202
|
+
project_folder = dest_folder_path
|
203
|
+
|
204
|
+
if plan_number:
|
205
|
+
if isinstance(plan_number, str):
|
206
|
+
plan_number = [plan_number]
|
207
|
+
ras_obj.plan_df = ras_obj.plan_df[ras_obj.plan_df['plan_number'].isin(plan_number)]
|
357
208
|
|
358
209
|
num_plans = len(ras_obj.plan_df)
|
359
210
|
max_workers = min(max_workers, num_plans) if num_plans > 0 else 1
|
360
211
|
print(f"Adjusted max_workers to {max_workers} based on the number of plans: {num_plans}")
|
361
212
|
|
362
|
-
# Clean up existing worker folders
|
213
|
+
# Clean up existing worker folders and create new ones
|
214
|
+
worker_ras_objects = {}
|
363
215
|
for worker_id in range(1, max_workers + 1):
|
364
216
|
worker_folder = project_folder.parent / f"{project_folder.name} [Worker {worker_id}]"
|
365
217
|
if worker_folder.exists():
|
366
218
|
shutil.rmtree(worker_folder)
|
367
|
-
print(f"Removed existing worker folder: {worker_folder}")
|
368
|
-
|
369
|
-
# Create worker folders and initialize RAS objects
|
370
|
-
worker_ras_objects = {}
|
371
|
-
for worker_id in range(1, max_workers + 1):
|
372
|
-
worker_folder = project_folder.parent / f"{project_folder.name} [Worker {worker_id}]"
|
373
219
|
shutil.copytree(project_folder, worker_folder)
|
374
220
|
|
375
|
-
worker_ras_instance = RasPrj()
|
376
221
|
worker_ras_instance = init_ras_project(
|
377
222
|
ras_project_folder=worker_folder,
|
378
223
|
ras_version=ras_obj.ras_exe_path,
|
379
|
-
ras_instance=
|
224
|
+
ras_instance=RasPrj()
|
380
225
|
)
|
381
226
|
worker_ras_objects[worker_id] = worker_ras_instance
|
382
227
|
|
383
|
-
# Prepare plan queue with plan numbers
|
384
228
|
plan_queue = queue.Queue()
|
385
229
|
for plan_number in ras_obj.plan_df['plan_number']:
|
386
230
|
plan_queue.put(plan_number)
|
387
231
|
|
388
|
-
# Initialize results dictionary and thread locks
|
389
232
|
execution_results: dict[str, bool] = {}
|
390
233
|
results_lock = Lock()
|
391
234
|
queue_lock = Lock()
|
@@ -399,16 +242,13 @@ class RasCommander:
|
|
399
242
|
plan_number = plan_queue.get()
|
400
243
|
|
401
244
|
try:
|
402
|
-
plan_path = RasPlan.get_plan_path(plan_number, ras_object=worker_ras_obj)
|
403
|
-
if not plan_path:
|
404
|
-
raise FileNotFoundError(f"Plan file not found: {plan_number}")
|
405
|
-
|
406
|
-
RasPlan.set_num_cores(plan_number, cores_per_run, ras_object=worker_ras_obj)
|
407
|
-
|
408
245
|
print(f"Worker {worker_id} executing plan {plan_number}")
|
409
|
-
|
410
|
-
|
411
|
-
|
246
|
+
success = RasCmdr.compute_plan(
|
247
|
+
plan_number,
|
248
|
+
ras_object=worker_ras_obj,
|
249
|
+
clear_geompre=clear_geompre,
|
250
|
+
num_cores=num_cores
|
251
|
+
)
|
412
252
|
with results_lock:
|
413
253
|
execution_results[plan_number] = success
|
414
254
|
print(f"Completed: Plan {plan_number} in worker {worker_id}")
|
@@ -418,30 +258,20 @@ class RasCommander:
|
|
418
258
|
print(f"Failed: Plan {plan_number} in worker {worker_id}. Error: {str(e)}")
|
419
259
|
|
420
260
|
# Start worker threads
|
421
|
-
worker_threads = []
|
422
|
-
for
|
423
|
-
worker_ras_instance = worker_ras_objects[worker_id]
|
424
|
-
worker_ras_instance.plan_df = worker_ras_instance.get_plan_entries()
|
425
|
-
worker_ras_instance.geom_df = worker_ras_instance.get_geom_entries()
|
426
|
-
worker_ras_instance.flow_df = worker_ras_instance.get_flow_entries()
|
427
|
-
worker_ras_instance.unsteady_df = worker_ras_instance.get_unsteady_entries()
|
428
|
-
|
429
|
-
thread = Thread(target=worker_thread, args=(worker_id,))
|
261
|
+
worker_threads = [Thread(target=worker_thread, args=(worker_id,)) for worker_id in range(1, max_workers + 1)]
|
262
|
+
for thread in worker_threads:
|
430
263
|
thread.start()
|
431
|
-
worker_threads.append(thread)
|
432
|
-
print(f"Started worker thread {worker_id}")
|
433
264
|
|
434
265
|
# Wait for all threads to complete
|
435
|
-
for
|
266
|
+
for thread in worker_threads:
|
436
267
|
thread.join()
|
437
|
-
print(f"Worker thread {worker_id} has completed.")
|
438
268
|
|
439
269
|
# Consolidate results
|
440
270
|
final_dest_folder = dest_folder_path if dest_folder is not None else project_folder.parent / f"{project_folder.name} [Computed]"
|
441
271
|
final_dest_folder.mkdir(exist_ok=True)
|
442
272
|
print(f"Final destination for computed results: {final_dest_folder}")
|
443
273
|
|
444
|
-
for
|
274
|
+
for worker_ras in worker_ras_objects.values():
|
445
275
|
worker_folder = worker_ras.project_folder
|
446
276
|
try:
|
447
277
|
for item in worker_folder.iterdir():
|
@@ -453,7 +283,6 @@ class RasCommander:
|
|
453
283
|
dest_path.unlink()
|
454
284
|
shutil.move(str(item), final_dest_folder)
|
455
285
|
shutil.rmtree(worker_folder)
|
456
|
-
print(f"Moved results and removed worker folder: {worker_folder}")
|
457
286
|
except Exception as e:
|
458
287
|
print(f"Error moving results from {worker_folder} to {final_dest_folder}: {str(e)}")
|
459
288
|
|
@@ -462,4 +291,159 @@ class RasCommander:
|
|
462
291
|
for plan_number, success in execution_results.items():
|
463
292
|
print(f"Plan {plan_number}: {'Successful' if success else 'Failed'}")
|
464
293
|
|
465
|
-
return execution_results
|
294
|
+
return execution_results
|
295
|
+
|
296
|
+
|
297
|
+
|
298
|
+
@staticmethod
|
299
|
+
def compute_test_mode(
|
300
|
+
plan_number=None,
|
301
|
+
dest_folder_suffix="[Test]",
|
302
|
+
clear_geompre=False,
|
303
|
+
num_cores=None,
|
304
|
+
ras_object=None,
|
305
|
+
overwrite_dest=False
|
306
|
+
):
|
307
|
+
"""
|
308
|
+
Execute HEC-RAS plans in test mode. This is a re-creation of the HEC-RAS command line -test flag,
|
309
|
+
which does not work in recent versions of HEC-RAS.
|
310
|
+
|
311
|
+
As a special-purpose function that emulates the original -test flag, it operates differently than the
|
312
|
+
other two compute_ functions. Per the original HEC-RAS test flag, it creates a separate test folder,
|
313
|
+
copies the project there, and executes the specified plans in sequential order.
|
314
|
+
|
315
|
+
For most purposes, just copying a the project folder, initing that new folder, then running each plan
|
316
|
+
with compute_plan is a simpler and more flexible approach. This is shown in the examples provided
|
317
|
+
in the ras-commander library.
|
318
|
+
|
319
|
+
Args:
|
320
|
+
plan_number (str, list[str], optional): Plan number or list of plan numbers to execute.
|
321
|
+
If None, all plans will be executed. Default is None.
|
322
|
+
dest_folder_suffix (str, optional): Suffix to append to the test folder name to create dest_folder.
|
323
|
+
Defaults to "[Test]".
|
324
|
+
dest_folder is always created in the project folder's parent directory.
|
325
|
+
clear_geompre (bool, optional): Whether to clear geometry preprocessor files.
|
326
|
+
Defaults to False.
|
327
|
+
num_cores (int, optional): Maximum number of cores to use for each plan.
|
328
|
+
If None, the current setting is not changed. Default is None.
|
329
|
+
ras_object (RasPrj, optional): Specific RAS object to use. If None, uses the global ras instance.
|
330
|
+
overwrite_dest (bool, optional): If True, overwrite the destination folder if it exists. Defaults to False.
|
331
|
+
|
332
|
+
Returns:
|
333
|
+
None
|
334
|
+
|
335
|
+
Example:
|
336
|
+
Run all plans: RasCommander.compute_test_mode()
|
337
|
+
Run a specific plan: RasCommander.compute_test_mode(plan_number="01")
|
338
|
+
Run multiple plans: RasCommander.compute_test_mode(plan_number=["01", "03", "05"])
|
339
|
+
Run plans with a custom folder suffix: RasCommander.compute_test_mode(dest_folder_suffix="[TestRun]")
|
340
|
+
Run plans and clear geometry preprocessor files: RasCommander.compute_test_mode(clear_geompre=True)
|
341
|
+
Run plans with a specific number of cores: RasCommander.compute_test_mode(num_cores=4)
|
342
|
+
|
343
|
+
Notes:
|
344
|
+
- This function executes plans in a separate folder for isolated testing.
|
345
|
+
- If plan_number is not provided, all plans in the project will be executed.
|
346
|
+
- The function does not change the geometry preprocessor and IB tables settings.
|
347
|
+
- To force recomputing of geometry preprocessor and IB tables, use the clear_geompre=True option.
|
348
|
+
- Plans are executed sequentially.
|
349
|
+
- Because copying the project is implicit, only a dest_folder_suffix option is provided.
|
350
|
+
- For more flexible run management, use the compute_parallel or compute_sequential functions.
|
351
|
+
"""
|
352
|
+
|
353
|
+
# This line of code is used to initialize the RasPrj object with the default "ras" object if no specific object is provided.
|
354
|
+
ras_obj = ras_object or ras
|
355
|
+
# This line of code is used to check if the RasPrj object is initialized.
|
356
|
+
ras_obj.check_initialized()
|
357
|
+
|
358
|
+
print("Starting the compute_test_mode...")
|
359
|
+
|
360
|
+
# Use the project folder from the ras object
|
361
|
+
project_folder = ras_obj.project_folder
|
362
|
+
|
363
|
+
# Check if the project folder exists
|
364
|
+
if not project_folder.exists():
|
365
|
+
print(f"Error: Project folder '{project_folder}' does not exist.")
|
366
|
+
return
|
367
|
+
|
368
|
+
# Create test folder with the specified suffix in the same directory as the project folder
|
369
|
+
compute_folder = project_folder.parent / f"{project_folder.name} {dest_folder_suffix}"
|
370
|
+
print(f"Creating the test folder: {compute_folder}...")
|
371
|
+
|
372
|
+
# Check if the compute folder exists and is empty
|
373
|
+
if compute_folder.exists():
|
374
|
+
if overwrite_dest:
|
375
|
+
shutil.rmtree(compute_folder)
|
376
|
+
elif any(compute_folder.iterdir()):
|
377
|
+
raise ValueError(
|
378
|
+
f"Compute folder '{compute_folder}' exists and is not empty. "
|
379
|
+
"Use overwrite_dest=True to overwrite."
|
380
|
+
)
|
381
|
+
else:
|
382
|
+
try:
|
383
|
+
shutil.copytree(project_folder, compute_folder)
|
384
|
+
except FileNotFoundError:
|
385
|
+
print(f"Error: Unable to copy project folder. Source folder '{project_folder}' not found.")
|
386
|
+
return
|
387
|
+
except PermissionError:
|
388
|
+
print(f"Error: Permission denied when trying to create or copy to '{compute_folder}'.")
|
389
|
+
return
|
390
|
+
except Exception as e:
|
391
|
+
print(f"Error occurred while copying project folder: {str(e)}")
|
392
|
+
return
|
393
|
+
|
394
|
+
# Initialize a new RAS project in the compute folder
|
395
|
+
try:
|
396
|
+
compute_ras = RasPrj()
|
397
|
+
compute_ras.initialize(compute_folder, ras_obj.ras_exe_path)
|
398
|
+
compute_prj_path = compute_ras.prj_file
|
399
|
+
except Exception as e:
|
400
|
+
print(f"Error initializing RAS project in compute folder: {str(e)}")
|
401
|
+
return
|
402
|
+
|
403
|
+
if not compute_prj_path:
|
404
|
+
print("Project file not found.")
|
405
|
+
return
|
406
|
+
|
407
|
+
|
408
|
+
# Get plan entries
|
409
|
+
print("Getting plan entries...")
|
410
|
+
try:
|
411
|
+
ras_compute_plan_entries = compute_ras.plan_df
|
412
|
+
print("Retrieved plan entries successfully.")
|
413
|
+
except Exception as e:
|
414
|
+
print(f"Error retrieving plan entries: {str(e)}")
|
415
|
+
return
|
416
|
+
|
417
|
+
if plan_number:
|
418
|
+
if isinstance(plan_number, str):
|
419
|
+
plan_number = [plan_number]
|
420
|
+
ras_compute_plan_entries = ras_compute_plan_entries[
|
421
|
+
ras_compute_plan_entries['plan_number'].isin(plan_number)
|
422
|
+
]
|
423
|
+
print(f"Filtered plans to execute: {plan_number}")
|
424
|
+
|
425
|
+
print("Running selected plans sequentially...")
|
426
|
+
for _, plan in ras_compute_plan_entries.iterrows():
|
427
|
+
plan_number = plan["plan_number"]
|
428
|
+
start_time = time.time()
|
429
|
+
try:
|
430
|
+
RasCommander.compute_plan(
|
431
|
+
plan_number,
|
432
|
+
ras_object=compute_ras,
|
433
|
+
clear_geompre=clear_geompre,
|
434
|
+
num_cores=num_cores
|
435
|
+
)
|
436
|
+
except Exception as e:
|
437
|
+
print(f"Error computing plan {plan_number}: {str(e)}")
|
438
|
+
end_time = time.time()
|
439
|
+
run_time = end_time - start_time
|
440
|
+
print(f"Total run time for plan {plan_number}: {run_time:.2f} seconds")
|
441
|
+
|
442
|
+
print("All selected plans have been executed.")
|
443
|
+
print("compute_test_mode completed.")
|
444
|
+
|
445
|
+
ras_obj = ras_object or ras
|
446
|
+
ras_obj.plan_df = ras_obj.get_plan_entries()
|
447
|
+
ras_obj.geom_df = ras_obj.get_geom_entries()
|
448
|
+
ras_obj.flow_df = ras_obj.get_flow_entries()
|
449
|
+
ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
|
ras_commander/RasExamples.py
CHANGED
@@ -10,13 +10,13 @@ from datetime import datetime
|
|
10
10
|
|
11
11
|
class RasExamples:
|
12
12
|
"""
|
13
|
-
A class for quickly loading HEC-RAS example projects for testing and development of
|
13
|
+
A class for quickly loading HEC-RAS example projects for testing and development of ras-commander.
|
14
14
|
|
15
15
|
This class provides functionality to download, extract, and manage HEC-RAS example projects.
|
16
16
|
It supports both default HEC-RAS example projects and custom projects from user-provided URLs.
|
17
17
|
|
18
18
|
Expected folder structure: Notes:
|
19
|
-
|
19
|
+
ras-commander/
|
20
20
|
├── examples/ # This is examples_dir
|
21
21
|
│ ├── example_projects/ # This is projects_dir
|
22
22
|
│ │ ├── Balde Eagle Creek/ # Individual Projects from Zip file
|
@@ -24,9 +24,9 @@ class RasExamples:
|
|
24
24
|
│ │ └── ...
|
25
25
|
│ ├── Example_Projects_6_5.zip # HEC-RAS Example Projects zip file will be downloaded here
|
26
26
|
│ ├── example_projects.csv # CSV file containing cached project metadata
|
27
|
-
│ └── 01_project_initialization.py #
|
27
|
+
│ └── 01_project_initialization.py # ras-commander library examples are also at this level
|
28
28
|
│ └── ...
|
29
|
-
└── ras_commander/ # Code for the
|
29
|
+
└── ras_commander/ # Code for the ras-commander library
|
30
30
|
|
31
31
|
Attributes:
|
32
32
|
base_url (str): Base URL for downloading HEC-RAS example projects.
|
ras_commander/RasPrj.py
CHANGED
@@ -10,7 +10,7 @@ Functions:
|
|
10
10
|
get_ras_exe: Determine the HEC-RAS executable path based on the input.
|
11
11
|
|
12
12
|
DEVELOPER NOTE:
|
13
|
-
This class is used to initialize a RAS project and is used in conjunction with the
|
13
|
+
This class is used to initialize a RAS project and is used in conjunction with the RasCmdr class to manage the execution of RAS plans.
|
14
14
|
By default, the RasPrj class is initialized with the global 'ras' object.
|
15
15
|
However, you can create multiple RasPrj instances to manage multiple projects.
|
16
16
|
Do not mix and match global 'ras' object instances and custom instances of RasPrj - it will cause errors.
|
ras_commander/RasUnsteady.py
CHANGED
@@ -10,8 +10,6 @@ class RasUnsteady:
|
|
10
10
|
Class for all operations related to HEC-RAS unsteady flow files.
|
11
11
|
"""
|
12
12
|
|
13
|
-
|
14
|
-
|
15
13
|
@staticmethod
|
16
14
|
def update_unsteady_parameters(unsteady_file, modifications, ras_object=None):
|
17
15
|
"""
|
@@ -27,6 +25,18 @@ class RasUnsteady:
|
|
27
25
|
|
28
26
|
Note:
|
29
27
|
This function updates the ras object's unsteady dataframe after modifying the unsteady flow file.
|
28
|
+
|
29
|
+
Example:
|
30
|
+
from ras_commander import RasCmdr
|
31
|
+
|
32
|
+
# Initialize RAS project
|
33
|
+
ras_cmdr = RasCmdr()
|
34
|
+
ras_cmdr.init_ras_project(project_folder, ras_version)
|
35
|
+
|
36
|
+
# Update unsteady parameters
|
37
|
+
unsteady_file = r"path/to/unsteady_file.u01"
|
38
|
+
modifications = {"Parameter1": "NewValue1", "Parameter2": "NewValue2"}
|
39
|
+
RasUnsteady.update_unsteady_parameters(unsteady_file, modifications, ras_object=ras_cmdr.ras)
|
30
40
|
"""
|
31
41
|
ras_obj = ras_object or ras
|
32
42
|
ras_obj.check_initialized()
|
@@ -60,5 +70,4 @@ class RasUnsteady:
|
|
60
70
|
else:
|
61
71
|
print(f"No matching parameters found in {unsteady_file}")
|
62
72
|
|
63
|
-
ras_obj = ras_object or ras
|
64
73
|
ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
|
ras_commander/RasUtils.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
"""
|
2
|
-
Utility functions for the
|
2
|
+
Utility functions for the ras-commander library.
|
3
3
|
"""
|
4
4
|
import os
|
5
5
|
import shutil
|
@@ -11,7 +11,7 @@ from typing import Union
|
|
11
11
|
|
12
12
|
class RasUtils:
|
13
13
|
"""
|
14
|
-
A class containing utility functions for the
|
14
|
+
A class containing utility functions for the ras-commander library.
|
15
15
|
When integrating new functions that do not clearly fit into other classes, add them here.
|
16
16
|
"""
|
17
17
|
|
@@ -258,8 +258,8 @@ class RasUtils:
|
|
258
258
|
FileNotFoundError: If the plan file doesn't exist
|
259
259
|
|
260
260
|
Example:
|
261
|
-
>>>
|
262
|
-
>>>
|
261
|
+
>>> RasUtils.update_plan_file(1, "Geom", 2)
|
262
|
+
>>> RasUtils.update_plan_file("path/to/plan.p01", "Geom", 2)
|
263
263
|
"""
|
264
264
|
ras_obj = ras_object or ras
|
265
265
|
ras_obj.check_initialized()
|
ras_commander/__init__.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
from importlib.metadata import version, PackageNotFoundError
|
2
2
|
|
3
3
|
try:
|
4
|
-
__version__ = version("
|
4
|
+
__version__ = version("ras-commander")
|
5
5
|
except PackageNotFoundError:
|
6
6
|
# package is not installed
|
7
7
|
__version__ = "unknown"
|
@@ -12,19 +12,19 @@ from .RasPrj import RasPrj
|
|
12
12
|
from .RasPlan import RasPlan
|
13
13
|
from .RasGeo import RasGeo
|
14
14
|
from .RasUnsteady import RasUnsteady
|
15
|
-
from .
|
15
|
+
from .RasCmdr import RasCmdr
|
16
16
|
from .RasUtils import RasUtils
|
17
17
|
from .RasExamples import RasExamples
|
18
18
|
|
19
19
|
# Import all attributes from these modules
|
20
20
|
from .RasPrj import *
|
21
|
-
from .RasPrj import *
|
22
21
|
from .RasPlan import *
|
23
22
|
from .RasGeo import *
|
24
23
|
from .RasUnsteady import *
|
25
|
-
from .
|
24
|
+
from .RasCmdr import *
|
26
25
|
from .RasUtils import *
|
27
26
|
from .RasExamples import *
|
27
|
+
|
28
28
|
# Define __all__ to specify what should be imported when using "from ras_commander import *"
|
29
29
|
__all__ = [
|
30
30
|
"ras",
|
@@ -34,7 +34,7 @@ __all__ = [
|
|
34
34
|
"RasPlan",
|
35
35
|
"RasGeo",
|
36
36
|
"RasUnsteady",
|
37
|
-
"
|
37
|
+
"RasCmdr",
|
38
38
|
"RasUtils",
|
39
39
|
"RasExamples"
|
40
|
-
]
|
40
|
+
]
|
ras_commander/_version.py
CHANGED
@@ -12,5 +12,5 @@ __version__: str
|
|
12
12
|
__version_tuple__: VERSION_TUPLE
|
13
13
|
version_tuple: VERSION_TUPLE
|
14
14
|
|
15
|
-
__version__ = version = '0.
|
16
|
-
__version_tuple__ = version_tuple = (0,
|
15
|
+
__version__ = version = '0.24.0.dev0'
|
16
|
+
__version_tuple__ = version_tuple = (0, 24, 0, 'dev0')
|
@@ -1,9 +1,9 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
|
-
Name:
|
3
|
-
Version: 0.
|
2
|
+
Name: ras-commander
|
3
|
+
Version: 0.24.0.dev0
|
4
4
|
Summary: A library for automating HEC-RAS operations using python functions.
|
5
5
|
Author-email: "William Katzenmeyer, P.E., C.F.M." <heccommander@gmail.com>
|
6
|
-
Project-URL: Homepage, https://github.com/
|
6
|
+
Project-URL: Homepage, https://github.com/billk-FM/ras-commander
|
7
7
|
Classifier: Programming Language :: Python :: 3
|
8
8
|
Classifier: License :: OSI Approved :: MIT License
|
9
9
|
Classifier: Operating System :: OS Independent
|
@@ -11,9 +11,15 @@ Requires-Python: >=3.9
|
|
11
11
|
Description-Content-Type: text/markdown
|
12
12
|
License-File: LICENSE
|
13
13
|
|
14
|
-
#
|
14
|
+
# Start of Selection
|
15
|
+
# RAS Commander (ras-commander)
|
15
16
|
|
16
|
-
|
17
|
+
RAS Commander is a Python library for automating HEC-RAS operations, providing a set of tools to interact with HEC-RAS project files, execute simulations, and manage project data. This library is an evolution of the RASCommander 1.0 Python Notebook Application previously released under HEC-Commander tools.
|
18
|
+
|
19
|
+
Contributors:
|
20
|
+
William Katzenmeyer, P.E., C.F.M. - billk@fenstermaker.com
|
21
|
+
Sean Micek, P.E., C.F.M. - smicek@fenstermaker.com
|
22
|
+
Aaron Nichols, P.E., C.F.M. - anichols@fenstermaker.com
|
17
23
|
|
18
24
|
## Features
|
19
25
|
|
@@ -26,9 +32,10 @@ ras_commander is a Python library for automating HEC-RAS operations, providing a
|
|
26
32
|
|
27
33
|
## Installation
|
28
34
|
|
29
|
-
Install
|
35
|
+
Install ras-commander using pip:
|
30
36
|
|
31
37
|
```bash
|
38
|
+
pip install pandas requests pathlib # Only 3 requirements for ras-commander, needs to be added to the requirements.txt file so pip install works
|
32
39
|
pip install ras-commander
|
33
40
|
```
|
34
41
|
|
@@ -42,19 +49,21 @@ For a full list of dependencies, see the `requirements.txt` file.
|
|
42
49
|
## Quick Start
|
43
50
|
|
44
51
|
```python
|
45
|
-
from ras_commander import init_ras_project,
|
52
|
+
from ras_commander import init_ras_project, RasCmdr, RasPlan
|
46
53
|
|
47
54
|
# Initialize a project
|
48
|
-
init_ras_project("/path/to/project", "6.5")
|
55
|
+
init_ras_project(r"/path/to/project", "6.5")
|
49
56
|
|
50
57
|
# Execute a single plan
|
51
|
-
|
58
|
+
RasCmdr.compute_plan("01", dest_folder=r"/path/to/results", overwrite_dest=True)
|
52
59
|
|
53
60
|
# Execute plans in parallel
|
54
|
-
results =
|
61
|
+
results = RasCmdr.compute_parallel(
|
55
62
|
plan_numbers=["01", "02"],
|
56
63
|
max_workers=2,
|
57
|
-
cores_per_run=2
|
64
|
+
cores_per_run=2,
|
65
|
+
dest_folder=r"/path/to/results",
|
66
|
+
overwrite_dest=True
|
58
67
|
)
|
59
68
|
|
60
69
|
# Modify a plan
|
@@ -64,7 +73,7 @@ RasPlan.set_geom("01", "02")
|
|
64
73
|
## Key Components
|
65
74
|
|
66
75
|
- `RasPrj`: Manages HEC-RAS projects
|
67
|
-
- `
|
76
|
+
- `RasCmdr`: Handles execution of HEC-RAS simulations
|
68
77
|
- `RasPlan`: Provides functions for modifying and updating plan files
|
69
78
|
- `RasGeo`: Handles operations related to geometry files
|
70
79
|
- `RasUnsteady`: Manages unsteady flow file operations
|
@@ -77,27 +86,7 @@ For detailed usage instructions and API documentation, please refer to the [Comp
|
|
77
86
|
|
78
87
|
## Examples
|
79
88
|
|
80
|
-
Check out the `examples/` directory for sample scripts demonstrating various features of
|
81
|
-
|
82
|
-
## Development
|
83
|
-
|
84
|
-
### Setting up the development environment
|
85
|
-
|
86
|
-
1. Clone the repository:
|
87
|
-
```
|
88
|
-
git clone https://github.com/yourusername/ras_commander.git
|
89
|
-
```
|
90
|
-
2. Create a virtual environment and activate it:
|
91
|
-
```
|
92
|
-
python -m venv venv
|
93
|
-
source venv/bin/activate # On Windows, use `venv\Scripts\activate`
|
94
|
-
```
|
95
|
-
3. Install the development dependencies:
|
96
|
-
```
|
97
|
-
pip install -r requirements.txt
|
98
|
-
```
|
99
|
-
Certainly! I'll provide an updated Project Organization Diagram based on the current structure of the ras_commander library. Here's the updated diagram:
|
100
|
-
|
89
|
+
Check out the `examples/` directory for sample scripts demonstrating various features of ras-commander.
|
101
90
|
|
102
91
|
## Project Organization Diagram
|
103
92
|
|
@@ -108,7 +97,7 @@ ras_commander
|
|
108
97
|
│ └── python-package.yml
|
109
98
|
├── ras_commander
|
110
99
|
│ ├── __init__.py
|
111
|
-
│ ├──
|
100
|
+
│ ├── RasCmdr.py
|
112
101
|
│ ├── RasExamples.py
|
113
102
|
│ ├── RasGeo.py
|
114
103
|
│ ├── RasPlan.py
|
@@ -142,10 +131,9 @@ ras_commander
|
|
142
131
|
└── requirements.txt
|
143
132
|
```
|
144
133
|
|
145
|
-
|
146
134
|
## Inclusion of .cursorrules and ai_tools for AI-driven Coding Experience
|
147
135
|
|
148
|
-
Open the ras_commander folder in the Cursor IDE, and it will automatically include the .cursorrules file in your instructions. Additionally, two other provided methods for interacting with the library
|
136
|
+
Open the ras_commander folder in the Cursor IDE, and it will automatically include the .cursorrules file in your instructions. Additionally, two other provided methods for interacting with the library through your current AI subscriptions:
|
149
137
|
|
150
138
|
- ChatGPT: ras_commander GPT Assistant (LINK HERE)
|
151
139
|
- Latest LLM summaries of the code base:
|
@@ -156,63 +144,58 @@ Open the ras_commander folder in the Cursor IDE, and it will automatically inclu
|
|
156
144
|
|
157
145
|
There are a series of scripts provided in the "llm_summaries" folder that provide summaries of the code base, and the docstrings of the functions. They can be run in your local environment, or provided to ChatGPT's code interpreter for execution.
|
158
146
|
|
159
|
-
## RAS-
|
160
|
-
|
161
|
-
The ras_commander GPT assistant has access the entire code base, and can be a helpful tool for understanding the library and its capabilities. However, it is subject to the same context window limitations and file retrieval limtations as I have covered in ADD BLOG LINK HERE. For best results, use the llm summaries above to provide robust context to the model before asking to generate complex workflows.
|
162
|
-
|
163
|
-
## Contributing
|
147
|
+
## RAS-Cmdr GPT Assistant
|
164
148
|
|
165
|
-
|
149
|
+
The RAS Commander GPT assistant has access to the entire code base, and can be a helpful tool for understanding the library and its capabilities. However, it is subject to the same context window limitations and file retrieval limitations as I have covered in ADD BLOG LINK HERE. For best results, use the llm summaries above to provide robust context to the model before asking to generate complex workflows.
|
166
150
|
|
167
|
-
##
|
168
|
-
|
169
|
-
This project follows a specific style guide to maintain consistency across the codebase. Please refer to the [Style Guide](STYLE_GUIDE.md) for details on coding conventions, documentation standards, and best practices.
|
151
|
+
## Current Uses and Roadmap Items
|
170
152
|
|
153
|
+
### Potential Uses (Roadmap Items) of HEC-RAS Automation Functions
|
171
154
|
|
172
|
-
|
173
|
-
|
174
|
-
ras_commander is released under the MIT License. See the license file for details.
|
175
|
-
|
176
|
-
## Acknowledgments
|
177
|
-
|
178
|
-
ras_commander is based on the HEC-Commander project's "Command Line is All You Need" approach, leveraging the HEC-RAS command-line interface for automation. The initial development of this library was presented in the HEC-Commander Tools repository. In a 2024 Australian Water School webinar, Bill demonstrated the derivation of basic HEC-RAS automation functions from plain language instructions. Leveraging the previously developed code and AI tools, the library was created. The primary tools used for this initial development were Anthropic's Claude, GPT-4o, Google's Gemini Experimental models,and the Cursor AI Coding IDE.
|
155
|
+
This set of functions provides a powerful foundation for automating various aspects of HEC-RAS modeling workflows. Here are some potential applications:
|
179
156
|
|
157
|
+
1. **Calibration and Sensitivity Analysis:**
|
158
|
+
- **Automated Parameter Variation:** Users can create multiple simulation scenarios with varying parameters (e.g., Manning's n values, boundary conditions, initial conditions) to calibrate their model against observed data.
|
159
|
+
- **Sensitivity Testing:** Evaluate the impact of different input parameters on model outputs by generating a range of scenarios and analyzing the results. This helps identify critical parameters that require more attention during calibration.
|
180
160
|
|
161
|
+
2. **Real-time Forecasting:**
|
162
|
+
- **Dynamic Model Updates:** Integrate with external data sources (e.g., weather forecasts, streamflow observations) to automatically update boundary conditions and initial conditions in unsteady flow files before running the simulation.
|
163
|
+
- **Ensemble Forecasting:** Generate multiple forecasts by incorporating uncertainty in input data and model parameters. This provides a more comprehensive understanding of potential future flow conditions.
|
181
164
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
William Katzenmeyer, P.E., C.F.M. - billk@fenstermaker.com
|
165
|
+
3. **Scenario Analysis:**
|
166
|
+
- **Land Use Change Impacts:** Evaluate the effects of land use changes on flood risk by modifying Manning's n values using `extract_2d_mannings_tables`, `modify_2d_mannings_table`, and `write_2d_mannings_tables` and running simulations with updated geometry files.
|
167
|
+
- **Climate Change Impacts:** Analyze the potential impacts of projected climate changes on flood risk by adjusting precipitation patterns and other relevant parameters in unsteady flow files.
|
186
168
|
|
169
|
+
4. **Batch Processing and High-Performance Computing:**
|
170
|
+
- **Large-scale Model Runs:** Utilize `run_plans_parallel` to execute multiple simulations concurrently on a multi-core system, significantly reducing processing time for large-scale models or complex scenarios.
|
171
|
+
- **Automated Report Generation:** Integrate with Python libraries like matplotlib and bokeh to automatically generate customized reports summarizing simulation results, including tables, figures, and maps.
|
187
172
|
|
173
|
+
5. **Model Development and Testing:**
|
174
|
+
- **Rapid Prototyping:** Quickly set up and run new model configurations using template files and automated workflows, facilitating rapid model development and testing.
|
175
|
+
- **Regression Testing:** Ensure model integrity and consistency after code changes or updates by automatically running a predefined set of simulations and comparing results with expected outputs.
|
188
176
|
|
177
|
+
6. **User-Friendly Interfaces:**
|
178
|
+
- **GUI Development:** Integrate with Python GUI libraries like Tkinter or PyQt to create user-friendly interfaces for automating HEC-RAS workflows, allowing non-programmers to access the power of automation.
|
189
179
|
|
180
|
+
## Contributing
|
190
181
|
|
182
|
+
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details on how to submit pull requests, report issues, and suggest improvements.
|
191
183
|
|
184
|
+
## Style Guide
|
192
185
|
|
186
|
+
This project follows a specific style guide to maintain consistency across the codebase. Please refer to the [Style Guide](STYLE_GUIDE.md) for details on coding conventions, documentation standards, and best practices.
|
193
187
|
|
188
|
+
## License
|
194
189
|
|
195
|
-
|
190
|
+
ras-commander is released under the MIT License. See the license file for details.
|
196
191
|
|
192
|
+
## Acknowledgments
|
197
193
|
|
198
|
-
|
199
|
-
This set of functions provides a powerful foundation for automating various aspects of HEC-RAS modeling workflows. Here are some potential applications:
|
200
|
-
1. Calibration and Sensitivity Analysis:
|
201
|
-
Automated Parameter Variation: Users can create multiple simulation scenarios with varying parameters (e.g., Manning's n values, boundary conditions, initial conditions) to calibrate their model against observed data.
|
202
|
-
Sensitivity Testing: Evaluate the impact of different input parameters on model outputs by generating a range of scenarios and analyzing the results. This helps identify critical parameters that require more attention during calibration.
|
203
|
-
2. Real-time Forecasting:
|
204
|
-
Dynamic Model Updates: Integrate with external data sources (e.g., weather forecasts, streamflow observations) to automatically update boundary conditions and initial conditions in unsteady flow files before running the simulation.
|
205
|
-
Ensemble Forecasting: Generate multiple forecasts by incorporating uncertainty in input data and model parameters. This provides a more comprehensive understanding of potential future flow conditions.
|
206
|
-
3. Scenario Analysis:
|
207
|
-
Land Use Change Impacts: Evaluate the effects of land use changes on flood risk by modifying Manning's n values using extract_2d_mannings_tables, modify_2d_mannings_table, and write_2d_mannings_tables and running simulations with updated geometry files.
|
208
|
-
Climate Change Impacts: Analyze the potential impacts of projected climate changes on flood risk by adjusting precipitation patterns and other relevant parameters in unsteady flow files.
|
209
|
-
4. Batch Processing and High-Performance Computing:
|
210
|
-
Large-scale Model Runs: Utilize run_plans_parallel to execute multiple simulations concurrently on a multi-core system, significantly reducing processing time for large-scale models or complex scenarios.
|
211
|
-
Automated Report Generation: Integrate with Python libraries like matplotlib and bokeh to automatically generate customized reports summarizing simulation results, including tables, figures, and maps.
|
212
|
-
5. Model Development and Testing:
|
213
|
-
Rapid Prototyping: Quickly set up and run new model configurations using template files and automated workflows, facilitating rapid model development and testing.
|
214
|
-
Regression Testing: Ensure model integrity and consistency after code changes or updates by automatically running a predefined set of simulations and comparing results with expected outputs.
|
215
|
-
6. User-Friendly Interfaces:
|
216
|
-
GUI Development: Integrate with Python GUI libraries like Tkinter or PyQt to create user-friendly interfaces for automating HEC-RAS workflows, allowing non-programmers to access the power of automation.
|
194
|
+
RAS Commander is based on the HEC-Commander project's "Command Line is All You Need" approach, leveraging the HEC-RAS command-line interface for automation. The initial development of this library was presented in the HEC-Commander Tools repository. In a 2024 Australian Water School webinar, Bill demonstrated the derivation of basic HEC-RAS automation functions from plain language instructions. Leveraging the previously developed code and AI tools, the library was created. The primary tools used for this initial development were Anthropic's Claude, GPT-4o, Google's Gemini Experimental models, and the Cursor AI Coding IDE.
|
217
195
|
|
196
|
+
## Contact
|
218
197
|
|
198
|
+
For questions, suggestions, or support, please contact:
|
199
|
+
William Katzenmeyer, P.E., C.F.M. - billk@fenstermaker.com
|
200
|
+
# End of Selection
|
201
|
+
```
|
@@ -0,0 +1,14 @@
|
|
1
|
+
ras_commander/RasCmdr.py,sha256=1_DquIgN-5Ljl6a7Tv5dd4ge96HxHXP_dDGaK_BJNEY,21599
|
2
|
+
ras_commander/RasExamples.py,sha256=yGWvhFGi9zp88d0ba8m76He6BXwUD-3Snzl4EVo3B-w,13548
|
3
|
+
ras_commander/RasGeo.py,sha256=0yNqnjEWh4KcItI9hUPczJ3AhPjld4rFY-W-gNUrnUI,3933
|
4
|
+
ras_commander/RasPlan.py,sha256=FU2o9nagq6UXv04WRY6gG01Ef0wOEsJJdqVqMjZ45O0,51789
|
5
|
+
ras_commander/RasPrj.py,sha256=g_4XorQ3U_cXk9suPwHDzFcHJeD942oFYFSts3L8klw,15158
|
6
|
+
ras_commander/RasUnsteady.py,sha256=_2Sq3ga5ufWQY7pRiUdlGl8NyeakSk8ungHQOf_swSk,2922
|
7
|
+
ras_commander/RasUtils.py,sha256=0parcRgrOKfgU6v-oSiiULQN1K3KbtpbtLLL4Vs0Vok,11744
|
8
|
+
ras_commander/__init__.py,sha256=uY-IIzmE36WvC1IwQR1OSvll0SY7COcb7rhVw6uvuoA,1050
|
9
|
+
ras_commander/_version.py,sha256=ZJJDzsOZCQxqZUvHLC-ADhN-BxwfDGPzKi0nQowJWZI,426
|
10
|
+
ras_commander-0.24.0.dev0.dist-info/LICENSE,sha256=_pbd6qHnlsz1iQ-ozDW_49r86BZT6CRwO2iBtw0iN6M,457
|
11
|
+
ras_commander-0.24.0.dev0.dist-info/METADATA,sha256=Wo9hW6r5e-UFBg2IfLIOE4WiX_PiQe6XDppqEQWEQCo,10008
|
12
|
+
ras_commander-0.24.0.dev0.dist-info/WHEEL,sha256=AHX6tWk3qWuce7vKLrj7lnulVHEdWoltgauo8bgCXgU,109
|
13
|
+
ras_commander-0.24.0.dev0.dist-info/top_level.txt,sha256=i76S7eKLFC8doKcXDl3aiOr9RwT06G8adI6YuKbQDaA,14
|
14
|
+
ras_commander-0.24.0.dev0.dist-info/RECORD,,
|
ras_commander/README.md
DELETED
@@ -1,187 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
# Developer's README
|
3
|
-
|
4
|
-
These notes should be followed by any developer who wants to use this library orcontribute to this project.
|
5
|
-
|
6
|
-
-----
|
7
|
-
|
8
|
-
|
9
|
-
# Developer's README for ras_commander
|
10
|
-
|
11
|
-
## Project Overview
|
12
|
-
|
13
|
-
ras_commander is a Python library for automating HEC-RAS operations. It provides a set of classes and functions to interact with HEC-RAS project files, execute simulations, and manage project data.
|
14
|
-
|
15
|
-
## Project Structure
|
16
|
-
|
17
|
-
The library is organized into several key modules:
|
18
|
-
|
19
|
-
- `RasPrj.py`: Handles project initialization and manages project-level information.
|
20
|
-
- `RasCommander.py`: Manages execution of HEC-RAS simulations.
|
21
|
-
- `RasPlan.py`: Provides functions for modifying and updating plan files.
|
22
|
-
- `RasGeo.py`: Handles operations related to geometry files.
|
23
|
-
- `RasUnsteady.py`: Manages unsteady flow file operations.
|
24
|
-
- `RasUtils.py`: Contains utility functions for file operations and data management.
|
25
|
-
|
26
|
-
## Key Concepts
|
27
|
-
|
28
|
-
### RAS Instance Management
|
29
|
-
|
30
|
-
The library supports both a global `ras` instance and the ability to create multiple instances for different projects:
|
31
|
-
|
32
|
-
- Use the global `ras` instance for simple, single-project scenarios.
|
33
|
-
- Create multiple `RasPrj` instances for working with multiple projects simultaneously.
|
34
|
-
|
35
|
-
### Function Design
|
36
|
-
|
37
|
-
Most functions in the library follow this pattern:
|
38
|
-
|
39
|
-
```python
|
40
|
-
def some_function(param1, param2, ras_object=None):
|
41
|
-
ras_obj = ras_object or ras
|
42
|
-
ras_obj.check_initialized()
|
43
|
-
# Function implementation
|
44
|
-
```
|
45
|
-
|
46
|
-
This design allows for flexibility in using either the global instance or a specific project instance.
|
47
|
-
|
48
|
-
## ras_commander Best Practices
|
49
|
-
|
50
|
-
1. Always check if a project is initialized before performing operations:
|
51
|
-
```python
|
52
|
-
ras_obj.check_initialized()
|
53
|
-
```
|
54
|
-
|
55
|
-
2. Use the `ras_object` parameter in functions to specify which project instance to use.
|
56
|
-
|
57
|
-
3. For complex projects with multiple HEC-RAS folders, prefer passing explicit `ras_object` instances to functions for clarity.
|
58
|
-
|
59
|
-
4. Use type hints and descriptive variable names to improve code readability.
|
60
|
-
|
61
|
-
5. Handle exceptions appropriately, especially for file operations and HEC-RAS interactions.
|
62
|
-
|
63
|
-
6. When adding new functionality, consider its placement within the existing class structure.
|
64
|
-
|
65
|
-
7. Update the `__init__.py` file when adding new modules or significant functionality.
|
66
|
-
|
67
|
-
## Testing
|
68
|
-
|
69
|
-
- Write unit tests for all new functions and methods.
|
70
|
-
- Ensure tests cover both single-project and multi-project scenarios.
|
71
|
-
- Use the `unittest` framework for consistency with existing tests.
|
72
|
-
|
73
|
-
## Documentation
|
74
|
-
|
75
|
-
- Keep docstrings up-to-date with any changes to function signatures or behavior.
|
76
|
-
- Update the main README.md file when adding new features or changing existing functionality.
|
77
|
-
- Consider adding or updating example scripts in the `examples/` directory for new features.
|
78
|
-
- Build a notebook first! We have AI to help us integrate functions into the library once we have a working example.
|
79
|
-
|
80
|
-
|
81
|
-
## Performance Considerations
|
82
|
-
|
83
|
-
- For parallel execution of plans, refer to the "Benchmarking is All You Need" blog post in the HEC-Commander repository for guidance on optimal core usage.
|
84
|
-
|
85
|
-
## Abbreviations
|
86
|
-
|
87
|
-
Consistently use these abbreviations throughout the codebase:
|
88
|
-
|
89
|
-
- ras: HEC-RAS
|
90
|
-
- prj: Project
|
91
|
-
- geom: Geometry
|
92
|
-
- pre: Preprocessor
|
93
|
-
- geompre: Geometry Preprocessor
|
94
|
-
- num: Number
|
95
|
-
- init: Initialize
|
96
|
-
- XS: Cross Section
|
97
|
-
- DSS: Data Storage System
|
98
|
-
- GIS: Geographic Information System
|
99
|
-
- BC: Boundary Condition
|
100
|
-
- IC: Initial Condition
|
101
|
-
- TW: Tailwater
|
102
|
-
|
103
|
-
## Future Development
|
104
|
-
|
105
|
-
Refer to the "Future Development Roadmap" for planned enhancements and features to be implemented.
|
106
|
-
|
107
|
-
By following these guidelines, we can maintain consistency, readability, and reliability across the ras_commander library.
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
# Understanding and Using RAS Instances in ras_commander
|
131
|
-
|
132
|
-
The `RasPrj` class now supports both a global instance named `ras` and the ability to create multiple instances for different projects.
|
133
|
-
|
134
|
-
Key points about RAS instances:
|
135
|
-
|
136
|
-
1. **Global Instance**: A default global instance named `ras` is still available for backwards compatibility and simple use cases.
|
137
|
-
2. **Multiple Instances**: Users can create and manage multiple `RasPrj` instances for different projects.
|
138
|
-
3. **Flexible Function Calls**: Most functions now accept an optional `ras_object` parameter, allowing use of specific project instances.
|
139
|
-
4. **Consistent State**: Each instance maintains its own project state, ensuring data consistency within each project context.
|
140
|
-
|
141
|
-
## Using RAS Instances
|
142
|
-
|
143
|
-
### Global Instance
|
144
|
-
For simple, single-project scenarios:
|
145
|
-
|
146
|
-
```python
|
147
|
-
from ras_commander import ras, init_ras_project
|
148
|
-
|
149
|
-
# Initialize the global instance
|
150
|
-
init_ras_project("/path/to/project", "6.5")
|
151
|
-
|
152
|
-
# Use the global instance
|
153
|
-
print(f"Working with project: {ras.project_name}")
|
154
|
-
plan_file = ras.get_plan_path("01")
|
155
|
-
```
|
156
|
-
|
157
|
-
### Multiple Instances
|
158
|
-
For working with multiple projects:
|
159
|
-
|
160
|
-
```python
|
161
|
-
from ras_commander import RasPrj, init_ras_project
|
162
|
-
|
163
|
-
# Create and initialize separate instances
|
164
|
-
project1 = init_ras_project("/path/to/project1", "6.5")
|
165
|
-
project2 = init_ras_project("/path/to/project2", "6.5")
|
166
|
-
|
167
|
-
# Use specific instances in function calls
|
168
|
-
RasPlan.set_geom("01", "02", ras_object=project1)
|
169
|
-
RasPlan.set_geom("01", "03", ras_object=project2)
|
170
|
-
```
|
171
|
-
|
172
|
-
### Best Practices
|
173
|
-
1. Always check if a project is initialized before using:
|
174
|
-
```python
|
175
|
-
def my_function(ras_object=None):
|
176
|
-
ras_obj = ras_object or ras
|
177
|
-
ras_obj.check_initialized()
|
178
|
-
# Proceed with operations using ras_obj
|
179
|
-
```
|
180
|
-
|
181
|
-
2. Use the `ras_object` parameter in functions to specify which project instance to use.
|
182
|
-
|
183
|
-
3. For any advance usage with multiple projects, you shouldprefer passing explicit `ras_object` instances to functions for clarity and to avoid unintended use of the global instance.
|
184
|
-
|
185
|
-
By supporting both a global instance and multiple instances, ras_commander provides flexibility for various usage scenarios while maintaining simplicity for basic use cases.
|
186
|
-
|
187
|
-
"""
|
@@ -1,15 +0,0 @@
|
|
1
|
-
ras_commander/README.md,sha256=MNWyvD9-MA3m3_HRZCk4uzrtIjyyYhhFs5CZ9M8ZNEo,5936
|
2
|
-
ras_commander/RasCommander.py,sha256=tpi_RqFLxrV08eFhcVoaf1e_j9539lMpyPKIT0EjHT0,21814
|
3
|
-
ras_commander/RasExamples.py,sha256=qNV1KK42S0TllyQ3wWnahQT2GEW1R-_39XI-fOYQemQ,13548
|
4
|
-
ras_commander/RasGeo.py,sha256=0yNqnjEWh4KcItI9hUPczJ3AhPjld4rFY-W-gNUrnUI,3933
|
5
|
-
ras_commander/RasPlan.py,sha256=FU2o9nagq6UXv04WRY6gG01Ef0wOEsJJdqVqMjZ45O0,51789
|
6
|
-
ras_commander/RasPrj.py,sha256=LhlMR6qP9xvLMiPFuwRhRM5yosyLH2h1EyZbnhKgxE8,15163
|
7
|
-
ras_commander/RasUnsteady.py,sha256=rsJH45WQU_30lytEgKY2Uxb4LYyHjgJ82UhA2rWzHOM,2442
|
8
|
-
ras_commander/RasUtils.py,sha256=aSPkqhbfxomEhNZVL9MAZ_Lf0IF3y5L89tcC5WJAkXs,11732
|
9
|
-
ras_commander/__init__.py,sha256=eoOVbWnLH-e405O6ADkO6qhx45a0n60CFgFl7mj4HYs,1089
|
10
|
-
ras_commander/_version.py,sha256=5dQWIAoxxbbT5u5V3xRGGg12h6-MSdkUEuA2DtK7p_8,426
|
11
|
-
ras_commander-0.23.0.dev0.dist-info/LICENSE,sha256=_pbd6qHnlsz1iQ-ozDW_49r86BZT6CRwO2iBtw0iN6M,457
|
12
|
-
ras_commander-0.23.0.dev0.dist-info/METADATA,sha256=-VO0sQxrngK5og6liXPV3r5x2LI_6uUVWmeRcZpMb3A,10057
|
13
|
-
ras_commander-0.23.0.dev0.dist-info/WHEEL,sha256=AHX6tWk3qWuce7vKLrj7lnulVHEdWoltgauo8bgCXgU,109
|
14
|
-
ras_commander-0.23.0.dev0.dist-info/top_level.txt,sha256=i76S7eKLFC8doKcXDl3aiOr9RwT06G8adI6YuKbQDaA,14
|
15
|
-
ras_commander-0.23.0.dev0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|