ras-commander 0.22.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.
@@ -25,64 +25,64 @@ import queue
25
25
  from threading import Thread, Lock
26
26
  import time
27
27
 
28
-
29
- class RasCommander:
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
- compute_folder=None,
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
- compute_folder (str, Path, optional): Name of the folder or full path for computation.
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 compute_folder already exists and is not empty.
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
- # Determine the compute folder path and plan path
74
- if compute_folder is not None:
75
- compute_folder = Path(ras_obj.project_folder).parent / compute_folder if isinstance(compute_folder, str) else Path(compute_folder)
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
- # Check if the compute folder exists and is empty
78
- if compute_folder.exists() and any(compute_folder.iterdir()):
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(compute_folder, ras_obj.ras_exe_path)
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
- plan_numbers: list[str] | None = None,
146
+ plan_number: str | list[str] | None = None,
281
147
  max_workers: int = 2,
282
- cores_per_run: int = 2,
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
- plan_numbers (list[str], optional): List of plan numbers to execute.
294
- If None, all plans will be executed. Default is None.
295
- max_workers (int, optional): Maximum number of worker threads to use.
296
- Default is 2.
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
- - Removed redundant variable initializations.
318
- - Streamlined worker folder creation and RAS object initialization.
319
- - Optimized the consolidation of results from worker folders.
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 any(dest_folder_path.iterdir()):
331
- raise ValueError(
332
- f"\nError: Destination folder already exists: '{dest_folder_path}'\n"
333
- f"To prevent accidental overwriting of results, this operation cannot proceed.\n"
334
- f"Please take one of the following actions:\n"
335
- f"1. Delete the folder manually and run the operation again.\n"
336
- f"2. Use a different destination folder name.\n"
337
- f"3. Programmatically delete the folder before calling compute_parallel, like this:\n"
338
- f" if Path('{dest_folder_path}').exists():\n"
339
- f" shutil.rmtree('{dest_folder_path}')\n"
340
- f"This safety measure ensures that you don't inadvertently overwrite existing results."
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=worker_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
- success = RasCommander.compute_plan(plan_number, ras_object=worker_ras_obj)
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 worker_id in range(1, max_workers + 1):
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 worker_id, thread in enumerate(worker_threads, 1):
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 worker_id, worker_ras in worker_ras_objects.items():
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()
@@ -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 ras_commander.
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
- ras_commander/
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 # ras_commander library examples are also at this level
27
+ │ └── 01_project_initialization.py # ras-commander library examples are also at this level
28
28
  │ └── ...
29
- └── ras_commander/ # Code for the ras_commander library
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 RasCommander class to manage the execution of RAS plans.
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.
@@ -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 ras_commander library.
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 ras_commander library.
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
- >>> update_plan_entries(1, "Geom", 2)
262
- >>> update_plan_entries("path/to/plan.p01", "Geom", 2)
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("ras_commander")
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 .RasCommander import RasCommander
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 .RasCommander import *
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
- "RasCommander",
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.22.0.dev0'
16
- __version_tuple__ = version_tuple = (0, 22, 0, 'dev0')
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: ras_commander
3
- Version: 0.22.0.dev0
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/yourusername/ras_commander
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
- # RAS-Commander (ras_commander)
14
+ # Start of Selection
15
+ # RAS Commander (ras-commander)
15
16
 
16
- 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 RAS-Commander 1.0 Python Notebook Application previously released under the HEC-Commander tools.
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 ras_commander using pip:
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, RasCommander, RasPlan
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
- RasCommander.compute_plan("01")
58
+ RasCmdr.compute_plan("01", dest_folder=r"/path/to/results", overwrite_dest=True)
52
59
 
53
60
  # Execute plans in parallel
54
- results = RasCommander.compute_parallel(
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
- - `RasCommander`: Handles execution of HEC-RAS simulations
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 ras_commander.
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
- │ ├── RasCommander.py
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 though your current AI subscriptions:
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-Commander GPT Assistant
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
- We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details on how to submit pull requests, report issues, and suggest improvements.
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
- ## Style Guide
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
- ## License
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
- ## Contact
183
-
184
- For questions, suggestions, or support, please contact:
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
- NOTES: INCORPORATE INTO THE README.MD FILE ABOVE UNDER A NEW SECTION FOR CURRENT USES AND ROADMAP ITEMS, THEN DELETE THIS NOTE
190
+ ras-commander is released under the MIT License. See the license file for details.
196
191
 
192
+ ## Acknowledgments
197
193
 
198
- Potential Uses of HEC-RAS Automation Functions
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=Ss5vVtwnG84xro0FVNRmQeuiMm2EPPdx1J-Vny18ryM,426
11
- ras_commander-0.22.0.dev0.dist-info/LICENSE,sha256=_pbd6qHnlsz1iQ-ozDW_49r86BZT6CRwO2iBtw0iN6M,457
12
- ras_commander-0.22.0.dev0.dist-info/METADATA,sha256=8nFhNB_dSh_OY_tt2pkqNMiPQJELc4mifgoMJks4ao4,10057
13
- ras_commander-0.22.0.dev0.dist-info/WHEEL,sha256=AHX6tWk3qWuce7vKLrj7lnulVHEdWoltgauo8bgCXgU,109
14
- ras_commander-0.22.0.dev0.dist-info/top_level.txt,sha256=i76S7eKLFC8doKcXDl3aiOr9RwT06G8adI6YuKbQDaA,14
15
- ras_commander-0.22.0.dev0.dist-info/RECORD,,