Skip to content

Commit 86cf568

Browse files
committed
Initial Work
1 parent 79275c5 commit 86cf568

File tree

4 files changed

+73
-21
lines changed

4 files changed

+73
-21
lines changed

test/test_subset.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,19 +104,40 @@ def test_grid_bounding_box_subset():
104104
bbox_antimeridian[0], bbox_antimeridian[1], element=element)
105105

106106

107-
108-
109107
def test_uxda_isel():
110108
uxds = ux.open_dataset(GRID_PATHS[0], DATA_PATHS[0])
111109

112110
sub = uxds['bottomDepth'].isel(n_face=[1, 2, 3])
113111

114112
assert len(sub) == 3
115113

114+
116115
def test_uxda_isel_with_coords():
117116
uxds = ux.open_dataset(GRID_PATHS[0], DATA_PATHS[0])
118117
uxds = uxds.assign_coords({"lon_face": uxds.uxgrid.face_lon})
119118
sub = uxds['bottomDepth'].isel(n_face=[1, 2, 3])
120119

121120
assert "lon_face" in sub.coords
122121
assert len(sub.coords['lon_face']) == 3
122+
123+
124+
def test_inverse_face_indices():
125+
grid = ux.open_grid(GRID_PATHS[0])
126+
127+
# Test nearest neighbor subsetting
128+
coord = [0, 0]
129+
subset = grid.subset.nearest_neighbor(coord, k=1, element="face centers", inverse_indices=True)
130+
131+
assert subset.inverse_face_indices is not None
132+
133+
# Test bounding box subsetting
134+
box = [(-10, 10), (-10, 10)]
135+
subset = grid.subset.bounding_box(box[0], box[1], element="edge centers", inverse_indices=True)
136+
137+
assert subset.inverse_face_indices is not None
138+
139+
# Test bounding circle subsetting
140+
center_coord = [0, 0]
141+
subset = grid.subset.bounding_circle(center_coord, r=10, element="nodes", inverse_indices=True)
142+
143+
assert subset.inverse_face_indices is not None

uxarray/grid/grid.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,6 +1506,18 @@ def global_sphere_coverage(self):
15061506
(i.e. contains no holes)"""
15071507
return not self.partial_sphere_coverage
15081508

1509+
@property
1510+
def inverse_face_indices(self):
1511+
if self._is_subset:
1512+
return self._ds["inverse_face_indices"]
1513+
1514+
@property
1515+
def _is_subset(self):
1516+
"""Boolean indicator for whether the Grid is from a subset or not."""
1517+
if "_is_subset" not in self._ds:
1518+
self._ds["_is_subset"] = False
1519+
return self._ds["_is_subset"]
1520+
15091521
def chunk(self, n_node="auto", n_edge="auto", n_face="auto"):
15101522
"""Converts all arrays to dask arrays with given chunks across grid
15111523
dimensions in-place.
@@ -2201,7 +2213,7 @@ def get_dual(self):
22012213

22022214
return dual
22032215

2204-
def isel(self, **dim_kwargs):
2216+
def isel(self, inverse_indices=False, **dim_kwargs):
22052217
"""Indexes an unstructured grid along a given dimension (``n_node``,
22062218
``n_edge``, or ``n_face``) and returns a new grid.
22072219
@@ -2226,13 +2238,19 @@ def isel(self, **dim_kwargs):
22262238
raise ValueError("Indexing must be along a single dimension.")
22272239

22282240
if "n_node" in dim_kwargs:
2229-
return _slice_node_indices(self, dim_kwargs["n_node"])
2241+
return _slice_node_indices(
2242+
self, dim_kwargs["n_node"], inverse_indices=inverse_indices
2243+
)
22302244

22312245
elif "n_edge" in dim_kwargs:
2232-
return _slice_edge_indices(self, dim_kwargs["n_edge"])
2246+
return _slice_edge_indices(
2247+
self, dim_kwargs["n_edge"], inverse_indices=inverse_indices
2248+
)
22332249

22342250
elif "n_face" in dim_kwargs:
2235-
return _slice_face_indices(self, dim_kwargs["n_face"])
2251+
return _slice_face_indices(
2252+
self, dim_kwargs["n_face"], inverse_indices=inverse_indices
2253+
)
22362254

22372255
else:
22382256
raise ValueError(

uxarray/grid/slice.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
pass
1111

1212

13-
def _slice_node_indices(grid, indices, inclusive=True):
13+
def _slice_node_indices(grid, indices, inclusive=True, inverse_indices=False):
1414
"""Slices (indexes) an unstructured grid given a list/array of node
1515
indices, returning a new Grid composed of elements that contain the nodes
1616
specified in the indices.
@@ -33,10 +33,10 @@ def _slice_node_indices(grid, indices, inclusive=True):
3333
face_indices = np.unique(grid.node_face_connectivity.values[indices].ravel())
3434
face_indices = face_indices[face_indices != INT_FILL_VALUE]
3535

36-
return _slice_face_indices(grid, face_indices)
36+
return _slice_face_indices(grid, face_indices, inverse_indices=inverse_indices)
3737

3838

