Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

diagnostix: diagnostics of geophysical fields for JAX

diagnostix computes what a geophysical field says, physically and statistically: kinematics (vorticity, strain, Okubo–Weiss, geostrophy, potential vorticity), column quantities (N², mixed-layer depth, boundary-layer height, column integrals), budgets, climatologies and anomalies, spectra, area-weighted EOFs and skill scores. It sits at GeoNumerics tier 2, on gridax’s differences, and holds the maths that xrops wraps for xarray. It absorbs what was planned as skillax. Status: proposed, at design-sketch depth.

At a glance

Layer, tierGeoNumerics, tier 2
Importsgridax, geophysix, functax; JAX, NumPy, SciPy
Imported byxrops; sesmax-ocean (planned)
Takes overthe maths of xrops ocn, atm (vertical and wind), budgets, anomaly, the spectra and decompositions in transforms and wavelets, and the scores in metrics; somax’s eval/metrics.py
Does not ownthe DataArray API (xrops); differences and metrics (gridax); bases and generic signal processing (functax); benchmark contracts and leaderboards (pipekit-evaluate); extremes modelling (xtremax)
Statusproposed; no repository yet
Tracked inthe layer map; the companion gridax and functax designs
where diagnostix sits · GeoNumerics tier 2

Where diagnostix sits · geonumerics tier 2.

diagnostix sits one tier above gridax, whose differences and metrics every diagnostic is built from. This page is the design; core types, algorithms and the code survey are in diagnostix: design details.

The problem

A plume’s emission rate is checked with diagnostics before anyone trusts it, and every one of them lives in xrops, on xarray, in NumPy and SciPy:

StepThe diagnosticWhere it is coded today
How deep the plume mixesboundary-layer height from the bulk Richardson numberxrops pbl_height_bulk_richardson
The enhancement over backgrounda climatology and the anomaly from itxrops calculate_climatology, calculate_anomaly and their smoothed forms
The rate by mass balancethe flux through the faces of a box around the sourcexrops boundary_flux (which sums over every face of the field, not a chosen box), control_volume_integral, budget_residual
Which scales a reconstruction resolvespower spectra and the PSD scorexrops power_spectrum, isotropic_power_spectrum, psd_score, resolved_scale
How good the transport model isRMSE, bias, correlation, spread–skillxrops metrics (rmse, bias, correlation, spread_skill_ratio, rank_histogram)
The ocean side, for sesmaxvorticity, strain, Okubo–Weiss, geostrophy, kinetic energyxrops ocn/kinematics.py; somax eval/metrics.py; finitevolX operators/diagnostics.py on its C-grid

Because they sit on xarray, none of them traces under JAX or differentiates, so a model that wants a diagnostic inside its loop or as a loss writes it again: kinetic energy, enstrophy and geostrophic imbalance each exist in xrops, in somax and in finitevolX. And xrops’ diagnostics run on geoops’ difference kernels, so the xarray toolkit depends on the GeoArray toolkit for maths that belongs to neither.

What a field says

A diagnostic is a function of one or more fields on a grid that returns a smaller or more telling field: a derivative combination, an integral, a residual, a spectrum, a decomposition or a score. Each is built from gridax’s differences and metrics and geophysix’s constants.

Kinematics. With u, v the horizontal velocity and f the Coriolis parameter, vorticity and the two strains are

ζ=∂xv−∂yu\zeta = \partial_x v - \partial_y u
σn=∂xu−∂yv\sigma_n = \partial_x u - \partial_y v
σs=∂xv+∂yu\sigma_s = \partial_x v + \partial_y u

The Okubo–Weiss parameter Okubo, 1970Weiss, 1991 W = σₙ² + σₛ² − ζ² separates strain-dominated from rotation-dominated flow, and the geostrophic velocity from sea-surface height η is u_g = −(g / f) ∂ᵧη, v_g = (g / f) ∂ₓη.

