ras-commander 0.65.0__py3-none-any.whl → 0.66.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/RasPrj.py CHANGED
@@ -160,101 +160,96 @@ class RasPrj:
160
160
  Load project data from the HEC-RAS project file.
161
161
 
162
162
  This method initializes DataFrames for plan, flow, unsteady, and geometry entries
163
- by calling the _get_prj_entries method for each entry type.
164
- Also extracts unsteady_number and geometry_number from plan files and adds them to plan_df.
165
- Now includes Geom Path, Flow Path, and other required columns.
163
+ and ensures all required columns are present with appropriate paths.
166
164
  """
167
- # Load unsteady first to ensure consistent handling of unsteady numbers
168
- self.unsteady_df = self._get_prj_entries('Unsteady')
169
- self.plan_df = self._get_prj_entries('Plan')
170
- self.flow_df = self._get_prj_entries('Flow')
171
- self.geom_df = self.get_geom_entries()
165
+ try:
166
+ # Load data frames
167
+ self.unsteady_df = self._get_prj_entries('Unsteady')
168
+ self.plan_df = self._get_prj_entries('Plan')
169
+ self.flow_df = self._get_prj_entries('Flow')
170
+ self.geom_df = self.get_geom_entries()
171
+
172
+ # Ensure required columns exist
173
+ self._ensure_required_columns()
174
+
175
+ # Set paths for geometry and flow files
176
+ self._set_file_paths()
177
+
178
+ except Exception as e:
179
+ logger.error(f"Error loading project data: {e}")
180
+ raise
181
+
182
+ def _ensure_required_columns(self):
183
+ """Ensure all required columns exist in plan_df."""
184
+ required_columns = [
185
+ 'plan_number', 'unsteady_number', 'geometry_number',
186
+ 'Geom File', 'Geom Path', 'Flow File', 'Flow Path', 'full_path'
187
+ ]
172
188
 
173
- # Set geometry and flow paths
189
+ for col in required_columns:
190
+ if col not in self.plan_df.columns:
191
+ self.plan_df[col] = None
192
+
193
+ if not self.plan_df['full_path'].any():
194
+ self.plan_df['full_path'] = self.plan_df['plan_number'].apply(
195
+ lambda x: str(self.project_folder / f"{self.project_name}.p{x}")
196
+ )
197
+
198
+ def _set_file_paths(self):
199
+ """Set geometry and flow paths in plan_df."""
174
200
  for idx, row in self.plan_df.iterrows():
175
201
  try:
176
- # Add Geom Path
177
- if row['Geom File'] is not None:
178
- geom_hdf_path = self.project_folder / f"{self.project_name}.g{row['Geom File']}"
179
- self.plan_df.at[idx, 'Geom Path'] = str(geom_hdf_path)
180
-
181
- # Add Flow Path
182
- if row['Flow File'] is not None:
183
- # Determine if this is a steady or unsteady flow file
184
- if row['unsteady_number'] is not None:
185
- flow_path = self.project_folder / f"{self.project_name}.u{row['Flow File']}"
186
- else:
187
- flow_path = self.project_folder / f"{self.project_name}.f{row['Flow File']}"
188
- self.plan_df.at[idx, 'Flow Path'] = str(flow_path)
202
+ self._set_geom_path(idx, row)
203
+ self._set_flow_path(idx, row)
189
204
 
190
- if not self.suppress_logging:
191
- logger.info(f"Plan {row['plan_number']} paths set up")
205
+ if not self.suppress_logging:
206
+ logger.info(f"Plan {row['plan_number']} paths set up")
192
207
  except Exception as e:
193
208
  logger.error(f"Error processing plan file {row['plan_number']}: {e}")
194
209
 
195
- def _get_geom_file_from_plan(self, plan_file_path):
196
- """
197
- Extract the geometry number from a plan file by finding the Geom File value
198
- and stripping the leading 'g'.
199
-
200
- Parameters:
201
- -----------
202
- plan_file_path : str or Path
203
- Path to the plan file.
204
-
205
- Returns:
206
- --------
207
- tuple: (str, str)
208
- A tuple containing (geometry_number, full_hdf_path) or (None, None) if not found.
209
- geometry_number is the number after 'g' in the Geom File value
210
- full_hdf_path is the path to the geometry HDF file
211
- """
212
- content, encoding = read_file_with_fallback_encoding(plan_file_path)
213
-
214
- if content is None:
215
- return None, None
216
-
217
- try:
218
- match = re.search(r'Geom File=g(\d+)', content)
219
- if match:
220
- geom_number = match.group(1) # This gets just the number after 'g'
221
- geom_file = f"g{geom_number}"
222
- geom_hdf_path = self.project_folder / f"{self.project_name}.{geom_file}.hdf"
223
- if geom_hdf_path.exists():
224
- return geom_number, str(geom_hdf_path)
225
- except Exception as e:
226
- logger.error(f"Error extracting geometry number from {plan_file_path}: {e}")
227
-
228
- return None, None
210
+ def _set_geom_path(self, idx: int, row: pd.Series):
211
+ """Set geometry path for a plan entry."""
212
+ if pd.notna(row['Geom File']):
213
+ geom_path = self.project_folder / f"{self.project_name}.g{row['Geom File']}"
214
+ self.plan_df.at[idx, 'Geom Path'] = str(geom_path)
215
+
216
+ def _set_flow_path(self, idx: int, row: pd.Series):
217
+ """Set flow path for a plan entry."""
218
+ if pd.notna(row['Flow File']):
219
+ prefix = 'u' if pd.notna(row['unsteady_number']) else 'f'
220
+ flow_path = self.project_folder / f"{self.project_name}.{prefix}{row['Flow File']}"
221
+ self.plan_df.at[idx, 'Flow Path'] = str(flow_path)
229
222
 
230
- def _get_flow_file_from_plan(self, plan_file_path):
223
+ def _get_geom_file_for_plan(self, plan_number):
231
224
  """
