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:
#> .
#> ├── 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:
Package
). We’re using
example
, and names with a dot may not work as expectedcpp11
,
dust2
and monty
in LinkingTo
,
which allows package compilation to find their respective header
filesOur 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/RtmpLNjZYX/fileccc25fe441f'
#> ℹ 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
dustsrc/dust.R
contains the R definitions of the dust
systemsrc/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()
.