Skip to content

Using eBFE Models: Lake Maurepas Validation

This notebook validates the organized Lake Maurepas eBFE delivery format. It is intentionally scoped to the delivery-readiness gate: organize the source archive, confirm ras-commander can initialize the local HEC-RAS project, review the saved geometry-preprocessor evidence, and document whether full hydraulic result HDFs are available.

What This Notebook Proves

  1. RasEbfeModels.organize_model("lake-maurepas") can retrieve/extract/organize the model into the shared delivery workspace.
  2. The organized RAS project has local projection, terrain, and land-cover assets.
  3. The geometry preprocessor has already passed through the ras-commander workflow with detailed compute-message evidence.
  4. Full RAS hydraulic result HDFs are absent from the delivered source archive, so Lake Maurepas is preprocessor-valid but not yet a results-ready demo.
Python
from pathlib import Path
import json
import logging
import os
import re
import sys
import zipfile

import h5py
import pandas as pd

logging.getLogger("ras_commander").setLevel(logging.WARNING)

try:
    from ras_commander import RasPrj, RasUtils, init_ras_project
    from ras_commander.sources.federal import RasEbfeModels
except ImportError:
    repo_root = Path.cwd().parent if Path.cwd().name == "examples" else Path.cwd()
    sys.path.insert(0, str(repo_root))
    from ras_commander import RasPrj, RasUtils, init_ras_project
    from ras_commander.sources.federal import RasEbfeModels

Configure the eBFE Workspace

Python
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"

organized_folder = ORGANIZED_ROOT / "LakeMaurepas_08070204"
if not organized_folder.exists():
    organized_folder = RasEbfeModels.organize_model(
        "lake-maurepas",
        download_root=DOWNLOAD_ROOT,
        output_root=ORGANIZED_ROOT,
    )

ras_model_root = organized_folder / "RAS Model"
print(f"Lake Maurepas organized folder: {organized_folder}")
print(f"RAS Model root exists: {ras_model_root.exists()}")
print(f"Validation root: {VALIDATION_ROOT}")
Text Only
Lake Maurepas organized folder: H:\Testing\eBFE Model Organization\Organized\LakeMaurepas_08070204
RAS Model root exists: True
Validation root: H:\Testing\eBFE Model Organization\Validation\ebfe_delivery

Discover and Initialize the RAS Project

Python
projects = RasUtils.find_valid_ras_folders(
    ras_model_root,
    max_depth=10,
    return_project_info=True,
)
project_df = pd.DataFrame(projects)
print(f"Discovered {len(project_df)} HEC-RAS project folder(s)")
project_df
Text Only
Discovered 1 HEC-RAS project folder(s)
folder project_name prj_file plan_count plan_numbers
0 H:\Testing\eBFE Model Organization\Organized\L... Lake_Maurepas H:\Testing\eBFE Model Organization\Organized\L... 7 [01, 02, 03, 04, 05, 06, 07]
Python
project_folder = Path(project_df.iloc[0]["folder"])
ras = RasPrj()
init_ras_project(
    project_folder,
    "5.0.7",
    ras_object=ras,
    load_results_summary=False,
)

plan_columns = [
    column for column in ["plan_number", "flow_type", "Geom File", "Flow File", "HDF_Results_Path"]
    if column in ras.plan_df.columns
]
ras.plan_df[plan_columns]
plan_number flow_type Geom File Flow File HDF_Results_Path
0 01 Steady None None None
1 02 Unsteady 01 02 H:\Testing\eBFE Model Organization\Organized\L...
2 03 Unsteady 01 03 None
3 04 Unsteady 01 04 None
4 05 Unsteady 01 05 None
5 06 Unsteady 01 06 None
6 07 Unsteady 01 07 None

Confirm Local Delivery Assets

Python
asset_rows = []
for label, pattern in {
    "projection": "Projection/*.prj",
    "terrain_hdf": "Terrain/**/*.hdf",
    "land_cover_tif": "Land Cover/**/*.tif",
    "land_cover_hdf": "Land Cover/**/*.hdf",
    "rasmap": "*.rasmap",
}.items():
    for path in sorted(project_folder.glob(pattern)):
        asset_rows.append(
            {
                "asset_type": label,
                "file": path.name,
                "relative_path": str(path.relative_to(project_folder)),
                "size_mb": round(path.stat().st_size / 1_000_000, 3),
            }
        )

