use super::grid::{Grid, GridError, Order};
use super::lumi::LumiCache;
use super::subgrid::Subgrid;
use ndarray::Array4;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::fmt::{self, Display, Formatter};
use std::io::Write;
use thiserror::Error;
#[repr(transparent)]
pub struct FkTable {
grid: Grid,
}
#[derive(Debug, Error)]
pub enum TryFromGridError {
#[error("multiple scales detected")]
MultipleScales,
#[error("different x grids detected")]
NonUniqueGrids,
#[error("complicated luminosity function detected")]
InvalidLumi,
#[error("multiple orders detected")]
NonTrivialOrder,
#[error("metadata is missing: expected key `{0}` to have a value")]
MetadataMissing(String),
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FkAssumptions {
Nf6Ind,
Nf6Sym,
Nf5Ind,
Nf5Sym,
Nf4Ind,
Nf4Sym,
Nf3Ind,
Nf3Sym,
}
#[derive(Debug, Error, PartialEq)]
#[error("unknown variant for FkAssumptions: {variant}")]
pub struct UnknownFkAssumption {
variant: String,
}
impl Display for FkAssumptions {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::Nf6Ind => "Nf6Ind",
Self::Nf6Sym => "Nf6Sym",
Self::Nf5Ind => "Nf5Ind",
Self::Nf5Sym => "Nf5Sym",
Self::Nf4Ind => "Nf4Ind",
Self::Nf4Sym => "Nf4Sym",
Self::Nf3Ind => "Nf3Ind",
Self::Nf3Sym => "Nf3Sym",
}
)
}
}
impl TryFrom<&str> for FkAssumptions {
type Error = UnknownFkAssumption;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Ok(match value {
"Nf6Ind" => Self::Nf6Ind,
"Nf6Sym" => Self::Nf6Sym,
"Nf5Ind" => Self::Nf5Ind,
"Nf5Sym" => Self::Nf5Sym,
"Nf4Ind" => Self::Nf4Ind,
"Nf4Sym" => Self::Nf4Sym,
"Nf3Ind" => Self::Nf3Ind,
"Nf3Sym" => Self::Nf3Sym,
_ => {
return Err(UnknownFkAssumption {
variant: value.to_string(),
});
}
})
}
}
impl FkTable {
#[must_use]
pub const fn grid(&self) -> &Grid {
&self.grid
}
#[must_use]
pub fn table(&self) -> Array4<f64> {
let first_non_empty_subgrid = self
.grid
.subgrids()
.iter()
.find(|s| !s.is_empty())
.unwrap_or_else(|| unreachable!());
let mut subgrid = Array4::zeros((
self.bins(),
self.grid.lumi().len(),
first_non_empty_subgrid.x1_grid().len(),
first_non_empty_subgrid.x2_grid().len(),
));
for bin in 0..self.bins() {
for lumi in 0..self.grid.lumi().len() {
for ((_, ix1, ix2), value) in self.grid().subgrid(0, bin, lumi).iter() {
subgrid[[bin, lumi, ix1, ix2]] = *value;
}
}
}
subgrid
}
#[must_use]
pub fn bins(&self) -> usize {
self.grid.bin_info().bins()
}
#[must_use]
pub fn bin_normalizations(&self) -> Vec<f64> {
self.grid.bin_info().normalizations()
}
#[must_use]
pub fn bin_dimensions(&self) -> usize {
self.grid.bin_info().dimensions()
}
#[must_use]
pub fn bin_left(&self, dimension: usize) -> Vec<f64> {
self.grid.bin_info().left(dimension)
}
#[must_use]
pub fn bin_right(&self, dimension: usize) -> Vec<f64> {
self.grid.bin_info().right(dimension)
}
#[must_use]
pub const fn key_values(&self) -> Option<&HashMap<String, String>> {
self.grid.key_values()
}
#[must_use]
pub fn lumi(&self) -> Vec<(i32, i32)> {
self.grid
.lumi()
.iter()
.map(|entry| (entry.entry()[0].0, entry.entry()[0].1))
.collect()
}
#[must_use]
pub fn muf2(&self) -> f64 {
for bin in 0..self.grid.bin_info().bins() {
for lumi in 0..self.grid.lumi().len() {
let subgrid = self.grid.subgrid(0, bin, lumi);
if subgrid.is_empty() {
continue;
}
return subgrid.mu2_grid()[0].fac;
}
}
unreachable!();
}
#[must_use]
pub fn x_grid(&self) -> Vec<f64> {
for bin in 0..self.grid.bin_info().bins() {
for lumi in 0..self.grid.lumi().len() {
let subgrid = self.grid.subgrid(0, bin, lumi);
if subgrid.is_empty() {
continue;
}
if subgrid.x1_grid().is_empty() {
return subgrid.x2_grid().into_owned();
}
return subgrid.x1_grid().into_owned();
}
}
unreachable!();
}
pub fn write(&self, writer: impl Write) -> Result<(), GridError> {
self.grid.write(writer)
}
pub fn write_lz4(&self, writer: impl Write) -> Result<(), GridError> {
self.grid.write_lz4(writer)
}
pub fn convolute(
&self,
lumi_cache: &mut LumiCache,
bin_indices: &[usize],
lumi_mask: &[bool],
) -> Vec<f64> {
self.grid
.convolute(lumi_cache, &[], bin_indices, lumi_mask, &[(1.0, 1.0)])
}
pub fn set_key_value(&mut self, key: &str, value: &str) {
self.grid.set_key_value(key, value);
}
pub fn optimize(&mut self, assumptions: FkAssumptions) {
let mut add = Vec::new();
match assumptions {
FkAssumptions::Nf6Ind => {
}
FkAssumptions::Nf6Sym => {
add.push((235, 200));
}
FkAssumptions::Nf5Ind => {
add.extend_from_slice(&[(235, 200), (135, 100)]);
}
FkAssumptions::Nf5Sym => {
add.extend_from_slice(&[(235, 200), (135, 100), (224, 200)]);
}
FkAssumptions::Nf4Ind => {
add.extend_from_slice(&[(235, 200), (135, 100), (224, 200), (124, 100)]);
}
FkAssumptions::Nf4Sym => {
add.extend_from_slice(&[
(235, 200),
(135, 100),
(224, 200),
(124, 100),
(215, 200),
]);
}
FkAssumptions::Nf3Ind => {
add.extend_from_slice(&[
(235, 200),
(135, 100),
(224, 200),
(124, 100),
(215, 200),
(115, 100),
]);
}
FkAssumptions::Nf3Sym => {
add.extend_from_slice(&[
(235, 200),
(135, 100),
(224, 200),
(124, 100),
(215, 200),
(115, 100),
(208, 200),
]);
}
}
self.grid.rewrite_lumi(&add, &[]);
self.grid
.set_key_value("fk_assumptions", &assumptions.to_string());
self.grid.optimize();
}
}
impl TryFrom<Grid> for FkTable {
type Error = TryFromGridError;
fn try_from(grid: Grid) -> Result<Self, Self::Error> {
let mut muf2 = -1.0;
let mut x_grid = Vec::new();
if grid.orders()
!= [Order {
alphas: 0,
alpha: 0,
logxir: 0,
logxif: 0,
}]
{
return Err(TryFromGridError::NonTrivialOrder);
}
for bin in 0..grid.bin_info().bins() {
for lumi in 0..grid.lumi().len() {
let subgrid = grid.subgrid(0, bin, lumi);
if subgrid.is_empty() {
continue;
}
let mu2_grid = subgrid.mu2_grid();
let x1_grid = subgrid.x1_grid();
let x2_grid = subgrid.x2_grid();
if mu2_grid.len() > 1 {
return Err(TryFromGridError::MultipleScales);
}
if muf2 < 0.0 {
muf2 = mu2_grid[0].fac;
if x1_grid.len() == 1 {
x_grid = x2_grid.into_owned();
} else {
x_grid = x1_grid.into_owned();
}
} else {
if muf2 != mu2_grid[0].fac {
return Err(TryFromGridError::MultipleScales);
}
if x1_grid.len() != 1 && x1_grid != x_grid {
return Err(TryFromGridError::NonUniqueGrids);
}
if x2_grid.len() != 1 && x2_grid != x_grid {
return Err(TryFromGridError::NonUniqueGrids);
}
}
}
}
for lumi in grid.lumi() {
let entry = lumi.entry();
if entry.len() != 1 || entry[0].2 != 1.0 {
return Err(TryFromGridError::InvalidLumi);
}
}
if (1..grid.lumi().len()).any(|i| grid.lumi()[i..].contains(&grid.lumi()[i - 1])) {
return Err(TryFromGridError::InvalidLumi);
}
if let Some(key_values) = grid.key_values() {
let keys = vec![
"initial_state_1".to_string(),
"initial_state_2".to_string(),
"lumi_id_types".to_string(),
];
for key in keys {
if !key_values.contains_key(&key) {
return Err(TryFromGridError::MetadataMissing(key));
}
}
} else {
return Err(TryFromGridError::MetadataMissing(
"initial_states_1".to_string(),
));
}
Ok(Self { grid })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fk_assumptions_try_from() {
assert_eq!(FkAssumptions::try_from("Nf6Ind"), Ok(FkAssumptions::Nf6Ind));
assert_eq!(FkAssumptions::try_from("Nf6Sym"), Ok(FkAssumptions::Nf6Sym));
assert_eq!(FkAssumptions::try_from("Nf5Ind"), Ok(FkAssumptions::Nf5Ind));
assert_eq!(FkAssumptions::try_from("Nf5Sym"), Ok(FkAssumptions::Nf5Sym));
assert_eq!(FkAssumptions::try_from("Nf4Ind"), Ok(FkAssumptions::Nf4Ind));
assert_eq!(FkAssumptions::try_from("Nf4Sym"), Ok(FkAssumptions::Nf4Sym));
assert_eq!(FkAssumptions::try_from("Nf3Ind"), Ok(FkAssumptions::Nf3Ind));
assert_eq!(FkAssumptions::try_from("Nf3Sym"), Ok(FkAssumptions::Nf3Sym));
assert_eq!(
FkAssumptions::try_from("XXXXXX"),
Err(UnknownFkAssumption {
variant: "XXXXXX".to_string()
})
);
}
#[test]
fn fk_assumptions_display() {
assert_eq!(format!("{}", FkAssumptions::Nf6Ind), "Nf6Ind");
assert_eq!(format!("{}", FkAssumptions::Nf6Sym), "Nf6Sym");
assert_eq!(format!("{}", FkAssumptions::Nf5Ind), "Nf5Ind");
assert_eq!(format!("{}", FkAssumptions::Nf5Sym), "Nf5Sym");
assert_eq!(format!("{}", FkAssumptions::Nf4Ind), "Nf4Ind");
assert_eq!(format!("{}", FkAssumptions::Nf4Sym), "Nf4Sym");
assert_eq!(format!("{}", FkAssumptions::Nf3Ind), "Nf3Ind");
assert_eq!(format!("{}", FkAssumptions::Nf3Sym), "Nf3Sym");
}
}