Fisheries

Fish growth (von Bertalanffy) and length-weight condition

Individual age-length data (otolith/scale ages) for one or more groups (populations, lakes, sexes, years); question is how fast fish grow, how large they get, at what age they approach maximum size, whether growth or condition differs among groups, or whether growth varies with a group-level covariate (temperature, density, year).

module id: fisheries-growth✓ Example verdict: DONE

Is this the right analysis?

Use it when

Individual age-length data (otolith/scale ages) for one or more groups (populations, lakes, sexes, years); question is how fast fish grow, how large they get, at what age they approach maximum size, whether growth or condition differs among groups, or whether growth varies with a group-level covariate (temperature, density, year). Optional weights give the length-weight relationship and condition (Wr or Kn).

Not for

Length-frequency data without ages (needs ELEFAN/mixture methods), tag-recapture growth increments (Fabens model), back-calculated lengths from annuli (repeated measures per fish), stock assessment / mortality (use fisheries-stock), growth models other than von Bertalanffy (Gompertz, logistic, Richards are not implemented).

The data you need

One row per fish with age (years), length, group (optional), weight (optional); optional group-covariate table (one row per group).

What the example's main data file looks like:

First 5 of 1,000 rows of sim_walleye_2014.csv.
fish_idyearsexagetl_mmwt_g
SIM14-F-00012014female1360454.3
SIM14-F-00022014female2431606.5
SIM14-F-00032014female55672065.4
SIM14-F-00042014female1344416.4
SIM14-F-00052014female24721049.6

Not sure? Use the finder to compare modules.

Try it

The worked example

Do female and male walleye differ in growth and condition? (simulated autumn 2014 gill-net survey)

The question: Do females and males differ in how large they get, how fast they grow, and in condition (weight at length)?

The data are simulated, so the true answer is known and the analysis can be checked against it.

What the researcher typed

I have age, length, weight and sex for 1,000 walleye from an October 2014 gill-net survey (sim_walleye_2014.csv). Do females and males differ in growth and in condition? I'd like a report and figures I can put in a paper (CJFAS format).

Run it yourself

  1. Download the example data below and unzip it.

  2. Open the unzipped folder in Claude Code (see Install).

  3. Paste the prompt above. The agent checks the files, writes a plan, runs planted truth, fits the model and writes the report. You can compare your results with the ones below.

Contains sim_walleye_2014.csv, the settings file (config_si.yaml), a description of the data and a step-by-step walkthrough.

Study design details
n groups2
groupsfemale (reference), male
n female530
n male470
n fish1000
otolith age range1, 16
age offset years0.5
total mortality Z0.55
prop weighed1.0
years1
errormultiplicative (lognormal)
enginenls (Kimura LRT)
What the report says

Results from the example data

