Biomass Dynamic Management Procedures

Laurence Kell

30 abril, 2017

Introduction

The mpb package implements biomass based methods for stock assessment and simulation testing using Management Strategy Evaluation (MSE). There are two classes biodyn and aspic, the former implements a Pella-Tomlinson production function using ADMB, while the later calls the ASPIC executable from R. Both classes use a common interface making it easy to compare the two implementations.

The provision of fisheries management advice requires the assessment of stock status relative to reference points, the prediction of the response of a stock to management, and checking that predictions are consistent with reality. Therefore mpb includes methods for the estimation of historical stock status, the calcuation of reference points, estimation of uncertainty in parameters, a variety of diagnostic methods, stock projection for a range of management options and tools for simulation testing, e.g. self and cross-testing and for conducting Management Strategy Evaluation (MSE)

Methods

The main processes influencing the dynamics of exploited populations are gains due to growth and recruitment and losses due to fishing and natural mortality. In a biomass dynamic stock assessment model recruitment, growth and natural mortality are simplified into a single production function (\(P\)), for example that of Pella and Tomlinson (1969).

\begin{equation} B_{t+1}=B_{t}-C_{t}+P_{t}\end{equation}

where

\begin{equation}P_t=\frac{r}{p}\cdot~B(1-({\frac{B}{K}})^p)\end{equation}

The dynamics are determined by the population growth rate (\(r\)) at low population size, i.e. in the absence of density dependence. and the shape of the production function (\(p\)). if \(p=1\) then the maximum sustainable yield (MSY) is found halfway between 0 and virgin biomass (\(K\)); as p deccreases MSY shifts to the left. There is seldom suffcient infomation in the catch data to estimate even these few parameters and so additional data are required, e.g. time series of relative abundance from catch per unit effort (CPUE), or surveys.

Biomass dynamic models have been criticised as being too simplistic to capture the actual population dynamics, however, if a simple model can provide robust advice on stock status and the response of a stock to management why use anything more complicated (Ludwig and Walters 1985)? For example the Pella-Tomlinson model is used by the IWC to set catch limits. Neither the form of the model nor its parameters are meant to provide an accurate representation of the dynamics of the population. Rather, it has been demonstrated by simulation that when a biomass dynamic model is used as part of a management strategy with a harvest control rule (HCR) it allows the robust calculation and setting of catches limits Butterworth and Punt (1999).

biodyn

The main class is biodyn, which has methods for importing data, exporting results, fitting models, checking diagnostics, plotting, estimation of uncertainly, projection, simulating HCRs, and for the provision of advice. The robustness of the methods can be simulation tested using MSE. biodyn also includes slots for catch, parameters, historical stock status, reference points, diagnostics, and summary statistics (use ??biodyn for more information)

An object can be created in various way, e.g. using the constructor

bd=biodyn()

by coercion from another class, or using an existing text file such as the input file of ASPIC and then coercing the aspic object into an object of the mpb class

asp=aspic("aspic.inp")
bd =as(asp,"biodyn")

Objects for use in simulation can also be created

bd=sim()

Plotting

are important for examining objects, exploring data, summarising results, checking outputs, and diagnosing problems.

bd=window(sim(),end=49)
plot(bd)+
  theme_bw()
Production function with simulated time series

Production function with simulated time series

mpb uses ggplot2 as this allows the basic plots to be modified as required, for example a trajectory can be added to the plot of the production function

plotProduction(bd)+
  geom_path( aes(stock,catch),
             model.frame(FLQuants(bd,"stock","catch")))+
  geom_point(aes(stock,catch),
             model.frame(FLQuants(bd,"stock","catch")))+
  theme_bw()+theme(legend.position="none")
Simulated CPUE series

Simulated CPUE series

Estimation

can be done using either maximum likelihood or Monte Carlo Markov Chain (MCMC) simulations. Simulation can help to check robustness by allowing estimated values to be compared with the ones used to generate the data.

A CPUE series is needed for fitting and can be simulated using mid year biomass and adding error.

cpue=(stock(bd)[,-dims(bd)$year]+
      stock(bd)[,-1])/2
set.seed(7890)
cpue=rlnorm(1,log(cpue),.2)

ggplot(as.data.frame(cpue))+
  geom_point(aes(year,data))+
  geom_line(aes(year,data),col="salmon",
            data=as.data.frame(stock(bd)))+
  theme_bw()
Simulated CPUE series

Simulated CPUE series

Starting values for the parameters are required. The defaults assume that \(r\) is 0.5, the production function is symetric (i.e. p=1) and the \(b0\) ratio of the initial biomass to \(k\) is 1. MSY should be the same order of magnitude as the catch and so carry capacity (\(k\)) can be calculated if a guess for \(r\) is provided.

params(bd)["k"]=mpb:::guessK(params(bd)["r"],mean(catch(bd),na.rm=T),params(bd)["p"])

Parameters are also required for catchability (\(q\)) and the CV for the CPUE indices; if the population parameters are known then the stock can be calculated from the catch and initial values for \(q\) and the CV derived.

setParams(bd)=cpue
params(bd)
An object of class "FLPar"
params
        r         k         p        b0        q1    sigma1 
  0.50000 818.73967   1.00000   1.00000   0.99575   0.20297 
units:  NA 

Before fitting the control slot has to be provided with the initial guesses, upper and lower bounds (min and max), and the phase for each parameter.

setControl(bd)=params(bd)
control(bd)
An object of class "FLPar"
        option
params   phase       min         val         max        
  r         1.000000    0.050000    0.500000    5.000000
  k         1.000000   81.873967  818.739669 8187.396690
  p        -1.000000    0.100000    1.000000   10.000000
  b0       -1.000000    0.100000    1.000000   10.000000
  q1        1.000000    0.099575    0.995754    9.957543
  sigma1    1.000000    0.020297    0.202975    2.029747
units:  NA 

Difficult to estimate parameters may be fixed by setting the phase (e.g. for \(B_0\) and p) to <0, while parameters can be sequentially estimated by setting phase >0.

Maximum Likelihood

can be performed using maximum likelihood

control(bd)[c("p","b0"),"phase"]=-1
control(bd)["r","max"]=1.0
bdHat=fit(bd,cpue)

Since the true parameter values are known the fit can be checked

params(bdHat)
An object of class "FLPar"
params
        r         k         p        b0        q1    sigma1 
  0.57889 881.42563   1.00000   1.00000   1.11655   1.02502 
units:  NA 
params(bdHat)/params(bd)
An object of class "FLPar"
params
     r      k      p     b0     q1 sigma1 
1.1578 1.0766 1.0000 1.0000 1.1213 5.0500 
units:  NA 
plot(mpb:::biodyns(list("True"=bd,"Hat"=bdHat)))+
  theme(legend.position="bottom")+
  theme_bw()
A comparison of the true and fitted time series

A comparison of the true and fitted time series

Diagnostics

diagnostics are important for replicability, by ensuring that a global solution has actually been found and that assumptions arnt violated, so when the assessment is repeated you get a similar result.

Residuals

Patterns in residuals from the fits of the CPUE to stock abundance may indicate a violation of models assumptions. Which may result in biased estimates of parameters, reference points and stock trends. While variance estimates obtained from bootstrapping assume that residuals are Independently and Identically Distributed (i.i.d.).

The residuals are in the diags slot.

print(head(bdHat@diags),digits=3)
  name year  obs hat residual residualLag    qqx    qqy   qqHat
1    1    1 1061 984    0.075      -0.144  0.184  0.075  0.0279
2    1    2  846 977   -0.144      -0.181 -0.579 -0.144 -0.1514
3    1    3  800 960   -0.181       0.113 -0.708 -0.181 -0.1816
4    1    4 1051 938    0.113      -0.061  0.518  0.113  0.1066
5    1    5  861 915   -0.061       0.102 -0.237 -0.061 -0.0710
6    1    6  988 892    0.102       0.166  0.346  0.102  0.0661

Checking the distribution of residuals can be done by plotting the obsevered quantiles against the predicted quantiles from the assumed distribution using Q-Q plots. These compare a sample of data (the residuals) on the vertical axis to a statistical population (e.g. from a normal distribution) on the horizontal axis. Any nonlinear patterns may imply that the data are not normally distributed i.e. \(X ~ N(0,1)\), for example a systematic departure from a straight line may indicate skewness or over or under dispersion.

rsdl=bdHat@diags
ggplot(rsdl)                                           +
  geom_point( aes(qqx,qqy))                            +
  stat_smooth(aes(qqx,qqHat),method="lm",se=T,fill="blue", alpha=0.1) +
  theme_bw()+theme(legend.position="bottom")               
