mizer: Multi-Species Size Spectrum Modelling in R
Le résumé fourni par la source
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. Dynamic stability The two steady-state finders gain an experimental solver argument. With solver = "newton" they solve the steady-state equation directly with a Newton-type root finder (via the nleqslv package) instead of running the dynamics to convergence. Unlike the default solver = "project" it converges even when the steady state is dynamically unstable, and it discovers the support of the steady state automatically. findSteadyState(solver = "newton") carries the resource densities among its unknowns and so needs the default semichemostat resource dynamics; tuneSteadyState(solver = "newton") holds the resource fixed and works with any. New experimental getStability() analyses the dynamic stability of a mizer steady state by computing the eigenvalues of the linearised dynamics at the fixed point. Mizer discretises the size axis but not time, so the model is a system of ODEs on the size grid; getStability() differentiates their right-hand side directly and returns the eigenvalues of that Jacobian. It reports whether the steady state is stable or unstable (based on the real parts of the eigenvalues), the maximum real part, and — where the spectrum contains a complex pair — the leading_oscillatory_eigenvalue, the oscillation_period at which the model rings and the leading_oscillatory_eigenvector of that mode. Components registered with setComponent() are held fixed and are not in the Jacobian, which mizer now warns about when it meets one with dynamics of its own. New experimental getDiscreteStability() asks the separate question of how mizer's numerical step behaves near the steady state: it linearises the one-step map that project(method = "euler") takes at a given dt and returns its discrete_eigenvalues and spectral_radius. A spectral radius below 1 means the scheme does not amplify perturbations at that step size, which is not the same as the model being stable — the implicit transport solve damps oscillations artificially, so a physically unstable steady state can look discretely stable at a large dt. New experimental getOscillationModeSim() takes a model at a steady state — or the stability list that getStability() returns — and constructs a MizerSim covering exactly one period of the leading oscillatory mode in the linear approximation, ending at the period itself so that the oscillation closes. It shows the shape of the mode — which species swing, how far, and in what phase relative to each other and to the resource — which is a limit cycle only at a Hopf bifurcation, where the mode's growth rate is zero. The trajectory is \eqn{x(t) = x^* + A,\text{Re}[e^{i\omega t},\mathbf{v}]} over the whole state \eqn{x = (N, n_{pp})}, where \eqn{\mathbf{v}} is the eigenvector of the dominant oscillatory mode and the amplitude \eqn{A} is scaled so that the largest relative swing in species biomass equals the amplitude argument (default 10%), which is the quantity plotBiomass() shows. The resource oscillates with the amplitude and phase the mode gives it, rather than being slaved to the fish. The returned object can be passed directly to plotBiomass(), plotSpectra(), and other standard mizer plot functions. plotYieldVsF() has moved to mizer from mizerExperimental and is now a thin wrapper over scanModel(), described below. It plots the yield of one species against the fishing mortality on that species, leaving the fishing on every other species alone, and marks the fishing mortality at which the yield is largest, which is $F_{MSY}$. The current fishing mortality is drawn as a "Current F" reference line. A limit cycle is drawn as a band around the average rather than being silently averaged away, and if the species has an F_MSY species parameter it is also drawn as a reference line for comparison. getYieldVsF() has not come with it: use plotYieldVsF(return_data = TRUE), which returns the MizerScan object behind the plot, or scanModel() itself. New experimental scanModel() scans any aspect of a model over a range of values and measures any quantity on the attractor the model settles on at each of them. You say what to vary by passing a function that changes the model, and what to measure by passing a function that computes a quantity from a MizerSim, so a yield-versus-fishing-mortality curve, a bifurcation diagram over fishing effort and a scan over the resource carrying capacity are all the same call with different arguments. All of mizer's summary functions (getBiomass(), getYield(), getSSB(), getN(), sizeIntegral()) work as the measuring function unchanged. How the quantity is measured depends on what the model settled on, which projectToSteady() now reports. On a fixed point it is read straight off the settled state. On a limit cycle the model is projected for exactly one period of the detected cycle and the quantity is averaged over it, giving its long-term average; a window that is not a whole number of periods would leave a residue of the oscillation in the average. When the model settled on neither, the quantity is averaged over t_sample years and the scan values concerned are named in a message, because those points should not be relied on. New scanEffort(), scanFishingMortality() and scanSpeciesParam() build the function that applies each scan value to the model; any function of (params, value) returning a MizerParams will do, as long as it is idempotent. New experimental MizerScan S3 class holds the result of a scanModel() run. It is a data frame with one row per scan value and series, carrying as attributes everything plot() needs to draw it — the axis names and units, the MizerParams the scan started from, optional reference lines, and at_max, the scanned value at which each series attains its largest value (which is $F_{MSY}$ on a yield-versus-fishing-mortality scan). It has plot(), print(), summary(), as.data.frame(), str() and [ methods; [ recomputes at_max from the rows that remain, so a subset never carries a stale maximum. plotDataFrame() gained two styles, "ribbon" and "envelope", which draw a band between the ymin and ymax variables of the data frame. "ribbon" draws the y variable as a line inside the band; "envelope" draws lines along the two edges instead. plot.MizerScan() uses them to draw the range a quantity covers on a limit cycle, and so shares the line types, highlighting and xlim that plotDataFrame() provides. steady() and projectToSteady() now report the nature of the solution they converged to via a "convergence" attribute on the returned object. It answers three questions separately, because conflating them is how a limit cycle came to be reported as a converged steady state: termination says why the run stopped, converged whether the solver met its own criterion, and attractor what the state actually is — "fixed_point", "limit_cycle" or NA. Only attractor may be read as a claim about the model, and it is set from the measured biomass drift, reported alongside as residual. Limit cycles are detected from a per-species biomass series sampled at the new t_save resolution (default dt). The relative-amplitude floor for calling an oscillation a limi
Ce résumé expose les affirmations des auteurs. BNTIC ne l’interprète pas comme une validation indépendante des résultats.