ras-commander 0.80.2__py3-none-any.whl → 0.81.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.
@@ -6,14 +6,23 @@ Attribution:
6
6
  Copyright (c) 2024 fema-ffrd, MIT license
7
7
 
8
8
  Description:
9
- Provides static methods for extracting unsteady flow results, volume accounting,
10
- and reference data from HEC-RAS plan HDF files.
9
+ Provides static methods for extracting both unsteady and steady flow results,
10
+ volume accounting, and reference data from HEC-RAS plan HDF files.
11
11
 
12
12
  Available Functions:
13
- - get_unsteady_info: Extract unsteady attributes
14
- - get_unsteady_summary: Extract unsteady summary data
15
- - get_volume_accounting: Extract volume accounting data
16
- - get_runtime_data: Extract runtime and compute time data
13
+ Unsteady Flow:
14
+ - get_unsteady_info: Extract unsteady attributes
15
+ - get_unsteady_summary: Extract unsteady summary data
16
+ - get_volume_accounting: Extract volume accounting data
17
+ - get_runtime_data: Extract runtime and compute time data
18
+ - get_reference_timeseries: Extract reference line/point timeseries
19
+ - get_reference_summary: Extract reference line/point summary
20
+
21
+ Steady Flow:
22
+ - is_steady_plan: Check if HDF contains steady state results
23
+ - get_steady_profile_names: Extract steady state profile names
24
+ - get_steady_wse: Extract WSE data for steady state profiles
25
+ - get_steady_info: Extract steady flow attributes and metadata
17
26
 
18
27
  Note:
19
28
  All methods are static and designed to be used without class instantiation.
@@ -378,4 +387,326 @@ class HdfResultsPlan:
378
387
 
379
388
  except Exception as e:
380
389
  logger.error(f"Error reading reference {reftype} summary: {str(e)}")
