use crate::fusion::{KEV, Pb11Fit, Reaction, bosch_hale, pb11_reactivity};
pub const Q_PB11: f64 = 8.6821;
pub const Q_DHE3: f64 = 18.3531;
pub const Q_HE3HE3: f64 = 12.8596;
pub const Q_PLI6: f64 = 4.0197;
pub const Q_DT: f64 = 17.5893;
pub const Q_DDN: f64 = 3.2689;
pub const Q_DDP: f64 = 4.0327;
const E_DD_NEUTRON: f64 = 2.45;
const E_DT_NEUTRON: f64 = 14.07;
const MEC2_KEV: f64 = 510.998_95;
const W0: f64 = 1.4219e-36;
pub fn dhe3_neutron_fraction(t: f64, x_d: f64, burn_tritium: bool) -> Option<f64> {
let (n_d, n_h) = (x_d, 1.0 - x_d);
let (dhe, ddn, ddp) = (bosch_hale(Reaction::DHe3, t)?, bosch_hale(Reaction::DDn, t)?, bosch_hale(Reaction::DDp, t)?);
let mut total = n_d * n_h * dhe * Q_DHE3 + 0.5 * n_d * n_d * (ddn * Q_DDN + ddp * Q_DDP);
let mut neutrons = 0.5 * n_d * n_d * ddn * E_DD_NEUTRON;
if burn_tritium {
total += 0.5 * n_d * n_d * ddp * Q_DT;
neutrons += 0.5 * n_d * n_d * ddp * E_DT_NEUTRON;
}
Some(neutrons / total)
}
pub fn bremsstrahlung(ne: f64, te: f64, z_eff: f64) -> f64 {
let t = te / MEC2_KEV;
8.511 * W0 * ne * ne * t.sqrt() * (z_eff * (1.0 + 1.78 * t.powf(1.34)) + 2.12 * t * (1.0 + 1.1 * t + t * t - 1.25 * t.powf(2.5)))
}
pub fn synchrotron(b: f64, ne: f64, te: f64) -> f64 {
6.21e-17 * b * b * ne * te
}
pub fn pb11_fusion_over_brems(ti: f64, te: f64, f_b: f64, fit: Pb11Fit) -> Option<f64> {
let (n_p, n_b) = (1.0 - f_b, f_b);
let ne = n_p + 5.0 * n_b;
let z_eff = (n_p + 25.0 * n_b) / ne;
Some(n_p * n_b * pb11_reactivity(fit, ti)? * Q_PB11 * 1e3 * KEV / bremsstrahlung(ne, te, z_eff))
}
#[cfg(test)]
mod tests {
use super::*;
fn close(a: f64, b: f64, rel: f64) -> bool {
(a / b - 1.0).abs() < rel
}
#[test]
fn deuterium_helium3_is_not_neutron_free() {
assert!(close(dhe3_neutron_fraction(50.0, 0.5, false).unwrap(), 0.0131, 0.01));
assert!(close(dhe3_neutron_fraction(50.0, 0.5, true).unwrap(), 0.0726, 0.01));
assert!(close(dhe3_neutron_fraction(100.0, 0.25, false).unwrap(), 0.0034, 0.02));
}
#[test]
fn radiation_losses() {
let low = bremsstrahlung(1.0, 1e-3, 1.0) / 1e-3f64.sqrt();
assert!(close(low, 5.355e-37, 2e-3));
let (n_p, n_b) = (0.85, 0.15);
let ne_rel = n_p + 5.0 * n_b;
let z_eff = (n_p + 25.0 * n_b) / ne_rel;
let ne = 1e20;
for (beta, expect) in [(1.0, 41.03), (0.1, 410.3)] {
let b2 = 2.0 * 1.256_637_062_12e-6 * (ne / ne_rel * 300.0 + ne * 150.0) * KEV / beta;
assert!(close(synchrotron(b2.sqrt(), ne, 150.0) / bremsstrahlung(ne, 150.0, z_eff), expect, 0.005));
}
}
#[test]
fn proton_boron_balance() {
let ratio = |ti: f64, te: f64, f: f64| pb11_fusion_over_brems(ti, te, f, Pb11Fit::TentoriBelloni).unwrap();
let (best, at) = (20..500).map(|k| (ratio(300.0, 300.0, f64::from(k) / 1000.0), f64::from(k) / 1000.0)).fold((0.0, 0.0), |a, b| if b.0 > a.0 { b } else { a });
assert!(close(best, 0.4572, 2e-3) && (at - 0.105).abs() < 0.003);
assert!((70..=500).all(|t| ratio(f64::from(t), f64::from(t), 0.15) < 1.0));
assert!(ratio(300.0, 100.0, 0.15) > 1.0);
}
}