diff --git a/NEWS.md b/NEWS.md index 30d23e339..d23d53aa1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -275,6 +275,10 @@ the mathematical details. resource abundance at positive consumption, the capacity is increased slightly with a warning instead of failing early. +- `project()` now warns when `t_max` is not a multiple of `t_save` and ensures + that the state at `t_max` is always saved, even if the final save interval is + shorter than `t_save`. (#341) + - New function `psi()` returns an `ArraySpeciesBySize` with the population-level reproductive proportion. diff --git a/R/project.R b/R/project.R index 53090586a..cb4cc5394 100644 --- a/R/project.R +++ b/R/project.R @@ -219,6 +219,14 @@ project.MizerParams <- function(object, effort, t_save <- t_max } times <- seq(t_start, t_start + t_max, by = t_save) + t_end <- t_start + t_max + if (!isTRUE(all.equal(times[length(times)], t_end))) { + warning("`t_max` (", t_max, ") is not a multiple of `t_save` (", + t_save, "). Results will be saved at `t_max` but the ", + "final save interval will be shorter than `t_save`.", + call. = FALSE) + times <- c(times, t_end) + } effort <- validEffortVector(effort, params) effort <- t(array(effort, dim = c(length(effort), length(times)), @@ -271,6 +279,14 @@ project.MizerParams <- function(object, effort, # Generate new time points times <- seq(t_start_effort, t_end, by = save_freq) + if (t_max_provided && + !isTRUE(all.equal(times[length(times)], t_end))) { + warning("`t_max` (", t_max, ") is not a multiple of `t_save` (", + save_freq, "). Results will be saved at `t_max` but the ", + "final save interval will be shorter than `t_save`.", + call. = FALSE) + times <- c(times, t_end) + } # Interpolate effort values for the new time points gear_names <- dimnames(effort)[[2]] diff --git a/tests/testthat/test-project.R b/tests/testthat/test-project.R index 3515ffee3..92a181629 100644 --- a/tests/testthat/test-project.R +++ b/tests/testthat/test-project.R @@ -80,6 +80,24 @@ test_that("time dimension is dealt with properly", { expect_equal(dimnames(sim@n)$time, c("2019", "2020", "2021")) }) +test_that("warns and includes t_max when not a multiple of t_save", { + # Scalar effort path + expect_warning( + sim <- project(params, t_max = 10, t_save = 3, effort = 1, + progress_bar = FALSE), + "`t_max`.*`t_save`" + ) + t_dims <- as.numeric(dimnames(sim@n)$time) + expect_equal(tail(t_dims, 1), 10) + expect_equal(t_dims, c(0, 3, 6, 9, 10)) + + # No warning when t_max is exactly a multiple of t_save + expect_no_warning( + project(params, t_max = 9, t_save = 3, effort = 1, + progress_bar = FALSE) + ) +}) + test_that("project method selects consumer time stepper", { params_small <- newMultispeciesParams(NS_species_params_gears[12, , drop = FALSE], info_level = 0)