Skip to content

Single Plan Execution

Python
# =============================================================================
# DEVELOPMENT MODE TOGGLE
# =============================================================================
USE_LOCAL_SOURCE = False  # <-- TOGGLE THIS

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

# Import ras-commander
from ras_commander import HdfResultsPlan, RasCmdr, RasExamples, RasPlan, RasPrj, init_ras_project, ras

# Additional imports
import os
import numpy as np
import pandas as pd
from IPython import display
import matplotlib.pyplot as plt
import psutil  # For getting system CPU info
from concurrent.futures import ThreadPoolExecutor, as_completed
import time
import subprocess
import shutil

# Verify which version loaded
import ras_commander
print(f"✓ Loaded: {ras_commander.__file__}")

Prerequisites

Before running this notebook, ensure you have:

  1. ras-commander installed: pip install ras-commander
  2. Python 3.10+: Check with python --version
  3. HEC-RAS 6.3+: REQUIRED for plan execution
  4. Disk Space: ~2 GB (project + computation results)
  5. CPU Cores: 2+ recommended for multi-core testing

What You'll Learn

This notebook demonstrates single plan execution using RasCmdr.compute_plan():

  • Basic Execution: Run a plan with default settings
  • Destination Folders: Copy project before execution (preserve original)
  • Core Count Control: Set number of CPU cores for computation
  • Result Verification: Check HDF output and computation messages
  • 111_executing_plan_sets.ipynb - Execute multiple plans sequentially
  • 112_sequential_plan_execution.ipynb - Test mode for debugging
  • 113_parallel_execution.ipynb - Parallel execution for speed
  • 101_project_initialization.ipynb - Project setup

Key Concept: The compute_plan() Method

RasCmdr.compute_plan() is the fundamental execution method in ras-commander:

Python
RasCmdr.compute_plan(
    plan_number,              # Required: "01", "02", etc.
    dest_folder=None,         # Optional: Copy to folder before execution
    num_cores=None,           # Optional: CPU cores (default: HEC-RAS decides)
    clear_geompre=False,      # Optional: Force geometry reprocessing
    overwrite_dest=False      # Optional: Overwrite existing dest_folder
)

See .claude/rules/hec-ras/execution.md for complete parameter reference.

Parameters

Configure these values to customize the notebook for your project.

Python
# =============================================================================
# PARAMETERS - Edit these to customize the notebook
# =============================================================================
from pathlib import Path

# Project Configuration
PROJECT_NAME = "Muncie"           # Example project to extract
RAS_VERSION = "7.0"               # HEC-RAS version (6.3, 6.5, 6.6, etc.)

# Execution Settings
PLAN = "01"                       # Plan number to execute
NUM_CORES = 4                     # CPU cores for 2D computation
RUN_SUFFIX = "run"                # Suffix for run folder (e.g., Muncie_run)
Python
# Extract the Bald Eagle Creek example project
# The extract_project method downloads the project from GitHub if not already present,
# and extracts it to the example_projects folder
bald_eagle_path = RasExamples.extract_project("Balde Eagle Creek", suffix="110")
print(f"Extracted project to: {bald_eagle_path}")  


# Verify the path exists
print(f"Bald Eagle Creek project exists: {bald_eagle_path.exists()}")

Setting Up Our Working Environment

Let's set up our working directory and paths to example projects. We'll also check the number of available CPU cores on this system.

Python
# Define paths to example projects
examples_dir = bald_eagle_path.parent

# Define computation output paths
compute_dest_folder = examples_dir / "compute_test_110"

# Remove stale compute directory before proceeding
if compute_dest_folder.exists():
    shutil.rmtree(compute_dest_folder, ignore_errors=True)
    print(f"Cleaned up stale directory: {compute_dest_folder}")

