Tanner Crab GMACS Input Notes

Published

June 9, 2026

Purpose

This document is the Tanner crab parallel to bbrkc_rtmb_port_steps.qmd. The current Tanner work is at the input-orientation stage: the repository now has a Tanner-specific reader for the available GMACS files in examples/tanners. This page also checks for Tanner ADMB report outputs and reports which parity targets are available for future RTMB objective and deterministic checks.

Files

The Tanner reader uses:

File Role
examples/tanners/gmacs_26_22_03d5_aEffExp1.Run1.dat GMACS main run file.
examples/tanners/TannerCrab_Data202509.EffExp1.dat Tanner data file.
examples/tanners/TannerCrab_26_22_03d5_a.Run1.ctl Tanner control file.
examples/tanners/TannerCrab_26_22_03d5_a.prj Tanner projection file.
examples/tanners/gmacs_26_22_03d5_aEffExp1.par Fitted parameter table.
examples/tanners/Gmacsall.out or Tanner equivalent Optional ADMB likelihood and fit-summary output.
examples/tanners/gmacs.rep or Tanner equivalent Optional ADMB report output for deterministic blocks.

Optional ADMB parity targets are discovered if they are added to the same folder. Canonical names are Gmacsall.out and gmacs.rep; the reader also checks common Tanner-specific variants such as gmacs_26_22_03d5_aEffExp1.Gmacsall.out and gmacs_26_22_03d5_aEffExp1.rep.

Current Reader

The Tanner files use a different GMACS text layout from the BBRKC example. R/read_gmacs_tanner.R therefore reads the Tanner file set directly instead of forcing these inputs through BBRKC-specific parser assumptions.

Code
source("R/read_gmacs_bbrkc.R")
source("R/read_gmacs_tanner.R")

tanner <- read_tanner_inputs("examples/tanners")
validate_tanner_inputs(tanner)
has_tanner_index_fit <- !is.null(tanner$admb_reference$index_fit)
has_tanner_size_fit <- !is.null(tanner$admb_reference$size_fit)
Code
data.frame(
  report = names(tanner$admb_reference$files),
  path = unlist(tanner$admb_reference$files),
  available = file.exists(unlist(tanner$admb_reference$files)),
  row.names = NULL
)
  report path available
1    all <NA>     FALSE
2 report <NA>     FALSE

Candidate ADMB report names checked by the reader:

Code
candidates <- tanner_admb_report_candidates("examples/tanners")
data.frame(
  report_type = rep(names(candidates), lengths(candidates)),
  candidate = basename(unlist(candidates)),
  present = file.exists(unlist(candidates)),
  row.names = NULL
)
   report_type                              candidate present
1          all                           Gmacsall.out   FALSE
2          all                           gmacsall.out   FALSE
3          all                           GmacsAll.out   FALSE
4          all                TannerCrab_Gmacsall.out   FALSE
5          all gmacs_26_22_03d5_aEffExp1.Gmacsall.out   FALSE
6          all      gmacs_26_22_03d5_aEffExp1.all.out   FALSE
7       report                              gmacs.rep   FALSE
8       report                              Gmacs.rep   FALSE
9       report                         TannerCrab.rep   FALSE
10      report            TannerCrab_26_22_03d5_a.rep   FALSE
11      report          gmacs_26_22_03d5_aEffExp1.rep   FALSE

Input Summary

Code
dims <- tanner$data$dimensions
data.frame(
  quantity = names(dims),
  value = as.integer(dims),
  row.names = NULL
)
  quantity value
1      syr  1948
2      nyr  2024
3  nseason     6
4   nfleet     6
5     nsex     2
6   nshell     2
7  nmature     2
8   nclass    32
Code
data.frame(
  component = c("catch", "survey", "size composition", "growth", "maturity"),
  dataframes = c(
    tanner$data$catch$n,
    tanner$data$survey$n,
    tanner$data$size_comp$n,
    NA_integer_,
    NA_integer_
  ),
  rows = c(
    sum(tanner$data$catch$rows),
    sum(tanner$data$survey$rows),
    sum(tanner$data$size_comp$rows),
    tanner$data$growth$n,
    tanner$data$maturity$n
  )
)
         component dataframes rows
