Budgets¶
Conservation-budget diagnostics. For a tracer \(\theta\) with flux \(\mathbf{F}\) and source \(Q\), the budget residual measures how far a dataset departs from exact conservation:
ControlVolumeIntegral and BoundaryFlux assemble the volume and surface
terms; the tracer-specific residuals specialise \(\theta\) to heat, salt,
volume, or kinetic energy.
Building blocks¶
ControlVolumeIntegral
¶
ControlVolumeIntegral(variable: str, *, volume_metrics: Dataset, region: DataArray | None = None, dims: Sequence[str] = ('z', 'lat', 'lon'), cell_volume_var: str = 'cell_volume')
Bases: Operator
Volume-weighted integral of a field over a control volume.
Integrates variable over dims weighted by the cell volumes in
volume_metrics (optionally masked to region). The metrics are
explicit, never auto-derived — build them with
:func:xrops.grad.grid_metrics_from_coords if the data does not ship
them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
variable
|
str
|
Name of the field to integrate. |
required |
volume_metrics
|
Dataset
|
Dataset carrying the |
required |
region
|
DataArray | None
|
Optional boolean mask selecting the control volume. |
None
|
dims
|
Sequence[str]
|
Dimensions integrated over. |
('z', 'lat', 'lon')
|
cell_volume_var
|
str
|
Name of the cell-volume variable in
|
'cell_volume'
|
Returns:
| Type | Description |
|---|---|
|
The volume integral as a DataArray (reduced over |
Source code in packages/xrtoolz-ops/src/xrops/budgets/operators.py
BoundaryFlux
¶
BoundaryFlux(*, variable: str | None, velocity_vars: dict[str, str], face_metrics: Dataset, region: DataArray | None = None)
Bases: Operator
Advective flux through the faces of a control volume.
Integrates the velocity_vars (optionally carrying variable) over
the face areas in face_metrics, optionally restricted to region.
The metrics are explicit, never auto-derived.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
variable
|
str | None
|
Tracer carried by the flow, or |
required |
velocity_vars
|
dict[str, str]
|
Mapping of face direction → velocity variable name. |
required |
face_metrics
|
Dataset
|
Dataset carrying the face-area weights. |
required |
region
|
DataArray | None
|
Optional boolean mask selecting the control volume. |
None
|
Returns:
| Type | Description |
|---|---|
|
Dataset of per-boundary fluxes. |
Source code in packages/xrtoolz-ops/src/xrops/budgets/operators.py
BudgetResidual
¶
Bases: Operator
Generic conservation residual ∂φ/∂t + ∇·F − source + sink.
Combines a precomputed tendency and flux divergence (with optional
source / sink terms) into the budget-closure residual; a perfectly
closed budget is zero. Inputs are passed to __call__, not the
constructor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tendency
|
Local time tendency |
required | |
flux_divergence
|
Divergence of the flux |
required | |
source
|
Optional source term. |
required | |
sink
|
Optional sink term. |
required |
Returns:
| Type | Description |
|---|---|
|
The budget-closure residual DataArray. |
Tracer-specific residuals¶
HeatBudgetResidual
¶
Bases: _TracerBudgetOp
Per-cell heat-budget residual ∂θ/∂t + ∇·(u θ) − Q.
Uses the spherical-metric divergence from :mod:xrops.grad; returns
the per-cell residual (zero for a closed budget). It does not consume
volume/face metrics — multiply by cell_volume and integrate with
:class:ControlVolumeIntegral for a control-volume closure.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
temp_var
|
str
|
Temperature (θ) variable name. |
'theta'
|
u_var
|
Eastward velocity variable name. |
required | |
v_var
|
Northward velocity variable name. |
required | |
w_var
|
Vertical velocity variable name, or |
required | |
surface_flux_var
|
Optional surface heat-flux variable name. |
required | |
time_dim
|
Time dimension name. |
required | |
lat
|
Latitude coordinate name. |
required | |
lon
|
Longitude coordinate name. |
required | |
depth
|
Vertical coordinate name, or |
required |
Returns:
| Type | Description |
|---|---|
|
The per-cell heat-budget residual DataArray. |
Source code in packages/xrtoolz-ops/src/xrops/budgets/operators.py
SaltBudgetResidual
¶
Bases: _TracerBudgetOp
Per-cell salt-budget residual ∂S/∂t + ∇·(u S) − F.
Like :class:HeatBudgetResidual but for salinity; returns the per-cell
residual (zero for a closed budget).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
salt_var
|
str
|
Salinity (S) variable name. |
'so'
|
u_var
|
Eastward velocity variable name. |
required | |
v_var
|
Northward velocity variable name. |
required | |
w_var
|
Vertical velocity variable name, or |
required | |
surface_flux_var
|
Optional surface salt-flux variable name. |
required | |
time_dim
|
Time dimension name. |
required | |
lat
|
Latitude coordinate name. |
required | |
lon
|
Longitude coordinate name. |
required | |
depth
|
Vertical coordinate name, or |
required |
Returns:
| Type | Description |
|---|---|
|
The per-cell salt-budget residual DataArray. |
Source code in packages/xrtoolz-ops/src/xrops/budgets/operators.py
VolumeBudgetResidual
¶
VolumeBudgetResidual(*, u_var: str = 'u', v_var: str = 'v', w_var: str | None = None, lat: str = 'lat', lon: str = 'lon', depth: str | None = 'depth')
Bases: Operator
Per-cell volume (continuity) residual ∇·u.
The horizontal + vertical velocity divergence; zero for a non-divergent (volume-conserving) flow.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
u_var
|
str
|
Eastward velocity variable name. |
'u'
|
v_var
|
str
|
Northward velocity variable name. |
'v'
|
w_var
|
str | None
|
Vertical velocity variable name, or |
None
|
lat
|
str
|
Latitude coordinate name. |
'lat'
|
lon
|
str
|
Longitude coordinate name. |
'lon'
|
depth
|
str | None
|
Vertical coordinate name, or |
'depth'
|
Returns:
| Type | Description |
|---|---|
|
The per-cell continuity residual DataArray. |
Source code in packages/xrtoolz-ops/src/xrops/budgets/operators.py
KineticEnergyBudgetResidual
¶
KineticEnergyBudgetResidual(*, u_var: str = 'u', v_var: str = 'v', forcing_vars: Sequence[str] | None = None, time_dim: str = 'time', lat: str = 'lat', lon: str = 'lon', depth: str | None = 'depth', w_var: str | None = None)
Bases: Operator
Per-cell kinetic-energy budget residual.
Residual of ∂KE/∂t + ∇·(u KE) − forcing with KE = ½(u² + v²);
zero for a closed KE budget.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
u_var
|
str
|
Eastward velocity variable name. |
'u'
|
v_var
|
str
|
Northward velocity variable name. |
'v'
|
forcing_vars
|
Sequence[str] | None
|
Optional forcing / dissipation variable names summed as the source term. |
None
|
time_dim
|
str
|
Time dimension name. |
'time'
|
lat
|
str
|
Latitude coordinate name. |
'lat'
|
lon
|
str
|
Longitude coordinate name. |
'lon'
|
depth
|
str | None
|
Vertical coordinate name, or |
'depth'
|
w_var
|
str | None
|
Vertical velocity variable name, or |
None
|
Returns:
| Type | Description |
|---|---|
|
The per-cell KE-budget residual DataArray. |
Source code in packages/xrtoolz-ops/src/xrops/budgets/operators.py
Functional primitives (Layer 0)¶
These pure functions back the operators above; each takes xr.DataArray/xr.Dataset and a dim: argument.
control_volume_integral
¶
control_volume_integral(ds: Dataset, *, variable: str, volume_metrics: Dataset, region: DataArray | None = None, dims: Sequence[str] = ('z', 'lat', 'lon'), cell_volume_var: str = 'cell_volume') -> DataArray
Volume-weighted integral ∫ var dV over a control volume.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ds
|
Dataset
|
Dataset containing |
required |
variable
|
str
|
Variable to integrate. |
required |
volume_metrics
|
Dataset
|
Dataset carrying |
required |
region
|
DataArray | None
|
Optional boolean mask selecting the integration volume.
|
None
|
dims
|
Sequence[str]
|
Spatial dims to integrate over. Any other dims (e.g.
|
('z', 'lat', 'lon')
|
cell_volume_var
|
str
|
Name of the volume variable in
|
'cell_volume'
|
Returns:
| Type | Description |
|---|---|
DataArray
|
DataArray with the integrated value, reduced over |
Notes
For 2-D budgets without a vertical coordinate, pass
volume_metrics whose cell_volume equals cell_area
(the V4.4 helper does this automatically when depth=None)
and set dims=("lat", "lon").
Source code in packages/xrtoolz-ops/src/xrops/budgets/_src/volume.py
boundary_flux
¶
boundary_flux(ds: Dataset, *, variable: str | None, velocity_vars: dict[str, str], face_metrics: Dataset, region: DataArray | None = None, time_dim: str = 'time') -> Dataset
Region-integrated advective face flux.
Computes ∑ φ u A_face over each face direction. By Gauss's
theorem the same sum equals the closed-boundary flux when φ u
is divergence-free over the integration domain; for general flows
the result is the area-weighted total of the face-flux field
rather than a pure boundary integral. See the module docstring.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ds
|
Dataset
|
Dataset containing |
required |
variable
|
str | None
|
Tracer name or |
required |
velocity_vars
|
dict[str, str]
|
Mapping |
required |
face_metrics
|
Dataset
|
Dataset carrying |
required |
region
|
DataArray | None
|
Optional boolean mask. |
None
|
time_dim
|
str
|
Name of the time coordinate to preserve as a non-
spatial dim. Default |
'time'
|
Returns:
| Type | Description |
|---|---|
Dataset
|
Dataset with one scalar (or |
Dataset
|
active face direction: |
Dataset
|
|
Source code in packages/xrtoolz-ops/src/xrops/budgets/_src/flux.py
budget_residual
¶
budget_residual(tendency: DataArray, flux_divergence: DataArray, *, source: DataArray | None = None, sink: DataArray | None = None) -> DataArray
Combine the four canonical budget terms into a residual.
Sign convention (matches the V4.3 tracer / KE residuals)::
residual = tendency + flux_divergence - source + sink
where flux_divergence is ∇·(u φ) — the divergence of the
advective flux out of each cell. The conservation equation
∂φ/∂t + ∇·(u φ) = source - sink rearranges to residual ≈ 0.
A closed budget has residual ≈ 0 within float tolerance.
Source code in packages/xrtoolz-ops/src/xrops/budgets/_src/residual.py
heat_budget_residual
¶
heat_budget_residual(ds: Dataset, *, temp_var: str = 'theta', u_var: str = 'u', v_var: str = 'v', w_var: str | None = None, surface_flux_var: str | None = None, time_dim: str = 'time', lat: str = 'lat', lon: str = 'lon', depth: str | None = 'depth') -> DataArray
Per-cell heat-budget residual.
Computes ∂θ/∂t + ∇·(u θ) - F_surface where F_surface is an
optional surface heat-flux source field. ρ c_p factors are
omitted: the residual is in units of [θ] / s, not W/m³. For
closure tests this is sufficient.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ds
|
Dataset
|
Dataset with potential temperature + velocities. |
required |
temp_var, u_var, v_var, w_var
|
Variable names. |
required | |
surface_flux_var
|
str | None
|
Optional source field on the surface plane. |
None
|
time_dim
|
str
|
Name of the time coordinate. |
'time'
|
lat, lon
|
Names of horizontal coords (degrees). |
required | |
depth
|
str | None
|
Vertical coord name. |
'depth'
|
Returns:
| Type | Description |
|---|---|
DataArray
|
Per-cell residual, same dims as |
Notes
ρ c_p θ would be the proper heat content; we use θ
directly so the closure test does not require a density field.
Multiply the output by ρ_0 c_p upstream if you need W/m³.
Source code in packages/xrtoolz-ops/src/xrops/budgets/_src/heat.py
salt_budget_residual
¶
salt_budget_residual(ds: Dataset, *, salt_var: str = 'so', u_var: str = 'u', v_var: str = 'v', w_var: str | None = None, surface_flux_var: str | None = None, time_dim: str = 'time', lat: str = 'lat', lon: str = 'lon', depth: str | None = 'depth') -> DataArray
Per-cell salt-budget residual ∂S/∂t + ∇·(u S) - F_surface.
Source code in packages/xrtoolz-ops/src/xrops/budgets/_src/salt.py
volume_budget_residual
¶
volume_budget_residual(ds: Dataset, *, u_var: str = 'u', v_var: str = 'v', w_var: str | None = None, lat: str = 'lat', lon: str = 'lon', depth: str | None = 'depth') -> DataArray
∇·u with spherical curvature + ∂w/∂z if w_var is given.
Closes to ≈ 0 for an incompressible flow. w_var=None returns
the surface 2-D divergence only. Uses :func:xrops.grad.divergence
so the spherical curvature term is included; when the vertical
component is present it goes in the same call as a third
rectilinear axis rather than a separately added partial.
Source code in packages/xrtoolz-ops/src/xrops/budgets/_src/volume_budget.py
kinetic_energy_budget_residual
¶
kinetic_energy_budget_residual(ds: Dataset, *, u_var: str = 'u', v_var: str = 'v', forcing_vars: Sequence[str] | None = None, time_dim: str = 'time', lat: str = 'lat', lon: str = 'lon', depth: str | None = 'depth', w_var: str | None = None) -> DataArray
Per-cell KE-budget residual.
KE per unit mass E = 0.5 (u² + v²). Residual::
∂E/∂t + ∇·(u E) - Σ forcings
Forcings (e.g. wind-stress work) are supplied as additional source fields summed into the source term.