ras-commander 0.33.0__py3-none-any.whl → 0.35.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ras_commander/RasCmdr.py CHANGED
@@ -12,18 +12,26 @@ from .RasPrj import ras, RasPrj, init_ras_project, get_ras_exe
12
12
  from .RasPlan import RasPlan
13
13
  from .RasGeo import RasGeo
14
14
  from .RasUtils import RasUtils
15
- import subprocess
16
- import os
17
15
  import logging
18
16
  import time
19
- import pandas as pd
20
- from threading import Thread, Lock
21
17
  import queue
18
+ from threading import Thread, Lock
19
+ from typing import Union, List, Optional, Dict
22
20
  from pathlib import Path
23
21
  import shutil
24
- import queue
25
- from threading import Thread, Lock
26
- import time
22
+ import logging
23
+ from concurrent.futures import ThreadPoolExecutor, as_completed
24
+ from threading import Lock, Thread
25
+ from itertools import cycle
26
+ from ras_commander.RasPrj import RasPrj # Ensure RasPrj is imported
27
+ from threading import Lock, Thread, current_thread
28
+ from concurrent.futures import ThreadPoolExecutor, as_completed
29
+ from itertools import cycle
30
+ from typing import Union, List, Optional, Dict
31
+
32
+
33
+ # Configure logging
34
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
27
35
 
28
36
  # TO DO:
29
37
  # 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.
@@ -66,7 +74,8 @@ class RasCmdr:
66
74
  Raises:
67
75
  ValueError: If the specified dest_folder already exists and is not empty, and overwrite_dest is False.
