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.

Decadal Near-Surface Turbulent Flux Measurements from ARM

ARM Logo

Decadal Near-Surface Turbulent Flux Measurements from ARM


Overview

Within this notebook, we will cover the methodology and analysis within Sullivan et al. (2025) :

  1. History of near 3 decades of ARM’s near-surface turbulent flux observations

  2. Introduction to the Energy Balance Bowen Ratio (EBBR) and Eddy Covariance (EC) methods

  3. Comparison of Sensible and Latent Heat Fluxes from these methods for 2015 to 2023 period at SGP

Prerequisites

ConceptsImportanceNotes
ACT BasicsHelpfulBasic features
Matplotlib BasicsHelpfulBasic plotting
NumPy BasicsHelpfulBasic arrays
Xarray BasicsHelpfulMulti-dimensional arrays
  • Time to learn: 15 minutes


History of ARM’s Near-Surface Turbulent Flux Observations at SGP

Sensible and latent heat fluxes (H and LE) are critical to modulating the sources and sinks of the energy and water budgets across the land-atmosphere-biosphere interface.

In-situ measurements of H and LE are critical for understanding and predicting processes relevant for heat waves, droughts, argiculture and irrigation scheduling, and are necessary to capture the fine scale scales at which these processes occur.

The U.S. Department of Energy (DOE) Atmospheric Radiation Measurement (ARM) user facility has measured fluxes primarily using in-situ meteorologically driven energy balance flux gradient method with energy balance Bowen ratio (EBBR) system, the eddy covariance method (EC) with the carbon dioxide flux (CO2FLX), and the eddy correlation (ECOR) flux measurement system.

ARM began measuring fluxes in 1992 [1] using the EBBR system at the Southern Great Plains (SGP) atmospheric observatory, at ten grassland extended facilities (EF) within Oklahoma and Kansas and one EF on the northern edge of cropland. THe intention was that these facilities would be representative of a typical global climate model (GCM) grid cell.

The Energy Balence Bowen Ratio (EBBR) Method

The EBBR measures near-surface gradients of temperature and humidty[2] to approximate the Bowen Ratio:

βHLECpρλΔTΔρv,\beta \equiv \frac{H}{LE} \approx \frac{C_p \rho}{\lambda}\frac{\overline{\Delta T}}{\overline{\Delta \rho_v}},

where:

  • β\beta is the Bowen ratio

  • HH is the sensible heat flux

  • LELE is the latent heat flux

  • CpC_p is the specific heat of air (J kg1 K1\mathrm{J\ kg^{-1}\ K^{-1}})

  • ρ\rho is the density of air (kg m3\mathrm{kg\ m^{-3}})

  • λ\lambda is the latent heat of vaporization of water (or the latent heat of sublimation for frozen conditions) (J kg1\mathrm{J\ kg^{-1}})

  • ΔT\overline{\Delta T} is the mean temperature difference between the upper and lower sensors (K\mathrm{K})

  • Δρv\overline{\Delta \rho_v} is the mean difference in water vapor density between the upper and lower sensor (kg m3\mathrm{kg\ m^{-3}})

EBBR also measures net radiation, soil heat flow, soil temperature, and soil moisture[3].

Combining an equational form of a closed surface energy budget, where the sum of sensible and latent heat fluxes equals the net radiation less energy consumed as the ground heat flux.

The definition of the Bowen ratio as the ratio of sensible to latent heat flux gives equations for the sensible and latent heat fluxes as:

R+G=(H+LE+other components)R + G = -(H + LE + \text{other components})
H=(R+G)1+β1H = \frac{-(R + G)}{1 + \beta^{-1}}
LE=(R+G)β+1LE = \frac{-(R + G)}{\beta + 1}

where

  • RR is radiation

  • GG is surface soil heat flux

  • RR, GG, HH, and LELE are in W m2\mathrm{W\ m^{-2}}

  • “other components” are assumed to be null

The Eddy Covariance (EC) Method

The EC method estimates fluxes from the covariance of the vertical wind speed and the quantity of interest:

