1 Introduction

Note: This document has been updated in May 2023. Structure of CMPs has changed slightly from previous versions

This document describes how to develop Management Procedures (MPs) and use them in the North Swordfish MSE (SWOMSE) framework.

SWOMSE is an R package designed to evaluate alternative management policies for the North Atlantic Swordfish fishery. The package is built based on the openMSE framework. The openMSE package is automatically installed when SWOMSE is installed, and all openMSE functions are available to the user when the SWOMSE package is loaded.

openMSE is an R package that has been developed for conducting fast, flexible, and transparent, MSE for a wide range of fisheries. A non-technical description of openMSE and its key features is available on the openMSE website.

More information on the SWOMSE package is available in the SWOMSE User Manual and the Trial Specifications Document.

All documents relating to the North Atlantic Swordfish MSE process can be found on the North Atlantic Swordfish MSE homepage

2 Overview of Management Procedures

A Management Procedure (MP) is a set of rules that define how fishery data is converted into a management recommendation. The openMSE framework allows users to design MPs that return management recommendations with any combination of the following management options:

  1. setting a catch limit (e.g., TAC);
  2. setting an effort limit (e.g., maximum allowable effort);
  3. closing a spatial area;
  4. setting a minimum size of retention (e.g., a size limit);
  5. setting an upper harvest slot limit;
  6. changing the selectivity of the fishing gear.

Since the swordfish fishery is managed by a TAC, in this guide we will only focus on MPs that return a total allowable catch limit. For more information on MPs, see the Management Procedures page on the openMSE website.

In the SWOMSE framework, an MP is a special function of class MP. Functions of class MP take a Data object and return an object of class Rec (recommendation). You can read more about objects of class Rec here.

openMSE includes a large number of data-rich and data-limited MPs, and these are all available within the SWOMSE framework:

avail('MP')
#> ✔ Searching for objects of class  MP  in package:  MSEtool
#> ✔ Searching for objects of class  MP  in package:  SAMtool
#> ✔ Searching for objects of class  MP  in package:  DLMtool
#>   [1] "curEref"      "FMSYref"      "FMSYref50"    "FMSYref75"    "NFref"       
#>   [6] "DDSS_4010"    "DDSS_75MSY"   "DDSS_MSY"     "SCA_4010"     "SCA_75MSY"   
#>  [11] "SCA_MSY"      "SP_4010"      "SP_75MSY"     "SP_MSY"       "SSS_4010"    
#>  [16] "SSS_75MSY"    "SSS_MSY"      "AvC"          "AvC_MLL"      "BK"          
#>  [21] "BK_CC"        "BK_ML"        "CC1"          "CC2"          "CC3"         
#>  [26] "CC4"          "CC5"          "CompSRA"      "CompSRA4010"  "CurC"        
#>  [31] "curE"         "curE75"       "DAAC"         "DBSRA"        "DBSRA_40"    
#>  [36] "DBSRA4010"    "DCAC"         "DCAC_40"      "DCAC_ML"      "DCAC4010"    
#>  [41] "DCACs"        "DD"           "DD4010"       "DDe"          "DDe75"       
#>  [46] "DDes"         "DepF"         "DTe40"        "DTe50"        "DynF"        
#>  [51] "EtargetLopt"  "Fadapt"       "Fdem"         "Fdem_CC"      "Fdem_ML"     
#>  [56] "Fratio"       "Fratio_CC"    "Fratio_ML"    "Fratio4010"   "GB_CC"       
#>  [61] "GB_slope"     "GB_target"    "Gcontrol"     "HDAAC"        "ICI"         
#>  [66] "ICI2"         "Iratio"       "Islope1"      "Islope2"      "Islope3"     
#>  [71] "Islope4"      "IT10"         "IT5"          "Itarget1"     "Itarget1_MPA"
#>  [76] "Itarget2"     "Itarget3"     "Itarget4"     "ItargetE1"    "ItargetE2"   
#>  [81] "ItargetE3"    "ItargetE4"    "ITe10"        "ITe5"         "ITM"         
#>  [86] "L95target"    "LBSPR"        "LBSPR_MLL"    "Lratio_BHI"   "Lratio_BHI2" 
#>  [91] "Lratio_BHI3"  "LstepCC1"     "LstepCC2"     "LstepCC3"     "LstepCC4"    
#>  [96] "LstepCE1"     "LstepCE2"     "Ltarget1"     "Ltarget2"     "Ltarget3"    
#> [101] "Ltarget4"     "LtargetE1"    "LtargetE4"    "matlenlim"    "matlenlim2"  
#> [106] "MCD"          "MCD4010"      "minlenLopt1"  "MRnoreal"     "MRreal"      
#> [111] "Rcontrol"     "Rcontrol2"    "SBT1"         "SBT2"         "slotlim"     
#> [116] "SPmod"        "SPMSY"        "SPslope"      "SPSRA"        "SPSRA_ML"    
#> [121] "YPR"          "YPR_CC"       "YPR_ML"

