Two forms accepted inside nlmixr2targets
The native nlmixr2 DSL syntax for a compartment initial value is
cmt(0) <- value inside a model({...})
block. Inside tar_nlmixr() and
tar_nlmixr_multimodel() you may also write
cmt(initial) <- value, which
nlmixr2targets translates back to
cmt(0) <- value before nlmixr2 ever sees the
model. The cmt(initial) form is a workaround that
exists only for use through nlmixr2targets; it is
not a form that nlmixr2 itself understands, so it will
fail if you call nlmixr2est::nlmixr() directly on a model
that still contains cmt(initial).
Both forms below produce the same fit when routed through
tar_nlmixr():
# Form 1: native nlmixr2 DSL syntax (works in nlmixr2 directly and
# inside nlmixr2targets)
model({
d / dt(central) <- -kel * central
central(0) <- 0 # initial value at time 0
cp <- central / vc
cp ~ add(cpaddSd)
})
# Form 2: `cmt(initial) <- value` -- a `nlmixr2targets`-only
# workaround. Use this only when something prevents the natural
# `central(0) <- 0` form from being walked by `targets` (see the
# "Known limitation" section below). `nlmixr2targets` rewrites this
# back to `central(0) <- 0` before passing the model to nlmixr2.
model({
d / dt(central) <- -kel * central
# nlmixr2targets-only; not understood by bare nlmixr2
central(initial) <- 0
cp <- central / vc
cp ~ add(cpaddSd)
})What tar_nlmixr() does behind the scenes
targets walks every function in the user’s environment
with codetools::findGlobals() and rejects replacement-style
assignments whose target is not a symbol – which is exactly what
central(0) <- 0 looks like. To allow the natural DSL
syntax, tar_nlmixr() walks the captured object
expression at construction time and rewrites every
cmt(0) <- value inside model({...}) to
cmt(initial) <- value. The rewrite is reversed at
runtime before the model is handed to nlmixr2, so the fit you get is
identical.
The rewrite mutates the function binding in place in the environment where it was found, so if you later print
body(my_model)at the REPL after calling tar_nlmixr(), you will see
central(initial) <- 0 rather than the originally-typed
central(0) <- 0. This is the only user-visible side
effect.
Pipe forms
Both pipe forms below also work, including with
cmt(0):
tar_nlmixr(
name = m1,
object = pheno |> model({
central(0) <- 0
}, append = TRUE),
data = nlmixr2data::pheno_sd,
est = "saem"
)
tar_nlmixr(
name = m2,
object = pheno |> ini(cpaddSd = 0.3),
data = nlmixr2data::pheno_sd,
est = "saem"
)A runtime delayed-eval wrapper restores cmt(0) before
nlmixr2’s pipe handlers see the parsed body.
Known limitation: env functions never routed through
tar_nlmixr()
targets walks every function in the
analysed environment, not just the ones referenced by a target. If you
define
sketch <- function() {
ini({
a <- 1
})
model({
central(0) <- 0
})
}in the same environment but never pass sketch to
tar_nlmixr(), targets’ static analysis will
still try to walk its body and will report a
codetools::findGlobals() error. The workaround is
either:
- pass the function through
tar_nlmixr()(the construction-time rewriter cleans it up), or - write the manual
central(initial) <- 0form by hand. Becausecmt(initial)is only meaningful whennlmixr2targetsis the one parsing the model, a function written this way is not usable in a bare nlmixr2 workflow – it only fits if it is routed throughtar_nlmixr()/tar_nlmixr_multimodel().
This case is pinned by a test in
tests/testthat/test-tar_nlmixr.R.
Summary
-
cmt(0) <- valueis the native nlmixr2 DSL syntax. Prefer it. -
cmt(initial) <- valueis anlmixr2targets-only workaround:nlmixr2targetsrewrites it back tocmt(0) <- valuebefore nlmixr2 sees the model. Bare nlmixr2 does not understandcmt(initial); a model written that way only fits when routed throughtar_nlmixr()/tar_nlmixr_multimodel(). - Inside
tar_nlmixr(), the package quietly rewritescmt(0)forms in env tocmt(initial)(to survivetargets’ static analysis) and back again at evaluation time. - After
tar_nlmixr(),body(my_model)will show the rewritten form; this is cosmetic and reversed at runtime. - Pipe forms work too via a runtime wrapper.
- Functions in env that contain
cmt(0)but are never routed throughtar_nlmixr()need the manualcmt(initial)workaround – with the caveat that they then only fit throughnlmixr2targets.