Using xgcm.transform to interpolate to isopycnal space#

%load_ext watermark

import numpy as np
import xarray as xr
import xgcm

import pop_tools

%watermark -iv
pop_tools: 0.0.post50+dirty
xarray   : 2024.7.0
xgcm     : 0.8.1
numpy    : 2.0.2

Load data#

Read an existing sample dataset for the tropical pacific. Unfortunately, this particular subset does not have SALT, so for demonstration purposes we set SALT=35.

# open sample data
filepath = pop_tools.DATASETS.fetch('Pac_POP0.1_JRA_IAF_1993-12-6-test.nc')
ds = xr.open_dataset(filepath)

# get DZU and DZT, needed for operations later on
filepath_g = pop_tools.DATASETS.fetch("Pac_grid_pbc_1301x305x62.tx01_62l.2013-07-13.nc")
ds_g = xr.open_dataset(filepath_g)
ds.update(ds_g[["DZU", "DZT"]])

# There is no salinity, so let's  create a fake SALT variable
ds["SALT"] = 35 * xr.ones_like(ds.TEMP)
ds.SALT.attrs["grid_loc"] = ds.TEMP.attrs["grid_loc"]
ds
Downloading file 'Pac_POP0.1_JRA_IAF_1993-12-6-test.nc' from 'https://ftp.cgd.ucar.edu/archive/aletheia-data/cesm-data/ocn/Pac_POP0.1_JRA_IAF_1993-12-6-test.nc' to '/home/docs/.pop_tools/data'.
Downloading file 'Pac_grid_pbc_1301x305x62.tx01_62l.2013-07-13.nc' from 'https://ftp.cgd.ucar.edu/archive/aletheia-data/cesm-data/ocn/Pac_grid_pbc_1301x305x62.tx01_62l.2013-07-13.nc' to '/home/docs/.pop_tools/data'.
<xarray.Dataset> Size: 2GB
Dimensions:         (time: 1, z_t: 62, nlat: 305, nlon: 1301, z_w_top: 62,
                     z_w: 62, z_w_bot: 62)
Coordinates:
  * time            (time) object 8B 0036-12-07 00:00:00
  * z_t             (z_t) float32 248B 500.0 1.5e+03 ... 5.625e+05 5.875e+05
  * z_w             (z_w) float32 248B 0.0 1e+03 2e+03 ... 5.5e+05 5.75e+05
  * z_w_top         (z_w_top) float32 248B 0.0 1e+03 2e+03 ... 5.5e+05 5.75e+05
  * z_w_bot         (z_w_bot) float32 248B 1e+03 2e+03 3e+03 ... 5.75e+05 6e+05
    ULONG           (nlat, nlon) float64 3MB ...
    ULAT            (nlat, nlon) float64 3MB ...
    TLONG           (nlat, nlon) float64 3MB ...
    TLAT            (nlat, nlon) float64 3MB ...
Dimensions without coordinates: nlat, nlon
Data variables: (12/29)
    TEND_TEMP       (time, z_t, nlat, nlon) float32 98MB ...
    UET             (time, z_t, nlat, nlon) float32 98MB ...
    VNT             (time, z_t, nlat, nlon) float32 98MB ...
    WTT             (time, z_w_top, nlat, nlon) float32 98MB ...
    KPP_SRC_TEMP    (time, z_t, nlat, nlon) float32 98MB ...
    UVEL            (time, z_t, nlat, nlon) float32 98MB ...
    ...              ...
    DIA_IMPVF_TEMP  (time, z_w_bot, nlat, nlon) float32 98MB ...
    SHF             (time, nlat, nlon) float32 2MB ...
    SHF_QSW         (time, nlat, nlon) float32 2MB ...
    DZU             (z_t, nlat, nlon) float32 98MB ...
    DZT             (z_t, nlat, nlon) float32 98MB ...
    SALT            (time, z_t, nlat, nlon) float32 98MB 35.0 35.0 ... 35.0 35.0
Attributes:
    title:             g.e20.G.TL319_t13.control.001_hfreq
    history:           none
    Conventions:       CF-1.0; http://www.cgd.ucar.edu/cms/eaton/netcdf/CF-cu...
    time_period_freq:  day_5
    model_doi_url:     https://doi.org/10.5065/D67H1H0V
    contents:          Diagnostic and Prognostic Variables
    source:            CCSM POP2, the CCSM Ocean Component
    revision:          $Id: tavg.F90 89091 2018-04-30 15:58:32Z altuntas@ucar...
    calendar:          All years have exactly  365 days.
    start_time:        This dataset was created on 2018-12-14 at 16:05:58.8
    cell_methods:      cell_methods = time: mean ==> the variable values are ...

Construct xgcm-compatible dataset and xgcm grid object#

metrics = {
    ("X",): ["DXU", "DXT"],  # X distances
    ("Y",): ["DYU", "DYT"],  # Y distances
    ("Z",): ["DZU", "DZT"],  # Z distances
    ("X", "Y"): ["UAREA", "TAREA"],
}

# here we get the xgcm compatible dataset
grid, xds = pop_tools.to_xgcm_grid_dataset(
    ds,
    periodic=False,
    metrics=metrics,
    boundary={"X": "extend", "Y": "extend", "Z": "extend"},
)

xds["rho"] = pop_tools.eos(xds.SALT, xds.TEMP, depth=xds.z_t * 1e-2)
xds
<xarray.Dataset> Size: 2GB
Dimensions:         (time: 1, z_t: 62, nlat_t: 305, nlon_t: 1301, nlon_u: 1301,
                     nlat_u: 305, z_w_top: 62, z_w_bot: 62)
