ras-commander 0.80.2__py3-none-any.whl → 0.80.3__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/HdfResultsPlan.py +338 -7
 - ras_commander/RasPrj.py +11 -2
 - ras_commander/__init__.py +1 -1
 - {ras_commander-0.80.2.dist-info → ras_commander-0.80.3.dist-info}/METADATA +1 -1
 - {ras_commander-0.80.2.dist-info → ras_commander-0.80.3.dist-info}/RECORD +8 -8
 - {ras_commander-0.80.2.dist-info → ras_commander-0.80.3.dist-info}/WHEEL +0 -0
 - {ras_commander-0.80.2.dist-info → ras_commander-0.80.3.dist-info}/licenses/LICENSE +0 -0
 - {ras_commander-0.80.2.dist-info → ras_commander-0.80.3.dist-info}/top_level.txt +0 -0
 
    
        ras_commander/HdfResultsPlan.py
    CHANGED
    
    | 
         @@ -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, 
     | 
| 
       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 
     | 
    
         
            -
                 
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
      
 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
    
    | 
         @@ -204,10 +204,19 @@ class RasPrj: 
     | 
|
| 
       204 
204 
     | 
    
         | 
| 
       205 
205 
     | 
    
         
             
                        # Set paths for geometry and flow files
         
     | 
| 
       206 
206 
     | 
    
         
             
                        self._set_file_paths()
         
     | 
| 
       207 
     | 
    
         
            -
             
     | 
| 
      
 207 
     | 
    
         
            +
             
     | 
| 
       208 
208 
     | 
    
         
             
                        # Make sure all plan paths are properly set
         
     | 
| 
       209 
209 
     | 
    
         
             
                        self._set_plan_paths()
         
     | 
| 
       210 
     | 
    
         
            -
             
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 211 
     | 
    
         
            +
                        # Add flow_type column for deterministic steady/unsteady identification
         
     | 
| 
      
 212 
     | 
    
         
            +
                        if not self.plan_df.empty and 'unsteady_number' in self.plan_df.columns:
         
     | 
| 
      
 213 
     | 
    
         
            +
                            self.plan_df['flow_type'] = self.plan_df['unsteady_number'].apply(
         
     | 
| 
      
 214 
     | 
    
         
            +
                                lambda x: 'Unsteady' if pd.notna(x) else 'Steady'
         
     | 
| 
      
 215 
     | 
    
         
            +
                            )
         
     | 
| 
      
 216 
     | 
    
         
            +
                        else:
         
     | 
| 
      
 217 
     | 
    
         
            +
                            if not self.plan_df.empty:
         
     | 
| 
      
 218 
     | 
    
         
            +
                                self.plan_df['flow_type'] = 'Unknown'
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
       211 
220 
     | 
    
         
             
                    except Exception as e:
         
     | 
| 
       212 
221 
     | 
    
         
             
                        logger.error(f"Error loading project data: {e}")
         
     | 
| 
       213 
222 
     | 
    
         
             
                        raise
         
     | 
    
        ras_commander/__init__.py
    CHANGED
    
    
| 
         @@ -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= 
     | 
| 
      
 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= 
     | 
| 
      
 24 
     | 
    
         
            +
            ras_commander/RasPrj.py,sha256=g9J_ewWrT0K1OP4BdEauU6VWq67m_JUqFXEr1AVIy2k,63893
         
     | 
| 
       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= 
     | 
| 
       28 
     | 
    
         
            -
            ras_commander-0.80. 
     | 
| 
       29 
     | 
    
         
            -
            ras_commander-0.80. 
     | 
| 
       30 
     | 
    
         
            -
            ras_commander-0.80. 
     | 
| 
       31 
     | 
    
         
            -
            ras_commander-0.80. 
     | 
| 
       32 
     | 
    
         
            -
            ras_commander-0.80. 
     | 
| 
      
 27 
     | 
    
         
            +
            ras_commander/__init__.py,sha256=v_fxJffY8lF74jv5gaihsKcLNE-b37dr5uJ6GvLx-Co,2039
         
     | 
| 
      
 28 
     | 
    
         
            +
            ras_commander-0.80.3.dist-info/licenses/LICENSE,sha256=_pbd6qHnlsz1iQ-ozDW_49r86BZT6CRwO2iBtw0iN6M,457
         
     | 
| 
      
 29 
     | 
    
         
            +
            ras_commander-0.80.3.dist-info/METADATA,sha256=kiXYlVO31V56GC9n0JmU-tjQucNV5yHnXyKqloi65bY,27941
         
     | 
| 
      
 30 
     | 
    
         
            +
            ras_commander-0.80.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
         
     | 
| 
      
 31 
     | 
    
         
            +
            ras_commander-0.80.3.dist-info/top_level.txt,sha256=i76S7eKLFC8doKcXDl3aiOr9RwT06G8adI6YuKbQDaA,14
         
     | 
| 
      
 32 
     | 
    
         
            +
            ras_commander-0.80.3.dist-info/RECORD,,
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     |