Skip to contents

This vignette illustrates how to use disturbance scenarii with matreex package. A disturbance scenario provides the timing and intensity at which different types of disturbance (storm, fire and biotic) will affect tree mortality in each size class. The tree killed by the disturbance are saved within the harvested output.

The basic matreex functions are shown in a previous introduction vignette.

Simulations input

Define a species

The first step is the IPM integration and species object creation. This part is common with basic usage of the package, so nothing is very important here.

Please keep in mind this computation is intensive and may take few minutes !

library(matreex)
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
library(ggplot2)

# Load fitted model for a species
# fit_species # list of all species in dataset
data("fit_Picea_abies")

# Load associated climate
data("climate_species")
climate <- subset(climate_species, N == 2 & sp == "Picea_abies", select = -sp)
# see ?climate_species to understand the filtering of N.
climate
#>        sgdd       wai        sgddb      waib      wai2   sgdd2      PC1
#> 62 1444.667 0.4519387 0.0006922012 0.6887343 0.2042486 2087062 1.671498
#>           PC2 N       SDM
#> 62 0.02602064 2 0.6760556

Picea_ipm <- make_IPM(
    species = "Picea_abies", 
    climate = climate, 
    fit = fit_Picea_abies,
    clim_lab = "optimum clim", 
    mesh = c(m = 700, L = 90, U = get_maxdbh(fit_Picea_abies) * 1.1),
    BA = 0:70, # Default values are 0:200, smaller values speed up this vignette.
    verbose = TRUE
)
#> Launching integration loop
#> GL integration occur on 32 cells
#> midbin integration occur on 25 cells
#> Loop done.
#> Time difference of 45.7 secs

Latter simulations will start at equilibrium, so we compute this before anything. Picea_equil is the equilibrium distribution after 2500 years.

Picea_sp <- species(IPM = Picea_ipm, init_pop = def_initBA(40))
Picea_for <- forest(species = list(Picea = Picea_sp))
set.seed(42)
Picea_sim <- sim_deter_forest(Picea_for, tlim = 2500,
                              equil_dist = 250, equil_time = 2500,
                              verbose = TRUE) 
#> Starting while loop. Maximum t = 2500
#> time 500 | BA diff : 15.07
#> time 1000 | BA diff : 6.33
#> time 1500 | BA diff : 2.80
#> time 2000 | BA diff : 1.25
#> time 2500 | BA diff : 0.56
#> Simulation ended after time 2500
#> BA stabilized at 36.43 with diff of 0.56 at time 2500
#> Time difference of 14.5 secs

Picea_equil <- filter(Picea_sim, var == "n", equil) %>% pull(value)

Disturbance

Definition of disturbance

We define a disturbance by few parameters used later in the formula. Details on the model definition and dataset used can be found in Barrere et al. (2023).

The calibration of the equations of disturbance mortality implemented in the package is described in details in Barrere et al. (2023). We characterize a disturbance in the simulations with two variables:

  • \(I\), its intensity between 0 and 1.

  • \(type\), the class of disturbance. This is a label in "storm", "fire" and "biotic". This is used to filter species parameters fitted, which were estimated separately for each disturbance type.

These variables are set in a data.frame object. A last column named IsSurv is used to inform whether the survival part of the IPM (competition-induced mortality) is needed during a disturbance. In Barrere et al. (2023), the model does not differentiate disturbance mortality and background mortality. Therefore, we recommend to deactivate baseline mortality to avoid an overestimation of mortality.

ex_disturb <- data.frame(type = "storm", intensity = 0.5, IsSurv = FALSE)

Impact on population

The disturbance impact on the population is calculated from parameters estimated in Barrere et al. (2023). The disturbance mortality equation takes the size distribution, quadratic diameter of the species, intensity (\(I\)) and duration (\(t\)) of the disturbance as inputs. A set of parameters is associated to each type of disturbance and species. This is computed with the following equations:

\[ dqm = \sqrt{\frac{\sum_{i=1}^m dbh_i^2 \times n_i}{\sum_{i=1}^m n_i}} \\ \] where \(dqm\) is the quadratic diameter, \(m\) is the number of size classes, and \(n_i\) is the number of trees in the size class and \(dbh_i\) is the mean diameter of the size class (in the model the number of trees is a continuous density per area).

