---
title: "Tracking the nlmixr2est and rxode2 versions of a saved fit"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Tracking the nlmixr2est and rxode2 versions of a saved fit}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

A saved `nlmixr2` fit is the result of two packages working together:
`nlmixr2est` estimates the model, and `rxode2` compiles and solves it.  A change
in either package can, in principle, change the fit.  Because a saved fit is
meant to be reloaded later -- possibly months later, on a different machine, with
newer packages installed -- `nlmixr2save` records which versions produced it and
tells you when they no longer match.

This behavior is easy to miss, because when the versions *do* match (the common
case) nothing is printed at all.

## What is recorded

When you `saveFit()` a fit (or cache one with `:=`), `nlmixr2save` stores a small
piece of metadata alongside it: the `nlmixr2est` and `rxode2` **versions**, and,
when either package was installed from a remote such as GitHub (via `remotes` or
`pak`), its **commit sha**.  The metadata is embedded in the saved fit's loader
script, so it travels inside the portable `.zip` and survives a load -> save
round-trip (it always records the version that *ran* the fit, not the version
that happened to re-save it).

This *version metadata* is only the package versions -- it does not itself
contain your model or data.  The saved fit as a whole is a different matter: it
does store the model and the data it was fit to (for example in `$origData`,
which is written into the `.zip`); see `vignette("uses")` for what a saved fit
includes.

## What happens on load

### `loadFit()`

When you load a fit and the installed `nlmixr2est` or `rxode2` differs from the
version that produced it, `loadFit()` warns:

```{r, eval = FALSE}
fit2 <- loadFit("fit")
#> Warning: this fit was run with nlmixr2est 6.2.0 (installed 6.3.0)
```

The fit still loads normally -- the warning is only there to explain why a
reloaded fit might not be bit-for-bit identical to one you would get by
re-running today.  `loadFit()` cannot re-run the fit itself, because a saved fit
does not carry the original `nlmixr2()` call.

### The `:=` caching operator

The `:=` operator *does* have the original call, so it can offer to re-run.  When
a cached fit was produced by a different package version:

- In an **interactive** session, you are asked what to do:

  ```{r, eval = FALSE}
  fit := nlmixr2(one.cmt, theo_sd, est = "focei")
  #> The cached fit was run with nlmixr2est 6.2.0 (installed 6.3.0).
  #>
  #> 1: Reload the cached fit as-is
  #> 2: Rerun the fit with the installed packages
  ```

  Choosing **2** re-runs the fit with the currently installed packages and
  refreshes the cache; choosing **1** keeps the cached fit.

- In a **non-interactive** session (scripts, `R CMD build`, CI, rendering a
  document), there is no one to ask, so `:=` loads the cached fit and warns:

  ```
  #> Warning: the cached fit was run with nlmixr2est 6.2.0 (installed 6.3.0);
  #> loading the cached fit
  ```

If both packages changed, the message names both, e.g.
`nlmixr2est 6.2.0 (installed 6.3.0); rxode2 5.1.3 (installed 6.0.0)`.

## Turning the check on or off

The version check is controlled by a single option, `nlmixr2save.checkVersion`,
which defaults to `TRUE`:

```{r, eval = FALSE}
# Silence version-mismatch warnings and skip the interactive rerun prompt
options(nlmixr2save.checkVersion = FALSE)

# Turn the check back on (the default)
options(nlmixr2save.checkVersion = TRUE)
```

### Always load, never rerun

If you would rather *always* load the cached fit -- never be interactively asked
to rerun, and never warned -- set the option to `FALSE`.  With
`nlmixr2save.checkVersion = FALSE`, an interactive `:=` behaves exactly like a
non-interactive one used to, except quietly: it loads the cached fit and moves
on, no matter which `nlmixr2est` or `rxode2` version is installed.

```{r, eval = FALSE}
options(nlmixr2save.checkVersion = FALSE)

# loads the cached fit.zip as-is, with no prompt and no warning
fit := nlmixr2(one.cmt, theo_sd, est = "focei")
```

This is the setting to reach for when you have deliberately committed a cache
and want it to load unconditionally -- for example in a rendered report or in
CI, or simply because you prefer to decide when to rerun yourself.

`loadFit()` also takes a per-call `checkVersion` argument, which defaults to the
option, so you can override it for a single load without changing the global
setting:

```{r, eval = FALSE}
fit2 <- loadFit("fit", checkVersion = FALSE) # load this one quietly
```

A common place to set the option is at the top of a script or vignette that
deliberately ships a committed cache and does not want version warnings:

```{r, eval = FALSE}
options(nlmixr2save.checkVersion = FALSE)
```

### Loading without checking the model/data hash

By default, `:=` verifies a cached fit against an `md5`/hash of the current
model, data, and arguments, and refits when they differ.  That check is separate
from the version check described above, and it has its own option,
`nlmixr2save.check`:

```{r, eval = FALSE}
# Load the cache file if it exists, WITHOUT checking the model/data/argument
# md5 -- the cache is trusted and only regenerated when it is missing
options(nlmixr2save.check = FALSE)

fit := nlmixr2(one.cmt, theo_sd, est = "focei")
```

With `nlmixr2save.check = FALSE` (trusted-cache mode, described in
`vignette("uses")`), `:=` simply loads a committed cache whenever the file
exists, ignoring any `md5` differences in the model, data, or arguments -- and,
because it does no comparison at all, it also never prompts or warns about
package versions.  In other words it subsumes `nlmixr2save.checkVersion = FALSE`
on the `:=` path.  To force a rerun in this mode, delete the cache with
`nlmixr2saveInvalidate()` (or remove the file).

Use the two options together to pick exactly what is (and is not) checked:

| `nlmixr2save.check` | `nlmixr2save.checkVersion` | behavior of `:=` on a cache hit |
|:---|:---|:---|
| `TRUE` (default) | `TRUE` (default) | refit if the model/data/args `md5` changed; otherwise warn/prompt if the package version changed |
| `TRUE` | `FALSE` | refit if the `md5` changed; ignore package versions |
| `FALSE` | (ignored) | always load the cache file if it exists; check nothing |

The `nlmixr2save.checkVersion` option is the one to reach for when you are using
ordinary (checked) caching or `loadFit()` and only want to control the version
comparison; `nlmixr2save.check` is the one that also turns off the model/data
`md5` check.

## Backward compatibility

Fits saved by versions of `nlmixr2save` that predate this feature carry no
version metadata.  Loading them is completely silent: with nothing to compare
against, `nlmixr2save` never warns and never prompts.
