diff --git a/R/fit.R b/R/fit.R index bf60a0e9b..9e7b0e464 100644 --- a/R/fit.R +++ b/R/fit.R @@ -95,7 +95,32 @@ CmdStanFit <- R6::R6Class( init_ = NULL, profiles_ = NULL, model_methods_env_ = NULL, - return_codes_ = NULL + return_codes_ = NULL, + # wrapper around read_cmdstan_csv() that allows for more informative error + # messages. currently only used for a specific case of missing files when + # rendering with caching in R Markdown or Quarto, but could be extended to + # other cases in the future + read_cmdstan_csv_ = function(...) { + tryCatch( + read_cmdstan_csv(...), + error = function(e) { + err_msg <- conditionMessage(e) + if (isTRUE(getOption("knitr.in.progress")) && + isTRUE(self$runset$args$using_tempdir) && + grepl("File does not exist:", err_msg, fixed = TRUE)) { + stop( + paste0( + err_msg, + "\n If this error happened during a cached Quarto or R Markdown render,\n", + " see `cmdstanr_output_dir` in `?cmdstanr_global_options`" + ), + call. = FALSE + ) + } + stop(e) + } + ) + } ) ) @@ -1432,7 +1457,7 @@ CmdStanMCMC <- R6::R6Class( if (!length(self$output_files(include_failed = FALSE))) { stop("No chains finished successfully. Unable to retrieve the draws.", call. = FALSE) } - csv_contents <- read_cmdstan_csv( + csv_contents <- private$read_cmdstan_csv_( files = self$output_files(include_failed = FALSE), variables = variables, sampler_diagnostics = sampler_diagnostics, @@ -1904,7 +1929,10 @@ CmdStanMLE <- R6::R6Class( if (!length(self$output_files(include_failed = FALSE))) { stop("Optimization failed. Unable to retrieve the draws.", call. = FALSE) } - csv_contents <- read_cmdstan_csv(self$output_files(), format = format) + csv_contents <- private$read_cmdstan_csv_( + files = self$output_files(), + format = format + ) private$draws_ <- csv_contents$point_estimates private$metadata_ <- csv_contents$metadata invisible(self) @@ -2014,7 +2042,10 @@ CmdStanLaplace <- R6::R6Class( if (!length(self$output_files(include_failed = FALSE))) { stop("Laplace inference failed. Unable to retrieve the draws.", call. = FALSE) } - csv_contents <- read_cmdstan_csv(self$output_files(), format = format) + csv_contents <- private$read_cmdstan_csv_( + files = self$output_files(), + format = format + ) private$draws_ <- csv_contents$draws private$metadata_ <- csv_contents$metadata invisible(self) @@ -2097,7 +2128,10 @@ CmdStanVB <- R6::R6Class( if (!length(self$output_files(include_failed = FALSE))) { stop("Variational inference failed. Unable to retrieve the draws.", call. = FALSE) } - csv_contents <- read_cmdstan_csv(self$output_files(), format = format) + csv_contents <- private$read_cmdstan_csv_( + files = self$output_files(), + format = format + ) private$draws_ <- csv_contents$draws private$metadata_ <- csv_contents$metadata invisible(self) @@ -2165,7 +2199,10 @@ CmdStanPathfinder <- R6::R6Class( if (!length(self$output_files(include_failed = FALSE))) { stop("Pathfinder failed. Unable to retrieve the draws.", call. = FALSE) } - csv_contents <- read_cmdstan_csv(self$output_files(), format = format) + csv_contents <- private$read_cmdstan_csv_( + files = self$output_files(), + format = format + ) private$draws_ <- csv_contents$draws private$metadata_ <- csv_contents$metadata invisible(self) @@ -2283,7 +2320,7 @@ CmdStanGQ <- R6::R6Class( if (!length(self$output_files(include_failed = FALSE))) { stop("Generating quantities for all MCMC chains failed. Unable to retrieve the generated quantities.", call. = FALSE) } - csv_contents <- read_cmdstan_csv( + csv_contents <- private$read_cmdstan_csv_( files = self$output_files(include_failed = FALSE), variables = variables, sampler_diagnostics = "", diff --git a/R/options.R b/R/options.R index 9e526ca71..1de6cd639 100644 --- a/R/options.R +++ b/R/options.R @@ -28,6 +28,9 @@ #' CSV files when fitting models. The default is a temporary directory. Files in #' a temporary directory are removed as part of \R garbage collection, while #' files in an explicitly defined directory are not automatically deleted. +#' Note that using caching with R Markdown or Quarto does not store files from a +#' temporary directory, so we recommend setting `cmdstanr_output_dir` to avoid +#' failures when re-rendering. #' #' * `cmdstanr_verbose`: Should more information be printed #' when compiling or running models, including showing how CmdStan was called diff --git a/man/cmdstanr-package.Rd b/man/cmdstanr-package.Rd index e6479f576..be303bac3 100644 --- a/man/cmdstanr-package.Rd +++ b/man/cmdstanr-package.Rd @@ -34,22 +34,22 @@ algorithms, and writing results to output files. \subsection{Advantages of RStan}{ \itemize{ \item Allows other developers to distribute R packages with \emph{pre-compiled} -Stan programs (like \strong{rstanarm}) on CRAN. (Note: As of 2023, this -can mostly be achieved with CmdStanR as well. See \href{https://mc-stan.org/cmdstanr/articles/cmdstanr-internals.html#developing-using-cmdstanr}{Developing using CmdStanR}.) -\item Avoids use of R6 classes, which may result in more familiar syntax -for many R users. +Stan programs (like \strong{rstanarm}) on CRAN. (Note: As of 2023, this can +mostly be achieved with CmdStanR as well. See \href{https://mc-stan.org/cmdstanr/articles/cmdstanr-internals.html#developing-using-cmdstanr}{Developing using CmdStanR}.) +\item Avoids use of R6 classes, which may result in more familiar syntax for +many R users. \item CRAN binaries available for Mac and Windows. } } \subsection{Advantages of CmdStanR}{ \itemize{ -\item Compatible with latest versions of Stan. Keeping up with Stan -releases is complicated for RStan, often requiring non-trivial -changes to the \strong{rstan} package and new CRAN releases of both -\strong{rstan} and \strong{StanHeaders}. With CmdStanR the latest improvements -in Stan will be available from R immediately after updating CmdStan -using \code{cmdstanr::install_cmdstan()}. +\item Compatible with latest versions of Stan. Keeping up with Stan releases +is complicated for RStan, often requiring non-trivial changes to the +\strong{rstan} package and new CRAN releases of both \strong{rstan} and +\strong{StanHeaders}. With CmdStanR the latest improvements in Stan will be +available from R immediately after updating CmdStan using +\code{cmdstanr::install_cmdstan()}. \item Running Stan via external processes results in fewer unexpected crashes, especially in RStudio. \item Less memory overhead. @@ -229,6 +229,7 @@ Other contributors: \item Martin ModrĂ¡k [contributor] \item Ven Popov [contributor] \item Visruth Srimath Kandali [contributor] + \item Aki Vehtari [contributor] } } diff --git a/man/cmdstanr_global_options.Rd b/man/cmdstanr_global_options.Rd index 04e08bd58..b35a642c5 100644 --- a/man/cmdstanr_global_options.Rd +++ b/man/cmdstanr_global_options.Rd @@ -25,6 +25,9 @@ CmdStan be disabled? The default is \code{FALSE}. CSV files when fitting models. The default is a temporary directory. Files in a temporary directory are removed as part of \R garbage collection, while files in an explicitly defined directory are not automatically deleted. +Note that using caching with R Markdown or Quarto does not store files from a +temporary directory, so we recommend setting \code{cmdstanr_output_dir} to avoid +failures when re-rendering. \item \code{cmdstanr_verbose}: Should more information be printed when compiling or running models, including showing how CmdStan was called internally? The default is \code{FALSE}. diff --git a/tests/testthat/test-fit-mcmc.R b/tests/testthat/test-fit-mcmc.R index aede11109..74a8af5ff 100644 --- a/tests/testthat/test-fit-mcmc.R +++ b/tests/testthat/test-fit-mcmc.R @@ -90,6 +90,37 @@ test_that("draws() method returns draws_array (reading csv works)", { expect_equal(posterior::variables(draws_beta_alpha), c("beta[1]", "beta[2]", "beta[3]", "alpha")) }) +test_that("draws() errors with Quarto cache message when temp files are missing", { + skip_if(os_is_wsl()) + + # https://github.com/stan-dev/cmdstanr/issues/1012 + # https://github.com/stan-dev/cmdstanr/pull/1176 + + fit <- testing_fit("logistic", method = "sample", seed = 123, chains = 1) + csv_files <- fit$output_files() + + # Simulate a later cached re-render: the fit object still points to the temp + # output files, which don't exist anymore + unlink(csv_files, force = TRUE) + withr::local_options(list( + # Even if cmdstanr_output_dir is now set to a non-temp directory, + # it was not set when the fit was created so we should still get the error message + # that mentions cached Quarto or R Markdown renders. + cmdstanr_output_dir = test_path("resources"), + knitr.in.progress = TRUE + )) + + expect_error( + fit$draws(), + paste0( + "Assertion on 'files' failed: File does not exist: '", csv_files[[1]], "'.\n", + " If this error happened during a cached Quarto or R Markdown render,\n", + " see `cmdstanr_output_dir` in `?cmdstanr_global_options`" + ), + fixed = TRUE + ) +}) + test_that("inv_metric() method works after mcmc", { x <- fit_mcmc_1$inv_metric() expect_length(x, fit_mcmc_1$num_chains())