You can access help documentation for any of these built-in functions in the usual way. For example;

?DBSRA

These built-in MPs can be used within the SWOMSE framework, but should be applied within a wrapper function to ensure that they maintain the specific requirements of the North Atlantic Swordfish MSE process.

3 Overview of an MP Function

3.1 General Function Arguments

All MP functions have a similar design. The function must take arguments x, Data, and ..., and be assigned to class MP. For example:

myMP <- function(x, Data, ...) {
  # MP code goes here
}
class(myMP) <- 'MP'

The x argument refers to the simulation number. When applied to real fishery data, x is always 1 (there is only one real world data set). Within the closed-loop MSE framework, x takes on values from 1 to nsim - the number of simulations in the analysis. The simulated data for each simulation may be different due to different environmental (e.g., recruitment deviations) and observation processes that are used for each simulation.

The Data argument is an object of class Data and contains either the real fishery data (i.e., SWOData) or the simulated data generated during the MSE analysis.

The ... argument is not used here but it is necessary to make the function compatible with the openMSE framework.

Finally, MP functions must be assigned class MP so that the MSE framework recognizes these functions as management procedures.

3.2 North Atlantic Swordfish Specific MP Arguments

MPs within the SWOMSE framework require four additional arguments specific to the North Atlantic MSE process: Data_Lag, Interval, tunepar, and mc.

3.2.1 Data_Lag

Data_Lag specifies the lag in available data from the current year when the MP is being applied. In the MSE framework, the MPs are always provided with the simulated fishery data up to the previous year. For example, if a MP is implemented in the simulated projections in year \(t\), the catch and indices of abundance data provided to the MP are up to and including year \(t-1\).

The Data_Lag argument is used inside the MP code to add an additional lag to the fishery data. The default value for Data_Lag is 2, which means the data provided to the MPs will be from 3 years before the current year. See the help documentation in ?Lag_Data for more information.

3.2.2 Interval

The Interval argument is used to set the management interval when the TAC advice is updated. The default value in the North Atlantic Swordfish MSE process is a 3-year management interval (see the Trial Specifications Document for more details).

3.2.3 tunepar

The tunepar argument is used for tuning the CMPs to specific performance metric targets (see Tuning CMPs). All CMPs must include tunepar as a tuning parameter within the CMP (e.g., tunepar could be the target exploitation rate).

3.2.4 mc

mc describes the maximum absolute change in the TAC between management cycles. It is specified as a fraction, e.g., mc=0.25 means the TAC cannot increase/decrease by more than 25% from one management cycle to the next. The constraint on maximum change in the TAC is ignored if mc=NA. This parameter is included in the tuning (see Tuning CMPs).

4 Custom Management Procedures

4.1 A Simple Example

Here we start with a simple example to demonstrate the key components of developing a management procedure.

This custom MP uses the AvC (average catch) MP from the DLMtool package and available within the SWOMSE framework. The MPs built into the openMSE framework can easily be used by creating a wrapper function with the additional Data_Lag and Interval arguments, and a few additional lines of code:

Average_Catch <- function(x, Data, Data_Lag=2, Interval=3, tunepar=1, mc=0.25, ...) {
  
  # 1. Create a `Rec` (recommendation) object
  Rec <- new('Rec')
  
  # 2. Check if TAC needs to be updated 
   if (SameTAC(Initial_MP_Yr, Interval, Data)) {
    Rec@TAC <- Data@MPrec[x]
    Rec <- FixedTAC(Rec, Data) # use actual catches if they are available
    return(Rec)
  }
  
  # 3. Lag the simulated data by `Data_Lag` years
  Data <- Lag_Data(Data, Data_Lag)

  # 4. Start of MP-specific Code 
  # If the function gets to here, apply the `AvC`
  # function from the `DLMtool` package
  Rec <- DLMtool::AvC(x, Data, ...)
  
  # 5. Apply the tuning parameter
  TAC <- tunepar * Rec@TAC
  
  # 6. Maximum allowed change in TAC
  Rec@TAC <- MaxChange(TAC, Data@MPrec[x], mc)
  
  # 7. Return the `Rec` object
  Rec
}
# 8. Assign function to class `MP`
class(Average_Catch) <- 'MP'

As indicated by the in-code comments, this custom MP has 8 sections:

  1. Create an object of class Rec. This object is returned by the function and contains the management recommendation(s). See the help documentation for more information on the Rec object.

  2. Check if the TAC should be kept unchanged. The SameTAC function will return TRUE if the projection year is before Initial_MP_Yr or if the projection year in not a TAC update year (specified with the Interval argument). As discussed above, the operating models are conditioned up to and including 2020. That means the first projection year in the simulated projection period will be 2021. According to the North Atlantic Swordfish MSE Roadmap, an MP is scheduled to be adopted in 2023 and implemented into the fishery in 2024. The code in this section returns the most recent TAC (stored in the Data@MPrec slot) if the most recent value in the Year slot of the Data object is less than 2023 or if the projection year is not a TAC update year.

  3. Lag the simulated data by the specified number of years. Here the Data object is updated by applying the lag specified in the Data_Lag argument.

  4. Apply the MP-specific code using the lagged data. Here we are simply using the built-in MP called AvC from the DLMtool package. We pass the arguments x and Data and it returns an object of class Rec.

  5. Apply the tuning parameter, here it adjusts the historical mean catch by tunepar.

  6. Apply the maximum change in TAC constraint (if applicable)

  7. The final line in the MP code returns the Rec object.

  8. After a custom MP is developed, it must be assigned to class MP so that the SWOMSE framework recognizes the function as a management procedure.

All custom MPs in the SWOMSE framework should follow this same structure.

4.2 Model-Free MPs

4.2.1 A Custom Model-Free Index Targeting MP

Here we create a custom model-free MP that calculates the average index over the last several years and compares it to the average index value over all years. It uses the ratio of these values to iteratively adjust, with constraints setting the maximum and minimum relative change in the TAC.

ITarget_1 <- function(x, Data,
                      Data_Lag=2, Interval=3,
                      yrsmth = 5, mc = 0.2, tunepar=1,
                      ...) {
  # 1. Create a `Rec` (recommendation) object
  Rec <- new('Rec')

  # 2. Check if TAC needs to be updated 
   if (SameTAC(Initial_MP_Yr, Interval, Data)) {
    Rec@TAC <- Data@MPrec[x]
    Rec <- FixedTAC(Rec, Data) # use actual catches if they are available
    return(Rec)
  }

  # 3. Lag the simulated data by `Data_Lag` years
  Data <- Lag_Data(Data, Data_Lag)

  # 4. Start of MP-specific Code 
  # number of years of index data
  n_years <- length(Data@Ind[x,])

  # year index for `yrsmth` most recent years
  yr_ind <- max(1, n_years-yrsmth+1):n_years

  # index target - mean index x `tunepar`
  Ind_Target <- mean(Data@Ind[x, ], na.rm=TRUE) * tunepar

  # ratio of mean recent index to Ind_Target
  deltaI <- mean(Data@Ind[x, yr_ind], na.rm=TRUE)/Ind_Target

  TAC <- Data@MPrec[x] * deltaI
  
  Rec@TAC <- MaxChange(TAC, Data@MPrec[x], mc)
  
  # 5. Return the `Rec` object
  Rec
}
# 6. Assign function to class `MP`
class(ITarget_1) <- 'MP'

4.2.2 An openMSE Index Targeting MP

Here we create a second iterative TAC index targeting method. This one uses the Itarget1 function from the DLMtool package, which was developed by Geromont & Butterworth (2014). See here or ?Itarget1 for more details.

