use crate::kernels::nuclear::quantities::FourMomentum;
use deep_causality_num::{FromPrimitive, RealField};
#[derive(Debug, Clone, Copy)]
pub struct LightconeEndpoint<R: RealField> {
pub p_plus: R,
pub p_minus: R,
pub pt_x: R,
pub pt_y: R,
}
impl<R: RealField + FromPrimitive> LightconeEndpoint<R> {
pub fn from_four_momentum(p: &FourMomentum<R>) -> Self {
Self {
p_plus: p.lightcone_plus(),
p_minus: p.lightcone_minus(),
pt_x: p.px(),
pt_y: p.py(),
}
}
#[allow(clippy::wrong_self_convention)]
pub fn to_four_momentum(&self) -> FourMomentum<R> {
let two = R::from_f64(2.0).expect("R::from_f64(2.0) failed");
let e = (self.p_plus + self.p_minus) / two;
let pz = (self.p_plus - self.p_minus) / two;
FourMomentum::<R>::new(e, self.pt_x, self.pt_y, pz)
}
#[allow(dead_code)]
pub fn invariant_mass_squared(&self) -> R {
self.p_plus * self.p_minus - self.pt_x * self.pt_x - self.pt_y * self.pt_y
}
}
#[derive(Debug, Clone, Copy)]
pub struct StringSegment<R: RealField> {
pub quark: LightconeEndpoint<R>,
pub antiquark: LightconeEndpoint<R>,
}
impl<R: RealField + FromPrimitive> StringSegment<R> {
pub fn from_endpoints(quark: &FourMomentum<R>, antiquark: &FourMomentum<R>) -> Self {
Self {
quark: LightconeEndpoint::from_four_momentum(quark),
antiquark: LightconeEndpoint::from_four_momentum(antiquark),
}
}
pub fn invariant_mass_squared(&self) -> R {
let total = self.quark.to_four_momentum() + self.antiquark.to_four_momentum();
total.invariant_mass_squared()
}
pub fn invariant_mass(&self) -> R {
let m_sq = self.invariant_mass_squared();
if m_sq > R::zero() {
m_sq.sqrt()
} else {
R::zero()
}
}
pub fn w_plus(&self) -> R {
self.quark.p_plus + self.antiquark.p_plus
}
pub fn w_minus(&self) -> R {
self.quark.p_minus + self.antiquark.p_minus
}
}
pub fn sample_z<R, RNG>(rng: &mut RNG, lund_a: R, lund_b: R, mt_squared: R) -> R
where
R: RealField + FromPrimitive,
RNG: deep_causality_rand::Rng,
{
let one = R::one();
let small = R::from_f64(0.01).expect("R::from_f64(0.01) failed");
let denom_factor = if lund_a + one > small {
lund_a + one
} else {
small
};
let z_max = one / (one + lund_b * mt_squared / denom_factor);
let f_max = lund_function(z_max, lund_a, lund_b, mt_squared);
let z_min = R::from_f64(0.01).expect("R::from_f64(0.01) failed");
let z_max_cutoff = R::from_f64(0.99).expect("R::from_f64(0.99) failed");
loop {
let u: R = R::from_f64(rng.random::<f64>()).expect("R::from_f64(rng) failed");
let z = z_min + (z_max_cutoff - z_min) * u;
let f_z = lund_function(z, lund_a, lund_b, mt_squared);
let u2: R = R::from_f64(rng.random::<f64>()).expect("R::from_f64(rng) failed");
if u2 * f_max < f_z {
return z;
}
}
}
fn lund_function<R: RealField>(z: R, a: R, b: R, mt_sq: R) -> R {
if z <= R::zero() || z >= R::one() {
return R::zero();
}
(R::one() / z) * (R::one() - z).powf(a) * (-b * mt_sq / z).exp()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lightcone_round_trip() {
let p = FourMomentum::<f64>::new(10.0, 1.0, 2.0, 8.0);
let lc: LightconeEndpoint<f64> = LightconeEndpoint::from_four_momentum(&p);
let p_back = lc.to_four_momentum();
assert!((p.e() - p_back.e()).abs() < 1e-10);
assert!((p.px() - p_back.px()).abs() < 1e-10);
assert!((p.py() - p_back.py()).abs() < 1e-10);
assert!((p.pz() - p_back.pz()).abs() < 1e-10);
}
#[test]
fn test_lund_function_bounds() {
assert_eq!(lund_function::<f64>(0.0, 0.68, 0.98, 0.5), 0.0);
assert_eq!(lund_function::<f64>(1.0, 0.68, 0.98, 0.5), 0.0);
assert!(lund_function::<f64>(0.5, 0.68, 0.98, 0.5) > 0.0);
}
}