![]() |
SWMMVis
6.0.0-alpha.4
Qt6/C++ GIS-based graphical user interface for the SWMMVis engine (6.0.0-alpha.4)
|
#include <swmm2dresultslayer.h>
Public Member Functions | |
| EngineMesh2DSource (std::vector< double > vx, std::vector< double > vy, std::vector< double > vz, std::vector< std::array< int, 3 > > tris) | |
| Construct with already-queried mesh geometry. | |
| EngineMesh2DSource (std::vector< double > vx, std::vector< double > vy, std::vector< double > vz, std::vector< std::array< int, 4 > > cells) | |
Mixed-mesh constructor: cells[i] = {v0,v1,v2,v3}, v3 == -1 for a triangle (engine cell order, swmm_2d_cell_get_vertices). The triangle constructor above is this with every v3 = -1. | |
| void | pushDepths (std::vector< float > depths, QDateTime simTime, double elapsedSec) |
| Append one tick's worth of per-triangle depth. | |
| void | pushFlux (std::vector< float > flux, QDateTime simTime, double elapsedSec) |
| Append one tick's worth of per-edge signed normal flux. | |
| void | pushVertexSignedDepths (std::vector< double > depths, QDateTime simTime, double elapsedSec) |
Append one tick's worth of SIGNED per-vertex render depths (swmm_2d_vertex_get_render_depths_bulk — the engine's wet-masked η_v − z_v; datum already subtracted engine-side in double). Mirrors pushFlux — pairs with the tick whose elapsed time matches. Values are stored as floats WITHOUT clamping: negatives carry the sub-cell shoreline intercept. | |
| void | pushRainfall (std::vector< float > rainfall, std::vector< float > rainCum, QDateTime simTime, double elapsedSec) |
Append one tick's per-cell rainfall intensity (m/s) and cumulative rainfall volume (m³) — the live counterparts of the HDF5 Mesh2_face_rainfall / Mesh2_face_rain_cum datasets, served back through readFaceFieldAt under those names so rainfall plots work during a run exactly as they do post-run. Pairs with the tick whose elapsed time matches (same convention as pushFlux). | |
| void | pushHeads (std::vector< float > heads, QDateTime simTime, double elapsedSec) |
Append one tick's per-cell water-surface elevation (m, swmm_2d_get_heads_bulk) — the live counterpart of the HDF5 Mesh2_face_head dataset, served back through readFaceFieldAt under that name so a mid-run export writes the solver's VFR head rather than the depth + mean-bed approximation. Same tick pairing as pushRainfall. | |
| void | setEnvelopes (std::vector< float > maxDepth, std::vector< float > maxVel) |
Replace the cumulative per-cell envelopes (m, m/s) read via swmm_2d_get_stat_max_depths / _velocities — the live counterparts of the HDF5 ENVELOPES datasets, served through readFaceEnvelope. Latest wins: the engine's envelope is monotone, so only the newest payload is kept (nothing per frame). | |
| void | setDryDepth (double m) |
Dry threshold (m) below which a cell's reconstructed velocity is zero — the engine's dry_depth cutoff in computeFaceVelocity. Set from swmm_2d_get_dry_depth at twoDInitialized; default = the layer's. | |
| double | dryDepth () const noexcept |
| void | setHistoryPinned (bool pinned) |
| Hold the frame numbering still: while pinned, pushDepths and friends still append but the history cap is not enforced (no thinning, no historyGeneration bump), so an index handed out before the pin stays valid. Unpinning enforces the cap once. Used by the mid-run export, whose modal dialog and progress loop pump the event queue that delivers the ticks. | |
| bool | historyPinned () const noexcept |
| void | setEdgeGeometry (std::vector< float > length, std::vector< float > nx, std::vector< float > ny) |
Install time-invariant edge geometry queried via swmm_2d_edge_get_geometry_bulk once at twoDInitialized. Sizes are mesh::edgeSlotCount(triangleCount()) each (a stride-3 array from an all-triangle engine is re-packed). Optional — when not called, the source advertises no edge geometry (readEdgeGeometry returns false). | |
| int | vertexCount () const override |
| Geometry counts. Stable for the lifetime of the source. | |
| int | triangleCount () const override |
| int | timeCount () const override |
| Latest known time-step count (grows during live mode). | |
| bool | isLive () const override |
| True while this source is streaming from a running simulation (frames keep arriving). The animation must NOT auto-advance to the newest frame for a live source — the user drives playback via the slider / Play. A completed file source returns false. | |
| void | markFinished () |
| Stop advertising as live once the run has ended. The retained in-memory history stays fully scrubbable; it just is not growing any more, so consumers that wait for new frames (the animation controller's wait-at-end behaviour, follow-live) must stop waiting. Called on the runner's finished signal, including the paths that keep this source instead of swapping in the .h5-backed one. | |
| bool | readDepthAt (int timeIdx, int cell, float &out) override |
One cell's depth at timeIdx. The default copies the whole frame through readDepthsAt; a source holding frames in memory overrides it in O(1) so per-cell time series (comparison plots) do not copy every frame per point. | |
| int | historyGeneration () const override |
| Bumps whenever frames are removed or reordered (a live source thinning its history), invalidating anything cached per frame index. Appends do not bump it. | |
| void | setMaxFrames (int n) |
Cap on retained frames (default 2000). Past the cap the OLDER half of the history is thinned 2:1 (frames keep their sim times, so scrubbing by time is unaffected); historyGeneration bumps. A 1 Hz tick on a 200 k-cell mesh is ~6 MB per frame — unbounded history paged the machine on long runs. n < 8 disables the cap. | |
| int | maxFrames () const noexcept |
| void | setMaxBytes (size_t bytes) |
| Byte budget for the retained history (0 = unlimited), checked alongside the frame cap. The frame cap alone let a 155 k-cell mesh hold ~9 GB (a frame is ~28 B/cell + 4 B/vertex). Past the budget the same 2:1 thinning repeats until the history is at or below 75 % of it, so a big mesh does not re-thin on every tick at the boundary. Preferences → Simulation → "Live 2D history budget". | |
| size_t | maxBytes () const noexcept |
| size_t | historyBytes () const |
| Bytes held by the retained frames' payload vectors. | |
| bool | readMeshGeometry (std::vector< double > &vx, std::vector< double > &vy, std::vector< double > &vz, std::vector< std::array< int, 3 > > &tris) override |
| Fetch mesh geometry as a DISPLAY triangle fan. Resizes outputs. | |
| bool | readCells (std::vector< double > &vx, std::vector< double > &vy, std::vector< double > &vz, std::vector< std::array< int, 4 > > &cells) override |
Fetch mesh geometry as CELLS: cells[i] = {v0,v1,v2,v3} with v3 == -1 for a triangle (cyclic order for a quad), one entry per face, engine order (triangles first, then quads). Resizes outputs. | |
| bool | readDepthsAt (int timeIdx, std::vector< float > &depths) override |
Fetch per-triangle depth at timeIdx. Resizes depths to triangleCount(). | |
| QDateTime | simTimeAt (int timeIdx) const override |
Wall-clock sim time at timeIdx (invalid if out of range or unknown). | |
| bool | readEdgeFluxAt (int timeIdx, std::vector< float > &flux) override |
Fetch per-edge signed normal flux at timeIdx. | |
| bool | readEdgeGeometry (std::vector< float > &length, std::vector< float > &nx, std::vector< float > &ny) override |
| Fetch time-invariant edge geometry (length + outward unit normal). | |
| bool | readVertexDepthsAt (int timeIdx, std::vector< float > &vdepths) override |
Fetch per-vertex SIGNED water depth (m) at timeIdx — the engine's wet-masked, depth-weighted render reconstruction η_v − z_v (Mesh2_node_depth / swmm_2d_vertex_get_render_depths_bulk). NOT clamped: negative values over the dry side of partially wet cells carry the sub-cell shoreline intercept; exactly 0 where no incident cell is wet. Dry-cell bed elevations never contribute (unlike the legacy Mesh2_node_head solver field, which is deliberately NOT consumed here — blending dry-cell beds lifted the rendered surface up adverse slopes/steps). Resized to vertexCount(). | |
| bool | hasFaceField (const char *dataset) const override |
| bool | readFaceFieldAt (const char *dataset, int timeIdx, std::vector< float > &values) override |
Fetch one time slice of a named per-face field (engine SI units). Resized to triangleCount(). Default returns false. | |
| bool | readFaceEnvelope (const char *dataset, std::vector< float > &values) override |
Fetch a time-INVARIANT per-face [nFace] envelope written by the engine's ENVELOPES output group ("Mesh2_face_max_depth", "Mesh2_face_max_velocity"): the whole-run maximum per cell, engine SI units, resized to triangleCount(). | |
Public Member Functions inherited from IMesh2DSource | |
| virtual | ~IMesh2DSource ()=default |
| virtual openswmmvis::io::CoordinateReference | coordinateReference () const |
| How the coordinates from readMeshGeometry relate to the model CRS — see openswmmvis::io::CoordinateReference. | |
| EngineMesh2DSource::EngineMesh2DSource | ( | std::vector< double > | vx, |
| std::vector< double > | vy, | ||
| std::vector< double > | vz, | ||
| std::vector< std::array< int, 3 > > | tris | ||
| ) |
Construct with already-queried mesh geometry.
The runner builds this on the GUI thread immediately after the engine finishes its 2D initialization, querying swmm_2d_vertex_get_xyz_bulk / swmm_2d_triangle_get_vertices against the in-process engine handle. The depth vector starts empty and is appended to with pushDepths().
| EngineMesh2DSource::EngineMesh2DSource | ( | std::vector< double > | vx, |
| std::vector< double > | vy, | ||
| std::vector< double > | vz, | ||
| std::vector< std::array< int, 4 > > | cells | ||
| ) |
Mixed-mesh constructor: cells[i] = {v0,v1,v2,v3}, v3 == -1 for a triangle (engine cell order, swmm_2d_cell_get_vertices). The triangle constructor above is this with every v3 = -1.
|
inlinenoexcept |
|
overridevirtual |
Live fields: rainfall / rain_cum (when any tick carried them), head (when any tick carried heads), and vx / vy — reconstructed on demand from the tick's edge flux and the installed edge geometry with the engine's own RT0 formula (mesh::rt0CellDischarge ÷ depth).
Reimplemented from IMesh2DSource.
| size_t EngineMesh2DSource::historyBytes | ( | ) | const |
Bytes held by the retained frames' payload vectors.
|
inlineoverridevirtual |
Bumps whenever frames are removed or reordered (a live source thinning its history), invalidating anything cached per frame index. Appends do not bump it.
Reimplemented from IMesh2DSource.
|
inlinenoexcept |
|
inlineoverridevirtual |
True while this source is streaming from a running simulation (frames keep arriving). The animation must NOT auto-advance to the newest frame for a live source — the user drives playback via the slider / Play. A completed file source returns false.
Reimplemented from IMesh2DSource.
|
inline |
Stop advertising as live once the run has ended. The retained in-memory history stays fully scrubbable; it just is not growing any more, so consumers that wait for new frames (the animation controller's wait-at-end behaviour, follow-live) must stop waiting. Called on the runner's finished signal, including the paths that keep this source instead of swapping in the .h5-backed one.
|
inlinenoexcept |
|
inlinenoexcept |
| void EngineMesh2DSource::pushDepths | ( | std::vector< float > | depths, |
| QDateTime | simTime, | ||
| double | elapsedSec | ||
| ) |
Append one tick's worth of per-triangle depth.
Pushed from SimulationRunner::twoDDepthsAvailable via queued connection — always on the GUI thread, so no synchronization is needed.
| void EngineMesh2DSource::pushFlux | ( | std::vector< float > | flux, |
| QDateTime | simTime, | ||
| double | elapsedSec | ||
| ) |
Append one tick's worth of per-edge signed normal flux.
Mirrors pushDepths but writes to the flux slot of the most recent tick. Expected size is mesh::edgeSlotCount(triangleCount()) (stride 4, swmm_2d_edge_stride); a stride-3 array from an all-triangle engine is re-packed to the padded layout. If called before pushDepths for the same tick, the runner buffers the flux into the pending slot and pushDepths will pair them. Empty flux vectors are accepted (older engines without swmm_2d_get_edge_flux_bulk skip the push entirely; see SimulationRunner CF.2.4 dlsym gating).
| void EngineMesh2DSource::pushHeads | ( | std::vector< float > | heads, |
| QDateTime | simTime, | ||
| double | elapsedSec | ||
| ) |
Append one tick's per-cell water-surface elevation (m, swmm_2d_get_heads_bulk) — the live counterpart of the HDF5 Mesh2_face_head dataset, served back through readFaceFieldAt under that name so a mid-run export writes the solver's VFR head rather than the depth + mean-bed approximation. Same tick pairing as pushRainfall.
| void EngineMesh2DSource::pushRainfall | ( | std::vector< float > | rainfall, |
| std::vector< float > | rainCum, | ||
| QDateTime | simTime, | ||
| double | elapsedSec | ||
| ) |
Append one tick's per-cell rainfall intensity (m/s) and cumulative rainfall volume (m³) — the live counterparts of the HDF5 Mesh2_face_rainfall / Mesh2_face_rain_cum datasets, served back through readFaceFieldAt under those names so rainfall plots work during a run exactly as they do post-run. Pairs with the tick whose elapsed time matches (same convention as pushFlux).
| void EngineMesh2DSource::pushVertexSignedDepths | ( | std::vector< double > | depths, |
| QDateTime | simTime, | ||
| double | elapsedSec | ||
| ) |
Append one tick's worth of SIGNED per-vertex render depths (swmm_2d_vertex_get_render_depths_bulk — the engine's wet-masked η_v − z_v; datum already subtracted engine-side in double). Mirrors pushFlux — pairs with the tick whose elapsed time matches. Values are stored as floats WITHOUT clamping: negatives carry the sub-cell shoreline intercept.
|
overridevirtual |
Fetch mesh geometry as CELLS: cells[i] = {v0,v1,v2,v3} with v3 == -1 for a triangle (cyclic order for a quad), one entry per face, engine order (triangles first, then quads). Resizes outputs.
Default: an all-triangle source — readMeshGeometry's triangles ARE the cells, so they are wrapped with v3 = -1. Mixed-mesh sources override.
Reimplemented from IMesh2DSource.
|
overridevirtual |
One cell's depth at timeIdx. The default copies the whole frame through readDepthsAt; a source holding frames in memory overrides it in O(1) so per-cell time series (comparison plots) do not copy every frame per point.
Reimplemented from IMesh2DSource.
|
overridevirtual |
Fetch per-triangle depth at timeIdx. Resizes depths to triangleCount().
Implements IMesh2DSource.
|
overridevirtual |
Fetch per-edge signed normal flux at timeIdx.
| flux | Resized to mesh::edgeSlotCount(triangleCount()), indexed mesh::edgeSlot(cell, localEdge) (stride mesh::kEdgeStride; slot 3 of a triangle is padding). Units m² s⁻¹; positive flows outward through the edge's outward normal. |
Reimplemented from IMesh2DSource.
|
overridevirtual |
Fetch time-invariant edge geometry (length + outward unit normal).
| length | Resized to mesh::edgeSlotCount(triangleCount()) (m). |
| nx,ny | Resized likewise (dimensionless); indexed mesh::edgeSlot. |
Reimplemented from IMesh2DSource.
|
overridevirtual |
Fetch a time-INVARIANT per-face [nFace] envelope written by the engine's ENVELOPES output group ("Mesh2_face_max_depth", "Mesh2_face_max_velocity"): the whole-run maximum per cell, engine SI units, resized to triangleCount().
Reimplemented from IMesh2DSource.
|
overridevirtual |
Fetch one time slice of a named per-face field (engine SI units). Resized to triangleCount(). Default returns false.
Reimplemented from IMesh2DSource.
|
overridevirtual |
Fetch mesh geometry as a DISPLAY triangle fan. Resizes outputs.
tris carries one triangle per triangle cell and two per quad (the engine's VFR sub-triangle split, mesh::cellGeom), in cell order — so tris.size() >= triangleCount(). Consumers that need per-cell values must use readCells and map fan triangles back to their cell.
Implements IMesh2DSource.
|
overridevirtual |
Fetch per-vertex SIGNED water depth (m) at timeIdx — the engine's wet-masked, depth-weighted render reconstruction η_v − z_v (Mesh2_node_depth / swmm_2d_vertex_get_render_depths_bulk). NOT clamped: negative values over the dry side of partially wet cells carry the sub-cell shoreline intercept; exactly 0 where no incident cell is wet. Dry-cell bed elevations never contribute (unlike the legacy Mesh2_node_head solver field, which is deliberately NOT consumed here — blending dry-cell beds lifted the rendered surface up adverse slopes/steps). Resized to vertexCount().
Reimplemented from IMesh2DSource.
|
inline |
Dry threshold (m) below which a cell's reconstructed velocity is zero — the engine's dry_depth cutoff in computeFaceVelocity. Set from swmm_2d_get_dry_depth at twoDInitialized; default = the layer's.
| void EngineMesh2DSource::setEdgeGeometry | ( | std::vector< float > | length, |
| std::vector< float > | nx, | ||
| std::vector< float > | ny | ||
| ) |
Install time-invariant edge geometry queried via swmm_2d_edge_get_geometry_bulk once at twoDInitialized. Sizes are mesh::edgeSlotCount(triangleCount()) each (a stride-3 array from an all-triangle engine is re-packed). Optional — when not called, the source advertises no edge geometry (readEdgeGeometry returns false).
| void EngineMesh2DSource::setEnvelopes | ( | std::vector< float > | maxDepth, |
| std::vector< float > | maxVel | ||
| ) |
Replace the cumulative per-cell envelopes (m, m/s) read via swmm_2d_get_stat_max_depths / _velocities — the live counterparts of the HDF5 ENVELOPES datasets, served through readFaceEnvelope. Latest wins: the engine's envelope is monotone, so only the newest payload is kept (nothing per frame).
| void EngineMesh2DSource::setHistoryPinned | ( | bool | pinned | ) |
Hold the frame numbering still: while pinned, pushDepths and friends still append but the history cap is not enforced (no thinning, no historyGeneration bump), so an index handed out before the pin stays valid. Unpinning enforces the cap once. Used by the mid-run export, whose modal dialog and progress loop pump the event queue that delivers the ticks.
|
inline |
Byte budget for the retained history (0 = unlimited), checked alongside the frame cap. The frame cap alone let a 155 k-cell mesh hold ~9 GB (a frame is ~28 B/cell + 4 B/vertex). Past the budget the same 2:1 thinning repeats until the history is at or below 75 % of it, so a big mesh does not re-thin on every tick at the boundary. Preferences → Simulation → "Live 2D history budget".
|
inline |
Cap on retained frames (default 2000). Past the cap the OLDER half of the history is thinned 2:1 (frames keep their sim times, so scrubbing by time is unaffected); historyGeneration bumps. A 1 Hz tick on a 200 k-cell mesh is ~6 MB per frame — unbounded history paged the machine on long runs. n < 8 disables the cap.
|
overridevirtual |
Wall-clock sim time at timeIdx (invalid if out of range or unknown).
Reimplemented from IMesh2DSource.
|
inlineoverridevirtual |
Latest known time-step count (grows during live mode).
Implements IMesh2DSource.
|
inlineoverridevirtual |
Implements IMesh2DSource.
|
inlineoverridevirtual |
Geometry counts. Stable for the lifetime of the source.
triangleCount() is the number of CELLS (engine faces — triangles and, on a mixed mesh, quads; workplans/TRI_QUAD_MESHING_PLAN_2026-09-06.md). Every per-face array (readDepthsAt, readFaceFieldAt) is sized to it; the historical name is kept for its callers.
Implements IMesh2DSource.