Keeping fits in a models directory

By default nlmixr2save writes fits into the working directory: fit := nlmixr2(...) caches to ./fit.zip, and saveFit(fit) writes ./fit.zip. In a project with more than a couple of models, that quickly mixes fits in with scripts, reports and data. This article shows how to keep them all in one place – a models/ directory – by default.

Caching := fits in a directory

The := operator reads three options:

Option Default Effect
nlmixr2save.dir "." Directory the cache files live in (created when needed)
nlmixr2save.prefix "" String prepended to the variable name to name the cache file
nlmixr2save.check TRUE Whether a cached fit is checked against the current model, data and arguments

To send every cache to models/, set the directory once:

library(nlmixr2)
library(nlmixr2save)

options(nlmixr2save.dir = "models")

fit := nlmixr2(one.cmt, theo_sd, est = "focei") # fits, writes models/fit.zip
fit := nlmixr2(one.cmt, theo_sd, est = "focei") # loads models/fit.zip

The directory is created the first time something is cached there. Other values assigned with := (simulations, or functions registered with saveFitRandom()) are cached there too, as models/<name>.rds.

Naming the files with a prefix

A prefix keeps caches from different analyses apart in the same directory:

options(nlmixr2save.dir = "models", nlmixr2save.prefix = "pk-")

fit := nlmixr2(one.cmt, theo_sd, est = "focei") # models/pk-fit.zip

The prefix applies only to the file name on disk. The variable is still fit, and inside the archive the fit is stored under its bare name, so models/pk-fit.zip is an ordinary fit archive that loadFit() reads like any other.

Setting the directory for a whole project

Options last for the R session, so set them where every script and report picks them up.

A project .Rprofile. R runs the .Rprofile in the directory it starts in, so the setting applies to every session in the project:

# .Rprofile
options(nlmixr2save.dir = "models")

The setup chunk of a report. When a report should be self-contained, set the option at the top instead:

```{r setup, include = FALSE}
options(nlmixr2save.dir = "models")
```

Only for part of a script. withr::local_options() or withr::with_options() restore the previous value afterwards:

withr::with_options(list(nlmixr2save.dir = "models/sensitivity"), {
  fitLow  := nlmixr2(one.cmt, theo_sd, est = "focei")
  fitHigh := nlmixr2(one.cmt, theo_sd, est = "saem")
})

Relative paths and rendered reports

A relative nlmixr2save.dir is resolved against the working directory at the time of the :=. An R Markdown or Quarto document is rendered from its own folder, so "models" means reports/models/ for a report in reports/, but models/ at the project root for a script run from there. To point everything at one directory, give an absolute path, for example with the here package:

options(nlmixr2save.dir = here::here("models"))

Saving and loading by path

saveFit() and loadFit() do not read nlmixr2save.dir; give them the path directly. The directory is created if needed:

saveFit(fit, "models/run001")        # writes models/run001.zip

fit2 <- loadFit("models/run001.zip") # or loadFit("models/run001")

The archive holds the fit under its bare name (run001), not the path it was saved to, so models/run001.zip can be moved, renamed or sent to a colleague and still loads from wherever it ends up. loadFit() extracts it to a temporary directory, so loading never writes into your working directory.

Sharing and clearing the directory

nlmixr2saveShare() and nlmixr2saveInvalidate() resolve names through the same options as :=:

options(nlmixr2save.dir = "models", nlmixr2save.prefix = "pk-")

nlmixr2saveShare("fit")  # models/pk-fit.zip -> models/pk-fit-noData.zip
nlmixr2saveInvalidate()  # removes models/pk-* so := refits next time

nlmixr2saveInvalidate() removes every file in nlmixr2save.dir whose name starts with nlmixr2save.prefix. With no prefix set, that is every file in the directory, including fits saved there by hand with saveFit(). If you keep both kinds of fit in models/, give the := caches a prefix, or cache them in a subdirectory such as models/cache.

Keeping the directory under version control

By default := refits when the cached fit no longer matches the model, the data or the arguments, and when the fit was made with a different nlmixr2est or rxode2 version it asks what to do (or, when rendering, warns). For a models/ directory committed to version control, where the cache is the result, turn the check off so a cached fit is loaded whenever it exists:

options(nlmixr2save.dir = "models", nlmixr2save.check = FALSE)

:= then fits only when the file is missing, and compares nothing – not the model, not the data, and not the package versions. Delete the file, or call nlmixr2saveInvalidate(), to refit. See vignette("version-tracking") for how the two checks combine.