Designing Landscapes in gen3sis

09.07.2020

To create your own landscape, you can start from anywere. For this exercise we will create from scratch a simple island landscape that we used in the main text of the package manuscript as case study 1.

Create your landscape grid

In this example we want to explores biodiversity dynamics and trait evolution on dynamic oceanic islands. It follows the general dynamic model of island biogeography (Whittaker et al. 2008 Journal of Biogeography), the island we create entails the number of sites and environmental dynamics.

First, we will define the spatial extent of our landscape, i.e. the number of sites. In our case, this will be 31 by 31 sites laid in a grid cells. Next, we define the temporal dimension i.e. our landscape evolves in 140 time steps (later we interpret 1 time-step = 1 Myr ).

Ncol <- 31
Nrow <- 31
Ntimesteps <- 140

The next step will be to create two arrays, one for each environmental variable (i.e. temperature and precipitation). These two initially empty arrays are 3 dimensional (2 spatial and 1 temporal dimension). Later we will save the environmental variable values for the landscape file.

# Create the temperature array and matrix:
temp_array <- array(NA, dim = c(Nrow, Ncol, Ntimesteps))
temp_dataframe <- matrix(NA, nrow = Nrow * Ncol, ncol = length(c(Ncol, Nrow)) + Ntimesteps)

# Create the precipitation array and matrix:
prec_array <- array(NA, dim = c(Nrow, Ncol, Ntimesteps))
prec_dataframe <- matrix(NA, nrow = Nrow * Ncol, ncol = length(c(Ncol, Nrow)) + Ntimesteps)

# Creating a vector for the names to be used to name input files (one per time
# step)
stringtimestepsnames <- vector(mode = "character", length = Ntimesteps)

Add environmental characteristics

Now that our template arrays and matrices are prepared, we can start to fill them with environmental values. We will use three nested loops to do so. The first loop iterates over the 140 time steps, while the two inner ones iterate over all spatial cells. To do so, we must create the enironmental values which will be saved in the corresponding landscape arrays and therefore generate a heterogenous and temporally dynamic landscape.

Our conceptual island has it’s number of sites and environemtnal values changing over time, as shown in animation A1. The island is initiated with four sites (at 140 Ma), but the island surface progressively increases to its maximum of 81 sites (at 90 Ma) and then shrinks back to four sites at the present time (due to erosion) at double the speed of orogeny. For each time step a temperature and precipitation layer are created following a normal distribution.

A1: Animation of the conceptual island that we will create as an input landscape for gen3sis.

A1: Animation of the conceptual island that we will create as an input landscape for gen3sis.

For both temperature and precipitation uniform distribution of values over space is created. We then add environmental white noise to introduce spatial and temporal heterogenity. For the temperature layer this will be an average of 20°C with a standard deviation of 0.5°C, where as for precipitation we settle for an average value of 500 mm/year with a standard deviation of 50 mm/year.

Note that we only have to assign these environmental values to the sites where we have land mass (see animation above). Within the loops we therefore check if, at the current time, we are indeed on a terrestrial site. If so, we assign a temperature and precipitation value.

Finally, we create one dataframe to store the temperature values for all time steps and a second dataframe for the precipitation values. These dataframes contain all possible x- and y-coordinates of our sample world as the first two columns. After the temperature and precipitation values have been created for one specific time step, we store these values within a new column for each time step in these new dataframes.

for (timestep in 1:Ntimesteps) {
    # temporal loop
    counting <- 1
    stringtimestepsnames[timestep] <- paste("X", timestep, "", sep = "")
    for (y in 1:Nrow) {
        # loop over the first spatial dimension loop over the second spatial dimension
        # time steps with only four (2x2) suitable sites suitable sites
        for (x in 1:Ncol) {
            if ((timestep <= 10) || (timestep > 120 && timestep <= 140)) {
                if ((y >= 15 && y <= 16) && (x >= 15 && x <= 16)) {
                  temp_array[x, y, timestep] <- rnorm(1, 20, 0.5)  #temperature
                  prec_array[x, y, timestep] <- rnorm(1, 500, 50)  #precipitation
                }
            }
            if ((timestep > 10 && timestep <= 20) || (timestep > 100 && timestep <= 
                120)) {
                # time steps with nine (3x3) suitable sites
                if ((y >= 15 && y <= 17) && (x >= 15 && x <= 17)) {
                  temp_array[x, y, timestep] <- rnorm(1, 20, 0.5)
                  prec_array[x, y, timestep] <- rnorm(1, 500, 50)
                }
            }
            if ((timestep > 20 && timestep <= 30) || (timestep > 80 && timestep <= 
                100)) {
                # time steps with 25 (5x5) suitable sites
                if ((y >= 14 && y <= 18) && (x >= 14 && x <= 18)) {
                  temp_array[x, y, timestep] <- rnorm(1, 20, 0.5)
                  prec_array[x, y, timestep] <- rnorm(1, 500, 50)
                }
            }
            if ((timestep > 30 && timestep <= 40) || (timestep > 60 && timestep <= 
                80)) {
                # time steps with 49 (7x7) suitable sites
                if ((y >= 13 && y <= 19) && (x >= 13 && x <= 19)) {
                  temp_array[x, y, timestep] <- rnorm(1, 20, 0.5)
                  prec_array[x, y, timestep] <- rnorm(1, 500, 50)
                }
            }
            if (timestep > 40 && timestep <= 60) {
                # time steps with 81 (9x9) suitable sites
                if ((y >= 12 && y <= 20) && (x >= 12 && x <= 20)) {
                  temp_array[x, y, timestep] <- rnorm(1, 20, 0.5)
                  prec_array[x, y, timestep] <- rnorm(1, 500, 50)
                }
            }
            # Saving the environmental variables in a dataframe format for distance matrices
            if (timestep == 1) {
                temp_dataframe[counting, 1] <- x
                temp_dataframe[counting, 2] <- y
                prec_dataframe[counting, 1] <- x
                prec_dataframe[counting, 2] <- y
            }
            temp_dataframe[counting, 2 + timestep] <- temp_dataframe[x, y, timestep]
            prec_dataframe[counting, 2 + timestep] <- prec_dataframe[x, y, timestep]
            counting <- counting + 1
        }
    }
}

The create_input_landscape() function of the gen3sis package takes temperature and precipitation values as a list raster layers or raster layer filenames. Based on the dataframes we just created, we can now create temperature and precipitation rasters for each time step. We then store them in a named list and save to the hard drive.

library(raster)
landscapes_list <- list()
for (timestep in 1:Ntimesteps) {
    temp_raster <- rasterFromXYZ(temp_dataframe[, c(1, 2, timestep + 2)])
    prec_raster <- rasterFromXYZ(prec_dataframe[, c(1, 2, timestep + 2)])
    
    landscapes_list$temp <- c(landscapes_list$temp, temp_raster)
    landscapes_list$prec <- c(landscapes_list$prec, prec_raster)
}

## saving the list of rasters into .rds format to be used as input
saveRDS(landscapes_list, "inputfolder/my_experiment/landscapes.rds")

Add a cost function

The connection cost between sites is defined as the distance between these sites modified by an optional cost function. This user defined cost function can give a penalty (or benefit) for crossing certain cells and will be stored as a distance matrix. For this virtual landscape, we will not add a cost function, meaning that the connection costs are only the distance between the sites inside our grid cells. If you want to learn how to make a cost function, refer to the create_input_landscape vignette.