68
76
  """
69
- ras_obj = ras_object or ras
77
+ ras_obj = ras_object if ras_object is not None else ras
78
+ logging.info(f"Using ras_object with project folder: {ras_obj.project_folder}")
70
79
  ras_obj.check_initialized()
71
80
 
72
81
  if dest_folder is not None:
@@ -75,11 +84,15 @@ class RasCmdr:
75
84
  if dest_folder.exists():
76
85
  if overwrite_dest:
77
86
  shutil.rmtree(dest_folder)
87
+ logging.info(f"Destination folder '{dest_folder}' exists. Overwriting as per overwrite_dest=True.")
78
88
  elif any(dest_folder.iterdir()):
79
- raise ValueError(f"Destination folder '{dest_folder}' exists and is not empty. Use overwrite_dest=True to overwrite.")
89
+ error_msg = f"Destination folder '{dest_folder}' exists and is not empty. Use overwrite_dest=True to overwrite."
90
+ logging.error(error_msg)
91
+ raise ValueError(error_msg)
80
92
 
81
93
  dest_folder.mkdir(parents=True, exist_ok=True)
82
94
  shutil.copytree(ras_obj.project_folder, dest_folder, dirs_exist_ok=True)
95
+ logging.info(f"Copied project folder to destination: {dest_folder}")
83
96
 
84
97
  compute_ras = RasPrj()
85
98
  compute_ras.initialize(dest_folder, ras_obj.ras_exe_path)
@@ -92,29 +105,29 @@ class RasCmdr:
92
105
  compute_plan_path = Path(plan_number) if isinstance(plan_number, (str, Path)) and Path(plan_number).is_file() else RasPlan.get_plan_path(plan_number, compute_ras)
93
106
 
94
107
  if not compute_prj_path or not compute_plan_path:
95
- print(f"Error: Could not find project file or plan file for plan {plan_number}")
108
+ logging.error(f"Could not find project file or plan file for plan {plan_number}")
96
109
  return False
97
110
 
98
111
  # Clear geometry preprocessor files if requested
99
112
  if clear_geompre:
100
113
  try:
101
114
  RasGeo.clear_geompre_files(compute_plan_path, ras_object=compute_ras)
102
- print(f"Cleared geometry preprocessor files for plan: {plan_number}")
115
+ logging.info(f"Cleared geometry preprocessor files for plan: {plan_number}")
103
116
  except Exception as e:
104
- print(f"Error clearing geometry preprocessor files for plan {plan_number}: {str(e)}")
117
+ logging.error(f"Error clearing geometry preprocessor files for plan {plan_number}: {str(e)}")
105
118
 
106
119
  # Set the number of cores if specified
107
120
  if num_cores is not None:
108
121
  try:
109
122
  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}")
123
+ logging.info(f"Set number of cores to {num_cores} for plan: {plan_number}")
111
124
  except Exception as e:
112
- print(f"Error setting number of cores for plan {plan_number}: {str(e)}")
125
+ logging.error(f"Error setting number of cores for plan {plan_number}: {str(e)}")
113
126
 
114
127
  # Prepare the command for HEC-RAS execution
115
128
  cmd = f'"{compute_ras.ras_exe_path}" -c "{compute_prj_path}" "{compute_plan_path}"'
116
- print("Running HEC-RAS from the Command Line:")
117
- print(f"Running command: {cmd}")
129
+ logging.info("Running HEC-RAS from the Command Line:")
130
+ logging.info(f"Running command: {cmd}")
118
131
 
119
132
  # Execute the HEC-RAS command
120
133
  start_time = time.time()
@@ -122,93 +135,67 @@ class RasCmdr:
122
135
  subprocess.run(cmd, check=True, shell=True, capture_output=True, text=True)
123
136
  end_time = time.time()
124
137
  run_time = end_time - start_time
125
- print(f"HEC-RAS execution completed for plan: {plan_number}")
126
- print(f"Total run time for plan {plan_number}: {run_time:.2f} seconds")
138
+ logging.info(f"HEC-RAS execution completed for plan: {plan_number}")
139
+ logging.info(f"Total run time for plan {plan_number}: {run_time:.2f} seconds")
127
140
  return True
128
141
  except subprocess.CalledProcessError as e:
129
142
  end_time = time.time()
130
143
  run_time = end_time - start_time
131
- print(f"Error running plan: {plan_number}")
132
- print(f"Error message: {e.output}")
133
- print(f"Total run time for plan {plan_number}: {run_time:.2f} seconds")
144
+ logging.error(f"Error running plan: {plan_number}")
145
+ logging.error(f"Error message: {e.output}")
146
+ logging.info(f"Total run time for plan {plan_number}: {run_time:.2f} seconds")
134
147
  return False
135
-
136
- ras_obj = ras_object or ras
137
- ras_obj.plan_df = ras_obj.get_plan_entries()
138
- ras_obj.geom_df = ras_obj.get_geom_entries()
139
- ras_obj.flow_df = ras_obj.get_flow_entries()
140
- ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
141
-
148
+ finally:
149
+ # Update the RAS object's dataframes
150
+ ras_obj.plan_df = ras_obj.get_plan_entries()
151
+ ras_obj.geom_df = ras_obj.get_geom_entries()
152
+ ras_obj.flow_df = ras_obj.get_flow_entries()
153
+ ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
154
+
142
155
 
143
156
 
144
157
  @staticmethod
145
158
  def compute_parallel(
146
- plan_number: str | list[str] | None = None,
159
+ plan_number: Union[str, List[str], None] = None,
147
160
  max_workers: int = 2,
148
161
  num_cores: int = 2,
149
162
  clear_geompre: bool = False,
150
- ras_object: RasPrj | None = None,
151
- dest_folder: str | Path | None = None,
163
+ ras_object: Optional['RasPrj'] = None, # Type hinting as string to avoid NameError
164
+ dest_folder: Union[str, Path, None] = None,
152
165
  overwrite_dest: bool = False
153
- ) -> dict[str, bool]:
166
+ ) -> Dict[str, bool]:
154
167
  """
