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 list,list,list,list,list,data.frame,data.frame,character
simInit(
  times,
  params,
  modules,
  objects,
  paths,
  inputs,
  outputs,
  loadOrder,
  notOlderThan = NULL,
  ...
)

# S4 method for ANY,ANY,ANY,character,ANY,ANY,ANY,ANY
simInit(
  times,
  params,
  modules,
  objects,
  paths,
  inputs,
  outputs,
  loadOrder,
  notOlderThan = NULL,
  ...
)

# S4 method for ANY,ANY,character,ANY,ANY,ANY,ANY,ANY
simInit(
  times,
  params,
  modules,
  objects,
  paths,
  inputs,
  outputs,
  loadOrder,
  notOlderThan = NULL,
  ...
)

# S4 method for ANY,ANY,ANY,ANY,ANY,ANY,ANY,ANY
simInit(
  times,
  params,
  modules,
  objects,
  paths,
  inputs,
  outputs,
  loadOrder,
  notOlderThan = NULL,
  ...
)

simInitDefaults()

Arguments

times

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.

params

A list of lists of the form list(moduleName=list(param1=value, param2=value)). See details.

modules

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).

objects

(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.

paths

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.

inputs

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.

outputs

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.

loadOrder

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.

notOlderThan

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.

Value

A simList simulation object, pre-initialized from values specified in the arguments supplied.

Details

Calling this simInit function does the following:

WhatDetailsArgument(s) to use
fills simList slotsplaces the arguments times, params, modules, paths into equivalently named simList slotstimes, params, modules, paths
sources all module filesplaces 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 objectsfrom the global environment to the simList environmentobjects
loads objectsfrom disk into the simListinputs
schedule object loading/copyingObjects can be loaded into the simList at any time during a simulationinputs
schedule object savingObjects 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" eventsfrom all modules (see events())automatic
assesses module dependenciesvia 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 unittakes time units of modules and how they fit togethertimes or automatic
runs .inputObjects functionsfrom every module in the module order as determined aboveautomatic

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.modulePath");

  • modulePath: getOption("spades.inputPath");

  • inputPath: getOption("spades.outputPath").

Note

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.

Parsing and Checking Code

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).

Caching

Using caching with SpaDES is vital when building re-usable and reproducible content. Please see the vignette dedicated to this topic.

References

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

See also

Author

Alex Chubaty and Eliot McIntire

Examples

 # 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/RtmpNobduY/sampleModules'
