{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "# Xarray 7: Calculating ENSO with Xarray\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Objectives \n", "\n", "- Load SST data from CESM2\n", "- Mask data using `.where()` method\n", "- Compute climatologies and anomalies using `.groupby()`\n", "- Use `.rolling()` to compute moving average\n", "- Compute, normalize, and plot the Niño 3.4 Index " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Prerequisites\n", "\n", "\n", "| Concepts | Importance | Notes |\n", "| --- | --- | --- |\n", "| [Introduction to Xarray](./xarray.ipynb) | Necessary | |\n", "| [Computation and Masking](./computation-masking.ipynb) | Necessary | |\n", "\n", "\n", "\n", "- **Time to learn**: 20 minutes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Imports \n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import cartopy.crs as ccrs\n", "import matplotlib.pyplot as plt\n", "import xarray as xr\n", "from pythia_datasets import DATASETS" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Niño 3.4 Index" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "In this notebook, we are going to combine all concepts/topics we've covered so far to compute [Niño 3.4 Index](https://climatedataguide.ucar.edu/climate-data/nino-sst-indices-nino-12-3-34-4-oni-and-tni) for the CESM2 submission for the [CMIP6 project](https://esgf-node.llnl.gov/projects/cmip6/). \n", "\n", "\n", "> Niño 3.4 (5N-5S, 170W-120W): The Niño 3.4 anomalies may be thought of as representing the average equatorial SSTs across the Pacific from about the dateline to the South American coast. The Niño 3.4 index typically uses a 5-month running mean, and El Niño or La Niña events are defined when the Niño 3.4 SSTs exceed +/- 0.4C for a period of six months or more.\n", "\n", "> Nino X Index computation: (a) Compute area averaged total SST from Niño X region; (b) Compute monthly climatology (e.g., 1950-1979) for area averaged total SST from Niño X region, and subtract climatology from area averaged total SST time series to obtain anomalies; (c) Smooth the anomalies with a 5-month running mean; (d) Normalize the smoothed values by its standard deviation over the climatological period.\n", "\n", "\n", "![](https://www.ncdc.noaa.gov/monitoring-content/teleconnections/nino-regions.gif)\n", "\n", "\n", "At the end of this notebook, you should be able to produce a plot that looks similar to this [Oceanic Niño Index plot](https://climatedataguide.ucar.edu/sites/default/files/styles/extra_large/public/2022-03/oni.monthly.smoo_stro.png?itok=KX4FZyFw):\n", "\n", "![](https://climatedataguide.ucar.edu/sites/default/files/styles/extra_large/public/2022-03/oni.monthly.smoo_stro.png?itok=KX4FZyFw)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Open the sea surface temperature and areacello datasets and use Xarray's `merge` method to combine them into a single one." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "remove-stderr" ] }, "outputs": [], "source": [ "filepath = DATASETS.fetch('CESM2_sst_data.nc')\n", "data = xr.open_dataset(filepath)\n", "filepath2 = DATASETS.fetch('CESM2_grid_variables.nc')\n", "areacello = xr.open_dataset(filepath2).areacello\n", "ds = xr.merge([data, areacello])\n", "ds" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ds.tos.isel(time=0).min()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Visualize the first time slice to make sure the data looks okay" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(12, 6))\n", "ax = plt.axes(projection=ccrs.Robinson(central_longitude=180))\n", "ax.coastlines()\n", "ax.gridlines()\n", "ds.tos.isel(time=0).plot(\n", " ax=ax, transform=ccrs.PlateCarree(), vmin=-2, vmax=30, cbar_kwargs={'shrink': 0.5}, cmap='coolwarm'\n", ");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Select the Niño 3.4 region \n", "\n", "There are a couple ways we could selecting the Niño 3.4 region:\n", "\n", "1. Use `sel()` or `isel()`\n", "2. Use `where()` and select all vlaues within the bounds of interest" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1: `sel`: " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "tos_nino34 = ds.sel(lat=slice(-5, 5), lon=slice(190, 240))\n", "tos_nino34" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "2: `where`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "tos_nino34 = ds.where((ds.lat<5) & (ds.lat>-5) & (ds.lon>190) & (ds.lon<240), drop=True)\n", "tos_nino34" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's plot the selected region to make sure we are doing the right thing." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(12, 6))\n", "ax = plt.axes(projection=ccrs.Robinson(central_longitude=180))\n", "ax.coastlines()\n", "ax.gridlines()\n", "tos_nino34.tos.isel(time=0).plot(\n", " ax=ax, transform=ccrs.PlateCarree(), cbar_kwargs={'shrink': 0.5}, vmin=-2, vmax=30, cmap='coolwarm'\n", ")\n", "ax.set_extent((120, 300, 10, -10))" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Compute monthly climatology" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "Compute the monthly climo for area averaged total SST from Niño 3.4 region, and subtract climatology from area averaged total SST time series to obtain anomalies" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "gb = tos_nino34.tos.groupby('time.month')\n", "tos_nino34_anom = gb - gb.mean(dim='time')\n", "index_nino34 = tos_nino34_anom.weighted(tos_nino34.areacello).mean(dim=['lat', 'lon'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Smooth the anomalies with a 5-month running mean" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "index_nino34_rolling_mean = index_nino34.rolling(time=5,center=True).mean()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "index_nino34.plot(size=8)\n", "index_nino34_rolling_mean.plot()\n", "plt.legend(['anomaly', '5-month running mean anomaly']);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Normalize the smoothed values" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll normalize the values by dividing the rolling mean by the standard deviation." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "std_dev = tos_nino34.tos.std()\n", "std_dev" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "normalized_index_nino34_rolling_mean = index_nino34_rolling_mean / std_dev" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Visualize the computed Niño 3.4 index. \n", "We'll highlight values in excess of 0.5, roughly corresponding to El Niño (warm) / La Niña (cold) events.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(12, 6))\n", "\n", "plt.fill_between(\n", " normalized_index_nino34_rolling_mean.time.data,\n", " normalized_index_nino34_rolling_mean.where(\n", " normalized_index_nino34_rolling_mean >= 0.4\n", " ).data,\n", " 0.4,\n", " color='red',\n", " alpha=0.9,\n", ")\n", "plt.fill_between(\n", " normalized_index_nino34_rolling_mean.time.data,\n", " normalized_index_nino34_rolling_mean.where(\n", " normalized_index_nino34_rolling_mean <= -0.4\n", " ).data,\n", " -0.4,\n", " color='blue',\n", " alpha=0.9,\n", ")\n", "\n", "normalized_index_nino34_rolling_mean.plot(color='black')\n", "plt.axhline(0, color='black', lw=0.5)\n", "plt.axhline(0.4, color='black', linewidth=0.5, linestyle='dotted')\n", "plt.axhline(-0.4, color='black', linewidth=0.5, linestyle='dotted')\n", "plt.title('Niño 3.4 Index');" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Summary \n", "\n", "- We have applied a variety of Xarray's selection, grouping, and statistical functions to compute and visualize an important climate index.\n", "\n", "### What's next?\n", "\n", "In the next notebook, we will use Xarray to analyze and visualize NWP model output in another common format, GRIB." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Resources and References\n", "\n", "- [Niño 3.4 Index](https://climatedataguide.ucar.edu/climate-data/nino-sst-indices-nino-12-3-34-4-oni-and-tni)\n", "- [Matplotlib's `fill_between` method](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.fill_between.html)\n", "- [Matplotlib's `axhline` method](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.axhline.html) (see also its analogous `axvline` method) \n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 August 2023 Environment", "language": "python", "name": "aug23" }, "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.11.4" }, "toc-autonumbering": false, "toc-showcode": false, "toc-showmarkdowntxt": false, "toc-showtags": false }, "nbformat": 4, "nbformat_minor": 4 }