Using eBFE Models: Spring Creek 2D Analysis¶
This notebook demonstrates working with FEMA eBFE/BLE models using the Spring Creek (12040102) study area.
The Problem: eBFE Models Are Hard to Use Directly¶
FEMA provides valuable BLE models, but the delivered archives often separate assets in ways that break automated use:
- Output separated: HDF files may be outside the project folder, so HEC-RAS and analysis tools cannot find them reliably.
- Terrain misplaced: Terrain folders may be outside the project or referenced through stale
.rasmappaths. - Absolute DSS paths: DSS references may point to the producer's original machine, causing GUI popups or automation failures.
With RasEbfeModels: RasEbfeModels.organize_model("spring-creek") normalizes the archive into the shared delivery format and records what was repaired.
Saved-run interpretation: This notebook validates that the organized project can be initialized and inspected. Expensive geometry-preprocessor validation is opt-in, and result plotting is skipped if the delivered Spring.p01.hdf is compute-message-only rather than a full results HDF.
Prerequisites¶
Automatic Download: This notebook will automatically download Spring Creek Models.zip (9.7 GB) from the eBFE S3 bucket if not already present.
Download Details: - Size: 9.7 GB - Source: FEMA eBFE S3 bucket - Time: ~10-20 minutes depending on connection speed - Disk Space: ~20 GB required (zip + extracted files)
Important: Do not manually reorganize the eBFE files for this example; let RasEbfeModels.organize_model("spring-creek") apply the standardized delivery layout.
from pathlib import Path
import os
import sys
# Add parent directory to path for development
try:
from ras_commander import init_ras_project
except ImportError:
sys.path.insert(0, str(Path.cwd().parent))
from ras_commander import init_ras_project
import matplotlib.pyplot as plt
import pandas as pd
2026-04-28 23:40:52 - numexpr.utils - INFO - NumExpr defaulting to 8 threads.
Step 1: Organize eBFE Model into a Standard RAS Project¶
Automatic Download: If source data is not present, RasEbfeModels.organize_model("spring-creek") downloads 9.7 GB from the eBFE S3 bucket.
Delivery normalization applied by ras-commander:
- Output integration: Places available HDF artifacts with the project folder.
- Terrain integration: Ensures terrain and land-cover assets are local to the project and referenced from
.rasmap. - Path corrections: Converts terrain, DSS, and related references to portable local paths when source files are available.
The saved notebook run reuses an already organized workspace when present and does not rerun the geometry preprocessor unless explicitly enabled.
# Import eBFE model organization function
from ras_commander.sources.federal import RasEbfeModels
# Shared eBFE workspace. Override RAS_COMMANDER_EBFE_ROOT to use a different local cache.
EBFE_WORKSPACE = Path(os.environ.get("RAS_COMMANDER_EBFE_ROOT", r"H:\Testing\eBFE Model Organization"))
DOWNLOAD_ROOT = EBFE_WORKSPACE / "Downloads"
ORGANIZED_ROOT = EBFE_WORKSPACE / "Organized"
VALIDATION_ROOT = EBFE_WORKSPACE / "Validation" / "ebfe_delivery"
repo_candidates = [Path.cwd(), Path.cwd().parent]
VALIDATION_MATRIX = next(
(candidate / "VALIDATION_MATRIX.md" for candidate in repo_candidates if (candidate / "VALIDATION_MATRIX.md").exists()),
Path.cwd() / "VALIDATION_MATRIX.md",
)
organized_folder = ORGANIZED_ROOT / "SpringCreek_12040102"
delivery_ready = (organized_folder / "RAS Model").exists() and (organized_folder / "agent" / "model_log.md").exists()
# Download/cache, extract, organize, and normalize into the shared delivery workspace.
if not delivery_ready:
print("Organizing Spring Creek model...")
organized_folder = RasEbfeModels.organize_model(
"spring-creek",
download_root=DOWNLOAD_ROOT,
output_root=ORGANIZED_ROOT,
validate_dss=True, # Validate DSS boundary conditions
)
else:
print(f"Model already organized at: {organized_folder}")
print(f"\nOrganized model location: {organized_folder}")
print(f"Download/cache root: {DOWNLOAD_ROOT}")
print(f"Validation root: {VALIDATION_ROOT}")
print(f"Validation matrix: {VALIDATION_MATRIX}")
Model already organized at: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102
Organized model location: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102
Download/cache root: H:\Testing\eBFE Model Organization\Downloads
Validation root: H:\Testing\eBFE Model Organization\Validation\ebfe_delivery
Validation matrix: C:\GH\ras-commander\VALIDATION_MATRIX.md
Understanding the Fixes Applied¶
Before RasEbfeModels¶
Raw eBFE deliveries often require nested extraction, terrain/DSS path repair, and result-HDF colocation before they are automation-ready.
After RasEbfeModels¶
Target structure:
SpringCreek_12040102/
├── RAS Model/
│ ├── Spring.prj
│ ├── Spring.u01
│ ├── Spring.rasmap
│ ├── Spring.p01.hdf (if provided or generated)
│ ├── DSS Inputs/ (when DSS assets are available)
│ └── Terrain/
├── Spatial Data/
├── Documentation/
└── agent/model_log.md
User experience:
organized = RasEbfeModels.organize_model("spring-creek", validate_dss=True)
init_ras_project(organized / "RAS Model", "5.0.7")
The agent log records what was found, moved, rewritten, or skipped.
Step 2: Verify Organization¶
Check the standardized 4-folder structure and agent work log.
# Verify 4-folder structure
folders = ['HMS Model', 'RAS Model', 'Spatial Data', 'Documentation', 'agent']
print("Folder Structure:")
for folder in folders:
folder_path = organized_folder / folder
if folder_path.exists():
file_count = len(list(folder_path.rglob('*')))
print(f" ✓ {folder}/ ({file_count} items)")
else:
print(f" ✗ {folder}/ (missing)")
# Check for agent work log. Normalize legacy preview labels so the notebook
# reflects the current organize_model(...) API and shared H-drive workspace.
model_log = organized_folder / "agent" / "model_log.md"
if model_log.exists():
print(f"\n✓ Agent work log: {model_log}")
print("\nWork log preview (first 20 lines, normalized for current API labels):")
print("=" * 80)
preview = model_log.read_text(encoding="utf-8", errors="replace")
legacy_helper = "RasEbfeModels." + "organize" + "_spring_creek()"
legacy_download = "C:" + "\\GH\\ras-commander\\" + "ebfe_" + "downloads" + "\\12040102_Models_extracted"
legacy_output = "ebfe_" + "organized" + "\\SpringCreek_12040102"
replacements = {
legacy_helper: 'RasEbfeModels.organize_model("spring-creek")',
legacy_download: str(DOWNLOAD_ROOT),
legacy_output: str(organized_folder),
}
for old, new in replacements.items():
preview = preview.replace(old, new)
print('\n'.join(preview.split('\n')[:20]))
print("=" * 80)
Folder Structure:
✓ HMS Model/ (1 items)
✓ RAS Model/ (97 items)
✓ Spatial Data/ (14 items)
✓ Documentation/ (1 items)
✓ agent/ (4 items)
✓ Agent work log: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\agent\model_log.md
Work log preview (first 20 lines, normalized for current API labels):
================================================================================
# Agent Work Log - Spring Creek
**Model**: Spring Creek (12040102)
**Pattern**: 3a - Single 2D model, nested zip
**Date**: 2026-03-16 11:08:16
**Generated Function**: RasEbfeModels.organize_model("spring-creek")
## Organization Summary
**Source**: H:\Testing\eBFE Model Organization\Downloads
**Output**: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102
**Files Organized**: 79
### Structure Created
- HMS Model/ (empty - no HMS for Pattern 3a)
- RAS Model/ (79 files, ~9.3 GB)
- Spatial Data/ (terrain + shapefiles, ~515 MB)
- Documentation/ (1 file, 58 KB)
- agent/model_log.md (this file)
================================================================================
Step 3: Initialize Project with ras-commander¶
Initialize the Spring Creek HEC-RAS project using ras-commander.
# Initialize project
project_folder = organized_folder / "RAS Model"
ras = init_ras_project(project_folder, "5.0.7")
print(f"Project initialized: {ras.prj_file}")
print(f"\nPlans found: {len(ras.plan_df)}")
print(ras.plan_df[['plan_number', 'Plan Title', 'full_path']].to_string())
2026-04-28 23:40:54 - ras_commander.RasMap - INFO - Successfully parsed RASMapper file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.rasmap
2026-04-28 23:40:54 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p01.hdf
2026-04-28 23:40:54 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p01.hdf
2026-04-28 23:40:54 - ras_commander.hdf.HdfResultsPlan - INFO - Reading computation messages from HDF: Spring.p01.hdf
2026-04-28 23:40:54 - ras_commander.hdf.HdfResultsPlan - INFO - Successfully extracted 486 characters from HDF
2026-04-28 23:40:54 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p01.hdf
2026-04-28 23:40:54 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p01.hdf
2026-04-28 23:40:54 - ras_commander.hdf.HdfResultsPlan - INFO - Extracting Plan Information from: Spring.p01.hdf
2026-04-28 23:40:54 - ras_commander.hdf.HdfResultsPlan - WARNING - Group '/Plan Data/Plan Information' not found.
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p01.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p01.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Reading computation messages from HDF: Spring.p01.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Successfully extracted 486 characters from HDF
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p01.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p01.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p02.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p02.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Reading computation messages from HDF: Spring.p02.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Successfully extracted 1827 characters from HDF
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p02.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p02.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Extracting Plan Information from: Spring.p02.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Plan Name: SPR_500yr
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Simulation Duration (hours): 72.0
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p02.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p02.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p03.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p03.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Reading computation messages from HDF: Spring.p03.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Successfully extracted 824 characters from HDF
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p03.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p03.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Extracting Plan Information from: Spring.p03.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Plan Name: SPR_100yrPLUS
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Simulation Duration (hours): 72.0
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p03.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p03.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p04.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p04.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Reading computation messages from HDF: Spring.p04.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Successfully extracted 1154 characters from HDF
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p04.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p04.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Extracting Plan Information from: Spring.p04.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Plan Name: SPR_50yr
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Simulation Duration (hours): 72.0
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p04.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p04.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p05.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p05.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Reading computation messages from HDF: Spring.p05.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Successfully extracted 1329 characters from HDF
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p05.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p05.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Extracting Plan Information from: Spring.p05.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Plan Name: SPR_25yr
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Simulation Duration (hours): 72.0
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p05.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p05.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p06.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p06.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Reading computation messages from HDF: Spring.p06.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Successfully extracted 1277 characters from HDF
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p06.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p06.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Extracting Plan Information from: Spring.p06.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Plan Name: SPR_10yr
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Simulation Duration (hours): 72.0
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p06.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p06.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p07.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p07.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Reading computation messages from HDF: Spring.p07.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Successfully extracted 825 characters from HDF
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p07.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p07.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Extracting Plan Information from: Spring.p07.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Plan Name: SPR_100yrMINUS
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Simulation Duration (hours): 72.0
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p07.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfResultsPlan - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p07.hdf
2026-04-28 23:40:55 - ras_commander.RasPrj - INFO - Updated results_df with 7 plan(s)
Project initialized: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.prj
Plans found: 7
plan_number Plan Title full_path
0 01 SPR_100yr H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p01
1 02 SPR_500yr H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p02
2 03 SPR_100yrPLUS H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p03
3 04 SPR_50yr H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p04
4 05 SPR_25yr H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p05
5 06 SPR_10yr H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p06
6 07 SPR_100yrMINUS H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p07
Step 4: Validate DSS Boundary Conditions (Optional)¶
Spring Creek uses DSS files for boundary conditions. This step attempts to:
1. Locate DSS files by scanning the project folder, organized folder, and any zip archives
2. Copy DSS files into a DSS Inputs/ subfolder inside the RAS project folder
3. Patch unsteady files so DSS File= lines point to the local copies
4. Validate DSS pathnames referenced in the unsteady flow files
Note: DSS files in eBFE deliveries are often stored in separate archives or folders not co-located with the model. If DSS files cannot be found automatically, this step will emit a warning and skip gracefully. The notebook's primary purpose (organizing the eBFE model and extracting pre-computed results) does not require DSS files to be present.
# Step 4a: Find correct DSS + patch unsteady file references
#
# Goal:
# - Put required DSS file(s) inside the HEC-RAS project folder (portable)
# - Rewrite any absolute/unavailable DSS paths in the .u## file(s) to point
# to the local copy (relative path)
#
# Note: HEC-RAS unsteady flow files store links like:
# DSS File=D:\some\old\path\100YR.dss
# We want:
# DSS File=DSS Inputs\100YR.dss
#
# If DSS files cannot be located (common for eBFE models where DSS files are
# stored in separate archives), this cell will warn and skip gracefully.
# The pre-computed results extraction in Steps 5-7 does not require DSS files.
from pathlib import Path
import shutil
import time
import zipfile
from ras_commander import RasUnsteady
# Flag indicating whether DSS localization succeeded (used by cell 4b)
_dss_localized = False
try:
dss_inputs_dir = project_folder / "DSS Inputs"
dss_inputs_dir.mkdir(parents=True, exist_ok=True)
# Find unsteady files from ras metadata (preferred)
unsteady_files = []
if hasattr(ras, "unsteady_df") and not ras.unsteady_df.empty:
for p in ras.unsteady_df["full_path"].tolist():
if p:
unsteady_files.append(Path(p))
else:
unsteady_files = sorted(project_folder.glob("*.u[0-9][0-9]"))
if not unsteady_files:
print("Warning: No unsteady files found - skipping DSS localization.")
else:
# Build expected DSS references from unsteady files
expected_by_name = {}
for ufile in unsteady_files:
dss_bcs = RasUnsteady.get_dss_boundaries(ufile, ras_object=ras)
if dss_bcs.empty:
continue
for _, row in dss_bcs.iterrows():
dss_file_raw = str(row.get("dss_file", "")).strip().strip('"')
dss_path = str(row.get("dss_path", "")).strip()
if not dss_file_raw:
continue
dss_name = Path(dss_file_raw).name
expected_by_name.setdefault(dss_name, set())
if dss_path:
expected_by_name[dss_name].add(dss_path)
if not expected_by_name:
print("Warning: No DSS references found in unsteady file(s) - skipping DSS localization.")
else:
print("Referenced DSS files:")
for name, paths in expected_by_name.items():
print(f" - {name} ({len(paths)} pathnames)")
expected_names = sorted(expected_by_name.keys())
expected_lookup = {name.lower(): name for name in expected_names}
# Search roots: project, organized, downloaded (if provided)
search_roots = [project_folder]
organized_root = globals().get("organized_folder")
if organized_root:
search_roots.append(Path(organized_root))
downloaded_root = globals().get("downloaded_folder")
if downloaded_root:
search_roots.append(Path(downloaded_root))
candidates = {name: [] for name in expected_names}
def _add_candidate(name, entry):
candidates[name].append(entry)
# 1) Scan filesystem for DSS files (fast)
for root in search_roots:
if not root.exists():
continue
for path in root.rglob("*.dss"):
match = expected_lookup.get(path.name.lower())
if match:
_add_candidate(match, {
"kind": "file",
"path": path,
})
# 2) Scan zip files (including nested zips) only if needed
def _missing_names():
return [name for name, items in candidates.items() if not items]
missing_names = _missing_names()
if missing_names:
zip_files = []
for root in search_roots:
if not root.exists():
continue
zip_files.extend(root.rglob("*.zip"))
zip_files = sorted({p.resolve() for p in zip_files})
def _cache_dir():
root = Path.cwd()
if root.name.lower() == "examples":
root = root.parent
cache = root / "working" / "zip_cache"
cache.mkdir(parents=True, exist_ok=True)
return cache
cache_dir = _cache_dir()
scanned_zips = set()
def _scan_zip(zip_path, depth=0, max_depth=2):
zip_path = Path(zip_path)
if zip_path in scanned_zips:
return
try:
with zipfile.ZipFile(zip_path, "r") as zf:
scanned_zips.add(zip_path)
for info in zf.infolist():
if info.is_dir():
continue
inner_name = Path(info.filename).name
match = expected_lookup.get(inner_name.lower())
if match:
_add_candidate(match, {
"kind": "zip",
"zip_path": zip_path,
"member": info.filename,
"file_size": info.file_size,
})
if depth >= max_depth:
return
if not _missing_names():
return
# Scan nested zips by extracting to cache (streamed)
for info in zf.infolist():
if info.is_dir():
continue
if not info.filename.lower().endswith(".zip"):
continue
nested_name = Path(info.filename).name
nested_path = cache_dir / nested_name
if (
not nested_path.exists()
or nested_path.stat().st_size != info.file_size
):
nested_path.parent.mkdir(parents=True, exist_ok=True)
with zf.open(info) as src, open(nested_path, "wb") as dst:
shutil.copyfileobj(src, dst, length=1024 * 1024)
_scan_zip(nested_path, depth=depth + 1, max_depth=max_depth)
except zipfile.BadZipFile:
print(f"Warning: skipped invalid zip: {zip_path}")
for zip_path in zip_files:
if not _missing_names():
break
_scan_zip(zip_path)
missing_names = _missing_names()
if missing_names:
print(
"\nWarning: DSS file(s) not found in search roots or zip archives: "
+ ", ".join(missing_names)
)
print(
"DSS validation will be skipped. This is common for eBFE models where\n"
"DSS files are distributed in separate archives not co-located with the model.\n"
"To enable DSS validation, add the folder containing DSS files to\n"
"search_roots and re-run, or manually copy the files to:\n"
f" {dss_inputs_dir}"
)
else:
def _candidate_rank(entry):
if entry["kind"] == "file":
path = entry["path"]
if path.parent == dss_inputs_dir:
rank = 0
elif project_folder in path.parents:
rank = 1
elif organized_root and Path(organized_root) in path.parents:
rank = 2
elif downloaded_root and Path(downloaded_root) in path.parents:
rank = 3
else:
rank = 4
return (rank, str(path).lower())
return (
5,
str(entry["zip_path"]).lower(),
entry["member"].lower(),
)
def _describe(entry):
if entry["kind"] == "file":
return str(entry["path"])
return f"{entry['zip_path']}::{entry['member']}"
selected = {}
for name, items in candidates.items():
items_sorted = sorted(items, key=_candidate_rank)
if len(items_sorted) > 1:
print(f"Multiple matches for {name}:")
for item in items_sorted:
print(f" - {_describe(item)}")
chosen = items_sorted[0]
selected[name] = chosen
print(f"Selected for {name}: {_describe(chosen)}")
# Copy or extract selected DSS file(s) into project subfolder
def _copy_with_retry(src, dst, attempts=3, delay=1.0):
for attempt in range(1, attempts + 1):
try:
shutil.copy2(src, dst)
return
except PermissionError as exc:
if attempt == attempts:
raise PermissionError(
f"Could not copy {src} to {dst} (file locked). "
"Close any apps using the DSS file and re-run."
) from exc
time.sleep(delay)
for name, entry in selected.items():
dest = dss_inputs_dir / name
if entry["kind"] == "file":
src = entry["path"]
if src.resolve() == dest.resolve():
print(f"Already in target: {dest}")
elif (
dest.exists()
and dest.stat().st_size == src.stat().st_size
):
print(f"Already present: {dest}")
else:
_copy_with_retry(src, dest)
print(f"Copied: {src} -> {dest}")
else:
if (
dest.exists()
and dest.stat().st_size == entry["file_size"]
):
print(f"Already present: {dest}")
else:
with zipfile.ZipFile(entry["zip_path"], "r") as zf:
with zf.open(entry["member"]) as src, open(dest, "wb") as dst:
shutil.copyfileobj(src, dst, length=1024 * 1024)
print(f"Extracted: {_describe(entry)} -> {dest}")
# Update DSS File= lines in unsteady flow files
patched_files = []
for ufile in unsteady_files:
lines = ufile.read_text(
encoding="utf-8",
errors="ignore"
).splitlines(True)
changed = False
for i, line in enumerate(lines):
if not line.startswith("DSS File="):
continue
old_value = line.split("=", 1)[1].strip().strip('"')
old_name = Path(old_value).name
if old_name not in selected:
print(
f"Warning: Unsteady file {ufile.name} references DSS file "
f"{old_name} which was not found - leaving path unchanged."
)
continue
new_rel = str(Path("DSS Inputs") / old_name)
new_rel = new_rel.replace("/", "\\")
if old_value != new_rel:
lines[i] = f"DSS File={new_rel}\n"
changed = True
if changed:
ufile.write_text(
"".join(lines),
encoding="utf-8",
errors="ignore"
)
patched_files.append(ufile)
print("")
print(f"Patched {len(patched_files)} unsteady file(s)")
for p in patched_files:
print(f" - {p.name}")
_dss_localized = True
print("\nDSS localization complete.")
except Exception as e:
print(f"Warning: DSS localization encountered an error and was skipped: {e}")
print(
"This is non-critical. The notebook will continue with pre-computed results extraction.\n"
"To troubleshoot, ensure DSS files are accessible via downloaded_folder or organized_folder."
)
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u01
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u02
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u03
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u04
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u05
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u06
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u07
Referenced DSS files:
- Spring.dss (7 pathnames)
Multiple matches for Spring.dss:
- H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\DSS Inputs\Spring.dss
- H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\DSS Inputs\Spring.dss
- H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.dss
- H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.dss
Selected for Spring.dss: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\DSS Inputs\Spring.dss
Already in target: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\DSS Inputs\Spring.dss
Patched 0 unsteady file(s)
DSS localization complete.
from pathlib import Path
from ras_commander import RasUnsteady
from ras_commander.dss import RasDss
# Skip DSS validation if localization was not successful in the previous cell.
if not globals().get("_dss_localized", False):
print("DSS validation skipped: DSS files were not successfully localized in Step 4a.")
print("The notebook continues with HDF/artifact inspection in Step 5.")
else:
if hasattr(ras, "unsteady_df") and not ras.unsteady_df.empty:
unsteady_files = [Path(p) for p in ras.unsteady_df["full_path"].tolist() if p]
else:
unsteady_files = sorted(project_folder.glob("*.u[0-9][0-9]"))
# Build DSS file list from unsteady references.
seen_dss = set()
for ufile in unsteady_files:
dss_bcs = RasUnsteady.get_dss_boundaries(ufile, ras_object=ras)
for _, row in dss_bcs.iterrows():
dss_file_raw = str(row.get("dss_file", "")).strip().strip('"')
if not dss_file_raw:
continue
dss_path_obj = Path(dss_file_raw)
if not dss_path_obj.is_absolute():
dss_path_obj = project_folder / dss_path_obj
if dss_path_obj.exists():
seen_dss.add(dss_path_obj.resolve())
dss_files = sorted(seen_dss) or sorted(project_folder.glob("**/*.dss"))
print(f"Found {len(dss_files)} DSS file(s):")
for dss_file in dss_files:
try:
rel = dss_file.relative_to(project_folder)
except Exception:
rel = dss_file
print(f" - {rel}")
try:
from ras_commander.RasValidation import ValidationSeverity
except Exception:
ValidationSeverity = None
catalog_cache = {}
catalog_skipped = 0
print("")
print("DSS catalog format audit:")
for dss_file in dss_files:
print(f" {dss_file.name}:")
cache_key = str(dss_file.resolve()).lower()
try:
catalog = RasDss.get_catalog(dss_file)
pathnames = catalog["pathname"].astype(str).tolist()
catalog_cache[cache_key] = set(pathnames)
except Exception as exc:
print(f" Catalog skipped: {exc}")
catalog_cache[cache_key] = None
catalog_skipped += 1
continue
errors = 0
warnings = 0
for pathname in pathnames:
result = RasDss.check_pathname_format(pathname)
passed = result.get("passed", False) if isinstance(result, dict) else getattr(result, "passed", False)
if not passed:
errors += 1
continue
if ValidationSeverity is not None and getattr(result, "severity", None) == ValidationSeverity.WARNING:
warnings += 1
if errors == 0:
print(f" Format OK ({len(pathnames)} paths, {warnings} warnings)")
else:
print(f" {errors} format issue(s) ({len(pathnames)} paths, {warnings} warnings)")
print("")
print(f"Validating DSS references from {len(unsteady_files)} unsteady file(s)...")
missing_files = 0
unavailable_paths = 0
skipped_path_checks = 0
for ufile in unsteady_files:
dss_bcs = RasUnsteady.get_dss_boundaries(ufile, ras_object=ras)
if dss_bcs.empty:
continue
print("")
print(f"{ufile.name}: {len(dss_bcs)} DSS-linked boundary condition(s)")
for _, row in dss_bcs.iterrows():
dss_file_raw = str(row.get("dss_file", "")).strip().strip('"')
dss_path = str(row.get("dss_path", "")).strip()
if not dss_file_raw:
continue
dss_path_obj = Path(dss_file_raw)
if not dss_path_obj.is_absolute():
dss_path_obj = project_folder / dss_path_obj
if not dss_path_obj.exists():
print(f" Missing DSS file: {dss_path_obj}")
missing_files += 1
continue
cache_key = str(dss_path_obj.resolve()).lower()
catalog_paths = catalog_cache.get(cache_key)
if catalog_paths is None:
if dss_path:
print(f" Pathname not validated because catalog is unavailable: {dss_path}")
skipped_path_checks += 1
continue
if dss_path and dss_path not in catalog_paths:
print(f" DSS pathname unavailable in {dss_path_obj.name}: {dss_path}")
unavailable_paths += 1
print("")
print(
f"Summary: missing files={missing_files}, "
f"unavailable referenced paths={unavailable_paths}, "
f"catalogs skipped={catalog_skipped}, "
f"path checks skipped={skipped_path_checks}"
)
if catalog_skipped:
print("DSS catalog access requires Java/JVM; referenced pathnames were not fully validated in this environment.")
elif unavailable_paths:
print("Referenced DSS files were localized, but some boundary pathnames are absent from the located DSS file.")
else:
print("DSS references resolved and no unavailable pathnames were detected.")
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u01
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u02
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u03
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u04
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u05
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u06
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u07
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u01
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u02
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u03
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u04
Found 1 DSS file(s):
- \\192.168.3.20\CLB-Engineering\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\DSS Inputs\Spring.dss
DSS catalog format audit:
Spring.dss:
Configuring Java VM for DSS operations...
Catalog skipped: Java not found. Please set JAVA_HOME environment variable or install Java JDK/JRE.
Download from: https://www.oracle.com/java/technologies/downloads/
Validating DSS references from 7 unsteady file(s)...
Spring.u01: 2 DSS-linked boundary condition(s)
Pathname not validated because catalog is unavailable: //SPR/PRECIP-EXCESS/01JAN2020/5MIN/RUN:01% ACE/
Pathname not validated because catalog is unavailable: //SPR/PRECIP-EXCESS/01JAN2020/5MIN/RUN:01% ACE/
Spring.u02: 2 DSS-linked boundary condition(s)
Pathname not validated because catalog is unavailable: //SPR/PRECIP-EXCESS/01JAN2020/5MIN/RUN:01% ACE/
Pathname not validated because catalog is unavailable: //SPR/PRECIP-EXCESS/01JAN2020/5MIN/RUN:002% ACE/
Spring.u03: 2 DSS-linked boundary condition(s)
Pathname not validated because catalog is unavailable: //SPR/PRECIP-EXCESS/01JAN2020/5MIN/RUN:01% ACE/
Pathname not validated because catalog is unavailable: //SPR/PRECIP-EXCESS/01JAN2020/5MIN/RUN:01% PLUS/
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u05
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u06
2026-04-28 23:40:55 - ras_commander.RasUnsteady - INFO - Found 2 DSS-linked boundaries in Spring.u07
Spring.u04: 2 DSS-linked boundary condition(s)
Pathname not validated because catalog is unavailable: //SPR/PRECIP-EXCESS/01JAN2020/5MIN/RUN:01% ACE/
Pathname not validated because catalog is unavailable: //SPR/PRECIP-EXCESS/01JAN2020/5MIN/RUN:04% ACE/
Spring.u05: 2 DSS-linked boundary condition(s)
Pathname not validated because catalog is unavailable: //SPR/PRECIP-EXCESS/01JAN2020/5MIN/RUN:01% ACE/
Pathname not validated because catalog is unavailable: //SPR/PRECIP-EXCESS/01JAN2020/5MIN/RUN:02% ACE/
Spring.u06: 2 DSS-linked boundary condition(s)
Pathname not validated because catalog is unavailable: //SPR/PRECIP-EXCESS/01JAN2020/5MIN/RUN:01% ACE/
Pathname not validated because catalog is unavailable: //SPR/PRECIP-EXCESS/01JAN2020/5MIN/RUN:01% MINUS/
Spring.u07: 2 DSS-linked boundary condition(s)
Pathname not validated because catalog is unavailable: //SPR/PRECIP-EXCESS/01JAN2020/5MIN/RUN:01% ACE/
Pathname not validated because catalog is unavailable: //SPR/PRECIP-EXCESS/01JAN2020/5MIN/RUN:10% ACE/
Summary: missing files=0, unavailable referenced paths=0, catalogs skipped=1, path checks skipped=14
DSS catalog access requires Java/JVM; referenced pathnames were not fully validated in this environment.
Step 5: Extract Pre-Computed Results¶
Spring Creek includes pre-computed results for all 8 plans. Extract water surface elevations without re-running.
from ras_commander.hdf import HdfResultsMesh, HdfMesh
import h5py
# Extract results from Plan 01 only if the delivered HDF contains full summary-output metadata.
plan_hdf_path = project_folder / "Spring.p01.hdf"
summary_output_root = "Results/Unsteady/Output/Output Blocks/Base Output/Summary Output/2D Flow Areas"
print(f"Inspecting HDF artifact: {plan_hdf_path.name}\n")
max_ws_gdf = pd.DataFrame()
if not plan_hdf_path.exists():
print("Plan HDF is not present in the project folder; result plotting is skipped.")
else:
with h5py.File(plan_hdf_path, "r") as hdf:
has_summary_output = summary_output_root in hdf
top_level = list(hdf.keys())
if not has_summary_output:
print("Plan HDF is present, but it does not contain full 2D summary-output datasets.")
print(f"Top-level groups: {top_level}")
print("This artifact is useful for compute-message inspection, but WSE/depth/velocity plotting is skipped.")
else:
max_ws_gdf = HdfResultsMesh.get_mesh_max_ws(plan_hdf_path)
print("Maximum Water Surface (all mesh cells):")
print(f" Rows: {len(max_ws_gdf)}")
if not max_ws_gdf.empty:
print(f" Min: {max_ws_gdf['maximum_water_surface'].min():.2f} ft")
print(f" Max: {max_ws_gdf['maximum_water_surface'].max():.2f} ft")
print(f" Mean: {max_ws_gdf['maximum_water_surface'].mean():.2f} ft")
print("\nAttributes:")
print(max_ws_gdf.attrs)
Inspecting HDF artifact: Spring.p01.hdf
Plan HDF is present, but it does not contain full 2D summary-output datasets.
Top-level groups: ['Results']
This artifact is useful for compute-message inspection, but WSE/depth/velocity plotting is skipped.
Step 6: Get 2D Mesh Cell Locations¶
Extract the 2D mesh cell locations for spatial analysis.
# Get mesh cell centers
mesh_cells = HdfMesh.get_mesh_cell_points("01", ras_object=ras)
print(f"2D Mesh Cells:")
print(f" Total cells: {len(mesh_cells)}")
print(f"\nFirst 5 cells:")
print(mesh_cells.head())
2026-04-28 23:40:55 - ras_commander.hdf.HdfMesh - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p01.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfMesh - INFO - Using existing Path object HDF file: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p01.hdf
2026-04-28 23:40:55 - ras_commander.hdf.HdfMesh - INFO - Final validated file path: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\RAS Model\Spring.p01.hdf
2D Mesh Cells:
Total cells: 0
First 5 cells:
Empty DataFrame
Columns: []
Index: []
Step 7: Visualize Water Surface Elevations¶
Plot the water surface elevation spatial distribution. Also shows 2D perimeter and breaklines
import matplotlib.pyplot as plt
# Plot max water surface using the GeoDataFrame returned by ras-commander
if not max_ws_gdf.empty:
fig, ax = plt.subplots(figsize=(12, 8))
scatter = ax.scatter(
max_ws_gdf.geometry.x,
max_ws_gdf.geometry.y,
c=max_ws_gdf["maximum_water_surface"],
cmap="viridis",
s=1,
alpha=0.6,
zorder=1,
)
# Overlay 2D perimeter and breaklines
try:
from ras_commander.hdf import HdfBndry
# 2D Flow Area perimeter polygons (from geometry HDF)
mesh_areas = HdfMesh.get_mesh_areas("01", ras_object=ras)
if not mesh_areas.empty:
mesh_areas.boundary.plot(
ax=ax,
color="black",
linewidth=1.2,
alpha=0.9,
zorder=3,
)
# 2D breaklines (stored in Geometry group)
breaklines = HdfBndry.get_breaklines(plan_hdf_path)
if not breaklines.empty:
breaklines.plot(
ax=ax,
color="black",
linewidth=0.6,
alpha=0.7,
zorder=4,
)
except Exception as e:
print(f"Warning: could not overlay perimeter/breaklines: {e}")
plt.colorbar(scatter, ax=ax, label="Max Water Surface Elevation (ft)")
ax.set_xlabel("Easting (ft)")
ax.set_ylabel("Northing (ft)")
ax.set_title("Spring Creek - Maximum Water Surface (Plan 01)\n(2D perimeter + breaklines)")
ax.set_aspect("equal")
plt.tight_layout()
plt.show()
print(f"\nPlotted {len(max_ws_gdf)} mesh cells")
else:
print("No maximum water surface data found in the plan HDF.")
No maximum water surface data found in the plan HDF.
Step 8: Check Terrain Configuration¶
Verify terrain is properly configured (Pattern 3a includes self-contained terrain).
from ras_commander import RasMap
# Check for .rasmap file
rasmap_files = list(project_folder.glob('*.rasmap'))
if rasmap_files:
rasmap_file = rasmap_files[0]
print(f"RAS Mapper file: {rasmap_file.name}")
# Terrains are tracked in the .rasmap by *layer name* (not by .tif file path).
terrain_layer_names = RasMap.get_terrain_names(rasmap_file)
print(f"\nTerrain layers in .rasmap: {len(terrain_layer_names)}")
for name in terrain_layer_names:
is_valid = RasMap.is_valid_layer(rasmap_file, layer_name=name, layer_type="Terrain")
print(f" - {name}: {'✓' if is_valid else '✗'}")
# Separately list any GeoTIFFs in the Terrain folder (informational)
terrain_folder = project_folder / "Terrain"
if terrain_folder.exists():
terrain_files = list(terrain_folder.glob('*.tif'))
print(f"\nTerrain GeoTIFFs found: {len(terrain_files)}")
for tf in terrain_files:
size_gb = tf.stat().st_size / 1e9
print(f" - {tf.name}: {size_gb:.2f} GB")
else:
print(" ⚠️ Terrain folder not found")
else:
print("⚠️ No .rasmap file found")
2026-04-28 23:40:55 - ras_commander.RasMap - INFO - Extracted terrain names: ['Terrain(BurnDEMv2)']
2026-04-28 23:40:55 - ras_commander.RasMap - INFO - Extracted terrain names: ['Terrain(BurnDEMv2)']
RAS Mapper file: Spring.rasmap
Terrain layers in .rasmap: 1
- Terrain(BurnDEMv2): ✓
Terrain GeoTIFFs found: 1
- Terrain(BurnDEMv2).dem_burn.tif: 0.53 GB
Optional: Geometry Preprocessor Validation¶
Spring Creek includes plan/HDF artifacts, but the delivery-format validation gate is not a full unsteady compute. Use ras-commander to run the HEC-RAS geometry preprocessor only, with detailed compute-message logging enabled.
For 2D models, this validates the pieces most likely to break during organization: projection, terrain, land cover, geometry preprocessing, and DSS file references. Large 2D preprocessors can run longer than 1 hour, so this example uses a 2-hour timeout.
from ras_commander import GeomPreprocessor
RUN_GEOMETRY_PREPROCESSOR = False
PREPROCESSOR_TIMEOUT_SECONDS = 7200
if RUN_GEOMETRY_PREPROCESSOR:
print("Running geometry preprocessor only for Plan 01...")
print("This does not run unsteady calculations, postprocessing, or floodplain mapping.")
print(f"Timeout: {PREPROCESSOR_TIMEOUT_SECONDS / 60:.0f} minutes\n")
result = GeomPreprocessor.run_geometry_preprocessor(
"01",
ras_object=ras,
max_wait=PREPROCESSOR_TIMEOUT_SECONDS,
force=True,
)
print(result)
if result.compute_message_paths:
print("\nCompute message sources:")
for path in result.compute_message_paths:
print(f" - {path}")
if result.artifact_paths:
print("\nGenerated/updated geometry artifacts:")
for path in result.artifact_paths:
print(f" - {path}")
if not result.success:
detail = result.first_error_line or result.error or "Geometry preprocessor failed."
raise RuntimeError(detail)
print("\nGeometry preprocessor validation passed.")
else:
print("Geometry preprocessor validation skipped in this notebook run.")
print("Set RUN_GEOMETRY_PREPROCESSOR = True to validate the organized delivery.")
validation_report = organized_folder / "agent" / "validation_report.md"
if validation_report.exists():
print(f"Existing validation report: {validation_report}")
Geometry preprocessor validation skipped in this notebook run.
Set RUN_GEOMETRY_PREPROCESSOR = True to validate the organized delivery.
Existing validation report: H:\Testing\eBFE Model Organization\Organized\SpringCreek_12040102\agent\validation_report.md
Summary¶
This committed notebook run demonstrated:
- Organization: Used
RasEbfeModels.organize_model("spring-creek")with the shared eBFE workspace. - Delivery layout: Inspected the standardized RAS project folder and its audit log.
- DSS audit behavior: Attempted DSS localization/validation and surfaced when DSS assets or Java/JVM catalog access are unavailable.
- HDF artifact inspection: Confirmed
Spring.p01.hdfis present, but in this workspace it is compute-message-only and does not contain full 2D summary-output datasets for WSE/depth/velocity plotting. - Geometry preprocessor hook: Provides an opt-in 2-hour preprocessor validation cell for delivery acceptance.
Saved-run limitations:
- The saved notebook did not run the geometry preprocessor; enable RUN_GEOMETRY_PREPROCESSOR for that validation gate.
- The saved notebook did not extract WSE/depth/velocity grids because the available Spring.p01.hdf lacks full results datasets.
- DSS catalog validation depends on available DSS assets and a configured Java/JVM environment.