# Check system resources
cpu_count = psutil.cpu_count(logical=True)
physical_cpu_count = psutil.cpu_count(logical=False)
print(f"System has {physical_cpu_count} physical CPU cores ({cpu_count} logical cores)")
print(f"For HEC-RAS computation, it's often most efficient to use 2-8 cores")

Understanding the RasCmdr.compute_plan Method

Before we dive into execution, let's understand the compute_plan method from the RasCmdr class, which is the core function for running HEC-RAS simulations.

Key Parameters

  • plan_number (str, Path): The plan number to execute or the full path to the plan file
  • dest_folder (str, Path, optional): Destination folder for computation
  • ras_object (RasPrj, optional): Specific RAS object to use (defaults to global ras)
  • clear_geompre (bool, optional): Whether to clear geometry preprocessor files (default: False)
  • num_cores (int, optional): Number of processor cores to use (default: None, uses plan settings)
  • overwrite_dest (bool, optional): Whether to overwrite the destination folder if it exists (default: False)

Returns

  • bool: True if the execution was successful, False otherwise

Key Concepts

  1. Destination Folder: By default, the simulation runs in the original project folder. Specifying a destination folder creates a copy of the project in that location for execution, leaving the original project untouched.

  2. Number of Cores: HEC-RAS can use multiple processor cores to speed up computation. The optimal number depends on the model complexity and your computer's specifications. Generally:

  3. 1-2 cores: Good for small models, highest efficiency per core
  4. 3-8 cores: Good balance for most models
  5. 8 cores: Diminishing returns, may actually be slower due to overhead

  6. Geometry Preprocessor Files: These files store precomputed hydraulic properties. Clearing them forces HEC-RAS to recompute these properties, which is useful after making geometry changes.

  7. Overwrite Destination: Controls whether an existing destination folder should be overwritten. This is a safety feature to prevent accidental deletion of important results.

Step 1: Project Initialization

Let's initialize the HEC-RAS project using the init_ras_project() function.

Python
# Initialize the HEC-RAS project
init_ras_project(bald_eagle_path, RAS_VERSION)
print(f"Initialized HEC-RAS project: {ras.project_name}")

Step 2: Explore Available Plans

Let's examine the available plans in the project to understand what we're working with.

Python
# Display the available plans in the project
print("Available plans in the project:")
display.display(ras.plan_df)

# Let's check the current setting for number of cores in the plans
print("\nCurrent core settings for plans:")
for plan_num in ras.plan_df['plan_number']:
    # Check all three core parameters
    d1_cores = RasPlan.get_plan_value(plan_num, "UNET D1 Cores")
    d2_cores = RasPlan.get_plan_value(plan_num, "UNET D2 Cores") 
    ps_cores = RasPlan.get_plan_value(plan_num, "PS Cores")

    print(f"Plan {plan_num}'s Existing Settings:")
    print(f"  1D Cores: {d1_cores}")
    print(f"  2D Cores: {d2_cores}")
    print(f"  Pump Station Cores: {ps_cores}")

Step 3: Create a Destination Folder Structure

Now, let's prepare a destination folder for our computation. This allows us to run simulations without modifying the original project files.

Python
# Create a destination folder path
dest_folder = examples_dir / "compute_test_cores_110"

# Remove stale compute directory before proceeding
if dest_folder.exists():
    shutil.rmtree(dest_folder, ignore_errors=True)
    print(f"Cleaned up stale directory: {dest_folder}")

# Check if the destination folder already exists
if dest_folder.exists():
    print(f"Destination folder already exists: {dest_folder}")
    print("We'll use overwrite_dest=True to replace it")
else:
    print(f"Destination folder will be created: {dest_folder}")

Step 4: Execute a Plan with a Specified Number of Cores

Now we're ready to execute a plan with a specified number of cores, overwriting the destination folder if it exists. This is the core functionality demonstrated in Example 5 of the original script.

Python
# Select a plan and number of cores
plan_number = "01"
num_cores = 2  # Specify the number of cores to use

