Skip to content

Insert essential boundary values in the rotated solve's field copy-back - #500

Open
lmoresi wants to merge 1 commit into
developmentfrom
bugfix/rotated-freeslip-copyback-497
Open

Insert essential boundary values in the rotated solve's field copy-back#500
lmoresi wants to merge 1 commit into
developmentfrom
bugfix/rotated-freeslip-copyback-497

Conversation

@lmoresi

@lmoresi lmoresi commented Aug 4, 2026

Copy link
Copy Markdown
Member

Fixes #497.

Essential-BC DOFs are absent from the global vector, so the rotated path's per-field globalToLocal scatter left them at zero in the output fields wherever the Dirichlet datum was non-zero. The solve itself was always correct — the residual path inserts the values internally — but every field-based diagnostic (projection, uw.maths.Integral, uw.function.evaluate, renders) read a garbage boundary strip. Measured on the split-node fault study: far-field sigma_xy of 0.79 for a true 1.00, and a projected stress row off by 3x one fault length out. Every existing rotated test used homogeneous Dirichlet on the other walls, so zero was accidentally the right value — which is how this survived.

Fix: petsc_dm_insert_boundary_values in cython/petsc_discretisation.pyx — wrapping DMPlexInsertBoundaryValues, which petsc4py does not expose; the same insertion the consistent-boundary-flux paths already use internally (cf. #407/#411) — called per field after the scatter in _finalize_rotated_solution.

Not affected (both live in the global vector): the rotated boundary's own prescribed datum (add_rotated_freeslip_bc(conds != 0, ...), the free-surface path), and the sigma_nn / dynamic-topography reaction recovery.

Regression test: test_rotated_solve_fields_carry_inhomogeneous_dirichlet_walls in tests/test_1018_rotated_freeslip.py — an inhomogeneous lid with rotated free-slip sides, asserting the FIELD carries the wall values; it fails without the fix. The change is a verbatim cherry-pick of 08f8603 from feature/fault-split-node, where the full rotated suite (19 tests) and the fault-contact suites run green with it.

The tell that found it, for the record: the divergence theorem — Integral(2 e_xy) dA against its own boundary term.

Underworld development team with AI support from Claude Code

Essential-BC DOFs are absent from the global vector, so the rotated path's
per-field globalToLocal scatter left them at ZERO in the output fields
wherever the Dirichlet datum was non-zero. The solve itself was always
right -- the residual path inserts the values internally -- but every
field-based diagnostic (projection, Integral, evaluate, renders) read a
garbage boundary strip. Measured: far-field sigma_xy of 0.79 for a true
1.00, and a projected stress row off by 3x one fault length out. Invisible
to every earlier rotated test because their Dirichlet walls were
homogeneous: zero was accidentally the datum.

Fix: petsc_dm_insert_boundary_values in cython/petsc_discretisation.pyx --
wrapping DMPlexInsertBoundaryValues, which petsc4py does not expose; the
same insertion the consistent-boundary-flux paths already use (issues
#407/#411) -- called per field after the scatter in
_finalize_rotated_solution. The regression test drives an inhomogeneous
lid with rotated free-slip sides and asserts the FIELD carries the wall
values. The tell that found it: the divergence theorem, Integral(2 e_xy)
against its own boundary term.

The rotated boundary's prescribed datum and the sigma_nn reaction recovery
were never affected (both live in the global vector). Fixes #497.

Underworld development team with AI support from Claude Code
Claude-Session: https://claude.ai/code/session_01WDKP73LD3dCFQSNgEXbbw9
Copilot AI lite review requested due to automatic review settings August 4, 2026 13:12
@lmoresi

lmoresi commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

Adversarial self-review, scope and hazards:

  • Scope: three files; the only behavioural change is one insertion call per field in _finalize_rotated_solution, on the rotated path only. The standard SNES path is untouched (it never had the gap — PETSc's own copy-back inserts).
  • Could the insertion be wrong for homogeneous BCs? No: inserting zeros over zeros is idempotent, and the pre-existing rotated suite (18 tests, all homogeneous-wall) passes bit-identically with the fix in place.
  • Could it double-apply a rotated datum? No: the rotated datum rows are velocity DOFs present in the global vector (constrained algebraically, not eliminated), so DMPlexInsertBoundaryValues does not touch them; the nonlinear-datum test asserts those field values and passes.
  • Parallel: the insertion is per-field on the sub-DM local vector, rank-local by construction; verified inside the fault-contact work at np=2 where the affected fields feed collective diagnostics.
  • What we did not do: audit every historical rotated result for inhomogeneous-Dirichlet contamination. The scan is cheap where suspected — the divergence-theorem check flags it immediately — and no production study we know of combined the two.

Underworld development team with AI support from Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a rotated free-slip solve copy-back defect where inhomogeneous essential (Dirichlet) boundary values were missing from the per-field local vectors after scattering from the composite global solution, leading to incorrect field-based diagnostics near Dirichlet boundaries.

Changes:

  • Adds a Cython shim petsc_dm_insert_boundary_values() wrapping DMPlexInsertBoundaryValues() (not exposed by petsc4py) to insert essential BC values into local vectors.
  • Updates the rotated solve finalization to call this insertion after the global-to-local scatter for each field.
  • Adds a regression test ensuring inhomogeneous Dirichlet wall values are preserved in the output field after a rotated solve.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
tests/test_1018_rotated_freeslip.py Adds a regression test that fails if rotated copy-back drops inhomogeneous Dirichlet values.
src/underworld3/utilities/rotated_bc.py Inserts essential BC values into each field local vector after scatter in _finalize_rotated_solution.
src/underworld3/cython/petsc_discretisation.pyx Introduces petsc_dm_insert_boundary_values() as a Python-level wrapper around DMPlexInsertBoundaryValues.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +395 to +396
from underworld3.cython.petsc_discretisation import \
petsc_dm_insert_boundary_values
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants