Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/underworld3/discretisation/discretisation_mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -1653,6 +1653,16 @@ def nuke_coords_and_rebuild(
# Invalidate projected boundary normals (rebuilt lazily on access)
self._projected_normals = None

# BUGFIX(#135): invalidate the per-cell face control-point arrays.
# These are populated lazily by _get_mesh_face_control_points, sized
# (num_faces, num_local_cells, dim). After mesh.adapt() the new mesh
# has a different cell count, so the stale arrays from the old mesh
# would be indexed with new-mesh cell IDs in
# _test_if_points_in_cells_internal — producing IndexError when the
# new cell count exceeds the old one (and silent corruption otherwise).
self.faces_outer_control_points = None
self.faces_inner_control_points = None

# BUGFIX(#130): refill the coord cache for every already-registered
# variable. Variables created before this rebuild would otherwise
# have their cache entry (from __init__) wiped above and refill
Expand Down
58 changes: 58 additions & 0 deletions tests/test_0810_amr_swarm_migration_regression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""Regression test for issue #135.

After mesh.adapt(), swarm migration into the new mesh raised IndexError in
Mesh._test_if_points_in_cells_internal because the per-cell face control-point
caches (faces_outer_control_points / faces_inner_control_points) were not
invalidated by nuke_coords_and_rebuild. Cell IDs from the new mesh would index
into stale arrays sized for the old mesh's cell count.

Reproducer reduced from the user's report (bknight1, 2026-04-23). Skipped if
the running PETSc was not built with the pragmatic backend.
"""

import pytest
import numpy as np
import sympy
Comment on lines +14 to +15

Copilot AI Apr 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused import: numpy as np is not referenced in this test.

Copilot uses AI. Check for mistakes.

import underworld3 as uw
from test_0800_optional_modules import requires_amr


pytestmark = [pytest.mark.level_1, requires_amr]


@requires_amr
def test_swarm_migration_after_adapt_does_not_raise():
"""mesh.adapt() then swarm._force_migration_after_mesh_change() must not
raise IndexError on the failing case from issue #135.
"""
mesh = uw.meshing.UnstructuredSimplexBox(
minCoords=(-1.0, 0.0),
maxCoords=(1.0, 1.0),
cellSize=0.2,
regular=False,
qdegree=3,
)

swarm = uw.swarm.Swarm(mesh=mesh)
swarm.populate(fill_param=4)

cells_before = mesh.dm.getHeightStratum(0)[1]

x, h_min, h_max, sigma = sympy.symbols("x h_min h_max sigma", positive=True)
h_expr = h_min + (h_max - h_min) * (1 - sympy.exp(-((x / sigma) ** 2)))
h_subbed = h_expr.subs({h_min: 0.05, h_max: 0.5, sigma: 0.25})
h_vals = uw.function.evaluate(h_subbed, mesh.X.coords)

metric = uw.adaptivity.create_metric(mesh, h_vals)
mesh.adapt(metric)

cells_after = mesh.dm.getHeightStratum(0)[1]
assert cells_after != cells_before, (
"test premise: mesh.adapt should change cell count for this metric"
)
Comment on lines +40 to +53

Copilot AI Apr 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mesh.dm.getHeightStratum(0) returns (cStart, cEnd) (a range of point ids), so using [1] here is not reliably the cell count (and can vary per-rank under MPI). This makes the cells_after != cells_before premise check brittle/flaky. Consider computing counts as cEnd - cStart and (if you want a global invariant) doing an MPI allreduce; if the adapt step does not change the count on a given build, prefer pytest.skip(...) over a hard assert to keep the regression focused on “no IndexError during migration”.

Copilot uses AI. Check for mistakes.

# Pre-fix: this line raises
# IndexError: index N is out of bounds for axis 1 with size M
# because faces_*_control_points kept the old (cells_before-sized) arrays.
swarm._force_migration_after_mesh_change()
Loading