232
- Extract the Flow File value from a plan file.
225
+ Get the geometry file path for a given plan number.
233
226
 
234
- Parameters:
235
- -----------
236
- plan_file_path : str or Path
237
- Path to the plan file.
227
+ Args:
228
+ plan_number (str): The plan number to find the geometry file for.
238
229
 
239
230
  Returns:
240
- --------
241
- str or None
242
- The Flow File value or None if not found.
231
+ str: The full path to the geometry HDF file, or None if not found.
243
232
  """
233
+ plan_file_path = self.project_folder / f"{self.project_name}.p{plan_number}"
244
234
  content, encoding = read_file_with_fallback_encoding(plan_file_path)
245
235
 
246
236
  if content is None:
247
237
  return None
248
238
 
249
239
  try:
250
- match = re.search(r'Flow File=([^\s]+)', content)
251
- if match:
252
- return match.group(1)
240
+ for line in content.splitlines():
241
+ if line.startswith("Geom File="):
242
+ geom_file = line.strip().split('=')[1]
243
+ geom_hdf_path = self.project_folder / f"{self.project_name}.{geom_file}.hdf"
244
+ if geom_hdf_path.exists():
245
+ return str(geom_hdf_path)
246
+ else:
247
+ return None
253
248
  except Exception as e:
254
- logger.error(f"Error extracting Flow File from {plan_file_path}: {e}")
255
-
249
+ logger.error(f"Error reading plan file for geometry: {e}")
256
250
  return None
257
251
 
252
+
258
253
  @staticmethod
259
254
  @log_call
260
255
  def get_plan_value(
@@ -439,112 +434,106 @@ class RasPrj:
439
434
 
440
435
  Returns:
441
436
  pd.DataFrame: A DataFrame containing the extracted entries.
437
+
438
+ Raises:
439
+ Exception: If there's an error reading or processing the project file.
442
440
  """
443
441
  entries = []
444
442
  pattern = re.compile(rf"{entry_type} File=(\w+)")
445
443
 
446
444
  try:
447
- with open(self.prj_file, 'r') as file:
445
+ with open(self.prj_file, 'r', encoding='utf-8') as file:
448
446
  for line in file:
449
447
  match = pattern.match(line.strip())
450
448
  if match:
451
449
  file_name = match.group(1)
452
450
  full_path = str(self.project_folder / f"{self.project_name}.{file_name}")
453
- entry_number = file_name[1:] # Extract number portion without prefix
451
+ entry_number = file_name[1:]
454
452
 
455
453
  entry = {
456
454
  f'{entry_type.lower()}_number': entry_number,
457
455
  'full_path': full_path
458
456
  }
459
457
 
458
+ # Handle Unsteady entries
460
459
  if entry_type == 'Unsteady':
