archive::archive_extract() (e.g. via
prepInputs()) inside a module is now throttled on Windows and other
non-dynamic sessions. The progress frames are routed through the
spades()/simInit() message handler by cli::start_app(output = "message");
.isCliProgressTick() previously recognised a non-dynamic frame only when
cli::cli_progress_num() >= 1, but archive's bar lives in compiled
libarchive code, so cli's R-level registry never sees it and the count stays
0. Every frame therefore fell through and was re-printed with a full
Date-Time-Module-Event prefix, flooding the log with thousands of near-identical
lines (and pushing earlier messages out of view). Such frames are now also
recognised by their leading cli Braille spinner glyph (U+2800-U+28FF,
cli's default spinner family, which never begins a normal message), plus the
trailing blank frame that closes the bar, so the flood collapses to one line per
getOption("spades.progressInterval", 2) seconds. The same path covers other
C-level cli progress bars routed through the handler.simInit no longer installs (or loads) the box package. box does not
work well within the SpaDES ecosystem, so the default for the
spades.reqdPkgsDontLoad option is now NULL (was "box"), and the unused,
unimplemented spades.useBox option and its associated (disabled) box::use
machinery have been removed. The generic spades.reqdPkgsDontLoad mechanism --
install a package but do not library()/require() it -- is retained for any
package a user explicitly lists.defineParameter defaults/min/max/class)
is no longer included in the .useCache cacheId for .inputObjects/events. Only
the resolved parameter values (digested separately) affect caching now. Including
the definition table made the cacheId depend on metadata that can differ across
machines/OSs/package versions even when the run is identical -- e.g. an
environment-derived default (getOption(...)), or a platform-dependent attribute
on a table column -- so the same call produced different cacheIds on Linux vs
Windows and could not share a (cloud) cache. (Extends the earlier removal of the
human-readable desc columns from this digest.) This changes existing cacheIds
once (a one-time recompute), after which identical runs across machines share a
cache as intended.simInit(objects = ...) again loads all user-supplied objects when
options(spades.allowInitDuringSimInit = TRUE) (as set by some
SpaDES.project::setupProject() configurations). A regression in the
.runInputObjectsPhase() refactor restricted the loaded objects to those
declared as a module inputObject, silently dropping arbitrary objects passed
via objects = list(...) -- and every object when no module declares them.
The intended behaviour (don't let user inputs clobber objects an init produced
during simInit) is preserved via objectsToUseUpdatesFromPrevInits().
.useCacheArgs entries on the .inputObjects path are now evaluated at the
splice site, in the module's context (sim@current is the module being
processed), matching the event path in .runEvent(). Previously a quoted
entry such as useCloud = quote(P(sim)$.useCloud) reached reproducible::Cache()
as an unevaluated call and was forced later, where the module context was no
longer available, so module-context accessors like P(sim) resolved to the
wrong module (or NULL). As a result, e.g. cloud caching of .inputObjects
keyed off .useCloud silently did not engage. Quoted entries now resolve to
this module's parameters before being merged into the Cache() call.
restartSimInit() now rewinds state the same way restartSpades() does: it
removes objects the interrupted .inputObjects created (so a re-run starts
from a clean slate, not leftover partial outputs) and re-establishes each
re-parsed module's mod/P active bindings. These were previously done only on
the spades() recovery path. Both restart functions now share the same rewind
helpers (.restartRestoreEventObjs(), .restartRefreshBindings()), sim-resolution
(.restartResolveSim()), and module-identification (.restartModuleToReparse()),
so the two paths cannot drift.
restartSimInit() now restarts the module that actually failed, even when an
earlier module's .inputObjects was cached. A cache hit returns a simList
with a fresh @.xData environment (.prepareOutput() copies it), so the
.inputObjects drain advanced onto a new environment while simInit()'s own
frame sim -- the one its on.exit recovery handler read -- went stale,
recording the module before the failure as the interrupted one. The recovery
handler now lives beside the drain loop (in .runInputObjectsPhase(), mirroring
how spades()'s inline loop already worked), so it reads the live sim. The
correct module is therefore reparsed on resume, so edits to the failing module's
.inputObjects take effect. A companion guard stops a resumed phase from
scheduling a second .inputObjects event for a module that already has one
queued (which otherwise ran it twice).
restartSimInit()'s resumed .inputObjects messages and warnings now carry the
usual simInit/<module>:<event> logging prefix. The resume previously ran the
phase outside simInit()'s withCallingHandlers(), so its output was
unprefixed; the handlers are now shared between the two paths.
A module's .inputObjects cache is now invalidated when you edit a helper
function it calls, not only when you edit .inputObjects itself. Previously the
cache digest covered only the .inputObjects function, so fixing a helper left
the cacheId unchanged and the stale cached result was returned -- most visibly
via restartSimInit()/restartSpades(), whose rewind recreates the exact state
the stale entry was keyed under (a fresh simInitAndSpades() could mask the
problem by computing a different state, hence a different cacheId). The digest
now covers the transitive call-graph closure of module-local functions reachable
from .inputObjects (via codetools::findGlobals(), including functions passed
as values and helpers reached through nested closures), so an edit to any
function it (transitively) calls busts the cache, while edits to unrelated module
functions (e.g. doEvent, Init, an unused helper) do not. String-based dynamic
dispatch (do.call("helper", ), get("helper")()) is followed too, by scanning
string literals against module function names. Package functions remain out of
scope; their changes are tracked via reqdPkgs.
Progress bars routed through the message handler (e.g. from
archive::archive_extract() during prepInputs()) are now throttled in
non-interactive sessions too. Previously, progress ticks were only recognised
when cli ran in dynamic mode (where each frame carries a carriage return or
cursor-control sequence). In non-dynamic sessions (non-interactive, logged
runs, CI, RStudio jobs) cli emits each tick as a plain newline-terminated
line, so every frame slipped past detection and was re-printed with a full
Date-Time-Module-Event prefix — flooding logs with thousands of near-identical
lines. Detection now also recognises a tick by its cli condition class
combined with an active cli progress bar, so the existing throttle
(getOption("spades.progressInterval", 2)) applies regardless of terminal
mode. cli alerts are unaffected.
simInit() no longer errors when called with params = list(.globals = ...)
but no modules. Previously this failed with
no applicable method for @ applied to an object of class "NULL"
because the default empty dependency list (list(NULL)) was iterated by
updateParamsFromGlobals().
If setting up a simulation fails partway through, you no longer have to start
over. After fixing the module that caused the error, call restartSpades() (or
the new restartSimInit()) and it picks up where it left off instead of redoing
everything. This is on by default. See ?restartSimInit.
To add this, rather than building a separate restart mechanism for setup, we
reworked setting up a simulation so its steps run as events, the same way a
running simulation does. That let recovery reuse the same mechanism
restartSpades() already uses. restartSimInit() remains as the entry point
for the setup case (and restartSpades() hands off to it automatically),
because, unlike a running simulation, setup cannot simply be resumed in place.
prepInputs() or
preProcess()), and tags each one with the module and event that asked for
it. This makes it easy to see where a simulation's input data came from. It is
on by default; turn it off with options(spades.urlLog = FALSE). See
?spadesOptions.restartSpades() (which resumes a simulation that was interrupted) now
remembers the events filter from the original spades() call, so the
resumed run repeats the same subset of events instead of running everything.
Supply a new events argument to restartSpades() to override it. See
?restartSpades.These changes are all to the module code checker, which warns module authors
about likely mistakes in their module's R code. See ?codeCheckModule.
# nolint comment (or # nolint: <rule_id> for one specific
check) to a line of module code; a user can set
options(spades.codeChecksIgnore = list(<rule_id> = c("objectName", ...)));
and options(spades.moduleCodeChecks = list(disable = ...)) now also works.reqdPkgs): a warning when a
package is listed more than once (especially with a conflicting source or
version), when a module calls package::function() for a package it never
declared, and (best-effort, informational) when a bare function call has no
obvious source among the declared packages.# nolint: vars a, b lets an author promise that objects a and b are
created on a line where the names cannot be seen automatically (for example
list2env(someList, envir(sim))), so they are not wrongly flagged as
declared-but-unused outputs.paramCheckOtherMods(sim, "x") as a use of
parameter "x".[conflicting_fn_unqualified]) so it can be pasted straight into a # nolint
comment or the spades.codeChecksIgnore option. The broader group name (e.g.
globals) is also accepted there.unresolved_accessor warning: it now explains that
some dynamic ways of reading objects (e.g. get()/mget() or
sim[[<variable>]]) cannot be checked automatically, and how to acknowledge
them with # nolint.suppliedElsewhere("x", sim) guard, nor when it is
asserted with # nolint: vars.params(sim)[[currentModule(sim)]]$x is now correctly understood as a
parameter of the current module, instead of being flagged as unresolved.compiler::cmpfun() or Cache(); the checker now looks
through such wrappers. Objects read inside anonymous callbacks (e.g.
lapply(x, function(i) sim[[i]])) are also no longer misattributed.codeCheckModules() checks several modules for coding problems in one
call. With no arguments, it checks every module in your project's module
folder (the spades.modulePath option). It is the bulk version of
codeCheckModule(), which checks a single module. See ?codeCheckModule.sim$<moduleName> <- ...) now gives a clearer message, explaining that this
name clash can cause hard-to-find problems.reproducible leave the cache location (the
reproducible.cachePath option) empty until the cache is first used;
setPaths() and getPaths() previously stopped with an "Invalid path: cannot
be NULL" error in that case, which prevented the package from loading. They
now fall back to a temporary cache folder, the same way reproducible itself
does. This works with both the current CRAN reproducible (3.1.1) and the
development version. See ?setPaths.test-module-deps-methods.R (using
igraph::cluster_optimal()) is fragile across igraph versions and
GLPK link configurations. Despite existing platform-specific skips,
individual Linux configurations on the CRAN check farm still triggered
the assertion. The test now unconditionally skip_on_cran(), while
retaining the existing platform skips for local/CI runs.randomLandscapes sample module called SpaDES.tools::neutralLandscapeMap()
via a path that required NLMR whenever the installed SpaDES.tools was
the CRAN version (2.1.1, which lacks the built-in gaussian generator).
CRAN's check machines do not install NLMR (it is not on a mainstream
repository, and Additional_repositories is not used to install Suggests
during routine checks), so every test that ran spades() with this
module errored with "Package 'NLMR' not available".randomLandscapes's makeNLM() helper now degrades gracefully with no
hard dependency: (1) SpaDES.tools (>= 2.1.2) -> built-in gaussian
generator; (2) older SpaDES.tools with NLMR installed -> NLMR's
nlm_mpd (unchanged behaviour for users who have NLMR); (3) otherwise
-> a zero-dependency terra fallback (smoothed i.i.d. normal noise), so
the module never errors.NLMR fully removed as a declared dependency: dropped from Suggests,
Additional_repositories, and the Description field. It is now used
only opportunistically by the randomLandscapes sample module when the
user happens to have it installed. Closes #334..robustDigest() for simList no longer
contributes the @inputs and module-scoped @outputs data.frames to the
cache digest. The rationale: if an object affects the event, it is already
digested directly via the module's expected inputs; whether it arrived
through simInit(inputs = ...) should not change the cache key. The visible
effect is that cached events written by SpaDES.core < 3.0.4.9020 will not
be found by ≥ 3.0.4.9020 — the digest changed, so the next call to
spades() recomputes (and re-caches) those events. Cloud caches keyed on
the old digest are similarly invisible to the new code. No data is lost;
the cache simply rebuilds.restartSpades() now defaults to numEvents = 1L (previously Inf).
Callers that relied on the old default to replay every saved event must
pass numEvents = Inf explicitly.reproducible ≥ 3.0.0.
See the reproducible NEWS for migration notes.?SpaDES.core).
Restructured into 14 sections, simpler language, every user-facing export
is now linked, and the inlined options table is replaced with a pointer to
[spadesOptions()] (which is the single source of truth). New sections cover
code checking, persistence/recovery, memory monitoring, and conditional
events.options(spades.codeCheckEngine = "v2")),
built on xmlparsedata + xml2. Adds: parameter-use checks (Par$x,
P(sim)$x, P(sim, module = "other")$x, params(sim)$mod$x,
params(sim)[["m"]][["x"]]), broader sim accessor coverage
(get/assign/exists/mget against envir(sim)), accurate
source-position reporting (file:line:col), structured data.frame of
findings (stashed on [email protected]$.codeCheck), one suggestion per finding,
and grouped cli table output. v1 (code-checking.R) remains the default
and is selectable via options(spades.codeCheckEngine = "v1"). New
standalone API codeCheckModule(path) runs the checks against a module
directory without requiring simInit()..useCacheArgs: optional named list (keyed by event name) of extra arguments spliced into the per-event reproducible::Cache() call. Lets a developer pin a fixed cacheId so a pre-seeded cloud folder (useCloud = TRUE, cloudFolderID = ...) can short-circuit a deterministic event to a download. Falls through to existing defaults when absent. The newModule() template emits a commented-out opt-in example.saveSimList: resilient to individual file-backed objects whose backing files are inaccessible at save time; such objects are saved as NULL with a warning rather than aborting the entire save.loadSimList: mirror-image resilience on the load side via .unwrapResiliently() — file-backed objects that cannot be .unwrap()ped (e.g. backing files missing on this machine) are loaded as NULL with a warning instead of aborting the load.loadSimList: path-remap loops now skip non-character / NULL list elements (left behind by .wrapResiliently having nulled an object at save time), preventing dirname(fileHere) from aborting with "a character vector argument expected".saveSimList: new lazy = TRUE option saves each user object individually into a <filename>_xData/ directory; loadSimList auto-detects this layout and loads objects lazily via delayedAssign, materialising each only on first access. During lazy load, .modObjs (per-module object copies held in the shell simList) is cleared before .unwrap() so that missing backing files do not cause a load failure; .modObjs is rebuilt by spades() on the next run.saveSimList(lazy = TRUE): restrict tools::makeLazyLoadDB to user objects (variables = userObjNames) so dot-prefixed internals (.mods, .modObjs, ...) are not bundled into the lazy DB. Without this, makeLazyLoadDB's envhook collapses each nested env's bindings into a single serialized blob, and .mods alone can blow R's 2 GB single-value lazyLoadDB limit (Error in lazyLoadDBinsertValue ... long vectors not supported yet: connections.c:6552).saveSimList: remove [email protected]$._sim (circular reference) before saving to avoid redundant data in the saved file.archive::archive_extract) no longer flood
the console during simInit/spades. Progress output is throttled to one line per
getOption("spades.progressInterval", 2) seconds. Set
options(spades.progressInterval = N) to change the interval.suppliedElsewhere now treats both types of NULL as equal: list(a = 1)$b and list(a = NULL)$a were seen as different; now they are the sameR/module-malformed.R). A small registry of detectors
(.CC_MALFORMED_CHECKS) catches common authoring mistakes and emits a
clear, actionable error message instead of R's cryptic parse-error or
the deep failures that used to surface only inside defineModule().
Initial detectors: trailing comma in defineModule(sim, list(...)),
missing comma between metadata rows inside rbind()/bindrows(),
unquoted parameter name in defineParameter(), and unquoted
objectName in expectsInput()/createsOutput(). New exported
function checkModuleMetadata() runs the checks on a module file
on disk (no simInit() needed). Adding a new check is one entry
in the registry — no surgery elsewhere. Closes #325.randomLandscapes (inst/sampleModules/randomLandscapes)
now calls SpaDES.tools::neutralLandscapeMap() in a version-adaptive
way: with SpaDES.tools (>= 2.1.2) it uses the new built-in
type = "gaussian" generator (dependency-free) and the per-layer
smooth argument to control autocorrelation length; with the current
CRAN SpaDES.tools (2.1.1) it falls back to the nlm_mpd generator,
which requires NLMR. NLMR is not in the module's reqdPkgs; the
SpaDES.tools requirement is relaxed to >= 2.1.1. Step toward
closing #334.NLMR is no longer a hard dependency, but it is not fully removed.
It remains a Suggested package, available from the additional
repository https://predictiveecology.r-universe.dev (declared via
Additional_repositories, with installation instructions in the
DESCRIPTION Description field). It is needed only when
neutral-landscape generation runs against SpaDES.tools < 2.1.2;
it can be dropped entirely once SpaDES.tools 2.1.2 reaches CRAN. The
Remotes entry for NLMR was removed (no longer needed). Partial
progress on #334.SpaDES.taols →
SpaDES.tools (in i-introduction and ii-modules); wrong option name
spades.modulesPath → spades.modulePath (three places); broken sentence
about the simList's environment; stale raster::Extent reference in the
module-metadata table → terra::SpatExtent (with Extent kept for
back-compat); stale sim$myFunction() call style — modules now use
namespaced calls; wrong event name "save" in a plot scheduleEvent()
example → "plot"; stale getOption("spades.cachePath") →
reproducible.cachePath; stale SpaDES::setPaths() →
SpaDES.core::setPaths(); Ubuntu 18.04 in v-automated-testing → 24.04.
Modernized SpatialPoints* / Raster* mentions to SpatVector / sf /
SpatRaster (older sp/raster classes still work). The Advanced
vignette is rewritten and gains a section documenting the static code
checker.Plots: refactor for stability, robustness, and expanded coverage.
<dataObjName>_time<simTime>.<ext>
(e.g. myStack_time1.tif) rather than a tempfile()-based suffix, so
repeated runs at the same sim time produce the same filename — making
outputs deterministic and easier to diff/version.png, pdf,
tiff, ...) fails inside fn(data, ...), the device is still closed,
a warning is emitted, and the offending file is recorded in a local
failedFiles set so it is skipped when appending to sim@outputs.
Previously, failures could leave an outputs row pointing at a missing
file.useCache argument: TRUE (or a character vector of types)
enables reproducible::Cache()-style memoization of the file-producing
branch; the cached needNewPlot flag controls whether the plot is
regenerated. Only relevant for non-screen types.data may now be a ggplot object directly (in addition to a
quote()d expression or a raw value); detection moved to a single
is(data, "gg") check, so passing Plots(data = myGgplot, ...) works.filenamesForSave / funsUsed (keyed by type),
and a single isBaseFormat flag selects the base-R vs ggplot2::ggsave
path — replacing two parallel, partly-overlapping code paths.tests/testthat/test-Plots.R) grow from 5 to 8 test_that
blocks (PASS 37 → 51): base-R plotting function (hist) saving png +
raw qs2, terra::SpatRaster saving tif raw, terra::SpatVector
saving qs2 raw, named ... args without data =, and ggplot passed
directly as data.restartSpades now has default numEvents = 1L instead of Infoptions(spades.dotInputObjects = FALSE), then it will not do .inputObjects even
if options(spades.allowInitDuringSimInit = TRUE); previously, this was not respected.reproducible dependency version to 3.0.0;spades.recoveryMode (previously
zero — the only exercise was gated behind if (interactive()) in
test-mod.R and never ran in CI). Three new test_that() blocks cover the
default 1-event recovery, the 2-event case (spades.recoveryMode = 2L,
asserting length(.recoverableObjs) == 2L, most-recent-first ordering, and
full rewind+replay via restartSpades(numEvents = 2L)), and the off case
(spades.recoveryMode = FALSE leaves .recoverableObjs NULL while the
sim is still stashed by saveSimOnExit).restartSpades(): drop unreachable "Cannot replay N events as requested"
message — numMods is pre-clipped via min(length(.recoverableObjs), numEvents), so the gating condition could never be true. Silent clipping
is the documented contract when numEvents exceeds available state.if (interactive()) guards that produced silent
coverage gaps or "empty test" skips, and widen the known igraph
cluster_optimal() community-count fragility skip to Linux (previously
Windows-only).qs2 package instead of qs (being removed from CRAN) for improved object serialization (#291; #316);
This may cause qs based event caching to fail and will have to be re-run/re-cached;simList's mod object:
The mod object was previously placed in [email protected][[moduleName]]$.objects;
even though it had parent.env that was emptyenv(), because it was attached to the
environment [email protected][[moduleName]], this meant that the objects in mod would become
part of the environment where the functions were defined. This created a memory leak,
resulting in inflated caches when events for that module were cached.
It is now in [email protected]$.modObjs, which appears to no longer suffer from the memory leak.
This change in location required many changes throughout all exported reproducible functions
that had simList methods, e.g., .robustDigest, etc.simList objects, i.e., when simInit is called within a module;spades() debug logging;debug as verbose when caching (#322);.robustDigest.simList, Plots,
allowInitDuringSimInit, .prepareOutput, defineParameter;moduleVersion when there are many pathssimList during event caching; missing ability to pass explicit classOptions for paramsreproducible::Cache and its many
exported methods;cacheChaining: a new experimental feature that will reduce time spent on digesting objects when
there is an unbroken sequence of cached events. Event level caching will assess whether
the most recent event was Cached. If it was, then the current cache will skip digesting
and use only the functions (and parameters) and the previous event's cacheId
to assess whether the sim can recover the current event from the cache repository.
Turned on via options(spades.cacheChaining = TRUE)reproducible package, specifically the Cache function;clearCacheEventsOnly is a convenience wrapper that will remove all event-level cached
objects in the cache repository;dotObjs and dotMod which create a canonical pointer in the simList
to these two parts of module-specific objects or functions, respectively;.inputObjects;Plots can now have a quote(data) argument, allowing the whole call to be Cached more easily;doCallSafe that can be used for doCallSafe(simInitAndSpades, out),
and it does not suffer from the slow downs of do.call;.depsEdgeList gains a new argument, outputObjects, which will return "hanging" outputs
as _OUTPUTS_, analogous to the hanging inputs as _INPUTS_.
This is needed to address a new case of suppliedElsewhere where the object is contained within
an objectSynonym, and has not yet been made, but is only listed as an outputObject in a module;evalPostEvent and options(spades.evalPostEvent), where a user can pass quoted code that will be evaluated at the end of each event and .inputObject call;outputs would create false positives
(i.e., a change, when there wasn't one); this meant that caching would only be successful
after the 2nd time running the event, if another module had put objects in the outputs list,
especially by using Plots.Plots() where plots were discarded if no filename was specified;spades.reqdPkgsDontLoad, a character vector. If anything is specified,
then it will not be loaded with require or library, but it will be installed, if needed,
and if spades.useRequire = TRUE, which is the default. Default for this new option is
"box", which is one of potentially many in the R universe that throws an error if it is
loaded.globals(sim) in Sample modulesrestartSpades();newModule, new events argument bugs that caused unwanted objects to be put in the module. Fixed..wrap.simList did not anticipate objects with pointers in the metadata, so recovery from Cache failed if there was, e.g., a SpatExtent object in the metadata. Fixed.simList objects, when loading e.g., lists of lists;options("spades.allowSequentialCaching" = TRUE). When a series of events of events in sequence are cached, setting this option will treat them as a single Cache, so it may be much faster. Experimental and should be used with caution.colnames in completed<- and all.equal.simList (#272);simList objects when multiple paths were used (e.g., length(modulePath) > 1);newModule();saveSimList(..., inputs = FALSE, outputs = FALSE, cache = FALSE, files = FALSE));savedSimEnv()$.sim -- savedSimEnv() is now exported for easier discovery -- an internal package environment is used, unless the user specifies options(reproducible.memoisePersist = TRUE), which will use the global environment to store the .sim object;crayon (superseded) to cli for message colours;reproducible v2.1.0 or higher;reproducible, loadSimList() is incompatible with simList objects saved with earlier versions of SpaDES.core.saveSimList documentation (#260);newModule now correctly defaults to current working directory (#273);figurePath() to get the directory of a module's output figures, which is now uses a separate subdirectory per module (i.e., file.path(outputPath(sim), "figures", <moduleName>)); Plots() defaults to using this path, and module developers are encouraged to update their module code to use figurePath(sim) where Plots() is not being used.simList no longer triggers on changes to .useCache parameter, when it doesn't pertain to the event or module in question.bind_rows from dplyr within expectsInput or createsOutput. Now, if a module uses bind_rows and doesn't have dplyr installed, SpaDES.core will intercept and use SpaDES.core::bindrows.saveSimList() better handles relative paths and symbolic links (#263)saveSimList() does an improved job at handling file-backed objects (previously, tests were passing because object was still there; now object is deleted, simList reloaded, and file-backed objects put back into place)reproducible changes to .wrapreproducible changes to Cache messaging, specifically, remove cases where function was a userTag for an outer function call. Now these will display as otherFunction, so that individual functions can be more easily isolated.simInit and spades that allows for nested calls to simInit and/or spadessimInit is now reportedelapsedTime now displays the largest time unit, which may not be secsIn modCall... is removed.options that are either RequireOptions(), spadesOptions() or reproducibleOptions() can now be set during the simInit by passing them as arguments, e.g., simInit(useMemoise = FALSE). See ?simInit, specifically the ... parameter description. This is not passed as an argument named options: these are just options. For convenience, user can omit the package prefix, e.g., useMemoise for reproducible.useMemoisereproducible updates to exported methodsdmin to go with the other d* SpaDES times.dplyr is removed (again)coltab<- from terra changed how it deals with multi-layer SpatRasters. Two sample modules have been modified to set colours on these multi-layer SpatRastersasc raster-type files using inputs did not work; fixed.simInit parameter checkingCacheing of events and modules. Now, for example, a change to the parameter .useCache = c("init") to .useCache = c("init", ".inputObjects") will not trigger a rerun of the init event. Also, .inputObjects is no longer evaluated for Cacheing of doEventError in if (is.na(sim@params[[m]][[x]])) fixed.asc spatial map files were incorrectly loaded by terra package: fixed.getSampleModules() and getMapPath() ) now used throughout examples, vignettes, and tests to avoid writing files in package installation directories.options(spades.futureEvents = TRUE) has been reworked and now works under a wider array of conditions.Copy, .wrap, wrap and unwrap all have fairly robust methods for simList class. The generics are in reproducible or terrasampleModules to use Plots and .plot parameter.registerOutputs can be used by a developer to add saved files to the outputs(sim) data.frame.simInit or spades calls will now not duplicate time prefixparams and .globals were previously not expected to change during Cached events. Thus returned cached values were always the same as input as params and .globals. They are now assessed and returned as part of the Cache, as expected.Require and reproducible, including renaming cacheRepo to cachePath in some inherited functions.SpaDES.tools::neutralLandscapeMap instead of NLMR package directlyterra and sf instead of raster, sp, rgeos, and rgdal as defaults. Attempts have been made to maintain backwards compatibility in all cases.moduleMetadata now handles multiple module pathsmemoryUseoptions("spades.allowInitDuringSimInit" = TRUE), a user will have init events of one ore more modules run during the simInit call, but only if they have no upstream dependencies, i.e., their expectsInputs cannot be supplied by another module's createsOutputs. simInit will determine which modules have no upstream dependencies and only these will be selected for running only their init events. This can be useful e.g., if there is a module that createsOutputs for a studyArea..plots arg in spades can be set to NA to turn of all plotting. This can also be set with option(spades.plots = NA),moduleMetadata no longer runs .inputObjects. In addition to being unnecessary and slow, it was also failing with reproducible (==1.2.16) because it was trying to run Cache, which had a bug for this case. Now, moduleMetadata no longer runs the .inputObjects internally, so this bug is no longer relevant.spades.loadReqdPkgs, so a user can turn off loading of packages, and spades.dotInputObjects, so a user can omit running of the .inputObjects function in modules during simInit. These are updated in spadesOptions.runScheduleEventsOnly will extract only the scheduleEvents call; needed for options(spades.futureEvents = TRUE)spades.saveFileExtensions which allows users to use the outputs(sim) mechanism for saving, for file extensions that are not already supported by .saveFileExtensions()reproducible v2.0.5 or higher;quickPlot v1.0.2 or higher;googledrive from Suggests.Plots when not specifying fn, but usePlot = FALSE\dontrun or \donttest were stale; these have been updatedsaveFiles bugfix: multiple objects names can now be passed to .saveOutputs module parameters.remoteFileSize(), updateList(). These will be removed by mid-2023."\b" in the message, and it will occur on same line as previous messagePlots now appends the filename any file saved during Plots to the outputs slot of the sim, i.e., it will show up in outputs(sim)logPath is now a function that points to a sub-folder of file.path(outputPath(sim), "log")defineEvent is a new function that allows a different way of specifying events than the doEvent function. This is not yet being used in the module templates, so does not appear with newModule.spades can now run correctly, with "incomplete" modules that don't have metadata or even a module file. Now, a "module" will work with simInit and spades if a doEvent.XXX exists somewhere e.g., in the .GlobalEnv. spades will find it through inheritance and no longer complain if specific structures are absent. This may make it easier to learn how to use SpaDES as it mimics a more normal user experience where functions are all in the .GlobalEnv.spades.DTthreads to limit the number of threads used by data.table (default 1).
Users can override this default if needed; modules can setDTthreads() as needed,
but should restore the original value on.exit.saveSimList() and loadSimList() accept .qs or .rds filesspades and simInit now force UTF-8 encoding; this is reset on.exit. If a module needs a different character encoding, then it can be set within the module code..studyAreaName parameter added to default module metadata when using newModule.spades.scratchPath, to be used for e.g., temporary raster files and temporary SpaDES recovery mode objects.rasterTmpDir has changed to be a subdirectory of scratchPath.
rasterPath will be deprecated in a future release.terraTmpDir set as a subdirectory of scratchPath.stop.README.md instead of README.txt in new modules.RandomFields dependency, as that package is no longer maintained;NLMR to Suggests to provide random landscape generation capabilities previously provided by RandomFields.memoryUse was not correctly handling timezones; if the system call to get time stamps was in a different timezone compared to the internal SpaDES event queue, then the memory stamps were not correctly associated with the correct events.data.table objects using loadSimList().inputObjects to correctly capture objects that were assigned to mod$xxx.simList objects where changes to functions appeared to be undetected, and so a Cache call would return a stale module with function code from the Cached simList, which was incorrect.options(spades.scratchPath) (see above).objSize could have infinite recursion problem if there are simList objects inside simList objects. Fixed with new reproducible::objSize, which uses lobstr::obj_size.PlotssaveFiles related to data.table assignment and use in outputs(sim)paramCheckOtherMods to deal with call parametersSpaDES modules can now be R packages.
The simplest way to convert a module to a package is using the new function convertToPackage.
Benefits of doing this are so that a SpaDES module can benefit from the infrastructure of an R package (e.g., devtools::document, devtools::check, setting up Continuous Integration systems etc.).
Any documentation written using #' i.e., roxygen2 will be copied immediately above the function where it was sitting.newModule now correctly places the SpaDES.core package dependency in the reqdPkgs element of the metadata, instead of # SpaDES.core. It will put the full GitHub reference if SpaDES.core was installed directly from GitHub.qs package: either qsave or qread converts data.table objects to list objects. loadSimList has a work around internally to convert these objects back to data.table, if the metadata indicate that the objects should be data.table objects.paramCheckOtherMods.
Can be used within a module to assert that a parameter has the same value as the same parameter in other modules.
This is therefore a check of a parameter that might be considered a .global and passed within simInit(..., params = list(.globals = list(someParam = "someValue"))), but the user did not do that.spades messaging when e.g., debug = 1 can correctly accommodate nested spades calls, i.e., a SpaDES module calling spades internally.newModule now puts SpaDES.core dependency in the correct reqdPkgs instead of # SpaDES.core metadata element.plots instead of .plotInitialTime, Plots will check whether .plotInitialTime is actually set in the module metadata first.
Only if it is there, will it evaluate its value. Currently, modules get default values for .plotInitialTime even if the module developer did not include it in the module metadata.debug arg of spades is set to an event type that is also in the core modules (e.g., save, load), such as "init"Cache-ing of a simList, when quick is a character vector, errored. Now fixed.moduleMetadata incorrectly dropped defineModuleListItems under certain signatures.moduleCoverage has been rewritten to estimate code coverage of a module using covr package.P now has a replacement method. So, to update a parameter within a module (where it is namespaced, i.e., don't have to specify the module name): P(sim, "paramName") <- 1. If using this outside a module, then module (3rd argument) will have to be specified.P argument order changed to accommodate the fact that namespacing is used to detect module name: the user does not need to supply module, so it should not be second. This is for the normal P method and the new replace method above: it is now P(sim, param, module); there are attempts to capture errors (i.e., parameter supplied that matches a module, but not a parameter; vice versa) and give a warning for user to change code. This may have little downstream effect as all known cases use the P(sim)$paramName, which will still work fine, instead of P(sim, "paramName").Plots does a better job with RasterStack objects plotted to screen without ggplot2.isFALSE: use base::isFALSE nowPlots can now omit the data argument; just use the named arguments in ...defineParameter now allows multi-line desc or multiple strings; paste is no longer needed for long descmoduleCodeFiles a new function that identifies all the code files in a collection of modules.globals functionality is modified. If a user specifies a .globals in the parameters object (passed into simInit), then all identical parameters in all modules will be overridden with these .global valuesdefineParameter, expectsInput and createsOuptut can all now have multi-line desc, without needing to use paste or paste0. Extraneous spaces and carriage returns will all be stripped. This can either be using a single multi-line quote or via multiple lines, each with its own "".simList is no longer "locked" with lockBinding. It is already hidden in sim$.mods, and since sim$.mods can be modified, this was a weak caution against user modification. Further, for moduleCoverage, the module environment needed to be unlocked, which is not allowed by CRAN..inputObjects was cached (via setting useCache = '.inputObjects' parameter), it was "too sensitive". Changes to any module's parameters, not just the current module, would cause rerun of .inputObjects. Now it correctly identifies parameter changes only in the current module. THIS WILL CAUSE some existing caches to trigger a rerun once; after this, it will be less sensitiverestartSpades did not correctly deal with objects that did not yet exist prior to the event. Fixed with: 24b9cd12973aa81a9a4923a02225e095fa28f77a.restartSpades was losing the previous completed events list. This has been fixed; it is now kept after restartSpadesdata only (e.g. quickPlot::Plot-like behaviour)simInitAndSpades now has .plots arg to match spadesPlots function can be used like Plot, but with types specified.
The devices to save on disk will have some different behaviours to the screen representation, since "wiping" an individual plot on a device doesn't exist for a file device.Plots function that will produce zero to 4 types of items that are relevant for plotting: 1) Visual on screen, 2) The plot object saved to disk, 3) The raw data that went into the plot and 4) The plot as one or more image files, e.g., .png or .pdf via ggsavespades now accepts an events argument, which will limit the events that are run to those specified in the argument. This seems to be most useful for the init case, e.g., spades(sim, events = "init"). See ?spades.simInit now is prefixed with Sys.time() and "simInit"spades is simplified to take up fewer characters: INFO:: has been removedsimInit now checks for minimum # SpaDES.core of SpaDES.core needed in a module and stops if it fails, giving instructions how to upgrade.spatialExtent, as they are not used by the spades algorithmsanyPlotting to test whether plotting of one form or another should occurspades call is now more informative, including module name (by default shortened -- can be changed with options("spades.messagingNumCharsModule")))defineParameter can now accept a vector of "class", so a parameter can be more than one class.
Presumably this should generally not be used, but a good reason could be, say, c("numeric", "function"), where the use can pass either a numeric or a function that would calculate that numeric.simFile to generate file names for use with e.g., saveSimListzipSimList is now exportedspades will now attempt to load reqdPkgs, which is already done in simInit.
In cases where simInit was not run, e.g., Cache(simInitAndSpades, ..., events = "init"), then modules will not have access to packages.
For cases where simInit was called, then this should add very little overhead.saveSimList will now convert file-backed Raster* class objects to memory if fileBackend = 0.
Previously, it left them as is (on disk if on disk, in memory if in memory).time(sim) in the case where it is equal to or after end(sim). Previously, this would not run any events if time(sim) >= end(sim) && events(sim)[[1]] < time(sim)..seed parameter for modules (#163)defineParameter was throwing is.na(default) warning when a parameter was not an atomic.spades.useRequire = FALSE (#141)sim; user informed with a warningtry() with communities() to skip tests on systems without igraph GLPK support.RandomFields being unavailable.spades.futureEvents option. If set to TRUE, spades will run module events in a "future" (see future package), if they do not produce outputs for other modules.use_gha() and corresponding vignette; #74)newProject creates Rstudio .Rproj file if invoked in RstudiopaddedFloatToChar to reproducible; but re-exported here, so still usable..seed which is a named list where names are the events and the elements are the seed with which to run the event. During doEvent, SpaDES.core will now set.seed(P(sim)$.seed[[currentEvent]]) and reset to random number stream afterwards.dplyr, lubridate, R.utils, tools, backports and rlang from dependenciestcltk to Suggestsdevtools, microbenchmark from Suggestsall.equal(..., check.environment = FALSE) for internal testingpkgDeps example for new # SpaDES.core of Requiredesc argument in defineParameter, expectsInput, and createsOutput can now have extraneous spaces and End-of-Line characters.
This means that they can now be written more easily with a single set of quotes, without needing paste.
The accessor functions, moduleParams, moduleInputs, and moduleOutputs all will strip extraneous spaces and End-of-Line characters.writeEventInfo() and writeRNGInfo() to write info to file.stringi.. for moduleParams etc. This is more accurate.Par is now an activeBinding (similar to mod) pointing to P(sim); this allows for tab autocomplete to function correctly.moduleParams(), moduleInputs(), moduleOutputs(). These are now used in default .Rmd template.memoryUse functionalitysim is now created at .pkgEnv$.sim at the start of spades call, rather than on.exit; failures due to "out of memory" were not completing the on.exitnewModule() sets useGitHub = TRUE by default).usethis to Suggests for use with GitHub ActionsFilenames function coming from reproducible packageoptions('spades.recoverMode') was creating temp folders every event and not removing them; now it does.options('spades.recoveryMode' = 0) may further helpreproduciblesimInit, e.g., simInit(times = list(start = "test")) now fails because times must be a list of 2 numeric objectsmessage instead of a mixture of message, cat and print.
This allows for easier suppressing of messaging, e.g., via suppressMessages.
This was requested in a downstream package, SpaDES.experiment that was submitted to CRAN but rejected due to the now former inability to suppress messages.restartR saves simulation objects using qs::qsave() which is faster and creates smaller file sizes.codetools, future, httr, logging, and tcltkarchivistqs now used for improved object serialization to disk.objSizeInclEnviros and removed
objectSynonyms caused a breakage under some conditions related to recovering a module from Cache.print and cat statements to message to allow use of suppressMessages, as recommended by CRANlogging package, invoked by setting debug argument in spades function call to a list(...). ?spades describes detailsrestartR minor bug fixesDEoptim, future.apply, Matrix, parallel, pryr, purrr, and rgenoud, which are no longer required.
See "deprecated" info below.whisker to Imports to facilitate module file templating (#100)future is installed.
See new vignette iv-advanced and ?memoryUse.restartR.
Restarts R mid-stream to deal with apparent memory leaks in R.
In our experience with large projects that have long time horizons, there appears to be a memory leak at a low level in R (identified here: https://github.com/r-lib/fastmap).
This has prevented projects from running to completion. Without diagnosing the root cause of the memory inflation, we have noticed that interrupting a simulation, saving the simList, restarting R, resets the memory consumption back to levels near the start of a simulation.
The new functionality allows a user who is hitting this memory leak issue to restart R as a work around.
See ?restartR for instructions.newProject to initialize a SpaDES project with subdirectories cache/, inputs/, modules/, and outputs/, and setPaths() accordingly.newModule() now uses open = interactive() as default to prevent files being left open during tests.experiment(), experiment2(), and POM() have been moved to the SpaDES.experiment packageSpaDES.core.
Too many dependency packages are not maintaining their backwards compatibility.backports to Imports for R-oldrel supportgoogledrive dependency (this functionality moved to reproducible)P, params, and parameters, thanks to Louis-Etienne Robert.objSize.simList method with 2 new arguments from reproducible package.robustDigest method for simList class objects now does only includes parameters that are listed within the module metadata, if Cache or .robustDigest is called within a module. This means that changes to parameter values in "other" modules will not affect the Caching of "the current" module.outputObjectNames will extract just the object names of all outputObjects across modulesrestartSpades and its associated options(spades.recoveryMode = 1), the new default, which is still experimental. Its purpose is to be able to restart a simulation in the case of an error or interruption.mod is now an active binding to sim[[currentModule(sim)]]$.objects (move from sim[[currentModule(sim)]]) and its parent environment is emptyenv(). This should cause no changes to users who use mod$..., but it will cause a change if user was calling objects directly via sim[[currentModule(sim)]]$.... This change is to separate the function enclosing environments and object enclosing environments, which should be different.sim@completed is now an environment instead of a list. Of the three event queues, this one can become the largest. The list would get increasingly slow as the number of completed events increased. There should be no user visible changes when using completed(sim)spades.debug is now set to 1
spades.recoveryMode is new and set to 1 (i.e., the current event will be kept at its initial state)
simInit especially in some weird cases of childModules.reqdPkgs not being loaded when only listed in child modules. Fixed in 5cd79ac95bc8d190e954313f125928458b0108d2.new vignette on caching SpaDES simulations moved from SpaDES package.
simList environment now has emptyenv() as its parent.env. The biggest user-facing changes are:
envir(sim) (unusual, but may occur) won't find objects in the .GlobalEnv;parent.env in which they are defined (little know fact identified here: http://adv-r.had.co.nz/memory.html#gc identified as a possible source of memory leaks).module's function environment in the simList now has its parent asNamespace("SpaDES.core") instead of the envir(sim) (as mentioned above), i.e,. parent.env(sim[[currentModule(sim)]]) is asNamespace("SpaDES.core"). The main user-noticeable changes of this are that module functions will not accidentally find objects in the simList unless they are actually passed in explicitly as arguments.
New active binding, mod that works as a module-specific variable, similar to a private object, i.e., mod$a is a local object inside the module that persists across events. It is a pointer to sim[[currentModule(sim)]]$a
New function scheduleConditionalEvent, which allows an event to be scheduled based on a condition. Still experimental.
An experimental new function and feature, objectSynonyms, which will create active bindings of two names to a single object
User can now specify modulePath as a character vector, e.g., simInit(..., paths = list(modulePath = c(".", "test"))). This means that a user can organize the modules in different locations.
modulePath now has a new argument, module, where user can specify (a) specific module(s)'s path. Modifications were implemented to dataPath to utilize this new feature
simInit and spades now call setPaths(paths) or setPaths(sim$paths), unsetting them on.exit internally to make the paths used for functions e.g., reproducible::Cache to use the correct path
under-the-hood speed improvements for the DES (about 20% faster) -- 38 microseconds per event under ideal conditions
improved default path settings in .inputObjects (#83)
following reproducible package updates, now uses data.table::setattr internally to avoid copying of
objects (this may have very little/no effect on simList objects)
suppliedElsewhere has a new argument, returnWhere, a logical which will cause a logical of length 3 to be returned, indicating in which of the 3 other places the object may have been supplied, instead of length 1, still the default.
data.table v1.12.0 (#85, @mattdowle)Copy (error existed because function inheritance persisted even though the location of the function was moved)igraphRandomFields >= 3.3.4archivist and devtools added to Suggests because they are used in vignettesreproducibleRandomFields to Suggests, as it is in the Suggests of SpaDES.tools and used in examples/tests.options("spades.saveSimOnExit" = TRUE). This will save the state of the simList to an object as SpaDES.core:::.pkgEnv$.sim, with a message, if there is a hard exist. There is virtually no computational cost to this, as the object is already in RAM.simList internals changed. It now inherits from environment. Amongst other things, this means that tab autocomplete in RStudio now works for objects in the simList. Also, we removed several associated methods, $, [[, ls, ls.str, objects, as the defaults for environments work correctly with the simList nowdebug arg in spades call can now take numeric, currently 1 or 2, giving a few pre-packaged informative messaging each eventelapsedTime which gives a summary of the clock time used by each module or eventinputObjects(sim) returns the inputObjects data.frame.citation replaces utils::citation with an S4 generic. If package arg is a character, it dispatches utils::citation; if a simList, it gives the citation for the module(s)downloadModule() now prints the module # SpaDES.core downloaded (#77).inputObjects() name conflict (internal .inputObjects renamed to ._inputObjectsDF; .outputObjects renamed to ._outputObjectsDF).inputObjects evaluated based on module load order (#72).robustDigest fix for simList objects -- needed to omit ._startClockTime and .timestampsp from importsreproducible (>=0.2.2)new option spades.useRequire: a logical which causes simInit to load packages with Require or require. Lower case is generally faster, but will not handle the case of uninstalled packages, so should only be used once all packages are installed.
spades.keepCompleted: a logical which causes spades() to keep (TRUE) or not keep a record of completed events. Keeping track of completed events when they are many (>1e5) gets slow enough that it may be worth turning it off.more robust and faster tests are used now, care of helper functions in tests
all.equal.simList now removes all time dependent attributes, e.g., ._startClockTime and .timestamp
speed enhancements for Discrete Event Simulator; now overhead is 1.3 seconds for 5000 events or, per event, 260 microseconds (185 microseconds if options("spades.keepCompleted" = FALSE)
Improvements to caching of functions with simList objects:
simList in the arguments would erroneously return cached copies of functions. These now are copied through from argument simList, rather than cached simList. This means that changes to the function definitions in a module will persist (e.g., debugging via browser() will work correctly)simList in arguments that return a simList will now do a post digest of the output. This will be compared with the predigest, and only those object which changed in the simList will be modified..inputObjects function was incorrect. Fixed.module metadata now in named lists inside depends(sim)
new debugging -- if debug is not FALSE, then any error will trigger a browser() call inside the event function. User can continue (c) or quit (Q) as per normal. c will trigger a reparse and events will continue as scheduled.
introduction of code checking for modules, currently turned on or off by an option spades.moduleCodeChecks, which is TRUE by default. Code checking includes various types:
codetools to check for various code problemsraster::level, raster::scale, quickPlot::Plot)checkCodeEnv on every function inside a modulesim$xxx occurrences in modules, comparing to outputObjects in metadata if used in assignment (i.e., left hand side of assign operator), or comparing to inputObjects if used on the right hand sideinputObjects have default values assigned in the .inputObjects functionoption spades.debug set to TRUE by default, instead of FALSE. This is better for new users.
moduleMetadata argument order changed, so sim is first, more consistent with all other simList accessors.
downloadData has changed dramatically, now it is a wrapper around reproducible::prepInputs which does more checking.
extractURL will extract the sourceURL from metadata, given an object name.
makeMemoiseable and unmakeMemoisable, new methods, each the inverse of the other, to deal with imperfect memoised returns under some cases of simList.
new option, spades.keepCompleted, TRUE by default, which can be useful for dramatically speeding up the DES when there are many (>10,000) events.
fileExt -- use tools::file_ext insteaddata.table changes (@mattdowle, #64).start and end.newModule template modified slightly based on workshop feedback.setPaths now only sets the directories that are passed into it.all.equal.simList method strips a small number of attributes that are used internally that create false failures.tools, pryr.rgeos, RCurl and googledrive.uses reproducible::Require instead of SpaDES.core::loadPackages to load required packages. Currently, does not use SpaDES.core control for packages, but does use installing (from CRAN or GitHub), and loading (via require). This means a module can indicate a GitHub package, e.g,. achubaty/amc@development
environments in modules are now as follows:
[email protected]$<moduleName>, and it is a is a child of [email protected]. Functions can be found in this environment, but prefixing functions is not necessary, because modules functions are within this environment already.[email protected] is a child of SpaDES.corescoping from within a function that is defined in a module is thus:
[email protected]$<moduleName> --> [email protected] --> SpaDES.core --> all imported packages including base --> .GlobalEnv --> search()speed improvements:
data.table objects. For small objects (e.g., the event queue) that have fewer than 200 objects, lists are faster. Accessors (e.g., events(sim), completed(sim)) of the event queues still show data.table objects, but these are made on the fly..parseModule and .parseModuePartial now put their parsed content into a temporary environment ([email protected]$.parsedFiles$<Full Filename>) during the simInit, which gets re-used. Previously, files were parsed multiple times in a given simInit call. Several functions now have envir argument to pass this through (including module# SpaDES.core, packages, checkParams)parsing of modules is now more intelligent, allowing for modules to contain functions (the current norm) and but they can also create objects at the module level. These can use the sim object in their definition. These objects can, for example, be used to help define parameters, for example, e.g., startSimPlus1 <- start(sim) + 1 can be defined in the module and used in defineModule
remove grDevices from Imports as it was not used (#1)
remove chron and CircStats dependencies
remove functions dwrpnorm2 and move to package SpaDES.tools
remove unused function F() due to conflicts with F/FALSE.
improved download of module data: added new quickCheck argument
improved download of modules: use fuzzy matching
new option: spades.switchPkgNamespaces which allows the user to turn off the SpaDES feature that loads and unloads libraries specific to each module. While useful, it slows down computations, in some cases, by a lot.
bug fixes:
zipModule that omitted the checksum file from being included when data = FALSE (#3).inputObjects functions was evaluating outputObjects instead of inputObjects. Now corrected.If .inputObjects contains arguments other than just sim, these will be evaluated as function inputs by the Cache mechanism (via .useCache), therefore correctly assessing when those inputs changed, e.g., if they are files and the arg is wrapped in asPath, then any change to the underlying file will cause a re-cache. e.g., .inputObjects <- function(sim, importantFile = asPath(file.path(inputPath(sim), "theFile.rdata"))) { ... }
default debug option in spades() now uses the package option spades.debug and default is set to FALSE (#5)
various other speed improvements and bug fixes
convert P to a function, rather than S4 generic and method, for speed.
@importFrom only used functions from utils due to name conflicts with raster::stack and utils::stack
new function remoteFileSize to check the size of remote files
new namespaced function dataPath will return file.path(modulePath(sim), currentModule(sim), "data"), which will return a different path, depending on which module it is placed inside.
add crayon to Imports -- now messages are more colour-coded;
bug fix in 'inputs' for the case of loading objects from the global environment, either from the same object to the same object, or from different global objects overwriting on the same simList object;
A new package, which takes all core DES functionality out of the SpaDES package:
?SpaDES.core for an overviewvarious speed improvements and bug fixes