use super::grid::Grid;
use super::pids;
use super::subgrid::{Mu2, Subgrid};
use itertools::Itertools;
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
pub struct LumiEntry {
entry: Vec<(i32, i32, f64)>,
}
impl LumiEntry {
#[must_use]
pub fn new(mut entry: Vec<(i32, i32, f64)>) -> Self {
assert!(!entry.is_empty());
entry.sort_by(|x, y| (x.0, x.1).cmp(&(y.0, y.1)));
Self {
entry: entry
.into_iter()
.coalesce(|lhs, rhs| {
if (lhs.0, lhs.1) == (rhs.0, rhs.1) {
Ok((lhs.0, lhs.1, lhs.2 + rhs.2))
} else {
Err((lhs, rhs))
}
})
.collect(),
}
}
pub fn translate(entry: &Self, translator: &dyn Fn(i32) -> Vec<(i32, f64)>) -> Self {
let mut tuples = Vec::new();
for &(a, b, factor) in &entry.entry {
for (aid, af) in translator(a) {
for (bid, bf) in translator(b) {
tuples.push((aid, bid, factor * af * bf));
}
}
}
Self::new(tuples)
}
#[must_use]
pub fn entry(&self) -> &[(i32, i32, f64)] {
&self.entry
}
#[must_use]
pub fn transpose(&self) -> Self {
Self::new(self.entry.iter().map(|(a, b, c)| (*b, *a, *c)).collect())
}
}
#[macro_export]
macro_rules! lumi_entry {
($a:expr, $b:expr, $factor:expr $(; $c:expr, $d:expr, $fac:expr)*) => {
$crate::lumi::LumiEntry::new(vec![($a, $b, $factor), $(($c, $d, $fac)),*])
};
}
enum Pdfs<'a> {
Two {
xfx1: &'a mut dyn FnMut(i32, f64, f64) -> f64,
xfx1_cache: FxHashMap<(i32, usize, usize), f64>,
xfx2: &'a mut dyn FnMut(i32, f64, f64) -> f64,
xfx2_cache: FxHashMap<(i32, usize, usize), f64>,
},
One {
xfx: &'a mut dyn FnMut(i32, f64, f64) -> f64,
xfx_cache: FxHashMap<(i32, usize, usize), f64>,
},
}
impl<'a> Pdfs<'a> {
pub fn clear(&mut self) {
match self {
Self::One { xfx_cache, .. } => xfx_cache.clear(),
Self::Two {
xfx1_cache,
xfx2_cache,
..
} => {
xfx1_cache.clear();
xfx2_cache.clear();
}
}
}
}
pub struct LumiCache<'a> {
pdfs: Pdfs<'a>,
alphas: &'a mut dyn FnMut(f64) -> f64,
alphas_cache: Vec<f64>,
mur2_grid: Vec<f64>,
muf2_grid: Vec<f64>,
x_grid: Vec<f64>,
imur2: Vec<usize>,
imuf2: Vec<usize>,
ix1: Vec<usize>,
ix2: Vec<usize>,
pdg1: i32,
pdg2: i32,
cc1: i32,
cc2: i32,
}
impl<'a> LumiCache<'a> {
pub fn with_two(
pdg1: i32,
xfx1: &'a mut dyn FnMut(i32, f64, f64) -> f64,
pdg2: i32,
xfx2: &'a mut dyn FnMut(i32, f64, f64) -> f64,
alphas: &'a mut dyn FnMut(f64) -> f64,
) -> Self {
Self {
pdfs: Pdfs::Two {
xfx1,
xfx1_cache: FxHashMap::default(),
xfx2,
xfx2_cache: FxHashMap::default(),
},
alphas,
alphas_cache: vec![],
mur2_grid: vec![],
muf2_grid: vec![],
x_grid: vec![],
imur2: Vec::new(),
imuf2: Vec::new(),
ix1: Vec::new(),
ix2: Vec::new(),
pdg1,
pdg2,
cc1: 0,
cc2: 0,
}
}
pub fn with_one(
pdg: i32,
xfx: &'a mut dyn FnMut(i32, f64, f64) -> f64,
alphas: &'a mut dyn FnMut(f64) -> f64,
) -> Self {
Self {
pdfs: Pdfs::One {
xfx,
xfx_cache: FxHashMap::default(),
},
alphas,
alphas_cache: vec![],
mur2_grid: vec![],
muf2_grid: vec![],
x_grid: vec![],
imur2: Vec::new(),
imuf2: Vec::new(),
ix1: Vec::new(),
ix2: Vec::new(),
pdg1: pdg,
pdg2: pdg,
cc1: 0,
cc2: 0,
}
}
pub(crate) fn setup(&mut self, grid: &Grid, xi: &[(f64, f64)]) -> Result<(), ()> {
let pdga = grid.key_values().map_or(2212, |kv| {
kv.get("initial_state_1")
.map_or(2212, |s| s.parse::<i32>().unwrap())
});
let pdgb = grid.key_values().map_or(2212, |kv| {
kv.get("initial_state_2")
.map_or(2212, |s| s.parse::<i32>().unwrap())
});
let has_pdfa = !grid
.lumi()
.iter()
.all(|entry| entry.entry().iter().all(|&(a, _, _)| a == pdga));
let has_pdfb = !grid
.lumi()
.iter()
.all(|entry| entry.entry().iter().all(|&(_, b, _)| b == pdgb));
let cc1 = if !has_pdfa {
0
} else if self.pdg1 == pdga {
1
} else if self.pdg1 == -pdga {
-1
} else {
return Err(());
};
let cc2 = if !has_pdfb {
0
} else if self.pdg2 == pdgb {
1
} else if self.pdg2 == -pdgb {
-1
} else {
return Err(());
};
self.clear();
let mut x_grid: Vec<_> = grid
.subgrids()
.iter()
.filter_map(|subgrid| {
if subgrid.is_empty() {
None
} else {
let mut vec = subgrid.x1_grid().into_owned();
vec.extend_from_slice(&subgrid.x2_grid());
Some(vec)
}
})
.flatten()
.collect();
x_grid.sort_by(|a, b| a.partial_cmp(b).unwrap_or_else(|| unreachable!()));
x_grid.dedup();
let mut mur2_grid: Vec<_> = grid
.subgrids()
.iter()
.filter_map(|subgrid| {
if subgrid.is_empty() {
None
} else {
Some(subgrid.mu2_grid().into_owned())
}
})
.flatten()
.flat_map(|Mu2 { ren, .. }| {
xi.iter()
.map(|(xir, _)| xir * xir * ren)
.collect::<Vec<_>>()
})
.collect();
mur2_grid.sort_by(|a, b| a.partial_cmp(b).unwrap_or_else(|| unreachable!()));
mur2_grid.dedup();
let mut muf2_grid: Vec<_> = grid
.subgrids()
.iter()
.filter_map(|subgrid| {
if subgrid.is_empty() {
None
} else {
Some(subgrid.mu2_grid().into_owned())
}
})
.flatten()
.flat_map(|Mu2 { fac, .. }| {
xi.iter()
.map(|(_, xif)| xif * xif * fac)
.collect::<Vec<_>>()
})
.collect();
muf2_grid.sort_by(|a, b| a.partial_cmp(b).unwrap_or_else(|| unreachable!()));
muf2_grid.dedup();
self.alphas_cache = mur2_grid.iter().map(|&mur2| (self.alphas)(mur2)).collect();
self.mur2_grid = mur2_grid;
self.muf2_grid = muf2_grid;
self.x_grid = x_grid;
self.cc1 = cc1;
self.cc2 = cc2;
Ok(())
}
pub fn xfx1(&mut self, pdg_id: i32, ix1: usize, imu2: usize) -> f64 {
let ix1 = self.ix1[ix1];
let x = self.x_grid[ix1];
if self.cc1 == 0 {
x
} else {
let imuf2 = self.imuf2[imu2];
let muf2 = self.muf2_grid[imuf2];
let pid = if self.cc1 == 1 {
pdg_id
} else {
pids::charge_conjugate_pdg_pid(pdg_id)
};
let (xfx, xfx_cache) = match &mut self.pdfs {
Pdfs::One { xfx, xfx_cache, .. } => (xfx, xfx_cache),
Pdfs::Two {
xfx1, xfx1_cache, ..
} => (xfx1, xfx1_cache),
};
*xfx_cache
.entry((pid, ix1, imuf2))
.or_insert_with(|| xfx(pid, x, muf2))
}
}
pub fn xfx2(&mut self, pdg_id: i32, ix2: usize, imu2: usize) -> f64 {
let ix2 = self.ix2[ix2];
let x = self.x_grid[ix2];
if self.cc2 == 0 {
x
} else {
let imuf2 = self.imuf2[imu2];
let muf2 = self.muf2_grid[imuf2];
let pid = if self.cc2 == 1 {
pdg_id
} else {
pids::charge_conjugate_pdg_pid(pdg_id)
};
let (xfx, xfx_cache) = match &mut self.pdfs {
Pdfs::One { xfx, xfx_cache, .. } => (xfx, xfx_cache),
Pdfs::Two {
xfx2, xfx2_cache, ..
} => (xfx2, xfx2_cache),
};
*xfx_cache
.entry((pid, ix2, imuf2))
.or_insert_with(|| xfx(pid, x, muf2))
}
}
pub fn alphas(&mut self, imu2: usize) -> f64 {
self.alphas_cache[self.imur2[imu2]]
}
pub fn clear(&mut self) {
self.alphas_cache.clear();
self.pdfs.clear();
self.mur2_grid.clear();
self.muf2_grid.clear();
self.x_grid.clear();
}
pub fn set_grids(
&mut self,
mu2_grid: &[Mu2],
x1_grid: &[f64],
x2_grid: &[f64],
xir: f64,
xif: f64,
) {
self.imur2 = mu2_grid
.iter()
.map(|Mu2 { ren, .. }| {
self.mur2_grid
.iter()
.position(|&mur2| mur2 == xir * xir * ren)
.unwrap_or_else(|| unreachable!())
})
.collect();
self.imuf2 = mu2_grid
.iter()
.map(|Mu2 { fac, .. }| {
self.muf2_grid
.iter()
.position(|&muf2| muf2 == xif * xif * fac)
.unwrap_or_else(|| unreachable!())
})
.collect();
self.ix1 = x1_grid
.iter()
.map(|x1| {
self.x_grid
.iter()
.position(|x| x1 == x)
.unwrap_or_else(|| unreachable!())
})
.collect();
self.ix2 = x2_grid
.iter()
.map(|x2| {
self.x_grid
.iter()
.position(|x| x2 == x)
.unwrap_or_else(|| unreachable!())
})
.collect();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pids;
#[test]
fn translate() {
let lumi = LumiEntry::translate(&lumi_entry![103, 203, 2.0], &pids::evol_to_pdg_mc_ids);
assert_eq!(
lumi,
lumi_entry![ 2, 2, 2.0; 2, -2, -2.0; 2, 1, -2.0; 2, -1, 2.0;
-2, 2, 2.0; -2, -2, -2.0; -2, 1, -2.0; -2, -1, 2.0;
1, 2, -2.0; 1, -2, 2.0; 1, 1, 2.0; 1, -1, -2.0;
-1, 2, -2.0; -1, -2, 2.0; -1, 1, 2.0; -1, -1, -2.0]
);
}
}