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 usingexample
, and names with a dot may not work as expected - including
cpp11
,dust2
andmonty
inLinkingTo
, 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 bydust2
-
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
andR/cpp11.R
are files generated bycpp11
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()
.