Skip to contents

Mechanistic species distribution modelling

This vignette describes the available options of incorporating mechanistic modelling approaches in the ibis.iSDM package. These approaches can broadly be separated as those that are “added” to existing modelling routines, or how ibis.iSDM outputs can be used as input in mechanistic modelling. For both this package provides some basic wrappers.

Before we delve into the options to do mechanistic SDMs in the ibis.iSDM package, it is useful to remind us what the term ‘mechanism’ actually means. In the literature there are a range of different definitions, sometimes referring to mechanistic SDMs as those that incorporate ecological processes (e.g. demography, dispersal, eco-evolutionary principles). Yet often, correlative SDMs are also declared “mechanistic” if they somehow incorporate a specific constrain or response function towards an environmental variable. For example, the micro-climatic limits to the persistence of a species Briscoe et al. 2023, or the presence of biotic interactions (estimated through a separate SDM of a different species) are also sometimes referred to as limiting mechanisms (Ohlmann et al. 2023).

The latter approaches - which are largely a fine-tuning of specific response function - can to some extent be emulated through creating specific derivates or by adding covariate priors (add_priors()) or model predictors (add_predictors_model()) to a SDM. Further methods will be added to the package as they become available or can be readily incorporated into the modelling framework. Some types of integration can also be directly modelled through integration. More details are provided in other vignettes such as the data preparation for creating derivates or the vignette on data integration. Users of the package are also directed to the various add_constraint() functions, many of which enable corrections of projected scenarios.

Adding ecological processes to correlative SDMs

There are a range of wrappers implemented in ibis.iSDM that allow a convenient passing of outputs or parameters to other mechanistic modelling packages. These wrappers support the convenient addition of ecological processes such as dispersal to scenarios data integration. Or they enable ibis.iSDM outputs to directly become inputs to further simulations. In case key parameters are not available, package users are encouraged to check out the various options in the add_constraint() function.

Most of these mechanistic approaches require quite extensive model understanding and in many cases additional training. Furthermore a range of parameters are usually required so that outputs are meaningful. It is beyond the scope of this vignette to provide an introduction to the various models. Rather, it is only demonstrated how linkages between ibis.iSDM and other models can be made, and the reader is referred to the original publication underlying each approach (see help page for references).

Adding dispersal to scenarios with KISSMig

The KISSMig model provide a simple model to estimate and limit dispersal in species distribution models (Nobis & Normand, 2014). It does not include any ecological mechanism related to recruitment as such, but instead works as a simple stochastic migration estimator that allows the inclusion of time-lagged dispersal to local neighborhoods. In the ibis.iSDM package the KISSMig simulator can be added as a dispersal constraint (among others) to scenario objects.

Example:

library(ibis.iSDM)
library(terra)
#> terra 1.8.5
#> 
#> Attaching package: 'terra'
#> The following object is masked from 'package:ibis.iSDM':
#> 
#>     modal
library(ggplot2)
# Don't print out as many messages
options("ibis.setupmessages" = FALSE)

# Background and biodiversity data
background <- terra::rast(system.file('extdata/europegrid_50km.tif', package='ibis.iSDM'))
virtual_points <- sf::st_read(system.file('extdata/input_data.gpkg', package='ibis.iSDM'), 'points', quiet = TRUE)
# Add some pseudo-absence information for later
poa <- virtual_points |> add_pseudoabsence(field_occurrence = 'Observed',
                                           template = background)

# Note we are loading different predictors than in previous examples
# These are in netcdf4 format, a format specific for storing spatial-temporal data including metadata.
ll <- list.files(system.file("extdata/predictors_presfuture/", package = "ibis.iSDM", mustWork = TRUE), "*.nc",full.names = TRUE)

# From those list of predictors are first loading the current ones as raster data
# We are loading only data from the very first, contemporary time step for model fitting
pred_current <- terra::rast()
for(i in ll) suppressWarnings( pred_current <- c(pred_current, terra::rast(i, lyrs = 1) ) )
names(pred_current) <- tools::file_path_sans_ext( basename(ll) )

# Get future predictors
# These we will load in some time steps using the stars package and ignoring the first time step
suppressWarnings( pred_future <- stars::read_stars(ll) |> stars:::slice.stars('Time', seq(2,86,by=10)) )
sf::st_crs(pred_future) <- sf::st_crs(4326) # Set projection
# Rename future predictors to those of current
names(pred_future) <- names(pred_current)

# ------ #
# Fit a model
fit <- distribution(background) |> 
  add_biodiversity_poipa(poa, field_occurrence = 'Observed', name = 'Virtual points') |> 
  # Note that we scale the predictors here
  add_predictors(pred_current, transform = 'scale',derivates = 'none') |> 
  engine_glmnet(alpha = 0) |> 
  # Train the model
  train(verbose = FALSE) |> 
  # Add simple percentile thresholds
  threshold(method = 'percentile', value = .33)

# Show the threshold
fit$plot_threshold()

Now lets add KISSMig as dispersal constraints. The constrain directly used the fitted suitability estimates from each projected timestep and also makes use of the created thresholded layer. Per time-step dispersal events are stochastically simulated and constraint range expansions in the next modelling steps. See ?kissmig::kissmig and the help-page for further help and explanations of parameters.


# Create a scenario object
sc <- scenario(fit) |> 
  # Apply the same variable transformations as above. 
  add_predictors(pred_future, transform = 'scale') |> 
  # Calculate thresholds at each time step. The threshold estimate is taken from 
  # the fitted model object.
  threshold() 
#> ! State variable of transformation not found?
  
