Packaging odin models

You should not use odin() within a package, because that would cause the model to compile each time you use it, rather than when the package builds. It may also cause issues when trying to use the model in parallel (e.g., with the parallel package), and will require all users of your code to have a full C++ toolchain. Instead you should use odin_package() which will generate appropriate code for you.

This vignette is based heavily on the packaging vignette in dust2 (vignette("packaging", package = "dust")), as odin uses dust2’s machinery for packaging.

library(odin2)

To package odin code, put your odin files in inst/odin/ within a package and run odin_package() on the package’s root directory. You can have several odin systems within a single package.

A skeleton package might contain:

#> .
#> ├── DESCRIPTION
#> ├── NAMESPACE
#> └── inst
#>     └── odin
#>         └── sir.R

This is the normal R package skeleton, though missing R/ and src/ directories (for now). The DESCRIPTION file contains

Package: example
Title: Example Odin in a Package
Version: 0.0.1
Imports: dust2
LinkingTo: cpp11, dust2, monty
Authors@R: c(person('A', 'Person', role = c('aut', 'cre'),
                     email = '[email protected]'))
License: CC0

The important things here are:

Our NAMESPACE file contains:

useDynLib('example', .registration = TRUE)

This useDynLib call to your package in the NAMESPACE file is required for R to load the compiled code into the package.

The file in inst/odin is the odin code from vignette("odin2"), with sir.R containing

p_IR <- 1 - exp(-gamma * dt)
N <- parameter(1000)

p_SI <- 1 - exp(-(beta * I / N * dt))
n_SI <- Binomial(S, p_SI)
n_IR <- Binomial(I, p_IR)

update(S) <- S - n_SI
update(I) <- I + n_SI - n_IR
update(R) <- R + n_IR

initial(S) <- N - I0
initial(I) <- I0
initial(R) <- 0

beta <- parameter(0.2)
gamma <- parameter(0.1)
I0 <- parameter(10)

There can be as many of these files as you want within the directory inst/odin.

To prepare the package, run odin_package():

odin_package(path)
#> ℹ Found 1 odin code file in 'inst/odin'
#> ✔ Wrote 'inst/dust/sir.cpp'
#> ℹ Working in package 'example' at '/tmp/RtmpupXWjH/filecab6cf98624'
#> ℹ Found 1 system
#> ✔ Wrote 'src/sir.cpp'
#> ✔ Wrote 'R/dust.R'
#> ✔ Wrote 'src/Makevars'
#> ℹ 12 functions decorated with [[cpp11::register]]
#> ✔ generated file 'cpp11.R'
#> ✔ generated file 'cpp11.cpp'

The directory structure now has more files:

#> .
#> ├── DESCRIPTION
#> ├── NAMESPACE
#> ├── R
#> │   ├── cpp11.R
#> │   └── dust.R
#> ├── inst
#> │   ├── dust
#> │   │   └── sir.cpp
#> │   └── odin
#> │       └── sir.R
#> └── src
#>     ├── Makevars
#>     ├── cpp11.cpp
#>     └── sir.cpp

None of these files should be edited:

Your package can include as much R code as you want, and can be developed like any other R package. But any time you change the code in inst/odin you should rerun odin_package().