Fix culvert discharge chatter, make drain volume transfer mass-exact, limit withdrawal per step - #360
Fix culvert discharge chatter, make drain volume transfer mass-exact, limit withdrawal per step#360keesnederhoff wants to merge 3 commits into
Conversation
Newark culvert discharges chattered full-amplitude (drains 108, 15) from v2.3.0 onward. Two independent regressions, both restored to v2.0-v2.2 behaviour (output now matches v2.2.0): 1. sfincs_discharges.f90: v2.3.0 / PR #211 ("Moving gates") unified the drainage types and moved the available-volume clamp to AFTER the structure_relax relaxation. For volume-limited drains this applies the raw clamp output (empty->0, refill->full) every step. Clamp first, relax last, so the applied discharge is a smooth low-pass again. 2. sfincs_continuity.f90: restore the max(z_volume + qtsrc*dt, 0.0) floor on the source/drain volume update. The v2.3.0+ form let the cell volume overshoot negative and then gated the source off, making volume-limited drains ring (residual ripple). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Leynse
left a comment
There was a problem hiding this comment.
Nice fix Kees!
2 questions;
- Did you think about implications for mass conservation?
- And possible impact on other point sources as disfile etc, that are also affected by this?
Copilot review - One real concern:
1. Reordering relax after the clamp breaks the volume limit (and mass conservation)
sfincs_discharges.f90:626-645 — the clamp now applies to the target discharge, but the value actually written to qtsrc is a blend with the previous (possibly much larger) discharge. So the applied withdrawal can exceed the water available in nmin. The new max(..., 0.0) floor in continuity absorbs that on the donor side, but qtsrc(jout) = qq still delivers the full discharge to the receiving cell → net water creation whenever the drain runs a cell dry. Under the old order this couldn't happen.
Suggested alternative that fixes chatter and keeps the limiter authoritative: the chatter comes from relaxing toward the clamped previous discharge (qq0 = -qtsrc(jin) at :440/:540 feeds the clamped value back into the Bates update). Keep an unclamped qq_prev per structure, relax against that, then clamp last:
qq = w*qq_target + (1.0 - w)*qq_prev(idrn) ! smooth, unclamped
qq_prev(idrn) = qq
... volume clamp ... ! authoritative, applied last
qtsrc(jin) = -qq ; qtsrc(jout) = qq
2. Scope of the continuity change is wider than drainage structures
sfincs_continuity.f90:319 — the loop runs over nsrcdrn, i.e. all point sources, not just drains. Previously a positive source into a cell with z_volume < 0 was silently dropped; now it is applied. That is arguably the correct fix, but it's a behaviour change for plain src points that isn't mentioned in the PR title/description — worth calling out in the release notes. Also, momentum:697, infiltration:649 and meteo:1321 all explicitly test for negative z_volume, so silently flooring it at source cells hides mass errors that were previously visible.
Drainage structures are now applied as pairs in continuity: the donor cell is floored (zero volume on subgrid, bed level on regular) and the receiving cell gets exactly the volume that was actually removed. With the clamp-then-relax order the outfall used to receive the full relaxed discharge while the intake floor absorbed the shortfall, creating 2.2 % of the drained volume on the Newark testbed. Plain point sources keep the floor. A new end-of-run log line reports the volume nominally withdrawn and the part never removed because the donor ran dry. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01669UBEbK5qqRoSsGHqhD4o
New keyword drainage_volfrac (default 0.5, range 0.05-1). With the old hard limit q <= V/dt a volume-limited culvert emptied its cell in one step and the limited discharge switched between zero and full demand; with alpha < 1 the cell volume decays geometrically and the discharge converges smoothly to the inflow. drainage_volfrac = 1 recovers the old behaviour. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01669UBEbK5qqRoSsGHqhD4o
|
Thanks Tim, both points were worth checking. 1. Mass conservation: you are right, and the suggested alternative does not fix it.
nTV is total variation over twice the range (about 1 for a smooth hydrograph, about 40 for full-amplitude chatter); reversals counts direction changes larger than 5 % of the range.
2. Scope of the continuity change. Correct, the floor also applies to plain |
Fix culvert discharge chatter, make drain volume transfer mass-exact, limit withdrawal per step
Tim reported wiggles in the culvert discharges when running the refreshed testbed after the Galibier release. In the Newark drainage model, the discharge on volume-limited culverts (drains 108 and 15) oscillates full-amplitude (0 to -16 m3/s) across the whole event, while drains that are not volume-limited (e.g. 10, 100) stay smooth. Bisected across the release archive: v2.2.0 is smooth, v2.3.0 (#211, moving gates) swapped the order of the volume clamp and the relaxation in the shared drainage tail, so the on/off clamp output is applied directly and fed back as the relaxation memory.
Changes
sfincs_discharges.f90: clamp first, relax last, so the applied discharge is a smooth low-pass again (restores the v2.0-v2.2 order).sfincs_continuity.f90: drainage structures are applied as pairs. The donor cell is floored at zero volume (subgrid) or at the bed (regular), and the receiving cell gets exactly the volume that was actually removed. v2.2 delivered the full discharge to the outfall even when the intake had run dry.sfincs_discharges.f90: the volume limit is now a fraction of the donor cell volume per step, new keyworddrainage_volfrac(default 0.5, range 0.05-1,1recovers the old hard limit). With the hard limitq <= V/dta volume-limited culvert emptied its cell in one step and the limited discharge switched between zero and full demand; with a fraction the cell volume decays geometrically and the discharge converges smoothly to the inflow.sfincs_continuity.f90: plain point sources are applied and floored (previously silently dropped when the cell volume was negative).Drain/source withdrawal: X m3, not removed (dry cell): Y m3 (Z%), the volume drains nominally withdrew and the part the dry donor never gave up.Newark testbed, 8 h event (nTV = total variation / 2 range, about 1 for smooth, about 40 for chatter; reversals = direction changes > 5 % of range; jump = largest step change / range)
drainage_volfrac = 1drainage_volfrac = 0.5drainage_volfrac = 0.25drainage_volfrac = 0.5also removes the sharp dip and the deep late peak that= 1still shows on drain 15. Flood map: zsmax at 0.5 is within 8 mm of the value at 1 in every cell; at 0.25 the limiter starts to throttle (11 cells change by more than 5 cm, up to 0.31 m), which is why 0.5 is the default.At the SFINCS default
structure_relax = 10(Newark sets 600) the volume-limited drains stay fixed (drain 108 nTV 33 on main, 1.8 here; drain 15 15 on main, 1.3 here; undelivered 3.7 % atdrainage_volfrac = 1, 0.07 % at 0.5). The small control drains 100 and 10 chatter at relax 10 on main and on this branch alike (drain 100 nTV 66 vs 64, about 570 reversals in both), so that is a separate, pre-existing weakness of the 10 s default against a 1.8 s time step and is not addressed here.Behaviour change to be aware of. Because the outfall no longer receives phantom water, zsmax drops by up to 1.1 m in 173 cells around outfalls relative to the first push of this PR, and relative to v2.2 which had the same phantom water. Regular-grid gate case
01_Implementation/controlled_gates_group/controlled_gates: water levels at the observation points agree to 1 mm with the first push, and the gate intake never dries; the only discharge difference is a phase shift of the +-0.5 m3/s flicker both versions show while the gate sits at near-zero head.