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 modulePath
s
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/Rtmp45trFG/sampleModules'
#> )
#> Paths set to:
#> options(
#> rasterTmpDir = '/tmp/Rtmp45trFG/scratch/raster'
#> reproducible.cachePath = '/tmp/Rtmp45trFG/cache'
#> spades.inputPath = '/tmp/Rtmp45trFG/inputs'
#> spades.outputPath = '/tmp/Rtmp45trFG'
#> spades.modulePath = '/tmp/Rtmp45trFG/sampleModules'
#> spades.scratchPath = '/tmp/Rtmp45trFG/scratch'
#> )
#> terra::terraOptions(tempdir = '/tmp/Rtmp45trFG/scratch/terra'
#> Nov21 04:22:52 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Nov21 04:22:52 simInit The following .globals were used:
#> Nov21 04:22:52 simInit Key: <global, module>
#> Nov21 04:22:52 simInit module global
#> Nov21 04:22:52 simInit <char> <char>
#> Nov21 04:22:52 simInit 1: caribouMovement stackName
#> Nov21 04:22:52 simInit 2: fireSpread stackName
#> Nov21 04:22:52 simInit 3: randomLandscapes stackName
#> Elpsed time for simInit: 0.1237538 secs
#> Nov21 04:22:52 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Nov21 04:22:52 frSprd:init fireSpread
#> Nov21 04:22:52 frSprd:stats fireSpread
#> Nov21 04:22:52 frSprd:stats fireSpread
#> simList saved in
#> SpaDES.core:::savedSimEnv()$.sim
#> It will be deleted at next spades() call.
#> Setting:
#> options(
#> spades.modulePath = '/tmp/Rtmp45trFG/sampleModules'
#> )
#> Paths set to:
#> options(
#> rasterTmpDir = '/tmp/Rtmp45trFG/scratch/raster'
#> reproducible.cachePath = '/tmp/Rtmp45trFG/cache'
#> spades.inputPath = '/tmp/Rtmp45trFG/inputs'
#> spades.outputPath = '/tmp/Rtmp45trFG'
#> spades.modulePath = '/tmp/Rtmp45trFG/sampleModules'
#> spades.scratchPath = '/tmp/Rtmp45trFG/scratch'
#> )
#> terra::terraOptions(tempdir = '/tmp/Rtmp45trFG/scratch/terra'
#> Nov21 04:22:52 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Nov21 04:22:53 simInit The following .globals were used:
#> Nov21 04:22:53 simInit Key: <global, module>
#> Nov21 04:22:53 simInit module global
#> Nov21 04:22:53 simInit <char> <char>
#> Nov21 04:22:53 simInit 1: caribouMovement stackName
#> Nov21 04:22:53 simInit 2: fireSpread stackName
#> Nov21 04:22:53 simInit 3: randomLandscapes stackName
#> Elpsed time for simInit: 0.09863734 secs
#> Nov21 04:22:53 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Nov21 04:22:53 frSprd:init fireSpread
#> Nov21 04:22:53 frSprd:stats fireSpread
#> Nov21 04:22:53 frSprd:stats fireSpread
#> simList saved in
#> SpaDES.core:::savedSimEnv()$.sim
#> It will be deleted at next spades() call.
#> Setting:
#> options(
#> spades.outputPath = '/tmp/Rtmp45trFG'
#> spades.modulePath = '/tmp/Rtmp45trFG/sampleModules'
#> )
#> Paths set to:
#> options(
#> rasterTmpDir = '/tmp/Rtmp45trFG/scratch/raster'
#> reproducible.cachePath = '/tmp/Rtmp45trFG/cache'
#> spades.inputPath = '/tmp/Rtmp45trFG/inputs'
#> spades.outputPath = '/tmp/Rtmp45trFG'
#> spades.modulePath = '/tmp/Rtmp45trFG/sampleModules'
#> spades.scratchPath = '/tmp/Rtmp45trFG/scratch'
#> )
#> terra::terraOptions(tempdir = '/tmp/Rtmp45trFG/scratch/terra'
#> Nov21 04:22:53 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Nov21 04:22:53 simInit The following .globals were used:
#> Nov21 04:22:53 simInit Key: <global, module>
#> Nov21 04:22:53 simInit module global
#> Nov21 04:22:53 simInit <char> <char>
#> Nov21 04:22:53 simInit 1: caribouMovement stackName
#> Nov21 04:22:53 simInit 2: fireSpread stackName
#> Nov21 04:22:53 simInit 3: randomLandscapes stackName
#> Elpsed time for simInit: 0.1435642 secs
#> Setting:
#> options(
#> spades.outputPath = '/tmp/Rtmp45trFG'
#> spades.modulePath = '/tmp/Rtmp45trFG/sampleModules'
#> )
#> Paths set to:
#> options(
#> rasterTmpDir = '/tmp/Rtmp45trFG/scratch/raster'
#> reproducible.cachePath = '/tmp/Rtmp45trFG/cache'
#> spades.inputPath = '/tmp/Rtmp45trFG/inputs'
#> spades.outputPath = '/tmp/Rtmp45trFG'
#> spades.modulePath = '/tmp/Rtmp45trFG/sampleModules'
#> spades.scratchPath = '/tmp/Rtmp45trFG/scratch'
#> )
#> terra::terraOptions(tempdir = '/tmp/Rtmp45trFG/scratch/terra'
#> Nov21 04:22:53 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Nov21 04:22:53 simInit The following .globals were used:
#> Nov21 04:22:53 simInit Key: <global, module>
#> Nov21 04:22:53 simInit module global
#> Nov21 04:22:53 simInit <char> <char>
#> Nov21 04:22:53 simInit 1: caribouMovement stackName
#> Nov21 04:22:53 simInit 2: fireSpread stackName
#> Nov21 04:22:53 simInit 3: randomLandscapes stackName
#> Elpsed time for simInit: 0.09778404 secs
#> Setting:
#> options(
#> spades.outputPath = '/tmp/Rtmp45trFG'
#> spades.modulePath = '/tmp/Rtmp45trFG/sampleModules'
#> )
#> Paths set to:
#> options(
#> rasterTmpDir = '/tmp/Rtmp45trFG/scratch/raster'
#> reproducible.cachePath = '/tmp/Rtmp45trFG/cache'
#> spades.inputPath = '/tmp/Rtmp45trFG/inputs'
#> spades.outputPath = '/tmp/Rtmp45trFG'
#> spades.modulePath = '/tmp/Rtmp45trFG/sampleModules'
#> spades.scratchPath = '/tmp/Rtmp45trFG/scratch'
#> )
#> terra::terraOptions(tempdir = '/tmp/Rtmp45trFG/scratch/terra'
#> Nov21 04:22:54 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Nov21 04:22:54 simInit The following .globals were used:
#> Nov21 04:22:54 simInit Key: <global, module>
#> Nov21 04:22:54 simInit module global
#> Nov21 04:22:54 simInit <char> <char>
#> Nov21 04:22:54 simInit 1: caribouMovement stackName
#> Nov21 04:22:54 simInit 2: fireSpread stackName
#> Nov21 04:22:54 simInit 3: randomLandscapes stackName
#> Elpsed time for simInit: 0.1038966 secs