ITarget_2 <- function(x, Data,
                      Data_Lag=2, Interval=3,
                      yrsmth = 5, xx = 0, tunepar=1.5,
                      ...) {
  Rec <- new('Rec')
  
   if (SameTAC(Initial_MP_Yr, Interval, Data)) {
    Rec@TAC <- Data@MPrec[x]
    Rec <- FixedTAC(Rec, Data) # use actual catches if they are available
    return(Rec)
  }

  Data <- Lag_Data(Data, Data_Lag)
  Data@Year <- Data@Year[1:length(Data@Cat[1,])]
  Rec <- DLMtool::Itarget1(x, Data, yrsmth = yrsmth, xx=xx, Imulti=tunepar, ...)
  Rec@TAC <- MaxChange(Rec@TAC, Data@MPrec[x], mc)
  Rec
}
class(ITarget_2) <- 'MP'

4.3 Model-Based MPs

In this section we develop three MPs that use surplus production stock assessment models.

The assessment models generate estimates of vulnerable stock biomass, \(F_{\text{MSY}}\), and other metrics related to stock status. A harvest control rule must be specified to convert these estimates into a TAC recommendation.

In these examples, we use a simple harvest control rule where the TAC is set to the MSY estimated by the assessment model.

Alternative harvest control rules can easily be developed by modifying this part of the function code.

4.3.1 openMSE Surplus Production Assessment 1

This MP uses the SP assessment model from the SAMtool package. The assumes continuous surplus production and fishing is modeled with sub-annual time steps, and is designed to approximate the behavior of ASPIC (Prager 1994). See ?SP for more details.

The MP includes a HCR that linearly reduces F when B/BMSY < 1.

SP_1 <- function(x, Data, Data_Lag=2, Interval=3, tunepar=1, mc=0.25, ...) {

  Rec <- new('Rec')

  # Does TAC need to be updated? (or set a fixed catch if before Initial_MP_Yr)
  if (SameTAC(Initial_MP_Yr, Interval, Data)) {
    Rec@TAC <- Data@MPrec[x]
    Rec <- FixedTAC(Rec, Data) # use actual catches if they are available
    return(Rec)
  }

  # Lag Data
  Data <- Lag_Data(Data, Data_Lag)

  # apply SP assessment model
  Mod <- SAMtool::SP(x, Data)

  # harvest control rule
  # based on: https://www.iccat.int/Documents/Recs/compendiopdf-e/2017-04-e.pdf
  Bthresh <- Mod@BMSY
  Blim <- 0.4 * Bthresh
  Ftar <- 0.8 * tunepar * Mod@FMSY
  Fmin <- 0.1 * tunepar * Mod@FMSY
  Bcurr <- Mod@B[length(Mod@B)]

  if (Bcurr>=Bthresh) {
    Fmort <- Ftar
  } else if (Bcurr>Blim) {
    Fmort <- Ftar * (-0.367 + 1.167*  Bcurr/Bthresh)
  } else {
    Fmort <- Fmin
  }

  M <- 0.2 # assumed natural mortality
  Z <- M+Fmort
  TAC <-  Fmort/Z*(1-exp(-Z))*Bcurr

  # Maximum allowed change in TAC
  Rec@TAC <- MaxChange(TAC, Data@MPrec[x], mc)
  Rec
}
class(SP_1) <- 'MP'

4.3.2 openMSE Surplus Production Assessment 2 (Fox Model)

This MP uses the SP_Fox assessment model from the SAMtool package, which fixes BMSY/K = 0.37.

This model is conditioned on catch and estimates a predicted index. The assumes continuous surplus production and fishing is modeled with sub-annual time steps, and is designed to approximate the behavior of ASPIC (Prager 1994). See ?SP_Fox for more details.

The MP includes a HCR that linearly reduces F when B/BMSY < 1.

