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
vertexdepthreconstruct.h
Go to the documentation of this file.
1
34#ifndef VERTEX_DEPTH_RECONSTRUCT_H
35#define VERTEX_DEPTH_RECONSTRUCT_H
36
37#include <algorithm>
38#include <array>
39#include <cmath>
40#include <vector>
41
43{
44
52inline double cellEtaFromMeanDepth(double h, double za, double zb, double zc)
53{
54 double z1 = za, z2 = zb, z3 = zc;
55 if (z1 > z2) std::swap(z1, z2);
56 if (z2 > z3) std::swap(z2, z3);
57 if (z1 > z2) std::swap(z1, z2);
58
59 if (!(h > 0.0)) return z1;
60
61 const double zbar = (z1 + z2 + z3) / 3.0;
62 const double relief = z3 - z1;
63 if (relief < 1.0e-9 || h >= z3 - zbar)
64 return zbar + h; // flat / fully wet
65
66 const double h_at_z2 = (z2 - z1) * (z2 - z1) / (3.0 * relief);
67 if (h <= h_at_z2) // waterline below z2
68 return z1 + std::cbrt(3.0 * h * (z2 - z1) * relief);
69
70 // Waterline between z2 and z3: safeguarded Newton on the bracket.
71 const double denom = 3.0 * relief * (z3 - z2);
72 double lo = z2, hi = z3;
73 double eta = zbar + h;
74 if (eta <= lo || eta >= hi) eta = 0.5 * (lo + hi);
75 for (int it = 0; it < 64; ++it) {
76 const double dz3 = z3 - eta;
77 const double f = (eta - zbar) + dz3 * dz3 * dz3 / denom - h;
78 if (f > 0.0) hi = eta; else lo = eta;
79 const double df = 1.0 - dz3 * dz3 / (relief * (z3 - z2));
80 double next = (df > 1.0e-12) ? eta - f / df : 0.5 * (lo + hi);
81 if (next <= lo || next >= hi) next = 0.5 * (lo + hi);
82 if (std::abs(next - eta) < 1.0e-12 * (1.0 + relief)) return next;
83 eta = next;
84 }
85 return eta;
86}
87
90inline double triMeanDepthFromEta(double eta, double za, double zb, double zc)
91{
92 double z1 = za, z2 = zb, z3 = zc;
93 if (z1 > z2) std::swap(z1, z2);
94 if (z2 > z3) std::swap(z2, z3);
95 if (z1 > z2) std::swap(z1, z2);
96 if (eta <= z1) return 0.0;
97 const double relief = z3 - z1;
98 const double zbar = (z1 + z2 + z3) / 3.0;
99 if (relief < 1.0e-9 || eta >= z3) return eta - zbar;
100 if (eta <= z2) {
101 const double d = eta - z1;
102 return d * d * d / (3.0 * (z2 - z1) * relief);
103 }
104 const double dz3 = z3 - eta;
105 return (eta - zbar) + dz3 * dz3 * dz3 / (3.0 * relief * (z3 - z2));
106}
107
118{
119 std::array<int, 4> v{{-1, -1, -1, -1}};
120 int nSub = 1;
121 std::array<std::array<int, 3>, 2> sub{};
122 std::array<double, 2> area{{0.0, 0.0}};
123
124 int vertexCount() const noexcept { return v[3] >= 0 ? 4 : 3; }
125};
126
133inline double quadEtaFromMeanDepth(const double zs[6], double a1, double a2, double h)
134{
135 const double A = a1 + a2;
136 if (!(A > 0.0)) {
137 // Degenerate quad — fall back to the flat closure over its mean bed.
138 double zm = 0.0;
139 for (int k = 0; k < 6; ++k) zm += zs[k];
140 return zm / 6.0 + ((h > 0.0) ? h : 0.0);
141 }
142 const double zbar1 = (zs[0] + zs[1] + zs[2]) / 3.0;
143 const double zbar2 = (zs[3] + zs[4] + zs[5]) / 3.0;
144 const double zw = (a1 * zbar1 + a2 * zbar2) / A;
145 double zlow = zs[0], ztop = zs[0];
146 for (int k = 1; k < 6; ++k) {
147 if (zs[k] < zlow) zlow = zs[k];
148 if (zs[k] > ztop) ztop = zs[k];
149 }
150 if (!(h > 0.0)) return zlow;
151 const double relief = ztop - zlow;
152 if (relief < 1.0e-9 || h >= ztop - zw) return zw + h; // flat / fully wet
153
154 auto meanDepth = [&](double eta) {
155 return (a1 * triMeanDepthFromEta(eta, zs[0], zs[1], zs[2])
156 + a2 * triMeanDepthFromEta(eta, zs[3], zs[4], zs[5])) / A;
157 };
158 double lo = zlow, hi = ztop;
159 for (int it = 0; it < 64; ++it) {
160 const double mid = 0.5 * (lo + hi);
161 if (meanDepth(mid) < h) lo = mid; else hi = mid;
162 if (hi - lo < 1.0e-12 * (1.0 + relief)) break;
163 }
164 return 0.5 * (lo + hi);
165}
166
184 const std::vector<CellSplit>& cells,
185 const std::vector<float>& cellDepths,
186 const std::vector<float>& cellZc,
187 const std::vector<double>& vz,
188 float dryF,
189 std::vector<float>& vsum,
190 std::vector<float>& wsum,
191 std::vector<float>& outVertexDepth)
192{
193 const int nVert = static_cast<int>(vz.size());
194 vsum.assign(static_cast<size_t>(nVert), 0.0f);
195 wsum.assign(static_cast<size_t>(nVert), 0.0f);
196 const int nCell = std::min<int>(static_cast<int>(cells.size()),
197 static_cast<int>(cellDepths.size()));
198 for (int i = 0; i < nCell; ++i) {
199 const float h = cellDepths[i];
200 // NaN-robust dry skip: `h < dryF` is false for NaN, so a non-finite
201 // depth would NOT be skipped and would poison vsum/wsum at all of the
202 // cell's vertices (→ streaked triangle fans in the Gouraud fill).
203 if (!(h >= dryF)) continue; // only wetted cells contribute
204 const CellSplit& c = cells[i];
205 const int nv = c.vertexCount();
206 bool zOk = true;
207 for (int k = 0; k < nv; ++k) {
208 const int vi = c.v[k];
209 if (vi < 0 || vi >= nVert || !std::isfinite(vz[vi])) { zOk = false; break; }
210 }
211 // Cell free surface via the planar-bed stage–storage inversion when
212 // the vertex elevations are usable; flat closure z_c + h as the
213 // fallback (out-of-range index / nodata z).
214 double eta;
215 if (!zOk) {
216 eta = double(cellZc[i]) + double(h);
217 } else if (nv == 3) {
218 eta = cellEtaFromMeanDepth(double(h), vz[c.v[0]], vz[c.v[1]], vz[c.v[2]]);
219 } else {
220 const double zs[6] = { vz[c.sub[0][0]], vz[c.sub[0][1]], vz[c.sub[0][2]],
221 vz[c.sub[1][0]], vz[c.sub[1][1]], vz[c.sub[1][2]] };
222 eta = quadEtaFromMeanDepth(zs, c.area[0], c.area[1], double(h));
223 }
224 const float w = (nv == 3) ? h : h * 0.75f; // depth weight × 3/nv
225 const float we = w * float(eta); // weighted η contribution
226 if (!std::isfinite(we)) continue; // non-finite z_c must not spread
227 for (int k = 0; k < nv; ++k) {
228 const int vi = c.v[k];
229 if (vi < 0 || vi >= nVert) continue;
230 // Wetted-contact gate (mirror of the engine): this cell's water
231 // votes at corner vi only if its surface reaches the corner.
232 // NaN vz[vi] compares false → skipped, consistent with the
233 // non-finite handling at output.
234 if (!(eta > vz[vi])) continue;
235 vsum[vi] += we;
236 wsum[vi] += w;
237 }
238 }
239 outVertexDepth.assign(static_cast<size_t>(nVert), 0.0f);
240 for (int v = 0; v < nVert; ++v)
241 if (wsum[v] > 0.0f) {
242 const double d = double(vsum[v]) / double(wsum[v]) - vz[v];
243 // Non-finite vertex elevation (e.g. DTM nodata) must yield a dry
244 // vertex, not a NaN that the colour ramp turns into garbage.
245 outVertexDepth[v] = std::isfinite(d) ? float(d) : 0.0f;
246 }
247}
248
252 const std::vector<std::array<int, 3>>& tris,
253 const std::vector<float>& cellDepths,
254 const std::vector<float>& cellZc,
255 const std::vector<double>& vz,
256 float dryF,
257 std::vector<float>& vsum,
258 std::vector<float>& wsum,
259 std::vector<float>& outVertexDepth)
260{
261 std::vector<CellSplit> cells(tris.size());
262 for (size_t i = 0; i < tris.size(); ++i) {
263 cells[i].v = {tris[i][0], tris[i][1], tris[i][2], -1};
264 cells[i].sub[0] = tris[i];
265 }
266 reconstructVertexSignedDepths(cells, cellDepths, cellZc, vz, dryF,
267 vsum, wsum, outVertexDepth);
268}
269
302inline void extrapolateDryCorners(double z0, double z1, double z2,
303 float& sd0, float& sd1, float& sd2)
304{
305 float* const sd[3] = { &sd0, &sd1, &sd2 };
306 const double z[3] = { z0, z1, z2 };
307
308 bool any = false;
309 double maxEta = 0.0;
310 for (int k = 0; k < 3; ++k) {
311 if (!(*sd[k] > 0.0f)) continue;
312 const double e = z[k] + double(*sd[k]);
313 if (!std::isfinite(e)) continue;
314 if (!any || e > maxEta) { maxEta = e; any = true; }
315 }
316 if (!any) return; // fully dry — nothing to extend
317
318 for (int k = 0; k < 3; ++k) {
319 if (*sd[k] > 0.0f) continue; // wet corner keeps its own η
320 if (!std::isfinite(z[k])) continue; // nodata bed stays the sentinel
321 const double d = maxEta - z[k];
322 // ADVERSE SLOPE ONLY. A dry corner standing above the driving head is
323 // the pooling case: the surface runs level into the cell and meets the
324 // rising bed at the intercept, so d <= 0 carries that geometry. A dry
325 // corner BELOW the driving head is the opposite — the bed falls away
326 // from the water, which is where the solver's dryness is meaningful
327 // (the water drained, or never arrived). Extrapolating there would
328 // inject d metres of standing water into a dry cell and spread the
329 // pool downhill one cell in every direction, which is the flood-fill
330 // behaviour this feature explicitly does not do. Leave the sentinel.
331 if (d > 0.0) continue;
332 *sd[k] = float(d);
333 }
334}
335
336} // namespace VertexDepthReconstruct
337
338#endif // VERTEX_DEPTH_RECONSTRUCT_H
size_t i
Definition contourjob.cpp:27
int e
Definition inpmeshreader.cpp:41
int h
Definition mesh2dresultsexport.cpp:387
int w
Definition mesh2dresultsexport.cpp:387
std::vector< mesh::MeshTriangle > cells
Definition mesh2dresultsexport.cpp:102
int k
Definition mesh2dresultsexport.cpp:549
double wsum
Σ weight.
Definition meshattributeassigndialog.cpp:221
int nv
Definition meshenginesync.cpp:32
Definition vertexdepthreconstruct.h:43
double triMeanDepthFromEta(double eta, double za, double zb, double zc)
Definition vertexdepthreconstruct.h:90
void reconstructVertexSignedDepths(const std::vector< CellSplit > &cells, const std::vector< float > &cellDepths, const std::vector< float > &cellZc, const std::vector< double > &vz, float dryF, std::vector< float > &vsum, std::vector< float > &wsum, std::vector< float > &outVertexDepth)
Reconstruct per-vertex SIGNED depths (η_v − z_v) from per-cell mean depths on a mixed triangle/quad m...
Definition vertexdepthreconstruct.h:183
void extrapolateDryCorners(double z0, double z1, double z2, float &sd0, float &sd1, float &sd2)
Replace the NO-DATA sentinel at a partially-wet cell's dry corners with the extrapolated signed depth...
Definition vertexdepthreconstruct.h:302
double cellEtaFromMeanDepth(double h, double za, double zb, double zc)
Definition vertexdepthreconstruct.h:52
double quadEtaFromMeanDepth(const double zs[6], double a1, double a2, double h)
Definition vertexdepthreconstruct.h:133
QVector< int > v
pool vertex indices
Definition pslgminsize.cpp:159
One cell of a mixed triangle/quad mesh, pre-split for the reconstruction (workplans/TRI_QUAD_MESHING_...
Definition vertexdepthreconstruct.h:118
std::array< std::array< int, 3 >, 2 > sub
sub-triangle vertex indices
Definition vertexdepthreconstruct.h:121
std::array< double, 2 > area
planimetric sub-triangle areas (quad)
Definition vertexdepthreconstruct.h:122
int vertexCount() const noexcept
Definition vertexdepthreconstruct.h:124
std::array< int, 4 > v
cell vertices; v[3] = -1 for a triangle
Definition vertexdepthreconstruct.h:119
int nSub
1 (triangle) or 2 (quad)
Definition vertexdepthreconstruct.h:120
double
Definition swmmpollutantpropertyadapter.cpp:50