While NONMEM offers great flexibility for estimation of PK and PK/PD
models, many users find the simulation features in NONMEM insufficient
and turn to alternative software for simulation. This leads to
additional work of model reimplementation, with risk of the simulation
model deviating from the estimated model, due to bugs in the
reimplementation. For a wide range of model types, the limitation is not
in NONMEM’s ability to perform such simulations, but rather in the lack
of a simple user-interface to obtain the simulations. NMsim
[@R-NMsim] provides such an interface as
an R package, allowing the modeler to simulate models as soon as an
estimate is available.
The goal for NMsim is to automate the NONMEM simulation workflow and provide a simple, flexible, and powerful R interface. With this automation, post-processing of model estimates can to great extends be automated.
NONMEM | Third-party | NMsim | |
---|---|---|---|
Implementation | Easy | Depends - Potentially error-prone | None |
Execution | Tedious | Easy | Easy |
Depends on NONMEM | Yes | No | Yes |
Runtime | Fair | Fast | Fair |
NMsim
does not simulate, translate or otherwise
interpret a NONMEM model. Instead, it automates the NONMEM simulation
workflow (including execution of NONMEM) and wraps it all into one R
function. Provided with a path to a NONMEM control stream and a
data.frame to simulate, NMsim
will do the following:
file.mod
($INPUT
and $DATA
matching the saved simulation data set; $SIMULATE
instead
of $ESTIMATION
and $COVARIANCE
)file.ext
)NMsim
can call NONMEM directly or via PSN
.
If NMsim
is run on a system where NONMEM cannot be
executed, NMsim
can still prepare the simulation control
stream and datafile.
NMsim
is in itself a relatively small R package. It
makes extensive use of functionality to handle NONMEM data and control
streams provided by the R package NMdata
[@R-NMdata].
When providing a simulation data set, the default
NMsim()
behavior is to sample a new subject (ETA’s).
file.mod <- system.file("examples/nonmem/xgxr021.mod",
package="NMsim")
data.sim <- read.csv(system.file("examples/derived/dat_sim1.csv",
package="NMsim"))
simres <- NMsim(file.mod=file.mod,data=data.sim)
simres <- NMsim(file.mod=file.mod,data=data.sim)
datl <- as.data.table(simres) |>
melt(measure.vars=cc(PRED,IPRED,Y))
ggplot(datl,aes(TIME,value,colour=variable))+
geom_line(data=function(x)x[variable!="Y"])+
geom_point(data=function(x)x[variable=="Y"])+
labs(x="Hours since first dose",y="Concentration (ng/mL)",
subtitle="Simulation of one new subject.",
colour="")
Notice that no information about the model is needed except for the
control stream file path. The simulation is based on evaluation of
PRED
, IPRED
, and optionally Y
.
Options exist for building more advanced simulation models. The models
shown here are based on data available in the xgxr
[@R-xgxr].
The simulation input data set is a data.frame, and
NMsim()
returns a data.frame. The input data is a
data.frame that
ID
, CMT
, AMT
,
etc. plus covariates)There are no requirements to how the data sets are created. NMsim provides convenient helper functions that can optionally be used. E.g., the data set used in these simulations can be created this way:
doses <- NMcreateDoses(TIME=c(0,24),AMT=c(300,150),
addl=list(ADDL=c(0,5),II=c(0,24)),CMT=1)
dat.sim <- addEVID2(doses,time.sim=0:(24*7),CMT=2)
Notice, NMcreateDoses()
has convenient behaviours. See
?NMcreateDoses
for more.
## arguments are expanded - makes loading easy
NMcreateDoses(TIME=c(0,12,24,36),AMT=c(2,1))
## Different doses by covariate
NMcreateDoses(TIME=c(0,12,24),AMT=data.table(AMT=c(2,1,4,2),DOSE=c(1,2)))
Also, be aware of NMdata::addTAPD()
for adding time
since previous dose:
addTAPD(dat.sim)
typical=TRUE
: replace all $OMEGA
values
with zerossimres.typ <- NMsim(file.mod=file.mod,data=data.sim,
typical=TRUE)
Multiple models can be simulated using the same data set in one
function call by supplying more than one model in the
file.mod
argument. The models can be simulated on multiple
data sets by submitting a list of data.frames in the data
argument. NMsim will return one data.frame with all the results for easy
post-processing.
file2.mod.source <- system.file("examples/nonmem/xgxr114.mod",package="NMsim")
## file2.mod.source <- "~/wdirs/NMsim/inst/examples/nonmem/xgxr114.mod"
lapply(fnExtension(file2.mod.source,cc(mod,ext,cov,lst,phi)),file.copy,to="models")
file2.mod <- "models/xgxr114.mod"
simres.typ2 <- NMsim(file.mod=c("2_compartments"=file.mod,
"1_compartment"=file2.mod),
data=data.sim,
typical=TRUE)
## The "model" column is used to distinguish the two models
subset(simres.typ2,EVID==2) |>
ggplot(aes(TIME,PRED,colour=model))+
geom_line()
Simulation of multiple models and even multiple data sets is handled
within one NMsim()
call.
addResVar()
if needed## this example uses the same sim data for all subjects
res <- NMscanData(file.mod,quiet=T)
ids <- unique(res$ID)[1:5]
data.sim.ind <- merge(subset(data.sim,select=-ID),
data.frame(ID=ids))
setorder(data.sim.ind,ID,TIME,EVID)
simres.ebe <- NMsim(file.mod,
data=data.sim.ind,
method.sim=NMsim_EBE,
table.vars=c("CL","V2","IPRED","PRED")
)
Individual parameters are confirmed to be identical in estimation results and simulation results
New subjects can be simulated in multiple ways with NMsim.
$SIMULATION
subproblems
argument translates to the
SUBPROBLEMS
NONMEM subroutine, replicating the simulation
the specified number of times with new seedssimPopEtas()
function can generate a synthetic .phi
file with a simulated population that can be reused in future
NMsim()
calls. This can be combined with simulation of
covariates in R, allowing reuse of the same subjects across multiple
simulations.simres.subprob <- NMsim(file.mod=file.mod,
data=data.sim,
name.sim="Subproblems",
subproblems=1000)
## data.sim.nsubjs replicates data.sim for each subject,
## with sampled covariates
simPopEtas(file.mod=file.mod,N=1000,seed=1231,
file.phi="simres/xgxr021_1000subjs.phi")
simres.datarep <- NMsim(file.mod=file.mod,
data=data.sim.nsubjs,
method.sim=NMsim_EBE,
file.phi="simres/xgxr021_1000subjs.phi",
name.sim="datarep")
simres.newsubjs <- rbind(as.data.table(simres.subprob),
as.data.table(simres.datarep),
fill=T)
#### Prediction intervals based on each of the two simulations
simres.newpops <- rbind(as.data.table(simres.subprob),
simres.datarep,fill=T)[EVID==2]
simres.pi <- simres.newpops[
,setNames(as.list(quantile(IPRED,probs=c(.05,.5,.95))),cc(ll,median,ul)),
by=.(sim,trt,TIME)]
simres.pi$type <- "pi"
simres.pi$pi.cover <- "90%"
ggplot(simres.pi,aes(TIME,fill=trt))+
geom_ribbon(aes(ymin=ll,ymax=ul,alpha=pi.cover))+
geom_line(aes(y=median,colour=trt))+
scale_alpha_manual(values=c("90%"=.5))+
facet_wrap(~sim)+
labs(x="Hours since first dose",y="Concentration (ng/mL)")+
theme(legend.position="none")
Prediction intervals. New subjects can be simulated in multiple ways with NMsim. A simulated population can be reused across simulations.
NMsim()
argumentsNMsim must be configured with the path to the NONMEM executable. This
can be done for each NMsim()
call using the
path.nonmem
argument, but more easily it can be configured
globally the following way. Also including where NMsim will run NONMEM
and store intermediate files (dir.sims
) and where to store
final results (dir.res
).
library(NMdata)
NMdataConf(path.nonmem = "/opt/NONMEM/nm75/run/nmfe75")
## or on Windows, it could be
NMdataConf(path.nonmem = "c:/nm75g64/run/nmfe75.bat")
NMdataConf(dir.sims="simtmp", ## location of sim tmp files
dir.res="simres") ## location of sim results
NMsim()
has many features which are explained and
demonstrated in manuals and vignettes. A few often-used arguments
are
table.vars
: Redefine the output table. This can
dramatically speed up simulations. E.g.,
table.vars=c("PRED","IPRED")
.name.sim
: Assign a name to the simulation and the
generated files. Keeps order and separates results files between
simulations.seed.R
and seed.nm
: Define seed, either
through R, or directly as the seed used in NONMEM simulation control
stream.