Skip to content

Real-Time Forecast Workflow with ras-commander

This notebook demonstrates how to build an operational real-time flood forecast workflow using ras-commander. It covers the equivalent functionality of the rtsPy pipeline used in NOAA/NWS operational systems, implemented with ras-commander's Python API.

The workflow downloads NOAA forecast products (HRRR precipitation, STOFS-3D coastal water levels), prepares boundary conditions, updates simulation dates, and executes HEC-RAS models automatically.

Note: This is a documentation/educational notebook. Actual download steps require internet access to NOAA NOMADS and STOFS servers. Code cells that require internet access will gracefully skip and continue.

Python
# =============================================================================
# DEVELOPMENT MODE TOGGLE
# =============================================================================
USE_LOCAL_SOURCE = True

if USE_LOCAL_SOURCE:
    import sys
    from pathlib import Path
    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")
Python
from pathlib import Path
import pandas as pd
from datetime import datetime, timedelta

from ras_commander.precip import PrecipHrrr
from ras_commander.boundaries import CoastalBoundary
from ras_commander import init_ras_project, RasCmdr, RasPlan

The rtsPy Workflow (Context)

The NWS operational flood forecast pipeline (historically known as rtsPy) follows a 10-step process:

Text Only
Step 1:  Check forecast cycle availability (HRRR, GFS, etc.)
Step 2:  Download precipitation forecast (HRRR GRIB2)
Step 3:  Download coastal boundary (STOFS-3D NetCDF)
Step 4:  Download upstream boundary (USGS real-time gauges)
Step 5:  Process precipitation to DSS or spatially-varied format
Step 6:  Generate stage/flow boundary conditions
Step 7:  Update HEC-RAS plan simulation dates
Step 8:  Execute HEC-RAS (single plan or ensemble)
Step 9:  Extract water surface elevation results
Step 10: Post-process and publish to NWS web services

ras-commander handles steps 2-9 via clean Python APIs:

Step rtsPy Approach ras-commander API
2 - HRRR Download Manual HTTP requests, hardcoded paths PrecipHrrr.get_latest_forecast()
3 - Coastal BC Jython + DSSVue scripting CoastalBoundary.download_stofs3d()
4 - Upstream BC Direct USGS API calls RasUsgsBoundaryGeneration.generate_bc_from_gauge()
5 - Precip processing Custom GRIB2 parsers PrecipHrrr.to_spatially_varied()
6 - Stage BC DSS Jython scripting CoastalBoundary.generate_stage_bc()
7 - Plan dates Text search-and-replace RasPlan.update_simulation_date()
8 - Execute subprocess / win32com RasCmdr.compute_plan() with smart skip
9 - Extract results Manual HDF5 parsing 18 HDF static classes

Prerequisites

  • HEC-RAS 6.6+ installed on Windows
  • ras-commander installed (pip install ras-commander)
  • Internet access for NOAA data downloads (NOMADS, STOFS servers)
  • Optional: HEC-DSS installed for DSS-based boundary conditions

Estimated runtime (with internet): 5-15 minutes depending on download speed. Estimated runtime (demonstration mode, no internet): < 30 seconds.

Example 1: Inland Riverine Forecast (Muncie)

This example demonstrates a riverine flood forecast workflow using HRRR precipitation as the forcing data. The Muncie, Indiana area is used as a representative inland watershed.

Step 1: Check HRRR Forecast Availability

Before downloading, check what HRRR cycles are currently available on NOAA NOMADS. The HRRR is updated hourly with 18-hour forecasts (48-hour for the 00z and 12z cycles).

Python
# Check what HRRR data is available
info = PrecipHrrr.get_info()
print("HRRR Forecast Information:")
for key, value in info.items():
    print(f"  {key}: {value}")

# Check latest available cycle
is_available = PrecipHrrr.check_availability()
print(f"\nLatest HRRR cycle available: {is_available}")

Step 2: Download HRRR Forecast

Download the latest available HRRR forecast GRIB2 files. Each file contains one forecast hour of gridded precipitation, wind, and thermodynamic data at 3 km resolution over CONUS.

