
Decadal Near-Surface Turbulent Flux Measurements from ARM¶
Overview¶
Within this notebook, we will cover the methodology and analysis within Sullivan et al. (2025) :
History of near 3 decades of ARM’s near-surface turbulent flux observations
Introduction to the Energy Balance Bowen Ratio (EBBR) and Eddy Covariance (EC) methods
Comparison of Sensible and Latent Heat Fluxes from these methods for 2015 to 2023 period at SGP
Prerequisites¶
| Concepts | Importance | Notes |
|---|---|---|
| ACT Basics | Helpful | Basic features |
| Matplotlib Basics | Helpful | Basic plotting |
| NumPy Basics | Helpful | Basic arrays |
| Xarray Basics | Helpful | Multi-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:
where:
is the Bowen ratio
is the sensible heat flux
is the latent heat flux
is the specific heat of air ()
is the density of air ()
is the latent heat of vaporization of water (or the latent heat of sublimation for frozen conditions) ()
is the mean temperature difference between the upper and lower sensors ()
is the mean difference in water vapor density between the upper and lower sensor ()
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:
where
is radiation
is surface soil heat flux
, , , and are in
“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 ()
temperature for
water vapor concentration for
another scalar (for example, CO or CH concentration)
where:
is the instantaneous perturbation, used herein as the departure of a given variable from its mean, of the vertical wind speed component ()
is the instantaneous perturbation of the horizontal wind speed component ()
is the instantaneous perturbation of temperature ()
is the instantaneous perturbation of the mixing ratio of water vapor in air ()
is the instantaneous perturbation of the mixing ratio of scalar in air ()
the overbar denotes a time-average operator
Accounting for the thermodynamic contribution of temperature fluctuations, can be computed as:
where:
is the ratio of molar masses of dry air and water vapor
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:
| Citation | ARM Datastream | Timeline |
|---|---|---|
| Sullivan et al. (1997) | ECOR system | 2015 - 2019 |
| Sullivan et al. (2019) | ECOR with Smartflux | 2019 - 2026 |
| Sullivan et al. (1993) | EBBR | 2015 - 2023 |
import glob
import matplotlib.pyplot as plt
import actfrom 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# --- 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")
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)
Location and Timeline of Flux Measurements at SGP from Sullivan et al. (2025)

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.
- 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
- 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
- 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
- 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