SpaDES useSpaDES useThis vignette covers tools that become useful once you are building
or maintaining a substantial SpaDES workflow: monitoring
memory use across events, and statically checking that a module’s code
agrees with its declared metadata.
profvis::profvis is great for digging into R internals,
but it doesn’t fit the discrete-event pattern very well. Often the
question is simpler: which event uses the most memory, and how much?
That number sets how many parallel runs you can fit on a machine.
SpaDES.core ships a small background sampler. Turn it on
by setting the option spades.memoryUseInterval to a
sampling interval in seconds (e.g., 0.2). If
future and future.callr are installed, each
spades() call will then:
future (parallel) R session that runs
system('ps') once per interval, keeping only the row for
the main R process;spades() returns, read the file into
sim@.xData$.memoryUse$obj and delete the file;if (requireNamespace("future", quietly = TRUE) &&
requireNamespace("future.callr", quietly = TRUE)) {
options(spades.memoryUseInterval = 0.5)
# sim <- simInit(...)
# sim <- spades(sim)
memoryUse(sim, max = TRUE) # peak memory by event type (summarised across times)
memoryUse(sim, max = FALSE) # peak memory for every individual event
}[memoryUseThisSession()] returns the memory currently used by the running R session; useful as a quick sanity check between events.
simInit() parses your module’s source and statically
checks that the code agrees with the metadata: every read of
sim$x should have x listed in
expectsInput() or createsOutput(), every
parameter access (Par$y, P(sim)$y,
params(sim)$mod$y) should match a
defineParameter() entry, every doEvent.*
function should return sim, and so on. Mismatches are
reported as a structured table with file/line/column locations.
The checks are controlled by two options:
options(spades.moduleCodeChecks = TRUE) # turn checking on/off
options(spades.codeCheckEngine = "v2") # "v1" (legacy) or "v2" (new, structured)You can also run the checks on a module on disk, without going
through simInit(). This is handy while authoring or
refactoring a module:
# returns a data.frame of findings; also prints a grouped report
findings <- codeCheckModule("path/to/myModule")
# subset by severity or rule id
findings[findings$severity == "warning", ]
findings[findings$id == "param_used_undeclared", ]A finding has columns id, severity,
module, where, name,
fn, file, line, col,
message, suggestion. The suggestion is a
plain-language fix (e.g., “add
defineParameter('coverThresh', ...) to parameters”). When
simInit() runs the checks, the same data.frame is stashed
at sim@.xData$.codeCheck[[moduleName]].
Typical findings the checker flags:
| id | what it catches |
|---|---|
out_declared_unused |
output declared in metadata, never assigned in code |
out_used_undeclared |
sim$x <- ... for x not in
createsOutput() |
in_declared_unused |
input declared, never read |
in_used_undeclared |
sim$x read for x not in
expectsInput() |
param_declared_unused |
defineParameter("x", ...) but no Par$x in
the code |
param_used_undeclared |
Par$x / P(sim)$x for x not
declared |
unresolved_accessor |
sim[[expr]] or get(var, envir=envir(sim))
— can’t be checked statically |
must_return_sim |
doEvent.* not ending with
return(invisible(sim)) |
must_assign_to_sim |
scheduleEvent(...) or saveFiles(...) not
assigned back to sim |
module_named_object |
sim$<moduleName> <- ... (forbidden) |
conflicting_fn_unqualified |
bare levels/scale/which.max
(ambiguous with raster::) |
See [codeCheckModule()] for details.