1            catch          6  282
2           survey          6  168
3 size composition         15  471
4           growth         NA  214
5         maturity         NA  136

Parameter Summary

Code
data.frame(
  parameter_rows = nrow(tanner$parameters),
  active_estimated_parameters = sum(tanner$parameters$active),
  min_estimated_index = min(tanner$parameters$estimated_index, na.rm = TRUE),
  max_estimated_index = max(tanner$parameters$estimated_index, na.rm = TRUE)
)
  parameter_rows active_estimated_parameters min_estimated_index
1            469                         173                   1
  max_estimated_index
1                 157
Code
head(tanner$parameters[tanner$parameters$active, c(
  "parameter_index", "estimated_index", "value", "label"
)], 12)
   parameter_index estimated_index       value
3                3               1   6.3256587
11              11               2   5.4245970
13              13              NA  32.3984669
15              15              NA 163.9410634
18              18              NA  33.3714519
20              20              NA 115.3816014
22              22               3   0.3174953
23              23               4   0.7395193
24              24               5   0.2510995
25              25               6   0.3191779
26              26               7   0.6503671
27               4               4   4.8327442
                                     label
3                              Log(Rbar) 1
11      Log(Rbar)_block_group_10_block_1 2
13                              Alpha_male
15                               Beta_male
18                            Alpha_female
20                             Beta_female
22                    M_base_male_mature 3
23   M_male_mature_block_group_1_block_1 4
24                  M_base_male_immature 5
25                  M_base_female_mature 6
26 M_female_mature_block_group_1_block_1 7
27                                        

ADMB Parity Report Check

The Tanner reader validates:

  • model dimensions and size breaks;
  • fishery and survey names;
  • catch, survey, and size-composition dataframe counts;
  • growth and maturity observation counts;
  • fitted parameter table extraction from the .par file.

The same style of ADMB target extraction used for BBRKC is wired for Tanner when report outputs are present:

  • read_admb_likelihood_reference() for nloglike, nlogPenalty, and priorDensity;
  • read_admb_catch_fit_summary(), read_admb_index_fit_summary(), and read_admb_size_fit_summary() for observed/predicted fit summaries;
  • read_admb_selectivity_block() for slx_capture, slx_retaind, and slx_discard from the report file.
Code
admb_ref <- tanner$admb_reference
data.frame(
  target = c(
    "nloglike",
    "nlogPenalty",
    "priorDensity",
    "catch fit summary",
    "index fit summary",
    "size fit summary",
    "slx_capture",
    "slx_retaind",
    "slx_discard"
  ),
  source_file = c(
    rep(admb_ref$files$all, 6),
    rep(admb_ref$files$report, 3)
  ),
  parsed_rows = c(
    if (!is.null(admb_ref$likelihood)) length(admb_ref$likelihood$nloglike) else 0L,
    if (!is.null(admb_ref$likelihood)) length(admb_ref$likelihood$nlog_penalty) else 0L,
    if (!is.null(admb_ref$likelihood)) length(admb_ref$likelihood$prior_density) else 0L,
    if (!is.null(admb_ref$catch_fit)) nrow(admb_ref$catch_fit) else 0L,
    if (!is.null(admb_ref$index_fit)) nrow(admb_ref$index_fit) else 0L,
    if (!is.null(admb_ref$size_fit)) nrow(admb_ref$size_fit) else 0L,
    if (!is.null(admb_ref$selectivity)) nrow(admb_ref$selectivity$capture) else 0L,
    if (!is.null(admb_ref$selectivity)) nrow(admb_ref$selectivity$retained) else 0L,
    if (!is.null(admb_ref$selectivity)) nrow(admb_ref$selectivity$discard) else 0L
  ),
  available = c(
    rep(!is.null(admb_ref$likelihood), 3),
    !is.null(admb_ref$catch_fit),
    !is.null(admb_ref$index_fit),
    !is.null(admb_ref$size_fit),
    rep(!is.null(admb_ref$selectivity), 3)
  ),
  row.names = NULL
)
             target source_file parsed_rows available