Python
# Download latest HRRR forecast (18 hours ahead)
# Note: Requires internet access to NOAA NOMADS
output_dir = Path("example_data/hrrr_forecast")
output_dir.mkdir(parents=True, exist_ok=True)

try:
    grib_files = PrecipHrrr.get_latest_forecast(
        output_dir=output_dir,
        hours=18,
        max_lookback_hours=6
    )
    print(f"Downloaded {len(grib_files)} HRRR GRIB2 files")
    for f in grib_files[:3]:
        print(f"  {f.name}")
    if len(grib_files) > 3:
        print(f"  ... and {len(grib_files) - 3} more")
except Exception as e:
    print(f"HRRR download requires internet access: {e}")
    print("Continuing with demonstration of remaining workflow steps...")
    grib_files = []

Step 3: Update Plan Simulation Dates

Before executing, update the HEC-RAS plan's simulation start and end dates to match the forecast window. RasPlan.update_simulation_date() modifies the .p## file directly, preserving all other plan settings.

Python
# In a real workflow, update the plan dates to match the forecast period
# RasPlan.update_simulation_date() modifies the .p## file directly

# Example: Set simulation to cover next 18 hours
forecast_start = datetime.now().replace(minute=0, second=0, microsecond=0)
forecast_end = forecast_start + timedelta(hours=18)

print(f"Forecast window: {forecast_start} to {forecast_end}")
print(f"\nTo update plan dates, use:")
print(f"  RasPlan.update_simulation_date(")
print(f"      plan_number='01',")
print(f"      new_start_date='{forecast_start.strftime('%d%b%Y').upper()}',")
print(f"      new_start_time='{forecast_start.strftime('%H%M')}',")
print(f"      new_end_date='{forecast_end.strftime('%d%b%Y').upper()}',")
print(f"      new_end_time='{forecast_end.strftime('%H%M')}'")
print(f"  )")

Step 4: Execute HEC-RAS

Execute the forecast plan. The force_rerun=True parameter ensures HEC-RAS always runs even if existing results appear current, which is essential in operational forecasting where the forecast data changes each cycle.

Python
# Execute the forecast plan
# init_ras_project("/path/to/project", "6.6")
# RasCmdr.compute_plan("01", force_rerun=True)

print("In a real workflow:")
print("  1. init_ras_project('/path/to/flood_forecast', '6.6')")
print("  2. RasCmdr.compute_plan('01', force_rerun=True)")
print("  3. Results available via HdfResultsPlan/HdfResultsMesh")

Example 2: Coastal Watershed Forecast

This example extends the inland workflow by adding a downstream coastal stage boundary condition from STOFS-3D (formerly ADCIRC). Coastal watersheds like those draining into Galveston Bay require a downstream WSE boundary that accounts for storm surge and tides.

Step 1: Download STOFS-3D Coastal Boundary

STOFS-3D (Surge, Tide, and Overland Flow Surge - 3D) is NOAA's operational coastal storm surge model. It provides water surface elevation forecasts at 1-km resolution along the US coastline, updated 4 times daily.

Python
# Check STOFS-3D availability
info = CoastalBoundary.get_info()
print("STOFS-3D Information:")
for key, value in info.items():
    print(f"  {key}: {value}")

# Download STOFS-3D forecast
stofs_dir = Path("example_data/stofs3d_forecast")
stofs_dir.mkdir(parents=True, exist_ok=True)

try:
    stofs_files = CoastalBoundary.download_stofs3d(
        output_dir=stofs_dir,
        file_type="points"
    )
    print(f"\nDownloaded {len(stofs_files)} STOFS-3D files")
except Exception as e:
    print(f"\nSTOFS-3D download requires internet access: {e}")
    print("Continuing with demonstration...")
    stofs_files = []

Step 2: Extract Coastal WSE at Point

Extract the water surface elevation time series at a specific coastal point. For the Houston Ship Channel example, we use the Galveston Bay entrance (lat=29.35, lon=-94.77). The WSE is extracted from the nearest STOFS-3D node and interpolated to the HEC-RAS simulation timestep.