Columns. The buoyancy frequency is N² = −(g / ρ₀) ∂ρ/∂z with ρ from geophysix’s equation of state. The boundary-layer height is the lowest z where the bulk Richardson number reaches a critical value Ri_c, with θ_v the virtual potential temperature:

Rib(z)=gθv,0(θv(z)−θv,0)zu(z)2+v(z)2\mathrm{Ri}_b(z) = \frac{g}{\theta_{v,0}} \frac{(\theta_v(z) - \theta_{v,0}) z}{u(z)^2 + v(z)^2}

Budgets. For a quantity q carried by the velocity u, with source S, over a control volume V with surface ∂V and outward normal n,

ddt∫VqdV+∮∂Vqu⋅ndS=∫VSdV\frac{d}{dt} \int_V q dV + \oint_{\partial V} q \mathbf{u} \cdot \mathbf{n} dS = \int_V S dV

and the residual of that balance is the diagnostic. For a plume in steady state, the flux through the faces of a box around the source is the emission rate, Q = ∮ Ω’ (u · n) dl, with Ω’ the column enhancement.

Climate. With q̄(d) the climatology at calendar day d (on a leap-year calendar, so each date keeps one slot in every year), the anomaly is q’(t) = q(t) − q̄(d(t)); trends, detrending and temporal filters act on q’.

Spectra. With q̂(k) the Fourier transform of q, the power spectrum is P(k) = |q̂(k)|², the isotropic spectrum averages it over rings of |k|, and the spectral flux Π(k) sums the nonlinear transfer below k. Cross, coherence and rotary spectra and wavelets follow the same pattern.

EOFs. With X the anomaly matrix (time × space) and W the diagonal of cell areas from gridax, the area-weighted EOFs come from the SVD of the weighted anomalies:

W1/2X⊤=UΣV⊤W^{1/2} X^\top = U \Sigma V^\top

The EOFs are W^(−1/2) U, the principal components VΣ, and the i-th explains a fraction σᵢ² / ∑ⱼ σⱼ² of the variance.

Skill. With ŷ a prediction and y the truth over valid cells with weights w, RMSE = (∑ w (ŷ − y)² / ∑ w)^½, the bias, the correlation, and for ensembles the spread–skill ratio and the rank histogram.

Modules and boundaries

Seven modules. Every function takes arrays and a gridax grid and returns arrays; xrops keeps the names, attributes and units.

ModuleOwnsComes from
diagnostix.kinematicsvorticity, divergence, strains, Okubo–Weiss, geostrophic and ageostrophic velocity, kinetic energy and enstrophy, frontogenesis, potential vorticityxrops ocn/kinematics.py; somax eval/metrics.py
diagnostix.columnsN², lapse rate, mixed-layer depth, boundary-layer height, column integralsxrops ocn/kinematics.py (N², mixed layer), atm/_src/vertical.py
diagnostix.budgetscontrol-volume integrals, boundary fluxes, budget residuals for volume, heat, salt, kinetic energy and tracer massxrops budgets/
diagnostix.climateclimatologies, anomalies, trends, detrending, temporal filtersxrops anomaly/
diagnostix.spectrapower, isotropic, cross, coherence and rotary spectra, spectral fluxes, slopes, waveletsxrops transforms/_src/fourier.py, wavelet.py, wavelets/
diagnostix.eofsarea weighting and the analysis of EOFs: explained variance, principal components, truncationxrops transforms/_src/decompose.py (eof, pca), which are unweighted scikit-learn PCA; the area weighting is new
diagnostix.skillpixel, spectral, probabilistic and physical scores: RMSE, bias, correlation, PSD score, resolved scale, spread–skill, rank histogram, geostrophic and PV residualsxrops metrics/; somax eval/metrics.py

What diagnostix owns, and what it hands off

The test for every name: does it take a field on a grid and say something about it, physically or statistically? Then diagnostix owns it.

