From 6c9c1047074da3ce744327f7a6208a0270747d73 Mon Sep 17 00:00:00 2001 From: Dylan Nelson <44685561+dylannelson@users.noreply.github.com> Date: Tue, 11 Aug 2026 11:48:48 -0400 Subject: [PATCH 1/4] tested v5 of cleaning stray data Time was getting into grid data, causing issues when subsetting down the line. This should clean the data in a way that shouldn't happen in the future --- uxarray/io/_ugrid.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/uxarray/io/_ugrid.py b/uxarray/io/_ugrid.py index a66dc8c18..4d17eb5bc 100644 --- a/uxarray/io/_ugrid.py +++ b/uxarray/io/_ugrid.py @@ -4,7 +4,7 @@ import uxarray.conventions.ugrid as ugrid from uxarray.constants import INT_DTYPE, INT_FILL_VALUE from uxarray.grid.connectivity import _replace_fill_values - +from uxarray.conventions.descriptors import DESCRIPTOR_NAMES def _read_ugrid(ds): """Parses an unstructured grid dataset and encodes it in the UGRID @@ -82,8 +82,30 @@ def _read_ugrid(ds): ds = ds.swap_dims(dim_dict) + # Strip non-grid extras (e.g. a stray scalar `time` coordinate, or unrelated data variables + ds = _keep_only_grid_vars(ds) + return ds, dim_dict +def _keep_only_grid_vars(ds): + """Return ``ds`` with only recognized UGRID grid variables/coordinates. + + Anything else on the dataset (a stray scalar ``time`` coordinate, unrelated + data variables, etc.) is dropped so it cannot leak onto ``grid._ds``. + + Runs on the file-read path only + """ + # uxarray's own canonical grid-variable names (the same lists Grid filters against) + keep = {"grid_topology"} + keep.update(ugrid.SPHERICAL_COORD_NAMES) # node/edge/face lon-lat + keep.update(ugrid.CARTESIAN_COORD_NAMES) # node/edge/face x-y-z + keep.update(ugrid.CONNECTIVITY_NAMES) # face_node_connectivity, edge_node_connectivity, ... + keep.update(DESCRIPTOR_NAMES) # n_nodes_per_face, face_areas, boundary_*_indices, ... + + # drop_vars removes variables/coords by name (never bare dimensions), so grid + # dims survive with their variables; errors="ignore" tolerates absent names. + drop = [name for name in ds.variables if name not in keep] + return ds.drop_vars(drop, errors="ignore") def _encode_ugrid(ds): """Encodes an unstructured grid represented under a ``Grid`` object as a From 621b0721696e531cc88d4c6446257a239fa23819 Mon Sep 17 00:00:00 2001 From: Dylan Nelson <44685561+dylannelson@users.noreply.github.com> Date: Tue, 11 Aug 2026 15:52:28 -0400 Subject: [PATCH 2/4] reordered imports for pre-commit.ci --- uxarray/io/_ugrid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uxarray/io/_ugrid.py b/uxarray/io/_ugrid.py index 4d17eb5bc..a1ddca448 100644 --- a/uxarray/io/_ugrid.py +++ b/uxarray/io/_ugrid.py @@ -3,8 +3,8 @@ import uxarray.conventions.ugrid as ugrid from uxarray.constants import INT_DTYPE, INT_FILL_VALUE -from uxarray.grid.connectivity import _replace_fill_values from uxarray.conventions.descriptors import DESCRIPTOR_NAMES +from uxarray.grid.connectivity import _replace_fill_values def _read_ugrid(ds): """Parses an unstructured grid dataset and encodes it in the UGRID From 36031ec593a7e3a1313019b85877ad23fe0cf60f Mon Sep 17 00:00:00 2001 From: Dylan Nelson <44685561+dylannelson@users.noreply.github.com> Date: Tue, 11 Aug 2026 16:43:09 -0400 Subject: [PATCH 3/4] ran `pre-commit run ...` pre-commit run --files uxarray/io/_ugrid.py Seemed to turn up a few issues with spaces and line lengths --- uxarray/io/_ugrid.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/uxarray/io/_ugrid.py b/uxarray/io/_ugrid.py index a1ddca448..91d4b7e6a 100644 --- a/uxarray/io/_ugrid.py +++ b/uxarray/io/_ugrid.py @@ -6,6 +6,7 @@ from uxarray.conventions.descriptors import DESCRIPTOR_NAMES from uxarray.grid.connectivity import _replace_fill_values + def _read_ugrid(ds): """Parses an unstructured grid dataset and encodes it in the UGRID conventions.""" @@ -87,6 +88,7 @@ def _read_ugrid(ds): return ds, dim_dict + def _keep_only_grid_vars(ds): """Return ``ds`` with only recognized UGRID grid variables/coordinates. @@ -97,16 +99,21 @@ def _keep_only_grid_vars(ds): """ # uxarray's own canonical grid-variable names (the same lists Grid filters against) keep = {"grid_topology"} - keep.update(ugrid.SPHERICAL_COORD_NAMES) # node/edge/face lon-lat - keep.update(ugrid.CARTESIAN_COORD_NAMES) # node/edge/face x-y-z - keep.update(ugrid.CONNECTIVITY_NAMES) # face_node_connectivity, edge_node_connectivity, ... - keep.update(DESCRIPTOR_NAMES) # n_nodes_per_face, face_areas, boundary_*_indices, ... + keep.update(ugrid.SPHERICAL_COORD_NAMES) # node/edge/face lon-lat + keep.update(ugrid.CARTESIAN_COORD_NAMES) # node/edge/face x-y-z + keep.update( + ugrid.CONNECTIVITY_NAMES + ) # face_node_connectivity, edge_node_connectivity, ... + keep.update( + DESCRIPTOR_NAMES + ) # n_nodes_per_face, face_areas, boundary_*_indices, ... # drop_vars removes variables/coords by name (never bare dimensions), so grid # dims survive with their variables; errors="ignore" tolerates absent names. drop = [name for name in ds.variables if name not in keep] return ds.drop_vars(drop, errors="ignore") + def _encode_ugrid(ds): """Encodes an unstructured grid represented under a ``Grid`` object as a ``xr.Dataset`` with an updated grid topology variable.""" From 6ad226b285e920646ab250d5aa3e42c5a1dd2506 Mon Sep 17 00:00:00 2001 From: Dylan Nelson <44685561+dylannelson@users.noreply.github.com> Date: Mon, 17 Aug 2026 14:42:09 -0400 Subject: [PATCH 4/4] rewrite for fix in grid.py --- uxarray/grid/grid.py | 18 ++++++++++++++++-- uxarray/io/_ugrid.py | 29 ----------------------------- 2 files changed, 16 insertions(+), 31 deletions(-) diff --git a/uxarray/grid/grid.py b/uxarray/grid/grid.py index 3c86890f2..2638d0382 100644 --- a/uxarray/grid/grid.py +++ b/uxarray/grid/grid.py @@ -107,6 +107,18 @@ from uxarray.core.dataarray import UxDataArray +def _drop_non_grid_coords(ds): + """Drop coordinates that aren't recognized grid coordinates (e.g. a stray ``time`` + carried in from the source file). + + Coordinate-only, so grid data variables — connectivity, descriptors, and the + subset's ``subgrid_*_indices`` — are always left intact. + """ + grid_coords = set(ugrid.SPHERICAL_COORD_NAMES) | set(ugrid.CARTESIAN_COORD_NAMES) + stray = [coord for coord in ds.coords if coord not in grid_coords] + return ds.drop_vars(stray, errors="ignore") + + class Grid: """Represents a two-dimensional unstructured grid encoded following the UGRID conventions and provides grid-specific functionality. @@ -190,8 +202,10 @@ def __init__( # source grid specification (i.e. UGRID, MPAS, SCRIP, etc.) self.source_grid_spec = source_grid_spec - # internal xarray dataset for storing grid variables - self._ds = grid_ds + # internal xarray dataset for storing grid variables. + # drop stray coordinates (e.g. a `time` carried in from the source file) so they + # can't leak onto the grid and collide during subsetting (see #1444). + self._ds = _drop_non_grid_coords(grid_ds) # source grid specification (i.e. UGRID, MPAS, SCRIP, etc.) self.source_grid_spec = source_grid_spec diff --git a/uxarray/io/_ugrid.py b/uxarray/io/_ugrid.py index 91d4b7e6a..a66dc8c18 100644 --- a/uxarray/io/_ugrid.py +++ b/uxarray/io/_ugrid.py @@ -3,7 +3,6 @@ import uxarray.conventions.ugrid as ugrid from uxarray.constants import INT_DTYPE, INT_FILL_VALUE -from uxarray.conventions.descriptors import DESCRIPTOR_NAMES from uxarray.grid.connectivity import _replace_fill_values @@ -83,37 +82,9 @@ def _read_ugrid(ds): ds = ds.swap_dims(dim_dict) - # Strip non-grid extras (e.g. a stray scalar `time` coordinate, or unrelated data variables - ds = _keep_only_grid_vars(ds) - return ds, dim_dict -def _keep_only_grid_vars(ds): - """Return ``ds`` with only recognized UGRID grid variables/coordinates. - - Anything else on the dataset (a stray scalar ``time`` coordinate, unrelated - data variables, etc.) is dropped so it cannot leak onto ``grid._ds``. - - Runs on the file-read path only - """ - # uxarray's own canonical grid-variable names (the same lists Grid filters against) - keep = {"grid_topology"} - keep.update(ugrid.SPHERICAL_COORD_NAMES) # node/edge/face lon-lat - keep.update(ugrid.CARTESIAN_COORD_NAMES) # node/edge/face x-y-z - keep.update( - ugrid.CONNECTIVITY_NAMES - ) # face_node_connectivity, edge_node_connectivity, ... - keep.update( - DESCRIPTOR_NAMES - ) # n_nodes_per_face, face_areas, boundary_*_indices, ... - - # drop_vars removes variables/coords by name (never bare dimensions), so grid - # dims survive with their variables; errors="ignore" tolerates absent names. - drop = [name for name in ds.variables if name not in keep] - return ds.drop_vars(drop, errors="ignore") - - def _encode_ugrid(ds): """Encodes an unstructured grid represented under a ``Grid`` object as a ``xr.Dataset`` with an updated grid topology variable."""