4  Introduction to SpaDES Modules

Author

Eliot McIntire

Published

June 21, 2024

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

Code
# create some data
x <- rnorm(10)
y <- x + rnorm(10)
# fit a linear model
model <- lm(y ~ x)

# plot the fit
plot(model)

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.

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
Require::Require(c("reproducible", "SpaDES.core (>= 2.1.5)"), 
                 repos = c("https://predictiveecology.r-universe.dev", getOption("repos")))

# make a module
modulePath <- "~/SpaDES_book/NewModuleIntro/NewModule"
SpaDES.core::newModule(name = "My_linear_model", 
                       path = modulePath, 
                       open = FALSE,
                       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.

Code
# make a module
SpaDES.core::newModule(name = "My_linear_model", 
                       path = modulePath, open = FALSE,
                       events = list(
                         init = {
                           x <- rnorm(10)
                           y <- x + rnorm(10)
                           # fit a linear model
                           sim$model <- lm(y ~ x)     #  <--------- change 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.

Code
out <- simInit(modules = "My_linear_model", 
               paths = list(modulePath = modulePath))
out <- spades(out)

We can look at the output:

Code
out$model

We 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 :)

simInit() before spades()

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

Code
SpaDES.core::newModule("visualize", path = modulePath, open = FALSE,
                       events = list(
                         init = {
                           plot(sim$model)
                         }
                       )
)

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

  1. Make a new module. Make it really simple. Run it with simInitAndSpades as in the above examples.

    • Use sim$ for an object that will be outputted.
  2. Make a second new module that uses the sim$ object.

    • Add another new object in sim$
  3. Make a third new module that uses the sim$ object from the 2nd module.

4.4 See also

simList accessors

?simInitAndSpades

?newModule

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)

Require::Require(c("reproducible", "SpaDES.core (>= 2.1.5)"), 
                 repos = c("https://predictiveecology.r-universe.dev", getOption("repos")))

# make a module
modulePath <- "~/SpaDES_book/NewModuleIntro/NewModule"
SpaDES.core::newModule(name = "My_linear_model", 
                       path = modulePath, 
                       open = FALSE,
                       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 = FALSE,
                       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 = FALSE,
                       events = list(
                         init = {
                           plot(sim$model)
                         }
                       )
)

out2 <- simInitAndSpades(module = c("My_linear_model", "visualize"), 
                         paths = list(modulePath = modulePath))

out2$model

completed(out2)

  1. We use the testthat package to automate module and code testing. By default, newModule() creates a tests folder inside the module folder that is designed to be used with testthat , hence the warning if this package has not been installed.↩︎