The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
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.
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 over the whole
state , where is the eigenvector of the dominant oscillatory
mode and the amplitude 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 saveRDS(), since the file deliberately holds a
base-class object and only readParams() puts the extension
class back.
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 (setExtDiffusion());setResource(); user-supplied full vectors are left
untouched), together with the matching initial resource abundance 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 (, the 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 , 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 , 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 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 . 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 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().These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.