R/mlxtran.R

Defines functions as.list.monolix2rxMlxtran print.monolix2rxMlxtran .unparsedMlxtran as.character.monolix2rxMlxtran mlxtran .mlxtranSubsubsection .mlxtranSubsection .mlxtranSection .mlxtranLine .mlxtranPasteLine .mlxtran .mlxtranFinalize .mlxtranParseItem .mlxtranIni

Documented in mlxtran

.mlxEnv <- new.env(parent=emptyenv())
#' Initialize the parsing environment
#'
#' @return nothing, called for side effects
#' @noRd
#' @author Matthew L. Fidler
.mlxtranIni <- function() {
  .mlxEnv$section <- NA_character_
  .mlxEnv$subsection <- NA_character_
  .mlxEnv$subsubsection <- NA_character_
  .mlxEnv$lst <- list(mlxtran="")
  .mlxEnv$isDesc <- FALSE
  .mlxEnv$desc <- ""
  .mlxEnv$parsedFile <- FALSE
}

#' This parses a single line from something like readLines
#'
#' @param l line to pars
#' @return nothing, called for side effects
#' @noRd
#' @author Matthew L. Fidler
.mlxtranParseItem <- function(l) {
  l <- stringi::stri_trans_general(l, "latin-ascii")
  l <- trimws(l)
  .begin <- 1
  .end <- .nc <- nchar(l)
  if (.nc >= 12) {
    if (substr(l, 1, 12) == "DESCRIPTION:") {
      .mlxEnv$isDesc <- TRUE
      if (.nc >= 14) {
        .mlxEnv$desc <- .mlxtranPasteLine(.mlxEnv$desc, trimws(substr(l, 13, .nc)))
      }
      return(invisible())
    }
  }
  .f <- substr(l, .begin, .begin)
  .e <- substr(l, .end, .end)
  if (.f == "<" && .e == ">") {
    .mlxtranSection(substr(l, .begin + 1, .end - 1))
    return(invisible())
  }
  if (.f == "[" && .e == "]") {
    .mlxtranSubsection(substr(l, .begin + 1, .end - 1))
    return(invisible())
  }
  if (.e == ":") {
    .sec <- substr(l, .begin, .end - 1)
    if (toupper(.sec) == .sec) {
      .mlxtranSubsubsection(.sec)
      return(invisible())
    }
  }
  .mlxtranLine(l)
  return(invisible())
}
#' Finalize monolix parsed object
#'
#' @param .ret object to finalize
#' @param equation boolean indicating if the equation should be parsed
#' @param update boolean indicating if the parameters should be updated.
#' @return nothing, called for side effects
#' @noRd
#' @author Matthew L. Fidler
.mlxtranFinalize <- function(.ret, equation=FALSE, update=FALSE) {
  if (!is.null(.ret$DATA_FORMATTING)) {
    if (!is.null(.ret$DATA_FORMATTING$FILEINFO$FILEINFO)) {
      .ret$DATA_FORMATTING$FILEINFO$FILEINFO <- .fileinfo(.ret$DATA_FORMATTING$FILEINFO$FILEINFO)
    }
    if (!is.null(.ret$DATA_FORMATTING$CONTENT$CONTENT)) {
      .ret$DATA_FORMATTING$CONTENT$CONTENT <- .content(.ret$DATA_FORMATTING$CONTENT$CONTENT)
    }
  }
  if (!is.null(.ret$PARAMETER)) {
    .ret$PARAMETER$PARAMETER <- .parameter(.ret$PARAMETER$PARAMETER)
  }
  if (!is.null(.ret$FIT)) {
    .ret$FIT$FIT <- .fit(.ret$FIT$FIT)
  }
  if (!is.null(.ret$DATAFILE)) {
    if (!is.null(.ret$DATAFILE$FILEINFO)) {
      .ret$DATAFILE$FILEINFO$FILEINFO <- .fileinfo(.ret$DATAFILE$FILEINFO$FILEINFO)
    }
    if (!is.null(.ret$DATAFILE$CONTENT)) {
      .ret$DATAFILE$CONTENT$CONTENT <- .content(.ret$DATAFILE$CONTENT$CONTENT)
    }
    if (!is.null(.ret$DATAFILE$SETTINGS)) {
      .ret$DATAFILE$SETTINGS$SETTINGS <- .dataSettings(.ret$DATAFILE$SETTINGS$SETTINGS)
    }
  }
  if (!is.null(.ret$MODEL)) {
    if (!is.null(.ret$MODEL$COVARIATE)) {
      if (!is.null(.ret$MODEL$COVARIATE$COVARIATE)) {
        .ret$MODEL$COVARIATE$COVARIATE <- .ind(.ret$MODEL$COVARIATE$COVARIATE)
      }
      if (!is.null(.ret$MODEL$COVARIATE$DEFINITION)) {
        .ret$MODEL$COVARIATE$DEFINITION <- .longDef(.ret$MODEL$COVARIATE$DEFINITION,
                                                    "<MODEL> [COVARIATE] DEFINITION:")
      }
      if (!is.null(.ret$MODEL$COVARIATE$EQUATION)) {
        .ret$MODEL$COVARIATE$EQUATION <- .covEq(.ret$MODEL$COVARIATE$EQUATION)
      }
    }
    if (!is.null(.ret$MODEL$INDIVIDUAL)) {
      .ret$MODEL$INDIVIDUAL$INDIVIDUAL <- .ind(.ret$MODEL$INDIVIDUAL$INDIVIDUAL)
      if (!is.null(.ret$MODEL$INDIVIDUAL$DEFINITION)) {
        .ret$MODEL$INDIVIDUAL$DEFINITION <- .indDef(.ret$MODEL$INDIVIDUAL$DEFINITION)
      }
    }
    if (!is.null(.ret$MODEL$LONGITUDINAL)) {
      .long <- .longitudinal(.ret$MODEL$LONGITUDINAL$LONGITUDINAL)
      if (.mlxEnv$parsedFile) {
        .long$file <- NULL
      }
      .ret$MODEL$LONGITUDINAL$LONGITUDINAL <- .long
      if (!is.null(.ret$MODEL$LONGITUDINAL$DEFINITION)) {
        .ld <- .longDef(.ret$MODEL$LONGITUDINAL$DEFINITION)
        .monolix2rx$endpointPred <- .getMonolixPreds(.ld)
        .ret$MODEL$LONGITUDINAL$DEFINITION <- .ld
      } else {
        .monolix2rx$endpointPred <- character(0)
      }
      if (!is.null(.ret$MODEL$LONGITUDINAL$PK)) {
        .ret$MODEL$LONGITUDINAL$PK <- .pk(.ret$MODEL$LONGITUDINAL$PK, TRUE)
      }
      if (!is.null(.ret$MODEL$LONGITUDINAL$OUTPUT)) {
        .ret$MODEL$LONGITUDINAL$OUTPUT <- .longOut(.ret$MODEL$LONGITUDINAL$OUTPUT)
      }
      if (equation && !is.null(.ret$MODEL$LONGITUDINAL$EQUATION)) {
        .ret$MODEL$LONGITUDINAL$EQUATION <- .equation(.ret$MODEL$LONGITUDINAL$EQUATION,
                                                      .ret$MODEL$LONGITUDINAL$PK)
      }
    }
    if (!is.null(.ret$MODEL$POPULATION)) {
      if (!is.null(.ret$MODEL$POPULATION$DEFINITION)) {
        .ret$MODEL$POPULATION$DEFINITION <- .popDef(.ret$MODEL$POPULATION$DEFINITION)
      }
    }
  }
  if (!is.null(.ret$MONOLIX)) {
    if (!is.null(.ret$MONOLIX$SETTINGS)) {
      if (!is.null(.ret$MONOLIX$SETTINGS$GLOBAL)) {
        .ret$MONOLIX$SETTINGS$GLOBAL <- .mlxtranOp(.ret$MONOLIX$SETTINGS$GLOBAL, "<MONOLIX> [SETTINGS] GLOBAL:")
      }
      if (!is.null(.ret$MONOLIX$SETTINGS$POPULATION)) {
        .ret$MONOLIX$SETTINGS$POPULATION <- .mlxtranOp(.ret$MONOLIX$SETTINGS$POPULATION, "<MONOLIX> [SETTINGS] POPULATION:")
      }
      if (!is.null(.ret$MONOLIX$SETTINGS$LL)) {
        .ret$MONOLIX$SETTINGS$LL <- .mlxtranOp(.ret$MONOLIX$SETTINGS$LL, "<MONOLIX> [SETTINGS] LL:")
      }
      if (!is.null(.ret$MONOLIX$SETTINGS$INDIVIDUAL)) {
        .ret$MONOLIX$SETTINGS$INDIVIDUAL <- .mlxtranOp(.ret$MONOLIX$SETTINGS$INDIVIDUAL, "<MONOLIX> [SETTINGS] INDIVIDUAL:")
      }
      if (!is.null(.ret$MONOLIX$SETTINGS$REPORTING)) {
        .ret$MONOLIX$SETTINGS$REPORTING <- .mlxtranOp(.ret$MONOLIX$SETTINGS$REPORTING, "<MONOLIX> [SETTINGS] REPORTING:")
      }
    }
    if (!is.null(.ret$MONOLIX$TASKS$TASKS)) {
      .ret$MONOLIX$TASKS$TASKS <- .task(.ret$MONOLIX$TASKS$TASKS)
    }
  }
  if (update && !is.null(.ret$PARAMETER)) {
    .ret <- .parameterUpdate(.ret)
  }
  .ret <- .mlxtranCov(.ret)
  attr(.ret, "desc") <- .mlxEnv$desc
  class(.ret) <- "monolix2rxMlxtran"
  .ret
}
#' This applies mlxtran to a set of lines
#'
#' @param lines a character vector representing a set of lines to parse
#' @param equation when TRUE, try to parse equation too
#' @param update when TRUE, try to update the initial estimates to the final estimates
#' @return a mlxtran object
#' @noRd
#' @author Matthew L. Fidler
.mlxtran <- function(lines, equation=FALSE,
                     update=FALSE) {
  .mlxtranIni()
  lapply(lines, .mlxtranParseItem)
  # Add file entries
  if (!is.null(.mlxEnv$lst$MODEL$LONGITUDINAL)) {
    .long <- .longitudinal(.mlxEnv$lst$MODEL$LONGITUDINAL$LONGITUDINAL)
    .file <- .long$file
    .mlxEnv$parsedFile <- FALSE
    mlxTxt(.long$file,  retFile=TRUE)
    if (.mlxEnv$parsedFile) {
      .minfo(paste0("integrated model file '", .file ,"' into mlxtran object"))
    }
  }
  .mlxtranFinalize(.mlxEnv$lst, equation=equation, update=update)
}
#' Paste together lines, ignoring empty ones and adding \n between substantial lines
#'
#' @param prior prior line
#' @param new new line
#' @return prior \n new (or prior / new depending on the simplicity of the lines)
#' @noRd
#' @author Matthew L. Fidler
.mlxtranPasteLine <- function(prior, new) {
  if (prior == "") return(new)
  if (new == "") return(prior)
  paste0(prior, "\n", new)
}
#' This handles a non-section line for a mlxtran file
#'
#' @param line line to put in the right place in the lst
#' @return nothing, called for side effects
#' @noRd
#' @author Matthew L. Fidler
.mlxtranLine <- function(line) {
  if (.mlxEnv$isDesc) {
    .mlxEnv$desc <- .mlxtranPasteLine(.mlxEnv$desc, line)
    return(invisible())
  }
  .s <- .mlxEnv$section
  .ss <- .mlxEnv$subsection
  .sss <- .mlxEnv$subsubsection
  if (is.na(.s)) {
    .mlxEnv$lst$mlxtran <- paste0(.mlxEnv$lst$mlxtran, "\n", line)
    return(invisible())
  }
  if (is.null(.mlxEnv$lst[[.s]])) {
    .mlxEnv$lst[[.s]] <- list()
    .mlxEnv$lst[[.s]][[.s]] <- ""
  }
  if (is.na(.ss)) {
    .mlxEnv$lst[[.s]][[.s]] <- .mlxtranPasteLine(.mlxEnv$lst[[.s]][[.s]], line)
    return(invisible())
  }
  if (is.null(.mlxEnv$lst[[.s]][[.ss]])) {
    .mlxEnv$lst[[.s]][[.ss]] <- list()
    .mlxEnv$lst[[.s]][[.ss]][[.ss]] <- ""
  }
  if (is.na(.sss)) {
    .mlxEnv$lst[[.s]][[.ss]][[.ss]] <- .mlxtranPasteLine(.mlxEnv$lst[[.s]][[.ss]][[.ss]], line)
    return(invisible())
  }
  if (is.null(.mlxEnv$lst[[.s]][[.ss]][[.sss]])) {
    .mlxEnv$lst[[.s]][[.ss]][[.sss]] <- ""
  }
  .mlxEnv$lst[[.s]][[.ss]][[.sss]] <- .mlxtranPasteLine(.mlxEnv$lst[[.s]][[.ss]][[.sss]], line)
  return(invisible())
}
#' This handles the section text when it encounters it
#'
#' @param sec section text
#' @return nothing, called for side effects
#' @noRd
#' @author Matthew L. Fidler
.mlxtranSection <- function(sec) {
  .mlxEnv$section <- sec
  .mlxEnv$subsection <- NA_character_
  .mlxEnv$subsubsection <- NA_character_
  .mlxEnv$isDesc <- FALSE
}
#' This handles the subsection text when it encounters it
#'
#' @param sec subsection text
#' @return nothing, called for side effects
#' @noRd
#' @author Matthew L. Fidler
.mlxtranSubsection <- function(sec) {
  .mlxEnv$subsection <- sec
  .mlxEnv$subsubsection <- NA_character_
  .mlxEnv$isDesc <- FALSE
}
#' This handles the sub-subsection text
#'
#' @param sec sub-subsection text
#' @return nothing, called for side effects
#' @noRd
#' @author Matthew L. Fidler
.mlxtranSubsubsection <- function(sec) {
  if (sec == "DESCRIPTION") {
    .mlxEnv$isDesc <- TRUE
    return(invisible())
  }
  .mlxEnv$subsubsection <- sec
  .mlxEnv$isDesc <- FALSE
}
#' Read and parse mlxtran lines
#'
#' @param file mlxtran file to process
#' @param equation parse the equation block to rxode2 (some models cannot be translated)
#' @param update when true, try to update the parameter block to the final parameter estimates
#' @return mlxtran object
#' @export
#' @author Matthew L. Fidler
#' @examples
#' # First load in the model; in this case the theo model
#' # This is modified from the Monolix demos by saving the model
#' # File as a text file (hence you can access without model library)
#' # setup.
#' #
#' # This example is also included in the monolix2rx package, so
#' # you refer to the location with `system.file()`:
#'
#' pkgTheo <- system.file("theo", package="monolix2rx")
#'
#' mlx <- mlxtran(file.path(pkgTheo, "theophylline_project.mlxtran"))
#'
#' mlx
mlxtran <- function(file, equation=FALSE, update=FALSE) {
  checkmate::assertLogical(equation, any.missing=FALSE, len=1)
  checkmate::assertLogical(update, any.missing=FALSE, len=1)
  on.exit({
    .Call(`_monolix2rx_r_parseFree`)
  })
  if (inherits(file, "monolix2rxMlxtran")) {
    .monolix2rx$endpointPred <- .getMonolixPreds(file)
    if (equation && !is.null(file$MODEL$LONGITUDINAL$EQUATION)) {
      file$MODEL$LONGITUDINAL$EQUATION <- .equation(file$MODEL$LONGITUDINAL$EQUATION,
                                                    file$MODEL$LONGITUDINAL$PK)
    }
    if (update && !is.null(file$PARAMETER)) {
      file <- .parameterUpdate(file)
      file <- .mlxtranCov(file)
    }
    return(file)
  }
  if (length(file) > 1L) {
    .lines <- file
    .dirn <- getwd()
  } else {
    if (checkmate::testFileExists(file, access="r", extension="txt")) {
      return(mlxTxt(file))
    }
    checkmate::assertFileExists(file, access="r", extension="mlxtran")
    .lines <- suppressWarnings(readLines(file))
    .dirn <- dirname(file)
  }
  .ret <- withr::with_dir(.dirn,
                          .mlxtran(.lines, equation=equation, update=update))
  attr(.ret, "dirn") <- .dirn
  .ret
}

