From 40edc91312074c0b87b502fa15fd61e958b738c7 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Tue, 25 Jun 2024 13:47:53 -0400 Subject: [PATCH 01/31] new run_nmtran function for checking tha validity of a model submission --- R/run-nmtran.R | 179 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 R/run-nmtran.R diff --git a/R/run-nmtran.R b/R/run-nmtran.R new file mode 100644 index 000000000..ebdd38dc7 --- /dev/null +++ b/R/run-nmtran.R @@ -0,0 +1,179 @@ + +#' Run NMTRAN on a model object +#' +#' @param .mod a `bbr` model object +#' @param .config_path Path to a bbi configuration file. If `NULL`, the +#' default, will attempt to use a `bbi.yaml` in the same directory as the +#' model. +#' @param nmtran_exe Path to an `NMTRAN` executable. If `NULL`, will look for a +#' `bbi.yaml` file in the same directory as the model. +#' @param delete_on_exit Logical. If `FALSE`, don't delete the temporary folder +#' containing the `NMTRAN` run. +#' +#' @examples +#' \dontrun{ +#' mod <- read_model(file.path(MODEL_DIR, 1)) +#' run_nmtran(mod) +#' +#' # Set the path to an NMTRAN executable +#' run_nmtran(mod, nmtran_exe = "/opt/NONMEM/nm75/tr/NMTRAN.exe") +#' } +#' +#' @export +run_nmtran <- function( + .mod, + .config_path = NULL, + nmtran_exe = NULL, + delete_on_exit = TRUE +){ + test_nmrec_version(.min_version = "0.3.0") + check_model_object(.mod, "bbi_nonmem_model") + + # Capture NONMEM and NMTRAN options + nmtran_exe <- locate_nmtran(.mod, .config_path, nmtran_exe) + nm_ver <- attr(nmtran_exe, "nonmem_version") + + mod_path <- get_model_path(.mod) + data_path <- get_data_path_from_ctl(.mod) + + # make temporary directory in current directory + mod_name <- fs::path_ext_remove(basename(mod_path)) + temp_folder <- paste0("nmtran_", mod_name, "_", basename(tempdir())) + dir.create(temp_folder) + if(isTRUE(delete_on_exit)){ + on.exit(unlink(temp_folder, recursive = TRUE, force = TRUE)) + } + + # Copy model + file.copy(mod_path, temp_folder, overwrite = TRUE) + nmtran_mod <- new_model(file.path(temp_folder, basename(mod_path)), .overwrite = TRUE) + + # Copy dataset & overwrite $DATA record of new model + # NMTRAN will error if data cannot be found + if(fs::file_exists(data_path)){ + file.copy(data_path, temp_folder, overwrite = TRUE) + # overwrite $DATA record of new model + modify_data_path_ctl(nmtran_mod, data_path = basename(data_path)) + } + + # Run NMTRAN + nmtran_results <- c( + list( + nmtran_exe = as.character(nmtran_exe), + nonmem_version = nm_ver, + absolute_model_path = mod_path + ), + execute_nmtran(nmtran_exe, mod_path = basename(mod_path), dir = temp_folder) + ) + + # assign class and return + class(nmtran_results) <- c(NMTRAN_PROCESS_CLASS, class(nmtran_results)) + return(nmtran_results) +} + + +#' Search for and validate existence of an `NMTRAN` executable +#' +#' If `nmtran_exe = NULL`, this will look for a `bbi.yaml` file in the same +#' directory as the model. +#' +#' @inheritParams run_nmtran +#' +#' @keywords internal +locate_nmtran <- function(.mod = NULL, .config_path = NULL, nmtran_exe = NULL){ + + if(is.null(nmtran_exe)){ + if(!is.null(.mod)){ + check_model_object(.mod, "bbi_nonmem_model") + model_dir <- get_model_working_directory(.mod) + } + config_path <- .config_path %||% file.path(model_dir, "bbi.yaml") + + if(!file_exists(config_path)){ + rlang::abort( + c( + "x" = "No bbi configuration was found in the execution directory.", + "i" = "Please run `bbi_init()` with the appropriate directory to continue." + ) + ) + } + + if(!is.null(.config_path)){ + config_path <- normalizePath(.config_path) + } + + bbi_config <- yaml::read_yaml(config_path) + nm_config <- bbi_config$nonmem + + # look for default nonmem installation + default_nm <- purrr::keep(nm_config, function(nm_ver){ + !is.null(nm_ver$default) + }) + + # Set nonmem path + if(length(default_nm) > 0){ + default_nm <- default_nm[[1]] + }else{ + # If no default, use the last one (likely higher version) + default_nm <- nm_config[[length(nm_config)]] + } + + # Set NMTRAN executable path + nm_path <- default_nm$home + # TODO: should we recursively look for this executable, or assume Metworx? + # i.e. can we assume NMTRAN is in a `tr` folder, and is called `NMTRAN.exe`? + nmtran_exe <- file.path(nm_path, "tr", "NMTRAN.exe") + + # If executable found via bbi.yaml, append NONMEM version as attribute + attr(nmtran_exe, "nonmem_version") <- basename(default_nm$home) + } + + if(!file_exists(nmtran_exe)){ + stop(glue("Could not find an NMTRAN executable at `{nmtran_exe}`")) + } + + return(nmtran_exe) +} + + +#' Execute NMTRAN in a given directory +#' +#' @param nmtran_exe Path to `NMTRAN` executable. +#' @param mod_path Path of a model to evaluate. Should be relative to `dir`. +#' @param dir Directory in which to execute the command. +#' +#' @keywords internal +execute_nmtran <- function(nmtran_exe, mod_path, dir = NULL) { + if(is.null(dir)) dir <- "." + checkmate::assert_directory_exists(dir) + + nmtran.p <- processx::process$new( + command = nmtran_exe, args = mod_path, wd = dir, + stdout = "|", stderr="|", stdin = file.path(dir, mod_path) + ) + + # Wait till finished for status to be reflective of result + nmtran.p$wait() + + # Assign status + status <- "Not Run" + status_val <- nmtran.p$get_exit_status() + if(status_val == 0){ + status <- "NMTRAN successful" + }else if(status_val == 4){ + status <- "NMTRAN failed. See errors." + }else{ + dev_error("NMTRAN exit status other than 0 or 4") + } + + # Tabulate NMTRAN results + nmtran_results <- list( + nmtran_model = nmtran.p$get_input_file(), + run_dir = as.character(fs::path_real(dir)), + status = status, status_val = status_val, + output_lines = nmtran.p$read_all_output_lines(), + error_lines = nmtran.p$read_all_error_lines() + ) + + return(nmtran_results) +} From 2fbd57a3b4e94ae8336b6b10166f772b4b847cf4 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Tue, 25 Jun 2024 13:48:13 -0400 Subject: [PATCH 02/31] new print method for nmtran_process --- R/aaa.R | 1 + R/print.R | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/R/aaa.R b/R/aaa.R index fd73ea989..105a70fb1 100644 --- a/R/aaa.R +++ b/R/aaa.R @@ -209,6 +209,7 @@ RUN_LOG_CLASS <- "bbi_run_log_df" CONF_LOG_CLASS <- "bbi_config_log_df" SUM_LOG_CLASS <- "bbi_summary_log_df" LOG_DF_CLASS <- "bbi_log_df" +NMTRAN_PROCESS_CLASS <- "nmtran_process" # YAML keys that are hard-coded YAML_YAML_MD5 <- "yaml_md5" diff --git a/R/print.R b/R/print.R index 9e6f9d158..11f6e7966 100644 --- a/R/print.R +++ b/R/print.R @@ -442,6 +442,65 @@ print.bbi_nmboot_summary <- function(x, .digits = 3, .nrow = 10, ...) { } } + +#' @describeIn print_bbi Prints the `NMTRAN` evaluation of a `bbi_model` model +#' @export +print.nmtran_process <- function(x, ...){ + + is_valid_print <- function(.x) { + if (!is.null(.x)) { + length(.x) != 0 + } else { + FALSE + } + } + + heading <- cli_h1 + subheading <- cli_h2 + bullet_list <- cat_bullet + + status <- x[["status"]] + if (x[["status_val"]] == 0) { + status <- col_green(status) + } else { + status <- col_red(status) + } + + nm_version <- x[["nonmem_version"]] + if(is.null(nm_version)) nm_version <- "unknown" + + heading('Status') + subheading(status) + + heading("Absolute Model Path") + bullet_list(x[[ABS_MOD_PATH]]) + + heading("NMTRAN Specifications") + cli::cli_bullets( + c( + "*" = paste0("NMTRAN Executable: {.path ", x[["nmtran_exe"]], "}"), + "*" = paste0("NONMEM Version: {.val ", nm_version, "}"), + "*" = paste0("Run Directory: {.path ", x[["run_dir"]], "}") + ) + ) + + if (is_valid_print(x[["output_lines"]])) { + heading('Output') + cat_line(style_italic(x[["output_lines"]])) + } + + if (is_valid_print(x[["error_lines"]])) { + heading('Additional Errors') + cat_line(style_italic(x[["error_lines"]])) + } + + # format and print status string + if (is_valid_print(x[["output_lines"]])) { + cat_line("Process finished.", col = "green") + } +} + + #' @describeIn print_bbi Draw model tree as a static plot #' @param x plot to display #' @param newpage Logical (T/F). If `TRUE`, draw new (empty) page first. @@ -471,7 +530,6 @@ print.model_tree_static <- function(x, newpage = is.null(vp), vp = NULL, ...){ return(invisible(x)) } - ##################### # INTERNAL HELPERS ##################### From aa78cbbbd7ab966c292c4c3d1b449610117c64af Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Tue, 25 Jun 2024 13:48:48 -0400 Subject: [PATCH 03/31] document and add tests --- NAMESPACE | 2 + man/execute_nmtran.Rd | 19 ++++++ man/locate_nmtran.Rd | 23 +++++++ man/print_bbi.Rd | 5 ++ man/run_nmtran.Rd | 34 +++++++++++ tests/testthat/test-workflow-bbi.R | 98 ++++++++++++++++++++++++++++++ 6 files changed, 181 insertions(+) create mode 100644 man/execute_nmtran.Rd create mode 100644 man/locate_nmtran.Rd create mode 100644 man/run_nmtran.Rd diff --git a/NAMESPACE b/NAMESPACE index 314a32e8b..6c09f664d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -76,6 +76,7 @@ S3method(print,bbi_nmboot_summary) S3method(print,bbi_nonmem_summary) S3method(print,bbi_process) S3method(print,model_tree_static) +S3method(print,nmtran_process) S3method(print_model_files,default) S3method(submit_model,bbi_base_model) S3method(submit_model,bbi_nmboot_model) @@ -194,6 +195,7 @@ export(replace_model_field) export(replace_note) export(replace_tag) export(run_log) +export(run_nmtran) export(setup_bootstrap_run) export(submit_model) export(submit_models) diff --git a/man/execute_nmtran.Rd b/man/execute_nmtran.Rd new file mode 100644 index 000000000..50d578dfb --- /dev/null +++ b/man/execute_nmtran.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/run-nmtran.R +\name{execute_nmtran} +\alias{execute_nmtran} +\title{Execute NMTRAN in a given directory} +\usage{ +execute_nmtran(nmtran_exe, mod_path, dir = NULL) +} +\arguments{ +\item{nmtran_exe}{Path to \code{NMTRAN} executable.} + +\item{mod_path}{Path of a model to evaluate. Should be relative to \code{dir}.} + +\item{dir}{Directory in which to execute the command.} +} +\description{ +Execute NMTRAN in a given directory +} +\keyword{internal} diff --git a/man/locate_nmtran.Rd b/man/locate_nmtran.Rd new file mode 100644 index 000000000..a1aeb3571 --- /dev/null +++ b/man/locate_nmtran.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/run-nmtran.R +\name{locate_nmtran} +\alias{locate_nmtran} +\title{Search for and validate existence of an \code{NMTRAN} executable} +\usage{ +locate_nmtran(.mod = NULL, .config_path = NULL, nmtran_exe = NULL) +} +\arguments{ +\item{.mod}{a \code{bbr} model object} + +\item{.config_path}{Path to a bbi configuration file. If \code{NULL}, the +default, will attempt to use a \code{bbi.yaml} in the same directory as the +model.} + +\item{nmtran_exe}{Path to an \code{NMTRAN} executable. If \code{NULL}, will look for a +\code{bbi.yaml} file in the same directory as the model.} +} +\description{ +If \code{nmtran_exe = NULL}, this will look for a \code{bbi.yaml} file in the same +directory as the model. +} +\keyword{internal} diff --git a/man/print_bbi.Rd b/man/print_bbi.Rd index bc3122306..50ee3fe8a 100644 --- a/man/print_bbi.Rd +++ b/man/print_bbi.Rd @@ -6,6 +6,7 @@ \alias{print.bbi_model} \alias{print.bbi_nonmem_summary} \alias{print.bbi_nmboot_summary} +\alias{print.nmtran_process} \alias{print.model_tree_static} \title{Print methods for bbr objects} \usage{ @@ -17,6 +18,8 @@ \method{print}{bbi_nmboot_summary}(x, .digits = 3, .nrow = 10, ...) +\method{print}{nmtran_process}(x, ...) + \method{print}{model_tree_static}(x, newpage = is.null(vp), vp = NULL, ...) } \arguments{ @@ -59,6 +62,8 @@ will make for prettier formatting, especially of table outputs. \item \code{print(bbi_nmboot_summary)}: Prints a high level summary of a model from a \code{bbi_nmboot_summary} object +\item \code{print(nmtran_process)}: Prints the \code{NMTRAN} evaluation of a \code{bbi_model} model + \item \code{print(model_tree_static)}: Draw model tree as a static plot }} diff --git a/man/run_nmtran.Rd b/man/run_nmtran.Rd new file mode 100644 index 000000000..f6e71ea73 --- /dev/null +++ b/man/run_nmtran.Rd @@ -0,0 +1,34 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/run-nmtran.R +\name{run_nmtran} +\alias{run_nmtran} +\title{Run NMTRAN on a model object} +\usage{ +run_nmtran(.mod, .config_path = NULL, nmtran_exe = NULL, delete_on_exit = TRUE) +} +\arguments{ +\item{.mod}{a \code{bbr} model object} + +\item{.config_path}{Path to a bbi configuration file. If \code{NULL}, the +default, will attempt to use a \code{bbi.yaml} in the same directory as the +model.} + +\item{nmtran_exe}{Path to an \code{NMTRAN} executable. If \code{NULL}, will look for a +\code{bbi.yaml} file in the same directory as the model.} + +\item{delete_on_exit}{Logical. If \code{FALSE}, don't delete the temporary folder +containing the \code{NMTRAN} run.} +} +\description{ +Run NMTRAN on a model object +} +\examples{ +\dontrun{ +mod <- read_model(file.path(MODEL_DIR, 1)) +run_nmtran(mod) + +# Set the path to an NMTRAN executable +run_nmtran(mod, nmtran_exe = "/opt/NONMEM/nm75/tr/NMTRAN.exe") +} + +} diff --git a/tests/testthat/test-workflow-bbi.R b/tests/testthat/test-workflow-bbi.R index 9ab8f6837..afb318117 100644 --- a/tests/testthat/test-workflow-bbi.R +++ b/tests/testthat/test-workflow-bbi.R @@ -305,5 +305,103 @@ withr::with_options(list( run_times <- model_summaries(mods) %>% check_run_times(.wait = FALSE) %>% suppressWarnings() expect_equal(dim(run_times), c(3, 3)) }) + + describe("run_nmtran", { + it("locate_nmtran", { + mod1 <- read_model(file.path(MODEL_DIR_BBI, "1")) + # Using model object, looks for bbi.yaml + nmtran_exe <- locate_nmtran(mod1) + # Confirm executable + expect_equal(as.character(nmtran_exe), "/opt/NONMEM/nm74gf/tr/NMTRAN.exe") + # Confirm NONMEM version + expect_equal(attr(nmtran_exe, "nonmem_version"), "nm74gf") + + # Passed executable + nmtran_exe <- locate_nmtran(mod1, nmtran_exe = "/opt/NONMEM/nm74gf/tr/NMTRAN.exe") + # Confirm executable + expect_equal(as.character(nmtran_exe), "/opt/NONMEM/nm74gf/tr/NMTRAN.exe") + # Confirm NONMEM version + expect_true(is.null(attr(nmtran_exe, "nonmem_version"))) + + # Passed config_path + nmtran_exe <- locate_nmtran(.config_path = file.path(MODEL_DIR_BBI, "bbi.yaml")) + # Confirm executable + expect_equal(as.character(nmtran_exe), "/opt/NONMEM/nm74gf/tr/NMTRAN.exe") + # Confirm NONMEM version + expect_equal(attr(nmtran_exe, "nonmem_version"), "nm74gf") + + # Wrong nmtran_exe path passed + expect_error( + locate_nmtran(mod1, nmtran_exe = "/opt/NONMEM/nm74gf/tr/NMTRAN2.exe"), + "Could not find an NMTRAN executable" + ) + + # no configuration file found + expect_error( + locate_nmtran(.config_path = file.path(tempdir(), "bbi.yaml")), + "No bbi configuration was found" + ) + }) + + it("execute_nmtran", { + # Execute in subdirectory to avoid messing with other tests + nmtran_dir <- file.path(MODEL_DIR_BBI, "nmtran") + fs::dir_create(nmtran_dir) + on.exit(fs::dir_delete(nmtran_dir), add = TRUE) + + # Copy model file into new model dir + fs::file_copy(CTL_TEST_FILE, nmtran_dir, overwrite = TRUE) + mod1 <- new_model(file.path(nmtran_dir, "1"), .overwrite = TRUE) + + # create new bbi.yaml + bbi_init( + nmtran_dir, + .nonmem_dir = Sys.getenv("BBR_TESTS_NONMEM_DIR", "/opt/NONMEM"), + .nonmem_version = Sys.getenv("BBR_TESTS_NONMEM_VERSION", "nm74gf"), + .bbi_args = list(mpi_exec_path = get_mpiexec_path()) + ) + + nmtran_exe <- locate_nmtran(mod1) + nmtran_results <- execute_nmtran( + nmtran_exe, mod_path = basename(get_model_path(mod1)), dir = nmtran_dir + ) + + # Check attributes + expect_equal(nmtran_dir, nmtran_results$run_dir) + expect_equal(nmtran_results$status_val, 0) + expect_equal(nmtran_results$status, "NMTRAN successful") + + # Test failure + data_path <- "test/this/path/data.csv" + modify_data_path_ctl(mod1, data_path) + + nmtran_results <- execute_nmtran( + nmtran_exe, mod_path = basename(get_model_path(mod1)), dir = nmtran_dir + ) + + # Check attributes + expect_equal(nmtran_results$status_val, 4) + expect_equal(nmtran_results$status, "NMTRAN failed. See errors.") + }) + + it("run_nmtran", { + # create model + mod1 <- read_model(file.path(MODEL_DIR_BBI, "1")) + + nmtran_results <- run_nmtran(mod1, delete_on_exit = FALSE) + on.exit(fs::dir_delete(nmtran_results$run_dir)) + + # Check attributes + expect_equal(get_model_path(mod1), nmtran_results$absolute_model_path) + expect_equal( + file.path(nmtran_results$run_dir, basename(get_model_path(mod1))), + nmtran_results$nmtran_model + ) + expect_equal(nmtran_results$nonmem_version, "nm74gf") + expect_equal(nmtran_results$status_val, 0) + expect_equal(nmtran_results$status, "NMTRAN successful") + }) + }) + }) # closing withr::with_options From 9f933cece84529ce16b961509a1dedef1712b5d2 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Tue, 25 Jun 2024 14:24:21 -0400 Subject: [PATCH 04/31] remove check for minimum version of nmrec - we now require nmrec >=0.3.0 --- R/run-nmtran.R | 1 - 1 file changed, 1 deletion(-) diff --git a/R/run-nmtran.R b/R/run-nmtran.R index ebdd38dc7..bebbfa241 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -26,7 +26,6 @@ run_nmtran <- function( nmtran_exe = NULL, delete_on_exit = TRUE ){ - test_nmrec_version(.min_version = "0.3.0") check_model_object(.mod, "bbi_nonmem_model") # Capture NONMEM and NMTRAN options From 51273bb07dda73c96f73cf0fb0fc407a4a181706 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Fri, 28 Jun 2024 12:21:27 -0400 Subject: [PATCH 05/31] add run_nmtran to pkgdown site --- _pkgdown.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/_pkgdown.yml b/_pkgdown.yml index 796d32d1b..12cead396 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -27,6 +27,7 @@ reference: contents: - submit_model - submit_models + - run_nmtran - print_bbi_args - title: Model summary and outputs From c2110599232770753102d65d21cc2d56595505b3 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Wed, 10 Jul 2024 11:35:08 -0400 Subject: [PATCH 06/31] fix: status value handling - In testing this feature out in more scenarios surrounding data setup, I noticed a status value of 8 was possible, and pointed to a particular error type. Rather than attempting to categorize each error type, just report a failure for anything other than an exit status of `0` --- R/run-nmtran.R | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/R/run-nmtran.R b/R/run-nmtran.R index bebbfa241..0ef1e8a30 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -159,10 +159,8 @@ execute_nmtran <- function(nmtran_exe, mod_path, dir = NULL) { status_val <- nmtran.p$get_exit_status() if(status_val == 0){ status <- "NMTRAN successful" - }else if(status_val == 4){ - status <- "NMTRAN failed. See errors." }else{ - dev_error("NMTRAN exit status other than 0 or 4") + status <- "NMTRAN failed. See errors." } # Tabulate NMTRAN results From 7c3b2ebd4cad5d02b9c35e1eba8c0173508946a9 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Wed, 10 Jul 2024 12:23:40 -0400 Subject: [PATCH 07/31] add new `nm_fdata` function for returning `FDATA` via `NM-TRAN` - organize documentation and add additional details --- NAMESPACE | 1 + R/nm-file.R | 5 +-- R/run-nmtran.R | 83 +++++++++++++++++++++++++++++++++++-------- man/execute_nmtran.Rd | 6 ++-- man/locate_nmtran.Rd | 11 +++--- man/nm_file_impl.Rd | 4 ++- man/nmtran.Rd | 59 ++++++++++++++++++++++++++++++ man/run_nmtran.Rd | 34 ------------------ 8 files changed, 143 insertions(+), 60 deletions(-) create mode 100644 man/nmtran.Rd delete mode 100644 man/run_nmtran.Rd diff --git a/NAMESPACE b/NAMESPACE index 6c09f664d..5e38717d5 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -160,6 +160,7 @@ export(new_bootstrap_run) export(new_ext) export(new_model) export(nm_data) +export(nm_fdata) export(nm_file) export(nm_file_multi_tab) export(nm_grd) diff --git a/R/nm-file.R b/R/nm-file.R index 45c0b5867..848279fed 100644 --- a/R/nm-file.R +++ b/R/nm-file.R @@ -113,8 +113,9 @@ nm_data <- function(.mod) { #' @importFrom data.table fread #' @importFrom stringr str_detect #' @param .path a path to a table file. +#' @param skip number of rows to skip when reading in table file. Defaults to `1` #' @keywords internal -nm_file_impl <- function(.path) { +nm_file_impl <- function(.path, skip = 1) { # read file and find top of table verbose_msg(glue("Reading {basename(.path)}")) @@ -128,7 +129,7 @@ nm_file_impl <- function(.path) { data <- fread( .path, na.strings = ".", - skip = 1, + skip = skip, verbose = FALSE ) }) diff --git a/R/run-nmtran.R b/R/run-nmtran.R index 0ef1e8a30..17292a00a 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -1,24 +1,43 @@ - -#' Run NMTRAN on a model object +#' Interface for running `NM-TRAN` on model objects +#' +#' Provides functions to run `NM-TRAN` on model objects for validation and to +#' generate `NM-TRAN` datasets (`FDATA`). +#' +#' @details +#' `NM-TRAN` is a preprocessor for `NONMEM` that translates user-specified +#' control stream data and instructions into a form executable by `NONMEM`. #' -#' @param .mod a `bbr` model object -#' @param .config_path Path to a bbi configuration file. If `NULL`, the -#' default, will attempt to use a `bbi.yaml` in the same directory as the -#' model. -#' @param nmtran_exe Path to an `NMTRAN` executable. If `NULL`, will look for a +#' `run_nmtran()` allows users to test their models ahead of submission to ensure +#' correct coding, whereas `nm_fdata()` generates and returns the `NM-TRAN` +#' dataset (`FDATA`) for further analysis and verification. +#' +#' @param .mod A `bbr` model object. +#' @param .config_path Path to a bbi configuration file. If `NULL`, the default, +#' will attempt to use a `bbi.yaml` in the same directory as the model. +#' @param nmtran_exe Path to an `NM-TRAN` executable. If `NULL`, will look for a #' `bbi.yaml` file in the same directory as the model. #' @param delete_on_exit Logical. If `FALSE`, don't delete the temporary folder -#' containing the `NMTRAN` run. +#' containing the `NM-TRAN` run, which will be stored in the current working +#' directory. #' #' @examples #' \dontrun{ #' mod <- read_model(file.path(MODEL_DIR, 1)) #' run_nmtran(mod) #' -#' # Set the path to an NMTRAN executable +#' # Set the path to an NM-TRAN executable #' run_nmtran(mod, nmtran_exe = "/opt/NONMEM/nm75/tr/NMTRAN.exe") -#' } #' +#' # Generate and return `FDATA` +#' fdata <- nm_fdata(mod) +#' +#' } +#' @name nmtran +NULL + + +#' @describeIn nmtran Run `NM-TRAN` on a model object to validate its control +#' stream for correct coding before submission. #' @export run_nmtran <- function( .mod, @@ -38,7 +57,8 @@ run_nmtran <- function( # make temporary directory in current directory mod_name <- fs::path_ext_remove(basename(mod_path)) temp_folder <- paste0("nmtran_", mod_name, "_", basename(tempdir())) - dir.create(temp_folder) + if(fs::dir_exists(temp_folder)) fs::dir_delete(temp_folder) + fs::dir_create(temp_folder) if(isTRUE(delete_on_exit)){ on.exit(unlink(temp_folder, recursive = TRUE, force = TRUE)) } @@ -71,7 +91,42 @@ run_nmtran <- function( } -#' Search for and validate existence of an `NMTRAN` executable +#' @describeIn nmtran Executes `run_nmtran` on a `bbi_nonmem_model` and +#' returns the `NM-TRAN` dataset (`FDATA`) +#' @export +nm_fdata <- function( + .mod, + .config_path = NULL, + nmtran_exe = NULL +){ + nmtran_p <- run_nmtran(.mod, .config_path, nmtran_exe, delete_on_exit = FALSE) + if(nmtran_p$status_val != 0){ + # trim output + output_lines <- nmtran_p$output_lines[!grepl("^\\s+$", nmtran_p$output_lines)] + rlang::warn( + c( + "NM-TRAN was unsuccessful and returned the following messages:", + paste(output_lines, collapse = "\n") + ) + ) + } + + # Attempt to read in FDATA (even if status_val is not 0) + # - FDATA can still be read in in _some scenarios_ where NM-TRAN fails + fdata_path <- file.path(nmtran_p$run_dir, "FDATA") + if(fs::file_exists(fdata_path)){ + input_data <- nm_data(.mod) %>% suppressMessages() + fdata <- nm_file_impl(fdata_path, skip = 0) %>% + stats::setNames(names(input_data)) + fs::dir_delete(nmtran_p$run_dir) + return(fdata) + }else{ + return(invisible(NULL)) + } +} + + +#' Search for and validate existence of an `NM-TRAN` executable #' #' If `nmtran_exe = NULL`, this will look for a `bbi.yaml` file in the same #' directory as the model. @@ -135,9 +190,9 @@ locate_nmtran <- function(.mod = NULL, .config_path = NULL, nmtran_exe = NULL){ } -#' Execute NMTRAN in a given directory +#' Execute `NM-TRAN` in a given directory #' -#' @param nmtran_exe Path to `NMTRAN` executable. +#' @param nmtran_exe Path to `NM-TRAN` executable. #' @param mod_path Path of a model to evaluate. Should be relative to `dir`. #' @param dir Directory in which to execute the command. #' diff --git a/man/execute_nmtran.Rd b/man/execute_nmtran.Rd index 50d578dfb..d3b3026a0 100644 --- a/man/execute_nmtran.Rd +++ b/man/execute_nmtran.Rd @@ -2,18 +2,18 @@ % Please edit documentation in R/run-nmtran.R \name{execute_nmtran} \alias{execute_nmtran} -\title{Execute NMTRAN in a given directory} +\title{Execute \code{NM-TRAN} in a given directory} \usage{ execute_nmtran(nmtran_exe, mod_path, dir = NULL) } \arguments{ -\item{nmtran_exe}{Path to \code{NMTRAN} executable.} +\item{nmtran_exe}{Path to \code{NM-TRAN} executable.} \item{mod_path}{Path of a model to evaluate. Should be relative to \code{dir}.} \item{dir}{Directory in which to execute the command.} } \description{ -Execute NMTRAN in a given directory +Execute \code{NM-TRAN} in a given directory } \keyword{internal} diff --git a/man/locate_nmtran.Rd b/man/locate_nmtran.Rd index a1aeb3571..05a2b638c 100644 --- a/man/locate_nmtran.Rd +++ b/man/locate_nmtran.Rd @@ -2,18 +2,17 @@ % Please edit documentation in R/run-nmtran.R \name{locate_nmtran} \alias{locate_nmtran} -\title{Search for and validate existence of an \code{NMTRAN} executable} +\title{Search for and validate existence of an \code{NM-TRAN} executable} \usage{ locate_nmtran(.mod = NULL, .config_path = NULL, nmtran_exe = NULL) } \arguments{ -\item{.mod}{a \code{bbr} model object} +\item{.mod}{A \code{bbr} model object.} -\item{.config_path}{Path to a bbi configuration file. If \code{NULL}, the -default, will attempt to use a \code{bbi.yaml} in the same directory as the -model.} +\item{.config_path}{Path to a bbi configuration file. If \code{NULL}, the default, +will attempt to use a \code{bbi.yaml} in the same directory as the model.} -\item{nmtran_exe}{Path to an \code{NMTRAN} executable. If \code{NULL}, will look for a +\item{nmtran_exe}{Path to an \code{NM-TRAN} executable. If \code{NULL}, will look for a \code{bbi.yaml} file in the same directory as the model.} } \description{ diff --git a/man/nm_file_impl.Rd b/man/nm_file_impl.Rd index f02a99c83..7fa311cf8 100644 --- a/man/nm_file_impl.Rd +++ b/man/nm_file_impl.Rd @@ -4,10 +4,12 @@ \alias{nm_file_impl} \title{Implementation function for reading NONMEM files} \usage{ -nm_file_impl(.path) +nm_file_impl(.path, skip = 1) } \arguments{ \item{.path}{a path to a table file.} + +\item{skip}{number of rows to skip when reading in table file. Defaults to \code{1}} } \description{ Implementation function for reading NONMEM files diff --git a/man/nmtran.Rd b/man/nmtran.Rd new file mode 100644 index 000000000..26527871a --- /dev/null +++ b/man/nmtran.Rd @@ -0,0 +1,59 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/run-nmtran.R +\name{nmtran} +\alias{nmtran} +\alias{run_nmtran} +\alias{nm_fdata} +\title{Interface for running \code{NM-TRAN} on model objects} +\usage{ +run_nmtran(.mod, .config_path = NULL, nmtran_exe = NULL, delete_on_exit = TRUE) + +nm_fdata(.mod, .config_path = NULL, nmtran_exe = NULL) +} +\arguments{ +\item{.mod}{A \code{bbr} model object.} + +\item{.config_path}{Path to a bbi configuration file. If \code{NULL}, the default, +will attempt to use a \code{bbi.yaml} in the same directory as the model.} + +\item{nmtran_exe}{Path to an \code{NM-TRAN} executable. If \code{NULL}, will look for a +\code{bbi.yaml} file in the same directory as the model.} + +\item{delete_on_exit}{Logical. If \code{FALSE}, don't delete the temporary folder +containing the \code{NM-TRAN} run, which will be stored in the current working +directory.} +} +\description{ +Provides functions to run \code{NM-TRAN} on model objects for validation and to +generate \code{NM-TRAN} datasets (\code{FDATA}). +} +\details{ +\code{NM-TRAN} is a preprocessor for \code{NONMEM} that translates user-specified +control stream data and instructions into a form executable by \code{NONMEM}. + +\code{run_nmtran()} allows users to test their models ahead of submission to ensure +correct coding, whereas \code{nm_fdata()} generates and returns the \code{NM-TRAN} +dataset (\code{FDATA}) for further analysis and verification. +} +\section{Functions}{ +\itemize{ +\item \code{run_nmtran()}: Run \code{NM-TRAN} on a model object to validate its control +stream for correct coding before submission. + +\item \code{nm_fdata()}: Executes \code{run_nmtran} on a \code{bbi_nonmem_model} and +returns the \code{NM-TRAN} dataset (\code{FDATA}) + +}} +\examples{ +\dontrun{ +mod <- read_model(file.path(MODEL_DIR, 1)) +run_nmtran(mod) + +# Set the path to an NM-TRAN executable +run_nmtran(mod, nmtran_exe = "/opt/NONMEM/nm75/tr/NMTRAN.exe") + +# Generate and return `FDATA` +fdata <- nm_fdata(mod) + +} +} diff --git a/man/run_nmtran.Rd b/man/run_nmtran.Rd deleted file mode 100644 index f6e71ea73..000000000 --- a/man/run_nmtran.Rd +++ /dev/null @@ -1,34 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/run-nmtran.R -\name{run_nmtran} -\alias{run_nmtran} -\title{Run NMTRAN on a model object} -\usage{ -run_nmtran(.mod, .config_path = NULL, nmtran_exe = NULL, delete_on_exit = TRUE) -} -\arguments{ -\item{.mod}{a \code{bbr} model object} - -\item{.config_path}{Path to a bbi configuration file. If \code{NULL}, the -default, will attempt to use a \code{bbi.yaml} in the same directory as the -model.} - -\item{nmtran_exe}{Path to an \code{NMTRAN} executable. If \code{NULL}, will look for a -\code{bbi.yaml} file in the same directory as the model.} - -\item{delete_on_exit}{Logical. If \code{FALSE}, don't delete the temporary folder -containing the \code{NMTRAN} run.} -} -\description{ -Run NMTRAN on a model object -} -\examples{ -\dontrun{ -mod <- read_model(file.path(MODEL_DIR, 1)) -run_nmtran(mod) - -# Set the path to an NMTRAN executable -run_nmtran(mod, nmtran_exe = "/opt/NONMEM/nm75/tr/NMTRAN.exe") -} - -} From 2abd55c0cd507e8f0a01aa6cf28b65251ac392d7 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Wed, 10 Jul 2024 12:52:31 -0400 Subject: [PATCH 08/31] add print method and error catching for fdata - print method shows how many records have dropped - noticed issue with current implementation. The setting of column names will not work if columns were dropped. May also consider take renaming into account (eg., `$INPUT DV=CP`). --- NAMESPACE | 1 + R/aaa.R | 1 + R/print.R | 15 ++++++++++++++- R/run-nmtran.R | 21 ++++++++++++++++++--- man/print_bbi.Rd | 8 +++++++- tests/testthat/test-workflow-bbi.R | 14 +++++++++++++- 6 files changed, 54 insertions(+), 6 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 5e38717d5..7aa7e76e7 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -76,6 +76,7 @@ S3method(print,bbi_nmboot_summary) S3method(print,bbi_nonmem_summary) S3method(print,bbi_process) S3method(print,model_tree_static) +S3method(print,nmtran_fdata) S3method(print,nmtran_process) S3method(print_model_files,default) S3method(submit_model,bbi_base_model) diff --git a/R/aaa.R b/R/aaa.R index 105a70fb1..9f85ce265 100644 --- a/R/aaa.R +++ b/R/aaa.R @@ -210,6 +210,7 @@ CONF_LOG_CLASS <- "bbi_config_log_df" SUM_LOG_CLASS <- "bbi_summary_log_df" LOG_DF_CLASS <- "bbi_log_df" NMTRAN_PROCESS_CLASS <- "nmtran_process" +NMTRAN_FDATA_CLASS <- "nmtran_fdata" # YAML keys that are hard-coded YAML_YAML_MD5 <- "yaml_md5" diff --git a/R/print.R b/R/print.R index 11f6e7966..12f9ef99f 100644 --- a/R/print.R +++ b/R/print.R @@ -443,7 +443,8 @@ print.bbi_nmboot_summary <- function(x, .digits = 3, .nrow = 10, ...) { } -#' @describeIn print_bbi Prints the `NMTRAN` evaluation of a `bbi_model` model +#' @describeIn print_bbi Prints the `NM-TRAN` evaluation of a `bbi_nonmem_model` +#' object #' @export print.nmtran_process <- function(x, ...){ @@ -501,6 +502,18 @@ print.nmtran_process <- function(x, ...){ } +#' @describeIn print_bbi Prints the `FDATA`, showing key changes from `nm_data` +#' @export +print.nmtran_fdata <- function(x, ...){ + recs_dropped <- attributes(x)$n_records_dropped + cli::cat_bullet( + paste("Number of records dropped:", col_blue(recs_dropped)), + bullet = "en_dash" + ) + cat("\n") + NextMethod() +} + #' @describeIn print_bbi Draw model tree as a static plot #' @param x plot to display #' @param newpage Logical (T/F). If `TRUE`, draw new (empty) page first. diff --git a/R/run-nmtran.R b/R/run-nmtran.R index 17292a00a..3f989df0f 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -100,6 +100,8 @@ nm_fdata <- function( nmtran_exe = NULL ){ nmtran_p <- run_nmtran(.mod, .config_path, nmtran_exe, delete_on_exit = FALSE) + on.exit(fs::dir_delete(nmtran_p$run_dir)) + if(nmtran_p$status_val != 0){ # trim output output_lines <- nmtran_p$output_lines[!grepl("^\\s+$", nmtran_p$output_lines)] @@ -116,9 +118,22 @@ nm_fdata <- function( fdata_path <- file.path(nmtran_p$run_dir, "FDATA") if(fs::file_exists(fdata_path)){ input_data <- nm_data(.mod) %>% suppressMessages() - fdata <- nm_file_impl(fdata_path, skip = 0) %>% - stats::setNames(names(input_data)) - fs::dir_delete(nmtran_p$run_dir) + fdata <- tryCatch({ + nm_file_impl(fdata_path, skip = 0) %>% + stats::setNames(names(input_data)) + }, error = function(cond){ + rlang::inform( + c("FDATA could not be read in:", cond$parent$message) + ) + return(NULL) + }) + + if(!is.null(fdata)){ + attr(fdata, "n_records_dropped") <- nrow(input_data) - nrow(fdata) + } + + # assign class and return + class(fdata) <- c(NMTRAN_FDATA_CLASS, class(fdata)) return(fdata) }else{ return(invisible(NULL)) diff --git a/man/print_bbi.Rd b/man/print_bbi.Rd index 50ee3fe8a..a6bc482f3 100644 --- a/man/print_bbi.Rd +++ b/man/print_bbi.Rd @@ -7,6 +7,7 @@ \alias{print.bbi_nonmem_summary} \alias{print.bbi_nmboot_summary} \alias{print.nmtran_process} +\alias{print.nmtran_fdata} \alias{print.model_tree_static} \title{Print methods for bbr objects} \usage{ @@ -20,6 +21,8 @@ \method{print}{nmtran_process}(x, ...) +\method{print}{nmtran_fdata}(x, ...) + \method{print}{model_tree_static}(x, newpage = is.null(vp), vp = NULL, ...) } \arguments{ @@ -62,7 +65,10 @@ will make for prettier formatting, especially of table outputs. \item \code{print(bbi_nmboot_summary)}: Prints a high level summary of a model from a \code{bbi_nmboot_summary} object -\item \code{print(nmtran_process)}: Prints the \code{NMTRAN} evaluation of a \code{bbi_model} model +\item \code{print(nmtran_process)}: Prints the \code{NM-TRAN} evaluation of a \code{bbi_nonmem_model} +object + +\item \code{print(nmtran_fdata)}: Prints the \code{FDATA}, showing key changes from \code{nm_data} \item \code{print(model_tree_static)}: Draw model tree as a static plot diff --git a/tests/testthat/test-workflow-bbi.R b/tests/testthat/test-workflow-bbi.R index afb318117..60350a8f1 100644 --- a/tests/testthat/test-workflow-bbi.R +++ b/tests/testthat/test-workflow-bbi.R @@ -384,7 +384,7 @@ withr::with_options(list( expect_equal(nmtran_results$status, "NMTRAN failed. See errors.") }) - it("run_nmtran", { + it("run_nmtran: integration", { # create model mod1 <- read_model(file.path(MODEL_DIR_BBI, "1")) @@ -401,6 +401,18 @@ withr::with_options(list( expect_equal(nmtran_results$status_val, 0) expect_equal(nmtran_results$status, "NMTRAN successful") }) + + it("nm_fdata: integration", { + fdata <- nm_fdata(mod1) + + expect_equal(names(fdata), names(nm_data(mod1))) + + # Test dropped/renamed columns + + # Test failure: incorrect data path + data_path <- "test/this/path/data.csv" + modify_data_path_ctl(mod1, data_path) + }) }) }) # closing withr::with_options From d23855ef312cb147aae9bf05f8039e090cd57e14 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Mon, 15 Jul 2024 15:43:27 -0400 Subject: [PATCH 09/31] remove `nm_fdata` from this PR - opting to separate this into a separate, more focused PR --- NAMESPACE | 2 -- R/aaa.R | 1 - R/print.R | 12 ------- R/run-nmtran.R | 56 +----------------------------- man/nmtran.Rd | 12 +------ man/print_bbi.Rd | 5 --- tests/testthat/test-workflow-bbi.R | 11 ------ 7 files changed, 2 insertions(+), 97 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 7aa7e76e7..6c09f664d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -76,7 +76,6 @@ S3method(print,bbi_nmboot_summary) S3method(print,bbi_nonmem_summary) S3method(print,bbi_process) S3method(print,model_tree_static) -S3method(print,nmtran_fdata) S3method(print,nmtran_process) S3method(print_model_files,default) S3method(submit_model,bbi_base_model) @@ -161,7 +160,6 @@ export(new_bootstrap_run) export(new_ext) export(new_model) export(nm_data) -export(nm_fdata) export(nm_file) export(nm_file_multi_tab) export(nm_grd) diff --git a/R/aaa.R b/R/aaa.R index 9f85ce265..105a70fb1 100644 --- a/R/aaa.R +++ b/R/aaa.R @@ -210,7 +210,6 @@ CONF_LOG_CLASS <- "bbi_config_log_df" SUM_LOG_CLASS <- "bbi_summary_log_df" LOG_DF_CLASS <- "bbi_log_df" NMTRAN_PROCESS_CLASS <- "nmtran_process" -NMTRAN_FDATA_CLASS <- "nmtran_fdata" # YAML keys that are hard-coded YAML_YAML_MD5 <- "yaml_md5" diff --git a/R/print.R b/R/print.R index 12f9ef99f..292f4375f 100644 --- a/R/print.R +++ b/R/print.R @@ -502,18 +502,6 @@ print.nmtran_process <- function(x, ...){ } -#' @describeIn print_bbi Prints the `FDATA`, showing key changes from `nm_data` -#' @export -print.nmtran_fdata <- function(x, ...){ - recs_dropped <- attributes(x)$n_records_dropped - cli::cat_bullet( - paste("Number of records dropped:", col_blue(recs_dropped)), - bullet = "en_dash" - ) - cat("\n") - NextMethod() -} - #' @describeIn print_bbi Draw model tree as a static plot #' @param x plot to display #' @param newpage Logical (T/F). If `TRUE`, draw new (empty) page first. diff --git a/R/run-nmtran.R b/R/run-nmtran.R index 3f989df0f..bfc406c54 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -8,8 +8,7 @@ #' control stream data and instructions into a form executable by `NONMEM`. #' #' `run_nmtran()` allows users to test their models ahead of submission to ensure -#' correct coding, whereas `nm_fdata()` generates and returns the `NM-TRAN` -#' dataset (`FDATA`) for further analysis and verification. +#' correct coding. #' #' @param .mod A `bbr` model object. #' @param .config_path Path to a bbi configuration file. If `NULL`, the default, @@ -28,9 +27,6 @@ #' # Set the path to an NM-TRAN executable #' run_nmtran(mod, nmtran_exe = "/opt/NONMEM/nm75/tr/NMTRAN.exe") #' -#' # Generate and return `FDATA` -#' fdata <- nm_fdata(mod) -#' #' } #' @name nmtran NULL @@ -91,56 +87,6 @@ run_nmtran <- function( } -#' @describeIn nmtran Executes `run_nmtran` on a `bbi_nonmem_model` and -#' returns the `NM-TRAN` dataset (`FDATA`) -#' @export -nm_fdata <- function( - .mod, - .config_path = NULL, - nmtran_exe = NULL -){ - nmtran_p <- run_nmtran(.mod, .config_path, nmtran_exe, delete_on_exit = FALSE) - on.exit(fs::dir_delete(nmtran_p$run_dir)) - - if(nmtran_p$status_val != 0){ - # trim output - output_lines <- nmtran_p$output_lines[!grepl("^\\s+$", nmtran_p$output_lines)] - rlang::warn( - c( - "NM-TRAN was unsuccessful and returned the following messages:", - paste(output_lines, collapse = "\n") - ) - ) - } - - # Attempt to read in FDATA (even if status_val is not 0) - # - FDATA can still be read in in _some scenarios_ where NM-TRAN fails - fdata_path <- file.path(nmtran_p$run_dir, "FDATA") - if(fs::file_exists(fdata_path)){ - input_data <- nm_data(.mod) %>% suppressMessages() - fdata <- tryCatch({ - nm_file_impl(fdata_path, skip = 0) %>% - stats::setNames(names(input_data)) - }, error = function(cond){ - rlang::inform( - c("FDATA could not be read in:", cond$parent$message) - ) - return(NULL) - }) - - if(!is.null(fdata)){ - attr(fdata, "n_records_dropped") <- nrow(input_data) - nrow(fdata) - } - - # assign class and return - class(fdata) <- c(NMTRAN_FDATA_CLASS, class(fdata)) - return(fdata) - }else{ - return(invisible(NULL)) - } -} - - #' Search for and validate existence of an `NM-TRAN` executable #' #' If `nmtran_exe = NULL`, this will look for a `bbi.yaml` file in the same diff --git a/man/nmtran.Rd b/man/nmtran.Rd index 26527871a..fae2bb6bc 100644 --- a/man/nmtran.Rd +++ b/man/nmtran.Rd @@ -3,12 +3,9 @@ \name{nmtran} \alias{nmtran} \alias{run_nmtran} -\alias{nm_fdata} \title{Interface for running \code{NM-TRAN} on model objects} \usage{ run_nmtran(.mod, .config_path = NULL, nmtran_exe = NULL, delete_on_exit = TRUE) - -nm_fdata(.mod, .config_path = NULL, nmtran_exe = NULL) } \arguments{ \item{.mod}{A \code{bbr} model object.} @@ -32,17 +29,13 @@ generate \code{NM-TRAN} datasets (\code{FDATA}). control stream data and instructions into a form executable by \code{NONMEM}. \code{run_nmtran()} allows users to test their models ahead of submission to ensure -correct coding, whereas \code{nm_fdata()} generates and returns the \code{NM-TRAN} -dataset (\code{FDATA}) for further analysis and verification. +correct coding. } \section{Functions}{ \itemize{ \item \code{run_nmtran()}: Run \code{NM-TRAN} on a model object to validate its control stream for correct coding before submission. -\item \code{nm_fdata()}: Executes \code{run_nmtran} on a \code{bbi_nonmem_model} and -returns the \code{NM-TRAN} dataset (\code{FDATA}) - }} \examples{ \dontrun{ @@ -52,8 +45,5 @@ run_nmtran(mod) # Set the path to an NM-TRAN executable run_nmtran(mod, nmtran_exe = "/opt/NONMEM/nm75/tr/NMTRAN.exe") -# Generate and return `FDATA` -fdata <- nm_fdata(mod) - } } diff --git a/man/print_bbi.Rd b/man/print_bbi.Rd index a6bc482f3..c9a8aed1c 100644 --- a/man/print_bbi.Rd +++ b/man/print_bbi.Rd @@ -7,7 +7,6 @@ \alias{print.bbi_nonmem_summary} \alias{print.bbi_nmboot_summary} \alias{print.nmtran_process} -\alias{print.nmtran_fdata} \alias{print.model_tree_static} \title{Print methods for bbr objects} \usage{ @@ -21,8 +20,6 @@ \method{print}{nmtran_process}(x, ...) -\method{print}{nmtran_fdata}(x, ...) - \method{print}{model_tree_static}(x, newpage = is.null(vp), vp = NULL, ...) } \arguments{ @@ -68,8 +65,6 @@ will make for prettier formatting, especially of table outputs. \item \code{print(nmtran_process)}: Prints the \code{NM-TRAN} evaluation of a \code{bbi_nonmem_model} object -\item \code{print(nmtran_fdata)}: Prints the \code{FDATA}, showing key changes from \code{nm_data} - \item \code{print(model_tree_static)}: Draw model tree as a static plot }} diff --git a/tests/testthat/test-workflow-bbi.R b/tests/testthat/test-workflow-bbi.R index 60350a8f1..dbcb7e1e9 100644 --- a/tests/testthat/test-workflow-bbi.R +++ b/tests/testthat/test-workflow-bbi.R @@ -402,17 +402,6 @@ withr::with_options(list( expect_equal(nmtran_results$status, "NMTRAN successful") }) - it("nm_fdata: integration", { - fdata <- nm_fdata(mod1) - - expect_equal(names(fdata), names(nm_data(mod1))) - - # Test dropped/renamed columns - - # Test failure: incorrect data path - data_path <- "test/this/path/data.csv" - modify_data_path_ctl(mod1, data_path) - }) }) }) # closing withr::with_options From 1b1681f49ad24a22cda0d9fd41d2928a2f07a932 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Mon, 15 Jul 2024 15:46:08 -0400 Subject: [PATCH 10/31] revert `nm_file_impl` change used in `nm_fdata` - see previous commit message --- R/nm-file.R | 5 ++--- man/nm_file_impl.Rd | 4 +--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/R/nm-file.R b/R/nm-file.R index 848279fed..45c0b5867 100644 --- a/R/nm-file.R +++ b/R/nm-file.R @@ -113,9 +113,8 @@ nm_data <- function(.mod) { #' @importFrom data.table fread #' @importFrom stringr str_detect #' @param .path a path to a table file. -#' @param skip number of rows to skip when reading in table file. Defaults to `1` #' @keywords internal -nm_file_impl <- function(.path, skip = 1) { +nm_file_impl <- function(.path) { # read file and find top of table verbose_msg(glue("Reading {basename(.path)}")) @@ -129,7 +128,7 @@ nm_file_impl <- function(.path, skip = 1) { data <- fread( .path, na.strings = ".", - skip = skip, + skip = 1, verbose = FALSE ) }) diff --git a/man/nm_file_impl.Rd b/man/nm_file_impl.Rd index 7fa311cf8..f02a99c83 100644 --- a/man/nm_file_impl.Rd +++ b/man/nm_file_impl.Rd @@ -4,12 +4,10 @@ \alias{nm_file_impl} \title{Implementation function for reading NONMEM files} \usage{ -nm_file_impl(.path, skip = 1) +nm_file_impl(.path) } \arguments{ \item{.path}{a path to a table file.} - -\item{skip}{number of rows to skip when reading in table file. Defaults to \code{1}} } \description{ Implementation function for reading NONMEM files From d93af1fb08fbc907f5f5e51c8702cb5e35947184 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Tue, 16 Jul 2024 12:28:56 -0400 Subject: [PATCH 11/31] remove mod_path from args - I had noticed some weird interaction here in the past when removing this, but seems fine now --- R/run-nmtran.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/run-nmtran.R b/R/run-nmtran.R index bfc406c54..477009a49 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -163,7 +163,7 @@ execute_nmtran <- function(nmtran_exe, mod_path, dir = NULL) { checkmate::assert_directory_exists(dir) nmtran.p <- processx::process$new( - command = nmtran_exe, args = mod_path, wd = dir, + command = nmtran_exe, wd = dir, stdout = "|", stderr="|", stdin = file.path(dir, mod_path) ) From afe85403fdb78f6cd0aec366a22ab19341dcdafc Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Thu, 18 Jul 2024 13:19:10 -0400 Subject: [PATCH 12/31] added support for passing specific `bbi_args` to `NM-TRAN` call - still need to add more details to the documentation --- R/run-nmtran.R | 25 ++++++++++++++++++++++--- man/execute_nmtran.Rd | 5 ++++- man/nmtran.Rd | 12 +++++++++++- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/R/run-nmtran.R b/R/run-nmtran.R index 477009a49..018e0b45e 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -11,6 +11,9 @@ #' correct coding. #' #' @param .mod A `bbr` model object. +#' @param .bbi_args A named list specifying arguments to pass to `NM-TRAN`. +#' Similar to the `.bbi_args` argument defined in [submit_model()], though here +#' only `prdefault`, `tprdefault`, and `maxlim` flags are passed to `NM-TRAN`. #' @param .config_path Path to a bbi configuration file. If `NULL`, the default, #' will attempt to use a `bbi.yaml` in the same directory as the model. #' @param nmtran_exe Path to an `NM-TRAN` executable. If `NULL`, will look for a @@ -37,6 +40,7 @@ NULL #' @export run_nmtran <- function( .mod, + .bbi_args = list(prdefault = TRUE, tprdefault = TRUE, maxlim = 3), .config_path = NULL, nmtran_exe = NULL, delete_on_exit = TRUE @@ -47,6 +51,14 @@ run_nmtran <- function( nmtran_exe <- locate_nmtran(.mod, .config_path, nmtran_exe) nm_ver <- attr(nmtran_exe, "nonmem_version") + # Combine NONMEM submission args + # - The main ones of interest are prdefault, tprdefault, and maxlim, which + # impact the evaluation of `NM-TRAN` + .bbi_args <- parse_args_list(.bbi_args, .mod[[YAML_BBI_ARGS]]) + .bbi_args <- Filter(Negate(is.null), .bbi_args[c("prdefault", "tprdefault", "maxlim")]) + cmd_args <- check_bbi_args(.bbi_args) + + mod_path <- get_model_path(.mod) data_path <- get_data_path_from_ctl(.mod) @@ -78,7 +90,10 @@ run_nmtran <- function( nonmem_version = nm_ver, absolute_model_path = mod_path ), - execute_nmtran(nmtran_exe, mod_path = basename(mod_path), dir = temp_folder) + execute_nmtran( + nmtran_exe, mod_path = basename(mod_path), cmd_args = cmd_args, + dir = temp_folder + ) ) # assign class and return @@ -155,15 +170,19 @@ locate_nmtran <- function(.mod = NULL, .config_path = NULL, nmtran_exe = NULL){ #' #' @param nmtran_exe Path to `NM-TRAN` executable. #' @param mod_path Path of a model to evaluate. Should be relative to `dir`. +#' @param cmd_args A character vector of command line arguments for the `NM-TRAN` +#' execution call #' @param dir Directory in which to execute the command. #' #' @keywords internal -execute_nmtran <- function(nmtran_exe, mod_path, dir = NULL) { +execute_nmtran <- function(nmtran_exe, mod_path, cmd_args = NULL, dir = NULL) { if(is.null(dir)) dir <- "." checkmate::assert_directory_exists(dir) + cmd_args <- if(is.null(cmd_args)) character() else cmd_args + nmtran.p <- processx::process$new( - command = nmtran_exe, wd = dir, + command = nmtran_exe, wd = dir, args = cmd_args, stdout = "|", stderr="|", stdin = file.path(dir, mod_path) ) diff --git a/man/execute_nmtran.Rd b/man/execute_nmtran.Rd index d3b3026a0..98627d0e1 100644 --- a/man/execute_nmtran.Rd +++ b/man/execute_nmtran.Rd @@ -4,13 +4,16 @@ \alias{execute_nmtran} \title{Execute \code{NM-TRAN} in a given directory} \usage{ -execute_nmtran(nmtran_exe, mod_path, dir = NULL) +execute_nmtran(nmtran_exe, mod_path, cmd_args = NULL, dir = NULL) } \arguments{ \item{nmtran_exe}{Path to \code{NM-TRAN} executable.} \item{mod_path}{Path of a model to evaluate. Should be relative to \code{dir}.} +\item{cmd_args}{A character vector of command line arguments for the \code{NM-TRAN} +execution call} + \item{dir}{Directory in which to execute the command.} } \description{ diff --git a/man/nmtran.Rd b/man/nmtran.Rd index fae2bb6bc..7d3f3355f 100644 --- a/man/nmtran.Rd +++ b/man/nmtran.Rd @@ -5,11 +5,21 @@ \alias{run_nmtran} \title{Interface for running \code{NM-TRAN} on model objects} \usage{ -run_nmtran(.mod, .config_path = NULL, nmtran_exe = NULL, delete_on_exit = TRUE) +run_nmtran( + .mod, + .bbi_args = list(prdefault = TRUE, tprdefault = TRUE, maxlim = 3), + .config_path = NULL, + nmtran_exe = NULL, + delete_on_exit = TRUE +) } \arguments{ \item{.mod}{A \code{bbr} model object.} +\item{.bbi_args}{A named list specifying arguments to pass to \code{NM-TRAN}. +Similar to the \code{.bbi_args} argument defined in \code{\link[=submit_model]{submit_model()}}, though here +only \code{prdefault}, \code{tprdefault}, and \code{maxlim} flags are passed to \code{NM-TRAN}.} + \item{.config_path}{Path to a bbi configuration file. If \code{NULL}, the default, will attempt to use a \code{bbi.yaml} in the same directory as the model.} From 7b245c821c669f43c476054de22f9b44b05dd084 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Fri, 19 Jul 2024 14:56:33 -0400 Subject: [PATCH 13/31] add details to .bbi_args options --- R/run-nmtran.R | 14 +++++++++++++- man/nmtran.Rd | 22 ++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/R/run-nmtran.R b/R/run-nmtran.R index 018e0b45e..fc65cb5ba 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -10,10 +10,22 @@ #' `run_nmtran()` allows users to test their models ahead of submission to ensure #' correct coding. #' +#' @section Supported `bbi_args`: +#' `run_nmtran()` supports passing the following raw `NMFE` options through to `NM-TRAN` +#' via the familiar `.bbi_args` argument. +#' - **`maxlim`**: Set the maximum values for the buffers used by `NONMEM` +#' (if 0, don't pass `-maxlim` to nmfe) (default is 2) +#' - If `maxlim = 3`, it is preferred to also set `tprdefault = TRUE`, but +#' _not_ `prdefault`, as `NM-TRAN`'s optional resizing of the `PREDPP` size +#' parameter `MAXRECID` may conflict with the `-prdefault` option. +#' - **`prdefault`**: If `TRUE`, do not recompile any routines other than `FSUBS` +#' - **`tprdefault`**: If `TRUE`, test if is okay to do `-prdefault` +#' #' @param .mod A `bbr` model object. #' @param .bbi_args A named list specifying arguments to pass to `NM-TRAN`. #' Similar to the `.bbi_args` argument defined in [submit_model()], though here #' only `prdefault`, `tprdefault`, and `maxlim` flags are passed to `NM-TRAN`. +#' See details. #' @param .config_path Path to a bbi configuration file. If `NULL`, the default, #' will attempt to use a `bbi.yaml` in the same directory as the model. #' @param nmtran_exe Path to an `NM-TRAN` executable. If `NULL`, will look for a @@ -40,7 +52,7 @@ NULL #' @export run_nmtran <- function( .mod, - .bbi_args = list(prdefault = TRUE, tprdefault = TRUE, maxlim = 3), + .bbi_args = list(prdefault = FALSE, tprdefault = FALSE, maxlim = 2), .config_path = NULL, nmtran_exe = NULL, delete_on_exit = TRUE diff --git a/man/nmtran.Rd b/man/nmtran.Rd index 7d3f3355f..58dd7e9c5 100644 --- a/man/nmtran.Rd +++ b/man/nmtran.Rd @@ -7,7 +7,7 @@ \usage{ run_nmtran( .mod, - .bbi_args = list(prdefault = TRUE, tprdefault = TRUE, maxlim = 3), + .bbi_args = list(prdefault = FALSE, tprdefault = FALSE, maxlim = 2), .config_path = NULL, nmtran_exe = NULL, delete_on_exit = TRUE @@ -18,7 +18,8 @@ run_nmtran( \item{.bbi_args}{A named list specifying arguments to pass to \code{NM-TRAN}. Similar to the \code{.bbi_args} argument defined in \code{\link[=submit_model]{submit_model()}}, though here -only \code{prdefault}, \code{tprdefault}, and \code{maxlim} flags are passed to \code{NM-TRAN}.} +only \code{prdefault}, \code{tprdefault}, and \code{maxlim} flags are passed to \code{NM-TRAN}. +See details.} \item{.config_path}{Path to a bbi configuration file. If \code{NULL}, the default, will attempt to use a \code{bbi.yaml} in the same directory as the model.} @@ -47,6 +48,23 @@ correct coding. stream for correct coding before submission. }} +\section{Supported \code{bbi_args}}{ + +\code{run_nmtran()} supports passing the following raw \code{NMFE} options through to \code{NM-TRAN} +via the familiar \code{.bbi_args} argument. +\itemize{ +\item \strong{\code{maxlim}}: Set the maximum values for the buffers used by \code{NONMEM} +(if 0, don't pass \code{-maxlim} to nmfe) (default is 2) +\itemize{ +\item If \code{maxlim = 3}, it is preferred to also set \code{tprdefault = TRUE}, but +\emph{not} \code{prdefault}, as \code{NM-TRAN}'s optional resizing of the \code{PREDPP} size +parameter \code{MAXRECID} may conflict with the \code{-prdefault} option. +} +\item \strong{\code{prdefault}}: If \code{TRUE}, do not recompile any routines other than \code{FSUBS} +\item \strong{\code{tprdefault}}: If \code{TRUE}, test if is okay to do \code{-prdefault} +} +} + \examples{ \dontrun{ mod <- read_model(file.path(MODEL_DIR, 1)) From fbe1344f1cb3a099dd1465cd5170582ddfd6ad31 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Tue, 23 Jul 2024 15:45:41 -0400 Subject: [PATCH 14/31] Minor updates from KyleM's feedback - make NM-TRAN label consistent across all functions and documentation - store NM-TRAN standard error in the same file as standard output. Adjust print method --- R/aaa.R | 2 +- R/get-path-from-object.R | 2 +- R/print.R | 22 ++++++---------------- R/run-nmtran.R | 24 ++++++++++++------------ 4 files changed, 20 insertions(+), 30 deletions(-) diff --git a/R/aaa.R b/R/aaa.R index 105a70fb1..63007c684 100644 --- a/R/aaa.R +++ b/R/aaa.R @@ -53,7 +53,7 @@ BBI_ARGS = list( delay = list( type = "numeric", flag = "--delay", - description = "Selects a random number of seconds between 1 and this value to stagger / jitter job execution. Assists in dealing with large volumes of work dealing with the same data set. May avoid NMTRAN issues about not being able read / close files" + description = "Selects a random number of seconds between 1 and this value to stagger / jitter job execution. Assists in dealing with large volumes of work dealing with the same data set. May avoid NM-TRAN issues about not being able read / close files" ), ext_file = list( type = "character", diff --git a/R/get-path-from-object.R b/R/get-path-from-object.R index 4ccd49b1c..9d5605d65 100644 --- a/R/get-path-from-object.R +++ b/R/get-path-from-object.R @@ -367,7 +367,7 @@ get_data_path_nonmem <- function( if(isTRUE(.check_exists)){ if(!fs::file_exists(data_path)){ - # The first error message line is what NMTRAN would return in this situation + # The first error message line is what NM-TRAN would return in this situation rlang::abort( c( "x" = "Input data file does not exist or cannot be opened", diff --git a/R/print.R b/R/print.R index 292f4375f..08f50ba66 100644 --- a/R/print.R +++ b/R/print.R @@ -476,28 +476,18 @@ print.nmtran_process <- function(x, ...){ heading("Absolute Model Path") bullet_list(x[[ABS_MOD_PATH]]) - heading("NMTRAN Specifications") + heading("NM-TRAN Specifications") cli::cli_bullets( c( - "*" = paste0("NMTRAN Executable: {.path ", x[["nmtran_exe"]], "}"), - "*" = paste0("NONMEM Version: {.val ", nm_version, "}"), - "*" = paste0("Run Directory: {.path ", x[["run_dir"]], "}") + "*" = "NM-TRAN Executable: {.path {x[['nmtran_exe']]}}", + "*" = "NONMEM Version: {.val {nm_version}}", + "*" = "Run Directory: {.path {x[['run_dir']]}}" ) ) if (is_valid_print(x[["output_lines"]])) { - heading('Output') - cat_line(style_italic(x[["output_lines"]])) - } - - if (is_valid_print(x[["error_lines"]])) { - heading('Additional Errors') - cat_line(style_italic(x[["error_lines"]])) - } - - # format and print status string - if (is_valid_print(x[["output_lines"]])) { - cat_line("Process finished.", col = "green") + heading('Standard Output') + cat_line(x[["output_lines"]]) } } diff --git a/R/run-nmtran.R b/R/run-nmtran.R index fc65cb5ba..312aeea5f 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -59,7 +59,7 @@ run_nmtran <- function( ){ check_model_object(.mod, "bbi_nonmem_model") - # Capture NONMEM and NMTRAN options + # Capture NONMEM and NM-TRAN options nmtran_exe <- locate_nmtran(.mod, .config_path, nmtran_exe) nm_ver <- attr(nmtran_exe, "nonmem_version") @@ -88,14 +88,14 @@ run_nmtran <- function( nmtran_mod <- new_model(file.path(temp_folder, basename(mod_path)), .overwrite = TRUE) # Copy dataset & overwrite $DATA record of new model - # NMTRAN will error if data cannot be found + # NM-TRAN will error if data cannot be found if(fs::file_exists(data_path)){ file.copy(data_path, temp_folder, overwrite = TRUE) # overwrite $DATA record of new model modify_data_path_ctl(nmtran_mod, data_path = basename(data_path)) } - # Run NMTRAN + # Run NM-TRAN nmtran_results <- c( list( nmtran_exe = as.character(nmtran_exe), @@ -160,10 +160,10 @@ locate_nmtran <- function(.mod = NULL, .config_path = NULL, nmtran_exe = NULL){ default_nm <- nm_config[[length(nm_config)]] } - # Set NMTRAN executable path + # Set NM-TRAN executable path nm_path <- default_nm$home # TODO: should we recursively look for this executable, or assume Metworx? - # i.e. can we assume NMTRAN is in a `tr` folder, and is called `NMTRAN.exe`? + # i.e. can we assume NM-TRAN is in a `tr` folder, and is called `NMTRAN.exe`? nmtran_exe <- file.path(nm_path, "tr", "NMTRAN.exe") # If executable found via bbi.yaml, append NONMEM version as attribute @@ -171,7 +171,7 @@ locate_nmtran <- function(.mod = NULL, .config_path = NULL, nmtran_exe = NULL){ } if(!file_exists(nmtran_exe)){ - stop(glue("Could not find an NMTRAN executable at `{nmtran_exe}`")) + stop(glue("Could not find an NM-TRAN executable at `{nmtran_exe}`")) } return(nmtran_exe) @@ -193,9 +193,10 @@ execute_nmtran <- function(nmtran_exe, mod_path, cmd_args = NULL, dir = NULL) { cmd_args <- if(is.null(cmd_args)) character() else cmd_args + # Store standard output and standard error in the same file nmtran.p <- processx::process$new( command = nmtran_exe, wd = dir, args = cmd_args, - stdout = "|", stderr="|", stdin = file.path(dir, mod_path) + stdout = "|", stderr="2>&1", stdin = file.path(dir, mod_path) ) # Wait till finished for status to be reflective of result @@ -205,18 +206,17 @@ execute_nmtran <- function(nmtran_exe, mod_path, cmd_args = NULL, dir = NULL) { status <- "Not Run" status_val <- nmtran.p$get_exit_status() if(status_val == 0){ - status <- "NMTRAN successful" + status <- "NM-TRAN successful" }else{ - status <- "NMTRAN failed. See errors." + status <- "NM-TRAN failed. See errors." } - # Tabulate NMTRAN results + # Tabulate NM-TRAN results nmtran_results <- list( nmtran_model = nmtran.p$get_input_file(), run_dir = as.character(fs::path_real(dir)), status = status, status_val = status_val, - output_lines = nmtran.p$read_all_output_lines(), - error_lines = nmtran.p$read_all_error_lines() + output_lines = nmtran.p$read_all_output_lines() ) return(nmtran_results) From fea7424dfe6eeb1e514012ec3405aecc8d86a49c Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Tue, 23 Jul 2024 17:22:15 -0400 Subject: [PATCH 15/31] refactor setup and formatting of .bbi_args - now always returns a character vector signifiying the values of `prdefault`, `tprdefault`, and `maxlim` respectively (i.e. '0' '0' '2' as the default) - Unsure if this is set up correctly, as im now receiving different NM-TRAN output messages. Might be a different order than the one highlighted above. --- R/run-nmtran.R | 56 ++++++++++++++++++++++++++++---------------- man/locate_nmtran.Rd | 7 +++--- man/nmtran.Rd | 31 +++++++----------------- 3 files changed, 48 insertions(+), 46 deletions(-) diff --git a/R/run-nmtran.R b/R/run-nmtran.R index 312aeea5f..db21def37 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -1,7 +1,8 @@ #' Interface for running `NM-TRAN` on model objects #' -#' Provides functions to run `NM-TRAN` on model objects for validation and to -#' generate `NM-TRAN` datasets (`FDATA`). +#' Function to run `NM-TRAN` on model objects for validation. The `NM-TRAN` +#' dataset (`FDATA`) and other `NONMEM` artifacts can be further inspected by +#' keeping the run directory around. #' #' @details #' `NM-TRAN` is a preprocessor for `NONMEM` that translates user-specified @@ -10,24 +11,13 @@ #' `run_nmtran()` allows users to test their models ahead of submission to ensure #' correct coding. #' -#' @section Supported `bbi_args`: -#' `run_nmtran()` supports passing the following raw `NMFE` options through to `NM-TRAN` -#' via the familiar `.bbi_args` argument. -#' - **`maxlim`**: Set the maximum values for the buffers used by `NONMEM` -#' (if 0, don't pass `-maxlim` to nmfe) (default is 2) -#' - If `maxlim = 3`, it is preferred to also set `tprdefault = TRUE`, but -#' _not_ `prdefault`, as `NM-TRAN`'s optional resizing of the `PREDPP` size -#' parameter `MAXRECID` may conflict with the `-prdefault` option. -#' - **`prdefault`**: If `TRUE`, do not recompile any routines other than `FSUBS` -#' - **`tprdefault`**: If `TRUE`, test if is okay to do `-prdefault` #' -#' @param .mod A `bbr` model object. +#' @param .mod A `bbi_nonmem_model` object. #' @param .bbi_args A named list specifying arguments to pass to `NM-TRAN`. #' Similar to the `.bbi_args` argument defined in [submit_model()], though here #' only `prdefault`, `tprdefault`, and `maxlim` flags are passed to `NM-TRAN`. -#' See details. -#' @param .config_path Path to a bbi configuration file. If `NULL`, the default, -#' will attempt to use a `bbi.yaml` in the same directory as the model. +#' See [print_bbi_args()] for more details. +#' @inheritParams submit_model #' @param nmtran_exe Path to an `NM-TRAN` executable. If `NULL`, will look for a #' `bbi.yaml` file in the same directory as the model. #' @param delete_on_exit Logical. If `FALSE`, don't delete the temporary folder @@ -66,10 +56,7 @@ run_nmtran <- function( # Combine NONMEM submission args # - The main ones of interest are prdefault, tprdefault, and maxlim, which # impact the evaluation of `NM-TRAN` - .bbi_args <- parse_args_list(.bbi_args, .mod[[YAML_BBI_ARGS]]) - .bbi_args <- Filter(Negate(is.null), .bbi_args[c("prdefault", "tprdefault", "maxlim")]) - cmd_args <- check_bbi_args(.bbi_args) - + cmd_args <- parse_nmtran_args(.mod, .bbi_args = .bbi_args) mod_path <- get_model_path(.mod) data_path <- get_data_path_from_ctl(.mod) @@ -221,3 +208,32 @@ execute_nmtran <- function(nmtran_exe, mod_path, cmd_args = NULL, dir = NULL) { return(nmtran_results) } + + +#' Parse `.bbi_args` and return the three expected `nmfe_options` for `NM-TRAN` +#' in the correct format +#' @inheritParams run_nmtran +#' @noRd +parse_nmtran_args <- function( + .mod, + .bbi_args = NULL +){ + + nmfe_args_def <- list(prdefault = FALSE, tprdefault = FALSE, maxlim = 2) + + # Combine with any options stored in yaml + .nmfe_args <- parse_args_list(.bbi_args, .mod[[YAML_BBI_ARGS]]) + + # Combine with and filter to default nmfe_options + check_bbi_args(.nmfe_args) + .nmfe_args <- parse_args_list(.nmfe_args, nmfe_args_def) + .nmfe_args <- .nmfe_args[names(nmfe_args_def)] + + .nmtran_args <- c( + ifelse(isTRUE(.nmfe_args$prdefault), 1, 0), + ifelse(isTRUE(.nmfe_args$tprdefault), 1, 0), + .nmfe_args$maxlim + ) + + return(as.character(.nmtran_args)) +} diff --git a/man/locate_nmtran.Rd b/man/locate_nmtran.Rd index 05a2b638c..ae5602593 100644 --- a/man/locate_nmtran.Rd +++ b/man/locate_nmtran.Rd @@ -7,10 +7,11 @@ locate_nmtran(.mod = NULL, .config_path = NULL, nmtran_exe = NULL) } \arguments{ -\item{.mod}{A \code{bbr} model object.} +\item{.mod}{A \code{bbi_nonmem_model} object.} -\item{.config_path}{Path to a bbi configuration file. If \code{NULL}, the default, -will attempt to use a \code{bbi.yaml} in the same directory as the model.} +\item{.config_path}{Path to a bbi configuration file. If \code{NULL}, the +default, will attempt to use a \code{bbi.yaml} in the same directory as the +model.} \item{nmtran_exe}{Path to an \code{NM-TRAN} executable. If \code{NULL}, will look for a \code{bbi.yaml} file in the same directory as the model.} diff --git a/man/nmtran.Rd b/man/nmtran.Rd index 58dd7e9c5..e6b24d1e1 100644 --- a/man/nmtran.Rd +++ b/man/nmtran.Rd @@ -14,15 +14,16 @@ run_nmtran( ) } \arguments{ -\item{.mod}{A \code{bbr} model object.} +\item{.mod}{A \code{bbi_nonmem_model} object.} \item{.bbi_args}{A named list specifying arguments to pass to \code{NM-TRAN}. Similar to the \code{.bbi_args} argument defined in \code{\link[=submit_model]{submit_model()}}, though here only \code{prdefault}, \code{tprdefault}, and \code{maxlim} flags are passed to \code{NM-TRAN}. -See details.} +See \code{\link[=print_bbi_args]{print_bbi_args()}} for more details.} -\item{.config_path}{Path to a bbi configuration file. If \code{NULL}, the default, -will attempt to use a \code{bbi.yaml} in the same directory as the model.} +\item{.config_path}{Path to a bbi configuration file. If \code{NULL}, the +default, will attempt to use a \code{bbi.yaml} in the same directory as the +model.} \item{nmtran_exe}{Path to an \code{NM-TRAN} executable. If \code{NULL}, will look for a \code{bbi.yaml} file in the same directory as the model.} @@ -32,8 +33,9 @@ containing the \code{NM-TRAN} run, which will be stored in the current working directory.} } \description{ -Provides functions to run \code{NM-TRAN} on model objects for validation and to -generate \code{NM-TRAN} datasets (\code{FDATA}). +Function to run \code{NM-TRAN} on model objects for validation. The \code{NM-TRAN} +dataset (\code{FDATA}) and other \code{NONMEM} artifacts can be further inspected by +keeping the run directory around. } \details{ \code{NM-TRAN} is a preprocessor for \code{NONMEM} that translates user-specified @@ -48,23 +50,6 @@ correct coding. stream for correct coding before submission. }} -\section{Supported \code{bbi_args}}{ - -\code{run_nmtran()} supports passing the following raw \code{NMFE} options through to \code{NM-TRAN} -via the familiar \code{.bbi_args} argument. -\itemize{ -\item \strong{\code{maxlim}}: Set the maximum values for the buffers used by \code{NONMEM} -(if 0, don't pass \code{-maxlim} to nmfe) (default is 2) -\itemize{ -\item If \code{maxlim = 3}, it is preferred to also set \code{tprdefault = TRUE}, but -\emph{not} \code{prdefault}, as \code{NM-TRAN}'s optional resizing of the \code{PREDPP} size -parameter \code{MAXRECID} may conflict with the \code{-prdefault} option. -} -\item \strong{\code{prdefault}}: If \code{TRUE}, do not recompile any routines other than \code{FSUBS} -\item \strong{\code{tprdefault}}: If \code{TRUE}, test if is okay to do \code{-prdefault} -} -} - \examples{ \dontrun{ mod <- read_model(file.path(MODEL_DIR, 1)) From 315f4cccb9e98bc48e94fd2427f150379b63d3c0 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Tue, 23 Jul 2024 17:53:01 -0400 Subject: [PATCH 16/31] adjust directory handling in execute_nmtran --- R/run-nmtran.R | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/R/run-nmtran.R b/R/run-nmtran.R index db21def37..117679960 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -149,8 +149,6 @@ locate_nmtran <- function(.mod = NULL, .config_path = NULL, nmtran_exe = NULL){ # Set NM-TRAN executable path nm_path <- default_nm$home - # TODO: should we recursively look for this executable, or assume Metworx? - # i.e. can we assume NM-TRAN is in a `tr` folder, and is called `NMTRAN.exe`? nmtran_exe <- file.path(nm_path, "tr", "NMTRAN.exe") # If executable found via bbi.yaml, append NONMEM version as attribute @@ -174,25 +172,26 @@ locate_nmtran <- function(.mod = NULL, .config_path = NULL, nmtran_exe = NULL){ #' @param dir Directory in which to execute the command. #' #' @keywords internal -execute_nmtran <- function(nmtran_exe, mod_path, cmd_args = NULL, dir = NULL) { - if(is.null(dir)) dir <- "." +execute_nmtran <- function(nmtran_exe, mod_path, cmd_args = NULL, dir = ".") { checkmate::assert_directory_exists(dir) + run_dir <- as.character(fs::path_real(dir)) cmd_args <- if(is.null(cmd_args)) character() else cmd_args # Store standard output and standard error in the same file nmtran.p <- processx::process$new( - command = nmtran_exe, wd = dir, args = cmd_args, - stdout = "|", stderr="2>&1", stdin = file.path(dir, mod_path) + command = nmtran_exe, wd = run_dir, args = cmd_args, + stdout = "|", stderr="2>&1", stdin = file.path(run_dir, mod_path) ) # Wait till finished for status to be reflective of result nmtran.p$wait() # Assign status - status <- "Not Run" status_val <- nmtran.p$get_exit_status() - if(status_val == 0){ + if(is.na(status_val)){ + rlang::abort("NM-TRAN terminated") + }else if(status_val == 0){ status <- "NM-TRAN successful" }else{ status <- "NM-TRAN failed. See errors." @@ -201,8 +200,9 @@ execute_nmtran <- function(nmtran_exe, mod_path, cmd_args = NULL, dir = NULL) { # Tabulate NM-TRAN results nmtran_results <- list( nmtran_model = nmtran.p$get_input_file(), - run_dir = as.character(fs::path_real(dir)), - status = status, status_val = status_val, + run_dir = run_dir, + status = status, + status_val = status_val, output_lines = nmtran.p$read_all_output_lines() ) From 7a5a762c40dac18ef7716e0db09217651817da3b Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Tue, 23 Jul 2024 18:03:17 -0400 Subject: [PATCH 17/31] adjust .config_path abort message when invalid path is specified --- R/run-nmtran.R | 15 +++++++-------- tests/testthat/test-workflow-bbi.R | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/R/run-nmtran.R b/R/run-nmtran.R index 117679960..d0294bd48 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -119,16 +119,15 @@ locate_nmtran <- function(.mod = NULL, .config_path = NULL, nmtran_exe = NULL){ config_path <- .config_path %||% file.path(model_dir, "bbi.yaml") if(!file_exists(config_path)){ - rlang::abort( - c( - "x" = "No bbi configuration was found in the execution directory.", + if(is.null(.config_path)){ + msg <- c( + "No bbi configuration was found in the execution directory.", "i" = "Please run `bbi_init()` with the appropriate directory to continue." ) - ) - } - - if(!is.null(.config_path)){ - config_path <- normalizePath(.config_path) + }else{ + msg <- glue("No bbi configuration was found at {.config_path}") + } + rlang::abort(msg) } bbi_config <- yaml::read_yaml(config_path) diff --git a/tests/testthat/test-workflow-bbi.R b/tests/testthat/test-workflow-bbi.R index dbcb7e1e9..e449bb09a 100644 --- a/tests/testthat/test-workflow-bbi.R +++ b/tests/testthat/test-workflow-bbi.R @@ -339,7 +339,7 @@ withr::with_options(list( # no configuration file found expect_error( locate_nmtran(.config_path = file.path(tempdir(), "bbi.yaml")), - "No bbi configuration was found" + "No bbi configuration was found at" ) }) From 85969c972f932610c3b755eec24f8d65823a1e61 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Wed, 24 Jul 2024 14:02:13 -0400 Subject: [PATCH 18/31] documentation and tempdir adjustments --- R/run-nmtran.R | 46 +++++++++++++++++--------------- man/execute_nmtran.Rd | 2 +- man/{nmtran.Rd => run_nmtran.Rd} | 21 ++++++--------- 3 files changed, 33 insertions(+), 36 deletions(-) rename man/{nmtran.Rd => run_nmtran.Rd} (76%) diff --git a/R/run-nmtran.R b/R/run-nmtran.R index d0294bd48..f41e723f8 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -1,8 +1,9 @@ #' Interface for running `NM-TRAN` on model objects #' -#' Function to run `NM-TRAN` on model objects for validation. The `NM-TRAN` -#' dataset (`FDATA`) and other `NONMEM` artifacts can be further inspected by -#' keeping the run directory around. +#' Function to run `NM-TRAN` on a model object to validate its control stream +#' for correct coding before submission. The `NM-TRAN` dataset (`FDATA`) and +#' other `NONMEM` artifacts can be further inspected by keeping the run directory +#' around. #' #' @details #' `NM-TRAN` is a preprocessor for `NONMEM` that translates user-specified @@ -20,7 +21,7 @@ #' @inheritParams submit_model #' @param nmtran_exe Path to an `NM-TRAN` executable. If `NULL`, will look for a #' `bbi.yaml` file in the same directory as the model. -#' @param delete_on_exit Logical. If `FALSE`, don't delete the temporary folder +#' @param clean Logical. If `FALSE`, don't delete the temporary folder #' containing the `NM-TRAN` run, which will be stored in the current working #' directory. #' @@ -33,19 +34,14 @@ #' run_nmtran(mod, nmtran_exe = "/opt/NONMEM/nm75/tr/NMTRAN.exe") #' #' } -#' @name nmtran -NULL - - -#' @describeIn nmtran Run `NM-TRAN` on a model object to validate its control -#' stream for correct coding before submission. +#' #' @export run_nmtran <- function( .mod, .bbi_args = list(prdefault = FALSE, tprdefault = FALSE, maxlim = 2), .config_path = NULL, nmtran_exe = NULL, - delete_on_exit = TRUE + clean = TRUE ){ check_model_object(.mod, "bbi_nonmem_model") @@ -63,21 +59,19 @@ run_nmtran <- function( # make temporary directory in current directory mod_name <- fs::path_ext_remove(basename(mod_path)) - temp_folder <- paste0("nmtran_", mod_name, "_", basename(tempdir())) - if(fs::dir_exists(temp_folder)) fs::dir_delete(temp_folder) - fs::dir_create(temp_folder) - if(isTRUE(delete_on_exit)){ - on.exit(unlink(temp_folder, recursive = TRUE, force = TRUE)) - } + temp_folder <- withr::local_tempdir( + pattern = paste0("nmtran-mod_", mod_name, "-"), + tmpdir = getwd(), clean = clean + ) # Copy model - file.copy(mod_path, temp_folder, overwrite = TRUE) - nmtran_mod <- new_model(file.path(temp_folder, basename(mod_path)), .overwrite = TRUE) + file.copy(mod_path, temp_folder) + nmtran_mod <- new_model(file.path(temp_folder, basename(mod_path))) # Copy dataset & overwrite $DATA record of new model # NM-TRAN will error if data cannot be found if(fs::file_exists(data_path)){ - file.copy(data_path, temp_folder, overwrite = TRUE) + file.copy(data_path, temp_folder) # overwrite $DATA record of new model modify_data_path_ctl(nmtran_mod, data_path = basename(data_path)) } @@ -135,11 +129,19 @@ locate_nmtran <- function(.mod = NULL, .config_path = NULL, nmtran_exe = NULL){ # look for default nonmem installation default_nm <- purrr::keep(nm_config, function(nm_ver){ - !is.null(nm_ver$default) + !is.null(nm_ver$default) & isTRUE(nm_ver$default) }) # Set nonmem path - if(length(default_nm) > 0){ + if(length(default_nm) > 1){ + nm_vers <- paste(names(default_nm), collapse = ", ") + rlang::abort( + c( + glue("Found multiple default NONMEM versions ({nm_vers}) at `{config_path}`"), + "i" = "Please ensure only one version is set to the default" + ) + ) + }else if(length(default_nm) == 1){ default_nm <- default_nm[[1]] }else{ # If no default, use the last one (likely higher version) diff --git a/man/execute_nmtran.Rd b/man/execute_nmtran.Rd index 98627d0e1..8121026dd 100644 --- a/man/execute_nmtran.Rd +++ b/man/execute_nmtran.Rd @@ -4,7 +4,7 @@ \alias{execute_nmtran} \title{Execute \code{NM-TRAN} in a given directory} \usage{ -execute_nmtran(nmtran_exe, mod_path, cmd_args = NULL, dir = NULL) +execute_nmtran(nmtran_exe, mod_path, cmd_args = NULL, dir = ".") } \arguments{ \item{nmtran_exe}{Path to \code{NM-TRAN} executable.} diff --git a/man/nmtran.Rd b/man/run_nmtran.Rd similarity index 76% rename from man/nmtran.Rd rename to man/run_nmtran.Rd index e6b24d1e1..51fdd00f8 100644 --- a/man/nmtran.Rd +++ b/man/run_nmtran.Rd @@ -1,7 +1,6 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/run-nmtran.R -\name{nmtran} -\alias{nmtran} +\name{run_nmtran} \alias{run_nmtran} \title{Interface for running \code{NM-TRAN} on model objects} \usage{ @@ -10,7 +9,7 @@ run_nmtran( .bbi_args = list(prdefault = FALSE, tprdefault = FALSE, maxlim = 2), .config_path = NULL, nmtran_exe = NULL, - delete_on_exit = TRUE + clean = TRUE ) } \arguments{ @@ -28,14 +27,15 @@ model.} \item{nmtran_exe}{Path to an \code{NM-TRAN} executable. If \code{NULL}, will look for a \code{bbi.yaml} file in the same directory as the model.} -\item{delete_on_exit}{Logical. If \code{FALSE}, don't delete the temporary folder +\item{clean}{Logical. If \code{FALSE}, don't delete the temporary folder containing the \code{NM-TRAN} run, which will be stored in the current working directory.} } \description{ -Function to run \code{NM-TRAN} on model objects for validation. The \code{NM-TRAN} -dataset (\code{FDATA}) and other \code{NONMEM} artifacts can be further inspected by -keeping the run directory around. +Function to run \code{NM-TRAN} on a model object to validate its control stream +for correct coding before submission. The \code{NM-TRAN} dataset (\code{FDATA}) and +other \code{NONMEM} artifacts can be further inspected by keeping the run directory +around. } \details{ \code{NM-TRAN} is a preprocessor for \code{NONMEM} that translates user-specified @@ -44,12 +44,6 @@ control stream data and instructions into a form executable by \code{NONMEM}. \code{run_nmtran()} allows users to test their models ahead of submission to ensure correct coding. } -\section{Functions}{ -\itemize{ -\item \code{run_nmtran()}: Run \code{NM-TRAN} on a model object to validate its control -stream for correct coding before submission. - -}} \examples{ \dontrun{ mod <- read_model(file.path(MODEL_DIR, 1)) @@ -59,4 +53,5 @@ run_nmtran(mod) run_nmtran(mod, nmtran_exe = "/opt/NONMEM/nm75/tr/NMTRAN.exe") } + } From e0c0281b27d74011e42b1031026307e7d2cc16c2 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Wed, 24 Jul 2024 17:30:42 -0400 Subject: [PATCH 19/31] refactor setup of NM-TRAN executable - users can now specify the nonmem version, which automatically determines the NM-TRAN executable path, assuming bbi has been initialized. - While this change potentially sacrifices some flexibility, specifying the nonmem version is an easier entry point and ensures bbi has been initialized. - update tests according to recent refactors --- R/run-nmtran.R | 145 ++++++++++++++++++----------- man/locate_nmtran.Rd | 23 ----- man/nmtran_setup.Rd | 35 +++++++ man/run_nmtran.Rd | 8 +- tests/testthat/test-workflow-bbi.R | 85 ++++++++++++----- 5 files changed, 190 insertions(+), 106 deletions(-) delete mode 100644 man/locate_nmtran.Rd create mode 100644 man/nmtran_setup.Rd diff --git a/R/run-nmtran.R b/R/run-nmtran.R index f41e723f8..899513338 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -39,25 +39,19 @@ run_nmtran <- function( .mod, .bbi_args = list(prdefault = FALSE, tprdefault = FALSE, maxlim = 2), + .nonmem_version = NULL, .config_path = NULL, - nmtran_exe = NULL, clean = TRUE ){ check_model_object(.mod, "bbi_nonmem_model") - # Capture NONMEM and NM-TRAN options - nmtran_exe <- locate_nmtran(.mod, .config_path, nmtran_exe) - nm_ver <- attr(nmtran_exe, "nonmem_version") - - # Combine NONMEM submission args - # - The main ones of interest are prdefault, tprdefault, and maxlim, which - # impact the evaluation of `NM-TRAN` - cmd_args <- parse_nmtran_args(.mod, .bbi_args = .bbi_args) + # Capture NONMEM/NM-TRAN options and format `NM-TRAN` args + nmtran_specs <- nmtran_setup(.mod, .nonmem_version, .bbi_args, .config_path) mod_path <- get_model_path(.mod) data_path <- get_data_path_from_ctl(.mod) - # make temporary directory in current directory + # Make temporary directory in current directory mod_name <- fs::path_ext_remove(basename(mod_path)) temp_folder <- withr::local_tempdir( pattern = paste0("nmtran-mod_", mod_name, "-"), @@ -79,12 +73,14 @@ run_nmtran <- function( # Run NM-TRAN nmtran_results <- c( list( - nmtran_exe = as.character(nmtran_exe), - nonmem_version = nm_ver, + nmtran_exe = nmtran_specs$nmtran_exe, + nonmem_version = nmtran_specs$nonmem_version, absolute_model_path = mod_path ), execute_nmtran( - nmtran_exe, mod_path = basename(mod_path), cmd_args = cmd_args, + nmtran_specs$nmtran_exe, + mod_path = basename(mod_path), + cmd_args = nmtran_specs$cmd_args, dir = temp_folder ) ) @@ -95,44 +91,50 @@ run_nmtran <- function( } -#' Search for and validate existence of an `NM-TRAN` executable -#' -#' If `nmtran_exe = NULL`, this will look for a `bbi.yaml` file in the same -#' directory as the model. +#' Sets up `NM-TRAN` to run by identifying an `NM-TRAN` executable for a +#' provided `NONMEM` version and formatting arguments based on `.bbi_args`, +#' the corresponding `.mod` yaml file, and any `nmfe_options` defined in a +#' `bbi.yaml` file #' #' @inheritParams run_nmtran -#' #' @keywords internal -locate_nmtran <- function(.mod = NULL, .config_path = NULL, nmtran_exe = NULL){ +nmtran_setup <- function( + .mod = NULL, + .nonmem_version = NULL, + .bbi_args = NULL, + .config_path = NULL +){ + bbi_yaml_path <- get_bbi_yaml_path(.mod, .config_path = .config_path) + bbi_yaml <- yaml::read_yaml(bbi_yaml_path) - if(is.null(nmtran_exe)){ - if(!is.null(.mod)){ - check_model_object(.mod, "bbi_nonmem_model") - model_dir <- get_model_working_directory(.mod) - } - config_path <- .config_path %||% file.path(model_dir, "bbi.yaml") + # Combine NONMEM submission args + # - The main ones of interest are prdefault, tprdefault, and maxlim, which + # impact the evaluation of `NM-TRAN` + # - Priority: .bbi_args > model yaml > bbi.yaml + cmd_args <- parse_nmtran_args( + .mod, .bbi_args = .bbi_args, nmfe_options = bbi_yaml$nmfe_options + ) - if(!file_exists(config_path)){ - if(is.null(.config_path)){ - msg <- c( - "No bbi configuration was found in the execution directory.", - "i" = "Please run `bbi_init()` with the appropriate directory to continue." + # Check nonmem version + nm_config <- bbi_yaml$nonmem + if (!is.null(.nonmem_version)) { + # check for valid version + if (!(.nonmem_version %in% names(nm_config))) { + rlang::abort( + c( + "Must specify a valid `.nonmem_version` for bbi_init().", + "i" = glue("{bbi_yaml_path} contains the following options:"), + glue("`{paste(names(nm_config), collapse='`, `')}`") ) - }else{ - msg <- glue("No bbi configuration was found at {.config_path}") - } - rlang::abort(msg) + ) } - - bbi_config <- yaml::read_yaml(config_path) - nm_config <- bbi_config$nonmem - - # look for default nonmem installation + default_nm <- nm_config[[.nonmem_version]] + }else{ + # Look for default nonmem installation default_nm <- purrr::keep(nm_config, function(nm_ver){ !is.null(nm_ver$default) & isTRUE(nm_ver$default) }) - # Set nonmem path if(length(default_nm) > 1){ nm_vers <- paste(names(default_nm), collapse = ", ") rlang::abort( @@ -147,22 +149,21 @@ locate_nmtran <- function(.mod = NULL, .config_path = NULL, nmtran_exe = NULL){ # If no default, use the last one (likely higher version) default_nm <- nm_config[[length(nm_config)]] } - - # Set NM-TRAN executable path - nm_path <- default_nm$home - nmtran_exe <- file.path(nm_path, "tr", "NMTRAN.exe") - - # If executable found via bbi.yaml, append NONMEM version as attribute - attr(nmtran_exe, "nonmem_version") <- basename(default_nm$home) } - if(!file_exists(nmtran_exe)){ - stop(glue("Could not find an NM-TRAN executable at `{nmtran_exe}`")) - } - return(nmtran_exe) -} + # Set NM-TRAN executable path + nm_path <- default_nm$home + nmtran_exe <- file.path(nm_path, "tr", "NMTRAN.exe") + return( + list( + nmtran_exe = nmtran_exe, + nonmem_version = basename(default_nm$home), + cmd_args = cmd_args + ) + ) +} #' Execute `NM-TRAN` in a given directory #' @@ -211,22 +212,56 @@ execute_nmtran <- function(nmtran_exe, mod_path, cmd_args = NULL, dir = ".") { } +#' Helper for finding and reading in a `bbi.yaml` file given a `bbi_nonmem_model` +#' object or explicit `.config_path`. +#' @inheritParams run_nmtran +#' @noRd +get_bbi_yaml_path <- function(.mod = NULL, .config_path = NULL){ + if(!is.null(.mod)){ + check_model_object(.mod, "bbi_nonmem_model") + model_dir <- get_model_working_directory(.mod) + } + bbi_yaml_path <- .config_path %||% file.path(model_dir, "bbi.yaml") + + if(!file_exists(bbi_yaml_path)){ + if(is.null(.config_path)){ + msg <- c( + "No bbi configuration was found in the execution directory.", + "i" = "Please run `bbi_init()` with the appropriate directory to continue." + ) + }else{ + msg <- glue("No bbi configuration was found at {.config_path}") + } + rlang::abort(msg) + } + + return(bbi_yaml_path) +} + + #' Parse `.bbi_args` and return the three expected `nmfe_options` for `NM-TRAN` #' in the correct format #' @inheritParams run_nmtran +#' @param nmfe_options named list of nmfe options defined in a `bbi.yaml` file. #' @noRd parse_nmtran_args <- function( .mod, - .bbi_args = NULL + .bbi_args = NULL, + nmfe_options = NULL ){ nmfe_args_def <- list(prdefault = FALSE, tprdefault = FALSE, maxlim = 2) - # Combine with any options stored in yaml + # Combine with any options stored in model yaml, preferring .bbi_args .nmfe_args <- parse_args_list(.bbi_args, .mod[[YAML_BBI_ARGS]]) - # Combine with and filter to default nmfe_options + # Combine with nmfe options stored in bbi.yaml, preferring .nmfe_args + .nmfe_args <- parse_args_list(.nmfe_args, nmfe_options) + + # Check provided args check_bbi_args(.nmfe_args) + + # Combine with and filter to default nmfe_options .nmfe_args <- parse_args_list(.nmfe_args, nmfe_args_def) .nmfe_args <- .nmfe_args[names(nmfe_args_def)] diff --git a/man/locate_nmtran.Rd b/man/locate_nmtran.Rd deleted file mode 100644 index ae5602593..000000000 --- a/man/locate_nmtran.Rd +++ /dev/null @@ -1,23 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/run-nmtran.R -\name{locate_nmtran} -\alias{locate_nmtran} -\title{Search for and validate existence of an \code{NM-TRAN} executable} -\usage{ -locate_nmtran(.mod = NULL, .config_path = NULL, nmtran_exe = NULL) -} -\arguments{ -\item{.mod}{A \code{bbi_nonmem_model} object.} - -\item{.config_path}{Path to a bbi configuration file. If \code{NULL}, the -default, will attempt to use a \code{bbi.yaml} in the same directory as the -model.} - -\item{nmtran_exe}{Path to an \code{NM-TRAN} executable. If \code{NULL}, will look for a -\code{bbi.yaml} file in the same directory as the model.} -} -\description{ -If \code{nmtran_exe = NULL}, this will look for a \code{bbi.yaml} file in the same -directory as the model. -} -\keyword{internal} diff --git a/man/nmtran_setup.Rd b/man/nmtran_setup.Rd new file mode 100644 index 000000000..6193deef2 --- /dev/null +++ b/man/nmtran_setup.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/run-nmtran.R +\name{nmtran_setup} +\alias{nmtran_setup} +\title{Sets up \code{NM-TRAN} to run by identifying an \code{NM-TRAN} executable for a +provided \code{NONMEM} version and formatting arguments based on \code{.bbi_args}, +the corresponding \code{.mod} yaml file, and any \code{nmfe_options} defined in a +\code{bbi.yaml} file} +\usage{ +nmtran_setup( + .mod = NULL, + .nonmem_version = NULL, + .bbi_args = NULL, + .config_path = NULL +) +} +\arguments{ +\item{.mod}{A \code{bbi_nonmem_model} object.} + +\item{.bbi_args}{A named list specifying arguments to pass to \code{NM-TRAN}. +Similar to the \code{.bbi_args} argument defined in \code{\link[=submit_model]{submit_model()}}, though here +only \code{prdefault}, \code{tprdefault}, and \code{maxlim} flags are passed to \code{NM-TRAN}. +See \code{\link[=print_bbi_args]{print_bbi_args()}} for more details.} + +\item{.config_path}{Path to a bbi configuration file. If \code{NULL}, the +default, will attempt to use a \code{bbi.yaml} in the same directory as the +model.} +} +\description{ +Sets up \code{NM-TRAN} to run by identifying an \code{NM-TRAN} executable for a +provided \code{NONMEM} version and formatting arguments based on \code{.bbi_args}, +the corresponding \code{.mod} yaml file, and any \code{nmfe_options} defined in a +\code{bbi.yaml} file +} +\keyword{internal} diff --git a/man/run_nmtran.Rd b/man/run_nmtran.Rd index 51fdd00f8..56925fb96 100644 --- a/man/run_nmtran.Rd +++ b/man/run_nmtran.Rd @@ -7,8 +7,8 @@ run_nmtran( .mod, .bbi_args = list(prdefault = FALSE, tprdefault = FALSE, maxlim = 2), + .nonmem_version = NULL, .config_path = NULL, - nmtran_exe = NULL, clean = TRUE ) } @@ -24,12 +24,12 @@ See \code{\link[=print_bbi_args]{print_bbi_args()}} for more details.} default, will attempt to use a \code{bbi.yaml} in the same directory as the model.} -\item{nmtran_exe}{Path to an \code{NM-TRAN} executable. If \code{NULL}, will look for a -\code{bbi.yaml} file in the same directory as the model.} - \item{clean}{Logical. If \code{FALSE}, don't delete the temporary folder containing the \code{NM-TRAN} run, which will be stored in the current working directory.} + +\item{nmtran_exe}{Path to an \code{NM-TRAN} executable. If \code{NULL}, will look for a +\code{bbi.yaml} file in the same directory as the model.} } \description{ Function to run \code{NM-TRAN} on a model object to validate its control stream diff --git a/tests/testthat/test-workflow-bbi.R b/tests/testthat/test-workflow-bbi.R index e449bb09a..a06650c92 100644 --- a/tests/testthat/test-workflow-bbi.R +++ b/tests/testthat/test-workflow-bbi.R @@ -307,42 +307,76 @@ withr::with_options(list( }) describe("run_nmtran", { - it("locate_nmtran", { + it("nmtran_setup: executable and nonmem version", { mod1 <- read_model(file.path(MODEL_DIR_BBI, "1")) # Using model object, looks for bbi.yaml - nmtran_exe <- locate_nmtran(mod1) + nmtran_specs <- nmtran_setup(mod1) # Confirm executable - expect_equal(as.character(nmtran_exe), "/opt/NONMEM/nm74gf/tr/NMTRAN.exe") + expect_equal(nmtran_specs$nmtran_exe, "/opt/NONMEM/nm74gf/tr/NMTRAN.exe") # Confirm NONMEM version - expect_equal(attr(nmtran_exe, "nonmem_version"), "nm74gf") + expect_equal(nmtran_specs$nonmem_version, "nm74gf") - # Passed executable - nmtran_exe <- locate_nmtran(mod1, nmtran_exe = "/opt/NONMEM/nm74gf/tr/NMTRAN.exe") + # Passed nonmem version + nmtran_specs <- nmtran_setup(mod1, .nonmem_version = "nm75") # Confirm executable - expect_equal(as.character(nmtran_exe), "/opt/NONMEM/nm74gf/tr/NMTRAN.exe") + expect_equal(nmtran_specs$nmtran_exe, "/opt/NONMEM/nm75/tr/NMTRAN.exe") # Confirm NONMEM version - expect_true(is.null(attr(nmtran_exe, "nonmem_version"))) + expect_equal(nmtran_specs$nonmem_version, "nm75") # Passed config_path - nmtran_exe <- locate_nmtran(.config_path = file.path(MODEL_DIR_BBI, "bbi.yaml")) + nmtran_specs <- nmtran_setup(.config_path = file.path(MODEL_DIR_BBI, "bbi.yaml")) # Confirm executable - expect_equal(as.character(nmtran_exe), "/opt/NONMEM/nm74gf/tr/NMTRAN.exe") + expect_equal(nmtran_specs$nmtran_exe, "/opt/NONMEM/nm74gf/tr/NMTRAN.exe") # Confirm NONMEM version - expect_equal(attr(nmtran_exe, "nonmem_version"), "nm74gf") + expect_equal(nmtran_specs$nonmem_version, "nm74gf") - # Wrong nmtran_exe path passed + # Incorrect nonmem version expect_error( - locate_nmtran(mod1, nmtran_exe = "/opt/NONMEM/nm74gf/tr/NMTRAN2.exe"), - "Could not find an NMTRAN executable" + nmtran_setup(mod1, .nonmem_version = "nm74"), + "Must specify a valid `.nonmem_version`" ) # no configuration file found expect_error( - locate_nmtran(.config_path = file.path(tempdir(), "bbi.yaml")), + nmtran_setup(mod1, .config_path = file.path(tempdir(), "bbi.yaml")), "No bbi configuration was found at" ) }) + it("nmtran_setup: NM-TRAN args", { + mod1 <- read_model(file.path(MODEL_DIR_BBI, "1")) + + # Default nmfe_options passed as .bbi_args + nmtran_specs <- nmtran_setup(mod1) + expect_equal(nmtran_specs$cmd_args, c("0", "0", "2")) + + # Override with .bbi_args + nmtran_specs <- nmtran_setup(mod1, .bbi_args = list(maxlim = 3, prdefault = TRUE)) + expect_equal(nmtran_specs$cmd_args, c("1", "0", "3")) + + # Override with model yaml + current_args <- mod1$bbi_args + mod1 <- add_bbi_args(mod1, list(maxlim = 3, tprdefault = TRUE)) + nmtran_specs <- nmtran_setup(mod1, .bbi_args = NULL) + expect_equal(nmtran_specs$cmd_args, c("0", "1", "3")) + mod1 <- replace_all_bbi_args(mod1, current_args) + + # Override with bbi.yaml + # - note: we only look at the `nmfe_options` in bbi.yaml. If these options + # were passed as regular `.bbi_args` (e.g., in a bbi_init() call), they + # would not be picked up. + bbi_yaml_path <- get_bbi_yaml_path(mod1) + bbi_yaml <- yaml::read_yaml(bbi_yaml_path) + bbi_yaml$nmfe_options$maxlim <- 3 + yaml::write_yaml(bbi_yaml, bbi_yaml_path) + nmtran_specs <- nmtran_setup(mod1, .bbi_args = NULL) + expect_equal(nmtran_specs$cmd_args, c("0", "0", "3")) + + # Revert back for any other tests + bbi_yaml$nmfe_options$maxlim <- 2 + yaml::write_yaml(bbi_yaml, bbi_yaml_path) + }) + it("execute_nmtran", { # Execute in subdirectory to avoid messing with other tests nmtran_dir <- file.path(MODEL_DIR_BBI, "nmtran") @@ -350,8 +384,8 @@ withr::with_options(list( on.exit(fs::dir_delete(nmtran_dir), add = TRUE) # Copy model file into new model dir - fs::file_copy(CTL_TEST_FILE, nmtran_dir, overwrite = TRUE) - mod1 <- new_model(file.path(nmtran_dir, "1"), .overwrite = TRUE) + fs::file_copy(CTL_TEST_FILE, nmtran_dir) + mod1 <- new_model(file.path(nmtran_dir, "1")) # create new bbi.yaml bbi_init( @@ -361,34 +395,37 @@ withr::with_options(list( .bbi_args = list(mpi_exec_path = get_mpiexec_path()) ) - nmtran_exe <- locate_nmtran(mod1) + nmtran_specs <- nmtran_setup(mod1) + nmtran_results <- execute_nmtran( - nmtran_exe, mod_path = basename(get_model_path(mod1)), dir = nmtran_dir + nmtran_specs$nmtran_exe, mod_path = basename(get_model_path(mod1)), + dir = nmtran_dir ) # Check attributes expect_equal(nmtran_dir, nmtran_results$run_dir) expect_equal(nmtran_results$status_val, 0) - expect_equal(nmtran_results$status, "NMTRAN successful") + expect_equal(nmtran_results$status, "NM-TRAN successful") # Test failure data_path <- "test/this/path/data.csv" modify_data_path_ctl(mod1, data_path) nmtran_results <- execute_nmtran( - nmtran_exe, mod_path = basename(get_model_path(mod1)), dir = nmtran_dir + nmtran_specs$nmtran_exe, mod_path = basename(get_model_path(mod1)), + dir = nmtran_dir ) # Check attributes expect_equal(nmtran_results$status_val, 4) - expect_equal(nmtran_results$status, "NMTRAN failed. See errors.") + expect_equal(nmtran_results$status, "NM-TRAN failed. See errors.") }) it("run_nmtran: integration", { # create model mod1 <- read_model(file.path(MODEL_DIR_BBI, "1")) - nmtran_results <- run_nmtran(mod1, delete_on_exit = FALSE) + nmtran_results <- run_nmtran(mod1, clean = FALSE) on.exit(fs::dir_delete(nmtran_results$run_dir)) # Check attributes @@ -399,7 +436,7 @@ withr::with_options(list( ) expect_equal(nmtran_results$nonmem_version, "nm74gf") expect_equal(nmtran_results$status_val, 0) - expect_equal(nmtran_results$status, "NMTRAN successful") + expect_equal(nmtran_results$status, "NM-TRAN successful") }) }) From f1e9c7d74e9212a52662d5ff4839423fb6b3fafc Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Wed, 24 Jul 2024 18:22:12 -0400 Subject: [PATCH 20/31] run nmtran_presort before `NM-TRAN` - doc fixes --- R/run-nmtran.R | 50 +++++++++++++++++++++++++----- man/execute_nmtran.Rd | 5 +-- man/nmtran_setup.Rd | 4 +++ man/run_nmtran.Rd | 7 +++-- tests/testthat/test-workflow-bbi.R | 20 ++++++++++-- 5 files changed, 72 insertions(+), 14 deletions(-) diff --git a/R/run-nmtran.R b/R/run-nmtran.R index 899513338..da5b9dc40 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -19,8 +19,9 @@ #' only `prdefault`, `tprdefault`, and `maxlim` flags are passed to `NM-TRAN`. #' See [print_bbi_args()] for more details. #' @inheritParams submit_model -#' @param nmtran_exe Path to an `NM-TRAN` executable. If `NULL`, will look for a -#' `bbi.yaml` file in the same directory as the model. +#' @param .nonmem_version Character scalar for default version of NONMEM to use. +#' If left `NULL`, will look for the default version specified in the provided +#' `bbi` configuration file. #' @param clean Logical. If `FALSE`, don't delete the temporary folder #' containing the `NM-TRAN` run, which will be stored in the current working #' directory. @@ -167,6 +168,9 @@ nmtran_setup <- function( #' Execute `NM-TRAN` in a given directory #' +#' Execute `NM-TRAN` in a given directory. Also runs `nmtran_presort` if an +#' executable is found. +#' #' @param nmtran_exe Path to `NM-TRAN` executable. #' @param mod_path Path of a model to evaluate. Should be relative to `dir`. #' @param cmd_args A character vector of command line arguments for the `NM-TRAN` @@ -174,16 +178,48 @@ nmtran_setup <- function( #' @param dir Directory in which to execute the command. #' #' @keywords internal -execute_nmtran <- function(nmtran_exe, mod_path, cmd_args = NULL, dir = ".") { +execute_nmtran <- function( + nmtran_exe, + mod_path, + cmd_args = c("0", "0", "2"), + dir = "." +){ checkmate::assert_directory_exists(dir) + checkmate::assert_character(cmd_args, len = 3) + run_dir <- as.character(fs::path_real(dir)) - cmd_args <- if(is.null(cmd_args)) character() else cmd_args + # Check if nmtran_presort exists + presort_dir <- file.path(dirname(dirname(nmtran_exe)), "util") + nmtran_presort_exe <- file.path(presort_dir, "nmtran_presort") + run_presort <- unname(fs::file_exists(nmtran_presort_exe)) + + # Preprocess with nmtran_presort + if(run_presort){ + presort.p <- processx::process$new( + command = nmtran_presort_exe, wd = run_dir, args = character(), + stdout = "|", stderr = "2>&1", stdin = file.path(run_dir, mod_path) + ) + + presort.p$wait() + presort_output <- presort.p$read_all_output() - # Store standard output and standard error in the same file + presort_status_val <- presort.p$get_exit_status() + if(is.na(presort_status_val)){ + rlang::abort("nmtran_presort terminated unexpectedly") + } else if(presort_status_val != 0) { + rlang::abort("nmtran_presort failed. See errors.") + } + + # Write the output to tempzzzz1 control stream + writeLines(presort_output, con = file.path(run_dir, "tempzzzz1.ctl")) + } + + # Run NM-TRAN + stdin_file <- ifelse(run_presort, "tempzzzz1.ctl", mod_path) nmtran.p <- processx::process$new( command = nmtran_exe, wd = run_dir, args = cmd_args, - stdout = "|", stderr="2>&1", stdin = file.path(run_dir, mod_path) + stdout = "|", stderr="2>&1", stdin = file.path(run_dir, stdin_file) ) # Wait till finished for status to be reflective of result @@ -192,7 +228,7 @@ execute_nmtran <- function(nmtran_exe, mod_path, cmd_args = NULL, dir = ".") { # Assign status status_val <- nmtran.p$get_exit_status() if(is.na(status_val)){ - rlang::abort("NM-TRAN terminated") + rlang::abort("NM-TRAN terminated unexpectedly") }else if(status_val == 0){ status <- "NM-TRAN successful" }else{ diff --git a/man/execute_nmtran.Rd b/man/execute_nmtran.Rd index 8121026dd..44366e785 100644 --- a/man/execute_nmtran.Rd +++ b/man/execute_nmtran.Rd @@ -4,7 +4,7 @@ \alias{execute_nmtran} \title{Execute \code{NM-TRAN} in a given directory} \usage{ -execute_nmtran(nmtran_exe, mod_path, cmd_args = NULL, dir = ".") +execute_nmtran(nmtran_exe, mod_path, cmd_args = c("0", "0", "2"), dir = ".") } \arguments{ \item{nmtran_exe}{Path to \code{NM-TRAN} executable.} @@ -17,6 +17,7 @@ execution call} \item{dir}{Directory in which to execute the command.} } \description{ -Execute \code{NM-TRAN} in a given directory +Execute \code{NM-TRAN} in a given directory. Also runs \code{nmtran_presort} if an +executable is found. } \keyword{internal} diff --git a/man/nmtran_setup.Rd b/man/nmtran_setup.Rd index 6193deef2..115c471a4 100644 --- a/man/nmtran_setup.Rd +++ b/man/nmtran_setup.Rd @@ -17,6 +17,10 @@ nmtran_setup( \arguments{ \item{.mod}{A \code{bbi_nonmem_model} object.} +\item{.nonmem_version}{Character scalar for default version of NONMEM to use. +If left \code{NULL}, will look for the default version specified in the provided +\code{bbi} configuration file.} + \item{.bbi_args}{A named list specifying arguments to pass to \code{NM-TRAN}. Similar to the \code{.bbi_args} argument defined in \code{\link[=submit_model]{submit_model()}}, though here only \code{prdefault}, \code{tprdefault}, and \code{maxlim} flags are passed to \code{NM-TRAN}. diff --git a/man/run_nmtran.Rd b/man/run_nmtran.Rd index 56925fb96..3bc1566f2 100644 --- a/man/run_nmtran.Rd +++ b/man/run_nmtran.Rd @@ -20,6 +20,10 @@ Similar to the \code{.bbi_args} argument defined in \code{\link[=submit_model]{s only \code{prdefault}, \code{tprdefault}, and \code{maxlim} flags are passed to \code{NM-TRAN}. See \code{\link[=print_bbi_args]{print_bbi_args()}} for more details.} +\item{.nonmem_version}{Character scalar for default version of NONMEM to use. +If left \code{NULL}, will look for the default version specified in the provided +\code{bbi} configuration file.} + \item{.config_path}{Path to a bbi configuration file. If \code{NULL}, the default, will attempt to use a \code{bbi.yaml} in the same directory as the model.} @@ -27,9 +31,6 @@ model.} \item{clean}{Logical. If \code{FALSE}, don't delete the temporary folder containing the \code{NM-TRAN} run, which will be stored in the current working directory.} - -\item{nmtran_exe}{Path to an \code{NM-TRAN} executable. If \code{NULL}, will look for a -\code{bbi.yaml} file in the same directory as the model.} } \description{ Function to run \code{NM-TRAN} on a model object to validate its control stream diff --git a/tests/testthat/test-workflow-bbi.R b/tests/testthat/test-workflow-bbi.R index a06650c92..bf3a4249e 100644 --- a/tests/testthat/test-workflow-bbi.R +++ b/tests/testthat/test-workflow-bbi.R @@ -425,7 +425,8 @@ withr::with_options(list( # create model mod1 <- read_model(file.path(MODEL_DIR_BBI, "1")) - nmtran_results <- run_nmtran(mod1, clean = FALSE) + # Run with no presort + nmtran_results <- run_nmtran(mod1, .nonmem_version = "nm73gf", clean = FALSE) on.exit(fs::dir_delete(nmtran_results$run_dir)) # Check attributes @@ -434,7 +435,22 @@ withr::with_options(list( file.path(nmtran_results$run_dir, basename(get_model_path(mod1))), nmtran_results$nmtran_model ) - expect_equal(nmtran_results$nonmem_version, "nm74gf") + expect_equal(nmtran_results$nonmem_version, "nm73gf") + expect_equal(nmtran_results$status_val, 0) + expect_equal(nmtran_results$status, "NM-TRAN successful") + + + # Run with presort + nmtran_results <- run_nmtran(mod1, .nonmem_version = "nm75", clean = FALSE) + on.exit(fs::dir_delete(nmtran_results$run_dir)) + + # Check attributes + expect_equal(get_model_path(mod1), nmtran_results$absolute_model_path) + expect_equal( + file.path(nmtran_results$run_dir, "tempzzzz1.ctl"), + nmtran_results$nmtran_model + ) + expect_equal(nmtran_results$nonmem_version, "nm75") expect_equal(nmtran_results$status_val, 0) expect_equal(nmtran_results$status, "NM-TRAN successful") }) From bfa71e18336c385e37936473bd66d0e3a3cff33e Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Thu, 25 Jul 2024 13:00:24 -0400 Subject: [PATCH 21/31] refactored nmtran_presort and updated documentation/tests - nmtran_presort detection now happens in nmtran_setup() - Print whether nmtran_presort was run ahead of `NM-TRAN` - Add documentation about nmtran_presort, and make `run_dir` an argument (defaulting to temporary directory). An example illustrates the benefit of changing this to your working directory --- R/print.R | 3 +- R/run-nmtran.R | 55 +++++++++++++++++++++--------- man/execute_nmtran.Rd | 12 ++++++- man/run_nmtran.Rd | 22 ++++++++---- tests/testthat/test-workflow-bbi.R | 3 ++ 5 files changed, 71 insertions(+), 24 deletions(-) diff --git a/R/print.R b/R/print.R index 08f50ba66..8ef778385 100644 --- a/R/print.R +++ b/R/print.R @@ -470,7 +470,7 @@ print.nmtran_process <- function(x, ...){ nm_version <- x[["nonmem_version"]] if(is.null(nm_version)) nm_version <- "unknown" - heading('Status') + heading(cli::col_magenta("NM-TRAN Status")) subheading(status) heading("Absolute Model Path") @@ -480,6 +480,7 @@ print.nmtran_process <- function(x, ...){ cli::cli_bullets( c( "*" = "NM-TRAN Executable: {.path {x[['nmtran_exe']]}}", + "*" = "nmtran_presort: {cli::col_cyan(!is.null(x[['nmtran_presort_exe']]))}", "*" = "NONMEM Version: {.val {nm_version}}", "*" = "Run Directory: {.path {x[['run_dir']]}}" ) diff --git a/R/run-nmtran.R b/R/run-nmtran.R index da5b9dc40..90bac3edb 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -9,9 +9,12 @@ #' `NM-TRAN` is a preprocessor for `NONMEM` that translates user-specified #' control stream data and instructions into a form executable by `NONMEM`. #' -#' `run_nmtran()` allows users to test their models ahead of submission to ensure -#' correct coding. -#' +#' Note that `nmtran_presort` is run ahead of `NM-TRAN` for `NONMEM` versions +#' `'nm74gf'`, `'nm74gf_nmfe'`, and `'nm75'`. +#' - `nmtran_presort` is an supplementary utility that preprocesses the control +#' stream to ensure it is in the correct format for `NM-TRAN`. It is particularly +#' relevant for handling specific data manipulations and ensuring compatibility +#' with the `NM-TRAN` executable. #' #' @param .mod A `bbi_nonmem_model` object. #' @param .bbi_args A named list specifying arguments to pass to `NM-TRAN`. @@ -22,17 +25,19 @@ #' @param .nonmem_version Character scalar for default version of NONMEM to use. #' If left `NULL`, will look for the default version specified in the provided #' `bbi` configuration file. -#' @param clean Logical. If `FALSE`, don't delete the temporary folder +#' @param clean Logical (`T`/`F`). If `FALSE`, don't delete the temporary folder #' containing the `NM-TRAN` run, which will be stored in the current working #' directory. +#' @param run_dir Directory to run `NM-TRAN` in. Only relevant if `clean = FALSE`. #' #' @examples #' \dontrun{ +#' #' mod <- read_model(file.path(MODEL_DIR, 1)) -#' run_nmtran(mod) +#' run_nmtran(mod, .nonmem_version = "nm75") #' -#' # Set the path to an NM-TRAN executable -#' run_nmtran(mod, nmtran_exe = "/opt/NONMEM/nm75/tr/NMTRAN.exe") +#' # Save the run directory for manual inspection +#' run_nmtran(mod, clean = FALSE, run_dir = getwd()) #' #' } #' @@ -42,6 +47,7 @@ run_nmtran <- function( .bbi_args = list(prdefault = FALSE, tprdefault = FALSE, maxlim = 2), .nonmem_version = NULL, .config_path = NULL, + run_dir = tempdir(), clean = TRUE ){ check_model_object(.mod, "bbi_nonmem_model") @@ -56,7 +62,7 @@ run_nmtran <- function( mod_name <- fs::path_ext_remove(basename(mod_path)) temp_folder <- withr::local_tempdir( pattern = paste0("nmtran-mod_", mod_name, "-"), - tmpdir = getwd(), clean = clean + tmpdir = run_dir, clean = clean ) # Copy model @@ -75,6 +81,7 @@ run_nmtran <- function( nmtran_results <- c( list( nmtran_exe = nmtran_specs$nmtran_exe, + nmtran_presort_exe = nmtran_specs$nmtran_presort_exe, nonmem_version = nmtran_specs$nonmem_version, absolute_model_path = mod_path ), @@ -82,6 +89,7 @@ run_nmtran <- function( nmtran_specs$nmtran_exe, mod_path = basename(mod_path), cmd_args = nmtran_specs$cmd_args, + nmtran_presort_exe = nmtran_specs$nmtran_presort_exe, dir = temp_folder ) ) @@ -123,7 +131,7 @@ nmtran_setup <- function( if (!(.nonmem_version %in% names(nm_config))) { rlang::abort( c( - "Must specify a valid `.nonmem_version` for bbi_init().", + "Must specify a valid `.nonmem_version` for run_nmtran.", "i" = glue("{bbi_yaml_path} contains the following options:"), glue("`{paste(names(nm_config), collapse='`, `')}`") ) @@ -152,16 +160,21 @@ nmtran_setup <- function( } } - # Set NM-TRAN executable path nm_path <- default_nm$home nmtran_exe <- file.path(nm_path, "tr", "NMTRAN.exe") + # Check if nmtran_presort exists + presort_dir <- file.path(dirname(dirname(nmtran_exe)), "util") + nmtran_presort_exe <- file.path(presort_dir, "nmtran_presort") + run_presort <- unname(fs::file_exists(nmtran_presort_exe)) + return( list( nmtran_exe = nmtran_exe, nonmem_version = basename(default_nm$home), - cmd_args = cmd_args + cmd_args = cmd_args, + nmtran_presort_exe = if(run_presort) nmtran_presort_exe else NULL ) ) } @@ -175,6 +188,9 @@ nmtran_setup <- function( #' @param mod_path Path of a model to evaluate. Should be relative to `dir`. #' @param cmd_args A character vector of command line arguments for the `NM-TRAN` #' execution call +#' @param nmtran_presort_exe Path to `nmtran_presort` executable. Only available +#' for `NONMEM` versions `nm74gf`, `nm74gf_nmfe`, and `nm75`. If provided, will run +#' `nmtran_presort` before running `NM-TRAN`. Set to `NULL` to skip this step. #' @param dir Directory in which to execute the command. #' #' @keywords internal @@ -182,6 +198,7 @@ execute_nmtran <- function( nmtran_exe, mod_path, cmd_args = c("0", "0", "2"), + nmtran_presort_exe = NULL, dir = "." ){ checkmate::assert_directory_exists(dir) @@ -190,9 +207,7 @@ execute_nmtran <- function( run_dir <- as.character(fs::path_real(dir)) # Check if nmtran_presort exists - presort_dir <- file.path(dirname(dirname(nmtran_exe)), "util") - nmtran_presort_exe <- file.path(presort_dir, "nmtran_presort") - run_presort <- unname(fs::file_exists(nmtran_presort_exe)) + run_presort <- !is.null(nmtran_presort_exe) # Preprocess with nmtran_presort if(run_presort){ @@ -207,8 +222,16 @@ execute_nmtran <- function( presort_status_val <- presort.p$get_exit_status() if(is.na(presort_status_val)){ rlang::abort("nmtran_presort terminated unexpectedly") - } else if(presort_status_val != 0) { - rlang::abort("nmtran_presort failed. See errors.") + }else if(presort_status_val != 0){ + return( + list( + nmtran_model = presort.p$get_input_file(), + run_dir = run_dir, + status = "nmtran_presort failed. See errors.", + status_val = presort_status_val, + output_lines = presort_output + ) + ) } # Write the output to tempzzzz1 control stream diff --git a/man/execute_nmtran.Rd b/man/execute_nmtran.Rd index 44366e785..7fd83cd06 100644 --- a/man/execute_nmtran.Rd +++ b/man/execute_nmtran.Rd @@ -4,7 +4,13 @@ \alias{execute_nmtran} \title{Execute \code{NM-TRAN} in a given directory} \usage{ -execute_nmtran(nmtran_exe, mod_path, cmd_args = c("0", "0", "2"), dir = ".") +execute_nmtran( + nmtran_exe, + mod_path, + cmd_args = c("0", "0", "2"), + nmtran_presort_exe = NULL, + dir = "." +) } \arguments{ \item{nmtran_exe}{Path to \code{NM-TRAN} executable.} @@ -14,6 +20,10 @@ execute_nmtran(nmtran_exe, mod_path, cmd_args = c("0", "0", "2"), dir = ".") \item{cmd_args}{A character vector of command line arguments for the \code{NM-TRAN} execution call} +\item{nmtran_presort_exe}{Path to \code{nmtran_presort} executable. Only available +for \code{NONMEM} versions \code{nm74gf}, \code{nm74gf_nmfe}, and \code{nm75}. If provided, will run +\code{nmtran_presort} before running \code{NM-TRAN}. Set to \code{NULL} to skip this step.} + \item{dir}{Directory in which to execute the command.} } \description{ diff --git a/man/run_nmtran.Rd b/man/run_nmtran.Rd index 3bc1566f2..87fbf116c 100644 --- a/man/run_nmtran.Rd +++ b/man/run_nmtran.Rd @@ -9,6 +9,7 @@ run_nmtran( .bbi_args = list(prdefault = FALSE, tprdefault = FALSE, maxlim = 2), .nonmem_version = NULL, .config_path = NULL, + run_dir = tempdir(), clean = TRUE ) } @@ -28,7 +29,9 @@ If left \code{NULL}, will look for the default version specified in the provided default, will attempt to use a \code{bbi.yaml} in the same directory as the model.} -\item{clean}{Logical. If \code{FALSE}, don't delete the temporary folder +\item{run_dir}{Directory to run \code{NM-TRAN} in. Only relevant if \code{clean = FALSE}.} + +\item{clean}{Logical (\code{T}/\code{F}). If \code{FALSE}, don't delete the temporary folder containing the \code{NM-TRAN} run, which will be stored in the current working directory.} } @@ -42,16 +45,23 @@ around. \code{NM-TRAN} is a preprocessor for \code{NONMEM} that translates user-specified control stream data and instructions into a form executable by \code{NONMEM}. -\code{run_nmtran()} allows users to test their models ahead of submission to ensure -correct coding. +Note that \code{nmtran_presort} is run ahead of \code{NM-TRAN} for \code{NONMEM} versions +\code{'nm74gf'}, \code{'nm74gf_nmfe'}, and \code{'nm75'}. +\itemize{ +\item \code{nmtran_presort} is an supplementary utility that preprocesses the control +stream to ensure it is in the correct format for \code{NM-TRAN}. It is particularly +relevant for handling specific data manipulations and ensuring compatibility +with the \code{NM-TRAN} executable. +} } \examples{ \dontrun{ + mod <- read_model(file.path(MODEL_DIR, 1)) -run_nmtran(mod) +run_nmtran(mod, .nonmem_version = "nm75") -# Set the path to an NM-TRAN executable -run_nmtran(mod, nmtran_exe = "/opt/NONMEM/nm75/tr/NMTRAN.exe") +# Save the run directory for manual inspection +run_nmtran(mod, clean = FALSE, run_dir = getwd()) } diff --git a/tests/testthat/test-workflow-bbi.R b/tests/testthat/test-workflow-bbi.R index bf3a4249e..e4b5b7f2d 100644 --- a/tests/testthat/test-workflow-bbi.R +++ b/tests/testthat/test-workflow-bbi.R @@ -438,6 +438,7 @@ withr::with_options(list( expect_equal(nmtran_results$nonmem_version, "nm73gf") expect_equal(nmtran_results$status_val, 0) expect_equal(nmtran_results$status, "NM-TRAN successful") + expect_true(is.null(nmtran_results$nmtran_presort_exe)) # Run with presort @@ -453,6 +454,8 @@ withr::with_options(list( expect_equal(nmtran_results$nonmem_version, "nm75") expect_equal(nmtran_results$status_val, 0) expect_equal(nmtran_results$status, "NM-TRAN successful") + expect_false(is.null(nmtran_results$nmtran_presort_exe)) + expect_true(fs::file_exists(nmtran_results$nmtran_presort_exe)) }) }) From d52d123034072096c21190674b6b7240ed9f70b5 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Thu, 25 Jul 2024 13:48:36 -0400 Subject: [PATCH 22/31] convert process$new() calls to run() calls - update print method to only print the run directory if it still exists --- R/print.R | 8 ++++++-- R/run-nmtran.R | 30 +++++++++++++----------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/R/print.R b/R/print.R index 8ef778385..5b0782f7a 100644 --- a/R/print.R +++ b/R/print.R @@ -481,11 +481,15 @@ print.nmtran_process <- function(x, ...){ c( "*" = "NM-TRAN Executable: {.path {x[['nmtran_exe']]}}", "*" = "nmtran_presort: {cli::col_cyan(!is.null(x[['nmtran_presort_exe']]))}", - "*" = "NONMEM Version: {.val {nm_version}}", - "*" = "Run Directory: {.path {x[['run_dir']]}}" + "*" = "NONMEM Version: {.val {nm_version}}" ) ) + # Print run directory if it still exists (clean = FALSE) + if(fs::dir_exists(x[['run_dir']])){ + cli::cli_bullets(c("*" = "Run Directory: {.path {x[['run_dir']]}}")) + } + if (is_valid_print(x[["output_lines"]])) { heading('Standard Output') cat_line(x[["output_lines"]]) diff --git a/R/run-nmtran.R b/R/run-nmtran.R index 90bac3edb..0cfee6c40 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -211,45 +211,41 @@ execute_nmtran <- function( # Preprocess with nmtran_presort if(run_presort){ - presort.p <- processx::process$new( + presort.p <- processx::run( command = nmtran_presort_exe, wd = run_dir, args = character(), - stdout = "|", stderr = "2>&1", stdin = file.path(run_dir, mod_path) + stdout = "|", stdin = file.path(run_dir, mod_path), + stderr_to_stdout = TRUE, error_on_status = FALSE ) - presort.p$wait() - presort_output <- presort.p$read_all_output() - - presort_status_val <- presort.p$get_exit_status() + presort_status_val <- presort.p$status if(is.na(presort_status_val)){ rlang::abort("nmtran_presort terminated unexpectedly") }else if(presort_status_val != 0){ return( list( - nmtran_model = presort.p$get_input_file(), + nmtran_model = file.path(run_dir, mod_path), run_dir = run_dir, status = "nmtran_presort failed. See errors.", status_val = presort_status_val, - output_lines = presort_output + output_lines = presort.p$stdout ) ) } # Write the output to tempzzzz1 control stream - writeLines(presort_output, con = file.path(run_dir, "tempzzzz1.ctl")) + writeLines(presort.p$stdout, con = file.path(run_dir, "tempzzzz1.ctl")) } # Run NM-TRAN stdin_file <- ifelse(run_presort, "tempzzzz1.ctl", mod_path) - nmtran.p <- processx::process$new( + nmtran.p <- processx::run( command = nmtran_exe, wd = run_dir, args = cmd_args, - stdout = "|", stderr="2>&1", stdin = file.path(run_dir, stdin_file) + stdout = "|", stdin = file.path(run_dir, stdin_file), + stderr_to_stdout = TRUE, error_on_status = FALSE ) - # Wait till finished for status to be reflective of result - nmtran.p$wait() - # Assign status - status_val <- nmtran.p$get_exit_status() + status_val <- nmtran.p$status if(is.na(status_val)){ rlang::abort("NM-TRAN terminated unexpectedly") }else if(status_val == 0){ @@ -260,11 +256,11 @@ execute_nmtran <- function( # Tabulate NM-TRAN results nmtran_results <- list( - nmtran_model = nmtran.p$get_input_file(), + nmtran_model = stdin_file, run_dir = run_dir, status = status, status_val = status_val, - output_lines = nmtran.p$read_all_output_lines() + output_lines = nmtran.p$stdout ) return(nmtran_results) From ed5d19ffe0355e1021d76fc20a01bda804a05c14 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Tue, 30 Jul 2024 16:17:04 -0400 Subject: [PATCH 23/31] refactors and updates from feedback - Documentation updates - Move tests to their own file and update based on refactors --- R/print.R | 13 +- R/run-nmtran.R | 142 +++++++++---------- man/execute_nmtran.Rd | 3 +- man/nmtran_setup.Rd | 15 ++- man/run_nmtran.Rd | 29 ++-- tests/testthat/test-run-nmtran.R | 210 +++++++++++++++++++++++++++++ tests/testthat/test-workflow-bbi.R | 154 --------------------- 7 files changed, 308 insertions(+), 258 deletions(-) create mode 100644 tests/testthat/test-run-nmtran.R diff --git a/R/print.R b/R/print.R index 5b0782f7a..a20332473 100644 --- a/R/print.R +++ b/R/print.R @@ -467,9 +467,6 @@ print.nmtran_process <- function(x, ...){ status <- col_red(status) } - nm_version <- x[["nonmem_version"]] - if(is.null(nm_version)) nm_version <- "unknown" - heading(cli::col_magenta("NM-TRAN Status")) subheading(status) @@ -479,9 +476,9 @@ print.nmtran_process <- function(x, ...){ heading("NM-TRAN Specifications") cli::cli_bullets( c( + "*" = "NONMEM Version: {.val {x[['nonmem_version']]}}", "*" = "NM-TRAN Executable: {.path {x[['nmtran_exe']]}}", - "*" = "nmtran_presort: {cli::col_cyan(!is.null(x[['nmtran_presort_exe']]))}", - "*" = "NONMEM Version: {.val {nm_version}}" + "*" = "nmtran_presort: {cli::col_cyan(!is.null(x[['nmtran_presort_exe']]))}" ) ) @@ -490,9 +487,9 @@ print.nmtran_process <- function(x, ...){ cli::cli_bullets(c("*" = "Run Directory: {.path {x[['run_dir']]}}")) } - if (is_valid_print(x[["output_lines"]])) { - heading('Standard Output') - cat_line(x[["output_lines"]]) + if (is_valid_print(x[["output"]])) { + heading('Output') + cat_line(x[["output"]]) } } diff --git a/R/run-nmtran.R b/R/run-nmtran.R index 0cfee6c40..03757be54 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -9,43 +9,37 @@ #' `NM-TRAN` is a preprocessor for `NONMEM` that translates user-specified #' control stream data and instructions into a form executable by `NONMEM`. #' -#' Note that `nmtran_presort` is run ahead of `NM-TRAN` for `NONMEM` versions -#' `'nm74gf'`, `'nm74gf_nmfe'`, and `'nm75'`. -#' - `nmtran_presort` is an supplementary utility that preprocesses the control -#' stream to ensure it is in the correct format for `NM-TRAN`. It is particularly -#' relevant for handling specific data manipulations and ensuring compatibility -#' with the `NM-TRAN` executable. +#' Note that `nmtran_presort` is run ahead of `NM-TRAN` for `NONMEM 7.4` and later +#' - `nmtran_presort` is a supplementary utility that preprocesses the control +#' stream to ensure it is in the correct format for `NM-TRAN`. #' #' @param .mod A `bbi_nonmem_model` object. #' @param .bbi_args A named list specifying arguments to pass to `NM-TRAN`. #' Similar to the `.bbi_args` argument defined in [submit_model()], though here -#' only `prdefault`, `tprdefault`, and `maxlim` flags are passed to `NM-TRAN`. +#' only `prdefault`, `tprdefault`, and `maxlim` arguments are passed to `NM-TRAN`. +#' `nm_version` is also supported and specifies which `NM-TRAN` executable to use. #' See [print_bbi_args()] for more details. #' @inheritParams submit_model -#' @param .nonmem_version Character scalar for default version of NONMEM to use. -#' If left `NULL`, will look for the default version specified in the provided -#' `bbi` configuration file. -#' @param clean Logical (`T`/`F`). If `FALSE`, don't delete the temporary folder -#' containing the `NM-TRAN` run, which will be stored in the current working -#' directory. +#' @param clean Logical (`T`/`F`). If `FALSE`, don't delete the temporary directory +#' containing the `NM-TRAN` run. #' @param run_dir Directory to run `NM-TRAN` in. Only relevant if `clean = FALSE`. #' #' @examples #' \dontrun{ #' #' mod <- read_model(file.path(MODEL_DIR, 1)) -#' run_nmtran(mod, .nonmem_version = "nm75") +#' run_nmtran(mod, .bbi_args = list(nm_version = "nm74gf")) #' #' # Save the run directory for manual inspection #' run_nmtran(mod, clean = FALSE, run_dir = getwd()) #' #' } #' +#' @return An S3 object of class `nmtran_process` #' @export run_nmtran <- function( .mod, - .bbi_args = list(prdefault = FALSE, tprdefault = FALSE, maxlim = 2), - .nonmem_version = NULL, + .bbi_args = NULL, .config_path = NULL, run_dir = tempdir(), clean = TRUE @@ -53,42 +47,49 @@ run_nmtran <- function( check_model_object(.mod, "bbi_nonmem_model") # Capture NONMEM/NM-TRAN options and format `NM-TRAN` args - nmtran_specs <- nmtran_setup(.mod, .nonmem_version, .bbi_args, .config_path) + nmtran_specs <- nmtran_setup( + .mod, .bbi_args = .bbi_args, .config_path = .config_path + ) mod_path <- get_model_path(.mod) data_path <- get_data_path_from_ctl(.mod) - # Make temporary directory in current directory - mod_name <- fs::path_ext_remove(basename(mod_path)) + # NM-TRAN run directory temp_folder <- withr::local_tempdir( - pattern = paste0("nmtran-mod_", mod_name, "-"), + pattern = paste0("nmtran-mod_", get_model_id(.mod), "-"), tmpdir = run_dir, clean = clean ) # Copy model - file.copy(mod_path, temp_folder) + fs::file_copy(mod_path, temp_folder) nmtran_mod <- new_model(file.path(temp_folder, basename(mod_path))) # Copy dataset & overwrite $DATA record of new model # NM-TRAN will error if data cannot be found if(fs::file_exists(data_path)){ - file.copy(data_path, temp_folder) + new_data_path <- file.path(temp_folder, "data.csv") + fs::file_copy(data_path, new_data_path) # overwrite $DATA record of new model - modify_data_path_ctl(nmtran_mod, data_path = basename(data_path)) + modify_data_path_ctl(nmtran_mod, data_path = basename(new_data_path)) + }else{ + rlang::abort( + glue("Could not find data at `{data_path}`") + ) } # Run NM-TRAN nmtran_results <- c( list( + nonmem_version = nmtran_specs$nonmem_version, nmtran_exe = nmtran_specs$nmtran_exe, nmtran_presort_exe = nmtran_specs$nmtran_presort_exe, - nonmem_version = nmtran_specs$nonmem_version, - absolute_model_path = mod_path + absolute_model_path = mod_path, + args = nmtran_specs$cmd_args ), execute_nmtran( nmtran_specs$nmtran_exe, - mod_path = basename(mod_path), - cmd_args = nmtran_specs$cmd_args, + mod_path = mod_path, + cmd_args = unname(nmtran_specs$cmd_args), nmtran_presort_exe = nmtran_specs$nmtran_presort_exe, dir = temp_folder ) @@ -106,11 +107,15 @@ run_nmtran <- function( #' `bbi.yaml` file #' #' @inheritParams run_nmtran +#' @param .nonmem_version Character scalar for default version of NONMEM to use. +#' If left `NULL`, will look for the default version specified in the provided +#' `bbi` configuration file. +#' #' @keywords internal nmtran_setup <- function( - .mod = NULL, - .nonmem_version = NULL, + .mod, .bbi_args = NULL, + .nonmem_version = .bbi_args[["nm_version"]], .config_path = NULL ){ bbi_yaml_path <- get_bbi_yaml_path(.mod, .config_path = .config_path) @@ -141,7 +146,7 @@ nmtran_setup <- function( }else{ # Look for default nonmem installation default_nm <- purrr::keep(nm_config, function(nm_ver){ - !is.null(nm_ver$default) & isTRUE(nm_ver$default) + !is.null(nm_ver$default) && isTRUE(nm_ver$default) }) if(length(default_nm) > 1){ @@ -165,14 +170,13 @@ nmtran_setup <- function( nmtran_exe <- file.path(nm_path, "tr", "NMTRAN.exe") # Check if nmtran_presort exists - presort_dir <- file.path(dirname(dirname(nmtran_exe)), "util") - nmtran_presort_exe <- file.path(presort_dir, "nmtran_presort") + nmtran_presort_exe <- file.path(nm_path, "util", "nmtran_presort") run_presort <- unname(fs::file_exists(nmtran_presort_exe)) return( list( nmtran_exe = nmtran_exe, - nonmem_version = basename(default_nm$home), + nonmem_version = basename(nm_path), cmd_args = cmd_args, nmtran_presort_exe = if(run_presort) nmtran_presort_exe else NULL ) @@ -185,7 +189,8 @@ nmtran_setup <- function( #' executable is found. #' #' @param nmtran_exe Path to `NM-TRAN` executable. -#' @param mod_path Path of a model to evaluate. Should be relative to `dir`. +#' @param mod_path Path of a model to evaluate. Should be relative to `dir`. Nested +#' directories not supported. #' @param cmd_args A character vector of command line arguments for the `NM-TRAN` #' execution call #' @param nmtran_presort_exe Path to `nmtran_presort` executable. Only available @@ -205,43 +210,33 @@ execute_nmtran <- function( checkmate::assert_character(cmd_args, len = 3) run_dir <- as.character(fs::path_real(dir)) - - # Check if nmtran_presort exists - run_presort <- !is.null(nmtran_presort_exe) + nmtran_input <- mod_path_new <- file.path(run_dir, basename(mod_path)) # Preprocess with nmtran_presort - if(run_presort){ - presort.p <- processx::run( - command = nmtran_presort_exe, wd = run_dir, args = character(), - stdout = "|", stdin = file.path(run_dir, mod_path), - stderr_to_stdout = TRUE, error_on_status = FALSE + if(!is.null(nmtran_presort_exe)){ + # NM-TRAN input is now output from nmtran_presort + nmtran_input <- file.path( + paste0(fs::path_ext_remove(mod_path_new), "_presort.", fs::path_ext(mod_path_new)) ) - presort_status_val <- presort.p$status - if(is.na(presort_status_val)){ - rlang::abort("nmtran_presort terminated unexpectedly") - }else if(presort_status_val != 0){ - return( - list( - nmtran_model = file.path(run_dir, mod_path), - run_dir = run_dir, - status = "nmtran_presort failed. See errors.", - status_val = presort_status_val, - output_lines = presort.p$stdout - ) - ) - } - - # Write the output to tempzzzz1 control stream - writeLines(presort.p$stdout, con = file.path(run_dir, "tempzzzz1.ctl")) + presort.p <- processx::run( + command = nmtran_presort_exe, + stdin = mod_path_new, + stdout = nmtran_input, + wd = run_dir, + error_on_status = TRUE + ) } # Run NM-TRAN - stdin_file <- ifelse(run_presort, "tempzzzz1.ctl", mod_path) nmtran.p <- processx::run( - command = nmtran_exe, wd = run_dir, args = cmd_args, - stdout = "|", stdin = file.path(run_dir, stdin_file), - stderr_to_stdout = TRUE, error_on_status = FALSE + command = nmtran_exe, + args = cmd_args, + stdin = nmtran_input, + stdout = "|", + wd = run_dir, + stderr_to_stdout = TRUE, + error_on_status = FALSE ) # Assign status @@ -249,18 +244,18 @@ execute_nmtran <- function( if(is.na(status_val)){ rlang::abort("NM-TRAN terminated unexpectedly") }else if(status_val == 0){ - status <- "NM-TRAN successful" + status <- "Finished Running" }else{ - status <- "NM-TRAN failed. See errors." + status <- "Failed. See errors." } # Tabulate NM-TRAN results nmtran_results <- list( - nmtran_model = stdin_file, + nmtran_model = basename(nmtran_input), run_dir = run_dir, status = status, status_val = status_val, - output_lines = nmtran.p$stdout + output = nmtran.p$stdout ) return(nmtran_results) @@ -305,6 +300,10 @@ parse_nmtran_args <- function( nmfe_options = NULL ){ + # These are the default NMFE options that are passed to `NM-TRAN` + # - They should always be passed (in the correct order), so set the + # defaults here and merge with other bbi_args to ensure these are passed + # to NM-TRAN nmfe_args_def <- list(prdefault = FALSE, tprdefault = FALSE, maxlim = 2) # Combine with any options stored in model yaml, preferring .bbi_args @@ -321,10 +320,11 @@ parse_nmtran_args <- function( .nmfe_args <- .nmfe_args[names(nmfe_args_def)] .nmtran_args <- c( - ifelse(isTRUE(.nmfe_args$prdefault), 1, 0), - ifelse(isTRUE(.nmfe_args$tprdefault), 1, 0), + if(isTRUE(.nmfe_args$prdefault)) 1 else 0, + if(isTRUE(.nmfe_args$tprdefault)) 1 else 0, .nmfe_args$maxlim - ) + ) %>% as.character() %>% + stats::setNames(names(nmfe_args_def)) - return(as.character(.nmtran_args)) + return(.nmtran_args) } diff --git a/man/execute_nmtran.Rd b/man/execute_nmtran.Rd index 7fd83cd06..63d7a382a 100644 --- a/man/execute_nmtran.Rd +++ b/man/execute_nmtran.Rd @@ -15,7 +15,8 @@ execute_nmtran( \arguments{ \item{nmtran_exe}{Path to \code{NM-TRAN} executable.} -\item{mod_path}{Path of a model to evaluate. Should be relative to \code{dir}.} +\item{mod_path}{Path of a model to evaluate. Should be relative to \code{dir}. Nested +directories not supported.} \item{cmd_args}{A character vector of command line arguments for the \code{NM-TRAN} execution call} diff --git a/man/nmtran_setup.Rd b/man/nmtran_setup.Rd index 115c471a4..36d3abd4a 100644 --- a/man/nmtran_setup.Rd +++ b/man/nmtran_setup.Rd @@ -8,24 +8,25 @@ the corresponding \code{.mod} yaml file, and any \code{nmfe_options} defined in \code{bbi.yaml} file} \usage{ nmtran_setup( - .mod = NULL, - .nonmem_version = NULL, + .mod, .bbi_args = NULL, + .nonmem_version = .bbi_args[["nm_version"]], .config_path = NULL ) } \arguments{ \item{.mod}{A \code{bbi_nonmem_model} object.} -\item{.nonmem_version}{Character scalar for default version of NONMEM to use. -If left \code{NULL}, will look for the default version specified in the provided -\code{bbi} configuration file.} - \item{.bbi_args}{A named list specifying arguments to pass to \code{NM-TRAN}. Similar to the \code{.bbi_args} argument defined in \code{\link[=submit_model]{submit_model()}}, though here -only \code{prdefault}, \code{tprdefault}, and \code{maxlim} flags are passed to \code{NM-TRAN}. +only \code{prdefault}, \code{tprdefault}, and \code{maxlim} arguments are passed to \code{NM-TRAN}. +\code{nm_version} is also supported and specifies which \code{NM-TRAN} executable to use. See \code{\link[=print_bbi_args]{print_bbi_args()}} for more details.} +\item{.nonmem_version}{Character scalar for default version of NONMEM to use. +If left \code{NULL}, will look for the default version specified in the provided +\code{bbi} configuration file.} + \item{.config_path}{Path to a bbi configuration file. If \code{NULL}, the default, will attempt to use a \code{bbi.yaml} in the same directory as the model.} diff --git a/man/run_nmtran.Rd b/man/run_nmtran.Rd index 87fbf116c..9d7315f8b 100644 --- a/man/run_nmtran.Rd +++ b/man/run_nmtran.Rd @@ -6,8 +6,7 @@ \usage{ run_nmtran( .mod, - .bbi_args = list(prdefault = FALSE, tprdefault = FALSE, maxlim = 2), - .nonmem_version = NULL, + .bbi_args = NULL, .config_path = NULL, run_dir = tempdir(), clean = TRUE @@ -18,22 +17,21 @@ run_nmtran( \item{.bbi_args}{A named list specifying arguments to pass to \code{NM-TRAN}. Similar to the \code{.bbi_args} argument defined in \code{\link[=submit_model]{submit_model()}}, though here -only \code{prdefault}, \code{tprdefault}, and \code{maxlim} flags are passed to \code{NM-TRAN}. +only \code{prdefault}, \code{tprdefault}, and \code{maxlim} arguments are passed to \code{NM-TRAN}. +\code{nm_version} is also supported and specifies which \code{NM-TRAN} executable to use. See \code{\link[=print_bbi_args]{print_bbi_args()}} for more details.} -\item{.nonmem_version}{Character scalar for default version of NONMEM to use. -If left \code{NULL}, will look for the default version specified in the provided -\code{bbi} configuration file.} - \item{.config_path}{Path to a bbi configuration file. If \code{NULL}, the default, will attempt to use a \code{bbi.yaml} in the same directory as the model.} \item{run_dir}{Directory to run \code{NM-TRAN} in. Only relevant if \code{clean = FALSE}.} -\item{clean}{Logical (\code{T}/\code{F}). If \code{FALSE}, don't delete the temporary folder -containing the \code{NM-TRAN} run, which will be stored in the current working -directory.} +\item{clean}{Logical (\code{T}/\code{F}). If \code{FALSE}, don't delete the temporary directory +containing the \code{NM-TRAN} run.} +} +\value{ +An S3 object of class \code{nmtran_process} } \description{ Function to run \code{NM-TRAN} on a model object to validate its control stream @@ -45,20 +43,17 @@ around. \code{NM-TRAN} is a preprocessor for \code{NONMEM} that translates user-specified control stream data and instructions into a form executable by \code{NONMEM}. -Note that \code{nmtran_presort} is run ahead of \code{NM-TRAN} for \code{NONMEM} versions -\code{'nm74gf'}, \code{'nm74gf_nmfe'}, and \code{'nm75'}. +Note that \code{nmtran_presort} is run ahead of \code{NM-TRAN} for \verb{NONMEM 7.4} and later \itemize{ -\item \code{nmtran_presort} is an supplementary utility that preprocesses the control -stream to ensure it is in the correct format for \code{NM-TRAN}. It is particularly -relevant for handling specific data manipulations and ensuring compatibility -with the \code{NM-TRAN} executable. +\item \code{nmtran_presort} is a supplementary utility that preprocesses the control +stream to ensure it is in the correct format for \code{NM-TRAN}. } } \examples{ \dontrun{ mod <- read_model(file.path(MODEL_DIR, 1)) -run_nmtran(mod, .nonmem_version = "nm75") +run_nmtran(mod, .bbi_args = list(nm_version = "nm74gf")) # Save the run directory for manual inspection run_nmtran(mod, clean = FALSE, run_dir = getwd()) diff --git a/tests/testthat/test-run-nmtran.R b/tests/testthat/test-run-nmtran.R new file mode 100644 index 000000000..ced81626c --- /dev/null +++ b/tests/testthat/test-run-nmtran.R @@ -0,0 +1,210 @@ +context("testing NM-TRAN") + +# define constants +MODEL_DIR_BBI <- file.path(dirname(ABS_MODEL_DIR), "test-nmtran-models") +NONMEM_DIR <- Sys.getenv("BBR_TESTS_NONMEM_DIR", "/opt/NONMEM") +REQ_NONMEM_VERSIONS <- c("nm73gf", "nm74gf", "nm75", Sys.getenv("BBR_TESTS_NONMEM_VERSION")) +REQ_NONMEM_VERSIONS <- unique(REQ_NONMEM_VERSIONS[REQ_NONMEM_VERSIONS != ""]) + +# Don't assume NONMEM is available if not on Metworx. +if (Sys.getenv("METWORX_VERSION") == "" || Sys.getenv("SKIP_BBI_TEST") == "true") { + skip("test-workflow-bbi only runs on Metworx because it needs NONMEM installed") +} +skip_long_tests("skipping long-running bbi workflow tests") + + +# Skip if required NONMEM versions are not installed +if(!all(REQ_NONMEM_VERSIONS %in% basename(fs::dir_ls(NONMEM_DIR)))){ + req_nm_ver_txt <- paste(REQ_NONMEM_VERSIONS, collapse = ", ") + skip(glue("test-run-nmtran-bbi only requires the following NONMEM versions {req_nm_ver_txt}")) +} + + +# cleanup function +cleanup_bbi <- function(.recreate_dir = FALSE) { + if (fs::dir_exists(MODEL_DIR_BBI)) fs::dir_delete(MODEL_DIR_BBI) + if (isTRUE(.recreate_dir)) fs::dir_create(MODEL_DIR_BBI) +} +cleanup_bbi(.recreate_dir = TRUE) + + +# set options and run tests +withr::with_options(list( + bbr.bbi_exe_path = read_bbi_path(), + bbr.verbose = FALSE), { + + # cleanup when done + on.exit({ + Sys.sleep(3) # wait for some NONMEM mess to delete itself + cleanup_bbi() + }) + + # clear old bbi.yaml + if (fs::file_exists(file.path(MODEL_DIR_BBI, "bbi.yaml"))) fs::file_delete(file.path(MODEL_DIR_BBI, "bbi.yaml")) + + # create new bbi.yaml + bbi_init( + MODEL_DIR_BBI, + .nonmem_dir = NONMEM_DIR, + .nonmem_version = Sys.getenv("BBR_TESTS_NONMEM_VERSION", "nm74gf"), + .bbi_args = list(mpi_exec_path = get_mpiexec_path()) + ) + + # copy model files into new model dir (not run) + fs::file_copy(CTL_TEST_FILE, MODEL_DIR_BBI) + fs::file_copy(YAML_TEST_FILE, MODEL_DIR_BBI) + + describe("run_nmtran", { + it("nmtran_setup: executable and nonmem version", { + mod1 <- read_model(file.path(MODEL_DIR_BBI, "1")) + # Using model object, looks for bbi.yaml + nmtran_specs <- nmtran_setup(mod1) + # Confirm executable + expect_equal(nmtran_specs$nmtran_exe, "/opt/NONMEM/nm74gf/tr/NMTRAN.exe") + # Confirm NONMEM version + expect_equal(nmtran_specs$nonmem_version, "nm74gf") + + # Passed nonmem version + nmtran_specs <- nmtran_setup(mod1, .nonmem_version = "nm75") + nmtran_specs2 <- nmtran_setup(mod1, list(nm_version = "nm75")) + # Confirm executable + expect_equal(nmtran_specs$nmtran_exe, "/opt/NONMEM/nm75/tr/NMTRAN.exe") + # Confirm NONMEM version + expect_equal(nmtran_specs$nonmem_version, "nm75") + expect_equal(nmtran_specs2$nonmem_version, "nm75") + + + # Passed config_path + nmtran_specs <- nmtran_setup(mod1, .config_path = file.path(MODEL_DIR_BBI, "bbi.yaml")) + # Confirm executable + expect_equal(nmtran_specs$nmtran_exe, "/opt/NONMEM/nm74gf/tr/NMTRAN.exe") + # Confirm NONMEM version + expect_equal(nmtran_specs$nonmem_version, "nm74gf") + + # Incorrect nonmem version + expect_error( + nmtran_setup(mod1, .bbi_args = list(nm_version = "nm74")), + "Must specify a valid `.nonmem_version`" + ) + + # no configuration file found + expect_error( + nmtran_setup(mod1, .config_path = file.path(tempfile(), "bbi.yaml")), + "No bbi configuration was found at" + ) + }) + + it("nmtran_setup: NM-TRAN args", { + mod1 <- read_model(file.path(MODEL_DIR_BBI, "1")) + + # Default nmfe_options passed as .bbi_args + nmtran_specs <- nmtran_setup(mod1) + expect_equal(unname(nmtran_specs$cmd_args), c("0", "0", "2")) + + # Override with .bbi_args + nmtran_specs <- nmtran_setup(mod1, .bbi_args = list(maxlim = 3, prdefault = TRUE)) + expect_equal(unname(nmtran_specs$cmd_args), c("1", "0", "3")) + + # Override with model yaml + current_args <- mod1$bbi_args + mod1 <- add_bbi_args(mod1, list(maxlim = 3, tprdefault = TRUE)) + nmtran_specs <- nmtran_setup(mod1, .bbi_args = NULL) + expect_equal(unname(nmtran_specs$cmd_args), c("0", "1", "3")) + mod1 <- replace_all_bbi_args(mod1, current_args) + + # Override with bbi.yaml + # - note: we only look at the `nmfe_options` in bbi.yaml. If these options + # were passed as regular `.bbi_args` (e.g., in a bbi_init() call), they + # would not be picked up. + bbi_yaml_path <- get_bbi_yaml_path(mod1) + bbi_yaml <- yaml::read_yaml(bbi_yaml_path) + bbi_yaml$nmfe_options$maxlim <- 3 + yaml::write_yaml(bbi_yaml, bbi_yaml_path) + nmtran_specs <- nmtran_setup(mod1, .bbi_args = NULL) + expect_equal(unname(nmtran_specs$cmd_args), c("0", "0", "3")) + + # Revert back for any other tests + bbi_yaml$nmfe_options$maxlim <- 2 + yaml::write_yaml(bbi_yaml, bbi_yaml_path) + }) + + it("execute_nmtran", { + # Execute in subdirectory to avoid messing with other tests + nmtran_dir <- file.path(MODEL_DIR_BBI, "nmtran") + fs::dir_create(nmtran_dir) + on.exit(fs::dir_delete(nmtran_dir), add = TRUE) + + # Copy model file into new model dir + fs::file_copy(CTL_TEST_FILE, file.path(nmtran_dir, "2.ctl")) + mod2 <- new_model(file.path(nmtran_dir, "2")) + + # create new bbi.yaml + bbi_init( + nmtran_dir, + .nonmem_dir = NONMEM_DIR, + .nonmem_version = Sys.getenv("BBR_TESTS_NONMEM_VERSION", "nm74gf"), + .bbi_args = list(mpi_exec_path = get_mpiexec_path()) + ) + + nmtran_specs <- nmtran_setup(mod2) + + nmtran_results <- execute_nmtran( + nmtran_specs$nmtran_exe, mod_path = get_model_path(mod2), + dir = nmtran_dir + ) + + # Check attributes + expect_equal(nmtran_dir, nmtran_results$run_dir) + expect_equal(nmtran_results$status_val, 0) + expect_equal(nmtran_results$status, "Finished Running") + + # Test failure + data_path <- "test/this/path/data.csv" + modify_data_path_ctl(mod2, data_path) + + nmtran_results <- execute_nmtran( + nmtran_specs$nmtran_exe, mod_path = basename(get_model_path(mod2)), + dir = nmtran_dir + ) + + # Check attributes + expect_equal(nmtran_results$status_val, 4) + expect_equal(nmtran_results$status, "Failed. See errors.") + }) + + it("run_nmtran: integration", { + # create model + mod1 <- read_model(file.path(MODEL_DIR_BBI, "1")) + + # Run with no presort + nmtran_results <- run_nmtran( + mod1, .bbi_args = list(nm_version = "nm73gf"), clean = FALSE + ) + on.exit(fs::dir_delete(nmtran_results$run_dir)) + + # Check attributes + expect_equal(get_model_path(mod1), nmtran_results$absolute_model_path) + expect_equal(nmtran_results$nmtran_model, basename(get_model_path(mod1))) + expect_equal(nmtran_results$nonmem_version, "nm73gf") + expect_equal(nmtran_results$status_val, 0) + expect_equal(nmtran_results$status, "Finished Running") + expect_true(is.null(nmtran_results$nmtran_presort_exe)) + + + # Run with presort + nmtran_results <- run_nmtran( + mod1, .bbi_args = list(nm_version = "nm75"), clean = FALSE + ) + on.exit(fs::dir_delete(nmtran_results$run_dir)) + + # Check attributes + expect_equal(get_model_path(mod1), nmtran_results$absolute_model_path) + expect_equal(nmtran_results$nmtran_model, "1_presort.ctl") + expect_equal(nmtran_results$nonmem_version, "nm75") + expect_equal(nmtran_results$status_val, 0) + expect_equal(nmtran_results$status, "Finished Running") + expect_false(is.null(nmtran_results$nmtran_presort_exe)) + expect_true(fs::file_exists(nmtran_results$nmtran_presort_exe)) + }) + }) + }) diff --git a/tests/testthat/test-workflow-bbi.R b/tests/testthat/test-workflow-bbi.R index e4b5b7f2d..bc5aceb1d 100644 --- a/tests/testthat/test-workflow-bbi.R +++ b/tests/testthat/test-workflow-bbi.R @@ -306,159 +306,5 @@ withr::with_options(list( expect_equal(dim(run_times), c(3, 3)) }) - describe("run_nmtran", { - it("nmtran_setup: executable and nonmem version", { - mod1 <- read_model(file.path(MODEL_DIR_BBI, "1")) - # Using model object, looks for bbi.yaml - nmtran_specs <- nmtran_setup(mod1) - # Confirm executable - expect_equal(nmtran_specs$nmtran_exe, "/opt/NONMEM/nm74gf/tr/NMTRAN.exe") - # Confirm NONMEM version - expect_equal(nmtran_specs$nonmem_version, "nm74gf") - - # Passed nonmem version - nmtran_specs <- nmtran_setup(mod1, .nonmem_version = "nm75") - # Confirm executable - expect_equal(nmtran_specs$nmtran_exe, "/opt/NONMEM/nm75/tr/NMTRAN.exe") - # Confirm NONMEM version - expect_equal(nmtran_specs$nonmem_version, "nm75") - - # Passed config_path - nmtran_specs <- nmtran_setup(.config_path = file.path(MODEL_DIR_BBI, "bbi.yaml")) - # Confirm executable - expect_equal(nmtran_specs$nmtran_exe, "/opt/NONMEM/nm74gf/tr/NMTRAN.exe") - # Confirm NONMEM version - expect_equal(nmtran_specs$nonmem_version, "nm74gf") - - # Incorrect nonmem version - expect_error( - nmtran_setup(mod1, .nonmem_version = "nm74"), - "Must specify a valid `.nonmem_version`" - ) - - # no configuration file found - expect_error( - nmtran_setup(mod1, .config_path = file.path(tempdir(), "bbi.yaml")), - "No bbi configuration was found at" - ) - }) - - it("nmtran_setup: NM-TRAN args", { - mod1 <- read_model(file.path(MODEL_DIR_BBI, "1")) - - # Default nmfe_options passed as .bbi_args - nmtran_specs <- nmtran_setup(mod1) - expect_equal(nmtran_specs$cmd_args, c("0", "0", "2")) - - # Override with .bbi_args - nmtran_specs <- nmtran_setup(mod1, .bbi_args = list(maxlim = 3, prdefault = TRUE)) - expect_equal(nmtran_specs$cmd_args, c("1", "0", "3")) - - # Override with model yaml - current_args <- mod1$bbi_args - mod1 <- add_bbi_args(mod1, list(maxlim = 3, tprdefault = TRUE)) - nmtran_specs <- nmtran_setup(mod1, .bbi_args = NULL) - expect_equal(nmtran_specs$cmd_args, c("0", "1", "3")) - mod1 <- replace_all_bbi_args(mod1, current_args) - - # Override with bbi.yaml - # - note: we only look at the `nmfe_options` in bbi.yaml. If these options - # were passed as regular `.bbi_args` (e.g., in a bbi_init() call), they - # would not be picked up. - bbi_yaml_path <- get_bbi_yaml_path(mod1) - bbi_yaml <- yaml::read_yaml(bbi_yaml_path) - bbi_yaml$nmfe_options$maxlim <- 3 - yaml::write_yaml(bbi_yaml, bbi_yaml_path) - nmtran_specs <- nmtran_setup(mod1, .bbi_args = NULL) - expect_equal(nmtran_specs$cmd_args, c("0", "0", "3")) - - # Revert back for any other tests - bbi_yaml$nmfe_options$maxlim <- 2 - yaml::write_yaml(bbi_yaml, bbi_yaml_path) - }) - - it("execute_nmtran", { - # Execute in subdirectory to avoid messing with other tests - nmtran_dir <- file.path(MODEL_DIR_BBI, "nmtran") - fs::dir_create(nmtran_dir) - on.exit(fs::dir_delete(nmtran_dir), add = TRUE) - - # Copy model file into new model dir - fs::file_copy(CTL_TEST_FILE, nmtran_dir) - mod1 <- new_model(file.path(nmtran_dir, "1")) - - # create new bbi.yaml - bbi_init( - nmtran_dir, - .nonmem_dir = Sys.getenv("BBR_TESTS_NONMEM_DIR", "/opt/NONMEM"), - .nonmem_version = Sys.getenv("BBR_TESTS_NONMEM_VERSION", "nm74gf"), - .bbi_args = list(mpi_exec_path = get_mpiexec_path()) - ) - - nmtran_specs <- nmtran_setup(mod1) - - nmtran_results <- execute_nmtran( - nmtran_specs$nmtran_exe, mod_path = basename(get_model_path(mod1)), - dir = nmtran_dir - ) - - # Check attributes - expect_equal(nmtran_dir, nmtran_results$run_dir) - expect_equal(nmtran_results$status_val, 0) - expect_equal(nmtran_results$status, "NM-TRAN successful") - - # Test failure - data_path <- "test/this/path/data.csv" - modify_data_path_ctl(mod1, data_path) - - nmtran_results <- execute_nmtran( - nmtran_specs$nmtran_exe, mod_path = basename(get_model_path(mod1)), - dir = nmtran_dir - ) - - # Check attributes - expect_equal(nmtran_results$status_val, 4) - expect_equal(nmtran_results$status, "NM-TRAN failed. See errors.") - }) - - it("run_nmtran: integration", { - # create model - mod1 <- read_model(file.path(MODEL_DIR_BBI, "1")) - - # Run with no presort - nmtran_results <- run_nmtran(mod1, .nonmem_version = "nm73gf", clean = FALSE) - on.exit(fs::dir_delete(nmtran_results$run_dir)) - - # Check attributes - expect_equal(get_model_path(mod1), nmtran_results$absolute_model_path) - expect_equal( - file.path(nmtran_results$run_dir, basename(get_model_path(mod1))), - nmtran_results$nmtran_model - ) - expect_equal(nmtran_results$nonmem_version, "nm73gf") - expect_equal(nmtran_results$status_val, 0) - expect_equal(nmtran_results$status, "NM-TRAN successful") - expect_true(is.null(nmtran_results$nmtran_presort_exe)) - - - # Run with presort - nmtran_results <- run_nmtran(mod1, .nonmem_version = "nm75", clean = FALSE) - on.exit(fs::dir_delete(nmtran_results$run_dir)) - - # Check attributes - expect_equal(get_model_path(mod1), nmtran_results$absolute_model_path) - expect_equal( - file.path(nmtran_results$run_dir, "tempzzzz1.ctl"), - nmtran_results$nmtran_model - ) - expect_equal(nmtran_results$nonmem_version, "nm75") - expect_equal(nmtran_results$status_val, 0) - expect_equal(nmtran_results$status, "NM-TRAN successful") - expect_false(is.null(nmtran_results$nmtran_presort_exe)) - expect_true(fs::file_exists(nmtran_results$nmtran_presort_exe)) - }) - - }) - }) # closing withr::with_options From 1bfc3f4003cb618a225ea4e8d0dbf4aea2cbde99 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Tue, 30 Jul 2024 16:52:30 -0400 Subject: [PATCH 24/31] add handling for NULL nonmem versions in bbi.yaml - update error messages to use cli::cli_abort with better formatting --- R/run-nmtran.R | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/R/run-nmtran.R b/R/run-nmtran.R index 03757be54..d26c66593 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -72,9 +72,7 @@ run_nmtran <- function( # overwrite $DATA record of new model modify_data_path_ctl(nmtran_mod, data_path = basename(new_data_path)) }else{ - rlang::abort( - glue("Could not find data at `{data_path}`") - ) + cli::cli_abort("Could not find data at `{.file {data_path}}`") } # Run NM-TRAN @@ -131,14 +129,23 @@ nmtran_setup <- function( # Check nonmem version nm_config <- bbi_yaml$nonmem + if(is.null(nm_config)){ + cli::cli_abort( + c( + "Did not find required NONMEM information in configuration file: {.path {bbi_yaml_path}}", + "i" = "Please see {.fun bbr::bbi_init} for additional details" + ) + ) + } if (!is.null(.nonmem_version)) { # check for valid version if (!(.nonmem_version %in% names(nm_config))) { - rlang::abort( + cli::cli_abort( c( - "Must specify a valid `.nonmem_version` for run_nmtran.", - "i" = glue("{bbi_yaml_path} contains the following options:"), - glue("`{paste(names(nm_config), collapse='`, `')}`") + "Must specify a valid {.var nm_version} for {.fun bbr::run_nmtran}.", + "i" = "{.path {bbi_yaml_path}} contains the following options:", + "{names(nm_config)}", + "*" = "e.g., {.code run_nmtran(mod, .bbi_args = list(nm_version = '{names(nm_config)[1]}'))}" ) ) } @@ -151,9 +158,9 @@ nmtran_setup <- function( if(length(default_nm) > 1){ nm_vers <- paste(names(default_nm), collapse = ", ") - rlang::abort( + cli::cli_abort( c( - glue("Found multiple default NONMEM versions ({nm_vers}) at `{config_path}`"), + "Found multiple default NONMEM versions ({nm_vers}) at {.path {bbi_yaml_path}}", "i" = "Please ensure only one version is set to the default" ) ) @@ -242,7 +249,7 @@ execute_nmtran <- function( # Assign status status_val <- nmtran.p$status if(is.na(status_val)){ - rlang::abort("NM-TRAN terminated unexpectedly") + cli::cli_abort("NM-TRAN terminated unexpectedly") }else if(status_val == 0){ status <- "Finished Running" }else{ @@ -275,14 +282,15 @@ get_bbi_yaml_path <- function(.mod = NULL, .config_path = NULL){ if(!file_exists(bbi_yaml_path)){ if(is.null(.config_path)){ - msg <- c( - "No bbi configuration was found in the execution directory.", - "i" = "Please run `bbi_init()` with the appropriate directory to continue." + cli::cli_abort( + c( + "No bbi configuration was found in the execution directory.", + "i" = "Please run {.fun bbr::bbi_init} with the appropriate directory to continue." + ) ) }else{ - msg <- glue("No bbi configuration was found at {.config_path}") + cli::cli_abort("No bbi configuration was found at {.path {(.config_path)}}") } - rlang::abort(msg) } return(bbi_yaml_path) From 6f1856089acb757dec82c5230f7d01d01deb8f6e Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Wed, 31 Jul 2024 11:01:10 -0400 Subject: [PATCH 25/31] update parse_nmtran_args to mimic submit_model handling of .bbi_args - add documentation for added clarity --- R/run-nmtran.R | 41 ++++++++++++++++++++++++++++++-- man/parse_nmtran_args.Rd | 51 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 man/parse_nmtran_args.Rd diff --git a/R/run-nmtran.R b/R/run-nmtran.R index d26c66593..31d3620ce 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -301,7 +301,30 @@ get_bbi_yaml_path <- function(.mod = NULL, .config_path = NULL){ #' in the correct format #' @inheritParams run_nmtran #' @param nmfe_options named list of nmfe options defined in a `bbi.yaml` file. -#' @noRd +#' +#' @details +#' +#' Combines NONMEM submission args and consolidates to `NMFE` arguments only +#' - The arguments of interest are `prdefault`, `tprdefault`, and `maxlim`, +#' which impact the evaluation of `NM-TRAN` +#' - Priority: `.bbi_args` > model yaml > `bbi.yaml` +#' +#' @examples +#' +#' \dontrun{ +#' +#' bbi_yaml_path <- get_bbi_yaml_path(.mod) +#' bbi_yaml <- yaml::read_yaml(bbi_yaml_path) +#' +#' parse_nmtran_args( +#' .mod, +#' .bbi_args = list(nm_version = "nm74gf", prdefault = TRUE), +#' nmfe_options = bbi_yaml$nmfe_options +#' ) +#' +#' } +#' @keywords internal +#' @return a named character vector parse_nmtran_args <- function( .mod, .bbi_args = NULL, @@ -314,13 +337,27 @@ parse_nmtran_args <- function( # to NM-TRAN nmfe_args_def <- list(prdefault = FALSE, tprdefault = FALSE, maxlim = 2) + # Mimic submit_model behavior: a FALSE value for .bbi_arg has no effect + # - Filter these out from .bbi_args + if(!is.null(.bbi_args)){ + check_bbi_args(.bbi_args) + req_nmtran_args <- paste(names(nmfe_args_def), collapse = "|") + .bbi_args <- purrr::imap(.bbi_args, function(val, bbi_arg){ + if(str_detect(bbi_arg, req_nmtran_args) && isFALSE(val)){ + return(NULL) + }else{ + return(val) + } + }) %>% purrr::compact() + } + # Combine with any options stored in model yaml, preferring .bbi_args .nmfe_args <- parse_args_list(.bbi_args, .mod[[YAML_BBI_ARGS]]) # Combine with nmfe options stored in bbi.yaml, preferring .nmfe_args .nmfe_args <- parse_args_list(.nmfe_args, nmfe_options) - # Check provided args + # Check all provided args check_bbi_args(.nmfe_args) # Combine with and filter to default nmfe_options diff --git a/man/parse_nmtran_args.Rd b/man/parse_nmtran_args.Rd new file mode 100644 index 000000000..c29ba4ff0 --- /dev/null +++ b/man/parse_nmtran_args.Rd @@ -0,0 +1,51 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/run-nmtran.R +\name{parse_nmtran_args} +\alias{parse_nmtran_args} +\title{Parse \code{.bbi_args} and return the three expected \code{nmfe_options} for \code{NM-TRAN} +in the correct format} +\usage{ +parse_nmtran_args(.mod, .bbi_args = NULL, nmfe_options = NULL) +} +\arguments{ +\item{.mod}{A \code{bbi_nonmem_model} object.} + +\item{.bbi_args}{A named list specifying arguments to pass to \code{NM-TRAN}. +Similar to the \code{.bbi_args} argument defined in \code{\link[=submit_model]{submit_model()}}, though here +only \code{prdefault}, \code{tprdefault}, and \code{maxlim} arguments are passed to \code{NM-TRAN}. +\code{nm_version} is also supported and specifies which \code{NM-TRAN} executable to use. +See \code{\link[=print_bbi_args]{print_bbi_args()}} for more details.} + +\item{nmfe_options}{named list of nmfe options defined in a \code{bbi.yaml} file.} +} +\value{ +a named character vector +} +\description{ +Parse \code{.bbi_args} and return the three expected \code{nmfe_options} for \code{NM-TRAN} +in the correct format +} +\details{ +Combines NONMEM submission args and consolidates to \code{NMFE} arguments only +\itemize{ +\item The arguments of interest are \code{prdefault}, \code{tprdefault}, and \code{maxlim}, +which impact the evaluation of \code{NM-TRAN} +\item Priority: \code{.bbi_args} > model yaml > \code{bbi.yaml} +} +} +\examples{ + +\dontrun{ + + bbi_yaml_path <- get_bbi_yaml_path(.mod) + bbi_yaml <- yaml::read_yaml(bbi_yaml_path) + + parse_nmtran_args( + .mod, + .bbi_args = list(nm_version = "nm74gf", prdefault = TRUE), + nmfe_options = bbi_yaml$nmfe_options + ) + +} +} +\keyword{internal} From 6333a00c854df803fa548d9327a43a1a99f03e2a Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Wed, 31 Jul 2024 11:02:28 -0400 Subject: [PATCH 26/31] test fix: adjust error message --- tests/testthat/test-run-nmtran.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-run-nmtran.R b/tests/testthat/test-run-nmtran.R index ced81626c..a6c47a5e1 100644 --- a/tests/testthat/test-run-nmtran.R +++ b/tests/testthat/test-run-nmtran.R @@ -84,7 +84,7 @@ withr::with_options(list( # Incorrect nonmem version expect_error( nmtran_setup(mod1, .bbi_args = list(nm_version = "nm74")), - "Must specify a valid `.nonmem_version`" + "Must specify a valid `nm_version`" ) # no configuration file found From ebe3f13f41eb1d2ebbabf97473b9ad73b007a700 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Wed, 31 Jul 2024 15:11:26 -0400 Subject: [PATCH 27/31] add additional test for parse_nmtran_args changes --- tests/testthat/test-run-nmtran.R | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/testthat/test-run-nmtran.R b/tests/testthat/test-run-nmtran.R index a6c47a5e1..950f3785e 100644 --- a/tests/testthat/test-run-nmtran.R +++ b/tests/testthat/test-run-nmtran.R @@ -119,12 +119,19 @@ withr::with_options(list( bbi_yaml_path <- get_bbi_yaml_path(mod1) bbi_yaml <- yaml::read_yaml(bbi_yaml_path) bbi_yaml$nmfe_options$maxlim <- 3 + bbi_yaml$nmfe_options$prdefault <- TRUE yaml::write_yaml(bbi_yaml, bbi_yaml_path) nmtran_specs <- nmtran_setup(mod1, .bbi_args = NULL) - expect_equal(unname(nmtran_specs$cmd_args), c("0", "0", "3")) + expect_equal(unname(nmtran_specs$cmd_args), c("1", "0", "3")) + + # mimic submit_model: a FALSE value for .bbi_arg has no effect + # if a TRUE value is found in the bbi.yaml + nmtran_specs <- nmtran_setup(mod1, .bbi_args = list(prdefault = FALSE)) + expect_equal(unname(nmtran_specs$cmd_args), c("1", "0", "3")) # Revert back for any other tests bbi_yaml$nmfe_options$maxlim <- 2 + bbi_yaml$nmfe_options$prdefault <- NULL yaml::write_yaml(bbi_yaml, bbi_yaml_path) }) From 123461275e61bbe83374a75afc3b0e90b9ec2ed1 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Thu, 1 Aug 2024 12:36:10 -0400 Subject: [PATCH 28/31] address minor feedback and add dedicated parse_nmtran_args tests - add parse_nmtran_args tests instead of documentation to help clarify expected results --- R/run-nmtran.R | 67 +++++++++++++------------------- man/execute_nmtran.Rd | 16 ++++---- man/parse_nmtran_args.Rd | 15 ------- tests/testthat/test-run-nmtran.R | 66 +++++++++++++++++++++++++------ 4 files changed, 89 insertions(+), 75 deletions(-) diff --git a/R/run-nmtran.R b/R/run-nmtran.R index 31d3620ce..45ece4421 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -193,16 +193,16 @@ nmtran_setup <- function( #' Execute `NM-TRAN` in a given directory #' #' Execute `NM-TRAN` in a given directory. Also runs `nmtran_presort` if an -#' executable is found. +#' executable is found. This assumes the model at `mod_path` has already been +#' copied to the top-level of `dir`. #' #' @param nmtran_exe Path to `NM-TRAN` executable. -#' @param mod_path Path of a model to evaluate. Should be relative to `dir`. Nested -#' directories not supported. -#' @param cmd_args A character vector of command line arguments for the `NM-TRAN` -#' execution call -#' @param nmtran_presort_exe Path to `nmtran_presort` executable. Only available -#' for `NONMEM` versions `nm74gf`, `nm74gf_nmfe`, and `nm75`. If provided, will run -#' `nmtran_presort` before running `NM-TRAN`. Set to `NULL` to skip this step. +#' @param mod_path Path of a model to evaluate. Should be relative to `dir`. +#' @param cmd_args A character vector of command line arguments for the +#' `NM-TRAN` execution call +#' @param nmtran_presort_exe Path to `nmtran_presort` executable. If provided, +#' will run `nmtran_presort` ahead of `NM-TRAN` for `NONMEM 7.4` and later. +#' Set to `NULL` to skip this step. #' @param dir Directory in which to execute the command. #' #' @keywords internal @@ -219,6 +219,11 @@ execute_nmtran <- function( run_dir <- as.character(fs::path_real(dir)) nmtran_input <- mod_path_new <- file.path(run_dir, basename(mod_path)) + # This should only be possible via manual intervention + if(!fs::file_exists(mod_path_new)){ + cli::cli_abort("Could not find model at {.path {mod_path_new}}") + } + # Preprocess with nmtran_presort if(!is.null(nmtran_presort_exe)){ # NM-TRAN input is now output from nmtran_presort @@ -226,7 +231,7 @@ execute_nmtran <- function( paste0(fs::path_ext_remove(mod_path_new), "_presort.", fs::path_ext(mod_path_new)) ) - presort.p <- processx::run( + processx::run( command = nmtran_presort_exe, stdin = mod_path_new, stdout = nmtran_input, @@ -309,20 +314,6 @@ get_bbi_yaml_path <- function(.mod = NULL, .config_path = NULL){ #' which impact the evaluation of `NM-TRAN` #' - Priority: `.bbi_args` > model yaml > `bbi.yaml` #' -#' @examples -#' -#' \dontrun{ -#' -#' bbi_yaml_path <- get_bbi_yaml_path(.mod) -#' bbi_yaml <- yaml::read_yaml(bbi_yaml_path) -#' -#' parse_nmtran_args( -#' .mod, -#' .bbi_args = list(nm_version = "nm74gf", prdefault = TRUE), -#' nmfe_options = bbi_yaml$nmfe_options -#' ) -#' -#' } #' @keywords internal #' @return a named character vector parse_nmtran_args <- function( @@ -330,31 +321,25 @@ parse_nmtran_args <- function( .bbi_args = NULL, nmfe_options = NULL ){ + check_bbi_args(.bbi_args) - # These are the default NMFE options that are passed to `NM-TRAN` - # - They should always be passed (in the correct order), so set the - # defaults here and merge with other bbi_args to ensure these are passed - # to NM-TRAN + # These are the default NMFE options that are passed to NM-TRAN + # - They should always be passed (in the correct order), so set the defaults + # here and merge with other .bbi_args to ensure these are passed to NM-TRAN nmfe_args_def <- list(prdefault = FALSE, tprdefault = FALSE, maxlim = 2) + # Combine with any options stored in model yaml, preferring .bbi_args + .nmfe_args <- parse_args_list(.bbi_args, .mod[[YAML_BBI_ARGS]]) + # Mimic submit_model behavior: a FALSE value for .bbi_arg has no effect - # - Filter these out from .bbi_args - if(!is.null(.bbi_args)){ - check_bbi_args(.bbi_args) - req_nmtran_args <- paste(names(nmfe_args_def), collapse = "|") - .bbi_args <- purrr::imap(.bbi_args, function(val, bbi_arg){ - if(str_detect(bbi_arg, req_nmtran_args) && isFALSE(val)){ - return(NULL) - }else{ - return(val) - } + # - Filter these out from .nmfe_args + if(!is.null(.nmfe_args)){ + .nmfe_args <- purrr::imap(.nmfe_args, function(val, nmfe_arg){ + if(isFALSE(val)) return(NULL) else return(val) }) %>% purrr::compact() } - # Combine with any options stored in model yaml, preferring .bbi_args - .nmfe_args <- parse_args_list(.bbi_args, .mod[[YAML_BBI_ARGS]]) - - # Combine with nmfe options stored in bbi.yaml, preferring .nmfe_args + # Combine with nmfe options stored in bbi.yaml, preferring .bbi_args .nmfe_args <- parse_args_list(.nmfe_args, nmfe_options) # Check all provided args diff --git a/man/execute_nmtran.Rd b/man/execute_nmtran.Rd index 63d7a382a..882b54d30 100644 --- a/man/execute_nmtran.Rd +++ b/man/execute_nmtran.Rd @@ -15,20 +15,20 @@ execute_nmtran( \arguments{ \item{nmtran_exe}{Path to \code{NM-TRAN} executable.} -\item{mod_path}{Path of a model to evaluate. Should be relative to \code{dir}. Nested -directories not supported.} +\item{mod_path}{Path of a model to evaluate. Should be relative to \code{dir}.} -\item{cmd_args}{A character vector of command line arguments for the \code{NM-TRAN} -execution call} +\item{cmd_args}{A character vector of command line arguments for the +\code{NM-TRAN} execution call} -\item{nmtran_presort_exe}{Path to \code{nmtran_presort} executable. Only available -for \code{NONMEM} versions \code{nm74gf}, \code{nm74gf_nmfe}, and \code{nm75}. If provided, will run -\code{nmtran_presort} before running \code{NM-TRAN}. Set to \code{NULL} to skip this step.} +\item{nmtran_presort_exe}{Path to \code{nmtran_presort} executable. If provided, +will run \code{nmtran_presort} ahead of \code{NM-TRAN} for \verb{NONMEM 7.4} and later. +Set to \code{NULL} to skip this step.} \item{dir}{Directory in which to execute the command.} } \description{ Execute \code{NM-TRAN} in a given directory. Also runs \code{nmtran_presort} if an -executable is found. +executable is found. This assumes the model at \code{mod_path} has already been +copied to the top-level of \code{dir}. } \keyword{internal} diff --git a/man/parse_nmtran_args.Rd b/man/parse_nmtran_args.Rd index c29ba4ff0..6e991cc39 100644 --- a/man/parse_nmtran_args.Rd +++ b/man/parse_nmtran_args.Rd @@ -31,21 +31,6 @@ Combines NONMEM submission args and consolidates to \code{NMFE} arguments only \item The arguments of interest are \code{prdefault}, \code{tprdefault}, and \code{maxlim}, which impact the evaluation of \code{NM-TRAN} \item Priority: \code{.bbi_args} > model yaml > \code{bbi.yaml} -} -} -\examples{ - -\dontrun{ - - bbi_yaml_path <- get_bbi_yaml_path(.mod) - bbi_yaml <- yaml::read_yaml(bbi_yaml_path) - - parse_nmtran_args( - .mod, - .bbi_args = list(nm_version = "nm74gf", prdefault = TRUE), - nmfe_options = bbi_yaml$nmfe_options - ) - } } \keyword{internal} diff --git a/tests/testthat/test-run-nmtran.R b/tests/testthat/test-run-nmtran.R index 950f3785e..c1b55c475 100644 --- a/tests/testthat/test-run-nmtran.R +++ b/tests/testthat/test-run-nmtran.R @@ -1,22 +1,19 @@ -context("testing NM-TRAN") # define constants MODEL_DIR_BBI <- file.path(dirname(ABS_MODEL_DIR), "test-nmtran-models") NONMEM_DIR <- Sys.getenv("BBR_TESTS_NONMEM_DIR", "/opt/NONMEM") -REQ_NONMEM_VERSIONS <- c("nm73gf", "nm74gf", "nm75", Sys.getenv("BBR_TESTS_NONMEM_VERSION")) -REQ_NONMEM_VERSIONS <- unique(REQ_NONMEM_VERSIONS[REQ_NONMEM_VERSIONS != ""]) +REQ_NONMEM_VERSIONS <- c("nm73gf", "nm74gf", "nm75") # Don't assume NONMEM is available if not on Metworx. if (Sys.getenv("METWORX_VERSION") == "" || Sys.getenv("SKIP_BBI_TEST") == "true") { skip("test-workflow-bbi only runs on Metworx because it needs NONMEM installed") } -skip_long_tests("skipping long-running bbi workflow tests") # Skip if required NONMEM versions are not installed if(!all(REQ_NONMEM_VERSIONS %in% basename(fs::dir_ls(NONMEM_DIR)))){ req_nm_ver_txt <- paste(REQ_NONMEM_VERSIONS, collapse = ", ") - skip(glue("test-run-nmtran-bbi only requires the following NONMEM versions {req_nm_ver_txt}")) + skip(glue("test-run-nmtran-bbi requires the following NONMEM versions: {req_nm_ver_txt}")) } @@ -34,10 +31,7 @@ withr::with_options(list( bbr.verbose = FALSE), { # cleanup when done - on.exit({ - Sys.sleep(3) # wait for some NONMEM mess to delete itself - cleanup_bbi() - }) + on.exit({cleanup_bbi()}) # clear old bbi.yaml if (fs::file_exists(file.path(MODEL_DIR_BBI, "bbi.yaml"))) fs::file_delete(file.path(MODEL_DIR_BBI, "bbi.yaml")) @@ -46,7 +40,7 @@ withr::with_options(list( bbi_init( MODEL_DIR_BBI, .nonmem_dir = NONMEM_DIR, - .nonmem_version = Sys.getenv("BBR_TESTS_NONMEM_VERSION", "nm74gf"), + .nonmem_version = "nm74gf", .bbi_args = list(mpi_exec_path = get_mpiexec_path()) ) @@ -135,6 +129,56 @@ withr::with_options(list( yaml::write_yaml(bbi_yaml, bbi_yaml_path) }) + it("parse_nmtran_args: parses and formats nmfe options correctly", { + mod1 <- read_model(file.path(MODEL_DIR_BBI, "1")) + bbi_yaml_path <- get_bbi_yaml_path(mod1) + bbi_yaml <- yaml::read_yaml(bbi_yaml_path) + + # Only model yaml + cmd_args <- parse_nmtran_args(mod1, nmfe_options = bbi_yaml$nmfe_options) + expect_equal(unname(cmd_args), c("0", "0", "2")) + + # Override with model yaml + current_args <- mod1$bbi_args + mod1 <- add_bbi_args(mod1, list(tprdefault = TRUE, maxlim = 3)) + + # Test priority of .bbi_args > model yaml > bbi.yaml + # - TRUE values overwrite FALSE values if both are present + + # Overwrite .bbi_args --> takes precedence + cmd_args <- mod1 %>% parse_nmtran_args( + .bbi_args = list(prdefault = TRUE, tprdefault = FALSE, maxlim = 1), + nmfe_options = bbi_yaml$nmfe_options + ) + + expect_equal(unname(cmd_args), c("1", "0", "1")) + + # Overwrite bbi.yaml --> model yaml defaults take precedence + cmd_args <- mod1 %>% parse_nmtran_args( + nmfe_options = list(prdefault = TRUE, tprdefault = FALSE, maxlim = 1) + ) + + expect_equal(unname(cmd_args), c("1", "1", "3")) + + # Check with all three sources + cmd_args <- mod1 %>% parse_nmtran_args( + .bbi_args = list(tprdefault = FALSE, maxlim = 2), + nmfe_options = list(tprdefault = FALSE, maxlim = 1) + ) + + expect_equal(unname(cmd_args), c("0", "0", "2")) + + cmd_args <- mod1 %>% parse_nmtran_args( + .bbi_args = list(prdefault = TRUE, tprdefault = FALSE, maxlim = 2), + nmfe_options = list(tprdefault = TRUE, maxlim = 1) + ) + + expect_equal(unname(cmd_args), c("1", "1", "2")) + + # Reset model yaml .bbi_args for other tests + mod1 <- replace_all_bbi_args(mod1, current_args) + }) + it("execute_nmtran", { # Execute in subdirectory to avoid messing with other tests nmtran_dir <- file.path(MODEL_DIR_BBI, "nmtran") @@ -149,7 +193,7 @@ withr::with_options(list( bbi_init( nmtran_dir, .nonmem_dir = NONMEM_DIR, - .nonmem_version = Sys.getenv("BBR_TESTS_NONMEM_VERSION", "nm74gf"), + .nonmem_version = "nm74gf", .bbi_args = list(mpi_exec_path = get_mpiexec_path()) ) From dd8bc6efe3af734961bca80c76df785660ad3ad8 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Fri, 2 Aug 2024 12:06:20 -0400 Subject: [PATCH 29/31] documentation and test adjustments from feedback - add details about additional `do2test` argument for NONMEM 7.5 and later - adjust test to not look for a specific status value on failure. Any non-zero value is a failure, so we dont need to check the specific value --- R/run-nmtran.R | 18 +++++++++++++++--- man/parse_nmtran_args.Rd | 18 ++++++++++++++++-- tests/testthat/test-run-nmtran.R | 12 +++++------- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/R/run-nmtran.R b/R/run-nmtran.R index 45ece4421..f874d3d98 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -65,7 +65,7 @@ run_nmtran <- function( nmtran_mod <- new_model(file.path(temp_folder, basename(mod_path))) # Copy dataset & overwrite $DATA record of new model - # NM-TRAN will error if data cannot be found + # - New data path is used to avoid any special characters that require quoting if(fs::file_exists(data_path)){ new_data_path <- file.path(temp_folder, "data.csv") fs::file_copy(data_path, new_data_path) @@ -311,8 +311,20 @@ get_bbi_yaml_path <- function(.mod = NULL, .config_path = NULL){ #' #' Combines NONMEM submission args and consolidates to `NMFE` arguments only #' - The arguments of interest are `prdefault`, `tprdefault`, and `maxlim`, -#' which impact the evaluation of `NM-TRAN` -#' - Priority: `.bbi_args` > model yaml > `bbi.yaml` +#' which impact the evaluation of `NM-TRAN`. Priority is `.bbi_args` > model +#' yaml > `bbi.yaml` +#' - Note: `run_nmtran()` considers only the first three `NM-TRAN` arguments +#' (`prdefault`, `tprdefault`, and `maxlim`), but, starting with `NONMEM 7.5`, +#' nmfe passes a fourth argument (`do2test`) to NM-TRAN. +#' - `bbi` _doesn't_ expose nmfe's `-do2test` argument, so in the `bbi`/`bbr` +#' context this call always passes `"0"` to `NM-TRAN` for the `do2test` +#' value. If that first call exits with a status of `9`, nmfe does a follow-up +#' call that drops the fourth argument (`do2test`). +#' - Given the above, `parse_nmtran_args()` does not support the additional +#' `do2test` argument for `NONMEM 7.5`. This should be revisited if +#' `run_nmtran()` is called within `submit_model()` as a method of validating +#' the control stream ahead of model execution. See additional discussion at +#' https://github.com/metrumresearchgroup/bbr/pull/705. #' #' @keywords internal #' @return a named character vector diff --git a/man/parse_nmtran_args.Rd b/man/parse_nmtran_args.Rd index 6e991cc39..8b34cdb3f 100644 --- a/man/parse_nmtran_args.Rd +++ b/man/parse_nmtran_args.Rd @@ -29,8 +29,22 @@ in the correct format Combines NONMEM submission args and consolidates to \code{NMFE} arguments only \itemize{ \item The arguments of interest are \code{prdefault}, \code{tprdefault}, and \code{maxlim}, -which impact the evaluation of \code{NM-TRAN} -\item Priority: \code{.bbi_args} > model yaml > \code{bbi.yaml} +which impact the evaluation of \code{NM-TRAN}. Priority is \code{.bbi_args} > model +yaml > \code{bbi.yaml} +\item Note: \code{run_nmtran()} considers only the first three \code{NM-TRAN} arguments +(\code{prdefault}, \code{tprdefault}, and \code{maxlim}), but, starting with \verb{NONMEM 7.5}, +nmfe passes a fourth argument (\code{do2test}) to NM-TRAN. +\itemize{ +\item \code{bbi} \emph{doesn't} expose nmfe's \code{-do2test} argument, so in the \code{bbi}/\code{bbr} +context this call always passes \code{"0"} to \code{NM-TRAN} for the \code{do2test} +value. If that first call exits with a status of \code{9}, nmfe does a follow-up +call that drops the fourth argument (\code{do2test}). +\item Given the above, \code{parse_nmtran_args()} does not support the additional +\code{do2test} argument for \verb{NONMEM 7.5}. This should be revisited if +\code{run_nmtran()} is called within \code{submit_model()} as a method of validating +the control stream ahead of model execution. See additional discussion at +https://github.com/metrumresearchgroup/bbr/pull/705. +} } } \keyword{internal} diff --git a/tests/testthat/test-run-nmtran.R b/tests/testthat/test-run-nmtran.R index c1b55c475..83bed7a24 100644 --- a/tests/testthat/test-run-nmtran.R +++ b/tests/testthat/test-run-nmtran.R @@ -6,14 +6,14 @@ REQ_NONMEM_VERSIONS <- c("nm73gf", "nm74gf", "nm75") # Don't assume NONMEM is available if not on Metworx. if (Sys.getenv("METWORX_VERSION") == "" || Sys.getenv("SKIP_BBI_TEST") == "true") { - skip("test-workflow-bbi only runs on Metworx because it needs NONMEM installed") + skip("test-run-nmtran only runs on Metworx because it needs NONMEM installed") } # Skip if required NONMEM versions are not installed if(!all(REQ_NONMEM_VERSIONS %in% basename(fs::dir_ls(NONMEM_DIR)))){ req_nm_ver_txt <- paste(REQ_NONMEM_VERSIONS, collapse = ", ") - skip(glue("test-run-nmtran-bbi requires the following NONMEM versions: {req_nm_ver_txt}")) + skip(glue("test-run-nmtran requires the following NONMEM versions: {req_nm_ver_txt}")) } @@ -40,8 +40,7 @@ withr::with_options(list( bbi_init( MODEL_DIR_BBI, .nonmem_dir = NONMEM_DIR, - .nonmem_version = "nm74gf", - .bbi_args = list(mpi_exec_path = get_mpiexec_path()) + .nonmem_version = "nm74gf" ) # copy model files into new model dir (not run) @@ -193,8 +192,7 @@ withr::with_options(list( bbi_init( nmtran_dir, .nonmem_dir = NONMEM_DIR, - .nonmem_version = "nm74gf", - .bbi_args = list(mpi_exec_path = get_mpiexec_path()) + .nonmem_version = "nm74gf" ) nmtran_specs <- nmtran_setup(mod2) @@ -219,7 +217,7 @@ withr::with_options(list( ) # Check attributes - expect_equal(nmtran_results$status_val, 4) + expect_true(nmtran_results$status_val > 0) expect_equal(nmtran_results$status, "Failed. See errors.") }) From 29995f7d222078283274c53c48cca66d2735a80c Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Fri, 2 Aug 2024 16:36:20 -0400 Subject: [PATCH 30/31] Add support for Windows - This is not an ideal solution, though it has worked in practice. On Windows the output ends up being saved to a `fort.6` file instead of the intended stdout location. stdout instead gets created with no contents. - This approach copies the `nmtran_presort` results from `fort.6` to stdout, and reads in the `NM-TRAN` results from `fort.6` instead of stdout --- R/run-nmtran.R | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/R/run-nmtran.R b/R/run-nmtran.R index f874d3d98..81bea9707 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -178,6 +178,7 @@ nmtran_setup <- function( # Check if nmtran_presort exists nmtran_presort_exe <- file.path(nm_path, "util", "nmtran_presort") + if(ON_WINDOWS) nmtran_presort_exe <- paste0(nmtran_presort_exe, ".exe") run_presort <- unname(fs::file_exists(nmtran_presort_exe)) return( @@ -238,6 +239,16 @@ execute_nmtran <- function( wd = run_dir, error_on_status = TRUE ) + + # On Windows, nmtran_presort outputs the results to a fort.6 file instead of + # the designated stdout location. + # - Overwrite stdout with contents of fort.6 if on windows + if(ON_WINDOWS){ + fort6_path <- file.path(run_dir, "fort.6") + if(file.exists(fort6_path)){ + fs::file_copy(fort6_path, nmtran_input, overwrite = TRUE) + } + } } # Run NM-TRAN @@ -261,13 +272,20 @@ execute_nmtran <- function( status <- "Failed. See errors." } + # Overwrite stdout with contents of fort.6 if on windows + if(ON_WINDOWS && file.exists(fort6_path)){ + output_lines <- readLines(fort6_path) + }else{ + output_lines <- nmtran.p$stdout + } + # Tabulate NM-TRAN results nmtran_results <- list( nmtran_model = basename(nmtran_input), run_dir = run_dir, status = status, status_val = status_val, - output = nmtran.p$stdout + output = output_lines ) return(nmtran_results) From 784c3c25a2365dee53081848ae0c8630e6c0e244 Mon Sep 17 00:00:00 2001 From: Kyle Barrett Date: Wed, 7 Aug 2024 12:39:02 -0400 Subject: [PATCH 31/31] tweak comment about NM-TRAN and nmtran_presort on Windows --- R/run-nmtran.R | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/R/run-nmtran.R b/R/run-nmtran.R index 81bea9707..2ce3e1627 100644 --- a/R/run-nmtran.R +++ b/R/run-nmtran.R @@ -240,8 +240,9 @@ execute_nmtran <- function( error_on_status = TRUE ) - # On Windows, nmtran_presort outputs the results to a fort.6 file instead of - # the designated stdout location. + # In our Window testing, nmtran_presort and NM-TRAN output write stdout to + # fort.6 instead of the locations specified in the processx::run() calls. + # See https://github.com/metrumresearchgroup/bbr/pull/705 for more discussion. # - Overwrite stdout with contents of fort.6 if on windows if(ON_WINDOWS){ fort6_path <- file.path(run_dir, "fort.6")