381
- return pd.DataFrame()
390
+ return pd.DataFrame()
391
+
392
+ # ==================== STEADY STATE METHODS ====================
393
+
394
+ @staticmethod
395
+ @log_call
396
+ @standardize_input(file_type='plan_hdf')
397
+ def is_steady_plan(hdf_path: Path) -> bool:
398
+ """
399
+ Check if HDF file contains steady state results.
400
+
401
+ Args:
402
+ hdf_path (Path): Path to HEC-RAS plan HDF file
403
+ ras_object (RasPrj, optional): Specific RAS object to use. If None, uses the global ras instance.
404
+
405
+ Returns:
406
+ bool: True if the HDF contains steady state results, False otherwise
407
+
408
+ Notes:
409
+ - Checks for existence of Results/Steady group
410
+ - Does not guarantee results are complete or valid
411
+ """
412
+ try:
413
+ with h5py.File(hdf_path, 'r') as hdf_file:
414
+ return "Results/Steady" in hdf_file
415
+ except Exception as e:
416
+ logger.error(f"Error checking if plan is steady: {str(e)}")
417
+ return False
418
+
419
+ @staticmethod
420
+ @log_call
421
+ @standardize_input(file_type='plan_hdf')
422
+ def get_steady_profile_names(hdf_path: Path) -> List[str]:
423
+ """
424
+ Extract profile names from steady state results.
425
+
426
+ Args:
427
+ hdf_path (Path): Path to HEC-RAS plan HDF file
428
+ ras_object (RasPrj, optional): Specific RAS object to use. If None, uses the global ras instance.
429
+
430
+ Returns:
431
+ List[str]: List of profile names (e.g., ['50Pct', '10Pct', '1Pct'])
432
+
433
+ Raises:
434
+ FileNotFoundError: If the specified HDF file is not found
435
+ KeyError: If steady state results or profile names are not found
436
+ ValueError: If the plan is not a steady state plan
437
+
438
+ Example:
439
+ >>> from ras_commander import HdfResultsPlan, init_ras_project
440
+ >>> init_ras_project(Path('/path/to/project'), '6.6')
441
+ >>> profiles = HdfResultsPlan.get_steady_profile_names('01')
442
+ >>> print(profiles)
443
+ ['50Pct', '20Pct', '10Pct', '4Pct', '2Pct', '1Pct', '0.2Pct']
444
+ """
445
+ try:
446
+ with h5py.File(hdf_path, 'r') as hdf_file:
447
+ # Check if this is a steady state plan
448
+ if "Results/Steady" not in hdf_file:
449
+ raise ValueError(f"HDF file does not contain steady state results: {hdf_path.name}")
450
+
451
+ # Path to profile names
452
+ profile_names_path = "Results/Steady/Output/Output Blocks/Base Output/Steady Profiles/Profile Names"
453
+
454
+ if profile_names_path not in hdf_file:
455
+ raise KeyError(f"Profile names not found at: {profile_names_path}")
456
+
457
+ # Read profile names dataset
458
+ profile_names_ds = hdf_file[profile_names_path]
459
+ profile_names_raw = profile_names_ds[()]
460
+
461
+ # Decode byte strings to regular strings
462
+ profile_names = []
463
+ for name in profile_names_raw:
464
+ if isinstance(name, bytes):
465
+ profile_names.append(name.decode('utf-8').strip())
466
+ else:
467
+ profile_names.append(str(name).strip())
468
+
469
+ logger.info(f"Found {len(profile_names)} steady state profiles: {profile_names}")
470
+ return profile_names
471
+
472
+ except FileNotFoundError:
473
+ raise FileNotFoundError(f"HDF file not found: {hdf_path}")
474
+ except KeyError as e:
475
+ raise KeyError(f"Error accessing steady state profile names: {str(e)}")
476
+ except Exception as e:
477
+ raise RuntimeError(f"Error reading steady state profile names: {str(e)}")
478
+
479
+ @staticmethod
480
+ @log_call
481
+ @standardize_input(file_type='plan_hdf')
482
+ def get_steady_wse(
483
+ hdf_path: Path,
484
+ profile_index: Optional[int] = None,
485
+ profile_name: Optional[str] = None
486
+ ) -> pd.DataFrame:
487
+ """
488
+ Extract water surface elevation (WSE) data for steady state profiles.
489
+
490
+ Args:
491
+ hdf_path (Path): Path to HEC-RAS plan HDF file
492
+ profile_index (int, optional): Index of profile to extract (0-based). If None, extracts all profiles.
493
+ profile_name (str, optional): Name of profile to extract (e.g., '1Pct'). If specified, overrides profile_index.
494
+ ras_object (RasPrj, optional): Specific RAS object to use. If None, uses the global ras instance.
495
+
496
+ Returns:
497
+ pd.DataFrame: DataFrame containing WSE data with columns:
498
+ - River: River name
499
+ - Reach: Reach name
500
+ - Station: Cross section river station
501
+ - Profile: Profile name (if multiple profiles)
502
+ - WSE: Water surface elevation (ft)
503
+
504
+ Raises:
505
+ FileNotFoundError: If the specified HDF file is not found
506
+ KeyError: If steady state results or WSE data are not found
507
+ ValueError: If profile_index or profile_name is invalid
508
+
509
+ Example:
510
+ >>> # Extract single profile by index
511
+ >>> wse_df = HdfResultsPlan.get_steady_wse('01', profile_index=5) # 100-year profile
512
+
513
+ >>> # Extract single profile by name
514
+ >>> wse_df = HdfResultsPlan.get_steady_wse('01', profile_name='1Pct')
515
+
516
+ >>> # Extract all profiles
517
+ >>> wse_df = HdfResultsPlan.get_steady_wse('01')
518
+ """
519
+ try:
520
+ with h5py.File(hdf_path, 'r') as hdf_file:
521
+ # Check if this is a steady state plan
522
+ if "Results/Steady" not in hdf_file:
523
+ raise ValueError(f"HDF file does not contain steady state results: {hdf_path.name}")
524
+
525
+ # Paths to data
526
+ wse_path = "Results/Steady/Output/Output Blocks/Base Output/Steady Profiles/Cross Sections/Water Surface"
527
+ xs_attrs_path = "Results/Steady/Output/Geometry Info/Cross Section Attributes"
528
+ profile_names_path = "Results/Steady/Output/Output Blocks/Base Output/Steady Profiles/Profile Names"
529
+
530
+ # Check required paths exist
531
+ if wse_path not in hdf_file:
532
+ raise KeyError(f"WSE data not found at: {wse_path}")
533
+ if xs_attrs_path not in hdf_file:
534
+ raise KeyError(f"Cross section attributes not found at: {xs_attrs_path}")
535
+
536
+ # Get WSE dataset (shape: num_profiles × num_cross_sections)
537
+ wse_ds = hdf_file[wse_path]
538
+ wse_data = wse_ds[()]
539
+ num_profiles, num_xs = wse_data.shape
540
+
541
+ # Get profile names
542
+ if profile_names_path in hdf_file:
543
+ profile_names_raw = hdf_file[profile_names_path][()]
544
+ profile_names = [
545
+ name.decode('utf-8').strip() if isinstance(name, bytes) else str(name).strip()
546
+ for name in profile_names_raw
547
+ ]
548
+ else:
549
+ # Fallback to numbered profiles
550
+ profile_names = [f"Profile_{i+1}" for i in range(num_profiles)]
551
+
552
+ # Get cross section attributes
553
+ xs_attrs = hdf_file[xs_attrs_path][()]
554
+
555
+ # Determine which profiles to extract
556
+ if profile_name is not None:
557
+ # Find profile by name
558
+ try:
559
+ profile_idx = profile_names.index(profile_name)
560
+ except ValueError:
561
+ raise ValueError(
562
+ f"Profile name '{profile_name}' not found. "
563
+ f"Available profiles: {profile_names}"
564
+ )
565
+ profiles_to_extract = [(profile_idx, profile_name)]
566
+
567
+ elif profile_index is not None:
568
+ # Validate profile index
569
+ if profile_index < 0 or profile_index >= num_profiles:
570
+ raise ValueError(
571
+ f"Profile index {profile_index} out of range. "
572
+ f"Valid range: 0 to {num_profiles-1}"
573
+ )
574
+ profiles_to_extract = [(profile_index, profile_names[profile_index])]
575
+
576
+ else:
577
+ # Extract all profiles
578
+ profiles_to_extract = list(enumerate(profile_names))
579
+
580
+ # Build DataFrame
581
+ rows = []
582
+ for prof_idx, prof_name in profiles_to_extract:
583
+ wse_values = wse_data[prof_idx, :]
584
+
585
+ for xs_idx in range(num_xs):
586
+ river = xs_attrs[xs_idx]['River']
587
+ reach = xs_attrs[xs_idx]['Reach']
588
+ station = xs_attrs[xs_idx]['Station']
589
+
590
+ # Decode byte strings
591
+ river = river.decode('utf-8') if isinstance(river, bytes) else str(river)
592
+ reach = reach.decode('utf-8') if isinstance(reach, bytes) else str(reach)
593
+ station = station.decode('utf-8') if isinstance(station, bytes) else str(station)
594
+
595
+ row = {
596
+ 'River': river.strip(),
597
+ 'Reach': reach.strip(),
598
+ 'Station': station.strip(),
599
+ 'WSE': float(wse_values[xs_idx])
600
+ }
601
+
602
+ # Only add Profile column if extracting multiple profiles
603
+ if len(profiles_to_extract) > 1:
604
+ row['Profile'] = prof_name
605
+
606
+ rows.append(row)
607
+
608
+ df = pd.DataFrame(rows)
609
+
610
+ # Reorder columns
611
+ if 'Profile' in df.columns:
612
+ df = df[['River', 'Reach', 'Station', 'Profile', 'WSE']]
613
+ else:
614
+ df = df[['River', 'Reach', 'Station', 'WSE']]
615
+
616
+ logger.info(
617
+ f"Extracted WSE data for {len(profiles_to_extract)} profile(s), "
618
+ f"{num_xs} cross sections"
619
+ )
620
+
621
+ return df
622
+
623
+ except FileNotFoundError:
624
+ raise FileNotFoundError(f"HDF file not found: {hdf_path}")
625
+ except KeyError as e:
626
+ raise KeyError(f"Error accessing steady state WSE data: {str(e)}")
627
+ except Exception as e:
628
+ raise RuntimeError(f"Error reading steady state WSE data: {str(e)}")
629
+
630
+ @staticmethod
631
+ @log_call
632
+ @standardize_input(file_type='plan_hdf')
633
+ def get_steady_info(hdf_path: Path) -> pd.DataFrame:
634
+ """
635
+ Get steady flow attributes and metadata from HEC-RAS HDF plan file.
636
+
637
+ Args:
638
+ hdf_path (Path): Path to HEC-RAS plan HDF file
639
+ ras_object (RasPrj, optional): Specific RAS object to use. If None, uses the global ras instance.
640
+
641
+ Returns:
642
+ pd.DataFrame: DataFrame containing steady flow attributes including:
643
+ - Program Name
644
+ - Program Version
645
+ - Type of Run
646
+ - Run Time Window
647
+ - Solution status
648
+ - And other metadata attributes
649
+
650
+ Raises:
651
+ FileNotFoundError: If the specified HDF file is not found
652
+ KeyError: If steady state results are not found
653
+ ValueError: If the plan is not a steady state plan
654
+
655
+ Example:
656
+ >>> info_df = HdfResultsPlan.get_steady_info('01')
657
+ >>> print(info_df['Solution'].values[0])
658
+ 'Steady Finished Successfully'
659
+ """
660
+ try:
661
+ with h5py.File(hdf_path, 'r') as hdf_file:
662
+ # Check if this is a steady state plan
663
+ if "Results/Steady" not in hdf_file:
664
+ raise ValueError(f"HDF file does not contain steady state results: {hdf_path.name}")
665
+
666
+ attrs_dict = {}
667
+
668
+ # Get attributes from Results/Steady/Output
669
+ output_path = "Results/Steady/Output"
670
+ if output_path in hdf_file:
671
+ output_group = hdf_file[output_path]
672
+ for key, value in output_group.attrs.items():
673
+ if isinstance(value, bytes):
674
+ attrs_dict[key] = value.decode('utf-8')
675
+ else:
676
+ attrs_dict[key] = value
677
+
678
+ # Get attributes from Results/Steady/Summary
679
+ summary_path = "Results/Steady/Summary"
680
+ if summary_path in hdf_file:
681
+ summary_group = hdf_file[summary_path]
682
+ for key, value in summary_group.attrs.items():
683
+ if isinstance(value, bytes):
684
+ attrs_dict[key] = value.decode('utf-8')
685
+ else:
686
+ attrs_dict[key] = value
687
+
688
+ # Add flow file information from Plan Data
689
+ plan_info_path = "Plan Data/Plan Information"
690
+ if plan_info_path in hdf_file:
691
+ plan_info = hdf_file[plan_info_path]
692
+ for key in ['Flow Filename', 'Flow Title']:
693
+ if key in plan_info.attrs:
694
+ value = plan_info.attrs[key]
695
+ if isinstance(value, bytes):
696
+ attrs_dict[key] = value.decode('utf-8')
697
+ else:
698
+ attrs_dict[key] = value
699
+
700
+ if not attrs_dict:
701
+ logger.warning("No steady state attributes found in HDF file")
702
+ return pd.DataFrame()
703
+
704
+ logger.info(f"Extracted {len(attrs_dict)} steady state attributes")
705
+ return pd.DataFrame(attrs_dict, index=[0])
706
+
707
+ except FileNotFoundError:
708
+ raise FileNotFoundError(f"HDF file not found: {hdf_path}")
709
+ except KeyError as e:
710
+ raise KeyError(f"Error accessing steady state info: {str(e)}")
711
+ except Exception as e:
712
+ raise RuntimeError(f"Error reading steady state info: {str(e)}")
ras_commander/RasPrj.py CHANGED
@@ -116,7 +116,7 @@ class RasPrj:
116
116
  self.suppress_logging = False # Add suppress_logging as instance variable
