Automating 2D profiles and reference-location results¶
Use a saved collection of named lines to produce repeatable plots and tables across plans. First choose the quantity and extraction method: a spatial profile at one time, a hydrograph at a reference location, and a maximum envelope answer different questions. HDF is the storage format for both mesh arrays and solver-recorded reference output; it does not identify the method by itself.
Choose the result you need¶
| Task | API and inputs | Output and interpretation | Runtime |
|---|---|---|---|
| Sample existing cell results along a straight transect | HdfResultsQuery.query_profile: plan HDF, endpoints, variable, time index |
DataFrame: station, coordinates, value, cell ID, mesh, nearest-cell distance; nearest-cell sampling | Offline Python/HDF |
| Obtain a mapped profile along a curved line | query_polyline_wse_profile, query_polyline_velocity_profile, query_polyline_flow_profile: plan HDF, line, concrete time index, spacing, terrain association |
DataFrame of native renderer samples along station; WSE, velocity or flow plus depth/terrain | Installed HEC-RAS libraries and pythonnet |
| Get per-cell peak WSE from the run | HdfResultsMesh.get_mesh_max_ws: completed plan HDF |
GeoDataFrame from HEC-RAS summary output; each cell's maximum over the computation, with optional time of maximum | Offline Python/HDF |
| Get another stored mesh summary variable | HdfResultsMesh.get_mesh_summary: plan HDF and exact HEC-RAS summary variable name, such as Maximum Face Velocity |
GeoDataFrame of the summary values with cell or face geometry | Offline Python/HDF |
| Read reference-line hydrographs already computed by the solver | HdfResultsXsec.get_ref_lines_timeseries: completed plan HDF |
xarray Dataset of available stored variables with time, line and mesh coordinates | Offline Python/HDF |
| Read reference-point hydrographs | HdfResultsXsec.get_ref_points_timeseries: completed plan HDF |
xarray Dataset of available point variables | Offline Python/HDF |
| Request Q for a named reference or ad hoc profile line | HdfResultsMesh.get_profile_line_flow_timeseries: HDF, name, optional feature file |
DataFrame with flow, time and selection_source; may read a native-associated precomputed hydrograph of unqualified provenance or aggregate face values | Installed HEC-RAS libraries and pythonnet |
| Prepare many reference lines for a future run | GeomReferenceFeatures.generate_reference_lines_from_longitudinal_line, then add_reference_lines: longitudinal line, spacing, length, geometry and flow-area name |
Reviewable line dictionaries, then edited geometry; no computed results until a new run | Python authoring; HEC-RAS for subsequent computation |
HEC distinguishes flow-weighted reference-line stage from mapped WSE, whose value depends on rendering. Reference lines use projected face contributions; reference locations must exist before computation to obtain their recorded output. These are method distinctions, not a blanket accuracy ranking. See HEC reference locations.
flowchart LR
A[Named lines in model coordinates] --> B[Sample existing results]
B --> C[Offline cell sampling]
B --> D[Native Mapper rendering]
A --> E[Write geometry reference lines]
E --> F[Recompute plan]
F --> G[Read recorded reference hydrographs]
Read maximum-result datasets¶
For a per-cell maximum that includes peaks between saved mapping/output timestamps, read the HEC-RAS Summary Output dataset. For maximum water surface elevation, use the convenience method; for another summary variable, use the generic summary reader with its HDF variable name:
from pathlib import Path
from ras_commander import HdfResultsMesh
plan_hdf = Path("model/Project.p01.hdf")
# One row per 2D cell, read from HEC-RAS Summary Output.
max_wse = HdfResultsMesh.get_mesh_max_ws(plan_hdf)
print(max_wse[["mesh_name", "cell_id", "maximum_water_surface"]].head())
# Summary variables can be cell-based or face-based. The exact available
# variables depend on the HEC-RAS version and what the run retained.
max_face_velocity = HdfResultsMesh.get_mesh_summary(
plan_hdf, "Maximum Face Velocity"
)
print(max_face_velocity.head())
These are solver summary datasets, not a reduction of the regular saved
time-series rows. Each native summary maximum is the largest value encountered
across the computation steps, not only the snapshots written at the
mapping/output interval; it can therefore include a peak between those stored
timestamps. By contrast,
HdfResultsMesh.get_mesh_timeseries(..., "Water Surface") returns the values
retained at those output timestamps; calling .max(dim="time") on that array
can miss an intervening peak. A direct comparison looks like this (replace
"2D Flow Area" with the exact mesh name in the HDF):
water_surface_at_outputs = HdfResultsMesh.get_mesh_timeseries(
plan_hdf, mesh_name="2D Flow Area", var="Water Surface", truncate=False
)
max_of_saved_outputs = water_surface_at_outputs.max(dim="time")
max_of_saved_outputs is the maximum of the retained output-interval snapshots;
max_wse is HEC-RAS's summary maximum across the computation. They answer
different questions and may differ. The summary is a spatial envelope:
neighboring cells can reach their maxima at different times, so it is not a
simultaneous water-surface profile.
Do not assume every method named get_mesh_max_* reads a native summary
dataset. In particular, HdfResultsMesh.get_mesh_max_depth() reduces the
retained Depth time series, or derives depth from retained Water Surface
and cell minimum elevation when Depth is absent. Its temporal resolution is
therefore the stored output interval and can miss a peak between saved output
times. Consult the method contract and available HDF datasets when the peak's
time basis matters. HEC-RAS documents mapping-interval output separately from
computation-level output; enabling computation-level output is a separate,
larger diagnostic output option, not required to read the native summary
maximum (HEC-RAS 2D computation settings).
Batch spatial plots without drawing each plot in the GUI¶
The following template reads your line file once and exports one CSV and PNG per line. Set the paths and time index for an existing computed project. It does not run HEC-RAS or claim new execution evidence. A projected line CRS must match the model coordinates; reproject deliberately before querying. The API does not reproject an arbitrary LineString for you. The checks below require a projected line CRS matching the projection read from the model. This template fails fast on an invalid feature; for production batches, log failures per line and review them explicitly rather than silently dropping lines.
from pathlib import Path
import json
import geopandas as gpd
import matplotlib.pyplot as plt
from pyproj import CRS
from ras_commander import HdfBase, HdfPlan, HdfResultsQuery
plan_hdf = Path("model/Project.p01.hdf")
lines = gpd.read_file("model/Features/Profile Lines.shp")
output = Path("working/profile_plots")
output.mkdir(parents=True, exist_ok=True)
time_index = 0 # Choose an available output time, not a computation-step number.
times = HdfPlan.get_plan_timestamps_list(plan_hdf)
if not 0 <= time_index < len(times):
raise ValueError("Choose a time_index from the stored output timestamps.")
if lines.crs is None or not lines.crs.is_projected:
raise ValueError("Use lines with the model's projected CRS.")
model_crs = HdfBase.get_projection(plan_hdf)
if not model_crs or not lines.crs.equals(CRS.from_user_input(model_crs)):
raise ValueError("Verify and reproject the line CRS to match this model.")
for index, (_, row) in enumerate(lines.iterrows()):
if row.geometry is None or row.geometry.geom_type != "LineString":
raise ValueError("Each feature must be a non-empty LineString.")
if row.geometry.is_empty:
raise ValueError("Empty profile line.")
label = str(row.get("Name", index))
profile = HdfResultsQuery.query_polyline_wse_profile(
plan_hdf, row.geometry, time_index=time_index,
sample_spacing=25.0, # Model coordinate units; choose for this study.
# terrain_raster=Path("model/Terrain/Terrain.hdf"),
)
stem = f"profile_{index:04d}" # Stable safe filenames; retain name in metadata.
profile.to_csv(output / f"{stem}.csv", index=False)
metadata = dict(profile.attrs)
metadata.update(line_name=label, plan_hdf=str(plan_hdf),
time_index=time_index, output_time=str(times[time_index]))
(output / f"{stem}.json").write_text(
json.dumps(metadata, indent=2, default=str), encoding="utf-8"
)
fig, ax = plt.subplots()
ax.plot(profile["station"], profile["terrain_elev"], label="Terrain")
ax.plot(profile["station"], profile["wse"], label="Mapped WSE")
ax.set(xlabel="Station (model length units)",
ylabel="Elevation (model vertical units)",
title=f"{label} — {times[time_index]}")
ax.legend()
fig.tight_layout()
fig.savefig(output / f"{stem}.png", dpi=150)
plt.close(fig)
Treat sample_spacing as a requested sampling parameter, not a promise of
uniform returned station increments. The native path imposes minimum-spacing
and sample-count limits, and retained example 416 includes repeated stations and
increments different from the request. Inspect the returned station values;
retain duplicates in the long output until their interpretation is established.
For velocity, use query_polyline_velocity_profile and plot velocity_mag;
retain velocity_x and velocity_y when direction matters. A sampled flow
column from query_polyline_flow_profile is not a whole-line discharge
hydrograph and must not be summed over sample stations as if each were an
independent flux contribution.
These native methods need pythonnet and a compatible installed RasMapperLib
runtime. The argument currently named terrain_raster is passed to the native
terrain loader as a RAS terrain HDF path, not a generic GeoTIFF sampler.
Otherwise the method resolves the geometry's associated terrain. Check that the
association includes the intended terrain modifications. Sampling an original
DEM separately may describe a different ground surface. The API does not expose
a render_mode argument: a native renderer call alone does not establish parity
with every saved GUI display setting. Compare a representative profile against
the intended Mapper view before scaling a batch. Native polyline methods
currently reject models with 2D bridge groups; do not substitute ordinary mesh
profiles for bridge-specific rendering.
Read all recorded reference hydrographs in one call¶
Despite its namespace name, HdfResultsXsec also reads 2D reference locations.
These readers do not need a Mapper installation. Discover actual variables rather
than assuming each HEC-RAS version wrote the same set. This template exports every
available variable for every recorded line; restrict variables to a subset
when the study needs fewer figures.
from pathlib import Path
import matplotlib.pyplot as plt
from ras_commander import HdfResultsXsec
plan_hdf = Path("model/Project.p01.hdf")
output = Path("working/reference_hydrographs")
output.mkdir(parents=True, exist_ok=True)
results = HdfResultsXsec.get_ref_lines_timeseries(plan_hdf)
if not results.data_vars:
raise ValueError("No recorded reference-line time series in this plan HDF.")
print(list(results.data_vars))
variables = list(results.data_vars) # Or select names from this inventory.
for i in range(results.sizes["refln_id"]):
name = str(results["refln_name"].isel(refln_id=i).item())
mesh = str(results["mesh_name"].isel(refln_id=i).item())
for j, variable in enumerate(variables):
values = results[variable].isel(refln_id=i)
stem = f"line_{i:04d}_variable_{j:02d}"
values.to_dataframe(name=variable).to_csv(output / f"{stem}.csv")
fig, ax = plt.subplots()
ax.plot(values["time"].values, values.values)
ax.set(title=f"{mesh}: {name}", xlabel="Stored output time",
ylabel=f"{variable} ({values.attrs.get('units', '')})")
fig.autofmt_xdate()
fig.tight_layout()
fig.savefig(output / f"{stem}.png", dpi=150)
plt.close(fig)
Adding a line after the run cannot create missing solver-recorded results.
Generate proposed transverse lines using
GeomReferenceFeatures.generate_reference_lines_from_longitudinal_line;
review their coordinates and station/orientation metadata, then write them to
a working geometry using add_reference_lines and recompute through RasCmdr.
The default orientation="normal" is perpendicular to the source line.
"velocity" and "depth_velocity" use sampled results from a prior plan;
they are proposals to inspect, not proof of a valid hydraulic section.
See 314 — reference-line generation
and 207 — reference lines and points.
The adjacent add_reference_points writer currently writes IC-point records.
Do not assume recomputing those records produces native reference-point output:
example 314 retained a HEC-RAS 7.0 run with no native Reference Points group.
Inspect the actual result groups with the reference readers; nearest-cell WSE
sampling is a different quantity, not evidence that native point output exists.
Interpret named-line flow provenance¶
get_profile_line_flow_timeseries first tries a precomputed hydrograph that
RasMapperLib associates with the reference-line name when native reference faces
are available. Its ObservedDataLayer.TryReadRefLineFlow route has not been
independently qualified as solver-recorded output; it may refer to associated
observed data. Use HdfResultsXsec.get_ref_lines_timeseries to read the explicit
solver-recorded Reference Lines datasets. When the native-associated hydrograph
lookup is unavailable, the named-line helper reads and aggregates selected
face-flow columns. Check the returned selection_source, direction,
face_count, and attrs["face_ids"] before comparing or labeling a result.
selection_source |
direction="absolute" |
direction="signed" |
|---|---|---|
try_read_ref_line_flow |
Absolute value of the native-associated hydrograph | Native-associated signed hydrograph; provenance unqualified |
reference_line_internal_faces or rasmapper_perimeter_faces |
Sum of absolute selected face flows | Sum of selected native face-normal signed flows |
The native-associated hydrograph lookup uses the line name alone. Duplicate
names across meshes can be ambiguous even when a mesh_name was supplied.
Its returned face_count=0 and empty face_ids describe this lookup path, not
physical absence of intersected faces; an omitted mesh is reported as an empty
string. Preserve these fields without inferring hydraulic geometry from them.
The fallback does not apply a common line-normal sign correction or reproduce
the solver's projected-length weighting. Neither its absolute sum nor its raw
signed sum should automatically be labeled net discharge across the drawn line.
The explicit _legacy method supports offline extraction with its own face
selection/reconstruction path; it is not a silent replacement for the native
method. get_profile_line_peak_flow summarizes the selected extraction path,
so retain that provenance with the peak. See
413 — profile-line flow extraction.
Time, terrain and resolution checks¶
- Computation timestep and saved output interval are different. Index stored output timestamps, and compare timestamps rather than row numbers across plans. Finer plotting spacing cannot recover omitted temporal peaks. Changing output settings requires a new run to produce finer saved output.
- A maximum envelope is not a simultaneous profile. Maxima at neighboring
locations may occur at different times. Native polyline profile methods require
a concrete output index; do not use
time_index="max". For an event-time comparison, select the same physical time in each plan, not merely index 10. - More sample points do not refine the computational mesh. Offline
query_profilerepeats nearest-cell values. Its depth quantity first uses stored cellDepthwhen present, otherwise WSE minus cell-minimum elevation; neither is a local terrain sample at each plotted station.query_transverse_profilereconstructs cell velocities and follows adjacency; it is an approximate exploration tool, not recorded reference output. - Preserve provenance. Keep the plan/HDF identity, HEC-RAS and library versions, coordinates, terrain association, units/datum, selected time, sampling spacing and returned method metadata with exports. CSV alone does not preserve DataFrame attributes.
Adjacent capabilities¶
Use query_polyline_*_timeseries for station-by-time mapped results; their
time_range=(start, stop) follows Python slicing with an excluded stop.
wide=False retains long tables; wide=True pivots the selected variable by
station and time index using the first value for each repeated station/time
pair. Inspect duplicates before pivoting if every returned sample matters.
WSE/velocity difference methods compare two result HDFs; confirm matching
coordinates, geometry assumptions and physical times before interpreting deltas.
Pipe profile/time-series methods are separate native renderers with their own
result requirements, not surface-flow substitutes.
Reference points report local cell-based results rather than a transect flux. Reference areas are a separate HEC-RAS concept for area flow and volume; the current reference helpers documented here provide points and lines, not a complete area authoring/extraction API. Raster-derived volume is also distinct from solver reference-area volume. Consult the HEC reference-location definitions for the applicable cell/face aggregation rules.
Continue with the API reference, HDF extraction, 416 — 2D velocity profiles, and stored maps for raster deliverables.