Create a new simulation object, the sim object (a simList).
This object is implemented using an environment where all objects and functions are placed.
Since environments in R are pass by reference, "putting" objects in
the sim object does no actual copy.
The simList also stores all parameters, and other important simulation
information, such as times, paths, modules, and module load order.
See more details below.
simInit(
times,
params,
modules,
objects,
paths,
inputs,
outputs,
loadOrder,
notOlderThan = NULL,
...
)
# S4 method for class 'list,list,list,list,list,data.frame,data.frame,character'
simInit(
times,
params,
modules,
objects,
paths,
inputs,
outputs,
loadOrder,
notOlderThan = NULL,
...
)
# S4 method for class 'ANY,ANY,ANY,character,ANY,ANY,ANY,ANY'
simInit(
times,
params,
modules,
objects,
paths,
inputs,
outputs,
loadOrder,
notOlderThan = NULL,
...
)
# S4 method for class 'ANY,ANY,character,ANY,ANY,ANY,ANY,ANY'
simInit(
times,
params,
modules,
objects,
paths,
inputs,
outputs,
loadOrder,
notOlderThan = NULL,
...
)
# S4 method for class 'ANY,ANY,ANY,ANY,ANY,ANY,ANY,ANY'
simInit(
times,
params,
modules,
objects,
paths,
inputs,
outputs,
loadOrder,
notOlderThan = NULL,
...
)
simInit2(l)
simInitDefaults()A named list of numeric simulation start and end times
(e.g., times = list(start = 0.0, end = 10.0, timeunit = "year")),
with the final optional element, timeunit, overriding the default
time unit used in the simulation which is the "smallest time unit" across all
modules. See examples.
A list of lists of the form list(moduleName=list(param1=value, param2=value)).
See details.
A named list of character strings specifying the names of modules to be loaded
for the simulation.
Note: the module name should correspond to the R source file from which the module is loaded.
Example: a module named "caribou" will be sourced form the file caribou.R,
located at the specified modulePath(simList) (see below).
(optional) A vector of object names (naming objects
that are in the calling environment of
the simInit, which is often the
.GlobalEnv unless used programmatically.
NOTE: this mechanism will
fail if object name is in a package dependency), or
a named list of data objects to be
passed into the simList (more reliable).
These objects will be accessible
from the simList as a normal list, e.g,. mySim$obj.
An optional named list with up to 4 named elements,
modulePath, inputPath, outputPath, and cachePath.
See details. NOTE: Experimental feature now allows for multiple modulePaths
to be specified in a character vector. The modules will be searched for sequentially
in the first modulePath, then if it doesn't find it, in the second etc.
A data.frame. Can specify from 1 to 6
columns with following column names: objectName (character, required),
file (character), fun (character), package (character),
interval (numeric), loadTime (numeric).
See inputs() and vignette("ii-modules") section about inputs.
A data.frame. Can specify from 1 to 5
columns with following column names: objectName (character, required),
file (character), fun (character), package (character),
saveTime (numeric) and eventPriority (numeric). If
eventPriority is not set, it defaults to .last(). If eventPriority
is set to a low value, e.g., 0, 1, 2 and saveTime is start(sim),
it should give "initial conditions".
See outputs() and
vignette("ii-modules") section about outputs.
An optional character vector of module names specifying the order in which to load the modules. If not specified, the module load order will be determined automatically.
A time, as in from Sys.time(). This is passed into
the Cache function that wraps .inputObjects.
If the module uses the .useCache parameter and it is
set to TRUE or ".inputObjects",
then the .inputObjects will be cached.
Setting notOlderThan = Sys.time() will cause the
cached versions of .inputObjects to be refreshed,
i.e., rerun.
An alternative way to pass objects, i.e., they can just be named
arguments rather than in a objects = list(...). It can also be any
options that begins with spades, reproducible or Require, i.e.,
those identified in spadesOptions(),
reproducibleOptions() or RequireOptions().
These will be assigned to the equivalent option during the simInit and spades
calls only, i.e., they will revert after the simInit or spades calls
are complete. NOTE: these are not passed to the simList per se, i.e., they are
not be available in the simList during either
the simInit or spades calls via sim$xxx, though they will be returned to the simList
at the end of each of these calls (so that the next call to e.g., spades can
see them). For convenience, these can be supplied without their package prefix,
e.g., lowMemory can be specified instead of spades.lowMemory. In cases that
share option name (reproducible.verbose and Require.verbose both exist),
passing verbose = FALSE will set both. Obviously this may cause unexpected
problems if a module is also expecting a value.
A list of arguments to passed to simInit.
A simList simulation object, pre-initialized from values
specified in the arguments supplied.
simInit function does the following:| What | Details | Argument(s) to use |
fills simList slots | places the arguments times,
params, modules, paths into equivalently named
simList slots | times,
params, modules, paths |
| sources all module files | places all function definitions in the
simList, specifically, into a sub-environment of the main
simList environment: e.g., sim$<moduleName>$function1
(see section on Scoping) | modules |
| copies objects | from the global environment to the
simList environment | objects |
| loads objects | from disk into the simList | inputs |
| schedule object loading/copying | Objects can be loaded into the
simList at any time during a simulation | inputs |
| schedule object saving | Objects can be saved to disk at any arbitrary
time during the simulation. If specified here, this will be in addition
to any saving due code inside a module (i.e., a module may manually
run write.table(...) | outputs |
| schedules "init" events | from all modules (see events()) | automatic |
| assesses module dependencies | via the inputs and outputs identified in their
metadata. This gives the order of the .inputObjects and init
events. This can be overridden by loadOrder. | automatic |
| determines time unit | takes time units of modules and how they fit together | times or automatic |
runs .inputObjects functions | from every module in the module order as determined above | automatic |
params can only contain updates to any parameters that are defined in
the metadata of modules. Take the example of a module named, Fire, which
has a parameter named .plotInitialTime. In the metadata of that module,
it says TRUE. Here we can override that default with:
list(Fire=list(.plotInitialTime=NA)), effectively turning off plotting.
Since this is a list of lists, one can override the module defaults for multiple
parameters from multiple modules all at once, with say:
list(Fire = list(.plotInitialTime = NA, .plotInterval = 2), caribouModule = list(N = 1000)).
The params list can contain a list (named .globals) of named objects
e.g., .globals = list(climateURL = "https:\\something.com") entry. Any and every
module that has a parameter with that name (in this case climateURL) will be
overridden with this value as passed.
params can be used to set the seed for a specific event in a module. This
is done using the normal params argument, specifying .seed as a list
where the elements are a numeric for the seed and the name is the event. Since
parameters must be specific to a module, this creates a module and event specific
seed e.g., params = list(moduleName = list(.seed = list(init = 123))) will
set the init event of module named moduleName to 123. The RN stream
will be reset to its state prior to the set.seed call after the event.
We implement a discrete event simulation in a more modular fashion so it is easier to add modules to the simulation. We use S4 classes and methods, and fast lists to manage the event queue.
paths specifies the location of the module source files,
the data input files, and the saving output files. If no paths are specified
the defaults are as follows:
cachePath: getOption("reproducible.cachePath");
inputPath: getOption("spades.inputPath");
modulePath: getOption("spades.modulePath");
outputPath: getOption("spades.outputPath").
Since the objects in the simList are passed-by-reference, it is useful
to create a copy of the initialized simList object prior to running
the simulation (e.g., mySimOut <- spades(Copy(mySim))).
This ensures you retain access to the original objects, which would otherwise
be overwritten/modified during the simulation.
The user can opt to run a simpler simInit call without inputs, outputs, and times.
These can be added later with the accessor methods (See example).
These are not required for initializing the simulation via simInit.
All of modules, paths, params, and objects are needed
for successful initialization.
The simInit function will attempt to find usage of sim$xxx or sim[['xxx']]
on either side of the assignment (<-) operator.
It will compare these to the module metadata, specifically inputObjects for cases where
objects or "gotten" from the simList and outputObjects for cases where objects are
assigned to the simList.
It will also attempt to find potential, common function name conflicts with things like
scale and stack (both in base and raster), and
Plot (in quickPlot and some modules).
This code checking is young and may get false positives and false negatives,
i.e., miss things.
It also takes computational time, which may be undesirable in operational code.
To turn off checking (i.e., if there are too many false positives and negatives), set
options(spades.moduleCodeChecks = FALSE).
Using caching with SpaDES is vital when building re-usable and reproducible content.
Please see the vignette dedicated to this topic.
Matloff, N. (2011). The Art of R Programming (ch. 7.8.3). San Francisco, CA: No Starch Press, Inc.. Retrieved from https://nostarch.com/artofr.htm
# Tests take several seconds
if (requireNamespace("SpaDES.tools", quietly = TRUE) &&
requireNamespace("NLMR", quietly = TRUE)) {
opts <- options("spades.moduleCodeChecks" = FALSE, "spades.useRequire" = FALSE)
if (!interactive()) opts <- append(opts, options("spades.plots" = NA,
"spades.debug" = FALSE))
mySim <- simInit(
times = list(start = 0.0, end = 2.0, timeunit = "year"),
params = list(
.globals = list(stackName = "landscape", burnStats = "nPixelsBurned")
),
modules = list("randomLandscapes", "fireSpread", "caribouMovement"),
paths = list(modulePath = getSampleModules(tempdir()))
)
spades(mySim) # shows plotting
# Change more parameters, removing plotting
mySim <- simInit(
times = list(start = 0.0, end = 2.0, timeunit = "year"),
params = list(
.globals = list(stackName = "landscape", burnStats = "nPixelsBurned"),
fireSpread = list(.plotInitialTime = NA)
),
modules = list("randomLandscapes", "fireSpread", "caribouMovement"),
paths = list(modulePath = getSampleModules(tempdir()))
)
outSim <- spades(mySim)
# A little more complicated with inputs and outputs
mapPath <- system.file("maps", package = "quickPlot")
mySim <- simInit(
times = list(start = 0.0, end = 2.0, timeunit = "year"),
params = list(
.globals = list(stackName = "landscape", burnStats = "nPixelsBurned")
),
modules = list("randomLandscapes", "fireSpread", "caribouMovement"),
paths = list(modulePath = getSampleModules(tempdir()),
outputPath = tempdir()),
inputs = data.frame(
files = dir(file.path(mapPath), full.names = TRUE, pattern = "tif")[1:2],
functions = "rast",
package = "terra",
loadTime = 1,
stringsAsFactors = FALSE),
outputs = data.frame(
expand.grid(objectName = c("caribou","landscape"),
saveTime = 1:2,
stringsAsFactors = FALSE)))
# Use accessors for inputs, outputs
mySim2 <- simInit(
times = list(start = 0.0, end = 2.0, timeunit = "year"),
modules = list("randomLandscapes", "fireSpread", "caribouMovement"),
params = list(
.globals = list(stackName = "landscape", burnStats = "nPixelsBurned"),
randomLandscapes = list(nx = 10, ny = 10)
),
paths = list(
modulePath = getSampleModules(tempdir()),
outputPath = tempdir()
)
)
# add by accessor is equivalent
inputs(mySim2) <- data.frame(
files = dir(file.path(mapPath), full.names = TRUE, pattern = "tif")[1:2],
functions = "rast",
package = "terra",
loadTime = 1,
stringsAsFactors = FALSE)
outputs(mySim2) <- data.frame(
expand.grid(objectName = c("caribou", "landscape"),
saveTime = 1:2,
stringsAsFactors = FALSE))
all.equal(mySim, mySim2) # TRUE
# Use accessors for times -- does not work as desired because times are
# adjusted to the input timeunit during simInit
mySim2 <- simInit(
params = list(
.globals = list(stackName = "landscape", burnStats = "nPixelsBurned")
),
modules = list("randomLandscapes", "fireSpread", "caribouMovement"),
paths = list(modulePath = getSampleModules(tempdir()),
outputPath = tempdir()),
inputs = data.frame(
files = dir(file.path(mapPath), full.names = TRUE, pattern = "tif")[1:2],
functions = "rast",
package = "terra",
loadTime = 1,
stringsAsFactors = FALSE),
outputs = data.frame(
expand.grid(objectName = c("caribou","landscape"),
saveTime = 1:2,
eventPriority = c(0,10), # eventPriority 0 may give "initial" conditions
stringsAsFactors = FALSE))
)
# add times by accessor fails all.equal test because "year" was not
# declared during module loading, so month became the default
times(mySim2) <- list(current = 0, start = 0.0, end = 2.0, timeunit = "year")
all.equal(mySim, mySim2) # fails because time units are all different, so
# several parameters that have time units in
# "months" because they were loaded that way
params(mySim)$fireSpread$.plotInitialTime
params(mySim2)$fireSpread$.plotInitialTime
events(mySim) # load event is at time 1 year
events(mySim2) # load event is at time 1 month, reported in years because of
# update to times above
options(opts)
}
#> Setting:
#> options(
#> spades.modulePath = '/tmp/RtmpXxjf07/sampleModules'
#> )
#> Paths set to:
#> options(
#> rasterTmpDir = '/tmp/RtmpXxjf07/scratch/raster'
#> reproducible.cachePath = '/tmp/RtmpXxjf07/cache'
#> spades.inputPath = '/tmp/RtmpXxjf07/inputs'
#> spades.outputPath = '/tmp/RtmpXxjf07'
#> spades.modulePath = '/tmp/RtmpXxjf07/sampleModules'
#> spades.scratchPath = '/tmp/RtmpXxjf07/scratch'
#> )
#> terra::terraOptions(tempdir = '/tmp/RtmpXxjf07/scratch/terra'
#> Sep18 15:24:24 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Sep18 15:24:24 simInit The following .globals were used:
#> Sep18 15:24:24 simInit Key: <global, module>
#> Sep18 15:24:24 simInit module global
#> Sep18 15:24:24 simInit <char> <char>
#> Sep18 15:24:24 simInit 1: fireSpread burnStats
#> Sep18 15:24:24 simInit 2: caribouMovement stackName
#> Sep18 15:24:24 simInit 3: fireSpread stackName
#> Sep18 15:24:24 simInit 4: randomLandscapes stackName
#> Elapsed time for simInit: 0.1005538 secs
#> Sep18 15:24:24 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Sep18 15:24:25 frSprd:init fireSpread
#> Sep18 15:24:25 frSprd:stats fireSpread
#> Sep18 15:24:25 frSprd:stats fireSpread
#> simList saved in
#> SpaDES.core:::savedSimEnv()$.sim
#> It will be deleted at next spades() call.
#> Setting:
#> options(
#> spades.modulePath = '/tmp/RtmpXxjf07/sampleModules'
#> )
#> Paths set to:
#> options(
#> rasterTmpDir = '/tmp/RtmpXxjf07/scratch/raster'
#> reproducible.cachePath = '/tmp/RtmpXxjf07/cache'
#> spades.inputPath = '/tmp/RtmpXxjf07/inputs'
#> spades.outputPath = '/tmp/RtmpXxjf07'
#> spades.modulePath = '/tmp/RtmpXxjf07/sampleModules'
#> spades.scratchPath = '/tmp/RtmpXxjf07/scratch'
#> )
#> terra::terraOptions(tempdir = '/tmp/RtmpXxjf07/scratch/terra'
#> Sep18 15:24:25 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Sep18 15:24:25 simInit The following .globals were used:
#> Sep18 15:24:25 simInit Key: <global, module>
#> Sep18 15:24:25 simInit module global
#> Sep18 15:24:25 simInit <char> <char>
#> Sep18 15:24:25 simInit 1: fireSpread burnStats
#> Sep18 15:24:25 simInit 2: caribouMovement stackName
#> Sep18 15:24:25 simInit 3: fireSpread stackName
#> Sep18 15:24:25 simInit 4: randomLandscapes stackName
#> Elapsed time for simInit: 0.1021752 secs
#> Sep18 15:24:25 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Sep18 15:24:25 frSprd:init fireSpread
#> Sep18 15:24:25 frSprd:stats fireSpread
#> Sep18 15:24:25 frSprd:stats fireSpread
#> simList saved in
#> SpaDES.core:::savedSimEnv()$.sim
#> It will be deleted at next spades() call.
#> Setting:
#> options(
#> spades.outputPath = '/tmp/RtmpXxjf07'
#> spades.modulePath = '/tmp/RtmpXxjf07/sampleModules'
#> )
#> Paths set to:
#> options(
#> rasterTmpDir = '/tmp/RtmpXxjf07/scratch/raster'
#> reproducible.cachePath = '/tmp/RtmpXxjf07/cache'
#> spades.inputPath = '/tmp/RtmpXxjf07/inputs'
#> spades.outputPath = '/tmp/RtmpXxjf07'
#> spades.modulePath = '/tmp/RtmpXxjf07/sampleModules'
#> spades.scratchPath = '/tmp/RtmpXxjf07/scratch'
#> )
#> terra::terraOptions(tempdir = '/tmp/RtmpXxjf07/scratch/terra'
#> Sep18 15:24:25 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Sep18 15:24:26 simInit The following .globals were used:
#> Sep18 15:24:26 simInit Key: <global, module>
#> Sep18 15:24:26 simInit module global
#> Sep18 15:24:26 simInit <char> <char>
#> Sep18 15:24:26 simInit 1: fireSpread burnStats
#> Sep18 15:24:26 simInit 2: caribouMovement stackName
#> Sep18 15:24:26 simInit 3: fireSpread stackName
#> Sep18 15:24:26 simInit 4: randomLandscapes stackName
#> Elapsed time for simInit: 0.1437967 secs
#> Setting:
#> options(
#> spades.outputPath = '/tmp/RtmpXxjf07'
#> spades.modulePath = '/tmp/RtmpXxjf07/sampleModules'
#> )
#> Paths set to:
#> options(
#> rasterTmpDir = '/tmp/RtmpXxjf07/scratch/raster'
#> reproducible.cachePath = '/tmp/RtmpXxjf07/cache'
#> spades.inputPath = '/tmp/RtmpXxjf07/inputs'
#> spades.outputPath = '/tmp/RtmpXxjf07'
#> spades.modulePath = '/tmp/RtmpXxjf07/sampleModules'
#> spades.scratchPath = '/tmp/RtmpXxjf07/scratch'
#> )
#> terra::terraOptions(tempdir = '/tmp/RtmpXxjf07/scratch/terra'
#> Sep18 15:24:26 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Sep18 15:24:26 simInit The following .globals were used:
#> Sep18 15:24:26 simInit Key: <global, module>
#> Sep18 15:24:26 simInit module global
#> Sep18 15:24:26 simInit <char> <char>
#> Sep18 15:24:26 simInit 1: fireSpread burnStats
#> Sep18 15:24:26 simInit 2: caribouMovement stackName
#> Sep18 15:24:26 simInit 3: fireSpread stackName
#> Sep18 15:24:26 simInit 4: randomLandscapes stackName
#> Elapsed time for simInit: 0.103411 secs
#> Setting:
#> options(
#> spades.outputPath = '/tmp/RtmpXxjf07'
#> spades.modulePath = '/tmp/RtmpXxjf07/sampleModules'
#> )
#> Paths set to:
#> options(
#> rasterTmpDir = '/tmp/RtmpXxjf07/scratch/raster'
#> reproducible.cachePath = '/tmp/RtmpXxjf07/cache'
#> spades.inputPath = '/tmp/RtmpXxjf07/inputs'
#> spades.outputPath = '/tmp/RtmpXxjf07'
#> spades.modulePath = '/tmp/RtmpXxjf07/sampleModules'
#> spades.scratchPath = '/tmp/RtmpXxjf07/scratch'
#> )
#> terra::terraOptions(tempdir = '/tmp/RtmpXxjf07/scratch/terra'
#> Sep18 15:24:26 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Sep18 15:24:26 simInit The following .globals were used:
#> Sep18 15:24:26 simInit Key: <global, module>
#> Sep18 15:24:26 simInit module global
#> Sep18 15:24:26 simInit <char> <char>
#> Sep18 15:24:26 simInit 1: fireSpread burnStats
#> Sep18 15:24:26 simInit 2: caribouMovement stackName
#> Sep18 15:24:26 simInit 3: fireSpread stackName
#> Sep18 15:24:26 simInit 4: randomLandscapes stackName
#> Elapsed time for simInit: 0.1115613 secs