Data Access¶
rivia exposes HEC-RAS data through four entry points depending on the source:
Data source |
Entry point |
What you get |
|---|---|---|
Text files ( |
|
Python dataclasses, readable and writable |
Plan HDF ( |
|
NumPy arrays, pandas DataFrames |
DSS file ( |
|
|
Geometry HDF ( |
|
NumPy arrays |
When to use which¶
Situation |
Use |
|---|---|
List all plans, units, file paths in the project |
|
Read/edit plan settings (intervals, window, flow file) |
|
Read/edit cross-section geometry, Manning’s n |
|
Read/edit boundary conditions and flow hydrographs |
|
2D mesh results (WSE, velocity) at specific timesteps |
|
Maximum/minimum summary over all timesteps |
|
1D cross-section results from the latest run |
|
1D results spanning multiple restart-file runs |
|
Inline/lateral structure results |
|
Mesh geometry only (no results) |
|
Text Input Files¶
rivia exposes the four HEC-RAS text input files as lazily loaded objects on Project.
model.project → Proj .prj — project index: plan list, unit system, file paths
model.plan → Plan .p** — plan settings: intervals, simulation window, file refs
model.geometry → Geometry .g** — geometry: cross sections, Manning's n, structures
model.flow → SteadyFlow — steady flow: profiles, boundary conditions
→ UnsteadyFlow — unsteady flow: hydrographs, gate openings, initial conditions
model.project — project file¶
proj = model.project # Proj
proj.title # project title string
proj.units # "English" or "SI"
proj.plan_files # list[Path] — all .p** files
proj.geom_files # list[Path] — all .g** files
proj.plans # list of dicts with title, short_id, path per plan
proj.plan_titles # list of plan title strings
proj.plan_short_ids # list of plan short IDs
model.plan — plan file¶
plan = model.plan # Plan for the current plan
plan.title # plan title string
plan.short_id # plan short identifier (e.g. "BC")
plan.is_unsteady # True for unsteady plans
plan.simulation_window # ((start_date, start_time), (end_date, end_time))
plan.computation_interval # e.g. "1MIN"
plan.output_interval # DSS hydrograph output interval
plan.instantaneous_interval # instantaneous output interval
plan.mapping_interval # RASMapper output interval
plan.title = "New Title" # properties are settable
plan.save() # write back to disk
model.geometry — geometry file¶
geom = model.geometry # Geometry
geom.reaches # list[(river, reach)]
geom.get_cross_section("Butte Cr", "Upper", "7") # → CrossSection dataclass
geom.cross_sections("Butte Cr", "Upper") # → list[CrossSection]
# Edit and save
geom.set_mannings("Butte Cr", "Upper", "7", entries)
geom.set_stations("Butte Cr", "Upper", "7", stations, elevations)
geom.save()
# Typed structure access (geometry only, no results)
geom.structures.inlines # StructureIndex[InlineStructure]
geom.structures.bridges # StructureIndex[Bridge]
geom.structures.laterals # StructureIndex[LateralStructure]
model.flow — flow file¶
model.flow returns an UnsteadyFlow.
flow = model.flow # UnsteadyFlow
flow.flow_hydrographs # list[FlowHydrograph]
flow.lateral_inflows # list[LateralInflow]
flow.gate_boundaries # list[GateBoundary]
flow.set_flow_hydrograph(0, vals) # edit by index
flow.set_flow_hydrograph_at("Butte Cr", "Upper", "7", vals) # edit by location
flow.set_lateral_inflow(0, vals) # edit by index
flow.set_lateral_inflow_at("Butte Cr", "Upper", "50", vals) # edit by location
flow.set_all_lateral_inflows([vals0, vals1]) # edit all at once
flow.save()
Plan HDF — model.results¶
model.results returns a lazily opened UnsteadyPlan or SteadyPlan
backed by the current plan’s HDF file (*.px.hdf). It gives access to
both geometry and results.
UnsteadyPlan
├── .flow_areas → FlowAreaResultsCollection
│ └── ["name"] → FlowAreaResults
│ ├── .water_surface h5py.Dataset shape (n_t, n_cells)
│ ├── .face_velocity h5py.Dataset shape (n_t, n_faces)
│ ├── .max_water_surface pd.DataFrame columns [value, time]
│ ├── .max_face_velocity pd.DataFrame columns [value, time]
│ ├── .get_water_surface(timestep=t) np.ndarray shape (n_cells,)
│ ├── .get_water_surface(cell=c) pd.Series WSE over time
│ ├── .get_depth(timestep=t) np.ndarray shape (n_cells,)
│ └── .get_max_depth() pd.DataFrame columns [value, time]
│
├── .storage_areas() → StorageAreaResultsCollection
│ └── ["name"] → StorageAreaResults
│ ├── .wse pd.Series indexed by timestamps
│ └── .max_water_surface pd.DataFrame columns [value, time]
│
├── .structures() → StructureResultsCollection
│ │ full mixed collection → [name | "River Reach RS" | index | (river, reach, rs)]
│ ├── .connections → StructureIndex[SA2DConnectionResults] keyed by connection name
│ ├── .bridges → StructureIndex[BridgeResults] keyed by "River Reach RS"
│ ├── .inlines → StructureIndex[InlineResults] keyed by "River Reach RS"
│ └── .laterals → StructureIndex[LateralResults] keyed by "River Reach RS"
│ each StructureIndex supports [name | index]
│
├── .cross_sections() → CrossSectionResultsCollection (mapping interval, default)
│ └── ["River Reach RS" | index | (river, reach, rs)] → CrossSectionMappingResults
│ ├── .timestamps pd.DatetimeIndex
│ ├── .wse pd.Series
│ ├── .flow pd.Series
│ ├── .flow_lateral pd.Series
│ ├── .velocity_channel / .velocity_total / .flow_cumulative pd.Series
│
├── .cross_sections("output") → CrossSectionResultsCollection (DSS hydrograph interval)
│ └── ["River Reach RS" | index | (river, reach, rs)] → CrossSectionOutputResults
│ ├── .timestamps pd.DatetimeIndex
│ ├── .wse / .flow / .flow_cumulative pd.Series
│
├── .cross_sections("profile") → CrossSectionResultsCollection (DSS Profile / detailed interval)
│ └── ["River Reach RS" | index | (river, reach, rs)] → CrossSectionProfileResults
│ ├── .timestamps pd.DatetimeIndex
│ ├── .wse / .flow pd.Series
│
└── .cross_sections("post_process") → CrossSectionPostProcessResultsCollection (Post Process Profiles)
├── .timestamps pd.DatetimeIndex (length n; maps integer profile columns 0…n-1)
├── .profile_table("Water Surface") pd.DataFrame location × ["max_wse", 0, 1, …]
├── .wse / .flow / .energy_grade (named properties delegating to profile_table)
├── .velocity_channel / .velocity_total / .shear / … (Additional Variables)
└── ["River Reach RS" | index | (river, reach, rs)] → CrossSectionPostProcessResults
├── .timestamps pd.DatetimeIndex
├── .wse / .flow / .energy_grade / .velocity_channel / … pd.Series (Max WS excluded)
2D flow areas¶
hdf = model.results
area = hdf.flow_areas["Perimeter 1"] # FlowAreaResults
# Raw time-series datasets (lazy h5py.Dataset — slice to control memory)
wse_t5 = area.water_surface[5] # WSE at timestep 5, shape (n_cells + n_ghost,)
vel_t5 = area.face_velocity[5] # face-normal velocity at timestep 5
# Derived snapshot (one timestep, all locations)
wse = area.get_water_surface(timestep=5) # np.ndarray (n_cells,)
depth = area.get_depth(timestep=5) # np.ndarray (n_cells,), max(0, WSE - bed)
# Derived time-history (one location, all timesteps)
wse_series = area.get_water_surface(cell=0) # pd.Series indexed by timestamps
# Scalar (one timestep, one location)
wse_val = area.get_water_surface(timestep=5, cell=0) # float
# Velocity (snapshot)
vecs = area.get_cell_velocity(5) # (n_cells, 2) [Vx, Vy]
speed = area.get_cell_velocity(5, component="speed") # (n_cells,)
fvec = area.get_face_velocity(timestep=5, component="vector") # (n_faces, 2)
# Summary results (raw HDF reads)
area.max_water_surface # pd.DataFrame, max WSE per cell
area.max_face_velocity # pd.DataFrame, max velocity per face
area.get_max_depth() # pd.DataFrame, max depth per cell (computed)
Cross-section results (1D)¶
plan.cross_sections(output) is a single method that selects which output
block to read via its output argument:
|
Collection type |
Per-XS data |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Mapping and output blocks¶
hdf = model.results
# mapping block (default)
coll = hdf.cross_sections()
xs = coll[0] # integer index
xs = coll["Butte Cr Upper 7"] # "River Reach RS" joined by spaces
xs = coll[("Butte Cr", "Upper", "7")] # (river, reach, rs) tuple
xs.timestamps # pd.DatetimeIndex — mapping interval
xs.wse # pd.Series indexed by timestamps
xs.flow # pd.Series
xs.flow_lateral # pd.Series
xs.velocity_channel # pd.Series
xs.velocity_total # pd.Series
xs.flow_cumulative # pd.Series
# DSS hydrograph block
coll_out = hdf.cross_sections("output")
xs_out = coll_out["Butte Cr Upper 7"]
xs_out.timestamps # pd.DatetimeIndex — output interval
xs_out.wse # pd.Series
xs_out.flow # pd.Series
xs_out.flow_cumulative # pd.Series
Post Process block (Post Process Profiles)¶
The post_process block exposes all data through both the collection and
per-XS objects. The collection’s profile axis is labeled
["max_wse", 0, 1, …, n-1] where "max_wse" is the Max WS envelope and
integers map into coll.timestamps.
coll = hdf.cross_sections("post_process")
# Timestamps for profiles 0…n-1 (excludes Max WS envelope)
coll.timestamps # pd.DatetimeIndex length n
# Generic engine — returns location × profiles DataFrame
wse = coll.profile_table("Water Surface")
# max_wse 0 1 2
# (Butte Cr, Upper, 7) 102.5 98.1 99.3 100.7
# (Butte Cr, Upper, 6) 101.2 97.0 98.1 99.4
wse["max_wse"] # max-WS longitudinal profile (pd.Series)
wse[0] # WSE along reach at first instantaneous profile
wse.loc[("Butte Cr", "Upper", "7")] # all profiles for one XS (pd.Series)
coll.timestamps[0] # datetime of profile column 0
# Named convenience properties (delegate to profile_table)
coll.wse # == coll.profile_table("Water Surface")
coll.flow # == coll.profile_table("Flow")
coll.energy_grade # == coll.profile_table("Energy Grade")
coll.velocity_channel # == coll.profile_table("Velocity Channel")
coll.shear # == coll.profile_table("Shear")
# … one property per variable; see CrossSectionPostProcessResultsCollection for the full list
# Per-XS access — named properties return pd.Series (Max WS excluded)
xs = coll["Butte Cr Upper 7"]
xs.timestamps # pd.DatetimeIndex
xs.wse # pd.Series indexed by timestamps
xs.flow # pd.Series
xs.energy_grade # pd.Series
xs.velocity_channel # pd.Series
The string key is the river, reach, and RS joined by spaces. The tuple form avoids ambiguity when any component contains spaces. RS values are stored exactly as they appear in the HDF.
Structures¶
structs = hdf.structures() # StructureResultsCollection
# Typed sub-collections — each is a StructureIndex supporting [name | index]
structs.connections # StructureIndex[SA2DConnectionResults]
structs.bridges # StructureIndex[BridgeResults]
structs.inlines # StructureIndex[InlineResults]
structs.laterals # StructureIndex[LateralResults]
# Access via typed sub-collection
inl = structs.inlines["Butte Cr Upper 100"] # by "River Reach RS"
inl = structs.inlines[0] # by integer index
conn = structs.connections["Reservoir Dam"] # SA2DConnection by name
conn = structs.connections[0]
# Access via the full mixed collection (all types together)
# SA2DConnection → by connection name
conn = structs["Reservoir Dam"]
# Bridge / Inline / Lateral → by "River Reach RS", tuple, or index
inl = structs["Butte Cr Upper 100"]
inl = structs[("Butte Cr", "Upper", "100")]
inl = structs[2]
inl.variable_names # list of available output variables
inl.stage_hw # headwater stage, pd.Series indexed by timestamps
inl.stage_tw # tailwater stage, pd.Series
inl.flow_total # total flow, pd.Series
DSS File — model.dss¶
model.dss returns a DssReader that reads directly from the DSS file
written alongside the unsteady simulation (.dss).
Note
Only a limited set of output variables is currently supported (cross-section flow/stage and inline structure flow/stage). More variables will be added in future updates.
The key difference from model.hdf.cross_sections: the DSS file accumulates
across restart runs, so it covers the full simulation period even when the
model was run as a sequence of short durations. The plan HDF is overwritten on
each run and only contains the most recent duration.
Requires the optional pydsstools dependency.
DssReader (all methods accept an optional window=(start, end) keyword arg)
├── .flow(river, reach, rs) → pd.Series XS flow or inline structure total flow
├── .stage(river, reach, rs) → pd.Series stage at a cross section
├── .flow_cum(river, reach, rs) → pd.Series cumulative flow volume
├── .stage_hw(river, reach, rs) → pd.Series inline structure headwater stage
├── .stage_tw(river, reach, rs) → pd.Series inline structure tailwater stage
├── .total_gate_flow(river, reach, rs, gate=N) → pd.Series
└── .weir_flow(river, reach, rs) → pd.Series
dss = model.dss
# Flow at cross-section RS "7" — no window → current plan simulation window
q = dss.flow("Butte Cr", "Upper", "7")
# Stage at an inline structure
hw = dss.stage_hw("Butte Cr", "Upper", "100")
# Restrict to a time window
q = dss.flow("Butte Cr", "Upper", "7",
window=(("01Jan2020", "0000"), ("31Jan2020","2400")))
Geometry HDF — hdf.Geometry¶
hdf.Geometry reads a standalone geometry HDF (*.gx.hdf) for mesh geometry
without simulation results.
Note
UnsteadyPlan and SteadyPlan both inherit from hdf.Geometry, so all
geometry properties described below are equally accessible via model.results
on a plan file — no need to open the geometry HDF separately when you already
have a plan open.
Geometry
├── .flow_areas → FlowAreaCollection
│ └── ["name"] → FlowArea
│ ├── .cell_centers np.ndarray shape (n_cells, 2)
│ ├── .facepoint_coordinates np.ndarray shape (n_fp, 2)
│ ├── .face_cell_indexes np.ndarray shape (n_faces, 2)
│ └── .cell_polygons list[np.ndarray]
│
├── .structures → StructureCollection
│ │ full mixed collection → [name | "River Reach RS" | index | (river, reach, rs)]
│ ├── .connections → StructureIndex[SA2DConnection] keyed by connection name
│ ├── .bridges → StructureIndex[Bridge] keyed by "River Reach RS"
│ ├── .inlines → StructureIndex[InlineStructure] keyed by "River Reach RS"
│ └── .laterals → StructureIndex[LateralStructure] keyed by "River Reach RS"
│
└── .cross_sections → CrossSectionCollection
└── ["River Reach RS" | index | (river, reach, rs)] → CrossSection (geometry only, no time-series)
from rivia.hdf import Geometry
geom = Geometry("model.g01.hdf")
area = geom.flow_areas["Perimeter 1"] # FlowArea (geometry only)
area.cell_centers # np.ndarray shape (n_cells, 2)
area.facepoint_coordinates # np.ndarray shape (n_fp, 2)
area.cell_polygons # list of polygon vertex arrays
# Cross sections support the same three key types as the results collections
xs = geom.cross_sections[0] # integer index
xs = geom.cross_sections["Butte Cr Upper 7"] # "River Reach RS" joined by spaces
xs = geom.cross_sections[("Butte Cr", "Upper", "7")] # (river, reach, rs) tuple
xs.station_elevation # np.ndarray shape (n_pts, 2)