{"id":41210,"name":"heatwave3","description":"The Goal of this package is to make life easier when working with the Hobday et al. marine heatwave definition on large datasets.","url":"https://github.com/robwschlegel/heatwave3","last_synced_at":"2026-06-15T04:30:20.617Z","repository":{"id":184651090,"uuid":"672252717","full_name":"robwschlegel/heatwave3","owner":"robwschlegel","description":"Apply the Hobday et al. (2016, 2018) marine heatwave definition directly to NetCDF files.","archived":false,"fork":false,"pushed_at":"2026-06-05T11:42:07.000Z","size":7145,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":5,"default_branch":"main","last_synced_at":"2026-06-06T00:05:32.164Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://robwschlegel.github.io/heatwave3/","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/robwschlegel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-29T12:45:06.000Z","updated_at":"2026-06-05T11:42:11.000Z","dependencies_parsed_at":"2023-07-29T14:35:06.737Z","dependency_job_id":"2d819721-6a3d-4354-b72d-fea26fabeaad","html_url":"https://github.com/robwschlegel/heatwave3","commit_stats":{"total_commits":44,"total_committers":4,"mean_commits":11.0,"dds":"0.20454545454545459","last_synced_commit":"ce16c4c183074a658675b78d80b3024eb080e07b"},"previous_names":["robwschlegel/heatwave3"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/robwschlegel/heatwave3","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robwschlegel%2Fheatwave3","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robwschlegel%2Fheatwave3/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robwschlegel%2Fheatwave3/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robwschlegel%2Fheatwave3/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robwschlegel","download_url":"https://codeload.github.com/robwschlegel/heatwave3/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robwschlegel%2Fheatwave3/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34043822,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-07T02:00:07.652Z","response_time":124,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"owner":{"login":"robwschlegel","name":"Robert William Schlegel","uuid":"7559399","kind":"user","description":"Data scientist for FACE-IT. What are the key drivers of change in Arctic fjord systems, and what is humanities role?","email":"","website":"https://theoceancode.netlify.com/","location":"Villefranche, France","twitter":"robwschlegel","company":"Institut de la mer de Villefranche","icon_url":"https://avatars.githubusercontent.com/u/7559399?u=95859736f0af87fb84c542a191062b56a6e977fa\u0026v=4","repositories_count":31,"last_synced_at":"2024-06-11T16:00:21.026Z","metadata":{"has_sponsors_listing":false},"html_url":"https://github.com/robwschlegel","funding_links":[],"total_stars":67,"followers":64,"following":21,"created_at":"2022-11-12T12:20:45.182Z","updated_at":"2024-06-11T16:00:22.978Z","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robwschlegel","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robwschlegel/repositories"},"packages":[],"commits":{"id":1351797,"full_name":"robwschlegel/heatwave3","default_branch":"main","total_commits":63,"total_committers":5,"total_bot_commits":0,"total_bot_committers":0,"mean_commits":12.6,"dds":0.4285714285714286,"past_year_total_commits":19,"past_year_total_committers":2,"past_year_total_bot_commits":0,"past_year_total_bot_committers":0,"past_year_mean_commits":9.5,"past_year_dds":0.052631578947368474,"last_synced_at":"2026-06-12T04:45:51.290Z","last_synced_commit":"194b0a6bd7d4b311294753271bdcabc2b56952f5","created_at":"2023-09-12T11:04:34.930Z","updated_at":"2026-06-12T04:45:22.671Z","committers":[{"name":"Robert William Schlegel","email":"robwschlegel@gmail.com","login":"robwschlegel","count":36},{"name":"AJ Smit","email":"ajsmit@uwc.ac.za","login":"ajsmit","count":18},{"name":"GuiSPinto","email":"kzagrande@gmail.com","login":"GuiSPinto","count":6},{"name":"Robert","email":"robert.schlegel@imev-mer.fr","login":null,"count":2},{"name":"Lily Genevier","email":"69518865+lilygen","login":"lilygen","count":1}],"past_year_committers":[{"name":"AJ Smit","email":"ajsmit@uwc.ac.za","login":"ajsmit","count":18},{"name":"Robert Schlegel","email":"robert@ecomonitor.info","login":"robwschlegel","count":1}],"commits_url":"https://commits.ecosyste.ms/api/v1/hosts/GitHub/repositories/robwschlegel%2Fheatwave3/commits","host":{"name":"GitHub","url":"https://github.com","kind":"github","last_synced_at":"2026-06-14T00:00:10.507Z","repositories_count":6259421,"commits_count":882448301,"contributors_count":35083943,"owners_count":1166452,"icon_url":"https://github.com/github.png","host_url":"https://commits.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://commits.ecosyste.ms/api/v1/hosts/GitHub/repositories"}},"issues_stats":{"full_name":"robwschlegel/heatwave3","html_url":"https://github.com/robwschlegel/heatwave3","last_synced_at":"2026-03-15T08:47:51.937Z","status":"error","issues_count":2,"pull_requests_count":0,"avg_time_to_close_issue":6947428.0,"avg_time_to_close_pull_request":null,"issues_closed_count":2,"pull_requests_closed_count":0,"pull_request_authors_count":0,"issue_authors_count":2,"avg_comments_per_issue":9.0,"avg_comments_per_pull_request":null,"merged_pull_requests_count":0,"bot_issues_count":0,"bot_pull_requests_count":0,"past_year_issues_count":0,"past_year_pull_requests_count":0,"past_year_avg_time_to_close_issue":null,"past_year_avg_time_to_close_pull_request":null,"past_year_issues_closed_count":0,"past_year_pull_requests_closed_count":0,"past_year_pull_request_authors_count":0,"past_year_issue_authors_count":0,"past_year_avg_comments_per_issue":null,"past_year_avg_comments_per_pull_request":null,"past_year_bot_issues_count":0,"past_year_bot_pull_requests_count":0,"past_year_merged_pull_requests_count":0,"created_at":"2023-09-12T11:04:45.561Z","updated_at":"2026-03-15T08:47:51.937Z","repository_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/repositories/robwschlegel%2Fheatwave3","issues_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/repositories/robwschlegel%2Fheatwave3/issues","issue_labels_count":{},"pull_request_labels_count":{},"issue_author_associations_count":{"NONE":2},"pull_request_author_associations_count":{},"issue_authors":{"geografif":1,"aonojeghuo":1},"pull_request_authors":{},"host":{"name":"GitHub","url":"https://github.com","kind":"github","last_synced_at":"2026-06-14T00:00:24.059Z","repositories_count":14800372,"issues_count":33150415,"pull_requests_count":109388113,"authors_count":11305301,"icon_url":"https://github.com/github.png","host_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/repositories","owners_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/owners","authors_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/authors"},"past_year_issue_labels_count":{},"past_year_pull_request_labels_count":{},"past_year_issue_author_associations_count":{},"past_year_pull_request_author_associations_count":{},"past_year_issue_authors":{},"past_year_pull_request_authors":{},"maintainers":[],"active_maintainers":[]},"events":{"total":{"WatchEvent":2,"PushEvent":7},"last_year":{"PushEvent":7}},"keywords":[],"dependencies":[{"ecosystem":"actions","filepath":".github/workflows/R-CMD-check.yaml","sha":null,"kind":"manifest","created_at":"2023-07-29T14:35:06.060Z","updated_at":"2023-07-29T14:35:06.060Z","repository_link":"https://github.com/robwschlegel/heatwave3/blob/main/.github/workflows/R-CMD-check.yaml","dependencies":[{"id":12201351541,"package_name":"actions/checkout","ecosystem":"actions","requirements":"v3","direct":true,"kind":"composite","optional":false},{"id":12201351542,"package_name":"r-lib/actions/setup-pandoc","ecosystem":"actions","requirements":"v2","direct":true,"kind":"composite","optional":false},{"id":12201351543,"package_name":"r-lib/actions/setup-r","ecosystem":"actions","requirements":"v2","direct":true,"kind":"composite","optional":false},{"id":12201351544,"package_name":"r-lib/actions/setup-r-dependencies","ecosystem":"actions","requirements":"v2","direct":true,"kind":"composite","optional":false},{"id":12201351545,"package_name":"r-lib/actions/check-r-package","ecosystem":"actions","requirements":"v2","direct":true,"kind":"composite","optional":false}]},{"ecosystem":"cran","filepath":"DESCRIPTION","sha":null,"kind":"manifest","created_at":"2023-07-29T14:35:06.321Z","updated_at":"2023-07-29T14:35:06.321Z","repository_link":"https://github.com/robwschlegel/heatwave3/blob/main/DESCRIPTION","dependencies":[{"id":12201351572,"package_name":"R","ecosystem":"cran","requirements":"\u003e= 2.10","direct":true,"kind":"depends","optional":false},{"id":12201351573,"package_name":"heatwaveR","ecosystem":"cran","requirements":"*","direct":true,"kind":"imports","optional":false},{"id":12201351574,"package_name":"terra","ecosystem":"cran","requirements":"*","direct":true,"kind":"imports","optional":false},{"id":12201351575,"package_name":"testthat","ecosystem":"cran","requirements":"\u003e= 3.0.0","direct":true,"kind":"suggests","optional":false}]}],"score":3.8066624897703196,"created_at":"2023-09-12T07:50:01.697Z","updated_at":"2026-06-15T04:30:20.633Z","avatar_url":"https://github.com/robwschlegel.png","language":"C++","category":"Hydrosphere","sub_category":"Ocean Carbon and Temperature","monthly_downloads":0,"total_dependent_repos":0,"total_dependent_packages":0,"readme":"\n\n# heatwave3 \u003cimg src=\"logo.png\" width=200 align=\"right\" /\u003e\n\n\u003c!-- badges: start --\u003e\n[![R-CMD-check](https://github.com/robwschlegel/heatwave3/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/robwschlegel/heatwave3/actions/workflows/R-CMD-check.yaml)\n[![Codecov test coverage](https://codecov.io/gh/robwschlegel/heatwave3/branch/main/graph/badge.svg)](https://app.codecov.io/gh/robwschlegel/heatwave3?branch=main)\n\u003c!-- badges: end --\u003e\n\n**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**](https://robwschlegel.github.io/heatwaveR/), designed for gridded datasets rather than individual time series.\n\n## Why heatwave3?\n\nWorking with large gridded SST datasets (such as OSTIA, OISST, ERA5, CMIP6) using heatwaveR requires looping over each pixel individually in R. For a 400 \u0026times; 200 grid with 40 years of daily data, this takes over an hour. **heatwave3** addresses this with:\n\n1. **C++17 implementation** of all core algorithms (climatology, event detection, categorisation, block averages)\n2. **OpenMP parallelism** across pixels, since each pixel is independent\n3. **Direct NetCDF I/O** via libnetcdf, with no R NetCDF packages needed at runtime\n\n### Performance\n\nBenchmarked on the OSTIA South-West Indian Ocean reanalysis (400 \u0026times; 200 grid, ca. 50,000 ocean pixels, 14,276 daily time steps), Apple M5 Max:\n\n| Method | Time | Speedup |\n|--------|------|---------|\n| heatwaveR (serial) | ca. 69 min | 1\u0026times; |\n| heatwave3 (1 thread) | 4.3 min | **16\u0026times;** |\n| heatwave3 (12 threads) | 1.9 min | **36\u0026times;** |\n\nFor larger grids using daily files (Benguela region, 260 \u0026times; 360 pixels, 16,049 daily OSTIA files, 12 threads): **3.4 minutes** end-to-end.\n\n## Features\n\n- **Climatology** (`ts2clm3`). Sliding-window percentile with Type-7 quantile, circular-padded rolling mean smoothing, and optional linear detrending (Jacox et al. 2020).\n- **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.\n- **All-in-one pipeline** (`detect3`). Climatology, detection, and optional categories in a single call from one `name` stem; read results back with `hw3_export()`.\n- **Spatial blob detection** (`detect_blob3`). 3D connected-component labelling with voxel-level footprint output.\n- **Event categorisation** (`category3`). Hobday et al. (2018) Moderate/Strong/Severe/Extreme categories, for both heatwaves and cold-spells.\n- **Yearly aggregation** (`block_average3`) and **static threshold exceedance** (`exceedance3`).\n- **Plotting**. `event_line3`, `geom_flame3`, `geom_lolli3`, and `plot_metric3` (C++-backed spatial aggregation).\n- **Flexible input**. Single multi-timestep NetCDF, directory of daily files, or explicit file vector.\n- **Automatic dimension detection**. Identifies lon/lat/time/SST from CF attributes, standard names, and units (works with GHRSST, OISST, OSTIA, ERA5, CMIP6, NEMO).\n- **Progress reporting**. Percentage-complete updates during long climatology and detection runs.\n- **NetCDF-native output, exported on demand**. The compute functions always write gridded NetCDF; `hw3_export()` reads any product back into R or converts it to CSV, RDS, or Parquet, with optional variable and lon/lat/time subsetting.\n- **Numerical equivalence**. Climatology and event metrics match heatwaveR to rounding precision, validated pixel-by-pixel.\n\n## Installation\n\n### Development version (recommended)\n\nThe `dev` branch contains the new C++ implementation:\n\n```r\n# install.packages(\"remotes\")\nremotes::install_github(\"robwschlegel/heatwave3\")\n```\n\n### System requirements\n\nheatwave3 requires the **netCDF C library** (version 4.0+). The `configure` script finds it automatically via `nc-config` or `pkg-config`.\n\n**macOS:**\n```bash\nbrew install netcdf\n```\n\n**Ubuntu / Debian:**\n```bash\nsudo apt install libnetcdf-dev\n```\n\n**Fedora / RHEL:**\n```bash\nsudo dnf install netcdf-devel\n```\n\n### OpenMP parallelism\n\nOn 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.\n\nTo install Homebrew's libomp (may help on some macOS configurations):\n\n```bash\nbrew install libomp\n```\n\n### Thread management\n\nheatwave3 defaults to **50% of available cores** (overridable via `R_HEATWAVE3_NUM_THREADS`). Control threads at the session level or per function call:\n\n```r\nlibrary(heatwave3)\ngetHW3threads()       # check current default\nsetHW3threads(8)      # use 8 threads for all subsequent calls\nsetHW3threads(0)      # reset to default (50% of cores)\n\n# Or override per call:\ndetect_event3(..., n_threads = 12)\n```\n\nheatwave3 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`).\n\n## Quick start\n\n```r\nlibrary(heatwave3)\n\nsst_file \u003c- \"path/to/sst.nc\"  # or a directory of daily files\n\n# All-in-one: climatology + event detection + categories. A single 'name' stem\n# writes benguela_clim.nc and benguela_events.nc.\ndetect3(\n  file_in           = sst_file,\n  name              = \"benguela\",\n  climatologyPeriod = c(\"1991-01-01\", \"2020-12-31\"),\n  lon_range         = c(15, 35),\n  lat_range         = c(-38, -28),\n  category          = TRUE,\n  hemisphere        = \"south\",\n  n_threads         = 4\n)\n\n# Read the events back as a data.frame (or a quick preview with n =)\nevents \u003c- hw3_export(\"benguela_events.nc\")\nhead(events)\ntable(events$category)\n```\n\n### Cold-spell detection\n\n```r\n# Use pctile = 10 for the cold tail\ndetect3(\n  file_in           = sst_file,\n  name              = \"benguela_cold\",\n  climatologyPeriod = c(\"1991-01-01\", \"2020-12-31\"),\n  pctile            = 10,\n  coldSpells        = TRUE,\n  category          = TRUE,\n  hemisphere        = \"south\",\n  n_threads         = 12\n)\n\ntable(hw3_export(\"benguela_cold_events.nc\")$category)\n```\n\n### Per-pixel time series plot\n\n```r\nevent_line3(sst_file, \"benguela_clim.nc\", lon = 25.0, lat = -34.0,\n            start_date = \"2018-01-01\", end_date = \"2019-12-31\")\n```\n\n### Spatial map of peak intensity\n\n```r\n# C++-backed per-pixel aggregation, efficient even for millions of events\nplot_metric3(\"benguela_events.nc\", metric = \"intensity_max\", summary = \"mean\")\n```\n\n### Event categories (standalone)\n\n```r\n# Reads pre-computed categories from event file (no clim_file needed)\ncats \u003c- category3(\"benguela_events.nc\")\ntable(cats$category)\n\n# Or compute from scratch for older event files\ncats \u003c- category3(\"benguela_events.nc\", \"benguela_clim.nc\", hemisphere = \"south\")\n```\n\n### Using daily files\n\n```r\n# Pass a directory. All .nc/.nc4 files are read, sorted, and merged\nts2clm3(\n  file_in = \"/path/to/daily_ostia/\",\n  name = \"ostia\",\n  climatologyPeriod = c(\"1991-01-01\", \"2020-12-31\"),\n  n_threads = 12\n)\n```\n\n### Spatial blob detection\n\n```r\nblobs \u003c- detect_blob3(\n  sst_file  = sst_file,\n  clim_file = \"benguela_clim.nc\",\n  minVoxels = 200,\n  topN      = 6,\n  return    = c(\"event\", \"daily\", \"voxel\")\n)\n\n# blobs$event: summary per blob (duration, peak area, cumulative intensity)\n# blobs$daily: daily progression (area, centroid, bounding box)\n# blobs$voxel: full 3D footprint (for spatial maps)\n```\n\n### Detrended climatology\n\n```r\n# Remove linear warming trend before computing climatology\n# (Jacox et al. 2020 approach)\nts2clm3(sst_file, name = \"benguela_detrended\",\n        climatologyPeriod = c(\"1991-01-01\", \"2020-12-31\"),\n        detrend = TRUE)\n```\n\n## API overview\n\nAll functions are suffixed with `3` to avoid namespace conflicts with heatwaveR:\n\n| Function | Purpose |\n|----------|---------|\n| `detect3()` | **Primary entry point**. Climatology, detection, and optional categories |\n| `ts2clm3()` | Compute climatology (NetCDF \u0026rarr; NetCDF) |\n| `detect_event3()` | Detect per-pixel events (with optional inline categories) |\n| `detect_blob3()` | 3D spatial blob detection |\n| `category3()` | Hobday et al. (2018) event categories (reads or computes) |\n| `block_average3()` | Yearly aggregation of event metrics |\n| `exceedance3()` | Static threshold exceedance |\n| `event_line3()` | Per-pixel time series plot |\n| `geom_flame3()` | ggplot2 flame polygon geom |\n| `geom_lolli3()` | ggplot2 lollipop geom |\n| `plot_metric3()` | Spatial map of event metrics (C++-backed aggregation) |\n| `hw3_export()` | Read any product into a data.frame, or export to CSV/RDS/Parquet |\n\n## Vignettes\n\n- **Getting started**. Full pipeline walkthrough with spatial blob figures.\n- **NetCDF output internals**. Output file structure, CF compliance, and reading in R/Python/CDO.\n- **Performance benchmark**. heatwaveR versus heatwave3 timing comparison.\n- **Parallel performance**. OpenMP threads versus R-side parallelism, with per-platform setup.\n\n## Citation\n\nIf you use heatwave3 in published research, please cite both:\n\n- Hobday, A.J., et al. (2016). A hierarchical approach to defining marine heatwaves. *Progress in Oceanography*, 141, 227\u0026ndash;238.\n- Hobday, A.J., et al. (2018). Categorizing and naming marine heatwaves. *Oceanography*, 31(2), 162\u0026ndash;173.\n\n## Code of Conduct\n\nPlease note that the heatwave3 project is released with a [Contributor Code of Conduct](https://contributor-covenant.org/version/2/1/CODE_OF_CONDUCT.html). By contributing to this project, you agree to abide by its terms.\n","funding_links":[],"readme_doi_urls":[],"works":{},"citation_counts":{},"total_citations":0,"keywords_from_contributors":[],"project_url":"https://ost.ecosyste.ms/api/v1/projects/41210","html_url":"https://ost.ecosyste.ms/projects/41210"}