Skip to content

Legacy COM Interface

The RasControl class provides access to HEC-RAS 3.x-6.x via the HECRASController COM interface.

Overview

For older HEC-RAS versions that don't support command-line execution or HDF output, RasControl provides:

  • Plan execution via COM automation
  • Steady state profile extraction
  • Unsteady time series extraction
  • Version migration validation

Supported Versions

Version Controller capability contract Result family
3.x alias Resolves to the 4.1 Controller contract below legacy .O##
4.0 RAS400; inherently blocking two-argument compute; exact owned-process cleanup (no QuitRas) legacy .O##
4.1 RAS41; inherently blocking two-argument compute; exact owned-process cleanup (no QuitRas) legacy .O##
5.0.x (501-507) Modern three-argument compute, Compute_Complete, and QuitRas plan HDF
6.0 Modern Controller contract plan HDF
6.3 family alias Modern contract; selects RAS630 plan HDF
6.3.0.2 exact Modern RAS630.HECRASController plan HDF
6.3.1 exact Modern RAS631.HECRASController plan HDF
6.6 Modern Controller contract plan HDF
7.0 Modern Controller contract plan HDF

Initialization

Python
from ras_commander import init_ras_project, RasControl

# Initialize with version number
init_ras_project("/path/to/project", "4.1")

# Or with specific version code
init_ras_project("/path/to/project", "506")  # HEC-RAS 5.0.6

Running Plans

Python
# Run a plan (uses plan number, not file path)
success, messages = RasControl.run_plan("02")

if success:
    print("Plan completed successfully")
else:
    print(f"Plan failed: {messages}")

The COM interface: 1. Opens HEC-RAS in the background 2. Loads the project 3. Sets the current plan 4. Executes the plan 5. Closes HEC-RAS

Exact HEC-RAS 6.3.0.2 batch execution

HEC-RAS 6.3.0.2 and 6.3.1 register different Controller ProgIDs. The 6.3 family alias selects RAS630; use the exact 6.3.0.2 identity when a batch receipt must retain the complete product version:

Python
from ras_commander import RasControl, RasPrj, init_ras_project

project = RasPrj()
init_ras_project("/path/to/project", "6.3", ras_object=project)

result = RasControl.run_plan(
    "01",
    ras_object=project,
    force_recompute=True,
    blocking=True,
    controller_version="6.3.0.2",
    use_watchdog=False,
    strict_close=True,
    refresh_results=False,
)

assert result.execution_details["controller_progid"] == (
    "RAS630.HECRASController"
)
assert result.execution_details["watchdog_started"] is False

blocking=True uses Compute_CurrentPlan(None, None, True) and does not poll Compute_Complete(). use_watchdog=False is intended only when an outer batch supervisor already owns the process tree and hard timeout. strict_close=True makes a QuitRas() failure or a verified surviving owned ras.exe process fail the operation instead of logging only a warning. If the internal watchdog is requested but its actual worker identity cannot be proved, execution fails before computation. The worker publishes its own PID, creation time, executable, and launch arguments; this also handles Windows virtual-environment launchers whose PID differs from the interpreter doing the monitoring.

HEC-RAS 4.0/4.1 always use the inherently blocking two-argument Compute_CurrentPlan(None, None) call, including when the caller leaves blocking=False. The API records the requested flag separately from the effective blocking mode. It does not poll on the same thread after that call returns. Its runtime bound requires the independent watchdog or an external supervisor. An outer supervisor is also necessary to bound project opening, which occurs before the internal compute watchdog starts.

execution_details contains JSON-safe provenance. Its stable common keys are requested_controller_version, resolved_controller_version, controller_progid, compute_mode, completion_method, controller_quit_supported, controller_close_method, message_count, controller_message_count, watchdog_requested, watchdog_started, and duration_seconds. Polled modern Controllers report Compute_Complete, True, and quit_ras for the three capability fields; blocking modern Controllers report Compute_CurrentPlan_blocking_return, True, and quit_ras. HEC-RAS 4.0/4.1 report Compute_CurrentPlan_blocking_return, False, and owned_process_cleanup. The common result-family fields include selected_result_format, artifact_preparation_cleanup, artifact_finalization_cleanup, and result_artifacts_finalized. A cleanup record's result_format names the opposing family targeted for deletion; removed_paths and missing_paths partition its complete exact target set. Both cleanup records are None for a current-result skip. A skip is explicitly non-calculation evidence: calculation_attempted is False and solver_quiescence_confirmed is None. current_check_performed, current_check_close_safe, and current_check_close_method describe the separate currency-check session. completion_method and controller_close_method remain unset because no calculation ran. Blocking results also include blocking_result; polled results include poll_count.

Use observe_dialogs=True for exact Controller-scoped diagnostic observation before project opening. The observer verifies process and window ownership and records its lifecycle in execution_details["dialog_observation"]. Unknown dialogs remain untouched. This option provides evidence for diagnosing a blocking GUI gate; it does not make an inherently blocking COM call asynchronous or replace the external deadline.