print(f"Executing plan {plan_number} with {num_cores} cores...")
print(f"Destination folder: {dest_folder}")

# Record the start time
start_time = time.time()

# Execute the plan with specified parameters
success = RasCmdr.compute_plan(
    plan_number,              # The plan to execute
    dest_folder=dest_folder,  # Where to run the simulation
    num_cores=num_cores,      # Number of processor cores to use
    overwrite_dest=True       # Overwrite destination folder if it exists
)

# Record the end time and calculate duration
end_time = time.time()
duration = end_time - start_time

# Report results
if success:
    print(f"✅ Plan {plan_number} executed successfully using {num_cores} cores")
    print(f"Execution time: {duration:.2f} seconds")
else:
    print(f"❌ Plan {plan_number} execution failed")
    print(f"Time elapsed: {duration:.2f} seconds")

Flexible Plan Number Formats

RasCmdr.compute_plan() accepts plan numbers in multiple formats — integer, string, zero-padded, or prefixed. All are normalized internally via RasUtils.normalize_ras_number() to the canonical two-digit form (e.g., "01").

With smart skip enabled (default), re-running an already-executed plan simply confirms the results are current without re-executing HEC-RAS.

Python
from ras_commander import RasUtils

# All of these formats refer to the same plan "01"
flexible_inputs = [1, "1", "01", "p01"]

for raw in flexible_inputs:
    normalized = RasUtils.normalize_ras_number(raw)
    print(f"  {str(raw):>5}  ->  {normalized!r}")

print()

# Re-run with each format — smart skip confirms results are current
for raw in flexible_inputs:
    result = RasCmdr.compute_plan(raw, dest_folder=dest_folder, overwrite_dest=True)
    print(f"  compute_plan({str(raw):>5}) returned {result}")

Verification: Successful Execution

Critical Success Criteria: 1. No exceptions raised during execution 2. HDF file created: {dest_folder}/{project_name}.p{plan_number}.hdf 3. Computation completed message in logs 4. HDF file size reasonable (> 1 KB, typically MB range)

Verification Code:

Python
# Check HDF file exists and is valid
from pathlib import Path
from ras_commander.hdf import HdfResultsPlan

hdf_file = dest_folder / f"{project_name}.p{plan_number}.hdf"

assert hdf_file.exists(), f"HDF file not created: {hdf_file}"
assert hdf_file.stat().st_size > 1024, f"HDF file too small (likely corrupted)"

# Check computation messages
hdf = HdfResultsPlan(hdf_file)
messages = hdf.get_compute_messages()

assert messages is not None, "No computation messages found"
assert "Run completed" in messages or "Run was successful" in messages, "Execution may have failed"

print(f"[OK] Execution successful: {hdf_file}")
print(f"     HDF size: {hdf_file.stat().st_size / 1e6:.1f} MB")

Visual Inspection: 1. Open dest_folder project in HEC-RAS GUI 2. Load the executed plan 3. View results in RAS Mapper 4. Check for warning/error messages in HEC-RAS GUI

Performance Metrics: - Execution time logged automatically via @log_call decorator - Check console output for timing information - Compare execution times with different num_cores settings

What Can Go Wrong?

Common Issues: 1. FileNotFoundError: Project not initialized - run init_ras_project() first 2. HDF not created: Plan execution failed - check computation messages 3. Slow execution: Use num_cores parameter to parallelize 2D models 4. Geometry errors: Set clear_geompre=True to force reprocessing

Step 5: Verify Results

After execution, let's verify the results by checking the results paths and examining the destination folder.

