4 Introduction to SpaDES Modules
See Barebones R script for the code shown in this chapter
SpaDES modules are comprised of R code that has metadata and that gets put into functions. With this in mind, we can start with some simple R code.
4.1 Some R code to convert
Looking at this code above, we can imagine that the first 3 lines are conceptually different – fit a dummy model – from the last line – visualize it. Let’s put these into 2 “modules”, and then run them in sequence.
4.2 Create a module
We use the SpaDES.core function newModule() to make a new module.
Code
repos <- c("https://predictiveecology.r-universe.dev", getOption("repos"))
options(repos = repos)
if (!require("pak")) install.packages("pak")
pak::pak(c("Require", "SpaDES.project"), ask = FALSE)
library(SpaDES.project)
out <- setupProject(
options = list(repos = repos),
paths = list(projectPath = "~/SpaDES_book/NewModuleIntro")
)Step 1 – move the code to an “init” event. This “init” event is a convention that SpaDES uses. A module must always have an “init” event. This is the only code that is “definitely run” in a module.
Code
library(SpaDES.core) # was installed with setupProject automatically
# make a module
modulePath <- "~/SpaDES_book/NewModuleIntro/NewModule"
SpaDES.core::newModule(name = "My_linear_model",
path = modulePath,
# open = TRUE,
events = list(
init = {
x <- rnorm(10)
y <- x + rnorm(10)
# fit a linear model
model <- lm(y ~ x)
}
))You may get a warning related with the testthat R package not being available. This is fine.1
Keeping objects
For now, we just say, if you want to keep an object, assign it to “sim”. We will explain this in much more detail later. Here, we want to keep the model that we fit. But we don’t care about the x and y. So we assign the model to sim.
How would we run this “init” event? We use simInit, which parses the code (like loading a library or sourcing a function) then spades, which executes it.
We can look at the output:
Code
out$modelWe can also look at “what ran”… or in other words, what was our workflow. Of course, this will just be one “user-created” event (there are 4 “SpaDES-created” events … we may use them later, ignore for now)
Code
completed(out)We have our first “simple workflow”! 1 chunk of code :)
SpaDES.core::simInit() always has to be called before SpaDES.core::spades().
simInit() will initialise the workflow simList object that spades() uses to execute the workflow.
See Chapter 7 for more detail about the simList
4.2.1 Add a second module
Our next step is to divide the pieces into conceptual chunks. Let’s now create a new module for the plot … a “visualization module”.
And now we run both together. We name them both in the module argument. We are using simInitAndSpades which is a shortcut to running the two functions separately.
Code
out2 <- simInitAndSpades(module = c("My_linear_model", "visualize"),
paths = list(modulePath = modulePath))We can look at the output again:
Code
out2$model
completed(out2)This time, there is an extra event that happened. We now have a workflow of 2 events.
4.3 Try on your own
-
Make a new module. Make it really simple. Run it with
simInitAndSpadesas in the above examples.- Use
sim$for an object that will be outputted.
- Use
-
Make a second new module that uses the
sim$object.- Add another new object in
sim$
- Add another new object in
Make a third new module that uses the
sim$object from the 2nd module.
4.4 See also
4.5 Barebones R script
Code
# create some data
x <- rnorm(10)
y <- x + rnorm(10)
# fit a linear model
model <- lm(y ~ x)
# plot the fit
plot(model, ask = FALSE)
repos <- c("https://predictiveecology.r-universe.dev", getOption("repos"))
options(repos = repos)
if (!require("pak")) install.packages("pak")
pak::pak(c("Require", "SpaDES.project"), ask = FALSE)
library(SpaDES.project)
out <- setupProject(
options = list(repos = repos),
paths = list(projectPath = "~/SpaDES_book/NewModuleIntro")
)
library(SpaDES.core) # was installed with setupProject automatically
# make a module
modulePath <- "~/SpaDES_book/NewModuleIntro/NewModule"
SpaDES.core::newModule(name = "My_linear_model",
path = modulePath,
# open = TRUE,
events = list(
init = {
x <- rnorm(10)
y <- x + rnorm(10)
# fit a linear model
model <- lm(y ~ x)
}
))
# make a module
SpaDES.core::newModule(name = "My_linear_model",
path = modulePath, # open = TRUE,
events = list(
init = {
x <- rnorm(10)
y <- x + rnorm(10)
# fit a linear model
sim$model <- lm(y ~ x) # <--------- change to sim$
}
))
out <- simInit(modules = "My_linear_model",
paths = list(modulePath = modulePath))
out <- spades(out)
out$model
completed(out)
SpaDES.core::newModule("visualize", path = modulePath, # open = TRUE,
events = list(
init = {
plot(sim$model, ask = FALSE)
}
)
)
out2 <- simInitAndSpades(module = c("My_linear_model", "visualize"),
paths = list(modulePath = modulePath))
out2$model
completed(out2)We use the
testthatpackage to automate module and code testing. By default,newModule()creates atestsfolder inside the module folder that is designed to be used withtestthat, hence the warning if this package has not been installed.↩︎