#' @export
as.character.monolix2rxMlxtran <- function(x, ...) {
  .env <- new.env(parent=emptyenv())
  .env$catText <- FALSE
  .desc <- attr(x, "desc")
  .ret <- character(0)
  if (.desc != "") {
    .ret <- c(.ret, "DESCRIPTION:")
    .ret <- c(.ret, .desc, "")
  }
  .env$ret <- .ret
  lapply(names(x), function(ns) {
    if (ns != "mlxtran") {
      .env$ret <- c(.env$ret,
                    ifelse(.env$catText, "", character(0)),
                    paste0("<", ns, ">"))
      .env$catText <- FALSE
    }
    .sec <- x[[ns]]
    lapply(names(.sec), function(nss) {
      if (ns != nss) {
        .env$ret <- c(.env$ret,
                      ifelse(.env$catText, "", character(0)),
                      paste0("[", nss, "]"))
        .env$catText <- FALSE
      }
      .subsec <- .sec[[nss]]
      .cls <- class(.subsec)
      if (length(.cls) == 1L) {
        if (.cls == "character") {
          if (.subsec == "") return(invisible())
          .env$catText <- TRUE
          .ret <- c(.ret,
                    .subsec)
          return(invisible())
        } else if (.cls == "list") {
          lapply(names(.subsec), function(nsss){
            .subsubsec <- .subsec[[nsss]]
            if (nss != nsss) {
              .env$ret <- c(.env$ret,
                            ifelse(.env$catText, "", character(0)),
                            paste0(nsss, ":"))
              .env$catText <- FALSE
            }
            .cls <- class(.subsubsec)
            if (length(.cls) == 1L) {
              if (.cls == "character") {
                if (.subsubsec == "") return(invisible())
                .env$catText <- TRUE
                .env$ret <- c(.env$ret,
                              ifelse(.env$catText, "", character(0)),
                              .subsubsec)
                return(invisible())
              }
            }
            .env$catText <- TRUE
            .env$ret <- c(.env$ret,
                          paste0('; parsed: $', ns, "$", nss,"$", nsss),
                          as.character(.subsubsec))
          })
          return(invisible())
        }
      }
      .env$catText <- TRUE
      .env$ret <- c(.env$ret,
                    paste0('; parsed: $', ns, "$", nss),
                    as.character(.subsec))
    })
  })
  .up <- .unparsedMlxtran(x, ...)
  if (length(.up) > 0) .up <- c("", "; unparsed sections:", paste0(";  $", .up))
  c(.env$ret[!is.na(.env$ret)], .up)
}
#' This is a method to print out unparsed sections of the mlxtran object
#'
#' @param x character string
#' @param ... other arguments
#' @return unparsed character string
#' @noRd
#' @author Matthew L. Fidler
.unparsedMlxtran <- function(x, ...) {
  .env <- new.env(parent=emptyenv())
  .desc <- attr(x, "desc")
  .ret <- character(0)
  .env$ret <- .ret
  lapply(names(x), function(ns) {
    .sec <- x[[ns]]
    lapply(names(.sec), function(nss) {
      .subsec <- .sec[[nss]]
      .cls <- class(.subsec)
      if (length(.cls) == 1L) {
        if (.cls == "character") {
          if (.subsec == "") return(invisible())
          .env$ret <- c(.env$ret,
                        paste0(ns, "$", nss))
          return(invisible())
        } else if (inherits(.subsec, "data.frame")) {
        } else if (.cls == "list") {
          lapply(names(.subsec), function(nsss){
            .subsubsec <- .subsec[[nsss]]
            .cls <- class(.subsubsec)
            if (length(.cls) == 1L) {
              if (.cls == "character") {
                if (.subsubsec == "") return(invisible())
                .env$ret <- c(.env$ret,
                              paste0(ns, "$", nss,"$", nsss))
                return(invisible())
              }
            }
          })
          return(invisible())
        }
      }
      if (length(.cls) == 1L) {
        .env$ret <- c(.env$ret,
                      paste0(ns, "$", nss))

      }
    })
  })
  .env$ret
}