117
117
 
118
118
  @log_call
119
- def initialize(self, project_folder, ras_exe_path, suppress_logging=True):
119
+ def initialize(self, project_folder, ras_exe_path, suppress_logging=True, prj_file=None):
120
120
  """
121
121
  Initialize a RasPrj instance with project folder and RAS executable path.
122
122
 
@@ -127,13 +127,16 @@ class RasPrj:
127
127
  project_folder (str or Path): Path to the HEC-RAS project folder.
128
128
  ras_exe_path (str or Path): Path to the HEC-RAS executable.
129
129
  suppress_logging (bool, default=True): If True, suppresses initialization logging messages.
130
+ prj_file (str or Path, optional): If provided, use this specific .prj file instead of searching.
131
+ This is used when user specifies a .prj file directly.
130
132
 
131
133
  Raises:
132
- ValueError: If no HEC-RAS project file is found in the specified folder.
134
+ ValueError: If no HEC-RAS project file is found in the specified folder,
135
+ or if the specified prj_file doesn't exist or is invalid.
133
136
 
134
137
  Note:
135
138
  This method sets up the RasPrj instance by:
136
- 1. Finding the project file (.prj)
139
+ 1. Finding the project file (.prj) or using the provided prj_file
137
140
  2. Loading project data (plans, geometries, flows)
138
141
  3. Extracting boundary conditions
139
142
  4. Setting the initialization flag
@@ -141,10 +144,21 @@ class RasPrj:
141
144
  """
