Skip to content

Stokes and Stokes_Constrained disagree about which solver options are reachable, in opposite directions #483

Description

@lmoresi

Measured off the live PETSc objects after two solves, tolerance = 1e-6, on development @ 04379583:

option Stokes Stokes_Constrained
ksp_rtol reachable NOT reachable (→ tolerance × 0.1)
fieldsplit_velocity_ksp_rtol NOT reachable (→ tolerance × 0.033) reachable
fieldsplit_pressure_ksp_rtol NOT reachable (→ tolerance × 0.1) reachable
fieldsplit_{velocity,pressure}_ksp_max_it reachable reachable
fieldsplit_{velocity,pressure}_ksp_type reachable reachable
snes_max_it NOT reachable (→ 50) NOT reachable (→ 50)
snes_rtol NOT reachable (→ tolerance) NOT reachable (→ tolerance)

Both are saddle-point solvers. They disagree about the outer KSP tolerance and about the inner ones, in opposite directions, and nothing documents the difference. max_it and ksp_type on the same prefixes are the control: same dict, same solve, they go through — so this is specific to the keys a tolerance setter writes.

Scalar (Poisson) and vector (Vector_Projection) families are clean — every option tested is reachable. The whole class is localised to the saddle-point path.

Repro: ~/+Simulations/solver_reachability_audit/reachability_matrix.py -uw_family stokes|constrained|scalar|vector. It reads the live objects rather than the options database (a clobbered key leaves no trace there), solves twice (solve-time clobbers only bite on the second), and runs a defaults arm as the control so a "reachable" verdict cannot be an accident of the override matching the default.

Two findings, one surface

1. The disagreement itself. Whatever the intended ownership rule is, it should be the same for both solvers.

2. snes_rtol is owned but looks settable. The in-flight _reassert_outer_tolerances() (see below) deliberately re-writes snes_rtol and ksp_atol at every solve, by design — tolerance owns the outer tolerances. That is a defensible decision, but it leaves snes_rtol looking like a knob that silently does nothing. Either honour the user's value or say in the tolerance docstring that these keys are owned and will be overwritten. The current middle ground — accept it, then discard it — is the worst of the three.

Where the fix belongs

#475 / feature/regime-diagram. That branch already carries the fix for the snes_max_it and fieldsplit_*_ksp_rtol half of this (#477), so this is adjacent work on the same mechanism. Note those commits are not yet pushedf1555b3a, 5ad68b2f, 954ff4f3 are local only, so the PR does not show them and this issue looks broader than it is until they land.

Found by the solver configuration audit, docs/reviews/2026-07/solver-configuration-reachability-audit.md (branch docs/solver-config-audit), findings F-3 and F-4.

Underworld development team with AI support from Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions