setPredKernel(reset = TRUE) now removes a predation kernel that had been
set by hand, rather than only removing the comment that protected it (#608).
The rate functions already used the recalculated kernel, but the discarded
array stayed in the model, so pred_kernel() went on returning it and
getTrophicLevel() paired it with the encounter rate from the new kernel,
reporting trophic levels that could be far off without any warning.
A species whose egg size w_min does not sit exactly on a grid point is no
longer emptied by steadySingleSpecies(), and addSpecies() no longer fails
for such a species with "Candidate steady state holds non-numeric values."
(#610). Under defaults edition 1 the initial abundances were set to zero in
every size class below w_min, which included the class that contains
w_min — the class that w_min_idx points to and from which the steady state
is grown upwards. The cutoff is now the start of that class, so it keeps a
positive abundance. steadySingleSpecies() also now reports which species has
an empty egg size class instead of silently returning a spectrum that is zero
at every size.
The headline change is one you should not be able to feel: MizerParams and
MizerSim are now ordinary S3 objects rather than S4 objects. Slot access with
@ still works and models saved by earlier versions still load, but extension
packages need a version built for this release. The release also changes what
getSteadyResidual() measures and takes components registered with
setComponent() out of the criterion by which mizer decides whether a model is
at steady state. It adds getMeanLength(), reconcileSpeciesParams(), and
accessors for extra mortality and encounter rates (other_mort(),
other_encounter()), and it fixes bugs in the species parameter setters, in the
defaults for gamma and f0, in scaleModel(), in validSim() and in
summary() of an array.
For an overview see the release announcement on the mizer blog.
MizerParams and MizerSim have been converted from S4 classes to pure S3
classes based on named lists. Backwards compatibility for @ and @<- slot
access is fully preserved via S3 @ operator methods, while standard list
operations ($, [[]], names()) now work directly on model objects. Legacy
S4 objects saved with earlier versions of mizer are automatically upgraded to
S3 lists on access.
Extension packages now chain using standard S3 class vectors (e.g.
c("mizerShelf", "MizerParams")), completely replacing dynamic S4 marker
class generation, runtime setClass() calls, and search-path attachment.
saveParams() and saveSim() now store the object's complete S3 class
vector instead of stripping extension classes before serialisation.
readParams() and readSim() still repair the class on legacy files through
their normal validation, but no longer need a separate coercion step.
The NS_params and NS_sim objects shipped with mizer are now stored as S3
objects, so isS4(NS_params) is FALSE and they no longer go through the
legacy-object conversion on first use.
getSteadyResidual() now reports, for each size class, its contribution to
the relative rate of change of its species' biomass, in 1/year, so that
rowSums(getSteadyResidual(params)) is the drift (dB_i/dt) / B_i that
isSteady(), the summary() line and project(check_steady = TRUE) judge a
model by. The array therefore says where a model is unsteady in the same
currency that mizer uses to decide whether it is. Pass the experimental
measure = "per_capita" for the previous default, (dN/dt)/N (#572, #589).
getSteadyResidual() reports every size class, including the ones holding no
fish, so only a species with no biomass at all comes back as NA. Under the
new default measure a class holding a trace contributes a trace, which is what
makes a threshold unnecessary. With measure = "per_capita" the NAs are
where they always were (#570, #572).
Components registered with setComponent() are no longer folded into the
biomass drift that isSteady(), the summary() line,
project(check_steady = TRUE), the "convergence" attribute and
getStability() judge a model by. Every function that finds a steady state
holds the components fixed, so a criterion that included them was one those
functions could not satisfy. Components are now reported separately, through
attr(getSteadyResidual(params), "other") and on their own lines in
summary(); a model can therefore be isSteady() while a component of it is
drifting, and projectUntilSettled() is what settles it (#589).
Mizer now names the species, resource or component responsible for a model's drift, instead of reporting that "a biomass" is changing and sending the user to look at which species are moving (#589).
Fixed: a size class holding a negligible density could stop
projectUntilSettled() from ever converging (#570). Above the size where
growth stops, the density decays exponentially without ever reaching zero, and
distanceSSLogN() counted every class with a positive density, so an
infinitesimal trace of 3e-92 g contributed almost the whole distance at every
check. It gains a biomass_share_cutoff argument, defaulting to 1e-8: a size
class counts only if it holds at least that share of its species' biomass.
Nothing changes for a model without such a trace, so existing distance_tol
values keep their meaning, and what decides whether a state is a fixed point is
unchanged and still integrates over every size class (#570, #574).
Fixed: projectUntilSettled() now says so when a run that stopped at t_max
nonetheless reached a fixed point. converged = FALSE beside
attractor = "fixed_point" is correct — they answer different questions —
but read as a contradiction (#570).
Fixed: tuneSteadyState() no longer reports that it could not rebalance the
resource capacity when the resource dynamics is resource_constant(). A
constant resource hands back the abundance it was given, so there is nothing
to rebalance. The report is meant for a custom resource dynamics with no
balance_<dynamics>() function, but it fired on every model that switches the
built-in resource off, which includes every model of an extension package that
supplies its own resources (#591).
Fixed: the residual entry of the "convergence" attribute, and the
residual column of a MizerScan filled from it, were documented as the
largest per-capita rate of change. They hold the largest relative rate of
biomass change, which is the quantity residual_tol is a tolerance on and
the number rowSums(getSteadyResidual(params)) gives (#572).
Fixed: scaleModel() wrote the rescaled R_max and gamma straight into the
species parameter table, bypassing the record of the values the user has
supplied, so the next recalculation of the species parameters silently undid
the rescaling. They are now recorded as given species parameters, as
matchGrowth() already did. calibrateBiomass(), calibrateNumber(),
matchBiomasses() and matchNumbers() inherit the fix (#599, #602).
Mizer now reports the length-weight defaults a = 0.01 and b = 3 at
info_level = 3 when it fills them in, alongside the other defaults it
reports. This matters because a length-based selectivity function converts the
lengths in gear_params() to weights through a and b, so a defaulted
relationship silently puts the selectivity curve at the wrong weights. The
defaults themselves are unchanged, and info_level = 0 silences the report as
it does the rest (#576).
setFishing() additionally names any species whose gear selects on length
while its a or b was defaulted. That is the case in which an invented
weight-length relationship changes the dynamics rather than just a diagnostic,
so the report is at info_level = 1 and is shown even when
calc_selectivity() or setFishing() is called on its own. Whether a gear
selects on length is decided from the selectivity function's formals, so a
custom sel_func taking species_params is covered too (#576).
New reconcileSpeciesParams() makes a model's species_params() a fixed
point of mizer's recalculation. Values written straight into the
species_params slot are not recorded as given, so mizer used to undo them
at the next recalculation without saying so. The function records every value
that a recalculation would change — repeating until the parameters reproduce
themselves, so that the parameters mizer derives from the hand-set ones are
caught too — and leaves the model itself untouched. readParams() now calls
it automatically, so a saved model whose species parameters had drifted out of
step with its given species parameters keeps the values it holds (#604).
Fixed: a species parameter column can be removed by dropping it from the table
you assign, with either setter (#578, #582). Mizer takes the column out of
given_species_params(), recalculates the parameters it knows how to
calculate and removes the ones it does not, so
species_params(params)$my_col <- NULL and
given_species_params(params)$my_col <- NULL now do what they look like they
do. Previously the first silently restored the column from the given table and
the second left it in species_params() reported as one mizer had calculated.
Removing a column from given_species_params() now also recalculates the
parameter it handed back, which makes it the same instruction as setting that
parameter to NA. This is what an extension package needs in order to
withdraw a species parameter it added when the user switches the extension
off. The removal is reported at info_level 3.
Fixed: the defaults for gamma and f0 are no longer measured through the
extension chain (#577, #584). get_gamma_default() measured the energy
available to a species with getEncounter(), so on an extension object any
modification the extension makes to the encounter rate was folded into mizer's
own gamma and re-applied on every rebuild: an extension that halves the
search volume doubled gamma each time the species parameters were touched,
and one whose factor is zero turned the calculation into an error. Both
get_gamma_default() and its inverse get_f0_default() now measure with
mizerEncounter(), so a rate function registered with setRateFunction() no
longer enters the calculation either. These defaults are a property of the
species parameters, not of the model's dynamics.
Fixed: the defaults for gamma and f0 now also exclude external encounter
and the functions registered through other_encounter(), including a
component's encounter_fun (#586, #596). These additive contributions still
affect the realised encounter and feeding level, but the defaults describe
feeding on the reference power-law resource alone. Previously
get_gamma_default() measured them where they were negligible while
get_f0_default() counted them at full scale, so the two were not inverses.
When no default gamma can be calculated, the error now names the species
concerned and reports the energy measured for them.
Fixed: an invalid w_mat25 is now reported as a missing value for which the
default will be used, and any l_mat25 is cleared with it (#580, #583).
Previously the rejected value was put straight back by the length-to-weight
conversion, landing within rounding error of w_mat, which passed the
w_mat25 < w_mat check and turned the maturity ogive into a step function.
Fixed: assigning a likely misspelling of a species parameter now produces one
warning, when the column is introduced, rather than one per validation pass
(#581, #583). The report also goes through the information-signal mechanism,
so it is collected with mizer's other reports and info_level = 0 silences it
along with everything else mizer says.
New accessors other_mort() and other_encounter(), with their replacement
forms, for the extra contributions to the mortality and encounter rates that
getMort() and getEncounter() sum into their results (#579, #585):
other_mort(params)[["starvation"]] <- "starvMort"
An extension that adds a term depending on the state of the model but carrying
no state of its own can now register it this way, where previously the only
route was to assign into params@other_mort directly, since setComponent()
requires a dynamics_fun and an initial_value that such a term has nothing
to supply. Like other_params(), the new accessors hide the entries belonging
to a component created with setComponent(), so assigning a whole list can no
longer wipe them out. Encounter contributions now receive the current
simulation time as t, as mortality contributions already did, and
setComponent() refuses a component name that a free-standing contribution is
already registered under.
The reference index now separates model-level extension functions from the
recordExtension() and coerceToExtensionClass() infrastructure intended
only for extension package authors.
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 invalid for atomic vectors instead of reporting which rates went non-finite
(#601).
project() and the other functions taking a method argument now accept
"second_order" as an alias for method = "tr_bdf2", matching the name of
the spatial second-order option second_order_w(). Aliases are resolved to
the canonical name, so getSimParams() still reports "tr_bdf2".
method = "predictor_corrector" is now documented as superseded by
method = "tr_bdf2". The two are second order at the same cost and treat the
nonlinear rates identically, but the Crank-Nicolson corrector is only
A-stable and rings at large time steps, where TR-BDF2 does not. It remains
available for backwards compatibility and for comparison.
New getMeanLength() calculates the mean length of the community, the
counterpart of getMeanWeight(), with which it shares a help page (#576).
Fixed: summary() of a species-by-size array now covers the same sizes as
plot() of the same array, namely each species' own range from w_min to
w_max (#575). The two described different arrays:
summary(getEncounter(NS_params)) gave Sprat a maximum encounter rate of
40000 g/year, the rate a 40 kg Sprat would have, against 240 g/year over the
sizes a Sprat reaches. Because a rate usually grows with size, the Min and
Max columns were affected most. Both summary() methods with a size
dimension take all.sizes = TRUE for the old behaviour.
Fixed: summary() of an array now reports NA for a species with no values
left, rather than the -Inf/Inf and warning that min() and max() of an
empty selection give (#575).
Fixed: plotYield(return_data = TRUE) once again returns only the three
documented columns Year, Yield and Species. In mizer 3.3.0 it also
returned the internal Legend column (#593, #594).
A new upgrade-extension-package skill and guide walk extension package
authors through migrating their packages to mizer 3.4.0.
The upgrade-mizer-code skill gained an audit mode and a code-pattern
index, allowing maintainers to systematically audit and update an entire
codebase for mizer 3.4.0 even when no errors or visible symptoms are present.
This release adds experimental tools for analysing the dynamic stability of
steady states and for scanning a model over a range of parameter values, tools
for checking whether a model actually is at its steady state, a general
sizeIntegral() for integrals over the size spectrum, and a substantial
extension of the plotting functions, in particular of plots against a length
axis. It routes nearly everything mizer says while building or changing a model
through a single mechanism controlled by info_level, which extension packages
can now use too, and it gives each of the seventeen accessors that had two names
a single preferred one. The topic articles and the AI-agent skills have become
one set of documents, so that there is now one guide per stage of the modelling
workflow. It also renames the steady-state finders after what each one keeps
fixed, keeping the old names as silent aliases.
For an overview see the release announcement on the mizer blog.
The two steady-state finders gain an experimental solver argument. With
solver = "newton" they solve the steady-state equation directly with a
Newton-type root finder (via the nleqslv package) instead of running the
dynamics to convergence. Unlike the default solver = "project" it converges
even when the steady state is dynamically unstable, and it discovers the
support of the steady state automatically. findSteadyState(solver = "newton")
carries the resource densities among its unknowns and so needs the default
semichemostat resource dynamics; tuneSteadyState(solver = "newton") holds
the resource fixed and works with any.
New experimental getStability() analyses the dynamic stability of a mizer
steady state by computing the eigenvalues of the linearised dynamics at the
fixed point. Mizer discretises the size axis but not time, so the model is a
system of ODEs on the size grid; getStability() differentiates their
right-hand side directly and returns the eigenvalues of that Jacobian.
It reports whether the steady state is stable or
unstable (based on the real parts of the eigenvalues), the maximum real part,
and — where the spectrum contains a complex pair — the
leading_oscillatory_eigenvalue, the oscillation_period at which the model
rings and the leading_oscillatory_eigenvector of that mode.
Components registered with setComponent()
are held fixed and are not in the Jacobian, which mizer now warns about when
it meets one with dynamics of its own.
New experimental getDiscreteStability() asks the separate question of how
mizer's numerical step behaves near the steady state: it linearises the
one-step map that project(method = "euler") takes at a given dt and
returns its discrete_eigenvalues and spectral_radius. A spectral radius
below 1 means the scheme does not amplify perturbations at that step size,
which is not the same as the model being stable — the implicit transport solve
damps oscillations artificially, so a physically unstable steady state can
look discretely stable at a large dt.
New experimental getOscillationModeSim() takes a model at a steady state — or
the stability list that getStability() returns — and constructs a MizerSim
covering exactly one period of the leading oscillatory mode in the linear
approximation, ending at the period itself so that the oscillation closes. It
shows the shape of the mode — which species swing, how far, and in what phase
relative to each other and to the resource — which is a limit cycle only at a
Hopf bifurcation, where the mode's growth rate is zero. The
trajectory is
\eqn{x(t) = x^* + A,\text{Re}[e^{i\omega t},\mathbf{v}]} over the whole
state \eqn{x = (N, n_{pp})}, where \eqn{\mathbf{v}} is the eigenvector of the
dominant oscillatory mode and the amplitude \eqn{A} is scaled so that the
largest relative swing in species biomass equals the amplitude argument
(default 10%), which is the quantity plotBiomass() shows. The resource
oscillates with the amplitude and phase the mode gives it, rather than being
slaved to the fish. The returned object
can be passed directly to plotBiomass(), plotSpectra(), and other
standard mizer plot functions.
plotYieldVsF() has moved to mizer from mizerExperimental and is now a thin
wrapper over scanModel(), described below. It plots the yield of one species
against the fishing mortality on that species, leaving the fishing on every
other species alone, and marks the fishing mortality at which the yield is
largest, which is $F_{MSY}$. The current fishing mortality is drawn as a
"Current F" reference line. A limit cycle is drawn as a band around the
average rather than being silently averaged away, and if the species has an
F_MSY species parameter it is also drawn as a reference line for comparison.
getYieldVsF() has not come with it: use plotYieldVsF(return_data = TRUE),
which returns the MizerScan object behind the plot, or scanModel() itself.
New experimental scanModel() scans any aspect of a model over a range of
values and measures any quantity on the attractor the model settles on at
each of them. You say what to vary by passing a function that changes the
model, and what to measure by passing a function that computes a quantity from
a MizerSim, so a yield-versus-fishing-mortality curve, a bifurcation diagram
over fishing effort and a scan over the resource carrying capacity are all the
same call with different arguments. All of mizer's summary functions
(getBiomass(), getYield(), getSSB(), getN(), sizeIntegral()) work as
the measuring function unchanged.
How the quantity is measured depends on what the model settled on, which
projectToSteady() now reports. On a fixed point it is read straight off the
settled state. On a limit cycle the model is projected for exactly one
period of the detected cycle and the quantity is averaged over it, giving
its long-term average; a window that is not a whole number of periods would
leave a residue of the oscillation in the average. When the model settled on
neither, the quantity is averaged over t_sample years and the scan values
concerned are named in a message, because those points should not be relied
on.
New scanEffort(), scanFishingMortality() and scanSpeciesParam() build the
function that applies each scan value to the model; any function of
(params, value) returning a MizerParams will do, as long as it is
idempotent.
New experimental MizerScan S3 class holds the result of a scanModel() run.
It is a data frame with one row per scan value and series, carrying as
attributes everything plot() needs to draw it — the axis names and units, the
MizerParams the scan started from, optional reference lines, and at_max,
the scanned value at which each series attains its largest value (which is
$F_{MSY}$ on a yield-versus-fishing-mortality scan). It has plot(),
print(), summary(), as.data.frame(), str() and [ methods; [
recomputes at_max from the rows that remain, so a subset never carries a
stale maximum.
plotDataFrame() gained two styles, "ribbon" and "envelope", which draw a
band between the ymin and ymax variables of the data frame. "ribbon"
draws the y variable as a line inside the band; "envelope" draws lines along
the two edges instead. plot.MizerScan() uses them to draw the range a
quantity covers on a limit cycle, and so shares the line types, highlighting
and xlim that plotDataFrame() provides.
steady() and projectToSteady() now report the nature of the solution they
converged to via a "convergence" attribute on the returned object. It answers
three questions separately, because conflating them is how a limit cycle came
to be reported as a converged steady state: termination says why the run
stopped, converged whether the solver met its own criterion, and attractor
what the state actually is — "fixed_point", "limit_cycle" or NA. Only
attractor may be read as a claim about the model, and it is set from the
measured biomass drift, reported alongside as residual. Limit
cycles are detected from a per-species biomass series sampled at the new
t_save resolution (default dt). The relative-amplitude floor for calling an
oscillation a limit cycle is a separate amplitude_tol argument (default
0.01), independent of the fixed-point convergence tolerance, and a
species is treated as extinct once its reproduction falls below the
extinction_threshold fraction (default 1e-6) of its value at the start of
the run. The species that went extinct are recorded in the new extinct
field of the "convergence" attribute.
tuneSteadyState() and findSteadyState() no longer stop at a state that is
still moving. The distance function dropping below distance_tol is only half
the criterion: the model's biomass drift must also be within the new
residual_tol argument (default 0.05/year, the tolerance isSteady()
uses), and the limit-cycle detection runs at every check rather than only when
the distance criterion has failed. A cycle whose period divides t_check is
sampled at one phase by the distance function and used to look perfectly
converged; it is now recognised. scanModel() gains the same argument and no
longer draws such a point as a band of zero width (#562).
The superseded steady() and projectToSteady() keep the stopping rule they
shipped with, so a script that relies on a loose tol for a quick run is
unaffected. They do report the drift they stopped on: termination = "distance_tolerance" with attractor = NA says the distance criterion was
met while the model was still moving.
The projection inside a steady-state search now passes absolute time to the
rate and component functions, as project() does. Each block used to restart
the clock at zero, so a model with seasonal or otherwise time-dependent
dynamics saw the same interval repeatedly and could not settle onto a forced
cycle (#562).
The projection tolerance of the new steady-state family is called
distance_tol rather than tol, so that it cannot be confused with
residual_tol, and the Newton solver's own tolerance is solver_tol rather
than residual_tol. The superseded steady() and projectToSteady() keep
their tol argument unchanged.
getDiscreteStability() linearises the step project() actually takes. It
used to substitute a backward-Euler formula for the resource instead of the
model's own resource_dynamics, which made the result an approximation to a
slightly different numerical map (#562).
findSteadyState(solver = "newton") accepts a model in which a species is
already absent. Its all-zero row used to reach log() and stop the root
finder with a non-finite starting vector; the species is now left out of the
system, held at zero and reported (#562).
tuneSteadyState() warns when it cannot rebalance a custom resource, and both
it and the stability analyses warn when a component registered with
setComponent() has dynamics of its own: these tools hold such components
fixed and cover the consumers and the resource only. Nothing is refused —
findSteadyState(solver = "project") advances everything and is not
restricted — but the assumption is no longer silent (#562).
getStability() and getOscillationModeSim() warn when they are handed a model
that is not at a steady state. Both linearise the dynamics at the stored
state, so on a state that is not a fixed point their eigenvalues describe the
neighbourhood of a point the model is not sitting at (#495).
New experimental S3 generic isSteady() returns TRUE if a model is at its
steady state (within a tolerance, defaulting to 0.05/year), FALSE otherwise.
Extension packages can provide custom methods.
New experimental getSteadyResidual() answers the question every calibration
workflow otherwise has to remember to ask: is this model still at its steady
state? It returns the rate at which each species' abundance would change if
the model were projected forward, as a per-capita rate in 1/year, so zero
means the model is on a fixed point. For the consumers the value is exact
rather than a finite difference. Everything is evaluated with the model's own
reproduction function and its own resource_dynamics, so unlike
findSteadyState(solver = "newton") it works whatever the resource dynamics
are. The result is an ArraySpeciesBySize, so
plot(getSteadyResidual(params)) shows which species and which sizes have
moved (#495).
summary() of a MizerParams object now reports the model's biomass drift
and whether that counts as being at a steady state. Whether a model is settled
is not visible from any of the other parameters shown, and getting it wrong is
the most common way a calibration goes quietly wrong (#495).
project() gains an experimental check_steady argument. With
check_steady = TRUE it warns when it is handed a model that is not at its
steady state, which catches the mistake of forgetting to re-run
tuneSteadyState() after a match…() step. It defaults to FALSE, because
projecting a model away from its steady state is a perfectly normal thing to
do. The check is made at the effort stored in the params object rather than at
the effort passed to project(), so running a fishing scenario at a new
effort does not warn (#495).
The "convergence" attribute also carries a residual field giving how far
the state reached actually is from a fixed point. The distance field only
compares two states t_check apart on whatever scale the distance function
uses, whereas residual measures the thing itself, and tuneSteadyState()
reports when the two disagree — a run declared converged whose biomasses are
still visibly moving (#495).
steady() and projectToSteady() no longer end a successful run by saying
"Convergence was achieved in 12 years." They now say what was actually tested
— "Reached the convergence tolerance after 12 years." — and report the biomass
drift every time rather than only when it is large, because that number is the
evidence for whether the state is a fixed point. Code matching the old wording
needs updating.
matchBiomasses(), matchNumbers() and matchGrowth() now say that they
have moved the model off its steady state, turning an instruction the
documentation had to keep repeating into something the package says at the
moment it becomes true. The calibrate…() functions and scaleModel()
deliberately do not: they apply one overall scaling factor, which is an exact
symmetry of the model and leaves the steady state untouched (#495).
New reproduction_level() and reproduction_level<- accessor and replacement
functions allow reading and changing the reproduction level while preserving
the steady state, matching the syntax of resource_level() and
resource_level<-. getReproductionLevel() is deprecated in favour of
reproduction_level().
matchGrowth() gains the info_level argument that the other match…()
functions already had, and matchBiomasses() and matchNumbers() now
actually honour theirs.
The biomass and the number variants of the calibration and matching functions
now share one implementation each: calibrateBiomass() and
calibrateNumber() differ only in whether the size integral carries a factor
of the weight, as do matchBiomasses() and matchNumbers(), but each was a
separate copy of the code and the copies had drifted apart. All four behave
exactly as before on the default quadrature scheme (#504).
New experimental sizeIntegral() calculates any integral
$\int N_i(w)K_i(w),dw$ over the size spectrum. It is now the recommended
way to write your own summary or indicator function: it selects the size
range, applies the quadrature scheme the model is actually on and wraps the
result in the appropriate mizer array class, so none of those rules need to
be remembered. It takes the weighting factor $K$ via the weighting
argument in any of the shapes mizer's own arrays come in, from a single
number to a gear x species x size array, and keeps the extra dimensions in the
result. getBiomass(), getN(), getSSB(), getYield(), getYieldGear()
and getProportionOfLargeFish() are now all implemented with it (#494).
New experimental bin_average_weight() prepares the weight of an integral
over the size spectrum so that the integral uses the quadrature scheme the
model is actually on. Use it when writing your own indicator or diagnostic
function: it is gated on the bin_average entry of second_order_w(), so an
indicator written with it is unchanged on the default scheme and correct to
second order when second-order bin-averaging is switched on. Previously only
available internally as bin_average_summary_weight().
New experimental encounter_kernel() returns the predation kernel that
mizerEncounter() actually uses under whichever quadrature scheme the model
is on, to be paired with the plain point prey weight w_full * dw_full. Any
diagnostic that decomposes the encounter rate must use this rather than
getPredKernel(), which is point-sampled on the grid and is intended for
plotting and for supplying a custom kernel.
Mizer arrays now state what kind of quantity they hold. Every array
constructor gains a type argument: "value" (the default) for a rate or an
amount, "density" for an amount per gram of body weight, "proportion" for
a fraction. Two things follow from it.
A "density" is multiplied by the appropriate Jacobian when it is plotted
against a length axis (size_axis = "l"), and its units are restated from
1/g to 1/cm. This replaces the guess mizer used to make from the array's
name and units, which recognised only densities that happened to be called
"Number density" or to have units "1/g" — and so missed getFluxGradient().
Arrays that declare no type still fall back to that guess, so existing code
and saved objects are unaffected.
A "proportion" — getFeedingLevel(), getCriticalFeedingLevel(),
maturity(), repro_prop(), psi(), resource_level() — is plotted on a
linear y axis showing the whole of the interval from 0 to 1, so the value can
be read against the scale it belongs to. The range is only ever widened to
include the data, never narrowed to that interval: the critical feeding level
and the resource level can both legitimately exceed 1, and their plots show
it. plot(getFeedingLevel(params)) therefore now shows the same y range that
plotFeedingLevel() always has.
plot(), plot2(), addPlot() and animate() on an array that holds a
density gain a per_log_size argument, which expresses the values per
logarithmic size rather than per size. This is the same change of measure that
size_axis makes — both rescale the density by a Jacobian — so the two now
sit side by side, and plotSpectra() is no longer the only way to see a
spectrum per log size. Unlike size_axis it needs no weight-length
relationship, so the resource classes take it too. Asking for it on an array
that does not hold a density is now an error; it used to be swallowed silently
by ....
plotSpectra(), plotSpectra2(), plotCDF(), plotCDF2() and animate()
now let you choose the plotted quantity with two independent arguments instead
of the single power: biomass selects a biomass density rather than a
number density and the new per_log_size selects a density with respect to
logarithmic size rather than with respect to size. The power of the weight is
the sum of the two, which is why power = 1 was ambiguous: it is both the
biomass density and the number density in log size, and mizer had to assume
the former when choosing the y-axis label and the Jacobian for a length axis.
The number density in log size,
plotSpectra(sim, biomass = FALSE, per_log_size = TRUE), is therefore now
available for the first time with the correct label and length conversion. The
power argument keeps working as before and remains the only way to ask for a
power that is not the sum of the two flags, but supplying it together with a
contradictory flag is now an error instead of silently ignoring the flag
(#501). plotCDF() does not accept per_log_size, because the cumulative
distribution does not depend on that choice. As a side effect,
plotlySpectra(), plotlyCDF(), plotlySpectra2() and plotlyCDF2() now
honour biomass, which they used to drop because they always passed power
on internally.
The resource can now be shown on length-based plots. resource_params()
gains the weight-length parameters a and b, defaulting to the equivalent
spherical diameter of an organism with the density of water, a = pi/6 and
b = 3, which is the convention plankton ecology uses for a composite of many
taxa. plotSpectra(params, size_axis = "l") therefore includes the resource
spectrum, where it used to drop it silently, and the resource array plots and
animate() gain size_axis and llim. The parameters feed none of the
rates. Note that the resource then sits on the length axis at its own
convention: a fish of a given weight is about 3.7 times longer than a sphere
of that weight, so the resource and the species are measured differently.
The total line is now shown on length-based plots, where it used to be dropped. A total can only be formed once every line sits on the same coordinate, and on a length axis the lines do not: each species, and the resource, converts weight to length with its own allometric relationship, so at a given length they sit at different weights. The total is therefore summed after the conversion — at equal length rather than at equal weight — interpolating each series onto the union of all the size coordinates, logarithmically in size, with a series contributing nothing outside its own range. Where the series already share a grid, which is always the case on a weight axis, the union is that grid and nothing is approximated: the weight-axis total is unchanged.
plotSpectra2(), plotSpectraRelative(), plotCDF() and plotCDF2() keep
their total on a length axis too. They used to convert the axis after
assembling the two spectra, at which point the total they had been given —
summed at equal weight — had no species to convert it by and was dropped. They
now let plotSpectra() convert instead. As a result plotSpectra2() also
applies ylim the way plotSpectra() does on a length axis, which it could
not do before because the values it was filtering were a Jacobian away from
the ones the limits described.
total = TRUE also now means the same thing everywhere: the total of
everything the object holds. For plotSpectra() that was already so and stays
so. The array plots have been brought into line: plot(<array>, total = TRUE)
used to sum only the species that were selected for display and now sums the
whole array, so a plot of two species can be read against the community
total.
The array-plotting toolkit now covers every mizer array class. The resource
classes ArrayResourceBySize (as returned by getResourceMort(),
resource_rate(), resource_capacity(), resource_level() and
initialNResource()) and ArrayTimeByResourceBySize (as returned by
NResource() on a MizerSim) gain plot2(), plotRelative() and
addPlot() methods, and ArrayTimeByResourceBySize also gains an animate()
method, so you can now compare resource spectra before and after a model
change or play one through a simulation the same way you already could for
species rates. ArrayTimeBySpeciesBySize gains the addPlot() method it was
missing. The species, total and background arguments do nothing for a
resource array, which holds a single spectrum, so the resource methods warn if
they are set (#468).
Each array now prepares its complete plotting data once — species selection,
background grouping, natural-size masking, density conversion, the total line
and the size-axis conversion — and plot(), addPlot(), plot2() and
plotRelative() all consume that one representation. The comparison plots
used to do the conversion themselves, for both operands at once, with the
first array's model. That is wrong whenever the two arrays come from models
that differ in the weight-length relationship w = a l^b: on a length axis
the second spectrum was drawn at the first model's lengths, and a density was
rescaled by the first model's Jacobian. Each operand is now prepared with its
own model, so a cross-model length plot is right. On a weight axis, and
whenever the two models agree, nothing changes.
plotRelative() now interpolates the two series onto a common size grid
instead of matching them by equality of the size coordinate. Once each
operand is converted with its own model, two length grids need not coincide,
and an inner join then kept only their exact coincidences — usually none, so
the plot came out nearly empty. Each series is now interpolated linearly in
the logarithm of size onto the union of the two sets of coordinates,
restricted to the interval both of them cover, so that nothing is
extrapolated. Where the two grids already coincide — always on a weight axis
— the union is that grid and the interpolation reproduces the values exactly.
plotSpectraRelative() gains the same treatment. For a density on a length
axis the relative difference itself now moves, because the Jacobian of the
two models no longer cancels out of the ratio; that cancellation was only
ever valid when the two models shared a and b.
Comparing two arrays that hold different kinds of value is now an error rather
than a warning. The type of an array decides whether its values are
multiplied by a Jacobian on a length axis and whether the y axis is scaled to
the interval from 0 to 1, so a density and a plain value have no pair of axes
in common. The warnings about a differing value_name or differing units are
unchanged.
plot2() and plotRelative() accept highlight, which they used to take
through ... and discard.
plot() on an ArrayTimeBySpeciesBySize now goes through the same slice
helper as the other methods. It used to slice the array by hand, losing the
representation tag on the way, so under second-order bin-averaging
(second_order_w = c(bin_average = TRUE)) a bin-averaged quantity such as
getFMort(sim) was drawn at the left bin edges instead of the geometric bin
centres it lives at.
Plots of a time-by-species array — plot(getBiomass(sim)) and friends —
handle background species in a single pass, which fixes three things. A
background species used to be drawn twice, once under its own name and again
under the "Background" legend, whenever species was left at its default of
all of them. background = FALSE used to leave the background species on the
plot, under their own names, instead of removing them. And a background
species with no values left to draw aborted the plot with replacement has 1 row, data has 0. A background species is now drawn when the selection asks
for it — as plot() on a species-by-size array has always done — and is
always labelled "Background".
The same plots no longer drop values of zero or less from a linear y axis.
They have no place on a logarithmic axis and are still dropped there, but on a
linear one they are data like any other, and a quantity that can go negative
lost exactly the part of it that was interesting. plotRelative() on a
time-by-species array is drawn on a linear axis and so keeps them too, which
is what lets it show the −2 of a species that has gone from present to absent.
animate() applies the same axis handling as the static plots. A resource
animation on a length axis labels its y axis 1/cm rather than keeping the
1/g of the weight axis, an animation of a proportion is drawn on a linear y
axis showing the whole of the interval from 0 to 1, and llim and tlim are
checked for length like the other limits.
plotFeedingLevel(), plotlyFeedingLevel() and plotYield() now delegate
directly to the array plotting methods — plot() / plotHover() on
ArraySpeciesBySize and plot(getYield(object)) — so they share the
behaviour and the arguments of those methods. plotFeedingLevel() keeps full
support for include_critical = TRUE and its non-clipping proportion
coordinate scaling.
plotYieldObservedVsModel() gains a gear argument that restricts the
comparison to the catch of the selected gears. Both the model yield and the
observed yield are then taken from those gears only, so in a model where
several gears catch the same species you can check the gears against their
own observations instead of only their total. Without the argument the plot
keeps comparing the yield summed over all gears. Because the species
parameter yield_observed is a total over all the gears, per-gear
observations have to be given in gear_params() (#286).
New mizer_info_level option sets how much mizer tells you about the choices
it makes, without your having to pass info_level to each call.
options(mizer_info_level = 0) quietens mizer as a whole, including the
functions that have no info_level argument of their own, such as
species_params<-() and the rate setters. The info_level argument still
overrides it for a single call, and its default is now
default_info_level(), which reads the option.
The reporting mechanism is now exported, so that an extension package can tell
the user what it decided on their behalf through the same channel mizer uses,
and have it obey the same switch: signal_info() raises a report,
with_info_level() collects the reports raised inside a call and gives them
together when it finishes, signal_not_recalculated() is the standard report
for a setter that left a hand-set array alone, and default_info_level()
reads the mizer_info_level option. Give your own constructors and setters an
info_level = default_info_level() argument and forward it, rather than
hard-coding a value in the call to newMultispeciesParams(), which would
collide with a user's own info_level. All four are marked experimental: the
mechanism is new and the details of the contract — the level scale, the
severity and unhandled arguments and the info_about_default condition
class — may still change in the light of how extensions use it.
The information mizer gives while it sets up or changes a model is now raised
through one function, signal_info(), which says which quantity the report is
about, how important it is, whether it is a message or a warning, and whether
it should still be shown when nothing is collecting reports. The collecting
handlers now nest by themselves, so a function can report the information
raised inside it without having to know whether its caller is already doing
so, and two different things said about the same quantity are both reported
where previously the second overwrote the first. This is the machinery behind
the frozen-rate warnings described below, and it is available to packages that
extend mizer.
Nearly every message and warning that mizer gives while building or changing
a model now goes through that mechanism, including the ones in steady(),
projectToSteady(), validParams(), setInteraction(), setReproduction(),
setBevertonHolt(), setResource(), newTraitParams(),
newSingleSpeciesParams(), plotYieldObservedVsModel() and the upgrade of
an old object. They are collected into a single report rather than a stream,
and info_level (or the mizer_info_level option) controls all of them
alike, where before each function decided for itself. steady(),
projectToSteady() and validParams() no longer implement their own
info_level threshold, and newSingleSpeciesParams() and
setBevertonHolt() gain an info_level argument.
The two species parameter setters now avoid unnecessary recalculation.
given_species_params<-() records every non-NA value as explicit input,
including a value equal to the current calculated value, but no longer
rebuilds the model for such a provenance-only change, and changes to
observation, direct-runtime and unrelated custom columns also skip the full
setParams() sequence. Changes that can reach a rate array keep the
conservative rebuild path.
Changing a species parameter that feeds a rate array you have set by hand now
warns you that the change has no effect on the model. Previously
given_species_params<-() recorded the new value in the species parameter
table but left the model unchanged and said nothing, because the message the
rate setter does emit in that situation was swallowed by the
suppressMessages() that quietens the routine chatter of the recalculation.
The report is now a warning, which survives that, and it names the species
parameters that were ignored, the quantity that is holding them back, and the
call that puts that quantity back under the control of the species parameters,
for example setMetabolicRate(params, reset = TRUE). It is raised only when a
parameter that actually feeds the frozen quantity changed, so models in which
mizer itself freezes arrays, like those from newTraitParams() and
newCommunityParams(), do not warn about unrelated changes (#489).
Changing a resource parameter that feeds a resource array you have set by
hand now warns you that the change has no effect, the same way a species
parameter change does. resource_params(params)$kappa <- ... with a
resource_capacity() that was set manually previously changed the stored
scalar and nothing else, in complete silence. setResource() also now says
when it leaves a frozen resource array alone although the resource
parameters ask for a different value (#489).
The division of labour between the two species parameter setters is now
clear-cut: given_species_params<-() is the one that warns when a change you
asked for cannot take effect, and species_params<-() stays quiet. That
covers all three such diagnostics — a parameter overridden by another one you
have given, a parameter feeding a rate array you have set by hand, and a gear
parameter that mizer reads from gear_params(). Use species_params<-() in
scripts and given_species_params<-() interactively, where the diagnostics
are worth having (#496).
Those three diagnostics now agree about what counts as a change. Clearing a
given species parameter to NA is one: it hands the parameter back to
mizer's calculation, and if the rate array that calculation feeds has been
frozen, that instruction cannot be carried out and you are now told so.
Adding a column that holds only NA is not a change, and no longer draws a
warning that nothing else agreed with. Previously each of the three
diagnostics answered the question differently, so an all-NA new column
warned about a frozen array while clearing a value that was actually given
was reported by none of them (#524).
given_species_params<-() now warns when k_vb is specified on a model where
h or age_mat is already given and will override k_vb.
setExtMort() now warns when explicitly supplied z0pre or z0exp
arguments are ignored because z0 is already present in
given_species_params() for every species or because ext_mort was
supplied. A z0 value that is present only in species_params() is now
recognised as calculated and is recalculated from z0pre, w_inf and
z0exp. When either argument is explicitly supplied and used, the resulting
values are recorded in given_species_params(). Values calculated from the
arguments' defaults remain calculated parameters and are not recorded there
(#493).
setParams(params, reset = TRUE) is now a documented argument of
setParams() rather than something that happened to be forwarded through
.... It thaws every rate array that setParams() sets.
get_gamma_default() and get_ks_default() now provide informative error
messages when called on a model with h = Inf, indicating that gamma or
ks must be supplied explicitly.
gear_params<-() and calc_selectivity() now list the missing column names
in the error message when arguments needed for a selectivity function are
missing from the gear parameter data frame.
New knife_edge_length() selectivity function applies a knife-edge cut at a
given length rather than a weight. Set sel_func = "knife_edge_length"
and provide a knife_edge_length column in gear_params(). The length is
converted to a cut-off weight via the length–weight parameters a and b.
New experimental gaussian_mixture_pred_kernel() supports multimodal feeding
preferences represented by mixtures of Gaussian distributions on the log
predator/prey mass-ratio scale. Its component parameters are held in the
list-columns kernel_p, kernel_mean and kernel_sd of the species
parameter data frame, a convention mizer uses nowhere else, which is why the
function is marked experimental.
validParams() is now about 15 times faster on an object that is already
valid (#461). The repair work — rebuilding the species parameter tables and
the w_min_idx and ft_mask slots, and checking the structural validity of
the object — is now skipped for an object that has already been through it.
Mizer recognises such an object by a fingerprint calculated from the slots
that the repair and the structural checks depend on. The fingerprint is
recalculated on every call and is not stored on the object, so it cannot go
stale: any change to any of those slots, made by any route including a direct
slot assignment, triggers the full validation. The checks for non-finite
values in the rate arrays are still made on every call, because they catch
what the fingerprint cannot see, and validSim() benefits automatically. One
consequence: a warning that the repair issues about a condition it does not
itself fix, for example that a species has a maximum size larger than the
largest size in the model, is now issued only the first time an object with
that content is validated in a session.
record_given_species_params() and the ArrayResourceBySize and
ArrayTimeByResourceBySize classes are now marked experimental, to say
explicitly that their interfaces may still change. Nothing about their
behaviour changes.
per_log_size is now a formal argument of the animate() generic rather than
reaching the methods through .... Nothing changes for code that calls
animate(), since the argument was always passed through; an extension
package that defines its own animate() method should add per_log_size
after size_axis to stay consistent with the generic.
steady() and projectToSteady() are superseded. Neither name said what
distinguished the two functions, and projectToSteady() returned a different
class depending on an argument. Use tuneSteadyState() in place of steady()
and findSteadyState() in place of projectToSteady(), or
projectUntilSettled() where you wanted projectToSteady(return_sim = TRUE).
The new names say what each one keeps: tuneSteadyState() holds the
reproduction rate and the resource abundance at the values you supply and
adjusts erepro/R_max and cc_pp to make them steady, while
findSteadyState() changes no parameter and lets everything settle together.
The two finders always return a MizerParams and projectUntilSettled()
always returns a MizerSim, so return_sim is gone from the new functions.
Three arguments are spelled differently on the new functions: tol is
distance_tol, t_per is t_check and defaults to 15 * dt so that it can
no longer contradict a dt you chose, and projectUntilSettled() gains
t_save, the interval at which the trajectory is saved, exactly as in
project() and independent of t_check.
Nothing breaks: the old names are kept as thin wrappers that reproduce the old
behaviour exactly, return_sim included. They do not warn and they are not
going away.
matchYields() and calibrateYield() have been removed. They were deprecated
in mizer 2.6.0 and no use case for them was reported. Both worked by
multiplying the abundance of a species at all sizes by a constant factor,
which is the wrong lever for a yield: use mizerExperimental::matchYield(),
which adjusts the catchability instead (#526).
Seventeen accessors that returned a value stored in the MizerParams object had
two names that did exactly the same thing. The get-prefixed name is now
superseded in favour of the bare name, which is the one that also has a
replacement function (catchability(params) <- value and friends):
getCatchability() → catchability(), getSelectivity() → selectivity(),
getInitialEffort() → initial_effort(), getInteraction() →
interaction_matrix(), getResourceDynamics() → resource_dynamics(),
getResourceLevel() → resource_level(), getResourceRate() →
resource_rate(), getResourceCapacity() → resource_capacity(),
getPredKernel() → pred_kernel(), getSearchVolume() → search_vol(),
getMaxIntakeRate() → intake_max(), getMetabolicRate() → metab(),
getExtMort() → ext_mort(), getExtEncounter() → ext_encounter(),
getMaturityProportion() → maturity(), getReproductionProportion() →
repro_prop() and getReproductionLevel() → reproduction_level(). The
get prefix is now reserved for the functions that calculate something from
the current state of a model, like getEncounter() or getFMort().
The old names are kept as plain aliases: they do not warn and they will not be
removed, so existing code and old scripts keep running unchanged. They are no
longer used anywhere inside mizer and should not be used in new code. Note
that getInteraction() and the four resource getters did warn in earlier
versions; they no longer do. Because these are aliases rather than generics,
an extension that defined a method such as getExtMort.MyClass or
getInteraction.MizerParams needs to rename it to ext_mort.MyClass or
interaction_matrix.MizerParams.
The sim2 argument of plotYield() is deprecated in favour of
plot2(getYield(sim1), getYield(sim2)).
The older aliases getM2(), getM2Background(), getZ(), getESpawning(),
plotM2(), setRmax(), completeSpeciesParams() and the inter dataset are
now labelled superseded in the documentation instead of deprecated.
Nothing about them changes: they never warned and they are not going away.
The deprecated label is now reserved for things that do warn and are
scheduled for removal.
The gear_params print method no longer reports each row as a gear. Every row
is a gear-species pair, so a single gear catching ten species was announced as
"gear parameters for 10 gears"; it now says how many pairs there are and how
many gears they belong to.
A logarithmic axis no longer warns about the values and limits it cannot
show. plotPredMort(sim, log_y = TRUE) warned twice: once because the
function asks for a y axis starting at zero, which is not a point on a log
axis, and once because predation mortality is exactly zero on the largest
individuals. A limit of zero or below is now auto-scaled away on a log axis,
and values that cannot be drawn there are dropped quietly, as plotSpectra()
has always done — unless dropping them would empty the plot, in which case
they are left for ggplot2 to complain about as before.
The summary plots plot(sim) and plot(params) now send the arguments that
describe the plotted spectrum — power, biomass, per_log_size and
resource — to plotSpectra() alone, instead of passing their ... to every
panel. So plot(sim, per_log_size = TRUE) works, where routing it to the
feeding level panel too would have failed with "per_log_size only applies to
an array that holds a density".
The default values for the gamma and f0 species parameters are no longer
corrupted by a search volume that you have set by hand. get_gamma_default()
measures the energy available to a predator by giving it a search volume
coefficient of 1, but it obtained that search volume by calling
setSearchVolume(), which refuses to recalculate a search_vol array you
have set yourself, so the available energy was read off your array instead and
the resulting gamma could be wrong by many orders of magnitude. Both
get_gamma_default() and get_f0_default() now compute the search volume
they need directly from the species parameters, so they are unaffected by a
frozen search_vol and remain exact inverses of each other (#488).
get_f0_default() now respects interaction_resource under
defaults_edition() >= 2, making it the exact inverse of
get_gamma_default() also in a model where the species do not all feed on
the resource with the same strength.
Setting f0 to a value outside the interval [0, 1) now gives an immediate
error, whether or not gamma has been supplied. Previously f0 = 1
silently produced an infinite gamma and a non-finite search_vol when
gamma was calculated, while an invalid f0 supplied alongside gamma
could be accepted and ignored (#517).
Changing the resource power-law parameters now refreshes the species search
volume parameters that mizer calculated from them. Changing lambda
recalculates calculated q and gamma entries, while changing kappa
recalculates calculated gamma; explicitly supplied species values remain
protected. Previously resource_params(params)$lambda <- ... and the
corresponding setResource() calls rebuilt the resource arrays but silently
left these calculated species parameters and search_vol stale (#497).
species_params<-() no longer records a default that mizer filled in as a
given species parameter. A parameter that the model does not yet carry and
that validSpeciesParams() supplies a default for — most commonly the
length-weight parameters a and b, which a model built without length data
does not have — looked like a column the user had just added, and so was
recorded as given and frozen against every later recalculation. On
NS_params any assignment to species_params(), even one setting an
unrelated parameter, added 24 given entries this way. Values you do supply,
including in a column that is new to the model, are recorded as before
(#496).
$ on a species_params or gear_params table no longer partially matches
column names. In a model without length-weight parameters,
species_params(params)$a returned the alpha column and $b the beta
column, with the species names attached, so code converting weights to
lengths silently got the assimilation efficiency and the preferred
predator/prey mass ratio instead. Writing was never partially matched, so
reads and writes disagreed about which column $b meant. A name that is not
a column now gives NULL, with a warning naming the column that used to be
returned (#487).
w_min is now included in given_species_params, so the min_w argument to
newMultispeciesParams() and emptyParams() is preserved across any operation
that rebuilds the species parameters from the given ones. Previously, a
given_species_params<- round-trip would silently reset w_min to 0.001 when
min_w was smaller than that, and emit a spurious warning when min_w was
larger (#460).
setParams() now gives an error when it is passed an argument that none of
the setter functions it calls accepts. Every one of those setters declares its
... as unused, so any misspelled or misplaced argument was silently accepted
and ignored: setParams(params, metabolic = 99) did nothing and said nothing.
Arguments that belong to another function are named in the error together with
the function that does accept them. In particular setParams() never called
setResource(), so setParams(params, resource_rate = ...) had no effect on
the model; the error now points at setResource(), and the deprecation
warnings for setResource(r_pp) and setResource(kappa), which used to
recommend setParams(), now recommend setResource() too. setResource()
likewise now errors on an argument it does not have, instead of ignoring it
(#491).
info_level = 0 now really does silence all the information about default
values, including the reports that until now were plain messages: the notes
about the interaction matrix dimnames, about no_w being increased, about
negative resource abundances, about what an upgrade changed, and the
consistency corrections to w_mat, w_mat25, w_min and w_repro_max.
Previously an information signal whose level was above info_level was
passed on rather than dropped, so it could still be reported by a handler
further out, and a plain message ignored info_level altogether.
The warning that a species' w_min is smaller than the minimum weight of the
size grid now tells you to expand the grid with adjustSizeGrid() instead of
with the deprecated expandSizeGrid().
compareParams() now uses relative tolerance when comparing species
parameters, so small-magnitude parameters like gamma (~1e-8) are no longer
silently treated as equal when they differ by up to ~10%.
calibrateBiomass(), calibrateNumber(), matchNumbers(),
plotBiomassObservedVsModel() and plotYieldObservedVsModel() now integrate
over the size grid with sizeIntegral() like everything else in mizer. They
had each hand-rolled the sum, so they stayed on the first-order quadrature and
cut the size range at a bin boundary even in a model with
second_order_w(params)["bin_average"] switched on. In such a model a species
matched to its observed biomass was then plotted off the 1:1 line, because the
match and the plot measured the biomass differently, and the model yields in
plotYieldObservedVsModel() came out 10-20% below getYield(), so the plot
and its total-relative-error caption reported an under-prediction of the
yields that was not there. Results on the default quadrature scheme are
unchanged (#504, #529).
matchNumbers() no longer re-tunes the reproduction parameters when it has
nothing to match. Its guard against an empty selection of species never
fired, so with no observations, or none for the species asked for, it left
the abundances alone but still adjusted the reproduction parameters and
reported a rescaling that had not happened (#504).
plotYieldObservedVsModel() now finds the observed yield where mizer says it
belongs. yield_observed is documented as a gear_params() column, and
given_species_params<-() tells you to put it there, but the plot read it
only from the species parameters, so a model that followed the advice got the
error that no yield_observed had been provided. The plot now takes the
observations from the gear parameters, summed over the gears catching each
species, and still accepts them in the species parameters for the species
that have no gear observation (#526).
getProportionOfLargeFish() called on a MizerParams object gave a wrong
answer. It multiplied the species x size abundance array by the vector of
weights, which R recycles down the columns of the array instead of along the
size axis, so every species but the first was weighted by the wrong sizes.
The MizerSim method was unaffected, and the two now agree when applied to
the same state (#494).
getN() now applies the model's quadrature scheme to the size range it is
given, so that under second_order_w(params) <- c(bin_average = TRUE) the
bin straddling min_w or max_w contributes only partially, as it already
did in getBiomass(). Results on the default first-order scheme are
unchanged (#494).
getDiet(proportion = FALSE) no longer overcounts when second-order
bin-averaging is switched on with second_order_w(). It was applying the
prey-bin quadrature twice — once through its bin-averaged prey weight and
again through the bin-integrated predation kernel that setPredKernel()
builds under second_order_w — so the diet was uniformly too large by
(1 + beta) / 2, where beta is the grid ratio (9.7% for NS_params).
Summing the diet over prey now reproduces
getEncounter() * (1 - getFeedingLevel()) under both schemes, on both the FFT
and the custom-kernel path. getDiet(proportion = TRUE), the default, was
unaffected because the factor was uniform and divided out (#474).
getTrophicLevel() had the same quadrature mismatch: its
trophic-level-weighted numerator was built from the point-sampled
getPredKernel() and a bin-averaged prey weight, while its denominator came
from getEncounter(). Under second_order_w the two are no longer the same
integral, so the reported trophic levels were off by up to 0.06. Numerator
and denominator now use the same quadrature, and a predator whose prey all
have trophic level 1 comes out at exactly 2 in both schemes (#474).
steady() now successfully converges when the advective flux scheme is set
to "van_leer" (via second_order_w). The time-stepping iteration used to
fall into a limit cycle because the flux limiter weights flipped wildly across
cells; the limiter chi is now relaxed with an exponential moving average
(#522).
Size-spectrum plots with size_axis = "l" now transform number and
biomass densities from weight to length units using the species-specific
Jacobian. Biomass density per logarithmic size interval (power = 2) uses
the corresponding logarithmic Jacobian (#469).
plotFeedingLevel(include_critical = TRUE) no longer draws a critical feeding
level above 1 off the top of the plot. The y axis was fixed to the interval
from 0 to 1, and now widens when the data need it.
The "Multi Species Model", "Community Model", "Trait-Based Model" and "The
General Mizer Size-spectrum Model" articles have been brought up to date with
the current API. Between them they used deprecated accessors, said that the
MizerParams object stores neither abundances nor fishing effort (it stores
the initial values of both), described steady() as re-tuning erepro when
it now preserves the reproduction_level by default and also rebalances the
resource, gave the wrong sign for the exponent in the default external
mortality $z0_i = z0_{pre},w_{\infty.i}^{n-1}$, said that the investment into
reproduction reaches one at w_max when since 3.1 that is w_repro_max,
claimed 100 size bins where newTraitParams() now chooses 161, and contained
Rd markup that does not render in an Rmd article.
The "Community Model" article now uses mizer's own plotting functions in place
of the base-R and ggplot2 code it used to build by hand, and reaches the final
state of a simulation with finalParams() and finalN() instead of working
out the index of the last time step. The trophic cascade is now shown with
plotSpectraRelative() and plotSpectra2(), which make the alternating
increases and decreases far easier to see than the ratio plot did. It also no
longer claims that w_mat is NA in a community model, and it points at the
current guides rather than at the articles they replaced.
The "Trait-Based Model" article has had the same treatment. Its base-R figures
are now plotSpectraRelative() and plot2(), and the predation mortalities
are taken from finalParams() rather than by passing finalN(sim) alongside
the original params, which paired the final fish abundances with the model's
initial resource spectrum and was wrong by up to 12%. The text now also
mentions the extinctions its summary plot has always shown — at an effort of
0.75 six of the ten species go extinct — and explains why the summary table
lists the species sizes under w_inf: with diffusion off nothing grows past
w_repro_max, so newTraitParams() puts the grid boundary there instead of
leaving the usual 1.5 * w_inf headroom, and w_inf, w_repro_max and
w_max all coincide.
"Extending mizer" and "Guide: Extending mizer" have been merged into a single
guide at guide-extend-mizer, generated from the extend-mizer skill; the old
address redirects. The topic had been split across three documents that each
explained setRateFunction() in full — the two articles and the "Level 3"
section of the change-parameters skill, which is now a pointer. Each half had
something the other lacked, and both are now in one place for readers and
agents alike: the table of the signature and return shape required of every
replaceable rate, and the rules on respecting the second_order_w quadrature
scheme and on never letting a rate jump as a function of abundance.
A skill can now keep material out of the agent's copy and in the article, which
is what lets the worked examples stay evaluated: an <!-- article-only -->
block, the mirror of the existing <!-- agent-only --> one. The guide builder
keeps its content and drops the markers, and mizerAgents (>= 0.4.0) drops the
block as it installs the skill, so a topic still lives in a single file.
The "Creating a mizer extension package" article is now generated from a new
create-extension-package skill, and is named guide-create-extension-package
like the other guides; the old address redirects. Everything in the
extending-mizer guide that only matters once you share an extension — method
dispatch, bundled data objects, reporting to the user, upgrading saved objects
— moved into it, leaving that guide to the mechanisms themselves. Its advice on
marker classes has also been corrected: it still told you to define them with
setClass(), which mizer 3.2 made unnecessary and which prevents your package
from being chained with another.
"Using mizer extension packages" is now a guide like the others, generated
from a new use-extension-packages skill, so an agent working in a project
that loads mizerEcopath, mizerShelf, therMizer or any other extension knows how
the extension chain works. Being named after its skill, the article moved from
using-extension-packages to guide-use-extension-packages; the old address
redirects. It gains a statement of the two rules that cover almost every
problem — load the packages before using a model that needs them, and persist
models with saveParams() and readParams() rather than bare
saveRDS()/readRDS(), because the mizer helpers validate and upgrade the
object and load its extension packages.
The "Point values and bin averages" section of vignette("numerical_details")
now explains where each bin integral is performed and why it must be applied
exactly once, and a new "The second_order_w switch" section documents what
the flag changes and how to make your own diagnostic second-order accurate.
New article "Discontinuous rate functions"
explains what goes wrong when a custom rate function registered with
setRateFunction() depends discontinuously on the abundances — chattering
trajectories that keep changing as dt is refined, a stalled Newton solver,
and an unreliable getStability() — why none of the time-stepping methods can
fix it, and how to avoid it by giving the switch a finite width.
The topic articles and the AI-agent skills are now one set of documents
rather than two. Each inst/skills/<topic>/SKILL.md is shipped as an agent
skill and is also the source of the matching guide-* article, built by
dev_scripts/build_guides.R. mizerAgents (>= 0.3.3) installs the
skills from the mizer it finds installed, so the two packages can no longer
drift apart.
These articles are no longer called cheatsheets. A cheatsheet reminds you of
something you already know, whereas these assume no prior knowledge, so each
is now a guide. Each is also named after the skill it comes from, and
titled with that skill's own heading, so a topic has one name instead of
three: cheatsheet-fishing has become guide-set-up-fishing, titled "Guide:
Setting up fishing", from the set-up-fishing skill. Every old address
redirects, but vignette("cheatsheet-fishing") does not — see the
"Upgrading mizer"
article for the full table of old and new names. The build-multispecies-model
skill was renamed to build-model at the same time, since it covers the
single-species, community and trait constructors too.
There is now one guide per stage of the workflow, matching the skills one
for one. Two are new: Running Simulations (the arguments of project(),
the four ways of giving fishing effort, continuing and comparing runs, and
when numerical diffusion in the default upwind scheme can damp a real
oscillation), which had a skill but no article, and Extending mizer, the
merged guide described above.
The former "Model setup and calibration" article has been split into Model setup and Steady state and calibration, which are separate tasks reached for at different times. The old URL redirects to the first.
The remaining guides gain the material that had previously only been
written on the skill side: the fishing guide now covers setFishing()
and how catchability fixes the units of fishing effort; the calibration
guide covers the solver argument and reproduction_level(); the model
setup guide covers saving and reloading a model with
saveParams()/readParams(); the changing-parameters guide explains
that the feeding level is set by f0 rather than by h; and the analysis
guide recommends finalParams() over indexing a time series with
idxFinalT().
New analyse-stability skill, and the matching "Guide: Dynamic
Stability" article, cover the experimental stability tools added in this
version: getStability(), findSteadyState(solver = "newton"),
getOscillationModeSim() and scanModel(). Like the other skills it is
shipped in inst/skills/ and picked up by mizerAgents::setup_mizer_agent()
from the installed mizer, so an agent's guidance describes the version of
mizer the project actually runs.
New understand-size-spectrum-dynamics skill, and the matching "Guide:
Size-Spectrum Dynamics" article, explain how mizer models behave rather than
which function to call: which quantities you impose and which the model
produces for itself, the food and predation feedback loops that couple
species, what sets the slope of the steady-state spectrum and the timescales
of its dynamics, and the difference between density dependence imposed through
R_max and the density dependence that emerges from the feedback loops. It
closes with a table mapping a symptom you actually see — a species that
collapses, oscillates, stops growing before w_mat, or refuses to respond to
fishing — to what to inspect.
The "Upgrading mizer"
article is now also shipped as an AI-agent skill, upgrade-mizer-code, built
from inst/skills/upgrade-mizer-code/ by the same generator as the guides —
the skill keeps its symptom index in SKILL.md and one file per release
under references/, so an agent loads the index and then only the release it
needs. Vignettes are not installed with the package, so an agent helping
you fix a script that stopped working after an upgrade previously had no
access to this information and would debug a deliberate, documented change
from first principles. The skill adds an index that maps the symptom you
actually see — an "unused argument" error, a deprecation warning, a plot that
changed, an identical() comparison that now fails — to the release that
caused it and the fix.
This patch release fixes how species and gear parameters are handled when they are assigned. A size given as a weight can now actually be set on a model specified by lengths, editing one of these parameter tables on its own no longer validates it on every assignment, and code that sets a species parameter together with the rate array it determines can now record its change without triggering a recalculation that would undo it.
Fixed: given_species_params<-() did not apply the length/weight precedence
rule, so it disagreed with species_params<-(), which the documentation
describes as equivalent apart from its warnings. On a model where a size is
given both as a weight and as a length, changing the length through
given_species_params<-() left the weight at its old value and warned that
the length was inconsistent, and it went on warning at every later parameter
change because the given species parameters were never brought into line.
The same edit made with species_params<-() gave a maturity weight up to 73%
larger. Both setters now follow the same rule: the one you gave last wins
(#490). given_species_params<-() also preserves the columns of the
species_params slot that mizer does not calculate, which
species_params<-() already did.
Fixed: species_params<-() did not re-derive the calculated species
parameters that a rate setter owns (h, gamma, ks, q, z0, beta,
w_mat25, …). It rebuilt the species parameters from
given_species_params() but then carried the previously calculated values
over, so they looked like given values and no longer responded to a change in
the parameters they are derived from. For example, in a model where h is
derived from the age at maturity, changing w_mat left h (and with it
gamma and ks) at its old value. species_params<-() and
given_species_params<-() now agree. Columns that mizer does not calculate
are still preserved, as are values you supplied yourself.
species_params<-() gains a recalculate argument. With
species_params(params, recalculate = FALSE) <- value mizer records the
values you changed in given_species_params, so that they are not calculated
away later, and stores the parameters you supplied, but does not re-derive
the calculated species parameters and does not recalculate any of the
size-dependent rates. This is for code that sets a species parameter together
with the rate array it determines, where the recalculation would undo the
caller's own adjustment. Keeping the object consistent is then the caller's
responsibility.
New record_given_species_params() exports the entry-by-entry change
detection that species_params<-() uses to decide which values to record in
given_species_params(). It is the recording step on its own, without the
rebuild of the species parameters and the recalculation of all the rates.
This is for code that has already written into the species_params slot
itself, for example an optimiser that fits a species parameter together with
the rate array it determines. Such code has to record its changes: a species
parameter written straight into the slot is silently reverted the next time
anything triggers a recalculation. If you have a species parameter data frame
to hand rather than having written into the slot, use
species_params(params, recalculate = FALSE) <- value instead.
A size that can be given either as a weight or as the length it converts to
(w_mat and l_mat, and likewise w_mat25, w_repro_max, w_inf, w_max
and w_min) now follows a single rule: the one given last wins, and if
both are given at the same time the weight wins. The other one is set to
match, so the two never disagree.
Previously the length always won, whenever and however it had been supplied.
On a model specified by lengths an assignment like
species_params(params)$w_mat[1] <- 100 was therefore silently replaced by
the value calculated from the unchanged l_mat, so weights simply could not
be set. Setting a length still determines the weight as before.
This also resolves a contradiction: given both a weight and a length for the
same size, mizer would report "I will ignore your value for l_max" and then
use l_max anyway. It now does what it says.
The conversion between lengths and weights is now applied per species. Previously a single species whose weight and length disagreed caused the whole column to be recalculated from the lengths, which also overwrote the weights of species whose length was not known at all.
Mizer now warns, naming the species, when a length and a weight for the same
size disagree and it changes the length to match the weight. This was
previously silent and in the other direction: on a model whose stored w_mat
had drifted from a * l_mat^b, any assignment to species_params() rewrote
w_mat for every species without saying so.
Editing a species_params or gear_params data frame on its own no longer
validates it on every assignment. The [<-, $<- and [[<- methods that
did this have been removed, so such a data frame now behaves like the plain
data frame it is: you can leave it in an inconsistent state while you work on
it, and no conversion, check or warning happens until you validate it, which
is what species_params() does to a plain data frame and what
species_params<-() does to the table it is given. The subclass is still
preserved, because the base data.frame methods preserve it.
This makes such an assignment about 13 times faster, and it is what lets the
length/weight rule work as intended: species_params<-() can see which
values you changed relative to the model, so the value you set last wins.
Note that a data frame edited on its own carries no such history, so if a
length and a weight both differ they count as given at the same time and the
weight wins.
species_params<-() no longer errors when the species parameter data frame
contains a list column (or a column holding S4/other objects), and no longer
mis-handles a matrix column. The old-vs-new diff now compares such columns
per species with identical() instead of ==.
l2w() and w2l() are about 3 times faster. They were spending about 85% of
each call on argument checking rather than on the conversion: assert_that()
builds its message whether or not it is needed, and is(species_params, "MizerParams") consulted the S4 class hierarchy even for a plain data frame.
They now do the same checks, in the same order and with the same error
messages, more cheaply. Validating a species parameter data frame is also
faster, because the length/weight conversion now does the arithmetic inline
instead of calling these functions once for each of the six size parameters.
matchGrowth() now records the species parameters it scales (gamma, h,
ks and k) among the given species parameters. Previously it wrote them
straight into the species_params slot, so any later species parameter
change recalculated them from the unscaled given values and silently undid
part of the match: on NS_params the growth rate no longer matched after a
subsequent assignment to species_params().
library(mizer) prints when it first sees a new
version now only appears when the major.minor part of the version changes.
It links to the release announcement for the series, and there is one of
those per minor release, so a patch release or a development version no
longer re-shows an announcement you have already read.This release overhauls how species and resource parameters are set, makes the
extension framework composable regardless of load order, adds a new
adjustSizeGrid() function and cheatsheets, and includes a range of smaller
improvements and bug fixes.
For an overview see the release announcement on the mizer blog.
Assigning to resource_params() (or one of its components, e.g.
resource_params(params)$kappa <- ...) now immediately rebuilds the
size-dependent resource rate (rr_pp) and capacity (cc_pp) arrays from the
scalars, leaving any manually set (frozen) arrays untouched, exactly as
species_params<- feeds the species rates. As a result changing the
rate-side scalars r_pp or n now takes effect (previously the value was
silently discarded), and successive scalar edits accumulate instead of
overwriting each other. Assigning to resource_params() no longer balances
the resource; balancing to preserve the steady state is now solely a feature
of setResource().
The resource_rate<-, resource_capacity<-, resource_level<- and
resource_dynamics<- setters gained a balance argument (default
unchanged) so it can be switched off, e.g.
resource_capacity(params, balance = FALSE) <- my_capacity.
setResource() no longer silently overwrites a manually set (frozen) rate
or capacity array when balancing: the frozen array wins and a warning is
issued. The one exception to the frozen-array protection is that steady()
will rebalance the resource_capacity in order to return a steady state,
ignoring any freeze.
These changes and how to adapt existing code are described in the new
vignette("upgrading") ("Upgrading mizer").
Modifying species parameters via species_params<-() now automatically
detects your changes, records them in given_species_params so they are
protected from being overwritten by defaults in the future, and silently
triggers the recalculation of any dependent parameters and rate arrays.
Previously, species_params<-() bypassed the given_species_params
protection and didn't trigger recalculations. This restores expected
behaviour and makes species_params<-() the recommended setter for scripts.
The given_species_params<-() setter remains as an explicit alternative
that is particularly useful during interactive sessions, because it issues
warnings if you change a parameter whose effect is overridden by another
parameter that has already been given.
Each species parameter default now has a single home: the rate-setting
function that uses the parameter. validSpeciesParams() now only fills in
defaults for parameters that no single rate setter owns, namely w_max,
w_repro_max, w_mat, w_min, alpha, n, a, b and is_background.
The defaults for p, k, z_ext, d, E_ext, D_ext and
interaction_resource are supplied by setMetabolicRate(), setExtMort(),
setExtEncounter(), setExtDiffusion() and setInteraction() respectively,
where they were already being set. Built models are unaffected, because
setParams() calls all the rate-setting functions, but
validSpeciesParams() applied to a bare species parameter data frame now
returns fewer columns. See the default_parameters vignette.
The p argument of setMetabolicRate() is deprecated (#459). It never had
any effect on a MizerParams object: such an object always has a p column
already, and the argument was only ever used to fill in a missing one, so it
was silently ignored. Set the species parameter instead, with
species_params(params)$p <- value. The p argument of
newMultispeciesParams() is a different argument and is not affected.
The default for the metabolic exponent p is now n rather than 3/4 in
setMetabolicRate(), which is where the default now lives;
validSpeciesParams() no longer sets p. No model changes as a result.
Models built with newMultispeciesParams() take p from its own p
argument (default 0.7), which is injected into the species parameter table
before validation and is untouched by this change, so neither of these
defaults fires for them. The validSpeciesParams() default (p = n) only
ever applied when it was called directly on a bare species parameter data
frame, which now returns no p column, and it shadowed the
setMetabolicRate() default whenever both were in play.
Default values for the a (0.01) and b (3) species parameters (for the
weight-length relationship) are now saved in species_params instead of
being calculated internally by l2w() and w2l() only when needed.
The species_params data frame is now an S3 subclass of data.frame
(class = c("species_params", "data.frame")). It supports class-preserving
subsetting and subassignment S3 methods, making it safer to use and paving
the way for future auto-recalculations.
Columns accessed via $ on a species_params or gear_params object now
return named vectors, where the names are the species names (or "species,
gear" row names for gear_params). For example,
species_params(params)$w_mat now returns a named vector making it easier
to identify entries. The species vector is left unnamed.
When sel_func is set on a gear_params object, any argument columns
required by that selectivity function (other than w, species_params, and
...) are now automatically added as NA columns. This means, for example,
that setting gp$sel_func <- "sigmoid_length" immediately creates the l25
and l50 columns, ready to be filled in (#431).
Misspelled column names in the gear_params and species_params data
frames are now detected by fuzzy matching against the recognised parameter
names. A near miss such as sel_fun (instead of sel_func) triggers a
warning that suggests the intended name, rather than being silently ignored
(#442). Columns are only flagged, never renamed, so legitimate custom
columns are left untouched.
See the new vignette("upgrading") ("Upgrading mizer") for how to adapt
existing code to these changes.
An installed extension package is now recognised as a dispatching extension
from the S3 methods it registers for its marker class (e.g.
getEncounter.mizerMR), rather than only from a statically defined S4 marker
class. This lets extension packages omit the static
setClass("mizerFoo", contains = "MizerParams") and instead let mizer create
the marker class dynamically when the package is loaded, inserting it at the
correct place in the S4 hierarchy relative to any other extensions loaded in
the session. As a result, two independently developed extension packages
(for example mizerReef and mizerMR) can now be chained in either load order,
which a static sibling-of-MizerParams class prevented.
recordExtension() now prepends a genuinely new extension to the front of the
object's @extensions chain, keeping it ordered outermost-first to match
registerExtension(). Existing entries stay in place.
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. 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.
Added a callback parameter to project() to allow user-defined functions
to be called at each saved time step.
Mizer plots no longer produce the unhelpful warning "log-10 transformation introduced infinite values" when a logged axis contains zero values (#463).
setColours() and setLinetypes() now also update the linecolour and
linetype entries in species_params and given_species_params whenever a
name being set coincides with a species name, so that the choice persists
with the species rather than only living in the plotting slot.
library(mizer) now prints a one-line startup message the first time you
load a new mizer version, pointing you to news(package = "mizer"). It is
shown at most once per version and never interrupts a session more than
that.
compareParams() now checks that the number of size bins, species and gears
agree before comparing the array-valued slots. When they differ it reports the
mismatch instead of erroring while trying to compare arrays of incompatible
dimensions. It also compares the species-parameter tables by matching species
and parameters by name, so differing species no longer produce a long list of
per-column length mismatches, and duplicated messages are no longer repeated.
Error messages that referred to w_max as a species' "maximum size" now
correctly describe it as the upper size-grid boundary, consistent with w_max
being a purely computational parameter.
newSingleSpeciesParams(), newTraitParams() and newCommunityParams() now
document why they place the size-grid boundary at the maximum size
(w_max = w_repro_max): because they do not yet set up stochastic growth by
diffusion, no individual grows beyond w_repro_max, so no headroom above it is
needed. This will be revisited when the constructors gain a diffusion parameter
(#339).
print() on ArraySpeciesBySize, ArrayTimeBySpecies, ArrayResourceBySize,
ArrayTimeByResourceBySize and ArrayTimeBySpeciesBySize objects (as
returned by getEncounter(), getBiomass(), getResourceMort(), NResource(),
getFMort() and similar functions) now prints the array's actual values
instead of a per-species min/mean/max summary, truncating large arrays to
fit the console: species are shown as a leading subset, sizes as an evenly
log-spaced sample across the full size range, and time series as a
representative sample of time steps that always includes the first and
last, with a note reporting how much was omitted. A three-dimensional
ArrayTimeBySpeciesBySize object is previewed via its final time slice,
matching plot()'s existing default for that class. Use
as.data.frame() for full, untruncated access to the data.
plotYieldGear() now supports log_x, log_y, and log arguments,
aligning its arguments with plotYield().
The upper boundary condition of the size-spectrum solvers now holds the
abundance at zero above each species' maximum size w_max. Without diffusion
this is automatic and results are unchanged, but with predation diffusion
switched on it stops a small amount of density leaking to sizes above w_max.
See the "Numerical Details" vignette.
The "Getting started" guide now includes a self-contained "A worked example: the Celtic Sea" section that takes a real ecosystem from raw species parameters through building, finding the steady state, calibrating to observed biomasses and growth, checking against observed yields, setting the resilience to fishing, and projecting a fishing scenario whose sustainable-yield curve is interpreted — the whole mizer workflow in one place (#450).
The first example in the "Getting started" guide no longer prints the
parameter-default notes from newMultispeciesParams(), which were alarming to
new users out of context (#450).
Added three new cheatsheets: "Model Setup and Calibration" (building a model,
finding the steady state, calibrating to observed biomass/yield/growth, and
projecting), "Fishing" (gears, selectivity functions, catchability, and
effort), and "Changing Model Parameters" (the distinction between
given_species_params(), calculated_species_params() and species_params(),
when changing a species parameter updates a size-dependent rate versus freezing
it, and how gear_params() and the resource setters differ).
The analysis-and-plotting cheatsheet now covers the newer plotting functions
(plotCDF(), plotSpectra2(), plotSpectraRelative(), plotCDF2(),
plot2(), plotRelative(), animate()) and the ArrayResourceBySize
class, and corrects the interactive-plot advice for array objects to use
plotHover() (they have no ggplotly() method).
The reference index on the website now opens with an "Overview: the mizer workflow" section that frames the whole page as a five-stage pipeline (create → calibrate → tune dynamics → project → analyse) with links to the key function in each stage, so readers can see how the sections below fit together.
The reference index on the website now explains the differences between related
families of functions. It disambiguates the calibrate...()/match...() and
...Biomass/...Number calibration functions and the plot...() variants
(...2, ...Relative, ...ObservedVsModel), and maps mizer's mortality-rate
names onto the standard fisheries notation (M2, F, Z).
Version 3.1.0 builds on 3.0.0 with an experimental second-order accurate numerical scheme in size, additional higher-order time-stepping options, and a range of smaller improvements and bug fixes. Unless you opt in to the experimental scheme, results are unchanged from 3.0.0.
For an overview see the release announcement on the mizer blog.
mizer gains an optional, experimental second-order accurate finite-volume scheme
in the size variable w. It is controlled by a new second_order_w slot and is
switched off by default, so all default results are unchanged (the first-order
path is byte-identical to previous mizer). Enabling it shifts size-integrated
diagnostics and the resource spectrum by O(Δw) (more on coarse grids), so
calibrated models may need recalibrating. See ?second_order_w and the
"Numerical Details" vignette.
The second_order_w slot is a named list with a character entry flux
("upwind", "van_leer" or "centred") selecting the advective
reconstruction, and a logical entry bin_average selecting the bin-averaged
rate quadratures. A fully second-order scheme needs both. Use the new
second_order_w() / second_order_w<-() accessors to get and set them. The
setter accepts a single logical, which sets both entries — flux = "van_leer"
(a TVD reconstruction that keeps abundances non-negative) and
bin_average = TRUE — or a named vector for individual control, e.g.
second_order_w(params) <- c(flux = "centred") for the unlimited flux that is
genuinely second order even at extrema. Setting it re-runs setParams() to
rebuild the precomputed arrays. The default flux = "upwind",
bin_average = FALSE is the original first-order upwind scheme. Old objects
are upgraded automatically.
newMultispeciesParams(), newTraitParams(), newCommunityParams() and
newSingleSpeciesParams() gain a second_order_w argument (default FALSE,
accepting the same values as the setter) that builds the new model with the
scheme already selected. Under bin_average the resource and abundance power
laws are constructed bin-averaged from the start; the construction-time
steady-state solve always uses the robust upwind flux, and the chosen flux is
activated only for the returned model. (#379)
When bin_average is TRUE, every point-sampled power law and quadrature that
feeds the finite-volume update is replaced by its exact bin average over each
size bin (the bin straddling w_pp_cutoff receiving the partial average), so
the sinks, sources and capacities are consistent with the bin-integrated
encounter convolution (#374):
setExtMort()) and external
diffusion \eqn{D_{ext} w^{n+1}} (setExtDiffusion());setResource(); user-supplied full
vectors are left untouched), together with the matching initial resource
abundance \eqn{\kappa w^{-\lambda}} used both for the initial spectrum and
for the temporary prey spectra behind the default gamma/f0 and consumer
abundances;getEncounter(), getPredRate(), getPredMort(),
getResourceMort() and getDiffusion() become second order with no change
to the rate functions and no extra runtime cost. The predation-diffusion
integral uses a dedicated Fourier kernel held in the new ft_pred_kernel_d
slot, which carries the extra power of prey size (\eqn{w_p^2 dw_p}, the
\eqn{\beta^{3s}} Jacobian) that the diffusion integrand needs; in the
first-order scheme it equals ft_pred_kernel_e, so existing models are
byte-identical. (#384)calc_selectivity()), so a knife-edge gear gets the
exact fraction of the straddling bin above the knife edge and fishing
mortality is second order;mizerRDI()
(the full investment averaged together, not psi alone), making
density-independent reproduction second order.The advective growth flux uses the chosen flux reconstruction ("van_leer"
or "centred") for a second-order transport step. Combined with the
bin-averaged diffusion from getDiffusion(), the full growth-transport step is
second order. The diffusion coefficient is consumed from getDiffusion()
directly rather than being re-averaged by the transport routine.
Size-integrated and size-resolved diagnostics are placed and weighted
consistently with the finite-volume scheme when bin_average is TRUE:
getBiomass(), getSSB(), getYield(),
getYieldGear(), getDiet() and getTrophicLevel() use the trapezoidal
bin-average of the size weight (getN() is already exact);getPredMort(),
getFMort(), getMort(), getExtMort() and the reproductive investment
getERepro() — are reported at the geometric bin centre
\eqn{\sqrt{w_j w_{j+1}}}, the location where a bin average actually lives,
while point-valued quantities (encounter, growth) stay on the grid nodes.
The ArraySpeciesBySize / ArrayTimeBySpeciesBySize classes carry a
representation tag ("point" / "average") recording this. (#382)plotSpectra(), plotlySpectra(),
plotSpectraRelative() and animateSpectra() evaluate both the w^power
weight and the marker location at the geometric bin centre
\eqn{w^* = w \sqrt{\beta}}, placing each marker as a point on the continuous
N w^power curve instead of misplacing it at the bin edge (the error grows
with power, worst for the common power = 2 Sheldon plot). (#383)project() gains a new time-stepping option method = "tr_bdf2". This is an
L-stable, second-order TR-BDF2 scheme that retains the second-order accuracy of
method = "predictor_corrector" while damping the oscillations the
Crank-Nicolson corrector can show at large time steps. Like the other methods
it only requires tridiagonal solves. See the "Numerical Details" vignette.
Under the second-order methods ("predictor_corrector" and "tr_bdf2") the
resource is now advanced with midpoint resource mortality rather than the
start-of-step value, and the other components (set via setComponent()) now
also receive a corrector step with the midpoint rates. So the resource and the
other components are integrated to the same second-order accuracy in time as
the consumer spectra. The "euler" method and the steady states are unchanged.
Extension packages can now upgrade their own data in saved model objects
independently of the mizer version. The @extensions slot can record, for
each extension, the version of the extension package that the object conforms
to (write entries with the new recordExtension()). needs_upgrading() flags
an object when an extension's recorded version is missing or older than the
installed package version, and validParams() then calls the extension's own
upgrade() method (an S3 method on utils::upgrade(), registered with
@exportS3Method utils::upgrade). The core mizer upgrade is now itself the
upgrade.MizerParams() / upgrade.MizerSim() method. See the "Upgrading
objects across versions of your extension" section of
vignette("creating-extension-packages").
getDiet() gains a MizerSim method and plotDiet() for a MizerSim now
accepts a time_range argument, computing the diet from the simulated
abundances at the requested times rather than always using the initial
abundances. As for the other MizerSim plotting functions, time_range
defaults to the final saved time step. When a range spanning several saved
time steps is given, the consumption rates are averaged over the range and
then normalised to proportions (rather than averaging the per-step
proportions, which are normalised independently). (#357)
New getFluxGradient() function returns the flux divergence
\eqn{(J_{j+1} - J_j)/\Delta w_j} that appears as the transport term in the
discretised size-spectrum equation. The bin-boundary fluxes are obtained from
getFlux(), so the advective-flux scheme stored in the flux entry of the
second_order_w slot is used, with the largest size class closed by the
boundary condition \eqn{N_{K+1} = 0}. Like getFlux(), it has both
MizerParams and MizerSim methods, returning an ArraySpeciesBySize or
ArrayTimeBySpeciesBySize object respectively.
getDiffusion() now works with a custom predation kernel that depends on
predator and prey size separately rather than only on their ratio. As for
getEncounter(), when such a kernel has been set (with
setPredKernel(params, pred_kernel = ...)) the diffusion integral is
evaluated by direct summation over the full predation kernel instead of via
the FFT method, which assumes a ratio-only kernel. (#373)
getTrophicLevel() and getTrophicLevelBySpecies() now assign the resource a
size-dependent trophic level
\eqn{T_R(w) = \max(1, 1 + \log(w / w_R) / \log(\beta_R))} instead of treating
it as trophic level 0. The new w_R (average primary-producer size) and
beta_R (average resource predator/prey mass ratio) arguments control this.
Resource functions now return classed objects that support the same
convenient print(), summary(), plot() and as.data.frame() methods as
the consumer rate functions. getResourceMort(), initialNResource(),
finalNResource(), resource_rate(), resource_capacity() and
resource_level() return an ArrayResourceBySize object, and NResource()
returns an ArrayTimeByResourceBySize object, so you can now do e.g.
plot(getResourceMort(NS_params)) or plot(NResource(NS_sim)).
summary() of a MizerSim object now reports the fishing effort that was
actually used during the simulation rather than the model's initial_effort.
Gears whose effort varied over time show the mean effort, flagged with a note
giving the min-max range.
Added a new vignette explaining the calculation of default parameter values (#189).
w_inf is now the required maximum-size parameter
and is used as the default for w_repro_max (previously w_max) and w_mat.
w_max is now purely a computational boundary (the size grid and plot range)
and defaults to 1.5 * w_inf. w_repro_max is documented as the size at which
a typical mature individual invests all its energy into reproduction, not as a
hard ceiling on size. The default value of the external mortality parameter
z0 is now calculated from w_inf rather than w_max, so the purely
computational boundary w_max no longer influences any model parameter. For
backwards compatibility, if w_inf is not supplied it is taken from
w_repro_max or w_max, so existing models and scripts are unaffected;
however, new models built from the defaults may differ from 3.0.0.Fixed a bug in project() where the abundances of other components (set via
setComponent()) were advanced only once per saved time step instead of once
per dt time step. Their dynamics are now integrated with the same time step
as the consumer and resource spectra, so results no longer depend on t_save.
getGrowthCurves() no longer emits a cryptic
collapsing to unique 'x' values warning when a species' w_max lies exactly
on a grid point. (#413)
getRDI(), getRDD() and getFlux() on a MizerSim object now correctly use
the simulated time-varying effort instead of the initial effort. (#370)
plotCDF() / plotlyCDF() now plot each cumulative value on its bin's
upper edge w_k + dw_k, following the inclusive cumulative-sum convention
(the sum through bin k is the integral up to that bin's upper edge). This
corrects a long-standing one-bin location offset and applies in both the
default and the second-order schemes; under second-order bin-averaging the CDF
is then second-order accurate in its placement as well as its increments.
(#383)
plotYield() now uses sim2 = NULL instead of missing(sim2) to detect the
optional second simulation argument, so it works correctly with do.call().
This is backward-compatible.
distanceMaxRelRDI() now returns Inf instead of NaN when a previous RDI
is zero, so projectToSteady() no longer mistakes a NaN distance for
convergence.
This release brings new biological realism, improved numerics, a richer interactive analysis experience, and a composable extension framework. For an overview see the blog post pre-announcing the release.
The McKendrick-von Foerster equation now supports a diffusion term, allowing individual variability in growth to be modelled.
New getDiffusion() calculates the total diffusion rate D(w) (g²/year) for
each species, combining the predation-induced diffusion from the jump-growth
equation and any externally specified diffusion set via setExtDiffusion().
It has both MizerParams and MizerSim methods and returns an
ArraySpeciesBySize or ArrayTimeBySpeciesBySize object respectively,
consistent with the other rate-getter functions.
The external diffusion coefficient is held in a new ext_diffusion slot in
MizerParams. Use setExtDiffusion() / ext_diffusion() /
ext_diffusion<-() to set and retrieve it. The new species parameter D_ext
(default 0) sets the coefficient of an external diffusion power law;
setExtDiffusion() calculates the default array from species parameters when
no custom array is supplied, following the same pattern as
setExtEncounter().
MizerParams gains a use_predation_diffusion slot (logical, default
FALSE). When FALSE (the default), mizerDiffusion() omits the
predation-induced diffusion term, preserving the behaviour of previous mizer
versions. Set to TRUE via the new use_predation_diffusion() accessor to
enable the jump-growth diffusion term.
New getFlux() function calculates the flux of individuals entering each
size class, combining the advective flux from somatic growth and the
diffusive flux. It has a power argument, similar to that of plotSpectra(),
for multiplying the flux by a power of the weight; power = 1 gives the flux
of biomass.
getRequiredRDD() is exported. It calculates the recruitment rate needed
to maintain a given initial abundance, accounting for both growth and
diffusion.
steadySingleSpecies() correctly preserves the steady state under
project(), including when diffusion is non-zero.
The vignette cohort dynamics demonstrates the effect of diffusion in an example.
project(), projectToSteady() and steady() gain a method argument for
choosing the consumer density time-stepper. The default "euler" preserves
the existing semi-implicit update, while "predictor_corrector" uses a new
second-order predictor-corrector method. The accuracy of the two methods is
compared in the numerical details
vignette.
MizerSim objects now have a sim_params slot (a named list) that records
the projection parameters — currently method and dt — passed to
project() or projectToSteady(). The new getSimParams() accessor
retrieves this list. When project() is called on an existing MizerSim
object it defaults dt and method from the stored sim_params, with a
warning if the supplied values differ. Older objects are upgraded
automatically by validSim(), with sim_params set to an empty list.
project_n() and project_n_2(2) are new exported functions, factored out of
project_simple(), that projects the abundance spectrum forward in time with
the different methods.
New ArraySpeciesBySize S3 class for the species × size arrays returned by
many mizer functions. An ArraySpeciesBySize object behaves like a regular
matrix for arithmetic and subsetting but carries a human-readable
value_name and units attribute and provides enhanced print(),
summary(), plot(), and as.data.frame() methods. The plot() method
accepts log_y, wlim, and ylim arguments for controlling the y-axis
scale and limits.
New ArrayTimeBySpecies S3 class for the time × species arrays returned by
getBiomass(), getSSB(), getN(), and getYield() when called on a
MizerSim object. Like ArraySpeciesBySize, it carries value_name and
units attributes and provides enhanced print(), summary(), plot(),
and as.data.frame() methods. The plot() method accepts log and ylim
arguments.
New ArrayTimeBySpeciesBySize S3 class for the time × species × size arrays.
The N() accessor on a MizerSim object now returns an
ArrayTimeBySpeciesBySize object. Many rate-getter functions —
getEGrowth(), getEReproAndGrowth(), getPredMort(), getFMort(),
getMort(), getFeedingLevel(), getEncounter(), getPredRate(),
getRDI(), getRDD() — now also accept a MizerSim object and return an
ArrayTimeBySpeciesBySize. An animate() method allows interactive
playback. Subsetting an ArrayTimeBySpeciesBySize object returns an
ArraySpeciesBySize object when a single time is selected, and an
ArrayTimeBySpecies object when a single size is selected.
New plot2() generic with methods for comparing two compatible mizer array
objects in one plot, with species or group shown by colour and model by
linetype. The plotSpectra2() helper has moved from mizerExperimental into
mizer for comparing two abundance spectra.
New plotRelative() generic with methods for plotting the symmetric relative
difference between two compatible mizer array objects. The
plotSpectraRelative() and plotlySpectraRelative() helpers have moved from
mizerExperimental into mizer.
New plotCDF() and plotCDF2() generics for plotting cumulative abundance
or biomass distributions from MizerParams and MizerSim objects, together
with plotlyCDF() and plotlyCDF2() wrappers.
New plotHover() generic with methods for ArraySpeciesBySize,
ArrayTimeBySpecies, ArrayTimeBySpeciesBySize, and mizer_plot converts
mizer plots into hover-enabled plotly figures.
New addPlot() generic with methods for adding ArraySpeciesBySize and
ArrayTimeBySpecies values as extra lines on an existing compatible ggplot.
The animate() methods produces animated plots showing the time evolution
during a simulation. It can take aMizerSim and ArrayTimeBySpeciesBySize
argument and supports axis range settings (xlim, ylim), timing controls,
interpolation options, arguments log_x log_y and log to control which
axis is log-transformed, and total and background arguments, consistent
with plotSpectra().
Plotting functions now consistently expose log_x, log_y and log
arguments. In all cases, when supplied, log overrides log_x and log_y.
plotBiomass() and plotYield() keep support for logical log values for
backward compatibility.
Time-filtering is now consistent across all time-series plot functions via a
new tlim parameter (analogous to wlim and ylim): a length-two numeric
vector c(start, end) that restricts the plotted time window. plotYield(),
plotYieldGear(), and animate() gain this parameter for the first time.
plotBiomass() and animate.MizerSim() now use tlim in place of the
former start_time/end_time and time_range parameters respectively;
the old parameters are deprecated and will be removed in a future release.
Size-based plots now accept size_axis = "l" to show length in cm on the
size axis instead of weight in grams, using the species' allometric
weight-length relationship.
Size-based plots with a size_axis argument now accept llim, the
length-axis equivalent of wlim, for filtering and limiting plots when
size_axis = "l".
A shift in interpretation of a MizerParams object from just a specification of the model to a representation of its state, consisting of both model parameters and current values of the state variables (the abundances).
getParams(sim, time_range, geometric_mean = FALSE) now extracts the
ecosystem state from a MizerSim object at a particular time or averaged
over a time range. When no time_range is given, the state at the final time
step is extracted. New finalParams(sim) and initialParams(sim) return the
states at the initial and final times of a simulation respectively.
Once a state has been extracted from a simulation, it can be analysed by all
the existing mizer functions. For that purpose the indicator functions
getProportionOfLargeFish(), getMeanWeight(), getMeanMaxWeight(), and
getCommunitySlope() now also accept a MizerParams object and return a
single value (or named vector for getMeanMaxWeight() with
measure = "both") calculated from that state. Closes #262.
setInitialValues() is deprecated. Replace
setInitialValues(params, sim) with finalParams(sim) (or
getParams(sim, time_range, geometric_mean) when averaging over a time
range).
Many functions are now S3 generics with methods for
MizerParams or MizerSim objects, and users can define their own subclass
methods to modify mizer behaviour (#330).
New composable extension chain infrastructure: registerExtensions(),
getRegisteredExtensions(), coerceToExtensionClass(),
clearExtensionChain(), and registerExtension(). Extension classes are S3
marker classes; MizerSim derives its extension chain from
sim@params@extensions. Extensions that do not provide a marker class remain
metadata-only and do not trigger the S3 projection-rate dispatch path.
S3 projection hooks have been added for all standard mizer rate functions.
Extension-aware projections dispatch through projectRates(),
projectEncounter(), projectFeedingLevel(), projectEReproAndGrowth(),
projectERepro(), projectEGrowth(), projectDiffusion(),
projectPredRate(), projectPredMort(), projectFMort(), projectMort(),
projectRDI(), projectRDD(), and projectResourceMort() — while models
without extensions continue to use the pre-resolved mizerRates() pipeline
directly, with no per-step overhead.
The MizerSim accessors getParams(),
validSim(), N(), NResource(), finalN(), finalNResource(),
idxFinalT(), getTimes(), getEffort(), and are now
registered as S3 generics with MizerSim methods, making extension-specific
methods possible. validParams() is also now an S3 generic.
saveParams() now serialises extension objects as plain MizerParams
objects while preserving their extension chain, and readParams() restores
the appropriate extension class. New saveSim() and readSim() helpers
provide the same lifecycle for MizerSim objects.
Extension installation support now integrates pak for managing missing or
outdated extension packages.
New vignette
Extending mizer
documents when to use setRateFunction(), setComponent(), and
customFunction(), summarises required function signatures and return shapes,
and gives worked examples for both a custom encounter function and an added
ecosystem component. A companion vignette
Using extension packages
is aimed at users of extension packages, and
Creating a mizer extension package
guides extension authors through setting up a new extension package.
setRateFunction() now validates the registered function by calling it with
test inputs and checking that the return value has the correct dimensions,
catching mismatched custom rate functions at registration time rather than
during a simulation run. Closes #167.
setComponent() now accepts optional colour and linetype arguments and
applies them via setColours() and setLinetypes() so added components can
be styled directly in plots.
The plot() and summary() methods for MizerParams, MizerSim, and the
mizer array classes are now registered as S3 methods rather than S4 methods,
so plot() and summary() remain plain S3 generics when mizer is loaded,
avoiding interference with S4 method dispatch for other packages.
See the model description vignette for the mathematical details.
New species parameters z_ext (default 0) and d (default n - 1) add an
optional power-law term to the external mortality: mu_ext(w) = z0 + z_ext * w^d. When z_ext is zero (the default) the behaviour is unchanged. Closes
#329.
New species parameter E_ext (default 0) sets the coefficient of the
external encounter rate power law. setExtEncounter() now calculates the
default external encounter rate as E_ext * w^n when no custom array is
supplied, matching the pattern of setMaxIntakeRate(). A reset argument is
also added to setExtEncounter() to force recalculation from species
parameters.
New species parameter D_ext (default 0) sets the coefficient of the
external diffusion rate power law. setExtDiffusion() calculates the default
array from species parameters when no custom array is supplied.
The MizerSim methods of the rate-getter functions (getEncounter(),
getFeedingLevel(), getEReproAndGrowth(), getERepro(), getEGrowth(),
getDiffusion(), getPredRate(), getPredMort(), getMort(), getFMort(),
getFMortGear(), getRDI(), getRDD() and getFlux()) are now much faster.
They resolve the rate functions and validate the parameters once and then, at
each saved time step, calculate only the rates needed (and their
dependencies) rather than re-resolving and recomputing the whole rate chain.
The speed-up grows with the depth of the rate chain, e.g. roughly 100× for
getRDI() and getFlux() on a 50-step simulation.
New scaleRates(params, factor) function that rescales all rates in a model
by a given factor. This is equivalent to a time rescaling: it speeds up or
slows down all dynamics without affecting the steady state. All rate slots
(search_vol, intake_max, metab, mu_b, ext_encounter,
ext_diffusion, catchability, rr_pp) and their associated species
parameters (gamma, h, ks, k, z0, z_ext, z0pre, E_ext,
D_ext, R_max) are rescaled consistently.
New getTrophicLevel() function returns a matrix (species × size) with the
trophic level of individuals at each size, accounting for ontogenetic diet
shifts by integrating the consumption-weighted average prey trophic level
over the individual's growth trajectory. New getTrophicLevelBySpecies()
returns the consumption-rate-weighted mean trophic level per species. Both
functions accept MizerParams and MizerSim objects. Closes #307.
New expandSizeGrid() function (an S3 generic) expands the size grid of a
MizerParams object to a new minimum and/or maximum size while preserving
all existing species data. Both addSpecies() and expandSizeGrid() now
preserve the MizerParams subclass. upgradeParams() also preserves
MizerParams subclasses and their extra slots.
compareParams() output is now printed in a human-readable format, with each
difference as its own block separated by blank lines. When array slots differ,
the max absolute difference is shown per species. When slots differ only in
their comment attributes, both comments are displayed. Closes #205.
summary() for MizerParams and MizerSim now displays metadata from the
@metadata slot, including title, description, authors, DOI, URL, mizer
version, and creation/modification timestamps (when set). Closes #294.
New str() methods for MizerParams and MizerSim objects, and the mizer
array classes (ArraySpeciesBySize, ArrayTimeBySpecies, and
ArrayTimeBySpeciesBySize), showing a clean, compact overview of their
structures without dumping large amounts of internal data.
A new steady argument to addSpecies() controls whether steady() is
called after adding the new species.
constantEggRDI() now accounts for diffusion across the egg-size boundary,
including when project() uses the "predictor-corrector" method.
setRateFunction() now validates custom RDI functions with the same
diffusion argument that they receive during projection.
Growth is now forced to always be non-negative, preventing unphysical shrinkage. No warning is issued when growth stops at or after maturity size.
Added info_level argument to projectToSteady(), steady(), setParams(),
newCommunityParams(), newTraitParams(), matchBiomasses(),
matchNumbers(), matchYields() and addSpecies()to control the
verbosity of information messages, consistent with newMultispeciesParams().
Set info_level = 0 to suppress all messages. Closes #290.
t_max and t_save arguments in project() are now respected even when an
effort array is supplied. When t_max is provided, the simulation extends
beyond the times in the effort array using the last known effort values. When
t_save is provided, it controls the save frequency with effort values
interpolated as needed (#231).
getBiomass() now has a use_cutoff argument to restrict the biomass
calculation to sizes above the biomass_cutoff species parameter.
plotBiomass() and plotlyBiomass() also gain this argument.
setResource() now allows resource_level = 1. When balancing would
otherwise divide by zero because the resource capacity equals the current
resource abundance at positive consumption, the capacity is increased
slightly with a warning instead of failing early.
project() now warns when t_max is not a multiple of t_save and ensures
that the state at t_max is always saved, even if the final save interval is
shorter than t_save. (#341)
New function psi() returns an ArraySpeciesBySize with the population-level
reproductive proportion.
age_mat_vB() is now exported.
New Cheatsheet: Analysis and Plotting vignette provides a quick reference for all functions that access simulation arrays, compute summaries, calculate indicators, and create plots. Closes #176.
getFMort() on a MizerSim object was silently dropping the component
names from n_other when passing it to the rate function and its
dependencies (getEGrowth(), getPredMort()), causing failures whenever
rate functions accessed n_other by name (e.g. n_other[["resource"]]).
The implementation has been refactored to use the same plyr::aaply pattern
as getFeedingLevel() and getPredMort().
getFMort.MizerSim() was not passing the time argument t to user-defined
fishing mortality functions.
plotSpectra() was incorrectly forcing the y-axis lower limit to 1e-20
(instead of auto-scaling to the data) and was using min(params@w) / 100
as the default lower w-axis limit even when resource = FALSE, where
min(params@w) is more appropriate.
upgradeParams() was silently dropping some slots (e.g. resource_dynamics)
and was not preserving MizerParams subclasses and their extra slots when
upgrading older objects.
getMeanMaxWeight() now correctly applies the species selector to the
denominator.
plotDataFrame() now correctly applies custom log-scale x breaks.
get_size_range_array() no longer gives an error when no size brackets are
selected.
The default ratio argument in plotBiomassObservedVsModel() and
plotlyBiomassObservedVsModel() is now consistently FALSE for all object
types. Calls that relied on the previous default ratio plot should now set
ratio = TRUE.
The first argument of plotBiomass(), plotYield(), plotYieldGear() and
their MizerSim methods and plotly* wrappers has been renamed from sim
to object for consistency with other plot generics. Calls using
sim = ... as a named argument must be updated to object = ....
The names of the dimnames of the arrays returned by getMort(),
getPredRate() are now sp and w to be in line with other
functions like getFMort().
Functions that return arrays of the form (species x size), (time x species)
or (time x species x size) now return them with extra attributes and an S3
class of ArraySpeciesBySize, ArrayTimeBySpecies or
ArrayTimeBySpeciesBySize. While this does not change their old behaviour,
the differences will be flagged by functions like is.identical().
Because plotDataFrame() now correctly applies custom log-scale x breaks,
the axis ticks in plots that use this function have changed.
plotDiet() no longer accepts a time_range argument.
renameGear() to rename gears in a MizerParams object, similar
to renameSpecies().addSpecies() now proceeds with a warning instead of an error when species
growth stops after maturity (#315).matchBiomasses() and matchNumbers() now provide more informative error
messages.plotDiet() now restricts the plot to size ranges with meaningful biomass
density (#317).wlim and ylim arguments in plotting functions now set the actual axis
limits instead of just zooming (#320).plotlyFeedingLevel() is improved when critical feeding level
is included.species and gears columns are now never factors, so no longer need to
call as.character() so often.validParams() also calls validGearParams().validParams() checks that w_min is valid for all species and increases it
if necessary.validSpeciesParams() now also sets default for p to be equal to n.species_params<-() and given_species_params<-() now check that species
names match.params argument in l2w() and w2l() has been renamed to species_params
to follow mizer's convention that params refers to a MizerParams object.animateSpectra() now uses consistent colours and preserves colour identity
across frames (#321).getReproductionProportion() no longer returns incorrect proportions > 1 (#299)setResource() now correctly applies the w_pp_cutoff parameter to the
carrying capacity and initial resource abundance when changed without
providing resource_capacity(#306).given_species_params() no longer makes unwanted changes to the species
parameters.steadySingleSpecies() no longer changes time_modified.A patch update so that users who had changed w_max manually in their model
will not get unhelpful error messages when trying to use their model in the
new version. General checking of parameters is made more robust. In particular
validSpeciesParams() has extra checks on consistency of species parametersvalidParams() checks that rate arrays contain finite numeric valuesvalidSim() checks that simulation results are finite and truncates the
simulation if they are not.newCommunityParams() to set up resource parameters
differently since version 2.4.0 (#293)addSpecies() now correctly preserves all species_params of the existing
model.addSpecies() no longer requires new species to grow to maximum size, only
maturity size is required.validGivenSpeciesParams() validates the given species parameters without
adding defaults and validSpeciesParams() validates and returns a completed
species parameter dataframe.w_repro_max giving the size at which a species
invests 100% of its energy into reproduction. Set to w_max by default.removeSpecies() now also removes species parameters that are not set for
any of the remaining species.w_max now also correctly updates ft_mask (#296).compareParams() now also spells out differences in given species parameters.getDiet() now also includes the contribution of the external encounter rate
to the diet.setPredKernel() now throws an error if some of the required predation kernel
parameters are NA.plotYieldGear() one can select a subset of gears with new gears
argument.valid_gears_arg() to check the gears argument in
functions that take a gears argument.plotGrowthCurves().steadySingleSpecies() no longer requires species to grow to w_max.matchGrowth() now also rescales the external encounter rate.setExtEncounter() no longer resets the external encounter rate to zero when
called without the ext_encounter argument.plotBiomassObservedVsModel() now plots the ratio of modelled
to observed biomass as default (ratio = T), as this is more useful visually
to see how far off modelled biomass is from observed biomass.time_modified field is now updated correctly by steadySingleSpecies(),
setColours() and setLinetypes().matchYields() and calibrateYield().This is a patch release made necessary by a change in CRAN's requirement regarding the vignettes. It also includes a bug fix:
project() and projectToSteady(..., return_sim = TRUE) now correctly
returns also the other components of the MizerSim object stored in n_other.
#285This release introduces a change that requires you to upgrade your old
MizerParams and MizerSim objects with upgradeParams() or upgradeSim().
Now the model can include an external encounter rate that represents the
rate at which a predator encounters food that is not explicitly modelled.
This encounter rate is set with setExtEncounter() or ext_encounter<-()
and can be read with getExtEncounter() or ext_encounter(). So this is
similar to how external mortality is handled.
You can now use given_species_params() to see the species parameter
values that you have explicitly specified and calculated_species_params()
to see the species parameter values that mizer has calculated automatically or
set to defaults. You can continue to use species_params() to get all
species parameters, irrespective of whether they were given or calculated.
You can still set parameter values with species_params<-(), but you can also
use the stronger given_species_params<-() which not only sets the values you
give but also triggers a re-calculation of the calculated species parameters.
Using given_species_params<-() is therefore usually the better option.
There is now a three-part mizer course at https://mizer.course.sizespectrum.org with each part consisting of several tutorials, including code and exercises:
Part 1: Understand
You will gain an understanding of size spectra and their dynamics by exploring simple example systems hands-on with mizer.
Part 2: Build
You will build your own multi-species mizer model for the Celtic sea, following our example. You can also create a model for your own area of interest.
Part 3: Use
You will explore the effects of changes in fishing and changes in resource dynamics on the fish community and the fisheries yield. You will run your own model scenarios.
effort argument in project() is improved.h of the maximum intake rate is not given, it is now
again given a default value. #282matchGrowth() no longer gives an error when there is no w_inf column. #279This minor release was made necessary to keep mizer on CRAN after a unit test failed on macOS 13.3 with version 14.3 of the CLT toolchain.
This release introduces a change that requires you to upgrade your old
MizerParams and MizerSim objects with upgradeParams() or upgradeSim().
For an explanation see blog post at https://blog.mizer.sizespectrum.org/posts/2022-11-30-dont-use-von-bertalanffy-growth-parameters/
The species parameter that specifies the size at which also the largest fish stop
growing is renamed from w_inf to w_max. The parameter w_inf is now
reserved for the von Bertalanffy asymptotic size parameter. If you upgrade
your existing MizerParams object with upgradeParams() the w_inf column is
copied over to the w_max column automatically, but you may want to change
the values yourself if they do not currently reflect the maximum size of the
species. Otherwise the size distributions predicted by mizer will not match
observations.
The resource parameters kappa and lambda are now used to set the abundance
of the resource in the steady state rather than the carrying capacity, because
the latter is not observable.
While tuning the steady state using the steady() function the resource
abundance is now being kept fixed at the chosen value. Then the resource
dynamics can be switched on later with setResource() without changing the
steady state. At that stage you only choose either the resource intrinsic
growth rate or the resource carrying capacity and the other is determined by
setResource() in such a way that the resource replenishes at the same rate at
which it is consumed. If you want to keep the old behaviour and switch off this
automatic balancing you have to add the balance = FALSE argument when calling
setResource().
You can also choose between semichemostat dynamics resource_semichemostat()
or logistic dynamics resource_logistic() or you can write your own function
implementing more sophisticated resource dynamics.
The setParams() function no longer includes the arguments for setting the
resource parameters. Instead you set these separately with setResource().
As explained in the blog post at https://blog.mizer.sizespectrum.org/posts/2022-11-30-dont-use-von-bertalanffy-growth-parameters/,
the von Bertalanffy curves fitted to size-at-age
data are not suitable for estimating the size-dependent growth rates in mizer.
It is therefore now recommended that instead of von Bertalanffy parameters you
supply the age at maturity in the age_mat column of the species parameter
data frame. This is then used by mizer to calculate a default for the
maximum intake rate parameter h if you do not supply this.
In the past, whenever you changed any model parameters, you needed to re-tune
other parameters to keep the growth rates in line with observations. There is
now a new function matchGrowth() that automatically scales the search volume,
the maximum consumption rate and the metabolic rate all by the same factor in
order to achieve a growth rate that allows individuals to reach their maturity
size by their maturity age while keeping the feeding level and the critical
feeding level unchanged. This function does not however preserve the steady
state, so you will need to also call steady() after matching the growth rates.
steadySingleSpecies() that only balances the size-spectrum
dynamics while ignoring multi-species effects. In other words, it calculates
the steady-state size spectrum of each species as it would be if the abundance
of prey and predators could be kept constant at their current values.plotGrowthCurves() can now superimpose a scatterplot of size-at-age data
if you supply this via the new size_at_age argument.calibrateNumber() and matchNumbers() that are like
calibrateBiomass() and matchBiomasses() but work with observed numbers
instead of observed biomasses.age_mat() to calculate the age at maturity from the growth
rate and the size at maturity.project() now also works when called with a MizerSim object with additional
components.steady() now preserves the RDD function in the MizerParams object rather
than always setting it to "BevertonHoltRDD".plotSpectra() or setInitialValues()
the user can now choose geometric averaging with geometric_mean = TRUE.w_mat25 species parameter is no longer filled in automatically if it is
not supplied. This makes it easier to change w_mat without having to change
w_mat25 at the same time.compareParams() now also checks the validity of its second argument.newTraitParams().info_level argument in newMultispeciesParams() now
leads to more messages.validSpeciesParams(). #136l2w() and w2l() for converting between length-based
and weight-based species parameters. #258style argument to plotDataFrame() to facilitate producing area plots.wrap_scale argument to plotDataFrame() to control scaling of axes in
faceted plots.plotDiet() can now show diets of several predator species in a faceted
plot. #267size to linewidth aesthetic to avoid warnings in new version
of ggplot2.compareParams() says so clearly.getReproductionLevel() works as long as R_max is set. #252gear_params().defaults_edition(). #186get_gamma_default() ensures a feeding level of f0
for larvae also if interaction_resource is not equal to 1. #238NS_paramsset_species_default(). #268w_inf no longer leads to error in plyr::aaply(). #269url field in metadata.plotYieldObservedVsModel() and
plotBiomassObservedVsModel(). #266plotDiet() restored after change to ggplot2
package. #265mizerPredRate() between the argument t and the
function base::t().upgradeSim() with a MizerSim object.getRequiredRDD()renameSpecies() no longer fails when linecolour and linetype are of
different lengths.setInitialValues() can now average over a time_range.getSSB(), getBiomass(), getN(), getYieldGear() and getYield()
can now be called with a MizerParams object as well as with a MizerSim
object. (#200)plotBiomassObservedVsModel() and plotYieldObservedVsModel()
contributed by @SamikDatta., together with their plotly counterparts.calibrateBiomass(), calibrateYield() to set the model scale to agree
with total observed biomass or total observed yield. Uses the new
scaleModel().matchBiomasses() and matchYields() will try to adjust the abundances
of the species to produce the observed biomasses or yields.
See blog post at https://blog.mizer.sizespectrum.org/posts/2021-08-20-a-5-step-recipe-for-tuning-the-model-steady-state/ .params <- setReproduction(params, maturity = my_maturity) one
can simply use maturity(params) <- my_maturity. These are documented
together with the setter functions. #213setMetadata() to add information to a MizerParams object describing
the model, for example a title, a description, the author or list of
authors, a url and a doi. This will be particularly useful for sharing your
models with otherssaveParams() for saving a MizerParams object to a file and
readParams() for reading it back in. The resulting files can be shared
with others who want to run your model.getMetadata(). This helps you keep track of
different versions of your model.steady() now has a preserve argument with possible values erepro,
R_max or reproduction_level to specify which quantity to preserve.
This means that one can continue to use steady() also
once one has started to tune the density dependence in reproduction. #208gear_params are now set to "species, gear", so that one
can access individual entries with for example
gear_params(NS_params)["Cod, Otter", "catchability"]. #212z0 argument of setExtMort() has been deprecated in favour of
ext_mort in order to avoid confusion with the species parameter z0.setColours() and setLinetypes() now issue warnings when invalid values
are given and ignores NAs.comment arguments to the setter functions have been
removed. #214reset argument which, when set to TRUE
will recalculate the rates from the species_, gear_ and resource_params even
when custom values had been set. #214species argument to various functions, which is checked with
valid_species_arg(), now does not throw an error even when there is no
valid species included. Only a warning is issued. That means that for
example plotSpectra(NS_params, species = list(), total = TRUE) is now
allowed.getComponent() from the mizer extension mechanism now returns NULL when
asked for a non-existent component instead of giving an error. This gives
an easy way to check for the existence of a component.inter for the North Sea model now has the
alternative name NS_interaction, with the old name deprecated.addSpecies() are now by default given a reproduction
level of 1/4 instead of 0, because at the low densities at which they are
introduced there would otherwise not be enough density dependence to
stabilise them.min_w, max_w, min_l and max_l used in some
summary functions and processed by get_size_range_array() accept vector
values setting different limits for different species.resource_rate and the
resource_capacity as arguments, which makes it easier to use them in
extension packages.newMultispeciesParams(), #199linecolour or linetype in the species parameters now actually
changes the linecolours and linetypes as intended.getGrowthCurves() and plotted with
plotGrowthCurves() are now correct, and no longer extend above the
asymptotic size.plotGrowthCurves() with species_panel = TRUE now respects the species
argument to only show growth curves for selected species, it works with
a MizerParams object as well as a MizerSim object, and it shows the panels
in the correct order. #201setBevertonHolt() function has been expanded with more arguments. It
allows you to change the density dependence in reproduction without changing
the steady state of your model.getReproductionLevel() function tells you at what proportion of
their maximum reproduction rate the species are operating in your model.NS_sim which holds
a simulation of the North Sea model.plotDataFrame() allows easier creation of plots.setInitialValues() correctly preserves the gear names on the
initial effort. Thanks to Axel Rossberg.getFMort() correctly passes the t argument on to any custom fishing
mortality function you may have written.mizerPredMort() suggested by Axel Rossberg.plotSpectra() now only shows those species in the legend that are
actually contained in the plot.initialNOther() also works with MizerSim object.projectToSteady() is called with effort, this effort is now also
stored in the initial_effort slot.summary() which is now using sprintf() for better
formatting and also gives the initial_effort.project_simple() have been given convenient defaults.projectToSteady().newSingleSpeciesParams() for creating a single species in a
power-law background.animateSpectra() creating an animated plot of a simulation.addSpecies(), removeSpecies() and renameSpecies().setComponent() can
now take any form, they no longer have to be a named list.return_data in the plot's functions allows to return the
data frame used for the ggplot instead of the plot.steady() no longer switches off the Beverton-Holt density dependence.
You can do this manually with setBevertonHolt() with R_factor = Inf.getYield() now also works with density-dependent fishing mortality.
Thanks to James Roger for discovering the problem.gamma argument now is no longer ignored in newTraitParams() but
correctly overrides the f0 argument. #188getFMort() again works correctly when called with a MizerSim object.resource_semichemostat() no longer fail when at some sizes both the
resource growth rate and the resource mortality rate are both zero.no_w in newTraitParams() is now always an integer.w_min_idx should point to the start of the size bin containing
the egg size w_min".valid_species_arg() is called with species = NULL and there are no
background species then it returns NULL.getDiet() and plotDiet().projectToSteady() to run the full dynamics to steady state.distanceSSLogN() and distanceMaxRelRDI() to measure
distance between two states.compareParams() to compare two MizerParams objects.constantEggRDI() to allow keeping egg densities fixed.customFunction() to allow users to overwrite mizer functions.plotGrowthCurves as a panel of
species with their respective Von Bertalanffy curvesplotPredMort() and plotFMort will stop
displaying mortality values past the species' asymptotic size. The argumentall.sizes allows you to continue to show these values.getFMort() now passes time argument correctly. #181validEffortArray() now sets the dimnames correctly. #173lifecycle package to indicate status of some functions and arguments as
'experimental' or 'deprecated'.setFishing(). #172species argument in mizer functions, via the new
valid_species_arg() function. #170setInitialValues()min_w_pp is larger than min_wt passed to dynamics functions has been corrected.setReproduction() now correctly sets the the total proportion psi when the
maturity proportion is changed.project() has been simplified. They are now either
set by the arguments t_start, t_max and t_save or by the dimension names
of the effort array.setRmax() to setBevertonHolt() and allow it to work on an
arbitrary MizerParams object. The old name setRmax() is still available as
alias.mizerFMort() now can also use the abundances and the rates e_growth and
pred_mort. This is useful for example for implementing balanced harvesting.gear_params is allowed to have zero rows.validGearParams() the species name is used as gear name in case
gear_name is NA.validGearParams() ensures that all required arguments of the
selectivity function are supplied and checks validity of species names.species_params()<- suppresses warnings.steady() fails because RDI is zero it gives a meaningful error message.newCommunityParams() now protects its zero investment in reproduction with
a comment.valid_species_arg() checks validity of species
selection arguments.upgradeParams() can now also upgrade old MizerParams objects that do not
have a consistent initial_effort.validParams() validates a MizerParams object and
automatically upgrades it with upgradeParams() if necessary.project() or steady(), #163.getRates() to calculates all rates and collects them in a list.steady() with return_sim = TRUE now creates the MizerSim object the same way
as project(), namely with the original values in the first time slot.species_params(), gear_params() and
resource_params().project().getRates() functions by removing the arguments that passed in
other rates. Instead the required rates are now always calculated within
these functions.validGearParams() handle NAs more gracefully and check that there are
no duplicates.upgradeParams() works also on params objects that were created with a
development version of mizer.upgradeParams() does a better job at
guessing the value for w_pp_cutoff.getFeedingLevel(), getPredMort(), setInitialValues() and steady() now
work also when model has extra components.plotFeedinglevel() when called with include_critical = TRUE, see #162.species_params is a tibble.params(), effort() and times() to getParams(),
getEffort() and getTimes() to avoid conflicts.getDiet() also takes into account possible contributions by user-defined
other components.project() is called with t_max < t_save then t_save is automatically
reduced so that the result at t_max will get saved.project().project_simple() that projects a given number of
time steps. This might be useful to extension writers.... argument to project() is passed on to the dynamics and rate
functions.steady() runs faster by using project_simple().summary() now also works with non-default feeding kernels. #159validSpeciesParams() no longer fails when w_mat25 is not specified. #160setInitialValues() also works in a model with only a single species. #161resource_params()<- now works and has unit tests.Some inconsistencies in the choice of names for parameters was removed by renaming
interaction_p -> interaction_resourcer_resource -> resource_rateK_resource -> resource_capacityother_params()<- and other_params() for setting and
getting other parameters, for example to be used in user-defined rate
functions.setInitialValues() also sets initial_effort. #157This is a major new release with many new features, an internal refactoring of the code and a new extension mechanism.
Nevertheless this version of mizer is almost fully backwards compatible with version 1.0 with the exception of bug fixes and the following breaking changes:
print_it argument from plot functions.plotFeedingLevel() now only plots the values within the size range of each
species. If for some reason you want the old plots that show a feeding level
also for sizes that the fish can never have, you need to supply an argument
all.sizes = TRUE.RDD argument in setReproduction().sex_ratio argument has been removed from getRDI() and getRDD().set_scaling_model() function has been removed because such models can
now be set up with newTraitParams() with the options perfect_scaling = TRUE
and egg_size_scaling = TRUE.display_frames(), addSpecies(), setBackground() and
retuneAbundance() have been removed to the "mizerExperimental" package
(https://sizespectrum.org/mizerExperimental/)project() a progress bar is displayed by default. You can
turn this off with the option `progress_bar = FALSE.The new functions
newCommunityParams()newTraitParams()newMultispeciesParams()replace the old functions set_community_model(), set_trait_model() and
MizerParams(), which are now deprecated. The new functions choose better
default values, in particular for metabolic rate and maximum intake rate.
After setting up a mizer model, it is possible to change specific model parameters with the new functions
species_params<-()resource_params<-()gear_params<-()setPredKernel()setSearchVolume()setInteraction()setMaxIntakeRate()setMetabolicRate()setExtMort()setReproduction()setFishing()setResource()The new function setParams() is a wrapper for all of the above functions
and is also used when setting up a new model with newMultispeciesParams().
(#51)
The documentation for these functions serves to explain the details of the mizer model.
Along with these setter functions there are accessor functions for getting the
parameter arrays: getPredKernel(), getSearchVolume(),
getInteraction(), getMaxIntakeRate(), getMetabolicRate(),
getExtMort(), getMaturityProportion(), getReproductionProportion(),
getCatchability(), getSelectivity(), getResourceRate(),
getResourceCapacity(), getResourceParams(), getResourceDynamics(),
setRmax().The MizerParams object now also contains the initial values for the size
spectra. This is particularly useful if the model has been tuned to produce
the observed steady state. The new function steady() finds a steady state
for a model and sets it as the initial value. The initial values can be
accessed and changed via functions initialN() and initialNResource(). The
initial values can be set to the final values of a previous simulation with
setInitialValues().
The MizerParams object now has a slot initial_effort that specifies the
initial fishing effort to which the steady state has been calibrated.
Mizer now has an extension mechanism that allows other R packages to be
written to generalise the mizer model. See setRateFunction() and
setComponent(). This mechanism is still experimental and may change as we
gain experience in writing extensions for mizer.
plotlyBiomass() as the
plotly version of plotBiomass(), and so on.plotGrowthCurves() plots growth curves and compares them to the von
Bertalanffy growth curve.plotDiet() plots the diet composition as a function of predator size.highlight argument to all plot functions that display curves for
multiple species. Displays highlighted species with wider lines.plot() method for MizerParams object to plot the initial state.include_critical in plotFeedingLevel() allows to show also
the critical feeding level.wlim argument to plotSpectra() in analogy to the existing ylim
argument to limit the w range in the plot.setColours().solid but this can be changed via the
setLinetypes() function.box_pred_kernel() implements a box-shaped kernel as an alternative to
the default lognormal_pred_kernel().power_law_pred_kernel() implements a power-law kernel with sigmoidal
cutoffs at both ends. This is suitable for filter feeders.setPredKernel()). Mizer automatically
falls back on the old non-FFT code to handle this. (#41)getPredKernel() returns the full 3-dimensional predation kernel array,
even when this is not stored in MizerParams object.Now it is finally possible to have several gears (or fleets) targeting the same
species. The information is set up via a new gear_params() data frame. See
setFishing() for details.
w(), w_full(), dw(), dw_full().upgradeParams() and upgradeSim() can upgrade objects from
previous versions of mizer so they work with the new version.getDiet() calculates the diet of predators. (#43)RickerRDD() and SheperdRDD() for density-dependence
in reproduction, as well as noRDD() and constantRDD().double_sigmoid_length() allows modelling
of escape of large individuals.sigmoidal_weight() is weight-based trawl
selectivity function. (Ken H Andersen)getGrowthCurves() calculates the growth curves (size at age).mizerRates() calculates all the rates needed in the model and collects
them in a list.times() to extract the times at which simulation
results are saved in a MizerSim object.finalN(), finalNResource() and finalNOther() as
well as idxFinalT() to access the values at the final time of a simulation.getCriticalFeedingLevel() returns the critical feeding level
for each species at each size.melt() function from the reshape2 package which allows
users to convert the arrays returned by mizer functions into data frames
that can be used for example in ggplot2 and plotly.validSpeciesParams() checks validity of species parameter data frame and
sets defaults for missing but required parameters.n, p and q as well as the feeding level f0
can now be set at the species level via columns in species_params.fc can now be specified as a species parameter
and will be used to calculate the metabolic rate parameter ks if it is not
supplied.project() now shows a progress bar while a simulation is running. Can be
turned off with progress_bar = FALSE argument.Inf.resource_semichemostat().interaction_resource column in the
species parameter data frame.w_mat25
column in the species parameter dataframe, which gives the size at which
25% of the individuals of a species are mature.m column in the species parameter data frame.project() can now continue projection from last time step of a previous
simulation if the first argument is a MizerSim object. The new append
argument then controls whether the new results are appended to the old.newMultispeciesParams().n, n_pp and n_other arguments
from the initial values in the params argument.perfect_scaling argument allows newTraitParams() to produce a perfectly
scale-invariant model.ext_mort_prop argument in newTraitParams() allows the inclusion of
external mortality.NS_params with the North Sea model MizerParams object.newMultispeciesParams().getmM2() -> getPredMort()plotM2 -> plotPredMort()getM2background() -> getResourceMort()getZ() -> getMort()getESpawning() -> getERepro()MizerParams() -> emptyParams() or set_multispecies_model()r_max to R_max.getSSB(), the calculation of the spawning stock biomass is done correctly
using the maturity ogive instead of the proportion of energy allocated to
reproduction. (#47)getEncounter() and getPredRate() now set names on the returned arrays.ylim argument is not handled correctly in plots.display_frame() is now exported.plotGrowthCurves() and getGrowthCurves() also works when there is only a
single speciest_start argument in project() is used correctlyget_initial_n() gets values for n and q from params objectsummary() of MizerParams object reflects the number of non-empty resource
bins. (@patricksykes)get_gamma_default(),
get_h_default() and get_ks_default(), making it easier to change or
extend these in the future.set_species_param_default() makes it easier to set default
values for species parameters.@inherit functionality of roxygen2 to reduce duplication in
roxygen documentation.getPhiPrey() is replaced by getEncounter() which now returns the full
encounter rate, including the contribution from other components. Even
in the absence of other components, getEncounter() differs from the
old getPhiPrey() because it includes the search volume factor.params to refer to an argument of class MizerParams, sim
to refer to an argument of class MizerSim, and object to an argument that
can be either.setClass() to follow the new guidelines, replacing
representation by class and removing prototype and validity.shiny_progress renamed to progress_bar because they control
any type of progress bar.outer() instead of tapply() where possible to improve readability.hasArg() and anyNA() because they were not available in R 3.1@std_metab and @activity slots into a single @metab slot.@w_min_idx out of @species_params into its own slot.@maturity to hold the maturity ogive.@pred_kernel to hold predation kernel if it has variable
predator/prey ratio.@resource_dynamics to allow user to specify alternative
resource dynamics.@gear_dynamics to species to be targeted by multiple gears.@ft_mask that is used when calculating predation rates using
the Fourier transform method.@rates_funcs to allow mizer extensions to replace mizer rate
functions with their own rate functions.@srr we now have the name of the
function in @rate_funcs$RDD, see #91.@other_dynamics, @other_params, @other_encounter,
@other_mort and @initial_n_other to allow mizer extensions to add more
ecosystem components.set_scaling_model(), see section 12 in the vignette.PlotYield() no longer fails when species names are numbers or when a
species abundance is zerototal parameter to several plot functions to add the curve for the
total community (sum over all species and resource)species parameter to all plot functions to allow for only a
selection of species to be plotted@initial_n and @initial_n_pp slots to MizerParams class.getPhiPrey() and getPredRate().getPhiPrey() and getPredRate().