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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ stability of steady states.

## Bug fixes

- `plot(getFluxGradient(params), size_axis = "l")` now converts its values to a
density with respect to length, and labels them `cm^-1/year`. The flux
gradient is a rate of change of a number density, but it was not recognised as
a density, so its values were plotted per gram against a length axis and
labelled `g^-1/year`.

- `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.

- `steady()` now successfully converges when the advective flux scheme is set to `"van_leer"` (via `second_order_w`). Previously, the time-stepping iteration would fall into a limit cycle because the flux limiter weights flipped wildly across cells. We resolved this by introducing an exponential moving average relaxation to the limiter `chi` (#522).

## New functions
Expand Down Expand Up @@ -104,6 +114,38 @@ stability of steady states.

## Other improvements

- 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 `...`.

- `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
Expand Down
51 changes: 36 additions & 15 deletions R/ArrayResourceBySize-class.R
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#' `is.ArrayResourceBySize()`, any object to test.
#' @param value_name A string giving the human-readable name for the value.
#' @param units A string giving the units (e.g. "1/year").
#' @param type The kind of quantity the values are, see [ArraySpeciesBySize()]
#' and [array_types].
#' @param params A `MizerParams` object. Used for the resource colour and the
#' size grid in the `plot()` method.
#'
Expand All @@ -41,10 +43,11 @@
#' plot(mort)
#' }
ArrayResourceBySize <- function(x, value_name = NULL, units = NULL,
params = NULL) {
type = NULL, params = NULL) {
if (!is.numeric(x) || !is.null(dim(x))) {
stop("`x` must be a numeric vector.")
}
type <- resolve_array_type(type, value_name, units)
if (!is.null(params) && length(x) == length(params@initial_n_pp) &&
is.null(names(x))) {
names(x) <- names(params@initial_n_pp)
Expand All @@ -53,6 +56,7 @@ ArrayResourceBySize <- function(x, value_name = NULL, units = NULL,
class = c("ArrayResourceBySize", "numeric"),
value_name = value_name,
units = units,
type = type,
params = params
)
}
Expand Down Expand Up @@ -148,6 +152,10 @@ print.summary.ArrayResourceBySize <- function(x, ...) {
#' @param ylim A numeric vector of length two providing lower and upper
#' limits for the value (y) axis. Use `NA` to refer to the existing
#' minimum or maximum.
#' @param per_log_size For an array that holds a density, whether to plot it
#' per logarithmic size (`TRUE`) rather than per size (`FALSE`). The default,
#' `NULL`, plots the density as it stands. An error for an array that does not
#' hold a density.
#' @param y_ticks The approximate number of ticks desired on the y axis.
#' @param ... Unused.
#'
Expand All @@ -163,26 +171,31 @@ print.summary.ArrayResourceBySize <- function(x, ...) {
plot.ArrayResourceBySize <- function(x, return_data = FALSE,
log_x = TRUE, log_y = TRUE, log = NULL,
wlim = c(NA, NA), ylim = c(NA, NA),
per_log_size = NULL,
y_ticks = 6, ...) {
check_per_log_size(x, per_log_size)
log_y <- array_log_y(x, log_y, log, !missing(log_y))
log_axes <- parsePlotLog(log, log_x = log_x, log_y = log_y)
log_x <- log_axes$log_x
log_y <- log_axes$log_y

assert_that(length(wlim) == 2,
length(ylim) == 2)
value_name <- attr(x, "value_name") %||% "value"
units_str <- attr(x, "units")
params <- attr(x, "params")

plot_dat <- prepare_ArrayResourceBySize_plot_data(x, wlim = wlim)
# The resource has no weight-length relationship, so it has no length axis.
# Expressing a density per logarithmic weight needs no such relationship,
# though, so that much is available here.
plot_dat <- convert_plot_density_axis(plot_dat, params, "w",
density_wrt = array_density_wrt(x),
per_log_size = per_log_size)

if (return_data) return(plot_dat)

y_label <- value_name
if (!is.null(units_str) && nzchar(units_str)) {
y_label <- paste0(value_name, " [", units_str, "]")
}
y_label <- array_y_label(x, default = "value", per_log_size = per_log_size)

ylim <- array_ylim(x, ylim, log_y, plot_dat[[2]])
plotDataFrame(plot_dat, params, xlab = "Weight (g)",
ylab = y_label,
xtrans = if (log_x) "log10" else "identity",
Expand Down Expand Up @@ -379,6 +392,7 @@ get_ArrayResourceBySize_w <- function(x) {
result <- NextMethod()
attr(result, "value_name") <- attr(x, "value_name")
attr(result, "units") <- attr(x, "units")
attr(result, "type") <- attr(x, "type")
attr(result, "params") <- attr(x, "params")
class(result) <- c("ArrayResourceBySize", "numeric")
result
Expand Down Expand Up @@ -416,6 +430,7 @@ unclass_resource <- function(x) {
x <- unclass(x)
attr(x, "value_name") <- NULL
attr(x, "units") <- NULL
attr(x, "type") <- NULL
attr(x, "params") <- NULL
x
}
Expand Down Expand Up @@ -461,6 +476,8 @@ str.ArrayResourceBySize <- function(object, ...) {
#' object to test.
#' @param value_name A string giving the human-readable name for the value.
#' @param units A string giving the units (e.g. "1/g").
#' @param type The kind of quantity the values are, see [ArraySpeciesBySize()]
#' and [array_types].
#' @param params A `MizerParams` object. Used for the resource colour and the
#' size grid in the `plot()` method.
#'
Expand All @@ -477,14 +494,16 @@ str.ArrayResourceBySize <- function(object, ...) {
#' plot(nr)
#' }
ArrayTimeByResourceBySize <- function(x, value_name = NULL, units = NULL,
params = NULL) {
type = NULL, params = NULL) {
if (!is.matrix(x)) {
stop("`x` must be a matrix.")
}
type <- resolve_array_type(type, value_name, units)
structure(x,
class = c("ArrayTimeByResourceBySize", "matrix", "array"),
value_name = value_name,
units = units,
type = type,
params = params
)
}
Expand Down Expand Up @@ -611,7 +630,8 @@ ArrayTimeByResourceBySize_slice <- function(x, time = NULL) {

vec <- unclass(x)[tidx, ]
ArrayResourceBySize(vec, value_name = value_name,
units = units, params = params)
units = units, type = attr(x, "type"),
params = params)
}

#' @rdname plot2
Expand Down Expand Up @@ -688,6 +708,7 @@ animate.ArrayTimeByResourceBySize <- function(x, species = NULL,
ylim = c(NA, NA),
tlim = c(NA, NA),
size_axis = c("w", "l"),
per_log_size = NULL,
total = FALSE,
background = TRUE,
frame_duration = 500,
Expand All @@ -699,6 +720,7 @@ animate.ArrayTimeByResourceBySize <- function(x, species = NULL,
is.string(easing),
length(wlim) == 2, length(ylim) == 2, length(tlim) == 2)
warn_unused_resource_args(species, total, background)
check_per_log_size(x, per_log_size)
# The length axis is derived from each species' weight-length parameters,
# and the resource is not a species.
size_axis <- plot_size_axis(size_axis)
Expand All @@ -711,8 +733,6 @@ animate.ArrayTimeByResourceBySize <- function(x, species = NULL,
log_y <- log_axes$log_y

params <- attr(x, "params")
value_name <- attr(x, "value_name") %||% "Value"
units_str <- attr(x, "units")

times <- as.numeric(dimnames(x)[[1]])
arr <- unclass(x)
Expand All @@ -735,13 +755,12 @@ animate.ArrayTimeByResourceBySize <- function(x, species = NULL,
df$Species <- "Resource"
df$legend_name <- "Resource"

y_label <- value_name
if (!is.null(units_str) && nzchar(units_str)) {
y_label <- paste0(value_name, " [", units_str, "]")
}
y_label <- array_y_label(x, default = "Value", per_log_size = per_log_size)

animate_plotly(df, params, log_x, log_y, y_label, wlim, llim, ylim,
size_axis = size_axis,
density_wrt = array_density_wrt(x),
per_log_size = per_log_size,
frame_duration = frame_duration,
transition_duration = transition_duration,
easing = easing)
Expand Down Expand Up @@ -780,6 +799,7 @@ as.data.frame.ArrayTimeByResourceBySize <- function(x, row.names = NULL,
if (is.matrix(result) && length(dim(result)) == 2) {
attr(result, "value_name") <- attr(x, "value_name")
attr(result, "units") <- attr(x, "units")
attr(result, "type") <- attr(x, "type")
attr(result, "params") <- attr(x, "params")
class(result) <- c("ArrayTimeByResourceBySize", "matrix", "array")
} else if (is.null(dim(result)) && !is.null(names(result)) &&
Expand All @@ -788,6 +808,7 @@ as.data.frame.ArrayTimeByResourceBySize <- function(x, row.names = NULL,
result <- ArrayResourceBySize(result,
value_name = attr(x, "value_name"),
units = attr(x, "units"),
type = attr(x, "type"),
params = attr(x, "params"))
}
result
Expand Down
Loading