Python
# Verify that the destination folder exists and contains the expected files
if dest_folder.exists():
    print(f"Destination folder exists: {dest_folder}")

    # List the key files in the destination folder
    print("\nKey files in destination folder:")
    project_files = list(dest_folder.glob(f"{ras.project_name}.*"))
    for file in project_files[:10]:  # Show first 10 files
        file_size = file.stat().st_size / 1024  # Size in KB
        print(f"  {file.name}: {file_size:.1f} KB")

    if len(project_files) > 10:
        print(f"  ... and {len(project_files) - 10} more files")

    # Check for HDF result files
    print("\nHDF result files:")
    hdf_files = list(dest_folder.glob(f"*.hdf"))
    for file in hdf_files:
        file_size = file.stat().st_size / (1024 * 1024)  # Size in MB
        print(f"  {file.name}: {file_size:.1f} MB")
else:
    print(f"Destination folder does not exist: {dest_folder}")
Python
# Since we are now working in the dest_folder, init_ras_project in that folder 

init_ras_project(dest_folder)

Viewing Execution Summary with results_df

The results_df DataFrame provides a lightweight summary of plan execution status, timing, and key metrics. It's automatically populated when you initialize a project.

Python
ras.results_df

Step 6: Extract Computation Messages

After successful plan execution, we can extract detailed computation messages that provide insights into: - Computation time and performance metrics - Warning messages and errors (if any) - Convergence information - Process timing breakdown

The HdfResultsPlan.get_compute_messages() function automatically extracts messages from the HDF file, with fallback to .txt files for older HEC-RAS versions.

Python
# Extract and display computation messages
from ras_commander import HdfResultsPlan

print("="*80)
print("EXTRACTING COMPUTATION MESSAGES")
print("="*80)

# Extract messages using plan number
compute_msgs = HdfResultsPlan.get_compute_messages(plan_number)

if compute_msgs:
    print(f"\nSuccessfully extracted computation messages ({len(compute_msgs)} characters)\n")

    # Display first 1000 characters
    print("First 1000 characters of computation messages:")
    print("-" * 80)
    print(compute_msgs[:1000])

    if len(compute_msgs) > 1000:
        print("\n... (message truncated for display) ...")
        print(f"\nTotal message length: {len(compute_msgs)} characters")

    # Check for warnings or errors
    print("\n" + "="*80)
    print("CHECKING FOR WARNINGS/ERRORS")
    print("="*80)

    lines = compute_msgs.split('\n')
    warnings_errors = [line for line in lines if 'warning' in line.lower() or 'error' in line.lower()]

    if warnings_errors:
        print(f"Found {len(warnings_errors)} warning/error messages:")
        for msg in warnings_errors[:10]:  # Show first 10
            print(f"  - {msg.strip()}")
    else:
        print("✓ No warnings or errors found in computation messages")
else:
    print("⚠ No computation messages available")
    print("This may indicate the plan was not computed or messages are not available.")

print("\n" + "="*80)
Python
# Check the results path using the RasPlan.get_results_path method
# First, initialize a RAS object using the destination folder
try:
    dest_ras = RasPrj()
    init_ras_project(dest_folder, RAS_VERSION, ras_object=dest_ras)

    # Get the results path for the plan we just executed
    results_path = RasPlan.get_results_path(plan_number, ras_object=dest_ras)

    if results_path:
        print(f"Results for plan {plan_number} are located at: {results_path}")

        # Check if the file exists and get its size
        results_file = Path(results_path)
        if results_file.exists():
            size_mb = results_file.stat().st_size / (1024 * 1024)
            print(f"Results file size: {size_mb:.2f} MB")
    else:
        print(f"No results found for plan {plan_number} in the destination folder")
except Exception as e:
    print(f"Error checking results: {e}")
Python
# Demonstrate force_geompre parameter
print("="*80)
print("DEMONSTRATING FORCE_GEOMPRE")
print("="*80)

# Check what geometry preprocessing files exist before force_geompre
geom_hdf = dest_folder / f"{ras.project_name}.g01.hdf"
c_file = dest_folder / f"{ras.project_name}.c01"

