Symptom
Mesh.extract_region(...) raises AttributeError on the first call,
because the post-construction step _build_vertex_map calls
self.X._get_kdtree() — and mesh.X is a CoordinateSystem object,
not a MeshVariable.
import underworld3 as uw
full = uw.meshing.AnnulusInternalBoundary(
radiusOuter=1.0, radiusInner=0.5, radiusInternal=0.75, cellSize=0.2,
)
rock = full.extract_region("Inner") # <-- AttributeError
File ".../discretisation_mesh.py", line 1270, in extract_region
sub_mesh._build_vertex_map()
File ".../discretisation_mesh.py", line 1287, in _build_vertex_map
tree = self.X._get_kdtree()
File ".../coordinates.py", line 2034, in __getattr__
raise AttributeError(...)
AttributeError: 'CoordinateSystem' object has no attribute '_get_kdtree'
Root cause
PR #182 ("Consolidate and unify cached spatial indexing (KDTree)") unified
_get_kdtree() on MeshVariable / Swarm. The corresponding rewrite of
Mesh._build_vertex_map reads:
def _build_vertex_map(self):
...
tree = self.X._get_kdtree()
dists, indices = tree.query(self.parent.X.coords_nd, sqr_dists=False)
...
But mesh.X is self._CoordinateSystem (a CoordinateSystem instance), not a
MeshVariable. CoordinateSystem has no _get_kdtree, no coords_nd, and
its __getattr__ delegates to the underlying sympy Matrix, which doesn't
have these either — so the call fails.
extract_region is unreachable on development as a result. The MPI test
tests/parallel/test_0770_submesh_extract_mpi.py is skipped by default,
so this regression wasn't caught at PR merge time.
Suggested fix (small)
_build_vertex_map only needs a kdtree of vertex coordinates and the parent's
vertex coordinates as a query set — both available directly off the
CoordinateSystem's .coords array (with a .nondimensional() step to match
the original coords_nd semantics, or by reading mesh._coords directly).
Inline construction in the method body removes the dependence on a
non-existent CoordinateSystem method:
def _build_vertex_map(self):
if hasattr(self, "_vertex_map") and self._vertex_map is not None:
return self._vertex_map
sub_coords = self._coords # underlying non-dimensional vertex coords
parent_coords = self.parent._coords
tree = uw.kdtree.KDTree(np.array(sub_coords))
dists, indices = tree.query(np.array(parent_coords), sqr_dists=False)
matched = dists < 1.0e-10
self._vertex_map = (indices[matched], np.where(matched)[0])
return self._vertex_map
A similar issue likely affects _build_dof_map (line 1482) — sub_var._get_kdtree()
does work because sub_var is a MeshVariable, but parent_var.coords_nd is fine.
That one is OK.
Why this surfaced now
Discovered while implementing the third submesh flavour (extract_surface,
codimension-1 boundary submesh via DMPlexCreateSubmesh) under
docs/examples/submesh_investigation/. The new prototype follows the
extract_region shape and tripped over this immediately.
Workaround
In investigation code: build the vertex map inline. The PR for the surface
flavour will do that and not depend on this method until the upstream fix
lands.
Underworld development team with AI support from Claude Code
Symptom
Mesh.extract_region(...)raisesAttributeErroron the first call,because the post-construction step
_build_vertex_mapcallsself.X._get_kdtree()— andmesh.Xis aCoordinateSystemobject,not a
MeshVariable.Root cause
PR #182 ("Consolidate and unify cached spatial indexing (KDTree)") unified
_get_kdtree()onMeshVariable/Swarm. The corresponding rewrite ofMesh._build_vertex_mapreads:But
mesh.Xisself._CoordinateSystem(aCoordinateSysteminstance), not aMeshVariable.CoordinateSystemhas no_get_kdtree, nocoords_nd, andits
__getattr__delegates to the underlying sympyMatrix, which doesn'thave these either — so the call fails.
extract_regionis unreachable ondevelopmentas a result. The MPI testtests/parallel/test_0770_submesh_extract_mpi.pyis skipped by default,so this regression wasn't caught at PR merge time.
Suggested fix (small)
_build_vertex_maponly needs a kdtree of vertex coordinates and the parent'svertex coordinates as a query set — both available directly off the
CoordinateSystem's.coordsarray (with a.nondimensional()step to matchthe original
coords_ndsemantics, or by readingmesh._coordsdirectly).Inline construction in the method body removes the dependence on a
non-existent CoordinateSystem method:
A similar issue likely affects
_build_dof_map(line 1482) —sub_var._get_kdtree()does work because
sub_varis aMeshVariable, butparent_var.coords_ndis fine.That one is OK.
Why this surfaced now
Discovered while implementing the third submesh flavour (
extract_surface,codimension-1 boundary submesh via
DMPlexCreateSubmesh) underdocs/examples/submesh_investigation/. The new prototype follows theextract_regionshape and tripped over this immediately.Workaround
In investigation code: build the vertex map inline. The PR for the surface
flavour will do that and not depend on this method until the upstream fix
lands.
Underworld development team with AI support from Claude Code