Skip to content

Give each species parameter default a single home#458

Merged
gustavdelius merged 6 commits into
masterfrom
default-a-b-species-params
Jul 17, 2026
Merged

Give each species parameter default a single home#458
gustavdelius merged 6 commits into
masterfrom
default-a-b-species-params

Conversation

@gustavdelius

@gustavdelius gustavdelius commented Jul 17, 2026

Copy link
Copy Markdown
Member

Summary

Every species parameter default now has exactly one home, and which home is decided by what consumes the parameter. Two commits, same principle pulling in opposite directions:

  • a and b (0b5b661) are consumed by the length-weight conversion itself — l2w()/w2l() are called from check_and_convert_species_params() inside species_params.data.frame(). Their defaults move into species_params rather than being applied ad-hoc inside l2w()/w2l().
  • p, z_ext, d, E_ext, D_ext, interaction_resource (c610222) are each read by exactly one rate setter. Their defaults move out of the central table to that setter — where they already were, duplicated.

Which parameters stay central

A parameter is defaulted by validSpeciesParams() only when no single rate setter owns it, which happens for five reasons:

Reason Parameters
Several setters read it n (six of them: setExtDiffusion, setExtEncounter, setExtMort, setMaxIntakeRate, setReproduction, setSearchVolume); w_mat, w_inf
Only the projection reads it; no setter touches it alpha
The size grid is built from it, before any setter runs w_min, w_max
The table's own length-weight conversion needs it a, b
Reporting only is_background

Everything else belongs to its setter: p/k to setMetabolicRate(), z0/z_ext/d to setExtMort(), E_ext to setExtEncounter(), D_ext to setExtDiffusion(), interaction_resource to setInteraction(), q/gamma to setSearchVolume(), erepro/m/w_mat25/R_max to setReproduction(), beta/sigma to setPredKernel().

The setter-local defaults are not redundancy: the rate setters are public API that can be called directly, without setParams() and hence without validSpeciesParams(), so each setter has to supply what it needs on its own. Tests already relied on this (e.g. test-setExtMort.R NULLs z_ext/d and requires setExtMort() to restore them).

The p default

Three places set p, with three different values:

  1. newMultispeciesParams()'s own p argument, default 0.7, injected into the species parameter table at newMultispeciesParams.R#L179-L180 before validation;
  2. species_params.data.frame()'s p = n;
  3. setMetabolicRate()'s p = 3/4.

(1) preempts both others, so it is p = 0.7 — not p = n — that models built in the usual way have been getting. (2) applied only when validSpeciesParams() was called directly on a bare data frame, and it shadowed (3).

This PR removes (2). That promotes (3) from shadowed to live, so it is corrected from 3/4 to n for consistency with the exponent it tracks. (1) is untouched and still wins for newMultispeciesParams(), which is why no model changes.

The p argument of setMetabolicRate() (deprecated here)

There is a fourth would-be home: setMetabolicRate()'s own p argument. It never had any effect on a MizerParams object, because it is applied with set_species_param_default() — which only fills a missing or NA column and never overwrites — and (1) guarantees the column is always already there. The condition it was documented for, "if the exponent is not specified in a p column", is unreachable. It was silently ignored.

It is now deprecated in favour of species_params(params)$p <- value, which triggers setParams() and does what was meant. This also makes the family consistent: setSearchVolume() has no q argument and setMaxIntakeRate() has no n argument. What the argument does is unchanged while deprecated; it just warns now.

newMultispeciesParams()'s p is a different argument, is not forwarded to setMetabolicRate(), and still works. It is now documented explicitly there so it does not inherit the deprecation badge, and is excluded from the arguments setParams() advertises in its dots.

Closes #459.

Behaviour

Built models are unchanged — setParams() calls every rate setter, so each deleted default is immediately re-supplied by its owner. Verified with a golden-model diff of NS_params across the change: no species_params column lost, and no numeric change to search_vol, intake_max, metab, mu_b, psi, maturity, ext_encounter, ext_diffusion, initial_n or interaction.

User-visible change: validSpeciesParams() applied to a bare species parameter data frame now returns fewer columns; the setter-owned ones appear once the model is built. Its roxygen previously documented returning n, p, z_ext, d, E_ext, D_ext and interaction_resource, and has been rewritten.

Also

  • Dropped the interaction_resource note in setInteraction(). It was dead while the column was set centrally and would otherwise have fired on every model build; "all species feed on the resource" is the unremarkable default.
  • Removed the unreachable a = 0.004 / b = 3 defaults in plot_growth_curves(), which conflicted with the central a = 0.01.

Tests

  • test-validSpeciesParams.R: asserts the setter-owned columns are absent from a bare validSpeciesParams() result but present on a built model; guards the no-maximum-size case.
  • test-setMetabolicRate.R: pins p == n rather than a hardcoded 3/4, and that the construction and standalone-setter paths agree.
  • test-setExtEncounter.R / test-setExtDiffusion.R: new standalone-setter tests for E_ext / D_ext restoration.
  • test-setMetabolicRate.R: the p argument warns; setting the species parameter works; newMultispeciesParams(sp, p =) still works and does not warn. The existing test that pinned the argument's fill-only semantics is kept (they are unchanged) and renamed.

