IN PROGRESS
Preface
In late 2025 it was noticed that the Gompertz growth model was misbehaving when using seasons-as-years with time-varying growth. This prompted renewed priority in completing a comprehensive review of growth. The resolution of the discrepancies is found in pull request #747 from March 2026. This document demonstrates the correctness of the SS3 growth x seasonality interaction as developed in SS3.30.24.3, in comparison to the previous model version 3.30.24.1.
Growth in SS3
Why is growth so complicated in SS3? Unlike most models, SS3 is designed to accommodate flexibility regarding the within year timing of when a cohort of fish begins its growth, and flexibility regarding the exact timing within a year of when an observation is made. There also is the capability to allow growth parameters to change over time, including density-dependence, so size-at-age cannot be calculated just once at the beginning of the time series.This means that there are three sections of code for calculating size-at-age: 1. At the beginning of the time series, calculate size-at-age; 2. At the beginning of each year, calculate the size at age to the beginning of each season within that year and to the beginning of the next year. This growth is according to the growth parameters of that year and follows the diagonal of the time x age matrix; 3. At the time of each subseason (minimum is two subseasons per year or season), calculate the size-at-age as growth from the beginning of that season.
Within each of these sections, there are four sections of code for each major growth function: 1. Von Bertallanffy 2. Richards/Gompertz 3. Three variants of age-specific K 4. Growth cessation
Within each of the growth functions, there is an equation to calculate size-at-age for the first age past the linear growth age range, and code for calculating growth of older ages, and some have specific code for growth within the plus group.
All of the above needs to account for season duration, which is simply 1.0 for an annual model, but models with seasons within year have great flexibility on number and duration of seasons, and models with seasons-as-years have a time step with a user specified duration in fractions of a year.
Unfortunately, as growth options were developed, nearly all the testing was with simple annual time steps; and as seasonality options were tested, nearly all the testing was with the simple von Bertallanffy function. This allowed some growth function x seasonality combinations to misbehave. Here we thoroughly test all combinations and update the code to behave as expected.
Test setup
The testbed for this demonstration started with the simple model setup which uses von Bertallanffy growth, single annual season, and growth parameters were not time-varying. From that base model, 42 model setups were created to cover all combinations of the following features:
| Growth Options |
1, 2(Richards), 2(Gompertz), 3, 4, 5, 8 |
| Seasonality |
annual, seasonal with two 6-month seasons, seasons-as-years with 6 month seasons |
| Model |
3.30.24.1 and new/test |
Growth option 2 (Richards) was configured such that the additional parameter had nil effect so that the resultant growth curve should match growth option 1 (von Bertallanffy). Growth options 3, 4, 5 for age-specific K were configured to allow for K at ages 2, 3 and 4 to have a multiplier, but all multipliers were fixed at 1.0 such that the resultant growth curve should match growth option 1. Growth options 2 (Gompertz - same as Richards but with parameter set to 0.001) and 8 (cessation) were included in the demonstration, but there is no parameter configuration that allows exact match to growth option 1.
An additional design configuration set the AFIX2 (L at Amax) value to 19 years old for the annual and two-season models, and to 39 seasons in the seasons-as-years model. With this configuration and the same K for all models, the relationship between the seasons-as-years result and the other two seasonality configurations should be apparent.
All models were run with Amin = 0.0 such that the Lmin growth parameter set the size at age 0.0 post-settlement. A secondary test used a range of Amin values to demonstrate the transition from linear growth below Amin to growth according to the curve.
The Linf parameter was set to be time-varying with a block beginning in 1980. This triggers SS3 to update the growth calculations in that year and all subsequent years.
All models were run with no estimation (-nohess -stopph 0) so will produce output directly from the input parameter values.
While conducting this test which included time-varying growth, we found it useful to augment the MEAN_SIZE_TIMESERIES output by adding output organized by cohort rather than year.
Results
The following results are based on the Report.sso output table labelled MEAN_SIZE_TIMESERIES with an additional output of mean size by cohort to better understand models with time-varying growth.
The tables below display mean length-at-age for females (Sex = 1), Season 1, Subseason 1, Yr = 1985 and selected ages for brevity.
With growth 1, the impact of time-varying Linf in 1980 affects the age 1 fish at the beginning of 1980 because they are just appearing in the population.
| growth1 |
oneseas |
3.30.24.1 |
1 |
1971 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1972 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1973 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1974 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1975 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1976 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1977 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1978 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1979 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1980 |
1 |
1 |
21.6597 |
28.9990 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1981 |
1 |
1 |
21.6597 |
28.9990 |
35.3335 |
40.7199 |
45.4064 |
74.7807 |
74.8875 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1982 |
1 |
1 |
21.6597 |
28.9990 |
35.3335 |
40.8008 |
45.4498 |
74.8241 |
74.9309 |
Mean length-at-age for the one season model with growth option 1 (von Bertallanffy) in subseason 1 using the current SS3 executable
And the time-varying Linf affects all other ages by the middle of 1980 (e.g. subseason = 2), then into 1981.
| growth1 |
oneseas |
3.30.24.1 |
1 |
1971 |
1 |
2 |
25.4383 |
32.2100 |
38.0546 |
43.0991 |
47.4530 |
74.7425 |
74.8417 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1972 |
1 |
2 |
25.4383 |
32.2100 |
38.0546 |
43.0991 |
47.4530 |
74.7425 |
74.8417 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1973 |
1 |
2 |
25.4383 |
32.2100 |
38.0546 |
43.0991 |
47.4530 |
74.7425 |
74.8417 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1974 |
1 |
2 |
25.4383 |
32.2100 |
38.0546 |
43.0991 |
47.4530 |
74.7425 |
74.8417 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1975 |
1 |
2 |
25.4383 |
32.2100 |
38.0546 |
43.0991 |
47.4530 |
74.7425 |
74.8417 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1976 |
1 |
2 |
25.4383 |
32.2100 |
38.0546 |
43.0991 |
47.4530 |
74.7425 |
74.8417 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1977 |
1 |
2 |
25.4383 |
32.2100 |
38.0546 |
43.0991 |
47.4530 |
74.7425 |
74.8417 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1978 |
1 |
2 |
25.4383 |
32.2100 |
38.0546 |
43.0991 |
47.4530 |
74.7425 |
74.8417 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1979 |
1 |
2 |
25.4383 |
32.2100 |
38.0546 |
43.0991 |
47.4530 |
74.7425 |
74.8417 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1980 |
1 |
2 |
25.4644 |
32.2828 |
38.0807 |
43.1252 |
47.4790 |
74.7686 |
74.8678 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1981 |
1 |
2 |
25.4644 |
32.2828 |
38.1677 |
43.1719 |
47.5257 |
74.8153 |
74.9145 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1982 |
1 |
2 |
25.4644 |
32.2828 |
38.1677 |
43.2470 |
47.5661 |
74.8557 |
74.9548 |
Mean length-at-age for the one season model with growth option 1 (von Bertallanffy) in subseason 2 using the current SS3 executable
The two season model matches the annual growth of the annual model, except for the plus group.
| growth1 |
twoseas |
3.30.24.1 |
1 |
1971 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
twoseas |
3.30.24.1 |
1 |
1972 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
twoseas |
3.30.24.1 |
1 |
1973 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
twoseas |
3.30.24.1 |
1 |
1974 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
twoseas |
3.30.24.1 |
1 |
1975 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
twoseas |
3.30.24.1 |
1 |
1976 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
twoseas |
3.30.24.1 |
1 |
1977 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
twoseas |
3.30.24.1 |
1 |
1978 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
twoseas |
3.30.24.1 |
1 |
1979 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
twoseas |
3.30.24.1 |
1 |
1980 |
1 |
1 |
21.6597 |
28.9990 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
twoseas |
3.30.24.1 |
1 |
1981 |
1 |
1 |
21.6597 |
28.9990 |
35.3335 |
40.7199 |
45.4064 |
74.7807 |
74.8502 |
| growth1 |
twoseas |
3.30.24.1 |
1 |
1982 |
1 |
1 |
21.6597 |
28.9990 |
35.3335 |
40.8008 |
45.4498 |
74.8241 |
74.8775 |
Mean length-at-age for the two season model with growth option 1 (von Bertallanffy) in subseason 1 using the current SS3 executable
This is because the plus group updating depends upon the numbers-at-age of the fish in the plus group and the numbers entering the plus group. The annual model does this correctly, but the order of operations in SS3 causes the seasonal model to do the updating as a simple average, not a numbers weighted average. This discrepancy is quite structural and is not addressed in the updated model.
The middle of the year for a one season model should match the beginning of season 2 in a two season model. Below, the display for years 1971 shows a match, but during the time series after the time-varying growth, there are noticeable discrepancies for the two season model.
| growth1 |
oneseas |
3.30.24.1 |
1 |
1971 |
1 |
2 |
25.4383 |
32.2100 |
38.0546 |
43.0991 |
47.4530 |
74.7425 |
74.8417 |
| growth1 |
oneseas |
3.30.24.1 |
1 |
1985 |
1 |
2 |
25.4644 |
32.2828 |
38.1677 |
43.2470 |
47.6309 |
74.9464 |
75.0456 |
| growth1 |
twoseas |
3.30.24.1 |
1 |
1971 |
2 |
1 |
25.4383 |
32.2100 |
38.0546 |
43.0991 |
47.4530 |
74.7425 |
74.8417 |
| growth1 |
twoseas |
3.30.24.1 |
1 |
1985 |
2 |
1 |
25.4644 |
28.9990 |
38.1677 |
43.2470 |
47.6309 |
74.9464 |
74.9855 |
Mean length-at-age for the one season and two season models with growth option 1 (von Bertallanffy) for season 1 and subseason 2 and then season 2 and subseason 1 for years 1971 and 1985 using the current SS3 executable
The code changes in 3.30.25 resolve this problem:
| growth1 |
oneseas |
test |
1 |
1971 |
1 |
2 |
25.4383 |
32.2100 |
38.0546 |
43.0991 |
47.4530 |
74.7425 |
74.8417 |
| growth1 |
oneseas |
test |
1 |
1985 |
1 |
2 |
25.4644 |
32.2828 |
38.1677 |
43.2470 |
47.6309 |
74.9464 |
75.0456 |
| growth1 |
twoseas |
test |
1 |
1971 |
2 |
1 |
25.4383 |
32.2100 |
38.0546 |
43.0991 |
47.4530 |
74.7425 |
74.8417 |
| growth1 |
twoseas |
test |
1 |
1985 |
2 |
1 |
25.4644 |
32.2828 |
38.1677 |
43.2470 |
47.6309 |
74.9464 |
74.9855 |
Mean length-at-age for the one season and two season models with growth option 1 (von Bertallanffy) for season 1 and subseason 2 and then season 2 and subseason 1 for years 1971 and 1985 using the updated SS3 executable
This investigation was launched on the basis of discrepancies that appeared in a Richards/Gompertz model with seasons-as-years and time-varying growth. First let’s show that a continuous string of 6 month seasons-as-years compares as expected to the annual model and the two season model in a year before time-varying starts when using the growth option 1 or 2. The size at age 4 seasons in the seasons as years is the same as the size at age 2 years in the annual and seasonal model.
| growth1 |
oneseas |
3.30.24.1 |
1 |
1979 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
twoseas |
3.30.24.1 |
1 |
1979 |
1 |
1 |
21.6597 |
28.9487 |
35.2398 |
40.6697 |
45.3561 |
74.7304 |
74.8372 |
| growth1 |
seas_as_years |
3.30.24.1 |
1 |
1979 |
1 |
1 |
21.6597 |
25.4383 |
28.9487 |
32.2100 |
35.2398 |
71.8855 |
73.2279 |
Mean length-at-age for the two season model with growth option 1 (von Bertallanffy) in subseason 1 using the updated SS3 executable
Here we compare 1979 (before timevary growth) to 1983, after Linf increased in 1980. With the one season model, the size-at-age increases slightly, as expected. However, with the seasons-as-years, the size-at-age decreases. This is because that section of code was erroneously applying the season duration twice.
Mean length-at-age for the one season and seasons-as-years models with growth option 2 (Richards/Gompertz) in subseason 1 using the 3.30.24.1 SS3 executable
The updated model corrects this problem.
Mean length-at-age for the one season and seasons-as-years models with growth option 2 (Richards/Gompertz) in subseason 1 using the test SS3 executable