From nlmixr2llm
Use this skill when the user is fitting population PK/PD models with the R package `nlmixr2`. Triggers include writing `ini({}) / model({})` model functions, calling `nlmixr2(model, data, est = ...)`, picking between saem / focei / foce / fo / agq / laplace / nlme / posthoc, configuring `saemControl()` / `foceiControl()`, or post-processing a fit with `augPred()`, `vpcPlot()`, `$parFixed`, `$omega`.
How this skill is triggered — by the user, by Claude, or both
Slash command
/nlmixr2llm:nlmixr2The summary Claude sees in its skill listing — used to decide when to auto-load this skill
`nlmixr2` is the open-source R package for nonlinear mixed-effects modeling. Models share their syntax with `rxode2` (function-style `ini({}) / model({})`), the same model can be fit by several estimation engines (saem, focei, laplace, agq, nlme), and the result is a tidy `nlmixr2` fit object that supports VPCs, augmented predictions, and the usual diagnostics.
nlmixr2 is the open-source R package for nonlinear mixed-effects modeling. Models share their syntax with rxode2 (function-style ini({}) / model({})), the same model can be fit by several estimation engines (saem, focei, laplace, agq, nlme), and the result is a tidy nlmixr2 fit object that supports VPCs, augmented predictions, and the usual diagnostics.
Activate whenever the user is:
nlmixr2(...).augPred, vpcPlot, $parFixed, $omega, GOF plots).nonmem2rx / monolix2rx,
followed by babelmixr2::as.nlmixr2(). This can also be refit to
give possibly different results if needed.babelmixr2 (est = "nonmem" / "monolix").library(nlmixr2)
one.compartment <- function() {
ini({
tka <- log(1.57); label("Ka")
tcl <- log(2.72); label("Cl")
tv <- log(31.5); label("V")
eta.ka ~ 0.6
eta.cl ~ 0.3
eta.v ~ 0.1
add.sd <- 0.7
})
model({
ka <- exp(tka + eta.ka)
cl <- exp(tcl + eta.cl)
v <- exp(tv + eta.v)
d/dt(depot) <- -ka * depot
d/dt(center) <- ka * depot - cl / v * center
cp <- center / v
cp ~ add(add.sd)
})
}
fit <- nlmixr2(one.compartment, theo_sd, est = "saem",
saemControl(print = 0))
print(fit)
fit$parFixed # population estimates + SE + %RSE + back-transformed
fit$omega # BSV variance/covariance
augPred(fit) # individual + population predictions, plottable
vpcPlot(fit) # visual predictive check
Always run the example (or its adapted form) and confirm the fit converges and prints sane parameter values before handing it back.
ini({}) / model({}). Hand the function itself (not model()) to nlmixr2() — nlmixr2() instantiates it internally.tka <- log(1.57) in ini, then ka <- exp(tka + eta.ka) in model. Use logit() / expit() for parameters bounded to (0, 1), or the expanded logit(est, low, hi) / expit(est, low, hi) for parameters bounded to (low, hi).~ with a starting variance (e.g. eta.cl ~ 0.3). Off-diagonal blocks: write multiple etas on one line with + and supply a matrix start.model({}) and uses the rxode2 error functions:
cp ~ add(add.sd) — additivecp ~ prop(prop.sd) — proportionalcp ~ add(add.sd) + prop(prop.sd) — combinedcp ~ lnorm(lnorm.sd) — log-normalcp ~ add(add.sd) + boxCox(lambda) - Box-Cox + additivecp ~ add(add.sd) + dt(df) - t-distribution with df degrees of freedomll(cp) ~ likelihood - generalized likelihood for an endpoint| endpointName.cp <- center / v) must appear before they're used and before the residual error line.est= deliberately:
"saem" — robust default for most popPK/popPD problems; doesn't compute Objective function by itself (but can be added with addCwres() or AIC(fit))."focei" — gradient-based, gives Hessian-based SEs, more sensitive to initial estimates and stiff models, but can be used with generalized likelihood."foce" — FOCE without interaction."fo" — first-order; mostly for legacy comparison."laplace" - agq with 1 quadrature point"agq" - More accurate approximation of likelihoods, can be
controlled by nAGQ, but should be low and only be used with a
model with a small number of between subject variability (etas)."nlme" — wraps R's nlme package; fine for simple problems."posthoc" — empirical Bayes only; freezes THETAs/OMEGAs and computes ETAs for the given data. Useful after a fit when you have new individuals.est: saemControl(), foceiControl(), foceControl(), foControl(), laplaceControl(), agqControl() ,nlmeControl(). Set print = 0 for quieter logs in scripts.ID, TIME, EVID, AMT, CMT (or cmt matching compartment names), DV, optional covariates. nlmixr2 also accepts compartment names in CMT rather than integers.The skill is "done" only when the model has been fit, converged, and inspected — not just written:
nlmixr2().print(fit), fit$parFixed (estimates, SE, %RSE, BSV%, shrinkage), fit$omega.augPred(fit) for individual fits or vpcPlot(fit) for predictive performance.fit$parFixed # population params, SE, %RSE, BSV%, shrinkage (data.frame)
fit$omega # BSV variance-covariance
fit$objf # OFV / -2LL
fit$cov # variance-covariance of fixed effects (if available)
fit$shrink # shrinkage by ETA
augPred(fit) # IPRED + PRED + observations
plot(augPred(fit))
vpcPlot(fit, n = 500, show = list(obs_dv = TRUE)) # standard VPC
For residual diagnostics use as.data.frame(fit) to get the per-row table with IPRED, PRED, IWRES, CWRES, ETA*.
model({
# ...
cp <- center / v
effect <- e0 - emax * cp / (ec50 + cp)
cp ~ add(prop.sd) | cp
effect ~ add(eff.sd) | effect
})
Use | endpoint (do NOT use dvid("endpoint")) to bind each error line to a row category in the dataset's DVID column.
| Symptom | Likely cause |
|---|---|
parameter not found at compile | symbol used in model({}) not declared in ini({}), not a compartment, not in the data |
| SAEM runs forever / huge OFV swings | bad initial estimates, especially on the log scale; sanity-check exp(tka) etc. |
| FOCEi fails with Hessian errors | over-parameterized OMEGA, near-zero variance estimate, or model identifiability issue — try fewer ETAs or fix small variances, or try other outerOpt optimizations like foceiControl(outerOpt="bobyqa") for instance |
vpcPlot empty / wrong | residual error not specified, or dvid mismatched between model and data |
augPred flat | dosing into wrong compartment, or cmt= in data doesn't match d/dt(name) |
Output looks fine but $parFixed SEs are NA | SAEM doesn't compute them; refit with FOCEi or call addCwres() / nlmixr2Est post-processing |
inst/syntax-functions.csv) and the nlmixr2 vignettes.library(nlmixr2) and a real dataset.label() on each THETA so the printout is readable.vignettes/running_nlmixr.Rmd — canonical introvignettes/multiple-endpoints.Rmd — multi-endpoint specificationvignettes/residualErrors.Rmd — residual error model referencevignettes/addingCovariances.Rmd — OMEGA covariance blocksvignettes/modelPiping.Rmd — model composition / pipingvignettes/censoring.Rmd — BLQ / censored observationsvignettes/broom.Rmd — tidying fit objectsvignettes/nimo.Rmd, mavoglurant.Rmd, wbc.Rmd — worked PK/PD examplesvignettes/xgxr-nlmixr-ggpmx.Rmd — exploratory + GOF plotting workflownlmixr2 model is an rxode2 model plus ini() and a residual error term.est = "nonmem" | "monolix" | "pknca" and returns an nlmixr2-shaped fit.Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Searches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.
npx claudepluginhub john-harrold/nlmixr2llm --plugin nlmixr2llm