# Add KISSMig constraint
sc1 <- sc |> 
  add_constraint_dispersal(method = "kissmig",
                           type = "DIS", # Final distribution result
                           value = 10, # Number of iteration steps
                           # These parameters are for KISSMig and get passed on
                           # Probablitiy of local extinction between iterations
                           pext = 0.5,
                           # Probability corner cells are colonized.
                           pcor = 0.2
                           )

sc2 <- sc |> 
  add_constraint_dispersal(method = "kissmig",
                           type = "DIS", # Final distribution result
                           value = 10, # Number of iteration steps
                           # These parameters are for KISSMig and get passed on
                           # Probablitiy of local extinction between iterations
                           pext = 0.9,
                           # Probability corner cells are colonized.
                           pcor = 0.1
                           )

# Project two scenarios with varying local extinction probability
df1 <- project(sc1, verbose = FALSE) |> summary()
#> Linking to GEOS 3.12.1, GDAL 3.8.4, PROJ 9.4.0; sf_use_s2() is FALSE
df2 <- project(sc2, verbose = FALSE) |> summary()
df <- dplyr::bind_rows(df1 |> dplyr::mutate(scenario = "low"),
                       df2 |> dplyr::mutate(scenario = "high") )
# ------ #
ggplot(df, aes(x = band, y= area_km2/1e6,
               group = scenario, color = scenario)) +
  theme_bw(base_size = 16) +
  geom_line() +
  labs(x = "Time", y = "Occupied area (Million km2)")

Adding dispersal to scenarios with MIGCLIM

Another dispersal simulator is MIGCLIM (Engler et al. 2014), a stochastic simulator which innovatively allows to differentiate between short and long-distance dispersal events as well as varying propagule pressure. Unfortunately is currently not available on CRAN anymore (stand September 2023), possibly because of lack of maintenance or missing dependency. The package can still be downloaded from github however (https://github.com/robinengler/MigClim/).

Assuming the user is able to install the MigClim package with all it’s dependencies (some of which have also disappared from CRAN), it can be run in ibis.iSDM as follows:

prj <- scenario(fit) |> 
  # Apply the same variable transformations as above. 
  add_predictors(pred_future, transform = 'scale') |> 
  # Calculate thresholds at each time step. The threshold estimate is taken from 
  # the fitted model object.
  threshold() |> 
# Check the help files for the function for an explanation of the parameters.
  add_constraint_MigClim(rcThresholdMode = 'continuous',
                         dispSteps = 1,
                         dispKernel = c(1.0, 0.4, 0.16, 0.06, 0.03),
                         barrierType = "strong",
                         lddFreq = 0, lddRange = c(1000, 10000),
                         iniMatAge = 1, propaguleProdProb = c(0.2, 0.6,0.8, 0.95),
                         replicateNb = 10) |> 
  # Project the model
  project()

# MIGCLIM outputs are provided a single updated layer and can be plotted through
# a customized plotting function.
prj$plot_migclim()

This example will be updated as an update for current R versions (>3.0) becomes available.

Simulating spatial-explicit population abundance with steps

The steps package implements a spatial-temporal explicit metapopulation simulator (Visintin et al. 2021) that is able to account for varying vital rates, dispersal with barriers and density dependence. steps is a simulator, thus makes use of a range of parameters and critical correlative assumptions to estimate for example abundance of a given time step.

In the ibis.iSDM package the linkage to steps can be established directly in the scenario projections by simply adding it as a separate module. When added, steps is used to make spatial-temporal abundance estimates that are aligned with each projection time step, eventual specified barriers and the provided parameters with regards to vital rates and density-dependence.

Note: The wrapper functionality implemented in the ibis.iSDM package is based on the assumption that higher habitat suitability (as estimated by a correlative SDM) is linearly correlated to higher population abundance. It should be noted that such assumptions have been questioned before and should be interpreted with caution (Lee-Yaw et al. 2021).

Users should always clearly understand the rationale behind parameter choices!

if("steps" %in% installed.packages()[,1]){
  require("steps")
  
  # Define some arbitrary vital rates for the transition for this purpose
  # Define vital rates
  vt <- matrix(c(0.0,0.52,0.75,
                0.52,0.28,0.0,
                0.0,0.52,0.75),
                nrow = 3, ncol = 3, byrow = TRUE)
  colnames(vt) <- rownames(vt) <- c('juvenile','subadult','adult')
  
  # We again specify a scenario as before using the fitted model
  prj <- scenario(fit) |> 
    # Apply the same variable transformations as above. 
    add_predictors(pred_future, transform = 'scale') |> 
    # Calculate thresholds at each time step. The threshold estimate is taken from 
    # the fitted model object.
    threshold() |> 
    # We then specify that we we 
    simulate_population_steps(vital_rates = vt)
  
  # Notice how we have added steps as additional simulation outcome
  prj
  # Now project
  scenario1 <- project(prj)
  plot(scenario1, "population")
  
  # Also see a different one where we add a dispersal constraint and density dependence
  dispersal <- steps::fast_dispersal(dispersal_kernel = steps::exponential_dispersal_kernel(distance_decay = 1))
  scenario2 <- project(prj |>
                         simulate_population_steps(vt,
                                                   dispersal = dispersal,
                                                   density_dependence = steps::ceiling_density(3) ) )
  
  # We can see that the dispersal constraint and higher density dependence cleary
  # results in a population abundance that tends to be concentrated in central Europe.
  plot(scenario2, "population")

}
#> Loading required package: steps
#> ! State variable of transformation not found?