1          nloglike        <NA>           0     FALSE
2       nlogPenalty        <NA>           0     FALSE
3      priorDensity        <NA>           0     FALSE
4 catch fit summary        <NA>           0     FALSE
5 index fit summary        <NA>           0     FALSE
6  size fit summary        <NA>           0     FALSE
7       slx_capture        <NA>           0     FALSE
8       slx_retaind        <NA>           0     FALSE
9       slx_discard        <NA>           0     FALSE

If the parsed row counts are zero, add Gmacsall.out and gmacs.rep, or one of the Tanner-equivalent names listed above, to examples/tanners. Once those files are present, this page will expose the ADMB likelihood, fit, and selectivity targets needed to build Tanner parity checks.

Code
if (!has_tanner_index_fit || !has_tanner_size_fit) {
  data.frame(
    diagnostic = c("index observed-vs-predicted", "size observed-vs-predicted"),
    status = c(
      if (has_tanner_index_fit) "available" else "missing ADMB Index_fit_summary",
      if (has_tanner_size_fit) "available" else "missing ADMB Size_fit_summary"
    ),
    required_file = "Tanner Gmacsall.out or equivalent",
    row.names = NULL
  )
}
                   diagnostic                         status
1 index observed-vs-predicted missing ADMB Index_fit_summary
2  size observed-vs-predicted  missing ADMB Size_fit_summary
                      required_file
1 Tanner Gmacsall.out or equivalent
2 Tanner Gmacsall.out or equivalent

Index Diagnostics

When Tanner ADMB fit summaries are present, the figure below shows observed and predicted index fits from Gmacsall.out. In the current repository, Tanner ADMB report output is not present, so this figure is observed-only.

Code
if (!is.null(tanner$admb_reference$index_fit)) {
  index_fit <- tanner$admb_reference$index_fit
  series_ids <- sort(unique(index_fit$series))
  panel_rows <- ceiling(length(series_ids) / 2)
  panel_slots <- panel_rows * 2
  old_par <- par(mar = c(3, 3, 2, 1), oma = c(0, 0, 0, 0))
  layout(
    rbind(matrix(seq_len(panel_slots), ncol = 2, byrow = TRUE), rep(panel_slots + 1, 2)),
    heights = c(rep(1, panel_rows), 0.16)
  )
  for (series in series_ids) {
    x <- index_fit[index_fit$series == series, ]
    ylim <- range(c(x$obs, x$predicted), finite = TRUE)
    plot(x$year, x$obs, pch = 16, col = "#1b6da8", ylim = ylim,
         xlab = "", ylab = "", main = paste("Series", series), bty = "l")
    line_keys <- unique(x[c("fleet", "season", "sex", "maturity")])
    for (line_i in seq_len(nrow(line_keys))) {
      keep <- rep(TRUE, nrow(x))
      for (key_name in names(line_keys)) {
        keep <- keep & x[[key_name]] == line_keys[[key_name]][line_i]
      }
      line_x <- x[keep, ]
      line_x <- line_x[order(line_x$year), ]
      lines(line_x$year, line_x$predicted, lwd = 2, col = "#c43c39")
      points(line_x$year, line_x$predicted, pch = 1, col = "#c43c39")
    }
    grid(col = "grey88")
  }
  if (length(series_ids) < panel_slots) {
    for (unused in seq_len(panel_slots - length(series_ids))) {
      plot.new()
    }
  }
  par(mar = c(0, 0, 0, 0))
  plot.new()
  legend("center", horiz = TRUE, bty = "n",
         pch = c(16, NA), lty = c(NA, 1), lwd = c(NA, 2),
         col = c("#1b6da8", "#c43c39"), legend = c("Observed", "Predicted"))
  layout(1)
  par(old_par)
} else {
  index_obs <- tanner$data$survey_observations
  series_ids <- sort(unique(index_obs$series))
  old_par <- par(mfrow = c(ceiling(length(series_ids) / 2), 2), mar = c(3, 3, 2, 1), oma = c(0, 0, 2, 0))
  for (series in series_ids) {
    x <- index_obs[index_obs$series == series, ]
    plot(x$year, x$obs, type = "b", pch = 16, col = "#1b6da8",
         xlab = "", ylab = "", main = paste(x$fleet[1], x$sex[1], x$maturity[1]),
         bty = "l")
    grid(col = "grey88")
  }
  mtext("Observed only: Tanner ADMB fit summaries are not present", outer = TRUE, line = 0.4)
  par(old_par)
}
Figure 1: Tanner crab index diagnostics; ADMB predictions are shown when report output is available.

