SWFSC Case Study Pacific Sardine

The setup

Code
# Names of required packages
packages <- c(
  "dplyr",
  "ggridges",
  "ggplot2",
  "gt",
  "here",
  "lubridate",
  "remotes",
  "reshape2",
  "shinystan",
  "tidybayes",
  "tidyr",
  "TMB"
)

# Install packages not yet installed
installed_packages <- packages %in% rownames(installed.packages())
if (any(installed_packages == FALSE)) {
  install.packages(packages[!installed_packages], repos = "http://cran.us.r-project.org")
}

# the Bayesian case study requires these extra packages
if (!"StanEstimators" %in% rownames(installed.packages())) {
  install.packages("StanEstimators", repos = c("https://andrjohns.r-universe.dev", "https://cloud.r-project.org"))
}

if (!"adnuts" %in% rownames(installed.packages())) {
  remotes::install_github("Cole-Monnahan-NOAA/adnuts")
}
StanEstim... (0.3.1 -> 6140a7560...) [GitHub]
parallelly   (NA    -> 1.46.1      ) [CRAN]
listenv      (NA    -> 0.10.0      ) [CRAN]
globals      (NA    -> 0.19.0      ) [CRAN]
snow         (NA    -> 0.4-4       ) [CRAN]
future       (NA    -> 1.69.0      ) [CRAN]
mvtnorm      (NA    -> 1.3-3       ) [CRAN]
snowfall     (NA    -> 1.84-6.3    ) [CRAN]
microbenc... (NA    -> 1.5.0       ) [CRAN]
R2admb       (NA    -> 0.7.16.3    ) [CRAN]
ellipse      (NA    -> 0.5.0       ) [CRAN]