asset_df = pd.DataFrame(asset_rows)
asset_df
asset_type file relative_path size_mb
0 projection Breaklines3.prj Projection\Breaklines3.prj 0.001
1 terrain_hdf Terrain.hdf Terrain\Terrain.hdf 13.377
2 land_cover_tif LandCover.tif Land Cover\LandCover.tif 22.675
3 land_cover_hdf LandCover.hdf Land Cover\LandCover.hdf 0.011
4 rasmap Lake_Maurepas.rasmap Lake_Maurepas.rasmap 0.017

Review Saved Audit Evidence

Python
def find_latest_json(root: Path, patterns: list[str], text_patterns: list[str] | None = None) -> Path | None:
    if not root.exists():
        return None
    candidates = []
    for pattern in patterns:
        candidates.extend(root.rglob(pattern))
    candidates = sorted({path for path in candidates if path.is_file()}, key=lambda path: path.stat().st_mtime)
    if not text_patterns:
        return candidates[-1] if candidates else None
    for path in reversed(candidates):
        try:
            text = path.read_text(encoding="utf-8", errors="ignore")
        except OSError:
            continue
        if any(pattern.lower() in text.lower() for pattern in text_patterns):
            return path
    return None


def size_gb(path: Path) -> float:
    return round(path.stat().st_size / 1_000_000_000, 3)


def classify_plan_hdf(path: Path) -> dict[str, object]:
    with h5py.File(path, "r") as hdf:
        top_groups = sorted(hdf.keys())
        has_geometry = "Geometry" in hdf
        has_plan_data = "Plan Data" in hdf
        has_event_conditions = "Event Conditions" in hdf
        has_summary_only = list(hdf.keys()) == ["Results"] and "Summary" in hdf["Results"]
        if has_geometry and has_plan_data:
            classification = "hydraulic results"
        elif has_summary_only:
            classification = "compute messages only"
        else:
            classification = "unknown HDF layout"
    return {
        "file": path.name,
        "size_gb": size_gb(path),
        "classification": classification,
        "has_geometry": has_geometry,
        "has_plan_data": has_plan_data,
        "has_event_conditions": has_event_conditions,
        "top_groups": ", ".join(top_groups),
    }


def existing_path_count(values) -> int:
    return sum(bool(value) and Path(str(value)).exists() for value in values)
Python
audit_path = find_latest_json(
    VALIDATION_ROOT,
    ["e2e_delivery_audit_*.json"],
    ["LakeMaurepas_08070204", "Lake Maurepas", "08070204"],
)
print(f"Audit report: {audit_path}")

audit_payload = json.loads(audit_path.read_text(encoding="utf-8")) if audit_path else []
lake_record = audit_payload[0] if audit_payload else {}
project_record = lake_record.get("projects", [{}])[0]

audit_summary = pd.DataFrame(
    [
        {
            "status": lake_record.get("status"),
            "project_count": lake_record.get("project_count"),
            "audit_issues": len(lake_record.get("issues", [])),
            "project_crs": project_record.get("project_crs"),
            "terrain_layers": len(project_record.get("terrain_layers", [])),
            "land_cover_layers": len(project_record.get("land_cover_layers", [])),
            "dss_reference_count": project_record.get("dss_reference_count"),
            "output_hdf_count": project_record.get("output_hdf_count"),
            "crs_mismatches": len(project_record.get("terrain_crs_mismatches", []))
            + len(project_record.get("land_cover_crs_mismatches", [])),
        }
    ]
)
audit_summary
Text Only
Audit report: H:\Testing\eBFE Model Organization\Validation\ebfe_delivery\e2e_delivery_audit_20260427_082751.json
status project_count audit_issues project_crs terrain_layers land_cover_layers dss_reference_count output_hdf_count crs_mismatches
0 audited 1 0 EPSG:2278 1 1 14 7 0

Review Geometry-Preprocessor Evidence

Python
preprocessor_root = VALIDATION_ROOT / "preprocessor_validation"
preprocessor_path = find_latest_json(
    preprocessor_root / "lake_maurepas",
    ["geometry_preprocessor_validation_*.json"],
    ["LakeMaurepas_08070204", "Lake_Maurepas", "lake-maurepas"],
)
if preprocessor_path is None:
    preprocessor_path = find_latest_json(
        preprocessor_root,
        ["geometry_preprocessor_validation_*.json"],
        ["LakeMaurepas_08070204", "Lake_Maurepas", "lake-maurepas"],
    )
print(f"Preprocessor report: {preprocessor_path}")

payload = json.loads(preprocessor_path.read_text(encoding="utf-8"))
records = payload.get("records", [])
plan_rows = []
for record in records:
    for plan in record.get("plans", []):
        plan_rows.append(
            {
                "study": record.get("study"),
                "project": record.get("project_name"),
                "status": record.get("status"),
                "plan": plan.get("plan_number"),
                "geometry": plan.get("geometry_number"),
                "flow_type": plan.get("flow_type"),
                "elapsed_seconds": round(plan.get("elapsed_seconds", 0), 1),
                "errors": plan.get("error_count"),
                "warnings": plan.get("warning_count"),
                "return_code": plan.get("return_code"),
            }
        )