461
- entry['unsteady_number'] = entry_number
462
- unsteady_info = self._parse_unsteady_file(Path(full_path))
463
- entry.update(unsteady_info)
460
+ entry.update(self._process_unsteady_entry(entry_number, full_path))
464
461
  else:
465
- entry.update({
466
- 'unsteady_number': None,
467
- 'geometry_number': None
468
- })
462
+ entry.update(self._process_default_entry())
469
463
 
464
+ # Handle Plan entries
470
465
  if entry_type == 'Plan':
471
- plan_info = self._parse_plan_file(Path(full_path))
472
- if plan_info:
473
- # Handle Flow File (unsteady) number
474
- flow_file = plan_info.get('Flow File')
475
- if flow_file and flow_file.startswith('u'):
476
- entry['unsteady_number'] = flow_file[1:]
477
- entry['Flow File'] = flow_file[1:]
478
- else:
479
- entry['unsteady_number'] = None
480
- entry['Flow File'] = flow_file[1:] if flow_file and flow_file.startswith('f') else None
481
-
482
- # Handle Geom File number
483
- geom_file = plan_info.get('Geom File')
484
- if geom_file and geom_file.startswith('g'):
485
- entry['geometry_number'] = geom_file[1:]
486
- entry['Geom File'] = geom_file[1:]
487
- else:
488
- entry['geometry_number'] = None
489
- entry['Geom File'] = None
490
-
491
- # Add all plan key information with exact same names
492
- for key, value in plan_info.items():
493
- if key not in ['Flow File', 'Geom File']:
494
- entry[key] = value
466
+ entry.update(self._process_plan_entry(entry_number, full_path))
495
467
 
496
- # Add HDF results path
497
- hdf_results_path = self.project_folder / f"{self.project_name}.p{entry_number}.hdf"
498
- entry['HDF_Results_Path'] = str(hdf_results_path) if hdf_results_path.exists() else None
499
-
500
468
  entries.append(entry)
501
-
502
- df = pd.DataFrame(entries)
503
469
 
504
- if not df.empty and entry_type == 'Plan':
505
- # Set required column order
506
- first_cols = ['plan_number', 'unsteady_number', 'geometry_number']
507
-
508
- # Standard plan key columns in the exact order specified
509
- plan_key_cols = [
510
- 'Plan Title', 'Program Version', 'Short Identifier', 'Simulation Date',
511
- 'Std Step Tol', 'Computation Interval', 'Output Interval', 'Instantaneous Interval',
512
- 'Mapping Interval', 'Run HTab', 'Run UNet', 'Run Sediment', 'Run PostProcess',
513
- 'Run WQNet', 'Run RASMapper', 'UNET Use Existing IB Tables', 'HDF_Results_Path',
514
- 'UNET 1D Methodology', 'Write IC File', 'Write IC File at Fixed DateTime',
515
- 'IC Time', 'Write IC File Reoccurance', 'Write IC File at Sim End'
516
- ]
517
-
518
- # Additional convenience columns
519
- file_path_cols = ['Geom File', 'Geom Path', 'Flow File', 'Flow Path']
520
-
521
- # Build the final column list
522
- all_cols = first_cols.copy()
523
- for col in plan_key_cols:
524
- if col in df.columns:
525
- all_cols.append(col)
526
-
527
- # Add any remaining columns not explicitly specified
528
- other_cols = [col for col in df.columns if col not in all_cols + file_path_cols + ['full_path']]
529
- all_cols.extend(other_cols)
530
- all_cols.extend(file_path_cols)
531
-
532
- # Rename plan_number column
533
- df = df.rename(columns={f'{entry_type.lower()}_number': 'plan_number'})
534
-
535
- # Fill in missing columns with None
536
- for col in all_cols:
537
- if col not in df.columns:
538
- df[col] = None
539
-
540
- # Return DataFrame with specified column order
541
- return df[all_cols]
542
-
543
- return df
470
+ df = pd.DataFrame(entries)
471
+ return self._format_dataframe(df, entry_type)
472
+
544
473
  except Exception as e:
545
474
  logger.error(f"Error in _get_prj_entries for {entry_type}: {e}")
546
475
  raise
547
476
 