Quantile-quantile plot to compare residual distribution with the normal distribution.

Quantile-quantile plot to compare residual distribution with the normal distribution.

It is assumed that an index is proportional to the stock so when plotting the observed against the fitted values the points should fall around the \(y=x\) line, if they do not then the index may not be a good proxy for the stock trend.

library(diags)

ggplot(with(rsdl, data.frame(obs=stdz(obs),hat=stdz(hat))))   +
    geom_abline(aes(slope=1,intercept=0))                     +
    geom_point( aes(obs,hat))                                 +
    stat_smooth(aes(obs,hat),method="lm", se=F)               +
    theme_bw()+theme(legend.position="bottom")                +
    xlab("Fitted") + ylab("Observed")
Observed CPUE verses fitted, blue line is a linear resgression fitted to points, black the y=x line.

Observed CPUE verses fitted, blue line is a linear resgression fitted to points, black the y=x line.

To look for systematic patterns the residuals can be plotted by year, a lowess smoother helps to identify if the proxy doesnt agree with the estimated stock trend based on the catch

dat=transform(subset(rsdl,!is.na(residual), 
                     residual=stdz(residual,na.rm=T)))

ggplot(aes(year,residual),data=dat)  +
  geom_hline(aes(yintercept=0))      +
  geom_point()                       +
  stat_smooth(method="loess",se=F)   +
  theme_bw()+theme(legend.position="bottom")                
Residuals by year, with lowess smoother

Residuals by year, with lowess smoother

It is also assumed that variance of the index does not vary with the mean, this can be checked by plotting the residuals against the fitted values.

ggplot(aes(hat, residual),
       data=subset(rsdl,!is.na(hat) & !is.na(residual)))   +
  geom_hline(aes(yintercept=0))         +
  geom_point()                          +
  stat_smooth(method="loess",se=F)      +
  theme_bw()+theme(legend.position="bottom")               
Plot of residuals against fitted value, to check variance relationship.

Plot of residuals against fitted value, to check variance relationship.

It is assumed that the residuals are not autocorrelated, which can be checked by plotting the residuals against each other with a lag of 1. Significant autocorrelations could be due to an increase in catchability with time, which may result in a more optimistic estimate of current stock status as a decline in the stock may be masked by an increase in catchability.

sum(rsdl$residual^2)
[1] 1.92
ggplot(rsdl)                                              +
  geom_point( aes(residual,residualLag))                  +
  stat_smooth(aes(residual,residualLag),method="lm",se=F) +
  geom_hline(aes(yintercept=0))     +
  xlab(expression(Residual[t]))     + 
  ylab(expression(Residual[t+1]))   +
  theme_bw()+theme(legend.position="bottom")                 
Warning: Removed 1 rows containing non-finite values (stat_smooth).
Warning: Removed 1 rows containing missing values (geom_point).

Plot of autocorrelation, i.e. residual_{t+1} verses residual_{t}.

Profiles

Likelihood profiles are useful to check that you are actually at a global solution and not stuck on a small hill with your back to the mountain. They are also useful for evaluating the infomation content of the data and whether different data sets are telling you different things and you need to ask more questions to determine the truth.

The control slot can be used to produce a profile, i.e. fix a parameter or parameters for a range of values and then find the maximum likelihood by estimating the other parameters.

1D

bdHat=fit(bdHat,cpue)
setControl(bdHat)=params(bdHat)
res=profile(bdHat,which='r',fixed=c('b0','p'),
            cpue,range=seq(0.95,1.03,.002))
ggplot(subset(res,ll<0))+
  geom_line(aes(r,ll))  +
  theme_bw()
Likelihood profile for r

Likelihood profile for r

res=profile(bdHat,which=c('r','k'),fixed=c('b0','p'),
            cpue,range=seq(0.97,1.03,.02))
ggplot(res, aes(r, k, z=ll))+ 
  stat_contour(aes(colour = ..level..), size = 1)+
  theme_bw()

likelihood components

bd=sim()

set.seed(7890)
Us  =FLQuants("Unbiased"     =
                rlnorm(1,log((stock(bd)[,-dims(bd)$year]+
                              stock(bd)[,-1])/2),0.2),
              "Increase in q"=
                rlnorm(1,log((stock(bd)[,-dims(bd)$year]+
                              stock(bd)[,-1])/2),0.2))

setParams( bd)=Us
setControl(bd)=params(bd)