── R CMD build ─────────────────────────────────────────────────────────────────
* checking for file ‘/tmp/Rtmp1PvMPy/remotes36f32529e6ff/andrjohns-StanEstimators-6140a75/DESCRIPTION’ ... OK
* preparing ‘StanEstimators’:
* checking DESCRIPTION meta-information ... OK
* cleaning src
* checking for LF line-endings in source and make files and shell scripts
* checking for empty or unneeded directories
* building ‘StanEstimators_0.3.1.tar.gz’
── R CMD build ─────────────────────────────────────────────────────────────────
* checking for file ‘/tmp/Rtmp1PvMPy/remotes36f323ed1623/Cole-Monnahan-NOAA-adnuts-33744b8/DESCRIPTION’ ... OK
* preparing ‘adnuts’:
* checking DESCRIPTION meta-information ... OK
* checking for LF line-endings in source and make files and shell scripts
* checking for empty or unneeded directories
Omitted ‘LazyData’ from DESCRIPTION
* building ‘adnuts_1.1.2.9000.tar.gz’
Code
# SS3 case studies require r4ss
if (!"r4ss" %in% rownames(installed.packages())) {
  remotes::install_github("r4ss/r4ss")
}
textshaping (NA -> 1.0.4 ) [CRAN]
systemfonts (NA -> 1.3.1 ) [CRAN]
sys         (NA -> 3.4.3 ) [CRAN]
askpass     (NA -> 1.2.1 ) [CRAN]
openssl     (NA -> 2.3.4 ) [CRAN]
svglite     (NA -> 2.2.2 ) [CRAN]
rstudioapi  (NA -> 0.18.0) [CRAN]
ini         (NA -> 0.3.1 ) [CRAN]
httr2       (NA -> 1.2.2 ) [CRAN]
gitcreds    (NA -> 0.1.2 ) [CRAN]
viridis     (NA -> 0.6.5 ) [CRAN]
kableExtra  (NA -> 1.4.0 ) [CRAN]
gh          (NA -> 1.5.0 ) [CRAN]
furrr       (NA -> 0.3.1 ) [CRAN]
forcats     (NA -> 1.0.1 ) [CRAN]
corpcor     (NA -> 1.6.10) [CRAN]
── R CMD build ─────────────────────────────────────────────────────────────────
* checking for file ‘/tmp/Rtmp1PvMPy/remotes36f33c93ba85/r4ss-r4ss-d7f7f2f/DESCRIPTION’ ... OK
* preparing ‘r4ss’:
* checking DESCRIPTION meta-information ... OK
* checking for LF line-endings in source and make files and shell scripts
* checking for empty or unneeded directories
* building ‘r4ss_1.52.1.tar.gz’
Code
# SS3 case studies require r4ss
if (!"stockplotr" %in% rownames(installed.packages())) {
  remotes::install_github("nmfs-ost/stockplotr")
}
SQUAREM      (NA -> 2021.1    ) [CRAN]
progressr    (NA -> 0.18.0    ) [CRAN]
future.apply (NA -> 1.20.1    ) [CRAN]
shape        (NA -> 1.4.6.1   ) [CRAN]
prettyunits  (NA -> 1.2.0     ) [CRAN]
bit          (NA -> 4.6.0     ) [CRAN]
progress     (NA -> 1.2.3     ) [CRAN]
bit64        (NA -> 4.6.0-1   ) [CRAN]
tzdb         (NA -> 0.5.0     ) [CRAN]
vroom        (NA -> 1.7.0     ) [CRAN]
hms          (NA -> 1.1.4     ) [CRAN]
crayon       (NA -> 1.5.3     ) [CRAN]
clipr        (NA -> 0.8.0     ) [CRAN]
readr        (NA -> 2.1.6     ) [CRAN]
zip          (NA -> 2.3.3     ) [CRAN]
uuid         (NA -> 1.2-2     ) [CRAN]
ragg         (NA -> 1.5.0     ) [CRAN]
fontLiber... (NA -> 0.1.0     ) [CRAN]
fontBitst... (NA -> 0.1.1     ) [CRAN]
fontquiver   (NA -> 0.2.1     ) [CRAN]
lava         (NA -> 1.8.2     ) [CRAN]
diagram      (NA -> 1.6.5     ) [CRAN]
data.table   (NA -> 1.18.2.1  ) [CRAN]
UpSetR       (NA -> 1.4.0     ) [CRAN]
visdat       (NA -> 0.6.0     ) [CRAN]
norm         (NA -> 1.0-11.1  ) [CRAN]
officer      (NA -> 0.7.3     ) [CRAN]
gdtools      (NA -> 0.5.0     ) [CRAN]
quarto       (NA -> 1.5.1     ) [CRAN]
prodlim      (NA -> 2025.04.28) [CRAN]
naniar       (NA -> 1.1.0     ) [CRAN]
httr         (NA -> 1.4.7     ) [CRAN]
flextable    (NA -> 0.9.10    ) [CRAN]
── R CMD build ─────────────────────────────────────────────────────────────────
* checking for file ‘/tmp/Rtmp1PvMPy/remotes36f3595e6381/nmfs-ost-stockplotr-1bbf87a/DESCRIPTION’ ... OK
* preparing ‘stockplotr’:
* checking DESCRIPTION meta-information ... OK
* checking for LF line-endings in source and make files and shell scripts
* checking for empty or unneeded directories
* building ‘stockplotr_0.7.0.tar.gz’
Code
# Install FIMS: main branch version if on main, dev version on any other branch
remotes::install_github("NOAA-FIMS/FIMS", ref = "dev-hake")

── R CMD build ─────────────────────────────────────────────────────────────────
* checking for file ‘/tmp/Rtmp1PvMPy/remotes36f35b2fc7e4/NOAA-FIMS-FIMS-14916c8/DESCRIPTION’ ... OK
* preparing ‘FIMS’:
* checking DESCRIPTION meta-information ... OK
* cleaning src
* checking for LF line-endings in source and make files and shell scripts
* checking for empty or unneeded directories
* building ‘FIMS_0.8.1.tar.gz’
Code
# Load packages
invisible(lapply(packages, library, character.only = TRUE))

