
diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/.nojekyll @@ -0,0 +1 @@ + diff --git a/404.html b/404.html new file mode 100644 index 000000000..b08b32f09 --- /dev/null +++ b/404.html @@ -0,0 +1,141 @@ + + +
+ + + + +If you have any
+please do not hesitate letting us know by creating a new issue at https://github.com/sizespectrum/mizer/issues/. Or you can drop us a line at mizer@sizespectrum.org or make a post on the size-spectrum discussion group.
+Errors, shortcomings or any comment on the documentation can also be submited as an issue in the issue tracker.
+In case you know how to correct the error or make the improvement yourself, you can also help us by editing the page. At the top of most documentation pages there is a link to the page source on GitHub. There the page will have an “Edit this file” button that looks like a pencil. Click this an edit the page. When you save your edits GitHub guides you through the process of creating a pull request for your edits, which will make it super-easy for us to merge your suggestions.
+So even small contributions like the removal of a simple typo are very welcome.
+If you have set up a mizer model for your area of interest or your research question, please share it with us. The best place to do that is in the MizerExamples package. Start by creating an issue at that package’s issue tracker or just send an email to mizer@sizespectrum.org. We will then discuss how to best integrate your example into that package to make it useful to others.
+We are more than happy to receive pull requests if you have new code or a bug fix. Your contribution will be appreciated by the community. If it is a new feature, the best place to submit it is as a pull request to the MizerExperimental package. There the feature can mature until it is ready to be incorporated either into the core mizer package or into one of its extension packages.
+Do not forget to mention the use of mizer in any of your documents and publications. Use
+citation('mizer')
+to get citation info for the main reference for mizer. You can also link to our website https://sizespectrum.org/mizer/ so readers can find out more about the project.
+vignettes/a_multispecies_model_of_the_north_sea.Rmd
+ a_multispecies_model_of_the_north_sea.Rmd
In this section we try to pull everything together with an extended +example of a multispecies model for the North Sea. First we will set up +the model, project it through time using historical levels of fishing +effort, and then examine the results. We then run two different future +projection scenarios.
+The first job is to set up the MizerParams
object for
+the North Sea model. In the previous multispecies examples we have
+already been using the life-history parameters and the interaction
+matrix for the North Sea model. We will use them again here but will
+make some changes. In particular we set up the fishing gears
+differently.
The species in the model are: Sprat, Sandeel, N.pout, Herring, Dab,
+Whiting, Sole, Gurnard, Plaice, Haddock, Cod, Saithe, which account for
+about 90% of the total biomass of all species sampled by research trawl
+surveys in the North Sea. The NS_species_params
object that
+comes as an example with the mizer package is a data.frame with columns
+for species
, w_max
, w_mat
,
+beta
, sigma
, k_vb
and
+R_max
.
+NS_species_params
## species w_max w_mat beta sigma k_vb R_max w_inf
+## 1 Sprat 33.0 13 51076 0.8 0.681 7.38e+11 33.0
+## 2 Sandeel 36.0 4 398849 1.9 1.000 4.10e+11 36.0
+## 3 N.pout 100.0 23 22 1.5 0.849 1.05e+13 100.0
+## 4 Herring 334.0 99 280540 3.2 0.606 1.11e+12 334.0
+## 5 Dab 324.0 21 191 1.9 0.536 1.12e+10 324.0
+## 6 Whiting 1192.0 75 22 1.5 0.323 5.48e+11 1192.0
+## 7 Sole 866.0 78 381 1.9 0.284 3.87e+10 866.0
+## 8 Gurnard 668.0 39 283 1.8 0.266 1.65e+12 668.0
+## 9 Plaice 2976.0 105 113 1.6 0.122 4.08e+14 2976.0
+## 10 Haddock 4316.5 165 558 2.1 0.271 1.84e+12 4316.5
+## 11 Cod 39851.3 1606 66 1.3 0.216 8.26e+09 39851.3
+## 12 Saithe 39658.6 1076 40 1.1 0.175 1.12e+11 39658.6
+We have seen before that only having these columns in the species
+data.frame is sufficient to make a MizerParams
object. Any
+missing columns will be filled with default values by the
+MizerParams
constructor. For example, the data.frame does
+not include columns for h
or gamma
. This means
+that they will be estimated using the k_vb
column.
We will use the default density dependence in the reproduction rate,
+which is the Beverton-Holt shape. This requires a column
+R_max
in the species data.frame which contains the maximum
+reproduction rate for each species. This column is already in the
+NS_species_params
data.frame. The values were found through
+a calibration process which is not covered here but will be described in
+a separate tutorial.
At the moment we are not providing any information on the selectivity
+of the gears for the species. By default, the selectivity function is a
+knife-edge which only takes a single argument,
+knife_edge_size
. In this model we want the selectivity
+pattern to be a sigmoid shape which more accurately reflects the
+selectivity pattern of trawlers in the North Sea. The sigmoid
+selectivity function is expressed in terms of length rather than weight
+and uses the parameters l25
and l50
, which are
+the lengths at which 25% and 50% of the stock is selected. The length
+based sigmoid selectivity looks like:
\[\begin{equation} +% {#eq:trawl_sel} + S_l = \frac{1}{1 + \exp(S1 - S2\ l)} +\end{equation}\]
+where \(l\) is the length of an +individual, \(S_l\) is the selectivity +at length, \(S2 = \log(3) / (l50 - +l25)\) and \(S1 = l50 \cdot +S2\).
+This selectivity function is included in mizer as
+sigmoid_length()
. You can see the help page for more
+details. As the mizer model is weight based, and this selectivity
+function is length based, it uses the length-weight parameters
+a
and b
to convert between length and weight
+using the standard relation \(w = a
+l^b\). These species parameters need to be added as columns to
+the NS_species_params
data frame.
+NS_species_params$a <- c(0.007, 0.001, 0.009, 0.002, 0.010, 0.006, 0.008, 0.004,
+ 0.007, 0.005, 0.005, 0.007)
+NS_species_params$b <- c(3.014, 3.320, 2.941, 3.429, 2.986, 3.080, 3.019, 3.198,
+ 3.101, 3.160, 3.173, 3.075)
sigmoid_length()
has the arguments l25
and
+l50
. As explained in the section on fishing
+gears and selectivity, the arguments of the selectivity function
+need to be in the gear parameter data frame. We also need a column
+specifying the name of the selectivity function we wish to use. Note it
+would probably be easier to put this data into a *.csv file and then
+read it in rather than type it in by hand like we do here:
+gear_params <-
+ data.frame(species = NS_species_params$species,
+ gear = NS_species_params$species,
+ sel_func = "sigmoid_length",
+ l25 = c(7.6, 9.8, 8.7, 10.1, 11.5, 19.8, 16.4, 19.8, 11.5,
+ 19.1, 13.2, 35.3),
+ l50 = c(8.1, 11.8, 12.2, 20.8, 17.0, 29.0, 25.8, 29.0, 17.0,
+ 24.3, 22.9, 43.6))
+gear_params
## species gear sel_func l25 l50
+## 1 Sprat Sprat sigmoid_length 7.6 8.1
+## 2 Sandeel Sandeel sigmoid_length 9.8 11.8
+## 3 N.pout N.pout sigmoid_length 8.7 12.2
+## 4 Herring Herring sigmoid_length 10.1 20.8
+## 5 Dab Dab sigmoid_length 11.5 17.0
+## 6 Whiting Whiting sigmoid_length 19.8 29.0
+## 7 Sole Sole sigmoid_length 16.4 25.8
+## 8 Gurnard Gurnard sigmoid_length 19.8 29.0
+## 9 Plaice Plaice sigmoid_length 11.5 17.0
+## 10 Haddock Haddock sigmoid_length 19.1 24.3
+## 11 Cod Cod sigmoid_length 13.2 22.9
+## 12 Saithe Saithe sigmoid_length 35.3 43.6
+Note that we have set up a gear
column so that each
+species will be caught by a separate gear named after the species.
In this model we are interested in projecting forward using
+historical fishing mortalities. The historical fishing mortality from
+1967 to 2010 for each species is stored in the csv file
+NS_f_history.csv
included in the package. As before, we can
+use read.csv()
to read in the data. This reads the data in
+as a data.frame
. We want this to be a matrix
+so we use the as()
function:
+f_location <- system.file("extdata", "NS_f_history.csv", package = "mizer")
+f_history <- as(read.csv(f_location, row.names = 1), "matrix")
We can take a look at the first years of the data:
+
+head(f_history)
## Sprat Sandeel N.pout Herring Dab Whiting Sole Gurnard
+## 1967 0 0 0 1.0360279 0.09417655 0.8294528 0.6502019 0
+## 1968 0 0 0 1.7344576 0.07376065 0.8008995 0.7831250 0
+## 1969 0 0 0 1.4345001 0.07573638 1.3168280 0.8744095 0
+## 1970 0 0 0 1.4342405 0.10537236 1.3473505 0.6389915 0
+## 1971 0 0 0 1.8234973 0.08385884 0.9741884 0.8167561 0
+## 1972 0 0 0 0.9033768 0.09044461 1.3148588 0.7382834 0
+## Plaice Haddock Cod Saithe
+## 1967 0.4708827 0.7428694 0.6677456 0.4725102
+## 1968 0.3688033 0.7084553 0.6994389 0.4270201
+## 1969 0.3786819 1.3302821 0.6917888 0.3844648
+## 1970 0.5268618 1.3670695 0.7070891 0.5987086
+## 1971 0.4192942 0.9173131 0.7737543 0.4827822
+## 1972 0.4522231 1.3279087 0.8393267 0.5796321
+Fishing mortality is calculated as the product of selectivity,
+catchability and fishing effort. The values in f_history
+are absolute levels of fishing mortality. We have seen that the fishing
+mortality in the mizer
simulations is driven by the fishing
+effort argument passed to the project()
function. Therefore
+if we want to project forward with historical fishing levels, we need to
+provide project()
with effort values that will result in
+these historical fishing mortality levels.
One of the model parameters that we have not really considered so far
+is catchability
. Catchability is a scalar parameter used to
+modify the fishing mortality at size given the selectivity at size and
+effort of the fishing gear. By default catchability has a value of 1,
+meaning that an effort of 1 results in a fishing mortality of 1 for a
+fully selected species. When considering the historical fishing
+mortality, one option is therefore to leave catchability at 1 for each
+species and then use the f_history
matrix as the fishing
+effort. However, an alternative method is to use the effort relative to
+a chosen reference year. This can make the effort levels used in the
+model more meaningful. Here we use the year 1990 as the reference year.
+If we set the catchability of each species to be the same as the fishing
+mortality in 1990 then an effort of 1 in 1990 will result in the fishing
+mortality being what it was in 1990. The effort in the other years will
+be relative to the effort in 1990.
+gear_params$catchability <- as.numeric(f_history["1990", ])
Considering the other model parameters, we will use default values
+for all of the other parameters apart from kappa
, the
+carrying capacity of the resource spectrum (see see the section on
+resource density). This was estimated along with the values
+R_max
as part of the calibration process.
We now have all the information we need to create the
+MizerParams
object using the species parameters
+data.frame.
+params <- newMultispeciesParams(NS_species_params,
+ interaction = inter,
+ kappa = 9.27e10,
+ gear_params = gear_params)
## Because you have n != p, the default value for `h` is not very good.
+## Because the age at maturity is not known, I need to fall back to using
+## von Bertalanffy parameters, where available, and this is not reliable.
+## No ks column so calculating from critical feeding level.
+## Using z0 = z0pre * w_max ^ z0exp for missing z0 values.
+## Using f0, h, lambda, kappa and the predation kernel to calculate gamma.
+As we set our catchability to be the level of fishing mortality in
+1990, before we can run the projection we need to rescale the effort
+matrix to get a matrix of efforts relative to 1990. To do this we want
+to rescale the f_history
object to 1990 so that the
+relative fishing effort in 1990 = 1. This is done using R function
+sweep()
. We then check a few rows of the effort matrix to
+check this has happened:
+relative_effort <- sweep(f_history, 2, f_history["1990", ], "/")
+relative_effort[as.character(1988:1992), ]
## Sprat Sandeel N.pout Herring Dab Whiting Sole
+## 1988 0.8953804 1.2633229 0.8953804 1.214900 1.176678 0.9972560 1.2786517
+## 1989 1.1046196 1.2931034 1.1046196 1.232790 1.074205 0.8797926 0.9910112
+## 1990 1.0000000 1.0000000 1.0000000 1.000000 1.000000 1.0000000 1.0000000
+## 1991 1.1902174 0.8814002 1.1902174 1.108016 1.143110 0.8096927 1.0044944
+## 1992 1.2500000 0.8500522 1.2500000 1.316576 1.113074 0.7718676 0.9505618
+## Gurnard Plaice Haddock Cod Saithe
+## 1988 0.0000000 1.176678 0.9946140 1.045964 1.0330579
+## 1989 0.0000000 1.074205 0.8545781 1.060538 1.1223140
+## 1990 1.0000000 1.000000 1.0000000 1.000000 1.0000000
+## 1991 0.8096927 1.143110 0.7971275 1.001121 0.9619835
+## 1992 0.7718676 1.113074 0.8797127 0.970852 1.0528926
+We could just project forward with these relative efforts. However, +the population dynamics in the early years will be strongly determined +by the initial population abundances (known as the transient behaviour - +essentially the initial behaviour before the long term dynamics are +reached). As this is ecology, we don’t know what the initial abundance +are. One way around this is to project forward at a constant fishing +mortality equal to the mortality in the first historical year until +equilibrium is reached. We then use this steady state as the initial +state for the simulation. This approach reduces the impact of transient +dynamics.
+
+params <- projectToSteady(params, effort = relative_effort["1967", ])
## Convergence was achieved in 24 years.
+We now have our parameter object and out matrix of efforts relative
+to 1990. We use this effort matrix as the effort
argument
+to the project()
function. We use dt
= 0.25
+(the simulation will run faster than with the default value of 0.1, but
+tests show that the results are still stable) and save the results every
+year.
+sim <- project(params, effort = relative_effort, dt = 0.25, t_save = 1)
Plotting the results, we can see how the biomasses of the stocks +change over time.
+
+plotBiomass(sim)
To explore the state of the community it is useful to calculate +indicators of the unexploited community. Therefore we also project +forward to the steady state with 0 fishing effort.
+
+sim0 <- projectToSteady(params, effort = 0, return_sim = TRUE)
## Convergence was achieved in 42 years.
+Here we look at some of the ways the results of the simulation can be
+explored. We calculate the community indicators
+mean maximum weight
, mean individual weight
,
+community slope
and the large fish indicator
+(LFI) over the simulation period, and compare them to the unexploited
+values. We also compare the simulated values of the LFI to a community
+target based on achieving a high proportion of the unexploited value of
+the LFI of \(0.8 LFI_{F=0}\).
The indicators are calculated using the functions described in the +section about indicator functions. Here we calculate the LFI and the +other community indicators for the unexploited community. When +calculating these indicators we only include demersal species and +individuals in the size range 10 g to 100 kg, and the LFI is based on +species larger than 40 cm. Each of these functions returns a time +series. We are interested only in the equilibrium unexploited values so +we just select the final time step.
+
+demersal_species <- c("Dab", "Whiting", "Sole", "Gurnard", "Plaice",
+ "Haddock", "Cod", "Saithe")
+final <- idxFinalT(sim0)
+lfi0 <- getProportionOfLargeFish(sim0, species = demersal_species,
+ min_w = 10, max_w = 100e3,
+ threshold_l = 40)[[final]]
+mw0 <- getMeanWeight(sim0, species = demersal_species,
+ min_w = 10, max_w = 100e3)[[final]]
+mmw0 <- getMeanMaxWeight(sim0, species = demersal_species,
+ min_w = 10, max_w = 100e3)[final, "mmw_biomass"]
+slope0 <- getCommunitySlope(sim0, species = demersal_species,
+ min_w = 10, max_w = 100e3)[final, "slope"]
We also calculate the time series of these indicators for the +exploited community:
+
+lfi <- getProportionOfLargeFish(sim, species = demersal_species,
+ min_w = 10, max_w = 100e3,
+ threshold_l = 40)
+mw <- getMeanWeight(sim, species = demersal_species,
+ min_w = 10, max_w = 100e3)
+mmw <- getMeanMaxWeight(sim, species = demersal_species, min_w = 10,
+ max_w = 100e3)[, "mmw_biomass"]
+slope <- getCommunitySlope(sim, species = demersal_species, min_w = 10,
+ max_w = 100e3)[, "slope"]
We can plot the exploited and unexploited indicators, along LFI
+reference level. Here we do it using ggplot2
which uses
+data.frames. We make three data.frames (one for the time series, one for
+the unexploited levels and one for the reference level): Each data.frame
+is a data.frame of each of the measures, stacked on top of each
+other.
+library(ggplot2)
+years <- 1967:2010
+# Simulated data
+community_plot_data <- rbind(
+ data.frame(year = years, measure = "LFI", data = lfi),
+ data.frame(year = years, measure = "Mean Weight", data = mw),
+ data.frame(year = years, measure = "Mean Max Weight", data = mmw),
+ data.frame(year = years, measure = "Slope", data = slope))
+# Unexploited data
+community_unfished_data <- rbind(
+ data.frame(year = years, measure = "LFI", data = lfi0),
+ data.frame(year = years, measure = "Mean Weight", data = mw0),
+ data.frame(year = years, measure = "Mean Max Weight", data = mmw0),
+ data.frame(year = years, measure = "Slope", data = slope0))
+# Reference level
+community_reference_level <-
+ data.frame(year = years, measure = "LFI", data = lfi0 * 0.8)
+# Build up the plot
+ggplot(community_plot_data) +
+ geom_line(aes(x = year, y = data)) +
+ facet_wrap(~measure, scales = "free") +
+ geom_line(aes(x = year, y = data), linetype = "dashed",
+ data = community_unfished_data) +
+ geom_line(aes(x = year, y = data), linetype = "dotted",
+ data = community_reference_level)
+Historical (solid) and unexploited (dashed) and reference (dotted) +community indicators for the North Sea multispecies model. +
+According to our simulations, historically the LFI in the North Sea +has been below the reference level.
+As well as investigating the historical simulations, we can run +projections into the future. Here we run two projections to 2050 with +different fishing scenarios.
+Rather than looking at community indicators here, we will calculate +the SSB of each species in the model and compare the projected levels to +a biodiversity target based on the reference point \(0.1 SSB_{F=0}.\)
+Before we can run the simulations, we need to set up arrays of future
+effort. We will continue to use effort relative to the level in 1990.
+Here we build on our existing array of relative effort to make an array
+for the first scenario. Note the use of the t()
command to
+transpose the array. This is needed because R recycles by rows, so we
+need to build the array with the dimensions rotated to start with. We
+make an array of the future effort, and then bind it underneath the
+relative_effort
array used in the previous section.
+scenario1 <- t(array(relative_effort["2010", ], dim = c(12, 40),
+ dimnames = list(NULL, year = 2011:2050)))
+scenario1 <- rbind(relative_effort, scenario1)
The relative effort array for the second scenario is more complicated +to make and requires a little bit of R gymnastics (it might be easier +for you to prepare this in a spreadsheet and read it in). For this one +we need values of \(F_{MSY}\).
+
+fmsy <- c(Sprat = 0.2, Sandeel = 0.2, N.pout = 0.2, Herring = 0.25, Dab = 0.2,
+ Whiting = 0.2, Sole = 0.22, Gurnard = 0.2, Plaice = 0.25, Haddock = 0.3,
+ Cod = 0.19, Saithe = 0.3)
+scenario2 <- t(array(fmsy, dim = c(12, 40),
+ dimnames = list(NULL, year = 2011:2050)))
+scenario2 <- rbind(relative_effort, scenario2)
+for (sp in dimnames(scenario2)[[2]]) {
+ scenario2[as.character(2011:2015), sp] <- scenario2["2010", sp] +
+ (((scenario2["2015", sp] - scenario2["2010", sp]) / 5) * 1:5)
+}
We are now ready to project the two scenarios.
+
+sim1 <- project(params, effort = scenario1, dt = 0.25)
+sim2 <- project(params, effort = scenario2, dt = 0.25)
We can now compare the projected SSB values in both scenarios to the
+biodiversity reference points. First we calculate the biodiversity
+reference points (from the final time step in the unexploited
+sim0
simulation):
+ssb0 <- getSSB(sim0)[final, ]
Now we build a data.frame of the projected SSB for each species. We
+make use of the melt()
function to transform arrays into
+data frames.
+years <- 1967:2050
+ssb1_df <- melt(getSSB(sim1))
+ssb2_df <- melt(getSSB(sim2))
+ssb_df <- rbind(
+ cbind(ssb1_df, scenario = "Status quo"),
+ cbind(ssb2_df, scenario = "Fmsy"))
+ssb_unexploited_df <- cbind(expand.grid(
+ sp = names(ssb0),
+ time = 1967:2050),
+ value = as.numeric(ssb0),
+ scenario = "Unexploited")
+ssb_reference_df <- cbind(expand.grid(
+ sp = names(ssb0),
+ time = 1967:2050),
+ value = as.numeric(ssb0 * 0.1),
+ scenario = "Reference")
+ssb_all_df <- rbind(ssb_df, ssb_unexploited_df, ssb_reference_df)
+colours <- c("Status quo" = "red", "Fmsy" = "yellow",
+ "Unexploited" = "blue", "Reference" = "purple")
+ggplot(ssb_all_df) +
+ geom_line(aes(x = time, y = value, colour = scenario)) +
+ facet_wrap(~sp, scales = "free", nrow = 4) +
+ theme(legend.position = "none") +
+ scale_colour_manual(values = colours)
+Historical and projected SSB under two fishing scenarios. Status quo +(red), Fmsy (yellow). Unexploited (blue) and reference levels (purple) +are also shown. +
+The simplest version of the size spectrum model is the community +model. In the community model, individuals are only characterized by +their size and are represented by a single group representing an +across-species aggregate. Reproduction is not considered; the +reproduction rate \(R\) is set to be +constant. The resource spectrum only extends to the start of the +community spectrum. Metabolism is turned off.
+In this section we describe how a community model can be set up and +projected through time. We then use a community model to illustrate the +idea of a trophic cascade. Due to the relative simplicity of this type +of model, it is useful for gently introducing some of the concepts +behind the mizer package. Consequently, this section should hopefully +serve as an introduction to using mizer.
+The first stage in implementing a model using mizer
is
+to create an object of class MizerParams
. This class
+contains the model parameters including the life-history parameters of
+the species in the model, the fishing selectivity functions and the
+parameters of the resource spectrum.
To avoid having to make a MizerParams
object directly,
+the newCommunityParams()
wrapper function, has been
+provided that conveniently creates a MizerParams
object
+specifically for a community model. The documentation for the function
+can be seen by clicking on the function name anywhere it appears on this
+page: newCommunityParams()
.
As can be seen in the help page, the function can take many +arguments. We can ignore most of these for the moment as they almost all +come with default values.
+The arguments that you should pay attention to are: z0
+(the external mortality rate), alpha
(the assimilation
+efficiency of the community), f0
(the average feeding level
+of the community, which is used to calculate \(\gamma\)) and h
(the
+coefficient of the maximum intake rate).
Although default values for these parameters are provided, you are
+encouraged to explore how changing the values affects the simulated
+community. For example, the default value of z0
is \(0.1\). Increasing this value effectively
+‘shortens’ the length of the community spectrum.
The newCommunityParams()
function is called by passing
+in the arguments by name. Any parameter that is not passed in is set to
+the default value. For example, the following line sets up the
+parameters with z0
= 0.05, f0
= 0.5. All other
+parameters will have their default value:
+params <- newCommunityParams(z0 = 0.05, f0 = 0.5)
Calling the function creates and returns an object of type
+MizerParams
. We can check this using the
+class()
function.
+class(params)
## [1] "MizerParams"
+## attr(,"package")
+## [1] "mizer"
+To work with mizer you do not have to worry about how this object is +realised internally. All you need to know is that it contains all the +information needed to run model simulations and that mizer provides you +with functions to access, use and modify this information.
+A very brief description of the model contained in the MizerParams
+object can be seen by calling the summary()
method on
+it:
+summary(params)
## An object of class "MizerParams"
+## Consumer size spectrum:
+## minimum size: 0.001
+## maximum size: 1e+06
+## no. size bins: 100
+## Resource size spectrum:
+## minimum size: 8.11131e-11
+## maximum size: 0.000811131
+## no. size bins: 78 (178 size bins in total)
+## Species details:
+## species w_max w_mat w_min f0 beta sigma
+## 1 Community 1e+06 NA 0.001 0.5 100 2
+##
+## Fishing gear details:
+## Gear Effort Target species
+## ----------------------------------
+## Community 0.00 Community
+In the summary you can see that the size range of the community
+spectrum has been set from \(0.001\) to
+\(10^{6}\) and this is divided into
+\(100\) size bins. Similar information
+is available for the resource spectrum. Additionally, the community is
+made up of only one species, called Community, which has an
+maximum size of \(10^{6}\) and a
+preferred predator prey mass ratio of \(100\). The w_mat
parameter has
+been set to NA
as it is not used when running a community
+model. These values have all been set by default using the
+newCommunityParams()
function.
The newCommunityParams()
function has given us a
+MizerParams
object that contains all the information mizer
+needs about the model community. We can use this to perform a simulation
+and project the community through time. In the mizer
+package, projections are performed using the project()
+function. You can see the help page for project()
for more
+details and it is described fully in the section on running a
+simulation. We will ignore the details for the moment and just use
+project()
to run some simple projections. The arguments for
+project()
that we need to be concerned with are
+effort
, which determines the fishing effort (and therefore
+fishing mortality) through time, and t_max
, which is the
+number of years we want to project into the future. Initial population
+abundances have been set automatically (by the
+get_initial_n()
function). It is possible to set your own
+initial abundances but we will not do this here.
To run a projection for 50 years, with no fishing effort (i.e. we +want to model an unexploited community) we run:
+
+sim <- project(params, t_max = 50, effort = 0)
The resulting object, sim
, is of type
+MizerSim
.
+class(sim)
## [1] "MizerSim"
+## attr(,"package")
+## [1] "mizer"
+This class holds the results of the simulation, including the +community and resource abundances at size through time, as well as the +original model parameters. It is explained in detail in the section on running a +simulation.
+After running the projection, it is possible to explore the results +using a range of plots and analyses. These are described fully in the section on exploring +the simulation results.
+To quickly look at the results of the projection you can call the
+plot()
method. This plots the feeding level, predation
+mortality, fishing mortality and abundance by size in the last time step
+of the simulation, and the total biomass through time. Each of the plots
+can be shown individually if desired.
+plot(sim)
In the above plot there are several things going on that are worth
+talking about. Looking at the total biomass of the community against
+time, you can see that the biomass quickly reaches a stable equilibrium.
+The other panels show what is happening at the last time step in the
+simulation, which in this case is when the community is at equilibrium.
+Fishing mortality is 0 because we set the effort
argument
+to 0 when running the simulation. The predation mortality rate is
+clearly a function of size, with the smallest sizes experiencing the
+highest levels of predation. The feeding level describes how satiated an
+individual is, with 0 being unfed, and 1 being full satiated. The
+feeding level at size will be strongly affected by the values of the
+f0
and alpha
arguments passed to the
+newCommunityParams()
function.
The resource and community spectra are shown in the bottom panel of
+the plot (the plotted resource spectrum has been truncated to make for a
+better plot, but really extends all the way back to \(8.11\times 10^{-11}\) g). You can see that
+the community spectrum forms a continuum with the resource spectrum.
+This is strongly affected by the level of fixed reproduction rate (the
+reproduction
argument passed to
+newCommunityParams()
)
Note the hump in the biomass at the largest end of the community
+spectrum. This is because the size spectrum model can be broadly
+described as ‘big things eating little things’. Given this, what is
+eating the very biggest things? Without fishing pressure, the mortality
+of the largest individuals is only from the external mortality
+(determined by the z0
argument) and the mortality from
+predation is almost 0. This is difficult to see in the plot due to the
+predation mortality being so high for the smaller individuals.
We can see this more clearly by extracting the predation mortality
+information from the MizerSim
object, sim
,
+that we created above. This is easily done by using the
+getPredMort()
function (see the help page for more
+details). There are several functions that can be used for extracting
+information from a MizerSim
object,
+e.g. getFeedingLevel()
and getFMort()
. For
+more information see the
+section on exploring the simulation results. Here we just call
+getPredMort()
using the sim
object:
+pred_mort <- getPredMort(sim)
The resulting pred_mort
object is an array that contains
+the predation mortality at time by species by size. Here we only have
+one species so the species dimension is dropped, leaving us with a two
+dimensional array of time by size. We projected the model for \(50\) time steps but the length of the time
+dimension is \(51\) as the initial
+population is also included as a time step. We can get the index of the
+final time with
+idxFinalT(sim)
## [1] 51
+To pull out the predation mortality at size in the final time step we +use:
+
+pred_mort_final <- pred_mort[idxFinalT(sim), ]
If you plot this predation mortality on a log-log scale you can see +how the predation mortality declines to almost zero for the largest +sizes.
+
+plot(x = w(params), y = pred_mort_final, log = "xy", type = "l",
+ xlab = "Size [g]", ylab = "Predation mortality [1/year]")
Note how we used w(sim)
to get the vector of sizes to
+plot along the x-axis and how we specified that we wanted to have
+logarithmic axes.
In the long run it is worthwhile to use the ggplot2 package for
+creating plots, so we show also how you would create the above graph
+with ggplot()
. For more detail see the section on using ggplot2 and plotly with mizer.
+library(ggplot2)
+sd <- data.frame(x = w(params), y = pred_mort_final)
+ggplot(sd, aes(x = x, y = y)) +
+ geom_line() +
+ scale_x_log10() + scale_y_log10() +
+ xlab("Size [g]") + ylab("Predation mortality [1/year]")
## Warning: Transformation introduced infinite values in continuous y-axis
+It is possible to use the community model to simulate a trophic +cascade. To do this we need to perform two simulations, one with fishing +and one without.
+This means we need to consider how fishing is handled in
+mizer
. The newCommunityParams()
function
+automatically sets the fishing selectivity to have a knife-edge shape,
+with only individuals larger than 1 kg selected (the size at the
+knife-edge can be changed by setting the knife_edge_size
+argument). Although it is possible to change the selectivity function,
+here we will use the default knife-edge selectivity. We set up the
+parameter object with default parameters:
+params_knife <- newCommunityParams()
First we perform a simulation without fishing in the same way we did
+above by setting the effort
argument to 0:
+sim0 <- project(params_knife, effort = 0, t_max = 50)
Now we want to simulate again, this time with fishing. In the +simulations, fishing mortality is calculated as the product of the +fishing selectivity, effort and catchability (see the section on fishing +gears for more details). By default catchability is set to 1. This +means that a fishing effort of 1 will result in a fishing mortality of +1/year for fully selected sizes. Here we run a simulation with fishing +effort set to 1 for the duration of the simulation:
+
+sim1 <- project(params_knife, effort = 1, t_max = 50)
You can compare the difference between these scenarios by using the
+plot()
method as before. Of particular interest is the
+fishing mortality at size. The knife-edge selectivity at 1000 g can be
+clearly seen and an effort of 1 has resulted in a fishing mortality of 1
+for the fully selected sizes.
+plot(sim1, power = 2)
To explore the presence of a trophic cascade, we are interested in
+looking at the relative change in abundance when the community is fished
+compared to when it is not fished. To do this we need to get the
+abundances at size from the simulation objects. This is done with the
+N()
function, which returns a three dimensional array with
+dimensions time x species x size. Here we have 51 time steps (50 from
+the simulation plus one which stores the initial population), 1 species
+and 100 sizes:
## [1] 51 1 100
+We want the abundances in the final time step, and we can use these +to calculate the relative abundances:
+ +For convenience, to save you from having to determine the index of
+the final time, mizer provides the function finalN()
, so we
+could have done
This can then be plotted using basic R plotting commands.
+
+plot(x = w(params), y = relative_abundance, log = "x", type = "n",
+ xlab = "Size (g)", ylab = "Relative abundance")
+lines(x = w(params), y = relative_abundance)
+lines(x = c(min(w(params)), max(w(params))), y = c(1, 1), lty = 2)
The impact of fishing on species larger than 1000g can be clearly +seen. The fishing pressure lowers the abundance of large fish (the +decrease in relative abundance at 1000 g). This then relieves the +predation pressure on their smaller prey (the preferred predator-prey +size ratio is given by the \(\beta\) +parameter, which is set to 100 by default), leading to an increase in +their abundance. This in turn increases the predation mortality on their +smaller prey, which reduces their abundance and so on.
+As described above, the \(\sigma\) +parameter determines the width of the predator prey size preference. +Here we take a look at how changing the value of \(\sigma\) can affect the dynamics of the +community.
+In the examples above, \(\sigma\) is
+set in the newCommunityParams()
function by default to a
+value of \(2\). We can see this by
+looking at the sigma
column of the species_params data
+frame that is contained in the MizerParams object:
+species_params(params)$sigma
## [1] 2
+When projected through time, the community abundances converge to a
+stable equilibrium. What happens if we reduce the value of \(\sigma\), for example by setting it to 1.0?
+We can do this by passing in the new value of \(\sigma\) into
+newCommunityParams()
.
+params_sigma1 <- newCommunityParams(sigma = 1)
We want to project this new model through time using the
+project()
function. Here we project the new parameter
+object for 50 time steps without fishing and save at intervals of 0.1
+years (t_save = 0.1
):
+sim_sigma1 <- project(params_sigma1, effort = 0, t_max = 50,
+ dt = 0.01, t_save = 0.1)
Note that we have introduced a new argument: \(dt\). This is the step size of the solver.
+It does not have anything to do with the biology in the model. It only
+affects the internal engine of project()
that performs the
+projection. As you can see in the underlying model equations in the model description section, the
+model is formulated in continuous time. Therefore, to project it
+forward, project()
must solve the system of equations using
+numerical methods. The quality of these methods is strongly affected by
+\(dt\). The default value of \(dt\) is 0.1, which will be fine for most of
+the projections we run in this document. Here it is necessary to reduce
+the value to 0.01 to avoid introducing any artefacts into the projected
+values. Decreasing \(dt\) increases the
+time it takes to run a projection.
Let’s take a look at how the abundances change through time. We can
+do this with the plotBiomass()
function:
+plotBiomass(sim_sigma1)
The plot above shows that abundances of the community no longer +converge to a stable equilibrium and the dynamics appear to be chaotic. +The ecological significance of the change in dynamics, and of the +ability of simple community models to show chaotic behaviour, is still +being debated. It can be argued that the size of the oscillations are +too large to be ‘true’. Additionally, when a trait-based model is +implemented, the magnitude of the oscillations are much smaller.
+The next section is about the trait based +model.
+Clicking on a question will reveal the answer.
+If you want to contribute some of your code to the official mizer +code at https://github.com/sizespectrum/mizer you need to first +create a branch in your repository that differs from the master branch +of sizespectrum/mizer only by the code that you want to contribute, not +by any other changes that you may have made in your fork. This answer +explains how.
+We assume that you have not followed the recommendation of keeping +your master branch identical with that at sizespectrum/master, because +if you had you could simply follow the procedure described in the “Working with git and GitHub” tutorial. +Given that your master branch differs from that at sizespectrum/mizer, +you need to create a new branch not from your master branch but directly +from that at sizespectrum/master. Follow the following steps:
+Go to the “Terminal” tab in RStudio. This should be the tab next +to the “Console” tab. If it is not there and you don’t know why not, +then you are possibly running an old version of RStudio and you will +want to update to a newer version, which is easy from the Help menu +under “Check for Updates”.
Make sure you have set the upstream repository correctly. You can +check this with the command in the terminal
git remote -v
+If that does not list sizespectrum/mizer as the upstream repository +for both fetch and push, then you should execute the command
+git remote add upstream https://github.com/sizespectrum/mizer.git
+git fetch upstream
+git checkout -b name_of_your_branch upstream/master
+git push --set-upstream origin name_of_your_branch
+where you should replace both name_of_your_branch
with
+the actual name that you
+have chosen for you branch.
The recommended way of working in your fork of the mizer project is +to keep the master branch of your repository always in sync with the +master branch at https://github.com/sizespectrum/mizer, and to create a +feature branch for any of your code changes, as is explained in the “Creating a branch” +section of the “Working with git and GitHub” tutorial. Note however that +having a clean master branch is not a prerequisite to making pull +requests, see the answer to “How +can I prepare a pull request without a clean master branch?” +above.
+However if you would like to have a clean master branch, you can +follow these steps:
+In the dialog box enter a name for your new branch and click +“Create”.
+Go to the “Terminal” tab in RStudio. This should be the tab next +to the “Console” tab. If it is not there and you don’t know why not, +then you are possibly running a very old version of RStudio and you will +want to update to a newer version, which is easy from the Help menu +under “Check for Updates”.
Make sure you have set the upstream repository correctly. You can +check this with the command in the terminal
git remote -v
+If that does not list sizespectrum/mizer as the upstream repository +for both fetch and push, then you should execute the command
+git remote add upstream https://github.com/sizespectrum/mizer.git
+git checkout master
+git pull upstream master
+git reset --hard upstream/master
+git push origin master --force
+Now both your local repository and your GitHub repository have the +master branch identical to the official master branch. You can now +create new feature branches by branching off from this master branch in +preparation for submitting pull requests, as explained in the “Working with git and GitHub” +tutorial.
+You can of course still get at your modified code by switching to the +branch you created in the first step above. +This guide is for you if you need to extend mizer to meet the needs
+of your research project. You will already have read the mizer model
+description in vignette("model_description")
and thus be
+familiar with what mizer can do out of the box. You now want to
+implement the extension or modification of the model required for your
+research, and for that you need to dive into the internal workings of
+mizer. This guide is meant to make that as easy as possible.
The first thing you should do, even before reading this guide, is to +go to https://github.com/sizespectrum/mizer/issues and create +a new “issue”” to share your ideas and plans with the mizer community. +You may get back valuable feedback and advice. Another way to get in +touch with the mizer community is via the size-spectrum +modelling Google group.
+In this section we describe how to set up your working environment to +allow you to easily work with the mizer code. Much of it you may already +have in place, so feel free to skip ahead.
+Mizer is compatible with R versions 3.1 and later. +If you still need to install +R, simply install the latest version. This guide was prepared with R +version 4.3.2 (2023-10-31).
+This guide assumes that you will be using RStudio to +work with R. There is really no reason not to use RStudio and it makes a +lot of things much easier. RStudio develops rapidly and adds useful +features all the time and so it pays to upgrade to the latest +version frequently. This guide was written with version +1.2.1268.
+Mizer is developed using the version control system +Git and the code is hosted on GitHub. To contribute to +the mizer code, you need to have the Git software installed on your +system. On most Linux machines it will be installed already, but on +other platforms you need to install it. You do +not need to install any GUI for git because RStudio has built-in +support for git. A good place to learn about using Git and GitHub is +this chapter in the guide +by Hadley on R package development.
+To work with the code you will create your own git repository with a +copy of the mizer code. Go to https://github.com/sizespectrum/mizer and fork it into +your own repository by clicking the “Fork” button.
+You will be prompted to log in to GitHub. If you do not yet have an +account, you need to create one for yourself.
+Once you have your own fork of the mizer repository, you create a +local copy on whichever machine you work on. You can do this from within +RStudio. For this you click on the “Project” drop-down and then select +“New Project…”.
+Provided you have Git installed and RStudio was able to find it you +can then choose “Git” on the next dialog box.
+If the Git option is not showing, the you need to troubleshoot, and +perhaps https://happygitwithr.com/rstudio-see-git.html +helps.
+In the next dialog box you let RStudio know where to find your fork. +You can copy that to the clipboard by pressing the button next to the +URL and then paste it back into the RStudio dialog box. In that dialog +box you can also change where RStudio stores the repository on your +machine. Choose anywhere convenient. Then click “Create Project”.
+To work with R packages as a developer, you will need to install +additional tools.
+Once you have these tools in place, you should install the devtools +and roxygen2 packages with
+
+install.packages(c("devtools", "roxygen2"))
You are now all set to develop R packages, and RStudio +makes this extra easy. There is even a cheat sheet “Package +Development with devtools” accessible from the Help menu in RStudio.
+To set things up, click on Build -> More -> Configure Build +Tools.
+In the resulting dialog box, tick the checkboxes “Use devtools +package functions if available” and “Generate documentation with +roxygen” and then click on “Configure”.
+This will open another dialog box where you tick “Install and +Restart”.
+Hit “OK”. While you are on the Project Options dialog box, click on +“Code Editing” and then check “Insert spaces for tabs” and set “Tab +width” to 4, because that is the convention the mizer code follows.
+Many useful commands for working with packages and with GitHub are +provided by the usethis +package, which is automatically installed along with devtools. The +package comes with a useful vignette with suggestions for how +to set things up most conveniently.
+You are now ready to install the mizer package using the development +code from GitHub. First you should do this with the command
+
+devtools::install_github("sizespectrum/mizer")
You can watch the progress in the “Build” tab. Once the build has +completed, you will see that in the console RStudio automatically +runs
+ +to load you freshly built mizer package. You will want to click +“Install and Restart” whenever you have changed your local code.
+You will be making your code changes in your fork of the mizer code +(we are using the so-called Fork & Pull model). From time to time +you will want to interact with the main mizer repository in two +ways:
+You will want to contribute some of your code back to the mizer +project, so that it benefits others and also so that it gets +automatically included in future releases.
You will want to be able to merge new developments made in mizer +by others into your code base.
This interaction is made possible with git and GitHub.
+It initially takes a bit of effort to get the hang of how this works. +Therefore we have created a little tutorial “Working with git and GitHub” with an +exercise that will take you through all the necessary steps. Unless you +are already very familiar with git and GitHub, it will be worthwhile for +you to work through that tutorial now.
+We use testthat and shinytest.
+The test are in the directory tests/testthat
.
Some tests compare the results of calculations to the results the
+code gave in the past, using the
+testthat::expect_known_value()
test. The past values are
+stored in tests/testthat/values. If one of the tests gives a value that
+is different from the stored value, then the test throws an error
+and overwrites the stored value with the new result. The second
+time the test is run, it then no longer fails. Luckily the original
+values will still be in the git repository. So after you think you have
+fixed the error that led to the wrong result, you should revert to the
+old stored values before re-running the test. Reverting to the old
+stored values is easy: Just go to the Git tab in RStudio, select the
+changed files in tests/testthat/values
(select, not tick),
+then right-click and choose Revert.
It may be that the change in the result of a calculation is intended, +perhaps because your new code is more accurate than the old code. If you +are 100% certain of this, but only then, should you commit the changed +files in tests/testthat/values, so that these new values form the basis +of future comparison tests.
+Plots are tested with the vdiffr package. When a plot has +changed, you should run vdiffr::manage_cases(), which will start a shiny +gadget where you can view the changes in the plot.
+This section is still in an early stage of development.
+Mizer is organised in a modular fashion. It is separated into setup +functions, simulation functions, and analysis and plotting +functions.
+There are several different functions for setting up a
+MizerParams
object for specifying various concrete models.
+These setup functions make various simplifying assumptions about the
+model parameters to reduce the amount of information that needs to be
+specified. This usually takes the form of assuming allometric scaling
+laws.
The core of mizer is the project()
function that runs a
+simulation of the size-spectrum model. It takes a specification of the
+model contained in an object of type MizerParams
and
+returns the results of the simulation in an object of type
+MizerSim
.
There are many functions for analysing and plotting the results of a
+mizer simulation contained in a MizerSim
object.
The MizerParams
and MizerSim
objects are S4
+objects, meaning that their slots are rigorously defined and are
+accessed with the ‘@’ notation. You do not need to learn about S4
+classes in order to understand the mizer code, because the code avoids
+using S4 methods. In the presentation below we assume that the
+MizerParams
object is called params
and the
+MizerSim
object is called sim
.
An object of class ‘MizerParams’ holds all the information needed for +the ‘project()’ function to simulate a model.
+If you need to add a new slot to the MizerParams class, you need to
+make the following additions in the file
+MizerParams-class.R
:
slots
list inside
+setClass
.emptyParams()
go to the section “Make
+object” and inside the call to new()
provide a default
+value for your slot. If your slot holds an array, then it is
+conventional in mizer to already give it the correct dimensions and
+dimnames here, if possible.What exactly to put into these places is usually clear in analogy to +what is already there for other similar slots.
+ +We should try to avoid committing too many very large files to the +git repository, because the larger the repository the longer it takes to +download. Currently (September 2019) the repository is still at a +manageable 110 MiB. The best way to check the size of the repository is +with the git-sizer, see https://github.com/github/git-sizer/
+We are currently storing the mizer website in the mizer repository +(in the docs subdirectory). That is convenient, partly because that is +where pkgdown puts it by default and GitHub serves it from there. +However in the future we might consider moving the website to its own +repository.
+The mizer website at [https://sizespectrum.org/mizer/] is created
+programmatically from the documentation included in the mizer GitHub
+repository. Thus to edit the website just means editing this
+documentation. The website creation is performed by a slight
+modification of the pkgdown
+package. You can trigger a build in your local repository by issuing the
+commands
+devtools::install_github("gustavdelius/pkgdown", ref = "mizer")
+pkgdown::build_site()
This will convert all existing documentation into html files and put
+these into the docs
subdirectory of your mizer directory.
+In detail:
All .Rmd files in the vignettes
subdirectory (and
+its subdirectories, if any) will produce html files of the same name in
+docs/articles
.
All .Rd documentation files in man
will produce html
+files of the same name in docs/reference
.
The NEWS
file will be converted to
+docs/news/index.html
The README.md file will be converted to
+docs/index.html
.
Then the package will use the information in the
+_pkgdown.yml
file to create a navbar and index pages. The
+navbar is configured with
navbar:
+ title: "mizer"
+ left:
+ - icon: fa-home fa-lg
+ href: index.html
+ - text: "Get Started"
+ href: articles/mizer.html
+ - text: "Articles"
+ href: articles/index.html
+ - text: "Reference"
+ href: reference/index.html
+ - text: "Publications"
+ href: articles/publications.html
+ - text: "News"
+ href: news/index.html
+ right:
+ - icon: fa-github fa-lg
+ href: https://github.com/sizespectrum/mizer
+This is quite self-explanatory, specifying the order of the navbar +entries, their text and link destination, and whether they are on the +left or right side. It is clearly easy to add additional navbar entries. +It would be possible to also include drop-down items in navbar. See the +pkgdown +documentation for more details.
+The file docs/articles/index.html
is created according
+to the article: section in the _pkgdown_yml
file. At an
+early stage it looked as follows:
articles:
+- title: User Guides
+ contents:
+ - model_description
+- title: Developer Guides
+ contents:
+ - developer_vignette
+ - mathematical_details
+By now it has more entries to link to more vignettes. It is not +necessary to use this index file. The menu entry could just as well +point to a more elaborate file created from an .Rmd file in the vignette +subdirectory, which might become useful in the future.
+The file docs/reference/index.html
is created according
+to the reference: section in the _pkgdown_yml
file, with a
+similar syntax as the article: section.
At the top of every page on the website, except the homepage, there
+is a link to the page source. So it is easy to know which file to edit
+to make changes to a page. If you want to rebuild the website after
+making a change to a single page you may not want to re-build
+everything. You can rebuild just a single vignette. For example to
+rebuild this page after editing the vignette
+vignette/editing_website.Rmd
you can call
+pkgdown::build_article("editing_website")
To rebuild the reference pages after changing the roxygen code doc +you can run
+
+pkgdown::build_reference()
To change the homepage you edit index.md. This file is very similar +to README.md that is used on the mizer homepage on GitHub, but can +contain extra content like embedded videos that the GitHub homepage does +not support. After changes to index.md you can update your local copy of +the website with
+
+pkgdown::build_home()
Of course these changes will affect only your local copy of the +website. To get those changes reflected on the online site you will need +to make a pull request for your changed files against the master branch +of the upstream repository.
+The website is hosted with GitHubPages. In the settings pages +of the mizer repository on GitHub the source is set to “master +branch/docs folder”. This means that only the master branch controls the +website.
+The search bar on the website is powered by Docsearch from Algolia. +This is a free service for open-source projects. The set-up is explained +in the pkgdown +documentation.
+It is easy to embed videos into a webpage by putting an iframe into +the R Markdown source file. For example putting the line
+<iframe width="560" height="315" src="https://www.youtube.com/embed/0RlXqLbFbWc" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
+into the source of this page produced the following embedded +video:
+ +The GitHub site itself (as opposed to GitHub Pages) does not support +embedded videos.
+vignettes/exploring_the_simulation_results.Rmd
+ exploring_the_simulation_results.Rmd
In the sections on the multispecies
+model and on running a
+simulation we saw how to set up a model and project it forward
+through time under our desired fishing scenario. The result of running a
+projection is an object of class MizerSim
. What do we then
+do? How can we explore the results of the simulation? In this section we
+introduce a range of summaries, plots and indicators that can be easily
+produced using functions included in mizer.
We will use the following MizerSim
object for these
+examples, where the effort array is the one we created in the previous section on running a
+simulation:
+sim <- project(NS_params, effort = effort_array, dt = 0.1, t_save = 1)
The projected species abundances at size through time can be obtained
+with N(sim)
. This returns a three-dimensional array (time x
+species x size). Consequently, this array can get very big so inspecting
+it can be difficult. In the example we have just run, the time dimension
+of n
has 10 rows (one for the initial population and then
+one for each of the saved time steps). There are also 12 species each
+with 100 sizes. We can check this by running the dim()
+function and looking at the dimensions of the n
array:
## [1] 10 12 100
+To pull out the abundances of a particular species through time at +size you can subset the array. For example to look at Cod through time +you can use:
+
+N(sim)[, "Cod", ]
This returns a two-dimensional array: time x size, containing the cod
+abundances. The time dimension depends on the value of the argument
+t_save
when project()
was run. You can see
+that even though we specified dt
to be 0.1 when we called
+project()
, the t_save = 1
argument has meant
+that the output is only saved every year.
Often we are particularly interested in the results at the final +time-step. These we can access with
+
+finalN(sim)
which is a two dimensional array (species x size).
+The projected resource abundances can be accesses similarly with
+
+NResource(sim)
This returns a two-dimensional array (time x size). And if we are +only interested in the final time step
+
+finalNResource(sim)
returns a vector with one entry for each size class.
+As well as the summary()
methods that are available for
+both MizerParams
and MizerSim
objects, there
+are other useful summary functions to pull information out of a
+MizerSim
object. A description of the different summary
+functions available is given in the summary functions help
+page.
All of these functions have help files to explain how they are used.
+(It is also possible to use most of these functions with a
+MizerParams
object if you also supply the population
+abundance as an argument. This can be useful for exploring how changes
+in parameter value or abundance can affect summary statistics and
+indicators. We won’t explore this here but you can see their help files
+for more details.)
The functions getBiomass()
and getN()
have
+additional arguments that allow the user to set the size range over
+which to calculate the summary statistic. This is done by passing in a
+combination of the arguments min_l
, min_w
,
+max_l
and max_w
for the minimum and maximum
+length or weight. If min_l
is specified there is no need to
+specify min_w
and so on. However, if a length is specified
+(minimum or maximum) then it is necessary for the species parameter
+data.frame (see the
+species parameters section) to include the parameters a
+and b
for length-weight conversion. It is possible to mix
+length and weight constraints, e.g. by supplying a minimum weight and a
+maximum length. The default values are the minimum and maximum weights
+of the spectrum, i.e. the full range of the size spectrum is used.
Here we show a simple demonstration of using a summary function using
+the sim
object we created earlier. Here, we use
+getSSB()
to calculate the SSB of each species through time
+(note the use of the head()
function to only display the
+first few rows).
## [1] 10 12
+
+head(ssb)
## sp
+## time Sprat Sandeel N.pout Herring Dab
+## 1 210810187886 5.378411e+12 183159668755 442644004208 6885675636
+## 2 157421300798 3.498137e+12 153386837865 499200353502 5278650740
+## 3 138701220818 2.510551e+12 135619248024 603931625682 5065030019
+## 4 118875574040 1.997337e+12 110306292341 569167018628 4877123531
+## 5 114060666493 1.790051e+12 112246876589 420615452917 5224602753
+## 6 125640651552 1.736155e+12 124869964868 351468531499 6147959412
+## sp
+## time Whiting Sole Gurnard Plaice Haddock Cod
+## 1 113572753243 63630241776 9102232924 303365911438 151965178029 531536722956
+## 2 87964359889 46775076902 7385280731 232689238362 117830523964 345850203280
+## 3 91749455062 45443905458 7528148453 240988454909 135946957263 290183064993
+## 4 85038348880 49465062866 6855105119 264670808435 158905104522 308445205952
+## 5 77139941423 53555703121 5600934574 279557468426 145778058562 335438386336
+## 6 82462061701 56441766035 5398238488 278206590917 123593721267 351386199474
+## sp
+## time Saithe
+## 1 327578246185
+## 2 218500695506
+## 3 197610219846
+## 4 228220656964
+## 5 271860341040
+## 6 287805766571
+As mentioned above, we can specify the size range for the
+getsummaryBiomass()
and getN()
functions. For
+example, here we calculate the total biomass of each species but only
+include individuals that are larger than 10 g and smaller than 1000
+g.
+biomass <- getBiomass(sim, min_w = 10, max_w = 1000)
+head(biomass)
## sp
+## time Sprat Sandeel N.pout Herring Dab
+## 1 244119195586 4.589606e+12 238847649463 1.273446e+12 8373096367
+## 2 189464604834 2.888858e+12 214678099536 1.413601e+12 6837801607
+## 3 169593586308 1.931520e+12 185655249238 1.536104e+12 6536986610
+## 4 146468867085 1.409300e+12 166879189209 1.360238e+12 6286073743
+## 5 144115814704 1.171670e+12 178630603519 1.175016e+12 6849215327
+## 6 159924185834 1.102235e+12 192414189537 1.177930e+12 7904848054
+## sp
+## time Whiting Sole Gurnard Plaice Haddock
+## 1 161515844306 127400523235 24759887586 766087469917 333172493301
+## 2 140923805330 112319321368 24087806247 722705573485 335440217763
+## 3 139784785126 114244854064 23828541236 743105121821 372682335506
+## 4 127051769924 115827036011 20324716177 711903001258 356182595515
+## 5 126548669345 116366841999 19072116393 661973266258 323469227911
+## 6 137220141191 121684387886 21334281004 668659838352 325245994096
+## sp
+## time Cod Saithe
+## 1 45159646196 158969500338
+## 2 52110333051 183496280557
+## 3 62885974091 216448253249
+## 4 59073484678 214031942473
+## 5 51993520691 174593364166
+## 6 49544506290 138713867303
+Functions are available to calculate a range of indicators from a
+MizerSim
object after a projection. A description of the
+different indicator functions available is given in the indicator functions
+help page.. You can read the help pages for each of the functions
+for full instructions on how to use them, along with examples.
With all of the functions in the table it is possible to specify the
+size range of the community to be used in the calculation (e.g. to
+exclude very small or very large individuals) so that the calculated
+metrics can be compared to empirical data. This is used in the same way
+that we saw with the function getBiomass()
in the section
+on summary functions for MizerSim objects.. It is also possible to
+specify which species to include in the calculation. See the help files
+for more details.
For these examples we use the sim
object we created
+earlier.
The slope of the community can be calculated using the
+getCommunitySlope()
function. Initially we include all
+species and all sizes in the calculation (only the first five rows are
+shown):
+slope <- getCommunitySlope(sim)
+head(slope)
## slope intercept r2
+## 1 -0.7822250 25.40779 0.8722251
+## 2 -0.7970084 25.24922 0.8666363
+## 3 -0.8066332 25.21573 0.8665750
+## 4 -0.8151679 25.23791 0.8678893
+## 5 -0.8229285 25.24489 0.8686275
+## 6 -0.8272602 25.23104 0.8673588
+This gives the slope, intercept and \(R^2\) value through time (see the help file
+for getCommunitySlope
for more details).
We can include only the species we want with the species
+argument. Below we only include demersal species. We also restrict the
+size range of the community that is used in the calculation to between
+10 g and 5 kg. The species
argument is a character vector
+of the names of the species that we want to include in the
+calculation.
+dem_species <- c("Dab", "Whiting", "Sole", "Gurnard", "Plaice", "Haddock",
+ "Cod", "Saithe")
+slope <- getCommunitySlope(sim, min_w = 10, max_w = 5000,
+ species = dem_species)
+head(slope)
## slope intercept r2
+## 1 -1.096584 26.88942 0.9749307
+## 2 -1.177678 27.21999 0.9796408
+## 3 -1.148456 27.13231 0.9754412
+## 4 -1.060903 26.70050 0.9748899
+## 5 -1.026889 26.50463 0.9820432
+## 6 -1.061542 26.68677 0.9807863
+R is very powerful when it comes to exploring data through plots. Two
+useful packages for plotting are ggplot2
and
+plotly
. These use data.frames for input data whereas many
+of the mizer functions return arrays or matrices. Fortunately it is
+straightforward to turn arrays and matrices into data.frames using the
+melt()
function from the reshape2
package that
+mizer makes available to you. Although mizer
does include
+some dedicated plots, it is definitely worth your time getting to grips
+with these other plotting packages. This will make it possible for you
+to make your own plots. We provide some details in the section on using ggplot2 and plotly with mizer.
Included in mizer
are several dedicated plots that use
+MizerSim
objects as inputs (see the plots help page.).
+As well as displaying the plots, these functions all return objects of
+type ggplot
from the ggplot2
package, meaning
+that they can be further modified by the user (e.g. by changing the
+plotting theme). See the help page of the individual plot functions for
+more details. The generic plot()
method has also been
+overloaded for MizerSim
objects. This produces several
+plots in the same window to provide a snapshot of the results of the
+simulation.
Some of the plots plot values by size (for example
+plotFeedingLevel()
and plotSpectra()
). For
+these plots, the default is to use the data at the final time step of
+the projection. With these plotting functions, it is also possible to
+specify a different time, or a time range to average the values over
+before plotting.
Using the plotting functions is straightforward. For example, to plot
+the total biomass of each species against time you use the
+plotBiomass()
function:
+plotBiomass(sim)
As mentioned above, some of the plot functions plot values against +size at a point in time (or averaged over a time period). For these +plots it is possible to specify the time step to plot, or the time +period to average the values over. The default is to use the final time +step. Here we plot the abundance spectra (biomass), averaged over time = +5 to 10:
+
+plotSpectra(sim, time_range = 5:10)
As mentioned above, and as we have seen several times in this guide,
+the generic plot()
method has also been overloaded. This
+produces 5 plots in the same window (plotFeedingLevel()
,
+plotBiomass()
, plotPredMort()
,
+plotFMort()
and plotSpectra()
). It is possible
+to pass in the same arguments that these individual plots use,
+e.g. arguments to change the time period over which the data is
+averaged.
+plot(sim)
The next section describes how to use what we have learned to model the North +Sea.
+In this tutorial you will gain an understanding of size-spectrum dynamics. To separate the size-spectrum effects from the multi-species effects, we will concentrate on a single species.
+The mizer package implements multi-species dynamic Size-spectrum models in R. It has been +designed for modelling aquatic ecosystems.
+Using mizer is relatively simple. There are four main stages, each +described in more detail in sections below.
+If you run into any difficulties or have any questions or +suggestions, let us know about it by posting about it on our issue +tracker. You can also twitter to @mizer_model. We love to hear +from you.
+A good way to get into mizer is to follow the online mizer course. This course has +three parts, each consisting of several tutorials with example 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]((https://mizer.course.sizespectrum.org/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.
Click on this preview to open a mizer cheat sheet.
There is a series +of YouTube videos by Richard Southwell about mizer which are however +no longer entirely up-to-date.
+If you already have R installed on your computer, then installation +of the mizer package is very simple (assuming you have an active +internet connection). Just start an R session and then type:
+
+install.packages("mizer")
After installing mizer, to actually use it, you need to load the
+package using the library()
function. Note that whilst you
+only need to install the package once, it will need to be loaded every
+time you start a new R session.
Mizer is compatible with R versions 3.1 and later. You can install R +on your computer by following the instructions at https://cran.r-project.org/ for your particular +platform.
+Alternatively, if you can not or do not want to install R on your +computer, you can also work with R and RStudio in your internet browser +by creating yourself a free account at https://rstudio.cloud. There you can then install mizer +as described above. Running mizer in the RStudio Cloud may be slightly +slower than running it locally on your machine, but the speed is usually +quite acceptable.
+This guide assumes that you will be using RStudio to work with R. +There is really no reason not to use RStudio and it makes a lot of +things much easier. RStudio develops rapidly and adds useful features +all the time and so it pays to upgrade to the latest +version frequently. This guide was written with version 1.3.820.
+The source code for mizer is hosted on GitHub. If you are +feeling brave and wish to try out a development version of mizer you can +install the package from here using the R package devtools (which was +used extensively in putting together mizer). If you have not yet +installed devtools, do
+
+install.packages("devtools")
Then you can install the latest version from GitHub using
+
+devtools::install_github("sizespectrum/mizer")
devtools::install_github()
function you can
+also install code from forked mizer repositories or from other branches
+on the official repository.
+With mizer it is possible to implement many different types of +size-spectrum models using the same basic tools and methods.
+Setting the model parameters is done by creating an object of
+class ? MizerParams
. This includes model parameters such as
+the life history parameters of each species, and the fishing gears. For
+each type of sizespectrum model there is a function for creating a new
+MizerParams object, newSingleSpeciesParams()
,
+newCommunityParams()
, newTraitParams()
and
+newMultispeciesParams()
.
These functions make reasonable default choices for many of the model +parameters that you do not want to specify explicitly. For example to +set up a simple model (described more in the Community +Model section) you can even let mizer choose all the parameters for +you.
+
+params <- newCommunityParams()
For a more complicated multi-species model you need to provide a data +frame with some species parameters. An example of a North Sea model is +included with the package. Here we also use a species interaction matrix +for the North Sea species.
+
+params <- newMultispeciesParams(NS_species_params, NS_interaction)
## Because you have n != p, the default value for `h` is not very good.
+## Because the age at maturity is not known, I need to fall back to using
+## von Bertalanffy parameters, where available, and this is not reliable.
+## No ks column so calculating from critical feeding level.
+## Using z0 = z0pre * w_max ^ z0exp for missing z0 values.
+## Using f0, h, lambda, kappa and the predation kernel to calculate gamma.
+The notes printed out by the function show us that mizer calculated +default values for many parameters that were not provided.
+This is done by calling the project()
function (as in
+“project forward in time”) with the model parameters.
+sim <- project(params, t_max = 10, effort = 1)
This produces an object of class MizerSim
which contains
+the results of the simulation. In this example we chose to set some
+parameters of the project()
function to specify that we
+want to project 10 years into the future, under the assumption of unit
+fishing effort. You can see the help page for project()
for
+more details and it is described fully in the
+section on running a simulation.
After a simulation has been run, the results can be examined using a
+range of ?plotting_functions
,
+?summary_functions
and ?indicator_functions
.
+The plot()
function combines several of these plots into
+one:
+plot(sim)
Just as an example: we might be interested in how the proportion of +large fish varies over time. We can get the proportion of Herrings in +terms of biomass that have a weight above 50g in each of the 10 +years:
+
+getProportionOfLargeFish(sim,
+ species = "Herring",
+ threshold_w = 50,
+ biomass_proportion = TRUE)
## 0 1 2 3 4 5 6 7
+## 0.8241807 0.1858698 0.7586884 0.6780193 0.3325129 0.3036110 0.4717033 0.6379461
+## 8 9 10
+## 0.5976582 0.5539731 0.5763835
+We can then use the full power of R to work with these results.
+The functionality provided by mizer to explore the simulation results +is more fully described in the +section on exploring the simulation results.
+Size spectrum models have emerged as a conceptually simple way to +model a large community of individuals which grow and change trophic +level during life. There is now a growing literature describing +different types of size spectrum models (e.g. Benoît and +Rochet 2004; K. H. Andersen and Beyer 2006; K. H. +Andersen et al. 2008; Law et al. 2009; Hartvig 2011; Hartvig, Andersen, +and Beyer 2011). The models can be used to understand how +marine communities are organised (K. H. Andersen +and Beyer 2006; K. H. Andersen, Beyer, and Lundberg 2009; Blanchard et al. +2009) and how they respond to fishing (K. H. Andersen and Rice 2010; K. H. Andersen and +Pedersen 2010). This section introduces the central +assumptions, concepts, processes, equations and parameters of size +spectrum models.
+Roughly speaking there are four versions of the size spectrum +modelling framework of increasing complexity: The single-species +model, the community model (Benoît and Rochet 2004; Maury et al. +2007; Blanchard et al. 2009; Law et al. +2009), the trait-based model +(K. H. Andersen and Beyer 2006; K. H. Andersen and +Pedersen 2010), and the multispecies model (Hartvig, Andersen, and Beyer 2011). The +single-species, community and trait-based models can be considered as +simplifications of the multispecies model. This section focuses on the +multispecies model but is also applicable to the other types of models. +Mizer is able to implement all types of model using similar +commands.
+Size spectrum models are a subset of physiologically structured +models (Metz and Diekmann 1986; De Roos +and Persson 2001) as growth (and thus maturation) is food +dependent, and processes are formulated in terms of individual level +processes. All parameters in the size spectrum models are related to +individual weight which makes it possible to formulate the model with a +small set of general parameters, which has prompted the label +``charmingly simple’’ to the model framework [Pope et al. (2006)}.
+The model framework builds on the central assumption that an +individual can be characterized by its weight \(w\) and its species number \(i\) only. The aim of the model is to +calculate the size- and trait-spectrum \({\cal +N}_i(w)\) which is the density of individuals such that \({\cal N}_i(w)dw\) is the number of +individuals in the interval \([w:w+dw]\). Scaling from individual-level +processes of growth and mortality to the size spectrum of each trait +group is achieved by means of the McKendrick-von Foerster equation, +which is simply a continuity equation that describes the flow of biomass +up the size spectrum, \[\begin{equation} + \frac{\partial N_i(w)}{\partial t} + \frac{\partial g_i(w) +N_i(w)}{\partial w} = -\mu_i(w) N_i(w) +\end{equation}\] where individual growth \(g_i(w)\) and mortality \(\mu_i(w)\) are coupled, because growth of +one individual is due to predation on another, who consequently +dies.
+The continuity equation is supplemented by a boundary condition at +the egg weight \(w_0\) where the flux +of individuals (numbers per time) \(g_i(w_0) +N_i(w_0)\) is determined by the reproduction of offspring by +mature individuals in the population \(R_i\): \[\begin{equation} + g_i(w_0)N_i(w_0) = R_i. +\end{equation}\]
+The rest of the formulation of the model rests on a number of +``standard’’ assumptions from ecology and fisheries science about how +encounters between predators and prey leads to growth \(g_i(w)\) and reproduction \(R_i\) of the predators, and mortality of +the prey \(\mu_i(w)\).
+For a more detailed exposition of the model see the section The mizer size-spectrum model.
+It is easiest to learn the basics of mizer through examples. We do +this by looking at four set-ups of the framework, of increasing +complexity. For each one there is an article that describes how to set +up the model, run it in different scenarios, and explore the results. We +recommend that you explore these in the following order:
+The single-species +model is a good starting point because it allows one to understand +the main features of size-spectrum modelling without the complexities of +multi-species interactions. The model describes a single species in a +fixed background community. It allows exploration of how the shape of +the species size spectrum is determined by the growth and death rates of +individuals of that species. The article gives you a first glimpse of +how to work with mizer, but in a simplified setting.
+In the community +model, individuals are only characterized by their size and are +represented by a single group representing an across-species average. +Community size spectrum models have been used to investigate how +abundance size spectra emerge solely from the individual-level process +of size-based predation and how fishing impacts metrics of +community-level size spectra. Since few parameters are required it has +been used for investigating large-scale community-level questions where +detailed trait- and species-level parameterisations are not +tractable.
+The trait-based +model resolves a continuum of species with varying maximum sizes. +The maximum size is considered to be the most important trait that +characterizes a species’ life history. The continuum is represented by a +discrete number of species spread evenly over the range of maximum +sizes. The number of species is not important and does not affect the +general dynamics of the model. Many of the parameters, such as the +preferred predator-prey mass ratio, are the same for all species. Other +model parameters are determined by the maximum size. For example, the +weight at maturation of each species is a set fraction of the maximum +size. In the trait-based model, species-level complexity is captured +through different life histories, and both intra- and inter-specific +size spectra emerge. This approach is powerful for examining the generic +population and whole community level responses to both size and species +selective fishing without the requirement for detailed species-specific +parameters.
+In the multispecies +model individual species are resolved in detail and each has +distinct life history, feeding and reproduction parameters. More +detailed information is required to parameterise the multispecies model +but the approach can be used to address management strategies for a +realistic community in a specific region or subset of interacting +species.
+All models predict abundance, biomass and yield as well as predation +and mortality rates at size. They are useful for establishing baselines +of abundance of unexploited communities, for understanding how fishing +impacts aquatic communities and for testing indicators that are being +developed to support an ecosystem approach to fisheries management.
+Which model to use in a specific case depends on needs and on the +amount of information available to calibrate the model. The +multi-species model could be set up for most systems where calibration +parameters can be estimated. This requires a lot of insight and data. If +the parameters are just guesstimates the results of the multi-species +model will be no more accurate than the results from the trait-based +model. In such situations we therefore recommend the use of the +trait-based model, even though it only provides general information +about the maximum size distribution and not about specific species.
+The community model is useful for large-scale community-level +questions where only the average spectrum is needed. Care should be +taken when the community model is used to infer the dynamical properties +of marine ecosystems, since it is prone to unrealistically strong +oscillations due to the lack of dampening effects provided by the +life-history diversity in the trait-based and multi-species models.
+The single-species model is mainly of pedagogical use and for +comparison to other single-species fisheries models. But an important +aim of size-spectrum modelling is to get away from single-species +thinking.
+ + + + + +vignettes/model_description.Rmd
+ model_description.Rmd
On this page we present the details of the mizer model, taking care
+to separate the essential features of the model that are hard-coded and
+the various possible specialisations for which mizer provides setup
+functions. We will provide links to the functions that can be used to
+set or change the various model parameters (all collected together in
+setParams()
) as well as to the functions that calculate the
+various ecological rates in the model (all collected together in
+mizerRates()
) because the help pages of these functions
+will provide useful additional details.
The model assumes that, to a first approximation, an individual can +be characterized by its weight \(w\) +and its species number \(i\) only. The +aim of the model is to calculate the size spectrum \(N_i(w)\), which is the density of +individuals of species \(i\) such that +\(\int_w^{w+dw}N_i(w)dw\) is the +number of individuals of species \(i\) in the size interval \([w,w+dw]\). In other words: the number of +individuals in a size range is the area under the number density \(N_i(w)\).
+Here is a plot of an example size spectrum for two species with \(N_i(w)\) on the vertical axis for \(i=1,2\) and \(w\) on the horizontal axis.
+
+library(mizer)
+params <- newTraitParams(no_sp = 2, min_w = 1e-3)
+plotSpectra(params, resource = FALSE, power = 0)
To represent this continuous size spectrum in the computer, the size
+variable \(w\) is discretized into a
+vector w
of discrete weights, providing a grid of sizes
+spanning the range from the smallest egg size to the largest maximum
+size. These grid values divide the full size range into a finite number
+of size bins. The size bins should be chosen small enough to avoid the
+discretisation errors from becoming too big. You can fetch this vector
+with w()
and the vector of bin sizes with
+dw()
.
The weight grid is set up to be logarithmically spaced, so that
+w[j]=w[1]*10^(j*dx)
for some fixed dx
. This
+means that the bin widths increase with size:
+dw[j] = w[j+1] - w[j] = w[j] * (10^dx - 1)
. This grid is
+set up automatically when creating a MizerParams object.
In the code the size spectrum is stored as an array N
+such that N[i, a]
holds the density \(N_i(w_a)\) at weights \(w_a=\)w[a]
, or, if time
+dependence is included, an array such that N[i, a, u]
holds
+\(N_i(w_a,t_u)\). See
+N()
.
Note that, contrary to what one might have expected,
+N[i, a]
is not the number of individuals in a size
+bin but the density at a grid point. The number of individuals
+in the size bin between w[a]
and
+w[a+1]=w[a]+dw[a]
is only approximately given as
+N[i, a]*dw[a]
, where dw[a]= w[a+1]-w[a]
.
Of course all these calculations with discrete sizes and size bins
+are only giving approximations to the continuous values, and these
+approximations get better the smaller the size bins are, i.e., the more
+size bins are used. This is why the functions setting up MizerParams
+objects allow you to choose the number of size bins
+no_w
.
A good way to think about the time evolution of the number density +\(N_i(w)\) is to consider the familiar +situation of traffic density on the roads. A fish’s life is a journey +along the size axis from egg size to size at death. There are many other +fish making the same journey. The speed with which the fish move along +the size axis is their growth rate. This is quite analogous to the speed +of traffic on a road. Just as the speed of cars depends on the density +of other cars on the road, the growth rate of fish depends on the +density of other fish. When the density of cars on a stretch of road is +high, their speed decreases, and this leads to a pile-up and can lead to +traffic jams. Similarly when the density of fish in a size interval is +high, their growth rate goes down due to competition for the same food +sources, and this too can lead to pile-ups and bottlenecks. So one just +has to replace the space variable in a traffic model by size to get an +equation for the fish size spectra.
+The additional feature in the evolution of fish densities that is not +present in road traffic density is that fish can die while they are +growing up, and this death rate is also dependent on the density of +other fish.
+The time evolution of the number density \(N_i(w)\) is described by the McKendrick-von +Foerster equation, which is a transport equation (as one would use for +traffic density) but with an additional loss term due to fish +mortality:
+\[\begin{equation} + \label{eq:MvF} + \frac{\partial N_i(w)}{\partial t} + \frac{\partial g_i(w) +N_i(w)}{\partial w} + = -\mu_i(w) N_i(w), +\end{equation}\]
+where individual growth \(g_i(w)\) +is described below in the Growth section and +mortality \(\mu_i(w)\) is described in +the Mortality section.
+There is no need for you to understand the mathematical notation used +in this equation to understand its origin: it just says that the rate at +which the number of fish in a size bracket changes is the rate at which +fish grow into the size bracket from a smaller size minus the rate at +which fish grow out of it to a larger size minus the rate at which the +fish in the size bracket die. So to simulate the size spectrum dynamics +we need to specify the growth rates and the mortality rates. This we +will do below. The important point is that these rates depend on the +density of other fish of other sizes, making the size-spectrum dynamics +non-linear and non-local in very interesting ways. The resulting effects +are too complicated to disentangle by pure thought. This is where +simulations with the mizer package come in.
+This McKendrick-von Foerster equation is approximated in mizer by a
+finite-difference method. This allows the project()
+function in mizer to project the size spectrum forwards in time: Given
+the spectrum at one time the project()
function calculates
+it at a set of later times.
Of course there also needs to be reproduction into the smallest size +class, otherwise there would be no small fish any more after a while. So +for the smallest size class instead of a rate of growth into the size +class there is a rate of reproduction of new individuals into that size +class. This reproduction will be described below in the Reproduction section.
+Besides the fish spectrum there is also a resource spectrum \(N_R(w)\), representing for example the +phytoplankton. This spectrum starts at a smaller size than the fish +spectrum, in order to provide food also for the smallest individuals +(larvae) of the fish spectrum. By default the time evolution of the +resource spectrum is described by a semi-chemostat equation.
+The semichemostat dynamics are given by \[\begin{equation}
+ \label{eq:nb}
+ \frac{\partial N_R(w,t)}{\partial t}
+ = r_R(w) \Big[ c_R (w) - N_R(w,t) \Big] - \mu_R(w) N_R(w,t).
+\end{equation}\] Here \(r_R(w)\)
+is the resource regeneration rate and \(c_R(w)\) is the carrying capacity in the
+absence of predation. These parameters are changed with
+setResource()
. By default mizer assumes allometric forms
+\[r_R(w)= r_R\, w^{n-1}.\] \[c_R(w)=\kappa\, w^{-\lambda}.\] You can
+retrieve these with getResourceRate()
and
+getResourceCapacity()
respectively. It is also possible to
+implement other resource dynamics, as described in the help page for
+setResource()
. The mortality \(\mu_R(w)\) is due to predation by consumers
+and is described in the subsection Resource mortality.
Because the resource spectrum spans a larger range of sizes these +sizes Because the resource spectrum spans a larger range of sizes these +sizes Because the resource spectrum spans a larger range of sizes these +sizes are discretized into a different vector of weights
+The resource spectrum is then represented by a vector
+NResource
such that NResource[c]
=\(N_R(\)w_full[c]
\()\).
Consumers can grow only by consuming prey. In the next few +subsections we will build towards determining the growth rate resulting +from predation. We will discuss how we model the predator-prey encounter rate, the resulting rate of +consumption, the rate of metabolic losses, and the partitioning of +the remaining energy into reproduction and growth.
+The rate at which a predator of species \(i\) and weight \(w\) encounters food (mass per time) is
+determined by summing over all prey species and the resource spectrum
+and integrating over all prey sizes \(w_p\), weighted by the selectivity factors:
+\[\begin{equation}
+ \label{eq:1}
+ E_{i}(w) = \gamma_i(w) \int \left(\sum_{j} \theta_{ij} N_j(w_p) +
+ \theta_{iR} N_R(w_p) \right)
+ \phi_i(w,w_p) w_p \, dw_p.
+\end{equation}\] This is calculated by
+getEncounter()
. The overall prefactor \(\gamma_i(w)\) sets the predation power of
+the predator. It could be interpreted as a search volume. It is set by
+setSearchVolume()
. By default it is assumed to scale
+allometrically as \(\gamma_i(w) = \gamma_i\,
+w^q.\)
The \(\theta\) matrix sets the
+interaction strength between predators and the various prey species and
+resource. It is changed with setInteraction()
.
The size selectivity is encoded in the predation kernel \(\phi_i(w,w_p)\). This is changed with
+setPredKernel()
.
An important simplification occurs when the predation kernel \(\phi_i(w,w_p)\) depends on the size of the +prey only through the predator/prey size ratio \(w_p/w\), \[\phi_i(w, w_p)=\tilde{\phi}_i(w/w_p).\] +This is assumed by default but can be overruled. The default for the +predation kernel is the truncated log-normal function \[ + \label{eq:4} + \tilde{\phi}_i(x) = \begin{cases} + \exp \left[ \dfrac{-(\ln(x / \beta_i))^2}{2\sigma_i^2} \right] + &\text{ if }x\in\left[0,\beta_i\exp(3\sigma_i)\right]\\ + 0&\text{ otherwise,} + \end{cases} +\] where \(\beta_i\) is the +preferred predator-prey mass ratio and \(\sigma_i\) sets the width of the predation +kernel.
+The integral in the expression for the encounter rate is approximated +by a Riemann sum over all weight brackets: \[ +{\tt encounter}[i,a] = {\tt search\_vol}[i,a]\sum_{k} +\left( n_{R}[k] + \sum_{j} \theta[i,j] n[j,k] \right) + \phi_i\left(w[a],w[k]\right) w[k]\, dw[k]. +\] In the case of a predation kernel that depends on \(w/w_p\) only, this becomes a convolution +sum and can be evaluated efficiently via fast Fourier transform.
+The encountered food is consumed subject to a standard Holling +functional response type II to represent satiation. This determines the +feeding level \(f_i(w)\), +which is a dimensionless number between 0 (no food) and 1 (fully +satiated) so that \(1-f_i(w)\) is the +proportion of the encountered food that is consumed. The feeding level +is given by
+\[\begin{equation} + \label{eq:f} + f_i(w) = \frac{E_i(w)}{E_i(w) + h_i(w)}, +\end{equation}\]
+where \(h_i(w)\) is the maximum
+consumption rate. This is changed with setMaxIntakeRate()
.
+By default mizer assumes an allometric form \(h_i(w) = h_i\, w^n.\) The feeding level is
+calculated with the function getFeedingLevel()
.
The rate at which food is consumed is then \[\begin{equation} +(1-f_i(w))E_{i}(w)=f_i(w)\, h_i(w). +\end{equation}\]
+Some of the consumed food is used to fuel the needs for metabolism
+and activity and movement, at a rate \({\tt
+metab}_i(w)\). By default this is made up out of standard
+metabolism, scaling with exponent \(p\), and loss due to activity and movement,
+scaling with exponent \(1\): \[{\tt metab}_i(w) = k_{s.i}\,w^p +
+k_i\,w.\] See the help page for
+setMetabolicRate()
.
The remaining rate, if any, is assimilated with an efficiency \(\alpha_i\) and is then available for growth
+and reproduction. So the rate at which energy becomes available for
+growth and reproduction is \[\begin{equation}
+ \label{eq:Er}
+ E_{r.i}(w) = \max(0, \alpha_i f_i(w)\, h_i(w) - {\tt metab}_i(w))
+\end{equation}\] This is calculated with the
+getEReproAndGrowth()
function.
A proportion \(\psi_i(w)\) of the
+energy available for growth and reproduction is used for reproduction.
+This proportion should change from zero below the weight \(w_{m.i}\) of maturation to one at the
+maximum weight \(w_{max.i}\), where all
+available energy is used for reproduction. This function is changed with
+setReproduction()
. Mizer provides a default form for the
+function which you can however overrule.
What is left over after metabolism and reproduction is taken into
+account is invested in somatic growth. Thus the growth rate is \[\begin{equation}
+ \label{eq:growth}
+ g_i(w) = E_{r.i}(w)\left(1-\psi_i(w)\right).
+\end{equation}\] It is calculated by the
+getEGrowth()
function.
When food supply does not cover the requirements of metabolism and +activity, growth and reproduction stops, i.e. there is no negative +growth. The individual should then be subjected to a starvation +mortality, but starvation mortality is not implemented in core mizer but +is provided by the mizerStarvation +extension package.
+The mortality rate of an individual \(\mu_i(w)\) has three sources: predation +mortality \(\mu_{p.i}(w)\), background +mortality \(\mu_{ext.i}(w)\) and +fishing mortality \(\mu_{f.i}(w)\).
+Predation mortality is calculated such that all that is eaten
+translates into corresponding predation mortalities on the ingested prey
+individuals. Recalling that \(1-f_j(w)\) is the proportion of the food
+encountered by a predator of species \(j\) and weight \(w\) that is actually consumed, the rate at
+which all predators of species \(j\)
+consume prey of size \(w_p\) is \[\begin{equation}
+ \label{eq:pred_rated}
+ {\tt pred\_rate}_j(w_p) = \int \phi_j(w,w_p) (1-f_j(w))
+ \gamma_j(w) N_j(w) \, dw.
+\end{equation}\] This predation rate is calculated by the
+function getPredRate()
.
The integral is approximated by a Riemann sum over all fish weight +brackets. \[ +{\tt pred\_rate}[j,c] = \sum_{a} + {\tt pred_kernel}[j,a,c]\,(1-{\tt feeding_level}[j,a])\, + \gamma[j,a]\,n[j,a]\,dw[a]. +\]
+The mortality rate due to predation is then obtained as \[\begin{equation}
+ \label{eq:mup}
+ \mu_{p.i}(w_p) = \sum_j {\tt pred\_rate}_j(w_p)\, \theta_{ji}.
+\end{equation}\] This predation mortality rate is calculated by
+the function getPredMort()
.
External mortality \(\mu_{ext.i}(w)\) is independent of the
+abundances and is changed with setExtMort()
. By default
+mizer assumes that the external mortality for each species is a constant
+\(z0_i\) independent of size. The value
+of \(z0_i\) is either specified as a
+species parameter or it is assumed to depend allometrically on the
+maximum size: \[z0_i = z0_{pre}
+w_{\infty.i}^{1-n}.\]
The fishing parameters for the model are set up with
+setFishing()
, where you can find the details of how to set
+up gears with different selectivities and the capabilities of different
+species. Fishing mortality \(\mu_{f.i}(w)\) is calculated with the
+function getFMort()
.
The total mortality rate \[\mu_i(w)=\mu_{p.i}(w)+\mu_{ext,u}(w)+\mu_{f.i}(w)\]
+is calculated with the function getMort()
.
The predation mortality rate on resource is given by a similar
+expression as the predation mortality on fish: \[\begin{equation}
+ \label{eq:mupp}
+ \mu_{p}(w_p) = \sum_j {\tt pred\_rate}_j(w_p)\, \theta_{jp}.
+\end{equation}\] This is the only mortality on resource currently
+implemented in mizer. It is calculated with the function
+getResourceMort()
.
The total rate of investment into reproduction (grams/year) is found
+by integrating the contribution from all individuals of species \(i\), each of which invests a proportion
+\(\psi_i(w)\) of their consumption.
+This total rate of energy investment can then be converted to a total
+rate of egg production \(R_{p.i}\)
+(numbers per year): \[\begin{equation}
+ \label{eq:Rp}
+ R_{p.i} = \frac{\epsilon}{2 w_0} \int N_i(w) E_{r.i}(w) \psi_i(w) \,
+dw,
+\end{equation}\] Here the total rate of investment is multiplied
+by an efficiency factor \(\epsilon\)
+and then dividing by the egg weight \(w_0\) to convert the energy into number of
+eggs. The result is multiplied by a factor \(1/2\) to take into account that only
+females reproduce. This rate of potential egg production is calculated
+with getRDI()
.
Three important density-dependent mechanisms widely assumed in +fisheries models are automatically captured in the mizer model that lead +to an emergent stock-recruitment relationship:
+However there are other sources of density dependence that are not +explicitly modelled mechanistically in mizer. An example would be a +limited carrying capacity of suitable spawning grounds and other spatial +effects.
+This requires additional phenomenological density dependent +contributions to the stock-recruitment. In mizer this type of density +dependence is modelled through constraints on egg production and +survival. The default functional form of this density dependence is +represented by a reproduction rate \(R_i\) (numbers per time) that approaches a +maximum as the energy invested in reproduction increases, modelled +mathematically it is analogous to a Beverton-Holt type +function:
+\[\begin{equation}
+ \label{eq:R}
+ R_i = R_{\max.i} \frac{R_{p.i}}{R_{p.i} + R_{\max.i}},
+\end{equation}\] where \(R_{\max.i}\) is the maximum reproduction
+rate of each trait class. This final rate of reproduction is calculated
+with getRDD()
.
This default Beverton-Holt type is implemented by
+BervertonHoldRDD()
but mizer also provides alternatives
+RickerRDD()
, SheperdRDD()
,
+constantRDD()
and noRDD()
. Also, users are
+able to write their own functions, e.g. hockey-stick. See
+setReproduction()
for details.
The reproduction rate \(R_i\) +determines the abundance density of species \(i\) at egg size. Intuitively, the rate at +which individuals of size \(w_0\) is +produced must equal the rate at which they grow away from size \(w_0\), which gives \[N_i(w_0) = \frac{R_i}{g_i(w_0)}.\]
+The previous sections have used wrapper functions to set up
+MizerParams objects that are appropriate for single-species, community-
+and trait-based models. We now turn our attention to multispecies, or
+species-specific, models. These are potentially more complicated than
+the community and trait-based models and use the full power of the
+mizer
package.
In multispecies type models multiple species are resolved. However, +unlike in the trait-based model which also resolves multiple species, +the species parameters will be those of real-world species. There are +several advantages to this approach. As well as investigating the +community as a whole (as was done for the community and trait-based +models), we are able to investigate the dynamics of individual species. +This means that species specific management rules can be tested and +species specific metrics, such as yield, can be compared to reference +levels.
+A multispecies model can take more effort to set up. For example, +each species will have different life-history parameters; there may be +multiple gear types with different selectivities targeting different +groups of species; the fishing effort of each gear may change with time +instead of just being constant (which has been the case in the +simulations we have looked at so far); the interactions between the +species needs to be considered.
+In later sections we build up a multispecies model for the North Sea.
+To effectively use mizer
for a multispecies model we are
+going to have to take a closer look at the MizerParams
+class and the project()
function. This will all be done in
+the context of examples so hopefully everything will be clear.
We also take a closer look at some of the summary plots and analyses +that can be performed, for example, calculating a range of size-based +indicators.
+The MizerParams class is used for storing model parameters. We have +already met the MizerParams class when we looked at community and +trait-based models. However, to set up a multispecies model we will need +to specify many more parameters.This is probably the most complicated +part of using the mizer package, so we will take it slowly.
+A MizerParams
object stores the:
Note that the MizerParams
class does not store any
+parameters that can vary through time, such as fishing effort or
+population abundance. These are stored in the MizerSim
+class which we will come to later in the section on running a
+simulation.
Although the MizerParams
class contains a lot of
+information, it is relatively straightforward to set up and use. Objects
+of class MizerParams
are created using the constructor
+method newMultispeciesParams()
(this constructor method was
+called MizerParams() in previous version of mizer). This constructor
+method can take many arguments. However, creation is simplified because
+many of the arguments have default values.
In the rest of this section we look at the main arguments to the
+newMultispeciesParams()
function. To help understand how
+the constructor is used and how the MizerParams
class
+relates to the equations given in the
+model description section, there is an example section where we
+create example parameter objects using data that comes with the
+mizer
package.
Although many of the arguments used when creating a
+MizerParams
object are optional, there is one argument that
+must be supplied by the user: the species specific parameters.
+These are stored in a single data.frame
object. The
+data.frame
is arranged species by parameter, so each column
+is a parameter and each row has the parameters for one of the species in
+the model. Although it is possible to create the data.frame by hand in
+R, it is probably easier to create the data externally as a .csv file
+(perhaps using a suitable open source spreadsheet such as LibreOffice)
+and then read the data into R.
For each species in the model community there are certain parameters +that are essential and that do not have default values. The user must +provide values for these parameters. There are also some essential +parameters that have default values, such as the selectivity function +parameters, and some that are calculated internally using default +relationships if not explicitly provided. These defaults are used if the +parameters are not found in the data.frame.
+The essential columns of the species parameters data.frame that have
+no default values are: species
, the names of the species in
+the community and w_max
, the maximum mass of the
+species.
In mizer
, fishing mortality is imposed on species by
+fishing gears. The total fishing mortality is obtained by summing over
+the mortality from all gears, \[\begin{equation}
+% {#eq:muf}
+ \mu_{f.i}(w) = \sum_g F_{g,i}(w),
+\end{equation}\] where the fishing mortality \(F_{g,i}(w)\) imposed by gear \(g\) on species \(i\) at size \(w\) is calculated as: \[\begin{equation}
+% {#eq:sel}
+ F_{g,i}(w) = S_{g,i}(w) Q_{g,i} E_{g}
+\end{equation}\] where \(S\) is
+the selectivity by species, gear and size, \(Q\) is the catchability by species and gear
+and \(E\) is the fishing effort by
+gear. The selectivity at size has a range between 0 (not selected at
+that size) to 1 (fully selected at that size). Catchability is used as
+an additional scalar to make the link between gear selectivity, fishing
+effort and fishing mortality. For example, it can be set so that an
+effort of 1 gives a desired fishing mortality. In this way effort can
+then be specified relative to a ‘base effort’, e.g. the effort in a
+particular year.
Selectivity and catchability are stored as arrays in the MizerParams
+object. However the user does not have to create these arrays by hand if
+they provide a data frame with the necessary information. In particular
+the selectivity can be calculate by specifying functions for the
+selectivity curves. Mizer provides a range of such selectivity functions
+and the user just needs to specify their parameters for each gear and
+each species in the gear_params
data frame. All the details
+can be found on the help page for setFishing()
.
Fishing effort is not stored in the MizerParams object. Instead, +effort is set when the simulation is run and can vary through time (see +the section on running a +simulation).
+MizerParams
objects
+As mentioned in the preceding sections, an object of
+MizerParams
is created by using the
+newMultispeciesParams()
constructor method.
The first step is to prepare the species specific parameter +data.frame. As mentioned above, one way of doing this is to use a +spreadsheet and save it as a .csv file. We will use this approach here. +An example .csv file has been included in the package. This contains the +species parameters for a multispecies North Sea model. The location of +the file can be found by running
+
+params_location <- system.file("extdata", "NS_species_params.csv",
+ package = "mizer")
This file can be opened with most spreadsheets or a text editor for +you to inspect. This can be loaded into R with
+
+species_params <- read.csv(params_location)
This reads the .csv file into R in the form of a data.frame. You can
+check this with the class
:
+class(species_params)
## [1] "data.frame"
+Let’s have a look at the data frame:
+
+species_params
## species w_inf w_mat beta sigma R_max k_vb
+## 1 Sprat 33 13 51076 0.8 7.38e+11 0.681
+## 2 Sandeel 36 4 398849 1.9 4.10e+11 1.000
+## 3 N.pout 100 23 22 1.5 1.05e+13 0.849
+## 4 Herring 334 99 280540 3.2 1.11e+12 0.606
+## 5 Dab 324 21 191 1.9 1.12e+10 0.536
+## 6 Whiting 1192 75 22 1.5 5.48e+11 0.323
+## 7 Sole 866 78 381 1.9 3.87e+10 0.284
+## 8 Gurnard 668 39 283 1.8 1.65e+12 0.266
+## 9 Plaice 2976 105 113 1.6 4.08e+14 0.122
+## 10 Haddock 3485 165 558 2.1 1.84e+12 0.271
+## 11 Cod 40044 1606 66 1.3 8.26e+09 0.216
+## 12 Saithe 16856 1076 40 1.1 1.12e+11 0.175
+You can see that there are \(12\)
+species and \(7\) columns of
+parameters: species
,
+w_max
,w_mat
,beta
,sigma
,R_max
+and k_vb
.
Of these parameters, species
and w_max
are
+essential and have no default values (as described in the
+section on species parameters). w_max
is the maximum
+size of the species, w_mat
is its maturity size, and
+beta
and sigma
are parameters of the predation
+kernel (by default mizer uses a log-normal predation kernel).
+R_max
is a parameter introducing additional density
+dependence into reproduction parameter using a Beverton-Holt type
+function (see setReproduction()
for details). The final
+column, k_vb
, will be used to calculate values for
+h
and then gamma
. This column is only
+essential here because the h
and gamma
are not
+included in the data.frame. It would also have been possible to include
+h
and gamma
columns in the data.frame and not
+include the k_vb
column.
The values of the non-essential species specific parameters, like for
+example alpha
, k
, ks
,
+z0
, w_min
and erepro
, were not
+included in the data.frame. This means that the default values will be
+automatically used when we create the MizerParams
+object.
For this example we will not set up gear parameters. There are no
+columns describing the fishing selectivity. There is no
+sel_func
column to determine the selectivity function. This
+means that the default selectivity function, knife_edge
,
+will be used. As mentioned in the section on fishing
+gears, this function also needs another argument,
+knife_edge_size
. This is not present in the data.frame and
+so it will be set to the default value of w_mat
. Also,
+there is no catchability
column so a default value for
+catchability
of 1 will be used for all gears and
+species.
To create the MizerParams
object we pass the species
+parameter data.frame into the newMultispeciesParams()
+constructor method:
+params <- newMultispeciesParams(species_params)
## Warning in validSpeciesParams(species_params): The species parameter data frame
+## is missing a `w_max` column. I am copying over the values from the `w_inf`
+## column. But note that `w_max` should be the maximum size of the largest
+## individual, not the asymptotic size of an average indivdidual.
+
+## Warning in validSpeciesParams(species_params): The species parameter data frame
+## is missing a `w_max` column. I am copying over the values from the `w_inf`
+## column. But note that `w_max` should be the maximum size of the largest
+## individual, not the asymptotic size of an average indivdidual.
+## Because you have n != p, the default value for `h` is not very good.
+## Because the age at maturity is not known, I need to fall back to using
+## von Bertalanffy parameters, where available, and this is not reliable.
+## No ks column so calculating from critical feeding level.
+## Using z0 = z0pre * w_max ^ z0exp for missing z0 values.
+## Using f0, h, lambda, kappa and the predation kernel to calculate gamma.
+We have just created a MizerParams
object:
+class(params)
## [1] "MizerParams"
+## attr(,"package")
+## [1] "mizer"
+The MizerParams object also stores a copy of the species parameter
+data frame that we provided. We can look at it with
+species_params()
:
+species_params(params)
## species w_inf w_mat beta sigma R_max k_vb n p w_max
+## Sprat Sprat 33 13 51076 0.8 7.38e+11 0.681 0.6666667 0.7 33
+## Sandeel Sandeel 36 4 398849 1.9 4.10e+11 1.000 0.6666667 0.7 36
+## N.pout N.pout 100 23 22 1.5 1.05e+13 0.849 0.6666667 0.7 100
+## Herring Herring 334 99 280540 3.2 1.11e+12 0.606 0.6666667 0.7 334
+## Dab Dab 324 21 191 1.9 1.12e+10 0.536 0.6666667 0.7 324
+## Whiting Whiting 1192 75 22 1.5 5.48e+11 0.323 0.6666667 0.7 1192
+## Sole Sole 866 78 381 1.9 3.87e+10 0.284 0.6666667 0.7 866
+## Gurnard Gurnard 668 39 283 1.8 1.65e+12 0.266 0.6666667 0.7 668
+## Plaice Plaice 2976 105 113 1.6 4.08e+14 0.122 0.6666667 0.7 2976
+## Haddock Haddock 3485 165 558 2.1 1.84e+12 0.271 0.6666667 0.7 3485
+## Cod Cod 40044 1606 66 1.3 8.26e+09 0.216 0.6666667 0.7 40044
+## Saithe Saithe 16856 1076 40 1.1 1.12e+11 0.175 0.6666667 0.7 16856
+## w_min alpha interaction_resource pred_kernel_type h k ks
+## Sprat 0.001 0.6 1 lognormal 14.51026 0 1.598545
+## Sandeel 0.001 0.6 1 lognormal 28.36951 0 3.250607
+## N.pout 0.001 0.6 1 lognormal 30.69918 0 3.318311
+## Herring 0.001 0.6 1 lognormal 31.20041 0 3.212332
+## Dab 0.001 0.6 1 lognormal 34.68295 0 3.760307
+## Whiting 0.001 0.6 1 lognormal 32.78322 0 3.406676
+## Sole 0.001 0.6 1 lognormal 24.90951 0 2.585095
+## Gurnard 0.001 0.6 1 lognormal 22.29126 0 2.367448
+## Plaice 0.001 0.6 1 lognormal 17.71691 0 1.820523
+## Haddock 0.001 0.6 1 lognormal 40.62144 0 4.111691
+## Cod 0.001 0.6 1 lognormal 74.81794 0 7.019866
+## Saithe 0.001 0.6 1 lognormal 43.50194 0 4.136466
+## z0 q gamma w_mat25 m erepro
+## Sprat 0.18705957 0.7166667 5.765885e-11 11.647460 1 1
+## Sandeel 0.18171206 0.7166667 4.267142e-11 3.583834 1 1
+## N.pout 0.12926608 0.7166667 9.749884e-11 20.607045 1 1
+## Herring 0.08647736 0.7166667 2.812559e-11 88.699888 1 1
+## Dab 0.08735805 0.7166667 7.663981e-11 18.815128 1 1
+## Whiting 0.05658819 0.7166667 1.041177e-10 67.196884 1 1
+## Sole 0.06294752 0.7166667 5.308445e-11 69.884760 1 1
+## Gurnard 0.06863713 0.7166667 5.091838e-11 34.942380 1 1
+## Plaice 0.04171321 0.7166667 4.774060e-11 94.075638 1 1
+## Haddock 0.03957464 0.7166667 7.679024e-11 147.833146 1 1
+## Cod 0.01753768 0.7166667 2.549664e-10 1438.909287 1 1
+## Saithe 0.02340093 0.7166667 1.797143e-10 964.051303 1 1
+We can see that this returns the original species data.frame (with
+w_max
and so on), plus any default values that may not have
+been included in the original data.frame. For example, we can see that
+there are now columns for alpha
and h
and
+gamma
etc.
Also note how the default fishing gears have been set up. Even though +we did not provide a gear parameter data frame, the MizerParams object +has one that we can access with
+
+gear_params(params)
## species gear sel_func knife_edge_size
+## Sprat, knife_edge_gear Sprat knife_edge_gear knife_edge 13
+## Sandeel, knife_edge_gear Sandeel knife_edge_gear knife_edge 4
+## N.pout, knife_edge_gear N.pout knife_edge_gear knife_edge 23
+## Herring, knife_edge_gear Herring knife_edge_gear knife_edge 99
+## Dab, knife_edge_gear Dab knife_edge_gear knife_edge 21
+## Whiting, knife_edge_gear Whiting knife_edge_gear knife_edge 75
+## Sole, knife_edge_gear Sole knife_edge_gear knife_edge 78
+## Gurnard, knife_edge_gear Gurnard knife_edge_gear knife_edge 39
+## Plaice, knife_edge_gear Plaice knife_edge_gear knife_edge 105
+## Haddock, knife_edge_gear Haddock knife_edge_gear knife_edge 165
+## Cod, knife_edge_gear Cod knife_edge_gear knife_edge 1606
+## Saithe, knife_edge_gear Saithe knife_edge_gear knife_edge 1076
+## catchability
+## Sprat, knife_edge_gear 1
+## Sandeel, knife_edge_gear 1
+## N.pout, knife_edge_gear 1
+## Herring, knife_edge_gear 1
+## Dab, knife_edge_gear 1
+## Whiting, knife_edge_gear 1
+## Sole, knife_edge_gear 1
+## Gurnard, knife_edge_gear 1
+## Plaice, knife_edge_gear 1
+## Haddock, knife_edge_gear 1
+## Cod, knife_edge_gear 1
+## Saithe, knife_edge_gear 1
+All species are caught by a gear called “knife_edge_gear”. The
+selectivity function for each fishing gear has been set in the
+sel_func
column to the default function,
+knife_edge()
. A catchability
column has been
+added with a default value of 1 for each of the species that the gear
+catches. An example of setting the catchability by hand can be seen in
+the section on the
+North Sea.
There is a summary()
method for MizerParams
+objects which prints a useful summary of the model parameters:
+summary(params)
## An object of class "MizerParams"
+## Consumer size spectrum:
+## minimum size: 0.001
+## maximum size: 40044
+## no. size bins: 100
+## Resource size spectrum:
+## minimum size: 8.6774e-13
+## maximum size: 9.84582
+## no. size bins: 171 (218 size bins in total)
+## Species details:
+## species w_max w_mat w_min beta sigma
+## 1 Sprat 33 13 0.001 51076 0.8
+## 2 Sandeel 36 4 0.001 398849 1.9
+## 3 N.pout 100 23 0.001 22 1.5
+## 4 Herring 334 99 0.001 280540 3.2
+## 5 Dab 324 21 0.001 191 1.9
+## 6 Whiting 1192 75 0.001 22 1.5
+## 7 Sole 866 78 0.001 381 1.9
+## 8 Gurnard 668 39 0.001 283 1.8
+## 9 Plaice 2976 105 0.001 113 1.6
+## 10 Haddock 3485 165 0.001 558 2.1
+## 11 Cod 40044 1606 0.001 66 1.3
+## 12 Saithe 16856 1076 0.001 40 1.1
+##
+## Fishing gear details:
+## Gear Effort Target species
+## ----------------------------------
+## knife_edge_gear 0.00 Sprat, Sandeel, N.pout, Herring, Dab, Whiting, Sole, Gurnard, Plaice, Haddock, Cod, Saithe
+As well as giving a summary of the species in the model and what gear
+is fishing what species, it gives a summary of the size structure of the
+community. For example there are \(100\) size classes in the community,
+ranging from \(0.001\) to \(4\times 10^{4}\) . These values are
+controlled by the arguments no_w
, min_w
and
+max_w
respectively. For example, if we wanted 200 size
+classes in the model we would use:
+params200 <- newMultispeciesParams(species_params, no_w = 200)
+summary(params200)
So far we have created a MizerParams
object by passing
+in only the species parameter data.frame argument. We did not specify an
+interaction matrix. The interaction matrix describes the interaction of
+each pair of species in the model. This can be viewed as a proxy for
+spatial interaction e.g. to model predator-prey interaction that is not
+size based. The values in the interaction matrix are used to scale the
+encountered food in [getEncounter()] and the predation mortality rate in
+[getPredMort()] (see the
+section on predator-prey encounter rate and on predation mortality).
The entries of the interaction matrix are dimensionless numbers +taking values are between 0 (species do not overlap and therefore do not +interact with each other) to 1 (species overlap perfectly). By default +mizer sets all values to 1, implying that all species fully interact +with each other, i.e. the species are spread homogeneously across the +model area.
+
+getInteraction(params)
## Warning: `getInteraction()` was deprecated in mizer 2.4.0.
+## ℹ Please use `interaction_matrix()` instead.
+## This warning is displayed once every 8 hours.
+## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
+## generated.
+## prey
+## predator Sprat Sandeel N.pout Herring Dab Whiting Sole Gurnard Plaice Haddock
+## Sprat 1 1 1 1 1 1 1 1 1 1
+## Sandeel 1 1 1 1 1 1 1 1 1 1
+## N.pout 1 1 1 1 1 1 1 1 1 1
+## Herring 1 1 1 1 1 1 1 1 1 1
+## Dab 1 1 1 1 1 1 1 1 1 1
+## Whiting 1 1 1 1 1 1 1 1 1 1
+## Sole 1 1 1 1 1 1 1 1 1 1
+## Gurnard 1 1 1 1 1 1 1 1 1 1
+## Plaice 1 1 1 1 1 1 1 1 1 1
+## Haddock 1 1 1 1 1 1 1 1 1 1
+## Cod 1 1 1 1 1 1 1 1 1 1
+## Saithe 1 1 1 1 1 1 1 1 1 1
+## prey
+## predator Cod Saithe
+## Sprat 1 1
+## Sandeel 1 1
+## N.pout 1 1
+## Herring 1 1
+## Dab 1 1
+## Whiting 1 1
+## Sole 1 1
+## Gurnard 1 1
+## Plaice 1 1
+## Haddock 1 1
+## Cod 1 1
+## Saithe 1 1
+For the North Sea this is not the case and so the model would be +improved by also including an interaction matrix which describes the +spatial overlap between species.
+An example interaction matrix for the North Sea has been included in
+mizer
as a .csv file. The location of the file can be found
+by running:
+inter_location <- system.file("extdata", "NS_interaction.csv",
+ package = "mizer")
Take a look at it in a spreadsheet if you want. As mentioned above,
+to read this file into R we can make use of the read.csv()
+function. However, this time we want the first column of the .csv file
+to be the row names. We therefore use an additional argument to the
+read.csv()
function: row.names
.
+inter <- read.csv(inter_location, row.names = 1)
+inter
## Sprat Sandeel N.pout Herring Dab Whiting
+## Sprat 0.72912919 0.03408440 0.06354517 0.27416982 0.36241552 0.26525924
+## Sandeel 0.03408440 0.68119882 0.04892432 0.05888214 0.09736663 0.07510011
+## N.pout 0.06354517 0.04892432 0.79660429 0.29755069 0.09088798 0.29989886
+## Herring 0.27416982 0.05888214 0.29755069 0.65890104 0.28963957 0.37373927
+## Dab 0.36241552 0.09736663 0.09088798 0.28963957 0.80817768 0.33389727
+## Whiting 0.26525924 0.07510011 0.29989886 0.37373927 0.33389727 0.70928230
+## Sole 0.29795558 0.06020860 0.01679020 0.20014139 0.38047464 0.19227455
+## Gurnard 0.17515576 0.05992649 0.30624141 0.27510627 0.22041200 0.37109904
+## Plaice 0.37065975 0.07801855 0.07855818 0.27791867 0.56492206 0.29503807
+## Haddock 0.08135547 0.09395730 0.54917554 0.34835469 0.13168065 0.39164787
+## Cod 0.33757969 0.09943453 0.32502256 0.40477930 0.41647801 0.44060879
+## Saithe 0.01681321 0.01609022 0.29498937 0.12620591 0.03138197 0.10228168
+## Sole Gurnard Plaice Haddock Cod Saithe
+## Sprat 0.29795558 0.17515576 0.37065975 0.08135547 0.33757969 0.01681321
+## Sandeel 0.06020860 0.05992649 0.07801855 0.09395730 0.09943453 0.01609022
+## N.pout 0.01679020 0.30624141 0.07855818 0.54917554 0.32502256 0.29498937
+## Herring 0.20014139 0.27510627 0.27791867 0.34835469 0.40477930 0.12620591
+## Dab 0.38047464 0.22041200 0.56492206 0.13168065 0.41647801 0.03138197
+## Whiting 0.19227455 0.37109904 0.29503807 0.39164787 0.44060879 0.10228168
+## Sole 0.71558049 0.10677895 0.39137317 0.03447799 0.25761229 0.01242055
+## Gurnard 0.10677895 0.88010500 0.16492120 0.35735444 0.35183282 0.12351994
+## Plaice 0.39137317 0.16492120 0.71922391 0.11248513 0.35043671 0.03294939
+## Haddock 0.03447799 0.35735444 0.11248513 0.85830725 0.39577341 0.26167470
+## Cod 0.25761229 0.35183282 0.35043671 0.39577341 0.78654705 0.20894496
+## Saithe 0.01242055 0.12351994 0.03294939 0.26167470 0.20894496 0.66383553
+We can set the interaction matrix in our existing MizerParams object
+params
with the setInteraction()
function:
+params <- setInteraction(params, interaction = inter)
Alternatively, instead of changing the interaction matrix in the
+existing MizerParams object, we could have created a new object from
+scratch with our interaction matrix by passing it to
+newMultispeciesParams()
:
+params_new <- newMultispeciesParams(species_params, interaction = inter)
## Warning in validSpeciesParams(species_params): The species parameter data frame
+## is missing a `w_max` column. I am copying over the values from the `w_inf`
+## column. But note that `w_max` should be the maximum size of the largest
+## individual, not the asymptotic size of an average indivdidual.
+
+## Warning in validSpeciesParams(species_params): The species parameter data frame
+## is missing a `w_max` column. I am copying over the values from the `w_inf`
+## column. But note that `w_max` should be the maximum size of the largest
+## individual, not the asymptotic size of an average indivdidual.
+## Because you have n != p, the default value for `h` is not very good.
+## Because the age at maturity is not known, I need to fall back to using
+## von Bertalanffy parameters, where available, and this is not reliable.
+## No ks column so calculating from critical feeding level.
+## Using z0 = z0pre * w_max ^ z0exp for missing z0 values.
+## Using f0, h, lambda, kappa and the predation kernel to calculate gamma.
+Note that the first argument must be the species parameters +data.frame. The remaining arguments can be in any order but should be +named. We are using the default values for all other parameters.
+We now have all we need to start running projections. Before we get +to that though, we’ll take a quick look at how different fishing gears +can be set up.
+In the above example, each species is caught by the same gear (named +“knife_edge_gear”). This is the default when no gear information is +provided.
+
+gear_params(params)
## species gear sel_func knife_edge_size
+## Sprat, knife_edge_gear Sprat knife_edge_gear knife_edge 13
+## Sandeel, knife_edge_gear Sandeel knife_edge_gear knife_edge 4
+## N.pout, knife_edge_gear N.pout knife_edge_gear knife_edge 23
+## Herring, knife_edge_gear Herring knife_edge_gear knife_edge 99
+## Dab, knife_edge_gear Dab knife_edge_gear knife_edge 21
+## Whiting, knife_edge_gear Whiting knife_edge_gear knife_edge 75
+## Sole, knife_edge_gear Sole knife_edge_gear knife_edge 78
+## Gurnard, knife_edge_gear Gurnard knife_edge_gear knife_edge 39
+## Plaice, knife_edge_gear Plaice knife_edge_gear knife_edge 105
+## Haddock, knife_edge_gear Haddock knife_edge_gear knife_edge 165
+## Cod, knife_edge_gear Cod knife_edge_gear knife_edge 1606
+## Saithe, knife_edge_gear Saithe knife_edge_gear knife_edge 1076
+## catchability
+## Sprat, knife_edge_gear 1
+## Sandeel, knife_edge_gear 1
+## N.pout, knife_edge_gear 1
+## Herring, knife_edge_gear 1
+## Dab, knife_edge_gear 1
+## Whiting, knife_edge_gear 1
+## Sole, knife_edge_gear 1
+## Gurnard, knife_edge_gear 1
+## Plaice, knife_edge_gear 1
+## Haddock, knife_edge_gear 1
+## Cod, knife_edge_gear 1
+## Saithe, knife_edge_gear 1
+Here, we look at an example where we set up four different gears:
+Industrial, Pelagic, Beam and Otter trawl, that catch different
+combinations of species. We can achieve that by only changing the
+gear
column in the gear_params
data frame.
+gear_params(params)$gear <- c("Industrial", "Industrial", "Industrial",
+ "Pelagic", "Beam", "Otter",
+ "Beam", "Otter", "Beam",
+ "Otter", "Otter", "Otter")
You can see the result by calling summary()
on the
+params
object.
+summary(params)
## An object of class "MizerParams"
+## Consumer size spectrum:
+## minimum size: 0.001
+## maximum size: 40044
+## no. size bins: 100
+## Resource size spectrum:
+## minimum size: 8.6774e-13
+## maximum size: 9.84582
+## no. size bins: 171 (218 size bins in total)
+## Species details:
+## species w_max w_mat w_min beta sigma
+## 1 Sprat 33 13 0.001 51076 0.8
+## 2 Sandeel 36 4 0.001 398849 1.9
+## 3 N.pout 100 23 0.001 22 1.5
+## 4 Herring 334 99 0.001 280540 3.2
+## 5 Dab 324 21 0.001 191 1.9
+## 6 Whiting 1192 75 0.001 22 1.5
+## 7 Sole 866 78 0.001 381 1.9
+## 8 Gurnard 668 39 0.001 283 1.8
+## 9 Plaice 2976 105 0.001 113 1.6
+## 10 Haddock 3485 165 0.001 558 2.1
+## 11 Cod 40044 1606 0.001 66 1.3
+## 12 Saithe 16856 1076 0.001 40 1.1
+##
+## Fishing gear details:
+## Gear Effort Target species
+## ----------------------------------
+## Industrial 0.00 Sprat, Sandeel, N.pout
+## Pelagic 0.00 Herring
+## Beam 0.00 Dab, Sole, Plaice
+## Otter 0.00 Whiting, Gurnard, Haddock, Cod, Saithe
+In this example the same gear now catches multiple stocks. For +example, the Industrial gear catches Sprat, Sandeel and Norway +Pout. Why would we want to set up the gears like this? In the next +section on running a multispecies +model we will see that to project the model through time you can +specify the fishing effort for each gear through time. By setting the +gears up in this way you can run different management scenarios of +changing the efforts of the fishing gears rather than on individual +species. It also means that after a simulation has been run you can +examine the catches by gear.
+Once the MizerParams
object has been properly set up, it
+may be the case that one wishes put the system in steady state.
+Sometimes this can be done simply by running the model using
+project()
until it reaches steady state. However, this
+method is not guaranteed to work, and there is a function called
+steady()
that is more reliable. The function
+steady()
must be supplied with a MizerParams object. It
+takes that MizerParams object, looks at the initial system state,
+computes the levels of reproduction of the different species, hold them
+fixed, and evolves the system until a steady state is reached (or more
+precisely, until the amount that the population abundances change during
+a time-step is below some small tolerance level). After this, the
+reproductive efficiency of each species is altered so that when the
+reproduction dynamics are turned back on (i.e., when we stop holding
+recruitment levels fixed), the values of the reproduction levels which
+we held the system fixed at will be realized. The steady function is not
+sure to converge, and the way it re-tunes the reproductive efficiency
+values may not be realistic, but the idea is to alter the other
+parameters in the system until steady()
does arrive at a
+steady state with sensible reproductive efficiency values.
Now that we know how to create a multispecies model we shall discuss +how to run a multispecies +model.
+In this tutorial we will use the ggplot2 package and the
plotly package for R to visualise the
+results from mizer simulations.
Mizer provides several functions for calculating summaries of the
+mizer simulation results, see ?summary_functions
. Many of
+these functions have corresponding plotting functions, see
+?plotting_functions
. However it is easy to produce
+customised plots directly using ggplot()
or
+plot_ly()
. This gives more flexibility than the built-in
+plotting functions. Also, you will occasionally want to look at
+different quantities for which perhaps there is not built-in plotting
+function. In those cases the examples you see below will provide a
+useful blueprint.
Both ggplot2 and plotly works with data frames, and the convenient +way to manipulate data frames is the dplyr package.
+ +We create a simple simulation that we will use for our examples +below.
+
+params <- newMultispeciesParams(NS_species_params)
+sim <- project(params, t_max = 10, t_save = 0.5, effort = 0)
Mizer likes to work with arrays indexed by species and time or size.
+For example the built-in summary functions return such arrays. These
+arrays need to be converted to data frames before they can be
+conveniently plotted with either ggplot()
or
+plot_ly
. This conversion is achieved by the function
+melt()
.
For example, the function getBiomass()
returns a
+two-dimensional array (matrix) with one dimension corresponding to the
+time and the second dimension to the species.
+biomass <- getBiomass(sim)
+str(biomass)
## num [1:21, 1:12] 8.77e+07 2.52e+09 3.21e+09 7.50e+08 2.09e+08 ...
+## - attr(*, "dimnames")=List of 2
+## ..$ time: chr [1:21] "0" "0.5" "1" "1.5" ...
+## ..$ sp : chr [1:12] "Sprat" "Sandeel" "N.pout" "Herring" ...
+This array can be converted with the melt()
function to
+a data frame that contains one row for each entry in the array.
## 'data.frame': 252 obs. of 3 variables:
+## $ time : num 0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 ...
+## $ sp : Factor w/ 12 levels "Sprat","Sandeel",..: 1 1 1 1 1 1 1 1 1 1 ...
+## $ value: num 8.77e+07 2.52e+09 3.21e+09 7.50e+08 2.09e+08 ...
+In this form the information can be handed to plot_ly()
+and converted to a line plot:
We have specified that the time is plotted along the x axis, the +value along the y axis and that the different species are represented by +the colours of the lines. Note the American spelling “color” required by +plotly.
+Alternatively we can do the same thing with
+ggplot()
:
Notice the different syntax for the ggplot2 and for plotly packages. +The underlying ideas are similar: they are both implementations of the +grammar of graphics. I recommend learning ggplot2 first, and switching +to plotly only when it has clear advantages (in particular for +animations, see below).
+The main advantage of plotly, namely the interactivity of the
+resulting figures, can be obtained also with the ggplot syntax by
+running the result of ggplot()
through the function
+ggplotly()
:
+ggplotly(pg)
Below we will always for each plot first give the ggplot code and +then the plotly code.
+We may want to add labels to the figure and to each of the axes. In
+ggplot this is done with labs()
.
+pg + labs(title = "Biomass plot",
+ x = "Time [years]",
+ y = "Biomass [g]")
In plotly we use the layout()
function.
We can use the filter function to filter out some of the data. For +example we could select only the data for specific species:
+ +Now if we plot this reduced data frame we get
+ +In the above we first converted the array to a data frame with
+melt()
and then selected the data of interest with
+filter()
. We could alternatively have first selected only
+the desired entries in the array and then created the data frame with
+melt()
from the resulting smaller array:
+nfr <- melt(getBiomass(sim)[, c("Gurnard", "Herring")])
+ggplot(nfr) +
+ geom_line(aes(x = time, y = value, color = sp))
The result looks almost identical, except that the colours associated +to the species have changed.
+If we want to make sure the same species always has the same colour, +we can use the colours specified by the MizerParams object
+
+getColours(params)
## Sprat Sandeel N.pout Herring Dab Whiting Sole
+## "#815f00" "#6237e2" "#8da600" "#de53ff" "#0e4300" "#430079" "#6caa72"
+## Gurnard Plaice Haddock Cod Saithe Resource Total
+## "#ee0053" "#007957" "#b42979" "#142300" "#a08dfb" "green" "black"
+## Background Fishing External
+## "grey" "red" "grey"
+To use these colours in ggplot we add
+scale_colour_manual()
:
+ggplot(biomass_frame) +
+ geom_line(aes(x = time, y = value, color = sp)) +
+ scale_colour_manual(values = getColours(params))
In plotly we add colors = getColours(params)r
to the
+add_lines()
command:
+plot_ly(biomass_frame) %>%
+ add_lines(x = ~time, y = ~value, color = ~sp,
+ colors = getColours(params))
Again note the American spelling “colors” required by plotly.
+Of course mizer has the plotSpectra()
function for
+plotting size spectra. Again it is instructional to create such plots by
+hand.
We can access the abundance spectra of the species via
+N(sim)
. This is a three-dimensional array (time x species x
+size). Let us first look at the abundance at the final time.
The drop = FALSE
means that the result will again be a 3
+dimensional array.
+str(final_n)
## num [1, 1:12, 1:100] 1.92e+06 4.81e+12 1.15e+14 3.86e+12 1.12e+11 ...
+## - attr(*, "dimnames")=List of 3
+## ..$ time: chr "10"
+## ..$ sp : chr [1:12] "Sprat" "Sandeel" "N.pout" "Herring" ...
+## ..$ w : chr [1:100] "0.001" "0.00119" "0.00142" "0.0017" ...
+We use the melt()
function to convert this array into a
+data frame.
+nf <- melt(final_n)
This has created a data frame with 4 variables and one observation
+for each of the 1200 entries in the 1
x 12 x
+rdim(final_n)[3]
matrix final_n
.
+str(nf)
## 'data.frame': 1200 obs. of 4 variables:
+## $ time : int 10 10 10 10 10 10 10 10 10 10 ...
+## $ sp : Factor w/ 12 levels "Sprat","Sandeel",..: 1 2 3 4 5 6 7 8 9 10 ...
+## $ w : num 0.001 0.001 0.001 0.001 0.001 0.001 0.001 0.001 0.001 0.001 ...
+## $ value: num 1.92e+06 4.81e+12 1.15e+14 3.86e+12 1.12e+11 ...
+The first three variables take their values from the dimension names
+of the array. Of course the time variable is the same for all
+observations, because we selected these before creating the data frame.
+The fourth variable called value
is the value of the entry
+of the array, so the abundance density in our case.
There are a lot of entries with value 0, which we are not really +interested in, so it makes sense to remove them:
+
+nf <- filter(nf, value > 0)
This leaves a data frame with only 945 observations.
+We can send this data frame to ggplot and add a line for the spectrum +for each species, with a different colour for each, and specify that we +want both the x axis and the y axis to be on a logarithmic scale.
+
+pg <- ggplot(nf) +
+ geom_line(aes(x = w, y = value, color = sp)) +
+ scale_x_log10() +
+ scale_y_log10()
+pg
The corresponding syntax for plotly is
+
+p <- plot_ly(nf) %>%
+ add_lines(x = ~w, y = ~value, color = ~sp) %>%
+ layout(xaxis = list(type = "log", exponentformat = "power"),
+ yaxis = list(type = "log", exponentformat = "power"))
+p
In the above we used the pipe operator %>%
which
+feeds the return value of one function into the first argument of the
+next function.
We can include additional lines in the plot by merging several data +frames. For example, we can include another line for the resource +spectrum. We first convert also the resource abundance at the final time +into a data frame and filter out the zero values
+ +This data frame only contains three variables, because it does not
+have the sp
column specifying the species. We add this
+column with the value “Resource”
+nf_pp$sp <- "Resource"
Now this new data frame has the same columns as the data frame
+nf
and the two can be bound together
+nf <- rbind(nf, nf_pp)
Using this extended data frame gives the following plot:
+
+p <- ggplot(nf) +
+ geom_line(aes(x = w, y = value, color = sp)) +
+ scale_colour_manual(values = getColours(params)) +
+ scale_x_log10() +
+ scale_y_log10()
+p
Of course we could use the same data frame also with plotly.
+We might want to zoom in on the part that includes the fish. There
+are three ways to achieve this. The first is to use
+filter()
to filter out all the rows in the data frame that
+have small w and then plot the resulting data frame as usual:
+nf %>%
+ filter(w > 10^-4) %>%
+ ggplot() +
+ geom_line(aes(x = w, y = value, color = sp)) +
+ scale_colour_manual(values = getColours(params)) +
+ scale_x_log10() +
+ scale_y_log10()
The second method is to specify limits for the axes. In ggplot this
+is done by adding limits
to the axis scales:
+ggplot(nf) +
+ geom_line(aes(x = w, y = value, color = sp)) +
+ scale_colour_manual(values = getColours(params)) +
+ scale_x_log10(limits = c(10^-4, NA)) +
+ scale_y_log10(limits = c(NA, 10^20))
The NA
means that the existing limits are kept.
In plotly we specify the range
as follows:
+plot_ly(nf) %>%
+ add_lines(x = ~w, y = ~value, color = ~sp,
+ colours = getColours(params)) %>%
+ layout(xaxis = list(type = "log", exponentformat = "power",
+ range = c(-4, 4)),
+ yaxis = list(type = "log", exponentformat = "power",
+ range = c(-14, 20)))
Note how in plotly the range is specified by giving the logarithm to +base 10 of the limits.
+Instead of picking out a specific time we can ask plotly to make an
+animation showing the changing spectra over time. So we melt the entire
+N(sim)
array and use the time variable to specify the
+frames with the frame = ~time
argument to
+add_lines()
:
+melt(N(sim)) %>%
+ filter(value > 0) %>%
+ plot_ly() %>%
+ add_lines(x = ~w, y = ~value,
+ color = ~sp, colors = getColours(params),
+ frame = ~time,
+ line = list(simplify = FALSE)) %>%
+ layout(xaxis = list(type = "log", exponentformat = "power"),
+ yaxis = list(type = "log", exponentformat = "power"))
Note how this produces a smooth animation in spite of the fact that
+we saved the abundances only once a year. That interpolation is
+facilitated by the line = list(simplify = FALSE)
+argument.
The ggplot package does not provide a similarly convenient way of +creating animations. There is the gganimate package, but it is not +nearly as convenient.
+We may also want to make plots contrasting the results of two +different simulations, for example with different fishing policies. To +illustrate this we create two simulations with different fishing +effort:
+
+sim1 <- project(params, t_max = 10, t_save = 0.2, effort = 2)
+sim2 <- project(params, t_max = 10, t_save = 0.2, effort = 4)
Let us look at a plot of the fishing yield against time. This is
+calculated by the getYield()
function, which returns an
+array (time x species) that we can convert to a data frame
Let’s look at the plot of the yield from the first simulation:
+ +To make the plot less cluttered, we keep only the 4 most important +species
+
+yield1 <- filter(yield1, sp %in% c("Saithe", "Cod", "Haddock", "N.pout"))
+yield2 <- filter(yield2, sp %in% c("Saithe", "Cod", "Haddock", "N.pout"))
and plot again
+ +For simulation 2 the plot looks like this:
+ +Comparison will be easier if we combine these two plots. For that we +add an extra variable to the data frames that allow us to distinguish +them and then we merge them together.
+ +In ggplot we can now use facet_grid()
to put the plot
+for each value of effort
side-by-side:
+ggplot(yield) +
+ geom_line(aes(x = time, y = value,
+ colour = sp)) +
+ facet_grid(cols = vars(effort))
Or we can use the linetype
aesthetic to represent the
+different effort
values by different line types:
plotly is less good at faceting, but it can put arbitrary plots
+side-by-side (or arrange them into a grid) with
+subplot()
+subplot(p1, p2, shareX = TRUE, shareY = TRUE)
Note how the shareY = TRUE
argument to
+suplot()
makes sure the two plots use the same scale on the
+y axis, and similarly for shareX = TRUE
.
Also in plotly we can now tie the effort
variable to the
+line type.
Arguably, ggplot2 does a nicer job in this case.
+=i.length)return e;var n=[],o=a[r++];return e.forEach((function(e,i){n.push({key:e,values:t(i,r)})})),o?n.sort((function(t,e){return o(t.key,e.key)})):n}(o(t.map,e,0),0)},n.key=function(t){return i.push(t),n},n.sortKeys=function(t){return a[i.length-1]=t,n},n.sortValues=function(t){return e=t,n},n.rollup=function(t){return r=t,n},n},t.set=function(t){var e=new L;if(t)for(var r=0,n=t.length;r 360?t-=360:t<0&&(t+=360),t<60?n+(i-n)*t/60:t<180?i:t<240?n+(i-n)*(240-t)/60:n}(t))}return t=isNaN(t)?0:(t%=360)<0?t+360:t,e=isNaN(e)||e<0?0:e>1?1:e,n=2*(r=r<0?0:r>1?1:r)-(i=r<=.5?r*(1+e):r+e-r*e),new Qt(a(t+120),a(t),a(t-120))}function Ut(e,r,n){return this instanceof Ut?(this.h=+e,this.c=+r,void(this.l=+n)):arguments.length<2?e instanceof Ut?new Ut(e.h,e.c,e.l):Xt(e instanceof qt?e.l:(e=ae((e=t.rgb(e)).r,e.g,e.b)).l,e.a,e.b):new Ut(e,r,n)}Nt.brighter=function(t){return t=Math.pow(.7,arguments.length?t:1),new Bt(this.h,this.s,this.l/t)},Nt.darker=function(t){return t=Math.pow(.7,arguments.length?t:1),new Bt(this.h,this.s,t*this.l)},Nt.rgb=function(){return jt(this.h,this.s,this.l)},t.hcl=Ut;var Vt=Ut.prototype=new Ft;function Ht(t,e,r){return isNaN(t)&&(t=0),isNaN(e)&&(e=0),new qt(r,Math.cos(t*=Lt)*e,Math.sin(t)*e)}function qt(t,e,r){return this instanceof qt?(this.l=+t,this.a=+e,void(this.b=+r)):arguments.length<2?t instanceof qt?new qt(t.l,t.a,t.b):t instanceof Ut?Ht(t.h,t.c,t.l):ae((t=Qt(t)).r,t.g,t.b):new qt(t,e,r)}Vt.brighter=function(t){return new Ut(this.h,this.c,Math.min(100,this.l+Gt*(arguments.length?t:1)))},Vt.darker=function(t){return new Ut(this.h,this.c,Math.max(0,this.l-Gt*(arguments.length?t:1)))},Vt.rgb=function(){return Ht(this.h,this.c,this.l).rgb()},t.lab=qt;var Gt=18,Yt=qt.prototype=new Ft;function Wt(t,e,r){var n=(t+16)/116,i=n+e/500,a=n-r/200;return new Qt(Kt(3.2404542*(i=.95047*Zt(i))-1.5371385*(n=1*Zt(n))-.4985314*(a=1.08883*Zt(a))),Kt(-.969266*i+1.8760108*n+.041556*a),Kt(.0556434*i-.2040259*n+1.0572252*a))}function Xt(t,e,r){return t>0?new Ut(Math.atan2(r,e)*Ct,Math.sqrt(e*e+r*r),t):new Ut(NaN,NaN,t)}function Zt(t){return t>.206893034?t*t*t:(t-4/29)/7.787037}function Jt(t){return t>.008856?Math.pow(t,1/3):7.787037*t+4/29}function Kt(t){return Math.round(255*(t<=.00304?12.92*t:1.055*Math.pow(t,1/2.4)-.055))}function Qt(t,e,r){return this instanceof Qt?(this.r=~~t,this.g=~~e,void(this.b=~~r)):arguments.length<2?t instanceof Qt?new Qt(t.r,t.g,t.b):ne(""+t,Qt,jt):new Qt(t,e,r)}function $t(t){return new Qt(t>>16,t>>8&255,255&t)}function te(t){return $t(t)+""}Yt.brighter=function(t){return new qt(Math.min(100,this.l+Gt*(arguments.length?t:1)),this.a,this.b)},Yt.darker=function(t){return new qt(Math.max(0,this.l-Gt*(arguments.length?t:1)),this.a,this.b)},Yt.rgb=function(){return Wt(this.l,this.a,this.b)},t.rgb=Qt;var ee=Qt.prototype=new Ft;function re(t){return t<16?"0"+Math.max(0,t).toString(16):Math.min(255,t).toString(16)}function ne(t,e,r){var n,i,a,o=0,s=0,l=0;if(n=/([a-z]+)\((.*)\)/.exec(t=t.toLowerCase()))switch(i=n[2].split(","),n[1]){case"hsl":return r(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return e(se(i[0]),se(i[1]),se(i[2]))}return(a=le.get(t))?e(a.r,a.g,a.b):(null==t||"#"!==t.charAt(0)||isNaN(a=parseInt(t.slice(1),16))||(4===t.length?(o=(3840&a)>>4,o|=o>>4,s=240&a,s|=s>>4,l=15&a,l|=l<<4):7===t.length&&(o=(16711680&a)>>16,s=(65280&a)>>8,l=255&a)),e(o,s,l))}function ie(t,e,r){var n,i,a=Math.min(t/=255,e/=255,r/=255),o=Math.max(t,e,r),s=o-a,l=(o+a)/2;return s?(i=l<.5?s/(o+a):s/(2-o-a),n=t==o?(e-r)/s+(e =f[0]&&l<=f[1]&&((s=c[t.bisect(h,l,1,d)-1]).y+=m,s.push(a[o]));return c}return a.value=function(t){return arguments.length?(r=t,a):r},a.range=function(t){return arguments.length?(n=ce(t),a):n},a.bins=function(t){return arguments.length?(i="number"==typeof t?function(e){return vn(e,t)}:ce(t),a):i},a.frequency=function(t){return arguments.length?(e=!!t,a):e},a},t.layout.pack=function(){var e,r=t.layout.hierarchy().sort(xn),n=0,i=[1,1];function a(t,a){var o=r.call(this,t,a),s=o[0],l=i[0],c=i[1],u=null==e?Math.sqrt:"function"==typeof e?e:function(){return e};if(s.x=s.y=0,$r(s,(function(t){t.r=+u(t.value)})),$r(s,Tn),n){var f=n*(e?1:Math.max(2*s.r/l,2*s.r/c))/2;$r(s,(function(t){t.r+=f})),$r(s,Tn),$r(s,(function(t){t.r-=f}))}return function t(e,r,n,i){var a=e.children;if(e.x=r+=i*e.x,e.y=n+=i*e.y,e.r*=i,a)for(var o=-1,s=a.length;++o =r)for(n=r;++an&&(n=r)}else for(;++a=r)for(n=r;++an&&(n=r);return n},t.mean=function(t,e){var r,n=t.length,i=n,a=-1,o=0;if(null==e)for(;++a l.length)return r;var i,a=c[n-1];return null!=e&&n>=l.length?i=r.entries():(i=[],r.each((function(e,r){i.push({key:r,values:t(e,n)})}))),null!=a?i.sort((function(t,e){return a(t.key,e.key)})):i}(u(t,0,a,o),0)},key:function(t){return l.push(t),s},sortKeys:function(t){return c[l.length-1]=t,s},sortValues:function(e){return t=e,s},rollup:function(t){return e=t,s}}},t.set=c,t.map=r,t.keys=function(t){var e=[];for(var r in t)e.push(r);return e},t.values=function(t){var e=[];for(var r in t)e.push(t[r]);return e},t.entries=function(t){var e=[];for(var r in t)e.push({key:r,value:t[r]});return e},Object.defineProperty(t,"__esModule",{value:!0})}))},{}],104:[function(t,e,r){!function(t,n){"object"==typeof r&&void 0!==e?n(r):n((t=t||self).d3=t.d3||{})}(this,(function(t){"use strict";function e(t,e,r){t.prototype=e.prototype=r,r.constructor=t}function r(t,e){var r=Object.create(t.prototype);for(var n in e)r[n]=e[n];return r}function n(){}var i="\\s*([+-]?\\d+)\\s*",a="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",o="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",s=/^#([0-9a-f]{3,8})$/,l=new RegExp("^rgb\\("+[i,i,i]+"\\)$"),c=new RegExp("^rgb\\("+[o,o,o]+"\\)$"),u=new RegExp("^rgba\\("+[i,i,i,a]+"\\)$"),f=new RegExp("^rgba\\("+[o,o,o,a]+"\\)$"),h=new RegExp("^hsl\\("+[a,o,o]+"\\)$"),p=new RegExp("^hsla\\("+[a,o,o,a]+"\\)$"),d={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function m(){return this.rgb().formatHex()}function g(){return this.rgb().formatRgb()}function v(t){var e,r;return t=(t+"").trim().toLowerCase(),(e=s.exec(t))?(r=e[1].length,e=parseInt(e[1],16),6===r?y(e):3===r?new w(e>>8&15|e>>4&240,e>>4&15|240&e,(15&e)<<4|15&e,1):8===r?x(e>>24&255,e>>16&255,e>>8&255,(255&e)/255):4===r?x(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|240&e,((15&e)<<4|15&e)/255):null):(e=l.exec(t))?new w(e[1],e[2],e[3],1):(e=c.exec(t))?new w(255*e[1]/100,255*e[2]/100,255*e[3]/100,1):(e=u.exec(t))?x(e[1],e[2],e[3],e[4]):(e=f.exec(t))?x(255*e[1]/100,255*e[2]/100,255*e[3]/100,e[4]):(e=h.exec(t))?M(e[1],e[2]/100,e[3]/100,1):(e=p.exec(t))?M(e[1],e[2]/100,e[3]/100,e[4]):d.hasOwnProperty(t)?y(d[t]):"transparent"===t?new w(NaN,NaN,NaN,0):null}function y(t){return new w(t>>16&255,t>>8&255,255&t,1)}function x(t,e,r,n){return n<=0&&(t=e=r=NaN),new w(t,e,r,n)}function b(t){return t instanceof n||(t=v(t)),t?new w((t=t.rgb()).r,t.g,t.b,t.opacity):new w}function _(t,e,r,n){return 1===arguments.length?b(t):new w(t,e,r,null==n?1:n)}function w(t,e,r,n){this.r=+t,this.g=+e,this.b=+r,this.opacity=+n}function T(){return"#"+A(this.r)+A(this.g)+A(this.b)}function k(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function A(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function M(t,e,r,n){return n<=0?t=e=r=NaN:r<=0||r>=1?t=e=NaN:e<=0&&(t=NaN),new L(t,e,r,n)}function S(t){if(t instanceof L)return new L(t.h,t.s,t.l,t.opacity);if(t instanceof n||(t=v(t)),!t)return new L;if(t instanceof L)return t;var e=(t=t.rgb()).r/255,r=t.g/255,i=t.b/255,a=Math.min(e,r,i),o=Math.max(e,r,i),s=NaN,l=o-a,c=(o+a)/2;return l?(s=e===o?(r-i)/l+6*(r0&&c<1?0:s,new L(s,l,c,t.opacity)}function E(t,e,r,n){return 1===arguments.length?S(t):new L(t,e,r,null==n?1:n)}function L(t,e,r,n){this.h=+t,this.s=+e,this.l=+r,this.opacity=+n}function C(t,e,r){return 255*(t<60?e+(r-e)*t/60:t<180?r:t<240?e+(r-e)*(240-t)/60:e)}e(n,v,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:m,formatHex:m,formatHsl:function(){return S(this).formatHsl()},formatRgb:g,toString:g}),e(w,_,r(n,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new w(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new w(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:T,formatHex:T,formatRgb:k,toString:k})),e(L,E,r(n,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new L(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new L(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),e=isNaN(t)||isNaN(this.s)?0:this.s,r=this.l,n=r+(r<.5?r:1-r)*e,i=2*r-n;return new w(C(t>=240?t-240:t+120,i,n),C(t,i,n),C(t<120?t+240:t-120,i,n),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));var P=Math.PI/180,I=180/Math.PI,O=6/29,z=3*O*O;function D(t){if(t instanceof F)return new F(t.l,t.a,t.b,t.opacity);if(t instanceof q)return G(t);t instanceof w||(t=b(t));var e,r,n=U(t.r),i=U(t.g),a=U(t.b),o=B((.2225045*n+.7168786*i+.0606169*a)/1);return n===i&&i===a?e=r=o:(e=B((.4360747*n+.3850649*i+.1430804*a)/.96422),r=B((.0139322*n+.0971045*i+.7141733*a)/.82521)),new F(116*o-16,500*(e-o),200*(o-r),t.opacity)}function R(t,e,r,n){return 1===arguments.length?D(t):new F(t,e,r,null==n?1:n)}function F(t,e,r,n){this.l=+t,this.a=+e,this.b=+r,this.opacity=+n}function B(t){return t>.008856451679035631?Math.pow(t,1/3):t/z+4/29}function N(t){return t>O?t*t*t:z*(t-4/29)}function j(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function U(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function V(t){if(t instanceof q)return new q(t.h,t.c,t.l,t.opacity);if(t instanceof F||(t=D(t)),0===t.a&&0===t.b)return new q(NaN,0=0&&"xmlns"!==(r=t.slice(0,e))&&(t=t.slice(e+1)),J.hasOwnProperty(r)?{space:J[r],local:t}:t}},Y.attr=function(e,r){if(arguments.length<2){if("string"==typeof e){var n=this.node();return(e=t.ns.qualify(e)).local?n.getAttributeNS(e.space,e.local):n.getAttribute(e)}for(r in e)this.each(K(r,e[r]));return this}return this.each(K(e,r))},Y.classed=function(t,e){if(arguments.length<2){if("string"==typeof t){var r=this.node(),n=(t=tt(t)).length,i=-1;if(e=r.classList){for(;++i=1?1:t(e)}}function kr(t){return function(e){return 1-t(1-e)}}function Ar(t){return function(e){return.5*(e<.5?t(2*e):2-t(2-2*e))}}function Mr(t){return t*t}function Sr(t){return t*t*t}function Er(t){if(t<=0)return 0;if(t>=1)return 1;var e=t*t,r=e*t;return 4*(t<.5?r:3*(t-e)+r-.75)}function Lr(t){return 1-Math.cos(t*Et)}function Cr(t){return Math.pow(2,10*(t-1))}function Pr(t){return 1-Math.sqrt(1-t*t)}function Ir(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375}function Or(t,e){return e-=t,function(r){return Math.round(t+e*r)}}function zr(t){var e,r,n,i=[t.a,t.b],a=[t.c,t.d],o=Rr(i),s=Dr(i,a),l=Rr(((e=a)[0]+=(n=-s)*(r=i)[0],e[1]+=n*r[1],e))||0;i[0]*a[1]=0?t.slice(0,e):t,i=e>=0?t.slice(e+1):"in";return n=_r.get(n)||br,Tr((i=wr.get(i)||C)(n.apply(null,r.call(arguments,1))))},t.interpolateHcl=function(e,r){e=t.hcl(e),r=t.hcl(r);var n=e.h,i=e.c,a=e.l,o=r.h-n,s=r.c-i,l=r.l-a;isNaN(s)&&(s=0,i=isNaN(i)?r.c:i);isNaN(o)?(o=0,n=isNaN(n)?r.h:n):o>180?o-=360:o<-180&&(o+=360);return function(t){return Ht(n+o*t,i+s*t,a+l*t)+""}},t.interpolateHsl=function(e,r){e=t.hsl(e),r=t.hsl(r);var n=e.h,i=e.s,a=e.l,o=r.h-n,s=r.s-i,l=r.l-a;isNaN(s)&&(s=0,i=isNaN(i)?r.s:i);isNaN(o)?(o=0,n=isNaN(n)?r.h:n):o>180?o-=360:o<-180&&(o+=360);return function(t){return jt(n+o*t,i+s*t,a+l*t)+""}},t.interpolateLab=function(e,r){e=t.lab(e),r=t.lab(r);var n=e.l,i=e.a,a=e.b,o=r.l-n,s=r.a-i,l=r.b-a;return function(t){return Wt(n+o*t,i+s*t,a+l*t)+""}},t.interpolateRound=Or,t.transform=function(e){var r=i.createElementNS(t.ns.prefix.svg,"g");return(t.transform=function(t){if(null!=t){r.setAttribute("transform",t);var e=r.transform.baseVal.consolidate()}return new zr(e?e.matrix:Fr)})(e)},zr.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var Fr={a:1,b:0,c:0,d:1,e:0,f:0};function Br(t){return t.length?t.pop()+",":""}function Nr(e,r){var n=[],i=[];return e=t.transform(e),r=t.transform(r),function(t,e,r,n){if(t[0]!==e[0]||t[1]!==e[1]){var i=r.push("translate(",null,",",null,")");n.push({i:i-4,x:dr(t[0],e[0])},{i:i-2,x:dr(t[1],e[1])})}else(e[0]||e[1])&&r.push("translate("+e+")")}(e.translate,r.translate,n,i),function(t,e,r,n){t!==e?(t-e>180?e+=360:e-t>180&&(t+=360),n.push({i:r.push(Br(r)+"rotate(",null,")")-2,x:dr(t,e)})):e&&r.push(Br(r)+"rotate("+e+")")}(e.rotate,r.rotate,n,i),function(t,e,r,n){t!==e?n.push({i:r.push(Br(r)+"skewX(",null,")")-2,x:dr(t,e)}):e&&r.push(Br(r)+"skewX("+e+")")}(e.skew,r.skew,n,i),function(t,e,r,n){if(t[0]!==e[0]||t[1]!==e[1]){var i=r.push(Br(r)+"scale(",null,",",null,")");n.push({i:i-4,x:dr(t[0],e[0])},{i:i-2,x:dr(t[1],e[1])})}else 1===e[0]&&1===e[1]||r.push(Br(r)+"scale("+e+")")}(e.scale,r.scale,n,i),e=r=null,function(t){for(var e,r=-1,a=i.length;++r0?n=t:(e.c=null,e.t=NaN,e=null,l.end({type:"end",alpha:n=0})):t>0&&(l.start({type:"start",alpha:n=t}),e=ve(s.tick)),s):n},s.start=function(){var t,e,r,n=v.length,l=y.length,u=c[0],d=c[1];for(t=0;tp.x&&(p=t),t.depth>d.depth&&(d=t)}));var m=r(h,p)/2-h.x,g=n[0]/(p.x+r(p,h)/2+m),v=n[1]/(d.depth||1);Qr(u,(function(t){t.x=(t.x+m)*g,t.y=t.depth*v}))}return c}function o(t){var e=t.children,n=t.parent.children,i=t.i?n[t.i-1]:null;if(e.length){!function(t){var e,r=0,n=0,i=t.children,a=i.length;for(;--a>=0;)(e=i[a]).z+=r,e.m+=r,r+=e.s+(n+=e.c)}(t);var a=(e[0].z+e[e.length-1].z)/2;i?(t.z=i.z+r(t._,i._),t.m=t.z-a):t.z=a}else i&&(t.z=i.z+r(t._,i._));t.parent.A=function(t,e,n){if(e){for(var i,a=t,o=t,s=e,l=a.parent.children[0],c=a.m,u=o.m,f=s.m,h=l.m;s=Ln(s),a=En(a),s&&a;)l=En(l),(o=Ln(o)).a=t,(i=s.z+f-a.z-c+r(s._,a._))>0&&(Cn(Pn(s,t,n),t,i),c+=i,u+=i),f+=s.m,c+=a.m,h+=l.m,u+=o.m;s&&!Ln(o)&&(o.t=s,o.m+=f-u),a&&!En(l)&&(l.t=a,l.m+=c-h,n=t)}return n}(t,i,t.parent.A||n[0])}function s(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function l(t){t.x*=n[0],t.y=t.depth*n[1]}return a.separation=function(t){return arguments.length?(r=t,a):r},a.size=function(t){return arguments.length?(i=null==(n=t)?l:null,a):i?null:n},a.nodeSize=function(t){return arguments.length?(i=null==(n=t)?null:l,a):i?n:null},Kr(a,e)},t.layout.cluster=function(){var e=t.layout.hierarchy().sort(null).value(null),r=Sn,n=[1,1],i=!1;function a(a,o){var s,l=e.call(this,a,o),c=l[0],u=0;$r(c,(function(e){var n=e.children;n&&n.length?(e.x=function(t){return t.reduce((function(t,e){return t+e.x}),0)/t.length}(n),e.y=function(e){return 1+t.max(e,(function(t){return t.y}))}(n)):(e.x=s?u+=r(e,s):0,e.y=0,s=e)}));var f=function t(e){var r=e.children;return r&&r.length?t(r[0]):e}(c),h=function t(e){var r,n=e.children;return n&&(r=n.length)?t(n[r-1]):e}(c),p=f.x-r(f,h)/2,d=h.x+r(h,f)/2;return $r(c,i?function(t){t.x=(t.x-c.x)*n[0],t.y=(c.y-t.y)*n[1]}:function(t){t.x=(t.x-p)/(d-p)*n[0],t.y=(1-(c.y?t.y/c.y:1))*n[1]}),l}return a.separation=function(t){return arguments.length?(r=t,a):r},a.size=function(t){return arguments.length?(i=null==(n=t),a):i?null:n},a.nodeSize=function(t){return arguments.length?(i=null!=(n=t),a):i?n:null},Kr(a,e)},t.layout.treemap=function(){var e,r=t.layout.hierarchy(),n=Math.round,i=[1,1],a=null,o=In,s=!1,l="squarify",c=.5*(1+Math.sqrt(5));function u(t,e){for(var r,n,i=-1,a=t.length;++i0;)s.push(r=c[i-1]),s.area+=r.area,"squarify"!==l||(n=p(s,m))<=h?(c.pop(),h=n):(s.area-=s.pop().area,d(s,m,a,!1),m=Math.min(a.dx,a.dy),s.length=s.area=0,h=1/0);s.length&&(d(s,m,a,!0),s.length=s.area=0),e.forEach(f)}}function h(t){var e=t.children;if(e&&e.length){var r,n=o(t),i=e.slice(),a=[];for(u(i,n.dx*n.dy/t.value),a.area=0;r=i.pop();)a.push(r),a.area+=r.area,null!=r.z&&(d(a,r.z?n.dx:n.dy,n,!i.length),a.length=a.area=0);e.forEach(h)}}function p(t,e){for(var r,n=t.area,i=0,a=1/0,o=-1,s=t.length;++oi&&(i=r));return e*=e,(n*=n)?Math.max(e*i*c/n,n/(e*a*c)):1/0}function d(t,e,r,i){var a,o=-1,s=t.length,l=r.x,c=r.y,u=e?n(t.area/e):0;if(e==r.dx){for((i||u>r.dy)&&(u=r.dy);++or.dx)&&(u=r.dx);++o1);return t+e*r*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var e=t.random.normal.apply(t,arguments);return function(){return Math.exp(e())}},bates:function(e){var r=t.random.irwinHall(e);return function(){return r()/e}},irwinHall:function(t){return function(){for(var e=0,r=0;rl;u--);e=e.slice(c,u)}return e},s.copy=function(){return t(e.copy(),r,n,i)},Un(s,e)}(t.scale.linear().domain([0,1]),10,!0,[1,10])};var Gn={floor:function(t){return-Math.ceil(-t)},ceil:function(t){return-Math.floor(-t)}};function Yn(t){return function(e){return e<0?-Math.pow(-e,t):Math.pow(e,t)}}t.scale.pow=function(){return function t(e,r,n){var i=Yn(r),a=Yn(1/r);function o(t){return e(i(t))}return o.invert=function(t){return a(e.invert(t))},o.domain=function(t){return arguments.length?(e.domain((n=t.map(Number)).map(i)),o):n},o.ticks=function(t){return qn(n,t)},o.tickFormat=function(t,e){return d3_scale_linearTickFormat(n,t,e)},o.nice=function(t){return o.domain(Vn(n,t))},o.exponent=function(t){return arguments.length?(i=Yn(r=t),a=Yn(1/r),e.domain(n.map(i)),o):r},o.copy=function(){return t(e.copy(),r,n)},Un(o,e)}(t.scale.linear(),1,[0,1])},t.scale.sqrt=function(){return t.scale.pow().exponent(.5)},t.scale.ordinal=function(){return function e(r,n){var i,a,o;function s(t){return a[((i.get(t)||("range"===n.t?i.set(t,r.push(t)):NaN))-1)%a.length]}function l(e,n){return t.range(r.length).map((function(t){return e+n*t}))}return s.domain=function(t){if(!arguments.length)return r;r=[],i=new _;for(var e,a=-1,o=t.length;++a=St)return l(c,p)+(s?l(s,1-p):"")+"Z";var d,m,g,v,y,x,b,_,w,T,k,A,M=0,S=0,E=[];if((v=(+o.apply(this,arguments)||0)/2)&&(g=n===Qn?Math.sqrt(s*s+c*c):+n.apply(this,arguments),p||(S*=-1),c&&(S=Pt(g/c*Math.sin(v))),s&&(M=Pt(g/s*Math.sin(v)))),c){y=c*Math.cos(u+S),x=c*Math.sin(u+S),b=c*Math.cos(f-S),_=c*Math.sin(f-S);var L=Math.abs(f-u-2*S)<=At?0:1;if(S&&ii(y,x,b,_)===p^L){var C=(u+f)/2;y=c*Math.cos(C),x=c*Math.sin(C),b=_=null}}else y=x=0;if(s){w=s*Math.cos(f-M),T=s*Math.sin(f-M),k=s*Math.cos(u+M),A=s*Math.sin(u+M);var P=Math.abs(u-f+2*M)<=At?0:1;if(M&&ii(w,T,k,A)===1-p^P){var I=(u+f)/2;w=s*Math.cos(I),T=s*Math.sin(I),k=A=null}}else w=T=0;if(h>kt&&(d=Math.min(Math.abs(c-s)/2,+r.apply(this,arguments)))>.001){m=s=0&&0,t+e.replace(/\u001b\[\d\d?m/g,"").length+1}),0)>60)return r[0]+(""===e?"":e+"\n ")+" "+t.join(",\n ")+" "+r[1];return r[0]+e+" "+t.join(", ")+" "+r[1]}(c,b,A)):A[0]+b+A[1]}function f(t){return"["+Error.prototype.toString.call(t)+"]"}function h(t,e,r,n,i,a){var o,s,l;if((l=Object.getOwnPropertyDescriptor(e,i)||{value:e[i]}).get?s=l.set?t.stylize("[Getter/Setter]","special"):t.stylize("[Getter]","special"):l.set&&(s=t.stylize("[Setter]","special")),E(n,i)||(o="["+i+"]"),s||(t.seen.indexOf(l.value)<0?(s=m(r)?u(t,l.value,null):u(t,l.value,r-1)).indexOf("\n")>-1&&(s=a?s.split("\n").map((function(t){return" "+t})).join("\n").substr(2):"\n"+s.split("\n").map((function(t){return" "+t})).join("\n")):s=t.stylize("[Circular]","special")),y(o)){if(a&&i.match(/^\d+$/))return s;(o=JSON.stringify(""+i)).match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(o=o.substr(1,o.length-2),o=t.stylize(o,"name")):(o=o.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),o=t.stylize(o,"string"))}return o+": "+s}function p(t){return Array.isArray(t)}function d(t){return"boolean"==typeof t}function m(t){return null===t}function g(t){return"number"==typeof t}function v(t){return"string"==typeof t}function y(t){return void 0===t}function x(t){return b(t)&&"[object RegExp]"===k(t)}function b(t){return"object"==typeof t&&null!==t}function _(t){return b(t)&&"[object Date]"===k(t)}function w(t){return b(t)&&("[object Error]"===k(t)||t instanceof Error)}function T(t){return"function"==typeof t}function k(t){return Object.prototype.toString.call(t)}function A(t){return t<10?"0"+t.toString(10):t.toString(10)}r.debuglog=function(t){if(y(a)&&(a=e.env.NODE_DEBUG||""),t=t.toUpperCase(),!o[t])if(new RegExp("\\b"+t+"\\b","i").test(a)){var n=e.pid;o[t]=function(){var e=r.format.apply(r,arguments);console.error("%s %d: %s",t,n,e)}}else o[t]=function(){};return o[t]},r.inspect=s,s.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]},s.styles={special:"cyan",number:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",date:"magenta",regexp:"red"},r.isArray=p,r.isBoolean=d,r.isNull=m,r.isNullOrUndefined=function(t){return null==t},r.isNumber=g,r.isString=v,r.isSymbol=function(t){return"symbol"==typeof t},r.isUndefined=y,r.isRegExp=x,r.isObject=b,r.isDate=_,r.isError=w,r.isFunction=T,r.isPrimitive=function(t){return null===t||"boolean"==typeof t||"number"==typeof t||"string"==typeof t||"symbol"==typeof t||void 0===t},r.isBuffer=t("./support/isBuffer");var M=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function S(){var t=new Date,e=[A(t.getHours()),A(t.getMinutes()),A(t.getSeconds())].join(":");return[t.getDate(),M[t.getMonth()],e].join(" ")}function E(t,e){return Object.prototype.hasOwnProperty.call(t,e)}r.log=function(){console.log("%s - %s",S(),r.format.apply(r,arguments))},r.inherits=t("inherits"),r._extend=function(t,e){if(!e||!b(e))return t;for(var r=Object.keys(e),n=r.length;n--;)t[r[n]]=e[r[n]];return t}}).call(this)}).call(this,t("_process"),"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./support/isBuffer":73,_process:272,inherits:72}],75:[function(t,e,r){"use strict";r.byteLength=function(t){var e=c(t),r=e[0],n=e[1];return 3*(r+n)/4-n},r.toByteArray=function(t){var e,r,n=c(t),o=n[0],s=n[1],l=new a(function(t,e,r){return 3*(e+r)/4-r}(0,o,s)),u=0,f=s>0?o-4:o;for(r=0;rs?s:o+16383));1===i?(e=t[r-1],a.push(n[e>>2]+n[e<<4&63]+"==")):2===i&&(e=(t[r-2]<<8)+t[r-1],a.push(n[e>>10]+n[e>>4&63]+n[e<<2&63]+"="));return a.join("")};for(var n=[],i=[],a="undefined"!=typeof Uint8Array?Uint8Array:Array,o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",s=0,l=o.length;ss&&(r=s-l),a=r;a>=0;a--){for(var f=!0,h=0;hf;)h.pop(),--p;var d,m=new Array(p+1);for(a=0;a<=p;++a)(d=m[a]=[]).x0=a>0?h[a-1]:u,d.x1=a(l=t.charCodeAt(a))||l>57){u=(46===l?p+t.slice(a+1):t.slice(a))+u,t=t.slice(0,a);break}}x&&!f&&(t=o(t,1/0));var S=c.length+t.length+u.length,E=Sa[o][2][0];++o);var l=t(e-a[o][1][0],n);return l[0]+=t(a[o][1][0],i*n>i*a[o][0][1]?a[o][0][1]:n)[0],l}n?o.invert=n(o):t.invert&&(o.invert=function(e,n){for(var i=a[+(n<0)],s=r[+(n<0)],l=0,c=i.length;l1.790857183?e=1.790857183:e<-1.790857183&&(e=-1.790857183);var r,i=e;do{var a=i*i;i-=r=(i*(1.0148+a*a*(.23185+a*(.02406*a-.14499)))-e)/(1.0148+a*a*(5*.23185+a*(.21654*a-1.01493)))}while(n(r)>v);return[t,i]},Fe.invert=function(t,e){if(n(e)pr&&p0?y-l:l)*A],u=e.geoProjection(t(s)).rotate(c),f=e.geoRotation(c),h=u.center;return delete u.rotate,u.center=function(t){return arguments.length?h(f(t)):f.invert(h())},u.clipAngle(90)}function Mr(t){var r=o(t);function n(t,n){var i=e.geoGnomonicRaw(t,n);return i[0]*=r,i}return n.invert=function(t,n){return e.geoGnomonicRaw.invert(t/r,n)},n}function Sr(t,e){return Ar(Mr,t,e)}function Er(t){if(!(t*=2))return e.geoAzimuthalEquidistantRaw;var r=-t/2,n=-r,i=t*t,s=g(n),l=.5/m(n);function c(e,a){var s=E(o(a)*o(e-r)),l=E(o(a)*o(e-n));return[((s*=s)-(l*=l))/(2*t),(a<0?-1:1)*L(4*i*l-(i-s+l)*(i-s+l))/(2*t)]}return c.invert=function(t,e){var i,c,u=e*e,f=o(L(u+(i=t+r)*i)),h=o(L(u+(i=t+n)*i));return[a(c=f-h,i=(f+h)*s),(e<0?-1:1)*E(L(i*i+c*c)*l)]},c}function Lr(t,e){return Ar(Er,t,e)}function Cr(t,e){if(n(e)nt&&(nt=e)),c?trr&&(rr=t);e