bd@control[3:4,"phase"]=-1
bd=fit(bd,index=Us)
bd@control[,c("min")]=bd@params*0.1
bd@control[,c("val")]=bd@params
bd@control[,c("max")]=bd@params*10

prfl=profile(bd,which='r',index=Us,
             range=seq(0.975,1.05,.001))

ggplot(prfl)+
  geom_path(aes(r,ll,group=index,col=index))+
  facet_wrap(~index,scale="free",ncol=1)          +
  theme(legend.position="bottom")+
  theme_bw()
Likelihood profile by data conmponent, i.e. CPUE series

Likelihood profile by data conmponent, i.e. CPUE series

Profile Slot

\newpage

Uncertainty

A main objective of stock assessment is to estimate uncertainly in stock status. This requires estimates of distributions as well as point estimates. As an example a catch and cpue are simulated and fitted using biodyn.

bd   =window(sim(),end=39)
cpue=(stock(bd)[,-dims(bd)$year]+
      stock(bd)[,-1])/2
set.seed(7890)
cpue=rlnorm(1,log(cpue),.2)
bdHat=bd

setParams( bdHat)=cpue
setControl(bdHat)=params(bdHat)
bdHat@control[3:4,"phase"]=-1
bdHat=fit(bdHat,cpue)

sims=mpb:::biodyns(list("True"=bd,"Best Fit"=bdHat))

There are various ways to estimate undercertainty in parameter estimates and quantities derived from them, i.e. use the covariance matrix provided by a maximum likelihood fit, bootstrapping, the jack knife or Bayesian methods such as Monte Carlo Markov Chain,

Variance/Covariance Matrix

Fitting using maximum likelihood provides the covariance matrix for the parameters. Only the \(r\) and \(k\) are of interest, as \(p\) and \(b0\) were fixed and \(q\) and \(sigma\) are nusiance parameters, i.e. are not of immediate interest but which must be accounted for in the analysis.

v=vcov(  bdHat)[c("r","k"),c("r","k"),1]
p=params(bdHat)[c("r","k")]
#refs=mvn(500,p,v)

The Bootstrap

The Bootstrap can be used to simulate CPUE series replicates and the model refitted.

set.seed(7890)
cpueBoot      =boot(bdHat)

sims[["Bootstrap"]]=fit(bdHat,cpueBoot)

Jack knife

The Jack knife is a relatively quick procedure

bdJK =fit(bdHat,FLQuant(jackknife(cpue)))

sims[["Jack Knife"]]=bdJK

MCMC

Monte Carlo Markov Chain

sims[["MCMC"]]=fit(bdHat,cpue,cmdOps=c("-mcmc 1000000, -mcsave 5000"))

Diagnostics need to be run to make sure that the MCMC has actually estimated a stationary distribution.

acf(c(params(sims[["MCMC"]])["r"]))
plot(sims[-4])+
  theme_bw()

\newpage

Stock Status

The Precautionary Approach requires stock status to be estimated relative to reference points. The covariance matrix can be used to estimate uncertainty in derived quantities, i.e. those used for management such as \(F:F_{MSY}\).

bdHat@mng
bdHat@mngVcov
currentState   =bdHat@mng[c("bbmsy","ffmsy"),"hat",drop=T]
currentStateVar=bdHat@mngVcov[c("bbmsy","ffmsy"),
                              c("bbmsy","ffmsy"),drop=T]

refs=mvrnorm(100,currentState,currentStateVar)

ggplot(data=as.data.frame(refs))+
  geom_histogram(aes(x=bbmsy))+
  theme_bw()

Marginal densities for stock

boot=stock(sims[["Bootstrap"]])[,39]

set.seed(7890) 
jack=mpb:::randJack(500,stock(sims[[  "Best Fit"]])[,39],
                  stock(sims[["Jack Knife"]])[,39])

bnow=rbind(data.frame(Method="boot",stock=c(boot)),
           data.frame(Method="jack",stock=c(jack)))
  
ggplot(bnow)+ 
  geom_density(aes(x=stock, y=..count..), position = "stack",fill="red")+
  facet_wrap(~Method,scale="free_y",ncol=1)+
  geom_vline(aes(xintercept=c(stock(sims[["Best Fit"]])[,"39"])))+
  theme_bw()
Densities of Stock from different methods for estimating uncertainty.

Densities of Stock from different methods for estimating uncertainty.

Kobe Phase Plot

library(kobe)

