Skip to contents

Load inputs

Below the required R libraries are loaded. The sbt TMB model is dynamically loaded along with several R functions using library(sbt). The kableExtra package is used for generating tables of inputs. The code theme_set(theme_bw()) alters the plot aesthetics.

Below are example inputs that are loaded from the sbt package. Normally the user would import these inputs (e.g., from .csv files using the read_csv function).

attach(data_csv1)
names(data_csv1)
#> [1] "scenarios_surface" "scenarios_LL1"     "sel_change_sd"
names(data_par1)
#>  [1] "ln_B0"             "lnq"               "lnqhsp"           
#>  [4] "deltalnq86"        "steep"             "sigma_r"          
#>  [7] "sigma_cpue"        "m4"                "m0"               
#> [10] "m10"               "m30"               "cpue_omega"       
#> [13] "psi"               "Reps"              "ln_bdiff"         
#> [16] "ln_sel_aerial"     "tau_aerial"        "tau_troll"        
#> [19] "tag_H_factor"      "par_sels_init_i"   "par_sels_change_i"
#> [22] "par_log_hstar_i"
names(data_labrep1)
#>   [1] "scenario_number"      "n"                    "years"               
#>   [4] "ObjF"                 "lnlike"               "penal"               
#>   [7] "Hhigh"                "H2003"                "H2004"               
#>  [10] "sigma.cpue"           "tag.var.factor"       "gtOD.factor"         
#>  [13] "tau.aerial"           "tau.troll"            "sigma.r"             
#>  [16] "rec_AC"               "AC_penalty"           "q_AC"                
#>  [19] "res.stats"            "B0"                   "R0"                  
#>  [22] "alpha"                "beta"                 "rho"                 
#>  [25] "steep"                "depletion"            "psi"                 
#>  [28] "lnq"                  "qhsp"                 "qgt"                 
#>  [31] "M"                    "omega"                "TOTbio"              
#>  [34] "B10_plus"             "Sbio"                 "phi"                 
#>  [37] "phsp"                 "Recruitment"          "Rdev"                
#>  [40] "Reps"                 "ages"                 "yrs.catch"           
#>  [43] "Ns"                   "Fs"                   "sel"                 
#>  [46] "yrs.cpue"             "cpue"                 "cpue.pred"           
#>  [49] "cpue.resid"           "Aerial.sw"            "Aerial.Surv"         
#>  [52] "Troll.sw"             "Troll.Index"          "ages.1"              
#>  [55] "age.obs.1"            "age.pred_1"           "age.res_1"           
#>  [58] "ages.2"               "age.obs.2"            "age.pred_2"          
#>  [61] "age.res_2"            "lengths"              "len.obs"             
#>  [64] "len.pred"             "len.res"              "catch.pred"          
#>  [67] "catch.weight.pred"    "specs"                "Nsamp"               
#>  [70] "sel.change.sd"        "sel.smooth.sd"        "tag.obs"             
#>  [73] "tag.pred"             "tag.res"              "pooled.tag.obs"      
#>  [76] "pooled.tag.pred"      "pooled.1yeartag.obs"  "pooled.1yeartag.pred"
#>  [79] "spweights.age"        "weights.ageLL1"       "weights.ageLL2"      
#>  [82] "weights.ageSurf"      "Bage"                 "Fage"                
#>  [85] "MSY"                  "Bmsy"                 "TBmsy"               
#>  [88] "Fmsy_a215"            "F_a215"               "Fmsy_a"              
#>  [91] "Bmsy_a"               "sB0"                  "sB0.sd"              
#>  [94] "sB1980"               "sB1980.sd"            "sBfinal"             
#>  [97] "sBfinal.sd"           "dep1"                 "dep1.sd"             
#> [100] "dep2"

Define the data list. The get_data function sets up some additional inputs that are required before the data list can be passed to MakeADFun.

Data <- list(last_yr = 2022, age_increase_M = 25, 
             length_m50 = 150, length_m95 = 180, 
             catch_UR_on = 0, catch_surf_case = 1, catch_LL1_case = 1, 
             scenarios_surf = scenarios_surface, scenarios_LL1 = scenarios_LL1,
             sel_min_age_f = c(2, 2, 2, 8, 6, 0), 
             sel_max_age_f = c(17, 9, 17, 22, 25, 7),
             sel_end_f = c(1, 0, 1, 1, 1, 0),
             sel_change_sd_fy = t(as.matrix(sel_change_sd[,-1])), 
             sel_smooth_sd_f = data_labrep1$sel.smooth.sd,
             removal_switch = c(0, 0, 0, 0, 0, 0), # 0=standard removals, 1=direct removals
             pop_switch = 1, 
             hsp_switch = 1, hsp_false_negative = 0.7467647, 
             gt_switch = 1, 
             cpue_switch = 1, cpue_a1 = 5, cpue_a2 = 17,
             aerial_switch = 4, aerial_tau = 0.3, 
             troll_switch = 0,
             af_switch = 3,
             lf_switch = 3, lf_minbin = c(1, 1, 1, 11),
             tag_switch = 1, tag_var_factor = 1.82
)

