advection.closure¶
Energy-balance closure methods and diagnostics.
This module is deliberately kept separate from the direct advection
accounting in advection.advection and advection.advect_detect.
Closure forcing (Twine et al. 2000) and physical advection accounting (Wang
et al. 2024; Moderow et al. 2021) are two fundamentally different responses to
eddy-covariance under-closure and must not be conflated:
Advection accounting adds the measured advective fluxes (
HA_T,HA_Q,VAT) to the budget — it explains where the missing energy went. This is the physically correct treatment in the oasis regime and is what this library exists to do (seeadvection.compute_advection_fluxes()andadvection.apply_advection_correction()).Closure forcing rescales the measured turbulent fluxes so the budget shuts by construction. It is a pragmatic post-processing convention, not a physical correction, and it is wrong in the oasis case — see the caveat on
bowen_ratio_closure().
The two are offered here side by side so a user can compute either, but the
synthesis recommendation (CLAUDE.md) stands: prefer adding measured
advective fluxes; do not force Bowen-ratio closure when ``LE > (Rn - G)``.
WPL assumption¶
Every LE consumed or produced here is assumed to be already WPL
(Webb-Pearman-Leuning 1980) density-corrected. WPL is a mandatory, separate
pre-processing step for open-path LE/CO2 — not an advection or closure
correction — and this library does not apply it. See
advection.advection.wpl_latent_heat_flux() (a convenience helper; prefer
EddyPro / established processing) and the package README.
Sign / storage conventions (CLAUDE.md)¶
Surface energy balance with storage:
Rn - G - J = H + LE.Available energy:
Rn - G - J(J= air heat storage, Wang Eq. 11; default0).Sis used as the storage symbol in the closure-forcing signatures below to match the Twine et al. (2000) presentation; it plays the same role asJ.Closure residual:
Residual = Rn - G - J - H - LE(positive ⇒ available energy exceeds the turbulent sum, the usual under-closure gap). This is theCLAUDE.mdconvention and is the negative of the legacy'residual'diagnostic returned byadvection.compute_advection_fluxes(), which uses(H + LE) - (Rn - G); the sign difference is intentional and documented.Energy Balance Ratio:
EBR = sum(H + LE) / sum(Rn - G - J).
References
Twine, T. E., et al. (2000), Correcting eddy-covariance flux underestimates over a grassland, Agric. For. Meteorol. 103, 279-300 — the Bowen-ratio and residual closure methods. Wilson, K., et al. (2002), Energy balance closure at FLUXNET sites, Agric. For. Meteorol. 113, 223-243 — the closure-slope regression diagnostic. Wang et al. (2024); Moderow et al. (2021) — advection accounting and the OUT-positive sign convention.
- advection.closure.bowen_ratio_closure(Rn, G, H, LE, S=0.0, *, warn_oasis=True, singular_tol=1e-12)[source]
Force energy-balance closure by Bowen-ratio-preserving rescaling.
Implements the Bowen-ratio (BR) closure of Twine et al. (2000): both turbulent fluxes are multiplied by the same scale factor so their sum equals the available energy while their ratio — the Bowen ratio
beta = H / LE— is left unchanged:f = (Rn - G - S) / (H + LE) H_closed = f * H LE_closed = f * LE H_closed + LE_closed = Rn - G - S (closure by construction) H_closed / LE_closed = H / LE = beta (beta preserved)
Sis the storage term (air heat storageJand/or any other storage); passS=0(default) for the storage-free balanceRn - G = H + LE.Caution
Do NOT use Bowen-ratio closure in the oasis / advection regime — i.e. whenever
LE > (Rn - G - S)(equivalently the evaporative fractionEF = LE / (Rn - G) > 1). There, warm dry air advects extra energy into the control volume, so the trueLElegitimately exceeds the available energy and no rescaling of the measured fluxes can reproduce it: BR closure would shrinkLEtoward the available energy and is physically wrong. This is aCLAUDE.mdhard rule (never force Bowen-ratio closure when ``LE > (Rn - G)``). In that regime, add the measured advective fluxes instead (advection.compute_advection_fluxes(),advection.apply_advection_correction()); never close the budget by forcing the turbulent fluxes. AUserWarningis emitted (unlesswarn_oasis=False) when any step hasLE > (Rn - G - S).- Parameters:
Rn (float or array-like) – Net radiation [W/m^2].
G (float or array-like) – Ground heat flux (storage-corrected) [W/m^2].
H (float or array-like) – Measured sensible heat flux [W/m^2].
LE (float or array-like) – Measured latent heat flux [W/m^2]. Assumed already WPL (Webb-Pearman-Leuning 1980) density-corrected (a mandatory separate pre-step, not a closure fix; see
advection.advection.wpl_latent_heat_flux()).S (float or array-like, optional) – Storage term [W/m^2] (air heat storage
Jetc.); default0.warn_oasis (bool, optional) – If
True(default), emit aUserWarningwhen any step hasLE > (Rn - G - S)— the oasis case where BR closure is invalid.singular_tol (float, optional) – Magnitude of the turbulent sum
H + LEat or below which the scale factor is singular; those steps returnnan(default 1e-12).
- Returns:
Keys (each a float for scalar input, else a length-matched array):
'H': closed sensible heat fluxf * H[W/m^2].'LE': closed latent heat fluxf * LE[W/m^2].'factor': the common scale factorf(nanwhere singular).'beta': the preserved Bowen ratioH / LE(nanwhereLE == 0).H_closed / LE_closedequals this by construction.
- Return type:
dict
- Warns:
UserWarning – When
LE > (Rn - G - S)for any step (oasis regime; BR closure invalid), unlesswarn_oasis=False; and when the turbulent sumH + LEis withinsingular_tolof zero (factor undefined →nan).
References
Twine, T. E., et al. (2000), Agric. For. Meteorol. 103, 279-300 (BR closure).
- advection.closure.closure_slope(H, LE, Rn, G, J=0.0, *, force_origin=False)[source]
Closure-slope diagnostic via ordinary least-squares regression.
Regresses the turbulent flux on the available energy across the series (Wilson et al. 2002):
y = H + LE (dependent / turbulent sum) x = Rn - G - J (independent / available energy) y = slope * x + intercept
The slope is the standard scalar measure of energy-balance closure:
slope == 1withintercept == 0is perfect closure; eddy-covariance towers typically report slopes of ~0.8 (under-closure). A slope above 1 indicates over-closure — the oasis advective-input signature.Non-finite pairs (a
nanin eitherxory) are dropped before the fit.- Parameters:
H (array-like) – Sensible and latent heat flux series [W/m^2].
LE (array-like) – Sensible and latent heat flux series [W/m^2].
Rn (array-like) – Net radiation and ground heat flux series [W/m^2].
G (array-like) – Net radiation and ground heat flux series [W/m^2].
J (float or array-like, optional) – Air heat storage series [W/m^2]; default
0.force_origin (bool, optional) – If
True, constrain the fit through the origin (intercept == 0,slope = sum(x*y) / sum(x*x)). DefaultFalse(free intercept).
- Returns:
Keys:
'slope': regression slope (dimensionless).'intercept': regression intercept [W/m^2] (0.0whenforce_origin=True).'r_squared': coefficient of determination of the fit.'n': number of finite(x, y)pairs used.
- Return type:
dict
- Raises:
ValueError – If fewer than two finite
(x, y)pairs are available — a slope is then undefined.
References
Wilson, K., et al. (2002), Agric. For. Meteorol. 113, 223-243 (closure regression). Twine, T. E., et al. (2000).
- advection.closure.energy_balance_ratio(H, LE, Rn, G, J=0.0)[source]
Compute the Energy Balance Ratio (EBR) over a series.
Implements the
CLAUDE.mddefinition:EBR = sum(H + LE) / sum(Rn - G - J)
EBR is the bulk closure fraction over an averaging window:
EBR == 1is perfect closure,EBR < 1under-closure (the typical 0.8-0.9 of eddy-covariance towers), andEBR > 1over-closure (turbulent sum exceeds available energy — the oasis advective-input signature).Only timesteps where all of
H,LE,Rn,GandJare finite are summed (complete-case masking), so the numerator and denominator are always formed from the same sample and a gap in one term cannot bias the ratio.- Parameters:
H (float or array-like) – Sensible and latent heat flux series [W/m^2].
LE (float or array-like) – Sensible and latent heat flux series [W/m^2].
Rn (float or array-like) – Net radiation and ground heat flux series [W/m^2].
G (float or array-like) – Net radiation and ground heat flux series [W/m^2].
J (float or array-like, optional) – Air heat storage series [W/m^2]; default
0.
- Returns:
The energy balance ratio (dimensionless).
nanif no timestep has all components finite, or if the summed available energy is zero.- Return type:
float
- Warns:
UserWarning – When no complete-case timesteps remain, or the summed available energy
sum(Rn - G - J)is zero (EBR undefined →nan).
- advection.closure.energy_balance_residual(Rn, G, H, LE, J=0.0)[source]
Compute the energy-balance closure residual (a diagnostic, not a flux).
Uses the
CLAUDE.mdconvention:Residual = Rn - G - J - H - LE
A positive residual means the available energy
Rn - G - Jexceeds the turbulent sumH + LE— the usual eddy-covariance under-closure gap. A negative residual means over-closure (turbulent sum exceeds available energy), which in the oasis regime signals advective input (EF > 1).Note
This is the negative of the legacy
'residual'returned byadvection.compute_advection_fluxes()(which uses(H + LE) - (Rn - G)). The sign here followsCLAUDE.md; the difference is intentional. Either way the residual is a closure diagnostic only and must never be relabelled as an advective flux (CLAUDE.mdhard rule).- Parameters:
Rn (float or array-like) – Net radiation [W/m^2].
G (float or array-like) – Ground heat flux (storage-corrected) [W/m^2].
H (float or array-like) – Sensible heat flux [W/m^2].
LE (float or array-like) – Latent heat flux [W/m^2].
J (float or array-like, optional) – Air heat storage [W/m^2] (Wang Eq. 11); default
0for the storage-free balanceRn - G = H + LE.
- Returns:
Closure residual [W/m^2] (float for scalar input, else array).
- Return type:
float or numpy.ndarray
- advection.closure.residual_le_closure(Rn, G, H, S=0.0)[source]
Force closure by assigning the entire residual to latent heat (LE).
Implements the residual-LE closure of Twine et al. (2000): the measured sensible heat flux
His trusted as-is and the latent heat flux is set to whatever closes the budget:LE = Rn - G - S - H
Unlike
bowen_ratio_closure(), this does not preserve the Bowen ratio —His held fixed andLEabsorbs the full closure gap. It is the appropriate choice whenHis believed more reliable thanLE(e.g. open-pathLEwith WPL/spectral uncertainty), and the two methods bracket the plausible partition of the missing energy.Note
This is still closure forcing, not advection accounting. In the oasis regime the missing energy is genuine advective input, and attributing all of it to
LEinflates the latent flux rather than identifying the advection. Prefer the measured advective fluxes (advection.compute_advection_fluxes()) there. (Residual-LE closure is less pathological than Bowen-ratio closure in the oasis case — it does not collapseLE— but it is still a forced, non-physical partition.)- Parameters:
Rn (float or array-like) – Net radiation [W/m^2].
G (float or array-like) – Ground heat flux (storage-corrected) [W/m^2].
H (float or array-like) – Measured sensible heat flux [W/m^2] (held fixed).
S (float or array-like, optional) – Storage term [W/m^2] (air heat storage
Jetc.); default0.
- Returns:
Closed latent heat flux
LE = Rn - G - S - H[W/m^2] (float for scalar input, else a length-matched array). To be comparable with a measured open-pathLE, that measurement must be already WPL (Webb-Pearman-Leuning 1980) density-corrected (a mandatory separate pre-step; seeadvection.advection.wpl_latent_heat_flux()).- Return type:
float or numpy.ndarray
References
Twine, T. E., et al. (2000), Agric. For. Meteorol. 103, 279-300 (residual closure).