This tutorial describes how Medium-Term Forecasts (MTF) can be performed using FLR. It uses the FLasher
package for running projections, an updated version of FLash
.
MTFs use the same engine as Short-Term Forecasts (STFs). However, there are some key differences between them.
Special attention must be paid to the conditioning and future assumptions of the stock.
To follow this tutorial you should have installed the following packages:
You can do so as follows,
install.packages(c("FLCore"), repos="http://flr-project.org/R")
install.packages(c("FLasher"), repos="http://flr-project.org/R")
install.packages(c("FLFishery"), repos="http://flr-project.org/R")
# Load all necessary packages, trim pkg messages
library(FLCore)
library(FLasher)
Running an MTF is similar to running an STF in that we need several components:
FLStock
object set up for the future (assumptions);However, there are some significant differences between an MTF and an STF:
In this tutorial we will build a 10 year projection, introduce a range of target types (including minimum and maximum target values, as well as relative target values), use a dynamic SRR and introduce uncertainty.
As ususal, we base the projections on plaice in the North Sea.
data(ple4)
The first step is to condition the projection by making assumptions about the stock in the future, and to fit the SRR.
We use the stf()
function to set up our stock into the future. ‘stf()’ makes a lot of assumptions to set up a future stock. We may want to change some of these assumptions, but for the moment we will use the defaults.
ple4_mtf <- stf(ple4, nyears = 10)
# Now the stock goes up to 2018
summary(ple4_mtf)
An object of class "FLStock"
Name: Plaice in IV
Description: Imported from a VPA file. ( N:\Projecten\ICES WG\Demersale werkgroep [...]
Quant: age
Dims: age year unit season area iter
10 62 1 1 1 1
Range: min max pgroup minyear maxyear minfbar maxfbar
1 10 10 1957 2018 2 6
catch : [ 1 62 1 1 1 1 ], units = t
catch.n : [ 10 62 1 1 1 1 ], units = 10^3
catch.wt : [ 10 62 1 1 1 1 ], units = kg
discards : [ 1 62 1 1 1 1 ], units = t
discards.n : [ 10 62 1 1 1 1 ], units = 10^3
discards.wt : [ 10 62 1 1 1 1 ], units = kg
landings : [ 1 62 1 1 1 1 ], units = t
landings.n : [ 10 62 1 1 1 1 ], units = 10^3
landings.wt : [ 10 62 1 1 1 1 ], units = kg
stock : [ 1 62 1 1 1 1 ], units = t
stock.n : [ 10 62 1 1 1 1 ], units = 10^3
stock.wt : [ 10 62 1 1 1 1 ], units = kg
m : [ 10 62 1 1 1 1 ], units = m
mat : [ 10 62 1 1 1 1 ], units =
harvest : [ 10 62 1 1 1 1 ], units = f
harvest.spwn : [ 10 62 1 1 1 1 ], units =
m.spwn : [ 10 62 1 1 1 1 ], units =
In these examples we use a Beverton-Holt model (see the tutorial on fitting SRRs for more detail). The resulting SRR fit can be seen in Figure 1.
ple4_sr <- fmle(as.FLSR(ple4, model="bevholt"), control=list(trace=0))
plot(ple4_sr)
We saw in the STF tutorial how to set an Fbar target (LINK). Here is some quick revision.
We will set the future F at F status quo and assume that F status quo is the mean of the last 4 years
f_status_quo <- mean(fbar(ple4)[,as.character(2005:2008)])
f_status_quo
[1] 0.4978
Make the control data.frame
including all the years of the projection (note that FLash
used quantity and val as column names and FLasher
uses quant and value)
ctrl_target <- data.frame(year = 2009:2018,
quant = "f",
value = f_status_quo)
Make the fwdControl
object from the control data.frame
ctrl_f <- fwdControl(ctrl_target)
ctrl_f
An object of class "fwdControl"
(step) year quant min value max
1 2009 f NA 0.498 NA
2 2010 f NA 0.498 NA
3 2011 f NA 0.498 NA
4 2012 f NA 0.498 NA
5 2013 f NA 0.498 NA
6 2014 f NA 0.498 NA
7 2015 f NA 0.498 NA
8 2016 f NA 0.498 NA
9 2017 f NA 0.498 NA
10 2018 f NA 0.498 NA
We have columns of year, quant (target type), min, value and max (and others not necessarily shown). Here we are only using year, quant and value. We can now run fwd() with our three ingredients. Note that the control argument used to be called ctrl in FLash
. Also, with FLasher
the control and sr arguments must be named.
ple4_f_sq <- fwd(ple4_mtf, control = ctrl_f, sr = ple4_sr)
# What just happened? We plot the stock from the year 2000.
plot(window(ple4_f_sq, start=2000))
The future Fs are as we set in the control object
fbar(ple4_f_sq)[,ac(2005:2018)]
An object of class "FLQuant"
, , unit = unique, season = all, area = unique
year
age 2005 2006 2007 2008 2009
all 0.62343 0.54764 0.46392 0.35631 0.49783
[ ... 4 years]
year
age 2014 2015 2016 2017 2018
all 0.49783 0.49783 0.49783 0.49783 0.49783
What about recruitment? Remember we are now using a Beverton-Holt model.
rec(ple4_f_sq)[,ac(2005:2018)]
An object of class "FLQuant"
, , unit = unique, season = all, area = unique
year
age 2005 2006 2007 2008 2009
1 714344 820006 949341 844041 903372
[ ... 4 years]
year
age 2014 2015 2016 2017 2018
1 906070 905709 905388 905275 905160
The recruitment is not constant but it is not changing very much. That’s because the fitted model looks flat (Figure 1).
In this example we introduce two new things:
Setting a catch target allows exploring the consequences of different TAC strategies. In this example, the TAC (the total catch of the stock) is reduced 10% each year for 10 years.
We create a vector of future catches based on the catch in 2008:
future_catch <- c(catch(ple4)[,"2008"]) * 0.9^(1:10)
future_catch
[1] 86436 77793 70013 63012 56711 51040 45936 41342 37208 33487
We create the fwdControl
object, setting the target quantity to catch and passing in the vector of future catches
ctrl_catch <- fwdControl(
data.frame(
year=2009:2018,
quant = "catch",
value=future_catch))
# The control object has the desired catch target values
ctrl_catch
An object of class "fwdControl"
(step) year quant min value max
1 2009 catch NA 86436.404 NA
2 2010 catch NA 77792.764 NA
3 2011 catch NA 70013.487 NA
4 2012 catch NA 63012.139 NA
5 2013 catch NA 56710.925 NA
6 2014 catch NA 51039.832 NA
7 2015 catch NA 45935.849 NA
8 2016 catch NA 41342.264 NA
9 2017 catch NA 37208.038 NA
10 2018 catch NA 33487.234 NA
We call fwd()
with the stock, the control object and the SRR, and look at the results
ple4_catch <- fwd(ple4_mtf, control = ctrl_catch, sr = ple4_sr)
catch(ple4_catch)[,ac(2008:2018)]
An object of class "FLQuant"
, , unit = unique, season = all, area = unique
year
age 2008 2009 2010 2011 2012
all 96040 86436 77793 70013 63012
[ ... 1 years]
year
age 2014 2015 2016 2017 2018
all 51040 45936 41342 37208 33487
plot(window(ple4_catch, start=2000))
The decreasing catch targets have been hit. Note that F has to be similarly reduced to hit the catch targets, resulting in a surge in SSB.
In the previous examples we have set target types based on the activity of the fleet (F and catch). We can also set biological target types. This is useful when there are biological reference points, e.g. Bpa.
Setting a biological target must be done with care because it may not be possible to hit the target. For example, even when F is set to 0, the stock may not be productive enough to increase its abundance sufficiently to hit the target.
There are currently three types of biological target available in FLasher
: SRP, SSB and biomass. Of these, there are several flavours of SSB and biomass that differ in terms of timing.
The SRP target is the Stock Recruitment Potential at the time of spawning, i.e. if a stock spawns in the middle of the year, after the abundance has been reduced by fishing and natural mortality, this is the SRP at that point in time. At the moment, SRP is calculated as the mass of mature fish. If setting an SRP target, you must be aware of the timing of spawning and the timing of the fishing period.
Internally, FLasher
attempts to hit the desired target in a time step by finding the appropriate value of F in that timestep. If the stock spawns before fishing starts, then changing the fishing activity in that timestep has no effect on the SRP at the time of spawning. It is not possible to hit the target by manipulating F in that timestep and FLasher
gives up.
SSB is the Spawning Stock Biomass calculated as the total biomass of mature fish. The biomass is simply the total biomass of the stock. For the SSB and biomass targets, there are three different flavours based on timing:
ssb()
method for FLStock
objects);FLash
package.This last bullet needs some explanation. If fishing starts before spawning (i.e. the harvest.spwn slot of an FLStock
is greater than 0) then the SSB or biomass at the time of spawning in that timestep is returned. If fishing starts after spawning, or there is no spawning in that time step (which may happen with a seasonal model), then the SSB or biomass at the time of spawning in the next timestep is returned.
However, this second case can be more complicated for several reasons. If there is no spawning in the next time step then we have a problem and FLasher
gives up (F in the current timestep does not affect the SSB or biomass at the time of spawning in the current or next timestep). Additionally, if there is no next time step (i.e. we have reached the end of the projection) then FLasher
gives up.
There is also a potential problem that if the fishing in the next timestep starts before spawning, the SSB or biomass at the time of spawning in the next timestep will be affected by the effort in the current timestep AND the next timestep. FLasher
cannot handle this and weird results will occur (although it is an unusal situation).
For these reasons, it is better to only use the FLash
-like target for annual models and when fishing and spawning happen at the same time in each year through the projection.
Here we give simple demonstrations of the different types of biological targets using SSB. The results of using a biomass target will have the same behaviour. Only a 1 year projection is run.
The timing of spawning and fishing are controlled by the m.spwn and harvest.spwn slots. Our test FLStock
object has m.spwn and harvest.spwn values of 0. This means that spawning and fishing happens at the start of the year and that spawning is assumed to happen before fishing.
Here we set a target SSB for the end of the timestep
final_ssb <- 100000
ctrl_ssb <- fwdControl(data.frame(year=2009, quant = "ssb_end", value=final_ssb))
ple4_ssb <- fwd(ple4_mtf, control=ctrl_ssb, sr = ple4_sr)
# Calculate the final SSB to check the target has been hit
survivors <- stock.n(ple4_ssb) * exp(-harvest(ple4_ssb) - m(ple4_ssb))
quantSums((survivors * stock.wt(ple4_ssb) * mat(ple4_ssb))[,ac(2009)])
An object of class "FLQuant"
An object of class "FLQuant"
, , unit = unique, season = all, area = unique
year
age 2009
all 1e+05
units: t
If fishing occurs after spawning, the level of fishing will not affect the SSB or biomass at the time of spawning. This is currently the case because m.spwn and harvest.spwn have values of 0. The result is that the projection will fail with a warning (intentionally). We see this here.
spawn_ssb <- 100000
ctrl_ssb <- fwdControl(data.frame(year=2009, quant = "ssb_spawn", value=spawn_ssb))
ple4_ssb <- fwd(ple4_mtf, control=ctrl_ssb, sr = ple4_sr)
Warning in operatingModelRun(fishery, biolscpp, control,
effort_mult_initial = 1, : In operatingModel eval_om, ssb_spawn target.
Either spawning happens before fishing (so fishing effort has no impact on
SRP), or no spawning in timestep. Cannot solve.
# Using the `ssb()` method to get the SSB at the time of spawning,
# we can see that the projection failed
ssb(ple4_ssb)[,ac(2009)]
An object of class "FLQuant"
An object of class "FLQuant"
, , unit = unique, season = all, area = unique
year
age 2009
all 231522
units: t
In our example, spawning happens at the start of the year. We can change this with the m.spwn slot. Natural mortality is assumed to happen continuously through the year. Therefore, if we set the m.spwn slot to 0.5, then half the natural mortality happens before spawning, i.e. spawning happens half way through the year. Similarly, the current value of harvest.spwn is 0, meaning that spawning happens before any fishing happens. If we set this value to 0.5 then half of the fishing mortality has occurred before spawning. With these changes, the example now runs.
m.spwn(ple4_mtf)[,ac(2009)] <- 0.5
harvest.spwn(ple4_mtf)[,ac(2009)] <- 0.5
spawn_ssb <- 100000
ctrl_ssb <- fwdControl(data.frame(year=2009, quant = "ssb_spawn", value=spawn_ssb))
ple4_ssb <- fwd(ple4_mtf, control=ctrl_ssb, sr = ple4_sr)
# We hit the target
ssb(ple4_ssb)[,ac(2009)]
An object of class "FLQuant"
An object of class "FLQuant"
, , unit = unique, season = all, area = unique
year
age 2009
all 1e+05
units: t
At the moment FLasher
calculates the SRP as SSB. This means that the SRP target type behaves in the same way as the ssb_spawn target.
srp <- 100000
ctrl_ssb <- fwdControl(data.frame(year=2009, quant = "srp", value=srp))
ple4_ssb <- fwd(ple4_mtf, control=ctrl_ssb, sr = ple4_sr)
# We hit the target
ssb(ple4_ssb)[,ac(2009)]
An object of class "FLQuant"
An object of class "FLQuant"
, , unit = unique, season = all, area = unique
year
age 2009
all 1e+05
units: t
FLash
-like targetsAs mentioned above, the FLash
-like targets can have different behaviour depending on the timing of spawning and fishing. If fishing starts before spawning, the SSB or biomass at the time of spawning in the current timestep will be hit (if possible). This is demonstrated here.
# Force spawning to happen half way through the year
# and fishing to start at the beginning of the year
m.spwn(ple4_mtf)[,ac(2009)] <- 0.5
harvest.spwn(ple4_mtf)[,ac(2009)] <- 0.5
flash_ssb <- 150000
ctrl_ssb <- fwdControl(data.frame(year=2009, quant = "ssb_flash", value=flash_ssb))
ple4_ssb <- fwd(ple4_mtf, control=ctrl_ssb, sr = ple4_sr)
# Hit the target? Yes
ssb(ple4_ssb)[,ac(2009)]
An object of class "FLQuant"
An object of class "FLQuant"
, , unit = unique, season = all, area = unique
year
age 2009
all 150000
units: t
However, if fishing starts after spawning, the SSB or biomass at the time of spawning in the next timestep will be hit (if possible). This is because fishing in the current timestep will have no impact on the SSB at the time of spawning in the current timestep.
# Force spawning to happen at the start of the year before fishing
m.spwn(ple4_mtf)[,ac(2009)] <- 0.0
harvest.spwn(ple4_mtf)[,ac(2009)] <- 0.0
flash_ssb <- 150000
ctrl_ssb <- fwdControl(data.frame(year=2009, quant = "ssb_flash", value=flash_ssb))
ple4_ssb <- fwd(ple4_mtf, control=ctrl_ssb, sr = ple4_sr)
# We did hit the SSB target, but not until 2010.
ssb(ple4_ssb)[,ac(2009:2010)]
An object of class "FLQuant"
An object of class "FLQuant"
, , unit = unique, season = all, area = unique
year
age 2009 2010
all 231522 150000
units: t
Here we run a longer projection with a constant FLash
-like SSB target. Spawning happens before fishing so the target will not be hit until the following year.
# Force spawning to happen at the start of the year before fishing
m.spwn(ple4_mtf)[,ac(2009)] <- 0.0
harvest.spwn(ple4_mtf)[,ac(2009)] <- 0.0
future_ssb <- 200000
ctrl_ssb <- fwdControl(data.frame(year=2009:2018, quant = "ssb_flash", value=future_ssb))
ple4_ssb <- fwd(ple4_mtf, control = ctrl_ssb, sr = ple4_sr)
We get a warning about running out of room. This is because future stock object, ple4_mtf, goes up to 2018. When we set the SSB target for 2018, it tries to hit the final year target in 2019. The targets that were set for 2009 to 2017 have been hit in 2010 to 2018. However, we cannot hit the target that was set for 2018. This means that the returned value of F in 2018 needs to be discounted.
ssb(ple4_ssb)[,ac(2009:2018)]
An object of class "FLQuant"
An object of class "FLQuant"
, , unit = unique, season = all, area = unique
year
age 2009 2010 2011 2012 2013 2014 2015 2016 2017
all 231522 200000 200000 200000 200000 200000 200000 200000 200000
year
age 2018
all 200000
units: t
fbar(ple4_ssb)[,ac(2009:2018)]
An object of class "FLQuant"
An object of class "FLQuant"
, , unit = unique, season = all, area = unique
year
age 2009 2010 2011 2012 2013 2014 2015 2016
all 0.66844 0.49965 0.51120 0.52245 0.51927 0.51772 0.52075 0.51900
year
age 2017 2018
all 0.51921 0.45596
units: f
plot(window(ple4_ssb, start=2000, end=2017))
The examples above have dealt with absolute target values. We now introduce the idea of relative values. This allows us to set the target value relative to the value in another time step.
We do this by using the relYear column in the control object (the year that the target is relative to). The value column now holds the relative value, not the absolute value.
Here we set catches in the projection years to be 90% of the catches in the previous year, i.e. we want the catche in 2009 to be 0.9 * value in 2008 etc.
ctrl_rel_catch <- fwdControl(
data.frame(year = 2009:2018,
quant = "catch",
value = 0.9,
relYear = 2008:2017))
# The relative year appears in the control object summary
ctrl_rel_catch
An object of class "fwdControl"
(step) year quant relYear min value max
1 2009 catch 2008 NA 0.900 NA
2 2010 catch 2009 NA 0.900 NA
3 2011 catch 2010 NA 0.900 NA
4 2012 catch 2011 NA 0.900 NA
5 2013 catch 2012 NA 0.900 NA
6 2014 catch 2013 NA 0.900 NA
7 2015 catch 2014 NA 0.900 NA
8 2016 catch 2015 NA 0.900 NA
9 2017 catch 2016 NA 0.900 NA
10 2018 catch 2017 NA 0.900 NA
We run the projection as normal
ple4_rel_catch <- fwd(ple4_mtf, control = ctrl_rel_catch, sr = ple4_sr)
catch(ple4_rel_catch)
An object of class "FLQuant"
, , unit = unique, season = all, area = unique
year
age 1957 1958 1959 1960 1961
all 78423 88240 109238 117138 118331
[ ... 52 years]
year
age 2014 2015 2016 2017 2018
all 51040 45936 41342 37208 33487
catch(ple4_rel_catch)[,ac(2008:2018)] / catch(ple4_rel_catch)[,ac(2007:2017)]
An object of class "FLQuant"
, , unit = unique, season = all, area = unique
year
age 2008 2009 2010 2011 2012
all 1.0638 0.9000 0.9000 0.9000 0.9000
[ ... 1 years]
year
age 2014 2015 2016 2017 2018
all 0.9 0.9 0.9 0.9 0.9
plot(window(ple4_rel_catch, start = 2001, end = 2018))
This is equivalent to the catch example above (LINK) but without using absolute values.
In this Example we introduce two new things:
Here we set an F target so that the future F = F0.1. However, we also don’t want the catch to fall below a minimum level. We do this by setting a minimum value for the catch.
First we set a value for F0.1 (you could use the FLBRP
package to do this (LINK))
f01 <- 0.1
We’ll set our minimum catch to be the mean catch of the last 3 years.
min_catch <- mean(catch(ple4_mtf)[,as.character(2006:2008)])
min_catch
[1] 102510
To create the control object, we make a data.frame
with both target types. Note that we include a min column.
df <- data.frame(
year = rep(2009:2018, each=2),
quant = c("f","catch"),
value = c(f01, NA),
min = c(NA, min_catch))
It is also important that when running the projection, the bounding targets (the min and the max) are processed after the non-bounding targets. This should be sorted out by the fwdControl
constructor.
Make the control object
ctrl_min_catch <- fwdControl(df)
ctrl_min_catch
An object of class "fwdControl"
(step) year quant min value max
1 2009 f NA 0.100 NA
2 2009 catch 102509.578 NA NA
3 2010 f NA 0.100 NA
4 2010 catch 102509.578 NA NA
5 2011 f NA 0.100 NA
6 2011 catch 102509.578 NA NA
7 2012 f NA 0.100 NA
8 2012 catch 102509.578 NA NA
9 2013 f NA 0.100 NA
10 2013 catch 102509.578 NA NA
11 2014 f NA 0.100 NA
12 2014 catch 102509.578 NA NA
13 2015 f NA 0.100 NA
14 2015 catch 102509.578 NA NA
15 2016 f NA 0.100 NA
16 2016 catch 102509.578 NA NA
17 2017 f NA 0.100 NA
18 2017 catch 102509.578 NA NA
19 2018 f NA 0.100 NA
20 2018 catch 102509.578 NA NA
What did we create? We can see that the min column has now got some data (the max column is still empty) and the targets appear in the correct order. Now project forward
ple4_min_catch <- fwd(ple4_mtf, control = ctrl_min_catch, sr = ple4_sr)
fbar(ple4_min_catch)[,ac(2008:2018)]
An object of class "FLQuant"
, , unit = unique, season = all, area = unique
year
age 2008 2009 2010 2011 2012
all 0.35631 0.34056 0.30514 0.26839 0.23888
[ ... 1 years]
year
age 2014 2015 2016 2017 2018
all 0.18829 0.16898 0.15188 0.13752 0.12523
catch(ple4_min_catch)[,ac(2008:2018)]
An object of class "FLQuant"
, , unit = unique, season = all, area = unique
year
age 2008 2009 2010 2011 2012
all 96040 102510 102510 102510 102510
[ ... 1 years]
year
age 2014 2015 2016 2017 2018
all 102510 102510 102510 102510 102510
What happens? The catch constraint is hit in every year of the projection. The projected F decreases but never hits the target F because the minimum catch constraint prevents it from dropping further.
plot(window(ple4_min_catch, start = 2001, end = 2018))
It is possible to also set a maximum constraint, for example, to prevent F from being too large.
In this example we use a combination of relative targets and bounds.
This kind of approach can be used to model a recovery plan. For example, we want to decrease F to F0.1 by 2015 (absolute target value) but catches cannot change by more than 15% each year (relative bound). This requires careful setting up of the control object.
We make a vector of the desired F targets using the F0.1 we calculated above. We set up an F sequence that decreases from the current Fbar in 2008 to F01 in 2015, then F01 until 2018.
current_fbar <- c(fbar(ple4)[,"2008"])
f_target <- c(seq(from = current_fbar, to = f01, length = 8)[-1], rep(f01, 3))
f_target
[1] 0.3197 0.2831 0.2465 0.2098 0.1732 0.1366 0.1000 0.1000 0.1000 0.1000
We set maximum annual change in catch to be 10% (in either direction).
rel_catch_bound <- 0.10
We make the control data.frame
with the F target and the catch target. Note the use of the relYear, min and max columns in the dataframe.
df <- data.frame(
year = rep(2009:2018, 2),
relYear =c(rep(NA,10), 2008:2017),
quant = c(rep("f",10), rep("catch",10)),
value = c(f_target, rep(NA,10)),
max = c(rep(NA,10), rep(1+rel_catch_bound, 10)),
min = c(rep(NA,10), rep(1-rel_catch_bound, 10)))
Make the control object. The min and max columns now both have data
ctrl_rel_min_max_catch <- fwdControl(df)
ctrl_rel_min_max_catch
An object of class "fwdControl"
(step) year quant relYear min value max
1 2009 f NA NA 0.320 NA
2 2009 catch 2008 0.900 NA 1.100
3 2010 f NA NA 0.283 NA
4 2010 catch 2009 0.900 NA 1.100
5 2011 f NA NA 0.246 NA
6 2011 catch 2010 0.900 NA 1.100
7 2012 f NA NA 0.210 NA
8 2012 catch 2011 0.900 NA 1.100
9 2013 f NA NA 0.173 NA
10 2013 catch 2012 0.900 NA 1.100
11 2014 f NA NA 0.137 NA
12 2014 catch 2013 0.900 NA 1.100
13 2015 f NA NA 0.100 NA
14 2015 catch 2014 0.900 NA 1.100
15 2016 f NA NA 0.100 NA
16 2016 catch 2015 0.900 NA 1.100
17 2017 f NA NA 0.100 NA
18 2017 catch 2016 0.900 NA 1.100
19 2018 f NA NA 0.100 NA
20 2018 catch 2017 0.900 NA 1.100
Run the projection:
recovery<-fwd(ple4_mtf, control=ctrl_rel_min_max_catch, sr=ple4_sr)
What happened? The F decreased and then remains constant, while the catch has changed by only a limited amount each year.
plot(window(recovery, start = 2001, end = 2018))
The minimum and maximum bounds on the catch are operational in several of the years. They prevent the catch from increasing as well as decreasing too strongly, (allegedly) providing stability to the fishery.
catch(recovery)[,ac(2009:2018)] / catch(recovery)[,ac(2008:2017)]
An object of class "FLQuant"
An object of class "FLQuant"
, , unit = unique, season = all, area = unique
year
age 2009 2010 2011 2012 2013 2014 2015 2016
all 1.01188 1.00637 1.00410 0.97043 0.94868 0.91367 0.90000 1.07802
year
age 2017 2018
all 1.10000 1.10000
units:
So far we have looked at combinations of:
But all of the projections have been deterministic, i.e. they all had only one iteration. Now, we are going start looking at projecting with multiple iterations. This is important because it can help us understand the impact of uncertainty (e.g. in the stock-recruitment relationship).
fwd() is happy to work over iterations. It treats each iteration separately. “All” you need to do is set the arguments correctly.
There are two main ways of introducing iterations into fwd():
You can actually use both of these methods at the same time. As you can probably imagine, this can quickly become very complicated so we’ll just do some simple examples to start with.
To perform a stochastic projection you need a stock object with multiple iterations. If you are using the output of a stock assessment method, such as a4a, then you may have one already. Here we use the propagate() method to expand the ple4 stock object to have 200 iterations. We’ll use the ten year projection as before (remember that we probably should change the assumptions that come with the stf() method).
niters <- 200
ple4_mtf <- stf(ple4, nyears = 10)
ple4_mtf <- propagate(ple4_mtf, niters)
You can see that the 6th dimension, iterations, now has length 200:
summary(ple4_mtf)
An object of class "FLStock"
Name: Plaice in IV
Description: Imported from a VPA file. ( N:\Projecten\ICES WG\Demersale werkgroep [...]
Quant: age
Dims: age year unit season area iter
10 62 1 1 1 200
Range: min max pgroup minyear maxyear minfbar maxfbar
1 10 10 1957 2018 2 6
catch : [ 1 62 1 1 1 200 ], units = t
catch.n : [ 10 62 1 1 1 200 ], units = 10^3
catch.wt : [ 10 62 1 1 1 200 ], units = kg
discards : [ 1 62 1 1 1 200 ], units = t
discards.n : [ 10 62 1 1 1 200 ], units = 10^3
discards.wt : [ 10 62 1 1 1 200 ], units = kg
landings : [ 1 62 1 1 1 200 ], units = t
landings.n : [ 10 62 1 1 1 200 ], units = 10^3
landings.wt : [ 10 62 1 1 1 200 ], units = kg
stock : [ 1 62 1 1 1 200 ], units = t
stock.n : [ 10 62 1 1 1 200 ], units = 10^3
stock.wt : [ 10 62 1 1 1 200 ], units = kg
m : [ 10 62 1 1 1 200 ], units = m
mat : [ 10 62 1 1 1 200 ], units =
harvest : [ 10 62 1 1 1 200 ], units = f
harvest.spwn : [ 10 62 1 1 1 200 ], units =
m.spwn : [ 10 62 1 1 1 200 ], units =
There is an argument to fwd() that we haven’t used yet: residuals
This is used for specifying the recruitment residuals (residuals) which are multiplicative. In this example we’ll use the residuals so that the predicted recruitment values in the projection = deterministic recruitment predicted by the SRR model * residuals. The residuals are passed in as an FLQuant with years and iterations. Here we make an empty FLQuant that will be filled with residuals.
rec_residuals <- FLQuant(NA, dimnames = list(year=2009:2018, iter=1:niters))
We’re going to use residuals from the stock-recruitment relationship we fitted at the beginning. We can access these using:
residuals(ple4_sr)
An object of class "FLQuant"
, , unit = unique, season = all, area = unique
year
age 1958 1959 1960 1961 1962
1 -0.268830 -0.058033 -0.190040 -0.063352 -0.443513
[ ... 41 years]
year
age 2004 2005 2006 2007 2008
1 0.255971 -0.218722 -0.086510 0.057988 -0.057139
These residuals are on a log scale i.e. log_residuals = log(observed_recruitment) - log(predicted_recruitment). To use these log residuals multiplicatively we need to transform them with exp():
We want to fill up our multi_rec_residuals FLQuant by randomly sampling from these log residuals. We can do this with the sample() function. We want to sample with replacement (i.e. if a residual is chosen, it gets put back in the pool and can be chosen again).
First we get generate the samples of the years (indices of the residuals we will pick).
sample_years <- sample(dimnames(residuals(ple4_sr))$year, niters * 10, replace = TRUE)
We fill up the FLQuant we made earlier with the residuals using the sampled years:
rec_residuals[] <- exp(residuals(ple4_sr)[,sample_years])
What have we got?
rec_residuals
An object of class "FLQuant"
An object of class "FLQuant"
iters: 200
, , unit = unique, season = all, area = unique
year
quant 2009 2010 2011 2012
all 0.92550(0.426) 0.94446(0.358) 0.96235(0.475) 0.94446(0.454)
year
quant 2013 2014 2015 2016
all 0.96235(0.407) 0.95227(0.428) 0.99172(0.428) 0.94362(0.355)
year
quant 2017 2018
all 0.94446(0.358) 0.97243(0.398)
units: NA
It’s an FLQuant of SRR residuals but what do those brackets mean? The information in the brackets is the Median Absolute Deviation, a way of summarising the iterations. We have 200 iterations but don’t want to see all of them - just a summary.
We now have the recruitment residuals. We’ll use the ctrl_catch control object we made earlier with decreasing catch. We call fwd() as usual, only now we have a residuals argument. This takes a little time (we have 200 iterations).
ple4_stoch_rec <- fwd(ple4_mtf, control = ctrl_catch, sr = ple4_sr, residuals = rec_residuals)
What just happened? We can see that now we have uncertainty in the recruitment estimates, driven by the residuals. This uncertainty feeds into the SSB and, to a lesser extent, the projected F and catch.
plot(window(ple4_stoch_rec, start = 2001, end = 2018))
We can see that the projected stock metrics also have uncertainty in them.
rec(ple4_stoch_rec)[,ac(2008:2018)]
An object of class "FLQuant"
iters: 200
, , unit = unique, season = all, area = unique
year
age 2008 2009 2010 2011
1 844041( 0) 836074(385110) 857335(325390) 881970(432963)
year
age 2012
1 872054(418206)
[ ... 1 years]
year
age 2014 2015 2016 2017
1 844041( 0) 836074(385110) 857335(325390) 881970(432963)
year
age 2018
1 872054(418206)
fbar(ple4_stoch_rec)[,ac(2008:2018)]
An object of class "FLQuant"
iters: 200
, , unit = unique, season = all, area = unique
year
age 2008 2009 2010 2011
all 0.35631(0.0000) 0.28059(0.0100) 0.21405(0.0300) 0.15290(0.0373)
year
age 2012
all 0.11049(0.0297)
[ ... 1 years]
year
age 2014 2015 2016 2017
all 0.35631(0.0000) 0.28059(0.0100) 0.21405(0.0300) 0.15290(0.0373)
year
age 2018
all 0.11049(0.0297)
ssb(ple4_stoch_rec)[,ac(2008:2018)]
An object of class "FLQuant"
iters: 200
, , unit = unique, season = all, area = unique
year
age 2008 2009 2010 2011
all 206480( 0) 231522( 0) 285544(19908) 355350(45295)
year
age 2012
all 451938(98260)
[ ... 1 years]
year
age 2014 2015 2016 2017
all 206480( 0) 231522( 0) 285544(19908) 355350(45295)
year
age 2018
all 451938(98260)
In this example we introduce uncertainty by including uncertainty in our target values. This example has catch as the target, except now catch will be stochastic.
We will use the ctrl_catch object from above (we make a copy):
ctrl_catch
An object of class "fwdControl"
(step) year quant min value max
1 2009 catch NA 86436.404 NA
2 2010 catch NA 77792.764 NA
3 2011 catch NA 70013.487 NA
4 2012 catch NA 63012.139 NA
5 2013 catch NA 56710.925 NA
6 2014 catch NA 51039.832 NA
7 2015 catch NA 45935.849 NA
8 2016 catch NA 41342.264 NA
9 2017 catch NA 37208.038 NA
10 2018 catch NA 33487.234 NA
ctrl_catch_iters <- ctrl_catch
Let’s take a look at what else is in the control object:
slotNames(ctrl_catch_iters)
[1] "target" "iters" "FCB"
The iterations of the target value are set in the iters slot.
ctrl_catch_iters@iters
, , iter = 1
val
row min value max
1 NA 86436 NA
2 NA 77793 NA
3 NA 70013 NA
4 NA 63012 NA
5 NA 56711 NA
6 NA 51040 NA
7 NA 45936 NA
8 NA 41342 NA
9 NA 37208 NA
10 NA 33487 NA
What is this slot?
class(ctrl_catch_iters@iters)
[1] "array"
dim(ctrl_catch_iters@iters)
[1] 10 3 1
It’s a 3D array with structure: target no x value x iteration. It’s in here that we set the stochastic projection values. Each row of the iters slot corresponds to a row in the control data.frame we passed in.
Here we set 10 targets (one for each year in the projection), so the first dimension of iters has length 10. The second dimension always has length 3 (for min, value and max columns). The third dimension is where the iterations are stored. This is currently length 1. We have 200 iterations and therefore we need to expand iters along the iter dimension so it can store the 200 iterations.
One way of doing this is to make a new array with the right dimensions. Note that we need to put in dimnames.
new_iters <- array(NA, dim=c(10,3,niters), dimnames = list(1:10, c("min","value","max"),iter=1:niters))
dim(new_iters)
[1] 10 3 200
Now we can fill it up with new data (our stochastic catch targets).
We need to generate random catch target data. This could come from a number of sources (e.g. MSY estimated with uncertainty). In this example we make it very simple, by using lognormal distribution with a fixed standard deviation of 0.3. We multiply the deterministic catch target values by samples from this distribution.
future_catch_iters <- ctrl_catch_iters@iters[,"value",] * rlnorm(10 * niters, meanlog = 0, sdlog=0.3)
We fill up iters with these values. We just fill up the value column (you can also set the min and max columns to set stochastic bounds).
new_iters[,"value",] <- future_catch_iters
We put our new iters into the control object:
ctrl_catch_iters@iters <- new_iters
We can see that now we have stochasticity in the target values.
ctrl_catch_iters
An object of class "fwdControl"
(step) year quant min value max
1 2009 catch NA 84109.290(23820.449) NA
2 2010 catch NA 80533.342(23428.470) NA
3 2011 catch NA 70844.549(20788.435) NA
4 2012 catch NA 61861.350(18906.882) NA
5 2013 catch NA 56576.740(17997.790) NA
6 2014 catch NA 50966.141(13603.201) NA
7 2015 catch NA 48688.132(14718.497) NA
8 2016 catch NA 41822.384(12024.879) NA
9 2017 catch NA 37644.407(11618.056) NA
10 2018 catch NA 34090.572(10278.505) NA
iters: 200
We project as normal using the deterministic SRR.
ple4_catch_iters <- fwd(ple4_mtf, control=ctrl_catch_iters, sr = ple4_sr)
What happened?
plot(window(ple4_catch_iters, start = 2001, end = 2018))
The projected catches reflect the uncertainty in the target.
catch(ple4_catch_iters)[,ac(2008:2018)]
An object of class "FLQuant"
iters: 200
, , unit = unique, season = all, area = unique
year
age 2008 2009 2010 2011 2012
all 96040( 0) 84109(23820) 80533(23428) 70845(20788) 61861(18907)
[ ... 1 years]
year
age 2014 2015 2016 2017 2018
all 96040( 0) 84109(23820) 80533(23428) 70845(20788) 61861(18907)
What is going on with recruitment in the results of the previous example?
rec(ple4_catch_iters)[,ac(2008:2018)]
An object of class "FLQuant"
iters: 200
, , unit = unique, season = all, area = unique
year
age 2008 2009 2010 2011 2012
1 844041( 0) 903372( 0) 907749( 0) 915265(2469) 920593(2761)
[ ... 1 years]
year
age 2014 2015 2016 2017 2018
1 844041( 0) 903372( 0) 907749( 0) 915265(2469) 920593(2761)
Remember that here recruitment is not being driven by random residuals, it is only be driven by SSB. The recruitment in year Y is a result of the SSB in year Y-1. The SSB in year Y-1 is a result of the catch in year Y-2. So if catch is stochastic in 2009, we don’t see the impact of the stochasticity on the recruitment until 2011. Even then the impact is small. This seems unlikely so we can also put in recruitment residuals (we already made them for Example 7).
ple4_catch_iters <- fwd(ple4_mtf, control=ctrl_catch_iters, sr = ple4_sr, residuals = rec_residuals)
What happened?
plot(window(ple4_catch_iters, start = 2001, end = 2018))
We have a projection with stochastic target catches and recruitment.
catch(ple4_catch_iters)[,ac(2008:2018)]
An object of class "FLQuant"
iters: 200
, , unit = unique, season = all, area = unique
year
age 2008 2009 2010 2011 2012
all 96040( 0) 84109(23820) 80533(23428) 70845(20788) 61861(18907)
[ ... 1 years]
year
age 2014 2015 2016 2017 2018
all 96040( 0) 84109(23820) 80533(23428) 70845(20788) 61861(18907)
rec(ple4_catch_iters)[,ac(2008:2018)]
An object of class "FLQuant"
iters: 200
, , unit = unique, season = all, area = unique
year
age 2008 2009 2010 2011
1 844041( 0) 836074(385110) 857335(325390) 880963(433424)
year
age 2012
1 870721(416835)
[ ... 1 years]
year
age 2014 2015 2016 2017
1 844041( 0) 836074(385110) 857335(325390) 880963(433424)
year
age 2018
1 870721(416835)
SOMETHING ON CALLING FWD() AND SPECIFYING TARGETS AS ARGUMENTS
SOMETHING ON FWD WINDOW
This document is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.