| Title: | Actuarial Climate Index |
|---|---|
| Description: | Computes the Actuarial Climate Index (ACI) and its components (temperature, precipitation, drought, wind, sea level) from gridded climate data ('NetCDF') and tide gauge records. Implements the methodology described in Garrido, Milhaud & Olympio (2025) <https://hal.science/hal-04491982v2> for a French/European actuarial climate index, building on the American Academy of Actuaries framework, to any country in the world. |
| Authors: | Xavier Milhaud [aut, cre, ctb], Dabakh Kane [ctb], Esteban Mauboussin [ctb] |
| Maintainer: | Xavier Milhaud <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 1.1.0 |
| Built: | 2026-10-11 11:44:11 UTC |
| Source: | https://github.com/xaviermilhaud/xaci |
Once data has been reduced to daily (or coarser) resolution, it is small
enough to hand off to the rest of the existing base-R pipeline
(resample_monthly(), standardize_metric(),
.compute_aci_grid(), etc.) unchanged. This is the bridge between the
terra-based loading/reduction steps and that pipeline.
.spatraster_to_list(r).spatraster_to_list(r)
r |
A |
A list with elements data ([lon x lat x time] array),
lon, lat, time – the same structure produced by
load_netcdf().
Computes the full Actuarial Climate Index by combining all five components: temperature (T90 and T10), precipitation, drought, wind, and sea level.
Aggregate a monthly data frame to a given temporal granularity
aggregate_granularity(df, granularity = "month")aggregate_granularity(df, granularity = "month")
df |
A data.frame with "YYYY-MM-01" Date row names (monthly). |
granularity |
One of "month", "season", "semester", "year". |
A data.frame with aggregated values and appropriate row names.
Produces a GIF animation (via gganimate) showing the evolution of a spatial variable at each time step. Requires gganimate and gifski (or png) to be installed.
animate_aci_map( data, variable = "ACI", country_abbrev = NULL, admin_level = NULL, crs_metric = 4326, var_label = "Standardised anomaly", title = "ACI over time", palette = "RdBu", reverse = TRUE, n_breaks = 9, borders = TRUE, fps = 4, width = 800, height = 600, save_path = NULL )animate_aci_map( data, variable = "ACI", country_abbrev = NULL, admin_level = NULL, crs_metric = 4326, var_label = "Standardised anomaly", title = "ACI over time", palette = "RdBu", reverse = TRUE, n_breaks = 9, borders = TRUE, fps = 4, width = 800, height = 600, save_path = NULL )
data |
Grid-cell list or admin |
variable |
Variable to animate. Default |
country_abbrev |
Three-letter ISO code. |
admin_level |
Integer or |
crs_metric |
EPSG code, used for admin choropleth projection only.
Default |
var_label |
Colour-bar label. Default |
title |
Plot title. Default |
palette |
RColorBrewer palette. Default |
reverse |
Logical. Default |
n_breaks |
Integer. Default |
borders |
Logical. Default |
fps |
Frames per second. Default |
width |
Output width in pixels. Default |
height |
Output height in pixels. Default |
save_path |
File path for the output GIF. If |
A gganimate animation object (invisibly).
dontrun
This example is wrapped in dontrun because it depends on the
optional gganimate and gifski packages (declared in
Suggests, not guaranteed to be installed on every machine
running R CMD check), and because rendering a multi-frame GIF
takes well over 5 seconds, making it unsuitable for donttest as
well.
## Not run: lon <- seq(-5, 8, length.out = 10) lat <- seq(42, 51, length.out = 8) arr <- array(stats::rnorm(10 * 8 * 5), dim = c(10, 8, 5)) grid <- list(ACI = arr, lon = lon, lat = lat, time = seq_len(5)) animate_aci_map(grid, variable = "ACI", country_abbrev = "FRA", fps = 2, save_path = file.path(tempdir(), "aci_animation.gif")) ## End(Not run)## Not run: lon <- seq(-5, 8, length.out = 10) lat <- seq(42, 51, length.out = 8) arr <- array(stats::rnorm(10 * 8 * 5), dim = c(10, 8, 5)) grid <- list(ACI = arr, lon = lon, lat = lat, time = seq_len(5)) animate_aci_map(grid, variable = "ACI", country_abbrev = "FRA", fps = 2, save_path = file.path(tempdir(), "aci_animation.gif")) ## End(Not run)
Sets grid cells to NA where the mask value is below threshold.
apply_mask(dataset, mask_path, var_name, threshold = 0.8)apply_mask(dataset, mask_path, var_name, threshold = 0.8)
dataset |
List returned by |
mask_path |
Path to the mask NetCDF file (variable: |
var_name |
Name of the variable inside |
threshold |
Numeric threshold; cells with mask < threshold become NA. Default 0.8. |
The dataset list with data masked in-place.
Equivalent of apply_mask(): sets cells to NA where the mask
value is below threshold. Processed block-by-block by terra.
apply_mask_terra(r, mask_path, threshold = 0.8, chunk_size = 20000)apply_mask_terra(r, mask_path, threshold = 0.8, chunk_size = 20000)
r |
A |
mask_path |
Path to the mask NetCDF file (variable: |
threshold |
Numeric threshold. Default |
chunk_size |
Nombre max de couches traitees par bloc quand
|
Note sur la limite de 65535 couches : le format interne utilise
par terra::mask() pour ecrire son resultat sur disque (fichier
temporaire) ne supporte pas plus de 65535 couches en sortie. Au-dela (cas
frequent en donnees horaires sur plusieurs decennies : ~300k couches pour
35 ans), on ne peut pas masquer l'objet en un seul appel. Cette fonction
bascule donc automatiquement sur un traitement par blocs temporels
('chunk_size' couches a la fois), ecrits en GeoTIFF (BigTIFF) puis
recombines en une seule source multi-fichiers, sans jamais materialiser
l'ensemble en RAM. Le masque etant purement spatial (identique a chaque
pas de temps), le decoupage en blocs ne change pas le resultat : un pixel
exclu par le masque l'est de la meme facon dans chaque bloc.
Precision numerique : terra ecrit sur disque en simple
precision (datatype = "FLT4S") PAR DEFAUT (voir
terra::terraOptions()), des que le resultat ne tient pas en RAM –
ce qui inclut la branche par blocs ci-dessous (TOUJOURS ecrite sur
disque). Sans le datatype = "FLT8S" explicite ci-dessous, un
masquage qui declenche une ecriture disque perdrait silencieusement de la
precision par rapport a un masquage qui reste en RAM – incoherence
potentielle non liee au chunking lui-meme, mais au meme risque general
(voir la note similaire dans resample_daily_terra()).
The masked terra::SpatRaster.
Performs a spatial join between PSMSL station coordinates and administrative
unit polygons, and computes for each unit the fraction of its perimeter
that is coastline. The coastline layer is cropped to the country bounding
box before intersection to speed up computation. All geometries are
projected to crs_metric before length calculations to ensure
results are in metres.
assign_sealevel_to_admin(country_abbrev, admin_level = 1, crs_metric = 4326)assign_sealevel_to_admin(country_abbrev, admin_level = 1, crs_metric = 4326)
country_abbrev |
ISO-3 country code (e.g. |
admin_level |
Integer >= 0. Administrative level fetched via
GADM (see |
crs_metric |
Integer. EPSG code of a metric CRS appropriate for
the country, used for accurate length calculations. Default |
A list with two elements:
station_idsNamed list: keys are administrative unit names, values are integer vectors of PSMSL station IDs within that unit.
factorsNamed numeric vector: keys are administrative
unit names, values are the coastal fraction (coastline length /
total perimeter) in [0, 1]. Zero for landlocked units.
For each ERA5 grid cell, computes the fraction of its surface area that
falls within each administrative unit polygon (using sf geometry
intersection). All geometries are projected to crs_metric before
area calculations to ensure results are in metres squared.
The result can be computationally expensive for fine grids and large
countries — consider caching it with saveRDS().
build_admin_mask( lon, lat, country_abbrev, admin_level = 1, resolution = 0.25, crs_metric = 4326, cache_dir = .gadm_cache_dir() )build_admin_mask( lon, lat, country_abbrev, admin_level = 1, resolution = 0.25, crs_metric = 4326, cache_dir = .gadm_cache_dir() )
lon |
Numeric vector of ERA5 longitudes. |
lat |
Numeric vector of ERA5 latitudes. |
country_abbrev |
ISO-3 country code (e.g. |
admin_level |
Integer >= 0. Administrative level fetched via GADM:
|
resolution |
Numeric. Half-width of ERA5 grid cells in degrees.
Default |
crs_metric |
Integer. EPSG code of a metric CRS appropriate for
the country, used for accurate area calculations. Default |
cache_dir |
Directory used to cache the downloaded GADM
administrative boundaries. Default: a persistent per-user cache
directory (see |
A list with elements:
weightsA named list of length
length(lon) * length(lat). Each element is a named numeric
vector of (unit -> fractional area weight) pairs.
unitsCharacter vector of all administrative unit names.
lonInput longitudes.
latInput latitudes.
This is the main entry point of the package. It instantiates all five climate components, standardises them over the reference period, and combines them into a single ACI time series:
calculate_aci( country_abbrev, study_period, reference_period, years = NULL, temperature_data_path = NULL, precipitation_data_path = NULL, wind_u10_data_path = NULL, wind_v10_data_path = NULL, mask_data_path = NULL, sealevel_dir = NULL, percentile_high = 90, percentile_low = 10, granularity = "month", area = TRUE, factor = 1/5, max_dist_km = 500, admin_level = NULL, crs_metric = 4326, save = FALSE, save_dir = NULL, load_dir = NULL, computed_components = FALSE, engine = c("base", "terra"), cores = 1L )calculate_aci( country_abbrev, study_period, reference_period, years = NULL, temperature_data_path = NULL, precipitation_data_path = NULL, wind_u10_data_path = NULL, wind_v10_data_path = NULL, mask_data_path = NULL, sealevel_dir = NULL, percentile_high = 90, percentile_low = 10, granularity = "month", area = TRUE, factor = 1/5, max_dist_km = 500, admin_level = NULL, crs_metric = 4326, save = FALSE, save_dir = NULL, load_dir = NULL, computed_components = FALSE, engine = c("base", "terra"), cores = 1L )
country_abbrev |
Three-letter ISO 3166-1 alpha-3 country code
(e.g. |
study_period |
Character vector of length 2:
|
reference_period |
Character vector of length 2:
|
years |
Integer vector of years covered by the NetCDF files
(e.g. |
temperature_data_path |
Path to the hourly 2-m temperature NetCDF
( |
precipitation_data_path |
Path to the precipitation NetCDF
( |
wind_u10_data_path |
Path to the u-component wind NetCDF
( |
wind_v10_data_path |
Path to the v-component wind NetCDF
( |
mask_data_path |
Path to the country mask NetCDF
( |
sealevel_dir |
Character or |
percentile_high |
Numeric. Upper percentile used for the hot temperature
component. Default |
percentile_low |
Numeric. Lower percentile used for the cold temperature
component. Default |
granularity |
Temporal aggregation level. One of
|
area |
Logical. Only used when |
factor |
Numeric in |
max_dist_km |
Numeric. Maximum distance (km) from a tide-gauge station
for a grid cell to receive a sea-level signal. Cells beyond this threshold
receive |
admin_level |
Integer or |
crs_metric |
Integer. EPSG code of a metric CRS
appropriate for the country, used for accurate area and length
calculations in |
save |
Logical. If |
save_dir |
Character. Directory for the cached |
load_dir |
Character. Directory from which to reload previously saved
|
computed_components |
Logical. If |
engine |
Character. |
cores |
Positive integer, default |
where is the sea-level erosion factor (coastal fraction,
default 1/5). For administrative units without coastal stations,
and the denominator becomes 5. At grid-cell level,
depending on whether the cell lies within
max_dist_km of a tide-gauge station.
area = TRUE, admin_level = NULL)A data.frame with columns t<percentile_high>,
t<percentile_low>, precipitation, drought,
wind, sealevel, and ACI, indexed by dates at
the chosen granularity.
area = FALSE, admin_level = NULL)A named list with one array [lon x lat x nt] per component
(ACI, t<percentile_high>, t<percentile_low>,
precipitation, drought, wind, sealevel),
plus lon, lat, and time (character vector of
period labels at the chosen granularity).
admin_level integer)A data.frame indexed by dates at the chosen granularity,
with one <component>_<unit> column per administrative unit
FOR EACH component (ACI, t<percentile_high>,
t<percentile_low>, precipitation, drought,
wind, sealevel) – e.g. ACI_<unit>,
t90_<unit>, precipitation_<unit>, etc. This mirrors
the grid-cell mode's per-component structure, and lets
plot_aci_map() plot any individual component at admin level,
not just ACI itself.
The four examples below all require real ERA5 NetCDF files (temperature,
precipitation, wind, country mask), obtained beforehand via
download_era5/download_mask and a Copernicus
CDS API key (see cds_set_key). They cannot be run
automatically by R CMD check and are therefore wrapped in
dontrun: (1) national ACI at annual granularity; (2) grid-cell
ACI (full spatial output); (3) ACI with custom percentiles (T95/T5); and
(4) ACI by department (admin level 2), using Lambert-93 for France.
## Not run: result <- calculate_aci( temperature_data_path = "t2m_1960-2020.nc", precipitation_data_path = "tp_1960-2020.nc", wind_u10_data_path = "u10_1960-2020.nc", wind_v10_data_path = "v10_1960-2020.nc", country_abbrev = "FRA", mask_data_path = "mask_FRA.nc", study_period = c("1980-01-01", "2020-12-31"), reference_period = c("1961-01-01", "1990-12-31"), granularity = "year", area = TRUE, factor = 1/5 ) grid <- calculate_aci( temperature_data_path = "t2m_1960-2020.nc", precipitation_data_path = "tp_1960-2020.nc", wind_u10_data_path = "u10_1960-2020.nc", wind_v10_data_path = "v10_1960-2020.nc", country_abbrev = "FRA", mask_data_path = "mask_FRA.nc", study_period = c("1980-01-01", "2020-12-31"), reference_period = c("1961-01-01", "1990-12-31"), granularity = "year", area = FALSE, max_dist_km = 500 ) result_custom <- calculate_aci( temperature_data_path = "t2m_1960-2020.nc", precipitation_data_path = "tp_1960-2020.nc", wind_u10_data_path = "u10_1960-2020.nc", wind_v10_data_path = "v10_1960-2020.nc", country_abbrev = "FRA", mask_data_path = "mask_FRA.nc", study_period = c("1980-01-01", "2020-12-31"), reference_period = c("1961-01-01", "1990-12-31"), granularity = "year", percentile_high = 95, percentile_low = 5 ) result_dept <- calculate_aci( temperature_data_path = "t2m_1960-2020.nc", precipitation_data_path = "tp_1960-2020.nc", wind_u10_data_path = "u10_1960-2020.nc", wind_v10_data_path = "v10_1960-2020.nc", country_abbrev = "FRA", mask_data_path = "mask_FRA.nc", study_period = c("1980-01-01", "2020-12-31"), reference_period = c("1961-01-01", "1990-12-31"), granularity = "year", area = FALSE, admin_level = 2, crs_metric = 2154 ) ## End(Not run)## Not run: result <- calculate_aci( temperature_data_path = "t2m_1960-2020.nc", precipitation_data_path = "tp_1960-2020.nc", wind_u10_data_path = "u10_1960-2020.nc", wind_v10_data_path = "v10_1960-2020.nc", country_abbrev = "FRA", mask_data_path = "mask_FRA.nc", study_period = c("1980-01-01", "2020-12-31"), reference_period = c("1961-01-01", "1990-12-31"), granularity = "year", area = TRUE, factor = 1/5 ) grid <- calculate_aci( temperature_data_path = "t2m_1960-2020.nc", precipitation_data_path = "tp_1960-2020.nc", wind_u10_data_path = "u10_1960-2020.nc", wind_v10_data_path = "v10_1960-2020.nc", country_abbrev = "FRA", mask_data_path = "mask_FRA.nc", study_period = c("1980-01-01", "2020-12-31"), reference_period = c("1961-01-01", "1990-12-31"), granularity = "year", area = FALSE, max_dist_km = 500 ) result_custom <- calculate_aci( temperature_data_path = "t2m_1960-2020.nc", precipitation_data_path = "tp_1960-2020.nc", wind_u10_data_path = "u10_1960-2020.nc", wind_v10_data_path = "v10_1960-2020.nc", country_abbrev = "FRA", mask_data_path = "mask_FRA.nc", study_period = c("1980-01-01", "2020-12-31"), reference_period = c("1961-01-01", "1990-12-31"), granularity = "year", percentile_high = 95, percentile_low = 5 ) result_dept <- calculate_aci( temperature_data_path = "t2m_1960-2020.nc", precipitation_data_path = "tp_1960-2020.nc", wind_u10_data_path = "u10_1960-2020.nc", wind_v10_data_path = "v10_1960-2020.nc", country_abbrev = "FRA", mask_data_path = "mask_FRA.nc", study_period = c("1980-01-01", "2020-12-31"), reference_period = c("1961-01-01", "1990-12-31"), granularity = "year", area = FALSE, admin_level = 2, crs_metric = 2154 ) ## End(Not run)
Calculate the half-day (day or night) temperature component
calculate_halfday_component( dataset, reference_period, part_of_day, extremum, percentile, above_thresholds, window_days = 5L )calculate_halfday_component( dataset, reference_period, part_of_day, extremum, percentile, above_thresholds, window_days = 5L )
dataset |
Full sub-daily |
reference_period |
Character vector |
part_of_day |
|
extremum |
|
percentile |
Numeric percentile (e.g. 90 or 10). |
above_thresholds |
Logical. |
window_days |
Passed to |
A list with data [lon × lat × months] and monthly time.
Calculate the half-day (day or night) temperature component (terra version)
calculate_halfday_component_terra( r, reference_period, part_of_day, extremum, percentile, above_thresholds, mask_path = NULL, threshold = 0.8, cores = 1L, target_chunk_gb = 1, window_days = 5L )calculate_halfday_component_terra( r, reference_period, part_of_day, extremum, percentile, above_thresholds, mask_path = NULL, threshold = 0.8, cores = 1L, target_chunk_gb = 1, window_days = 5L )
r |
A |
reference_period |
Character vector |
part_of_day |
|
extremum |
|
percentile |
Numeric percentile (e.g. 90 or 10). |
above_thresholds |
Logical. |
mask_path |
Path to the mask NetCDF file, or |
threshold |
Numeric threshold for the mask. Default |
cores |
Passed to |
target_chunk_gb |
Passed to |
window_days |
Passed to |
Same structure as calculate_halfday_component():
list(data, time, lon, lat).
Computes the rolling window_size-day sum of precipitation, then
takes the monthly maximum.
calculate_maximum_precipitation_over_window( dataset, var_name = "tp", window_size = 5L )calculate_maximum_precipitation_over_window( dataset, var_name = "tp", window_size = 5L )
dataset |
List returned by |
var_name |
Variable name in |
window_size |
Rolling window in DAYS. Default |
A list with data [lon x lat x months] and time.
Calculate maximum precipitation over a rolling window (terra version)
calculate_maximum_precipitation_over_window_terra( r, var_name = "tp", window_size = 5L, mask_path = NULL, threshold = 0.8, target_chunk_gb = 1 )calculate_maximum_precipitation_over_window_terra( r, var_name = "tp", window_size = 5L, mask_path = NULL, threshold = 0.8, target_chunk_gb = 1 )
r |
A |
var_name |
Variable name in |
window_size |
Rolling window in DAYS. Default |
mask_path |
Path to the mask NetCDF file, or |
threshold |
Numeric threshold for the mask. Default |
target_chunk_gb |
Passed to |
Same structure as calculate_maximum_precipitation_over_window().
Uses a rolling window (in days) over the reference period,
followed by a group-by-day-of-year percentile. Matches the CLIMDEX / ACI
methodology (see ACI (2018), Appendix A of Garrido et al.): the
threshold for a given calendar day is the n-th percentile of the
daily TX/TN values falling within a window of window_days
days (default 5, i.e. 2 days) around that calendar day,
pooled across all years of the reference period.
calculate_percentiles(daily_dataset, n, reference_period, window_days = 5L)calculate_percentiles(daily_dataset, n, reference_period, window_days = 5L)
daily_dataset |
List with |
n |
Percentile (e.g. 90 or 10). |
reference_period |
Character vector |
window_days |
Width, in days, of the window used to pool
neighbouring calendar days before taking the percentile (e.g. 5 pools
|
Bug fix #1 (see NEWS): earlier versions of this function computed
the rolling window/percentile on hourly t2m values (with
window_size expressed in hours: 80 for day, 40 for night), then
compared the resulting threshold to a daily extremum in
.crossing_frequency(). Since the daily max (or min) of ~16 (or 8)
hourly values almost always exceeds (or falls below) the 90th (or 10th)
percentile of the marginal hourly distribution, this made the exceedance
frequency during the reference period itself massively higher than the
intended ~10
the reference frequency approximately by construction. calculate_percentiles()
now takes the already-reduced daily extremum series (the output
of temp_extremum()) as input, so the threshold and the tested
variable are computed from the same daily quantity.
Bug fix #2 (see NEWS): even after bug fix #1, the threshold was
still computed as a percentile of rolling percentiles: a
window_days-wide rolling n-th percentile was first applied
along the daily series, and a second n-th percentile was
then taken of those already-extreme rolled values, grouped by
day-of-year across reference years. This two-stage procedure is
systematically biased: taking the n-th percentile of a set of
local n-th percentiles pushes the final threshold further into the
tail than a single, direct percentile would, so it under-counts the true
exceedance frequency (observed ~5.6
even with bug fix #1 alone applied). The standard CLIMDEX/ACI methodology
instead pools the raw daily values that fall within
window_days of a given calendar day, across all years of
the reference period, into a single sample, and takes one
percentile of that pooled sample. calculate_percentiles() now
follows this pooling approach (no more rolling window / zoo
dependency for this function), which empirically recovers the intended
~10
A numeric array [lon x lat x 366] (day-of-year 1-366).
Equivalent of calculate_percentiles().
calculate_percentiles_terra( r_daily, n, reference_period, window_days = 5L, filename = "", cores = 1L )calculate_percentiles_terra( r_daily, n, reference_period, window_days = 5L, filename = "", cores = 1L )
r_daily |
A |
n |
Percentile (e.g. 90 or 10). |
reference_period |
Character vector |
window_days |
Width, in days, of the window used to pool
neighbouring calendar days before taking the percentile (e.g. 5 pools
|
filename |
Optional output path for the final thresholds. |
cores |
How many spatial tiles to process IN PARALLEL (a ceiling,
further capped by |
Bug fix #1 (see NEWS): this function used to take the raw
hourly raster, filter it to day/night hours, and compute the
rolling-window percentile directly on those hourly values (with
window_size expressed in hours: 80 for day, 40 for night) –
while calculate_halfday_component_terra() compared the resulting
threshold to a daily max/min. Because the daily extremum of
~16 (or 8) hourly values almost always crosses the 90th (or 10th)
percentile of the hourly distribution, this made the exceedance
frequency during the reference period itself massively higher than the
intended ~10
already-reduced daily extremum raster (the output of
temp_extremum_terra()) as input, with window_days
expressed in days (default 5), so the threshold and the tested
variable are computed from the same daily quantity.
Bug fix #2 (see NEWS): even after bug fix #1, the threshold was
still computed as a rolling-window n-th percentile
(terra::roll()) followed by a second n-th
percentile of those already-extreme rolled values, grouped by
day-of-year (terra::tapp()). This two-stage procedure
systematically under-counts the true exceedance frequency (observed
~5.6
applied) – see calculate_percentiles() (base-R engine) for the
full explanation and numeric illustration, which applies identically
here. calculate_percentiles_terra() now instead pools the RAW
daily values within window_days of a given calendar day, across
ALL years of the reference period, and takes a SINGLE
terra::app() quantile of that pooled sample per cell, per
calendar day – the standard CLIMDEX/ACI methodology. This also removes
the terra::roll()/terra::tapp() dependency for this
function entirely (simpler, and no longer sensitive to
terra::roll()'s argument names changing across terra versions).
Performance AND memory note: looping over 366 calendar days and
calling terra::app() on a (typically ~100-200 layer) subset of the
reference period for each is comparable in total workload to the single
whole-series terra::roll() call used before bug fix #2, but
touches far fewer layers per call. A single such call over a whole
country's grid could still, in principle, use substantial memory for a
large grid – as a precaution the raster is still ALWAYS split into small
spatial tiles sized to a conservative, fixed memory target (see
target_tile_gb in .calculate_percentiles_terra_tiled()),
regardless of cores – cores only controls how many of
those already-memory-safe tiles run concurrently (default 1: one
at a time).
A terra::SpatRaster with 366 layers (day-of-year 1-366).
Calculate monthly wind exceedance frequency
calculate_period_wind_exceedance_frequency( u10_dataset, v10_dataset, reference_period )calculate_period_wind_exceedance_frequency( u10_dataset, v10_dataset, reference_period )
u10_dataset |
List for |
v10_dataset |
List for |
reference_period |
Character vector |
A list with data [lon x lat x periods] and time.
Drop-in, memory-safe replacement for
calculate_period_wind_exceedance_frequency(). Only the initial
wind_power_terra() call differs from the base-R version; the
threshold, crossing, and monthly-aggregation steps reuse the exact same
helpers, since they already operate on small, daily-resolution data.
calculate_period_wind_exceedance_frequency_terra( u10_r, v10_r, reference_period, mask_path = NULL, threshold = 0.8, target_chunk_gb = 1 )calculate_period_wind_exceedance_frequency_terra( u10_r, v10_r, reference_period, mask_path = NULL, threshold = 0.8, target_chunk_gb = 1 )
u10_r, v10_r
|
|
reference_period |
Character vector |
mask_path |
Path to the mask NetCDF file, or |
threshold |
Numeric threshold for the mask. Default |
target_chunk_gb |
Passed to |
Same as calculate_period_wind_exceedance_frequency().
Computes a rolling (sliding-window) sum along the time dimension of a 3-D climate array.
calculate_rolling_sum(dataset, var_name, window_size)calculate_rolling_sum(dataset, var_name, window_size)
dataset |
List returned by |
var_name |
Variable name (for documentation; the array in
|
window_size |
Integer number of time steps for the rolling window. |
A list with the same structure as dataset but with
data replaced by the rolling sum (leading incomplete windows are
NA).
Wrapper around wf_set_key for the new CDS API
(post-October 2024). The token is stored securely in the system keyring and
never needs to appear in your scripts again.
cds_set_key(token)cds_set_key(token)
token |
Character. Your CDS Personal Access Token (UUID format). |
To obtain your token:
Create a free account at https://cds.climate.copernicus.eu.
Go to your profile → Personal Access Token.
Copy the token (a UUID string) and pass it to this function once.
Invisibly NULL. The token is saved in the system keyring.
## Not run: cds_set_key("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") ## End(Not run)## Not run: cds_set_key("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") ## End(Not run)
Memory-safe alternatives to load_netcdf()/apply_mask()
and to the hourly-resolution steps of the temperature, precipitation and wind
pipelines (temp_extremum(), calculate_percentiles(), the daily
resampling inside wind_power()).
Why this file exists: for a whole country at hourly resolution
over 40+ years, ncdf4::ncvar_get() materialises the full
[lon x lat x time] array in RAM in one shot (tens of GB). terra
reads NetCDF lazily (via GDAL) and processes computations block-by-block,
writing results to disk instead of accumulating them in memory.
Only the hourly-scale steps are ported here. Once data has been reduced
to DAILY resolution (via resample_daily_terra(), temp_extremum_terra(), ...),
the resulting object is small enough (~40 years x 365 days) to convert back
to a plain array with .spatraster_to_list() and hand off, unchanged, to the
rest of the existing (already tested) pipeline – resample_monthly(),
standardize_metric(), .compute_aci_grid(), etc.
Calculate days with wind power above the 90th-percentile threshold
days_above_wind_thresholds(u10_dataset, v10_dataset, reference_period)days_above_wind_thresholds(u10_dataset, v10_dataset, reference_period)
u10_dataset |
List for |
v10_dataset |
List for |
reference_period |
Character vector |
A list with binary data [lon x lat x days] and time.
Functions to download ERA5 climate variables and country mask from the Copernicus Climate Data Store (CDS) via the ecmwfr package (v2.0+, new CDS API — Personal Access Token only, no UID required).
Downloads hourly ERA5 single-level data for one variable and one or several years, for a given geographical bounding box. Files are downloaded year by year and then optionally merged into a single NetCDF using the ncdf4 package.
download_era5( variable = c("t2m", "tp", "u10", "v10"), years, area = c(51.5, -5.5, 41, 10), country_abbrev = NULL, dest_dir = NULL, merge = TRUE, overwrite = FALSE )download_era5( variable = c("t2m", "tp", "u10", "v10"), years, area = c(51.5, -5.5, 41, 10), country_abbrev = NULL, dest_dir = NULL, merge = TRUE, overwrite = FALSE )
variable |
Character. Short name of the ERA5 variable: one of
|
years |
Integer vector. Years to download (e.g. |
area |
Numeric vector of length 4: |
country_abbrev |
Character. Three-letter ISO 3166-1 alpha-3 country
code (e.g. |
dest_dir |
Character. Directory where files will be saved. If
|
merge |
Logical. If |
overwrite |
Logical. If |
The CDS Personal Access Token must be stored beforehand with
cds_set_key.
Invisibly, the path to the final NetCDF file (merged if
merge = TRUE, otherwise the directory containing yearly files).
## Not run: # Store your token once cds_set_key("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") # Automatic path: <tempdir()>/xaci_era5/FRA/tp_1961_1990.nc download_era5( variable = "tp", years = 1961:1990, area = c(51.5, -5.5, 41.0, 10.0), country_abbrev = "FRA" ) # Or explicit path: download_era5( variable = "tp", years = 1961:1990, area = c(51.5, -5.5, 41.0, 10.0), dest_dir = "my/custom/path" ) ## End(Not run)## Not run: # Store your token once cds_set_key("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") # Automatic path: <tempdir()>/xaci_era5/FRA/tp_1961_1990.nc download_era5( variable = "tp", years = 1961:1990, area = c(51.5, -5.5, 41.0, 10.0), country_abbrev = "FRA" ) # Or explicit path: download_era5( variable = "tp", years = 1961:1990, area = c(51.5, -5.5, 41.0, 10.0), dest_dir = "my/custom/path" ) ## End(Not run)
Convenience wrapper that calls download_era5 four times (one
per variable: t2m, tp, u10, v10).
download_era5_all( years, area = c(51.5, -5.5, 41, 10), country_abbrev = NULL, dest_dir = NULL, merge = TRUE, overwrite = FALSE )download_era5_all( years, area = c(51.5, -5.5, 41, 10), country_abbrev = NULL, dest_dir = NULL, merge = TRUE, overwrite = FALSE )
years |
Integer vector of years to download. |
area |
Bounding box |
country_abbrev |
Character. ISO-3 country code (e.g. |
dest_dir |
Character. Output directory. If |
merge |
Logical. Merge yearly files. Default |
overwrite |
Logical. Overwrite existing files. Default |
Invisibly, a named character vector of output paths (one per variable).
## Not run: cds_set_key("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") # Produces (by default): <tempdir()>/xaci_era5/FRA/t2m_1961_1990.nc, tp_..., u10_..., v10_... download_era5_all( years = 1961:1990, area = c(51.5, -5.5, 41.0, 10.0), country_abbrev = "FRA" ) ## End(Not run)## Not run: cds_set_key("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") # Produces (by default): <tempdir()>/xaci_era5/FRA/t2m_1961_1990.nc, tp_..., u10_..., v10_... download_era5_all( years = 1961:1990, area = c(51.5, -5.5, 41.0, 10.0), country_abbrev = "FRA" ) ## End(Not run)
Downloads the ERA5 land-sea mask for the given bounding box, then rescales
it to [0, 1] and saves it as mask_<country_abbrev>.nc in
dest_dir. The mask variable is renamed to country so it is
directly compatible with apply_mask.
download_mask( country_abbrev = "FRA", area = c(51.5, -5.5, 41, 10), dest_dir = NULL, overwrite = FALSE )download_mask( country_abbrev = "FRA", area = c(51.5, -5.5, 41, 10), dest_dir = NULL, overwrite = FALSE )
country_abbrev |
Three-letter ISO 3166-1 alpha-3 country code
(e.g. |
area |
Numeric vector |
dest_dir |
Character. Directory for the output file.
Default |
overwrite |
Logical. Overwrite if the file already exists.
Default |
Invisibly, the path to the mask NetCDF file.
## Not run: cds_set_key("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") # Produces (by default): <tempdir()>/xaci_era5/FRA/mask_FRA.nc download_mask( country_abbrev = "FRA", area = c(51.5, -5.5, 41.0, 10.0) ) ## End(Not run)## Not run: cds_set_key("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") # Produces (by default): <tempdir()>/xaci_era5/FRA/mask_FRA.nc download_mask( country_abbrev = "FRA", area = c(51.5, -5.5, 41.0, 10.0) ) ## End(Not run)
Computes the drought component of the Actuarial Climate Index via maximum consecutive dry days (CDD).
Computes standardised consecutive dry days.
drought_component( precipitation_data_path, country_abbrev, reference_period, study_period, mask_path = NULL, area = FALSE, admin_level = NULL, admin_mask = NULL, crs_metric = 4326, computed_components = FALSE, save = FALSE, save_dir = NULL, load_dir = NULL )drought_component( precipitation_data_path, country_abbrev, reference_period, study_period, mask_path = NULL, area = FALSE, admin_level = NULL, admin_mask = NULL, crs_metric = 4326, computed_components = FALSE, save = FALSE, save_dir = NULL, load_dir = NULL )
precipitation_data_path |
Path to the precipitation NetCDF file. |
country_abbrev |
Three-letter ISO country code (e.g.
|
reference_period |
Character vector |
study_period |
Character vector |
mask_path |
Path to the country mask NetCDF file, or
|
area |
Logical. If |
admin_level |
Integer or |
admin_mask |
Output of |
crs_metric |
EPSG code for the metric CRS used when
building the admin mask. Default |
computed_components |
Logical. If |
save |
Logical. Default |
save_dir |
Character. Default |
load_dir |
Character. Default |
Named numeric vector (area = TRUE), standardised list
(area = FALSE), or data.frame per admin unit.
Drop-in, memory-safe replacement for drought_component().
drought_component_terra( precipitation_data_path, country_abbrev, reference_period, study_period, mask_path = NULL, area = FALSE, admin_level = NULL, admin_mask = NULL, crs_metric = 4326, computed_components = FALSE, save = FALSE, save_dir = NULL, load_dir = NULL, target_chunk_gb = 1 )drought_component_terra( precipitation_data_path, country_abbrev, reference_period, study_period, mask_path = NULL, area = FALSE, admin_level = NULL, admin_mask = NULL, crs_metric = 4326, computed_components = FALSE, save = FALSE, save_dir = NULL, load_dir = NULL, target_chunk_gb = 1 )
precipitation_data_path |
Path to the precipitation NetCDF file. |
country_abbrev |
Three-letter ISO country code (e.g.
|
reference_period |
Character vector |
study_period |
Character vector |
mask_path |
Path to the country mask NetCDF file, or
|
area |
Logical. If |
admin_level |
Integer or |
admin_mask |
Output of |
crs_metric |
EPSG code for the metric CRS used when
building the admin mask. Default |
computed_components |
Logical. If |
save |
Logical. Default |
save_dir |
Character. Default |
load_dir |
Character. Default |
target_chunk_gb |
Passed to |
Same as drought_component().
For month of year , the monthly CDD interpolates
between the ANNUAL values of year and year (each annual
value being anchored at December 31 of its own year):
and exactly (December of year coincides
with that year's own annual anchor point). This matches Eq. (A.6) of
Garrido et al. The first year, having no to interpolate from,
repeats its own annual value for all 12 months.
drought_interpolate(cdd_annual)drought_interpolate(cdd_annual)
cdd_annual |
List returned by |
Bug fix (see NEWS): earlier versions of this function computed
this same interpolation formula correctly, but assigned the result to
the WRONG calendar year – labelling month of year
as w1 * data[,,k] + w2 * data[,,k+1], which
is actually month of year per Eq. (A.6)
(whose December value must equal that year's OWN annual value, not the
following year's). Verified numerically: with annual CDD values
10/20/30/40 for 2000-2003, December 2000 came out as 20 (year 2001's
annual value) instead of 10 (year 2000's own value). The interpolation
arithmetic itself was correct; only the year label was off by one. As a
consequence, the "repeat own value for all 12 months" edge-case handling
now applies to the FIRST year (which has no preceding year to interpolate
from) instead of the last (which, under the corrected labelling, has no
such issue: Eq. A.6 only ever needs year to compute year
's months, so the last year is computed normally).
A list with data [lon × lat × months] and monthly time.
Memory-safe equivalents of max_consecutive_dry_days()
and drought_component(). Only the hourly-to-daily reduction runs
through terra; drought_interpolate() and the consecutive-dry-day
scan already operate on small, annual/daily-resolution data, so they are
reused as-is via the shared helper
.max_consecutive_dry_days_from_daily() (defined in
drought.R) – no logic is duplicated.
This function is a constructor.
It reads a NetCDF variable and, if mask_path is provided, zeroes out
(sets to NA) all grid cells where the mask value is below
threshold.
load_component(data_path, var_name, mask_path = NULL, threshold = 0.8)load_component(data_path, var_name, mask_path = NULL, threshold = 0.8)
data_path |
Path to the NetCDF data file. |
var_name |
Name of the primary variable to load. |
mask_path |
Path to the mask NetCDF file, or |
threshold |
Numeric threshold for the mask. Default |
A list with elements data (3-D array [lon × lat × time]),
lon, lat, time (POSIXct vector), and
var_name.
Drop-in, memory-safe replacement for load_component() intended for
full-resolution, grid-cell-level ("area = FALSE") workflows over long
historical periods.
load_component_terra( data_path, var_name, mask_path = NULL, threshold = 0.8, chunk_size = 20000 )load_component_terra( data_path, var_name, mask_path = NULL, threshold = 0.8, chunk_size = 20000 )
data_path |
Path to the NetCDF file. |
var_name |
Name of the variable to extract. |
mask_path |
Path to the mask NetCDF file, or |
threshold |
Numeric threshold for the mask. Default |
chunk_size |
Passe a |
A terra::SpatRaster.
Equivalent of load_netcdf(), but never materialises pixel values in
RAM: only metadata (dimensions, CRS, time) is read up front.
load_netcdf_terra(path, var_name)load_netcdf_terra(path, var_name)
path |
Path to the NetCDF file. |
var_name |
Name of the variable to extract. |
A terra::SpatRaster with one layer per time step and
terra::time() attached.
Load the bundled PSMSL station metadata
load_psmsl_data()load_psmsl_data()
A data.frame of PSMSL tide-gauge station information.
A dry day is defined as total daily precipitation < 1 mm. The function returns the annual maximum number of consecutive dry days for each grid cell.
max_consecutive_dry_days(dataset)max_consecutive_dry_days(dataset)
dataset |
List returned by |
A list with data [lon × lat × years] and annual time.
Calculate maximum consecutive dry days (terra version)
max_consecutive_dry_days_terra( r, mask_path = NULL, threshold = 0.8, target_chunk_gb = 1 )max_consecutive_dry_days_terra( r, mask_path = NULL, threshold = 0.8, target_chunk_gb = 1 )
r |
A |
mask_path |
Path to the mask NetCDF file, or |
threshold |
Numeric threshold for the mask. Default |
target_chunk_gb |
Passed to |
Same structure as max_consecutive_dry_days().
Produces either a bar chart (one panel per component,
type = "bar") or a stacked area chart
(type = "stacked") showing the six standardised components that
make up the ACI.
plot_aci_components( aci_df, type = c("stacked", "line", "bar"), components = NULL, title = "ACI Component Contributions", subtitle = NULL )plot_aci_components( aci_df, type = c("stacked", "line", "bar"), components = NULL, title = "ACI Component Contributions", subtitle = NULL )
aci_df |
A |
type |
|
components |
Character vector of component names to include.
Default: all six |
title |
Plot title. |
subtitle |
Character or |
A ggplot object.
# Toy monthly ACI data.frame (see plot_aci_timeseries() for details) n <- 60L dates <- format(seq(as.Date("2015-01-01"), by = "month", length.out = n), "%Y-%m") aci_df <- data.frame( t90 = 20 + stats::rnorm(n, sd = 2), t10 = 5 + stats::rnorm(n, sd = 2), precipitation = stats::rnorm(n, sd = 1), drought = stats::rnorm(n, sd = 1), wind = stats::rnorm(n, sd = 1), sealevel = stats::rnorm(n, sd = 1), row.names = dates ) aci_df$ACI <- with(aci_df, (t90 - t10 + precipitation + drought + wind + sealevel) / 5) plot_aci_components(aci_df, type = "bar") plot_aci_components(aci_df, type = "stacked", components = c("t90", "t10"))# Toy monthly ACI data.frame (see plot_aci_timeseries() for details) n <- 60L dates <- format(seq(as.Date("2015-01-01"), by = "month", length.out = n), "%Y-%m") aci_df <- data.frame( t90 = 20 + stats::rnorm(n, sd = 2), t10 = 5 + stats::rnorm(n, sd = 2), precipitation = stats::rnorm(n, sd = 1), drought = stats::rnorm(n, sd = 1), wind = stats::rnorm(n, sd = 1), sealevel = stats::rnorm(n, sd = 1), row.names = dates ) aci_df$ACI <- with(aci_df, (t90 - t10 + precipitation + drought + wind + sealevel) / 5) plot_aci_components(aci_df, type = "bar") plot_aci_components(aci_df, type = "stacked", components = c("t90", "t10"))
Arranges the four core plots (time series, component facets, boxplots and density) into a single patchwork figure. Requires the patchwork package to be installed.
plot_aci_dashboard(aci_df, title = "ACI Summary Dashboard")plot_aci_dashboard(aci_df, title = "ACI Summary Dashboard")
aci_df |
A |
title |
Overall figure title. Default |
A patchwork object (inherits from ggplot).
# Toy monthly ACI data.frame (see plot_aci_timeseries() for details) n <- 60L dates <- format(seq(as.Date("2015-01-01"), by = "month", length.out = n), "%Y-%m") aci_df <- data.frame( t90 = 20 + stats::rnorm(n, sd = 2), t10 = 5 + stats::rnorm(n, sd = 2), precipitation = stats::rnorm(n, sd = 1), drought = stats::rnorm(n, sd = 1), wind = stats::rnorm(n, sd = 1), sealevel = stats::rnorm(n, sd = 1), row.names = dates ) aci_df$ACI <- with(aci_df, (t90 - t10 + precipitation + drought + wind + sealevel) / 5) plot_aci_dashboard(aci_df)# Toy monthly ACI data.frame (see plot_aci_timeseries() for details) n <- 60L dates <- format(seq(as.Date("2015-01-01"), by = "month", length.out = n), "%Y-%m") aci_df <- data.frame( t90 = 20 + stats::rnorm(n, sd = 2), t10 = 5 + stats::rnorm(n, sd = 2), precipitation = stats::rnorm(n, sd = 1), drought = stats::rnorm(n, sd = 1), wind = stats::rnorm(n, sd = 1), sealevel = stats::rnorm(n, sd = 1), row.names = dates ) aci_df$ACI <- with(aci_df, (t90 - t10 + precipitation + drought + wind + sealevel) / 5) plot_aci_dashboard(aci_df)
Displays the statistical distribution of each standardised component (and
optionally the global ACI) as boxplots (type = "boxplot"),
violin plots (type = "violin"), or
density curves (type = "density").
plot_aci_distribution( aci_df, components = NULL, include_aci = FALSE, type = c("boxplot", "violin", "density"), title = "ACI Component Distributions", subtitle = NULL )plot_aci_distribution( aci_df, components = NULL, include_aci = FALSE, type = c("boxplot", "violin", "density"), title = "ACI Component Distributions", subtitle = NULL )
aci_df |
A |
components |
Character vector of component columns to include. |
include_aci |
Logical. If |
type |
|
title |
Plot title. |
subtitle |
Character or |
A ggplot object.
# Toy monthly ACI data.frame (see plot_aci_timeseries() for details) n <- 60L dates <- format(seq(as.Date("2015-01-01"), by = "month", length.out = n), "%Y-%m") aci_df <- data.frame( t90 = 20 + stats::rnorm(n, sd = 2), t10 = 5 + stats::rnorm(n, sd = 2), precipitation = stats::rnorm(n, sd = 1), drought = stats::rnorm(n, sd = 1), wind = stats::rnorm(n, sd = 1), sealevel = stats::rnorm(n, sd = 1), row.names = dates ) aci_df$ACI <- with(aci_df, (t90 - t10 + precipitation + drought + wind + sealevel) / 5) plot_aci_distribution(aci_df, type = "violin") plot_aci_distribution(aci_df, type = "density")# Toy monthly ACI data.frame (see plot_aci_timeseries() for details) n <- 60L dates <- format(seq(as.Date("2015-01-01"), by = "month", length.out = n), "%Y-%m") aci_df <- data.frame( t90 = 20 + stats::rnorm(n, sd = 2), t10 = 5 + stats::rnorm(n, sd = 2), precipitation = stats::rnorm(n, sd = 1), drought = stats::rnorm(n, sd = 1), wind = stats::rnorm(n, sd = 1), sealevel = stats::rnorm(n, sd = 1), row.names = dates ) aci_df$ACI <- with(aci_df, (t90 - t10 + precipitation + drought + wind + sealevel) / 5) plot_aci_distribution(aci_df, type = "violin") plot_aci_distribution(aci_df, type = "density")
Handles three types of input, at any spatial aggregation level produced
by calculate_aci() (grid-cell, admin level 1, admin level 2, ...):
Bare array (e.g. grid$t90, grid$ACI): a
single component array as stored in the grid-cell list returned by
calculate_aci(area = FALSE). Self-describing via its attached
lon/lat/time/country_abbrev attributes, so
it can be passed on its own. Renders a raster map. Since ACI
and its components share the exact same array structure, this works
identically whether variable is a component or the ACI itself.
Grid-cell list (the full output of
calculate_aci(area = FALSE), or of load_component()):
variable selects which field of the list to map. Renders a
raster map.
Administrative data.frame (output of
calculate_aci(admin_level = N)): renders a choropleth map.
country_abbrev/admin_level/crs_metric are read
from the data.frame's attributes when not supplied explicitly.
plot_aci_map( data, variable = "ACI", time_index = "mean", country_abbrev = NULL, admin_level = NULL, crs_metric = NULL, var_label = "Standardised anomaly", title = "ACI Spatial Distribution", palette = "RdBu", reverse = TRUE, n_breaks = 9, borders = TRUE )plot_aci_map( data, variable = "ACI", time_index = "mean", country_abbrev = NULL, admin_level = NULL, crs_metric = NULL, var_label = "Standardised anomaly", title = "ACI Spatial Distribution", palette = "RdBu", reverse = TRUE, n_breaks = 9, borders = TRUE )
data |
A bare array (component or ACI, with attributes), a
named list (grid-cell / raw dataset), or a |
variable |
Name of the variable to map when |
time_index |
Integer (1-based time slice) or |
country_abbrev |
Three-letter ISO code. Required for admin maps and
for overlaying country/region borders on raster maps. If |
admin_level |
Integer or |
crs_metric |
EPSG code used for admin choropleth projection only.
If |
var_label |
Colour-bar label. Default |
title |
Plot title. Default |
palette |
RColorBrewer palette. Default |
reverse |
Logical. Reverse colour scale? Default |
n_breaks |
Number of colour breaks. Default |
borders |
Logical. Overlay administrative borders? Default
|
A ggplot object.
The first two examples below use a small synthetic 10x8x5
(lon x lat x time) grid and run offline (borders = FALSE).
Overlaying administrative borders, or plotting an admin-level
choropleth, additionally requires downloading boundary polygons via
geodata::gadm() and therefore a working internet connection; the
corresponding examples are wrapped in dontrun and not run
automatically by R CMD check.
lon <- seq(-5, 8, length.out = 10) lat <- seq(42, 51, length.out = 8) arr <- array(stats::rnorm(10 * 8 * 5), dim = c(10, 8, 5)) arr <- structure(arr, lon = lon, lat = lat, time = seq_len(5), country_abbrev = "FRA") # Bare array, self-describing thanks to its attributes plot_aci_map(arr, time_index = "mean", borders = FALSE) # Equivalent, via the parent list and `variable` grid <- list(t90 = arr, lon = lon, lat = lat, time = seq_len(5)) plot_aci_map(grid, variable = "t90", time_index = 1, borders = FALSE) ## Not run: plot_aci_map(arr, time_index = "mean", country_abbrev = "FRA") admin <- calculate_aci( temperature_data_path = "t2m_1960-2020.nc", precipitation_data_path = "tp_1960-2020.nc", wind_u10_data_path = "u10_1960-2020.nc", wind_v10_data_path = "v10_1960-2020.nc", country_abbrev = "FRA", mask_data_path = "mask_FRA.nc", study_period = c("1961-01-01", "2020-12-31"), reference_period = c("1961-01-01", "1990-12-31"), granularity = "year", area = FALSE, admin_level = 1 ) plot_aci_map(admin, variable = "ACI", time_index = 1) ## End(Not run)lon <- seq(-5, 8, length.out = 10) lat <- seq(42, 51, length.out = 8) arr <- array(stats::rnorm(10 * 8 * 5), dim = c(10, 8, 5)) arr <- structure(arr, lon = lon, lat = lat, time = seq_len(5), country_abbrev = "FRA") # Bare array, self-describing thanks to its attributes plot_aci_map(arr, time_index = "mean", borders = FALSE) # Equivalent, via the parent list and `variable` grid <- list(t90 = arr, lon = lon, lat = lat, time = seq_len(5)) plot_aci_map(grid, variable = "t90", time_index = 1, borders = FALSE) ## Not run: plot_aci_map(arr, time_index = "mean", country_abbrev = "FRA") admin <- calculate_aci( temperature_data_path = "t2m_1960-2020.nc", precipitation_data_path = "tp_1960-2020.nc", wind_u10_data_path = "u10_1960-2020.nc", wind_v10_data_path = "v10_1960-2020.nc", country_abbrev = "FRA", mask_data_path = "mask_FRA.nc", study_period = c("1961-01-01", "2020-12-31"), reference_period = c("1961-01-01", "1990-12-31"), granularity = "year", area = FALSE, admin_level = 1 ) plot_aci_map(admin, variable = "ACI", time_index = 1) ## End(Not run)
Convenience wrapper around plot_aci_map that computes the mean
over a user-defined date range rather than a single time index.
plot_aci_map_mean( data, variable = "ACI", period = NULL, country_abbrev = NULL, admin_level = NULL, crs_metric = NULL, var_label = "Standardised anomaly", title = NULL, palette = "RdBu", reverse = TRUE, n_breaks = 9, borders = TRUE )plot_aci_map_mean( data, variable = "ACI", period = NULL, country_abbrev = NULL, admin_level = NULL, crs_metric = NULL, var_label = "Standardised anomaly", title = NULL, palette = "RdBu", reverse = TRUE, n_breaks = 9, borders = TRUE )
data |
Bare array, grid-cell list, or admin |
variable |
Variable to map. Default |
period |
Character vector |
country_abbrev |
Three-letter ISO code. |
admin_level |
Integer or |
crs_metric |
EPSG code. Default |
var_label |
Colour-bar label. |
title |
Plot title. |
palette |
RColorBrewer palette. Default |
reverse |
Logical. Default |
n_breaks |
Integer. Default |
borders |
Logical. Default |
A ggplot object.
Draws a line chart of the ACI values returned by
calculate_aci, with an optional smoothed
trend and a horizontal zero reference line.
plot_aci_timeseries( aci_df, smooth = TRUE, span = 0.2, title = "Actuarial Climate Index (ACI)", colour = "#1F77B4", fill_area = TRUE )plot_aci_timeseries( aci_df, smooth = TRUE, span = 0.2, title = "Actuarial Climate Index (ACI)", colour = "#1F77B4", fill_area = TRUE )
aci_df |
A |
smooth |
Logical. If |
span |
Numeric span for the LOESS smoother. Default |
title |
Plot title. Default |
colour |
Line colour for the raw ACI series.
Default |
fill_area |
Logical. If |
A ggplot object.
# Toy monthly ACI data.frame (not from calculate_aci(), just for # illustration), with the columns and "YYYY-MM" row names that # calculate_aci(area = TRUE) would return. n <- 60L dates <- format(seq(as.Date("2015-01-01"), by = "month", length.out = n), "%Y-%m") aci_df <- data.frame( t90 = 20 + stats::rnorm(n, sd = 2), t10 = 5 + stats::rnorm(n, sd = 2), precipitation = stats::rnorm(n, sd = 1), drought = stats::rnorm(n, sd = 1), wind = stats::rnorm(n, sd = 1), sealevel = stats::rnorm(n, sd = 1), row.names = dates ) aci_df$ACI <- with(aci_df, (t90 - t10 + precipitation + drought + wind + sealevel) / 5) plot_aci_timeseries(aci_df)# Toy monthly ACI data.frame (not from calculate_aci(), just for # illustration), with the columns and "YYYY-MM" row names that # calculate_aci(area = TRUE) would return. n <- 60L dates <- format(seq(as.Date("2015-01-01"), by = "month", length.out = n), "%Y-%m") aci_df <- data.frame( t90 = 20 + stats::rnorm(n, sd = 2), t10 = 5 + stats::rnorm(n, sd = 2), precipitation = stats::rnorm(n, sd = 1), drought = stats::rnorm(n, sd = 1), wind = stats::rnorm(n, sd = 1), sealevel = stats::rnorm(n, sd = 1), row.names = dates ) aci_df$ACI <- with(aci_df, (t90 - t10 + precipitation + drought + wind + sealevel) / 5) plot_aci_timeseries(aci_df)
Computes the precipitation component of the Actuarial Climate Index.
Computes the standardised anomaly of maximum monthly precipitation over a rolling 5-day window.
precipitation_component( precipitation_data_path, country_abbrev, reference_period, study_period, mask_path = NULL, var_name = "tp", window_size = 5L, area = FALSE, admin_level = NULL, admin_mask = NULL, crs_metric = 4326, computed_components = FALSE, save = FALSE, save_dir = NULL, load_dir = NULL )precipitation_component( precipitation_data_path, country_abbrev, reference_period, study_period, mask_path = NULL, var_name = "tp", window_size = 5L, area = FALSE, admin_level = NULL, admin_mask = NULL, crs_metric = 4326, computed_components = FALSE, save = FALSE, save_dir = NULL, load_dir = NULL )
precipitation_data_path |
Path to the precipitation NetCDF file. |
country_abbrev |
Three-letter ISO country code. |
reference_period |
Character vector |
study_period |
Character vector |
mask_path |
Path to the country mask NetCDF file, or
|
var_name |
Variable name in the NetCDF. Default
|
window_size |
Rolling window in days. Default |
area |
Logical. Default |
admin_level |
Integer or |
admin_mask |
Output of |
crs_metric |
EPSG code. Default |
computed_components |
Logical. Default |
save |
Logical. Default |
save_dir |
Character. Default |
load_dir |
Character. Default |
Named numeric vector, standardised list, or data.frame
per admin unit.
Drop-in, memory-safe replacement for precipitation_component().
precipitation_component_terra( precipitation_data_path, country_abbrev, reference_period, study_period, mask_path = NULL, var_name = "tp", window_size = 5L, area = FALSE, admin_level = NULL, admin_mask = NULL, crs_metric = 4326, computed_components = FALSE, save = FALSE, save_dir = NULL, load_dir = NULL, target_chunk_gb = 1 )precipitation_component_terra( precipitation_data_path, country_abbrev, reference_period, study_period, mask_path = NULL, var_name = "tp", window_size = 5L, area = FALSE, admin_level = NULL, admin_mask = NULL, crs_metric = 4326, computed_components = FALSE, save = FALSE, save_dir = NULL, load_dir = NULL, target_chunk_gb = 1 )
precipitation_data_path |
Path to the precipitation NetCDF file. |
country_abbrev |
Three-letter ISO country code. |
reference_period |
Character vector |
study_period |
Character vector |
mask_path |
Path to the country mask NetCDF file, or
|
var_name |
Variable name in the NetCDF. Default
|
window_size |
Rolling window in days. Default |
area |
Logical. Default |
admin_level |
Integer or |
admin_mask |
Output of |
crs_metric |
EPSG code. Default |
computed_components |
Logical. Default |
save |
Logical. Default |
save_dir |
Character. Default |
load_dir |
Character. Default |
target_chunk_gb |
Passed to |
Same as precipitation_component().
Memory-safe equivalents of
calculate_maximum_precipitation_over_window() and
precipitation_component(). Only the hourly-to-daily reduction
runs through terra; the rolling-sum and monthly-max logic already
operates on small, daily-resolution data, reused as-is via the shared
helper .max_precipitation_from_daily() (defined in
precipitation.R) – no logic is duplicated.
If admin_mask is NULL, averages over all non-NA cells
(national mean). Otherwise computes a weighted mean
per administrative unit using surface-fraction weights.
reduce_dataarray_to_dataframe(metric, column_name = "value", admin_mask = NULL)reduce_dataarray_to_dataframe(metric, column_name = "value", admin_mask = NULL)
metric |
List with fields |
column_name |
Character. Column name prefix. Default |
admin_mask |
Output of |
A data.frame with one column per administrative unit (or one
column for the national mean), indexed by month-start dates.
Reads the bundled psmsl_data.csv to identify stations for the given
country abbreviation, downloads the corresponding data files from the PSMSL
website, and stores them locally.
request_sealevel_data(country_abbrev, dest_dir = NULL)request_sealevel_data(country_abbrev, dest_dir = NULL)
country_abbrev |
Three-letter ISO country code (e.g. |
dest_dir |
Destination directory. If |
Invisibly, the path to the destination directory.
Equivalent of resample_daily(). Groups layers by calendar day and
applies fun via terra::tapp().
resample_daily_terra(r, fun = "mean", filename = "", target_chunk_gb = 1)resample_daily_terra(r, fun = "mean", filename = "", target_chunk_gb = 1)
r |
A |
fun |
Aggregation function name understood by |
filename |
Optional path to write the final result directly to disk
(highly recommended for large jobs). Default |
target_chunk_gb |
Target raw INPUT size (GB) per temporal chunk, used
to decide how many chunks are needed for memory safety. Conservative
default |
Memory note : pour agreger par jour, terra::tapp() doit
lire, PAR BLOC SPATIAL, la serie temporelle COMPLETE de r
(readValues() recupere nrows x ncol x nlyr(r) valeurs d'un
coup, puis matrix() en fait une copie contigue). Sur une grille
pourtant petite (quelques milliers de cellules), nlyr(r) horaire
sur plusieurs decennies (des centaines de milliers de couches) suffit a
lui seul a depasser la limite memoire d'un processus R (constate
empiriquement : plafond de 16 Go de R sous macOS, "vector memory limit ...
reached", pour ~65 ans horaires sur la France entiere) – MEME quand le
resultat final (agrege par jour) est minuscule. C'est exactement la meme
classe de probleme que celle documentee pour terra::roll() dans
.calculate_percentiles_terra_tiled() : le pic memoire depend de la
taille de L'ENTREE lue par bloc, pas de la sortie. On applique donc ici le
meme principe, en decoupant TEMPORELLEMENT (et non spatialement, puisque
c'est le nombre de couches qui explose ici, pas le nombre de cellules) :
r est scinde en blocs de jours CONSECUTIFS (donc de couches
horaires contigues – l'ordre chronologique de r est requis),
chaque bloc est agrege independamment et ecrit sur disque, puis les blocs
journaliers (petits) sont recombines. Le resultat est identique, bloc par
bloc ou en un seul appel : chaque jour est agrege a partir de ses propres
couches horaires, jamais a cheval sur deux blocs.
Precision numerique : les blocs intermediaires sont ecrits sur
disque en datatype = "FLT8S" (double precision, 64 bits) –
explicitement force pour eviter que terra ne bascule par defaut
sur du float 32 bits, ce qui romprait l'invariance du resultat par
rapport a target_chunk_gb (chemin non-decoupe = calcul garde en
RAM, en double precision R). Le resultat est ainsi, a l'arithmetique
flottante pres (memes operations, meme ordre par groupe), rigoureusement
identique quel que soit le nombre de blocs.
A terra::SpatRaster with one layer per day.
Processes PSMSL tide-gauge data and computes the sea-level component of the Actuarial Climate Index.
Replace PSMSL sentinel values (-99999) with NA
sealevel_clean_data(df)sealevel_clean_data(df)
df |
|
Cleaned data.frame.
Calculate the sea-level component of the ACI
sealevel_component( country_abbrev, study_period, reference_period, mask_path = NULL, area = TRUE, max_dist_km = 500, sealevel_dir = NULL, admin_level = NULL, admin_assignment = NULL, crs_metric = 4326, computed_components = FALSE, save = FALSE, save_dir = NULL, load_dir = NULL )sealevel_component( country_abbrev, study_period, reference_period, mask_path = NULL, area = TRUE, max_dist_km = 500, sealevel_dir = NULL, admin_level = NULL, admin_assignment = NULL, crs_metric = 4326, computed_components = FALSE, save = FALSE, save_dir = NULL, load_dir = NULL )
country_abbrev |
Three-letter country code (e.g. |
study_period |
Character vector |
reference_period |
Character vector |
mask_path |
Path to the country mask NetCDF. Used to extract the
ERA5 grid when |
area |
Logical. Mirrors the |
max_dist_km |
Numeric. Maximum distance (km) for IDW interpolation.
Only used when |
sealevel_dir |
Character or |
admin_level |
Integer or |
admin_assignment |
Output of |
crs_metric |
EPSG code used when building |
computed_components |
Logical. If |
save |
Logical. If |
save_dir |
Character. Directory for saving results.
Default |
load_dir |
Character. Directory from which to reload a cached result
when |
If area = FALSE: a list with a [lon x lat x t] array,
lon, lat, time — same structure as other grid-cell
components. If area = TRUE: a data.frame of station
anomalies (national or per admin unit).
Compute monthly reference statistics for sea-level data
sealevel_compute_monthly_stats(df, reference_period, stats)sealevel_compute_monthly_stats(df, reference_period, stats)
df |
Clean |
reference_period |
Character vector |
stats |
|
A numeric matrix, 12 rows (calendar months "1"-"12"
as row names) x one column per station (station names as returned by
colnames(df)). Each station is standardised against its own
monthly reference statistics, independently of the other stations –
consistent with how the other ACI components (ERA5 grid cells,
administrative units) are each standardised against their own
reference, not a value pooled across the whole spatial domain.
PSMSL encodes dates as YYYY.fraction where the fraction
identifies the month.
sealevel_correct_date_format(df)sealevel_correct_date_format(df)
df |
|
The same data.frame with Date row names
("YYYY-MM-01"), rows with unrecognised dates removed.
Load sea-level txt files from a directory
sealevel_load_data(directory)sealevel_load_data(directory)
directory |
Path to the directory containing PSMSL |
A data.frame with one column per station and a numeric date
as row names.
Full sea-level processing pipeline
sealevel_process(directory, study_period, reference_period)sealevel_process(directory, study_period, reference_period)
directory |
Path to the directory with PSMSL |
study_period |
Character vector |
reference_period |
Character vector |
A named list with:
dataStandardised data.frame of anomalies
[time x stations], row names "YYYY-MM-DD". Stations
listed in excluded_stations are entirely absent from this
data.frame (all-NA columns are dropped), not merely NA.
coordsA data.frame with columns
station_id, lon, lat, one row per station
present in data (excluded stations are also dropped here).
excluded_stationsCharacter vector of station column
names (possibly empty) that have ZERO observations in
reference_period for at least one calendar month, and are
therefore impossible to standardise against Eq. A.12 (no reference
mean is definable for that station/month) – see the "Bug fix"
note below. Use this to know which stations (e.g. ones installed
after 1990) silently contribute nothing, so this isn't found out
by surprise downstream.
Stations with literally no data during reference_period (e.g. a
tide gauge installed in 2018, for a 1961-1990 reference period) used to
get a reference mean of NaN (not NA), which propagated
through sealevel_standardize_data() to make the entire
standardised series of that station NaN – including during
years where the station DOES have perfectly good data. This had no
visible effect only because reduce_sealevel_over_region()'s
rowMeans(..., na.rm = TRUE) happens to also drop NaN
(R treats NaN as a kind of NA), so the station was
silently and permanently excluded from every national/regional average
it could have contributed to, with no warning. For the French stations
listed in Garrido et al.'s Table B.2, 12 of the 40 stations (30%) have
zero overlap with 1961-1990 and were affected. This function now (a)
produces clean NA instead of NaN for this case, and more
importantly (b) explicitly detects and reports these stations via
warning() and the excluded_stations return value, instead
of relying on an implicit, easy-to-miss floating-point coincidence.
Excluding such stations is not itself a choice this function makes –
there is no principled reference baseline to compute for a station that
did not exist during the reference period – but that exclusion is now
visible rather than silent.
Standardise sea-level data over the study period
sealevel_standardize_data(df, monthly_means, monthly_std_devs, study_period)sealevel_standardize_data(df, monthly_means, monthly_std_devs, study_period)
df |
Clean |
monthly_means |
Numeric matrix, 12 rows (months |
monthly_std_devs |
Same shape as |
study_period |
Character vector |
A data.frame of standardised anomalies for the study period,
with rows containing all-NA removed. Each station's values are
standardised against its own monthly reference (see
sealevel_compute_monthly_stats()), not a value pooled across
stations.
For each calendar month, computes the mean and standard deviation over the reference period, then returns (x - mean) / sd. Optionally averages over the spatial dimensions first.
standardize_metric(metric, reference_period, area = FALSE)standardize_metric(metric, reference_period, area = FALSE)
metric |
A list with fields |
reference_period |
Character vector of length 2: start and end dates
( |
area |
Logical. If |
If area = TRUE: a named numeric vector (names = dates).
Otherwise: a list with the same structure as metric but standardised
values.
Compute daily temperature extremum (min or max) for day or night hours
temp_extremum(dataset, extremum, period)temp_extremum(dataset, extremum, period)
dataset |
List returned by |
extremum |
|
period |
|
A list with data [lon × lat × days] and daily time.
Equivalent of temp_extremum(). Filters layers by hour-of-day (a
cheap, lazy operation on a SpatRaster – no data is read), then reduces to
daily resolution via resample_daily_terra().
temp_extremum_terra(r, extremum, period, filename = "", target_chunk_gb = 1)temp_extremum_terra(r, extremum, period, filename = "", target_chunk_gb = 1)
r |
A |
extremum |
|
period |
|
filename |
Optional output path (see |
target_chunk_gb |
Passed to |
A terra::SpatRaster with one layer per day.
Computes the temperature component of the Actuarial Climate Index.
Combines day and night half-day components (equal weighting), then standardises relative to the reference period.
temperature_component( temperature_data_path, country_abbrev, reference_period, study_period, mask_path = NULL, percentile = 90, extremum = "max", above_thresholds = TRUE, area = FALSE, admin_level = NULL, admin_mask = NULL, crs_metric = 4326, computed_components = FALSE, save = FALSE, save_dir = NULL, load_dir = NULL, window_days = 5L )temperature_component( temperature_data_path, country_abbrev, reference_period, study_period, mask_path = NULL, percentile = 90, extremum = "max", above_thresholds = TRUE, area = FALSE, admin_level = NULL, admin_mask = NULL, crs_metric = 4326, computed_components = FALSE, save = FALSE, save_dir = NULL, load_dir = NULL, window_days = 5L )
temperature_data_path |
Path to the hourly |
country_abbrev |
Three-letter ISO country code. |
reference_period |
Character vector |
study_period |
Character vector |
mask_path |
Path to the country mask NetCDF file. |
percentile |
Percentile for the threshold. Default |
extremum |
|
above_thresholds |
Logical. Default |
area |
Logical. Default |
admin_level |
Integer or |
admin_mask |
Output of |
crs_metric |
EPSG code. Default |
computed_components |
Logical. Default |
save |
Logical. Default |
save_dir |
Character. Default |
load_dir |
Character. Default |
window_days |
Passed to |
Named numeric vector, standardised list, or data.frame
per admin unit.
Drop-in, memory-safe replacement for temperature_component(),
intended for full-resolution, grid-cell-level ("area = FALSE") workflows
over long historical periods (40+ years hourly).
temperature_component_terra( temperature_data_path, country_abbrev, reference_period, study_period, mask_path = NULL, percentile = 90, extremum = "max", above_thresholds = TRUE, area = FALSE, admin_level = NULL, admin_mask = NULL, crs_metric = 4326, cores = 1L, computed_components = FALSE, save = FALSE, save_dir = NULL, load_dir = NULL, target_chunk_gb = 1, window_days = 5L )temperature_component_terra( temperature_data_path, country_abbrev, reference_period, study_period, mask_path = NULL, percentile = 90, extremum = "max", above_thresholds = TRUE, area = FALSE, admin_level = NULL, admin_mask = NULL, crs_metric = 4326, cores = 1L, computed_components = FALSE, save = FALSE, save_dir = NULL, load_dir = NULL, target_chunk_gb = 1, window_days = 5L )
temperature_data_path |
Path to the hourly |
country_abbrev |
Three-letter ISO country code. |
reference_period |
Character vector |
study_period |
Character vector |
mask_path |
Path to the country mask NetCDF file. |
percentile |
Percentile for the threshold. Default |
extremum |
|
above_thresholds |
Logical. Default |
area |
Logical. Default |
admin_level |
Integer or |
admin_mask |
Output of |
crs_metric |
EPSG code. Default |
cores |
Passed through to |
computed_components |
Logical. Default |
save |
Logical. Default |
save_dir |
Character. Default |
load_dir |
Character. Default |
target_chunk_gb |
Passed to |
window_days |
Passed to |
Same as temperature_component(): a standardized metric list,
or a data frame if admin_mask/admin_level is provided.
Memory-safe equivalents of calculate_halfday_component()
and temperature_component(), for full-resolution, grid-cell-level
workflows over long historical periods (see R/component_terra.R
for the underlying loading/reduction primitives).
Only the hourly-scale steps (temp_extremum_terra(),
calculate_percentiles_terra()) run through terra. Once reduced to
daily/monthly resolution, the exact same .crossing_frequency()
helper used by the base-R pipeline takes over – no logic is duplicated.
Helper functions to manipulate and merge climate data.
A collection of ggplot2-based plotting functions to visualize the Actuarial Climate Index (ACI) and its components. Four families of plots are provided:
Time series of the global ACI (plot_aci_timeseries)
Component decomposition stacked / faceted chart
(plot_aci_components)
Geographic maps from a NetCDF-derived array
(plot_aci_map)
Distribution plots - boxplots and density ridges
(plot_aci_distribution)
Calculate the wind component of the ACI
wind_component( wind_u10_data_path, wind_v10_data_path, country_abbrev, reference_period, study_period, mask_path = NULL, area = FALSE, admin_level = NULL, admin_mask = NULL, crs_metric = 4326, computed_components = FALSE, save = FALSE, save_dir = NULL, load_dir = NULL )wind_component( wind_u10_data_path, wind_v10_data_path, country_abbrev, reference_period, study_period, mask_path = NULL, area = FALSE, admin_level = NULL, admin_mask = NULL, crs_metric = 4326, computed_components = FALSE, save = FALSE, save_dir = NULL, load_dir = NULL )
wind_u10_data_path |
Path to the u10 NetCDF file. |
wind_v10_data_path |
Path to the v10 NetCDF file. |
country_abbrev |
Three-letter ISO country code. |
reference_period |
Character vector |
study_period |
Character vector |
mask_path |
Path to the country mask NetCDF file, or
|
area |
Logical. Default |
admin_level |
Integer or |
admin_mask |
Output of |
crs_metric |
EPSG code. Default |
computed_components |
Logical. Default |
save |
Logical. Default |
save_dir |
Character. Default |
load_dir |
Character. Default |
Named numeric vector, standardised list, or data.frame
per admin unit.
Drop-in, memory-safe replacement for wind_component(), intended for
full-resolution, grid-cell-level ("area = FALSE") workflows over long
historical periods (40+ years hourly).
wind_component_terra( wind_u10_data_path, wind_v10_data_path, country_abbrev, reference_period, study_period, mask_path = NULL, area = FALSE, admin_level = NULL, admin_mask = NULL, crs_metric = 4326, computed_components = FALSE, save = FALSE, save_dir = NULL, load_dir = NULL, target_chunk_gb = 1 )wind_component_terra( wind_u10_data_path, wind_v10_data_path, country_abbrev, reference_period, study_period, mask_path = NULL, area = FALSE, admin_level = NULL, admin_mask = NULL, crs_metric = 4326, computed_components = FALSE, save = FALSE, save_dir = NULL, load_dir = NULL, target_chunk_gb = 1 )
wind_u10_data_path |
Path to the u10 NetCDF file. |
wind_v10_data_path |
Path to the v10 NetCDF file. |
country_abbrev |
Three-letter ISO country code. |
reference_period |
Character vector |
study_period |
Character vector |
mask_path |
Path to the country mask NetCDF file, or
|
area |
Logical. Default |
admin_level |
Integer or |
admin_mask |
Output of |
crs_metric |
EPSG code. Default |
computed_components |
Logical. Default |
save |
Logical. Default |
save_dir |
Character. Default |
load_dir |
Character. Default |
target_chunk_gb |
Passed to |
Same as wind_component().
Wind speed = sqrt(u10^2 + v10^2), wind power = 0.5 * rho * ws^3.
wind_power(u10_dataset, v10_dataset, reference_period = NULL)wind_power(u10_dataset, v10_dataset, reference_period = NULL)
u10_dataset |
List returned by |
v10_dataset |
List returned by |
reference_period |
Optional character vector |
Bug fix (see NEWS): earlier versions averaged u10 and
v10 to daily means SEPARATELY first, then took the magnitude of
that daily-mean vector (sqrt(mean(u)^2 + mean(v)^2)) as the "daily
mean wind speed". This "vector averaging" systematically UNDER-estimates
the true daily mean wind speed whenever wind direction varies over the
day, because opposing (or merely differing) hourly components partially
cancel out before the magnitude is taken – e.g. a wind blowing at a
constant 8 m/s whose direction oscillates by only +/-60 degrees over the
day averages out to a vector magnitude of ~6 m/s, a ~58% error on the
resulting wind power (0.5*rho*w^3), verified numerically. The standard
meteorological convention for a "daily mean wind speed" is instead to
average the INSTANTANEOUS scalar speed sqrt(u^2+v^2) computed at
each timestep, which is always positive and does not suffer from this
cancellation; that is what this function now does.
A list with data [lon x lat x days] (wind power in W/m²)
and daily time.
Terra-based equivalent of wind_power(): computes daily mean u/v via
resample_daily_terra() (block-by-block, never loading the full
hourly cube in RAM), then wind speed/power arithmetic directly on the
(now daily, small) SpatRasters before converting back to the package's
list format.
wind_power_terra( u10_r, v10_r, reference_period = NULL, mask_path = NULL, threshold = 0.8, target_chunk_gb = 1 )wind_power_terra( u10_r, v10_r, reference_period = NULL, mask_path = NULL, threshold = 0.8, target_chunk_gb = 1 )
u10_r, v10_r
|
|
reference_period |
Optional character vector |
mask_path |
Path to the mask NetCDF file, or |
threshold |
Numeric threshold for the mask. Default |
target_chunk_gb |
Passed to |
Same structure as wind_power(): list(data, time, lon, lat).
Memory-safe equivalents of wind_power() and
wind_component(). Only wind_power_terra() touches raw
hourly data (via terra); wind_thresholds()'s internal logic,
threshold-crossing, and monthly aggregation already operate on DAILY
resolution data (small even for 40+ years), so they are reused as-is via
the shared helpers .wind_thresholds_from_wp(),
.days_above_from_wp(), .monthly_frequency_from_binary()
defined in wind.R – no logic is duplicated.
Calculate wind power thresholds (90th percentile per day-of-year)
wind_thresholds(u10_dataset, v10_dataset, reference_period)wind_thresholds(u10_dataset, v10_dataset, reference_period)
u10_dataset |
List for |
v10_dataset |
List for |
reference_period |
Character vector |
A list with data [lon x lat x all_days] giving the threshold
for each day of the full series, and time.