39-
def _slice_edge_indices(grid, indices, inclusive=True):
39+
def _slice_edge_indices(grid, indices, inclusive=True, inverse_indices=False):
4040
"""Slices (indexes) an unstructured grid given a list/array of edge
4141
indices, returning a new Grid composed of elements that contain the edges
4242
specified in the indices.
@@ -59,10 +59,10 @@ def _slice_edge_indices(grid, indices, inclusive=True):
5959
face_indices = np.unique(grid.edge_face_connectivity.values[indices].ravel())
6060
face_indices = face_indices[face_indices != INT_FILL_VALUE]
6161

62-
return _slice_face_indices(grid, face_indices)
62+
return _slice_face_indices(grid, face_indices, inverse_indices=inverse_indices)
6363

6464

65-
def _slice_face_indices(grid, indices, inclusive=True):
65+
def _slice_face_indices(grid, indices, inclusive=True, inverse_indices=False):
6666
"""Slices (indexes) an unstructured grid given a list/array of face
6767
indices, returning a new Grid composed of elements that contain the faces
6868
specified in the indices.
@@ -77,7 +77,6 @@ def _slice_face_indices(grid, indices, inclusive=True):
7777
Whether to perform inclusive (i.e. elements must contain at least one desired feature from a slice) as opposed
7878
to exclusive (i.e elements be made up all desired features from a slice)
7979
"""
80-
8180
if inclusive is False:
8281
raise ValueError("Exclusive slicing is not yet supported.")
8382

@@ -132,4 +131,9 @@ def _slice_face_indices(grid, indices, inclusive=True):
132131
# drop any conn that would require re-computation
133132
ds = ds.drop_vars(conn_name)
134133

134+
if inverse_indices:
135+
ds["inverse_face_indices"] = indices
136+
137+
ds["_is_subset"] = True
138+
135139
return Grid.from_dataset(ds, source_grid_spec=grid.source_grid_spec)

uxarray/subset/grid_accessor.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def bounding_box(
3333
lat_bounds: Union[Tuple, List, np.ndarray],
3434
element: Optional[str] = "nodes",
3535
method: Optional[str] = "coords",
36+
inverse_indices=False,
3637
**kwargs,
3738
):
3839
"""Subsets an unstructured grid between two latitude and longitude
@@ -53,6 +54,8 @@ def bounding_box(
5354
face centers, or edge centers lie within the bounds.
5455
element: str
5556
Element for use with `coords` comparison, one of `nodes`, `face centers`, or `edge centers`
57+
inverse_indices : bool
58+
Flag to indicate whether to store the original grids face indices for later use
5659
"""
5760

5861
if method == "coords":
@@ -101,11 +104,11 @@ def bounding_box(
101104
)
102105

103106
if element == "nodes":
104-
return self.uxgrid.isel(n_node=indices)
107+
return self.uxgrid.isel(inverse_indices, n_node=indices)
105108
elif element == "face centers":
106-
return self.uxgrid.isel(n_face=indices)
109+
return self.uxgrid.isel(inverse_indices, n_face=indices)
107110
elif element == "edge centers":
108-
return self.uxgrid.isel(n_edge=indices)
111+
return self.uxgrid.isel(inverse_indices, n_edge=indices)
109112

110113
else:
111114
raise ValueError(f"Method '{method}' not supported.")
@@ -115,6 +118,7 @@ def bounding_circle(
115118
center_coord: Union[Tuple, List, np.ndarray],
116119
r: Union[float, int],
117120
element: Optional[str] = "nodes",
121+
inverse_indices=False,
118122
**kwargs,
119123
):
120124
"""Subsets an unstructured grid by returning all elements within some
@@ -128,6 +132,8 @@ def bounding_circle(
128132
Radius of bounding circle (in degrees)
129133
element: str
130134
Element for use with `coords` comparison, one of `nodes`, `face centers`, or `edge centers`
135+
inverse_indices : bool
136+
Flag to indicate whether to store the original grids face indices for later use
131137
"""
132138

133139
coords = np.asarray(center_coord)
@@ -141,13 +147,14 @@ def bounding_circle(
141147
f"No elements founding within the bounding circle with radius {r} when querying {element}"
142148
)
143149

144-
return self._index_grid(ind, element)
150+
return self._index_grid(ind, element, inverse_indices)
145151

146152
def nearest_neighbor(
147153
self,
148154
center_coord: Union[Tuple, List, np.ndarray],
149155
k: int,
150156
element: Optional[str] = "nodes",
157+
inverse_indices=False,
151158
**kwargs,
152159
):
153160
"""Subsets an unstructured grid by returning the ``k`` closest
@@ -161,6 +168,8 @@ def nearest_neighbor(
161168
Number of neighbors to query
162169
element: str
163170
Element for use with `coords` comparison, one of `nodes`, `face centers`, or `edge centers`
171+
inverse_indices : bool
172+
Flag to indicate whether to store the original grids face indices for later use
164173
"""
165174

166175
coords = np.asarray(center_coord)
@@ -169,7 +178,7 @@ def nearest_neighbor(
169178

170179
_, ind = tree.query(coords, k)
171180

172-
return self._index_grid(ind, element)
181+
return self._index_grid(ind, element, inverse_indices=inverse_indices)
173182

174183
def _get_tree(self, coords, tree_type):
175184
"""Internal helper for obtaining the desired KDTree or BallTree."""
@@ -187,12 +196,12 @@ def _get_tree(self, coords, tree_type):
187196

188197
return tree
189198

190-
def _index_grid(self, ind, tree_type):
199+
def _index_grid(self, ind, tree_type, inverse_indices=False):
191200
"""Internal helper for indexing a grid with indices based off the
192201
provided tree type."""
193202
if tree_type == "nodes":
194-
return self.uxgrid.isel(n_node=ind)
203+
return self.uxgrid.isel(inverse_indices, n_node=ind)
195204
elif tree_type == "edge centers":
196-
return self.uxgrid.isel(n_edge=ind)
205+
return self.uxgrid.isel(inverse_indices, n_edge=ind)
197206
else:
198-
return self.uxgrid.isel(n_face=ind)
207+
return self.uxgrid.isel(inverse_indices, n_face=ind)

0 commit comments

Comments
 (0)