--- title: "Architecture" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Architecture} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- The purpose of this document is for people editing the `SPoRC` codebase (contributors, successors, future-you). The goal here is to keep the package navigable as it grows, especially as spatial model development adds more moving parts. It should be updated whenever the pipeline below changes shape (a new `Setup_Mod_*` stage, a new section of `SPoRC_rtmb.R`, a new post-fit diagnostic) — treat a stale architecture doc as a bug. ## Mental model In general, `SPoRC` has three phases, in this order: 1. **Setup** — a chain of R functions builds up three plain lists: `data`, `parameters`, `mapping`. No likelihood is evaluated yet; this phase is pure data wrangling and input validation. 2. **Fit** — `data` and `parameters` are handed to `RTMB::MakeADFun` wrapped around a single objective function, `SPoRC_rtmb()`, which is optimized with `nlminb` + Newton refinement. 3. **Post-fit** — the fitted object (`obj$rep`, `obj$sdrep`, `obj$optim`) is passed to diagnostics, reference-point, plotting, and resampling routines. ``` Setup_Mod_Dim() -> Setup_Mod_Rec() -> Setup_Mod_Biologicals() -> Setup_Mod_Movement() -> Setup_Mod_Tagging() -> Setup_Mod_Catch_and_F() -> Setup_Mod_SrvIdx_and_Comps() -> Setup_Mod_Srvsel_and_Q() -> Setup_Mod_Weighting() | v input_list$data, input_list$par, input_list$map fit_model(data, parameters, mapping) | RTMB::MakeADFun(cmb(SPoRC_rtmb, data), parameters, map = mapping) | nlminb() + Newton steps v obj (obj$rep, obj$sdrep, obj$optim) | +--> Get_Reference_Points() (SPR / MSY / Fmsy by structure) +--> get_model_fits() / plot_* (index, comp, OSA residual diagnostics) +--> do_retrospective(), do_jitter(), do_likelihood_profile(), run_francis() +--> Do_Population_Projection(), closed_loop_simulations() ``` ### The `input_list` accumulator pattern Every `Setup_Mod_*` function takes an `input_list` (initialized by `Setup_Mod_Dim()` as `list(data = list(), par = list(), map = list())`) and returns it with more keys filled in. This is the central structure of the package: setup functions are pipeline stages that thread one growing list through each other, not independent builders. Order generally matters — later stages read dimensions (`input_list$data$n_regions`, etc.) set by earlier ones, and default arguments frequently reference `input_list$data$...` in their own definitions (see `Setup_Mod_Rec()`'s defaults for an example). At the end of the chain, `input_list$data`, `input_list$par`, and `input_list$map` are unpacked and passed to `fit_model()`. `Setup_Sim_*` functions (in the same files as their `Setup_Mod_*` counterparts) follow the identical accumulator pattern but build inputs for `Simulate_Population.R`'s operating model instead of for fitting. ### `SPoRC_rtmb()` — the objective function [R/SPoRC_rtmb.R](https://github.com/chengmatt/SPoRC/blob/main/R/SPoRC_rtmb.R) is the one RTMB objective function for every model configuration the package supports; there is no per-model-type branching at the file level; instead the objective always runs but is regulated by the `data` switches set during Setup (e.g. `rec_model`, `move_type`, which likelihood/OSA flags are on). Concretely it moves through, in order: parameter transforms (movement, natural mortality, selectivity) -> mortality -> recruitment -> initial age structure -> population projection -> observation models (fishery, survey, tagging) -> likelihood components (retained/discarded catch, indices, compositions, tags) -> priors and penalties. If you're adding a new data source or process to the model, this is the file where its likelihood contribution gets wired in; the corresponding `Setup_Mod_*` function is where its data/parameters/mapping get prepared beforehand. ## Naming conventions The prefix on a function name generally tells you its role in the pipeline: | Prefix | Role | |---|---| | `Setup_Mod_*` | Builds `data`/`parameters`/`mapping` for a real fit, one life-history process or data source per file (`Setup_Rec.R`, `Setup_Biologicals.R`, `Setup_Movement.R`, `Setup_Fishery.R`, `Setup_Survey.R`, `Setup_Tagging.R`, `Setup_Observation_Processes.R`). | | `Setup_Sim_*` | Same role, but for the **operating model** in `Simulate_Population.R` (self-tests, closed-loop simulation). Lives alongside its `Setup_Mod_*` counterpart in the same file. | | `do_*_mapping` | Private helper (not exported) inside a `Setup_*` file that builds the RTMB `map` factor list for one parameter block — this is how parameters get fixed, shared across blocks, or turned into random-effect deviations. | | `Get_*` | Pulls a derived quantity out of already-set-up data/parameters/a fitted object: `Get_Movement`, `Get_Selex`, `Get_Reference_Points`, `Get_Init_NAA`, `Get_Det_Recruitment`. | | `do_*` (lowercase, top-level) | A post-fit procedure that re-runs or perturbs a fitted model: `do_retrospective`, `do_jitter`, `do_likelihood_profile`, `do_runs_test`, `do_francis`. | | `get_*_plot` / `get_*_fits` | Post-fit plotting/tabulation, mostly in `Make_Plots_Tables.R` and `get_model_fits.R`. | Within `SPoRC_rtmb.R` the same `## Section` / `### Subsection` comment banner style is used consistently — keep using it there if you extend the file, since it's the only navigation aid for a ~2,800-line function. ## File map ``` R/ Setup_Dimensions.R entry point: n_regions, n_pop, ages, years, seasons... Setup_Rec.R recruitment model, steepness, rec devs, stray/season/sex-ratio pars Setup_Biologicals.R growth, weight-at-age, maturity, natural mortality Setup_Movement.R movement parameterization (rates, blocks, continuous PE) Setup_Tagging.R conventional tagging design, reporting, shedding Setup_Fishery.R catch, F, discard mortality, fishery comps setup Setup_Survey.R survey indices, comps, selectivity, catchability Setup_Observation_Processes.R likelihood weighting across data sources Setup_Containers.R shared array/container scaffolding for Setup_Sim_* SPoRC_rtmb.R the RTMB objective function (see above) fit_model.R MakeADFun + nlminb + Newton refinement wrapper Get_Movement.R, Get_Selex.R, Get_Det_Recruitment.R, Get_Init_NAA.R derived-quantity helpers used inside SPoRC_rtmb.R Get_PE_loglik.R process-error / random-effect log-likelihood + penalty helpers Get_Comp_Likelihoods.R, Get_Conv_Tag_Likelihoods.R composition and tag-recapture likelihood assembly Get_Reference_Points.R SPR/BH-MSY solvers (single-region, global, local x sgl/multi-pop) Get_3d_precision.R GMRF/precision-matrix helpers for correlated deviations Distributions.R custom density functions (Dirichlet, Dirichlet-multinomial, negative binomial, OSA residual machinery) used by RTMB and sims Build_PE_Map.R shared process-error map-building across parameter blocks Do_Population_Projection.R forward projection off a fitted model Simulate_Population.R operating model: simulate data forward given true dynamics Simulate_RV.R random-variate generators used by the simulator closed_loop_simulations.R condition + run closed-loop MSE, given an OM and estimator do_retrospective.R, do_jitter.R, do_likelihood_profile.R, do_runs_test.R, do_francis.R post-fit diagnostics / resampling procedures get_model_fits.R OSA residuals, fitted vs. observed comps/indices Make_Plots_Tables.R ggplot2 plotting and summary tables Utility_Functions.R cross-cutting helpers (cmb() currying, safe_extract, parameter-mapping introspection, misc math) check_dimensions.R input validation for data/sim dimension lists release_tag_attr.R tag-release attribute bookkeeping data.R documents the bundled example datasets in data/ ``` Test files in `tests/testthat/` mirror this structure loosely by feature (`test-movement.R`, `test-fdev_pe.R`, `test-getselex.R`, ...) plus a set of end-to-end regression tests against bundled example fits (`test-mlt_rg_*`, `test-ebs_pol_sgl_rtmb*.R`, `test-dusky_rtmb.R`) that pin down `obj$rep`/`nll` values for known configurations — these are the tests most likely to catch an accidental change in `SPoRC_rtmb.R`'s numerics. ## Extending the model: a checklist Adding a new process, fleet type, or data source touches the pipeline in a predictable set of places: 1. **Setup**: add or extend a `Setup_Mod_*` function to accept the new spec, validate it, and append the resulting entries to `input_list$data` / `$par` / `$map`. If parameters need fixing/sharing, add a `do_*_mapping` helper in the same file. 2. **Objective**: add a `## Section` in `SPoRC_rtmb.R` that consumes the new `data`/`parameters` entries and contributes to `jnll`/`nll` — put it in the section it belongs to (transform, observation model, or likelihood/prior) rather than appending to the end. 3. **Sim counterpart**: if the process should be simulate-able, add the matching `Setup_Sim_*` inputs and generation logic in `Simulate_Population.R`. 4. **Post-fit**: expose derived output through `Get_*` (if other code needs to reuse the derived quantity) and/or `Make_Plots_Tables.R` / `get_model_fits.R` (if it needs to be seen). 5. **Tests**: add a targeted `testthat` test near the feature, and check whether the `test-mlt_rg_*` / `test-*_rtmb.R` regression tests need updated reference values. 6. **Vignette**: if the feature is user-facing, document it in the relevant lettered vignette (see the [Articles index](index.html)) rather than only in code comments.