Neighbourdiagnostix ownsThe neighbour ownsThe line
xropsthe mathsthe DataArray API: names, attributes, units, dask chunkingxrops calls diagnostix kernels
gridaxthe diagnosticsdifferences, metrics, vertical coordinates on analysis gridsdiagnostix composes gridax’s differences
functaxthe area weighting and the analysis of EOFsbases, including an EOF basis from data; generic signal processingthe basis is functax’s, what it says about the climate is diagnostix’s
finitevolXdiagnostics on an A-gridoperators and conserved quantities on a model’s C-gridin a model’s own staggering, finitevolX (decision 4)
pipekit-evaluatethe scoresbenchmark contracts, evaluation protocols, leaderboardsa score is a function here; a benchmark that runs it is pipekit’s
xtremaxanomalies and climatologiesextremes modelling: GEV, GPD, return levelsxtremax fits extremes to what diagnostix extracts

What moves in

FromWhatIts users then import
xropsthe maths of ocn, atm (vertical and wind), budgets, anomaly, the spectra and decompositions in transforms and wavelets, and metricsdiagnostix underneath; xrops keeps every DataArray function and Operator, and metrics/leaderboard.py goes to pipekit-evaluate
somaxeval/metrics.py: kinetic energy, enstrophy, RMS divergence, geostrophic imbalance, QG balance residualdiagnostix.kinematics, diagnostix.skill; sesmax-core inherits nothing to move
the skillax planskill scoresdiagnostix.skill (decision 2)

In use

Checking a plume’s emission rate: how deep it mixes, its enhancement over background, its rate by mass balance, and how well the transport model reproduces it. Every diagnostix name is proposed.

# Shapes: Z levels; T times; Ny × Nx lon/lat cells

import diagnostix as dgx
import gridax as gdx

# proposed (gridax)
grid = gdx.grids.SphericalGrid.from_axes(lon_centres, lat_centres, stagger="A")

# --- how deep the plume mixes ---
# proposed (diagnostix)
# lowest z where Ri_b(z) = (g / θ_v0) (θ_v(z) − θ_v0) z / (u² + v²) reaches Ri_c
mixing_height = dgx.columns.boundary_layer_height(  # (Z, Ny, Nx) → (Ny, Nx)
    virtual_potential_temperature, eastward_wind, northward_wind, heights
)

# --- enhancement over background ---
# proposed (diagnostix)
# Ω' = Ω − Ω̄(d(t))
enhancement = dgx.climate.anomaly(column, climatology, day_of_year)  # → (T, Ny, Nx)

# --- the rate by mass balance ---
# proposed (diagnostix)
# Q = ∮ Ω' (u · n) dl over the faces of the box
rate = dgx.budgets.boundary_flux(  # (T, Ny, Nx) → (T,)
    grid, enhancement, eastward_wind_pbl, northward_wind_pbl, box=source_box
)

# --- skill of the transport model ---
# proposed (gridax)
cell_area = gdx.metrics.cell_area(grid)  # → (Ny, Nx)
# proposed (diagnostix)
# RMSE = (∑ w (ŷ − y)² / ∑ w)^½, with w the cell areas
error = dgx.skill.rmse(simulated, enhancement, weights=cell_area)  # → (T,)

Each call is a pure JAX function, so the same rmse and boundary_flux can sit inside a training loss for an emulator of the transport model, where today they cannot.

Integration across the stack

Two libraries would import diagnostix, and one model’s metrics retire into it.

ConsumerUses todayWith diagnostix
xropsevery diagnostic written on xarray in NumPy and SciPy, its differences through geoopsthe same DataArray functions and Operators, each a thin wrapper over a diagnostix kernel; differences through gridax, so xrops’ calculus no longer needs geoops
sesmax-ocean (planned)—kinematics, spectra and skill as in-loop diagnostics and losses
somaxeval/metrics.pyretires with somax; sesmax-ocean imports diagnostix instead

For an xrops user nothing changes; inside xrops the kernel moves:

# Today: xrops computes ζ on xarray with geoops' difference kernels
# today (xrops)
from xrops.ocn import relative_vorticity