library(FIMS)
library(adnuts)
R_version <- version$version.string
TMB_version <- packageDescription("TMB")$Version
FIMS_commit <- substr(packageDescription("FIMS")$GithubSHA1, 1, 7)
ggplot2::theme_set(ggplot2::theme_bw())

data_directory <- "data_files"
function_directory <- "R"
purrr::walk(
    dir(function_directory, full.names = TRUE),
  source
)
Code
common_name <- "Northern subpopulation of Pacific sardine"
  • R version: R version 4.5.2 (2025-10-31)
  • TMB version: 1.9.19
  • FIMS commit: 14916c8
  • Stock name: Northern subpopulation of Pacific sardine
  • Region: SWFSC
  • Analyst: Peter Kuriyama

Simplifications to the original assessment

The model presented in this case study was changed substantially from the operational version and should not be considered reflective of the Northern subpopulation of Pacific sardine stock. These results are intended to be a demonstration and nothing more.

To get the operational model to more closely match a FIMS model the following changes were made:

  • Convert two-semester time step to annual
  • Sum the catch values for MexCal S1 and S2
  • Sum the age comps for MexCal S1 and S2
  • Drop PNW fishing fleet data (catch and age comp data)
  • Drop spring AT survey index values
  • Use expected summary biomass values as AT survey CPUE
  • Fix Q=1 for AT survey, rather than have fixed, year-specific values
  • No SR_regime parameter estimated (population assumed to start a equilibrium)
  • Fixed M at 0.8
  • Assume time-invariant logistic selectivity AT survey fleets (time-varying age-0 in benchmark assessment)
  • Assume time-invariant logistic selectivity MexCal fleet (time-varying in benchmark assessment)
  • Lorenzen age M -> one M value
  • No ageing error in FIMS?
  • Assume time-invariant weight at age for the fishery and survey

Setting up the data

Code
FIMS::clear()

#--------------------------------------------------------
#Logistic function for later use
logistic <- function(x, slope, inflection_point){
  out <- 1 / (1 + exp(-1 * slope * (x - inflection_point)))
  out <- data.frame(x = x, value = out)
  return(out)
}

#--------------------------------------------------------
#Manually enter data

# setwd("C://Users//peter.kuriyama//SynologyDrive/Research//noaa//FIMS")

#-----Catch
catch <- data.frame(year = 2005:2023, catch = c(29188.50, 53107.00, 69929.40,
                                                56317.80, 33546.40, 17466.40, 39383.10, 2585.38, 5705.77, 2558.63, 7.18, 428.26,
                                                347.11, 514.20, 619.04, 653.15, 285.89, 508.02, 152.31))

# ggplot(catch, aes(x = year, y = catch)) + geom_point() +
#   geom_line() + scale_y_continuous(label = comma)


fimscatch <- tibble::tibble(type = "landings", name = "fleet1",
                    age = NA, timing = catch$year, value = catch$catch,
                    unit = "mt", uncertainty = 0.05)

#-----CPUE
cpue <- data.frame(year = 2005:2023, obs = c(649619.0, 899635.0, 956354.0, 863281.0, 652029.0,
                                             504970.0, 395783.0, 293980.0, 182417.0, 89260.1,
                                             46403.0, 40704.0, 44592.1, 48789.1, 53551.8,
                                             59765.8, 68451.7, 71612.5, 68957.9))


# ggplot(cpue, aes(x = year, y = obs)) + geom_point() + geom_line() +
#   scale_y_continuous(label = comma)

fimsindex <- tibble::tibble(type = "index", name = "survey1",
                    age = NA, timing = cpue$year,
                    value = cpue$obs, unit = "mt", uncertainty = .3)
fimsindex$unit <- ""

#-----Age compositions
acomps <- utils::read.csv(file.path(data_directory, "sardine_acomps.csv")) |>
  dplyr::mutate(value_prop = value / Nsamp) # convert age-comp data to proportions, to match fims-demo.Rmd

