Rceattle C++ template
The CEATTLE likelihood: src/TMB/ceattle.cpp and its headers
Loading...
Searching...
No Matches
ceattle.cpp
Go to the documentation of this file.
1#if defined(__clang__)
2# pragma clang diagnostic push
3# pragma clang diagnostic ignored "-Wunknown-warning-option"
4#endif
5#include <TMB.hpp>
6#if defined(__clang__)
7# pragma clang diagnostic pop
8#endif
9
10// GCC/Eigen emits false -Warray-bounds positives where its vectorized reductions
11// inline TMB's multi-dimensional indexing (e.g. the 5-D suitability() access in
12// predation.hpp). A source pragma rather than a -Wno-array-bounds compiler flag,
13// so CRAN's compilation-flags check stays clean and other diagnostics still show.
14#if defined(__GNUC__)
15# pragma GCC diagnostic ignored "-Warray-bounds"
16#endif
17
18#include "helper_functions.hpp"
19#include "comp_osa.hpp"
20#include "comp_sim.hpp"
21#include "growth.hpp"
22#include "selectivity.hpp"
23#include "recruitment.hpp"
24#include "spr.hpp"
25#include "bioenergetics.hpp"
26#include "predation.hpp"
27#include "diet_data.hpp"
28#include "linkage.hpp"
29#include "likelihood.hpp"
30
31/**
32 * @file ceattle.cpp
33 * @brief The CEATTLE objective function: configuration, population dynamics,
34 * predation, likelihood and report. The numbered section index is in the
35 * banner below; the process equations live in the headers this file includes.
36 */
37
38// Reads an R list() of matrices into a vector<matrix<Type>>: the per-fleet
39// survey-index covariance matrices Sigma supplied when Index_loglike is
40// "MVN"/"MVNORM" (the AMAK/ebswp DoCovBTS covariance survey likelihood; sections
41// 8.2b and 13.1). Other fleets pass a 1x1 inert dummy, so the list is always
42// length n_flt and indexes by fleet code.
43template<class Type>
44struct LOM_t : vector<matrix<Type> > {
45 LOM_t(SEXP x) { // x = R list of matrices
46 (*this).resize(LENGTH(x));
47 for(int i = 0; i < LENGTH(x); i++){
48 SEXP m = VECTOR_ELT(x, i);
49 (*this)(i) = asMatrix<Type>(m);
50 }
51 }
52};
53
54/* ------------------------------------------------------------------------- //
55 * CEATTLE: Bioenergetic-based Assessment for Understanding Biomass Linkages To
56 * The Environment -- a multispecies statistical model in Template Model
57 * Builder. Version: see DESCRIPTION.
58 *
59 * Follows the Google C++ style guide
60 * (https://google.github.io/styleguide/cppguide.html#General_Naming_Rules),
61 * except that free functions stay snake_case.
62 *
63 * CITATIONS:
64 * 1. Holsman, K. K., Ianelli, J., Aydin, K., Punt, A. E., & Moffitt, E. A. (2015). A comparison of fisheries biological reference points estimated from temperature-specific multi-species and single-species climate-enhanced stock assessment models. Deep-Sea Research Part II: Topical Studies in Oceanography, 134, 360–378. https://doi.org/10.1016/j.dsr2.2015.08.001
65 * 2. Adams, G. D., Holsman, K. K., Barbeaux, S. J., Dorn, M. W., Ianelli, J. N., Spies, I., ... & Punt, A. E. (2022). An ensemble approach to understand predation mortality for groundfish in the Gulf of Alaska. Fisheries Research, 251, 106303.
66 * 3. Wassermann, S. N., Adams, G. D., Haltuch, M. A., Kaplan, I. C., Marshall, K. N., & Punt, A. E. (2025). Even low levels of cannibalism can bias population estimates for Pacific hake. ICES Journal of Marine Science, 82(1), fsae064.
67 * ------------------------------------------------------------------------- //
68 * INDEX:
69 * 0. Load dependencies
70 * 1. Model configuration
71 * 2. Model inputs
72 * 3. Model parameters
73 * 4. Derived quantities
74 * 5. Initial calculations
75 * 6. Population dynamics
76 * 7. Predation mortality equations
77 * 8. Index components equations
78 * 9. Fishery components equations
79 * 10. Composition equations
80 * 11. Predicted stomach content
81 * 12. Derived quantities
82 * 13. Likelihood components
83 * 14. Report section
84 * 15. Model return/end
85 *
86 */
87
88
89template<class Type>
90Type objective_function<Type>::operator() () {
91 using namespace density; // necessary to use AR1, SCALE, SEPARABLE
92
93 /** ------------------------------------------------------------------------ //
94 * 1. MODEL CONFIGURATION //
95 * ------------------------------------------------------------------------- */
96 // 1.1. CONFIGURE MODEL (this section sets up the switches)
97 DATA_INTEGER(estimateMode); // 0 = hindcast + HCR projection, 1 = hindcast, 2 = projection from inits, 3 = build only, 4 = smoke test
98 DATA_INTEGER(msmMode);
99 // 0 = single species (no predation mortality)
100 // 1 = Type II MSVPA based (sensu Holsman et al 2015)
101 // 2 = Type III MSVPA based
102 // 3-9 = not yet implemented (Holling forms, predator interference and
103 // preemption, Hassell-Varley, Ecosim)
104 DATA_IVECTOR(suitMode); // Estimate suitability
105 // DATA_INTEGER(est_diet); // Include diet data in the likelihood
106 // DATA_INTEGER(avgnMode); // Not read; predation uses avgN_at_age
107 DATA_INTEGER(initMode); // How the age-structure is initialized
108 DATA_IVECTOR(forecast); // Run the model in forecast mode
109
110
111 // 1.2. Temporal dimensions
112 DATA_INTEGER( styr ); // Start year
113 DATA_INTEGER( endyr ); // End of estimation years
114 DATA_INTEGER( projyr ); // End year of projection
115
116 DATA_INTEGER( srr_mse_switchyr ); // Last year of the average-recruitment window; for MSE runs.
117 DATA_IVECTOR( suit_styr ); // First year of the suitability average, per predator species.
118 DATA_IVECTOR( suit_endyr ); // Last year of the suitability average, per predator species.
119 DATA_INTEGER( srr_hat_styr ); // First year of the stock-recruitment-penalty / env-rec window.
120 DATA_INTEGER( srr_hat_endyr ); // Last year of the stock-recruitment-penalty / env-rec window.
121
122 int nyrs = projyr - styr + 1;
123 int nyrs_hind = endyr - styr + 1;
124
125 // suit_styr / suit_endyr are per-predator (offset to model-year indices below,
126 // once `nspp` is available).
127 int nyrs_srrmean = srr_mse_switchyr - styr + 1;
128
129 srr_hat_styr = srr_hat_styr - styr;
130 srr_hat_endyr = srr_hat_endyr - styr;
131 if(nyrs_srrmean > nyrs_hind){nyrs_srrmean = nyrs_hind;}
132 if(srr_hat_styr == 0){srr_hat_styr = 1;} // R_hat starts at year 1.
133
134 // 1.3. Number of species
135 DATA_INTEGER( nspp ); // Number of species (prey)
136
137 // Offset per-predator suitability reference years to model-year indices and
138 // count the averaging window length for each predator species.
139 vector<int> nyrs_suit(nspp);
140 for(int sp = 0; sp < nspp; sp++){
141 suit_endyr(sp) = suit_endyr(sp) - styr;
142 suit_styr(sp) = suit_styr(sp) - styr;
143 nyrs_suit(sp) = suit_endyr(sp) - suit_styr(sp) + 1;
144 }
145
146 DATA_IVECTOR( pop_wt_index ); // Dim 1 of weight_obs to use for population dynamics
147 DATA_IVECTOR( ssb_wt_index ); // Dim 1 of weight_obs to use for spawning stock biomass
148 DATA_IVECTOR( pop_age_transition_index );// Dim 1 of age_trans_matrix to use for population dynamics
149 DATA_IVECTOR( estDynamics ); // 0 = population parameters estimated, 1 = numbers-at-age provided, 2 = provided numbers-at-age times an estimated scalar
150 pop_wt_index -= 1; // Indexing starts at 0
151 ssb_wt_index -= 1; // Indexing starts at 0
152 pop_age_transition_index -= 1; // Indexing starts at 0
153
154
155 // 1.4. RECRUITMENT SETTINGS
156 DATA_INTEGER(srr_fun); // Stock recruit relationship for hindcast estimation
157 DATA_INTEGER(srr_pred_fun); // Stock recruit relationship for projection/brps/penalty
158 DATA_INTEGER(proj_mean_rec);
159 // 0 = project recruitment using log_R0 and rec devs
160 // 1 = project recruitment using mean rec (can also have adjusted rec devs)
161 DATA_INTEGER(srr_est_mode); // Logical of wether to add normal prior to stock recruit-relationship
162 DATA_VECTOR(srr_prior); // Prior mean for stock recruit relationship parameter
163 DATA_VECTOR(srr_prior_sd); // Prior sd for stock recruit relationship parameter
164 DATA_INTEGER( niter ); // Number of loops for MSM mode
165 DATA_VECTOR( Bmsy_lim ); // Upper limit for Bmsy in ricker function. Will add penalty if 1/beta > lim
166
167
168 // 1.5. HARVEST CONTROL RULE (HCR) SETTINGS
169 DATA_INTEGER(HCR); // Function to be used for harvest control rule
170 DATA_INTEGER(DynamicHCR); // TRUE/FALSE. Whether to use static or dynamic reference points (default = FALSE)
171 DATA_VECTOR(Ptarget); // Target SSB as a PROPORTION of static or dynamic SSB at F = 0 (B40% is 0.40)
172 DATA_VECTOR(Plimit); // Limit SSB as a PROPORTION of static or dynamic SSB at F = 0 (B10% is 0.10)
173 DATA_VECTOR(Ftarget_percent); // Proportion of SSB-per-recruit or SB0 at F = 0 that sets the target F (F40% is 0.40)
174 DATA_VECTOR(Flimit_percent); // Proportion of SSB-per-recruit or SB0 at F = 0 that sets the limit F (F35% is 0.35)
175 DATA_VECTOR(Alpha); // Parameter used in NPFMC Tier 3 HCR
176 DATA_VECTOR(Fmult); // Multiplier for target fishing mortality (Fmult * Ftarget). For example 0.75 * F40%.
177 DATA_VECTOR(QnormHCR); // Add to Flimit to set Ftarget based on the Pstar approach: Flimit + qnorm(Pstar, 0, Sigma)
178
179
180 // 1.6. MODEL OBJECTS
181 // 1.6.1. LOOPING INDICES -- k = observation, sp = species, sex = sex (0 = combined; 1 = females; 2 = males)
182 // age = age, ln = length, yr = year
183 int sp, sex, age, ln, yr;
184 int index, flt; // Survey and fishery indices
185 int flt_yr, flt_sex, comp_type;
186 int fsh_ind, index_ind, comp_ind, yr_ind; // Indices for survey sets
187 int wt_idx_pop, wt_idx_ssb, wt_idx_flt; // Indices for weight indices
188 Type mo = 0; // Month float
189 if(msmMode == 0) { niter = 1; } // Number of iterations for SS mode
190
191
192 /** ------------------------------------------------------------------------ //
193 * 2. MODEL INPUTS //
194 * ------------------------------------------------------------------------- */
195
196 // -- 2.1. Species attributes
197 DATA_IVECTOR( nsex ); // Sexes modelled per species: 1 = combined, 2 = both.
198 int max_sex = imax(nsex); // Max sexes, for array dimensions
199 DATA_VECTOR( spawn_month ); // Month of spawning to adjust mortality
200 DATA_IVECTOR( nages ); // Age bins per species.
201 DATA_IVECTOR( minage ); // Minimum age of each species
202 DATA_IVECTOR( nlengths ); // Observation length bins per species.
203 DATA_MATRIX(lengths); // Length bins for each species [sp, nlengths]
204 DATA_IVECTOR( nlengths_pop ); // Population length bins per species. Equals nlengths when no population grid is given
205 DATA_MATRIX( lengths_pop ); // Population length bins, lower edges (cm) [sp, nlengths_pop]: the grid the age-length key, weight and maturity are integrated on
206 DATA_IMATRIX( pop_to_data_bin ); // Data length bin (0-based) that each population bin sums into [sp, nlengths_pop]
207 DATA_ARRAY( NByageFixed ); // Input numbers- or index-at-age, multiplied (or not) by pop_scalar to get N_at_age
208 DATA_VECTOR( MSSB0 ); // SB0 from projecting forward in multi-species mode under no fishing
209 DATA_VECTOR( MSB0 ); // B0 from projecting forward in multi-species mode under no fishing
210
211 int max_nlengths = imax(nlengths); // Max nlengths, for array dimensions
212 // The age-length key and selectivity-at-length live on the POPULATION length bins,
213 // which are the data bins whenever no pop_lengths is supplied.
214 int max_nlengths_pop = imax(nlengths_pop);
215
216 // -- 2.3c. First and last population bin in each data bin, per species. SS3 forms
217 // sel(L) * P(L|age) at population resolution and bins the result, so pred_CAAL and the
218 // length compositions sum each data bin's run rather than applying one bin-average
219 // selectivity across it. The map is monotone, so the runs are contiguous; with one grid
220 // every run is one bin.
221 matrix<int> pop_bin_lo(nspp, max_nlengths); pop_bin_lo.setZero();
222 matrix<int> pop_bin_hi(nspp, max_nlengths); pop_bin_hi.setZero();
223 for(int sp_i = 0; sp_i < nspp; sp_i++){
224 for(int d = 0; d < nlengths(sp_i); d++){ pop_bin_lo(sp_i, d) = -1; pop_bin_hi(sp_i, d) = -2; }
225 for(int lp = 0; lp < nlengths_pop(sp_i); lp++){
226 int d = pop_to_data_bin(sp_i, lp);
227 if(pop_bin_lo(sp_i, d) < 0) pop_bin_lo(sp_i, d) = lp;
228 pop_bin_hi(sp_i, d) = lp;
229 }
230 }
231 int max_age = imax(nages); // Max nages, for array dimensions
232 // Sized on the POPULATION bins: the non-parametric curves are built over nlengths_pop,
233 // so a finer grid would otherwise write past non_par_sel.
234 int max_bin = (max_age > max_nlengths_pop) ? max_age : max_nlengths_pop;
235
236 // -- 2.2. M1_at_age specifications
237 DATA_IVECTOR(M1_model);
238 DATA_IVECTOR(M1_re);
239 DATA_IVECTOR(M1_use_prior);
240 DATA_IVECTOR(M2_use_prior);
241 DATA_VECTOR(M_prior);
242 DATA_VECTOR(M_prior_sd);
243
244 // -- 2.3. Growth model specifications
245 DATA_IVECTOR(growth_model); // 0: "input", 1: "vB-classic", 2: "Richards", 3: "nonparametric LAA" [sp]
246 DATA_IVECTOR(growth_sd_style); // Plus-group SD-at-age treatment [sp]: 1 = WHAM (pin to exp(sd_Linf)), 2 = interpolate by length
247 DATA_IVECTOR(growth_sd_form); // Growth variability endpoints [sp]: 1 = SDs in cm (SS3 CV_Growth_Pattern 2), 2 = CVs, SD = CV * L (SS3 pattern 0)
248 DATA_IVECTOR(growth_plus_length); // Plus-group mean length [sp]: 1 = M1-weighted, 2 = none (SS3 Linf_decay -998), 3 = SS3.24 (-999), 4 = SS3 decay rate
249 DATA_VECTOR(plus_group_decay); // Decay rate (per year) for growth_plus_length == 4 [sp]
250 DATA_IMATRIX(sel_dn6_ends); // DoubleNormalSS3, per fleet [flt, 2]: 1 = scale the initial / final end by P5 / P6, 0 = SS3's -999 (unscaled)
251 DATA_IVECTOR(mat_len_use); // 1 = maturity-at-length replaces the age-based maturity in spawning output [sp]
252 DATA_MATRIX(mat_len_pars); // Maturity-at-length [sp, 2]: L50 (cm), logistic slope (per cm)
253 DATA_VECTOR(growth_age_L1); // VB anchor age (= SS3 Growth_Age_for_L1) per sp; defaults to max(0.5, minage[sp]) in R-side fit_mod()
254
255 // -- 2.3b. Linkage table (R/0-linkage_encode.R): covariate and year effects on a
256 // process parameter p in one stratum (species, sex, age bin, fleet; 0 = all).
257 // Row i adds beta_i * x_i(y), with x_i(y) = linkage_X(y, linkage_X_col(i)) its
258 // design column in model year y (row 0 = styr; offset 0 past the last row).
259 // With S_log, S_id and S_exp the sums of beta_i * x_i(y) over p's log-,
260 // identity- and exponential-link rows, and p's own deviates (log_M1_dev,
261 // index_q_dev, sel_inf_dev, log_sel_slp_dev) still applied:
262 // most parameters: p_y = exp(log p + S_log) + S_id
263 // sel_inf, DoubleNormalSS3 P1-P6: p_y = (p + S_id) * exp(S_log), stored scale
264 // catchability (5.3): q_y = exp((log q + S_log) * exp(S_exp) + index_q_dev) + S_id
265 // (Intercept) rows are held at 0, so p carries the level, except R_init's,
266 // which is estimated: a log multiplier on the initial age structure, not on
267 // year-1 recruitment (6.4). Composition and growth-sd rows add no offset;
268 // an (Intercept)'s init, bounds and prior go to the base parameter.
269 // Terms used below:
270 // row one beta_i; fixed-effect rows read beta_linkage.
271 // slot one random-effect deviation, e.g. one year of (1 | Year) in one
272 // stratum; its row's beta is beta_linkage_re_all(slot) (5.2b), times
273 // beta_linkage_obs if observed.
274 // position where a slot sits in beta_linkage_re (integrated) or
275 // beta_linkage_re_pen (penalized): linkage_re_slot.
276 // group the slots of one random term (re_group, e.g. Year, and structure)
277 // on one parameter and stratum: one sd, log_sigma_linkage, and one
278 // structure, IID, random walk or AR1. An observed AR1 is QAR1 (Rogers
279 // et al. 2024), its latent also fitted to an env_data series.
280 // Empty when no build_*() supplied `linkages`.
281 DATA_IVECTOR(linkage_process); // process code, RCEATTLE_PROC_* (linkage.hpp): recruitment, M, growth, q, sel, comp
282 DATA_IVECTOR(linkage_param); // per-process parameter code
283 DATA_IVECTOR(linkage_species); // 1-based sp id; 0 = all
284 DATA_IVECTOR(linkage_sex); // 1-based sex id; 0 = all
285 DATA_IVECTOR(linkage_age_bin); // 1-based age bin, age - minage + 1; 0 = all
286 DATA_IVECTOR(linkage_fleet); // 1-based Fleet_code; 0 = all
287 DATA_IVECTOR(linkage_X_col); // 0-based column of linkage_X
288 DATA_IVECTOR(linkage_link); // link code, RCEATTLE_LINK_* (linkage.hpp)
289 // Only an identity-link recruitment offset can make the curve non-positive, so
290 // the recruitment floors (sections 6.3, 6.5, 6.6 and 6.9) run only then; every
291 // other model's AD tape is left exactly as it was.
292 int rec_floor_on = 0;
293 for (int i = 0; i < linkage_link.size(); ++i) {
294 if (linkage_process(i) == RCEATTLE_PROC_RECRUIT && linkage_link(i) == RCEATTLE_LINK_IDENTITY) rec_floor_on = 1;
295 }
296 DATA_IVECTOR(linkage_re_index); // per row: -1 = fixed; else its 0-based slot in beta_linkage_re_all
297 // linkage_re_sigma, _rho and _obs give positions in log_sigma_linkage, trans_rho_linkage
298 // and beta_linkage_obs; one sd per group, so linkage_re_sigma is the slot's group.
299 DATA_IVECTOR(linkage_re_sigma); // per RE slot: its 0-based group (an index, not an sd)
300 DATA_IVECTOR(linkage_re_integrate); // per RE slot: 1 = integrated out (Laplace), 0 = penalized
301 DATA_IVECTOR(linkage_re_slot); // per RE slot: its position (2.3b)
302 DATA_IVECTOR(linkage_re_struct); // per RE group: RCEATTLE_RE_IID (a plain (1 | Year)), _RW or _AR1 (linkage.hpp)
303 DATA_IVECTOR(linkage_re_rho); // per RE group: 0-based position in trans_rho_linkage (ar1 only; -1 otherwise)
304 DATA_IVECTOR(linkage_re_sigma_prior_family); // per RE group: prior on the SD (0=none,1=normal,2=lognormal,3=gamma,4=beta)
305 DATA_IVECTOR(linkage_re_rho_prior_family); // per RE group: prior on rho (natural (-1,1) scale)
306 DATA_VECTOR(linkage_re_rho_prior_p1); // per RE group: rho prior param 1
307 DATA_VECTOR(linkage_re_rho_prior_p2); // per RE group: rho prior param 2
308 DATA_IVECTOR(linkage_re_obs); // per RE group: 0-based position in beta_linkage_obs (Rogers QAR1 observed ar1; -1 otherwise)
309 // linkage_re_obs_sd is unread here; log_obs_sd_linkage starts at its log.
310 DATA_VECTOR(linkage_re_obs_sd); // per RE group: the obs_sd supplied (0 if unobserved)
311 DATA_VECTOR(linkage_re_obs_value); // per RE slot: observed covariate value (0 if unobserved)
312 DATA_IVECTOR(linkage_re_obs_mask); // per RE slot: 1 if the covariate is observed that year, 0 otherwise
313 DATA_IVECTOR(linkage_re_obsvec_idx); // per RE slot: 0-based obsvec position for OSA (-1 if unobserved / not built)
314 DATA_VECTOR(linkage_re_sigma_prior_p1); // per RE group: prior param 1
315 DATA_VECTOR(linkage_re_sigma_prior_p2); // per RE group: prior param 2
316 DATA_IVECTOR(linkage_is_intercept); // 1 if design_col == "(Intercept)", 0 otherwise
317 DATA_IVECTOR(linkage_prior_family); // none=0, normal=1, lognormal=2, gamma=3, beta=4
318 DATA_VECTOR(linkage_prior_p1); // family-specific prior param 1
319 DATA_VECTOR(linkage_prior_p2); // family-specific prior param 2
320 DATA_MATRIX(linkage_X); // design matrix [model year from styr, design column]
321
322 // -- 2.4. Fleet controls (i.e. how to assign data to objects)
323 DATA_IVECTOR(flt_type); // Fleet_type: 0 = Off, 1 = Fishery, 2 = Survey (NOT a boolean)
324 DATA_VECTOR(flt_month);
325 DATA_IVECTOR(flt_sel_type); // Fleet selectivity parameterization
326 DATA_IVECTOR(flt_sel_dim); // Fleet selectivity dimension (0 = age, 1 = length)
327 DATA_IVECTOR(flt_n_sel_bins); // Age/length bins for non-parametric selectivity
328 DATA_IVECTOR(flt_sel_cap_bin); // NonParametricPM (type 9): first bin (0-based, age or length per flt_sel_dim) at/after which realized selectivity is held flat (RTMB cap_old_age). < 0 = no cap.
329 DATA_IVECTOR(flt_varying_sel); // Whether time-varying selectivity is estimated
330 DATA_IVECTOR(flt_spp); // Fleet species
331 DATA_IVECTOR(bin_first_selected); // First selected age/length BIN, 0-based (selectivity below it = 0)
332 DATA_IVECTOR(sel_norm_bin1); // Normalization BIN, 0-based, or a control flag when negative (-999 = do not normalize, -99 = normalize by the maximum); rearrange_data() converts, so NA never arrives. LogisticPM (type 11): lower bin of the selectivity-penalty range.
333 DATA_IVECTOR(sel_norm_bin2); // Upper normalization BIN, same encoding as sel_norm_bin1. LogisticPM (type 11): upper bin of the selectivity-penalty range.
334 DATA_IVECTOR(sel_norm_scope); // Whether normalization pools its reference across sexes: 0 = WithinSex (each sex divided by its own reference; both reach 1), 1 = AcrossSexes (one pooled reference; the lower sex stays below 1). Orthogonal to sel_norm_bin1/2, which say WHERE the reference is taken.
335 DATA_IVECTOR(flt_sel_start_yr); // Per-fleet selectivity start year (0-based from styr); selectivity penalties start the year after this. Default 0 (= styr).
336 DATA_IVECTOR(flt_sel_pen_first_bin); // Per-fleet first bin (0-based, age or length per flt_sel_dim) for the non-parametric shape/monotonicity penalty. < 0 -> defaults to bin_first_selected. Lets the shape constraint span a narrower range than the (possibly non-zero) first selected bin (e.g. ATS mina_ats > first selected age).
337 DATA_IVECTOR(flt_sel_lead); // 1 if this fleet's selectivity penalty should be accumulated; 0 if it mirrors an earlier fleet's selectivity (same Selectivity_index + type) so the shared penalty is counted once.
338 DATA_IVECTOR(flt_q_lead); // As flt_sel_lead, for catchability: 1 if this fleet carries the q prior / deviate penalties, 0 if it shares an earlier fleet's Q_index so the shared block is counted once.
339 DATA_IVECTOR(flt_sel_pen_last_bin); // Per-fleet last bin (0-based, = left bin of the last adjacent pair) for the non-parametric shape penalty. < 0 -> defaults to nbins-2 (whole range).
340 DATA_IVECTOR(flt_sel_shape_mode); // Non-parametric shape-penalty mode: 0 = directional (sign of Sel_curve_pen1 -> penalize decreasing/increasing, one-sided, ADMB/AMAK); 1 = smooth (two-sided d^2 over adjacent ages, RTMB "rpm").
341 DATA_VECTOR(flt_sel_avgsel_pen); // Per-fleet weight on the AMAK "avgsel" base-level penalty (type 9 only); the form is penalty (5) in section 13. 0 = off (default).
342 DATA_IVECTOR(comp_ll_type); // Composition log-likelihood type
343 DATA_IVECTOR(comp_accum_young); // Per-fleet age/length-composition young-tail accumulation bin (1-based ordinal on the comp dimension); bins below it fold into it. 1 (or <1) = no young accumulation (AFSC ac_yng).
344 DATA_IVECTOR(comp_accum_old); // Per-fleet age/length-composition old-tail accumulation bin (1-based); bins above it fold into it. 0/NA or >= nbins = no old accumulation (AFSC ac_old).
345 DATA_IVECTOR(caal_ll_type); // CAAL composition log-likelihood type
346 DATA_IVECTOR(flt_units); // Fleet units (1 = weight, 2 = numbers)
347 DATA_IVECTOR(flt_wt_index); // Vector to save 1st dim of weight to use for weight-at-age
348 DATA_IVECTOR(flt_age_transition_index); // Dim 1 of age_trans_matrix each fleet reads for its ALK
349 DATA_IVECTOR(flt_ageing_error_index); // Ageing error matrix each fleet reads (0-based); the fleet's own species unless fleet_control gives one
350 DATA_IVECTOR(est_index_q); // Catchability form per fleet; see q_map and sections 8.2/8.2b
351 DATA_IVECTOR(index_varying_q); // Whether time-varying q is estimated
352 DATA_IVECTOR(est_sigma_index); // Whether survey sigma is estimated
353 DATA_IVECTOR(est_sigma_fsh); // Whether fishery sigma is estimated
354
355 // -- 2.4.1 Fishery Components
356 DATA_IMATRIX( catch_ctl ); // Fishery catch metadata; columns = Fleet_code, Species, Year
357 DATA_IMATRIX( catch_n ); // Info for fishery biomass; columns = Month
358 DATA_MATRIX( catch_obs ); // Observed fishery catch (mt or thousands of fish per the fleet's Observation_units) and log_sd; columns = Observation, Error
359
360 // -- 2.4.2 Survey components
361 DATA_IMATRIX( index_ctl ); // Index metadata; columns = Fleet_code, Species, Year
362 DATA_MATRIX( index_n ); // Info for index; columns = Month
363 DATA_MATRIX( index_obs ); // Observed index and log_sd; columns = Observation, Error
364 DATA_VECTOR( index_log_q_prior ); // Prior mean for catchability
365 DATA_IVECTOR( index_ll_type ); // Survey index likelihood family per fleet (0 = lognormal IID, 1 = MVN bare quadratic form, 2 = MVNORM full density, 3 = natural-scale normal with absolute sd)
366 DATA_STRUCT( index_cov_mat, LOM_t ); // Per-fleet covariance matrices Sigma for the MVN/MVNORM survey likelihood (1x1 dummy if unused)
367 DATA_VECTOR( index_cov_const ); // Per-fleet 0.5*(logdet(Sigma) + n*log(2pi)); subtracted for "MVN" so it reports the bare quadratic form
368
369 // -- 2.4.2b One-step-ahead (OSA) residual support
370 // `obsvec` is every observation entering the likelihood, flat: log catch, log index,
371 // each comp/caal row's bin counts, each stomach's diet composition. `keep` is the
372 // indicator TMB::oneStepPredict() toggles element by element; it defaults to all ones,
373 // so ordinary fitting is unchanged. Each `*_obsvec_idx` gives its `*_obs` row's 0-based
374 // position in `obsvec` (for a composition, where the row's bins start), or -1 when the
375 // row is not fitted. osa_mode 1 switches the comp/caal/diet branches to a proper,
376 // unweighted, keep-gated density; mode 2 adds the conditional CDF terms
377 // oneStepPredict(method = "cdf") reads -- log F(x) gated by keep.cdf_lower,
378 // log(1 - F(x)) by keep.cdf_upper -- computed only in mode 2, so no other path pays
379 // for the pbeta/pnorm. The Dirichlet-multinomial families have no CDF term
380 // (comp_osa.hpp) and osa_residuals() keeps those fleets off this method.
381 DATA_VECTOR( obsvec ); // Flat observations for OSA residuals
382 DATA_VECTOR_INDICATOR( keep, obsvec ); // oneStepPredict keep indicator (defaults to 1 when fitting)
383 DATA_IVECTOR( catch_obsvec_idx ); // obsvec position for each catch_obs row (-1 = excluded)
384 DATA_IVECTOR( index_obsvec_idx ); // obsvec position for each index_obs row (-1 = excluded)
385 DATA_IVECTOR( comp_obsvec_idx ); // obsvec start position for each comp_obs row's bins (-1 = excluded)
386 DATA_IVECTOR( caal_obsvec_idx ); // obsvec start position for each caal_obs row's bins (-1 = excluded)
387 DATA_IVECTOR( diet_obsvec_idx ); // obsvec start position for each stomach's prey bins (incl. "other prey"); length n_stomach_obs (-1 = excluded)
388 DATA_INTEGER( osa_mode ); // 0 = normal fitting (default); 1 = OSA build (unweighted keep-gated comp/caal/diet densities); 2 = OSA build + conditional CDF terms for method = "cdf"
389 DATA_INTEGER( adreport_sel ); // 0 = no selectivity standard errors (default); 1 = ADREPORT log_sel_at_age; set via fit_control(selectivity_se =)
390 DATA_SCALAR( comp_offset ); // proportion offset added to comp/caal/diet obs & pred before the multinomial; set via rearrange_data()/fit_control()
391
392 // -- 2.4.2c. Simulation switches (sim_mod(simulate = TRUE))
393 // Observations are always drawn; process error is a choice, so it is switched.
394 //
395 // simulate_state: which process. Slots 0 recruitment, 1 M, 2 growth, 3 catchability,
396 // 4 selectivity -- the codes linkage_process uses, so a random linkage gates on
397 // simulate_state(linkage_process(i)) untranslated. Default off: redrawing a process
398 // changes what self_test() measures, from recovering parameters to recovering a
399 // process. Recruitment covers init_dev too, the initial age structure being recruitment
400 // pre-styr.
401 //
402 // simulate_period: slot 0 the fitted window (0 < Year <= endyr), slot 1 outside it.
403 // Only slot 0 is read, and only by the PROCESS draws, whose densities cover the
404 // hindcast alone. Observations are drawn in every period deliberately, because
405 // run_mse() splices the negative-Year rows back in as the next assessment's data. Slot
406 // 1 is reserved and inert; sim_mod() exposes neither slot.
407 DATA_IVECTOR( simulate_state );
408 DATA_IVECTOR( simulate_period );
409
410 // -- 2.4.3. Composition data
411 DATA_IMATRIX( comp_ctl ); // Age/length comp metadata; columns = Fleet_code, Species, Sex, Age0_Length1, Year
412 DATA_MATRIX( comp_n ); // Month and sample size on observed age/length comp; columns = Month, Sample size
413 DATA_MATRIX( comp_obs ); // Observed age/length comp; cols = Comp_1, Comp_2, etc. can be proportion
414 DATA_IMATRIX( caal_ctl ); // CAAL metadata; columns = Fleet_code, Species, Sex, Year, Length_bin
415 DATA_MATRIX( caal_n ); // Sample size on CAAL; ONE column = Sample size. Unlike comp_n there is no month: a CAAL observation is placed at its fleet's Month, and the age-length key is annual.
416
417 // -- 2.4.4. Initial equilibrium catch: the catch yielded under the initial F,
418 // in the year before the hindcast. 0 rows unless the data supply one.
419 DATA_IMATRIX( equil_catch_ctl ); // columns = Fleet_code, Species
420 DATA_MATRIX( equil_catch_obs ); // columns = Catch, Log_sd
421 DATA_MATRIX( caal_obs ); // Observed CAAL; cols = Comp_1, Comp_2, etc. can be proportion
422
423 // -- 2.4.5 Age and selectivity
424 DATA_IMATRIX( emp_sel_ctl ); // Empirical selectivity metadata; columns = Fleet_code, Species, Sex, Year
425 DATA_MATRIX( emp_sel_obs ); // Observed emprical fishery selectivity; columns = Compe_1, Comp_2, etc.
426 DATA_ARRAY( age_trans_matrix); // observed sp_age/size compositions; n = [nspp, nages, index_age_bins]
427 DATA_ARRAY( age_error ); // Aging error matrices; n = [n_matrices, nages, nages]. One per species unless fleet_control$Ageing_error_index selects otherwise.
428
429 // -- 2.3.5. Growth
430 DATA_ARRAY( weight_obs ); // Weight-at-age by year; n = [nweight, sex, nages, nyrs]
431
432 // 2.3.6. Diet data
433 DATA_IVECTOR(diet_ll_type); // Diet composition log-likelihood type
434 DATA_VECTOR( fday ); // number of foraging days for each predator
435 DATA_ARRAY( ration_data ); // Relative-foraging rate
436 DATA_MATRIX( diet_obs ); // Pred, prey, pred-age, prey-age for diet matrix (weight of prey in pred stomach)
437 DATA_IMATRIX( diet_ctl ); // Per diet_obs row: predator, prey, both sexes, both ages, year
438 DATA_INTEGER(n_stomach_obs); // The total number of unique stomach samples (groups)
439 DATA_IVECTOR(stomach_id); // A vector mapping each diet data row to a stomach ID
440
441 // 2.3.7. Environmental data
442 DATA_MATRIX( env_index ); // Matrix of environmental predictors such as bottom temperature
443
444 // 2.4. INPUT PARAMETERS
445 // -- 2.4.1. Bioenergetics parameters (BP)
446 DATA_VECTOR( other_food ); // Biomass of other prey (mt)
447 DATA_VECTOR( Pvalue ); // Proportion of Cmax; the P in Cmax*fT*Pvalue*PAge
448 DATA_IVECTOR( Ceq ); // Which consumption equation to use; currently all sp = 1
449 DATA_IVECTOR( Cindex ); // Which env_index column drives bioenergetics.
450 DATA_VECTOR( CA ); // Wt specific intercept of Cmax=CA*W^CB
451 DATA_VECTOR( CB ); // Wt specific slope of Cmax=CA*W^CB
452 DATA_VECTOR( Qc ); // used in fT, QC value
453 DATA_VECTOR( Tco ); // used in fT, thermal optimum
454 DATA_VECTOR( Tcm ); // used in fT, thermal max
455 DATA_VECTOR( Tcl ); // used in fT eq 3, limit
456 DATA_VECTOR( CK1 ); // used in fT eq 3, limit where C is .98 max (ascending)
457 DATA_VECTOR( CK4 ); // used in fT eq 3, temp where C is .98 max (descending)
458
459 // -- 2.4.3. Others
460 DATA_MATRIX( sex_ratio ); // Proportion-at-age of females of population
461 DATA_MATRIX( maturity ); // Proportion of mature females at age; [nspp, nages]
462 DATA_SCALAR(bias_adjust_obs); // Whether to apply bias adjustment (-sigma^2/2) in index likelihoods
463 DATA_SCALAR(bias_adjust_proc); // Whether to apply bias adjustment (-sigma^2/2) in process likelihoods
464
465
466 /** ------------------------------------------------------------------------ //
467 * 3. PARAMETER SECTION //
468 * ------------------------------------------------------------------------- */
469
470 PARAMETER( dummy ); // Tests derived quantities given input parameters; n = [1]
471 PARAMETER_VECTOR( log_pop_scalar ); // Log multiplier on input numbers-at-age, per species (estDynamics = 2)
472
473 // -- 3.1. Recruitment parameters
474 PARAMETER_MATRIX( rec_pars ); // Stock-recruit parameters: col1 = mean rec, col2 = SRR alpha, col3 = SRR beta
475 PARAMETER_VECTOR( R_log_sd ); // Standard deviation of recruitment deviations
476 PARAMETER_MATRIX( rec_dev ); // Annual recruitment deviation; n = [nspp, nyrs]
477 PARAMETER_MATRIX( init_dev ); // Log initial numbers-at-age under initMode 0, log deviations off equilibrium under 2/3/4/6; n = [nspp, max_age]
478
479 // -- 3.2. Natural mortality (M1)
480 PARAMETER_ARRAY( log_M1 ); // Natural mortality: residual under multispecies mode, total under single species; n = [nspp, nsex, nages]
481 PARAMETER_ARRAY( log_M1_dev ); // Natural mortality annual deviate; n = [nspp, nsex, nyrs]
482 PARAMETER_ARRAY( M1_beta ); // Regression coefficients for environmnetally linked M1; n = [nspp, nsex, n indices]
483 PARAMETER_ARRAY( M1_rho ); // Correlation for AR1 random effects on age and year; n = [nspp, nsex, 2]
484 PARAMETER_ARRAY( M1_dev_log_sd ); // Innovation sd of the M1 random effects, one per species/sex; n = [nspp, nsex]
485
486 // -- 3.3. Growth
487 PARAMETER_ARRAY(log_growth_pars); // Mean growth curve parameters [sp, sex, par]
488 // Time-varying growth comes from growth_linkage_offset below.
489 PARAMETER_ARRAY(growth_log_sd); // Log standard deviation of length-at- min and max age [sp, sex, 2]
490 PARAMETER_MATRIX(weight_length_pars); // Length-weight parameters [sp, (alpha, beta)]
491
492 // -- 3.3b. Linkage-table coefficients (see section 2.3b). Aligned row-for-row with the
493 // linkage_* DATA vectors above. May be length 0 (no linkages supplied).
494 PARAMETER_VECTOR(beta_linkage);
495
496 // Random-effect linkage coefficients and their hyperparameters. All length 0 unless a
497 // `~ (1|group)` / rw() / ar1() linkage was supplied; when present, beta_linkage_re
498 // enters the Laplace approximation (added to random=) and its density goes to
499 // JNLL_LINKAGE_RE (13.14).
500 PARAMETER_VECTOR(beta_linkage_re); // RE deviation coefficients (Laplace-integrated)
501 // Penalized RE deviations: same density, outside `random` (TMB picks random effects by
502 // name). Fixed SD (and AR1 rho) only; see the vignette, "Integrated or penalized".
503 PARAMETER_VECTOR(beta_linkage_re_pen);
504 PARAMETER_VECTOR(log_sigma_linkage); // one log-SD per RE group
505 PARAMETER_VECTOR(trans_rho_linkage); // one per AR1 group; rho = tanh(trans_rho_linkage), via rho_trans()
506 PARAMETER_VECTOR(beta_linkage_obs); // Rogers QAR1 effect size: one per observed ar1 group
507 PARAMETER_VECTOR(log_obs_sd_linkage); // Rogers QAR1 observation log-SD: one per observed ar1 group (estimated if obs_sd_est = TRUE)
508
509 // -- 3.4. Fishing mortality parameters
510 PARAMETER_VECTOR( log_Flimit ); // Target fishing mortality for projections on log scale; n = [nspp, nyrs]
511 PARAMETER_VECTOR( log_Ftarget ); // Target fishing mortality for projections on log scale; n = [nspp, nyrs]
512 PARAMETER_VECTOR( log_Finit ); // Fishing mortality for initial population to induce non-equilibrium; n = [nspp]
513 PARAMETER_VECTOR( proj_F_prop ); // Proportion of fishing mortality from each fleet for projections; n = [n_fsh]
514 PARAMETER_MATRIX( log_F ); // Annual fishing mortality; n = [n_fsh, nyrs]
515
516 // -- 3.4. Survey catchability parameters
517 PARAMETER_VECTOR( index_log_q ); // Survey catchability; n = [n_index]
518 PARAMETER_MATRIX( index_q_beta ); // Survey catchability regression coefficients on env_data columns
519 PARAMETER_MATRIX( index_q_dev ); // Annual survey catchability deviates; n = [n_index, nyrs_hind]
520 PARAMETER_VECTOR( index_q_log_sd ); // Log standard deviation of prior on survey catchability; n = [1, n_index]
521 PARAMETER_VECTOR( index_q_dev_log_sd );// Log standard deviation of time varying survey catchability; n = [1, n_index]
522
523 // -- 3.5. Selectivity parameters
524 PARAMETER_ARRAY( sel_coff ); // selectivity parameters for non-parametric; n = [n_selectivities, nsex, n_sel_bins]
525 PARAMETER_ARRAY( sel_coff_dev ); // Annual deviates for non-parametric selectivity parameters; n = [n_selectivities, nsex, n_sel_bins]
526 PARAMETER_ARRAY( log_sel_slp ); // selectivity paramaters for logistic; n = [2, n_selectivities, nsex]
527 PARAMETER_ARRAY( sel_inf ); // selectivity paramaters for logistic; n = [2, n_selectivities, nsex]
528 PARAMETER_ARRAY( log_sel_slp_dev ); // selectivity parameter deviate for logistic; n = [2, n_selectivities, nsex, n_sel_blocks]
529 PARAMETER_ARRAY( sel_inf_dev ); // selectivity parameter deviate for logistic; n = [2, n_selectivities, nsex, n_sel_blocks]
530 PARAMETER_ARRAY( log_sel_apical ); // per-sex log multiplier on the whole curve, after the form and before normalization; n = [n_selectivities, nsex]
531 PARAMETER_ARRAY( sel_dn6 ); // DoubleNormalSS3 (SS3 pattern 24) parameters on SS3's scales: peak, logit top, log asc, log desc, logit init, logit final; n = [6, n_selectivities, nsex]
532 PARAMETER_VECTOR( sel_dev_log_sd ); // Log standard deviation of selectivity; n = [1, n_selectivities]
533 PARAMETER_MATRIX( sel_curve_pen ); // Non-parametric selectivity penalty columns (Sel_curve_pen1-3), read differently per form -- see the penalty blocks in section 13.7
534
535 // -- 3.6. Data variance
536 //FIXME: remove log_sd terms below
537 PARAMETER_VECTOR( index_log_sd ); // Log standard deviation of survey index time-series; n = [1, n_index]
538 PARAMETER_VECTOR( catch_log_sd ); // Log standard deviation of fishery catch time-series; n = [1, n_fsh]
539 PARAMETER_VECTOR( comp_weights ); // Weights for composition data
540 PARAMETER_VECTOR( caal_weights ); // Weights for CAAL data
541 vector<Type> DM_pars_comp = exp(comp_weights); // Dirichlet-multinomial scalars
542 vector<Type> DM_pars_caal = exp(caal_weights); // Dirichlet-multinomial scalars
543
544 PARAMETER_VECTOR( diet_comp_weights ); // Weights for diet composition data
545 vector<Type> DM_diet_pars = exp(diet_comp_weights);// Dirichlet-multinomial scalars
546
547 // 3.8. Gamma selectivity parameters
548 PARAMETER_VECTOR( log_gam_a ); // Log predator selectivity; n = [1,nspp]; FIXME: bounds = 1.0e-10 and 19.9
549 PARAMETER_VECTOR( log_gam_b ); // Log predator selectivity; n = [1,nspp]; FIXME: bounds = -5.2 and 10
550
551 // 3.9. Preference
552 PARAMETER_MATRIX( log_phi ); // Species preference coefficient; n = [nspp, nspp]
553
554
555 /** ------------------------------------------------------------------------ //
556 * 4. DERIVED QUANTITIES SECTION ---- //
557 * ------------------------------------------------------------------------- */
558
559 // 4.1. Derived indices
560 int n_flt = flt_type.size();
561 vector<int> joint_adjust(comp_obs.rows()); joint_adjust.setZero();
562 Type penalty = 0.0;
563 Type ricker_intercept = 0.0;
564
565 // -- 4.2. Growth
566 array<Type> growth_matrix(nspp * 2 + n_flt, max_sex, max_age, max_nlengths_pop, nyrs); growth_matrix.setZero(); // Growth transition matrix on the POPULATION length bins, per fleet and per species derived quantity (biomass, ssb)
567 array<Type> weight_hat(nspp * 2 + n_flt, max_sex, max_age, nyrs); weight_hat.setZero(); // Estimated weight-at-age, per fleet and per species derived quantity (biomass, ssb)
568 array<Type> mat_weight_hat(nspp * 2 + n_flt, max_sex, max_age, nyrs); mat_weight_hat.setZero(); // Mature weight-at-age (kg), maturity-at-length species only
569 array<Type> length_hat(nspp * 2 + n_flt, max_sex, max_age, nyrs); length_hat.setZero(); // Estimated length-at-age, per fleet and per species derived quantity (biomass, ssb)
570
571 // -- 4.3. Estimated population quantities
572 vector<Type> pop_scalar = exp(log_pop_scalar); // Multiplier on input numbers-at-age, per species
573 vector<Type> avg_R(nspp); avg_R.setZero(); // Mean recruitment of hindcast
574 matrix<Type> R_hat(nspp, nyrs); R_hat.setZero(); // Expected recruitment given SR curve
575 matrix<Type> mort_sum(nspp, max_age); mort_sum.setZero();
576 // Fishery selectivity at age used by initMode 6 to spread Finit over ages;
577 // filled in section 5.12.1, and 1 everywhere for every other mode.
578 array<Type> sel_init(nspp, max_sex, max_age); sel_init.setZero();
579 // Initial age structure WITHOUT the initial deviates: the equilibrium they are applied
580 // to. Only the equilibrium catch reads it, and that is the quantity it is defined on --
581 // SS3 computes the equilibrium catch from the deviation-free equilibrium, not from the
582 // year-1 numbers. Filled alongside N_at_age in section 6.5 so the two cannot drift
583 // apart.
584 array<Type> N_eq(nspp, max_sex, max_age); N_eq.setZero();
585 matrix<Type> R0(nspp, nyrs); R0.setZero(); // Equilibrium recruitment at F = 0.
586 matrix<Type> alpha(nspp, nyrs); alpha.setZero(); // Stock recruit alpha
587 matrix<Type> Beta(nspp, nyrs); Beta.setZero(); // Stock recruit beta
588 matrix<Type> steepness(nspp, nyrs); steepness.setZero(); // Expected % of R0 at 20% SSB0.
589 vector<Type> R_init(nspp); R_init.setZero(); // Equilibrium recruitment at F = Finit (non-equilibrium).
590 matrix<Type> R(nspp, nyrs); R.setZero(); // Estimated recruitment (thousands of fish; n-at-age is in thousands)
591 array<Type> biomass_at_age(nspp, max_sex, max_age, nyrs); biomass_at_age.setZero();// Estimated biomass-at-age (mt; thousands of fish x kg)
592 matrix<Type> biomass(nspp, nyrs); biomass.setZero(); // Estimated biomass (mt)
593 matrix<Type> exploitable_biomass(nspp, nyrs); exploitable_biomass.setZero(); // Estimated exploitable biomass (mt)
594 matrix<Type> ssb(nspp, nyrs); ssb.setZero(); // Estimated spawning stock biomass (mt)
595 matrix<Type> biomass_depletion(nspp, nyrs); biomass_depletion.setZero(); // Estimated biomass biomass_depletion
596 matrix<Type> ssb_depletion(nspp, nyrs); ssb_depletion.setZero(); // Estimated biomass_depletion of spawning stock biomass
597 array<Type> M_at_age(nspp, max_sex, max_age, nyrs); M_at_age.setZero(); // Total natural mortality at age
598 array<Type> M_at_age_dB0(nspp, max_sex, max_age, nyrs); M_at_age_dB0.setZero(); // Total natural mortality at age (dynamic B0)
599 array<Type> M_at_age_dBF(nspp, max_sex, max_age, nyrs); M_at_age_dBF.setZero(); // Total natural mortality at age (dynamic BF)
600 array<Type> M1_at_age(nspp, max_sex, max_age, nyrs); M1_at_age.setZero(); // Residual or total natural mortality at age
601 array<Type> N_at_age(nspp, max_sex, max_age, nyrs); N_at_age.setZero(); // Numbers at age
602 array<Type> avgN_at_age(nspp, max_sex, max_age, nyrs); avgN_at_age.setZero(); // Average numbers-at-age
603 array<Type> avgN_at_age_dB0(nspp, max_sex, max_age, nyrs); avgN_at_age_dB0.setZero(); // Average numbers-at-age (dynamic B0)
604 array<Type> avgN_at_age_dBF(nspp, max_sex, max_age, nyrs); avgN_at_age_dBF.setZero(); // Average numbers-at-age (dynamic BF)
605 array<Type> Z_at_age(nspp, max_sex, max_age, nyrs); Z_at_age.setZero(); // Total mortality at age
606 vector<Type> R_sd(nspp); R_sd.setZero(); // Standard deviation of recruitment variation
607 vector<Type> zero_N_pen(nspp); zero_N_pen.setZero(); // Additional penalty to add to likelihood if n-at-age goes < 0
608
609 // -- 4.4. Selectivity parameters
610 array<Type> sel_at_age(n_flt, max_sex, max_age, nyrs); sel_at_age.setZero(); // Estimated selectivity at age
611 array<Type> sel_at_length(n_flt, max_sex, max_nlengths_pop, nyrs); sel_at_length.setZero();// Estimated selectivity at length, on the POPULATION length bins
612 array<Type> avg_sel(n_flt, max_sex, nyrs_hind); avg_sel.setZero(); // Average selectivity for non-parametric up to n_sel_bins
613 array<Type> non_par_sel(n_flt, max_sex, max_bin, nyrs); non_par_sel.setZero(); // Estimated selectivity for AMAK non-parametric (pre-normalization)
614 array<Type> log_non_par_sel(n_flt, max_sex, max_bin, nyrs); log_non_par_sel.setZero(); // Same curve on the log scale, where the penalties are written (exp() underflows below about -745, so it is carried, not recovered)
615 vector<Type> sel_dev_sd(n_flt); sel_dev_sd.setZero(); // Standard deviation of selectivity deviates
616
617 // -- 4.5. Fishery components
618 matrix<Type> F_spp(nspp, nyrs); F_spp.setZero(); // Fully selected fishing mortality by species
619 matrix<Type> F_flt(n_flt, nyrs); F_flt.setZero(); // Fully selected fishing mortality by fleet
620 array<Type> F_flt_age(n_flt, max_sex, max_age, nyrs); F_flt_age.setZero(); // Estimated fishing mortality-at-age/sex for each fishery
621 array<Type> Flimit_at_age(nspp, max_sex, max_age, nyrs); Flimit_at_age.setZero(); // Estimated limit fishing mortality-at-age/sex for each species (FOFL proxy)
622 array<Type> Ftarget_at_age(nspp, max_sex, max_age, nyrs); Ftarget_at_age.setZero(); // Estimated target fishing mortality-at-age/sex for each species (max FABC proxy)
623 array<Type> F_at_age(nspp, max_sex, max_age, nyrs); F_at_age.setZero(); // Sum of annual estimated fishing mortalities for each species-at-age
624 vector<Type> catch_hat(catch_obs.rows()); catch_hat.setZero(); // Estimated fishery yield/numbers (mt, or thousands of fish)
625 vector<Type> max_catch_hat(catch_obs.rows()); max_catch_hat.setZero(); // Estimated exploitable biomass/numbers by fleet (mt, or thousands of fish)
626 // As index_sd below, for the fishery. Catch is lognormal throughout, so this is
627 // always a log-scale sd (unitless), not a log of anything. rename_output()
628 // also writes it as log_catch_sd, the name fits saved before 5.9.0 carry.
629 vector<Type> catch_sd(catch_obs.rows()); catch_sd.setZero();
630 // Concentrated (analytical) catch sd used when Estimate_catch_sd = 2; a
631 // log-scale sd (unitless), not a log. Mirrors index_analytical_sd below.
632 vector<Type> catch_analytical_sd(n_flt); catch_analytical_sd.setZero();
633 vector<Type> catch_n_obs(n_flt); catch_n_obs.setZero(); // fitted catch rows per fleet
634
635 // -- 4.6. Biological reference points
636 array<Type> NByage0(nspp, max_sex, max_age, nyrs); NByage0.setZero(); // Numbers at age at mean recruitment and F = 0
637 array<Type> NByageF(nspp, max_sex, max_age, nyrs); NByageF.setZero(); // Numbers at age at mean recruitment and F = Ftarget; the age structure behind SBF
638 array<Type> N_at_age_dB0(nspp, max_sex, max_age, nyrs); N_at_age_dB0.setZero(); // Numbers at age at F = 0 (accounts for annual recruitment)
639 array<Type> N_at_age_dBF(nspp, max_sex, max_age, nyrs); N_at_age_dBF.setZero(); // Female numbers at age at F = Ftarget (accounts for annual recruitment)
640 matrix<Type> DynamicSB0(nspp, nyrs); DynamicSB0.setZero(); // Estimated dynamic spawning biomass at F = 0 (accounts for S-R curve)
641 matrix<Type> DynamicB0(nspp, nyrs); DynamicB0.setZero(); // Estimated dynamic biomass at F = 0 (accounts for S-R curve)
642 matrix<Type> DynamicSBF(nspp, nyrs); DynamicSBF.setZero(); // Estimated dynamic spawning biomass at F = Ftarget (accounts for S-R curve)
643 // Zeroed explicitly, like every other array here: the recursion writes only
644 // ages below a species' own nages, and nothing at all under predation, so the
645 // rest is read from the REPORT as whatever it was initialised to.
646 array<Type> NbyageSPR(4, nspp, max_age); NbyageSPR.setZero(); // Numbers at age per recruit for the SPR reference points; slots 0-3 are F = 0, Flimit, Ftarget, Finit
647 vector<Type> SPRlimit(nspp); SPRlimit.setZero(); // Estimated Plimit SPR
648 vector<Type> SPRtarget(nspp); SPRtarget.setZero(); // Estimated Ptarget SPR
649 vector<Type> SPR0(nspp); SPR0.setZero(); // Estimated spawning biomass per recruit at F = 0
650 vector<Type> SPRFinit(nspp); SPRFinit.setZero(); // Estimated spawning biomass per recruit at Finit
651 matrix<Type> SB0(nspp, nyrs); SB0.setZero(); // Estimated spawning stock biomass at F = 0 (Accounts for S-R)
652 matrix<Type> SBF(nspp, nyrs); SBF.setZero(); // Estimated spawning stock biomass at F = target (Accounts for S-R)
653 matrix<Type> B0(nspp, nyrs); B0.setZero(); // Estimated biomass at F = 0 (Accounts for S-R)
654 vector<Type> Flimit = exp(log_Flimit); // Limit F (FOFL proxy, e.g. F35%) on natural scale
655 vector<Type> Ftarget = exp(log_Ftarget); // Target F (max FABC proxy, e.g. F40%) on natural scale
656 vector<Type> Finit = exp(log_Finit); // Initial F for non-equilibrium age-structure
657 vector<Type> index_q_mult(index_q_beta.cols()); index_q_mult.setZero(); // Environmental design matrix for q
658 vector<Type> M1_mult(M1_beta.dim(2)); M1_mult.setZero(); // Environmental design matrix for M1
659 vector<Type> beta_q_tmp(index_q_beta.cols()); beta_q_tmp.setZero(); // Q beta parameters by species, for the matrix multiply
660 vector<Type> env_q_tmp(index_q_beta.cols()); env_q_tmp.setZero(); // Q env data by year, for the matrix multiply
661 vector<Type> beta_M1_tmp(M1_beta.dim(2)); beta_M1_tmp.setZero(); // Beta parameters by species, for the matrix multiply
662 vector<Type> env_M1_tmp(M1_beta.dim(2)); env_M1_tmp.setZero(); // Env data by year, for the matrix multiply
663 matrix<Type> proj_F(nspp, nyrs); proj_F.setZero(); // Projected F (Fabc/Ftac/etc) using harvest control rule
664
665 // -- 4.7. Survey components
666 vector<Type> index_q_sd(n_flt); index_q_sd.setZero(); // Vector of standard deviation of survey catchability prior
667 vector<Type> index_q_dev_sd(n_flt); index_q_dev_sd.setZero(); // Vector of standard deviation of time-varying survey catchability deviation
668 vector<Type> index_hat(index_obs.rows()); index_hat.setZero(); // Estimated survey index; mt or thousands of fish per the fleet's Observation_units
669 // Rejection bookkeeping for the natural-scale survey draws (sim_mod only), per
670 // index_obs row: a non-zero tries count marks the rows a rejection-capable branch drew
671 // -- the untruncated "Normal" family and MVN/MVNORM, which must redraw a non-positive
672 // index. "TruncatedNormal" draws by inverse CDF and never enters here. sim_mod() sizes
673 // the draw/density gap from index_hat and index_sd, not from these counts.
674 vector<Type> index_trunc_tries_sim(index_obs.rows()); index_trunc_tries_sim.setZero();
675 vector<Type> index_trunc_rejects_sim(index_obs.rows()); index_trunc_rejects_sim.setZero();
676 // The marginal sd the draw itself used, so sim_mod() can size the truncated mass as
677 // Phi(-index_hat/sd). Separate from index_sd because a covariance fleet draws from
678 // index_cov_mat, leaving its index_sd whatever the (unused) Index_sd column carried.
679 vector<Type> index_trunc_sd_sim(index_obs.rows()); index_trunc_sd_sim.setZero();
680 // The redraw budget ACTUALLY applied to each row, which is what sim_mod() quotes: the
681 // constant below on the univariate branch, and that scaled by the fleet's row count on
682 // the correlated one, which rejects the whole vector at once, so its joint rejection
683 // probability climbs with the row count.
684 vector<Type> index_trunc_budget_sim(index_obs.rows()); index_trunc_budget_sim.setZero();
685 const int index_trunc_max_tries = 100;
686 // The observation sd the index likelihood actually used for each row, whichever of the
687 // three est_sigma_index routes supplied it. NOT a log: the sd itself, on the scale that
688 // fleet's Index_distribution works on -- a log-scale sd (unitless) for Lognormal, an
689 // ABSOLUTE sd in the units of the index for the natural-scale families. rename_output()
690 // also writes it as log_index_sd, the name fits saved before 5.9.0 carry.
691 vector<Type> index_sd(index_obs.rows()); index_sd.setZero();
692 vector<Type> index_analytical_sd(n_flt); index_analytical_sd.setZero(); // Concentrated (analytical) index sd used when Estimate_index_sd = 2; a log-scale sd, not a log
693 vector<Type> index_q_analytical(n_flt); index_q_analytical.setZero(); // Analytical catchability (not an sd) used when Catchability = 3; "AnalyticalArith" (7) has its own block at 8.2b
694 matrix<Type> index_q(n_flt, nyrs_hind); index_q.setZero(); // Estimated survey catchability //FIXME: extend out to full time-series
695 vector<Type> index_n_obs(n_flt); index_n_obs.setZero(); // Observations per survey time series
696
697 // -- 4.8. Composition data
698 // age_hat / age_obs_hat are indexed by AGE whatever dimension the fleet's composition
699 // data are on -- a length composition is built at age, then converted through the
700 // age-length transition -- so the width is the widest age index any row writes:
701 // nages(sp), or nages(sp) * 2 for a joint-sex row (comp_ctl column 2 == 3), never
702 // narrower than comp_obs. Sizing from comp_obs alone gives only the workbook's Comp_
703 // columns, i.e. nlengths on a length-only model, and Eigen does not bounds-check in a
704 // release build, so nlengths < nages would write silently into adjacent memory. Species
705 // is range-checked because nothing between rearrange_data() and MakeADFun validates
706 // comp_data$Species, and a non-numeric one arrives as NA_integer_.
707 int max_age_cols = static_cast<int>(comp_obs.cols());
708 for(int comp_row = 0; comp_row < comp_ctl.rows(); comp_row++){
709 int comp_row_sp = comp_ctl(comp_row, 1) - 1;
710 if((comp_row_sp < 0) | (comp_row_sp >= nspp)){
711 error("comp_data Species on row %d is not in 1..nspp", comp_row + 1);
712 }
713 int comp_row_cols = (comp_ctl(comp_row, 2) == 3) ? nages(comp_row_sp) * 2 : nages(comp_row_sp);
714 if(comp_row_cols > max_age_cols){ max_age_cols = comp_row_cols; }
715 }
716 vector<Type> n_hat(comp_obs.rows()) ; n_hat.setZero() ; // Estimated catch (numbers)
717 matrix<Type> age_hat(comp_obs.rows(), max_age_cols); age_hat.setZero(); // Estimated catch at true age
718 matrix<Type> age_obs_hat(comp_obs.rows(), max_age_cols); age_obs_hat.setZero(); // Estimated catch at observed age (accounts for ageing error)
719 matrix<Type> comp_hat = comp_obs; comp_hat.setZero(); // Estimated comp
720 matrix<Type> caal_hat = caal_obs; caal_hat.setZero(); // Estimated CAAL
721 array<Type> pred_CAAL(n_flt, max_sex, max_age, max_nlengths, nyrs); pred_CAAL.setZero(); // Predicted CAAL for each fleet
722
723 // -- 4.9. Ration components
724 array<Type> consumption_at_age( nspp, max_sex, max_age, nyrs ); consumption_at_age.setZero(); // Annual ration at age (kg/yr)
725 matrix<Type> fT( nspp, nyrs ); fT.setZero(); // Pre-allocation of temperature function of consumption
726
727 // -- 4.10. Diet components
728 array<Type> diet_prop(nspp * max_sex, nspp * max_sex, max_age, max_age, nyrs); diet_prop.setZero(); // Stomach proportion by weight U
729 array<Type> diet_prop_hat(nspp * max_sex, nspp * max_sex, max_age, max_age, nyrs); diet_prop_hat.setZero(); // Predicted stomach proportion by weight U
730 array<Type> other_food_diet_prop(nspp, max_sex, max_age, nyrs); other_food_diet_prop.setZero(); // Other food diet proportion by weight
731 matrix<Type> diet_hat = diet_obs; diet_hat.setZero(); // Estimated stomach proportion by weight U (formated following data input)
732
733 // -- 4.11. Suitability components
734 array<Type> avail_food(nspp, max_sex, max_age, nyrs); avail_food.setZero(); // Available food to predator
735 array<Type> avail_food_dB0(nspp, max_sex, max_age, nyrs); avail_food_dB0.setZero(); // Available food to predator (dynamic B0)
736 array<Type> avail_food_dBF(nspp, max_sex, max_age, nyrs); avail_food_dBF.setZero(); // Available food to predator (dynamic BF)
737 array<Type> stom_div_bio(nspp * max_sex, nspp * max_sex, max_age, max_age, nyrs); stom_div_bio.setZero(); // Stomach proportion over biomass; U/ (W * N)
738 array<Type> suitability(nspp * max_sex, nspp * max_sex, max_age, max_age, nyrs); suitability.setZero(); // Suitability/gamma selectivity of predator age u on prey age a
739 array<Type> suit_other(nspp, max_sex, max_age, nyrs); suit_other.setZero(); // Suitability not accounted for by the included prey
740 array<Type> other_diet_prop_hat(nspp, max_sex, max_age, nyrs); other_diet_prop_hat.setZero(); // Diet of prey not included in the model
741
742 // -- 4.12. Suitability parameters
743 vector<Type> gam_a = exp(log_gam_a); // Predator size-selectivity: shape parameter for gamma suitability, mean for normal of logs
744 vector<Type> gam_b = exp(log_gam_b); // Predator size-selectivity: scale parameter for gamma suitability, sd for normal of logs
745 matrix<Type> vulnerability(nspp, nspp); vulnerability.setZero(); // Predator-prey preference coefficients
746 vector<Type> vulnerability_other(nspp); vulnerability_other.setZero(); // Preference for other food
747
748 // -- 4.13. Predation components
749 array<Type> M2_at_age(nspp, max_sex, max_age, nyrs); M2_at_age.setZero(); // Predation mortality at age
750 array<Type> M2_at_age_dB0(nspp, max_sex, max_age, nyrs); M2_at_age_dB0.setZero(); // Predation mortality at age (dynamic B0)
751 array<Type> M2_at_age_dBF(nspp, max_sex, max_age, nyrs); M2_at_age_dBF.setZero(); // Predation mortality at age (dynamic BF)
752 array<Type> M2_prop(nspp * max_sex, nspp * max_sex, max_age, max_age, nyrs); M2_prop.setZero(); // Each predator-at-age's contribution to prey M2 (yr^-1), not a share of it
753 array<Type> B_eaten(nspp * max_sex, nspp * max_sex, max_age, max_age, nyrs); B_eaten.setZero(); // Biomass of prey eaten via predation by a predator at age
754 array<Type> B_eaten_as_prey(nspp, max_sex, max_age, nyrs); B_eaten_as_prey.setZero(); // Biomass eaten as prey via predation
755
756 /** ------------------------------------------------------------------------ //
757 * 5. INITIAL CALCULATIONS //
758 * ------------------------------------------------------------------------- */
759
760 // 5.1. DATA VARIANCE TERMS
761 R_sd = exp(R_log_sd); // Convert log sd to natural scale
762 sel_dev_sd = exp(sel_dev_log_sd) ;
763 index_q_sd = exp(index_q_log_sd) ;
764 index_q_dev_sd = exp(index_q_dev_log_sd) ;
765 Cindex -=1; // Subtract 1 from Cindex to deal with indexing start at 0
766
767
768 // -- 5.2. SIMULATE LINKAGE RANDOM EFFECTS (sim_mod(simulate = TRUE))
769 // A random linkage on any of the five processes draws here, before they read it, from
770 // its density in rceattle_re_group_nll() (linkage.hpp, called in 13.14). Slots are in
771 // time order within a group, so a random walk starts from the fitted first slot, and an
772 // AR1 draw is standardized then scaled by the MARGINAL sd sigma.
773 //
774 // beta_linkage_re_drawn_sim marks which slots this call wrote. The reported deviation
775 // vector spans EVERY random linkage, drawn or not, so without the mask a caller would
776 // score recovery on slots left at their fitted values.
777 SIMULATE {
778 vector<Type> beta_linkage_re_drawn_sim(linkage_re_sigma.size());
779 beta_linkage_re_drawn_sim.setZero();
780 if (log_sigma_linkage.size() > 0 && simulate_period(0) == 1) {
781 int n_grp = log_sigma_linkage.size();
782
783 // Drawn only if EVERY row using the group names a process the caller asked for.
784 // A group belongs to one process (.assign_re_registry() keys it on process,
785 // parameter, stratum and random term), so this reads that process's simulate_state.
786 vector<int> grp_used(n_grp), grp_draw(n_grp);
787 grp_used.setZero();
788 grp_draw.fill(1);
789 for (int i = 0; i < linkage_re_index.size(); ++i) {
790 if (linkage_re_index(i) < 0) continue;
791 int grp = linkage_re_sigma(linkage_re_index(i));
792 grp_used(grp) = 1;
793 // linkage_process also codes composition (5), which simulate_state does not cover
794 // because composition linkages carry priors, not random effects. Treat anything
795 // outside the five as "not asked for" rather than reading past simulate_state.
796 int proc = linkage_process(i);
797 if (proc < 0 || proc >= simulate_state.size() || simulate_state(proc) != 1) {
798 grp_draw(grp) = 0;
799 }
800 }
801
802 for (int grp = 0; grp < n_grp; ++grp) {
803 if (grp_used(grp) == 0 || grp_draw(grp) == 0) continue;
804
805 // Rogers QAR1: the latent is measured by an observed covariate series, so
806 // redrawing it alone would leave the two describing different histories.
807 // Left as fitted; sim_mod() warns that it was.
808 if (linkage_re_obs(grp) >= 0) continue;
809
810 vector<int> slots = rceattle_group_slots(linkage_re_sigma, grp);
811 int len = slots.size();
812 if (len == 0) continue;
813
814 Type sigma = exp(log_sigma_linkage(grp));
815 vector<Type> re(len);
816 int st = linkage_re_struct(grp);
817
818 // Each slot sits in beta_linkage_re if integrated, else beta_linkage_re_pen, at
819 // position linkage_re_slot(slot).
820 if (st == RCEATTLE_RE_RW) {
821 re(0) = linkage_re_integrate(slots(0))
822 ? beta_linkage_re(linkage_re_slot(slots(0)))
823 : beta_linkage_re_pen(linkage_re_slot(slots(0)));
824 for (int t = 1; t < len; ++t) re(t) = re(t - 1) + rnorm(Type(0), sigma);
825 } else if (st == RCEATTLE_RE_AR1) {
826 Type rho = rho_trans(trans_rho_linkage(linkage_re_rho(grp)));
827 AR1(rho).simulate(re);
828 re *= sigma;
829 } else { // RCEATTLE_RE_IID
830 for (int t = 0; t < len; ++t) re(t) = rnorm(Type(0), sigma);
831 }
832
833 for (int t = 0; t < len; ++t) {
834 if (linkage_re_integrate(slots(t))) {
835 beta_linkage_re(linkage_re_slot(slots(t))) = re(t);
836 } else {
837 beta_linkage_re_pen(linkage_re_slot(slots(t))) = re(t);
838 }
839 // A random walk keeps its first slot at the fitted level (it has no density
840 // term of its own), so that slot is written but not drawn.
841 if (!(st == RCEATTLE_RE_RW && t == 0)) beta_linkage_re_drawn_sim(slots(t)) = 1;
842 }
843 }
844 }
845 REPORT(beta_linkage_re_drawn_sim);
846 }
847
848 // -- 5.2b. EFFECTIVE LINKAGE COEFFICIENTS (beta_linkage_re_all, beta_linkage_eff)
849 // Every deviation in one vector, indexed by RE slot: integrated deviations in
850 // beta_linkage_re, penalized ones in beta_linkage_re_pen, with linkage_re_slot giving
851 // the position within whichever holds it. With no penalized group it copies
852 // beta_linkage_re.
853 vector<Type> beta_linkage_re_all(linkage_re_sigma.size());
854 for (int s = 0; s < beta_linkage_re_all.size(); ++s) {
855 beta_linkage_re_all(s) = linkage_re_integrate(s)
856 ? beta_linkage_re(linkage_re_slot(s))
857 : beta_linkage_re_pen(linkage_re_slot(s));
858 }
859
860 // THE _sim NAMING RULE, stated once here and referred to below. TMB never clears the
861 // report environment, so anything REPORTed inside a SIMULATE block stays visible in
862 // obj$report() for this object's life -- under the observed object's own name, a random
863 // replicate would read as the data. Every draw is reported under a name ending _sim,
864 // and the composition, CAAL and diet draws go into separate copies (comp_sim, caal_sim,
865 // diet_sim) for the same reason.
866 SIMULATE {
867 vector<Type> beta_linkage_re_sim = beta_linkage_re_all;
868 REPORT(beta_linkage_re_sim);
869 }
870
871 // Effective linkage coefficient per table row. A fixed row uses its beta_linkage(i); a
872 // random-effect row (linkage_re_index >= 0) takes its deviation from the slot-space
873 // vector, whose density is JNLL_LINKAGE_RE (13.14). The accumulators below see one beta vector
874 // either way; with no RE rows this copies beta_linkage.
875 vector<Type> beta_linkage_eff = beta_linkage;
876 for (int i = 0; i < beta_linkage_eff.size(); ++i) {
877 if (linkage_re_index(i) >= 0) {
878 Type z = beta_linkage_re_all(linkage_re_index(i));
879 // Rogers QAR1: an observed AR1 latent enters the target scaled by an estimated
880 // effect size, beta_linkage_obs. Unobserved groups keep the deviate as is.
881 int grp = linkage_re_sigma(linkage_re_index(i));
882 if (linkage_re_obs(grp) >= 0) z *= beta_linkage_obs(linkage_re_obs(grp));
883 beta_linkage_eff(i) = z;
884 }
885 }
886
887 // The table every rceattle_apply_*_linkages() call below reads (linkage.hpp): each row
888 // adds offset = beta * X(yr, X_col) to one parameter in one stratum (2.3b).
889 LinkageTable<Type> linkage_table = {
890 linkage_process, linkage_param, // which parameter
891 linkage_species, linkage_sex, linkage_age_bin, linkage_fleet, // which stratum (0 = all)
892 linkage_X_col, linkage_link, linkage_X, beta_linkage_eff}; // the offset
893
894
895 // 5.3. CATCHABILITY
896 // Environmental linkage offsets, one pass per link scale. Zero unless a
897 // q linkage was supplied, so models without one are unaffected.
898 matrix<Type> q_linkage_offset(n_flt, nyrs_hind); q_linkage_offset.setZero();
899 matrix<Type> q_linkage_offset_nat(n_flt, nyrs_hind); q_linkage_offset_nat.setZero();
900 matrix<Type> q_linkage_log_mult(n_flt, nyrs_hind); q_linkage_log_mult.setZero();
901
902 // Log-link rows into the log-scale offset, identity-link rows into the natural-scale
903 // one, exponential rows into the log of the multiplier on log q (applied as exp()).
904 rceattle_apply_q_linkages(q_linkage_offset, RCEATTLE_LINK_LOG, linkage_table, n_flt, nyrs_hind);
905 rceattle_apply_q_linkages(q_linkage_offset_nat, RCEATTLE_LINK_IDENTITY, linkage_table, n_flt, nyrs_hind);
906 rceattle_apply_q_linkages(q_linkage_log_mult, RCEATTLE_LINK_EXPONENTIAL, linkage_table, n_flt, nyrs_hind);
907
908 REPORT(q_linkage_offset);
909 REPORT(q_linkage_offset_nat);
910 REPORT(q_linkage_log_mult);
911
912 for(flt = 0; flt < n_flt; flt++){
913 for(yr = 0; yr < nyrs_hind; yr++){
914 // q_y = exp((log q + S_log) * exp(S_exp) + index_q_dev) + S_id. The deviate is added
915 // after the product, as SS3 applies its devs after the env link; inside, its sd
916 // would vary by year.
917 index_q(flt, yr) = exp((index_log_q(flt) + q_linkage_offset(flt, yr))
918 * exp(q_linkage_log_mult(flt, yr))
919 + index_q_dev(flt, yr))
920 + q_linkage_offset_nat(flt, yr);
921
922 // Catchability 5 ("Environmental", legacy): log q_y = log q + beta * env_index,
923 // replacing the line above; R refuses a q linkage on such a fleet.
924 if(est_index_q(flt) == 5){
925 beta_q_tmp = index_q_beta.row(flt);
926 env_q_tmp = env_index.row(yr) ;
927 index_q_mult = env_q_tmp * beta_q_tmp;
928 index_q(flt, yr) = exp(index_log_q(flt) + (index_q_mult).sum());
929 }
930 }
931 }
932
933 matrix<int> r_sexes(diet_obs.rows(), 2); r_sexes.setZero();
934 matrix<int> k_sexes(diet_obs.rows(), 2); k_sexes.setZero();
935
936
937 // 5.4. MATURITY, Finit, AND SEX RATIO
938 // -- for SSB derivation, not SPR
939 matrix<Type> mature_females = maturity;
940 for( sp = 0; sp < nspp ; sp++) {
941
942 if(initMode < 3 || initMode == 5){
943 Finit(sp) = 0; // An equilibrium start takes Finit = 0, so R_init and R0 agree. initMode 5 (OffsetEquilibrium) is an F = 0 equilibrium seeded by first-year recruitment.
944 }
945
946 // Sex ratio for SSB derivation
947 for( age = 0 ; age < nages(sp); age++ ) {
948 if(nsex(sp) == 1){
949 mature_females( sp, age ) = maturity( sp, age ) * sex_ratio(sp, age); // Multiply sex_ratio and maturity for 1 sex models
950 }
951 }
952 }
953
954 // 5.5. OFFSETS
955 // One array per link scale per process: `*_linkage_offset` from log-link rows (S_log),
956 // `*_linkage_offset_nat` from identity-link rows (S_id), combined as in section 2.3b.
957 // Catchability has a third, `q_linkage_log_mult` (S_exp, 5.3). All zero with no linkages.
958 //
959 // - RECRUITMENT OFFSETS
960 array<Type> recruitment_linkage_offset(nspp,
962 nyrs);
963 array<Type> recruitment_linkage_offset_nat(nspp,
965 nyrs);
966 recruitment_linkage_offset.setZero();
967 recruitment_linkage_offset_nat.setZero();
968 rceattle_apply_recruitment_linkages(recruitment_linkage_offset, RCEATTLE_LINK_LOG, linkage_table, nspp, nyrs);
969 rceattle_apply_recruitment_linkages(recruitment_linkage_offset_nat, RCEATTLE_LINK_IDENTITY, linkage_table, nspp, nyrs);
970 REPORT(recruitment_linkage_offset);
971 REPORT(recruitment_linkage_offset_nat);
972
973 // - M OFFSETS
974 array<Type> M_linkage_offset(nspp, max_sex, max_age, nyrs);
975 array<Type> M_linkage_offset_nat(nspp, max_sex, max_age, nyrs);
976 M_linkage_offset.setZero();
977 M_linkage_offset_nat.setZero();
978 rceattle_apply_M_linkages(M_linkage_offset, RCEATTLE_LINK_LOG, linkage_table, nspp, nsex, nages, nyrs);
979 rceattle_apply_M_linkages(M_linkage_offset_nat, RCEATTLE_LINK_IDENTITY, linkage_table, nspp, nsex, nages, nyrs);
980 REPORT(M_linkage_offset);
981 REPORT(M_linkage_offset_nat);
982
983
984 // - GROWTH OFFSETS
985 array<Type> growth_linkage_offset(nspp, max_sex, nyrs, RCEATTLE_N_GROWTH_PARAMS);
986 array<Type> growth_linkage_offset_nat(nspp, max_sex, nyrs, RCEATTLE_N_GROWTH_PARAMS);
987 growth_linkage_offset.setZero();
988 growth_linkage_offset_nat.setZero();
989 rceattle_apply_growth_linkages(growth_linkage_offset, RCEATTLE_LINK_LOG, linkage_table, nspp, nsex, nyrs);
990 rceattle_apply_growth_linkages(growth_linkage_offset_nat, RCEATTLE_LINK_IDENTITY, linkage_table, nspp, nsex, nyrs);
991 REPORT(growth_linkage_offset);
992 REPORT(growth_linkage_offset_nat);
993
994
995 // 5.6. RECRUITMENT PARAMETERS
996 // R0, alpha, Beta: p_y = exp(log p + S_log) + S_id (2.3b).
997 // TODO: a negative nat_offset can make R0, alpha or Beta non-positive; only hindcast R, R_hat and the penalty curve are floored (issue #234).
998 for(sp = 0; sp < nspp; sp++){
999 for(yr = 0; yr < nyrs; yr++){
1000 R0(sp, yr) = exp(rec_pars(sp, RCEATTLE_REC_R0) + recruitment_linkage_offset(sp, RCEATTLE_REC_R0, yr))
1001 + recruitment_linkage_offset_nat(sp, RCEATTLE_REC_R0, yr);
1002 alpha(sp, yr) = exp(rec_pars(sp, RCEATTLE_REC_ALPHA) + recruitment_linkage_offset(sp, RCEATTLE_REC_ALPHA, yr))
1003 + recruitment_linkage_offset_nat(sp, RCEATTLE_REC_ALPHA, yr);
1004 Beta(sp, yr) = exp(rec_pars(sp, RCEATTLE_REC_BETA) + recruitment_linkage_offset(sp, RCEATTLE_REC_BETA, yr))
1005 + recruitment_linkage_offset_nat(sp, RCEATTLE_REC_BETA, yr);
1006 }
1007 }
1008
1009
1010 // 5.7. GROWTH
1011 // -- Rearrange growth parameters. Log-link offset enters additively
1012 // on the log scale (multiplicative on natural K/L1/Linf/m);
1013 // identity-link offset enters additively on the natural scale.
1014 array<Type> growth_parameters(nspp, max_sex, nyrs, RCEATTLE_N_GROWTH_PARAMS); growth_parameters.setZero(); // K, L1, Linf, m
1015 for(sp = 0; sp < nspp; sp++){
1016 for(sex = 0; sex < nsex(sp); sex ++){
1017 for(yr = 0; yr < nyrs; yr++){
1018 for(int par = 0; par < RCEATTLE_N_GROWTH_PARAMS; par++){
1019 growth_parameters(sp, sex, yr, par) = exp(
1020 log_growth_pars(sp, sex, par)
1021 + growth_linkage_offset(sp, sex, yr, par)
1022 ) + growth_linkage_offset_nat(sp, sex, yr, par);
1023 }
1024 }
1025 }
1026 }
1027 REPORT(growth_parameters);
1028
1029 // -- Calculate weight
1031 weight_hat,
1032 length_hat,
1033 growth_matrix,
1034 mat_weight_hat,
1035 weight_obs,
1036 growth_model,
1037 growth_sd_style,
1038 growth_sd_form,
1039 growth_plus_length,
1040 plus_group_decay,
1041 nspp,
1042 nyrs,
1043 nyrs_hind,
1044 n_flt,
1045 flt_spp,
1046 flt_month,
1047 nsex,
1048 minage,
1049 growth_age_L1,
1050 nages,
1051 nlengths,
1052 pop_wt_index,
1053 ssb_wt_index,
1054 flt_wt_index,
1055 spawn_month,
1056 lengths,
1057 nlengths_pop,
1058 lengths_pop,
1059 growth_parameters,
1060 growth_log_sd,
1061 weight_length_pars,
1062 mat_len_use,
1063 mat_len_pars,
1064 log_M1
1065 );
1066
1067 // -- Spawning output per fish (kg) at spawning time, female-only for a
1068 // two-sex species. Weight x maturity-at-age x sex ratio by default; with
1069 // maturity-at-length it is the mature weight integrated over the length
1070 // distribution at spawning, so maturity and weight vary together with length.
1071 array<Type> spawn_output(nspp, max_age, nyrs); spawn_output.setZero();
1072 for(sp = 0; sp < nspp; sp++){
1073 wt_idx_ssb = 2 * sp + 1;
1074 for(age = 0; age < nages(sp); age++){
1075 for(yr = 0; yr < nyrs; yr++){
1076 if(mat_len_use(sp) == 1){
1077 spawn_output(sp, age, yr) = mat_weight_hat(wt_idx_ssb, 0, age, yr) * ((nsex(sp) == 1) ? sex_ratio(sp, age) : Type(1.0));
1078 } else {
1079 spawn_output(sp, age, yr) = weight_hat(wt_idx_ssb, 0, age, yr) * mature_females(sp, age);
1080 }
1081 }
1082 }
1083 }
1084 REPORT(spawn_output);
1085
1086
1087 // 5.8. SELECTIVITY
1088 // Environmental-linkage offsets. Two arrays per parameter family (log and
1089 // natural scale), all zero unless a selectivity linkage was supplied.
1090 array<Type> sel_slp_off (2, n_flt, max_sex, nyrs); sel_slp_off.setZero();
1091 array<Type> sel_slp_off_nat (2, n_flt, max_sex, nyrs); sel_slp_off_nat.setZero();
1092 array<Type> sel_inf_off (2, n_flt, max_sex, nyrs); sel_inf_off.setZero();
1093 array<Type> sel_inf_off_nat (2, n_flt, max_sex, nyrs); sel_inf_off_nat.setZero();
1094 array<Type> sel_coff_off (n_flt, max_sex, max_bin, nyrs); sel_coff_off.setZero();
1095 array<Type> sel_coff_off_nat (n_flt, max_sex, max_bin, nyrs); sel_coff_off_nat.setZero();
1096 array<Type> sel_apical_off (n_flt, max_sex, nyrs); sel_apical_off.setZero();
1097 array<Type> sel_apical_off_nat (n_flt, max_sex, nyrs); sel_apical_off_nat.setZero();
1098 array<Type> sel_dn6_off (6, n_flt, max_sex, nyrs); sel_dn6_off.setZero();
1099 array<Type> sel_dn6_off_nat (6, n_flt, max_sex, nyrs); sel_dn6_off_nat.setZero();
1100
1101 // The per-sex apical multiplier is compiled in only when a selectivity
1102 // linkage names it (RCEATTLE_SEL_APICAL); otherwise the curve is untouched and the AD
1103 // tape of every existing model is unchanged.
1104 int sel_apical_on = 0;
1105 for (int i = 0; i < linkage_process.size(); i++) {
1106 if (linkage_process(i) == RCEATTLE_PROC_SEL && linkage_param(i) == RCEATTLE_SEL_APICAL) sel_apical_on = 1;
1107 }
1108
1109 rceattle_apply_sel_linkages( // log link, into the log-scale arrays
1110 sel_slp_off, sel_inf_off, sel_coff_off, sel_apical_off, sel_dn6_off,
1111 RCEATTLE_LINK_LOG, linkage_table, n_flt, max_sex, max_bin, nyrs);
1112 rceattle_apply_sel_linkages( // identity link, into the natural-scale arrays
1113 sel_slp_off_nat, sel_inf_off_nat, sel_coff_off_nat, sel_apical_off_nat, sel_dn6_off_nat,
1114 RCEATTLE_LINK_IDENTITY, linkage_table, n_flt, max_sex, max_bin, nyrs);
1115
1116 // "sel_at_age" and "sel_at_length" modified via pass-by-reference
1117 // when "sel_at_length" is used, it is converted to "sel_at_age" using the growth matrix
1119 nspp, // Number of species.
1120 n_flt, // Number of fleets.
1121 nyrs, // Years, hindcast plus projection.
1122 nyrs_hind, // Years in the hindcast.
1123 styr, // Start year
1124 nsex, // Sexes modelled per species: 1 = combined, 2 = both.
1125 nages, // Age bins per species.
1126 nlengths, // Observation length bins per species.
1127 lengths, // Length bin boundaries matrix
1128 nlengths_pop, // Population length bins per species (= nlengths when no pop grid)
1129 lengths_pop, // Population length bin lower edges
1130 flt_spp, // Fleet to species mapping
1131 flt_sel_type, // Selectivity model type per fleet
1132 flt_varying_sel, // Time_varying_sel per fleet (picks NonParametricIntegrable's construction)
1133 flt_sel_dim, // Age or length based
1134 bin_first_selected, // Min bin selected per fleet
1135 flt_n_sel_bins, // Max estimated bins per fleet
1136 flt_sel_cap_bin, // Bin (0-based) at/after which realized non-par sel is capped flat (NonParametricPM)
1137 sel_norm_bin1, // Normalization control/bin 1
1138 sel_norm_bin2, // Normalization control/bin 2
1139 sel_norm_scope, // Normalization reference pooled across sexes?
1140 flt_sel_start_yr, // Per-fleet selectivity start year (0-based)
1141 emp_sel_obs, // Empirical observations matrix
1142 emp_sel_ctl, // Empirical control matrix
1143 log_sel_slp, // Logistic slope parameters
1144 log_sel_slp_dev, // Slope deviations
1145 sel_inf, // Inflection parameters
1146 sel_inf_dev, // Inflection deviations
1147 sel_coff, // Non-parametric coefficients
1148 sel_coff_dev, // Coefficient deviations
1149 avg_sel, // [Modified] Average selectivity
1150 non_par_sel, // [Modified] Unnormalized non-parametric selectivity
1151 log_non_par_sel, // [Modified] The same curve on the log scale, for the penalties
1152 sel_at_length, // [Modified] Final length-based selectivity
1153 sel_at_age, // [Modified] Final age-based selectivity
1154 growth_matrix, // Length to age transition matrix
1155 sel_slp_off, sel_slp_off_nat, // selectivity linkage offsets (log / natural)
1156 sel_inf_off, sel_inf_off_nat,
1157 sel_coff_off, sel_coff_off_nat,
1158 sel_apical_on, log_sel_apical, // per-sex apical height, only when a linkage names it
1159 sel_apical_off, sel_apical_off_nat,
1160 sel_dn6, sel_dn6_off, sel_dn6_off_nat, sel_dn6_ends // DoubleNormalSS3
1161 );
1162
1163 // -- Selected body weight (kg): sum_l P(l|a) s(l) w(l) / sum_l P(l|a) s(l),
1164 // SS3's "bodywt". A length-selective fleet takes the larger or smaller fish
1165 // of an age class, so its catch and survey biomass weigh that class by the
1166 // fish it selects, not by the class mean. Estimated growth only.
1167 for(flt = 0; flt < n_flt; flt++){
1168 sp = flt_spp(flt);
1169 if(growth_model(sp) == 0 || flt_sel_dim(flt) != 1 || flt_sel_type(flt) == 0) continue;
1170 int wt_idx_sel = nspp * 2 + flt;
1171 for(sex = 0; sex < nsex(sp); sex++){
1172 for(age = 0; age < nages(sp); age++){
1173 for(yr = 0; yr < nyrs; yr++){
1174 Type num = 0, den = 0;
1175 // Integrated over POPULATION bins: both the key and the curve live
1176 // there, so the selectivity weighting is applied at the resolution
1177 // the curve is defined on rather than to a bin average.
1178 for(int ln = 0; ln < nlengths_pop(sp); ln++){
1179 // The last bin takes the previous bin's width; a single-bin grid has
1180 // no neighbour to take one from, so it keeps the edge itself.
1181 Type lenmid = (ln < nlengths_pop(sp) - 1)
1182 ? (lengths_pop(sp, ln) + lengths_pop(sp, ln + 1)) / Type(2.0)
1183 : (nlengths_pop(sp) < 2
1184 ? lengths_pop(sp, ln)
1185 : lengths_pop(sp, ln) + (lengths_pop(sp, ln) - lengths_pop(sp, ln - 1)) / Type(2.0));
1186 Type p_sel = growth_matrix(wt_idx_sel, sex, age, ln, yr) * sel_at_length(flt, sex, ln, yr);
1187 num += p_sel * weight_length_pars(sp, 0) * pow(lenmid, weight_length_pars(sp, 1));
1188 den += p_sel;
1189 }
1190 // An age class the fleet does not select contributes nothing either way.
1191 weight_hat(wt_idx_sel, sex, age, yr) = num / (den + Type(1e-30));
1192 }
1193 }
1194 }
1195 }
1196
1197
1198 // 5.9. BIOENERGETICS AND CONSUMPTION
1199 // - Calculate temperature function of consumption
1200 calculate_temperature_function(fT, nspp, nyrs, Ceq, Cindex, Qc, Tcm, Tco, Tcl, CK1, CK4, env_index);
1201
1202 // - Calculate historic ration
1203 calculate_ration(consumption_at_age, nspp, nyrs, nyrs_hind, nsex, nages, Ceq, CA, CB, fday, Pvalue, fT, ration_data, weight_hat);
1204
1205
1206 // 5.10. PARAMETRIC SUITABILITY
1207 // - Estimate vulnerability (Parametric Suitability)
1208 calculate_vulnerability(vulnerability, vulnerability_other, nspp, suitMode, log_phi);
1209
1210 // - Parametric Suitability (Gamma, Lognormal, Normal)
1211 calculate_parametric_suitability(suitability, suit_other, nspp, nyrs, nsex, nages, suitMode,
1212 length_hat, weight_hat, vulnerability, vulnerability_other, gam_a, gam_b);
1213
1214
1215 // 5.11. REORGANIZE MSVPA DIET DATA
1216 // - Reorganize diet_obs content
1217 organize_diet_obs(nspp, nyrs, nyrs_hind, styr, minage, nsex,
1218 diet_obs, diet_ctl, diet_prop);
1219
1220 // - Calculate other food stomach content
1221 calculate_other_food_diet_prop(nspp, nyrs, nsex, nages, other_food,
1222 diet_prop, other_food_diet_prop);
1223
1224 // 5.12. FISHING MORTALITY and FSPRs
1225 // Outside the multi-species iteration loop: hindcast F needs no M. The
1226 // forecast arm below is provisional -- section 6.7 recomputes it once the
1227 // HCR has its reference points.
1228 F_spp.setZero();
1229 F_flt_age.setZero();
1230 F_at_age.setZero();
1231 Ftarget_at_age.setZero();
1232 Flimit_at_age.setZero();
1233 for(flt = 0; flt < n_flt; flt++) {
1234
1235 sp = flt_spp(flt); // Species this fleet fishes or surveys
1236
1237 if(flt_type(flt) == 1){
1238 for(age = 0; age < nages(sp); age++) {
1239 for(sex = 0; sex < nsex(sp); sex ++){
1240 for(yr = 0; yr < nyrs; yr++) {
1241
1242 // Hindcast
1243 if( yr < nyrs_hind){
1244 F_flt_age(flt, sex, age, yr) = sel_at_age(flt, sex, age, yr) * exp(log_F(flt, yr));
1245 }
1246
1247
1248 // Forecast
1249 if( yr >= nyrs_hind){
1250 // -- Apply HCRs
1251 switch(HCR){
1252 case 0: // No fishing
1253 proj_F(sp, yr) = 0;
1254 break;
1255
1256 case 1: // CMSY
1257 proj_F(sp, yr) = Ftarget(sp);
1258 break;
1259
1260 case 2: // Constant F
1261 proj_F(sp, yr) = Ftarget(sp);
1262 break;
1263
1264 case 3: // Constant F that acheives X% of SSB0
1265 proj_F(sp, yr) = Ftarget(sp);
1266 break;
1267
1268 case 4: // Constant Fspr
1269 proj_F(sp, yr) = Ftarget(sp) * Fmult(sp);
1270 break;
1271
1272 case 5: // NPFMC Tier 3 HCR
1273 proj_F(sp, yr) = Ftarget(sp); // Used Fabc of Ftarget_age%
1274 break;
1275
1276 case 6: // PFMC Category 1 HCR
1277 proj_F(sp, yr) = Ftarget(sp) = Flimit(sp) + QnormHCR(sp);
1278 break;
1279
1280 case 7: // SESSF Tier 1 HCR
1281 proj_F(sp, yr) = Ftarget(sp); // Used Fabc of Ftarget_age%
1282 break;
1283 }
1284
1285 // No projected F when the forecast is off or numbers-at-age are input (estDynamics > 0).
1286 if((forecast(sp) == 0) | (estDynamics(sp) > 0)){
1287 proj_F(sp, yr) = 0;
1288 }
1289 F_flt_age(flt, sex, age, yr) = sel_at_age(flt, sex, age, yr) * proj_F_prop(flt) * proj_F(sp, yr);
1290 }
1291
1292 // -- Sum F across fleets
1293 F_at_age(sp, sex, age, yr) += F_flt_age(flt, sex, age, yr);
1294
1295 // -- Calculate F target by age and sex for reference points
1296 Flimit_at_age(sp, sex, age, yr) += sel_at_age(flt, sex, age, yr) * proj_F_prop(flt) * Flimit(sp); // account for time-varying sel
1297 Ftarget_at_age(sp, sex, age, yr) += sel_at_age(flt, sex, age, yr) * proj_F_prop(flt) * Ftarget(sp); // account for time-varying sel
1298 }
1299 }
1300 }
1301
1302 // F across fleets or species
1303 for(yr = 0; yr < nyrs; yr++) {
1304 // Hindcast
1305 if( yr < nyrs_hind){
1306 F_flt(flt, yr) = exp(log_F(flt, yr));
1307 F_spp(sp, yr) += exp(log_F(flt, yr)); // Fully selected fishing mortality
1308 }
1309
1310 // Forecast
1311 if( yr >= nyrs_hind){
1312 F_flt(flt, yr) = proj_F_prop(flt) * proj_F(sp, yr);
1313 F_spp(sp, yr) += proj_F_prop(flt) * proj_F(sp, yr);
1314 }
1315 }
1316 }
1317 }
1318
1319
1320 // 5.12.1. FISHERY SELECTIVITY AT AGE FOR THE INITIAL STATE (initMode 6 only)
1321 // SS3's InitF convention: the initial equilibrium decays at Finit weighted by fishery
1322 // selectivity, so a size-selective fishery barely touches the young ages. Mean over the
1323 // species' fishery fleets in year 1, and 1 everywhere for every other mode. Filled
1324 // before the SPR reference points build Z_init, and so SPRFinit, from it.
1325 for(sp = 0; sp < nspp; sp++){
1326 for(sex = 0; sex < nsex(sp); sex++){
1327 for(age = 0; age < nages(sp); age++){
1328 sel_init(sp, sex, age) = 1.0;
1329 }
1330 }
1331 }
1332 if(initMode == 6){
1333 for(sp = 0; sp < nspp; sp++){
1334 int n_fsh = 0;
1335 for(flt = 0; flt < n_flt; flt++){
1336 if((flt_type(flt) == 1) && (flt_spp(flt) == sp)) n_fsh++;
1337 }
1338 if(n_fsh > 0){
1339 for(sex = 0; sex < nsex(sp); sex++){
1340 for(age = 0; age < nages(sp); age++){
1341 Type acc = 0.0;
1342 for(flt = 0; flt < n_flt; flt++){
1343 if((flt_type(flt) == 1) && (flt_spp(flt) == sp)){
1344 acc += sel_at_age(flt, sex, age, 0);
1345 }
1346 }
1347 sel_init(sp, sex, age) = acc / Type(n_fsh);
1348 }
1349 }
1350 }
1351 }
1352 }
1353
1354
1355 // 5.13. SIMULATE PROCESS ERROR (sim_mod(simulate = TRUE), simulate_state)
1356 // Drawn before the dynamics consume the deviations -- the initial-numbers block below
1357 // is the first reader of rec_dev and init_dev -- so the dynamics and every observation
1358 // draw downstream are consistent with the simulated process. No second copy of the
1359 // population dynamics is needed (WHAM's sim_pop), these deviations and their SDs all
1360 // being parameters. Each draw uses the density that scores it in section 13, bias
1361 // correction included, which is what makes simulated recruitment mean-unbiased.
1362 //
1363 // True if exactly one density scores rec_dev. False for the AMAK/Ianelli configuration
1364 // (srr_fun == 0 with srr_pred_fun > 0), where the stock-recruit curve is fitted as a
1365 // second penalty on the same deviation. REPORTed so sim_mod() can warn without
1366 // re-deriving it.
1367 int rec_srr_single_density = !((srr_fun == 0) && (srr_pred_fun > 0));
1368 REPORT(rec_srr_single_density);
1369
1370 SIMULATE {
1371 // Masks marking which cells this call actually drew. Every deviation array
1372 // is REPORTed whole -- hindcast and projection alike -- so without these a
1373 // caller cannot tell a simulated cell from one left at its fitted value, and
1374 // a recovery statistic taken over the whole array scores both.
1375 matrix<Type> rec_dev_drawn_sim(rec_dev.rows(), rec_dev.cols());
1376 matrix<Type> init_dev_drawn_sim(init_dev.rows(), init_dev.cols());
1377 array<Type> log_M1_dev_drawn_sim(log_M1_dev.dim);
1378 rec_dev_drawn_sim.setZero();
1379 init_dev_drawn_sim.setZero();
1380 log_M1_dev_drawn_sim.setZero();
1381
1382 for(sp = 0; sp < nspp; sp++){
1383
1384 // The density covers the hindcast only -- projection recruitment comes from the
1385 // harvest control rule or sample_rec() -- so the fitted window is the only period
1386 // this can honestly redraw. srr_fun == 0 with srr_pred_fun > 0
1387 // (rec_srr_single_density, above) is excluded: in that AMAK/Ianelli configuration
1388 // section 13 scores rec_dev through JNLL_REC_DEV and again through
1389 // JNLL_SRR_PENALTY, so there is no single distribution to draw from. Nothing is
1390 // drawn, sim_mod() says why, and the full argument is in
1391 // vignette("model-diagnostics").
1392 if(simulate_state(0) == 1 && simulate_period(0) == 1 && rec_srr_single_density){
1393 for(yr = 0; yr < nyrs_hind; yr++){
1394 rec_dev(sp, yr) = rnorm(-bias_adjust_proc*square(R_sd(sp))/2.0, R_sd(sp));
1395 rec_dev_drawn_sim(sp, yr) = 1;
1396 }
1397 }
1398
1399 // Initial abundance-at-age: the same process, for years before styr. The
1400 // gate is the density's own -- the equilibrium modes and OffsetEquilibrium
1401 // (5) fix init_dev and carry no penalty, so there is nothing to draw -- and
1402 // the rec_srr_single_density gate as well, a fresh initial age structure on
1403 // top of FITTED hindcast deviations being no history the model generated.
1404 if(simulate_state(0) == 1 && simulate_period(0) == 1 && rec_srr_single_density &&
1405 (initMode > 1) && (initMode != 5)){
1406 for(age = 1; age < nages(sp); age++){
1407 init_dev(sp, age - 1) = rnorm(-bias_adjust_proc*square(R_sd(sp))/2.0, R_sd(sp));
1408 init_dev_drawn_sim(sp, age - 1) = 1;
1409 }
1410 }
1411 }
1412
1413 // Natural mortality random effects. The IID modes are the AR1 code at rho = 0, so one
1414 // construction covers all six: draw a standardized AR1 and scale by the marginal SD
1415 // afterwards. M1_dev_log_sd is the INNOVATION sd; Sigma_M is the marginal.
1416 //
1417 // The draw is BROADCAST along whichever dimension the mode holds constant (modes 1/4
1418 // over years, modes 2/5 over ages) and across the sexes sharing one parameter --
1419 // build_map() separates the sexes only at M1_model = 2, so num_re_sexes is the number
1420 // of INDEPENDENT draws. This block writes the expanded array, so writing only the
1421 // cell the density reads would leave the rest at fitted values and the deviation
1422 // would not be constant.
1423 if(simulate_state(1) == 1 && simulate_period(0) == 1){
1424 for(sp = 0; sp < nspp; sp++){
1425 int num_re_sexes = (M1_model(sp) == 2 && nsex(sp) > 1) ? 2 : 1;
1426
1427 if((M1_re(sp) == 1) || (M1_re(sp) == 4)){ // by age, constant over years
1428 Type sigma_M = exp(M1_dev_log_sd(sp, 0));
1429 Type rho_M_a = rho_trans(M1_rho(sp, 0, 0));
1430 Type Sigma_M = pow(pow(sigma_M, 2) / (1.0 - pow(rho_M_a, 2)), 0.5);
1431 for(int s = 0; s < num_re_sexes; s++){
1432 vector<Type> M_re_age(nages(sp));
1433 AR1(rho_M_a).simulate(M_re_age);
1434 M_re_age *= Sigma_M;
1435 int sex_end = (num_re_sexes == 1) ? nsex(sp) : s + 1;
1436 for(int sex = s; sex < sex_end; sex++){
1437 for(age = 0; age < nages(sp); age++){
1438 for(yr = 0; yr < nyrs; yr++){
1439 log_M1_dev(sp, sex, age, yr) = M_re_age(age);
1440 log_M1_dev_drawn_sim(sp, sex, age, yr) = 1;
1441 }
1442 }
1443 }
1444 }
1445 }
1446
1447 if((M1_re(sp) == 2) || (M1_re(sp) == 5)){ // by year, constant over ages
1448 Type sigma_M = exp(M1_dev_log_sd(sp, 0));
1449 Type rho_M_y = rho_trans(M1_rho(sp, 0, 1));
1450 Type Sigma_M = pow(pow(sigma_M, 2) / (1.0 - pow(rho_M_y, 2)), 0.5);
1451 for(int s = 0; s < num_re_sexes; s++){
1452 vector<Type> M_re_yr(nyrs_hind);
1453 AR1(rho_M_y).simulate(M_re_yr);
1454 M_re_yr *= Sigma_M;
1455 int sex_end = (num_re_sexes == 1) ? nsex(sp) : s + 1;
1456 for(int sex = s; sex < sex_end; sex++){
1457 for(yr = 0; yr < nyrs_hind; yr++){
1458 for(age = 0; age < nages(sp); age++){
1459 log_M1_dev(sp, sex, age, yr) = M_re_yr(yr);
1460 log_M1_dev_drawn_sim(sp, sex, age, yr) = 1;
1461 }
1462 }
1463 }
1464 }
1465 }
1466
1467 if((M1_re(sp) == 3) || (M1_re(sp) == 6)){ // by age and year
1468 Type sigma_M = exp(M1_dev_log_sd(sp, 0));
1469 Type rho_M_a = rho_trans(M1_rho(sp, 0, 0));
1470 Type rho_M_y = rho_trans(M1_rho(sp, 0, 1));
1471 Type Sigma_M = pow(pow(sigma_M, 2) / ((1.0 - pow(rho_M_y, 2)) * (1.0 - pow(rho_M_a, 2))), 0.5);
1472 for(int s = 0; s < num_re_sexes; s++){
1473 array<Type> M_re_a_yr(nages(sp), nyrs_hind);
1474 // Same argument order as the density in section 14 -- year on the
1475 // outermost dimension, age on the fastest-running one.
1476 SEPARABLE(AR1(rho_M_y), AR1(rho_M_a)).simulate(M_re_a_yr);
1477 int sex_end = (num_re_sexes == 1) ? nsex(sp) : s + 1;
1478 for(int sex = s; sex < sex_end; sex++){
1479 for(age = 0; age < nages(sp); age++){
1480 for(yr = 0; yr < nyrs_hind; yr++){
1481 log_M1_dev(sp, sex, age, yr) = Sigma_M * M_re_a_yr(age, yr);
1482 log_M1_dev_drawn_sim(sp, sex, age, yr) = 1;
1483 }
1484 }
1485 }
1486 }
1487 }
1488 }
1489 }
1490
1491 // Reported so the draw can be checked directly. Inferring it from the data
1492 // it produces cannot tell "drew correctly" from "drew nothing, and the
1493 // observation error moved". Each goes out with its drawn-cell mask.
1494 matrix<Type> rec_dev_sim = rec_dev;
1495 matrix<Type> init_dev_sim = init_dev;
1496 array<Type> log_M1_dev_sim = log_M1_dev;
1497 REPORT(rec_dev_sim);
1498 REPORT(init_dev_sim);
1499 REPORT(log_M1_dev_sim);
1500 REPORT(rec_dev_drawn_sim);
1501 REPORT(init_dev_drawn_sim);
1502 REPORT(log_M1_dev_drawn_sim);
1503 }
1504
1505 /** ------------------------------------------------------------------------ //
1506 * 6. POPULATION DYNAMICS EQUATIONS //
1507 * ------------------------------------------------------------------------- */
1508 // Start iterations for multi-species convergence (anything dependent on M)
1509 for(int iter = 0; iter < niter; iter++) {
1510
1511 // 6.1. TOTAL MORTALITY-AT-AGE
1512 for(sp = 0; sp < nspp; sp++) {
1513 for(sex = 0; sex < nsex(sp); sex ++){
1514 // Element-wise: assigning a scalar to a vector would broadcast.
1515 for(int i = 0; i < M1_beta.dim(2); i++){
1516 beta_M1_tmp(i) = M1_beta(sp, sex, i);
1517 }
1518 for(age = 0; age < nages(sp); age++) {
1519 for(yr = 0; yr < nyrs; yr++) {
1520 // Matrix multiplication from sliced arrays doesn't work
1521 env_M1_tmp = env_index.row(yr);
1522 M1_mult = env_M1_tmp * beta_M1_tmp;
1523
1524 // M1_mult.sum() is LEGACY (scheduled removal: v4.5.0). It carries
1525 // the environmental effect of the soft-deprecated M1_indices path,
1526 // so fits built that way still reproduce; `linkages` is the current
1527 // route. See "Scheduled removal" in NEWS.md (4.1.0 section).
1528 M1_at_age(sp, sex, age, yr) = exp(
1529 log_M1(sp, sex, age)
1530 + log_M1_dev(sp, sex, age, yr)
1531 + M1_mult.sum()
1532 + M_linkage_offset(sp, sex, age, yr)
1533 ) + M_linkage_offset_nat(sp, sex, age, yr);
1534 M_at_age(sp, sex, age, yr) = M1_at_age(sp, sex, age, yr) + M2_at_age(sp, sex, age, yr);
1535 M_at_age_dB0(sp, sex, age, yr) = M1_at_age(sp, sex, age, yr) + M2_at_age_dB0(sp, sex, age, yr);
1536 M_at_age_dBF(sp, sex, age, yr) = M1_at_age(sp, sex, age, yr) + M2_at_age_dBF(sp, sex, age, yr);
1537 Z_at_age(sp, sex, age, yr) = M1_at_age(sp, sex, age, yr) + F_at_age(sp, sex, age, yr) + M2_at_age(sp, sex, age, yr);
1538 }
1539 }
1540 }
1541 }
1542
1543
1544 // 6.2. SPR BASED REFERENCE POINTS
1545 //FIXME - make time-varying?
1546 SPR0.setZero();
1547 SPRFinit.setZero();
1548 SPRlimit.setZero();
1549 SPRtarget.setZero();
1550 // Not defined under predation: M carries M2, which scales with predator abundance, so
1551 // spawning output per recruit would not be a property of the prey stock alone.
1552 // data_check() refuses the switch combinations that read these downstream.
1553 if(msmMode == 0){
1554 for(sp = 0; sp < nspp; sp++) {
1555
1556 int na = nages(sp);
1557 int term_yr = nyrs_hind - 1; // rates for a reference point are the terminal hindcast year's
1558 wt_idx_ssb = 2 * sp + 1;
1559
1560 // The four schedules. Flimit and Ftarget are selectivity-scaled and read the
1561 // terminal hindcast year; Finit is the flat F that generated the initial age
1562 // structure, so it is constant across ages and reads year 0, weight included.
1563 // FIXME: time-vary sel in the forecast
1564 vector<Type> Z_unfished(na), Z_limit(na), Z_target(na), Z_init(na);
1565 vector<Type> wt_term(na), wt_first(na), mature_at_age(na);
1566 Type female_split = (nsex(sp) == 1) ? Type(1.0) : sex_ratio(sp, 0);
1567
1568 for(age = 0; age < na; age++){
1569 Z_unfished(age) = M_at_age(sp, 0, age, term_yr);
1570 Z_limit(age) = M_at_age(sp, 0, age, term_yr) + Flimit_at_age(sp, 0, age, term_yr);
1571 Z_target(age) = M_at_age(sp, 0, age, term_yr) + Ftarget_at_age(sp, 0, age, term_yr);
1572 Z_init(age) = M_at_age(sp, 0, age, 0) + Finit(sp) * sel_init(sp, 0, age);
1573
1574 // Spawning output per TOTAL recruit, so the female fraction enters once:
1575 // spawn_output (5.7) carries maturity and the age-varying ratio for a
1576 // one-sex species, female_split the recruitment split (6.6) for a two-sex one.
1577 wt_term(age) = spawn_output(sp, age, term_yr);
1578 wt_first(age) = spawn_output(sp, age, 0);
1579 mature_at_age(age) = female_split;
1580 }
1581
1582 vector<Type> n_unfished = per_recruit_survivors(Z_unfished);
1583 vector<Type> n_limit = per_recruit_survivors(Z_limit);
1584 vector<Type> n_target = per_recruit_survivors(Z_target);
1585 vector<Type> n_init = per_recruit_survivors(Z_init);
1586
1587 SPR0(sp) = spawning_biomass_per_recruit(n_unfished, Z_unfished, wt_term, mature_at_age, spawn_month(sp));
1588 SPRlimit(sp) = spawning_biomass_per_recruit(n_limit, Z_limit, wt_term, mature_at_age, spawn_month(sp));
1589 SPRtarget(sp) = spawning_biomass_per_recruit(n_target, Z_target, wt_term, mature_at_age, spawn_month(sp));
1590 SPRFinit(sp) = spawning_biomass_per_recruit(n_init, Z_init, wt_first, mature_at_age, spawn_month(sp));
1591
1592 // Reported for inspection; slot order matches the four calls above.
1593 for(age = 0; age < na; age++){
1594 NbyageSPR(0, sp, age) = n_unfished(age);
1595 NbyageSPR(1, sp, age) = n_limit(age);
1596 NbyageSPR(2, sp, age) = n_target(age);
1597 NbyageSPR(3, sp, age) = n_init(age);
1598 }
1599 }
1600 }
1601
1602
1603 // 6.3. INITIAL RECRUITMENT
1604 // -- For beverton-holt, steepness and R0 are derived from SPR0
1605 penalty = 0.0;
1606 zero_N_pen.setZero();
1607 for( sp = 0; sp < nspp ; sp++) {
1608 // Under predation SPR is undefined: a hindcast curve takes R_init from the
1609 // free level R0(sp, 0) and reports steepness 0, skipping the cases below.
1610 if((msmMode > 0) & (srr_fun > 1)){
1611 for(yr = 0; yr < nyrs; yr++){ steepness(sp, yr) = 0; }
1612 R_init(sp) = R0(sp, 0);
1613 } else switch(srr_fun){
1614 case 0: // Random about mean (e.g. Alaska)
1615 // No compensation, so steepness is constant across years.
1616 for(yr = 0; yr < nyrs; yr++){ steepness(sp, yr) = 0.99; }
1617 {
1618 R_init(sp) = R0(sp, 0);
1619 }
1620 break;
1621
1622 case 1: // Random about mean with environmental linkage
1623 for(yr = 0; yr < nyrs; yr++){ steepness(sp, yr) = 0.99; }
1624 {
1625 R_init(sp) = R0(sp, 0);
1626 }
1627 break;
1628
1629 case 2: // Beverton-Holt
1630 {
1631 // Steepness for every year: alpha may be time-varying through a linkage.
1632 for(yr = 0; yr < nyrs; yr++){
1633 steepness(sp, yr) = alpha(sp, yr) * SPR0(sp)/(4.0 + alpha(sp, yr) * SPR0(sp));
1634 }
1635 // Unfished recruitment implied by the curve, first year only. Filling every
1636 // year would make R0 an AD function of alpha and Beta across the whole series
1637 // -- ~6x the fit time for a value that is reported rather than used,
1638 // Beverton-Holt recruitment being a function of alpha, Beta and SSB, not of R0.
1639 R0(sp, 0) = (alpha(sp, 0) - 1.0/SPR0(sp)) / Beta(sp, 0); // (Alpha-1/SPR0)/beta
1640 R_init(sp) = (alpha(sp, 0) - 1.0/SPRFinit(sp)) / Beta(sp, 0); // (Alpha-1/SPRFinit)/beta
1641 // Same floor as R in 6.6.1: an identity-link alpha offset can make both negative.
1642 if (rec_floor_on) { Type pen_R = 0; R0(sp, 0) = posfun(R0(sp, 0), Type(1e-3), pen_R); R_init(sp) = posfun(R_init(sp), Type(1e-3), pen_R); zero_N_pen(sp) += pen_R; }
1643 }
1644 break;
1645
1646 case 3: // Retired alias of 2; build_srr() refuses it (alpha moves via a linkage)
1647 {
1648 // Steepness for every year: alpha may be time-varying through a linkage.
1649 for(yr = 0; yr < nyrs; yr++){
1650 steepness(sp, yr) = alpha(sp, yr) * SPR0(sp)/(4.0 + alpha(sp, yr) * SPR0(sp));
1651 }
1652 // Unfished recruitment implied by the curve, first year only -- see
1653 // case 2 for why it is not filled for every year.
1654 R0(sp, 0) = (alpha(sp, 0) - 1.0/SPR0(sp)) / Beta(sp, 0); // (Alpha-1/SPR0)/beta
1655 R_init(sp) = (alpha(sp, 0) - 1.0/SPRFinit(sp)) / Beta(sp, 0); // (Alpha-1/SPRFinit)/beta
1656 // Same floor as R in 6.6.1: an identity-link alpha offset can make both negative.
1657 if (rec_floor_on) { Type pen_R = 0; R0(sp, 0) = posfun(R0(sp, 0), Type(1e-3), pen_R); R_init(sp) = posfun(R_init(sp), Type(1e-3), pen_R); zero_N_pen(sp) += pen_R; }
1658 }
1659 break;
1660
1661 case 4: // Ricker
1662 {
1663 // Steepness for every year: alpha may be time-varying through a linkage.
1664 for(yr = 0; yr < nyrs; yr++){
1665 { Type aS = alpha(sp, yr) * SPR0(sp); // kept positive under an identity-link alpha offset
1666 if (rec_floor_on) { Type pen_d = 0; aS = posfun(aS, Type(1e-3), pen_d); }
1667 steepness(sp, yr) = 0.2 * exp(0.8*log(aS)); }
1668 }
1669
1670 // - R at F0
1671 Type pen_rk = 0; // this species' floor penalties alone
1672 ricker_intercept = alpha(sp, 0) * SPR0(sp) - 1.0;
1673 ricker_intercept = posfun(ricker_intercept, Type(0.001), pen_rk) + 1.0;
1674 // Year 0 only: posfun() keeps the Ricker intercept positive, charging the
1675 // excursion to the objective. The Ianelli configuration (srr_fun < 2)
1676 // never reaches this case, so its R0 keeps its estimated value.
1677 R0(sp, 0) = log(ricker_intercept)/(Beta(sp, 0) * SPR0(sp)/1000000.0);
1678
1679 // R at equilibrium F
1680 ricker_intercept = alpha(sp, 0) * SPRFinit(sp) - 1.0;
1681 ricker_intercept = posfun(ricker_intercept, Type(0.001), pen_rk) + 1.0;
1682 R_init(sp) = log(ricker_intercept)/(Beta(sp, 0) * SPRFinit(sp)/1000000.0);
1683 zero_N_pen(sp) += pen_rk;
1684 }
1685 break;
1686
1687 case 5: // Ricker with environmental impacts on alpha
1688 {
1689 // Steepness for every year: alpha may be time-varying through a linkage.
1690 for(yr = 0; yr < nyrs; yr++){
1691 { Type aS = alpha(sp, yr) * SPR0(sp); // kept positive under an identity-link alpha offset
1692 if (rec_floor_on) { Type pen_d = 0; aS = posfun(aS, Type(1e-3), pen_d); }
1693 steepness(sp, yr) = 0.2 * exp(0.8*log(aS)); }
1694 }
1695
1696 // - R at F0
1697 Type pen_rk = 0; // this species' floor penalties alone
1698 ricker_intercept = alpha(sp, 0) * SPR0(sp) - 1.0;
1699 ricker_intercept = posfun(ricker_intercept, Type(0.001), pen_rk) + 1.0;
1700 // Year 0 only, as case 4.
1701 R0(sp, 0) = log(ricker_intercept)/(Beta(sp, 0) * SPR0(sp)/1000000.0);
1702
1703 // R at equilibrium F
1704 ricker_intercept = alpha(sp, 0) * SPRFinit(sp) - 1.0;
1705 ricker_intercept = posfun(ricker_intercept, Type(0.001), pen_rk) + 1.0;
1706 R_init(sp) = log(ricker_intercept)/(Beta(sp, 0) * SPRFinit(sp)/1000000.0);
1707 zero_N_pen(sp) += pen_rk;
1708 }
1709 break;
1710
1711 default:
1712 error("Invalid 'srr_fun'");
1713 }
1714
1715 // Under the Ianelli configuration (srr_fun 0 or 1, srr_pred_fun > 1) the curve is
1716 // estimated as a recruitment penalty rather than driving the hindcast, and the
1717 // switch above -- keyed on srr_fun -- would leave steepness at the mean-recruitment
1718 // constant. Derive it from the penalty curve's alpha instead, since the
1719 // stock-recruit prior (JNLL_SRR_PRIOR) is evaluated on it. R0 is deliberately NOT
1720 // re-derived: recruitment here is R0 * exp(rec_dev), so R0 keeps its estimated
1721 // value.
1722 if((srr_fun < 2) & (srr_pred_fun > 1) & (msmMode == 0)){
1723 for(yr = 0; yr < nyrs; yr++){
1724 if((srr_pred_fun == 2) | (srr_pred_fun == 3)){
1725 steepness(sp, yr) = alpha(sp, yr) * SPR0(sp)/(4.0 + alpha(sp, yr) * SPR0(sp));
1726 }
1727 if((srr_pred_fun == 4) | (srr_pred_fun == 5)){
1728 { Type aS = alpha(sp, yr) * SPR0(sp); // kept positive under an identity-link alpha offset
1729 if (rec_floor_on) { Type pen_d = 0; aS = posfun(aS, Type(1e-3), pen_d); }
1730 steepness(sp, yr) = 0.2 * exp(0.8*log(aS)); }
1731 }
1732 }
1733 }
1734 // SPR0 is undefined under predation, so steepness is reported as 0;
1735 // data_check() refuses a steepness prior there.
1736 if((srr_fun < 2) & (srr_pred_fun > 1) & (msmMode > 0)){
1737 for(yr = 0; yr < nyrs; yr++){ steepness(sp, yr) = 0; }
1738 }
1739 }
1740
1741
1742 // 6.4. INITIAL ABUNDANCE AT AGE, BIOMASS, AND SSB (YEAR 1)
1743 biomass.setZero();
1744 ssb.setZero();
1745 for(sp = 0; sp < nspp; sp++) {
1746
1747 // Sex ratio at recruitment (1-sex model gets all R)
1748 if(nsex(sp) == 1){
1749 sex_ratio(sp, 0) = 1.0;
1750 }
1751
1752 // A free initial recruitment level, log scale; 0 without an R_init linkage.
1753 // Read at year 0 because the initial state is one year, and per species
1754 // because that is the stratum it is estimated on. Only the estimated branch
1755 // reads it: input numbers-at-age have no initial state to scale.
1756 Type init_level = recruitment_linkage_offset(sp, RCEATTLE_REC_R_INIT, 0);
1757
1758 for(age = 0; age < nages(sp); age++){
1759 for(sex = 0; sex < nsex(sp); sex ++){
1760
1761 switch(estDynamics(sp)){
1762 case 0: // Estimated
1763
1764 // - Amin (i.e. recruitment).
1765 if(age == 0){
1766 // Year-1 recruitment is a recruitment year with its own deviate,
1767 // not part of the pre-hindcast level, so it stays at R_init.
1768 R(sp, 0) = R_init(sp) * exp(rec_dev(sp, 0));
1769 N_at_age(sp, 0, 0, 0) = R(sp, 0) * sex_ratio(sp, 0);
1770 // Guarded because the male slot exists only where some species is
1771 // two-sex: the value would be 0 anyway (sex_ratio 1, set above),
1772 // but the index is out of range when max_sex is 1.
1773 if(nsex(sp) > 1){
1774 N_at_age(sp, 1, 0, 0) = R(sp, 0) * (1-sex_ratio(sp, 0));
1775 }
1776 // The equilibrium carries no recruitment deviation: it is the mean
1777 // level the deviates depart from. It DOES carry the initial level,
1778 // on every age including this one, so the equilibrium that
1779 // equil_catch_hat integrates sits at one recruitment level.
1780 N_eq(sp, 0, 0) = R_init(sp) * exp(init_level) * sex_ratio(sp, 0);
1781 if(nsex(sp) > 1){
1782 N_eq(sp, 1, 0) = R_init(sp) * exp(init_level) * (1-sex_ratio(sp, 0));
1783 }
1784 }
1785
1786 // - Estimate as free parameters
1787 if(initMode == 0){
1788 if(age > 0){
1789 N_at_age(sp, 0, age, 0) = exp(init_dev(sp, age-1)) * sex_ratio(sp, 0);
1790 if(nsex(sp) > 1){
1791 N_at_age(sp, 1, age, 0) = exp(init_dev(sp, age-1)) * (1-sex_ratio(sp, 0));
1792 }
1793 // No equilibrium underlies free initial numbers; Finit is 0 here
1794 // and an equilibrium catch is refused, so this is never fitted.
1795 N_eq(sp, 0, age) = N_at_age(sp, 0, age, 0);
1796 if(nsex(sp) > 1){
1797 N_eq(sp, 1, age) = N_at_age(sp, 1, age, 0);
1798 }
1799 }
1800 }
1801
1802 // - Equilibrium or non-equilibrium from Rinit, Finit, mortality and init devs
1803 // Finit is 0 for initMode 0, 1, 2 and 5; it is estimated for the
1804 // fished non-equilibrium modes 3, 4 and 6 (see section 6.1).
1805 if(initMode > 0){
1806
1807 // How far the initial age-structure sits off the mean-recruitment
1808 // equilibrium R0, on the log scale; 0 under neither source below. Two
1809 // sources, never both (an R_init linkage is refused under mode 5): the free
1810 // R_init level -- SS3's SR_regime block on the year before the hindcast,
1811 // which SS3 shrinks at sigmaR/ave_age and Rceattle leaves free, so put a
1812 // prior on the coefficient to match SS3 -- or, under OffsetEquilibrium (5),
1813 // the first-year recruitment deviation, seeding the structure off
1814 // exp(rec_pars + rec_dev(sp, 0)) with init devs off (Cole Monnahan / AFSC
1815 // GOA pollock convention).
1816 Type init_log_scalar = init_level;
1817 if(initMode == 5){
1818 init_log_scalar += rec_dev(sp, 0);
1819 }
1820
1821 // Sum M1 until age - 1. OffsetEquilibrium (5) uses the same
1822 // standard departing-age cumulative-M decay as the equilibrium
1823 // modes (Finit is 0 for mode 5).
1824 if((initMode == 1) | (initMode == 2) | (initMode == 3) | (initMode == 5)){
1825 mort_sum(sp, age) = 0;
1826 for(int age_tmp = 0; age_tmp < age; age_tmp++){
1827 mort_sum(sp, age) += M1_at_age(sp, sex, age_tmp, 0) + Finit(sp);
1828 }
1829 }
1830
1831 // SS3's InitF convention: the initial F is spread over ages by
1832 // the fishery's selectivity before it accumulates, so an
1833 // unselected young age carries almost none of it.
1834 if(initMode == 6){
1835 mort_sum(sp, age) = 0;
1836 for(int age_tmp = 0; age_tmp < age; age_tmp++){
1837 mort_sum(sp, age) += M1_at_age(sp, sex, age_tmp, 0)
1838 + Finit(sp) * sel_init(sp, sex, age_tmp);
1839 }
1840 }
1841
1842 if(initMode == 4){
1843 mort_sum(sp, age) = 0;
1844 for(int age_tmp = 0; age_tmp < age; age_tmp++){
1845 mort_sum(sp, age) += M1_at_age(sp, sex, age_tmp, 0);
1846 }
1847 mort_sum(sp, age) += Finit(sp);
1848 }
1849
1850 // -- 6.5.2. Age Amin+1:Amax-1 (initial abundance)
1851 if((age > 0) & (age < nages(sp) - 1)) {
1852
1853 if(sex == 0){
1854 N_at_age(sp, 0, age, 0) = R_init(sp) * exp( - mort_sum(sp, age) + init_dev(sp, age - 1) + init_log_scalar) * sex_ratio(sp, 0);
1855 N_eq(sp, 0, age) = R_init(sp) * exp( - mort_sum(sp, age) + init_log_scalar) * sex_ratio(sp, 0);
1856 }
1857 if(sex == 1){
1858 N_at_age(sp, 1, age, 0) = R_init(sp) * exp( - mort_sum(sp, age) + init_dev(sp, age - 1) + init_log_scalar) * (1-sex_ratio(sp, 0));
1859 N_eq(sp, 1, age) = R_init(sp) * exp( - mort_sum(sp, age) + init_log_scalar) * (1-sex_ratio(sp, 0));
1860 }
1861 }
1862
1863 // -- 6.5.3. Amax
1864 if(age == (nages(sp) - 1)) {
1865
1866 // The plus group accumulates at its own total mortality. Under initMode
1867 // 6 the initial F reaches an age through that age's selectivity, so the
1868 // oldest age decays at M1 + Finit * sel (SS3's equilibrium convention);
1869 // every other mode charges the full Finit, as in mort_sum above.
1870 Type Z_plus = M1_at_age(sp, sex, nages(sp) - 1, 0) + Finit(sp) *
1871 ((initMode == 6) ? sel_init(sp, sex, nages(sp) - 1) : Type(1.0));
1872 Type plus_grp = 1 - exp(-Z_plus);
1873
1874 if(sex == 0){// NOTE: This solves for the geometric series
1875 N_at_age(sp, 0, age, 0) = R_init(sp) * exp( - mort_sum(sp, age) + init_dev(sp, age - 1) + init_log_scalar) / plus_grp * sex_ratio(sp, 0);
1876 N_eq(sp, 0, age) = R_init(sp) * exp( - mort_sum(sp, age) + init_log_scalar) / plus_grp * sex_ratio(sp, 0);
1877 }
1878
1879 if(sex == 1){
1880 N_at_age(sp, 1, age, 0) = R_init(sp) * exp( - mort_sum(sp, age) + init_dev(sp, age - 1) + init_log_scalar) / plus_grp * (1-sex_ratio(sp, 0));
1881 N_eq(sp, 1, age) = R_init(sp) * exp( - mort_sum(sp, age) + init_log_scalar) / plus_grp * (1-sex_ratio(sp, 0));
1882 }
1883 }
1884 }
1885 break;
1886
1887 case 1: // Numbers-at-age fixed exactly to NByageFixed (pop_scalar mapped to
1888 // NA in build_map so log_pop_scalar = 0 -> pop_scalar = 1.0)
1889 N_at_age(sp, sex, age, 0) = pop_scalar(sp) * NByageFixed(sp, sex, age, 0);
1890 break;
1891
1892 case 2: // Numbers-at-age scaled by a single estimated age-independent scalar
1893 N_at_age(sp, sex, age, 0) = pop_scalar(sp) * NByageFixed(sp, sex, age, 0);
1894 break;
1895
1896 default:
1897 error("Invalid 'estDynamics'");
1898 }
1899
1900 // -- 6.5.3. Estimate total biomass in year 1
1901 wt_idx_pop = 2 * sp;
1902 biomass_at_age(sp, sex, age, 0) = N_at_age(sp, sex, age, 0) * weight_hat( wt_idx_pop, sex, age, 0);
1903 biomass(sp, 0) += N_at_age(sp, sex, age, 0) * weight_hat( wt_idx_pop, sex, age, 0);
1904 }
1905
1906 // -- 6.5.4. Estimated initial female SSB
1907 wt_idx_ssb = 2 * sp + 1;
1908 ssb(sp, 0) += N_at_age(sp, 0, age, 0) * exp(-Z_at_age(sp, 0, age, 0) * spawn_month(sp)/12) * spawn_output(sp, age, 0); // 6.6.
1909 }
1910 }
1911
1912
1913 // 6.5. HINDCAST NUMBERS AT AGE, BIOMASS-AT-AGE (mt), and ssb-AT-AGE (mt)
1914 penalty = 0.0;
1915 for(sp = 0; sp < nspp; sp++) {
1916 for(yr = 1; yr < nyrs_hind; yr++) {
1917
1918 // Switch for srr (for MSEs if using Ianelli SRR method)
1919 int srr_switch = srr_fun;
1920 if(yr >= nyrs_srrmean){
1921 srr_switch = srr_pred_fun;
1922 }
1923
1924 // -- 6.6.1. Recruitment
1925
1926 // Recruits arriving at age minage in year yr were spawned in yr - minage,
1927 // before styr while yr < minage. With no modelled SSB those years take
1928 // R_init * exp(rec_dev) -- equilibrium recruitment at F = Finit, what
1929 // year 0 gets, so ages seeded from the same equilibrium agree. Stock
1930 // Synthesis treats the pre-start period the same way; WHAM avoids it by
1931 // fixing the lag at one year. Cannot fire at minage = 1.
1932 int spawn_yr = yr - minage(sp);
1933 int srr_use = (spawn_yr < 0) ? 0 : srr_switch;
1934 Type ssb_tmp = (spawn_yr < 0) ? Type(0.0) : ssb(sp, spawn_yr);
1935 // Pre-styr years take R_init, not R0(sp, yr): under a hindcast curve R0 is
1936 // set only at year 0 (derived, or the free level under predation).
1937 Type rec_mean = (spawn_yr < 0) ? R_init(sp) : R0(sp, yr);
1938
1939 R(sp, yr) = calculate_recruitment(srr_use, rec_mean, ssb_tmp, alpha(sp, yr), Beta(sp, yr), rec_dev(sp, yr), SPR0(sp));
1940 // Under an identity-link recruitment offset the curve can go non-positive: keep R
1941 // positive (posfun, 0.001 barrier), charging the excursion to the zero-N row.
1942 if (rec_floor_on) { Type pen_R = 0; R(sp, yr) = posfun(R(sp, yr), Type(1e-3), pen_R); zero_N_pen(sp) += pen_R; }
1943
1944 N_at_age(sp, 0, 0, yr) = R(sp, yr) * sex_ratio(sp, 0);
1945 if(nsex(sp) > 1){
1946 N_at_age(sp, 1, 0, yr) = R(sp, yr) * (1.0-sex_ratio(sp, 0));
1947 }
1948
1949
1950 // -- 6.6.2. Ages beyond recruitment
1951 for(age = 0; age < nages(sp); age++){
1952 for(sex = 0; sex < nsex(sp); sex ++){
1953
1954 switch(estDynamics(sp)){
1955 case 0: // Estimated numbers-at-age
1956
1957 // -- Where Amin < age < Amax
1958 if(age < (nages(sp) - 1)) {
1959 N_at_age(sp, sex, age + 1, yr) = N_at_age(sp, sex, age, yr - 1) * exp(-Z_at_age(sp, sex, age, yr-1));// S_at_age(sp, sex, age, yr - 1);
1960 }
1961
1962 // -- Plus group where age = Amax.
1963 if(age == (nages(sp) - 1)) {
1964 N_at_age(sp, sex, age, yr) = N_at_age(sp, sex, age - 1, yr - 1) * exp(-Z_at_age(sp, sex, age-1, yr-1)) + N_at_age(sp, sex, age, yr - 1) * exp(-Z_at_age(sp, sex, age, yr-1)); // S_at_age(sp, sex, age, yr - 1);
1965 }
1966 break;
1967
1968 case 1: // Numbers-at-age fixed exactly to NByageFixed (pop_scalar = 1.0 via map)
1969 N_at_age(sp, sex, age, yr) = pop_scalar(sp) * NByageFixed(sp, sex, age, yr);
1970 break;
1971
1972 case 2: // Numbers-at-age scaled by a single estimated age-independent scalar
1973 N_at_age(sp, sex, age, yr) = pop_scalar(sp) * NByageFixed(sp, sex, age, yr);
1974 break;
1975
1976 default:
1977 error("Invalid 'estDynamics'");
1978 }
1979
1980 // A fresh accumulator per cell: the penalty is this cell's excursion alone.
1981 { Type pen_N = 0; N_at_age(sp, sex, age, yr) = posfun(N_at_age(sp, sex, age, yr), Type(0.001), pen_N); zero_N_pen(sp) += pen_N; }
1982
1983 // -- 6.6.3. Estimate total biomass
1984 wt_idx_pop = 2 * sp ;
1985 biomass_at_age(sp, sex, age, yr) = N_at_age(sp, sex, age, yr) * weight_hat( wt_idx_pop, sex, age, yr );
1986 biomass(sp, yr) += biomass_at_age(sp, sex, age, yr);
1987 }
1988
1989 // -- 6.6.4. Estimated female ssb
1990 wt_idx_ssb = 2 * sp + 1;
1991 ssb(sp, yr) += N_at_age(sp, 0, age, yr) * exp(-Z_at_age(sp, 0, age, yr) * spawn_month(sp)/12.0) * spawn_output(sp, age, yr); // 6.6.
1992 }
1993 }
1994 }
1995
1996
1997 // 6.6. DEPLETION BASED BIOMASS REFERENCE POINTS (i.e. SB0 and dynamic SB0)
1998 // -- calculate mean recruitment
1999 // FIXME: may want specified year ranges
2000 avg_R.setZero();
2001 for(sp = 0; sp < nspp; sp++) {
2002 for(yr = 0; yr < nyrs_srrmean; yr++) {
2003 avg_R(sp) += R(sp, yr)/Type(nyrs_srrmean);
2004 }
2005 }
2006
2007 // -- Calc biomass_depletion based BRPs
2008 NByage0.setZero();
2009 NByageF.setZero();
2010 N_at_age_dBF.setZero();
2011 N_at_age_dB0.setZero();
2012 B0.setZero();
2013 SB0.setZero();
2014 SBF.setZero();
2015 DynamicB0.setZero();
2016 DynamicSB0.setZero();
2017 DynamicSBF.setZero();
2018 for(sp = 0; sp < nspp; sp++) {
2019 for(yr = 0; yr < nyrs; yr++) {
2020
2021 // - Year 1
2022 // -- Initial abundance-at-age is the same as the hindcast
2023 if(yr == 0){
2024 for(sex = 0; sex < nsex(sp); sex ++){
2025 for(age = 0; age < nages(sp); age++){
2026 NByage0(sp, sex, age , 0) = NByageF(sp, sex, age , 0) = N_at_age_dBF(sp, sex, age, 0) = N_at_age_dB0(sp, sex, age, 0) = N_at_age(sp,sex,age,0);
2027 }
2028 }
2029 }
2030
2031 // - Recruitment Year > 1
2032 if(yr > 0){
2033 // - Option 1a: Use mean rec
2034 if((proj_mean_rec == 1) & (srr_pred_fun < 2)){
2035 // Equilibrium RPs used mean rec across all years
2036 NByage0(sp, 0, 0, yr) = avg_R(sp);
2037 NByageF(sp, 0, 0, yr) = avg_R(sp);
2038
2039 // Dynamic RPs use mean rec for the forecast and observed rec for the
2040 // hindcast. LEGACY (scheduled removal: v4.5.0): prefer the
2041 // stock-recruit arm below. This split feeds SB0(sp, nyrs-1), so it
2042 // wants a deliberate decision rather than a sweep.
2043 if(yr < nyrs_hind){
2044 N_at_age_dBF(sp, 0, 0, yr) = N_at_age_dB0(sp, 0, 0, yr) = R(sp, yr); // Hindcast use observed R
2045 }
2046 if(yr >= nyrs_hind){
2047 N_at_age_dBF(sp, 0, 0, yr) = N_at_age_dB0(sp, 0, 0, yr) = exp(log(avg_R(sp)) + rec_dev(sp, yr)); // Projections use mean R given bias in R0
2048 }
2049 }
2050
2051
2052 // - Option 2: Use SRR
2053 // Reached whenever a curve exists, not only when the projection runs on it. The
2054 // breadth is deliberate: Option 1a excludes srr_pred_fun >= 2, so proj_mean_rec
2055 // = 1 (the build_srr() default) with an estimated curve would match NEITHER
2056 // arm, leaving reference-point recruitment 0 after the first year. SB0 would
2057 // decay towards zero, and SB0(sp, nyrs-1) is the depletion reference HCRs 5 and
2058 // 6 read. Projection recruitment is unaffected: 6.8 reads that switch
2059 // separately, so mean-recruitment projections stay mean.
2060 if((proj_mean_rec == 0) | (srr_pred_fun >= 2)){
2061
2062 // - Equilibrium reference points (No recruitment deviation: pass Type(0.0))
2063 // Equilibrium spawning biomass exists every year; yr < minage reads the
2064 // first year's value so the lag stays in bounds.
2065 int rp_yr = yr - minage(sp);
2066 if(rp_yr < 0){ rp_yr = 0; }
2067
2068 // TODO: not floored under an identity-link recruitment offset, so SB0, SBF and B0 can go negative (issue #234).
2069 NByage0(sp, 0, 0, yr) = calculate_recruitment(srr_pred_fun, R0(sp, yr), SB0(sp, rp_yr), alpha(sp, yr), Beta(sp, yr), Type(0.0), SPR0(sp));
2070 NByageF(sp, 0, 0, yr) = calculate_recruitment(srr_pred_fun, R0(sp, yr), SBF(sp, rp_yr), alpha(sp, yr), Beta(sp, yr), Type(0.0), SPR0(sp));
2071
2072 // - Dynamic reference points (Includes annual recruitment deviation: pass rdev)
2073 // Cohorts spawned before the first year take the hindcast's realized R
2074 // (R_init with its deviation), so both runs start from the same cohorts.
2075 Type rdev = rec_dev(sp, yr);
2076 if(yr < minage(sp)){
2077 Type R_early = (yr < nyrs_hind) ? R(sp, yr) : R_init(sp) * exp(rdev);
2078 N_at_age_dB0(sp, 0, 0, yr) = R_early;
2079 N_at_age_dBF(sp, 0, 0, yr) = R_early;
2080 } else {
2081 // Under the Ianelli form the hindcast is on mean recruitment until
2082 // srr_mse_switchyr, so rec_dev there is measured from R0; the curve takes
2083 // the realized deviation from it, log R - log R_hat.
2084 if((srr_fun != srr_pred_fun) & (yr < nyrs_srrmean)){
2085 Type R_curve = calculate_recruitment(srr_pred_fun, R0(sp, yr), ssb(sp, rp_yr), alpha(sp, yr), Beta(sp, yr), Type(0.0), SPR0(sp));
2086 // Same floor as R in 6.6.1, for an identity-link offset that makes the curve non-positive.
2087 if (rec_floor_on) { Type pen_R = 0; R_curve = posfun(R_curve, Type(1e-3), pen_R); zero_N_pen(sp) += pen_R; }
2088 rdev = log(R(sp, yr)) - log(R_curve);
2089 }
2090 // TODO: dynamic-B0 recruitment is not floored under an identity-link offset (issue #234).
2091 N_at_age_dB0(sp, 0, 0, yr) = calculate_recruitment(srr_pred_fun, R0(sp, yr), DynamicSB0(sp, rp_yr), alpha(sp, yr), Beta(sp, yr), rdev, SPR0(sp));
2092 N_at_age_dBF(sp, 0, 0, yr) = calculate_recruitment(srr_pred_fun, R0(sp, yr), DynamicSBF(sp, rp_yr), alpha(sp, yr), Beta(sp, yr), rdev, SPR0(sp));
2093 }
2094
2095 } // End recruitment switch
2096
2097 // Account for sex ratio. The male block is guarded because that slot
2098 // exists only where some species is two-sex; the value would be 0
2099 // anyway (sex_ratio 1, set in 6.4).
2100 NByage0(sp, 0, 0, yr) = NByage0(sp, 0, 0, yr) * sex_ratio(sp, 0); // Females
2101 NByageF(sp, 0, 0, yr) = NByageF(sp, 0, 0, yr) * sex_ratio(sp, 0); // Females
2102 N_at_age_dB0(sp, 0, 0, yr) = N_at_age_dB0(sp, 0, 0, yr) * sex_ratio(sp, 0); // Females
2103 N_at_age_dBF(sp, 0, 0, yr) = N_at_age_dBF(sp, 0, 0, yr) * sex_ratio(sp, 0); // Females
2104
2105 if(nsex(sp) > 1){
2106 // Males (divide by sex_ratio because we multiplied in the lines above)
2107 NByage0(sp, 1, 0, yr) = NByage0(sp, 0, 0, yr) / sex_ratio(sp, 0) * (1.0-sex_ratio(sp, 0));
2108 NByageF(sp, 1, 0, yr) = NByageF(sp, 0, 0, yr) / sex_ratio(sp, 0) * (1.0-sex_ratio(sp, 0));
2109 N_at_age_dB0(sp, 1, 0, yr) = N_at_age_dB0(sp, 0, 0, yr) / sex_ratio(sp, 0) * (1.0-sex_ratio(sp, 0));
2110 N_at_age_dBF(sp, 1, 0, yr) = N_at_age_dBF(sp, 0, 0, yr) / sex_ratio(sp, 0) * (1.0-sex_ratio(sp, 0));
2111 }
2112
2113
2114 // N-at-age year > 1
2115 for(sex = 0; sex < nsex(sp); sex ++){
2116 for(age = 1; age < nages(sp)-1; age++) {
2117 NByage0(sp, sex, age, yr) = NByage0(sp, sex, age-1, yr-1) * exp(-M_at_age(sp, sex, age-1, yr-1)); // F = 0
2118
2119 NByageF(sp, sex, age, yr) = NByageF(sp, sex, age-1, yr-1) * exp(-M_at_age(sp, sex, age-1, yr-1) - Ftarget_at_age(sp, sex, age-1, yr-1)); // F = target
2120
2121 // No 0.001 floor here: the hindcast's is a numerical guard, and dynamic B0
2122 // must not assume a stock that fell to it would not have crashed without
2123 // fishing.
2124 N_at_age_dB0(sp, sex, age, yr) = N_at_age_dB0(sp, sex, age-1, yr-1) * exp(-M_at_age_dB0(sp, sex, age-1, yr - 1)); // F = 0
2125
2126 N_at_age_dBF(sp, sex, age, yr) = N_at_age_dBF(sp, sex, age-1, yr-1) * exp(-M_at_age_dBF(sp, sex, age-1, yr - 1) - Ftarget_at_age(sp, sex, age-1, yr-1)); // F = Ftarget
2127 }
2128
2129 // Plus group -- No fishing
2130 NByage0(sp, sex, nages(sp)-1, yr) = NByage0(sp, sex, nages(sp)-2, yr - 1) * exp(-M_at_age(sp, sex, nages(sp)-2, yr - 1)) + NByage0(sp, sex, nages(sp)-1, yr - 1) * exp(-M_at_age(sp, sex, nages(sp)-1, yr - 1));
2131
2132 NByageF(sp, sex, nages(sp)-1, yr) = NByageF(sp, sex, nages(sp)-2, yr - 1) * exp(-M_at_age(sp, sex, nages(sp)-2, yr - 1) - Ftarget_at_age(sp, sex, nages(sp)-2, yr-1)) + NByageF(sp, sex, nages(sp)-1, yr - 1) * exp(-M_at_age(sp, sex, nages(sp)-1, yr - 1) - Ftarget_at_age(sp, sex, nages(sp)-1, yr-1));
2133
2134 N_at_age_dB0(sp, sex, nages(sp)-1, yr) = N_at_age_dB0(sp, sex, nages(sp)-2, yr - 1) * exp(-M_at_age_dB0(sp, sex, nages(sp)-2, yr - 1)) + N_at_age_dB0(sp, sex, nages(sp)-1, yr - 1) * exp(-M_at_age_dB0(sp, sex, nages(sp)-1, yr - 1));
2135
2136 N_at_age_dBF(sp, sex, nages(sp)-1, yr) = N_at_age_dBF(sp, sex, nages(sp)-2, yr - 1) * exp(-M_at_age_dBF(sp, sex, nages(sp)-2, yr - 1) - Ftarget_at_age(sp, sex, nages(sp)-2, yr-1)) + N_at_age_dBF(sp, sex, nages(sp)-1, yr - 1) * exp(-M_at_age_dBF(sp, sex, nages(sp)-1, yr - 1) - Ftarget_at_age(sp, sex, nages(sp)-1, yr-1));
2137
2138 }
2139 }
2140
2141 // A species with input numbers-at-age (estDynamics > 0) keeps them in the
2142 // dynamic runs; projection years are read directly, as 6.8 fills N later.
2143 if(estDynamics(sp) > 0){
2144 for(sex = 0; sex < nsex(sp); sex ++){
2145 for(age = 0; age < nages(sp); age++){
2146 Type n_fixed = (yr < nyrs_hind) ? N_at_age(sp, sex, age, yr) :
2147 pop_scalar(sp) * NByageFixed(sp, sex, age, yr);
2148 N_at_age_dB0(sp, sex, age, yr) = n_fixed;
2149 N_at_age_dBF(sp, sex, age, yr) = n_fixed;
2150 }
2151 }
2152 }
2153
2154
2155 // Calculate Dynamic SB0 and SB at F target
2156 for(age = 0; age < nages(sp); age++) {
2157
2158 wt_idx_ssb = 2 * sp + 1;
2159 // Multispecies: M_at_age carries the projection's realized M2. The one rule
2160 // that reads SBF, NPFMC (HCR 5), is refused there; SB0 is replaced by MSSB0
2161 // below.
2162 SB0(sp, yr) += NByage0(sp, 0, age, yr) * spawn_output(sp, age, nyrs_hind - 1) * exp(-M_at_age(sp, 0, age, yr) * spawn_month(sp)/12.0);
2163 SBF(sp, yr) += NByageF(sp, 0, age, yr) * spawn_output(sp, age, nyrs_hind - 1) * exp(-(M_at_age(sp, 0, age, yr) + Ftarget_at_age(sp, 0, age, yr)) * spawn_month(sp)/12.0);
2164 DynamicSB0(sp, yr) += N_at_age_dB0(sp, 0, age, yr) * spawn_output(sp, age, yr) * exp(-M_at_age_dB0(sp, 0, age, yr) * spawn_month(sp)/12.0);
2165 DynamicSBF(sp, yr) += N_at_age_dBF(sp, 0, age, yr) * spawn_output(sp, age, yr) * exp(-(M_at_age_dBF(sp, 0, age, yr) + Ftarget_at_age(sp, 0, age, yr)) * spawn_month(sp)/12.0);
2166
2167 for(sex = 0; sex < nsex(sp); sex ++){
2168
2169 wt_idx_pop = 2 * sp;
2170 B0(sp, yr) += NByage0(sp, sex, age, yr) * weight_hat( wt_idx_pop, sex, age, nyrs_hind - 1 );
2171 DynamicB0(sp, yr) += N_at_age_dB0(sp, sex,age, yr) * weight_hat( wt_idx_pop, sex, age, yr );
2172 }
2173 }
2174
2175
2176 // Input SB0 (if running in multi-species mode)
2177 if(msmMode > 0){
2178 SB0(sp, yr) = MSSB0(sp);
2179 B0(sp, yr) = MSB0(sp);
2180 }
2181 }
2182 }
2183
2184 // 6.7-6.9. FORECAST NUMBERS AT AGE, BIOMASS-AT-AGE (mt), and ssb-AT-AGE (mt)
2185 // Includes Harvest Control Rules
2186 for(sp = 0; sp < nspp; sp++) {
2187 for(yr = nyrs_hind; yr < nyrs; yr++){
2188
2189 // 6.7. HARVEST CONTROL RULES FOR PROJECTION
2190 // Equilibrium or dynamic reference points (i.e. SB0 or dynamic SB0)
2191 Type ref_SB0 = (DynamicHCR == 1) ? DynamicSB0(sp, yr-1) : SB0(sp, nyrs-1);
2192 Type ref_SBF = (DynamicHCR == 1) ? DynamicSBF(sp, yr-1) : SBF(sp, nyrs-1);
2193
2194 switch(HCR){
2195 case 0: // No fishing
2196 proj_F(sp, yr) = 0.0;
2197 break;
2198
2199 case 1: // CMSY
2200 // proj_F(sp, yr) retains its incoming value
2201 break;
2202
2203 case 2: // Constant F
2204 // proj_F(sp, yr) retains its incoming value
2205 break;
2206
2207 case 3: // Constant F to acheive X% of SB0
2208 // proj_F(sp, yr) retains its incoming value
2209 break;
2210
2211 case 4: // Constant Fspr with multiplier
2212 // proj_F(sp, yr) retains its incoming value
2213 break;
2214
2215 case 5: // NPFMC Tier 3 HCR
2216 if(ssb(sp, yr-1) < ref_SBF){
2217 proj_F(sp, yr) = Ftarget(sp) * (((ssb(sp, yr-1)/ref_SBF)-Alpha(sp))/(1-Alpha(sp))); // Used Fabc of FtargetSPR%
2218 }
2219 if((ssb(sp, nyrs_hind-1) < ref_SB0 * Plimit(sp)) || (ssb(sp, yr-1) / ref_SBF < Alpha(sp))){ // If overfished
2220 proj_F(sp, yr) = 0.0;
2221 }
2222 break;
2223
2224 case 6: // PFMC Category 1 HCR
2225 if(ssb(sp, yr-1) < ref_SB0 * Ptarget(sp)){
2226 proj_F(sp, yr) = (Flimit(sp) + QnormHCR(sp)) * (ref_SB0 * Ptarget(sp) * (ssb(sp, yr-1) - ref_SB0 * Plimit(sp))) / (ssb(sp, yr-1) * (ref_SB0 * (Ptarget(sp) - Plimit(sp))));
2227 }
2228 if(ssb(sp, yr-1) < ref_SB0 * Plimit(sp)){ // If overfished
2229 proj_F(sp, yr) = 0.0;
2230 }
2231 break;
2232
2233 case 7: // SESSF Tier 1 HCR
2234 if(ssb(sp, yr-1) < ref_SB0 * Ptarget(sp)){
2235 proj_F(sp, yr) = Ftarget(sp) * ((ssb(sp, yr-1)/(ref_SB0 * Plimit(sp)))-1); // Used Fabc of FtargetSPR%
2236 }
2237 if(ssb(sp, yr-1) < ref_SB0 * Plimit(sp)){ // If overfished
2238 proj_F(sp, yr) = 0.0;
2239 }
2240 break;
2241 }
2242
2243
2244 // No projected F when the forecast is off or numbers-at-age are input (estDynamics > 0).
2245 if((forecast(sp) == 0) | (estDynamics(sp) > 0)){
2246 proj_F(sp, yr) = 0.0;
2247 }
2248
2249 // -- Update F for the projection (account for selectivity and fleets)
2250 for(age = 0; age < nages(sp); age++) {
2251 for(sex = 0; sex < nsex(sp); sex ++){
2252 F_at_age(sp, sex, age, yr) = 0.0;
2253 }
2254 }
2255
2256 // -- Multiply F from HCR by selectivity and fleet proportion
2257 F_spp(sp, yr) = proj_F(sp, yr);
2258 for(flt = 0; flt < n_flt; flt++) {
2259 if((sp == flt_spp(flt)) & (flt_type(flt) == 1)){ // Fisheries only, as in 5.12
2260 F_flt(flt, yr) = proj_F_prop(flt) * proj_F(sp, yr);
2261 for(age = 0; age < nages(sp); age++) {
2262 for(sex = 0; sex < nsex(sp); sex ++){
2263 F_flt_age(flt, sex, age, yr) = sel_at_age(flt, sex, age, nyrs_hind - 1) * proj_F_prop(flt) * proj_F(sp, yr); // Projection F uses the terminal hindcast year's selectivity
2264 if(flt_type(flt) == 1){
2265 F_at_age(sp, sex, age, yr) += F_flt_age(flt, sex, age, yr);
2266 }
2267 }
2268 }
2269 }
2270 }
2271
2272
2273 // -- Update mortality for forecast with new F
2274 for(age = 0; age < nages(sp); age++) {
2275 for(sex = 0; sex < nsex(sp); sex ++){
2276 M_at_age(sp, sex, age, yr) = M1_at_age(sp, sex, age, yr) + M2_at_age(sp, sex, age, yr);
2277 Z_at_age(sp, sex, age, yr) = M1_at_age(sp, sex, age, yr) + F_at_age(sp, sex, age, yr) + M2_at_age(sp, sex, age, yr);
2278 }
2279 }
2280
2281
2282 // 6.8. FORECAST NUMBERS AT AGE, BIOMASS-AT-AGE (mt), and ssb-AT-AGE (mt)
2283 // -- 6.8.1. Forecasted recruitment
2284 // - Option 1: Use mean rec
2285 if(proj_mean_rec == 1){
2286 R(sp, yr) = avg_R(sp); // // Projections use mean R given bias in R0
2287 }
2288
2289 // - Option 2: Use SRR and rec devs
2290 if(proj_mean_rec == 0){
2291
2292 // - Calculate recruitment (with linkage offsets pre-added).
2293 // Pre-styr spawning year takes R_init, as in the hindcast block;
2294 // reachable only if the hindcast is shorter than minage.
2295 int proj_spawn_yr = yr - minage(sp);
2296 int proj_srr_use = (proj_spawn_yr < 0) ? 0 : srr_pred_fun;
2297 Type ssb_tmp = (proj_spawn_yr < 0) ? Type(0.0) : ssb(sp, proj_spawn_yr);
2298 Type proj_rec_mean = (proj_spawn_yr < 0) ? R_init(sp) : R0(sp, yr);
2299 // TODO: projected recruitment is not floored under an identity-link offset (issue #234).
2300 R(sp, yr) = calculate_recruitment(proj_srr_use, proj_rec_mean, ssb_tmp, alpha(sp, yr), Beta(sp, yr), rec_dev(sp, yr), SPR0(sp));
2301 }
2302
2303 N_at_age(sp, 0, 0 , yr) = R(sp, yr) * sex_ratio(sp, 0);
2304 if(nsex(sp) > 1){
2305 N_at_age(sp, 1, 0 , yr) = R(sp, yr) * (1 - sex_ratio(sp, 0));
2306 }
2307
2308
2309 // -- 6.8.2. Ages > recruitment
2310 for(age = 0; age < nages(sp); age++) {
2311 for(sex = 0; sex < nsex(sp); sex ++){
2312 switch(estDynamics(sp)){
2313 case 0: // Estimated numbers-at-age
2314
2315 // -- 6.8.2. Where minage + 1 <= age < Ai
2316 if(age < (nages(sp) - 1)) {
2317 N_at_age(sp, sex, age + 1, yr) = N_at_age(sp, sex, age, yr - 1) * exp(-Z_at_age(sp, sex, age, yr-1));
2318 }
2319
2320 // -- 6.8.3. Plus group where age > Ai.
2321 if(age == (nages(sp) - 1)) {
2322 N_at_age(sp, sex, age, yr) = N_at_age(sp, sex, age - 1, yr - 1) * exp(-Z_at_age(sp, sex, age-1, yr-1)) + N_at_age(sp, sex, age, yr - 1) * exp(-Z_at_age(sp, sex, age, yr-1));
2323 }
2324 break;
2325
2326 case 1: // Fixed numbers-at-age - fixed scalar
2327 N_at_age(sp, sex, age, yr) = pop_scalar(sp) * NByageFixed(sp, sex, age, yr);
2328 break;
2329
2330 case 2: // Fixed numbers-at-age age-independent scalar
2331 N_at_age(sp, sex, age, yr) = pop_scalar(sp) * NByageFixed(sp, sex, age, yr);
2332 break;
2333
2334 default: // Wrong estDynamics
2335 error("Invalid 'estDynamics'");
2336 }
2337
2338 // -- 6.8.4. FORECAST BIOMASS
2339 wt_idx_pop = 2 * sp;
2340 biomass_at_age(sp, sex, age, yr) = N_at_age(sp, sex, age, yr) * weight_hat( wt_idx_pop, sex, age, nyrs_hind-1 ); // 6.5.
2341 biomass(sp, yr) += biomass_at_age(sp, sex, age, yr);
2342 } // End sex loop
2343
2344 // -- 6.8.5. FORECAST SSB (SUM ACROSS AGES)
2345 wt_idx_ssb = 2 * sp + 1;
2346 ssb(sp, yr) += N_at_age(sp, 0, age, yr) * exp(-Z_at_age(sp, 0, age, yr) * spawn_month(sp)/12.0) * spawn_output(sp, age, nyrs_hind-1); // 6.6.
2347 }
2348 }
2349 }
2350
2351
2352 // 6.9. EXPECTED RECRUITMENT
2353 // The function-scope `yr` is left at `nyrs` by the forecast loop above, so
2354 // the year-0 block uses the explicit `first_yr` constant: reading
2355 // R0/alpha/Beta at `yr` would run one column past the end and segfault
2356 // inside MakeADFun. The assertion pins those matrices to the year horizon.
2357 const int first_yr = 0;
2358 if (R0.cols() != nyrs || alpha.cols() != nyrs || Beta.cols() != nyrs) {
2359 error("Section 6.9: R0/alpha/Beta column count does not equal nyrs; "
2360 "recruitment-parameter matrices were sized inconsistently.");
2361 }
2362 for(sp = 0; sp < nspp; sp++) {
2363
2364 // Year 1 (arent fit in likelihood)
2365 // Under predation there is no SPRFinit (6.2) to place the first year on
2366 // the curve, so it takes R_init, the anchor its realised recruitment uses.
2367 if((msmMode > 0) & (srr_pred_fun > 1)){
2368 R_hat(sp, first_yr) = R_init(sp);
2369 } else {
2370 switch(srr_pred_fun){
2371 case 0: // Random about mean (e.g. Alaska)
2372 R_hat(sp, first_yr) = R0(sp, first_yr);
2373 break;
2374
2375 case 1: // Random about mean with environmental effects
2376 R_hat(sp, first_yr) = R0(sp, first_yr);
2377 break;
2378
2379 case 2: // Beverton-Holt
2380 R_hat(sp, first_yr) = (alpha(sp, first_yr) - 1/SPRFinit(sp)) / Beta(sp, first_yr); // (Alpha-1/SPR0)/beta
2381 break;
2382
2383 case 3: // Retired alias of 2; build_srr() refuses it (alpha moves via a linkage)
2384 R_hat(sp, first_yr) = (alpha(sp, first_yr) - 1/SPRFinit(sp)) / Beta(sp, first_yr); // (Alpha-1/SPR0)/beta
2385 break;
2386
2387 case 4: // Ricker
2388 { Type aS = alpha(sp, first_yr) * SPRFinit(sp); // kept positive so the log is finite; R_hat itself is floored below
2389 if (rec_floor_on) { Type pen_d = 0; aS = posfun(aS, Type(1e-3), pen_d); }
2390 R_hat(sp, first_yr) = log(aS) / (Beta(sp, first_yr) * SPRFinit(sp)/1000000.0); }
2391 break;
2392
2393 case 5: // Ricker with environmental impacts on alpha
2394 { Type aS = alpha(sp, first_yr) * SPRFinit(sp); // kept positive so the log is finite; R_hat itself is floored below
2395 if (rec_floor_on) { Type pen_d = 0; aS = posfun(aS, Type(1e-3), pen_d); }
2396 R_hat(sp, first_yr) = log(aS) / (Beta(sp, first_yr) * SPRFinit(sp)/1000000.0); }
2397 break;
2398 default:
2399 error("Invalid 'srr_pred_fun'");
2400 }
2401 }
2402 // Same floor as R in 6.6.1: the first year's R_hat is reported and read by retrospective().
2403 if (rec_floor_on) { Type pen_R = 0; R_hat(sp, first_yr) = posfun(R_hat(sp, first_yr), Type(1e-3), pen_R); zero_N_pen(sp) += pen_R; }
2404
2405 // Year 1+
2406 for(yr = 1; yr < nyrs; yr++){
2407 // Pre-styr spawning year takes R_init, the SAME anchor the realised recruitment
2408 // above uses, so expected and realised differ only by rec_dev and the
2409 // stock-recruit penalty (13.9) scores the deviation alone -- these years have no
2410 // spawning biomass to carry information about the curve's level. Anchoring on the
2411 // curve instead (R_hat's own first year) leaves a mean-versus-curve level gap the
2412 // penalty reads as signal: under the Ianelli configuration (srr_fun mean,
2413 // srr_pred_fun BevertonHolt) a fixed 0.83 nats per guarded year, pulling rec_pars
2414 // against alpha and Beta from years with no data. Identical for
2415 // BevertonHolt/BevertonHolt and Ricker/Ricker, where R_init is already R_hat's
2416 // first-year value.
2417 int hat_spawn_yr = yr - minage(sp);
2418 int hat_srr_use = (hat_spawn_yr < 0) ? 0 : srr_pred_fun;
2419 Type ssb_tmp = (hat_spawn_yr < 0) ? Type(0.0) : ssb(sp, hat_spawn_yr);
2420 Type hat_rec_mean = (hat_spawn_yr < 0) ? R_init(sp) : R0(sp, yr);
2421
2422 // Expected recruitment carries no deviation, hence Type(0.0).
2423 R_hat(sp, yr) = calculate_recruitment(hat_srr_use, hat_rec_mean, ssb_tmp, alpha(sp, yr), Beta(sp, yr), Type(0.0), SPR0(sp));
2424 // Same floor as R in 6.6.1: log(R_hat) enters the stock-recruit penalty.
2425 if (rec_floor_on) { Type pen_R = 0; R_hat(sp, yr) = posfun(R_hat(sp, yr), Type(1e-3), pen_R); zero_N_pen(sp) += pen_R; }
2426 }
2427 }
2428
2429
2430
2431 // 6.11. ESTIMATE AVERAGE NUMBERS AT AGE
2432 for(sp = 0; sp < nspp; sp++) {
2433 for(sex = 0; sex < nsex(sp); sex ++){
2434 for(age = 0; age < nages(sp); age++) {
2435 for(yr = 0; yr < nyrs; yr++) {
2436 // MSVPA average N over the year.
2437 avgN_at_age(sp, sex, age, yr) = N_at_age(sp, sex, age, yr) * (1 - exp(-Z_at_age(sp, sex, age, yr))) / Z_at_age(sp, sex, age, yr);
2438 avgN_at_age_dB0(sp, sex, age, yr) = N_at_age_dB0(sp, sex, age, yr) * (1 - exp(-M_at_age_dB0(sp, sex, age, yr))) / M_at_age_dB0(sp, sex, age, yr);
2439 avgN_at_age_dBF(sp, sex, age, yr) = N_at_age_dBF(sp, sex, age, yr) * (1 - exp(-M_at_age_dBF(sp, sex, age, yr) - Ftarget_at_age(sp, sex, age, yr))) / (M_at_age_dBF(sp, sex, age, yr) + Ftarget_at_age(sp, sex, age, yr));
2440 }
2441 }
2442 }
2443 }
2444
2445
2446 /** ------------------------------------------------------------------------ //
2447 // 7. MSVPA PREDATION MORTALITY EQUATIONS //
2448 * ------------------------------------------------------------------------- */
2449
2450 // START PREDATION
2451 if(msmMode > 0) {
2452
2453 // NOTE -- LOOPING INDICES -- sp = species, age = age, ln = length, ksp = prey, k_age = prey age (yr), k_ln = prey length, yr = year, rsp = predator, r_age = predator age (yr), r_ln = predator length
2454
2455 // 7.1. Empirical MSVPA Suitability
2456 calculate_msvpa_suitability(stom_div_bio, suitability, suit_other,
2457 nspp, nyrs, suit_styr, suit_endyr, nyrs_suit, msmMode,
2458 nsex, nages, suitMode, diet_prop, avgN_at_age, weight_hat, other_food_diet_prop);
2459
2460
2461 // 7.2. MSVPA PREDATION MORTALITY
2462 if((msmMode == 1) | (msmMode == 2)) {
2463 calculate_msvpa_predation(avail_food, avail_food_dB0, avail_food_dBF,
2464 M2_at_age, M2_at_age_dB0, M2_at_age_dBF, M2_prop,
2465 B_eaten, B_eaten_as_prey, diet_prop_hat, other_diet_prop_hat,
2466 nspp, nyrs, nsex, nages, msmMode,
2467 avgN_at_age, avgN_at_age_dB0, avgN_at_age_dBF,
2468 suitability, suit_other, weight_hat, consumption_at_age, other_food);
2469 }
2470 } // End 7. Predation mortality
2471 } // End population dynamics iterations
2472
2473
2474
2475 /** ------------------------------------------------------------------------ //
2476 // 8. INDEX COMPONENTS EQUATIONS //
2477 * ------------------------------------------------------------------------- */
2478
2479 // -- 8.1. Index of abundance/biomass
2480 for(index_ind = 0; index_ind < index_ctl.rows(); index_ind++){
2481
2482 index = index_ctl(index_ind, 0) - 1; // Survey index
2483 sp = index_ctl(index_ind, 1) - 1; // Species
2484 flt_yr = index_ctl(index_ind, 2); // Data year
2485 wt_idx_flt = nspp * 2 + index; // Fleet weight index
2486 mo = index_n(index_ind, 0); // Month
2487
2488 index_hat(index_ind) = 0.0;
2489
2490 if(flt_yr > 0){
2491 flt_yr = flt_yr - styr;
2492 }
2493 else if(flt_yr < 0){
2494 flt_yr = -flt_yr - styr;
2495 }
2496
2497 for(age = 0; age < nages(sp); age++) {
2498 for(sex = 0; sex < nsex(sp); sex++){
2499
2500 // Index by fleet type. Month is not used for a fishery: a year-average
2501 // has no instant to be taken at. SS3 splits the same way on its
2502 // per-fleet survey timing (SS_expval.tpl: timing >= 0 -> exp(-Z*timing);
2503 // timing < 0, required of every fishing fleet -> (1 - exp(-Z))/Z).
2504 //
2505 // A seasonal fishery takes the same form. Its exact predictor over [t1, t1+D] is
2506 // N_a * exp(-Zpre_a * t1) * (1 - exp(-Zwin_a * D)) / (Zwin_a * D)
2507 // Zpre_a = M_a + other fleets' F_a (this fishery has yet to fish)
2508 // Zwin_a = M_a + F_a/D + other fleets' F_a
2509 // Since Zwin*D = M*D + F, the window factor tracks F almost as the annual
2510 // one does, and q absorbs the F-free exp(-M*t1) offset. Trend error
2511 // against the exact form is ~1.5% over F 0.05-0.8, against -29% to +33%
2512 // for the snapshot. TODO: fit the window form directly if a fleet needs it.
2513 Type n_index;
2514 if(flt_type(index) == 1){
2515 n_index = N_at_age(sp, sex, age, flt_yr) * (1.0 - exp( - Z_at_age(sp, sex, age, flt_yr))) / Z_at_age(sp, sex, age, flt_yr);
2516 } else {
2517 n_index = N_at_age(sp, sex, age, flt_yr) * exp( - (mo/12.0) * Z_at_age(sp, sex, age, flt_yr));
2518 }
2519
2520 // Weight
2521 if(flt_units(index) == 1){
2522 index_hat(index_ind) += n_index * sel_at_age(index, sex, age, flt_yr) * weight_hat(wt_idx_flt, sex, age, flt_yr );
2523 }
2524 // Numbers
2525 if(flt_units(index) == 2){
2526 index_hat(index_ind) += n_index * sel_at_age(index, sex, age, flt_yr);
2527 }
2528 }
2529 }
2530 }
2531
2532
2533 // -- 8.2. Analytical survey q following Walters and Ludwig 1994
2534 index_n_obs.setZero();
2535 index_q_analytical.setZero();
2536 for(index_ind = 0; index_ind < index_ctl.rows(); index_ind++){
2537
2538
2539
2540 index = index_ctl(index_ind, 0) - 1; // Survey index
2541 sp = index_ctl(index_ind, 1) - 1; // Species
2542 flt_yr = index_ctl(index_ind, 2); // Data year
2543
2544 if(flt_yr > 0){
2545 flt_yr = flt_yr - styr;
2546
2547
2548 mo = index_n(index_ind, 0); // Month
2549 if(flt_yr < nyrs_hind){
2550
2551 // If etimated standard deviation or analytical sigma (non-time-varying)
2552 if(est_sigma_index(index) > 0) {
2553 index_n_obs(index) += 1;
2554 index_q_analytical(index) += log(index_obs(index_ind, 0) / index_hat(index_ind));
2555 }
2556
2557 // If time-varying sigma
2558 if(est_sigma_index(index) == 0 ) {
2559 index_n_obs(index) += 1 / square(index_obs(index_ind, 1));
2560 index_q_analytical(index) += log(index_obs(index_ind, 0) / index_hat(index_ind)) / square(index_obs(index_ind, 1));
2561 }
2562 }
2563 }
2564 }
2565
2566 // Take average
2567 for(index = 0 ; index < n_flt; index ++){
2568 index_q_analytical(index) = exp(index_q_analytical(index) / index_n_obs(index));
2569
2570 // Set index_q to analytical if used
2571 if(est_index_q(index) == 3){
2572 for(yr = 0; yr < nyrs_hind; yr++){
2573 index_q(index, yr) = index_q_analytical(index);
2574 }
2575 }
2576 }
2577
2578
2579 // -- 8.2b. Arithmetic-mean analytical q (AMAK/ebswp): q = sum(obs)/sum(pred)
2580 // over the fitted survey years (est_index_q == 7, "AnalyticalArith"). This
2581 // reproduces the AMAK BTS q_bts = mean(ob_bts)/mean(eb_bts) and is the q
2582 // form paired with the MVN covariance survey likelihood (Index_loglike =
2583 // "MVN"). Uses index_hat before it is scaled by q just below.
2584 vector<Type> index_obs_sum(n_flt); index_obs_sum.setZero();
2585 vector<Type> index_hat_sum(n_flt); index_hat_sum.setZero();
2586 for(index_ind = 0; index_ind < index_ctl.rows(); index_ind++){
2587 index = index_ctl(index_ind, 0) - 1;
2588 flt_yr = index_ctl(index_ind, 2);
2589 if(flt_yr > 0){
2590 flt_yr = flt_yr - styr;
2591 if(flt_yr < nyrs_hind){
2592 index_obs_sum(index) += index_obs(index_ind, 0);
2593 index_hat_sum(index) += index_hat(index_ind);
2594 }
2595 }
2596 }
2597 for(index = 0; index < n_flt; index++){
2598 if(est_index_q(index) == 7){
2599 Type q_arith = index_obs_sum(index) / index_hat_sum(index);
2600 for(yr = 0; yr < nyrs_hind; yr++){
2601 index_q(index, yr) = q_arith;
2602 }
2603 }
2604 }
2605
2606
2607 // -- 8.3. Survey Biomass - multiply by q
2608 for(index_ind = 0; index_ind < index_ctl.rows(); index_ind++){
2609
2610 index = index_ctl(index_ind, 0) - 1; // Survey index
2611 flt_yr = index_ctl(index_ind, 2); // Data year
2612
2613 if(flt_yr > 0){
2614 flt_yr = flt_yr - styr;
2615 }
2616 else if(flt_yr < 0){
2617 flt_yr = -flt_yr - styr;
2618 }
2619
2620 // Hindcast or projection indexing for q
2621 int yr_ind = (flt_yr < nyrs_hind) ? flt_yr : (nyrs_hind - 1);
2622
2623 index_hat(index_ind) = index_q(index, yr_ind) * index_hat(index_ind); // pow(index_hat(index_ind), (1 + index_q_pow(index)));
2624 }
2625
2626
2627
2628 // --8.4. Calculate analytical sigma following Walters and Ludwig 1994
2629 // The positive-observation guard matches the catch estimator below; it is
2630 // belt-and-braces, data_check() having already refused a non-positive index.
2631 index_n_obs.setZero();
2632 index_analytical_sd.setZero();
2633 for(index_ind = 0; index_ind < index_ctl.rows(); index_ind++){
2634
2635 index = index_ctl(index_ind, 0) - 1; // Survey index
2636 flt_yr = index_ctl(index_ind, 2); // Data year
2637
2638 if(flt_yr > 0){
2639 flt_yr = flt_yr - styr;
2640
2641 if((flt_yr < nyrs_hind) && (index_obs(index_ind, 0) > 0)){
2642 index_n_obs(index) += 1;
2643 index_analytical_sd(index) += square( log(index_obs(index_ind, 0)) - log(index_hat(index_ind)));
2644 }
2645 }
2646 }
2647
2648 for(index = 0 ; index < n_flt; index ++){
2649 if(index_n_obs(index) > 0){
2650 index_analytical_sd(index) = concentrated_lognormal_sd(index_analytical_sd(index) / index_n_obs(index), bias_adjust_obs);
2651 }
2652 }
2653
2654
2655 /** ------------------------------------------------------------------------ //
2656 // 9. FISHERY COMPONENTS EQUATIONS //
2657 * ------------------------------------------------------------------------- */
2658 // 9.1. ESTIMATE CATCH-AT-AGE and TOTAL YIELD (mt or thousands of fish)
2659 for(fsh_ind = 0; fsh_ind < catch_ctl.rows(); fsh_ind++){
2660
2661 flt = catch_ctl(fsh_ind, 0) - 1; // Fleet index (0-based)
2662 sp = catch_ctl(fsh_ind, 1) - 1; // Species
2663 flt_yr = catch_ctl(fsh_ind, 2); // Data year
2664 mo = catch_n(fsh_ind, 0); // Month
2665 wt_idx_flt = nspp * 2 + flt; // Weight index for fleet
2666
2667 catch_hat(fsh_ind) = 0.0;
2668 max_catch_hat(fsh_ind) = 0.0;
2669
2670 if(flt_yr > 0){
2671 flt_yr = flt_yr - styr;
2672 }
2673 else if(flt_yr < 0){
2674 flt_yr = -flt_yr - styr;
2675 }
2676
2677 for(sex = 0; sex < nsex(sp); sex ++){
2678 for(age = 0; age < nages(sp); age++) {
2679
2680 // By weight
2681 if(flt_units(flt) == 1){
2682 catch_hat(fsh_ind) += F_flt_age(flt, sex, age, flt_yr) / Z_at_age(sp, sex, age, flt_yr) * (1.0 - exp(-Z_at_age(sp, sex, age, flt_yr))) * N_at_age(sp, sex, age, flt_yr) * weight_hat( wt_idx_flt, sex, age, flt_yr ); // 5.5.
2683 max_catch_hat(fsh_ind) += N_at_age(sp, sex, age, flt_yr) * weight_hat( wt_idx_flt, sex, age, flt_yr ) * sel_at_age(flt, sex, age, flt_yr) * proj_F_prop(flt);
2684 }
2685
2686 // By numbers
2687 if(flt_units(flt) == 2){
2688 catch_hat(fsh_ind) += F_flt_age(flt, sex, age, flt_yr) / Z_at_age(sp, sex, age, flt_yr) * (1.0 - exp(-Z_at_age(sp, sex, age, flt_yr))) * N_at_age(sp, sex, age, flt_yr);
2689 max_catch_hat(fsh_ind) += N_at_age(sp, sex, age, flt_yr) * sel_at_age(flt, sex, age, flt_yr) * proj_F_prop(flt);
2690 }
2691 }
2692 }
2693 }
2694
2695 // -- 9.1a. Initial equilibrium catch, in weight or numbers per the fleet's
2696 // Observation_units, as catch_hat is:
2697 // C_eq = sum_a Finit * s_a * w_a * N_eq_a * (1 - exp(-Z_a)) / Z_a
2698 // Z_a = M1_a + Finit * s_a
2699 // Baranov on the deviation-free equilibrium age structure, at the fleet's own
2700 // selectivity and weight in the first hindcast year; SS3's Equil_catch
2701 // (SS_popdyn.tpl, Do_Equil_Calc). R supplies these rows only under initMode 6,
2702 // the one mode that builds N_eq at this same Finit * selectivity.
2703 vector<Type> equil_catch_hat(equil_catch_ctl.rows()); equil_catch_hat.setZero();
2704 for(int eq_ind = 0; eq_ind < equil_catch_ctl.rows(); eq_ind++){
2705 flt = equil_catch_ctl(eq_ind, 0) - 1;
2706 sp = equil_catch_ctl(eq_ind, 1) - 1;
2707 wt_idx_flt = nspp * 2 + flt;
2708 for(sex = 0; sex < nsex(sp); sex++){
2709 for(age = 0; age < nages(sp); age++){
2710 Type s_a = sel_at_age(flt, sex, age, 0);
2711 Type Z_a = M1_at_age(sp, sex, age, 0) + Finit(sp) * s_a;
2712 Type baranov = Finit(sp) * s_a / Z_a * (1.0 - exp(-Z_a)) * N_eq(sp, sex, age);
2713 if(flt_units(flt) == 1){ // by weight
2714 equil_catch_hat(eq_ind) += baranov * weight_hat( wt_idx_flt, sex, age, 0 );
2715 }
2716 if(flt_units(flt) == 2){ // by numbers
2717 equil_catch_hat(eq_ind) += baranov;
2718 }
2719 }
2720 }
2721 }
2722
2723 // -- 9.1b. Analytical catch sigma, following Walters and Ludwig (1994)
2724 // The sd that minimises the catch density: squared log residuals accumulated
2725 // here, concentrated in concentrated_lognormal_sd(). The row predicate matches
2726 // the likelihood's own -- a fitted year, a fishery, a positive observation --
2727 // so the sd is estimated over exactly the rows it is applied to. A zero-catch
2728 // year is legal input and log(0) is -inf, which would poison the fleet's sd,
2729 // hence the positive-observation guard.
2730 //
2731 // It reads catch_obs, not obsvec, so oneStepPredict()'s candidate value never
2732 // reaches it: the sd enters an OSA sweep as a fixed full-data number, as an
2733 // estimated sd held at its MLE would.
2734 catch_n_obs.setZero();
2735 catch_analytical_sd.setZero();
2736 for(fsh_ind = 0; fsh_ind < catch_ctl.rows(); fsh_ind++){
2737
2738 flt = catch_ctl(fsh_ind, 0) - 1;
2739 flt_yr = catch_ctl(fsh_ind, 2);
2740
2741 if((flt_yr > 0) && (flt_yr <= endyr) && (flt_type(flt) == 1) && (catch_obs(fsh_ind, 0) > 0)){
2742 catch_n_obs(flt) += 1;
2743 catch_analytical_sd(flt) += square( log(catch_obs(fsh_ind, 0)) - log(catch_hat(fsh_ind)) );
2744 }
2745 }
2746
2747 for(flt = 0; flt < n_flt; flt++){
2748 if(catch_n_obs(flt) > 0){
2749 catch_analytical_sd(flt) = concentrated_lognormal_sd(catch_analytical_sd(flt) / catch_n_obs(flt), bias_adjust_obs);
2750 }
2751 }
2752
2753
2754 // 9.2 Exploitable biomass ----
2755 exploitable_biomass.setZero();
2756
2757 for(flt = 0; flt < n_flt; flt++) {
2758
2759 sp = flt_spp(flt);
2760 wt_idx_flt = nspp * 2 + flt;
2761
2762 if(flt_type(flt) == 1){ //Fishery only
2763 for(yr = 0; yr < nyrs; yr++) {
2764 for(age = 0; age < nages(sp); age++) {
2765 for(sex = 0; sex < nsex(sp); sex ++){
2766 exploitable_biomass(sp, yr) += N_at_age(sp, sex, age, yr) * weight_hat( wt_idx_flt, sex, age, yr ) * sel_at_age(flt, sex, age, yr) * proj_F_prop(flt);
2767 }
2768 }
2769 }
2770 }
2771 }
2772
2773
2774 /** ------------------------------------------------------------------------ //
2775 * 10. COMPOSITION EQUATIONS //
2776 * ------------------------------------------------------------------------- */
2777
2778 // -- 10.1. Marginal age/length composition
2779 age_hat.setZero(); // Age-composition
2780 age_obs_hat.setZero(); // Age-composition with ageing error
2781 comp_hat.setZero(); // Age- or length-comp following data
2782 for(comp_ind = 0; comp_ind < comp_hat.rows(); comp_ind++){
2783
2784 flt = comp_ctl(comp_ind, 0) - 1; // Fleet index (0-based)
2785 sp = comp_ctl(comp_ind, 1) - 1; // Species
2786 flt_sex = comp_ctl(comp_ind, 2); // Comp sex: 0 = combined, 1 = female, 2 = male
2787 comp_type = comp_ctl(comp_ind, 3); // Comp type: 0 = age, 1 = length, 2 = CAAL
2788 yr = comp_ctl(comp_ind, 4); // Data year
2789 mo = (growth_model(sp) > 0) ? flt_month(flt) : comp_n(comp_ind, 0); // Month (use fleet month if estimating growth, otherwise use from data)
2790 int wtind = nspp * 2 + flt;
2791 n_hat(comp_ind) = 0.0;
2792
2793 // Likelihood (yr > 0) vs prediction (yr < 0)
2794 if(yr > 0){
2795 yr = yr - styr;
2796 }
2797 else if(yr < 0){
2798 yr = -yr - styr;
2799 }
2800
2801 // Hindcast
2802 Type Frate = 0.0;
2803 if(yr < nyrs_hind){
2804 yr_ind = yr;
2805 Frate = exp(log_F(flt, yr));
2806 }
2807
2808 // Projection
2809 if(yr >= nyrs_hind){
2810 yr_ind = nyrs_hind - 1;
2811 Frate = proj_F_prop(flt) * proj_F(sp, yr);
2812 }
2813
2814 // 10.1.1. Estimated growth
2815 if(growth_model(sp) > 0){
2816 for(age = 0; age < nages(sp); age++) {
2817 for(ln = 0; ln < nlengths(sp); ln++) {
2818 for(sex = 0; sex < nsex(sp); sex ++){
2819
2820 switch(flt_type(flt)){
2821 case 1: // - Fishery
2822 if(flt_sel_dim(flt) == 1){ // Length based
2823 {
2824 // sel(L) * P(L|age) over this data bin's population bins (2.3c).
2825 Type ps_ln = 0.0;
2826 for(int lp = pop_bin_lo(sp, ln); lp <= pop_bin_hi(sp, ln); lp++){
2827 ps_ln += sel_at_length(flt, sex, lp, yr) * growth_matrix(wtind, sex, age, lp, yr);
2828 }
2829 pred_CAAL(flt, sex, age, ln, yr) = ps_ln * (Frate / Z_at_age(sp, sex, age, yr) * (1 - exp(-Z_at_age(sp, sex, age, yr))) * N_at_age(sp, sex, age, yr));
2830 }
2831 }
2832 break;
2833
2834
2835 case 2: // - Survey
2836 if(flt_sel_dim(flt) == 1){ // Length based
2837 {
2838 // sel(L) * P(L|age) over this data bin's population bins (2.3c).
2839 Type ps_ln = 0.0;
2840 for(int lp = pop_bin_lo(sp, ln); lp <= pop_bin_hi(sp, ln); lp++){
2841 ps_ln += sel_at_length(flt, sex, lp, yr) * growth_matrix(wtind, sex, age, lp, yr);
2842 }
2843 pred_CAAL(flt, sex, age, ln, yr) = ps_ln * (N_at_age(sp, sex, age, yr) * index_q(flt, yr_ind) * exp( - Type(mo/12.0) * Z_at_age(sp, sex, age, yr)));
2844 }
2845 }
2846 break;
2847 }
2848 }
2849 }
2850 }
2851
2852 // Assign CAAL to marginal age or length composition
2853 for(age = 0; age < nages(sp); age++) {
2854 for(ln = 0; ln < nlengths(sp); ln++) {
2855
2856 // Age composition
2857 // - Catch-at-age
2858 if(comp_type == 0){
2859 switch(flt_sex){
2860 case 0: // Sexes combined or 1 sex assessment
2861 for(sex = 0; sex < nsex(sp); sex ++){
2862 age_hat(comp_ind, age ) += pred_CAAL(flt, sex, age, ln, yr);
2863 }
2864 break;
2865
2866 case 1: case 2: // Sex-specific composition data
2867 sex = flt_sex - 1;
2868 age_hat(comp_ind, age ) += pred_CAAL(flt, sex, age, ln, yr);
2869 break;
2870
2871 case 3: // Joint composition data
2872 for(sex = 0; sex < nsex(sp); sex ++){
2873 age_hat(comp_ind, age + nages(sp) * sex ) += pred_CAAL(flt, sex, age, ln, yr);
2874 }
2875 break;
2876 }
2877 }
2878
2879
2880 // Length composition
2881 // - Catch-at-length
2882 if(comp_type == 1){
2883 switch(flt_sex){
2884 case 0: // Sexes combined or 1 sex assessment
2885 for(sex = 0; sex < nsex(sp); sex ++){
2886 comp_hat(comp_ind, ln ) += pred_CAAL(flt, sex, age, ln, yr);
2887 }
2888 break;
2889
2890 case 1: case 2: // Sex-specific composition data
2891 sex = flt_sex - 1;
2892 comp_hat(comp_ind, ln ) += pred_CAAL(flt, sex, age, ln, yr);
2893 break;
2894
2895 case 3: // Joint composition data
2896 for(sex = 0; sex < nsex(sp); sex ++){
2897 comp_hat(comp_ind, ln + nlengths(sp) * sex ) += pred_CAAL(flt, sex, age, ln, yr) ;
2898 }
2899 break;
2900 }
2901 }
2902 }
2903 }
2904
2905 // Adjust for aging error
2906 for(int obs_age = 0; obs_age < nages(sp); obs_age++) {
2907 for(int true_age = 0; true_age < nages(sp); true_age++) {
2908 age_obs_hat(comp_ind, obs_age) += age_hat(comp_ind, true_age ) * age_error(flt_ageing_error_index(flt), true_age, obs_age);
2909 }
2910 }
2911
2912 // Adjust for aging error for joint data
2913 if(flt_sex == 3){
2914 for(int obs_age = nages(sp); obs_age < nages(sp) * 2; obs_age++) {
2915 for(int true_age = nages(sp); true_age < nages(sp) * 2; true_age++) {
2916
2917 // Adjust indexing for joint age/length comp
2918 int true_age_tmp = true_age - nages(sp);
2919 int obs_age_tmp = obs_age - nages(sp);
2920
2921 age_obs_hat(comp_ind, obs_age) += age_hat(comp_ind, true_age ) * age_error(flt_ageing_error_index(flt), true_age_tmp, obs_age_tmp);
2922 }
2923 }
2924 }
2925
2926 // Assign age-comp to estimated comp
2927 if( comp_type == 0) {
2928 joint_adjust(comp_ind) = 1;
2929 if(flt_sex == 3){
2930 joint_adjust(comp_ind) = 2;
2931 }
2932 for(age = 0; age < nages(sp) * joint_adjust(comp_ind); age++) {
2933 comp_hat(comp_ind, age ) = age_obs_hat(comp_ind, age );
2934 }
2935 }
2936 } // End estimated growth loop
2937
2938
2939 // 10.1.2. Empirical weight-at-age
2940 if(growth_model(sp) == 0){
2941 for(age = 0; age < nages(sp); age++) {
2942
2943 switch(flt_type(flt)){
2944 case 1: // - Fishery
2945 switch(flt_sex){
2946 case 0: // Sexes combined or 1 sex assessment
2947 for(sex = 0; sex < nsex(sp); sex ++){
2948 // Catch-at-age
2949 age_hat(comp_ind, age ) += F_flt_age(flt, sex, age, yr) / Z_at_age(sp, sex, age, yr) * (1 - exp(-Z_at_age(sp, sex, age, yr))) * N_at_age(sp, sex, age, yr);
2950 }
2951 break;
2952
2953 case 1: case 2: // Sex-specific composition data
2954 sex = flt_sex - 1;
2955 // Catch-at-age
2956 age_hat(comp_ind, age ) = F_flt_age(flt, sex, age, yr) / Z_at_age(sp, sex, age, yr) * (1 - exp(-Z_at_age(sp, sex, age, yr))) * N_at_age(sp, sex, age, yr);
2957 break;
2958
2959 case 3: // Joint composition data
2960 for(sex = 0; sex < nsex(sp); sex ++){
2961 // Catch-at-age
2962 age_hat(comp_ind, age + nages(sp) * sex ) = F_flt_age(flt, sex, age, yr) / Z_at_age(sp, sex, age, yr) * (1 - exp(-Z_at_age(sp, sex, age, yr))) * N_at_age(sp, sex, age, yr);
2963 }
2964 break;
2965 }
2966 break;
2967
2968
2969 case 2: // - Survey
2970 switch(flt_sex){
2971 case 0: // Sexes combined or 1 sex assessment
2972 for(sex = 0; sex < nsex(sp); sex ++){
2973 // Survey catch-at-age
2974 age_hat(comp_ind, age ) += N_at_age(sp, sex, age, yr) * sel_at_age(flt, sex, age, yr) * index_q(flt, yr_ind) * exp( - Type(mo/12.0) * Z_at_age(sp, sex, age, yr));
2975 }
2976 break;
2977
2978 case 1: case 2: // Sex-specific composition data
2979 sex = flt_sex - 1;
2980 // Survey catch-at-age
2981 age_hat(comp_ind, age ) = N_at_age(sp, sex, age, yr) * sel_at_age(flt, sex, age, yr) * index_q(flt, yr_ind) * exp( - Type(mo/12.0) * Z_at_age(sp, sex, age, yr));
2982 break;
2983
2984 case 3: // Joint composition data
2985 for(sex = 0; sex < nsex(sp); sex ++){
2986 // Survey catch-at-age
2987 age_hat(comp_ind, age + nages(sp) * sex ) = N_at_age(sp, sex, age, yr) * sel_at_age(flt, sex, age, yr) * index_q(flt, yr_ind) * exp( - Type(mo/12.0) * Z_at_age(sp, sex, age, yr));
2988 }
2989 break;
2990 }
2991 }
2992 }
2993
2994
2995 // Adjust for aging error
2996 for(int obs_age = 0; obs_age < nages(sp); obs_age++) {
2997 for(int true_age = 0; true_age < nages(sp); true_age++) {
2998 age_obs_hat(comp_ind, obs_age) += age_hat(comp_ind, true_age ) * age_error(flt_ageing_error_index(flt), true_age, obs_age);
2999 }
3000 }
3001
3002 // Adjust for aging error for joint data
3003 if(flt_sex == 3){
3004 for(int obs_age = nages(sp); obs_age < nages(sp) * 2; obs_age++) {
3005 for(int true_age = nages(sp); true_age < nages(sp) * 2; true_age++) {
3006
3007 // Adjust indexing for joint age/length comp
3008 int true_age_tmp = true_age - nages(sp);
3009 int obs_age_tmp = obs_age - nages(sp);
3010
3011 age_obs_hat(comp_ind, obs_age) += age_hat(comp_ind, true_age ) * age_error(flt_ageing_error_index(flt), true_age_tmp, obs_age_tmp);
3012 }
3013 }
3014 }
3015
3016 // Assign age-comp to estimated comp
3017 if( comp_type == 0) {
3018 joint_adjust(comp_ind) = 1;
3019 if(flt_sex == 3){
3020 joint_adjust(comp_ind) = 2;
3021 }
3022 for(age = 0; age < nages(sp) * joint_adjust(comp_ind); age++) {
3023 comp_hat(comp_ind, age ) = age_obs_hat(comp_ind, age );
3024 }
3025 }
3026
3027 // Convert from catch-at-age to catch-at-length, reading age_hat, the TRUE ages, not
3028 // age_obs_hat: the age-length key is P(length | true age), and a measured length
3029 // carries no otolith reading, so ageing error has no part in a length composition.
3030 // SS3 likewise applies its age_age matrix only to age and CAAL data.
3031 if( comp_type == 1) {
3032 for(ln = 0; ln < nlengths(sp); ln++) {
3033 for(age = 0; age < nages(sp); age++) {
3034
3035 sex = 0;
3036
3037 // Adjust sex for males/females
3038 if((flt_sex > 0) & (flt_sex < 3)){
3039 sex = flt_sex - 1;
3040 }
3041 comp_hat(comp_ind, ln ) += age_hat(comp_ind, age) * age_trans_matrix(flt_age_transition_index(flt), sex, age, ln );
3042 }
3043 }
3044
3045
3046 // Convert from catch-at-age to catch-at-length for joint comp data
3047 if( flt_sex == 3) {
3048 for(ln = nlengths(sp); ln < nlengths(sp) * 2; ln++) {
3049 for(age = nages(sp); age < nages(sp) * 2; age++) {
3050
3051 // Adjust indexing for joint age/length comp
3052 int obs_log_tmp = ln;
3053 int obs_age_tmp = age;
3054
3055 obs_age_tmp = age - nages(sp);
3056 obs_log_tmp = ln - nlengths(sp);
3057 sex = 1;
3058
3059 comp_hat(comp_ind, ln ) += age_hat(comp_ind, age) * age_trans_matrix(flt_age_transition_index(flt), sex, obs_age_tmp, obs_log_tmp );
3060 }
3061 }
3062 }
3063 }
3064 } // End empirical weight-at-age loop
3065
3066 // Standardize to sum to 1
3067 Type penalty = 0.0;
3068 comp_hat.row(comp_ind) /= posfun(comp_hat.row(comp_ind).sum(), Type(0.001), penalty);
3069 (void) penalty;
3070 }
3071
3072
3073 // -- 10.2. CAAL
3074 // -- length-based selectivity was converted above
3075 caal_hat.setZero();
3076 for(int caal_ind = 0; caal_ind < caal_hat.rows(); caal_ind++){
3077
3078 flt = caal_ctl(caal_ind, 0) - 1; // Fleet index (0-based)
3079 sp = caal_ctl(caal_ind, 1) - 1; // Species
3080 flt_sex = caal_ctl(caal_ind, 2); // CAAL sex: 0 = combined, 1 = female, 2 = male
3081 yr = caal_ctl(caal_ind, 3); // Data year
3082 ln = caal_ctl(caal_ind, 4) - 1; // Length bin
3083 mo = flt_month(flt); // Month (use fleet month b/c estimating growth)
3084
3085 if(growth_model(sp) == 0) continue; // Skip empirical weight-at-age species
3086
3087 if(flt_sex > 0){
3088 sex = flt_sex - 1;
3089 } else {
3090 sex = flt_sex;
3091 }
3092
3093 int wtind = nspp * 2 + flt;
3094
3095 // Likelihood (yr > 0) vs prediction (yr < 0)
3096 if(yr > 0){
3097 yr = yr - styr;
3098 }
3099 else if(yr < 0){
3100 yr = -yr - styr;
3101 }
3102 // Hindcast
3103 Type Frate = 0.0;
3104 if(yr < nyrs_hind){
3105 yr_ind = yr;
3106 Frate = exp(log_F(flt, yr));
3107 }
3108
3109 // Projection
3110 if(yr >= nyrs_hind){
3111 yr_ind = nyrs_hind - 1;
3112 Frate = proj_F_prop(flt) * proj_F(sp, yr);
3113 }
3114
3115 // 10.2. Calculate CAAL (predicted conditional age-at-length)
3116 for(age = 0; age < nages(sp); age++) {
3117
3118 switch(flt_type(flt)){
3119 case 1: // - Fishery
3120 if(flt_sel_dim(flt) == 1){
3121 {
3122 // sel(L) * P(L|age) over this data bin's population bins (2.3c).
3123 Type ps_ln = 0.0;
3124 for(int lp = pop_bin_lo(sp, ln); lp <= pop_bin_hi(sp, ln); lp++){
3125 ps_ln += sel_at_length(flt, sex, lp, yr) * growth_matrix(wtind, sex, age, lp, yr);
3126 }
3127 pred_CAAL(flt, sex, age, ln, yr) = ps_ln * (Frate / Z_at_age(sp, sex, age, yr) * (1 - exp(-Z_at_age(sp, sex, age, yr))) * N_at_age(sp, sex, age, yr));
3128 }
3129 }
3130 break;
3131
3132
3133 case 2: // - Survey
3134 if(flt_sel_dim(flt) == 1){
3135 {
3136 // sel(L) * P(L|age) over this data bin's population bins (2.3c).
3137 Type ps_ln = 0.0;
3138 for(int lp = pop_bin_lo(sp, ln); lp <= pop_bin_hi(sp, ln); lp++){
3139 ps_ln += sel_at_length(flt, sex, lp, yr) * growth_matrix(wtind, sex, age, lp, yr);
3140 }
3141 pred_CAAL(flt, sex, age, ln, yr) = ps_ln * (N_at_age(sp, sex, age, yr) * index_q(flt, yr_ind) * exp( - Type(mo/12.0) * Z_at_age(sp, sex, age, yr)) );
3142 }
3143 }
3144 break;
3145 }
3146 }
3147
3148 // Adjust for aging error
3149 for(int obs_age = 0; obs_age < nages(sp); obs_age++) {
3150 for(int true_age = 0; true_age < nages(sp); true_age++) {
3151 caal_hat(caal_ind, obs_age) += pred_CAAL(flt, sex, true_age, ln, yr) * age_error(flt_ageing_error_index(flt), true_age, obs_age);
3152 }
3153 }
3154
3155 // Observed CAAL standardize to sum to 1
3156 Type penalty = 0.0;
3157 caal_hat.row(caal_ind) /= posfun(caal_hat.row(caal_ind).sum(), Type(0.0001), penalty);
3158 (void) penalty;
3159 }
3160
3161
3162
3163 /** ------------------------------------------------------------------------ //
3164 * 11. PREDICTED STOMACH CONTENT //
3165 * ------------------------------------------------------------------------- */
3166
3167 // Predict stomach content
3168 if((msmMode > 2) || (imax(suitMode) > 0)) {
3169 predict_stomach_content(nspp, nyrs_hind, styr, suit_styr, suit_endyr,
3170 minage, nsex, nages, diet_obs, diet_ctl,
3171 avgN_at_age, diet_prop_hat, diet_hat);
3172 }
3173
3174
3175 /** ------------------------------------------------------------------------ //
3176 // 12. DERIVED QUANTITIES
3177 * ------------------------------------------------------------------------- */
3178
3179
3180 // 12.1. Depletion
3181 biomass_depletion.setZero();
3182 ssb_depletion.setZero();
3183 for(sp = 0; sp < nspp; sp++){
3184 for(yr = 0; yr < nyrs; yr++){
3185
3186 if(DynamicHCR == 0){
3187 biomass_depletion(sp, yr) = biomass(sp, yr)/B0(sp, nyrs-1);
3188 ssb_depletion(sp, yr) = ssb(sp, yr)/SB0(sp, nyrs-1);
3189 }
3190
3191 if(DynamicHCR == 1){
3192 biomass_depletion(sp, yr) = biomass(sp, yr)/DynamicB0(sp, yr);
3193 ssb_depletion(sp, yr) = ssb(sp, yr)/DynamicSB0(sp, yr);
3194 }
3195
3196 // Multi-species, no HCR: the projection runs unfished, so SSB in the last
3197 // projection year is multispecies SB0 once `projyr` is far enough out to
3198 // equilibrate. Excluded under DynamicHCR, where DynamicSB0 is the
3199 // reference and this would overwrite it.
3200 if((HCR == 0) & (msmMode > 0) & (DynamicHCR == 0)){
3201 biomass_depletion(sp, yr) = biomass(sp, yr)/biomass(sp, nyrs-1);
3202 ssb_depletion(sp, yr) = ssb(sp, yr)/ssb(sp, nyrs-1);
3203 }
3204 }
3205 }
3206
3207
3208
3209
3210 /** ------------------------------------------------------------------------ //
3211 // 13. LIKELIHOOD EQUATIONS
3212 * ------------------------------------------------------------------------- */
3213
3214 // 13.0. OBJECTIVE FUNCTION
3215 // Data likelihoods come first (13.1-13.6), then priors and penalties (13.7-13.14).
3216 // Each SIMULATE block sits beside the density it draws from, in the same order,
3217 // so sim_mod() draws what the likelihood scores.
3218 // dnorm(..., true) and similar return a log density, which is subtracted; TMB's
3219 // MVNORM, AR1, SEPARABLE and GMRF return a negative log density, which is added.
3220 // Data terms read observations from obsvec, gated by keep so oneStepPredict() can
3221 // hold each one out for OSA residuals; in ordinary fitting keep = 1.
3222 int n_col = std::max(n_flt, nspp);
3223 // Rows of jnll_comp. Keep in sync with the row names in R/6-rename_output.R and
3224 // .JNLL_ROW_AXIS in R/9-profile.R; test-schema-jnll-rows.R checks all three.
3225 enum JnllRow {
3226 JNLL_INDEX = 0, // Index data
3227 JNLL_CATCH = 1, // Catch data
3228 JNLL_COMP = 2, // Composition data
3229 JNLL_CAAL = 3, // CAAL data
3230 JNLL_SEL_NONPARAM = 4, // Non-parametric selectivity
3231 JNLL_SEL_DEV = 5, // Selectivity deviates
3232 JNLL_Q_PRIOR = 6, // Catchability prior
3233 JNLL_Q_DEV = 7, // Catchability deviates
3234 JNLL_SRR_PRIOR = 8, // Stock-recruit prior
3235 JNLL_INIT_DEV = 9, // Initial abundance deviates
3236 JNLL_REC_DEV = 10, // Recruitment deviates
3237 JNLL_SRR_PENALTY = 11, // Stock-recruit penalty
3238 JNLL_REFPT_PENALTY = 12, // Reference point penalties
3239 JNLL_ZERO_N_PENALTY = 13, // Zero n-at-age penalty
3240 JNLL_M_PRIOR = 14, // M prior
3241 JNLL_M_RE = 15, // M random effects
3242 JNLL_RATION = 16, // Ration
3243 JNLL_RATION_PENALTY = 17, // Ration penalties
3244 JNLL_STOMACH = 18, // Stomach content data
3245 JNLL_LINKAGE_PRIOR = 19, // Linkage-table priors (per-row)
3246 JNLL_LINKAGE_RE = 20, // Linkage random effects
3247 JNLL_EQUIL_CATCH = 21, // Initial equilibrium catch
3248 JNLL_N_ROWS = 22 // total row count (for dimensioning)
3249 };
3250 matrix<Type> jnll_comp(JNLL_N_ROWS, n_col); jnll_comp.setZero(); // negative log-likelihood components
3251 matrix<Type> unweighted_jnll_comp(JNLL_N_ROWS, n_col); unweighted_jnll_comp.setZero(); // see 13.15
3252
3253 // A column is a fleet on rows JNLL_INDEX-JNLL_Q_DEV and JNLL_EQUIL_CATCH, a species
3254 // on JNLL_SRR_PRIOR-JNLL_LINKAGE_PRIOR, and neither on JNLL_LINKAGE_RE (one
3255 // model-wide density), so never sum down a column. Nothing writes the two ration rows.
3256
3257
3258 // 13.1. INDEX OF ABUNDANCE (JNLL_INDEX)
3259 // Index_distribution: 0 Lognormal; 1 MVN and 2 MVNORM, correlated across years
3260 // (fitted per fleet below); 3 Normal and 4 TruncatedNormal, both with an absolute
3261 // sd on the natural scale.
3262 Type index_std_dev = 0;
3263 for(index_ind = 0; index_ind < index_obs.rows(); index_ind++) {
3264
3265 index = index_ctl(index_ind, 0) - 1; // Fleet
3266 sp = index_ctl(index_ind, 1) - 1; // Species
3267 flt_yr = index_ctl(index_ind, 2); // Year
3268 int family = index_ll_type(index);
3269
3270 switch (est_sigma_index(index)) {
3271 case 0: index_std_dev = index_obs(index_ind, 1); break; // input, per row
3272 case 1: index_std_dev = exp(index_log_sd(index)); break; // estimated
3273 case 2: index_std_dev = index_analytical_sd(index); break; // Walters and Ludwig (1994), section 8.4
3274 default: error("Invalid 'Estimate_sigma_index'");
3275 }
3276 index_sd(index_ind) = index_std_dev;
3277
3278 // Fit hindcast years with an observation.
3279 bool fit_row = (flt_yr > 0) && (flt_yr <= endyr) && (flt_type(index) > 0) && (index_obs(index_ind, 0) > 0);
3280
3281 // Lognormal, read from obsvec (= log index) so OSA can gate each row with keep.
3282 if(fit_row && (family == 0)){
3283 int pos = index_obsvec_idx(index_ind);
3284 if(pos >= 0){
3285 Type index_mu = log(index_hat(index_ind)) - bias_adjust_obs*square(index_std_dev)/2.0;
3286 jnll_comp(JNLL_INDEX, index) -= keep(pos) * dnorm(obsvec(pos), index_mu, index_std_dev, true);
3287 if(osa_mode == 2){
3288 jnll_comp(JNLL_INDEX, index) -= osa_norm_cdf_terms(
3289 obsvec(pos), index_mu, index_std_dev, keep.cdf_lower(pos), keep.cdf_upper(pos));
3290 }
3291 }
3292 }
3293
3294 // Normal (3) matches AMAK's avo_like/cpue_like, 0.5 * (obs - q * pred)^2 / sd^2.
3295 // TruncatedNormal (4) renormalizes over obs > 0: log Phi(mu / sd), in [log 0.5, 0].
3296 if(fit_row && ((family == 3) || (family == 4))){
3297 Type log_Z = (family == 4) ? log(pnorm(index_hat(index_ind) / index_std_dev)) : Type(0.0);
3298 if(osa_mode == 0){
3299 jnll_comp(JNLL_INDEX, index) -= dnorm(index_obs(index_ind, 0), index_hat(index_ind), index_std_dev, true);
3300 jnll_comp(JNLL_INDEX, index) += log_Z;
3301 } else {
3302 // OSA: obsvec holds the untransformed index for these families.
3303 int pos = index_obsvec_idx(index_ind);
3304 if(pos >= 0){
3305 jnll_comp(JNLL_INDEX, index) -= keep(pos) * dnorm(obsvec(pos), index_hat(index_ind), index_std_dev, true);
3306 jnll_comp(JNLL_INDEX, index) += keep(pos) * log_Z;
3307 if(osa_mode == 2){
3308 if(family == 4){
3309 jnll_comp(JNLL_INDEX, index) -= osa_trunc_norm_cdf_terms(
3310 obsvec(pos), index_hat(index_ind), index_std_dev,
3311 keep.cdf_lower(pos), keep.cdf_upper(pos));
3312 } else {
3313 jnll_comp(JNLL_INDEX, index) -= osa_norm_cdf_terms(
3314 obsvec(pos), index_hat(index_ind), index_std_dev,
3315 keep.cdf_lower(pos), keep.cdf_upper(pos));
3316 }
3317 }
3318 }
3319 }
3320 }
3321
3322 // -- Simulate (families 0, 3, 4; MVN is drawn per fleet below). Projection rows
3323 // are always drawn, as run_mse() reveals them; a missing hindcast year stays NA.
3324 SIMULATE {
3325 bool sim_row = (flt_yr <= 0) || (flt_yr > endyr) || (index_obs(index_ind, 0) > 0);
3326 if(sim_row && (flt_type(index) > 0) && (family == 0)){
3327 index_obs(index_ind, 0) = exp(rnorm(log(index_hat(index_ind)) - bias_adjust_obs*square(index_std_dev)/2.0, index_std_dev));
3328 int sim_pos = index_obsvec_idx(index_ind);
3329 if(sim_pos >= 0){
3330 obsvec(sim_pos) = log(index_obs(index_ind, 0));
3331 }
3332 }
3333 if(sim_row && (flt_type(index) > 0) && (family == 3)){
3334 // The untruncated normal can draw <= 0, which data_check() refuses, so redraw and
3335 // count rejections; sim_mod() warns when they matter. Family 4 has no such gap.
3336 Type sim_draw = rnorm(index_hat(index_ind), index_std_dev);
3337 int tn_tries = 0;
3338 while((sim_draw <= Type(0)) && (tn_tries < index_trunc_max_tries)){
3339 index_trunc_rejects_sim(index_ind) += 1;
3340 sim_draw = rnorm(index_hat(index_ind), index_std_dev);
3341 tn_tries++;
3342 }
3343 index_trunc_tries_sim(index_ind) += tn_tries + 1;
3344 index_trunc_sd_sim(index_ind) = index_std_dev;
3345 index_trunc_budget_sim(index_ind) = index_trunc_max_tries;
3346 index_obs(index_ind, 0) = sim_draw;
3347 }
3348 if(sim_row && (flt_type(index) > 0) && (family == 4)){
3349 // Exact inverse-CDF draw from the normal restricted to (0, inf).
3350 Type a = pnorm(-index_hat(index_ind) / index_std_dev);
3351 Type u = a + (Type(1.0) - a) * runif(Type(0.0), Type(1.0));
3352 index_obs(index_ind, 0) = index_hat(index_ind) + index_std_dev * qnorm(u);
3353 }
3354 // Keep obsvec in step: log(obs) when fitting, the untransformed obs under OSA.
3355 if((flt_type(index) > 0) && ((family == 3) || (family == 4))){
3356 int sim_pos = index_obsvec_idx(index_ind);
3357 if(sim_pos >= 0){
3358 if(osa_mode == 0){
3359 if(index_obs(index_ind, 0) > Type(0)) obsvec(sim_pos) = log(index_obs(index_ind, 0));
3360 } else {
3361 obsvec(sim_pos) = index_obs(index_ind, 0);
3362 }
3363 }
3364 }
3365 }
3366 }
3367
3368 // MVN (1) and MVNORM (2): the AMAK/ebswp DoCovBTS survey likelihood on the arithmetic
3369 // residual r = obs - q * pred, with the fleet's fixed covariance index_cov_mat. MVN
3370 // reports the bare quadratic form 0.5 r' Sigma^-1 r, MVNORM the full density; the fit
3371 // is the same. Pair with Catchability 7 for the AMAK arithmetic-mean q.
3372 for(index = 0; index < n_flt; index++){
3373 if((flt_type(index) > 0) && (index_ll_type(index) == 1 || index_ll_type(index) == 2)){
3374 int n_mvn = 0;
3375 for(index_ind = 0; index_ind < index_obs.rows(); index_ind++){
3376 if((index_ctl(index_ind, 0) - 1) == index){
3377 flt_yr = index_ctl(index_ind, 2);
3378 if((flt_yr > 0) && (flt_yr <= endyr) && (index_obs(index_ind, 0) > 0)) n_mvn++;
3379 }
3380 }
3381 if(n_mvn > 0){
3382 // Fitted rows in index_obs order, which is the row order of Sigma.
3383 vector<Type> resid(n_mvn);
3384 vector<Type> mu(n_mvn);
3385 vector<int> posv(n_mvn);
3386 vector<int> rowv(n_mvn);
3387 int k = 0;
3388 for(index_ind = 0; index_ind < index_obs.rows(); index_ind++){
3389 if((index_ctl(index_ind, 0) - 1) == index){
3390 flt_yr = index_ctl(index_ind, 2);
3391 if((flt_yr > 0) && (flt_yr <= endyr) && (index_obs(index_ind, 0) > 0)){
3392 resid(k) = index_obs(index_ind, 0) - index_hat(index_ind);
3393 mu(k) = index_hat(index_ind);
3394 posv(k) = index_obsvec_idx(index_ind);
3395 rowv(k) = index_ind;
3396 k++;
3397 }
3398 }
3399 }
3400 if(osa_mode == 0){
3401 // MVNORM() returns 0.5 * (r' Sigma^-1 r + logdet(Sigma) + n log(2 pi));
3402 // index_cov_const holds the constant MVN drops.
3403 Type dens = MVNORM(index_cov_mat(index))(resid);
3404 jnll_comp(JNLL_INDEX, index) += (index_ll_type(index) == 2) ? dens : (dens - index_cov_const(index));
3405 } else {
3406 // OSA: build_osa_data() stores z = L^-1 obs, with Sigma = L L'. Each z_k is an
3407 // independent N((L^-1 mu)_k, 1) (Thygesen et al. 2017).
3408 Eigen::LLT<Eigen::Matrix<Type, Eigen::Dynamic, Eigen::Dynamic> > llt(index_cov_mat(index));
3409 matrix<Type> Lmat = llt.matrixL();
3410 matrix<Type> muc(n_mvn, 1);
3411 for(k = 0; k < n_mvn; k++) muc(k, 0) = mu(k);
3412 matrix<Type> mwhite = Lmat.template triangularView<Eigen::Lower>().solve(muc); // L^-1 mu
3413 for(k = 0; k < n_mvn; k++){
3414 int pos = posv(k);
3415 if(pos >= 0){
3416 jnll_comp(JNLL_INDEX, index) -= keep(pos) * dnorm(obsvec(pos), mwhite(k, 0), Type(1.0), true);
3417 if(osa_mode == 2){
3418 jnll_comp(JNLL_INDEX, index) -= osa_norm_cdf_terms(
3419 obsvec(pos), mwhite(k, 0), Type(1.0),
3420 keep.cdf_lower(pos), keep.cdf_upper(pos));
3421 }
3422 }
3423 }
3424 }
3425
3426 // -- Simulate the fitted rows only (Sigma covers no others), redrawing the vector
3427 // while any row is <= 0: exact for the positive-orthant truncation, which per-margin
3428 // truncation would decorrelate. A draw left when the budget runs out is reported.
3429 SIMULATE {
3430 vector<Type> sim_dev = MVNORM(index_cov_mat(index)).simulate();
3431 int mvn_tries = 0;
3432 while(true){
3433 bool any_nonpos = false;
3434 for(k = 0; k < n_mvn; k++){
3435 if(mu(k) + sim_dev(k) <= Type(0)) any_nonpos = true;
3436 }
3437 if(!any_nonpos) break;
3438 if(mvn_tries >= index_trunc_max_tries * n_mvn) break;
3439 for(k = 0; k < n_mvn; k++){
3440 if(mu(k) + sim_dev(k) <= Type(0)) index_trunc_rejects_sim(rowv(k)) += 1;
3441 }
3442 sim_dev = MVNORM(index_cov_mat(index)).simulate();
3443 mvn_tries++;
3444 }
3445 for(k = 0; k < n_mvn; k++){
3446 index_trunc_tries_sim(rowv(k)) += mvn_tries + 1;
3447 index_trunc_sd_sim(rowv(k)) = sqrt(index_cov_mat(index)(k, k));
3448 index_trunc_budget_sim(rowv(k)) = index_trunc_max_tries * n_mvn;
3449 index_obs(rowv(k), 0) = mu(k) + sim_dev(k);
3450 // obsvec is log(obs) when fitting and whitened under OSA; update only the former.
3451 int sim_pos = posv(k);
3452 if((sim_pos >= 0) && (osa_mode == 0) && (index_obs(rowv(k), 0) > Type(0))){
3453 obsvec(sim_pos) = log(index_obs(rowv(k), 0));
3454 }
3455 }
3456 }
3457 }
3458 }
3459 }
3460
3461 // Reported under a _sim name -- see the naming rule in section 5.2b.
3462 SIMULATE {
3463 matrix<Type> index_obs_sim = index_obs;
3464 REPORT(index_obs_sim);
3465 REPORT(index_trunc_tries_sim);
3466 REPORT(index_trunc_rejects_sim);
3467 REPORT(index_trunc_sd_sim);
3468 REPORT(index_trunc_budget_sim);
3469 }
3470
3471
3472 // 13.2. CATCH (JNLL_CATCH)
3473 // Lognormal on total catch, fishery fleets and hindcast years only.
3474 Type fsh_std_dev = 0;
3475 for(fsh_ind = 0; fsh_ind < catch_obs.rows(); fsh_ind++) {
3476
3477 flt = catch_ctl(fsh_ind, 0) - 1; // Fleet
3478 sp = catch_ctl(fsh_ind, 1) - 1; // Species
3479 flt_yr = catch_ctl(fsh_ind, 2); // Year
3480 yr = flt_yr - styr; // Year index, 0-based
3481
3482 switch (est_sigma_fsh(flt)) {
3483 case 0: fsh_std_dev = catch_obs(fsh_ind, 1); break; // input, per row
3484 case 1: fsh_std_dev = exp(catch_log_sd(flt)); break; // estimated
3485 case 2: fsh_std_dev = catch_analytical_sd(flt); break; // Walters and Ludwig (1994), section 9.1b
3486 default: error("Invalid 'Estimate_sigma_catch'");
3487 }
3488 catch_sd(fsh_ind) = fsh_std_dev;
3489
3490 // Read from obsvec (= log catch) so OSA can gate each row with keep.
3491 if((flt_yr > 0) && (flt_yr <= endyr) && (flt_type(flt) == 1) && (catch_obs(fsh_ind, 0) > 0)){
3492 int pos = catch_obsvec_idx(fsh_ind);
3493 if(pos >= 0){
3494 Type catch_mu = log(catch_hat(fsh_ind)) - bias_adjust_obs*square(fsh_std_dev)/2.0;
3495 jnll_comp(JNLL_CATCH, flt) -= keep(pos) * dnorm(obsvec(pos), catch_mu, fsh_std_dev, true) ;
3496 if(osa_mode == 2){
3497 jnll_comp(JNLL_CATCH, flt) -= osa_norm_cdf_terms(
3498 obsvec(pos), catch_mu, fsh_std_dev, keep.cdf_lower(pos), keep.cdf_upper(pos));
3499 }
3500 }
3501 }
3502
3503 // -- Simulate every row, with the dnorm's bias-corrected mean. Projection rows
3504 // (NA catch from clean_data()) are filled too; where catch_hat = 0 the draw is
3505 // exp(-inf) = 0, safe only on doubles. Do not share this mean with the dnorm (log 0
3506 // puts a NaN on the tape) or add a catch_hat > 0 guard (it changes the draw count).
3507 SIMULATE {
3508 catch_obs(fsh_ind, 0) = exp(rnorm(log(catch_hat(fsh_ind)) - bias_adjust_obs*square(fsh_std_dev)/2.0, fsh_std_dev));
3509 int sim_pos = catch_obsvec_idx(fsh_ind);
3510 if(sim_pos >= 0){
3511 obsvec(sim_pos) = log(catch_obs(fsh_ind, 0));
3512 }
3513 }
3514 }
3515
3516
3517 // 13.3. INITIAL EQUILIBRIUM CATCH (JNLL_EQUIL_CATCH)
3518 // Lognormal on its own Log_sd and its own row, as SS3's equ_catch_like. No OSA: one
3519 // observation has no sequence to condition on.
3520 for(int eq_ind = 0; eq_ind < equil_catch_ctl.rows(); eq_ind++){
3521 int eq_flt = equil_catch_ctl(eq_ind, 0) - 1;
3522 if((equil_catch_obs(eq_ind, 0) > 0) && (flt_type(eq_flt) == 1)){
3523 Type eq_sd = equil_catch_obs(eq_ind, 1);
3524 Type eq_mu = log(equil_catch_hat(eq_ind)) - bias_adjust_obs*square(eq_sd)/2.0;
3525 jnll_comp(JNLL_EQUIL_CATCH, eq_flt) -=
3526 dnorm(log(equil_catch_obs(eq_ind, 0)), eq_mu, eq_sd, true);
3527
3528 // -- Simulate. Under initMode 6 this is the only observation on Finit, so a
3529 // self_test() replicate must redraw it.
3530 SIMULATE {
3531 equil_catch_obs(eq_ind, 0) = exp(rnorm(eq_mu, eq_sd));
3532 }
3533 }
3534 }
3535
3536 // Reported under _sim names -- see the naming rule in section 5.2b. obsvec_sim is for
3537 // inspection: only its catch and index entries are redrawn.
3538 SIMULATE {
3539 matrix<Type> catch_obs_sim = catch_obs;
3540 matrix<Type> equil_catch_obs_sim = equil_catch_obs;
3541 vector<Type> obsvec_sim = obsvec;
3542 REPORT(catch_obs_sim);
3543 REPORT(equil_catch_obs_sim);
3544 REPORT(obsvec_sim);
3545 }
3546
3547
3548 // 13.4. AGE AND LENGTH COMPOSITION (JNLL_COMP)
3549 // Likelihood types are in comp_row_nll() (likelihood.hpp). comp_offset (default
3550 // 1e-5, from fit_control()) keeps log(0) out, and build_osa_data() uses the same.
3551 // FIXME: the multinomial (type 0) is renormalized after the offset, so its reported
3552 // NLL carries a per-row constant N_eff * log(1 + n_bins * offset); fit and MLE unchanged.
3553 Type comp_prop_offset = comp_offset;
3554 matrix<Type> comp_sim = comp_obs; // simulated comps; see the _sim rule in section 5.2b
3555
3556 for(comp_ind = 0; comp_ind < comp_obs.rows(); comp_ind++) {
3557
3558 flt = comp_ctl(comp_ind, 0) - 1; // Fleet
3559 sp = comp_ctl(comp_ind, 1) - 1; // Species
3560 flt_sex = comp_ctl(comp_ind, 2); // 0 = combined, 1 = female, 2 = male, 3 = joint
3561 comp_type = comp_ctl(comp_ind, 3); // 0 = age, 1 = length
3562 yr = comp_ctl(comp_ind, 4); // Year
3563
3564 // A joint-sex row holds both sexes, so twice the bins.
3565 joint_adjust(comp_ind) = 1;
3566 if(flt_sex == 3){
3567 joint_adjust(comp_ind) = 2;
3568 }
3569 int nbins_blk = (comp_type == 0) ? nages(sp) : nlengths(sp);
3570 int n_comp = 0; // a comp type other than age or length scores no bins
3571 if((comp_type == 0) || (comp_type == 1)) n_comp = nbins_blk * joint_adjust(comp_ind);
3572
3573 vector<Type> comp_obs_tmp = comp_obs.row(comp_ind).segment(0, n_comp); // Observed proportion
3574 vector<Type> comp_hat_tmp = comp_hat.row(comp_ind).segment(0, n_comp); // Expected proportion
3575
3576 // Tail accumulation (Comp_accum_young / Comp_accum_old). OSA reads an obsvec that
3577 // build_osa_data() folds the same way.
3578 fold_comp_tails(comp_obs_tmp, comp_hat_tmp, n_comp, nbins_blk, joint_adjust(comp_ind),
3579 comp_accum_young(flt), comp_accum_old(flt));
3580
3581 if((yr <= endyr) && (yr > 0) && (flt_type(flt) > 0) && (comp_n(comp_ind, 1) > 0)){
3582 comp_row_nll(comp_ll_type(flt), osa_mode, comp_obs_tmp, comp_hat_tmp,
3583 comp_prop_offset, comp_n(comp_ind, 1), comp_weights(flt), DM_pars_comp(flt),
3584 obsvec, keep, comp_obsvec_idx(comp_ind), "comp_ll_type",
3585 jnll_comp(JNLL_COMP, flt), unweighted_jnll_comp(JNLL_COMP, flt));
3586 }
3587
3588 // -- Simulate in raw bins, before the fold and offset (both families are closed
3589 // under merging bins), projection rows included, as run_mse() reveals them.
3590 SIMULATE {
3591 int nbin_raw = ((comp_type == 0) ? nages(sp) : nlengths(sp)) * joint_adjust(comp_ind);
3592 vector<Type> sim_p = comp_hat.row(comp_ind).segment(0, nbin_raw);
3593 Type sim_tot = sim_p.sum();
3594 Type n_nom = comp_n(comp_ind, 1);
3595 // A row with nothing predicted or nothing sampled comes back EMPTY, never as the
3596 // noise-free prediction: run_mse() reads an all-zero row as "no catch, no comp".
3597 vector<Type> sim_out(nbin_raw); sim_out.setZero();
3598
3599 if((sim_tot > Type(0)) && (n_nom > Type(0))){
3600 vector<Type> p = sim_p / sim_tot;
3601 if(comp_ll_type(flt) == 1){
3602 // DM: theta already carries the weight, so draw at the nominal N.
3603 vector<Type> sim_alphas = p * n_nom * DM_pars_comp(flt);
3604 sim_out = rdirmultinom_rce(n_nom, sim_alphas);
3605 } else {
3606 // Multinomial: the weight scales the log-likelihood, so it is an effective
3607 // sample size and the draw uses it too.
3608 sim_out = rmultinom_rce(n_nom * comp_weights(flt), p);
3609 }
3610 }
3611 for(int b = 0; b < nbin_raw; b++){
3612 comp_sim(comp_ind, b) = sim_out(b);
3613 }
3614 // Zero the padding past this row's bins, which rearrange_data() would otherwise
3615 // normalize into the real ones.
3616 for(int b = nbin_raw; b < comp_sim.cols(); b++){
3617 comp_sim(comp_ind, b) = Type(0);
3618 }
3619 }
3620 }
3621
3622 SIMULATE {
3623 matrix<Type> comp_obs_sim = comp_sim;
3624 REPORT(comp_obs_sim);
3625 }
3626
3627
3628 // 13.5. CONDITIONAL AGE-AT-LENGTH (JNLL_CAAL)
3629 // One row per fleet, year, sex and length bin, scored like a composition row.
3630 matrix<Type> caal_sim = caal_obs; // simulated CAAL; see the _sim rule in section 5.2b
3631
3632 for(int caal_ind = 0; caal_ind < caal_hat.rows(); caal_ind++){
3633
3634 flt = caal_ctl(caal_ind, 0) - 1; // Fleet
3635 sp = caal_ctl(caal_ind, 1) - 1; // Species
3636 flt_sex = caal_ctl(caal_ind, 2); // 0 = combined, 1 = female, 2 = male, 3 = joint
3637 yr = caal_ctl(caal_ind, 3); // Year
3638 ln = caal_ctl(caal_ind, 4) - 1; // Length bin
3639
3640 int n_caal = nages(sp);
3641
3642 vector<Type> caal_obs_tmp = caal_obs.row(caal_ind).segment(0, n_caal); // Observed proportion
3643 vector<Type> caal_hat_tmp = caal_hat.row(caal_ind).segment(0, n_caal); // Expected proportion
3644
3645 if((yr <= endyr) && (yr > 0) && (flt_type(flt) > 0) && (caal_n(caal_ind, 0) > 0)){
3646 comp_row_nll(caal_ll_type(flt), osa_mode, caal_obs_tmp, caal_hat_tmp,
3647 comp_prop_offset, caal_n(caal_ind, 0), caal_weights(flt), DM_pars_caal(flt),
3648 obsvec, keep, caal_obsvec_idx(caal_ind), "caal_ll_type",
3649 jnll_comp(JNLL_CAAL, flt), unweighted_jnll_comp(JNLL_CAAL, flt));
3650 }
3651
3652 // -- Simulate, as for the compositions above (CAAL has no tail fold).
3653 SIMULATE {
3654 vector<Type> sim_p = caal_hat.row(caal_ind).segment(0, n_caal);
3655 Type sim_tot = sim_p.sum();
3656 Type n_nom = caal_n(caal_ind, 0);
3657 vector<Type> sim_out(n_caal); sim_out.setZero();
3658
3659 if((sim_tot > Type(0)) && (n_nom > Type(0))){
3660 vector<Type> p = sim_p / sim_tot;
3661 if(caal_ll_type(flt) == 1){
3662 vector<Type> sim_alphas = p * n_nom * DM_pars_caal(flt);
3663 sim_out = rdirmultinom_rce(n_nom, sim_alphas);
3664 } else {
3665 sim_out = rmultinom_rce(n_nom * caal_weights(flt), p);
3666 }
3667 }
3668 for(int b = 0; b < n_caal; b++){
3669 caal_sim(caal_ind, b) = sim_out(b);
3670 }
3671 for(int b = n_caal; b < caal_sim.cols(); b++){
3672 caal_sim(caal_ind, b) = Type(0);
3673 }
3674 }
3675 }
3676
3677 SIMULATE {
3678 matrix<Type> caal_obs_sim = caal_sim;
3679 REPORT(caal_obs_sim);
3680 }
3681
3682
3683 // 13.6. STOMACH CONTENTS (JNLL_STOMACH)
3684 // Each stomach's prey proportions plus an "other prey" balance, scored per predator
3685 // when suitability is estimated.
3686 matrix<Type> diet_sim = diet_obs; // simulated diet; see the _sim rule in section 5.2b
3687
3688 if((msmMode > 2) || (imax(suitMode) > 0)) {
3689
3690 int current_j = 0; // Track position in diet_ctl
3691
3692 for (int i = 0; i < n_stomach_obs; ++i) {
3693
3694 // This stomach's prey are the run of diet_ctl rows with stomach_id == i. The scan
3695 // only moves forward, so ids must be sorted 0, 1, 2, ... with no gaps; clean_data()
3696 // builds them that way and data_check() refuses a table that is not.
3697 int start_j = current_j;
3698 while((current_j < diet_ctl.rows()) && (stomach_id(current_j) == i)) {
3699 current_j++;
3700 }
3701 int n_prey = current_j - start_j;
3702 if (n_prey == 0) continue; // before reading row start_j, which may be past the end
3703
3704 int rsp = diet_ctl(start_j, 0) - 1; // predator species
3705 Type N_s = diet_obs(start_j, 0); // stomach sample size
3706
3707 if (suitMode(rsp) <= 0) continue;
3708
3709 // Prey proportions, with "other prey" as the last bin.
3710 vector<Type> obs_diet_prop(n_prey + 1); obs_diet_prop.setZero();
3711 vector<Type> pred_diet_prop(n_prey + 1); pred_diet_prop.setZero();
3712
3713 for (int k = 0; k < n_prey; ++k) {
3714 obs_diet_prop(k) = diet_obs(start_j + k, 1);
3715 pred_diet_prop(k) = diet_hat(start_j + k, 1);
3716 }
3717
3718 Type sum_obs_p = obs_diet_prop.head(n_prey).sum();
3719 if (sum_obs_p > 1.0) sum_obs_p = 1.0;
3720 obs_diet_prop(n_prey) = 1.0 - sum_obs_p;
3721
3722 Type sum_est_p = pred_diet_prop.head(n_prey).sum();
3723 pred_diet_prop(n_prey) = posfun(1.0 - sum_est_p, Type(0.00001), penalty); // smooth floor at 0
3724
3725 // Same offset as the compositions, then renormalize.
3726 obs_diet_prop += comp_offset;
3727 pred_diet_prop += comp_offset;
3728
3729 obs_diet_prop /= obs_diet_prop.sum();
3730 pred_diet_prop /= pred_diet_prop.sum();
3731
3732 vector<Type> obs_diet_content = obs_diet_prop * N_s; // observed counts
3733
3734 // Not comp_row_nll(): the stomach multinomial is TMB's dmultinom() on proportions
3735 // renormalized after the offset, a different number from the composition form.
3736
3737 Type stomach_log_likelihood = 0;
3738 Type unweighted_stomach_log_likelihood = 0;
3739 Type DM_diet_par = DM_diet_pars(rsp);
3740
3741 vector<Type> diet_alphas = pred_diet_prop * N_s * DM_diet_par;
3742 vector<Type> unweighted_diet_alphas = pred_diet_prop * N_s;
3743
3744 if(osa_mode == 0){
3745 switch(diet_ll_type(rsp)){
3746
3747 case 0: // Multinomial; the weight multiplies the log-likelihood
3748 stomach_log_likelihood = dmultinom(obs_diet_content, pred_diet_prop, true);
3749
3750 unweighted_jnll_comp(JNLL_STOMACH, rsp) -= stomach_log_likelihood;
3751 jnll_comp(JNLL_STOMACH, rsp) -= diet_comp_weights(rsp) * stomach_log_likelihood;
3752 break;
3753 case 1: // Dirichlet-multinomial; theta = exp(weight)
3754 stomach_log_likelihood = ddirmultinom(obs_diet_content, diet_alphas, true);
3755 unweighted_stomach_log_likelihood = ddirmultinom(obs_diet_content, unweighted_diet_alphas, true);
3756
3757 unweighted_jnll_comp(JNLL_STOMACH, rsp) -= unweighted_stomach_log_likelihood;
3758 jnll_comp(JNLL_STOMACH, rsp) -= stomach_log_likelihood;
3759 break;
3760
3761 default:
3762 error("Invalid 'diet_ll_type'");
3763 }
3764 } else {
3765 // OSA: unweighted and keep-gated, read from obsvec over the n_prey + 1 bins.
3766 int start = diet_obsvec_idx(i);
3767 if(start >= 0){
3768 vector<Type> osa_x = obsvec.segment(start, n_prey + 1);
3769 if(diet_ll_type(rsp) == 1){ // Dirichlet-multinomial: no CDF term (comp_osa.hpp)
3770 jnll_comp(JNLL_STOMACH, rsp) -= ddirmultinom_osa(osa_x, diet_alphas, keep.segment(start, n_prey + 1), 1, 1);
3771 } else {
3772 jnll_comp(JNLL_STOMACH, rsp) -= dmultinom_osa(osa_x, pred_diet_prop, keep.segment(start, n_prey + 1), 1, 1, osa_mode == 2);
3773 }
3774 }
3775 }
3776
3777 // -- Simulate from the same family, from the prediction before the offset. Only
3778 // the prey bins are written back; "other prey" is recomputed on the next fit.
3779 SIMULATE {
3780 vector<Type> sim_p(n_prey + 1);
3781 Type prey_sum = 0.0;
3782 for(int k = 0; k < n_prey; k++){
3783 sim_p(k) = diet_hat(start_j + k, 1);
3784 prey_sum += sim_p(k);
3785 }
3786 sim_p(n_prey) = (prey_sum < Type(1.0)) ? Type(1.0) - prey_sum : Type(0.0);
3787 Type sim_tot = sim_p.sum();
3788 if(sim_tot > Type(0)) sim_p /= sim_tot;
3789
3790 if((N_s > Type(0)) && (sim_tot > Type(0))){
3791 vector<Type> sim_counts(n_prey + 1);
3792 switch(diet_ll_type(rsp)){
3793 case 0: {
3794 // The weight on a multinomial log-likelihood is an effective sample size.
3795 Type n_eff = N_s * diet_comp_weights(rsp);
3796 sim_counts = rmultinom_rce(n_eff, sim_p);
3797 break;
3798 }
3799 case 1: {
3800 vector<Type> sim_alphas = sim_p * N_s * DM_diet_par;
3801 sim_counts = rdirmultinom_rce(N_s, sim_alphas);
3802 break;
3803 }
3804 default:
3805 error("Invalid 'diet_ll_type'");
3806 }
3807 // Divide by the count actually drawn (rmultinom_rce rounds N), so the prey
3808 // proportions cannot sum past 1. A stomach that draws nothing comes back empty.
3809 Type n_drawn = sim_counts.sum();
3810 for(int k = 0; k < n_prey; k++){
3811 diet_sim(start_j + k, 1) = (n_drawn > Type(0))
3812 ? sim_counts(k) / n_drawn
3813 : Type(0);
3814 }
3815 }
3816 }
3817 }
3818 }
3819
3820 // Reported under a _sim name -- see the naming rule in section 5.2b.
3821 SIMULATE {
3822 matrix<Type> diet_obs_sim = diet_sim;
3823 REPORT(diet_obs_sim);
3824 }
3825
3826
3827 // 13.7. SELECTIVITY PENALTIES (JNLL_SEL_NONPARAM, JNLL_SEL_DEV)
3828 // Charged once per Selectivity_index, on the lead fleet. Bins are on the fleet's
3829 // selectivity dimension (ages, or population length bins).
3830 // FIXME: penalize every deviation, not a sub-range, and the bin and year range controls
3831 // (Sel_pen_first_bin, Sel_pen_last_bin, Sel_cap_bin, Sel_start_year) could go.
3832 for(flt = 0; flt < n_flt; flt++){
3833 sp = flt_spp(flt);
3834 bool sel_is_length = (flt_sel_dim(flt) == 1);
3835 int nbins = sel_is_length ? nlengths_pop(sp) : nages(sp);
3836
3837 if(flt_type(flt) > 0 && flt_sel_lead(flt) == 1){
3838
3839 // NonParametric (2): Ianelli/AMAK penalties on log selectivity centred to mean 1
3840 // (arrowtooth ADMB model), each year's curve when it varies (Atka mackerel).
3841 // Refused under random_sel = TRUE; form 13 is the integrable version.
3842 if(flt_sel_type(flt) == 2) {
3843 int nyrs_tmp = 1;
3844 if((flt_varying_sel(flt) == 1) || (flt_varying_sel(flt) == 4)){
3845 nyrs_tmp = nyrs_hind;
3846 }
3847
3848 for(yr = 0; yr < nyrs_tmp; yr++){
3849
3850 // 1. Decreasing selectivity: (|d| + d) / 2 = max(d, 0), d > 0 if decreasing.
3851 // FIXME: AMAK starts this penalty at bin nbins/2; here it starts at bin 0.
3852 for(sex = 0; sex < nsex(sp); sex++){
3853 for(age = 0; age < (nbins - 1); age++) {
3854 Type sel_ratio_tmp = log_non_par_sel(flt, sex, age, yr) - log_non_par_sel(flt, sex, age + 1, yr);
3855 jnll_comp(JNLL_SEL_NONPARAM, flt) += sel_curve_pen(flt, 0) * square( (CppAD::abs(sel_ratio_tmp) + sel_ratio_tmp)/2.0);
3856 }
3857 }
3858
3859 // 2. Curvature: squared second differences.
3860 for(sex = 0; sex < nsex(sp); sex++){
3861 vector<Type> sel_tmp(nbins); sel_tmp.setZero();
3862 for(age = 0; age < nbins; age++) {
3863 sel_tmp(age) = log_non_par_sel(flt, sex, age, yr);
3864 }
3865 vector<Type> sel_d2 = first_difference( first_difference( sel_tmp ) );
3866 for(int a2 = 0; a2 < sel_d2.size(); a2++) {
3867 jnll_comp(JNLL_SEL_NONPARAM, flt) += sel_curve_pen(flt, 1) * sel_d2(a2) * sel_d2(a2);
3868 }
3869 }
3870
3871 // 3. Time variation. RandomWalk (4): the year-to-year change in realized log
3872 // selectivity (Atka mackerel). IID (1): each deviate, over only the bins
3873 // build_map() estimates, as a fixed bin would pull the sd toward zero.
3874 if(flt_varying_sel(flt) == 4){
3875 if(yr > 0){
3876 for(sex = 0; sex < nsex(sp); sex++){
3877 for(age = 0; age < (nbins - 1); age++) {
3878 jnll_comp(JNLL_SEL_DEV, flt) -= dnorm(log_non_par_sel(flt, sex, age, yr), log_non_par_sel(flt, sex, age, yr - 1), sel_dev_sd(flt), true);
3879 }
3880 }
3881 }
3882 } else if(flt_varying_sel(flt) == 1){
3883 for(sex = 0; sex < nsex(sp); sex++){
3884 for(int bin = bin_first_selected(flt); bin < flt_n_sel_bins(flt); bin++) {
3885 jnll_comp(JNLL_SEL_DEV, flt) -= dnorm(sel_coff_dev(flt, sex, bin, yr), Type(0.0), sel_dev_sd(flt), true);
3886 }
3887 }
3888 }
3889
3890 // 4. Average selectivity: holds each year's mean, deviates included.
3891 for(sex = 0; sex < nsex(sp); sex++){
3892 jnll_comp(JNLL_SEL_NONPARAM, flt) += 2.0 * square(avg_sel(flt, sex, yr));
3893 }
3894 }
3895 }
3896
3897 // NonParametricPM (9): ADMB's Selectivity_Likelihood. JNLL_SEL_NONPARAM is
3898 // sel_like(1): shape, Sel_curve_pen1 = ctrl_flag(13); plus the optional avgsel (5).
3899 // JNLL_SEL_DEV is sel_like_dev(1): curvature, Sel_curve_pen2 = ctrl_flag(11)/nch; a
3900 // random walk with no normalizing constant; increments, Sel_curve_pen3 =
3901 // ctrl_flag(10)/group_num.
3902 if(flt_sel_type(flt) == 9) {
3903
3904 int nyrs_tmp = 1;
3905 if(flt_varying_sel(flt) == 4){
3906 nyrs_tmp = nyrs_hind;
3907 }
3908
3909 // Years before the fleet's selectivity start year (0-based) are skipped, as in
3910 // ADMB, whose base-year terms are anchored at styr.
3911 int start_yr = flt_sel_start_yr(flt);
3912 int shape_a0 = flt_sel_pen_first_bin(flt); // first left bin of the shape pairs
3913 if(shape_a0 < 0) shape_a0 = bin_first_selected(flt);
3914 int shape_a1 = flt_sel_pen_last_bin(flt); // last left bin of the shape pairs
3915 if(shape_a1 < 0) shape_a1 = nbins - 2;
3916 // Pairs are (age, age + 1), so the last left bin is nbins - 2.
3917 if(shape_a1 > nbins - 2) shape_a1 = nbins - 2;
3918 int shape_mode = flt_sel_shape_mode(flt); // 0 = directional, 1 = smooth
3919 for(sex = 0; sex < nsex(sp); sex++){
3920 for(yr = start_yr; yr < nyrs_tmp; yr++){
3921
3922 // (1) Shape. Directional (ADMB/AMAK): Sel_curve_pen1 >= 0 penalizes
3923 // decreasing pairs, < 0 increasing ones. Smooth (RTMB "rpm"): two-sided d^2.
3924 for(age = shape_a0; age <= shape_a1; age++) {
3925 Type d = log_non_par_sel(flt, sex, age, yr) - log_non_par_sel(flt, sex, age + 1, yr); // > 0 if decreasing
3926 if(shape_mode == 1)
3927 jnll_comp(JNLL_SEL_NONPARAM, flt) += sel_curve_pen(flt, 0) * d * d;
3928 else if(sel_curve_pen(flt, 0) >= 0)
3929 jnll_comp(JNLL_SEL_NONPARAM, flt) += sel_curve_pen(flt, 0) * square( (CppAD::abs(d) + d)/2.0 );
3930 else
3931 jnll_comp(JNLL_SEL_NONPARAM, flt) += -sel_curve_pen(flt, 0) * square( (CppAD::abs(d) - d)/2.0 );
3932 }
3933
3934 // (2) Curvature; the start year counts only for a fleet starting at styr.
3935 if((yr > start_yr) || (start_yr == 0)){
3936 vector<Type> ls(nbins); ls.setZero();
3937 for(age = 0; age < nbins; age++) ls(age) = log_non_par_sel(flt, sex, age, yr);
3938 vector<Type> d2 = first_difference( first_difference( ls ) );
3939 for(int a2 = 0; a2 < d2.size(); a2++) jnll_comp(JNLL_SEL_DEV, flt) += sel_curve_pen(flt, 1) * d2(a2) * d2(a2);
3940 }
3941
3942 // (3) Random walk, a bare sum of squares.
3943 if(yr > start_yr){
3944 for(age = 0; age < nbins; age++) {
3945 Type dd = log_non_par_sel(flt, sex, age, yr) - log_non_par_sel(flt, sex, age, yr - 1);
3946 jnll_comp(JNLL_SEL_DEV, flt) += dd * dd / (2.0 * sel_dev_sd(flt) * sel_dev_sd(flt));
3947 }
3948 }
3949 }
3950
3951 // (4) Increment size: sum of squared sel_coff_dev, the random-walk increments.
3952 for(int bin = 0; bin < flt_n_sel_bins(flt); bin++){
3953 for(yr = start_yr; yr < nyrs_tmp; yr++){
3954 jnll_comp(JNLL_SEL_DEV, flt) += sel_curve_pen(flt, 2) * sel_coff_dev(flt, sex, bin, yr) * sel_coff_dev(flt, sex, bin, yr);
3955 }
3956 }
3957
3958 // (5) AMAK avgsel: Sel_avgsel_pen * log(mean(exp(base coefficients)))^2 over
3959 // the estimated bins; AMAK uses 10. Off (0) by default.
3960 if(flt_sel_avgsel_pen(flt) > 0){
3961 int n_base = flt_n_sel_bins(flt) - bin_first_selected(flt);
3962 vector<Type> base(n_base);
3963 for(int bin = 0; bin < n_base; bin++){
3964 base(bin) = sel_coff(flt, sex, bin + bin_first_selected(flt));
3965 }
3966 Type avgsel = log_mean_exp(base);
3967 jnll_comp(JNLL_SEL_NONPARAM, flt) += flt_sel_avgsel_pen(flt) * avgsel * avgsel;
3968 }
3969 }
3970 }
3971
3972 // NonParametricIntegrable (13): the NonParametric shape penalties, charged once on
3973 // the base curve sel_coff, and a proper normal density on the deviates, so
3974 // random_sel = TRUE integrates a complete density. With no deviates it equals form 2.
3975 if(flt_sel_type(flt) == 13) {
3976 int n_sel_bins = flt_n_sel_bins(flt);
3977 int start_yr = flt_sel_start_yr(flt);
3978 for(sex = 0; sex < nsex(sp); sex++){
3979
3980 // The base curve as selectivity.hpp builds a year with no deviates: the
3981 // estimated bins, the last held through the tail, centred to a mean exp() of 1.
3982 vector<Type> base(nbins);
3983 for(int bin = 0; bin < nbins; bin++){
3984 base(bin) = sel_coff(flt, sex, (bin < n_sel_bins ? bin : n_sel_bins - 1));
3985 }
3986 vector<Type> est(n_sel_bins);
3987 for(int bin = 0; bin < n_sel_bins; bin++) est(bin) = base(bin);
3988 Type avg_base = log_mean_exp(est);
3989 Type ctr = log_mean_exp(base);
3990 for(int bin = 0; bin < nbins; bin++) base(bin) -= ctr;
3991
3992 // 1. Decreasing selectivity.
3993 for(int bin = 0; bin < (nbins - 1); bin++) {
3994 Type d = base(bin) - base(bin + 1);
3995 jnll_comp(JNLL_SEL_NONPARAM, flt) += sel_curve_pen(flt, 0) * square( (CppAD::abs(d) + d)/2.0 );
3996 }
3997 // 2. Curvature.
3998 vector<Type> d2 = first_difference( first_difference( base ) );
3999 for(int a2 = 0; a2 < d2.size(); a2++) {
4000 jnll_comp(JNLL_SEL_NONPARAM, flt) += sel_curve_pen(flt, 1) * d2(a2) * d2(a2);
4001 }
4002 // 4. Average selectivity of the estimated coefficients.
4003 jnll_comp(JNLL_SEL_NONPARAM, flt) += 2.0 * square(avg_base);
4004
4005 // 3. Deviates, over the estimated bins only: IID in every hindcast year, or
4006 // random-walk increments after the start year (whose increment is fixed at 0).
4007 bool scored = (flt_varying_sel(flt) == 1) || (flt_varying_sel(flt) == 4);
4008 int yr_lo = (flt_varying_sel(flt) == 4) ? start_yr + 1 : 0;
4009 if(scored){
4010 for(yr = yr_lo; yr < nyrs_hind; yr++){
4011 for(int bin = bin_first_selected(flt); bin < n_sel_bins; bin++) {
4012 jnll_comp(JNLL_SEL_DEV, flt) -= dnorm(sel_coff_dev(flt, sex, bin, yr), Type(0.0), sel_dev_sd(flt), true);
4013 }
4014 }
4015 }
4016 }
4017 }
4018
4019 // Logistic-family selectivity deviates: logistic (1), double logistic (3),
4020 // descending logistic (4), double normal (8). Each limb's inflection has sd and log
4021 // slope 4 * sd; the ascending random walk swaps them, to match the GOA pollock
4022 // assessment. Double normal: slot 0 is the peak and ascending width, slot 1 the
4023 // right-floor logit and descending width.
4024 bool asc_limb = (flt_sel_type(flt) == 1) || (flt_sel_type(flt) == 3) || (flt_sel_type(flt) == 8);
4025 bool desc_limb = (flt_sel_type(flt) == 3) || (flt_sel_type(flt) == 4) || (flt_sel_type(flt) == 8);
4026
4027 // Time_varying_sel IID (1) or 2, scored the same way.
4028 if((flt_varying_sel(flt) == 1) || (flt_varying_sel(flt) == 2)){
4029 for(sex = 0; sex < nsex(sp); sex ++){
4030 for(yr = 0; yr < nyrs_hind; yr++){
4031 if(asc_limb){
4032 jnll_comp(JNLL_SEL_DEV, flt) -= dnorm(sel_inf_dev(0, flt, sex, yr), Type(0.0), sel_dev_sd(flt), true);
4033 jnll_comp(JNLL_SEL_DEV, flt) -= dnorm(log_sel_slp_dev(0, flt, sex, yr), Type(0.0), 4 * sel_dev_sd(flt), true);
4034 }
4035 if(desc_limb){
4036 jnll_comp(JNLL_SEL_DEV, flt) -= dnorm(sel_inf_dev(1, flt, sex, yr), Type(0.0), sel_dev_sd(flt), true);
4037 jnll_comp(JNLL_SEL_DEV, flt) -= dnorm(log_sel_slp_dev(1, flt, sex, yr), Type(0.0), 4 * sel_dev_sd(flt), true);
4038 }
4039 }
4040 }
4041 }
4042
4043 // Random walk on both limbs (4) or the ascending limb only (5). Under 5 the
4044 // descending deviates are not estimated, so scoring them would bias the sd down.
4045 if((flt_varying_sel(flt) == 4) || (flt_varying_sel(flt) == 5)){
4046 for(sex = 0; sex < nsex(sp); sex ++){
4047 for(yr = 1; yr < nyrs_hind; yr++){
4048 if(asc_limb){
4049 jnll_comp(JNLL_SEL_DEV, flt) -= dnorm(log_sel_slp_dev(0, flt, sex, yr) - log_sel_slp_dev(0, flt, sex, yr-1), Type(0.0), sel_dev_sd(flt), true);
4050 jnll_comp(JNLL_SEL_DEV, flt) -= dnorm(sel_inf_dev(0, flt, sex, yr) - sel_inf_dev(0, flt, sex, yr-1), Type(0.0), 4 * sel_dev_sd(flt), true);
4051 }
4052 if((flt_varying_sel(flt) == 4) && desc_limb){
4053 jnll_comp(JNLL_SEL_DEV, flt) -= dnorm(sel_inf_dev(1, flt, sex, yr) - sel_inf_dev(1, flt, sex, yr-1), Type(0.0), sel_dev_sd(flt), true);
4054 jnll_comp(JNLL_SEL_DEV, flt) -= dnorm(log_sel_slp_dev(1, flt, sex, yr) - log_sel_slp_dev(1, flt, sex, yr-1), Type(0.0), sel_dev_sd(flt) * 4, true);
4055 }
4056 }
4057 }
4058 }
4059
4060 // LogisticPM (11): the AMAK "pm" survey penalties (ctrl_flag(19) > 0), from the year
4061 // after the selectivity start year. Sel_curve_pen1 (ctrl_flag(26)) on the change in
4062 // log selectivity over [Sel_norm_bin, Sel_norm_bin_upper], and Sel_curve_pen3 (ADMB's
4063 // 8) on the change in the free age-1 deviate. Sel_curve_pen2 is unused.
4064 if(flt_sel_type(flt) == 11){
4065 int start_yr = flt_sel_start_yr(flt);
4066 int alo = sel_norm_bin1(flt); int ahi = sel_norm_bin2(flt);
4067 if(alo < 0) alo = bin_first_selected(flt); // default: whole selected range
4068 if(ahi < 0) ahi = nbins - 1;
4069 for(sex = 0; sex < nsex(sp); sex ++){
4070 for(yr = start_yr + 1; yr < nyrs_hind; yr++){
4071 for(age = alo; age <= ahi; age++){
4072 Type s_now = sel_is_length ? sel_at_length(flt, sex, age, yr) : sel_at_age(flt, sex, age, yr);
4073 Type s_prev = sel_is_length ? sel_at_length(flt, sex, age, yr - 1) : sel_at_age(flt, sex, age, yr - 1);
4074 Type d = log(s_now) - log(s_prev);
4075 jnll_comp(JNLL_SEL_DEV, flt) += sel_curve_pen(flt, 0) * d * d;
4076 }
4077 Type da1 = sel_inf_dev(1, flt, sex, yr) - sel_inf_dev(1, flt, sex, yr - 1);
4078 jnll_comp(JNLL_SEL_DEV, flt) += sel_curve_pen(flt, 2) * da1 * da1;
4079 }
4080 }
4081 }
4082
4083 // Hake non-parametric (5): IID deviates (Taylor et al. 2014), over all N_sel_bins,
4084 // including any bin build_map() holds at 0.
4085 if(((flt_varying_sel(flt) == 1) || (flt_varying_sel(flt) == 2)) && (flt_sel_type(flt) == 5)){
4086 for(int bin = 0; bin < flt_n_sel_bins(flt); bin++){
4087 for(sex = 0; sex < nsex(sp); sex++){
4088 for(yr = 0; yr < nyrs_hind; yr++){
4089 jnll_comp(JNLL_SEL_DEV, flt) -= dnorm(sel_coff_dev(flt, sex, bin, yr), Type(0.0), sel_dev_sd(flt), true);
4090 }
4091 }
4092 }
4093 }
4094
4095 // 2D AR1 (6): separable AR1 over year and bin. rho_trans() maps Sel_curve_pen1 and 2
4096 // to the bin and year correlations; sel_dev_sd is the conditional sd.
4097 if(flt_sel_type(flt) == 6){
4098 int n_sel_bins = flt_n_sel_bins(flt);
4099 array<Type> tmp_AR2(n_sel_bins, nyrs_hind);
4100
4101 for(sex = 0; sex < nsex(sp); sex ++){
4102 for (int bin = 0; bin < n_sel_bins; bin++) {
4103 for (int yr = 0; yr < nyrs_hind; yr++) {
4104 tmp_AR2(bin, yr) = sel_coff_dev(flt, sex, bin, yr);
4105 }
4106 }
4107
4108 Type rho_a = rho_trans(sel_curve_pen(flt, 0));
4109 Type rho_y = rho_trans(sel_curve_pen(flt, 1));
4110
4111 Type Sigma_sig_sel = pow(pow(sel_dev_sd(flt),2) / ((1-pow(rho_y,2))*(1-pow(rho_a,2))),0.5);
4112 // SEPARABLE(f, g): f correlates the last array index, g the first; tmp_AR2 is
4113 // (bin, year), so the year correlation goes first.
4114 jnll_comp(JNLL_SEL_DEV, flt) += SCALE(SEPARABLE(AR1(rho_y),AR1(rho_a)), Sigma_sig_sel)(tmp_AR2);
4115 }
4116 }
4117
4118 // 3D AR1 (7): GMRF over bin, year and cohort (Cheng et al. 2024). Sel_curve_pen1-3
4119 // carry the bin, year and cohort correlations.
4120 if(flt_sel_type(flt) == 7){
4121 int n_sel_bins = flt_n_sel_bins(flt);
4122 int num_rows = n_sel_bins * nyrs_hind;
4123 matrix<Type> ay_index(num_rows, 2);
4124
4125 for(sex = 0; sex < nsex(sp); sex ++){
4126
4127 int row = 0;
4128 for (int year = 1; year <= nyrs_hind; ++year) {
4129 for (int bin = 1; bin <= n_sel_bins; ++bin) {
4130 ay_index(row, 0) = bin;
4131 ay_index(row, 1) = year;
4132 row++;
4133 }
4134 }
4135
4136 // Arguments run bin, year, cohort. construct_Q()'s parameter names read the
4137 // other way round, but ay_index is also filled the other way round from WHAM's,
4138 // so the two cancel. Do not "correct" the call to match the names.
4139 Eigen::SparseMatrix<Type> Q_sparse(num_rows, num_rows);
4140 Q_sparse = construct_Q(nyrs_hind, n_sel_bins, ay_index,
4141 sel_curve_pen(flt, 0), sel_curve_pen(flt, 1), sel_curve_pen(flt, 2), // natural scale
4142 log(square(sel_dev_sd(flt))), 0); // conditional variance
4143
4144 array<Type> tmp_AR2(n_sel_bins, nyrs_hind);
4145 for (int bin = 0; bin < n_sel_bins; bin++) {
4146 for (int yr = 0; yr < nyrs_hind; yr++) {
4147 tmp_AR2(bin, yr) = sel_coff_dev(flt, sex, bin, yr);
4148 }
4149 }
4150 jnll_comp(JNLL_SEL_DEV, flt) += GMRF(Q_sparse)(tmp_AR2);
4151 }
4152 }
4153 }
4154 }
4155
4156
4157 // 13.8. CATCHABILITY (JNLL_Q_PRIOR, JNLL_Q_DEV)
4158 // Charged once per Catchability_index, on the lead fleet.
4159 for(flt = 0; flt < n_flt; flt++){
4160 if(flt_q_lead(flt) == 1){
4161
4162 // Lognormal prior (Catchability 2, Estimated-with-prior). Catchability_init is the
4163 // mean of q when bias_adjust_proc = 1, the median when 0.
4164 if( est_index_q(flt) == 2){
4165 jnll_comp(JNLL_Q_PRIOR, flt) -= dnorm(index_log_q(flt), index_log_q_prior(flt) - bias_adjust_proc*square(index_q_sd(flt))/2.0, index_q_sd(flt), true);
4166 }
4167
4168 // Deviates, on an estimated q (Catchability 1 or 2) of a survey or CPUE fleet.
4169 bool q_devs = (flt_type(flt) > 0) && ((est_index_q(flt) == 1) || (est_index_q(flt) == 2));
4170
4171 // IID (1); the retired AR1 (2) is scored as IID.
4172 if(q_devs && ((index_varying_q(flt) == 1) || (index_varying_q(flt) == 2))){
4173 for(yr = 0; yr < nyrs_hind; yr++){
4174 jnll_comp(JNLL_Q_DEV, flt) -= dnorm(index_q_dev(flt, yr), Type(0.0), index_q_dev_sd(flt), true );
4175 }
4176 }
4177
4178 // RandomWalk (4)
4179 if(q_devs && (index_varying_q(flt) == 4)){
4180 for(yr = 1; yr < nyrs_hind; yr++){
4181 jnll_comp(JNLL_Q_DEV, flt) -= dnorm(index_q_dev(flt, yr) - index_q_dev(flt, yr-1), Type(0.0), index_q_dev_sd(flt), true );
4182 }
4183 }
4184 }
4185 }
4186
4187
4188 // 13.9. RECRUITMENT (JNLL_SRR_PRIOR, JNLL_INIT_DEV, JNLL_REC_DEV, JNLL_SRR_PENALTY)
4189 // Lognormal deviates are centred at -sigma^2/2 when bias_adjust_proc = 1, so the
4190 // expected recruitment is the curve's (mean-unbiased); at 0 the curve is the median.
4191 for(sp = 0; sp < nspp; sp++) {
4192 penalty = 0.0;
4193 // Input numbers-at-age (estDynamics > 0) have no stock-recruit curve to penalize.
4194 int srr_terms_on = (estDynamics(sp) == 0);
4195
4196 // Beverton-Holt steepness prior: lognormal (srr_prior is its mean or median, as above)...
4197 if(srr_terms_on && (srr_est_mode == 2) & ((srr_pred_fun == 2) | (srr_pred_fun == 3))){
4198 jnll_comp(JNLL_SRR_PRIOR, sp) -= dnorm(log(steepness(sp, 0)), log(srr_prior(sp)) - bias_adjust_proc*square(srr_prior_sd(sp))/2.0, srr_prior_sd(sp), true);
4199 }
4200
4201 // ...or beta, from the prior mean and sd.
4202 if(srr_terms_on && (srr_est_mode == 3) & ((srr_pred_fun == 2) | (srr_pred_fun == 3))){
4203 Type beta_alpha = ((1 - srr_prior(sp))/ square(srr_prior_sd(sp)) - 1/srr_prior(sp)) * square(srr_prior(sp));
4204 Type beta_beta = beta_alpha * (1/srr_prior(sp) - 1);
4205 jnll_comp(JNLL_SRR_PRIOR, sp) -= dbeta(steepness(sp, 0), beta_alpha, beta_beta, true);
4206 }
4207
4208 // Ricker alpha prior, lognormal.
4209 if(srr_terms_on && (srr_est_mode == 2) & ((srr_pred_fun == 4) | (srr_pred_fun == 5))){
4210 jnll_comp(JNLL_SRR_PRIOR, sp) -= dnorm(rec_pars(sp, 1), log(srr_prior(sp)) - bias_adjust_proc*square(srr_prior_sd(sp))/2.0, srr_prior_sd(sp), true);
4211 }
4212
4213 // Ricker: penalty when 1 / beta (spawning biomass at peak recruitment) exceeds
4214 // Bmsy_lim / 1e6.
4215 if(srr_terms_on && (!isNA(Bmsy_lim(sp))) && ((srr_pred_fun == 4) || (srr_pred_fun == 5))){
4216 Type bmsy = 1.0/exp(rec_pars(sp, 2));
4217 bmsy = posfun(Bmsy_lim(sp)/Type(1000000.0) - bmsy, Type(0.001), penalty);
4218 jnll_comp(JNLL_SRR_PRIOR, sp) += 100 * penalty;
4219 }
4220
4221 // Initial-abundance deviates; none under FreeParams (0), Equilibrium (1) or
4222 // OffsetEquilibrium (5).
4223 if(initMode > 1 && initMode != 5){
4224 for(age = 1; age < nages(sp); age++) {
4225 jnll_comp(JNLL_INIT_DEV, sp) -= dnorm( init_dev(sp, age - 1), -bias_adjust_proc*square(R_sd(sp))/2.0, R_sd(sp), true);
4226 }
4227 }
4228
4229 // Recruitment deviates.
4230 for(yr = 0; yr < nyrs_hind; yr++) {
4231 jnll_comp(JNLL_REC_DEV, sp) -= dnorm( rec_dev(sp, yr), -bias_adjust_proc*square(R_sd(sp))/2.0, R_sd(sp), true);
4232 }
4233
4234 // Stock-recruit curve penalty (AMAK/Ianelli): recruitment is estimated about a mean
4235 // (srr_fun 0) and a curve is fitted to it (srr_pred_fun > 0). R_hat is the mean of R
4236 // when bias_adjust_proc = 1 (Ianelli pm.tpl, Dorn 2002), the median (AMAK) when 0.
4237 if(srr_terms_on && (srr_fun == 0) & (srr_pred_fun > 0)){
4238 for(yr = srr_hat_styr; yr <= srr_hat_endyr; yr++) {
4239 jnll_comp(JNLL_SRR_PENALTY, sp) -= dnorm( log(R(sp, yr)), log(R_hat(sp,yr)) - bias_adjust_proc*square(R_sd(sp))/2.0, R_sd(sp), true);
4240 }
4241 }
4242 }
4243
4244
4245 // 13.10. REFERENCE POINT PENALTIES (JNLL_REFPT_PENALTY)
4246 // Each finds the F that meets a harvest-control-rule target by penalizing the miss.
4247
4248 // HCR 1 (CMSY): maximize total terminal-year catch across species, subject to
4249 // SSB depletion staying above Plimit.
4250 Type CMSY = 0;
4251 if(HCR == 1){
4252 for(fsh_ind = 0; fsh_ind < catch_ctl.rows(); fsh_ind++){
4253 flt = catch_ctl(fsh_ind, 0) - 1;
4254 sp = catch_ctl(fsh_ind, 1) - 1;
4255 flt_yr = catch_ctl(fsh_ind, 2);
4256 if((flt_type(flt) == 1) && (forecast(sp) == 1) && (estDynamics(sp) == 0)){
4257 if(flt_yr == projyr){
4258 CMSY += catch_hat(fsh_ind);
4259 }
4260 }
4261 }
4262
4263 jnll_comp(JNLL_REFPT_PENALTY, 0) = - square(CMSY/1000000.0);
4264
4265 for(sp = 0; sp < nspp; sp++) {
4266 if((forecast(sp) == 1) && (estDynamics(sp) == 0)){
4267 penalty = 0.0;
4268 Type nothing_useful = posfun( (ssb_depletion(sp, nyrs-1) - Plimit(sp)), Type(0.0001), penalty); (void) nothing_useful;
4269 jnll_comp(JNLL_REFPT_PENALTY, sp) += 500.0 * square(CMSY/1000.0) * penalty;
4270 }
4271 }
4272 }
4273
4274 for(sp = 0; sp < nspp; sp++) {
4275 // Single species: Flimit and Ftarget are SPR fractions, except under HCR 3, where
4276 // Ftarget is a fraction of SB0.
4277 if((forecast(sp) == 1) && (msmMode == 0) && (estDynamics(sp) == 0) && ((DynamicHCR == 0) || (DynamicHCR == 1))){
4278
4279 // ConstantF (2): Flimit is still estimated, for reference.
4280 if(HCR == 2){
4281 jnll_comp(JNLL_REFPT_PENALTY, sp) += 200*square((SPRlimit(sp)/SPR0(sp))-Flimit_percent(sp));
4282 }
4283
4284 // ConstantFSSB (3): SSB at Ftarget of SB0 in the last projection year (static),
4285 // or every year against dynamic SB0.
4286 if(HCR == 3){
4287 if(DynamicHCR == 0){
4288 jnll_comp(JNLL_REFPT_PENALTY, sp) += 200*square((ssb(sp, nyrs-1)/SB0(sp, nyrs-1))-Ftarget_percent(sp));
4289 } else {
4290 for(yr = 1; yr < nyrs; yr++){ // No initial abundance
4291 jnll_comp(JNLL_REFPT_PENALTY, sp) += 200*square((DynamicSBF(sp, yr)/DynamicSB0(sp, yr))-Ftarget_percent(sp));
4292 }
4293 }
4294 }
4295
4296 // ConstantFSPR, NPFMC, PFMC, SESSF (4-7): SPR-based limit and target.
4297 if(HCR > 3){
4298 jnll_comp(JNLL_REFPT_PENALTY, sp) += 200*square((SPRlimit(sp)/SPR0(sp))-Flimit_percent(sp));
4299 jnll_comp(JNLL_REFPT_PENALTY, sp) += 200*square((SPRtarget(sp)/SPR0(sp))-Ftarget_percent(sp));
4300 }
4301 }
4302
4303 // Multispecies: per-recruit quantities are undefined under predation, so each
4304 // target is a fraction of SB0 in the last projection year (2 and 6 Flimit, 3 Ftarget).
4305 if((forecast(sp) == 1) && (msmMode > 0) && (estDynamics(sp) == 0)){
4306 if(HCR == 2){
4307 jnll_comp(JNLL_REFPT_PENALTY, sp) += 200*square((ssb(sp, nyrs-1)/SB0(sp, nyrs-1))-Flimit_percent(sp));
4308 }
4309 if(HCR == 3){
4310 jnll_comp(JNLL_REFPT_PENALTY, sp) += 200*square((ssb(sp, nyrs-1)/SB0(sp, nyrs-1))-Ftarget_percent(sp));
4311 }
4312 if(HCR == 6){
4313 jnll_comp(JNLL_REFPT_PENALTY, sp) += 200*square((ssb(sp, nyrs-1)/SB0(sp, nyrs-1))-Flimit_percent(sp));
4314 }
4315 }
4316 }
4317
4318
4319 // 13.11. FLOOR PENALTIES (JNLL_ZERO_N_PENALTY): what posfun() adds in the dynamics
4320 // when it holds numbers-at-age, or recruitment terms, above zero.
4321 for(sp = 0; sp < nspp; sp++){
4322 if(estDynamics(sp) == 0){
4323 jnll_comp(JNLL_ZERO_N_PENALTY, sp) += zero_N_pen(sp);
4324 }
4325 }
4326
4327
4328 // 13.12. NATURAL MORTALITY (JNLL_M_PRIOR, JNLL_M_RE)
4329 for(sp = 0; sp < nspp; sp++) {
4330
4331 // Lognormal prior: M_prior is its mean when bias_adjust_proc = 1, its median when 0.
4332 Type M_prior_mean = log(M_prior(sp)) - bias_adjust_proc*square(M_prior_sd(sp)) / 2.0;
4333
4334 // On the estimated M1 only: one value (M1_model 1), one per sex (2), or one per sex
4335 // and age (3).
4336 if( (M1_use_prior(sp) == 1) && (M2_use_prior(sp) == 0) ) {
4337 int nsex_tmp = (M1_model(sp) > 1) ? nsex(sp) : 1;
4338 int nage_tmp = (M1_model(sp) == 3) ? nages(sp) : 1;
4339
4340 for(int sex = 0; sex < nsex_tmp; sex++) {
4341 for(int age = 0; age < nage_tmp; age++) {
4342 jnll_comp(JNLL_M_PRIOR, sp) -= dnorm(log_M1(sp, sex, age), M_prior_mean, M_prior_sd(sp), true);
4343 }
4344 }
4345 }
4346
4347 // On total M = M1 + M2, every sex, age and hindcast year.
4348 // FIXME: sex-specific prior?
4349 if( M2_use_prior(sp) == 1 ) {
4350 for(int sex = 0; sex < nsex(sp); sex++) {
4351 for(int age = 0; age < nages(sp); age++) {
4352 for(int yr = 0; yr < nyrs_hind; yr++) {
4353 jnll_comp(JNLL_M_PRIOR, sp) -= dnorm(log(M_at_age(sp, sex, age, yr)), M_prior_mean, M_prior_sd(sp), true);
4354 }
4355 }
4356 }
4357 }
4358
4359 // M1 random effects (M1_re): 1 IID by age, 2 IID by year, 3 IID by age and year,
4360 // 4 AR1 by age, 5 AR1 by year, 6 2D AR1; the IID codes are AR1 with rho mapped at
4361 // its start. sigma (the conditional sd) and rho are per species, shared by sex;
4362 // the deviates are per sex under M1_model 2. SCALE() takes the marginal sd.
4363 int num_re_sexes = (M1_model(sp) == 2 && nsex(sp) > 1) ? 2 : 1;
4364
4365 // By age, constant over years.
4366 if( (M1_re(sp) == 1) || (M1_re(sp) == 4) ) {
4367 Type sigma_M = exp(M1_dev_log_sd(sp, 0));
4368 Type rho_M_a = rho_trans(M1_rho(sp, 0, 0));
4369 Type Sigma_M = pow(pow(sigma_M, 2) / (1.0 - pow(rho_M_a, 2)), 0.5);
4370 vector<Type> M_re_age(nages(sp));
4371
4372 for(int sex = 0; sex < num_re_sexes; sex++) {
4373 for(int age = 0; age < nages(sp); age++) {
4374 M_re_age(age) = log_M1_dev(sp, sex, age, 0);
4375 }
4376 jnll_comp(JNLL_M_RE, sp) += SCALE(AR1(rho_M_a), Sigma_M)(M_re_age);
4377 }
4378 }
4379
4380 // By year, constant over ages.
4381 if( (M1_re(sp) == 2) || (M1_re(sp) == 5) ) {
4382 Type sigma_M = exp(M1_dev_log_sd(sp, 0));
4383 Type rho_M_y = rho_trans(M1_rho(sp, 0, 1));
4384 Type Sigma_M = pow(pow(sigma_M, 2) / (1.0 - pow(rho_M_y, 2)), 0.5);
4385 vector<Type> M_re_yr(nyrs_hind);
4386
4387 for(int sex = 0; sex < num_re_sexes; sex++) {
4388 for(int yr = 0; yr < nyrs_hind; yr++) {
4389 M_re_yr(yr) = log_M1_dev(sp, sex, 0, yr);
4390 }
4391 jnll_comp(JNLL_M_RE, sp) += SCALE(AR1(rho_M_y), Sigma_M)(M_re_yr);
4392 }
4393 }
4394
4395 // By age and year.
4396 if( (M1_re(sp) == 3) || (M1_re(sp) == 6) ) {
4397 Type sigma_M = exp(M1_dev_log_sd(sp, 0));
4398 Type rho_M_a = rho_trans(M1_rho(sp, 0, 0));
4399 Type rho_M_y = rho_trans(M1_rho(sp, 0, 1));
4400 Type Sigma_M = pow(pow(sigma_M, 2) / ((1.0 - pow(rho_M_y, 2)) * (1.0 - pow(rho_M_a, 2))), 0.5);
4401 array<Type> M_re_a_yr(nages(sp), nyrs_hind);
4402
4403 for(int sex = 0; sex < num_re_sexes; sex++) {
4404 for(int age = 0; age < nages(sp); age++) {
4405 for(int yr = 0; yr < nyrs_hind; yr++) {
4406 M_re_a_yr(age, yr) = log_M1_dev(sp, sex, age, yr);
4407 }
4408 }
4409 // SEPARABLE(f, g): f correlates the last array index, g the first; the array is
4410 // (age, year), so the year correlation goes first.
4411 jnll_comp(JNLL_M_RE, sp) += SCALE(SEPARABLE(AR1(rho_M_y), AR1(rho_M_a)), Sigma_M)(M_re_a_yr);
4412 }
4413 }
4414 }
4415
4416
4417 // 13.13. LINKAGE PRIORS (JNLL_LINKAGE_PRIOR)
4418 // Families are in linkage_log_prior() (likelihood.hpp). A prior on a log-stored base (R0,
4419 // alpha, M1, K, q, slope, apical) is on its natural scale; one on a sel inflection or
4420 // DoubleNormalSS3 parameter is on its stored scale. Most (Intercept) rows are held at 0 (2.3b), so
4421 // the prior reads the base parameter they stand in for; a slope row reads its coefficient.
4422 for (int i = 0; i < beta_linkage.size(); ++i) {
4423 int fam = linkage_prior_family(i);
4424 if (fam == 0) continue; // 0 = no prior
4425 Type p1 = linkage_prior_p1(i);
4426 Type p2 = linkage_prior_p2(i);
4427 int slot_col = linkage_species(i) - 1; // jnll column: the row's species, or 0 if it names none
4428 if (slot_col < 0) slot_col = 0; // all species => column 0
4429 if (slot_col >= n_col) slot_col = 0;
4430
4431 Type b = beta_linkage(i);
4432 bool base_is_log = true; // most base parameters are stored as logs
4433 if (linkage_is_intercept(i) == 1) {
4434 // Species/sex/age 0 means "all"; the prior reads the first level's base parameter.
4435 int sp_in = linkage_species(i);
4436 int sx_in = linkage_sex(i);
4437 int ab_in = linkage_age_bin(i);
4438 int sp_idx = (sp_in == 0) ? 0 : (sp_in - 1);
4439 int sx_idx = (sx_in == 0) ? 0 : (sx_in - 1);
4440 int ab_idx = (ab_in == 0) ? 0 : (ab_in - 1);
4441
4442 int proc = linkage_process(i);
4443 int param = linkage_param(i);
4444 if (proc == RCEATTLE_PROC_RECRUIT) {
4445 // rec_pars columns: log R0, log alpha, log Beta (stock-recruit). R_init has no base parameter;
4446 // its intercept is the level, so its prior stays on beta_linkage(i).
4447 if (param != RCEATTLE_REC_R_INIT) {
4448 b = rec_pars(sp_idx, param);
4449 }
4450 } else if (proc == RCEATTLE_PROC_M) {
4451 b = log_M1(sp_idx, sx_idx, ab_idx);
4452 } else if (proc == RCEATTLE_PROC_GROWTH) {
4453 // K, L1, Linf, m in log_growth_pars; then sd_L1, sd_Linf in growth_log_sd.
4454 if (param < RCEATTLE_N_GROWTH_PARAMS) {
4455 b = log_growth_pars(sp_idx, sx_idx, param);
4456 } else {
4457 b = growth_log_sd(sp_idx, sx_idx, param - RCEATTLE_N_GROWTH_PARAMS);
4458 }
4459 } else if (proc == RCEATTLE_PROC_Q) {
4460 // Fleet-indexed: log q.
4461 int fl_in = linkage_fleet(i);
4462 int fl_idx = (fl_in == 0) ? 0 : (fl_in - 1);
4463 b = index_log_q(fl_idx);
4464 } else if (proc == RCEATTLE_PROC_COMP) {
4465 // Dirichlet-multinomial log theta: comp and CAAL by fleet, diet by predator.
4466 int fl_in = linkage_fleet(i);
4467 int fl_idx = (fl_in == 0) ? 0 : (fl_in - 1);
4468 if (param == RCEATTLE_COMP_THETA_COMP) b = comp_weights(fl_idx);
4469 else if (param == RCEATTLE_COMP_THETA_CAAL) b = caal_weights(fl_idx);
4470 else if (param == RCEATTLE_COMP_THETA_DIET) b = diet_comp_weights(sp_idx);
4471 } else if (proc == RCEATTLE_PROC_SEL) {
4472 // Parametric selectivity, element 0 ascending, 1 descending. The (Intercept)
4473 // adds no offset, so the prior regularizes the base parameter (as in Cole
4474 // Monnahan's GOA pollock priors). RCEATTLE_SEL_COFF has no scalar base and is
4475 // refused by .check_sel_linkage_support().
4476 int fl_in = linkage_fleet(i);
4477 int fl_idx = (fl_in == 0) ? 0 : (fl_in - 1);
4478 if (param == RCEATTLE_SEL_SLP_ASC || param == RCEATTLE_SEL_SLP_DESC) {
4479 b = log_sel_slp(param - RCEATTLE_SEL_SLP_ASC, fl_idx, sx_idx); // log slope
4480 } else if (param == RCEATTLE_SEL_INF_ASC || param == RCEATTLE_SEL_INF_DESC) {
4481 // As stored: an inflection, DoubleNormal's peak or logit floor, or LogisticPM's
4482 // log first-bin selectivity.
4483 b = sel_inf(param - RCEATTLE_SEL_INF_ASC, fl_idx, sx_idx);
4484 base_is_log = false;
4485 } else if (param == RCEATTLE_SEL_APICAL) {
4486 b = log_sel_apical(fl_idx, sx_idx); // log apical multiplier; 0 = a multiplier of 1
4487 } else if (param >= RCEATTLE_SEL_DN_PEAK && param <= RCEATTLE_SEL_END_LOGIT) {
4488 // DoubleNormalSS3, on SS3's own scale (peak in bins or cm, log widths, logits),
4489 // where an SS3 control file puts its priors.
4490 b = sel_dn6(param - RCEATTLE_SEL_DN_PEAK, fl_idx, sx_idx);
4491 base_is_log = false;
4492 }
4493 }
4494 }
4495
4496 Type log_prior = linkage_log_prior(fam, b, (linkage_is_intercept(i) == 1) && base_is_log,
4497 p1, p2, bias_adjust_proc);
4498 jnll_comp(JNLL_LINKAGE_PRIOR, slot_col) -= log_prior;
4499 unweighted_jnll_comp(JNLL_LINKAGE_PRIOR, slot_col) -= log_prior;
4500 }
4501
4502 // Priors on each random-effect group's sd, from linkage_spec(priors = list(sigma = )).
4503 // FIXME(jacobian): normal, gamma and beta read the sd while log_sigma is estimated,
4504 // with no Jacobian -- a penalty, not a proper prior. Add log_sigma for a density.
4505 if (log_sigma_linkage.size() > 0) {
4506 for (int g = 0; g < log_sigma_linkage.size(); ++g) {
4507 int fam = linkage_re_sigma_prior_family(g);
4508 if (fam == 0) continue; // 0 = no prior
4509 Type sd = exp(log_sigma_linkage(g));
4510 Type log_prior = linkage_log_prior(fam, sd, false, linkage_re_sigma_prior_p1(g),
4511 linkage_re_sigma_prior_p2(g), bias_adjust_proc);
4512 jnll_comp(JNLL_LINKAGE_PRIOR, 0) -= log_prior;
4513 unweighted_jnll_comp(JNLL_LINKAGE_PRIOR, 0) -= log_prior;
4514 }
4515 }
4516
4517 // Priors on each AR1 group's correlation rho in (-1, 1), from priors = list(rho = ):
4518 // normal on rho, or beta on (rho + 1) / 2.
4519 // FIXME(jacobian): as for sigma, no rho_trans Jacobian.
4520 if (trans_rho_linkage.size() > 0) {
4521 for (int g = 0; g < linkage_re_rho.size(); ++g) {
4522 int fam = linkage_re_rho_prior_family(g);
4523 if ((fam != 1) && (fam != 4)) continue; // only normal (1) and beta (4) are read for rho
4524 Type rho = rho_trans(trans_rho_linkage(linkage_re_rho(g)));
4525 Type x = (fam == 4) ? (rho + Type(1)) / Type(2) : rho;
4526 Type log_prior = linkage_log_prior(fam, x, false, linkage_re_rho_prior_p1(g),
4527 linkage_re_rho_prior_p2(g), bias_adjust_proc);
4528 jnll_comp(JNLL_LINKAGE_PRIOR, 0) -= log_prior;
4529 unweighted_jnll_comp(JNLL_LINKAGE_PRIOR, 0) -= log_prior;
4530 }
4531 }
4532
4533
4534 // 13.14. LINKAGE RANDOM EFFECTS (JNLL_LINKAGE_RE)
4535 // Group g's deviations u, in time order: IID u_t ~ N(0, sigma_g); random walk
4536 // u_t - u_{t-1} ~ N(0, sigma_g); or stationary AR1, marginal sd sigma_g
4537 // (rceattle_re_group_nll()). Integrated and penalized deviations share this density.
4538 for (int grp = 0; grp < log_sigma_linkage.size(); ++grp) {
4539 vector<int> slots = rceattle_group_slots(linkage_re_sigma, grp);
4540 int len = slots.size();
4541 if (len == 0) continue;
4542 vector<Type> re(len);
4543 for (int t = 0; t < len; ++t) re(t) = beta_linkage_re_all(slots(t));
4544
4545 int st = linkage_re_struct(grp);
4546 Type sigma = exp(log_sigma_linkage(grp));
4547 Type rho = (st == RCEATTLE_RE_AR1) ? rho_trans(trans_rho_linkage(linkage_re_rho(grp))) : Type(0);
4548 rceattle_re_group_nll(st, re, sigma, rho,
4549 jnll_comp(JNLL_LINKAGE_RE, 0), unweighted_jnll_comp(JNLL_LINKAGE_RE, 0));
4550
4551 // Rogers QAR1: covariate x(t) ~ N(re(t), obs_sd), obs_sd fixed unless obs_sd_est =
4552 // TRUE. Tying re to x identifies beta_linkage_obs, which multiplies re (5.2b).
4553 if (linkage_re_obs(grp) >= 0) {
4554 Type osd = exp(log_obs_sd_linkage(linkage_re_obs(grp)));
4555 for (int t = 0; t < len; ++t) {
4556 int s = slots(t);
4557 if (linkage_re_obs_mask(s) == 0) continue; // no observation that year
4558 if (osa_mode == 0) {
4559 Type log_dens = dnorm(linkage_re_obs_value(s), re(t), osd, true);
4560 jnll_comp(JNLL_LINKAGE_RE, 0) -= log_dens;
4561 unweighted_jnll_comp(JNLL_LINKAGE_RE, 0) -= log_dens;
4562 } else {
4563 // OSA (osa_mode 1-2): the same x(t), from obsvec so oneStepPredict() can hold
4564 // it out through keep(pos); residualized against re(t), as WHAM's Ecov.
4565 int pos = linkage_re_obsvec_idx(s);
4566 if (pos >= 0) {
4567 jnll_comp(JNLL_LINKAGE_RE, 0) -= keep(pos) * dnorm(obsvec(pos), re(t), osd, true);
4568 unweighted_jnll_comp(JNLL_LINKAGE_RE, 0) -= keep(pos) * dnorm(obsvec(pos), re(t), osd, true);
4569 // The CDF term is not a likelihood, so only jnll_comp gets it here.
4570 if (osa_mode == 2) {
4571 jnll_comp(JNLL_LINKAGE_RE, 0) -= osa_norm_cdf_terms(
4572 obsvec(pos), re(t), osd, keep.cdf_lower(pos), keep.cdf_upper(pos));
4573 }
4574 }
4575 }
4576 }
4577 }
4578 }
4579
4580
4581 // 13.15. UNWEIGHTED COMPONENTS
4582 // jnll_comp without data weights. In ordinary fitting the composition, CAAL and
4583 // stomach rows were written unweighted above (0 under OSA), and the linkage rows to
4584 // both; every other row carries no data weight, so it is copied.
4585 for(flt = 0; flt < n_col; flt++){
4586 for(int ind = 0; ind < JNLL_N_ROWS; ind++){
4587 bool written_above = (ind == JNLL_COMP) || (ind == JNLL_CAAL) || (ind == JNLL_STOMACH) ||
4588 (ind == JNLL_LINKAGE_PRIOR) || (ind == JNLL_LINKAGE_RE);
4589 if(!written_above){
4590 unweighted_jnll_comp(ind, flt) = jnll_comp(ind, flt);
4591 }
4592 }
4593 }
4594
4595
4596 /** ------------------------------------------------------------------------ //
4597 // 14. REPORT SECTION //
4598 * ------------------------------------------------------------------------- */
4599
4600 // 14.1. Population components
4601 REPORT( pop_scalar );
4602 ADREPORT( pop_scalar );
4603 REPORT( avg_R );
4604 REPORT( Z_at_age );
4605 REPORT( N_at_age );
4606 REPORT( avgN_at_age );
4607 REPORT( biomass_at_age );
4608 REPORT( biomass_depletion );
4609 REPORT( ssb_depletion );
4610 REPORT( biomass );
4611 REPORT( ssb );
4612 REPORT( exploitable_biomass );
4613 REPORT(R_sd);
4614 REPORT( R0 );
4615 REPORT( R_init );
4616 REPORT( R );
4617 REPORT( R_hat );
4618 REPORT( M_at_age );
4619 //REPORT( mature_females );
4620 //REPORT( sex_ratio_hat );
4621 //REPORT( N_at_age_dB0);
4622 //REPORT( N_at_age_dBF);
4623 // ADREPORT( M_at_age ); // no SEs: see ?plot_mortality
4624 REPORT( weight_hat );
4625 REPORT( growth_matrix );
4626 REPORT( length_hat );
4627
4628 ADREPORT( biomass );
4629 ADREPORT( ssb );
4630
4631 // -- Secondary display series, so add_ci = TRUE can draw an interval at all.
4632 // Natural scale ONLY: exploitable_biomass sums over fisheries alone, so it is
4633 // exactly 0 for a survey-only species or proj_F_prop = 0, and the depletions
4634 // divide by B0 / SB0. log() of either puts -Inf on the tape and NaNs the whole
4635 // sdreport; the plotters recover sd(log x) = sd(x)/x instead.
4636 ADREPORT( exploitable_biomass );
4637 ADREPORT( biomass_depletion );
4638 ADREPORT( ssb_depletion );
4639
4640 // -- Log-scale derived series.
4641 // sdreport linearizes once about the MLE, so its SD is exact only for a linear function
4642 // of the parameters. These are built multiplicatively (R = R0 * exp(rec_dev); n-at-age
4643 // is a product of survivals), so log(x) is near-linear and exp(log(x) +/- z * sd) is
4644 // right-skewed and cannot cross zero. The log SD is also the CV, the form the ABC / OFL
4645 // buffer wants. The natural-scale ADREPORTs stay so existing sdrep$value callers work.
4646 matrix<Type> log_biomass = biomass; log_biomass = log(biomass.array());
4647 matrix<Type> log_ssb = ssb; log_ssb = log(ssb.array());
4648 matrix<Type> log_R = R; log_R = log(R.array());
4649 REPORT( log_biomass );
4650 REPORT( log_ssb );
4651 REPORT( log_R );
4652 ADREPORT( log_biomass );
4653 ADREPORT( log_ssb );
4654 ADREPORT( log_R );
4655 ADREPORT( R_sd );
4656 ADREPORT( R );
4657
4658 // -- Log selectivity-at-age, for a delta-method interval on the curve. Log and not
4659 // logit, because the non-parametric forms normalize to a MEAN of one, so sel_at_age
4660 // routinely exceeds one. Gated because the delta method costs every reported value
4661 // against every parameter; see ?fit_control.
4662 //
4663 // Only estimated, age-based lead fleets, from their first selected bin: those cells
4664 // cannot be a structural zero, and one log(0) = -Inf on the tape turns EVERY sdreport
4665 // quantity to NaN, biomass and SSB included. The four routes to a structural zero are
4666 // enumerated in ?fit_control and all read from the data, so the reported SET never
4667 // depends on a parameter value. A reported VALUE can still underflow -- a narrow dome
4668 // puts exp(-x^2/2) below 1e-308 many bins from its peak -- so the log is floored with
4669 // max2(), exact above the floor. log_sel_at_age_index carries each row's fleet, sex,
4670 // absolute age and calendar year, since which cells are reported depends on the model.
4671 if(adreport_sel == 1){
4672 // First bin and bin count per fleet. data_check() bounds Bin_first_selected
4673 // by nages, but a 0 in the workbook still arrives here as -1, and the model
4674 // builds safebounds = FALSE, so both ends are clamped before indexing.
4675 vector<int> sel_first_bin(n_flt); sel_first_bin.setZero();
4676 vector<int> sel_rep_bins(n_flt); sel_rep_bins.setZero();
4677 for(int flt = 0; flt < n_flt; flt++){
4678 if((flt_type(flt) <= 0) || (flt_sel_lead(flt) != 1) ||
4679 (flt_sel_type(flt) == 0) || (flt_sel_dim(flt) == 1)) continue;
4680 int n_age = nages(flt_spp(flt));
4681 int first = bin_first_selected(flt);
4682 if(first < 0) first = 0;
4683 if(first > n_age) first = n_age;
4684 sel_first_bin(flt) = first;
4685 sel_rep_bins(flt) = n_age - first;
4686 }
4687
4688 int n_sel = 0;
4689 for(int flt = 0; flt < n_flt; flt++){
4690 n_sel += nsex(flt_spp(flt)) * sel_rep_bins(flt) * nyrs_hind;
4691 }
4692
4693 vector<Type> log_sel_at_age(n_sel); log_sel_at_age.setZero();
4694 matrix<Type> log_sel_at_age_index(n_sel, 4); log_sel_at_age_index.setZero();
4695
4696 int i_sel = 0;
4697 for(int flt = 0; flt < n_flt; flt++){
4698 if(sel_rep_bins(flt) == 0) continue;
4699 int sel_sp = flt_spp(flt);
4700 for(int sel_sex = 0; sel_sex < nsex(sel_sp); sel_sex++){
4701 for(int sel_age = sel_first_bin(flt); sel_age < nages(sel_sp); sel_age++){
4702 for(int sel_yr = 0; sel_yr < nyrs_hind; sel_yr++){
4703 log_sel_at_age(i_sel) = log(max2(sel_at_age(flt, sel_sex, sel_age, sel_yr),
4704 Type(1e-300)));
4705 log_sel_at_age_index(i_sel, 0) = Type(flt + 1);
4706 log_sel_at_age_index(i_sel, 1) = Type(sel_sex + 1);
4707 log_sel_at_age_index(i_sel, 2) = Type(minage(sel_sp) + sel_age);
4708 log_sel_at_age_index(i_sel, 3) = Type(styr + sel_yr);
4709 i_sel++;
4710 }
4711 }
4712 }
4713 }
4714
4715 REPORT( log_sel_at_age );
4716 REPORT( log_sel_at_age_index );
4717 ADREPORT( log_sel_at_age );
4718 }
4719
4720
4721 // -- 14.2. Biological reference points
4722 REPORT( NByageF );
4723 REPORT( NByage0);
4724 REPORT( NbyageSPR);
4725 REPORT( B0 );
4726 REPORT( SB0 );
4727 REPORT( SBF );
4728 REPORT( DynamicB0 );
4729 REPORT( DynamicSB0 );
4730 REPORT( DynamicSBF );
4731 REPORT( SPR0 );
4732 REPORT( SPRFinit );
4733 REPORT( SPRlimit );
4734 REPORT( SPRtarget );
4735 REPORT( steepness );
4736 REPORT( proj_F );
4737 REPORT( Ftarget );
4738 REPORT( Flimit );
4739
4740 // -- 14.3. Selectivity
4741 REPORT( sel_at_age );
4742 REPORT( sel_at_length );
4743
4744
4745 // -- 14.4. Survey components
4746 REPORT( index_hat );
4747 REPORT( index_sd );
4748 REPORT( index_q );
4749 vector<Type> log_index_hat = index_hat; log_index_hat = log(index_hat.array());// Log predicted index
4750 REPORT( log_index_hat );
4751 ADREPORT( log_index_hat );
4752
4753 // -- 14.4b. Formula-linkage effect sizes: the estimated coefficients, the random-effect
4754 // deviations and the Rogers (2024) QAR1 effect size, so a user can read the *effect*
4755 // and, through ADREPORT, its uncertainty -- not just the realized parameter (index_q
4756 // above). All may be length 0 (no linkages), in which case they report nothing.
4757 REPORT( beta_linkage );
4758 ADREPORT( beta_linkage );
4759 REPORT( beta_linkage_re );
4760 REPORT( beta_linkage_re_pen );
4761 // Slot-ordered deviations across both vectors, so a caller can read a group's
4762 // walk without knowing how it is stored.
4763 REPORT( beta_linkage_re_all );
4764 REPORT( beta_linkage_obs );
4765 ADREPORT( beta_linkage_obs );
4766
4767
4768 // -- 14.5. Fishery components
4769 REPORT( F_spp );
4770 REPORT( F_flt );
4771 REPORT( F_flt_age );
4772 REPORT( F_at_age );
4773 REPORT( catch_hat );
4774 REPORT( max_catch_hat );
4775 REPORT( catch_sd );
4776 REPORT( catch_analytical_sd );
4777
4778
4779 // 14.6. Age/length composition
4780 REPORT( age_obs_hat );
4781 REPORT( age_hat );
4782 REPORT( comp_obs );
4783 REPORT( comp_hat );
4784 REPORT( caal_hat );
4785 REPORT( equil_catch_hat );
4786 REPORT( caal_obs );
4787 REPORT( pred_CAAL );
4788
4789
4790 // -- 14.7. Likelihood components
4791 REPORT( jnll_comp );
4792 REPORT( unweighted_jnll_comp );
4793
4794
4795 // -- 14.8. Ration components
4796 REPORT( consumption_at_age );
4797 REPORT( fT );
4798
4799 // -- 14.9. Predation components
4800 REPORT( M2_prop );
4801 REPORT( diet_obs );
4802 REPORT( suitability );
4803 REPORT( M1_at_age );
4804 REPORT( M2_at_age );
4805 REPORT( B_eaten );
4806 REPORT( B_eaten_as_prey );
4807 // ADREPORT( B_eaten_as_prey ); // no SEs: see ?plot_b_eaten_prop
4808 REPORT( vulnerability );
4809 REPORT( vulnerability_other );
4810 REPORT( gam_a );
4811 REPORT( gam_b );
4812 REPORT( diet_hat );
4813 REPORT( suit_other );
4814 REPORT( stom_div_bio );
4815 REPORT( avail_food );
4816
4817
4818 // -- 14.10. Initial-state cumulative mortality (feeds the initial age structure)
4819 REPORT(mort_sum);
4820
4821 /** ------------------------------------------------------------------------ //
4822 // 15. END MODEL //
4823 * ------------------------------------------------------------------------- */
4824
4825 Type jnll = 0;
4826
4827 // Modes 0-3 return the real objective. Mode 3 builds the TMB object without
4828 // running the optimizer, so obj$fn()/obj$gr() are usable for diagnostics.
4829 if(estimateMode < 4) {
4830 jnll = jnll_comp.sum();
4831 }
4832
4833 // Mode 4: build_map() maps out every hindcast parameter, leaving `dummy` as
4834 // the only free parameter. Placeholder objective so nlminb has something
4835 // valid to minimize -- a plumbing smoke test, not a likelihood.
4836 if(estimateMode > 3) {
4837 jnll = dummy * dummy;
4838 }
4839
4840
4841 REPORT( jnll );
4842 return jnll;
4843}
4844
void calculate_temperature_function(matrix< Type > &fT, int nspp, int nyrs, const vector< int > &Ceq, const vector< int > &Cindex, const vector< Type > &Qc, const vector< Type > &Tcm, const vector< Type > &Tco, const vector< Type > &Tcl, const vector< Type > &CK1, const vector< Type > &CK4, const matrix< Type > &env_index)
Temperature-dependent function of consumption (fT) for bioenergetics.
void calculate_ration(array< Type > &consumption_at_age, int nspp, int nyrs, int nyrs_hind, const vector< int > &nsex, const vector< int > &nages, const vector< int > &Ceq, const vector< Type > &CA, const vector< Type > &CB, const vector< Type > &fday, const vector< Type > &Pvalue, matrix< Type > &fT, array< Type > &ration_data, array< Type > &weight_hat)
Annual ration of a predator at age, in kg per predator per year.
One-step-ahead (OSA) residual helpers for composition data.
Type ddirmultinom_osa(vector< Type > obs, vector< Type > alpha, data_indicator< vector< Type >, Type > keep, int do_osa, int give_log)
Keep-aware Dirichlet-multinomial log-density via conditional beta-binomials.
Definition comp_osa.hpp:252
Type dmultinom_osa(vector< Type > x, vector< Type > p, data_indicator< vector< Type >, Type > keep, int do_osa, int give_log, int do_cdf=0)
Keep-aware multinomial log-density via conditional binomials.
Definition comp_osa.hpp:159
Type osa_norm_cdf_terms(Type x, Type mu, Type sd, Type cdf_lower, Type cdf_upper)
One-step-ahead CDF terms for a normal observation.
Definition comp_osa.hpp:117
Type osa_trunc_norm_cdf_terms(Type x, Type mu, Type sd, Type cdf_lower, Type cdf_upper)
One-step-ahead CDF terms for a normal left-truncated at zero.
Definition comp_osa.hpp:139
Random draws for composition-style observations, used by SIMULATE.
vector< Type > rdirmultinom_rce(Type N, vector< Type > alpha)
Draw Dirichlet-multinomial counts.
Definition comp_sim.hpp:128
vector< Type > rmultinom_rce(Type N, vector< Type > p)
Draw multinomial counts by sequential conditional binomials.
Definition comp_sim.hpp:41
void organize_diet_obs(const int &nspp, const int &nyrs, const int &nyrs_hind, const int &styr, const vector< int > &minage, const vector< int > &nsex, matrix< Type > &diet_obs, matrix< int > &diet_ctl, array< Type > &diet_prop)
Parses empirical diet observations and maps them into a 5D proportion array.
Definition diet_data.hpp:24
void predict_stomach_content(const int &nspp, const int &nyrs_hind, const int &styr, const vector< int > &suit_styr, const vector< int > &suit_endyr, const vector< int > &minage, const vector< int > &nsex, const vector< int > &nages, matrix< Type > &diet_obs, matrix< int > &diet_ctl, array< Type > &avgN_at_age, array< Type > &diet_prop_hat, matrix< Type > &diet_hat)
Expected stomach content proportions given the modeled population state.
void calculate_other_food_diet_prop(const int &nspp, const int &nyrs, const vector< int > &nsex, const vector< int > &nages, const vector< Type > &other_food, array< Type > &diet_prop, array< Type > &other_food_diet_prop)
Residual stomach content: the "other food" not explicitly modeled.
Definition diet_data.hpp:97
void calculate_weight(array< Type > &weight_hat, array< Type > &length_hat, array< Type > &growth_matrix, array< Type > &mat_weight_hat, array< Type > &weight_obs, const vector< int > &growth_model, const vector< int > &growth_sd_style, const vector< int > &growth_sd_form, const vector< int > &growth_plus_length, const vector< Type > &plus_group_decay, const int &nspp, const int &nyrs, const int &nyrs_hind, const int &n_flt, const vector< int > &flt_spp, vector< Type > flt_month, const vector< int > &nsex, const vector< int > &minage, const vector< Type > &growth_age_L1, const vector< int > &nages, const vector< int > &nlengths, const vector< int > &pop_wt_index, const vector< int > &ssb_wt_index, const vector< int > &flt_wt_index, vector< Type > spawn_month, matrix< Type > &lengths, const vector< int > &nlengths_pop, matrix< Type > &lengths_pop, array< Type > &growth_parameters, array< Type > &growth_log_sd, matrix< Type > weight_length_pars, const vector< int > &mat_len_use, matrix< Type > &mat_len_pars, array< Type > &log_M1)
Population, SSB and fleet weight-at-age (kg) for every species and fleet.
Definition growth.hpp:498
Small differentiable (AD-safe) utilities used across the CEATTLE template: numeric guards and transfo...
Type concentrated_lognormal_sd(Type mean_sq_resid, Type bias_adjust)
Concentrated (analytical) sd of a lognormal observation likelihood.
bool isNA(Type x)
Type rho_trans(Type x)
Eigen::SparseMatrix< Type > construct_Q(int n_years, int n_ages, matrix< Type > ay_Index, Type rho_y, Type rho_a, Type rho_c, Type log_sigma2, int Var_Param)
Construct a sparse GMRF precision matrix separable along year, age, and cohort.
Type log_mean_exp(const vector< Type > &x)
log(mean(exp(x))) over a non-empty vector, computed so it cannot overflow.
Type imax(const vector< Type > &x)
Type max2(Type x, Type y)
Type ddirmultinom(vector< Type > obs, vector< Type > alpha, int do_log)
Type square(Type x)
Type posfun(Type x, Type eps, Type &penalty)
vector< Type > first_difference(const vector< Type > &x)
Likelihood math for section 13 of ceattle.cpp: the composition row density and linkage prior families...
Type linkage_log_prior(int family, Type value, bool value_is_log, Type p1, Type p2, Type bias_adjust_proc)
Log density of a linkage prior on the natural scale.
void fold_comp_tails(vector< Type > &obs, vector< Type > &hat, int &n_comp, int nbins_blk, int nblk, int yng, int old)
Composition tail accumulation (AFSC ac_yng / ac_old).
void comp_row_nll(int ll_type, int osa_mode, vector< Type > obs_prop, vector< Type > hat_prop, Type offset, Type n_samp, Type weight, Type dm_theta, vector< Type > &obsvec, data_indicator< vector< Type >, Type > &keep, int osa_start, const char *type_name, Type &nll, Type &unweighted_nll)
Negative log-likelihood of one composition row: an age or length composition, or one length bin of co...
#define RCEATTLE_SEL_DN_PEAK
Definition linkage.hpp:61
#define RCEATTLE_N_REC_PARAMS
Definition linkage.hpp:35
#define RCEATTLE_SEL_APICAL
Definition linkage.hpp:60
#define RCEATTLE_COMP_THETA_DIET
Definition linkage.hpp:71
#define RCEATTLE_REC_BETA
Definition linkage.hpp:38
#define RCEATTLE_SEL_END_LOGIT
Definition linkage.hpp:66
#define RCEATTLE_PROC_RECRUIT
Definition linkage.hpp:22
void rceattle_apply_q_linkages(matrix< Type > &q_offset, int link_code, const LinkageTable< Type > &lt, int n_flt, int nyrs)
Add catchability offsets to q_offset[fleet, yr].
Definition linkage.hpp:280
#define RCEATTLE_REC_R_INIT
Definition linkage.hpp:39
#define RCEATTLE_PROC_GROWTH
Definition linkage.hpp:24
#define RCEATTLE_N_GROWTH_PARAMS
Definition linkage.hpp:31
#define RCEATTLE_REC_ALPHA
Definition linkage.hpp:37
#define RCEATTLE_RE_AR1
Definition linkage.hpp:51
void rceattle_apply_sel_linkages(array< Type > &slp_offset, array< Type > &inf_offset, array< Type > &coff_offset, array< Type > &apical_offset, array< Type > &dn6_offset, int link_code, const LinkageTable< Type > &lt, int n_flt, int max_sex, int n_sel_bins, int nyrs)
Add selectivity offsets, by fleet, sex and year, to one of five arrays.
Definition linkage.hpp:328
void rceattle_apply_recruitment_linkages(array< Type > &rec_offset, int link_code, const LinkageTable< Type > &lt, int nspp, int nyrs)
Add recruitment offsets to rec_offset[sp, param, yr].
Definition linkage.hpp:236
#define RCEATTLE_COMP_THETA_COMP
Definition linkage.hpp:69
#define RCEATTLE_LINK_IDENTITY
Definition linkage.hpp:42
#define RCEATTLE_RE_RW
Definition linkage.hpp:50
#define RCEATTLE_PROC_Q
Definition linkage.hpp:25
void rceattle_apply_growth_linkages(array< Type > &growth_offset, int link_code, const LinkageTable< Type > &lt, int nspp, const vector< int > &nsex, int nyrs)
Add growth offsets to growth_offset[sp, sex, yr, param].
Definition linkage.hpp:136
#define RCEATTLE_SEL_SLP_ASC
Definition linkage.hpp:55
void rceattle_re_group_nll(int structure, vector< Type > re, Type sigma, Type rho, Type &nll, Type &nll_unweighted)
Add the negative log density of one group's deviations to two accumulators.
Definition linkage.hpp:414
#define RCEATTLE_PROC_COMP
Definition linkage.hpp:27
void rceattle_apply_M_linkages(array< Type > &M_offset, int link_code, const LinkageTable< Type > &lt, int nspp, const vector< int > &nsex, const vector< int > &nages, int nyrs)
Add natural-mortality offsets to M_offset[sp, sex, age, yr].
Definition linkage.hpp:185
#define RCEATTLE_REC_R0
Definition linkage.hpp:36
#define RCEATTLE_LINK_LOG
Definition linkage.hpp:43
#define RCEATTLE_SEL_INF_DESC
Definition linkage.hpp:58
vector< int > rceattle_group_slots(const vector< int > &group_of_slot, int grp)
The random-effect slots of one group, in slot order, which is time order within a group (....
Definition linkage.hpp:389
#define RCEATTLE_PROC_M
Definition linkage.hpp:23
#define RCEATTLE_SEL_SLP_DESC
Definition linkage.hpp:56
#define RCEATTLE_LINK_EXPONENTIAL
Definition linkage.hpp:45
#define RCEATTLE_SEL_INF_ASC
Definition linkage.hpp:57
#define RCEATTLE_COMP_THETA_CAAL
Definition linkage.hpp:70
#define RCEATTLE_PROC_SEL
Definition linkage.hpp:26
void calculate_msvpa_predation(array< Type > &avail_food, array< Type > &avail_food_dB0, array< Type > &avail_food_dBF, array< Type > &M2_at_age, array< Type > &M2_at_age_dB0, array< Type > &M2_at_age_dBF, array< Type > &M2_prop, array< Type > &B_eaten, array< Type > &B_eaten_as_prey, array< Type > &diet_prop_hat, array< Type > &other_diet_prop_hat, int nspp, int nyrs, const vector< int > &nsex, const vector< int > &nages, int msmMode, array< Type > &avgN_at_age, array< Type > &avgN_at_age_dB0, array< Type > &avgN_at_age_dBF, array< Type > &suitability, array< Type > &suit_other, array< Type > &weight_hat, array< Type > &ration, const vector< Type > &other_food)
Available food, predation mortality (M2), and consumed biomass (MSVPA).
void calculate_parametric_suitability(array< Type > &suitability, array< Type > &suit_other, int nspp, int nyrs, const vector< int > &nsex, const vector< int > &nages, const vector< int > &suitMode, array< Type > &length_hat, array< Type > &weight_hat, matrix< Type > &vulnerability, const vector< Type > &vulnerability_other, const vector< Type > &gam_a, const vector< Type > &gam_b)
Parametric suitability (Gamma, Lognormal, Normal) from size ratios.
Definition predation.hpp:76
void calculate_vulnerability(matrix< Type > &vulnerability, vector< Type > &vulnerability_other, int nspp, const vector< int > &suitMode, matrix< Type > &log_phi)
Transforms predator-prey preference parameters into vulnerability.
Definition predation.hpp:20
void calculate_msvpa_suitability(array< Type > &stom_div_bio, array< Type > &suitability, array< Type > &suit_other, int nspp, int nyrs, const vector< int > &suit_styr, const vector< int > &suit_endyr, const vector< int > &nyrs_suit, int msmMode, const vector< int > &nsex, const vector< int > &nages, const vector< int > &suitMode, array< Type > &diet_prop, array< Type > &avgN_at_age, array< Type > &weight_hat, array< Type > &other_food_diet_prop)
MSVPA/empirical suitability, from stomach content and prey biomass.
Type calculate_recruitment(int srr_fun, Type R0, Type ssb, Type srr_alpha, Type srr_beta, Type rec_dev, Type spr0)
Calculates the recruitment for a given species and year.
void calculate_selectivity(const int &nspp, const int &n_flt, const int &nyrs, const int &nyrs_hind, const int &styr, const vector< int > &nsex, const vector< int > &nages, const vector< int > &nlengths, matrix< Type > &lengths, const vector< int > &nlengths_pop, matrix< Type > &lengths_pop, const vector< int > &flt_spp, const vector< int > &flt_sel_type, const vector< int > &flt_varying_sel, const vector< int > &flt_sel_dim, const vector< int > &bin_first_selected, const vector< int > &flt_n_sel_bins, const vector< int > &flt_sel_cap_bin, const vector< int > &sel_norm_bin1, const vector< int > &sel_norm_bin2, const vector< int > &sel_norm_scope, const vector< int > &flt_sel_start_yr, matrix< Type > &emp_sel_obs, matrix< int > &emp_sel_ctl, array< Type > &log_sel_slp, array< Type > &log_sel_slp_dev, array< Type > &sel_inf, array< Type > &sel_inf_dev, array< Type > &sel_coff, array< Type > &sel_coff_dev, array< Type > &avg_sel, array< Type > &non_par_sel, array< Type > &log_non_par_sel, array< Type > &sel_at_length, array< Type > &sel_at_age, array< Type > &growth_matrix, array< Type > &sel_slp_off, array< Type > &sel_slp_off_nat, array< Type > &sel_inf_off, array< Type > &sel_inf_off_nat, array< Type > &sel_coff_off, array< Type > &sel_coff_off_nat, const int &sel_apical_on, array< Type > &log_sel_apical, array< Type > &sel_apical_off, array< Type > &sel_apical_off_nat, array< Type > &sel_dn6, array< Type > &sel_dn6_off, array< Type > &sel_dn6_off_nat, matrix< int > &sel_dn6_ends)
Calculates selectivity for every fleet, sex, bin (age or length) and year.
Spawning biomass per recruit, for the Amendment-56 SPR proxies.
Type spawning_biomass_per_recruit(const vector< Type > &n, const vector< Type > &Z, const vector< Type > &weight, const vector< Type > &mature_female, Type spawn_month)
Female spawning biomass per recruit, given a survivor schedule.
Definition spr.hpp:69
vector< Type > per_recruit_survivors(const vector< Type > &Z)
Survivors at age from one recruit under a fixed mortality schedule.
Definition spr.hpp:38
LOM_t(SEXP x)
Definition ceattle.cpp:45
The linkage table: one entry per row, and the shared design matrix.
Definition linkage.hpp:96