Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions docs/notebooks/demos/demo_conservative_2d_curvilinear.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
{
"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 matplotlib.pyplot as plt\n",
"import numpy as np\n",
"import xarray as xr\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\")\n",
"ax.set_ylabel(\"latitude\")\n",
"ax.set_aspect(\"equal\")"
]
},
{
"cell_type": "markdown",
"id": "6",
"metadata": {},
"source": [
"## Regrid and plot\n",
"\n",
"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."
]
},
{
"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\")\n",
"ax.set_ylabel(\"latitude\")\n",
"ax.set_aspect(\"equal\")"
]
}
],
"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
}
Loading