142
145
  self.suppress_logging = suppress_logging # Store suppress_logging state
143
146
  self.project_folder = Path(project_folder)
144
- self.prj_file = self.find_ras_prj(self.project_folder)
145
- if self.prj_file is None:
146
- logger.error(f"No HEC-RAS project file found in {self.project_folder}")
147
- raise ValueError(f"No HEC-RAS project file found in {self.project_folder}. Please check the path and try again.")
147
+
148
+ # If user specified a .prj file directly, use it (Phase 2 optimization)
149
+ if prj_file is not None:
150
+ self.prj_file = Path(prj_file).resolve()
151
+ if not self.prj_file.exists():
152
+ logger.error(f"Specified .prj file does not exist: {self.prj_file}")
153
+ raise ValueError(f"Specified .prj file does not exist: {self.prj_file}. Please check the path and try again.")
154
+ logger.debug(f"Using specified .prj file: {self.prj_file}")
155
+ else:
156
+ # Search for .prj file (existing behavior)
157
+ self.prj_file = self.find_ras_prj(self.project_folder)
158
+ if self.prj_file is None:
159
+ logger.error(f"No HEC-RAS project file found in {self.project_folder}")
160
+ raise ValueError(f"No HEC-RAS project file found in {self.project_folder}. Please check the path and try again.")
161
+
148
162
  self.project_name = Path(self.prj_file).stem