477
+ def _process_unsteady_entry(self, entry_number: str, full_path: str) -> dict:
478
+ """Process unsteady entry data."""
479
+ entry = {'unsteady_number': entry_number}
480
+ unsteady_info = self._parse_unsteady_file(Path(full_path))
481
+ entry.update(unsteady_info)
482
+ return entry
483
+
484
+ def _process_default_entry(self) -> dict:
485
+ """Process default entry data."""
486
+ return {
487
+ 'unsteady_number': None,
488
+ 'geometry_number': None
489
+ }
490
+
491
+ def _process_plan_entry(self, entry_number: str, full_path: str) -> dict:
492
+ """Process plan entry data."""
493
+ entry = {}
494
+ plan_info = self._parse_plan_file(Path(full_path))
495
+
496
+ if plan_info:
497
+ entry.update(self._process_flow_file(plan_info))
498
+ entry.update(self._process_geom_file(plan_info))
499
+
500
+ # Add remaining plan info
501
+ for key, value in plan_info.items():
502
+ if key not in ['Flow File', 'Geom File']:
503
+ entry[key] = value
504
+
505
+ # Add HDF results path
506
+ hdf_results_path = self.project_folder / f"{self.project_name}.p{entry_number}.hdf"
507
+ entry['HDF_Results_Path'] = str(hdf_results_path) if hdf_results_path.exists() else None
508
+
509
+ return entry
510
+
511
+ def _process_flow_file(self, plan_info: dict) -> dict:
512
+ """Process flow file information from plan info."""
513
+ flow_file = plan_info.get('Flow File')
514
+ if flow_file and flow_file.startswith('u'):
515
+ return {
516
+ 'unsteady_number': flow_file[1:],
517
+ 'Flow File': flow_file[1:]
518
+ }
519
+ return {
520
+ 'unsteady_number': None,
521
+ 'Flow File': flow_file[1:] if flow_file and flow_file.startswith('f') else None
522
+ }
523
+
524
+ def _process_geom_file(self, plan_info: dict) -> dict:
525
+ """Process geometry file information from plan info."""
526
+ geom_file = plan_info.get('Geom File')
527
+ if geom_file and geom_file.startswith('g'):
528
+ return {
529
+ 'geometry_number': geom_file[1:],
530
+ 'Geom File': geom_file[1:]
531
+ }
532
+ return {
533
+ 'geometry_number': None,
534
+ 'Geom File': None
535
+ }
536
+
548
537
  def _parse_unsteady_file(self, unsteady_file_path):
549
538
  """
550
539
  Parse an unsteady flow file and extract critical information.
@@ -935,27 +924,135 @@ class RasPrj:
935
924
  return bc_info, unparsed_lines
936
925
 
937
926
  @log_call
938
- def get_unsteady_numbers_from_plans(self):
927
+ def _format_dataframe(self, df, entry_type):
939
928
  """
940
- Get all plans that use unsteady flow files.
929
+ Format the DataFrame according to the desired column structure.
930
+
931
+ Args:
932
+ df (pd.DataFrame): The DataFrame to format.
933
+ entry_type (str): The type of entry (e.g., 'Plan', 'Flow', 'Unsteady', 'Geom').
941
934
 
942
935
  Returns:
943
- --------
944
- pd.DataFrame
945
- A DataFrame containing only plan entries that use unsteady flow files.
936
+ pd.DataFrame: The formatted DataFrame.
937
+ """
938
+ if df.empty:
939
+ return df
946
940
 
947
- Raises:
948
- -------
949
- RuntimeError: If the project has not been initialized.
941
+ if entry_type == 'Plan':
942
+ # Define column groups
943
+ first_cols = ['plan_number', 'unsteady_number', 'geometry_number']
944
+ plan_key_cols = [
945
+ 'Plan Title', 'Program Version', 'Short Identifier', 'Simulation Date',
946
+ 'Computation Interval', 'Mapping Interval', 'Run HTab', 'Run UNet',
947
+ 'Run Sediment', 'Run PostProcess', 'Run WQNet', 'UNET Use Existing IB Tables',
948
+ 'HDF_Results_Path', 'UNET 1D Methodology'
949
+ ]
950
+ file_path_cols = ['Geom File', 'Geom Path', 'Flow File', 'Flow Path']
951
+
952
+ # Build column list
953
+ all_cols = first_cols.copy()
954
+ all_cols.extend([col for col in plan_key_cols if col in df.columns])
955
+ all_cols.extend([col for col in df.columns if col not in all_cols + file_path_cols + ['full_path']])
956
+ all_cols.extend(file_path_cols)
957
+
958
+ # Rename and fill missing columns
959
+ df = df.rename(columns={f'{entry_type.lower()}_number': 'plan_number'})
960
+ for col in all_cols:
961
+ if col not in df.columns:
962
+ df[col] = None
963
+
964
+ # Add full_path if present
965
+ if 'full_path' in df.columns:
966
+ all_cols.append('full_path')
967
+
968
+ return df[[col for col in all_cols if col in df.columns]]
969
+
970
+ return df
971
+
972
+ @log_call
973
+ def _get_prj_entries(self, entry_type):
950
974
  """
