ras2fim-2D flood inundation mapping (optional)¶
RasFim maps water-surface elevation (WSE) profiles defined on HEC-RAS 2D
cells to depth and WSE rasters with the ras2fim-2D Step 05 method. It maps a
batch of profiles against one shared terrain, accepts a bounding box or a
polygon area of interest (AOI), and returns a product manifest DataFrame.
The feature is optional. Install the Python extra and the matching Linux x86-64 or Windows x86-64 engine wheel into the same environment:
pip install "ras-commander[fim]"
pip install /path/to/ras2fim_engine-0.1.0-py3-none-<platform>.whl
ras2fim-batch method
The repository wheels/ folder includes both tested wheels, checksums, and
BUILD.json provenance. See wheels/README.md for the exact platform filenames.
Wheels are rebuilt by the Engine wheels workflow
and available as its platform artifacts with SHA-256 checksums. Linux wheels
target glibc 2.28 or newer. The wheel installs ras2fim-batch on the environment's
PATH; no Rust compiler or system GDAL is needed for the native engine.
The Python GIS stack remains required. Source builds are also supported.
The workflow does not publish to PyPI. Installed-binary synthetic mapping tests
run on both platforms; retained real-model agreement evidence remains Linux-only.
Method¶
The method comes from ras2fim-2D Step 05 (Andy Carter, PE, Center for Water and the Environment, The University of Texas at Austin; BSD 3-Clause). For each profile:
- Wet cells are the cells whose profile WSE is not NaN.
- Samples are the wet cell centres in ascending
cell_idxorder, followed by the wet-cell polygon vertices outside the convex hull of those centres. Vertices that round to the same 0.01 model units are merged with their mean WSE. - WSE is interpolated linearly on the SciPy Delaunay triangulation of the
samples over
np.arangeaxes that span the wet-cell union at the terrain pixel width, then masked to that union. - Depth is WSE minus the terrain sampled at each pixel centre; negative depths become NaN. WSE is then terrain plus depth where depth is finite.
- Depth and WSE are reprojected with nearest-neighbour resampling to the output CRS on a grid whose origin is snapped to a multiple of the output resolution.
- The floodplain polygon covers pixels with
0 < depth <= 10000in the model vertical units.
Not RAS Mapper stored maps
This method differs from RAS Mapper stored maps
(RasProcess.store_maps()), which use HEC-RAS's own rendering of the
hydraulic results. The two produce different rasters; do not substitute
one for the other.
Engine¶
The interpolation, terrain, and reprojection arithmetic runs in the
ras2fim-batch engine. Python prepares the global samples, the SciPy Delaunay
topology, the query axes, the wet mask, and the explicit final grid, then
validates each engine output before writing products. The engine source and
build instructions are in rust/step05/ of the RAS Commander repository.
The tested scope is x86-64 Linux; a Windows engine build is not qualified.
RasFim resolves the engine in this order:
- the
engine_path=argument; - the
RAS_COMMANDER_FIM_ENGINEenvironment variable; ras2fim-batchonPATH.
A missing engine raises FimEngineNotFoundError. RAS Commander never builds,
downloads, or installs the engine, and it has no Python fallback for this
method.
from ras_commander import RasFim
info = RasFim.get_engine_info("/opt/ras2fim/ras2fim-batch")
print(info["method"])
Map a ras2fim-2D catalog¶
A ras2fim-2D hydraulic-results GeoPackage stores one polygon per cell in layer
02_cells_wsel_ar, with cell_idx and one wsel_* column per flow profile.
Cell centres come from the plan HDF through HdfMesh.get_mesh_cell_points().
from ras_commander import RasFim
manifest = RasFim.map_ras2fim_results(
"results.gpkg",
"model.p01.hdf",
"terrain.tif",
"fim_out", # new or empty folder
engine_path="/opt/ras2fim/ras2fim-batch",
output_crs="EPSG:3857",
output_resolution=3.0,
threads=8,
)
print(manifest[["profile", "status", "flooded_pixels", "depth_tif"]])
Map cells from a plan HDF¶
get_cells_from_plan_hdf() builds a one-profile cell table from the plan's
maximum WSE and maximum depth. A cell is wet when its maximum depth exceeds
min_depth. Frames from several plans on the same geometry can be joined on
cell_idx to map them as one batch.
cells = RasFim.get_cells_from_plan_hdf("model.p01.hdf", profile_name="wsel_max")
manifest = RasFim.map_profiles(
cells, "terrain.tif", "fim_max",
hdf_path="model.p01.hdf",
profiles=["wsel_max"],
)
Any GeoDataFrame with cell_idx, cell polygons, and numeric profile columns in
the terrain CRS can be mapped the same way. Pass centers= (a GeoDataFrame of
cell_idx and centre points) instead of hdf_path= when the centres come from
elsewhere. When an HDF has more than one 2D flow area, pass mesh_name=.
Area of interest¶
Pass either bbox=(xmin, ymin, xmax, ymax) or polygon=. A polygon can be a
Shapely Polygon or MultiPolygon, a GeoDataFrame, or a vector file (use
polygon_layer= for a multi-layer file). Holes and multiple parts are kept,
and file features are unioned. A bbox without aoi_crs is in output_crs;
a Shapely polygon requires aoi_crs; a file or frame CRS that conflicts with
aoi_crs is an error.
manifest = RasFim.map_ras2fim_results(
"results.gpkg", "model.p01.hdf", "terrain.tif", "fim_reach",
polygon="reach.gpkg", polygon_layer="reach",
)
The AOI selects pixels of the final output grid whose centres lie inside it. It never changes sample preparation, triangulation, query axes, the wet mask, terrain sampling, the grid origin, or the reprojection, so an AOI product is the crop of the full product. Products cover the AOI window; pixels outside a polygon are NaN.
Wet-cell discovery, sample preparation, and triangulation always use every wet cell of the profile, and the engine's lookup warp spans the full output grid. AOI runtime therefore does not shrink in proportion to the AOI area.
Terrain-anchored grids and coplanar samples (opt-in)¶
Two keywords depart from the upstream Step 05 recipe. Both default to the
upstream behaviour and are recorded in fim_manifest.json under options.
grid_anchor="terrain" places the query axes and the final grid on the
terrain pixel lattice. Upstream starts the query axes at the wet-union bounds
and snaps the final origin to a multiple of output_resolution, so the
terrain pixel each output pixel takes depends on the phase of the wet-union
bounds. On one 1 m terrain whose origin is not a multiple of the pixel size,
that put every output pixel of a reach one terrain column away from the
terrain pixel it lies on, and moved results when the cell set changed. With
the anchor, each output pixel takes the terrain pixel it lies on, products
stack with the terrain without resampling, and equal lattice positions give
bit-equal transforms. The anchor needs a north-up terrain with square pixels,
output_crs equal to the results CRS, and output_resolution equal to the
terrain pixel size; anything else raises FimUnsupportedError. Grid edges are
integer lattice positions, and the grid is clamped to the terrain extent where the
wet cells reach past it (those pixels would only read terrain nodata). Samples and
triangulation are unchanged.
coplanar="ignore" maps a profile whose samples include points Qhull reports
as coplanar or duplicate (for example two perimeter vertices closer than the
0.01-unit merge) without those points, as upstream griddata does. The
default "error" fails such a profile. The count per profile is in the
manifest's coplanar_samples.
manifest = RasFim.map_profiles(
cells, "terrain.tif", "fim_reach", centers=centers,
output_crs=cells.crs, output_resolution=terrain_pixel_size,
polygon=reach_polygon, aoi_crs=cells.crs,
grid_anchor="terrain", coplanar="ignore",
)
Batches, resources, and outputs¶
| Keyword | Meaning |
|---|---|
profiles |
Profile columns; default all columns starting with profile_prefix ("wsel") |
profiles_per_request |
Profiles prepared and sent per engine call (default 4) |
max_inflight_profiles |
Profiles the engine evaluates concurrently in one call (default 1); they share one decoded-terrain cache and the threads pool |
threads |
Engine thread pool; default min(cpu_count - 1, 16) |
max_cache_mib |
Engine decoded-terrain cache budget (default 64) |
max_profile_mib |
Engine per-profile planned working-set budget (default 1024); a profile over budget fails before allocation |
nudge_method |
"lookup" (default) or "full_warp" |
keep_work |
Keep engine requests and raw outputs in output_folder/.fim_work |
The engine budgets are admission checks on the engine's planned allocations.
They are not a resident-memory limit for the engine or the Python process.
Each profile in flight counts against max_profile_mib.
Each profile writes the ras2fim-2D product names into output_folder:
09_<profile>_depth_nudge.tifand10_<profile>_wsel_nudge.tif: float32, LZW-compressed, NaN nodata;11_<profile>_floodplain_ar.gpkg: one floodplain (Multi)Polygon, written when at least one pixel is flooded.
fim_manifest.json records the engine path and method record, the options,
the AOI, the input file identities, engine reports, and one entry per profile.
The returned DataFrame has the same rows. Its columns are declared as
fim_product_manifest in ras_commander/schemas.py, which also feeds the
published machine-readable DataFrame surface:
| Column | Meaning |
|---|---|
profile, status |
Profile name; complete, no_flooded_pixels, empty_aoi, failed, or not_run |
wet_cells, samples |
Wet cells and interpolation samples used for the global preparation |
depth_tif, wse_tif, floodplain_gpkg |
Product paths (None when not written) |
col_off, row_off, width, height, transform, crs |
Written window on the full output grid |
depth_finite_pixels, flooded_pixels |
Finite depth pixels and floodplain pixels |
error |
Diagnostic for failed or not_run rows |
Errors and inputs¶
output_foldermust be new or empty; no existing file is replaced.- Profile names use letters, digits, underscores, dots, or hyphens and must be
unique ignoring case. Caller centres require unique integer
cell_idxvalues and non-empty point geometries. - Input files are opened read-only, and caller DataFrames are not modified. Their size and modification time are checked after mapping.
- The hydraulic results and the terrain must share a CRS; the output CRS needs an EPSG code.
- Coplanar samples, infinite WSE, grids that extend beyond the terrain, and
similar inputs raise
FimUnsupportedErroror mark the profilefailed. - An engine exit other than 0 or 2, or an output that is missing, sized
incorrectly, has inconsistent finite counts, or has values outside a polygon
mask, fails the profile or raises
FimEngineError. - Typed errors isolate a failed profile from the others in its batch; they do not sandbox the engine process or bound its operating-system resources.
- After writing the manifest,
map_profiles()raisesFimMappingErrorwhen any profile failed;error.manifestholds the DataFrame. Passraise_on_failure=Falseto return it instead.
The FIM engine comparison
records the agreement checks and the measurement scope. Its throughput
observations come from the ras2fim-2D adapter, not from RasFim, and do not
establish a general speedup.
Complete source reference¶
Static namespace for the optional ras2fim-2D FIM mapping method.
All methods are static; do not instantiate.
find_engine
staticmethod
¶
Return the path of the ras2fim-batch engine executable.
Resolution order: engine_path, then the RAS_COMMANDER_FIM_ENGINE
environment variable, then ras2fim-batch on PATH.
Raises:
| Type | Description |
|---|---|
FimEngineNotFoundError
|
No executable was found. The engine is never built or installed automatically. |
get_engine_info
staticmethod
¶
Return the engine path and its method record (ras2fim-batch method).
get_cells_from_results_gpkg
staticmethod
¶
get_cells_from_results_gpkg(results_gpkg: PathLike, layer: str = RAS2FIM_CELLS_LAYER) -> 'gpd.GeoDataFrame'
Read a ras2fim-2D hydraulic-results cell layer.
The ras2fim-2D layer 02_cells_wsel_ar holds one polygon per cell
with cell_idx and WSE profile columns (wsel_*), NaN where the
cell is dry for that profile. The file is opened read-only.
Returns:
| Type | Description |
|---|---|
GeoDataFrame
|
The layer as stored. |
get_cell_centers
staticmethod
¶
get_cell_centers(hdf_path: PathLike, cell_ids: Optional[Iterable[int]] = None, *, mesh_name: Optional[str] = None) -> 'gpd.GeoDataFrame'
Return HEC-RAS 2D cell centres for the FIM method.
Centres are the computational cell centres (HdfMesh.get_mesh_cell_points),
not polygon centroids.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hdf_path
|
PathLike
|
Plan or geometry HDF that contains the 2D mesh geometry. |
required |
cell_ids
|
Optional[Iterable[int]]
|
Zero-based cell indices to return. |
None
|
mesh_name
|
Optional[str]
|
2D flow area name. Required when the HDF has more than one. |
None
|
Returns:
| Type | Description |
|---|---|
GeoDataFrame
|
Cell indices and point geometries. |
Raises:
| Type | Description |
|---|---|
FimUnsupportedError
|
No mesh, an ambiguous or unknown mesh, or a requested cell that is not in the mesh. |
get_cells_from_plan_hdf
staticmethod
¶
get_cells_from_plan_hdf(hdf_path: PathLike, *, mesh_name: Optional[str] = None, profile_name: str = 'wsel_max', min_depth: float = 0.0) -> 'gpd.GeoDataFrame'
Build a one-profile cell table from a plan HDF's maximum results.
Uses HdfMesh.get_mesh_cell_polygons(strict=True),
HdfResultsMesh.get_mesh_max_ws and HdfResultsMesh.get_mesh_max_depth.
A cell is wet when its maximum depth is greater than min_depth;
dry cells get NaN.
Returns:
| Type | Description |
|---|---|
GeoDataFrame
|
Cell indices, profile WSE and polygon geometries.
Frames from plans on the same geometry can be joined on
|
resolve_aoi
staticmethod
¶
resolve_aoi(bbox: Optional[Sequence[float]] = None, polygon: Any = None, *, polygon_layer: Optional[str] = None, aoi_crs: Any = None, default_crs: Any = None) -> Optional[FimAoi]
Validate a bounding box OR polygon area of interest.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bbox
|
Optional[Sequence[float]]
|
|
None
|
polygon
|
Any
|
A Shapely Polygon/MultiPolygon (needs |
None
|
polygon_layer
|
Optional[str]
|
Layer of a multi-layer polygon file. |
None
|
aoi_crs
|
Any
|
CRS of the AOI. Must agree with a frame or file CRS. |
None
|
default_crs
|
Any
|
CRS used for a bbox without |
None
|
Returns:
| Type | Description |
|---|---|
Optional[FimAoi]
|
FimAoi, or None when neither bbox nor polygon is given. |
Raises:
| Type | Description |
|---|---|
FimAoiError
|
Both or invalid AOIs, a missing or conflicting CRS, invalid or empty geometry. |
prepare_profile
staticmethod
¶
prepare_profile(cells, centers, profile: str, pixel_size: float, lattice_origin: Optional[Sequence[float]] = None) -> FimPreparedProfile
Prepare the global interpolation samples and axes for one profile.
This is the preparation map_profiles sends to the engine, exposed
for inspection and qualification. It always uses every wet cell.
Inputs are not modified. lattice_origin (a terrain pixel corner
(x, y)) gives the grid_anchor="terrain" query axes.
map_profiles
staticmethod
¶
map_profiles(cells, terrain_path: PathLike, output_folder: PathLike, *, profiles: Optional[Sequence[str]] = None, profile_prefix: str = DEFAULT_PROFILE_PREFIX, centers=None, hdf_path: Optional[PathLike] = None, mesh_name: Optional[str] = None, output_crs: Any = 'EPSG:3857', output_resolution: float = 3.0, bbox: Optional[Sequence[float]] = None, polygon: Any = None, polygon_layer: Optional[str] = None, aoi_crs: Any = None, aoi: Optional[FimAoi] = None, engine_path: Optional[PathLike] = None, threads: Optional[int] = None, profiles_per_request: int = 4, max_inflight_profiles: int = 1, max_cache_mib: int = 64, max_profile_mib: int = 1024, nudge_method: str = 'lookup', grid_anchor: str = 'ras2fim', coplanar: str = 'error', terrain_crs_fnv1a64: Optional[str] = None, write_floodplain: bool = True, keep_work: bool = False, timeout: Optional[float] = None, raise_on_failure: bool = True) -> pd.DataFrame
Map a batch of cell WSE profiles to depth and WSE rasters.
Every profile shares one terrain and one engine session per request
of profiles_per_request profiles. Sample preparation, Delaunay
topology, query axes and the wet mask always use every wet cell of a
profile. An AOI only selects pixels of the final output grid, so an
AOI product equals the crop of the full product. The preparation cost
therefore does not shrink with the AOI.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cells
|
GeoDataFrame
|
Cell polygons with |
required |
terrain_path
|
PathLike
|
Single-band terrain GeoTIFF, same CRS as |
required |
output_folder
|
PathLike
|
New or empty folder for products and the manifest. |
required |
profiles
|
Optional[Sequence[str]]
|
Profile columns to map. Default: columns starting with
|
None
|
centers
|
GeoDataFrame
|
Unique integer |
None
|
hdf_path
|
Optional[PathLike]
|
Plan or geometry HDF; centres are read through
:meth: |
None
|
mesh_name
|
Optional[str]
|
2D flow area for |
None
|
output_crs
|
Any
|
CRS of the final grid (must have an EPSG code). |
'EPSG:3857'
|
output_resolution
|
float
|
Final pixel size in |
3.0
|
bbox
|
Optional[Sequence[float]]
|
Bounding box; without |
None
|
polygon
|
Any
|
Polygon area of interest; see :meth: |
None
|
polygon_layer
|
Optional[str]
|
Layer name for a polygon file. |
None
|
aoi_crs
|
Any
|
CRS of the area of interest. |
None
|
aoi
|
Optional[FimAoi]
|
A :class: |
None
|
engine_path
|
Optional[PathLike]
|
Engine executable; see :meth: |
None
|
threads
|
Optional[int]
|
Engine threads. Default |
None
|
profiles_per_request
|
int
|
Profiles prepared and run per engine call. This bounds the prepared inputs on disk at one time. |
4
|
max_inflight_profiles
|
int
|
Profiles the engine evaluates concurrently
within one call (default 1). Concurrent profiles share one
decoded-terrain cache and the |
1
|
max_cache_mib
|
int
|
Engine decoded-terrain cache budget. |
64
|
max_profile_mib
|
int
|
Engine per-profile working-set budget; a profile whose planned peak exceeds it fails before allocation. |
1024
|
nudge_method
|
str
|
|
'lookup'
|
grid_anchor
|
str
|
|
'ras2fim'
|
coplanar
|
str
|
|
'error'
|
terrain_crs_fnv1a64
|
Optional[str]
|
Engine WKT hash to accept when the engine cannot identify the terrain EPSG code. |
None
|
write_floodplain
|
bool
|
Write product 11. |
True
|
keep_work
|
bool
|
Keep the engine requests and raw outputs in
|
False
|
timeout
|
Optional[float]
|
Seconds allowed per engine call (default no limit). |
None
|
raise_on_failure
|
bool
|
Raise :class: |
True
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with one row per profile ( |
DataFrame
|
schema). |
DataFrame
|
|
DataFrame
|
stopped the batch sequence). The manifest is also written to |
DataFrame
|
|
Raises:
| Type | Description |
|---|---|
FimEngineNotFoundError
|
No engine executable. |
FimEngineError
|
The engine rejected a request or crashed. |
FimUnsupportedError
|
Input outside the engine contract. |
FimAoiError
|
Invalid area of interest. |
FimMappingError
|
One or more profiles failed. |
FileExistsError
|
|
map_ras2fim_results
staticmethod
¶
map_ras2fim_results(results_gpkg: PathLike, hdf_path: PathLike, terrain_path: PathLike, output_folder: PathLike, **kwargs) -> pd.DataFrame
Map every wsel_* profile of a ras2fim-2D hydraulic-results GeoPackage.
Reads layer 02_cells_wsel_ar, takes cell centres from hdf_path,
and calls :meth:map_profiles with kwargs.
A validated bounding-box or polygon area of interest.
Attributes:
| Name | Type | Description |
|---|---|---|
kind |
str
|
|
geometry |
Any
|
Shapely box, Polygon, or MultiPolygon in |
crs |
Any
|
:class: |
source |
str
|
Human-readable origin, recorded in the product manifest. |
Global interpolation inputs for one WSE profile.
Attributes:
| Name | Type | Description |
|---|---|---|
profile |
str
|
Profile (column) name. |
x, |
(y, z)
|
Sample coordinates and WSE in upstream order (wet centres, then merged hull-exterior vertices). |
x_axis, |
y_axis
|
Exact |
xmin, |
(ymin, res)
|
Raster-01 origin and pixel size. |
wet_union |
Any
|
Shapely union of the wet cell polygons. |
crs |
Any
|
CRS of the hydraulic results (and of all coordinates above). |
wet_cell_count |
int
|
Number of wet cells (centres) used. |
grid_anchor |
str
|
|
lattice_origin |
Optional[Tuple[float, float]]
|
Terrain pixel corner of a terrain-anchored profile. |
lattice_index |
Optional[Tuple[int, int]]
|
Integer |