149
163
  self.ras_exe_path = ras_exe_path
150
164
 
@@ -204,10 +218,19 @@ class RasPrj:
204
218
 
205
219
  # Set paths for geometry and flow files
206
220
  self._set_file_paths()
207
-
221
+
208
222
  # Make sure all plan paths are properly set
209
223
  self._set_plan_paths()
210
-
224
+
225
+ # Add flow_type column for deterministic steady/unsteady identification
226
+ if not self.plan_df.empty and 'unsteady_number' in self.plan_df.columns:
227
+ self.plan_df['flow_type'] = self.plan_df['unsteady_number'].apply(
228
+ lambda x: 'Unsteady' if pd.notna(x) else 'Steady'
229
+ )
230
+ else:
231
+ if not self.plan_df.empty:
232
+ self.plan_df['flow_type'] = 'Unknown'
233
+
211
234
  except Exception as e:
212
235
  logger.error(f"Error loading project data: {e}")
213
236
  raise
@@ -1265,13 +1288,18 @@ def init_ras_project(ras_project_folder, ras_version=None, ras_object=None):
1265
1288
  Initialize a RAS project for use with the ras-commander library.
1266
1289
 
1267
1290
  This is the primary function for setting up a HEC-RAS project. It:
1268
- 1. Finds the project file (.prj) in the specified folder
1269
- 2. Identifies the appropriate HEC-RAS executable
1270
- 3. Loads project data (plans, geometries, flows)
1271
- 4. Creates dataframes containing project components
1291
+ 1. Finds the project file (.prj) in the specified folder OR uses the provided .prj file
1292
+ 2. Validates .prj files by checking for "Proj Title=" marker
1293
+ 3. Identifies the appropriate HEC-RAS executable
1294
+ 4. Loads project data (plans, geometries, flows)
1295
+ 5. Creates dataframes containing project components
1272
1296
 