\[ logratio_i = log(\frac{dbh_i}{dqm}) \\ \] In the original model fitted in Barrere et al. (2023), \(logratio\) and \(dbh\) were centered and scaled. The linear parameters \(logratio.intercept\), \(logratio.slope\), \(dbh.intercept\) and dbh.slope$ are used to apply the same transformation for these two variables: \[ dbh.scaled_i = dbh.intercept + dbh_i \times dbh.slope \\ \] \[ logratio.scaled_i = logratio.intercept + logratio_i \times logratio.slope \\ \] \[ Pdeath_{i,t} = 1- (1 - logist(a_0 + a_1 \times logratio.scaled_i + b \times I*dbh.scaled^{c }))^t \]

Where, \(Pdeath_{i,t}\) is the disturbance probability of mortality over \(t\) years of the individual \(i\), of dbh \(dbh_i\). The parameters are estimated with Bayesian computations. The mean of all posterior estimations are stored inside the package for each combination of species and disturbance type.

(Picea_coefs <- filter(matreex::disturb_coef, species == "Picea_abies"))
#>   disturbance     species        a0         a1        b           c
#> 1      biotic Picea_abies -5.635219  0.0000000 5.832025 -0.08002929
#> 2       storm Picea_abies -4.628462 -0.1402871 5.409031  0.00142451
#>   dbh.intercept   dbh.slope logratio.intercept logratio.slope
#> 1    -0.7870239 0.007930586        -0.14791154       3.067571
#> 2    -0.7219773 0.007283356        -0.09485699       2.831488
Picea_sp$disturb_coef <- Picea_coefs

Along with this set of parameters, we need to provide a disturbance function to the species we want to simulate. The species is initiated with a default empty function that will throw warnings. The function also require the quadratic mean diameter of the population.

Below is an example of the difference in size distribution before and after a disturbance of type "storm" and intensity 0.7.

#' Disturbance function
matreex::disturb_fun
#> function (x, species, disturb = NULL, ...) 
#> {
#>     dots <- list(...)
#>     qmd <- dots$qmd
#>     size <- species$IPM$mesh
#>     coef <- species$disturb_coef
#>     disturbance <- NULL
#>     if (any(disturb$type %in% coef$disturbance)) {
#>         coef <- subset(coef, disturbance == disturb$type)
#>     }
#>     else {
#>         stop(sprintf("The species %s miss this disturbance type (%s) parameters", 
#>             sp_name(species), disturb$type))
#>     }
#>     size[size == 0] <- min(size[size != 0])
#>     logratio <- log(size/qmd)
#>     dbh.scaled = coef$dbh.intercept + size * coef$dbh.slope
#>     logratio.scaled = coef$logratio.intercept + logratio * coef$logratio.slope
#>     Pkill <- plogis(coef$a0 + coef$a1 * logratio.scaled + coef$b * 
#>         disturb$intensity^(coef$c * dbh.scaled))
#>     return(x * Pkill)
#> }
#> <bytecode: 0x562421952588>
#> <environment: namespace:matreex>
Picea_sp$disturb_fun <- disturb_fun

Any user can define the disturb_fun() function they need and provide parameters for the wanted species. Obviously, this type of modification have not been thoroughly tested and thus is more prone to error so don’t hesitate to contact matreex maintainer in this eventuality.

Simulations

Running a simulation takes the same parameters as usual, with an added data.frame with disturbance along time. We will start from equilibrium and have a disturbance after 100 years.

disturb <- data.frame(type = "storm", intensity = 0.2, IsSurv = FALSE, t = 100)

Disturbance size distribution is saved in harvest output. Harvest and basal mortality are canceled when a disturbance happens.

Picea_sp$init_pop <- def_init_k(Picea_equil)

disturb_picea <- sim_deter_forest(
    forest(species = list(Picea = Picea_sp)),
    tlim = 1000, equil_dist = 250, equil_time = 1000,
    disturbance  = disturb, # providing disturbance table
    verbose = TRUE)
#> Starting while loop. Maximum t = 1000
#> time 100 | Disturbance : storm I = 0.20
#> time 500 | BA diff : 21.42
#> time 1000 | BA diff : 9.29
#> Simulation ended after time 1000
#> BA stabilized at 40.57 with diff of 9.29 at time 1000
#> Time difference of 5.79 secs

