Using eBFE Models: Tickfaw Results-Ready Validation¶
This notebook validates the organized Tickfaw eBFE delivery as a full 2D example with source-provided result HDFs. It confirms local organization, reviews the saved ras-commander geometry-preprocessor evidence, and checks that plan result HDFs are present inside the RAS project folder.
What This Notebook Proves¶
RasEbfeModels.organize_model("tickfaw")resolves to the shared organized delivery.- RASMapper terrain, land-cover, and projection assets are local to the RAS project folder.
- The geometry preprocessor has already passed through ras-commander for plan 13 / geometry 05.
- Seven hydraulic result HDFs are present and loadable from the organized project folder.
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 / "Tickfaw_08070203"
if not organized_folder.exists():
organized_folder = RasEbfeModels.organize_model(
"tickfaw",
download_root=DOWNLOAD_ROOT,
output_root=ORGANIZED_ROOT,
)
ras_model_root = organized_folder / "RAS Model"
print(f"Tickfaw organized folder: {organized_folder}")
print(f"RAS Model root exists: {ras_model_root.exists()}")
print(f"Validation root: {VALIDATION_ROOT}")
Text Only
Tickfaw organized folder: H:\Testing\eBFE Model Organization\Organized\Tickfaw_08070203
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\T... | TickfawRASLSModel | H:\Testing\eBFE Model Organization\Organized\T... | 7 | [13, 14, 15, 16, 17, 18, 19] |
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 | 13 | Unsteady | 05 | 12 | H:\Testing\eBFE Model Organization\Organized\T... |
| 1 | 14 | Unsteady | 05 | 13 | H:\Testing\eBFE Model Organization\Organized\T... |
| 2 | 15 | Unsteady | 05 | 14 | H:\Testing\eBFE Model Organization\Organized\T... |
| 3 | 16 | Unsteady | 05 | 15 | H:\Testing\eBFE Model Organization\Organized\T... |
| 4 | 17 | Unsteady | 05 | 16 | H:\Testing\eBFE Model Organization\Organized\T... |
| 5 | 18 | Unsteady | 05 | 17 | H:\Testing\eBFE Model Organization\Organized\T... |
| 6 | 19 | Unsteady | 05 | 18 | H:\Testing\eBFE Model Organization\Organized\T... |
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 | projection_file.prj | Projection\projection_file.prj | 0.001 |
| 1 | terrain_hdf | Terrain_Repaired.hdf | Terrain\Terrain_Repaired.hdf | 69.397 |
| 2 | land_cover_tif | LandCover.tif | Land Cover\LandCover.tif | 19.459 |
| 3 | land_cover_hdf | LandCover.hdf | Land Cover\LandCover.hdf | 0.011 |
| 4 | rasmap | TickfawRASLSModel.rasmap | TickfawRASLSModel.rasmap | 0.018 |
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"],
["Tickfaw_08070203", "Tickfaw", "08070203"],
)
print(f"Audit report: {audit_path}")
audit_payload = json.loads(audit_path.read_text(encoding="utf-8")) if audit_path else []
tickfaw_record = audit_payload[0] if audit_payload else {}
project_record = tickfaw_record.get("projects", [{}])[0]
audit_summary = pd.DataFrame(
[
{
"status": tickfaw_record.get("status"),
"project_count": tickfaw_record.get("project_count"),
"audit_issues": len(tickfaw_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"),
"outputs_inside_project": project_record.get("outputs_inside_project"),
"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 | outputs_inside_project | crs_mismatches | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | audited | 1 | 0 | EPSG:2278 | 1 | 1 | 14 | 7 | True | 0 |
Review Geometry-Preprocessor Evidence¶
Python
preprocessor_root = VALIDATION_ROOT / "preprocessor_validation"
preprocessor_path = find_latest_json(
preprocessor_root,
["geometry_preprocessor_validation_*.json"],
["Tickfaw_08070203", "TickfawRASLSModel", "tickfaw"],
)
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\geometry_preprocessor_validation_20260424_185812.json
| study | project | status | plan | geometry | flow_type | elapsed_seconds | errors | warnings | return_code | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | tickfaw | TickfawRASLSModel | passed | 13 | 05 | Unsteady | 662.9 | 0 | 0 | 0 |
Verify Result-HDF Availability¶
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
7 of 7 plan dataframe HDF paths exist locally
| file | size_gb | classification | has_geometry | has_plan_data | has_event_conditions | top_groups | |
|---|---|---|---|---|---|---|---|
| 0 | TickfawRASLSModel.p13.hdf | 1.383 | hydraulic results | True | True | True | Event Conditions, Geometry, Plan Data, Results |
| 1 | TickfawRASLSModel.p14.hdf | 1.405 | hydraulic results | True | True | True | Event Conditions, Geometry, Plan Data, Results |
| 2 | TickfawRASLSModel.p15.hdf | 1.361 | hydraulic results | True | True | True | Event Conditions, Geometry, Plan Data, Results |
| 3 | TickfawRASLSModel.p16.hdf | 1.397 | hydraulic results | True | True | True | Event Conditions, Geometry, Plan Data, Results |
| 4 | TickfawRASLSModel.p17.hdf | 1.603 | hydraulic results | True | True | True | Event Conditions, Geometry, Plan Data, Results |
| 5 | TickfawRASLSModel.p18.hdf | 1.609 | hydraulic results | True | True | True | Event Conditions, Geometry, Plan Data, Results |
| 6 | TickfawRASLSModel.p19.hdf | 1.620 | hydraulic results | True | True | True | Event Conditions, Geometry, Plan Data, Results |
Python
result_path_df = ras.plan_df[["plan_number", "HDF_Results_Path"]].copy()
result_path_df["exists"] = result_path_df["HDF_Results_Path"].apply(
lambda value: bool(value) and Path(str(value)).exists()
)
result_path_df["inside_project"] = result_path_df["HDF_Results_Path"].apply(
lambda value: bool(value)
and Path(str(value)).exists()
and project_folder in Path(str(value)).parents
)
result_path_df
| plan_number | HDF_Results_Path | exists | inside_project | |
|---|---|---|---|---|
| 0 | 13 | H:\Testing\eBFE Model Organization\Organized\T... | True | True |
| 1 | 14 | H:\Testing\eBFE Model Organization\Organized\T... | True | True |
| 2 | 15 | H:\Testing\eBFE Model Organization\Organized\T... | True | True |
| 3 | 16 | H:\Testing\eBFE Model Organization\Organized\T... | True | True |
| 4 | 17 | H:\Testing\eBFE Model Organization\Organized\T... | True | True |
| 5 | 18 | H:\Testing\eBFE Model Organization\Organized\T... | True | True |
| 6 | 19 | H:\Testing\eBFE Model Organization\Organized\T... | True | True |
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",
"tickfaw",
"--plan",
"13",
"--max-wait",
"7200",
"--output-dir",
str(VALIDATION_ROOT / "preprocessor_validation"),
]
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 tickfaw --plan 13 --max-wait 7200 --output-dir "H:\Testing\eBFE Model Organization\Validation\ebfe_delivery\preprocessor_validation"
Delivery-Format Interpretation¶
Tickfaw is a results-ready eBFE demonstration model. The organized project has local terrain, land cover, projection, and RASMapper references; all seven plan result HDFs resolve inside the project folder; and the ras-commander geometry preprocessor validation passed for plan 13 / geometry 05 with no errors or warnings.