1273
1297
  Args:
1274
- ras_project_folder (str or Path): The path to the RAS project folder.
1298
+ ras_project_folder (str or Path): Path to the RAS project folder OR direct path to a .prj file.
1299
+ If a .prj file is provided:
1300
+ - File is validated to have .prj extension
1301
+ - File content is checked for "Proj Title=" marker
1302
+ - Parent folder is used as the project folder
1275
1303
  ras_version (str, optional): The version of RAS to use (e.g., "6.6") OR
1276
1304
  a full path to the Ras.exe file (e.g., "D:/Programs/HEC/HEC-RAS/6.6/Ras.exe").
1277
1305
  If None, will attempt to detect from plan files.
@@ -1281,25 +1309,71 @@ def init_ras_project(ras_project_folder, ras_version=None, ras_object=None):
1281
1309
 
1282
1310
  Returns:
1283
1311
  RasPrj: An initialized RasPrj instance.
1284
-
1312
+
1285
1313
  Raises:
1286
- FileNotFoundError: If the specified project folder doesn't exist.
1287
- ValueError: If no HEC-RAS project file is found in the folder.
1288
-
1314
+ FileNotFoundError: If the specified project folder or .prj file doesn't exist.
1315
+ ValueError: If the provided file is not a .prj file, does not contain "Proj Title=",
1316
+ or if no HEC-RAS project file is found in the folder.
1317
+
1289
1318
  Example:
1290
- >>> # Initialize using the global 'ras' object (most common)
1319
+ >>> # Initialize using project folder (existing behavior)
1291
1320
  >>> init_ras_project("/path/to/project", "6.6")
1292
1321
  >>> print(f"Initialized project: {ras.project_name}")
1293
1322
  >>>
1294
- >>> # Create a new RasPrj instance
1295
- >>> my_project = init_ras_project("/path/to/project", "6.6", "new")
1323
+ >>> # Initialize using direct .prj file path (new feature)
1324
+ >>> init_ras_project("/path/to/project/MyModel.prj", "6.6")
1325
+ >>> print(f"Initialized project: {ras.project_name}")
1326
+ >>>
1327
+ >>> # Create a new RasPrj instance with .prj file
1328
+ >>> my_project = init_ras_project("/path/to/project/MyModel.prj", "6.6", "new")
1296
1329
  >>> print(f"Created project instance: {my_project.project_name}")
