Skip to content

Commit 7c2e09d

Browse files
adacovskclaude
andcommitted
fix(geospatial): fix VertexGrid layer search and boundary point handling
Fixes two critical issues in GeospatialIndex: 1. VertexGrid layer search: Was always returning layer 0. Now properly searches through all layers using zv[lay, icell2d] for top and zv[lay+1, icell2d] for bottom elevations. 2. Boundary point handling: Points exactly on shared vertices/edges could match multiple cells. Implemented max(matching_cells) tiebreaker to match searchsorted() behavior with side="right" for consistency. Performance impact: Checks up to 5 candidates instead of returning first match, adding minimal overhead while ensuring correctness for edge cases. Fixes: - test_vertex_xyz_intersect (layer 0 vs layer 2 mismatch) - test_intersection (cell 267 vs 268 boundary point) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 0fffdeb commit 7c2e09d

File tree

1 file changed

+34
-21
lines changed

1 file changed

+34
-21
lines changed

flopy/utils/geospatial_index.py

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -388,26 +388,40 @@ def query_points(self, x, y, z=None, k=None):
388388
# Query KD-tree adaptively to get k unique cells
389389
candidates = self._get_k_unique_cells(point, k)
390390

391+
# Collect matching cells for boundary point tiebreaker
392+
matching_cells = []
393+
391394
# Test candidates in order (nearest first)
392395
for cellid in candidates:
393396
if self.is_3d:
394397
# 3D: check 3D bounding box
395398
if self._point_in_cell_3d(point, cellid):
396-
results[i] = cellid
397-
break
399+
matching_cells.append(cellid)
400+
# For performance, stop after finding enough matches
401+
if len(matching_cells) >= 5:
402+
break
398403
else:
399404
# 2D: use ConvexHull test
400405
if self._point_in_cell_vectorized(point[:2], cellid):
401-
# Found 2D cell, now check z if provided
406+
# For z-search, handle differently
402407
if z is None:
403-
results[i] = cellid
404-
break
408+
matching_cells.append(cellid)
409+
# For performance, stop after finding enough matches
410+
if len(matching_cells) >= 5:
411+
break
405412
else:
406413
# Search through layers to find the right z
407414
cell_3d = self._find_layer_for_z(cellid, z[i])
408415
if cell_3d is not None:
409-
results[i] = cell_3d
410-
break
416+
matching_cells.append(cell_3d)
417+
# For performance, stop after finding enough matches
418+
if len(matching_cells) >= 5:
419+
break
420+
421+
# If multiple cells match (boundary point), choose highest index
422+
# This matches searchsorted() behavior with side="right"
423+
if matching_cells:
424+
results[i] = max(matching_cells)
411425

412426
return results
413427

@@ -554,20 +568,19 @@ def _find_layer_for_z(self, icell2d, z):
554568

555569
# For VertexGrid: same 2D geometry for all layers
556570
if self.grid.grid_type == "vertex":
557-
# Build array of cell indices for this 2D cell across all layers
558-
# For VertexGrid, it's just the same icell2d index repeated
559-
z_top = zv[0, icell2d]
560-
z_bot = zv[1, icell2d]
561-
z_min = min(z_top, z_bot)
562-
z_max = max(z_top, z_bot)
563-
564-
# Check if z is within bounds
565-
if z_min <= z <= z_max:
566-
# Find which layer by proportional division
567-
# For now, just return first layer (layer index)
568-
# Note: VertexGrid may have varying layer thicknesses
569-
# TODO: This assumes uniform z for all layers - may need refinement
570-
return 0 if z_min <= z <= z_max else None
571+
# Search through all layers to find which contains z
572+
# For VertexGrid: zv[lay, icell2d] = top, zv[lay+1, icell2d] = bottom
573+
for lay in range(self.grid.nlay):
574+
z_top = zv[lay, icell2d]
575+
z_bot = zv[lay + 1, icell2d]
576+
z_min = min(z_top, z_bot)
577+
z_max = max(z_top, z_bot)
578+
579+
if z_min <= z <= z_max:
580+
# Found the layer! Return the layer index
581+
return lay
582+
583+
# z not found in any layer
571584
return None
572585

573586
# For UnstructuredGrid: compute 3D cell index using vectorized search

0 commit comments

Comments
 (0)