Fixit Module¶
Automated geometry repair for HEC-RAS models.
Overview¶
The fixit module provides automated fix capabilities for common HEC-RAS geometry issues. It complements the check module by providing repair functionality for detected issues.
Engineering Review Required
All fixes should be reviewed by a licensed professional engineer before accepting changes to production models. The visualization outputs provide an audit trail showing original and fixed configurations.
Supported Fixes¶
| Fix Type | Method | Description |
|---|---|---|
| Blocked Obstruction Overlaps | fix_blocked_obstructions() |
Resolves overlapping obstructions using max elevation envelope |
Quick Start¶
from ras_commander import RasFixit
# Detect overlaps (non-destructive)
results = RasFixit.detect_obstruction_overlaps("model.g04")
print(f"Found {results.total_xs_fixed} cross sections with overlaps")
# Fix with visualization for engineering review
results = RasFixit.fix_blocked_obstructions(
"model.g04",
backup=True, # Create timestamped backup
visualize=True # Generate before/after PNGs
)
print(f"Fixed {results.total_xs_fixed} cross sections")
print(f"Visualizations: {results.visualization_folder}")
RasFixit¶
RasFixit
¶
RasFixit - Automated Geometry Repair for HEC-RAS Models.
This module provides automated fix capabilities for common HEC-RAS geometry issues. All methods are static and follow ras-commander conventions.
Supported Fixes
General-purpose (any model version): - Blocked Obstruction Overlaps: Resolves overlapping obstructions using max elevation - HTAB Starting Elevation: Fixes starting_el < invert issues
Version upgrade (HEC-RAS 4.x → 6.x): - Bank Station Normalization: Re-writes sta/elev with normalized fixed-width formatting - Ineffective Flow Areas: Replaces right_station=0 sentinels with actual rightmost station - Manning's n Repair: Reconstructs missing data or snaps misaligned breakpoints to banks
Engineering Review Requirements
All fixes generated by RasFixit modify hydraulic model geometry. Results should be reviewed by a licensed professional engineer before accepting changes to production models.
The visualization output provides an audit trail showing: - Original obstruction configuration - Fixed obstruction configuration - Algorithm decisions made
Example
from ras_commander import RasFixit results = RasFixit.fix_blocked_obstructions("model.g01", visualize=True) print(f"Fixed {results.total_xs_fixed} cross sections")
Method Details¶
fix_blocked_obstructions¶
Main method for fixing overlapping blocked obstructions.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
geom_path |
str, Path | required | Path to HEC-RAS geometry file (.g##) |
backup |
bool | True | Create timestamped backup before modification |
visualize |
bool | False | Generate before/after PNG comparisons |
dry_run |
bool | False | Detect issues without modifying file |
Returns: FixResults object containing:
total_xs_checked: Number of cross sections examinedtotal_xs_fixed: Number of cross sections modifiedbackup_path: Path to backup file (if created)visualization_folder: Path to PNG visualizations (if generated)messages: List ofFixMessageobjects with detailed fix information
Example:
from ras_commander import RasFixit
# Fix with all options
results = RasFixit.fix_blocked_obstructions(
"BaldEagle.g01",
backup=True,
visualize=True
)
# Examine results
for msg in results.messages:
print(f"RS {msg.station}: {msg.original_count} -> {msg.fixed_count} obstructions")
print(f" Visualization: {msg.visualization_path}")
# Export to DataFrame
df = results.to_dataframe()
df.to_csv("fix_report.csv")
Result Classes¶
FixResults¶
FixResults
dataclass
¶
Container for all fix operation results.
Provides aggregate statistics and detailed messages for each fixed cross section.
Attributes:
| Name | Type | Description |
|---|---|---|
messages |
List[FixMessage]
|
List of FixMessage objects for each processed cross section |
total_xs_checked |
int
|
Total number of cross sections examined |
total_xs_fixed |
int
|
Number of cross sections that were modified |
backup_path |
Optional[Path]
|
Path to backup file (if created) |
visualization_folder |
Optional[Path]
|
Path to folder containing PNG visualizations |
statistics |
Dict[str, Any]
|
Additional statistics dictionary |
Example
results = RasFixit.fix_blocked_obstructions("model.g01") print(f"Fixed {results.total_xs_fixed} of {results.total_xs_checked} cross sections") df = results.to_dataframe()
Source code in ras_commander/fixit/results.py
to_dataframe
¶
Convert all messages to a pandas DataFrame.
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with one row per fix message. |
Source code in ras_commander/fixit/results.py
| Python | |
|---|---|
get_fixed_count
¶
Count cross sections that were actually fixed.
Returns:
| Type | Description |
|---|---|
int
|
Number of messages with action other than NO_ACTION. |
Source code in ras_commander/fixit/results.py
get_messages_by_action
¶
Filter messages by action type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
action
|
FixAction
|
FixAction enum value to filter by. |
required |
Returns:
| Type | Description |
|---|---|
List[FixMessage]
|
List of FixMessage objects matching the action. |
Source code in ras_commander/fixit/results.py
| Python | |
|---|---|
FixMessage¶
FixMessage
dataclass
¶
A single fix action message for a cross section.
Attributes:
| Name | Type | Description |
|---|---|---|
message_id |
str
|
Unique identifier for the fix type (e.g., "FX_BO_01") |
fix_type |
str
|
Category of fix (e.g., "OBSTRUCTION", "INEFFECTIVE", "BANK") |
river |
str
|
River name (if available) |
reach |
str
|
Reach name (if available) |
station |
str
|
River station identifier |
action |
FixAction
|
Type of fix action taken |
message |
str
|
Human-readable description of the fix |
original_count |
int
|
Number of obstructions before fix |
fixed_count |
int
|
Number of obstructions after fix |
original_data |
List[Tuple[float, float, float]]
|
List of (start_sta, end_sta, elevation) tuples before fix |
fixed_data |
List[Tuple[float, float, float]]
|
List of (start_sta, end_sta, elevation) tuples after fix |
visualization_path |
Optional[Path]
|
Path to before/after PNG visualization (if generated) |
Source code in ras_commander/fixit/results.py
to_dict
¶
Convert to dictionary for DataFrame creation.
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary representation of the fix message. |
Source code in ras_commander/fixit/results.py
FixAction¶
FixAction
¶
Bases: Enum
Type of fix action taken.
Source code in ras_commander/fixit/results.py
Log Parser¶
The log_parser module provides utilities for detecting obstruction errors from HEC-RAS compute logs.
from ras_commander.fixit import log_parser
# Parse log for errors
errors = log_parser.detect_obstruction_errors(log_content)
# Find geometry files
geom_files = log_parser.find_geometry_files_in_directory(project_dir)
# Generate report
print(log_parser.generate_error_report(errors))
Functions¶
| Function | Description |
|---|---|
detect_obstruction_errors(log_content) |
Parse log text, return list of error dicts |
extract_geometry_files(log_content, project_dir) |
Find geometry file references in log |
find_geometry_files_in_directory(directory) |
Scan directory for .g## files |
has_obstruction_errors(log_file_path) |
Quick boolean check for errors |
generate_error_report(errors) |
Format errors for human review |
extract_cross_section_ids(log_content) |
Get list of affected river stations |
Automated Workflow Example¶
from ras_commander import RasFixit
from ras_commander.fixit import log_parser
# Step 1: Check log for errors
if log_parser.has_obstruction_errors("compute.log"):
# Step 2: Parse errors
with open("compute.log", "r") as f:
errors = log_parser.detect_obstruction_errors(f.read())
print(f"Found {len(errors)} obstruction errors")
# Step 3: Find and fix geometry files
geom_files = log_parser.find_geometry_files_in_directory(project_dir)
for geom_path in geom_files:
results = RasFixit.fix_blocked_obstructions(
geom_path,
visualize=True
)
print(f"Fixed {results.total_xs_fixed} cross sections in {geom_path}")
Algorithm Details¶
Elevation Envelope¶
The core algorithm for fixing overlapping obstructions:
- Collect Critical Stations: Extract all start/end stations from obstructions
- Find Max Elevation: For each segment between stations, use the maximum elevation from all overlapping obstructions (hydraulically conservative)
- Merge Segments: Combine adjacent segments with identical elevations
- Insert Gaps: Add 0.02-unit gaps where different elevations meet (HEC-RAS requirement)
Original: [100-120@elev5, 110-130@elev3] (overlap 110-120)
Step 1 - Critical stations: [100, 110, 120, 130]
Step 2 - Max elevation per segment:
100-110: elev 5 (only covered by first obstruction)
110-120: elev 5 (max of 5 and 3)
120-130: elev 3 (only covered by second obstruction)
Step 3 - Merge same-elevation:
100-120: elev 5 (merged)
120-130: elev 3
Step 4 - Insert gap:
100-120: elev 5
120.02-130: elev 3 (0.02 gap added)
Result: [100-120@elev5, 120.02-130@elev3]
Why Max Elevation?¶
Using maximum elevation in overlap zones is hydraulically conservative:
- Blocked obstructions represent areas where flow is completely blocked up to the specified elevation
- Using the maximum ensures we preserve the most restrictive flow condition
- This prevents underestimating flood impacts
Gap Insertion¶
HEC-RAS requires a minimum separation between adjacent obstructions:
- Touching obstructions (e.g., end=100.0, start=100.0) cause errors
- The algorithm inserts 0.02-unit gaps where obstructions would otherwise touch
- This is the minimum safe separation that preserves hydraulic behavior
Integration with Check Module¶
The fixit module complements the check module:
from ras_commander import RasCheck, RasFixit
# Detect issues with RasCheck (operates on HDF files)
check_results = RasCheck.check_xs(geom_hdf)
obstruction_issues = [m for m in check_results.messages
if m.message_id.startswith('XS_BO')]
if obstruction_issues:
print(f"Found {len(obstruction_issues)} obstruction issues")
# Fix with RasFixit (operates on plain text geometry files)
fix_results = RasFixit.fix_blocked_obstructions(
"model.g01",
visualize=True
)
print(f"Fixed {fix_results.total_xs_fixed} cross sections")
| Module | Input | Purpose |
|---|---|---|
RasCheck |
HDF files (.p##.hdf) | Detect issues during results review |
RasFixit |
Geometry files (.g##) | Repair issues in source geometry |
Visualization Output¶
When visualize=True, the module generates PNG files showing:
- Top Panel: Original obstructions (with overlaps) using 'viridis' colormap
- Bottom Panel: Fixed obstructions (elevation envelope) using 'plasma' colormap
Files are saved to: {ProjectName}_g{##}_Obstructions_Fixed/RS_{station}.png
These visualizations are critical for engineering review - they show exactly what changes were made and why.
Example Project¶
The repository includes the HCFCD M3 Model A120-00-00 (Harris County Flood Control District) as a real-world example with blocked obstruction issues:
- Location:
examples/example_projects/A120-00-00/ - Geometry files:
A120_00_00.g01,A120_00_00.g02 - Cross sections: 91 total, 15 with overlapping obstructions
- Source:
examples/data/A120-00-00.zip
An example notebook for blocked obstructions will be added in a future release; the API above covers the current workflow end-to-end.