Computed ssb

35 views
Skip to first unread message

Guillermo Caballero Luján

unread,
Jun 12, 2025, 9:29:28 AMJun 12
to SS3 - Forum
Good evening.

I was wondering if someone could point me in the code where is computed the SSB in the C++ code, including all parameters and values used in such computation and how can I extract all values of this calculations once I ran my model so I can compute it my self to see why I am getting a really weird result.

The model is running, is not giving an error but the result Is far away from the one expected and I need to see where is exactly the problem.

Thank you in advance,

Guillermo.

Richard Methot - NOAA Federal

unread,
Jun 12, 2025, 12:34:37 PMJun 12
to SS3 - Forum
Hello Guillermo,
The code for calculating SSB is found in the file SS_popdyn.tpl.

There you will find code beginning at line 956  (or close by as the number of lines of code can change) where you can look for the comments:
      //  SS_Label_Info_24.2.2 #Compute spawning biomass if this is spawning season so recruits could occur later this season
      //  SPAWN-RECR:   calc SPB in time series if spawning is at beginning of the season

or near line 1420  if spawn_month occurs not at the beginning of a season:
      //  SS_Label_Info_24.3.4 #Compute spawning biomass if occurs after start of current season
      //  SPAWN-RECR:   calc spawn biomass in time series if after beginning of the season

There is equivalent code in the forecast module and in the equilibrium module.

The calculation combines reproductive output at age, which is stored in matrix fec(g, a), with numbers at age at the time of spawning.  Index "g" is to morph (sex, growth_pattern, settlement season, etc.)
              SSB_pop_gp(y, p, GP4(g)) += fracfemale_mult * fec(g) * natage(t, p, g); // accumulates SSB by area and by growthpattern

fec(g) is either read from the wtatage.ss file if you are doing empirical weight-at-age approach, or is created from parametric body length, length to weight, and weight to fecundity and maturity, which is an entire function in SS3.

I hope this helps you solve your mystery.

Rick

Richard Methot - NOAA Federal

unread,
Jun 12, 2025, 12:56:40 PMJun 12
to SS3 - Forum
I suggest that you check the detailed model output to be sure that the fraction mature at age matches your information.
You can see detailed -at-age biology in the tables in report.sso.  Look for:
Biology_at_age_in_endyr report:47
also, the table:  AGE_SELEX report:32 has extra information including rows labelled as fecund (which has the values of fec(g)) and bodywt

Ian Taylor - NOAA Federal

unread,
Jun 12, 2025, 1:41:30 PMJun 12
to Richard Methot - NOAA Federal, Mosqueira Sanchez, Iago, SS3 - Forum
Hi Guillermo,
A few years ago Iago Mosqueira and I went through a process of replicating the SSB calculations outside of SS3 to confirm that the answers made sense for the models we were looking at. I've pasted below some R code cleaned up from that effort, where for the model I'm looking at, the ratio of the internal to external calculations of SSB in the final step is 1.000008. 

Note that in addition to potential issues around parameterization and input values for maturity and fecundity, where it's easy to introduce errors, the numbers at age are in 1000s. And if there is a fecundity relationship, it's possible to add division by a power of 1000 to make the spawning output in units of billions or trillions (here are some notes for how we do this rockfish on the U.S. West Coast: https://pfmc-assessments.github.io/pfmc_assessment_handbook/02-model-choices.html#fecundity).
-Ian

# read model output into R
model <- r4ss::SS_output()

# if maturity and/or fecundity is length-based, convert to age-based values
# if model is age based, skip ahead
# maturity * fecundity at length (matches product Mat and Fec columns)
spawn_output_at_length <- model$biology$"Mat*Fec"

# matrix of length at each age
len_at_age <- model$ALK[, , "Seas: 1 Sub_Seas: 1 Morph: 1"]
# matrix is in decreasing order of length, so reverse it
rownames(len_at_age)
len_at_age <- len_at_age[nrow(len_at_age):1, ]
rownames(len_at_age)

# spawning output at age is product of spawning output at length by distribution fo length-at-age
spawn_output_at_age1 <- spawn_output_at_length %*% len_at_age |> as.numeric()

# compare results above to spawning output at age in model output
spawn_output_at_age2 <- model$endgrowth |>
  dplyr::filter(
    Sex == 1
  ) |>
  dplyr::pull("Mat*Fecund")
# confirm that spawning output at age matches
range(spawn_output_at_age1 - spawn_output_at_age2)

# female numbers at age in the final year of the model
n_at_age_fem <- model$natage |>
  dplyr::filter(
    Time == model$endyr,
    Sex == 1
  ) |>
  dplyr::select(paste(0:model$accuage)) |>
  as.numeric()


# spawning output in the final year of the model
ssb_endyr1 <- model$timeseries |>
  dplyr::filter(
    Yr == model$endyr,
  ) |>
  dplyr::pull(SpawnBio)

# sum of spawning output * numbers calculated above
ssb_endyr2 <- sum(spawn_output_at_age2 * n_at_age_fem)
 
# look at ratio of the values
ssb_endyr1 / ssb_endyr2

--
You received this message because you are subscribed to the Google Groups "SS3 - Forum" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ss3-forum+...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/ss3-forum/6e502c80-b5d0-48dc-86ae-afdf08acbaacn%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages