8 LandMine Module
This documentation is work in progress. Please report any discrepancies or omissions at https://github.com/PredictiveEcology/LandMine/issues.
8.1 Module Overview
8.1.1 Module summary
Landmine is a model created for simulating the natural range of variation for landscapes in the boreal forest (Andison 1996; Andison 1998). It has been widely used by the public and the private sector for various purposes. This SpaDES module is a rewrite of the fire component in native R.
8.1.2 Model Differences
The current version has not yet been fully tested and compared with the original version, but there are currently several known differences:
- Fire sizes are taken from a Truncated Pareto distribution, resulting in numerous very small fires, and few large fires;
- Parameters have been fitted to the landscapes that are under study in the LandWeb project.
8.1.3 Known Species
Landmine requires the following codes as inputs (the genus and species codes below), which converts and groups species as follows. Each of the species groups has its own Rate of Spread (ROS) for fire spreading:
Species | Group | Code |
---|---|---|
Jack pine | Pine (PINU) | Pinu_ban |
Lodgepole pine | Pine (PINU) | Pinu_con |
Unspecified pine species | Pine (PINU) | Pinu_sp |
Paper birch | Deciduous (DECI) | Betu_pap |
Balsam poplar | Deciduous (DECI) | Popu_bal |
Trembling aspen | Deciduous (DECI) | Popu_tre |
Larch/Tamarack | Deciduous (DECI) | Lari_lar |
Black spruce | Black spruce (PICE_MAR) | Pice_mar |
White spruce | White spruce (PICE_GLA) | Pice_gla |
Fir species | Fir (ABIE) | Abie_sp |
8.1.4 Module inputs and parameters
Table 8.2 shows the full list of module inputs.
objectName | objectClass | desc | sourceURL |
---|---|---|---|
cohortData | data.table | Columns: B, pixelGroup, speciesCode (as a factor of the names), age. indicating several features about the current vegetation of stand. | NA |
fireReturnInterval | Raster | A raster layer that is a factor raster, with at least 1 column called fireReturnInterval , representing the fire return interval in years. |
NA |
pixelGroupMap | RasterLayer | Pixels with identical values share identical stand features | NA |
rasterToMatch | RasterLayer | DESCRIPTION NEEDED | NA |
ROSTable | data.table | A data.table with 3 columns, ‘age’, ‘leading’, and ‘ros’. The values under the ‘age’ column can be ‘mature’, ‘immature’, ‘young’ and compound versions of these, e.g., ‘immature_young’ which can be used when 2 or more age classes share same ‘ros’. ‘leading’ should be vegetation type. ‘ros’ gives the rate of spread values for each age and type. | NA |
rstFlammable | Raster | A raster layer, with 0, 1 and NA, where 1 indicates areas that are flammable, 0 not flammable (e.g., lakes) and NA not applicable (e.g., masked) | NA |
rstTimeSinceFire | Raster | a time since fire raster layer | NA |
species | data.table | Columns: species, speciesCode, Indicating several features about species | NA |
sppColorVect | character | named character vector of hex colour codes corresponding to each species | NA |
sppEquiv | data.table | Multi-columned data.table indicating species name equivalencies. Default taken from LandR::sppEquivalencies_CA which has names for species of trees in Canada |
NA |
studyArea | SpatialPolygonsDataFrame | multipolygon, typically buffered around an area of interest (i.e., studyAreaReporting ) to use for simulation. Defaults to an area in Southwestern Alberta, Canada. |
NA |
studyAreaReporting | SpatialPolygonsDataFrame | multipolygon (typically smaller/unbuffered than studyArea ) to use for plotting/reporting. Defaults to an area in Southwestern Alberta, Canada. |
NA |
Summary of user-visible parameters (Table 8.3).
paramName | paramClass | default | min | max | paramDesc |
---|---|---|---|---|---|
biggestPossibleFireSizeHa | numeric | 1e+06 | 10000 | 2e+06 | An upper limit, in hectares, of the truncated Pareto distribution of fire sizes |
burnInitialTime | numeric | 1 | NA | NA | This describes the simulation time at which the first burn event should occur |
fireTimestep | numeric | 1 | NA | NA | This describes the simulation time interval between burn events |
flushCachedRandomFRI | logical | FALSE | NA | NA | If no Fire Return Interval map is supplied, then a random one will be created and cached. Use this to make a new one. |
maxReburns | integer | 10 | 0 | 20 | Number of attempts to reburn fires that don’t reach their target fire size. Unlike maxRetriesPerID , which tries spreading an ongoing fire to nearby cells, this reignites the fire from a new pixel, so it’s less likely to continue being stuck in a region with sinuous fires. |
maxRetriesPerID | integer | 4 | 0 | 20 | Number of attempts (‘jumps’) that will be made per event ID, before abandoning. See ?SpaDES.tools::spread2 . NOTE: increasing this value results in drastically longer simulation times because firelets get ‘stuck’. |
minPropBurn | numeric | 0.9 | 0 | 1 | Minimum proportion burned pixels to use when triggering warnings about simulated fires. |
mixedType | numeric | 2 | 1 | 2 | How to define mixed stands: 1 for any species admixture; 2 for deciduous > conifer. See ?vegTypeMapGenerator. |
mode | character | single | NA | NA | use ‘single’ to run part of a landscape simulation; use ‘multi’ to run as part of postprocessing multiple simulation runs. |
reps | integer | NA | 1 | NA | number of replicates/runs per study area when running in ‘multi’ mode. |
ROSother | integer | 30 | NA | NA | default ROS value for non-forest vegetation classes.this is needed when passing a modified ROSTable , e.g. using log-transformed values. |
ROStype | character | default | NA | NA | One of ‘burny’, ‘equal’, ‘log’, or ‘default’. |
sppEquivCol | character | LandR | NA | NA | The column in sim$specieEquivalency data.table to use as a naming convention. |
useSeed | integer | NA | NA | Only used for creating a starting cohortData dataset. If NULL , then it will be randomly generated; If non-NULL , will pass this value to set.seed and be deterministic and identical each time. WARNING: setting the seed to a specific value will cause all simulations to be identical! |
|
vegLeadingProportion | numeric | 0.8 | 0 | 1 | a number that define whether a species is leading for a given pixel |
.plotInitialTime | numeric | 1 | NA | NA | This describes the simulation time at which the first plot event should occur |
.plotInterval | numeric | 1 | NA | NA | This describes the simulation time interval between plot events |
.plots | character | png, screen | NA | NA | Passed to types in Plots (see ?Plots ). There are a few plots that are made within this module, if set. Note that plots (or their data) saving will ONLY occur at end(sim) . If NA , plotting is turned off completely (this includes plot saving). |
.saveInitialTime | numeric | NA | NA | NA | This describes the simulation time at which the first save event should occur |
.saveInterval | numeric | NA | NA | NA | This describes the simulation time interval between save events |
.studyAreaName | character | NA | NA | NA | Human-readable name for the study area used - e.g., a hash of the studyarea obtained using reproducible::studyAreaName()
|
.useCache | logical | FALSE | NA | NA | Should this entire module be run with caching activated? This is generally intended for data-type modules, where stochasticity and time are not relevant |
.unitTest | logical | TRUE | NA | NA | Some functions can have internal testing. This will turn those on or off, if any exist. |
.useParallel | numeric | 2 | NA | NA | Used in burning. Will be passed to data.table::setDTthreads() . NOTE: use .useParallel <= 2 as the additonal RAM overhead too high given marginal speedup. |
8.1.5 Module outputs
Description of the module outputs (Table 8.4).
objectName | objectClass | desc |
---|---|---|
fireInitialTime | numeric | The initial event time of the burn event. This is simply a reassignment from P(sim)$burnInitialTime . |
fireSizes | list | A list of data.tables, one per burn event, each with two columns, size and maxSize . These indicate the actual sizes and expected sizes burned, respectively. These can be put into a single data.table with rbindlist(sim$fireSizes, idcol = 'year')
|
fireReturnInterval | RasterLayer | A Raster map showing the fire return interval. This is created from the rstCurrentBurn . |
fireReturnIntervalsByPolygonNumeric | numeric | A vector of the fire return intervals, ordered by the numeric representation of polygon ID |
fireTimestep | numeric | The number of time units between successive fire events in a fire module. |
friSummary | data.table | summary fire return interval table |
kBest | numeric | A numeric scalar that is the optimal value of K in the Truncated Pareto distribution (rtruncpareto ) |
numFiresPerYear | numeric | The average number of fires per year, by fire return interval level on rstCurrentBurn . |
rstCurrentBurn | RasterLayer | A raster layer, produced at each timestep, where each pixel is either 1 or 0 indicating burned or not burned. |
rstCurrentBurnCumulative | RasterLayer | Cumulative number of times a pixel has burned |
sppEquiv | data.table | Same as input, but with new column, LandMine . |
8.2 Usage
To run this Landmine module alone (i.e., for fitting), the following should work (iff raster inputs for rstStudyRegion
and rstFlammable
are available), assuming all R packages are available.
NB: Paths will have be changed for a different user.
8.2.1 Package dependencies
To determine which packages are used by LandMine
, use:
SpaDES.core::packages(modules = "LandMine", paths = "..")[[1]]
## [1] "SpaDES.core"
## [2] "assertthat"
## [3] "data.table"
## [4] "ggplot2"
## [5] "grDevices"
## [6] "gridExtra"
## [7] "magrittr"
## [8] "PredictiveEcology/LandR@development (>= 1.1.0.9003)"
## [9] "PredictiveEcology/pemisc@development"
## [10] "PredictiveEcology/SpaDES.tools@development"
## [11] "raster"
## [12] "RColorBrewer"
## [13] "VGAM"
8.2.2 Module usage
studyArea <- SpaDES.tools::randomStudyArea(seed = 1234, size = 1e+10)
rasterToMatch <- raster(studyArea, res = 250)
<- list(start = 0, end = 13)
times
<- list(# LandMine = list(flushCachedRandomFRI = TRUE) ) parameters
parameters <- list(# LandMine = list(flushCachedRandomFRI = TRUE) ) <-
parameters <- list(# LandMine = list(flushCachedRandomFRI = TRUE) ) list(#
parameters <- list(# LandMine = list(flushCachedRandomFRI = TRUE) ) LandMine
parameters <- list(# LandMine = list(flushCachedRandomFRI = TRUE) ) =
parameters <- list(# LandMine = list(flushCachedRandomFRI = TRUE) ) list(flushCachedRandomFRI
parameters <- list(# LandMine = list(flushCachedRandomFRI = TRUE) ) =
parameters <- list(# LandMine = list(flushCachedRandomFRI = TRUE) ) TRUE)
parameters <- list(# LandMine = list(flushCachedRandomFRI = TRUE) ) )
parameters
<- list("LandMine")
modules
<- list(studyArea = studyArea, rasterToMatch = rasterToMatch)
objects
<- list(cachePath = cacheDir, modulePath = moduleDir, inputPath = inputDir,
paths outputPath = outputDir)
mySim <- simInit(times = times, params = parameters, modules = modules,
objects = objects, paths = paths)
dev()
mySimOut <- spades(mySim, .plotInitialTime = times$start, debug = TRUE)
8.3 Testing the burn algorithm
Require(c("data.table", "DEoptim", "parallel", "SDMTools"))
s <- simInit(times = times, params = parameters, modules = modules,
objects = objects, paths = paths)
ros <- raster(extent(0, 250000, 0, 250000), res = 250, vals = 0)
ros <- ros == 0
fireSize <- 1e+05
source(file.path(moduleDir, "LandMine", "R", "burn.R"))
burnFun <- function(ros, centreCell, fireSize, spawnNewActive,
sizeCutoffs, burn1, spreadProb) {
burned <- burn1(landscape = ros, startCells = centreCell,
fireSizes = fireSize, spreadProb = spreadProb, spreadProbRel = ros,
spawnNewActive = spawnNewActive, sizeCutoffs = sizeCutoffs)
burnedMap <- raster(ros)
burnedMap[] <- NA
burnedMap[burned$pixels] <- burned$initialPixels
LM <- SDMTools::PatchStat(burnedMap, cellsize = res(burnedMap)[1])
list(burnedMap = burnedMap, LM = LM)
}
makeParallel <- function(wantParallel, numClus, cl, funs, addlPkgs) {
if (wantParallel) {
library(parallel)
if (is.null(cl)) {
cl <- parallel::makeCluster(numClus)
}
funs <- c(funs)
parallel::clusterExport(cl, funs)
# env <- environment()
parallel::clusterExport(cl, "addlPkgs", envir = parent.frame())
parallel::clusterEvalQ(cl, {
lapply(addlPkgs, library, character.only = TRUE)
})
return(cl)
}
}
wrap <- function(cl = NULL, reps, par, burn1, burnFun, objs,
addlPkgs) {
funNames <- c(deparse(substitute(burn1)), deparse(substitute(burnFun)))
cl <- makeParallel(TRUE, numClus = if (length(cl))
length(cl) else detectCores() - 1, cl, c(funNames, objs), addlPkgs)
objs <- append(mget(objs, envir = parent.frame()), list(burn1 = burn1))
parallel::clusterExport(cl, "objs", envir = parent.frame())
burnMapList <- parallel::clusterApplyLB(cl, reps, function(r) {
do.call("burnFun", objs)
})
return(list(cl = cl, out = burnMapList))
}
fitSN <- function(sna, ros, centreCell, fireSizes = 10^(2:5),
desiredPerimeterArea = 0.004, burnFun, burn1) {
sizeCutoffs <- 10^sna[5:6]
spreadProb <- sna[7]
sna <- c(10^(sna[1]), 10^(sna[2]), 10^(sna[3]), 10^(sna[4]))
bfs1 <- lapply(fireSizes, function(fireSize) {
burnFun(ros, centreCell, fireSize, sna, sizeCutoffs,
burn1 = burn1, spreadProb = spreadProb)
})
res <- lapply(seq(bfs1), function(bfCount) {
abs(log(bfs1[[bfCount]]$LM[1, "perim.area.ratio"]) -
log(desiredPerimeterArea)) + 100 * (sum(bfs1[[bfCount]]$burnedMap[],
na.rm = TRUE) < fireSizes[bfCount])
# it needs to get to above 90,000 HA for it to
# count
})
a <- sum(unlist(res)) # * log10(fireSizes)) # weigh larger ones more
attr(a, "bfs1") <- bfs1
a
}
fitSN2 <- function(par, ros, centreCell, fireSizes = 10^(2:5),
desiredPerimeterArea = 0.003, spreadProb = 0.9) {
sizeCutoffs <- 10^c(par[4], par[5])
bfs1 <- lapply(fireSizes, function(fireSize) {
sna <- min(-0.15, par[1] + par[2] * log10(fireSize))
sna <- 10^c(sna * par[3], sna * 2 * par[3], sna * 3 *
par[3], sna * 4 * par[3])
# sna <- -1
burnFun(ros, centreCell, fireSize, sna, sizeCutoffs,
burn1, spreadProb)
})
res <- lapply(seq(bfs1), function(bfCount) {
abs(log(bfs1[[bfCount]]$LM[1, "perim.area.ratio"]) -
log(desiredPerimeterArea)) + 100 * (sum(bfs1[[bfCount]]$burnedMap[],
na.rm = TRUE) < fireSizes[bfCount])
# it needs to get to above 90,000 HA for it to
# count
})
a <- sum(unlist(res))
attr(a, "bfs1") <- bfs1
a
}
8.4 Optimizing parameters
The following code chunk tries to find values of spawnNewActive
that creates “reasonable” fire shapes at all sizes.
wantParallel <- TRUE
maxRetriesPerID <- 4 ## 4 retries (5 attempts total)
spreadProb <- 0.9
spawnNewActive <- c(0.46, 0.2, 0.26, 0.11)
sizeCutoffs <- c(8000, 20000)
# spawnNewActive <- c(0.1, 0.04, 0.025, 0.01) sizeCutoffs
# <- c(8e3, 2e4)
NineCorners <- cellFromRowCol(ros, row = nrow(ros)/4 * rep(1:3,
3), col = ncol(ros)/4 * rep(1:3, each = 3))
centreCell <- cellFromRowCol(ros, row = nrow(ros)/2, col = ncol(ros)/2)
## Set variables
objs <- c("ros", "centreCell", "fireSize", "spawnNewActive",
"sizeCutoffs", "spreadProb")
funs <- c("burnFun", "burn1")
addlPkgs <- c("data.table", "raster", "SDMTools", "SpaDES.tools")
### SET UP CLUSTER FOR PARALLEL
numCores <- min(detectCores()/2, 120L)
if (Sys.info()["sysname"] == "Windows") {
cl <- parallel::makeCluster(numCores)
} else if (grepl("W-VIC-A1053|pinus[.]for-cast[.]ca", Sys.info()["nodename"])) {
Require("future")
## TODO: customize this to make a cluster on several
## machines via ssh
clNames <- c(rep("localhost", 0), rep("picea.for-cast.ca",
25), rep("pseudotsuga.for-cast.ca", 80))
## check the cluster: 1. at least 10 populations per
## parameter; 7 params; 2. ensure it's a multiple of
## the number of params (7); 3. R can't create more
## than ~125 socket connections.
stopifnot(length(clNames) >= 70, length(clNames)%%7 == 0,
length(clNames) <= 120)
cl <- makeClusterPSOCK(clNames, homogeneous = FALSE, verbose = TRUE)
clusterSetRNGStream(cl, sample(1e+08, 1))
} else {
cl <- parallel::makeCluster(numCores, type = "FORK")
clusterSetRNGStream(cl, sample(1e+08, 1))
}
if (!inherits(cl[[1]], "forknode")) {
parallel::clusterExport(cl, funs)
parallel::clusterExport(cl, objs)
# env <- environment()
parallel::clusterExport(cl, "addlPkgs", envir = parent.frame())
parallel::clusterEvalQ(cl, {
lapply(addlPkgs, library, character.only = TRUE)
})
}
opt_sn <- DEoptim(fitSN, lower = c(-2, -3, -3, -3, 1, 3.5, 0.75),
upper = c(-0.1, -0.5, -0.5, -1, 3.5, 5, 1), control = DEoptim.control(VTR = 0.001,
NP = as.integer(length(clNames)), itermax = 200, cluster = cl,
strategy = 6), ros = ros, centreCell = centreCell, fireSizes = c(10,
100, 1000, 10000, 1e+05), desiredPerimeterArea = 0.003,
burnFun = burnFun, burn1 = burn1)
opt_sn$optim$bestmem ## best param values
# dput(unname(opt_sn$optim$bestmem))
## assign with suffix to facilitate multiple DEoptim runs
timestamp <- format(Sys.time(), "%Y%M%d%H%M%S")
assign(paste0("opt_sn_", timestamp), opt_sn)
saveRDS(opt_sn, file.path(moduleDir, "LandMine", "data", paste0(timestamp,
"_DEoptim_250m.rds")))
fs_sn <- c(10, 100, 1000, 10000, 1e+05)
fit_sn <- fitSN(sna = c(-1, -1, -1, -2, 2, 4, 0.9), ros = ros,
centreCell = centreCell, fireSizes = fs_sn, desiredPerimeterArea = 0.003,
burnFun = burnFun, burn1 = burn1)
bfs1_sn <- purrr::transpose(attr(fit_sn, "bfs1"))
LM_sn <- do.call(rbind, bfs1_sn$LM)
plot(fs_sn, LM_sn[, "perim.area.ratio"]) ## NOTE: visual inspection - not too round; not too sinuous
A second (alternative) version tries the optimization using fewer parameters, to test whether a simpler version gets better/different results. Although this version was not used for the final module, we preserve it here for posterity.
fs_optim2 <- c(0.2, 1:8) * 10000
opt_sn2 <- DEoptim(fitSN2, lower = c(1, -1, 1, 3, 4), upper = c(3,
-0.3, 3, 4, 5), control = DEoptim.control(VTR = 0.001, itermax = 40,
cluster = cl, strategy = 6), ros = ros, centreCell = centreCell,
fireSizes = fs_optim2, desiredPerimeterArea = 0.003)
fs_sn2 <- round(runif(10, 10, 4000))
fit_sn2 <- fitSN2(par = c(2, -0.63333, 1, 3.2, 4.4), ros = ros,
centreCell = centreCell, fireSizes = fs_sn2, desiredPerimeterArea = 0.003,
spreadProb = 0.9)
bfs1_sn2 <- purrr::transpose(attr(fit_sn2, "bfs1"))
LM_sn2 <- do.call(rbind, bfs1_sn2$LM)
plot(fs_sn2, LM_sn2[, "perim.area.ratio"]) ## NOTE: visual inspection - not too round; not too sinuous
8.5 Manual inspection of optimization results
8.5.1 Original (2018) version
The original version was run using 100m pixels, despite the simulations being run using 250m pixels. This was corrected and rerun below.
## 10,000 hectares burns gave this
spawnNewActive[2:3] <- c(0.0235999945606232, 0.0263073265505955)
# 100,000 hectare burns gave this spawnNewActive <-
# 10^c(-1.264780, -1.970946, -1.288213, -2.202580)
spawnNewActive <- 10^c(-0.586503645288758, -1.08108837273903,
-2.14391896536108, -1.00221184641123)
sizeCutoffs <- 10^c(3.37711253212765, 4.52040993282571)
sns <- c(-1.733262, -0.933318, -2.562183, -2.493687, 3.064458,
4.812305)
spawnNewActive <- 10^sns[1:4]
sizeCutoffs <- 10^sns[5:6]
# spawnNewActive <- 10^c(-1.646419, -1.815395, -2.809013,
# -2.613337) sizeCutoffs <- 10^c(3.888317, 4.641961)
## 100,000 pixel fires -- the next worked, but I think we
## can get better sns <- structure(
## c(-1.652459,-0.962121,-0.964879,-2.304902, 3.522345,
## 4.173242), .Names = c('par1',
sns <- structure(c(-1.641197, -1.152821, -0.697335, -1.751917,
3.720378, 4.034059), .Names = c("par1", "par2", "par3", "par4",
"par5", "par6"))
spawnNewActive <- 10^sns[1:4]
sizeCutoffs <- 10^(sns[5:6])
fireSize <- 30000
## 100
sns <- c(-0.77716149196811, -0.769325340166688, -1.2772046867758,
-1.99332102853805, 3.14260408212431, 4.46155184064992)
## 1000
sns <- c(-0.775107, -1.03176, -0.599669, -1.958105, 3.048958,
4.275831)
## seemed good for 100,000, pretty good for 1e3
sns <- c(-1.54885, -0.97052, -1.38305, -1.93759, 3.20379, 4.13237)
## good for 100 000, 10 000 ha -- too sinuous for 1000 and
## 100 ha
sns <- c(-1.537203, -1.462981, -0.524957, -1.002567, 3.642046,
4.501754)
## good for 100 000, 10 000 ha (except some fires @ 1e5
## don't make it to full size) -- too sinuous for smaller
sns <- c(-1.484338, -1.22044, -2.948275, -2.15594, 3.945281,
4.904893)
sns <- c(-1.495247, -0.800494, -1.58235, -2.270646, 3.530671,
4.663245)
## final optimization after 75 iterations, Good: 1e5, 1e4
sns <- c(-1.47809, -0.86224, -1.34532, -1.93568, 3.27149, 4.20741)
## based on equal weights 10^(1:5)
sns <- c(-0.923528, -1.804549, -1.760455, -1.793594, 1.683355,
4.466668)
## With spreadProb = 0.9 # Pretty GOOD!
sns <- c(-0.73152, -0.501823, -0.605968, -1.809726, 2.202732,
4.69606, 0.9) ## used in module
## With spreadProb = 0.9 # Optimal
sns <- c(-0.978947, -0.540946, -0.790736, -1.583039, 2.532013,
4.267547, 0.94673)
spawnNewActive <- 10^sns[1:4]
sizeCutoffs <- 10^(sns[5:6])
if (length(sns) == 7) spreadProb <- sns[7]
# from linear model version
par <- c(1.548899, -0.396904, 2.191424, 3.903082, 4.854002)
sizeCutoffs <- 10^c(par[4], par[5])
sna <- min(-0.15, par[1] + par[2] * log10(fireSize))
sna <- 10^c(sna * par[3], sna * 2 * par[3], sna * 3 * par[3],
sna * 4 * par[3])
spawnNewActive <- sna
###########################
clearPlot()
dev()
for (i in 1:5) {
fireSize <- 10^i
dim <- round(sqrt(fireSize) * 5 * 250)
ros <- raster(extent(0, dim, 0, dim), res = 250, vals = 1)
centreCell <- cellFromRowCol(ros, rownr = nrow(ros)/2, colnr = ncol(ros)/2)
reps <- paste0("rep", 1:4 + (log10(fireSize) - 1) * 4)
burnedMapList <- wrap(cl = cl, reps = reps, par = TRUE, burn1 = burn1,
burnFun = burnFun, objs = objs, libs = addlPkgs)
names(burnedMapList$out) <- reps
burnedMapList <- purrr::transpose(burnedMapList$out)
do.call(rbind, burnedMapList$LM)
Plot(burnedMapList$burnedMap, cols = c("red"), new = FALSE,
na.color = "white", legend = FALSE, visualSqueeze = 0.7,
title = paste0("Fire size: ", fireSize))
}
reps <- paste0("rep", 1:1)
perims <- list()
perm <- list()
mod <- list()
dev()
clearPlot()
fireSizes <- 10^(4)
for (fs in fireSizes) {
for (i in 1:1) {
ros <- raster(extent(0, 2e+05, 0, 2e+05), res = 250,
vals = 1)
NineCorners <- cellFromRowCol(ros, rownr = nrow(ros)/4 *
rep(1:3, 3), colnr = ncol(ros)/4 * rep(1:3, each = 3))
centreCell <- NineCorners
ran <- runif(4, -3, -1)
spawnNewActive <- 10^ran
# spawnNewActive <- 10^c(-0.1, -0.75, -1.2,
# ran*2.5)
fireSize = rep(fs, length(centreCell))
sizeCutoffs <- 10^c(1, 3)
burnedMapList <- wrap(cl = cl, reps = reps, par = TRUE,
burn1 = burn1, burnFun = burnFun, objs = objs, libs = addlPkgs)
names(burnedMapList$out) <- reps
burnedMapList <- purrr::transpose(burnedMapList$out)
do.call(rbind, burnedMapList$LM)
Plot(burnedMapList$burnedMap, new = TRUE, zero.color = "white")
perims[[i]] <- data.frame(perim = burnedMapList$LM$rep1$perim.area.ratio,
spawnNewActive = mean(spawnNewActive), others = t(spawnNewActive))
}
fsChar <- as.character(fs)
perm[[fsChar]] <- rbindlist(perims)
perm[[fsChar]]$perim <- log10(perm[[fsChar]]$perim)
perm[[fsChar]]$spawnNewActive <- perm[[fsChar]]$spawnNewActive
Plot(perm[[fsChar]]$perim, perm[[fsChar]]$spawnNewActive,
new = TRUE, addTo = paste0("fs", fsChar))
mod[[fsChar]] <- lm(spawnNewActive ~ perim, data = perm[[fsChar]])
}
(predict(mod[["10"]], data.frame(perim = log10(0.003))))
(predict(mod[["100"]], data.frame(perim = log10(0.003))))
log10(predict(mod[["1000"]], data.frame(perim = log10(0.003))))
8.5.2 Current (2022) version
The original version was run using 100m pixels, despite the simulations being run using 250m pixels. This version uses 250m pixels.
## final optimization after 170 iterations (fitSN)
sns <- unname(opt_sn$optim$bestmem)
spawnNewActive <- 10^sns[1:4]
sizeCutoffs <- 10^(sns[5:6])
spreadProb <- sns[7]
## from linear model version
par <- c(1.548899, -0.396904, 2.191424, 3.903082, 4.854002)
sizeCutoffs <- 10^c(par[4], par[5])
sna <- min(-0.15, par[1] + par[2] * log10(fireSize))
sna <- 10^c(sna * par[3], sna * 2 * par[3], sna * 3 * par[3],
sna * 4 * par[3])
spawnNewActive <- sna
clearPlot()
for (i in 1:5) {
fireSize <- 10^i
dim <- round(sqrt(fireSize) * 5 * 250)
ros <- raster(extent(0, dim, 0, dim), res = 250, vals = 1)
centreCell <- cellFromRowCol(ros, row = nrow(ros)/2, col = ncol(ros)/2)
reps <- paste0("rep", 1:4 + (log10(fireSize) - 1) * 4)
burnedMapList <- wrap(cl = cl, reps = reps, par = TRUE, burn1 = burn1,
burnFun = burnFun, objs = objs, addlPkgs = addlPkgs)
names(burnedMapList$out) <- reps
burnedMapList <- purrr::transpose(burnedMapList$out)
do.call(rbind, burnedMapList$LM)
Plot(burnedMapList$burnedMap, cols = c("red"), new = FALSE,
na.color = "white", legend = FALSE, visualSqueeze = 0.7,
title = paste0("Fire size: ", fireSize))
}
8.5.3 Cleaning up
parallel::stopCluster(cl)
unlink(cacheDir, recursive = TRUE)
8.5.4 Code and data availability
Code available from https://github.com/PredictiveEcology/LandMine.
8.5.5 Links to other modules
Originally developed as part of the LandWeb project. ## References