fimsage <- tibble::tibble(type = "age_comp", name = acomps$name,
                  age = acomps$age, timing = acomps$Yr,
                  value = acomps$value_prop, unit = "proportion", uncertainty = acomps$Nsamp)
#fimsage$uncertainty <- 50 Leave as empirical values

#-----Weight-at-age
  # Q: What are the differences in how SS3 and FIMS process wtatage inputs/units?
  # A: SS3 assumes WAA is expressed in kg, and then converts to mt for biomass calculations
      # SS3 also expresses natage in terms of 1000s/fish
    # FIMS assumes WAA is expressed in mt, but expresses natage in fish
    # WAA input values are supplied to SS3 in kg and FIMS in mt
    # Model now correctly processes inputs and provides comparable NAA, WAA, and biomass outputs to SS3
wtatage <- r4ss::SS_readwtatage(file.path(data_directory, "sardine_wtatage.ss_new")) |>
  dplyr::filter(fleet %in% c(1, 2)) |>
  dplyr::mutate(fleet = ifelse(fleet == 1, "fleet1", "survey1")) |>
  tidyr::pivot_longer(cols = `0`:`10`, names_to = "age", values_to = "value") |>
  dplyr::filter(year != 2024, !(age %in% c("9", "10"))) |> # Trim ages 9 and 10 to match age-comps
  dplyr::mutate(value = value / 1000) # WAA converted from kg to mt
fimswaa <- tibble::tibble(
  type = "weight-at-age",
  name = wtatage$fleet,
  age = wtatage$age,
  timing = wtatage$year,
  value = wtatage$value,
  unit = "mt",
  uncertainty = NA
)

#Combine everything, format
data_4_model <- rbind(fimscatch, fimsindex, fimsage, fimswaa)
data_4_model$age <- as.integer(data_4_model$age)
data_4_model$value <- as.numeric(data_4_model$value)

# Convert to FIMSFrame
data_4_model <- FIMS::FIMSFrame(data_4_model)

n_years <- FIMS::get_n_years(data_4_model)
years <- FIMS::get_start_year(data_4_model):FIMS::get_end_year(data_4_model)
n_ages <- FIMS::get_n_ages(data_4_model)
ages <- FIMS::get_ages(data_4_model)
rzero <- 17 #14.2 is log(R0) in sardine simplified model
M_value <- 0.8 #.8 worked pretty well
init_naa <- exp(rzero) * exp(-(ages - 1) * M_value)
init_naa[n_ages] <- init_naa[n_ages] / M_value # sum of infinite series