Full suite green apart from test-plots.R:501, which fails identically on master and is annotated in-source as machine-dependent.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QZop1EdWQNm9SXGCqwWg8k

gustavdelius and others added 2 commits July 17, 2026 10:33
A default for a species parameter is now set by the rate setter that uses
it. `species_params.data.frame()` only sets defaults for parameters that no
single rate setter owns: those read by several setters (`n`), used only when
projecting (`alpha`), needed to build the size grid (`w_min`, `w_max`),
needed for the length-weight conversion (`a`, `b`), or used only for
reporting (`is_background`).

The defaults for `p`, `z_ext`, `d`, `E_ext`, `D_ext` and
`interaction_resource` are therefore removed from the central table. They
were already being set by `setMetabolicRate()`, `setExtMort()`,
`setExtEncounter()`, `setExtDiffusion()` and `setInteraction()`, which need
them anyway because the rate setters can be called directly, without going
through `setParams()` and hence without `validSpeciesParams()`.

Built models are unchanged, because `setParams()` calls every rate setter
and so each deleted default is immediately re-supplied by its owner. But
`validSpeciesParams()` applied to a bare species parameter data frame now
returns fewer columns, which is a documented change.

Removing the central `p = n` promotes `setMetabolicRate()`'s own `p` default
from shadowed to live, so it is corrected from `3/4` to `n` to keep the
effective default the same. The two had silently disagreed whenever
`n != 3/4`, but the central one always ran first and won.

Also removes the message in `setInteraction()`, which was dead while
`interaction_resource` was set centrally and would otherwise now fire on
every model build, and the unreachable `a`/`b` defaults in
`plot_growth_curves()`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QZop1EdWQNm9SXGCqwWg8k
@gustavdelius gustavdelius changed the title Set default values for a and b in species_params Give each species parameter default a single home Jul 17, 2026
gustavdelius and others added 2 commits July 17, 2026 12:34
The entry claimed `setMetabolicRate()` is the only place that sets `p` and
that `p = n` is the value models have been getting all along. Both are
wrong: `newMultispeciesParams()` has its own `p` argument, default `0.7`,
which it injects into the species parameter table before validation, so it
preempts both the `validSpeciesParams()` and `setMetabolicRate()` defaults.
Models built in the usual way get `p = 0.7`, not `p = n`.

The claim that no model changes is unaffected and still holds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QZop1EdWQNm9SXGCqwWg8k
The argument never had any effect on a `MizerParams` object. It is applied
with `set_species_param_default()`, which only creates a missing column or
fills in NAs and never overwrites, and every constructed `MizerParams`
already has a non-NA `p` column because `newMultispeciesParams()` injects
its own `p` argument into the species parameter table before validation. So
the condition the argument was documented for -- "if the exponent is not
specified in a `p` column" -- is unreachable, and the argument was silently
ignored.

Deprecate it in favour of setting the species parameter directly with
`species_params(params)$p <- value`, which triggers `setParams()` and does
what was meant. This also fits the rest of the family: `setSearchVolume()`
has no `q` argument and `setMaxIntakeRate()` has no `n` argument.

What the argument does is unchanged while it is deprecated; it now warns.
The `p` argument of `newMultispeciesParams()` is a separate argument, is not
forwarded to `setMetabolicRate()`, and is unaffected -- it is now documented
explicitly there so that it does not inherit the deprecation badge, and is
excluded from the arguments `setParams()` advertises in its dots.

Closes #459

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QZop1EdWQNm9SXGCqwWg8k
gustavdelius and others added 2 commits July 17, 2026 13:40
Add a `species-param-defaults` skill recording the rule that #458 implements:
a default lives with the rate setter that reads the parameter, and only
parameters that no single rate setter owns are defaulted centrally in
`species_params.data.frame()`.

The skill deliberately does not repeat the list of central parameters or the
five reasons for being on it; those live in the "Where defaults live" section
of the `default_parameters` vignette, and the skill points there so the two
cannot drift apart. What it adds is the part a contributor needs and a user
does not: decide by consumption rather than by tidiness; a setter-local
default is a precondition guard rather than redundancy, because the rate
setters are public API called without `setParams()`, and removing one fails
silently; two homes for one default go divergent, as `p` did; the
`given_species_params` argument rule; and how to prove a move is numerically
inert with a golden-model diff.

AGENTS.md gains the pointer to the skill plus the one fact that is
definitional rather than procedural: `given_species_params` holds the
defaults of function arguments that set species parameters, even when the
user did not override them. The slot's name invites the opposite conclusion,
and a reader can reach it without doing any work on defaults at all.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QZop1EdWQNm9SXGCqwWg8k
@gustavdelius
gustavdelius merged commit b5c23b5 into master Jul 17, 2026
@gustavdelius
gustavdelius deleted the default-a-b-species-params branch July 17, 2026 13:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

p argument of setMetabolicRate() is silently ignored

1 participant