print("\nBefore force_geompre:")
print(f"  Geometry HDF exists: {geom_hdf.exists()} ({geom_hdf.name})")
print(f"  Binary preprocessor exists: {c_file.exists()} ({c_file.name})")

print("\nForcing complete geometry reprocessing...")
print("This will delete BOTH .g##.hdf and .c## files\n")

start_time = time.time()

success = RasCmdr.compute_plan(
    plan_number,
    dest_folder=dest_folder,
    num_cores=num_cores,
    overwrite_dest=True,
    force_geompre=True  # Force complete geometry reprocessing
)

elapsed = time.time() - start_time

if success:
    print(f"\n✓ Plan executed with force_geompre (took {elapsed:.2f} seconds)")
    print(f"  Complete geometry reprocessing was performed")

    # Check files were regenerated
    print(f"\nAfter force_geompre:")
    print(f"  Geometry HDF exists: {geom_hdf.exists()} (regenerated)")
    print(f"  Binary preprocessor exists: {c_file.exists()} (regenerated)")
else:
    print(f"✗ Execution failed")

print("\n" + "="*80)

Summary of Single Plan Execution Options

The RasCmdr.compute_plan() method provides a flexible way to execute HEC-RAS plans with various options. Here's a summary of all parameters:

Basic Parameters

  1. Basic Execution: Simply provide a plan number

    Python
    RasCmdr.compute_plan("01")
    

  2. Destination Folder: Run in a separate folder to preserve the original project

    Python
    RasCmdr.compute_plan("01", dest_folder="path/to/folder")
    

  3. Number of Cores: Control the CPU resources used

    Python
    RasCmdr.compute_plan("01", num_cores=2)
    

  4. Overwrite Destination: Replace existing computation folders

    Python
    RasCmdr.compute_plan("01", dest_folder="path/to/folder", overwrite_dest=True)
    

Geometry Preprocessing Parameters

  1. Clear Geometry Preprocessor (.c## files only):

    Python
    RasCmdr.compute_plan("01", clear_geompre=True)
    

  2. Force Geometry Reprocessing (.g##.hdf AND .c## files) - NEW in v0.88.0:

    Python
    RasCmdr.compute_plan("01", force_geompre=True)
    

Smart Skip Parameters (NEW in v0.88.0)

  1. Smart Skip (default behavior): Automatically skips if results are current

    Python
    RasCmdr.compute_plan("01")  # Checks file mtimes, skips if current
    

  2. Force Re-run: Override smart skip and always execute

    Python
    RasCmdr.compute_plan("01", force_rerun=True)
    

  3. Simple Existence Check: Use older skip logic (HDF exists check only)

    Python
    RasCmdr.compute_plan("01", skip_existing=True)
    

Combined Options

  1. Complete Control: Combine multiple parameters
    Python
    RasCmdr.compute_plan(
        "01",
        dest_folder="path/to/folder",
        num_cores=4,
        force_geompre=True,
        force_rerun=True,
        overwrite_dest=True,
        verify=True
    )
    

Decision Matrix: When to Use Which Parameters

Scenario Parameters to Use
First run, default settings compute_plan("01")
Preserve original project dest_folder="run1"
Re-run after geometry edit force_geompre=True
Re-run same inputs (testing) force_rerun=True
Resume interrupted batch Default (smart skip handles this)
Geometry slightly modified clear_geompre=True (lighter)
Geometry heavily modified force_geompre=True (complete)

Next Steps

To further enhance your HEC-RAS automation, consider exploring:

  1. Parallel Execution: Use RasCmdr.compute_parallel() to run multiple plans simultaneously
  2. Test Mode: Use RasCmdr.compute_test_mode() for testing purposes
  3. Pre-Processing: Modify plans, geometries, and unsteady flows before execution
  4. Post-Processing: Analyze results after computation
  5. Batch Processing: Create scripts for parameter sweeps or scenario analysis

These advanced topics are covered in other examples and documentation for the RAS Commander library.

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.