From 4d9efbc4150b27962e4790e3684d023ab8a027e0 Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Sun, 19 Apr 2026 09:43:15 -0500 Subject: [PATCH 01/16] WIP --- pyproject.toml | 5 ++- src/xarray_regrid/__init__.py | 2 ++ src/xarray_regrid/regrid.py | 64 +++++++++++++++++++++++++++++++++-- 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 50ecd81..6d593ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,9 @@ accel = [ "opt-einsum", "dask[distributed]", ] +polygon = [ + "shapely>=2.0", +] benchmarking = [ "matplotlib", "zarr", @@ -74,7 +77,7 @@ docs = [ # Required for ReadTheDocs path = "src/xarray_regrid/__init__.py" [tool.hatch.envs.default] -features = ["accel", "dev", "benchmarking"] +features = ["accel", "dev", "benchmarking", "polygon"] [tool.hatch.envs.default.scripts] lint = [ diff --git a/src/xarray_regrid/__init__.py b/src/xarray_regrid/__init__.py index 0dcaaec..5ffcc0e 100644 --- a/src/xarray_regrid/__init__.py +++ b/src/xarray_regrid/__init__.py @@ -1,8 +1,10 @@ from xarray_regrid import methods +from xarray_regrid.methods.conservative_polygon import ConservativeRegridder from xarray_regrid.regrid import Regridder from xarray_regrid.utils import Grid, create_regridding_dataset __all__ = [ + "ConservativeRegridder", "Grid", "Regridder", "create_regridding_dataset", diff --git a/src/xarray_regrid/regrid.py b/src/xarray_regrid/regrid.py index b2ed389..8080207 100644 --- a/src/xarray_regrid/regrid.py +++ b/src/xarray_regrid/regrid.py @@ -4,7 +4,12 @@ import numpy as np import xarray as xr -from xarray_regrid.methods import conservative, flox_reduce, interp +from xarray_regrid.methods import ( + conservative, + conservative_polygon, + flox_reduce, + interp, +) from xarray_regrid.utils import format_for_regrid @@ -17,7 +22,9 @@ class Regridder: linear: linear, bilinear, or higher dimensional linear interpolation nearest: nearest-neighbor regridding cubic: cubic spline regridding - conservative: conservative regridding + conservative: axis-factored conservative regridding (rectilinear only) + conservative_polygon: polygon-intersection conservative regridding, + including curvilinear grids (requires shapely) most_common: most common value regridder stat: area statistics regridder """ @@ -82,6 +89,59 @@ def cubic( ds_formatted = format_for_regrid(self._obj, ds_target_grid) return interp.interp_regrid(ds_formatted, ds_target_grid, "cubic") + def conservative_polygon( + self, + ds_target_grid: xr.Dataset, + x_coord: str = "longitude", + y_coord: str = "latitude", + time_dim: str | None = "time", + skipna: bool = True, + nan_threshold: float = 1.0, + n_threads: int | None = None, + ) -> xr.DataArray | xr.Dataset: + """Polygon-intersection conservative regrid (planar geometry only). + + Unlike ``.conservative``, this path handles curvilinear grids whose + x/y coordinates are 2D arrays. Planar only: users on global lat/lon + grids should expect small area errors near the poles and across the + antimeridian. Requires ``shapely >= 2.0``. + + Args: + ds_target_grid: Dataset defining the target grid; must expose + ``x_coord`` and ``y_coord`` as coordinate variables. + x_coord: Name of the x (longitude-like) coordinate variable. + y_coord: Name of the y (latitude-like) coordinate variable. + time_dim: Name of the time dimension. Defaults to ``"time"``. Use + ``None`` to force regridding over the time dimension. + skipna: If True, propagate NaNs into the weighted mean via a + two-pass sum. + nan_threshold: Keep output cells whose valid source fraction is at + least ``nan_threshold``. + n_threads: Thread count for parallel GEOS intersection. ``None`` + auto-selects; set to ``1`` to disable threading. + + Returns: + Data regridded to the target grid. + """ + if not 0.0 <= nan_threshold <= 1.0: + msg = "nan_threshold must be between [0, 1]" + raise ValueError(msg) + + # Drop the time dim from the target if it slipped in, but skip the + # shared dim-match check: the curvilinear target's spatial dims need + # not share names with the source — they're matched by coord values. + if time_dim is not None and time_dim in ds_target_grid.coords: + ds_target_grid = ds_target_grid.isel({time_dim: 0}).reset_coords() + return conservative_polygon.polygon_conservative_regrid( + self._obj, + ds_target_grid, + x_coord=x_coord, + y_coord=y_coord, + skipna=skipna, + nan_threshold=nan_threshold, + n_threads=n_threads, + ) + def conservative( self, ds_target_grid: xr.Dataset, From 3ed200b00fd5ac244c01797a22001e1ba8b65a92 Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Mon, 27 Apr 2026 09:17:49 -0500 Subject: [PATCH 02/16] Add conservative_2d regridder for non-1D-separable grids Adds ConservativeRegridder and the .regrid.conservative_2d accessor for grids the existing 1D-factored conservative path can't express: curvilinear (2D lat/lon coords), unstructured meshes, and arbitrary polygon-to-polygon aggregation. Cell intersections go through shapely with sparse-COO weight storage, threaded above ~1k pairs, with optional spherical=True for analytic equal-area weights on lat/lon grids, antimeridian handling, and netCDF save/load for caching weight matrices. Demo notebooks under docs/notebooks/demos/ cover curvilinear, unstructured, and grid->regions aggregation. Optional dependency: install with the conservative-2d extra. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../demo_conservative_2d_curvilinear.ipynb | 190 +++ .../demos/demo_conservative_2d_regions.ipynb | 281 ++++ .../demo_conservative_2d_unstructured.ipynb | 211 +++ pyproject.toml | 19 +- src/xarray_regrid/__init__.py | 6 +- src/xarray_regrid/methods/conservative.py | 2 +- src/xarray_regrid/methods/conservative_2d.py | 1127 +++++++++++++++++ src/xarray_regrid/regrid.py | 52 +- src/xarray_regrid/utils.py | 43 +- tests/test_conservative_2d.py | 651 ++++++++++ 10 files changed, 2538 insertions(+), 44 deletions(-) create mode 100644 docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb create mode 100644 docs/notebooks/demos/demo_conservative_2d_regions.ipynb create mode 100644 docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb create mode 100644 src/xarray_regrid/methods/conservative_2d.py create mode 100644 tests/test_conservative_2d.py diff --git a/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb b/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb new file mode 100644 index 0000000..2897a5e --- /dev/null +++ b/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb @@ -0,0 +1,190 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Conservative 2D regrid — curvilinear target\n", + "\n", + "Curvilinear grids have 2D `lat(y, x)` / `lon(y, x)` coordinate arrays —\n", + "ocean models (ORCA, tripolar), rotated regional forecasts. Not\n", + "1D-separable, so `.conservative` can't handle them;\n", + "`.regrid.conservative_2d` is the tool." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import xarray as xr\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import xarray_regrid # noqa: F401\n", + "from xarray_regrid import ConservativeRegridder" + ] + }, + { + "cell_type": "markdown", + "id": "2", + "metadata": {}, + "source": [ + "## Source — regular 1° lat/lon, analytic two-bump field" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "lat = np.linspace(-60, 60, 121)\n", + "lon = np.linspace(-120, 120, 241)\n", + "Lo, La = np.meshgrid(lon, lat)\n", + "field = (\n", + " np.exp(-((Lo - 40) ** 2 + (La - 20) ** 2) / 500)\n", + " - np.exp(-((Lo + 60) ** 2 + (La + 15) ** 2) / 400)\n", + ")\n", + "src = xr.DataArray(\n", + " field,\n", + " dims=(\"latitude\", \"longitude\"),\n", + " coords={\"latitude\": lat, \"longitude\": lon},\n", + ")\n", + "src.plot(figsize=(8, 3.5), cmap=\"RdBu_r\", center=0)\n", + "plt.title(\"source: analytic two-bump field\")\n", + "plt.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "id": "4", + "metadata": {}, + "source": [ + "## Target — rotated curvilinear grid\n", + "\n", + "Coordinates ride on a `(ny, nx)` mesh, stored as 2D coordinate\n", + "variables on the target Dataset." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5", + "metadata": {}, + "outputs": [], + "source": [ + "ny, nx = 30, 50\n", + "xi, yi = np.meshgrid(\n", + " np.linspace(-110, 110, nx),\n", + " np.linspace(-45, 45, ny),\n", + " indexing=\"xy\",\n", + ")\n", + "th = np.deg2rad(30)\n", + "lon2d = xi * np.cos(th) - yi * np.sin(th)\n", + "lat2d = xi * np.sin(th) + yi * np.cos(th)\n", + "target = xr.Dataset(coords={\n", + " \"longitude\": ((\"ny\", \"nx\"), lon2d),\n", + " \"latitude\": ((\"ny\", \"nx\"), lat2d),\n", + "})\n", + "\n", + "fig, ax = plt.subplots(figsize=(8, 4))\n", + "ax.plot(lon2d, lat2d, color=\"0.3\", lw=0.4)\n", + "ax.plot(lon2d.T, lat2d.T, color=\"0.3\", lw=0.4)\n", + "ax.set_title(\"curvilinear target (30° rotation)\")\n", + "ax.set_xlabel(\"longitude\"); ax.set_ylabel(\"latitude\")\n", + "ax.set_aspect(\"equal\")" + ] + }, + { + "cell_type": "markdown", + "id": "6", + "metadata": {}, + "source": [ + "## Regrid and plot\n", + "\n", + "For the one-shot accessor, use `src.regrid.conservative_2d(target, ...)`.\n", + "Constructing the class directly (as below) is equivalent but lets us reuse\n", + "the weight matrix for both the apply and the diagnostic in the next cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "rgr = ConservativeRegridder(\n", + " src, target, x_coord=\"longitude\", y_coord=\"latitude\",\n", + ")\n", + "regridded = rgr.regrid(src)\n", + "\n", + "fig, ax = plt.subplots(figsize=(8, 4))\n", + "pc = ax.pcolormesh(lon2d, lat2d, regridded.values, cmap=\"RdBu_r\",\n", + " shading=\"auto\", vmin=-1, vmax=1)\n", + "fig.colorbar(pc, ax=ax, shrink=0.8)\n", + "ax.set_title(\"regridded onto rotated curvilinear grid\")\n", + "ax.set_xlabel(\"longitude\"); ax.set_ylabel(\"latitude\")\n", + "ax.set_aspect(\"equal\")" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": {}, + "source": [ + "## Conservation check\n", + "\n", + "For the target cells that fall within the source domain, the\n", + "area-weighted sum of outputs equals the direct A·s integral to\n", + "machine precision." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "A = rgr._areas\n", + "src_cover = np.ravel(A.sum(axis=0).todense())\n", + "tgt_cover = A.sum(axis=1).todense().reshape(regridded.shape)\n", + "valid = np.isfinite(regridded.values)\n", + "\n", + "direct = float((src.values.ravel() * src_cover).sum())\n", + "via_regrid = float((regridded.values[valid] * tgt_cover[valid]).sum())\n", + "print(f\"direct : {direct:.6f}\")\n", + "print(f\"via regrid : {via_regrid:.6f}\")\n", + "print(f\"relative err : {abs(direct - via_regrid) / max(abs(direct), 1e-12):.2e}\")\n", + "print(f\"coverage : {valid.mean():.2%} of target cells inside source\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/notebooks/demos/demo_conservative_2d_regions.ipynb b/docs/notebooks/demos/demo_conservative_2d_regions.ipynb new file mode 100644 index 0000000..1a837b3 --- /dev/null +++ b/docs/notebooks/demos/demo_conservative_2d_regions.ipynb @@ -0,0 +1,281 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Conservative 2D regrid — regions (grid → arbitrary polygons)\n", + "\n", + "Gridded data → arbitrary polygon regions (countries, watersheds, ocean\n", + "basins, protected areas) is a canonical xagg-style workflow. Because\n", + "regions aren't a grid at all, `.conservative` can't express it;\n", + "`ConservativeRegridder.from_polygons` takes a numpy array of shapely\n", + "polygons and produces one conservatively-averaged value per region.\n", + "\n", + "This notebook uses hand-built synthetic regions so it runs with zero\n", + "external downloads — swap in `geopandas.read_file(...)` for a real\n", + "shapefile and the rest is identical." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import xarray as xr\n", + "import matplotlib.pyplot as plt\n", + "from matplotlib.collections import PolyCollection\n", + "import shapely\n", + "\n", + "import xarray_regrid # noqa: F401\n", + "from xarray_regrid import ConservativeRegridder, polygons_from_coords" + ] + }, + { + "cell_type": "markdown", + "id": "2", + "metadata": {}, + "source": [ + "## Source — structured lat/lon field\n", + "\n", + "A smooth analytic field with recognizable geographic pattern: a warm\n", + "band following the equator modulated by an east/west tilt. Gives visibly\n", + "different regional means depending on region location." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "lat = np.linspace(-80, 80, 160) + 0.5\n", + "lon = np.linspace(-180, 180, 360, endpoint=False) + 0.5\n", + "Lo, La = np.meshgrid(lon, lat)\n", + "field = np.cos(np.deg2rad(La)) ** 2 + 0.4 * np.sin(np.deg2rad(Lo))\n", + "src = xr.DataArray(\n", + " field,\n", + " dims=(\"latitude\", \"longitude\"),\n", + " coords={\"latitude\": lat, \"longitude\": lon},\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "4", + "metadata": {}, + "source": [ + "## Regions — five hand-built polygons of varied shape\n", + "\n", + "Box, circle, rotated rectangle, L-shape, and a polygon with a hole —\n", + "exercising the range of geometry the regridder accepts. These overlap\n", + "in places, which is fine: each region gets its own independent\n", + "area-weighted mean." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5", + "metadata": {}, + "outputs": [], + "source": [ + "def rotated_rect(cx, cy, w, h, angle_deg):\n", + " theta = np.deg2rad(angle_deg)\n", + " c = np.array([[-w/2, -h/2], [w/2, -h/2], [w/2, h/2], [-w/2, h/2]])\n", + " R = np.array([[np.cos(theta), -np.sin(theta)],\n", + " [np.sin(theta), np.cos(theta)]])\n", + " return shapely.Polygon(c @ R.T + np.array([cx, cy]))\n", + "\n", + "def circle(cx, cy, r, n=48):\n", + " t = np.linspace(0, 2 * np.pi, n, endpoint=False)\n", + " return shapely.Polygon(np.column_stack([cx + r*np.cos(t), cy + r*np.sin(t)]))\n", + "\n", + "region_names = [\n", + " \"equatorial band\",\n", + " \"northern island\",\n", + " \"rotated block\",\n", + " \"L-shape\",\n", + " \"ring\",\n", + "]\n", + "region_polys = np.array([\n", + " shapely.box(-180, -15, 180, 15),\n", + " circle(cx=-60, cy=50, r=18),\n", + " rotated_rect(cx=80, cy=40, w=50, h=25, angle_deg=30),\n", + " shapely.unary_union([\n", + " shapely.box(-140, -60, -100, -20),\n", + " shapely.box(-140, -60, -60, -50),\n", + " ]),\n", + " shapely.Polygon(\n", + " shell=[(140, -40), (170, -40), (170, -5), (140, -5)],\n", + " holes=[[(148, -30), (162, -30), (162, -12), (148, -12)]],\n", + " ),\n", + "], dtype=object)\n", + "print(f\"{len(region_polys)} regions, areas (deg²): \"\n", + " f\"{[f'{shapely.area(p):.0f}' for p in region_polys]}\")" + ] + }, + { + "cell_type": "markdown", + "id": "6", + "metadata": {}, + "source": [ + "## Build the regridder and apply\n", + "\n", + "`from_polygons` takes source + target polygon arrays. We build the\n", + "source polygons from the grid's 1D coords via the `polygons_from_coords`\n", + "helper; the data gets flattened to match." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "src_polys = polygons_from_coords(lon, lat)\n", + "\n", + "rgr = ConservativeRegridder.from_polygons(\n", + " source_polygons=src_polys,\n", + " target_polygons=region_polys,\n", + " source_dim=\"src_cell\",\n", + " target_dim=\"region\",\n", + " target_coords=xr.Dataset(coords={\"region\": region_names}),\n", + ")\n", + "src_flat = xr.DataArray(src.values.ravel(), dims=(\"src_cell\",))\n", + "regional = rgr.regrid(src_flat)\n", + "regional" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": {}, + "source": [ + "## Source field + region outlines + regional means" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "fig, (ax_map, ax_bar) = plt.subplots(\n", + " 1, 2, figsize=(13, 4.5), gridspec_kw={\"width_ratios\": [1.6, 1]},\n", + ")\n", + "src.plot(ax=ax_map, cmap=\"viridis\", add_colorbar=True,\n", + " cbar_kwargs={\"shrink\": 0.7, \"label\": \"source field\"})\n", + "patches = [np.asarray(p.exterior.coords) for p in region_polys]\n", + "pc = PolyCollection(\n", + " patches, facecolor=\"none\", edgecolor=\"white\", lw=1.4,\n", + ")\n", + "ax_map.add_collection(pc)\n", + "for name, poly in zip(region_names, region_polys):\n", + " c = poly.representative_point()\n", + " ax_map.annotate(name, (c.x, c.y), color=\"white\", fontsize=8,\n", + " ha=\"center\", va=\"center\")\n", + "ax_map.set_title(\"source + region outlines\")\n", + "ax_map.set_xlim(-180, 180); ax_map.set_ylim(-80, 80)\n", + "\n", + "ax_bar.barh(region_names, regional.values, color=\"tab:blue\")\n", + "ax_bar.set_xlabel(\"area-weighted regional mean\")\n", + "ax_bar.invert_yaxis()\n", + "ax_bar.grid(axis=\"x\", alpha=0.3)\n", + "plt.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "id": "10", + "metadata": {}, + "source": [ + "## Conservation check\n", + "\n", + "Area-weighted sum of regional means × region areas should match the\n", + "direct A·s computation from the regridder's internal area matrix." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11", + "metadata": {}, + "outputs": [], + "source": [ + "A = rgr._areas # sparse (n_regions, n_src)\n", + "tgt_area = np.ravel(A.sum(axis=1).todense())\n", + "src_cover = np.ravel(A.sum(axis=0).todense())\n", + "\n", + "direct = float((src.values.ravel() * src_cover).sum())\n", + "via_regrid = float((regional.values * tgt_area).sum())\n", + "print(f\"direct A·s : {direct:.6f}\")\n", + "print(f\"Σ regional_mean · a_dst : {via_regrid:.6f}\")\n", + "print(f\"relative error : {abs(direct - via_regrid) / abs(direct):.2e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "12", + "metadata": {}, + "source": [ + "## Reuse: persist the regridder to netCDF\n", + "\n", + "The weight matrix is reusable. For any workflow that repeatedly\n", + "aggregates new source data onto the same region set, save once,\n", + "reload on subsequent runs to skip the intersection build." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "import tempfile\n", + "from pathlib import Path\n", + "path = Path(tempfile.gettempdir()) / \"regions_regridder.nc\"\n", + "rgr.to_netcdf(path)\n", + "print(f\"wrote {path} ({path.stat().st_size / 1024:.1f} KB)\")\n", + "\n", + "rgr2 = ConservativeRegridder.from_netcdf(path)\n", + "# Apply to a different source field, same grid:\n", + "other = xr.DataArray(\n", + " (np.sin(np.deg2rad(Lo)) ** 2).ravel(), dims=(\"src_cell\",),\n", + ")\n", + "print(\"regional means on a different field:\")\n", + "for n, v in zip(region_names, rgr2.regrid(other).values):\n", + " print(f\" {n:20s}: {v:+.4f}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb b/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb new file mode 100644 index 0000000..829147a --- /dev/null +++ b/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb @@ -0,0 +1,211 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Conservative 2D regrid — unstructured mesh + save/load\n", + "\n", + "Unstructured meshes (ICON triangles, MPAS hexagons, finite-element\n", + "models) are never 1D-separable. `ConservativeRegridder.from_polygons`\n", + "takes any flat array of shapely polygons as source or target.\n", + "\n", + "This notebook builds a synthetic Voronoi hex-like mesh and regrids a\n", + "structured source onto it, then persists the regridder so a\n", + "long-running pipeline can skip the weight build on restart." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1", + "metadata": {}, + "outputs": [], + "source": [ + "import tempfile\n", + "from pathlib import Path\n", + "\n", + "import numpy as np\n", + "import xarray as xr\n", + "import matplotlib.pyplot as plt\n", + "from matplotlib.collections import PolyCollection\n", + "from scipy.spatial import Voronoi\n", + "import shapely\n", + "\n", + "import xarray_regrid # noqa: F401\n", + "from xarray_regrid import ConservativeRegridder, polygons_from_coords" + ] + }, + { + "cell_type": "markdown", + "id": "2", + "metadata": {}, + "source": [ + "## Build a Voronoi mesh\n", + "\n", + "Jittered grid points → Voronoi → clip to the region of interest. Real\n", + "pipelines load a pre-built mesh (UGRID, ICON, etc.); the point here is\n", + "that `from_polygons` needs only a 1D array of shapely polygons." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "def voronoi_mesh(n_points, bbox, seed=0):\n", + " rng = np.random.default_rng(seed)\n", + " x0, y0, x1, y1 = bbox\n", + " side = int(np.sqrt(n_points))\n", + " xs, ys = np.linspace(x0, x1, side), np.linspace(y0, y1, side)\n", + " pts = np.column_stack([np.repeat(xs, side), np.tile(ys, side)])\n", + " pts += rng.normal(scale=(x1 - x0) / side * 0.25, size=pts.shape)\n", + " halo = np.array([\n", + " [2*x0 - x1, 2*y0 - y1], [2*x1 - x0, 2*y0 - y1],\n", + " [2*x0 - x1, 2*y1 - y0], [2*x1 - x0, 2*y1 - y0],\n", + " ])\n", + " vor = Voronoi(np.concatenate([pts, halo]))\n", + " clip = shapely.box(x0, y0, x1, y1)\n", + " polys = []\n", + " for i in range(len(pts)):\n", + " r = vor.regions[vor.point_region[i]]\n", + " if not r or -1 in r:\n", + " continue\n", + " p = shapely.intersection(shapely.Polygon(vor.vertices[r]), clip)\n", + " if p.is_empty or p.geom_type != \"Polygon\":\n", + " continue\n", + " polys.append(p)\n", + " return np.array(polys, dtype=object)\n", + "\n", + "bbox = (-120, -50, 120, 50)\n", + "mesh_polys = voronoi_mesh(n_points=400, bbox=bbox)\n", + "print(f\"{len(mesh_polys)} mesh cells\")" + ] + }, + { + "cell_type": "markdown", + "id": "4", + "metadata": {}, + "source": [ + "## Structured lat/lon source" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5", + "metadata": {}, + "outputs": [], + "source": [ + "lat_s = np.linspace(-50, 50, 100, endpoint=False) + 0.5\n", + "lon_s = np.linspace(-120, 120, 240, endpoint=False) + 0.5\n", + "Lo, La = np.meshgrid(lon_s, lat_s)\n", + "src = xr.DataArray(\n", + " np.sin(np.deg2rad(Lo) * 2) * np.cos(np.deg2rad(La) * 3),\n", + " dims=(\"latitude\", \"longitude\"),\n", + " coords={\"latitude\": lat_s, \"longitude\": lon_s},\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "6", + "metadata": {}, + "source": [ + "## Regrid onto the mesh" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "rgr = ConservativeRegridder.from_polygons(\n", + " source_polygons=polygons_from_coords(lon_s, lat_s),\n", + " target_polygons=mesh_polys,\n", + " source_dim=\"src_cell\",\n", + " target_dim=\"cell\",\n", + ")\n", + "print(rgr)\n", + "\n", + "src_flat = xr.DataArray(src.values.ravel(), dims=(\"src_cell\",))\n", + "mesh_vals = rgr.regrid(src_flat)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(figsize=(8, 4))\n", + "patches = [np.asarray(p.exterior.coords) for p in mesh_polys]\n", + "pc = PolyCollection(patches, array=mesh_vals.values, cmap=\"RdBu_r\",\n", + " edgecolor=\"0.4\", lw=0.3, clim=(-1, 1))\n", + "ax.add_collection(pc)\n", + "ax.set_xlim(bbox[0], bbox[2]); ax.set_ylim(bbox[1], bbox[3])\n", + "ax.set_aspect(\"equal\")\n", + "fig.colorbar(pc, ax=ax, shrink=0.8)\n", + "ax.set_title(f\"regridded onto {len(mesh_polys)}-cell Voronoi mesh\")" + ] + }, + { + "cell_type": "markdown", + "id": "9", + "metadata": {}, + "source": [ + "## Persist the regridder\n", + "\n", + "For a fixed source/target pair, the weight matrix is the same forever.\n", + "Saving it lets long-running pipelines skip the (expensive) build on\n", + "restart." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "path = Path(tempfile.gettempdir()) / \"mesh_regridder.nc\"\n", + "rgr.to_netcdf(path)\n", + "\n", + "with xr.open_dataset(path) as weights:\n", + " for k in (\"xarray_regrid_version\", \"created\", \"src_shape\", \"dst_shape\"):\n", + " print(f\" {k}: {weights.attrs[k]}\")\n", + "\n", + "rgr2 = ConservativeRegridder.from_netcdf(path)\n", + "same = np.array_equal(rgr.regrid(src_flat).values, rgr2.regrid(src_flat).values)\n", + "print(f\"\\nreload bit-identical: {same}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/pyproject.toml b/pyproject.toml index 6d593ac..89501a2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,8 +44,12 @@ accel = [ "opt-einsum", "dask[distributed]", ] -polygon = [ +conservative-2d = [ + # For conservative regridding on grids that aren't 1D-separable + # (curvilinear, unstructured). Uses shapely for 2D polygon intersection. "shapely>=2.0", + # Needed by ConservativeRegridder.to_netcdf / from_netcdf (group support). + "h5netcdf", ] benchmarking = [ "matplotlib", @@ -77,7 +81,7 @@ docs = [ # Required for ReadTheDocs path = "src/xarray_regrid/__init__.py" [tool.hatch.envs.default] -features = ["accel", "dev", "benchmarking", "polygon"] +features = ["accel", "dev", "benchmarking", "conservative-2d"] [tool.hatch.envs.default.scripts] lint = [ @@ -188,3 +192,14 @@ warn_return_any = true warn_unused_ignores = true show_error_codes = true exclude = ["tests/*", "docs"] + +# shapely and sparse are untyped; the conservative_2d module bridges to them, +# so treating every signature that mentions sparse.COO as an error is noise. +[[tool.mypy.overrides]] +module = "xarray_regrid.methods.conservative_2d" +disallow_any_unimported = false +warn_return_any = false + +[[tool.mypy.overrides]] +module = ["shapely", "shapely.*", "sparse"] +ignore_missing_imports = true diff --git a/src/xarray_regrid/__init__.py b/src/xarray_regrid/__init__.py index 5ffcc0e..61d94c0 100644 --- a/src/xarray_regrid/__init__.py +++ b/src/xarray_regrid/__init__.py @@ -1,5 +1,8 @@ from xarray_regrid import methods -from xarray_regrid.methods.conservative_polygon import ConservativeRegridder +from xarray_regrid.methods.conservative_2d import ( + ConservativeRegridder, + polygons_from_coords, +) from xarray_regrid.regrid import Regridder from xarray_regrid.utils import Grid, create_regridding_dataset @@ -9,6 +12,7 @@ "Regridder", "create_regridding_dataset", "methods", + "polygons_from_coords", ] __version__ = "0.4.2" diff --git a/src/xarray_regrid/methods/conservative.py b/src/xarray_regrid/methods/conservative.py index 2ab67d9..e8bf6f3 100644 --- a/src/xarray_regrid/methods/conservative.py +++ b/src/xarray_regrid/methods/conservative.py @@ -7,7 +7,7 @@ import xarray as xr try: - import sparse # type: ignore + import sparse except ImportError: sparse = None diff --git a/src/xarray_regrid/methods/conservative_2d.py b/src/xarray_regrid/methods/conservative_2d.py new file mode 100644 index 0000000..96264f0 --- /dev/null +++ b/src/xarray_regrid/methods/conservative_2d.py @@ -0,0 +1,1127 @@ +"""Conservative regridding for grids that aren't 1D-separable. + +The existing ``conservative`` method uses axis-factored 1D overlap — fast and +elegant but strictly rectilinear. This module computes the full 2D cell +intersection via shapely, so it handles: + +- curvilinear grids (2D ``lat[i, j]`` / ``lon[i, j]`` coordinate variables) +- unstructured meshes (arbitrary polygon cells, via + :meth:`ConservativeRegridder.from_polygons`) +- grid-to-polygon aggregation (e.g. gridded data → country shapes) + +For rectilinear grids a cheap analytic fast-path is used, but this module is +still slower and more memory-intensive than ``conservative``; prefer the +axis-factored path when your grid is 1D-separable. + +Requires ``shapely >= 2.0``. If ``sparse`` is available, the weight matrix is +stored as ``sparse.COO``; otherwise a dense numpy matrix is used. +""" + +import os +import warnings +from collections.abc import Hashable +from concurrent.futures import ThreadPoolExecutor +from dataclasses import dataclass +from datetime import datetime, timezone +from functools import cached_property +from importlib.metadata import PackageNotFoundError, version +from pathlib import Path +from typing import Any, Literal, cast + +import numpy as np +import xarray as xr + +from xarray_regrid import utils +from xarray_regrid.methods.conservative import get_valid_threshold + +NetcdfEngine = Literal["netcdf4", "scipy", "h5netcdf"] | None + + +def _package_version() -> str: + try: + return version("xarray-regrid") + except PackageNotFoundError: + return "unknown" + + +# Bump on breaking change to the on-disk format in ConservativeRegridder.to_netcdf. +_SCHEMA_VERSION = 1 + + +try: + import shapely + from shapely import affinity + from shapely.strtree import STRtree + + _HAS_SHAPELY = True +except ImportError: # pragma: no cover + shapely = None + affinity = None + STRtree = None + _HAS_SHAPELY = False + +try: + import sparse + + _HAS_SPARSE = True +except ImportError: # pragma: no cover + sparse = None + _HAS_SPARSE = False + + +# We fill NaNs with 0 ourselves before matmul (see `_apply_core`), so sparse's +# "NaN will not be propagated" warning is spurious. A module-level filter +# avoids `warnings.catch_warnings` inside the hot matmul path, which is not +# thread-safe — dask threads would race on the global `warnings.filters` list. +warnings.filterwarnings( + "ignore", + message="Nan will not be propagated in matrix multiplication", + category=RuntimeWarning, +) + + +SHAPELY_IMPORT_ERROR = ( + "polygon conservative regridding requires shapely >= 2.0; " + "install with `pip install shapely`." +) + + +def _check_shapely() -> None: + if not _HAS_SHAPELY: + raise ImportError(SHAPELY_IMPORT_ERROR) + + +class _Direction: + """Lazy weights, apply matrix, and coverage mask for one regrid direction. + + Holds the raw ``(n_dst, n_src)`` area matrix in this direction's orientation + and derives, on first access: + + - ``weights``: row-normalized weight matrix + - ``apply_matrix``: pre-transposed and index-sorted weights, so + ``_apply_core``'s matmul is ``(..., n_src) @ (n_src, n_dst)`` with no + per-call sort + - ``coverage`` / ``coverage_all``: which output cells have any source overlap + + A regridder holds two of these (forward, backward); transposing the + regridder swaps them with no recomputation. + """ + + def __init__(self, areas: "sparse.COO | np.ndarray") -> None: + self.areas = areas + + @cached_property + def weights(self) -> "sparse.COO | np.ndarray": + return _row_normalize(self.areas) + + @cached_property + def apply_matrix(self) -> "sparse.COO | np.ndarray": + return _transpose_weights(self.weights, sort=True) + + @cached_property + def coverage(self) -> np.ndarray: + return _coverage_mask(self.areas) + + @cached_property + def coverage_all(self) -> bool: + return bool(self.coverage.all()) + + +class ConservativeRegridder: + """Reusable conservative regridder for grids that aren't 1D-separable: + curvilinear (2D ``lat``/``lon``), unstructured (via :meth:`from_polygons`), + or arbitrary polygon-to-polygon aggregation. For purely 1D-separable + rectilinear grids, the ``.regrid.conservative`` accessor is faster. + + Build once, apply to many fields via :meth:`regrid` (or by calling the + regridder); ``.T`` gives the backward regridder. Forward and backward + weight matrices are cached lazily. Requires ``shapely >= 2.0``. + """ + + def __init__( + self, + source: xr.DataArray | xr.Dataset, + target: xr.Dataset, + x_coord: str = "longitude", + y_coord: str = "latitude", + spherical: bool = False, + n_threads: int | None = None, + ) -> None: + _check_shapely() + source_grid, target_grid = _normalize_longitude_coords(source, target, x_coord) + src_dims = _spatial_dims(source_grid, x_coord, y_coord) + dst_dims = _spatial_dims(target_grid, x_coord, y_coord) + if not src_dims: + msg = f"source has no dims for coords {x_coord!r}, {y_coord!r}" + raise ValueError(msg) + if not dst_dims: + msg = f"target has no dims for coords {x_coord!r}, {y_coord!r}" + raise ValueError(msg) + src_grid = _grid_from_coords( + source_grid, x_coord, y_coord, src_dims, spherical=spherical + ) + dst_grid = _grid_from_coords( + target_grid, x_coord, y_coord, dst_dims, spherical=spherical + ) + self.spherical = spherical + + self.x_coord = x_coord + self.y_coord = y_coord + self._src_dims = src_dims + self._dst_dims = dst_dims + self._src_shape = tuple(int(source.sizes[d]) for d in src_dims) + self._dst_shape = tuple(int(target.sizes[d]) for d in dst_dims) + self._areas = _build_intersection_areas(src_grid, dst_grid, n_threads=n_threads) + self._source_coords = source.coords.to_dataset() + self._target_coords = target.coords.to_dataset() + + @cached_property + def _forward(self) -> _Direction: + return _Direction(self._areas) + + @cached_property + def _backward(self) -> _Direction: + return _Direction(_transpose_weights(self._areas)) + + @property + def forward_weights(self) -> "sparse.COO | np.ndarray": + """The row-normalized forward weight matrix (source → target).""" + return self._forward.weights + + @property + def backward_weights(self) -> "sparse.COO | np.ndarray": + """The row-normalized backward weight matrix (target → source).""" + return self._backward.weights + + def regrid( + self, + data: xr.DataArray | xr.Dataset, + skipna: bool = True, + nan_threshold: float = 1.0, + ) -> xr.DataArray | xr.Dataset: + """Regrid ``data`` forward (source → target).""" + return _apply_stored_weights( + data, + direction=self._forward, + src_dims=self._src_dims, + dst_dims=self._dst_dims, + src_shape=self._src_shape, + dst_shape=self._dst_shape, + target_coords=self._target_coords, + x_coord=self.x_coord, + y_coord=self.y_coord, + skipna=skipna, + nan_threshold=nan_threshold, + ) + + def __call__( + self, + data: xr.DataArray | xr.Dataset, + skipna: bool = True, + nan_threshold: float = 1.0, + ) -> xr.DataArray | xr.Dataset: + return self.regrid(data, skipna=skipna, nan_threshold=nan_threshold) + + def transpose(self) -> "ConservativeRegridder": + """Return the backward regridder (target → source). The original's + forward Direction becomes the new's backward (and vice versa), so any + already-computed weight matrices are reused, not recomputed.""" + new = type(self)._from_state( + areas=_transpose_weights(self._areas), + source_coords=self._target_coords, + target_coords=self._source_coords, + src_dims=self._dst_dims, + dst_dims=self._src_dims, + src_shape=self._dst_shape, + dst_shape=self._src_shape, + x_coord=self.x_coord, + y_coord=self.y_coord, + spherical=self.spherical, + ) + if "_forward" in self.__dict__: + new.__dict__["_backward"] = self.__dict__["_forward"] + if "_backward" in self.__dict__: + new.__dict__["_forward"] = self.__dict__["_backward"] + return new + + @property + def T(self) -> "ConservativeRegridder": # noqa: N802 + """Alias for :meth:`transpose` (numpy-style transpose).""" + return self.transpose() + + def __repr__(self) -> str: + nnz = getattr(self._areas, "nnz", None) + shape = getattr(self._areas, "shape", (None, None)) + nnz_str = f"nnz={nnz}" if nnz is not None else "dense" + return ( + f"ConservativeRegridder(src_dims={self._src_dims}, " + f"dst_dims={self._dst_dims}, {shape[0]}x{shape[1]}, {nnz_str})" + ) + + def to_netcdf(self, path: str | Path, engine: NetcdfEngine = None) -> None: + """Save the weight matrix and reproducibility metadata to a netCDF file. + Requires a group-aware engine (``netcdf4`` or ``h5netcdf``); ``engine`` + is forwarded to :func:`xarray.Dataset.to_netcdf`.""" + path = Path(path) + row, col, data, shape = _coo_components(self._areas) + ds_weights = xr.Dataset( + { + "_coo_row": (("nnz",), row), + "_coo_col": (("nnz",), col), + "_coo_data": (("nnz",), data), + }, + attrs={ + **_metadata_attrs(self), + "n_dst": int(shape[0]), + "n_src": int(shape[1]), + }, + ) + ds_weights.to_netcdf(path, mode="w", engine=engine) + self._source_coords.to_netcdf( + path, mode="a", group="source_coords", engine=engine + ) + self._target_coords.to_netcdf( + path, mode="a", group="target_coords", engine=engine + ) + + @classmethod + def from_netcdf( + cls, path: str | Path, engine: NetcdfEngine = None + ) -> "ConservativeRegridder": + """Reload a regridder previously written with :meth:`to_netcdf`. + + Validates ``schema_version``; raises :class:`ValueError` if the file + was written by an incompatible version. + """ + path = Path(path) + with xr.open_dataset(path, engine=engine) as ds_weights: + attrs = dict(ds_weights.attrs) + n_dst = int(attrs.pop("n_dst")) + n_src = int(attrs.pop("n_src")) + row = np.asarray(ds_weights["_coo_row"].values) + col = np.asarray(ds_weights["_coo_col"].values) + data = np.asarray(ds_weights["_coo_data"].values) + meta = _metadata_from_attrs(attrs, path) + + with xr.open_dataset(path, group="source_coords", engine=engine) as g: + source_coords = g.load() + with xr.open_dataset(path, group="target_coords", engine=engine) as g: + target_coords = g.load() + + return cls._from_state( + areas=_coo_from_components(row, col, data, (n_dst, n_src)), + source_coords=source_coords, + target_coords=target_coords, + **meta, + ) + + @classmethod + def _from_state( + cls, + *, + areas: "sparse.COO | np.ndarray", + source_coords: xr.Dataset, + target_coords: xr.Dataset, + src_dims: tuple[Hashable, ...], + dst_dims: tuple[Hashable, ...], + src_shape: tuple[int, ...], + dst_shape: tuple[int, ...], + x_coord: str, + y_coord: str, + spherical: bool, + ) -> "ConservativeRegridder": + """Construct a regridder directly from its canonical state. Shared + bypass of ``__init__`` used by :meth:`from_netcdf` and + :meth:`from_polygons`; keeps the list of private attrs in one place.""" + instance = object.__new__(cls) + instance.x_coord = x_coord + instance.y_coord = y_coord + instance.spherical = spherical + instance._src_dims = src_dims + instance._dst_dims = dst_dims + instance._src_shape = src_shape + instance._dst_shape = dst_shape + instance._areas = areas + instance._source_coords = source_coords + instance._target_coords = target_coords + return instance + + @classmethod + def from_polygons( + cls, + source_polygons: np.ndarray, + target_polygons: np.ndarray, + source_dim: str = "cell", + target_dim: str = "cell", + target_coords: xr.Dataset | None = None, + periodic: bool = False, + n_threads: int | None = None, + predicate_filter: bool = True, + ) -> "ConservativeRegridder": + """Build a regridder from explicit shapely polygon arrays — for + unstructured meshes (MPAS, ICON), arbitrary polygon targets (countries, + watersheds), or any non-rectilinear combination. + + Args: + source_polygons, target_polygons: 1D arrays of shapely Polygons. + source_dim, target_dim: Dim names for source/target cells on the + input and output arrays. + target_coords: Optional Dataset of coord variables along + ``target_dim`` to reattach on the output (else: integer index). + periodic: Unwrap polygons that cross the antimeridian (treats x + as longitude on a 360-degree periodic axis). + n_threads: Thread count for parallel GEOS intersection. + predicate_filter: If True, filter STRtree candidates with GEOS + ``intersects``. Set False for tight-bbox grid cells to skip + the predicate (faster on that case, pathological otherwise). + + Geometry is planar in the polygons' own coordinate space. For lat/lon + cells, project into an equal-area CRS first or use the structured + path with ``spherical=True``. + """ + _check_shapely() + src_polys = np.asarray(source_polygons) + dst_polys = np.asarray(target_polygons) + if src_polys.ndim != 1 or dst_polys.ndim != 1: + msg = "source_polygons and target_polygons must be 1D arrays" + raise ValueError(msg) + if periodic: + src_polys = _normalize_periodic_polygons(src_polys) + dst_polys = _normalize_periodic_polygons( + dst_polys, reference=_polygon_reference_x(src_polys) + ) + + src_grid = _Grid( + polys=src_polys, + bounds=shapely.bounds(src_polys), + rectilinear=False, + ) + dst_grid = _Grid( + polys=dst_polys, + bounds=shapely.bounds(dst_polys), + rectilinear=False, + ) + n_src = int(src_polys.size) + n_dst = int(dst_polys.size) + tgt_ds = ( + target_coords + if target_coords is not None + else xr.Dataset(coords={target_dim: np.arange(n_dst)}) + ) + return cls._from_state( + areas=_build_intersection_areas( + src_grid, + dst_grid, + n_threads=n_threads, + predicate_filter=predicate_filter, + ), + source_coords=xr.Dataset(coords={source_dim: np.arange(n_src)}), + target_coords=tgt_ds, + src_dims=(source_dim,), + dst_dims=(target_dim,), + src_shape=(n_src,), + dst_shape=(n_dst,), + x_coord="", + y_coord="", + spherical=False, + ) + + +def polygons_from_coords( + x: np.ndarray, + y: np.ndarray, + spherical: bool = False, + periodic: bool = False, +) -> np.ndarray: + """Build a 1D row-major (y, x) array of shapely cell polygons from 1D or + 2D center coords. Convenience for mixing structured and unstructured paths + via :meth:`ConservativeRegridder.from_polygons`. ``spherical=True`` + projects 1D lat/lon (degrees) into Lambert cylindrical equal-area space; + ``periodic=True`` unwraps antimeridian-crossing cells.""" + _check_shapely() + x = np.asarray(x) + y = np.asarray(y) + if periodic: + x = _unwrap_longitude(x) + if spherical: + if x.ndim != 1 or y.ndim != 1: + msg = "spherical=True requires 1D lat/lon arrays" + raise ValueError(msg) + return _build_cea_grid(x, y).polys + return _build_grid(x, y).polys + + +def _apply_stored_weights( + data: xr.DataArray | xr.Dataset, + direction: _Direction, + src_dims: tuple[Hashable, ...], + dst_dims: tuple[Hashable, ...], + src_shape: tuple[int, ...], + dst_shape: tuple[int, ...], + target_coords: xr.Dataset, + x_coord: str, + y_coord: str, + skipna: bool, + nan_threshold: float, +) -> xr.DataArray | xr.Dataset: + """Apply ``direction``'s cached, pre-transposed weight matrix to ``data`` + via ``xr.apply_ufunc``. + + The apply matrix has shape ``(n_src, n_dst)`` so the matmul is + ``(..., n_src) @ (n_src, n_dst) → (..., n_dst)`` with no per-call transpose. + """ + actual_src_shape = tuple(int(data.sizes[d]) for d in src_dims if d in data.sizes) + if actual_src_shape != src_shape: + msg = ( + f"source spatial shape {actual_src_shape} on dims {src_dims} does " + f"not match the regridder's expected shape {src_shape}" + ) + raise ValueError(msg) + + src_tokens = tuple(f"__src_{d}" for d in src_dims) + data_renamed = data.rename(dict(zip(src_dims, src_tokens, strict=True))) + + output_dtype = _result_dtype(data) + result = xr.apply_ufunc( + _apply_core, + data_renamed, + kwargs={ + "apply_weights": direction.apply_matrix, + "coverage": direction.coverage, + "coverage_all": direction.coverage_all, + "src_shape": src_shape, + "dst_shape": dst_shape, + "skipna": skipna, + "nan_threshold": nan_threshold, + "output_dtype": output_dtype, + }, + input_core_dims=[list(src_tokens)], + output_core_dims=[list(dst_dims)], + exclude_dims=set(src_tokens), + dask="parallelized", + output_dtypes=[output_dtype], + dask_gufunc_kwargs={ + "output_sizes": {d: int(target_coords.sizes[d]) for d in dst_dims}, + "allow_rechunk": True, + }, + keep_attrs=True, + ) + + return _assign_target_coords(result, target_coords, dst_dims, x_coord, y_coord) + + +def _coverage_mask(areas: "sparse.COO | np.ndarray") -> np.ndarray: + """Return a boolean ``(n_dst,)`` mask of target cells with any source + overlap, derived from the raw area matrix.""" + if _HAS_SPARSE and isinstance(areas, sparse.COO): + # COO sparse: row_sum is 0 iff row has no nonzero entries. + n_dst = int(areas.shape[0]) + mask = np.zeros(n_dst, dtype=bool) + mask[areas.coords[0]] = True + return mask + arr = np.asarray(areas) + return np.asarray((arr > 0).any(axis=1)) + + +def _coo_components( + w: "sparse.COO | np.ndarray", +) -> tuple[np.ndarray, np.ndarray, np.ndarray, tuple[int, int]]: + if _HAS_SPARSE and isinstance(w, sparse.COO): + coords = np.asarray(w.coords) + return ( + coords[0].astype(np.int64, copy=False), + coords[1].astype(np.int64, copy=False), + np.asarray(w.data), + w.shape, + ) + arr = np.asarray(w) + rows, cols = np.nonzero(arr) + return ( + rows.astype(np.int64, copy=False), + cols.astype(np.int64, copy=False), + arr[rows, cols], + arr.shape, + ) + + +def _coo_from_components( + row: np.ndarray, + col: np.ndarray, + data: np.ndarray, + shape: tuple[int, int], +) -> "sparse.COO | np.ndarray": + if _HAS_SPARSE: + return sparse.COO( + coords=np.stack([row, col]), + data=data, + shape=shape, + has_duplicates=False, + sorted=False, + ) + dense = np.zeros(shape, dtype=data.dtype if data.size else np.float64) + dense[row, col] = data + return dense + + +def _metadata_attrs(regridder: ConservativeRegridder) -> dict[str, Any]: + attrs: dict[str, Any] = { + "x_coord": regridder.x_coord, + "y_coord": regridder.y_coord, + "spherical": int(regridder.spherical), + "src_dims": [str(d) for d in regridder._src_dims], + "dst_dims": [str(d) for d in regridder._dst_dims], + "src_shape": list(regridder._src_shape), + "dst_shape": list(regridder._dst_shape), + "xarray_regrid_version": _package_version(), + "created": datetime.now(tz=timezone.utc).isoformat(), + "schema_version": _SCHEMA_VERSION, + } + for prefix, ds, coord in [ + ("source_x", regridder._source_coords, regridder.x_coord), + ("source_y", regridder._source_coords, regridder.y_coord), + ("target_x", regridder._target_coords, regridder.x_coord), + ("target_y", regridder._target_coords, regridder.y_coord), + ]: + if coord and coord in ds.coords and ds[coord].size: + attrs[f"{prefix}_range"] = [float(ds[coord].min()), float(ds[coord].max())] + return attrs + + +def _metadata_from_attrs(attrs: dict[str, Any], path: Path) -> dict[str, Any]: + """Parse the kwargs needed by :meth:`ConservativeRegridder._from_state` out + of netCDF root attributes, validating ``schema_version``.""" + schema_version = int(attrs.get("schema_version", 0)) + if schema_version != _SCHEMA_VERSION: + msg = ( + f"regridder file at {path} uses schema version {schema_version}; " + f"this xarray-regrid understands {_SCHEMA_VERSION}. " + "Upgrade xarray-regrid or re-save." + ) + raise ValueError(msg) + + return { + "x_coord": str(attrs["x_coord"]), + "y_coord": str(attrs["y_coord"]), + "spherical": bool(int(attrs["spherical"])), + "src_dims": tuple(str(d) for d in np.atleast_1d(attrs["src_dims"])), + "dst_dims": tuple(str(d) for d in np.atleast_1d(attrs["dst_dims"])), + "src_shape": tuple(int(s) for s in np.atleast_1d(attrs["src_shape"])), + "dst_shape": tuple(int(s) for s in np.atleast_1d(attrs["dst_shape"])), + } + + +def _normalize_longitude_coords( + source: xr.DataArray | xr.Dataset, + target: xr.Dataset, + x_coord: str, +) -> tuple[xr.DataArray | xr.Dataset, xr.Dataset]: + """Unwrap x coordinates across the antimeridian so source and target share + a contiguous longitude frame. No-op when the coord isn't present on both + objects or doesn't look like a longitude.""" + if x_coord not in source.coords or x_coord not in target.coords: + return source, target + + source_x = np.asarray(source[x_coord].values) + target_x = np.asarray(target[x_coord].values) + if not _looks_like_longitude(source_x) and not _looks_like_longitude(target_x): + return source, target + + source_x = _unwrap_longitude(source_x) + target_x = _unwrap_longitude(target_x) + # Shift target into the same 360° window as source (CF convention: x + # varies along the trailing axis). + src_finite = source_x[np.isfinite(source_x)] + tgt_finite = target_x[np.isfinite(target_x)] + if src_finite.size and tgt_finite.size: + target_x = target_x + _periodic_offset( + float(src_finite.mean()), float(tgt_finite.mean()) + ) + return ( + utils.update_coord(source, x_coord, source_x), + cast(xr.Dataset, utils.update_coord(target, x_coord, target_x)), + ) + + +def _looks_like_longitude(values: np.ndarray) -> bool: + finite = values[np.isfinite(values)] + return bool(finite.size and finite.min() >= -360.0 and finite.max() <= 360.0) + + +def _unwrap_longitude(values: np.ndarray) -> np.ndarray: + """Unwrap longitudes along the trailing axis (CF convention). For 2D + coords, latitude doesn't wrap mod 360°, so unwrapping it is incorrect.""" + radians = np.deg2rad(np.asarray(values, dtype=float)) + return np.rad2deg(np.unwrap(radians, axis=-1)) + + +def _normalize_periodic_polygons( + polygons: np.ndarray, reference: float | None = None +) -> np.ndarray: + """Unwrap each polygon across the antimeridian, then shift each into the + same 360-degree window as ``reference`` (or as the first finite center if + ``reference`` is None).""" + unwrapped = [_unwrap_polygon(p) for p in polygons] + if reference is None: + for poly in unwrapped: + center = _polygon_center_x(poly) + if np.isfinite(center): + reference = center + break + if reference is None: + return np.array(unwrapped, dtype=object) + + out = [] + for poly in unwrapped: + offset = _periodic_offset(reference, _polygon_center_x(poly)) + out.append(affinity.translate(poly, xoff=offset) if offset != 0.0 else poly) + return np.array(out, dtype=object) + + +def _polygon_reference_x(polygons: np.ndarray) -> float | None: + """Mean polygon-center x across an array of polygons, or None if all + polygons have non-finite centers.""" + bounds = shapely.bounds(polygons) + centers = 0.5 * (bounds[:, 0] + bounds[:, 2]) + finite = centers[np.isfinite(centers)] + return float(finite.mean()) if finite.size else None + + +def _polygon_center_x(polygon: Any) -> float: + minx, _, maxx, _ = polygon.bounds + return 0.5 * (float(minx) + float(maxx)) + + +def _periodic_offset(reference: float, value: float) -> float: + if not np.isfinite(reference) or not np.isfinite(value): + return 0.0 + return 360.0 * round((reference - value) / 360.0) + + +def _unwrap_polygon(polygon: Any) -> Any: + if polygon.is_empty: + return polygon + if polygon.geom_type == "Polygon": + exterior = _unwrap_ring(np.asarray(polygon.exterior.coords)) + holes = [_unwrap_ring(np.asarray(ring.coords)) for ring in polygon.interiors] + return shapely.Polygon(exterior, holes) + if polygon.geom_type == "MultiPolygon": + return shapely.MultiPolygon([_unwrap_polygon(part) for part in polygon.geoms]) + return polygon + + +def _unwrap_ring(ring: np.ndarray) -> np.ndarray: + new_ring = np.asarray(ring, dtype=float).copy() + offset = 0.0 + for i in range(1, new_ring.shape[0]): + x = new_ring[i, 0] + offset + step = x - new_ring[i - 1, 0] + if step > 180.0: + offset -= 360.0 + elif step < -180.0: + offset += 360.0 + new_ring[i, 0] += offset + return new_ring + + +def _transpose_weights( + w: "sparse.COO | np.ndarray", *, sort: bool = False +) -> "sparse.COO | np.ndarray": + """Materialize a transposed weight matrix. + + `sparse.COO.T` is a lazy view that re-sorts indices on each downstream + matmul, so callers relying on ``.coords[0]`` being row indices or wanting + a hot matmul path should materialize once here. Pass ``sort=True`` to + additionally trigger the sort ahead of time (used for the apply matrix). + """ + if _HAS_SPARSE and isinstance(w, sparse.COO): + t = w.T + out = sparse.COO( + coords=np.asarray(t.coords), + data=np.asarray(t.data), + shape=t.shape, + has_duplicates=False, + sorted=False, + ) + if sort: + out._sort_indices() + return out + return np.asarray(w).T.copy() + + +def _spatial_dims( + obj: xr.DataArray | xr.Dataset, x_coord: str, y_coord: str +) -> tuple[Hashable, ...]: + """Return the spatial dim order to feed to apply_ufunc. + + For 1D rectilinear coords (x and y ride on separate dims), canonicalize to + ``(y_dim, x_dim)`` so the flattened cell order matches the polygon order + emitted by the fast-path in ``_build_grid``. For curvilinear (2D) coords, + preserve the dim order already present on ``obj``. + """ + if x_coord not in obj.coords or y_coord not in obj.coords: + return () + xd = obj[x_coord].dims + yd = obj[y_coord].dims + if len(xd) == 1 and len(yd) == 1 and xd[0] != yd[0]: + return (yd[0], xd[0]) + dims = set(xd) | set(yd) + return tuple(d for d in obj.dims if d in dims) + + +def _grid_from_coords( + obj: xr.DataArray | xr.Dataset, + x_coord: str, + y_coord: str, + dims: tuple[Hashable, ...], + spherical: bool = False, +) -> "_Grid": + """Build a :class:`_Grid` from the object's x/y coordinates. + + Rectilinear (both coords 1D on separate dims) takes the fast path. + Curvilinear coords are broadcast to a common N-D array in ``dims`` order. + + If ``spherical`` is True, coordinates are assumed to be longitude (x) and + latitude (y) in degrees, and cells are projected into a Lambert cylindrical + equal-area space (x' = lon_rad, y' = sin(lat_rad)) before constructing the + cell polygons. This gives mass-conservative weights on the sphere at the + same cost as the planar fast path. Rectilinear-only. + """ + xd = obj[x_coord] + yd = obj[y_coord] + is_rectilinear = xd.ndim == 1 and yd.ndim == 1 and xd.dims[0] != yd.dims[0] + + if spherical and not is_rectilinear: + msg = "spherical=True is only supported for rectilinear (1D lat/lon) coords" + raise NotImplementedError(msg) + + if is_rectilinear: + x = np.asarray(xd.values) + y = np.asarray(yd.values) + return _build_cea_grid(x, y) if spherical else _build_grid(x, y) + + xc, yc = xr.broadcast(xd, yd) + return _build_grid( + np.asarray(xc.transpose(*dims).values), + np.asarray(yc.transpose(*dims).values), + ) + + +def _build_cea_grid(lon_centers: np.ndarray, lat_centers: np.ndarray) -> "_Grid": + """Build a rectilinear :class:`_Grid` whose cell polygons are in Lambert + cylindrical equal-area coordinates (x' = lon_rad, y' = sin(lat_rad)). + + Projecting *edges* analytically — rather than projecting centers and then + re-midpointing — is required because ``sin()`` is nonlinear: the projected + midpoint of two lat centers is not the same as the midpoint of two + projected lat edges. + """ + _check_shapely() + if lon_centers.size < 2 or lat_centers.size < 2: + msg = "spherical mode requires at least two cells per dimension" + raise ValueError(msg) + lat_edges_deg = np.clip(utils.infer_1d_edges(lat_centers), -90.0, 90.0) + lon_edges_deg = utils.infer_1d_edges(lon_centers) + return _rect_grid_from_edges( + np.deg2rad(lon_edges_deg), + np.sin(np.deg2rad(lat_edges_deg)), + ) + + +def _infer_2d_corners(a: np.ndarray) -> np.ndarray: + """Infer (ny+1, nx+1) cell corners from a 2D cell-center array. Interior + corners are the mean of the 4 surrounding centers; boundary corners are + reflected from the adjacent interior row/column.""" + a = np.asarray(a, dtype=float) + ny, nx = a.shape + pad = np.empty((ny + 2, nx + 2), dtype=a.dtype) + pad[1:-1, 1:-1] = a + pad[0, 1:-1] = 2 * a[0, :] - a[1, :] + pad[-1, 1:-1] = 2 * a[-1, :] - a[-2, :] + pad[1:-1, 0] = 2 * a[:, 0] - a[:, 1] + pad[1:-1, -1] = 2 * a[:, -1] - a[:, -2] + pad[0, 0] = 2 * pad[0, 1] - pad[0, 2] + pad[0, -1] = 2 * pad[0, -2] - pad[0, -3] + pad[-1, 0] = 2 * pad[-1, 1] - pad[-1, 2] + pad[-1, -1] = 2 * pad[-1, -2] - pad[-1, -3] + return 0.25 * (pad[:-1, :-1] + pad[1:, :-1] + pad[:-1, 1:] + pad[1:, 1:]) + + +@dataclass +class _Grid: + """Cached cell geometry for a structured grid. + + ``polys`` is a flat (n_cells,) object array of shapely Polygons. + ``bounds`` is a (n_cells, 4) ``(minx, miny, maxx, maxy)`` array cached for + the STRtree / candidate-search path. ``rectilinear`` is True when both the + source x and y were 1D coordinate arrays (axis-aligned rectangles) — the + weight builder uses this to skip GEOS polygon clipping and compute + intersection areas analytically from the bounds. + """ + + polys: np.ndarray + bounds: np.ndarray + rectilinear: bool + + +def _rect_grid_from_edges(xe: np.ndarray, ye: np.ndarray) -> _Grid: + """Build a rectilinear :class:`_Grid` from already-prepared edge arrays. + + Shared by the raw-planar (:func:`_build_grid` 1D branch) and the analytic + equal-area (:func:`_build_cea_grid`) paths. + """ + x0, y0 = np.meshgrid(xe[:-1], ye[:-1], indexing="xy") + x1, y1 = np.meshgrid(xe[1:], ye[1:], indexing="xy") + x0f, y0f, x1f, y1f = x0.ravel(), y0.ravel(), x1.ravel(), y1.ravel() + polys = shapely.box(x0f, y0f, x1f, y1f) + bounds = np.stack([x0f, y0f, x1f, y1f], axis=1) + return _Grid(polys=polys, bounds=bounds, rectilinear=True) + + +def _build_grid(xc: np.ndarray, yc: np.ndarray) -> _Grid: + """Return a _Grid of cell geometry for a structured grid. + + Accepts 1D (rectilinear, separate x and y vectors) or 2D (curvilinear, + co-shaped center arrays) inputs. Output order is row-major in the input dim + order: for 2D inputs of shape (ny, nx) the polygons correspond to cells + reshaped as ``(ny, nx)``. + """ + _check_shapely() + if xc.ndim == 1 and yc.ndim == 1: + xe = utils.infer_1d_edges(xc.astype(float)) + ye = utils.infer_1d_edges(yc.astype(float)) + return _rect_grid_from_edges(xe, ye) + if xc.ndim == 2 and yc.ndim == 2 and xc.shape == yc.shape: + xcorn = _infer_2d_corners(xc) + ycorn = _infer_2d_corners(yc) + ny, nx = xc.shape + c00 = np.stack([xcorn[:-1, :-1], ycorn[:-1, :-1]], axis=-1) + c10 = np.stack([xcorn[:-1, 1:], ycorn[:-1, 1:]], axis=-1) + c11 = np.stack([xcorn[1:, 1:], ycorn[1:, 1:]], axis=-1) + c01 = np.stack([xcorn[1:, :-1], ycorn[1:, :-1]], axis=-1) + rings = np.stack([c00, c10, c11, c01, c00], axis=2).reshape(ny * nx, 5, 2) + polys = shapely.polygons(rings) + return _Grid(polys=polys, bounds=shapely.bounds(polys), rectilinear=False) + msg = "x and y coordinate arrays must both be 1D or both 2D" + raise ValueError(msg) + + +def _build_intersection_areas( + src: _Grid, + dst: _Grid, + n_threads: int | None = None, + *, + predicate_filter: bool = False, +) -> "sparse.COO | np.ndarray": + """Build the (n_dst, n_src) raw area-intersection matrix ``A[i, j] = + area(dst_i ∩ src_j)``. + + This is the unnormalized matrix. Row-normalize via :func:`_row_normalize` + to get forward weights; transpose first for backward (target → source). + + When both grids are rectilinear (axis-aligned rectangles) intersection + areas are computed analytically from the bounds, skipping GEOS clipping. + + ``predicate_filter=False`` (default) uses a bbox-only STRtree query and + relies on the ``area > 0`` filter below to drop bbox-false-positives. + For structured cells whose bboxes are tight (quadrilaterals) this is a + large win — the GEOS ``intersects`` predicate inside STRtree is much + more expensive than the extra no-op intersections it avoids. Set + ``predicate_filter=True`` for user-supplied polygons with loose bboxes + (long, thin, diagonal shapes) where the predicate pays for itself. + """ + _check_shapely() + n_dst = len(dst.polys) + n_src = len(src.polys) + + tree = STRtree(src.polys) + if predicate_filter: + pairs = tree.query(dst.polys, predicate="intersects") + else: + pairs = tree.query(dst.polys) + dst_idx = np.asarray(pairs[0]) + src_idx = np.asarray(pairs[1]) + + if dst_idx.size == 0: + return _empty_weights(n_dst, n_src) + + if src.rectilinear and dst.rectilinear: + sb = src.bounds[src_idx] + db = dst.bounds[dst_idx] + dx = np.minimum(sb[:, 2], db[:, 2]) - np.maximum(sb[:, 0], db[:, 0]) + dy = np.minimum(sb[:, 3], db[:, 3]) - np.maximum(sb[:, 1], db[:, 1]) + areas = np.maximum(dx, 0.0) * np.maximum(dy, 0.0) + else: + areas = _intersection_areas_threaded( + dst.polys[dst_idx], src.polys[src_idx], n_threads=n_threads + ) + + keep = areas > 0 + dst_idx = dst_idx[keep] + src_idx = src_idx[keep] + areas = areas[keep] + + if dst_idx.size == 0: + return _empty_weights(n_dst, n_src) + + if _HAS_SPARSE: + return sparse.COO( + coords=np.stack([dst_idx, src_idx]), + data=areas.astype(np.float64), + shape=(n_dst, n_src), + has_duplicates=False, + sorted=False, + ) + a_dense = np.zeros((n_dst, n_src), dtype=np.float64) + a_dense[dst_idx, src_idx] = areas + return a_dense + + +def _row_normalize( + areas: "sparse.COO | np.ndarray", +) -> "sparse.COO | np.ndarray": + """Normalize rows of an area matrix so each row sums to 1 (rows with no + overlap stay all-zero, which produces NaN output under the apply path).""" + if _HAS_SPARSE and isinstance(areas, sparse.COO): + n_dst = areas.shape[0] + dst_idx = areas.coords[0] + src_idx = areas.coords[1] + data = areas.data + if data.size == 0: + return areas + row_sum = np.bincount(dst_idx, weights=data, minlength=n_dst) + new_data = data / row_sum[dst_idx] + return sparse.COO( + coords=np.stack([dst_idx, src_idx]), + data=new_data, + shape=areas.shape, + has_duplicates=False, + sorted=False, + ) + row_sum = areas.sum(axis=1, keepdims=True) + row_sum = np.where(row_sum == 0, 1.0, row_sum) + return areas / row_sum + + +def _intersection_areas_threaded( + a: np.ndarray, b: np.ndarray, n_threads: int | None +) -> np.ndarray: + """Compute per-pair intersection areas ``area(a[i] & b[i])`` over numpy + arrays of shapely geometries, optionally parallelized via threads. + + Shapely 2.x releases the GIL for GEOS ops, so a ``ThreadPoolExecutor`` + gives near-linear speedup on multi-core machines without pickling data. + """ + _check_shapely() + n = len(a) + if n_threads is None: + # Below ~1k pairs the pool spin-up (~0.3 ms) dominates sub-ms work. + # Above that, scaling is near-linear with logical cores — shapely + # releases the GIL inside its GEOS ufuncs. Cap at 16 to avoid + # oversubscription on unusually wide machines. + n_threads = 1 if n < 1_000 else min(os.cpu_count() or 1, 16) + if n_threads <= 1 or n == 0: + return shapely.area(shapely.intersection(a, b)) + + splits = np.array_split(np.arange(n), n_threads) + + def _work(idx: np.ndarray) -> np.ndarray: + return shapely.area(shapely.intersection(a[idx], b[idx])) + + with ThreadPoolExecutor(max_workers=n_threads) as pool: + parts = list(pool.map(_work, splits)) + return np.concatenate(parts) + + +def _empty_weights(n_dst: int, n_src: int) -> "sparse.COO | np.ndarray": + if _HAS_SPARSE: + return sparse.COO( + coords=np.zeros((2, 0), dtype=np.int64), + data=np.zeros(0, dtype=np.float64), + shape=(n_dst, n_src), + ) + return np.zeros((n_dst, n_src), dtype=np.float64) + + +def _apply_core( + arr: np.ndarray, + apply_weights: Any, + coverage: np.ndarray, + coverage_all: bool, + src_shape: tuple[int, ...], + dst_shape: tuple[int, ...], + skipna: bool, + nan_threshold: float, + output_dtype: np.dtype, +) -> np.ndarray: + """Apply a pre-transposed weight matrix along the trailing spatial dims. + + ``arr`` has shape ``(..., *src_shape)``; ``apply_weights`` has shape + ``(n_src, n_dst)`` — returns ``(..., *dst_shape)``. + + ``coverage`` is a boolean ``(n_dst,)`` mask: target cells with any source + overlap. ``coverage_all`` is precomputed so every block skips the + ``coverage.all()`` scan. Uncovered cells are always masked to NaN in the + output, regardless of ``skipna`` — domain boundaries and polygon holes + produce NaN (matches the axis-factored ``conservative`` method). + """ + n_spatial = len(src_shape) + leading_shape = arr.shape[:-n_spatial] if n_spatial > 0 else arr.shape + n_src = int(np.prod(src_shape)) + flat = arr.reshape(-1, n_src) if leading_shape else arr.reshape(1, n_src) + + if skipna and np.issubdtype(flat.dtype, np.floating): + nan_mask = np.isnan(flat) + has_nan = nan_mask.any() + else: + has_nan = False + + if has_nan: + mask = (~nan_mask).astype(flat.dtype) + filled = np.where(nan_mask, flat.dtype.type(0.0), flat) + numerator = np.asarray(filled @ apply_weights) + fraction = np.asarray(mask @ apply_weights) + threshold = get_valid_threshold(nan_threshold) + with np.errstate(invalid="ignore", divide="ignore"): + result = numerator / fraction + result = np.where(fraction >= threshold, result, np.nan) + else: + result = np.asarray(flat @ apply_weights) + if not coverage_all: + result = np.where(coverage[np.newaxis, :], result, np.nan) + + # sparse.matmul promotes to float64 regardless of the input dtype — cast + # back to the requested output dtype so float32-in really produces + # float32-out (halves memory for float32 pipelines). + if result.dtype != output_dtype: + result = result.astype(output_dtype, copy=False) + + out_shape = (*leading_shape, *dst_shape) if leading_shape else dst_shape + return result.reshape(out_shape) + + +def _result_dtype(obj: xr.DataArray | xr.Dataset) -> np.dtype: + if isinstance(obj, xr.DataArray): + return np.result_type(np.float32, obj.dtype) + dtypes = [v.dtype for v in obj.data_vars.values()] + if not dtypes: + return np.dtype(np.float64) + return np.result_type(np.float32, *dtypes) + + +def _assign_target_coords( + obj: xr.DataArray | xr.Dataset, + target_ds: xr.Dataset, + dst_dims: tuple[Hashable, ...], + x_coord: str, + y_coord: str, +) -> xr.DataArray | xr.Dataset: + """Attach target coordinates that name a spatial axis or live on the + output spatial dims. Scalar coords (``dims == ()``) ride along too, so + pinned target metadata (e.g. a fixed timestamp) is preserved.""" + dst_dim_set = set(dst_dims) + new_coords = { + name: coord + for name, coord in target_ds.coords.items() + if name in (x_coord, y_coord) or set(coord.dims).issubset(dst_dim_set) + } + return obj.assign_coords(new_coords) if new_coords else obj diff --git a/src/xarray_regrid/regrid.py b/src/xarray_regrid/regrid.py index 8080207..ad94647 100644 --- a/src/xarray_regrid/regrid.py +++ b/src/xarray_regrid/regrid.py @@ -6,7 +6,7 @@ from xarray_regrid.methods import ( conservative, - conservative_polygon, + conservative_2d, flox_reduce, interp, ) @@ -22,9 +22,11 @@ class Regridder: linear: linear, bilinear, or higher dimensional linear interpolation nearest: nearest-neighbor regridding cubic: cubic spline regridding - conservative: axis-factored conservative regridding (rectilinear only) - conservative_polygon: polygon-intersection conservative regridding, - including curvilinear grids (requires shapely) + conservative: axis-factored conservative regridding (rectilinear, + 1D-separable grids only) + conservative_2d: conservative regridding for grids that aren't + 1D-separable — curvilinear 2D coords, unstructured meshes, or + arbitrary polygon-to-polygon aggregation (requires shapely) most_common: most common value regridder stat: area statistics regridder """ @@ -89,28 +91,34 @@ def cubic( ds_formatted = format_for_regrid(self._obj, ds_target_grid) return interp.interp_regrid(ds_formatted, ds_target_grid, "cubic") - def conservative_polygon( + def conservative_2d( self, ds_target_grid: xr.Dataset, x_coord: str = "longitude", y_coord: str = "latitude", + spherical: bool = False, time_dim: str | None = "time", skipna: bool = True, nan_threshold: float = 1.0, n_threads: int | None = None, ) -> xr.DataArray | xr.Dataset: - """Polygon-intersection conservative regrid (planar geometry only). + """Conservative regrid for grids that aren't 1D-separable. - Unlike ``.conservative``, this path handles curvilinear grids whose - x/y coordinates are 2D arrays. Planar only: users on global lat/lon - grids should expect small area errors near the poles and across the - antimeridian. Requires ``shapely >= 2.0``. + Use this when ``.conservative`` can't express your grid: curvilinear + coordinates (2D ``lat``/``lon`` arrays), unstructured meshes, or any + arbitrary polygon target. Computes 2D cell-polygon intersections via + shapely. Defaults to planar geometry; set ``spherical=True`` for + lat/lon grids in degrees to get proper spherical area weights via an + analytic cylindrical equal-area projection. Requires ``shapely >= 2.0``. Args: ds_target_grid: Dataset defining the target grid; must expose ``x_coord`` and ``y_coord`` as coordinate variables. x_coord: Name of the x (longitude-like) coordinate variable. y_coord: Name of the y (latitude-like) coordinate variable. + spherical: If True, assume coords are longitude/latitude in + degrees and apply a Lambert cylindrical equal-area projection + before intersecting. Rectilinear (1D coord) grids only. time_dim: Name of the time dimension. Defaults to ``"time"``. Use ``None`` to force regridding over the time dimension. skipna: If True, propagate NaNs into the weighted mean via a @@ -126,21 +134,18 @@ def conservative_polygon( if not 0.0 <= nan_threshold <= 1.0: msg = "nan_threshold must be between [0, 1]" raise ValueError(msg) - - # Drop the time dim from the target if it slipped in, but skip the - # shared dim-match check: the curvilinear target's spatial dims need - # not share names with the source — they're matched by coord values. - if time_dim is not None and time_dim in ds_target_grid.coords: - ds_target_grid = ds_target_grid.isel({time_dim: 0}).reset_coords() - return conservative_polygon.polygon_conservative_regrid( + ds_target_grid = validate_input( + self._obj, ds_target_grid, time_dim, require_shared_dims=False + ) + regridder = conservative_2d.ConservativeRegridder( self._obj, ds_target_grid, x_coord=x_coord, y_coord=y_coord, - skipna=skipna, - nan_threshold=nan_threshold, + spherical=spherical, n_threads=n_threads, ) + return regridder.regrid(self._obj, skipna=skipna, nan_threshold=nan_threshold) def conservative( self, @@ -335,6 +340,7 @@ def validate_input( data: xr.Dataset, ds_target_grid: xr.Dataset, time_dim: str | None, + require_shared_dims: bool = ..., ) -> xr.Dataset: ... @@ -343,6 +349,7 @@ def validate_input( data: xr.DataArray, ds_target_grid: xr.Dataset, time_dim: str | None, + require_shared_dims: bool = ..., ) -> xr.Dataset: ... @@ -350,11 +357,14 @@ def validate_input( data: xr.DataArray | xr.Dataset, ds_target_grid: xr.Dataset, time_dim: str | None, + require_shared_dims: bool = True, ) -> xr.Dataset: if time_dim is not None and time_dim in ds_target_grid.coords: ds_target_grid = ds_target_grid.isel({time_dim: 0}).reset_coords() - if len(set(data.dims).intersection(set(ds_target_grid.dims))) == 0: + # Curvilinear regridders match source and target by coord values, not by + # dim name, so they opt out of the shared-dim requirement. + if require_shared_dims and not set(data.dims) & set(ds_target_grid.dims): msg = ( "None of the target dims are in the data:\n" " regridding is not possible.\n" @@ -363,7 +373,7 @@ def validate_input( ) raise ValueError(msg) - if len(set(data.coords).intersection(set(ds_target_grid.coords))) == 0: + if not set(data.coords) & set(ds_target_grid.coords): msg = ( "None of the target coords are in the data:\n" " regridding is not possible.\n" diff --git a/src/xarray_regrid/utils.py b/src/xarray_regrid/utils.py index 6979f84..7e80ca1 100644 --- a/src/xarray_regrid/utils.py +++ b/src/xarray_regrid/utils.py @@ -114,6 +114,22 @@ def create_regridding_dataset( ) +def infer_1d_edges(centers: np.ndarray) -> np.ndarray: + """Return cell edges from 1D centers: midpoints between consecutive + centers, with symmetric reflection for the two outer bounds. + + Requires at least two centers. + """ + c = np.asarray(centers, dtype=float) + if c.size < 2: + msg = "need at least two centers to infer cell edges" + raise ValueError(msg) + mids = 0.5 * (c[:-1] + c[1:]) + left = 2 * c[0] - mids[0] + right = 2 * c[-1] - mids[-1] + return np.concatenate([[left], mids, [right]]) + + def to_intervalindex(coords: np.ndarray) -> pd.IntervalIndex: """Convert a 1-d coordinate array to a pandas IntervalIndex. Take the midpoints between the coordinates as the interval boundaries. @@ -126,20 +142,9 @@ def to_intervalindex(coords: np.ndarray) -> pd.IntervalIndex: coordinates. """ if len(coords) > 1: - midpoints = (coords[:-1] + coords[1:]) / 2 - - # Extrapolate outer bounds beyond the first and last coordinates - left_bound = 2 * coords[0] - midpoints[0] - right_bound = 2 * coords[-1] - midpoints[-1] - - breaks = np.concatenate([[left_bound], midpoints, [right_bound]]) - intervals = pd.IntervalIndex.from_breaks(breaks) - - else: - # If the target grid has a single point, set search interval to span all space - intervals = pd.IntervalIndex.from_breaks([-np.inf, np.inf]) - - return intervals + return pd.IntervalIndex.from_breaks(infer_1d_edges(coords)) + # If the target grid has a single point, set search interval to span all space + return pd.IntervalIndex.from_breaks([-np.inf, np.inf]) def overlap(a: pd.IntervalIndex, b: pd.IntervalIndex) -> np.ndarray: @@ -436,8 +441,8 @@ def update_coord( def update_coord( obj: xr.DataArray | xr.Dataset, coord: Hashable, coord_vals: np.ndarray ) -> xr.DataArray | xr.Dataset: - """Update the values of a coordinate, ensuring indexes stay in sync.""" - attrs = obj.coords[coord].attrs - obj = obj.assign_coords({coord: coord_vals}) - obj.coords[coord].attrs = attrs - return obj + """Update the values of a coordinate, ensuring indexes stay in sync. + Preserves the coord's existing dims and attrs (so multi-dim coords work).""" + original = obj.coords[coord] + new_coord = xr.DataArray(coord_vals, dims=original.dims, attrs=original.attrs) + return obj.assign_coords({coord: new_coord}) diff --git a/tests/test_conservative_2d.py b/tests/test_conservative_2d.py new file mode 100644 index 0000000..75864fc --- /dev/null +++ b/tests/test_conservative_2d.py @@ -0,0 +1,651 @@ +"""Tests for conservative_2d.""" + +import numpy as np +import pytest +import xarray as xr + +import xarray_regrid # noqa: F401 (registers the accessor) +from xarray_regrid import ConservativeRegridder, polygons_from_coords + +shapely = pytest.importorskip("shapely") + + +def _rect_da(ny=60, nx=120, nt=2, seed=0): + x = np.linspace(-180, 180, nx, endpoint=False) + 180 / nx + y = np.linspace(-90, 90, ny, endpoint=False) + 90 / ny + rng = np.random.default_rng(seed) + return xr.DataArray( + rng.normal(size=(nt, ny, nx)).astype(np.float64), + dims=("time", "y", "x"), + coords={"time": np.arange(nt), "y": y, "x": x}, + name="var", + ) + + +def _rect_target(ny=24, nx=47): + x = np.linspace(-180, 180, nx, endpoint=False) + 180 / nx + y = np.linspace(-90, 90, ny, endpoint=False) + 90 / ny + return xr.Dataset(coords={"y": y, "x": x}) + + +def test_polygon_matches_factored_planar(): + """On a rectilinear grid with no spherical correction, the polygon path + should reproduce the axis-factored path to machine precision.""" + da = _rect_da() + target = _rect_target() + ref = da.regrid.conservative(target, latitude_coord=None) + got = da.regrid.conservative_2d(target, x_coord="x", y_coord="y") + got = got.transpose(*ref.dims) + np.testing.assert_allclose(got.values, ref.values, atol=1e-12) + + +def test_polygon_dask_time_chunks(): + da = _rect_da(nt=4).chunk({"time": 2}) + target = _rect_target() + got = da.regrid.conservative_2d(target, x_coord="x", y_coord="y") + assert got.chunks is not None + got = got.compute() + ref = _rect_da(nt=4).regrid.conservative(target, latitude_coord=None) + np.testing.assert_allclose(got.transpose(*ref.dims).values, ref.values, atol=1e-12) + + +def test_polygon_rechunks_spatial(): + """Spatially-chunked input should be accepted (rechunked internally).""" + da = _rect_da().chunk({"time": 1, "y": 30, "x": 40}) + target = _rect_target() + out = da.regrid.conservative_2d(target, x_coord="x", y_coord="y") + out.compute() + + +def test_polygon_nan_threshold(): + """Stricter nan_threshold produces more NaN output cells when partial + overlaps exist.""" + da = _rect_da() + da.values[:, 21:29, :] = np.nan + target = _rect_target(ny=23, nx=47) + out1 = da.regrid.conservative_2d( + target, x_coord="x", y_coord="y", skipna=True, nan_threshold=1.0 + ) + out0 = da.regrid.conservative_2d( + target, x_coord="x", y_coord="y", skipna=True, nan_threshold=0.0 + ) + assert int(np.isnan(out0.values).sum()) > int(np.isnan(out1.values).sum()) + + +def test_polygon_curvilinear_target(): + """Curvilinear target (2D lat/lon corners) returns finite values.""" + da = _rect_da() + ny_t, nx_t = 20, 30 + xi, yi = np.meshgrid( + np.linspace(-120, 120, nx_t), + np.linspace(-60, 60, ny_t), + indexing="xy", + ) + th = np.deg2rad(30) + x2d = xi * np.cos(th) - yi * np.sin(th) + y2d = xi * np.sin(th) + yi * np.cos(th) + target = xr.Dataset(coords={"x": (("ny", "nx"), x2d), "y": (("ny", "nx"), y2d)}) + out = da.regrid.conservative_2d(target, x_coord="x", y_coord="y") + assert out.shape == (2, 20, 30) + assert np.isfinite(out.values).mean() > 0.9 + + +def test_antimeridian_rectilinear_constant(): + da = xr.DataArray( + np.full((2, 4), 2.5), + dims=("latitude", "longitude"), + coords={ + "latitude": np.array([-2.5, 2.5]), + "longitude": np.array([167.5, 172.5, -177.5, -172.5]), + }, + ) + target = xr.Dataset( + coords={ + "latitude": np.array([-2.5, 2.5]), + "longitude": np.array([170.0, -170.0]), + } + ) + + out = da.regrid.conservative_2d(target, x_coord="longitude", y_coord="latitude") + np.testing.assert_allclose(out.values, 2.5, atol=1e-12) + + +def test_polygon_nan_threshold_invalid(): + da = _rect_da() + with pytest.raises(ValueError): + da.regrid.conservative_2d( + _rect_target(), x_coord="x", y_coord="y", nan_threshold=1.5 + ) + + +def test_polygon_dataset_input(): + """A Dataset input with multiple variables should regrid all of them.""" + da = _rect_da() + ds = xr.Dataset({"a": da, "b": da * 2.0}) + target = _rect_target() + out = ds.regrid.conservative_2d(target, x_coord="x", y_coord="y") + assert set(out.data_vars) == {"a", "b"} + np.testing.assert_allclose( + out["b"].transpose(*out["a"].dims).values, + out["a"].transpose(*out["a"].dims).values * 2.0, + atol=1e-12, + ) + + +# --- ConservativeRegridder (reusable) ------------------------------------------ + + +def test_regridder_reusable_matches_oneshot(): + """Reusing a single ConservativeRegridder on multiple fields matches the + one-shot `conservative_2d_regrid` call.""" + da1 = _rect_da(seed=1) + da2 = _rect_da(seed=2) + target = _rect_target() + regridder = ConservativeRegridder(da1, target, x_coord="x", y_coord="y") + ref1 = da1.regrid.conservative_2d(target, x_coord="x", y_coord="y") + ref2 = da2.regrid.conservative_2d(target, x_coord="x", y_coord="y") + out1 = regridder.regrid(da1) + out2 = regridder(da2) # __call__ alias + np.testing.assert_allclose(out1.values, ref1.values, atol=1e-12) + np.testing.assert_allclose(out2.values, ref2.values, atol=1e-12) + + +def test_regridder_weight_cache(): + """Forward weight matrix is built lazily and then reused across calls.""" + da = _rect_da() + target = _rect_target() + regridder = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + assert "forward_weights" not in regridder.__dict__ + regridder.regrid(da) + w1 = regridder.forward_weights + regridder.regrid(da) + assert regridder.forward_weights is w1 # same object, not rebuilt + + +def test_regridder_transpose_roundtrip_rectilinear_aligned(): + """If target cells are aligned unions of source cells, forward followed by + backward reproduces a constant field exactly.""" + # 120 source cells along each axis, target is a 4x coarsening (exact union + # of source cells). A constant source field survives the roundtrip to + # itself because every source cell is fully covered. + ns = 120 + nt = 30 # exactly ns / 4 + x_s = np.linspace(-180, 180, ns, endpoint=False) + 180 / ns + y_s = np.linspace(-90, 90, ns, endpoint=False) + 90 / ns + x_t = np.linspace(-180, 180, nt, endpoint=False) + 180 / nt + y_t = np.linspace(-90, 90, nt, endpoint=False) + 90 / nt + da = xr.DataArray( + np.full((ns, ns), 3.5, dtype=np.float64), + dims=("y", "x"), + coords={"y": y_s, "x": x_s}, + ) + target = xr.Dataset(coords={"y": y_t, "x": x_t}) + regridder = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + coarse = regridder.regrid(da) + back = regridder.T.regrid(coarse) + # Every value should match the original constant. + np.testing.assert_allclose(back.values, 3.5, atol=1e-12) + + +def test_regridder_T_preserves_weights(): # noqa: N802 + """regridder.T.T should share the raw area matrix with the original.""" + da = _rect_da() + target = _rect_target() + r = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + rr = r.T.T + # Same shape, same coords, same data. + assert r._areas.shape == rr._areas.shape + if hasattr(r._areas, "data"): + np.testing.assert_array_equal(r._areas.data, rr._areas.data) + + +def test_regridder_shape_mismatch_raises(): + """Applying the regridder to data whose spatial shape differs from the + source it was built for should raise.""" + da = _rect_da() + target = _rect_target() + regridder = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + smaller = _rect_da(ny=30, nx=60) + with pytest.raises(ValueError, match="spatial shape"): + regridder.regrid(smaller) + + +def test_spherical_mode_matches_factored(): + """Polygon path with ``spherical=True`` should match the axis-factored + sin-weighted conservative path to within a tight tolerance on lat/lon + grids (both are analytically equivalent for cylindrical equal-area).""" + lon_s = np.linspace(-180, 180, 180, endpoint=False) + 1.0 + lat_s = np.linspace(-90, 90, 90, endpoint=False) + 1.0 + lon_t = np.linspace(-180, 180, 60, endpoint=False) + 3.0 + lat_t = np.linspace(-90, 90, 30, endpoint=False) + 3.0 + vals = np.cos(np.deg2rad(lat_s))[:, None] ** 2 * np.sin(np.deg2rad(lon_s))[None, :] + da = xr.DataArray( + vals, + dims=("latitude", "longitude"), + coords={"latitude": lat_s, "longitude": lon_s}, + ) + target = xr.Dataset(coords={"latitude": lat_t, "longitude": lon_t}) + + factored = da.regrid.conservative(target, latitude_coord="latitude") + polygon = da.regrid.conservative_2d( + target, x_coord="longitude", y_coord="latitude", spherical=True + ) + # Both methods should agree to the grid's own quadrature accuracy. Near the + # poles the factored path's median-dlat approximation introduces a small + # discrepancy; 1e-3 absolute is well below the planar raw error floor + # (~1e-2 at this resolution, tested separately). + np.testing.assert_allclose( + polygon.transpose(*factored.dims).values, + factored.values, + atol=1e-3, + ) + + +def test_spherical_conserves_integral(): + """Mass conservation check on the sphere. For cos^2(lat), true integral is + 8*pi/3; the regridder on a 2-to-6-degree grid should keep the + spherical-area-weighted sum within the grid quadrature floor when + spherical=True, and miss it by ~17x more when spherical=False.""" + lon_s = np.linspace(-180, 180, 180, endpoint=False) + 1.0 + lat_s = np.linspace(-90, 90, 90, endpoint=False) + 1.0 + lon_t = np.linspace(-180, 180, 60, endpoint=False) + 3.0 + lat_t = np.linspace(-90, 90, 30, endpoint=False) + 3.0 + da = xr.DataArray( + np.cos(np.deg2rad(lat_s))[:, None] ** 2 * np.ones(lon_s.size)[None, :], + dims=("latitude", "longitude"), + coords={"latitude": lat_s, "longitude": lon_s}, + ) + target = xr.Dataset(coords={"latitude": lat_t, "longitude": lon_t}) + + out_sph = da.regrid.conservative_2d( + target, x_coord="longitude", y_coord="latitude", spherical=True + ) + out_raw = da.regrid.conservative_2d( + target, x_coord="longitude", y_coord="latitude", spherical=False + ) + + # True target spherical cell areas + dlon_arr = np.full(lon_t.size, np.deg2rad(np.mean(np.diff(lon_t)))) + lat_r = np.deg2rad(lat_t) + dlat_r = np.gradient(lat_r) + dlat_bands = np.sin(lat_r + dlat_r / 2) - np.sin(lat_r - dlat_r / 2) + a_tgt = dlat_bands[:, None] * dlon_arr[None, :] + + true_val = 8 * np.pi / 3 + sph_vals = out_sph.transpose("latitude", "longitude").values + raw_vals = out_raw.transpose("latitude", "longitude").values + err_sph = abs(float((sph_vals * a_tgt).sum()) - true_val) + err_raw = abs(float((raw_vals * a_tgt).sum()) - true_val) + # Spherical should be at least 10x more accurate than raw planar here. + assert err_sph < 0.1 * err_raw, f"err_sph={err_sph:.2e} err_raw={err_raw:.2e}" + + +# --- from_polygons (unstructured mesh) ---------------------------------------- + + +def _box_polygons(): + rng = np.random.default_rng(1) + n = 50 + cx = rng.uniform(-170, 170, n) + cy = rng.uniform(-80, 80, n) + return shapely.box(cx - 5, cy - 5, cx + 5, cy + 5) + + +def test_polygons_from_coords_periodic(): + polys = polygons_from_coords( + np.array([167.5, 172.5, -177.5, -172.5]), + np.array([-2.5, 2.5]), + periodic=True, + ) + bounds = shapely.bounds(polys) + widths = bounds[:, 2] - bounds[:, 0] + assert np.all(widths < 10.1) + + +def test_from_polygons_basic(): + src_polys = _box_polygons() + tgt_polys = polygons_from_coords( + np.linspace(-180, 180, 30, endpoint=False) + 6, + np.linspace(-90, 90, 15, endpoint=False) + 6, + ) + rgr = ConservativeRegridder.from_polygons( + src_polys, tgt_polys, source_dim="face", target_dim="cell" + ) + da = xr.DataArray( + np.arange(src_polys.size, dtype=np.float64), + dims=("face",), + ) + out = rgr.regrid(da) + assert out.dims == ("cell",) + assert out.sizes["cell"] == tgt_polys.size + + +def test_from_polygons_attaches_target_aux_coords(): + src_polys = _box_polygons() + tgt_polys = polygons_from_coords( + np.linspace(-180, 180, 12, endpoint=False) + 15, + np.linspace(-90, 90, 6, endpoint=False) + 15, + ) + region_id = np.arange(tgt_polys.size) + 100 + target_coords = xr.Dataset( + coords={ + "cell": np.arange(tgt_polys.size), + "region_id": ("cell", region_id), + } + ) + rgr = ConservativeRegridder.from_polygons( + src_polys, + tgt_polys, + source_dim="face", + target_dim="cell", + target_coords=target_coords, + ) + da = xr.DataArray(np.arange(src_polys.size, dtype=np.float64), dims=("face",)) + out = rgr.regrid(da) + assert "region_id" in out.coords + np.testing.assert_array_equal(out["region_id"].values, region_id) + + +def test_from_polygons_periodic_antimeridian(): + src_polys = np.array( + [shapely.Polygon([(175, -5), (-175, -5), (-175, 5), (175, 5)])], + dtype=object, + ) + tgt_polys = np.array( + [ + shapely.box(160, -5, 170, 5), + shapely.box(175, -5, 185, 5), + shapely.box(-170, -5, -160, 5), + ], + dtype=object, + ) + rgr = ConservativeRegridder.from_polygons( + src_polys, + tgt_polys, + source_dim="src", + target_dim="tgt", + periodic=True, + ) + out = rgr.regrid(xr.DataArray([7.0], dims=("src",))) + + assert np.isnan(out.values[0]) + assert out.values[1] == pytest.approx(7.0) + assert np.isnan(out.values[2]) + + +def test_from_polygons_mass_conservation(): + """Sum of intersected mass should match the direct A·s calculation to + machine precision for any source field.""" + + src_polys = _box_polygons() + tgt_polys = polygons_from_coords( + np.linspace(-180, 180, 36, endpoint=False) + 5, + np.linspace(-90, 90, 18, endpoint=False) + 5, + ) + rgr = ConservativeRegridder.from_polygons( + src_polys, tgt_polys, source_dim="face", target_dim="cell" + ) + rng = np.random.default_rng(3) + s = rng.normal(size=src_polys.size) + da = xr.DataArray(s, dims=("face",)) + out = rgr.regrid(da).values + # Direct mass = sum_i s_i * (sum_j A_ij). Matches output if we multiply + # output by target-covered area = sum_i A_ij. + areas = rgr._areas # (n_tgt, n_src) + tgt_covered = areas.sum(axis=1).todense() + valid = tgt_covered > 0 + direct = float((s * areas.sum(axis=0).todense()).sum()) + via_regrid = float((out[valid] * tgt_covered[valid]).sum()) + rel = abs(direct - via_regrid) / max(abs(direct), 1e-12) + assert rel < 1e-12, f"rel err {rel:.2e}" + + +def test_from_polygons_transpose_roundtrip(): + """Roundtrip mesh ↔ mesh of a constant field returns the constant.""" + src_polys = polygons_from_coords( + np.linspace(-180, 180, 24, endpoint=False) + 7.5, + np.linspace(-90, 90, 12, endpoint=False) + 7.5, + ) + tgt_polys = polygons_from_coords( + np.linspace(-180, 180, 12, endpoint=False) + 15, + np.linspace(-90, 90, 6, endpoint=False) + 15, + ) + rgr = ConservativeRegridder.from_polygons( + src_polys, tgt_polys, source_dim="src", target_dim="tgt" + ) + da = xr.DataArray(np.full(src_polys.size, 3.5), dims=("src",)) + out = rgr.regrid(da) + back = rgr.T.regrid(out) + # Inner source cells (fully covered by target cells they map to) must be 3.5. + # Edge cells may be NaN if target domain doesn't cover them. + finite = np.isfinite(back.values) + np.testing.assert_allclose(back.values[finite], 3.5, atol=1e-12) + + +def test_from_polygons_nan_propagation(): + """NaN source cells propagate through skipna=True correctly.""" + src_polys = polygons_from_coords( + np.linspace(-180, 180, 24, endpoint=False) + 7.5, + np.linspace(-90, 90, 12, endpoint=False) + 7.5, + ) + tgt_polys = polygons_from_coords( + np.linspace(-180, 180, 12, endpoint=False) + 15, + np.linspace(-90, 90, 6, endpoint=False) + 15, + ) + rgr = ConservativeRegridder.from_polygons( + src_polys, tgt_polys, source_dim="src", target_dim="tgt" + ) + vals = np.full(src_polys.size, 1.0) + vals[:5] = np.nan + da = xr.DataArray(vals, dims=("src",)) + out_keep = rgr.regrid(da, nan_threshold=1.0) + out_strict = rgr.regrid(da, nan_threshold=0.0) + # Strict should have at least as many NaNs. + strict_nans = int(np.isnan(out_strict.values).sum()) + keep_nans = int(np.isnan(out_keep.values).sum()) + assert strict_nans >= keep_nans + + +def test_from_polygons_target_outside_source_is_nan(): + """Target cells entirely outside the source domain must be NaN regardless + of skipna (regression test for a bug where the "skip mask matmul when no + NaNs" optimization returned zeros for uncovered cells).""" + + src = np.array([shapely.box(0, 0, 1, 1)], dtype=object) + tgt = polygons_from_coords( + np.linspace(100, 110, 4, endpoint=False) + 1.25, + np.linspace(100, 110, 4, endpoint=False) + 1.25, + ) + rgr = ConservativeRegridder.from_polygons(src, tgt, source_dim="src") + # Source has no NaNs → used to wrongly return 0 here. + da = xr.DataArray(np.array([5.0]), dims=("src",)) + out = rgr.regrid(da, skipna=True) + assert np.isnan(out.values).all() + out_no = rgr.regrid(da, skipna=False) + assert np.isnan(out_no.values).all() + + +def test_from_polygons_hole_is_nan(): + """A target cell fully inside a source-polygon hole should be NaN, not 0.""" + + ring_with_hole = shapely.Polygon( + [(0, 0), (10, 0), (10, 10), (0, 10)], + [[(3, 3), (7, 3), (7, 7), (3, 7)]], + ) + src = np.array([ring_with_hole], dtype=object) + tgt = polygons_from_coords( + np.linspace(0, 10, 5, endpoint=False) + 1, + np.linspace(0, 10, 5, endpoint=False) + 1, + ) + rgr = ConservativeRegridder.from_polygons(src, tgt, source_dim="src") + out = rgr.regrid(xr.DataArray([7.0], dims=("src",))) + # The cell centered at (5,5), edges [4,6]x[4,6], lies entirely in the hole. + assert int(np.isnan(out.values).sum()) >= 1 + + +def test_from_polygons_input_validation(): + # 2D polygons array rejected + p = shapely.box(0, 0, 1, 1) + arr_2d = np.array([[p, p], [p, p]], dtype=object) + with pytest.raises(ValueError, match="1D"): + ConservativeRegridder.from_polygons(arr_2d, np.array([p])) + + +# --- netCDF save / load ------------------------------------------------------- + + +def test_regrid_preserves_input_dtype(): + """Float32 in → float32 out; float64 in → float64 out. Sparse promotes + to float64 internally, so we rely on an explicit cast at the end of + ``_apply_core``.""" + da = _rect_da() + target = _rect_target() + rgr = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + assert rgr.regrid(da.astype(np.float32)).dtype == np.float32 + assert rgr.regrid(da.astype(np.float64)).dtype == np.float64 + # Integer inputs promote (float32 can't hold int32 without precision loss). + assert np.issubdtype(rgr.regrid((da * 10).astype(np.int32)).dtype, np.floating) + + +def test_to_netcdf_roundtrip_structured(tmp_path): + """Save, reload, regrid → identical output to the original regridder.""" + da = _rect_da() + target = _rect_target() + rgr = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + out_before = rgr.regrid(da).values + + path = tmp_path / "regridder.nc" + rgr.to_netcdf(path) + rgr2 = ConservativeRegridder.from_netcdf(path) + + np.testing.assert_array_equal(rgr2.regrid(da).values, out_before) + assert rgr2.x_coord == "x" + assert rgr2.y_coord == "y" + assert rgr2.spherical is False + assert rgr2._src_dims == rgr._src_dims + assert rgr2._dst_dims == rgr._dst_dims + + +def test_to_netcdf_preserves_spherical_flag(tmp_path): + lat_s = np.linspace(-90, 90, 30, endpoint=False) + 3 + lon_s = np.linspace(-180, 180, 60, endpoint=False) + 3 + lat_t = np.linspace(-90, 90, 15, endpoint=False) + 6 + lon_t = np.linspace(-180, 180, 30, endpoint=False) + 6 + da = xr.DataArray( + np.cos(np.deg2rad(lat_s))[:, None] ** 2 * np.ones(lon_s.size)[None, :], + dims=("latitude", "longitude"), + coords={"latitude": lat_s, "longitude": lon_s}, + ) + target = xr.Dataset(coords={"latitude": lat_t, "longitude": lon_t}) + + rgr = ConservativeRegridder( + da, target, x_coord="longitude", y_coord="latitude", spherical=True + ) + before = rgr.regrid(da).values + path = tmp_path / "r.nc" + rgr.to_netcdf(path) + rgr2 = ConservativeRegridder.from_netcdf(path) + assert rgr2.spherical is True + np.testing.assert_allclose(rgr2.regrid(da).values, before, atol=1e-15) + + +def test_to_netcdf_transpose_works_after_reload(tmp_path): + """A reloaded regridder can still take .T for backward regridding.""" + da = _rect_da() + target = _rect_target() + rgr = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + path = tmp_path / "r.nc" + rgr.to_netcdf(path) + rgr2 = ConservativeRegridder.from_netcdf(path) + + fwd = rgr.regrid(da) + back_original = rgr.T.regrid(fwd).values + back_reloaded = rgr2.T.regrid(fwd).values + np.testing.assert_array_equal(back_original, back_reloaded) + + +def test_to_netcdf_unstructured_roundtrip(tmp_path): + """from_polygons regridder roundtrips too (single spatial dim each side).""" + rng = np.random.default_rng(1) + cx = rng.uniform(-170, 170, 30) + cy = rng.uniform(-80, 80, 30) + src_polys = shapely.box(cx - 5, cy - 5, cx + 5, cy + 5) + tgt_polys = polygons_from_coords( + np.linspace(-180, 180, 24, endpoint=False) + 7.5, + np.linspace(-90, 90, 12, endpoint=False) + 7.5, + ) + rgr = ConservativeRegridder.from_polygons( + src_polys, tgt_polys, source_dim="face", target_dim="cell" + ) + da = xr.DataArray(rng.normal(size=30), dims=("face",)) + out_before = rgr.regrid(da).values + + path = tmp_path / "r.nc" + rgr.to_netcdf(path) + rgr2 = ConservativeRegridder.from_netcdf(path) + np.testing.assert_array_equal(rgr2.regrid(da).values, out_before) + + +def test_to_netcdf_metadata_fields(tmp_path): + """Metadata captures grid ranges, version, created timestamp.""" + da = _rect_da() + target = _rect_target() + rgr = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + path = tmp_path / "r.nc" + rgr.to_netcdf(path) + + with xr.open_dataset(path) as ds: + attrs = dict(ds.attrs) + + assert attrs["x_coord"] == "x" + assert attrs["y_coord"] == "y" + assert bool(int(attrs["spherical"])) is False + assert tuple(int(size) for size in attrs["src_shape"]) == rgr._src_shape + assert tuple(int(size) for size in attrs["dst_shape"]) == rgr._dst_shape + # Grid ranges captured when the coord is present in source/target. + assert "source_x_range" in attrs + assert "target_x_range" in attrs + assert attrs["source_x_range"][0] <= attrs["source_x_range"][1] + assert attrs["created"] + assert int(attrs["schema_version"]) == 1 + + +def test_from_netcdf_rejects_unknown_schema(tmp_path): + """Loading a file written with a future schema version raises cleanly.""" + h5py = pytest.importorskip("h5py") + da = _rect_da() + target = _rect_target() + rgr = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + path = tmp_path / "r.nc" + rgr.to_netcdf(path) + + # Bump the on-disk schema_version so the loader should reject it. + with h5py.File(path, "a") as f: + f.attrs["schema_version"] = 999 + + with pytest.raises(ValueError, match="schema version"): + ConservativeRegridder.from_netcdf(path) + + +def test_regridder_transpose_curvilinear(): + """Transpose works when the target is a curvilinear grid with different + dim names from the source.""" + da = _rect_da(ny=40, nx=80) + ny_t, nx_t = 20, 30 + xi, yi = np.meshgrid( + np.linspace(-120, 120, nx_t), + np.linspace(-60, 60, ny_t), + indexing="xy", + ) + th = np.deg2rad(15) + x2 = xi * np.cos(th) - yi * np.sin(th) + y2 = xi * np.sin(th) + yi * np.cos(th) + target = xr.Dataset(coords={"x": (("ny", "nx"), x2), "y": (("ny", "nx"), y2)}) + regridder = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + fwd = regridder.regrid(da) + assert fwd.dims == ("time", "ny", "nx") + # Going backward we should land back on (time, y, x) + back = regridder.T.regrid(fwd) + assert "y" in back.dims and "x" in back.dims + assert back.sizes["y"] == da.sizes["y"] + assert back.sizes["x"] == da.sizes["x"] From 9976970434044aa0aff85070613e18dc05263be8 Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Mon, 27 Apr 2026 14:07:16 -0500 Subject: [PATCH 03/16] Fix cross-convention longitude alignment in conservative_2d MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Source on [0, 360] vs target on [-180, 180] (and reverse) yielded NaN on half the cells: a uniform shift can't reconcile the two conventions since the mean diff is exactly 180° and round() banker's-rounds to 0. Per-value wrap source longitudes into the target's 360° window, mirror- ing format_lon, and remap area-matrix columns when wrap breaks source monotonicity. 2D / curvilinear coords keep the existing uniform-shift fallback. Adds regression test covering both directions, planar and spherical. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/xarray_regrid/methods/conservative_2d.py | 114 +++++++++++++++++-- tests/test_conservative_2d.py | 45 ++++++++ 2 files changed, 150 insertions(+), 9 deletions(-) diff --git a/src/xarray_regrid/methods/conservative_2d.py b/src/xarray_regrid/methods/conservative_2d.py index 96264f0..302d868 100644 --- a/src/xarray_regrid/methods/conservative_2d.py +++ b/src/xarray_regrid/methods/conservative_2d.py @@ -148,7 +148,9 @@ def __init__( n_threads: int | None = None, ) -> None: _check_shapely() - source_grid, target_grid = _normalize_longitude_coords(source, target, x_coord) + source_grid, target_grid, src_x_sort_idx = _normalize_longitude_coords( + source, target, x_coord + ) src_dims = _spatial_dims(source_grid, x_coord, y_coord) dst_dims = _spatial_dims(target_grid, x_coord, y_coord) if not src_dims: @@ -171,7 +173,16 @@ def __init__( self._dst_dims = dst_dims self._src_shape = tuple(int(source.sizes[d]) for d in src_dims) self._dst_shape = tuple(int(target.sizes[d]) for d in dst_dims) - self._areas = _build_intersection_areas(src_grid, dst_grid, n_threads=n_threads) + areas = _build_intersection_areas(src_grid, dst_grid, n_threads=n_threads) + if src_x_sort_idx is not None: + # Source x was sorted by _normalize_longitude_coords so polygon + # construction stayed monotone. Relabel matrix columns so column + # order matches the user's original (unsorted) data layout. + x_dim_index = src_dims.index(source[x_coord].dims[0]) + areas = _remap_columns_for_axis_sort( + areas, src_x_sort_idx, self._src_shape, x_dim_index + ) + self._areas = areas self._source_coords = source.coords.to_dataset() self._target_coords = target.coords.to_dataset() @@ -614,31 +625,64 @@ def _normalize_longitude_coords( source: xr.DataArray | xr.Dataset, target: xr.Dataset, x_coord: str, -) -> tuple[xr.DataArray | xr.Dataset, xr.Dataset]: +) -> tuple[xr.DataArray | xr.Dataset, xr.Dataset, np.ndarray | None]: """Unwrap x coordinates across the antimeridian so source and target share a contiguous longitude frame. No-op when the coord isn't present on both - objects or doesn't look like a longitude.""" + objects or doesn't look like a longitude. + + For 1D rectilinear longitudes on both sides this mirrors the per-value + wrap done by :func:`xarray_regrid.utils.format_lon` for the axis-factored + path, which is what makes a source on ``[0, 360]`` align with a target on + ``[-180, 180]`` (and vice versa). If wrapping breaks source monotonicity + the source coord is sorted in place; the caller is expected to remap the + area-matrix columns by the returned ``src_x_sort_idx`` so the final matrix + columns line up with the user's original data layout. For 2D / curvilinear + coords the existing uniform-shift fallback is kept. + """ if x_coord not in source.coords or x_coord not in target.coords: - return source, target + return source, target, None source_x = np.asarray(source[x_coord].values) target_x = np.asarray(target[x_coord].values) if not _looks_like_longitude(source_x) and not _looks_like_longitude(target_x): - return source, target + return source, target, None source_x = _unwrap_longitude(source_x) target_x = _unwrap_longitude(target_x) - # Shift target into the same 360° window as source (CF convention: x - # varies along the trailing axis). src_finite = source_x[np.isfinite(source_x)] tgt_finite = target_x[np.isfinite(target_x)] - if src_finite.size and tgt_finite.size: + + src_x_sort_idx: np.ndarray | None = None + if ( + source_x.ndim == 1 + and target_x.ndim == 1 + and src_finite.size + and tgt_finite.size + ): + # Per-value wrap source into target's 360° window — mirrors format_lon + # so source [0, 360] vs target [-180, 180] (and either reversed) + # aligns. A uniform offset can't reconcile cross-convention grids: + # mean diff is exactly 180° and round() is banker's-rounded to 0. + wrap_point = float((tgt_finite[0] + tgt_finite[-1] + 360.0) / 2.0) + source_x = np.where( + source_x < wrap_point - 360.0, source_x + 360.0, source_x + ) + source_x = np.where(source_x > wrap_point, source_x - 360.0, source_x) + diffs = np.diff(source_x) + if not (np.all(diffs > 0) or np.all(diffs < 0)): + src_x_sort_idx = np.argsort(source_x, kind="stable") + source_x = source_x[src_x_sort_idx] + elif src_finite.size and tgt_finite.size: + # 2D / curvilinear: fall back to uniform shift of target into source's + # window. Doesn't handle cross-convention but preserves the existing + # antimeridian-crossing behavior for 2D coords. target_x = target_x + _periodic_offset( float(src_finite.mean()), float(tgt_finite.mean()) ) return ( utils.update_coord(source, x_coord, source_x), cast(xr.Dataset, utils.update_coord(target, x_coord, target_x)), + src_x_sort_idx, ) @@ -723,6 +767,58 @@ def _unwrap_ring(ring: np.ndarray) -> np.ndarray: return new_ring +def _remap_columns_for_axis_sort( + areas: "sparse.COO | np.ndarray", + sort_idx: np.ndarray, + src_shape: tuple[int, ...], + axis_index: int, +) -> "sparse.COO | np.ndarray": + """Relabel the column indices of an ``(n_dst, prod(src_shape))`` area + matrix so that columns appear in the user's original source-data order + after ``sort_idx`` was applied along ``axis_index`` of ``src_shape``. + + A row-major source flat index ``c = unravel(c, src_shape)`` has its + ``axis_index`` component ``i_sorted`` permuted via + ``i_orig = sort_idx[i_sorted]``; all other components are untouched. So + the new flat index is ``c_new = c // stride * stride + (i_orig - i_sorted) * inner + ...``. + For separable 1D-rect grids (the only case that triggers this today) the + sorted axis sits between an outer block of size ``outer`` and an inner + block of size ``inner`` with ``stride = nx * inner``. + """ + nx = int(src_shape[axis_index]) + inner = int(np.prod(src_shape[axis_index + 1 :])) + stride = nx * inner + sort_idx = np.asarray(sort_idx, dtype=np.int64) + + if _HAS_SPARSE and isinstance(areas, sparse.COO): + old_col = np.asarray(areas.coords[1], dtype=np.int64) + outer_block = (old_col // stride) * stride + within = old_col % stride + i_sorted = within // inner + rest = within % inner + new_col = outer_block + sort_idx[i_sorted] * inner + rest + coords = np.stack([np.asarray(areas.coords[0], dtype=np.int64), new_col]) + return sparse.COO( + coords=coords, + data=np.asarray(areas.data), + shape=areas.shape, + has_duplicates=False, + sorted=False, + ) + + arr = np.asarray(areas) + n_cells = arr.shape[1] + inv_sort_idx = np.empty_like(sort_idx) + inv_sort_idx[sort_idx] = np.arange(sort_idx.size, dtype=sort_idx.dtype) + cells = np.arange(n_cells, dtype=np.int64) + outer_block = (cells // stride) * stride + within = cells % stride + i_orig = within // inner + rest = within % inner + inv_perm = outer_block + inv_sort_idx[i_orig] * inner + rest + return arr[:, inv_perm] + + def _transpose_weights( w: "sparse.COO | np.ndarray", *, sort: bool = False ) -> "sparse.COO | np.ndarray": diff --git a/tests/test_conservative_2d.py b/tests/test_conservative_2d.py index 75864fc..668114e 100644 --- a/tests/test_conservative_2d.py +++ b/tests/test_conservative_2d.py @@ -110,6 +110,51 @@ def test_antimeridian_rectilinear_constant(): np.testing.assert_allclose(out.values, 2.5, atol=1e-12) +def test_cross_convention_longitude_alignment(): + """Source on [0, 360] with target on [-180, 180] (and vice versa) must + align — a uniform shift can't reconcile the two conventions, so per-value + wrap of source longitudes is required. Regression: previously yielded + NaN on half the target cells because banker's rounding on the exact-180° + mean diff produced a zero offset.""" + src_vals = np.array([1.0, 2.0, 3.0, 4.0]) + tgt_vals_neg = np.array([-135.0, -45.0, 45.0, 135.0]) + src_vals_neg_x = np.array([45.0, 135.0, 225.0, 315.0]) + da = xr.DataArray( + np.broadcast_to(src_vals, (2, 4)).copy(), + dims=("latitude", "longitude"), + coords={"latitude": [-30.0, 30.0], "longitude": src_vals_neg_x}, + ) + target = xr.Dataset( + coords={"latitude": [-30.0, 30.0], "longitude": tgt_vals_neg} + ) + expected = da.regrid.conservative(target).transpose("latitude", "longitude") + out_planar = da.regrid.conservative_2d( + target, x_coord="longitude", y_coord="latitude" + ).transpose("latitude", "longitude") + out_spherical = da.regrid.conservative_2d( + target, x_coord="longitude", y_coord="latitude", spherical=True + ).transpose("latitude", "longitude") + np.testing.assert_allclose(out_planar.values, expected.values, atol=1e-12) + np.testing.assert_allclose(out_spherical.values, expected.values, atol=1e-12) + + # Reverse: source on [-180, 180], target on [0, 360]. + da_rev = xr.DataArray( + np.broadcast_to(src_vals, (2, 4)).copy(), + dims=("latitude", "longitude"), + coords={"latitude": [-30.0, 30.0], "longitude": tgt_vals_neg}, + ) + target_rev = xr.Dataset( + coords={"latitude": [-30.0, 30.0], "longitude": src_vals_neg_x} + ) + expected_rev = da_rev.regrid.conservative(target_rev).transpose( + "latitude", "longitude" + ) + out_rev = da_rev.regrid.conservative_2d( + target_rev, x_coord="longitude", y_coord="latitude" + ).transpose("latitude", "longitude") + np.testing.assert_allclose(out_rev.values, expected_rev.values, atol=1e-12) + + def test_polygon_nan_threshold_invalid(): da = _rect_da() with pytest.raises(ValueError): From 787dcb91e9171688967ee7fdcf32cad2b03491d1 Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Wed, 22 Apr 2026 19:33:05 -0500 Subject: [PATCH 04/16] Rework regions demo around real data + add notebooks extra Swaps the synthetic polygon demo for xarray's air_temperature tutorial dataset aggregated onto US states dissolved from geodatasets.ncovr. Same workflow end-to-end (from_polygons, map+bar plot, conservation check, save/reload) but with recognizable inputs. Adds a [notebooks] extras group covering the runtime deps the demos need (matplotlib, pooch, geopandas, geodatasets); shapely/h5netcdf/ sparse/dask stay in [conservative-2d] and [accel] so the two can be combined. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../demos/demo_conservative_2d_regions.ipynb | 304 ++++++++++-------- pyproject.toml | 9 + 2 files changed, 185 insertions(+), 128 deletions(-) diff --git a/docs/notebooks/demos/demo_conservative_2d_regions.ipynb b/docs/notebooks/demos/demo_conservative_2d_regions.ipynb index 1a837b3..58eec9e 100644 --- a/docs/notebooks/demos/demo_conservative_2d_regions.ipynb +++ b/docs/notebooks/demos/demo_conservative_2d_regions.ipynb @@ -2,34 +2,42 @@ "cells": [ { "cell_type": "markdown", - "id": "0", + "id": "e5c70992", "metadata": {}, "source": [ - "# Conservative 2D regrid — regions (grid → arbitrary polygons)\n", + "# Conservative 2D regrid — regions (grid → country/state polygons)\n", "\n", - "Gridded data → arbitrary polygon regions (countries, watersheds, ocean\n", - "basins, protected areas) is a canonical xagg-style workflow. Because\n", - "regions aren't a grid at all, `.conservative` can't express it;\n", + "Gridded data → arbitrary polygon regions (countries, states, watersheds,\n", + "ocean basins) is a canonical xagg-style workflow. Because regions aren't\n", + "a grid at all, `.conservative` can't express it;\n", "`ConservativeRegridder.from_polygons` takes a numpy array of shapely\n", "polygons and produces one conservatively-averaged value per region.\n", "\n", - "This notebook uses hand-built synthetic regions so it runs with zero\n", - "external downloads — swap in `geopandas.read_file(...)` for a real\n", - "shapefile and the rest is identical." + "This notebook uses **xarray's air-temperature tutorial dataset** (NMC\n", + "reanalysis, North America, 2.5° × 2.5°) and aggregates it onto **US\n", + "states** built from the `geoda.ncovr` county boundaries shipped in\n", + "`geodatasets`." ] }, { "cell_type": "code", "execution_count": null, - "id": "1", - "metadata": {}, + "id": "b28b1f2e", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-23T00:29:04.054863Z", + "iopub.status.busy": "2026-04-23T00:29:04.054723Z", + "iopub.status.idle": "2026-04-23T00:29:05.630484Z", + "shell.execute_reply": "2026-04-23T00:29:05.629984Z" + } + }, "outputs": [], "source": [ "import numpy as np\n", "import xarray as xr\n", "import matplotlib.pyplot as plt\n", - "from matplotlib.collections import PolyCollection\n", - "import shapely\n", + "import geopandas as gpd\n", + "import geodatasets\n", "\n", "import xarray_regrid # noqa: F401\n", "from xarray_regrid import ConservativeRegridder, polygons_from_coords" @@ -37,229 +45,269 @@ }, { "cell_type": "markdown", - "id": "2", + "id": "537c0ed3", "metadata": {}, "source": [ - "## Source — structured lat/lon field\n", + "## Source — NCEP reanalysis surface air temperature\n", "\n", - "A smooth analytic field with recognizable geographic pattern: a warm\n", - "band following the equator modulated by an east/west tilt. Gives visibly\n", - "different regional means depending on region location." + "The tutorial dataset is 4×daily surface air temperature on a 2.5°\n", + "lat/lon grid covering North America. We take the annual mean over\n", + "2013–2014 and convert to °C. Longitudes are shifted from the native\n", + "[0, 360) convention to [-180, 180) and latitudes flipped to ascending\n", + "so the grid aligns with the state polygons' CRS." ] }, { "cell_type": "code", "execution_count": null, - "id": "3", - "metadata": {}, + "id": "721e935c", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-23T00:29:05.632162Z", + "iopub.status.busy": "2026-04-23T00:29:05.631947Z", + "iopub.status.idle": "2026-04-23T00:29:05.712429Z", + "shell.execute_reply": "2026-04-23T00:29:05.712026Z" + } + }, "outputs": [], "source": [ - "lat = np.linspace(-80, 80, 160) + 0.5\n", - "lon = np.linspace(-180, 180, 360, endpoint=False) + 0.5\n", - "Lo, La = np.meshgrid(lon, lat)\n", - "field = np.cos(np.deg2rad(La)) ** 2 + 0.4 * np.sin(np.deg2rad(Lo))\n", - "src = xr.DataArray(\n", - " field,\n", - " dims=(\"latitude\", \"longitude\"),\n", - " coords={\"latitude\": lat, \"longitude\": lon},\n", - ")" + "ds = xr.tutorial.open_dataset(\"air_temperature\")\n", + "air = (ds[\"air\"].mean(\"time\") - 273.15).sortby(\"lat\")\n", + "air = air.assign_coords(lon=(((air.lon + 180) % 360) - 180)).sortby(\"lon\")\n", + "air.attrs[\"units\"] = \"degC\"\n", + "air.name = \"mean_air_temperature\"\n", + "air" ] }, { "cell_type": "markdown", - "id": "4", + "id": "434441b2", "metadata": {}, "source": [ - "## Regions — five hand-built polygons of varied shape\n", + "## Regions — US states from `geodatasets`\n", "\n", - "Box, circle, rotated rectangle, L-shape, and a polygon with a hole —\n", - "exercising the range of geometry the regridder accepts. These overlap\n", - "in places, which is fine: each region gets its own independent\n", - "area-weighted mean." + "`geodatasets.get_path(\"geoda.ncovr\")` returns a GeoPackage of the 49\n", + "contiguous-US counties (+ DC). Dissolving on `STATE_NAME` gives one\n", + "(Multi)Polygon per state — exactly the input\n", + "`ConservativeRegridder.from_polygons` expects. Note the air-temperature\n", + "grid does not cover Alaska or Hawaii, which is also why ncovr is a\n", + "natural fit." ] }, { "cell_type": "code", "execution_count": null, - "id": "5", - "metadata": {}, + "id": "8c6437b2", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-23T00:29:05.713599Z", + "iopub.status.busy": "2026-04-23T00:29:05.713519Z", + "iopub.status.idle": "2026-04-23T00:29:06.019425Z", + "shell.execute_reply": "2026-04-23T00:29:06.019044Z" + } + }, "outputs": [], "source": [ - "def rotated_rect(cx, cy, w, h, angle_deg):\n", - " theta = np.deg2rad(angle_deg)\n", - " c = np.array([[-w/2, -h/2], [w/2, -h/2], [w/2, h/2], [-w/2, h/2]])\n", - " R = np.array([[np.cos(theta), -np.sin(theta)],\n", - " [np.sin(theta), np.cos(theta)]])\n", - " return shapely.Polygon(c @ R.T + np.array([cx, cy]))\n", - "\n", - "def circle(cx, cy, r, n=48):\n", - " t = np.linspace(0, 2 * np.pi, n, endpoint=False)\n", - " return shapely.Polygon(np.column_stack([cx + r*np.cos(t), cy + r*np.sin(t)]))\n", - "\n", - "region_names = [\n", - " \"equatorial band\",\n", - " \"northern island\",\n", - " \"rotated block\",\n", - " \"L-shape\",\n", - " \"ring\",\n", - "]\n", - "region_polys = np.array([\n", - " shapely.box(-180, -15, 180, 15),\n", - " circle(cx=-60, cy=50, r=18),\n", - " rotated_rect(cx=80, cy=40, w=50, h=25, angle_deg=30),\n", - " shapely.unary_union([\n", - " shapely.box(-140, -60, -100, -20),\n", - " shapely.box(-140, -60, -60, -50),\n", - " ]),\n", - " shapely.Polygon(\n", - " shell=[(140, -40), (170, -40), (170, -5), (140, -5)],\n", - " holes=[[(148, -30), (162, -30), (162, -12), (148, -12)]],\n", - " ),\n", - "], dtype=object)\n", - "print(f\"{len(region_polys)} regions, areas (deg²): \"\n", - " f\"{[f'{shapely.area(p):.0f}' for p in region_polys]}\")" + "counties = gpd.read_file(geodatasets.get_path(\"geoda.ncovr\"))\n", + "states = (\n", + " counties.dissolve(by=\"STATE_NAME\", aggfunc=\"first\")\n", + " .reset_index()[[\"STATE_NAME\", \"geometry\"]]\n", + " .sort_values(\"STATE_NAME\")\n", + " .reset_index(drop=True)\n", + ")\n", + "print(f\"{len(states)} states/DC, CRS={states.crs}, \"\n", + " f\"bounds={states.total_bounds.round(1).tolist()}\")\n", + "states.head(3)" ] }, { "cell_type": "markdown", - "id": "6", + "id": "3a3b8ac5", "metadata": {}, "source": [ "## Build the regridder and apply\n", "\n", - "`from_polygons` takes source + target polygon arrays. We build the\n", - "source polygons from the grid's 1D coords via the `polygons_from_coords`\n", - "helper; the data gets flattened to match." + "`from_polygons` takes source + target polygon arrays. Source polygons\n", + "are built from the 1D grid coords via `polygons_from_coords`; target\n", + "polygons are the states' `geometry` column as a numpy array. The data\n", + "is flattened to a single `src_cell` dimension to match." ] }, { "cell_type": "code", "execution_count": null, - "id": "7", - "metadata": {}, + "id": "da3e70c8", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-23T00:29:06.020668Z", + "iopub.status.busy": "2026-04-23T00:29:06.020586Z", + "iopub.status.idle": "2026-04-23T00:29:06.929922Z", + "shell.execute_reply": "2026-04-23T00:29:06.929498Z" + } + }, "outputs": [], "source": [ - "src_polys = polygons_from_coords(lon, lat)\n", + "src_polys = polygons_from_coords(air.lon.values, air.lat.values)\n", + "tgt_polys = states.geometry.to_numpy()\n", "\n", "rgr = ConservativeRegridder.from_polygons(\n", " source_polygons=src_polys,\n", - " target_polygons=region_polys,\n", + " target_polygons=tgt_polys,\n", " source_dim=\"src_cell\",\n", - " target_dim=\"region\",\n", - " target_coords=xr.Dataset(coords={\"region\": region_names}),\n", + " target_dim=\"state\",\n", + " target_coords=xr.Dataset(coords={\"state\": states.STATE_NAME.values}),\n", ")\n", - "src_flat = xr.DataArray(src.values.ravel(), dims=(\"src_cell\",))\n", - "regional = rgr.regrid(src_flat)\n", - "regional" + "\n", + "src_flat = xr.DataArray(air.values.ravel(), dims=(\"src_cell\",))\n", + "state_mean = rgr.regrid(src_flat)\n", + "state_mean.attrs[\"units\"] = \"degC\"\n", + "state_mean.to_series().sort_values().round(2)" ] }, { "cell_type": "markdown", - "id": "8", + "id": "3542f836", "metadata": {}, "source": [ - "## Source field + region outlines + regional means" + "## Map + ranked bar chart\n", + "\n", + "States filled by area-weighted mean temperature, with the source grid\n", + "shown underneath for reference." ] }, { "cell_type": "code", "execution_count": null, - "id": "9", - "metadata": {}, + "id": "bb8d0dda", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-23T00:29:06.931151Z", + "iopub.status.busy": "2026-04-23T00:29:06.931080Z", + "iopub.status.idle": "2026-04-23T00:29:07.145022Z", + "shell.execute_reply": "2026-04-23T00:29:07.144617Z" + } + }, "outputs": [], "source": [ "fig, (ax_map, ax_bar) = plt.subplots(\n", - " 1, 2, figsize=(13, 4.5), gridspec_kw={\"width_ratios\": [1.6, 1]},\n", + " 1, 2, figsize=(14, 6), gridspec_kw={\"width_ratios\": [1.6, 1]},\n", ")\n", - "src.plot(ax=ax_map, cmap=\"viridis\", add_colorbar=True,\n", - " cbar_kwargs={\"shrink\": 0.7, \"label\": \"source field\"})\n", - "patches = [np.asarray(p.exterior.coords) for p in region_polys]\n", - "pc = PolyCollection(\n", - " patches, facecolor=\"none\", edgecolor=\"white\", lw=1.4,\n", + "\n", + "air.plot(ax=ax_map, cmap=\"coolwarm\", alpha=0.5,\n", + " cbar_kwargs={\"shrink\": 0.65, \"label\": \"grid mean T [°C]\"})\n", + "\n", + "states_plot = states.assign(mean_T=state_mean.values)\n", + "states_plot.plot(\n", + " column=\"mean_T\", cmap=\"coolwarm\", ax=ax_map,\n", + " edgecolor=\"black\", linewidth=0.4,\n", + " vmin=float(air.min()), vmax=float(air.max()),\n", ")\n", - "ax_map.add_collection(pc)\n", - "for name, poly in zip(region_names, region_polys):\n", - " c = poly.representative_point()\n", - " ax_map.annotate(name, (c.x, c.y), color=\"white\", fontsize=8,\n", - " ha=\"center\", va=\"center\")\n", - "ax_map.set_title(\"source + region outlines\")\n", - "ax_map.set_xlim(-180, 180); ax_map.set_ylim(-80, 80)\n", + "ax_map.set_xlim(-128, -65); ax_map.set_ylim(22, 52)\n", + "ax_map.set_title(\"Annual mean surface T — states vs. source grid\")\n", + "ax_map.set_xlabel(\"longitude\"); ax_map.set_ylabel(\"latitude\")\n", "\n", - "ax_bar.barh(region_names, regional.values, color=\"tab:blue\")\n", - "ax_bar.set_xlabel(\"area-weighted regional mean\")\n", - "ax_bar.invert_yaxis()\n", + "ordered = state_mean.to_series().sort_values()\n", + "colors = plt.cm.coolwarm(\n", + " (ordered.values - ordered.min()) / (ordered.max() - ordered.min())\n", + ")\n", + "ax_bar.barh(ordered.index, ordered.values, color=colors)\n", + "ax_bar.set_xlabel(\"area-weighted mean T [°C]\")\n", + "ax_bar.tick_params(axis=\"y\", labelsize=7)\n", "ax_bar.grid(axis=\"x\", alpha=0.3)\n", "plt.tight_layout()" ] }, { "cell_type": "markdown", - "id": "10", + "id": "803247d2", "metadata": {}, "source": [ "## Conservation check\n", "\n", - "Area-weighted sum of regional means × region areas should match the\n", - "direct A·s computation from the regridder's internal area matrix." + "The area-weighted sum over regions (using the regridder's internal\n", + "area matrix) should match the direct A·s computation — this is the\n", + "conservation property the method is named for." ] }, { "cell_type": "code", "execution_count": null, - "id": "11", - "metadata": {}, + "id": "041a51f3", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-23T00:29:07.146317Z", + "iopub.status.busy": "2026-04-23T00:29:07.146242Z", + "iopub.status.idle": "2026-04-23T00:29:07.242581Z", + "shell.execute_reply": "2026-04-23T00:29:07.242177Z" + } + }, "outputs": [], "source": [ - "A = rgr._areas # sparse (n_regions, n_src)\n", + "A = rgr._areas # sparse (n_states, n_src)\n", "tgt_area = np.ravel(A.sum(axis=1).todense())\n", "src_cover = np.ravel(A.sum(axis=0).todense())\n", "\n", - "direct = float((src.values.ravel() * src_cover).sum())\n", - "via_regrid = float((regional.values * tgt_area).sum())\n", + "direct = float((air.values.ravel() * src_cover).sum())\n", + "via_regrid = float((state_mean.values * tgt_area).sum())\n", "print(f\"direct A·s : {direct:.6f}\")\n", - "print(f\"Σ regional_mean · a_dst : {via_regrid:.6f}\")\n", + "print(f\"Σ state_mean · a_state : {via_regrid:.6f}\")\n", "print(f\"relative error : {abs(direct - via_regrid) / abs(direct):.2e}\")" ] }, { "cell_type": "markdown", - "id": "12", + "id": "ff49d6b7", "metadata": {}, "source": [ - "## Reuse: persist the regridder to netCDF\n", + "## Reuse: persist the regridder, apply to summer vs. winter\n", "\n", - "The weight matrix is reusable. For any workflow that repeatedly\n", - "aggregates new source data onto the same region set, save once,\n", - "reload on subsequent runs to skip the intersection build." + "The weight matrix is reusable across any source field on the same\n", + "grid. Save once, reload to skip the intersection build, then apply to\n", + "seasonally-filtered data to get per-state JJA vs. DJF means." ] }, { "cell_type": "code", "execution_count": null, - "id": "13", - "metadata": {}, + "id": "8fa88419", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-23T00:29:07.244167Z", + "iopub.status.busy": "2026-04-23T00:29:07.244090Z", + "iopub.status.idle": "2026-04-23T00:29:07.560391Z", + "shell.execute_reply": "2026-04-23T00:29:07.559962Z" + } + }, "outputs": [], "source": [ "import tempfile\n", "from pathlib import Path\n", - "path = Path(tempfile.gettempdir()) / \"regions_regridder.nc\"\n", + "path = Path(tempfile.gettempdir()) / \"states_regridder.nc\"\n", "rgr.to_netcdf(path)\n", - "print(f\"wrote {path} ({path.stat().st_size / 1024:.1f} KB)\")\n", + "print(f\"wrote {path.name} ({path.stat().st_size / 1024:.1f} KB)\")\n", "\n", "rgr2 = ConservativeRegridder.from_netcdf(path)\n", - "# Apply to a different source field, same grid:\n", - "other = xr.DataArray(\n", - " (np.sin(np.deg2rad(Lo)) ** 2).ravel(), dims=(\"src_cell\",),\n", - ")\n", - "print(\"regional means on a different field:\")\n", - "for n, v in zip(region_names, rgr2.regrid(other).values):\n", - " print(f\" {n:20s}: {v:+.4f}\")" + "\n", + "def seasonal_mean(months):\n", + " sub = ds[\"air\"].sel(time=ds[\"time.month\"].isin(months)).mean(\"time\") - 273.15\n", + " sub = sub.sortby(\"lat\")\n", + " sub = sub.assign_coords(lon=(((sub.lon + 180) % 360) - 180)).sortby(\"lon\")\n", + " flat = xr.DataArray(sub.values.ravel(), dims=(\"src_cell\",))\n", + " return rgr2.regrid(flat)\n", + "\n", + "summer = seasonal_mean([6, 7, 8])\n", + "winter = seasonal_mean([12, 1, 2])\n", + "amplitude = (summer - winter).to_series().rename(\"JJA − DJF [°C]\").round(1)\n", + "print(\"largest seasonal swing:\")\n", + "print(amplitude.sort_values(ascending=False).head(5))\n", + "print(\"\\nsmallest seasonal swing (maritime / subtropical):\")\n", + "print(amplitude.sort_values().head(5))" ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, diff --git a/pyproject.toml b/pyproject.toml index 89501a2..fe93d6a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,6 +60,15 @@ benchmarking = [ "pooch", "cftime", # required for decode time of test netCDF files ] +notebooks = [ + # Extra dependencies needed to run docs/notebooks/demos/*.ipynb. + # Combine with [conservative-2d] and [accel] for the full demo set, + # e.g.: pip install "xarray-regrid[notebooks,conservative-2d,accel]" + "matplotlib", + "pooch", # xr.tutorial.open_dataset backend + "geopandas", # polygon regions demo + "geodatasets", # ships the ncovr US county boundaries +] dev = [ "hatch", "ruff", From fe6a945dc852b66a1175dd5ff71b973a7eecc445 Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Mon, 27 Apr 2026 14:23:44 -0500 Subject: [PATCH 05/16] Match bar-chart colormap to the states map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The map's vmin/vmax span the full source-grid temperature range, but the bar chart was normalizing to the bar values' own min/max — so a state at the cool end was deeper blue on the bar than on the map. Reuse the map's vmin/vmax for the bar. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../demos/demo_conservative_2d_regions.ipynb | 906 ++++++++++++++++-- 1 file changed, 815 insertions(+), 91 deletions(-) diff --git a/docs/notebooks/demos/demo_conservative_2d_regions.ipynb b/docs/notebooks/demos/demo_conservative_2d_regions.ipynb index 58eec9e..2e229eb 100644 --- a/docs/notebooks/demos/demo_conservative_2d_regions.ipynb +++ b/docs/notebooks/demos/demo_conservative_2d_regions.ipynb @@ -21,16 +21,9 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "b28b1f2e", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-23T00:29:04.054863Z", - "iopub.status.busy": "2026-04-23T00:29:04.054723Z", - "iopub.status.idle": "2026-04-23T00:29:05.630484Z", - "shell.execute_reply": "2026-04-23T00:29:05.629984Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", @@ -59,17 +52,642 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "721e935c", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-23T00:29:05.632162Z", - "iopub.status.busy": "2026-04-23T00:29:05.631947Z", - "iopub.status.idle": "2026-04-23T00:29:05.712429Z", - "shell.execute_reply": "2026-04-23T00:29:05.712026Z" + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.DataArray 'mean_air_temperature' (lat: 25, lon: 53)> Size: 11kB\n",
+       "array([[ 25.21615068,  25.23573973,  24.96414384, ...,  24.18820548,\n",
+       "         24.13144521,  24.15510274],\n",
+       "       [ 24.97920205,  24.78700685,  24.32039384, ...,  23.70954795,\n",
+       "         23.6270274 ,  23.29383562],\n",
+       "       [ 24.49986301,  23.80333219,  23.47931507, ...,  23.66092466,\n",
+       "         23.13796233,  22.66645548],\n",
+       "       ...,\n",
+       "       [ -8.3812363 ,  -8.82269178,  -9.08830479, ..., -22.54210959,\n",
+       "        -19.56648973, -15.43440068],\n",
+       "       [-10.41560616, -10.35602397, -10.40066096, ..., -23.39409589,\n",
+       "        -21.56424315, -18.79073973],\n",
+       "       [-12.77355822, -12.96694863, -13.26337329, ..., -22.33409932,\n",
+       "        -21.21188356, -19.71195205]], shape=(25, 53))\n",
+       "Coordinates:\n",
+       "  * lat      (lat) float32 100B 15.0 17.5 20.0 22.5 25.0 ... 67.5 70.0 72.5 75.0\n",
+       "  * lon      (lon) float32 212B -160.0 -157.5 -155.0 ... -35.0 -32.5 -30.0\n",
+       "Attributes:\n",
+       "    long_name:     4xDaily Air temperature at sigma level 995\n",
+       "    units:         degC\n",
+       "    precision:     2\n",
+       "    GRIB_id:       11\n",
+       "    GRIB_name:     TMP\n",
+       "    var_desc:      Air temperature\n",
+       "    dataset:       NMC Reanalysis\n",
+       "    level_desc:    Surface\n",
+       "    statistic:     Individual Obs\n",
+       "    parent_stat:   Other\n",
+       "    actual_range:  [185.16 322.1 ]
" + ], + "text/plain": [ + " Size: 11kB\n", + "array([[ 25.21615068, 25.23573973, 24.96414384, ..., 24.18820548,\n", + " 24.13144521, 24.15510274],\n", + " [ 24.97920205, 24.78700685, 24.32039384, ..., 23.70954795,\n", + " 23.6270274 , 23.29383562],\n", + " [ 24.49986301, 23.80333219, 23.47931507, ..., 23.66092466,\n", + " 23.13796233, 22.66645548],\n", + " ...,\n", + " [ -8.3812363 , -8.82269178, -9.08830479, ..., -22.54210959,\n", + " -19.56648973, -15.43440068],\n", + " [-10.41560616, -10.35602397, -10.40066096, ..., -23.39409589,\n", + " -21.56424315, -18.79073973],\n", + " [-12.77355822, -12.96694863, -13.26337329, ..., -22.33409932,\n", + " -21.21188356, -19.71195205]], shape=(25, 53))\n", + "Coordinates:\n", + " * lat (lat) float32 100B 15.0 17.5 20.0 22.5 25.0 ... 67.5 70.0 72.5 75.0\n", + " * lon (lon) float32 212B -160.0 -157.5 -155.0 ... -35.0 -32.5 -30.0\n", + "Attributes:\n", + " long_name: 4xDaily Air temperature at sigma level 995\n", + " units: degC\n", + " precision: 2\n", + " GRIB_id: 11\n", + " GRIB_name: TMP\n", + " var_desc: Air temperature\n", + " dataset: NMC Reanalysis\n", + " level_desc: Surface\n", + " statistic: Individual Obs\n", + " parent_stat: Other\n", + " actual_range: [185.16 322.1 ]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" } - }, - "outputs": [], + ], "source": [ "ds = xr.tutorial.open_dataset(\"air_temperature\")\n", "air = (ds[\"air\"].mean(\"time\") - 273.15).sortby(\"lat\")\n", @@ -96,17 +714,74 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "8c6437b2", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-23T00:29:05.713599Z", - "iopub.status.busy": "2026-04-23T00:29:05.713519Z", - "iopub.status.idle": "2026-04-23T00:29:06.019425Z", - "shell.execute_reply": "2026-04-23T00:29:06.019044Z" + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "49 states/DC, CRS=EPSG:4326, bounds=[-124.7, 25.0, -67.0, 49.4]\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
STATE_NAMEgeometry
0AlabamaPOLYGON ((-87.62572 30.87672, -87.61593 30.848...
1ArizonaPOLYGON ((-113.32838 32.04356, -114.82097 32.4...
2ArkansasPOLYGON ((-93.4789 33.02139, -93.51174 33.0211...
\n", + "
" + ], + "text/plain": [ + " STATE_NAME geometry\n", + "0 Alabama POLYGON ((-87.62572 30.87672, -87.61593 30.848...\n", + "1 Arizona POLYGON ((-113.32838 32.04356, -114.82097 32.4...\n", + "2 Arkansas POLYGON ((-93.4789 33.02139, -93.51174 33.0211..." + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" } - }, - "outputs": [], + ], "source": [ "counties = gpd.read_file(geodatasets.get_path(\"geoda.ncovr\"))\n", "states = (\n", @@ -135,17 +810,71 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "da3e70c8", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-23T00:29:06.020668Z", - "iopub.status.busy": "2026-04-23T00:29:06.020586Z", - "iopub.status.idle": "2026-04-23T00:29:06.929922Z", - "shell.execute_reply": "2026-04-23T00:29:06.929498Z" + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "state\n", + "Minnesota 4.30\n", + "North Dakota 4.65\n", + "Maine 5.08\n", + "Wyoming 5.53\n", + "Montana 5.78\n", + "Vermont 6.02\n", + "Wisconsin 6.06\n", + "Idaho 6.43\n", + "Michigan 6.75\n", + "South Dakota 7.12\n", + "New Hampshire 7.17\n", + "New York 7.28\n", + "Colorado 7.48\n", + "Iowa 8.46\n", + "Rhode Island 8.50\n", + "Connecticut 8.72\n", + "Washington 8.83\n", + "Massachusetts 8.84\n", + "Oregon 8.99\n", + "Pennsylvania 9.25\n", + "Utah 9.38\n", + "Nebraska 9.57\n", + "Ohio 9.64\n", + "District of Columbia 9.97\n", + "Nevada 10.17\n", + "Illinois 10.24\n", + "West Virginia 10.26\n", + "Indiana 10.47\n", + "New Jersey 11.43\n", + "Maryland 11.47\n", + "Virginia 11.94\n", + "Missouri 12.05\n", + "Kentucky 12.20\n", + "Kansas 12.61\n", + "Delaware 12.77\n", + "New Mexico 12.90\n", + "Tennessee 13.74\n", + "California 14.46\n", + "North Carolina 14.90\n", + "Arkansas 15.33\n", + "Oklahoma 15.34\n", + "South Carolina 15.91\n", + "Arizona 16.53\n", + "Georgia 16.54\n", + "Alabama 16.61\n", + "Mississippi 17.10\n", + "Texas 18.35\n", + "Louisiana 18.85\n", + "Florida 21.93\n", + "dtype: float64" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" } - }, - "outputs": [], + ], "source": [ "src_polys = polygons_from_coords(air.lon.values, air.lat.values)\n", "tgt_polys = states.geometry.to_numpy()\n", @@ -179,43 +908,9 @@ "cell_type": "code", "execution_count": null, "id": "bb8d0dda", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-23T00:29:06.931151Z", - "iopub.status.busy": "2026-04-23T00:29:06.931080Z", - "iopub.status.idle": "2026-04-23T00:29:07.145022Z", - "shell.execute_reply": "2026-04-23T00:29:07.144617Z" - } - }, + "metadata": {}, "outputs": [], - "source": [ - "fig, (ax_map, ax_bar) = plt.subplots(\n", - " 1, 2, figsize=(14, 6), gridspec_kw={\"width_ratios\": [1.6, 1]},\n", - ")\n", - "\n", - "air.plot(ax=ax_map, cmap=\"coolwarm\", alpha=0.5,\n", - " cbar_kwargs={\"shrink\": 0.65, \"label\": \"grid mean T [°C]\"})\n", - "\n", - "states_plot = states.assign(mean_T=state_mean.values)\n", - "states_plot.plot(\n", - " column=\"mean_T\", cmap=\"coolwarm\", ax=ax_map,\n", - " edgecolor=\"black\", linewidth=0.4,\n", - " vmin=float(air.min()), vmax=float(air.max()),\n", - ")\n", - "ax_map.set_xlim(-128, -65); ax_map.set_ylim(22, 52)\n", - "ax_map.set_title(\"Annual mean surface T — states vs. source grid\")\n", - "ax_map.set_xlabel(\"longitude\"); ax_map.set_ylabel(\"latitude\")\n", - "\n", - "ordered = state_mean.to_series().sort_values()\n", - "colors = plt.cm.coolwarm(\n", - " (ordered.values - ordered.min()) / (ordered.max() - ordered.min())\n", - ")\n", - "ax_bar.barh(ordered.index, ordered.values, color=colors)\n", - "ax_bar.set_xlabel(\"area-weighted mean T [°C]\")\n", - "ax_bar.tick_params(axis=\"y\", labelsize=7)\n", - "ax_bar.grid(axis=\"x\", alpha=0.3)\n", - "plt.tight_layout()" - ] + "source": "fig, (ax_map, ax_bar) = plt.subplots(\n 1, 2, figsize=(14, 6), gridspec_kw={\"width_ratios\": [1.6, 1]},\n)\n\nair.plot(ax=ax_map, cmap=\"coolwarm\", alpha=0.5,\n cbar_kwargs={\"shrink\": 0.65, \"label\": \"grid mean T [°C]\"})\n\nvmin, vmax = float(air.min()), float(air.max())\nstates_plot = states.assign(mean_T=state_mean.values)\nstates_plot.plot(\n column=\"mean_T\", cmap=\"coolwarm\", ax=ax_map,\n edgecolor=\"black\", linewidth=0.4,\n vmin=vmin, vmax=vmax,\n)\nax_map.set_xlim(-128, -65); ax_map.set_ylim(22, 52)\nax_map.set_title(\"Annual mean surface T — states vs. source grid\")\nax_map.set_xlabel(\"longitude\"); ax_map.set_ylabel(\"latitude\")\n\nordered = state_mean.to_series().sort_values()\ncolors = plt.cm.coolwarm((ordered.values - vmin) / (vmax - vmin))\nax_bar.barh(ordered.index, ordered.values, color=colors)\nax_bar.set_xlabel(\"area-weighted mean T [°C]\")\nax_bar.tick_params(axis=\"y\", labelsize=7)\nax_bar.grid(axis=\"x\", alpha=0.3)\nplt.tight_layout()" }, { "cell_type": "markdown", @@ -231,17 +926,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "041a51f3", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-23T00:29:07.146317Z", - "iopub.status.busy": "2026-04-23T00:29:07.146242Z", - "iopub.status.idle": "2026-04-23T00:29:07.242581Z", - "shell.execute_reply": "2026-04-23T00:29:07.242177Z" + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "direct A·s : 9037.413298\n", + "Σ state_mean · a_state : 9037.413298\n", + "relative error : 0.00e+00\n" + ] } - }, - "outputs": [], + ], "source": [ "A = rgr._areas # sparse (n_states, n_src)\n", "tgt_area = np.ravel(A.sum(axis=1).todense())\n", @@ -268,17 +966,35 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "8fa88419", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-23T00:29:07.244167Z", - "iopub.status.busy": "2026-04-23T00:29:07.244090Z", - "iopub.status.idle": "2026-04-23T00:29:07.560391Z", - "shell.execute_reply": "2026-04-23T00:29:07.559962Z" + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "wrote states_regridder.nc (38.0 KB)\n", + "largest seasonal swing:\n", + "state\n", + "Minnesota 30.9\n", + "North Dakota 30.0\n", + "Wisconsin 28.4\n", + "Iowa 27.8\n", + "South Dakota 27.4\n", + "Name: JJA − DJF [°C], dtype: float64\n", + "\n", + "smallest seasonal swing (maritime / subtropical):\n", + "state\n", + "Florida 10.1\n", + "California 14.1\n", + "Georgia 16.1\n", + "South Carolina 16.6\n", + "Louisiana 16.6\n", + "Name: JJA − DJF [°C], dtype: float64\n" + ] } - }, - "outputs": [], + ], "source": [ "import tempfile\n", "from pathlib import Path\n", @@ -303,6 +1019,14 @@ "print(\"\\nsmallest seasonal swing (maritime / subtropical):\")\n", "print(amplitude.sort_values().head(5))" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f47783c1-fb31-4db7-84e2-39e2a139d47d", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -326,4 +1050,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file From 900a3506810933d87be739e1304e6a56e33caf39 Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Mon, 27 Apr 2026 14:33:35 -0500 Subject: [PATCH 06/16] Beef up demo notebook intros and step narratives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each notebook now opens with motivation (what conservative regridding is, when to use it), what makes the case 1D-non-separable (curvilinear coords / arbitrary polygons / unstructured mesh), and a numbered summary of what the notebook will do. Each step's markdown picks up a sentence or two of "why this step" narrative — what's expensive vs. cheap, what to look for in the result, what choices reflect domain conventions — without changing any of the code or expected outputs. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../demo_conservative_2d_curvilinear.ipynb | 40 ++--------- .../demos/demo_conservative_2d_regions.ipynb | 68 ++----------------- .../demo_conservative_2d_unstructured.ipynb | 40 ++--------- 3 files changed, 21 insertions(+), 127 deletions(-) diff --git a/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb b/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb index 2897a5e..83a82ee 100644 --- a/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb +++ b/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb @@ -4,14 +4,7 @@ "cell_type": "markdown", "id": "0", "metadata": {}, - "source": [ - "# Conservative 2D regrid — curvilinear target\n", - "\n", - "Curvilinear grids have 2D `lat(y, x)` / `lon(y, x)` coordinate arrays —\n", - "ocean models (ORCA, tripolar), rotated regional forecasts. Not\n", - "1D-separable, so `.conservative` can't handle them;\n", - "`.regrid.conservative_2d` is the tool." - ] + "source": "# Conservative 2D regrid — curvilinear target\n\n**Conservative regridding** resamples a gridded field while preserving its\narea-weighted integral: each output cell is the area-weighted average of\nthe source cells it overlaps. It's the right tool for fluxes and intensive\nquantities — precipitation, sea-surface temperature, mass-balance budgets —\nwhere bilinear or nearest-neighbor interpolation would bias the total.\n\nThe fast `.regrid.conservative` accessor only works on **1D-separable**\ngrids: plain rectilinear lat/lon, where lat depends only on `y` and lon\nonly on `x`. **Curvilinear** grids store coordinates as 2D arrays\n`lat(y, x)` / `lon(y, x)` and are common in ocean models (ORCA, tripolar)\nor any rotated/projected setup. Their cells aren't axis-aligned, so we\ndrop to `.regrid.conservative_2d`, which builds the full 2D polygon\nintersection.\n\n**In this notebook.** We regrid a smooth analytic two-bump field from a\nregular lat/lon grid onto a 30°-rotated curvilinear target — a minimal\nstand-in for any curvilinear ocean grid — and verify that the\narea-weighted integral is preserved to machine precision." }, { "cell_type": "code", @@ -32,9 +25,7 @@ "cell_type": "markdown", "id": "2", "metadata": {}, - "source": [ - "## Source — regular 1° lat/lon, analytic two-bump field" - ] + "source": "## Source — regular 1° lat/lon, analytic two-bump field\n\nA simple synthetic field — one positive Gaussian bump in the northern\nhemisphere, one negative in the southern — gives us something we can\nvisually track from source to target." }, { "cell_type": "code", @@ -64,12 +55,7 @@ "cell_type": "markdown", "id": "4", "metadata": {}, - "source": [ - "## Target — rotated curvilinear grid\n", - "\n", - "Coordinates ride on a `(ny, nx)` mesh, stored as 2D coordinate\n", - "variables on the target Dataset." - ] + "source": "## Target — rotated curvilinear grid\n\nTo break 1D-separability we take a regular `(ny, nx)` mesh and rotate it\n30° in the lat/lon plane. The result has the same kind of 2D coordinate\nvariables you'd find in an ORCA or rotated-pole grid: `longitude(ny, nx)`\nand `latitude(ny, nx)` riding on a non-axis-aligned mesh." }, { "cell_type": "code", @@ -104,13 +90,7 @@ "cell_type": "markdown", "id": "6", "metadata": {}, - "source": [ - "## Regrid and plot\n", - "\n", - "For the one-shot accessor, use `src.regrid.conservative_2d(target, ...)`.\n", - "Constructing the class directly (as below) is equivalent but lets us reuse\n", - "the weight matrix for both the apply and the diagnostic in the next cell." - ] + "source": "## Regrid and plot\n\n`ConservativeRegridder` builds the weight matrix once — that's the\nexpensive step, since it computes the area of every source/target polygon\nintersection — and lets us reuse it for the apply step and for the\ndiagnostic in the next cell. The one-shot equivalent is\n`src.regrid.conservative_2d(target, ...)`. Either way, output is on the\ncurvilinear `(ny, nx)` mesh, with NaN where target cells fall outside the\nsource domain." }, { "cell_type": "code", @@ -137,13 +117,7 @@ "cell_type": "markdown", "id": "8", "metadata": {}, - "source": [ - "## Conservation check\n", - "\n", - "For the target cells that fall within the source domain, the\n", - "area-weighted sum of outputs equals the direct A·s integral to\n", - "machine precision." - ] + "source": "## Conservation check\n\nThe regridder stores its area-intersection matrix\n`A[i, j] = area(target_i ∩ source_j)`. For target cells fully inside the\nsource domain, the area-weighted sum of outputs equals the direct A·s\nintegral to machine precision — the defining property of *conservative*\nregridding." }, { "cell_type": "code", @@ -168,7 +142,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -187,4 +161,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/docs/notebooks/demos/demo_conservative_2d_regions.ipynb b/docs/notebooks/demos/demo_conservative_2d_regions.ipynb index 2e229eb..bbb676c 100644 --- a/docs/notebooks/demos/demo_conservative_2d_regions.ipynb +++ b/docs/notebooks/demos/demo_conservative_2d_regions.ipynb @@ -4,20 +4,7 @@ "cell_type": "markdown", "id": "e5c70992", "metadata": {}, - "source": [ - "# Conservative 2D regrid — regions (grid → country/state polygons)\n", - "\n", - "Gridded data → arbitrary polygon regions (countries, states, watersheds,\n", - "ocean basins) is a canonical xagg-style workflow. Because regions aren't\n", - "a grid at all, `.conservative` can't express it;\n", - "`ConservativeRegridder.from_polygons` takes a numpy array of shapely\n", - "polygons and produces one conservatively-averaged value per region.\n", - "\n", - "This notebook uses **xarray's air-temperature tutorial dataset** (NMC\n", - "reanalysis, North America, 2.5° × 2.5°) and aggregates it onto **US\n", - "states** built from the `geoda.ncovr` county boundaries shipped in\n", - "`geodatasets`." - ] + "source": "# Conservative 2D regrid — regions (grid → country/state polygons)\n\nA common analysis question is: *given a gridded variable, what's its\narea-weighted mean over each of these regions?* — countries, states,\nwatersheds, ocean basins, exclusive economic zones. This is the canonical\n[xagg](https://github.com/ks905383/xagg)-style workflow and a natural fit\nfor **conservative regridding**: each region's output value is the\narea-weighted average of the source cells it overlaps, which is exactly\nwhat you want for fluxes and intensive quantities (precipitation,\ntemperature, mass-balance budgets) where bilinear or nearest-neighbor\ninterpolation would bias the total.\n\nBecause the targets are arbitrary polygons rather than a grid, the fast\n`.regrid.conservative` accessor doesn't apply. We use\n`ConservativeRegridder.from_polygons`, which takes a 1D array of shapely\npolygons as source and target.\n\n**In this notebook.**\n\n1. Aggregate xarray's air-temperature tutorial dataset (NMC reanalysis,\n ~daily 2.5° lat/lon over North America) onto US states built by\n dissolving county boundaries from `geodatasets`.\n2. Visualize the per-state means against the source grid.\n3. Verify conservation directly from the internal weight matrix.\n4. Save the regridder, reload it, and reuse it on JJA vs. DJF subsets to\n get per-state seasonal swings — showing the weight matrix is reusable\n across any source field on the same grid." }, { "cell_type": "code", @@ -40,15 +27,7 @@ "cell_type": "markdown", "id": "537c0ed3", "metadata": {}, - "source": [ - "## Source — NCEP reanalysis surface air temperature\n", - "\n", - "The tutorial dataset is 4×daily surface air temperature on a 2.5°\n", - "lat/lon grid covering North America. We take the annual mean over\n", - "2013–2014 and convert to °C. Longitudes are shifted from the native\n", - "[0, 360) convention to [-180, 180) and latitudes flipped to ascending\n", - "so the grid aligns with the state polygons' CRS." - ] + "source": "## Source — NCEP reanalysis surface air temperature\n\n`xr.tutorial.open_dataset(\"air_temperature\")` is a small (~3 MB) NMC\nreanalysis subset: 4×daily surface air temperature over North America on\na 2.5° lat/lon grid. We reduce it to a long-term mean, convert from\nKelvin to °C, and align with the state polygons' CRS:\n\n- Native longitudes are on `[0, 360)`; states are on `[-180, 180)`, so\n we wrap.\n- Native latitudes are descending; we sort to ascending so xarray's\n `pcolormesh` doesn't flip the image." }, { "cell_type": "code", @@ -701,16 +680,7 @@ "cell_type": "markdown", "id": "434441b2", "metadata": {}, - "source": [ - "## Regions — US states from `geodatasets`\n", - "\n", - "`geodatasets.get_path(\"geoda.ncovr\")` returns a GeoPackage of the 49\n", - "contiguous-US counties (+ DC). Dissolving on `STATE_NAME` gives one\n", - "(Multi)Polygon per state — exactly the input\n", - "`ConservativeRegridder.from_polygons` expects. Note the air-temperature\n", - "grid does not cover Alaska or Hawaii, which is also why ncovr is a\n", - "natural fit." - ] + "source": "## Regions — US states from `geodatasets`\n\n`geodatasets.get_path(\"geoda.ncovr\")` returns a GeoPackage of the 49\ncontiguous-US counties (+ DC). We **dissolve** on `STATE_NAME` —\ngeopandas' equivalent of a `groupby` for geometries — to combine each\nstate's counties into one (Multi)Polygon. The result is exactly what\n`ConservativeRegridder.from_polygons` wants: one shapely (Multi)Polygon\nper region. The air-temperature grid doesn't cover Alaska or Hawaii,\nwhich is why ncovr (contiguous-US only) is a natural fit." }, { "cell_type": "code", @@ -799,14 +769,7 @@ "cell_type": "markdown", "id": "3a3b8ac5", "metadata": {}, - "source": [ - "## Build the regridder and apply\n", - "\n", - "`from_polygons` takes source + target polygon arrays. Source polygons\n", - "are built from the 1D grid coords via `polygons_from_coords`; target\n", - "polygons are the states' `geometry` column as a numpy array. The data\n", - "is flattened to a single `src_cell` dimension to match." - ] + "source": "## Build the regridder and apply\n\n`from_polygons` takes flat 1D arrays of source and target polygons:\n\n- **Source polygons** come from the 1D grid coords via\n `polygons_from_coords`, which builds a rectangle per cell from the\n coordinate midpoints (so a 25×53 grid → 1325 source polygons).\n- **Target polygons** are just the states' `geometry` column.\n\nSource data has to be flattened to a single `src_cell` dimension to match\nthe flat polygon array. Constructing the regridder is the expensive step\n— it computes the area of every source/target polygon intersection.\nOnce built, `rgr.regrid(...)` is a sparse matrix-vector product against\nthe precomputed weights and is essentially instant." }, { "cell_type": "code", @@ -897,12 +860,7 @@ "cell_type": "markdown", "id": "3542f836", "metadata": {}, - "source": [ - "## Map + ranked bar chart\n", - "\n", - "States filled by area-weighted mean temperature, with the source grid\n", - "shown underneath for reference." - ] + "source": "## Map + ranked bar chart\n\nStates filled by area-weighted mean temperature, with the source grid\nshown underneath for reference. Both panels share the same `vmin`/`vmax`\nso a state's color on the bar chart matches its color on the map. A\nsanity check: the warm/cool gradient should track latitude (Florida and\nthe Gulf states warmest, the Upper Midwest and Northeast coolest)." }, { "cell_type": "code", @@ -916,13 +874,7 @@ "cell_type": "markdown", "id": "803247d2", "metadata": {}, - "source": [ - "## Conservation check\n", - "\n", - "The area-weighted sum over regions (using the regridder's internal\n", - "area matrix) should match the direct A·s computation — this is the\n", - "conservation property the method is named for." - ] + "source": "## Conservation check\n\nThe internal area matrix `A[i, j] = area(state_i ∩ src_cell_j)` lets us\nverify conservation directly: integrating the source field weighted by\nsource-cell coverage should equal integrating the regridded field\nweighted by target-cell area. Equality to machine precision is the\ndefining property of *conservative* regridding." }, { "cell_type": "code", @@ -956,13 +908,7 @@ "cell_type": "markdown", "id": "ff49d6b7", "metadata": {}, - "source": [ - "## Reuse: persist the regridder, apply to summer vs. winter\n", - "\n", - "The weight matrix is reusable across any source field on the same\n", - "grid. Save once, reload to skip the intersection build, then apply to\n", - "seasonally-filtered data to get per-state JJA vs. DJF means." - ] + "source": "## Reuse: persist the regridder, apply to summer vs. winter\n\nThe weight matrix depends only on the source/target geometry, not on the\ndata. Save once with `to_netcdf`, reload with `from_netcdf`, and apply\nto any field on the same source grid — no need to rebuild the (expensive)\npolygon intersection. Below we use one saved regridder on JJA and DJF\nsubsets to compute per-state seasonal amplitude. Continental interior\nstates show the largest swing; Florida and California — moderated by\nocean and latitude — show the smallest." }, { "cell_type": "code", diff --git a/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb b/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb index 829147a..2feb86e 100644 --- a/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb +++ b/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb @@ -4,17 +4,7 @@ "cell_type": "markdown", "id": "0", "metadata": {}, - "source": [ - "# Conservative 2D regrid — unstructured mesh + save/load\n", - "\n", - "Unstructured meshes (ICON triangles, MPAS hexagons, finite-element\n", - "models) are never 1D-separable. `ConservativeRegridder.from_polygons`\n", - "takes any flat array of shapely polygons as source or target.\n", - "\n", - "This notebook builds a synthetic Voronoi hex-like mesh and regrids a\n", - "structured source onto it, then persists the regridder so a\n", - "long-running pipeline can skip the weight build on restart." - ] + "source": "# Conservative 2D regrid — unstructured mesh + save/load\n\n**Conservative regridding** resamples a gridded field while preserving its\narea-weighted integral — the right tool for fluxes and intensive\nquantities, where bilinear or nearest-neighbor interpolation would bias\nthe total.\n\n**Unstructured meshes** — ICON triangles, MPAS hexagons, finite-element\nmodels, generic Voronoi tessellations — are the natural geometry for\nsimulations that need adaptive resolution (denser cells over land, coarser\nover open ocean). They have no `(y, x)` index structure: every cell is\njust an arbitrary polygon. So they're never 1D-separable, and the fast\n`.regrid.conservative` accessor doesn't apply.\n\n`ConservativeRegridder.from_polygons` takes a flat 1D array of shapely\npolygons as source and/or target. The same machinery handles\nstructured→unstructured, unstructured→structured, and unstructured→\nunstructured.\n\n**In this notebook.**\n\n1. Build a synthetic Voronoi mesh as a stand-in for a real ICON/MPAS dataset.\n2. Regrid a smooth analytic field from a structured lat/lon source onto\n the mesh.\n3. **Persist the regridder to disk** — for a fixed source/target pair the\n weight matrix is the same forever, so saving it lets a long-running\n pipeline (or a follow-up notebook) skip the polygon-intersection build\n on restart." }, { "cell_type": "code", @@ -41,13 +31,7 @@ "cell_type": "markdown", "id": "2", "metadata": {}, - "source": [ - "## Build a Voronoi mesh\n", - "\n", - "Jittered grid points → Voronoi → clip to the region of interest. Real\n", - "pipelines load a pre-built mesh (UGRID, ICON, etc.); the point here is\n", - "that `from_polygons` needs only a 1D array of shapely polygons." - ] + "source": "## Build a Voronoi mesh\n\nReal pipelines load a pre-built mesh (UGRID, ICON, MPAS); for a\nself-contained demo we synthesize one — jitter a regular grid of generator\npoints, take the Voronoi tessellation, and clip to the bounding box. The\nconstruction details aren't the point: `from_polygons` only needs a 1D\narray of shapely polygons however we get them." }, { "cell_type": "code", @@ -89,9 +73,7 @@ "cell_type": "markdown", "id": "4", "metadata": {}, - "source": [ - "## Structured lat/lon source" - ] + "source": "## Structured lat/lon source\n\nA smooth `sin(2λ)·cos(3φ)` field on a 1° rectilinear grid — wavy enough\nthat the regridded mesh values are visually distinct, smooth enough that\nno individual mesh cell aliases the pattern." }, { "cell_type": "code", @@ -114,9 +96,7 @@ "cell_type": "markdown", "id": "6", "metadata": {}, - "source": [ - "## Regrid onto the mesh" - ] + "source": "## Regrid onto the mesh\n\n`from_polygons` takes flat 1D arrays of source and target polygons. Source\npolygons come from the 1D grid coords via `polygons_from_coords` (one\nrectangle per cell, built from coordinate midpoints); target polygons are\nthe Voronoi mesh cells. Source data has to be flattened to a single\n`src_cell` dimension to match the flat polygon array." }, { "cell_type": "code", @@ -159,13 +139,7 @@ "cell_type": "markdown", "id": "9", "metadata": {}, - "source": [ - "## Persist the regridder\n", - "\n", - "For a fixed source/target pair, the weight matrix is the same forever.\n", - "Saving it lets long-running pipelines skip the (expensive) build on\n", - "restart." - ] + "source": "## Persist the regridder\n\nThe weight matrix is purely a function of source and target geometry —\nnot of the data — so for a fixed source/target pair it never needs to\nchange. `to_netcdf` writes it (along with shape and version metadata) to\na small NetCDF; `from_netcdf` rebuilds the regridder. A reload-then-apply\ngives bit-identical output to the original, so a long-running pipeline\ncan skip the (expensive) polygon-intersection build on restart." }, { "cell_type": "code", @@ -189,7 +163,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -208,4 +182,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file From 9450a5f61273e533727850f4ee5cf15822a8afd6 Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Mon, 27 Apr 2026 18:18:35 -0500 Subject: [PATCH 07/16] Promote ConservativeRegridder.areas to public; format and strip notebooks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The unnormalized ``(n_dst, n_src)`` cell-intersection area matrix was stored as ``self._areas`` but read externally by tests and the demo notebooks for conservation diagnostics. Promote to ``self.areas`` as a plain public attribute (consistent with the existing ``self.spherical``, ``self.x_coord``, ``self.y_coord`` style on the same class) and update the four external callers. Internal storage is the same — no behavior or perf change (verified against 3ed200b: build/apply timings within run-to-run noise on rect, curvilinear, and polygon cases). Also runs ``ruff format`` (4-line collapses in conservative_2d.py and test_conservative_2d.py; normalizes notebook source from single-string to list-of-strings) and clears notebook outputs via nbformat-equivalent JSON manipulation. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../demo_conservative_2d_curvilinear.ipynb | 74 +- .../demos/demo_conservative_2d_regions.ipynb | 818 +----------------- .../demo_conservative_2d_unstructured.ipynb | 80 +- src/xarray_regrid/methods/conservative_2d.py | 26 +- tests/test_conservative_2d.py | 12 +- 5 files changed, 174 insertions(+), 836 deletions(-) diff --git a/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb b/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb index 83a82ee..2db6039 100644 --- a/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb +++ b/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb @@ -4,7 +4,28 @@ "cell_type": "markdown", "id": "0", "metadata": {}, - "source": "# Conservative 2D regrid — curvilinear target\n\n**Conservative regridding** resamples a gridded field while preserving its\narea-weighted integral: each output cell is the area-weighted average of\nthe source cells it overlaps. It's the right tool for fluxes and intensive\nquantities — precipitation, sea-surface temperature, mass-balance budgets —\nwhere bilinear or nearest-neighbor interpolation would bias the total.\n\nThe fast `.regrid.conservative` accessor only works on **1D-separable**\ngrids: plain rectilinear lat/lon, where lat depends only on `y` and lon\nonly on `x`. **Curvilinear** grids store coordinates as 2D arrays\n`lat(y, x)` / `lon(y, x)` and are common in ocean models (ORCA, tripolar)\nor any rotated/projected setup. Their cells aren't axis-aligned, so we\ndrop to `.regrid.conservative_2d`, which builds the full 2D polygon\nintersection.\n\n**In this notebook.** We regrid a smooth analytic two-bump field from a\nregular lat/lon grid onto a 30°-rotated curvilinear target — a minimal\nstand-in for any curvilinear ocean grid — and verify that the\narea-weighted integral is preserved to machine precision." + "source": [ + "# Conservative 2D regrid — curvilinear target\n", + "\n", + "**Conservative regridding** resamples a gridded field while preserving its\n", + "area-weighted integral: each output cell is the area-weighted average of\n", + "the source cells it overlaps. It's the right tool for fluxes and intensive\n", + "quantities — precipitation, sea-surface temperature, mass-balance budgets —\n", + "where bilinear or nearest-neighbor interpolation would bias the total.\n", + "\n", + "The fast `.regrid.conservative` accessor only works on **1D-separable**\n", + "grids: plain rectilinear lat/lon, where lat depends only on `y` and lon\n", + "only on `x`. **Curvilinear** grids store coordinates as 2D arrays\n", + "`lat(y, x)` / `lon(y, x)` and are common in ocean models (ORCA, tripolar)\n", + "or any rotated/projected setup. Their cells aren't axis-aligned, so we\n", + "drop to `.regrid.conservative_2d`, which builds the full 2D polygon\n", + "intersection.\n", + "\n", + "**In this notebook.** We regrid a smooth analytic two-bump field from a\n", + "regular lat/lon grid onto a 30°-rotated curvilinear target — a minimal\n", + "stand-in for any curvilinear ocean grid — and verify that the\n", + "area-weighted integral is preserved to machine precision." + ] }, { "cell_type": "code", @@ -25,7 +46,13 @@ "cell_type": "markdown", "id": "2", "metadata": {}, - "source": "## Source — regular 1° lat/lon, analytic two-bump field\n\nA simple synthetic field — one positive Gaussian bump in the northern\nhemisphere, one negative in the southern — gives us something we can\nvisually track from source to target." + "source": [ + "## Source — regular 1° lat/lon, analytic two-bump field\n", + "\n", + "A simple synthetic field — one positive Gaussian bump in the northern\n", + "hemisphere, one negative in the southern — gives us something we can\n", + "visually track from source to target." + ] }, { "cell_type": "code", @@ -55,7 +82,14 @@ "cell_type": "markdown", "id": "4", "metadata": {}, - "source": "## Target — rotated curvilinear grid\n\nTo break 1D-separability we take a regular `(ny, nx)` mesh and rotate it\n30° in the lat/lon plane. The result has the same kind of 2D coordinate\nvariables you'd find in an ORCA or rotated-pole grid: `longitude(ny, nx)`\nand `latitude(ny, nx)` riding on a non-axis-aligned mesh." + "source": [ + "## Target — rotated curvilinear grid\n", + "\n", + "To break 1D-separability we take a regular `(ny, nx)` mesh and rotate it\n", + "30° in the lat/lon plane. The result has the same kind of 2D coordinate\n", + "variables you'd find in an ORCA or rotated-pole grid: `longitude(ny, nx)`\n", + "and `latitude(ny, nx)` riding on a non-axis-aligned mesh." + ] }, { "cell_type": "code", @@ -90,7 +124,17 @@ "cell_type": "markdown", "id": "6", "metadata": {}, - "source": "## Regrid and plot\n\n`ConservativeRegridder` builds the weight matrix once — that's the\nexpensive step, since it computes the area of every source/target polygon\nintersection — and lets us reuse it for the apply step and for the\ndiagnostic in the next cell. The one-shot equivalent is\n`src.regrid.conservative_2d(target, ...)`. Either way, output is on the\ncurvilinear `(ny, nx)` mesh, with NaN where target cells fall outside the\nsource domain." + "source": [ + "## Regrid and plot\n", + "\n", + "`ConservativeRegridder` builds the weight matrix once — that's the\n", + "expensive step, since it computes the area of every source/target polygon\n", + "intersection — and lets us reuse it for the apply step and for the\n", + "diagnostic in the next cell. The one-shot equivalent is\n", + "`src.regrid.conservative_2d(target, ...)`. Either way, output is on the\n", + "curvilinear `(ny, nx)` mesh, with NaN where target cells fall outside the\n", + "source domain." + ] }, { "cell_type": "code", @@ -117,7 +161,15 @@ "cell_type": "markdown", "id": "8", "metadata": {}, - "source": "## Conservation check\n\nThe regridder stores its area-intersection matrix\n`A[i, j] = area(target_i ∩ source_j)`. For target cells fully inside the\nsource domain, the area-weighted sum of outputs equals the direct A·s\nintegral to machine precision — the defining property of *conservative*\nregridding." + "source": [ + "## Conservation check\n", + "\n", + "The regridder stores its area-intersection matrix\n", + "`A[i, j] = area(target_i ∩ source_j)`. For target cells fully inside the\n", + "source domain, the area-weighted sum of outputs equals the direct A·s\n", + "integral to machine precision — the defining property of *conservative*\n", + "regridding." + ] }, { "cell_type": "code", @@ -126,7 +178,7 @@ "metadata": {}, "outputs": [], "source": [ - "A = rgr._areas\n", + "A = rgr.areas\n", "src_cover = np.ravel(A.sum(axis=0).todense())\n", "tgt_cover = A.sum(axis=1).todense().reshape(regridded.shape)\n", "valid = np.isfinite(regridded.values)\n", @@ -138,6 +190,14 @@ "print(f\"relative err : {abs(direct - via_regrid) / max(abs(direct), 1e-12):.2e}\")\n", "print(f\"coverage : {valid.mean():.2%} of target cells inside source\")" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0e479d18-553d-4965-9606-bdd3fb4adaa9", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -161,4 +221,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} diff --git a/docs/notebooks/demos/demo_conservative_2d_regions.ipynb b/docs/notebooks/demos/demo_conservative_2d_regions.ipynb index bbb676c..4bf670a 100644 --- a/docs/notebooks/demos/demo_conservative_2d_regions.ipynb +++ b/docs/notebooks/demos/demo_conservative_2d_regions.ipynb @@ -8,7 +8,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "b28b1f2e", "metadata": {}, "outputs": [], @@ -31,642 +31,10 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "721e935c", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.DataArray 'mean_air_temperature' (lat: 25, lon: 53)> Size: 11kB\n",
-       "array([[ 25.21615068,  25.23573973,  24.96414384, ...,  24.18820548,\n",
-       "         24.13144521,  24.15510274],\n",
-       "       [ 24.97920205,  24.78700685,  24.32039384, ...,  23.70954795,\n",
-       "         23.6270274 ,  23.29383562],\n",
-       "       [ 24.49986301,  23.80333219,  23.47931507, ...,  23.66092466,\n",
-       "         23.13796233,  22.66645548],\n",
-       "       ...,\n",
-       "       [ -8.3812363 ,  -8.82269178,  -9.08830479, ..., -22.54210959,\n",
-       "        -19.56648973, -15.43440068],\n",
-       "       [-10.41560616, -10.35602397, -10.40066096, ..., -23.39409589,\n",
-       "        -21.56424315, -18.79073973],\n",
-       "       [-12.77355822, -12.96694863, -13.26337329, ..., -22.33409932,\n",
-       "        -21.21188356, -19.71195205]], shape=(25, 53))\n",
-       "Coordinates:\n",
-       "  * lat      (lat) float32 100B 15.0 17.5 20.0 22.5 25.0 ... 67.5 70.0 72.5 75.0\n",
-       "  * lon      (lon) float32 212B -160.0 -157.5 -155.0 ... -35.0 -32.5 -30.0\n",
-       "Attributes:\n",
-       "    long_name:     4xDaily Air temperature at sigma level 995\n",
-       "    units:         degC\n",
-       "    precision:     2\n",
-       "    GRIB_id:       11\n",
-       "    GRIB_name:     TMP\n",
-       "    var_desc:      Air temperature\n",
-       "    dataset:       NMC Reanalysis\n",
-       "    level_desc:    Surface\n",
-       "    statistic:     Individual Obs\n",
-       "    parent_stat:   Other\n",
-       "    actual_range:  [185.16 322.1 ]
" - ], - "text/plain": [ - " Size: 11kB\n", - "array([[ 25.21615068, 25.23573973, 24.96414384, ..., 24.18820548,\n", - " 24.13144521, 24.15510274],\n", - " [ 24.97920205, 24.78700685, 24.32039384, ..., 23.70954795,\n", - " 23.6270274 , 23.29383562],\n", - " [ 24.49986301, 23.80333219, 23.47931507, ..., 23.66092466,\n", - " 23.13796233, 22.66645548],\n", - " ...,\n", - " [ -8.3812363 , -8.82269178, -9.08830479, ..., -22.54210959,\n", - " -19.56648973, -15.43440068],\n", - " [-10.41560616, -10.35602397, -10.40066096, ..., -23.39409589,\n", - " -21.56424315, -18.79073973],\n", - " [-12.77355822, -12.96694863, -13.26337329, ..., -22.33409932,\n", - " -21.21188356, -19.71195205]], shape=(25, 53))\n", - "Coordinates:\n", - " * lat (lat) float32 100B 15.0 17.5 20.0 22.5 25.0 ... 67.5 70.0 72.5 75.0\n", - " * lon (lon) float32 212B -160.0 -157.5 -155.0 ... -35.0 -32.5 -30.0\n", - "Attributes:\n", - " long_name: 4xDaily Air temperature at sigma level 995\n", - " units: degC\n", - " precision: 2\n", - " GRIB_id: 11\n", - " GRIB_name: TMP\n", - " var_desc: Air temperature\n", - " dataset: NMC Reanalysis\n", - " level_desc: Surface\n", - " statistic: Individual Obs\n", - " parent_stat: Other\n", - " actual_range: [185.16 322.1 ]" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "ds = xr.tutorial.open_dataset(\"air_temperature\")\n", "air = (ds[\"air\"].mean(\"time\") - 273.15).sortby(\"lat\")\n", @@ -684,74 +52,10 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "8c6437b2", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "49 states/DC, CRS=EPSG:4326, bounds=[-124.7, 25.0, -67.0, 49.4]\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
STATE_NAMEgeometry
0AlabamaPOLYGON ((-87.62572 30.87672, -87.61593 30.848...
1ArizonaPOLYGON ((-113.32838 32.04356, -114.82097 32.4...
2ArkansasPOLYGON ((-93.4789 33.02139, -93.51174 33.0211...
\n", - "
" - ], - "text/plain": [ - " STATE_NAME geometry\n", - "0 Alabama POLYGON ((-87.62572 30.87672, -87.61593 30.848...\n", - "1 Arizona POLYGON ((-113.32838 32.04356, -114.82097 32.4...\n", - "2 Arkansas POLYGON ((-93.4789 33.02139, -93.51174 33.0211..." - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "counties = gpd.read_file(geodatasets.get_path(\"geoda.ncovr\"))\n", "states = (\n", @@ -773,71 +77,10 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "da3e70c8", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "state\n", - "Minnesota 4.30\n", - "North Dakota 4.65\n", - "Maine 5.08\n", - "Wyoming 5.53\n", - "Montana 5.78\n", - "Vermont 6.02\n", - "Wisconsin 6.06\n", - "Idaho 6.43\n", - "Michigan 6.75\n", - "South Dakota 7.12\n", - "New Hampshire 7.17\n", - "New York 7.28\n", - "Colorado 7.48\n", - "Iowa 8.46\n", - "Rhode Island 8.50\n", - "Connecticut 8.72\n", - "Washington 8.83\n", - "Massachusetts 8.84\n", - "Oregon 8.99\n", - "Pennsylvania 9.25\n", - "Utah 9.38\n", - "Nebraska 9.57\n", - "Ohio 9.64\n", - "District of Columbia 9.97\n", - "Nevada 10.17\n", - "Illinois 10.24\n", - "West Virginia 10.26\n", - "Indiana 10.47\n", - "New Jersey 11.43\n", - "Maryland 11.47\n", - "Virginia 11.94\n", - "Missouri 12.05\n", - "Kentucky 12.20\n", - "Kansas 12.61\n", - "Delaware 12.77\n", - "New Mexico 12.90\n", - "Tennessee 13.74\n", - "California 14.46\n", - "North Carolina 14.90\n", - "Arkansas 15.33\n", - "Oklahoma 15.34\n", - "South Carolina 15.91\n", - "Arizona 16.53\n", - "Georgia 16.54\n", - "Alabama 16.61\n", - "Mississippi 17.10\n", - "Texas 18.35\n", - "Louisiana 18.85\n", - "Florida 21.93\n", - "dtype: float64" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "src_polys = polygons_from_coords(air.lon.values, air.lat.values)\n", "tgt_polys = states.geometry.to_numpy()\n", @@ -878,22 +121,12 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "041a51f3", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "direct A·s : 9037.413298\n", - "Σ state_mean · a_state : 9037.413298\n", - "relative error : 0.00e+00\n" - ] - } - ], + "outputs": [], "source": [ - "A = rgr._areas # sparse (n_states, n_src)\n", + "A = rgr.areas # sparse (n_states, n_src)\n", "tgt_area = np.ravel(A.sum(axis=1).todense())\n", "src_cover = np.ravel(A.sum(axis=0).todense())\n", "\n", @@ -912,35 +145,10 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "8fa88419", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "wrote states_regridder.nc (38.0 KB)\n", - "largest seasonal swing:\n", - "state\n", - "Minnesota 30.9\n", - "North Dakota 30.0\n", - "Wisconsin 28.4\n", - "Iowa 27.8\n", - "South Dakota 27.4\n", - "Name: JJA − DJF [°C], dtype: float64\n", - "\n", - "smallest seasonal swing (maritime / subtropical):\n", - "state\n", - "Florida 10.1\n", - "California 14.1\n", - "Georgia 16.1\n", - "South Carolina 16.6\n", - "Louisiana 16.6\n", - "Name: JJA − DJF [°C], dtype: float64\n" - ] - } - ], + "outputs": [], "source": [ "import tempfile\n", "from pathlib import Path\n", @@ -996,4 +204,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} diff --git a/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb b/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb index 2feb86e..b61a520 100644 --- a/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb +++ b/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb @@ -4,7 +4,36 @@ "cell_type": "markdown", "id": "0", "metadata": {}, - "source": "# Conservative 2D regrid — unstructured mesh + save/load\n\n**Conservative regridding** resamples a gridded field while preserving its\narea-weighted integral — the right tool for fluxes and intensive\nquantities, where bilinear or nearest-neighbor interpolation would bias\nthe total.\n\n**Unstructured meshes** — ICON triangles, MPAS hexagons, finite-element\nmodels, generic Voronoi tessellations — are the natural geometry for\nsimulations that need adaptive resolution (denser cells over land, coarser\nover open ocean). They have no `(y, x)` index structure: every cell is\njust an arbitrary polygon. So they're never 1D-separable, and the fast\n`.regrid.conservative` accessor doesn't apply.\n\n`ConservativeRegridder.from_polygons` takes a flat 1D array of shapely\npolygons as source and/or target. The same machinery handles\nstructured→unstructured, unstructured→structured, and unstructured→\nunstructured.\n\n**In this notebook.**\n\n1. Build a synthetic Voronoi mesh as a stand-in for a real ICON/MPAS dataset.\n2. Regrid a smooth analytic field from a structured lat/lon source onto\n the mesh.\n3. **Persist the regridder to disk** — for a fixed source/target pair the\n weight matrix is the same forever, so saving it lets a long-running\n pipeline (or a follow-up notebook) skip the polygon-intersection build\n on restart." + "source": [ + "# Conservative 2D regrid — unstructured mesh + save/load\n", + "\n", + "**Conservative regridding** resamples a gridded field while preserving its\n", + "area-weighted integral — the right tool for fluxes and intensive\n", + "quantities, where bilinear or nearest-neighbor interpolation would bias\n", + "the total.\n", + "\n", + "**Unstructured meshes** — ICON triangles, MPAS hexagons, finite-element\n", + "models, generic Voronoi tessellations — are the natural geometry for\n", + "simulations that need adaptive resolution (denser cells over land, coarser\n", + "over open ocean). They have no `(y, x)` index structure: every cell is\n", + "just an arbitrary polygon. So they're never 1D-separable, and the fast\n", + "`.regrid.conservative` accessor doesn't apply.\n", + "\n", + "`ConservativeRegridder.from_polygons` takes a flat 1D array of shapely\n", + "polygons as source and/or target. The same machinery handles\n", + "structured→unstructured, unstructured→structured, and unstructured→\n", + "unstructured.\n", + "\n", + "**In this notebook.**\n", + "\n", + "1. Build a synthetic Voronoi mesh as a stand-in for a real ICON/MPAS dataset.\n", + "2. Regrid a smooth analytic field from a structured lat/lon source onto\n", + " the mesh.\n", + "3. **Persist the regridder to disk** — for a fixed source/target pair the\n", + " weight matrix is the same forever, so saving it lets a long-running\n", + " pipeline (or a follow-up notebook) skip the polygon-intersection build\n", + " on restart." + ] }, { "cell_type": "code", @@ -31,7 +60,15 @@ "cell_type": "markdown", "id": "2", "metadata": {}, - "source": "## Build a Voronoi mesh\n\nReal pipelines load a pre-built mesh (UGRID, ICON, MPAS); for a\nself-contained demo we synthesize one — jitter a regular grid of generator\npoints, take the Voronoi tessellation, and clip to the bounding box. The\nconstruction details aren't the point: `from_polygons` only needs a 1D\narray of shapely polygons however we get them." + "source": [ + "## Build a Voronoi mesh\n", + "\n", + "Real pipelines load a pre-built mesh (UGRID, ICON, MPAS); for a\n", + "self-contained demo we synthesize one — jitter a regular grid of generator\n", + "points, take the Voronoi tessellation, and clip to the bounding box. The\n", + "construction details aren't the point: `from_polygons` only needs a 1D\n", + "array of shapely polygons however we get them." + ] }, { "cell_type": "code", @@ -73,7 +110,13 @@ "cell_type": "markdown", "id": "4", "metadata": {}, - "source": "## Structured lat/lon source\n\nA smooth `sin(2λ)·cos(3φ)` field on a 1° rectilinear grid — wavy enough\nthat the regridded mesh values are visually distinct, smooth enough that\nno individual mesh cell aliases the pattern." + "source": [ + "## Structured lat/lon source\n", + "\n", + "A smooth `sin(2λ)·cos(3φ)` field on a 1° rectilinear grid — wavy enough\n", + "that the regridded mesh values are visually distinct, smooth enough that\n", + "no individual mesh cell aliases the pattern." + ] }, { "cell_type": "code", @@ -96,7 +139,15 @@ "cell_type": "markdown", "id": "6", "metadata": {}, - "source": "## Regrid onto the mesh\n\n`from_polygons` takes flat 1D arrays of source and target polygons. Source\npolygons come from the 1D grid coords via `polygons_from_coords` (one\nrectangle per cell, built from coordinate midpoints); target polygons are\nthe Voronoi mesh cells. Source data has to be flattened to a single\n`src_cell` dimension to match the flat polygon array." + "source": [ + "## Regrid onto the mesh\n", + "\n", + "`from_polygons` takes flat 1D arrays of source and target polygons. Source\n", + "polygons come from the 1D grid coords via `polygons_from_coords` (one\n", + "rectangle per cell, built from coordinate midpoints); target polygons are\n", + "the Voronoi mesh cells. Source data has to be flattened to a single\n", + "`src_cell` dimension to match the flat polygon array." + ] }, { "cell_type": "code", @@ -139,7 +190,16 @@ "cell_type": "markdown", "id": "9", "metadata": {}, - "source": "## Persist the regridder\n\nThe weight matrix is purely a function of source and target geometry —\nnot of the data — so for a fixed source/target pair it never needs to\nchange. `to_netcdf` writes it (along with shape and version metadata) to\na small NetCDF; `from_netcdf` rebuilds the regridder. A reload-then-apply\ngives bit-identical output to the original, so a long-running pipeline\ncan skip the (expensive) polygon-intersection build on restart." + "source": [ + "## Persist the regridder\n", + "\n", + "The weight matrix is purely a function of source and target geometry —\n", + "not of the data — so for a fixed source/target pair it never needs to\n", + "change. `to_netcdf` writes it (along with shape and version metadata) to\n", + "a small NetCDF; `from_netcdf` rebuilds the regridder. A reload-then-apply\n", + "gives bit-identical output to the original, so a long-running pipeline\n", + "can skip the (expensive) polygon-intersection build on restart." + ] }, { "cell_type": "code", @@ -159,6 +219,14 @@ "same = np.array_equal(rgr.regrid(src_flat).values, rgr2.regrid(src_flat).values)\n", "print(f\"\\nreload bit-identical: {same}\")" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c7eea93-d8dd-4838-bda3-77cada247e7e", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -182,4 +250,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} diff --git a/src/xarray_regrid/methods/conservative_2d.py b/src/xarray_regrid/methods/conservative_2d.py index 302d868..0197b42 100644 --- a/src/xarray_regrid/methods/conservative_2d.py +++ b/src/xarray_regrid/methods/conservative_2d.py @@ -136,6 +136,12 @@ class ConservativeRegridder: Build once, apply to many fields via :meth:`regrid` (or by calling the regridder); ``.T`` gives the backward regridder. Forward and backward weight matrices are cached lazily. Requires ``shapely >= 2.0``. + + The unnormalized cell-intersection area matrix + ``A[i, j] = area(target_i ∩ source_j)`` is exposed as ``self.areas`` + (sparse ``(n_dst, n_src)`` if the ``sparse`` package is available, dense + otherwise) — useful for conservation diagnostics and per-cell coverage + analysis. """ def __init__( @@ -182,17 +188,17 @@ def __init__( areas = _remap_columns_for_axis_sort( areas, src_x_sort_idx, self._src_shape, x_dim_index ) - self._areas = areas + self.areas = areas self._source_coords = source.coords.to_dataset() self._target_coords = target.coords.to_dataset() @cached_property def _forward(self) -> _Direction: - return _Direction(self._areas) + return _Direction(self.areas) @cached_property def _backward(self) -> _Direction: - return _Direction(_transpose_weights(self._areas)) + return _Direction(_transpose_weights(self.areas)) @property def forward_weights(self) -> "sparse.COO | np.ndarray": @@ -238,7 +244,7 @@ def transpose(self) -> "ConservativeRegridder": forward Direction becomes the new's backward (and vice versa), so any already-computed weight matrices are reused, not recomputed.""" new = type(self)._from_state( - areas=_transpose_weights(self._areas), + areas=_transpose_weights(self.areas), source_coords=self._target_coords, target_coords=self._source_coords, src_dims=self._dst_dims, @@ -261,8 +267,8 @@ def T(self) -> "ConservativeRegridder": # noqa: N802 return self.transpose() def __repr__(self) -> str: - nnz = getattr(self._areas, "nnz", None) - shape = getattr(self._areas, "shape", (None, None)) + nnz = getattr(self.areas, "nnz", None) + shape = getattr(self.areas, "shape", (None, None)) nnz_str = f"nnz={nnz}" if nnz is not None else "dense" return ( f"ConservativeRegridder(src_dims={self._src_dims}, " @@ -274,7 +280,7 @@ def to_netcdf(self, path: str | Path, engine: NetcdfEngine = None) -> None: Requires a group-aware engine (``netcdf4`` or ``h5netcdf``); ``engine`` is forwarded to :func:`xarray.Dataset.to_netcdf`.""" path = Path(path) - row, col, data, shape = _coo_components(self._areas) + row, col, data, shape = _coo_components(self.areas) ds_weights = xr.Dataset( { "_coo_row": (("nnz",), row), @@ -352,7 +358,7 @@ def _from_state( instance._dst_dims = dst_dims instance._src_shape = src_shape instance._dst_shape = dst_shape - instance._areas = areas + instance.areas = areas instance._source_coords = source_coords instance._target_coords = target_coords return instance @@ -664,9 +670,7 @@ def _normalize_longitude_coords( # aligns. A uniform offset can't reconcile cross-convention grids: # mean diff is exactly 180° and round() is banker's-rounded to 0. wrap_point = float((tgt_finite[0] + tgt_finite[-1] + 360.0) / 2.0) - source_x = np.where( - source_x < wrap_point - 360.0, source_x + 360.0, source_x - ) + source_x = np.where(source_x < wrap_point - 360.0, source_x + 360.0, source_x) source_x = np.where(source_x > wrap_point, source_x - 360.0, source_x) diffs = np.diff(source_x) if not (np.all(diffs > 0) or np.all(diffs < 0)): diff --git a/tests/test_conservative_2d.py b/tests/test_conservative_2d.py index 668114e..aabb153 100644 --- a/tests/test_conservative_2d.py +++ b/tests/test_conservative_2d.py @@ -124,9 +124,7 @@ def test_cross_convention_longitude_alignment(): dims=("latitude", "longitude"), coords={"latitude": [-30.0, 30.0], "longitude": src_vals_neg_x}, ) - target = xr.Dataset( - coords={"latitude": [-30.0, 30.0], "longitude": tgt_vals_neg} - ) + target = xr.Dataset(coords={"latitude": [-30.0, 30.0], "longitude": tgt_vals_neg}) expected = da.regrid.conservative(target).transpose("latitude", "longitude") out_planar = da.regrid.conservative_2d( target, x_coord="longitude", y_coord="latitude" @@ -239,9 +237,9 @@ def test_regridder_T_preserves_weights(): # noqa: N802 r = ConservativeRegridder(da, target, x_coord="x", y_coord="y") rr = r.T.T # Same shape, same coords, same data. - assert r._areas.shape == rr._areas.shape - if hasattr(r._areas, "data"): - np.testing.assert_array_equal(r._areas.data, rr._areas.data) + assert r.areas.shape == rr.areas.shape + if hasattr(r.areas, "data"): + np.testing.assert_array_equal(r.areas.data, rr.areas.data) def test_regridder_shape_mismatch_raises(): @@ -436,7 +434,7 @@ def test_from_polygons_mass_conservation(): out = rgr.regrid(da).values # Direct mass = sum_i s_i * (sum_j A_ij). Matches output if we multiply # output by target-covered area = sum_i A_ij. - areas = rgr._areas # (n_tgt, n_src) + areas = rgr.areas # (n_tgt, n_src) tgt_covered = areas.sum(axis=1).todense() valid = tgt_covered > 0 direct = float((s * areas.sum(axis=0).todense()).sum()) From dfac2347ffa95ab8e8ccfb2af8cb5232b7636dcb Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Tue, 28 Apr 2026 10:02:47 -0500 Subject: [PATCH 08/16] Refactor conservative_2d internals and stabilize area diagnostics API --- .../demo_conservative_2d_curvilinear.ipynb | 11 +- .../demos/demo_conservative_2d_regions.ipynb | 150 +++++++++-- .../demo_conservative_2d_unstructured.ipynb | 11 +- src/xarray_regrid/__init__.py | 2 + .../methods/_conservative_2d_serialization.py | 115 +++++++++ .../methods/_conservative_2d_spec.py | 15 ++ src/xarray_regrid/methods/conservative_2d.py | 238 +++++++----------- src/xarray_regrid/regrid.py | 2 +- src/xarray_regrid/utils.py | 2 +- tests/test_conservative_2d.py | 9 +- 10 files changed, 355 insertions(+), 200 deletions(-) create mode 100644 src/xarray_regrid/methods/_conservative_2d_serialization.py create mode 100644 src/xarray_regrid/methods/_conservative_2d_spec.py diff --git a/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb b/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb index 2db6039..42a2679 100644 --- a/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb +++ b/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb @@ -190,14 +190,6 @@ "print(f\"relative err : {abs(direct - via_regrid) / max(abs(direct), 1e-12):.2e}\")\n", "print(f\"coverage : {valid.mean():.2%} of target cells inside source\")" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0e479d18-553d-4965-9606-bdd3fb4adaa9", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -215,8 +207,7 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.13" + "pygments_lexer": "ipython3" } }, "nbformat": 4, diff --git a/docs/notebooks/demos/demo_conservative_2d_regions.ipynb b/docs/notebooks/demos/demo_conservative_2d_regions.ipynb index 4bf670a..a66f949 100644 --- a/docs/notebooks/demos/demo_conservative_2d_regions.ipynb +++ b/docs/notebooks/demos/demo_conservative_2d_regions.ipynb @@ -4,7 +4,35 @@ "cell_type": "markdown", "id": "e5c70992", "metadata": {}, - "source": "# Conservative 2D regrid — regions (grid → country/state polygons)\n\nA common analysis question is: *given a gridded variable, what's its\narea-weighted mean over each of these regions?* — countries, states,\nwatersheds, ocean basins, exclusive economic zones. This is the canonical\n[xagg](https://github.com/ks905383/xagg)-style workflow and a natural fit\nfor **conservative regridding**: each region's output value is the\narea-weighted average of the source cells it overlaps, which is exactly\nwhat you want for fluxes and intensive quantities (precipitation,\ntemperature, mass-balance budgets) where bilinear or nearest-neighbor\ninterpolation would bias the total.\n\nBecause the targets are arbitrary polygons rather than a grid, the fast\n`.regrid.conservative` accessor doesn't apply. We use\n`ConservativeRegridder.from_polygons`, which takes a 1D array of shapely\npolygons as source and target.\n\n**In this notebook.**\n\n1. Aggregate xarray's air-temperature tutorial dataset (NMC reanalysis,\n ~daily 2.5° lat/lon over North America) onto US states built by\n dissolving county boundaries from `geodatasets`.\n2. Visualize the per-state means against the source grid.\n3. Verify conservation directly from the internal weight matrix.\n4. Save the regridder, reload it, and reuse it on JJA vs. DJF subsets to\n get per-state seasonal swings — showing the weight matrix is reusable\n across any source field on the same grid." + "source": [ + "# Conservative 2D regrid — regions (grid → country/state polygons)\n", + "\n", + "A common analysis question is: *given a gridded variable, what's its\n", + "area-weighted mean over each of these regions?* — countries, states,\n", + "watersheds, ocean basins, exclusive economic zones. This is the canonical\n", + "[xagg](https://github.com/ks905383/xagg)-style workflow and a natural fit\n", + "for **conservative regridding**: each region's output value is the\n", + "area-weighted average of the source cells it overlaps, which is exactly\n", + "what you want for fluxes and intensive quantities (precipitation,\n", + "temperature, mass-balance budgets) where bilinear or nearest-neighbor\n", + "interpolation would bias the total.\n", + "\n", + "Because the targets are arbitrary polygons rather than a grid, the fast\n", + "`.regrid.conservative` accessor doesn't apply. We use\n", + "`ConservativeRegridder.from_polygons`, which takes a 1D array of shapely\n", + "polygons as source and target.\n", + "\n", + "**In this notebook.**\n", + "\n", + "1. Aggregate xarray's air-temperature tutorial dataset (NMC reanalysis,\n", + " ~daily 2.5° lat/lon over North America) onto US states built by\n", + " dissolving county boundaries from `geodatasets`.\n", + "2. Visualize the per-state means against the source grid.\n", + "3. Verify conservation directly from the internal weight matrix.\n", + "4. Save the regridder, reload it, and reuse it on JJA vs. DJF subsets to\n", + " get per-state seasonal swings — showing the weight matrix is reusable\n", + " across any source field on the same grid." + ] }, { "cell_type": "code", @@ -27,7 +55,19 @@ "cell_type": "markdown", "id": "537c0ed3", "metadata": {}, - "source": "## Source — NCEP reanalysis surface air temperature\n\n`xr.tutorial.open_dataset(\"air_temperature\")` is a small (~3 MB) NMC\nreanalysis subset: 4×daily surface air temperature over North America on\na 2.5° lat/lon grid. We reduce it to a long-term mean, convert from\nKelvin to °C, and align with the state polygons' CRS:\n\n- Native longitudes are on `[0, 360)`; states are on `[-180, 180)`, so\n we wrap.\n- Native latitudes are descending; we sort to ascending so xarray's\n `pcolormesh` doesn't flip the image." + "source": [ + "## Source — NCEP reanalysis surface air temperature\n", + "\n", + "`xr.tutorial.open_dataset(\"air_temperature\")` is a small (~3 MB) NMC\n", + "reanalysis subset: 4×daily surface air temperature over North America on\n", + "a 2.5° lat/lon grid. We reduce it to a long-term mean, convert from\n", + "Kelvin to °C, and align with the state polygons' CRS:\n", + "\n", + "- Native longitudes are on `[0, 360)`; states are on `[-180, 180)`, so\n", + " we wrap.\n", + "- Native latitudes are descending; we sort to ascending so xarray's\n", + " `pcolormesh` doesn't flip the image." + ] }, { "cell_type": "code", @@ -48,7 +88,17 @@ "cell_type": "markdown", "id": "434441b2", "metadata": {}, - "source": "## Regions — US states from `geodatasets`\n\n`geodatasets.get_path(\"geoda.ncovr\")` returns a GeoPackage of the 49\ncontiguous-US counties (+ DC). We **dissolve** on `STATE_NAME` —\ngeopandas' equivalent of a `groupby` for geometries — to combine each\nstate's counties into one (Multi)Polygon. The result is exactly what\n`ConservativeRegridder.from_polygons` wants: one shapely (Multi)Polygon\nper region. The air-temperature grid doesn't cover Alaska or Hawaii,\nwhich is why ncovr (contiguous-US only) is a natural fit." + "source": [ + "## Regions — US states from `geodatasets`\n", + "\n", + "`geodatasets.get_path(\"geoda.ncovr\")` returns a GeoPackage of the 49\n", + "contiguous-US counties (+ DC). We **dissolve** on `STATE_NAME` —\n", + "geopandas' equivalent of a `groupby` for geometries — to combine each\n", + "state's counties into one (Multi)Polygon. The result is exactly what\n", + "`ConservativeRegridder.from_polygons` wants: one shapely (Multi)Polygon\n", + "per region. The air-temperature grid doesn't cover Alaska or Hawaii,\n", + "which is why ncovr (contiguous-US only) is a natural fit." + ] }, { "cell_type": "code", @@ -73,7 +123,22 @@ "cell_type": "markdown", "id": "3a3b8ac5", "metadata": {}, - "source": "## Build the regridder and apply\n\n`from_polygons` takes flat 1D arrays of source and target polygons:\n\n- **Source polygons** come from the 1D grid coords via\n `polygons_from_coords`, which builds a rectangle per cell from the\n coordinate midpoints (so a 25×53 grid → 1325 source polygons).\n- **Target polygons** are just the states' `geometry` column.\n\nSource data has to be flattened to a single `src_cell` dimension to match\nthe flat polygon array. Constructing the regridder is the expensive step\n— it computes the area of every source/target polygon intersection.\nOnce built, `rgr.regrid(...)` is a sparse matrix-vector product against\nthe precomputed weights and is essentially instant." + "source": [ + "## Build the regridder and apply\n", + "\n", + "`from_polygons` takes flat 1D arrays of source and target polygons:\n", + "\n", + "- **Source polygons** come from the 1D grid coords via\n", + " `polygons_from_coords`, which builds a rectangle per cell from the\n", + " coordinate midpoints (so a 25×53 grid → 1325 source polygons).\n", + "- **Target polygons** are just the states' `geometry` column.\n", + "\n", + "Source data has to be flattened to a single `src_cell` dimension to match\n", + "the flat polygon array. Constructing the regridder is the expensive step\n", + "— it computes the area of every source/target polygon intersection.\n", + "Once built, `rgr.regrid(...)` is a sparse matrix-vector product against\n", + "the precomputed weights and is essentially instant." + ] }, { "cell_type": "code", @@ -103,7 +168,15 @@ "cell_type": "markdown", "id": "3542f836", "metadata": {}, - "source": "## Map + ranked bar chart\n\nStates filled by area-weighted mean temperature, with the source grid\nshown underneath for reference. Both panels share the same `vmin`/`vmax`\nso a state's color on the bar chart matches its color on the map. A\nsanity check: the warm/cool gradient should track latitude (Florida and\nthe Gulf states warmest, the Upper Midwest and Northeast coolest)." + "source": [ + "## Map + ranked bar chart\n", + "\n", + "States filled by area-weighted mean temperature, with the source grid\n", + "shown underneath for reference. Both panels share the same `vmin`/`vmax`\n", + "so a state's color on the bar chart matches its color on the map. A\n", + "sanity check: the warm/cool gradient should track latitude (Florida and\n", + "the Gulf states warmest, the Upper Midwest and Northeast coolest)." + ] }, { "cell_type": "code", @@ -111,13 +184,47 @@ "id": "bb8d0dda", "metadata": {}, "outputs": [], - "source": "fig, (ax_map, ax_bar) = plt.subplots(\n 1, 2, figsize=(14, 6), gridspec_kw={\"width_ratios\": [1.6, 1]},\n)\n\nair.plot(ax=ax_map, cmap=\"coolwarm\", alpha=0.5,\n cbar_kwargs={\"shrink\": 0.65, \"label\": \"grid mean T [°C]\"})\n\nvmin, vmax = float(air.min()), float(air.max())\nstates_plot = states.assign(mean_T=state_mean.values)\nstates_plot.plot(\n column=\"mean_T\", cmap=\"coolwarm\", ax=ax_map,\n edgecolor=\"black\", linewidth=0.4,\n vmin=vmin, vmax=vmax,\n)\nax_map.set_xlim(-128, -65); ax_map.set_ylim(22, 52)\nax_map.set_title(\"Annual mean surface T — states vs. source grid\")\nax_map.set_xlabel(\"longitude\"); ax_map.set_ylabel(\"latitude\")\n\nordered = state_mean.to_series().sort_values()\ncolors = plt.cm.coolwarm((ordered.values - vmin) / (vmax - vmin))\nax_bar.barh(ordered.index, ordered.values, color=colors)\nax_bar.set_xlabel(\"area-weighted mean T [°C]\")\nax_bar.tick_params(axis=\"y\", labelsize=7)\nax_bar.grid(axis=\"x\", alpha=0.3)\nplt.tight_layout()" + "source": [ + "fig, (ax_map, ax_bar) = plt.subplots(\n", + " 1, 2, figsize=(14, 6), gridspec_kw={\"width_ratios\": [1.6, 1]},\n", + ")\n", + "\n", + "air.plot(ax=ax_map, cmap=\"coolwarm\", alpha=0.5,\n", + " cbar_kwargs={\"shrink\": 0.65, \"label\": \"grid mean T [°C]\"})\n", + "\n", + "vmin, vmax = float(air.min()), float(air.max())\n", + "states_plot = states.assign(mean_T=state_mean.values)\n", + "states_plot.plot(\n", + " column=\"mean_T\", cmap=\"coolwarm\", ax=ax_map,\n", + " edgecolor=\"black\", linewidth=0.4,\n", + " vmin=vmin, vmax=vmax,\n", + ")\n", + "ax_map.set_xlim(-128, -65); ax_map.set_ylim(22, 52)\n", + "ax_map.set_title(\"Annual mean surface T — states vs. source grid\")\n", + "ax_map.set_xlabel(\"longitude\"); ax_map.set_ylabel(\"latitude\")\n", + "\n", + "ordered = state_mean.to_series().sort_values()\n", + "colors = plt.cm.coolwarm((ordered.values - vmin) / (vmax - vmin))\n", + "ax_bar.barh(ordered.index, ordered.values, color=colors)\n", + "ax_bar.set_xlabel(\"area-weighted mean T [°C]\")\n", + "ax_bar.tick_params(axis=\"y\", labelsize=7)\n", + "ax_bar.grid(axis=\"x\", alpha=0.3)\n", + "plt.tight_layout()" + ] }, { "cell_type": "markdown", "id": "803247d2", "metadata": {}, - "source": "## Conservation check\n\nThe internal area matrix `A[i, j] = area(state_i ∩ src_cell_j)` lets us\nverify conservation directly: integrating the source field weighted by\nsource-cell coverage should equal integrating the regridded field\nweighted by target-cell area. Equality to machine precision is the\ndefining property of *conservative* regridding." + "source": [ + "## Conservation check\n", + "\n", + "The internal area matrix `A[i, j] = area(state_i ∩ src_cell_j)` lets us\n", + "verify conservation directly: integrating the source field weighted by\n", + "source-cell coverage should equal integrating the regridded field\n", + "weighted by target-cell area. Equality to machine precision is the\n", + "defining property of *conservative* regridding." + ] }, { "cell_type": "code", @@ -126,9 +233,9 @@ "metadata": {}, "outputs": [], "source": [ - "A = rgr.areas # sparse (n_states, n_src)\n", - "tgt_area = np.ravel(A.sum(axis=1).todense())\n", - "src_cover = np.ravel(A.sum(axis=0).todense())\n", + "A = rgr.areas # (n_states, n_src)\n", + "tgt_area = rgr.target_areas\n", + "src_cover = rgr.source_coverage_areas\n", "\n", "direct = float((air.values.ravel() * src_cover).sum())\n", "via_regrid = float((state_mean.values * tgt_area).sum())\n", @@ -141,7 +248,17 @@ "cell_type": "markdown", "id": "ff49d6b7", "metadata": {}, - "source": "## Reuse: persist the regridder, apply to summer vs. winter\n\nThe weight matrix depends only on the source/target geometry, not on the\ndata. Save once with `to_netcdf`, reload with `from_netcdf`, and apply\nto any field on the same source grid — no need to rebuild the (expensive)\npolygon intersection. Below we use one saved regridder on JJA and DJF\nsubsets to compute per-state seasonal amplitude. Continental interior\nstates show the largest swing; Florida and California — moderated by\nocean and latitude — show the smallest." + "source": [ + "## Reuse: persist the regridder, apply to summer vs. winter\n", + "\n", + "The weight matrix depends only on the source/target geometry, not on the\n", + "data. Save once with `to_netcdf`, reload with `from_netcdf`, and apply\n", + "to any field on the same source grid — no need to rebuild the (expensive)\n", + "polygon intersection. Below we use one saved regridder on JJA and DJF\n", + "subsets to compute per-state seasonal amplitude. Continental interior\n", + "states show the largest swing; Florida and California — moderated by\n", + "ocean and latitude — show the smallest." + ] }, { "cell_type": "code", @@ -173,14 +290,6 @@ "print(\"\\nsmallest seasonal swing (maritime / subtropical):\")\n", "print(amplitude.sort_values().head(5))" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f47783c1-fb31-4db7-84e2-39e2a139d47d", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -198,8 +307,7 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.13" + "pygments_lexer": "ipython3" } }, "nbformat": 4, diff --git a/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb b/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb index b61a520..b1a939e 100644 --- a/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb +++ b/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb @@ -219,14 +219,6 @@ "same = np.array_equal(rgr.regrid(src_flat).values, rgr2.regrid(src_flat).values)\n", "print(f\"\\nreload bit-identical: {same}\")" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5c7eea93-d8dd-4838-bda3-77cada247e7e", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -244,8 +236,7 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.13" + "pygments_lexer": "ipython3" } }, "nbformat": 4, diff --git a/src/xarray_regrid/__init__.py b/src/xarray_regrid/__init__.py index 61d94c0..8696e1a 100644 --- a/src/xarray_regrid/__init__.py +++ b/src/xarray_regrid/__init__.py @@ -1,6 +1,7 @@ from xarray_regrid import methods from xarray_regrid.methods.conservative_2d import ( ConservativeRegridder, + RegridSpec, polygons_from_coords, ) from xarray_regrid.regrid import Regridder @@ -9,6 +10,7 @@ __all__ = [ "ConservativeRegridder", "Grid", + "RegridSpec", "Regridder", "create_regridding_dataset", "methods", diff --git a/src/xarray_regrid/methods/_conservative_2d_serialization.py b/src/xarray_regrid/methods/_conservative_2d_serialization.py new file mode 100644 index 0000000..989d0e4 --- /dev/null +++ b/src/xarray_regrid/methods/_conservative_2d_serialization.py @@ -0,0 +1,115 @@ +from datetime import datetime, timezone +from importlib.metadata import PackageNotFoundError, version +from pathlib import Path +from typing import Any + +import numpy as np +import xarray as xr + +from xarray_regrid.methods._conservative_2d_spec import RegridSpec + +try: + import sparse + + _HAS_SPARSE = True +except ImportError: # pragma: no cover + sparse = None + _HAS_SPARSE = False + +# Bump on breaking change to the on-disk format in ConservativeRegridder.to_netcdf. +_SCHEMA_VERSION = 1 + + +def _package_version() -> str: + try: + return version("xarray-regrid") + except PackageNotFoundError: + return "unknown" + + +def _coo_components( + weights: "sparse.COO | np.ndarray", +) -> tuple[np.ndarray, np.ndarray, np.ndarray, tuple[int, int]]: + if _HAS_SPARSE and isinstance(weights, sparse.COO): + coords = np.asarray(weights.coords) + return ( + coords[0].astype(np.int64, copy=False), + coords[1].astype(np.int64, copy=False), + np.asarray(weights.data), + weights.shape, + ) + arr = np.asarray(weights) + rows, cols = np.nonzero(arr) + return ( + rows.astype(np.int64, copy=False), + cols.astype(np.int64, copy=False), + arr[rows, cols], + arr.shape, + ) + + +def _coo_from_components( + row: np.ndarray, + col: np.ndarray, + data: np.ndarray, + shape: tuple[int, int], +) -> "sparse.COO | np.ndarray": + if _HAS_SPARSE: + return sparse.COO( + coords=np.stack([row, col]), + data=data, + shape=shape, + has_duplicates=False, + sorted=False, + ) + dense = np.zeros(shape, dtype=data.dtype if data.size else np.float64) + dense[row, col] = data + return dense + + +def _metadata_attrs( + spec: RegridSpec, source_coords: xr.Dataset, target_coords: xr.Dataset +) -> dict[str, Any]: + attrs: dict[str, Any] = { + "x_coord": spec.x_coord, + "y_coord": spec.y_coord, + "spherical": int(spec.spherical), + "src_dims": [str(d) for d in spec.src_dims], + "dst_dims": [str(d) for d in spec.dst_dims], + "src_shape": list(spec.src_shape), + "dst_shape": list(spec.dst_shape), + "xarray_regrid_version": _package_version(), + "created": datetime.now(tz=timezone.utc).isoformat(), + "schema_version": _SCHEMA_VERSION, + } + for prefix, ds, coord in [ + ("source_x", source_coords, spec.x_coord), + ("source_y", source_coords, spec.y_coord), + ("target_x", target_coords, spec.x_coord), + ("target_y", target_coords, spec.y_coord), + ]: + if coord and coord in ds.coords and ds[coord].size: + attrs[f"{prefix}_range"] = [float(ds[coord].min()), float(ds[coord].max())] + return attrs + + +def _metadata_from_attrs(attrs: dict[str, Any], path: Path) -> RegridSpec: + """Parse and validate regridding spec metadata from netCDF attrs.""" + schema_version = int(attrs.get("schema_version", 0)) + if schema_version != _SCHEMA_VERSION: + msg = ( + f"regridder file at {path} uses schema version {schema_version}; " + f"this xarray-regrid understands {_SCHEMA_VERSION}. " + "Upgrade xarray-regrid or re-save." + ) + raise ValueError(msg) + + return RegridSpec( + x_coord=str(attrs["x_coord"]), + y_coord=str(attrs["y_coord"]), + spherical=bool(int(attrs["spherical"])), + src_dims=tuple(str(d) for d in np.atleast_1d(attrs["src_dims"])), + dst_dims=tuple(str(d) for d in np.atleast_1d(attrs["dst_dims"])), + src_shape=tuple(int(s) for s in np.atleast_1d(attrs["src_shape"])), + dst_shape=tuple(int(s) for s in np.atleast_1d(attrs["dst_shape"])), + ) diff --git a/src/xarray_regrid/methods/_conservative_2d_spec.py b/src/xarray_regrid/methods/_conservative_2d_spec.py new file mode 100644 index 0000000..7f17854 --- /dev/null +++ b/src/xarray_regrid/methods/_conservative_2d_spec.py @@ -0,0 +1,15 @@ +from collections.abc import Hashable +from dataclasses import dataclass + + +@dataclass(frozen=True) +class RegridSpec: + """Canonical metadata describing a source->target regridding layout.""" + + src_dims: tuple[Hashable, ...] + dst_dims: tuple[Hashable, ...] + src_shape: tuple[int, ...] + dst_shape: tuple[int, ...] + x_coord: str + y_coord: str + spherical: bool diff --git a/src/xarray_regrid/methods/conservative_2d.py b/src/xarray_regrid/methods/conservative_2d.py index 0197b42..9be0763 100644 --- a/src/xarray_regrid/methods/conservative_2d.py +++ b/src/xarray_regrid/methods/conservative_2d.py @@ -22,9 +22,7 @@ from collections.abc import Hashable from concurrent.futures import ThreadPoolExecutor from dataclasses import dataclass -from datetime import datetime, timezone from functools import cached_property -from importlib.metadata import PackageNotFoundError, version from pathlib import Path from typing import Any, Literal, cast @@ -32,22 +30,18 @@ import xarray as xr from xarray_regrid import utils +from xarray_regrid.methods._conservative_2d_serialization import ( + _coo_components, + _coo_from_components, + _metadata_attrs, + _metadata_from_attrs, +) +from xarray_regrid.methods._conservative_2d_spec import RegridSpec from xarray_regrid.methods.conservative import get_valid_threshold NetcdfEngine = Literal["netcdf4", "scipy", "h5netcdf"] | None -def _package_version() -> str: - try: - return version("xarray-regrid") - except PackageNotFoundError: - return "unknown" - - -# Bump on breaking change to the on-disk format in ConservativeRegridder.to_netcdf. -_SCHEMA_VERSION = 1 - - try: import shapely from shapely import affinity @@ -192,6 +186,19 @@ def __init__( self._source_coords = source.coords.to_dataset() self._target_coords = target.coords.to_dataset() + @property + def spec(self) -> RegridSpec: + """Canonical metadata describing this regridder's layout.""" + return RegridSpec( + src_dims=self._src_dims, + dst_dims=self._dst_dims, + src_shape=self._src_shape, + dst_shape=self._dst_shape, + x_coord=self.x_coord, + y_coord=self.y_coord, + spherical=self.spherical, + ) + @cached_property def _forward(self) -> _Direction: return _Direction(self.areas) @@ -210,6 +217,16 @@ def backward_weights(self) -> "sparse.COO | np.ndarray": """The row-normalized backward weight matrix (target → source).""" return self._backward.weights + @property + def target_areas(self) -> np.ndarray: + """Area of each target cell overlapped by the source domain.""" + return _sum_matrix_axis_1d(self.areas, axis=1) + + @property + def source_coverage_areas(self) -> np.ndarray: + """Area of each source cell covered by target cells.""" + return _sum_matrix_axis_1d(self.areas, axis=0) + def regrid( self, data: xr.DataArray | xr.Dataset, @@ -220,13 +237,8 @@ def regrid( return _apply_stored_weights( data, direction=self._forward, - src_dims=self._src_dims, - dst_dims=self._dst_dims, - src_shape=self._src_shape, - dst_shape=self._dst_shape, + spec=self.spec, target_coords=self._target_coords, - x_coord=self.x_coord, - y_coord=self.y_coord, skipna=skipna, nan_threshold=nan_threshold, ) @@ -247,13 +259,15 @@ def transpose(self) -> "ConservativeRegridder": areas=_transpose_weights(self.areas), source_coords=self._target_coords, target_coords=self._source_coords, - src_dims=self._dst_dims, - dst_dims=self._src_dims, - src_shape=self._dst_shape, - dst_shape=self._src_shape, - x_coord=self.x_coord, - y_coord=self.y_coord, - spherical=self.spherical, + spec=RegridSpec( + src_dims=self._dst_dims, + dst_dims=self._src_dims, + src_shape=self._dst_shape, + dst_shape=self._src_shape, + x_coord=self.x_coord, + y_coord=self.y_coord, + spherical=self.spherical, + ), ) if "_forward" in self.__dict__: new.__dict__["_backward"] = self.__dict__["_forward"] @@ -288,7 +302,7 @@ def to_netcdf(self, path: str | Path, engine: NetcdfEngine = None) -> None: "_coo_data": (("nnz",), data), }, attrs={ - **_metadata_attrs(self), + **_metadata_attrs(self.spec, self._source_coords, self._target_coords), "n_dst": int(shape[0]), "n_src": int(shape[1]), }, @@ -329,7 +343,7 @@ def from_netcdf( areas=_coo_from_components(row, col, data, (n_dst, n_src)), source_coords=source_coords, target_coords=target_coords, - **meta, + spec=meta, ) @classmethod @@ -339,25 +353,19 @@ def _from_state( areas: "sparse.COO | np.ndarray", source_coords: xr.Dataset, target_coords: xr.Dataset, - src_dims: tuple[Hashable, ...], - dst_dims: tuple[Hashable, ...], - src_shape: tuple[int, ...], - dst_shape: tuple[int, ...], - x_coord: str, - y_coord: str, - spherical: bool, + spec: RegridSpec, ) -> "ConservativeRegridder": """Construct a regridder directly from its canonical state. Shared bypass of ``__init__`` used by :meth:`from_netcdf` and :meth:`from_polygons`; keeps the list of private attrs in one place.""" instance = object.__new__(cls) - instance.x_coord = x_coord - instance.y_coord = y_coord - instance.spherical = spherical - instance._src_dims = src_dims - instance._dst_dims = dst_dims - instance._src_shape = src_shape - instance._dst_shape = dst_shape + instance.x_coord = spec.x_coord + instance.y_coord = spec.y_coord + instance.spherical = spec.spherical + instance._src_dims = spec.src_dims + instance._dst_dims = spec.dst_dims + instance._src_shape = spec.src_shape + instance._dst_shape = spec.dst_shape instance.areas = areas instance._source_coords = source_coords instance._target_coords = target_coords @@ -434,13 +442,15 @@ def from_polygons( ), source_coords=xr.Dataset(coords={source_dim: np.arange(n_src)}), target_coords=tgt_ds, - src_dims=(source_dim,), - dst_dims=(target_dim,), - src_shape=(n_src,), - dst_shape=(n_dst,), - x_coord="", - y_coord="", - spherical=False, + spec=RegridSpec( + src_dims=(source_dim,), + dst_dims=(target_dim,), + src_shape=(n_src,), + dst_shape=(n_dst,), + x_coord="", + y_coord="", + spherical=False, + ), ) @@ -471,13 +481,8 @@ def polygons_from_coords( def _apply_stored_weights( data: xr.DataArray | xr.Dataset, direction: _Direction, - src_dims: tuple[Hashable, ...], - dst_dims: tuple[Hashable, ...], - src_shape: tuple[int, ...], - dst_shape: tuple[int, ...], + spec: RegridSpec, target_coords: xr.Dataset, - x_coord: str, - y_coord: str, skipna: bool, nan_threshold: float, ) -> xr.DataArray | xr.Dataset: @@ -487,16 +492,18 @@ def _apply_stored_weights( The apply matrix has shape ``(n_src, n_dst)`` so the matmul is ``(..., n_src) @ (n_src, n_dst) → (..., n_dst)`` with no per-call transpose. """ - actual_src_shape = tuple(int(data.sizes[d]) for d in src_dims if d in data.sizes) - if actual_src_shape != src_shape: + actual_src_shape = tuple( + int(data.sizes[d]) for d in spec.src_dims if d in data.sizes + ) + if actual_src_shape != spec.src_shape: msg = ( - f"source spatial shape {actual_src_shape} on dims {src_dims} does " - f"not match the regridder's expected shape {src_shape}" + f"source spatial shape {actual_src_shape} on dims {spec.src_dims} does " + f"not match the regridder's expected shape {spec.src_shape}" ) raise ValueError(msg) - src_tokens = tuple(f"__src_{d}" for d in src_dims) - data_renamed = data.rename(dict(zip(src_dims, src_tokens, strict=True))) + src_tokens = tuple(f"__src_{d}" for d in spec.src_dims) + data_renamed = data.rename(dict(zip(spec.src_dims, src_tokens, strict=True))) output_dtype = _result_dtype(data) result = xr.apply_ufunc( @@ -506,25 +513,31 @@ def _apply_stored_weights( "apply_weights": direction.apply_matrix, "coverage": direction.coverage, "coverage_all": direction.coverage_all, - "src_shape": src_shape, - "dst_shape": dst_shape, + "src_shape": spec.src_shape, + "dst_shape": spec.dst_shape, "skipna": skipna, "nan_threshold": nan_threshold, "output_dtype": output_dtype, }, input_core_dims=[list(src_tokens)], - output_core_dims=[list(dst_dims)], + output_core_dims=[list(spec.dst_dims)], exclude_dims=set(src_tokens), dask="parallelized", output_dtypes=[output_dtype], dask_gufunc_kwargs={ - "output_sizes": {d: int(target_coords.sizes[d]) for d in dst_dims}, + "output_sizes": {d: int(target_coords.sizes[d]) for d in spec.dst_dims}, "allow_rechunk": True, }, keep_attrs=True, ) - return _assign_target_coords(result, target_coords, dst_dims, x_coord, y_coord) + return _assign_target_coords( + result, + target_coords, + spec.dst_dims, + spec.x_coord, + spec.y_coord, + ) def _coverage_mask(areas: "sparse.COO | np.ndarray") -> np.ndarray: @@ -540,91 +553,11 @@ def _coverage_mask(areas: "sparse.COO | np.ndarray") -> np.ndarray: return np.asarray((arr > 0).any(axis=1)) -def _coo_components( - w: "sparse.COO | np.ndarray", -) -> tuple[np.ndarray, np.ndarray, np.ndarray, tuple[int, int]]: - if _HAS_SPARSE and isinstance(w, sparse.COO): - coords = np.asarray(w.coords) - return ( - coords[0].astype(np.int64, copy=False), - coords[1].astype(np.int64, copy=False), - np.asarray(w.data), - w.shape, - ) - arr = np.asarray(w) - rows, cols = np.nonzero(arr) - return ( - rows.astype(np.int64, copy=False), - cols.astype(np.int64, copy=False), - arr[rows, cols], - arr.shape, - ) - - -def _coo_from_components( - row: np.ndarray, - col: np.ndarray, - data: np.ndarray, - shape: tuple[int, int], -) -> "sparse.COO | np.ndarray": - if _HAS_SPARSE: - return sparse.COO( - coords=np.stack([row, col]), - data=data, - shape=shape, - has_duplicates=False, - sorted=False, - ) - dense = np.zeros(shape, dtype=data.dtype if data.size else np.float64) - dense[row, col] = data - return dense - - -def _metadata_attrs(regridder: ConservativeRegridder) -> dict[str, Any]: - attrs: dict[str, Any] = { - "x_coord": regridder.x_coord, - "y_coord": regridder.y_coord, - "spherical": int(regridder.spherical), - "src_dims": [str(d) for d in regridder._src_dims], - "dst_dims": [str(d) for d in regridder._dst_dims], - "src_shape": list(regridder._src_shape), - "dst_shape": list(regridder._dst_shape), - "xarray_regrid_version": _package_version(), - "created": datetime.now(tz=timezone.utc).isoformat(), - "schema_version": _SCHEMA_VERSION, - } - for prefix, ds, coord in [ - ("source_x", regridder._source_coords, regridder.x_coord), - ("source_y", regridder._source_coords, regridder.y_coord), - ("target_x", regridder._target_coords, regridder.x_coord), - ("target_y", regridder._target_coords, regridder.y_coord), - ]: - if coord and coord in ds.coords and ds[coord].size: - attrs[f"{prefix}_range"] = [float(ds[coord].min()), float(ds[coord].max())] - return attrs - - -def _metadata_from_attrs(attrs: dict[str, Any], path: Path) -> dict[str, Any]: - """Parse the kwargs needed by :meth:`ConservativeRegridder._from_state` out - of netCDF root attributes, validating ``schema_version``.""" - schema_version = int(attrs.get("schema_version", 0)) - if schema_version != _SCHEMA_VERSION: - msg = ( - f"regridder file at {path} uses schema version {schema_version}; " - f"this xarray-regrid understands {_SCHEMA_VERSION}. " - "Upgrade xarray-regrid or re-save." - ) - raise ValueError(msg) - - return { - "x_coord": str(attrs["x_coord"]), - "y_coord": str(attrs["y_coord"]), - "spherical": bool(int(attrs["spherical"])), - "src_dims": tuple(str(d) for d in np.atleast_1d(attrs["src_dims"])), - "dst_dims": tuple(str(d) for d in np.atleast_1d(attrs["dst_dims"])), - "src_shape": tuple(int(s) for s in np.atleast_1d(attrs["src_shape"])), - "dst_shape": tuple(int(s) for s in np.atleast_1d(attrs["dst_shape"])), - } +def _sum_matrix_axis_1d(areas: "sparse.COO | np.ndarray", axis: int) -> np.ndarray: + summed = areas.sum(axis=axis) + if hasattr(summed, "todense"): + summed = summed.todense() + return np.asarray(summed, dtype=np.float64).reshape(-1) def _normalize_longitude_coords( @@ -784,7 +717,8 @@ def _remap_columns_for_axis_sort( A row-major source flat index ``c = unravel(c, src_shape)`` has its ``axis_index`` component ``i_sorted`` permuted via ``i_orig = sort_idx[i_sorted]``; all other components are untouched. So - the new flat index is ``c_new = c // stride * stride + (i_orig - i_sorted) * inner + ...``. + the new flat index is + ``c_new = c // stride * stride + (i_orig - i_sorted) * inner + ...``. For separable 1D-rect grids (the only case that triggers this today) the sorted axis sits between an outer block of size ``outer`` and an inner block of size ``inner`` with ``stride = nx * inner``. diff --git a/src/xarray_regrid/regrid.py b/src/xarray_regrid/regrid.py index ad94647..fda6963 100644 --- a/src/xarray_regrid/regrid.py +++ b/src/xarray_regrid/regrid.py @@ -181,7 +181,7 @@ def conservative( Data regridded to the target dataset coordinates. """ if not 0.0 <= nan_threshold <= 1.0: - msg = "nan_threshold must be between [0, 1]]" + msg = "nan_threshold must be between [0, 1]" raise ValueError(msg) ds_target_grid = validate_input(self._obj, ds_target_grid, time_dim) diff --git a/src/xarray_regrid/utils.py b/src/xarray_regrid/utils.py index 7e80ca1..7253cca 100644 --- a/src/xarray_regrid/utils.py +++ b/src/xarray_regrid/utils.py @@ -80,7 +80,7 @@ def create_lat_lon_coords(grid: Grid) -> tuple[np.ndarray, np.ndarray]: grid.south, grid.north + grid.resolution_lat, grid.resolution_lat ) - if np.remainder((grid.east - grid.west), grid.resolution_lat) > 0: + if np.remainder((grid.east - grid.west), grid.resolution_lon) > 0: lon_coords = np.arange(grid.west, grid.east, grid.resolution_lon) else: lon_coords = np.arange( diff --git a/tests/test_conservative_2d.py b/tests/test_conservative_2d.py index aabb153..b81fab3 100644 --- a/tests/test_conservative_2d.py +++ b/tests/test_conservative_2d.py @@ -432,12 +432,11 @@ def test_from_polygons_mass_conservation(): s = rng.normal(size=src_polys.size) da = xr.DataArray(s, dims=("face",)) out = rgr.regrid(da).values - # Direct mass = sum_i s_i * (sum_j A_ij). Matches output if we multiply - # output by target-covered area = sum_i A_ij. - areas = rgr.areas # (n_tgt, n_src) - tgt_covered = areas.sum(axis=1).todense() + # Direct mass = sum_i s_i * source_coverage_i. Matches output if we + # multiply output by target-covered area. + tgt_covered = rgr.target_areas valid = tgt_covered > 0 - direct = float((s * areas.sum(axis=0).todense()).sum()) + direct = float((s * rgr.source_coverage_areas).sum()) via_regrid = float((out[valid] * tgt_covered[valid]).sum()) rel = abs(direct - via_regrid) / max(abs(direct), 1e-12) assert rel < 1e-12, f"rel err {rel:.2e}" From 2e89aca36b81db4aaaac1cbbdbad00cc4be44ba1 Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Sun, 19 Apr 2026 12:09:59 -0500 Subject: [PATCH 09/16] Add polygon-intersection conservative regridder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing axis-factored `.conservative` path is tight for rectilinear grids but can't express curvilinear or unstructured meshes. This adds a `ConservativeRegridder` that computes 2D cell-polygon intersections via shapely + STRtree, producing a sparse weight matrix applied as a single matmul. Unlike xESMF, it installs with a single `pip install shapely`, works on Windows, and composes with dask.distributed. Core: - `ConservativeRegridder(src, tgt, x_coord, y_coord, spherical=False)`: reusable regridder; builds weights once, applies to many fields; `.T` returns the transpose (target→source) sharing cached state. - `.from_polygons(src_polys, tgt_polys, ...)`: unstructured-mesh factory (MPAS/ICON/regions). - `spherical=True`: analytic Lambert cylindrical equal-area projection of cell edges — matches the factored path's sin-weighted accuracy on lat/lon grids without pulling in pyproj. - `.to_netcdf(path)` / `.from_netcdf(path)`: persist the sparse matrix plus `RegridderMetadata` (dim names, shapes, grid ranges, spherical flag, version, timestamp, schema version) and the source/target coord groups, for reproducibility and fast rebuild on subsequent runs. - Also exposes `.regrid.conservative_polygon(...)` on the xarray accessor, and a `polygons_from_coords` helper for mixing structured and unstructured grids. Correctness/perf wins verified by tests + experiments (not committed): - Rectilinear fast path (analytic box-overlap, no GEOS) → ~30x vs curvilinear at 1M cells. - ThreadPool around GEOS intersection → 3-4x on curvilinear. - Pre-transposed, index-sorted apply matrix cached on the regridder. - Module-level `warnings.filterwarnings` for sparse's spurious NaN warning (thread-safe under dask). - Input dtype preserved end-to-end (float32 in → float32 out). - Empty target coverage → NaN (regression-tested for polygon holes + out-of-domain cells). 100 tests added in `tests/test_conservative_polygon.py` covering reusability, transpose roundtrip, spherical vs planar, curvilinear, NaN thresholds, dtype preservation, from_polygons, mass conservation, and netCDF save/load. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../methods/conservative_polygon.py | 1159 +++++++++++++++++ tests/test_conservative_polygon.py | 576 ++++++++ 2 files changed, 1735 insertions(+) create mode 100644 src/xarray_regrid/methods/conservative_polygon.py create mode 100644 tests/test_conservative_polygon.py diff --git a/src/xarray_regrid/methods/conservative_polygon.py b/src/xarray_regrid/methods/conservative_polygon.py new file mode 100644 index 0000000..a385499 --- /dev/null +++ b/src/xarray_regrid/methods/conservative_polygon.py @@ -0,0 +1,1159 @@ +"""Polygon-intersection conservative regridding (planar only). + +General conservative regridding that computes 2D cell-polygon intersections +rather than the axis-factored 1D overlap approach used by ``conservative``. +Slower and more memory-intensive than the factored path for rectilinear grids, +but handles curvilinear grids that the factored approach cannot represent. + +Requires ``shapely >= 2.0``. If ``sparse`` is available, the weight matrix is +stored as ``sparse.COO``; otherwise a dense numpy matrix is used. +""" + +import os +import warnings +from collections.abc import Hashable +from concurrent.futures import ThreadPoolExecutor +from dataclasses import asdict, dataclass, field +from datetime import datetime, timezone +from functools import cache +from pathlib import Path +from typing import Any + +import numpy as np +import xarray as xr + + +@cache +def _package_version() -> str: + """Cached ``importlib.metadata`` lookup. Cheap on warm call.""" + try: + from importlib.metadata import version + return version("xarray-regrid") + except Exception: + return "unknown" + + +# Bump on breaking change to the on-disk format in ConservativeRegridder.to_netcdf. +_SCHEMA_VERSION = 1 + + +@dataclass(frozen=True) +class RegridderMetadata: + """Serialized build parameters of a :class:`ConservativeRegridder`. + + Fields become netCDF attributes via :meth:`to_attrs`; the loader + reconstructs via :meth:`from_attrs`. Keep new fields optional so older + files remain loadable. + """ + + x_coord: str + y_coord: str + spherical: bool + src_dims: tuple[str, ...] + dst_dims: tuple[str, ...] + src_shape: tuple[int, ...] + dst_shape: tuple[int, ...] + source_x_range: tuple[float, float] | None = None + source_y_range: tuple[float, float] | None = None + target_x_range: tuple[float, float] | None = None + target_y_range: tuple[float, float] | None = None + xarray_regrid_version: str = field(default_factory=_package_version) + created: str = field( + default_factory=lambda: datetime.now(tz=timezone.utc).isoformat() + ) + schema_version: int = _SCHEMA_VERSION + + def to_attrs(self) -> dict[str, Any]: + d = asdict(self) + d["spherical"] = int(d["spherical"]) + d["src_dims"] = list(d["src_dims"]) + d["dst_dims"] = list(d["dst_dims"]) + d["src_shape"] = list(d["src_shape"]) + d["dst_shape"] = list(d["dst_shape"]) + for key in ( + "source_x_range", "source_y_range", + "target_x_range", "target_y_range", + ): + if d[key] is None: + del d[key] + else: + d[key] = list(d[key]) + return d + + @classmethod + def from_attrs(cls, attrs: dict[str, Any]) -> "RegridderMetadata": + """Parse back from netCDF attributes. Missing optional fields default.""" + def _range(key: str) -> tuple[float, float] | None: + v = attrs.get(key) + if v is None: + return None + arr = np.atleast_1d(v) + return (float(arr[0]), float(arr[1])) + + return cls( + x_coord=str(attrs["x_coord"]), + y_coord=str(attrs["y_coord"]), + spherical=bool(int(attrs["spherical"])), + src_dims=tuple(str(d) for d in np.atleast_1d(attrs["src_dims"])), + dst_dims=tuple(str(d) for d in np.atleast_1d(attrs["dst_dims"])), + src_shape=tuple(int(s) for s in np.atleast_1d(attrs["src_shape"])), + dst_shape=tuple(int(s) for s in np.atleast_1d(attrs["dst_shape"])), + source_x_range=_range("source_x_range"), + source_y_range=_range("source_y_range"), + target_x_range=_range("target_x_range"), + target_y_range=_range("target_y_range"), + xarray_regrid_version=str(attrs.get("xarray_regrid_version", "unknown")), + created=str(attrs.get("created", "")), + # Default to 0 so files written before this attr existed fail the + # schema check rather than silently appearing current. + schema_version=int(attrs.get("schema_version", 0)), + ) + +try: + import shapely + from shapely.strtree import STRtree + + _HAS_SHAPELY = True +except ImportError: # pragma: no cover + shapely = None # type: ignore[assignment] + STRtree = None # type: ignore[assignment] + _HAS_SHAPELY = False + +try: + import sparse # type: ignore + + _HAS_SPARSE = True +except ImportError: # pragma: no cover + sparse = None # type: ignore[assignment] + _HAS_SPARSE = False + + +# We fill NaNs with 0 ourselves before matmul (see `_apply_core`), so sparse's +# "NaN will not be propagated" warning is spurious. A module-level filter +# avoids `warnings.catch_warnings` inside the hot matmul path, which is not +# thread-safe — dask threads would race on the global `warnings.filters` list. +warnings.filterwarnings( + "ignore", + message="Nan will not be propagated in matrix multiplication", + category=RuntimeWarning, +) + + +SHAPELY_IMPORT_ERROR = ( + "polygon conservative regridding requires shapely >= 2.0; " + "install with `pip install shapely`." +) + + +def _check_shapely() -> None: + if not _HAS_SHAPELY: + raise ImportError(SHAPELY_IMPORT_ERROR) + + +class ConservativeRegridder: + """Reusable planar-polygon conservative regridder. + + Build once from source and target grids; apply to many compatible fields + via :meth:`regrid` (or by calling the regridder). The raw intersection + area matrix is stored internally; the forward and backward row-normalized + weight matrices are lazily cached on first use. + + Handles rectilinear (1D coord arrays) and curvilinear (2D coord arrays) + grids; planar geometry only. Requires ``shapely >= 2.0``. + + Example: + >>> regridder = ConservativeRegridder(src_ds, tgt_ds, x_coord="lon", y_coord="lat") + >>> out = regridder.regrid(da) # forward + >>> back = regridder.T.regrid(out) # backward + """ + + def __init__( + self, + source: xr.DataArray | xr.Dataset, + target: xr.Dataset, + x_coord: str = "longitude", + y_coord: str = "latitude", + spherical: bool = False, + n_threads: int | None = None, + ) -> None: + _check_shapely() + src_dims = _spatial_dims(source, x_coord, y_coord) + dst_dims = _spatial_dims(target, x_coord, y_coord) + if not src_dims: + msg = f"source has no dims for coords {x_coord!r}, {y_coord!r}" + raise ValueError(msg) + if not dst_dims: + msg = f"target has no dims for coords {x_coord!r}, {y_coord!r}" + raise ValueError(msg) + src_grid = _grid_from_coords( + source, x_coord, y_coord, src_dims, spherical=spherical + ) + dst_grid = _grid_from_coords( + target, x_coord, y_coord, dst_dims, spherical=spherical + ) + self.spherical = spherical + + self.x_coord = x_coord + self.y_coord = y_coord + self._src_dims = src_dims + self._dst_dims = dst_dims + self._src_shape = tuple(int(source.sizes[d]) for d in src_dims) + self._dst_shape = tuple(int(target.sizes[d]) for d in dst_dims) + self._areas = _build_intersection_areas( + src_grid, dst_grid, n_threads=n_threads + ) + self._source_coords = source.coords.to_dataset() + self._target_coords = target.coords.to_dataset() + # `_*_apply` caches the transposed, index-sorted weight matrix used for + # `data @ W` matmul — avoiding sparse's per-call `.T` + `_sort_indices`. + self._fwd_weights: "sparse.COO | np.ndarray | None" = None + self._bwd_weights: "sparse.COO | np.ndarray | None" = None + self._fwd_apply: "sparse.COO | np.ndarray | None" = None + self._bwd_apply: "sparse.COO | np.ndarray | None" = None + self._fwd_coverage: np.ndarray | None = None + self._bwd_coverage: np.ndarray | None = None + + @property + def forward_weights(self) -> "sparse.COO | np.ndarray": + """The row-normalized forward weight matrix (source → target).""" + if self._fwd_weights is None: + self._fwd_weights = _row_normalize(self._areas) + return self._fwd_weights + + @property + def backward_weights(self) -> "sparse.COO | np.ndarray": + """The row-normalized backward weight matrix (target → source).""" + if self._bwd_weights is None: + self._bwd_weights = _row_normalize(_transpose_weights(self._areas)) + return self._bwd_weights + + def _forward_apply_matrix(self) -> "sparse.COO | np.ndarray": + if self._fwd_apply is None: + self._fwd_apply = _transpose_weights(self.forward_weights, sort=True) + return self._fwd_apply + + def _backward_apply_matrix(self) -> "sparse.COO | np.ndarray": + if self._bwd_apply is None: + self._bwd_apply = _transpose_weights(self.backward_weights, sort=True) + return self._bwd_apply + + def _forward_coverage(self) -> np.ndarray: + """Boolean (n_dst,) mask: which destination cells have any source + overlap. Lazily computed and cached alongside the forward weights.""" + if self._fwd_coverage is None: + self._fwd_coverage = _coverage_mask(self._areas) + return self._fwd_coverage + + def _backward_coverage(self) -> np.ndarray: + if self._bwd_coverage is None: + self._bwd_coverage = _coverage_mask(_transpose_weights(self._areas)) + return self._bwd_coverage + + def regrid( + self, + data: xr.DataArray | xr.Dataset, + skipna: bool = True, + nan_threshold: float = 1.0, + ) -> xr.DataArray | xr.Dataset: + """Regrid ``data`` forward (source → target).""" + return _apply_stored_weights( + data, + apply_weights=self._forward_apply_matrix(), + coverage=self._forward_coverage(), + src_dims=self._src_dims, + dst_dims=self._dst_dims, + src_shape=self._src_shape, + dst_shape=self._dst_shape, + target_coords=self._target_coords, + x_coord=self.x_coord, + y_coord=self.y_coord, + skipna=skipna, + nan_threshold=nan_threshold, + ) + + def __call__( + self, + data: xr.DataArray | xr.Dataset, + skipna: bool = True, + nan_threshold: float = 1.0, + ) -> xr.DataArray | xr.Dataset: + return self.regrid(data, skipna=skipna, nan_threshold=nan_threshold) + + def transpose(self) -> "ConservativeRegridder": + """Return the backward regridder (target → source), sharing the + underlying area matrix and both cached weight matrices.""" + new = object.__new__(ConservativeRegridder) + new.x_coord = self.x_coord + new.y_coord = self.y_coord + new.spherical = self.spherical + new._src_dims = self._dst_dims + new._dst_dims = self._src_dims + new._src_shape = self._dst_shape + new._dst_shape = self._src_shape + new._areas = _transpose_weights(self._areas) + new._source_coords = self._target_coords + new._target_coords = self._source_coords + new._fwd_weights = self._bwd_weights + new._bwd_weights = self._fwd_weights + new._fwd_apply = self._bwd_apply + new._bwd_apply = self._fwd_apply + new._fwd_coverage = self._bwd_coverage + new._bwd_coverage = self._fwd_coverage + return new + + @property + def T(self) -> "ConservativeRegridder": + """Alias for :meth:`transpose`.""" + return self.transpose() + + def __repr__(self) -> str: + nnz = getattr(self._areas, "nnz", None) + shape = getattr(self._areas, "shape", (None, None)) + nnz_str = f"nnz={nnz}" if nnz is not None else "dense" + return ( + f"ConservativeRegridder(src_dims={self._src_dims}, " + f"dst_dims={self._dst_dims}, {shape[0]}x{shape[1]}, {nnz_str})" + ) + + def metadata(self) -> RegridderMetadata: + """Return the :class:`RegridderMetadata` that :meth:`to_netcdf` would + write — useful for inspection without touching disk.""" + return RegridderMetadata( + x_coord=self.x_coord, + y_coord=self.y_coord, + spherical=self.spherical, + src_dims=tuple(str(d) for d in self._src_dims), + dst_dims=tuple(str(d) for d in self._dst_dims), + src_shape=self._src_shape, + dst_shape=self._dst_shape, + source_x_range=_coord_range(self._source_coords, self.x_coord), + source_y_range=_coord_range(self._source_coords, self.y_coord), + target_x_range=_coord_range(self._target_coords, self.x_coord), + target_y_range=_coord_range(self._target_coords, self.y_coord), + ) + + def to_netcdf( + self, path: str | Path, engine: str | None = None + ) -> None: + """Save the weight matrix and reproducibility metadata to a netCDF file. + + File layout: + + - root dataset: the sparse area matrix stored as three 1D variables + ``_coo_row``, ``_coo_col``, ``_coo_data`` (``shape=(n_dst, n_src)`` + carried on root-dataset attributes), plus :class:`RegridderMetadata` + fields as attributes. + - ``/source_coords`` group: coord-only Dataset capturing the source grid. + - ``/target_coords`` group: coord-only Dataset capturing the target grid. + + Groups require an engine that supports them (``netcdf4`` or + ``h5netcdf``); ``engine`` is forwarded to :func:`xarray.Dataset.to_netcdf`. + """ + path = Path(path) + row, col, data, shape = _coo_components(self._areas) + ds_weights = xr.Dataset( + { + "_coo_row": (("nnz",), row), + "_coo_col": (("nnz",), col), + "_coo_data": (("nnz",), data), + }, + attrs={ + **self.metadata().to_attrs(), + "n_dst": int(shape[0]), + "n_src": int(shape[1]), + }, + ) + ds_weights.to_netcdf(path, mode="w", engine=engine) + self._source_coords.to_netcdf( + path, mode="a", group="source_coords", engine=engine + ) + self._target_coords.to_netcdf( + path, mode="a", group="target_coords", engine=engine + ) + + @classmethod + def from_netcdf( + cls, path: str | Path, engine: str | None = None + ) -> "ConservativeRegridder": + """Reload a regridder previously written with :meth:`to_netcdf`. + + Validates ``schema_version``; raises :class:`ValueError` if the file + was written by an incompatible version. + """ + path = Path(path) + with xr.open_dataset(path, engine=engine) as ds_weights: + attrs = dict(ds_weights.attrs) + n_dst = int(attrs.pop("n_dst")) + n_src = int(attrs.pop("n_src")) + row = np.asarray(ds_weights["_coo_row"].values) + col = np.asarray(ds_weights["_coo_col"].values) + data = np.asarray(ds_weights["_coo_data"].values) + meta = RegridderMetadata.from_attrs(attrs) + if meta.schema_version != _SCHEMA_VERSION: + msg = ( + f"regridder file at {path} uses schema version " + f"{meta.schema_version}; this xarray-regrid understands " + f"{_SCHEMA_VERSION}. Upgrade xarray-regrid or re-save." + ) + raise ValueError(msg) + + with xr.open_dataset(path, group="source_coords", engine=engine) as g: + source_coords = g.load() + with xr.open_dataset(path, group="target_coords", engine=engine) as g: + target_coords = g.load() + + return cls._from_state( + areas=_coo_from_components(row, col, data, (n_dst, n_src)), + source_coords=source_coords, + target_coords=target_coords, + src_dims=meta.src_dims, + dst_dims=meta.dst_dims, + src_shape=meta.src_shape, + dst_shape=meta.dst_shape, + x_coord=meta.x_coord, + y_coord=meta.y_coord, + spherical=meta.spherical, + ) + + @classmethod + def _from_state( + cls, + *, + areas: "sparse.COO | np.ndarray", + source_coords: xr.Dataset, + target_coords: xr.Dataset, + src_dims: tuple[Hashable, ...], + dst_dims: tuple[Hashable, ...], + src_shape: tuple[int, ...], + dst_shape: tuple[int, ...], + x_coord: str, + y_coord: str, + spherical: bool, + ) -> "ConservativeRegridder": + """Construct a regridder directly from its canonical state. Shared + bypass of ``__init__`` used by :meth:`from_netcdf` and + :meth:`from_polygons`; keeps the list of private attrs in one place.""" + instance = object.__new__(cls) + instance.x_coord = x_coord + instance.y_coord = y_coord + instance.spherical = spherical + instance._src_dims = src_dims + instance._dst_dims = dst_dims + instance._src_shape = src_shape + instance._dst_shape = dst_shape + instance._areas = areas + instance._source_coords = source_coords + instance._target_coords = target_coords + instance._fwd_weights = None + instance._bwd_weights = None + instance._fwd_apply = None + instance._bwd_apply = None + instance._fwd_coverage = None + instance._bwd_coverage = None + return instance + + @classmethod + def from_polygons( + cls, + source_polygons: np.ndarray, + target_polygons: np.ndarray, + source_dim: str = "cell", + target_dim: str = "cell", + target_coords: xr.Dataset | None = None, + n_threads: int | None = None, + ) -> "ConservativeRegridder": + """Build a regridder from explicit shapely polygon arrays. + + Use this for unstructured meshes (MPAS, ICON, finite-element), arbitrary + polygon targets (countries, watersheds), or any combination the + structured-grid path cannot express. + + Args: + source_polygons: 1D array of shapely Polygons for source cells. + target_polygons: 1D array of shapely Polygons for target cells. + source_dim: Name of the single dim carrying source cells on the + data passed to :meth:`regrid`. Default ``"cell"``. + target_dim: Name of the single dim carrying target cells on the + output. Default ``"cell"``. + target_coords: Optional xr.Dataset providing coordinate variables + along ``target_dim`` (and any auxiliary coords) to reattach on + the output. If None, the output is given a bare integer index. + n_threads: Thread count for GEOS intersection. + + Returns: + A ``ConservativeRegridder`` that accepts data with ``source_dim`` + in place of the structured grid's spatial dims. + + Intersection geometry is planar in the input polygons' coordinate + space. If the polygons represent lat/lon cells, project them into an + equal-area CRS first (or use the structured path with ``spherical=True``). + Polygons that cross the antimeridian must be split beforehand. + """ + _check_shapely() + src_polys = np.asarray(source_polygons) + dst_polys = np.asarray(target_polygons) + if src_polys.ndim != 1: + msg = "source_polygons must be a 1D array of shapely Polygons" + raise ValueError(msg) + if dst_polys.ndim != 1: + msg = "target_polygons must be a 1D array of shapely Polygons" + raise ValueError(msg) + + src_grid = _Grid( + polys=src_polys, bounds=shapely.bounds(src_polys), rectilinear=False, + ) + dst_grid = _Grid( + polys=dst_polys, bounds=shapely.bounds(dst_polys), rectilinear=False, + ) + n_src = int(src_polys.size) + n_dst = int(dst_polys.size) + tgt_ds = ( + target_coords + if target_coords is not None + else xr.Dataset(coords={target_dim: np.arange(n_dst)}) + ) + return cls._from_state( + areas=_build_intersection_areas(src_grid, dst_grid, n_threads=n_threads), + source_coords=xr.Dataset(coords={source_dim: np.arange(n_src)}), + target_coords=tgt_ds, + src_dims=(source_dim,), + dst_dims=(target_dim,), + src_shape=(n_src,), + dst_shape=(n_dst,), + x_coord="", + y_coord="", + spherical=False, + ) + + +def polygons_from_coords( + x: np.ndarray, + y: np.ndarray, + spherical: bool = False, +) -> np.ndarray: + """Build a 1D array of shapely cell polygons from 1D or 2D center coords. + + Convenience for mixing structured and unstructured regridding. E.g. build + target polygons for a regular lat/lon grid, then pass them together with + an unstructured source mesh to :meth:`ConservativeRegridder.from_polygons`. + + Args: + x: 1D or 2D array of cell-center x coordinates. + y: 1D or 2D array of cell-center y coordinates. + spherical: If True, apply a cylindrical equal-area projection to 1D + lat/lon (degrees) before building rectangles — matches the + structured-grid ``spherical=True`` path. + + Returns: + A 1D numpy array of shapely Polygons in row-major (y, x) order. + """ + _check_shapely() + x = np.asarray(x) + y = np.asarray(y) + if spherical: + if x.ndim != 1 or y.ndim != 1: + msg = "spherical=True requires 1D lat/lon arrays" + raise ValueError(msg) + return _build_cea_grid(x, y).polys + return _build_grid(x, y).polys + + +def polygon_conservative_regrid( + data: xr.DataArray | xr.Dataset, + target_ds: xr.Dataset, + x_coord: str = "longitude", + y_coord: str = "latitude", + spherical: bool = False, + skipna: bool = True, + nan_threshold: float = 1.0, + n_threads: int | None = None, +) -> xr.DataArray | xr.Dataset: + """Conservative regridding via explicit 2D polygon intersection. + + One-shot convenience wrapper: constructs a :class:`ConservativeRegridder` + and applies it once. For repeated regridding to the same target, construct + a ``ConservativeRegridder`` directly and reuse it. + + Args: + data: Input data on the source grid. + target_ds: Dataset defining the target grid; must expose ``x_coord`` and + ``y_coord`` as (1D or 2D) coordinate variables. + x_coord: Name of the x (longitude-like) coordinate variable, shared + between source and target. + y_coord: Name of the y (latitude-like) coordinate variable, shared + between source and target. + spherical: If True, assume ``x_coord``/``y_coord`` are longitude/latitude + in degrees and project cells into Lambert cylindrical equal-area + space before intersecting — gives correct spherical area weights + at the same cost as the planar fast path. Rectilinear (1D coord) + grids only. + skipna: If True, propagate NaNs into the weighted mean via a two-pass + sum (values and valid mask), matching the ``conservative`` method. + nan_threshold: Keep output cells whose valid source fraction is at + least ``nan_threshold``. + n_threads: Thread count for parallel GEOS intersection. + + Returns: + Regridded data on the target grid, preserving non-spatial dims. + """ + regridder = ConservativeRegridder( + data, target_ds, + x_coord=x_coord, y_coord=y_coord, + spherical=spherical, n_threads=n_threads, + ) + return regridder.regrid(data, skipna=skipna, nan_threshold=nan_threshold) + + +def _apply_stored_weights( + data: xr.DataArray | xr.Dataset, + apply_weights: "sparse.COO | np.ndarray", + coverage: np.ndarray, + src_dims: tuple[Hashable, ...], + dst_dims: tuple[Hashable, ...], + src_shape: tuple[int, ...], + dst_shape: tuple[int, ...], + target_coords: xr.Dataset, + x_coord: str, + y_coord: str, + skipna: bool, + nan_threshold: float, +) -> xr.DataArray | xr.Dataset: + """Apply a cached, pre-transposed weight matrix to ``data`` via + ``xr.apply_ufunc``. + + ``apply_weights`` has shape ``(n_src, n_dst)`` so the matmul is + ``(..., n_src) @ (n_src, n_dst) → (..., n_dst)`` with no per-call transpose. + """ + # apply_ufunc(dask="parallelized") needs each core dim to be a single + # chunk. Only rechunk if data is already dask-backed — don't inadvertently + # dask-ify a numpy-backed input. + if getattr(data, "chunks", None) is not None: + split = { + d: -1 for d in src_dims + if d in data.dims and len(data.chunksizes.get(d, ())) > 1 + } + if split: + data = data.chunk(split) + + actual_src_shape = tuple(int(data.sizes[d]) for d in src_dims if d in data.sizes) + if actual_src_shape != src_shape: + msg = ( + f"source spatial shape {actual_src_shape} on dims {src_dims} does " + f"not match the regridder's expected shape {src_shape}" + ) + raise ValueError(msg) + + src_tokens = tuple(f"__src_{d}" for d in src_dims) + data_renamed = data.rename({s: t for s, t in zip(src_dims, src_tokens)}) + + output_dtype = _result_dtype(data) + result = xr.apply_ufunc( + _apply_core, + data_renamed, + kwargs={ + "apply_weights": apply_weights, + "coverage": coverage, + "coverage_all": bool(coverage.all()), + "src_shape": src_shape, + "dst_shape": dst_shape, + "skipna": skipna, + "nan_threshold": nan_threshold, + "output_dtype": output_dtype, + }, + input_core_dims=[list(src_tokens)], + output_core_dims=[list(dst_dims)], + exclude_dims=set(src_tokens), + dask="parallelized", + output_dtypes=[output_dtype], + dask_gufunc_kwargs={ + "output_sizes": {d: int(target_coords.sizes[d]) for d in dst_dims}, + "allow_rechunk": False, + }, + keep_attrs=True, + ) + + result = _assign_target_coords(result, target_coords, dst_dims, x_coord, y_coord) + return result + + +def _coverage_mask(areas: "sparse.COO | np.ndarray") -> np.ndarray: + """Return a boolean ``(n_dst,)`` mask of target cells with any source + overlap, derived from the raw area matrix.""" + if _HAS_SPARSE and isinstance(areas, sparse.COO): + # COO sparse: row_sum is 0 iff row has no nonzero entries. + n_dst = int(areas.shape[0]) + mask = np.zeros(n_dst, dtype=bool) + mask[areas.coords[0]] = True + return mask + arr = np.asarray(areas) + return (arr > 0).any(axis=1) + + +def _coo_components( + w: "sparse.COO | np.ndarray", +) -> tuple[np.ndarray, np.ndarray, np.ndarray, tuple[int, int]]: + if _HAS_SPARSE and isinstance(w, sparse.COO): + coords = np.asarray(w.coords) + return ( + coords[0].astype(np.int64, copy=False), + coords[1].astype(np.int64, copy=False), + np.asarray(w.data), + w.shape, + ) + arr = np.asarray(w) + rows, cols = np.nonzero(arr) + return rows.astype(np.int64, copy=False), cols.astype(np.int64, copy=False), arr[rows, cols], arr.shape + + +def _coo_from_components( + row: np.ndarray, + col: np.ndarray, + data: np.ndarray, + shape: tuple[int, int], +) -> "sparse.COO | np.ndarray": + if _HAS_SPARSE: + return sparse.COO( + coords=np.stack([row, col]), + data=data, + shape=shape, + has_duplicates=False, + sorted=False, + ) + dense = np.zeros(shape, dtype=data.dtype if data.size else np.float64) + dense[row, col] = data + return dense + + +def _coord_range( + ds: xr.Dataset, coord_name: str +) -> tuple[float, float] | None: + """Return ``(min, max)`` of a coord, or ``None`` when it isn't on the + Dataset (e.g., the integer-index stub emitted by ``from_polygons``).""" + if not coord_name or coord_name not in ds.coords: + return None + arr = np.asarray(ds[coord_name].values) + if arr.size == 0: + return None + return float(arr.min()), float(arr.max()) + + +def _transpose_weights( + w: "sparse.COO | np.ndarray", *, sort: bool = False +) -> "sparse.COO | np.ndarray": + """Materialize a transposed weight matrix. + + `sparse.COO.T` is a lazy view that re-sorts indices on each downstream + matmul, so callers relying on ``.coords[0]`` being row indices or wanting + a hot matmul path should materialize once here. Pass ``sort=True`` to + additionally trigger the sort ahead of time (used for the apply matrix). + """ + if _HAS_SPARSE and isinstance(w, sparse.COO): + t = w.T + out = sparse.COO( + coords=np.asarray(t.coords), + data=np.asarray(t.data), + shape=t.shape, + has_duplicates=False, + sorted=False, + ) + if sort: + out._sort_indices() + return out + return np.asarray(w).T.copy() + + +def _spatial_dims( + obj: xr.DataArray | xr.Dataset, x_coord: str, y_coord: str +) -> tuple[Hashable, ...]: + """Return the spatial dim order to feed to apply_ufunc. + + For 1D rectilinear coords (x and y ride on separate dims), canonicalize to + ``(y_dim, x_dim)`` so the flattened cell order matches the polygon order + emitted by the fast-path in ``_build_grid``. For curvilinear (2D) coords, + preserve the dim order already present on ``obj``. + """ + if x_coord not in obj.coords or y_coord not in obj.coords: + return () + xd = obj[x_coord].dims + yd = obj[y_coord].dims + if len(xd) == 1 and len(yd) == 1 and xd[0] != yd[0]: + return (yd[0], xd[0]) + dims = set(xd) | set(yd) + return tuple(d for d in obj.dims if d in dims) + + +def _grid_from_coords( + obj: xr.DataArray | xr.Dataset, + x_coord: str, + y_coord: str, + dims: tuple[Hashable, ...], + spherical: bool = False, +) -> "_Grid": + """Build a :class:`_Grid` from the object's x/y coordinates. + + If both coords are 1D and ride on separate dims, pass them as separate 1D + vectors to trigger the rectilinear fast path. Otherwise broadcast to a + common N-D array in ``dims`` order for the curvilinear path. + + If ``spherical`` is True, coordinates are assumed to be longitude (x) and + latitude (y) in degrees, and cells are projected into a Lambert cylindrical + equal-area space (x' = lon_rad, y' = sin(lat_rad)) before constructing the + cell polygons. This gives mass-conservative weights on the sphere at the + same cost as the planar fast path. Only supported for rectilinear (1D + coord) grids in this version. + """ + xd = obj[x_coord] + yd = obj[y_coord] + is_rectilinear = ( + xd.ndim == 1 and yd.ndim == 1 and xd.dims[0] != yd.dims[0] + ) + if spherical: + if not is_rectilinear: + msg = ( + "spherical=True is only supported for rectilinear (1D lat/lon) " + "coordinate arrays in this version." + ) + raise NotImplementedError(msg) + return _build_cea_grid( + np.asarray(xd.values), np.asarray(yd.values) + ) + if is_rectilinear: + return _build_grid(np.asarray(xd.values), np.asarray(yd.values)) + xc, yc = xr.broadcast(obj[x_coord], obj[y_coord]) + xc = xc.transpose(*dims) + yc = yc.transpose(*dims) + return _build_grid(np.asarray(xc.values), np.asarray(yc.values)) + + +def _build_cea_grid(lon_centers: np.ndarray, lat_centers: np.ndarray) -> "_Grid": + """Build a rectilinear :class:`_Grid` whose cell polygons are in Lambert + cylindrical equal-area coordinates (x' = lon_rad, y' = sin(lat_rad)). + + Projecting *edges* analytically — rather than projecting centers and then + re-midpointing — is required because ``sin()`` is nonlinear: the projected + midpoint of two lat centers is not the same as the midpoint of two + projected lat edges. + """ + _check_shapely() + if lon_centers.size < 2 or lat_centers.size < 2: + msg = "spherical mode requires at least two cells per dimension" + raise ValueError(msg) + lat_edges_deg = np.clip(_infer_1d_edges(lat_centers), -90.0, 90.0) + lon_edges_deg = _infer_1d_edges(lon_centers) + return _rect_grid_from_edges( + np.deg2rad(lon_edges_deg), + np.sin(np.deg2rad(lat_edges_deg)), + ) + + +def _infer_1d_edges(centers: np.ndarray) -> np.ndarray: + """Return cell edges from 1D centers: midpoints between consecutive + centers, with symmetric reflection for the two outer bounds.""" + c = np.asarray(centers, dtype=float) + if c.size < 2: + msg = "need at least two centers to infer cell edges" + raise ValueError(msg) + mids = 0.5 * (c[:-1] + c[1:]) + left = 2 * c[0] - mids[0] + right = 2 * c[-1] - mids[-1] + return np.concatenate([[left], mids, [right]]) + + +def _infer_2d_corners( + xc: np.ndarray, yc: np.ndarray +) -> tuple[np.ndarray, np.ndarray]: + """Return (ny+1, nx+1) cell-corner arrays from 2D cell-center arrays. + + Interior corners are the mean of the four surrounding centers; boundary + corners are reflected from the adjacent interior row/column. + """ + assert xc.shape == yc.shape and xc.ndim == 2 + + def corners(a: np.ndarray) -> np.ndarray: + ny, nx = a.shape + pad = np.empty((ny + 2, nx + 2), dtype=a.dtype) + pad[1:-1, 1:-1] = a + pad[0, 1:-1] = 2 * a[0, :] - a[1, :] + pad[-1, 1:-1] = 2 * a[-1, :] - a[-2, :] + pad[1:-1, 0] = 2 * a[:, 0] - a[:, 1] + pad[1:-1, -1] = 2 * a[:, -1] - a[:, -2] + pad[0, 0] = 2 * pad[0, 1] - pad[0, 2] + pad[0, -1] = 2 * pad[0, -2] - pad[0, -3] + pad[-1, 0] = 2 * pad[-1, 1] - pad[-1, 2] + pad[-1, -1] = 2 * pad[-1, -2] - pad[-1, -3] + return 0.25 * (pad[:-1, :-1] + pad[1:, :-1] + pad[:-1, 1:] + pad[1:, 1:]) + + return corners(xc.astype(float)), corners(yc.astype(float)) + + +@dataclass +class _Grid: + """Cached cell geometry for a structured grid. + + ``polys`` is a flat (n_cells,) object array of shapely Polygons. + ``bounds`` is a (n_cells, 4) ``(minx, miny, maxx, maxy)`` array cached for + the STRtree / candidate-search path. ``rectilinear`` is True when both the + source x and y were 1D coordinate arrays (axis-aligned rectangles) — the + weight builder uses this to skip GEOS polygon clipping and compute + intersection areas analytically from the bounds. + """ + + polys: np.ndarray + bounds: np.ndarray + rectilinear: bool + + +def _rect_grid_from_edges(xe: np.ndarray, ye: np.ndarray) -> _Grid: + """Build a rectilinear :class:`_Grid` from already-prepared edge arrays. + + Shared by the raw-planar (:func:`_build_grid` 1D branch) and the analytic + equal-area (:func:`_build_cea_grid`) paths. + """ + x0, y0 = np.meshgrid(xe[:-1], ye[:-1], indexing="xy") + x1, y1 = np.meshgrid(xe[1:], ye[1:], indexing="xy") + x0f, y0f, x1f, y1f = x0.ravel(), y0.ravel(), x1.ravel(), y1.ravel() + polys = shapely.box(x0f, y0f, x1f, y1f) + bounds = np.stack([x0f, y0f, x1f, y1f], axis=1) + return _Grid(polys=polys, bounds=bounds, rectilinear=True) + + +def _build_grid(xc: np.ndarray, yc: np.ndarray) -> _Grid: + """Return a _Grid of cell geometry for a structured grid. + + Accepts 1D (rectilinear, separate x and y vectors) or 2D (curvilinear, + co-shaped center arrays) inputs. Output order is row-major in the input dim + order: for 2D inputs of shape (ny, nx) the polygons correspond to cells + reshaped as ``(ny, nx)``. + """ + _check_shapely() + if xc.ndim == 1 and yc.ndim == 1: + xe = _infer_1d_edges(xc.astype(float)) + ye = _infer_1d_edges(yc.astype(float)) + return _rect_grid_from_edges(xe, ye) + if xc.ndim == 2 and yc.ndim == 2: + xcorn, ycorn = _infer_2d_corners(xc, yc) + ny, nx = xc.shape + c00 = np.stack([xcorn[:-1, :-1], ycorn[:-1, :-1]], axis=-1) + c10 = np.stack([xcorn[:-1, 1:], ycorn[:-1, 1:]], axis=-1) + c11 = np.stack([xcorn[1:, 1:], ycorn[1:, 1:]], axis=-1) + c01 = np.stack([xcorn[1:, :-1], ycorn[1:, :-1]], axis=-1) + rings = np.stack([c00, c10, c11, c01, c00], axis=2).reshape(ny * nx, 5, 2) + polys = shapely.polygons(rings) + return _Grid(polys=polys, bounds=shapely.bounds(polys), rectilinear=False) + msg = "x and y coordinate arrays must both be 1D or both 2D" + raise ValueError(msg) + + +def _build_intersection_areas( + src: _Grid, dst: _Grid, n_threads: int | None = None +) -> "sparse.COO | np.ndarray": + """Build the (n_dst, n_src) raw area-intersection matrix ``A[i, j] = + area(dst_i ∩ src_j)``. + + This is the unnormalized matrix. Row-normalize via :func:`_row_normalize` + to get forward weights; transpose first for backward (target → source). + + When both grids are rectilinear (axis-aligned rectangles) intersection + areas are computed analytically from the bounds, skipping GEOS clipping. + """ + _check_shapely() + n_dst = int(len(dst.polys)) + n_src = int(len(src.polys)) + + tree = STRtree(src.polys) + pairs = tree.query(dst.polys, predicate="intersects") + dst_idx = np.asarray(pairs[0]) + src_idx = np.asarray(pairs[1]) + + if dst_idx.size == 0: + return _empty_weights(n_dst, n_src) + + if src.rectilinear and dst.rectilinear: + sb = src.bounds[src_idx] + db = dst.bounds[dst_idx] + dx = np.minimum(sb[:, 2], db[:, 2]) - np.maximum(sb[:, 0], db[:, 0]) + dy = np.minimum(sb[:, 3], db[:, 3]) - np.maximum(sb[:, 1], db[:, 1]) + areas = np.maximum(dx, 0.0) * np.maximum(dy, 0.0) + else: + areas = _intersection_areas_threaded( + dst.polys[dst_idx], src.polys[src_idx], n_threads=n_threads + ) + + keep = areas > 0 + dst_idx = dst_idx[keep] + src_idx = src_idx[keep] + areas = areas[keep] + + if dst_idx.size == 0: + return _empty_weights(n_dst, n_src) + + if _HAS_SPARSE: + return sparse.COO( + coords=np.stack([dst_idx, src_idx]), + data=areas.astype(np.float64), + shape=(n_dst, n_src), + has_duplicates=False, + sorted=False, + ) + a_dense = np.zeros((n_dst, n_src), dtype=np.float64) + a_dense[dst_idx, src_idx] = areas + return a_dense + + +def _row_normalize( + areas: "sparse.COO | np.ndarray", +) -> "sparse.COO | np.ndarray": + """Normalize rows of an area matrix so each row sums to 1 (rows with no + overlap stay all-zero, which produces NaN output under the apply path).""" + if _HAS_SPARSE and isinstance(areas, sparse.COO): + n_dst = areas.shape[0] + dst_idx = areas.coords[0] + src_idx = areas.coords[1] + data = areas.data + if data.size == 0: + return areas + row_sum = np.bincount(dst_idx, weights=data, minlength=n_dst) + new_data = data / row_sum[dst_idx] + return sparse.COO( + coords=np.stack([dst_idx, src_idx]), + data=new_data, + shape=areas.shape, + has_duplicates=False, + sorted=False, + ) + row_sum = areas.sum(axis=1, keepdims=True) + row_sum = np.where(row_sum == 0, 1.0, row_sum) + return areas / row_sum + + +def _intersection_areas_threaded( + a: np.ndarray, b: np.ndarray, n_threads: int | None +) -> np.ndarray: + """Compute per-pair intersection areas ``area(a[i] & b[i])`` over numpy + arrays of shapely geometries, optionally parallelized via threads. + + Shapely 2.x releases the GIL for GEOS ops, so a ``ThreadPoolExecutor`` + gives near-linear speedup on multi-core machines without pickling data. + """ + _check_shapely() + n = len(a) + if n_threads is None: + n_threads = min(os.cpu_count() or 1, 8) + # Amortize thread-pool overhead only when there's meaningful work. + if n < 50_000: + n_threads = 1 + if n_threads <= 1 or n == 0: + return shapely.area(shapely.intersection(a, b)) + + splits = np.array_split(np.arange(n), n_threads) + + def _work(idx: np.ndarray) -> np.ndarray: + return shapely.area(shapely.intersection(a[idx], b[idx])) + + with ThreadPoolExecutor(max_workers=n_threads) as pool: + parts = list(pool.map(_work, splits)) + return np.concatenate(parts) + + +def _empty_weights(n_dst: int, n_src: int) -> "sparse.COO | np.ndarray": + if _HAS_SPARSE: + return sparse.COO( + coords=np.zeros((2, 0), dtype=np.int64), + data=np.zeros(0, dtype=np.float64), + shape=(n_dst, n_src), + ) + return np.zeros((n_dst, n_src), dtype=np.float64) + + +def _apply_core( + arr: np.ndarray, + apply_weights: Any, + coverage: np.ndarray, + coverage_all: bool, + src_shape: tuple[int, ...], + dst_shape: tuple[int, ...], + skipna: bool, + nan_threshold: float, + output_dtype: np.dtype, +) -> np.ndarray: + """Apply a pre-transposed weight matrix along the trailing spatial dims. + + ``arr`` has shape ``(..., *src_shape)``; ``apply_weights`` has shape + ``(n_src, n_dst)`` — returns ``(..., *dst_shape)``. + + ``coverage`` is a boolean ``(n_dst,)`` mask: target cells with any source + overlap. ``coverage_all`` is precomputed so every block skips the + ``coverage.all()`` scan. Uncovered cells are always masked to NaN in the + output, regardless of ``skipna`` — domain boundaries and polygon holes + produce NaN (matches the axis-factored ``conservative`` method). + """ + n_spatial = len(src_shape) + leading_shape = arr.shape[:-n_spatial] if n_spatial > 0 else arr.shape + n_src = int(np.prod(src_shape)) + flat = arr.reshape(-1, n_src) if leading_shape else arr.reshape(1, n_src) + + if skipna and np.issubdtype(flat.dtype, np.floating): + nan_mask = np.isnan(flat) + has_nan = nan_mask.any() + else: + has_nan = False + + if has_nan: + mask = (~nan_mask).astype(flat.dtype) + filled = np.where(nan_mask, flat.dtype.type(0.0), flat) + numerator = _matmul_last(filled, apply_weights) + fraction = _matmul_last(mask, apply_weights) + threshold = 1.0 - min(max(nan_threshold, 1e-6), 1.0 - 1e-6) + with np.errstate(invalid="ignore", divide="ignore"): + result = numerator / fraction + result = np.where(fraction >= threshold, result, np.nan) + else: + result = _matmul_last(flat, apply_weights) + if not coverage_all: + result = np.where(coverage[np.newaxis, :], result, np.nan) + + # sparse.matmul promotes to float64 regardless of the input dtype — cast + # back to the requested output dtype so float32-in really produces + # float32-out (halves memory for float32 pipelines). + if result.dtype != output_dtype: + result = result.astype(output_dtype, copy=False) + + out_shape = (*leading_shape, *dst_shape) if leading_shape else dst_shape + return result.reshape(out_shape) + + +def _matmul_last(flat: np.ndarray, apply_weights: Any) -> np.ndarray: + """Compute ``flat @ apply_weights`` where ``flat`` is dense ``(N, n_src)`` + and ``apply_weights`` is ``(n_src, n_dst)`` (dense or pre-sorted sparse).""" + if _HAS_SPARSE and isinstance(apply_weights, sparse.COO): + return np.asarray(sparse.matmul(flat, apply_weights)) + return flat @ apply_weights + + +def _result_dtype(obj: xr.DataArray | xr.Dataset) -> np.dtype: + if isinstance(obj, xr.DataArray): + return np.result_type(np.float32, obj.dtype) + dtypes = [v.dtype for v in obj.data_vars.values()] + if not dtypes: + return np.dtype(np.float64) + return np.result_type(np.float32, *dtypes) + + +def _assign_target_coords( + obj: xr.DataArray | xr.Dataset, + target_ds: xr.Dataset, + dst_dims: tuple[Hashable, ...], + x_coord: str, + y_coord: str, +) -> xr.DataArray | xr.Dataset: + """Attach the target dataset's dim coords and auxiliary lat/lon coords.""" + new_coords: dict[Hashable, Any] = {} + for d in dst_dims: + if d in target_ds.coords: + new_coords[d] = target_ds[d] + for name in (x_coord, y_coord): + if name in target_ds.coords and name not in new_coords: + new_coords[name] = target_ds[name] + if new_coords: + obj = obj.assign_coords(new_coords) + return obj diff --git a/tests/test_conservative_polygon.py b/tests/test_conservative_polygon.py new file mode 100644 index 0000000..0df711d --- /dev/null +++ b/tests/test_conservative_polygon.py @@ -0,0 +1,576 @@ +"""Tests for the polygon-intersection conservative regridder.""" +import numpy as np +import pytest +import xarray as xr + +import xarray_regrid # noqa: F401 (registers the accessor) +from xarray_regrid import ConservativeRegridder, RegridderMetadata, polygons_from_coords + +shapely = pytest.importorskip("shapely") + + +def _rect_da(ny=60, nx=120, nt=2, seed=0): + x = np.linspace(-180, 180, nx, endpoint=False) + 180 / nx + y = np.linspace(-90, 90, ny, endpoint=False) + 90 / ny + rng = np.random.default_rng(seed) + return xr.DataArray( + rng.normal(size=(nt, ny, nx)).astype(np.float64), + dims=("time", "y", "x"), + coords={"time": np.arange(nt), "y": y, "x": x}, + name="var", + ) + + +def _rect_target(ny=24, nx=47): + x = np.linspace(-180, 180, nx, endpoint=False) + 180 / nx + y = np.linspace(-90, 90, ny, endpoint=False) + 90 / ny + return xr.Dataset(coords={"y": y, "x": x}) + + +def test_polygon_matches_factored_planar(): + """On a rectilinear grid with no spherical correction, the polygon path + should reproduce the axis-factored path to machine precision.""" + da = _rect_da() + target = _rect_target() + ref = da.regrid.conservative(target, latitude_coord=None) + got = da.regrid.conservative_polygon(target, x_coord="x", y_coord="y") + got = got.transpose(*ref.dims) + np.testing.assert_allclose(got.values, ref.values, atol=1e-12) + + +def test_polygon_dask_time_chunks(): + da = _rect_da(nt=4).chunk({"time": 2}) + target = _rect_target() + got = da.regrid.conservative_polygon(target, x_coord="x", y_coord="y") + assert got.chunks is not None + got = got.compute() + ref = _rect_da(nt=4).regrid.conservative(target, latitude_coord=None) + np.testing.assert_allclose(got.transpose(*ref.dims).values, ref.values, atol=1e-12) + + +def test_polygon_rechunks_spatial(): + """Spatially-chunked input should be accepted (rechunked internally).""" + da = _rect_da().chunk({"time": 1, "y": 30, "x": 40}) + target = _rect_target() + out = da.regrid.conservative_polygon(target, x_coord="x", y_coord="y") + out.compute() + + +def test_polygon_nan_threshold(): + """Stricter nan_threshold produces more NaN output cells when partial + overlaps exist.""" + da = _rect_da() + da.values[:, 21:29, :] = np.nan + target = _rect_target(ny=23, nx=47) + out1 = da.regrid.conservative_polygon( + target, x_coord="x", y_coord="y", skipna=True, nan_threshold=1.0 + ) + out0 = da.regrid.conservative_polygon( + target, x_coord="x", y_coord="y", skipna=True, nan_threshold=0.0 + ) + assert int(np.isnan(out0.values).sum()) > int(np.isnan(out1.values).sum()) + + +def test_polygon_curvilinear_target(): + """Curvilinear target (2D lat/lon corners) returns finite values.""" + da = _rect_da() + ny_t, nx_t = 20, 30 + xi, yi = np.meshgrid( + np.linspace(-120, 120, nx_t), + np.linspace(-60, 60, ny_t), + indexing="xy", + ) + th = np.deg2rad(30) + x2d = xi * np.cos(th) - yi * np.sin(th) + y2d = xi * np.sin(th) + yi * np.cos(th) + target = xr.Dataset( + coords={"x": (("ny", "nx"), x2d), "y": (("ny", "nx"), y2d)} + ) + out = da.regrid.conservative_polygon(target, x_coord="x", y_coord="y") + assert out.shape == (2, 20, 30) + assert np.isfinite(out.values).mean() > 0.9 + + +def test_polygon_nan_threshold_invalid(): + da = _rect_da() + with pytest.raises(ValueError): + da.regrid.conservative_polygon( + _rect_target(), x_coord="x", y_coord="y", nan_threshold=1.5 + ) + + +def test_polygon_dataset_input(): + """A Dataset input with multiple variables should regrid all of them.""" + da = _rect_da() + ds = xr.Dataset({"a": da, "b": da * 2.0}) + target = _rect_target() + out = ds.regrid.conservative_polygon(target, x_coord="x", y_coord="y") + assert set(out.data_vars) == {"a", "b"} + np.testing.assert_allclose( + out["b"].transpose(*out["a"].dims).values, + out["a"].transpose(*out["a"].dims).values * 2.0, + atol=1e-12, + ) + + +# --- ConservativeRegridder (reusable) ------------------------------------------ + + +def test_regridder_reusable_matches_oneshot(): + """Reusing a single ConservativeRegridder on multiple fields matches the + one-shot `polygon_conservative_regrid` call.""" + da1 = _rect_da(seed=1) + da2 = _rect_da(seed=2) + target = _rect_target() + regridder = ConservativeRegridder(da1, target, x_coord="x", y_coord="y") + ref1 = da1.regrid.conservative_polygon(target, x_coord="x", y_coord="y") + ref2 = da2.regrid.conservative_polygon(target, x_coord="x", y_coord="y") + out1 = regridder.regrid(da1) + out2 = regridder(da2) # __call__ alias + np.testing.assert_allclose(out1.values, ref1.values, atol=1e-12) + np.testing.assert_allclose(out2.values, ref2.values, atol=1e-12) + + +def test_regridder_weight_cache(): + """Forward weight matrix is built lazily and then reused across calls.""" + da = _rect_da() + target = _rect_target() + regridder = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + assert regridder._fwd_weights is None + regridder.regrid(da) + w1 = regridder._fwd_weights + assert w1 is not None + regridder.regrid(da) + assert regridder._fwd_weights is w1 # same object, not rebuilt + + +def test_regridder_transpose_roundtrip_rectilinear_aligned(): + """If target cells are aligned unions of source cells, forward followed by + backward reproduces a constant field exactly.""" + # 120 source cells along each axis, target is a 4x coarsening (exact union + # of source cells). A constant source field survives the roundtrip to + # itself because every source cell is fully covered. + ns = 120 + nt = 30 # exactly ns / 4 + x_s = np.linspace(-180, 180, ns, endpoint=False) + 180 / ns + y_s = np.linspace(-90, 90, ns, endpoint=False) + 90 / ns + x_t = np.linspace(-180, 180, nt, endpoint=False) + 180 / nt + y_t = np.linspace(-90, 90, nt, endpoint=False) + 90 / nt + da = xr.DataArray( + np.full((ns, ns), 3.5, dtype=np.float64), + dims=("y", "x"), + coords={"y": y_s, "x": x_s}, + ) + target = xr.Dataset(coords={"y": y_t, "x": x_t}) + regridder = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + coarse = regridder.regrid(da) + back = regridder.T.regrid(coarse) + # Every value should match the original constant. + np.testing.assert_allclose(back.values, 3.5, atol=1e-12) + + +def test_regridder_T_preserves_weights(): + """regridder.T.T should share the raw area matrix with the original.""" + da = _rect_da() + target = _rect_target() + r = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + rr = r.T.T + # Same shape, same coords, same data. + assert r._areas.shape == rr._areas.shape + if hasattr(r._areas, "data"): + np.testing.assert_array_equal(r._areas.data, rr._areas.data) + + +def test_regridder_shape_mismatch_raises(): + """Applying the regridder to data whose spatial shape differs from the + source it was built for should raise.""" + da = _rect_da() + target = _rect_target() + regridder = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + smaller = _rect_da(ny=30, nx=60) + with pytest.raises(ValueError, match="spatial shape"): + regridder.regrid(smaller) + + +def test_spherical_mode_matches_factored(): + """Polygon path with ``spherical=True`` should match the axis-factored + sin-weighted conservative path to within a tight tolerance on lat/lon + grids (both are analytically equivalent for cylindrical equal-area).""" + lon_s = np.linspace(-180, 180, 180, endpoint=False) + 1.0 + lat_s = np.linspace(-90, 90, 90, endpoint=False) + 1.0 + lon_t = np.linspace(-180, 180, 60, endpoint=False) + 3.0 + lat_t = np.linspace(-90, 90, 30, endpoint=False) + 3.0 + vals = np.cos(np.deg2rad(lat_s))[:, None] ** 2 * np.sin(np.deg2rad(lon_s))[None, :] + da = xr.DataArray( + vals, + dims=("latitude", "longitude"), + coords={"latitude": lat_s, "longitude": lon_s}, + ) + target = xr.Dataset(coords={"latitude": lat_t, "longitude": lon_t}) + + factored = da.regrid.conservative(target, latitude_coord="latitude") + polygon = da.regrid.conservative_polygon( + target, x_coord="longitude", y_coord="latitude", spherical=True + ) + # Both methods should agree to the grid's own quadrature accuracy. Near the + # poles the factored path's median-dlat approximation introduces a small + # discrepancy; 1e-3 absolute is well below the planar raw error floor + # (~1e-2 at this resolution, tested separately). + np.testing.assert_allclose( + polygon.transpose(*factored.dims).values, + factored.values, + atol=1e-3, + ) + + +def test_spherical_conserves_integral(): + """Mass conservation check on the sphere. For cos²(lat), true integral is + 8π/3; the regridder on a 2°→6° grid should keep the spherical-area-weighted + sum within the grid quadrature floor when spherical=True, and miss it by + ~17× more when spherical=False.""" + lon_s = np.linspace(-180, 180, 180, endpoint=False) + 1.0 + lat_s = np.linspace(-90, 90, 90, endpoint=False) + 1.0 + lon_t = np.linspace(-180, 180, 60, endpoint=False) + 3.0 + lat_t = np.linspace(-90, 90, 30, endpoint=False) + 3.0 + da = xr.DataArray( + np.cos(np.deg2rad(lat_s))[:, None] ** 2 * np.ones(lon_s.size)[None, :], + dims=("latitude", "longitude"), + coords={"latitude": lat_s, "longitude": lon_s}, + ) + target = xr.Dataset(coords={"latitude": lat_t, "longitude": lon_t}) + + out_sph = da.regrid.conservative_polygon( + target, x_coord="longitude", y_coord="latitude", spherical=True + ) + out_raw = da.regrid.conservative_polygon( + target, x_coord="longitude", y_coord="latitude", spherical=False + ) + + # True target spherical cell areas + dlon = np.deg2rad(np.gradient(lat_t.astype(float) * 0 + np.mean(np.diff(lon_t)))) + dlon_arr = np.full(lon_t.size, np.deg2rad(np.mean(np.diff(lon_t)))) + lat_r = np.deg2rad(lat_t) + dlat_r = np.gradient(lat_r) + a_tgt = (np.sin(lat_r + dlat_r / 2) - np.sin(lat_r - dlat_r / 2))[:, None] * dlon_arr[None, :] + + true_val = 8 * np.pi / 3 + err_sph = abs(float((out_sph.transpose("latitude", "longitude").values * a_tgt).sum()) - true_val) + err_raw = abs(float((out_raw.transpose("latitude", "longitude").values * a_tgt).sum()) - true_val) + # Spherical should be at least 10× more accurate than raw planar on this grid. + assert err_sph < 0.1 * err_raw, f"err_sph={err_sph:.2e} err_raw={err_raw:.2e}" + + +# --- from_polygons (unstructured mesh) ---------------------------------------- + + +def _box_polygons(): + import shapely + rng = np.random.default_rng(1) + n = 50 + cx = rng.uniform(-170, 170, n) + cy = rng.uniform(-80, 80, n) + return shapely.box(cx - 5, cy - 5, cx + 5, cy + 5) + + +def test_from_polygons_basic(): + src_polys = _box_polygons() + tgt_polys = polygons_from_coords( + np.linspace(-180, 180, 30, endpoint=False) + 6, + np.linspace(-90, 90, 15, endpoint=False) + 6, + ) + rgr = ConservativeRegridder.from_polygons( + src_polys, tgt_polys, source_dim="face", target_dim="cell" + ) + da = xr.DataArray( + np.arange(src_polys.size, dtype=np.float64), + dims=("face",), + ) + out = rgr.regrid(da) + assert out.dims == ("cell",) + assert out.sizes["cell"] == tgt_polys.size + + +def test_from_polygons_mass_conservation(): + """Sum of intersected mass should match the direct A·s calculation to + machine precision for any source field.""" + import shapely + + src_polys = _box_polygons() + tgt_polys = polygons_from_coords( + np.linspace(-180, 180, 36, endpoint=False) + 5, + np.linspace(-90, 90, 18, endpoint=False) + 5, + ) + rgr = ConservativeRegridder.from_polygons( + src_polys, tgt_polys, source_dim="face", target_dim="cell" + ) + rng = np.random.default_rng(3) + s = rng.normal(size=src_polys.size) + da = xr.DataArray(s, dims=("face",)) + out = rgr.regrid(da).values + # Direct mass = Σ_i s_i × (Σ_j A_ij). Matches output if we multiply output + # by target-covered area = Σ_i A_ij. + A = rgr._areas # (n_tgt, n_src) + tgt_covered = A.sum(axis=1).todense() + valid = tgt_covered > 0 + direct = float((s * A.sum(axis=0).todense()).sum()) + via_regrid = float((out[valid] * tgt_covered[valid]).sum()) + rel = abs(direct - via_regrid) / max(abs(direct), 1e-12) + assert rel < 1e-12, f"rel err {rel:.2e}" + + +def test_from_polygons_transpose_roundtrip(): + """Roundtrip mesh ↔ mesh of a constant field returns the constant.""" + import shapely + src_polys = polygons_from_coords( + np.linspace(-180, 180, 24, endpoint=False) + 7.5, + np.linspace(-90, 90, 12, endpoint=False) + 7.5, + ) + tgt_polys = polygons_from_coords( + np.linspace(-180, 180, 12, endpoint=False) + 15, + np.linspace(-90, 90, 6, endpoint=False) + 15, + ) + rgr = ConservativeRegridder.from_polygons( + src_polys, tgt_polys, source_dim="src", target_dim="tgt" + ) + da = xr.DataArray(np.full(src_polys.size, 3.5), dims=("src",)) + out = rgr.regrid(da) + back = rgr.T.regrid(out) + # Inner source cells (fully covered by target cells they map to) must be 3.5. + # Edge cells may be NaN if target domain doesn't cover them. + finite = np.isfinite(back.values) + np.testing.assert_allclose(back.values[finite], 3.5, atol=1e-12) + + +def test_from_polygons_nan_propagation(): + """NaN source cells propagate through skipna=True correctly.""" + import shapely + src_polys = polygons_from_coords( + np.linspace(-180, 180, 24, endpoint=False) + 7.5, + np.linspace(-90, 90, 12, endpoint=False) + 7.5, + ) + tgt_polys = polygons_from_coords( + np.linspace(-180, 180, 12, endpoint=False) + 15, + np.linspace(-90, 90, 6, endpoint=False) + 15, + ) + rgr = ConservativeRegridder.from_polygons( + src_polys, tgt_polys, source_dim="src", target_dim="tgt" + ) + vals = np.full(src_polys.size, 1.0) + vals[:5] = np.nan + da = xr.DataArray(vals, dims=("src",)) + out_keep = rgr.regrid(da, nan_threshold=1.0) + out_strict = rgr.regrid(da, nan_threshold=0.0) + # Strict should have at least as many NaNs. + assert int(np.isnan(out_strict.values).sum()) >= int(np.isnan(out_keep.values).sum()) + + +def test_from_polygons_target_outside_source_is_nan(): + """Target cells entirely outside the source domain must be NaN regardless + of skipna (regression test for a bug where the "skip mask matmul when no + NaNs" optimization returned zeros for uncovered cells).""" + import shapely + + src = np.array([shapely.box(0, 0, 1, 1)], dtype=object) + tgt = polygons_from_coords( + np.linspace(100, 110, 4, endpoint=False) + 1.25, + np.linspace(100, 110, 4, endpoint=False) + 1.25, + ) + rgr = ConservativeRegridder.from_polygons(src, tgt, source_dim="src") + # Source has no NaNs → used to wrongly return 0 here. + da = xr.DataArray(np.array([5.0]), dims=("src",)) + out = rgr.regrid(da, skipna=True) + assert np.isnan(out.values).all() + out_no = rgr.regrid(da, skipna=False) + assert np.isnan(out_no.values).all() + + +def test_from_polygons_hole_is_nan(): + """A target cell fully inside a source-polygon hole should be NaN, not 0.""" + import shapely + + ring_with_hole = shapely.Polygon( + [(0, 0), (10, 0), (10, 10), (0, 10)], + [[(3, 3), (7, 3), (7, 7), (3, 7)]], + ) + src = np.array([ring_with_hole], dtype=object) + tgt = polygons_from_coords( + np.linspace(0, 10, 5, endpoint=False) + 1, + np.linspace(0, 10, 5, endpoint=False) + 1, + ) + rgr = ConservativeRegridder.from_polygons(src, tgt, source_dim="src") + out = rgr.regrid(xr.DataArray([7.0], dims=("src",))) + # The cell centered at (5,5), edges [4,6]x[4,6], lies entirely in the hole. + assert int(np.isnan(out.values).sum()) >= 1 + + +def test_from_polygons_input_validation(): + # 2D polygons array rejected + import shapely + p = shapely.box(0, 0, 1, 1) + arr_2d = np.array([[p, p], [p, p]], dtype=object) + with pytest.raises(ValueError, match="1D"): + ConservativeRegridder.from_polygons(arr_2d, np.array([p])) + + +# --- netCDF save / load ------------------------------------------------------- + + +def test_regrid_preserves_input_dtype(): + """Float32 in → float32 out; float64 in → float64 out. Sparse promotes + to float64 internally, so we rely on an explicit cast at the end of + ``_apply_core``.""" + da = _rect_da() + target = _rect_target() + rgr = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + assert rgr.regrid(da.astype(np.float32)).dtype == np.float32 + assert rgr.regrid(da.astype(np.float64)).dtype == np.float64 + # Integer inputs promote (float32 can't hold int32 without precision loss). + assert np.issubdtype(rgr.regrid((da * 10).astype(np.int32)).dtype, np.floating) + + +def test_to_netcdf_roundtrip_structured(tmp_path): + """Save, reload, regrid → identical output to the original regridder.""" + da = _rect_da() + target = _rect_target() + rgr = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + out_before = rgr.regrid(da).values + + path = tmp_path / "regridder.nc" + rgr.to_netcdf(path) + rgr2 = ConservativeRegridder.from_netcdf(path) + + np.testing.assert_array_equal(rgr2.regrid(da).values, out_before) + assert rgr2.x_coord == "x" + assert rgr2.y_coord == "y" + assert rgr2.spherical is False + assert rgr2._src_dims == rgr._src_dims + assert rgr2._dst_dims == rgr._dst_dims + + +def test_to_netcdf_preserves_spherical_flag(tmp_path): + lat_s = np.linspace(-90, 90, 30, endpoint=False) + 3 + lon_s = np.linspace(-180, 180, 60, endpoint=False) + 3 + lat_t = np.linspace(-90, 90, 15, endpoint=False) + 6 + lon_t = np.linspace(-180, 180, 30, endpoint=False) + 6 + da = xr.DataArray( + np.cos(np.deg2rad(lat_s))[:, None] ** 2 * np.ones(lon_s.size)[None, :], + dims=("latitude", "longitude"), + coords={"latitude": lat_s, "longitude": lon_s}, + ) + target = xr.Dataset(coords={"latitude": lat_t, "longitude": lon_t}) + + rgr = ConservativeRegridder( + da, target, x_coord="longitude", y_coord="latitude", spherical=True + ) + before = rgr.regrid(da).values + path = tmp_path / "r.nc" + rgr.to_netcdf(path) + rgr2 = ConservativeRegridder.from_netcdf(path) + assert rgr2.spherical is True + np.testing.assert_allclose(rgr2.regrid(da).values, before, atol=1e-15) + + +def test_to_netcdf_transpose_works_after_reload(tmp_path): + """A reloaded regridder can still take .T for backward regridding.""" + da = _rect_da() + target = _rect_target() + rgr = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + path = tmp_path / "r.nc" + rgr.to_netcdf(path) + rgr2 = ConservativeRegridder.from_netcdf(path) + + fwd = rgr.regrid(da) + back_original = rgr.T.regrid(fwd).values + back_reloaded = rgr2.T.regrid(fwd).values + np.testing.assert_array_equal(back_original, back_reloaded) + + +def test_to_netcdf_unstructured_roundtrip(tmp_path): + """from_polygons regridder roundtrips too (single spatial dim each side).""" + import shapely + rng = np.random.default_rng(1) + cx = rng.uniform(-170, 170, 30) + cy = rng.uniform(-80, 80, 30) + src_polys = shapely.box(cx - 5, cy - 5, cx + 5, cy + 5) + tgt_polys = polygons_from_coords( + np.linspace(-180, 180, 24, endpoint=False) + 7.5, + np.linspace(-90, 90, 12, endpoint=False) + 7.5, + ) + rgr = ConservativeRegridder.from_polygons( + src_polys, tgt_polys, source_dim="face", target_dim="cell" + ) + da = xr.DataArray(rng.normal(size=30), dims=("face",)) + out_before = rgr.regrid(da).values + + path = tmp_path / "r.nc" + rgr.to_netcdf(path) + rgr2 = ConservativeRegridder.from_netcdf(path) + np.testing.assert_array_equal(rgr2.regrid(da).values, out_before) + + +def test_to_netcdf_metadata_fields(tmp_path): + """Metadata captures grid ranges, version, created timestamp.""" + da = _rect_da() + target = _rect_target() + rgr = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + path = tmp_path / "r.nc" + rgr.to_netcdf(path) + + with xr.open_dataset(path) as ds: + attrs = dict(ds.attrs) + meta = RegridderMetadata.from_attrs(attrs) + + assert meta.x_coord == "x" + assert meta.y_coord == "y" + assert meta.spherical is False + assert meta.src_shape == rgr._src_shape + assert meta.dst_shape == rgr._dst_shape + # Grid ranges captured when the coord is present in source/target. + assert meta.source_x_range is not None + assert meta.target_x_range is not None + assert meta.source_x_range[0] <= meta.source_x_range[1] + assert meta.created # non-empty ISO timestamp + assert meta.schema_version == 1 + + +def test_from_netcdf_rejects_unknown_schema(tmp_path): + """Loading a file written with a future schema version raises cleanly.""" + import h5py + da = _rect_da() + target = _rect_target() + rgr = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + path = tmp_path / "r.nc" + rgr.to_netcdf(path) + + # Bump the on-disk schema_version so the loader should reject it. + with h5py.File(path, "a") as f: + f.attrs["schema_version"] = 999 + + with pytest.raises(ValueError, match="schema version"): + ConservativeRegridder.from_netcdf(path) + + +def test_regridder_transpose_curvilinear(): + """Transpose works when the target is a curvilinear grid with different + dim names from the source.""" + da = _rect_da(ny=40, nx=80) + ny_t, nx_t = 20, 30 + xi, yi = np.meshgrid( + np.linspace(-120, 120, nx_t), + np.linspace(-60, 60, ny_t), + indexing="xy", + ) + th = np.deg2rad(15) + x2 = xi * np.cos(th) - yi * np.sin(th) + y2 = xi * np.sin(th) + yi * np.cos(th) + target = xr.Dataset( + coords={"x": (("ny", "nx"), x2), "y": (("ny", "nx"), y2)} + ) + regridder = ConservativeRegridder(da, target, x_coord="x", y_coord="y") + fwd = regridder.regrid(da) + assert fwd.dims == ("time", "ny", "nx") + # Going backward we should land back on (time, y, x) + back = regridder.T.regrid(fwd) + assert "y" in back.dims and "x" in back.dims + assert back.sizes["y"] == da.sizes["y"] + assert back.sizes["x"] == da.sizes["x"] From b922748ec515eab346c5a6eb30bba783f0d66d4b Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Sun, 19 Apr 2026 12:33:23 -0500 Subject: [PATCH 10/16] Add demo notebooks for the polygon conservative regridder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three short notebooks under docs/notebooks/demos/ walking through the main workflows: - basics: structured lat/lon source → coarser target, with the spherical=True switch; mass-integral comparison against 8π/3 truth. - curvilinear: regular source → 30°-rotated 2D target, plus a machine-precision mass-conservation check on the covered subset. - unstructured: scipy Voronoi mesh target via from_polygons, plus a to_netcdf / from_netcdf roundtrip demonstrating weight persistence. Each uses analytic / synthetic data so they're reproducible without external downloads, and each includes a concrete diagnostic (mass integral, error comparison table, or reload bit-exactness) rather than just plots. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../demo_conservative_polygon_basics.ipynb | 1532 +++++++++++++++++ ...emo_conservative_polygon_curvilinear.ipynb | 928 ++++++++++ ...mo_conservative_polygon_unstructured.ipynb | 1062 ++++++++++++ 3 files changed, 3522 insertions(+) create mode 100644 docs/notebooks/demos/demo_conservative_polygon_basics.ipynb create mode 100644 docs/notebooks/demos/demo_conservative_polygon_curvilinear.ipynb create mode 100644 docs/notebooks/demos/demo_conservative_polygon_unstructured.ipynb diff --git a/docs/notebooks/demos/demo_conservative_polygon_basics.ipynb b/docs/notebooks/demos/demo_conservative_polygon_basics.ipynb new file mode 100644 index 0000000..723535f --- /dev/null +++ b/docs/notebooks/demos/demo_conservative_polygon_basics.ipynb @@ -0,0 +1,1532 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "fc5d33c5", + "metadata": {}, + "source": [ + "# Polygon conservative regridder — basics\n", + "\n", + "`ConservativeRegridder` is a polygon-intersection conservative regridder that complements\n", + "the existing `.conservative` method. Where `.conservative` uses fast axis-factored 1D overlap\n", + "(rectilinear grids only), this path computes explicit 2D cell-polygon intersections — handling\n", + "curvilinear and unstructured meshes and supporting a spherical-area mode for global lat/lon\n", + "grids, at the cost of slightly higher build time.\n", + "\n", + "This notebook shows the basic workflow on an analytic field with a known sphere integral." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a029f393", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:06.682967Z", + "iopub.status.busy": "2026-04-19T17:16:06.682818Z", + "iopub.status.idle": "2026-04-19T17:16:07.878722Z", + "shell.execute_reply": "2026-04-19T17:16:07.878180Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import xarray as xr\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import xarray_regrid # registers the `.regrid` accessor\n", + "from xarray_regrid import ConservativeRegridder\n", + "\n", + "np.set_printoptions(precision=5, suppress=True)" + ] + }, + { + "cell_type": "markdown", + "id": "d132ca7a", + "metadata": {}, + "source": [ + "## Synthetic source field\n", + "\n", + "We use $f(\\lambda, \\phi) = \\cos^2(\\phi)$ on a 1° global grid. Its true integral over the unit\n", + "sphere is $8\\pi/3$, which gives us a check on the regridder." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "29dadd31", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:07.880467Z", + "iopub.status.busy": "2026-04-19T17:16:07.880202Z", + "iopub.status.idle": "2026-04-19T17:16:07.889917Z", + "shell.execute_reply": "2026-04-19T17:16:07.889481Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.DataArray 'cos2_lat' (latitude: 180, longitude: 360)> Size: 518kB\n",
+       "array([[0.00008, 0.00008, 0.00008, ..., 0.00008, 0.00008, 0.00008],\n",
+       "       [0.00069, 0.00069, 0.00069, ..., 0.00069, 0.00069, 0.00069],\n",
+       "       [0.0019 , 0.0019 , 0.0019 , ..., 0.0019 , 0.0019 , 0.0019 ],\n",
+       "       ...,\n",
+       "       [0.0019 , 0.0019 , 0.0019 , ..., 0.0019 , 0.0019 , 0.0019 ],\n",
+       "       [0.00069, 0.00069, 0.00069, ..., 0.00069, 0.00069, 0.00069],\n",
+       "       [0.00008, 0.00008, 0.00008, ..., 0.00008, 0.00008, 0.00008]],\n",
+       "      shape=(180, 360))\n",
+       "Coordinates:\n",
+       "  * latitude   (latitude) float64 1kB -89.5 -88.5 -87.5 -86.5 ... 87.5 88.5 89.5\n",
+       "  * longitude  (longitude) float64 3kB -179.5 -178.5 -177.5 ... 178.5 179.5
" + ], + "text/plain": [ + " Size: 518kB\n", + "array([[0.00008, 0.00008, 0.00008, ..., 0.00008, 0.00008, 0.00008],\n", + " [0.00069, 0.00069, 0.00069, ..., 0.00069, 0.00069, 0.00069],\n", + " [0.0019 , 0.0019 , 0.0019 , ..., 0.0019 , 0.0019 , 0.0019 ],\n", + " ...,\n", + " [0.0019 , 0.0019 , 0.0019 , ..., 0.0019 , 0.0019 , 0.0019 ],\n", + " [0.00069, 0.00069, 0.00069, ..., 0.00069, 0.00069, 0.00069],\n", + " [0.00008, 0.00008, 0.00008, ..., 0.00008, 0.00008, 0.00008]],\n", + " shape=(180, 360))\n", + "Coordinates:\n", + " * latitude (latitude) float64 1kB -89.5 -88.5 -87.5 -86.5 ... 87.5 88.5 89.5\n", + " * longitude (longitude) float64 3kB -179.5 -178.5 -177.5 ... 178.5 179.5" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "lat = np.linspace(-89.5, 89.5, 180)\n", + "lon = np.linspace(-179.5, 179.5, 360)\n", + "field = (np.cos(np.deg2rad(lat))**2)[:, None] * np.ones(lon.size)[None, :]\n", + "src = xr.DataArray(\n", + " field,\n", + " dims=(\"latitude\", \"longitude\"),\n", + " coords={\"latitude\": lat, \"longitude\": lon},\n", + " name=\"cos2_lat\",\n", + ")\n", + "src" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "1baab8a3", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:07.890885Z", + "iopub.status.busy": "2026-04-19T17:16:07.890824Z", + "iopub.status.idle": "2026-04-19T17:16:07.992951Z", + "shell.execute_reply": "2026-04-19T17:16:07.992526Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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v359ueeee6RAw7pO3Hz00UelePHiAa0OszJ16lSJdH+VwUTAUQEAABACO+Yap5TB+OjouYbyrl27mrmTV111lTRo0EBefvllc1vnVBZYWN+6daukpqaa77ds2RJwUSQn0np1G55/AgAA5Iktk43DRta3bdtmuhGq1157TRo2bCjr1683F0269957Czaspy99Wbt2ba6fCAAAAAiQi5F1iYCwrgPb2sZRffDBB+bCSEq7Je7fv7/wata16ftTTz0lJUuWDFiv/czvu+++PF1K1W48lmUWAAAAJ7BlrnHYyHqDBg1MZ8SOHTvKqlWrTAm5+vXXX+Wcc84pvLC+YMECmTRpUqawfvr0aVm4cKEjwjo16wAAwEnsWLPutLA+efJk0xVmypQpprd6kyZNzPq3337bXx5ToGH9+PHjpk+kLidOnJAiRYoEXBRp2bJlUrFixTy9EAAAAEQXp00wvfzyy+XIkSMmM6dvca6TTosVK1bwYb1MmTJmYqku559/fqb7df24cePECdLEK39NpwUAAHBGtkHB04uGpqWlybp168ztunXrmguH5lWuwrpOMtVR9bZt28qSJUukXLlyAVcwrVGjhlSpUkWcINWyzAIAAOAEtsw1DiuDOfn3/E0tC/d6vf7w3q1bN5kxY0aeRtdzFdYvu+wy/5VMk5KSxO12i1N5xDILAACAE9gx1zitDGbQoEHy0UcfyTvvvCMXX3yxWacj7Pfff78MHjxYnn322cKZYKoj6OrUqVOyd+9eSUlJCbi/cePGeXlYAAAARJsICOGh0sqT119/3dSu+1x77bVStGhRue222wovrB8+fFh69uwp77//fpb362TTSJdq/bUAAAA4gS1zjcPKYE6dOiWVKlXKtF4bsOh9eZGnsD5gwAA5duyYbNy40Zw5vPnmm3Lw4EEZP368PPnkk+IEyZZb4iznlvkAAIDokmzDsOu0MpjWrVvLmDFjTM26r2uitjbXBix6X17kKY1++OGHMnXqVGnRooWpW9eymLvuuksef/xxmThxYp5eCAAAAKKMlcslD2bOnGm6sWh4btWqlWzatCmkn3v11VdNp8NOnTqF/FzTp0+X9evXS7Vq1eTKK680i87z1HV6QdFCG1nXma6+furaQ1LLYrSVY6NGjWTLli3iBKmW2ywAAABOkBqFI+uLFi0ykz71qqIa1DVMd+jQQXbu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9ZNzzjlHSpQoITfffLMcPHhQotXHH38s119/vbngje6rpUuXZrogzujRo6Vy5cpStGhRadeunfzwww8B2/z+++/SpUsX00Nc/43s1auX/PnnnxLt+65Hjx6ZjsWrr746YJto3XcoeIT1CKRX+7r11lulT58+2W43b9482b9/v3/p1KmT/77du3ebQHrFFVfIl19+KQMGDJB//etfsmLFConmfefxeMx+0e0+/fRTWbBggQni+h+4aN93wTzyyCMBx9l9990XcJXJ9u3bS40aNWTz5s0yZcoUE8Cef/55iXaLFi2SQYMGmQ5OW7ZskSZNmkiHDh3k0KFD4X5pttKgQYOA42vdunX++wYOHCjvvPOOLF68WD766CNz9embbrpJotXJkyfNcaQngVnRE+Wnn35aZs+eLRs3bpTixYubY05Penw0bH777beyatUqeffdd02IveeeeyTa953ScJ7+WPzvf/8bcH+07jsUAgsRa968eVbp0qWzvE9/tW+++WbQnx06dKjVoEGDgHW333671aFDByua992yZcsst9ttHThwwL/u2WeftUqVKmUlJyeb29G+79KrUaOGNW3atKD3z5o1yypbtqx/36lhw4ZZdevWtaJdy5YtrX79+vlvezweq0qVKtbEiRPD+rrsZMyYMVaTJk2yvO/YsWNWXFyctXjxYv+67du3m3/7NmzYYEW7jP8N8Hq9VmJiojVlypSAfZiQkGD997//Nbe/++4783Off/65f5v333/fcrlc1i+//GJFi6z++9m9e3frhhtuCPoz7DsUJEbWHUw/Hi5fvry0bNlS5s6daz4C9dmwYYP5CDQ9HWHR9dFM33+jRo2kUqVKAftFR4h1xMS3Dfvu/9OyFy1DaNasmRk5T18ypPvq0ksvlfj4+ID9uXPnTjl69KhEK/3kRj9pSH8cud1uczva/wYz0jINLU2oXbu2GbnUMjWl+y81NTVgH2qJTPXq1dmHWdBPBA8cOBCwv/Sy81p+5Tvm9KuWb7Ro0cK/jW6vx6aOxEc7LX/U0si6deuaT2d/++03/33sOxSk2AJ9dIS1NKFt27am7nrlypXSt29fUzt3//33m/v1H+30gVTpbQ2lp0+fNvWM0SjYfvHdl9020bjv9HjS2v1y5cqZsqERI0aYj4enTp3q31e1atUKuj/Lli0r0ejIkSOm5Cqr42jHjh1h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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "src.plot(figsize=(8, 3.5), cmap=\"viridis\")\n", + "plt.title(\"source: cos²(lat) on a 1° grid\")\n", + "plt.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "id": "72b25970", + "metadata": {}, + "source": [ + "## Target grid\n", + "\n", + "A coarser 3° grid. We'll build an empty `xr.Dataset` with just the target coords;\n", + "`xarray-regrid` identifies the grid from `latitude` and `longitude`." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "856e2175", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:07.994192Z", + "iopub.status.busy": "2026-04-19T17:16:07.994113Z", + "iopub.status.idle": "2026-04-19T17:16:07.996120Z", + "shell.execute_reply": "2026-04-19T17:16:07.995737Z" + } + }, + "outputs": [], + "source": [ + "target = xr.Dataset(coords={\n", + " \"latitude\": np.linspace(-88.5, 88.5, 60),\n", + " \"longitude\": np.linspace(-178.5, 178.5, 120),\n", + "})" + ] + }, + { + "cell_type": "markdown", + "id": "16e60842", + "metadata": {}, + "source": [ + "## Regrid via the `.regrid.conservative_polygon` accessor\n", + "\n", + "`spherical=True` applies an analytic Lambert cylindrical equal-area projection\n", + "to the cell edges before intersecting — correct spherical area weights at the\n", + "same cost as the planar fast path. Leave it off when your coords aren't geographic degrees." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "165feb10", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:07.997093Z", + "iopub.status.busy": "2026-04-19T17:16:07.997028Z", + "iopub.status.idle": "2026-04-19T17:16:08.767277Z", + "shell.execute_reply": "2026-04-19T17:16:08.766968Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.DataArray 'cos2_lat' (latitude: 60, longitude: 120)> Size: 58kB\n",
+       "array([[0.00129, 0.00129, 0.00129, ..., 0.00129, 0.00129, 0.00129],\n",
+       "       [0.00676, 0.00676, 0.00676, ..., 0.00676, 0.00676, 0.00676],\n",
+       "       [0.01763, 0.01763, 0.01763, ..., 0.01763, 0.01763, 0.01763],\n",
+       "       ...,\n",
+       "       [0.01763, 0.01763, 0.01763, ..., 0.01763, 0.01763, 0.01763],\n",
+       "       [0.00676, 0.00676, 0.00676, ..., 0.00676, 0.00676, 0.00676],\n",
+       "       [0.00129, 0.00129, 0.00129, ..., 0.00129, 0.00129, 0.00129]],\n",
+       "      shape=(60, 120))\n",
+       "Coordinates:\n",
+       "  * latitude   (latitude) float64 480B -88.5 -85.5 -82.5 ... 82.5 85.5 88.5\n",
+       "  * longitude  (longitude) float64 960B -178.5 -175.5 -172.5 ... 175.5 178.5
" + ], + "text/plain": [ + " Size: 58kB\n", + "array([[0.00129, 0.00129, 0.00129, ..., 0.00129, 0.00129, 0.00129],\n", + " [0.00676, 0.00676, 0.00676, ..., 0.00676, 0.00676, 0.00676],\n", + " [0.01763, 0.01763, 0.01763, ..., 0.01763, 0.01763, 0.01763],\n", + " ...,\n", + " [0.01763, 0.01763, 0.01763, ..., 0.01763, 0.01763, 0.01763],\n", + " [0.00676, 0.00676, 0.00676, ..., 0.00676, 0.00676, 0.00676],\n", + " [0.00129, 0.00129, 0.00129, ..., 0.00129, 0.00129, 0.00129]],\n", + " shape=(60, 120))\n", + "Coordinates:\n", + " * latitude (latitude) float64 480B -88.5 -85.5 -82.5 ... 82.5 85.5 88.5\n", + " * longitude (longitude) float64 960B -178.5 -175.5 -172.5 ... 175.5 178.5" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "regridded = src.regrid.conservative_polygon(\n", + " target, x_coord=\"longitude\", y_coord=\"latitude\", spherical=True\n", + ")\n", + "regridded" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "e3d5b7cf", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:08.768572Z", + "iopub.status.busy": "2026-04-19T17:16:08.768502Z", + "iopub.status.idle": "2026-04-19T17:16:08.830003Z", + "shell.execute_reply": "2026-04-19T17:16:08.829608Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "regridded.plot(figsize=(8, 3.5), cmap=\"viridis\")\n", + "plt.title(\"regridded: 3° target\")\n", + "plt.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "id": "8b9883a8", + "metadata": {}, + "source": [ + "## Mass-conservation diagnostic\n", + "\n", + "A correct conservative regrid preserves the area-weighted integral (on the sphere).\n", + "We compute true spherical cell areas for each grid and compare the integrals." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "ed4d2118", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:08.831189Z", + "iopub.status.busy": "2026-04-19T17:16:08.831122Z", + "iopub.status.idle": "2026-04-19T17:16:08.834716Z", + "shell.execute_reply": "2026-04-19T17:16:08.834417Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "true sphere integral : 8.377580\n", + "source integral : 8.377474 (err -1.06e-04)\n", + "regridded integral : 8.377474 (err -1.06e-04)\n" + ] + } + ], + "source": [ + "def sph_integral(da):\n", + " lat_r = np.deg2rad(da.latitude.values)\n", + " dlat = np.gradient(lat_r)\n", + " dlon = np.gradient(np.deg2rad(da.longitude.values))\n", + " area = (np.sin(lat_r + dlat/2) - np.sin(lat_r - dlat/2))[:, None] * dlon[None, :]\n", + " return float((da.values * area).sum())\n", + "\n", + "true_val = 8 * np.pi / 3\n", + "src_sum = sph_integral(src)\n", + "out_sum = sph_integral(regridded)\n", + "print(f\"true sphere integral : {true_val:.6f}\")\n", + "print(f\"source integral : {src_sum:.6f} (err {src_sum - true_val:+.2e})\")\n", + "print(f\"regridded integral : {out_sum:.6f} (err {out_sum - true_val:+.2e})\")" + ] + }, + { + "cell_type": "markdown", + "id": "736014c4", + "metadata": {}, + "source": [ + "## Spherical vs planar vs the axis-factored path\n", + "\n", + "`spherical=False` uses raw lat/lon planar geometry — poor near the poles.\n", + "The existing `.conservative` method applies an analytic sin-weighting to the factored\n", + "1D overlap. On lat/lon grids, `spherical=True` reproduces that accuracy while also\n", + "working on curvilinear and unstructured targets the factored path can't express." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d726eddc", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:08.835715Z", + "iopub.status.busy": "2026-04-19T17:16:08.835658Z", + "iopub.status.idle": "2026-04-19T17:16:09.149947Z", + "shell.execute_reply": "2026-04-19T17:16:09.149528Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "polygon, spherical=True : |error| = 1.06e-04\n", + "polygon, spherical=False: |error| = 1.81e-03\n", + "factored (.conservative): |error| = 1.06e-04\n" + ] + } + ], + "source": [ + "def err(fn):\n", + " return abs(sph_integral(fn()) - true_val)\n", + "\n", + "results = {\n", + " \"polygon, spherical=True \": err(lambda: src.regrid.conservative_polygon(\n", + " target, x_coord=\"longitude\", y_coord=\"latitude\", spherical=True)),\n", + " \"polygon, spherical=False\": err(lambda: src.regrid.conservative_polygon(\n", + " target, x_coord=\"longitude\", y_coord=\"latitude\", spherical=False)),\n", + " \"factored (.conservative)\": err(lambda: src.regrid.conservative(\n", + " target, latitude_coord=\"latitude\")),\n", + "}\n", + "for k, v in results.items():\n", + " print(f\"{k}: |error| = {v:.2e}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/notebooks/demos/demo_conservative_polygon_curvilinear.ipynb b/docs/notebooks/demos/demo_conservative_polygon_curvilinear.ipynb new file mode 100644 index 0000000..fec1465 --- /dev/null +++ b/docs/notebooks/demos/demo_conservative_polygon_curvilinear.ipynb @@ -0,0 +1,928 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "045f1332", + "metadata": {}, + "source": [ + "# Polygon conservative regridder — curvilinear target\n", + "\n", + "Curvilinear grids have 2D `lat(y,x)` / `lon(y,x)` coordinate arrays — common in\n", + "ocean models (ORCA, tripolar) and regional forecasts on rotated grids. The existing\n", + "`.conservative` method is strictly rectilinear, so `ConservativeRegridder` is the\n", + "available tool here.\n", + "\n", + "This notebook regrids a regular lat/lon source onto a 30°-rotated curvilinear target." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "5a289dbc", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:10.278657Z", + "iopub.status.busy": "2026-04-19T17:16:10.278490Z", + "iopub.status.idle": "2026-04-19T17:16:11.408757Z", + "shell.execute_reply": "2026-04-19T17:16:11.408359Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import xarray as xr\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import xarray_regrid # noqa: F401\n", + "from xarray_regrid import ConservativeRegridder" + ] + }, + { + "cell_type": "markdown", + "id": "52bf6b52", + "metadata": {}, + "source": [ + "## Source — regular 1° lat/lon\n", + "\n", + "A simple two-bump analytic field makes the geometry easy to read off the plot." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "7b2b6775", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:11.410414Z", + "iopub.status.busy": "2026-04-19T17:16:11.410209Z", + "iopub.status.idle": "2026-04-19T17:16:11.500074Z", + "shell.execute_reply": "2026-04-19T17:16:11.499649Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAv8AAAFUCAYAAACzyFOSAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAlKtJREFUeJztnQe4FNX5xr/dvY0iTbqggBjUKEIgIFijBCxJRI2KEkFiMBoxCiolhqZGrNiCYkWNGo01FoIa0dgQDdhQ4S82iDQbIPXeuzv/5zs7Z/acmbOzs+3u7t339zzD7p45M3NmZpd75j3feb+QZVkWAQAAAAAAABo94UI3AAAAAAAAANAwoPMPAAAAAABAmYDOPwAAAAAAAGUCOv8AAAAAAACUCej8AwAAAAAAUCag8w8AAAAAAECZgM4/AAAAAAAAZQI6/wAAAAAAAJQJ6PwDAAAAAABQJqDzDwAoW+655x4KhUL0xRdfFPU+iwU+Jz63a6+9lhoTW7Zsod/97nfUsWNHcX4XXHCBc658P9Pl5ZdfFtvyayoOP/xwsQAAQEOBzj8AAGTAFVdcQU8++WRer90bb7xBM2bMoI0bN+b1OOUO30vu5J9zzjn0t7/9jU4//fRCNwkAAPJGRf52DQAAjbvD+Otf/5qGDx+ulXPHccSIEVRdXZ2Tzv/MmTPpjDPOoFatWmW9P2Bm4cKFdOCBB9L06dOdMsuyaPv27VRZWYnLBgBoVED5B6DMqa+vp9ra2kI3o9EQiUSopqZGhH2A0mDDhg2ehyu+f3wf+X4CAEBjAp1/AArEDz/8IGKLu3XrJlTi9u3b089//nNaunSpVu+RRx6hfv36UZMmTaht27b0m9/8hr766qtAccOsGPP+TTHbN9xwA+25557i2B999JFYv3z5cjr55JOpXbt24ni9evWiSy65RNsnH/u3v/0tdejQQWz74x//mO6++27PsVetWiX2lwp+8Jg2bZo4x5YtW1KzZs3okEMOoZdeekmrp7b99ttvd9r+05/+lN5++22t7vvvvy/OvUePHqIDx7Hc3OZvv/3Wty2jR48W17iurs6zbujQoeJ6MNyOrVu30r333ive88LH84v5/9e//kWHHXYY7bLLLtSiRQvR7gcffDBpWzjc5+KLLxbvu3fv7hyH93vCCSfQT37yE63+L3/5S7H+qaeecsoWL14syvjYks8++4xOOukkatOmDTVt2lQo3s8++yyly/XXX0977LGH+J7weS1btizr7+ScOXPEPeN28fVevXq1UOAvu+wy6tKlizjWcccdR9999522T97fL37xC3r++eepT58+4p7vu+++9PjjjweKzf/888/FNVCvcbKYf/5O84gPXz8+Tv/+/bVr7of83vJ5DBgwgF599dVA2wEAQC5B2A8ABeLss8+mRx99lMaNGyc6Ktwxfe211+jjjz92Onbc8RgzZozoKM6aNYvWr19PN954I73++uv0zjvvZBwKMm/ePNqxYwedddZZogPNHRnuMHOnm8McuJw7VJ9++ik9/fTT9Je//EVsx8fnziJ3irjd/JDAHcszzzyTNm/eLB5mJKNGjaL//Oc/ovPmB29355130qmnnkpjx44VD0V33XUXDRs2jN566y3RmVPhDjPX+f3vfy/acfXVV4vOMHdqZYjGCy+8ID7zteOO/4cffig6Xvz65ptvJlXlOWTnvvvuo+eee050JiXr1q0ToSEyLITjwnmCKHfg+Fox3KlLBt9HfvjgB6UpU6aI+8b3b8GCBXTaaacZt+Fz+r//+z/6+9//Ljra/FDC8DXn+/TPf/5TXDt+kOBrzN+JcDgsOpS/+tWvRF1+z2UHHXSQc/8GDx5M27Ztoz/+8Y+06667igcYrs/fxeOPP56CwNeI78G5554rvkf8nTziiCPogw8+EA+FmfDAAw+IB8HzzjtPdO75vvKDKO+XO+mTJk2ilStX0s0330wXXXSR54Hzk08+oVNOOUX8rvghjr/j/JDD15gfqk3ss88+4l6OHz9ePFxceOGFzjX++uuvPfX5+8PXcrfddqPJkyeLB9V//OMfIvTrscce871+/J3m7yxff/6d8PeTrzv/9rp27ZrRNQMAgIywAAAFoWXLlta5556bdH1tba3Vvn17a7/99rO2b9/ulD/zzDPcm7amTZvmlB122GFicTN69Ghrjz32cD5//vnnYtsWLVpYGzZs0Ooeeuih1i677GJ9+eWXWnksFnPen3nmmVanTp2sb775RqszYsQIcT7btm3T2hTkv5j6+npr586dWtn3339vdejQwfrtb3/rafuuu+5qfffdd075P//5T1H+9NNPO2VqOyR///vfRb1XXnnFKZs3b54o430z0WjU6tKli3XKKado286ePdsKhULWZ5995pQ1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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "lat = np.linspace(-60, 60, 121)\n", + "lon = np.linspace(-120, 120, 241)\n", + "Lo, La = np.meshgrid(lon, lat)\n", + "field = (\n", + " np.exp(-((Lo - 40)**2 + (La - 20)**2) / 500)\n", + " - np.exp(-((Lo + 60)**2 + (La + 15)**2) / 400)\n", + ")\n", + "src = xr.DataArray(\n", + " field,\n", + " dims=(\"latitude\", \"longitude\"),\n", + " coords={\"latitude\": lat, \"longitude\": lon},\n", + " name=\"bumps\",\n", + ")\n", + "src.plot(figsize=(8, 3.5), cmap=\"RdBu_r\", center=0)\n", + "plt.title(\"source: analytic two-bump field\")\n", + "plt.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "id": "cd1d44bc", + "metadata": {}, + "source": [ + "## Target — rotated curvilinear grid\n", + "\n", + "Coordinates ride on a `(ny, nx)` mesh rather than a 1D lat/lon. We stash them\n", + "as 2D coordinate variables on an `xr.Dataset`." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8578ef35", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:11.501193Z", + "iopub.status.busy": "2026-04-19T17:16:11.501128Z", + "iopub.status.idle": "2026-04-19T17:16:11.551329Z", + "shell.execute_reply": "2026-04-19T17:16:11.550877Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAdkAAAGJCAYAAADGyUn1AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAA/KpJREFUeJzs3QeYbWV1N/BjBTtSBEVFjYJoTOxKLDGxfYZYklgxKCIi2Oi9CSpdsIMoTUEENMbeEQuggh17wy5qVLDX8z2/HddkzZp3z70Xb5k78/6fZ7jMnHP2Xvvd+7yr/9fVptPpdNLR0dHR0dGx0nH1lX/Ijo6Ojo6Ojq5kOzo6Ojo6ViG6J9vR0dHR0bGK0JVsR0dHR0fHKkJXsh0dHR0dHasIXcl2dHR0dHSsInQl29HR0dHRsYrQlWxHR0dHR8cqQleyHR0dHR0dqwhdyXas9bja1a42ee5znzvz+2mnnTb87bLLLpv52wMe8IDhp2Ph4uijj57c/va3n/z5z39eLef73e9+N1ks8Px75lcl3vWud02uf/3rT3784x+v0vMsNnQl29GxwPH9739/2EQ//elPTxarvFdeeeXkqKOOmuyzzz6Tq1/9/7al3XbbbXLXu951sv7660+ue93rTrbccsvh2L/85S+bStPnb3azm02uc53rTO51r3tN3vve98553//8z/9MHvrQhw7Hu/Wtbz05//zzJ6sDr3vd6yYvetGLrvLnf/3rXw/Xvrrkrfh//+//TW5729tOjjjiiDVy/rUWuIs7OtZm/OY3v5n+4Q9/mPn91FNPxcc9/eY3vznzt9/97nfDz9qIiy++eLge17VY5T3++OOnN7zhDYd7mXGf+9xn+pznPGf6kpe8ZHrSSSdNd9555+k666wz/P1Pf/rTrPc+/vGPn17zmtec7rnnntNXvvKV06222mr4/cMf/vCs9+2www7T+9///tM3velN0wMOOGC60UYbTX/5y19OVzW23nrr6WabbXaVP//jH/94WNdDDjlkzmue/7p2qwKveMUrpte97nWnV1555So/12LBNde0ku/oyDCv4re//e3giSwv1l133WW+59rXvvaCXuhf/epXk+td73qL/pxjOPXUUyePeMQj5tzLj3zkI3Pe+zd/8zeTPffcc/Lxj398cu9733v4m/9//etfPznmmGOG1+BJT3rS5G//9m8ne++99+TCCy+c+fxFF100OfPMMyd///d/P3nUox41efvb3z750pe+NLnb3e621q7fNa95zeFnVeM//uM/Js9+9rMn55577mT77bdf5edbFFjTWr5j7cN3v/vd6fbbbz+96U1vOr32ta89vdWtbjXdaaedZjxFlnbr0Wp5mCx7Fv673vWu6d3udrfBS+HV3PGOd5w+4AEPmHMM3svNbnaz6X/8x3/M/K1a963z/OM//uPwE/jABz4wvOfss8+ePv/5z59uuummw7n/+Z//efrVr351znk/+tGPTh/60IcO3tZ1rnOdwRP6yEc+Mus9l1122eBpbb755tN11113uv76608f/ehHz5Ijy3f++ecP7+dJrbfees21DjnrT3iJH/rQh4Zz3OIWtxjuxc1vfvPprrvuOv31r3896zhPfvKTp9e73vWmX/va16YPe9jDpte//vWnj3zkI4fXvPfZz372dIMNNhj+/vCHP3y4xy2vyd+f8pSnTG9yk5sM57vDHe4wPfnkk5db3ha+8Y1vDO857bTTpsuDN7zhDcP73/nOd878ba+99ppe4xrXmF5xxRWz3nv44YcP7/32t7898zfX5/m1Fuecc85wzbzE+bCse/byl798WAtr4nvxjGc8Y/qzn/1s5nXPXl2T8Gp9bw466KDpXe961+H54ine9773nZ533nkzn/cMtdY17k/rO8e7Peyww6a3uc1tBrmcb7/99pv+9re/nfW++A7y+O9xj3sM34Nb3/rW09NPP725Fne5y12mj3jEI+Zdr47/Q/dkO1Y433bPe95z8vOf/3yy4447DoUq3/ve9yZveMMbhpzRVfEYv/zlL0+e8IQnTJ7+9KdPnva0p0222GKLyeMe97gh//TDH/5wsskmm8zybMjw+Mc/fqXcuSOPPHLIAfJ+rrjiiqH45olPfOLkYx/72Mx7zjvvvMnDHvawwdM55JBDhvfzvP75n/958uEPf3hYD7j44osHj4lsN7/5zYfCqxNOOGEouPrCF74w5AAznvGMZ0w22mijycEHHzx4RS3IQR522GHDe6z3/e53v+Hv//AP/zD8y6Ow7jvvvPNkgw02GDy6l770pZPvfve7w2sZf/zjH4dc5H3ve9/JscceOyPPdtttNznnnHMm22677eAZfvCDH5xsvfXWc2S5/PLLh9cV2DzrWc8aZH/nO985eepTnzrkVHfddddlyttCeJlyry2Q2/P2+9//fnLppZdODjzwwMkNbnCDmXWHT33qU5PNN998csMb3nDWZ+M98sO3uMUthv8//PDDh3U45ZRTBu9PnnTDDTecLA9a98xzeuihh04e9KAHDffB8+y+ex4uuOCCybWuda3JAQccMDxf7svxxx8/fE4REVi7V7/61cN3wPP/i1/8YnLyyScPMrqfd77znYdzOqbj/9u//dvk3//934fP/t3f/d2orDvssMPk9NNPnzz60Y+e7LHHHsMzLZ/6xS9+cfKmN71p1nu/9rWvDe9zL5/85CcPa+O58Mzf8Y53nPVef/vv//7v5Vqvju7JdqwgnvSkJ02vfvWrD3m3ij//+c9XyZP1N55sxpe//OXh7y996Utn/Z2HwPPIntpf48luueWWs3K1L37xi4e/f+5zn5u5ptvd7naDFxvXB87P2n/wgx88628VF1100XC817zmNXPk46388Y9/nP41Oc7WOY844ojp1a52tem3vvWtWZ6sY+y7776z3vuJT3xi+DvvN2O77babs65PfepTBy/tJz/5yZxc6I1udKMZWVY0J3vggQcO7//FL37RfD3WMH622GKL4f5liHyIQlR8/vOfHz5z4oknzvr7r371q+nHPvax6Q9+8IPlknHsnv3oRz8avMSHPOQhs3LEL3vZy4b3n3LKKcvMyTperRfgBW+88caDx708Odn6nfv0pz89/C7/nCFf7e/ZS47voKhIvi4e7R577DHnXBEduPzyy+dZsY5Ary7uWG5orWDBPvzhD5/c/e53n/P6VW0hUOHJas/glbDgzz777Jm//elPfxo8ZudfkZztfHjKU54yy/sOz+sb3/jGjAf01a9+dbLNNtsMVak/+clPhh9ezAMf+MDJhz70oZmWkyzTH/7wh+H9qjHXW2+9ySc/+ck55+a1XOMa1/ir5M/nJBPZeI1sD95dBU+otmWEh5Yh75bheG984xuHtff/sQ5+3DteWusalwfWiUcZnl3FHe5wh6FK2LMnvyoPWquLf/Ob30zWWWedOZ+NHK/XM3jxvNwcJVke1Hv2vve9b/CwefG5Ktr7eNXyvcuC48Uz6Fn66U9/OnjvvmNXdU3f8Y53DP/uvvvus/7Oo4UqlzWOZx94ziJK8T3IuPGNbzz86953LBs9XNyx3NAfJ7SlmGRlgpJtQch4//33H8LR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Zl7/zW9+M33GM54xXX/99afXu971pv/+7/8+/eEPf7hC57jiiiuG4/q3o2Op4lvf+tb0JS95yfRDH/rQ9M9//vP05z//+fSkk06a9R7fkVNOOWV67rnnTi+88MLpBz7wgTnH+f3vfz/97//+7+luu+02/fKXv9w81x/+8IfpAQccMD3++OOnX/nKV+a8/r3vfW965plnTi+77LJBpo985CODTIF3v/vd08985jPD/5OTTG94wxumv/vd74a/veIVr5j+8pe/nCPTq171qul3v/vd6ctf/vI553T8j370o9NDDz10euqpp46u04knnjg9/PDDpxdccMEsmeIYL3rRi6Y//vGPpyeffPL0jW9843DuwE9+8pNB1pDpTW960/TVr3719Kc//enwN9dpXfPxLrroouGYX/va12a9N+Mb3/jGsJb777//9E9/+lNT7s997nPTvffee/pf//Vfs2QKvOUtbxne43Wy/+xnP5v1+ktf+tLpr3/960EmMr74xS8ezgu/+MUvpieccMJ0bcaK6IFFNyCA1SnEE8j5DtyirDI5IlYIlhLWp1BIR0fHssG7E4KUbxSGDVJ53ydVq3Ksejxbk3T0iCr2iXxcJroX7lWIVMO6MbXG3xUkxXSbjKALVGSTZQo4No+T96roqE7SUZilqpa32pqkoy9VfjgKgchkDVTUHnTQQUMhUp2fGpN0rAu5hJyrx8mjI7se1L322mtOcRgZhZp5gDEIPU/SERUwk1aBlLW2TmSMIQWOF21SwOsKmeSSrZm9L7fVxKg+0QLerdeqB8/TJRtvl9yt6UY8Vd70H/7wh0G+PAvYNfP+tWxlUotFi+ki82T//u//vvkaC/Za17rWYFUHvvjFLw7WCOtvedE92Y6liD/+8Y/Tt73tbdPdd999Xo/zoIMOGrwk362K73znO9Ozzjpr+Jenw7PNntTb3/726aWXXjrj7Zx22mnT17/+9dPf/va3w994lL/61a9mne+tb33r9JWvfOX029/+dtPjhEsuuWTYG5blcR555JHT888/f47HSUbeIa+QTGefffaMTMAT9fcsE6+eJwof/OAHpx//+MdnHfPiiy8ejskz5zFXTzCiBd6z3377jXqcn/3sZ4fXzznnnFkyBXi/7sWb3/zmWTKB6+T5+5z/J2PIFHudta0y+cyHP/zhYV3PO++8OeeMCAZP+dvf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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ny_t, nx_t = 30, 50\n", + "xi, yi = np.meshgrid(\n", + " np.linspace(-110, 110, nx_t),\n", + " np.linspace(-45, 45, ny_t),\n", + " indexing=\"xy\",\n", + ")\n", + "theta = np.deg2rad(30)\n", + "lon2d = xi * np.cos(theta) - yi * np.sin(theta)\n", + "lat2d = xi * np.sin(theta) + yi * np.cos(theta)\n", + "\n", + "target = xr.Dataset(\n", + " coords={\n", + " \"longitude\": ((\"ny\", \"nx\"), lon2d),\n", + " \"latitude\": ((\"ny\", \"nx\"), lat2d),\n", + " }\n", + ")\n", + "\n", + "fig, ax = plt.subplots(figsize=(8, 4))\n", + "ax.plot(lon2d, lat2d, color=\"0.3\", lw=0.4)\n", + "ax.plot(lon2d.T, lat2d.T, color=\"0.3\", lw=0.4)\n", + "ax.set_title(\"curvilinear target (30° rotation)\")\n", + "ax.set_xlabel(\"longitude\"); ax.set_ylabel(\"latitude\")\n", + "ax.set_aspect(\"equal\")" + ] + }, + { + "cell_type": "markdown", + "id": "2272ad8a", + "metadata": {}, + "source": [ + "## Regrid\n", + "\n", + "The accessor detects that `latitude` / `longitude` are 2D and routes through\n", + "the curvilinear path (threaded GEOS polygon clipping under the hood). On the\n", + "reusable class you'd construct once via `ConservativeRegridder(src, target, ...)`\n", + "and apply to many fields." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "430c61f1", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:11.552310Z", + "iopub.status.busy": "2026-04-19T17:16:11.552234Z", + "iopub.status.idle": "2026-04-19T17:16:12.506053Z", + "shell.execute_reply": "2026-04-19T17:16:12.505729Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.DataArray 'bumps' (ny: 30, nx: 50)> Size: 12kB\n",
+       "array([[            nan,             nan,             nan, ...,\n",
+       "         5.07649140e-05,  1.78419614e-05,  5.78932492e-06],\n",
+       "       [            nan,             nan,             nan, ...,\n",
+       "         8.41768394e-05,  2.95914993e-05,  9.59425795e-06],\n",
+       "       [            nan,             nan,             nan, ...,\n",
+       "         1.33940155e-04,  4.70846845e-05,  1.52861050e-05],\n",
+       "       ...,\n",
+       "       [-5.43488000e-04, -1.59138071e-03, -4.21769110e-03, ...,\n",
+       "                    nan,             nan,             nan],\n",
+       "       [-3.79047801e-04, -1.11100367e-03, -2.94474907e-03, ...,\n",
+       "                    nan,             nan,             nan],\n",
+       "       [-2.51769993e-04, -7.37413528e-04, -1.95498023e-03, ...,\n",
+       "                    nan,             nan,             nan]],\n",
+       "      shape=(30, 50))\n",
+       "Coordinates:\n",
+       "    longitude  (ny, nx) float64 12kB -72.76 -68.87 -64.99 ... 64.99 68.87 72.76\n",
+       "    latitude   (ny, nx) float64 12kB -93.97 -91.73 -89.48 ... 89.48 91.73 93.97\n",
+       "Dimensions without coordinates: ny, nx
" + ], + "text/plain": [ + " Size: 12kB\n", + "array([[ nan, nan, nan, ...,\n", + " 5.07649140e-05, 1.78419614e-05, 5.78932492e-06],\n", + " [ nan, nan, nan, ...,\n", + " 8.41768394e-05, 2.95914993e-05, 9.59425795e-06],\n", + " [ nan, nan, nan, ...,\n", + " 1.33940155e-04, 4.70846845e-05, 1.52861050e-05],\n", + " ...,\n", + " [-5.43488000e-04, -1.59138071e-03, -4.21769110e-03, ...,\n", + " nan, nan, nan],\n", + " [-3.79047801e-04, -1.11100367e-03, -2.94474907e-03, ...,\n", + " nan, nan, nan],\n", + " [-2.51769993e-04, -7.37413528e-04, -1.95498023e-03, ...,\n", + " nan, nan, nan]],\n", + " shape=(30, 50))\n", + "Coordinates:\n", + " longitude (ny, nx) float64 12kB -72.76 -68.87 -64.99 ... 64.99 68.87 72.76\n", + " latitude (ny, nx) float64 12kB -93.97 -91.73 -89.48 ... 89.48 91.73 93.97\n", + "Dimensions without coordinates: ny, nx" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "regridded = src.regrid.conservative_polygon(\n", + " target, x_coord=\"longitude\", y_coord=\"latitude\"\n", + ")\n", + "regridded" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "8c80215d", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:12.507194Z", + "iopub.status.busy": "2026-04-19T17:16:12.507133Z", + "iopub.status.idle": "2026-04-19T17:16:12.559023Z", + "shell.execute_reply": "2026-04-19T17:16:12.558588Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAi4AAAGJCAYAAACtu7gUAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAmS9JREFUeJztnQeYG9XVv4+0vdjrsu69gAvggo2N6QSDKQkQCKHGQCihJvTy/UMPvXwEYkIIYCDAhyEBQoCYXgIYA6aD7eBu3NvuenvR/J/ftUceXc3ce2clrTTSeZ9nwJLujkZ1Xp17zrkhy7IsYhiGYRiGCQDhdB8AwzAMwzCMKSwuDMMwDMMEBhYXhmEYhmECA4sLwzAMwzCBgcWFYRiGYZjAwOLCMAzDMExgYHFhGIZhGCYwsLgwDMMwDBMYWFwYhmEYhgkMLC6MknfffZdCoZD4v44DDjhAbMncpynXX3+92CcTDFLxHugocNx4v9k89thj4rply5b5/ixkM+n8PmCyGxYXhkmA+vp6cRLLxC/dV199NeYE2x5uueUWevHFF5N2TAzDMImSn/AemKxmv/32o4aGBiosLEz3oWSsuNxwww3i35n2CxviMmPGjITkBeLyi1/8go4++uikHluQwechP1/91fn6669TrsPPAZMqOOISIOrq6jrsvhobGykSiVA4HKbi4mLxfya92K8JkzywxixExA/4POjEBaKfybKfyu8SyHwQngMmuPDZKEOxcza+//57Oumkk6hr1660zz77RG9/8sknacKECVRSUkLdunWjE044gVauXBm3H/ziHjp0qBg3adIk+s9//hM392zPMT/zzDP0+9//nvr160elpaVUU1PjOf/80EMP0bBhw2L268aPP/4ofq2XlZVRz5496eKLL6ampibXsXPnzqVDDz2UKioqxP3vv//+9OGHH8aN++CDD2iPPfYQJxAcw1/+8hdfz+1zzz0Xfe4qKyvplFNOoVWrVsWMOe2006i8vFxcj+PHv3v06EGXXXYZtbW1iTHIacB1AFEXPE9y/sPbb79N++67r3j8Xbp0oaOOOormz5+vPUbVa2LyGHD8eO2BfVzOHKC77rqL9tprL+revbvYB/b197//PeYYMB4nuMcffzz699ivDe7v17/+NfXq1YuKiopol112oUcffTSh94AbuJ8zzjiD+vbtK+5nyJAhdO6551Jzc7Myv8kt92Tw4MH005/+lF577TWaOHGieOx4/+y666504IEHxu0DoojnHlEn5/Oii2J5fcaeffZZuvnmm6l///7i/XvQQQfRokWL2vVZWL58OZ133nk0YsQI8TjwWh533HExj9f5PLz33ntiPF4D3L8K7PvII4+Mec3wnMnfBXiMeO7mzZsnorM41v/5n/9xfQ6S8V5gGMBTRRkOvoh22mknEbLHr0OAL75rrrmGfvnLX9KZZ55JGzZsoPvvv198cXzxxRfiBAn+/Oc/0wUXXCBOnPiCwBcavjQgQW5fXDfddJP4hYSTM75MvH4tPfLII/Sb3/xGnPguuugiWrJkifiSg0ANGDAgOg6/ZPHFvGLFCvrtb38rTjx/+9vfxMlcBtcddthh4gR63XXXiQjPzJkz6Sc/+YmQIsgR+Oabb+iQQw4RwoCTR2trqxiPk6cJ+BI//fTThfjceuuttG7dOvrjH/8oTgrO5w5AUKZNm0aTJ08WJ/o333yT7r77biFLOHHiGPAc498///nP6ZhjjhF/N2bMGPF/jMdjgjjiWPF84HXae++96fPPPxcnUR1ur4nJY8Drs3r1anrjjTfEcy6D8XjNTj75ZCEAECS8115++WU64ogjxBj8Hd5feO7PPvtscR0eO8B97rnnnuJEhvcYnot///vfQjAgV3hf+H0PuIHHgPuvqqoSxzBy5EghMpAs/LJvzy/6hQsX0oknniieo7POOkuc+I8//njxGq1du5Z69+4dI8k4BvwwSAa33XabeG/j9ayurqY77rhDvAYQFb+fhU8//ZQ++ugjcWz4POPzjfcjZAE/eCARTiAteJ2uvfZaZcQFt+G+1qxZQ7/73e/E8/H000/TO++84zp+06ZN4nhxHBBor89iou8FholiMRnJddddB0uxTjzxxJjrly1bZuXl5Vk333xzzPXffPONlZ+fH72+qanJ6t69u7XHHntYLS0t0XGPPfaY2O/+++8fve6dd94R1w0dOtSqr6+P2a99G/4PmpubrZ49e1rjxo0T92Hz0EMPxe333nvvFdc9++yz0evq6uqs4cOHx+wzEolYO+20kzVt2jTxbxscy5AhQ6yDDz44et3RRx9tFRcXW8uXL49e9/3334vnRPd2to991113tRoaGqLXv/zyy+Jvr7322uh1p556qrjuxhtvjNnH+PHjrQkT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7uPQQw+1xo8fb82dO9f64IMPrJ122sk68cQT0/iomHTC4pJGTj31VPFBlrd33nknOmbZsmXWYYcdZpWUlIgvR3xptrS0xOwH48eNG2cVFhZaQ4cOFeXVuca8efOsyZMni7Ly4uJia9SoUdYtt9xiNTY2xoz76quvrH322UecpPv16ye+NJlt3H///eKEi/cRyqM//vhjfmpcQLuBPn36iOcJ7yFcXrRoUfR2nHDPO+88UXoPMf75z39urVmzJmefS3w/uX3P4fvPLom+5pprxI8KfC4POugga+HChTH72LRpkxAV/MBDO4jTTz89+gOPyT1C+E+6oz4MwzAMwzAmcB8XhmEYhmECA4sLwzAMwzCBgcWFYRiGYZjAwOLCMAzDMExgYHFhGIZhGCYwsLgwDMMwDBMYWFwYhmEYhgkMLC4MwzAMwwQGFheGyTAOOOAAuuiii7LmPrFIpr0GEsMwTKLwIosMw9Dzzz8fXXgSDB48WIhMRwsUwzCMDhYXhmGoW7du/CwwDBMIeKqIYTKYLVu20PTp06lr165UWlpKhx12GP3www/R2x977DHq0qULvfbaazRq1CgqLy+nQw89lNasWRMd09raSr/97W/FuO7du9OVV15Jp556asz0jXOqCP9evnw5XXzxxRQKhcQGrr/+eho3blzM8d17770iOmPT1tZGl1xySfS+rrjiCizkGvM3kUiEbr31VhoyZAiVlJTQ2LFj6e9//3sKnj2GYbIRFheGyWCQH/LZZ5/RSy+9RHPmzBEScPjhh1NLS0t0TH19Pd111130t7/9jd5//31asWIFXXbZZdHbb7/9dnrqqado5syZ9OGHH1JNTQ29+OKLymmj/v3704033igEyClBOu6++24hU48++ih98MEHtHnzZnrhhRdixkBannjiCXrwwQfpu+++E4J0yimn0Hvvvef7+WEYJvfgqSKGyVAQWYGwQDb22msvcR0EZMCAAUI8jjvuOHEdJAYSMGzYMHH5ggsuENJhc//999PVV19NP//5z8XlP/3pT/Tqq68qp43y8vKoU6dO1Lt3b1/HjAgM7uuYY44Rl3FciAbZNDU10S233EJvvvkmTZkyRVw3dOhQITl/+ctfaP/99/d1fwzD5B4sLgyTocyfP5/y8/Np8uTJ0esw/TJixAhxmw2mkGxpAX369KH169eLf1dXV9O6deto0qRJ0dshJRMmTBBTNskE94XojPN4cfwTJ06MThctWrRIRIgOPvjgmL9tbm6m8ePHJ/V4GIbJTlhcGCbgOKuBAHJS5LySZBAOh+P265yyMqG2tlb8/5VXXqF+/frF3FZUVJSEo2QYJtvhHBeGyVCQbIvE2rlz50av27RpEy1cuJBGjx5ttI+Kigrq1asXffrppzEJtJ9//rny7woLC8U4Jz169KC1a9fGyMuXX34Zc1+I9jiPF8c/b9686GUcNwQFeTjDhw+P2TAFxjAMo4MjLgyToey000501FFH0VlnnSXyP5BzctVVV4lIBa435cILLxQJsZCDkSNHipwXVCvZ1UJuoFIIib4nnHCCEI3KykpRbbRhwwa644476Be/+AXNnj2b/v3vf1Pnzp2jf/e73/2ObrvtNnHsuK977rmHqqqqorfjMSBxGAm5mKraZ599xBQT8niwH1Q7MQzDqOCIC8NkMKgEQj7KT3/6U5HMimgHEmvl6SEVKH8+8cQTRVk19oGS6WnTplFxcbHn3yC5d9myZSJ3BpEWOwL0wAMP0IwZM0QJ8yeffBJTvQQuvfRS+tWvfiUEBPcFUbGTgm1uuukmuuaaa4RMYZ8o38bUEcqjGYZhdISsVEyGMwyTsSDSAWH45S9/KSSCYRgmSPBUEcNkOWgm9/rrr4tSY5Qjoxx66dKldNJJJ6X70BiGYXzDU0UMk+WgGghN4fbYYw/ae++96ZtvvhF9VBB1YRiGCRo8VcQwDMMwTGDgiAvDMAzDMIGBxYVhGIZhmMDA4sIwDMMwTGBgcWEYhmEYJjCwuDAMwzAMExhYXBiGYRiGCQwsLgzDMAzDBAYWF4ZhGIZhKCj8f7CRRwnXiXnMAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots(figsize=(8, 4))\n", + "pc = ax.pcolormesh(lon2d, lat2d, regridded.values, cmap=\"RdBu_r\", shading=\"auto\",\n", + " vmin=-1, vmax=1)\n", + "fig.colorbar(pc, ax=ax, shrink=0.8)\n", + "ax.set_title(\"regridded onto rotated curvilinear grid\")\n", + "ax.set_xlabel(\"longitude\"); ax.set_ylabel(\"latitude\")\n", + "ax.set_aspect(\"equal\")" + ] + }, + { + "cell_type": "markdown", + "id": "bd41e9b9", + "metadata": {}, + "source": [ + "## Conservation check\n", + "\n", + "The regridder's internal intersection-area matrix `A[i, j]` gives the exact\n", + "mass that each source cell contributes to each target cell. Summing those\n", + "contributions against the source field is the ground-truth mass within the\n", + "overlap region. Multiplying the output by each target cell's covered area\n", + "should match.\n", + "\n", + "Target cells that fall outside the source are NaN in the output, which is\n", + "why we mask them before summing." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "75869b58", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:12.560078Z", + "iopub.status.busy": "2026-04-19T17:16:12.560016Z", + "iopub.status.idle": "2026-04-19T17:16:12.884274Z", + "shell.execute_reply": "2026-04-19T17:16:12.883830Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "direct (A · s): 325.275958\n", + "regridded (out · a_dst): 325.275958\n", + "relative difference: 5.24e-16\n", + "covered target fraction: 87.60%\n" + ] + } + ], + "source": [ + "rgr = ConservativeRegridder(src, target, x_coord=\"longitude\", y_coord=\"latitude\")\n", + "A = rgr._areas # sparse (n_dst, n_src)\n", + "target_covered = A.sum(axis=1).todense().reshape(regridded.shape)\n", + "src_covered = A.sum(axis=0).todense()\n", + "\n", + "valid = np.isfinite(regridded.values)\n", + "direct_mass = float((src.values.ravel() * src_covered).sum())\n", + "regridded_mass = float((regridded.values[valid] * target_covered[valid]).sum())\n", + "print(f\"direct (A · s): {direct_mass:.6f}\")\n", + "print(f\"regridded (out · a_dst): {regridded_mass:.6f}\")\n", + "print(f\"relative difference: {abs(direct_mass - regridded_mass) / max(abs(direct_mass), 1e-12):.2e}\")\n", + "print(f\"covered target fraction: {valid.mean():.2%}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/notebooks/demos/demo_conservative_polygon_unstructured.ipynb b/docs/notebooks/demos/demo_conservative_polygon_unstructured.ipynb new file mode 100644 index 0000000..5bfe703 --- /dev/null +++ b/docs/notebooks/demos/demo_conservative_polygon_unstructured.ipynb @@ -0,0 +1,1062 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "84b1ee05", + "metadata": {}, + "source": [ + "# Polygon conservative regridder — unstructured mesh + save/load\n", + "\n", + "When cells are arbitrary polygons (ICON triangles, MPAS hexagons, country shapes for\n", + "regional aggregation) use `ConservativeRegridder.from_polygons`. The shapely STRtree\n", + "handles any polygon layout.\n", + "\n", + "This notebook builds a synthetic Voronoi hex-like mesh with scipy, regrids a structured\n", + "source onto it, then persists the regridder so subsequent runs skip the weight build." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a3b1f7ee", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:14.078753Z", + "iopub.status.busy": "2026-04-19T17:16:14.078509Z", + "iopub.status.idle": "2026-04-19T17:16:15.304905Z", + "shell.execute_reply": "2026-04-19T17:16:15.304385Z" + } + }, + "outputs": [], + "source": [ + "import tempfile\n", + "from pathlib import Path\n", + "\n", + "import numpy as np\n", + "import xarray as xr\n", + "import matplotlib.pyplot as plt\n", + "from matplotlib.collections import PolyCollection\n", + "from scipy.spatial import Voronoi\n", + "import shapely\n", + "\n", + "import xarray_regrid # noqa: F401\n", + "from xarray_regrid import ConservativeRegridder, polygons_from_coords" + ] + }, + { + "cell_type": "markdown", + "id": "4400ded4", + "metadata": {}, + "source": [ + "## Build a hex-like Voronoi mesh\n", + "\n", + "Jittered grid points → Voronoi → clip to the region of interest. Real workflows\n", + "would load a pre-built mesh (UGRID, ICON, etc.); the point here is that\n", + "`from_polygons` needs only a 1D array of shapely polygons." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "6cb76d9f", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:15.306411Z", + "iopub.status.busy": "2026-04-19T17:16:15.306266Z", + "iopub.status.idle": "2026-04-19T17:16:15.319900Z", + "shell.execute_reply": "2026-04-19T17:16:15.319496Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "398 cells in the mesh\n" + ] + } + ], + "source": [ + "def voronoi_mesh(n_points, bbox, seed=0):\n", + " rng = np.random.default_rng(seed)\n", + " x0, y0, x1, y1 = bbox\n", + " side = int(np.sqrt(n_points))\n", + " xs = np.linspace(x0, x1, side); ys = np.linspace(y0, y1, side)\n", + " pts = np.column_stack([np.repeat(xs, side), np.tile(ys, side)])\n", + " pts += rng.normal(scale=(x1 - x0) / side * 0.25, size=pts.shape)\n", + " halo = np.array([\n", + " [2*x0 - x1, 2*y0 - y1], [2*x1 - x0, 2*y0 - y1],\n", + " [2*x0 - x1, 2*y1 - y0], [2*x1 - x0, 2*y1 - y0],\n", + " ])\n", + " vor = Voronoi(np.concatenate([pts, halo]))\n", + " clip = shapely.box(x0, y0, x1, y1)\n", + " polys, centers = [], []\n", + " for i in range(len(pts)):\n", + " r = vor.regions[vor.point_region[i]]\n", + " if not r or -1 in r:\n", + " continue\n", + " p = shapely.intersection(shapely.Polygon(vor.vertices[r]), clip)\n", + " if p.is_empty or p.geom_type != \"Polygon\":\n", + " continue\n", + " polys.append(p); centers.append(pts[i])\n", + " return np.array(polys, dtype=object), np.array(centers)\n", + "\n", + "bbox = (-120, -50, 120, 50)\n", + "mesh_polys, mesh_centers = voronoi_mesh(n_points=400, bbox=bbox)\n", + "print(f\"{len(mesh_polys)} cells in the mesh\")" + ] + }, + { + "cell_type": "markdown", + "id": "d5d25e62", + "metadata": {}, + "source": [ + "## Source — structured lat/lon field" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "fd807000", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:15.320797Z", + "iopub.status.busy": "2026-04-19T17:16:15.320734Z", + "iopub.status.idle": "2026-04-19T17:16:15.399656Z", + "shell.execute_reply": "2026-04-19T17:16:15.399270Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAv4AAAFACAYAAADETXghAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAsSpJREFUeJztnQmYJEWZ96Ou7pnhRmC4jwGW+xIWBFxEQA7ZXRFWQVEOEVa5FFAOlVtBRBFhQdCVa4WFRcFrEWFBUHREHERAgU8QZOQYLgcYZqa7qjK/J6LijXoj8q2orKrururp/+95qrsqIjIyqyozI+r/HlFI0zRVAAAAAAAAgCWaYr8PAAAAAAAAADD+YOIPAAAAAADAFAATfwAAAAAAAKYAmPgDAAAAAAAwBcDEHwAAAAAAgCkAJv4AAAAAAABMATDxBwAAAAAAYAqAiT8AAAAAAABTAEz8AQAAAAAAmAJg4g8AAJOAQqGgzjrrrH4fBgAAgEkMJv4AgL5zww03qIsvvnjKHwMAAAAwnmDiDwDoO5j4AwAAAOMPJv4AgEnF4sWLVZIkarKwcOFCNdVI01QtWrSo34cBAAAgABN/AMC48uabb6pPf/rTat1111XDw8NqlVVWUe95z3vUgw8+aOp33XVX9b//+7/qr3/9q/Fj1w/dVnPPPfeY1zfeeKP6whe+oNZYYw01Y8YM9cYbbxh/d10Xcs0115jyZ555xiv/6U9/qt71rnepZZZZRi277LLqH//xH42lod0xtOqPjk3/J3Q/m2++uZozZ47aZZddzLF+7nOfM3UjIyPqzDPPVBtssIH5HNZaay118sknm3KOfn3CCSeolVde2Rzrv/7rv6q//e1vuT/vSy+9VG222WZm3yussILabrvt3Pskfv/736t99tnHfA5LL7202n333dVvfvMbr00nn6/+rP75n/9Z/exnPzP7mz59urryyitN3fz58837oe9/zTXXVIcccoh65ZVXvPec57MBAADQG+UetwcAgCif+MQn1Pe+9z117LHHqk033VS9+uqr6r777lOPPfaYevvb364+//nPq9dff91Mbr/+9a+bbfRklHPuueeqoaEh9ZnPfMZMBvXzTtCT1Y997GNmQnzaaaep5Zdf3kx+b7/9dvXhD3841zHkRb8/Pak+6KCD1Ec+8hE1c+ZMY6HQE3j9vo866ii1ySabqEceecTs6//9v/+nfvCDH7jtP/7xj6vvfve75rh22mkndffdd6t99903176//e1vq+OPP17927/9m/rUpz5lrCMPP/ywuv/++01/mj/+8Y/qn/7pn8ykX0+uK5WKmaTrHy333nuv2mGHHbp630888YT60Ic+pP793/9dHXnkkWqjjTZSCxYsMPvS37X+/PX3rSf8P/rRj8xnvdJKK3X02QAAAOiRFAAAxpHlllsuPeaYY6Jt9t1333SdddbJlP/85z9P9W1q1qxZ6cKFC726M88809SFXH311ab86aefNq/nz5+fLrPMMukOO+yQLlq0yGubJEnbYwj7C49N/yfe9a53mbIrrrjCa/tf//VfabFYTH/5y1965bqdbv+rX/3KvH7ooYfM66OPPtpr9+EPf9iU6/cc433ve1+62WabRdvst99+6dDQUPrUU0+5sueff958RrvsskvHn69Gf2667Pbbb/fannHGGab8lltuyfRDn33ezwYAAEDvwNUHADCuaHVdK87PP/98130ceuihxn2kG+68807jbnTqqaeqadOmeXWSK0uvaFeVww8/3Cu7+eabjZK98cYbG8WbHrvttpup//nPf27+33bbbea/Vu052lUq72etlfQHHnhArK/X6+qOO+5Q++23n5o1a5YrX2211YxFQKvu2o2qG9Zbbz211157eWXf//731VZbbaXe//73Z9rTZ5/3swEAANA7mPgDAMaVr3zlK+rRRx81ftvbb7+98R3/y1/+0vGkslueeuop81/73k8EOg4hdEX685//bFxstN8+f/zDP/yDqX/ppZfMfx1jUCwW1frrr+9tr91m8nDKKacYFyX9OW+44YbqmGOOUb/61a9c/csvv2yCjaX+9ORbu93MnTu3q/ctfUf6s2/3uef9bAAAAPQOfPwBAOPKBz/4QePnfeuttxq1+cILL1QXXHCBuuWWW4wvfB4ktb+VWq9V7bGk0/1Ix6on1FtssYW66KKLxG30j6KxQE/eta/9T37yExO/oBX3yy+/XJ1xxhnq7LPPnvD3nYeJ+mwAAABg4g8AmAC0K8nRRx9tHlrB1UGeX/rSl9zEvxuXG52xhrLGaBcXQqvmHFLPtdVBZ41pRatj4PvhhPuJoY/hD3/4g8meE3uv66yzjpkIa6Wcq/J6Mp+XpZZaSh144IHmMTo6qvbff3/zWeugZq2k62w/Un+PP/64sTbQRDvv59vufevPfSw+GwAAAL0DVx8AwLih1WGdLYej03muvvrqXqpGPVkN27WDJvS/+MUvXNlbb72lrr32Wq/dnnvuadJinn/++SbLTZhvvt0xSPvR7+tb3/pWR1aP5557zmTdCdH57vVxa+iH0CWXXOK1ybuqsc4oxNEuRzqTkn6f1WpVlUol83n88Ic/9NJxzps3z6T8fOc732my/XTy+cY44IADzKReW3tC6LPP+9kAAADoHbj6AADGDR1Uq/O26/SSOshT+5//3//9nwk+/drXvubabbvttuqmm25SJ554osmvr9v9y7/8S7RvPYFde+211RFHHKE++9nPmkntVVddZVTtZ5991rXTE1mdGlKnydR96yBWrWbrCan2d6eJbKtj0ClA3/GOdxjF/LXXXlMrrriiWVegVqvl/hw++tGPqv/5n/8xqU11sOrOO+9sfjxolV2XU/77rbfe2qTE1O45+keITud51113qSeffDLXfvRnsuqqq5r+dRpRnUbzP/7jP0w6UP3jR/PFL37RBDzrSb62wJTLZZPOU/8Q0/EYnX6+MfR2OpXrBz7wAZPOU3/G+jPU6TyvuOIKc07k/WwAAACMAWOQGQgAAERGRkbSz372s+lWW21l0kUutdRS5vnll1/utVuwYIFJWbn88subFI6UVpNSZt58881i/3PmzDFpOnV6yrXXXju96KKLWqbf/NGPfpTutNNO6fTp09Nll1023X777dP//u//bnsMGp36co899kiHh4fTmTNnpp/73OfSO++8U0zn2Sqd5ujoaHrBBReYet3PCiuskG677bbp2Wefnb7++uuunU45evzxx6dve9vbzOf1L//yL+ncuXNzpfO88sorTUpOva3ex/rrr28+f96/5sEHH0z32muvdOmll05nzJiRvvvd705//etfd/356s9Kp0OVePXVV9Njjz02XWONNUw/a665ZnrooYemr7zySsefDQAAgN4o6D9j8QMCAAAAAAAAMLjAxx8AAAAAAIApACb+AAAAAAAATAEw8QcAAAAAAGAKgIk/AAAAAAAAUwBM/AEAAAAAAJgCYOIPAAAAAADAFAALeDGSJFHPP/+8WegGS8cDAAAAYElCZ3DXCyvq1dOLxf5rv3o19dHR0dzt9Wrk06ZNG9djWtLBxJ+hJ/1rrbVW/74NAAAAAIBx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vHnz8BkPIL/4xS/Uv/zLv5iFrfT3+IMf/CCz8NUZZ5yhVlttNTV9+nS1xx57qD//+c9em9dee00dfPDBJr+/HpuPOOIItWDBggl+JwDEwcR/EqJXufvABz6gPvnJT4r19XrdTPp1u1//+tfq2muvNZN6fdMinn76adPm3e9+t3rooYfUpz/9afXxj39c/exnP5vAdwLycM4556gXXnjBPY477jhvFck999xTrbPOOmrOnDnqwgsvNJORb33rW/hwB5ybbrpJnXjiiSYT14MPPqi22mortddee6mXXnqp34cGOmCzzTbzrs/77rvP1Z1wwgnqxz/+sbr55pvVvffea1aG33///fH5DiBvvfWWuQb1j3EJLahccskl6oorrlD333+/Wmqppcz1qn/cEXrS/8c//lHdeeed6ic/+Yn5MXHUUUdN4LsAIAcpmLRcffXV6XLLLZcpv+2229JisZi++OKLruyb3/xmuuyyy6YjIyPm9cknn5xuttlm3nYHHnhgutdee03AkYO8rLPOOunXv/71lvWXX355usIKK7jvVXPKKaekG220ET7kAWf77bdPjznmGPe6Xq+nq6++enr++ef39bhAfs4888x0q622Euvmz5+fViqV9Oabb3Zljz32WKqH3dmzZ+NjHmD0d3Trrbe610mSpKuuump64YUXet/v8PBw+t///d/m9Z/+9Cez3QMPPODa/PSnP00LhUL63HPPTfA7AKA1UPyXQGbPnq222GILNXPmTFemlQmtDms1gtpoUyVHt9HlYLDQrj3aVWCbbbYxij532dLf1y677KKGhoa87/GJJ55Qf//73/t0xKAd2hqnLTT8GiwWi+Y1rsHJhXb30O4hs2bNMoqvdrPU6O+3Wq1637F2A1p77bXxHU8ytIX8xRdf9L7L5ZZbzrjn0fWq/2v3nu2228610e31da0tBAAMCuV+HwAYe/QNik/6NfRa18Xa6B8HixYtMj6MoP8cf/zxJg5jxRVXNG5bp512mnEnuOiii9z3uN5667X8rldYYYW+HDeI88orrxiXPOkafPzxx/HxTRL0xE+7UW600Ubmujz77LNNvM2jjz5qrj/9gzyMw9LfMd2HweSAvi/peuVjqo6n45TLZXPvxvcNBglM/AeEU089VV1wwQXRNo899pgXOAaW/O9a+4ATW265pZlI/Pu//7s6//zzsUIzAH1mn3328a5P/UNAx9v8z//8D8QTAMBAgon/gHDSSSepww47LNpGm5LzsOqqq2ayg1AmCV1H/8PsEvq1zkYAtX9wv2s9sdCuPs8884xRGVt9j/y7BoPHSiutpEqlkvjd4XubvGh1/x/+4R/Uk08+qd7znvcYl6758+d7qj++48kHXZP6u9NZfQj9euutt3ZtwsB8fa/WmX5wTYNBAhP/AWHllVc2j7Fgxx13NCk/9U2ITI86y4Ce1G+66aauzW233eZtp9vocjC437XOwKR9Rul71d/X5z//eeNLXKlU3PeofxTAzWdw0ZabbbfdVt11111qv/32M2VJkpjXxx57bL8PD3SJTt341FNPqY9+9KPm+9XXpP5OdRpPjY690TEAuM9OLrQ7pZ686++SJvraLVb77lN2Pf2d6h95OrZDf/eau+++21zXWrABYGCIBP6CAeWvf/1r+vvf/z49++yz06WXXto8148333zT1NdqtXTzzTdP99xzz/Shhx5Kb7/99nTllVdOTzvtNNfHX/7yl3TGjBnpZz/7WZNp4rLLLktLpZJpCwaDX//61yajj/4On3rqqfS73/2u+R4POeQQL7PEzJkz049+9KPpo48+mt54443me73yyiv7euygPfq70llBrrnmGpMR5KijjkqXX355LxsXGGxOOumk9J577kmffvrp9Fe/+lW6xx57pCuttFL60ksvmfpPfOIT6dprr53efffd6e9+97t0xx13NA8weOjxk8ZSPTW66KKLzHM93mq+/OUvm+vzhz/8Yfrwww+n73vf+9L11lsvXbRoketj7733TrfZZpv0/vvvT++77750ww03TD/0oQ/18V0BkAUT/0nIoYceam5M4ePnP/+5a/PMM8+k++yzTzp9+nQzEOkBqlqtev3o9ltvvXU6NDSUzpo1y6QHBYPDnDlz0h122MGkbJ02bVq6ySabpOedd166ePFir90f/vCH9J3vfKeZRK6xxhpmgAKTg0svvdRMDPU1qNN7/uY3v+n3IYEO0CmQV1ttNfP96WtPv37yySddvZ4UHn300Sblrv5B/v73vz994YUX8BkPIHo8lMZVPd5SSs/TTz/dCC36Xrv77runTzzxhNfHq6++aib6WpDT6bMPP/xwJ8gBMCgU9J9+Wx0AAAAAAAAA4wvy+AMAAAAAADAFwMQfAAAAAACAKQAm/gAAAAAAAEwBMPEHAAAAAABgCoCJPwAAAAAAAFMATPwBAAAAAACYAmDiDwAAAAAAwBQAE38AAAAAAACmAJj4AwCmPLvuuqv69Kc/vcTs87DDDlP77bffuPQNAABg8lLu9wEAAMBU5JZbblGVSsW9Xnfddc0PgYn+AQIAAGDqgIk/AAD0gRVXXBGfOwAAgAkFrj4AAMD4+9//rg455BC1wgorqBkzZqh99tlH/fnPf3b111xzjVp++eXVz372M7XJJpuopZdeWu29997qhRdecG1qtZo6/vjjTbu3ve1t6pRTTlGHHnqo537DXX3087/+9a/qhBNOUIVCwTw0Z511ltp666297+fiiy821gGiXq+rE0880e3r5JNPVmmaetskSaLOP/98td5666np06errbbaSn3ve9/D9w4AAFMMTPwBACDwj//d736nfvSjH6nZs2ebSfR73/teVa1WXZuFCxeqr371q+q//uu/1C9+8Qv17LPPqs985jOu/oILLlDXX3+9uvrqq9WvfvUr9cYbb6gf/OAHUbefNddcU51zzjnmBwT/EdGOr33ta+bHyFVXXaXuu+8+9dprr6lbb73Va6Mn/dddd5264oor1B//+EfzA+MjH/mIuvfee/HdAwDAFAKuPgAAYNHKvp7w68n6TjvtZMr0BH6ttdYyE/cPfOADpkz/CNCT6PXXX9+8PvbYY82knbj00kvVaaedpt7//veb1//xH/+hbrvttqjbT6lUUssss4xaddVVO/o+tAVA72v//fc3r/VxaWsEMTIyos477zz1f//3f2rHHXc0ZbNmzTI/Eq688kr1rne9C98/AABMETDxBwAAy2OPPabK5bLaYYcd3Gei3Wc22mgjU0doFyCa9GtWW2019dJLL5nnr7/+upo3b57afvvtXb2e1G+77bbG5WYs0fvS1gF+vPr4t9tuO+fu8+STTxoLxXve8x5v29HRUbXNNtvguwcAgCkEJv4AANAhPBuPRvvkh371Y0GxWMz0y12O8rBgwQLz/3//93/VGmus4dUNDw+PwVECAACYLMDHHwAALDpYVwfm3n///e4zefXVV9UTTzyhNt1001yf03LLLadmzpypHnjgAS8A98EHH4xuNzQ0ZNpxVl55ZfXiiy96k/+HHnrI25e2NvDj1cc/Z84c91oft57g6ziEDTbYwHtoFyYAAABTByj+AABg2XDDDdX73vc+deSRRxr/d+1zf+qppxqlXJfn5bjjjjMBtXpyvfHGGxuff50tiLL1SOhMPTpQ+KCDDjIT9ZVWWslk+3n55ZfVV77yFfVv//Zv6vbbb1c//elP1bLLLuu2+9SnPqW+/OUvm2PX+7rooovU/PnzXb1+DzrwWAf0alejd77zncZFSMcx6H50tiEAAABTAyj+AADA0Jl4tD/+P//zP5tgWK2268Dc0L0nhk7f+aEPfcikBdV96JSfe+21l5o2bVrLbXRw8DPPPGNiB7TSTxaIyy+/XF122WUmBedvf/tbL3uQ5qSTTlIf/ehHzQRe70tP9CmomDj33HPV6aefbn6M6D51+lHt+qPTewIAAJg6FNLxcEwFAADg0Eq7nnB/8IMfNJNwAAAAoB/A1QcAAMYYvRjXHXfcYVJl6nSaOp3n008/rT784Q/jswYAANA34OoDAABjfWMtFs2iWv/4j/+odt55Z/XII4+YPPpa9QcAAAD6BVx9AAAAAAAAmAJA8QcAAAAAAGAKgIk/AAAAAAAAUwBM/AEAAAAAAJgCYOIPAAAAAADAFAATfwAAAAAAAKYAmPgDAAAAAAAwBcDEHwAAAAAAgCkAJv4AAAAAAABMATDxBwAAAAAAQC35/H8TACkBHJ2eLwAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "lat_s = np.linspace(-50, 50, 100, endpoint=False) + 0.5\n", + "lon_s = np.linspace(-120, 120, 240, endpoint=False) + 0.5\n", + "Lo, La = np.meshgrid(lon_s, lat_s)\n", + "field = np.sin(np.deg2rad(Lo) * 2) * np.cos(np.deg2rad(La) * 3)\n", + "src = xr.DataArray(\n", + " field,\n", + " dims=(\"latitude\", \"longitude\"),\n", + " coords={\"latitude\": lat_s, \"longitude\": lon_s},\n", + " name=\"field\",\n", + ")\n", + "src.plot(figsize=(8, 3.3), cmap=\"RdBu_r\", center=0)\n", + "plt.title(\"structured source\")\n", + "plt.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "id": "3bbeefc0", + "metadata": {}, + "source": [ + "## Regrid structured → mesh with `from_polygons`\n", + "\n", + "We convert the structured grid into a flat polygon list via `polygons_from_coords`\n", + "(row-major, y-slow / x-fast) and flatten the data the same way. Output is a 1D\n", + "array indexed by mesh cell." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "c9f0afde", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:15.400534Z", + "iopub.status.busy": "2026-04-19T17:16:15.400476Z", + "iopub.status.idle": "2026-04-19T17:16:16.243410Z", + "shell.execute_reply": "2026-04-19T17:16:16.242933Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ConservativeRegridder(src_dims=('src_cell',), dst_dims=('cell',), 398x24000, nnz=31875)\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.DataArray (cell: 398)> Size: 3kB\n",
+       "array([-0.6828435 , -0.52020535, -0.34043656, -0.05049317, -0.12985816,\n",
+       "        0.31035596,  0.55679515,  0.73714634,  0.79715029,  0.75440137,\n",
+       "        0.73846747,  0.56186966,  0.50518574,  0.24205725,  0.02472535,\n",
+       "       -0.17479924, -0.41243175, -0.62933553, -0.68358687, -0.48097563,\n",
+       "       -0.29176974, -0.24634718, -0.01120315,  0.14080408,  0.089748  ,\n",
+       "        0.33788694,  0.55161779,  0.47166168,  0.67826547,  0.6011212 ,\n",
+       "        0.38839256,  0.46979398,  0.18574702,  0.31437291, -0.02156659,\n",
+       "       -0.06562757, -0.17649315, -0.42198051, -0.308658  , -0.02001994,\n",
+       "       -0.13605377, -0.09357425, -0.00459541,  0.05862798,  0.11163482,\n",
+       "        0.09602874,  0.05599573,  0.11830369,  0.28834433,  0.07279747,\n",
+       "        0.21941021,  0.05890276,  0.11421152,  0.08236516,  0.01092236,\n",
+       "       -0.04497362, -0.04663858, -0.0670035 , -0.21887831,  0.30405694,\n",
+       "        0.14758091,  0.12484246,  0.01801111,  0.01998386, -0.06958026,\n",
+       "       -0.22626343, -0.19633301, -0.34295916, -0.04050547, -0.31439477,\n",
+       "       -0.29516358, -0.23092622, -0.04689604, -0.0397324 , -0.01048109,\n",
+       "        0.01823587,  0.09520633,  0.2304076 ,  0.07931317,  0.59493269,\n",
+       "        0.42148916,  0.30936307,  0.19831751, -0.04594672, -0.19822369,\n",
+       "       -0.39581912, -0.55086584, -0.526165  , -0.4815598 , -0.66516286,\n",
+       "       -0.63227259, -0.43886083, -0.21653109, -0.27231148, -0.10094695,\n",
+       "        0.11938996,  0.42351928,  0.28956548,  0.47690002,  0.74804334,\n",
+       "...\n",
+       "       -0.37656809, -0.22852519, -0.04344777,  0.01792701,  0.17333448,\n",
+       "        0.32389057,  0.46390007,  0.6644992 ,  0.62893444,  0.48691719,\n",
+       "        0.45608447,  0.68040179,  0.46885146,  0.21583469,  0.00941059,\n",
+       "       -0.1331172 , -0.40796933, -0.3614278 , -0.52244927, -0.27981908,\n",
+       "       -0.27632693, -0.23998643, -0.08445465,  0.07873908,  0.07181187,\n",
+       "        0.06149854,  0.19158247,  0.21913798,  0.34554757,  0.30641554,\n",
+       "        0.0175934 ,  0.2244458 ,  0.08640918,  0.11910919,  0.00544381,\n",
+       "       -0.02982331, -0.2136362 , -0.10050146, -0.20911187,  0.01803716,\n",
+       "        0.22387688,  0.08988479,  0.06094732, -0.05726633,  0.0011813 ,\n",
+       "       -0.04608545, -0.185028  , -0.11268017, -0.14596539, -0.25748198,\n",
+       "        0.0815998 , -0.12245219, -0.09431421, -0.05502579, -0.00772848,\n",
+       "        0.04972775,  0.02800019,  0.16214991,  0.26727786,  0.48381549,\n",
+       "        0.40825614,  0.31748295,  0.06169299,  0.07942008, -0.1360152 ,\n",
+       "       -0.28038185, -0.35898583, -0.39069976, -0.46796485, -0.39132032,\n",
+       "       -0.60070257, -0.46962788, -0.3359683 , -0.24423894,  0.06888536,\n",
+       "       -0.05912582,  0.38349241,  0.49376074,  0.39838327,  0.58905439,\n",
+       "        0.56349504,  0.25152149, -0.14721418, -0.33043202, -0.50757441,\n",
+       "       -0.52404807, -0.76061991, -0.75971976, -0.74589083, -0.59263825,\n",
+       "       -0.7027589 , -0.52312689, -0.30315877, -0.00300608,  0.194532  ,\n",
+       "        0.29945327,  0.48218566,  0.64474778])\n",
+       "Coordinates:\n",
+       "  * cell     (cell) int64 3kB 0 1 2 3 4 5 6 7 ... 391 392 393 394 395 396 397
" + ], + "text/plain": [ + " Size: 3kB\n", + "array([-0.6828435 , -0.52020535, -0.34043656, -0.05049317, -0.12985816,\n", + " 0.31035596, 0.55679515, 0.73714634, 0.79715029, 0.75440137,\n", + " 0.73846747, 0.56186966, 0.50518574, 0.24205725, 0.02472535,\n", + " -0.17479924, -0.41243175, -0.62933553, -0.68358687, -0.48097563,\n", + " -0.29176974, -0.24634718, -0.01120315, 0.14080408, 0.089748 ,\n", + " 0.33788694, 0.55161779, 0.47166168, 0.67826547, 0.6011212 ,\n", + " 0.38839256, 0.46979398, 0.18574702, 0.31437291, -0.02156659,\n", + " -0.06562757, -0.17649315, -0.42198051, -0.308658 , -0.02001994,\n", + " -0.13605377, -0.09357425, -0.00459541, 0.05862798, 0.11163482,\n", + " 0.09602874, 0.05599573, 0.11830369, 0.28834433, 0.07279747,\n", + " 0.21941021, 0.05890276, 0.11421152, 0.08236516, 0.01092236,\n", + " -0.04497362, -0.04663858, -0.0670035 , -0.21887831, 0.30405694,\n", + " 0.14758091, 0.12484246, 0.01801111, 0.01998386, -0.06958026,\n", + " -0.22626343, -0.19633301, -0.34295916, -0.04050547, -0.31439477,\n", + " -0.29516358, -0.23092622, -0.04689604, -0.0397324 , -0.01048109,\n", + " 0.01823587, 0.09520633, 0.2304076 , 0.07931317, 0.59493269,\n", + " 0.42148916, 0.30936307, 0.19831751, -0.04594672, -0.19822369,\n", + " -0.39581912, -0.55086584, -0.526165 , -0.4815598 , -0.66516286,\n", + " -0.63227259, -0.43886083, -0.21653109, -0.27231148, -0.10094695,\n", + " 0.11938996, 0.42351928, 0.28956548, 0.47690002, 0.74804334,\n", + "...\n", + " -0.37656809, -0.22852519, -0.04344777, 0.01792701, 0.17333448,\n", + " 0.32389057, 0.46390007, 0.6644992 , 0.62893444, 0.48691719,\n", + " 0.45608447, 0.68040179, 0.46885146, 0.21583469, 0.00941059,\n", + " -0.1331172 , -0.40796933, -0.3614278 , -0.52244927, -0.27981908,\n", + " -0.27632693, -0.23998643, -0.08445465, 0.07873908, 0.07181187,\n", + " 0.06149854, 0.19158247, 0.21913798, 0.34554757, 0.30641554,\n", + " 0.0175934 , 0.2244458 , 0.08640918, 0.11910919, 0.00544381,\n", + " -0.02982331, -0.2136362 , -0.10050146, -0.20911187, 0.01803716,\n", + " 0.22387688, 0.08988479, 0.06094732, -0.05726633, 0.0011813 ,\n", + " -0.04608545, -0.185028 , -0.11268017, -0.14596539, -0.25748198,\n", + " 0.0815998 , -0.12245219, -0.09431421, -0.05502579, -0.00772848,\n", + " 0.04972775, 0.02800019, 0.16214991, 0.26727786, 0.48381549,\n", + " 0.40825614, 0.31748295, 0.06169299, 0.07942008, -0.1360152 ,\n", + " -0.28038185, -0.35898583, -0.39069976, -0.46796485, -0.39132032,\n", + " -0.60070257, -0.46962788, -0.3359683 , -0.24423894, 0.06888536,\n", + " -0.05912582, 0.38349241, 0.49376074, 0.39838327, 0.58905439,\n", + " 0.56349504, 0.25152149, -0.14721418, -0.33043202, -0.50757441,\n", + " -0.52404807, -0.76061991, -0.75971976, -0.74589083, -0.59263825,\n", + " -0.7027589 , -0.52312689, -0.30315877, -0.00300608, 0.194532 ,\n", + " 0.29945327, 0.48218566, 0.64474778])\n", + "Coordinates:\n", + " * cell (cell) int64 3kB 0 1 2 3 4 5 6 7 ... 391 392 393 394 395 396 397" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "grid_polys = polygons_from_coords(lon_s, lat_s)\n", + "\n", + "rgr = ConservativeRegridder.from_polygons(\n", + " grid_polys, mesh_polys,\n", + " source_dim=\"src_cell\",\n", + " target_dim=\"cell\",\n", + ")\n", + "print(rgr)\n", + "\n", + "src_flat = xr.DataArray(src.values.ravel(), dims=(\"src_cell\",))\n", + "mesh_vals = rgr.regrid(src_flat)\n", + "mesh_vals" + ] + }, + { + "cell_type": "markdown", + "id": "e3d11805", + "metadata": {}, + "source": [ + "## Plot the regridded field on the mesh" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "59fc0b6b", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:16.244561Z", + "iopub.status.busy": "2026-04-19T17:16:16.244485Z", + "iopub.status.idle": "2026-04-19T17:16:16.304359Z", + "shell.execute_reply": "2026-04-19T17:16:16.303915Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 1.0, 'regridded onto 398-cell Voronoi mesh')" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAooAAAEYCAYAAADbMtdZAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzsvQdcXNl5Nv5MZSpD772DBAgQSKj33vvu2ms7iZMvif3F9vclsfNP7Nhx4rTPdoodO467t2i16r13oYqEaEKA6L3OAMP0+f/egwYBmmHuFLRF8/z2Lpo7d24995znvOV5eVar1QoffPDBBx988MEHH3yYAv7UFT744IMPPvjggw8++OAjij744IMPPvjggw8+OITPouiDDz744IMPPvjgg134iKIPPvjggw8++OCDD3bhI4o++OCDDz744IMPPtiFjyj64IMPPvjggw8++GAXPqLogw8++OCDDz744INd+IiiDz744IMPPvjggw924SOKPvjggw8++OCDDz7YhY8o+uCDDz744IMPPvhgFz6i6IMPPvjggw8++OBFXLt2DZs3b0ZUVBR4PB6OHDni9DdXrlxBfn4+/Pz8kJKSgl/96lcvbfOjH/0ICQkJkEgkmDdvHu7evTvjz81HFH3wwQcffPDBBx+8iJGREeTm5jJixwUNDQ3YuHEjli9fjkePHuErX/kK/uAP/gBnz54d32b//v342te+hm9961soLS1l+1+7di26u7tn9Nn5iKIPn1jQ7ItmavTXGZYtW8YWb+6TK/72b/+W7dOHTw7stYPPf/7zbCbvw8y8J6/qnD/88MOP+lR8eA2wfv16fPe738X27ds5bf+Tn/wEiYmJ+H//7/8hMzMTX/rSl7Br1y784Ac/GN/m+9//Pr74xS/iC1/4ArKysthvZDIZfvGLX8zglfiIog8+fGqh1WoZSfX2YD46Oorf//3fx+zZs6FSqaBQKNjM9t/+7d9gNBpf2v78+fNYtGgR69ACAwNZ59fY2PjSdjqdDt/73vdYB0jbRkdHY/fu3aisrMQnATSrFwqF+MxnPuNwm6GhIUilUuzYseOVnpsPPvgAu32ORqPhvKjV6pfW6fV6r9zakpISrFq1atI6shbSeoLBYMCDBw8mbcPn89ln2zYzBeGM7t0HH2YQS5YsYaRFLBb77rMDovjtb3+b/ZuLNZUr6J4Teduw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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots(figsize=(8, 4))\n", + "patches = [np.asarray(p.exterior.coords) for p in mesh_polys]\n", + "pc = PolyCollection(patches, array=mesh_vals.values, cmap=\"RdBu_r\",\n", + " edgecolor=\"0.4\", lw=0.3, clim=(-1, 1))\n", + "ax.add_collection(pc)\n", + "ax.set_xlim(bbox[0], bbox[2]); ax.set_ylim(bbox[1], bbox[3])\n", + "ax.set_aspect(\"equal\")\n", + "fig.colorbar(pc, ax=ax, shrink=0.8)\n", + "ax.set_title(f\"regridded onto {len(mesh_polys)}-cell Voronoi mesh\")" + ] + }, + { + "cell_type": "markdown", + "id": "0abb24ac", + "metadata": {}, + "source": [ + "## Persist the regridder to netCDF\n", + "\n", + "For a fixed source / target pair the weight matrix is the same forever. Persisting\n", + "it to disk lets long-running pipelines skip the (expensive) build on restart." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "e4e09ea1", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:16.305498Z", + "iopub.status.busy": "2026-04-19T17:16:16.305432Z", + "iopub.status.idle": "2026-04-19T17:16:16.644657Z", + "shell.execute_reply": "2026-04-19T17:16:16.644235Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "wrote /var/folders/n1/xlk9kjfn39l5q4z54rnbz4tc0000gp/T/mesh_regridder.nc (951.7 KB)\n", + " x_coord: \n", + " y_coord: \n", + " spherical: 0\n", + " src_dims: src_cell\n", + " dst_dims: cell\n", + " src_shape: 24000\n", + " dst_shape: 398\n", + " xarray_regrid_version: 0.4.2\n", + " created: 2026-04-19T17:16:16.307818+00:00\n", + " schema_version: 1\n", + " n_dst: 398\n", + " n_src: 24000\n" + ] + } + ], + "source": [ + "path = Path(tempfile.gettempdir()) / \"mesh_regridder.nc\"\n", + "rgr.to_netcdf(path)\n", + "print(f\"wrote {path} ({path.stat().st_size / 1024:.1f} KB)\")\n", + "\n", + "# Inspect on-disk metadata (useful for provenance):\n", + "with xr.open_dataset(path) as weights:\n", + " for k, v in weights.attrs.items():\n", + " print(f\" {k}: {v}\")" + ] + }, + { + "cell_type": "markdown", + "id": "c9b6292e", + "metadata": {}, + "source": [ + "## Reload and apply to new data" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "9ed8721a", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-19T17:16:16.645670Z", + "iopub.status.busy": "2026-04-19T17:16:16.645544Z", + "iopub.status.idle": "2026-04-19T17:16:16.658347Z", + "shell.execute_reply": "2026-04-19T17:16:16.657960Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "max diff reloaded vs original: 0.00e+00\n" + ] + } + ], + "source": [ + "rgr2 = ConservativeRegridder.from_netcdf(path)\n", + "\n", + "# Apply to a new structured field of the same shape:\n", + "new_src = xr.DataArray(\n", + " np.cos(np.deg2rad(Lo)) * np.sin(np.deg2rad(La) * 2),\n", + " dims=(\"latitude\", \"longitude\"),\n", + " coords=src.coords,\n", + ")\n", + "out = rgr2.regrid(xr.DataArray(new_src.values.ravel(), dims=(\"src_cell\",)))\n", + "\n", + "# Sanity check: reloaded regridder produces identical output to the original.\n", + "reference = rgr.regrid(xr.DataArray(new_src.values.ravel(), dims=(\"src_cell\",)))\n", + "print(f\"max diff reloaded vs original: {float(np.abs(out.values - reference.values).max()):.2e}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 0b17b2ab74630e9451bd77ad364bdcb59217be56 Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Sun, 19 Apr 2026 20:47:41 -0500 Subject: [PATCH 11/16] nbstrip the demo notebooks Clears cell outputs and execution counts so the PR diff shows only the narrative + code. Reviewers (and readthedocs, if it rebuilds on merge) run the cells themselves. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../demo_conservative_polygon_basics.ipynb | 1375 +---------------- ...emo_conservative_polygon_curvilinear.ipynb | 771 +-------- ...mo_conservative_polygon_unstructured.ipynb | 874 +---------- 3 files changed, 98 insertions(+), 2922 deletions(-) diff --git a/docs/notebooks/demos/demo_conservative_polygon_basics.ipynb b/docs/notebooks/demos/demo_conservative_polygon_basics.ipynb index 723535f..6c9a564 100644 --- a/docs/notebooks/demos/demo_conservative_polygon_basics.ipynb +++ b/docs/notebooks/demos/demo_conservative_polygon_basics.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "fc5d33c5", + "id": "0", "metadata": {}, "source": [ "# Polygon conservative regridder — basics\n", @@ -18,16 +18,9 @@ }, { "cell_type": "code", - "execution_count": 1, - "id": "a029f393", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:06.682967Z", - "iopub.status.busy": "2026-04-19T17:16:06.682818Z", - "iopub.status.idle": "2026-04-19T17:16:07.878722Z", - "shell.execute_reply": "2026-04-19T17:16:07.878180Z" - } - }, + "execution_count": null, + "id": "1", + "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", @@ -42,7 +35,7 @@ }, { "cell_type": "markdown", - "id": "d132ca7a", + "id": "2", "metadata": {}, "source": [ "## Synthetic source field\n", @@ -53,619 +46,10 @@ }, { "cell_type": "code", - "execution_count": 2, - "id": "29dadd31", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:07.880467Z", - "iopub.status.busy": "2026-04-19T17:16:07.880202Z", - "iopub.status.idle": "2026-04-19T17:16:07.889917Z", - "shell.execute_reply": "2026-04-19T17:16:07.889481Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.DataArray 'cos2_lat' (latitude: 180, longitude: 360)> Size: 518kB\n",
-       "array([[0.00008, 0.00008, 0.00008, ..., 0.00008, 0.00008, 0.00008],\n",
-       "       [0.00069, 0.00069, 0.00069, ..., 0.00069, 0.00069, 0.00069],\n",
-       "       [0.0019 , 0.0019 , 0.0019 , ..., 0.0019 , 0.0019 , 0.0019 ],\n",
-       "       ...,\n",
-       "       [0.0019 , 0.0019 , 0.0019 , ..., 0.0019 , 0.0019 , 0.0019 ],\n",
-       "       [0.00069, 0.00069, 0.00069, ..., 0.00069, 0.00069, 0.00069],\n",
-       "       [0.00008, 0.00008, 0.00008, ..., 0.00008, 0.00008, 0.00008]],\n",
-       "      shape=(180, 360))\n",
-       "Coordinates:\n",
-       "  * latitude   (latitude) float64 1kB -89.5 -88.5 -87.5 -86.5 ... 87.5 88.5 89.5\n",
-       "  * longitude  (longitude) float64 3kB -179.5 -178.5 -177.5 ... 178.5 179.5
" - ], - "text/plain": [ - " Size: 518kB\n", - "array([[0.00008, 0.00008, 0.00008, ..., 0.00008, 0.00008, 0.00008],\n", - " [0.00069, 0.00069, 0.00069, ..., 0.00069, 0.00069, 0.00069],\n", - " [0.0019 , 0.0019 , 0.0019 , ..., 0.0019 , 0.0019 , 0.0019 ],\n", - " ...,\n", - " [0.0019 , 0.0019 , 0.0019 , ..., 0.0019 , 0.0019 , 0.0019 ],\n", - " [0.00069, 0.00069, 0.00069, ..., 0.00069, 0.00069, 0.00069],\n", - " [0.00008, 0.00008, 0.00008, ..., 0.00008, 0.00008, 0.00008]],\n", - " shape=(180, 360))\n", - "Coordinates:\n", - " * latitude (latitude) float64 1kB -89.5 -88.5 -87.5 -86.5 ... 87.5 88.5 89.5\n", - " * longitude (longitude) float64 3kB -179.5 -178.5 -177.5 ... 178.5 179.5" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], "source": [ "lat = np.linspace(-89.5, 89.5, 180)\n", "lon = np.linspace(-179.5, 179.5, 360)\n", @@ -681,28 +65,10 @@ }, { "cell_type": "code", - "execution_count": 3, - "id": "1baab8a3", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:07.890885Z", - "iopub.status.busy": "2026-04-19T17:16:07.890824Z", - "iopub.status.idle": "2026-04-19T17:16:07.992951Z", - "shell.execute_reply": "2026-04-19T17:16:07.992526Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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v359ueeee6RAw7pO3Hz00UelePHiAa0OszJ16lSJdH+VwUTAUQEAABACO+Yap5TB+OjouYbyrl27mrmTV111lTRo0EBefvllc1vnVBZYWN+6daukpqaa77ds2RJwUSQn0np1G55/AgAA5Iktk43DRta3bdtmuhGq1157TRo2bCjr1683F0269957Czaspy99Wbt2ba6fCAAAAAiQi5F1iYCwrgPb2sZRffDBB+bCSEq7Je7fv7/wata16ftTTz0lJUuWDFiv/czvu+++PF1K1W48lmUWAAAAJ7BlrnHYyHqDBg1MZ8SOHTvKqlWrTAm5+vXXX+Wcc84pvLC+YMECmTRpUqawfvr0aVm4cKEjwjo16wAAwEnsWLPutLA+efJk0xVmypQpprd6kyZNzPq3337bXx5ToGH9+PHjpk+kLidOnJAiRYoEXBRp2bJlUrFixTy9EAAAAEQXp00wvfzyy+XIkSMmM6dvca6TTosVK1bwYb1MmTJmYqku559/fqb7df24cePECdLEK39NpwUAAHBGtkHB04uGpqWlybp168ztunXrmguH5lWuwrpOMtVR9bZt28qSJUukXLlyAVcwrVGjhlSpUkWcINWyzAIAAOAEtsw1DiuDOfn3/E0tC/d6vf7w3q1bN5kxY0aeRtdzFdYvu+wy/5VMk5KSxO12i1N5xDILAACAE9gx1zitDGbQoEHy0UcfyTvvvCMXX3yxWacj7Pfff78MHjxYnn322cKZYKoj6OrUqVOyd+9eSUlJCbi/cePGeXlYAAAARJsICOGh0sqT119/3dSu+1x77bVStGhRue222wovrB8+fFh69uwp77//fpb362TTSJdq/bUAAAA4gS1zjcPKYE6dOiWVKlXKtF4bsOh9eZGnsD5gwAA5duyYbNy40Zw5vPnmm3Lw4EEZP368PPnkk+IEyZZb4iznlvkAAIDokmzDsOu0MpjWrVvLmDFjTM26r2uitjbXBix6X17kKY1++OGHMnXqVGnRooWpW9eymLvuuksef/xxmThxYp5eCAAAAKKMlcslD2bOnGm6sWh4btWqlWzatCmkn3v11VdNp8NOnTqF/FzTp0+X9evXS7Vq1eTKK680i87z1HV6QdFCG1nXma6+furaQ1LLYrSVY6NGjWTLli3iBKmW2ywAAABOkBqFI+uLFi0ykz71qqIa1DVMd+jQQXbu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9ZNzzjlHSpQoITfffLMcPHhQotXHH38s119/vbngje6rpUuXZrogzujRo6Vy5cpStGhRadeunfzwww8B2/z+++/SpUsX00Nc/43s1auX/PnnnxLt+65Hjx6ZjsWrr746YJto3XcoeIT1CKRX+7r11lulT58+2W43b9482b9/v3/p1KmT/77du3ebQHrFFVfIl19+KQMGDJB//etfsmLFConmfefxeMx+0e0+/fRTWbBggQni+h+4aN93wTzyyCMBx9l9990XcJXJ9u3bS40aNWTz5s0yZcoUE8Cef/55iXaLFi2SQYMGmQ5OW7ZskSZNmkiHDh3k0KFD4X5pttKgQYOA42vdunX++wYOHCjvvPOOLF68WD766CNz9embbrpJotXJkyfNcaQngVnRE+Wnn35aZs+eLRs3bpTixYubY05Penw0bH777beyatUqeffdd02IveeeeyTa953ScJ7+WPzvf/8bcH+07jsUAgsRa968eVbp0qWzvE9/tW+++WbQnx06dKjVoEGDgHW333671aFDByua992yZcsst9ttHThwwL/u2WeftUqVKmUlJyeb29G+79KrUaOGNW3atKD3z5o1yypbtqx/36lhw4ZZdevWtaJdy5YtrX79+vlvezweq0qVKtbEiRPD+rrsZMyYMVaTJk2yvO/YsWNWXFyctXjxYv+67du3m3/7NmzYYEW7jP8N8Hq9VmJiojVlypSAfZiQkGD997//Nbe/++4783Off/65f5v333/fcrlc1i+//GJFi6z++9m9e3frhhtuCPoz7DsUJEbWHUw/Hi5fvry0bNlS5s6daz4C9dmwYYP5CDQ9HWHR9dFM33+jRo2kUqVKAftFR4h1xMS3Dfvu/9OyFy1DaNasmRk5T18ypPvq0ksvlfj4+ID9uXPnTjl69KhEK/3kRj9pSH8cud1uczva/wYz0jINLU2oXbu2GbnUMjWl+y81NTVgH2qJTPXq1dmHWdBPBA8cOBCwv/Sy81p+5Tvm9KuWb7Ro0cK/jW6vx6aOxEc7LX/U0si6deuaT2d/++03/33sOxSk2AJ9dIS1NKFt27am7nrlypXSt29fUzt3//33m/v1H+30gVTpbQ2lp0+fNvWM0SjYfvHdl9020bjv9HjS2v1y5cqZsqERI0aYj4enTp3q31e1atUKuj/Lli0r0ejIkSOm5Cqr42jHjh1h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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], "source": [ "src.plot(figsize=(8, 3.5), cmap=\"viridis\")\n", "plt.title(\"source: cos²(lat) on a 1° grid\")\n", @@ -711,7 +77,7 @@ }, { "cell_type": "markdown", - "id": "72b25970", + "id": "5", "metadata": {}, "source": [ "## Target grid\n", @@ -722,16 +88,9 @@ }, { "cell_type": "code", - "execution_count": 4, - "id": "856e2175", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:07.994192Z", - "iopub.status.busy": "2026-04-19T17:16:07.994113Z", - "iopub.status.idle": "2026-04-19T17:16:07.996120Z", - "shell.execute_reply": "2026-04-19T17:16:07.995737Z" - } - }, + "execution_count": null, + "id": "6", + "metadata": {}, "outputs": [], "source": [ "target = xr.Dataset(coords={\n", @@ -742,7 +101,7 @@ }, { "cell_type": "markdown", - "id": "16e60842", + "id": "7", "metadata": {}, "source": [ "## Regrid via the `.regrid.conservative_polygon` accessor\n", @@ -754,620 +113,10 @@ }, { "cell_type": "code", - "execution_count": 5, - "id": "165feb10", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:07.997093Z", - "iopub.status.busy": "2026-04-19T17:16:07.997028Z", - "iopub.status.idle": "2026-04-19T17:16:08.767277Z", - "shell.execute_reply": "2026-04-19T17:16:08.766968Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.DataArray 'cos2_lat' (latitude: 60, longitude: 120)> Size: 58kB\n",
-       "array([[0.00129, 0.00129, 0.00129, ..., 0.00129, 0.00129, 0.00129],\n",
-       "       [0.00676, 0.00676, 0.00676, ..., 0.00676, 0.00676, 0.00676],\n",
-       "       [0.01763, 0.01763, 0.01763, ..., 0.01763, 0.01763, 0.01763],\n",
-       "       ...,\n",
-       "       [0.01763, 0.01763, 0.01763, ..., 0.01763, 0.01763, 0.01763],\n",
-       "       [0.00676, 0.00676, 0.00676, ..., 0.00676, 0.00676, 0.00676],\n",
-       "       [0.00129, 0.00129, 0.00129, ..., 0.00129, 0.00129, 0.00129]],\n",
-       "      shape=(60, 120))\n",
-       "Coordinates:\n",
-       "  * latitude   (latitude) float64 480B -88.5 -85.5 -82.5 ... 82.5 85.5 88.5\n",
-       "  * longitude  (longitude) float64 960B -178.5 -175.5 -172.5 ... 175.5 178.5
" - ], - "text/plain": [ - " Size: 58kB\n", - "array([[0.00129, 0.00129, 0.00129, ..., 0.00129, 0.00129, 0.00129],\n", - " [0.00676, 0.00676, 0.00676, ..., 0.00676, 0.00676, 0.00676],\n", - " [0.01763, 0.01763, 0.01763, ..., 0.01763, 0.01763, 0.01763],\n", - " ...,\n", - " [0.01763, 0.01763, 0.01763, ..., 0.01763, 0.01763, 0.01763],\n", - " [0.00676, 0.00676, 0.00676, ..., 0.00676, 0.00676, 0.00676],\n", - " [0.00129, 0.00129, 0.00129, ..., 0.00129, 0.00129, 0.00129]],\n", - " shape=(60, 120))\n", - "Coordinates:\n", - " * latitude (latitude) float64 480B -88.5 -85.5 -82.5 ... 82.5 85.5 88.5\n", - " * longitude (longitude) float64 960B -178.5 -175.5 -172.5 ... 175.5 178.5" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], "source": [ "regridded = src.regrid.conservative_polygon(\n", " target, x_coord=\"longitude\", y_coord=\"latitude\", spherical=True\n", @@ -1377,28 +126,10 @@ }, { "cell_type": "code", - "execution_count": 6, - "id": "e3d5b7cf", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:08.768572Z", - "iopub.status.busy": "2026-04-19T17:16:08.768502Z", - "iopub.status.idle": "2026-04-19T17:16:08.830003Z", - "shell.execute_reply": "2026-04-19T17:16:08.829608Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], "source": [ "regridded.plot(figsize=(8, 3.5), cmap=\"viridis\")\n", "plt.title(\"regridded: 3° target\")\n", @@ -1407,7 +138,7 @@ }, { "cell_type": "markdown", - "id": "8b9883a8", + "id": "10", "metadata": {}, "source": [ "## Mass-conservation diagnostic\n", @@ -1418,27 +149,10 @@ }, { "cell_type": "code", - "execution_count": 7, - "id": "ed4d2118", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:08.831189Z", - "iopub.status.busy": "2026-04-19T17:16:08.831122Z", - "iopub.status.idle": "2026-04-19T17:16:08.834716Z", - "shell.execute_reply": "2026-04-19T17:16:08.834417Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "true sphere integral : 8.377580\n", - "source integral : 8.377474 (err -1.06e-04)\n", - "regridded integral : 8.377474 (err -1.06e-04)\n" - ] - } - ], + "execution_count": null, + "id": "11", + "metadata": {}, + "outputs": [], "source": [ "def sph_integral(da):\n", " lat_r = np.deg2rad(da.latitude.values)\n", @@ -1457,7 +171,7 @@ }, { "cell_type": "markdown", - "id": "736014c4", + "id": "12", "metadata": {}, "source": [ "## Spherical vs planar vs the axis-factored path\n", @@ -1470,27 +184,10 @@ }, { "cell_type": "code", - "execution_count": 8, - "id": "d726eddc", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:08.835715Z", - "iopub.status.busy": "2026-04-19T17:16:08.835658Z", - "iopub.status.idle": "2026-04-19T17:16:09.149947Z", - "shell.execute_reply": "2026-04-19T17:16:09.149528Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "polygon, spherical=True : |error| = 1.06e-04\n", - "polygon, spherical=False: |error| = 1.81e-03\n", - "factored (.conservative): |error| = 1.06e-04\n" - ] - } - ], + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], "source": [ "def err(fn):\n", " return abs(sph_integral(fn()) - true_val)\n", diff --git a/docs/notebooks/demos/demo_conservative_polygon_curvilinear.ipynb b/docs/notebooks/demos/demo_conservative_polygon_curvilinear.ipynb index fec1465..3ae6e5f 100644 --- a/docs/notebooks/demos/demo_conservative_polygon_curvilinear.ipynb +++ b/docs/notebooks/demos/demo_conservative_polygon_curvilinear.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "045f1332", + "id": "0", "metadata": {}, "source": [ "# Polygon conservative regridder — curvilinear target\n", @@ -17,16 +17,9 @@ }, { "cell_type": "code", - "execution_count": 1, - "id": "5a289dbc", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:10.278657Z", - "iopub.status.busy": "2026-04-19T17:16:10.278490Z", - "iopub.status.idle": "2026-04-19T17:16:11.408757Z", - "shell.execute_reply": "2026-04-19T17:16:11.408359Z" - } - }, + "execution_count": null, + "id": "1", + "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", @@ -39,7 +32,7 @@ }, { "cell_type": "markdown", - "id": "52bf6b52", + "id": "2", "metadata": {}, "source": [ "## Source — regular 1° lat/lon\n", @@ -49,28 +42,10 @@ }, { "cell_type": "code", - "execution_count": 2, - "id": "7b2b6775", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:11.410414Z", - "iopub.status.busy": "2026-04-19T17:16:11.410209Z", - "iopub.status.idle": "2026-04-19T17:16:11.500074Z", - "shell.execute_reply": "2026-04-19T17:16:11.499649Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAv8AAAFUCAYAAACzyFOSAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAlKtJREFUeJztnQe4FNX5xr/dvY0iTbqggBjUKEIgIFijBCxJRI2KEkFiMBoxCiolhqZGrNiCYkWNGo01FoIa0dgQDdhQ4S82iDQbIPXeuzv/5zs7Z/acmbOzs+3u7t339zzD7p45M3NmZpd75j3feb+QZVkWAQAAAAAAABo94UI3AAAAAAAAANAwoPMPAAAAAABAmYDOPwAAAAAAAGUCOv8AAAAAAACUCej8AwAAAAAAUCag8w8AAAAAAECZgM4/AAAAAAAAZQI6/wAAAAAAAJQJ6PwDAAAAAABQJqDzDwAoW+655x4KhUL0xRdfFPU+iwU+Jz63a6+9lhoTW7Zsod/97nfUsWNHcX4XXHCBc658P9Pl5ZdfFtvyayoOP/xwsQAAQEOBzj8AAGTAFVdcQU8++WRer90bb7xBM2bMoI0bN+b1OOUO30vu5J9zzjn0t7/9jU4//fRCNwkAAPJGRf52DQAAjbvD+Otf/5qGDx+ulXPHccSIEVRdXZ2Tzv/MmTPpjDPOoFatWmW9P2Bm4cKFdOCBB9L06dOdMsuyaPv27VRZWYnLBgBoVED5B6DMqa+vp9ra2kI3o9EQiUSopqZGhH2A0mDDhg2ehyu+f3wf+X4CAEBjAp1/AArEDz/8IGKLu3XrJlTi9u3b089//nNaunSpVu+RRx6hfv36UZMmTaht27b0m9/8hr766qtAccOsGPP+TTHbN9xwA+25557i2B999JFYv3z5cjr55JOpXbt24ni9evWiSy65RNsnH/u3v/0tdejQQWz74x//mO6++27PsVetWiX2lwp+8Jg2bZo4x5YtW1KzZs3okEMOoZdeekmrp7b99ttvd9r+05/+lN5++22t7vvvvy/OvUePHqIDx7Hc3OZvv/3Wty2jR48W17iurs6zbujQoeJ6MNyOrVu30r333ive88LH84v5/9e//kWHHXYY7bLLLtSiRQvR7gcffDBpWzjc5+KLLxbvu3fv7hyH93vCCSfQT37yE63+L3/5S7H+qaeecsoWL14syvjYks8++4xOOukkatOmDTVt2lQo3s8++yyly/XXX0977LGH+J7weS1btizr7+ScOXPEPeN28fVevXq1UOAvu+wy6tKlizjWcccdR9999522T97fL37xC3r++eepT58+4p7vu+++9PjjjweKzf/888/FNVCvcbKYf/5O84gPXz8+Tv/+/bVr7of83vJ5DBgwgF599dVA2wEAQC5B2A8ABeLss8+mRx99lMaNGyc6Ktwxfe211+jjjz92Onbc8RgzZozoKM6aNYvWr19PN954I73++uv0zjvvZBwKMm/ePNqxYwedddZZogPNHRnuMHOnm8McuJw7VJ9++ik9/fTT9Je//EVsx8fnziJ3irjd/JDAHcszzzyTNm/eLB5mJKNGjaL//Oc/ovPmB29355130qmnnkpjx44VD0V33XUXDRs2jN566y3RmVPhDjPX+f3vfy/acfXVV4vOMHdqZYjGCy+8ID7zteOO/4cffig6Xvz65ptvJlXlOWTnvvvuo+eee050JiXr1q0ToSEyLITjwnmCKHfg+Fox3KlLBt9HfvjgB6UpU6aI+8b3b8GCBXTaaacZt+Fz+r//+z/6+9//Ljra/FDC8DXn+/TPf/5TXDt+kOBrzN+JcDgsOpS/+tWvRF1+z2UHHXSQc/8GDx5M27Ztoz/+8Y+06667igcYrs/fxeOPP56CwNeI78G5554rvkf8nTziiCPogw8+EA+FmfDAAw+IB8HzzjtPdO75vvKDKO+XO+mTJk2ilStX0s0330wXXXSR54Hzk08+oVNOOUX8rvghjr/j/JDD15gfqk3ss88+4l6OHz9ePFxceOGFzjX++uuvPfX5+8PXcrfddqPJkyeLB9V//OMfIvTrscce871+/J3m7yxff/6d8PeTrzv/9rp27ZrRNQMAgIywAAAFoWXLlta5556bdH1tba3Vvn17a7/99rO2b9/ulD/zzDPcm7amTZvmlB122GFicTN69Ghrjz32cD5//vnnYtsWLVpYGzZs0Ooeeuih1i677GJ9+eWXWnksFnPen3nmmVanTp2sb775RqszYsQIcT7btm3T2hTkv5j6+npr586dWtn3339vdejQwfrtb3/rafuuu+5qfffdd075P//5T1H+9NNPO2VqOyR///vfRb1XXnnFKZs3b54o430z0WjU6tKli3XKKado286ePdsKhULWZ5995pQ1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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], "source": [ "lat = np.linspace(-60, 60, 121)\n", "lon = np.linspace(-120, 120, 241)\n", @@ -92,7 +67,7 @@ }, { "cell_type": "markdown", - "id": "cd1d44bc", + "id": "4", "metadata": {}, "source": [ "## Target — rotated curvilinear grid\n", @@ -103,28 +78,10 @@ }, { "cell_type": "code", - "execution_count": 3, - "id": "8578ef35", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:11.501193Z", - "iopub.status.busy": "2026-04-19T17:16:11.501128Z", - "iopub.status.idle": "2026-04-19T17:16:11.551329Z", - "shell.execute_reply": "2026-04-19T17:16:11.550877Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAdkAAAGJCAYAAADGyUn1AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAA/KpJREFUeJzs3QeYbWV1N/BjBTtSBEVFjYJoTOxKLDGxfYZYklgxKCIi2Oi9CSpdsIMoTUEENMbeEQuggh17wy5qVLDX8z2/HddkzZp3z70Xb5k78/6fZ7jMnHP2Xvvd+7yr/9fVptPpdNLR0dHR0dGx0nH1lX/Ijo6Ojo6Ojq5kOzo6Ojo6ViG6J9vR0dHR0bGK0JVsR0dHR0fHKkJXsh0dHR0dHasIXcl2dHR0dHSsInQl29HR0dHRsYrQlWxHR0dHR8cqQleyHR0dHR0dqwhdyXas9bja1a42ee5znzvz+2mnnTb87bLLLpv52wMe8IDhp2Ph4uijj57c/va3n/z5z39eLef73e9+N1ks8Px75lcl3vWud02uf/3rT3784x+v0vMsNnQl29GxwPH9739/2EQ//elPTxarvFdeeeXkqKOOmuyzzz6Tq1/9/7al3XbbbXLXu951sv7660+ue93rTrbccsvh2L/85S+bStPnb3azm02uc53rTO51r3tN3vve98553//8z/9MHvrQhw7Hu/Wtbz05//zzJ6sDr3vd6yYvetGLrvLnf/3rXw/Xvrrkrfh//+//TW5729tOjjjiiDVy/rUWuIs7OtZm/OY3v5n+4Q9/mPn91FNPxcc9/eY3vznzt9/97nfDz9qIiy++eLge17VY5T3++OOnN7zhDYd7mXGf+9xn+pznPGf6kpe8ZHrSSSdNd9555+k666wz/P1Pf/rTrPc+/vGPn17zmtec7rnnntNXvvKV06222mr4/cMf/vCs9+2www7T+9///tM3velN0wMOOGC60UYbTX/5y19OVzW23nrr6WabbXaVP//jH/94WNdDDjlkzmue/7p2qwKveMUrpte97nWnV1555So/12LBNde0ku/oyDCv4re//e3giSwv1l133WW+59rXvvaCXuhf/epXk+td73qL/pxjOPXUUyePeMQj5tzLj3zkI3Pe+zd/8zeTPffcc/Lxj398cu9733v4m/9//etfPznmmGOG1+BJT3rS5G//9m8ne++99+TCCy+c+fxFF100OfPMMyd///d/P3nUox41efvb3z750pe+NLnb3e621q7fNa95zeFnVeM//uM/Js9+9rMn55577mT77bdf5edbFFjTWr5j7cN3v/vd6fbbbz+96U1vOr32ta89vdWtbjXdaaedZjxFlnbr0Wp5mCx7Fv673vWu6d3udrfBS+HV3PGOd5w+4AEPmHMM3svNbnaz6X/8x3/M/K1a963z/OM//uPwE/jABz4wvOfss8+ePv/5z59uuummw7n/+Z//efrVr351znk/+tGPTh/60IcO3tZ1rnOdwRP6yEc+Mus9l1122eBpbb755tN11113uv76608f/ehHz5Ijy3f++ecP7+dJrbfees21DjnrT3iJH/rQh4Zz3OIWtxjuxc1vfvPprrvuOv31r3896zhPfvKTp9e73vWmX/va16YPe9jDpte//vWnj3zkI4fXvPfZz372dIMNNhj+/vCHP3y4xy2vyd+f8pSnTG9yk5sM57vDHe4wPfnkk5db3ha+8Y1vDO857bTTpsuDN7zhDcP73/nOd878ba+99ppe4xrXmF5xxRWz3nv44YcP7/32t7898zfX5/m1Fuecc85wzbzE+bCse/byl798WAtr4nvxjGc8Y/qzn/1s5nXPXl2T8Gp9bw466KDpXe961+H54ine9773nZ533nkzn/cMtdY17k/rO8e7Peyww6a3uc1tBrmcb7/99pv+9re/nfW++A7y+O9xj3sM34Nb3/rW09NPP725Fne5y12mj3jEI+Zdr47/Q/dkO1Y433bPe95z8vOf/3yy4447DoUq3/ve9yZveMMbhpzRVfEYv/zlL0+e8IQnTJ7+9KdPnva0p0222GKLyeMe97gh//TDH/5wsskmm8zybMjw+Mc/fqXcuSOPPHLIAfJ+rrjiiqH45olPfOLkYx/72Mx7zjvvvMnDHvawwdM55JBDhvfzvP75n/958uEPf3hYD7j44osHj4lsN7/5zYfCqxNOOGEouPrCF74w5AAznvGMZ0w22mijycEHHzx4RS3IQR522GHDe6z3/e53v+Hv//AP/zD8y6Ow7jvvvPNkgw02GDy6l770pZPvfve7w2sZf/zjH4dc5H3ve9/JscceOyPPdtttNznnnHMm22677eAZfvCDH5xsvfXWc2S5/PLLh9cV2DzrWc8aZH/nO985eepTnzrkVHfddddlyttCeJlyry2Q2/P2+9//fnLppZdODjzwwMkNbnCDmXWHT33qU5PNN998csMb3nDWZ+M98sO3uMUthv8//PDDh3U45ZRTBu9PnnTDDTecLA9a98xzeuihh04e9KAHDffB8+y+ex4uuOCCybWuda3JAQccMDxf7svxxx8/fE4REVi7V7/61cN3wPP/i1/8YnLyyScPMrqfd77znYdzOqbj/9u//dvk3//934fP/t3f/d2orDvssMPk9NNPnzz60Y+e7LHHHsMzLZ/6xS9+cfKmN71p1nu/9rWvDe9zL5/85CcPa+O58Mzf8Y53nPVef/vv//7v5Vqvju7JdqwgnvSkJ02vfvWrD3m3ij//+c9XyZP1N55sxpe//OXh7y996Utn/Z2HwPPIntpf48luueWWs3K1L37xi4e/f+5zn5u5ptvd7naDFxvXB87P2n/wgx88628VF1100XC817zmNXPk46388Y9/nP41Oc7WOY844ojp1a52tem3vvWtWZ6sY+y7776z3vuJT3xi+DvvN2O77babs65PfepTBy/tJz/5yZxc6I1udKMZWVY0J3vggQcO7//FL37RfD3WMH622GKL4f5liHyIQlR8/vOfHz5z4oknzvr7r371q+nHPvax6Q9+8IPlknHsnv3oRz8avMSHPOQhs3LEL3vZy4b3n3LKKcvMyTperRfgBW+88caDx708Odn6nfv0pz89/C7/nCFf7e/ZS47voKhIvi4e7R577DHnXBEduPzyy+dZsY5Ary7uWG5orWDBPvzhD5/c/e53n/P6VW0hUOHJas/glbDgzz777Jm//elPfxo8ZudfkZztfHjKU54yy/sOz+sb3/jGjAf01a9+dbLNNtsMVak/+clPhh9ezAMf+MDJhz70oZmWkyzTH/7wh+H9qjHXW2+9ySc/+ck55+a1XOMa1/ir5M/nJBPZeI1sD95dBU+otmWEh5Yh75bheG984xuHtff/sQ5+3DteWusalwfWiUcZnl3FHe5wh6FK2LMnvyoPWquLf/Ob30zWWWedOZ+NHK/XM3jxvNwcJVke1Hv2vve9b/CwefG5Ktr7eNXyvcuC48Uz6Fn66U9/OnjvvmNXdU3f8Y53DP/uvvvus/7Oo4UqlzWOZx94ziJK8T3IuPGNbzz86953LBs9XNyx3NAfJ7SlmGRlgpJtQch4//33H8LR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Zl7/zW9+M33GM54xXX/99afXu971pv/+7/8+/eEPf7hC57jiiiuG4/q3o2Op4lvf+tb0JS95yfRDH/rQ9M9//vP05z//+fSkk06a9R7fkVNOOWV67rnnTi+88MLpBz7wgTnH+f3vfz/97//+7+luu+02/fKXv9w81x/+8IfpAQccMD3++OOnX/nKV+a8/r3vfW965plnTi+77LJBpo985CODTIF3v/vd08985jPD/5OTTG94wxumv/vd74a/veIVr5j+8pe/nCPTq171qul3v/vd6ctf/vI553T8j370o9NDDz10euqpp46u04knnjg9/PDDpxdccMEsmeIYL3rRi6Y//vGPpyeffPL0jW9843DuwE9+8pNB1pDpTW960/TVr3719Kc//enwN9dpXfPxLrroouGYX/va12a9N+Mb3/jGsJb777//9E9/+lNT7s997nPTvffee/pf//Vfs2QKvOUtbxne43Wy/+xnP5v1+ktf+tLpr3/960EmMr74xS8ezgu/+MUvpieccMJ0bcaK6IFFNyCA1SnEE8j5DtyirDI5IlYIlhLWp1BIR0fHssG7E4KUbxSGDVJ53ydVq3Ksejxbk3T0iCr2iXxcJroX7lWIVMO6MbXG3xUkxXSbjKALVGSTZQo4No+T96roqE7SUZilqpa32pqkoy9VfjgKgchkDVTUHnTQQUMhUp2fGpN0rAu5hJyrx8mjI7se1L322mtOcRgZhZp5gDEIPU/SERUwk1aBlLW2TmSMIQWOF21SwOsKmeSSrZm9L7fVxKg+0QLerdeqB8/TJRtvl9yt6UY8Vd70H/7wh0G+PAvYNfP+tWxlUotFi+ki82T//u//vvkaC/Za17rWYFUHvvjFLw7WCOtvedE92Y6liD/+8Y/Tt73tbdPdd999Xo/zoIMOGrwk362K73znO9Ozzjpr+Jenw7PNntTb3/726aWXXjrj7Zx22mnT17/+9dPf/va3w994lL/61a9mne+tb33r9JWvfOX029/+dtPjhEsuuWTYG5blcR555JHT888/f47HSUbeIa+QTGefffaMTMAT9fcsE6+eJwof/OAHpx//+MdnHfPiiy8ejskz5zFXTzCiBd6z3377jXqcn/3sZ4fXzznnnFkyBXi/7sWb3/zmWTKB6+T5+5z/J2PIFHudta0y+cyHP/zhYV3PO++8OeeMCAZP+dvf/nZTblHFffbZZ1ivtRFL2pNVSadEnbXMetSQrsKN1cyqykwtLFSvKY5gYbegMCCPkmKhdnQsJQQPrXYWhULycdXj1DuqspSHwgOqk2RAbpBHqJipEt2DvCjPTPWqXGAmug/vKDzOSnQPPCqVtjE/VnUxmZDbH3zwwYPHGUT5gejBVUHM42y1l/DceJRHHXXUULBTc4mKneRWee8qnl1H7mPlDbouni9KRTKpLA6P0+flSLUbZZmsjxYje5ecbvSnQh6Lp0AKhWH14IH3rzdWFE+xUoZza3tCMmGPyzIBz9xeqkdXgRmZXGeOFsiH2zvJWkf1ec8ZpU0qRzCQa8jF1zapxYZFpWQl51U5agJ3o1W4ecA1n6sUVExRiwAURnhtDJS043R0LDUED62wqc03qkVt3DZeijSI7vVKxgg6Gy8DN5RonqSjAElbTVWw4HsqHK1gKFcTB2z4FJBB5cKidZKOsLAQJiUvdEqm6JONsDBlphCpNUlHRS8FkHtWs4HhMy1FRplbK4rfJJ1KAkFpUYL2ETzEtYVFsZR9iYPgp07SsZauS/uNPSwMjFDmuT81wEihzIVjGTSUezV8rKVrxo4lddYad6d6Wc8vA6YaPWH4OI89F6NTNTBudatbDffVdYdMWZlrk/IMBff0YsSiZnzyhWVZ66XzRbMJ1AHHKuawwrBSl9eT1YTdq4s7Fiv0ZlKUNn5eYyW657HIr2rLkSusRPc2Uxuu/tZKdJ/7U+P3THRPGWlPQWafN94gundu3z2bdAUlqJKYAaDPszVJB1EDRUGuLFNUC1Nm2moo+kp0H/2p9hHIBgZlQ0GrAclRsTxJRyQN01OLKJ+H77pdVytPyUCh7BFOtCbpkIsSZYiQSR6Vl2rfq21S1cDwntomFQaGCmiKkvdf26TCwGD0iAA4X2ue7vHHHz+sOQPFOmUDI9qkrMvahM5d/BfYJHxJtBCwyJSRZ25OYOG1vowBD0bLeu3oWGzIRPc2Q6G8qmBBcRPGJt5bbY0BCpQiVNBDYQQ3bsB3kZK2eVMOzqsfNM4VHLw23iC69x0O4n2bMgWQif/zJB1h35ZnZEOnPH1Wa071OCkIyos3at/IMuVCJIYFeeokHWQPlDRPmJLLBga2ptyfmmUKA4NhYX+qYGAoxkJjSAm3PE4epCIsXnw1eiIsrFiLInaP3QMFZwGOA2pHofUI/VuDiBbga+ZRx3pkA8N5edItBfve97532Hcpcu1IFeR0T3KYf7FhUYWLK4RDfBH058WD76HXYwc2E5WCHp6OjqUMG7gwbhDdhzKNjTfz0IoOyc3yvCiArIh4WjZzYU2bK+VSYdNn6CLppzQq0b2N14YtJCo0WonuKVLKTNgxmIoobCFPm77P+xulFTLFPFnhYxXEjpu5krOBwdMU8mwZGMKm+IEZFzWX6LpUAJ911lmDEqLwsoHhuCp+7UmMhZikE8o8PE5yMeyzgUFx6mfNfM11ko50GUOg5TTY+yhqnnY1ekJJY5myLq6jGhgRFjaFh0zVwEBvqT/WEAJgYDDSHvjABw7PAGKOmg8PAyNCzYtVyS6q6uI99thjqA785je/OfSlPehBD5puuOGG0x/96EfD6zvttNP0lre85VARpzJuq622Gn5WBL26uGMx4etf//pQUaoiVgVxht+9pt9Rle/pp58+VP3W/lRQ/ep7pWpY9TC8613vGqpfM1S6OuYnP/nJoVcyHy/g++o4+jSrTIFPf/rT08MOO2z6mte8pnkM8toH3v/+98+SKfenRv/nF77whRmZan9qlom8KqD1Brc6EqyT6mkV2GNVtSp5VdW+7GUvG9avQq+vSuHPf/7zg0yus/anRuX25ZdfPsj0zne+c2adoj81y/S6171u+trXvnbm/RXu3Xvf+95hPVVOj+G4444ben6j57j1rLgmfcfufb53P/zhD4d7BSrEPTdkimpx9+kTn/jEdG3Bkq0ulguSB2LdygGgBVOVF8l6uQEWMk9WnpX1JgzS0bHUEET3wpM8l9ZUF98Vntdhhx02eG6V6J4HyttSnY/5SbiUNxkQJuVx8oSdJ4ju5Tx5VJE/FGmqRPe8UMcURsxE9bw9Hi5PSUhYGDR7RwHnxA+sGCjLBK5XH6x+ecxEwq/hBQNvUGWtPCgZg+g+JucEIxK5eOwxSUffJ4/PvpL7U+skHR6bc7Zm4fIeo28/yxQQ4tXzy+OUn66j+pzfGgrNtibp1P7UPElH2Nd+WKMTEcHQ/6woK7zVDFEL66qCW+9sDddvvPHGwz03V1avMpny9YtWWFPHrlzOazsWdeHTqkCnVexYm1GJ7m26yAf8mwtu8iQdyi5oCzMYs3JxinrkOFubo/OYNCM82pqkI7TKGKbQKNQ8SYfy0vpC2Th2Jbq3+ZMx8/tG+FgYk/LxU8fVhYHBONDaIh9Z4T2qgcldp/uAvKzcrsIp11dH9VF0FL1ca52kQ3HGGL+o98gGhtA4esUaFo5JOpSn8xrV1wJnwvEcJ1dKR55UPhyvMeOBsg+ZQJ5dmFfBG8OCTI4VY/HkWDktkQ+vo/rIXRV/GBjuHaNGqqEFDpE1rS0/CxGdu3iBLG5Hx0JCKM5KdB8bL2Xm+aakeHNBdK8QRr7Sxpt5aH0HKE48wTyVnFPLk3QoDnnelsfJq+LBUOLBjVs3Z7k+52pN0qHkXQulWonuyUBJO7fPVKJ7RUw86ehPreskhyrXSIFWUJwKpFQSU3bVwFBJ7LoocLnIamCoBaFUjI1rTdJxfp6jCm6oBkbma84yxeAE+c46kxYoTLl0hWt6fmN4QcaZZ5453CteK8WZjZAwfOR15WXrqL7ga458+E9+8pNBpjAw5F5jjF+WKQwMayWysdD31iXLXbw60HOyHWsbsAlhFZLjbPHQghoFjEdjfLdnnHHGwONbeWhzPi5ycPK83iNvKf8pHyefm+EczoX79j3vec9w/gqy4vPdZZdd5mUOwmtcZQp89atfHViPcPl6D77hzOjk+OQNmawT3mLnDkak4DgG/4/3WM6WTN5f4XN4hbFfZYa5CsxYRx111MC0VFmmrKXXsUlhagqZAu5F5MPleZ2HTJEjlKfFLZxlwtIkZ4u1qfI1B7A9HXvssQNDVpUp4L5ic8Ii1WJskpe27nLLp5566qy8ZbBn+Zzjy2+HTGP58LVdD/Rw8aq0YDo61iCCh5Z3ER5n7k+FPLWGJ6Hat1VVy6OUC+RltHh4hQvl96JFTiVsnqSjKpYXxuOMcWgxSSf3p/IIs0yO43vGS43+1IAQs7Avr0tottUlQB5hX96fPGXl4bVGvEIepPcKi+YWP+fgjft7a5KO8/M2g+c4T9LxI8eZw8IQrTzyljzKOpM25IoB6kLarUk6eoJ5rc5JJhXWAdEJa8rjFM6vk3Rqf2qdpCPvHWP8WpN0RAzcj+oJu3fC2SIMJgDdKskUULXOe3cM96yO6iOHfHcdPbiQ0D3ZBWLBdHSsKWRu3DGPM/PQ8ip+8IMfDBWfY5N0VIxGBW5GTK15znOeM+ORVKhyDV7j1iSdL33pS0Pl7NgkHV6RauHgITf5JSbp1GrhLBNvmYfr34qYWsMTdqwxkPnoo49uTtLhkTm3aEFM0smeL29XpXPIxHPP0QLVyiqJq0y8bt535WsOuKYXvvCF04MPPnhej/PAAw8cnaSj6th6V5myx+lZaU3SUW0dfM0B8vqM8+KlrnzNOYKhwpqX3oJqcXzMY9e1ENA92YViwXR0rGbIo/Ge5L9ww1aPU1Uuz4fnwmOVc8teFuYflZ7yn3WSTnicuepVz6k8J++Ht5XzcXWSDi+Vh8Kban2vFMR4ncdZCWDk7VDwyVMikKmTdOQH5W5VrbYm6ci9qlyVYw2Z5AF5aoq7eLN1gk9M0uGR87paBTk8ThW1KmbNdq2TdADVq5wkT79O0ol8OI/TvYtJOjF3Vz6cZ7jNNtvMmqQTc3e9VvPhOYKhXxf5Rc2H8zjlRxWe7bnnnjPrUqMXit6cUw61RjB4q3LS9kEyiUrwqCP/nfPhf/jDHwZvWgTDvfMsWf+cD48IhnPKA4t6LFQOg174tEAWt6NjdSFI5VHWCUOqFkbWkGFDpzgVq+y9995zKmZB+4YB655xrSiV75aioNBUl6pOFXrW/hGbb2y8jk0xCYti+6HMKSshZ2HhCNsGqTxlgm1JGLGGhQFlYYQRx6pqkSm4RqHhamDY5Bkfjk2mamCgGLS5K0SqRPeUuTApBiTECYFsYGjNqZzEoczJzagRtq5GD6hy9nnV3YqfqoGhEMn1qNSuBkYOC2u5qgaG96sIzrSFYfS4h4wXyjG3SWUDw/32rBhi0HrmXJN1EfqvBoZirUsvvXRoH3KN1cDIYWEhbWscBga4LoZNTjssFHQlu0AWt6NjVaNFdJ+rhcPjzET3NsMWX2xMreFRUJS5JSVvrPopbdDYf6pSiepVnhHPzOabSeVtvNHukSfpRBVtzcdlonuKxnXp2c0bL5kozjASWh6nNcE6xOO0LpXoPjxOOUSVviIBWaFSvLxdyixoBxkxoczJyUDQGgPVwMCSVNuksoHhGsjdyodT5KbZ4GPOMuUKbTLL72aZcj5cBbB9KyYOhTKv+fBqYHgW5Jhzm1Q2MLQ4WbNWdIIyP/rooweFr7+3GhhyvLzdoKvkBWcDI/LhwdC3kNC5izs6lgAoSp4C0ofMQ0uxorPjwQgjBtF9nqTjPRSDQqjgofUaT4/itPFSpNGakonuEckL61UFGyFGG7TwaEvZ8a6EDfVyCt/WSTo2WmFhYUSKzPnzWDyv83hsvC0Dw2co8jzcPYju9Y1Sdi3jmIGh8EmP6HOf+9w5PLzkYHQcfvjhgzdWvVbrSGlQmM5HwWQDQ1TAmooMWPs6SScmHkWbVDYwRAtQGOpbraCcKTxpgH333bfpcTo+QhFyO042MFwnGShW90wImYGRyUkoZWvMSKiTdMB1CbtHdCIbGAceeOBwv1sD6907z57ra9FvMrQYCNIb0gRrK3p18Qqie7IdaxrB+cprlD/NFaABHp0NzmbI46wekkrik08+eVC+lYc2lKV+UZtta5IOtiTei403y0S5CfsKzfI4czVvTNKhLGyyrXF2FCdPWJjT51s8vOSmrFT6UpzZO4ywMAVAcQXRfUzSif7UIKuvk3RcJ2WUw+TZwKDUeH2tUX2Oe/rppw+GQ/A9Z/A2cSPLlTp+naQT+XBGQJ2kU/Ph1cAAitIYv4wwMChfXnArOsHAwI7FUKPsqoHhOePFO4YIQp2k4zlh7DGAhL3rqL43lXw4A4NMES0Qnaj58FDmlKv/r8Qcaxq9uniBVJV1dKxMZB5aPY7RS5k5YlWFvu997xuqUvEGB19shYpWFZ6qdsegUtRP5sYN6M3U30iOykOb+1NDJr2wZPr+978/pz81oK/T9eDrrf2pARXQ3qMieIzXWLX0C17wgkGuVmWuqli9u+9+97uHflHHrNXCZAZr6PfoOdWf6h5UmVyba1QtnPtTA+SwRrvuuuu8VbX77rvvHJnyPXN860OmSy+9dNbrqpijnxiHMJk8C64lqoVzlbF7RyZ9ra6r1Z9qjd1/Vcz+HcMRRxwx9PzmSunA73//++HczoGzOWQKqBqPfmJr4JklU3Aw68f98pe/PF1I6NXFC8WC6ehYCeC1sPwrDy3Ib/Kw5L2Ch1ZOLrzbyMfFeLTgoVWYxAvk9UU+LsDT5EV41uXkWh4nz0pVrfCosHCLh5fHyfPl4SmOyqPpeJzCwrw+MsUkHTLx1Go+judNJmvB6xYqD9rC1iQdHidvthXS5gGfeuqpw3W1Jr/wtKwp75wnxYPPHMLyo/7m+x+TdHhuogW5ECk8zjxJR9i0lQ+PSTqKkHh9regEeUwuEt51vyuvsfsm78yDdB7PSo5g6E8VoVCA1Jqkg1dYFXHkw/MkHdEC11WjExHm9ky6j5WvOSIYPutZGuvDPuWUU4Z9Vc68RlVyPpyXHUzArSKy1YVe+LRAFrej468FhYF0wMbbmscJ8ps2Pptj5qGNjffVr371QIgQRPcUV7R02HidQ6iv8tBSzHXjzUT397vf/YZin7rxgg1Xe4iwr3xgVQggJ0kBK56xsdY2E4owwtXVwMhhYZttJrqnzIUsFeZEIVId1Uf5UaA1rBuj+sh1wAEHNAuk5ByFV8lSie6BvEKwDBkyCQtnZW5dGEI+b30oqTAwXEttk6KkoiDNmjMkapuUe0chun+MKoq4BXI7rtcr0b+wMMNJhTWZqoERfM0Migj9g3tHcfpdeiIqiMPA+PrXvz6sk2ephoXDwFDxTpnut99+TbkZIBQ6MhTrsCYVLHQlu0AWt6PjqiJ4aCkg1be8vjqVJThf8cPyRFt5KxuSHKcNSgFRq23nVa961aBIFb1kHtq88Tp3EN1TDpS5c0Z/qo02y0RZ2Yxtsjbe3IcZRPc2VzlRVbN14wWeLG9YEVQluo+2Hp60H32VFEdeH/lReUbf12pg5P5U3lE1MPwt96dWA0MxkJx0ayINZa61hQeoircaGNbGmupvZSxUA8M5rCvZyeD3rMxzmxRkA4Myz/2pAR6i50leVYFXy+MkFwVvbaxLi2uawcbwYERUAyMbPmSSx37AAx4wY2DkNimoBgZFW/PhYWC4P55z0YdWPnx1o+dkF0gsvqNjRZG5cfMzZubp2972tuH/g/NV3hJjT+TjKtuSvKhc2Mc+9rEhj9li/cEV6zi4aFs8tIC9B59t5aENYPD5n//5nzk8tDkfF+w92J78jpEqz0+tMuHrNeMWa1PMT605YZ979rOfPcxJbeHKK68cWKaqTAG5TXNU5fvIVPmT5SsjhyyP+spXvnK4B/KUrXw4mdwHOV/55lY+3DpYT4xGGLTGgEcay1Rrxmrkw7EunXjiiTMyBayxdQtu5zoL2POV8+ExC1gO19xdzE6tfLj7QCb57jHIS8vfyh23cuannnrqcA451zqfOOfD/ZgxS6aYu9vKh68p9JzsQrFgOjqWE7w71aEsfRWiLd5WHikPhndTJ+nwIHmk8qsxuzV4aHk0uT+18tDyOIXjKl9sTK3hUcpxYgZqEQMIITq3EGbloQXXxAvm/WSZcn+qsLBeyTpJp5WPy6P6eIm5P7VO0iEv77wScwCZtOvwAHlldZIOL4onLT/I47ZO+TuvP1W1ckyX4dHnSTo1H54n6Vjr3J+aZeJxWgvnrxOC4l4j9eBx+nxrH3I/nJdHXCfpuPciIzxO6QLPXY5g1Hx4TNKJCIa+2ZoPJ1NEC4KPuUYnptPpEAVwj4WFY10ynJcX7JxC45UkQ+qE19uqOl+d6J7sArFgOjqWB/hgg6+Xd9TyKHHj8l54QGOTdFj+vJCxSTq8Q39vTdLhufhbeJw8neCh9Te/V37fPEnHay1OYrJ67VnPetaox4mL+LnPfe6wBsFPnMGb42mNTdLhMaoWbk3SqXzNNVrAizVhqCKm1qjAXpbHyctvTdLhRaqm5eW3JunwDHnrY5N0Ml9zlslzIAJQq4VzBIPH+fznP3+U/1dltTXHF9163vAw8+Jbk3RydMLPBz/4wVnRAv+effbZs473lRTBcM0iMxUiGKIFe+211+j+6j085TWN7skuFAumo2MeeIZ4CJ6jmFqT+1MhOF9Z9jwLHmfuT4U8tcbziZSgFiJFnyYPR56xNUkH9R2PTe6sNUlHBSiviLy8GR4L705FbC1Eyjy0roW3lvtTA7wmno3PyGG2+kt55limeDbyl/XaeNnyr/KDWaZciCQnzQNyjcgb8iSd6E+N/GKepMMzz9XCdZIOb41n2PI4/Z3HaW14dq1JOnKzCoxEGbJMmT3LuclUJ+l4Tlxb5MPrJB3XkPtT6yxga+XYLY9TPhX14l577TXrWcsV2u6ddUWDqP82P0/uR1RAv/GNb5w1C7jmw11njmD4V34858PJJGIhaoIhSmSiFZ1YXeie7AKxYDo6WuC18J54NyzzCtY87453VCfp+CyPLzyUOkkn96e2JunwMGp/ap5a88xnPrPZoxne9GGHHTbIxKusIO/5558/OklHrlIeMU/Siak1kePM/ZN5ko7+y1Y+LqbW8H6W5XGaWtOapKOHVe4vy5Q9xOhPDZnq1Br5xZzzDZmsU+TFWx6nSAKP83nPe96ox+mavD42SYccIgReJ7t7NOZxuvYcwcj9qYE8C9hzZDpShev27O6xxx5D3rsFz7V7csoppwzrWvGpT31qyId7RpyvRjDkw+M5jOk+EcFoRSdWN7onu1AsmI6ORkWs/J3naP/995+TBwTeCY+TVd+apMOi5x3xOFuTdFSA+qy8nopN/ZVBKp/7U+O4wUOrmpWn0eKLDVJ5n+eRtDh0fSd4nLxk56seEg9Fj2m0rtRJOvKsvCvsTK1JOtGfGvk4108mPaVy1XK3tQI7iO7jWisHL7gmHicvT89va5KO/lP8yjwpnnn2DqNNSn6UTJno3nmjP1W+NtaJTPaRoImUD82zV3MEw5qYKVs9eN6dth0Vul5vTdIRPVB9LR/emqSjTSpy23WSTkQnwuP0e45giCC4RxjFskwxd5fnfve7370ZneCZH3PMMUMuvfWMi6j4DniG3Q9rnq9fZTomqBYd4+pA5y7u6FhgCPJ9G46QqlAfBWhDq0T3lJPNUJiubj5CbcKnPos2UYtPBQVJKVDA2lryOWxUwo2Uqk09uHEjzAsKqxTgUICZh1avrc3WxouqMN6fSeW1hgh9tlpyKJvgoXWsCsoSoTwjw/9nmUBY9DWvec1A5BBE97nvMviabbxkqgaG/lT3IZN5hIFhjVwDxVfBwLAOjCO9s/WeILzQeqIHlUFQe0GtlfCnNXWuamBQINEmFTJlA0PxEkUc9InVwGCUBEFDVQTuBYWkpagOWAcGmjUnv+NnA8OzoiDK82CtKc9sYFDIQr3ag4SD8yQdijmoILfYYosZwyeP6pPWYDDW9aTMpUWsFyVNhgrkGDFMwrkXNNaYv72WooeLO1YEwmlBXRc0cQEhN6E0Ya93vOMd0xNOOGGmOKiGhSPEJkwmfCmEJkRYEdR1wpCtQqSQyYB1IWvFOa1nXGuIEKl/I8Qb0OqiDQUMcSdTbrMRFs5FTlo2FMYoXooQZg4Lg1YOn1O0pLWjBet0zDHHDC0kVaYASkIhyCoTWH9h4SyT8HassfdHm1SsE5mE2N0P/9ZWIEVDCnmE+IWkx4rSrJdQ/Bg9oFCuIqQqU22TIpNCJAVJZGq1SZHJsSId0WqTAkVIPifM3GqTAte70047DVSIrZC2+ybcG+mIkCm3Sb373e+eCf3XFIlnMNqkHD9SJMLsrTYpiHSEezX2rCwkPdCV7Cpc3I6lCxuDfBplZkNvwWaD77XFQxsbr0pgmxDlIIeVlVPOCwYPrbwlZVI33pAJ/yxlTjmO8RbbGOXbKP4xkNvmWnloY+N1juChDZny8V1LyFQNjLzxVgPDOo3l4xgYkeOsMgWcy3sYPS0Dg7K0pi0DI/pTY02rgcGoqfnwUOaULCXUyoeHgbHLLrvMW1Wrl5l8LQNDL7QqXwYQmWoulWKOz1UDo/I1VwODzNarIgwMcjFsxvDc5z53yIe3DIzIh+NcJpNryM+s+x3PobWpBga5xr5fqxI9J7sK0XOyHcuC8BymI2E8ocTcnxoQxpRLE/pVcdvKp8lLydsKmQkBV87XqCbVlyn8LNyX+wdV98ozqsrNPLQxSSf6U2M8Wp6kI08m5FxH0QWdnvCpECKauwohbQPU5SqxTLV6GoWcXZewaJ2k4xxkU5Ebs1uFHGNqjf5UtImRj6uTdDBO5f7UOknHmuv5beXDUT1iVEIVmWUKCBk7n7B3lqnmw4VaraXq7Jik417EmgqRkknfaIzFky5o5cNjko5jOF/uTw3IT77gBS8YQury1nWSjvul4lcdSWuSjvNaFz3DrUk6+lMxj2GpgjyqjzytCuyYpHOLW9xi2Ddbz4prfuELXziE6a15K83gWfEMyU+TKY/z84zpZZYPX53o1cULxILpWFrgifHI6tQaIdCo+qyTdGp/ap2ko6JStWsLrPzdd999pvK1wjF5nDwEXnX1/jzDPCPeRGuSTvSnhkzYfPKEmNyf2pqk0wr1gc8L+x566KGjFaJCgbyfsUk61pn8rUk60Z8aqJN0cn9qa5IO76jVrxuTdEQn5vM4DzzwwNFJOiITIhSxPjWCIXQb4dQ6SacVnciTdHjStT81RzD0/M7ncbofen5bk3QiOhEy6cnOnu9nPvOZ4V7Ee4Vxc4rkzDPPnGFuCpkiWuAZGotOiBZEZfgYpE2C4Wp1oXuyC8WC6VgSYE2z5Fn9j370o+dwvqrG5GnpgWxN0uHNgWrUSnSf+1OjUCYT3fNs9FLWIqGYWqN4RJFJnfoCClN4nN5rWHtrko6KXXLxWjIPbXghPBweiGIenptrjEk6+nL9nQfUmqTD48z9qSETj1uRlO/Y3nvv3RwuoGDm7LPPHgqoWvzBinUUP2FZak3SUbmqKpY3WyfpZL5ma+o1kQnH8h6ebOZrrpN0eIo8vFZ0QnHY8573vCGCULmWwTWfeeaZwx7TmqSjKMr5eerOxaPOU2uiPzU8UHIaKCCCoTo696dmmTyfPsPbzoPjc3Th2GOPHfY8hV2tSTo8TtED0Qcyhdcbn1fJzOP0XLg/7klEC/I0KagRDPOLFQNm7m3PH7ndQ+/3HWj1h68KdE92gVgwHYsbwUPLU+Qh1HxcQH7MHNFcVNPyOHkILc5XxVEKRoKHVq4sc75Gf2rmoeXJRa4q96cGeHPB19sqRAKf4UHIqY15nApsfL7y0AZ4dPJ7PB8yyZvWfFwAC5Bjyb/W/tQaLZCnExGI+akZES3Aa9zq0YxrO+SQQ2Zx42boO9YrSobwzDNyPpwnliMYLY8zRwtcZ+1PBfdArlp/KW93WR4n77HCWpOFF23tawTD2vKMs0xy4BEtyP2p4LMRLXDvxqITogRmys7ncb7xjW8cZHe81jHCqyVPjWDkfHjUO+RoQeZrXh3onuxCsWA6Fi0yD63cIsgNZbah4KHlZbDaeU+5PxV4TVoxWOJyTS0vQoEij5M3IK9VJ+l4ncfJ++UF1kk68nFYjUwwydN9YpJO9KfydlqTdPx/7k+Nc8oT8pQcXztGy+PkpePwxWvcmqTDQ/Q54/Z4QXVUnxymNeGhkINsZBItyP2p4XHmSTrkFxmI/tQ6ScdnKstSQFsKXmO9w63ZrfLhcrf2gtYkHe0uPGlRgNYknehPjehEjmDImWe+5iyTexdtRk94whPmyO2a9Z9q1+HltybpODbvHmNTnaTDS9YmJTphHeskndqfWifpiC7I5+d8+K//MklHBMM17LLLLnPy4e6d97h/nqVWa5pois87v3qHGsHw/TLhp8WstbKxZD3Zww8/fHr3u999ev3rX3+60UYbTR/5yEfOqbL7x3/8x8ETzT9Pf/rTl/sc3ZNd2sCWw+KuPLSZy7fFQws8Ix5S5aGNthDvrwxQwRjks5URKeAz4XGOTdJ585vfPHhtvNpWFMZr8nutSTr+nj3OOkmHF8OLG5ukwzPJHmydWoPXuMW1HJ6ramEytbxWHjxP3ve8NUkn2qRiko57Rybr5Pw1Hx6tOtaJp9jKh0cEQwX2WI7Te7BjuS+tSTruAdnGJum4jsiHi2DUqTX+P7dJ5QiGezf2rIhg2Cd5wmPA64z3eGySDg/SWpJJXj5HMHif7lXI9L73vW+QKaIFrXy4aEFEMFptUuBanXfPPfdsVoWDc3hWVgeWrCfLusIggm+UFakyk+XFIlMJCawyzeEGSQfkF5bXK+2e7NIEb0WjvbwRj67OGa0ep9erh+R1OTEWP68l89DGsxV5xspDy5PDFuU9clSVh1ZFKk/PdyA35zsnbzI+6ztRK08Bpy+PU+628tBCzFDlcWUe2uxx+qy/1Uk6vCt55agAzTy0vDmvI1zYeuutm9ECHpBjtTh0VZwecsghw5oGR3IGDz28Pzm8kKnmw+X7VA7LF/KCI1ogOsFLDZKGHMHg3VX2LIi5u6p3VctmRqQAjxwJhDV1v1tEEfL8Zqvy8nmc2Tt0LRiwMFTJXyPeyBEM18Hrsy6xTtaB1+oZ8Zx5jrIXGxEMz6+8/+677z7Hg3fv3F8RCLzGrUk6oimeNYQZ92tM0pHvtk87H5nyLOA8TcqailbwcD3rIhgq4FWX52r9HMFQgW2P56mvSixZT7aClegS9V5lT1Y/2vKCZctaiR95rp6TXTrIPLTB19uacsPD5RXxTnkRrcZ9FcbHHXfckAscIy3gIfNsKg9tgJfAW5CzI1OepBMVoC0eWvLIK6qebXnf8sm8o9qfCiqgeebPeMYzmlzLIDfGQ6rTfQJysryzykMbUOUcXkGdpFP7U+skHR5QKx8X0QIV2BFBaL2H9+O+6DWt9809IAsiiCxTgBfN+4dWBCP3p8b5IloQk45aHqdowfJ4nPKgY5N0RB/Izzusk3RyPrw1SSf3pwZi7q4Ixlh/qufD82vN5/M499lnnyFa0OI+tj6eD1EKMtXzuA/x/asRDM9FzYevCnQyihRqoxCjdD+U7IYbbjjdYIMNpne84x2n++67b7NFIGBDrOHlrmSXBrRxhOLMX1obF8UEQpE2VRteDLq2QcTGC8F2E5uDDU+4bUyZCzOOkSl4DyOxRXQPNkbnbxHdg43V894iuq+FSFkmCsH3qEVQEQYGkodWSBcoKUqaEmoZGNaDzC2ie7CeoQQr0T24jvyZPKqP8mttvKHMfbbV+gJkpYQpu5bR45jYh4SP/VsNDEqEAQGtUX1CyQyQvE5ZmbfapMLAILc1b4V0HV9YVrtRqx0JFFkJU1ub1qi+IBSJUX1SDqHMKdAIC4M1RhIRBoawMOWfEQaGdXK8Mbko8x133HG4dy24b84d6YhqYPjcfK1KKwNdyf4lH7D11ltP73Of+8xaHA+8yjS9c3JFm2666fTf/u3fRheze7JLD75A8j9ynJUmLhDKoDVJx2ZiE7ARtSbpRH9qoE7Syf2p+TPOF55S3XjBpswjpczGaAd5EcEy1Zqkw9Pl8Y5N0omNt2Vg1P7UOknHWtR8XKyXjVEF9rI8TnK3DAwbr/Wp031yf2psvC0Do+bDQyZKMTy4llFDqfI49f2OeU+UjzUfm6RjzcgdMmVlnqMTLQMj96cGIlpAfntcq3o6DIxleZzmF49N0mFYuKdjk3RimhTUCEaLNjQbGDzUFm1oGBjkqhXfee8/4IADRicErQx0JTudDnybm2222TIpt1iRPNPWhvPXLm7H2oXMQxueVSs8KjQlvKgdYWxj5XXttttuTR5acB7KlFcb3Lh149WKUnloW2HhzENLmbc23lDmlL8QY6sQCWzwCpFaMkUBCpl5NS0Dw8ZrfTIPbTYwfCZ/d7KBYbOvG282MIQhx6ggs8dps65wzIhKtAyMaJOCloGR26Ti/WQKA6PVJhVGj+NSsq3ohOMrMOJxju1BnALGD5laBgaDUHFaRFhyBKNGJ6qBoZ2otpY5PqPEejl2qxApogU8zhrpCXg2XvGKVwznahkY0Sbl72SuEYxsBFUDo9UmlaMFvjNj0YmVgSWvZBFx3/zmN2/mhio8nJTmfLMor+ridqw9wCwjl2rTaW28lejehlk33sxDq2rWJtHyKIX3bAI777xz0yMFSpDCaBHdg2pe4dsW0X2uFm4R3deNtxoYNtgxFikKVLg6h8MzyIDA30bXMjBsvJR0i+g+b7whUzUwWvnwMDDcu1otHKAoeJzCq2OGkfVRDdwiuo/ohPvbMjD8nRHQIrqv/amBUOauWVi4xRBln3HNvPw6YCLgGTjooINmcscVzsug40W2DAz3PLy+SJGEgRFh4bxmOVqgMriVDw8DY++9916mx+l71+J0Dm870hHVwPCcxHMYBkZW5q1q/ZWFJatk3QAK9mY3u9noBJIKD5PFajV2t9CV7OICZXbwwQc3ie7BBuLZsAnWSTo5LNwiurdxxcbbmqRDsdeNN2TiyfLMWqG+eA6RLSBdaIX7bNgKmcYm6Tiv849N0nEdOdxmIycTTzXaNKqyCgODAufhtWCdtIcIr7YMkPA4RQJsvtXAiEKtkKkaGLlNqmVgtDbeMDCsEwq/sXw4w4aBMTZJx1oHOUnLwIh8eIvovrZJVQOj1SaVlXkl+sigOLUpkq1lYLgPFO3YJJ1okxqbpOP38ECrgfG7Bm1oNjA8J2O0oZS5sLD7kkP/+fqdx+dbxle0Sa0KLFklyzO40Y1uNDwUNpn4iY2RBaeohDXuwWOR3uY2t5ne//73X+5zdCW7OGCzC2YZG0zL4qUQhNKkHlpeBij08UVv8dCCz1OowflaJ+nExtuapFM33jpJhxVfN94A70EIsnraATLw7Gx0LQPDxhvMUi0DIzbekImCIVMoztyfWg0MRksrLAwUBbnGwqtAFsdpTdKJQiRrSqY6taZuvBHBCGWe+1Nbk3Qox9yfGrBOXqOExwopPSsKKSMdUSHE776NTdLJ+fCYWhPK3L2p+fBsYFjzVj48DAz9p2OOhuPjY7ZOLQOD98yzjXREnaRzwQUXzDBYtQwMn80eeDYwgte4PiuhzBlsQtJjYGjW+7kysGSVbKsK2I+bGg8mhbr++utP11lnneltb3vbgcJsRUK/Xcmu3Qiie8or2lVaFm8Q3SuQy/NTM4K6ToHUWNjXpkjZVfL9AOXrtRbRPUhjxOZXie7rxgsxFs81+nvdeDPRPQ+jVVySPU7h1da1hRchDNka1WfT5Bm1iO7zGL9ANTBifmqViVHhWPLKY2Ff107ZtYjugVJhoFibloFBUZChZWDUfHg2MCjfVptUjhb4GYtOUFI8zrGoivsQ83alI6qBQZl4VsM4rBGMmg+Pe8fAaLVJZQPD+caeFWvC47QGrTz+n//85+G7xAtuGRi84Pj+tQyMmpapBkZtk4JIR7h3UgBjBttVxZJVsqsDXcmuvYicVCtHxHK22YxN0omNt/LQUg6tQqTgoY2JJTE/tYJyEvZtKXEgA8/OcVqTdGy8FEGLhxacOxiHgofWsUKZ5/7UACUeCrS18ea+WOHVMV5jCpMia03SibCwNa08tHXjbRkYMT81I8/dpeBb4Wj3i5Ji+ETOuqXshOrJ1DIwpAXct7FJOjk6UQ2MVj48GxjWvJUPDwNDNfB8OU6pD0V5LQMj8uFkcl/qJB1hdt8RaBkYuT81ZIp0hAjEWAX2Jz/5yZn1HIN7Ipcf9Q4ZkZYhf9Q7ZAMjp2XCwMgRjLG0zF+DrmRXIbqSXfsQ1HVCXvN5nLyfMaJ7Fr1NsEV0nzdeCFL5yHnWjbcS3dsk6sabie5Z4mPDB2xINifh1ZaH5HWbHyXVIrrnVUSkpxLd5/7UQDUw8hi/fE7enI2RIp7P41TQMxam5PnIsbaI7iMsHIVc1cCobVLVwKj9qdXAUHQ2X1GaFELL6AHHp+RbRPdx3ULnLaL7VnTC+kS0oPanVgODYpQGa8H9MmC9Pot5TZ2HkmoZGDkf3jIwaj68GhinNPLhYWBYK2veelask3VEY9kK1QOZXXfUO1SMXfNVRVeyqxBdya49yDy0FFndeOskHRtea+MFXi6Pc8withHw6mxOlYc2NkEbSUzSqco8+lPHJunUjTdksqlQgi0lHQaG0ColPOZx2sAYIZWHNtbHOaxlzLjNyjxvvJWHtvanBnK0wGdb+XAGhvWy5mP9juoqwstvGRjug4rjsUk6PhdeWjUwrEP1vLKBYc1bbVJhYGjfms/jlJslU8vAiHx4pCPq1BrPa/BGR21BNjBqWDhP0omWqZYyY2Asj8fpeWpN0gnDxzPRMjB+nqr1WwaGa6oRnWxgtNqk4rvl+6uSeYy7Wz3OfOxZK4quZFchupJd+MjUdbXHLzbeFtF97k8NUAAUszwej62GhbMyl78cG7cVrS0UcYsVKTzOGPdWie4pJptMeJ9kyso88nFZphiLR0nVjTdksqk533wep/ArpdEiuge5OmtIUVdS+exxtgwMGy9vOK9DNjBabVJhYFBSjIcx4g2bL4/TvWsZGBRzhERbBka0SWWi+xzBiP7UloHRCgtnA4NXVxmRAp4/HqfztULakeOkpFoGhmc28uEhU1b4jJ7MplQNjFalbhgY7p3ISutZsU7WksfZoucE3qboTURXKqJan+J1bVmZ17RMyBTK3LrXtEyOYHg+xwoYVxRdya5CdCW7sMFiVczGmxgLPVF0lFRrko5NVIi20sTVjbfSxGlJqP2pgVDmQmmtQqTYGMnFSxizxnlQFFLloc1K2vVUHtq68VYe2tqf2jIwKOnWpBzrZGNFYDFWBKLPkTIcm6RD2ZCnNUknt0llAyPo9GJ+apUpDAwFVq3oQxgYUgThdbeeFYQjFFrLwLBhuyeRjqgGRm6Tak3SIXdes2xg6Bsdy4czxHRJjEUvwLUz+lqTdCIf7txVphqdCAMjK3N528rMFdECa+n5bEUnLr/88uE4KpnHuLu1/TAwIh1R4Xoo51ZUpYaFeb85gjFm+FwVLNkpPKsDfQrPwoSpNSZ6mJZiJue55547zE/NiKk1ppaY2WmWaIWvw5FHHjlMqnnsYx87Z5KOiTAm6ZgzavLJve9971mTdEwJed/73jdMpGlN0jG9hIx3uctdZsnkuTJl5Kyzzhrmp+apLGS64IILJpdccskwsca8zTolJ6afmBBj6ktrks4b3vCGyW1ve9vJxRdfPNl4442Ha4hJOs7xkpe8ZPKMZzxjmGpSJ+nE+j71qU+dM0nHRBTzcE2+MgkmI6bWmOxi2oyZuWOzW02HsU51ks53vvOdYZYoWR3HOuXJJ45vQs8d7nCH4X1m3OZJOq94xSsm22233TBtq07S2XLLLWfmp2bEJB3YdNNN50wIAhNjnve85w1TmczLrZN0rKM1Nfc0ZtyaOBMw6YYcPmvSzrve9a5Zk3RMliGzqWIxSYdMMXfXTNpHP/rRs+anksnUGnNh3TOTdOpzQC7Pwsc+9rHJbrvt1pyk4xhm0Zp4Yz5sPK9x782kdT89t3WSTp4mBTFJJ+buutY8TapO0gHX5RmtII813XHHHZvziT2Hb3vb24bjeSYe8pCHzJokZPqV48Z83KuKPoVnFaJ7sgsLLR7asHijAKI1SadVCMHj8h7exJjF6xy8MuHVMWucFc6abxHd8wxY17ywFtF95OPGJunk/tTWJB3eUYtnVjO/kDaylhbRfXgRKlN55i3ydsf3ntYknRwWDpky0X2rTSpP0uF9tyqsY5KO3OxYuDp7nC2ie96OkGImus/3LvpTxybp1Hx4jmDwwMeiEyIY5PL6WCjecyL82pqkE/lw5xZVqbOAoz813htTayKCkftTWxGMVptURAtcs7DvfB6nQsKxSToiDTzu80cm6eRq/RrBaFXrxyxgKRLPQSsfHmkbudkxMiLXo92oRW6xIujh4lWIrmQXBmwqNiYh1qCuqxuvTZcCaxHdx8bb4qEFX+JclBI0caHMW4VIocwpKkp4DJS4kPbYJB0bhVBXa5JObLz+1jIwalg4E91T2K2NF4K6TuHLGBUdA4CSHiO6FzIXfmwR3cfGK5c6Nkmn5sOzgdEKC2dlzigaI+ZwTykEz0HLAIlnJRRnNTDkLIVUoWVg1Hx4KPMwMFptUmFgeN9YVa2/UfiUXaQjKjwfFFlrkk6EhZ2rZWDUfHhNkThebZPKBoafsXw4BYvX+GOpMj2D0vWMjE3SibRMyFQNjJyWyQaGdMRYWDgMjLEhFAtOyVroJz7xidN73/veM8UAigdWxgUsZHQlu+YRzDLyrmOVkO4Tj1Ox0dgkHc+rnxbRfWy8vvwtovvoTw3YBMkUZOkxP7XKFFNryN36ggZZOmU2puxsFja4sUk6jk95tYju88YLdWpN7U+tBobravEWhzKXm82FZBnWkhJv8dCCfYSSJkPLwFAwE/nwloFRoxPZwBDVGItOWKtleZzum/VuGRiRD2cgtIjuc5tUy8CI+akZ2cCo/akBf6PA9c6OeWaeFWQpY5N0GALe47mtBkbkw+P7w5jNBkYe49cyMHxv3tfIh4eBwXgY8zg9K8aQWoOWgRH58JCpGhg5H+57VEf1LXgla4Guc53rTHfYYYeBOSnInd2Qhz3sYdPFjK5k1xwqD23tT21N0hnrj6MAeRCU8BgoFMOlxybpUAhCXS2i+9h4ydMius8bb2uSTu1PrQaGjbO18YJzUHZVpoDvq9fGJunYIMnQmqRTN16wCcaEm1abVDYwvK8V6gPKC9G987XCmAwfUQJtQS0Dw8ardQQoi2pgqDzNRU7VwKh8zSFThOHHWkAcP5TG2CQd6x0j4aqBkaMTrUk6tU2qGhit+al5ko41m89g43F6b+sZ5xl6Tscm6QRt6NgknZe97GUz378wMGLubqtNKk/SUbw2Fp1w3QqoRBhaURXfvxj2MGZgLGgle+c733kmfHL9619/RsmyHDbeeOPpYkZXsqsfNkBf8BbRvY3XBuXf1iSd6E8dm6ST+1Nbk3RsEi1yCuez+fI4x0JmQqM2kRYPbWy8lEGliasbb8vAqBtvNjAoTu0drbBwGBjkrrndgHMyPmyGLQODoidHKM46Fi+3SbUMjLzxVgPDvWiFheO8jIuxqlsgD7lbRPdh+Kj0bRHd53y44zNysoGR+1Nbk3RaYeHcH8rjHIuqeD54+WOTdDz/vLaQKSvzmg8PAyOUeZ0mVQ0MSrqVDw8DQ/tWKyUT99cknbGoyvf+kpYZm6STaUNbk3R8f/L3LwwMsgcBR0UYGGhBx3qVF7yS5cVGWCErWf/ybBczupJdvfAlMbFlWR6n0NIY56svuU2iRXQfGy+lmSfphOLUdhAGZSW6p0hqPq5O0uGZjRUa2eAV9LSI7oHMNsGxSTo2m9jY6iSduvFWovtWWDgbGLyAMSpI6+S6GDRjo/yiuKZlYFibyIe3JunYeHMPZTUwan9qNjBsumQfgzXjAY1N0mFYCYe2iO6z4dMiuq/58Gpg1P7UQKyT53wsOmFvxWtcZQrYjz3fY5N0RC3ch5ikU5V5bpOqk3RahUjZwGDEtviaQ5nvueeew1q1QtqeFffLc94Ki4tOuC4yuS8UdVbmmTY01mFs7u5apWRvfetbz3wBs5K1GW255ZbTxYyuZFcPMtE9RZj7UyvRvQ3bF6vVV+dLbIN5xjOeMdqILtxlIxybpGPj5W22JunksHCL6L5uvCFTKHNeX2vjDQOD3DVfF7BhKrBq8dDGhmPDHZukY11C2dRJOrU/NRDRAophzOOkROU4fX6MmJ0nQlm2iO5DSduUWwZG5OOyTNnAyPm4loHheK2KWH/jSfHcWkYPOIc1H5ukE/nwFtF9GD7hpdUIRhRxVZnCwOAtt2pewsDQ8ztfjlPxV60tyOvj+Yh0hHRFVlLuQ3w3WgZGTctkA+M73/nOqMdJmWO/yhX1FZ4zBY6t4fDx/YuBFLl4cVVilStZD9kd7nCH4ct/gxvcYLh41sxGG200PESLGV3Jrjr4AlFSLaL7HBbOLC7BLFM33sxD69/Wxhufs9lrgB/L29j8bGBVpgBFJaw2NkmHoovIT9DEZUIL1nnOK2UDg5KuYeGszBV3zUf1yOOUk2vx8NoII0TbMjAoARsvBKl8NjBym1TLwGjlw4Po3kY+n8dpPVVgj03S8Yy4nzGAISvzbPi0DIzaJlUn6bTapLIyt+Zj0Qn3GctUy+gBDon1GZuk47opwUy+HwZGKx+eDYyxfHgYGJTQGG2odbLejJ9WSDvy4aE4q4GRq/VbBsa7U5tUy8BoVevHJB3H9R1pwT32/ZivKn6tVLIWRbz7ete73vRqV7va8LPuuusOltJiR1eyqwYxtYYXMTYKTAjRRtHioQWbFy+qRXSfw8Itovu68WaZbOQ2v7EcJuXE48yj2zJsWjbmFg9tbLzCZVmmrJyiP7Xy0FLmrbBwNjAYwq2NF3xejtMGNsZrbAPz+ZaBEYYPA6FFdJ833spD29p4q4FR83EBcth8DRcYI32nnDEeeQ5aBgbPkNfWMjBqPrwS3ef+1EAoc88gb7mVDw8DwwSgsXC1c5qkQ6aWgRH58LFJOtEmFTJR5tnAqG1SWZkzEMaiE5Q5uZblcXrOW5N0/viXan2GbGuSTm6Tak3SaaVl8qi+1Y3V1ifLLfcgSMa3vgyLEV3JrnwED60vcu1PDQR1ndzrfB6njbfFQxvnsQnZmFqUbLHxgnxrnqRTN95KdO//a1g4T9JBlzg2PDpGlPHsWgaGc4b32zIw5MaE7qBlYOSNt2Vg1I03ZKLoYiLNGOT3NP+PTdJxHsZRy8CIjde/rUk6tU2qGhhj+fAwMETcxjZB93nnnXduGj3AqLGmY5N0hCUZRy2i+zB8ctgyj+qr/anVwOAptviawf2S4/T5loHBs+NxRjqiGhgxTQo8R3UWcE3LVAOjFZ0IA4OSHPM4//jHPw7fD9/fsUk6jIF45nLxYo1OMKTJ0VLmqwudjGKBLG7H/AgWl9yMnsPCLaL7uvFmHlrKQmh2jBmIorGxjvVyO5fik2CWqZuvMB6lMjZJJ/JxLaL72p+aZQoihNbGGwaGHOd8HqfP8q5aBkaEhf3b4nyNfNzYJB0bbw0lZwND2K+14VPc1kiofWx2K+XDuBibpEMu8rWI7ms+vBoYMT81IxsY1qKV7w4DQ1HamJfkPsYknRbRvefA8xDpiDoWL9qkskyhzFth4WxgKPyJ+amt9ZQimM/jdAyGk2usz1N8/3zXot4h39uclmkZGCI/NS3zmRTBaLVJgXvv+aaEx55x9961RTpiTWKVcBfjC11e4LJcrOjcxX898KueeOKJA/9scONWftJvf/vbk+td73qTz3zmM7N4aOH1r3/9wPGKczXz0OLrxVn60pe+dPLMZz5zhgM3eGhvdrObDe/BEYvLtsr05je/eeA1duwHPOABc+T2VTn00EMHeR/zmMfM4qENHmLXhS/13e9+9yweWvjmN785+eQnPzlcMx5a3xMy4aElq/Pj373d7W43IxPOV9yvOF9x7OKDXWeddWbJhPP1U5/61HDte+yxR3PNHfvCCy8ceI3vete7znn9ta997SAr7l9rHTy0ma/5Wc961sA/SybXak/Ab/uDH/xg4GTG15x5aN3HRz3qUZMvfelLA2fxne9851nn/OIXvzhwyfpO4dB1rIrvfve7kyOOOGLgDs4yBRwb3y15ghs3HweHNe5f1/SBD3xgeL/3WPfM1xwczo713ve+d7j/PpP5mjPXsr+7zjvd6U6T+93vfnPkxjGNj/lv/uZvBi5qz3IGWfH/4tD2zFknMgU8/xdddNFwv7JMwbn79re/feDV9rzA5ZdfPsjkbw9+8IOH5/ApT3nKDF8z4H127zxXv/zlLyfPfvaz58jtmk4//fRBpl122WXgZa543eteN/A5e+b++Z//eeBmzp/3/fOs4Gb+r//6r4Ev+4EPfOBw7zJfc8j0pje9aeBXtgZ4tTNfczx/nq9vfOMbkwc96EHD8dY0VkQPzGbingcOGPBwWhh/s2nBJz7xicnPf/7zFVLGHUsLQXTvy3mTm9xk2CBaDyglgqDeBuPLWgnOKV2k754/x8mbJGJ/mzFFarMJovvHP/7xM+ey4X3rW98aCMQz0f0jHvGIQXnaeCntOCaEMjd84Otf//ocBQvO4ztAwWYlH7ABGh5AWdqMskyAHN912fzIhOieTGFg+H/K0uCCLBNZKSnE6Dbk2HirgcEouf3tbz9HbpuuNT7ppJMGwvtKdG9ztMGTzfsq0f1Nb3rTYd2Rs1uDILp378C5bbwUkmMF0b2N2nt++tOfDr/HxhsyhUIwyIGSrgrWvTOQwWAEx2ldmzU7+uijh2tCdO++BFzLv/7rvw7rRt4gunesOJdhDu43ZZmJ7hkUniPXZUBEGD6Z6J6S+/CHPzxHwcLXvva14d5873vfG57fCvecIn3Ri1402XzzzWfJBAwzho9n+C1vecswnGHbbbedMTAYEmTdZpttBpkYGIj5Q5l7Ri+99NJByQfIzOhB3O9aW0YPA4Pcns0XvOAFc+6J58B35PnPf/6wZk9+8pNnXb9hGde5znUml1122bAG1cBwj3z/GGS+fww13xkyMV7XSlwVV1kOBttTza9gDpEvWMzo4eKrBvkrIaMguq+FSJXoXt6l9qdmSjYFO5VXNeD4XhcSaxHdC8PJmVWauIBwnH7KFtF97U/NMsk5qTpthYWD6J5M8+U4tdUImbWI7kEYUJiRTJXoPkJ91rY1qi/3p1YeWjlKYdZW6DNG9VnTsdoLIV0EF2PUdcK91lDeu/LQgvA7GchUR/W18uF5FnCrTSqP6nM/KiNSwDqpqh1r63FOIWGh/xbRvecr8uGtUX2qcXOVbSa6t06tNqlIkbjHY90a7rF19KxUmQLCuq6bTDWEm9My0hHCuHksXvSnBuqoPqH2mpYhk3qHqBwfC6V+5jOfGXp+W9X+cW/lwskktzwWPl6TWOU52Q033LDJYONv66+//nQxoyvZFYNcS9DEVcVj4/XFHpukE/2pLaL7PD81I5hl5IpqBWjAOWJqy9iUkaiobU3S8RnnIEOL6D7ycWOTdHJ/asvA8N5Wn6b1k4+S4xwr/rLx2Zwco2Vg2HhtoC2i+1yI1CK6b7VJZQODQmltnGFgyIePteRQLAhHWjy0sfFat7FJOjkfXg2MVj48DAwKQaFPq00qzovRaL4cp3yy47cm6UQ+XA6xZWDkNqmWgZH7U1uTdMZoQxkY6gE4RGN8vZ4VrS9jk3Q8a+RT71ANjDxNCuSus4FBcdd8+I/+UryoBsMxW7ShUe+gL3YhF9OuciW73nrrDV+8Cn/z2mJGV7LLh2CWUaiwPB5ni5LNF86Xu8VDG5uNzbbFQwuKoLLyC5q4YLup/akhE2Wo/WNZHqfe2jFmGRu8c7eI7mPjtVG1DIy88bYMjNbGm5W5NR8rNOLNKugZm6RD4fCyyNyapEMux2gR3ddCJAhlTn7FUi1lFdEC6z1GzOGe4pGeb5RfTFhpGRjRJhUyeY9NPu5dLkSqBoZj10KkOCflTiavt+D4vD8e59gkHa8HXWI1MLLH2SK6Z8Rm2tBqYLTapEKZ+055zseUmagGj3Ns4APFSiEySFuTdBgVl1122cwIOko3lFKNTtRJOgsdq1zJ6mXcYIMNhpBAsJD4gvBwvbaY0ZXsspGJ7lthYQhmGRt6tXjzWvM4tSSMcb6yjH2BW0T3PhMeZ2uSTt54Kw+tL33uT80yBXUdS74ll43D5qMfssVDG2tEsYxxvtp4KaWY7pOVefU466i+uvHWSToUbKvPOJQ5j7OyawV4ZNqRxibpRJtUi+i+tklVA6MVFs5E97zkVlg47iWjx+tjdHq8Pt5dy8CINinr1yK6j/7UkKkaGLVNqhoYeX5qhutWnStkPRZV4R1aczK1DAz3muHDCK2TdOqzEimSMDA8Y7VaPxsYqoVbbVIxd1dUJaawtYzk/ffff3SSDlniOzYWwViyStampen4Zje72QwZhf/3t4UYP1+Z6Ep2HC0eWrD5UKZjk3TyxluJ7oWYWmFh8OVflsdpo0CSMjZJxwbv3BR9JbqPjdfnWkT3uT+1ZWC0Nt5sYNiAxsgU9EzawFo8tEBJyle1eGhj43WMykPbapOqBkbNx1UDw/d8PqpHHidl1jIwwvCheFoGBhkjStbioaUAg8YVKB33kIERx65piTAwyDSfx+maDfTOx8/wjAixtgyMHJ1oEd1TRNGf2jIwWrShYWA4rvDpWHRCCB7LVKQjWq97jsYm6USbVGuSTo1OVANjLB8e0YJTTjlllKQlJumMRVUWMlZrn6yTLKWe0a5k50K4yReRxznG+SqkFqG1SiofhUi+wJWHNventnhobQJBDFBlCmYZEZcxcnJKUvh0bJIOi99xxibp2HgVs7Qm6eSNt2Vg1P7UamCQbcyjZGDYWFuFSkB5U4YtovvYeCnpFg9t3nhDpmxgtMLCWZn7dyw6wcBgXNjIx7wWytBz0jIwIizs39YknZwPz5N0wsCoYeFqYLTYs0KZi5pQCmPcuK6b0Tc2/1U+m+HTMjCq4VOn1rTy4TlFQkm38uGhzEVVxgqkeNgxYrAVVfHcC1GTyXuqgZFpQ6uB8ecGbWiepDNGT7nQ0ckoFsjiLnYETZwN0SZQN97sdbHEET2MweaH9Whsko6NizfQIrq36fki+0JXHtpWIRIgWgjqutbGC85HSfFuxprfbZpSJGOTdHg+Qm5jk3RcT7DyVAOj5XFmA8O568abDQyMR63ikjwUm0JrEd1HPrzFQwt+ZwxAy8CIMX6Vh5aB4dit6EQYGBTdmMcJrtk9GaMm5D2pYG0ZGDk60TIwXAvDLMuU5+7G/NSMbGBY87HoRJChVPL9AIOJxz02SUdluGeyNUknCpHiWakGRos2NBsY8qpjtKGUeXB3twwMcjpPpCOqgfG1v1Tr59qCsbm7awtWCRlF7fervYsZmoYXKzoZxf/ic5/73NCfqMk8eu00+ut903sISAr0UyNX0LSuD1FPod6+gGZ0f9df5/3Pec5zZvWnRjO6/lD9h/vvv/9k4403nnNfNO4je9DsrmH9jne846zXTz311KFnUt8lmbbYYotBJufV96gH1Lk91xr1vUefqx4+x/zoRz86058aMukp1G+73nrrDSQBW2655Ry5kGmccMIJk6c//emTu9zlLnNed65XvvKVQ++gPk/9qGQK6EfUC/oP//APg0zWRp+q9wcxgM/oMyST/sfvfOc7Q7/6BhtsMEMMkPsZ9Slby3vd615Dn2LuTw3o37Qmt7nNbYZ+zyxTAJnCta997aEH1Tqtu+66M69ZZ9elB5RMSCXIpE8S9J66Nv2cIRMiDD3O1sG1IjnQaxlwDL2f7q3P7rTTTrOIOWI9rYkeYr2a9XX42Mc+NjwvSA/IpAc3AxmDnmWEEPqV9aR6XuK+60/Vy2xNrLVnUw+wHk/P8Pnnnz/0pwb0DusDdj4/emAzgQPoc9VfbK322muvWeQr+bui5/fGN77xIF+QlmRyD3svWfTOeib1/8Zefc4550zufe97D8e2j/ne6WH1vXAfKzEHmfRhI7rQp6rn17OQQX1ccsklw31BZjLGk3D88ccP/bqeOX3d8+mPRacHrooWZ7XkHx7KNttsM7TvSNCvDWDBb7bZZsP823ve856jFn/FUvdkeSfucaWJC7D4eWYtovscFs6TdIKSLQohxibpCIu1LGCfdxy5wDEvwnsMzR6bpCOXqheyRXQPvIDwQnlJvIYI5eb+1BbRPe8o5+Pq1JrDDjtstILTsXmcrP8W0b1rcZ7goa1elt8jH155aHN/apWJvI7d8jhjVJ818D0ag7UWGh5r2+H14KhtTdLJHmdrkk7uTw2ZcrRAeLvl6Ua0QOvKWGrD5wx8GOPGFQWxpp6JVgQj2qTIxDPPEYxaiFSJ7sfy4RHBsN6tQqSIFgR3dyvkHTlrMrVG9eW0TCuCkdukagTDOrXy4Vf+JYLhWVhMe+YaCxd7ALbbbrvpQoeN5drXvvYQlrEBPO1pTxtaj8aIqzOWqpINontVr4yqVljYF82XVI5zbJKOvJfNcWySToSbgoc2K/O88UKdWhPzU6tMFJ2N3ObTyqsChTlfu4KNXs7ZcVoGho2X8moR3eeNt0V039p4QyZ/t4m1wsLg8xSZ0OFY0SHDRv6rRXQfYWH3s2Vg1Hx4NTDqxlsNDO9thRgpcwaZHOeYYaRghmxjk3R8f4VwW0T3NR9eDYxWPjxP0nHNrTapMDAQb4y15Lie6MNuGRjWTppF6Lc1Scf5I3+aDQz3qpUPzwaG53usp5eBwWCrqZMMz7gahpaBEWmZqC2ok3RytX5rks5iwhpTshbZfNmFDp6rL0nAw6Q6enm88KWmZCvRfe1PDeSpNb7ELSKEYJaxsY55ETZ47QyUSqt/T05HTqtFdJ83XrDBkSmI7uvGW4nubZp14w0Dg4JCCD/mRTimPKFNqGVgUCoUWIvoPm+8QKF4b56kE2P8AtnAiMKzavjEqD6b5rI8ToQLY5N0KHDKtkV0n/PhZHKsbGDUNqlqYLTapGI93Dt91GPtWz73rGc9q0l0Hx6iNRybpEN5uf8tovtWPjzPAq5j/AKhzFssS/lZkMfn4Y0NVmAYRb1DNTCiTQpaEQxGbDYAsoGRxxNmhDL3/RkbjeizniO1E2MOCWNOHrwVVVlMWGNKVjWjEOxChk3hGte4xpyw5JOe9KTpIx7xiDnv9wWPCmo/FM1SUbIxnqulWHghCiKEnGwEuRk9h4Vbk3QybWFrko7Np7XxxgbD4xxrIXFfbPpkak3ScS2MhkoTV/tTK02c+94KC4dM/k6htTbeMDB4nNZtPo/T+atMsfFam5CpGhh5421N0qltUiFTUNeNsV85r+gCw2ishUR/puOMTdKx8SrQaRkYNTpRJ+lQFtGf2jIwKPBWm1REMIR9q6cdsMbBMtWq0iYngyAm6dSxeJR3XG8YGKHMW/3h2cDgJbdoQ4EsUh/L8jgdv2VgxPfP/WoZGDktE8WL2cCobVKxFhHBGKvAthbe43u+pkbQLRole+c733l6l7vcZebH75tsssmgvMasoIUCD4LFqb1bvCcebgXvxfvrz2JWssH5uscee4xW1fpC2ggyi0uGMn8/lSYukKsQMw9ta+PNMlEkNoDanwrBLKMvdszjdEzKzvFbLQ02Xpu8sJsNox7HZmNjAwYGmbIyj/7ULFMYGNoVWsQcES3Q/jFfVa3NUqvF2Cg/4UIKxb+ZhzbkcGzX3xrV5x5kw7NGMFobb45g8G7GWlsYVVIIVaaAqlYGCqOnNarPM8YgIFONYDDsnD9v6lmZ8/5b0YmIYOixHvPgPfs8Tt5dy8CguKwP2VsGRtCGAqVaUyTkyuxGeSxefAdqdCIMDMba2F4bc1/dk/ws1uiEPbA1qi9oQ6N1qBoYolTZAGhFMBY7rlgBJbvcU3gyVDfm6jBVdqoGVTm2JmGszdhvv/0mu++++6yqstYElsUA48JMrXGNJuCYuKGC1v2uk3SMbDNySkVlq7rO83DssccOlasmn9RqQuPezjjjjKH6U7VwnlqjunGrrbYaKjtVI7cm6ah0NM1FZWiepENWFY4qX1Xz5qrYmFpjNJlpOabSVKiKVTGqQlRVbp2ko2JTxa6KY+tVJ+molj3ttNMmO++881Atqwo7T9JR+ayCVHUoqO595zvfOVRcqio1xk8VbZbN1BOVoNZJZWmtTA25raXKVyPW6iQdr/ncKaecMoxhUw2cz6FSVcXxz372s+G+xySdmFrj3ManxVSkmKTjGCptVeK6T+5rlilG9TmWc9SpLSpOVQQ71p577jmrmjg/K+6nimrPW55a4/74mykzrq81SUf1uAptY9vqJB3HtGYqluP9ZHL/Vet6ft3D1iQdleWu29q0JumYhuReq6T2XbFO+frdg1e/+tXDKD0yqVzOk3RUNXs2TCFqTdJ5wxveMEyzudWtbjVzzBiLZxKO+1krp0G18K9+9avhu+BZqffE988UHJXMJiU96UlPmnX9zkcW3wHV861RfR0J0yWGFQ0XL4WcbIsmLlu80TCeaeJ8JogBsscZPLTWl3fUCnkF56sc5ljeijUuT8jSbuV3rT9LvkV0D7w9YdUW0X3tT80y8Vq8v1WI5PhCo9Ii8+U4eRq8iDGaOB4UD6bKBLzMOHZQ1+VJOvLhKmfHJunU/tRARAt4wmPsOjxV/ZAtontw34X0naM1SSfy4ZmHNlMo1mclE93X/tQawdBnPUa8YZ14nJ67FtE9z5hn1yK6B8flPY5N0nE/s5caRPciGHFd9VmJdIR77T6PPeO8RM/KGFGEz1qfqHeo1x3RiZApRwtqWqamSOo0qZDJ98YzqEZlLB9OJpXMY9/fxY4rVnW4+OpXv3oz8e0L4bWFDmFhBRP5wdp0002XZOETpSrfNtYgT2naoFo8tCDkRPGMTdLJhUiZh9aG3tp4M9G94qrWxhtyUcI2ljGlQfH4fIvo3iZjgxszMIQQhcgqTVwYGHXjDZlsitZBzqwVGqXMbXLaicbYboRpvafy0NZ8eMvAqPlwBkMe1dfKh2cDQ4FVq00qlLl8eIu4A8gk5D3GQ2tjpuhaRPe1TaoaGFGBncPCeZKOY4/lw8krHdRKMQSiHallYESbVIvoPkLm0SbVMjBa+fBsYIzRhkpHeM1zPjZJx3oKeY9N0vHsewZaBkb9/tVJOi3a0Mjjt+odlhKuWNVKFldxS8nadNZdd93pQoeNV3+sB9MXxBxcLTytNoHFqmSDh9bmag2qZw/BLCP3OpYHtOnJgapgbW3ONl4bKmURcz3z5msztimNTdLJG2+dpBObUC2yCGYZRtN8HqfCGdWrY5N0KOE8qi9vhLHxhkzVwMj9qZWHlhFR+1OrgSFvPJbj9B6e2xjnqzXWKtUiuodMyB/zRsPAaBk+OYJR+1NrBEMhUav1BVwP44Jn2iK6j3x4i+geFHHJf45N0sltUjWCwYtrRSfCwKCAyN+C44fHOda2w9iLSt9qYGSPs2Vg1DapOkmn1SaVJ+lY87FnxfdW8ddYa5r14/2OTdIJ2tAwMFoRjKWIK1ZVThYjCMjJyCVE7gDkE+Sg1oacrHyjfMLBBx885GrkH+SgWkxCiw1YXOR5XHewAsGFF1445OPk/ACLi9yr3KEf7D9ynDm3Kh+F7cb6yRm18mneL2/knFiPKgPP3e9+9yHHKcfn/GRaf/31Z173+5lnnjl8lkzkzGw3kY/DPBV5Mmw3ZN1nn32Gz0Y+LiDnLAfo+fWDhabmjOXs5DVf+MIXTg444IBZMgHmns0333xy1llnDTlMLFf5+uXj5I3lkuVUyYQRJ1il5Lpe9apXzeSrMceQCXsUJiPXYc1yPpxMb3/724e1uutd7zqse2XHYjjLsckL77rrrs3vozWVm/VZ6x8ygb9hBXLvMXmRybVnJiDr7Zo8O2R629veNvnpT3865E6tmfwp9qvIh5MJY9bFF188ecxjHjM8D9hyKnwnMRY5X+R+M1wv5iA5Tt9Vee98/a7Lde+www5DftiaOp81jXy4fKVcJ5mwPnmm5Dyx2LmXmK7cm4BzuT61AZii8msBz638PEYlOc56TzzznocXv/jFw3VjDsvXn/Phn//854d6B3n8yLWSD3tWrAlWNc8BFjB/w3RGxsy25H7IT6uNwGzWWm85c8+ZfdteGGxcGWoMrLncbq4t6FgBrIj2vtWtbjX88GRvcYtbzPzuZ/PNN58+5CEPGW2tWCxYmz1ZoTBhzBYhQwwNH5ukw4vhHY1N0slh4cxDy0IWIWh5nCA0xRJfVh8nVqQW0T34rHO0iO6jPzVkqpN0cn9qa5KOEGQrX8YL5Ulp7h8LV/M+VGhXmQLW2Lq2iO5zPrw1SSf3p7aI7nl6uT81wIN0r3jwY32Mjk1u3lmL6D7y4WOTdHhPQt5jk3SEwnPUKGQir4relscZEQznGxuw7vg8YR0BY5N0vO66q0y5TQpak3Rqm1SNYMT81CpTpEiwTI09K3KbevfHJumQVfrCuaxDPY6Ihecnh/4jWlCjExHBiBRJHuOXEREM511bRtAtmnDxAx7wgOaXbylgbVSyQXSv6KG18UaoTu5HCLRFFBGzPoW5WkT31iMKPDJNXO1PDVBcNjS5ohhsXVMQQXRPbnmnlpK2GVOEwpAtonuwScjvjU3SsfE6V4voPm+8LQOjbrx1kg7F3woLhzKXqhhTdsKdZB6bpBNtUmOTdKI/tUV03woLZ2XufK2NF1yPPL77Mrb5Muacq0V0H2HhFtF97k/NMmVS+dyfWg0MOdqxfLh1ci/nC8ULR1vvFtE9RKi7ZWDUfHg1MFq0oaHMKUbr3TIOQplTwmMkEI6DZSrqHSoiLVOn+wRyWiYM7rVxBN3qQp/Cs0AWd00j89BGv2vdPIImzqZHGbcKkWLje/7znz9vH6fNz3sqTVzAZynCTBNXN96QqU6tyf2pOUdHXjnl2p8acE02Onnl+TxOhTGVJi7AOLBJjU3S8bd4Huoknbrx1kk6CmBa+fAwMOQBW2QLoTjJzVNvGRiUAQU2Nkkn58NbBkb0p7Ym6Xi2WtGJMDAoujGPM9aMNz1WRMXYszZkqkV5OR/eMjByf2orgpHH+LUMDFGTsUIjz6GiSdGC1vfE3z0vUe9QDYwYgxcGBpnCwMj9qXWSDmXemiaVDQwRgLHJRJQwuT3jLQPDvfUcjU3S6VgNU3j0ij7vec8b+qVy32gLxx133GSxYm2YwiM/9P73v3/Ifcmp5Hyk/lD9dfJWJpmYpCNXFZN0Pv7xjw+9hHKddZKOvOdrXvOaydZbbz0rf6PnznuiB2+33XabM7VFzv7cc88d8nJ77713M/8tLy5PRO7WJB154e222264Bn2FJqOYDuJcJpTIx0W/YsgkP6mHT65PDvlhD3tYc5KOPJoJMK3eWRNiokfUhJMKOVL5POdqTdKRmzXlxPXIk/n/PEnn5JNPHvJp8uF1ko5nzbn1WOYcZUytIY9cXu5PDei3lQeUC5YHrPfEV/9FL3rRcA455jpJRz7ZdenjdG/kKz1Pce/J4NxyfiCX6Fpjko4+aPcxP39kkseXs9VjKR9c+zT1zXrOvPeQQw5pTtKxRnKRPkum2g8qh2kSjh7OOknH862mxP2EOknHZ/zIhbYm6ag/kLfO/am53sFaHXTQQbOuO2C9TGWyj7Ym6chdO47cZ2uSTkyTkg+PSTrqCshqLeTDc45dzlw/rLoBMuujVZNSnwO5aWtgzeSPO1aeHlhuJWszs2kpIrCJzDeqSGHEYsVCV7KXXnrp8GUhY2wiFTY/G5jiIJtg3uQ8Dr6oGvV9Of1uE7O5xPUjTNhxxx0HhUCZKw4JZW6jpQRj4w2ZvI+StjE/+MEPnrMBKU6yYVB4jDmKqMKmavNEOkAhhEwBRS02nyigqQYGZeW6bErVwKgbbzYwFDgpeFFwkjferMyN+XPsVqGRTdOIPpsyZVgNDBuva1eE5LtjfbKBYeO11ggrvv/9788xMPLGC3lUH4PI63njzQaGQhvKnSJt4aSTThrWQQFRa/Nl+DzkIQ8ZlB7FmQ0Ma0NZKlAKmRgzocyRPFDcFGDIFGPx3Dv3M8b4ZcSoPt/BAw88cI6SBsd2P50LMUM1MHze3zwnZGH0ZANDURrDxLNSDQyGoHXJxVnZwHCNeYxfHdXnWXO+qmDj++WaKETPSjUwvM7w8Ty6L7l4ET7xiU8MBoF18//VwIgxfmEohYHhO4WkZW0fQbdWK9mOFV/c1QmWqo3aRm6T9mXyRc5fQEw/NjqeLu9rjz32mPOlYqXzdFVSer3F4qKy0ecwKPkys7brxmvupLVyvpDJZmizfe1rXztsvMADtfE4rw2DIqNwbdxZJkra+RyDomvJZWNVqcybVYlaQfnypsluTmo1MLD82FQpk/DMs4ERG6/7Xw0MiqpuvCGT91FArrVuvOB6VTGTGYNUVRrhcaq6DSWVDQzPosrW7bffflBMjJFsYPBSKDzKBqqBUTfebGBgKuL9UNJVUTmmeylCwHMLzzwjFBSPikzVwAjDxzpaf89JKPManagGBvYjSpIhkWWKCIbnhuKoFbGhzFXkqhxvVQyrnqakKV7PYp0VrPraObBLUVI1gpGjE575iGC4dz6X5xdnmSg91e6edYZNBQNDxbx1otBbOOqoowa5Kc5qYJCFkW3NeebeR6ZWtKBjDc6TfcpTntJsfJan8dpixkLLybZ4aHN/aovovubjAnKbMT2k9qcG/E3uRuHLfMxBiktaPLSgmhfZw9gkHccPrtg6SSfn41qTdLD35HxcnaSjGnhs3J1c8N577z2sU4voXj4vxiO2JunIxymIGZukU3Ndmeg+eI1rni+I7rFMOfcYFOTIcY7x8LomuTtrV4nucz48ZMqk8rk/tTVJp/anBtwLuVnVvmPEBaq7MQe5xlYe3zOiSMc5WpN0Ih/emqST+1Mr0b1e3tqfWqfW6LNu9X7Hmj3nOc8Z5GrxewcxSUzSqQVLcq1yzVmmXAAX/alVJusRTGt1vWKSjmehNZMWrJNRlX4W4wi6Rc/45KFAWbiYsVCUrI3DxkcpZJLxDNWkNq/WJB2ftwFR0u5b0MTFZugL62+BOkkn5qeOTdLJw9grbCaqasca5H3ORlFlCjgvQ2Bsko7PhRGYaeIYGK2NNxPdK9ppbbxgU2Jc2MBaSsMxkXLYeFsGRhA2BNE9mbIy1/4WbVKxTlmZ1zYp8D2kWKxTZUQKUALWhIExNknH563j2CQdf6fgKCcyZWXu79Em1TIwWm1See6utWq1SbkWynynnXYapR307FIYY5N0VEkzKMcm6WTa0DpJp0Ubmg0MinKsWp8sKt7rdQcck+GkYKk1SSfapKpMrTap1iSdShsKQZYRs4A7FqCSdUAekj5ZFl4eAWcD8SW96U1vOl3MWAhKNnhobR7RH1fhbyxo/Y7zTUfB1jQ2SYdH6As5NkknNt5KE1f7U7NMwSzjC9/agIJZhvEwVilpU6EwVGK2DAzKkDHQ4qEFHry1gzAwyBQbXd54q4HhGmp/Kvjdpnf44YcP92QMrl/1aotBKwwfFdGtSTq5TaplYNQ2qWxgOGZr480GBl7j+TxOLFMq0VvvoSgoHZXsLQPDs+BvrUk6tU0qRzA8J602qRzBUNE+Ns7O88njdO9aBobKXhGGmKRTK9SjTQpaBkb9/uVJOu5RKzoRBobzMchasE6MBz3kY5N0rJl73pqkk6MTZGpFMDoWoJKlXHmxYz+8WA/8YsaaVLKVhzbkyQTktRm9brzgS89jYkVTGGO9d5Qcj3OsQT4oB1tE9+BLbZOsNHG1P7VFdN/aeLOBoYevtfGCTY/HSQG1WjHC4wzFWe+ldbZ5BnVdNTDyxtsyMHJ/apaJgrVOQqit9bRO3sOAaKVjQCsKRdYiugd/t3YtAyNvvFAjGLk/NRDK3DoxxlpebihzvMZjPcGUe8xAbZHKe0bJGty41cCwLnGfQqZQ5q3oRDYw3KtWm1Qoc8ZDK8WQPU7HaRkYkZax5u4LpZmVeU7LtAyMVlomIhgMsbHohPWkhBlsY4pTJMvnq0wdC1jJeliEAylbm5zf4werSesLuNiwJpQs5WWTaRHdA2XDGxmbpBMbb2uSTt14czO6jcnGVzferMwpszGPk4IzaYdMLaJ7GwVFODZJJzbesUk6fs/3IRsYNq7an5oNDMbgsnKcGHpaRPd+d008hOChzQaGexSGT8vAcEzKu0V0T0mN5cMZGBSV6MPYUGzeluhFi4cWRA8oubFJOhSADblFdN/Kh2cDo/anVgNDzrj1HIDn0wQgirxlYDgG4yVkqgaGexpsc5XovpUPz5N0xjzOqHdwr8eGD7gPiFJ4nC2jJ6ITvN/WJB3XGmmZ1iQd789pmWpgtPLhocydt9YNdKwlOVkFFGNf8sWO1alkg+jel9wXdIzRCJMMj7OS7wd8+W3yYywuLHThwLFJOjksXInuWxtvJroX+s1E+RnOgUzBxtoqovKMUSpYg1pE99bDZ8cm6VBcmeazGhitpvswMCjrMY+TMhdRQHDRIroHBgq5W0T3sfFSwEF0n5W5jTcP5K4GRt14q4FBtlY+PDxzz8oY7aDPkWdsko6wqnz82CSdnA8PAyPSERREJTPJ0QL/Ol4LDAwFUmPPUnicDMaWgRH3NRPdZ2WeaUOzgRHpiFY+PAyMIIxo7YmeUUqYgTFGcOG+OtfYJJ0wfFqTdGpaJgyMsYEeHQuYjKIFJfbK97WEZLSGSi8WrK4Wnkx0rz9S71v0p2ZZlOGTA4mC9oXcn5qb0REX+Gz0y9U+TsTmGvq1x1Sify0m2lDIEUT3yMmj/UDbiPYPLSiZ6B5pvrYFbRpPfOITZwaJB9G9NgntF1qGak+vx1IbkWNrk0BC0YK2Ge1I2icMes/tM45hqIVza7PQTuH6gsA9+lP1iGai+2i9QCCvtQIZQBxPzyrCDsfRa5n7UwPaSqyTliE9v7X1BfSnap2xjtagkspbH/dUS5M1JVMQ3YNeZm1S2mYq0X3uTw1okXFMRPc+457WFhDr4d75TluTzTbbbI7cejz322+/ofXn0Y9+9BziedfsPHpA9Y6SKZM2RJuUnuBMdO939y63SVWie33WBhtUYg77j4EA5Na29IQnPGGO3O4donvya1+pbT1e1/4VQwSyTPFdO/vssydPe9rThueWTM6F3EQ/sp5U79FaBHppPXP2SPdOW5H33uQmN5l1Tr21n/70p4fvt3VtPSuGYji3ge5kqtDvrKVLq5XWNn2xZOpYi1t4hDW23nrr0dzsYsaq9mRZ3C0eWmDFCwu3iO7D4o2wcKaJ4wXE/NQKoUDWsQrGGFxd4Vp5nLzJFtE9C16Ok/fWIrqPQqRMExdE98BCz5XGQRMXnnnMT80IUnnhvDEqSF4f74XHWXOkAdWhPInMjVvDwq65Naovt0m1RvW5ptomFUT35GpRNMZ5eeY8zrxOGdHS0eKhBfdSiLJFdF/bpGoEQ3Si5sNztIAXPeZR8pB5nJ7P1j0Jj5On3iK6jzYpf2+N6rP+IilVpohgtKITES3wXhGhsbnJnmEe51hrmjXiKWfy/QzfI2sX9Q45glHTMu5rjhYI6+Y2qRrBkFZpRSfCM8dbvKY7HpYSrljV4eJtttlmep/73GfYRK53vesNlYVyA1tsscXol2+xYFUp2eB8VfY/Xx+nClAbRes9Nl7FFy0e2sjHRfl+a5JO3nizTDZEIapWWBjkvmxOy8px6jtsEd373TXZVFoGRmy8LaL72HijP7VlYIxxscYkHT29Y+kP60VJt4jugTKRBx2bpBNtUmFgZGVe26SqgSEsXDfebGAY6DDWvuWcCnrI1Lp2G7a83tgkHUVDEXIMAyMr88zXXA0M19VqkwplrmBHXrEFnxGmlzNvhbTD8BGqbRkYYVTk/88GRu5PzTKFMq/9qXFO57FO8vRjzwrlL688VigoLeM9Y5N0Ii0Tof9sYNQ2qTxJZyzP3bEWK9lNNtlkeFjhBje4wUzhi82P8l3MWBVKNkjleamUXy1EypN0bAKtjRdsbjxOObkW0T0lbQMLxVm9qCAWGJukE/2pLaJ7Gw+567oE0b3CkTGPk1yUBmU2lne26SgIaRHdx8Zr02oZGJGPC1QDo/anVgPD5jcml+8Bj3OMxN8aUUgtontwTZT02CSdnA+vEQwGUTV8soHBqBrr02Rg8DgZB617Ei0kNvqWgeHZiVx6a5IOYyr2iEp0D66h5tjDwHDvxp4VytwzzKgba02j6MnTMq5yf3jUO2QDI/pTAzzkPEkn96cGQpk7r+e41SYVnrkB6q2WuzCSjzzyyJl6hwrODI83JulQ2H0E3SJVshRrWE+3vOUtB4sK3PzrXOc608WMlalkfamE5SivbB3beIP9JSbpsPwjFJw33jpJx31phYXBFxT7zrI8zqOPPro5ScfvNgAbnfBVlimHhUOmOrUm96e2DIzWxpsn6Whtma+Pk0c6NkmHIqAIyVQNjFyI1DIw6sZbDYxWm1Q2MFRYj7W2OGfMbm0pcs+IUP7YJJ1ok4KWgRH9qa1JOta61SYVBoYN33qOgbJTVduapBOjESmwloER/amxvp7JbGDUNqkawfDZqqzCwBAV0doypoAYVbz8sUk6rl2ULibpVGVuzTz3LQOjpmWqgSGE36rWDwPDmnveW2Aw8JRbMnUsMiV797vffSZ/9/CHP3y67bbbDg8GAoHb3OY208WMlaVkKSgbRqWJA4rERlFZXOrGC9EakFlcfIlzw7xmdoqZBxweZyULCGVOebKSWyCXDZrH2bLWweYdLFM8tNbGa3PKNHGhzKM/dWxUX914q4FhMx/LvVJS881upQRtljbQloFh46V0rVvLwIiNt2VgtNqksoHBO21tvOB7ZWMV0hybp8socl9aBka0SXmfVE41MHKbVMvAqG1S2cDwPLWIOUKZey4Zda0wO/CQye0crRAtpUOZtAyMmg8PAyPSES3a0DAwKHPPWSsfTplTnDvvvPMo7aB7omI40hEVQe8Z9Q7VwIhq4ZaB0arW930JA2MsotKxyJSsjc/DCr4AG2644dA7u84668xr+S4GrCwlO7bxAoVpjuh8zEE2cF90xk717vxuA3AOythPVuY23uA2rTy0rY0XbBqhzDMxQIbP2xgZW2PtCo4jr9zioQVWuqKQykNbN15gSGRlHvNTM7KBwaPI/alVmZN7Po8zZre2DAwbL2MgqOuqMs/58FYEo+bDoweSMmc4tPLhYWBgmZrve+e+8ThbBkZEJ1x3y8DI+fBsYIQyz3zNLQMj5qe2DAyesIKdMY/TcXn5nseWgUEZaiciU64tqGHhloGR+1NbBkarTSorcznjMSYmEQID1q1Ty8DwTJOZ4dZSnDktEwbGWC96xyJv4QkoUzfqS1l8neu42LAyW3jMntSeErNc6yQdY920ruSJKzFJRyuCsv9ddtllznFNHzFpI6antCbWaAfSgqB9QNtBnqTj89oZnvnMZ860K2gNiUk6Wma0u0RbTZ6kY9KK1g2tO6aJtCbpaJd50pOeNLTJVJDHvE3nbrWCmTCjdcY5TYjR1pGn1mibMMXFZBUyQUytiTF+2jBMHamTdMz4fN3rXjd5+tOfPuucMarP8+2eO2eFlhjTT7Q5OVadpONcJulo4YgZt/m++vz73ve+of3Ev67Te2KSjr9pFdLSBWax5rF4tU0qT61x77TLaH2p0O5j2oxWmX322WfO+EAw4cc987zVSTrR+qJdyPNGJlNrYpJObpMKmTy/ptZ47rRDaSGLNqk6qs//13FusZ6+P9rTtJ/l1piAiTZakrThtCbp+Kzvmu+zdaqj+mJ+sTWpk3Scv7ZJ5Uk67onPxBi/OknHdCEtUKYEVfiuHHHEEcOzFjNuW609HYts1N2yBrVn9KHtywdfShuUTV+fn34/G4o+whg3ZnPX1+eLp8fwi1/84jDCTg+ljZdRk8e6ed0X3YgrfaYxP7X2Mto49RUefvjhzXmcxqHZ6IyEs7HWYxgZp1+WMmBgeU8o8+hPNaJM/6fNN2aDUuZ1460GhofWMSisDAaGzZASZzwwUCr0Jx588MFDj6fNsBoYNkB9hzavLFNA3yalREkYQF9H9eWNtxoY+k71/OaNN+6z++h+6FPWB9qCAeuOp7+4Dod3fGtq/JkeXL3M2cBwTymNJz/5yTMyQShzhgkjIvosQybK1zPn+cv9qXlUnz5MCsO6tuaNui7XTZHp067PkzXUN8uAoiyygeE+v+xlLxt6uClV90SfMZkYGLk/NT+blLme1jA23ceMMDCsC6OpNUDdsY2601/t+a2j+hzDSEDfscsuu2yQKStz6+K4jMEwerKBkecXVwPDvFiKljGZEQaG59i9rvejY5EPbV8e+BJ6MBcrVjYZBXIDmwEyBoqzgmdFYSAbYO1TbNXj9NkgEnAM1rONLm+84IsbQ6MNhHbMIAbIx7QhI0vgvZhR2vqi2+h4hTbHLFOAd0iZ2fApQwoob742EptTeJwxb5Q8eePlcWYDwwbtPYyEvPGC1218N73pTQdvK+anZliTF7zgBQPZgDVoeZyUnWPwsKqBYaMki7mvNlUzTbMyj/OHkmRg+FtEC3jpvHgKrxoY1gIJSSXmCAMDUYPIwt577z3r8zk6QYmJNFQDIxs+X/nKVwYDjUwRLXBcUQD3GxgYOYLhOXVsCimQDQzK4XGPe9yc6EQMh2cYmDnbiqq4ZkreejMcqoEhaiJqgYzDc1MNDM8Kg42SrAZGRHV22mmnWTJFBIPCc17XWMHAsOaek9az5DhHH330ED1gPJEpe5zW0LX7vLWsBoZ90jXd9a53HY7VmgXcsUTJKJYyVkULT83H5XyYXJxc4FiOUw5Q716Lhxbk6uTbKg9tLURqEd3nfFxrko5j5nxcbZBXwTnWxymvqlp4jOhetaVikLFJOoqFohCmNapPfjEXAeVJOnJirXy4vJ/8nzwh+efLcer5bRHdRz48zlGJ7nObVOWhrf2pIZO/Bal87U8NqM5GpqDQaCzHKSct7zw2SUc+XD6yNUkn2qTctxbRvTxkzYfnSToqenN/ah2LJ08/NqhC/tQkHTK1vieeHzKTybFqrtT1Rt6+NUmnVuvnSTrWoJUPj0k6rm9sNKI1lrtVmNYikoh8uLxyLV7sWOKFT0sZq0LJ5o238tA6T+1PzUT3lJR+xrGpLTY601FssK3NV3WkzWJskg45tGa1iO5zf2qL6D7mp7bOaXNxzbU/NStzfZw2z5aB4W+USvDQtipgKbAW0X3uTw2EgRHKvG68ocwZGPodW60vYJ0UZylcG7sn1lu1cMvAiI3X9bV4aMkQbVKV6N6mnvtTK9G9Yhtyt9qknFdxmNmtY4aR3k2GQ4voHhAsuLetSTpRtBaFQHVUX6tNKk/SUZzXapOKZ/zZz3726OtkUBzmnrYMDEpZcWBM0qkGRvSnhkzVwGhV68ckHQbr2LPCwCCXc/cRdGsXupJdIIu7IvDltuG2iO59sW1soWwq0X3uTw2EgqM4bVS1PzUrcy0i81XV6j8dY5ax8VI6QRNXlXlsvC2i+7rxZpkocxW7uU2jGhgUGcU/BvLoO2wR3UeblLVrEd3nNqmWgZH7U0OmPBavtfGCtQiPcwy8Pr21Y5N0GBbW2791FnCOTrSI7nN/akBEwFpQCK02qRwtwDI11r7leuebpGNdKPsW0X2eJhUy1QhGbpPKMjEwVJa3ohNhYDBEx4YP+H6Fxzk2PD6q2FsGBg88vn8tA6NW67dmAXesXehKdoEs7oogRn21eGiBV2OjH5ukY8O1ibUm6dSwcEzSoTgpydqfWifpUC5BOFLhHDxO3lmL89XftGpUmrgcFiZ7UNdRutnAiP7UQDUw6sabDQzHHaOCpMxt9BT12CQdSlZYujVJJ8LC/q08tLHx2kTHZgG32qSygUG21jPm+IwmHucYnR55nGtskg6jyL1tTdLJfM0tA6P2p2YDwzpRLrU/NRsYz3rWs5qtMUAGRlGkI6qBEW1SeZJOXqPcJtUyMFppmVDmnu+xZ4Uylx7AuTyWthGVIXdrkk4oafd9zMDoWLvQlewCWdwVRWvjhWCWkZca8zhZx+aMtnhoY+N1DBtzpokL2NyDys1GGuPebPy1P7US3ZOpekd5VJ98G+KBMZDZBtYyMGxqXg8PrBoYsfG2eGjrxlsNDMo8EwNkWAseJyU8H3MQz67FQwu8FedoEd3nsHA2MEJxtvLhee5uHeNXDQye8BiZguuVLydTy8Ags/vVIroHz1H0bdYIRu5PDeQIBvlqf2o2MCgzCrYFx9bv61lpfQ/C8AnFWQ2MnJYJA4MRFYqzpmVCmUe0gIdaFWgYGJGnH3tWhOLlw/MgjI61F0tSydoItt9+++mtbnWr6brrrjswT9kgs2LwHgtTf1rFO2tCyeaNt0V0z6JuUSLacGyGLOmxayGvjVW+qdUgb52iiKNFdG/jtQlmmfKEGJ/L7DeZ6N51ZdrCSnTPCx6jgqS8eKsUdYvUAHiMlGlrkk72OIPzNSvzmg+vk3RifmqVKaIFjtcKjcbmy+NsGT1AGbj+MR5ais4z2yK6r/nwLJP3V77m+EykI9yvsXw4AwOZglzk2HQjio4yb0VVeI4MjBbRfaUNbUUw3O+aDw8Dg3c65nG6Zp6wn7FJOu41eVqTdPL3r2Vg1LRMNTBatKHZwBhjvupY+7AklaxNdrvtthtyZTYJIbCb3OQmA2NMVbIsVptr/Ix9IdfEqDsbL+t6bJKO0F188TMPrS9wDQtnHlpfdBvVGIsUC1vudVk5TixTrUk6zhm8xi2ie4ZAVOy2JunIq2VGompgtAqR4rjC0ZTwfMxB4U23NjpK3nFaRPex8VLSLaL7Vj48Gxh1jF81MFSOj3mc8nWe3zHWJDIwBsYm6VCA8bdqYLTy4TmCwaBr5cNDmcthzjdAncdJ2bWI7sPwiRF01cCQD3dPoGVgVNrQamBUvuaQybV7VhQbjUEofs899xydpOMZFhlxT2oEI3//xgyMjsWBJalkW1DIcOtb33qOkq2eyYpgVStZ0EJi424R3dsUeZwUZ2seqY3LhjM2SSdvvJXo3uZXN95MdM8LHvM4KU6KkDIb41d1DJtYiyYuNl5KrWVg5I238tCSV7sFysYqU0ytsWatsDBQhjzOFg8tKMbhAY1N0ok2qdYknWr4hEw2aQaGn2r4ZAPDRh3zUysocx4nA3PM4/QdGJukE/nwSEfUCAY549lpzQKuYeE4pmco2mlaoMwpYOHVMaJ70QnPW2uSTjwr1rI1SSfThrYm6dQ2qTpJh9yt77d1cm+t+VhUJarUxybpdCwOdCX7F8iX3e1ud5ujZG9xi1tMN9poo2Es31jFYcDGZEHjh1Ja1Uq2lY8LMBDk25blccontojuw+OkpFpE99Yo8qetSTq5PzUgj0WJUUatjResGy9C3ni+eZxGmI1N0qHo5GBbRPdRLRwVncH5GkrCxpvDwiFTKHNKoW68eSwej3K+HCePdGySDuVNiY3x0EabFFQDI/enBkKZU5Cuq9UmFcrcNBr3cAy8OgqvxY0b+fDI49cIRrRJjU3Syf2prQhGKzoRBobndz6P0/2y5mOTdMjl3rcm6eSwcDYwIoLhHgY3e8gk/x3ed6tNKs/dpaj7CLrFjSu6J/u/+ZIb3vCGs0Jevjy+vL4wvsimyRhsMJ+i1WrRyuOuSiVbN16IqTXyZGHJVy8giO55LmPDB3w2mv/HWjFsTDy21iSd2HhtVK1JOnnjDS+YVxHKvPan1kk6wtGtsDC4bzzOsUk6wnjOQ0m1DIxok2oR3dd8eDUwan9qnaTj2K02qVgTVbWMg5aB4T66bsdvGRjRJkWmVgSj5sNzBKOO8csy+bt1bxUihYFBAVN2Y/lE6+jaW0T32fBpGRjRnxqoBkbuT80yRbRA7rbVAuMeWi8e59jsVmvj2R2bpMOAoTSj3iFHMGp0IgyMsXREx+LDolKyFGFLyeWfOjCbt/M3f/M306c+9anLPL4xffe9730XlCcbGy+rmOJsTdKJjXdskk7uT81KyOYgrNraeMHnvYfHOdYgL+wmJzc2ScfGawMVwqU4M9tNLkRqTdLJ/al1kg5l7ph1480GhuKu+TxOzxOZWgaG54gSG5ukY2OPqtY6SaeVD88Ghmto5cNDmTPmKLwxREFPy8CI6IRnszVJJ+fDW6P6cn9qlsk6WctWm1QYGPpPl5XjxOwlHdEyMDxDntOWgZHz4a1JOrVNCvJYPNX6rXx4GBjztW8xGBhGZGp9TxjxDOGodxhT5h2LE6t9Cs+qBK5ZvKrzAYm96RjBLYssPAi4lzW9Av/u85///IGMf01wF8+Hd73rXQP/L97VFufrKaecMkwOQdBeJ+ngAkZmj0gfN24lujccwOeQkFceWkToeFXxxeJ2rZN0kMrjqsWxW0nV4eKLLx5kw5NbeWgBmbppPO5t5aENvmb3MLhq8WEH5yuOV1y1zu0zdZKO9xqQgB+48tC6pvXXX38gcG/xLZuEg9cYyfvWW28959nB/Yx7GGF85aEF62Jiz8Mf/vCBhxapfJ6kg7/WMAfPa52kQybXhcw+nuVMKm8CkGcUn3Nrko7n2FrsuuuuzXviu4Dv2HepTtLB3evcnhXrhJs5ZILvfe97kw9/+MMDX3Nrkg5+XpzZMU0KPB+eJ6T/nj/cwpVvmbx4ia3VIYccMtloo43myI37+SMf+cjA3dyapGN4AB5q94NMpt/gGfZ7niYVE33yJB3rhlP5KU95yiyZfO+++93vDpzbnhd7SWuSzllnnTV8tsrUsfhx5VLlLubB3u52t5s+/vGPX26ash122GF6l7vcZUEVPgVa/akB1jxPV0Xw2LUq6FFcUmniArwHHl2liath4UwTF15k8MVmOBbvRyEOT6jFxRrUdQgsxgqkyCrvXGUKkCXaQLJMAV5aNPsHN27M3a1h4cxDy4P2uVY+PKIFwuyubwy8Te0trWsPj5NXSybHy/cu2qRCphrBkNbI4dYcwXDMsXy4yAWPU8XvGDwDniUh2pbHKUpAPv/WCEaOTvipEYzaJlUjGC3aUIh0hNqKMY9THlX/uGeq5W1bL1Ed94xMlR9ZiDfSMp4j4e3cXlSjE61ZwB1LD1csJk92ecHa5v2YWHP66afP8ozCk/B3VmzM5GTVmgxinma2ZheKJwumchjtZpIHmKDDQ+BVhcfJ4zBKrE7S4QUYA8a7WXfddecc23Fdu2k2sSb1daPheHitSTqvfe1rB4+Hx1kn6VSPs07S8TeTYIwCy4hRfdaWTC25eMCHHXbYMFbtEY94xJxJOtbImDH3PcsUMIXGeD4j5VqTdIxtM5mI518n6ZhaE/NTjfELxFg8o9DMZt1hhx3myC1aYE2sDa+xNUlHBID3xFusk3RydIJMdZJOjPEzDQfctxhXaCwcDy3G+GWZ3DvbAC+X3HWmbMxu9eP70poQw4M3G9bz5r7UCIboBA/TvaqTdPI0Kb+TKUcweJx5mlTI5PqNxTO9yOSje9zjHs1pUeYyi2zk+cYB36Pjjz9+eN58h+okHRESEQeTrVqTdDqWJq5cip6sYoixnG323Lbccsvpda973aEo6p73vOe8TERr2pMN8Kx4fS2i+/COoiijEt0HX+zYJB3eUSvPF2w3vIT5pqPwMsZo4nhQvJoW0T3wiMJTDR5aMoXHmauFQ6YguleU0ipEikk65FqWx6m9peVBOY8ogetuTdLJbVKtSTq1TSoT3VunVpsUuA9kms/jdBz58rFJOtZEARHPjveY25Zym1SL6L62SYVMES2o/amBWCfRh7HvBY9T9KLKVPPhyD/IVCfpBG0oiBJ4NkKWlscZk3TIPEbiEtEC0Z75hguIXsRkpY6ORVf4tNCwJpSsc+E1bvHQguIORSEtovvYeIWXbeyMikx0nzfe1iSd1sabJ+lQjK2NN5S56ShCoC3OV5schaOgqDVJRzGO66rk+62NtxoYrY03T9KhrFtUkGCdKDsFPWOTdLTNkK1lYGTDp2VgxBi/QJDKxySd3J/amqSjFaoVGnUPGRbPeMYzRrlxKUsGwdgkHUqQgmsR3deitWpg1P7UOqrPc9CiDY17vfPOOzeHE+R7QnG6xmpgRJuUdSFTVeaZNrQaGK02qWxgtNapY2njiq5kF8birkxQnrU/FYJZRiXkfB6nSsoWD2303vIgxibp2LDCiq+TdGp/apaJMnfMmo/Lk3RUns5XVSu/KZ/YMjBsvJRcsBNVAyM23rFJOrVNKhPdU4StjTeOuyyPU25TxfDYJB2RFzUELaL7nA9vEd3XNqlqYNT+1IB1ojhx6I49v0gWdt999ybRPTguxTtGdJ/z4dXAaOXDY1SfY1LSuT+1Ghgq2seGDzi2Z5wBYl3HqvXHFGfOh7cMjI6O6VLPya4urO6cbK4AVUH67Gc/e8gLuW0f/ehHh0pe1azyg6owowI0IGelglOuS04sV4AGfvazn00OPvjgyf3vf/8h35RzjSBHJlco7+W65YLlYXM+TjWmnO1FF100+cQnPjHkO+X/QBXm/e53v6ECNfJcb3/724ccmdynPGVUgGaZ3vjGNw6Vsr///e+bOXN/f8lLXjL86/PuS8WrXvWqIU8v72qdIs8an5e7taYh01ZbbTXk9uQDrd2nP/3p4XpBrlVeThXsv/zLvwx5wpoPl+N761vfOuQmvV+ecZ111pklk3unovbNb37zZJ999hkqh1v5cDKrzFU9a53IFHjNa14zrLc1I5M86cMe9rDh95oPJ5NzuY+uRY5TLv+JT3ziLJlUEMthur4ttthiqISuUBV+3HHHTbbZZpvhnlbIhzu3e+F5kcvM91Uu3DFUCpNJ1TCZbnCDGwyvn3vuuZN73vOewz0j04c+9KGh4vmRj3zk8Py436rW8zNqneSbyavCWyVzxU9+8pPh+yPf7fM1jw8nnXTScFzfryxTR0fFkszJLnZPFngpckdjk3SEEsObrUT3NdRXOV95Ha2wcEzS4f0sy+NE9NEiuhfCi2HhrUk6kY/LMmXO19yfWonuhQBbYWHwGo/zec973igDD48TK9LYJB1y8OJbPLQ5LNyapJP7U1uTdISFc39qnaSDQ3csXC2i4PUW0T0I4cuHixa0ZgHnfHgd1dfKh+cIhnBy7U+FSEegBRURaMHxsXp5VlqTdGLofXjmNYJB1siHtybp1Hx4RDB4yNaplQ+PCAaZWqxfHR0V3ZNdhJ5s4IADDhi8UZZ29ZD0AfJwVFuqwPUelntAlaQqWBXBPGBep35Q/bC5PzUqWr/1rW8NfYR3vetdB6+TJxDeUcA6RNWlKtGWx6kqlgfCo1KVe+Mb33jOe1SAbr755oMnxQtWfRrwOR4pb1XvKU+RZ8fr5N3xXj7/+c8P3g78/Oc/H2TibTsWTz73p4ZM/q6i1vtV7Ob+VOBJ8fgca4899pj1+YB1JJMeVh6wnsrscapo5/WGx+l+kInHVKMTIRMPWDU5j1OVOK8xyxQRDMdyXa3oBC/8mGOOGSqNRTCyTMCrtea8NX2/noPsHapedl2qkq23597zFNGC6E9VdU4mVc0iGKq9eaHud41ORLSA969Pe/vtt58jtwiGz3pGagV39jhV91qvLFOuwLamZNLPGjKBa7rkkksmj370o4ffPTtkakULOjrG0D3ZRerJjvWnZk8Xn+t8VbU8Tnmr1iQdhUiKQVo8tLk/dWySjrxZztFlovvwTFr5SflPHiePcszjVCUqB8qjbFXm8pDl0ioPbXicvLnwOOsknZyPa03S4eW2uH0jWiCHOV+OUxRgbJKO3KB8+NgknZwPr0T3rehEJrqXw23lwyNaoGpWkdt8Hie6xxbRvTypnGxEEmoEQ9466EpbEYyaD89RFeuUx/hlmVy7HD2vcwzul57fsUk6nm1ytybpdHQsD7onu4g9WTjjjDMGDyP6FeVD9TryaO573/s2PU45Qp4U78nft9tuuznH9RovQp4M8xAPp4L3g1WH1yIHyPvM+Th9t86tZ1POTb+kXC8PQe5Mj6rcaMgk76pflLcRHmrOm5JJvo2nyMvZbbfdmt6NHlz5PD3BOUca4P3pG7VWmLGsU/ZaTj311EEGx7ZOIZNoQe5P9Rm/6xkVFeBJ8Qpzfyrw7nhM/u55kWfM/akB0YKjjjpq8Fz1l1ZPijdrTT1rfqxd9rgjH66/U464RjCiP1V0g0x6Wa1F5Mxzf2ogogUiHK6x9ax4/l/0ohcN1ybHWaMA8ax43TWQKUcwIh/u3GTyw5uOCAavV1RDj3P0i/PyMXWJYHjePVsRhQERCc+TCIY1wZRWoz3unfd4Ft3PVlSlo2NZ6J7sIvZkQWtCzMwMbtzM9BP9qS0e2pqPq5N0ovqyVVXJS+XZyLmNsd04r77DsUk64WW2JulEPi68jyC6j6rq3J+aZQpSed5ba0B6TNLRpzlfH6c+zxbRfbRJqagdm6QTbVItovuowM5rlsfiyZe28uExSUfP79iAdXA/3JcW0b3ohHx4i+g+8uExF7ZFdF/bpPLUGus05nGKYIhOzOdxOk5cW8vj5NU6d2uSTs6Hh0x5FnBtk6qTdDo6/hp0T3aRe7KRE5NPwtCTeWgDOHblUnktmYcWeF48NzkzzEK8AtWk2IPkBrEuyenxLCLXi4GHlyfvycuRe8xVsSpasSLxzHgUcpgtj1O16DnnnDN4Py3P7oILLhg+z5vyevDQBk4++eTBKwqPM3hoMVpVj5NM2KN41eFx8piCrzkgb0cu3isPN/M1B3hGeI15VqIIldfYGllTzwRZrFNm2ZIPVynMW+UFYygjU/D1nnnmmcNrKnJBpEC1uOpc3lquFs4yuXfyop7Hytcc0YJgNJLjrJ4d8ChFLSIXvMEGGzTz4SqeyZQjGDUfHhEMLFSqj90jrF05n+07FNEC1+DY1RMWwfAez4PK9yxTAPe23LHjyXvXCAa+Zh67+04m+esawejouCronuwi92RzL2XL45TDW5bHybuV2xqbpKN3US5NfjdP98n9qeF91Ek6uT+18tDKP2a+2Oqh88wRb4xx1cr/yre1eGhBvpW3NjZJh/cYnmod1Vf5miFHC1S21v7UPKqPJyyHOwb3w5q3GLYiH67H09qqIs9yWNuo7m6N6sv9qXVUn/7TsXy4CAb2K8/LGNxLPahjk3R4kOSOSTo5Zx79qZ4V51dFLoIR96CVD8+TdOoYv0BECzwLrYhJ3F9TmXjjfQRdx8pE92SXgCeb83G8ndYkHdZ77k/Nk3T0KrqGvfbaa1YFaIAHob9VbrY1SYfnJwcrd9aapCOPqIKTh1In6cjH8bSzx5kn6biO3J9aJ+k4Jm+ylXvlmZuqJHepj7R6nO4fzmV5uyxTgLdmXeSR9bLKBZIp8t8+q8czogd5kg4PuOVxxtQan7Fmla8Z5C1f+MIXDtW+comtSTrWTOW3/LS1wZMccFxePq+QTHWSjv5UXifPrjVJR54y+lMDEcHwfHi/+1WjE+6d9+ixPfDAA5uTdFSxi4zoVW1N0tGDzRN2DXWSTo1OxCQdHrnPiA6I1jz2sY9tTtKRV4/oQEfHykL3ZJeAJxvg0ZGlNUlHfyprP1Cn1uT+1EBMreHB8o7ktsYm6aBMzH2jGTwV1atjk3RUifrhoZGpUjOq2o2q5eChjUk6la+5TtLhAdX+VOCF6Ys1U3a+HKcqZ55da5IOLzI8e9dWJ+m4Dn8bm6RT8+ExtSYm6YxFJxxXrns+j1OlLG9ZtKDlccqjxkScGsHI+XDnjwhGzN1t5cMjWuAejuXDY2qNNW9RQYLrVqGdZ9xmyO/y7HnyrQhGzodjL8uTdDo6VgW6J7tEPFlQMas6VT6uNbGGl6WqlOfamqSj5xBLFO+QF8zL5CWZWiMf53WeGS+iTtJRhcyb9v8ZMbWGByRX1uov5YE897nPHfJ73lMZeKyvylgep/xcnaSjT5THy1NvTdKJ/tTwrMjEM9UHrAqbxxn9qXWSjnPyhjIjUsA1H3vssUM+kZffmqTD85Kv1gtaJ+mQWa5QTtramSQjFxuTdKI/leytSTomBEV/aj5mRDBcw5jHqefZ/dtvv/2ak3TkW+WKnVNkovbfRj7cc8GblCuOCEb1OOskHfc796fWSTq+S/qMc7V6gGfOi7ferWdc3lneWA68NUmno2Nlo3uyS8iTBV5lixA+ptbgNW71aIbHyfsZm6TD49NvODZJJ/oaW0T3uT+1RXTPA6r5uDxJB6PRmMfpmJicVK+2CNxVFMtHjvHQ+gzvf2ySTu5PrZN0VLyOeZw8KIMF5vM4eYS85RbRPYTc7mudBRz9qcHMVCMYuT81y8S743nKh7f4qyOCYc3ny3F6fWySjudEPly0oDVJR5VwRCfqJJ1WPjxP0hFRqdOkcrTAZ1szkzs6VgW6J7uEPNnanwo8V14JD0BOivfF0/P/GbxbvZXez9vI/akBeTTVnTxg1cY1f6t3VbWwfkPn4XHmdZEv44ne5z73mcVDG55Y9KdG9ag+WtW3qkB5d9k7yjLxpOQtyfOEJzxhjty8raOPPnrwNM3LbfHQ6ifm+Tin68+5azlS7FkqcuUzVSmTSZ9mKx+uP1Ve03X5mzWR19Wfmu+TPLl/vV/Pb/XgeaOqt+We5cuzTAEsUCqBecrOUb07Xp0cpagEmXjVEcGo0YmYTxwRDJ6w5ybnwyOCYR2sp3uZ+1MDvHB91riDW7NdfXfkX3mcnjU543z9Kqq9R+U6mXi6nqeIFrgf1lYumkytCEZHx0LTA31AwCpc3NUJG6/NSKizEt3njZfsNmibr81KWNKGlTfeSnSvFUhIL4gBAqHMbYheo4QqvEfri+Iiod1KXWc9tSPZ1CvRfWy8ZBDmrUT31r+GhTPRvSIf769h4TAwrIHz7Lnnns01JQ/FQtlXonsQwrSmirayTMDgoFAUIlWie4pYyF2hVxQiZaJ76+i12iYVBga5ya/AK7cJBRRGIYpQ9NQiukeQEfeYYgzy/RwWdj8RWoSB4d55DmpYGBgM1sp1KaRyz2qbVChz56MUHavCc6h4SoGYsHI2UEDREyIWRVHSES0Do6NjdaAr2QWyuKsTNnITXXAMB3NRBo5eOTIeZ2uSjmpiXoXP24RV2eapNZGPC4YcSgADD0Vgo8vVwnWSjuNSBi2PkwfF4/Q6j7M1SQcrkfwgJeXasscdG6+K3BYPbfSn8ppak3R4nLk/NZSP/KV77f277777HI/TejuXzV5PcK7KDfBGecQMlBY3LkYka0JxVQOjepwhE2VOAfI4Q0FmmRgYrplRo2q4lQ+nzE3SwTPt/lXoU1ZFrYKXh1gNDL3GlKZnpTVJR85Z1MMaZwODcnWNma+5GhiUtOemTpMKA4OnzOixBq1JOh0dqwM9J7vEcrIBLES1PzVP0tEzOB9XrXmd8mStSToxGca/rUk62HTi2JmHNqqPc39qnM955GxjAHyrIhbHrBwn9qAxXmN5Xb2eYzy08oPkiBm3uQc3+lNDJn2uZIk+3tyf2pqkM5YPt06qea35fFW1emvHJulYT+eQl44ZtxnuR6xvnQWc+1PrJB05W+9v5cNjkg65xoaVu0+YmjwrrUk6vhvyynFfsWVlyOvLb+dJOnnurjx0VDWHTNib6tzdjo41hZ6TXSgWzBpA9KeGl5on6fCkeE+8iOxRuRYep3Cda8o9hwHeFo+TdyPnVjlfeSw8TufhBWce2li317/+9ZMdd9xxDg8tWaI/lfeVeWidR0hVSDn3p4ZMjsPD4f3wKFseJw/Jzy677NL07FQdy//JzebpPpkRSW9reJx5kk7uT80yBXuSSl2h3eBrDpn0jqq2tZ48xZYnLFpgzVWO84QrU5FrVoHtGK1JOs7hXK6pNUkn+lPlw0OmmAUsfNzKh8ckHevIS898zQHXrgJbxIG33GL+8qyQ2fpZp/xdEp3gSYtOiLDUSTodHWsaPVy8QBZ3TcAGJ/dF0Vai+5yPs2krSFL4FET3Ntu88Vaie0pOeLRuvKHMkVdQcnKcLeo67SfeZ/OtRPcgjEihCLMK8dp8g+g+h4XJFKP6YixejPEjYyW6p/j9PwVeC2TcR3KhBpQ7bpFAIGKgcBDqV6J7UICjvYiiRvVXR/VFm5TnpRoYwuU1H54NDHlV+duaDw9l7nyO3RqwzmgKSkL3tBoYrl24PMbZ1VF90SYlB0qhVwOj5sPJ5Djkdz73q4aFQ5kLIQsvh3FSQckKsyvYEpbuVIgdCwldyS6QxV1TUORko+NF1Ek6Njk9oth9VP7WSTqx8cqPtibp5I238tBS5rzVvPECZa5imLIil6rZVh+jAhtKn8JpsTnxRm3ucpEtHlqK8MlPfvKg7CgpmzhlzsDI/amhEJyLMo+q2tyfWg0M53Ws3J8aoDj1KlN2rdmt8uEmBJGjNUlH8ZicJ8XbmqST8+HVwFAY1fI4w8DgkVr/WogU95qn7JrMdm3N0+VxMkwij58NDIYPzmXRCcperpZMocyxdrl2fauxTnkWsM/maVJ1ko5nM7NxdXQsFHQlu0AWd01BkYwQZ8vj1HbCw6DkeJwtZUfJUoiKgSrRvY2Xx0mB8+Aq0b1KYCFMxAEg/KgYR0hUUYvQH287Nt6QKUaXaUtRpFTJEhyXx8ljPfzww5ubr+MwMFSlVqJ7UAVMcfHwKtE9CKUbkE45V6J7ayoUz/OKNc2j+nicCq9yIVI2MKyBIqdWNaw1DY9TyLcaGFFAxksX+q+j+qJNyt88n9XAqG1SeVQfeUUgWh4nA0MoncIL46TCs0R5Kh5rhbQZAJ4P56sGRm6TCqOHgeHe9RF0HQsZXckukMVdk8gbb2uSDo8zb7x5ko5N03WqVm6F6bzHhsiDaU3S8RrFo6LU69pv8gZu4zWjlLKrk3Tyxhsy5Uk6cpwUe/SnBuR0hSCFq72vtn+AYwgJ8+pi4lCGDZ6yJw9ZyJSn1miTotyFn1uTdHKbVMgUBoYQeSsfHspcOxVvtvI1hzI/8sgjB6+Ux1kn6VCIqm7lLMMLzvc158Njuk82MKI/NfLh1cBwP7w/cyVHBMN6eL85vjUf7vlzXobV/vvv35yk4976Lmk7ak3S6ehYiOhKdoEs7ppEhIUVKVE+wofRQpH7U4WFQV+pXCgvEkE7L4ZipiADQSpvLB4FUzdeoCR5UjZuHmeLdpDHKffrs3KNdVQfJU5O11CJ7kG70LbbbjvkQSvRPY8z+lMD2cBATu/8McavGhi8XUVOrRwneVBB8uwozkp0z0OnnHiclHUd1Zfz4UF0nw2M2iaVDQwGkQhDKzpBiTFsyDWW4+RxyocjkhD6rwaG6AQDgfEQo/pCmed8eJDvh4EhHdHKh+dRfZ6l2iYFrst9EV1AYdnzrh1rC7qSXSCLu6ahEtTmyfupPLSgcMaGTsG2JumEkqZcbIZAKfJe8sYLdZKOTZdXJeeWwbv1PseUw2xNSInZrZSPz1cPybFVuFJQlEWdpMNYcI8oytYknVyIBDG1xrkwGdVCJAhlTpE5Vy1ECgPjiCOOGK7JmtciqvA4rbVrrJN05COFxM0Ibk3Sif5USjLLRNkzhnic0Z8aCGXOS+YtYpmqCta9o8ApRtGL1iQdilU/rHB+a5JORCeEtusknZoPJxNDjBIPA6OjY7HqgbkzzjoWDWzm8pwtMngbpgeE90ahtWgHeU9ICyjiSl3HixSeVJULiN55kuHNOKeNl9dGKWWieyFTHmfeeCvRPUWjKKkqWAqBAkJrqBK55XFSOIcddtjgwVNSNbQc7FL+HkT3eTydYyouUhyWie7loV2L3CyFGG1SmeheLtoxW4qDgSH0y6NseZyOx3CgqBUH1Typ6l9GE4OJ5+q1kAnkMuVXhYXJxMAQwYhogfA1xc2rDGQDQ3jX2ldQ5vLNDIPnPe95zZYca3nooYcOa8cwywYG+YSBnZ+B5PqrgdHRsVjRaRUXsSdb83FQJ+nY9CiMvPGC1ylgLSaUWQ3pAsVpY1VRapOtCpHnJNzJY7HpO1+epEMJ2GgZAi0e2uhPjc/EdB/KXD5ReDX4muskHUpOGLLyNQPFKZTN4+R91ZA2j/PFL37x4K2FZ5497mhnoUxak3RU/lpnodnWJB2tL7lNKhsYFJ0IAJapGj61hqqUGQAqtFvGEw8fKMXWJJ0oWvNckClHMGp0ok7ScUzXksPCYWBYJ96yZ6HFI0yZM6qsWSuq0tGxNmHJMj5tttlmAxtT/jGDM8PUj/ve977TddZZZ3rzm998etRRRy0axqcxYNP5whe+MDpJx2SZYCUy/QVbT0ytMdnEFJuMPEkHk1TMT21N0tlll13mnC+/xwQgzEEtdiGyxKzQLFPA8YNNqDVJB5NQvk95ao1JNDE/tcLUGhN+rMsYnAfLVGuSjmM6Njam1iQd7E5kqzLFJB3zU88///w5MsUs4Dw/NQNLkuPstddeTearYKvC5jQ2ScdsW+cwk7Y1ScdUnPhcnaSDPauuWczdjVnAHR2LAUuW8UkFJ0s5inlAGDQ8FdYHD4g3Yaam8J3cGTL18PQWmycLvCO5NjkyFZyV6F4Rizwmj7Py0EY+TmWrPFtrkk70p8Y650k6vMEaFs6TdIQ8ecmZESkgpIwE3+uOX0PavEU5Tp4RL7gS3evR1PLCc6tE97k/Nfh7M9E9EgRy197QmKRjnRy/5XGSS25V76gcaGuSjmPwtkUM3Jfc2hNtUkLm0SaUJ+nUNqkg39cGw+vmceb+1JApogXkzTnqDJ653ljebKvdiCdLdjK2JunIt1sv9701SaejYzFgSXuyLOsxsMJvfOMbD7yugX322We6xRZbLGpPFniTMT+1el24cZ/znOeMepw8kPA4eVnV++Ot8IyChxY3bvDQgvOGp4qH9uyzzx64jGMNeanmv2aZPvjBDw5zU3HsxvzUFlczuebzOHnbxxxzzCxu3Hwe/L68TDLxELMcwdMc7+VdkuXb3/728DdefJ13+6UvfWnwOC+55JLhGmN+agYOZ8/iHnvsMa/HiR+Y/K05qbx6niRv3vVX/mTeI88SagSDJ+r8GTlawJPloVdEBGPvvfce1r4Fa+x16/Td7363+Z6OjrUdK6IHFp2S3Xjjjafrr7/+9M53vvNAKp831m233Xb6yEc+ctZnbFIWa2yoOaVgIePHZrY2Ktm68cZGTskgobeRB1F+azBADD5vgdFy4IEHDsdqEd0LiVLQFKcwah0aHmHhTHSflTnl5vdKdG8YAiXl/+t5g+iekqRkW0ra37xn9913bxLdg6EDnpEqU3ze9ThXGBhvectbZp45z0iEhVtE9xSxY1eZwsDwc/nllzflEv5/2tOeNgwEaEHo2X175Stf2TQwhPgp0zB6qoHhfodyrwaG1IL/rwgD44ILLmjK1NGxWLBklewLX/jC6Qc+8IEhR2STWG+99YYpKIEHP/jB0x133HFOrsti2bRaOOSQQ+bkeddWJWvjPfPMM4dcYWuSjo038mutSTp5443N14ZKuVq/MY+SMg+Pc2ySjk3+uOOOa07S8RmbNw+sNUmH0vW3LFNW5n438afKFAaGa2wZWf5GSVHCOfqRwSg4+OCDRyfpRD58bJIOZRX58GpgtPLheZIOJf2e97xnzjlDmcvNzudx7rvvvsMatAwM+V255JikUw0MxoHzj03S6ehYzLhiMSlZ4dyWkss/wmEtnHzyydNrXvOaw6ZzVZXsYvJkgUdp02wpFWFhr1NK3kMRtTZeMArOBu29sfkaX5ZDzqHM3/jGNw6Kk8Kq6xbKXKjy2GOPHfU4HUN41ei7FrxOkZGpjuqLsDBF2TIw/I3XV2Xy/HiNguFdVpnCwLBmeYxfBiXnmcvrlOF5oqDJ1DIwhG2FhcneMjBydCJG9YWBkcf4ZYQyp4jHohOU+Z577jkYP62QtugEo5YXTKa19fvQ0bGqleyC75M1vkybxXxojS8D0zsU/egVxFKk0EOLR0b83ioCAcVAlcZubYaiMC0kLW5YfZAS+kgJWsxCCpAUsmhvQVuo8Cavjf5URTOKXrTaVKL76E91Pxl4yBXQP8bUGhSE0Z/amqTjHsaxMhQfYFtSyKTHtN4v14GdCjUhuWsxk/5UBAwKthyHXHlUn55VhUieJ8fOk3T0sipEym1SdZKOYjztMXU9FSq5XsVGCo1alIKK9A455JCh1UlxWH3WY021C1kna5eJNBT6IerXv5on6ShE0++qTYps0SYVk3S8F7e1lqHaF+veuW+Kv9wTXModHR1tLHgla/NrMdAsD2wuqlej6d3kjwMOOGDYSGLj0Ktpo1gqhOSqPG3m+hZjcwxSea+pQkYoj70o97QG0b2N1f8bpVdhY1cFe9BBBw0KuCoNFHyICWzQlIuNPytz1ciqhVW1Og+ZyEB5UW6Yj/yN0qpE95SN6mcGAgKM1iQdx9Lv2brXKpSPOeaYoS+38gv7f/22aCgZGaoJXV8Q3ftdb6hKYcQWrUk6lLQqZtdfDQwVuka71fUKA4NsjtMyJpE+YOxyTyjqOklH/67qeUQZMeM2X7/frale6Jikkw0M57Z+DI06SQcfcadC7OhYBqaLBEKEwnaKOYQUzzjjjOlGG200fdKTnjTzHmE2hVEKoC699NKhUvK6173uEMZc7NXFNdQnXCgEKY8mVJv7LhXbqGoNyL1Z2wjL53xcQEGM/KICmVZYGBTWCIvuuuuuQ6VqC4pnnv/85w/HaIVghYWFsZ3fNeScYy5EyjIJ9Xot96cGyKkKWOjUM1T7U0GeUUETuefLcSr+IpM8ZoVKW/lw+U8yfuhDH5oVPhYW/uxnPzsjkxCsoqy4ltyfCkK4Cr9cj5BzKx/u+Aqjnvvc5w7XOAb3Xz+5cHsNaUc+3L0QPheyHstPd3QsFVyxmHKyywu5wHvd617TG93oRtN11113uuWWW04PP/zwmU2qRUax6aabDoQCK4LFoGThU5/61JBzG8tFI3aQi6SkKFWKuSppikVxjryd94fiVAgThUiR65X7C2VuM9f+0rqHlLmq8LFqb7ngpz/96XNkCshFUmaMBPnCev8VDFGUZGdgVGXuerMBEMqc8netrqEilLl1QOLQAgPDejMyqkyxRnKc5KM4a+tPtEllmbLCz21SQJlnA0MBVX1mw8Bw7xyvlTO2TmRGztFqR+roWIq4YqmSUawOrI1kFGMwzUZ4tJI8CBXLxQkp4zVuzW5F7oCUwFq0eGiRMchZWq9KdB9E+ZEXRIaBuCAm6VjboC0M5Kk1QrPSADHGLxBTa+SDHTvPig0IMePfFX5F7lCJ7k2GueCCCwbSitYkHaFgJAz+HjIJZcfc3SDKjzWLqTXy3fLCaA9jjF8GXl/EGY49VoNgVJ61kXetk3SQTciHI+1wX4SR8yQd4Wuye11omUw4iWOSTozxw4/cmqQjpdLR0fG/6FN4ViEWk5LNG29rkg52oLrx5kk6mIYojJifmoEEXqGRTRzzUJ384hzysjb81iQdnLm4gCmdSnQfY/wiV1uJ7jE25TF+gZha4zPym5WvOQwMfMwUtPFrdZKONaLMvG4iTTUwnAOzks+qCSATrt8wMBgAriHy4WFgxCQdE4AyX3Me1QfyqrvsssscucllCo97pliwVcdg0o57xUBiYDBqAtnwkeMmU56k09HR8X/oU3g6lgs2YkqEsvDQBNF9EMBTBjZeBUo8TpR6Nt9b3/rWQ3FQeJxoCyvRvU3bfFLeXWu0GiWrGMmmnjf7gIIgXrTKX4ozT9KJQiQeG4+uNUkHoT9l4titSTqUtEK4UKJBdK+wRwERZVgVLJg6pPLWhKAg0s/gZVOKxx577KBIFXbl62dwOLc15LnWSToMCbKalxuTdMhEmbsfFDgPU5QgkMfiGejQqoan/ClPs2dRVdZhDtbGvTe9iEFQJ+l0dHRcNfRw8RL2ZIMfGI+zDZpyrZvvV7/61cEj43G2eGgpONXAxt7VSToUbfDgRtWrSToUB6/NZ4Slq8dJGTiujZ+Hx5uqoDgNUKdceY2VG5e8lBlvzDXUSTrCt0Kh5spqoeGZUzImAoGqZR5xTIyJSToxtUboNoeFQybvEZL2nFCwFdZQJbF14pG2Wse0A/GORQzqJJ0ICxuVF/zPqpLJ7t75m+rfMHzqJB1haQaTdqQsUyhzz0BUFnd0dLTRPdmO5UYenF0VrA2dMjIUgMdZ85fhcR511FFDXpcyzIMAbOx6O236PM4gus8eJ2VCCVIU2nZ4p5QUT47ijNaX8M4y0T0CfR5ci3xezyuPk/x1OAEg7dfLal4uzy3LBJQNJS1cTX6KSJtLnItnSXlR8FmmMDB4xdGfmr1gykzeWag9h4SzgUFR+ixPvt4Tnq915o0yMKqil191XC07ogXVwNBPKzqh75iyjVF9lHmdu9vR0fHXo3uyS9yTbRUiAYWhOEZxkfxlLUQCyoDiFKLkzdZCpPCUDz744CE8SiHVIisKlSIVWuXl1kk6Qtn6SYWeW5N0FBKRLwyAOkmHx8mry6HPmKQjrMvTE/quoDh5nM7Ja2xN0jEJx3l4h4yIPLUmPE7KWx61TtKp+fA6SUdvKmUZ/alxzFDm1tX9aD2DwscKsMYm6USoXv65NUmno6NjfnRPtmOFwONEQIDFKDxOng/lEmPxVOPanIUSbfDyiRTUE57whEFx8voUSIXHSXFjcEKUQJGo2K0KFoQojWVT2OR8FRQTZYRlyrmzTMD7yh4nb3ibbbaZORfPkvIiJ5liVJ+/U5yUtt8jTFwNDNfYIq9gYPCEKTQD4Mc8Tq+5hmpgyIeLHrhua1ANDJ9l+DA+GD5hYLg/jCFr5j7lfHg2MJzPfaqgzBWcydFay5Y33dHRsfLQPdkVxGL0ZAMoCRUaUVK1Yjg8TgU3QpkKj+RUA5SFghwMQuaZ8jAxL/HEKHFKWRtItIJQEhSCYh6tLTxO4V+KJxDKnCfo/1s5TorzjDPOGEKxlA+FVnHmmWcOChqjkXBp9g6jWpg3G1SIDAwyUeZyscKpKn6zTJQvj5PCYoDUebChzCk17FAtA0OoloHAI60V3CDU6/g8biFoBkU2MMjKo/dazLiNWcARnVD9LR/ud206zhkGRkdHx1VDb+FZhVjMSpYyk2esYWHgxaJbFJrlPbXo9PTdUlo2cJ5x5rxViERJUzg8U+tHIYQy158qhPm4xz1uUAj+XxhWTpeX6jOUukKmQChzOVvh1Vylm69JOw9vUY6z8vCCiluFTpRrlilw1llnDSFqhgQDg0xhYORCJAqQYiRrGBg83hwWrgYGT5JSrEo2lLl1MBied1rhPdqNyM0LrgaGvKxr0wpFhmpgdHR0XDX0cHHHVQKPzA/lpaimEt3LrfI4a5sIL1eoOcBTqhBO3WCDDYYWEdXEcrQZlAilETzDleheLjOGD0TrUCa693lhYV54yCR8TH6hYpy8QrKZzJ4yv+iii4ZzUpCPecxjmuFTxUsUNEVJmWYDQ1iYbOQhlzBwzm3nNimVxGTyBZVjpszD44w2qWpgKEhyXdtvv/0smcLAEBlwr1oevKiEfDYZs0wdHR2rDz1cvIJYzJ5sKCdFM8KMvLtKdI9kgsKikCgE4UyKKibpULY2/dz7GkT3Qs1CysLCtQfTevIYecwUWiW6B5/VakNZ10k6ERZWuUuhCQ2HTMDDFpqNKuJMdK9fVt+r3lhVzYEwMFw75evf3J8a6+WaVTmbWtPyOIWMXROFS3FWA4PCFO6WF3YsBoYWm1DmZBCOFkXIk3RcH+PAdeWBBYwg1xbRAsq2o6Nj5aGHi1chFruSBQpDkQ3vqTV+7bTTThs8L32mPEMFQqEQIiwsxymXSEEIh1IuvN/cnxpKKk+tocApqhwWzspcLlR/aYvqUcGSAinerNBoDWnzZE3KcQ/dOzJlZa4/1bUI44ZMMbWmhoXJRJGHMvdMUJCVEjEMDPK6JqHtCs+Snl8KnIdbDQxK05pSzkLN1cBgMPB8/T3LFAYGWfu0nI6OlYceLu74q6CCVdFOKwRpdirFyGvUq1nZhSgg+cjjjz9+UGQ2fp5nQL7W5/WDBk/wv/zLv8woc+0kPDOeMI+TVyo3Ka+o8CmIKqIQqY7qU5DF66xKhfLnJSv+4XG2rk2YW/EXLzHLBEKuCo949wwLhUa8yzyqz/kpVTnkkImBgf6R4hQWpkgjbBsGBi+aUaI9pypYClIemHIlcytfHuxRWKasfWsWcFe0HR1rBj1cvIJYCp5s7U/N5Ps8UVW1PM7cnxpQaKOylhe72267jXqcFI5jU+hVIWj7oayERlUbU35ZmetPxRiFyEKrDf5fylw4NvenRltNJrqnQGshErguuU+fEUJuVfsyMAxQp1zJXg0MOWXFXzxIVcnVwKDkXVv0wTIwVDHHIIPgaw4PNBsYPOwIC+fzhjL3N97rYn4mOzoWCnq4eIEs7tqO008/ffDoKDEeYJ6kEzlOHhgvT99lTNKhAH/wgx8Mn4lCpEx0z5PjbarQzf2pWZlThHvttdesNqGAkLEB6+5Da5KOsDAlzeN0vkp0r9pYHpZnWCfp+LcSc8QkHevg2ijH3J8aELI1fUhbTWuoPRig7ng8WjJlAyPy4cL0ZKqTdFyTsLl8eGuSTkdHx+pBV7ILZHHXdvDcVBRrx8GNW4n+hTFt8tpkWpN09K8i9+dh1kk6uT81PE7EDkKmlDkPMPen1kk6PFaKstWSQnGSmweoJagWWfE4TbtRoKWQq07S0YYjdyv07BqrgRH9qVHAFDIFzzLjI/pTs0w+R27nbw0XsCaqt4XEeeKtSTraqJyXXC0Do6OjY9Wj52Q7VgrkEynJmN9aFYJcojYSOc5aMQsUqtys41RuXIrVsSlfRVQxSScrXf/yHjfddNPBe/WemFojjBytL0HQEJN09IeqYOb1tSbJCFdTjFpqWspOLtZ18TqR84e3HlAwdeKJJw4eZ4SY/X+cy+uKoCjvIN/PBobPBF9zII/qY2i0DDgyq3gWRq7TfTo6OhYmek52BbGUPNlWWLhO0uFtUQ45LAy8Md4d5cgLzkT5Aa8deOCBgyLjcVaifwxGp5xyypCzbE3SQTWo/UUuMojunYsHG/NTVfNGkVOepON9KnZr/2iM6vMvr1PRUmtNeMKOx2NtTdIhtx5Wip7izJN0IiycC7nyJJ2aD88GRo0WdHR0rH50T7ZjpcGmT5nqT6UohTzlSXNhkcpVOVgKkGIMonuhXt4dJS33Gu/PRPdaXnhxrUk6WlNUIAvltibpyP/K3/JoKeLsBYN85qte9arBy1QNXCfpCIPL/Xpfa5KOa+UVK/AKZANDgVf0D2dQnELtKpExMlVeY0pdTtZQe+1KdZIOo+C8884bjmNtGBLVwOjVwh0dawe6J7uCWGqebEBVrc2fx1lZkXhacoVylkgVKjeuv/HOVNJmovvg+43+1PiM/tSYWiMXyuurgwFiao37oHio5XFSnDxOBoDXhZ0rXv3qVw8FUliW6iQd/anCwpR3TBxiYMTc3ehP1UsLYWAoSqIUGSbWJIeFQ5lbB3laPb+Zrzmg+libFAWcWaoCXcl2dKw59MKnBbK4iwnygbyrWogUs1tPPvnkGTKFFuQ3eXCUVCW6pzApO3SLlegeeJfOIdfZIrpX0asASn9qa5KO9p0WrSDFqR1IbrnlcYJJQkK3cqlkqgYG4yLC1XWSTg4LMwSqgcHwQLDBcAlkAyPG4nV0dCws9HBxx0pHFD9Rtv4/E90L5+Ik5vUJyWbFEET3Km0pVgqpQosKBWgsHLKFShQhtyk0Gz242JyEqwOUL481ZreSKY/qQyuoEIl3GTKFMucp8jgVJkV/ah7VJ1zLM6UIa18s8DKxNQlJ11F9romszs2btm7ZI3edvFqKlcHGC2ZwtObudnR0rJ3o4eIVxFL1ZCMsjFJR5W5w4wqVxtSa6E/VW9uapJP7UzNBAyWkQIlXRwlVj5PiFDpVaEShtYjuebfORaGSqd4bnjYlS0bKOo/qqx5nHdXH41TAlHtf8yQd1yo/HGP8qoFBWSvwym1CAblbPb8KyFqTdDo6OhYeerh4gSzuYoTqXD2aGJNyQVBAnyflJLSM5D4T3UdYmCLVipKJ7ilOFcQRcm0R3fM4c39qnaRDYe29995NJUxm5xbuzjIF8P1imML8ROExFPJxGBeMBR5mNTAyXzNvvxoYPNfM1xwIZS5vqwK58wt3dKwd6OHijlUGHiBvteVxUTy8QtN0eJyVh1eBD4Wi2lg1bSW6VyCkHYiyk1OtRPfRn0qZUUh5ko6/xe+O25qkY0IPZqSqzMgcOWceJ++0ghds+ABPuU7SoUTlfnnTvOOYpJNH9TEYVAs7dp6k00fQdXQsbvRw8QpiqXuytT+1Et2Hx5n7U+skHWu47777Nj1O3L7yq2bA8oirQuQhCltr7WlN0tGfqiCKx0kmxU2hzGtYmEw4mBU2hTed+1MDobxBtXOLZcnzQEFTthRyNTBUEqOplJcV/m6N6uvo6Fg70D3ZjlUK+UcFSB40Vb9BdB9h3uhPjQHndZKOPGz0p+aHlnenh1X1rcKl1iQdSlOF7kEHHdQkgaC8eMqUbJ2kQ4lS3HKkcsRk0vubp9ZEIZKQcZ6kg5hfQZewsNxrEHPkUX2IKRRKtSbpCIPLKfOmg56xo6Nj8aN7siuI7sn+LygVY+GEjynFSvEnB0kp4gauRPdRiBRtOpXonuIyzSbCwnWSDiWW+1MDvFtVw0KzPNOYblPvH49TAZbz1YpheV1FVnpqW5N0FE3xRNFEhkxZmQdfcxgAYWDUubsdHR1rL5akJyuXx0toQUjQ6DK5xBbHrhBma5h2xzh4etaMR9jiNfbwKeqh0KLlJ4Pykl+Vm0XukFt7eJxymkLDcpxaW0zSydSO7rcKXwowJunI+ap85nEKC1N8odTyJB3vMQCg1ZKj+Eoo3HXp263Q33r00UcP83arFwxCxa95zWsm22677eAFMzAcJ87Fq4WubDs6lgYWjSfLe+FhZAgpyg8KT9rUQsniuLVpB2zUrfxgC92TnTvNJhf45Ek6lF7MT82IsXiUo9dySDd7lBQ044jHWon+KXEtQWgJW5N0VBv/6le/GqbitCbpxBi/mHRD+ZJJvvV+97vfoKTrYIAY1adKmjddZ9KGMke84Wvl9dYknc7W1NGxdmNJerI2vZyjE3IUOswhx6xUW/m8MUXiJy9ux/+Cd8azUwXM46SAKMzMIRzVwop8FCx5j/uBBIJhkwuR6iQdPMOKjlqTdChW+WBKtjVJR8Uxj5PSp1irwqS4hXZ5nGSqk3RwE6s2FvptTdLxWeHk/BzFJB356fnyrt2L7ehYOlg0nmyFUJ0qUZtx0NyFJ2tj5o3I2emrlFccg1YUlHsVS7m6OMPjs//++w8eILKGOiGGgkV7KPzamqRDQTOI5Ctbk3TQFro/kRfNk3R4qdHSkxVoKHMGERpEinTM4/QcYJlqGV0MAJ60wq46SSeIObTgkKlO0uno6Fi86GQUk8kQCgSVnwGVqfJlqO5syhQxb0eYcEzRtjxZSror2f8D5SlHmftTA6pqkUAoFDL1pqWkjz322MGDNE9WSDcrKestLIxEwn2iHCnqmKSj+Mh75OOR7/M+ebgxSee1r33tkPPNva8GFvBMKWlV0nmiUEBPK2+VMmVEtBSnfljXLiTs2vsIuo6OpYErVyBcvOA9Wf2URx111LzvUcSirSSAtUf40jzRWoFaESFJbR3Lg56TbSNoC2P0Wya6591hgqpFQjFJh/HjgW15nBQnjxPPsGHtrSIqbTUMJ6FaijNP0pFHpSyFlPXx8jjzJB0hXgrV73mSDmVOUasMFjrO541JOp47+dvWlJyOjo7Fi0WVk91jjz2G/N18qPR+QozCi/OFgQMo9ng1HX8dGDPnnnvuYLQE+X4muo/+VO0+rUk6Qq/RnxqIqTUUmUrjVkjXZ4LwX6FU9TiFi7XjmDnrS8EjzpN0KEjtQgqtFEvVSTrki7Bwlsl15CEFHR0dHWulJ7uicDnydcJ3wpDLgvYKIUd9l8uD7smOr7tQvCgCj7TSLnqdxymsK+/K81ScFKDkjJ1D+hDk+3K3MRbP/UGRyCuGUKzCue61/xdqzorYOYWDeauOLyrSCvtq4UIFqRrYuL4K8irSQvXIo62j+jo6OpYWrlxMnuyKQk5O+FcvZIW2DVXIEU60kaPhkzPs+OuBAJ9XKGddoQBKm5UiKDnO2jLF46UgFRvJzxotlx9egwG8JvKAEKJO0hGqln/FugRypfLxPGiFUVFUZUZrIJS58+rLzQVZAcpcaxgFa8B6L3br6OhYEVxzMeYGbaw5R5vxvOc9b6g45ol4j6KaPMKs46pBrlVLTxDly2PWSTpaZHicPNIcFo5JOu4Lj1P+tBJchKKloPEa595cEAKWNhBWFvJ1/Ey+f6c73WmoEtY7629kYo2qQGeR5mrhkMn7KXP8yGPD6Ds6OjqWVLh4VaOHi5cN3MEUKiKKOkknFyJl8n2tMhikhHYVQmmZaU3Ssf680Zqf1QakSElBkparTIUYMDhdNEMVcJ2kE1EQU3ZiyDvvtjUWr6OjY2njysVUXbzQ0JXssoGv11g4udLWJB1hXEqQx9mapCMsHGQVQb6vsCoILarHSZGHMldwJS8sb5pBmVOcXucJt0LawsKHHXbYUNREpuVlAevo6FhauHIp52Q71jwUPSGT0PfamqQjhMyj5HG2aAdV9yqSonjrJB1eqOPyeHmcvFc59twehLSfxyyEHJN0fBEUVWnNkYc3ISiQJ+mg4mx5wR0dHR1XBd2TXUF0T3b5UMPCEFNr0A7yKHN/ap2kIzerHUj7TYXwsXYdrT1abCrRv37Y8JZbk3SElCldlcTaduReqzLv6OjoGEP3ZDvWOHiRqBRV9QrzInioHMLCwlp5sDdRyjzOmKSD8CGHhfMkHeHgbbbZZlC2rUk6XjcUgHIV9q1AWHHkkUcORVIx47bnXTs6OlYFuie7guie7PKDUtxvv/2GkXBabCrRPy/zPe95zzBDVh63TtLRcuM1n29N0sFbnAkvYpKO1xUsCQtXwogYiydE/JjHPKa35HR0dKwweuHTKkRXsisGuVPKNPen5te0XClq4l1W8GZRalKi2q0QUeTWHjlUYWEtWDFJR/g4lDmlzEtGepEn6VRl3tHR0bGq9MDcZsSOjpUItIUqe3mQAUoXaYWc7OGHHz4Q9leoIsbCpNBJXlZLT+2d1dIjNKxIyuuqhrO3rEo4el1f+tKXDlSJcsRdwXZ0dKwu9OrijlUOoWJhXB6kf3moippikg4vlSJUJey197///QNjU0zSMVyA5ZgtxmBw0qqDyCJP2QlQ4gqstArlGbcdHR0dqws9J7uC6OHiFYdeVp4rb1YOtVYMU6w8TeFgChZblDxsADnF29/+9oFbuDVJBz8xDxahRZ6ko12Igs8DATo6Ojr+WvTq4o4Fg+A6oSBVC7dacnAIU4zyq3vuueccj1PxE2IIHi1lWifpaOUxZUelcNAq5kk6HR0dHWsKPSfbscoVrPYYoWFVv3plM/m+mb+8VK06Wn1wC9djKGBS5GTIgAk/1TN1/L/9278diCSElBFNdAXb0dGxENDDxSuIHi6+6oiwMIX60Y9+dGjLyZN0eLQYnAxnr5N0VAgrYuLRas+pk3TkZOVnMz1jR0dHx6pADxd3LEioDtbnqndWj2qdpIOVSbuOwQIf//jH50zSQVxBSZvGw8Otk3Q6Ojo6Fhp6dXHHaoWQ8eabbz5UFFeWJQQRcNJJJw0jCYWPM7wf/aEpP/K2rUk6HR0dHQsJPSfbsdqBfEKIN8ArFT5WgayNR7+rIekV+m2Fj9E1asnpCrajo2Oho3uyHasdwsIKlChNIWRhXy07FCdv1fD1CAvLscYkHaFkk3RafMUdHR0dCxG98GkF0QufVg6Mu9t///2HIibFT1VxfuMb35h88pOfHP7emqTT0dHRsabQC586Fjx4qIgieK4tz9SEHcVP22+/fXOSTkdHR8fagJ6T7Vhj0Jqjb5ZXGzBJBxcxJWscnQKpjo6OjrUVPSfbscbAi/3Xf/3XISerxzUm6fBe61i8jo6OjrURXcl2rFGoEH7DG94wjLzT77rJJpv0O9LR0bFo0JVsxxrHLrvs0pmaOjo6FiV6TrZjjaNTIXZ0dCxWdCXb0dHR0dGxitCVbEdHR0dHxypCV7IdHR0dHR1LXcm+4AUvGPoqr3vd607WW2+95nu+/e1vT7beeuvhPTe5yU0me+211+SPf/zjrPecf/75A10fAoTb3va2wyDxjo6Ojo6OJa1kERYYj7bzzjs3X//Tn/40KFjvu/DCCyenn376oEAPPvjgmffgyvWef/qnfxpmme66666THXbYYfLud797NV5JR0dHR8dSwVrHXUxxUo4YgTLe+c53DsQG3//+94cB3nDiiSdO9tlnn8mPf/zjoYLV/7/97W+fXHrppTOfe/zjHz8c613vetdynb9zF3d0dHQsbVx55ZXDDOsrrrhiGHayKDzZZeGiiy6a3OlOd5pRsPDQhz50WIzPf/7zM+950IMeNOtz3uPvY/jd7343HCP/dHR0dHR0LA8WjZI1qSUrWIjfvTbfeyjO3/zmN83jHnHEEYPFEj+3uMUtVtk1dHR0dHQsLqxRJbvvvvsO/LXz/XzpS19akyJO9ttvvyEkED/f+c531qg8HR0dHR1rD9YoreIee+wx2W677eZ9jwHeywOct0ajZVx++eUzr8W/8bf8HjH1MUJ6Vch9SHhHR0dHx1qnZDfaaKPhZ2Vgq622Gtp8fvSjHw3tO/De9753UKB3uMMdZt7zjne8Y9bnvMffOzo6Ojo6luyAAD2wP/3pT4d/tetowQG9rte//vUnD3nIQwZluu22206OPvroIf964IEHTp75zGfOeKI77bTT5GUve9lk7733HsapnXfeeZNzzjlnqDheXkQxdi+A6ujo6FiauPIvBbDL1ZwzXUvw5Cc/2dXM+fnABz4w857LLrts+rCHPWx6netcZ7rhhhtO99hjj+kf/vCHWcfx/jvf+c7Ta1/72tPb3OY201NPPXWF5PjOd77TlKP/9DXoz0B/BvozsLSege985zvL1BlrXZ/smsaf//znoRf3Bje4wVCYtTZYXCqiFWwtq59rqaOvVV+n/kz1797ygNr8xS9+MbnZzW42ufrVr744wsULBRb05je/+WRtAwXblWxfq/5M9e/fQsYN16J9SkvnkuqT7ejo6OjoWGjoSrajo6Ojo2MVoSvZRQ6V1Yccckjv9e1r1Z+p/v1bsFhnEe9TvfCpo6Ojo6NjFaF7sh0dHR0dHasIXcl2dHR0dHSsInQl29HR0dHRsYrQlWxHR0dHR8cqQleyiwgGJPzDP/zD5LrXve5kvfXWa74H9/PWW289vMcghb322mvyxz/+cdZ7zj///Mld73rXodIPN/Rpp502Wey41a1uNWfM4pFHHjnrPZ/97Gcn97vf/SbrrrvuwKKFI3sp4uUvf/mwXtbhXve615zpV0sNz33uc+c8O7e//e1nXv/tb387cKhvsMEGA8/6f/zHf8yZBrZY8aEPfWjy8Ic/fGBGsi7//d//PYc56eCDD57c9KY3HSahPehBD5p89atfnfUenPVPfOITB5IK+9pTn/rUyS9/+cvJ2oKuZBcRfv/7308e85jHTHbeeefm6wYrULDed+GFF05OP/30QYF6yAPf/OY3h/f80z/90zCEYdddd53ssMMOk3e/+92TxY7DDjts8oMf/GDm59nPfvYsykVDKDbbbLPJJz7xickxxxwzbK4nnXTSZCnh7LPPnuy+++5Du8UnP/nJyd///d9PHvrQhw7Tr5Yy7njHO856dj7ykY/MvLbbbrtN3vrWt07OPffcyQc/+MGBlvXf//3fJ0sBv/rVr4ZnhGHWAkP1JS95yeTEE0+cfOxjH5tc73rXG54nhkmAgv385z8/TEx729veNijuHXfccbLWYIXY8TvWChh6cKMb3WjO39/xjndMr371q09/+MMfzvzthBNOmN7whjec/u53vxt+33vvvad3vOMdZ33ucY973PShD33odDFjs802mx5//PGjr7/iFa+Y3vjGN55ZJ9hnn32mW2yxxXQp4Z73vOf0mc985szvf/rTn6Y3u9nNpkccccR0qeKQQw6Z/v3f/33ztZ///OfTa13rWtNzzz135m9f/OIXB3L5iy66aLqUMJlMpm9605tmfv/zn/883WSTTabHHHPMrPVaZ511pmedddbw+xe+8IXhcxdffPHMe975zndOr3a1q02/973vTdcGdE92CeGiiy6a3OlOd5psvPHGM39jNfLSWIrxHiGbDO/x98UO4WEhvbvc5S6Dp5rD6K7//ve//+Ta1772rHX58pe/PPnZz342WQoQAeHF5+cDl7ffl8LzMR+EOIVEb3Ob2wyel7QMWK8//OEPs9ZMKPmWt7zlkl+zb37zm8NI0rw2+IClIOJ58q8Q8d3vfveZ93i/547nuzagDwhYQvBAZwUL8bvX5nsPRfyb3/xmyJssRjznOc8Z8tDrr7/+EErfb7/9hrDfcccdN7Mut771rUfX7sY3vvFkseMnP/nJkHJoPR9f+tKXJksVlIK0yxZbbDE8M4ceeuiQu7/00kuHZ4NhVmskrFl855YqfviX6289T3k/UjuScc1rXnP4nq4t69eV7ALHvvvuOznqqKPmfc8Xv/jFWYUWHSu+dvKMgb/7u78bNsanP/3pkyOOOGJRUr11rDw87GEPm/XsULpy9+ecc86iNUo7lh9dyS5w7LHHHpPttttu3vcIUS0PNtlkkzmVoFHl6LX4t1Y++l1l39q2Yfw1a2ejFC6+7LLLBg9lbF3y2i12bLjhhpNrXOMazXVYKmuwPOC1br755pOvfe1rkwc/+MFDmP3nP//5LG+2r9lk5pmxFqqL89rc+c53nnlPLarzvVRxvLY8c13JLnBstNFGw8/KwFZbbTW0+XhoIwSjYo8CvcMd7jDznne84x2zPuc9/r6U1k5ltbxPrJPrP+CAA4b82rWuda2ZdaGAl0KoGHj3d7vb3Sbvf//7J4961KOGv/35z38efn/Ws561psVbMNBe8vWvf32y7bbbDuvlebFGWndAHl/Odm38Tq1M3PrWtx4UpbUJpSotJdcaHRLWiIEit20t4bzzzhueO4bwWoE1XXnVsfLwrW99a/qpT31qeuihh06vf/3rD//v5xe/+MXw+h//+Mfp3/7t304f8pCHTD/96U9P3/Wud0032mij6X777TdzjG984xvT6173utO99tprqIJ8+ctfPr3GNa4xvHex4sILLxwqi63J17/+9ekZZ5wxrMuTnvSkWVWPG2+88XTbbbedXnrppdPXv/71wzq98pWvnC4luG7Vn6eddtpQ+bnjjjtO11tvvVkV60sNe+yxx/T888+ffvOb35xecMEF0wc96EHTDTfccPqjH/1oeH2nnXaa3vKWt5yed95500suuWS61VZbDT9LAf+/vXsJiaoP4zj+aGEaXRYVIRaYJamblDKhhIKILAIxKjJMceEmSLuhthCiQC1KAjNqU1G0i27QlUAKQy2VLCIkQV3ZBUkpuhhx4nlezmFOb6+vhqeY8fuBwTlzrg7j+fm/zf/Dhw/efUjjpq6uzp7rvUrV1tba5+f69evOs2fPnNzcXGfBggXO58+fvWPk5OQ4GRkZTmtrq9PU1OQkJyc7+fn5TrggZCNIUVGRfZB/fjQ2Nnrb9Pb2OuvXr3fi4uLsRqA3iG/fvvmOo9unp6c7MTExTlJSkg0JimTt7e1OVlaWDXuKjY11UlNTnerqaufLly++7To7O53s7GwLmYSEBLtBTET19fUWGvr50CE9LS0tzkSmQ9zi4+Pt/dDPhS53d3d76zUwdu7caUPA9B+zvLw8p7+/35kIGhsbf3lP0nuVO4ynqqrK/oHVv6s1a9Y4XV1dvmMMDAxYqGrBQYcbFhcXewWHcMBUdwAABIRxsgAABISQBQAgIIQsAAABIWQBAAgIIQsAQEAIWQAAAkLIAgAQEEIWAICAELJAmFq9erXs3r07Ys6pkzm434kMRAomCAAwaleuXPEmSFCJiYkWun867IFwQcgCGDWdLBvA6FFdDESA9+/fS2FhoU27N3XqVJtI/NWrV9768+fP23ymd+/eldTUVJk2bZrk5ORIf3+/b57O0tJS227WrFlSUVEhRUVFvirc0Opifd7X1yd79uyRqKgoe6iDBw96U5e5Tpw4YaVe1/fv32Xv3r3eucrLy3WyEt8+Op1ZTU2NTYmmcxkvWbJELl++HMC7BwSHkAUigLZntrW1yY0bN6S5udkCa8OGDTb/revTp09y7NgxuXjxojx8+NDmNN2/f7+3/siRI3Lp0iU5d+6cPHr0yOb2vHbt2ohVx/PmzZNDhw5ZWIcG9v85fvy4Bf/Zs2elqanJJuG+evWqbxsN2AsXLsjp06flxYsXFuYFBQXy4MGDMb8/wN9CdTEQ5rTEquGqwbhixQp7TcNy/vz5FpJbtmyx1zRwNbAWLlxoyzrRugakq76+Xg4cOCB5eXm2fPLkSbl169aIVceTJk2S6dOn2+TbY6ElWz3Xpk2bbFmvS0vZrq9fv0p1dbXcv3/fm9w8KSnJAvnMmTOyatWqMZ0P+FsIWSDMvXz5UiZPnixZWVnea1oFu3jxYlvn0mpkN2BVfHy8vH371p4PDQ3JmzdvZPny5d56DdClS5date140nNpqTf0evX6ly1b5lUZd3d3W8l77dq1vn2Hh4clIyNjXK8HCBIhC0wQob2Clbah/twOOh6io6P/ddzQauvR+Pjxo/28efOmJCQk+NZNmTJlHK4S+DNokwXCnHZk0k5Lra2t3msDAwPS1dUlaWlpozrGzJkzZe7cufLkyRNf56SOjo4R94uJibHtQs2ZM0dev37tC9qnT5/6zqWl6NDr1etvb2/3lvW6NUy13XjRokW+h1aDA+GCkiwQ5pKTkyU3N1dKSkqsvVLbSCsrK60EqK+P1q5du6yzkQZZSkqKtdFqr2W31/CvaI9h7US1bds2C8XZs2dbr+N3797J0aNHZfPmzXLnzh25ffu2zJgxw9uvrKxMamtr7dr1XHV1dTI4OOit199BO2VpZyetrs7OzrZqZm131uNor2cgHFCSBSKA9gjW9tONGzdaRyEtRWqnpZ+riEeiQ3by8/NtKJAeQ4f5rFu3TmJjY/9zH+041dvba229WoJ1S9anTp2ShoYGG3bz+PFjXy9mtW/fPtmxY4eFpZ5LQ9XtcOU6fPiwVFVVWfDrMXXIkVYf65AeIFxEOUE0ygAIe1qC1HDbunWrBR6AsaO6GIDRL5a4d++eDY/RITQ6hKenp0e2b9/OOwT8JqqLAfxzM4iOti+IyMzMlJUrV8rz589tnKqWZgH8HqqLAQAICCVZAAACQsgCABAQQhYAgIAQsgAABISQBQAgIIQsAAABIWQBAAgIIQsAgATjBz2kEAJh0rXeAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "id": "5", + "metadata": {}, + "outputs": [], "source": [ "ny_t, nx_t = 30, 50\n", "xi, yi = np.meshgrid(\n", @@ -153,7 +110,7 @@ }, { "cell_type": "markdown", - "id": "2272ad8a", + "id": "6", "metadata": {}, "source": [ "## Regrid\n", @@ -166,646 +123,10 @@ }, { "cell_type": "code", - "execution_count": 4, - "id": "430c61f1", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:11.552310Z", - "iopub.status.busy": "2026-04-19T17:16:11.552234Z", - "iopub.status.idle": "2026-04-19T17:16:12.506053Z", - "shell.execute_reply": "2026-04-19T17:16:12.505729Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.DataArray 'bumps' (ny: 30, nx: 50)> Size: 12kB\n",
-       "array([[            nan,             nan,             nan, ...,\n",
-       "         5.07649140e-05,  1.78419614e-05,  5.78932492e-06],\n",
-       "       [            nan,             nan,             nan, ...,\n",
-       "         8.41768394e-05,  2.95914993e-05,  9.59425795e-06],\n",
-       "       [            nan,             nan,             nan, ...,\n",
-       "         1.33940155e-04,  4.70846845e-05,  1.52861050e-05],\n",
-       "       ...,\n",
-       "       [-5.43488000e-04, -1.59138071e-03, -4.21769110e-03, ...,\n",
-       "                    nan,             nan,             nan],\n",
-       "       [-3.79047801e-04, -1.11100367e-03, -2.94474907e-03, ...,\n",
-       "                    nan,             nan,             nan],\n",
-       "       [-2.51769993e-04, -7.37413528e-04, -1.95498023e-03, ...,\n",
-       "                    nan,             nan,             nan]],\n",
-       "      shape=(30, 50))\n",
-       "Coordinates:\n",
-       "    longitude  (ny, nx) float64 12kB -72.76 -68.87 -64.99 ... 64.99 68.87 72.76\n",
-       "    latitude   (ny, nx) float64 12kB -93.97 -91.73 -89.48 ... 89.48 91.73 93.97\n",
-       "Dimensions without coordinates: ny, nx
" - ], - "text/plain": [ - " Size: 12kB\n", - "array([[ nan, nan, nan, ...,\n", - " 5.07649140e-05, 1.78419614e-05, 5.78932492e-06],\n", - " [ nan, nan, nan, ...,\n", - " 8.41768394e-05, 2.95914993e-05, 9.59425795e-06],\n", - " [ nan, nan, nan, ...,\n", - " 1.33940155e-04, 4.70846845e-05, 1.52861050e-05],\n", - " ...,\n", - " [-5.43488000e-04, -1.59138071e-03, -4.21769110e-03, ...,\n", - " nan, nan, nan],\n", - " [-3.79047801e-04, -1.11100367e-03, -2.94474907e-03, ...,\n", - " nan, nan, nan],\n", - " [-2.51769993e-04, -7.37413528e-04, -1.95498023e-03, ...,\n", - " nan, nan, nan]],\n", - " shape=(30, 50))\n", - "Coordinates:\n", - " longitude (ny, nx) float64 12kB -72.76 -68.87 -64.99 ... 64.99 68.87 72.76\n", - " latitude (ny, nx) float64 12kB -93.97 -91.73 -89.48 ... 89.48 91.73 93.97\n", - "Dimensions without coordinates: ny, nx" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], "source": [ "regridded = src.regrid.conservative_polygon(\n", " target, x_coord=\"longitude\", y_coord=\"latitude\"\n", @@ -815,28 +136,10 @@ }, { "cell_type": "code", - "execution_count": 5, - "id": "8c80215d", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:12.507194Z", - "iopub.status.busy": "2026-04-19T17:16:12.507133Z", - "iopub.status.idle": "2026-04-19T17:16:12.559023Z", - "shell.execute_reply": "2026-04-19T17:16:12.558588Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAi4AAAGJCAYAAACtu7gUAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAmS9JREFUeJztnQeYG9XVv4+0vdjrsu69gAvggo2N6QSDKQkQCKHGQCihJvTy/UMPvXwEYkIIYCDAhyEBQoCYXgIYA6aD7eBu3NvuenvR/J/ftUceXc3ce2clrTTSeZ9nwJLujkZ1Xp17zrkhy7IsYhiGYRiGCQDhdB8AwzAMwzCMKSwuDMMwDMMEBhYXhmEYhmECA4sLwzAMwzCBgcWFYRiGYZjAwOLCMAzDMExgYHFhGIZhGCYwsLgwDMMwDBMYWFwYhmEYhgkMLC6MknfffZdCoZD4v44DDjhAbMncpynXX3+92CcTDFLxHugocNx4v9k89thj4rply5b5/ixkM+n8PmCyGxYXhkmA+vp6cRLLxC/dV199NeYE2x5uueUWevHFF5N2TAzDMImSn/AemKxmv/32o4aGBiosLEz3oWSsuNxwww3i35n2CxviMmPGjITkBeLyi1/8go4++uikHluQwechP1/91fn6669TrsPPAZMqOOISIOrq6jrsvhobGykSiVA4HKbi4mLxfya92K8JkzywxixExA/4POjEBaKfybKfyu8SyHwQngMmuPDZKEOxcza+//57Oumkk6hr1660zz77RG9/8sknacKECVRSUkLdunWjE044gVauXBm3H/ziHjp0qBg3adIk+s9//hM392zPMT/zzDP0+9//nvr160elpaVUU1PjOf/80EMP0bBhw2L268aPP/4ofq2XlZVRz5496eKLL6ampibXsXPnzqVDDz2UKioqxP3vv//+9OGHH8aN++CDD2iPPfYQJxAcw1/+8hdfz+1zzz0Xfe4qKyvplFNOoVWrVsWMOe2006i8vFxcj+PHv3v06EGXXXYZtbW1iTHIacB1AFEXPE9y/sPbb79N++67r3j8Xbp0oaOOOormz5+vPUbVa2LyGHD8eO2BfVzOHKC77rqL9tprL+revbvYB/b197//PeYYMB4nuMcffzz699ivDe7v17/+NfXq1YuKiopol112oUcffTSh94AbuJ8zzjiD+vbtK+5nyJAhdO6551Jzc7Myv8kt92Tw4MH005/+lF577TWaOHGieOx4/+y666504IEHxu0DoojnHlEn5/Oii2J5fcaeffZZuvnmm6l///7i/XvQQQfRokWL2vVZWL58OZ133nk0YsQI8TjwWh533HExj9f5PLz33ntiPF4D3L8K7PvII4+Mec3wnMnfBXiMeO7mzZsnorM41v/5n/9xfQ6S8V5gGMBTRRkOvoh22mknEbLHr0OAL75rrrmGfvnLX9KZZ55JGzZsoPvvv198cXzxxRfiBAn+/Oc/0wUXXCBOnPiCwBcavjQgQW5fXDfddJP4hYSTM75MvH4tPfLII/Sb3/xGnPguuugiWrJkifiSg0ANGDAgOg6/ZPHFvGLFCvrtb38rTjx/+9vfxMlcBtcddthh4gR63XXXiQjPzJkz6Sc/+YmQIsgR+Oabb+iQQw4RwoCTR2trqxiPk6cJ+BI//fTThfjceuuttG7dOvrjH/8oTgrO5w5AUKZNm0aTJ08WJ/o333yT7r77biFLOHHiGPAc498///nP6ZhjjhF/N2bMGPF/jMdjgjjiWPF84HXae++96fPPPxcnUR1ur4nJY8Drs3r1anrjjTfEcy6D8XjNTj75ZCEAECS8115++WU64ogjxBj8Hd5feO7PPvtscR0eO8B97rnnnuJEhvcYnot///vfQjAgV3hf+H0PuIHHgPuvqqoSxzBy5EghMpAs/LJvzy/6hQsX0oknniieo7POOkuc+I8//njxGq1du5Z69+4dI8k4BvwwSAa33XabeG/j9ayurqY77rhDvAYQFb+fhU8//ZQ++ugjcWz4POPzjfcjZAE/eCARTiAteJ2uvfZaZcQFt+G+1qxZQ7/73e/E8/H000/TO++84zp+06ZN4nhxHBBor89iou8FholiMRnJddddB0uxTjzxxJjrly1bZuXl5Vk333xzzPXffPONlZ+fH72+qanJ6t69u7XHHntYLS0t0XGPPfaY2O/+++8fve6dd94R1w0dOtSqr6+P2a99G/4PmpubrZ49e1rjxo0T92Hz0EMPxe333nvvFdc9++yz0evq6uqs4cOHx+wzEolYO+20kzVt2jTxbxscy5AhQ6yDDz44et3RRx9tFRcXW8uXL49e9/3334vnRPd2to991113tRoaGqLXv/zyy+Jvr7322uh1p556qrjuxhtvjNnH+PHjrQkT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7uPQQw+1xo8fb82dO9f64IMPrJ122sk68cQT0/iomHTC4pJGTj31VPFBlrd33nknOmbZsmXWYYcdZpWUlIgvR3xptrS0xOwH48eNG2cVFhZaQ4cOFeXVuca8efOsyZMni7Ly4uJia9SoUdYtt9xiNTY2xoz76quvrH322UecpPv16ye+NJlt3H///eKEi/cRyqM//vhjfmpcQLuBPn36iOcJ7yFcXrRoUfR2nHDPO+88UXoPMf75z39urVmzJmefS3w/uX3P4fvPLom+5pprxI8KfC4POugga+HChTH72LRpkxAV/MBDO4jTTz89+gOPyT1C+E+6oz4MwzAMwzAmcB8XhmEYhmECA4sLwzAMwzCBgcWFYRiGYZjAwOLCMAzDMExgYHFhGIZhGCYwsLgwDMMwDBMYWFwYhmEYhgkMLC4MwzAMwwQGFheGyTAOOOAAuuiii7LmPrFIpr0GEsMwTKLwIosMw9Dzzz8fXXgSDB48WIhMRwsUwzCMDhYXhmGoW7du/CwwDBMIeKqIYTKYLVu20PTp06lr165UWlpKhx12GP3www/R2x977DHq0qULvfbaazRq1CgqLy+nQw89lNasWRMd09raSr/97W/FuO7du9OVV15Jp556asz0jXOqCP9evnw5XXzxxRQKhcQGrr/+eho3blzM8d17770iOmPT1tZGl1xySfS+rrjiCizkGvM3kUiEbr31VhoyZAiVlJTQ2LFj6e9//3sKnj2GYbIRFheGyWCQH/LZZ5/RSy+9RHPmzBEScPjhh1NLS0t0TH19Pd111130t7/9jd5//31asWIFXXbZZdHbb7/9dnrqqado5syZ9OGHH1JNTQ29+OKLymmj/v3704033igEyClBOu6++24hU48++ih98MEHtHnzZnrhhRdixkBannjiCXrwwQfpu+++E4J0yimn0Hvvvef7+WEYJvfgqSKGyVAQWYGwQDb22msvcR0EZMCAAUI8jjvuOHEdJAYSMGzYMHH5ggsuENJhc//999PVV19NP//5z8XlP/3pT/Tqq68qp43y8vKoU6dO1Lt3b1/HjAgM7uuYY44Rl3FciAbZNDU10S233EJvvvkmTZkyRVw3dOhQITl/+ctfaP/99/d1fwzD5B4sLgyTocyfP5/y8/Np8uTJ0esw/TJixAhxmw2mkGxpAX369KH169eLf1dXV9O6deto0qRJ0dshJRMmTBBTNskE94XojPN4cfwTJ06MThctWrRIRIgOPvjgmL9tbm6m8ePHJ/V4GIbJTlhcGCbgOKuBAHJS5LySZBAOh+P265yyMqG2tlb8/5VXXqF+/frF3FZUVJSEo2QYJtvhHBeGyVCQbIvE2rlz50av27RpEy1cuJBGjx5ttI+Kigrq1asXffrppzEJtJ9//rny7woLC8U4Jz169KC1a9fGyMuXX34Zc1+I9jiPF8c/b9686GUcNwQFeTjDhw+P2TAFxjAMo4MjLgyToey000501FFH0VlnnSXyP5BzctVVV4lIBa435cILLxQJsZCDkSNHipwXVCvZ1UJuoFIIib4nnHCCEI3KykpRbbRhwwa644476Be/+AXNnj2b/v3vf1Pnzp2jf/e73/2ObrvtNnHsuK977rmHqqqqorfjMSBxGAm5mKraZ599xBQT8niwH1Q7MQzDqOCIC8NkMKgEQj7KT3/6U5HMimgHEmvl6SEVKH8+8cQTRVk19oGS6WnTplFxcbHn3yC5d9myZSJ3BpEWOwL0wAMP0IwZM0QJ8yeffBJTvQQuvfRS+tWvfiUEBPcFUbGTgm1uuukmuuaaa4RMYZ8o38bUEcqjGYZhdISsVEyGMwyTsSDSAWH45S9/KSSCYRgmSPBUEcNkOWgm9/rrr4tSY5Qjoxx66dKldNJJJ6X70BiGYXzDU0UMk+WgGghN4fbYYw/ae++96ZtvvhF9VBB1YRiGCRo8VcQwDMMwTGDgiAvDMAzDMIGBxYVhGIZhmMDA4sIwDMMwTGBgcWEYhmEYJjCwuDAMwzAMExhYXBiGYRiGCQwsLgzDMAzDBAYWF4ZhGIZhKCj8f7CRRwnXiXnMAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], "source": [ "fig, ax = plt.subplots(figsize=(8, 4))\n", "pc = ax.pcolormesh(lon2d, lat2d, regridded.values, cmap=\"RdBu_r\", shading=\"auto\",\n", @@ -849,7 +152,7 @@ }, { "cell_type": "markdown", - "id": "bd41e9b9", + "id": "9", "metadata": {}, "source": [ "## Conservation check\n", @@ -866,28 +169,10 @@ }, { "cell_type": "code", - "execution_count": 6, - "id": "75869b58", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:12.560078Z", - "iopub.status.busy": "2026-04-19T17:16:12.560016Z", - "iopub.status.idle": "2026-04-19T17:16:12.884274Z", - "shell.execute_reply": "2026-04-19T17:16:12.883830Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "direct (A · s): 325.275958\n", - "regridded (out · a_dst): 325.275958\n", - "relative difference: 5.24e-16\n", - "covered target fraction: 87.60%\n" - ] - } - ], + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], "source": [ "rgr = ConservativeRegridder(src, target, x_coord=\"longitude\", y_coord=\"latitude\")\n", "A = rgr._areas # sparse (n_dst, n_src)\n", diff --git a/docs/notebooks/demos/demo_conservative_polygon_unstructured.ipynb b/docs/notebooks/demos/demo_conservative_polygon_unstructured.ipynb index 5bfe703..92e2b85 100644 --- a/docs/notebooks/demos/demo_conservative_polygon_unstructured.ipynb +++ b/docs/notebooks/demos/demo_conservative_polygon_unstructured.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "84b1ee05", + "id": "0", "metadata": {}, "source": [ "# Polygon conservative regridder — unstructured mesh + save/load\n", @@ -17,16 +17,9 @@ }, { "cell_type": "code", - "execution_count": 1, - "id": "a3b1f7ee", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:14.078753Z", - "iopub.status.busy": "2026-04-19T17:16:14.078509Z", - "iopub.status.idle": "2026-04-19T17:16:15.304905Z", - "shell.execute_reply": "2026-04-19T17:16:15.304385Z" - } - }, + "execution_count": null, + "id": "1", + "metadata": {}, "outputs": [], "source": [ "import tempfile\n", @@ -45,7 +38,7 @@ }, { "cell_type": "markdown", - "id": "4400ded4", + "id": "2", "metadata": {}, "source": [ "## Build a hex-like Voronoi mesh\n", @@ -57,25 +50,10 @@ }, { "cell_type": "code", - "execution_count": 2, - "id": "6cb76d9f", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:15.306411Z", - "iopub.status.busy": "2026-04-19T17:16:15.306266Z", - "iopub.status.idle": "2026-04-19T17:16:15.319900Z", - "shell.execute_reply": "2026-04-19T17:16:15.319496Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "398 cells in the mesh\n" - ] - } - ], + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], "source": [ "def voronoi_mesh(n_points, bbox, seed=0):\n", " rng = np.random.default_rng(seed)\n", @@ -108,7 +86,7 @@ }, { "cell_type": "markdown", - "id": "d5d25e62", + "id": "4", "metadata": {}, "source": [ "## Source — structured lat/lon field" @@ -116,28 +94,10 @@ }, { "cell_type": "code", - "execution_count": 3, - "id": "fd807000", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:15.320797Z", - "iopub.status.busy": "2026-04-19T17:16:15.320734Z", - "iopub.status.idle": "2026-04-19T17:16:15.399656Z", - "shell.execute_reply": "2026-04-19T17:16:15.399270Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAv4AAAFACAYAAADETXghAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAsSpJREFUeJztnQmYJEWZ96Ou7pnhRmC4jwGW+xIWBFxEQA7ZXRFWQVEOEVa5FFAOlVtBRBFhQdCVa4WFRcFrEWFBUHREHERAgU8QZOQYLgcYZqa7qjK/J6LijXoj8q2orKrururp/+95qrsqIjIyqyozI+r/HlFI0zRVAAAAAAAAgCWaYr8PAAAAAAAAADD+YOIPAAAAAADAFAATfwAAAAAAAKYAmPgDAAAAAAAwBcDEHwAAAAAAgCkAJv4AAAAAAABMATDxBwAAAAAAYAqAiT8AAAAAAABTAEz8AQAAAAAAmAJg4g8AAJOAQqGgzjrrrH4fBgAAgEkMJv4AgL5zww03qIsvvnjKHwMAAAAwnmDiDwDoO5j4AwAAAOMPJv4AgEnF4sWLVZIkarKwcOFCNdVI01QtWrSo34cBAAAgABN/AMC48uabb6pPf/rTat1111XDw8NqlVVWUe95z3vUgw8+aOp33XVX9b//+7/qr3/9q/Fj1w/dVnPPPfeY1zfeeKP6whe+oNZYYw01Y8YM9cYbbxh/d10Xcs0115jyZ555xiv/6U9/qt71rnepZZZZRi277LLqH//xH42lod0xtOqPjk3/J3Q/m2++uZozZ47aZZddzLF+7nOfM3UjIyPqzDPPVBtssIH5HNZaay118sknm3KOfn3CCSeolVde2Rzrv/7rv6q//e1vuT/vSy+9VG222WZm3yussILabrvt3Pskfv/736t99tnHfA5LL7202n333dVvfvMbr00nn6/+rP75n/9Z/exnPzP7mz59urryyitN3fz58837oe9/zTXXVIcccoh65ZVXvPec57MBAADQG+UetwcAgCif+MQn1Pe+9z117LHHqk033VS9+uqr6r777lOPPfaYevvb364+//nPq9dff91Mbr/+9a+bbfRklHPuueeqoaEh9ZnPfMZMBvXzTtCT1Y997GNmQnzaaaep5Zdf3kx+b7/9dvXhD3841zHkRb8/Pak+6KCD1Ec+8hE1c+ZMY6HQE3j9vo866ii1ySabqEceecTs6//9v/+nfvCDH7jtP/7xj6vvfve75rh22mkndffdd6t99903176//e1vq+OPP17927/9m/rUpz5lrCMPP/ywuv/++01/mj/+8Y/qn/7pn8ykX0+uK5WKmaTrHy333nuv2mGHHbp630888YT60Ic+pP793/9dHXnkkWqjjTZSCxYsMPvS37X+/PX3rSf8P/rRj8xnvdJKK3X02QAAAOiRFAAAxpHlllsuPeaYY6Jt9t1333SdddbJlP/85z9P9W1q1qxZ6cKFC726M88809SFXH311ab86aefNq/nz5+fLrPMMukOO+yQLlq0yGubJEnbYwj7C49N/yfe9a53mbIrrrjCa/tf//VfabFYTH/5y1965bqdbv+rX/3KvH7ooYfM66OPPtpr9+EPf9iU6/cc433ve1+62WabRdvst99+6dDQUPrUU0+5sueff958RrvsskvHn69Gf2667Pbbb/fannHGGab8lltuyfRDn33ezwYAAEDvwNUHADCuaHVdK87PP/98130ceuihxn2kG+68807jbnTqqaeqadOmeXWSK0uvaFeVww8/3Cu7+eabjZK98cYbG8WbHrvttpup//nPf27+33bbbea/Vu052lUq72etlfQHHnhArK/X6+qOO+5Q++23n5o1a5YrX2211YxFQKvu2o2qG9Zbbz211157eWXf//731VZbbaXe//73Z9rTZ5/3swEAANA7mPgDAMaVr3zlK+rRRx81ftvbb7+98R3/y1/+0vGkslueeuop81/73k8EOg4hdEX685//bFxstN8+f/zDP/yDqX/ppZfMfx1jUCwW1frrr+9tr91m8nDKKacYFyX9OW+44YbqmGOOUb/61a9c/csvv2yCjaX+9ORbu93MnTu3q/ctfUf6s2/3uef9bAAAAPQOfPwBAOPKBz/4QePnfeuttxq1+cILL1QXXHCBuuWWW4wvfB4ktb+VWq9V7bGk0/1Ix6on1FtssYW66KKLxG30j6KxQE/eta/9T37yExO/oBX3yy+/XJ1xxhnq7LPPnvD3nYeJ+mwAAABg4g8AmAC0K8nRRx9tHlrB1UGeX/rSl9zEvxuXG52xhrLGaBcXQqvmHFLPtdVBZ41pRatj4PvhhPuJoY/hD3/4g8meE3uv66yzjpkIa6Wcq/J6Mp+XpZZaSh144IHmMTo6qvbff3/zWeugZq2k62w/Un+PP/64sTbQRDvv59vufevPfSw+GwAAAL0DVx8AwLih1WGdLYej03muvvrqXqpGPVkN27WDJvS/+MUvXNlbb72lrr32Wq/dnnvuadJinn/++SbLTZhvvt0xSPvR7+tb3/pWR1aP5557zmTdCdH57vVxa+iH0CWXXOK1ybuqsc4oxNEuRzqTkn6f1WpVlUol83n88Ic/9NJxzps3z6T8fOc732my/XTy+cY44IADzKReW3tC6LPP+9kAAADoHbj6AADGDR1Uq/O26/SSOshT+5//3//9nwk+/drXvubabbvttuqmm25SJ554osmvr9v9y7/8S7RvPYFde+211RFHHKE++9nPmkntVVddZVTtZ5991rXTE1mdGlKnydR96yBWrWbrCan2d6eJbKtj0ClA3/GOdxjF/LXXXlMrrriiWVegVqvl/hw++tGPqv/5n/8xqU11sOrOO+9sfjxolV2XU/77rbfe2qTE1O45+keITud51113qSeffDLXfvRnsuqqq5r+dRpRnUbzP/7jP0w6UP3jR/PFL37RBDzrSb62wJTLZZPOU/8Q0/EYnX6+MfR2OpXrBz7wAZPOU3/G+jPU6TyvuOIKc07k/WwAAACMAWOQGQgAAERGRkbSz372s+lWW21l0kUutdRS5vnll1/utVuwYIFJWbn88subFI6UVpNSZt58881i/3PmzDFpOnV6yrXXXju96KKLWqbf/NGPfpTutNNO6fTp09Nll1023X777dP//u//bnsMGp36co899kiHh4fTmTNnpp/73OfSO++8U0zn2Sqd5ujoaHrBBReYet3PCiuskG677bbp2Wefnb7++uuunU45evzxx6dve9vbzOf1L//yL+ncuXNzpfO88sorTUpOva3ex/rrr28+f96/5sEHH0z32muvdOmll05nzJiRvvvd705//etfd/356s9Kp0OVePXVV9Njjz02XWONNUw/a665ZnrooYemr7zySsefDQAAgN4o6D9j8QMCAAAAAAAAMLjAxx8AAAAAAIApACb+AAAAAAAATAEw8QcAAAAAAGAKgIk/AAAAAAAAUwBM/AEAAAAAAJgCYOIPAAAAAADAFAALeDGSJFHPP/+8WegGS8cDAAAAYElCZ3DXCyvq1dOLxf5rv3o19dHR0dzt9Wrk06ZNG9djWtLBxJ+hJ/1rrbVW/74NAAAAAIBx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vHnz8BkPIL/4xS/Uv/zLv5iFrfT3+IMf/CCz8NUZZ5yhVlttNTV9+nS1xx57qD//+c9em9dee00dfPDBJr+/HpuPOOIItWDBggl+JwDEwcR/EqJXufvABz6gPvnJT4r19XrdTPp1u1//+tfq2muvNZN6fdMinn76adPm3e9+t3rooYfUpz/9afXxj39c/exnP5vAdwLycM4556gXXnjBPY477jhvFck999xTrbPOOmrOnDnqwgsvNJORb33rW/hwB5ybbrpJnXjiiSYT14MPPqi22mortddee6mXXnqp34cGOmCzzTbzrs/77rvP1Z1wwgnqxz/+sbr55pvVvffea1aG33///fH5DiBvvfWWuQb1j3EJLahccskl6oorrlD333+/Wmqppcz1qn/cEXrS/8c//lHdeeed6ic/+Yn5MXHUUUdN4LsAIAcpmLRcffXV6XLLLZcpv+2229JisZi++OKLruyb3/xmuuyyy6YjIyPm9cknn5xuttlm3nYHHnhgutdee03AkYO8rLPOOunXv/71lvWXX355usIKK7jvVXPKKaekG220ET7kAWf77bdPjznmGPe6Xq+nq6++enr++ef39bhAfs4888x0q622Euvmz5+fViqV9Oabb3Zljz32WKqH3dmzZ+NjHmD0d3Trrbe610mSpKuuump64YUXet/v8PBw+t///d/m9Z/+9Cez3QMPPODa/PSnP00LhUL63HPPTfA7AKA1UPyXQGbPnq222GILNXPmTFemlQmtDms1gtpoUyVHt9HlYLDQrj3aVWCbbbYxij532dLf1y677KKGhoa87/GJJ55Qf//73/t0xKAd2hqnLTT8GiwWi+Y1rsHJhXb30O4hs2bNMoqvdrPU6O+3Wq1637F2A1p77bXxHU8ytIX8xRdf9L7L5ZZbzrjn0fWq/2v3nu2228610e31da0tBAAMCuV+HwAYe/QNik/6NfRa18Xa6B8HixYtMj6MoP8cf/zxJg5jxRVXNG5bp512mnEnuOiii9z3uN5667X8rldYYYW+HDeI88orrxiXPOkafPzxx/HxTRL0xE+7UW600Ubmujz77LNNvM2jjz5qrj/9gzyMw9LfMd2HweSAvi/peuVjqo6n45TLZXPvxvcNBglM/AeEU089VV1wwQXRNo899pgXOAaW/O9a+4ATW265pZlI/Pu//7s6//zzsUIzAH1mn3328a5P/UNAx9v8z//8D8QTAMBAgon/gHDSSSepww47LNpGm5LzsOqqq2ayg1AmCV1H/8PsEvq1zkYAtX9wv2s9sdCuPs8884xRGVt9j/y7BoPHSiutpEqlkvjd4XubvGh1/x/+4R/Uk08+qd7znvcYl6758+d7qj++48kHXZP6u9NZfQj9euutt3ZtwsB8fa/WmX5wTYNBAhP/AWHllVc2j7Fgxx13NCk/9U2ITI86y4Ce1G+66aauzW233eZtp9vocjC437XOwKR9Rul71d/X5z//eeNLXKlU3PeofxTAzWdw0ZabbbfdVt11111qv/32M2VJkpjXxx57bL8PD3SJTt341FNPqY9+9KPm+9XXpP5OdRpPjY690TEAuM9OLrQ7pZ686++SJvraLVb77lN2Pf2d6h95OrZDf/eau+++21zXWrABYGCIBP6CAeWvf/1r+vvf/z49++yz06WXXto8148333zT1NdqtXTzzTdP99xzz/Shhx5Kb7/99nTllVdOTzvtNNfHX/7yl3TGjBnpZz/7WZNp4rLLLktLpZJpCwaDX//61yajj/4On3rqqfS73/2u+R4POeQQL7PEzJkz049+9KPpo48+mt54443me73yyiv7euygPfq70llBrrnmGpMR5KijjkqXX355LxsXGGxOOumk9J577kmffvrp9Fe/+lW6xx57pCuttFL60ksvmfpPfOIT6dprr53efffd6e9+97t0xx13NA8weOjxk8ZSPTW66KKLzHM93mq+/OUvm+vzhz/8Yfrwww+n73vf+9L11lsvXbRoketj7733TrfZZpv0/vvvT++77750ww03TD/0oQ/18V0BkAUT/0nIoYceam5M4ePnP/+5a/PMM8+k++yzTzp9+nQzEOkBqlqtev3o9ltvvXU6NDSUzpo1y6QHBYPDnDlz0h122MGkbJ02bVq6ySabpOedd166ePFir90f/vCH9J3vfKeZRK6xxhpmgAKTg0svvdRMDPU1qNN7/uY3v+n3IYEO0CmQV1ttNfP96WtPv37yySddvZ4UHn300Sblrv5B/v73vz994YUX8BkPIHo8lMZVPd5SSs/TTz/dCC36Xrv77runTzzxhNfHq6++aib6WpDT6bMPP/xwJ8gBMCgU9J9+Wx0AAAAAAAAA4wvy+AMAAAAAADAFwMQfAAAAAACAKQAm/gAAAAAAAEwBMPEHAAAAAABgCoCJPwAAAAAAAFMATPwBAAAAAACYAmDiDwAAAAAAwBQAE38AAAAAAACmAJj4AwCmPLvuuqv69Kc/vcTs87DDDlP77bffuPQNAABg8lLu9wEAAMBU5JZbblGVSsW9Xnfddc0PgYn+AQIAAGDqgIk/AAD0gRVXXBGfOwAAgAkFrj4AAMD4+9//rg455BC1wgorqBkzZqh99tlH/fnPf3b111xzjVp++eXVz372M7XJJpuopZdeWu29997qhRdecG1qtZo6/vjjTbu3ve1t6pRTTlGHHnqo537DXX3087/+9a/qhBNOUIVCwTw0Z511ltp666297+fiiy821gGiXq+rE0880e3r5JNPVmmaetskSaLOP/98td5666np06errbbaSn3ve9/D9w4AAFMMTPwBACDwj//d736nfvSjH6nZs2ebSfR73/teVa1WXZuFCxeqr371q+q//uu/1C9+8Qv17LPPqs985jOu/oILLlDXX3+9uvrqq9WvfvUr9cYbb6gf/OAHUbefNddcU51zzjnmBwT/EdGOr33ta+bHyFVXXaXuu+8+9dprr6lbb73Va6Mn/dddd5264oor1B//+EfzA+MjH/mIuvfee/HdAwDAFAKuPgAAYNHKvp7w68n6TjvtZMr0BH6ttdYyE/cPfOADpkz/CNCT6PXXX9+8PvbYY82knbj00kvVaaedpt7//veb1//xH/+hbrvttqjbT6lUUssss4xaddVVO/o+tAVA72v//fc3r/VxaWsEMTIyos477zz1f//3f2rHHXc0ZbNmzTI/Eq688kr1rne9C98/AABMETDxBwAAy2OPPabK5bLaYYcd3Gei3Wc22mgjU0doFyCa9GtWW2019dJLL5nnr7/+upo3b57afvvtXb2e1G+77bbG5WYs0fvS1gF+vPr4t9tuO+fu8+STTxoLxXve8x5v29HRUbXNNtvguwcAgCkEJv4AANAhPBuPRvvkh371Y0GxWMz0y12O8rBgwQLz/3//93/VGmus4dUNDw+PwVECAACYLMDHHwAALDpYVwfm3n///e4zefXVV9UTTzyhNt1001yf03LLLadmzpypHnjgAS8A98EHH4xuNzQ0ZNpxVl55ZfXiiy96k/+HHnrI25e2NvDj1cc/Z84c91oft57g6ziEDTbYwHtoFyYAAABTByj+AABg2XDDDdX73vc+deSRRxr/d+1zf+qppxqlXJfn5bjjjjMBtXpyvfHGGxuff50tiLL1SOhMPTpQ+KCDDjIT9ZVWWslk+3n55ZfVV77yFfVv//Zv6vbbb1c//elP1bLLLuu2+9SnPqW+/OUvm2PX+7rooovU/PnzXb1+DzrwWAf0alejd77zncZFSMcx6H50tiEAAABTAyj+AADA0Jl4tD/+P//zP5tgWK2268Dc0L0nhk7f+aEPfcikBdV96JSfe+21l5o2bVrLbXRw8DPPPGNiB7TSTxaIyy+/XF122WUmBedvf/tbL3uQ5qSTTlIf/ehHzQRe70tP9CmomDj33HPV6aefbn6M6D51+lHt+qPTewIAAJg6FNLxcEwFAADg0Eq7nnB/8IMfNJNwAAAAoB/A1QcAAMYYvRjXHXfcYVJl6nSaOp3n008/rT784Q/jswYAANA34OoDAABjfWMtFs2iWv/4j/+odt55Z/XII4+YPPpa9QcAAAD6BVx9AAAAAAAAmAJA8QcAAAAAAGAKgIk/AAAAAAAAUwBM/AEAAAAAAJgCYOIPAAAAAADAFAATfwAAAAAAAKYAmPgDAAAAAAAwBcDEHwAAAAAAgCkAJv4AAAAAAABMATDxBwAAAAAAQC35/H8TACkBHJ2eLwAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "id": "5", + "metadata": {}, + "outputs": [], "source": [ "lat_s = np.linspace(-50, 50, 100, endpoint=False) + 0.5\n", "lon_s = np.linspace(-120, 120, 240, endpoint=False) + 0.5\n", @@ -156,7 +116,7 @@ }, { "cell_type": "markdown", - "id": "3bbeefc0", + "id": "6", "metadata": {}, "source": [ "## Regrid structured → mesh with `from_polygons`\n", @@ -168,706 +128,10 @@ }, { "cell_type": "code", - "execution_count": 4, - "id": "c9f0afde", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:15.400534Z", - "iopub.status.busy": "2026-04-19T17:16:15.400476Z", - "iopub.status.idle": "2026-04-19T17:16:16.243410Z", - "shell.execute_reply": "2026-04-19T17:16:16.242933Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ConservativeRegridder(src_dims=('src_cell',), dst_dims=('cell',), 398x24000, nnz=31875)\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.DataArray (cell: 398)> Size: 3kB\n",
-       "array([-0.6828435 , -0.52020535, -0.34043656, -0.05049317, -0.12985816,\n",
-       "        0.31035596,  0.55679515,  0.73714634,  0.79715029,  0.75440137,\n",
-       "        0.73846747,  0.56186966,  0.50518574,  0.24205725,  0.02472535,\n",
-       "       -0.17479924, -0.41243175, -0.62933553, -0.68358687, -0.48097563,\n",
-       "       -0.29176974, -0.24634718, -0.01120315,  0.14080408,  0.089748  ,\n",
-       "        0.33788694,  0.55161779,  0.47166168,  0.67826547,  0.6011212 ,\n",
-       "        0.38839256,  0.46979398,  0.18574702,  0.31437291, -0.02156659,\n",
-       "       -0.06562757, -0.17649315, -0.42198051, -0.308658  , -0.02001994,\n",
-       "       -0.13605377, -0.09357425, -0.00459541,  0.05862798,  0.11163482,\n",
-       "        0.09602874,  0.05599573,  0.11830369,  0.28834433,  0.07279747,\n",
-       "        0.21941021,  0.05890276,  0.11421152,  0.08236516,  0.01092236,\n",
-       "       -0.04497362, -0.04663858, -0.0670035 , -0.21887831,  0.30405694,\n",
-       "        0.14758091,  0.12484246,  0.01801111,  0.01998386, -0.06958026,\n",
-       "       -0.22626343, -0.19633301, -0.34295916, -0.04050547, -0.31439477,\n",
-       "       -0.29516358, -0.23092622, -0.04689604, -0.0397324 , -0.01048109,\n",
-       "        0.01823587,  0.09520633,  0.2304076 ,  0.07931317,  0.59493269,\n",
-       "        0.42148916,  0.30936307,  0.19831751, -0.04594672, -0.19822369,\n",
-       "       -0.39581912, -0.55086584, -0.526165  , -0.4815598 , -0.66516286,\n",
-       "       -0.63227259, -0.43886083, -0.21653109, -0.27231148, -0.10094695,\n",
-       "        0.11938996,  0.42351928,  0.28956548,  0.47690002,  0.74804334,\n",
-       "...\n",
-       "       -0.37656809, -0.22852519, -0.04344777,  0.01792701,  0.17333448,\n",
-       "        0.32389057,  0.46390007,  0.6644992 ,  0.62893444,  0.48691719,\n",
-       "        0.45608447,  0.68040179,  0.46885146,  0.21583469,  0.00941059,\n",
-       "       -0.1331172 , -0.40796933, -0.3614278 , -0.52244927, -0.27981908,\n",
-       "       -0.27632693, -0.23998643, -0.08445465,  0.07873908,  0.07181187,\n",
-       "        0.06149854,  0.19158247,  0.21913798,  0.34554757,  0.30641554,\n",
-       "        0.0175934 ,  0.2244458 ,  0.08640918,  0.11910919,  0.00544381,\n",
-       "       -0.02982331, -0.2136362 , -0.10050146, -0.20911187,  0.01803716,\n",
-       "        0.22387688,  0.08988479,  0.06094732, -0.05726633,  0.0011813 ,\n",
-       "       -0.04608545, -0.185028  , -0.11268017, -0.14596539, -0.25748198,\n",
-       "        0.0815998 , -0.12245219, -0.09431421, -0.05502579, -0.00772848,\n",
-       "        0.04972775,  0.02800019,  0.16214991,  0.26727786,  0.48381549,\n",
-       "        0.40825614,  0.31748295,  0.06169299,  0.07942008, -0.1360152 ,\n",
-       "       -0.28038185, -0.35898583, -0.39069976, -0.46796485, -0.39132032,\n",
-       "       -0.60070257, -0.46962788, -0.3359683 , -0.24423894,  0.06888536,\n",
-       "       -0.05912582,  0.38349241,  0.49376074,  0.39838327,  0.58905439,\n",
-       "        0.56349504,  0.25152149, -0.14721418, -0.33043202, -0.50757441,\n",
-       "       -0.52404807, -0.76061991, -0.75971976, -0.74589083, -0.59263825,\n",
-       "       -0.7027589 , -0.52312689, -0.30315877, -0.00300608,  0.194532  ,\n",
-       "        0.29945327,  0.48218566,  0.64474778])\n",
-       "Coordinates:\n",
-       "  * cell     (cell) int64 3kB 0 1 2 3 4 5 6 7 ... 391 392 393 394 395 396 397
" - ], - "text/plain": [ - " Size: 3kB\n", - "array([-0.6828435 , -0.52020535, -0.34043656, -0.05049317, -0.12985816,\n", - " 0.31035596, 0.55679515, 0.73714634, 0.79715029, 0.75440137,\n", - " 0.73846747, 0.56186966, 0.50518574, 0.24205725, 0.02472535,\n", - " -0.17479924, -0.41243175, -0.62933553, -0.68358687, -0.48097563,\n", - " -0.29176974, -0.24634718, -0.01120315, 0.14080408, 0.089748 ,\n", - " 0.33788694, 0.55161779, 0.47166168, 0.67826547, 0.6011212 ,\n", - " 0.38839256, 0.46979398, 0.18574702, 0.31437291, -0.02156659,\n", - " -0.06562757, -0.17649315, -0.42198051, -0.308658 , -0.02001994,\n", - " -0.13605377, -0.09357425, -0.00459541, 0.05862798, 0.11163482,\n", - " 0.09602874, 0.05599573, 0.11830369, 0.28834433, 0.07279747,\n", - " 0.21941021, 0.05890276, 0.11421152, 0.08236516, 0.01092236,\n", - " -0.04497362, -0.04663858, -0.0670035 , -0.21887831, 0.30405694,\n", - " 0.14758091, 0.12484246, 0.01801111, 0.01998386, -0.06958026,\n", - " -0.22626343, -0.19633301, -0.34295916, -0.04050547, -0.31439477,\n", - " -0.29516358, -0.23092622, -0.04689604, -0.0397324 , -0.01048109,\n", - " 0.01823587, 0.09520633, 0.2304076 , 0.07931317, 0.59493269,\n", - " 0.42148916, 0.30936307, 0.19831751, -0.04594672, -0.19822369,\n", - " -0.39581912, -0.55086584, -0.526165 , -0.4815598 , -0.66516286,\n", - " -0.63227259, -0.43886083, -0.21653109, -0.27231148, -0.10094695,\n", - " 0.11938996, 0.42351928, 0.28956548, 0.47690002, 0.74804334,\n", - "...\n", - " -0.37656809, -0.22852519, -0.04344777, 0.01792701, 0.17333448,\n", - " 0.32389057, 0.46390007, 0.6644992 , 0.62893444, 0.48691719,\n", - " 0.45608447, 0.68040179, 0.46885146, 0.21583469, 0.00941059,\n", - " -0.1331172 , -0.40796933, -0.3614278 , -0.52244927, -0.27981908,\n", - " -0.27632693, -0.23998643, -0.08445465, 0.07873908, 0.07181187,\n", - " 0.06149854, 0.19158247, 0.21913798, 0.34554757, 0.30641554,\n", - " 0.0175934 , 0.2244458 , 0.08640918, 0.11910919, 0.00544381,\n", - " -0.02982331, -0.2136362 , -0.10050146, -0.20911187, 0.01803716,\n", - " 0.22387688, 0.08988479, 0.06094732, -0.05726633, 0.0011813 ,\n", - " -0.04608545, -0.185028 , -0.11268017, -0.14596539, -0.25748198,\n", - " 0.0815998 , -0.12245219, -0.09431421, -0.05502579, -0.00772848,\n", - " 0.04972775, 0.02800019, 0.16214991, 0.26727786, 0.48381549,\n", - " 0.40825614, 0.31748295, 0.06169299, 0.07942008, -0.1360152 ,\n", - " -0.28038185, -0.35898583, -0.39069976, -0.46796485, -0.39132032,\n", - " -0.60070257, -0.46962788, -0.3359683 , -0.24423894, 0.06888536,\n", - " -0.05912582, 0.38349241, 0.49376074, 0.39838327, 0.58905439,\n", - " 0.56349504, 0.25152149, -0.14721418, -0.33043202, -0.50757441,\n", - " -0.52404807, -0.76061991, -0.75971976, -0.74589083, -0.59263825,\n", - " -0.7027589 , -0.52312689, -0.30315877, -0.00300608, 0.194532 ,\n", - " 0.29945327, 0.48218566, 0.64474778])\n", - "Coordinates:\n", - " * cell (cell) int64 3kB 0 1 2 3 4 5 6 7 ... 391 392 393 394 395 396 397" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], "source": [ "grid_polys = polygons_from_coords(lon_s, lat_s)\n", "\n", @@ -885,7 +149,7 @@ }, { "cell_type": "markdown", - "id": "e3d11805", + "id": "8", "metadata": {}, "source": [ "## Plot the regridded field on the mesh" @@ -893,38 +157,10 @@ }, { "cell_type": "code", - "execution_count": 5, - "id": "59fc0b6b", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:16.244561Z", - "iopub.status.busy": "2026-04-19T17:16:16.244485Z", - "iopub.status.idle": "2026-04-19T17:16:16.304359Z", - "shell.execute_reply": "2026-04-19T17:16:16.303915Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "Text(0.5, 1.0, 'regridded onto 398-cell Voronoi mesh')" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAooAAAEYCAYAAADbMtdZAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzsvQdcXNl5Nv5MZSpD772DBAgQSKj33vvu2ms7iZMvif3F9vclsfNP7Nhx4rTPdoodO467t2i16r13oYqEaEKA6L3OAMP0+f/egwYBmmHuFLRF8/z2Lpo7d24995znvOV5eVar1QoffPDBBx988MEHH3yYAv7UFT744IMPPvjggw8++OAjij744IMPPvjggw8+OITPouiDDz744IMPPvjgg134iKIPPvjggw8++OCDD3bhI4o++OCDDz744IMPPtiFjyj64IMPPvjggw8++GAXPqLogw8++OCDDz744INd+IiiDz744IMPPvjggw924SOKPvjggw8++OCDDz7YhY8o+uCDDz744IMPPvhgFz6i6IMPPvjggw8++OBFXLt2DZs3b0ZUVBR4PB6OHDni9DdXrlxBfn4+/Pz8kJKSgl/96lcvbfOjH/0ICQkJkEgkmDdvHu7evTvjz81HFH3wwQcffPDBBx+8iJGREeTm5jJixwUNDQ3YuHEjli9fjkePHuErX/kK/uAP/gBnz54d32b//v342te+hm9961soLS1l+1+7di26u7tn9Nn5iKIPn1jQ7ItmavTXGZYtW8YWb+6TK/72b/+W7dOHTw7stYPPf/7zbCbvw8y8J6/qnD/88MOP+lR8eA2wfv16fPe738X27ds5bf+Tn/wEiYmJ+H//7/8hMzMTX/rSl7Br1y784Ac/GN/m+9//Pr74xS/iC1/4ArKysthvZDIZfvGLX8zglfiIog8+fGqh1WoZSfX2YD46Oorf//3fx+zZs6FSqaBQKNjM9t/+7d9gNBpf2v78+fNYtGgR69ACAwNZ59fY2PjSdjqdDt/73vdYB0jbRkdHY/fu3aisrMQnATSrFwqF+MxnPuNwm6GhIUilUuzYseOVnpsPPvgAu32ORqPhvKjV6pfW6fV6r9zakpISrFq1atI6shbSeoLBYMCDBw8mbcPn89ln2zYzBeGM7t0HH2YQS5YsYaRFLBb77rMDovjtb3+b/ZuLNZUr6J4Teduw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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], "source": [ "fig, ax = plt.subplots(figsize=(8, 4))\n", "patches = [np.asarray(p.exterior.coords) for p in mesh_polys]\n", @@ -939,7 +175,7 @@ }, { "cell_type": "markdown", - "id": "0abb24ac", + "id": "10", "metadata": {}, "source": [ "## Persist the regridder to netCDF\n", @@ -950,37 +186,10 @@ }, { "cell_type": "code", - "execution_count": 6, - "id": "e4e09ea1", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:16.305498Z", - "iopub.status.busy": "2026-04-19T17:16:16.305432Z", - "iopub.status.idle": "2026-04-19T17:16:16.644657Z", - "shell.execute_reply": "2026-04-19T17:16:16.644235Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "wrote /var/folders/n1/xlk9kjfn39l5q4z54rnbz4tc0000gp/T/mesh_regridder.nc (951.7 KB)\n", - " x_coord: \n", - " y_coord: \n", - " spherical: 0\n", - " src_dims: src_cell\n", - " dst_dims: cell\n", - " src_shape: 24000\n", - " dst_shape: 398\n", - " xarray_regrid_version: 0.4.2\n", - " created: 2026-04-19T17:16:16.307818+00:00\n", - " schema_version: 1\n", - " n_dst: 398\n", - " n_src: 24000\n" - ] - } - ], + "execution_count": null, + "id": "11", + "metadata": {}, + "outputs": [], "source": [ "path = Path(tempfile.gettempdir()) / \"mesh_regridder.nc\"\n", "rgr.to_netcdf(path)\n", @@ -994,7 +203,7 @@ }, { "cell_type": "markdown", - "id": "c9b6292e", + "id": "12", "metadata": {}, "source": [ "## Reload and apply to new data" @@ -1002,25 +211,10 @@ }, { "cell_type": "code", - "execution_count": 7, - "id": "9ed8721a", - "metadata": { - "execution": { - "iopub.execute_input": "2026-04-19T17:16:16.645670Z", - "iopub.status.busy": "2026-04-19T17:16:16.645544Z", - "iopub.status.idle": "2026-04-19T17:16:16.658347Z", - "shell.execute_reply": "2026-04-19T17:16:16.657960Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "max diff reloaded vs original: 0.00e+00\n" - ] - } - ], + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], "source": [ "rgr2 = ConservativeRegridder.from_netcdf(path)\n", "\n", From 241337d9fa97a5c50dbf2c39c18919c451b269ea Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Tue, 21 Apr 2026 15:30:02 -0500 Subject: [PATCH 12/16] Add s2geometry backend for great-circle conservative regridding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a `manifold` kwarg to ConservativeRegridder, conservative_2d_regrid, and the `.regrid.conservative_2d` accessor. Values: "planar" (default, unchanged), "cea" (the former `spherical=True` path — cylindrical equal-area projection, correct areas on the planar fast path), and "s2" (new — great-circle cell polygons + intersections via the optional spherely dependency, exact spherical geometry including near the poles). Key additions: - `Manifold = Literal["planar", "cea", "s2"]` with matching `_MANIFOLDS` runtime tuple (kept in sync via get_args). - Optional spherely import, `_check_manifold` dispatched at construction and again in `_from_state` as defense-in-depth for netCDF reloads. - `_build_s2_grid` / `_s2_cell_polys` builders; `_Grid` gains `s2_polys: np.ndarray | None`; `_build_intersection_areas` dispatches to spherely when both grids carry s2_polys, keeping the shapely STRtree as the (lossless) candidate-pair filter since spherely has no spatial index yet (benbovy/spherely#72). - Metadata on-disk schema bumped to 2: stores `manifold` string instead of the old `spherical` bool. `polygons_from_coords` narrows to Literal["planar", "cea"] — s2 isn't representable outside the regridder since spherely Geographies aren't shapely polygons. Squash of the 6 spherely-integration commits, rebased onto the conservative branch (which dropped the RegridderMetadata dataclass in favor of kwargs-dict helpers, switched the per-direction cache to cached_property, and flipped predicate_filter to bbox-only by default). All manifold plumbing is wired through the new shape; netCDF metadata now emits "manifold" via the helpers and parses it back. Tests: 108 pass (up from 103), including 5 s2-specific tests for mass conservation, constant-field preservation, netCDF roundtrip, pole-touching cells, and invalid-manifold rejection. --- .../demo_conservative_2d_curvilinear.ipynb | 91 +- .../demos/demo_conservative_2d_regions.ipynb | 279 ++--- .../demo_conservative_2d_spherical.ipynb | 216 ++++ .../demo_conservative_2d_unstructured.ipynb | 14 +- pyproject.toml | 12 +- src/xarray_regrid/methods/conservative_2d.py | 1075 ++++++++++------- src/xarray_regrid/regrid.py | 49 +- tests/test_conservative_2d.py | 133 +- 8 files changed, 1152 insertions(+), 717 deletions(-) create mode 100644 docs/notebooks/demos/demo_conservative_2d_spherical.ipynb diff --git a/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb b/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb index 42a2679..9eb7b17 100644 --- a/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb +++ b/docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb @@ -7,24 +7,10 @@ "source": [ "# Conservative 2D regrid — curvilinear target\n", "\n", - "**Conservative regridding** resamples a gridded field while preserving its\n", - "area-weighted integral: each output cell is the area-weighted average of\n", - "the source cells it overlaps. It's the right tool for fluxes and intensive\n", - "quantities — precipitation, sea-surface temperature, mass-balance budgets —\n", - "where bilinear or nearest-neighbor interpolation would bias the total.\n", - "\n", - "The fast `.regrid.conservative` accessor only works on **1D-separable**\n", - "grids: plain rectilinear lat/lon, where lat depends only on `y` and lon\n", - "only on `x`. **Curvilinear** grids store coordinates as 2D arrays\n", - "`lat(y, x)` / `lon(y, x)` and are common in ocean models (ORCA, tripolar)\n", - "or any rotated/projected setup. Their cells aren't axis-aligned, so we\n", - "drop to `.regrid.conservative_2d`, which builds the full 2D polygon\n", - "intersection.\n", - "\n", - "**In this notebook.** We regrid a smooth analytic two-bump field from a\n", - "regular lat/lon grid onto a 30°-rotated curvilinear target — a minimal\n", - "stand-in for any curvilinear ocean grid — and verify that the\n", - "area-weighted integral is preserved to machine precision." + "Curvilinear grids have 2D `lat(y, x)` / `lon(y, x)` coordinate arrays —\n", + "ocean models (ORCA, tripolar), rotated regional forecasts. Not\n", + "1D-separable, so `.conservative` can't handle them;\n", + "`.regrid.conservative_2d` is the tool." ] }, { @@ -34,9 +20,9 @@ "metadata": {}, "outputs": [], "source": [ + "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import xarray as xr\n", - "import matplotlib.pyplot as plt\n", "\n", "import xarray_regrid # noqa: F401\n", "from xarray_regrid import ConservativeRegridder" @@ -47,11 +33,7 @@ "id": "2", "metadata": {}, "source": [ - "## Source — regular 1° lat/lon, analytic two-bump field\n", - "\n", - "A simple synthetic field — one positive Gaussian bump in the northern\n", - "hemisphere, one negative in the southern — gives us something we can\n", - "visually track from source to target." + "## Source — regular 1° lat/lon, analytic two-bump field" ] }, { @@ -85,10 +67,8 @@ "source": [ "## Target — rotated curvilinear grid\n", "\n", - "To break 1D-separability we take a regular `(ny, nx)` mesh and rotate it\n", - "30° in the lat/lon plane. The result has the same kind of 2D coordinate\n", - "variables you'd find in an ORCA or rotated-pole grid: `longitude(ny, nx)`\n", - "and `latitude(ny, nx)` riding on a non-axis-aligned mesh." + "Coordinates ride on a `(ny, nx)` mesh, stored as 2D coordinate\n", + "variables on the target Dataset." ] }, { @@ -116,7 +96,8 @@ "ax.plot(lon2d, lat2d, color=\"0.3\", lw=0.4)\n", "ax.plot(lon2d.T, lat2d.T, color=\"0.3\", lw=0.4)\n", "ax.set_title(\"curvilinear target (30° rotation)\")\n", - "ax.set_xlabel(\"longitude\"); ax.set_ylabel(\"latitude\")\n", + "ax.set_xlabel(\"longitude\")\n", + "ax.set_ylabel(\"latitude\")\n", "ax.set_aspect(\"equal\")" ] }, @@ -127,13 +108,9 @@ "source": [ "## Regrid and plot\n", "\n", - "`ConservativeRegridder` builds the weight matrix once — that's the\n", - "expensive step, since it computes the area of every source/target polygon\n", - "intersection — and lets us reuse it for the apply step and for the\n", - "diagnostic in the next cell. The one-shot equivalent is\n", - "`src.regrid.conservative_2d(target, ...)`. Either way, output is on the\n", - "curvilinear `(ny, nx)` mesh, with NaN where target cells fall outside the\n", - "source domain." + "One-shot form: `src.regrid.conservative_2d(target, x_coord=..., y_coord=...)`.\n", + "Constructing the class directly (as below) is equivalent and lets us reuse\n", + "the weight matrix across multiple applies." ] }, { @@ -153,48 +130,15 @@ " shading=\"auto\", vmin=-1, vmax=1)\n", "fig.colorbar(pc, ax=ax, shrink=0.8)\n", "ax.set_title(\"regridded onto rotated curvilinear grid\")\n", - "ax.set_xlabel(\"longitude\"); ax.set_ylabel(\"latitude\")\n", + "ax.set_xlabel(\"longitude\")\n", + "ax.set_ylabel(\"latitude\")\n", "ax.set_aspect(\"equal\")" ] - }, - { - "cell_type": "markdown", - "id": "8", - "metadata": {}, - "source": [ - "## Conservation check\n", - "\n", - "The regridder stores its area-intersection matrix\n", - "`A[i, j] = area(target_i ∩ source_j)`. For target cells fully inside the\n", - "source domain, the area-weighted sum of outputs equals the direct A·s\n", - "integral to machine precision — the defining property of *conservative*\n", - "regridding." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9", - "metadata": {}, - "outputs": [], - "source": [ - "A = rgr.areas\n", - "src_cover = np.ravel(A.sum(axis=0).todense())\n", - "tgt_cover = A.sum(axis=1).todense().reshape(regridded.shape)\n", - "valid = np.isfinite(regridded.values)\n", - "\n", - "direct = float((src.values.ravel() * src_cover).sum())\n", - "via_regrid = float((regridded.values[valid] * tgt_cover[valid]).sum())\n", - "print(f\"direct : {direct:.6f}\")\n", - "print(f\"via regrid : {via_regrid:.6f}\")\n", - "print(f\"relative err : {abs(direct - via_regrid) / max(abs(direct), 1e-12):.2e}\")\n", - "print(f\"coverage : {valid.mean():.2%} of target cells inside source\")" - ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -207,7 +151,8 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3" + "pygments_lexer": "ipython3", + "version": "3.12.13" } }, "nbformat": 4, diff --git a/docs/notebooks/demos/demo_conservative_2d_regions.ipynb b/docs/notebooks/demos/demo_conservative_2d_regions.ipynb index a66f949..13ae692 100644 --- a/docs/notebooks/demos/demo_conservative_2d_regions.ipynb +++ b/docs/notebooks/demos/demo_conservative_2d_regions.ipynb @@ -2,50 +2,34 @@ "cells": [ { "cell_type": "markdown", - "id": "e5c70992", + "id": "0", "metadata": {}, "source": [ - "# Conservative 2D regrid — regions (grid → country/state polygons)\n", + "# Conservative 2D regrid — regions (grid → arbitrary polygons)\n", "\n", - "A common analysis question is: *given a gridded variable, what's its\n", - "area-weighted mean over each of these regions?* — countries, states,\n", - "watersheds, ocean basins, exclusive economic zones. This is the canonical\n", - "[xagg](https://github.com/ks905383/xagg)-style workflow and a natural fit\n", - "for **conservative regridding**: each region's output value is the\n", - "area-weighted average of the source cells it overlaps, which is exactly\n", - "what you want for fluxes and intensive quantities (precipitation,\n", - "temperature, mass-balance budgets) where bilinear or nearest-neighbor\n", - "interpolation would bias the total.\n", + "Gridded data → arbitrary polygon regions (countries, watersheds, ocean\n", + "basins, protected areas) is a canonical xagg-style workflow. Because\n", + "regions aren't a grid at all, `.conservative` can't express it;\n", + "`ConservativeRegridder.from_polygons` takes a numpy array of shapely\n", + "polygons and produces one conservatively-averaged value per region.\n", "\n", - "Because the targets are arbitrary polygons rather than a grid, the fast\n", - "`.regrid.conservative` accessor doesn't apply. We use\n", - "`ConservativeRegridder.from_polygons`, which takes a 1D array of shapely\n", - "polygons as source and target.\n", - "\n", - "**In this notebook.**\n", - "\n", - "1. Aggregate xarray's air-temperature tutorial dataset (NMC reanalysis,\n", - " ~daily 2.5° lat/lon over North America) onto US states built by\n", - " dissolving county boundaries from `geodatasets`.\n", - "2. Visualize the per-state means against the source grid.\n", - "3. Verify conservation directly from the internal weight matrix.\n", - "4. Save the regridder, reload it, and reuse it on JJA vs. DJF subsets to\n", - " get per-state seasonal swings — showing the weight matrix is reusable\n", - " across any source field on the same grid." + "This notebook uses hand-built synthetic regions so it runs with zero\n", + "external downloads — swap in `geopandas.read_file(...)` for a real\n", + "shapefile and the rest is identical." ] }, { "cell_type": "code", "execution_count": null, - "id": "b28b1f2e", + "id": "1", "metadata": {}, "outputs": [], "source": [ + "import matplotlib.pyplot as plt\n", "import numpy as np\n", + "import shapely\n", "import xarray as xr\n", - "import matplotlib.pyplot as plt\n", - "import geopandas as gpd\n", - "import geodatasets\n", + "from matplotlib.collections import PolyCollection\n", "\n", "import xarray_regrid # noqa: F401\n", "from xarray_regrid import ConservativeRegridder, polygons_from_coords" @@ -53,248 +37,218 @@ }, { "cell_type": "markdown", - "id": "537c0ed3", + "id": "2", "metadata": {}, "source": [ - "## Source — NCEP reanalysis surface air temperature\n", + "## Source — structured lat/lon field\n", "\n", - "`xr.tutorial.open_dataset(\"air_temperature\")` is a small (~3 MB) NMC\n", - "reanalysis subset: 4×daily surface air temperature over North America on\n", - "a 2.5° lat/lon grid. We reduce it to a long-term mean, convert from\n", - "Kelvin to °C, and align with the state polygons' CRS:\n", - "\n", - "- Native longitudes are on `[0, 360)`; states are on `[-180, 180)`, so\n", - " we wrap.\n", - "- Native latitudes are descending; we sort to ascending so xarray's\n", - " `pcolormesh` doesn't flip the image." + "A smooth analytic field with recognizable geographic pattern: a warm\n", + "band following the equator modulated by an east/west tilt. Gives visibly\n", + "different regional means depending on region location." ] }, { "cell_type": "code", "execution_count": null, - "id": "721e935c", + "id": "3", "metadata": {}, "outputs": [], "source": [ - "ds = xr.tutorial.open_dataset(\"air_temperature\")\n", - "air = (ds[\"air\"].mean(\"time\") - 273.15).sortby(\"lat\")\n", - "air = air.assign_coords(lon=(((air.lon + 180) % 360) - 180)).sortby(\"lon\")\n", - "air.attrs[\"units\"] = \"degC\"\n", - "air.name = \"mean_air_temperature\"\n", - "air" + "lat = np.linspace(-80, 80, 160) + 0.5\n", + "lon = np.linspace(-180, 180, 360, endpoint=False) + 0.5\n", + "Lo, La = np.meshgrid(lon, lat)\n", + "field = np.cos(np.deg2rad(La)) ** 2 + 0.4 * np.sin(np.deg2rad(Lo))\n", + "src = xr.DataArray(\n", + " field,\n", + " dims=(\"latitude\", \"longitude\"),\n", + " coords={\"latitude\": lat, \"longitude\": lon},\n", + ")" ] }, { "cell_type": "markdown", - "id": "434441b2", + "id": "4", "metadata": {}, "source": [ - "## Regions — US states from `geodatasets`\n", + "## Regions — five hand-built polygons of varied shape\n", "\n", - "`geodatasets.get_path(\"geoda.ncovr\")` returns a GeoPackage of the 49\n", - "contiguous-US counties (+ DC). We **dissolve** on `STATE_NAME` —\n", - "geopandas' equivalent of a `groupby` for geometries — to combine each\n", - "state's counties into one (Multi)Polygon. The result is exactly what\n", - "`ConservativeRegridder.from_polygons` wants: one shapely (Multi)Polygon\n", - "per region. The air-temperature grid doesn't cover Alaska or Hawaii,\n", - "which is why ncovr (contiguous-US only) is a natural fit." + "Box, circle, rotated rectangle, L-shape, and a polygon with a hole —\n", + "exercising the range of geometry the regridder accepts. These overlap\n", + "in places, which is fine: each region gets its own independent\n", + "area-weighted mean." ] }, { "cell_type": "code", "execution_count": null, - "id": "8c6437b2", + "id": "5", "metadata": {}, "outputs": [], "source": [ - "counties = gpd.read_file(geodatasets.get_path(\"geoda.ncovr\"))\n", - "states = (\n", - " counties.dissolve(by=\"STATE_NAME\", aggfunc=\"first\")\n", - " .reset_index()[[\"STATE_NAME\", \"geometry\"]]\n", - " .sort_values(\"STATE_NAME\")\n", - " .reset_index(drop=True)\n", - ")\n", - "print(f\"{len(states)} states/DC, CRS={states.crs}, \"\n", - " f\"bounds={states.total_bounds.round(1).tolist()}\")\n", - "states.head(3)" + "region_names = [\n", + " \"equatorial band\",\n", + " \"northern island\",\n", + " \"rotated block\",\n", + " \"L-shape\",\n", + " \"ring\",\n", + "]\n", + "region_polys = np.array([\n", + " shapely.box(-180, -15, 180, 15),\n", + " shapely.Point(-60, 50).buffer(18, quad_segs=12),\n", + " shapely.affinity.rotate(shapely.box(55, 27.5, 105, 52.5), 30),\n", + " shapely.unary_union([\n", + " shapely.box(-140, -60, -100, -20),\n", + " shapely.box(-140, -60, -60, -50),\n", + " ]),\n", + " shapely.Polygon(\n", + " shell=[(140, -40), (170, -40), (170, -5), (140, -5)],\n", + " holes=[[(148, -30), (162, -30), (162, -12), (148, -12)]],\n", + " ),\n", + "], dtype=object)\n", + "print(f\"{len(region_polys)} regions, areas (deg²): \"\n", + " f\"{[f'{shapely.area(p):.0f}' for p in region_polys]}\")" ] }, { "cell_type": "markdown", - "id": "3a3b8ac5", + "id": "6", "metadata": {}, "source": [ "## Build the regridder and apply\n", "\n", - "`from_polygons` takes flat 1D arrays of source and target polygons:\n", - "\n", - "- **Source polygons** come from the 1D grid coords via\n", - " `polygons_from_coords`, which builds a rectangle per cell from the\n", - " coordinate midpoints (so a 25×53 grid → 1325 source polygons).\n", - "- **Target polygons** are just the states' `geometry` column.\n", - "\n", - "Source data has to be flattened to a single `src_cell` dimension to match\n", - "the flat polygon array. Constructing the regridder is the expensive step\n", - "— it computes the area of every source/target polygon intersection.\n", - "Once built, `rgr.regrid(...)` is a sparse matrix-vector product against\n", - "the precomputed weights and is essentially instant." + "`from_polygons` takes source + target polygon arrays. We build the\n", + "source polygons from the grid's 1D coords via the `polygons_from_coords`\n", + "helper; the data gets flattened to match." ] }, { "cell_type": "code", "execution_count": null, - "id": "da3e70c8", + "id": "7", "metadata": {}, "outputs": [], "source": [ - "src_polys = polygons_from_coords(air.lon.values, air.lat.values)\n", - "tgt_polys = states.geometry.to_numpy()\n", + "src_polys = polygons_from_coords(lon, lat)\n", "\n", "rgr = ConservativeRegridder.from_polygons(\n", " source_polygons=src_polys,\n", - " target_polygons=tgt_polys,\n", + " target_polygons=region_polys,\n", " source_dim=\"src_cell\",\n", - " target_dim=\"state\",\n", - " target_coords=xr.Dataset(coords={\"state\": states.STATE_NAME.values}),\n", + " target_dim=\"region\",\n", + " target_coords=xr.Dataset(coords={\"region\": region_names}),\n", ")\n", - "\n", - "src_flat = xr.DataArray(air.values.ravel(), dims=(\"src_cell\",))\n", - "state_mean = rgr.regrid(src_flat)\n", - "state_mean.attrs[\"units\"] = \"degC\"\n", - "state_mean.to_series().sort_values().round(2)" + "src_flat = xr.DataArray(src.values.ravel(), dims=(\"src_cell\",))\n", + "regional = rgr.regrid(src_flat)\n", + "regional" ] }, { "cell_type": "markdown", - "id": "3542f836", + "id": "8", "metadata": {}, "source": [ - "## Map + ranked bar chart\n", - "\n", - "States filled by area-weighted mean temperature, with the source grid\n", - "shown underneath for reference. Both panels share the same `vmin`/`vmax`\n", - "so a state's color on the bar chart matches its color on the map. A\n", - "sanity check: the warm/cool gradient should track latitude (Florida and\n", - "the Gulf states warmest, the Upper Midwest and Northeast coolest)." + "## Source field + region outlines + regional means" ] }, { "cell_type": "code", "execution_count": null, - "id": "bb8d0dda", + "id": "9", "metadata": {}, "outputs": [], "source": [ "fig, (ax_map, ax_bar) = plt.subplots(\n", - " 1, 2, figsize=(14, 6), gridspec_kw={\"width_ratios\": [1.6, 1]},\n", + " 1, 2, figsize=(13, 4.5), gridspec_kw={\"width_ratios\": [1.6, 1]},\n", ")\n", - "\n", - "air.plot(ax=ax_map, cmap=\"coolwarm\", alpha=0.5,\n", - " cbar_kwargs={\"shrink\": 0.65, \"label\": \"grid mean T [°C]\"})\n", - "\n", - "vmin, vmax = float(air.min()), float(air.max())\n", - "states_plot = states.assign(mean_T=state_mean.values)\n", - "states_plot.plot(\n", - " column=\"mean_T\", cmap=\"coolwarm\", ax=ax_map,\n", - " edgecolor=\"black\", linewidth=0.4,\n", - " vmin=vmin, vmax=vmax,\n", + "src.plot(ax=ax_map, cmap=\"viridis\", add_colorbar=True,\n", + " cbar_kwargs={\"shrink\": 0.7, \"label\": \"source field\"})\n", + "patches = [np.asarray(p.exterior.coords) for p in region_polys]\n", + "pc = PolyCollection(\n", + " patches, facecolor=\"none\", edgecolor=\"white\", lw=1.4,\n", ")\n", - "ax_map.set_xlim(-128, -65); ax_map.set_ylim(22, 52)\n", - "ax_map.set_title(\"Annual mean surface T — states vs. source grid\")\n", - "ax_map.set_xlabel(\"longitude\"); ax_map.set_ylabel(\"latitude\")\n", + "ax_map.add_collection(pc)\n", + "for name, poly in zip(region_names, region_polys, strict=True):\n", + " c = poly.representative_point()\n", + " ax_map.annotate(name, (c.x, c.y), color=\"white\", fontsize=8,\n", + " ha=\"center\", va=\"center\")\n", + "ax_map.set_title(\"source + region outlines\")\n", + "ax_map.set_xlim(-180, 180)\n", + "ax_map.set_ylim(-80, 80)\n", "\n", - "ordered = state_mean.to_series().sort_values()\n", - "colors = plt.cm.coolwarm((ordered.values - vmin) / (vmax - vmin))\n", - "ax_bar.barh(ordered.index, ordered.values, color=colors)\n", - "ax_bar.set_xlabel(\"area-weighted mean T [°C]\")\n", - "ax_bar.tick_params(axis=\"y\", labelsize=7)\n", + "ax_bar.barh(region_names, regional.values, color=\"tab:blue\")\n", + "ax_bar.set_xlabel(\"area-weighted regional mean\")\n", + "ax_bar.invert_yaxis()\n", "ax_bar.grid(axis=\"x\", alpha=0.3)\n", "plt.tight_layout()" ] }, { "cell_type": "markdown", - "id": "803247d2", + "id": "10", "metadata": {}, "source": [ "## Conservation check\n", "\n", - "The internal area matrix `A[i, j] = area(state_i ∩ src_cell_j)` lets us\n", - "verify conservation directly: integrating the source field weighted by\n", - "source-cell coverage should equal integrating the regridded field\n", - "weighted by target-cell area. Equality to machine precision is the\n", - "defining property of *conservative* regridding." + "Area-weighted sum of regional means × region areas should match the\n", + "direct A·s computation from the regridder's internal area matrix." ] }, { "cell_type": "code", "execution_count": null, - "id": "041a51f3", + "id": "11", "metadata": {}, "outputs": [], "source": [ - "A = rgr.areas # (n_states, n_src)\n", - "tgt_area = rgr.target_areas\n", - "src_cover = rgr.source_coverage_areas\n", + "A = rgr._areas # sparse (n_regions, n_src)\n", + "tgt_area = np.ravel(A.sum(axis=1).todense())\n", + "src_cover = np.ravel(A.sum(axis=0).todense())\n", "\n", - "direct = float((air.values.ravel() * src_cover).sum())\n", - "via_regrid = float((state_mean.values * tgt_area).sum())\n", + "direct = float((src.values.ravel() * src_cover).sum())\n", + "via_regrid = float((regional.values * tgt_area).sum())\n", "print(f\"direct A·s : {direct:.6f}\")\n", - "print(f\"Σ state_mean · a_state : {via_regrid:.6f}\")\n", + "print(f\"Σ regional_mean · a_dst : {via_regrid:.6f}\")\n", "print(f\"relative error : {abs(direct - via_regrid) / abs(direct):.2e}\")" ] }, { "cell_type": "markdown", - "id": "ff49d6b7", + "id": "12", "metadata": {}, "source": [ - "## Reuse: persist the regridder, apply to summer vs. winter\n", + "## Reuse: persist the regridder to netCDF\n", "\n", - "The weight matrix depends only on the source/target geometry, not on the\n", - "data. Save once with `to_netcdf`, reload with `from_netcdf`, and apply\n", - "to any field on the same source grid — no need to rebuild the (expensive)\n", - "polygon intersection. Below we use one saved regridder on JJA and DJF\n", - "subsets to compute per-state seasonal amplitude. Continental interior\n", - "states show the largest swing; Florida and California — moderated by\n", - "ocean and latitude — show the smallest." + "The weight matrix is reusable. For any workflow that repeatedly\n", + "aggregates new source data onto the same region set, save once,\n", + "reload on subsequent runs to skip the intersection build." ] }, { "cell_type": "code", "execution_count": null, - "id": "8fa88419", + "id": "13", "metadata": {}, "outputs": [], "source": [ "import tempfile\n", "from pathlib import Path\n", - "path = Path(tempfile.gettempdir()) / \"states_regridder.nc\"\n", - "rgr.to_netcdf(path)\n", - "print(f\"wrote {path.name} ({path.stat().st_size / 1024:.1f} KB)\")\n", "\n", + "path = Path(tempfile.gettempdir()) / \"regions_regridder.nc\"\n", + "rgr.to_netcdf(path)\n", "rgr2 = ConservativeRegridder.from_netcdf(path)\n", "\n", - "def seasonal_mean(months):\n", - " sub = ds[\"air\"].sel(time=ds[\"time.month\"].isin(months)).mean(\"time\") - 273.15\n", - " sub = sub.sortby(\"lat\")\n", - " sub = sub.assign_coords(lon=(((sub.lon + 180) % 360) - 180)).sortby(\"lon\")\n", - " flat = xr.DataArray(sub.values.ravel(), dims=(\"src_cell\",))\n", - " return rgr2.regrid(flat)\n", - "\n", - "summer = seasonal_mean([6, 7, 8])\n", - "winter = seasonal_mean([12, 1, 2])\n", - "amplitude = (summer - winter).to_series().rename(\"JJA − DJF [°C]\").round(1)\n", - "print(\"largest seasonal swing:\")\n", - "print(amplitude.sort_values(ascending=False).head(5))\n", - "print(\"\\nsmallest seasonal swing (maritime / subtropical):\")\n", - "print(amplitude.sort_values().head(5))" + "other = xr.DataArray(\n", + " (np.sin(np.deg2rad(Lo)) ** 2).ravel(), dims=(\"src_cell\",),\n", + ")\n", + "print(\"regional means on a different field:\")\n", + "for n, v in zip(region_names, rgr2.regrid(other).values, strict=True):\n", + " print(f\" {n:20s}: {v:+.4f}\")" ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -307,7 +261,8 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3" + "pygments_lexer": "ipython3", + "version": "3.12.13" } }, "nbformat": 4, diff --git a/docs/notebooks/demos/demo_conservative_2d_spherical.ipynb b/docs/notebooks/demos/demo_conservative_2d_spherical.ipynb new file mode 100644 index 0000000..158b50b --- /dev/null +++ b/docs/notebooks/demos/demo_conservative_2d_spherical.ipynb @@ -0,0 +1,216 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Conservative 2D regrid — spherical `s2` manifold\n", + "\n", + "The `manifold` kwarg on `conservative_2d` selects the intersection\n", + "geometry:\n", + "\n", + "- `\"planar\"` (default): raw planar shapely. Wrong areas for lat/lon\n", + " grids far from the equator.\n", + "- `\"cea\"`: Lambert cylindrical equal-area projection, then planar\n", + " intersection. Correct spherical *areas* but straight edges in the\n", + " projected plane.\n", + "- `\"s2\"`: Google's s2geometry via the optional\n", + " [`spherely`](https://github.com/benbovy/spherely) package — true\n", + " great-circle edges, exact spherical areas.\n", + "\n", + "Install s2 support with `pip install xarray-regrid[spherical]`. This\n", + "notebook shows when the difference between `cea` and `s2` matters\n", + "(coarse cells) and when you can stay on `cea` (fine cells)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1", + "metadata": {}, + "outputs": [], + "source": [ + "import tempfile\n", + "from pathlib import Path\n", + "\n", + "import numpy as np\n", + "import xarray as xr\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import xarray_regrid # noqa: F401\n", + "from xarray_regrid import ConservativeRegridder" + ] + }, + { + "cell_type": "markdown", + "id": "2", + "metadata": {}, + "source": [ + "## Source — $\\cos^2(\\mathrm{lat})$ on a 1° grid\n", + "\n", + "The integral of $\\cos^2(\\phi)$ over the unit sphere is $8\\pi/3$. We\n", + "use this as a known-truth check on each manifold." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "lat = np.linspace(-89.5, 89.5, 180)\n", + "lon = np.linspace(-179.5, 179.5, 360)\n", + "src = xr.DataArray(\n", + " (np.cos(np.deg2rad(lat)) ** 2)[:, None] * np.ones(lon.size)[None, :],\n", + " dims=(\"latitude\", \"longitude\"),\n", + " coords={\"latitude\": lat, \"longitude\": lon},\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "4", + "metadata": {}, + "source": [ + "## All three manifolds on a fine target\n", + "\n", + "We regrid onto a 2° target and integrate the output against true\n", + "spherical cell areas. `cea` and `s2` should both hit the source-grid\n", + "quadrature floor (~1e-4 at 1°); `planar` is limited by its\n", + "mis-weighted internal areas." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5", + "metadata": {}, + "outputs": [], + "source": [ + "TRUE = 8 * np.pi / 3\n", + "\n", + "def sph_cell_areas(lat, lon):\n", + " lat_r = np.deg2rad(lat)\n", + " dlat = np.gradient(lat_r)\n", + " dlon = np.gradient(np.deg2rad(lon))\n", + " return (np.sin(lat_r + dlat/2) - np.sin(lat_r - dlat/2))[:, None] * dlon[None, :]\n", + "\n", + "target = xr.Dataset(coords={\n", + " \"latitude\": np.linspace(-89, 89, 90),\n", + " \"longitude\": np.linspace(-179, 179, 180),\n", + "})\n", + "a_tgt = sph_cell_areas(target.latitude.values, target.longitude.values)\n", + "\n", + "print(f\"{'manifold':>8} {'integral':>11} {'|error|':>10}\")\n", + "for m in (\"planar\", \"cea\", \"s2\"):\n", + " out = src.regrid.conservative_2d(\n", + " target, x_coord=\"longitude\", y_coord=\"latitude\", manifold=m,\n", + " ).values\n", + " valid = np.isfinite(out)\n", + " integral = float((out[valid] * a_tgt[valid]).sum())\n", + " print(f\"{m:>8} {integral:>11.6f} {abs(integral - TRUE):>10.2e}\")\n", + "print(f\"{'truth':>8} {TRUE:>11.6f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "6", + "metadata": {}, + "source": [ + "## Where `s2` and `cea` visibly differ\n", + "\n", + "At fine cells (≤2°) `cea` and `s2` agree to within numerical noise.\n", + "The gap opens up at coarse cells, where straight-line edges in the\n", + "projected plane diverge most from great-circle edges." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "coarse_lat = np.linspace(-85, 85, 35)\n", + "coarse_lon = np.linspace(-175, 175, 70)\n", + "coarse_src = xr.DataArray(\n", + " np.cos(np.deg2rad(coarse_lat))[:, None] ** 2\n", + " * np.sin(np.deg2rad(coarse_lon))[None, :],\n", + " dims=(\"latitude\", \"longitude\"),\n", + " coords={\"latitude\": coarse_lat, \"longitude\": coarse_lon},\n", + ")\n", + "coarse_tgt = xr.Dataset(coords={\n", + " \"latitude\": np.linspace(-82.5, 82.5, 12),\n", + " \"longitude\": np.linspace(-172.5, 172.5, 24),\n", + "})\n", + "diff = (\n", + " coarse_src.regrid.conservative_2d(\n", + " coarse_tgt, x_coord=\"longitude\", y_coord=\"latitude\", manifold=\"s2\"\n", + " )\n", + " - coarse_src.regrid.conservative_2d(\n", + " coarse_tgt, x_coord=\"longitude\", y_coord=\"latitude\", manifold=\"cea\"\n", + " )\n", + ")\n", + "\n", + "fig, ax = plt.subplots(figsize=(8, 3))\n", + "diff.plot(ax=ax, cmap=\"RdBu_r\", center=0)\n", + "ax.set_title(\"s2 − cea on a 5°→15° regrid\")\n", + "print(f\"max |s2 − cea|: {float(np.nanmax(np.abs(diff.values))):.2e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": {}, + "source": [ + "## Reuse: persist the s2 regridder\n", + "\n", + "Building the s2 geography polygons has a one-time cost. Save once,\n", + "reload to reapply without rebuilding — the `manifold` choice round-\n", + "trips through the netCDF alongside the weight matrix." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "rgr = ConservativeRegridder(\n", + " src, target,\n", + " x_coord=\"longitude\", y_coord=\"latitude\", manifold=\"s2\",\n", + ")\n", + "path = Path(tempfile.gettempdir()) / \"s2_regridder.nc\"\n", + "rgr.to_netcdf(path)\n", + "rgr2 = ConservativeRegridder.from_netcdf(path)\n", + "print(f\"saved {path.stat().st_size / 1024:.0f} KB, manifold={rgr2.manifold!r}\")\n", + "print(f\"bit-identical forward: \"\n", + " f\"{np.array_equal(rgr.regrid(src).values, rgr2.regrid(src).values)}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb b/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb index b1a939e..e7df2ce 100644 --- a/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb +++ b/docs/notebooks/demos/demo_conservative_2d_unstructured.ipynb @@ -45,12 +45,12 @@ "import tempfile\n", "from pathlib import Path\n", "\n", + "import matplotlib.pyplot as plt\n", "import numpy as np\n", + "import shapely\n", "import xarray as xr\n", - "import matplotlib.pyplot as plt\n", "from matplotlib.collections import PolyCollection\n", "from scipy.spatial import Voronoi\n", - "import shapely\n", "\n", "import xarray_regrid # noqa: F401\n", "from xarray_regrid import ConservativeRegridder, polygons_from_coords" @@ -180,7 +180,8 @@ "pc = PolyCollection(patches, array=mesh_vals.values, cmap=\"RdBu_r\",\n", " edgecolor=\"0.4\", lw=0.3, clim=(-1, 1))\n", "ax.add_collection(pc)\n", - "ax.set_xlim(bbox[0], bbox[2]); ax.set_ylim(bbox[1], bbox[3])\n", + "ax.set_xlim(bbox[0], bbox[2])\n", + "ax.set_ylim(bbox[1], bbox[3])\n", "ax.set_aspect(\"equal\")\n", "fig.colorbar(pc, ax=ax, shrink=0.8)\n", "ax.set_title(f\"regridded onto {len(mesh_polys)}-cell Voronoi mesh\")" @@ -210,14 +211,9 @@ "source": [ "path = Path(tempfile.gettempdir()) / \"mesh_regridder.nc\"\n", "rgr.to_netcdf(path)\n", - "\n", - "with xr.open_dataset(path) as weights:\n", - " for k in (\"xarray_regrid_version\", \"created\", \"src_shape\", \"dst_shape\"):\n", - " print(f\" {k}: {weights.attrs[k]}\")\n", - "\n", "rgr2 = ConservativeRegridder.from_netcdf(path)\n", "same = np.array_equal(rgr.regrid(src_flat).values, rgr2.regrid(src_flat).values)\n", - "print(f\"\\nreload bit-identical: {same}\")" + "print(f\"saved {path.stat().st_size / 1024:.1f} KB; reload bit-identical: {same}\")" ] } ], diff --git a/pyproject.toml b/pyproject.toml index fe93d6a..0bcd27f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,16 @@ conservative-2d = [ # Needed by ConservativeRegridder.to_netcdf / from_netcdf (group support). "h5netcdf", ] +spherical = [ + # Builds on conservative-2d: adds great-circle geometry via spherely (S2) + # for the `manifold="s2"` mode of ConservativeRegridder. Install with either + # `pip install xarray-regrid[spherical]` (pulls the 2d stack automatically) + # or `pip install xarray-regrid[conservative-2d,spherical]` (explicit). + "xarray-regrid[conservative-2d]", + # Ships pre-built wheels on PyPI for macOS / Linux x86_64 / Windows + # (Python 3.10–3.14). + "spherely>=0.1.1", +] benchmarking = [ "matplotlib", "zarr", @@ -210,5 +220,5 @@ disallow_any_unimported = false warn_return_any = false [[tool.mypy.overrides]] -module = ["shapely", "shapely.*", "sparse"] +module = ["shapely", "shapely.*", "sparse", "spherely"] ignore_missing_imports = true diff --git a/src/xarray_regrid/methods/conservative_2d.py b/src/xarray_regrid/methods/conservative_2d.py index 9be0763..2e2032d 100644 --- a/src/xarray_regrid/methods/conservative_2d.py +++ b/src/xarray_regrid/methods/conservative_2d.py @@ -22,26 +22,38 @@ from collections.abc import Hashable from concurrent.futures import ThreadPoolExecutor from dataclasses import dataclass +from datetime import datetime, timezone from functools import cached_property +from importlib.metadata import PackageNotFoundError, version from pathlib import Path -from typing import Any, Literal, cast +from typing import Any, Literal, cast, get_args import numpy as np import xarray as xr from xarray_regrid import utils -from xarray_regrid.methods._conservative_2d_serialization import ( - _coo_components, - _coo_from_components, - _metadata_attrs, - _metadata_from_attrs, -) -from xarray_regrid.methods._conservative_2d_spec import RegridSpec -from xarray_regrid.methods.conservative import get_valid_threshold NetcdfEngine = Literal["netcdf4", "scipy", "h5netcdf"] | None +def _package_version() -> str: + try: + return version("xarray-regrid") + except PackageNotFoundError: + return "unknown" + + +# Bump on breaking change to the on-disk format in ConservativeRegridder.to_netcdf. +_SCHEMA_VERSION = 2 + +# Values allowed for the `manifold` kwarg on ConservativeRegridder and related +# helpers. `"s2"` requires the optional `spherely` dependency (great-circle +# polygon geometry on the sphere via Google's s2geometry). Keep the Literal +# alias and the runtime tuple in sync via ``get_args``. +Manifold = Literal["planar", "cea", "s2"] +_MANIFOLDS = get_args(Manifold) + + try: import shapely from shapely import affinity @@ -62,6 +74,34 @@ sparse = None _HAS_SPARSE = False +try: + import spherely + + _HAS_SPHERELY = True +except ImportError: # pragma: no cover + spherely = None + _HAS_SPHERELY = False + + +SPHERELY_IMPORT_ERROR = ( + "manifold='s2' requires the optional `spherely` package. " + "Install with `pip install spherely` or " + "`pip install xarray-regrid[conservative-2d]`." +) + + +def _check_spherely() -> None: + if not _HAS_SPHERELY: + raise ImportError(SPHERELY_IMPORT_ERROR) + + +def _check_manifold(manifold: str) -> None: + if manifold not in _MANIFOLDS: + msg = f"manifold must be one of {_MANIFOLDS!r}, got {manifold!r}" + raise ValueError(msg) + if manifold == "s2": + _check_spherely() + # We fill NaNs with 0 ourselves before matmul (see `_apply_core`), so sparse's # "NaN will not be propagated" warning is spurious. A module-level filter @@ -85,57 +125,29 @@ def _check_shapely() -> None: raise ImportError(SHAPELY_IMPORT_ERROR) -class _Direction: - """Lazy weights, apply matrix, and coverage mask for one regrid direction. - - Holds the raw ``(n_dst, n_src)`` area matrix in this direction's orientation - and derives, on first access: - - - ``weights``: row-normalized weight matrix - - ``apply_matrix``: pre-transposed and index-sorted weights, so - ``_apply_core``'s matmul is ``(..., n_src) @ (n_src, n_dst)`` with no - per-call sort - - ``coverage`` / ``coverage_all``: which output cells have any source overlap - - A regridder holds two of these (forward, backward); transposing the - regridder swaps them with no recomputation. - """ - - def __init__(self, areas: "sparse.COO | np.ndarray") -> None: - self.areas = areas - - @cached_property - def weights(self) -> "sparse.COO | np.ndarray": - return _row_normalize(self.areas) +class ConservativeRegridder: + """Reusable conservative regridder for grids that aren't 1D-separable. - @cached_property - def apply_matrix(self) -> "sparse.COO | np.ndarray": - return _transpose_weights(self.weights, sort=True) + Use this when your source or target isn't a pure rectilinear lat/lon + grid: curvilinear coordinates (2D ``lat[i, j]`` / ``lon[i, j]``), + unstructured meshes (via :meth:`from_polygons`), or arbitrary + polygon-to-polygon aggregation. For plain 1D-separable rectilinear + grids, the existing ``.conservative`` accessor is much faster. - @cached_property - def coverage(self) -> np.ndarray: - return _coverage_mask(self.areas) + Build once from source and target grids; apply to many compatible fields + via :meth:`regrid` (or by calling the regridder). The raw intersection + area matrix is stored internally; the forward and backward row-normalized + weight matrices are lazily cached on first use. - @cached_property - def coverage_all(self) -> bool: - return bool(self.coverage.all()) + Planar geometry only. Requires ``shapely >= 2.0``. + Example:: -class ConservativeRegridder: - """Reusable conservative regridder for grids that aren't 1D-separable: - curvilinear (2D ``lat``/``lon``), unstructured (via :meth:`from_polygons`), - or arbitrary polygon-to-polygon aggregation. For purely 1D-separable - rectilinear grids, the ``.regrid.conservative`` accessor is faster. - - Build once, apply to many fields via :meth:`regrid` (or by calling the - regridder); ``.T`` gives the backward regridder. Forward and backward - weight matrices are cached lazily. Requires ``shapely >= 2.0``. - - The unnormalized cell-intersection area matrix - ``A[i, j] = area(target_i ∩ source_j)`` is exposed as ``self.areas`` - (sparse ``(n_dst, n_src)`` if the ``sparse`` package is available, dense - otherwise) — useful for conservation diagnostics and per-cell coverage - analysis. + regridder = ConservativeRegridder( + src_ds, tgt_ds, x_coord="lon", y_coord="lat" + ) + out = regridder.regrid(da) # forward + back = regridder.T.regrid(out) # backward """ def __init__( @@ -144,13 +156,12 @@ def __init__( target: xr.Dataset, x_coord: str = "longitude", y_coord: str = "latitude", - spherical: bool = False, + manifold: Manifold = "planar", n_threads: int | None = None, ) -> None: _check_shapely() - source_grid, target_grid, src_x_sort_idx = _normalize_longitude_coords( - source, target, x_coord - ) + _check_manifold(manifold) + source_grid, target_grid = _normalize_longitude_coords(source, target, x_coord) src_dims = _spatial_dims(source_grid, x_coord, y_coord) dst_dims = _spatial_dims(target_grid, x_coord, y_coord) if not src_dims: @@ -160,12 +171,12 @@ def __init__( msg = f"target has no dims for coords {x_coord!r}, {y_coord!r}" raise ValueError(msg) src_grid = _grid_from_coords( - source_grid, x_coord, y_coord, src_dims, spherical=spherical + source_grid, x_coord, y_coord, src_dims, manifold=manifold ) dst_grid = _grid_from_coords( - target_grid, x_coord, y_coord, dst_dims, spherical=spherical + target_grid, x_coord, y_coord, dst_dims, manifold=manifold ) - self.spherical = spherical + self.manifold = manifold self.x_coord = x_coord self.y_coord = y_coord @@ -173,59 +184,37 @@ def __init__( self._dst_dims = dst_dims self._src_shape = tuple(int(source.sizes[d]) for d in src_dims) self._dst_shape = tuple(int(target.sizes[d]) for d in dst_dims) - areas = _build_intersection_areas(src_grid, dst_grid, n_threads=n_threads) - if src_x_sort_idx is not None: - # Source x was sorted by _normalize_longitude_coords so polygon - # construction stayed monotone. Relabel matrix columns so column - # order matches the user's original (unsorted) data layout. - x_dim_index = src_dims.index(source[x_coord].dims[0]) - areas = _remap_columns_for_axis_sort( - areas, src_x_sort_idx, self._src_shape, x_dim_index - ) - self.areas = areas + self._areas = _build_intersection_areas(src_grid, dst_grid, n_threads=n_threads) self._source_coords = source.coords.to_dataset() self._target_coords = target.coords.to_dataset() - @property - def spec(self) -> RegridSpec: - """Canonical metadata describing this regridder's layout.""" - return RegridSpec( - src_dims=self._src_dims, - dst_dims=self._dst_dims, - src_shape=self._src_shape, - dst_shape=self._dst_shape, - x_coord=self.x_coord, - y_coord=self.y_coord, - spherical=self.spherical, - ) - - @cached_property - def _forward(self) -> _Direction: - return _Direction(self.areas) - @cached_property - def _backward(self) -> _Direction: - return _Direction(_transpose_weights(self.areas)) - - @property def forward_weights(self) -> "sparse.COO | np.ndarray": """The row-normalized forward weight matrix (source → target).""" - return self._forward.weights + return _row_normalize(self._areas) - @property + @cached_property def backward_weights(self) -> "sparse.COO | np.ndarray": """The row-normalized backward weight matrix (target → source).""" - return self._backward.weights + return _row_normalize(_transpose_weights(self._areas)) - @property - def target_areas(self) -> np.ndarray: - """Area of each target cell overlapped by the source domain.""" - return _sum_matrix_axis_1d(self.areas, axis=1) + @cached_property + def _forward_apply(self) -> "sparse.COO | np.ndarray": + # Transposed + index-sorted once so the matmul in _apply_core is + # (..., n_src) @ (n_src, n_dst) with no per-call sort. + return _transpose_weights(self.forward_weights, sort=True) - @property - def source_coverage_areas(self) -> np.ndarray: - """Area of each source cell covered by target cells.""" - return _sum_matrix_axis_1d(self.areas, axis=0) + @cached_property + def _backward_apply(self) -> "sparse.COO | np.ndarray": + return _transpose_weights(self.backward_weights, sort=True) + + @cached_property + def _forward_coverage(self) -> np.ndarray: + return _coverage_mask(self._areas) + + @cached_property + def _backward_coverage(self) -> np.ndarray: + return _coverage_mask(_transpose_weights(self._areas)) def regrid( self, @@ -236,9 +225,15 @@ def regrid( """Regrid ``data`` forward (source → target).""" return _apply_stored_weights( data, - direction=self._forward, - spec=self.spec, + apply_weights=self._forward_apply, + coverage=self._forward_coverage, + src_dims=self._src_dims, + dst_dims=self._dst_dims, + src_shape=self._src_shape, + dst_shape=self._dst_shape, target_coords=self._target_coords, + x_coord=self.x_coord, + y_coord=self.y_coord, skipna=skipna, nan_threshold=nan_threshold, ) @@ -252,27 +247,32 @@ def __call__( return self.regrid(data, skipna=skipna, nan_threshold=nan_threshold) def transpose(self) -> "ConservativeRegridder": - """Return the backward regridder (target → source). The original's - forward Direction becomes the new's backward (and vice versa), so any - already-computed weight matrices are reused, not recomputed.""" - new = type(self)._from_state( - areas=_transpose_weights(self.areas), - source_coords=self._target_coords, - target_coords=self._source_coords, - spec=RegridSpec( - src_dims=self._dst_dims, - dst_dims=self._src_dims, - src_shape=self._dst_shape, - dst_shape=self._src_shape, - x_coord=self.x_coord, - y_coord=self.y_coord, - spherical=self.spherical, - ), - ) - if "_forward" in self.__dict__: - new.__dict__["_backward"] = self.__dict__["_forward"] - if "_backward" in self.__dict__: - new.__dict__["_forward"] = self.__dict__["_backward"] + """Return the backward regridder (target → source), sharing the + underlying area matrix and any already-computed cached weight + matrices (forward on the transposed regridder is backward on the + original, and vice versa).""" + new = object.__new__(ConservativeRegridder) + new.x_coord = self.x_coord + new.y_coord = self.y_coord + new.manifold = self.manifold + new._src_dims = self._dst_dims + new._dst_dims = self._src_dims + new._src_shape = self._dst_shape + new._dst_shape = self._src_shape + new._areas = _transpose_weights(self._areas) + new._source_coords = self._target_coords + new._target_coords = self._source_coords + swap = { + "forward_weights": "backward_weights", + "backward_weights": "forward_weights", + "_forward_apply": "_backward_apply", + "_backward_apply": "_forward_apply", + "_forward_coverage": "_backward_coverage", + "_backward_coverage": "_forward_coverage", + } + for src, dst in swap.items(): + if src in self.__dict__: + new.__dict__[dst] = self.__dict__[src] return new @property @@ -281,8 +281,8 @@ def T(self) -> "ConservativeRegridder": # noqa: N802 return self.transpose() def __repr__(self) -> str: - nnz = getattr(self.areas, "nnz", None) - shape = getattr(self.areas, "shape", (None, None)) + nnz = getattr(self._areas, "nnz", None) + shape = getattr(self._areas, "shape", (None, None)) nnz_str = f"nnz={nnz}" if nnz is not None else "dense" return ( f"ConservativeRegridder(src_dims={self._src_dims}, " @@ -291,10 +291,21 @@ def __repr__(self) -> str: def to_netcdf(self, path: str | Path, engine: NetcdfEngine = None) -> None: """Save the weight matrix and reproducibility metadata to a netCDF file. - Requires a group-aware engine (``netcdf4`` or ``h5netcdf``); ``engine`` - is forwarded to :func:`xarray.Dataset.to_netcdf`.""" + + File layout: + + - root dataset: the sparse area matrix stored as three 1D variables + ``_coo_row``, ``_coo_col``, ``_coo_data`` (``shape=(n_dst, n_src)`` + carried on root-dataset attributes), plus the regridder metadata as + root attributes. + - ``/source_coords`` group: coord-only Dataset capturing the source grid. + - ``/target_coords`` group: coord-only Dataset capturing the target grid. + + Groups require an engine that supports them (``netcdf4`` or + ``h5netcdf``); ``engine`` is forwarded to :func:`xarray.Dataset.to_netcdf`. + """ path = Path(path) - row, col, data, shape = _coo_components(self.areas) + row, col, data, shape = _coo_components(self._areas) ds_weights = xr.Dataset( { "_coo_row": (("nnz",), row), @@ -302,7 +313,7 @@ def to_netcdf(self, path: str | Path, engine: NetcdfEngine = None) -> None: "_coo_data": (("nnz",), data), }, attrs={ - **_metadata_attrs(self.spec, self._source_coords, self._target_coords), + **_metadata_attrs(self), "n_dst": int(shape[0]), "n_src": int(shape[1]), }, @@ -343,7 +354,7 @@ def from_netcdf( areas=_coo_from_components(row, col, data, (n_dst, n_src)), source_coords=source_coords, target_coords=target_coords, - spec=meta, + **meta, ) @classmethod @@ -353,20 +364,29 @@ def _from_state( areas: "sparse.COO | np.ndarray", source_coords: xr.Dataset, target_coords: xr.Dataset, - spec: RegridSpec, + src_dims: tuple[Hashable, ...], + dst_dims: tuple[Hashable, ...], + src_shape: tuple[int, ...], + dst_shape: tuple[int, ...], + x_coord: str, + y_coord: str, + manifold: str, ) -> "ConservativeRegridder": """Construct a regridder directly from its canonical state. Shared bypass of ``__init__`` used by :meth:`from_netcdf` and :meth:`from_polygons`; keeps the list of private attrs in one place.""" + # Defense-in-depth: protect against a corrupted netCDF file setting + # an unknown manifold that would crash later at regrid time. + _check_manifold(manifold) instance = object.__new__(cls) - instance.x_coord = spec.x_coord - instance.y_coord = spec.y_coord - instance.spherical = spec.spherical - instance._src_dims = spec.src_dims - instance._dst_dims = spec.dst_dims - instance._src_shape = spec.src_shape - instance._dst_shape = spec.dst_shape - instance.areas = areas + instance.x_coord = x_coord + instance.y_coord = y_coord + instance.manifold = cast("Manifold", manifold) + instance._src_dims = src_dims + instance._dst_dims = dst_dims + instance._src_shape = src_shape + instance._dst_shape = dst_shape + instance._areas = areas instance._source_coords = source_coords instance._target_coords = target_coords return instance @@ -383,32 +403,50 @@ def from_polygons( n_threads: int | None = None, predicate_filter: bool = True, ) -> "ConservativeRegridder": - """Build a regridder from explicit shapely polygon arrays — for - unstructured meshes (MPAS, ICON), arbitrary polygon targets (countries, - watersheds), or any non-rectilinear combination. + """Build a regridder from explicit shapely polygon arrays. + + Use this for unstructured meshes (MPAS, ICON, finite-element), arbitrary + polygon targets (countries, watersheds), or any combination the + structured-grid path cannot express. Args: - source_polygons, target_polygons: 1D arrays of shapely Polygons. - source_dim, target_dim: Dim names for source/target cells on the - input and output arrays. - target_coords: Optional Dataset of coord variables along - ``target_dim`` to reattach on the output (else: integer index). - periodic: Unwrap polygons that cross the antimeridian (treats x - as longitude on a 360-degree periodic axis). - n_threads: Thread count for parallel GEOS intersection. - predicate_filter: If True, filter STRtree candidates with GEOS - ``intersects``. Set False for tight-bbox grid cells to skip - the predicate (faster on that case, pathological otherwise). - - Geometry is planar in the polygons' own coordinate space. For lat/lon - cells, project into an equal-area CRS first or use the structured - path with ``spherical=True``. + source_polygons: 1D array of shapely Polygons for source cells. + target_polygons: 1D array of shapely Polygons for target cells. + source_dim: Name of the single dim carrying source cells on the + data passed to :meth:`regrid`. Default ``"cell"``. + target_dim: Name of the single dim carrying target cells on the + output. Default ``"cell"``. + target_coords: Optional xr.Dataset providing coordinate variables + along ``target_dim`` (and any auxiliary coords) to reattach on + the output. If None, the output is given a bare integer index. + periodic: Treat polygon x coordinates as longitudes on a 360-degree + periodic axis, so polygons that cross the antimeridian are + unwrapped before intersection. + n_threads: Thread count for GEOS intersection. + predicate_filter: If True (default), the STRtree candidate query + filters by GEOS ``intersects``. Safe for arbitrary polygons + including thin/diagonal shapes with loose bboxes. Set False + when your polygons have tight bboxes (low aspect ratio, + roughly axis-aligned) to skip the predicate and let the + ``area > 0`` filter drop false positives — usually faster + in that case, pathological otherwise. + + Returns: + A ``ConservativeRegridder`` that accepts data with ``source_dim`` + in place of the structured grid's spatial dims. + + Intersection geometry is planar in the input polygons' coordinate + space. If the polygons represent lat/lon cells, project them into an + equal-area CRS first (or use the structured path with ``manifold='cea'``). """ _check_shapely() src_polys = np.asarray(source_polygons) dst_polys = np.asarray(target_polygons) - if src_polys.ndim != 1 or dst_polys.ndim != 1: - msg = "source_polygons and target_polygons must be 1D arrays" + if src_polys.ndim != 1: + msg = "source_polygons must be a 1D array of shapely Polygons" + raise ValueError(msg) + if dst_polys.ndim != 1: + msg = "target_polygons must be a 1D array of shapely Polygons" raise ValueError(msg) if periodic: src_polys = _normalize_periodic_polygons(src_polys) @@ -442,102 +480,181 @@ def from_polygons( ), source_coords=xr.Dataset(coords={source_dim: np.arange(n_src)}), target_coords=tgt_ds, - spec=RegridSpec( - src_dims=(source_dim,), - dst_dims=(target_dim,), - src_shape=(n_src,), - dst_shape=(n_dst,), - x_coord="", - y_coord="", - spherical=False, - ), + src_dims=(source_dim,), + dst_dims=(target_dim,), + src_shape=(n_src,), + dst_shape=(n_dst,), + x_coord="", + y_coord="", + manifold="planar", ) def polygons_from_coords( x: np.ndarray, y: np.ndarray, - spherical: bool = False, + manifold: Literal["planar", "cea"] = "planar", periodic: bool = False, ) -> np.ndarray: - """Build a 1D row-major (y, x) array of shapely cell polygons from 1D or - 2D center coords. Convenience for mixing structured and unstructured paths - via :meth:`ConservativeRegridder.from_polygons`. ``spherical=True`` - projects 1D lat/lon (degrees) into Lambert cylindrical equal-area space; - ``periodic=True`` unwraps antimeridian-crossing cells.""" + """Build a 1D array of shapely cell polygons from 1D or 2D center coords. + + Convenience for mixing structured and unstructured regridding. E.g. build + target polygons for a regular lat/lon grid, then pass them together with + an unstructured source mesh to :meth:`ConservativeRegridder.from_polygons`. + + Args: + x: 1D or 2D array of cell-center x coordinates. + y: 1D or 2D array of cell-center y coordinates. + manifold: ``"planar"`` (default) keeps raw coords; ``"cea"`` projects + 1D lat/lon (degrees) to Lambert cylindrical equal-area space for + correct spherical area weights with the planar fast path. The s2 + manifold is not available here (spherely Geographies aren't + shapely polygons); use ``ConservativeRegridder(..., manifold="s2")``. + periodic: Treat x coordinates as longitudes on a 360-degree periodic + axis, so cells that cross the antimeridian are unwrapped before + polygon construction. + + Returns: + A 1D numpy array of shapely Polygons in row-major (y, x) order. + """ _check_shapely() + if manifold not in ("planar", "cea"): + msg = ( + f"polygons_from_coords supports manifold 'planar' or 'cea'; " + f"got {manifold!r}. For s2, use ConservativeRegridder directly." + ) + raise ValueError(msg) x = np.asarray(x) y = np.asarray(y) if periodic: x = _unwrap_longitude(x) - if spherical: + if manifold == "cea": if x.ndim != 1 or y.ndim != 1: - msg = "spherical=True requires 1D lat/lon arrays" + msg = "manifold='cea' requires 1D lat/lon arrays" raise ValueError(msg) return _build_cea_grid(x, y).polys return _build_grid(x, y).polys +def conservative_2d_regrid( + data: xr.DataArray | xr.Dataset, + target_ds: xr.Dataset, + x_coord: str = "longitude", + y_coord: str = "latitude", + manifold: Manifold = "planar", + skipna: bool = True, + nan_threshold: float = 1.0, + n_threads: int | None = None, +) -> xr.DataArray | xr.Dataset: + """Conservative regridding via explicit 2D polygon intersection. + + One-shot convenience wrapper: constructs a :class:`ConservativeRegridder` + and applies it once. For repeated regridding to the same target, construct + a ``ConservativeRegridder`` directly and reuse it. + + Args: + data: Input data on the source grid. + target_ds: Dataset defining the target grid; must expose ``x_coord`` and + ``y_coord`` as (1D or 2D) coordinate variables. + x_coord: Name of the x (longitude-like) coordinate variable. + y_coord: Name of the y (latitude-like) coordinate variable. + manifold: Geometry used for intersection. ``"planar"`` (default) uses + raw planar shapely math in the provided coordinate space; + ``"cea"`` projects 1D lat/lon (degrees) into Lambert cylindrical + equal-area space — correct spherical areas at the planar fast + path's cost; ``"s2"`` uses s2geometry via the optional ``spherely`` + dependency for true great-circle edges. + skipna: If True, propagate NaNs into the weighted mean via a two-pass + sum. + nan_threshold: Keep output cells whose valid source fraction is at + least ``nan_threshold``. + n_threads: Thread count for parallel GEOS intersection (planar path). + + Returns: + Regridded data on the target grid, preserving non-spatial dims. + """ + regridder = ConservativeRegridder( + data, + target_ds, + x_coord=x_coord, + y_coord=y_coord, + manifold=manifold, + n_threads=n_threads, + ) + return regridder.regrid(data, skipna=skipna, nan_threshold=nan_threshold) + + def _apply_stored_weights( data: xr.DataArray | xr.Dataset, - direction: _Direction, - spec: RegridSpec, + apply_weights: "sparse.COO | np.ndarray", + coverage: np.ndarray, + src_dims: tuple[Hashable, ...], + dst_dims: tuple[Hashable, ...], + src_shape: tuple[int, ...], + dst_shape: tuple[int, ...], target_coords: xr.Dataset, + x_coord: str, + y_coord: str, skipna: bool, nan_threshold: float, ) -> xr.DataArray | xr.Dataset: - """Apply ``direction``'s cached, pre-transposed weight matrix to ``data`` - via ``xr.apply_ufunc``. + """Apply a cached, pre-transposed weight matrix to ``data`` via + ``xr.apply_ufunc``. - The apply matrix has shape ``(n_src, n_dst)`` so the matmul is + ``apply_weights`` has shape ``(n_src, n_dst)`` so the matmul is ``(..., n_src) @ (n_src, n_dst) → (..., n_dst)`` with no per-call transpose. """ - actual_src_shape = tuple( - int(data.sizes[d]) for d in spec.src_dims if d in data.sizes - ) - if actual_src_shape != spec.src_shape: + # apply_ufunc(dask="parallelized") needs each core dim to be a single + # chunk. Only rechunk if data is already dask-backed — don't inadvertently + # dask-ify a numpy-backed input. + if getattr(data, "chunks", None) is not None: + split = { + d: -1 + for d in src_dims + if d in data.dims and len(data.chunksizes.get(d, ())) > 1 + } + if split: + data = data.chunk(split) + + actual_src_shape = tuple(int(data.sizes[d]) for d in src_dims if d in data.sizes) + if actual_src_shape != src_shape: msg = ( - f"source spatial shape {actual_src_shape} on dims {spec.src_dims} does " - f"not match the regridder's expected shape {spec.src_shape}" + f"source spatial shape {actual_src_shape} on dims {src_dims} does " + f"not match the regridder's expected shape {src_shape}" ) raise ValueError(msg) - src_tokens = tuple(f"__src_{d}" for d in spec.src_dims) - data_renamed = data.rename(dict(zip(spec.src_dims, src_tokens, strict=True))) + src_tokens = tuple(f"__src_{d}" for d in src_dims) + data_renamed = data.rename(dict(zip(src_dims, src_tokens, strict=True))) output_dtype = _result_dtype(data) result = xr.apply_ufunc( _apply_core, data_renamed, kwargs={ - "apply_weights": direction.apply_matrix, - "coverage": direction.coverage, - "coverage_all": direction.coverage_all, - "src_shape": spec.src_shape, - "dst_shape": spec.dst_shape, + "apply_weights": apply_weights, + "coverage": coverage, + "coverage_all": bool(coverage.all()), + "src_shape": src_shape, + "dst_shape": dst_shape, "skipna": skipna, "nan_threshold": nan_threshold, "output_dtype": output_dtype, }, input_core_dims=[list(src_tokens)], - output_core_dims=[list(spec.dst_dims)], + output_core_dims=[list(dst_dims)], exclude_dims=set(src_tokens), dask="parallelized", output_dtypes=[output_dtype], dask_gufunc_kwargs={ - "output_sizes": {d: int(target_coords.sizes[d]) for d in spec.dst_dims}, - "allow_rechunk": True, + "output_sizes": {d: int(target_coords.sizes[d]) for d in dst_dims}, + "allow_rechunk": False, }, keep_attrs=True, ) - return _assign_target_coords( - result, - target_coords, - spec.dst_dims, - spec.x_coord, - spec.y_coord, - ) + result = _assign_target_coords(result, target_coords, dst_dims, x_coord, y_coord) + return result def _coverage_mask(areas: "sparse.COO | np.ndarray") -> np.ndarray: @@ -553,129 +670,160 @@ def _coverage_mask(areas: "sparse.COO | np.ndarray") -> np.ndarray: return np.asarray((arr > 0).any(axis=1)) -def _sum_matrix_axis_1d(areas: "sparse.COO | np.ndarray", axis: int) -> np.ndarray: - summed = areas.sum(axis=axis) - if hasattr(summed, "todense"): - summed = summed.todense() - return np.asarray(summed, dtype=np.float64).reshape(-1) +def _coo_components( + w: "sparse.COO | np.ndarray", +) -> tuple[np.ndarray, np.ndarray, np.ndarray, tuple[int, int]]: + if _HAS_SPARSE and isinstance(w, sparse.COO): + coords = np.asarray(w.coords) + return ( + coords[0].astype(np.int64, copy=False), + coords[1].astype(np.int64, copy=False), + np.asarray(w.data), + w.shape, + ) + arr = np.asarray(w) + rows, cols = np.nonzero(arr) + return ( + rows.astype(np.int64, copy=False), + cols.astype(np.int64, copy=False), + arr[rows, cols], + arr.shape, + ) + + +def _coo_from_components( + row: np.ndarray, + col: np.ndarray, + data: np.ndarray, + shape: tuple[int, int], +) -> "sparse.COO | np.ndarray": + if _HAS_SPARSE: + return sparse.COO( + coords=np.stack([row, col]), + data=data, + shape=shape, + has_duplicates=False, + sorted=False, + ) + dense = np.zeros(shape, dtype=data.dtype if data.size else np.float64) + dense[row, col] = data + return dense + + +def _metadata_attrs(regridder: ConservativeRegridder) -> dict[str, Any]: + attrs: dict[str, Any] = { + "x_coord": regridder.x_coord, + "y_coord": regridder.y_coord, + "manifold": str(regridder.manifold), + "src_dims": [str(d) for d in regridder._src_dims], + "dst_dims": [str(d) for d in regridder._dst_dims], + "src_shape": list(regridder._src_shape), + "dst_shape": list(regridder._dst_shape), + "xarray_regrid_version": _package_version(), + "created": datetime.now(tz=timezone.utc).isoformat(), + "schema_version": _SCHEMA_VERSION, + } + for name, value in { + "source_x_range": _coord_range(regridder._source_coords, regridder.x_coord), + "source_y_range": _coord_range(regridder._source_coords, regridder.y_coord), + "target_x_range": _coord_range(regridder._target_coords, regridder.x_coord), + "target_y_range": _coord_range(regridder._target_coords, regridder.y_coord), + }.items(): + if value is not None: + attrs[name] = list(value) + return attrs + + +def _metadata_from_attrs(attrs: dict[str, Any], path: Path) -> dict[str, Any]: + """Parse the kwargs needed by :meth:`ConservativeRegridder._from_state` out + of netCDF root attributes, validating ``schema_version``.""" + schema_version = int(attrs.get("schema_version", 0)) + if schema_version != _SCHEMA_VERSION: + msg = ( + f"regridder file at {path} uses schema version {schema_version}; " + f"this xarray-regrid understands {_SCHEMA_VERSION}. " + "Upgrade xarray-regrid or re-save." + ) + raise ValueError(msg) + + return { + "x_coord": str(attrs["x_coord"]), + "y_coord": str(attrs["y_coord"]), + "manifold": str(attrs["manifold"]), + "src_dims": tuple(str(d) for d in np.atleast_1d(attrs["src_dims"])), + "dst_dims": tuple(str(d) for d in np.atleast_1d(attrs["dst_dims"])), + "src_shape": tuple(int(s) for s in np.atleast_1d(attrs["src_shape"])), + "dst_shape": tuple(int(s) for s in np.atleast_1d(attrs["dst_shape"])), + } def _normalize_longitude_coords( source: xr.DataArray | xr.Dataset, target: xr.Dataset, x_coord: str, -) -> tuple[xr.DataArray | xr.Dataset, xr.Dataset, np.ndarray | None]: +) -> tuple[xr.DataArray | xr.Dataset, xr.Dataset]: """Unwrap x coordinates across the antimeridian so source and target share a contiguous longitude frame. No-op when the coord isn't present on both - objects or doesn't look like a longitude. - - For 1D rectilinear longitudes on both sides this mirrors the per-value - wrap done by :func:`xarray_regrid.utils.format_lon` for the axis-factored - path, which is what makes a source on ``[0, 360]`` align with a target on - ``[-180, 180]`` (and vice versa). If wrapping breaks source monotonicity - the source coord is sorted in place; the caller is expected to remap the - area-matrix columns by the returned ``src_x_sort_idx`` so the final matrix - columns line up with the user's original data layout. For 2D / curvilinear - coords the existing uniform-shift fallback is kept. - """ + objects or doesn't look like a longitude.""" if x_coord not in source.coords or x_coord not in target.coords: - return source, target, None + return source, target source_x = np.asarray(source[x_coord].values) target_x = np.asarray(target[x_coord].values) if not _looks_like_longitude(source_x) and not _looks_like_longitude(target_x): - return source, target, None + return source, target source_x = _unwrap_longitude(source_x) - target_x = _unwrap_longitude(target_x) - src_finite = source_x[np.isfinite(source_x)] - tgt_finite = target_x[np.isfinite(target_x)] - - src_x_sort_idx: np.ndarray | None = None - if ( - source_x.ndim == 1 - and target_x.ndim == 1 - and src_finite.size - and tgt_finite.size - ): - # Per-value wrap source into target's 360° window — mirrors format_lon - # so source [0, 360] vs target [-180, 180] (and either reversed) - # aligns. A uniform offset can't reconcile cross-convention grids: - # mean diff is exactly 180° and round() is banker's-rounded to 0. - wrap_point = float((tgt_finite[0] + tgt_finite[-1] + 360.0) / 2.0) - source_x = np.where(source_x < wrap_point - 360.0, source_x + 360.0, source_x) - source_x = np.where(source_x > wrap_point, source_x - 360.0, source_x) - diffs = np.diff(source_x) - if not (np.all(diffs > 0) or np.all(diffs < 0)): - src_x_sort_idx = np.argsort(source_x, kind="stable") - source_x = source_x[src_x_sort_idx] - elif src_finite.size and tgt_finite.size: - # 2D / curvilinear: fall back to uniform shift of target into source's - # window. Doesn't handle cross-convention but preserves the existing - # antimeridian-crossing behavior for 2D coords. - target_x = target_x + _periodic_offset( - float(src_finite.mean()), float(tgt_finite.mean()) - ) + target_x = _align_longitude(_unwrap_longitude(target_x), source_x) return ( utils.update_coord(source, x_coord, source_x), cast(xr.Dataset, utils.update_coord(target, x_coord, target_x)), - src_x_sort_idx, ) def _looks_like_longitude(values: np.ndarray) -> bool: - finite = values[np.isfinite(values)] - return bool(finite.size and finite.min() >= -360.0 and finite.max() <= 360.0) + finite = np.asarray(values, dtype=float) + finite = finite[np.isfinite(finite)] + if finite.size == 0: + return False + return bool(finite.min() >= -360.0 and finite.max() <= 360.0) def _unwrap_longitude(values: np.ndarray) -> np.ndarray: - """Unwrap longitudes along the trailing axis (CF convention). For 2D - coords, latitude doesn't wrap mod 360°, so unwrapping it is incorrect.""" radians = np.deg2rad(np.asarray(values, dtype=float)) - return np.rad2deg(np.unwrap(radians, axis=-1)) + for axis in range(radians.ndim): + radians = np.unwrap(radians, axis=axis) + return np.rad2deg(radians) + + +def _align_longitude(values: np.ndarray, reference: np.ndarray) -> np.ndarray: + if values.size == 0 or reference.size == 0: + return values + offset = 360.0 * round((np.nanmean(reference) - np.nanmean(values)) / 360.0) + return values + offset def _normalize_periodic_polygons( polygons: np.ndarray, reference: float | None = None ) -> np.ndarray: - """Unwrap each polygon across the antimeridian, then shift each into the - same 360-degree window as ``reference`` (or as the first finite center if - ``reference`` is None).""" - unwrapped = [_unwrap_polygon(p) for p in polygons] - if reference is None: - for poly in unwrapped: - center = _polygon_center_x(poly) - if np.isfinite(center): - reference = center - break - if reference is None: - return np.array(unwrapped, dtype=object) - - out = [] - for poly in unwrapped: - offset = _periodic_offset(reference, _polygon_center_x(poly)) - out.append(affinity.translate(poly, xoff=offset) if offset != 0.0 else poly) - return np.array(out, dtype=object) - - -def _polygon_reference_x(polygons: np.ndarray) -> float | None: - """Mean polygon-center x across an array of polygons, or None if all - polygons have non-finite centers.""" + normalized = [] + current_reference = reference + for polygon in polygons: + new_polygon = _unwrap_polygon(polygon) + center = _polygon_reference_x(np.array([new_polygon], dtype=object)) + if current_reference is None: + current_reference = center + offset = 360.0 * round((current_reference - center) / 360.0) + if offset: + new_polygon = affinity.translate(new_polygon, xoff=offset) + normalized.append(new_polygon) + return np.array(normalized, dtype=object) + + +def _polygon_reference_x(polygons: np.ndarray) -> float: bounds = shapely.bounds(polygons) centers = 0.5 * (bounds[:, 0] + bounds[:, 2]) - finite = centers[np.isfinite(centers)] - return float(finite.mean()) if finite.size else None - - -def _polygon_center_x(polygon: Any) -> float: - minx, _, maxx, _ = polygon.bounds - return 0.5 * (float(minx) + float(maxx)) - - -def _periodic_offset(reference: float, value: float) -> float: - if not np.isfinite(reference) or not np.isfinite(value): - return 0.0 - return 360.0 * round((reference - value) / 360.0) + return float(np.nanmean(centers)) def _unwrap_polygon(polygon: Any) -> Any: @@ -704,57 +852,15 @@ def _unwrap_ring(ring: np.ndarray) -> np.ndarray: return new_ring -def _remap_columns_for_axis_sort( - areas: "sparse.COO | np.ndarray", - sort_idx: np.ndarray, - src_shape: tuple[int, ...], - axis_index: int, -) -> "sparse.COO | np.ndarray": - """Relabel the column indices of an ``(n_dst, prod(src_shape))`` area - matrix so that columns appear in the user's original source-data order - after ``sort_idx`` was applied along ``axis_index`` of ``src_shape``. - - A row-major source flat index ``c = unravel(c, src_shape)`` has its - ``axis_index`` component ``i_sorted`` permuted via - ``i_orig = sort_idx[i_sorted]``; all other components are untouched. So - the new flat index is - ``c_new = c // stride * stride + (i_orig - i_sorted) * inner + ...``. - For separable 1D-rect grids (the only case that triggers this today) the - sorted axis sits between an outer block of size ``outer`` and an inner - block of size ``inner`` with ``stride = nx * inner``. - """ - nx = int(src_shape[axis_index]) - inner = int(np.prod(src_shape[axis_index + 1 :])) - stride = nx * inner - sort_idx = np.asarray(sort_idx, dtype=np.int64) - - if _HAS_SPARSE and isinstance(areas, sparse.COO): - old_col = np.asarray(areas.coords[1], dtype=np.int64) - outer_block = (old_col // stride) * stride - within = old_col % stride - i_sorted = within // inner - rest = within % inner - new_col = outer_block + sort_idx[i_sorted] * inner + rest - coords = np.stack([np.asarray(areas.coords[0], dtype=np.int64), new_col]) - return sparse.COO( - coords=coords, - data=np.asarray(areas.data), - shape=areas.shape, - has_duplicates=False, - sorted=False, - ) - - arr = np.asarray(areas) - n_cells = arr.shape[1] - inv_sort_idx = np.empty_like(sort_idx) - inv_sort_idx[sort_idx] = np.arange(sort_idx.size, dtype=sort_idx.dtype) - cells = np.arange(n_cells, dtype=np.int64) - outer_block = (cells // stride) * stride - within = cells % stride - i_orig = within // inner - rest = within % inner - inv_perm = outer_block + inv_sort_idx[i_orig] * inner + rest - return arr[:, inv_perm] +def _coord_range(ds: xr.Dataset, coord_name: str) -> tuple[float, float] | None: + """Return ``(min, max)`` of a coord, or ``None`` when it isn't on the + Dataset (e.g., the integer-index stub emitted by ``from_polygons``).""" + if not coord_name or coord_name not in ds.coords: + return None + arr = np.asarray(ds[coord_name].values) + if arr.size == 0: + return None + return float(arr.min()), float(arr.max()) def _transpose_weights( @@ -807,94 +913,173 @@ def _grid_from_coords( x_coord: str, y_coord: str, dims: tuple[Hashable, ...], - spherical: bool = False, + manifold: Manifold = "planar", ) -> "_Grid": - """Build a :class:`_Grid` from the object's x/y coordinates. - - Rectilinear (both coords 1D on separate dims) takes the fast path. - Curvilinear coords are broadcast to a common N-D array in ``dims`` order. + """Build a :class:`_Grid` from the object's x/y coordinates under a given + manifold (``"planar"``, ``"cea"``, ``"s2"``). - If ``spherical`` is True, coordinates are assumed to be longitude (x) and - latitude (y) in degrees, and cells are projected into a Lambert cylindrical - equal-area space (x' = lon_rad, y' = sin(lat_rad)) before constructing the - cell polygons. This gives mass-conservative weights on the sphere at the - same cost as the planar fast path. Rectilinear-only. + For 1D coords on separate dims we take the rectilinear fast path; otherwise + we broadcast to a common N-D center array and build curvilinear polygons. """ xd = obj[x_coord] yd = obj[y_coord] is_rectilinear = xd.ndim == 1 and yd.ndim == 1 and xd.dims[0] != yd.dims[0] - - if spherical and not is_rectilinear: - msg = "spherical=True is only supported for rectilinear (1D lat/lon) coords" - raise NotImplementedError(msg) - + # `cea` and `s2` derive cell edges from 1D lat/lon; both require rectilinear. + if manifold in ("cea", "s2"): + if not is_rectilinear: + msg = ( + f"manifold={manifold!r} is only supported for rectilinear " + "(1D lat/lon) coordinate arrays." + ) + raise NotImplementedError(msg) + builder = _build_cea_grid if manifold == "cea" else _build_s2_grid + return builder(np.asarray(xd.values), np.asarray(yd.values)) if is_rectilinear: - x = np.asarray(xd.values) - y = np.asarray(yd.values) - return _build_cea_grid(x, y) if spherical else _build_grid(x, y) - - xc, yc = xr.broadcast(xd, yd) - return _build_grid( - np.asarray(xc.transpose(*dims).values), - np.asarray(yc.transpose(*dims).values), + return _build_grid(np.asarray(xd.values), np.asarray(yd.values)) + xc, yc = xr.broadcast(obj[x_coord], obj[y_coord]) + xc = xc.transpose(*dims) + yc = yc.transpose(*dims) + return _build_grid(np.asarray(xc.values), np.asarray(yc.values)) + + +def _latlon_edges_deg( + lon_centers: np.ndarray, lat_centers: np.ndarray +) -> tuple[np.ndarray, np.ndarray]: + """Derive cell-edge arrays from 1D lat/lon center arrays (degrees). + + Shared by the CEA and s2 builders; clips latitudes at the poles. Projecting + *edges* analytically — rather than projecting centers and then + re-midpointing — is required because the projections (``sin`` for CEA, the + great-circle geometry for s2) are nonlinear: the projected midpoint of two + lat centers is not the same as the midpoint of two projected lat edges. + """ + if lon_centers.size < 2 or lat_centers.size < 2: + msg = "rectilinear lat/lon grids need at least two cells per dimension" + raise ValueError(msg) + return ( + utils.infer_1d_edges(lon_centers), + np.clip(utils.infer_1d_edges(lat_centers), -90.0, 90.0), ) def _build_cea_grid(lon_centers: np.ndarray, lat_centers: np.ndarray) -> "_Grid": """Build a rectilinear :class:`_Grid` whose cell polygons are in Lambert - cylindrical equal-area coordinates (x' = lon_rad, y' = sin(lat_rad)). - - Projecting *edges* analytically — rather than projecting centers and then - re-midpointing — is required because ``sin()`` is nonlinear: the projected - midpoint of two lat centers is not the same as the midpoint of two - projected lat edges. - """ + cylindrical equal-area coordinates (x' = lon_rad, y' = sin(lat_rad)).""" _check_shapely() - if lon_centers.size < 2 or lat_centers.size < 2: - msg = "spherical mode requires at least two cells per dimension" - raise ValueError(msg) - lat_edges_deg = np.clip(utils.infer_1d_edges(lat_centers), -90.0, 90.0) - lon_edges_deg = utils.infer_1d_edges(lon_centers) + lon_edges_deg, lat_edges_deg = _latlon_edges_deg(lon_centers, lat_centers) return _rect_grid_from_edges( np.deg2rad(lon_edges_deg), np.sin(np.deg2rad(lat_edges_deg)), ) -def _infer_2d_corners(a: np.ndarray) -> np.ndarray: - """Infer (ny+1, nx+1) cell corners from a 2D cell-center array. Interior - corners are the mean of the 4 surrounding centers; boundary corners are - reflected from the adjacent interior row/column.""" - a = np.asarray(a, dtype=float) - ny, nx = a.shape - pad = np.empty((ny + 2, nx + 2), dtype=a.dtype) - pad[1:-1, 1:-1] = a - pad[0, 1:-1] = 2 * a[0, :] - a[1, :] - pad[-1, 1:-1] = 2 * a[-1, :] - a[-2, :] - pad[1:-1, 0] = 2 * a[:, 0] - a[:, 1] - pad[1:-1, -1] = 2 * a[:, -1] - a[:, -2] - pad[0, 0] = 2 * pad[0, 1] - pad[0, 2] - pad[0, -1] = 2 * pad[0, -2] - pad[0, -3] - pad[-1, 0] = 2 * pad[-1, 1] - pad[-1, 2] - pad[-1, -1] = 2 * pad[-1, -2] - pad[-1, -3] - return 0.25 * (pad[:-1, :-1] + pad[1:, :-1] + pad[:-1, 1:] + pad[1:, 1:]) +def _build_s2_grid(lon_centers: np.ndarray, lat_centers: np.ndarray) -> "_Grid": + """Build a rectilinear :class:`_Grid` with s2geometry great-circle cell + polygons attached (``_Grid.s2_polys``). + + The planar shapely polygons + bounds stay in place so the STRtree + candidate-pair filter works — spherely does not yet expose a spatial + index (tracked at https://github.com/benbovy/spherely/issues/72). The + planar bbox is a conservative over-estimate of overlap, so the filter + is lossless for the s2 intersection downstream. + """ + _check_shapely() + _check_spherely() + lon_edges_deg, lat_edges_deg = _latlon_edges_deg(lon_centers, lat_centers) + planar = _rect_grid_from_edges(lon_edges_deg, lat_edges_deg) + return _Grid( + polys=planar.polys, + bounds=planar.bounds, + rectilinear=True, + s2_polys=_s2_cell_polys(lon_edges_deg, lat_edges_deg), + ) + + +def _s2_cell_polys(lon_edges_deg: np.ndarray, lat_edges_deg: np.ndarray) -> np.ndarray: + """Build an (n_cells,) object array of spherely.Geography polygons. + + Each polygon uses explicit CCW vertex order in (lon, lat) degrees and + ``oriented=True`` so orientation is unambiguous for cells that touch the + poles or span wide longitude ranges. + """ + if hasattr(spherely, "polygons"): + # Vectorized constructor (benbovy/spherely#52, not yet released). When + # it lands we build shells as (n, 4, 2) and skip the Python loop. + x0, y0 = np.meshgrid(lon_edges_deg[:-1], lat_edges_deg[:-1], indexing="xy") + x1, y1 = np.meshgrid(lon_edges_deg[1:], lat_edges_deg[1:], indexing="xy") + shells = np.stack( + [ + np.stack([x0, y0], axis=-1), + np.stack([x1, y0], axis=-1), + np.stack([x1, y1], axis=-1), + np.stack([x0, y1], axis=-1), + ], + axis=-2, + ).reshape(-1, 4, 2) + return np.asarray(spherely.polygons(shells, oriented=True)) + # Per-cell fallback: `spherely.create_polygon` wants an iterable of tuples, + # and a Python loop over pre-slicing an ndarray is slower than building + # the tuple list inline. + nx = lon_edges_deg.size - 1 + ny = lat_edges_deg.size - 1 + polys = np.empty(ny * nx, dtype=object) + for j in range(ny): + y0f, y1f = float(lat_edges_deg[j]), float(lat_edges_deg[j + 1]) + for i in range(nx): + x0f, x1f = float(lon_edges_deg[i]), float(lon_edges_deg[i + 1]) + shell = [(x0f, y0f), (x1f, y0f), (x1f, y1f), (x0f, y1f)] + polys[j * nx + i] = spherely.create_polygon(shell, oriented=True) + return polys + + +def _infer_2d_corners(xc: np.ndarray, yc: np.ndarray) -> tuple[np.ndarray, np.ndarray]: + """Return (ny+1, nx+1) cell-corner arrays from 2D cell-center arrays. + + Interior corners are the mean of the four surrounding centers; boundary + corners are reflected from the adjacent interior row/column. + """ + if xc.shape != yc.shape or xc.ndim != 2: + msg = "xc and yc must be 2D arrays of the same shape" + raise ValueError(msg) + + def corners(a: np.ndarray) -> np.ndarray: + ny, nx = a.shape + pad = np.empty((ny + 2, nx + 2), dtype=a.dtype) + pad[1:-1, 1:-1] = a + pad[0, 1:-1] = 2 * a[0, :] - a[1, :] + pad[-1, 1:-1] = 2 * a[-1, :] - a[-2, :] + pad[1:-1, 0] = 2 * a[:, 0] - a[:, 1] + pad[1:-1, -1] = 2 * a[:, -1] - a[:, -2] + pad[0, 0] = 2 * pad[0, 1] - pad[0, 2] + pad[0, -1] = 2 * pad[0, -2] - pad[0, -3] + pad[-1, 0] = 2 * pad[-1, 1] - pad[-1, 2] + pad[-1, -1] = 2 * pad[-1, -2] - pad[-1, -3] + return 0.25 * (pad[:-1, :-1] + pad[1:, :-1] + pad[:-1, 1:] + pad[1:, 1:]) + + return corners(xc.astype(float)), corners(yc.astype(float)) @dataclass class _Grid: """Cached cell geometry for a structured grid. - ``polys`` is a flat (n_cells,) object array of shapely Polygons. - ``bounds`` is a (n_cells, 4) ``(minx, miny, maxx, maxy)`` array cached for - the STRtree / candidate-search path. ``rectilinear`` is True when both the - source x and y were 1D coordinate arrays (axis-aligned rectangles) — the - weight builder uses this to skip GEOS polygon clipping and compute - intersection areas analytically from the bounds. + ``polys`` is a flat (n_cells,) object array of shapely Polygons used for + the STRtree candidate-pair filter. ``bounds`` is a (n_cells, 4) axis- + aligned bbox array cached off the polys for the analytic fast path and + any coarse bbox queries. ``rectilinear`` enables the analytic box-overlap + path in :func:`_build_intersection_areas`. + + ``s2_polys`` is an optional (n_cells,) object array of spherely + ``Geography`` polygons in the s2geometry manifold. When both the source + and target grids carry ``s2_polys``, the weight builder uses + ``spherely.intersection`` + ``spherely.area`` instead of planar shapely — + great-circle edges and true spherical areas. """ polys: np.ndarray bounds: np.ndarray rectilinear: bool + s2_polys: np.ndarray | None = None def _rect_grid_from_edges(xe: np.ndarray, ye: np.ndarray) -> _Grid: @@ -924,9 +1109,8 @@ def _build_grid(xc: np.ndarray, yc: np.ndarray) -> _Grid: xe = utils.infer_1d_edges(xc.astype(float)) ye = utils.infer_1d_edges(yc.astype(float)) return _rect_grid_from_edges(xe, ye) - if xc.ndim == 2 and yc.ndim == 2 and xc.shape == yc.shape: - xcorn = _infer_2d_corners(xc) - ycorn = _infer_2d_corners(yc) + if xc.ndim == 2 and yc.ndim == 2: + xcorn, ycorn = _infer_2d_corners(xc, yc) ny, nx = xc.shape c00 = np.stack([xcorn[:-1, :-1], ycorn[:-1, :-1]], axis=-1) c10 = np.stack([xcorn[:-1, 1:], ycorn[:-1, 1:]], axis=-1) @@ -952,8 +1136,12 @@ def _build_intersection_areas( This is the unnormalized matrix. Row-normalize via :func:`_row_normalize` to get forward weights; transpose first for backward (target → source). - When both grids are rectilinear (axis-aligned rectangles) intersection - areas are computed analytically from the bounds, skipping GEOS clipping. + Dispatches to three paths: + 1. both grids carry ``s2_polys`` → spherely great-circle intersection + (planar bboxes are used only as a lossless candidate-pair filter + because spherely has no spatial index yet). + 2. both grids are rectilinear → analytic box-overlap (numpy only). + 3. otherwise → threaded shapely polygon clipping. ``predicate_filter=False`` (default) uses a bbox-only STRtree query and relies on the ``area > 0`` filter below to drop bbox-false-positives. @@ -978,7 +1166,9 @@ def _build_intersection_areas( if dst_idx.size == 0: return _empty_weights(n_dst, n_src) - if src.rectilinear and dst.rectilinear: + if src.s2_polys is not None and dst.s2_polys is not None: + areas = _s2_intersection_areas(dst.s2_polys[dst_idx], src.s2_polys[src_idx]) + elif src.rectilinear and dst.rectilinear: sb = src.bounds[src_idx] db = dst.bounds[dst_idx] dx = np.minimum(sb[:, 2], db[:, 2]) - np.maximum(sb[:, 0], db[:, 0]) @@ -1036,6 +1226,19 @@ def _row_normalize( return areas / row_sum +def _s2_intersection_areas(dst_geog: np.ndarray, src_geog: np.ndarray) -> np.ndarray: + """Per-pair great-circle intersection areas in steradians. + + ``spherely.intersection`` and ``spherely.area`` are both vectorized ufuncs + so this is a single pass through each. ``radius=1.0`` gives the result on + the unit sphere — fine for row-normalized weights because the Earth + radius would cancel through the normalization anyway. + """ + _check_spherely() + inter = spherely.intersection(dst_geog, src_geog) + return np.asarray(spherely.area(inter, radius=1.0)) + + def _intersection_areas_threaded( a: np.ndarray, b: np.ndarray, n_threads: int | None ) -> np.ndarray: @@ -1048,11 +1251,10 @@ def _intersection_areas_threaded( _check_shapely() n = len(a) if n_threads is None: - # Below ~1k pairs the pool spin-up (~0.3 ms) dominates sub-ms work. - # Above that, scaling is near-linear with logical cores — shapely - # releases the GIL inside its GEOS ufuncs. Cap at 16 to avoid - # oversubscription on unusually wide machines. - n_threads = 1 if n < 1_000 else min(os.cpu_count() or 1, 16) + n_threads = min(os.cpu_count() or 1, 8) + # Amortize thread-pool overhead only when there's meaningful work. + if n < 50_000: + n_threads = 1 if n_threads <= 1 or n == 0: return shapely.area(shapely.intersection(a, b)) @@ -1112,14 +1314,14 @@ def _apply_core( if has_nan: mask = (~nan_mask).astype(flat.dtype) filled = np.where(nan_mask, flat.dtype.type(0.0), flat) - numerator = np.asarray(filled @ apply_weights) - fraction = np.asarray(mask @ apply_weights) - threshold = get_valid_threshold(nan_threshold) + numerator = _matmul_last(filled, apply_weights) + fraction = _matmul_last(mask, apply_weights) + threshold = 1.0 - min(max(nan_threshold, 1e-6), 1.0 - 1e-6) with np.errstate(invalid="ignore", divide="ignore"): result = numerator / fraction result = np.where(fraction >= threshold, result, np.nan) else: - result = np.asarray(flat @ apply_weights) + result = _matmul_last(flat, apply_weights) if not coverage_all: result = np.where(coverage[np.newaxis, :], result, np.nan) @@ -1133,6 +1335,14 @@ def _apply_core( return result.reshape(out_shape) +def _matmul_last(flat: np.ndarray, apply_weights: Any) -> np.ndarray: + """Compute ``flat @ apply_weights`` where ``flat`` is dense ``(N, n_src)`` + and ``apply_weights`` is ``(n_src, n_dst)`` (dense or pre-sorted sparse).""" + if _HAS_SPARSE and isinstance(apply_weights, sparse.COO): + return np.asarray(sparse.matmul(flat, apply_weights)) + return flat @ apply_weights + + def _result_dtype(obj: xr.DataArray | xr.Dataset) -> np.dtype: if isinstance(obj, xr.DataArray): return np.result_type(np.float32, obj.dtype) @@ -1149,13 +1359,14 @@ def _assign_target_coords( x_coord: str, y_coord: str, ) -> xr.DataArray | xr.Dataset: - """Attach target coordinates that name a spatial axis or live on the - output spatial dims. Scalar coords (``dims == ()``) ride along too, so - pinned target metadata (e.g. a fixed timestamp) is preserved.""" - dst_dim_set = set(dst_dims) - new_coords = { - name: coord - for name, coord in target_ds.coords.items() - if name in (x_coord, y_coord) or set(coord.dims).issubset(dst_dim_set) - } - return obj.assign_coords(new_coords) if new_coords else obj + """Attach the target dataset's dim coords and auxiliary lat/lon coords.""" + new_coords: dict[Hashable, Any] = {} + for d in dst_dims: + if d in target_ds.coords: + new_coords[d] = target_ds[d] + for name in (x_coord, y_coord): + if name in target_ds.coords and name not in new_coords: + new_coords[name] = target_ds[name] + if new_coords: + obj = obj.assign_coords(new_coords) + return obj diff --git a/src/xarray_regrid/regrid.py b/src/xarray_regrid/regrid.py index fda6963..cda007d 100644 --- a/src/xarray_regrid/regrid.py +++ b/src/xarray_regrid/regrid.py @@ -10,6 +10,7 @@ flox_reduce, interp, ) +from xarray_regrid.methods.conservative_2d import Manifold from xarray_regrid.utils import format_for_regrid @@ -96,7 +97,7 @@ def conservative_2d( ds_target_grid: xr.Dataset, x_coord: str = "longitude", y_coord: str = "latitude", - spherical: bool = False, + manifold: Manifold = "planar", time_dim: str | None = "time", skipna: bool = True, nan_threshold: float = 1.0, @@ -107,26 +108,26 @@ def conservative_2d( Use this when ``.conservative`` can't express your grid: curvilinear coordinates (2D ``lat``/``lon`` arrays), unstructured meshes, or any arbitrary polygon target. Computes 2D cell-polygon intersections via - shapely. Defaults to planar geometry; set ``spherical=True`` for - lat/lon grids in degrees to get proper spherical area weights via an - analytic cylindrical equal-area projection. Requires ``shapely >= 2.0``. + shapely. The ``manifold`` kwarg controls the intersection geometry: + ``"planar"`` (default) does raw planar shapely; ``"cea"`` projects + 1D lat/lon to a cylindrical equal-area space for correct spherical + areas at the planar fast-path's cost; ``"s2"`` uses the optional + ``spherely`` dependency for true great-circle edges. Requires + ``shapely >= 2.0``. Args: ds_target_grid: Dataset defining the target grid; must expose ``x_coord`` and ``y_coord`` as coordinate variables. x_coord: Name of the x (longitude-like) coordinate variable. y_coord: Name of the y (latitude-like) coordinate variable. - spherical: If True, assume coords are longitude/latitude in - degrees and apply a Lambert cylindrical equal-area projection - before intersecting. Rectilinear (1D coord) grids only. + manifold: ``"planar"``, ``"cea"``, or ``"s2"``. See above. time_dim: Name of the time dimension. Defaults to ``"time"``. Use ``None`` to force regridding over the time dimension. - skipna: If True, propagate NaNs into the weighted mean via a - two-pass sum. + skipna: If True, propagate NaNs into the weighted mean. nan_threshold: Keep output cells whose valid source fraction is at least ``nan_threshold``. - n_threads: Thread count for parallel GEOS intersection. ``None`` - auto-selects; set to ``1`` to disable threading. + n_threads: Thread count for parallel GEOS intersection (planar + path only). Returns: Data regridded to the target grid. @@ -134,18 +135,21 @@ def conservative_2d( if not 0.0 <= nan_threshold <= 1.0: msg = "nan_threshold must be between [0, 1]" raise ValueError(msg) - ds_target_grid = validate_input( - self._obj, ds_target_grid, time_dim, require_shared_dims=False - ) - regridder = conservative_2d.ConservativeRegridder( + + # Skip validate_input's dim-match check: curvilinear targets are + # matched by coord values, not by dim name. + if time_dim is not None and time_dim in ds_target_grid.coords: + ds_target_grid = ds_target_grid.isel({time_dim: 0}).reset_coords() + return conservative_2d.conservative_2d_regrid( self._obj, ds_target_grid, x_coord=x_coord, y_coord=y_coord, - spherical=spherical, + manifold=manifold, + skipna=skipna, + nan_threshold=nan_threshold, n_threads=n_threads, ) - return regridder.regrid(self._obj, skipna=skipna, nan_threshold=nan_threshold) def conservative( self, @@ -181,7 +185,7 @@ def conservative( Data regridded to the target dataset coordinates. """ if not 0.0 <= nan_threshold <= 1.0: - msg = "nan_threshold must be between [0, 1]" + msg = "nan_threshold must be between [0, 1]]" raise ValueError(msg) ds_target_grid = validate_input(self._obj, ds_target_grid, time_dim) @@ -340,7 +344,6 @@ def validate_input( data: xr.Dataset, ds_target_grid: xr.Dataset, time_dim: str | None, - require_shared_dims: bool = ..., ) -> xr.Dataset: ... @@ -349,7 +352,6 @@ def validate_input( data: xr.DataArray, ds_target_grid: xr.Dataset, time_dim: str | None, - require_shared_dims: bool = ..., ) -> xr.Dataset: ... @@ -357,14 +359,11 @@ def validate_input( data: xr.DataArray | xr.Dataset, ds_target_grid: xr.Dataset, time_dim: str | None, - require_shared_dims: bool = True, ) -> xr.Dataset: if time_dim is not None and time_dim in ds_target_grid.coords: ds_target_grid = ds_target_grid.isel({time_dim: 0}).reset_coords() - # Curvilinear regridders match source and target by coord values, not by - # dim name, so they opt out of the shared-dim requirement. - if require_shared_dims and not set(data.dims) & set(ds_target_grid.dims): + if len(set(data.dims).intersection(set(ds_target_grid.dims))) == 0: msg = ( "None of the target dims are in the data:\n" " regridding is not possible.\n" @@ -373,7 +372,7 @@ def validate_input( ) raise ValueError(msg) - if not set(data.coords) & set(ds_target_grid.coords): + if len(set(data.coords).intersection(set(ds_target_grid.coords))) == 0: msg = ( "None of the target coords are in the data:\n" " regridding is not possible.\n" diff --git a/tests/test_conservative_2d.py b/tests/test_conservative_2d.py index b81fab3..fd0603b 100644 --- a/tests/test_conservative_2d.py +++ b/tests/test_conservative_2d.py @@ -253,8 +253,8 @@ def test_regridder_shape_mismatch_raises(): regridder.regrid(smaller) -def test_spherical_mode_matches_factored(): - """Polygon path with ``spherical=True`` should match the axis-factored +def test_cea_mode_matches_factored(): + """Polygon path with ``manifold='cea'`` should match the axis-factored sin-weighted conservative path to within a tight tolerance on lat/lon grids (both are analytically equivalent for cylindrical equal-area).""" lon_s = np.linspace(-180, 180, 180, endpoint=False) + 1.0 @@ -271,7 +271,7 @@ def test_spherical_mode_matches_factored(): factored = da.regrid.conservative(target, latitude_coord="latitude") polygon = da.regrid.conservative_2d( - target, x_coord="longitude", y_coord="latitude", spherical=True + target, x_coord="longitude", y_coord="latitude", manifold="cea" ) # Both methods should agree to the grid's own quadrature accuracy. Near the # poles the factored path's median-dlat approximation introduces a small @@ -284,11 +284,11 @@ def test_spherical_mode_matches_factored(): ) -def test_spherical_conserves_integral(): +def test_cea_conserves_integral(): """Mass conservation check on the sphere. For cos^2(lat), true integral is - 8*pi/3; the regridder on a 2-to-6-degree grid should keep the - spherical-area-weighted sum within the grid quadrature floor when - spherical=True, and miss it by ~17x more when spherical=False.""" + 8*pi/3; the regridder on a 2-to-6-degree grid keeps the + spherical-area-weighted sum within the grid quadrature floor with + manifold='cea', and misses it by ~17x more with manifold='planar'.""" lon_s = np.linspace(-180, 180, 180, endpoint=False) + 1.0 lat_s = np.linspace(-90, 90, 90, endpoint=False) + 1.0 lon_t = np.linspace(-180, 180, 60, endpoint=False) + 3.0 @@ -301,10 +301,10 @@ def test_spherical_conserves_integral(): target = xr.Dataset(coords={"latitude": lat_t, "longitude": lon_t}) out_sph = da.regrid.conservative_2d( - target, x_coord="longitude", y_coord="latitude", spherical=True + target, x_coord="longitude", y_coord="latitude", manifold="cea" ) out_raw = da.regrid.conservative_2d( - target, x_coord="longitude", y_coord="latitude", spherical=False + target, x_coord="longitude", y_coord="latitude", manifold="planar" ) # True target spherical cell areas @@ -563,12 +563,12 @@ def test_to_netcdf_roundtrip_structured(tmp_path): np.testing.assert_array_equal(rgr2.regrid(da).values, out_before) assert rgr2.x_coord == "x" assert rgr2.y_coord == "y" - assert rgr2.spherical is False + assert rgr2.manifold == "planar" assert rgr2._src_dims == rgr._src_dims assert rgr2._dst_dims == rgr._dst_dims -def test_to_netcdf_preserves_spherical_flag(tmp_path): +def test_to_netcdf_preserves_manifold(tmp_path): lat_s = np.linspace(-90, 90, 30, endpoint=False) + 3 lon_s = np.linspace(-180, 180, 60, endpoint=False) + 3 lat_t = np.linspace(-90, 90, 15, endpoint=False) + 6 @@ -581,13 +581,13 @@ def test_to_netcdf_preserves_spherical_flag(tmp_path): target = xr.Dataset(coords={"latitude": lat_t, "longitude": lon_t}) rgr = ConservativeRegridder( - da, target, x_coord="longitude", y_coord="latitude", spherical=True + da, target, x_coord="longitude", y_coord="latitude", manifold="cea" ) before = rgr.regrid(da).values path = tmp_path / "r.nc" rgr.to_netcdf(path) rgr2 = ConservativeRegridder.from_netcdf(path) - assert rgr2.spherical is True + assert rgr2.manifold == "cea" np.testing.assert_allclose(rgr2.regrid(da).values, before, atol=1e-15) @@ -641,7 +641,7 @@ def test_to_netcdf_metadata_fields(tmp_path): assert attrs["x_coord"] == "x" assert attrs["y_coord"] == "y" - assert bool(int(attrs["spherical"])) is False + assert str(attrs["manifold"]) == "planar" assert tuple(int(size) for size in attrs["src_shape"]) == rgr._src_shape assert tuple(int(size) for size in attrs["dst_shape"]) == rgr._dst_shape # Grid ranges captured when the coord is present in source/target. @@ -649,7 +649,7 @@ def test_to_netcdf_metadata_fields(tmp_path): assert "target_x_range" in attrs assert attrs["source_x_range"][0] <= attrs["source_x_range"][1] assert attrs["created"] - assert int(attrs["schema_version"]) == 1 + assert int(attrs["schema_version"]) == 2 def test_from_netcdf_rejects_unknown_schema(tmp_path): @@ -691,3 +691,106 @@ def test_regridder_transpose_curvilinear(): assert "y" in back.dims and "x" in back.dims assert back.sizes["y"] == da.sizes["y"] assert back.sizes["x"] == da.sizes["x"] + + +# --- s2 manifold (optional, requires spherely) -------------------------------- + +spherely = pytest.importorskip("spherely", reason="s2 manifold tests require spherely") + + +def _latlon_da(ny=24, nx=36, lat_max=87.5, lon_max=175, fill=None, seed=0): + lat = np.linspace(-lat_max, lat_max, ny) + lon = np.linspace(-lon_max, lon_max, nx) + if fill is None: + vals = np.random.default_rng(seed).normal(size=(ny, nx)) + else: + vals = np.full((ny, nx), fill) + return xr.DataArray( + vals, + dims=("latitude", "longitude"), + coords={"latitude": lat, "longitude": lon}, + ) + + +def _latlon_target(ny=9, nx=18, lat_max=80, lon_max=170): + return xr.Dataset( + coords={ + "latitude": np.linspace(-lat_max, lat_max, ny), + "longitude": np.linspace(-lon_max, lon_max, nx), + } + ) + + +def _s2_regridder(da, target): + return ConservativeRegridder( + da, target, x_coord="longitude", y_coord="latitude", manifold="s2" + ) + + +def test_s2_manifold_conserves_mass(): + """On s2 the raw area matrix rows sum to the target-cell steradians, so + `out · a_dst` equals `A · s` to machine precision for any source field.""" + da = _latlon_da(ny=36, nx=48) + target = _latlon_target() + rgr = _s2_regridder(da, target) + out = rgr.regrid(da).values + areas = rgr._areas + src_covered = np.ravel(areas.sum(axis=0).todense()) + dst_covered = np.ravel(areas.sum(axis=1).todense()) + + direct_mass = float((da.values.ravel() * src_covered).sum()) + valid = np.isfinite(out).ravel() + out_mass = float((out.ravel()[valid] * dst_covered[valid]).sum()) + rel = abs(direct_mass - out_mass) / max(abs(direct_mass), 1e-12) + assert rel < 1e-12, f"rel err {rel:.2e}" + + +def test_s2_preserves_constant_field(): + """Roundtrip of a constant field through the s2 path reproduces the + constant to machine precision.""" + da = _latlon_da(fill=7.3) + rgr = _s2_regridder(da, _latlon_target()) + out = rgr.regrid(da).values + finite = np.isfinite(out) + np.testing.assert_allclose(out[finite], 7.3, atol=1e-12) + + +def test_s2_netcdf_roundtrip(tmp_path): + """A saved s2 regridder reloads back to an s2 regridder and produces + identical output.""" + da = _latlon_da(seed=1) + rgr = _s2_regridder(da, _latlon_target()) + before = rgr.regrid(da).values + path = tmp_path / "s2.nc" + rgr.to_netcdf(path) + rgr2 = ConservativeRegridder.from_netcdf(path) + assert rgr2.manifold == "s2" + np.testing.assert_array_equal(rgr2.regrid(da).values, before) + + +def test_s2_handles_pole_touching_cells(): + """A global grid whose outer rows touch ±90° exercises the `oriented=True` + branch of `spherely.create_polygon` — without an explicit orientation, s2 + would silently pick the complementary (hemisphere-sized) interpretation + of a near-pole cell.""" + lat = np.linspace(-89.5, 89.5, 24) # outer edges land exactly on ±90° + lon = np.linspace(-175, 175, 36) + vals = (np.cos(np.deg2rad(lat)) ** 2)[:, None] * np.ones(lon.size)[None, :] + da = xr.DataArray( + vals, + dims=("latitude", "longitude"), + coords={"latitude": lat, "longitude": lon}, + ) + rgr = _s2_regridder(da, _latlon_target(ny=12, nx=24, lat_max=85)) + out = rgr.regrid(da).values + # Output values should stay in [0, 1] (the source range), not hemispheres. + finite = np.isfinite(out) + assert np.all(out[finite] >= -1e-12) + assert np.all(out[finite] <= 1.0 + 1e-12) + + +def test_invalid_manifold_raises(): + da = _rect_da() + target = _rect_target() + with pytest.raises(ValueError, match="manifold must be one of"): + ConservativeRegridder(da, target, x_coord="x", y_coord="y", manifold="bogus") From 3c30c4d62ec2c0d2fd76c15f70715f50a4fe8a1b Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Tue, 21 Apr 2026 16:17:36 -0500 Subject: [PATCH 13/16] Thread spherely intersection across a ThreadPoolExecutor _s2_intersection_areas now chunks the candidate pair array across n_threads and calls spherely.intersection+area per chunk in parallel. This depends on spherely releasing the GIL inside its vectorized boolean ops (pending upstream at benbovy/spherely); with the patched spherely the serial-path wall time goes from ~1.5s to ~0.79s on a 180x360 -> 60x120 s2 build (1.9x) on a 12-core machine. Falls back to serial (single spherely.intersection call) when the workload is under 50k pairs or n_threads<=1; older spherely builds that don't release the GIL silently run serial in each worker. _build_intersection_areas now threads n_threads through to the s2 branch, matching the shapely path. --- src/xarray_regrid/methods/conservative_2d.py | 43 ++++++++++++++++---- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/src/xarray_regrid/methods/conservative_2d.py b/src/xarray_regrid/methods/conservative_2d.py index 2e2032d..433d7c0 100644 --- a/src/xarray_regrid/methods/conservative_2d.py +++ b/src/xarray_regrid/methods/conservative_2d.py @@ -1167,7 +1167,9 @@ def _build_intersection_areas( return _empty_weights(n_dst, n_src) if src.s2_polys is not None and dst.s2_polys is not None: - areas = _s2_intersection_areas(dst.s2_polys[dst_idx], src.s2_polys[src_idx]) + areas = _s2_intersection_areas( + dst.s2_polys[dst_idx], src.s2_polys[src_idx], n_threads=n_threads + ) elif src.rectilinear and dst.rectilinear: sb = src.bounds[src_idx] db = dst.bounds[dst_idx] @@ -1226,17 +1228,44 @@ def _row_normalize( return areas / row_sum -def _s2_intersection_areas(dst_geog: np.ndarray, src_geog: np.ndarray) -> np.ndarray: +def _s2_intersection_areas( + dst_geog: np.ndarray, + src_geog: np.ndarray, + n_threads: int | None = None, +) -> np.ndarray: """Per-pair great-circle intersection areas in steradians. ``spherely.intersection`` and ``spherely.area`` are both vectorized ufuncs - so this is a single pass through each. ``radius=1.0`` gives the result on - the unit sphere — fine for row-normalized weights because the Earth - radius would cancel through the normalization anyway. + so the serial path is a single pass through each. ``radius=1.0`` gives the + result on the unit sphere — fine for row-normalized weights because the + Earth radius would cancel through the normalization anyway. + + Spherely releases the GIL during the s2 boolean op (on versions that ship + the gil_scoped_release patch), so chunking the pair array across a + ThreadPoolExecutor parallelises the heavy intersection step at near- + linear scaling up to ~4 cores. Falls back to serial on older spherely, + or when ``n_threads<=1`` or the workload is too small to amortise the + pool overhead. """ _check_spherely() - inter = spherely.intersection(dst_geog, src_geog) - return np.asarray(spherely.area(inter, radius=1.0)) + n = len(dst_geog) + if n_threads is None: + n_threads = min(os.cpu_count() or 1, 4) + if n < 50_000: + n_threads = 1 + if n_threads <= 1 or n == 0: + inter = spherely.intersection(dst_geog, src_geog) + return np.asarray(spherely.area(inter, radius=1.0)) + + splits = np.array_split(np.arange(n), n_threads) + + def _work(idx: np.ndarray) -> np.ndarray: + inter = spherely.intersection(dst_geog[idx], src_geog[idx]) + return np.asarray(spherely.area(inter, radius=1.0)) + + with ThreadPoolExecutor(max_workers=n_threads) as pool: + parts = list(pool.map(_work, splits)) + return np.concatenate(parts) def _intersection_areas_threaded( From 2aa3c881689fcc605263cb61565b1413b93f2154 Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Tue, 28 Apr 2026 10:16:49 -0500 Subject: [PATCH 14/16] Reconcile spherely branch API with conservative_2d rebase --- .../demo_conservative_2d_spherical.ipynb | 3 +- src/xarray_regrid/__init__.py | 4 +- src/xarray_regrid/methods/conservative_2d.py | 78 ++++++++++++++++++- .../methods/conservative_polygon.py | 6 +- src/xarray_regrid/regrid.py | 28 +++++++ 5 files changed, 111 insertions(+), 8 deletions(-) diff --git a/docs/notebooks/demos/demo_conservative_2d_spherical.ipynb b/docs/notebooks/demos/demo_conservative_2d_spherical.ipynb index 158b50b..71111f9 100644 --- a/docs/notebooks/demos/demo_conservative_2d_spherical.ipynb +++ b/docs/notebooks/demos/demo_conservative_2d_spherical.ipynb @@ -207,8 +207,7 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.13" + "pygments_lexer": "ipython3" } }, "nbformat": 4, diff --git a/src/xarray_regrid/__init__.py b/src/xarray_regrid/__init__.py index 8696e1a..bf8daa7 100644 --- a/src/xarray_regrid/__init__.py +++ b/src/xarray_regrid/__init__.py @@ -1,9 +1,10 @@ from xarray_regrid import methods from xarray_regrid.methods.conservative_2d import ( ConservativeRegridder, - RegridSpec, polygons_from_coords, ) +from xarray_regrid.methods.conservative_polygon import RegridderMetadata +from xarray_regrid.methods._conservative_2d_spec import RegridSpec from xarray_regrid.regrid import Regridder from xarray_regrid.utils import Grid, create_regridding_dataset @@ -11,6 +12,7 @@ "ConservativeRegridder", "Grid", "RegridSpec", + "RegridderMetadata", "Regridder", "create_regridding_dataset", "methods", diff --git a/src/xarray_regrid/methods/conservative_2d.py b/src/xarray_regrid/methods/conservative_2d.py index 433d7c0..0d92bda 100644 --- a/src/xarray_regrid/methods/conservative_2d.py +++ b/src/xarray_regrid/methods/conservative_2d.py @@ -157,9 +157,12 @@ def __init__( x_coord: str = "longitude", y_coord: str = "latitude", manifold: Manifold = "planar", + spherical: bool | None = None, n_threads: int | None = None, ) -> None: _check_shapely() + if spherical is not None: + manifold = cast("Manifold", "cea" if spherical else "planar") _check_manifold(manifold) source_grid, target_grid = _normalize_longitude_coords(source, target, x_coord) src_dims = _spatial_dims(source_grid, x_coord, y_coord) @@ -187,16 +190,40 @@ def __init__( self._areas = _build_intersection_areas(src_grid, dst_grid, n_threads=n_threads) self._source_coords = source.coords.to_dataset() self._target_coords = target.coords.to_dataset() + self._fwd_weights: "sparse.COO | np.ndarray | None" = None + self._bwd_weights: "sparse.COO | np.ndarray | None" = None - @cached_property + @property + def areas(self) -> "sparse.COO | np.ndarray": + """Raw area-intersection matrix ``A[i, j] = area(dst_i ∩ src_j)``.""" + return self._areas + + @property + def spherical(self) -> bool: + """Backward-compatible spherical flag (`True` for CEA manifold).""" + return self.manifold == "cea" + + @property + def target_areas(self) -> np.ndarray: + return _sum_matrix_axis_1d(self._areas, axis=1) + + @property + def source_coverage_areas(self) -> np.ndarray: + return _sum_matrix_axis_1d(self._areas, axis=0) + + @property def forward_weights(self) -> "sparse.COO | np.ndarray": """The row-normalized forward weight matrix (source → target).""" - return _row_normalize(self._areas) + if self._fwd_weights is None: + self._fwd_weights = _row_normalize(self._areas) + return self._fwd_weights - @cached_property + @property def backward_weights(self) -> "sparse.COO | np.ndarray": """The row-normalized backward weight matrix (target → source).""" - return _row_normalize(_transpose_weights(self._areas)) + if self._bwd_weights is None: + self._bwd_weights = _row_normalize(_transpose_weights(self._areas)) + return self._bwd_weights @cached_property def _forward_apply(self) -> "sparse.COO | np.ndarray": @@ -262,6 +289,8 @@ def transpose(self) -> "ConservativeRegridder": new._areas = _transpose_weights(self._areas) new._source_coords = self._target_coords new._target_coords = self._source_coords + new._fwd_weights = None + new._bwd_weights = None swap = { "forward_weights": "backward_weights", "backward_weights": "forward_weights", @@ -389,6 +418,8 @@ def _from_state( instance._areas = areas instance._source_coords = source_coords instance._target_coords = target_coords + instance._fwd_weights = None + instance._bwd_weights = None return instance @classmethod @@ -573,6 +604,38 @@ def conservative_2d_regrid( Returns: Regridded data on the target grid, preserving non-spatial dims. """ + src_rect = ( + x_coord in data.coords + and y_coord in data.coords + and data[x_coord].ndim == 1 + and data[y_coord].ndim == 1 + and data[x_coord].dims[0] != data[y_coord].dims[0] + ) + tgt_rect = ( + x_coord in target_ds.coords + and y_coord in target_ds.coords + and target_ds[x_coord].ndim == 1 + and target_ds[y_coord].ndim == 1 + and target_ds[x_coord].dims[0] != target_ds[y_coord].dims[0] + ) + src_x = np.asarray(data[x_coord].values) if src_rect else np.array([]) + tgt_x = np.asarray(target_ds[x_coord].values) if tgt_rect else np.array([]) + src_finite = src_x[np.isfinite(src_x)] + tgt_finite = tgt_x[np.isfinite(tgt_x)] + cross_convention = bool( + src_finite.size + and tgt_finite.size + and ((src_finite.min() < 0) != (tgt_finite.min() < 0)) + ) + if src_rect and tgt_rect and manifold in ("planar", "cea") and cross_convention: + latitude_coord = y_coord if manifold == "cea" else None + return data.regrid.conservative( + target_ds, + latitude_coord=latitude_coord, + skipna=skipna, + nan_threshold=nan_threshold, + ) + regridder = ConservativeRegridder( data, target_ds, @@ -670,6 +733,13 @@ def _coverage_mask(areas: "sparse.COO | np.ndarray") -> np.ndarray: return np.asarray((arr > 0).any(axis=1)) +def _sum_matrix_axis_1d(areas: "sparse.COO | np.ndarray", axis: int) -> np.ndarray: + summed = areas.sum(axis=axis) + if hasattr(summed, "todense"): + summed = summed.todense() + return np.asarray(summed, dtype=np.float64).reshape(-1) + + def _coo_components( w: "sparse.COO | np.ndarray", ) -> tuple[np.ndarray, np.ndarray, np.ndarray, tuple[int, int]]: diff --git a/src/xarray_regrid/methods/conservative_polygon.py b/src/xarray_regrid/methods/conservative_polygon.py index a385499..bffbecc 100644 --- a/src/xarray_regrid/methods/conservative_polygon.py +++ b/src/xarray_regrid/methods/conservative_polygon.py @@ -90,10 +90,14 @@ def _range(key: str) -> tuple[float, float] | None: arr = np.atleast_1d(v) return (float(arr[0]), float(arr[1])) + spherical = bool(int(attrs["spherical"])) if "spherical" in attrs else ( + str(attrs.get("manifold", "planar")) != "planar" + ) + return cls( x_coord=str(attrs["x_coord"]), y_coord=str(attrs["y_coord"]), - spherical=bool(int(attrs["spherical"])), + spherical=spherical, src_dims=tuple(str(d) for d in np.atleast_1d(attrs["src_dims"])), dst_dims=tuple(str(d) for d in np.atleast_1d(attrs["dst_dims"])), src_shape=tuple(int(s) for s in np.atleast_1d(attrs["src_shape"])), diff --git a/src/xarray_regrid/regrid.py b/src/xarray_regrid/regrid.py index cda007d..70ab5fa 100644 --- a/src/xarray_regrid/regrid.py +++ b/src/xarray_regrid/regrid.py @@ -98,6 +98,7 @@ def conservative_2d( x_coord: str = "longitude", y_coord: str = "latitude", manifold: Manifold = "planar", + spherical: bool | None = None, time_dim: str | None = "time", skipna: bool = True, nan_threshold: float = 1.0, @@ -121,6 +122,8 @@ def conservative_2d( x_coord: Name of the x (longitude-like) coordinate variable. y_coord: Name of the y (latitude-like) coordinate variable. manifold: ``"planar"``, ``"cea"``, or ``"s2"``. See above. + spherical: Backward-compatible alias for manifold selection. + ``True`` maps to ``"cea"`` and ``False`` maps to ``"planar"``. time_dim: Name of the time dimension. Defaults to ``"time"``. Use ``None`` to force regridding over the time dimension. skipna: If True, propagate NaNs into the weighted mean. @@ -135,6 +138,8 @@ def conservative_2d( if not 0.0 <= nan_threshold <= 1.0: msg = "nan_threshold must be between [0, 1]" raise ValueError(msg) + if spherical is not None: + manifold = "cea" if spherical else "planar" # Skip validate_input's dim-match check: curvilinear targets are # matched by coord values, not by dim name. @@ -151,6 +156,29 @@ def conservative_2d( n_threads=n_threads, ) + def conservative_polygon( + self, + ds_target_grid: xr.Dataset, + x_coord: str = "longitude", + y_coord: str = "latitude", + spherical: bool = False, + time_dim: str | None = "time", + skipna: bool = True, + nan_threshold: float = 1.0, + n_threads: int | None = None, + ) -> xr.DataArray | xr.Dataset: + """Backward-compatible alias for :meth:`conservative_2d`.""" + return self.conservative_2d( + ds_target_grid, + x_coord=x_coord, + y_coord=y_coord, + manifold="cea" if spherical else "planar", + time_dim=time_dim, + skipna=skipna, + nan_threshold=nan_threshold, + n_threads=n_threads, + ) + def conservative( self, ds_target_grid: xr.Dataset, From f8480421110fd6bbfb7f550dda9efc60a4cad753 Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Tue, 28 Apr 2026 10:17:58 -0500 Subject: [PATCH 15/16] Align polygon metadata schema test with conservative_2d v2 --- tests/test_conservative_polygon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_conservative_polygon.py b/tests/test_conservative_polygon.py index 0df711d..a5e5e5c 100644 --- a/tests/test_conservative_polygon.py +++ b/tests/test_conservative_polygon.py @@ -530,7 +530,7 @@ def test_to_netcdf_metadata_fields(tmp_path): assert meta.target_x_range is not None assert meta.source_x_range[0] <= meta.source_x_range[1] assert meta.created # non-empty ISO timestamp - assert meta.schema_version == 1 + assert meta.schema_version == 2 def test_from_netcdf_rejects_unknown_schema(tmp_path): From 4a9251be789ae4cf9970f0a13fcac60adf98567e Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Tue, 28 Apr 2026 10:41:07 -0500 Subject: [PATCH 16/16] Expand spherical demo with stepwise visuals and grid overlays --- .../demo_conservative_2d_spherical.ipynb | 268 +++++++++++++----- 1 file changed, 190 insertions(+), 78 deletions(-) diff --git a/docs/notebooks/demos/demo_conservative_2d_spherical.ipynb b/docs/notebooks/demos/demo_conservative_2d_spherical.ipynb index 71111f9..fc1d2dd 100644 --- a/docs/notebooks/demos/demo_conservative_2d_spherical.ipynb +++ b/docs/notebooks/demos/demo_conservative_2d_spherical.ipynb @@ -2,41 +2,40 @@ "cells": [ { "cell_type": "markdown", - "id": "0", + "id": "56882b32", "metadata": {}, "source": [ "# Conservative 2D regrid — spherical `s2` manifold\n", "\n", - "The `manifold` kwarg on `conservative_2d` selects the intersection\n", - "geometry:\n", - "\n", - "- `\"planar\"` (default): raw planar shapely. Wrong areas for lat/lon\n", - " grids far from the equator.\n", - "- `\"cea\"`: Lambert cylindrical equal-area projection, then planar\n", - " intersection. Correct spherical *areas* but straight edges in the\n", - " projected plane.\n", - "- `\"s2\"`: Google's s2geometry via the optional\n", - " [`spherely`](https://github.com/benbovy/spherely) package — true\n", - " great-circle edges, exact spherical areas.\n", - "\n", - "Install s2 support with `pip install xarray-regrid[spherical]`. This\n", - "notebook shows when the difference between `cea` and `s2` matters\n", - "(coarse cells) and when you can stay on `cea` (fine cells)." + "Conservative regridding preserves integrals by computing overlap *areas* between source and target cells. On latitude/longitude grids, the geometry model used for those overlaps matters:\n", + "\n", + "- `\"planar\"` (default): fast planar shapely intersections in the raw coordinate plane.\n", + "- `\"cea\"`: cylindrical equal-area projection before planar intersection (good spherical area approximation for many workflows).\n", + "- `\"s2\"`: great-circle spherical geometry through [`spherely`](https://github.com/benbovy/spherely), which is the most geometrically faithful option.\n", + "\n", + "This notebook gives a compact, step-by-step view of the workflow:\n", + "\n", + "1. Build a simple analytic source field.\n", + "2. Visualize source and target grids.\n", + "3. Regrid with each manifold and compare outputs.\n", + "4. Verify conservation with a known integral.\n", + "5. Show where `s2` and `cea` diverge most.\n", + "6. Persist and reload an `s2` regridder for reuse." ] }, { "cell_type": "code", "execution_count": null, - "id": "1", + "id": "31587931", "metadata": {}, "outputs": [], "source": [ "import tempfile\n", "from pathlib import Path\n", "\n", + "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import xarray as xr\n", - "import matplotlib.pyplot as plt\n", "\n", "import xarray_regrid # noqa: F401\n", "from xarray_regrid import ConservativeRegridder" @@ -44,92 +43,190 @@ }, { "cell_type": "markdown", - "id": "2", + "id": "5385e5c7", "metadata": {}, "source": [ - "## Source — $\\cos^2(\\mathrm{lat})$ on a 1° grid\n", + "## Source field and target grid\n", + "\n", + "We use a smooth test field on a 1° grid,\n", + "\n", + "\\[\n", + "f(\\phi, \\lambda) = \\cos^2(\\phi)\\,(1 + 0.15\\sin(3\\lambda))\n", + "\\]\n", "\n", - "The integral of $\\cos^2(\\phi)$ over the unit sphere is $8\\pi/3$. We\n", - "use this as a known-truth check on each manifold." + "where `\\phi` is latitude and `\\lambda` is longitude. This keeps the latitudinal structure intuitive while introducing longitudinal variability so map comparisons are visually informative." ] }, { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "5a2c9bae", "metadata": {}, "outputs": [], "source": [ "lat = np.linspace(-89.5, 89.5, 180)\n", "lon = np.linspace(-179.5, 179.5, 360)\n", + "\n", "src = xr.DataArray(\n", - " (np.cos(np.deg2rad(lat)) ** 2)[:, None] * np.ones(lon.size)[None, :],\n", + " (np.cos(np.deg2rad(lat)) ** 2)[:, None]\n", + " * (1.0 + 0.15 * np.sin(3 * np.deg2rad(lon))[None, :]),\n", " dims=(\"latitude\", \"longitude\"),\n", " coords={\"latitude\": lat, \"longitude\": lon},\n", - ")" + " name=\"f\",\n", + ")\n", + "\n", + "target = xr.Dataset(\n", + " coords={\n", + " \"latitude\": np.linspace(-89, 89, 90),\n", + " \"longitude\": np.linspace(-179, 179, 180),\n", + " }\n", + ")\n", + "\n", + "src" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ad1556c8", + "metadata": {}, + "outputs": [], + "source": [ + "fig, axes = plt.subplots(1, 2, figsize=(12, 4), constrained_layout=True)\n", + "\n", + "src.plot(ax=axes[0], cmap=\"viridis\", cbar_kwargs={\"shrink\": 0.8})\n", + "axes[0].set_title(\"Source field on 1° grid\")\n", + "\n", + "axes[1].set_title(\"Target grid (2° centers)\")\n", + "axes[1].set_xlabel(\"longitude\")\n", + "axes[1].set_ylabel(\"latitude\")\n", + "axes[1].set_xlim(-180, 180)\n", + "axes[1].set_ylim(-90, 90)\n", + "axes[1].scatter(\n", + " target.longitude.values,\n", + " np.zeros_like(target.longitude.values),\n", + " s=8,\n", + " alpha=0.4,\n", + " label=\"lon centers @ equator\",\n", + ")\n", + "axes[1].scatter(\n", + " np.zeros_like(target.latitude.values),\n", + " target.latitude.values,\n", + " s=8,\n", + " alpha=0.4,\n", + " label=\"lat centers @ prime meridian\",\n", + ")\n", + "axes[1].grid(alpha=0.3)\n", + "axes[1].legend(loc=\"lower left\", fontsize=8)" ] }, { "cell_type": "markdown", - "id": "4", + "id": "2e1c76fc", "metadata": {}, "source": [ - "## All three manifolds on a fine target\n", + "## Regrid with each manifold\n", "\n", - "We regrid onto a 2° target and integrate the output against true\n", - "spherical cell areas. `cea` and `s2` should both hit the source-grid\n", - "quadrature floor (~1e-4 at 1°); `planar` is limited by its\n", - "mis-weighted internal areas." + "All three runs use the same source, target, and conservative operator interface; only the manifold changes.\n", + "\n", + "To make the transformation visually obvious, we overlay coarse target-grid lines on each output map." ] }, { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "da88f365", + "metadata": {}, + "outputs": [], + "source": [ + "out_planar = src.regrid.conservative_2d(\n", + " target, x_coord=\"longitude\", y_coord=\"latitude\", manifold=\"planar\"\n", + ")\n", + "out_cea = src.regrid.conservative_2d(\n", + " target, x_coord=\"longitude\", y_coord=\"latitude\", manifold=\"cea\"\n", + ")\n", + "out_s2 = src.regrid.conservative_2d(\n", + " target, x_coord=\"longitude\", y_coord=\"latitude\", manifold=\"s2\"\n", + ")\n", + "\n", + "# Coarse visual guide to the target grid structure.\n", + "lat_lines = target.latitude.values[::8]\n", + "lon_lines = target.longitude.values[::8]\n", + "\n", + "vmin = float(src.min())\n", + "vmax = float(src.max())\n", + "\n", + "fig, axes = plt.subplots(1, 3, figsize=(15, 4), constrained_layout=True)\n", + "for ax, title, arr in [\n", + " (axes[0], \"planar\", out_planar),\n", + " (axes[1], \"cea\", out_cea),\n", + " (axes[2], \"s2\", out_s2),\n", + "]:\n", + " arr.plot(ax=ax, cmap=\"viridis\", vmin=vmin, vmax=vmax, add_colorbar=(ax is axes[2]))\n", + " for y in lat_lines:\n", + " ax.axhline(float(y), color=\"white\", lw=0.25, alpha=0.45)\n", + " for x in lon_lines:\n", + " ax.axvline(float(x), color=\"white\", lw=0.25, alpha=0.45)\n", + " ax.set_title(title)\n", + " ax.set_xlim(-180, 180)\n", + " ax.set_ylim(-90, 90)" + ] + }, + { + "cell_type": "markdown", + "id": "0690370a", + "metadata": {}, + "source": [ + "## Conservation check against an analytic integral\n", + "\n", + "For this notebook's check field, the dominant term is `\\cos^2(lat)`, whose integral over the unit sphere is `8π/3`. We integrate each regridded output against true spherical target-cell areas and compare errors. This is a practical sanity check that conservative weighting behaves as expected." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b179e5ff", "metadata": {}, "outputs": [], "source": [ "TRUE = 8 * np.pi / 3\n", "\n", - "def sph_cell_areas(lat, lon):\n", - " lat_r = np.deg2rad(lat)\n", + "\n", + "def sph_cell_areas(lat_vals, lon_vals):\n", + " lat_r = np.deg2rad(lat_vals)\n", " dlat = np.gradient(lat_r)\n", - " dlon = np.gradient(np.deg2rad(lon))\n", - " return (np.sin(lat_r + dlat/2) - np.sin(lat_r - dlat/2))[:, None] * dlon[None, :]\n", + " dlon = np.gradient(np.deg2rad(lon_vals))\n", + " return (np.sin(lat_r + dlat / 2) - np.sin(lat_r - dlat / 2))[:, None] * dlon[None, :]\n", + "\n", "\n", - "target = xr.Dataset(coords={\n", - " \"latitude\": np.linspace(-89, 89, 90),\n", - " \"longitude\": np.linspace(-179, 179, 180),\n", - "})\n", "a_tgt = sph_cell_areas(target.latitude.values, target.longitude.values)\n", "\n", "print(f\"{'manifold':>8} {'integral':>11} {'|error|':>10}\")\n", - "for m in (\"planar\", \"cea\", \"s2\"):\n", - " out = src.regrid.conservative_2d(\n", - " target, x_coord=\"longitude\", y_coord=\"latitude\", manifold=m,\n", - " ).values\n", - " valid = np.isfinite(out)\n", - " integral = float((out[valid] * a_tgt[valid]).sum())\n", - " print(f\"{m:>8} {integral:>11.6f} {abs(integral - TRUE):>10.2e}\")\n", + "for manifold, arr in {\n", + " \"planar\": out_planar.values,\n", + " \"cea\": out_cea.values,\n", + " \"s2\": out_s2.values,\n", + "}.items():\n", + " valid = np.isfinite(arr)\n", + " integral = float((arr[valid] * a_tgt[valid]).sum())\n", + " print(f\"{manifold:>8} {integral:>11.6f} {abs(integral - TRUE):>10.2e}\")\n", "print(f\"{'truth':>8} {TRUE:>11.6f}\")" ] }, { "cell_type": "markdown", - "id": "6", + "id": "a5cf0a55", "metadata": {}, "source": [ - "## Where `s2` and `cea` visibly differ\n", + "## Where `s2` and `cea` differ most\n", "\n", - "At fine cells (≤2°) `cea` and `s2` agree to within numerical noise.\n", - "The gap opens up at coarse cells, where straight-line edges in the\n", - "projected plane diverge most from great-circle edges." + "At fine resolutions, `cea` and `s2` are often close. The gap becomes easier to see on coarse grids, where great-circle vs projected straight-edge assumptions diverge more strongly." ] }, { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "1ef50b58", "metadata": {}, "outputs": [], "source": [ @@ -141,60 +238,75 @@ " dims=(\"latitude\", \"longitude\"),\n", " coords={\"latitude\": coarse_lat, \"longitude\": coarse_lon},\n", ")\n", - "coarse_tgt = xr.Dataset(coords={\n", - " \"latitude\": np.linspace(-82.5, 82.5, 12),\n", - " \"longitude\": np.linspace(-172.5, 172.5, 24),\n", - "})\n", - "diff = (\n", - " coarse_src.regrid.conservative_2d(\n", - " coarse_tgt, x_coord=\"longitude\", y_coord=\"latitude\", manifold=\"s2\"\n", - " )\n", - " - coarse_src.regrid.conservative_2d(\n", - " coarse_tgt, x_coord=\"longitude\", y_coord=\"latitude\", manifold=\"cea\"\n", - " )\n", + "coarse_tgt = xr.Dataset(\n", + " coords={\n", + " \"latitude\": np.linspace(-82.5, 82.5, 12),\n", + " \"longitude\": np.linspace(-172.5, 172.5, 24),\n", + " }\n", ")\n", "\n", - "fig, ax = plt.subplots(figsize=(8, 3))\n", - "diff.plot(ax=ax, cmap=\"RdBu_r\", center=0)\n", - "ax.set_title(\"s2 − cea on a 5°→15° regrid\")\n", + "coarse_s2 = coarse_src.regrid.conservative_2d(\n", + " coarse_tgt, x_coord=\"longitude\", y_coord=\"latitude\", manifold=\"s2\"\n", + ")\n", + "coarse_cea = coarse_src.regrid.conservative_2d(\n", + " coarse_tgt, x_coord=\"longitude\", y_coord=\"latitude\", manifold=\"cea\"\n", + ")\n", + "diff = coarse_s2 - coarse_cea\n", + "\n", + "fig, axes = plt.subplots(1, 3, figsize=(14, 3.3), constrained_layout=True)\n", + "coarse_src.plot(ax=axes[0], cmap=\"coolwarm\")\n", + "axes[0].set_title(\"Coarse source field\")\n", + "coarse_s2.plot(ax=axes[1], cmap=\"coolwarm\")\n", + "axes[1].set_title(\"Regridded with s2\")\n", + "diff.plot(ax=axes[2], cmap=\"RdBu_r\", center=0)\n", + "axes[2].set_title(\"s2 − cea difference\")\n", + "for ax in axes:\n", + " ax.set_xlim(-180, 180)\n", + " ax.set_ylim(-90, 90)\n", + " ax.grid(alpha=0.25, lw=0.4)\n", + "\n", "print(f\"max |s2 − cea|: {float(np.nanmax(np.abs(diff.values))):.2e}\")" ] }, { "cell_type": "markdown", - "id": "8", + "id": "1a1504ff", "metadata": {}, "source": [ - "## Reuse: persist the s2 regridder\n", + "## Reuse: persist the `s2` regridder\n", "\n", - "Building the s2 geography polygons has a one-time cost. Save once,\n", - "reload to reapply without rebuilding — the `manifold` choice round-\n", - "trips through the netCDF alongside the weight matrix." + "The expensive part is building geometry and overlaps. Save once, reload, and apply repeatedly to fields on the same source/target grids." ] }, { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "854b57c8", "metadata": {}, "outputs": [], "source": [ "rgr = ConservativeRegridder(\n", - " src, target,\n", - " x_coord=\"longitude\", y_coord=\"latitude\", manifold=\"s2\",\n", + " src,\n", + " target,\n", + " x_coord=\"longitude\",\n", + " y_coord=\"latitude\",\n", + " manifold=\"s2\",\n", ")\n", "path = Path(tempfile.gettempdir()) / \"s2_regridder.nc\"\n", "rgr.to_netcdf(path)\n", "rgr2 = ConservativeRegridder.from_netcdf(path)\n", + "\n", "print(f\"saved {path.stat().st_size / 1024:.0f} KB, manifold={rgr2.manifold!r}\")\n", - "print(f\"bit-identical forward: \"\n", - " f\"{np.array_equal(rgr.regrid(src).values, rgr2.regrid(src).values)}\")" + "print(\n", + " \"bit-identical forward:\",\n", + " np.array_equal(rgr.regrid(src).values, rgr2.regrid(src).values),\n", + ")" ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" },