From 033e93a4573712b471f4bc679e194afc36205856 Mon Sep 17 00:00:00 2001 From: Gustav Delius Date: Wed, 29 Jul 2026 14:57:09 +0200 Subject: [PATCH] Add `gear` argument to getYieldVsF() and plotYieldVsF() When several gears catch the target species, `gear` selects the one whose fishing mortality is varied. Only that gear's catchability for the species is zeroed and taken over by the temporary gear whose effort is swept, so the fishing mortality that the other gears exert on the species is kept fixed and the returned `F` values refer to the selected gear alone. Without a `gear` the previous behaviour is retained (the fishing mortality from all gears on the species is replaced), but a message now points at the new argument when more than one gear catches the species. The species and gear checks now happen before the initial projection to steady state so that invalid arguments fail fast. Co-Authored-By: Claude Opus 5 --- NEWS.md | 4 ++ R/getYieldVsFcurve.R | 71 +++++++++++++++++++++++++------ man/getYieldVsF.Rd | 19 ++++++++- man/plotYieldVsF.Rd | 15 +++++++ tests/testthat/test-getYieldVsF.R | 34 +++++++++++++++ 5 files changed, 129 insertions(+), 14 deletions(-) create mode 100644 tests/testthat/test-getYieldVsF.R diff --git a/NEWS.md b/NEWS.md index 0b0d22b6..662b4e19 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,6 +3,10 @@ * Now works with and requires development version of mizer. * New `biomass_callback()` function that can be passed to `project()` to plot species biomasses in real time during a simulation. +* `getYieldVsF()` and `plotYieldVsF()` gained a `gear` argument to select which + gear's fishing mortality on the target species is varied when several gears + catch that species. The fishing mortality from the other gears is then kept + fixed. # mizerExperimental 3.1.0 diff --git a/R/getYieldVsFcurve.R b/R/getYieldVsFcurve.R index 9cb3c368..d4a813e3 100644 --- a/R/getYieldVsFcurve.R +++ b/R/getYieldVsFcurve.R @@ -4,11 +4,24 @@ #' that species while the fishing mortalities for the other species are held #' fixed. #' +#' If the target species is caught by several gears you can use the `gear` +#' argument to select the gear whose fishing mortality is to be varied. The +#' fishing mortality that the other gears exert on the target species is then +#' held fixed and the `F` values in the result refer only to the fishing +#' mortality from the selected gear. If you do not select a gear, the fishing +#' mortality from all gears on the target species is replaced by the fishing +#' mortality given by `F_range`, using the selectivity of the first gear that +#' catches the species. +#' #' This is a generic function with a method for objects of class #' [MizerParams][mizer::MizerParams]. #' #' @param params An object of class `MizerParams`. #' @param species Name of the target species +#' @param gear Name of the gear whose fishing mortality on the target species +#' is to be varied. Only needed when several gears catch the target species. +#' If NULL (default), the fishing mortality from all gears catching the +#' target species is varied together. #' @param F_range A sequence of fishing mortalities at which to evaluate the #' yield. If missing, it is set to #' `seq(from = 0, to = F_max, length.out = no_steps)`. @@ -25,7 +38,9 @@ #' @param t_max The longest time to run project to find steady state. #' @param ... Not used. #' -#' @return A data frame with columns `F` and `yield`. +#' @return A data frame with columns `F` and `yield`. If a `gear` was selected +#' then `F` is the fishing mortality exerted by that gear alone, otherwise it +#' is the total fishing mortality on the target species. #' @export #' @family summary functions @@ -37,14 +52,16 @@ #' params <- newMultispeciesParams(NS_species_params_gears, inter) #' y <- getYieldVsF(params, "Cod", F_max = 1, no_steps = 5) #' } -getYieldVsF <- function(params, species, F_range, F_max = 1, F_min = 0, - no_steps = 10, distance_func = distanceSSLogN, +getYieldVsF <- function(params, species, gear = NULL, F_range, F_max = 1, + F_min = 0, no_steps = 10, + distance_func = distanceSSLogN, tol = 0.001, t_max = 100, ...) UseMethod("getYieldVsF") #' @export getYieldVsF.MizerParams <- function(params, species, + gear = NULL, F_range, F_max = 1, F_min = 0, @@ -64,11 +81,7 @@ getYieldVsF.MizerParams <- function(params, stop("Invalid species specification") } - # Project to steady with the current fishing - params <- projectToSteady(params, t_max = t_max, progress_bar = FALSE, - distance_func = distance_func, tol = tol) - - # First make a new gear for that specific species + # Determine the gear params rows whose fishing mortality is to be varied sp_name <- params@species_params$species[[idx_species]] gp <- gear_params(params) gp$gear <- as.character(gp$gear) @@ -76,10 +89,33 @@ getYieldVsF.MizerParams <- function(params, if (length(sp_sel) == 0) { stop(species, " is not selected by any gear.") } + if (!is.null(gear)) { + gear <- as.character(gear) + if (length(gear) != 1) { + stop("You can only select a single gear.") + } + gear_sel <- sp_sel[gp$gear[sp_sel] == gear] + if (length(gear_sel) == 0) { + stop("The gear ", gear, " does not catch ", sp_name, ".") + } + # Only the fishing mortality from the selected gear will be varied, + # that from the other gears is kept fixed. + sp_sel <- gear_sel + } else if (length(sp_sel) > 1) { + message("Several gears catch ", sp_name, + ". The fishing mortality from all of them will be replaced. ", + "Use the `gear` argument if you want to vary the fishing ", + "mortality from only one gear.") + } + + # Project to steady with the current fishing + params <- projectToSteady(params, t_max = t_max, progress_bar = FALSE, + distance_func = distance_func, tol = tol) + current_FMort <- sum(params@initial_effort[gp$gear[sp_sel]] * gp$catchability[sp_sel]) - # base the new gear on the first gear that catches this species - # TODO: think about how to improve this arbitrary choice. + # Make a new gear that exerts the fishing mortality that is to be varied, + # based on the first of the selected gears. gp_extra <- gp[sp_sel[1], ] gp_extra$gear <- "tmp" gp_extra$catchability <- 1 @@ -130,14 +166,16 @@ getYieldVsF.MizerParams <- function(params, #' params <- newMultispeciesParams(NS_species_params_gears, inter) #' plotYieldVsF(params, "Cod") #' } -plotYieldVsF <- function(params, species, F_range, F_max = 1, F_min = 0, - no_steps = 10, distance_func = distanceSSLogN, +plotYieldVsF <- function(params, species, gear = NULL, F_range, F_max = 1, + F_min = 0, no_steps = 10, + distance_func = distanceSSLogN, tol = 0.001, t_max = 100, ...) UseMethod("plotYieldVsF") #' @export plotYieldVsF.MizerParams <- function(params, species, + gear = NULL, F_range, F_max = 1, F_min = 0, @@ -152,6 +190,7 @@ plotYieldVsF.MizerParams <- function(params, curve <- getYieldVsF(params, species = species, + gear = gear, F_range = F_range, F_max = F_max, F_min = F_min, @@ -161,9 +200,15 @@ plotYieldVsF.MizerParams <- function(params, t_max = t_max ) + xlabel <- if (is.null(gear)) { + "Fishing mortality (1/yr)" + } else { + paste0("Fishing mortality from ", gear, " (1/yr)") + } + pl <- ggplot(curve, aes(x = F, y = yield)) + geom_line() + - xlab("Fishing mortality (1/yr)") + + xlab(xlabel) + ylab("Yield") + ggtitle(species) diff --git a/man/getYieldVsF.Rd b/man/getYieldVsF.Rd index 7ec5eab1..5ea6cd77 100644 --- a/man/getYieldVsF.Rd +++ b/man/getYieldVsF.Rd @@ -7,6 +7,7 @@ getYieldVsF( params, species, + gear = NULL, F_range, F_max = 1, F_min = 0, @@ -22,6 +23,11 @@ getYieldVsF( \item{species}{Name of the target species} +\item{gear}{Name of the gear whose fishing mortality on the target species +is to be varied. Only needed when several gears catch the target species. +If NULL (default), the fishing mortality from all gears catching the +target species is varied together.} + \item{F_range}{A sequence of fishing mortalities at which to evaluate the yield. If missing, it is set to \code{seq(from = 0, to = F_max, length.out = no_steps)}.} @@ -46,7 +52,9 @@ RDI over t_per years is less than tol for every species.} \item{...}{Not used.} } \value{ -A data frame with columns \code{F} and \code{yield}. +A data frame with columns \code{F} and \code{yield}. If a \code{gear} was selected +then \code{F} is the fishing mortality exerted by that gear alone, otherwise it +is the total fishing mortality on the target species. } \description{ Calculates the yield of a species for a range of fishing mortalities for @@ -54,6 +62,15 @@ that species while the fishing mortalities for the other species are held fixed. } \details{ +If the target species is caught by several gears you can use the \code{gear} +argument to select the gear whose fishing mortality is to be varied. The +fishing mortality that the other gears exert on the target species is then +held fixed and the \code{F} values in the result refer only to the fishing +mortality from the selected gear. If you do not select a gear, the fishing +mortality from all gears on the target species is replaced by the fishing +mortality given by \code{F_range}, using the selectivity of the first gear that +catches the species. + This is a generic function with a method for objects of class \link[mizer:MizerParams]{MizerParams}. } diff --git a/man/plotYieldVsF.Rd b/man/plotYieldVsF.Rd index 325f42e6..507ad23b 100644 --- a/man/plotYieldVsF.Rd +++ b/man/plotYieldVsF.Rd @@ -7,6 +7,7 @@ plotYieldVsF( params, species, + gear = NULL, F_range, F_max = 1, F_min = 0, @@ -22,6 +23,11 @@ plotYieldVsF( \item{species}{Name of the target species} +\item{gear}{Name of the gear whose fishing mortality on the target species +is to be varied. Only needed when several gears catch the target species. +If NULL (default), the fishing mortality from all gears catching the +target species is varied together.} + \item{F_range}{A sequence of fishing mortalities at which to evaluate the yield. If missing, it is set to \code{seq(from = 0, to = F_max, length.out = no_steps)}.} @@ -54,6 +60,15 @@ that species while the fishing mortalities for the other species are held fixed. } \details{ +If the target species is caught by several gears you can use the \code{gear} +argument to select the gear whose fishing mortality is to be varied. The +fishing mortality that the other gears exert on the target species is then +held fixed and the \code{F} values in the result refer only to the fishing +mortality from the selected gear. If you do not select a gear, the fishing +mortality from all gears on the target species is replaced by the fishing +mortality given by \code{F_range}, using the selectivity of the first gear that +catches the species. + This is a generic function with a method for objects of class \link[mizer:MizerParams]{MizerParams}. } diff --git a/tests/testthat/test-getYieldVsF.R b/tests/testthat/test-getYieldVsF.R new file mode 100644 index 00000000..8f518c5d --- /dev/null +++ b/tests/testthat/test-getYieldVsF.R @@ -0,0 +1,34 @@ +# A params object where two gears catch the same species +params <- NS_params +gp <- gear_params(params) +gp_extra <- gp[gp$species == "Cod", ] +gp_extra$gear <- "Extra" +gp_extra$catchability <- 0.5 +gear_params(params) <- rbind(gp, gp_extra) +initial_effort(params)["Extra"] <- 1 + +test_that("getYieldVsF() checks the `gear` argument", { + expect_error(getYieldVsF(params, "Cod", gear = "Nonexistent"), + "The gear Nonexistent does not catch Cod.") + expect_error(getYieldVsF(params, "Cod", gear = c("Otter", "Extra")), + "You can only select a single gear.") + # Gears that do not catch the species are rejected + expect_error(getYieldVsF(params, "Cod", gear = "Industrial"), + "The gear Industrial does not catch Cod.") + expect_message(getYieldVsF(params, "Cod", F_range = numeric(0)), + "Several gears catch Cod") +}) + +test_that("getYieldVsF() only changes F from the selected gear", { + # Only vary the fishing mortality from the "Extra" gear, keeping the + # current fishing mortality from that gear, so that nothing should change. + curve <- getYieldVsF(params, "Cod", gear = "Extra", F_range = 0.5) + expect_equal(curve$F, 0.5) + # The yield is still positive even at zero effort of the selected gear + # because the "Otter" gear keeps catching Cod. + curve0 <- getYieldVsF(params, "Cod", gear = "Extra", F_range = 0) + expect_gt(curve0$yield, 0) + # Whereas without a gear selection all fishing on Cod is switched off + curve_all <- getYieldVsF(params, "Cod", F_range = 0) + expect_equal(curve_all$yield, 0) +})