Skip to contents

sbt 3.0.0

  • sbt_fit_save() and sbt_fit_read() can now externalise large portable fits to checksum-verified GitHub Release assets when a project opts in through a .sbt-artifacts.dcf configuration. Ordinary local RDS behaviour is unchanged elsewhere.

  • Renamed sbt_simulate_example() to sbt_example_simulate() so the public simulated-example functions share the sbt_example_*() prefix.

  • Removed get_mcmc_profile(). Posterior likelihood profiling will be maintained outside sbt.

  • Added a fully simulated, full-scale assessment example for public articles and tests. sbt_example_data() returns the processed model inputs, and sbt_example_fit() returns the corresponding 1,559-parameter MLE. The new example files contain only simulated observations and fitted output.

  • get_loo() now takes a single sampled sbt_fit and obtains its model data, transient objective, and posterior draws internally.

  • Added sbt_add_msy() and a validated msy slot to portable sbt_fit objects. Named MLE or posterior calculations are bound to the fitted-model and posterior identities, survive save/read round trips, and are cleared when their upstream fit or posterior changes. The lower-level run_msy() interface remains available for derived posteriors assembled from multiple fits.

  • The current model now reports legacy comparable biomass (B10_plus_y) as season-1 numbers aged 10 and older weighted by the LL1 weight-at-age schedule, together with its draw-wise ratio to the initial model year.

  • Standardised plural observation-family plot names: plot_tags_residuals(), plot_tags_fits_pooled(), plot_tags_recaptures_pooled(), plot_pops_residuals(), and plot_hsps_residuals(). The former singular aliases have been removed.

  • Added pooled conventional-tag diagnostics for assessment reporting. plot_tags_fits_pooled() compares observed and expected recaptures by release year, release age, and recapture age after pooling tagger groups, with 95% posterior credible intervals calculated after pooling within each retained draw; plot_tags_recaptures_pooled() summarises total observed recaptures at age for each calendar release year.

  • Future gene-tagging release and harvest sample sizes in run_projections() can now be supplied as release-year-specific vectors as well as scalars. This supports programme comparisons that preserve known cohorts already in the release/recapture pipeline before future effort scenarios diverge.

  • Long-horizon closed-loop CTP projections now stage catch only through the TAC blocks already calculated. Catch in not-yet-calculated CTP years is held at zero while generating the observations available to the current update, then every final year is populated by its calculated CTP TAC. This avoids an infeasible artificial continuation of the last fixed TAC across the entire horizon.

  • run_projections() now has an explicit, opt-in catch-realisation rule for operating-model paths where nominal CTP advice cannot physically be taken. The default "error" behavior remains fail-closed. With "cap", all seasonal and fishery allocations in an affected draw-year are reduced by one common factor until the configured raw-harvest ceiling is met. Nominal TAC advice, realized nominal catch, and biological removals are retained separately and revalidated when projection caches are read.

  • Aligned the projection CTP with the adopted specification and made its end-to-end calculation independently auditable. The gene-tagging upper threshold is now 2.6 million fish, projection CKMR fits use the assessment-data HSP detection/retention multiplier, and terminal-age HSP survival applies exactly one term per cohort-gap year. CKMR input cutoffs now apply symmetrically to historical and projected rows, and projection caches retain checksum-bound snapshots of the complete raw simulated GT, CPUE, HSP, and POP observation arrays available at every update. This lets an external implementation apply the calendar lags independently and compare the CKMR, multiplier, unconstrained-TAC, final-TAC, and allocation calculations. The CTP CKMR optimizer now requires a maximum absolute gradient no larger than 5e-4, rather than accepting materially less-refined fits below 0.01; a tightly controlled nlminb() polish with recorded deterministic numerical nudges is used when BFGS stalls above that gate, followed by a final code-zero BFGS verification pass. A safeguarded exact-Hessian Newton proposal and final BFGS certification are available only if that route remains marginally above the gate; the proposal normalizes RTMB’s vector, row-matrix, and column-matrix gradient representations before solving the Newton system.

  • Completed a pre-sign-off maintenance pass across the package, tests, articles, and website configuration. Several row-index loops now handle empty inputs safely, obsolete interactive scripts are archived outside the installed package, orphaned data-weighting optimizer caches are no longer bundled, public references and installation examples are current, and CI actions are updated. These maintenance changes do not alter the scientific model contract.

  • Restored autocorrelated CPUE observations in management projections. Each posterior draw now carries its final fitted historical log-CPUE residual forward with the empirical historical lag-one correlation and the legacy stationary AR(1) innovation scaling. Projected observations remain lognormal, and projection cache/signature versions were advanced so caches produced by the former independent-error implementation cannot be reused.

  • Projection caches now distinguish scientific validity from execution provenance. Exact package/dependency versions and implementation hashes are retained for audit but no longer invalidate a cache when the explicit projection scientific contract and all numerical inputs are unchanged. legacy_cache_signatures provides an opt-in, fully revalidated one-time migration for audited caches from the immediately preceding contract.

  • get_loo() now documents and tests its existing policy of retaining zero-effective-N composition rows in model I/O while excluding them from the pointwise LOO calculation. Fractional multinomial composition likelihoods now use one gamma-function implementation in both AD fitting and numeric reporting, so pointwise LOO values match the fitted objective. plot_loo() can include or exclude named components, uses data-type shapes in its legend, and omits non-finite Pareto shape diagnostics rather than assigning them an artificial finite plotting coordinate.

  • Added plot_prior_posterior() to compare every estimated parameter with an explicit scalar prior against its retained marginal posterior, on either model or natural scale. Explicitly selected posterior-only parameters can also be included.

  • Added the Francis LN1 logistic-normal likelihood for age compositions as af_switch = 4. It compresses only the leading and trailing tails, replaces exact internal zeros with a configurable epsilon, applies the Francis relative-sample-size weighting, and conditionally profiles one dispersion parameter per age-composition fishery. The Gaussian log-ratio kernel is evaluated with RTMB’s AD-safe multivariate-normal density. Length compositions are unchanged.

  • Standardized the portable-fit lifecycle API around the sbt_fit_* prefix: sbt_fit_validate(), sbt_fit_save(), sbt_fit_read(), sbt_fit_upgrade(), and sbt_fit_rebuild() replace the former verb-first names.

  • Active package, development-script, and vignette code now uses R’s native |> pipe. The minimum supported R version is therefore 4.1. Frozen version-1 scientific behaviour and archived historical source remain unchanged.

  • sbt_fit(data_in = ...) now runs the appropriate model-data preprocessor before creating the staged fit, so a current-model workflow can begin directly from the reviewable input list. Supplying already processed data remains fully supported.

  • sbt_fit schema 6 adds a staged fitted-model lifecycle. Start with sbt_fit(data), optionally configure it with the sbt_add_*() setters, and call sbt_optimise() and sbt_mcmc(); missing parameters, priors, map, and bounds are resolved automatically. Repeated optimization starts from RTMB’s authoritative last.par.best, while repeated MCMC retains every attempt and selects the diagnostically best compatible posterior. sbt_add_priors(fit) and sbt_add_map(fit) now resolve their package defaults directly, so callers only need to supply those arguments when overriding the defaults. Specialized external optimizers can attach their selected result with sbt_add_optimisation() without reverting to the completed-fit constructor.

  • Removed the dead catch_UR_on compatibility input. get_data() and get_data_v1() now reject it with an explanatory error. The old ADMB I42 robustness-trial multiplier was inactive in the previous base assessment and is separate from the active catch_UA UAM/NCNM additions and historical LL1/surface catch-scenario inputs.

  • Removed the non-operational get_posterior2() and legacy get_posterior3() extractors, the unsafe save_grid()/load_grid() serializers, the redundant plot_aerial_survey() wrapper, the unused get_POP_like_v2(), and the test-only .save_grid_mcmc_cell() helper. Use get_posterior(), portable sbt_fit grid cells, and plot_aerial(). The frozen V1 model now calls its own get_recruitment_v1() helper so future changes to current recruitment cannot alter the V1 benchmark.

  • Replaced the thin development article with CONTRIBUTING.md and removed the obsolete duplicate model specification. equations_rtmb.qmd is the sole active scientific specification.

  • MLE and MCMC checks can be deferred with check = FALSE and run later with check_mle() or check_mcmc(). Failed checks preserve expensive results for inspection. MCMC no longer requires an MLE, configuration changes invalidate downstream state, and re-optimizing a sampled fit preserves but clearly marks its earlier posterior. sbt_obj() is now the sole public runtime-object accessor.

  • Supported schema-3, schema-4, and schema-5 fits upgrade to schema 6 in memory. Migration is representation-only and never runs optimization or sampling; optimizer and posterior payloads are preserved.

  • sbt_fit schema 5 stores payload-bound MLE and MCMC validation records. check_mle() and check_mcmc() now reuse matching records by default, support an explicit full audit, and can explicitly skip checking. Records are invalidated by changed fits, posterior samples, sampler diagnostics, scientific contracts, thresholds, or stored diagnostic results.

  • Portable fits now declare an explicit scientific model contract separately from their exact implementation checksum. Strict reads still validate the payload, schema, parameter layout, and rebuilt objective, but source-only changes within an unchanged scientific contract no longer force a refit. Recognized pre-contract schema-2 fits are upgraded in memory.

  • Standardized comma spacing and explicit integer literals throughout the active R implementation. Continuous initial values retain explicit double storage, and the frozen version-1 model behaviour remains unchanged.

  • Updated the fixed additional aerial-survey standard-deviation default to 0.59 following ESC31 residual calibration. Conventional-tag randomized quantile residuals now use a reproducible seed by default while preserving the caller’s random-number state.

  • find_msy() now retries a failed final nlminb() step with bounded L-BFGS-B, avoiding isolated PORT false-convergence codes at otherwise stable posterior solutions.

  • find_msy() also restarts the catch-split optimization when its first phase identifies the near-zero-yield basin, even if the final penalized objective is marginally negative.

  • Added a model-data M_switch: direct mortality anchors (1) or length-based mortality (2). The length-based form estimates M10 and M30, fixes its length exponent at -1 by default, and applies the M10-scaled curve from age 0.

  • Reworked sbt_fit schema 3 as a lean portable checkpoint. The constructor now derives the fitted parameter state, map, and random effects from a required transient RTMB objective; it no longer stores the objective, report, optimizer start, duplicate fitted parameters, or duplicate parameter layouts. Strict reads validate compatibility and rebuild a fresh objective into a session-only cache used by plotting and reporting functions. sbt_obj() exposes that cached runtime object only when an external API requires it, with fresh = TRUE for isolated mutable work. make_parameter_table() now accepts an sbt_fit directly. Schema-1 and schema-2 files are migrated in memory and can be resaved in the compact format.

  • Added a configurable, smooth preventive harvest wall on raw combined seasonal harvest at age. Its four fixed model-data settings are validated together and are now fully defaulted by get_data() before model execution. The wall is normalized to the requested objective contribution for one cell at the retained biological ceiling. It is reported separately from the feasibility-only posfun continuation and does not alter conditioned catches or population dynamics. Omitting all four settings keeps the wall disabled for backward-compatible package data.

  • Restored the conditional legacy posfun() continuation and applied a fixed, age-specific feasibility penalty to combined ordinary and direct LL4 removals. Conditioned catches remain exact, raw harvest is retained for validation, and only a positive numerical continuation is propagated when a trial parameter vector is infeasible. New biological-state diagnostics require every accepted MLE and posterior draw to have raw seasonal harvest no greater than 0.9, strictly positive populated abundance, zero material harvest penalty, and exact catch accounting.

  • Simplified get_initial_numbers() with the equivalent cumulative-survival calculation and a numerically stable terminal plus-group denominator. This improves direct numeric-call overhead but does not materially change full model objective or gradient timing.

