Skip to contents

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.

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:

#> Error in get(paste0(generic, ".", class), envir = get_method_env()) : 
#>   object 'type_sum.accel' not found
#> .
#> ├── 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 = 'person@example.com'))
License: CC0

The important things here are:

  • the package name (Package). We’re using example, and names with a dot may not work as expected
  • including cpp11, dust2 and monty in LinkingTo, which allows package compilation to find their respective header files

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/Rtmpj3YXeY/file210b794a86e5'
#>  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:

  • inst/dust/sir.cpp contains the C++ code generated by odin, used by dust2
  • src/sir.cpp is the full code generated in turn by dust
  • src/dust.R contains the R definitions of the dust system
  • src/cpp11.cpp and R/cpp11.R are files generated by cpp11

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().