use crate::Field;
use nalgebra::{Matrix4, Vector3, Vector4};
use std::{
fmt::Display,
ops::{Add, Sub},
};
#[derive(Debug, Clone, PartialEq, Copy, Default)]
pub struct FourMomentum<F: Field + 'static>(Vector4<F>);
impl<F: Field> Eq for FourMomentum<F> {}
impl<F: Field> Display for FourMomentum<F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"[{}, ({}, {}, {})]",
self.e(),
self.px(),
self.py(),
self.pz(),
)
}
}
impl<F: Field> FourMomentum<F> {
#[allow(clippy::missing_const_for_fn)]
pub fn new(e: F, px: F, py: F, pz: F) -> Self {
Self(Vector4::new(e, px, py, pz))
}
#[allow(clippy::missing_const_for_fn)]
pub fn e(&self) -> F {
self.0[0]
}
#[allow(clippy::missing_const_for_fn)]
pub fn px(&self) -> F {
self.0[1]
}
#[allow(clippy::missing_const_for_fn)]
pub fn py(&self) -> F {
self.0[2]
}
#[allow(clippy::missing_const_for_fn)]
pub fn pz(&self) -> F {
self.0[3]
}
pub fn set_e(&mut self, value: F) {
self.0[0] = value;
}
pub fn set_px(&mut self, value: F) {
self.0[1] = value;
}
pub fn set_py(&mut self, value: F) {
self.0[2] = value;
}
pub fn set_pz(&mut self, value: F) {
self.0[3] = value;
}
#[allow(clippy::suboptimal_flops)]
pub fn m2(&self) -> F {
F::powi(self.e(), 2) - F::powi(self.px(), 2) - F::powi(self.py(), 2) - F::powi(self.pz(), 2)
}
pub fn m(&self) -> F {
F::sqrt(self.m2())
}
pub fn boost_along(&self, other: &Self) -> Self {
let m_boost = other.boost_matrix();
(m_boost * self.0).into()
}
pub fn momentum(&self) -> Vector3<F> {
Vector3::new(self.px(), self.py(), self.pz())
}
pub fn costheta(&self) -> F {
let v = self.momentum();
let r = F::sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
v.z / r
}
pub fn theta_cos(&self) -> F {
self.costheta()
}
pub fn theta(&self) -> F {
F::acos(self.costheta())
}
pub fn phi(&self) -> F {
let v = self.momentum();
F::atan2(v.y, v.x)
}
pub fn beta3(&self) -> Vector3<F> {
self.momentum() / self.e()
}
pub fn direction(&self) -> Vector3<F> {
let v = self.momentum();
let mag = F::sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
v / mag
}
pub fn boost_matrix(&self) -> Matrix4<F> {
let b = self.beta3();
let b2 = b.dot(&b);
let g = F::one() / F::sqrt(F::one() - b2);
Matrix4::new(
g,
-g * b[0],
-g * b[1],
-g * b[2],
-g * b[0],
F::one() + (g - F::one()) * b[0] * b[0] / b2,
(g - F::one()) * b[0] * b[1] / b2,
(g - F::one()) * b[0] * b[2] / b2,
-g * b[1],
(g - F::one()) * b[1] * b[0] / b2,
F::one() + (g - F::one()) * b[1] * b[1] / b2,
(g - F::one()) * b[1] * b[2] / b2,
-g * b[2],
(g - F::one()) * b[2] * b[0] / b2,
(g - F::one()) * b[2] * b[1] / b2,
F::one() + (g - F::one()) * b[2] * b[2] / b2,
)
}
}
impl<F: Field> From<FourMomentum<F>> for Vector4<F> {
fn from(val: FourMomentum<F>) -> Self {
Self::new(val.e(), val.px(), val.py(), val.pz())
}
}
impl<F: Field> From<&FourMomentum<F>> for Vector4<F> {
fn from(val: &FourMomentum<F>) -> Self {
Self::new(val.e(), val.px(), val.py(), val.pz())
}
}
impl<F: Field> From<Vector4<F>> for FourMomentum<F> {
fn from(value: Vector4<F>) -> Self {
Self(value)
}
}
impl<F: Field> From<&Vector4<F>> for FourMomentum<F> {
fn from(value: &Vector4<F>) -> Self {
Self(*value)
}
}
impl<F: Field> From<Vec<F>> for FourMomentum<F> {
fn from(value: Vec<F>) -> Self {
Self::new(value[0], value[1], value[2], value[3])
}
}
impl<F: Field> From<&Vec<F>> for FourMomentum<F> {
fn from(value: &Vec<F>) -> Self {
Self::new(value[0], value[1], value[2], value[3])
}
}
impl<F: Field> Add for FourMomentum<F> {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl<F: Field> Add for &FourMomentum<F> {
type Output = <FourMomentum<F> as Add>::Output;
fn add(self, rhs: &FourMomentum<F>) -> Self::Output {
FourMomentum::add(*self, *rhs)
}
}
impl<F: Field> Sub for FourMomentum<F> {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0)
}
}
impl<F: Field> Sub for &FourMomentum<F> {
type Output = <FourMomentum<F> as Sub>::Output;
fn sub(self, rhs: &FourMomentum<F>) -> Self::Output {
FourMomentum::sub(*self, *rhs)
}
}
impl<F: Field> std::iter::Sum<Self> for FourMomentum<F> {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::default(), |a, b| a + b)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::assert_is_close;
#[test]
fn test_set_components() {
let mut p = FourMomentum::<f64>::default();
p.set_e(1.0);
p.set_px(2.0);
p.set_py(3.0);
p.set_pz(4.0);
assert_is_close!(p.e(), 1.0, f64);
assert_is_close!(p.px(), 2.0, f64);
assert_is_close!(p.py(), 3.0, f64);
assert_is_close!(p.pz(), 4.0, f64);
}
#[test]
fn test_sum() {
let a = FourMomentum::new(0.1, 0.2, 0.3, 0.4);
let b = FourMomentum::new(1.0, 2.0, 3.0, 4.0);
let c = FourMomentum::new(10.0, 20.0, 30.0, 40.0);
let d: FourMomentum<f64> = [a, b, c].into_iter().sum();
assert_is_close!(d.e(), 11.1, f64);
assert_is_close!(d.px(), 22.2, f64);
assert_is_close!(d.py(), 33.3, f64);
assert_is_close!(d.pz(), 44.4, f64);
}
#[test]
fn test_ops() {
let a = FourMomentum::new(0.1, 0.2, 0.3, 0.4);
let b = FourMomentum::new(1.0, 2.0, 3.0, 4.0);
let c = a + b;
let d = b - a;
assert_is_close!(c.e(), 1.1, f64);
assert_is_close!(c.px(), 2.2, f64);
assert_is_close!(c.py(), 3.3, f64);
assert_is_close!(c.pz(), 4.4, f64);
assert_is_close!(d.e(), 0.9, f64);
assert_is_close!(d.px(), 1.8, f64);
assert_is_close!(d.py(), 2.7, f64);
assert_is_close!(d.pz(), 3.6, f64);
}
}