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}