aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Ranke <jranke@uni-bremen.de>2020-11-27 18:35:56 +0100
committerJohannes Ranke <jranke@uni-bremen.de>2020-11-27 21:19:19 +0100
commit1e3fd1bef2a0ec1c8b73fcfefdd62fd3463bc87c (patch)
tree22fc54a9ebb62487887e779639c91c56e3d55db9
parent503441b0a958c1df50df0ee7cfc3bde4ea1b1865 (diff)
Improved way to have persistent DLLs for mkinmod
Depends on inline >= 0.16.2 (including the bug fixes from eddelbuettel/inline#18), which provides 'moveDLL' to store the DLL for a compiled function in a safe place in case the argument 'dll_dir' is specified in the call to 'mkinmod'. Huge thanks to Dirk @eddelbuettel for his review and support for the work on the inline package.
-rw-r--r--DESCRIPTION2
-rw-r--r--R/mkinfit.R4
-rw-r--r--R/mkinmod.R60
-rw-r--r--R/mkinpredict.R9
-rw-r--r--man/mkinmod.Rd27
-rw-r--r--test.log12
-rw-r--r--vignettes/mkin.html4
7 files changed, 67 insertions, 51 deletions
diff --git a/DESCRIPTION b/DESCRIPTION
index 0953d07e..f1e4f169 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -17,7 +17,7 @@ Description: Calculation routines based on the FOCUS Kinetics Report (2006,
note that no warranty is implied for correctness of results or fitness for a
particular purpose.
Depends: R (>= 2.15.1), parallel
-Imports: stats, graphics, methods, deSolve, R6, inline (>= 0.3.17), numDeriv,
+Imports: stats, graphics, methods, deSolve, R6, inline (>= 0.3.16.2), numDeriv,
lmtest, pkgbuild, nlme (>= 3.1-150.1), purrr, saemix (>= 3.1.9000)
Suggests: knitr, rbenchmark, tikzDevice, testthat, rmarkdown, covr, vdiffr,
benchmarkme, tibble, stats4
diff --git a/R/mkinfit.R b/R/mkinfit.R
index e482285d..a6efc858 100644
--- a/R/mkinfit.R
+++ b/R/mkinfit.R
@@ -479,10 +479,6 @@ mkinfit <- function(mkinmod, observed,
solution_type = "analytical"
} else {
if (!is.null(mkinmod$cf) & use_compiled[1] != FALSE) {
- try_dynlib <- try(inline::getDynLib(mkinmod$cf)[["path"]])
- if (inherits(try_dynlib, "try-error")) {
- mkinmod$cf <- inline::readDynLib(mkinmod$cf_name, mkinmod$cf_dir)
- }
solution_type = "deSolve"
} else {
if (is.matrix(mkinmod$coefmat)) {
diff --git a/R/mkinmod.R b/R/mkinmod.R
index 434282fd..a7353e81 100644
--- a/R/mkinmod.R
+++ b/R/mkinmod.R
@@ -11,6 +11,7 @@
#' of the system of differential equations is included in the resulting
#' mkinmod object in some cases, speeding up the solution.
#'
+#' If a C compiler is found by [pkgbuild::has_compiler()] and there
#' is more than one observed variable in the specification, C code is generated
#' for evaluating the differential equations, compiled using
#' [inline::cfunction()] and added to the resulting mkinmod object.
@@ -34,15 +35,21 @@
#' formation fractions are always used (default). If "min", a minimum use of
#' formation fractions is made, i.e. each pathway to a metabolite has its
#' own rate constant.
-#' @param name A name for the model. Should be a valid R object name.
#' @param speclist The specification of the observed variables and their
#' submodel types and pathways can be given as a single list using this
#' argument. Default is NULL.
#' @param quiet Should messages be suppressed?
#' @param verbose If \code{TRUE}, passed to [inline::cfunction()] if
#' applicable to give detailed information about the C function being built.
-#' @param cf_dir Directory where CFunc objects should be saved. Specifying
-#' 'cf_dir' without specifying a 'name' for the object is an error.
+#' @param name A name for the model. Should be a valid R object name.
+#' @param dll_dir Directory where an DLL object, if generated internally by
+#' [inline::cfunction()], should be saved. The DLL will only be stored in a
+#' permanent location for use in future sessions, if 'dll_dir' and 'name'
+#' are specified.
+#' @param unload If a DLL from the target location in 'dll_dir' is already
+#' loaded, should that be unloaded first?
+#' @param overwrite If a file exists at the target DLL location in 'dll_dir',
+#' should this be overwritten?
#' @importFrom methods signature
#' @return A list of class \code{mkinmod} for use with [mkinfit()],
#' containing, among others,
@@ -95,12 +102,20 @@
#' \dontrun{
#' fit_sfo_sfo <- mkinfit(SFO_SFO, FOCUS_2006_D, quiet = TRUE, solution_type = "deSolve")
#'
-#' # Now supplying full names used for plotting, and write to user defined location
+#' # Now supplying compound names used for plotting, and write to user defined location
+#' # We need to choose a path outside the session tempdir because this gets removed
+#' DLL_dir <- "~/.local/share/mkin"
+#' if (!dir.exists(DLL_dir)) dir.create(DLL_dir)
#' SFO_SFO.2 <- mkinmod(
#' parent = mkinsub("SFO", "m1", full_name = "Test compound"),
#' m1 = mkinsub("SFO", full_name = "Metabolite M1"),
-#' name = "SFOSFO", cf_dir = tempdir())
-#' fit_sfo_sfo <- mkinfit(SFO_SFO.2, FOCUS_2006_D, quiet = TRUE, solution_type = "deSolve")
+#' name = "SFO_SFO", dll_dir = DLL_dir, unload = TRUE, overwrite = TRUE)
+#' # Now we can save the model and restore it in a new session
+#' saveRDS(SFO_SFO.2, file = "~/SFO_SFO.rds")
+#' # Terminate the R session here if you would like to check, and then do
+#' library(mkin)
+#' SFO_SFO.3 <- readRDS("~/SFO_SFO.rds")
+#' fit_sfo_sfo <- mkinfit(SFO_SFO.3, FOCUS_2006_D, quiet = TRUE, solution_type = "deSolve")
#'
#' # Show details of creating the C function
#' SFO_SFO <- mkinmod(
@@ -126,15 +141,17 @@
#'
#' @export mkinmod
mkinmod <- function(..., use_of_ff = "max", name = NULL,
- speclist = NULL, quiet = FALSE, verbose = FALSE, cf_dir = NULL)
+ speclist = NULL, quiet = FALSE, verbose = FALSE, dll_dir = NULL,
+ unload = FALSE, overwrite = FALSE)
{
if (is.null(speclist)) spec <- list(...)
else spec <- speclist
obs_vars <- names(spec)
- if (!is.null(cf_dir)) {
- if (!dir.exists(cf_dir)) stop(cf_dir, " does not exist")
- if (is.null(name)) stop("You must give a name if you want to use 'cf_dir'")
+ save_msg <- "You need to specify both 'name' and 'dll_dir' to save a model DLL"
+ if (!is.null(dll_dir)) {
+ if (!dir.exists(dll_dir)) stop(dll_dir, " does not exist")
+ if (is.null(name)) stop(save_msg)
}
# Check if any of the names of the observed variables contains any other
@@ -310,7 +327,7 @@ mkinmod <- function(..., use_of_ff = "max", name = NULL,
} #}}}
} #}}}
- model <- list(diffs = diffs, parms = parms, map = map, spec = spec, use_of_ff = use_of_ff)
+ model <- list(diffs = diffs, parms = parms, map = map, spec = spec, use_of_ff = use_of_ff, name = name)
# Create coefficient matrix if possible #{{{
if (mat) {
@@ -438,26 +455,19 @@ mkinmod <- function(..., use_of_ff = "max", name = NULL,
"}\n\n")
# Try to build a shared library
- cf <- try(inline::cfunction(derivs_sig, derivs_code,
+ model$cf <- try(inline::cfunction(derivs_sig, derivs_code,
otherdefs = initpar_code,
- verbose = verbose,
+ verbose = verbose, name = "diffs",
convention = ".C", language = "C"),
silent = TRUE)
- if (!inherits(cf, "try-error")) {
- if (is.null(cf_dir)) {
- model$cf <- cf
+ if (!inherits(model$cf, "try-error")) {
+ if (is.null(dll_dir)) {
if (!quiet) message("Temporary DLL for differentials generated and loaded")
+ dll_info <- inline::getDynLib(model$cf)
} else {
- cf_file <- inline::writeDynLib(cf, name, cf_dir)
- model$cf <- inline::readDynLib(name, cf_dir)
- model$cf_name <- name
- model$cf_dir <- cf_dir
- fileDLL <- inline::getDynLib(model$cf)[["path"]]
- if (!quiet) {
- message("CFunc object written to ", cf_file)
- message("DLL written to ", fileDLL)
- }
+ dll_info <- inline::moveDLL(model$cf, name, dll_dir,
+ unload = unload, overwrite = overwrite, verbose = !quiet)
}
}
}
diff --git a/R/mkinpredict.R b/R/mkinpredict.R
index a294a114..277c3604 100644
--- a/R/mkinpredict.R
+++ b/R/mkinpredict.R
@@ -169,18 +169,13 @@ mkinpredict.mkinmod <- function(x,
if (solution_type == "deSolve") {
if (!is.null(x$cf) & use_compiled[1] != FALSE) {
- DLL <- try(inline::getDynLib(x$cf))
- if (inherits(DLL, "try-error")) {
- x$cf <- inline::readDynLib(x$cf_name, x$cf_dir)
- }
- cf_env <- environment(x$cf)
out <- deSolve::ode(
y = odeini,
times = outtimes,
- func = cf_env$name,
+ func = "diffs",
initfunc = "initpar",
- dllname = cf_env$f,
+ dllname = inline::getDynLib(x$cf)[["name"]],
parms = odeparms[x$parms], # Order matters when using compiled models
method = method.ode,
atol = atol,
diff --git a/man/mkinmod.Rd b/man/mkinmod.Rd
index f71ebfb3..77319aac 100644
--- a/man/mkinmod.Rd
+++ b/man/mkinmod.Rd
@@ -13,7 +13,9 @@ mkinmod(
speclist = NULL,
quiet = FALSE,
verbose = FALSE,
- cf_dir = NULL
+ dll_dir = NULL,
+ unload = FALSE,
+ overwrite = FALSE
)
\method{print}{mkinmod}(x, ...)
@@ -53,8 +55,16 @@ argument. Default is NULL.}
\item{verbose}{If \code{TRUE}, passed to \code{\link[inline:cfunction]{inline::cfunction()}} if
applicable to give detailed information about the C function being built.}
-\item{cf_dir}{Directory where CFunc objects should be saved. Specifying
-'cf_dir' without specifying a 'name' for the object is an error.}
+\item{dll_dir}{Directory where an DLL object, if generated internally by
+\code{\link[inline:cfunction]{inline::cfunction()}}, should be saved. The DLL will only be stored in a
+permanent location for use in future sessions, if 'dll_dir' and 'name'
+are specified.}
+
+\item{unload}{If a DLL from the target location in 'dll_dir' is already
+loaded, should that be unloaded first?}
+
+\item{overwrite}{If a file exists at the target DLL location in 'dll_dir',
+should this be overwritten?}
\item{x}{An \code{\link{mkinmod}} object.}
@@ -120,6 +130,7 @@ For kinetic models with more than one observed variable, a symbolic solution
of the system of differential equations is included in the resulting
mkinmod object in some cases, speeding up the solution.
+If a C compiler is found by \code{\link[pkgbuild:has_compiler]{pkgbuild::has_compiler()}} and there
is more than one observed variable in the specification, C code is generated
for evaluating the differential equations, compiled using
\code{\link[inline:cfunction]{inline::cfunction()}} and added to the resulting mkinmod object.
@@ -143,12 +154,16 @@ print(SFO_SFO)
\dontrun{
fit_sfo_sfo <- mkinfit(SFO_SFO, FOCUS_2006_D, quiet = TRUE, solution_type = "deSolve")
- # Now supplying full names used for plotting, and write to user defined location
+ # Now supplying compound names used for plotting, and write to user defined location
SFO_SFO.2 <- mkinmod(
parent = mkinsub("SFO", "m1", full_name = "Test compound"),
m1 = mkinsub("SFO", full_name = "Metabolite M1"),
- name = "SFOSFO", cf_dir = tempdir())
-fit_sfo_sfo <- mkinfit(SFO_SFO.2, FOCUS_2006_D, quiet = TRUE, solution_type = "deSolve")
+ name = "SFO_SFO", dll_dir = "~/dll", unload = TRUE, overwrite = TRUE)
+# Now we can save the model and restore it in a new session
+saveRDS(SFO_SFO.2, file = "~/SFO_SFO.rds")
+# Terminate the R session here if you would like to check, and then do
+SFO_SFO.3 <- readRDS("~/SFO_SFO.rds")
+fit_sfo_sfo <- mkinfit(SFO_SFO.3, FOCUS_2006_D, quiet = TRUE, solution_type = "deSolve")
# Show details of creating the C function
SFO_SFO <- mkinmod(
diff --git a/test.log b/test.log
index 844c7542..67fd66c4 100644
--- a/test.log
+++ b/test.log
@@ -6,8 +6,8 @@ Testing mkin
✔ | 2 | Export dataset for reading into CAKE
✔ | 14 | Results for FOCUS D established in expertise for UBA (Ranke 2014) [0.9 s]
✔ | 4 | Calculation of FOCUS chi2 error levels [0.4 s]
-✔ | 7 | Fitting the SFORB model [3.4 s]
-✔ | 5 | Analytical solutions for coupled models [3.1 s]
+✔ | 7 | Fitting the SFORB model [3.2 s]
+✔ | 5 | Analytical solutions for coupled models [2.9 s]
✔ | 5 | Calculation of Akaike weights
✔ | 10 | Confidence intervals and p-values [1.0 s]
✔ | 14 | Error model fitting [4.3 s]
@@ -33,15 +33,15 @@ Skip (test_plot.R:25:3): Plotting mkinfit and mmkin objects is reproducible
Reason: getRversion() < "4.1.0" is TRUE
────────────────────────────────────────────────────────────────────────────────
✔ | 4 | Residuals extracted from mkinfit models
-✔ | 2 | Complex test case from Schaefer et al. (2007) Piacenza paper [1.5 s]
+✔ | 2 | Complex test case from Schaefer et al. (2007) Piacenza paper [1.4 s]
✔ | 4 | Summary [0.1 s]
✔ | 1 | Summaries of old mkinfit objects
-✔ | 4 | Results for synthetic data established in expertise for UBA (Ranke 2014) [2.2 s]
-✔ | 9 | Hypothesis tests [7.1 s]
+✔ | 4 | Results for synthetic data established in expertise for UBA (Ranke 2014) [2.0 s]
+✔ | 9 | Hypothesis tests [6.4 s]
✔ | 4 | Calculation of maximum time weighted average concentrations (TWAs) [2.4 s]
══ Results ═════════════════════════════════════════════════════════════════════
-Duration: 38.5 s
+Duration: 36.9 s
── Skipped tests ──────────────────────────────────────────────────────────────
● getRversion() < "4.1.0" is TRUE (3)
diff --git a/vignettes/mkin.html b/vignettes/mkin.html
index 43481d8f..1f696c37 100644
--- a/vignettes/mkin.html
+++ b/vignettes/mkin.html
@@ -11,7 +11,7 @@
<meta name="author" content="Johannes Ranke" />
-<meta name="date" content="2020-11-24" />
+<meta name="date" content="2020-11-27" />
<title>Introduction to mkin</title>
@@ -1631,7 +1631,7 @@ div.tocify {
<h1 class="title toc-ignore">Introduction to mkin</h1>
<h4 class="author">Johannes Ranke</h4>
-<h4 class="date">2020-11-24</h4>
+<h4 class="date">2020-11-27</h4>
</div>

Contact - Imprint