Overview
The mizer package provides several routes for extending its behaviour
without editing the mizer source code. A full description of these
mechanisms is given in
vignette("extensions", package = "mizer"). This vignette
explains which of those mechanisms mizerStarvation uses and why.
The starvation mortality added by this package is an additional mortality source that runs alongside the standard background, fishing, and predation mortalities. It is not a replacement for any of them. This calls for a mechanism that adds to the mortality rate pipeline rather than replacing it.
The other_mort mechanism
The MizerParams object carries a slot
other_mort: a named list of rate function names. During
every time step, getMort() calls each function in this list
and adds its return value to the total per-capita mortality rate. This
is the correct tool when:
- the extra mortality source is dynamical (it responds to the current community state at each time step), and
- it should be superimposed on the standard mortality terms rather than replacing them.
mizerStarvation registers its rate function into this list:
params@other_mort[["starvation"]]
#> [1] "starvMort"The string "starvMort" is the name of the exported
function mizerStarvation::starvMort(). mizer looks up this
name in the package namespace at projection time, so the function must
remain exported as long as any saved model has starvation mortality
enabled.
Storing parameters in species_params
The only parameter that governs starvation mortality is
starv_coef, one value per species. This is stored as a
column of the species_params data frame:
species_params(params)[["starv_coef"]]Using species_params rather than
other_params() is appropriate here because
starv_coef varies by species. It is also the standard place
mizer uses to keep per-species biological parameters, so it is
automatically saved and restored with the MizerParams
object.
setStarvation() writes the column with the
given_species_params() replacement function rather than by
assigning to the species_params slot directly:
given_species_params(params)[["starv_coef"]] <- starv_coefstarv_coef is user input that mizer never calculates for
itself, so declaring it as a given species parameter records
that provenance. It then shows up in
given_species_params(params) alongside the other parameters
the user supplied, rather than in
calculated_species_params(params).
Registering with mizer on load
When mizerStarvation is loaded, its .onLoad hook
registers the package with mizer:
.onLoad <- function(libname, pkgname) {
mizer::registerExtension(pkgname, requirement = "sizespectrum/mizerStarvation")
}This adds mizerStarvation to the session’s extension
chain. The requirement string is a pak
installation spec, so mizer can install the package automatically if it
is missing. The call is idempotent: reloading the package (e.g. via
devtools::load_all()) does not modify the chain a second
time.
Recording the extension in the params object
Registering the package in .onLoad only announces it to
the session. A MizerParams object additionally
records which extensions were actually applied to it, in its
extensions slot:
params@extensions
#> $mizerStarvation
#> requirement version
#> "sizespectrum/mizerStarvation" "0.2.0" setStarvation() writes that entry with
mizer::recordExtension():
params <- recordExtension(params, "mizerStarvation", version = version)recordExtension() takes the installation requirement
from the session’s registered chain, preserves any entries other
extensions have already made, and inserts this one at the right position
in the chain. This is deliberately narrower than copying the whole of
getRegisteredExtensions() into the object: another
extension package may well be loaded in the session without having been
applied to this model, and the slot is a record of what was
applied, not of what happened to be attached.
The version argument is the version of mizerStarvation
whose object layout the recorded component conforms to.
setStarvation() stamps the installed version only when it
first adds starvation mortality to a model, and passes
version = NULL on subsequent calls so that an existing
stamp is preserved. Re-stamping on an ordinary modification would let an
old object claim to be current and so skip a migration it actually
needs.
Storing this record lets mizer warn users who try to load a saved model in a session where mizerStarvation is missing or out of date.
When starvation mortality is disabled by passing
starv_coef = 0, setStarvation() removes the
"mizerStarvation" entry from params@extensions
again, so the saved object no longer requires the package.
How the pieces fit together
setStarvation() sets up all three pieces in one
call:
params <- setStarvation(params, starv_coef = 10)Passing starv_coef = 0 reverses the change: it removes
the "starvation" entry from other_mort,
removes the starv_coef column from
species_params, and removes "mizerStarvation"
from params@extensions.
During a projection, at each time step and for each species and size
class, mizer calls starvMort():
starvMort <- function(params, n, n_pp, n_other, t = 0, ...) {
e <- getEReproAndGrowth(params, n = n, n_pp = n_pp, n_other = n_other,
t = t)
starv_coef <- species_params(params)[["starv_coef"]]
mu_s <- -t(t(e * starv_coef) / params@w)
mu_s[e > 0] <- 0
mu_s
}getMort() calls each other_mort function
with params, n, n_pp,
n_other, t and the component
name, so a rate function has to accept ... to absorb the
arguments it does not use. Note that t is named explicitly
here and forwarded, so that starvation mortality is computed at the same
time as the rest of the mortality in models whose rates depend on
time.
The function:
- Calls the standard mizer function
getEReproAndGrowth()to get the energy available for growth and reproduction, \(E_r(w)\), at the current community state. - Computes the starvation mortality rate \[\mu_s(w) = \frac{-E_r(w)}{w} \times \texttt{starv\_coef}\] for size classes where the energy balance is negative (\(E_r < 0\)).
- Sets mortality to zero for size classes with a positive energy balance.
The result is a species-by-size matrix with the same dimensions as
initialN(params), which getMort() adds to the
other mortality terms.
Summary of extension points used
| Mechanism | Where | Purpose |
|---|---|---|
params@other_mort |
named list of rate function names | registers starvMort so getMort() calls it
at each time step |
species_params(params)$starv_coef |
column in species parameters, written with
given_species_params<-()
|
stores the per-species proportionality constant |
params@extensions |
named list written with recordExtension()
|
records that mizerStarvation set up this model, and under which version |
This package does not use setRateFunction(),
setComponent(), setExtMort(), or S4
subclassing, because those mechanisms are either designed for replacing
rather than augmenting existing rates, or add more infrastructure than
this simple additive mortality term requires.