Heatwave and coldwave duration is the total duration of “extreme spells”; the total number of days in a season where the temperature exceeds a threshold over a minimum number of consecutive days. Other tools for computing such events are also available, but the novelty of this tool is that the users can select their own thresholds (based on quantiles) and minimum duration for an event. The duration of heatwaves and coldwaves helps to understand potential changes in energy demand.
Note: This vignette has been written to process and plot the output of an ESMValTool namelist configured with a specific set of parameters. If you wish to run and plot the outputs for another set of parameters, you need to re-run the corresponding namelist with the desired parameters, interpolate the output NetCDF files to a common grid, and modify the vignette code accordingly.
The following example will compute the heatwvaves and coldwaves duration in the North Atlantic - European Sector [20ºW-50ºE; 30ºN-80ºN] using the CMIP5 projection for the 8.5 scenario.
This example requires the following system libraries:
You will need to install specific versions of certain R packages as follows:
library(devtools)
install_git('https://earth.bsc.es/gitlab/es/startR', branch = 'develop-hotfixes-0.0.2')
Six functions should be loaded by running the following lines in R, until integrated into the new magic.bsc package.
source('https://earth.bsc.es/gitlab/nperez/magic.bsc/raw/develop-bugfixes-0.0.0/R/Climdex.R')
source('https://earth.bsc.es/gitlab/nperez/magic.bsc/raw/develop-bugfixes-0.0.0/R/Threshold.R')
source('https://earth.bsc.es/gitlab/nperez/magic.bsc/raw/develop-bugfixes-0.0.0/R/DailyAno.R')
source('https://earth.bsc.es/gitlab/nperez/magic.bsc/raw/develop-bugfixes-0.0.0/R/WeightedMean.R')
source('https://earth.bsc.es/gitlab/nperez/magic.bsc/raw/develop-bugfixes-0.0.0/R/CombineIndices.R')
source('https://earth.bsc.es/gitlab/nperez/magic.bsc/raw/develop-bugfixes-0.0.0/R/SeasonSelect.R')
source('https://earth.bsc.es/gitlab/nperez/s2dverification/raw/develop-magic/R/WaveDuration.R')
source('https://earth.bsc.es/gitlab/nperez/s2dverification/raw/develop-magic/R/WaveIntensity.R')
source('https://earth.bsc.es/gitlab/nperez/s2dverification/raw/develop-magic/R/WaveStart.R')
All the other R packages involved can be installed directly from CRAN and loaded as follows:
library(s2dverification)
library(startR)
library(multiApply)
library(ggplot2)
library(climdex.pcic)
library(parallel)
The Heatwaves duration is the number of consecutive days for which the maximum temperature is exceeding a threshold over a minimum number of days during summer.
The next image shows an example of 17 days heatwave duration in a year corresponding to the red bars. Five days is the minimum number of consecutive days considered in the example.
In this example, the threshold is defined as the 90th percentile of maximum temprature during the period 1971-2000.
The historical simulations for the CMIP5 model should be loaded by the function Start
from s2dverification
package:
var0 <- 'tasmax'
start_climatology <- '1971-01-01'
end_climatology <- '2000-12-31'
lat.min <- 30; lat.max <- 80
lon.min <- -20; lon.max <- 50
climatology_filenames <- paste0("/esarchive/scratch/pbretonn/cmip5-cp4cds/historical/day/tasmax/",
"tasmax_day_IPSL-CM5A-LR_historical_r1i1p1_19500101-20051231.nc")
reference_data <- Start(model = climatology_filenames, var = var0, var_var = 'var_names',
time = values(list(as.POSIXct(start_climatology), as.POSIXct(end_climatology))),
lat = values(list(lat.min, lat.max)), lon = values(list(lon.min, lon.max)),
lon_var = 'lon', lon_reorder = CircularSort(0, 360), return_vars = list(time = 'model',
lon = 'model', lat = 'model'), retrieve = TRUE)
metadata <- attributes(reference_data)
The summer data can be picked by SeasonSelect
function from the magic.bsc
package.
reference_data <- SeasonSelect(reference_data, seasons = 'JJA')
To define the heat wave it is necessary to select the maximum temperature threshold that must be exceeded. In this example, the 90 th percentile of the maximum temperature during the reference periode is selected. This calculation is performed using Threshold
function from magic.bsc
package.
quantile <- 0.9
base_range <- as.numeric(c(substr(start_climatology, 1, 4), substr(end_climatology, 1, 4)))
thresholds <- Threshold(reference_data, base_range = as.numeric(base_range),
qtiles = quantile, ncores = detectCores() -1)[152:243,,]
names(dim(thresholds)) <- c('jdays', 'lat', 'lon')
Considering the 8.5 rcp scenario of the IPSL-CM5A-LR model during the period 2020-2040, the data is loaded using the function Start
.
start_model <- '2020-01-01'
end_model <- '2040-12-31'
model_filenames <- paste0("/esarchive/scratch/pbretonn/cmip5-cp4cds/",
"rcp85/day/tasmax/tasmax_day_IPSL-CM5A-LR_rcp85_r1i1p1_20060101-22051231.nc")
rcp_data <- Start(model = model_filenames, var = var0, var_var = 'var_names',
time = values(list(as.POSIXct(start_model), as.POSIXct(end_model))),
lat = values(list(lat.min, lat.max)), lon = values(list(lon.min, lon.max)),
lon_var = 'lon', lon_reorder = CircularSort(0, 360),
return_vars = list(time = 'model', lon = 'model', lat = 'model'),
retrieve = TRUE)
rcp_data <- SeasonSelect(rcp_data, seasons = 'JJA')
It will be necessary an index identifiying the year in which each value is recorded. In this case, a simple ordered sequence of years is built by repeting the value of the year 92 times (the number of days in summer):
years <- sort(rep(substr(start_model, 1, 4):substr(end_model, 1, 4), 92))
The duration of the heatwaves is obtained by using the function Apply
with WaveDuration
function. The parameter case
is defined as heat
to indicate that we are considering heatwaves and the minimum length of a heatwave is 5 days in this example. This function returns the number of days for each summer in which the maximum temperature is exceeding the 90th percentile of the reference period when they occur in a cluster of a minimum length of 5 consequtive days. This means that isolated events are not included.
duration <- Apply(list(rcp_data, thresholds), target_dims = list(c('time'), c('jdays')), AtomicFun = WaveDuration,
case = 'heat', min.conse = 5, indices = years, output_dims = 'time')
The spatial representation of the maximum, mean and minimum duration of heat waves can be plotted and saved in the working directory by running:
lat <- attr(rcp_data, "Variables")$dat1$lat
lon <- attr(rcp_data, "Variables")$dat1$lon
breaks <- seq(0,92,4)
png("Spatial_Heatwave_rcp85_2020-2040_90thpercentile_1971-2000.png", width = 8, height = 12, units = 'in',
res = 100, type = "cairo")
par(mfrow=c(4,1))
PlotEquiMap(apply(duration$output1, c(2,3), max), lon = lon, lat = lat, brks = breaks, drawleg = FALSE,
filled.continents = FALSE, toptitle = c("Heatwave duration rcp 8.5 2020-2040 compared 90th percentile 1971-2000","Maximum"), title_scale = 0.8,
cols = heat.colors(length(breaks)-1)[(length(breaks)-1):1])
PlotEquiMap(apply(duration$output1, c(2,3), mean), lon = lon, lat = lat, filled.continents = FALSE, brks = breaks,
drawleg = FALSE, toptitle = "Mean", title_scale = 0.8,
cols = heat.colors(length(breaks)-1)[(length(breaks)-1):1])
PlotEquiMap(apply(duration$output1, c(2,3), min), lon = lon, lat = lat, filled.continents = FALSE, brks = breaks,
drawleg = FALSE, toptitle = "Minimum", title_scale = 0.8,
cols = heat.colors(length(breaks)-1)[(length(breaks)-1):1])
ColorBar(brks = breaks, vertical = FALSE, extra_margin = c(8,1.5,0.5,0.5), title = "Number of summer days",
cols = heat.colors(length(breaks)-1)[(length(breaks)-1):1])
dev.off()
The zonal average can be computed and plotted to detect differences with latitude:
png("Latitudinal_Heatwave_rcp85_2020-2040_90thpercentile_1971-2000.png", width = 8, height = 5, units = 'in',
res = 100, type = "cairo")
layout(matrix(c(1,1,1,2), ncol =4, nrow=1))
matplot(2020:2040, apply(duration$output1, c(1,2), mean), type = "l", lwd = 2, col = heat.colors(26)[26:1], bty = 'n', xlab = "Time (years)", ylab = "Duration (days)", main = "Zonal Average of Heatwaves Durations", cex.axis = 1.3)
ColorBar(brks = c(lat[1] - 0.9473675, lat + 0.9473675), vertical = TRUE, extra_margin = c(2,1.5,2,1.5),
title = "Latitude", cols = heat.colors(26)[26:1])
dev.off()
Intensities of heatwave are calculated as the maximum difference between the threshold and the maximum temperature achieved for each summer during a heatwave. Again, Apply
function is used with WaveIntensity
function:
intensities <- Apply(list(rcp_data, thresholds), target_dims = list(c('time'), c('jdays')), AtomicFun = WaveIntensity,
case = 'heat', min.conse = 5, indices = years, output_dims = 'time')
The mean spatial distribution can be plotted and saved by running:
breaks = 0:18
PlotEquiMap(apply(intensities$output1, c(2,3), mean), lon = lon, lat = lat, filled.continents = FALSE,
brks = breaks, drawleg = TRUE, toptitle = c("Heatwaves rcp 8.5 2020-2040 compared 90th percentile 1971-2000", "Mean Intensity"), title_scale = 0.6, cols = heat.colors(length(breaks)-1)[(length(breaks)-1):1],
fileout = "MeanIntensityHeatwaves_rcp85_2020-2040_90thpercentile_1971-2000.png")
The temporal evolution of the zonal average can be visualized and saved by:
png("Latitudinal_Intensity_Heatwave_rcp85_2020-2040_90thpercentile_1971-2000.png", width = 8, height = 5, units = 'in',
res = 100, type = "cairo")
layout(matrix(c(1,1,1,2), ncol =4, nrow=1))
matplot(2020:2040, apply(intensities$output1, c(1,2), mean), type = "l", lwd = 2, col = heat.colors(26)[26:1], bty = 'n', xlab = "Time (years)", ylab = "Intensity (ºC)", main = "Zonal Average of Heatwaves Intensity", cex.axis = 1.3)
ColorBar(brks = c(lat[1] - 0.9473675, lat + 0.9473675), vertical = TRUE, extra_margin = c(2,1.5,2,1.5),
title = "Latitude", cols = heat.colors(26)[26:1])
dev.off()
Another interesting value can be the date in wich the first day of a heatwave is recorded:
start_date <- Apply(list(rcp_data, thresholds), target_dims = list(c('time'), c('jdays')), AtomicFun = WaveStart,
case = 'heat', min.conse = 5, indices = years, output_dims = 'time')
The function WaveStart
returns the month and day in the format ‘month-day’. It can be convert to a number from 1 to 92 to facilitate future calculations: Notice that function WaveStart
returns 0 wether the minimum duration of the heatwaves is not reached.
summer <- c(paste(rep(6,30), 1:30, sep = "-"), paste(rep(7,31), 1:31, sep = "-"), paste(rep(8,31), 1:31, sep = "-"))
start_date <- apply(start_date$output1, c(1,2,3), match, summer)
The spatial distribution of the earliest starting date of a Heatwave can be plotted and saved:
PlotEquiMap(apply(start_date, c(2,3), min, na.rm = TRUE), lon = lon, lat = lat, filled.continents = FALSE, brks = 0:92,
fileout = "EarliestStartingDateHeatwaves_rcp85_2020-2040_90thpercentile_1971-2000.png",
toptitle = c("Heatwaves rcp 8.5 2020-2040 compared to 90th percentile of 1971-2000",
"Earliest Starting Date"), title_scale = 0.6)
And its temporal evolution by running:
png("Latitudinal_Start_Heatwave_rcp85_2020-2040_90thpercentile_1971-2000.png", width = 8, height = 5, units = 'in',
res = 100, type = "cairo")
layout(matrix(c(1,1,1,2), ncol =4, nrow=1))
matplot(2020:2040, apply(start_date, c(1,2), mean, na.rm = TRUE), type = "l", lwd = 2, col = heat.colors(26)[26:1], bty = 'n', xlab = "Time (years)", ylab = "Start Date (days)", main = "Zonal Average of Heatwaves Start Date", cex.axis = 1.3)
ColorBar(brks = c(lat[1] - 0.9473675, lat + 0.9473675), vertical = TRUE, extra_margin = c(2,1.5,2,1.5),
title = "Latitude", cols = heat.colors(26)[26:1])
dev.off()