The defect
rotated_bc._finalize_rotated_solution scatters the converged global vector into the velocity/pressure fields:
for name, var in solver.fields.items():
sg = U.getSubVector(solver._subdict[name][0])
solver._subdict[name][1].globalToLocal(sg, var.vec)
U.restoreSubVector(solver._subdict[name][0], sg)
Essential-BC DOFs are absent from the global system, so this scatter leaves them at zero in the local fields. Wherever a native Dirichlet datum g != 0 is combined with a rotated solve, the fields carry the wrong wall values while the solve itself is correct (the residual path inserts the values internally).
Measured consequence (split-node fault study, walls (y-0.5, 0) + fault contact): far-field sigma_xy read 0.79 for a true 1.00; a projected-stress station row was off by 3x at one fault length. The tell was the divergence theorem: Integral(2*e_xy) dA = -0.045 against the boundary-term value ~0.96.
Scope
- Not affected: the rotated boundary's own prescribed datum (
add_rotated_freeslip_bc(conds != 0, ...)) — imposed on the global vector by the feasibility projection before the scatter; and the sigma_nn / dynamic-topography recovery, which reads the reaction (global residual).
- Affected: every field-based diagnostic (projection,
uw.maths.Integral, uw.function.evaluate, renders) in the boundary strip of any inhomogeneous native Dirichlet wall in a rotated solve. All existing rotated tests use homogeneous Dirichlet elsewhere, so zero was accidentally the right value — which is why this survived.
Fix (on feature/fault-split-node, to cherry-pick)
petsc_dm_insert_boundary_values shim in cython/petsc_discretisation.pyx (wraps DMPlexInsertBoundaryValues, which petsc4py does not expose — same insertion the CBF paths use internally, cf. #407/#411), called per field after the scatter in _finalize_rotated_solution. Regression test test_rotated_solve_fields_carry_inhomogeneous_dirichlet_walls in tests/test_1018_rotated_freeslip.py (fails without the fix).
Underworld development team with AI support from Claude Code
The defect
rotated_bc._finalize_rotated_solutionscatters the converged global vector into the velocity/pressure fields:Essential-BC DOFs are absent from the global system, so this scatter leaves them at zero in the local fields. Wherever a native Dirichlet datum g != 0 is combined with a rotated solve, the fields carry the wrong wall values while the solve itself is correct (the residual path inserts the values internally).
Measured consequence (split-node fault study, walls
(y-0.5, 0)+ fault contact): far-field sigma_xy read 0.79 for a true 1.00; a projected-stress station row was off by 3x at one fault length. The tell was the divergence theorem:Integral(2*e_xy) dA = -0.045against the boundary-term value ~0.96.Scope
add_rotated_freeslip_bc(conds != 0, ...)) — imposed on the global vector by the feasibility projection before the scatter; and the sigma_nn / dynamic-topography recovery, which reads the reaction (global residual).uw.maths.Integral,uw.function.evaluate, renders) in the boundary strip of any inhomogeneous native Dirichlet wall in a rotated solve. All existing rotated tests use homogeneous Dirichlet elsewhere, so zero was accidentally the right value — which is why this survived.Fix (on
feature/fault-split-node, to cherry-pick)petsc_dm_insert_boundary_valuesshim incython/petsc_discretisation.pyx(wrapsDMPlexInsertBoundaryValues, which petsc4py does not expose — same insertion the CBF paths use internally, cf. #407/#411), called per field after the scatter in_finalize_rotated_solution. Regression testtest_rotated_solve_fields_carry_inhomogeneous_dirichlet_wallsintests/test_1018_rotated_freeslip.py(fails without the fix).Underworld development team with AI support from Claude Code