horizontal wind speed for momentum flux (τ\tau)

τ=ρwu\tau = \rho \overline{w' u'}

temperature for HH

H=CpρwTH = C_p \rho \overline{w' T'}

water vapor concentration for LELE

LE=λρwXvLE = \lambda \rho \overline{w' X_v'}

another scalar (for example, CO2_2 or CH4_4 concentration)

Fc=ρwXcF_c = \rho \overline{w' X_c'}

where:

  • ww' is the instantaneous perturbation, used herein as the departure of a given variable from its mean, of the vertical wind speed component (m s1\mathrm{m\ s^{-1}})

  • uu' is the instantaneous perturbation of the horizontal wind speed component (m s1\mathrm{m\ s^{-1}})

  • TT' is the instantaneous perturbation of temperature (K\mathrm{K})

  • XvX_v' is the instantaneous perturbation of the mixing ratio of water vapor in air (kg kg1\mathrm{kg\ kg^{-1}})

  • XcX_c' is the instantaneous perturbation of the mixing ratio of scalar cc in air (kg kg1\mathrm{kg\ kg^{-1}})

  • the overbar denotes a time-average operator

Accounting for the thermodynamic contribution of temperature fluctuations, LELE can be computed as:

LE=(1+μσ)[wρv+(ρvT)wT]LE = (1 + \mu \sigma)\left[\overline{w' \rho_v'} + \left(\frac{\rho_v}{T}\right)\overline{w' T'}\right]

where:

  • μ\mu is the ratio of molar masses of dry air and water vapor

  • σ\sigma is the ratio of the densities of water vapor and dry air

  • and T is the air temperature.

EBBR and EC Data Ingest

From Sullivan et al. (2025), the timelines for the EBBR and EC products:

CitationARM DatastreamTimeline
Sullivan et al. (1997)ECOR system2015 - 2019
Sullivan et al. (2019)ECOR with Smartflux2019 - 2026
Sullivan et al. (1993)EBBR2015 - 2023
import glob
import matplotlib.pyplot as plt

import act
from zoneinfo import ZoneInfo
# Define the path to the EBBR, ECOR, and EC datasets
base_path = "/Users/jrobrien/ARM/2026-Summer-School/"
ebbr_files = sorted(glob.glob(base_path + "sgp30ebbrE39.b1/*.nc"))
ecor_files = sorted(glob.glob(base_path + "sgp30ecorE39.b1/*.cdf"))
ecor_sf_files = sorted(glob.glob(base_path + "sgpecorsfE39.b1/*.nc"))
ds_ebbr = act.io.read_arm_netcdf(ebbr_files,
                                 keep_variables=["sensible_heat_flux", "latent_heat_flux", "temp_air_top", "wspd_vec_mean", "wdir_vec_mean"],
                                 cleanup_qc=True)
ds_ecor = act.io.read_arm_netcdf(ecor_files, 
                                 keep_variables=["h", "lv_e", "wind_dir", "wind_spd", "temp_irga"],
                                 cleanup_qc=True)
ds_ecor_sf = act.io.read_arm_netcdf(ecor_sf_files, 
                                    keep_variables=["sensible_heat_flux", "latent_flux", "wind_direction_from_north", "mean_wind", "air_temperature"],
                                    cleanup_qc=True)
/Users/jrobrien/.vscode-micromamba/envs/arm-summer-school-2026-dev/lib/python3.11/site-packages/act/io/arm.py:155: FutureWarning: In a future version of xarray the default value for join will change from join='outer' to join='exact'. This change will result in the following ValueError: cannot be aligned with join='exact' because index/labels/sizes are not equal along these coordinates (dimensions): 'time' ('time',) The recommendation is to set join explicitly for this case.
  ds = xr.open_mfdataset(filenames, **kwargs)