951
- self.check_initialized()
975
+ Extract entries of a specific type from the HEC-RAS project file.
976
+ """
977
+ entries = []
978
+ pattern = re.compile(rf"{entry_type} File=(\w+)")
979
+
980
+ try:
981
+ with open(self.prj_file, 'r') as file:
982
+ for line in file:
983
+ match = pattern.match(line.strip())
984
+ if match:
985
+ file_name = match.group(1)
986
+ full_path = str(self.project_folder / f"{self.project_name}.{file_name}")
987
+ entry = self._create_entry(entry_type, file_name, full_path)
988
+ entries.append(entry)
952
989
 
953
- # Filter plan_df to only include plans with unsteady_number not None
954
- unsteady_plans = self.plan_df[self.plan_df['unsteady_number'].notna()].copy()
990
+ return self._format_dataframe(pd.DataFrame(entries), entry_type)
955
991
 
956
- logger.info(f"Found {len(unsteady_plans)} plans using unsteady flow files")
992
+ except Exception as e:
993
+ logger.error(f"Error in _get_prj_entries for {entry_type}: {e}")
994
+ raise
995
+
996
+ def _create_entry(self, entry_type, file_name, full_path):
997
+ """Helper method to create entry dictionary."""
998
+ entry_number = file_name[1:]
999
+ entry = {
1000
+ f'{entry_type.lower()}_number': entry_number,
1001
+ 'full_path': full_path,
1002
+ 'unsteady_number': None,
1003
+ 'geometry_number': None
1004
+ }
1005
+
1006
+ if entry_type == 'Unsteady':
1007
+ entry['unsteady_number'] = entry_number
1008
+ entry.update(self._parse_unsteady_file(Path(full_path)))
1009
+ elif entry_type == 'Plan':
1010
+ self._update_plan_entry(entry, entry_number, full_path)
1011
+
1012
+ return entry
1013
+
1014
+ def _update_plan_entry(self, entry, entry_number, full_path):
1015
+ """Helper method to update plan entry with additional information."""
1016
+ plan_info = self._parse_plan_file(Path(full_path))
1017
+ if plan_info:
1018
+ # Handle Flow File
1019
+ flow_file = plan_info.get('Flow File')
1020
+ if flow_file:
1021
+ if flow_file.startswith('u'):
1022
+ entry.update({'unsteady_number': flow_file[1:], 'Flow File': flow_file[1:]})
1023
+ else:
1024
+ entry['Flow File'] = flow_file[1:] if flow_file.startswith('f') else None
1025
+
1026
+ # Handle Geom File
1027
+ geom_file = plan_info.get('Geom File')
1028
+ if geom_file and geom_file.startswith('g'):
1029
+ entry.update({'geometry_number': geom_file[1:], 'Geom File': geom_file[1:]})
1030
+
1031
+ # Add remaining plan info
1032
+ entry.update({k: v for k, v in plan_info.items() if k not in ['Flow File', 'Geom File']})
1033
+
1034
+ # Add HDF results path
1035
+ hdf_path = self.project_folder / f"{self.project_name}.p{entry_number}.hdf"
1036
+ entry['HDF_Results_Path'] = str(hdf_path) if hdf_path.exists() else None
1037
+
1038
+ def _set_plan_paths(self):
1039
+ """Helper method to set paths in plan_df."""
1040
+ if not self.plan_df['full_path'].any():
1041
+ self.plan_df['full_path'] = self.plan_df['plan_number'].apply(
1042
+ lambda x: str(self.project_folder / f"{self.project_name}.p{x}")
1043
+ )
957
1044
 
958
- return unsteady_plans
1045
+ for idx, row in self.plan_df.iterrows():
1046
+ if pd.notna(row['Geom File']) and pd.isna(row.get('Geom Path')):
1047
+ self.plan_df.at[idx, 'Geom Path'] = str(self.project_folder / f"{self.project_name}.g{row['Geom File']}")
1048
+
1049
+ if pd.notna(row['Flow File']) and pd.isna(row.get('Flow Path')):
1050
+ prefix = 'u' if pd.notna(row['unsteady_number']) else 'f'
1051
+ self.plan_df.at[idx, 'Flow Path'] = str(self.project_folder / f"{self.project_name}.{prefix}{row['Flow File']}")
1052
+
1053
+ if not self.suppress_logging:
1054
+ logger.info(f"Plan {row['plan_number']} paths set up")
1055
+
959
1056
 
960
1057
  # Create a global instance named 'ras'
961
1058
  # Defining the global instance allows the init_ras_project function to initialize the project.
@@ -1027,7 +1124,7 @@ def get_ras_exe(ras_version=None):
1027
1124
  Args:
1028
1125
  ras_version (str, optional): Either a version number or a full path to the HEC-RAS executable.
1029
1126
  If None, the function will first check the global 'ras' object for a path.
1030
- If the global 'ras' object is not initialized or doesn't have a path, a default path will be used.
1127
+ or a default path.
1031
1128
 
1032
1129
  Returns:
1033
1130
  str: The full path to the HEC-RAS executable.
ras_commander/RasUtils.py CHANGED
@@ -216,30 +216,49 @@ class RasUtils:
216
216
  Returns:
217
217
  Path: Full path to the plan file
218
218
 
219
+ Raises:
220
+ ValueError: If plan number is not between 1 and 99
221
+ TypeError: If input type is invalid
222
+ FileNotFoundError: If the plan file does not exist
223
+
219
224
  Example:
220
225
  >>> plan_path = RasUtils.get_plan_path(1)
221
226
  >>> print(f"Plan file path: {plan_path}")
222
227
  >>> plan_path = RasUtils.get_plan_path("path/to/plan.p01")
223
228
  >>> print(f"Plan file path: {plan_path}")
224
229
  """
