Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ S3method(getYield,MizerParams)
S3method(getYield,MizerSim)
S3method(getYieldGear,MizerParams)
S3method(getYieldGear,MizerSim)
S3method(get_f0_default,MizerParams)
S3method(get_gamma_default,MizerParams)
S3method(get_h_default,MizerParams)
S3method(get_h_default,default)
S3method(get_ks_default,MizerParams)
S3method(given_species_params,MizerParams)
S3method(given_species_params,MizerSim)
S3method(given_species_params,data.frame)
Expand All @@ -144,6 +149,7 @@ S3method(matchBiomasses,MizerParams)
S3method(matchGrowth,MizerParams)
S3method(matchNumbers,MizerParams)
S3method(maturity,MizerParams)
S3method(measure_avail_energy,MizerParams)
S3method(metab,MizerParams)
S3method(plot,ArrayResourceBySize)
S3method(plot,ArraySpeciesBySize)
Expand Down Expand Up @@ -479,6 +485,7 @@ export(matchBiomasses)
export(matchGrowth)
export(matchNumbers)
export(maturity)
export(measure_avail_energy)
export(melt)
export(metab)
export(mizerDiffusion)
Expand Down
11 changes: 11 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# mizer 3.4.0

- The functions that calculate species parameter defaults from the model —
`get_gamma_default()`, `get_f0_default()`, `get_h_default()` and
`get_ks_default()` — are now S3 generics, so an extension package can register
its own methods for them instead of reimplementing the calculation. The
measurement of the energy available in the power-law reference state, which is
where those defaults look at the prey in the model, is exported as the generic
`measure_avail_energy()`. An extension that replaces mizer's resource by a
resource system of its own only needs a method for that one function to have
the `gamma` and `f0` defaults derived against its resources. The guide to
creating an extension package explains when this is needed (#598).

- Fixed: `validSim()` passed the resource spectrum where the rate functions
expect the list of other components, so diagnosing a failed simulation of a
model with components created by `setComponent()` failed with `$ operator is
Expand Down
103 changes: 88 additions & 15 deletions R/species_params.R
Original file line number Diff line number Diff line change
Expand Up @@ -1582,16 +1582,31 @@ set_species_param_default <- function(object, parname, default,
#' mathematical derivation.
#'
#' @param params A MizerParams object or a species parameter data frame
#' @param ... Unused.
#' @return A vector with the values of h for all species
#' @export
#' @concept helper
#' @family functions calculating defaults
get_h_default <- function(params) {
if (is(params, "MizerParams")) {
species_params <- params@species_params
} else {
species_params <- validSpeciesParams(params)
}
get_h_default <- function(params, ...) {
UseMethod("get_h_default")
}

#' @export
get_h_default.MizerParams <- function(params, ...) {
h_from_species_params(params@species_params)
}

#' @export
get_h_default.default <- function(params, ...) {
h_from_species_params(validSpeciesParams(params))
}

#' Calculate the default `h` from a species parameter data frame
#'
#' @param species_params A validated species parameter data frame
#' @return A vector with the values of h for all species
#' @noRd
h_from_species_params <- function(species_params) {
assert_that("n" %in% names(species_params))
species_params <- set_species_param_default(species_params, "f0", 0.6)
if (!("h" %in% colnames(species_params))) {
Expand Down Expand Up @@ -1659,10 +1674,43 @@ get_h_default <- function(params) {
#' The caller is responsible for having set the `q` and `gamma` species
#' parameters and the `search_vol` slot that the measurement is to use.
#'
#' # For extension authors
#'
#' This function is the hook by which an extension package that changes the
#' resource structure of the model keeps [get_gamma_default()] and
#' [get_f0_default()] working. Mizer's own method measures the energy available
#' from the single resource spectrum in the `initial_n_pp` slot. An extension
#' that replaces that resource by a system of its own registers a method for its
#' marker class, for example
#'
#' ```
#' measure_avail_energy.mizerMR <- function(params, ...) {
#' # Put each of the extension's resources into the power-law
#' # reference state and measure the energy they make available.
#' }
#' ```
#'
#' and both defaults then measure against that system without the extension
#' having to reimplement the rest of the calculation. A method must return the
#' coefficient \eqn{A_i} described above, not the encounter rate itself, and
#' should follow mizer in measuring a property of the species parameters: leave
#' out any dynamic modulation of the search volume, and any additive encounter
#' contribution, that your extension applies. Folding such a factor into the
#' measurement folds it into `gamma`, which then determines the search volume,
#' so the factor is re-applied on every rebuild (issues #577 and #586).
#'
#' @param params A MizerParams object
#' @param ... Unused.
#' @return A vector with one number for each species
#' @noRd
measure_avail_energy <- function(params) {
#' @export
#' @concept helper
#' @family functions calculating defaults
measure_avail_energy <- function(params, ...) {
UseMethod("measure_avail_energy")
}

#' @export
measure_avail_energy.MizerParams <- function(params, ...) {
params@initial_n[] <- 0
params@ext_encounter[] <- 0
params@other_encounter <- list()
Expand Down Expand Up @@ -1700,13 +1748,23 @@ measure_avail_energy <- function(params) {
#' deliberately does not go through the extension dispatch chain, nor through an
#' encounter function registered with [setRateFunction()].
#'
#' This is an S3 generic. An extension package that replaces mizer's single
#' resource by a resource system of its own directs the measurement at that
#' system by registering a method for [measure_avail_energy()], which is where
#' the energy available in the reference state is measured.
#'
#' @param params A MizerParams object
#' @param ... Unused.
#' @return A vector with the values of gamma for all species
#' @export
#' @concept helper
#' @family functions calculating defaults
get_gamma_default <- function(params) {
assert_that(is(params, "MizerParams"))
get_gamma_default <- function(params, ...) {
UseMethod("get_gamma_default")
}

#' @export
get_gamma_default.MizerParams <- function(params, ...) {
species_params <- params@species_params %>%
set_species_param_default("f0", 0.6)
if (!("gamma" %in% colnames(species_params))) {
Expand Down Expand Up @@ -1782,13 +1840,23 @@ get_gamma_default <- function(params) {
#' deliberately does not go through the extension dispatch chain, nor through an
#' encounter function registered with [setRateFunction()].
#'
#' This is an S3 generic. An extension package that replaces mizer's single
#' resource by a resource system of its own directs the measurement at that
#' system by registering a method for [measure_avail_energy()], which is where
#' the energy available in the reference state is measured.
#'
#' @param params A MizerParams object
#' @param ... Unused.
#' @return A vector with the values of f0 for all species
#' @export
#' @concept helper
#' @family functions calculating defaults
get_f0_default <- function(params) {
assert_that(is(params, "MizerParams"))
get_f0_default <- function(params, ...) {
UseMethod("get_f0_default")
}

#' @export
get_f0_default.MizerParams <- function(params, ...) {
species_params <- params@species_params %>%
set_species_param_default("f0", 0.6)
if (!("gamma" %in% colnames(species_params))) {
Expand Down Expand Up @@ -1837,13 +1905,18 @@ get_f0_default <- function(params) {
#' mathematical derivation.
#'
#' @param params A MizerParams object
#' @param ... Unused.
#' @return A vector with the values of ks for all species
#' @export
#' @concept helper
#' @family functions calculating defaults
get_ks_default <- function(params) {
assert_that(is(params, "MizerParams"),
"n" %in% names(params@species_params),
get_ks_default <- function(params, ...) {
UseMethod("get_ks_default")
}

#' @export
get_ks_default.MizerParams <- function(params, ...) {
assert_that("n" %in% names(params@species_params),
"p" %in% names(params@species_params))
if (!"h" %in% names(params@species_params) ||
any(is.na(params@species_params[["h"]]))) {
Expand Down
3 changes: 3 additions & 0 deletions docs/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1074,6 +1074,9 @@ users building extensions or working with model objects directly.
[`w2l()`](https://sizespectrum.org/mizer/reference/l2w.md) :
Length-weight conversion

- [`measure_avail_energy()`](https://sizespectrum.org/mizer/reference/measure_avail_energy.md)
: Measure the available energy in a power-law prey spectrum

- [`needs_upgrading()`](https://sizespectrum.org/mizer/reference/needs_upgrading.md)
: Determine whether a MizerParams or MizerSim object needs to be
upgraded
Expand Down
3 changes: 3 additions & 0 deletions inst/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,9 @@ users building extensions or working with model objects directly.
[`w2l()`](https://sizespectrum.org/mizer/reference/l2w.md) :
Length-weight conversion

- [`measure_avail_energy()`](https://sizespectrum.org/mizer/reference/measure_avail_energy.md)
: Measure the available energy in a power-law prey spectrum

- [`needs_upgrading()`](https://sizespectrum.org/mizer/reference/needs_upgrading.md)
: Determine whether a MizerParams or MizerSim object needs to be
upgraded
Expand Down
57 changes: 57 additions & 0 deletions inst/skills/create-extension-package/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,59 @@ declare `gamma` as a given species parameter merely to protect it from your own
method or component; that is no longer necessary, and it costs the model the
ability to let `gamma` follow `f0`.

#### But they do go through your `get_*_default()` method

That non-dispatching measurement is right for an extension that *modulates* a
rate mizer already has. It is wrong for an extension that changes the model's
structure — most of all one that replaces the single resource spectrum by a
resource system of its own. The `gamma` values that
`newMultispeciesParams()` auto-derived at construction time were calibrated
against the resource that was there then. Swapping that resource out afterwards
does not re-derive them, so every species that did not come with a `gamma` of
its own is left calibrated against a resource the model no longer has.

Nothing in core mizer can notice this for you, and re-calling
`get_gamma_default()` will not fix it, because its measurement deliberately does
not see your resource system. The five functions involved are therefore S3
generics, so you can supply the part that mizer cannot know:

| Generic | What a method should return |
|---|---|
| `measure_avail_energy()` | the coefficient $A_i$ in $E_i(w) = A_i w^{2 + q_i - \lambda}$, the energy the reference state makes available |
| `get_gamma_default()` | search volume coefficient `gamma` for each species |
| `get_f0_default()` | target feeding level `f0` for each species |
| `get_h_default()` | maximum intake rate coefficient `h` |
| `get_ks_default()` | standard metabolic rate coefficient `ks` |

`measure_avail_energy()` is nearly always the only one you need. It is the
single point at which `get_gamma_default()` and `get_f0_default()` look at the
prey in the model, so a method for it redirects both defaults at your resource
system without your having to reimplement either:

```r
#' @export
measure_avail_energy.mizerShelf <- function(params, ...) {
# Put the extension's own resources into the power-law reference state
# and return the energy they make available to each species.
}
```

Because the rate setters call the generics, an object of your class picks the
method up on every rebuild — a user who hands `gamma` back to mizer by removing
it from `given_species_params()` gets a `gamma` derived against your resources
from then on.

Two rules for such a method:

- Return a property of the *species parameters*, not of the model's dynamics.
Leave out any dynamic modulation of the search volume, and any additive
encounter contribution, that your extension applies. Folding such a factor in
folds it into `gamma`, and since `gamma` then determines the search volume,
the factor gets re-applied on every rebuild (issues #577 and #586).
- Call `NextMethod()` if what you are doing is adding to mizer's reference
state rather than replacing it, so that another extension in the chain still
gets its turn.

### Creating objects: the two commands

A constructor function that returns a `mizerShelf` object must end with these
Expand Down Expand Up @@ -677,6 +730,10 @@ When building a dispatching extension package, verify the following:
own with `other_mort()` or `other_encounter()`, never by assigning into
`params@other_mort` or `params@other_encounter` directly. See
[Metadata-only extensions: mizerStarvation].
- [ ] If your package changes the resource structure of the model, define a
`measure_avail_energy()` method so that the `gamma` and `f0` defaults are
derived against your resources rather than against the one mizer replaced.
See [But they do go through your `get_*_default()` method].
- [ ] Report anything you tell the user with `signal_info()` inside a
`with_info_level()`, never a bare `message()` or `warning()`, and give every
entry point an `info_level = default_info_level()` argument that it forwards.
Expand Down
8 changes: 8 additions & 0 deletions inst/skills/extend-mizer/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ not enter the power-law reference state used by `get_gamma_default()` and
`other_encounter()`, including a component's `encounter_fun`: they describe
feeding on the reference resource alone, before extra food sources are added.

If instead of adding food you *replace* the resource — a package that installs a
resource system of its own — then any `gamma` mizer auto-derived at construction
time is calibrated against a resource that is no longer in the model, and it
will not be re-derived on its own. Give your class a `measure_avail_energy()`
method, which is the hook both defaults use to look at the prey in the model;
see the section "But they do go through your `get_*_default()` method" in
[Creating a mizer extension package](https://sizespectrum.org/mizer/articles/guide-create-extension-package.html).

## Replacing a rate function

Use when the model still follows mizer's standard flow but one step should be
Expand Down
12 changes: 10 additions & 2 deletions man/get_f0_default.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions man/get_gamma_default.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading