diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index f630db04..19a06191 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -2,9 +2,9 @@ # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: - branches: [main, master] + branches: main pull_request: - branches: [main, master] + branches: main name: R-CMD-check @@ -61,7 +61,6 @@ jobs: nlmixr2/rxode2et nlmixr2/rxode2 needs: check - - uses: r-lib/actions/check-r-package@v2 with: upload-snapshots: true diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index b966ef62..16e7759b 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -39,6 +39,10 @@ jobs: nlmixr2/rxode2random nlmixr2/rxode2et nlmixr2/rxode2 + nlmixr2/nlmixr2est + nlmixr2/nlmixr2extra + nlmixr2/nlmixr2plot + nlmixr2/nlmixr2 nlmixr2/babelmixr2 nlmixr2/nlmixr2rpt lixoftConnectors=?ignore diff --git a/DESCRIPTION b/DESCRIPTION index 0ae41db9..6a540465 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: nonmem2rx Type: Package Title: 'nonmem2rx' Converts 'NONMEM' Models to 'rxode2' -Version: 0.1.2.9000 +Version: 0.1.3 Maintainer: Matthew Fidler Authors@R: c(person("Matthew","Fidler", role = c("aut", "cre"), email = "matt.fidler@novartis.com", comment=c(ORCID="0000-0001-8538-6691")), person("Philip", "Delff", email = "philip@delff.dk",role = c("ctb")), @@ -24,14 +24,14 @@ Description: 'NONMEM' has been a tool for running nonlinear mixed matrix) with a 'rxode2' model. This is complementary to the 'babelmixr2' package that translates 'nlmixr2' models to 'NONMEM' and can convert the objects converted from 'nonmem2rx' to a full 'nlmixr2' fit. -License: GPL (>= 3) +License: GPL (>= 3) URL: https://nlmixr2.github.io/nonmem2rx/, https://github.com/nlmixr2/nonmem2rx/ Encoding: UTF-8 -LinkingTo: +LinkingTo: dparser, Rcpp, rxode2parse -Imports: +Imports: checkmate, digest, dparser, @@ -48,7 +48,7 @@ Imports: ggplot2, ggforce, crayon -Suggests: +Suggests: devtools, testthat (>= 3.0.0), nonmemica, diff --git a/NAMESPACE b/NAMESPACE index 10f2bcef..d957801f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -18,7 +18,6 @@ S3method(nonmem2rxRec,sub) S3method(nonmem2rxRec,tab) S3method(nonmem2rxRec,the) S3method(print,nonmem2rx) -S3method(rxRename,nonmem2rx) S3method(rxSolve,nonmem2rx) export("%>%") export("model<-") diff --git a/NEWS.md b/NEWS.md index 5419715f..e66a93e7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ -# nonmem2rx (development version) +# nonmem2rx 0.1.3 -* Added explicit requirement for rxode2 2.0.12 +* Added explicit requirement for rxode2 2.0.13 * Added support of `DADT(#)` statements on the right side of the equation, i.e. `DADT(3) = DADT(1) + DADT(2)` (#164) @@ -9,6 +9,16 @@ * Added more NONMEM-specific solving options +* Fixed security related format issues as requested by CRAN #167 + +* Now `omega`, `thetaMat`, `dfObs` and `dfSub` are incorporated into + model function (by default). You can change this with the + `nonmem2rx` `keep` argument + +* Using the `rxode2` 2.0.13 makes sure that the solves for models + where the endpoint is not determined in the typical `nlmixr2` style + will validate more often (due to a bug in solving in `rxode2`). + # nonmem2rx 0.1.2 * Added support for `ADVAN5` and `ADVAN7` models diff --git a/R/asNonmem2rx.R b/R/asNonmem2rx.R index f4824a73..daf36a16 100644 --- a/R/asNonmem2rx.R +++ b/R/asNonmem2rx.R @@ -9,7 +9,7 @@ #' @examples #' #' \donttest{ -#' +#' #' mod <- nonmem2rx(system.file("mods/cpt/runODE032.ctl", package="nonmem2rx"), #' determineError=FALSE, lst=".res", save=FALSE) #' @@ -66,10 +66,22 @@ as.nonmem2rx <- function(model1, model2, compress=TRUE) { .cp <- c("sticky", "nonmemData", "atol", "rtol", "ssAtol", "ssRtol", "etaData", "ipredData", "predData", "sigmaNames", "dfSub", "thetaMat", "dfObs", "file", "outputExtension") + .meta <- new.env(parent=emptyenv()) + if (exists("meta", envir=.nm2rx)) { + .meta <- get("meta", envir=.nm2rx) + } + .metaRx <- new.env(parent=emptyenv()) + if (exists("meta", envir=.rx)) { + .metaRx <- get("meta", envir=.rx) + } lapply(.cp, function(x) { if (exists(x, envir=.nm2rx)) { assign(x, get(x, envir=.nm2rx), envir=.rx) } + if (exists(x, envir=.meta) && !exists(x, envir=.metaRx)) { + .minfo(paste0("copy '", x, "' to nonmem2rx model")) + assign(x, get(x, envir=.meta), envir=.metaRx) + } }) .nonmemData <- .rx$nonmemData .w <- which(names(.nonmemData) == "nmdvid") @@ -132,7 +144,11 @@ as.nonmem2rx <- function(model1, model2, compress=TRUE) { if (length(.nonmem2rx$modelDesc) > 0) { .rx$meta$description <- .nm2rx$meta$description } - .rx$thetaMat <- .thetaMat + if (exists("thetaMat", .rx$meta)) { + assign("thetaMat", .thetaMat, envir=.rx$meta) + } else { + assign("thetaMat", .thetaMat, envir=.rx) + } if (compress) { .ret <- rxode2::rxUiCompress(.rx) } else { diff --git a/R/buildParser.R b/R/buildParser.R index bbd6c58e..f44deb84 100644 --- a/R/buildParser.R +++ b/R/buildParser.R @@ -99,6 +99,9 @@ .args[1] <- paste0("rxSolve.nonmem2rx <-", .args[1]) .args <- .args[-length(.args)] .extra <- quote({ + if (missing(cores)) { + cores <- 0L + } if (missing(covsInterpolation)) { covsInterpolation <- "nocb" .minfo("using nocb interpolation like NONMEM, specify directly to change") @@ -125,20 +128,30 @@ } if (!missing(nStud)) { if (missing(dfSub)) { - if (!is.null(object$dfSub)){ + if (!is.null(object$meta$dfSub)){ + dfSub <- object$meta$dfSub + .minfo(paste0("using dfSub=", dfSub, " from NONMEM")) + } else if (!is.null(object$dfSub)) { dfSub <- object$dfSub .minfo(paste0("using dfSub=", dfSub, " from NONMEM")) } } if (missing(dfObs)) { - if (!is.null(object$dfObs)){ + if (!is.null(object$meta$dfObs)) { + dfObs <- object$meta$dfObs + .minfo(paste0("using dfObs=", dfObs, " from NONMEM")) + } else if (!is.null(object$dfObs)) { dfObs <- object$dfObs + dfObs <- object$meta$dfObs .minfo(paste0("using dfObs=", dfObs, " from NONMEM")) } } if (missing(thetaMat)) { - if (!is.null(object$thetaMat)){ - thetaMat <- object$thetaMat + if (!is.null(object$meta$thetaMat)) { + thetaMat <- object$meta$thetaMat + .minfo(paste0("using thetaMat from NONMEM")) + } else if (!is.null(object$thetaMat)) { + thetaMat <- object$meta$thetaMat .minfo(paste0("using thetaMat from NONMEM")) } } @@ -147,8 +160,11 @@ if (missing(sigma)) { if (is.null(object$predDf)) { # if a true nlmixr2 model, this is not needed - if (!is.null(object$sigma)){ - sigma <- object$sigma + if (!is.null(object$meta$sigma)){ + sigma <- object$meta$sigma + .minfo(paste0("using sigma from NONMEM")) + } else if (!is.null(object$sigma)) { + sigma <- object$meta$sigma .minfo(paste0("using sigma from NONMEM")) } } @@ -160,20 +176,40 @@ } } if (missing(atol)) { - atol <- object$atol - .minfo(paste0("using NONMEM specified atol=", atol)) + if (!is.null(object$meta$atol)) { + atol <- object$meta$atol + .minfo(paste0("using NONMEM specified atol=", atol)) + } else if (!is.null(object$atol)) { + atol <- object$atol + .minfo(paste0("using NONMEM specified atol=", atol)) + } } if (missing(rtol)) { - rtol <- object$rtol - .minfo(paste0("using NONMEM specified rtol=", rtol)) + if (!is.null(object$meta$atol)) { + rtol <- object$meta$rtol + .minfo(paste0("using NONMEM specified rtol=", rtol)) + } else if (!is.null(object$atol)) { + rtol <- object$rtol + .minfo(paste0("using NONMEM specified rtol=", rtol)) + } } if (missing(ssRtol)) { - ssRtol <- object$ssRtol - .minfo(paste0("using NONMEM specified ssRtol=", ssRtol)) + if (!is.null(object$meta$ssRtol)) { + ssRtol <- object$meta$ssRtol + .minfo(paste0("using NONMEM specified ssRtol=", ssRtol)) + } else if (!is.null(object$meta$ssRtol)) { + ssRtol <- object$meta$ssRtol + .minfo(paste0("using NONMEM specified ssRtol=", ssRtol)) + } } if (missing(ssAtol)) { - ssAtol <- object$ssAtol - .minfo(paste0("using NONMEM specified ssAtol=", ssAtol)) + if (!is.null(object$meta$ssAtol)) { + ssAtol <- object$meta$ssAtol + .minfo(paste0("using NONMEM specified ssAtol=", ssAtol)) + } else if (!is.null(object$ssAtol)) { + ssAtol <- object$ssAtol + .minfo(paste0("using NONMEM specified ssAtol=", ssAtol)) + } } .cls <- class(object) class(object) <- .cls[-which(.cls == "nonmem2rx")] @@ -229,10 +265,13 @@ .desc <- setNames(.meth[i], NULL) .ret <- c("", sprintf("rxUiGet.%s <- function(x, ...) {", .name), + " .meta <- new.env(parent=emptyenv())", + " if (exists(\"meta\", envir=x[[1]])) .meta <- get(\"meta\", envir=x[[1]])", + sprintf(" if (exists(\"%s\", envir=.meta)) return(get(\"%s\", envir=.meta))", .name, .name), sprintf(" if (!exists(\"%s\", envir=x[[1]])) return(NULL)", .name), sprintf(" get(\"%s\", envir=x[[1]])", .name), "}", - sprintf("attr(rxUiGet.%s, \"desc=\") <- %s", .name, deparse1(.desc))) + sprintf("attr(rxUiGet.%s, \"desc\") <- %s", .name, deparse1(.desc))) .ret <- paste(.ret, collapse="\n") }, character(1), USE.NAMES=TRUE), ".rxUiGetRegister <- function() {", diff --git a/R/nonmem2rx.R b/R/nonmem2rx.R index 38ecdeaa..c863f5fe 100644 --- a/R/nonmem2rx.R +++ b/R/nonmem2rx.R @@ -125,7 +125,7 @@ .lhs <- .lhs[-.w] .rhs <- .rhs[-.w] } - .ret <- eval(parse(text=paste0("rxode2::rxRename(rxui, ", paste(paste(.lhs,"=",.rhs, sep=""), collapse=", "), ")"))) + .ret <- eval(str2lang(paste0("rxode2::rxRename(rxui, ", paste(paste(.lhs,"=",.rhs, sep=""), collapse=", "), ")"))) .minfo("done") .ret } @@ -475,6 +475,11 @@ #' should be `FALSE`. Otherwise use `TRUE` if you are using a newer #' rxode2. #' +#' @param keep is a character vector of imported model items that are +#' kept in the model itself; The defaults is "sigma" which keeps the +#' sigma matrix in the model itself. You can add rxode2 solving +#' options that are imported from NONMEM to keep in the model. +#' #' @details #' #' Since some of these options you may want to set per project, the @@ -514,7 +519,7 @@ #' # You can run a translation without validating the input. This is #' # a faster way to import a dataset (and allows the CRAN machines to #' # run a quick example) -#' +#' #' mod <- nonmem2rx(system.file("mods/cpt/runODE032.ctl", package="nonmem2rx"), lst=".res", #' save=FALSE, validate=FALSE, compress=FALSE) #' @@ -580,7 +585,8 @@ nonmem2rx <- function(file, inputData=NULL, nonmemOutputDir=NULL, saveTime=getOption("nonmem2rx.saveTime", 15), overwrite=getOption("nonmem2rx.overwrite", TRUE), load=getOption("nonmem2rx.load", TRUE), - compress=getOption("nonmem2rx.compress", TRUE)) { + compress=getOption("nonmem2rx.compress", TRUE), + keep=getOption("nonmem2rx.keep", c("dfSub", "dfObs", "thetaMat", "sigma"))) { .pt <- proc.time() .ret <- .collectWarn({ checkmate::assertFileExists(file) @@ -596,6 +602,9 @@ nonmem2rx <- function(file, inputData=NULL, nonmemOutputDir=NULL, checkmate::assertLogical(load, len=1, any.missing = FALSE) checkmate::assertLogical(compress, len=1, any.missing = FALSE) checkmate::assertNumeric(saveTime, len=1, lower=1.0) + checkmate::assertSubset(keep, + c("dfSub", "dfObs", "thetaMat", "sigma", "atol", + "rtol", "ssRtol", "ssAtol")) .saveWithTime <- FALSE if (is.logical(save)) { checkmate::assertLogical(save, len=1, any.missing=TRUE) @@ -613,7 +622,7 @@ nonmem2rx <- function(file, inputData=NULL, nonmemOutputDir=NULL, rename, tolowerLhs, thetaNames, etaNames, cmtNames, updateFinal, determineError, validate, nonmemData, strictLst, unintFixed, extended, nLinesPro, delta, usePhi, useExt, useCov, useXml, - useLst, mod, cov, phi, lst, xml, ext, scanLines)) + useLst, mod, cov, phi, lst, xml, ext, scanLines, keep)) if (!is.null(save)) { if (load && overwrite) { if (utils::file_test("-nt", file, save)) { @@ -897,12 +906,31 @@ nonmem2rx <- function(file, inputData=NULL, nonmemOutputDir=NULL, .rx$nonmemData <- .nonmemData .rx$sticky <- "nonmemData" } - .rx$atol <- .nonmem2rx$atol - .rx$rtol <- .nonmem2rx$rtol - .rx$ssAtol <- .nonmem2rx$ssAtol - .rx$ssRtol <- .nonmem2rx$ssRtol + if ("atol" %in% keep) { + .rx$meta$atol <- .nonmem2rx$atol + } else { + .rx$atol <- .nonmem2rx$atol + .rx$sticky <- c(.rx$sticky, "atol") + } + if ("rtol" %in% keep) { + .rx$meta$rtol <- .nonmem2rx$rtol + } else { + .rx$rtol <- .nonmem2rx$rtol + .rx$sticky <- c(.rx$sticky, "rtol") + } + if ("ssAtol" %in% keep) { + .rx$meta$ssAtol <- .nonmem2rx$ssAtol + } else { + .rx$ssAtol <- .nonmem2rx$ssAtol + .rx$sticky <- c(.rx$sticky, "ssAtol") + } + if ("ssRtol" %in% keep) { + .rx$meta$ssRtol <- .nonmem2rx$ssRtol + } else { + .rx$ssRtol <- .nonmem2rx$ssRtol + .rx$sticky <- c(.rx$sticky, "ssRtol") + } .rx$etaData <- .etaData - .rx$sticky <- c(.rx$sticky, "atol", "rtol", "ssAtol", "ssRtol") .rx$ipredData <- .ipredData .rx$predData <- .predData .rx$sigmaNames <- dimnames(.sigma)[[1]] @@ -914,17 +942,33 @@ nonmem2rx <- function(file, inputData=NULL, nonmemOutputDir=NULL, .rx$meta$description <- .nonmem2rx$modelDesc } if (!is.null(.sigma)) { - .rx$sigma <- .sigma - .rx$sticky <- c(.rx$sticky, "sigma") + if ("sigma" %in% keep) { + .rx$meta$sigma <- .sigma + } else { + .rx$sigma <- .sigma + .rx$sticky <- c(.rx$sticky, "sigma") + } } if (!is.null(.cov)) { - .rx$thetaMat <- .cov + if ("thetaMat" %in% keep) { + .rx$meta$thetaMat <- .cov + } else { + .rx$thetaMat <- .cov + } } if (inherits(.lstInfo$nsub, "numeric")) { - .rx$dfSub <- .lstInfo$nsub + if ("dfSub" %in% keep) { + .rx$meta$dfSub <- .lstInfo$nsub + } else { + .rx$dfSub <- .lstInfo$nsub + } } if (inherits(.lstInfo$nobs, "numeric")) { - .rx$dfObs <- .lstInfo$nobs + if ("dfObs" %in% keep) { + .rx$meta$dfObs <- .lstInfo$nobs + } else { + .rx$dfObs <- .lstInfo$nobs + } } .rx$digest <- .digest .rx diff --git a/R/omega.R b/R/omega.R index 8c372b72..c04e2c68 100644 --- a/R/omega.R +++ b/R/omega.R @@ -58,7 +58,7 @@ nonmem2rxRec.sig <- function(x) { comment } #' Add omega parameter comment to `.nonmem2rx` environment -#' +#' #' @param comment comment for the Omega parameter #' @param prefix Prefix of parameter names (currently eta or eps) #' @return Nothing, called for side effects diff --git a/R/print.R b/R/print.R index 2c5121b9..99f75c89 100644 --- a/R/print.R +++ b/R/print.R @@ -20,7 +20,7 @@ print.nonmem2rx <- function(x, ...) { cli::cli_h2("nonmem2rx extra properties:") }), "\n") if (is.null(x$predDf)) { - cat(paste0("\n", crayon::bold("Sigma"), " (", crayon::bold$blue("$sigma"), + cat(paste0("\n", crayon::bold("Sigma"), " (", crayon::bold$blue("$sigma"), "):"), "\n") print(x$sigma) cat("\n") diff --git a/R/rxSolve.R b/R/rxSolve.R index 2ff900ac..b42ce640 100644 --- a/R/rxSolve.R +++ b/R/rxSolve.R @@ -38,7 +38,10 @@ rxSolve.nonmem2rx <- function(object, params = NULL, events = NULL, ssRtolSens = 1e-06, simVariability = NA, nLlikAlloc = NULL, useStdPow = FALSE, naTimeHandle = c("ignore", "warn", "error"), addlKeepsCov = FALSE, addlDropSs = TRUE, ssAtDoseTime = TRUE, - ss2cancelAllPending = FALSE) { + ss2cancelAllPending = FALSE, envir = parent.frame()) { + if (missing(cores)) { + cores <- 0L + } if (missing(covsInterpolation)) { covsInterpolation <- "nocb" .minfo("using nocb interpolation like NONMEM, specify directly to change") @@ -65,28 +68,45 @@ rxSolve.nonmem2rx <- function(object, params = NULL, events = NULL, } if (!missing(nStud)) { if (missing(dfSub)) { - if (!is.null(object$dfSub)) { + if (!is.null(object$meta$dfSub)) { + dfSub <- object$meta$dfSub + .minfo(paste0("using dfSub=", dfSub, " from NONMEM")) + } + else if (!is.null(object$dfSub)) { dfSub <- object$dfSub .minfo(paste0("using dfSub=", dfSub, " from NONMEM")) } } if (missing(dfObs)) { - if (!is.null(object$dfObs)) { + if (!is.null(object$meta$dfObs)) { + dfObs <- object$meta$dfObs + .minfo(paste0("using dfObs=", dfObs, " from NONMEM")) + } + else if (!is.null(object$dfObs)) { dfObs <- object$dfObs + dfObs <- object$meta$dfObs .minfo(paste0("using dfObs=", dfObs, " from NONMEM")) } } if (missing(thetaMat)) { - if (!is.null(object$thetaMat)) { - thetaMat <- object$thetaMat + if (!is.null(object$meta$thetaMat)) { + thetaMat <- object$meta$thetaMat + .minfo(paste0("using thetaMat from NONMEM")) + } + else if (!is.null(object$thetaMat)) { + thetaMat <- object$meta$thetaMat .minfo(paste0("using thetaMat from NONMEM")) } } } if (missing(sigma)) { if (is.null(object$predDf)) { - if (!is.null(object$sigma)) { - sigma <- object$sigma + if (!is.null(object$meta$sigma)) { + sigma <- object$meta$sigma + .minfo(paste0("using sigma from NONMEM")) + } + else if (!is.null(object$sigma)) { + sigma <- object$meta$sigma .minfo(paste0("using sigma from NONMEM")) } } @@ -98,20 +118,44 @@ rxSolve.nonmem2rx <- function(object, params = NULL, events = NULL, } } if (missing(atol)) { - atol <- object$atol - .minfo(paste0("using NONMEM specified atol=", atol)) + if (!is.null(object$meta$atol)) { + atol <- object$meta$atol + .minfo(paste0("using NONMEM specified atol=", atol)) + } + else if (!is.null(object$atol)) { + atol <- object$atol + .minfo(paste0("using NONMEM specified atol=", atol)) + } } if (missing(rtol)) { - rtol <- object$rtol - .minfo(paste0("using NONMEM specified rtol=", rtol)) + if (!is.null(object$meta$atol)) { + rtol <- object$meta$rtol + .minfo(paste0("using NONMEM specified rtol=", rtol)) + } + else if (!is.null(object$atol)) { + rtol <- object$rtol + .minfo(paste0("using NONMEM specified rtol=", rtol)) + } } if (missing(ssRtol)) { - ssRtol <- object$ssRtol - .minfo(paste0("using NONMEM specified ssRtol=", ssRtol)) + if (!is.null(object$meta$ssRtol)) { + ssRtol <- object$meta$ssRtol + .minfo(paste0("using NONMEM specified ssRtol=", ssRtol)) + } + else if (!is.null(object$meta$ssRtol)) { + ssRtol <- object$meta$ssRtol + .minfo(paste0("using NONMEM specified ssRtol=", ssRtol)) + } } if (missing(ssAtol)) { - ssAtol <- object$ssAtol - .minfo(paste0("using NONMEM specified ssAtol=", ssAtol)) + if (!is.null(object$meta$ssAtol)) { + ssAtol <- object$meta$ssAtol + .minfo(paste0("using NONMEM specified ssAtol=", ssAtol)) + } + else if (!is.null(object$ssAtol)) { + ssAtol <- object$ssAtol + .minfo(paste0("using NONMEM specified ssAtol=", ssAtol)) + } } .cls <- class(object) class(object) <- .cls[-which(.cls == "nonmem2rx")] @@ -148,5 +192,5 @@ rxSolve.nonmem2rx <- function(object, params = NULL, events = NULL, simVariability = simVariability, nLlikAlloc = nLlikAlloc, useStdPow = useStdPow, naTimeHandle = naTimeHandle, addlKeepsCov = addlKeepsCov, addlDropSs = addlDropSs, ssAtDoseTime = ssAtDoseTime, - ss2cancelAllPending = ss2cancelAllPending) + ss2cancelAllPending = ss2cancelAllPending, envir = envir) } diff --git a/R/rxUiGetGen.R b/R/rxUiGetGen.R index 4eec7bd1..1e9c7803 100644 --- a/R/rxUiGetGen.R +++ b/R/rxUiGetGen.R @@ -2,100 +2,148 @@ # This is built from buildParser.R, edit there rxUiGet.nonmemData <- function(x, ...) { + .meta <- new.env(parent=emptyenv()) + if (exists("meta", envir=x[[1]])) .meta <- get("meta", envir=x[[1]]) + if (exists("nonmemData", envir=.meta)) return(get("nonmemData", envir=.meta)) if (!exists("nonmemData", envir=x[[1]])) return(NULL) get("nonmemData", envir=x[[1]]) } -attr(rxUiGet.nonmemData, "desc=") <- "NONMEM input data from nonmem2rx" +attr(rxUiGet.nonmemData, "desc") <- "NONMEM input data from nonmem2rx" rxUiGet.etaData <- function(x, ...) { + .meta <- new.env(parent=emptyenv()) + if (exists("meta", envir=x[[1]])) .meta <- get("meta", envir=x[[1]]) + if (exists("etaData", envir=.meta)) return(get("etaData", envir=.meta)) if (!exists("etaData", envir=x[[1]])) return(NULL) get("etaData", envir=x[[1]]) } -attr(rxUiGet.etaData, "desc=") <- "NONMEM etas input from nonmem2rx" +attr(rxUiGet.etaData, "desc") <- "NONMEM etas input from nonmem2rx" rxUiGet.ipredAtol <- function(x, ...) { + .meta <- new.env(parent=emptyenv()) + if (exists("meta", envir=x[[1]])) .meta <- get("meta", envir=x[[1]]) + if (exists("ipredAtol", envir=.meta)) return(get("ipredAtol", envir=.meta)) if (!exists("ipredAtol", envir=x[[1]])) return(NULL) get("ipredAtol", envir=x[[1]]) } -attr(rxUiGet.ipredAtol, "desc=") <- "50th percentile of the IPRED atol comparison between rxode2 and model import" +attr(rxUiGet.ipredAtol, "desc") <- "50th percentile of the IPRED atol comparison between rxode2 and model import" rxUiGet.ipredRtol <- function(x, ...) { + .meta <- new.env(parent=emptyenv()) + if (exists("meta", envir=x[[1]])) .meta <- get("meta", envir=x[[1]]) + if (exists("ipredRtol", envir=.meta)) return(get("ipredRtol", envir=.meta)) if (!exists("ipredRtol", envir=x[[1]])) return(NULL) get("ipredRtol", envir=x[[1]]) } -attr(rxUiGet.ipredRtol, "desc=") <- "50th percentile of the IPRED rtol comparison between rxode2 and model import" +attr(rxUiGet.ipredRtol, "desc") <- "50th percentile of the IPRED rtol comparison between rxode2 and model import" rxUiGet.ipredCompare <- function(x, ...) { + .meta <- new.env(parent=emptyenv()) + if (exists("meta", envir=x[[1]])) .meta <- get("meta", envir=x[[1]]) + if (exists("ipredCompare", envir=.meta)) return(get("ipredCompare", envir=.meta)) if (!exists("ipredCompare", envir=x[[1]])) return(NULL) get("ipredCompare", envir=x[[1]]) } -attr(rxUiGet.ipredCompare, "desc=") <- "Dataset comparing ID, TIME and the IPREDs between rxode2 and model import" +attr(rxUiGet.ipredCompare, "desc") <- "Dataset comparing ID, TIME and the IPREDs between rxode2 and model import" rxUiGet.predAtol <- function(x, ...) { + .meta <- new.env(parent=emptyenv()) + if (exists("meta", envir=x[[1]])) .meta <- get("meta", envir=x[[1]]) + if (exists("predAtol", envir=.meta)) return(get("predAtol", envir=.meta)) if (!exists("predAtol", envir=x[[1]])) return(NULL) get("predAtol", envir=x[[1]]) } -attr(rxUiGet.predAtol, "desc=") <- "50th percentile of the PRED atol comparison between rxode2 and model import" +attr(rxUiGet.predAtol, "desc") <- "50th percentile of the PRED atol comparison between rxode2 and model import" rxUiGet.predRtol <- function(x, ...) { + .meta <- new.env(parent=emptyenv()) + if (exists("meta", envir=x[[1]])) .meta <- get("meta", envir=x[[1]]) + if (exists("predRtol", envir=.meta)) return(get("predRtol", envir=.meta)) if (!exists("predRtol", envir=x[[1]])) return(NULL) get("predRtol", envir=x[[1]]) } -attr(rxUiGet.predRtol, "desc=") <- "50th percentile of the PRED rtol comparison between rxode2 and model import" +attr(rxUiGet.predRtol, "desc") <- "50th percentile of the PRED rtol comparison between rxode2 and model import" rxUiGet.predCompare <- function(x, ...) { + .meta <- new.env(parent=emptyenv()) + if (exists("meta", envir=x[[1]])) .meta <- get("meta", envir=x[[1]]) + if (exists("predCompare", envir=.meta)) return(get("predCompare", envir=.meta)) if (!exists("predCompare", envir=x[[1]])) return(NULL) get("predCompare", envir=x[[1]]) } -attr(rxUiGet.predCompare, "desc=") <- "Dataset comparing ID, TIME and the PREDs between rxode2 and model import" +attr(rxUiGet.predCompare, "desc") <- "Dataset comparing ID, TIME and the PREDs between rxode2 and model import" rxUiGet.sigma <- function(x, ...) { + .meta <- new.env(parent=emptyenv()) + if (exists("meta", envir=x[[1]])) .meta <- get("meta", envir=x[[1]]) + if (exists("sigma", envir=.meta)) return(get("sigma", envir=.meta)) if (!exists("sigma", envir=x[[1]])) return(NULL) get("sigma", envir=x[[1]]) } -attr(rxUiGet.sigma, "desc=") <- "sigma matrix from model import" +attr(rxUiGet.sigma, "desc") <- "sigma matrix from model import" rxUiGet.thetaMat <- function(x, ...) { + .meta <- new.env(parent=emptyenv()) + if (exists("meta", envir=x[[1]])) .meta <- get("meta", envir=x[[1]]) + if (exists("thetaMat", envir=.meta)) return(get("thetaMat", envir=.meta)) if (!exists("thetaMat", envir=x[[1]])) return(NULL) get("thetaMat", envir=x[[1]]) } -attr(rxUiGet.thetaMat, "desc=") <- "covariance matrix" +attr(rxUiGet.thetaMat, "desc") <- "covariance matrix" rxUiGet.dfSub <- function(x, ...) { + .meta <- new.env(parent=emptyenv()) + if (exists("meta", envir=x[[1]])) .meta <- get("meta", envir=x[[1]]) + if (exists("dfSub", envir=.meta)) return(get("dfSub", envir=.meta)) if (!exists("dfSub", envir=x[[1]])) return(NULL) get("dfSub", envir=x[[1]]) } -attr(rxUiGet.dfSub, "desc=") <- "Number of subjects" +attr(rxUiGet.dfSub, "desc") <- "Number of subjects" rxUiGet.dfObs <- function(x, ...) { + .meta <- new.env(parent=emptyenv()) + if (exists("meta", envir=x[[1]])) .meta <- get("meta", envir=x[[1]]) + if (exists("dfObs", envir=.meta)) return(get("dfObs", envir=.meta)) if (!exists("dfObs", envir=x[[1]])) return(NULL) get("dfObs", envir=x[[1]]) } -attr(rxUiGet.dfObs, "desc=") <- "Number of observations" +attr(rxUiGet.dfObs, "desc") <- "Number of observations" rxUiGet.atol <- function(x, ...) { + .meta <- new.env(parent=emptyenv()) + if (exists("meta", envir=x[[1]])) .meta <- get("meta", envir=x[[1]]) + if (exists("atol", envir=.meta)) return(get("atol", envir=.meta)) if (!exists("atol", envir=x[[1]])) return(NULL) get("atol", envir=x[[1]]) } -attr(rxUiGet.atol, "desc=") <- "atol imported from translation" +attr(rxUiGet.atol, "desc") <- "atol imported from translation" rxUiGet.rtol <- function(x, ...) { + .meta <- new.env(parent=emptyenv()) + if (exists("meta", envir=x[[1]])) .meta <- get("meta", envir=x[[1]]) + if (exists("rtol", envir=.meta)) return(get("rtol", envir=.meta)) if (!exists("rtol", envir=x[[1]])) return(NULL) get("rtol", envir=x[[1]]) } -attr(rxUiGet.rtol, "desc=") <- "rtol imported from translation" +attr(rxUiGet.rtol, "desc") <- "rtol imported from translation" rxUiGet.ssRtol <- function(x, ...) { + .meta <- new.env(parent=emptyenv()) + if (exists("meta", envir=x[[1]])) .meta <- get("meta", envir=x[[1]]) + if (exists("ssRtol", envir=.meta)) return(get("ssRtol", envir=.meta)) if (!exists("ssRtol", envir=x[[1]])) return(NULL) get("ssRtol", envir=x[[1]]) } -attr(rxUiGet.ssRtol, "desc=") <- "ssRtol imported from translation" +attr(rxUiGet.ssRtol, "desc") <- "ssRtol imported from translation" rxUiGet.ssAtol <- function(x, ...) { + .meta <- new.env(parent=emptyenv()) + if (exists("meta", envir=x[[1]])) .meta <- get("meta", envir=x[[1]]) + if (exists("ssAtol", envir=.meta)) return(get("ssAtol", envir=.meta)) if (!exists("ssAtol", envir=x[[1]])) return(NULL) get("ssAtol", envir=x[[1]]) } -attr(rxUiGet.ssAtol, "desc=") <- "ssRtol imported from translation" +attr(rxUiGet.ssAtol, "desc") <- "ssRtol imported from translation" .rxUiGetRegister <- function() { rxode2::.s3register("rxode2::rxUiGet", "nonmemData") rxode2::.s3register("rxode2::rxUiGet", "etaData") diff --git a/R/rxode2.R b/R/rxode2.R deleted file mode 100644 index 4974d63b..00000000 --- a/R/rxode2.R +++ /dev/null @@ -1,41 +0,0 @@ -.rxnmRename1 <- function(rxui, lst) { - .thetaMat <- rxui$thetaMat - .dnt <- dimnames(.thetaMat)[[1]] - .sigma <- rxui$sigma - .dns <- dimnames(.sigma)[[1]] - .w <- which(.dnt == lst[[4]]) - if (length(.w) == 1) { - .dnt[.w] <- lst[[3]] - dimnames(.thetaMat) <- list(.dnt, .dnt) - rxui$thetaMat <- .thetaMat - } - .w <- which(.dns == lst[[4]]) - if (length(.w) == 1) { - .dns[.w] <- lst[[3]] - dimnames(.sigma) <- list(.dns, .dns) - rxui$sigma <- .sigma - } -} - -#'@export -rxRename.nonmem2rx <- function(.data, ...) { - .modelLines <- rxode2::.quoteCallInfoLines(match.call(expand.dots = TRUE)[-(1:2)]) - .lst0 <- as.list(match.call()[-1]) - .lst0$.data <- .data - .vars <- unique(c(.data$mv0$state, .data$mv0$params, .data$mv0$lhs, .data$predDf$var, .data$predDf$cond, .data$iniDf$name)) - .lst <- lapply(seq_along(.modelLines), function(i) { - rxode2::.assertRenameErrorModelLine(.modelLines[[i]], .vars) - }) - .rxui <- rxode2::rxUiDecompress(do.call(rxode2::.rxRename, c(.lst0, list(envir=parent.frame(2))))) - ## now use call information to rename any other variables in `thetaMat` and `sigma` - lapply(seq_along(.lst), function(i) { - .rxnmRename1(.rxui, .lst[[i]]) - }) - .ret <- rxode2::rxUiCompress(.rxui) - if (!inherits(.ret, "nonmem2x")) { - class(.ret) <- c("nonmem2rx", class(.ret)) - } - .ret -} - -rename.nonmem2rx <- rxRename.nonmem2rx diff --git a/R/validate.R b/R/validate.R index 463fc379..2532f411 100644 --- a/R/validate.R +++ b/R/validate.R @@ -142,12 +142,34 @@ # dummy id to match the .params .nonmemData2[,.wid] <- fromNonmemToRxId(as.integer(.nonmemData2[,.wid])) } + if (exists("atol", envir=.rx$meta)) { + .atol <- .rx$meta$atol + } else { + .atol <- .rx$atol + } + if (exists("rtol", envir=.rx$meta)) { + .rtol <- .rx$meta$rtol + } else { + .rtol <- .rx$rtol + } + if (exists("ssAtol", envir=.rx$meta)) { + .ssAtol <- .rx$meta$ssAtol + } else { + .ssAtol <- .rx$ssAtol + } + if (exists("ssRtol", envir=.rx$meta)) { + .ssRtol <- .rx$meta$ssRtol + } else { + .ssRtol <- .rx$ssRtol + } if (.doIpred) { .minfo("solving ipred problem") .ipredSolve <- try(rxSolve(.model, .params, .nonmemData2, returnType = "data.frame", covsInterpolation="nocb", - atol=.rx$atol, rtol=.rx$rtol, - ssAtol=.rx$ssAtol, ssRtol=.rx$ssRtol, + addlKeepsCov=TRUE, addlDropSs=TRUE, ssAtDoseTime=TRUE, + safeZero=TRUE, ss2cancelAllPending=TRUE, + atol=.atol, rtol=.rtol, + ssAtol=.ssAtol, ssRtol=.ssRtol, omega=NULL, addDosing = FALSE)) .minfo("done") } @@ -241,8 +263,10 @@ .minfo("solving pred problem") .predSolve <- try(rxSolve(.model, .params, .nonmemData, returnType = "tibble", covsInterpolation="nocb", - atol=.rx$atol, rtol=.rx$rtol, - ssAtol=.rx$ssAtol, ssRtol=.rx$ssRtol, + addlKeepsCov=TRUE, addlDropSs=TRUE, ssAtDoseTime=TRUE, + safeZero=TRUE, ss2cancelAllPending=TRUE, + atol=.atol, rtol=.rtol, + ssAtol=.ssAtol, ssRtol=.ssRtol, addDosing = FALSE)) .minfo("done") if (!inherits(.predSolve, "try-error")) { diff --git a/R/zzz.R b/R/zzz.R index 32149319..35738fc1 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -3,9 +3,6 @@ if (requireNamespace("nlme", quietly=TRUE)) { rxode2::.s3register("nlme::getData", "nonmem2rx") } - if (requireNamespace("dplyr", quietly=TRUE)) { - rxode2::.s3register("dplyr::rename", "nonmem2rx") - } rxode2::.s3register("ggplot2::autoplot", "nonmem2rx") rxode2::.s3register("base::plot", "nonmem2rx") .rxUiGetRegister() diff --git a/man/nonmem2rx.Rd b/man/nonmem2rx.Rd index 7f62ea79..b4182f03 100644 --- a/man/nonmem2rx.Rd +++ b/man/nonmem2rx.Rd @@ -38,7 +38,8 @@ nonmem2rx( saveTime = getOption("nonmem2rx.saveTime", 15), overwrite = getOption("nonmem2rx.overwrite", TRUE), load = getOption("nonmem2rx.load", TRUE), - compress = getOption("nonmem2rx.compress", TRUE) + compress = getOption("nonmem2rx.compress", TRUE), + keep = getOption("nonmem2rx.keep", c("dfSub", "dfObs", "thetaMat", "sigma")) ) } \arguments{ @@ -180,6 +181,11 @@ compressed UI. If you are using this for simulation with old versions of rxode2, the compressed ui is not supported, so this should be \code{FALSE}. Otherwise use \code{TRUE} if you are using a newer rxode2.} + +\item{keep}{is a character vector of imported model items that are +kept in the model itself; The defaults is "sigma" which keeps the +sigma matrix in the model itself. You can add rxode2 solving +options that are imported from NONMEM to keep in the model.} } \value{ rxode2 function diff --git a/src/abbrev.c b/src/abbrev.c index c2a3b505..bde9383b 100644 --- a/src/abbrev.c +++ b/src/abbrev.c @@ -1068,11 +1068,11 @@ int abbrev_cmt_properties(char *name, int i, D_ParseNode *pn) { } if (abbrevLin == 1) { if (scaleCmt > 1) { - if (i == 0) Rf_warning("scale%d could be meaningless with this linCmt() model translation"); + if (i == 0) Rf_warning("scale0 could be meaningless with this linCmt() model translation"); } } else if (abbrevLin == 2) { if (scaleCmt > 2) { - if (i == 0) Rf_warning("scale%d could be meaningless with this linCmt() model translation"); + if (i == 0) Rf_warning("scale0 could be meaningless with this linCmt() model translation"); } } if (i == 0) { diff --git a/src/parseSyntaxErrors.h b/src/parseSyntaxErrors.h index c169b559..89b539fb 100644 --- a/src/parseSyntaxErrors.h +++ b/src/parseSyntaxErrors.h @@ -388,6 +388,6 @@ static inline void finalizeSyntaxError(void) { } char *v= rc_dup_str(firstErr.s, 0); sClear(&firstErr); - Rf_errorcall(R_NilValue, v); + Rf_errorcall(R_NilValue, "%s", v); } } diff --git a/tests/testthat/test-as-nonmem2rx.R b/tests/testthat/test-as-nonmem2rx.R index 4d71475c..472b4078 100644 --- a/tests/testthat/test-as-nonmem2rx.R +++ b/tests/testthat/test-as-nonmem2rx.R @@ -1,5 +1,6 @@ test_that("as.nonmem2rx", { skip_on_cran() + mod <- nonmem2rx(system.file("mods/cpt/runODE032.ctl", package="nonmem2rx"), determineError=FALSE, lst=".res", save=FALSE) diff --git a/tests/testthat/test-piping.R b/tests/testthat/test-piping.R index 82e7b0b0..cfb72e61 100644 --- a/tests/testthat/test-piping.R +++ b/tests/testthat/test-piping.R @@ -6,7 +6,8 @@ withr::with_options(list(nonmem2rx.save=FALSE, nonmem2rx.load=FALSE, nonmem2rx.o test_that("piping works", { skip_on_cran() - f <- .nonmem2rx(system.file("mods/cpt/runODE032.ctl", package="nonmem2rx"), lst=".res") + f <- .nonmem2rx(system.file("mods/cpt/runODE032.ctl", package="nonmem2rx"), lst=".res", + keep=NULL) expect_true(inherits(f, "nonmem2rx")) expect_false(is.null(f$nonmemData)) diff --git a/vignettes/articles/convert-nlmixr2.Rmd b/vignettes/articles/convert-nlmixr2.Rmd index 7656e031..f702872a 100644 --- a/vignettes/articles/convert-nlmixr2.Rmd +++ b/vignettes/articles/convert-nlmixr2.Rmd @@ -8,7 +8,7 @@ knitr::opts_chunk$set( comment = "#>" ) ``` -### Creating a nlmixr2 compatible model +### Creating a nlmixr2 compatible model Depending on the model, not all the residual specifications are translated to the `nlmixr2` style residuals. This means the model @@ -30,7 +30,7 @@ cp ~ prop(prop.sd) ``` Since the model when import has most of the translation done already, -you can easily tweak the model to have this form. +you can easily tweak the model to have this form. Here is the same example where the residual errors are not automatically translated to the `nlmixr2` parameter style (in this @@ -115,7 +115,7 @@ standard deviation estimate required by many estimation methods. Once you have a `rxode2()` model that: -- Qualifies against the NONMEM model, +- Qualifies against the NONMEM model, - Has `nlmixr2` compatible residuals @@ -123,6 +123,7 @@ You can then convert it to a `nlmixr2` fit object with `babelmixr2`: ```{r convertNlmixr2object} library(babelmixr2) + fit <- as.nlmixr2(new) fit diff --git a/vignettes/articles/create-vpc.Rmd b/vignettes/articles/create-vpc.Rmd index 57452a23..6b825209 100644 --- a/vignettes/articles/create-vpc.Rmd +++ b/vignettes/articles/create-vpc.Rmd @@ -31,7 +31,7 @@ mod <- nonmem2rx(ctlFile, lst=".res", save=FALSE) In this step, you convert the model to `nlmixr2` by `as.nlmixr2(mod)`; You may need to do a [little manual work to get the residual -specification to match between nlmixr2 and NONMEM](convert-nlmixr2.html). +specification to match between nlmixr2 and NONMEM](convert-nlmixr2.html). Once the residual specification is compatible with a nlmixr2 object, you can convert the model, `mod`, to a nlmixr2 fit object: @@ -51,11 +51,12 @@ them on a single plot: ```{r vpc} library(ggplot2) -p1 <- vpcPlot(fit, show=list(obs_dv=TRUE)); +p1 <- vpcPlot(fit, show=list(obs_dv=TRUE)) + p1 <- p1 + ylab("Concentrations") + rxode2::rxTheme() + xlab("Time (hr)") + - xgxr::xgx_scale_x_time_units("h", "h") + xgxr::xgx_scale_x_time_units("hour", "hour") p1a <- p1 + xgxr::xgx_scale_y_log10() @@ -64,7 +65,7 @@ p2 <- vpcPlot(fit, pred_corr = TRUE, show=list(obs_dv=TRUE)) p2 <- p2 + ylab("Prediction-Corrected Concentrations") + rxode2::rxTheme() + xlab("Time (hr)") + - xgxr::xgx_scale_x_time_units("h", "h") + xgxr::xgx_scale_x_time_units("hour", "hour") p2a <- p2 + xgxr::xgx_scale_y_log10()