Coordinates:
  * time            (time) object 8B 0036-12-07 00:00:00
  * z_t             (z_t) float32 248B 500.0 1.5e+03 ... 5.625e+05 5.875e+05
  * z_w_top         (z_w_top) float32 248B 0.0 1e+03 2e+03 ... 5.5e+05 5.75e+05
  * z_w_bot         (z_w_bot) float32 248B 1e+03 2e+03 3e+03 ... 5.75e+05 6e+05
    ULONG           (nlat_u, nlon_u) float64 3MB 160.0 160.1 ... -70.1 -70.0
    ULAT            (nlat_u, nlon_u) float64 3MB -15.03 -15.03 ... 15.03 15.03
    TLONG           (nlat_t, nlon_t) float64 3MB 159.9 160.0 ... 289.9 290.0
    TLAT            (nlat_t, nlon_t) float64 3MB -15.07 -15.07 ... 14.98 14.98
  * nlon_u          (nlon_u) int64 10kB 1 2 3 4 5 6 ... 1297 1298 1299 1300 1301
  * nlat_u          (nlat_u) int64 2kB 1 2 3 4 5 6 7 ... 300 301 302 303 304 305
  * nlon_t          (nlon_t) float64 10kB 0.5 1.5 2.5 ... 1.3e+03 1.3e+03
  * nlat_t          (nlat_t) float64 2kB 0.5 1.5 2.5 3.5 ... 302.5 303.5 304.5
Data variables: (12/30)
    TEND_TEMP       (time, z_t, nlat_t, nlon_t) float32 98MB 3.461e-08 ... nan
    UET             (time, z_t, nlat_t, nlon_u) float32 98MB -0.0005898 ... 0.0
    VNT             (time, z_t, nlat_u, nlon_t) float32 98MB 0.0002135 ... 0.0
    WTT             (time, z_w_top, nlat_t, nlon_t) float32 98MB 0.0 0.0 ... nan
    KPP_SRC_TEMP    (time, z_t, nlat_t, nlon_t) float32 98MB 1.908e-06 ... nan
    UVEL            (time, z_t, nlat_u, nlon_u) float32 98MB -23.85 ... nan
    ...              ...
    SHF             (time, nlat_t, nlon_t) float32 2MB 94.64 96.18 ... -58.65
    SHF_QSW         (time, nlat_t, nlon_t) float32 2MB 245.0 245.2 ... 203.9
    DZU             (z_t, nlat_u, nlon_u) float32 98MB 1e+03 1e+03 ... 2.5e+04
    DZT             (z_t, nlat_t, nlon_t) float32 98MB 1e+03 1e+03 ... 2.5e+04
    SALT            (time, z_t, nlat_t, nlon_t) float32 98MB 35.0 35.0 ... 35.0
    rho             (time, z_t, nlat_t, nlon_t) float64 197MB 1.023e+03 ... nan
Attributes:
    title:             g.e20.G.TL319_t13.control.001_hfreq
    history:           none
    Conventions:       CF-1.0; http://www.cgd.ucar.edu/cms/eaton/netcdf/CF-cu...
    time_period_freq:  day_5
    model_doi_url:     https://doi.org/10.5065/D67H1H0V
    contents:          Diagnostic and Prognostic Variables
    source:            CCSM POP2, the CCSM Ocean Component
    revision:          $Id: tavg.F90 89091 2018-04-30 15:58:32Z altuntas@ucar...
    calendar:          All years have exactly  365 days.
    start_time:        This dataset was created on 2018-12-14 at 16:05:58.8
    cell_methods:      cell_methods = time: mean ==> the variable values are ...

Visualize surface density field#

(xds.rho - 1000).isel(z_t=0).plot(robust=True)
<matplotlib.collections.QuadMesh at 0x7f5e6d456ee0>
../_images/f1d6759d8226a404b72973bd4ebbd0ca4dc93566ee53e2719fc9bf561a99d40e.png

Regrid to density space using grid.transform#

See https://xgcm.readthedocs.io/en/latest/transform.html for more

regridded = grid.transform(
    xds.TEMP,
    axis="Z",
    target=np.arange(25, 30, 0.5),
    target_data=xds.rho - 1000,
    method="linear",
)
/home/docs/checkouts/readthedocs.org/user_builds/pop-tools/conda/latest/lib/python3.9/site-packages/xgcm/grid.py:989: FutureWarning: From version 0.8.0 the Axis computation methods will be removed, in favour of using the Grid computation methods instead. i.e. use `Grid.transform` instead of `Axis.transform`
  warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pop-tools/conda/latest/lib/python3.9/site-packages/numba/np/ufunc/gufunc.py:252: RuntimeWarning: invalid value encountered in _interp_1d_linear
  return self.ufunc(*args, **kwargs)

Visualize temperature on a single isopycnal surface#

regridded.isel(rho=5).plot(robust=True)
<matplotlib.collections.QuadMesh at 0x7f5e713573d0>
../_images/e51c7a610e7c41bbff69d5ecb8a60321b7e3341341ec10df85248ca2f7d32932.png

Find depth of isopycnal surface#

regridded.coords["z_t"] = grid.transform(
    xds.z_t.broadcast_like(xds.rho),
    axis="Z",
    target=np.arange(24, 30, 0.5),
    target_data=xds.rho - 1000,
    method="linear",
)
regridded.z_t.isel(rho=5).plot(robust=True)
<matplotlib.collections.QuadMesh at 0x7f5e6cd085e0>
../_images/c0fecb8117bd4fe2685453a618e4dcba2d38816bc03862d9f9cecd4bc42f2f95.png