#' Get the nonmem control statement and install it into the ui
#'
#' @param env Environment with ui in it
#' @param ... Other arguments
#' @return Nothing, called for side effects
#' @author Matthew L. Fidler
#' @noRd
.nonmemFamilyControl <- function(env, ...) {
.ui <- env$ui
.control <- env$control
if (is.null(.control)) {
.control <- nonmemControl()
}
if (!inherits(.control, "nonmemControl")){
.control <- do.call(babelmixr2::nonmemControl, .control)
}
assign("control", .control, envir=.ui)
}
.nonmemFormatData <- function(data, ui) {
.ret <- data
if (any(names(.ret) == "SS")) {
.ret$SS <- ifelse(.ret$SS == 0, NA_real_, .ret$SS)
if (all(is.na(.ret$SS))) {
.ret <- .ret[, !(names(.ret) %in% c("SS", "II"))]
}
}
.n <- names(.ret)
rxode2::rxAssignControlValue(ui, ".hasRate", any(.n == "RATE"))
rxode2::rxAssignControlValue(ui, ".hasCens", any(.n == "CENS"))
rxode2::rxAssignControlValue(ui, ".hasLimit", any(.n == "LIMIT"))
rxode2::rxAssignControlValue(ui, ".hasIi", any(.n == "II"))
rxode2::rxAssignControlValue(ui, ".hasSs", any(.n == "SS"))
.ret
}
.nonmemFinalizeEnv <- function(env, oldUi) {
# The environment needs:
.iniDf <- oldUi$nonmemIniDf
.ui <- new.env(parent=emptyenv())
for (n in ls(envir=oldUi, all.names=TRUE)) {
assign(n, get(n, envir=oldUi), envir=.ui)
}
assign("iniDf", .iniDf, envir=.ui)
class(.ui) <- class(oldUi)
# - $table for table options -- already present
# - $origData -- Original Data -- already present
# - $dataSav -- Processed data from .foceiPreProcessData --already present
# - $idLvl -- Level information for ID factor added -- already present
env$ui <- .ui
# - $ui for ui fullTheta Full theta information
env$fullTheta <- .ui$nonmemFullTheta
# - $etaObf data frame with ID, etas and OBJI
env$etaObf <- .ui$nonmemEtaObf
if (is.null(env$etaObf)) {
.df <- data.frame(ID=unique(env$dataSav$ID))
for (.n in .getEtaNames(.ui)) {
.df[[.n]] <- 0
}
.df[["OBJI"]] <- NA_real_
env$etaObf <- .df
warning("since NONMEM did not output between subject variability, assuming all ETA(#) are zero",
call.=FALSE)
}
# - $cov For covariance
.cov <- .ui$nonmemCovariance
if (!is.null(.cov)) {
env$cov <- .cov
# - $covMethod for the method of calculating the covariance
env$covMethod <- paste0("nonmem.", rxode2::rxGetControl(.ui, "cov", "r,s"))
}
# - $objective objective function value
env$objective <- NA_real_ #.ui$nonmemObjf
# - $extra Extra print information
env$extra <- paste0(" ver ", env$ui$nonmemOutputVersion)
# - $method Estimation method (for printing)
env$method <- "Nonmem"
# - $omega Omega matrix
env$omega <- .ui$nonmemOutputOmega
# - $theta Is a theta data frame
env$theta <- .ui$nonmemThetaDf
# - $model a list of model information for table generation. Needs a `predOnly` model
env$model <- .ui$ebe
# - $message Message for display
env$message <- ""
# - $est estimation method
env$est <- "nonmem"
# - $ofvType (optional) tells the type of ofv is currently being used
#env$ofvType
env$ofvType <- .ui$nonmemObjfType
# Add parameter history
env$parHistData <- .ui$nonmemParHistory
env$nobs <- .lastNobs
env$nobs2<- .lastNobs
# Run before converting to nonmemControl
.objf <- .ui$nonmemObjf + env$nmLikAdj
# When running the focei problem to create the nlmixr object, you also need a
# foceiControl object
.nonmemControlToFoceiControl(env, TRUE)
env <- nlmixr2est::nlmixr2CreateOutputFromUi(env$ui, data=env$origData,
control=env$control, table=env$table,
env=env, est="nonmem")
.env <- env$env
.env$adj <- .env$nobs*log(2 * pi)
.objf2 <- .objf + .env$adj
.llik <- -(.objf2) / 2
attr(.llik, "df") <- attr(.env$logLik, "df")
attr(.llik, "nobs") <- .env$nobs
class(.llik) <- "logLik"
.env$logLik <- .llik
.tmp <- data.frame(
OBJF = .objf, AIC = .objf2 + 2 * attr(get("logLik", .env), "df"),
BIC = .objf2 + log(.env$nobs) * attr(get("logLik", .env), "df"),
"Log-likelihood" = as.numeric(.llik), check.names = FALSE
)
.env$method <- "nonmem"
.time <- get("time", .env$env)
.time <- .time[,!(names(.time) %in% c("optimize", "covariance"))]
assign("time",
cbind(.time, data.frame(NONMEM=.ui$nonmemRunTime)),
.env)
nlmixr2est::nlmixrAddObjectiveFunctionDataFrame(env, .tmp, .env$ofvType)
env
}
.nonmemFamilyFit <- function(env, ...) {
.ui <- env$ui
.control <- .ui$control
.data <- env$data
.ret <- new.env(parent=emptyenv())
.ret$table <- env$table
.ret$nonmemControl <- .control
.tmp <- bblDatToNonmem(.ui, .data, table=env$table, rxControl=.control$rxControl, env=.ret)
.ret$nonmemData <- .nonmemFormatData(.tmp, .ui)
rxode2::rxAssignControlValue(.ui, ".cmtCnt", env$nmNcmt)
# Now make sure time varying covariates are not considered
# mu-referenced items
.et <- rxode2::etTrans(.ret$dataSav, .ui$mv0, addCmt=TRUE)
.nTv <- attr(class(.et), ".rxode2.lst")$nTv
if (is.null(.nTv)) {
.tv <- names(.et)[-seq(1, 6)]
.nTv <- length(.tv)
} else {
.tv <- character(0)
if (.nTv != 0) {
.tv <- names(.et)[-seq(1, 6)]
}
}
.muRefCovariateDataFrame <- .ui$muRefCovariateDataFrame
if (length(.tv) > 0) {
# Drop time-varying covariates
.muRefCovariateDataFrame <- .muRefCovariateDataFrame[!(.muRefCovariateDataFrame$covariate %in% .tv), ]
}
assign("muRefFinal", .muRefCovariateDataFrame, .ui)
assign("timeVaryingCovariates", .tv, .ui)
on.exit({
if (exists("muRefFinal", envir=.ui)) {
rm(list="muRefFinal", envir=.ui)
}
if (exists("timeVaryingCovariates", envir=.ui)) {
rm(list="timeVaryingCovariates", envir=.ui)
}
})
.nmctl <- .ui$nonmemModel
.contra <- .ui$nonmemContra
.hashMd5 <- digest::digest(list(.nmctl, .contra, .ret$nonmemData))
.foundModelName <- FALSE
.hashFile <- file.path(.ui$nonmemExportPath, .ui$nonmemHashFile)
while (!.foundModelName) {
if (!file.exists(.hashFile)) {
.foundModelName <- TRUE
} else {
if (readLines(.hashFile) == .hashMd5) {
.foundModelName <- TRUE
} else {
.num <- rxode2::rxGetControl(.ui, ".modelNumber", 0) + 1
rxode2::rxAssignControlValue(.ui, ".modelNumber", .num)
.hashFile <- file.path(.ui$nonmemExportPath, .ui$nonmemHashFile)
}
}
}
.exportPath <- .ui$nonmemExportPath
if (!dir.exists(.exportPath)) dir.create(.exportPath)
.csv <- file.path(.exportPath, .ui$nonmemCsv)
.nmctlFile <- file.path(.exportPath, .ui$nonmemNmctl)
.contraFile <- file.path(.exportPath, .ui$nonmemContraName)
.ccontraFile <- file.path(.exportPath, .ui$nonmemCcontraName)
.qs <- file.path(.exportPath, .ui$nonmemQs)
if (file.exists(.qs)) {
.minfo("load saved nlmixr2 object")
.ret <- qs::qread(.qs)
return(.ret)
} else if (!file.exists(.nmctlFile)) {
.minfo("writing nonmem files")
writeLines(text=.nmctl, con=.nmctlFile)
writeLines(text=.hashMd5, con=.hashFile)
if (!is.null(.contra)) {
writeLines(text=.contra, con=.contraFile)
.ccontra <- .ui$nonmemCcontra
writeLines(text=.ccontra, con=.ccontraFile)
}
write.csv(.ret$nonmemData, file=.csv, na = ".", row.names = FALSE,
quote=FALSE)
.minfo("done")
}
.cmd <- rxode2::rxGetControl(.ui, "runCommand", "")
if (!rxode2::rxGetControl(.ui, "run", TRUE) ||
is.na(.cmd)) {
.minfo("only exported NONMEM control stream/data")
return(invisible(.ui))
}
if (!file.exists(file.path(.exportPath, .ui$nonmemXml))) {
print(file.path(.exportPath, .ui$nonmemXml))
.nonmemRunner(ui=.ui)
}
.read <- .ui$nonmemSuccessful
.readRounding <- rxode2::rxGetControl(.ui, "readRounding", FALSE)
.roundingErrors <- .ui$nonmemRoundingErrors
if (!.read && .roundingErrors && .readRounding) {
warning("NONMEM terminated due to rounding errors, but reading into nlmixr2/rxode2 anyway",
call.=FALSE)
.read <- TRUE
}
.readBadOpt <- rxode2::rxGetControl(.ui, "readBadOpt", FALSE)
.isBadOpt <- FALSE
if (!.read && .readBadOpt) {
warning("NONMEM unsuccessful, but reading into nlmixr2/rxode2 anyway",
call.=FALSE)
.isBadOpt <- TRUE
.read <- TRUE
}
if (!.read) {
.msg <- c(.ui$nonmemTransMessage,
.ui$nonmemTermMessage)
.msg <- c(.msg,
paste0("nonmem model: '", .nmctlFile, "'"))
message(paste(.msg, collapse="\n"))
if (.roundingErrors) {
rxode2::.malert("terminated with rounding errors, can force nlmixr2/rxode2 to read with nonmemControl(readRounding=TRUE)")
} else {
rxode2::.malert("terminated with bad optimization, can force nlmixr2/rxode2 to read with nonmemControl(readBadOpt=TRUE)")
}
stop("nonmem minimization not successful",
call.=FALSE)
}
.ret <- .nonmemFinalizeEnv(.ret, .ui)
if (inherits(.ret, "nlmixr2FitData")) {
.msg <- .nonmemMergePredsAndCalcRelativeErr(.ret)
.prderrPath <- file.path(.exportPath, "PRDERR")
.msg$message <- c(.ui$nonmemTransMessage,
.ui$nonmemTermMessage,
.msg$message)
if (file.exists(.prderrPath)) {
.prderr <- paste(readLines(.prderrPath), collapse="\n")
.msg$message <- c(.msg$message,
"there are solving errors during optimization (see '$prderr')")
assign("prderr", .prderr, envir=.ret$env)
}
.msg$message <- c(.msg$message, paste0("nonmem model: '", .nmctlFile, "'"))
assign("message", paste(.msg$message, collapse="\n "), envir=.ret$env)
qs::qsave(.ret, .qs)
}
.ret
}
#' Run NONMEM using either the user-specified command or function
#'
#' @param ui The nlmixr2 UI object for running
#' @param monolix are we actually running monolix
#' @return NULL
#' @noRd
.nonmemRunner <- function(ui) {
cmd <- rxode2::rxGetControl(ui, "runCommand", "")
if (is.character(cmd)) {
cmd <- .nonmemRunCommand
} else if (!is.function(cmd)) {
stop("invalid value for nonmemControl(runCommand=)",
call.=FALSE)
}
cmd(ctl=ui$nonmemNmctl, directory=ui$nonmemExportPath, ui=ui)
NULL
}
.nonmemRunCommand <- function(ctl, directory, ui) {
cmd <- rxode2::rxGetControl(ui, "runCommand", "")
if (cmd != "") {
fullCmd <- paste(cmd, ctl, ui$nonmemNmlst)
.minfo(paste0("run NONMEM: ", fullCmd))
withr::with_dir(ui$nonmemExportPath, system(fullCmd))
} else {
stop("run NONMEM manually and rerun nlmixr() or setup NONMEM's run command")
}
}
#' @export
nlmixr2Est.nonmem <- function(env, ...) {
.model <- nlmixr2est::.uiApplyMu2(env)
.ui <- env$ui
rxode2::assertRxUiTransformNormal(.ui, " for the estimation routine 'nonmem'", .var.name=.ui$modelName)
rxode2::assertRxUiRandomOnIdOnly(.ui, " for the estimation routine 'nonmem'", .var.name=.ui$modelName)
rxode2::assertRxUiEstimatedResiduals(.ui, " for the estimation routine 'nonmem'", .var.name=.ui$modelName)
.nonmemFamilyControl(env, ...)
on.exit({
if (exists("control", envir=.ui)) {
rm("control", envir=.ui)
}
}, add=TRUE)
nlmixr2est::.uiFinalizeMu2(.nonmemFamilyFit(env, ...), .model)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.