Skip to content

Commit 98f1980

Browse files
committed
remove structured
1 parent 8fdface commit 98f1980

File tree

1 file changed

+54
-99
lines changed

1 file changed

+54
-99
lines changed

flopy/utils/geospatial_index.py

Lines changed: 54 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
"""
2-
Unified geospatial indexing for FloPy grids.
2+
Geospatial indexing for FloPy vertex and unstructured grids.
33
4-
Provides efficient spatial queries for all grid types using KD-tree
5-
with cell centroids AND vertices for robust edge case handling,
6-
plus pre-computed ConvexHull equations for fast point-in-polygon testing.
4+
Provides efficient spatial queries using KD-tree with cell centroids
5+
AND vertices for robust edge case handling, plus pre-computed ConvexHull
6+
equations for fast point-in-polygon testing.
7+
8+
Note: StructuredGrid has its own optimized spatial methods and does not
9+
use this index.
710
"""
811

912
import numpy as np
@@ -12,7 +15,7 @@
1215

1316
class GeospatialIndex:
1417
"""
15-
Geospatial index for efficient geometric queries on model grids.
18+
Geospatial index for efficient geometric queries on vertex/unstructured grids.
1619
1720
Uses KD-tree indexing with cell centroids + vertices to find candidate cells,
1821
then pre-computed ConvexHull hyperplane equations for fast vectorized
@@ -22,10 +25,13 @@ class GeospatialIndex:
2225
- Points near cell boundaries
2326
- Points in thin/sliver cells where centroid is far from the cell
2427
28+
Note: StructuredGrid has its own optimized spatial methods and should not
29+
use this index.
30+
2531
Parameters
2632
----------
27-
grid : Grid
28-
Any FloPy grid object (StructuredGrid, VertexGrid, UnstructuredGrid)
33+
grid : VertexGrid or UnstructuredGrid
34+
A FloPy vertex or unstructured grid object
2935
epsilon : float, optional
3036
Tolerance for point-in-cell tests. Used for both bounding box
3137
expansion and ConvexHull hyperplane distance tests. Ensures boundary
@@ -52,10 +58,11 @@ class GeospatialIndex:
5258
5359
Examples
5460
--------
55-
>>> from flopy.discretization import StructuredGrid
61+
>>> from flopy.discretization import VertexGrid
5662
>>> from flopy.utils.geospatial_index import GeospatialIndex
5763
>>>
58-
>>> grid = StructuredGrid(delr=np.ones(10), delc=np.ones(10))
64+
>>> # Create a simple triangular grid
65+
>>> grid = VertexGrid(vertices, cell2d)
5966
>>> index = GeospatialIndex(grid)
6067
>>>
6168
>>> # Single point query
@@ -67,12 +74,12 @@ class GeospatialIndex:
6774

6875
def __init__(self, grid, epsilon=1e-3):
6976
"""
70-
Build geospatial index for a model grid.
77+
Build geospatial index for a vertex or unstructured grid.
7178
7279
Parameters
7380
----------
74-
grid : Grid
75-
Any FloPy grid (StructuredGrid, VertexGrid, UnstructuredGrid)
81+
grid : VertexGrid or UnstructuredGrid
82+
A FloPy vertex or unstructured grid
7683
epsilon : float, optional
7784
Tolerance for point-in-cell tests.
7885
Used for bounding box expansion and ConvexHull tests.
@@ -97,37 +104,37 @@ def _build_index(self):
97104
points = []
98105
point_to_cell = []
99106

100-
# Get grid dimensions
101-
if self.grid.grid_type == "structured":
102-
self.ncells = self.grid.nrow * self.grid.ncol
103-
self._build_structured_index(points, point_to_cell)
104-
elif self.grid.grid_type in ("vertex", "unstructured"):
105-
if self.is_3d:
106-
# 3D indexing: index all nnodes with x,y,z coordinates
107-
self.ncells = self.grid.nnodes
108-
self._build_3d_index(points, point_to_cell)
109-
else:
110-
# 2D indexing: index only 2D cells with x,y coordinates
111-
if hasattr(self.grid, "ncpl"):
112-
ncpl = self.grid.ncpl
113-
if ncpl is None:
114-
# ncpl not set, fall back to xcellcenters
115-
self.ncells = len(self.grid.xcellcenters)
116-
elif np.isscalar(ncpl):
117-
# VertexGrid: ncpl is scalar
118-
self.ncells = ncpl
119-
else:
120-
# UnstructuredGrid: ncpl is array
121-
# Use first layer's cell count for 2D spatial indexing
122-
if len(ncpl) > 0:
123-
self.ncells = ncpl[0]
124-
else:
125-
self.ncells = len(self.grid.xcellcenters)
126-
else:
127-
self.ncells = len(self.grid.xcellcenters)
128-
self._build_vertex_index(points, point_to_cell)
107+
# Get grid dimensions for vertex/unstructured grids
108+
if self.grid.grid_type not in ("vertex", "unstructured"):
109+
raise ValueError(
110+
f"GeospatialIndex only supports vertex and unstructured grids, "
111+
f"got: {self.grid.grid_type}"
112+
)
113+
114+
if self.is_3d:
115+
# 3D indexing: index all nnodes with x,y,z coordinates
116+
self.ncells = self.grid.nnodes
117+
self._build_3d_index(points, point_to_cell)
129118
else:
130-
raise ValueError(f"Unknown grid type: {self.grid.grid_type}")
119+
# 2D indexing: index only 2D cells with x,y coordinates
120+
if hasattr(self.grid, "ncpl"):
121+
ncpl = self.grid.ncpl
122+
if ncpl is None:
123+
# ncpl not set, fall back to xcellcenters
124+
self.ncells = len(self.grid.xcellcenters)
125+
elif np.isscalar(ncpl):
126+
# VertexGrid: ncpl is scalar
127+
self.ncells = ncpl
128+
else:
129+
# UnstructuredGrid: ncpl is array
130+
# Use first layer's cell count for 2D spatial indexing
131+
if len(ncpl) > 0:
132+
self.ncells = ncpl[0]
133+
else:
134+
self.ncells = len(self.grid.xcellcenters)
135+
else:
136+
self.ncells = len(self.grid.xcellcenters)
137+
self._build_vertex_index(points, point_to_cell)
131138

