nereids_physics/urr.rs
1//! Unresolved Resonance Region (LRU=2) cross-section calculation.
2//!
3//! Computes average cross-sections via the Hauser-Feshbach formula using
4//! the average level-spacing and partial widths stored in `UrrData`.
5//!
6//! ## Hauser-Feshbach Formula
7//!
8//! For each (L, J) combination at energy E:
9//!
10//! ```text
11//! g_J = (2J+1) / ((2I+1) · (2s+1))
12//!
13//! LRF=1: Γ_n(E) = 2 · P_L(ρ(E)) · GNO [GNO = reduced neutron width]
14//! LRF=2: Γ_n(E) from tabulated energy grid using INT interpolation
15//! INT=1 histogram, INT=2 lin-lin, INT=3 log-lin,
16//! INT=4 lin-log, INT=5 log-log (all 5 ENDF codes supported)
17//!
18//! Γ_tot = Γ_n + GG + GF + GX
19//!
20//! σ_γ += (π/k²) · g_J · (2π · Γ_n · GG) / (D · Γ_tot)
21//! σ_f += (π/k²) · g_J · (2π · Γ_n · GF) / (D · Γ_tot)
22//! σ_cn += (π/k²) · g_J · (2π · Γ_n · Γ_n) / (D · Γ_tot) [neutron-out]
23//! ```
24//!
25//! `σ_cn` is the compound-elastic contribution; the competitive (GX) channel
26//! contributes to `total` but not `elastic`. Potential scattering
27//! `σ_pot = 4π·AP²/100` (AP in fm, result in barns) is included in the
28//! returned `total` and `elastic` so that the URR band produces a physically
29//! consistent cross-section without requiring special handling at the call site.
30//!
31//! ## Units
32//! All energies in eV, all lengths (AP, channel radii) in fm (true physics
33//! femtometers, 10⁻¹⁵ m), cross-sections in barns.
34//!
35//! ENDF stores radii in 10⁻¹² cm (= 10 fm); the parser converts to fm at
36//! parse time by multiplying by 10 (see `ENDF_RADIUS_TO_FM` in `parser.rs`),
37//! matching SAMMY's `FillSammyRmatrixFromRMat.cpp` line 422.
38//!
39//! ## SAMMY Reference
40//! - `unr/munr03.f90` Csig3 subroutine
41//! - SAMMY manual Section 4 (URR treatment)
42//!
43//! ## References
44//! - ENDF-6 Formats Manual §2.2.2
45//! - W. Hauser, H. Feshbach, Phys. Rev. 87 (1952) 366
46
47use nereids_endf::resonance::UrrData;
48
49use crate::channel;
50use crate::penetrability;
51
52/// Compute Hauser-Feshbach average cross-sections in the Unresolved Resonance Region.
53///
54/// Returns `(total, elastic, capture, fission)` in barns.
55/// All four are zero when `e_ev` falls outside the URR energy band
56/// `[urr.e_low, urr.e_high]`.
57///
58/// `elastic` = compound-nuclear elastic + smooth potential scattering.
59/// `total` = elastic + capture + fission + competitive (GX).
60/// Potential scattering `σ_pot = 4π·AP²/100` (AP in fm, result in barns)
61/// is folded in here rather than at the call site.
62/// SAMMY ref: `unr/munr03.f90` includes the hard-sphere background.
63///
64/// ## SAMMY Reference
65/// `unr/munr03.f90` Csig3 — Hauser-Feshbach cross-section kernel.
66///
67/// # Arguments
68/// * `urr` — Parsed URR parameters (LRF=1 or LRF=2).
69/// * `e_ev` — Neutron lab-frame energy in eV.
70/// * `ap_fm` — Scattering radius in fm at this energy. The caller is
71/// responsible for evaluating any AP(E) table (NRO≠0) or falling back
72/// to the constant `urr.ap`.
73pub fn urr_cross_sections(urr: &UrrData, e_ev: f64, ap_fm: f64) -> (f64, f64, f64, f64) {
74 if e_ev < urr.e_low || e_ev > urr.e_high {
75 return (0.0, 0.0, 0.0, 0.0);
76 }
77
78 let mut sig_cap = 0.0_f64;
79 let mut sig_fiss = 0.0_f64;
80 let mut sig_compound_n = 0.0_f64;
81 let mut sig_competitive = 0.0_f64;
82
83 for lg in &urr.l_groups {
84 let awri = lg.awri;
85 let l = lg.l;
86
87 // Guard: non-positive AWRI makes k² = 0 → π/k² = inf.
88 // SAMMY skips URR entirely so never hits this; we must guard explicitly.
89 if awri <= 0.0 {
90 continue;
91 }
92
93 // π/k² in barns and channel parameter ρ = k·AP.
94 // Uses the same channel formulas as the RRR calculator for consistency.
95 let pi_over_k2 = channel::pi_over_k_squared_barns(e_ev, awri);
96 let rho = channel::rho(e_ev, awri, ap_fm);
97
98 // Blatt-Weisskopf penetrability P_L(ρ) for LRF=1 Γ_n calculation.
99 let p_l = penetrability::penetrability(l, rho);
100
101 for jg in &lg.j_groups {
102 // Statistical weight g_J = (2J+1) / ((2I+1)·(2s+1)).
103 // Target spin I = urr.spi, neutron spin s = 1/2.
104 let g_j = channel::statistical_weight(jg.j, urr.spi);
105
106 // Obtain effective widths at energy e_ev.
107 //
108 // LRF=1: Γ_n = 2·P_L(ρ)·GNO (GNO = reduced neutron width, eV)
109 // D, GG, GF are energy-independent (single-element vecs).
110 // LRF=2: widths interpolated from the energy table using the
111 // INT code stored per J-group.
112 // INT=2 (lin-lin) and INT=5 (log-log) are both supported.
113 //
114 // SAMMY ref: unr/munr03.f90 Csig3 — `Gn = Two*Pene*Gno` for LRF=1.
115 let (gn_eff, d_eff, gx_eff, gg_eff, gf_eff) = if urr.lrf == 1 {
116 let gno = jg.gn[0]; // reduced neutron width (eV)
117 // LFW=1: fission widths are energy-dependent (tabulated).
118 // LFW=0: fission width is a single constant.
119 let gf = if jg.gf.len() > 1 {
120 // Interpolate fission width from shared energy grid.
121 lin_lin_interp(&jg.energies, &jg.gf, e_ev)
122 } else {
123 jg.gf[0]
124 };
125 (2.0 * p_l * gno, jg.d[0], 0.0_f64, jg.gg[0], gf)
126 } else {
127 // LRF=2: dispatch on the stored interpolation law.
128 // ENDF-6 §0.5: INT=1 histogram, 2 lin-lin, 3 log-lin,
129 // 4 lin-log, 5 log-log.
130 let interp: fn(&[f64], &[f64], f64) -> f64 = match jg.int_code {
131 1 => histogram_interp,
132 3 => log_lin_interp,
133 4 => lin_log_interp,
134 5 => log_log_interp,
135 _ => lin_lin_interp, // INT=2 and any unknown → lin-lin
136 };
137 let gn_i = interp(&jg.energies, &jg.gn, e_ev);
138 let d_i = interp(&jg.energies, &jg.d, e_ev);
139 let gx_i = interp(&jg.energies, &jg.gx, e_ev);
140 let gg_i = interp(&jg.energies, &jg.gg, e_ev);
141 let gf_i = interp(&jg.energies, &jg.gf, e_ev);
142 (gn_i, d_i, gx_i, gg_i, gf_i)
143 };
144
145 // Skip degenerate entries.
146 if d_eff <= 0.0 || gn_eff <= 0.0 {
147 continue;
148 }
149
150 let gamma_tot = gn_eff + gg_eff + gf_eff + gx_eff;
151 if gamma_tot <= 0.0 {
152 continue;
153 }
154
155 // Hauser-Feshbach kernel:
156 // (π/k²) · g_J · (2π · Γ_n / D) · Γ_x / Γ_tot
157 //
158 // The 2π/D factor is the average level density times 2π.
159 // SAMMY ref: unr/munr03.f90 `Two*Pi*Gn/D` prefactor.
160 let prefactor = pi_over_k2 * g_j * (2.0 * std::f64::consts::PI * gn_eff) / d_eff;
161
162 sig_cap += prefactor * gg_eff / gamma_tot;
163 sig_fiss += prefactor * gf_eff / gamma_tot;
164 // Compound-elastic contribution: neutron absorbed into the compound
165 // nucleus and re-emitted as a neutron. The probability of re-emission
166 // into the neutron channel is Γ_n / Γ_tot, giving:
167 // σ_cn = (π/k²) · g_J · (2π·Γ_n/D) · Γ_n / Γ_tot
168 // SAMMY ref: unr/munr03.f90 — `Sigxxx` uses Gn for both transmission
169 // coefficients in the neutron elastic formula.
170 sig_compound_n += prefactor * gn_eff / gamma_tot;
171 // Competitive (inelastic) channel: GX is included in Γ_tot but goes
172 // into neither capture nor fission. It contributes to the total
173 // cross section without appearing in elastic, capture, or fission.
174 // SAMMY ref: unr/munr03.f90 — GX field in Gamma_total.
175 sig_competitive += prefactor * gx_eff / gamma_tot;
176 }
177 }
178
179 // Smooth potential scattering: σ_pot = 4π·AP²
180 // AP is in fm; 1 barn = 100 fm².
181 // SAMMY ref: unr/munr03.f90 adds hard-sphere background to elastic.
182 let sig_pot = 4.0 * std::f64::consts::PI * ap_fm * ap_fm / 100.0;
183 let elastic = sig_compound_n + sig_pot;
184 let total = elastic + sig_cap + sig_fiss + sig_competitive;
185 (total, elastic, sig_cap, sig_fiss)
186}
187
188/// Shared binary-search kernel for the two interpolation modes.
189///
190/// Handles empty-table, lower-clamp, and upper-clamp, then locates the
191/// bracketing interval and calls `interp_fn(x, x0, y0, x1, y1)`.
192/// `xs` must be strictly ascending and the same length as `ys`.
193#[inline]
194fn table_interp(
195 xs: &[f64],
196 ys: &[f64],
197 x: f64,
198 interp_fn: impl Fn(f64, f64, f64, f64, f64) -> f64,
199) -> f64 {
200 debug_assert_eq!(xs.len(), ys.len());
201 debug_assert!(
202 !xs.is_empty(),
203 "URR interpolation table must not be empty (parser bug?)"
204 );
205 if xs.is_empty() {
206 return 0.0;
207 }
208 if x <= xs[0] {
209 return ys[0];
210 }
211 if x >= xs[xs.len() - 1] {
212 return ys[xs.len() - 1];
213 }
214 let i = xs.partition_point(|&xi| xi <= x);
215 interp_fn(x, xs[i - 1], ys[i - 1], xs[i], ys[i])
216}
217
218/// Linear-linear interpolation (INT=2, clamped to table endpoints).
219fn lin_lin_interp(xs: &[f64], ys: &[f64], x: f64) -> f64 {
220 table_interp(xs, ys, x, |x, x0, y0, _x1, y1| {
221 let dx = _x1 - x0;
222 if dx.abs() < f64::EPSILON {
223 return y0;
224 }
225 y0 + (x - x0) / dx * (y1 - y0)
226 })
227}
228
229/// Histogram interpolation (INT=1, clamped to table endpoints).
230///
231/// Returns the y-value at the left endpoint of each interval (step function).
232/// ENDF-6 §0.5: "y = y_i for x_i ≤ x < x_{i+1}".
233fn histogram_interp(xs: &[f64], ys: &[f64], x: f64) -> f64 {
234 table_interp(xs, ys, x, |_x, _x0, y0, _x1, _y1| y0)
235}
236
237/// Log-x / Linear-y interpolation (INT=3, clamped to table endpoints).
238///
239/// y(x) = y₀ + [ln(x) - ln(x₀)] / [ln(x₁) - ln(x₀)] × (y₁ - y₀)
240/// Falls back to lin-lin when any x ≤ 0 (log undefined).
241fn log_lin_interp(xs: &[f64], ys: &[f64], x: f64) -> f64 {
242 table_interp(xs, ys, x, |x, x0, y0, x1, y1| {
243 if x <= 0.0 || x0 <= 0.0 || x1 <= 0.0 {
244 let dx = x1 - x0;
245 return if dx.abs() < f64::EPSILON {
246 y0
247 } else {
248 y0 + (x - x0) / dx * (y1 - y0)
249 };
250 }
251 let denom = x1.ln() - x0.ln();
252 if denom.abs() < f64::EPSILON {
253 return y0;
254 }
255 let t = (x.ln() - x0.ln()) / denom;
256 y0 + t * (y1 - y0)
257 })
258}
259
260/// Linear-x / Log-y interpolation (INT=4, clamped to table endpoints).
261///
262/// y(x) = y₀ × (y₁/y₀)^[(x - x₀) / (x₁ - x₀)]
263/// Falls back to lin-lin when any y ≤ 0 (log undefined).
264fn lin_log_interp(xs: &[f64], ys: &[f64], x: f64) -> f64 {
265 table_interp(xs, ys, x, |x, x0, y0, x1, y1| {
266 let dx = x1 - x0;
267 if dx.abs() < f64::EPSILON {
268 return y0;
269 }
270 if y0 <= 0.0 || y1 <= 0.0 {
271 return y0 + (x - x0) / dx * (y1 - y0);
272 }
273 let t = (x - x0) / dx;
274 (y0.ln() + t * (y1.ln() - y0.ln())).exp()
275 })
276}
277
278/// Log-log interpolation (INT=5, clamped to table endpoints).
279///
280/// Used for LRF=2 width tables in ENDF (common for heavy actinides such as
281/// U-238 where widths follow an approximate power-law energy dependence).
282/// Falls back to lin-lin when any bracket value is ≤ 0 (log undefined) or
283/// when the log-space denominator is too small for safe division.
284fn log_log_interp(xs: &[f64], ys: &[f64], x: f64) -> f64 {
285 table_interp(xs, ys, x, |x, x0, y0, x1, y1| {
286 let dx = x1 - x0;
287 // Guard against degenerate x-intervals (duplicate energy points).
288 if dx.abs() < f64::EPSILON {
289 return y0;
290 }
291 // Guard against non-positive values (log undefined).
292 if x <= 0.0 || x0 <= 0.0 || x1 <= 0.0 || y0 <= 0.0 || y1 <= 0.0 {
293 // Fall back to lin-lin for degenerate (non-positive) bracket entries.
294 return y0 + (x - x0) / dx * (y1 - y0);
295 }
296 let denom_ln = x1.ln() - x0.ln();
297 // Guard against nearly equal log values to avoid numerical blow-up.
298 if denom_ln.abs() < f64::EPSILON {
299 return y0;
300 }
301 let log_t = (x.ln() - x0.ln()) / denom_ln;
302 (y0.ln() + log_t * (y1.ln() - y0.ln())).exp()
303 })
304}
305
306#[cfg(test)]
307mod tests {
308 use super::*;
309 use nereids_endf::resonance::{UrrData, UrrJGroup, UrrLGroup};
310
311 /// σ = 0 when energy is outside the URR band.
312 #[test]
313 fn urr_outside_band_returns_zero() {
314 let urr = make_lrf1_urr(1000.0, 30_000.0);
315 let (t, e, c, f) = urr_cross_sections(&urr, 500.0, urr.ap); // below band
316 assert_eq!(t, 0.0);
317 assert_eq!(e, 0.0);
318 assert_eq!(c, 0.0);
319 assert_eq!(f, 0.0);
320
321 let (t2, _, _, _) = urr_cross_sections(&urr, 50_000.0, urr.ap); // above band
322 assert_eq!(t2, 0.0);
323 }
324
325 /// LRF=1 formula check: σ_γ = (π/k²) · g_J · (2π · Γ_n · GG) / (D · Γ_tot).
326 ///
327 /// Uses a single (L=0, J=2.0) group with known parameters; verifies
328 /// σ_γ matches the hand-computed value within numerical precision (~1e-10 relative).
329 #[test]
330 fn urr_lrf1_formula_check() {
331 // Hand-crafted parameters similar to U-233 URR (order of magnitude).
332 let e_test = 1_000.0_f64; // eV, mid-URR
333 let awri = 231.038_f64;
334 let ap = 9.6931_f64; // scattering radius in fm (ENDF 0.96931 × 10)
335 let spi = 2.5_f64; // U-233 target spin
336
337 let gno = 3.0e-4; // reduced neutron width (eV)
338 let gg = 3.5e-2; // gamma width (eV)
339 let d = 0.5; // level spacing (eV)
340 let j = 2.0_f64;
341
342 let urr = UrrData {
343 lrf: 1,
344 spi,
345 ap,
346 e_low: 100.0,
347 e_high: 30_000.0,
348 l_groups: vec![UrrLGroup {
349 l: 0,
350 awri,
351 j_groups: vec![UrrJGroup {
352 j,
353 amun: 1.0,
354 amuf: 0.0,
355 energies: vec![],
356 d: vec![d],
357 gx: vec![0.0],
358 gn: vec![gno],
359 gg: vec![gg],
360 gf: vec![0.0],
361 int_code: 2,
362 }],
363 }],
364 };
365
366 let (total, elastic, capture, fission) = urr_cross_sections(&urr, e_test, ap);
367
368 // Hand-compute expected values.
369 let pi_over_k2 = channel::pi_over_k_squared_barns(e_test, awri);
370 let rho = channel::rho(e_test, awri, ap);
371 let p_l = penetrability::penetrability(0, rho); // L=0
372 let gn_eff = 2.0 * p_l * gno;
373 let gamma_tot = gn_eff + gg;
374 let g_j = channel::statistical_weight(j, spi);
375 let prefactor = pi_over_k2 * g_j * (2.0 * std::f64::consts::PI * gn_eff) / d;
376 let expected_cap = prefactor * gg / gamma_tot;
377 let expected_cn = prefactor * gn_eff / gamma_tot; // neutron-out term
378 let expected_sig_pot = 4.0 * std::f64::consts::PI * ap * ap / 100.0;
379 let expected_elastic = expected_cn + expected_sig_pot;
380
381 assert!(
382 capture > 0.0,
383 "Capture cross-section must be positive, got {capture}"
384 );
385 assert_eq!(fission, 0.0, "No fission in this test case");
386 assert!(
387 (capture - expected_cap).abs() / expected_cap < 1e-10,
388 "Capture deviates from hand calculation: got {capture:.6e}, expected {expected_cap:.6e}"
389 );
390 assert!(
391 (elastic - expected_elastic).abs() / expected_elastic < 1e-10,
392 "Elastic deviates: got {elastic:.6e}, expected {expected_elastic:.6e} \
393 (compound_n={expected_cn:.6e} + sig_pot={expected_sig_pot:.6e})"
394 );
395 assert!(
396 (total - (capture + elastic)).abs() < 1e-14,
397 "Total ≠ capture + elastic: {total} ≠ {}",
398 capture + elastic
399 );
400 }
401
402 /// LRF=2 lin-lin interpolation smoke test.
403 ///
404 /// Two-point energy table; verifies σ_γ > 0 and changes monotonically
405 /// with energy (Γ_n increases with energy for L=0 neutrons).
406 #[test]
407 fn urr_lrf2_interpolation_smoke() {
408 let e1 = 1_000.0_f64;
409 let e2 = 10_000.0_f64;
410
411 let urr = UrrData {
412 lrf: 2,
413 spi: 2.5,
414 ap: 9.6931,
415 e_low: 600.0,
416 e_high: 30_000.0,
417 l_groups: vec![UrrLGroup {
418 l: 0,
419 awri: 231.038,
420 j_groups: vec![UrrJGroup {
421 j: 2.0,
422 amun: 1.0,
423 amuf: 4.0,
424 energies: vec![e1, e2],
425 d: vec![0.5, 0.4], // D decreases (more levels at higher E)
426 gx: vec![0.0, 0.0],
427 gn: vec![1.0e-3, 5.0e-3], // Γ_n increases with E
428 gg: vec![3.5e-2, 3.5e-2], // GG roughly constant
429 gf: vec![0.0, 0.0],
430 int_code: 2,
431 }],
432 }],
433 };
434
435 let (_, _, c1, _) = urr_cross_sections(&urr, e1, urr.ap);
436 let (_, _, c2, _) = urr_cross_sections(&urr, e2, urr.ap);
437 let (_, _, c_mid, _) = urr_cross_sections(&urr, 5_000.0, urr.ap);
438
439 assert!(c1 > 0.0, "σ_γ at {e1} eV must be positive, got {c1}");
440 assert!(c2 > 0.0, "σ_γ at {e2} eV must be positive, got {c2}");
441 // For a lin-lin table with two energy points, any interior energy
442 // should produce a σ_γ between the endpoint values.
443 let (c_lo, c_hi) = if c1 <= c2 { (c1, c2) } else { (c2, c1) };
444 assert!(
445 c_mid >= c_lo && c_mid <= c_hi,
446 "σ_γ at midpoint must lie between endpoint values; \
447 c1={c1:.3e} c_mid={c_mid:.3e} c2={c2:.3e}"
448 );
449
450 // Outside band returns zero.
451 let (t_out, _, _, _) = urr_cross_sections(&urr, 100.0, urr.ap);
452 assert_eq!(t_out, 0.0, "Outside band must be zero");
453 }
454
455 /// lin_lin_interp: boundary clamping and interior interpolation.
456 #[test]
457 fn lin_lin_interp_basic() {
458 let xs = vec![1.0, 3.0, 9.0];
459 let ys = vec![0.0, 2.0, 8.0];
460
461 // Below lower bound: clamp to y[0]
462 assert!((lin_lin_interp(&xs, &ys, 0.0) - 0.0).abs() < 1e-12);
463 // Above upper bound: clamp to y[last]
464 assert!((lin_lin_interp(&xs, &ys, 100.0) - 8.0).abs() < 1e-12);
465 // Interior: x=2.0, between [1,3], t=0.5 → y=1.0
466 assert!((lin_lin_interp(&xs, &ys, 2.0) - 1.0).abs() < 1e-12);
467 // Interior: x=6.0, between [3,9], t=0.5 → y=5.0
468 assert!((lin_lin_interp(&xs, &ys, 6.0) - 5.0).abs() < 1e-12);
469 }
470
471 /// log_log_interp: boundary clamping, power-law interior, and lin-lin fallback.
472 #[test]
473 fn log_log_interp_basic() {
474 // y = x^2: log-log should recover exact values.
475 let xs = vec![1.0, 10.0, 100.0];
476 let ys = vec![1.0, 100.0, 10_000.0]; // y = x^2
477
478 // Clamp below lower bound
479 assert!((log_log_interp(&xs, &ys, 0.5) - 1.0).abs() < 1e-12);
480 // Clamp above upper bound
481 assert!((log_log_interp(&xs, &ys, 200.0) - 10_000.0).abs() < 1e-12);
482 // Interior: x=sqrt(10)≈3.162, between [1,10]; exact power-law gives y=10
483 let x_mid = (1.0_f64 * 10.0_f64).sqrt(); // geometric mean → y = x_mid^2 = 10
484 assert!((log_log_interp(&xs, &ys, x_mid) - 10.0).abs() < 1e-10);
485 // At table points: exact values
486 assert!((log_log_interp(&xs, &ys, 10.0) - 100.0).abs() < 1e-10);
487 }
488
489 /// log_log_interp: falls back to lin-lin when a bracket value is ≤ 0.
490 #[test]
491 fn log_log_interp_nonpositive_fallback() {
492 // y[0] = 0 forces the lin-lin fallback in the first interval.
493 let xs = vec![1.0, 3.0, 9.0];
494 let ys = vec![0.0, 2.0, 8.0]; // y[0] = 0 → log undefined
495
496 // x=2 is in [1,3]; fallback: t=0.5, y = 0 + 0.5*(2-0) = 1.0
497 assert!((log_log_interp(&xs, &ys, 2.0) - 1.0).abs() < 1e-12);
498 // x=6 is in [3,9]; both y>0, so log-log applies (not a straight power law here,
499 // but result must be between 2 and 8)
500 let v = log_log_interp(&xs, &ys, 6.0);
501 assert!(
502 v > 2.0 && v < 8.0,
503 "log-log interior must be between endpoints: {v}"
504 );
505 }
506
507 // ─── Helpers ─────────────────────────────────────────────────────────────
508
509 fn make_lrf1_urr(e_low: f64, e_high: f64) -> UrrData {
510 UrrData {
511 lrf: 1,
512 spi: 2.5,
513 ap: 9.6931,
514 e_low,
515 e_high,
516 l_groups: vec![UrrLGroup {
517 l: 0,
518 awri: 231.038,
519 j_groups: vec![UrrJGroup {
520 j: 2.0,
521 amun: 1.0,
522 amuf: 0.0,
523 energies: vec![],
524 d: vec![0.5],
525 gx: vec![0.0],
526 gn: vec![1.0e-4],
527 gg: vec![3.5e-2],
528 gf: vec![0.0],
529 int_code: 2,
530 }],
531 }],
532 }
533 }
534}