disturb_picea %>%
    filter(var %in% c("BAsp", "H"), ! equil, value != 0) %>%
    ggplot(aes(x = time, y = value)) +
    facet_wrap(~ var, scales = "free_y") +
    geom_line(linewidth = .4) + geom_point(size = .4)

Multispecific simulations

Plurispecific simulations are performed by creating species object and attributing disturbance function to each species individually as previously illustrated. The disturbance dataframe is common to all species included in the simulations. We also change the harvesting model to illustrate other kind of simulations.

data("fit_Fagus_sylvatica")

Fagus_ipm <- make_IPM(
    species = "Fagus_sylvatica", 
    climate = climate, 
    fit = fit_Fagus_sylvatica,
    clim_lab = "optimum clim", 
    mesh = c(m = 700, L = 90, U = get_maxdbh(fit_Fagus_sylvatica) * 1.1),
    BA = 0:70, 
    verbose = TRUE
)
#> Launching integration loop
#> GL integration occur on 21 cells
#> midbin integration occur on 25 cells
#> Loop done.
#> Time difference of 25.3 secs
Fagus_sp <- species(
    IPM = Fagus_ipm, init_pop = def_initBA(30),
    disturb_fun = disturb_fun, 
    harvest_fun = Uneven_harv,
    disturb_coef = filter(matreex::disturb_coef, species == "Fagus_sylvatica")
)
time <- 1500
disturb <- data.frame(type = "storm", intensity = 0.2, IsSurv = FALSE, t = 500)
Picea_sp$harvest_fun <- Uneven_harv

set.seed(42)
disturb_PiFa <- sim_deter_forest(
    forest(species = list(Picea = Picea_sp, Fagus = Fagus_sp),
           harv_rules = c(Pmax = 0.25, dBAmin = 3, freq = 10, alpha = 1)), 
    tlim = 1000, equil_dist = 250, equil_time = 1000,
    disturbance  = disturb,
    harvest = "Uneven", targetBA = 30,
    verbose = TRUE, correction = "cut")
#> apply a IPM cut correction
#> Starting while loop. Maximum t = 1000
#> time 500 | Disturbance : storm I = 0.20
#> time 500 | BA diff : 19.10
#> time 1000 | BA diff : 6.46
#> Simulation ended after time 1000
#> BA stabilized at 32.29 with diff of 6.46 at time 1000
#> Time difference of 9.14 secs

disturb_PiFa %>%
    filter(var %in% c("BAsp", "H"), time > 450 & time < 600, ! equil) %>%
    ggplot(aes(x = time, y = value, color = species)) +
    facet_wrap(~ var, scales = "free_y") +
    geom_line(linewidth = .4) + geom_point(size = .4)

Linear stabilizing effect of species mixture

When simulating the effect of biotic disturbances on forests mixing coniferous and broadleaf species, an additional effect can be taken into account based on the survival models developed by Brandl et al. (2020). This mixture effect is a linear function, and the mean of the function has been scaled to match the average mortality disturbance estimated by Barrere et al. (2023) for the average share of conifers in the fitting data.

To include this effect, the probability of disturbance mortality is multiplied by the linear function linking the share of conifer to biotic mortality.

\[ f_{mixture.effect}\left(share.conifers\right) = 0.4767 + 0.6015 \cdot share.conifers \\ \] \[ Pdeath_{new}\left(share.conifers\right) = Pdeath_{disturb} \cdot f_{mixture.effect}\left(share.conifers\right) \]

To compute the share of conifers, the type (broadleaf or conifer) of each species needs to be set as shown below. This can be set when creating the species object or edited later :

Picea_sp$info["type"]
#>        type 
#> "Undefined"
Picea_sp$info["type"] <- "Coniferous"
Fagus_sp$info["type"] <- "Broadleaf"

An alternative disturbance function is provided by the package as disturb_fun_mixt(), to accept the input of the coniferous percentage computed at the plot scale before disturbing each species and to modify the mortality probability (Pkill) if the species is a coniferous.