# Create default recruitment, growth, and maturity parameters
parameters <- FIMS::create_default_configurations(data = data_4_model) |>
  FIMS::create_default_parameters(data = data_4_model) |>
  tidyr::unnest(data) |>
  dplyr::rows_update(
    tibble::tibble(
      module_name = "Selectivity",
      label = rep(c("inflection_point", "slope"), 2),
      value = c(1, 5, 1.2, 2),
      estimation_type = "constant",
      fleet_name = rep(c("fleet1", "survey1"), each = 2)
    ),
    by = c("module_name", "label", "fleet_name")
  ) |>
  dplyr::rows_update(
    tibble::tibble(
      module_name = "Recruitment",
      label = "logit_steep",
      value = FIMS::logit(0.2, 1.0, 0.6)
    ),
    by = c("module_name", "label")
  ) |>
  dplyr::rows_update(
    tibble::tibble(
      module_name = "Recruitment",
      label = c("log_rzero", "log_sd"),
      value = c(rzero, log(1.2)),
    ),
    by = c("module_name", "label")
  ) |>
  dplyr::rows_update(
    tibble::tibble(
      module_name = "Recruitment",
      label = "log_devs",
      value = log(1),
    ),
    by = c("module_name", "label")
  ) |>
  dplyr::rows_update(
    tibble::tibble(
      module_name = "Maturity",
      label = c("inflection_point", "slope"),
      value = c(1.2, 1.5) # 1.5 is arbitrary guess
    ),
    by = c("module_name", "label")
  ) |>
  dplyr::rows_update(
    tibble::tibble(
      module_name = "Fleet",
      fleet_name = "survey1",
      label = "log_q",
      value = 0,
      estimation_type = "constant"
    ),
    by = c("module_name", "fleet_name", "label")
  ) |>
  dplyr::rows_update(
    tibble::tibble(
      module_name = "Fleet",
      fleet_name = "fleet1",
      label = "log_Fmort",
      value = log(0.2)
    ),
    by = c("module_name", "fleet_name", "label")
  ) |>
  dplyr::rows_update(
    tibble::tibble(
      module_name = "Population",
      label = c("log_M"),
      value = log(M_value)
    ),
    by = c("module_name", "label")
  ) |>
  dplyr::rows_update(
    tibble::tibble(
      module_name = "Population",
      label = c("log_init_naa"),
      age = ages,
      value = log(init_naa),
      estimation_type = "fixed_effects"
    ),
    by = c("module_name", "age", "label")
  ) |>
  dplyr::rows_update(
    tibble::tibble(
      module_name = "Data",
      fleet_name = "fleet1",
      label = "log_sd",
      value = log(sqrt(log(0.01^2 + 1))),
      estimation_type = "fixed_effects"
    ),
    by = c("module_name", "fleet_name", "label")
  ) |>
  dplyr::rows_update(
    tibble::tibble(
      module_name = "Data",
      fleet_name = "survey1",
      label = "log_sd",
      value = log(sqrt(log(0.1^2 + 1))),
      estimation_type = "fixed_effects"
    ),
    by = c("module_name", "fleet_name", "label")
  )

Run FIMS model

Code
# Fit the model
fit <- parameters |>
  FIMS::initialize_fims(data = data_4_model) |>
  FIMS::fit_fims(optimize = TRUE)
Warning: ✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✖ Multiple fleets found in weight-at-age data.
ℹ `m_weight_at_age()` will average values across fleets.
✔ Starting optimization ...
ℹ Restarting optimizer 3 times to improve gradient.
ℹ Maximum gradient went from 309368361652.616 to 309368361652.616 after 3
  steps.
✔ Finished optimization
✔ Finished sdreport
ℹ FIMS model version: 0.8.1
ℹ Total run time was 2.09433 seconds
ℹ Number of parameters: fixed_effects=85, random_effects=0, and total=85
ℹ Maximum gradient= 309368361652.616
ℹ Negative log likelihood (NLL):
• Marginal NLL= 634.40737
• Total NLL= 634.40737
ℹ Terminal SB= 0
Code
output <- FIMS::get_estimates(fit) |>
  dplyr::mutate(
    uncertainty_label = "se",
    year = year_i,
    estimate = estimated
  )

# Get information about the model and print a few characters to the screen
log <- FIMS::get_log_module("information")

Add your comparison figures

Code
load(file.path(data_directory, "sardine_simplified_res.Rdata"))

#------------------------------------------------------------------------
#------SSB
# Q: Is 'ssb' from  get_report(fit) calculated assuming 2 genders and a 50:50 ratio?
  # If so, it might explain the discrepancy between ss3 (1 gender model) and fims (2 genders)
ssbs <- ssres$timeseries |>
  dplyr::select(Yr, ss3 = SpawnBio) |>
  dplyr::mutate(fims = c(0, 0, FIMS::get_report(fit)[["spawning_biomass"]][[1]]))

ssbs |>
  dplyr::filter(Yr >= 2005, Yr < 2024) |>
  tidyr::pivot_longer(names_to = "variable", cols = dplyr::matches("ss3|fims")) |>
  ggplot2::ggplot(ggplot2::aes(x = Yr, y = value, group = variable, color = variable)) +
  ggplot2::geom_point() +
  ggplot2::geom_line() +
  ggplot2::ylab("Spawning biomass (mt)") +
  ggplot2::theme_bw() +
  ggplot2::xlab("year") +
  ggplot2::theme(legend.position.inside = c(.9, .9))

