Rceattle C++ template
The CEATTLE likelihood: src/TMB/ceattle.cpp and its headers
Loading...
Searching...
No Matches
selectivity.hpp
Go to the documentation of this file.
1#ifndef SELECTIVITY_HPP
2#define SELECTIVITY_HPP
3
4/**
5 * @brief Zeroes, normalizes and projects ONE fleet's selectivity (age or length based).
6 *
7 * @details Normalization never applies to Hake (5), LogisticPM (11) or DoubleNormalSS3
8 * (15). Under Sel_norm_scope = AcrossSexes the less-selected sex peaks below 1.
9 *
10 * @param sel_norm_bin1 Normalization bin, 0-based; -99 = the maximum, -999 = off.
11 * @param sel_norm_bin2 Upper bin for the mean over a range; negative = no range.
12 * @param sel_norm_scope 0 = WithinSex (a reference per sex), 1 = AcrossSexes (pooled).
13 * @param selectivity [fleet, sex, age or length bin, year], modified in place.
14 */
15template<class Type>
17 const int& flt,
18 const int& nyrs_hind,
19 const int& nyrs,
20 const vector<int>& flt_spp,
21 const vector<int>& flt_sel_type,
22 const vector<int>& flt_sel_dim,
23 const vector<int>& bin_first_selected,
24 const vector<int>& nages,
25 const vector<int>& nlengths_pop,
26 const vector<int>& nsex,
27 const vector<int>& sel_norm_bin1,
28 const vector<int>& sel_norm_bin2,
29 const vector<int>& sel_norm_scope,
30 array<Type> &selectivity
31) {
32 int sp = flt_spp(flt);
33 int sel_type = flt_sel_type(flt);
34 int nbins = (flt_sel_dim(flt) == 0) ? nages(sp) : nlengths_pop(sp);
35
36 // 1. Bins below the first selected bin are not selected
37 for (int yr = 0; yr < nyrs_hind; yr++) {
38 for (int sex = 0; sex < nsex(sp); sex++) {
39 for (int bin = 0; bin < nbins && bin < bin_first_selected(flt); bin++) {
40 selectivity(flt, sex, bin, yr) = 0;
41 }
42 }
43 }
44
45 // 2. Normalization. Hake scales itself per sex, LogisticPM reads Sel_norm_bin as a
46 // penalty range, and DoubleNormalSS3 peaks at 1. Codes 10, 12 and 14 are unused.
47 bool normalize = (sel_norm_bin1(flt) > -500) && (sel_type != 5) && (sel_type != 12)
48 && (sel_type != 11) && (sel_type != 15);
49 if (normalize) {
50 bool at_bin = (sel_norm_bin1(flt) >= 0);
51 bool over_range = at_bin && (sel_norm_bin2(flt) >= 0);
52 bool across = (sel_norm_scope(flt) == 1);
53 vector<Type> ref(nsex(sp));
54
55 for (int yr = 0; yr < nyrs_hind; yr++) {
56 for (int sex = 0; sex < nsex(sp); sex++) {
57 if (over_range) {
58 int n_range = sel_norm_bin2(flt) - sel_norm_bin1(flt) + 1;
59 ref(sex) = 0;
60 for (int bin = sel_norm_bin1(flt); bin <= sel_norm_bin2(flt); bin++) {
61 ref(sex) += selectivity(flt, sex, bin, yr) / n_range;
62 }
63 } else if (at_bin) {
64 // One bin, floored so the division stays finite
65 ref(sex) = max2(Type(0.001), selectivity(flt, sex, sel_norm_bin1(flt), yr));
66 } else {
67 // max2(), not an `if`: TMB records the model once, so an `if` would freeze
68 // which bin is the maximum.
69 ref(sex) = 0;
70 for (int bin = 0; bin < nbins; bin++) {
71 ref(sex) = max2(ref(sex), selectivity(flt, sex, bin, yr));
72 }
73 }
74 }
75
76 if (across) {
77 Type pooled = ref(0);
78 for (int sex = 1; sex < nsex(sp); sex++) pooled = max2(pooled, ref(sex));
79 for (int sex = 0; sex < nsex(sp); sex++) ref(sex) = pooled;
80 }
81
82 for (int bin = 0; bin < nbins; bin++) {
83 for (int sex = 0; sex < nsex(sp); sex++) {
84 selectivity(flt, sex, bin, yr) /= ref(sex);
85 }
86 }
87 }
88 }
89
90 // 3. Projection years repeat the last hindcast year
91 for (int yr = nyrs_hind; yr < nyrs; yr++) {
92 for (int sex = 0; sex < nsex(sp); sex++) {
93 for (int bin = 0; bin < nbins; bin++) {
94 selectivity(flt, sex, bin, yr) = selectivity(flt, sex, bin, nyrs_hind - 1);
95 }
96 }
97 }
98}
99
100
101/**
102 * @brief Converts one fleet/sex/year of length-based selectivity to ages: the sum over
103 * population length bins of selectivity-at-length x Prob(length | age).
104 */
105template <class Type>
107 const int& flt,
108 const int& sp,
109 const int& sex,
110 const int& yr,
111 const int& wtind,
112 const vector<int>& nages,
113 const vector<int>& nlengths_pop,
114 array<Type>& sel_length,
115 array<Type>& growth_matrix,
116 array<Type>& sel_at_age
117) {
118 for (int age = 0; age < nages(sp); age++) {
119 sel_at_age(flt, sex, age, yr) = 0.0;
120 for (int lp = 0; lp < nlengths_pop(sp); lp++) {
121 sel_at_age(flt, sex, age, yr) += growth_matrix(wtind, sex, age, lp, yr) * sel_length(flt, sex, lp, yr);
122 }
123 }
124}
125
126
127/**
128 * @brief Calculates selectivity for every fleet, sex, bin (age or length) and year.
129 *
130 * @details An age-based curve is evaluated at the bin number bin + 1, without minage,
131 * so an inflection or peak is on the bin scale; a length-based one at bin midpoints (cm).
132 *
133 * Forms (fleet_control$Selectivity; R/0-switches.R is the authority):
134 * - 0 Fixed: read from emp_sel_obs; age-based.
135 * - 1 Logistic.
136 * - 2 NonParametric (Ianelli et al. 2018, AMAK): one log coefficient per bin, mean-centred.
137 * - 3 DoubleLogistic (Dorn and Methot 1990).
138 * - 4 DescendingLogistic.
139 * - 5 Hake (Taylor et al. 2014): exp of the cumulative sum of coefficients.
140 * - 6 2DAR1: inverse logit of coefficients with AR1 deviates by bin and year.
141 * - 7 3DAR1 (Cheng et al. 2024): as 6, with a cohort term.
142 * - 8 DoubleNormal: Gaussian dome with a right-tail floor.
143 * - 9 NonParametricPM (AMAK "pm"): random walk on the log curve, capped at Sel_cap_bin.
144 * - 11 LogisticPM (AMAK "pm" bottom-trawl survey): multiplicative deviates, free first bin.
145 * - 13 NonParametricIntegrable: form 2 with deviates random_sel can integrate.
146 * - 15 DoubleNormalSS3: Stock Synthesis size pattern 24 (age pattern 20).
147 *
148 * Which linkage offsets a form accepts is checked in R, by .check_sel_linkage_support().
149 */
150template<class Type>
152 const int& nspp,
153 const int& n_flt,
154 const int& nyrs,
155 const int& nyrs_hind,
156 const int& styr,
157 const vector<int>& nsex,
158 const vector<int>& nages,
159 const vector<int>& nlengths,
160 matrix<Type>& lengths,
161 const vector<int>& nlengths_pop,
162 matrix<Type>& lengths_pop,
163 const vector<int>& flt_spp,
164 const vector<int>& flt_sel_type,
165 const vector<int>& flt_varying_sel,
166 const vector<int>& flt_sel_dim,
167 const vector<int>& bin_first_selected,
168 const vector<int>& flt_n_sel_bins,
169 const vector<int>& flt_sel_cap_bin,
170 const vector<int>& sel_norm_bin1,
171 const vector<int>& sel_norm_bin2,
172 const vector<int>& sel_norm_scope,
173 const vector<int>& flt_sel_start_yr,
174 matrix<Type>& emp_sel_obs,
175 matrix<int>& emp_sel_ctl,
176 array<Type>& log_sel_slp,
177 array<Type>& log_sel_slp_dev,
178 array<Type>& sel_inf,
179 array<Type>& sel_inf_dev,
180 array<Type>& sel_coff,
181 array<Type>& sel_coff_dev,
182 array<Type> &avg_sel,
183 array<Type> &non_par_sel,
184 array<Type> &log_non_par_sel,
185 array<Type> &sel_at_length,
186 array<Type> &sel_at_age,
187 array<Type>& growth_matrix,
188 // Linkage offsets: log link, and identity link (_nat). The coefficient ones are unread.
189 array<Type>& sel_slp_off, array<Type>& sel_slp_off_nat,
190 array<Type>& sel_inf_off, array<Type>& sel_inf_off_nat,
191 array<Type>& sel_coff_off, array<Type>& sel_coff_off_nat,
192 const int& sel_apical_on,
193 array<Type>& log_sel_apical, // [n_flt, max_sex]
194 array<Type>& sel_apical_off, array<Type>& sel_apical_off_nat, // [n_flt, max_sex, nyrs]
195 array<Type>& sel_dn6, // [6, n_flt, max_sex]
196 array<Type>& sel_dn6_off, array<Type>& sel_dn6_off_nat, // [6, n_flt, max_sex, nyrs]
197 matrix<int>& sel_dn6_ends // [n_flt, 2]: SS3 initial/final selectivity used (1) or -999 (0)
198) {
199 sel_at_age.setZero();
200 sel_at_length.setZero();
201
202 // --- 1. FIXED SELECTIVITY (code 0) ---
203 for (int i = 0; i < emp_sel_obs.rows(); i++) {
204 int flt = emp_sel_ctl(i, 0) - 1;
205 int sp = emp_sel_ctl(i, 1) - 1;
206 int sex_in = emp_sel_ctl(i, 2); // 0 = every sex
207 int yr = emp_sel_ctl(i, 3) - styr;
208 if (flt_sel_type(flt) != 0 || yr >= nyrs_hind) continue;
209
210 int sex_first = (sex_in == 0) ? 0 : sex_in - 1;
211 int sex_last = (sex_in == 0 && nsex(sp) == 2) ? 1 : sex_first;
212 for (int sex = sex_first; sex <= sex_last; sex++) {
213 for (int age = 0; age < nages(sp); age++) {
214 if (!isNA(emp_sel_obs(i, age))) sel_at_age(flt, sex, age, yr) = emp_sel_obs(i, age);
215 }
216 }
217 }
218
219 // --- 2. ESTIMATED SELECTIVITY ---
220 for (int flt = 0; flt < n_flt; flt++) {
221 int sp = flt_spp(flt);
222 int sel_type = flt_sel_type(flt);
223 if (sel_type == 0) continue;
224
225 // Form 13 is built differently under Time_varying_sel = "RandomWalk" (4). The
226 // negative label is internal, not a Selectivity code.
227 const int SEL_CASE_NP_INTEGRABLE_WALK = -13;
228 int sel_case = (sel_type == 13 && flt_varying_sel(flt) == 4)
229 ? SEL_CASE_NP_INTEGRABLE_WALK : sel_type;
230
231 // A length-based curve runs over the POPULATION length bins. `sel` is that array
232 // itself, not a copy.
233 bool is_length_based = flt_sel_dim(flt) == 1;
234 array<Type>& sel = is_length_based ? sel_at_length : sel_at_age;
235 int nbins = is_length_based ? nlengths_pop(sp) : nages(sp);
236 int n_sel_bins = flt_n_sel_bins(flt);
237
238 // x-axis: bin + 1 by age; by length, each bin's midpoint from its own width
239 // (SS3's len_bins_m), since SS3 grids often widen the tail bins.
240 vector<Type> x(nbins);
241 for (int b = 0; b < nbins; b++) {
242 if (!is_length_based) {
243 x(b) = Type(b + 1);
244 } else if (nbins < 2) {
245 x(b) = lengths_pop(sp, b);
246 } else {
247 Type w = (b + 1 < nbins) ? (lengths_pop(sp, b + 1) - lengths_pop(sp, b))
248 : (lengths_pop(sp, b) - lengths_pop(sp, b - 1));
249 x(b) = lengths_pop(sp, b) + 0.5 * w;
250 }
251 }
252 // SS3's binwidth2 (SS_readdata_330.tpl:1644), read only by DoubleNormalSS3's peak2
253 // (SS_selex.tpl:153).
254 Type binwidth = (is_length_based && nbins > 1)
255 ? (lengths_pop(sp, nbins / 2) - lengths_pop(sp, nbins / 2 - 1)) : Type(1.0);
256
257 // Random-walk state for forms 9 and 13, carried across years.
258 array<Type> log_walk(nsex(sp), nbins, nyrs_hind); log_walk.setZero();
259
260 for (int yr = 0; yr < nyrs_hind; yr++) {
261 for (int sex = 0; sex < nsex(sp); sex++) {
262
263 // Local functions: slope and inflection of logistic slot k (0 = ascending, 1 =
264 // descending), base + deviate + linkage offsets (log link multiplies, identity adds).
265 auto slope = [&](int k) -> Type {
266 return exp(log_sel_slp(k, flt, sex) + log_sel_slp_dev(k, flt, sex, yr)
267 + sel_slp_off(k, flt, sex, yr)) + sel_slp_off_nat(k, flt, sex, yr);
268 };
269 auto inflection = [&](int k) -> Type {
270 return (sel_inf(k, flt, sex) + sel_inf_dev(k, flt, sex, yr)
271 + sel_inf_off_nat(k, flt, sex, yr)) * exp(sel_inf_off(k, flt, sex, yr));
272 };
273
274 switch (sel_case) {
275
276 case 1: { // Logistic
277 Type slp = slope(0);
278 Type inf = inflection(0);
279 for (int bin = 0; bin < nbins; bin++) {
280 sel(flt, sex, bin, yr) = invlogit(slp * (x(bin) - inf));
281 }
282 break;
283 }
284
285 case 2: // NonParametric
286 case 13: // NonParametricIntegrable, "Off" or "IID"
287 case SEL_CASE_NP_INTEGRABLE_WALK: { // NonParametricIntegrable, "RandomWalk"
288 // Log coefficients: base + this year's deviate, or + the running sum of
289 // increments after the start year under the random walk.
290 vector<Type> coef(n_sel_bins);
291 for (int bin = 0; bin < n_sel_bins; bin++) {
292 if (sel_case == SEL_CASE_NP_INTEGRABLE_WALK) {
293 Type inc = (yr > flt_sel_start_yr(flt)) ? sel_coff_dev(flt, sex, bin, yr) : Type(0.0);
294 log_walk(sex, bin, yr) = (yr > 0 ? log_walk(sex, bin, yr - 1) : Type(0.0)) + inc;
295 coef(bin) = sel_coff(flt, sex, bin) + log_walk(sex, bin, yr);
296 } else {
297 coef(bin) = sel_coff(flt, sex, bin) + sel_coff_dev(flt, sex, bin, yr);
298 }
299 }
300 // AMAK avgsel: the log mean that centring removes. Form 2 penalizes it
301 // (ceattle.cpp, JNLL_SEL_NONPARAM).
302 avg_sel(flt, sex, yr) = log_mean_exp(coef);
303
304 // Plateau past N_sel_bins, then centre so mean selectivity is 1.
305 vector<Type> log_curve(nbins);
306 for (int bin = 0; bin < nbins; bin++) {
307 int b = (bin < n_sel_bins) ? bin : (n_sel_bins - 1);
308 log_curve(bin) = coef(b);
309 }
310 Type centre = log_mean_exp(log_curve);
311 for (int bin = 0; bin < nbins; bin++) {
312 log_non_par_sel(flt, sex, bin, yr) = log_curve(bin) - centre;
313 non_par_sel(flt, sex, bin, yr) = exp(log_non_par_sel(flt, sex, bin, yr));
314 sel(flt, sex, bin, yr) = non_par_sel(flt, sex, bin, yr);
315 }
316 break;
317 }
318
319 case 3: { // DoubleLogistic
320 // Descending limb as a negative-slope logistic, not 1 minus a logistic, so the
321 // oldest ages keep a positive, accurate value on a steep slope.
322 Type slp1 = slope(0);
323 Type inf1 = inflection(0);
324 Type slp2 = slope(1);
325 Type inf2 = inflection(1);
326 for (int bin = 0; bin < nbins; bin++) {
327 sel(flt, sex, bin, yr) = invlogit(slp1 * (x(bin) - inf1)) * invlogit(-slp2 * (x(bin) - inf2));
328 }
329 break;
330 }
331
332 case 4: { // DescendingLogistic
333 Type slp2 = slope(1);
334 Type inf2 = inflection(1);
335 for (int bin = 0; bin < nbins; bin++) {
336 sel(flt, sex, bin, yr) = invlogit(-slp2 * (x(bin) - inf2));
337 }
338 break;
339 }
340
341 case 5: { // Hake: log selectivity is the cumulative sum from bin_first_selected
342 Type cum_sum = 0;
343 for (int bin = bin_first_selected(flt); bin < nbins; bin++) {
344 if (bin < n_sel_bins) cum_sum += sel_coff(flt, sex, bin) + sel_coff_dev(flt, sex, bin, yr);
345 sel(flt, sex, bin, yr) = cum_sum;
346 }
347 // Scaled to 1 per sex here, so Sel_norm_scope is inert on Hake (issue #236).
348 Type log_ref = -1e10;
349 if (sel_norm_bin1(flt) >= 0 && sel_norm_bin2(flt) < 0) {
350 log_ref = sel(flt, sex, sel_norm_bin1(flt), yr);
351 } else {
352 for (int bin = 0; bin < nbins; bin++) log_ref = max2(log_ref, sel(flt, sex, bin, yr));
353 }
354 for (int bin = 0; bin < nbins; bin++) {
355 sel(flt, sex, bin, yr) = exp(sel(flt, sex, bin, yr) - log_ref);
356 }
357 break;
358 }
359
360 case 6: // 2DAR1
361 case 7: // 3DAR1
362 for (int bin = 0; bin < nbins; bin++) {
363 int b = (bin < n_sel_bins) ? bin : (n_sel_bins - 1); // plateau past N_sel_bins
364 sel(flt, sex, bin, yr) = invlogit(sel_coff(flt, sex, b) + sel_coff_dev(flt, sex, b, yr));
365 }
366 break;
367
368 case 8: { // DoubleNormal
369 // Slots: sel_inf(0) peak, log_sel_slp(0)/(1) log SDs of the two limbs, sel_inf(1)
370 // logit of the right-tail floor (SS3's P6; a floor of 0 is a full dome, 1 a logistic).
371 Type peak = inflection(0);
372 Type sigma_asc = slope(0);
373 Type sigma_desc = slope(1);
374 Type right_floor = invlogit(inflection(1));
375 for (int bin = 0; bin < nbins; bin++) {
376 Type d = x(bin) - peak;
377 Type w = invlogit(20.0 * d); // smooth switch between the limbs
378 Type asc_gauss = exp(-0.5 * pow(d / sigma_asc, 2.0));
379 Type desc_gauss = exp(-0.5 * pow(d / sigma_desc, 2.0));
380 sel(flt, sex, bin, yr) = (1.0 - w) * asc_gauss
381 + w * (right_floor + (1.0 - right_floor) * desc_gauss);
382 }
383 break;
384 }
385
386 case 9: { // NonParametricPM
387 // Random walk on the log curve; it restarts from the base sel_coff at or before
388 // the start year, as AMAK sets a survey's base selectivity once.
389 bool from_base = (yr <= flt_sel_start_yr(flt));
390 for (int bin = 0; bin < nbins; bin++) {
391 if (bin < n_sel_bins) {
392 Type prev = from_base ? sel_coff(flt, sex, bin) : log_walk(sex, bin, yr - 1);
393 log_walk(sex, bin, yr) = prev + sel_coff_dev(flt, sex, bin, yr);
394 } else {
395 log_walk(sex, bin, yr) = log_walk(sex, n_sel_bins - 1, yr);
396 }
397 }
398 // Unselected bins are held at 0 rather than walking (AMAK's fixed first-bin value).
399 for (int bin = 0; bin < bin_first_selected(flt); bin++) log_walk(sex, bin, yr) = 0.0;
400
401 vector<Type> walk(nbins);
402 for (int bin = 0; bin < nbins; bin++) walk(bin) = log_walk(sex, bin, yr);
403 Type centre = log_mean_exp(walk);
404 for (int bin = 0; bin < nbins; bin++) log_walk(sex, bin, yr) -= centre;
405
406 // The curve: the centred walk held flat above the cap bin, centred again.
407 int cap = flt_sel_cap_bin(flt);
408 vector<Type> capped(nbins);
409 for (int bin = 0; bin < nbins; bin++) {
410 capped(bin) = (cap >= 0 && bin > cap) ? log_walk(sex, cap, yr) : log_walk(sex, bin, yr);
411 }
412 Type recentre = log_mean_exp(capped);
413 for (int bin = 0; bin < nbins; bin++) {
414 log_non_par_sel(flt, sex, bin, yr) = capped(bin) - recentre;
415 non_par_sel(flt, sex, bin, yr) = exp(log_non_par_sel(flt, sex, bin, yr));
416 sel(flt, sex, bin, yr) = non_par_sel(flt, sex, bin, yr);
417 }
418 break;
419 }
420
421 case 11: { // LogisticPM
422 // AMAK evaluates at mid-age (j + 0.5), so x is bin + 1.5; the shift cannot go
423 // into a50, whose deviate is multiplicative (so not inflection(0)). Not normalized.
424 Type slp = slope(0);
425 Type inf = (sel_inf(0, flt, sex) + sel_inf_off_nat(0, flt, sex, yr))
426 * exp(sel_inf_dev(0, flt, sex, yr) + sel_inf_off(0, flt, sex, yr));
427 for (int bin = 0; bin < nbins; bin++) {
428 Type x_val = is_length_based ? x(bin) : Type(bin + 1.5);
429 sel(flt, sex, bin, yr) = invlogit(slp * (x_val - inf));
430 }
431 // Free first-bin log selectivity, held in the unused descending slot sel_inf(1).
432 Type log_s1 = (sel_inf(1, flt, sex) + sel_inf_off_nat(1, flt, sex, yr))
433 * exp(sel_inf_dev(1, flt, sex, yr) + sel_inf_off(1, flt, sex, yr));
434 sel(flt, sex, 0, yr) = exp(log_s1);
435 break;
436 }
437
438 case 15: { // DoubleNormalSS3 (SS_selex.tpl)
439 // SS3's six parameters: P1 peak, P2 top width (logit), P3/P4 limb widths (log),
440 // P5/P6 initial and final selectivity (logit). Each is (base + identity offset) x
441 // exp(log offset): an SS3 block, then annual deviates, in SS3's own order.
442 vector<Type> P(6);
443 for (int k = 0; k < 6; k++) {
444 P(k) = (sel_dn6(k, flt, sex) + sel_dn6_off_nat(k, flt, sex, yr)) * exp(sel_dn6_off(k, flt, sex, yr));
445 }
446 Type peak = P(0);
447 Type upselex = exp(P(2));
448 Type downselex = exp(P(3));
449 // startbin: first population bin at or above the first data bin
450 // (SS_readdata_330.tpl:1642-1646, used at SS_selex.tpl:142).
451 int startbin = 0;
452 if (is_length_based) {
453 while (startbin < nbins - 1 && lengths_pop(sp, startbin) < lengths(sp, 0)) startbin++;
454 }
455 Type x_first = x(startbin);
456 Type x_last = x(nbins - 1);
457 Type peak2 = peak + binwidth + (0.99 * x_last - peak - binwidth) / (1.0 + exp(-P(1)));
458 Type point1 = invlogit(P(4));
459 Type point2 = invlogit(P(5));
460 Type t1min = exp(-square(x_first - peak) / upselex);
461 Type t2min = exp(-square(x_last - peak2) / downselex);
462 for (int bin = startbin; bin < nbins; bin++) {
463 Type t1 = x(bin) - peak;
464 Type t2 = x(bin) - peak2;
465 Type join1 = invlogit(20.0 * t1 / (1.0 + CppAD::abs(t1)));
466 Type join2 = invlogit(20.0 * t2 / (1.0 + CppAD::abs(t2)));
467 Type asc = (sel_dn6_ends(flt, 0) == 1)
468 ? point1 + (1.0 - point1) * (exp(-square(t1) / upselex) - t1min) / (1.0 - t1min)
469 : exp(-square(t1) / upselex);
470 Type dsc = (sel_dn6_ends(flt, 1) == 1)
471 ? 1.0 + (point2 - 1.0) * (exp(-square(t2) / downselex) - 1.0) / (t2min - 1.0)
472 : exp(-square(t2) / downselex);
473 sel(flt, sex, bin, yr) = asc * (1.0 - join1) + join1 * ((1.0 - join2) + dsc * join2);
474 }
475 // Quadratic ramp below startbin (SS_selex.tpl:197-201).
476 if (is_length_based && startbin > 0) {
477 Type sel_start = sel(flt, sex, startbin, yr);
478 for (int bin = 0; bin < startbin; bin++) {
479 sel(flt, sex, bin, yr) = square(x(bin) / x(startbin)) * sel_start;
480 }
481 }
482 break;
483 }
484 }
485
486 } // End sex
487 } // End year
488
489 // --- 2b. PER-SEX APICAL HEIGHT (only when a linkage names `apical`) ---
490 // Skipped otherwise, so other models are unchanged. The normalizer must pool across
491 // sexes or be off for it to survive; .check_sel_apical_rows() refuses the rest.
492 if (sel_apical_on == 1) {
493 for (int yr = 0; yr < nyrs_hind; yr++) {
494 for (int sex = 0; sex < nsex(sp); sex++) {
495 Type scale = exp(log_sel_apical(flt, sex) + sel_apical_off(flt, sex, yr))
496 + sel_apical_off_nat(flt, sex, yr);
497 for (int bin = 0; bin < nbins; bin++) sel(flt, sex, bin, yr) *= scale;
498 }
499 }
500 }
501
502 // --- 3. ZERO, NORMALIZE AND PROJECT ---
504 flt, nyrs_hind, nyrs, flt_spp, flt_sel_type, flt_sel_dim, bin_first_selected, nages,
505 nlengths_pop, nsex, sel_norm_bin1, sel_norm_bin2, sel_norm_scope, sel);
506
507 // --- 4. LENGTH TO AGE ---
508 if (is_length_based) {
509 int wtind = nspp * 2 + flt; // growth_matrix: two entries per species, then one per fleet
510 for (int yr = 0; yr < nyrs; yr++) {
511 for (int sex = 0; sex < nsex(sp); sex++) {
512 convert_length_selectivity(flt, sp, sex, yr, wtind, nages, nlengths_pop,
513 sel_at_length, growth_matrix, sel_at_age);
514 }
515 }
516 }
517 } // End fleet
518}
519
520#endif
bool isNA(Type x)
Type log_mean_exp(const vector< Type > &x)
log(mean(exp(x))) over a non-empty vector, computed so it cannot overflow.
Type max2(Type x, Type y)
Type square(Type x)
void normalize_and_project_selectivity(const int &flt, const int &nyrs_hind, const int &nyrs, const vector< int > &flt_spp, const vector< int > &flt_sel_type, const vector< int > &flt_sel_dim, const vector< int > &bin_first_selected, const vector< int > &nages, const vector< int > &nlengths_pop, const vector< int > &nsex, const vector< int > &sel_norm_bin1, const vector< int > &sel_norm_bin2, const vector< int > &sel_norm_scope, array< Type > &selectivity)
Zeroes, normalizes and projects ONE fleet's selectivity (age or length based).
void convert_length_selectivity(const int &flt, const int &sp, const int &sex, const int &yr, const int &wtind, const vector< int > &nages, const vector< int > &nlengths_pop, array< Type > &sel_length, array< Type > &growth_matrix, array< Type > &sel_at_age)
Converts one fleet/sex/year of length-based selectivity to ages: the sum over population length bins ...
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.