225
-
230
+ # Validate RAS object
226
231
  ras_obj = ras_object or ras
227
232
  ras_obj.check_initialized()
228
233
 
234
+ # Handle direct file path input
229
235
  plan_path = Path(current_plan_number_or_path)
230
236
  if plan_path.is_file():
231
237
  logger.info(f"Using provided plan file path: {plan_path}")
232
238
  return plan_path
233
239
 
240
+ # Handle plan number input
234
241
  try:
235
- current_plan_number = f"{int(current_plan_number_or_path):02d}" # Ensure two-digit format
242
+ plan_num = int(current_plan_number_or_path)
243
+ if not 1 <= plan_num <= 99:
244
+ raise ValueError(f"Plan number must be between 1 and 99, got: {plan_num}")
245
+ current_plan_number = f"{plan_num:02d}" # Ensure two-digit format
236
246
  logger.debug(f"Converted plan number to two-digit format: {current_plan_number}")
237
- except ValueError:
247
+ except (ValueError, TypeError) as e:
248
+ if isinstance(e, TypeError):
249
+ logger.error(f"Invalid input type: {type(current_plan_number_or_path)}. Expected string, number, or Path.")
250
+ raise TypeError(f"Invalid input type: {type(current_plan_number_or_path)}. Expected string, number, or Path.")
238
251
  logger.error(f"Invalid plan number: {current_plan_number_or_path}. Expected a number from 1 to 99.")
239
252
  raise ValueError(f"Invalid plan number: {current_plan_number_or_path}. Expected a number from 1 to 99.")
240
253
 
254
+ # Construct and validate plan path
241
255
  plan_name = f"{ras_obj.project_name}.p{current_plan_number}"
242
256
  full_plan_path = ras_obj.project_folder / plan_name
257
+
258
+ if not full_plan_path.exists():
259
+ logger.error(f"Plan file does not exist: {full_plan_path}")
260
+ raise FileNotFoundError(f"Plan file does not exist: {full_plan_path}")
261
+
243
262
  logger.info(f"Constructed plan file path: {full_plan_path}")
244
263
  return full_plan_path
245
264
 
