|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>ANUGA SGS — Merewether Validation Master Report</title> |
|
<style> |
|
:root { |
|
--bg: #0d1117; |
|
--surface: #161b22; |
|
--surface2: #21262d; |
|
--border: #30363d; |
|
--text: #e6edf3; |
|
--muted: #8b949e; |
|
--blue: #58a6ff; |
|
--green: #3fb950; |
|
--orange: #d29922; |
|
--red: #f85149; |
|
--purple: #bc8cff; |
|
--teal: #39d353; |
|
--yellow: #e3b341; |
|
} |
|
* { box-sizing: border-box; margin: 0; padding: 0; } |
|
body { background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-size: 14px; line-height: 1.6; } |
|
a { color: var(--blue); text-decoration: none; } |
|
a:hover { text-decoration: underline; } |
|
|
|
/* Layout */ |
|
.page-header { background: var(--surface); border-bottom: 1px solid var(--border); padding: 28px 40px; } |
|
.page-header h1 { font-size: 26px; font-weight: 700; } |
|
.page-header .subtitle { color: var(--muted); margin-top: 6px; font-size: 15px; } |
|
.page-header .branch-link { margin-top: 10px; font-size: 13px; } |
|
.page-header .branch-link a { background: var(--surface2); border: 1px solid var(--border); border-radius: 6px; padding: 4px 10px; font-family: monospace; } |
|
|
|
nav { background: var(--surface); border-bottom: 1px solid var(--border); padding: 0 40px; display: flex; gap: 0; overflow-x: auto; } |
|
nav a { display: block; padding: 12px 18px; color: var(--muted); font-size: 13px; white-space: nowrap; border-bottom: 2px solid transparent; transition: color 0.15s, border-color 0.15s; } |
|
nav a:hover { color: var(--text); text-decoration: none; } |
|
nav a.active { color: var(--blue); border-color: var(--blue); } |
|
|
|
main { max-width: 1200px; margin: 0 auto; padding: 36px 40px; } |
|
|
|
section { margin-bottom: 52px; } |
|
h2 { font-size: 20px; font-weight: 700; margin-bottom: 16px; padding-bottom: 10px; border-bottom: 1px solid var(--border); } |
|
h3 { font-size: 16px; font-weight: 600; margin: 24px 0 12px; color: var(--blue); } |
|
h4 { font-size: 14px; font-weight: 600; margin: 18px 0 8px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; } |
|
p { margin-bottom: 12px; } |
|
|
|
/* Stat cards */ |
|
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 14px; margin-bottom: 24px; } |
|
.stat-card { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 18px 20px; } |
|
.stat-card .label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); margin-bottom: 6px; } |
|
.stat-card .value { font-size: 26px; font-weight: 700; } |
|
.stat-card .sub { font-size: 12px; color: var(--muted); margin-top: 4px; } |
|
.stat-card.green .value { color: var(--green); } |
|
.stat-card.orange .value { color: var(--orange); } |
|
.stat-card.red .value { color: var(--red); } |
|
.stat-card.blue .value { color: var(--blue); } |
|
.stat-card.purple .value { color: var(--purple); } |
|
|
|
/* Tables */ |
|
.table-wrap { overflow-x: auto; margin: 16px 0; } |
|
table { width: 100%; border-collapse: collapse; font-size: 13px; } |
|
th { background: var(--surface2); color: var(--muted); text-align: left; padding: 9px 14px; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; border-bottom: 1px solid var(--border); white-space: nowrap; } |
|
td { padding: 9px 14px; border-bottom: 1px solid var(--border); vertical-align: middle; } |
|
tr:last-child td { border-bottom: none; } |
|
tr:hover td { background: var(--surface2); } |
|
.pass { color: var(--green); font-weight: 600; } |
|
.fail { color: var(--red); } |
|
.warn { color: var(--orange); } |
|
.mono { font-family: monospace; font-size: 12px; } |
|
.chip { display: inline-block; padding: 2px 8px; border-radius: 12px; font-size: 11px; font-weight: 600; } |
|
.chip.de0 { background: rgba(88,166,255,0.15); color: var(--blue); } |
|
.chip.sg { background: rgba(63,185,80,0.15); color: var(--green); } |
|
.chip.ok { background: rgba(63,185,80,0.15); color: var(--green); } |
|
.chip.bad { background: rgba(248,81,73,0.15); color: var(--red); } |
|
.chip.warn { background: rgba(210,153,34,0.15); color: var(--orange); } |
|
|
|
/* Code block */ |
|
pre { background: var(--surface2); border: 1px solid var(--border); border-radius: 8px; padding: 18px 20px; overflow-x: auto; font-family: 'Cascadia Code', 'Fira Code', monospace; font-size: 12.5px; line-height: 1.5; margin: 12px 0; } |
|
code { font-family: 'Cascadia Code', 'Fira Code', monospace; font-size: 12.5px; background: var(--surface2); padding: 1px 5px; border-radius: 4px; } |
|
|
|
/* Callout boxes */ |
|
.callout { border-left: 4px solid; border-radius: 0 8px 8px 0; padding: 14px 18px; margin: 16px 0; background: var(--surface); } |
|
.callout.fix { border-color: var(--green); } |
|
.callout.warn { border-color: var(--orange); } |
|
.callout.info { border-color: var(--blue); } |
|
.callout strong { display: block; margin-bottom: 4px; } |
|
.callout.fix strong { color: var(--green); } |
|
.callout.warn strong { color: var(--orange); } |
|
.callout.info strong { color: var(--blue); } |
|
|
|
/* Bar chart */ |
|
.bar-chart { display: flex; flex-direction: column; gap: 10px; margin: 16px 0; } |
|
.bar-row { display: grid; grid-template-columns: 130px 1fr 60px; align-items: center; gap: 10px; } |
|
.bar-label { font-size: 12px; color: var(--muted); text-align: right; } |
|
.bar-track { background: var(--surface2); border-radius: 4px; height: 22px; position: relative; overflow: hidden; } |
|
.bar-fill { height: 100%; border-radius: 4px; display: flex; align-items: center; padding-left: 8px; font-size: 11px; font-weight: 600; transition: width 0.4s ease; } |
|
.bar-fill.de0 { background: rgba(88,166,255,0.35); color: var(--blue); } |
|
.bar-fill.sg { background: rgba(63,185,80,0.35); color: var(--green); } |
|
.bar-val { font-size: 12px; color: var(--muted); } |
|
|
|
/* Accuracy grid */ |
|
.accuracy-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; } |
|
@media (max-width: 700px) { .accuracy-grid { grid-template-columns: 1fr; } } |
|
|
|
/* File link list */ |
|
.file-links { display: flex; flex-direction: column; gap: 8px; margin: 12px 0; } |
|
.file-link { display: flex; align-items: center; gap: 10px; background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 12px 16px; } |
|
.file-link .icon { font-size: 18px; } |
|
.file-link .info { flex: 1; } |
|
.file-link .info .name { font-family: monospace; font-size: 13px; color: var(--text); } |
|
.file-link .info .desc { font-size: 12px; color: var(--muted); margin-top: 2px; } |
|
.file-link a { font-size: 12px; white-space: nowrap; } |
|
|
|
/* Timeline */ |
|
.timeline { border-left: 2px solid var(--border); padding-left: 20px; margin: 16px 0; display: flex; flex-direction: column; gap: 18px; } |
|
.timeline-item { position: relative; } |
|
.timeline-item::before { content: ''; position: absolute; left: -26px; top: 5px; width: 10px; height: 10px; border-radius: 50%; background: var(--border); border: 2px solid var(--bg); } |
|
.timeline-item.done::before { background: var(--green); } |
|
.timeline-item.bug::before { background: var(--red); } |
|
.timeline-item .ti-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); } |
|
.timeline-item .ti-title { font-weight: 600; margin: 2px 0; } |
|
.timeline-item .ti-body { font-size: 13px; color: var(--muted); } |
|
|
|
/* Divider */ |
|
hr { border: none; border-top: 1px solid var(--border); margin: 32px 0; } |
|
|
|
/* Footer */ |
|
footer { background: var(--surface); border-top: 1px solid var(--border); padding: 20px 40px; text-align: center; color: var(--muted); font-size: 12px; } |
|
</style> |
|
</head> |
|
<body> |
|
|
|
<div class="page-header"> |
|
<h1>ANUGA Sub-Grid Terrain Sampling — Merewether Validation</h1> |
|
<div class="subtitle">Master report · Bug fix, regression tests, and benchmark results · February 2026</div> |
|
<div class="branch-link"> |
|
Branch: <a href="https://github.com/Hydrata/anuga_core/tree/feature/subgrid-terrain-sampling" target="_blank">Hydrata/anuga_core · feature/subgrid-terrain-sampling</a> |
|
· |
|
Commit: <a href="https://github.com/Hydrata/anuga_core/commit/c376af88" target="_blank"><code>c376af88</code></a> |
|
— Fix SGS phantom mass creation in building cells |
|
</div> |
|
</div> |
|
|
|
<nav> |
|
<a href="#summary" class="active">Summary</a> |
|
<a href="#bugfix">Bug Fix</a> |
|
<a href="#tests">Tests</a> |
|
<a href="#performance">Performance</a> |
|
<a href="#accuracy">Accuracy</a> |
|
<a href="#scenarios">All Scenarios</a> |
|
<a href="#files">Files</a> |
|
</nav> |
|
|
|
<main> |
|
|
|
<!-- ══════════════════════════════════════════════════════════ SUMMARY --> |
|
<section id="summary"> |
|
<h2>Executive Summary</h2> |
|
|
|
<div class="stats-grid"> |
|
<div class="stat-card green"> |
|
<div class="label">Mass conservation</div> |
|
<div class="value"><0.15%</div> |
|
<div class="sub">vs DE0 baseline, all scenarios</div> |
|
</div> |
|
<div class="stat-card orange"> |
|
<div class="label">Speed overhead</div> |
|
<div class="value">+4–5%</div> |
|
<div class="sub">SGS table lookup cost</div> |
|
</div> |
|
<div class="stat-card blue"> |
|
<div class="label">2m accuracy</div> |
|
<div class="value">0.149m</div> |
|
<div class="sub">RMSE, 5/5 pts pass ±0.3m</div> |
|
</div> |
|
<div class="stat-card purple"> |
|
<div class="label">Regression tests</div> |
|
<div class="value">9/9</div> |
|
<div class="sub">building-cell phantom fix</div> |
|
</div> |
|
<div class="stat-card green"> |
|
<div class="label">Building cells fixed</div> |
|
<div class="value">333</div> |
|
<div class="sub">z_min > bed_centroid in 2m mesh</div> |
|
</div> |
|
<div class="stat-card red"> |
|
<div class="label">Before fix (max vol)</div> |
|
<div class="value">165k m³</div> |
|
<div class="sub">vs correct 9.7k m³</div> |
|
</div> |
|
</div> |
|
|
|
<p>The SGS (Sub-Grid terrain Sampling) feature stores a per-cell volume–stage lookup table |
|
<em>V(η)</em> built from a fine DEM (≤2m pixel), allowing the shallow-water solver to account for |
|
sub-mesh topography without refining the computational mesh. |
|
This enables accurate simulation of urban flooding around buildings on coarser meshes (4m+) that |
|
would otherwise be too coarse to resolve individual structures.</p> |
|
|
|
<p>During testing on the Merewether benchmark (Brisbane, Australia), a <strong>phantom mass creation |
|
bug</strong> was discovered and fixed. After the fix, SGS produces results that are essentially identical |
|
to the standard DE0 solver at 2m resolution while adding only 4–5% wall-clock overhead.</p> |
|
|
|
<div class="callout info"> |
|
<strong>What is Merewether?</strong> |
|
The Merewether benchmark is a field-scale flash flood event used to validate ANUGA. |
|
Five ARR (Australian Rainfall and Runoff) observation points provide field-measured peak stage. |
|
Pass criterion: simulated peak stage within ±0.3 m of observed. EPSG:32756 (zone 56S). |
|
</div> |
|
</section> |
|
|
|
<!-- ══════════════════════════════════════════════════════════ BUG FIX --> |
|
<section id="bugfix"> |
|
<h2>Bug Fix — Phantom Mass Creation in Building Cells</h2> |
|
|
|
<h3>Root Cause</h3> |
|
<p>When mesh elevation smoothing (<code>alpha</code> close to 1) is applied near buildings, some |
|
triangular cells end up with <code>z_min > bed_centroid</code>. |
|
In the Merewether 2m mesh, 333 such <em>building cells</em> exist (buildings ~38 m elevation, |
|
smoothed mesh bed ~18 m).</p> |
|
|
|
<p>Two failure modes occurred in these cells:</p> |
|
|
|
<div class="callout warn"> |
|
<strong>Failure 1 — Phantom depth enforced by <code>_openmp_protect</code></strong> |
|
The dry-cell guard sets <code>stage = z_min</code> (the DEM floor, ~38 m) instead of |
|
<code>bed_centroid</code> (~18 m). This makes a nominally dry cell appear wet with a |
|
flat-bed depth of <code>z_min − bed_centroid ≈ 20 m</code>, creating phantom mass that grows |
|
without bound. |
|
</div> |
|
|
|
<div class="callout warn"> |
|
<strong>Failure 2 — Explosive stage jump on water entry</strong> |
|
<code>sg_stage_from_volume(tiny_V)</code> returns <code>~z_min ≈ 38 m</code> even for a tiny |
|
volume, because all DEM data inside the triangle lies above the table floor. Any real water |
|
entering the cell triggers an enormous phantom stage jump. |
|
</div> |
|
|
|
<p>Combined effect: within seconds of the simulation starting, volume exploded to <strong>165,000 m³</strong> |
|
(vs the correct ~9,700 m³) with velocities exceeding 20 m/s.</p> |
|
|
|
<h3>Fix Applied in <code>set_subgrid_dem()</code></h3> |
|
|
|
<div class="callout fix"> |
|
<strong>Fix 1 — Replace building-cell SGS tables with flat-bed tables</strong> |
|
For every cell where <code>z_min > bed_centroid</code>, the SGS lookup table is replaced with a |
|
2-breakpoint flat-bed table anchored at <code>bed_centroid</code>. |
|
The cell then behaves identically to the standard DE0 solver. |
|
<code>z_min</code> is reset to <code>bed_centroid</code>, eliminating the phantom depth. |
|
</div> |
|
|
|
<div class="callout fix"> |
|
<strong>Fix 2 — Correct phantom stage at initialisation</strong> |
|
For any cell where <code>stage > z_min</code> and <code>stage ≤ bed_centroid</code> |
|
(sub-grid-wet but flat-bed-dry), reset <code>stage = z_min</code> so that <code>V_old = 0</code> |
|
before the first timestep. |
|
</div> |
|
|
|
<pre><code># Fix 1: replace SGS tables for "building cells" with flat-bed tables |
|
_bed_cv = self.quantities['elevation'].centroid_values |
|
_z_min = cell_table.z_min |
|
_FLATBED_RANGE = 60.0 # metres above bed to cover realistic flood depths |
|
|
|
_problem_cells = _z_min > _bed_cv |
|
if _problem_cells.any(): |
|
for _k in _np_sgs.where(_problem_cells)[0]: |
|
_bed_k = float(_bed_cv[_k]) |
|
_area_k = float(cell_table.cell_areas[_k]) |
|
cell_table.eta_breaks[_k, 0] = _bed_k |
|
cell_table.eta_breaks[_k, 1] = _bed_k + _FLATBED_RANGE |
|
cell_table.vol_cumul[_k, 0] = 0.0 |
|
cell_table.vol_cumul[_k, 1] = _FLATBED_RANGE * _area_k |
|
cell_table.wet_area[_k, 0] = _area_k |
|
cell_table.wet_area[_k, 1] = _area_k |
|
cell_table.n_breaks[_k] = 2 |
|
cell_table.z_min[_problem_cells] = _bed_cv[_problem_cells] |
|
|
|
# Fix 2: correct phantom stage for sub-grid-wet / flat-bed-dry cells |
|
_stage_cv = self.quantities['stage'].centroid_values |
|
_phantom = (_stage_cv > cell_table.z_min) & (_stage_cv <= _bed_cv) |
|
_stage_cv[_phantom] = cell_table.z_min[_phantom]</code></pre> |
|
|
|
<h3>Timeline</h3> |
|
<div class="timeline"> |
|
<div class="timeline-item done"> |
|
<div class="ti-label">Fix committed</div> |
|
<div class="ti-title"><code>inlet.py</code> — <code>get_depths()</code> clamped to ≥ 0</div> |
|
<div class="ti-body">Prevents assertion error when SGS sets <code>stage = z_min < bed_centroid</code> for dry cells in inlet operator.</div> |
|
</div> |
|
<div class="timeline-item bug"> |
|
<div class="ti-label">Bug discovered (Feb 2026)</div> |
|
<div class="ti-title">Building cell phantom mass: 165k m³ vs correct 9.7k m³</div> |
|
<div class="ti-body">Identified 333 building cells with z_min (38m) > bed_centroid (18m) in Merewether 2m mesh with alpha=0.99 smoothing.</div> |
|
</div> |
|
<div class="timeline-item done"> |
|
<div class="ti-label">Fix 1 + Fix 2 applied</div> |
|
<div class="ti-title"><code>shallow_water_domain.py</code> — flat-bed table replacement + stage initialisation fix</div> |
|
<div class="ti-body">After fix: vol = 9,767.8 m³ (−0.008% vs DE0). All 5 ARR points pass at ±0.3m.</div> |
|
</div> |
|
<div class="timeline-item done"> |
|
<div class="ti-label">Regression tests written</div> |
|
<div class="ti-title"><code>anuga/subgrid/tests/test_building_cell_fix.py</code> — 9 tests, all pass</div> |
|
<div class="ti-body">Verifies both fixes without needing a real DEM file. Uses CellVolumeTable directly.</div> |
|
</div> |
|
<div class="timeline-item done"> |
|
<div class="ti-label">Committed & pushed</div> |
|
<div class="ti-title">Branch <code>feature/subgrid-terrain-sampling</code> — commit <a href="https://github.com/Hydrata/anuga_core/commit/c376af88">c376af88</a></div> |
|
<div class="ti-body">3 files changed, 383 insertions, 2 deletions.</div> |
|
</div> |
|
</div> |
|
</section> |
|
|
|
<!-- ══════════════════════════════════════════════════════════ TESTS --> |
|
<section id="tests"> |
|
<h2>Regression Tests</h2> |
|
|
|
<p>File: <a href="https://github.com/Hydrata/anuga_core/blob/feature/subgrid-terrain-sampling/anuga/subgrid/tests/test_building_cell_fix.py" target="_blank"> |
|
<code>anuga/subgrid/tests/test_building_cell_fix.py</code></a> |
|
</p> |
|
|
|
<div class="table-wrap"> |
|
<table> |
|
<thead><tr> |
|
<th>Test</th> |
|
<th>Fix</th> |
|
<th>What it verifies</th> |
|
<th>Result</th> |
|
</tr></thead> |
|
<tbody> |
|
<tr><td class="mono">test_fix1_z_min_corrected</td><td>1</td><td>After fix, z_min for building cells equals bed_centroid</td><td><span class="pass">PASS</span></td></tr> |
|
<tr><td class="mono">test_fix1_no_phantom_volume_at_dry_state</td><td>1</td><td>V(z_min) = 0 after fix (no phantom water)</td><td><span class="pass">PASS</span></td></tr> |
|
<tr><td class="mono">test_fix1_flat_bed_behaviour_after_replacement</td><td>1</td><td>V(η) = area·(η − bed) for all depths after table replacement</td><td><span class="pass">PASS</span></td></tr> |
|
<tr><td class="mono">test_fix1_normal_cells_unaffected</td><td>1</td><td>Normal cells (z_min ≤ bed) are not modified</td><td><span class="pass">PASS</span></td></tr> |
|
<tr><td class="mono">test_fix1_n_breaks_set_to_2</td><td>1</td><td>Building cell has exactly 2 breakpoints after fix</td><td><span class="pass">PASS</span></td></tr> |
|
<tr><td class="mono">test_fix2_phantom_stage_corrected</td><td>2</td><td>stage reset to z_min when stage > z_min and stage ≤ bed</td><td><span class="pass">PASS</span></td></tr> |
|
<tr><td class="mono">test_fix2_already_dry_not_touched</td><td>2</td><td>Correctly dry cells (stage = z_min) are not modified</td><td><span class="pass">PASS</span></td></tr> |
|
<tr><td class="mono">test_combined_merewether_scenario</td><td>1+2</td><td>z_min=38m → bed=18m: zero phantom depth after fix</td><td><span class="pass">PASS</span></td></tr> |
|
<tr><td class="mono">test_multiple_building_cells</td><td>1+2</td><td>333 building cells all get fixed, 200 normal cells unchanged</td><td><span class="pass">PASS</span></td></tr> |
|
</tbody> |
|
</table> |
|
</div> |
|
|
|
<pre><code>cd /opt/anuga_core/sandpit |
|
OMP_NUM_THREADS=1 ~/anuga_venv/bin/pytest -rs anuga/subgrid/tests/test_building_cell_fix.py -v |
|
|
|
# Result: |
|
# ========== 9 passed in 0.34s ==========</code></pre> |
|
|
|
<p>The full subgrid test suite also passes: <strong>42/42 tests</strong> in <code>anuga/subgrid/tests/</code>.</p> |
|
</section> |
|
|
|
<!-- ══════════════════════════════════════════════════════════ PERFORMANCE --> |
|
<section id="performance"> |
|
<h2>Performance Results</h2> |
|
|
|
<h3>Wall-clock time comparison</h3> |
|
<p>All runs on the same workstation (24-core, 62 GB RAM). Duration = 1000 s sim time. CFL = 0.9.</p> |
|
|
|
<div class="bar-chart"> |
|
<div class="bar-row"> |
|
<div class="bar-label">de0_elev_2m</div> |
|
<div class="bar-track"> |
|
<div class="bar-fill de0" style="width:95%">DE0 · 2m · 276s</div> |
|
</div> |
|
<div class="bar-val">276s</div> |
|
</div> |
|
<div class="bar-row"> |
|
<div class="bar-label">sg_elev_2m</div> |
|
<div class="bar-track"> |
|
<div class="bar-fill sg" style="width:100%">DE0_SG · 2m · 291s</div> |
|
</div> |
|
<div class="bar-val">291s</div> |
|
</div> |
|
<div class="bar-row"> |
|
<div class="bar-label">de0_elev_4m</div> |
|
<div class="bar-track"> |
|
<div class="bar-fill de0" style="width:10.6%">31s</div> |
|
</div> |
|
<div class="bar-val">31s</div> |
|
</div> |
|
<div class="bar-row"> |
|
<div class="bar-label">sg_elev_4m</div> |
|
<div class="bar-track"> |
|
<div class="bar-fill sg" style="width:11%">32s</div> |
|
</div> |
|
<div class="bar-val">32s</div> |
|
</div> |
|
<div class="bar-row"> |
|
<div class="bar-label">de0_mann_4m</div> |
|
<div class="bar-track"> |
|
<div class="bar-fill de0" style="width:10.4%">30s</div> |
|
</div> |
|
<div class="bar-val">30s</div> |
|
</div> |
|
<div class="bar-row"> |
|
<div class="bar-label">sg_mann_4m</div> |
|
<div class="bar-track"> |
|
<div class="bar-fill sg" style="width:10.8%">31s</div> |
|
</div> |
|
<div class="bar-val">31s</div> |
|
</div> |
|
</div> |
|
|
|
<div class="callout warn"> |
|
<strong>Note — earlier "2.5× speedup at 4m" was a measurement artifact</strong> |
|
An early sg_mann_4m run showed 12.2s vs DE0's 30.2s. Re-running sg_mann_4m under identical |
|
conditions gives 31.4s — consistent with all other 4m runs (~30–32s). The 12.2s result was |
|
likely caused by filesystem cache or CPU governor state. SGS adds overhead, it does not speed up |
|
computations. |
|
</div> |
|
|
|
<h3>Summary table</h3> |
|
<div class="table-wrap"> |
|
<table> |
|
<thead><tr> |
|
<th>Scenario</th> |
|
<th>Algorithm</th> |
|
<th>Res</th> |
|
<th>Triangles</th> |
|
<th>Wall (s)</th> |
|
<th>vs DE0</th> |
|
<th>Final vol (m³)</th> |
|
<th>Mass error</th> |
|
</tr></thead> |
|
<tbody> |
|
<tr> |
|
<td>de0_elev_2m</td> |
|
<td><span class="chip de0">DE0</span></td> |
|
<td>2m</td> |
|
<td class="mono">100,530</td> |
|
<td>276</td> |
|
<td>baseline</td> |
|
<td>9,768.6</td> |
|
<td>baseline</td> |
|
</tr> |
|
<tr> |
|
<td>sg_elev_2m</td> |
|
<td><span class="chip sg">DE0_SG</span></td> |
|
<td>2m</td> |
|
<td class="mono">100,530</td> |
|
<td>291</td> |
|
<td class="warn">+5%</td> |
|
<td>9,767.8</td> |
|
<td class="pass">−0.008%</td> |
|
</tr> |
|
<tr> |
|
<td>de0_elev_4m</td> |
|
<td><span class="chip de0">DE0</span></td> |
|
<td>4m</td> |
|
<td class="mono">25,236</td> |
|
<td>31</td> |
|
<td>baseline</td> |
|
<td>10,369.0</td> |
|
<td>baseline</td> |
|
</tr> |
|
<tr> |
|
<td>sg_elev_4m</td> |
|
<td><span class="chip sg">DE0_SG</span></td> |
|
<td>4m</td> |
|
<td class="mono">25,236</td> |
|
<td>32</td> |
|
<td class="warn">+3%</td> |
|
<td>10,368.8</td> |
|
<td class="pass">−0.002%</td> |
|
</tr> |
|
<tr> |
|
<td>de0_mann_4m</td> |
|
<td><span class="chip de0">DE0</span></td> |
|
<td>4m</td> |
|
<td class="mono">25,236</td> |
|
<td>30</td> |
|
<td>baseline</td> |
|
<td>12,005.4</td> |
|
<td>baseline</td> |
|
</tr> |
|
<tr> |
|
<td>sg_mann_4m</td> |
|
<td><span class="chip sg">DE0_SG</span></td> |
|
<td>4m</td> |
|
<td class="mono">25,236</td> |
|
<td>31</td> |
|
<td class="warn">+4%</td> |
|
<td>11,987.6</td> |
|
<td class="pass">−0.148%</td> |
|
</tr> |
|
</tbody> |
|
</table> |
|
</div> |
|
|
|
<p>The 4m mesh uses ~4× fewer triangles than the 2m mesh, giving an 8–9× wall-clock speedup. |
|
This is a <em>mesh resolution</em> benefit, not an SGS benefit. |
|
SGS itself adds 3–5% overhead due to sub-grid table lookup at each solver call.</p> |
|
</section> |
|
|
|
<!-- ══════════════════════════════════════════════════════════ ACCURACY --> |
|
<section id="accuracy"> |
|
<h2>Accuracy — Peak Stage at ARR Observation Points</h2> |
|
|
|
<p>Tolerance: ±0.3 m. Field measurements from the Merewether flash flood event (Brisbane, Australia).</p> |
|
|
|
<div class="accuracy-grid"> |
|
<div> |
|
<h3>2m Elevated buildings (primary comparison)</h3> |
|
<div class="callout fix"> |
|
<strong>RMSE: DE0=0.148m, SGS=0.149m — effectively identical</strong> |
|
Both pass all 5 observation points. SGS introduces no accuracy degradation at 2m resolution. |
|
</div> |
|
<div class="table-wrap"> |
|
<table> |
|
<thead><tr><th>Point</th><th>Field (m)</th><th>DE0</th><th>SGS</th></tr></thead> |
|
<tbody> |
|
<tr><td>0</td><td>20.00</td><td>20.23 <span class="pass">✓</span></td><td>20.23 <span class="pass">✓</span></td></tr> |
|
<tr><td>1</td><td>18.40</td><td>18.50 <span class="pass">✓</span></td><td>18.50 <span class="pass">✓</span></td></tr> |
|
<tr><td>2</td><td>23.50</td><td>23.63 <span class="pass">✓</span></td><td>23.62 <span class="pass">✓</span></td></tr> |
|
<tr><td>3</td><td>23.10</td><td>23.11 <span class="pass">✓</span></td><td>23.11 <span class="pass">✓</span></td></tr> |
|
<tr><td>4</td><td>23.00</td><td>22.82 <span class="pass">✓</span></td><td>22.82 <span class="pass">✓</span></td></tr> |
|
<tr><td colspan="2"><strong>RMSE / Pass</strong></td><td><strong>0.148m / 5/5</strong></td><td><strong>0.149m / 5/5</strong></td></tr> |
|
</tbody> |
|
</table> |
|
</div> |
|
</div> |
|
|
|
<div> |
|
<h3>4m Elevated buildings (valid comparison)</h3> |
|
<div class="callout warn"> |
|
<strong>RMSE: ~13.4–13.6m — both fail at 4m resolution</strong> |
|
The 4m mesh is too coarse to resolve individual building geometry. SGS is marginally better |
|
(Δ RMSE = −0.22m) but both fail 4/5 points by >8m. |
|
</div> |
|
<div class="table-wrap"> |
|
<table> |
|
<thead><tr><th>Point</th><th>Field (m)</th><th>DE0</th><th>SGS</th></tr></thead> |
|
<tbody> |
|
<tr><td>0</td><td>20.00</td><td>45.10 <span class="fail">✗</span></td><td>44.63 <span class="fail">✗</span></td></tr> |
|
<tr><td>1</td><td>18.40</td><td>28.90 <span class="fail">✗</span></td><td>28.90 <span class="fail">✗</span></td></tr> |
|
<tr><td>2</td><td>23.50</td><td>23.65 <span class="pass">✓</span></td><td>23.64 <span class="pass">✓</span></td></tr> |
|
<tr><td>3</td><td>23.10</td><td>33.65 <span class="fail">✗</span></td><td>33.54 <span class="fail">✗</span></td></tr> |
|
<tr><td>4</td><td>23.00</td><td>31.80 <span class="fail">✗</span></td><td>31.60 <span class="fail">✗</span></td></tr> |
|
<tr><td colspan="2"><strong>RMSE / Pass</strong></td><td><strong>13.632m / 1/5</strong></td><td><strong>13.417m / 1/5</strong></td></tr> |
|
</tbody> |
|
</table> |
|
</div> |
|
</div> |
|
|
|
<div> |
|
<h3>2m Manning buildings (incomplete — blowup)</h3> |
|
<div class="callout warn"> |
|
<strong>Both blow up at t=660s — not a meaningful comparison</strong> |
|
Manning n=10 at 2m resolution causes numerical instability (physics/mesh issue, not an SGS bug). |
|
sg_mann_2m SWW is truncated before blowup (5/5 pass), while de0_mann_2m SWW includes the |
|
blowup timesteps (0/5 pass) — the difference reflects output timing, not solver quality. |
|
</div> |
|
<div class="table-wrap"> |
|
<table> |
|
<thead><tr><th>Point</th><th>Field (m)</th><th>DE0 (blowup)</th><th>SGS (partial)</th></tr></thead> |
|
<tbody> |
|
<tr><td>0</td><td>20.00</td><td>29.25 <span class="fail">✗</span></td><td>20.27 <span class="pass">✓</span></td></tr> |
|
<tr><td>1</td><td>18.40</td><td>27.89 <span class="fail">✗</span></td><td>18.51 <span class="pass">✓</span></td></tr> |
|
<tr><td>2</td><td>23.50</td><td>27.08 <span class="fail">✗</span></td><td>23.62 <span class="pass">✓</span></td></tr> |
|
<tr><td>3</td><td>23.10</td><td>26.49 <span class="fail">✗</span></td><td>23.10 <span class="pass">✓</span></td></tr> |
|
<tr><td>4</td><td>23.00</td><td>27.84 <span class="fail">✗</span></td><td>22.83 <span class="pass">✓</span></td></tr> |
|
<tr><td colspan="2"><strong>RMSE / Pass</strong></td><td><strong>6.684m / 0/5</strong></td><td><strong>0.163m* / 5/5</strong></td></tr> |
|
</tbody> |
|
</table> |
|
</div> |
|
<p style="font-size:12px;color:var(--muted)">* SGS RMSE measured before blowup — not directly comparable to DE0.</p> |
|
</div> |
|
|
|
<div> |
|
<h3>4m Manning buildings (valid comparison)</h3> |
|
<div class="callout warn"> |
|
<strong>RMSE: ~13.4–13.7m — both fail at 4m resolution</strong> |
|
Same root cause as 4m elevated: mesh too coarse for buildings. SGS marginally better (Δ RMSE = −0.24m). |
|
</div> |
|
<div class="table-wrap"> |
|
<table> |
|
<thead><tr><th>Point</th><th>Field (m)</th><th>DE0</th><th>SGS</th></tr></thead> |
|
<tbody> |
|
<tr><td>0</td><td>20.00</td><td>45.13 <span class="fail">✗</span></td><td>44.63 <span class="fail">✗</span></td></tr> |
|
<tr><td>1</td><td>18.40</td><td>28.94 <span class="fail">✗</span></td><td>28.93 <span class="fail">✗</span></td></tr> |
|
<tr><td>2</td><td>23.50</td><td>23.65 <span class="pass">✓</span></td><td>23.64 <span class="pass">✓</span></td></tr> |
|
<tr><td>3</td><td>23.10</td><td>33.64 <span class="fail">✗</span></td><td>33.54 <span class="fail">✗</span></td></tr> |
|
<tr><td>4</td><td>23.00</td><td>31.90 <span class="fail">✗</span></td><td>31.60 <span class="fail">✗</span></td></tr> |
|
<tr><td colspan="2"><strong>RMSE / Pass</strong></td><td><strong>13.659m / 1/5</strong></td><td><strong>13.422m / 1/5</strong></td></tr> |
|
</tbody> |
|
</table> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<h3>SGS accuracy delta (vs DE0 baseline)</h3> |
|
<div class="table-wrap"> |
|
<table> |
|
<thead><tr> |
|
<th>Scenario</th> |
|
<th>Buildings</th> |
|
<th>Res</th> |
|
<th>DE0 RMSE</th> |
|
<th>SGS RMSE</th> |
|
<th>ΔRMSE</th> |
|
<th>DE0 MaxErr</th> |
|
<th>SGS MaxErr</th> |
|
<th>Valid?</th> |
|
</tr></thead> |
|
<tbody> |
|
<tr> |
|
<td>Elevated</td><td>Reflective</td><td>2m</td> |
|
<td>0.148m</td><td>0.149m</td> |
|
<td class="warn">+0.001m</td> |
|
<td>0.227m</td><td>0.227m</td> |
|
<td><span class="chip ok">Valid</span></td> |
|
</tr> |
|
<tr> |
|
<td>Elevated</td><td>Reflective</td><td>4m</td> |
|
<td>13.632m</td><td>13.417m</td> |
|
<td class="pass">−0.215m</td> |
|
<td>25.103m</td><td>24.632m</td> |
|
<td><span class="chip ok">Valid</span></td> |
|
</tr> |
|
<tr> |
|
<td>Manning</td><td>n=10</td><td>2m</td> |
|
<td>6.684m</td><td>0.163m</td> |
|
<td class="pass">−6.521m</td> |
|
<td>9.494m</td><td>0.275m</td> |
|
<td><span class="chip warn">⚠ Blowup</span></td> |
|
</tr> |
|
<tr> |
|
<td>Manning</td><td>n=10</td><td>4m</td> |
|
<td>13.659m</td><td>13.422m</td> |
|
<td class="pass">−0.237m</td> |
|
<td>25.128m</td><td>24.632m</td> |
|
<td><span class="chip ok">Valid</span></td> |
|
</tr> |
|
</tbody> |
|
</table> |
|
</div> |
|
</section> |
|
|
|
<!-- ══════════════════════════════════════════════════════════ ALL SCENARIOS --> |
|
<section id="scenarios"> |
|
<h2>All Scenarios — Complete Data</h2> |
|
|
|
<div class="table-wrap"> |
|
<table> |
|
<thead><tr> |
|
<th>Scenario</th> |
|
<th>Algorithm</th> |
|
<th>Buildings</th> |
|
<th>Res</th> |
|
<th>Triangles</th> |
|
<th>Wall (s)</th> |
|
<th>Vol (m³)</th> |
|
<th>Max depth</th> |
|
<th>Max speed</th> |
|
<th>Pass/5</th> |
|
<th>RMSE</th> |
|
<th>MaxErr</th> |
|
<th>Stable</th> |
|
</tr></thead> |
|
<tbody> |
|
<tr> |
|
<td>de0_elev_2m</td> |
|
<td><span class="chip de0">DE0</span></td> |
|
<td>Reflective</td><td>2m</td> |
|
<td class="mono">100,530</td><td>276</td><td>9,768.6</td> |
|
<td>31.4m</td><td>3.86 m/s</td> |
|
<td><span class="pass">5/5</span></td><td>0.148m</td><td>0.227m</td> |
|
<td><span class="chip ok">✓</span></td> |
|
</tr> |
|
<tr> |
|
<td>sg_elev_2m</td> |
|
<td><span class="chip sg">DE0_SG</span></td> |
|
<td>Reflective</td><td>2m</td> |
|
<td class="mono">100,530</td><td>291</td><td>9,767.8</td> |
|
<td>31.3m</td><td>3.86 m/s</td> |
|
<td><span class="pass">5/5</span></td><td>0.149m</td><td>0.227m</td> |
|
<td><span class="chip warn">~</span></td> |
|
</tr> |
|
<tr> |
|
<td>de0_elev_4m</td> |
|
<td><span class="chip de0">DE0</span></td> |
|
<td>Reflective</td><td>4m</td> |
|
<td class="mono">25,236</td><td>31</td><td>10,369.0</td> |
|
<td>14.5m</td><td>3.80 m/s</td> |
|
<td><span class="warn">1/5</span></td><td>13.632m</td><td>25.103m</td> |
|
<td><span class="chip warn">~</span></td> |
|
</tr> |
|
<tr> |
|
<td>sg_elev_4m</td> |
|
<td><span class="chip sg">DE0_SG</span></td> |
|
<td>Reflective</td><td>4m</td> |
|
<td class="mono">25,236</td><td>32</td><td>10,368.8</td> |
|
<td>14.4m</td><td>3.79 m/s</td> |
|
<td><span class="warn">1/5</span></td><td>13.417m</td><td>24.632m</td> |
|
<td><span class="chip warn">~</span></td> |
|
</tr> |
|
<tr> |
|
<td>de0_mann_2m</td> |
|
<td><span class="chip de0">DE0</span></td> |
|
<td>Manning n=10</td><td>2m</td> |
|
<td class="mono">100,530</td><td>—</td><td>—</td> |
|
<td>—</td><td>—</td> |
|
<td><span class="fail">0/5</span></td><td>6.684m</td><td>9.494m</td> |
|
<td><span class="chip bad">Blowup 660s</span></td> |
|
</tr> |
|
<tr> |
|
<td>sg_mann_2m</td> |
|
<td><span class="chip sg">DE0_SG</span></td> |
|
<td>Manning n=10</td><td>2m</td> |
|
<td class="mono">100,530</td><td>—</td><td>—</td> |
|
<td>—</td><td>—</td> |
|
<td><span class="pass">5/5*</span></td><td>0.163m*</td><td>0.275m*</td> |
|
<td><span class="chip bad">Blowup 660s</span></td> |
|
</tr> |
|
<tr> |
|
<td>de0_mann_4m</td> |
|
<td><span class="chip de0">DE0</span></td> |
|
<td>Manning n=10</td><td>4m</td> |
|
<td class="mono">25,236</td><td>30</td><td>12,005.4</td> |
|
<td>14.5m</td><td>3.74 m/s</td> |
|
<td><span class="warn">1/5</span></td><td>13.659m</td><td>25.128m</td> |
|
<td><span class="chip warn">~</span></td> |
|
</tr> |
|
<tr> |
|
<td>sg_mann_4m</td> |
|
<td><span class="chip sg">DE0_SG</span></td> |
|
<td>Manning n=10</td><td>4m</td> |
|
<td class="mono">25,236</td><td>31</td><td>11,987.6</td> |
|
<td>14.4m</td><td>3.74 m/s</td> |
|
<td><span class="warn">1/5</span></td><td>13.422m</td><td>24.632m</td> |
|
<td><span class="chip ok">✓</span></td> |
|
</tr> |
|
</tbody> |
|
</table> |
|
</div> |
|
<p style="font-size:12px;color:var(--muted)">* sg_mann_2m measured before blowup at t=660s — truncated SWW file, not directly comparable to de0_mann_2m.</p> |
|
<p style="font-size:12px;color:var(--muted)">"~" stable flag: run completed normally but diagnostics flagged an implied-speed spike above threshold (typical at initial timestep).</p> |
|
</section> |
|
|
|
<!-- ══════════════════════════════════════════════════════════ FILES --> |
|
<section id="files"> |
|
<h2>Code Changes & Files</h2> |
|
|
|
<h3>Modified source files (branch: feature/subgrid-terrain-sampling)</h3> |
|
<div class="file-links"> |
|
<div class="file-link"> |
|
<div class="icon">🔧</div> |
|
<div class="info"> |
|
<div class="name">anuga/shallow_water/shallow_water_domain.py</div> |
|
<div class="desc">Building cell fix in <code>set_subgrid_dem()</code> — Fix 1 (flat-bed table replacement) + Fix 2 (phantom stage correction)</div> |
|
</div> |
|
<a href="https://github.com/Hydrata/anuga_core/blob/feature/subgrid-terrain-sampling/anuga/shallow_water/shallow_water_domain.py" target="_blank">View on GitHub →</a> |
|
</div> |
|
<div class="file-link"> |
|
<div class="icon">🔧</div> |
|
<div class="info"> |
|
<div class="name">anuga/structures/inlet.py</div> |
|
<div class="desc"><code>get_depths()</code> clamped to ≥ 0 — prevents assertion error when SGS dry cell has stage < bed_centroid</div> |
|
</div> |
|
<a href="https://github.com/Hydrata/anuga_core/blob/feature/subgrid-terrain-sampling/anuga/structures/inlet.py" target="_blank">View on GitHub →</a> |
|
</div> |
|
<div class="file-link"> |
|
<div class="icon">🧪</div> |
|
<div class="info"> |
|
<div class="name">anuga/subgrid/tests/test_building_cell_fix.py</div> |
|
<div class="desc">9 regression tests for both fixes. Standalone — no DEM file required. Uses CellVolumeTable directly.</div> |
|
</div> |
|
<a href="https://github.com/Hydrata/anuga_core/blob/feature/subgrid-terrain-sampling/anuga/subgrid/tests/test_building_cell_fix.py" target="_blank">View on GitHub →</a> |
|
</div> |
|
</div> |
|
|
|
<h3>Branch</h3> |
|
<div class="file-links"> |
|
<div class="file-link"> |
|
<div class="icon">🌿</div> |
|
<div class="info"> |
|
<div class="name">feature/subgrid-terrain-sampling</div> |
|
<div class="desc">Commit c376af88 — Fix SGS phantom mass creation in building cells · 3 files · +383 / −2</div> |
|
</div> |
|
<a href="https://github.com/Hydrata/anuga_core/tree/feature/subgrid-terrain-sampling" target="_blank">Browse branch →</a> |
|
</div> |
|
<div class="file-link"> |
|
<div class="icon">📝</div> |
|
<div class="info"> |
|
<div class="name">Commit c376af88</div> |
|
<div class="desc">Fix SGS phantom mass creation in building cells · Full commit with description of both fixes</div> |
|
</div> |
|
<a href="https://github.com/Hydrata/anuga_core/commit/c376af88" target="_blank">View commit →</a> |
|
</div> |
|
</div> |
|
|
|
<h3>Next steps</h3> |
|
<ul style="padding-left:20px;line-height:2"> |
|
<li>Test with a finer DEM (0.5m or 1m pixel) and different mesh resolutions to find where SGS accuracy improvement becomes significant</li> |
|
<li>Profile the table-lookup overhead to understand the 4–5% slowdown (vectorisation potential?)</li> |
|
<li>Write a CHANGELOG entry for the phantom-depth fix</li> |
|
<li>Open PR to merge <code>feature/subgrid-terrain-sampling</code> into main once review is complete</li> |
|
</ul> |
|
</section> |
|
|
|
</main> |
|
|
|
<footer> |
|
ANUGA 3.2.0 · SGS feature branch · Generated 2026-02-27 · |
|
<a href="https://github.com/Hydrata/anuga_core/tree/feature/subgrid-terrain-sampling">Hydrata/anuga_core</a> |
|
</footer> |
|
|
|
</body> |
|
</html> |