155
- Execute HEC-RAS plans in parallel using multiple worker threads.
156
-
157
- This function creates separate worker folders, copies the project to each, and executes the specified plans
158
- in parallel. It allows for isolated and concurrent execution of multiple plans.
159
-
160
- Args:
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.
165
- ras_object (RasPrj, optional): Specific RAS object to use. If None, uses the global ras instance.
166
- dest_folder (str | Path, optional): Destination folder for the final computed results.
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.
169
-
170
- Returns:
171
- dict[str, bool]: A dictionary with plan numbers as keys and boolean values indicating success (True) or failure (False).
172
-
173
- Raises:
174
- ValueError: If the destination folder exists and is not empty, and overwrite_dest is False.
175
- FileNotFoundError: If a plan file is not found.
176
-
177
- Notes:
178
- - This function creates separate folders for each worker to ensure isolated execution.
179
- - Each worker uses its own RAS object to prevent conflicts.
180
- - Plans are distributed among workers using a queue to ensure efficient parallel processing.
181
- - The function automatically handles cleanup and consolidation of results after execution.
182
-
183
- Revision Notes:
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.
168
+ [Docstring remains unchanged]
187
169
  """
188
- ras_obj = ras_object or ras
170
+ ras_obj = ras_object or ras # Assuming 'ras' is a global RasPrj instance
189
171
  ras_obj.check_initialized()
190
172
 
191
- project_folder = ras_obj.project_folder
173
+ project_folder = Path(ras_obj.project_folder)
192
174
 
193
175
  if dest_folder is not None:
194
176
  dest_folder_path = Path(dest_folder)
195
177
  if dest_folder_path.exists():
196
178
  if overwrite_dest:
197
179
  shutil.rmtree(dest_folder_path)
180
+ logging.info(f"Destination folder '{dest_folder_path}' exists. Overwriting as per overwrite_dest=True.")
198
181
  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.")
182
+ error_msg = f"Destination folder '{dest_folder_path}' exists and is not empty. Use overwrite_dest=True to overwrite."
183
+ logging.error(error_msg)
184
+ raise ValueError(error_msg)
200
185
  dest_folder_path.mkdir(parents=True, exist_ok=True)
201
186
  shutil.copytree(project_folder, dest_folder_path, dirs_exist_ok=True)
187
+ logging.info(f"Copied project folder to destination: {dest_folder_path}")
202
188
  project_folder = dest_folder_path
203
189
 
204
190
  if plan_number:
205
191
  if isinstance(plan_number, str):
206
192
  plan_number = [plan_number]
207
193
  ras_obj.plan_df = ras_obj.plan_df[ras_obj.plan_df['plan_number'].isin(plan_number)]
194
+ logging.info(f"Filtered plans to execute: {plan_number}")
208
195
 
209
196
  num_plans = len(ras_obj.plan_df)
210
197
  max_workers = min(max_workers, num_plans) if num_plans > 0 else 1
211
- print(f"Adjusted max_workers to {max_workers} based on the number of plans: {num_plans}")
198
+ logging.info(f"Adjusted max_workers to {max_workers} based on the number of plans: {num_plans}")
212
199
 
213
200
  # Clean up existing worker folders and create new ones
214
201
  worker_ras_objects = {}
@@ -216,85 +203,122 @@ class RasCmdr:
216
203
  worker_folder = project_folder.parent / f"{project_folder.name} [Worker {worker_id}]"
217
204
  if worker_folder.exists():
218
205
  shutil.rmtree(worker_folder)
206
+ logging.info(f"Removed existing worker folder: {worker_folder}")
219
207
  shutil.copytree(project_folder, worker_folder)
220
-
208
+ logging.info(f"Created worker folder: {worker_folder}")
209
+
210
+ # Instantiate RasPrj properly
211
+ ras_instance = RasPrj() # Add necessary parameters if required
221
212
  worker_ras_instance = init_ras_project(
222
213
  ras_project_folder=worker_folder,
223
214
  ras_version=ras_obj.ras_exe_path,
224
- ras_instance=RasPrj()
215
+ ras_instance=ras_instance # Pass the instance instead of a string
225
216
  )
226
217
  worker_ras_objects[worker_id] = worker_ras_instance
227
218
 
228
- plan_queue = queue.Queue()
229
- for plan_number in ras_obj.plan_df['plan_number']:
230
- plan_queue.put(plan_number)
231
-
232
- execution_results: dict[str, bool] = {}
233
- results_lock = Lock()
234
- queue_lock = Lock()
235
-
236
- def worker_thread(worker_id: int):
237
- worker_ras_obj = worker_ras_objects[worker_id]
238
- while True:
239
- with queue_lock:
240
- if plan_queue.empty():
241
- break
242
- plan_number = plan_queue.get()
243
-
219
+ # Distribute plans among workers in a round-robin fashion
220
+ worker_cycle = cycle(range(1, max_workers + 1))
221
+ plan_assignments = [(next(worker_cycle), plan_num) for plan_num in ras_obj.plan_df['plan_number']]
222
+
223
+ # Initialize ThreadPoolExecutor without tracking individual plan success
224
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
225
+ # Submit all plan executions to the executor
226
+ futures = [
227
+ executor.submit(
228
+ RasCmdr.compute_plan,
229
+ plan_num,
230
+ ras_object=worker_ras_objects[worker_id],
231
+ clear_geompre=clear_geompre,
232
+ num_cores=num_cores
233
+ )
234
+ for worker_id, plan_num in plan_assignments
235
+ ]
236
+
237
+ # Optionally, you can log when each plan starts and completes
238
+ for future, (worker_id, plan_num) in zip(as_completed(futures), plan_assignments):
244
239
  try:
245
- print(f"Worker {worker_id} executing plan {plan_number}")
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
- )
252
- with results_lock:
253
- execution_results[plan_number] = success
254
- print(f"Completed: Plan {plan_number} in worker {worker_id}")
240
+ future.result() # We don't need the success flag here
241
+ logging.info(f"Plan {plan_num} executed in worker {worker_id}")
255
242
  except Exception as e:
256
- with results_lock:
257
- execution_results[plan_number] = False
258
- print(f"Failed: Plan {plan_number} in worker {worker_id}. Error: {str(e)}")
259
-
260
- # Start worker threads
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:
263
- thread.start()
264
-
265
- # Wait for all threads to complete
266
- for thread in worker_threads:
267
- thread.join()
243
+ logging.error(f"Plan {plan_num} failed in worker {worker_id}: {str(e)}")
244
+ # Depending on requirements, you might want to handle retries or mark these plans differently
268
245
 
269
246
  # Consolidate results
270
247
  final_dest_folder = dest_folder_path if dest_folder is not None else project_folder.parent / f"{project_folder.name} [Computed]"
271
- final_dest_folder.mkdir(exist_ok=True)
272
- print(f"Final destination for computed results: {final_dest_folder}")
248
+ final_dest_folder.mkdir(parents=True, exist_ok=True)
249
+ logging.info(f"Final destination for computed results: {final_dest_folder}")
273
250
 
274
251
  for worker_ras in worker_ras_objects.values():
275
- worker_folder = worker_ras.project_folder
252
+ worker_folder = Path(worker_ras.project_folder)
276
253
  try:
277
254
  for item in worker_folder.iterdir():
278
255
  dest_path = final_dest_folder / item.name
279
256
  if dest_path.exists():
280
257
  if dest_path.is_dir():
281
258
  shutil.rmtree(dest_path)
259
+ logging.debug(f"Removed existing directory at {dest_path}")
282
260
  else:
283
261
  dest_path.unlink()
262
+ logging.debug(f"Removed existing file at {dest_path}")
284
263
  shutil.move(str(item), final_dest_folder)
264
+ logging.debug(f"Moved {item} to {final_dest_folder}")
285
265
  shutil.rmtree(worker_folder)
266
+ logging.info(f"Removed worker folder: {worker_folder}")
286
267
  except Exception as e:
287
- print(f"Error moving results from {worker_folder} to {final_dest_folder}: {str(e)}")
268
+ logging.error(f"Error moving results from {worker_folder} to {final_dest_folder}: {str(e)}")
269
+
270
+ # Initialize a new RasPrj object for the final destination
271
+ try:
272
+ # Create a new RasPrj instance
273
+ final_dest_folder_ras_obj = RasPrj()
274
+
275
+ # Initialize it using init_ras_project
276
+ final_dest_folder_ras_obj = init_ras_project(
277
+ ras_project_folder=final_dest_folder,
278
+ ras_version=ras_obj.ras_exe_path,
279
+ ras_instance=final_dest_folder_ras_obj
280
+ )
281
+
282
+ # Now we can check if it's initialized
283
+ final_dest_folder_ras_obj.check_initialized()
284
+ except Exception as e:
285
+ logging.error(f"Failed to initialize RasPrj for final destination: {str(e)}")
286
+ raise
287
+
288
+ # Retrieve plan entries and check for HDF results
289
+ try:
290
+ plan_entries = final_dest_folder_ras_obj.get_prj_entries('Plan')
291
+ except Exception as e:
292
+ logging.error(f"Failed to retrieve plan entries from final RasPrj: {str(e)}")
293
+ raise
294
+
295
+ execution_results: Dict[str, bool] = {}
296
+ for _, row in ras_obj.plan_df.iterrows():
297
+ plan_num = row['plan_number']
298
+ # Find the corresponding entry in plan_entries
299
+ entry = plan_entries[plan_entries['plan_number'] == plan_num]
300
+ if not entry.empty:
301
+ hdf_path = entry.iloc[0].get('HDF_Results_Path')
302
+ success = hdf_path is not None and Path(hdf_path).exists()
303
+ else:
304
+ success = False
305
+ execution_results[plan_num] = success
288
306
 
289
307
  # Print execution results for each plan
290
- print("\nExecution Results:")
291
- for plan_number, success in execution_results.items():
292
- print(f"Plan {plan_number}: {'Successful' if success else 'Failed'}")
308
+ logging.info("\nExecution Results:")
309
+ for plan_num, success in execution_results.items():
310
+ status = 'Successful' if success else 'Failed'
311
+ logging.info(f"Plan {plan_num}: {status} \n(HDF_Results_Path: {hdf_path})")
312
+
313
+ ras_obj = ras_object or ras
314
+ ras_obj.plan_df = ras_obj.get_plan_entries()
315
+ ras_obj.geom_df = ras_obj.get_geom_entries()
316
+ ras_obj.flow_df = ras_obj.get_flow_entries()
317
+ ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
293
318
 
294
319
  return execution_results
295
320
 
296
-
297
-
321
+
298
322
  @staticmethod
299
323
  def compute_test_mode(
300
324
  plan_number=None,
@@ -355,40 +379,44 @@ class RasCmdr:
355
379
  # This line of code is used to check if the RasPrj object is initialized.
356
380
  ras_obj.check_initialized()
357
381
 
358
- print("Starting the compute_test_mode...")
382
+ logging.info("Starting the compute_test_mode...")
359
383
 
360
384
  # Use the project folder from the ras object
361
385
  project_folder = ras_obj.project_folder
362
386
 
363
387
  # Check if the project folder exists
364
388
  if not project_folder.exists():
365
- print(f"Error: Project folder '{project_folder}' does not exist.")
389
+ logging.error(f"Project folder '{project_folder}' does not exist.")
366
390
  return
367
391
 
368
392
  # Create test folder with the specified suffix in the same directory as the project folder
369
393
  compute_folder = project_folder.parent / f"{project_folder.name} {dest_folder_suffix}"
370
- print(f"Creating the test folder: {compute_folder}...")
394
+ logging.info(f"Creating the test folder: {compute_folder}...")
371
395
 
372
396
  # Check if the compute folder exists and is empty
373
397
  if compute_folder.exists():
374
398
  if overwrite_dest:
375
399
  shutil.rmtree(compute_folder)
400
+ logging.info(f"Compute folder '{compute_folder}' exists. Overwriting as per overwrite_dest=True.")
376
401
  elif any(compute_folder.iterdir()):
377
- raise ValueError(
402
+ error_msg = (
378
403
  f"Compute folder '{compute_folder}' exists and is not empty. "
379
404
  "Use overwrite_dest=True to overwrite."
380
405
  )
406
+ logging.error(error_msg)
407
+ raise ValueError(error_msg)
381
408
  else:
382
409
  try:
383
410
  shutil.copytree(project_folder, compute_folder)
411
+ logging.info(f"Copied project folder to compute folder: {compute_folder}")
384
412
  except FileNotFoundError:
385
- print(f"Error: Unable to copy project folder. Source folder '{project_folder}' not found.")
413
+ logging.error(f"Unable to copy project folder. Source folder '{project_folder}' not found.")
386
414
  return
387
415
  except PermissionError:
388
- print(f"Error: Permission denied when trying to create or copy to '{compute_folder}'.")
416
+ logging.error(f"Permission denied when trying to create or copy to '{compute_folder}'.")
389
417
  return
390
418
  except Exception as e:
391
- print(f"Error occurred while copying project folder: {str(e)}")
419
+ logging.error(f"Error occurred while copying project folder: {str(e)}")
392
420
  return
393
421
 
394
422
  # Initialize a new RAS project in the compute folder
@@ -396,22 +424,22 @@ class RasCmdr:
396
424
  compute_ras = RasPrj()
397
425
  compute_ras.initialize(compute_folder, ras_obj.ras_exe_path)
398
426
  compute_prj_path = compute_ras.prj_file
427
+ logging.info(f"Initialized RAS project in compute folder: {compute_prj_path}")
399
428
  except Exception as e:
400
- print(f"Error initializing RAS project in compute folder: {str(e)}")
429
+ logging.error(f"Error initializing RAS project in compute folder: {str(e)}")
401
430
  return
402
431
 
403
432
  if not compute_prj_path:
404
- print("Project file not found.")
433
+ logging.error("Project file not found.")
405
434
  return
406
435
 
407
-
408
436
  # Get plan entries
409
- print("Getting plan entries...")
437
+ logging.info("Getting plan entries...")
410
438
  try:
411
439
  ras_compute_plan_entries = compute_ras.plan_df
412
- print("Retrieved plan entries successfully.")
440
+ logging.info("Retrieved plan entries successfully.")
413
441
  except Exception as e:
414
- print(f"Error retrieving plan entries: {str(e)}")
442
+ logging.error(f"Error retrieving plan entries: {str(e)}")
415
443
  return
416
444
 
417
445
  if plan_number:
@@ -420,30 +448,35 @@ class RasCmdr:
420
448
  ras_compute_plan_entries = ras_compute_plan_entries[
421
449
  ras_compute_plan_entries['plan_number'].isin(plan_number)
422
450
  ]
423
- print(f"Filtered plans to execute: {plan_number}")
451
+ logging.info(f"Filtered plans to execute: {plan_number}")
424
452
 
425
- print("Running selected plans sequentially...")
453
+ logging.info("Running selected plans sequentially...")
426
454
  for _, plan in ras_compute_plan_entries.iterrows():
427
455
  plan_number = plan["plan_number"]
428
456
  start_time = time.time()
429
457
  try:
430
- RasCommander.compute_plan(
458
+ success = RasCmdr.compute_plan(
431
459
  plan_number,
432
460
  ras_object=compute_ras,
433
461
  clear_geompre=clear_geompre,
434
462
  num_cores=num_cores
435
463
  )
464
+ if success:
465
+ logging.info(f"Successfully computed plan {plan_number}")
466
+ else:
467
+ logging.error(f"Failed to compute plan {plan_number}")
436
468
  except Exception as e:
437
- print(f"Error computing plan {plan_number}: {str(e)}")
469
+ logging.error(f"Error computing plan {plan_number}: {str(e)}")
438
470
  end_time = time.time()
439
471
  run_time = end_time - start_time
440
- print(f"Total run time for plan {plan_number}: {run_time:.2f} seconds")
472
+ logging.info(f"Total run time for plan {plan_number}: {run_time:.2f} seconds")
441
473
 
442
- print("All selected plans have been executed.")
443
- print("compute_test_mode completed.")
474
+ logging.info("All selected plans have been executed.")
475
+ logging.info("compute_test_mode completed.")
444
476
 
445
477
  ras_obj = ras_object or ras
446
478
  ras_obj.plan_df = ras_obj.get_plan_entries()
447
479
  ras_obj.geom_df = ras_obj.get_geom_entries()
448
480
  ras_obj.flow_df = ras_obj.get_flow_entries()
449
- ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
481
+ ras_obj.unsteady_df = ras_obj.get_unsteady_entries()
482
+