ras_commander/__init__.py CHANGED
@@ -10,7 +10,7 @@ try:
10
10
  __version__ = version("ras-commander")
11
11
  except PackageNotFoundError:
12
12
  # package is not installed
13
- __version__ = "0.65.0"
13
+ __version__ = "0.66.0"
14
14
 
15
15
  # Set up logging
16
16
  setup_logging()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ras-commander
3
- Version: 0.65.0
3
+ Version: 0.66.0
4
4
  Summary: A Python library for automating HEC-RAS 6.x operations
5
5
  Home-page: https://github.com/gpt-cmdr/ras-commander
6
6
  Author: William M. Katzenmeyer, P.E., C.F.M.
@@ -21,14 +21,14 @@ ras_commander/RasExamples.py,sha256=6IZ96LcAsk5LYFehdD0zDW5wyZWxQa6OQu2N9upxWXA,
21
21
  ras_commander/RasGeo.py,sha256=M0sVNKlWmmbve8iMXLWq25WgbxqLWBo7_1oDg_rALzU,5607
22
22
  ras_commander/RasGpt.py,sha256=N_7p2nucWrBBXdB2k2ZKvOeOdXNmFD9dIY3W7_5i5nw,1206
23
23
  ras_commander/RasMapper.py,sha256=LO_blvQnd4pwkEU8A30-RoE-CYIoU3s_fNLHBoM8ljw,613
24
- ras_commander/RasPlan.py,sha256=9RbOzQyz0_BCRa-gds_ca3_DImNMeX6n5NslkjpfT1w,62477
25
- ras_commander/RasPrj.py,sha256=b0d0A5WKkUXRtQobUcDPBTTGymtM_EWZwFbpOrNjqfY,44731
24
+ ras_commander/RasPlan.py,sha256=0THxeB9ldeDuS4of2ruYG7Abbc5jz2729y5qOHntMuI,61664
25
+ ras_commander/RasPrj.py,sha256=gQoGG1Mqsoj5W1oVlulqhA0kQ5eD69vI96uihnY-kLE,47744
26
26
  ras_commander/RasToGo.py,sha256=TKujfaV1xQhFaOddF4g2ogGy6ky-CLlfelSMPD2J3Nk,1223
27
27
  ras_commander/RasUnsteady.py,sha256=KfCXAag-_bPwwS3JbPZH-s4hbaoHACO0mlRnGrzbFgA,32092
28
- ras_commander/RasUtils.py,sha256=P2-aBL61kdRINsjnBpstZVD6VVc7hI_D3RUXqr6ldmc,34863
29
- ras_commander/__init__.py,sha256=Aojks8MKjFZceg9TEaTqrr6RsgLeJAh6iZrRZVoOdg0,2001
30
- ras_commander-0.65.0.dist-info/LICENSE,sha256=_pbd6qHnlsz1iQ-ozDW_49r86BZT6CRwO2iBtw0iN6M,457
31
- ras_commander-0.65.0.dist-info/METADATA,sha256=7Ddh7KnY46exroA1XG0YC77l3YKdQJqiNZuyZIUtOPE,26233
32
- ras_commander-0.65.0.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
33
- ras_commander-0.65.0.dist-info/top_level.txt,sha256=i76S7eKLFC8doKcXDl3aiOr9RwT06G8adI6YuKbQDaA,14
34
- ras_commander-0.65.0.dist-info/RECORD,,
28
+ ras_commander/RasUtils.py,sha256=0fm4IIs0LH1dgDj3pGd66mR82DhWLEkRKUvIo2M_5X0,35886
29
+ ras_commander/__init__.py,sha256=KICL5peRDDjE2BXEFVDMmtDZ4TKHtNhHh5eQgTSdqqs,2001
30
+ ras_commander-0.66.0.dist-info/LICENSE,sha256=_pbd6qHnlsz1iQ-ozDW_49r86BZT6CRwO2iBtw0iN6M,457
31
+ ras_commander-0.66.0.dist-info/METADATA,sha256=-jPNaFlPs0iCcscfGjO31DwF3-LT632rbiunJQmYzoM,26233
32
+ ras_commander-0.66.0.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
33
+ ras_commander-0.66.0.dist-info/top_level.txt,sha256=i76S7eKLFC8doKcXDl3aiOr9RwT06G8adI6YuKbQDaA,14
34
+ ras_commander-0.66.0.dist-info/RECORD,,