/Users/jrobrien/.vscode-micromamba/envs/arm-summer-school-2026-dev/lib/python3.11/site-packages/act/io/arm.py:155: FutureWarning: In a future version of xarray the default value for compat will change from compat='no_conflicts' to compat='override'. This is likely to lead to different results when combining overlapping variables with the same name. To opt in to new defaults and get rid of these warnings now use `set_options(use_new_combine_kwarg_defaults=True) or set compat explicitly.
  ds = xr.open_mfdataset(filenames, **kwargs)
/Users/jrobrien/.vscode-micromamba/envs/arm-summer-school-2026-dev/lib/python3.11/site-packages/act/io/arm.py:155: FutureWarning: In a future version of xarray the default value for compat will change from compat='no_conflicts' to compat='override'. This is likely to lead to different results when combining overlapping variables with the same name. To opt in to new defaults and get rid of these warnings now use `set_options(use_new_combine_kwarg_defaults=True) or set compat explicitly.
  ds = xr.open_mfdataset(filenames, **kwargs)
/Users/jrobrien/.vscode-micromamba/envs/arm-summer-school-2026-dev/lib/python3.11/site-packages/act/io/arm.py:155: FutureWarning: In a future version of xarray the default value for compat will change from compat='no_conflicts' to compat='override'. This is likely to lead to different results when combining overlapping variables with the same name. To opt in to new defaults and get rid of these warnings now use `set_options(use_new_combine_kwarg_defaults=True) or set compat explicitly.
  ds = xr.open_mfdataset(filenames, **kwargs)
/Users/jrobrien/.vscode-micromamba/envs/arm-summer-school-2026-dev/lib/python3.11/site-packages/act/io/arm.py:155: FutureWarning: In a future version of xarray the default value for compat will change from compat='no_conflicts' to compat='override'. This is likely to lead to different results when combining overlapping variables with the same name. To opt in to new defaults and get rid of these warnings now use `set_options(use_new_combine_kwarg_defaults=True) or set compat explicitly.
  ds = xr.open_mfdataset(filenames, **kwargs)