Data <- get_data(data_in = Data)

Input tables

Below are examples of some of the model inputs in tabular form.

Mean length (cm) by year, season, and age. Only a subset of years and ages 1-10 are shown:

kbl(bind_rows(head(length_mean), tail(length_mean))[,1:13]) %>%
  add_header_above(c(" " = 2, "Age" = 11))
Age
Year Season 0 1 2 3 4 5 6 7 8 9 10
1931 1 45 57.1 74.1 88.8 101.8 113.9 124.3 133.3 141.0 147.6 153.3
1932 1 45 57.1 74.1 88.8 101.8 113.9 124.3 133.3 141.0 147.6 153.3
1933 1 45 57.1 74.1 88.8 101.8 113.9 124.3 133.3 141.0 147.6 153.3
1934 1 45 57.1 74.1 88.8 101.8 113.9 124.3 133.3 141.0 147.6 153.3
1935 1 45 57.1 74.1 88.8 101.8 113.9 124.3 133.3 141.0 147.6 153.3
1936 1 45 57.1 74.1 88.8 101.8 113.9 124.3 133.3 141.0 147.6 153.3
2017 2 50 68.9 91.6 105.5 117.3 127.2 135.6 142.7 148.7 153.8 158.1
2018 2 50 68.9 91.6 105.5 117.3 127.2 135.6 142.7 148.7 153.8 158.1
2019 2 50 68.9 91.6 105.5 117.3 127.2 135.6 142.7 148.7 153.8 158.1
2020 2 50 68.9 91.6 105.5 117.3 127.2 135.6 142.7 148.7 153.8 158.1
2021 2 50 68.9 91.6 105.5 117.3 127.2 135.6 142.7 148.7 153.8 158.1
2022 2 50 68.9 91.6 105.5 117.3 127.2 135.6 142.7 148.7 153.8 158.1

The parent offspring pair (POP) data set:

kbl(tail(POPs, 15))
Cohort CaptureYear CaptureAge NPOPs Comps
2004 2021 30 0 6625
2005 2021 30 0 6780
2006 2021 30 0 6735
2007 2021 30 0 6575
2008 2021 30 0 4815
2009 2021 30 0 4380
2010 2021 30 0 4515
2011 2021 30 0 4495
2012 2021 30 1 4765
2013 2021 30 0 4270
2014 2021 30 0 4740
2015 2021 30 0 3780
2016 2021 30 0 7245
2017 2021 30 0 7560
2018 2021 30 0 6920

The unaccounted catch (tonnes) data set:

kbl(tail(catch_UA, 15))
Year LL1 LL2 LL3 LL4 Indonesia Australia
2008 72 0 0 0 0 0
2009 152 0 0 0 0 0
2010 271 0 0 0 0 0
2011 151 0 0 0 0 0
2012 275 0 0 0 0 0
2013 432 0 0 0 0 0
2014 121 0 0 0 0 0
2015 326 0 0 0 0 0
2016 756 0 0 0 0 0
2017 984 0 0 0 0 0
2018 1511 0 0 0 0 0
2019 1155 0 0 0 0 0
2020 1160 0 0 0 0 0
2021 1160 0 0 0 0 0
2022 1160 0 0 0 0 0

Input plots

plot_length_at_age(data = Data)
Length (cm) at age during each season.

Length (cm) at age during each season.

plot_weight_at_age(data = Data)
Weight (kg) at age for each fishery.

Weight (kg) at age for each fishery.

Model setup

Parameters

Define the parameter list:

Params <- list(par_log_B0 = data_par1$ln_B0, 
               par_log_psi = log(data_par1$psi),
               par_log_m0 = log(data_par1$m0), 
               par_log_m4 = log(data_par1$m4),
               par_log_m10 = log(data_par1$m10), 
               par_log_m30 = log(data_par1$m30),
               par_log_h = log(data_par1$steep), 
               par_log_sigma_r = log(data_labrep1$sigma.r), 
               par_log_cpue_q = data_par1$lnq,
               par_log_cpue_sigma = log(data_par1$sigma_cpue),
               par_log_cpue_omega = log(data_par1$cpue_omega),
               par_log_aerial_tau = log(data_par1$tau_aerial),
               par_log_aerial_sel = data_par1$ln_sel_aerial,
               par_log_troll_tau = log(data_par1$tau_troll),
               par_log_hsp_q = data_par1$lnqhsp, 
               par_log_tag_H_factor = log(data_par1$tag_H_factor),
               par_log_af_alpha = c(1, 1),
               par_log_lf_alpha = c(1, 1, 1, 1),
               par_rdev_y = data_par1$Reps,
               par_sels_init_i = data_par1$par_sels_init_i, 
               par_sels_change_i = data_par1$par_sels_change_i
)

Use TMB’s Map option to turn parameters on/off:

Map <- list()
Map[["par_log_psi"]] <- factor(NA)
Map[["par_log_m0"]] <- factor(NA)
Map[["par_log_m10"]] <- factor(NA)
Map[["par_log_h"]] <- factor(NA)
Map[["par_log_sigma_r"]] <- factor(NA)
Map[["par_log_cpue_sigma"]] <- factor(NA)
Map[["par_log_cpue_omega"]] <- factor(NA)
Map[["par_log_aerial_tau"]] <- factor(NA)
Map[["par_log_aerial_sel"]] <- factor(rep(NA, 2))
Map[["par_log_troll_tau"]] <- factor(NA)
Map[["par_log_hsp_q"]] <- factor(NA)
Map[["par_log_tag_H_factor"]] <- factor(NA)
Map[["par_log_af_alpha"]] <- factor(rep(NA, 2))
Map[["par_log_lf_alpha"]] <- factor(rep(NA, 4))

Specify the random effects:

Random <- c()

Normally, recruitment deviates (i.e., par_rdev_y) should be treated as random effects, but they are left as fixed effects for comparability.

Create the AD object using TMBs MakeADFun function:

obj <- MakeADFun(data = Data, parameters = Params, map = Map, random = Random, 
                 hessian = TRUE, inner.control = list(maxit = 50), DLL = "sbt")
#> Constructing atomic D_lgamma

Optimisation

List of parameters that are on:

unique(names(obj$par))
#> [1] "par_log_B0"        "par_log_m4"        "par_log_m30"      
#> [4] "par_log_cpue_q"    "par_rdev_y"        "par_sels_init_i"  
#> [7] "par_sels_change_i"

The objective function value:

obj$fn(obj$par)
#> [1] 27328.36

Load the default parameter bounds:

bnd <- get_bounds(obj = obj)

Optimise using the nlminb function:

opt <- nlminb(start = obj$par, objective = obj$fn, gradient = obj$gr, 
              lower = bnd$lower, upper = bnd$upper)
#> Warning in nlminb(start = obj$par, objective = obj$fn, gradient = obj$gr, :
#> NA/NaN function evaluation
#> Warning in nlminb(start = obj$par, objective = obj$fn, gradient = obj$gr, :
#> NA/NaN function evaluation

Calculate standard deviations of all model parameters, including non linear functions of random effects and parameters specified through the ADREPORT() macro from the user template:

Check that the Hessian is positive definite:

print(Report$pdHess)
#> [1] FALSE

Try the analytical Hessian instead:

# he <- obj$he()
# he_inv <- solve(he)
# he_ch <- chol(he)
# ev <- eigen(he)
# range(ev$values)

Plot outputs

plot_natural_mortality(data = Data, object = obj)

plot_initial_numbers(data = Data, object = obj)

plot_catch(data = Data, object = obj)
#> [1] "The maximum catch difference was: 1.78220943780616e-07"

plot_hrate(data = Data, object = obj, years = 1990:2010)

# yrs <- c(1952, 1953, 1954, seq(1955, 2020, 5), 2021, 2022)
yrs <- c(1952:2022)
plot_selectivity(data = Data, object = obj, years = yrs, fisheries = "LL1")

plot_selectivity(data = Data, object = obj, years = yrs, fisheries = "LL2")

plot_selectivity(data = Data, object = obj, years = yrs, fisheries = "LL3")

plot_selectivity(data = Data, object = obj, years = yrs, fisheries = "LL4")

plot_selectivity(data = Data, object = obj, years = yrs, fisheries = "Indonesian")

plot_selectivity(data = Data, object = obj, years = yrs, fisheries = "Australian")

plot_recruitment(data = Data, object = obj)

plot_biomass_spawning(data = Data, object = obj)

Model fits

plot_cpue(data = Data, object = obj)

plot_aerial_survey(data = Data, object = obj)