HEC-RAS 4.0/4.1 expose neither Compute_Complete nor QuitRas. RasControl fails closed unless exact PID/create-time exit is positively proved. An access-denied or other identity-query error preserves the session lock as evidence; it is never interpreted as exit or PID reuse.

Steady State Results

Python
# Extract steady state profiles
results = RasControl.get_steady_results("02")
print(results)

# DataFrame columns:
# - river
# - reach
# - station
# - profile (PF 1, PF 2, etc.)
# - wse (water surface elevation)
# - velocity
# - flow
# - area
# - top_width

Plot Steady Profiles

Python
import matplotlib.pyplot as plt

results = RasControl.get_steady_results("02")

# Plot water surface for each profile
fig, ax = plt.subplots(figsize=(12, 6))

for profile in results['profile'].unique():
    profile_data = results[results['profile'] == profile]
    ax.plot(
        profile_data['station'],
        profile_data['wse'],
        label=profile
    )

ax.set_xlabel('River Station')
ax.set_ylabel('Water Surface Elevation (ft)')
ax.set_title('Steady Flow Profiles')
ax.legend()
ax.invert_xaxis()  # Upstream on left
plt.show()

Unsteady Results

Python
# Get available output times
times = RasControl.get_output_times("01")
print(f"Available times: {times}")
# Includes special "Max WS" timestep

# Extract unsteady results
results = RasControl.get_unsteady_results("01", max_times=20)
print(results)

# DataFrame columns:
# - river
# - reach
# - station
# - time (datetime or "Max WS")
# - wse
# - velocity
# - flow

Plot Time Series at Cross Section

Python
import matplotlib.pyplot as plt

results = RasControl.get_unsteady_results("01", max_times=50)

# Filter to single cross section (exclude Max WS)
station = 1000.0
xs_data = results[
    (results['station'] == station) &
    (results['time'] != "Max WS")
]

fig, axes = plt.subplots(2, 1, figsize=(12, 8), sharex=True)

axes[0].plot(xs_data['time'], xs_data['wse'])
axes[0].set_ylabel('Water Surface (ft)')
axes[0].set_title(f'Station {station}')

axes[1].plot(xs_data['time'], xs_data['flow'])
axes[1].set_ylabel('Flow (cfs)')
axes[1].set_xlabel('Time')

plt.tight_layout()
plt.show()

Setting Current Plan

Python
# Set by plan title
RasControl.set_current_plan("Steady Flow Run")

# Then perform operations
results = RasControl.get_steady_results()  # Uses current plan

Version Comparison Workflow

Compare results between HEC-RAS versions for migration validation:

Python
from ras_commander import init_ras_project, RasControl, RasPrj
import pandas as pd

versions = ["4.1", "5.0.6", "6.6"]
all_results = {}

for version in versions:
    project = RasPrj()
    init_ras_project("/path/to/project", version, ras_object=project)

    # Run plan
    success, _ = RasControl.run_plan("02", ras_object=project)

    if success:
        results = RasControl.get_steady_results("02", ras_object=project)
        all_results[version] = results

# Compare WSE at key stations
compare_df = pd.DataFrame()
for version, results in all_results.items():
    wse_col = results.groupby('station')['wse'].mean()
    compare_df[version] = wse_col

print("WSE Comparison by Version:")
print(compare_df)

# Calculate differences
compare_df['diff_4.1_to_6.6'] = compare_df['6.6'] - compare_df['4.1']
print(f"\nMax difference: {compare_df['diff_4.1_to_6.6'].abs().max():.2f} ft")

Open-Operate-Close Pattern

RasControl uses an open-operate-close pattern to prevent conflicts:

Python
# Each operation is self-contained
success1, _ = RasControl.run_plan("01")  # Opens, runs, closes
success2, _ = RasControl.run_plan("02")  # Opens, runs, closes

# No lingering HEC-RAS windows

Integration with Modern Workflow

Combine RasControl (legacy) with modern RAS Commander features:

Python
from ras_commander import (
    init_ras_project, RasControl, RasCmdr, ras
)

# For legacy extraction
init_ras_project("/path/to/old_project", "4.1")
legacy_results = RasControl.get_steady_results("01")

# For modern execution
init_ras_project("/path/to/new_project", "7.0")
RasCmdr.compute_plan("01")

# Use HDF extraction for modern results
from ras_commander import HdfResultsPlan
modern_wse = HdfResultsPlan.get_steady_wse(ras.plan_df['hdf_path'].iloc[0])

Error Handling

Python
from ras_commander import RasControl

try:
    success, messages = RasControl.run_plan("99")  # Non-existent
except ValueError as e:
    print(f"Plan not found: {e}")
except RuntimeError as e:
    if "COM" in str(e):
        print("COM interface error - check HEC-RAS installation")
    else:
        raise

Requirements

  • Windows operating system
  • HEC-RAS installed (version you want to use)
  • pywin32 package: pip install pywin32

Limitations

  • Windows only (COM interface)
  • One HEC-RAS instance at a time per version
  • No direct HDF access (use file-based output)
  • Slower than command-line execution

See Also