|
17 | 17 | "metadata": {}, |
18 | 18 | "outputs": [], |
19 | 19 | "source": [ |
| 20 | + "import matplotlib.pyplot as plt\n", |
20 | 21 | "import numpy as np\n", |
21 | 22 | "\n", |
22 | 23 | "import uxarray as ux\n", |
23 | 24 | "\n", |
24 | 25 | "uxds = ux.open_dataset(\n", |
25 | 26 | " \"../../test/meshfiles/ugrid/outCSne30/outCSne30.ug\",\n", |
26 | 27 | " \"../../test/meshfiles/ugrid/outCSne30/outCSne30_vortex.nc\",\n", |
27 | | - ")\n", |
28 | | - "uxds[\"psi\"].plot(cmap=\"inferno\", periodic_elements=\"split\")" |
| 28 | + ")" |
29 | 29 | ] |
30 | 30 | }, |
31 | 31 | { |
|
35 | 35 | "source": [ |
36 | 36 | "## What is a Zonal Average/Mean?\n", |
37 | 37 | "\n", |
38 | | - "A zonal average (or zonal mean) is a statistical measure that represents the average of a variable along one or more lines of constant latitude. In other words, it's the mean value calculated around the sphere at constant latitudes.\n", |
| 38 | + "A zonal average (or zonal mean) is a statistical measure that represents the average of a variable along lines of constant latitude or over latitudinal bands.\n", |
39 | 39 | "\n", |
40 | 40 | "UXarray provides two types of zonal averaging:\n", |
41 | | - "- **Non-conservative**: Weights candidate faces by the length of intersection of a line of constant latitude\n", |
| 41 | + "- **Non-conservative**: Samples values at specific latitude lines\n", |
42 | 42 | "- **Conservative**: Preserves integral quantities by weighting faces by their area overlap with latitude bands\n", |
43 | 43 | "\n", |
44 | 44 | "```{seealso}\n", |
|
53 | 53 | "source": [ |
54 | 54 | "## Non-Conservative Zonal Averaging\n", |
55 | 55 | "\n", |
56 | | - "The non-conservative method samples values at specific lines of constant latitude. This is the default behavior and is suitable for visualization and general analysis where exact conservation is not required." |
| 56 | + "The non-conservative method samples values at specific lines of constant latitude. This is the default behavior and is suitable for visualization and general analysis where exact conservation is not required.\n", |
| 57 | + "\n", |
| 58 | + "Let's first visualize our data field and then demonstrate zonal averaging:" |
| 59 | + ] |
| 60 | + }, |
| 61 | + { |
| 62 | + "cell_type": "code", |
| 63 | + "execution_count": null, |
| 64 | + "id": "global_field_plot", |
| 65 | + "metadata": {}, |
| 66 | + "outputs": [], |
| 67 | + "source": [ |
| 68 | + "# Display the global field\n", |
| 69 | + "uxds[\"psi\"].plot(cmap=\"inferno\", periodic_elements=\"split\", title=\"Global Field\")\n", |
| 70 | + "\n", |
| 71 | + "# Show conceptual latitude bands for zonal averaging\n", |
| 72 | + "print(\"Latitude bands for zonal averaging:\")\n", |
| 73 | + "lat_bands = np.arange(-90, 91, 30)\n", |
| 74 | + "for i, lat in enumerate(lat_bands[:-1]):\n", |
| 75 | + " print(f\"Band {i + 1}: {lat}° to {lat_bands[i + 1]}°\")" |
57 | 76 | ] |
58 | 77 | }, |
59 | 78 | { |
|
222 | 241 | "id": "visual_comparison", |
223 | 242 | "metadata": {}, |
224 | 243 | "source": [ |
225 | | - "### Visual Comparison: Conservative vs Non-Conservative", |
226 | | - "", |
227 | | - "The differences between methods reflect their fundamental approaches:", |
228 | | - "", |
229 | | - "- **Conservative**: More accurate for physical quantities because it accounts for the actual area of each face within latitude bands", |
230 | | - "- **Non-conservative**: Faster but approximates by sampling at specific latitude lines", |
231 | | - "", |
| 244 | + "### Visual Comparison: Conservative vs Non-Conservative\n", |
| 245 | + "\n", |
| 246 | + "The differences between methods reflect their fundamental approaches:\n", |
| 247 | + "\n", |
| 248 | + "- **Conservative**: More accurate for physical quantities because it accounts for the actual area of each face within latitude bands\n", |
| 249 | + "- **Non-conservative**: Faster but approximates by sampling at specific latitude lines\n", |
| 250 | + "\n", |
232 | 251 | "The differences you see indicate how much area-weighting matters for your specific data and grid resolution." |
233 | 252 | ] |
234 | 253 | }, |
|
239 | 258 | "metadata": {}, |
240 | 259 | "outputs": [], |
241 | 260 | "source": [ |
242 | | - "import matplotlib.pyplot as plt\n", |
243 | | - "\n", |
244 | 261 | "# Compare with non-conservative at same latitudes\n", |
245 | 262 | "band_centers = 0.5 * (bands[:-1] + bands[1:])\n", |
246 | 263 | "non_conservative_comparison = uxds[\"psi\"].zonal_mean(lat=band_centers)\n", |
|
338 | 355 | "metadata": {}, |
339 | 356 | "outputs": [], |
340 | 357 | "source": [ |
341 | | - "# Create combined plot with matplotlib\n", |
342 | | - "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))\n", |
| 358 | + "# Display the global field\n", |
| 359 | + "print(\"Global Field:\")\n", |
| 360 | + "uxds[\"psi\"].plot(cmap=\"inferno\", periodic_elements=\"split\", title=\"Global Field\")\n", |
343 | 361 | "\n", |
344 | | - "# Left: Data field\n", |
345 | | - "uxds[\"psi\"].plot(ax=ax1, cmap=\"inferno\")\n", |
346 | | - "ax1.set_title(\"Global Field\")\n", |
| 362 | + "# Create zonal average plot\n", |
| 363 | + "fig, ax = plt.subplots(1, 1, figsize=(8, 6))\n", |
347 | 364 | "\n", |
348 | | - "# Right: Zonal average\n", |
349 | | - "ax2.plot(\n", |
| 365 | + "ax.plot(\n", |
350 | 366 | " zonal_mean_psi.values,\n", |
351 | 367 | " zonal_mean_psi.coords[\"latitudes\"],\n", |
352 | 368 | " \"o-\",\n", |
353 | 369 | " linewidth=2,\n", |
354 | | - " markersize=4,\n", |
| 370 | + " markersize=6,\n", |
| 371 | + " color=\"blue\",\n", |
| 372 | + " label=\"Zonal Mean\",\n", |
355 | 373 | ")\n", |
356 | | - "ax2.set_xlabel(\"Zonal Mean Value\")\n", |
357 | | - "ax2.set_ylabel(\"Latitude (degrees)\")\n", |
358 | | - "ax2.set_title(\"Zonal Average\")\n", |
359 | | - "ax2.grid(True, alpha=0.3)\n", |
360 | | - "ax2.set_ylim(-90, 90)\n", |
361 | | - "ax2.set_xlim(0.8, 1.2)\n", |
362 | | - "\n", |
363 | | - "plt.suptitle(\"Combined Zonal Average & Raster Plot\")\n", |
| 374 | + "\n", |
| 375 | + "ax.set_xlabel(\"Zonal Mean Value\")\n", |
| 376 | + "ax.set_ylabel(\"Latitude (degrees)\")\n", |
| 377 | + "ax.set_title(\"Zonal Average\")\n", |
| 378 | + "ax.grid(True, alpha=0.3)\n", |
| 379 | + "ax.set_ylim(-90, 90)\n", |
| 380 | + "ax.legend()\n", |
| 381 | + "\n", |
| 382 | + "# Add reference latitude lines\n", |
| 383 | + "sample_bands = [-60, -30, 0, 30, 60]\n", |
| 384 | + "for lat_band in sample_bands:\n", |
| 385 | + " ax.axhline(y=lat_band, color=\"red\", linestyle=\"--\", alpha=0.5, linewidth=1)\n", |
| 386 | + "\n", |
364 | 387 | "plt.tight_layout()\n", |
365 | 388 | "plt.show()" |
366 | 389 | ] |
|
0 commit comments