{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "182a8d3a-09f8-448e-a029-d03db69bbe44", "metadata": {}, "outputs": [], "source": [ "%load_ext autoreload\n", "%autoreload 2" ] }, { "cell_type": "code", "execution_count": 2, "id": "a5f0eef4-7368-4f12-bc35-e09387046539", "metadata": {}, "outputs": [], "source": [ "import xarray as xr\n", "import xgcm\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "id": "b623a0a5-dcd2-4884-8c85-db861506a60e", "metadata": {}, "source": [ "# Tutorial: analyzing finite-volume ocean model budgets with `xbudget`\n", "\n", "This tutorial shows how to use `xbudget` to close and manipulate mass and tracer budgets in finite-volume. " ] }, { "cell_type": "markdown", "id": "2f73cabd-5dab-44f4-91de-9d8c6a0d6f92", "metadata": {}, "source": [ "### Load example MOM6 dataset from Zenodo" ] }, { "cell_type": "code", "execution_count": 3, "id": "5aa1b5c9-60d4-4b5e-9c7d-d0c25efb5093", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "File 'MOM6_global_example_diagnostics_zlevels_v0_0_6.nc' already exists at ../data/MOM6_global_example_diagnostics_zlevels_v0_0_6.nc. Skipping download.\n" ] } ], "source": [ "from load_example_model_grid import load_MOM6_coarsened_diagnostics\n", "grid = load_MOM6_coarsened_diagnostics()" ] }, { "cell_type": "markdown", "id": "4bbb04e1-2137-4174-8654-f78a55e2342d", "metadata": {}, "source": [ "### Load budget metadata from preset MOM6 dictionary" ] }, { "cell_type": "code", "execution_count": 4, "id": "449ee6d3-57ca-4e3d-94dc-076887309454", "metadata": {}, "outputs": [], "source": [ "import xbudget\n", "xbudget_dict = xbudget.load_preset_budget(model=\"MOM6\").copy()" ] }, { "cell_type": "markdown", "id": "c0625069-93c7-4fc1-bec0-d6c543a4412b", "metadata": {}, "source": [ "We load in the file `xbudget/conventions/MOM6.yaml`, which comprehensively details the mass, heat, and salt budgets in MOM6, and how they can be evaluated using various combinations of diagnostics. In MOM6, most mass and tracer diagnostics are output as thickness tendencies and thickness-weighted tracer tendencies, respectively, and must be converted into density-weighted integrals over a finite-volume cell by multiplying by a seawater density and grid cell area.\n", "\n", "For example, consider the structure of the heat budget:" ] }, { "cell_type": "code", "execution_count": 5, "id": "2d158238-b8a5-4fe5-8a59-7198d9e759ce", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\n", " \"lambda\": \"thetao\",\n", " \"lhs\": {\n", " \"sum\": {\n", " \"Eulerian_tendency\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"opottemptend\",\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"advection\": {\n", " \"sum\": {\n", " \"interfacial\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"sign\": -1.0,\n", " \"tracer_content_tendency_per_unit_area\": \"Th_tendency_vert_remap\",\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"lateral\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"sign\": -1.0,\n", " \"tracer_content_tendency_per_unit_area\": \"T_advection_xy\",\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"surface_ocean_flux_advective_negative_lhs\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"density\": 1035.0,\n", " \"lambda_mass\": \"tos\",\n", " \"sign\": -1.0,\n", " \"specific_heat_capacity\": 3992.0,\n", " \"thickness_tendency\": \"boundary_forcing_h_tendency\",\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"rhs\": {\n", " \"sum\": {\n", " \"bottom_flux\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"internal_heat_heat_tendency\",\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"diffusion\": {\n", " \"sum\": {\n", " \"interfacial\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"opottempdiff\",\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"lateral\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"opottemppmdiff\",\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"frazil_ice\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"frazil_heat_tendency\",\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"surface_exchange_flux\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"boundary_forcing_heat_tendency\",\n", " \"var\": null\n", " },\n", " \"sum\": {\n", " \"advective\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"heat_content_surfwater\",\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"nonadvective\": {\n", " \"sum\": {\n", " \"latent\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"hflso\",\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"longwave\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"rlntds\",\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"sensible\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"hfsso\",\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"shortwave\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"rsdoabsorb\",\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"surface_ocean_flux_advective_negative_rhs\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"density\": 1035.0,\n", " \"lambda_mass\": \"tos\",\n", " \"sign\": -1.0,\n", " \"specific_heat_capacity\": 3992.0,\n", " \"thickness_tendency\": \"boundary_forcing_h_tendency\",\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"var\": null\n", " },\n", " \"surface_lambda\": \"tos\"\n", "}\n" ] } ], "source": [ "import json\n", "print(json.dumps(xbudget_dict['heat'], sort_keys=True, indent=4))" ] }, { "cell_type": "markdown", "id": "fbbcd656-22be-44b3-9a79-412f4bd03d86", "metadata": {}, "source": [ "The `var: null` entries in the dictionary denote where diagnostics are not directly available. In many cases, however, they can be reconstructed by taking the sum or product of available diagnostics.\n", "\n", "We can use the `xbudget.collect_budgets` function to leverage the information in this metadata dictionary to fill in all of these gaps in the budget." ] }, { "cell_type": "code", "execution_count": 6, "id": "527f1b10", "metadata": {}, "outputs": [], "source": [ "xbudget.collect_budgets(grid, xbudget_dict)" ] }, { "cell_type": "markdown", "id": "b70229ef-846f-4764-816f-e7bb9ce0268d", "metadata": {}, "source": [ " All of these reconstructed budget terms are also automatically (and lazily) added to the original dataset with a straight-forward naming convention, so they can be used directly to evaluate the budget." ] }, { "cell_type": "code", "execution_count": 7, "id": "aa8797da-0f49-40aa-be98-b03ba49b380a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\n", " \"lambda\": \"thetao\",\n", " \"lhs\": {\n", " \"sum\": {\n", " \"Eulerian_tendency\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"opottemptend\",\n", " \"var\": \"heat_lhs_sum_Eulerian_tendency_product\"\n", " },\n", " \"var\": \"heat_lhs_sum_Eulerian_tendency\"\n", " },\n", " \"advection\": {\n", " \"sum\": {\n", " \"interfacial\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"sign\": -1.0,\n", " \"tracer_content_tendency_per_unit_area\": \"Th_tendency_vert_remap\",\n", " \"var\": \"heat_lhs_sum_advection_sum_interfacial_product\"\n", " },\n", " \"var\": \"heat_lhs_sum_advection_sum_interfacial\"\n", " },\n", " \"lateral\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"sign\": -1.0,\n", " \"tracer_content_tendency_per_unit_area\": \"T_advection_xy\",\n", " \"var\": \"heat_lhs_sum_advection_sum_lateral_product\"\n", " },\n", " \"var\": \"heat_lhs_sum_advection_sum_lateral\"\n", " },\n", " \"var\": \"heat_lhs_sum_advection_sum\"\n", " },\n", " \"var\": \"heat_lhs_sum_advection\"\n", " },\n", " \"surface_ocean_flux_advective_negative_lhs\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"density\": 1035.0,\n", " \"lambda_mass\": \"tos\",\n", " \"sign\": -1.0,\n", " \"specific_heat_capacity\": 3992.0,\n", " \"thickness_tendency\": \"boundary_forcing_h_tendency\",\n", " \"var\": \"heat_lhs_sum_surface_ocean_flux_advective_negative_lhs_product\"\n", " },\n", " \"var\": \"heat_lhs_sum_surface_ocean_flux_advective_negative_lhs\"\n", " },\n", " \"var\": \"heat_lhs_sum\"\n", " },\n", " \"var\": \"heat_lhs\"\n", " },\n", " \"rhs\": {\n", " \"sum\": {\n", " \"bottom_flux\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"internal_heat_heat_tendency\",\n", " \"var\": \"heat_rhs_sum_bottom_flux_product\"\n", " },\n", " \"var\": \"heat_rhs_sum_bottom_flux\"\n", " },\n", " \"diffusion\": {\n", " \"sum\": {\n", " \"interfacial\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"opottempdiff\",\n", " \"var\": \"heat_rhs_sum_diffusion_sum_interfacial_product\"\n", " },\n", " \"var\": \"heat_rhs_sum_diffusion_sum_interfacial\"\n", " },\n", " \"lateral\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"opottemppmdiff\",\n", " \"var\": \"heat_rhs_sum_diffusion_sum_lateral_product\"\n", " },\n", " \"var\": \"heat_rhs_sum_diffusion_sum_lateral\"\n", " },\n", " \"var\": \"heat_rhs_sum_diffusion_sum\"\n", " },\n", " \"var\": \"heat_rhs_sum_diffusion\"\n", " },\n", " \"frazil_ice\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"frazil_heat_tendency\",\n", " \"var\": \"heat_rhs_sum_frazil_ice_product\"\n", " },\n", " \"var\": \"heat_rhs_sum_frazil_ice\"\n", " },\n", " \"surface_exchange_flux\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"boundary_forcing_heat_tendency\",\n", " \"var\": \"heat_rhs_sum_surface_exchange_flux_product\"\n", " },\n", " \"sum\": {\n", " \"advective\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"heat_content_surfwater\",\n", " \"var\": \"heat_rhs_sum_surface_exchange_flux_sum_advective_product\"\n", " },\n", " \"var\": \"heat_rhs_sum_surface_exchange_flux_sum_advective\"\n", " },\n", " \"nonadvective\": {\n", " \"sum\": {\n", " \"latent\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"hflso\",\n", " \"var\": \"heat_rhs_sum_surface_exchange_flux_sum_nonadvective_sum_latent_product\"\n", " },\n", " \"var\": \"heat_rhs_sum_surface_exchange_flux_sum_nonadvective_sum_latent\"\n", " },\n", " \"longwave\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"rlntds\",\n", " \"var\": \"heat_rhs_sum_surface_exchange_flux_sum_nonadvective_sum_longwave_product\"\n", " },\n", " \"var\": \"heat_rhs_sum_surface_exchange_flux_sum_nonadvective_sum_longwave\"\n", " },\n", " \"sensible\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"hfsso\",\n", " \"var\": \"heat_rhs_sum_surface_exchange_flux_sum_nonadvective_sum_sensible_product\"\n", " },\n", " \"var\": \"heat_rhs_sum_surface_exchange_flux_sum_nonadvective_sum_sensible\"\n", " },\n", " \"shortwave\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"tracer_content_tendency_per_unit_area\": \"rsdoabsorb\",\n", " \"var\": \"heat_rhs_sum_surface_exchange_flux_sum_nonadvective_sum_shortwave_product\"\n", " },\n", " \"var\": \"heat_rhs_sum_surface_exchange_flux_sum_nonadvective_sum_shortwave\"\n", " },\n", " \"var\": \"heat_rhs_sum_surface_exchange_flux_sum_nonadvective_sum\"\n", " },\n", " \"var\": \"heat_rhs_sum_surface_exchange_flux_sum_nonadvective\"\n", " },\n", " \"var\": \"heat_rhs_sum_surface_exchange_flux_sum\"\n", " },\n", " \"var\": \"heat_rhs_sum_surface_exchange_flux\"\n", " },\n", " \"surface_ocean_flux_advective_negative_rhs\": {\n", " \"product\": {\n", " \"area\": \"areacello\",\n", " \"density\": 1035.0,\n", " \"lambda_mass\": \"tos\",\n", " \"sign\": -1.0,\n", " \"specific_heat_capacity\": 3992.0,\n", " \"thickness_tendency\": \"boundary_forcing_h_tendency\",\n", " \"var\": \"heat_rhs_sum_surface_ocean_flux_advective_negative_rhs_product\"\n", " },\n", " \"var\": \"heat_rhs_sum_surface_ocean_flux_advective_negative_rhs\"\n", " },\n", " \"var\": \"heat_rhs_sum\"\n", " },\n", " \"var\": \"heat_rhs\"\n", " },\n", " \"surface_lambda\": \"tos\"\n", "}\n" ] } ], "source": [ "import json\n", "print(json.dumps(xbudget_dict['heat'], sort_keys=True, indent=4))" ] }, { "cell_type": "markdown", "id": "b97ed161-a913-4239-8f85-4b3f12e7cf3f", "metadata": {}, "source": [ "Because searching through this data structure by eye can be tedious, we include helpers functions to help retrieve the variable name corresponding to each term in the budget." ] }, { "cell_type": "code", "execution_count": 8, "id": "7c9bfd4c-aaff-44ca-b360-d3b43f3addf1", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
<xarray.DataArray 'heat_lhs_sum_advection' (time: 1, z_l: 35, yh: 180, xh: 240)> Size: 12MB\n",
"array([[[[0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" ...,\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.]],\n",
"\n",
" [[0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" ...,\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.]],\n",
"\n",
" [[0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" ...,\n",
"...\n",
" ...,\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.]],\n",
"\n",
" [[0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" ...,\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.]],\n",
"\n",
" [[0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" ...,\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.],\n",
" [0., 0., 0., ..., 0., 0., 0.]]]], shape=(1, 35, 180, 240))\n",
"Coordinates:\n",
" * time (time) object 8B 2000-07-01 00:00:00\n",
" * z_l (z_l) float64 280B 2.5 10.0 20.0 32.5 ... 5.5e+03 6e+03 6.5e+03\n",
" * yh (yh) int64 1kB 0 1 2 3 4 5 6 7 ... 173 174 175 176 177 178 179\n",
" * xh (xh) int64 2kB 0 1 2 3 4 5 6 7 ... 233 234 235 236 237 238 239\n",
" geolon (yh, xh) float64 346kB ...\n",
" lon (yh, xh) float64 346kB ...\n",
" geolat (yh, xh) float64 346kB ...\n",
" lat (yh, xh) float64 346kB ...\n",
" deptho (yh, xh) float32 173kB ...\n",
" wet (yh, xh) float32 173kB ...\n",
" areacello (yh, xh) float64 346kB ...\n",
"Attributes:\n",
" cell_measures: volume: volcello area: areacello\n",
" time_avg_info: average_T1,average_T2,average_DT\n",
" standard_name: cell_area\n",
" note: We ignore land cells in partially wet cells when coarseni...\n",
" provenance: heat_lhs_sum_advection_sum