Fix spherical shell labels and add Stokes residual diagnostics#242
Conversation
Rework SphericalShellInternalBoundary mesh construction so the shell volume is retained, the internal spherical surface is embedded, and duplicate internal geometry is removed before meshing. The previous nested-sphere fragment path could leave the Lower boundary unusable for BdIntegral; the benchmark probe observed lower_area=0.0 even though the Stokes solve converged. Add a boundary-integral regression test that checks nonzero, close-to-analytic Lower, Internal, and Upper surface areas. Validation: ./uw build passed. py_compile passed for src/underworld3/meshing/spherical.py. pytest -q tests/test_0502_boundary_integrals.py::test_bd_integral_spherical_internal_boundary_areas passed. 005_internal_boundary_delta_probe.py passed with -uw_mesh_source uw3_builtin for serial Nitsche, serial constrained, 8-rank Nitsche, and 8-rank constrained runs.
Add explicit radius ordering validation for SphericalShellInternalBoundary and wrap the entity-selection comprehensions introduced by the internal-boundary label fix. Validation: py_compile passed for src/underworld3/meshing/spherical.py and test_bd_integral_spherical_internal_boundary_areas passed.
Record the Zhong-style SphericalShellInternalBoundary constrained free-slip behaviour in UW3 tests. The new regression separates the constrained weak-form check from the practical solver failure: monolithic LU constrained matches monolithic Nitsche, while the fast grouped pressure-plus-multiplier Schur path remains a strict expected failure against the Zhong velocity response. This documents that the remaining fix belongs in the practical grouped Schur/preconditioner path, not in the basic 3-D vector-scalar multiplier assembly.
Record that the constrained weak form matches monolithic Nitsche while PETSc field-split constrained solves remain incorrect even with exact LU sub-solves on the velocity and grouped pressure-multiplier blocks. Validation: pixi run -e amr-dev pytest -q tests/test_1064_constrained_spherical_shell_response.py -> 1 passed, 2 xfailed in 137.51s.
Use the validated Nitsche/default field-split response as the Zhong reference and relabel the direct-LU Nitsche/constrained comparison as a diagnostic path rather than a monolithic benchmark reference. Validation: FI_PROVIDER=tcp pixi run -e amr-dev pytest -q tests/test_1064_constrained_spherical_shell_response.py -> 2 passed, 2 xfailed in 174.07s.
Expose a low-level Stokes saddle-point helper for diagnostic residual recovery. The helper assembles the volume-only PETSc FEM residual into local field layouts, with an optional cell-index path through DMPlexComputeResidualByKey for boundary-strip probes. This is intended for CBF/topography debugging and does not alter the normal Stokes solve path. Validated by rebuilding the mantle-convection worktree with ./uw build and rerunning the Zhong 010 serial and 8-rank benchmark paths from the benchmark repository.
Expose a small PETSc compatibility helper to fetch the boundary-specific PetscWeakForm registered by DMAddBoundary/PetscDSAddBoundary_UW. Add SNES_Stokes_SaddlePt.compute_boundary_residual_fields() so benchmark diagnostics can assemble the registered Nitsche boundary residual for a named boundary and field through DMPlexComputeBdResidualSingle. This fixes the earlier zero boundary-residual diagnostic, which used the global PetscDS weak form instead of the boundary weak form that actually stores UW3 Nitsche terms.
Document that compute_volume_residual_fields uses PETSc's keyed residual path, which appends registered boundary residuals in the implicit Stokes mode. This prevents the helper from being treated as a true volume-only CBF recovery path.
Add a UW PETSc compatibility helper that assembles DMPlexComputeResidualByKey on a cloned DM with copied sections, fields, equations, constants, and auxiliary vector, but without registered boundary objects. This gives selected-cell volume residuals without mutating the live solver PetscDS. Update compute_volume_residual_fields() so selected-cell calls use the volume-only cloned-DM path by default, while include_boundary_terms=True preserves PETSc's original keyed residual behavior for cancellation/debug checks.
Use void * for the UW_DMPlexComputeResidualByKeyVolumeOnly context argument so the shared PETSc compatibility header builds in all Cython extension modules. The Cython declaration already exposes this wrapper with a void * context. Validation: ./uw build passed. pytest -q tests/test_0502_boundary_integrals.py::test_bd_integral_spherical_internal_boundary_areas passed. pytest -q tests/test_1064_constrained_spherical_shell_response.py reported 2 passed and 2 expected xfails.
Review — strong work; one regression to resolve before mergeReviewed in two parts (C/Cython diagnostics API, and the spherical meshing + tests). The boundary-label fix is correct and backward-compatible, and the new residual-diagnostics API is additive-only and memory-clean against the custom PETSc 3.25 build target. Two items should be resolved before this lands on ✅ What's solid
|
…uter regions
The single-volume cut+embed design (this PR) produces one shell volume with the
internal sphere embedded as a conformal surface — it has no Inner/Outer region
sub-volumes. But mesh.regions was still set to the Inner/Outer enum, so
extract_region('Inner') failed with an opaque 'Label not found on DM'. Leave
mesh.regions as None (drop the phantom enum) so region extraction reports a
clean 'no regions defined' and the API doesn't advertise a capability the DM
can't back. Use the 'Internal' boundary label for the internal interface.
Also re-tier the new 3D-mesh-gen area test to level_2/tier_b (it inherits the
module-level level_1/tier_a but builds a full 3D gmsh+embed mesh and the
generator is not yet production-soaked). Addresses the PR underworldcode#242 review.
Underworld development team with AI support from Claude Code
…-convection-benchmarks # Conflicts: # src/underworld3/cython/petsc_generic_snes_solvers.pyx
Updated and ready — region regression fixed properlyPushed three changes to address the review and bring this current (maintainer-edit), per Louis's direction:
Validated on the merged branch: Remaining NIT from review (not blocking): the Underworld development team with AI support from Claude Code |
lmoresi
left a comment
There was a problem hiding this comment.
Approved. Internal-surface labels fixed (Lower 0→160 pts), Inner/Outer region extraction now works end-to-end, residual diagnostics are additive + memory-clean. Region regression and test-tier addressed; merged current development (conflict resolved keeping both diagnostics + callback method sets). See detailed comment above.
Summary
This PR adds the UW3 support needed to reproduce the spherical-shell Stokes response benchmarks used in Zhong et al. (2008):
uw.meshing.SphericalShellInternalBoundary()soLower,Internal, andUpperlabels are valid DMPlex boundary labels.Benchmark Theory
Zhong et al. (2008) compare Stokes response functions in a spherical shell with:
r_b = 0.55, outer radiusr_t = 1.0;delta(r-r0) P_l^0(theta) e_r;In UW3 the delta forcing is represented as the equivalent radial natural traction on an embedded internal spherical boundary at
r0. This requiresSphericalShellInternalBoundary()to produce three usable surface labels:Lower: CMB boundary;Internal: load boundary;Upper: surface boundary.The velocity response is a standard Stokes solve result. Dynamic topography is more delicate: direct pointwise/projected
sigma_rris not the same as the consistent-boundary-flux style stress recovery used by strong free-slip benchmark codes. The residual diagnostic hooks in this PR allow the benchmark script to recover boundary reaction/topography from selected Stokes residuals instead of relying only on directstokes.stresspost-processing.Nitsche Free Slip: What Changed and Why It Matters
This PR does not change UW3's production Nitsche weak form.
The important update is diagnostic and validation support around the Nitsche path:
sigma_rrpost-processing was not sufficient for topography/geoid comparison.This separation matters because a correct velocity solve and a correct topography recovery are related but not identical checks. The benchmark now confirms that Nitsche gives the correct velocity response, then uses residual-based recovery plus Zhong Appendix A post-processing for dynamic topography and geoid.
What Changed
Mesh
SphericalShellInternalBoundary()geometry construction so the physical shell volume and the embedded internal spherical surface are preserved cleanly.radiusInner < radiusInternal < radiusOuter.Lower,Internal, andUpperboundary areas.Residual Diagnostics
void *, matching the Cython declaration and allowing all Cython extension modules to build.Regression Tests
tests/test_1064_constrained_spherical_shell_response.py.Benchmark Results
External benchmark repository:
gthyagi/uw3-mantle-convection-benchmarksBenchmark script:
benchmarks/bench_010_zhong2008_isoviscous_response.pyConfiguration:
P_l^0internal-boundary forcing;volume_residual_cbf_lumpedtopography recovery;l = 2, 5, 8, 15and depths0.25d,0.5d,0.75d.Default
l=2,0.5dCaseZhong Table 2 analytical values:
1/161.006e-021.0076094909651055e-020.16%1.186e-021.186556831000749e-020.05%4.998e-014.9577940809588633e-01-0.80%9.313e-019.156930209567329e-01-1.68%4.486e-024.376413039396167e-02-2.44%5.461e-025.265637036600948e-02-3.58%Full Table 2 Sweep, 8-rank
1/16Errors relative to Zhong Table 2 analytical values:
Interpretation:
l <= 8is reproduced well at1/16: velocity errors are below about2%, topography errors are mostly below6%, and geoid errors are mostly below8%.l = 15remains resolution-sensitive, especially for CMB topography/geoid. This is now a convergence-resolution issue rather than a free-slip or forcing-definition issue.Are All Recent Commits Essential?
For a single PR whose goal is “support Zhong-style spherical-shell benchmark reproduction”, yes, the commits are connected:
SphericalShellInternalBoundaryfixes are essential. Without them the benchmark cannot reliably integrate/apply loads onLower,Internal, andUpper.void *context-signature fix is essential because the shared compatibility header must build in all Cython extension modules.If maintainers prefer smaller PRs, this branch can still be split into two review units later:
SphericalShellInternalBoundary();Validation
Local validation in this branch:
./uw buildpassed.pixi run -e amr-dev pytest -q tests/test_0502_boundary_integrals.py::test_bd_integral_spherical_internal_boundary_areaspassed:1 passed in 4.70s.pixi run -e amr-dev pytest -q tests/test_1064_constrained_spherical_shell_response.pypassed with expected failures:2 passed, 2 xfailed in 180.38s.Representative fixed serial boundary areas at
ri=0.55,rint=0.775,ro=1.0,cellSize=0.25:Lower = 3.651738570092663;Internal = 7.402572621636933;Upper = 12.418895120898583.Before the mesh fix, the same benchmark path reported
Lower = 0.0and failed the boundary-area check.