matreex::disturb_fun_mixt
#> function (x, species, disturb = NULL, ...) 
#> {
#>     dots <- list(...)
#>     qmd <- dots$qmd
#>     coni_perc <- dots$perc_coni
#>     size <- species$IPM$mesh
#>     coef <- species$disturb_coef
#>     disturbance <- NULL
#>     if (any(disturb$type %in% coef$disturbance)) {
#>         coef <- subset(coef, disturbance == disturb$type)
#>     }
#>     else {
#>         stop(sprintf("The species %s miss this disturbance type (%s) parameters", 
#>             sp_name(species), disturb$type))
#>     }
#>     size[size == 0] <- min(size[size != 0])
#>     logratio <- log(size/qmd)
#>     dbh.scaled = coef$dbh.intercept + size * coef$dbh.slope
#>     logratio.scaled = coef$logratio.intercept + logratio * coef$logratio.slope
#>     Pkill <- plogis(coef$a0 + coef$a1 * logratio.scaled + coef$b * 
#>         disturb$intensity^(coef$c * dbh.scaled))
#>     if (species$info["type"] == "Coniferous" && disturb$type == 
#>         "biotic") {
#>         message(sprintf("sp : %s | coni : %.3f | modif : %.2f", 
#>             species$info["species"], coni_perc, (0.4767 + 0.6015 * 
#>                 coni_perc)))
#>         Pkill <- Pkill * (0.4767 + 0.6015 * coni_perc)
#>     }
#>     Pkill <- pmin(Pkill, 1)
#>     return(x * Pkill)
#> }
#> <bytecode: 0x562420bef670>
#> <environment: namespace:matreex>
# Giving the new disturb_fun
Picea_sp$disturb_fun <- disturb_fun_mixt
Fagus_sp$disturb_fun <- disturb_fun_mixt

This is an example simulation. Note that it is imperative to apply this mixture effect with biotic disturbance types only, since this effect was only calibrated for biotic mortality.

disturb <- data.frame(type = "biotic", intensity = 0.2, IsSurv = FALSE, t = 500)
set.seed(42)
mixture_PiFa <- sim_deter_forest(
    forest(species = list(Picea = Picea_sp, Fagus = Fagus_sp)), 
    tlim = 1000, equil_dist = 250, equil_time = 1000,
    disturbance  = disturb,
    harvest = "Uneven", targetBA = 30,
    verbose = TRUE, correction = "cut")
#> apply a IPM cut correction
#> Starting while loop. Maximum t = 1000
#> time 500 | Disturbance : biotic I = 0.20
#> sp : Picea_abies | coni : 0.588 | modif : 0.83
#> time 500 | BA diff : 18.84
#> time 1000 | BA diff : 2.82
#> Simulation ended after time 1000
#> BA stabilized at 32.58 with diff of 2.82 at time 1000
#> Time difference of 9.67 secs

mixture_PiFa %>%
    filter(var %in% c("BAsp", "N"), ! equil) %>%
    ggplot(aes(x = time, y = value, color = species)) +
    facet_wrap(~ var, scales = "free_y") +
    geom_line(linewidth = .4) + geom_point(size = .4)

I compare the two simulations in order to check the presence of a mixture effect. As seen above in messages, the mortality is multiplied by 0.74, which we see here with the blue line under the red one for Picea abies.

Yes, the disturbance mortality is saved in the harvest variable since there is no harvest when there is a disturbance.

bind_rows(mixture = mixture_PiFa, basic =  disturb_PiFa, .id = "jasper") %>% 
    filter(var == "h", time == 500) %>%
    ggplot(aes(x = mesh, y = value, color = jasper)) + 
    facet_wrap(~ species, scales = "free_y") +
    geom_line(linewidth = .4) + geom_point(size = .4)

References

Barrere, Julien, Björn Reineking, Thomas Cordonnier, Niko Kulha, Juha Honkaniemi, Mikko Peltoniemi, Kari T. Korhonen, Paloma Ruiz-Benito, Miguel A. Zavala, and Georges Kunstler. 2023. “Functional Traits and Climate Drive Interspecific Differences in Disturbance-Induced Tree Mortality.” Global Change Biology 29 (10): 2836–51. https://doi.org/https://doi.org/10.1111/gcb.16630.
Brandl, Susanne, Carola Paul, Thomas Knoke, and Wolfgang Falk. 2020. “The Influence of Climate and Management on Survival Probability for Germany’s Most Important Tree Species.” Forest Ecology and Management 458: 117652. https://doi.org/https://doi.org/10.1016/j.foreco.2019.117652.