Executes HEC-RAS plans using RasControl class via HECRASController COM interface for legacy HEC-RAS versions (3.x-5.x). Handles COM lifecycle management, session tracking, orphan cleanup, steady/unsteady result extraction, and watchdog protection for Jupyter notebooks. Use when automating legacy HEC-RAS, extracting results from older versions, comparing across HEC-RAS versions, or needing COM-based automation that RasCmdr cannot provide. Triggers: RasControl, HECRASController, COM, legacy, HE...
Scanned 2/12/2026
Install via CLI
openskills install majiayu000/claude-skill-registry---
name: executing-hecras-rascontrol
description: |
Executes HEC-RAS plans using RasControl class via HECRASController COM interface
for legacy HEC-RAS versions (3.x-5.x). Handles COM lifecycle management, session
tracking, orphan cleanup, steady/unsteady result extraction, and watchdog
protection for Jupyter notebooks. Use when automating legacy HEC-RAS, extracting
results from older versions, comparing across HEC-RAS versions, or needing
COM-based automation that RasCmdr cannot provide.
Triggers: RasControl, HECRASController, COM, legacy, HEC-RAS 4, HEC-RAS 5,
win32com, steady results, unsteady results, version comparison, COM interface,
legacy automation, 3.x, 4.x, 5.x, orphan cleanup, watchdog, session tracking.
---
# Executing HEC-RAS Plans via RasControl (COM Interface)
This skill helps you automate HEC-RAS versions 3.x-5.x using the RasControl class, which wraps the HECRASController COM interface with ras-commander conventions.
## Primary Sources
### 1. RasControl Implementation
**Location**: `ras_commander/RasControl.py`
**Key sections**:
- Lines 438-527: `RasControl` class with version mapping and output variable codes
- Lines 543-742: COM lifecycle management (`_com_open_close()`)
- Lines 746-884: `run_plan()` with smart skip and watchdog protection
- Lines 936-1177: `get_steady_results()` - extract steady state profiles
- Lines 1179-1458: `get_unsteady_results()` - extract time series with Max WS
- Lines 1647-1909: Process management API (orphan cleanup)
### 2. Working Examples (Jupyter Notebooks)
- `examples/120_automating_ras_with_win32com.ipynb` - Low-level win32com exploration
- `examples/121_legacy_hecrascontroller_and_rascontrol.ipynb` - Complete RasControl workflows
### 3. Related Documentation
- `.claude/rules/hec-ras/execution.md` - Execution mode comparison
- `ras_commander/CLAUDE.md` - Library context and module organization
## When to Use RasControl vs RasCmdr
| Scenario | Use RasControl | Use RasCmdr |
|----------|---------------|-------------|
| HEC-RAS 3.x-5.x | Yes | No |
| HEC-RAS 6.x+ | No (use COM only if needed) | Yes (preferred) |
| Need GUI interaction | Yes (COM opens GUI) | No (headless) |
| Version comparison | Yes | Yes |
| Production workflows | Limited | Preferred |
| Parallel execution | No (single-threaded) | Yes |
## Quick Reference
### Basic Workflow
```python
from ras_commander import init_ras_project, RasControl
# Initialize project with version
init_ras_project(project_path, "5.0.6") # Or "506", "4.1", "41", etc.
# Run plan (checks if current, skips if up-to-date)
success, msgs = RasControl.run_plan("02")
# Force recomputation
success, msgs = RasControl.run_plan("02", force_recompute=True)
# Extract steady state results
df_steady = RasControl.get_steady_results("02")
# Extract unsteady time series (includes Max WS row)
df_unsteady = RasControl.get_unsteady_results("01")
```
### Version Format Support
RasControl accepts flexible version formats:
```python
# All equivalent - HEC-RAS 4.1
init_ras_project(path, "4.1")
init_ras_project(path, "41")
# All equivalent - HEC-RAS 5.0.6
init_ras_project(path, "5.0.6")
init_ras_project(path, "506")
# All equivalent - HEC-RAS 6.6
init_ras_project(path, "6.6")
init_ras_project(path, "66")
```
**Supported versions**: 3.0-3.1.3, 4.0-4.1, 5.0-5.0.7, 6.0-6.7
### Separating Max WS from Time Series
```python
# Max WS contains computational maximums (critical for design!)
df_maxws = df_unsteady[df_unsteady['time_string'] == 'Max WS']
df_timeseries = df_unsteady[df_unsteady['datetime'].notna()]
# Plot time series with Max WS reference line
import matplotlib.pyplot as plt
xs_data = df_timeseries[df_timeseries['node_id'] == '10000']
plt.plot(xs_data['datetime'], xs_data['wsel'])
plt.axhline(df_maxws[df_maxws['node_id'] == '10000']['wsel'].iloc[0],
color='r', linestyle='--', label='Max WS')
```
## COM Lifecycle Management
### Critical Pattern: Open-Operate-Close
RasControl handles COM lifecycle automatically via `_com_open_close()`:
```python
# Internal pattern (you don't call this directly)
# 1. Create COM object: win32com.client.Dispatch("RAS66.HECRASController")
# 2. Open project: com_rc.Project_Open(project_path)
# 3. Create session lock for crash recovery
# 4. Execute operation
# 5. Close HEC-RAS: com_rc.QuitRas()
# 6. Clean up session tracking
```
**Why this matters**:
- COM objects MUST be released properly
- Orphaned ras.exe processes consume resources
- Session tracking enables recovery after crashes
### Session Tracking Infrastructure
RasControl tracks all active COM sessions:
```python
# List tracked processes
df = RasControl.list_processes()
print(df) # Shows PID, tracked status, project, age
# List all ras.exe (including untracked)
df_all = RasControl.list_processes(show_all=True)
```
### Orphan Detection and Cleanup
After crashes or Jupyter kernel restarts:
```python
# Scan for orphaned processes
orphans = RasControl.scan_orphans()
if orphans:
print(f"Found {len(orphans)} orphaned processes")
# Interactive cleanup (prompts for confirmation)
RasControl.cleanup_orphans()
# Automatic cleanup (no prompts)
count = RasControl.cleanup_orphans(interactive=False)
# Dry run (see what would be cleaned)
RasControl.cleanup_orphans(dry_run=True)
# Nuclear option - kill ALL ras.exe (requires "YES" confirmation)
RasControl.force_cleanup_all()
```
### Watchdog Protection
For long-running operations in Jupyter:
```python
# With watchdog (default) - survives kernel restarts
success, msgs = RasControl.run_plan("01", use_watchdog=True, max_runtime=3600)
# Without watchdog (not recommended in Jupyter)
success, msgs = RasControl.run_plan("01", use_watchdog=False)
```
**Watchdog features**:
- Independent Python process monitors parent
- Terminates ras.exe if Python crashes
- Enforces max_runtime timeout
- Responds to manual lock file deletion
## Result Extraction
### Steady State Results
```python
df = RasControl.get_steady_results("02")
# DataFrame columns:
# - river, reach, node_id (location)
# - profile (profile name)
# - wsel (water surface elevation)
# - velocity, flow, froude, energy, max_depth, min_ch_el
```
### Unsteady Results with Datetime
```python
df = RasControl.get_unsteady_results("01")
# DataFrame columns:
# - river, reach, node_id (location)
# - time_index (1 = Max WS, 2+ = timesteps)
# - time_string ("Max WS" or "18FEB1999 0000")
# - datetime (datetime64[ns], NaT for Max WS)
# - wsel, velocity, flow, froude, energy, max_depth, min_ch_el
```
### Computation Messages
```python
# Read computation messages (tries .txt then HDF)
msgs = RasControl.get_comp_msgs("01")
print(msgs)
```
## Common Patterns
### Pattern: Version Comparison
```python
from ras_commander import RasPlan
versions = [("5.0.6", "506"), ("6.3.1", "631"), ("6.6", "66")]
results = {}
for version_name, version_code in versions:
# Clone plan for this version
new_plan = RasPlan.clone_plan("01", new_shortid=f"v{version_code}")
# Run with this version
init_ras_project(project_path, version_name)
RasControl.run_plan(new_plan, force_recompute=True)
# Extract results
results[version_name] = RasControl.get_unsteady_results(new_plan)
# Compare across versions
for version, df in results.items():
max_wse = df[df['time_string'] == 'Max WS']['wsel'].max()
print(f"v{version}: Max WSE = {max_wse:.2f} ft")
```
### Pattern: Error Recovery
```python
try:
success, msgs = RasControl.run_plan("01")
except Exception as e:
print(f"COM error: {e}")
# Clean up any orphaned processes
RasControl.cleanup_orphans(interactive=False)
# Read computation messages for diagnosis
comp_msgs = RasControl.get_comp_msgs("01")
print(f"Computation messages:\n{comp_msgs}")
```
### Pattern: Extract Before Running
```python
# Check if results are current before running
success, msgs = RasControl.run_plan("01")
if "Results are current" in msgs[0]:
print("Skipped - results already up-to-date")
else:
print("Plan executed")
# Extract results (works whether run or skipped)
df = RasControl.get_unsteady_results("01")
```
## Troubleshooting
### COM Object Not Found
```python
# Error: "COM object not found" or "Class not registered"
# Verify HEC-RAS COM registration
import win32com.client
try:
rc = win32com.client.Dispatch("RAS66.HECRASController")
print("COM registered correctly")
except Exception as e:
print(f"COM issue: {e}")
print("Solution: Reinstall HEC-RAS or run as Administrator")
```
### HEC-RAS Freezes or Hangs
```python
# Check for orphaned processes
RasControl.list_processes(show_all=True)
# Clean up orphans
RasControl.cleanup_orphans()
# If still stuck, nuclear option (kills ALL ras.exe)
RasControl.force_cleanup_all()
```
### Results Extraction Fails
```python
# If get_steady_results() or get_unsteady_results() fails:
# 1. Check if plan was executed
success, msgs = RasControl.run_plan("01", force_recompute=True)
# 2. Check computation messages
comp_msgs = RasControl.get_comp_msgs("01")
print(comp_msgs)
# 3. Look for "error" or "warning" in messages
if "error" in comp_msgs.lower():
print("Computation had errors - fix model issues")
```
### Version Mismatch
```python
# Error: Version 'X.Y' not supported
# Check supported versions
print(RasControl.VERSION_MAP.keys())
# Use normalized format
init_ras_project(path, "5.0.6") # Not "5.06" or "5.6"
```
## Performance Considerations
### COM is Single-Threaded
- RasControl cannot run plans in parallel
- Each operation opens/closes HEC-RAS GUI
- For parallel execution, use RasCmdr with HEC-RAS 6.x+
### GUI Overhead
- COM interface opens HEC-RAS GUI (requires active desktop)
- Slower than RasCmdr subprocess execution
- Consider RDP session for headless servers
### Memory Usage
- Large unsteady simulations may consume significant RAM
- Use `max_times` parameter to limit extraction:
```python
df = RasControl.get_unsteady_results("01", max_times=20)
```
## Migration Path: RasControl to RasCmdr
When upgrading to HEC-RAS 6.x+:
```python
# Legacy (RasControl - HEC-RAS 5.x)
init_ras_project(path, "5.0.6")
RasControl.run_plan("01")
df = RasControl.get_steady_results("01")
# Modern (RasCmdr - HEC-RAS 6.x)
from ras_commander import RasCmdr
from ras_commander.hdf import HdfResultsPlan
init_ras_project(path, "6.6")
RasCmdr.compute_plan("01")
hdf = HdfResultsPlan(ras.plan_df.loc[0, 'HDF_Results_Path'])
wse = hdf.get_steady_wse()
```
## Where to Learn More
### Primary Sources
- **RasControl.py** - Complete API with docstrings
- **examples/121_legacy_hecrascontroller_and_rascontrol.ipynb** - Full workflow examples
### Related Skills
- **executing-hecras-plans** - Modern RasCmdr execution (HEC-RAS 6.x+)
- **extracting-hecras-results** - HDF-based result extraction
### Related Rules
- `.claude/rules/hec-ras/execution.md` - Execution mode comparison
- `.claude/rules/python/static-classes.md` - Static class pattern
---
**Remember**: RasControl is for legacy HEC-RAS (3.x-5.x). For HEC-RAS 6.x+, prefer RasCmdr and HDF-based methods for better performance and reliability.
No comments yet. Be the first to comment!