Skip to article frontmatterSkip to article content

Calculate Surface Ocean Heat using CESM2 LENS data


Overview

  • This notebook is an adpation of a workflow in the NCAR gallery of the Pangeo collection
  • This notebook illustrates how to compute surface ocean heat content using potential temperature data from CESM2 Large Ensemble Dataset (Community Earth System Model 2) hosted on NCAR’s RDA.
  • This data is open access and is accessed via OSDF using the pelicanFS package and demonstrates how you can stream data from NCAR’s RDA
  • Please refer to the first chapter of this cookbook to learn more about OSDF, pelican or pelicanFS

Prerequisites

ConceptsImportanceNotes
Intro to Intake-ESMNecessaryUsed for searching CMIP6 data
Understanding of ZarrHelpfulFamiliarity with metadata structure
MatplotlibHelpfulPackage used for plotting
PelicanFSNecessaryThe python package used to stream data in this notebook
OSDFHelpfulOSDF is used to stream data in this notebook
  • Time to learn: 20 mins

Imports

import intake
import numpy as np
import pandas as pd
import xarray as xr
import seaborn as sns
import re
import matplotlib.pyplot as plt
import dask
from dask.distributed import LocalCluster
import pelicanfs 
import cf_units as cf
# Load Catalog URL
cat_url = 'https://stratus.rda.ucar.edu/d010092/catalogs/d010092-osdf-zarr-gdex.json'

Set up local dask cluster

Before we do any computation let us first set up a local cluster using dask

cluster = LocalCluster()          
client = cluster.get_client()
# Scale the cluster
n_workers = 5
cluster.scale(n_workers)
cluster
Loading...

Data Loading

Load CESM2 LENS data from NCAR’s RDA

col = intake.open_esm_datastore(cat_url)
col
Loading...
# Uncomment this line to see all the variables
# cesm_cat.df['variable'].values
cesm_temp = col.search(variable ='TEMP', frequency ='monthly')
cesm_temp
Loading...
cesm_temp.df['path'].values
array(['https://data-osdf.rda.ucar.edu/ncar-rda/d010092/ocn/monthly/cesm2LE-historical-cmip6-TEMP.zarr', 'https://data-osdf.rda.ucar.edu/ncar-rda/d010092/ocn/monthly/cesm2LE-ssp370-cmip6-TEMP.zarr', 'https://data-osdf.rda.ucar.edu/ncar-rda/d010092/ocn/monthly/cesm2LE-ssp370-smbb-TEMP.zarr'], dtype=object)
dsets_cesm = cesm_temp.to_dataset_dict()
Loading...
dsets_cesm.keys()
dict_keys(['ocn.ssp370.monthly.cmip6', 'ocn.historical.monthly.cmip6', 'ocn.ssp370.monthly.smbb'])
historical       = dsets_cesm['ocn.historical.monthly.cmip6']
future_smbb      = dsets_cesm['ocn.ssp370.monthly.smbb']
future_cmip6     = dsets_cesm['ocn.ssp370.monthly.cmip6']

Change units

orig_units = cf.Unit(historical.z_t.attrs['units'])
orig_units
Unit('centimeters')
def change_units(ds, variable_str, variable_bounds_str, target_unit_str):
    orig_units = cf.Unit(ds[variable_str].attrs['units'])
    target_units = cf.Unit(target_unit_str)
    variable_in_new_units = xr.apply_ufunc(orig_units.convert, ds[variable_bounds_str], target_units, dask='parallelized', output_dtypes=[ds[variable_bounds_str].dtype])
    return variable_in_new_units
depth_levels_in_m = change_units(historical, 'z_t', 'z_t', 'm')
hist_temp_in_degK = change_units(historical, 'TEMP', 'TEMP', 'degK')
fut_cmip6_temp_in_degK = change_units(future_cmip6, 'TEMP', 'TEMP', 'degK')
fut_smbb_temp_in_degK = change_units(future_smbb, 'TEMP', 'TEMP', 'degK')
#
hist_temp_in_degK  = hist_temp_in_degK.assign_coords(z_t=("z_t", depth_levels_in_m['z_t'].data))
hist_temp_in_degK["z_t"].attrs["units"] = "m"
hist_temp_in_degK
Loading...
depth_levels_in_m.isel(z_t=slice(0, -1))
Loading...
  • Compute depth level deltas using the difference of z_t levels
depth_level_deltas = depth_levels_in_m.isel(z_t=slice(1, None)).values - depth_levels_in_m.isel(z_t=slice(0, -1)).values
# Optionally, if you want to keep it as an xarray DataArray, re-wrap the result
depth_level_deltas = xr.DataArray(depth_level_deltas, dims=["z_t"], coords={"z_t": depth_levels_in_m.z_t.isel(z_t=slice(0, -1))})
depth_level_deltas  
Loading...

Compute Ocean Heat content for ocean surface

  • Ocean surface is considered to be the top 100m
  • The formula for this is:
    H=ρC0zT(z)dzH = \rho C \int_0^z T(z) dz

Where H is ocean heat content, the value we are trying to calculate,

ρ\rho is the density of sea water, 1026kg/m31026 kg/m^3 ,

CC is the specific heat of sea water, 3990J/(kgK)3990 J/(kg K) ,

zz is the depth limit of the calculation in meters,

and T(z)T(z) is the temperature at each depth in degrees Kelvin.

def calc_ocean_heat(delta_level, temperature):
    rho = 1026 #kg/m^3
    c_p = 3990 #J/(kg K)
    weighted_temperature = delta_level * temperature
    heat = weighted_temperature.sum(dim="z_t")*rho*c_p
    return heat
# Remember that the coordinate z_t still has values in cm
hist_temp_ocean_surface = hist_temp_in_degK.where(hist_temp_in_degK['z_t'] < 1e4,drop=True)
hist_temp_ocean_surface
Loading...
depth_level_deltas_surface = depth_level_deltas.where(depth_level_deltas['z_t'] <1e4, drop= True)
depth_level_deltas_surface
Loading...
hist_ocean_heat = calc_ocean_heat(depth_level_deltas_surface,hist_temp_ocean_surface)
hist_ocean_heat
Loading...

Plot Ocean Heat

%%time
# Compute annual and ensemble mean
hist_oceanheat_ann_mean = hist_ocean_heat.mean('member_id').groupby('time.year').mean()
hist_oceanheat_ann_mean 
Loading...
hist_oceanheat_ano = \
hist_oceanheat_ann_mean.sel(year=2014) - hist_oceanheat_ann_mean.sel(year=1850)
%%time
hist_oceanheat_ano.plot()
CPU times: user 10.8 s, sys: 2.82 s, total: 13.6 s
Wall time: 2min 32s
<Figure size 640x480 with 2 Axes>

Indeed! The surface ocean is trapping heat as the globe warms!


Summary

In this notebook we used sea temperature data from the Community Earth System Model (CESM2) Large Ensemble dataset to compute surface ocean heat and convince ourselves that the surface ocean is trapping extra heat as the globe warms. We used an intake-ESM catalog backed by pelican links to stream data from NCAR’s Research Data Archive via NCAR’s OSDF origin!

What’s next?

In the next notebook, we will see how to load data from multiple OSDF origins into a workflow. We will stream CMIP6 model data from AWS and observational data from RDA.

Resources and references