Size-Composition Diagnostics

When Tanner ADMB size-fit summaries are present, the panels compare observed and predicted compositions from Gmacsall.out. Otherwise they show observed input compositions for recent rows from the first four size-composition dataframes. In the current repository, Tanner ADMB report output is not present, so this figure is observed-only.

Code
if (!is.null(tanner$admb_reference$size_fit)) {
  size_fit <- tanner$admb_reference$size_fit
  size_keys <- c("modified_series", "year", "fleet", "season", "sex", "type", "shell", "maturity")
  size_key_rows <- unique(size_fit[size_fit$vector == "obs", size_keys])
  size_key_rows <- size_key_rows[order(size_key_rows$modified_series, size_key_rows$year), ]
  series_ids <- sort(unique(size_key_rows$modified_series))[1:4]
  old_par <- par(mar = c(3, 3, 2, 1), oma = c(0, 0, 0, 0))
  layout(rbind(matrix(1:4, ncol = 2, byrow = TRUE), c(5, 5)), heights = c(1, 1, 0.16))
  for (series in series_ids) {
    candidates <- size_key_rows[size_key_rows$modified_series == series, ]
    key <- candidates[nrow(candidates), ]
    key_match <- rep(TRUE, nrow(size_fit))
    for (key_name in size_keys) {
      key_match <- key_match & size_fit[[key_name]] == key[[key_name]]
    }
    obs <- size_fit[key_match & size_fit$vector == "obs", c("size_bin", "value")]
    pred <- size_fit[key_match & size_fit$vector == "pred", c("size_bin", "value")]
    x <- merge(obs, pred, by = "size_bin", suffixes = c("_observed", "_predicted"))
    x <- x[order(x$size_bin), ]
    observed <- x$value_observed / sum(x$value_observed)
    predicted <- x$value_predicted / sum(x$value_predicted)
    ylim <- range(c(observed, predicted), finite = TRUE)
    mids <- barplot(observed, col = "#1b6da8", border = NA,
         ylim = ylim, xlab = "", ylab = "",
         main = paste("Series", series, key$year), bty = "l")
    axis(1, at = mids, labels = x$size_bin)
    lines(mids, predicted, lwd = 2, col = "#c43c39")
    grid(col = "grey88")
  }
  if (length(series_ids) < 4) {
    for (unused in seq_len(4 - length(series_ids))) {
      plot.new()
    }
  }
  par(mar = c(0, 0, 0, 0))
  plot.new()
  legend("center", horiz = TRUE, bty = "n",
         fill = c("#1b6da8", NA), border = c(NA, NA),
         lty = c(NA, 1), lwd = c(NA, 2), col = c(NA, "#c43c39"),
         legend = c("Observed", "Predicted"))
  layout(1)
  par(old_par)
} else {
  size_obs <- tanner$data$size_comp_observations
  series_ids <- sort(unique(size_obs$series))[1:4]
  old_par <- par(mfrow = c(2, 2), mar = c(3, 3, 2, 1), oma = c(0, 0, 2, 0))
  for (series in series_ids) {
    x <- size_obs[size_obs$series == series, ]
    x <- x[order(x$year), ]
    row <- x[nrow(x), ]
    bins <- grep("^bin_", names(row), value = TRUE)
    values <- as.numeric(row[1, bins])
    values <- values / sum(values)
    barplot(values, col = "#1b6da8", border = NA,
         xlab = "", ylab = "", main = paste(row$fleet, row$sex, row$catch_type, row$year),
         bty = "l")
    grid(col = "grey88")
  }
  mtext("Observed only: Tanner ADMB size-fit summary is not present", outer = TRUE, line = 0.4)
  par(old_par)
}
Figure 2: Tanner crab size-composition diagnostics; ADMB predictions are shown when report output is available.