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