Add SNES_Scalar.petsc_use_constant_nullspace for singular Poisson#201
Open
lmoresi wants to merge 1 commit into
Open
Add SNES_Scalar.petsc_use_constant_nullspace for singular Poisson#201lmoresi wants to merge 1 commit into
lmoresi wants to merge 1 commit into
Conversation
Scalar problems with no Dirichlet boundary conditions have a constant kernel — Poisson on a fully-Neumann box, on a closed manifold (sphere, torus), on a periodic domain. The linear operator is singular and the solver either diverges or returns an arbitrary (initial-guess-dependent) solution within the constant null space. The new opt-in flag attaches MatNullSpace(constant=True) to the Jacobian operator (and its transpose, and the preconditioner) at setup. PETSc projects each KSP RHS onto the orthogonal complement of the nullspace before solving and returns the minimum-norm (near-zero-mean) solution. Default False, so every existing scalar solver is unaffected. Verified on a pure-Neumann 2D box: pushes the discrete mean of the solution from -0.27 (arbitrary) to -0.04 (canonical); non-constant part of the solution is unchanged. Test at tests/test_1055_snes_scalar_constant_nullspace.py. Underworld development team with AI support from Claude Code
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds an opt-in PETSc constant-nullspace attachment for SNES_Scalar to improve robustness and solution selection for singular scalar operators (e.g., pure-Neumann Poisson problems and closed-manifold Laplacians) by informing PETSc of the constant kernel via MatNullSpace(constant=True).
Changes:
- Introduces
SNES_Scalar.petsc_use_constant_nullspace(defaultFalse) and wires it into solver setup via a new_attach_constant_nullspace()helper. - Adds a regression test covering the new flag on a pure-Neumann Poisson problem and validating the property setter behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
src/underworld3/cython/petsc_generic_snes_solvers.pyx |
Adds the petsc_use_constant_nullspace property and attaches a constant MatNullSpace to the scalar Jacobian. |
tests/test_1055_snes_scalar_constant_nullspace.py |
New regression tests for solver convergence/shape and the flag/property behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+2117
to
+2119
| if self._petsc_use_constant_nullspace: | ||
| self._attach_constant_nullspace() | ||
|
|
Comment on lines
+2142
to
+2144
| nullspace = PETSc.NullSpace().create( | ||
| constant=True, vectors=(), comm=self.dm.comm, | ||
| ) |
Comment on lines
+89
to
+98
| # With the flag, the mean of the solution should be much closer to | ||
| # zero than without (canonical minimum-norm pinning). The exact | ||
| # value depends on the discrete inner product against the constant | ||
| # mode, but the flag should move it toward zero by a clear margin. | ||
| mean_off = T_off.data[:, 0].mean() | ||
| mean_on = T_on.data[:, 0].mean() | ||
| assert abs(mean_on) < abs(mean_off), ( | ||
| f"with nullspace declaration, |mean(T)| should drop; " | ||
| f"got |mean_off|={abs(mean_off):.3e}, |mean_on|={abs(mean_on):.3e}" | ||
| ) |
Comment on lines
+1676
to
+1680
| projects the right-hand side onto the orthogonal complement of | ||
| the nullspace and selects the minimum-norm solution from the | ||
| null-affine family, so the system becomes uniquely solvable up | ||
| to that nullspace. | ||
|
|
4 tasks
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.
Summary
Adds an opt-in
petsc_use_constant_nullspaceflag onSNES_Scalarthat attaches a constantMatNullSpaceto the Jacobian. Needed for scalar problems whose linear operator has a constant kernel and no Dirichlet BC to break it: pure-Neumann domains, closed manifolds (sphere, torus, periodic boxes).Without it, PETSc either diverges on the singular system or returns an arbitrary solution within the constant null space, dependent on initial guess and solver path. With it, PETSc projects each KSP right-hand side onto the orthogonal complement of the nullspace and returns the minimum-norm (near-zero-mean) solution.
Default
False, so every existing scalar solver is unaffected.Implementation
src/underworld3/cython/petsc_generic_snes_solvers.pyx:_petsc_use_constant_nullspace = Falsefield inSNES_Scalar.__init__petsc_use_constant_nullspaceproperty + setter (setter invalidatesis_setupso the rebuild attaches the nullspace)_attach_constant_nullspace()helper: callssnes.setUp()to materialise the Jacobian, then setsMatNullSpace().create(constant=True)on operator + preconditioner + their transposesSNES_Scalar._setup_solverMirrors the existing Stokes saddle-point nullspace pattern (
_attach_stokes_nullspace) but specialised for the scalar case (no field decomposition, just the one Jacobian Mat).Example
Why this matters / context
Surfaced while testing the
SphericalManifoldwork (cd-1 mesh, separate WIP branch). Surface Poisson-Δ_S T = zon a closed sphere failsDIVERGED_LINE_SEARCHat iteration 0 without the nullspace declaration; with the flag it solves cleanly and recoversT = z/2 + constto discretisation error with monotone refinement convergence. The same flag works on any pure-Neumann scalar problem, manifold or not — flagging this as a small standalone change keeps the API addition independent of the larger manifold-mesh work.Test plan
tests/test_1055_snes_scalar_constant_nullspace.py— 2 tests:test_constant_nullspace_canonical_solution: pure-Neumann Poisson on a 2-D box; verifies convergence on both paths, shape-correctness matches analytic to FE accuracy on both paths, and the flag pulls the discrete mean toward zero.test_constant_nullspace_property_setter: property round-trip, setup invalidation.test_0001_meshes.py,test_0506_tensor_evaluate.py,test_1010_stokesCart.py(25/25 Stokes tests) all pass.Underworld development team with AI support from Claude Code