heatwave3 detects marine heatwaves (MHWs) and cold-spells directly on gridded NetCDF data using the Hobday et al. (2016, 2018) definition. It is a ground-up C++ reimplementation of heatwaveR, designed for gridded datasets rather than individual time series.
Why heatwave3?
Working with large gridded SST datasets (such as OSTIA, OISST, ERA5, CMIP6) using heatwaveR requires looping over each pixel individually in R. For a 400 × 200 grid with 40 years of daily data, this takes over an hour. heatwave3 addresses this with:
- C++17 implementation of all core algorithms (climatology, event detection, categorisation, block averages)
- OpenMP parallelism across pixels, since each pixel is independent
- Direct NetCDF I/O via libnetcdf, with no R NetCDF packages needed at runtime
Performance
Benchmarked on the OSTIA South-West Indian Ocean reanalysis (400 × 200 grid, ca. 50,000 ocean pixels, 14,276 daily time steps), Apple M3 Pro:
| Method | Time | Speedup |
|---|---|---|
| heatwaveR (serial) | ca. 69 min | 1× |
| heatwave3 (1 thread) | 4.3 min | 16× |
| heatwave3 (12 threads) | 1.9 min | 36× |
For larger grids using daily files (Benguela region, 260 × 360 pixels, 16,049 daily OSTIA files, 12 threads): 3.4 minutes end-to-end.
Features
-
Climatology (
ts2clm3). Sliding-window percentile with Type-7 quantile, circular-padded rolling mean smoothing, and optional linear detrending (Jacox et al. 2020). -
Event detection (
detect_event3). Threshold exceedance, run-length encoding, gap joining, and 19 event metrics. Cold-spell support. Optional inline Hobday et al. (2018) severity categories. -
All-in-one pipeline (
detect3). Climatology, detection, and optional categories in a single call, withreturn_df = TRUEfor interactive use. -
Spatial blob detection (
detect_blob3). 3D connected-component labelling with voxel-level footprint output. -
Event categorisation (
category3). Hobday et al. (2018) Moderate/Strong/Severe/Extreme categories, for both heatwaves and cold-spells. -
Yearly aggregation (
block_average3) and static threshold exceedance (exceedance3). -
Plotting.
event_line3,geom_flame3,geom_lolli3, andplot_metric3(C++-backed spatial aggregation). - Flexible input. Single multi-timestep NetCDF, directory of daily files, or explicit file vector.
- Automatic dimension detection. Identifies lon/lat/time/SST from CF attributes, standard names, and units (works with GHRSST, OISST, OSTIA, ERA5, CMIP6, NEMO).
- Progress reporting. Percentage-complete updates during long climatology and detection runs.
- Multiple output formats. NetCDF always, plus optional CSV, RDA, or Parquet companion files.
- Numerical equivalence. Climatology and event metrics match heatwaveR to rounding precision, validated pixel-by-pixel.
Installation
Development version (recommended)
The dev branch contains the new C++ implementation:
# install.packages("remotes")
remotes::install_github("robwschlegel/heatwave3")System requirements
heatwave3 requires the netCDF C library (version 4.0+). The configure script finds it automatically via nc-config or pkg-config.
macOS:
Ubuntu / Debian:
Fedora / RHEL:
OpenMP parallelism
On Linux, OpenMP works without extra setup. On macOS, the configure script probes for a working OpenMP runtime (Apple Clang with R’s own bundled libomp, Homebrew libomp, or user-supplied flags) using a compile-link-run test. If none is found, heatwave3 falls back to single-threaded mode.
To install Homebrew’s libomp (may help on some macOS configurations):
Thread management
heatwave3 defaults to 50% of available cores (overridable via R_HEATWAVE3_NUM_THREADS). Control threads at the session level or per function call:
library(heatwave3)
getHW3threads() # check current default
setHW3threads(8) # use 8 threads for all subsequent calls
setHW3threads(0) # reset to default (50% of cores)
# Or override per call:
detect_event3(..., n_threads = 12)heatwave3 never calls omp_set_num_threads(), so it does not change thread counts for other OpenMP-using packages. It is also safe under parallel::mclapply() (fork-safe via pthread_atfork).
Quick start
library(heatwave3)
sst_file <- "path/to/sst.nc" # or a directory of daily files
clim_file <- tempfile(fileext = ".nc")
event_file <- tempfile(fileext = ".nc")
# All-in-one: climatology + event detection + categories
events <- detect3(
file_in = sst_file,
file_out_clim = clim_file,
file_out_event = event_file,
climatologyPeriod = c("1991-01-01", "2020-12-31"),
lon_range = c(15, 35),
lat_range = c(-38, -28),
category = TRUE,
hemisphere = "south",
return_df = TRUE,
n_threads = 4
)
head(events)
table(events$category)Cold-spell detection
# Use pctile = 10 for the cold tail
events_cold <- detect3(
file_in = sst_file,
file_out_clim = "clim_10pct.nc",
file_out_event = "events_cold.nc",
climatologyPeriod = c("1991-01-01", "2020-12-31"),
pctile = 10,
coldSpells = TRUE,
category = TRUE,
hemisphere = "south",
return_df = TRUE,
n_threads = 12
)
table(events_cold$category)Per-pixel time series plot
event_line3(sst_file, clim_file, lon = 25.0, lat = -34.0,
start_date = "2018-01-01", end_date = "2019-12-31")Spatial map of peak intensity
# C++-backed per-pixel aggregation, efficient even for millions of events
plot_metric3(event_file, metric = "intensity_max", summary = "mean")Spatial blob detection
blobs <- detect_blob3(
sst_file = sst_file,
clim_file = clim_file,
minVoxels = 200,
topN = 6,
return = c("event", "daily", "voxel")
)
# blobs$event: summary per blob (duration, peak area, cumulative intensity)
# blobs$daily: daily progression (area, centroid, bounding box)
# blobs$voxel: full 3D footprint (for spatial maps)API overview
All functions are suffixed with 3 to avoid namespace conflicts with heatwaveR:
| Function | Purpose |
|---|---|
detect3() |
Primary entry point. Climatology, detection, and optional categories |
ts2clm3() |
Compute climatology (NetCDF → NetCDF) |
detect_event3() |
Detect per-pixel events (with optional inline categories) |
detect_blob3() |
3D spatial blob detection |
category3() |
Hobday et al. (2018) event categories (reads or computes) |
block_average3() |
Yearly aggregation of event metrics |
exceedance3() |
Static threshold exceedance |
event_line3() |
Per-pixel time series plot |
geom_flame3() |
ggplot2 flame polygon geom |
geom_lolli3() |
ggplot2 lollipop geom |
plot_metric3() |
Spatial map of event metrics (C++-backed aggregation) |
hw3_export() |
Export NetCDF output to CSV/RDA/Parquet |
Vignettes
- Getting started. Full pipeline walkthrough with spatial blob figures.
- NetCDF output internals. Output file structure, CF compliance, and reading in R/Python/CDO.
- Performance benchmark. heatwaveR versus heatwave3 timing comparison.
- Parallel performance. OpenMP threads versus R-side parallelism, with per-platform setup.
Citation
If you use heatwave3 in published research, please cite both:
- Hobday, A.J., et al. (2016). A hierarchical approach to defining marine heatwaves. Progress in Oceanography, 141, 227–238.
- Hobday, A.J., et al. (2018). Categorizing and naming marine heatwaves. Oceanography, 31(2), 162–173.
Code of Conduct
Please note that the heatwave3 project is released with a Contributor Code of Conduct. By contributing to this project, you agree to abide by its terms.