pd.DataFrame(plan_rows)
Text Only
Preprocessor report: H:\Testing\eBFE Model Organization\Validation\ebfe_delivery\preprocessor_validation\lake_maurepas\geometry_preprocessor_validation_20260426_134751.json
study project status plan geometry flow_type elapsed_seconds errors warnings return_code
0 LakeMaurepas_08070204 Lake_Maurepas passed 02 01 Unsteady 256.1 0 0 0

Verify Result-HDF Availability

Python
source_zip = DOWNLOAD_ROOT / "08070204_Models.zip"
plan_hdf_pattern = re.compile(r"\.p\d{2}\.hdf$", re.IGNORECASE)
source_hdf_entries = []
source_plan_hdfs = []

if source_zip.exists():
    with zipfile.ZipFile(source_zip) as zf:
        for name in zf.namelist():
            if name.lower().endswith(".hdf"):
                source_hdf_entries.append(name)
            if plan_hdf_pattern.search(name):
                source_plan_hdfs.append(name)

print(f"Source ZIP: {source_zip}")
print(f"Source HDF entries: {len(source_hdf_entries)}")
print(f"Source plan-result HDF entries: {len(source_plan_hdfs)}")
source_hdf_entries[:10]
Text Only
Source ZIP: H:\Testing\eBFE Model Organization\Downloads\08070204_Models.zip
Source HDF entries: 2
Source plan-result HDF entries: 0





['08070204_Models/Hydraulics/LandCover.hdf',
 '08070204_Models/Hydraulics/Terrain/Terrain.hdf']
Python
organized_plan_hdfs = sorted(project_folder.glob("*.p??.hdf"))
hdf_summary = pd.DataFrame(classify_plan_hdf(path) for path in organized_plan_hdfs)
print(
    f"{existing_path_count(ras.plan_df['HDF_Results_Path'])} of {len(ras.plan_df)} plan dataframe HDF paths exist locally"
)
hdf_summary
Text Only
1 of 7 plan dataframe HDF paths exist locally
file size_gb classification has_geometry has_plan_data has_event_conditions top_groups
0 Lake_Maurepas.p02.hdf 0.0 compute messages only False False False Results

Optional Preprocessor Re-Run Command

Python
RUN_GEOMETRY_PREPROCESSOR = False

repo_root = Path.cwd().parent if Path.cwd().name == "examples" else Path.cwd()
batch_script = repo_root / "scripts" / "ebfe_geometry_preprocessor_batch.py"
cmd = [
    sys.executable,
    str(batch_script),
    "--study",
    "lake-maurepas",
    "--plan",
    "02",
    "--max-wait",
    "7200",
    "--output-dir",
    str(VALIDATION_ROOT / "preprocessor_validation" / "lake_maurepas"),
]
display_cmd = [
    "python",
    str(batch_script.relative_to(repo_root)),
    *cmd[2:],
]

if RUN_GEOMETRY_PREPROCESSOR:
    import subprocess

    subprocess.run(cmd, check=True)
else:
    print("Geometry preprocessor is opt-in for the committed notebook.")
    print(" ".join(f'"{part}"' if " " in part else part for part in display_cmd))
Text Only
Geometry preprocessor is opt-in for the committed notebook.
python scripts\ebfe_geometry_preprocessor_batch.py --study lake-maurepas --plan 02 --max-wait 7200 --output-dir "H:\Testing\eBFE Model Organization\Validation\ebfe_delivery\preprocessor_validation\lake_maurepas"

Delivery-Format Interpretation

Lake Maurepas is organized and geometry-preprocessor-valid. The organized project has local terrain, land cover, projection, and RASMapper references, and the saved ras-commander preprocessor run completed cleanly for plan 02.

It should not yet be advertised as a full results-ready example: the source ZIP does not include RAS plan-result HDFs, and the local Lake_Maurepas.p02.hdf created by preprocessing contains compute messages only. To promote this model to results-ready status, run the hydraulic plans or obtain/source validated result HDFs and update VALIDATION_MATRIX.md.

CLB Engineering Corporation  ·  LLM Forward Engineering
RAS Commander is a free and open-source project maintained by CLB Engineering Corporation. For agencies and firms seeking to modernize H&H workflows with LLM Forward approaches, contact CLB to partner with the engineers who wrote the automation.