From d3c2892e2918523c8e03685ea85e283c1d2c5ee3 Mon Sep 17 00:00:00 2001 From: Gustav Delius Date: Tue, 14 Jul 2026 12:28:12 +0100 Subject: [PATCH] adjustSizeGrid(): warn on per-species diet loss, not resource biomass The high-end resource truncation check previously warned when non-negligible resource *biomass* was discarded above the new maximum size. Biomass loss is not the relevant quantity: what matters is whether truncation changes the food a fish actually gets. This replaces that check with a per-species diet-loss check, mirroring the existing low-end check, evaluated at the largest size each species occupies (where the largest resource matters most). It reports the fraction of the fish's total encounter that is lost. The low-end and high-end resource checks now use consistent wording ("diet of smallest/largest fish ... lost due to resource truncation"), and the tol documentation makes clear that all three checks (biomass, smallest-fish diet, largest-fish diet) are applied separately to each species. Lost fractions are now reported with 3 significant figures rather than two decimal places, so a loss just above the default tol = 1e-6 shows as 0.0001% instead of 0.00%. Co-Authored-By: Claude Opus 4.8 --- NEWS.md | 5 +-- R/manipulate_species.R | 46 +++++++++++++++++------- man/adjustSizeGrid.Rd | 11 ++++-- tests/testthat/test-manipulate_species.R | 4 +-- 4 files changed, 47 insertions(+), 19 deletions(-) diff --git a/NEWS.md b/NEWS.md index e5c2f1fc6..b354f5ece 100644 --- a/NEWS.md +++ b/NEWS.md @@ -85,8 +85,9 @@ - New `adjustSizeGrid()` function (an S3 generic) adjusts the size grid of a `MizerParams` object to a new minimum and/or maximum size. It can both - expand and truncate (shrink) the grid, warning if non-negligible abundance - (species or resource) is discarded. + expand and truncate (shrink) the grid. For each species it warns if truncation + discards a non-negligible fraction of the species' biomass, of the diet of its + smallest individuals, or of the diet of its largest individuals. - New experimental `steadyNewton()` finds a steady state by solving the steady-state equation directly with a Newton-type root finder (using the diff --git a/R/manipulate_species.R b/R/manipulate_species.R index eba52503c..7b0c1d5f0 100644 --- a/R/manipulate_species.R +++ b/R/manipulate_species.R @@ -566,9 +566,14 @@ renameSpecies.MizerParams <- function(params, replace, ...) { #' should be copied over to the new params object rather than being #' re-calculated from the species parameters. If missing, all species are #' preserved. -#' @param tol A numeric value specifying the tolerance for lost biomass. -#' If the fraction of species biomass or resource biomass lost due to -#' truncation exceeds this value, a warning is raised. Defaults to `1e-6`. +#' @param tol A numeric value specifying the tolerance for truncation losses. +#' The following checks are made separately for each species and a warning is +#' raised, listing the affected species, if the lost fraction exceeds this +#' value for any of them: the fraction of the species' biomass lost, the +#' fraction of the diet of the smallest individuals of the species lost to +#' resource truncation, and the fraction of the diet of the largest +#' individuals of the species lost to resource truncation. Defaults to +#' `1e-6`. #' @param ... Additional arguments. #' #' @return A new [MizerParams] object with the updated size grid. @@ -737,7 +742,7 @@ adjustSizeGrid.MizerParams <- function(params, warn_sp <- lost_fracs[lost_fracs > tol] if (length(warn_sp) > 0) { warning("Non-negligible species biomass was lost due to grid truncation: ", - paste(names(warn_sp), sprintf("(%.2f%%)", warn_sp * 100), collapse = ", ")) + paste(names(warn_sp), sprintf("(%g%%)", signif(warn_sp * 100, 3)), collapse = ", ")) } } @@ -764,18 +769,35 @@ adjustSizeGrid.MizerParams <- function(params, warn_diet <- lost_diet_fracs[lost_diet_fracs > tol] if (length(warn_diet) > 0) { warning("Non-negligible diet of smallest fish was lost due to resource truncation: ", - paste(names(warn_diet), sprintf("(%.2f%%)", warn_diet * 100), collapse = ", ")) + paste(names(warn_diet), sprintf("(%g%%)", signif(warn_diet * 100, 3)), collapse = ", ")) } } - # Resource high-end truncation: check resource biomass loss + # Resource high-end truncation: check diet loss of largest fish if (length(truncated_full_high_idx) > 0) { - tot_pp <- sum(params@initial_n_pp * params@w_full * params@dw_full) - if (tot_pp > 0) { - lost_pp <- sum(params@initial_n_pp[truncated_full_high_idx] * params@w_full[truncated_full_high_idx] * params@dw_full[truncated_full_high_idx]) / tot_pp - if (lost_pp > tol) { - warning("Non-negligible resource biomass (", sprintf("%.2f%%", lost_pp * 100), ") was lost due to grid truncation.") - } + pred_kernel <- getPredKernel(params) + encounter_old <- getEncounter(params) + + lost_enc_fracs <- sapply(seq_along(params@species_params$species), function(sp_idx) { + # Largest size bin the species occupies + w_top_idx <- max(which(params@w <= params@species_params$w_max[sp_idx])) + tot_enc <- encounter_old[sp_idx, w_top_idx] + if (tot_enc <= 0) return(0) + + lost_enc <- params@search_vol[sp_idx, w_top_idx] * + params@species_params$interaction_resource[sp_idx] * + sum(pred_kernel[sp_idx, w_top_idx, truncated_full_high_idx] * + params@w_full[truncated_full_high_idx] * + params@dw_full[truncated_full_high_idx] * + params@initial_n_pp[truncated_full_high_idx]) + + return(lost_enc / tot_enc) + }) + names(lost_enc_fracs) <- params@species_params$species + warn_enc <- lost_enc_fracs[lost_enc_fracs > tol] + if (length(warn_enc) > 0) { + warning("Non-negligible diet of largest fish was lost due to resource truncation: ", + paste(names(warn_enc), sprintf("(%g%%)", signif(warn_enc * 100, 3)), collapse = ", ")) } } diff --git a/man/adjustSizeGrid.Rd b/man/adjustSizeGrid.Rd index d7faee401..460aaa485 100644 --- a/man/adjustSizeGrid.Rd +++ b/man/adjustSizeGrid.Rd @@ -36,9 +36,14 @@ should be copied over to the new params object rather than being re-calculated from the species parameters. If missing, all species are preserved.} -\item{tol}{A numeric value specifying the tolerance for lost biomass. -If the fraction of species biomass or resource biomass lost due to -truncation exceeds this value, a warning is raised. Defaults to \code{1e-6}.} +\item{tol}{A numeric value specifying the tolerance for truncation losses. +The following checks are made separately for each species and a warning is +raised, listing the affected species, if the lost fraction exceeds this +value for any of them: the fraction of the species' biomass lost, the +fraction of the diet of the smallest individuals of the species lost to +resource truncation, and the fraction of the diet of the largest +individuals of the species lost to resource truncation. Defaults to +\code{1e-6}.} } \value{ A new \link{MizerParams} object with the updated size grid. diff --git a/tests/testthat/test-manipulate_species.R b/tests/testthat/test-manipulate_species.R index cc5386114..1989b5745 100644 --- a/tests/testthat/test-manipulate_species.R +++ b/tests/testthat/test-manipulate_species.R @@ -520,7 +520,7 @@ test_that("adjustSizeGrid works for expansion and truncation", { expect_warning(adjustSizeGrid(params, new_max_w = params@w[18]), "Non-negligible species biomass was lost") - # Truncating further down with low tol and non-zero resource at large sizes triggers resource biomass warning: + # Truncating the top with low tol and non-zero resource at large sizes triggers largest-fish diet warning: params_pp <- params params_pp@resource_params$w_pp_cutoff <- 50000 params_pp@initial_n_pp[] <- 1 @@ -529,7 +529,7 @@ test_that("adjustSizeGrid works for expansion and truncation", { adjustSizeGrid(params_pp, new_max_w = params@w[18], tol = 1e-15), "Non-negligible species biomass was lost" ), - "Non-negligible resource biomass" + "Non-negligible diet of largest fish was lost" ) # 5. Invalid parameters