/Users/jrobrien/.vscode-micromamba/envs/arm-summer-school-2026-dev/lib/python3.11/site-packages/act/io/arm.py:155: FutureWarning: In a future version of xarray the default value for compat will change from compat='no_conflicts' to compat='override'. This is likely to lead to different results when combining overlapping variables with the same name. To opt in to new defaults and get rid of these warnings now use `set_options(use_new_combine_kwarg_defaults=True) or set compat explicitly.
  ds = xr.open_mfdataset(filenames, **kwargs)
/Users/jrobrien/.vscode-micromamba/envs/arm-summer-school-2026-dev/lib/python3.11/site-packages/act/qc/clean.py:243: RuntimeWarning: invalid value encountered in cast
  data = data.astype(dtype)

Sensible and Latent Heat Flux Diel Cycles

## --- Clean up the datasets ---
ds_ecor_corr = ds_ecor.where(ds_ecor['h'] > -200).where(ds_ecor['h'] < 2000)
ds_ebbr_corr = ds_ebbr.where(ds_ebbr['sensible_heat_flux'] > -200).where(ds_ebbr['sensible_heat_flux'] < 2000)
# --- Convert to Local time for Diel Cycles ---
# Create Timezone-aware index, converting to local time
chicago_tz = ZoneInfo("America/Chicago")
ecor_sf_index = ds_ecor_sf.indexes["time"].tz_localize("UTC").tz_convert(chicago_tz)
ecor_index = ds_ecor_corr.indexes["time"].tz_localize("UTC").tz_convert(chicago_tz)
ebbr_index = ds_ebbr_corr.indexes["time"].tz_localize("UTC").tz_convert(chicago_tz)

# Assign the new index back to the xarray object
ds_ecor_sf = ds_ecor_sf.assign_coords(time=ecor_sf_index)
ds_ecor_corr = ds_ecor_corr.assign_coords(time=ecor_index)
ds_ebbr_corr = ds_ebbr_corr.assign_coords(time=ebbr_index)

# Now remove the timezone aware information for groupby options
# Will still be in local time!
ds_ecor_sf["time"] = ds_ecor_sf["time"].data.tz_localize(None)
ds_ecor_corr["time"] = ds_ecor_corr["time"].data.tz_localize(None)
ds_ebbr_corr["time"] = ds_ebbr_corr["time"].data.tz_localize(None)
ds_ebbr_corr
Loading...
# --- Plot Diel Cycle using xarray grouby function ---
fig, axarr = plt.subplots(1, 1, figsize=(12, 5))

ds_ecor_sf['sensible_heat_flux'].groupby(ds_ecor_sf['time'].dt.hour).mean().plot(marker="o",
                                                                                 ax=axarr,
                                                                                 label="ECOR-SF")
ds_ecor_corr['h'].groupby(ds_ecor_corr['time'].dt.hour).mean().plot(marker="o",
                                                                    ax=axarr,
                                                                    label="ECOR")
ds_ebbr_corr['sensible_heat_flux'].groupby(ds_ebbr_corr['time'].dt.hour).mean().plot(marker="o",
                                                                                         ax=axarr,
                                                                                         label="EBBR")

axarr.set_ylabel(f"Sensible Heat Flux \n Away from the Surface (W $m^{{-2}}$)")
axarr.set_ylim([-150, 250])
axarr.legend(loc="upper left")
<Figure size 1200x500 with 1 Axes>

Diel Cycles per Crop Type

From Sullivan et al. (2025), at Southern Great Plains Extended Site (E39):

  • southerly (100-260°) fetch contains the cropland footprint

  • northerly (0-80 and 280-360°) fetch contains the ungrazed grass footprint

ecor_sf_crop = ds_ecor_sf.where((ds_ecor_sf['wind_direction_from_north'] >= 100) & (ds_ecor_sf['wind_direction_from_north'] <= 260), drop=True)
<Figure size 640x480 with 1 Axes>

Footnotes
  1. Location and Timeline of Flux Measurements at SGP from Sullivan et al. (2025)

    @sullivan_over_2025 Figure 1
  2. Gradients of temperature and humidity are measured above vegetation height using two sets of aspirated temperature and relative humidity (T /RH; Vaisala HMP45[4]) probes mounted with a vertical separation of 1 m.

  3. Net Radation is measured with Radiation and Energy Balance Systems (REBS), Inc, Q*7.1, REBS HFT-3 for soil heat flow, REBS STP-1 for soil temperature, and REBS SMP-2 for soil moisture.

References
  1. Sullivan, R. C., Billesbach, D. P., Biraud, S., Chan, S., Hart, R., Keeler, E., Kyrouac, J., Pal, S., Pekour, M., Sullivan, S. L., Theisen, A., Tuftedal, M., & Cook, D. R. (2025). Over three decades, and counting, of near-surface turbulent flux measurements from the Atmospheric Radiation Measurement (ARM) user facility. Earth System Science Data, 17(9), 5007–5038. 10.5194/essd-17-5007-2025
  2. Sullivan, R., Billesbach, D., Keeler, E., Ermold, B., & Pal, S. (1997). Eddy Correlation Flux Measurement System (30ECOR), 2015-10-06 to 2019-10-23, Southern Great Plains (SGP), Morrison, OK (Extended) (E39). In Atmospheric Radiation Measurement (ARM) user facility. 10.5439/1025039
  3. Sullivan, R., Cook, D., Shi, Y., Keeler, E., & Pal, S. (2019). Eddy Correlation Flux Measurement System (ECORSF), 2019-10-23 to 2026-03-12, Southern Great Plains (SGP), Morrison, OK (Extended) (E39). In Atmospheric Radiation Measurement (ARM) user facility. 10.5439/1494128
  4. Sullivan, R., Ermold, B., Pal, S., & Keeler, E. (1993). Energy Balance Bowen Ratio Station (30EBBR), 2015-09-30 to 2023-12-17, Southern Great Plains (SGP), Morrison, OK (Extended) (E39). In Atmospheric Radiation Measurement (ARM) user facility. 10.5439/1023895