# After: the same function, a wrapper over the JAX kernel
# proposed (diagnostix)
from diagnostix.kinematics import vorticity

Decisions and open questions

Four decided, two leaning, one open. Each entry gives the position first, then the trade-off.

1. Layer: GeoNumerics tier 2

Decided (5 Nov 2026, on the layer map). diagnostix imports gridax (tier 1), geophysix (tier 0) and functax (Core). xrops and sesmax-ocean sit above it.

2. One library for physical and statistical diagnostics

Decided (5 Nov 2026). The skill scores planned for a separate skillax live here. A PSD score and a resolved scale need the same spectra as an energy-flux diagnostic, and a geostrophic-imbalance score needs the same kinematics; two libraries would import each other.

3. The DataArray API stays in xrops

Decided (5 Nov 2026, on the layer map). diagnostix takes arrays and a gridax grid. Names, attributes, units, CF conventions and dask chunking are xrops’, which wraps each kernel.

4. Diagnostics on a model’s own grid

Open. finitevolX’s operators/diagnostics.py computes kinetic energy, strain, Okubo–Weiss, enstrophy, QG potential vorticity and available potential energy in its C-grid staggering, as functions, as masked classes and as pipekit Operators, as a model steps. Leaning: those stay in finitevolX, because they are operators on the model’s state; diagnostix’s versions work on A-grid fields as they arrive from a model’s output or a product. somax’s evaluation metrics, which work on outputs, move here. Either way diagnostix never imports finitevolX: finitevolX is GeoNumerics tier 3 and diagnostix tier 2, and an optional import still counts as an edge. If both come to need one kernel, it moves down into diagnostix (or gridax) and finitevolX imports it.

5. JAX kernels, SciPy where it must be

Leaning. Everything a loss might use (kinematics, columns, budgets, spectra, skill) is a traceable JAX function. The few routines that rest on host-only libraries today (PyWavelets for wavelets, xrft for spectra, scikit-learn for EOFs) may stay on the host behind the same names, marked as such, until a JAX version is worth writing.

6. EOFs: the basis in functax, the analysis here

Decided (5 Nov 2026, on the layer map). functax builds a basis from data with weights as an argument; diagnostix supplies the area weights from gridax, the truncation and the reading of the modes.

7. Scores here, benchmarks in pipekit-evaluate

Leaning. A score is a function of a prediction and a truth; a benchmark that fixes the data, the splits and the leaderboard is pipekit-evaluate’s. xrops’ metrics/leaderboard.py moves there, not here.

Roadmap

Six waves, each a GitHub issue epic in the diagnostix repository once it exists.

WaveDeliversEndsNeeds
0. Scaffoldthe repository from the library template; CI, capability index; reference tests against xrops on the same arrays—gridax wave 3
1. Kinematics and columnsdiagnostix.kinematics, diagnostix.columnsthe maths of xrops ocn and atm/vertical0
2. Budgets and climatediagnostix.budgets, diagnostix.climatethe maths of xrops budgets and anomaly0
3. Spectra and EOFsdiagnostix.spectra, diagnostix.eofsthe maths of xrops transforms and wavelets0; functax’s EOF basis
4. Skilldiagnostix.skillthe maths of xrops metrics; somax eval/metrics.py1, 3
5. Consumers switchxrops onto diagnostix; sesmax-ocean from the startthe copies1–4

Waves 1, 2 and 3 can run side by side.

Sources

References
  1. Okubo, A. (1970). Horizontal dispersion of floatable particles in the vicinity of velocity singularities such as convergences. Deep-Sea Research, 17, 445–454.
  2. Weiss, J. (1991). The dynamics of enstrophy transfer in two-dimensional hydrodynamics. Physica D, 48, 273–294.
  3. Vogelezang, D. H. P., & Holtslag, A. A. M. (1996). Evaluation and model impacts of alternative boundary-layer height formulations. Boundary-Layer Meteorology, 81, 245–269.