From f03c38f99d39bb57d4dcf69f8b581fafa9ed858f Mon Sep 17 00:00:00 2001 From: aaronspring <aaron.spring@mpimet.mpg.de> Date: Tue, 29 Jun 2021 14:30:32 +0200 Subject: [PATCH] xoak example --- ci/pymistral.yml | 24 ++- notebooks/xarray_xoak_ICON.ipynb | 356 +++++++++++++++++++++++++++++++ 2 files changed, 370 insertions(+), 10 deletions(-) create mode 100644 notebooks/xarray_xoak_ICON.ipynb diff --git a/ci/pymistral.yml b/ci/pymistral.yml index c396676..9d5c278 100644 --- a/ci/pymistral.yml +++ b/ci/pymistral.yml @@ -3,7 +3,8 @@ channels: - conda-forge dependencies: - bokeh - - cartopy + - cartopy>=0.18 + - matplotlib>=3.3 - cdo - dask - dask-jobqueue @@ -20,9 +21,6 @@ dependencies: - intake - intake-xarray - hvplot - - geoviews - - holoviews - - pytest<5.4.0 - pytest-cov - pytest-sugar - nb_conda_kernels @@ -34,15 +32,21 @@ dependencies: - pynio - flake8 - nc-time-axis - - xrviz - - xhistogram + - nodejs>=10.0.0 + - nb_black + - xoak + - climpred + - regionmask - pip: - - git+https://github.com/jbusecke/cmip6_preprocessing.git - - git+https://github.com/xgcm/xgcm.git - - git+https://github.com/xgcm/xrft.git - - git+https://github.com/mathause/regionmask.git + - cmip6_preprocessing + - xgcm - git+https://github.com/intake/intake-esm.git - pre-commit - pytest-lazy-fixture - pytest-tldr - git+https://github.com/dask/dask-labextension.git + - git+https://github.com/intake/intake_geopandas.git + - git+https://github.com/NCAR/intake-thredds.git + #- git+https://github.com/intake/intake.git + - git+https://github.com/intake/intake-xarray.git + - git+https://github.com/intake/filesystem_spec.git diff --git a/notebooks/xarray_xoak_ICON.ipynb b/notebooks/xarray_xoak_ICON.ipynb new file mode 100644 index 0000000..736c8ef --- /dev/null +++ b/notebooks/xarray_xoak_ICON.ipynb @@ -0,0 +1,356 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# `xarray` and `ICON`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import xarray as xr\n", + "xr.set_options(display_style='text')\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib\n", + "import cartopy.crs as ccrs\n", + "import cartopy as cp" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# `xoak`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- https://xoak.readthedocs.io/en/latest/examples/introduction.html\n", + "- https://xoak.readthedocs.io/en/latest/examples/dask_support.html" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import xoak" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def rad_to_deg(ds):\n", + " \"\"\"Convert radian units to deg.\"\"\"\n", + " #ds.coords.compute()\n", + " for c in ds.coords:\n", + " if 'units' in ds[c].attrs:\n", + " if ds[c].attrs['units'] == 'radian':\n", + " print(f'convert {c} from rad to deg')\n", + " ds[c] = ds[c]* 180./np.pi\n", + " ds[c].attrs['units'] = 'degrees'\n", + " elif 'bnds' in c:\n", + " print(f'convert {c} from rad to deg')\n", + " ds[c] = ds[c]* 180./np.pi\n", + " ds[c].attrs['units'] = 'degrees'\n", + " return ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#igrid = xr.open_dataset('/work/mh0727/m300732/icon-oes-hamocc/experiments/ler0956/icon_grid_0036_R02B04_O.nc')\n", + "#igrid = rad_to_deg(igrid)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ICON output I want to work with\n", + "ds = xr.open_dataset('/work/mh0727/m300732/icon-oes-hamocc/experiments/ler0956/ler0956_hamocc_34500101T000000Z.nc',\n", + " chunks={'time': 1})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# move from data_vars to coords\n", + "ds = ds.set_coords(['clon_bnds','clat_bnds'])\n", + "\n", + "ds = rad_to_deg(ds)\n", + "# \n", + "ds['phyp'] # ncells is dimension" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# new grid I want to work on\n", + "mpiom_grid = xr.open_dataset('/home/mpim/m300524/mpiom_fx.nc')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## remap with xoak" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# set xoak index\n", + "ds.xoak.set_index(['clat', 'clon'], 'sklearn_geo_balltree')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from dask.diagnostics import ProgressBar\n", + "import dask\n", + "with ProgressBar(), dask.config.set(scheduler='processes'):\n", + " ds_selection = ds.xoak.sel(clat=mpiom_grid.lat, clon=mpiom_grid.lon)\n", + "\n", + "# %time ds_selection = ds.xoak.sel(clat=mpiom_grid.lat, clon=mpiom_grid.lon) # also runs without dask scheduler\n", + "print('remapping',ds.nbytes/1e6,'MB to ',ds_selection.nbytes/1e6,'MB')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# all lazy dask arrays\n", + "ds_selection['phyp'] # ncells is no dimension anymore, but x and y" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## plot remapped with `xarray.plot()`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ds_selection['phyp'].isel(depth=0).plot(col='time', col_wrap=5, robust=True, yincrease=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## plot remapped with `pymistral.plot()` wrapping xarray and cartopy" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pymistral\n", + "ds_selection['phyp'].isel(depth=0).rename({'clon':'lon','clat':'lat'}).compute().plot_map(col='time',col_wrap=3,robust=True, aspect=2, feature='land')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Triangular plotting\n", + "\n", + "plotting the triangles with `plt.tripcolor` from `xarray`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- could be handle by accessor: http://xarray.pydata.org/en/stable/internals/extending-xarray.html\n", + "- rewrite nicer with xarray instead of np commands where possible" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "@xr.register_dataset_accessor('icon')\n", + "class CartopyMap(object):\n", + " def __init__(self, xarray_obj):\n", + " self._obj = xarray_obj\n", + "\n", + " def plot(self,\n", + " v=None,\n", + " ax=None,\n", + " proj=ccrs.PlateCarree(),\n", + " robust=False,\n", + " **tripcolor_kwargs\n", + " ):\n", + " \"\"\"\n", + " Plot the variable v from an xr.Dataset.\n", + " \n", + " Note: xr.DataArray.icon.plot() would be nicer,\n", + " but the xr.DataArray doesnt carry the neccessary clon_bnds and clat_bnds coords.\n", + " \n", + " Also having some issues how to get the plot nice without buggy triangle,\n", + " but that happened also in matplotlib.\n", + " \n", + " Ideally, this plot function could work like xr.DataArray.plot(col, row, vmin, vmax, robust, ...)\n", + " \"\"\"\n", + " da = self._obj\n", + " # --- Triangulation\n", + " ntr = da.clon.size\n", + " clon_bnds_rs = da['clon_bnds'].values.reshape(ntr*3)\n", + " clat_bnds_rs = da['clat_bnds'].values.reshape(ntr*3)\n", + " triangles = np.arange(ntr*3).reshape(ntr,3)\n", + " Tri = matplotlib.tri.Triangulation(clon_bnds_rs, clat_bnds_rs, triangles=triangles)\n", + "\n", + " if isinstance(da,xr.Dataset):\n", + " if v is None:\n", + " v=list(da.data_vars)\n", + " if len(v)>0:\n", + " v=v[0] # take first variable\n", + " da=da[v]\n", + " else:\n", + " da=da[v]\n", + "\n", + " vmin = tripcolor_kwargs.pop('vmin',None)\n", + " vmax = tripcolor_kwargs.pop('vmax',None)\n", + " if vmin is None and vmax is None and robust:\n", + " vmin = da.quantile(0.02).values\n", + " vmax = da.quantile(.98).values\n", + " \n", + " # what if more than two dims: see xr.facetgrid\n", + " fig, axes = plt.subplots(subplot_kw=dict(projection=proj),figsize=(8,4))\n", + " # best would be to add tripcolor to xarray\n", + " hm = axes.tripcolor(Tri,\n", + " da,\n", + " vmin=vmin,\n", + " vmax=vmax,\n", + " #edgecolors='k',\n", + " #lw=0.01,\n", + " transform=ccrs.PlateCarree(),\n", + " alpha=.5,\n", + " **tripcolor_kwargs\n", + " )\n", + " # now add features, labels etc\n", + " axes.add_feature(cp.feature.LAND, zorder=0)\n", + " #axes.set_extent((-178, 178, -88, 88), crs=ccrs.PlateCarree())\n", + " cb = plt.colorbar(mappable=hm, ax=axes, extend='both')\n", + " ## use metadata\n", + " title_str = ''\n", + " title_labels = [c for c in da.coords if da.coords[c].size == 1]\n", + " for l in title_labels:\n", + " title_str += f', {l} = {str(da[l].astype(str).values)}'\n", + " axes.set_title(title_str[1:])\n", + " cb.set_label(f'{da.attrs[\"long_name\"]}\\n[{da.attrs[\"units\"]}]', rotation = 90)\n", + " # add x,y ticks and labels\n", + " return axes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ds.isel(depth=0,time=2).icon.plot('phyp', cmap='RdBu')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ds.isel(depth=0,time=2).icon.plot('po4', robust=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:pymistral]", + "language": "python", + "name": "conda-env-pymistral-py" + }, + "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.8.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} -- GitLab