Python
# Extract water surface elevation at Galveston Bay entrance
# lat=29.35, lon=-94.77 (Houston Ship Channel)

try:
    wse_df = CoastalBoundary.extract_wse_at_point(
        stofs_dir=stofs_dir,
        lat=29.35,
        lon=-94.77,
        units="feet"
    )
    print(f"Extracted {len(wse_df)} time steps of coastal WSE")
    print(f"WSE range: {wse_df['wse_ft'].min():.2f} to {wse_df['wse_ft'].max():.2f} ft NAVD88")
    print(wse_df.head())
except Exception as e:
    print(f"WSE extraction requires downloaded STOFS-3D data: {e}")
    print("\nExample output:")
    print("  datetime          wse_m    wse_ft")
    print("  2025-01-01 00:00  0.305    1.00")
    print("  2025-01-01 01:00  0.335    1.10")
    print("  ...")

Step 3: Generate Stage Boundary Condition

Write the coastal WSE time series as a downstream stage boundary condition in the HEC-RAS unsteady flow file (.u##). The method writes in HEC-RAS fixed-width format and automatically detects the appropriate time interval.

Python
# Write coastal WSE as downstream stage boundary condition
# CoastalBoundary.generate_stage_bc() writes to HEC-RAS .u## file

print("To write coastal boundary condition:")
print("  CoastalBoundary.generate_stage_bc(")
print("      wse_timeseries=wse_df,")
print("      unsteady_file='project.u01',")
print("      bc_location='Downstream',")
print("      units='feet',")
print("      datum_adjustment_ft=0.0")
print("  )")
print()
print("This writes fixed-width format (8.2f, 10 values/line)")
print("directly into the Stage Hydrograph section of the .u## file")

Key Takeaways

Summary

This notebook demonstrated the real-time forecast workflow that ras-commander enables:

  1. HRRR Download (PrecipHrrr) - Download NOAA HRRR forecast precipitation
  2. STOFS-3D Download (CoastalBoundary) - Download coastal storm surge forecasts
  3. Plan Date Update (RasPlan.update_simulation_date()) - Set forecast window
  4. Execution (RasCmdr.compute_plan()) - Run HEC-RAS with smart skip
  5. Results (HDF classes) - Extract and analyze forecast results

rtsPy vs ras-commander Comparison

Feature rtsPy ras-commander
HRRR Download Manual HTTP, hardcoded paths PrecipHrrr.get_latest_forecast()
Coastal BC Jython + DSSVue scripting CoastalBoundary.generate_stage_bc()
Plan Dates Text replacement RasPlan.update_simulation_date()
Execution subprocess or win32com RasCmdr.compute_plan() with smart skip
Results Manual HDF parsing 18 HDF static classes

Adapting for Your Project

Replace the example parameters with your project-specific values: - Project path and HEC-RAS version - HRRR forecast hours and bounding box - Coastal extraction point (lat/lon) - Boundary condition location names in your .u## file

Common Pitfalls

  • HRRR latency: NOMADS may not have the current cycle immediately. Use max_lookback_hours to fall back to a recent cycle.
  • Datum mismatch: STOFS-3D outputs in NAVD88. Verify your HEC-RAS model uses the same datum or apply datum_adjustment_ft.
  • Simulation window: The plan's simulation end time must extend past the last forecast hour to avoid truncated results.
  • force_rerun: Always use force_rerun=True in operational forecasting. Smart skip (default) may skip execution if the HDF appears current.

See Also

  • examples/900_aorc_precipitation.ipynb - AORC historic precipitation (calibration)
  • examples/721_precipitation_hyetograph_comparison.ipynb - Atlas 14 design storm events
  • ras_commander/precip/CLAUDE.md - Complete precipitation module documentation
  • ras_commander/sources/federal/ - NOAA/USGS data source modules

Cleanup

Python
import shutil
for d in ["example_data/hrrr_forecast", "example_data/stofs3d_forecast"]:
    if Path(d).exists():
        shutil.rmtree(d)
        print(f"Cleaned up: {d}")
print("Done!")
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.