Refuse CBF recovery on a multiplier-constrained boundary (#614) - #653
Open
lmoresi wants to merge 1 commit into
Open
Refuse CBF recovery on a multiplier-constrained boundary (#614)#653lmoresi wants to merge 1 commit into
lmoresi wants to merge 1 commit into
Conversation
The consistent-boundary-flux back-calculation reads the VELOCITY residual. On a boundary held by `add_constraint_bc` the traction has been moved into the multiplier block, so the velocity rows retain only a CONSTANT traction, and de-smearing hands that constant straight back. That is not an inaccuracy, it is a different quantity. Measured on SolCx, the raw reaction along the boundary took exactly two magnitudes in the ratio 4 : 2 : 1 at midpoint / interior vertex / end vertex -- the P2 line-trace lumped mass, i.e. R = c*m_i to the digit -- so the recovered flux had a standard deviation of 2e-15 across 63 nodes where the equivalent `Stokes` solve varied correctly and correlated -0.997 with the exact topography. The issue reported this as ~1e12, which is no longer what happens; today it returns a plausible O(0.1) number that happens to carry no spatial information at all. That is worse, not better: 1e12 announces itself and 0.11 does not, and a caller taking a mean or a peak off it gets something entirely reasonable looking. Nothing here is corrupted -- the reaction assembly and the de-smear both do exactly what they are told. The primitive reads a place that, on this solver, no longer holds the quantity. So this refuses instead, naming multiplier() and topography(), which is where the traction actually is. The guard is on the BOUNDARY, not the solver: the regression checks that the Dirichlet boundary of the same constrained solve still recovers, and that what it recovers has spatial content rather than being constant too. This is the cheap half. The full fix is for the traction accessor to DISPATCH on how each boundary is held -- rotated reaction, multiplier, or CBF -- so a caller does not have to know. The solver already has what it needs to decide (`_block_constraint_bcs`, `_rotated_freeslip_info`); what is missing is a common return shape, since multiplier() returns a field where the others return nodal arrays. That belongs with #607/#617, which is already making the constrained route return the whole traction. Underworld development team with AI support from Claude Code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stops the silent wrong answer in #614. Root-caused first — see the analysis on the issue.
What is actually wrong
CBF reads the velocity residual. On a boundary held by
add_constraint_bcthe traction has been moved into the multiplier block, so the velocity rows retain only a constant traction. The raw reaction along the boundary takes exactly two magnitudes, in ratiowhich is the P2 line-trace lumped mass at midpoint / interior vertex / end vertex. So
R = c·m_ito the digit, and de-smearing dividesm_iback out and returnsc. Recovered std across 63 nodes: 2e-15, against 0.256 and correlation −0.997 for the equivalentStokessolve.Nothing is corrupted. The reaction assembly and the de-smear both do exactly what they are told; the primitive reads a place that no longer holds the quantity on this solver.
Why this is more urgent than the issue implies
#614 reported ~1e12. That is no longer what happens — it now returns a plausible O(0.1) number carrying no spatial information. 1e12 announces itself; 0.11 does not. Anyone taking a mean or a peak off it gets something entirely reasonable-looking.
What this does
Refuses, naming
multiplier()/topography()— where the traction actually is.The guard is on the boundary, not the solver. The regression asserts that the Dirichlet boundary of the same constrained solve still recovers, and that what it recovers has spatial content rather than being constant too — otherwise this would be a guard on the wrong thing.
Also retracted on the issue: the augmented-Lagrangian hypothesis (
r = 1e4·μ) rested on the 1e12 magnitude, and there is no r-scaling left — the peak moves only 0.110 → 0.143 across three decades of viscosity contrast.The half this deliberately does not do
The real fix is for the traction accessor to dispatch on how each boundary is held — rotated reaction, multiplier, or CBF — so callers do not have to know which primitive matches their boundary condition. Today there are three entry points with three different contracts:
boundary_normal_tractionraises unless the boundary is rotated,boundary_fluxsilently misread a constrained one (until this PR), andmultiplier()/topography()exist only on the constrained class.The solver already has everything needed to decide (
_block_constraint_bcs,_rotated_freeslip_info). What is missing is a common return shape:multiplier()returns a field where the others return nodal arrays. That is exactly what #617 is building, so the dispatch belongs there rather than here.Tests:
test_101915 passed;test_1061+test_101839 passed.Underworld development team with AI support from Claude Code