kb=rbind(data.frame(Method="Boot",kobe(sims[["Bootstrap"]], what="pts")),
         data.frame(Method="Jack",kobe(sims[["Jack Knife"]],what="pts")))
             
ggplot(kb)+ 
  geom_point(aes(stock,harvest))+
  facet_wrap(~Method,scale="free_y",ncol=1)+
  theme_bw()

Projections

Once stock parameters and status has been estimated then projections need to be conducted to inform management.

set.seed(7890)
harvest=rlnorm(100,log(harvest(bdHat))[,-dims(bdHat)$year],.1)
bdHat =fwd(bdHat,harvest=harvest)

plot(bdHat,worm=c(2,8))+    
  theme(legend.position="bottom")+
  theme_bw()
Projection

Projection

Harvest Control Rules

Use simulated data to run annual, tri-annual, F bound and TAC bounded HCRs

Annual

bd=window(sim(),end=29)
for (i in seq(29,49,1))
  bd=fwd(bd,harvest=mpb:::hcr(bd,yr=i-1,hyr=i+1:2))
simHCR=mpb:::biodyns(list("Annual"=bd))

Tri-annual

bd=window(bd,end=29)
for (i in seq(29,49,3))
  bd=fwd(bd,harvest=mpb:::hcr(bd,yr=i,hyr=i+1:3))
simHCR[["Triennial"]]=bd

Bound on F

bd=window(bd,end=29)
for (i in seq(29,49,3))
  bd=fwd(bd,harvest=mpb:::hcr(bd,yr=i,byr=i,hyr=i+1:3,bndF=c(0.9,1.1)))
simHCR[["bound F"]]=bd

Bound on catch

bd=window(bd,end=29)
for (i in seq(29,49,3))
  bd=fwd(bd,catch=mpb:::hcr(bd,yr=i,hyr=i+1:3,tac=TRUE,bndTac=c(0.9,1.1)))
simHCR[["bound TAC"]]=bd
plot(simHCR)+
  theme_bw()+
  theme(legend.position="bottom")
Warning: Removed 3 rows containing missing values (geom_path).
Plots of projections

Plots of projections

Process Error and Harvest Control Rule

set.seed(7890)
pe=rlnorm(500,FLQuant(0,dimnames=list(year=1:50)),0.5)

bd=window(sim(),end=30)
bd.=bd
bd@stock =propagate(bd@stock, 500)
bd=fwd(bd,harvest=harvest(bd)[,2:30],pe=pe)

for (i in seq(30,48,1))
  bd=fwd(bd,
         catch=hcr(bd,yr=i,hyr=i+1,tac=TRUE,bndTac=c(0.9,1.1)),
         pe   =pe)

plot(bd)+
  theme_bw()

Advice

library(plyr)
library(mpb)
library(reshape)
library(kobe)
bd=sim()

#source('~/Desktop/flr/mpb/R/biodyn-msy.R')
#source('~/Desktop/flr/mpb/R/biodyn-hcr.R')

trks=kobe(bd,what="trks")
trks=mdply(data.frame(Year=seq(33,49,3)), 
           function(Year) subset(trks,year<=Year))

pts =mdply(data.frame(Year=seq(33,49,3)),function(Year)
                 kobe(bd,year=Year,what="pts"))

kobePhase()+    
    geom_line(aes(stock,harvest),data=plotHcr(bd),
              col="brown",size=1.5)                             +    
    geom_path( aes(stock,harvest),data=subset(trks,pctl=="50%"),col="blue")+
    geom_point(aes(stock,harvest),data=subset(pts,year>=33),size=.3,col="cyan")    +
    facet_wrap(~Year)
Warning in if (is(pts) == "try-error") t. = as(rbind(as.data.frame(pts.
[1, : the condition has length > 1 and only the first element will be used

MSE

mseBiodyn
\newpage

References

Butterworth, DS, and AE Punt. 1999. “Experiences in the Evaluation and Implementation of Management Procedures.” ICES J. Mar. Sci. 56 (6). Oxford University Press: 985–98.

Ludwig, Donald, and Carl J Walters. 1985. “Are Age-Structured Models Appropriate for Catch-Effort Data?” Can. J. Fish. Aquat. Sci. 42 (6). NRC Research Press: 1066–72.

Pella, J.J., and P.K. Tomlinson. 1969. A Generalized Stock Production Model. Inter-American Tropical Tuna Commission.