uxarray 2026.7.0__tar.gz → 2026.8.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. {uxarray-2026.7.0 → uxarray-2026.8.0}/PKG-INFO +1 -1
  2. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/__init__.py +2 -1
  3. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/core/aggregation.py +12 -6
  4. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/core/api.py +11 -24
  5. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/core/dataarray.py +118 -98
  6. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/core/dataset.py +56 -34
  7. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/core/gradient.py +3 -2
  8. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/core/utils.py +2 -1
  9. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/core/zonal.py +3 -2
  10. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/cross_sections/dataarray_accessor.py +4 -6
  11. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/cross_sections/sample.py +2 -1
  12. uxarray-2026.8.0/uxarray/errors.py +40 -0
  13. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/grid/arcs.py +203 -0
  14. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/grid/area.py +64 -29
  15. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/grid/bounds.py +2 -2
  16. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/grid/coordinates.py +4 -4
  17. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/grid/geometry.py +0 -2
  18. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/grid/grid.py +172 -82
  19. uxarray-2026.8.0/uxarray/grid/intersections.py +772 -0
  20. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/grid/neighbors.py +7 -6
  21. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/grid/slice.py +2 -2
  22. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/grid/utils.py +1 -1
  23. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/io/_delaunay.py +3 -2
  24. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/io/_esmf.py +7 -7
  25. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/io/_healpix.py +4 -1
  26. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/io/_icon.py +3 -1
  27. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/io/_scrip.py +8 -5
  28. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/io/utils.py +4 -1
  29. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/plot/accessor.py +3 -2
  30. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/plot/matplotlib.py +6 -4
  31. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/plot/utils.py +2 -3
  32. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/remap/apply_weights.py +9 -12
  33. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/remap/bilinear.py +3 -1
  34. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/remap/spatial_coords_remap.py +8 -7
  35. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/remap/structured.py +17 -31
  36. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/remap/utils.py +5 -4
  37. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/remap/weights.py +10 -4
  38. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/remap/yac.py +12 -15
  39. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/subset/dataarray_accessor.py +10 -4
  40. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/subset/grid_accessor.py +4 -2
  41. uxarray-2026.8.0/uxarray/utils/computing.py +570 -0
  42. uxarray-2026.8.0/uxarray/utils/coords.py +54 -0
  43. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray.egg-info/PKG-INFO +1 -1
  44. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray.egg-info/SOURCES.txt +3 -1
  45. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray.egg-info/scm_file_list.json +331 -308
  46. uxarray-2026.8.0/uxarray.egg-info/scm_version.json +8 -0
  47. uxarray-2026.7.0/uxarray/grid/intersections.py +0 -452
  48. uxarray-2026.7.0/uxarray/utils/computing.py +0 -628
  49. uxarray-2026.7.0/uxarray.egg-info/scm_version.json +0 -8
  50. {uxarray-2026.7.0 → uxarray-2026.8.0}/.codecov.yml +0 -0
  51. {uxarray-2026.7.0 → uxarray-2026.8.0}/.git_archival.txt +0 -0
  52. {uxarray-2026.7.0 → uxarray-2026.8.0}/.gitattributes +0 -0
  53. {uxarray-2026.7.0 → uxarray-2026.8.0}/CITATION.cff +0 -0
  54. {uxarray-2026.7.0 → uxarray-2026.8.0}/CODE_OF_CONDUCT.md +0 -0
  55. {uxarray-2026.7.0 → uxarray-2026.8.0}/CONTRIBUTING.md +0 -0
  56. {uxarray-2026.7.0 → uxarray-2026.8.0}/INSTALLATION.md +0 -0
  57. {uxarray-2026.7.0 → uxarray-2026.8.0}/LICENSE +0 -0
  58. {uxarray-2026.7.0 → uxarray-2026.8.0}/MANIFEST.in +0 -0
  59. {uxarray-2026.7.0 → uxarray-2026.8.0}/README.md +0 -0
  60. {uxarray-2026.7.0 → uxarray-2026.8.0}/build.sh +0 -0
  61. {uxarray-2026.7.0 → uxarray-2026.8.0}/pyproject.toml +0 -0
  62. {uxarray-2026.7.0 → uxarray-2026.8.0}/setup.cfg +0 -0
  63. {uxarray-2026.7.0 → uxarray-2026.8.0}/setup.py +0 -0
  64. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/constants.py +0 -0
  65. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/conventions/__init__.py +0 -0
  66. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/conventions/descriptors.py +0 -0
  67. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/conventions/ugrid.py +0 -0
  68. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/core/__init__.py +0 -0
  69. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/core/accessors.py +0 -0
  70. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/cross_sections/__init__.py +0 -0
  71. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/cross_sections/grid_accessor.py +0 -0
  72. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/formatting_html.py +0 -0
  73. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/grid/__init__.py +0 -0
  74. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/grid/connectivity.py +0 -0
  75. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/grid/dual.py +0 -0
  76. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/grid/integrate.py +0 -0
  77. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/grid/point_in_face.py +0 -0
  78. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/grid/validation.py +0 -0
  79. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/io/__init__.py +0 -0
  80. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/io/_exodus.py +0 -0
  81. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/io/_fesom2.py +0 -0
  82. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/io/_geopandas.py +0 -0
  83. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/io/_geos.py +0 -0
  84. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/io/_mpas.py +0 -0
  85. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/io/_structured.py +0 -0
  86. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/io/_topology.py +0 -0
  87. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/io/_ugrid.py +0 -0
  88. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/io/_vertices.py +0 -0
  89. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/io/_voronoi.py +0 -0
  90. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/plot/__init__.py +0 -0
  91. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/plot/constants.py +0 -0
  92. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/remap/__init__.py +0 -0
  93. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/remap/accessor.py +0 -0
  94. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/remap/inverse_distance_weighted.py +0 -0
  95. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/remap/nearest_neighbor.py +0 -0
  96. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/subset/__init__.py +0 -0
  97. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/tutorial/__init__.py +0 -0
  98. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/tutorial/registry.py +0 -0
  99. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray/utils/__init__.py +0 -0
  100. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray.egg-info/dependency_links.txt +0 -0
  101. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray.egg-info/requires.txt +0 -0
  102. {uxarray-2026.7.0 → uxarray-2026.8.0}/uxarray.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: uxarray