Code
ggplot2::ggsave(
  "figures/SWFSC-sardine-sb.png",
  width = 7.35,
  height = 4.8
)

#------------------------------------------------------------------------
#------Index fits
index <- ssres$cpue |>
  dplyr::select(Yr, Obs, Exp)
names(index) <- c("year", "obs", "ss3")
index$fims <- FIMS::get_report(fit)[["index_expected"]][[2]]
index |>
  tidyr::pivot_longer(names_to = "variable", cols = dplyr::matches("ss3|fims")) |>
  ggplot2::ggplot(ggplot2::aes(x = year)) +
  ggplot2::geom_point(ggplot2::aes(y = obs)) +
  ggplot2::geom_line(ggplot2::aes(y = value, color = variable, group = variable)) +
  ggplot2::theme_bw() +
  ggplot2::theme(legend.position.inside = c(.9, .9)) +
  ggplot2::xlab("Year") +
  ggplot2::ylab("Survey biomass value (mt)")

Code
ggplot2::ggsave("figures/SWFSC-sardine-surveyfit.png", width = 6.8, height = 5.25)

#------------------------------------------------------------------------
#-----Calculate age-1+ biomass
#Multiply numbers at age by weight at age and sum
naa <- get_report(fit)[["numbers_at_age"]][[1]]

naa1 <- tidyr::crossing(c(years, 2024), ages) |>
  dplyr::mutate(naa = naa) |>
  as.data.frame()
names(naa1) <- c("year", "age", "value")
naa1$cohort <- naa1$year - naa1$age

#Format Weight at age (identical WAA for each year and fleet)
waa <- data.frame(
  age = wtatage$age[(wtatage$year == 2005 & wtatage$fleet == "fleet1")],
  waa = wtatage$value[(wtatage$year == 2005 & wtatage$fleet == "fleet1")]
) |>
  dplyr::mutate(age = as.integer(age))

naa1 <- naa1 |>
  dplyr::left_join(waa, by = "age")


naa1 <- naa1 |>
  dplyr::mutate(weight = value * waa)
age1plus <- naa1 |>
  dplyr::filter(age != 0) |>
  dplyr::group_by(year) |>
  dplyr::summarize(summbio = sum(weight))

bio1 <- ssres$timeseries |>
  dplyr::filter(Seas == 1) |>
  dplyr::select(Yr, Bio_smry) |>
  dplyr::mutate(year = Yr, ss3bio = Bio_smry) |>
  dplyr::select(-Yr, -Bio_smry)


age1plus <- age1plus |>
  dplyr::left_join(bio1, by = "year")

names(age1plus) <- c("year", "fims", "ss3")

#Full time series of age-1+ biomass
age1plus |>
  reshape2::melt(id.var = "year") |>
  ggplot2::ggplot(ggplot2::aes(x = year, y = value, group = variable, color = variable)) +
  ggplot2::geom_point() +
  ggplot2::geom_line() +
  ggplot2::ylab("Age-1+ biomass (mt)") +
  ggplot2::theme_bw() +
  ggplot2::theme(legend.position.inside = c(.9, .9))

Code
ggplot2::ggsave("figures/SWFSC-sardine-age1plusbio.png", width = 6.8, height = 5.25)

#Zoomed in time series of age-1+
age1plus |>
  reshape2::melt(id.var = "year") |>
  dplyr::filter(year >= 2010) |>
  ggplot2::ggplot(ggplot2::aes(x = year, y = value, group = variable, color = variable)) +
  ggplot2::geom_point() +
  ggplot2::geom_line() +
  ggplot2::theme_bw() +
  ggplot2::ylab("Age-1+ biomass (mt)") +
  ggplot2::theme(legend.position.inside = c(.9, .9))

Code
ggplot2::ggsave(
  "figures/SWFSC-sardine-age1plusbio_zoomedin.png",
  width = 6.8,
  height = 5.25
)