Enhancements

  • run_projections() can now keep nominal future TAC allocations separate from fishery-specific biological removals. The optional projection_removal_multiplier_f applies only after the terminal assessment year, CTP updates remain on the nominal-TAC scale, and projection caches validate both representations and their totals.
  • project_selectivity() now supports retaining fitted posterior selectivity through a specified terminal year before applying the selected projection rule to later years.
  • Added check_mle() and check_mcmc() for portable sbt_fit objects. They recalculate numerical and biological-state diagnostics, including natural mortality at every age, and store the checked results back in the fit.
  • run_msy() now evaluates the legacy annual equilibrium MSY calculation for every retained posterior iteration, chain, and fitted catch year in an sbt_fit. It preserves draw/year identity, supports draw and year subsets plus forked parallel execution, and returns compact scalar diagnostics with separate fishery- and age-specific arrays. Each row also retains the fitted spawning output, age-2–15 fishing mortality, unfished spawning output, and the corresponding Bmsy/B0, B/Bmsy, and F/Fmsy status ratios from the already evaluated posterior state.
  • Fitted-output and residual plot_*() functions now take an sbt_fit object as their primary input. Posterior-capable plots use the fit’s stored MCMC by default; set posterior = FALSE to request point estimates only. Legacy (data, object) calls remain supported.
  • Derived-data plots for growth, weight, sliced age frequencies, and PALY now also accept sbt_fit as their primary input while retaining a named legacy data alias.
  • run_projections(cache_only = TRUE) now loads only a signature-compatible cache and otherwise stops before simulation, allowing report renders to be fail-closed by default.
  • run_grid_mcmc() now writes portable grid<N>.sbt.rds cell fits, supports explicit cell subsets and validated resume, and updates aggregate diagnostics after every skipped or completed cell. Resume identities include bounds and sampler-global implementations. Canonical grid posterior conversion rejects mixed model, input, or run signatures; legacy .rda cells remain readable with an explicit unverifiable-provenance warning.
  • Model data can set cpue_sel_fishery to choose the selectivity used by the CPUE abundance index. It defaults to the existing dedicated CPUE selectivity (row 7); CPUE length compositions continue to use row 7.