1297
1330
  """
1298
- # Convert to absolute path immediately to ensure consistent path handling
1299
- project_folder = Path(ras_project_folder).resolve()
1300
- if not project_folder.exists():
1301
- logger.error(f"The specified RAS project folder does not exist: {project_folder}")
1302
- raise FileNotFoundError(f"The specified RAS project folder does not exist: {project_folder}. Please check the path and try again.")
1331
+ # Convert to Path object for consistent handling
1332
+ input_path = Path(ras_project_folder).resolve()
1333
+
1334
+ # Detect if input is a file or folder
1335
+ if input_path.is_file():
1336
+ # User provided a .prj file path
1337
+ if input_path.suffix.lower() != '.prj':
1338
+ error_msg = f"The provided file is not a HEC-RAS project file (.prj): {input_path}"
1339
+ logger.error(error_msg)
1340
+ raise ValueError(f"{error_msg}. Please provide either a project folder or a .prj file.")
1341
+
1342
+ # Enhanced validation: Check if file contains "Proj Title=" to verify it's a HEC-RAS project file
1343
+ try:
1344
+ content, encoding = read_file_with_fallback_encoding(input_path)
1345
+ if content is None or "Proj Title=" not in content:
1346
+ error_msg = f"The file does not appear to be a valid HEC-RAS project file (missing 'Proj Title='): {input_path}"
1347
+ logger.error(error_msg)
1348
+ raise ValueError(f"{error_msg}. Please provide a valid HEC-RAS .prj file.")
1349
+ logger.debug(f"Validated .prj file contains 'Proj Title=' marker")
1350
+ except Exception as e:
1351
+ error_msg = f"Error validating .prj file: {e}"
1352
+ logger.error(error_msg)
1353
+ raise ValueError(f"{error_msg}. Please ensure the file is a valid HEC-RAS project file.")
1354
+
1355
+ # Extract the parent folder to use as project_folder
1356
+ project_folder = input_path.parent
1357
+ specified_prj_file = input_path # Store for optimization
1358
+ logger.debug(f"User provided .prj file: {input_path}")
1359
+ logger.debug(f"Using parent folder as project_folder: {project_folder}")
1360
+
1361
+ elif input_path.is_dir():
1362
+ # User provided a folder path (existing behavior)
1363
+ project_folder = input_path
1364
+ specified_prj_file = None
1365
+ logger.debug(f"User provided folder path: {project_folder}")
1366
+
1367
+ else:
1368
+ # Path doesn't exist
1369
+ if input_path.suffix.lower() == '.prj':
1370
+ error_msg = f"The specified .prj file does not exist: {input_path}"
1371
+ logger.error(error_msg)
1372
+ raise FileNotFoundError(f"{error_msg}. Please check the path and try again.")
1373
+ else:
1374
+ error_msg = f"The specified RAS project folder does not exist: {input_path}"
1375
+ logger.error(error_msg)
1376
+ raise FileNotFoundError(f"{error_msg}. Please check the path and try again.")
1303
1377
 
1304
1378
  # Determine which RasPrj instance to use
1305
1379
  if ras_object is None:
@@ -1372,13 +1446,20 @@ def init_ras_project(ras_project_folder, ras_version=None, ras_object=None):
1372
1446
  logger.warning("No valid HEC-RAS version was detected. Running HEC-RAS will fail.")
1373
1447
 
1374
1448
  # Initialize or re-initialize with the determined executable path
1375
- ras_object.initialize(project_folder, ras_exe_path)
1376
-
1449
+ # Pass specified_prj_file to avoid re-searching when user provided .prj file directly
1450
+ if specified_prj_file is not None:
1451
+ ras_object.initialize(project_folder, ras_exe_path, prj_file=specified_prj_file)
1452
+ else:
1453
+ ras_object.initialize(project_folder, ras_exe_path)
1454
+
1377
1455
  # Always update the global ras object as well
1378
1456
  if ras_object is not ras:
1379
- ras.initialize(project_folder, ras_exe_path)
1457
+ if specified_prj_file is not None:
1458
+ ras.initialize(project_folder, ras_exe_path, prj_file=specified_prj_file)
1459
+ else:
1460
+ ras.initialize(project_folder, ras_exe_path)
1380
1461
  logger.debug("Global 'ras' object also updated to match the new project.")
1381
-
1462
+
1382
1463
  logger.debug(f"Project initialized. Project folder: {ras_object.project_folder}")
1383
1464
  logger.debug(f"Using HEC-RAS executable: {ras_exe_path}")
1384
1465
  return ras_object
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.80.2"
13
+ __version__ = "0.81.0"
14
14
 
15
15
  # Set up logging
16
16
  setup_logging()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ras-commander
3
- Version: 0.80.2
3
+ Version: 0.81.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.
@@ -9,7 +9,7 @@ ras_commander/HdfPlan.py,sha256=WINI3lp865cE99QXztgvKKIhVUTOqu4X41ZPBfhYJGU,1214
9
9
  ras_commander/HdfPlot.py,sha256=7MNI5T9qIz-Ava1RdlnB6O9oJElE5BEB29QVF5Y2Xuc,3401
10
10
  ras_commander/HdfPump.py,sha256=Vc2ff16kRISR7jwtnaAqxI0p-gfBSuZKzR3rQbBLQoE,12951
11
11
  ras_commander/HdfResultsMesh.py,sha256=FIUN_yZ6h1XrcaHuOdAQrozOBN_UKNNn3_l8lO703dk,44172
12
- ras_commander/HdfResultsPlan.py,sha256=gmeAceYHABubv0V8dJL2F5GN4Th7g92E_1y-Hc4JZjg,16626
12
+ ras_commander/HdfResultsPlan.py,sha256=wsIUTyBFowJXeNwMsX0cZ00srqJDEwx5wENPpJLzatw,31479
13
13
  ras_commander/HdfResultsPlot.py,sha256=ylzfT78CfgoDO0XAlRwlgMNRzvNQYBMn9eyXyBfjv_w,7660
14
14
  ras_commander/HdfResultsXsec.py,sha256=-P7nXnbjOLAeUnrdSC_lJQSfzrlWKmDF9Z5gEjmxbJY,13031
15
15
  ras_commander/HdfStruc.py,sha256=SznjPvBWiqNVtviZvfCdjB_iwZF4UXxlIxITD8kwjP4,13733
@@ -21,12 +21,12 @@ ras_commander/RasExamples.py,sha256=QFWnWnxACpQzewzA3QFMp4z4iEkg5PWf9cPDdMay7MA,
21
21
  ras_commander/RasGeo.py,sha256=Wy5N1yP7_St3cA3ENJliojQ2sb2w2dL8Fy8L_sZsykc,22208
22
22
  ras_commander/RasMap.py,sha256=20db61KkUz2CgjfCCYY8F-IYy5doHOtdnTKChiK0ENs,20257
23
23
  ras_commander/RasPlan.py,sha256=ogIpLqawXTsjLnKRZTqzZydn_EFVJZFZZGgHvJ_t_-c,65408
24
- ras_commander/RasPrj.py,sha256=oXPWuNC_403mUfqyysXyBVul0Xtz_0SKDxZdj4fvNYU,63448
24
+ ras_commander/RasPrj.py,sha256=30y6kJ4jcfX7dE2BB5w7KHIHP1vUVlfB-YWM0xBNhbg,68031
25
25
  ras_commander/RasUnsteady.py,sha256=PdQQMiY7Mz1EsOQk6ygFQtlC2sFEa96Ntg-pznWVpLQ,37187
26
26
  ras_commander/RasUtils.py,sha256=0fm4IIs0LH1dgDj3pGd66mR82DhWLEkRKUvIo2M_5X0,35886
27
- ras_commander/__init__.py,sha256=h27KNGTejtJoQ-Fk8rC7KmLiZx2IKAMyXRts7zuGw-w,2039
28
- ras_commander-0.80.2.dist-info/licenses/LICENSE,sha256=_pbd6qHnlsz1iQ-ozDW_49r86BZT6CRwO2iBtw0iN6M,457
29
- ras_commander-0.80.2.dist-info/METADATA,sha256=cQIheuTvKMWe1sVNecRfyCK0b9j4hBjKjBgpisnRKII,27941
30
- ras_commander-0.80.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
31
- ras_commander-0.80.2.dist-info/top_level.txt,sha256=i76S7eKLFC8doKcXDl3aiOr9RwT06G8adI6YuKbQDaA,14
32
- ras_commander-0.80.2.dist-info/RECORD,,
27
+ ras_commander/__init__.py,sha256=cceh-WJ7RMhQjPUH7ztMO5sWZDUx5jMsOEtrnOPMYMU,2039
28
+ ras_commander-0.81.0.dist-info/licenses/LICENSE,sha256=_pbd6qHnlsz1iQ-ozDW_49r86BZT6CRwO2iBtw0iN6M,457
29
+ ras_commander-0.81.0.dist-info/METADATA,sha256=pwshM3miZgeD-5tQAZaBLUb-HKzw5gaCM06OCFrkTgE,27941
30
+ ras_commander-0.81.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
31
+ ras_commander-0.81.0.dist-info/top_level.txt,sha256=i76S7eKLFC8doKcXDl3aiOr9RwT06G8adI6YuKbQDaA,14
32
+ ras_commander-0.81.0.dist-info/RECORD,,