132139
# Build KD-tree from centroids + vertices
133140
self.points = np.array(points)
@@ -141,26 +148,6 @@ def _build_index(self):
141148
# For 3D, store z-bounds for each cell
142149
self._precompute_3d_bounds()
143150

144-
def _build_structured_index(self, points, point_to_cell):
145-
"""Build index for structured grid - centroids + vertices."""
146-
xc = self.grid.xcellcenters
147-
yc = self.grid.ycellcenters
148-
xv = self.grid.xvertices
149-
yv = self.grid.yvertices
150-
151-
for i in range(self.grid.nrow):
152-
for j in range(self.grid.ncol):
153-
cellid = i * self.grid.ncol + j
154-
155-
# Add centroid
156-
points.append([xc[i, j], yc[i, j]])
157-
point_to_cell.append(cellid)
158-
159-
# Add 4 corner vertices
160-
for vi, vj in [(i, j), (i, j + 1), (i + 1, j + 1), (i + 1, j)]:
161-
points.append([xv[vi, vj], yv[vi, vj]])
162-
point_to_cell.append(cellid)
163-
164151
def _build_vertex_index(self, points, point_to_cell):
165152
"""Build index for vertex/unstructured grid - centroids + vertices."""
166153
# Disable copy cache to avoid expensive deepcopy during index build
@@ -284,24 +271,8 @@ def _precompute_3d_bounds(self):
284271

285272
def _get_cell_vertices(self, cellid):
286273
"""Get vertices for a cell."""
287-
if self.grid.grid_type == "structured":
288-
i = cellid // self.grid.ncol
289-
j = cellid % self.grid.ncol
290-
xv = self.grid.xvertices
291-
yv = self.grid.yvertices
292-
return np.array(
293-
[
294-
[xv[i, j], yv[i, j]],
295-
[xv[i, j + 1], yv[i, j + 1]],
296-
[xv[i + 1, j + 1], yv[i + 1, j + 1]],
297-
[xv[i + 1, j], yv[i + 1, j]],
298-
]
299-
)
300-
elif self.grid.grid_type in ("vertex", "unstructured"):
301-
xv, yv, _ = self.grid.xyzvertices
302-
return np.column_stack([xv[cellid], yv[cellid]])
303-
else:
304-
raise ValueError(f"Unknown grid type: {self.grid.grid_type}")
274+
xv, yv, _ = self.grid.xyzvertices
275+
return np.column_stack([xv[cellid], yv[cellid]])
305276

306277
def query_point(self, x, y, z=None, k=None):
307278
"""
@@ -540,11 +511,9 @@ def _point_in_cell_3d(self, point, cellid):
540511

541512
def _apply_tiebreaker(self, matching_cells):
542513
"""
543-
Apply grid-specific tie-breaking when multiple cells match.
514+
Apply tie-breaking when multiple cells match.
544515
545-
Implements the same tie-breaking logic as the original grid implementations:
546-
- StructuredGrid: cell with lowest row, then lowest column
547-
- VertexGrid/UnstructuredGrid: cell with lowest cell ID
516+
For vertex/unstructured grids: choose cell with lowest cell ID.
548517
549518
Parameters
550519
----------
@@ -559,21 +528,7 @@ def _apply_tiebreaker(self, matching_cells):
559528
if len(matching_cells) == 1:
560529
return matching_cells[0]
561530

562-
if self.grid.grid_type == "structured":
563-
# For structured grids: choose cell with lowest row, then lowest column
564-
# Convert cell indices to (row, col) tuples for comparison
565-
rows_cols = []
566-
for cellid in matching_cells:
567-
row = cellid // self.grid.ncol
568-
col = cellid % self.grid.ncol
569-
rows_cols.append((row, col, cellid))
570-
571-
# Sort by row first, then column
572-
rows_cols.sort(key=lambda x: (x[0], x[1]))
573-
return rows_cols[0][2] # Return the cellid
574-
else:
575-
# For vertex/unstructured grids: choose cell with lowest cell ID
576-
return min(matching_cells)
531+
return min(matching_cells)
577532

578533
def _find_layer_for_z(self, icell2d, z):
579534
"""

0 commit comments

Comments
 (0)