Grid operations, regridding and sigma coordinates#
Introduction#
The xoa library provides comprehensive tools for working with grids, performing
regridding operations, and handling terrain-following parametric vertical coordinates
(sigma coordinates). This guide covers three main modules:
xoa.grid: Grid utilities for 1D to nD grid operations on a single gridxoa.regrid: Regridding utilities for operations between different gridsxoa.sigma: Terrain-following parametric vertical coordinates following CF conventions
Grid operations with xoa.grid#
The xoa.grid module provides utilities to get information or perform
operations on a grid.
Note
For operations between different grids, use xoa.regrid instead.
Basic grid operations#
The xoa.grid module offers various functions to manipulate grids:
Getting grid centers and edges:
In [1]: import xarray as xr
In [2]: import numpy as np
In [3]: import xoa.grid as xgrid
# Create a simple 1D coordinate
In [4]: x_edges = xr.DataArray(np.arange(0, 5), dims='x', name='x')
# Get centers from edges
In [5]: x_centers = xgrid.get_centers(x_edges, "x")
In [6]: print(x_centers.values)
[0.5 1.5 2.5 3.5]
# Get edges from centers
In [7]: x_back = xgrid.get_edges(x_centers, "x", mode="linear_extrap")
In [8]: print(x_back.values)
[0. 1. 2. 3. 4.]
Applying operations along dimensions:
The apply_along_dim() function allows you to apply an xarray operator
on data array or dataset dimensions, potentially changing the size of the array.
# Create sample dataset
In [9]: ds = xr.Dataset({
...: 'temp': (['x', 'y'], np.random.rand(4, 3)),
...: }, coords={
...: 'x': np.arange(4),
...: 'y': np.arange(3)
...: })
...:
# Interpolate between grid points
In [10]: ds_mean = xgrid.apply_along_dim(ds, 'x', xgrid.get_centers)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[10], line 1
----> 1 ds_mean = xgrid.apply_along_dim(ds, 'x', xgrid.get_centers)
File ~/checkouts/readthedocs.org/user_builds/xoa/conda/v2026.2.0/lib/python3.14/site-packages/xoa/grid.py:115, in apply_along_dim(ds, dim, func, coord_func, data_kwargs, coord_kwargs, name_kwargs, **kwargs)
113 if isinstance(dso, xr.Dataset):
114 dso = dso.update(daos)
--> 115 dso.attrs = ds.attrs
116 dso.encoding = ds.encoding
117 else:
AttributeError: 'NoneType' object has no attribute 'attrs' and no __dict__ for setting new attributes
Staggered grids#
The xoa library supports staggered grids commonly used in ocean and atmospheric models.
Grid location information can be encoded in variable names using the staggered grid
locator (see Metadata, CF and naming conventions for more details on the SGLocator).
Regridding with xoa.regrid#
The xoa.regrid module provides utilities for regridding data from one grid
to another. It supports various interpolation methods for both horizontal and
vertical regridding.
1D regridding#
The regrid1d() function performs 1D interpolation/regridding
with support for multiple methods:
Supported methods#
In [11]: from xoa.regrid import regrid1d_methods
In [12]: print(list(regrid1d_methods))
[<regrid1d_methods.linear: 1>, <regrid1d_methods.nearest: 0>, <regrid1d_methods.cubic: 2>, <regrid1d_methods.hermit: 3>, <regrid1d_methods.cellave: -1>, <regrid1d_methods.cellerr: -2>]
These include:
linear: Linear interpolation (default)
nearest: Nearest neighbor interpolation
cubic: Cubic interpolation
hermit/hermitian: Hermitian interpolation
cellave: Cell-averaging or conservative regridding
Example usage#
In [13]: from xoa import regrid
# Create source data
In [14]: x_old = xr.DataArray(np.arange(0, 10, 2.0), dims='x')
In [15]: data_old = xr.DataArray(np.sin(x_old), dims='x', coords={'x': x_old})
# Create new grid
In [16]: x_new = xr.DataArray(np.arange(0, 9, 0.5), dims='x')
# Regrid with linear interpolation
In [17]: data_new = regrid.regrid1d(data_old, x_new, method='linear')
Extrapolation modes#
You can control extrapolation behavior:
In [18]: from xoa.regrid import extrap_modes
In [19]: print(list(extrap_modes))
[<extrap_modes.no: 0>, <extrap_modes.top: 1>, <extrap_modes.bottom: -1>, <extrap_modes.both: 2>]
Available modes include:
no/none/false: No extrapolation (default)
top/above/after: Extrapolate toward the top
bottom/below: Extrapolate toward the bottom
both/all/yes/true: Extrapolate both directions
Horizontal regridding#
For 2D horizontal regridding, xoa provides the grid2loc() function
to interpolate from a grid to specific locations.
Vertical regridding#
Vertical regridding is particularly important for ocean models. The isoslice()
function can extract data on isobaric or isopycnal surfaces.
Sigma coordinates with xoa.sigma#
The xoa.sigma module handles terrain-following parametric vertical coordinates
according to the CF conventions for
Parametric Vertical Coordinates.
What are sigma coordinates?#
Sigma coordinates are terrain-following vertical coordinates commonly used in ocean and atmospheric models. Instead of using fixed depth or pressure levels, sigma coordinates follow the bathymetry (ocean models) or topography (atmospheric models), which allows better resolution near boundaries.
Supported coordinate types#
The module supports the following CF-compliant sigma coordinate types:
In [20]: from xoa.sigma import SIGMA_COORDINATE_FUNCTIONS
In [21]: for coord_type in SIGMA_COORDINATE_FUNCTIONS:
....: print(f"- {coord_type}")
....:
- atmosphere_sigma_coordinate
- ocean_sigma_coordinate
- ocean_s_coordinate
- ocean_s_coordinate_g1
- ocean_s_coordinate_g2
These include:
atmosphere_sigma_coordinate: For atmospheric models
ocean_sigma_coordinate: Basic ocean sigma coordinates
ocean_s_coordinate: Generalized ocean s-coordinates
ocean_s_coordinate_g1: Song & Haidvogel s-coordinates (generic form 1)
ocean_s_coordinate_g2: Shchepetkin s-coordinates (generic form 2)
Converting sigma to physical coordinates#
The main functionality is to decode sigma coordinates into physical depths (for ocean) or pressures (for atmosphere) using the appropriate formula terms.
Note
The core sigma conversion functions use Numba’s guvectorize decorator to create optimized universal functions (ufuncs). This provides:
Efficient computation through JIT compilation
Automatic broadcasting over horizontal dimensions
Compatibility with dask arrays for lazy evaluation
Seamless integration with
xarray.apply_ufunc()
Ocean sigma coordinates#
For ocean models, sigma coordinates are converted to depths using formulas that depend on:
sigma (σ): The sigma level (typically from -1 to 0)
eta (η): Sea surface height
depth: Bathymetry (bottom depth)
Additional parameters depending on the coordinate type (e.g., hc, theta_s, theta_b for s-coordinates)
# Example with CROCO model output
In [22]: import xoa
# Open a sample with sigma coordinates
In [23]: ds = xoa.open_data_sample("MODELS/CROCO/SOUTH-AFRICA/croco.south-africa.meridional.nc")
# Check the sigma coordinate
In [24]: print(ds.s_rho.attrs.get('standard_name'))
ocean_s_coordinate_g2
# Decode sigma to depths using the sigma module
# The decode_cf_sigma function will automatically detect
# the coordinate type and apply the correct formula
In [25]: from xoa.sigma import decode_sigma
In [26]: dsz = decode_sigma(ds)
In [27]: print(dsz.depth)
<xarray.DataArray 'depth' (time: 1, s_rho: 32, eta_rho: 56, xi_rho: 1)> Size: 14kB
array([[[[-4.47877110e+03],
[-4.45341971e+03],
[-4.43332704e+03],
...,
[-7.38942221e+01],
[-7.34145067e+01],
[-7.34091375e+01]],
[[-4.10998820e+03],
[-4.08677643e+03],
[-4.06837911e+03],
...,
[-7.04949884e+01],
[-7.00423304e+01],
[-7.00372638e+01]],
[[-3.70399833e+03],
[-3.68314549e+03],
[-3.66661727e+03],
...,
...
...,
[-4.29529272e+00],
[-4.27484202e+00],
[-4.27461272e+00]],
[[-1.00794918e+01],
[-1.00480588e+01],
[-1.00177861e+01],
...,
[-2.57388434e+00],
[-2.56165088e+00],
[-2.56151371e+00]],
[[-3.17876268e+00],
[-3.15406668e+00],
[-3.12913607e+00],
...,
[-8.56901999e-01],
[-8.52836074e-01],
[-8.52790485e-01]]]], shape=(1, 32, 56, 1))
Coordinates:
* time (time) float64 8B 2.592e+06
* s_rho (s_rho) float32 128B -0.9844 -0.9531 -0.9219 ... -0.04688 -0.01562
* eta_rho (eta_rho) float32 224B 6.0 7.0 8.0 9.0 10.0 ... 58.0 59.0 60.0 61.0
* xi_rho (xi_rho) float32 4B 131.0
lat_rho (eta_rho, xi_rho) float32 224B -37.67 -37.61 ... -34.03 -33.96
lon_rho (eta_rho, xi_rho) float32 224B 18.83 18.83 18.83 ... 18.83 18.83
depth (time, s_rho, eta_rho, xi_rho) float64 14kB -4.479e+03 ... -0.8528
Attributes:
long_name: Ocean s roms coordinate at rho point
Vtransform: 2
standard_name: ocean_layer_depth
units: m
Atmosphere sigma coordinates#
For atmospheric models, sigma coordinates are converted to pressure levels:
# p(k) = ptop + sigma(k) * (ps - ptop)
# where:
# - p: pressure at level k
# - ptop: pressure at top of model
# - sigma: sigma coordinate value
# - ps: surface pressure
Working with vertical sections#
Once sigma coordinates are decoded to physical depths, you can work with vertical sections and perform operations like:
Extracting data at specific depth levels
Interpolating to regular depth levels using
xoa.regridComputing vertical derivatives
Creating vertical transects
See the gallery example Interpolate a meridional section of CROCO outputs to regular depths for a practical demonstration of working with sigma coordinates and vertical sections.
Formula terms mapping#
The module provides automatic mapping from formula terms (as defined in the CF conventions) to the standard xoa CF names:
In [28]: from xoa.sigma import FORMULA_TERMS_TO_CF_NAMES
In [29]: from pprint import pprint
In [30]: pprint(FORMULA_TERMS_TO_CF_NAMES)
{'a': 'thetas',
'b': 'thetab',
'c': 'cs',
'depth': 'bathy',
'depth_c': 'hc',
'eta': 'ssh',
's': 'sig',
'sigma': 'sig'}
This allows the module to find the required variables in your dataset regardless of their actual names, as long as they follow CF conventions.
Example: Working with CROCO model output#
Here’s a complete example showing how to work with grid operations, regridding, and sigma coordinates using CROCO model output:
In [31]: import xoa
In [32]: import xoa.meta
In [33]: import xoa.regrid
In [34]: import numpy as np
# To have 'xoa' and 'decode_sigma' accessors
In [35]: xoa.register_accessors()
# Load CROCO sample data
In [36]: ds = xoa.open_data_sample("MODELS/CROCO/SOUTH-AFRICA/croco.south-africa.meridional.nc")
# Set internal croco decoding rules and decode s-coordinate and dataset names
In [37]: xoa.meta.set_meta_specs('croco')
Out[37]: <xoa.meta.set_meta_specs at 0x759688e21e50>
In [38]: ds_decoded = ds.decode_sigma().xoa.decode()
# The sigma coordinate has been converted to depths
# You can now regrid to regular depth levels if needed
# Define regular depth levels
In [39]: regular_depths = xr.DataArray(np.linspace(-100, 0, 20), dims='depth', attrs={'units': 'm', 'positive': 'up'})
# Regrid temperature to regular depths
In [40]: temp_regular = xoa.regrid.regrid1d(ds_decoded.ptemp, regular_depths, extrap="top")
In [41]: print(temp_regular[0, :, 0, 0])
<xarray.DataArray 'ptemp' (depth: 20)> Size: 80B
array([15.634643, 15.841397, 16.087772, 16.342157, 16.596542, 16.90044 ,
17.249428, 17.598415, 17.978926, 18.375126, 18.781078, 19.230177,
19.676584, 19.866636, 20.036734, 20.02078 , 20.003649, 19.984797,
19.963604, 19.955126], dtype=float32)
Coordinates:
* depth (depth) float64 160B -100.0 -94.74 -89.47 ... -10.53 -5.263 0.0
y_rho float32 4B 6.0
lat_rho float32 4B -37.67
lon_rho float32 4B 18.83
time float64 8B 2.592e+06
x_rho float32 4B 131.0
Attributes:
long_name: Potential temperature
units: Celsius
field: temperature, scalar, series
standard_name: sea_water_potential_temperature
See also#
xoa.grid: Grid utilities modulexoa.regrid: Regridding modulexoa.sigma: Sigma coordinates modulexoa.coords: Coordinate utilitiesExamples: Gallery of examples including grid and sigma operations
Metadata, CF and naming conventions: For more on CF metadata and grid location encoding