#' @export
print.monolix2rxMlxtran <- function(x, ...) {
  cat(paste(as.character.monolix2rxMlxtran(x, ...), collapse="\n"), "\n", sep="")
  invisible(x)
}

#' @export
as.list.monolix2rxMlxtran <- function(x, ...) {
  .n <- names(x)
  .x <- setNames(lapply(.n,
               function(n) {
                 .y <- x[[n]]
                 if (is.list(.y)) {
                   .n2 <- names(.y)
                   .y <- setNames(
                     lapply(.n2,
                            function(n2) {
                              .z <- .y[[n2]]
                              if (inherits(.z, "data.frame")) {
                                return(as.data.frame(.z))
                              } else {
                                .z <- as.list(.z)
                                .n3 <- names(.z)
                                .z <- setNames(
                                  lapply(.n3,
                                         function(n3) {
                                           .w <- .z[[n3]]
                                           if (inherits(.w, "data.frame")) {
                                             return(as.data.frame(.w))
                                           }
                                           return(as.list(.w))
                                         }),
                                  .n3
                                )
                                return(.z)
                              }
                            }), .n2)
                 }
                 .y
               }), .n)
  class(.x) <- NULL
  .x
}

Try the monolix2rx package in your browser

Any scripts or data that you put into this service are public.

monolix2rx documentation built on April 4, 2025, 3:54 a.m.