add_rotated_freeslip_bc and the Lagrange-multiplier (block-constrained) Stokes machinery can both be active on the same solver, and nothing checks for it.
The rotated solve builds its own index-set fieldsplit over exactly two fields:
# src/underworld3/utilities/rotated_bc.py, _solve_rotated_iterative
pc.setFieldSplitIS(("vel", vel_is), ("pres", pres_is))
vel_is and pres_is come from solver._subdict["velocity"] / ["pressure"]. When solver._multipliers is non-empty those fields carry their own DOFs (registered at petsc_generic_snes_solvers.pyx ~7824), and those DOFs are in neither index set. The preconditioner is then built over a strict subset of the operator's rows.
build_rotation is likewise field-0/field-1 only (_VELOCITY_FIELD = 0, _PRESSURE_FIELD = 1).
add_rotated_freeslip_bc (petsc_generic_snes_solvers.pyx ~5880-5905) validates the boundary label and the datum shape but does not check for multipliers, and the solve dispatch (~8682) routes to the rotated driver without checking either.
What to do
The two mechanisms are alternative ways to impose the same constraint, so combining them is almost certainly user error rather than a configuration worth supporting. A raise at add_rotated_freeslip_bc (and symmetrically wherever a block constraint is added) naming both mechanisms would be the right size of fix.
If the combination should be supported, the rotated fieldsplit needs a third split for the multiplier fields and build_rotation needs to know about them.
Found while auditing the rotated free-slip preconditioner; not exercised by any current test or example, so this is a latent trap rather than an observed failure.
Underworld development team with AI support from Claude Code
add_rotated_freeslip_bcand the Lagrange-multiplier (block-constrained) Stokes machinery can both be active on the same solver, and nothing checks for it.The rotated solve builds its own index-set fieldsplit over exactly two fields:
vel_isandpres_iscome fromsolver._subdict["velocity"]/["pressure"]. Whensolver._multipliersis non-empty those fields carry their own DOFs (registered atpetsc_generic_snes_solvers.pyx~7824), and those DOFs are in neither index set. The preconditioner is then built over a strict subset of the operator's rows.build_rotationis likewise field-0/field-1 only (_VELOCITY_FIELD = 0,_PRESSURE_FIELD = 1).add_rotated_freeslip_bc(petsc_generic_snes_solvers.pyx~5880-5905) validates the boundary label and the datum shape but does not check for multipliers, and the solve dispatch (~8682) routes to the rotated driver without checking either.What to do
The two mechanisms are alternative ways to impose the same constraint, so combining them is almost certainly user error rather than a configuration worth supporting. A
raiseatadd_rotated_freeslip_bc(and symmetrically wherever a block constraint is added) naming both mechanisms would be the right size of fix.If the combination should be supported, the rotated fieldsplit needs a third split for the multiplier fields and
build_rotationneeds to know about them.Found while auditing the rotated free-slip preconditioner; not exercised by any current test or example, so this is a latent trap rather than an observed failure.
Underworld development team with AI support from Claude Code