Empty-rank mesh construction, tensor stats, global metric moments, swarm.points label#406
Conversation
…louds, nav index shapes empty arrays (#399) Any mesh construction where a rank owned zero cells crashed: the navigation kd-tree build passed numpy.array([]) — a 1-D array — into the KDTree memoryview, and the surviving ranks died on the resulting collective divergence (repro: StructuredQuadBox(elementRes=(2,2)) at np3/np4). Two layers, both fixed: - _build_kd_tree_index shapes its point arrays explicitly to (0, cdim) when the cell lists are empty; - KDTree itself accepts an empty (0, dim) cloud: no C++ index is built (taking &points[0][0] would be invalid), and the query methods return honest empties (found=False, infinite distances). A 1-D input now gets a legible error instead of the memoryview's buffer message. The count==0 query guard also removes the same invalid-address hazard for empty QUERY arrays against a populated tree. Regression tests: empty-cloud + 1-D-rejection in test_0101; starved-rank mesh construction at np4 in tests/parallel/test_0700 (domain integral exact). Direct repro verified at np3 and np4. Closes #399 Underworld development team with AI support from Claude Code
…p variable (#400) Two stacked defects made stats() raise for every genuine TENSOR variable: - the Frobenius accumulation indexed the flat component count (d*d) against the structured .array axis of size d (IndexError); - the temporary was created through the enhanced factory, whose __getattr__ refuses underscore-name delegation, so the _scalar_stats call could never be reached (_vector_stats already used _BaseMeshVariable). Components are now read through the flat .data layout and the temp is a _BaseMeshVariable. Regression: identity-tensor Frobenius = sqrt(d) exact, plus a repeat-call/cleanup lifecycle test (tests/test_0102). Closes #400 Underworld development team with AI support from Claude Code
…mpty-rank safe (#351) A_target/A_mean were built from rank-local sums, so the returned delta moments (rms/max/median_abs) were partition-dependent diagnostics against a per-rank equidistribution target. The sums, the cell count and the moments are now globally reduced (median via gather-to-root + broadcast — a diagnostic at adapt cadence). A rank owning zero cells — constructible since the #399 fix — participates in every reduction with empty contributions instead of raising through the tris-is-None path and desynchronising the collectives. Regression: moments identical across ranks to 1e-14 at np2 and np4 (tests/parallel/test_0750_global_statistics). Closes #351 Underworld development team with AI support from Claude Code
The deprecated swarm.points scales model coordinates by the SI length scale (metres) but labelled the result with mesh.units — kilometre-unit meshes got metre magnitudes under a km label, a 1000x label/value mismatch. Scaled values are now labelled 'meter', the same convention as mesh.X.coords; the unscaled path keeps its previous label. Regression: label + magnitude consistency under an active km-scale model (tests/test_0114). Closes #386 Underworld development team with AI support from Claude Code
…etric raise, sentinel guards, mis-route) Responses to the public adversarial review of this branch: - Tensor stats read the structured (N, d, d) array view, which mirrors symmetric storage — SYM_TENSOR off-diagonals now count twice as the Frobenius norm requires (the flat read converted the pre-existing crash into a silently under-measured norm). Validated against numpy for non-symmetric TENSOR and Voigt SYM_TENSOR fields. - mesh_metric_mismatch refuses non-simplex meshes rank-symmetrically via mesh.isSimplex (the per-rank tris check let starved ranks sail into the collective reductions while populated ranks raised). The field-valued metric limitation on starved ranks is documented in place — it needs the evaluate layer to be empty-rank safe first (#405). - The three kd-tree sentinel guards use >= (valid indices are 0..n-1), so the empty-tree sentinel trips the sanity error instead of indexing _indexMap out of bounds. - _get_domain_centroids gives starved ranks a huge finite sentinel centroid instead of NaN: gather_data silently strips NaN rows, which de-aligned the rank/row correspondence and made _route_by_nearest_centroid silently migrate particles to a rank owning zero cells (demonstrated live in the review). Finite, not inf — infinities poison kd-tree bounding boxes. - Test hygiene from the review: the swarm.points test asserts _scaled rather than hiding behind it, and drops the deprecated units= mesh kwarg. Deeper findings outside this diff are tracked as #405 (evaluate / points_in_domain / radii / gather_data NaN-stripping — the next empty-rank layer). Underworld development team with AI support from Claude Code
Adversarial review — round 1 (public record)Independent skeptic pass over the branch diff, instructed to REFUTE each fix; live probes at np2/np4 including starved-rank configurations. #399 — KDTree empty clouds
#351 — global metric moments
#400 — tensor stats
#386 — swarm.points labelHELD: all three coordinate views (points / coords / mesh.X.coords) agree physically under a 1000-km-scale model; the save path is confirmed migrated off self.points; no consumer depends on the old label. Two test-hygiene nits (deprecated kwarg, guarded assertion) fixed in the response commit. Outside the diff
Response commitad63bc9; the level_1+tier_a gate (443 passed) and the np2/np4 batteries were re-run after it. Underworld development team with AI support from Claude Code |
|
Professor @lmoresi, this adversarial review is very helpful. Since the remaining failures are in the empty-rank / collective-evaluation family tracked in #405, I will not try to modify the core evaluation path directly. One place where I may be able to help is with small validation/regression work once the fix direction is clear: running the starved-rank reproducers at np2/np4, checking macOS behaviour, and helping add or update focused tests around the empty-rank cases without touching the solver internals. I'll keep my contributions limited to small, measurable test/API/documentation fixes unless there is a specific low-risk piece you would like me to try. |
Four fixes from the triage backlog plus the public adversarial-review round and its responses. Closes #399, closes #400, closes #351, closes #386.
The fixes
#399 — mesh construction crashed on ranks owning zero cells. Two layers: the navigation kd-tree build passed a 1-D
numpy.array([])into the KDTree memoryview, and the KDTree constructor itself could not represent an empty cloud (&points[0][0]). Empty point sets are now first-class:(0, dim)clouds build no C++ index and answer queries honestly (found=False, infinite distances); 1-D input gets a legible error.StructuredQuadBox(elementRes=(2,2))now constructs, integrates and solves at np3/np4 (PETSc itself is fine with empty ranks). Follow-ups from the review, also in this PR: the three sentinel sanity guards were off-by-one (>vs>=— the empty-tree sentinel masqueraded as valid index 0), and_get_domain_centroidsproduced NaN for starved ranks, whichgather_datasilently stripped — de-aligning the rank/row table and making_route_by_nearest_centroidsilently migrate particles to a rank owning zero cells (demonstrated live in the review; now a huge-finite sentinel keeps the table aligned and the starved rank unreachable).#400 —
stats()crashed for every genuine tensor variable. Two stacked defects: the Frobenius loop indexed the flat component count against the structured array axis, and the temp variable was built through the enhanced factory whose__getattr__blocks underscore delegation. The review then caught my first fix under-measuring SYM_TENSOR norms (off-diagonals must count twice) — the final form reads the structured(N, d, d)view, correct for both tensor kinds, validated against numpy.#351 —
mesh_metric_mismatchmoments were partition-dependent. The equidistribution target and the returned moments are now globally reduced (median via gather-to-root); starved ranks participate in the reductions with empty contributions; non-simplex refusal is rank-symmetric viamesh.isSimplex. Known limit, documented in place: a metric containing MeshVariable data still cannot run with a starved rank — that needs the evaluate layer (#405).#386 — deprecated
swarm.pointslabelled metre magnitudes withmesh.units. Scaled values are now labelledmeter, themesh.X.coordsconvention; verified all three coordinate views agree physically under a km-scale model.Adversarial review (public, round 1)
Posted in full below. All four primary fixes held their core claims; the review found and demonstrated live: the SYM_TENSOR under-measurement, the silent particle mis-route, the sentinel off-by-ones, a deadlock in #351's empty path with field metrics, and — most valuable — the honest map of where empty-rank support ends: construction, solve, integrals, swarm populate and stats all work; the first
evaluate/estimate_dtstill fails (rank-asymmetric raise inget_min/max_radius). Everything in that next layer is scoped in #405 (which also ties in #314).Verification
StructuredQuadBox(elementRes=(2,2))constructs at np3 and np4 (crashed on development).Reminder for the merge: manual close needed for #399, #400, #351, #386.
Underworld development team with AI support from Claude Code