3
- Version: 2026.7.0
3
+ Version: 2026.8.0
4
4
  Summary: Xarray extension for unstructured climate and global weather data analysis and visualization.
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -1,4 +1,4 @@
1
- from . import tutorial
1
+ from . import errors, tutorial
2
2
  from .constants import INT_DTYPE, INT_FILL_VALUE
3
3
  from .core.api import (
4
4
  concat,
@@ -37,4 +37,5 @@ __all__ = (
37
37
  "INT_DTYPE",
38
38
  "INT_FILL_VALUE",
39
39
  "Grid",
40
+ "errors",
40
41
  )
@@ -1,7 +1,9 @@
1
1
  import numpy as np
2
2
 
3
3
  import uxarray.core.dataarray
4
+ from uxarray.errors import DataCenteringError
4
5
  from uxarray.grid.connectivity import get_face_node_partitions
6
+ from uxarray.utils.coords import _preserve_valid_coords
5
7
 
6
8
  NUMPY_AGGREGATIONS = {
7
9
  "mean": np.mean,
@@ -32,7 +34,7 @@ def _uxda_grid_aggregate(uxda, destination, aggregation, **kwargs):
32
34
  elif destination == "edge":
33
35
  return _node_to_edge_aggregation(uxda, aggregation, kwargs)
34
36
  else:
35
- raise ValueError(
37
+ raise DataCenteringError(
36
38
  f"Invalid destination for a node-centered data variable. Expected"
37
39
  f"one of ['face', 'edge' but received {destination}"
38
40
  )
@@ -62,7 +64,7 @@ def _uxda_grid_aggregate(uxda, destination, aggregation, **kwargs):
62
64
  # raise ValueError("TODO: ")
63
65
 
64
66
  else:
65
- raise ValueError(
67
+ raise DataCenteringError(
66
68
  "Invalid data mapping. Data variable is expected to be mapped to either the "
67
69
  "nodes, faces, or edges of the source grid."
68
70
  )
@@ -73,7 +75,7 @@ def _node_to_face_aggregation(uxda, aggregation, aggregation_func_kwargs):
73
75
  import dask.array as da
74
76
 
75
77
  if not uxda._node_centered():
76
- raise ValueError(
78
+ raise DataCenteringError(
77
79
  f"Data Variable must be mapped to the corner nodes of each face, with dimension "
78
80
  f"{uxda.uxgrid.n_face}."
79
81
  )
@@ -89,12 +91,14 @@ def _node_to_face_aggregation(uxda, aggregation, aggregation_func_kwargs):
89
91
  uxda, NUMPY_AGGREGATIONS[aggregation], aggregation_func_kwargs
90
92
  )
91
93
  else:
92
- raise ValueError
94
+ raise TypeError
93
95
 
94
96
  return uxarray.core.dataarray.UxDataArray(
95
97
  uxgrid=uxda.uxgrid,
96
98
  data=aggregated_var,
97
99
  dims=uxda.dims,
100
+ coords=_preserve_valid_coords(uxda, "n_node"),
101
+ attrs=uxda.attrs,
98
102
  name=uxda.name,
99
103
  ).rename({"n_node": "n_face"})
100
104
 
@@ -141,7 +145,7 @@ def _node_to_edge_aggregation(uxda, aggregation, aggregation_func_kwargs):
141
145
  import dask.array as da
142
146
 
143
147
  if not uxda._node_centered():
144
- raise ValueError(
148
+ raise DataCenteringError(
145
149
  f"Data Variable must be mapped to the corner nodes of each face, with dimension "
146
150
  f"{uxda.uxgrid.n_face}."
147
151
  )
@@ -157,12 +161,14 @@ def _node_to_edge_aggregation(uxda, aggregation, aggregation_func_kwargs):
157
161
  uxda, NUMPY_AGGREGATIONS[aggregation], aggregation_func_kwargs
158
162
  )
159
163
  else:
160
- raise ValueError
164
+ raise TypeError
161
165
 
162
166
  return uxarray.core.dataarray.UxDataArray(
163
167
  uxgrid=uxda.uxgrid,
164
168
  data=aggregation_var,
165
169
  dims=uxda.dims,
170
+ coords=_preserve_valid_coords(uxda, "n_node"),
171
+ attrs=uxda.attrs,
166
172
  name=uxda.name,
167
173
  ).rename({"n_node": "n_edge"})
168
174
 
@@ -2,10 +2,11 @@ from __future__ import annotations
2
2
 
3
3
  import os
4
4
  from pathlib import Path
5
- from typing import TYPE_CHECKING, Any, Mapping, Sequence, TypeAlias
5
+ from typing import Any, Mapping, Sequence, TypeAlias
6
6
  from warnings import warn
7
7
 
8
8
  import numpy as np
9
+ import xarray as xr
9
10
 
10
11
  from uxarray.core.dataset import UxDataset
11
12
  from uxarray.core.utils import (
@@ -13,6 +14,7 @@ from uxarray.core.utils import (
13
14
  _open_dataset_with_fallback,
14
15
  match_chunks_to_ugrid,
15
16
  )
17
+ from uxarray.errors import GridInvalidError
16
18
  from uxarray.grid import Grid
17
19
  from uxarray.io._scrip import (
18
20
  _detect_multigrid,
@@ -22,9 +24,6 @@ from uxarray.io._scrip import (
22
24
  _stack_cell_dims,
23
25
  )
24
26
 
25
- if TYPE_CHECKING:
26
- from xarray import Dataset
27
-
28
27
  __all__ = [
29
28
  "open_grid",
30
29
  "open_multigrid",
@@ -35,7 +34,7 @@ __all__ = [
35
34
 
36
35
 
37
36
  def open_grid(
38
- grid_filename_or_obj: str | os.PathLike[Any] | dict | Dataset,
37
+ grid_filename_or_obj: str | os.PathLike[Any] | dict | xr.Dataset,
39
38
  chunks=None,
40
39
  use_dual: bool | None = False,
41
40
  **kwargs: dict[str, Any],
@@ -85,8 +84,6 @@ def open_grid(
85
84
 
86
85
  >>> uxgrid = ux.open_grid("grid_filename.nc", chunks=-1)
87
86
  """
88
- import xarray as xr
89
-
90
87
  # Handle chunk-related kwargs first.
91
88
  data_chunks = kwargs.pop("data_chunks", None)
92
89
  if data_chunks is not None:
@@ -144,9 +141,9 @@ MaskActiveValue: TypeAlias = MaskValue | Mapping[str, MaskValue] | None
144
141
 
145
142
 
146
143
  def open_multigrid(
147
- grid_filename_or_obj: str | Path | "Dataset",
144
+ grid_filename_or_obj: str | Path | xr.Dataset,
148
145
  gridnames: list[str] | None = None,
149
- mask_filename: str | Path | "Dataset" | None = None,
146
+ mask_filename: str | Path | xr.Dataset | None = None,
150
147
  mask_active_value: MaskActiveValue = 1,
151
148
  **kwargs: dict[str, Any],
152
149
  ) -> dict[str, Grid]:
@@ -175,8 +172,6 @@ def open_multigrid(
175
172
  dict[str, Grid]
176
173
  Dictionary mapping grid names to ``Grid`` objects.
177
174
  """
178
- import xarray as xr
179
-
180
175
  grid_ds_opened = False
181
176
  if isinstance(grid_filename_or_obj, xr.Dataset):
182
177
  grid_ds = grid_filename_or_obj
@@ -254,7 +249,7 @@ def open_multigrid(
254
249
  }
255
250
 
256
251
  if not grids_dict:
257
- raise ValueError(f"No grids detected in file: {grid_filename_or_obj}")
252
+ raise GridInvalidError(f"No grids detected in file: {grid_filename_or_obj}")
258
253
 
259
254
  available_grids = list(grids_dict.keys())
260
255
 
@@ -314,7 +309,7 @@ def open_multigrid(
314
309
 
315
310
 
316
311
  def list_grid_names(
317
- grid_filename_or_obj: str | Path | "Dataset", **kwargs: dict[str, Any]
312
+ grid_filename_or_obj: str | Path | xr.Dataset, **kwargs: dict[str, Any]
318
313
  ) -> list[str]:
319
314
  """List all grid names available within a grid file.
320
315
 
@@ -331,8 +326,6 @@ def list_grid_names(
331
326
  ``['grid']`` for single-grid files or the detected grid names for
332
327
  multi-grid files.
333
328
  """
334
- import xarray as xr
335
-
336
329
  grid_ds_opened = False
337
330
  if isinstance(grid_filename_or_obj, xr.Dataset):
338
331
  grid_ds = grid_filename_or_obj
@@ -352,8 +345,8 @@ def list_grid_names(
352
345
 
353
346
 
354
347
  def open_dataset(
355
- grid_filename_or_obj: str | os.PathLike[Any] | dict | Dataset,
356
- filename_or_obj: str | os.PathLike[Any] | Dataset | None = None,
348
+ grid_filename_or_obj: str | os.PathLike[Any] | dict | xr.Dataset,
349
+ filename_or_obj: str | os.PathLike[Any] | xr.Dataset | None = None,
357
350
  chunks=None,
358
351
  chunk_grid: bool = True,
359
352
  use_dual: bool | None = False,
@@ -426,8 +419,6 @@ def open_dataset(
426
419
 
427
420
  >>> ux_ds = ux.open_dataset("combined_file.nc")
428
421
  """
429
- import xarray as xr
430
-
431
422
  if grid_kwargs is None:
432
423
  grid_kwargs = {}
433
424
 
@@ -481,7 +472,7 @@ def open_dataset(
481
472
 
482
473
 
483
474
  def open_mfdataset(
484
- grid_filename_or_obj: str | os.PathLike[Any] | dict | Dataset,
475
+ grid_filename_or_obj: str | os.PathLike[Any] | dict | xr.Dataset,
485
476
  paths: str | os.PathLike,
486
477
  chunks=None,
487
478
  chunk_grid: bool = True,
@@ -546,8 +537,6 @@ def open_mfdataset(
546
537
 
547
538
  >>> ux_ds = ux.open_mfdataset("grid_filename.g", "grid_filename_vortex_*.nc")
548
539
  """
549
- import xarray as xr
550
-
551
540
  if grid_kwargs is None:
552
541
  grid_kwargs = {}
553
542
 
@@ -588,8 +577,6 @@ def _get_grid(
588
577
 
589
578
  def concat(objs, *args, **kwargs):
590
579
  # Ensure there is at least one object to concat.
591
- import xarray as xr
592
-
593
580
  if not objs:
594
581
  raise ValueError("No objects provided for concatenation.")
595
582