SWMMVis  6.0.0-alpha.4
Qt6/C++ GIS-based graphical user interface for the SWMMVis engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
EngineMesh2DSource Class Reference

#include <swmm2dresultslayer.h>

Inheritance diagram for EngineMesh2DSource:
Collaboration diagram for EngineMesh2DSource:

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.
 

Constructor & Destructor Documentation

◆ EngineMesh2DSource() [1/2]

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() [2/2]

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.

Member Function Documentation

◆ dryDepth()

double EngineMesh2DSource::dryDepth ( ) const
inlinenoexcept

◆ hasFaceField()

bool EngineMesh2DSource::hasFaceField ( const char *  dataset) const
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.

Here is the caller graph for this function:

◆ historyBytes()

size_t EngineMesh2DSource::historyBytes ( ) const

Bytes held by the retained frames' payload vectors.

◆ historyGeneration()

int EngineMesh2DSource::historyGeneration ( ) const
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.

◆ historyPinned()

bool EngineMesh2DSource::historyPinned ( ) const
inlinenoexcept

◆ isLive()

bool EngineMesh2DSource::isLive ( ) const
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.

◆ markFinished()

void EngineMesh2DSource::markFinished ( )
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.

◆ maxBytes()

size_t EngineMesh2DSource::maxBytes ( ) const
inlinenoexcept

◆ maxFrames()

int EngineMesh2DSource::maxFrames ( ) const
inlinenoexcept

◆ pushDepths()

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.

◆ pushFlux()

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).

◆ pushHeads()

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.

◆ 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).

◆ pushVertexSignedDepths()

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.

◆ readCells()

bool EngineMesh2DSource::readCells ( std::vector< double > &  vx,
std::vector< double > &  vy,
std::vector< double > &  vz,
std::vector< std::array< int, 4 > > &  cells 
)
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.

◆ readDepthAt()

bool EngineMesh2DSource::readDepthAt ( int  timeIdx,
int  cell,
float &  out 
)
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.

◆ readDepthsAt()

bool EngineMesh2DSource::readDepthsAt ( int  timeIdx,
std::vector< float > &  depths 
)
overridevirtual

Fetch per-triangle depth at timeIdx. Resizes depths to triangleCount().

Implements IMesh2DSource.

◆ readEdgeFluxAt()

bool EngineMesh2DSource::readEdgeFluxAt ( int  timeIdx,
std::vector< float > &  flux 
)
overridevirtual

Fetch per-edge signed normal flux at timeIdx.

Parameters
fluxResized 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.
Returns
true on success. Default implementation returns false so callers can probe whether the source supports flux data without an error.

Reimplemented from IMesh2DSource.

Here is the call graph for this function:

◆ readEdgeGeometry()

bool EngineMesh2DSource::readEdgeGeometry ( std::vector< float > &  length,
std::vector< float > &  nx,
std::vector< float > &  ny 
)
overridevirtual

Fetch time-invariant edge geometry (length + outward unit normal).

Parameters
lengthResized to mesh::edgeSlotCount(triangleCount()) (m).
nx,nyResized likewise (dimensionless); indexed mesh::edgeSlot.
Returns
true on success. Default returns false.

Reimplemented from IMesh2DSource.

◆ readFaceEnvelope()

bool EngineMesh2DSource::readFaceEnvelope ( const char *  dataset,
std::vector< float > &  values 
)
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().

Returns
true on success. Default false — a source that cannot serve the envelope makes the caller scan every frame instead.

Reimplemented from IMesh2DSource.

◆ readFaceFieldAt()

bool EngineMesh2DSource::readFaceFieldAt ( const char *  dataset,
int  timeIdx,
std::vector< float > &  values 
)
overridevirtual

Fetch one time slice of a named per-face field (engine SI units). Resized to triangleCount(). Default returns false.

Reimplemented from IMesh2DSource.

Here is the call graph for this function:

◆ readMeshGeometry()

bool EngineMesh2DSource::readMeshGeometry ( std::vector< double > &  vx,
std::vector< double > &  vy,
std::vector< double > &  vz,
std::vector< std::array< int, 3 > > &  tris 
)
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.

◆ readVertexDepthsAt()

bool EngineMesh2DSource::readVertexDepthsAt ( int  timeIdx,
std::vector< float > &  vdepths 
)
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().

Returns
true on success. Default returns false so callers can probe whether the source carries the signed field (older engines / older HDF5 files) and fall back to the GUI-side wet-only reconstruction.

Reimplemented from IMesh2DSource.

◆ setDryDepth()

void EngineMesh2DSource::setDryDepth ( double  m)
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.

◆ setEdgeGeometry()

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).

◆ setEnvelopes()

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).

◆ setHistoryPinned()

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.

◆ setMaxBytes()

void EngineMesh2DSource::setMaxBytes ( size_t  bytes)
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".

◆ setMaxFrames()

void EngineMesh2DSource::setMaxFrames ( int  n)
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.

◆ simTimeAt()

QDateTime EngineMesh2DSource::simTimeAt ( int  timeIdx) const
overridevirtual

Wall-clock sim time at timeIdx (invalid if out of range or unknown).

Reimplemented from IMesh2DSource.

◆ timeCount()

int EngineMesh2DSource::timeCount ( ) const
inlineoverridevirtual

Latest known time-step count (grows during live mode).

Implements IMesh2DSource.

◆ triangleCount()

int EngineMesh2DSource::triangleCount ( ) const
inlineoverridevirtual

Implements IMesh2DSource.

◆ vertexCount()

int EngineMesh2DSource::vertexCount ( ) const
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.


The documentation for this class was generated from the following files: