Found while fixing the boundary-normal half of #564 (PR #568). Independent defect, not fixed there — marked TODO(BUG) in place.
mesh.cell_size() / _get_mesh_sizes queries a kd-tree built from this rank's cell centroids, so the per-node local mesh size depends on the partition:
|
np=1 |
np=2 |
np=4 |
| cell-size field sum |
26.0822 |
26.1211 |
26.1386 |
It reaches users through add_nitsche_bc(local_h=True) — which is the default — and the Nitsche penalty is scaled by that local size, so the solve inherits the dependence:
local_h=True (default): 6.6e-03 relative difference in velocity between rank counts, stable from solver tolerance 1e-9 through 1e-12 (so it is the discrete problem, not convergence).
local_h=False on the same case: the rank counts agree to 3.6e-10.
Same defect class as #560/#564: a per-node geometric quantity assembled from rank-local information. The fix shape is the same — complete the quantity across ranks before it is used — but the mechanism is a kd-tree rather than a facet sum, and a kd-tree cannot simply be reduced: each rank's tree is built from its own coordinates, so the nearest-centroid answer differs. It likely needs the local size derived from the mesh topology (the cell's own diameter / its facets) rather than from a spatial query, which is a design question rather than a one-line change.
Note the house guidance in CLAUDE.md and the Nitsche skill recommends local_h for Nitsche BCs, so the default is also the recommended setting.
Related: #564 (boundary normals, the sibling defect, fixed in #568), #560/#561 (the rotated-path instance).
Underworld development team with AI support from Claude Code
Found while fixing the boundary-normal half of #564 (PR #568). Independent defect, not fixed there — marked
TODO(BUG)in place.mesh.cell_size()/_get_mesh_sizesqueries a kd-tree built from this rank's cell centroids, so the per-node local mesh size depends on the partition:It reaches users through
add_nitsche_bc(local_h=True)— which is the default — and the Nitsche penalty is scaled by that local size, so the solve inherits the dependence:local_h=True(default): 6.6e-03 relative difference in velocity between rank counts, stable from solver tolerance 1e-9 through 1e-12 (so it is the discrete problem, not convergence).local_h=Falseon the same case: the rank counts agree to 3.6e-10.Same defect class as #560/#564: a per-node geometric quantity assembled from rank-local information. The fix shape is the same — complete the quantity across ranks before it is used — but the mechanism is a kd-tree rather than a facet sum, and a kd-tree cannot simply be reduced: each rank's tree is built from its own coordinates, so the nearest-centroid answer differs. It likely needs the local size derived from the mesh topology (the cell's own diameter / its facets) rather than from a spatial query, which is a design question rather than a one-line change.
Note the house guidance in CLAUDE.md and the Nitsche skill recommends
local_hfor Nitsche BCs, so the default is also the recommended setting.Related: #564 (boundary normals, the sibling defect, fixed in #568), #560/#561 (the rotated-path instance).
Underworld development team with AI support from Claude Code