#------------------------------------------------------------------------
#------Recruitment
# Divide FIMS recruits by 1000 to match reporting in SS3
recs <- ssres$timeseries |>
  dplyr::select(Yr, Recruit_0) |>
  dplyr::mutate(fims = c(0, 0, get_report(fit)[["expected_recruitment"]][[1]]) / 1000)
names(recs)[2] <- "ss3"

recs |>
  dplyr::filter(Yr >= 2005, Yr < 2024) |>
  reshape2::melt(id.var = "Yr") |>
  ggplot2::ggplot(ggplot2::aes(x = Yr, y = value, group = variable, color = variable)) +
  ggplot2::theme_bw() +
  ggplot2::geom_point() +
  ggplot2::geom_line() +
  ggplot2::theme(legend.position.inside = c(.9, .9)) +
  ggplot2::ylab("Recruits (x1000)")

Code
ggplot2::ggsave("figures/SWFSC-sardine-recruitment.png", width = 6.8, height = 5.25)


#------------------------------------------------------------------------
#------Fixed selectivities
#Are fixed but plot for comparison's sake
##Fishery

sel_fishery <- logistic(ages,
                        slope = dplyr::filter(FIMS::get_estimates(fit), module_name == "Selectivity", label == "slope", module_id == 1) |>
                          dplyr::pull(estimated),
                        inflection_point = dplyr::filter(FIMS::get_estimates(fit), module_name == "Selectivity", label == "inflection_point", module_id == 1) |>
                          dplyr::pull(estimated))

names(sel_fishery) <- c("age", "fims")

sel_fishery$ss3 <- ssres$ageselex |>
  dplyr::filter(Yr == 2005, Factor == "Asel", Fleet == 1) |>
  dplyr::select(as.character(0:8)) |>
  t()
sel_fishery <- sel_fishery |>
  reshape2::melt(id.var = "age")
Warning: attributes are not identical across measure variables; they will be
dropped
Code
ggplot2::ggplot(
  sel_fishery,
  ggplot2::aes(x = age, y = value, group = variable, color = variable)
) +
  ggplot2::geom_point() +
  ggplot2::geom_line()

Code
#-----------Survey
sel_survey <- logistic(ages,
                       slope = dplyr::filter(FIMS::get_estimates(fit), module_name == "Selectivity", label == "slope", module_id == 2) |>
                          dplyr::pull(estimated),
                       inflection_point = dplyr::filter(FIMS::get_estimates(fit), module_name == "Selectivity", label == "inflection_point", module_id == 2) |>
                          dplyr::pull(estimated))

names(sel_survey) <- c("age", 'fims')

sel_survey$ss3 <- ssres$ageselex |>
  dplyr::filter(Yr == 2005, Factor == "Asel", Fleet == 2) |>
  dplyr::select(as.character(0:8)) |>
  t()
sel_survey <- sel_survey |>
  reshape2::melt(id.var = "age")
Warning: attributes are not identical across measure variables; they will be
dropped
Code
ggplot2::ggplot(
  sel_survey,
  ggplot2::aes(x = age, y = value, group = variable, color = variable)
) +
  ggplot2::geom_point() +
  ggplot2::geom_line()

Add comparison tables

What was your experience using FIMS? What could we do to improve usability?

Tools to check: * data were inputted correctly (dimension checks) * starting values and settings are reasonable * Perhaps have a model template file that will work as is, then users can modify as necessary * I had issues installing FIMS with install_github that seemed to be related to R settings

Model: * streamline configuration of fleets and maybe make it easier to add additional fleets (perhaps clone existing ones then change specific settings) * Think about ways to modify single model settings based on say different starting values

Output: * include all parameter values and names (fixed and estimated, also show starting values) * Code to generate a default set of figures and tables for use in assessment documents/presentations

What features are most important to add based on this case study?

  • The ability to start the model at non-equilibrium conditions (In SS3 there is a SR_regime parameter and Initial F to match equilbirium age comps to first year of data age comps) *Does the model year start in the first year of the data input?
Code
FIMS::clear()