Bug fixes

  • find_msy() now honors the legacy phase_F active-fishery mask and restores the TPL’s two-stage catch-allocation optimization. The stiff final phase is solved on log harvest rates, avoiding false convergence in the recruitment-floor basin and a pathological 60,000-iteration raw-scale case.

  • The experimental msy_model() stub, whose objective and reports were commented out and which always returned zero, is no longer exported. Use find_msy() for one prepared state or run_msy() for fitted posteriors.

  • CPUE length-frequency fit and residual plots now use their dedicated cpue_lf_years vector rather than the CPUE abundance-index years.

  • Balanced-grid tmbfit objects no longer retain first-cell unbounded draws or classify lp__ as a model parameter, and support requested draw counts below the number of cells.

  • Projection cache signatures now cover the transitive package-function graph; corrupt or incomplete caches are misses and successful writes are atomic.

  • Grid diagnostics now fail closed when any R-hat, ESS, divergence, or treedepth diagnostic is undefined. Named lower and upper grid bounds are validated and aligned independently.

  • Projection cache identity now explicitly includes the assessment model and CTP implementation. Grid, projection, and fixture replacements use atomic same-directory renames with a portable backup-and-restore fallback.

  • Refit the bundled example optimum against the current likelihood and data contract. The fixture no longer serializes a transient RTMB objective; consumers rebuild it from the saved data, parameters, and map.

  • Mark cached posterior, profile, and projection examples that predate the current likelihood as legacy outputs that require regeneration for inference.