diff --git a/examples/key_metrics/config.yml b/examples/key_metrics/config.yml index cc397e1..ceed3e6 100644 --- a/examples/key_metrics/config.yml +++ b/examples/key_metrics/config.yml @@ -115,19 +115,27 @@ compute_notebooks: # config_path: . # config_fil_str: "config_f.cam6_3_119.FLTHIST_ne30.r328_gamma0.33_soae.001.yaml" -# ocn: -# ocean_surface: + glc: + LIWG_SMB_diagnostic: + parameter_groups: + none: + obs_path: '/glade/u/home/gunterl/obs_diagnostic_cesm/' + obs_name: 'GrIS_MARv3.12_climo_1960_1999.nc' + climo_nyears: 40 + last_year: 101 + +# ice: +# seaice: # parameter_groups: # none: -# Case: b.e23_alpha16b.BLT1850.ne30_t232.054 -# savefigs: False -# mom6_tools_config: -# start_date: '0091-01-01' -# end_date: '0101-01-01' -# Fnames: -# native: 'mom6.h.native.????-??.nc' -# static: 'mom6.h.static.nc' -# oce_cat: /glade/u/home/gmarques/libs/oce-catalogs/reference-datasets.yml +# cases: +# - g.e23_a16g.GJRAv4.TL319_t232_hycom1_N75.2024.005 +# - g.e23_a16g.GJRAv4.TL319_t232_zstar_N65.2024.004 +# begyr1: 245 +# endyr1: 305 +# begyr2: 245 +# endyr2: 305 +# nyears: 25 # lnd: # land_comparison: @@ -140,18 +148,19 @@ compute_notebooks: # - 1850pAD # - 1850pSASU -# ice: -# seaice: +# ocn: +# ocean_surface: # parameter_groups: # none: -# cases: -# - g.e23_a16g.GJRAv4.TL319_t232_hycom1_N75.2024.005 -# - g.e23_a16g.GJRAv4.TL319_t232_zstar_N65.2024.004 -# begyr1: 245 -# endyr1: 305 -# begyr2: 245 -# endyr2: 305 -# nyears: 25 +# Case: b.e23_alpha16b.BLT1850.ne30_t232.054 +# savefigs: False +# mom6_tools_config: +# start_date: '0091-01-01' +# end_date: '0101-01-01' +# Fnames: +# native: 'mom6.h.native.????-??.nc' +# static: 'mom6.h.static.nc' +# oce_cat: /glade/u/home/gmarques/libs/oce-catalogs/reference-datasets.yml ########### JUPYTER BOOK CONFIG ########### @@ -173,31 +182,35 @@ book_toc: # Parts group notebooks into different sections in the Jupyter book # table of contents, so you can organize different parts of your project. - - caption: Atmosphere + # - caption: Atmosphere - # Each chapter is the name of one of the notebooks that you executed - # in compute_notebooks above, also without .ipynb - chapters: - - file: atm/adf_quick_run + # # Each chapter is the name of one of the notebooks that you executed + # # in compute_notebooks above, also without .ipynb + # chapters: + # - file: atm/adf_quick_run - - caption: Ocean - chapters: - - file: ocn/ocean_surface + # - caption: Ocean + # chapters: + # - file: ocn/ocean_surface - - caption: Land - chapters: - - file: lnd/land_comparison + # - caption: Land + # chapters: + # - file: lnd/land_comparison + + # - caption: Sea Ice + # chapters: + # - file: ice/seaice - - caption: Sea Ice + - caption: Land Ice chapters: - - file: ice/seaice + - file: glc/LIWG_SMB_diagnostic ##################################### # Keys for Jupyter Book _config.yml # ##################################### book_config_keys: - title: Example project # Title of your jupyter book + title: CESM Key Metrics # Title of your jupyter book # Other keys can be added here, see https://jupyterbook.org/en/stable/customize/config.html ### for many more options diff --git a/examples/nblibrary/glc/LIWG_SMB_diagnostic.ipynb b/examples/nblibrary/glc/LIWG_SMB_diagnostic.ipynb new file mode 100644 index 0000000..64a356c --- /dev/null +++ b/examples/nblibrary/glc/LIWG_SMB_diagnostic.ipynb @@ -0,0 +1,494 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e45c723f-aa6f-4db4-a197-492132cc8156", + "metadata": {}, + "source": [ + "# Land ice SMB model comparison\n", + "This notebook compares the downscaled output of surface mass balance (SMB) over the Greenland ice sheet (GrIS) to the regional model MAR. In what follows, we interchangeably call the MAR data \"observation\".\n", + "\\\n", + "Note1: the MAR data are processed as a climatology spanning 1960-1999.\\\n", + "Note2: the MAR data are available at a uniform resolution of 1km using the same projection as the CISM grid. This notebook requires the interpolation of the MAR data on the CISM grid. The interpolation is done in this notebook (for now) to allow for the eventuality of the CISM grid or the MAR grid to change in the future. \\\n", + "creation: 05-26-24 \\\n", + "contact: Gunter Leguy (gunterl@ucar.edu)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81bedf90-288c-4cfa-add5-b199ca9bcf72", + "metadata": {}, + "outputs": [], + "source": [ + "# Import packages\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib.cm as mcm\n", + "from scipy.interpolate import RegularGridInterpolator\n", + "import xarray as xr\n", + "\n", + "import utils\n", + "\n", + "# to display figures in notebook after executing the code.\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "id": "24029ab9-fe52-4c7e-bb41-847c7f3a0b1d", + "metadata": {}, + "source": [ + "## Parameter configuration\n", + "\n", + "Some parameters are set in CUPiD's `config.yml` file,\n", + "others are derived from these parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e02f1300-e0e1-448c-b534-68146555d660", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [ + "parameters" + ] + }, + "outputs": [], + "source": [ + "# Parameter Defaults\n", + "\n", + "CESM_output_dir = \"/glade/campaign/cesm/development/cross-wg/diagnostic_framework/CESM_output_for_testing\"\n", + "case_name = \"b.e23_alpha17f.BLT1850.ne30_t232.092\" # case name\n", + "climo_nyears = 40 # number of years to compute the climatology\n", + "last_year = 101\n", + "\n", + "base_case_output_dir = CESM_output_dir\n", + "base_case_name = None\n", + "base_last_year = last_year\n", + "\n", + "obs_path = \"/glade/u/home/gunterl/obs_diagnostic_cesm\" # path to observed dataset\n", + "obs_name = \"GrIS_MARv3.12_climo_1960_1999.nc\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cef60ddb-9ff4-4a14-a8ea-0d5740b6c18a", + "metadata": {}, + "outputs": [], + "source": [ + "case_init_file = f\"{CESM_output_dir}/{case_name}/glc/hist/{case_name}.cism.gris.initial_hist.0001-01-01-00000.nc\" # name of glc file output\n", + "\n", + "case_path = f\"{CESM_output_dir}/{case_name}/cpl/hist\" # path to glc output\n", + "case_file = f\"{case_path}/{case_name}.cpl.hx.1yr2glc.{last_year:04d}-01-01-00000.nc\" # name of glc file output\n", + "obs_file = f\"{obs_path}/{obs_name}\" # name of observed dataset file\n", + "\n", + "if base_case_name:\n", + " base_case_path = (\n", + " f\"{base_case_output_dir}/{base_case_name}/cpl/hist\" # path to cpl output\n", + " )\n", + " base_file = f\"{base_case_path}/{base_case_name}.cpl.hx.1yr2glc.{base_last_year:04d}-01-01-00000.nc\" # name of last cpl simulation output" + ] + }, + { + "cell_type": "markdown", + "id": "70ee51c9-1c10-475d-a45b-b97583a3a5a9", + "metadata": {}, + "source": [ + "## Set up grid\n", + "\n", + "Read in the grid data, compute resolution and other grid-specific parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27373d08-084b-4c3f-8c3f-d5c8a445b2dc", + "metadata": {}, + "outputs": [], + "source": [ + "## Get grid from initial_hist stream\n", + "thk_init_da = xr.open_dataset(case_init_file).isel(time=0)[\"thk\"]\n", + "mask = thk_init_da.data[:, :] == 0\n", + "\n", + "# Shape of array is (ny, nx)\n", + "grid_dims = thk_init_da.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "05eee529-d42f-4872-8cf2-f484ca44bf3f", + "metadata": {}, + "outputs": [], + "source": [ + "# Constants\n", + "res = np.abs(\n", + " thk_init_da[\"x1\"].data[1] - thk_init_da[\"x1\"].data[0]\n", + ") # CISM output resolution\n", + "\n", + "rhow = 1000 # water density kg/m3\n", + "kg_to_Gt = 1e-12 # Converting kg to Gt\n", + "mm_to_Gt = rhow * 1e-3 * res**2 * kg_to_Gt # converting mm/yr to Gt/yr" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "44eda4c4-3c23-450d-83d8-c76204cefdc4", + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"grid_dims\": grid_dims,\n", + " \"climo_nyears\": climo_nyears,\n", + " \"mm_to_Gt\": mm_to_Gt,\n", + " \"mask\": mask,\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "68fca423-582b-4179-8771-16250a5f1904", + "metadata": {}, + "source": [ + "## Make datasets\n", + "\n", + "Read in observations and CESM output.\n", + "Also do necessary computations\n", + "(global mean for time series, temporal mean for climatology)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb81be67-98d6-4924-a90e-930d9b2caed8", + "metadata": {}, + "outputs": [], + "source": [ + "# creating the SMB climatology for new case\n", + "smb_case = utils.read_cesm_smb(case_path, case_name, last_year, params)\n", + "smb_case_climo = smb_case.mean(\"time\")\n", + "\n", + "# creating the SMB climatology for base_case\n", + "if base_case_name:\n", + " smb_base_case = utils.read_cesm_smb(\n", + " base_case_path, base_case_name, base_last_year, params\n", + " )\n", + " smb_base_climo = smb_base_case.mean(\"time\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac96bb16-7bd8-4d7b-b00b-d315feeb1a5d", + "metadata": {}, + "outputs": [], + "source": [ + "# Interpolating the observed data onto the CISM grid\n", + "smb_obs_da = xr.open_dataset(obs_file).isel(time=0)[\"SMB\"]\n", + "\n", + "# Defining the interpolation functions\n", + "myInterpFunction_smb_obs = RegularGridInterpolator(\n", + " (smb_obs_da[\"x\"].data, smb_obs_da[\"y\"].data),\n", + " smb_obs_da.data.transpose(),\n", + " method=\"linear\",\n", + " bounds_error=False,\n", + " fill_value=None,\n", + ")\n", + "\n", + "# Initializing the glacier ID variable\n", + "smb_obs_climo = xr.DataArray(np.zeros(grid_dims), dims=[\"glc1Exp_ny\", \"glc1Exp_nx\"])\n", + "\n", + "# Performing the interpolation\n", + "for j in range(grid_dims[0]):\n", + " point_y = np.zeros(grid_dims[1])\n", + " point_y[:] = thk_init_da[\"y1\"].data[j]\n", + " pts = (thk_init_da[\"x1\"].data[:], point_y[:])\n", + " smb_obs_climo.data[j, :] = myInterpFunction_smb_obs(pts)\n", + "\n", + "# Filtering out fill values\n", + "smb_obs_climo.data = np.where(\n", + " np.logical_or(mask, smb_obs_climo > 1e20), 0, smb_obs_climo\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a38682c9-dc87-4d7b-887d-8abbbe8a7265", + "metadata": {}, + "outputs": [], + "source": [ + "# Integrated SMB time series\n", + "first_year = last_year - len(smb_case[\"time\"]) + 1\n", + "avg_smb_case_climo = smb_case.sum([\"glc1Exp_ny\", \"glc1Exp_nx\"]) * params[\"mm_to_Gt\"]\n", + "\n", + "if base_case_name:\n", + " base_first_year = base_last_year - len(smb_base_case[\"time\"]) + 1\n", + " avg_smb_base_case_climo = (\n", + " smb_base_case.sum([\"glc1Exp_ny\", \"glc1Exp_nx\"]) * params[\"mm_to_Gt\"]\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "1641747b-4997-45ad-bf70-981ed97688dd", + "metadata": {}, + "source": [ + "## Generate plots\n", + "\n", + "Map comparing CESM to observation,\n", + "possibly map comparing CESM to older case,\n", + "and time series of spatial mean SMB." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c7973cfe-64e0-47d4-a1b6-73cd9e62fdb2", + "metadata": {}, + "outputs": [], + "source": [ + "# Comparing SMB new run vs obs\n", + "\n", + "# Colormap choice\n", + "my_cmap = mcm.get_cmap(\"Spectral\")\n", + "my_cmap_diff = mcm.get_cmap(\"bwr_r\")\n", + "\n", + "\n", + "# Colorbar bounds\n", + "vmin = -2000\n", + "vmax = 2000\n", + "\n", + "# Figure\n", + "fig, ax = plt.subplots(1, 3, sharey=True, figsize=[22, 9])\n", + "\n", + "## Left panel\n", + "utils.plot_contour(\n", + " smb_case_climo,\n", + " fig,\n", + " ax[0],\n", + " 0.35,\n", + " f\"{case_name}\\nSMB (mm/y w.e.)\\nMean from {first_year:04d} - {last_year:04d}\",\n", + " vmin,\n", + " vmax,\n", + " my_cmap,\n", + " mm_to_Gt,\n", + ")\n", + "\n", + "## Center panel\n", + "utils.plot_contour(\n", + " smb_obs_climo,\n", + " fig,\n", + " ax[1],\n", + " 0.35,\n", + " \"SMB Obs\\n(mm/y w.e.)\",\n", + " vmin,\n", + " vmax,\n", + " my_cmap,\n", + " mm_to_Gt,\n", + ")\n", + "\n", + "## Right panel\n", + "utils.plot_contour(\n", + " smb_case_climo - smb_obs_climo,\n", + " fig,\n", + " ax[2],\n", + " 0.89,\n", + " \"SMB bias (mm/yr w.e.)\",\n", + " vmin,\n", + " vmax,\n", + " my_cmap_diff,\n", + " mm_to_Gt,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "176594bc-53a1-4934-8210-7aa7d62f5659", + "metadata": {}, + "outputs": [], + "source": [ + "# Comparing SMB new run vs base case\n", + "if base_case_name:\n", + " # Colormap choice\n", + " my_cmap = mcm.get_cmap(\"Spectral\")\n", + " my_cmap_diff = mcm.get_cmap(\"bwr_r\")\n", + "\n", + " # Colorbar bounds\n", + " vmin = -2000\n", + " vmax = 2000\n", + "\n", + " # Figure\n", + " fig, ax = plt.subplots(1, 3, sharey=True, figsize=[22, 9])\n", + "\n", + " ## Left panel\n", + " utils.plot_contour(\n", + " smb_case_climo,\n", + " fig,\n", + " ax[0],\n", + " 0.35,\n", + " f\"{case_name}\\nSMB (mm/y w.e.)\\nMean from {first_year:04d} - {last_year:04d}\",\n", + " vmin,\n", + " vmax,\n", + " my_cmap,\n", + " mm_to_Gt,\n", + " )\n", + "\n", + " ## Center panel\n", + " utils.plot_contour(\n", + " smb_base_climo,\n", + " fig,\n", + " ax[1],\n", + " 0.35,\n", + " f\"{base_case_name}\\nSMB (mm/y w.e.)\\nMean from {base_first_year:04d} - {base_last_year:04d}\",\n", + " vmin,\n", + " vmax,\n", + " my_cmap,\n", + " mm_to_Gt,\n", + " )\n", + "\n", + " ## Right panel\n", + " utils.plot_contour(\n", + " smb_case_climo - smb_base_climo,\n", + " fig,\n", + " ax[2],\n", + " 0.89,\n", + " \"SMB difference (mm/yr w.e.)\",\n", + " vmin,\n", + " vmax,\n", + " my_cmap_diff,\n", + " mm_to_Gt,\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "01af1cd1-0351-452c-99e7-125546469f69", + "metadata": {}, + "outputs": [], + "source": [ + "# Plotting the SMB spatially averaged time series\n", + "\n", + "# TODO: include base case, base case climo (horizontal line), new case, new case climo, and obs climo\n", + "# Note: base case is 10 years of historical, new case is PI.\n", + "# what comparisons make sense when base case is HIST and new case is 1850?\n", + "\n", + "\n", + "time = np.arange(first_year, last_year + 1)\n", + "if base_case_name:\n", + " base_time = (\n", + " np.arange(base_first_year, base_last_year + 1) + last_year - base_last_year\n", + " )\n", + " base_nt = len(base_time)\n", + "nt = len(time)\n", + "\n", + "avg_smb_obs_timeseries = np.zeros(nt)\n", + "avg_smb_case_timeseries = np.zeros(nt)\n", + "if base_case_name:\n", + " avg_smb_base_timeseries = np.zeros(base_nt)\n", + "\n", + "avg_smb_obs_timeseries[:] = np.round(smb_obs_climo.sum() * mm_to_Gt, 2)\n", + "avg_smb_case_timeseries[:] = np.round(smb_case_climo.sum() * mm_to_Gt, 2)\n", + "if base_case_name:\n", + " avg_smb_base_timeseries[:] = np.round(smb_base_climo.sum() * mm_to_Gt, 2)\n", + "\n", + "\n", + "x_ticks = np.arange(first_year, last_year + 2, 5)\n", + "tickx = x_ticks\n", + "\n", + "ymin = 100\n", + "ymax = 600\n", + "y_step = 50\n", + "y_ticks = np.arange(ymin, ymax + y_step, y_step)\n", + "\n", + "\n", + "plt.figure(figsize=(16, 7))\n", + "\n", + "# Plotting annual / spatial means\n", + "plt.subplot(111)\n", + "utils.plot_line(\n", + " avg_smb_case_climo,\n", + " time,\n", + " line=\"-\",\n", + " color=\"blue\",\n", + " label=f\"{case_name} ({first_year:04d} - {last_year:04d})\",\n", + " linewidth=2,\n", + ")\n", + "utils.plot_line(\n", + " avg_smb_case_timeseries[:],\n", + " time,\n", + " line=\":\",\n", + " color=\"blue\",\n", + " label=f\"{case_name} (mean from {first_year:04d} - {last_year:04d})\",\n", + " linewidth=2,\n", + ")\n", + "if base_case_name:\n", + " utils.plot_line(\n", + " avg_smb_base_case_climo,\n", + " base_time,\n", + " line=\"-\",\n", + " color=\"red\",\n", + " label=f\"{base_case_name} ({base_first_year:04d} - {base_last_year:04d})\",\n", + " linewidth=2,\n", + " )\n", + " utils.plot_line(\n", + " avg_smb_base_timeseries[:],\n", + " base_time,\n", + " line=\":\",\n", + " color=\"red\",\n", + " label=f\"{base_case_name} (mean from {base_first_year:04d} - {base_last_year:04d})\",\n", + " linewidth=2,\n", + " )\n", + "utils.plot_line(\n", + " avg_smb_obs_timeseries[:],\n", + " time,\n", + " line=\"-\",\n", + " color=\"black\",\n", + " label=\"Observations (mean)\",\n", + " linewidth=2,\n", + ")\n", + "\n", + "sizefont = 16\n", + "plt.xlim([first_year, last_year])\n", + "plt.xticks(x_ticks, tickx, fontsize=sizefont)\n", + "plt.xlabel(r\"$Time$ (y)\", fontsize=sizefont)\n", + "plt.ylabel(\"SMB average evolution (Gt/yr)\", multialignment=\"center\", fontsize=sizefont)\n", + "plt.ylim([ymin, ymax])\n", + "plt.yticks(fontsize=sizefont)\n", + "plt.legend(loc=\"upper left\", ncol=1, frameon=True, borderaxespad=0)\n", + "\n", + "plt.title(\"SMB average evolution\", fontsize=sizefont);" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:cupid-analysis]", + "language": "python", + "name": "conda-env-cupid-analysis-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.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/nblibrary/glc/utils.py b/examples/nblibrary/glc/utils.py new file mode 100644 index 0000000..2a11bc3 --- /dev/null +++ b/examples/nblibrary/glc/utils.py @@ -0,0 +1,118 @@ +from __future__ import annotations + +import os + +import numpy as np +import xarray as xr +from matplotlib import pyplot as plt + + +def read_cesm_smb(path, case_name, last_year, params): + """ + This function reads CESM coupler history files and returns + an xarray DataArray containing surface mass balance in units mm/y + """ + # Set parameters + rhoi = 917 # ice density kg/m3 + sec_in_yr = 60 * 60 * 24 * 365 # seconds in a year + smb_convert = sec_in_yr / rhoi * 1000 # converting kg m-2 s-1 ice to mm y-1 w.e. + + filenames = [] + for k in range(params["climo_nyears"]): + + year_to_read = last_year - k + filename = ( + f"{path}/{case_name}.cpl.hx.1yr2glc.{year_to_read:04d}-01-01-00000.nc" + ) + + if not os.path.isfile(filename): + print(f"The couple file for time {year_to_read} does not exist.") + print( + "We will only use the files that existed until now to create the SMB climatology.", + ) + break + + filenames.append(filename) + + climo_out = ( + xr.open_mfdataset(filenames)["glc1Exp_Flgl_qice"].compute() * smb_convert + ) + # Mask out data that is 0 in initial condition + for k in range(len(climo_out["time"])): + climo_out.data[k, :, :] = np.where( + params["mask"], + 0, + climo_out.isel(time=k).data, + ) + print("number of years used in climatology = ", len(climo_out["time"])) + return climo_out + + +# -------------------- # +# PLOTTING FUNCTIONS # +# -------------------- # + + +def set_plot_prop_clean(ax): + """ + This function cleans up the figures from unnecessary default figure properties. + """ + ax.invert_yaxis() + ax.set_xlabel("") + ax.set_ylabel("") + ax.set_xticklabels("") + ax.set_yticklabels("") + ax.set_xticks([]) + ax.set_yticks([]) + + +def plot_contour(da, fig, ax, left, title, vmin, vmax, cmap, mm_to_Gt): + """ + Plot a contour map of surface mass balance (assumed to be in da.data). + Also computes global mean, in Gt, and prints average in lower left corner. + Arguments: + da - xr.DataArray containing SMB in units of mm/yr + fig - matplotlib.figure.Figure + ax - matplotlib.axes.Axes + left - left dimension of rect (dimensions for colorbar) + title - string containing title of plot + vmin - minimum value for contours + vmax - maximum value for contours + cmap - matplotlib.colors.Colormap + mm_to_Gt - conversion factor for mm/yr -> Gt/yr + """ + avg_data = np.round(da.sum().data * mm_to_Gt, 2) + last_panel = ax.imshow(da.data[:, :], vmin=vmin, vmax=vmax, cmap=cmap) + ax.set_title(title, fontsize=16) + set_plot_prop_clean(ax) + ax.annotate("net avg =" + str(avg_data) + " Gt/yr", xy=(5, 5), fontsize=16) + + pos = ax.get_position() + cax = fig.add_axes([left, pos.y0, 0.02, pos.y1 - pos.y0]) + + cbar = fig.colorbar(last_panel, cax=cax) + cbar.ax.tick_params(labelsize=16) + + +def plot_line(da, time, line, color, label, linewidth): + """ + Plot a time series of spatially averaged surface mass balance (assumed to + be in da.data). + Arguments: + da - xr.DataArray containing spatially averaged SMB in units of Gt/yr + time - np.array containing time dimension + line - style of line to use in plot + color - color of line in plot + label - label of line in legend + linewidth - thickness of line in plot + """ + plt.plot( + time, + da, + line, + ms=3, + mfc=color, + color=color, + label=label, + linewidth=linewidth, + )