Figure: Length at age with the fitted von Bertalanffy curve per group (line: median length at age, drawn over each group's observed ages), 95% confidence band for the curve (shaded) and 95% prediction interval for individual fish (dotted).
Length at age with the fitted von Bertalanffy curve per group (line: median length at age, drawn over each group's observed ages), 95% confidence band for the curve (shaded) and 95% prediction interval for individual fish (dotted). Points are individual fish (ages jittered).

What this means

  • female: fish approach an asymptotic length (Linf) of 685 mm (95% CI 665 to 706) and grow toward it at K = 0.302 (95% CI 0.272 to 0.335) per year. They reach 90% of Linf (about 617 mm) at age 6.6 (95% CI 6.0 to 7.2) years.
  • male: fish approach an asymptotic length (Linf) of 561 mm (95% CI 547 to 575) and grow toward it at K = 0.385 (95% CI 0.339 to 0.437) per year. They reach 90% of Linf (about 505 mm) at age 4.8 (95% CI 4.3 to 5.4) years.
  • Linf in male is smaller than in female by 124 mm (95% CI 99 to 149). K in male is larger than in female by 0.084 per year (95% CI 0.025 to 0.142).
  • Kimura (1980) likelihood-ratio tests: common linf chi-square = 81.7, df = 1, p < 0.0001; common k chi-square = 8.3, df = 1, p = 0.0041; common t0 chi-square = 0.4, df = 1, p = 0.51; all parameters common chi-square = 448.5, df = 3, p < 0.0001.
  • Length-weight (1000 weighed fish): W = 1.19e-06 x L^3.340, with b 95% CI 3.308 to 3.372; this is positive allometry.
  • At the same length, fish in male weigh 2.1% more than fish in female (95% CI +0.9% to +3.3%).
  • Relative weight Wr by group: female 90.2 (95% CI 89.5-91.0, n = 530); male 91.1 (95% CI 90.4-91.9, n = 470).

More figures

Figure: Growth parameters by group (panel titles give units): estimate with 95% Wald confidence interval.
Growth parameters by group (panel titles give units): estimate with 95% Wald confidence interval. Linf, K and t0 are strongly correlated; compare whole curves and age at 90% Linf rather than one parameter alone.
Figure: Length-weight relationship on log-log axes: mean weight at length (bias-corrected back-transform; line, drawn over each group's observed lengths) with 95% confidence band, common slope b = 3.340 with group-specific intercepts.
Length-weight relationship on log-log axes: mean weight at length (bias-corrected back-transform; line, drawn over each group's observed lengths) with 95% confidence band, common slope b = 3.340 with group-specific intercepts. Points are weighed fish.
Figure: Relative weight Wr by group: mean (large symbol) with 95% confidence interval (vertical line; may be shorter than the symbol when n is large; n = 530, 470 fish).
Relative weight Wr by group: mean (large symbol) with 95% confidence interval (vertical line; may be shorter than the symbol when n is large; n = 530, 470 fish). Small points are individual fish. Dashed line: Wr = 100, the standard weight for the fish's length (Walleye standard weight equation (Murphy et al. (1990), RLP method; via FSA::wsVal)).

Figures are the module's default publication figures, reduced for the web. The report also saves them as PDF and full-resolution PNG.

How we know it works

Planted truth at the example's design

Before trusting the example's results, the module was run on many simulated data sets with the same design and known true values. Each row is one quantity: did the method get it back? (What the thresholds are.)

Verdict for this example: ✓ DONE

Every quantity checked passed planted truth at this design.

The example's design compared with the validated design: larger than validated
This example2 groups (530 females, 470 males), 1,000 aged fish, ages 1-16
Design the module was validated at3 groups x 150 aged fish, ages 1-15, total mortality 0.35

The example has many more fish per group than the validated default (about 500 vs 150). At the default all 25 growth and condition estimands passed; the main weakness is low power for single-parameter group differences (0.59 for a 50 mm Linf difference even at 250 fish per group), and Linf is only reliable when enough old fish are sampled, which the example meets (19 females and 54 males beyond the age at 90% of Linf).

17 of 17 rows pass.

Show the full recovery table (17 rows)
Bias: average estimate minus truth, as a percentage of the truth. Allowed bias: the largest bias that still passes, the bigger of 10% of the truth and 3 Monte Carlo standard errors († = set by that simulation-noise floor, so the allowance is wider than 10%). Coverage: share of 95% intervals that contained the true value, with the minimum needed in brackets. ≈ borderline: the bias used 80–125% of its allowance, or coverage was within 0.015 of the minimum; such a result could flip between PASS and FAIL with another set of simulations. Quantity names are as they appear in the report.
QuantityTrue valueAverage estimateBiasAllowed biasCoverage (needed)Result
age_at_90pct_Linf_female6.436.46+0.6%±10.0%0.94 (0.904)✓ PASS
age_at_90pct_Linf_male4.84.8-0.1%±10.0%0.94 (0.904)✓ PASS
condition_ratio_male_vs_female1.021.02-0.0%±10.0%0.94 (0.904)✓ PASS
K_female0.310.309-0.2%±10.0%0.93 (0.904)✓ PASS
K_male0.390.392+0.5%±10.0%0.96 (0.904)✓ PASS
K_diff_male_vs_female0.080.0825+3.2%±10.0%0.94 (0.904)✓ PASS
Linf_female680681+0.2%±10.0%0.95 (0.904)✓ PASS
Linf_male560559.5-0.1%±10.0%0.94 (0.904)✓ PASS
Linf_diff_male_vs_female-120-121.5-1.3%±10.0%0.95 (0.904)✓ PASS
lw_a00-0.1%±10.0%0.96 (0.904)✓ PASS
lw_b3.343.34+0.0%±10.0%0.96 (0.904)✓ PASS
lw_residual_sd0.090.0897-0.4%±10.0%0.94 (0.904)✓ PASS
mean_weight_at_500_female1,2461,246+0.0%±10.0%0.95 (0.904)✓ PASS
residual_sd0.050.0501+0.1%±10.0%0.93 (0.904)✓ PASS
t0_female-1-1.01-0.6%±10.0%0.93 (0.904)✓ PASS
t0_male-1.1-1.1-0.2%±10.0%0.96 (0.904)✓ PASS
t0_diff_male_vs_female-0.1-0.0958+4.2%±34.4%†0.93 (0.904)✓ PASS

Limits that matter

  • Linf needs old fish: 19 females and 54 males are beyond the age at 90% Linf here (enough); with < 5 such fish Linf is unreliable.
  • Length-selective gear (e.g. gill nets) biases K (-14%) and t0 (-0.36 y) and drops coverage to 0.58; selectivity is not modelled (simulated data have none). Drop partly selected ages with real data.
  • Single-parameter differences have low power: at this design power is 0.81 for a 0.08 difference in K and 0.10 for a 0.1-year difference in t0; a non-significant t0 test means not detectable, not equal.
  • Not modelled: ageing error, individual growth variation beyond the residual, growth models other than von Bertalanffy.
All known limits from the module's validation record
  1. Linf needs old fish. With few fish near the asymptote (hard_nls, hard_nimble_fixed) Linf, and so age at 90% Linf, is not identified: nls occasionally returns absurd Linf with wide intervals (age at 90% Linf fails the gate); the Bayesian model returns Linf biased +16 to +26% toward its prior. The module notes when < 5 fish are at or beyond the estimated age at 90% Linf; treat Linf as unreliable then and compare groups by length at ages that were sampled.
  2. Length-selective gear biases growth (section 4): K -14%, t0 -0.36 years, coverage down to 0.58. Selectivity is not modelled; drop partially selected ages.
  3. Single-parameter group differences have low power (section 6): 0.59 for a 50 mm Linf difference with 250 fish per group. Use Kimura's joint test or whole-curve comparisons.
  4. Hierarchical SDs with few groups. At 10 groups the posterior median of the among-group SDs is biased +6 to +7% (passes); the posterior mean was +14% (failed). With 6 groups x 40 fish sd_log_Linf is biased +24.5% and FAILS the gate (section 5): with fewer than 10 groups (independent verification: 8 groups x 70 fish also FAILS, +14.7% and +16.2%), interpret the among-group SD of Linf through its upper 95% limit ("variation in Linf is at most about N%"), not as a point value. nlme's REML gives smaller SDs than the Bayesian median on the example data.
  5. Hierarchical convergence needs longer chains than the fixed-group model: on the example data (12 groups, 2106 fish) 10,000 iterations gave max R-hat 1.010 and min bulk ESS 335 (t0), short of the real-data standard, so the hierarchical default is 20,000 iterations with 5,000 burn-in.
  6. Not modelled: ageing error, individual growth variation beyond the residual, Linf-K correlation among groups, growth models other than von Bertalanffy, length-at-capture date beyond age_offset.

How this module was validated

  • 500 simulated datasets at the default design with nonlinear least squares, and 100 with the Bayesian Nimble engine (3 chains x 3,000 iterations); both PASSED all 25 estimands (nls bias at most 0.8%, coverage 0.928-0.968).
  • Hierarchical model with 10 groups x 80 fish passed all 69 estimands after switching to posterior medians for the among-group SDs; with 6 groups (and 8 groups in independent checks) the among-group SD of Linf failed (+24.5% and +14.7%).
  • Harder design with few old fish (60 per group, high mortality): Linf was not identified in some datasets, and age at 90% Linf failed the gate.
  • Negative controls failed as required: length-selective gear (12 of 25 estimands failed, K -14%), unmodelled ageing error (13 of 33 failed), and Gompertz data fitted as von Bertalanffy (4 of 12 failed).
  • False-positive rates of the Kimura likelihood-ratio tests were 0.039-0.057 with identical growth (nominal 0.05); about 6-7% with a small second group of 90 fish.
  • Power over 30-250 fish per group: single-parameter differences need many fish, while Kimura's joint test had power 0.996 at 150 per group.
  • Cross-checks matched FSA, fishmethods, nlme and lm; two separate verifier agents with their own simulators (different units, group numbers and age structures) passed and reproduced the builder's runs; verdict: verified with limits.

Every run uses the acceptance gate described on the validation page. The full record follows, section by section, exactly as kept in the module's VALIDATION.md.

Settings and pass/fail rule used for every run

Date: 2026-10-19. R 4.5.3, FSA 0.10.1, nimble (ecotools Rlib), fishmethods and nlme for cross-checks. Gate: recovery_gate() with its fixed thresholds (|bias| <= max(10% of truth, 3 MC SE), or 0.1 x empirical SD when the truth is 0; coverage >= 0.95 - 3 MC SE, i.e. 0.921 for 500 reps and about 0.884 for 100 reps; >= 90% of reps converged). Recovery uses fast_settings (Nimble: 3 chains x 3,000 iterations, 1,000 burn-in) unless a config overrides it; a Bayesian rep counts as converged when max R-hat < 1.05.

Code state: the nls runs and default_nimble_fixed used the files hashed in dev/fisheries-growth/code_md5_validation.txt. On 2026-10-19 the hierarchical engine was changed to report the posterior median of sd_log_Linf and sd_log_K (section 3), and hier_nimble, null_hier, hard_nimble_fixed and hard_hier were rerun with dev/fisheries-growth/code_md5_rerun.txt. That change touches only the hierarchical point estimates of the two SDs, so the nls and fixed-group runs are unaffected. Later edits touch only text in output.R and default settings in module.R (final hashes: dev/fisheries-growth/code_md5_final.txt).

Reproduce: bash dev/fisheries-growth/run_validation.sh <run> <nsim> with configs in modules/fisheries-growth/validation/<run>.yaml (outputs in results/fisheries-growth/validation/<run>/), bin/Rscript dev/fisheries-growth/kimura_fpr.R, bin/Rscript dev/fisheries-growth/crosscheck.R. Tables come from dev/fisheries-growth/validation_tables.R. Times per fit: nls 0.3 s; Bayesian fixed groups (3 groups x 150 fish) 12 s; hierarchical (10 groups x 80 fish) 35 s, all at fast settings.

Default design (module defaults): 3 groups x 150 aged fish; catch-at-age P(a) ∝ exp(-0.35(a-1)), ages 1-15 (about 13 fish per group at age >= 8); Linf 600/650/600 mm, K 0.30/0.25/0.30, t0 -0.5; multiplicative error sigma 0.08 (log scale); every fish weighed, W = 3.5e-6 L^3.18 with condition 1/1.05/1 and log-scale SD 0.08.

0. Selftest

core/run.R selftest fisheries-growth (4 reps): contract checks pass; with 4 reps the coverage bound is 0.80 and some estimands fail on Monte Carlo noise alone, as expected (results/fisheries-growth/selftest/).

1. Default design, nls engine (nsim = 500): PASS on all 25 estimands

500/500 fits, no errors.

estimandtruthmean estrel. bias %bias MCSEcoveragepowergate
Linf_A6006010.20.840.928-PASS
Linf_B6506510.20.910.968-PASS
Linf_C6006000.10.790.932-PASS
K_A0.300.3000.10.00110.936-PASS
K_B0.250.2510.30.000850.962-PASS
K_C0.300.3010.50.00110.944-PASS
t0_A-0.5-0.503-0.60.00390.950-PASS
t0_B-0.5-0.4990.20.00340.962-PASS
t0_C-0.5-0.4990.20.00380.954-PASS
age_at_90pct_Linf_A7.187.220.60.0260.938-PASS
age_at_90pct_Linf_B8.718.740.30.0280.952-PASS
age_at_90pct_Linf_C7.187.190.20.0250.944-PASS
Linf_diff_B_vs_A5049.7-0.71.260.9500.430PASS
Linf_diff_C_vs_A0-0.86-1.190.9360.064PASS
K_diff_B_vs_A-0.05-0.04960.80.00150.9500.356PASS
K_diff_C_vs_A00.0011-0.00160.9280.072PASS
t0_diff_B_vs_A00.0039-0.00530.9460.054PASS
t0_diff_C_vs_A00.0040-0.00550.9440.056PASS
residual_sd0.080.0799-0.10.000120.942-PASS
lw_b3.183.18-0.00.000450.960-PASS
lw_a3.5e-63.51e-60.29.2e-90.962-PASS
lw_residual_sd0.080.0799-0.10.000120.958-PASS
mean_weight_at_500_A13401340-0.10.460.952-PASS
condition_ratio_B_vs_A1.051.050.00.000460.9320.996PASS
condition_ratio_C_vs_A11.000.10.000440.9400.060PASS

Other default-design runs (full tables in the appendix):

runengine / variantrepsconvergedgate
additive_nlsnls, additive normal error (sigma 30 mm) simulated and fitted500500PASS all 25 (max abs rel. bias 1.0%, coverage 0.928-0.966)
default_nimble_fixedBayesian, fixed groups (Nimble)100100/100PASS all 25 (Linf rel. bias +0.4 to +1.0%, K -1.0 to 0.0%, t0 -1.7 to -2.8%; coverage 0.92-0.98)
2. Kimura (1980) likelihood-ratio tests: false-positive rate and power

dev/fisheries-growth/kimura_fpr.R; reject at p < 0.05. Groups with identical growth (Linf 600, K 0.30, t0 -0.5 for all three groups):

designrepsall commoncommon Linfcommon Kcommon t0
default (150/group, Z 0.35, multiplicative)10000.057 (MCSE 0.007)0.0520.0460.039
hard (60/group, Z 0.9)9890.0460.0420.0440.052
additive error (sigma 30 mm), additive fit5000.040 (MCSE 0.009)0.0560.0520.040

All within 2 MC SE of 0.05. Power at the default design (Linf 600 vs 650, K 0.30 vs 0.25; 500 reps): all common 0.996, common Linf 0.440, common K 0.362, common t0 0.058 (t0 is truly equal).

3. Hierarchical model (Nimble, random group effects + group covariate)

Design hier_nimble: 10 groups (lakes/years) x 80 fish, log Linf_g ~ N(log 600 - 0.05 z_g, 0.08), log K_g ~ N(log 0.30 + 0.10 z_g, 0.15), common t0 -0.5, group covariate "temperature" (mean 20, SD 2, standardised). nsim = 100, 96/100 reps with R-hat < 1.05.

PASS on all 69 estimands (group-level Linf_g, K_g, age at 90% Linf, differences, condition ratios included; appendix). The hyper-parameters:

estimandtruthmean estrel. bias %bias MCSEcoveragepowergate
Linf_typical6006000.01.970.948-PASS
K_typical0.300.299-0.40.00180.990-PASS
t0-0.5-0.507-1.40.00380.979-PASS
sd_log_Linf0.080.08556.80.00240.969-PASS
sd_log_K0.150.1606.40.00430.969-PASS
Linf_beta_temperature-0.05-0.0502-0.40.00290.9690.281PASS
K_beta_temperature0.100.0949-5.10.00500.9790.354PASS
residual_sd0.080.08020.20.00020.938-PASS

History, reported so the change is visible: the first run (same seeds) reported the posterior mean of the SDs and FAILED them on bias: sd_log_Linf +14.6% (MCSE 3.2%), sd_log_K +13.5% (MCSE 3.0%), coverage 0.969/0.969. With 10 groups the SD posterior is right-skewed and its mean sits above the truth. We now report the posterior median for these two SDs only (README, methods text). The thresholds were not changed. With 6 groups the median is still biased (section 5).

Negative control null_hier (same design, Linf_beta = K_beta = 0; 98/100 converged): the 95% credible interval excluded 0 in 4.1% (Linf_beta) and 3.1% (K_beta) of reps; all 69 estimands PASS (sd_log_Linf +5.5%, sd_log_K +5.6%).

4. Negative control: the gate can fail (mis-specified sampling)

misspec_selectivity: the same default design, but the gear never catches fish < 250 mm, while the model assumes each age class is sampled at random. nls engine, 500 reps. 12 of 25 estimands FAIL:

estimandtruthmean estrel. bias %coveragegate
K_A0.300.259-13.80.768FAIL
K_C0.300.255-14.90.676FAIL
t0_A-0.5-0.856-71.20.624FAIL
t0_C-0.5-0.886-77.30.576FAIL
Linf_A600619+3.20.912FAIL (coverage)
Linf_C600621+3.50.892FAIL (coverage)
age_at_90pct_Linf_A7.188.17+13.80.898FAIL
K_diff_B_vs_A-0.05-0.016+680.868FAIL
Linf_diff_B_vs_A5038.4-23.10.932FAIL (bias)
t0_diff_B_vs_A00.271-0.828FAIL

Length-weight and condition still PASS (the selectivity does not bias W given L). This is the failure mode the README warns about.

5. Harder designs: where the module stops working

hard_nls: few old fish (60 fish/group, Z = 0.9; about 0.1 fish per group at age >= 8). 494/500 fits (6 errors: "nls fit failed from every starting value", reported to the user as such). 23/25 estimands pass the gate, but this hides the real problem: with almost no fish near the asymptote, Linf is not identified in a minority of data sets. Median Linf_A is 596 (truth 600) but 6.3-7.1% of fits put Linf above 1.5 x truth (mean Linf_A 2,380 mm; the bias passes only because 3 MC SE is then enormous), the median 95% CI is 56-68% of Linf wide, and age at 90% Linf FAILS (coverage 0.907 and 0.909 < 0.921; mean 55 vs 7.2 years, driven by those fits). Condition, length-weight and the K/t0 estimates pass. The module flags this situation in every fit (note when < 5 fish are at or beyond the estimated age at 90% Linf).

hard_nimble_fixed: same design, Bayesian fixed groups (97/100 converged). 9 FAIL: Linf biased +16 to +26% (posterior mean 722-757 vs 600-650), K_C -12.5%, t0 -21 to -31%, age at 90% Linf +33 to +59%; coverage stays 0.92-0.97. When the data do not identify Linf the posterior follows the prior N(log max length, 1), which is wide and skewed upward. Condition, length-weight, differences between groups and the residual SD pass.

hard_hier: hierarchical with 6 groups x 40 fish, Z = 0.6 (15,000 iterations, 5,000 burn-in). 97/100 reps converged (the first attempt at the fast setting of 3,000 iterations converged in only 44/100, so this design needs longer chains; 57 s per fit). 44 of 45 estimands PASS; sd_log_Linf FAILS: posterior median 0.0996 vs truth 0.08 (+24.5%, MCSE 5.8%), coverage 0.948. sd_log_K passes (+5.7%, coverage 0.959). Linf_typical +1.4% (coverage 0.969), K_typical -0.1%, t0 -1.1%, Linf_beta -0.8% (power 0.04), K_beta -6.0% (power 0.13); group-level Linf_g, K_g, age at 90% Linf all pass. With 6 groups the data barely inform the among-group SD of Linf, and the half-normal(0, 0.5) prior pulls it upward; the 95% interval is still honest. With 6 groups the covariate effects have power <= 0.13 for effects of this size.

6. Power over sample size

--vary n_per_group=30,60,100,150,250 at the default parameters (results/fisheries-growth/power/). Proportion of reps whose 95% CI excludes the null:

fish per groupLinf_diff_B_vs_A (50 mm)K_diff_B_vs_A (-0.05)condition_ratio_B_vs_A (1.05)null contrasts (C vs A, t0)
300.0750.0900.7100.035-0.090
600.1300.1150.9350.030-0.060
1000.2950.2450.9900.025-0.055
1500.4300.3651.0000.040-0.080
2500.5900.4801.0000.040-0.065

Coverage stayed 0.91-0.98 at every sample size. Single-parameter differences need many fish because Linf and K trade off; Kimura's joint test that the curves differ has power 0.996 at 150/group (section 2).

7. Cross-checks against independent implementations

dev/fisheries-growth/crosscheck.R on the example data (Lake Erie walleye 2014, female vs male, n = 1006; and 2003-2014 for the hierarchical model, n = 2106):

checkresult
nls (additive) vs per-group FSA::vbFuns + nls fitsmax relative difference in Linf/K 6.2e-6; max t0 difference 1.0e-5 years
Kimura LRT chi-squares vs fishmethods::growthlrt (independent code)132.45 / 4.97 / 5.64 / 638.56 in both (max abs difference 0.005, fishmethods prints 2 decimals)
nls (multiplicative) vs Bayesian fixed groups (Nimble, 10,000 iterations)every growth estimate within 0.09 nls SE (e.g. Linf_female 679.6 vs 680.3; K_male 0.3903 vs 0.3905)
hierarchical Nimble vs nlme::nlme (REML, random log Linf and log K, common t0)Linf_typical 583.6 vs 582.9; K_typical 0.4037 vs 0.4048; t0 -0.837 vs -0.828; residual SD 0.0677 vs 0.0677; group Linf within 1.0%, group K within 1.8%. Among-group SDs: Nimble median 0.047 / 0.133 vs REML 0.039 / 0.113 (both REML values inside the 95% CIs 0.028-0.087 and 0.085-0.230)
Relative weight vs FSA::wrAddmax abs difference 2.8e-14; same fish missing Wr (below 150 mm minimum)
length-weight slope vs plain lm(log W ~ log L + group)identical
8. Example fits
  • example/config.yaml (nls, sexes, 2014): report results/fisheries-growth/example/. Female Linf 680 mm (661-699), K 0.312; male Linf 559 mm (548-569), K 0.390; males reach 90% of Linf at 4.8 years vs 6.4. Kimura: Linf and K differ (chi-square 113.4 and 8.8), t0 does not (p = 0.21). b = 3.34; males 1.8% heavier at the same length. Every sentence in the report was checked against the estimates table; figures inspected.
  • example/config_hierarchical.yaml (Nimble, year as random effect, calendar year as covariate): 20,000 iterations, 5,000 burn-in, 3 chains: max R-hat 1.002, min bulk ESS 581 (meets the real-data standard; at 10,000 iterations it did not: R-hat 1.010, ESS 335). Typical year: Linf 584 mm (565-605), K 0.402 (0.362-0.445); among-year SD 0.049 on log Linf (about 5%) and 0.126 on log K (about 13%); calendar year: +0.3% Linf per SD (CI -3.2 to +3.8%), +5.7% K per SD (CI -3.1 to +15.3%, P(positive) 0.91), i.e. no clear trend. 2004 stands out (Linf 636 mm, K 0.294). Condition fell: 2014 fish weigh 7.5% less at the same length than 2003 fish (CI -9.1 to -5.8%). Report sentences checked against the table; conditional-effects plots (Linf and K vs year, response scale with 50/95% bands and shrunken group estimates) inspected.
  • Both example reports in results/fisheries-growth/example*/ were regenerated on 2026-10-19 with the final verified code (hashes in dev/fisheries-growth/verify/code_md5_verified.txt); estimates are unchanged (hierarchical: max R-hat 1.002, min bulk ESS 581). The final hierarchical report also notes that year 2005 has only 4 fish at or beyond its estimated age at 90% of Linf.
Independent verification (2026-10-19, separate verifier agent)

Script: dev/fisheries-growth/verify/verify.R (own simulator, no module code used to simulate; module fit() + core summarise_recovery()/recovery_gate()). Outputs in dev/fisheries-growth/verify/out/; logs logs/fisheries-growth_verify_*.log.

Literature / code check. VB typical parameterisation L = Linf(1 - exp(-K(age - t0))) matches FSA (makeGrowthFun, param 1); age at 90% Linf = t0 + ln10/K is correct. Kimura LRT -2 ln Lambda = N ln(RSS_H/RSS_general) with (G-1) df per constrained parameter matches fishmethods::growthlrt source (-N*log(RSS1/RSS_k)); rerun on the 2014 walleye data with additive error: 132.448 / 4.968 / 5.644 / 638.565 vs fishmethods 132.45 / 4.97 / 5.64 / 638.56; parameter estimates and SEs identical (Linf_female 684.05, SE 7.575). Walleye Ws from FSA::wsVal: log10 Ws = -5.453 + 3.18 log10 TL (Murphy et al. 1990, RLP, min 150 mm); hand Wr means 88.77 / 90.30 = report. Length-weight bias correction exp(s^2/2) and condition ratio exp(group coefficient) are correct. simulate() and fit() share no model code (fit uses formula strings / Nimble code); the independent simulator below rules out cancelling errors.

Independent planted truth (design differs from the builder's: 4 unequal groups north/south/ east/west, n = 100/120/80/150, ages 2-18 drawn as recruitment age + geometric survival (Z 0.25) plus a 0.4-yr capture offset, Linf 450/480/450/520, K 0.20/0.20/0.26/0.17, t0 -1.0/-0.6/-1.0/-0.8, W = 10^-5.2 * cond * L^3.05, lw sigma 0.10, condition 1/0.94/1.06/1, 70% of fish weighed, reference length 400):

runrepsresult
nls, multiplicative (sigma 0.06)500PASS 33/33;rel bias<= 1.6% except t0_diff_west (+6.0%); coverage 0.930-0.970
nls, additive (sigma 25 mm)500PASS 33/33; coverage 0.930-0.968
Nimble fixed groups (fast settings)100PASS 33/33, 100/100 converged; coverage 0.93-0.98
Hierarchical, 8 lakes x 70 fish, covariate depth (Linf_beta 0.06, K_beta -0.08, sd_log 0.10/0.12)10045/46 PASS, 96 converged. Linf_typical +0.8%, K_typical +0.2%, Linf_beta +6.0%, K_beta +2.6%, sd_log_K +9.7% (pass); sd_log_Linf +14.7% FAILS (coverage 0.927 is fine)

Builder's runs rerun. recover with validation/default_nls.yaml (500 reps) and validation/hier_nimble.yaml (100 reps) reproduced the recorded summaries exactly (max abs difference in mean estimates 0; PASS 25/25 and 69/69).

Controls.

  • Identical growth and condition in 3 groups (nls, 1,000 reps): Wald false-positive rates 0.043-0.052 for every Linf/K/t0 difference and condition ratio; Kimura tests reject 0.055 (Linf), 0.061 (K), 0.056 (t0), 0.053 (all) (binomial 95% range at n = 1000: 0.037-0.064).
  • Hierarchical covariate effects = 0 (8 lakes, 97 converged): false-positive rate 0.031 (Linf), 0.062 (K).
  • Negative control, ageing error (fish older than 6.4 read 1 year young, older than 9.4 read 1-3 years young; not modelled): 13/33 FAIL (Linf up to +9.6% with coverage 0.55, age at 90% Linf +10 to +15%, K coverage 0.85, residual SD coverage 0.76). The gate can fail.

Usability. fit on example/config.yaml reproduces the stored estimates exactly. Every sentence of both example reports was checked against the estimates (Linf, 90% Linf, differences, Kimura, b, condition %, Wr, covariate % effects = 100(exp(beta)-1), SD of calendar year 3.61); all correct, association (not causal) wording for covariates. Figures viewed (growth curves with CI and PI bands, conditional effects of calendar year on Linf/K on the response scale, condition). Messy-data test (dev/fisheries-growth/verify/messy/): wrong length column and duplicated id already gave clear messages. Fixed in module.R (read_data/check_data only; fit and simulate unchanged):

  • a missing filter: column now stops with the column list (was "no rows left");
  • group values not in group_levels are listed with a "did you mean" hint, and a rare group that looks like a typo of another (femal, Male) gets the same hint;
  • NA/non-numeric age and length messages list fish ids (row numbers were post-filter and misleading);
  • ages > 150 are flagged as probably months/days;
  • weights more than 2.5x off the length-weight line (e.g. some weights in kg) are now a data problem (they were only a note after fitting). Both example datasets and 20 default simulations raise no problems. Also changed: output.R adds a sentence when a hierarchical fit has < 10 groups saying the among-group SDs tend to be overestimated; README/Known limit 4 corrected from "fewer than about 8 groups" to "fewer than 10 groups". Selftest (4 reps) runs. Code hashes after verification: dev/fisheries-growth/verify/code_md5_verified.txt.

Verdict: VERIFIED_WITH_LIMITS. The default design and an independent design pass for nls, additive nls and Bayesian fixed groups; the hierarchical model recovers everything except the among-group SD of log Linf below 10 groups (documented limit 4).

Independent verification 2 (2026-10-19, second verifier agent)

Script: dev/fisheries-growth/verify/v2/verify2.R (own simulator written from the VB equations; no module code used to simulate; module fit() + core summarise_recovery()/recovery_gate()). Outputs: dev/fisheries-growth/verify/v2/out/; logs logs/fisheries-growth_verify2_*.log. Code state: dev/fisheries-growth/verify/v2/code_md5_verify2.txt. Note: output.R differs from verify/code_md5_verified.txt because a figures session edited the figure code (fg_figures only) at 08:15 and 08:23 after the first verification; engines, lw.R, simulate.R and module.R are unchanged, so recovery results are unaffected. The example fit below was run with the 08:23 output.R.

Literature: VB form matches FSA::makeGrowthFun(param = 1); Kimura statistic matches the fishmethods::growthlrt source (-N*log(RSS1/RSSk)); walleye Ws (-5.453, 3.18, min 150 mm, Murphy et al. 1990 RLP) matches FSA::wsVal; hierarchical lognormal Linf/K follows Helser & Lai (2004).

Design (different from the builder's and verifier 1's): lengths in cm, weights in kg, 2 groups (inlet n = 180, outlet n = 90), dome-shaped catch-at-age P(a) ∝ a^2 exp(-0.25a), ages 3-28 + 0.5, Linf 92/80 cm, K 0.13/0.17, t0 -1.4/-0.9, W = 7e-6 cond L^3.1 (cond 1/0.93), lw sigma 0.12, 60% weighed.

runrepsresult
nls multiplicative (sigma 0.07)500PASS 17/17;rel bias<= 1.3% except t0_outlet -7.9% and t0_diff -11% (within 3 MC SE); coverage 0.930-0.962
nls additive (sigma 3.5 cm)500PASS 17/17; coverage 0.934-0.960
Nimble fixed groups (fast settings)100PASS 17/17, 100/100 converged; t0_diff -30% (within 3 MC SE, coverage 0.98)
Hierarchical, 14 lakes x 50 fish, cm, covariate elevation (Linf_beta 0.04, K_beta -0.06, sd_log 0.12/0.10)100PASS 50/50; 91/100 converged at fast settings (gate needs 90); sd_log_Linf +0.4%, sd_log_K +8.8%, betas -1.4% / +0.04%
Builder's validation/additive_nls.yaml rerun with core/run.R recover500identical to stored summary (max diff in mean estimate and coverage 0)

With 14 groups the among-group SDs pass, consistent with Known limit 4 (problem below 10 groups).

Controls:

  • Null (identical growth and condition, 1,000 reps, nls): Wald false-positive rates 0.049 (condition), 0.054 (Linf), 0.067 (K), 0.061 (t0); Kimura rejection 0.057 (Linf), 0.070 (K), 0.068 (t0), 0.070 (all). Binomial 95% range at 1,000 reps is 0.037-0.064, so with a small second group (90 fish) the nominal 5% tests are mildly liberal (about 6-7%). Treat p-values near 0.05 with caution when a group has fewer than about 100 fish.
  • Negative control (data from a Gompertz curve, fitted as VB, 300 reps): 4/12 FAIL (K -21% with coverage 0.10 and -15% with coverage 0.73; K difference +13%). The gate can fail.

Usability: fit on example/config.yaml reproduces the stored estimates exactly. Every number in the report's interpretation was checked against the estimates table (Linf 680/559, 90% Linf 612/503 mm at 6.4/4.8 yr, Linf difference 121 mm (99-143), K difference 0.078, condition +1.8% (+0.7 to +2.9%), mean weight 1223 g, Wr 88.8/90.3); wording is correct and non-causal. Figures (growth curves with confidence and prediction bands drawn over observed ages, parameter comparison, condition) were viewed and agree with the table. Messy data (dev/fisheries-growth/verify/v2/messy/): NA, blank and "4+" ages; misspelled/case/space group values ("Femal", "Female", " male"); weights in kg; a renamed length column; a misspelled filter column; "512mm" and negative lengths all gave clear messages that name fish ids and suggest fixes. One cosmetic issue: a negative length also prints an R warning "NaNs produced" from the weight-outlier check.

Changes made: none to module code (no blocking problems found).

Verdict: VERIFIED_WITH_LIMITS (agrees with verifier 1; limits as listed above, plus mild liberalness of the group-difference tests with small groups).

Appendix: full recovery tables
default_nls: nsim 500, fit errors 0, converged 500 (nls, no R-hat), coverage bound 0.921
estimandtruthmean estrel. bias %biasbias MCSEcoveragepowergate
age_at_90pct_Linf_A7.187.220.60.04310.02570.938-PASS
age_at_90pct_Linf_B8.718.740.30.02750.02830.952-PASS
age_at_90pct_Linf_C7.187.190.20.01680.02480.944-PASS
condition_ratio_B_vs_A1.051.050.02.67e-044.6e-040.9320.996PASS
condition_ratio_C_vs_A110.10.001074.38e-040.9400.060PASS
K_A0.30.30.13.28e-040.001130.936-PASS
K_B0.250.2510.37.12e-048.49e-040.962-PASS
K_C0.30.3010.50.001380.00110.944-PASS
K_diff_B_vs_A-0.05-0.04960.83.84e-040.001450.9500.356PASS
K_diff_C_vs_A00.00105-0.001050.00160.9280.072PASS
Linf_A6006010.21.360.8370.928-PASS
Linf_B6506510.21.030.9140.968-PASS
Linf_C6006000.10.4980.7870.932-PASS
Linf_diff_B_vs_A5049.7-0.7-0.3321.260.9500.430PASS
Linf_diff_C_vs_A0-0.861--0.8611.190.9360.064PASS
lw_a3.5e-063.51e-060.26.67e-099.17e-090.962-PASS
lw_b3.183.18-0.0-1.72e-044.45e-040.960-PASS
lw_residual_sd0.080.0799-0.1-5.79e-051.19e-040.958-PASS
mean_weight_at_500_A13401340-0.1-1.130.460.952-PASS
residual_sd0.080.0799-0.1-5.57e-051.22e-040.942-PASS
t0_A-0.5-0.503-0.6-0.002810.003870.950-PASS
t0_B-0.5-0.4990.20.001120.003350.962-PASS
t0_C-0.5-0.4990.20.001190.003820.954-PASS
t0_diff_B_vs_A00.00394-0.003940.005270.9460.054PASS
t0_diff_C_vs_A00.004-0.0040.00550.9440.056PASS
additive_nls: nsim 500, fit errors 0, converged 500 (nls, no R-hat), coverage bound 0.921
estimandtruthmean estrel. bias %biasbias MCSEcoveragepowergate
age_at_90pct_Linf_A7.187.210.40.03040.02150.954-PASS
age_at_90pct_Linf_B8.718.720.20.0140.02390.954-PASS
age_at_90pct_Linf_C7.187.180.10.008110.02080.948-PASS
condition_ratio_B_vs_A1.051.050.02.7e-044.6e-040.9320.996PASS
condition_ratio_C_vs_A110.10.001074.38e-040.9400.060PASS
K_A0.30.30.13e-049.84e-040.942-PASS
K_B0.250.2510.37.78e-047.51e-040.956-PASS
K_C0.30.3010.40.001289.6e-040.944-PASS
K_diff_B_vs_A-0.05-0.04951.04.78e-040.001260.9400.444PASS
K_diff_C_vs_A09.76e-04-9.76e-040.001390.9420.058PASS
Linf_A6006010.10.8250.6170.928-PASS
Linf_B6506500.10.4850.6630.966-PASS
Linf_C6006000.00.180.5810.934-PASS
Linf_diff_B_vs_A5049.7-0.7-0.3390.9230.9440.678PASS
Linf_diff_C_vs_A0-0.645--0.6450.8690.9300.070PASS
lw_a3.5e-063.51e-060.26.59e-098.84e-090.964-PASS
lw_b3.183.18-0.0-1.89e-044.29e-040.964-PASS
lw_residual_sd0.080.0799-0.1-5.45e-051.19e-040.958-PASS
mean_weight_at_500_A13401340-0.1-1.150.4560.950-PASS
residual_sd3030-0.1-0.02170.04580.942-PASS
t0_A-0.5-0.503-0.6-0.003160.004440.946-PASS
t0_B-0.5-0.4980.30.001710.004150.946-PASS
t0_C-0.5-0.4980.30.001670.004370.938-PASS
t0_diff_B_vs_A00.00487-0.004870.006050.9540.046PASS
t0_diff_C_vs_A00.00483-0.004830.006240.9500.050PASS
default_nimble_fixed: nsim 100, fit errors 0, converged 100 (R-hat < 1.05 share 1.00), coverage bound 0.885
estimandtruthmean estrel. bias %biasbias MCSEcoveragepowergate
age_at_90pct_Linf_A7.187.291.60.1180.05030.960-PASS
age_at_90pct_Linf_B8.718.922.40.2060.06970.930-PASS
age_at_90pct_Linf_C7.187.271.30.09530.05590.940-PASS
condition_ratio_B_vs_A1.051.050.10.00110.001050.9501.000PASS
condition_ratio_C_vs_A110.10.001029.61e-040.9500.050PASS
K_A0.30.299-0.5-0.001390.002110.970-PASS
K_B0.250.247-1.0-0.002570.001980.940-PASS
K_C0.30.3-0.0-7.77e-050.002420.930-PASS
K_diff_B_vs_A-0.05-0.0512-2.3-0.001170.002890.9700.390PASS
K_diff_C_vs_A00.00132-0.001320.003490.9500.050PASS
Linf_A6006040.63.61.670.960-PASS
Linf_B6506561.06.362.230.940-PASS
Linf_C6006020.42.371.640.960-PASS
Linf_diff_B_vs_A5052.85.52.762.670.9400.450PASS
Linf_diff_C_vs_A0-1.23--1.232.510.9400.060PASS
lw_a3.5e-063.52e-060.51.59e-081.95e-080.980-PASS
lw_b3.183.18-0.0-6.21e-049.2e-040.970-PASS
lw_residual_sd0.080.08020.32.13e-042.7e-040.940-PASS
mean_weight_at_500_A13401340-0.1-1.080.9390.950-PASS
residual_sd0.080.08050.65.01e-042.54e-040.930-PASS
t0_A-0.5-0.511-2.1-0.01060.007520.970-PASS
t0_B-0.5-0.514-2.8-0.01420.008180.960-PASS
t0_C-0.5-0.508-1.7-0.008290.009020.920-PASS
t0_diff_B_vs_A0-0.00365--0.003650.01150.9300.070PASS
t0_diff_C_vs_A00.00231-0.002310.01260.9500.050PASS
hard_nls: nsim 500, fit errors 6, converged 494 (nls, no R-hat), coverage bound 0.921
estimandtruthmean estrel. bias %biasbias MCSEcoveragepowergate
age_at_90pct_Linf_A7.1854.8664.047.646.60.907-FAIL
age_at_90pct_Linf_B8.7158.7573.95046.60.909-FAIL
age_at_90pct_Linf_C7.182042748.919793.30.923-PASS
condition_ratio_B_vs_A1.051.05-0.1-8.26e-046.95e-040.9550.895PASS
condition_ratio_C_vs_A110.02.48e-046.52e-040.9570.043PASS
K_A0.30.3082.60.007890.005020.941-PASS
K_B0.250.2583.40.008490.004320.957-PASS
K_C0.30.3-0.0-2.54e-050.005320.972-PASS
K_diff_B_vs_A-0.05-0.04941.26.03e-040.006690.9550.049PASS
K_diff_C_vs_A0-0.00791--0.007910.007350.9510.049PASS
Linf_A6002380297.0178017400.925-PASS
Linf_B6502470280.1182016900.945-PASS
Linf_C60086301338.0803038200.955-PASS
Linf_diff_B_vs_A5088.677.138.624300.9820.016PASS
Linf_diff_C_vs_A06250-625042100.9840.016PASS
lw_a3.5e-063.54e-061.13.95e-081.94e-080.951-PASS
lw_b3.183.18-0.0-7.07e-049.88e-040.953-PASS
lw_residual_sd0.080.08010.21.29e-041.86e-040.951-PASS
mean_weight_at_500_A13401340-0.0-0.6651.040.960-PASS
residual_sd0.080.08020.22e-042.02e-040.955-PASS
t0_A-0.5-0.52-4.0-0.020.01070.949-PASS
t0_B-0.5-0.509-1.9-0.009390.010.947-PASS
t0_C-0.5-0.541-8.1-0.04070.01190.953-PASS
t0_diff_B_vs_A00.0106-0.01060.01440.9680.032PASS
t0_diff_C_vs_A0-0.0207--0.02070.0160.9600.040PASS
misspec_selectivity: nsim 500, fit errors 0, converged 500 (nls, no R-hat), coverage bound 0.921
estimandtruthmean estrel. bias %biasbias MCSEcoveragepowergate
age_at_90pct_Linf_A7.188.1713.80.990.03640.898-FAIL
age_at_90pct_Linf_B8.719.064.00.3520.04670.956-PASS
age_at_90pct_Linf_C7.188.2615.11.080.03920.862-FAIL
condition_ratio_B_vs_A1.051.050.01.41e-044.27e-040.9561.000PASS
condition_ratio_C_vs_A11-0.0-4.48e-044.17e-040.9500.050PASS
K_A0.30.259-13.8-0.04140.001350.768-FAIL
K_B0.250.243-3.0-0.007410.001370.934-PASS
K_C0.30.255-14.9-0.04470.001370.676-FAIL
K_diff_B_vs_A-0.05-0.01668.00.0340.001980.8680.068FAIL
K_diff_C_vs_A0-0.00326--0.003260.002040.9380.062PASS
Linf_A6006193.219.30.8960.912-FAIL
Linf_B6506581.27.71.170.954-PASS
Linf_C6006213.5210.9610.892-FAIL
Linf_diff_B_vs_A5038.4-23.1-11.61.490.9320.198FAIL
Linf_diff_C_vs_A01.73-1.731.390.9560.044PASS
lw_a3.5e-063.51e-060.27.22e-091.5e-080.948-PASS
lw_b3.183.180.04.41e-047.15e-040.946-PASS
lw_residual_sd0.080.0798-0.3-2.05e-041.23e-040.946-PASS
mean_weight_at_500_A134013400.00.2990.4350.952-PASS
residual_sd0.080.08040.53.97e-041.22e-040.950-PASS
t0_A-0.5-0.856-71.2-0.3560.010.624-FAIL
t0_B-0.5-0.585-17.0-0.08510.01010.924-FAIL
t0_C-0.5-0.886-77.3-0.3860.01020.576-FAIL
t0_diff_B_vs_A00.271-0.2710.01430.8280.172FAIL
t0_diff_C_vs_A0-0.0302--0.03020.0150.9380.062PASS
null_nls: nsim 500, fit errors 0, converged 500 (nls, no R-hat), coverage bound 0.921
estimandtruthmean estrel. bias %biasbias MCSEcoveragepowergate
age_at_90pct_Linf_A7.187.220.60.04310.02570.938-PASS
age_at_90pct_Linf_B7.187.20.30.01990.02250.954-PASS
age_at_90pct_Linf_C7.187.190.20.01680.02480.944-PASS
condition_ratio_B_vs_A110.02.58e-044.38e-040.9300.070PASS
condition_ratio_C_vs_A110.10.001064.38e-040.9400.060PASS
K_A0.30.30.13.28e-040.001130.936-PASS
K_B0.30.3010.39.25e-040.0010.962-PASS
K_C0.30.3010.50.001380.00110.944-PASS
K_diff_B_vs_A05.97e-04-5.97e-040.001560.9480.052PASS
K_diff_C_vs_A00.00105-0.001050.00160.9280.072PASS
Linf_A6006010.21.360.8370.928-PASS
Linf_B6006010.10.7510.7150.966-PASS
Linf_C6006000.10.4980.7870.932-PASS
Linf_diff_B_vs_A0-0.607--0.6071.110.9400.060PASS
Linf_diff_C_vs_A0-0.861--0.8611.190.9360.064PASS
lw_a3.5e-063.51e-060.27.43e-099.49e-090.962-PASS
lw_b3.183.18-0.0-1.88e-044.6e-040.964-PASS
lw_residual_sd0.080.0799-0.1-5.82e-051.19e-040.958-PASS
mean_weight_at_500_A13401340-0.1-1.140.4630.954-PASS
residual_sd0.080.0799-0.1-5.6e-051.22e-040.944-PASS
t0_A-0.5-0.503-0.6-0.002810.003870.950-PASS
t0_B-0.5-0.4990.20.00110.00360.956-PASS
t0_C-0.5-0.4990.20.001190.003820.954-PASS
t0_diff_B_vs_A00.00391-0.003910.005440.9480.052PASS
t0_diff_C_vs_A00.004-0.0040.00550.9440.056PASS
hier_nimble: nsim 100, fit errors 0, converged 96 (R-hat < 1.05 share 0.96), coverage bound 0.883
estimandtruthmean estrel. bias %biasbias MCSEcoveragepowergate
age_at_90pct_Linf_A7.47.491.50.08990.03890.958-PASS
age_at_90pct_Linf_B7.167.231.10.0670.04080.938-PASS
age_at_90pct_Linf_C7.377.451.40.08330.04430.948-PASS
age_at_90pct_Linf_D7.57.530.60.03150.04210.948-PASS
age_at_90pct_Linf_E7.257.311.00.06230.04210.938-PASS
age_at_90pct_Linf_F7.27.31.70.1060.04430.927-PASS
age_at_90pct_Linf_G7.417.491.20.07350.04630.917-PASS
age_at_90pct_Linf_H7.227.31.20.08110.03850.948-PASS
age_at_90pct_Linf_I7.237.270.60.03830.0410.979-PASS
age_at_90pct_Linf_J7.177.230.90.05790.0440.969-PASS
condition_ratio_B_vs_A110.10.001220.001360.9480.052PASS
condition_ratio_C_vs_A110.18.63e-040.001070.9790.021PASS
condition_ratio_D_vs_A110.17.3e-040.001280.969-PASS
condition_ratio_E_vs_A110.20.001570.001290.948-PASS
condition_ratio_F_vs_A110.10.001460.001150.979-PASS
condition_ratio_G_vs_A110.30.00270.001260.990-PASS
condition_ratio_H_vs_A110.01.15e-050.001330.938-PASS
condition_ratio_I_vs_A110.20.002380.001290.927-PASS
condition_ratio_J_vs_A110.20.001710.001170.979-PASS
K_A0.3010.298-0.9-0.003450.00170.958-PASS
K_B0.3080.306-0.5-0.002280.001720.938-PASS
K_beta_temperature0.10.0949-5.1-0.005130.005020.9790.354PASS
K_C0.3030.3-0.8-0.003040.001610.948-PASS
K_D0.2970.297-0.0-5.97e-040.001650.948-PASS
K_diff_B_vs_A0.006760.0079217.00.001160.002060.9480.656PASS
K_diff_C_vs_A0.001520.00192-10.24.05e-040.001930.9480.729PASS
K_diff_D_vs_A-0.00417-0.00132-2.30.002850.00210.896-PASS
K_diff_E_vs_A0.006350.00831164.20.001960.00210.938-PASS
K_diff_F_vs_A0.007460.00705-20.9-4.15e-040.001890.938-PASS
K_diff_G_vs_A-0.00148-5.52e-04-2.39.26e-040.002070.917-PASS
K_diff_H_vs_A0.004610.00569-39.70.001080.002060.938-PASS
K_diff_I_vs_A0.005980.0087638.10.002790.001860.969-PASS
K_diff_J_vs_A0.007160.00909-11.00.001940.002150.906-PASS
K_E0.3080.306-0.4-0.001480.001680.938-PASS
K_F0.3090.305-1.1-0.003860.001760.927-PASS
K_G0.30.297-0.6-0.002520.001760.927-PASS
K_H0.3060.304-0.7-0.002370.001580.948-PASS
K_I0.3070.307-0.1-6.56e-040.001430.990-PASS
K_J0.3080.307-0.4-0.001510.001570.969-PASS
K_typical0.30.299-0.4-0.001250.001750.990-PASS
Linf_A5986010.73.611.630.958-PASS
Linf_B6046070.42.611.720.948-PASS
Linf_beta_temperature-0.05-0.0502-0.4-2.21e-040.002910.9690.281PASS
Linf_C5955970.62.761.740.979-PASS
Linf_D6006010.21.061.660.938-PASS
Linf_diff_B_vs_A6.825.8131.8-12.280.9900.583PASS
Linf_diff_C_vs_A-2.93-3.78-2.8-0.8522.30.9580.479PASS
Linf_diff_D_vs_A2.670.12-6.9-2.552.410.969-PASS
Linf_diff_E_vs_A9.387.16-66.0-2.222.360.958-PASS
Linf_diff_F_vs_A0.6380.958-23.60.322.190.979-PASS
Linf_diff_G_vs_A5.133.58-7.9-1.542.490.948-PASS
Linf_diff_H_vs_A1.10.4147.4-0.6872.220.948-PASS
Linf_diff_I_vs_A4.191.19-10.7-32.210.958-PASS
Linf_diff_J_vs_A-2.64-4.48-2.9-1.842.550.927-PASS
Linf_E6076080.31.391.780.938-PASS
Linf_F5986020.73.931.880.927-PASS
Linf_G6036050.42.071.910.948-PASS
Linf_H5996020.52.921.670.948-PASS
Linf_I6026020.20.6071.70.969-PASS
Linf_J5955970.31.771.780.938-PASS
Linf_typical6006000.00.2451.970.948-PASS
lw_a3.5e-063.49e-06-0.3-9.4e-091.68e-080.958-PASS
lw_b3.183.180.04.55e-048.02e-040.958-PASS
lw_residual_sd0.080.08010.11.03e-042.07e-040.938-PASS
mean_weight_at_500_A13401340-0.1-1.211.140.969-PASS
residual_sd0.080.08020.21.98e-041.97e-040.938-PASS
sd_log_K0.150.166.40.009640.004260.969-PASS
sd_log_Linf0.080.08556.80.005470.002370.969-PASS
t0-0.5-0.507-1.4-0.007020.003750.979-PASS
null_hier: nsim 100, fit errors 0, converged 98 (R-hat < 1.05 share 0.98), coverage bound 0.884
estimandtruthmean estrel. bias %biasbias MCSEcoveragepowergate
age_at_90pct_Linf_A7.297.391.40.09030.03960.959-PASS
age_at_90pct_Linf_B7.197.230.90.04360.04240.908-PASS
age_at_90pct_Linf_C7.347.421.30.08410.04230.949-PASS
age_at_90pct_Linf_D7.417.450.50.0320.0420.939-PASS
age_at_90pct_Linf_E7.197.240.90.05950.04140.939-PASS
age_at_90pct_Linf_F7.197.31.60.1040.04410.929-PASS
age_at_90pct_Linf_G7.377.441.20.07250.04570.929-PASS
age_at_90pct_Linf_H7.247.311.00.06690.03940.949-PASS
age_at_90pct_Linf_I7.27.230.50.03290.03930.980-PASS
age_at_90pct_Linf_J7.257.290.80.0450.04020.959-PASS
condition_ratio_B_vs_A110.10.001170.001330.9490.051PASS
condition_ratio_C_vs_A110.18.43e-040.001050.9800.020PASS
condition_ratio_D_vs_A110.17.94e-040.001250.969-PASS
condition_ratio_E_vs_A110.10.001440.001270.949-PASS
condition_ratio_F_vs_A110.10.001490.001130.980-PASS
condition_ratio_G_vs_A110.30.002820.001240.990-PASS
condition_ratio_H_vs_A11-0.0-1.41e-040.001310.939-PASS
condition_ratio_I_vs_A110.20.002270.001270.929-PASS
condition_ratio_J_vs_A110.20.001920.001160.980-PASS
K_A0.3020.299-0.8-0.002960.001660.969-PASS
K_B0.3050.303-0.3-0.001550.001760.918-PASS
K_beta_temperature0-0.00496--0.004960.004940.9690.031PASS
K_C0.30.298-0.7-0.002360.001610.959-PASS
K_D0.2970.2970.0-2.96e-040.001630.929-PASS
K_diff_B_vs_A0.003380.0047974.20.001410.002020.9390.531PASS
K_diff_C_vs_A-0.00119-5.9e-04-30.46e-040.001860.9390.612PASS
K_diff_D_vs_A-0.00419-0.0015211.90.002670.002050.888-PASS
K_diff_E_vs_A0.005520.007143.30.001580.002050.929-PASS
K_diff_F_vs_A0.005130.00481-0.0-3.21e-040.001840.949-PASS
K_diff_G_vs_A-0.00236-0.0015431.18.2e-040.0020.918-PASS
K_diff_H_vs_A0.001380.00262146.20.001240.001960.939-PASS
K_diff_I_vs_A0.00390.0065234.90.002620.001770.969-PASS
K_diff_J_vs_A0.002080.00378.20.001620.002050.939-PASS
K_E0.3070.306-0.4-0.001390.001620.929-PASS
K_F0.3070.303-0.9-0.003280.001760.929-PASS
K_G0.2990.297-0.5-0.002140.001760.929-PASS
K_H0.3030.301-0.5-0.001720.001570.939-PASS
K_I0.3050.305-0.0-3.38e-040.001450.980-PASS
K_J0.3040.302-0.3-0.001340.001530.980-PASS
K_typical0.30.299-0.4-0.001270.001750.990-PASS
Linf_A5955980.73.681.680.959-PASS
Linf_B6066080.31.681.780.939-PASS
Linf_beta_temperature05.04e-04-5.04e-040.002860.9590.041PASS
Linf_C5945970.52.921.660.969-PASS
Linf_D5985990.21.151.660.939-PASS
Linf_diff_B_vs_A11.29.174.9-22.310.9800.541PASS
Linf_diff_C_vs_A-1.03-1.798.0-0.7562.230.9490.469PASS
Linf_diff_D_vs_A3.230.696-12.2-2.532.370.949-PASS
Linf_diff_E_vs_A8.966.67-300.9-2.292.370.959-PASS
Linf_diff_F_vs_A3.313.5530.00.2442.110.969-PASS
Linf_diff_G_vs_A6.364.71418.8-1.662.460.939-PASS
Linf_diff_H_vs_A4.713.5-36.5-1.222.230.949-PASS
Linf_diff_I_vs_A5.021.81-21.1-3.222.170.969-PASS
Linf_diff_J_vs_A2.670.553806.9-2.122.490.939-PASS
Linf_E6046050.31.391.770.939-PASS
Linf_F5986020.73.921.880.929-PASS
Linf_G6016030.42.021.870.939-PASS
Linf_H6006020.52.461.650.949-PASS
Linf_I6006000.10.4621.630.959-PASS
Linf_J5975990.31.561.620.949-PASS
Linf_typical6006000.00.1021.960.939-PASS
lw_a3.5e-063.49e-06-0.2-8.21e-091.65e-080.959-PASS
lw_b3.183.180.03.96e-047.89e-040.949-PASS
lw_residual_sd0.080.08010.18.62e-052.03e-040.939-PASS
mean_weight_at_500_A13401340-0.1-1.241.120.969-PASS
residual_sd0.080.08020.21.79e-041.94e-040.939-PASS
sd_log_K0.150.1585.60.008340.004270.959-PASS
sd_log_Linf0.080.08445.50.00440.002440.939-PASS
t0-0.5-0.506-1.2-0.006140.003770.990-PASS
hard_nimble_fixed: nsim 100, fit errors 0, converged 97 (R-hat < 1.05 share 0.97), coverage bound 0.884
estimandtruthmean estrel. bias %biasbias MCSEcoveragepowergate
age_at_90pct_Linf_A7.1810.545.73.280.4820.938-FAIL
age_at_90pct_Linf_B8.7111.633.02.870.4770.959-FAIL
age_at_90pct_Linf_C7.1811.458.94.230.5150.928-FAIL
condition_ratio_B_vs_A1.051.050.04.13e-040.001530.9480.907PASS
condition_ratio_C_vs_A11-0.0-3.06e-040.001570.9590.041PASS
K_A0.30.284-5.2-0.01550.009390.948-PASS
K_B0.250.2520.80.002080.007790.959-PASS
K_C0.30.262-12.5-0.03760.008550.938-FAIL
K_diff_B_vs_A-0.05-0.032435.20.01760.01220.9480.072PASS
K_diff_C_vs_A0-0.0221--0.02210.01310.9590.041PASS
Linf_A60072220.312218.70.948-FAIL
Linf_B65075616.3106180.969-FAIL
Linf_C60075726.2157200.918-FAIL
Linf_diff_B_vs_A5034.2-31.6-15.826.60.9480.062PASS
Linf_diff_C_vs_A035.1-35.128.30.9480.052PASS
lw_a3.5e-063.53e-061.03.33e-084.4e-080.948-PASS
lw_b3.183.18-0.0-3.04e-040.002260.959-PASS
lw_residual_sd0.080.08060.86.48e-044.85e-040.918-PASS
mean_weight_at_500_A134013400.00.112.240.979-PASS
residual_sd0.080.08151.80.001454.42e-040.948-PASS
t0_A-0.5-0.607-21.3-0.1070.02210.959-FAIL
t0_B-0.5-0.549-9.8-0.04890.01940.948-PASS
t0_C-0.5-0.653-30.6-0.1530.02180.938-FAIL
t0_diff_B_vs_A00.0576-0.05760.02920.9480.052PASS
t0_diff_C_vs_A0-0.0463--0.04630.03190.9480.052PASS
hard_hier: nsim 100, fit errors 0, converged 97 (R-hat < 1.05 share 0.97), coverage bound 0.884
estimandtruthmean estrel. bias %biasbias MCSEcoveragepowergate
age_at_90pct_Linf_A7.497.61.70.1080.1030.969-PASS
age_at_90pct_Linf_B7.267.473.30.2180.1020.969-PASS
age_at_90pct_Linf_C7.227.372.70.1530.09660.948-PASS
age_at_90pct_Linf_D7.387.613.00.2290.1060.948-PASS
age_at_90pct_Linf_E7.47.674.10.2760.1080.938-PASS
age_at_90pct_Linf_F7.357.472.20.1220.0960.969-PASS
condition_ratio_B_vs_A10.998-0.2-0.002330.001640.9790.021PASS
condition_ratio_C_vs_A110.19.64e-040.001710.9690.031PASS
condition_ratio_D_vs_A10.999-0.1-0.001460.001940.948-PASS
condition_ratio_E_vs_A10.998-0.2-0.002410.001970.907-PASS
condition_ratio_F_vs_A10.998-0.2-0.002310.001710.938-PASS
K_A0.2990.3031.60.004090.003830.969-PASS
K_B0.3040.3040.2-3.74e-040.003830.969-PASS
K_beta_temperature0.10.094-6.0-0.006040.007880.9480.134PASS
K_C0.3070.3070.55.01e-050.00360.948-PASS
K_D0.30.3010.30.00120.003630.938-PASS
K_diff_B_vs_A0.005045.79e-0438.4-0.004470.003190.9480.299PASS
K_diff_C_vs_A0.007830.00379198.8-0.004040.003050.9790.433PASS
K_diff_D_vs_A8.56e-04-0.00204-21.0-0.002890.002870.969-PASS
K_diff_E_vs_A9.65e-04-0.00607-25.2-0.007040.002990.969-PASS
K_diff_F_vs_A0.00285-1.31e-05-156.1-0.002870.003190.959-PASS
K_E0.30.297-0.6-0.002950.003780.948-PASS
K_F0.3020.3030.90.001220.003820.969-PASS
K_typical0.30.3-0.1-3.19e-040.003710.969-PASS
Linf_A6016040.73.354.040.979-PASS
Linf_B6056131.58.814.40.979-PASS
Linf_beta_temperature-0.05-0.0504-0.8-3.88e-040.004530.9790.041PASS
Linf_C6036081.15.83.970.969-PASS
Linf_D6096161.26.794.210.969-PASS
Linf_diff_B_vs_A3.949.426.95.464.740.9690.330PASS
Linf_diff_C_vs_A1.84.259.42.454.470.9690.320PASS
Linf_diff_D_vs_A8.6312.148.53.443.870.990-PASS
Linf_diff_E_vs_A-1.466.74-183.08.214.490.959-PASS
Linf_diff_F_vs_A22.86107.50.8624.10.990-PASS
Linf_E5996112.111.64.430.938-PASS
Linf_F6036070.94.214.270.948-PASS
Linf_typical6006081.48.23.490.969-PASS
lw_a3.5e-063.58e-062.27.6e-083.79e-080.887-PASS
lw_b3.183.18-0.1-0.002490.001890.907-PASS
lw_residual_sd0.080.08020.21.69e-043.48e-040.969-PASS
mean_weight_at_500_A134013400.10.912.250.918-PASS
residual_sd0.080.08080.97.57e-044.07e-040.948-PASS
sd_log_K0.150.1585.70.008490.007550.959-PASS
sd_log_Linf0.080.099624.50.01960.004640.948-FAIL
t0-0.5-0.505-1.1-0.00540.008550.959-PASS

Engine, references and full details

Engine: stats::nls with FSA starting values + Kimura (1980) likelihood-ratio tests (default), or nimble (Bayesian, fixed or hierarchical random group effects, covariates on log Linf and log K); stats::lm for length-weight.

References

  • von Bertalanffy L (1938) A quantitative theory of organic growth. Human Biology 10:181-213.
  • Beverton RJH, Holt SJ (1957) On the Dynamics of Exploited Fish Populations. Fishery Investigations Series II 19. HMSO, London.
  • Kimura DK (1980) Likelihood methods for the von Bertalanffy growth curve. Fishery Bulletin 77:765-776.
  • Helser TE, Lai HL (2004) A Bayesian hierarchical meta-analysis of fish growth: with an example for North American largemouth bass, Micropterus salmoides. Ecological Modelling 178:399-416.
  • Ogle DH (2016) Introductory Fisheries Analyses with R. Chapman & Hall/CRC.
  • Ogle DH, Doll JC, Wheeler AP, Dinno A. FSA: Simple Fisheries Stock Assessment Methods. R package 0.10.1.
  • Nelson GA. fishmethods: Fishery Science Methods and Models. R package (growthlrt, used for the cross-check).
  • Froese R (2006) Cube law, condition factor and weight-length relationships: history, meta-analysis and recommendations. Journal of Applied Ichthyology 22:241-253.
  • Sprugel DG (1983) Correcting for bias in log-transformed allometric equations. Ecology 64:209-210.
  • Le Cren ED (1951) The length-weight relationship and seasonal cycle in gonad weight and condition in the perch (Perca fluviatilis). Journal of Animal Ecology 20:201-219.
  • Wege GJ, Anderson RO (1978) Relative weight (Wr): a new index of condition for largemouth bass. Pages 79-91 in New Approaches to the Management of Small Impoundments. AFS North Central Division Special Publication 5.
  • Murphy BR, Brown ML, Springer TA (1990) Evaluation of the relative weight (Wr) index, with new applications to walleye. North American Journal of Fisheries Management 10:85-97.
  • de Valpine P et al. (2017) Programming with models: writing statistical algorithms for general model structures with NIMBLE. Journal of Computational and Graphical Statistics 26:403-413.
  • Pinheiro JC, Bates DM (2000) Mixed-Effects Models in S and S-PLUS. Springer (nlme, used for the cross-check).

CascadiaTools is made by Taal Levi, Cascadia Wildlife Lab, Oregon State University. Source code on GitHub · How to cite

Based at
Oregon State University