2D Geometry File Parsing¶
This notebook demonstrates parsing and manipulating 2D geometry elements from HEC-RAS plain text geometry files, including: - Storage Area operations (BaldEagleCrkMulti2D) - SA/2D Connection operations and dam crest profiles (BaldEagleCrkMulti2D) - Gate extraction (Davis project) - Practical dam breach workflow using both projects
Overview¶
This notebook demonstrates extraction of 2D geometry elements from HEC-RAS geometry files. 2D areas use computational meshes rather than cross sections.
What You'll Learn¶
- Extract storage areas and elevation-volume curves
- List SA/2D connections and dam crest profiles
- Pull gate configurations from a project that includes gates (Davis)
- Use multiple RasPrj objects to target specific projects
LLM Forward Approach¶
- Verification: Compare storage curves and connection profiles with HEC-RAS GUI
- Visual Outputs: Plot storage curves and crest profiles for quick review
- Audit Trail: Keep geometry file paths and ras_object context explicit
Reference Documentation¶
# =============================================================================
# DEVELOPMENT MODE TOGGLE
# =============================================================================
from pathlib import Path
import sys
USE_LOCAL_SOURCE = True # <-- TOGGLE THIS
if USE_LOCAL_SOURCE:
local_path = str(Path.cwd().parent)
if local_path not in sys.path:
sys.path.insert(0, local_path)
print(f"LOCAL SOURCE MODE: Loading from {local_path}/ras_commander")
else:
print("PIP PACKAGE MODE: Loading installed ras-commander")
# Import RAS Commander geometry modules
from ras_commander import (
RasGeometry, GeomLateral, GeomLandCover,
RasGeometryUtils,
HdfHydraulicTables,
GeomStorage, HdfStruc,
RasExamples,
init_ras_project,
RasCmdr,
RasPrj
)
# Additional imports
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from IPython import display
# Verify which version loaded
import ras_commander
print(f"Loaded: {ras_commander.__file__}")
print(f"Working directory: {Path.cwd()}")
LOCAL SOURCE MODE: Loading from <repo>/ras_commander
2026-06-11 15:45:56 - numexpr.utils - INFO - NumExpr defaulting to 8 threads.
Loaded: <repo>\ras_commander\__init__.py
Working directory: <repo>\examples
Parameters¶
Configure these values to customize the notebook for your project.
# =============================================================================
# PARAMETERS - Edit these to customize the notebook
# =============================================================================
from pathlib import Path
# Project Configuration
PROJECT_NAME_2D = "BaldEagleCrkMulti2D" # 2D geometry source project
PROJECT_NAME_GATES = "Davis" # Gate information source project
PROJECT_SUFFIX = "202" # Folder suffix for extracted projects
RAS_VERSION = "7.0" # HEC-RAS version (6.3, 6.5, 6.6, etc.)
# Geometry Settings
GEOM_NUMBER_2D = "12" # Geometry file with SA/2D connections (BaldEagle)
GATE_GEOM_NUMBER = None # Optional: set if you know the Davis geometry with gates
RIVER = None # Not applicable for 2D geometry
REACH = None # Not applicable for 2D geometry
CROSS_SECTION = None # Not applicable for 2D geometry
Package Setup and Imports¶
# Extract and initialize the example projects
project_paths = RasExamples.extract_project(
[PROJECT_NAME_2D, PROJECT_NAME_GATES],
suffix=PROJECT_SUFFIX
)
bald_eagle_path = project_paths[0]
davis_path = project_paths[1]
# Initialize each project with its own RasPrj instance
bald_eagle_ras = RasPrj()
init_ras_project(bald_eagle_path, RAS_VERSION, ras_object=bald_eagle_ras)
print(f"Bald Eagle project initialized: {bald_eagle_ras.project_name}")
print(f"Found {len(bald_eagle_ras.geom_df)} geometry files")
print(f"Found {len(bald_eagle_ras.plan_df)} plan files")
# Initialize Davis for gate information
# (use a separate RasPrj to keep project state isolated)
davis_ras = RasPrj()
init_ras_project(davis_path, RAS_VERSION, ras_object=davis_ras)
print("")
print(f"Davis project initialized: {davis_ras.project_name}")
print(f"Found {len(davis_ras.geom_df)} geometry files")
print(f"Found {len(davis_ras.plan_df)} plan files")
def find_gate_connections(ras_object, geom_number=None):
# Find a geometry file and connection table that includes gate definitions.
if geom_number:
geom_row = ras_object.geom_df.loc[
ras_object.geom_df['geom_number'] == geom_number
]
if geom_row.empty:
raise ValueError(
f"Geometry number {geom_number} not found in {ras_object.project_name}"
)
geom_file = Path(geom_row.iloc[0]["full_path"])
connections = RasGeometry.get_connections(geom_file)
if connections.empty or "HasGate" not in connections.columns:
return geom_file, connections, pd.DataFrame()
gate_connections = connections[connections['HasGate'] > 0]
return geom_file, connections, gate_connections
for _, row in ras_object.geom_df.iterrows():
geom_file = Path(row["full_path"])
connections = RasGeometry.get_connections(geom_file)
if connections.empty or "HasGate" not in connections.columns:
continue
gate_connections = connections[connections['HasGate'] > 0]
if len(gate_connections) > 0:
return geom_file, connections, gate_connections
return None, pd.DataFrame(), pd.DataFrame()
davis_gate_geom_file, davis_connections, davis_gate_connections = (
find_gate_connections(davis_ras, GATE_GEOM_NUMBER)
)
if davis_gate_geom_file is not None:
print("")
print(f"Gate geometry identified: {davis_gate_geom_file.name}")
print(f"Gate connections found: {len(davis_gate_connections)}")
else:
print("")
print("No gate connections found in Davis project")
2026-06-11 15:45:59 - ras_commander.RasExamples - INFO - Successfully extracted project 'BaldEagleCrkMulti2D' to <repo>\examples\example_projects\BaldEagleCrkMulti2D_202
2026-06-11 15:45:59 - ras_commander.RasExamples - INFO - Successfully extracted project 'Davis' to <repo>\examples\example_projects\Davis_202
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 7.0 at C:\Program Files (x86)\HEC\HEC-RAS\7.0\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.7 Beta 5 at C:\Program Files (x86)\HEC\HEC-RAS\6.7 Beta 5\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.7 Beta 4 at C:\Program Files (x86)\HEC\HEC-RAS\6.7 Beta 4\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.6 at C:\Program Files (x86)\HEC\HEC-RAS\6.6\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.5 at C:\Program Files (x86)\HEC\HEC-RAS\6.5\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.4.1 at C:\Program Files (x86)\HEC\HEC-RAS\6.4.1\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.3.1 at C:\Program Files (x86)\HEC\HEC-RAS\6.3.1\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.3 at C:\Program Files (x86)\HEC\HEC-RAS\6.3\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.2 at C:\Program Files (x86)\HEC\HEC-RAS\6.2\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.1 at C:\Program Files (x86)\HEC\HEC-RAS\6.1\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.0 at C:\Program Files (x86)\HEC\HEC-RAS\6.0\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 5.0.7 at C:\Program Files (x86)\HEC\HEC-RAS\5.0.7\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 5.0.6 at C:\Program Files (x86)\HEC\HEC-RAS\5.0.6\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 5.0.5 at C:\Program Files (x86)\HEC\HEC-RAS\5.0.5\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 5.0.4 at C:\Program Files (x86)\HEC\HEC-RAS\5.0.4\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 5.0.3 at C:\Program Files (x86)\HEC\HEC-RAS\5.0.3\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 5.0.1 at C:\Program Files (x86)\HEC\HEC-RAS\5.0.1\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 5.0 at C:\Program Files (x86)\HEC\HEC-RAS\5.0\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 4.1.0 at C:\Program Files (x86)\HEC\HEC-RAS\4.1.0\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 4.0 at C:\Program Files (x86)\HEC\HEC-RAS\4.0\Ras.exe via filesystem (x86)
2026-06-11 15:45:59 - ras_commander.RasUtils - INFO - Discovered 20 installed HEC-RAS version(s)
2026-06-11 15:45:59 - ras_commander.RasPrj - INFO - HEC-RAS 7.0 found via version discovery: C:\Program Files (x86)\HEC\HEC-RAS\7.0\Ras.exe
2026-06-11 15:45:59 - ras_commander.RasMap - INFO - Successfully parsed RASMapper file: <repo>\examples\example_projects\BaldEagleCrkMulti2D_202\BaldEagleDamBrk.rasmap
2026-06-11 15:46:00 - ras_commander.RasPrj - INFO - ras-commander v0.98.0 | An open-source project of CLB Engineering Corporation (https://clbengineering.com/) | Docs: https://rascommander.info | GitHub: https://github.com/gpt-cmdr/ras-commander
2026-06-11 15:46:00 - ras_commander.RasPrj - INFO - Project initialized: BaldEagleDamBrk | Folder: <repo>\examples\example_projects\BaldEagleCrkMulti2D_202
2026-06-11 15:46:00 - ras_commander.RasPrj - INFO - Using HEC-RAS executable: C:\Program Files (x86)\HEC\HEC-RAS\7.0\Ras.exe
2026-06-11 15:46:00 - ras_commander.RasPrj - INFO -
═══════════════════════════════════════════════════════════════════════
ras-commander | HEC-RAS Automation Library
Docs: https://rascommander.info/
Repo: https://github.com/gpt-cmdr/ras-commander
═══════════════════════════════════════════════════════════════════════
PROJECT DATAFRAMES (single source of truth — use these, not file globbing):
ras_object.plan_df Plans, HDF paths, geometry/flow associations
ras_object.geom_df Geometry files and HDF preprocessor paths
ras_object.flow_df Steady flow files
ras_object.unsteady_df Unsteady flow files and configurations
ras_object.boundaries_df Boundary conditions (type, name, location)
ras_object.results_df Lightweight HDF results summaries
ras_object.rasmap_df RASMapper layers, terrain, land cover paths
KEY APIS (static classes — call directly, never instantiate):
Execution: RasCmdr.compute_plan() / compute_parallel() / compute_test_mode()
Plan Files: RasPlan.clone_plan() / clone_geom() / set_geom()
Unsteady: RasUnsteady — IC/BC management, gate openings, precipitation
Geometry: GeomCrossSection, GeomBridge, GeomStorage, GeomLateral, GeomMesh
HDF Results: HdfResultsPlan.get_wse() / get_compute_messages()
HdfResultsMesh.get_mesh_max_ws() / get_mesh_cells_timeseries()
HdfMesh.get_mesh_cell_points()
QA/QC: RasCheck.run_check() / RasFixit (geometry repair)
DSS: RasDss.get_timeseries() / check_pathname()
USGS: UsgsGaugeSpatial, GaugeMatcher, RasUsgsBoundaryGeneration
Precipitation: StormGenerator, Atlas14Storm, PrecipAorc, Atlas14Variance
Terrain: RasTerrain.create_terrain_hdf() / RasTerrainMod
MULTI-PROJECT: Pass ras_object= to all API calls when using local RasPrj instances.
EXAMPLES: 100+ notebooks in examples/ (100s=execution, 200s=geometry, 300s=unsteady,
400s=HDF results, 500s=remote, 800s=QA/QC, 900s=data integration).
Review relevant notebooks before assembling new workflows.
PLATFORM: Most HEC-RAS operations require Windows. Linux/Wine support for
headless execution, data access, geometry modification, and preprocessing
is available via RasProcess (HEC-RAS 6.6+). See ras_commander/RasProcess.py.
Remote distributed execution: ras_commander/remote/ (PsExec, Docker, SSH, cloud).
═══════════════════════════════════════════════════════════════════════
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 7.0 at C:\Program Files (x86)\HEC\HEC-RAS\7.0\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.7 Beta 5 at C:\Program Files (x86)\HEC\HEC-RAS\6.7 Beta 5\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.7 Beta 4 at C:\Program Files (x86)\HEC\HEC-RAS\6.7 Beta 4\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.6 at C:\Program Files (x86)\HEC\HEC-RAS\6.6\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.5 at C:\Program Files (x86)\HEC\HEC-RAS\6.5\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.4.1 at C:\Program Files (x86)\HEC\HEC-RAS\6.4.1\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.3.1 at C:\Program Files (x86)\HEC\HEC-RAS\6.3.1\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.3 at C:\Program Files (x86)\HEC\HEC-RAS\6.3\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.2 at C:\Program Files (x86)\HEC\HEC-RAS\6.2\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.1 at C:\Program Files (x86)\HEC\HEC-RAS\6.1\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 6.0 at C:\Program Files (x86)\HEC\HEC-RAS\6.0\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 5.0.7 at C:\Program Files (x86)\HEC\HEC-RAS\5.0.7\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 5.0.6 at C:\Program Files (x86)\HEC\HEC-RAS\5.0.6\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 5.0.5 at C:\Program Files (x86)\HEC\HEC-RAS\5.0.5\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 5.0.4 at C:\Program Files (x86)\HEC\HEC-RAS\5.0.4\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 5.0.3 at C:\Program Files (x86)\HEC\HEC-RAS\5.0.3\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 5.0.1 at C:\Program Files (x86)\HEC\HEC-RAS\5.0.1\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 5.0 at C:\Program Files (x86)\HEC\HEC-RAS\5.0\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 4.1.0 at C:\Program Files (x86)\HEC\HEC-RAS\4.1.0\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered HEC-RAS 4.0 at C:\Program Files (x86)\HEC\HEC-RAS\4.0\Ras.exe via filesystem (x86)
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Discovered 20 installed HEC-RAS version(s)
2026-06-11 15:46:00 - ras_commander.RasPrj - INFO - HEC-RAS 7.0 found via version discovery: C:\Program Files (x86)\HEC\HEC-RAS\7.0\Ras.exe
2026-06-11 15:46:00 - ras_commander.RasMap - INFO - Successfully parsed RASMapper file: <repo>\examples\example_projects\Davis_202\DavisStormSystem.rasmap
2026-06-11 15:46:00 - ras_commander.RasPrj - INFO - ras-commander v0.98.0 | An open-source project of CLB Engineering Corporation (https://clbengineering.com/) | Docs: https://rascommander.info | GitHub: https://github.com/gpt-cmdr/ras-commander
2026-06-11 15:46:00 - ras_commander.RasPrj - INFO - Project initialized: DavisStormSystem | Folder: <repo>\examples\example_projects\Davis_202
2026-06-11 15:46:00 - ras_commander.RasPrj - INFO - Using HEC-RAS executable: C:\Program Files (x86)\HEC\HEC-RAS\7.0\Ras.exe
2026-06-11 15:46:00 - ras_commander.RasPrj - INFO -
═══════════════════════════════════════════════════════════════════════
ras-commander | HEC-RAS Automation Library
Docs: https://rascommander.info/
Repo: https://github.com/gpt-cmdr/ras-commander
═══════════════════════════════════════════════════════════════════════
PROJECT DATAFRAMES (single source of truth — use these, not file globbing):
ras_object.plan_df Plans, HDF paths, geometry/flow associations
ras_object.geom_df Geometry files and HDF preprocessor paths
ras_object.flow_df Steady flow files
ras_object.unsteady_df Unsteady flow files and configurations
ras_object.boundaries_df Boundary conditions (type, name, location)
ras_object.results_df Lightweight HDF results summaries
ras_object.rasmap_df RASMapper layers, terrain, land cover paths
KEY APIS (static classes — call directly, never instantiate):
Execution: RasCmdr.compute_plan() / compute_parallel() / compute_test_mode()
Plan Files: RasPlan.clone_plan() / clone_geom() / set_geom()
Unsteady: RasUnsteady — IC/BC management, gate openings, precipitation
Geometry: GeomCrossSection, GeomBridge, GeomStorage, GeomLateral, GeomMesh
HDF Results: HdfResultsPlan.get_wse() / get_compute_messages()
HdfResultsMesh.get_mesh_max_ws() / get_mesh_cells_timeseries()
HdfMesh.get_mesh_cell_points()
QA/QC: RasCheck.run_check() / RasFixit (geometry repair)
DSS: RasDss.get_timeseries() / check_pathname()
USGS: UsgsGaugeSpatial, GaugeMatcher, RasUsgsBoundaryGeneration
Precipitation: StormGenerator, Atlas14Storm, PrecipAorc, Atlas14Variance
Terrain: RasTerrain.create_terrain_hdf() / RasTerrainMod
MULTI-PROJECT: Pass ras_object= to all API calls when using local RasPrj instances.
EXAMPLES: 100+ notebooks in examples/ (100s=execution, 200s=geometry, 300s=unsteady,
400s=HDF results, 500s=remote, 800s=QA/QC, 900s=data integration).
Review relevant notebooks before assembling new workflows.
PLATFORM: Most HEC-RAS operations require Windows. Linux/Wine support for
headless execution, data access, geometry modification, and preprocessing
is available via RasProcess (HEC-RAS 6.6+). See ras_commander/RasProcess.py.
Remote distributed execution: ras_commander/remote/ (PsExec, Docker, SSH, cloud).
═══════════════════════════════════════════════════════════════════════
Bald Eagle project initialized: BaldEagleDamBrk
Found 10 geometry files
Found 11 plan files
Davis project initialized: DavisStormSystem
Found 1 geometry files
Found 1 plan files
No gate connections found in Davis project
DEV NOTE: No gate connections found in Davis project - This needs to be mapped to a different example project that has gate operations.
Section 1: Storage Area Operations¶
Storage areas represent reservoirs, detention basins, or other water storage features in the HEC-RAS model. We'll demonstrate extracting storage area properties and elevation-volume curves.
1.1 List Storage Areas¶
Select Geometry 12 (SA to 2D Connection)¶
Geometry 12 in the BaldEagleCrkMulti2D project contains storage areas and SA/2D connections for dam breach analysis.
# Select geometry 12 which has SA/2D connections for dam breach analysis
bald_eagle_geom_number = GEOM_NUMBER_2D
geom_row = bald_eagle_ras.geom_df.loc[
bald_eagle_ras.geom_df['geom_number'] == bald_eagle_geom_number
]
if geom_row.empty:
raise ValueError(
f"Geometry number {bald_eagle_geom_number} not found in "
f"{bald_eagle_ras.project_name}"
)
bald_eagle_geom_file = Path(geom_row.iloc[0]["full_path"])
bald_eagle_geom_hdf = Path(geom_row.iloc[0]["hdf_path"])
print(f"Selected geometry (Bald Eagle): {bald_eagle_geom_file.name}")
print(
"Geometry HDF: "
f"{bald_eagle_geom_hdf.name if bald_eagle_geom_hdf.exists() else 'Not yet created'}"
)
Selected geometry (Bald Eagle): BaldEagleDamBrk.g12
Geometry HDF: BaldEagleDamBrk.g12.hdf
print("Bald Eagle geometry entries:") display.display(bald_eagle_ras.geom_df)
print("") print("Davis geometry entries:") display.display(davis_ras.geom_df)
# Path to dam breach geometry with storage areas (Bald Eagle)
dam_geom_file = bald_eagle_geom_file
print(f"Geometry file: {dam_geom_file.name}")
# Get storage areas (excluding 2D flow areas)
storage_areas = RasGeometry.get_storage_areas(dam_geom_file, exclude_2d=True)
print("")
print(f"Traditional storage areas found: {len(storage_areas)}")
for i, name in enumerate(storage_areas, 1):
print(f" {i}. {name}")
# Get all storage areas (including 2D)
all_storage = RasGeometry.get_storage_areas(dam_geom_file, exclude_2d=False)
print("")
print(f"All storage areas (including 2D): {len(all_storage)}")
for i, name in enumerate(all_storage, 1):
print(f" {i}. {name}")
Geometry file: BaldEagleDamBrk.g12
Traditional storage areas found: 1
1. Reservoir Pool
All storage areas (including 2D): 2
1. BaldEagleCr
2. Reservoir Pool
1.2 Extract Elevation-Volume Curve¶
# Get elevation-volume curve for first storage area
if len(storage_areas) > 0:
area_name = storage_areas[0]
print(f"Extracting elevation-volume curve for: {area_name}")
try:
elev_vol = RasGeometry.get_storage_elevation_volume(dam_geom_file, area_name)
print(f"\nStorage Curve Data:")
print(f" Points: {len(elev_vol)}")
print(f" Elevation range: {elev_vol['Elevation'].min():.2f} to {elev_vol['Elevation'].max():.2f} ft")
print(f" Volume range: {elev_vol['Volume'].min():.0f} to {elev_vol['Volume'].max():.0f} cu ft")
print("\nFirst 10 points:")
display.display(elev_vol.head(10))
except ValueError as e:
print(f"\nNote: Could not extract elevation-volume curve: {e}")
print("This may be a 2D flow area without traditional storage curve data.")
print("Trying second storage area if available...")
if len(storage_areas) > 1:
area_name = storage_areas[1]
print(f"\nTrying: {area_name}")
try:
elev_vol = RasGeometry.get_storage_elevation_volume(dam_geom_file, area_name)
print(f"Storage Curve Data:")
print(f" Points: {len(elev_vol)}")
display.display(elev_vol.head(10))
except ValueError as e2:
print(f"Could not extract from {area_name} either: {e2}")
elev_vol = pd.DataFrame({'Elevation': [], 'Volume': []})
else:
elev_vol = pd.DataFrame({'Elevation': [], 'Volume': []})
else:
print("No traditional storage areas found in this geometry file")
elev_vol = pd.DataFrame({'Elevation': [], 'Volume': []})
Extracting elevation-volume curve for: Reservoir Pool
Storage Curve Data:
Points: 52
Elevation range: 583.00 to 683.00 ft
Volume range: 0 to 212000 cu ft
First 10 points:
| Elevation | Volume | |
|---|---|---|
| 0 | 583.0 | 0.00 |
| 1 | 588.0 | 0.01 |
| 2 | 590.0 | 20.00 |
| 3 | 592.0 | 80.00 |
| 4 | 594.0 | 180.00 |
| 5 | 596.0 | 360.00 |
| 6 | 598.0 | 760.00 |
| 7 | 600.0 | 1390.00 |
| 8 | 602.0 | 2160.00 |
| 9 | 604.0 | 3030.00 |
1.3 Visualize Storage Curve¶
if len(storage_areas) > 0 and len(elev_vol) > 0:
# Plot elevation-volume curve
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
# Plot 1: Volume vs Elevation
ax1.plot(elev_vol['Volume'], elev_vol['Elevation'], 'b-', linewidth=2)
ax1.fill_betweenx(elev_vol['Elevation'], 0, elev_vol['Volume'], alpha=0.3)
ax1.set_xlabel('Storage Volume (cu ft)', fontsize=11)
ax1.set_ylabel('Elevation (ft)', fontsize=11)
ax1.set_title(f'Storage Curve: {area_name}', fontsize=12, fontweight='bold')
ax1.grid(True, alpha=0.3)
# Plot 2: Incremental Storage (dV/dZ)
elev_vol['dV'] = elev_vol['Volume'].diff()
elev_vol['dZ'] = elev_vol['Elevation'].diff()
elev_vol['Surface_Area'] = elev_vol['dV'] / elev_vol['dZ']
ax2.plot(elev_vol['Surface_Area'], elev_vol['Elevation'], 'r-', linewidth=2)
ax2.fill_betweenx(elev_vol['Elevation'], 0, elev_vol['Surface_Area'], alpha=0.3, color='red')
ax2.set_xlabel('Surface Area (sq ft)', fontsize=11)
ax2.set_ylabel('Elevation (ft)', fontsize=11)
ax2.set_title('Surface Area vs Elevation (dV/dZ)', fontsize=12, fontweight='bold')
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print("\nStorage Statistics:")
print(f" Total storage capacity: {elev_vol['Volume'].max():.0f} cu ft")
print(f" Elevation range: {elev_vol['Elevation'].max() - elev_vol['Elevation'].min():.1f} ft")
print(f" Average surface area: {elev_vol['Surface_Area'].mean():.0f} sq ft")
else:
print("Skipping storage visualization - no storage area data available")
print("Note: This geometry may use 2D flow areas instead of traditional storage areas.")

Storage Statistics:
Total storage capacity: 212000 cu ft
Elevation range: 100.0 ft
Average surface area: 2213 sq ft
Section 2: SA/2D Connection Operations¶
Connections link storage areas to 2D flow areas for dam breach analysis. We'll extract connection properties and dam crest profiles from BaldEagleCrkMulti2D, and pull gate information from the Davis project.
2.1 List All Connections¶
# Extract all connections (Bald Eagle geometry)
bald_eagle_connections = RasGeometry.get_connections(dam_geom_file)
print(f"SA/2D Connections found (Bald Eagle): {len(bald_eagle_connections)}")
if len(bald_eagle_connections) > 0:
print("")
print("Connection inventory:")
display_cols = ['Name', 'Type', 'From', 'To', 'NumPoints', 'HasGate']
display_cols = [c for c in display_cols if c in bald_eagle_connections.columns]
display.display(bald_eagle_connections[display_cols])
print("")
print("Connection summary:")
print(f" Total connections with gates: {bald_eagle_connections["HasGate"].sum():.0f}")
print(f" Connection types: {bald_eagle_connections["Type"].unique().tolist()}")
else:
print("No connections found in this geometry file")
SA/2D Connections found (Bald Eagle): 1
Connection inventory:
| Name | Type | From | To | NumPoints | HasGate | |
|---|---|---|---|---|---|---|
| 0 | Dam | SA to 2D | Reservoir Pool | BaldEagleCr | 6 | True |
Connection summary:
Total connections with gates: 1
Connection types: ['SA to 2D']
2.2 Extract Dam Crest Profile¶
if len(bald_eagle_connections) > 0:
# Get weir profile for first connection
conn_name = bald_eagle_connections.iloc[0]['Name']
print(f"Extracting weir profile for connection: {conn_name}")
print(f" Upstream: {bald_eagle_connections.iloc[0]['From']}")
print(f" Downstream: {bald_eagle_connections.iloc[0]['To']}")
weir_profile = RasGeometry.get_connection_weir_profile(dam_geom_file, conn_name)
print("")
print("Weir/Dam Crest Profile:")
print(f" Points: {len(weir_profile)}")
print(f" Station range: {weir_profile['Station'].min():.1f} to {weir_profile['Station'].max():.1f} ft")
print(f" Elevation range: {weir_profile['Elevation'].min():.2f} to {weir_profile['Elevation'].max():.2f} ft")
print(f" Crest length: {weir_profile['Station'].max() - weir_profile['Station'].min():.1f} ft")
print("")
print("First 10 points:")
display.display(weir_profile.head(10))
else:
print("Skipping weir profile extraction - no connections found")
Extracting weir profile for connection: Dam
Upstream: Reservoir Pool
Downstream: BaldEagleCr
Weir/Dam Crest Profile:
Points: 6
Station range: 0.0 to 7423.0 ft
Elevation range: 657.00 to 683.00 ft
Crest length: 7423.0 ft
First 10 points:
| Station | Elevation | |
|---|---|---|
| 0 | 0.0 | 683.0 |
| 1 | 2250.0 | 683.0 |
| 2 | 2250.0 | 657.0 |
| 3 | 2850.0 | 657.0 |
| 4 | 2850.0 | 683.0 |
| 5 | 7423.0 | 683.0 |
2.3 Visualize Dam Crest Profile¶
if len(bald_eagle_connections) > 0:
# Plot weir/dam crest profile
fig, ax = plt.subplots(figsize=(12, 6))
ax.plot(weir_profile['Station'], weir_profile['Elevation'],
'ro-', linewidth=2, markersize=6, label='Dam Crest')
ax.fill_between(weir_profile['Station'], weir_profile['Elevation'],
weir_profile['Elevation'].min() - 10,
alpha=0.3, color='brown')
ax.set_xlabel('Station Along Dam (ft)', fontsize=12)
ax.set_ylabel('Crest Elevation (ft)', fontsize=12)
ax.set_title(f'Dam Crest Profile: {conn_name}',
fontsize=14, fontweight='bold')
ax.grid(True, alpha=0.3)
ax.legend(fontsize=10)
# Add statistics
stats_text = "\n".join([
f'Points: {len(weir_profile)}',
f'Length: {weir_profile["Station"].max() - weir_profile["Station"].min():.1f} ft',
f'Elev Range: {weir_profile["Elevation"].min():.1f} - {weir_profile["Elevation"].max():.1f} ft',
f'Type: {bald_eagle_connections.iloc[0]["Type"]}'
])
ax.text(0.02, 0.98, stats_text,
transform=ax.transAxes,
verticalalignment='top',
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5),
fontsize=10)
plt.tight_layout()
plt.show()
else:
print("Skipping visualization - no connections")

2.4 Extract Gate Information¶
Gate information is pulled from the Davis project (which includes gate definitions).
if davis_gate_geom_file is not None and len(davis_gate_connections) > 0:
# Get gates for first connection with gates (Davis)
conn_name_with_gates = davis_gate_connections.iloc[0]['Name']
print(f"Extracting gates for Davis connection: {conn_name_with_gates}")
print(f" Geometry file: {davis_gate_geom_file.name}")
gates = RasGeometry.get_connection_gates(
davis_gate_geom_file,
conn_name_with_gates
)
print("")
print(f"Gates found: {len(gates)}")
if len(gates) > 0:
print("")
print("Gate parameters:")
display.display(gates[['Gate_Name', 'Width', 'Height', 'Invert', 'Gate_Coefficient']])
# Visualize gate geometry
fig, ax = plt.subplots(figsize=(8, 6))
for idx, gate in gates.iterrows():
# Draw gate as rectangle
width = gate['Width']
height = gate['Height']
invert = gate['Invert']
rect = plt.Rectangle((idx * 20, invert), width, height,
linewidth=2, edgecolor='blue',
facecolor='lightblue', alpha=0.5)
ax.add_patch(rect)
# Label gate
ax.text(idx * 20 + width/2, invert + height + 2,
gate['Gate_Name'],
ha='center', fontsize=10, fontweight='bold')
ax.text(idx * 20 + width/2, invert + height/2,
f"{width}' x {height}'\nInvert: {invert}'",
ha='center', va='center', fontsize=9)
ax.set_xlim(-5, len(gates) * 20 + 15)
ax.set_ylim(invert - 10, invert + height + 20)
ax.set_xlabel('Position', fontsize=11)
ax.set_ylabel('Elevation (ft)', fontsize=11)
ax.set_title(f'Gate Configuration: {conn_name_with_gates}',
fontsize=12, fontweight='bold')
ax.grid(True, alpha=0.3)
ax.set_aspect('equal')
plt.tight_layout()
plt.show()
else:
print("No gates found in the Davis project connections")
No gates found in the Davis project connections
2.5 Analyze All Connections¶
Analyze all Davis connections to summarize weir profiles and gate settings.
if davis_gate_geom_file is not None and len(davis_connections) > 0:
# Extract profiles for all connections (Davis)
print("Processing all connections (Davis)...")
for idx, conn in davis_connections.iterrows():
conn_name = conn['Name']
try:
# Get weir profile
profile = RasGeometry.get_connection_weir_profile(
davis_gate_geom_file,
conn_name
)
# Get gates
gates = RasGeometry.get_connection_gates(
davis_gate_geom_file,
conn_name
)
print(f"{idx+1}. {conn_name}:")
print(f" {conn['From']} -> {conn['To']}")
print(
f" Weir profile: {len(profile)} points, "
f"length={profile['Station'].max():.0f} ft"
)
print(f" Gates: {len(gates)}")
if len(gates) > 0:
gate = gates.iloc[0]
print(
f" {gate['Gate_Name']}: {gate['Width']:.0f}' W x "
f"{gate['Height']:.0f}' H, Invert={gate['Invert']:.0f}'"
)
print()
except Exception as e:
print(f"{idx+1}. {conn_name}: ERROR - {e}")
else:
print("No Davis connections to process")
No Davis connections to process
Section 3: Practical Application - Dam Breach Analysis Workflow¶
Real-world workflow combining storage areas, connections, and gate operations for dam breach analysis. Storage and connection geometry come from BaldEagleCrkMulti2D; gate inventory is illustrated with the Davis project.
print("="*70)
print("DAM BREACH ANALYSIS WORKFLOW")
print("="*70)
# Step 1: Identify storage areas (Bald Eagle)
print("")
print("Step 1: Storage Areas")
storage_areas = RasGeometry.get_storage_areas(dam_geom_file)
print(f" Found {len(storage_areas)} storage area(s)")
for area in storage_areas:
print(f" - {area}")
# Step 2: Get storage capacity (Bald Eagle)
if len(storage_areas) > 0:
print("")
print("Step 2: Storage Capacity")
elev_vol = RasGeometry.get_storage_elevation_volume(dam_geom_file, storage_areas[0])
print(f" Maximum storage: {elev_vol['Volume'].max():.0f} cu ft")
print(f" Pool elevation range: {elev_vol['Elevation'].min():.1f} to {elev_vol['Elevation'].max():.1f} ft")
# Step 3: Identify connections (Bald Eagle)
print("")
print("Step 3: Connections")
bald_eagle_connections = RasGeometry.get_connections(dam_geom_file)
print(f" Found {len(bald_eagle_connections)} connection(s)")
for _, conn in bald_eagle_connections.iterrows():
print(f" - {conn['Name']}: {conn['From']} -> {conn['To']}")
# Step 4: Analyze dam crest (Bald Eagle)
if len(bald_eagle_connections) > 0:
dam_connections = bald_eagle_connections[
bald_eagle_connections['Name'].str.contains('Dam', case=False, na=False)
]
if len(dam_connections) > 0:
print("")
print("Step 4: Dam Crest Geometry")
dam_name = dam_connections.iloc[0]['Name']
dam_profile = RasGeometry.get_connection_weir_profile(dam_geom_file, dam_name)
print(f" Dam: {dam_name}")
print(f" Crest length: {dam_profile['Station'].max() - dam_profile['Station'].min():.0f} ft")
print(f" Crest elevation range: {dam_profile['Elevation'].min():.1f} to {dam_profile['Elevation'].max():.1f} ft")
print(f" Min crest elevation: {dam_profile['Elevation'].min():.1f} ft")
# Step 5: Gate inventory (Davis)
print("")
print("Step 5: Gate Inventory (Davis)")
if (
davis_gate_geom_file is not None
and len(davis_connections) > 0
and "HasGate" in davis_connections.columns
):
total_gates = davis_connections['HasGate'].sum()
print(f" Total gates in Davis geometry: {total_gates:.0f}")
if total_gates > 0:
conn_with_gates = davis_connections[davis_connections['HasGate'] > 0]
for _, conn in conn_with_gates.iterrows():
gates = RasGeometry.get_connection_gates(
davis_gate_geom_file,
conn['Name']
)
print(f" {conn['Name']}: {len(gates)} gate(s)")
for _, gate in gates.iterrows():
print(
f" - {gate['Gate_Name']}: {gate['Width']:.0f}' x "
f"{gate['Height']:.0f}', Invert {gate['Invert']:.0f}'"
)
else:
print(" No gate connections found in Davis project")
print("")
print("="*70)
print("ANALYSIS COMPLETE")
print("="*70)
======================================================================
DAM BREACH ANALYSIS WORKFLOW
======================================================================
Step 1: Storage Areas
Found 1 storage area(s)
- Reservoir Pool
Step 2: Storage Capacity
Maximum storage: 212000 cu ft
Pool elevation range: 583.0 to 683.0 ft
Step 3: Connections
Found 1 connection(s)
- Dam: Reservoir Pool -> BaldEagleCr
Step 4: Dam Crest Geometry
Dam: Dam
Crest length: 7423 ft
Crest elevation range: 657.0 to 683.0 ft
Min crest elevation: 657.0 ft
Step 5: Gate Inventory (Davis)
No gate connections found in Davis project
======================================================================
ANALYSIS COMPLETE
======================================================================
Summary¶
Methods Demonstrated¶
Storage Area Operations (RasGeometry / GeomStorage):
- get_storage_areas() - List storage areas with metadata
- get_storage_elevation_volume() - Extract elevation-volume relationships
- GeomStorage.get_storage_area_polygons() - Extract SA perimeter polygon from plain text .g##
Structure HDF Operations (HdfStruc):
- HdfStruc.get_storage_area_polygons() - Extract SA perimeter polygon from .g##.hdf or .p##.hdf
SA/2D Connection Operations (RasGeometry):
- get_connections() - List SA/2D connections
- get_connection_weir_profile() - Extract dam crest profiles
- get_connection_gates() - Extract gate definitions (Davis project)
Manning's n Land Cover Operations (GeomLandCover):
- get_base_mannings_n() - Read base Manning's n table from geometry file
- set_base_mannings_n() - Write modified Manning's n values back to geometry file
- get_region_mannings_n() - Read regional Manning's n overrides
Multi-Project Context:
- RasPrj + init_ras_project(..., ras_object=...) to keep project data isolated
Section 4: Manning's n Land Cover Tables (GeomLandCover)¶
2D flow areas in HEC-RAS use land cover classifications to assign Manning's n roughness values. The GeomLandCover class provides methods to read and modify these tables directly in the geometry file.
We'll demonstrate: - Reading the base Manning's n table - Modifying roughness values (e.g., +10% sensitivity) - Writing modified values back to the geometry file - Reading regional Manning's n overrides
4.1 Read Base Manning's n Table¶
# Find a BaldEagle geometry file with populated Manning's n land cover data.
# Geometry 12 (used for storage/connections above) has an empty LCMann table;
# other geometries (e.g. g01) carry the full land cover classification.
lc_geom_file = None
for _, row in bald_eagle_ras.geom_df.iterrows():
candidate = Path(row["full_path"])
df_candidate = GeomLandCover.get_base_mannings_n(candidate)
if len(df_candidate) > 0:
lc_geom_file = candidate
break
if lc_geom_file is None:
raise RuntimeError("No geometry file with Manning's n land cover data found.")
# Read base Manning's n table from the geometry file with land cover data
mannings_df = GeomLandCover.get_base_mannings_n(lc_geom_file)
print(f"Geometry file: {lc_geom_file.name}")
print(f"Land cover entries: {len(mannings_df)}")
print(f"Table number(s): {mannings_df['Table Number'].unique().tolist()}")
print()
display.display(mannings_df)
Geometry file: BaldEagleDamBrk.g06
Land cover entries: 16
Table number(s): ['16']
| Table Number | Land Cover Name | Base Mannings n Value | |
|---|---|---|---|
| 0 | 16 | NoData | 0.060 |
| 1 | 16 | Barren Land Rock/Sand/Clay | 0.040 |
| 2 | 16 | Cultivated Crops | 0.060 |
| 3 | 16 | Deciduous Forest | 0.100 |
| 4 | 16 | Developed, High Intensity | 0.150 |
| 5 | 16 | Developed, Low Intensity | 0.100 |
| 6 | 16 | Developed, Medium Intensity | 0.080 |
| 7 | 16 | Developed, Open Space | 0.040 |
| 8 | 16 | Emergent Herbaceous Wetlands | 0.080 |
| 9 | 16 | Evergreen Forest | 0.120 |
| 10 | 16 | Grassland/Herbaceous | 0.045 |
| 11 | 16 | Mixed Forest | 0.080 |
| 12 | 16 | Open Water | 0.035 |
| 13 | 16 | Pasture/Hay | 0.060 |
| 14 | 16 | Shrub/Scrub | 0.080 |
| 15 | 16 | Woody Wetlands | 0.120 |
4.2 Modify and Write Back Manning's n Values¶
A common sensitivity analysis involves scaling Manning's n values (e.g., +10%). GeomLandCover.set_base_mannings_n() writes modified values back to the geometry file. It creates a .bak backup and validates that land cover names match.
# Modify Manning's n values: increase all by 10% for sensitivity analysis
modified_df = mannings_df.copy()
original_values = modified_df['Base Mannings n Value'].copy()
modified_df['Base Mannings n Value'] = modified_df['Base Mannings n Value'] * 1.1
print("Manning's n modification (+10%):")
print(f"{'Land Cover':<30} {'Original':>10} {'Modified':>10}")
print("-" * 52)
for _, row in modified_df.iterrows():
orig = original_values[row.name]
new = row['Base Mannings n Value']
print(f"{row['Land Cover Name']:<30} {orig:>10.4f} {new:>10.4f}")
# Write modified values back to geometry file
success = GeomLandCover.set_base_mannings_n(lc_geom_file, modified_df)
print(f"\nWrite successful: {success}")
# Verify round-trip by re-reading
verify_df = GeomLandCover.get_base_mannings_n(lc_geom_file)
print(f"\nRound-trip verification ({len(verify_df)} entries):")
max_error = abs(verify_df['Base Mannings n Value'] - modified_df['Base Mannings n Value'].values).max()
print(f" Max difference: {max_error:.9f}")
print(f" Round-trip OK: {max_error < 1e-6}")
# Restore original values
GeomLandCover.set_base_mannings_n(lc_geom_file, mannings_df)
print("\nOriginal values restored.")
Manning's n modification (+10%):
Land Cover Original Modified
----------------------------------------------------
NoData 0.0600 0.0660
Barren Land Rock/Sand/Clay 0.0400 0.0440
Cultivated Crops 0.0600 0.0660
Deciduous Forest 0.1000 0.1100
Developed, High Intensity 0.1500 0.1650
Developed, Low Intensity 0.1000 0.1100
Developed, Medium Intensity 0.0800 0.0880
Developed, Open Space 0.0400 0.0440
Emergent Herbaceous Wetlands 0.0800 0.0880
Evergreen Forest 0.1200 0.1320
Grassland/Herbaceous 0.0450 0.0495
Mixed Forest 0.0800 0.0880
Open Water 0.0350 0.0385
Pasture/Hay 0.0600 0.0660
Shrub/Scrub 0.0800 0.0880
Woody Wetlands 0.1200 0.1320
Write successful: True
Round-trip verification (16 entries):
Max difference: 0.000000000
Round-trip OK: True
Original values restored.
4.3 Read Regional Manning's n Overrides¶
HEC-RAS allows region-specific Manning's n overrides that apply different roughness values within polygon regions of a 2D flow area. GeomLandCover.get_region_mannings_n() reads these regional overrides.
# Read regional Manning's n overrides
region_df = GeomLandCover.get_region_mannings_n(lc_geom_file)
if len(region_df) > 0:
print(f"Regional overrides found: {len(region_df)} entries")
print(f"Regions: {region_df['Region Name'].unique().tolist()}")
print()
display.display(region_df)
else:
print("No regional Manning's n overrides found in this geometry file.")
print("(This is normal — region overrides are optional and only used when")
print(" different roughness values are needed within specific polygon regions.)")
No regional Manning's n overrides found in this geometry file.
(This is normal — region overrides are optional and only used when
different roughness values are needed within specific polygon regions.)
Section 5: Storage Area Polygon Geometry¶
Storage areas have a polygon perimeter that defines their spatial extent. This section demonstrates extracting that polygon from two sources:
- Plain text (
.g##):GeomStorage.get_storage_area_polygons()— parses theStorage Area Surface Line=block using 16-char fixed-width XY pairs - HDF (
.g##.hdfor.p##.hdf):HdfStruc.get_storage_area_polygons()— reads/Geometry/Storage Areas/Polygon Pointsand related datasets
Both methods return a GeoDataFrame with polygon geometry. The BaldEagleCrkMulti2D project contains one 1D storage area ("Reservoir Pool") in geometry g12 with 116 polygon vertices.
Note: CRS is not embedded in HEC-RAS geometry sources. Use
gdf.set_crs(epsg=...)after extraction to assign the project coordinate system.
5.1 Plain Text Extraction (GeomStorage)¶
GeomStorage.get_storage_area_polygons() parses the Storage Area Surface Line= N block in the geometry file. Each row has 16-char fixed-width XY pairs (no space separator). By default exclude_2d=True so 2D flow areas are omitted, returning only traditional 1D storage areas.
# ----- Plain text extraction from geometry file -----
print(f"Geometry file: {bald_eagle_geom_file.name}")
sa_polygons_txt = GeomStorage.get_storage_area_polygons(bald_eagle_geom_file)
print(f"\nFound {len(sa_polygons_txt)} storage area polygon(s)")
if len(sa_polygons_txt) > 0:
display.display(sa_polygons_txt[['Name', 'centroid_x', 'centroid_y', 'is_2d']])
for _, row in sa_polygons_txt.iterrows():
coords = list(row.geometry.exterior.coords)
print(f" {row['Name']}: {len(coords)-1} vertices, "
f"centroid=({row['centroid_x']:.1f}, {row['centroid_y']:.1f})")
Geometry file: BaldEagleDamBrk.g12
Found 1 storage area polygon(s)
| Name | centroid_x | centroid_y | is_2d | |
|---|---|---|---|---|
| 0 | Reservoir Pool | 1.987124e+06 | 305685.85565 | False |
Reservoir Pool: 116 vertices, centroid=(1987123.9, 305685.9)
5.2 HDF Extraction from Geometry HDF¶
HdfStruc.get_storage_area_polygons() reads the /Geometry/Storage Areas/Polygon Points, Polygon Info, and Polygon Parts datasets from the HDF file. It works on both geometry HDF (.g##.hdf) and plan HDF (.p##.hdf) files — pass a geometry number string (e.g. "12") to resolve via geom_df, or pass a full Path to any HDF directly.
Note:
avg_areaandmin_elevare read from the HDFAttributesdataset. For some SA types (e.g.,Elev Vol RCmode), HEC-RAS stores NaN in these fields — this is expected behavior from the model, not a parsing error.
# ----- HDF extraction from pre-built geometry HDF -----
# BaldEagleDamBrk.g12.hdf is included in the example project — no plan run needed.
print(f"Geometry HDF: {bald_eagle_geom_hdf.name}")
print(f" exists: {bald_eagle_geom_hdf.exists()}")
sa_polygons_hdf = HdfStruc.get_storage_area_polygons(bald_eagle_geom_hdf)
print(f"\nFound {len(sa_polygons_hdf)} storage area polygon(s) in geometry HDF")
if len(sa_polygons_hdf) > 0:
display.display(sa_polygons_hdf[['Name', 'avg_area', 'min_elev', 'mode']])
for _, row in sa_polygons_hdf.iterrows():
coords = list(row.geometry.exterior.coords)
avg_area_str = "N/A" if pd.isna(row['avg_area']) else f"{row['avg_area']:.1f} sq ft"
min_elev_str = "N/A" if pd.isna(row['min_elev']) else f"{row['min_elev']:.2f} ft"
print(f" {row['Name']}: {len(coords)-1} vertices, "
f"avg_area={avg_area_str}, min_elev={min_elev_str}")
Geometry HDF: BaldEagleDamBrk.g12.hdf
exists: True
Found 1 storage area polygon(s) in geometry HDF
| Name | avg_area | min_elev | mode | |
|---|---|---|---|---|
| 0 | Reservoir Pool | NaN | NaN | Elev Vol RC |
Reservoir Pool: 116 vertices, avg_area=N/A, min_elev=N/A
5.3 Run Plan p04 and Extract from Plan HDF¶
Plan HDF files (.p##.hdf) contain the same /Geometry/Storage Areas/ group as geometry HDFs — both sources are supported. Here we run plan p04 (SA to 2D Levee Struc) to generate a plan HDF, then extract the polygon from it.
Note: Requires HEC-RAS 6.6 installed and licensed. The cell gracefully skips if HEC-RAS is unavailable.
# ----- Run plan p04 and extract polygons from plan HDF -----
# Plan p04 ("SA to 2D Levee Struc") uses the Reservoir Pool SA.
# Requires HEC-RAS 6.6. Skip gracefully if not available.
sa_polygons_plan = None
try:
print("Running plan p04 (SA to 2D Levee Struc) with HEC-RAS 6.6 ...")
RasCmdr.compute_plan("04", ras_object=bald_eagle_ras, num_cores=2)
# Get plan HDF path from plan_df (authoritative source for HDF paths)
plan_row = bald_eagle_ras.plan_df.loc[
bald_eagle_ras.plan_df['plan_number'] == '04', 'HDF_Results_Path'
]
if plan_row.empty or not plan_row.iloc[0]:
print("[!] Plan 04 HDF path not found in plan_df — skipping plan HDF extraction")
else:
plan_hdf_path = Path(plan_row.iloc[0])
print(f"Plan HDF: {plan_hdf_path.name}")
sa_polygons_plan = HdfStruc.get_storage_area_polygons(plan_hdf_path)
print(f"Found {len(sa_polygons_plan)} storage area polygon(s) in plan HDF")
if len(sa_polygons_plan) > 0:
display.display(sa_polygons_plan[['Name', 'avg_area', 'min_elev', 'mode']])
except Exception as exc:
print(f"[!] Plan p04 execution skipped or failed: {exc}")
print(" HEC-RAS 6.6 must be installed and licensed to run this step.")
print(" Geometry HDF extraction (Section 5.2) still works without running a plan.")
2026-06-11 15:46:00 - ras_commander.RasCmdr - INFO - Using ras_object with project folder: <repo>\examples\example_projects\BaldEagleCrkMulti2D_202
2026-06-11 15:46:00 - ras_commander.RasUtils - INFO - Successfully updated file: <repo>\examples\example_projects\BaldEagleCrkMulti2D_202\BaldEagleDamBrk.p04
Running plan p04 (SA to 2D Levee Struc) with HEC-RAS 6.6 ...
2026-06-11 15:46:00 - ras_commander.RasCmdr - INFO - Set number of cores to 2 for plan: 04
2026-06-11 15:46:00 - ras_commander.RasCmdr - INFO - Running HEC-RAS from the Command Line:
2026-06-11 15:46:00 - ras_commander.RasCmdr - INFO - Running command: "C:\Program Files (x86)\HEC\HEC-RAS\7.0\Ras.exe" -c "<repo>\examples\example_projects\BaldEagleCrkMulti2D_202\BaldEagleDamBrk.prj" "<repo>\examples\example_projects\BaldEagleCrkMulti2D_202\BaldEagleDamBrk.p04"
2026-06-11 15:46:00 - ras_commander.RasDialogWatchdog - INFO - DialogWatchdog started — polling every 1.5s for RAS dialog windows
2026-06-11 15:49:30 - ras_commander.RasCmdr - INFO - HEC-RAS execution completed for plan: 04
2026-06-11 15:49:30 - ras_commander.RasCmdr - INFO - Total run time for plan 04: 209.14 seconds
2026-06-11 15:49:30 - ras_commander.RasDialogWatchdog - INFO - DialogWatchdog stopped — no dialogs encountered
Plan HDF: BaldEagleDamBrk.p04.hdf
Found 1 storage area polygon(s) in plan HDF
| Name | avg_area | min_elev | mode | |
|---|---|---|---|---|
| 0 | Reservoir Pool | NaN | NaN | Elev Vol RC |
5.4 Comparison and Visualization¶
if len(sa_polygons_txt) > 0 and len(sa_polygons_hdf) > 0:
# ----- Coordinate agreement check -----
txt_name = sa_polygons_txt.iloc[0]['Name']
hdf_name = sa_polygons_hdf.iloc[0]['Name']
print(f"Plain-text SA: {txt_name}")
print(f"HDF SA: {hdf_name}")
txt_coords = list(sa_polygons_txt.iloc[0].geometry.exterior.coords)
hdf_coords = list(sa_polygons_hdf.iloc[0].geometry.exterior.coords)
print(f"\nVertex count — plain text: {len(txt_coords)-1}, HDF: {len(hdf_coords)-1}")
if len(txt_coords) > 0 and len(hdf_coords) > 0:
txt_x0, txt_y0 = txt_coords[0]
hdf_x0, hdf_y0 = hdf_coords[0]
print(f"First vertex — txt: ({txt_x0:.3f}, {txt_y0:.3f})")
print(f" — hdf: ({hdf_x0:.3f}, {hdf_y0:.3f})")
print(f" — diff: ({abs(txt_x0-hdf_x0):.4f}, {abs(txt_y0-hdf_y0):.4f})")
# ----- Polygon visualization -----
fig, ax = plt.subplots(figsize=(10, 7))
sa_polygons_txt.plot(
ax=ax, edgecolor='blue', facecolor='lightblue', alpha=0.5,
linewidth=2, label='Plain Text (.g##)'
)
sa_polygons_hdf.plot(
ax=ax, edgecolor='red', facecolor='none',
linewidth=2, linestyle='--', label='Geometry HDF (.g##.hdf)'
)
if sa_polygons_plan is not None and len(sa_polygons_plan) > 0:
sa_polygons_plan.plot(
ax=ax, edgecolor='green', facecolor='none',
linewidth=1, linestyle=':', label='Plan HDF (.p##.hdf)'
)
# Mark centroid
cx = sa_polygons_txt.iloc[0]['centroid_x']
cy = sa_polygons_txt.iloc[0]['centroid_y']
if cx is not None and cy is not None:
ax.plot(cx, cy, 'k+', markersize=12, markeredgewidth=2, label='Centroid')
ax.legend(fontsize=10)
ax.set_title(
f'Storage Area Polygon: {txt_name}\n'
f'({len(txt_coords)-1} vertices)',
fontsize=13, fontweight='bold'
)
ax.set_xlabel('Easting (ft)', fontsize=11)
ax.set_ylabel('Northing (ft)', fontsize=11)
ax.grid(True, alpha=0.3)
ax.ticklabel_format(style='plain')
plt.tight_layout()
plt.show()
print("\nNote: No CRS is embedded in the HEC-RAS geometry sources.")
print("Use gdf.set_crs(epsg=...) to assign the project coordinate system.")
else:
print("Skipping visualization — no polygon data available")
Plain-text SA: Reservoir Pool
HDF SA: Reservoir Pool
Vertex count — plain text: 116, HDF: 116
First vertex — txt: (2002489.474, 323477.846)
— hdf: (2002489.474, 323477.846)
— diff: (0.0000, 0.0000)
<ipykernel-temp>.py:42: UserWarning: Legend does not support handles for PatchCollection instances.
See: https://matplotlib.org/stable/tutorials/intermediate/legend_guide.html#implementing-a-custom-legend-handler
ax.legend(fontsize=10)

Note: No CRS is embedded in the HEC-RAS geometry sources.
Use gdf.set_crs(epsg=...) to assign the project coordinate system.