SP_Fox_1 <- function(x, Data, Data_Lag=2, Interval=3, tunepar=1, mc=0.25, ...) {

  Rec <- new('Rec')

  # Does TAC need to be updated? (or set a fixed catch if before Initial_MP_Yr)
  if (SameTAC(Initial_MP_Yr, Interval, Data)) {
    Rec@TAC <- Data@MPrec[x]
    Rec <- FixedTAC(Rec, Data) # use actual catches if they are available
    return(Rec)
  }

  # Lag Data
  Data <- Lag_Data(Data, Data_Lag)

  # apply SP assessment model
  Mod <- SAMtool::SP_Fox(x, Data)

  # harvest control rule
  # based on: https://www.iccat.int/Documents/Recs/compendiopdf-e/2017-04-e.pdf
  Bthresh <- Mod@BMSY
  Blim <- 0.4 * Bthresh
  Ftar <- 0.8 * tunepar * Mod@FMSY
  Fmin <- 0.1 * tunepar * Mod@FMSY
  Bcurr <- Mod@B[length(Mod@B)]

  if (Bcurr>=Bthresh) {
    Fmort <- Ftar
  } else if (Bcurr>Blim) {
    Fmort <- Mod@FMSY * (-0.367 + 1.167*  Bcurr/Bthresh)
  } else {
    Fmort <- Fmin
  }

  M <- 0.2 # assumed natural mortality
  Z <- M+Fmort
  TAC <-  Fmort/Z*(1-exp(-Z))*Bcurr

  # Maximum allowed change in TAC
  Rec@TAC <- MaxChange(TAC, Data@MPrec[x], mc)
  Rec
}
class(SP_Fox_1) <- 'MP'

5 Applying MPs to Real Fishery Data

Now that we’ve defined some custom MPs, we can apply them to our fishery data to see what TAC recommendations they will provide.

The current fishery data for the North Atlantic Swordfish fishery is available in the SWOData object. We can apply the MPs to the data by simply calling the functions with the SWOData object:

Average_Catch(1, SWOData)
#> TAC (median) 
#>         9729
ITarget_1(1, SWOData)
#> TAC (median) 
#>         9729
ITarget_2(1, SWOData)
#> TAC (median) 
#>         9729
SP_1(1, SWOData)
#> TAC (median) 
#>         9729
SP_Fox_1(1, SWOData)
#> TAC (median) 
#>         9729

We can also run all 5 MPs at the same time:

TACs <- applyMP(SWOData, MPs=c('Average_Catch', 'ITarget_1', 'ITarget_2', 'SP_1', 'SP_Fox_1'), reps=1)
#> ℹ Attempting to run  5  MPs:
#> ✔ Average_Catch
#> ✔ ITarget_1
#> ✔ ITarget_2
#> ✔ SP_1
#> ✔ SP_Fox_1
data.frame(MP=TACs[[2]]@MPs, TAC=TACs[[2]]@TAC[,1,1])
#>              MP  TAC
#> 1 Average_Catch 9729
#> 2     ITarget_1 9729
#> 3     ITarget_2 9729
#> 4          SP_1 9729
#> 5      SP_Fox_1 9729

Ok, the MPs all returned a TAC recommendation, but they are all identical! This is because the model is being applied with data up to 2020, and so the TAC for 2021 is being returned. The TAC recommendations won’t be different until 2024 (see above.

We can modify the Year slot in the SWOData object to force the MPs to calculate a TAC from the index in the Data object:

Data <- SWOData # make a copy
Data@Year <- 1950:2023 # set Year up to 2023

# Apply MPs to new Data object
TACs <- applyMP(Data, MPs=c('Average_Catch', 'ITarget_1', 'ITarget_2', 'SP_1', 'SP_Fox_1'), reps=1)
#> ℹ Attempting to run  5  MPs:
#> ✔ Average_Catch
#> ✔ ITarget_1
#> ✔ ITarget_2
#> Warning in applyMP(Data, MPs = c("Average_Catch", "ITarget_1", "ITarget_2", : Method ITarget_2 failed with error: Error in MaxChange(Rec@TAC, Data@MPrec[x], mc) : object 'mc' not found
#> ✔ SP_1
#> ✔ SP_Fox_1
data.frame(MP=TACs[[2]]@MPs, TAC=round(TACs[[2]]@TAC[,1,1],0))
#>              MP   TAC
#> 1 Average_Catch 10361
#> 2     ITarget_1 10560
#> 3     ITarget_2    NA
#> 4          SP_1  9900
#> 5      SP_Fox_1  9900

It’s clear which of these MPs is likely to provide the highest TAC in 2024. But which of these will best meet the management objectives for the fishery?

To determine this, we need to evaluate them in using closed-loop simulation testing. A description of how to conduct the closed-loop simulation testing in available in SWOMSE package User Manual.

6 Tuning Candidate Management Procedures