#>   )
#> Paths set to:
#>   options(
#>     rasterTmpDir = '/tmp/RtmpNobduY/scratch/raster'
#>     reproducible.cachePath = '/tmp/RtmpNobduY/cache'
#>     spades.inputPath = '/tmp/RtmpNobduY/inputs'
#>     spades.outputPath = '/tmp/RtmpNobduY'
#>     spades.modulePath = '/tmp/RtmpNobduY/sampleModules'
#>     spades.scratchPath = '/tmp/RtmpNobduY/scratch'
#>   )
#>   terra::terraOptions(tempdir = '/tmp/RtmpNobduY/scratch/terra'
#> Apr16 20:31:00 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Apr16 20:31:00 simInit The following .globals were used:
#> Apr16 20:31:00 simInit Key: <global, module>
#> Apr16 20:31:00 simInit              module    global
#> Apr16 20:31:00 simInit              <char>    <char>
#> Apr16 20:31:00 simInit 1:  caribouMovement stackName
#> Apr16 20:31:00 simInit 2:       fireSpread stackName
#> Apr16 20:31:00 simInit 3: randomLandscapes stackName
#> Elpsed time for simInit: 0.09583259 secs
#> Apr16 20:31:00 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> simList saved in
#> SpaDES.core:::.pkgEnv$.sim
#> It will be deleted at next spades() call.
#> Setting:
#>   options(
#>     spades.modulePath = '/tmp/RtmpNobduY/sampleModules'
#>   )
#> Paths set to:
#>   options(
#>     rasterTmpDir = '/tmp/RtmpNobduY/scratch/raster'
#>     reproducible.cachePath = '/tmp/RtmpNobduY/cache'
#>     spades.inputPath = '/tmp/RtmpNobduY/inputs'
#>     spades.outputPath = '/tmp/RtmpNobduY'
#>     spades.modulePath = '/tmp/RtmpNobduY/sampleModules'
#>     spades.scratchPath = '/tmp/RtmpNobduY/scratch'
#>   )
#>   terra::terraOptions(tempdir = '/tmp/RtmpNobduY/scratch/terra'
#> Apr16 20:31:01 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Apr16 20:31:01 simInit The following .globals were used:
#> Apr16 20:31:01 simInit Key: <global, module>
#> Apr16 20:31:01 simInit              module    global
#> Apr16 20:31:01 simInit              <char>    <char>
#> Apr16 20:31:01 simInit 1:  caribouMovement stackName
#> Apr16 20:31:01 simInit 2:       fireSpread stackName
#> Apr16 20:31:01 simInit 3: randomLandscapes stackName
#> Elpsed time for simInit: 0.0957346 secs
#> Apr16 20:31:01 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> simList saved in
#> SpaDES.core:::.pkgEnv$.sim
#> It will be deleted at next spades() call.
#> Setting:
#>   options(
#>     spades.outputPath = '/tmp/RtmpNobduY'
#>     spades.modulePath = '/tmp/RtmpNobduY/sampleModules'
#>   )
#> Paths set to:
#>   options(
#>     rasterTmpDir = '/tmp/RtmpNobduY/scratch/raster'
#>     reproducible.cachePath = '/tmp/RtmpNobduY/cache'
#>     spades.inputPath = '/tmp/RtmpNobduY/inputs'
#>     spades.outputPath = '/tmp/RtmpNobduY'
#>     spades.modulePath = '/tmp/RtmpNobduY/sampleModules'
#>     spades.scratchPath = '/tmp/RtmpNobduY/scratch'
#>   )
#>   terra::terraOptions(tempdir = '/tmp/RtmpNobduY/scratch/terra'
#> Apr16 20:31:02 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Apr16 20:31:02 simInit The following .globals were used:
#> Apr16 20:31:02 simInit Key: <global, module>
#> Apr16 20:31:02 simInit              module    global
#> Apr16 20:31:02 simInit              <char>    <char>
#> Apr16 20:31:02 simInit 1:  caribouMovement stackName
#> Apr16 20:31:02 simInit 2:       fireSpread stackName
#> Apr16 20:31:02 simInit 3: randomLandscapes stackName
#> Elpsed time for simInit: 0.1051326 secs
#> Setting:
#>   options(
#>     spades.outputPath = '/tmp/RtmpNobduY'
#>     spades.modulePath = '/tmp/RtmpNobduY/sampleModules'
#>   )
#> Paths set to:
#>   options(
#>     rasterTmpDir = '/tmp/RtmpNobduY/scratch/raster'
#>     reproducible.cachePath = '/tmp/RtmpNobduY/cache'
#>     spades.inputPath = '/tmp/RtmpNobduY/inputs'
#>     spades.outputPath = '/tmp/RtmpNobduY'
#>     spades.modulePath = '/tmp/RtmpNobduY/sampleModules'
#>     spades.scratchPath = '/tmp/RtmpNobduY/scratch'
#>   )
#>   terra::terraOptions(tempdir = '/tmp/RtmpNobduY/scratch/terra'
#> Apr16 20:31:02 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Apr16 20:31:02 simInit The following .globals were used:
#> Apr16 20:31:02 simInit Key: <global, module>
#> Apr16 20:31:02 simInit              module    global
#> Apr16 20:31:02 simInit              <char>    <char>
#> Apr16 20:31:02 simInit 1:  caribouMovement stackName
#> Apr16 20:31:02 simInit 2:       fireSpread stackName
#> Apr16 20:31:02 simInit 3: randomLandscapes stackName
#> Elpsed time for simInit: 0.1094615 secs
#> Setting:
#>   options(
#>     spades.outputPath = '/tmp/RtmpNobduY'
#>     spades.modulePath = '/tmp/RtmpNobduY/sampleModules'
#>   )
#> Paths set to:
#>   options(
#>     rasterTmpDir = '/tmp/RtmpNobduY/scratch/raster'
#>     reproducible.cachePath = '/tmp/RtmpNobduY/cache'
#>     spades.inputPath = '/tmp/RtmpNobduY/inputs'
#>     spades.outputPath = '/tmp/RtmpNobduY'
#>     spades.modulePath = '/tmp/RtmpNobduY/sampleModules'
#>     spades.scratchPath = '/tmp/RtmpNobduY/scratch'
#>   )
#>   terra::terraOptions(tempdir = '/tmp/RtmpNobduY/scratch/terra'
#> Apr16 20:31:02 simInit Using setDTthreads(1). To change: 'options(spades.DTthreads = X)'.
#> Apr16 20:31:02 simInit The following .globals were used:
#> Apr16 20:31:02 simInit Key: <global, module>
#> Apr16 20:31:02 simInit              module    global
#> Apr16 20:31:02 simInit              <char>    <char>
#> Apr16 20:31:02 simInit 1:  caribouMovement stackName
#> Apr16 20:31:02 simInit 2:       fireSpread stackName
#> Apr16 20:31:02 simInit 3: randomLandscapes stackName
#> Elpsed time for simInit: 0.1282065 secs