#![cfg_attr(feature = "no_std", no_std)]
#![allow(non_snake_case)]
use core::f64::consts::PI;
const EV_SQ_KM_TO_GEV_OVER4: f64 = 1e-9 / 1.97327e-7 * 1e3 / 4.0;
const YE_RHO_E_TO_A: f64 = 1.52e-4;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct VacuumParameters {
pub s12sq: f64,
pub s13sq: f64,
pub s23sq: f64,
pub delta: f64,
pub Dmsq21: f64,
pub Dmsq31: f64,
pub L: f64,
pub E: f64,
}
impl VacuumParameters {
pub fn nufit52_no(l: f64, e: f64) -> Self {
Self {
s12sq: 0.307,
s13sq: 0.02203,
s23sq: 0.546,
delta: 1.36 * PI,
Dmsq21: 7.42e-5,
Dmsq31: 2.517e-3,
L: l,
E: e,
}
}
pub fn nufit52_io(l: f64, e: f64) -> Self {
Self {
s12sq: 0.307,
s13sq: 0.02219,
s23sq: 0.539,
delta: 1.56 * PI,
Dmsq21: 7.42e-5,
Dmsq31: -2.498e-3,
L: l,
E: e,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct MatterParameters {
pub s12sq: f64,
pub s13sq: f64,
pub s23sq: f64,
pub delta: f64,
pub Dmsq21: f64,
pub Dmsq31: f64,
pub L: f64,
pub E: f64,
pub rho: f64,
pub Ye: f64,
pub N_Newton: u8,
}
impl MatterParameters {
pub fn nufit52_no(l: f64, e: f64) -> Self {
Self {
s12sq: 0.307,
s13sq: 0.02203,
s23sq: 0.546,
delta: 1.36 * PI,
Dmsq21: 7.42e-5,
Dmsq31: 2.517e-3,
L: l,
E: e,
rho: 2.6,
Ye: 0.5,
N_Newton: 0,
}
}
pub fn from_vacuum(vac: &VacuumParameters, rho: f64, ye: f64, n_newton: u8) -> Self {
Self {
s12sq: vac.s12sq,
s13sq: vac.s13sq,
s23sq: vac.s23sq,
delta: vac.delta,
Dmsq21: vac.Dmsq21,
Dmsq31: vac.Dmsq31,
L: vac.L,
E: vac.E,
rho,
Ye: ye,
N_Newton: n_newton,
}
}
}
pub type ProbabilityMatrix = [[f64; 3]; 3];
pub fn probability_vacuum_lbl(parameters: &VacuumParameters) -> ProbabilityMatrix {
let VacuumParameters {
s12sq,
s13sq,
s23sq,
delta,
Dmsq21,
Dmsq31,
L,
E,
} = *parameters;
let c13sq = 1.0 - s13sq;
let Ue3sq = s13sq;
let Ue2sq = c13sq * s12sq;
let Um3sq = c13sq * s23sq;
let Ut2sq = s13sq * s12sq * s23sq;
let Um2sq = (1.0 - s12sq) * (1.0 - s23sq);
let Jrr = (Um2sq * Ut2sq).sqrt();
let sind = delta.sin();
let cosd = delta.cos();
let Um2sq = Um2sq + Ut2sq - 2.0 * Jrr * cosd;
let Jvac = 8.0 * Jrr * c13sq * sind;
let Ue1sq = 1.0 - Ue3sq - Ue2sq;
let Um1sq = 1.0 - Um3sq - Um2sq;
let Ut3sq = 1.0 - Um3sq - Ue3sq;
let Ut2sq = 1.0 - Um2sq - Ue2sq;
let Ut1sq = 1.0 - Um1sq - Ue1sq;
let Lover4E = EV_SQ_KM_TO_GEV_OVER4 * L / E;
let D21 = Dmsq21 * Lover4E;
let D31 = Dmsq31 * Lover4E;
let sinD21 = D21.sin();
let sinD31 = D31.sin();
let sinD32 = (D31 - D21).sin();
let triple_sin = sinD21 * sinD31 * sinD32;
let sinsqD21_2 = 2.0 * sinD21 * sinD21;
let sinsqD31_2 = 2.0 * sinD31 * sinD31;
let sinsqD32_2 = 2.0 * sinD32 * sinD32;
let Pme_CPC = (Ut3sq - Um2sq * Ue1sq - Um1sq * Ue2sq) * sinsqD21_2
+ (Ut2sq - Um3sq * Ue1sq - Um1sq * Ue3sq) * sinsqD31_2
+ (Ut1sq - Um3sq * Ue2sq - Um2sq * Ue3sq) * sinsqD32_2;
let Pme_CPV = -Jvac * triple_sin;
let Pmm = 1.0
- 2.0
* (Um2sq * Um1sq * sinsqD21_2
+ Um3sq * Um1sq * sinsqD31_2
+ Um3sq * Um2sq * sinsqD32_2);
let Pee = 1.0
- 2.0
* (Ue2sq * Ue1sq * sinsqD21_2
+ Ue3sq * Ue1sq * sinsqD31_2
+ Ue3sq * Ue2sq * sinsqD32_2);
let mut probs = [[0.0; 3]; 3];
probs[0][0] = Pee; probs[0][1] = Pme_CPC - Pme_CPV; probs[0][2] = 1.0 - Pee - probs[0][1];
probs[1][0] = Pme_CPC + Pme_CPV; probs[1][1] = Pmm; probs[1][2] = 1.0 - probs[1][0] - Pmm;
probs[2][0] = 1.0 - Pee - probs[1][0]; probs[2][1] = 1.0 - probs[0][1] - Pmm; probs[2][2] = 1.0 - probs[0][2] - probs[1][2];
probs
}
pub fn probability_matter_lbl(parameters: &MatterParameters) -> ProbabilityMatrix {
let MatterParameters {
s12sq,
s13sq,
s23sq,
delta,
Dmsq21,
Dmsq31,
L,
E,
rho,
Ye,
N_Newton,
} = *parameters;
let c13sq = 1.0 - s13sq;
let Ue2sq = c13sq * s12sq;
let Ue3sq = s13sq;
let Um3sq = c13sq * s23sq;
let Ut2sq = s13sq * s12sq * s23sq;
let Um2sq = (1.0 - s12sq) * (1.0 - s23sq);
let Jrr = (Um2sq * Ut2sq).sqrt();
let sind = delta.sin();
let cosd = delta.cos();
let Um2sq = Um2sq + Ut2sq - 2.0 * Jrr * cosd;
let Jmatter = 8.0 * Jrr * c13sq * sind;
let Amatter = Ye * rho * E * YE_RHO_E_TO_A;
let Dmsqee = Dmsq31 - s12sq * Dmsq21;
let A_sum = Dmsq21 + Dmsq31; let See = A_sum - Dmsq21 * Ue2sq - Dmsq31 * Ue3sq;
let Tmm_base = Dmsq21 * Dmsq31;
let Tee = Tmm_base * (1.0 - Ue3sq - Ue2sq);
let C = Amatter * Tee;
let A = A_sum + Amatter;
let xmat = Amatter / Dmsqee;
let tmp = 1.0 - xmat;
let mut lambda3 = Dmsq31 + 0.5 * Dmsqee * (xmat - 1.0 + (tmp * tmp + 4.0 * s13sq * xmat).sqrt());
let B = Tmm_base + Amatter * See; for _ in 0..N_Newton {
lambda3 = (lambda3 * lambda3 * (lambda3 - A) + C)
/ (lambda3 * (2.0 * lambda3 - A) + B);
}
let tmp = A - lambda3;
let Dlambda21 = (tmp * tmp - 4.0 * C / lambda3).sqrt();
let lambda2 = 0.5 * (A - lambda3 + Dlambda21);
let Dlambda32 = lambda3 - lambda2;
let Dlambda31 = Dlambda32 + Dlambda21;
let PiDlambdaInv = 1.0 / (Dlambda31 * Dlambda32 * Dlambda21);
let Xp3 = PiDlambdaInv * Dlambda21;
let Xp2 = -PiDlambdaInv * Dlambda31;
let Ue3sq = (lambda3 * (lambda3 - See) + Tee) * Xp3;
let Ue2sq = (lambda2 * (lambda2 - See) + Tee) * Xp2;
let Smm = A - Dmsq21 * Um2sq - Dmsq31 * Um3sq;
let Tmm = Tmm_base * (1.0 - Um3sq - Um2sq) + Amatter * (See + Smm - A_sum);
let Um3sq = (lambda3 * (lambda3 - Smm) + Tmm) * Xp3;
let Um2sq = (lambda2 * (lambda2 - Smm) + Tmm) * Xp2;
let Jmatter = Jmatter * Dmsq21 * Dmsq31 * (Dmsq31 - Dmsq21) * PiDlambdaInv;
let Ue1sq = 1.0 - Ue3sq - Ue2sq;
let Um1sq = 1.0 - Um3sq - Um2sq;
let Ut3sq = 1.0 - Um3sq - Ue3sq;
let Ut2sq = 1.0 - Um2sq - Ue2sq;
let Ut1sq = 1.0 - Um1sq - Ue1sq;
let Lover4E = EV_SQ_KM_TO_GEV_OVER4 * L / E;
let D21 = Dlambda21 * Lover4E;
let D32 = Dlambda32 * Lover4E;
let sinD21 = D21.sin();
let sinD31 = (D32 + D21).sin();
let sinD32 = D32.sin();
let triple_sin = sinD21 * sinD31 * sinD32;
let sinsqD21_2 = 2.0 * sinD21 * sinD21;
let sinsqD31_2 = 2.0 * sinD31 * sinD31;
let sinsqD32_2 = 2.0 * sinD32 * sinD32;
let Pme_CPC = (Ut3sq - Um2sq * Ue1sq - Um1sq * Ue2sq) * sinsqD21_2
+ (Ut2sq - Um3sq * Ue1sq - Um1sq * Ue3sq) * sinsqD31_2
+ (Ut1sq - Um3sq * Ue2sq - Um2sq * Ue3sq) * sinsqD32_2;
let Pme_CPV = -Jmatter * triple_sin;
let Pmm = 1.0
- 2.0
* (Um2sq * Um1sq * sinsqD21_2
+ Um3sq * Um1sq * sinsqD31_2
+ Um3sq * Um2sq * sinsqD32_2);
let Pee = 1.0
- 2.0
* (Ue2sq * Ue1sq * sinsqD21_2
+ Ue3sq * Ue1sq * sinsqD31_2
+ Ue3sq * Ue2sq * sinsqD32_2);
let mut probs = [[0.0; 3]; 3];
probs[0][0] = Pee;
probs[0][1] = Pme_CPC - Pme_CPV;
probs[0][2] = 1.0 - Pee - probs[0][1];
probs[1][0] = Pme_CPC + Pme_CPV;
probs[1][1] = Pmm;
probs[1][2] = 1.0 - probs[1][0] - Pmm;
probs[2][0] = 1.0 - Pee - probs[1][0];
probs[2][1] = 1.0 - probs[0][1] - Pmm;
probs[2][2] = 1.0 - probs[0][2] - probs[1][2];
probs
}
pub fn normalize_probabilities(probs: &mut ProbabilityMatrix) {
for row in probs.iter_mut() {
for p in row.iter_mut() {
*p = p.clamp(0.0, 1.0);
}
let sum: f64 = row.iter().sum();
if sum > 0.0 && (sum - 1.0).abs() > 1e-10 {
for p in row.iter_mut() {
*p /= sum;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const EPSILON: f64 = 1e-10;
#[test]
fn test_vacuum_unitarity() {
let params = VacuumParameters::nufit52_no(1000.0, 2.0);
let probs = probability_vacuum_lbl(¶ms);
for (i, row) in probs.iter().enumerate() {
let sum: f64 = row.iter().sum();
assert!(
(sum - 1.0).abs() < EPSILON,
"Row {} sum = {}, expected 1.0",
i,
sum
);
}
}
#[test]
fn test_vacuum_zero_distance() {
let params = VacuumParameters::nufit52_no(0.0, 2.0);
let probs = probability_vacuum_lbl(¶ms);
for i in 0..3 {
for j in 0..3 {
let expected = if i == j { 1.0 } else { 0.0 };
assert!(
(probs[i][j] - expected).abs() < EPSILON,
"probs[{}][{}] = {}, expected {}",
i,
j,
probs[i][j],
expected
);
}
}
}
#[test]
fn test_matter_unitarity() {
let params = MatterParameters::nufit52_no(1300.0, 2.5);
let mut probs = probability_matter_lbl(¶ms);
normalize_probabilities(&mut probs);
for (i, row) in probs.iter().enumerate() {
let sum: f64 = row.iter().sum();
assert!(
(sum - 1.0).abs() < 1e-6,
"Row {} sum = {}, expected 1.0",
i,
sum
);
}
}
#[test]
fn test_matter_enhances_appearance() {
let vac = VacuumParameters::nufit52_no(1300.0, 2.5);
let mat = MatterParameters::nufit52_no(1300.0, 2.5);
let probs_vac = probability_vacuum_lbl(&vac);
let probs_mat = probability_matter_lbl(&mat);
assert!(
(probs_mat[1][0] - probs_vac[1][0]).abs() > 0.001,
"Matter effect should modify P(νμ → νe)"
);
}
#[test]
fn test_cp_conjugate() {
let mut params_pos = VacuumParameters::nufit52_no(1000.0, 2.0);
params_pos.delta = 1.36 * PI;
let mut params_neg = params_pos;
params_neg.delta = -1.36 * PI;
let probs_pos = probability_vacuum_lbl(¶ms_pos);
let probs_neg = probability_vacuum_lbl(¶ms_neg);
assert!(
(probs_pos[1][0] - probs_neg[1][0]).abs() > 1e-6,
"CP conjugate probabilities should differ"
);
}
}