use crate::padwing::{AfterId, BoardId, PadChannelId};
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::f64::consts::PI;
use thiserror::Error;
pub const DETECTOR_LENGTH: f64 = 2.304;
pub const CATHODE_PADS_RADIUS: f64 = 0.190;
pub const PWB_PAD_COLUMNS: usize = 4;
pub const PWB_PAD_ROWS: usize = 72;
pub const TPC_PWB_COLUMNS: usize = 8;
pub const TPC_PWB_ROWS: usize = 8;
pub const TPC_PAD_COLUMNS: usize = TPC_PWB_COLUMNS * PWB_PAD_COLUMNS;
pub const TPC_PAD_ROWS: usize = TPC_PWB_ROWS * PWB_PAD_ROWS;
pub const TPC_PADS: usize = TPC_PAD_COLUMNS * TPC_PAD_ROWS;
pub const PAD_PITCH_Z: f64 = DETECTOR_LENGTH / (TPC_PAD_ROWS as f64);
pub const PAD_PITCH_PHI: f64 = 2.0 * PI / (TPC_PAD_COLUMNS as f64);
#[derive(Debug, Error)]
#[error("unknown conversion from {input} to row or column")]
pub struct TryPositionFromIndexError {
input: usize,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TpcPwbColumn(usize);
impl TryFrom<usize> for TpcPwbColumn {
type Error = TryPositionFromIndexError;
fn try_from(value: usize) -> Result<Self, Self::Error> {
if value < TPC_PWB_COLUMNS {
Ok(Self(value))
} else {
Err(TryPositionFromIndexError { input: value })
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TpcPwbRow(usize);
impl TryFrom<usize> for TpcPwbRow {
type Error = TryPositionFromIndexError;
fn try_from(value: usize) -> Result<Self, Self::Error> {
if value < TPC_PWB_ROWS {
Ok(Self(value))
} else {
Err(TryPositionFromIndexError { input: value })
}
}
}
const PADWING_BOARDS_4418: [[&str; TPC_PWB_ROWS]; TPC_PWB_COLUMNS] = [
["12", "13", "14", "02", "11", "17", "18", "19"],
["20", "21", "22", "23", "24", "25", "26", "27"],
["46", "29", "08", "77", "10", "33", "34", "35"],
["36", "37", "01", "39", "76", "41", "42", "40"],
["44", "49", "07", "78", "03", "04", "45", "15"],
["52", "53", "54", "55", "56", "57", "58", "05"],
["60", "00", "06", "63", "64", "65", "66", "67"],
["68", "69", "70", "71", "72", "73", "74", "75"],
];
const PADWING_BOARDS_10418: [[&str; TPC_PWB_ROWS]; TPC_PWB_COLUMNS] = [
["12", "13", "14", "02", "11", "17", "18", "19"],
["20", "21", "22", "23", "24", "25", "26", "27"],
["90", "29", "08", "85", "10", "33", "34", "35"],
["36", "37", "01", "39", "76", "41", "42", "40"],
["89", "49", "07", "87", "03", "04", "84", "91"],
["52", "53", "54", "55", "56", "57", "58", "81"],
["60", "00", "44", "63", "64", "65", "66", "67"],
["68", "69", "70", "71", "72", "73", "74", "75"],
];
fn inverse_pwb_map(
map: [[&str; TPC_PWB_ROWS]; TPC_PWB_COLUMNS],
) -> HashMap<BoardId, TpcPwbPosition> {
let mut inverse = HashMap::new();
for (column, row) in map.iter().enumerate() {
for (row, name) in row.iter().enumerate() {
inverse.insert(
BoardId::try_from(*name).unwrap(),
TpcPwbPosition {
column: TpcPwbColumn::try_from(column).unwrap(),
row: TpcPwbRow::try_from(row).unwrap(),
},
);
}
}
inverse
}
lazy_static! {
static ref INV_PADWING_BOARDS_4418: HashMap<BoardId, TpcPwbPosition> =
inverse_pwb_map(PADWING_BOARDS_4418);
static ref INV_PADWING_BOARDS_10418: HashMap<BoardId, TpcPwbPosition> =
inverse_pwb_map(PADWING_BOARDS_10418);
}
#[derive(Debug, Error)]
pub enum MapTpcPwbPositionError {
#[error("no rTPC PWB mapping available for run number {run_number}")]
MissingMap { run_number: u32 },
#[error("pwb `{}` not found in map for run number {run_number}", board_id.name())]
BoardIdNotFound { run_number: u32, board_id: BoardId },
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct TpcPwbPosition {
column: TpcPwbColumn,
row: TpcPwbRow,
}
impl TpcPwbPosition {
pub fn new(column: TpcPwbColumn, row: TpcPwbRow) -> Self {
Self { column, row }
}
pub fn try_new(run_number: u32, board_id: BoardId) -> Result<Self, MapTpcPwbPositionError> {
let position_map = match run_number {
u32::MAX => &*INV_PADWING_BOARDS_4418,
10418.. => &*INV_PADWING_BOARDS_10418,
4418.. => &*INV_PADWING_BOARDS_4418,
_ => return Err(MapTpcPwbPositionError::MissingMap { run_number }),
};
position_map
.get(&board_id)
.copied()
.ok_or(MapTpcPwbPositionError::BoardIdNotFound {
run_number,
board_id,
})
}
pub fn column(&self) -> TpcPwbColumn {
self.column
}
pub fn row(&self) -> TpcPwbRow {
self.row
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PwbPadColumn(usize);
impl TryFrom<usize> for PwbPadColumn {
type Error = TryPositionFromIndexError;
fn try_from(value: usize) -> Result<Self, Self::Error> {
if value < PWB_PAD_COLUMNS {
Ok(Self(value))
} else {
Err(TryPositionFromIndexError { input: value })
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PwbPadRow(usize);
impl TryFrom<usize> for PwbPadRow {
type Error = TryPositionFromIndexError;
fn try_from(value: usize) -> Result<Self, Self::Error> {
if value < PWB_PAD_ROWS {
Ok(Self(value))
} else {
Err(TryPositionFromIndexError { input: value })
}
}
}
lazy_static! {
static ref INV_PADS_0: HashMap<(AfterId, PadChannelId), PwbPadPosition> = {
let mut inverse = HashMap::new();
for after in 0..=3u8 {
let offset = (after % 2) * 36;
for channel in 1..=72u8 {
let mut col: u8;
let mut row: u8;
match channel {
0..=18 => {
col = 1;
row = channel - 1 + offset;
},
19..=36 => {
col = 0;
row = 36 - channel + offset;
},
37..=54 => {
col = 0;
row = 72 - channel + offset;
},
55..=72 => {
col = 1;
row = channel - 37 + offset;
}
_ => unreachable!(),
}
if after > 1 {
col = 3 - col;
row = 71 - row;
}
inverse.insert(
(
AfterId::try_from(after).unwrap(),
PadChannelId::try_from(u16::from(channel)).unwrap(),
),
PwbPadPosition {
column: PwbPadColumn::try_from(usize::from(col)).unwrap(),
row: PwbPadRow::try_from(usize::from(row)).unwrap(),
},
);
}
}
inverse
};
}
#[derive(Debug, Error)]
#[error("no PWB pad mapping available for run number {run_number}")]
pub struct MapPwbPadPositionError {
run_number: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PwbPadPosition {
column: PwbPadColumn,
row: PwbPadRow,
}
impl PwbPadPosition {
pub fn new(column: PwbPadColumn, row: PwbPadRow) -> Self {
Self { column, row }
}
pub fn try_new(
_run_number: u32,
after_id: AfterId,
pad_channel_id: PadChannelId,
) -> Result<PwbPadPosition, MapPwbPadPositionError> {
let position_map = &INV_PADS_0;
Ok(*position_map.get(&(after_id, pad_channel_id)).unwrap())
}
pub fn column(&self) -> PwbPadColumn {
self.column
}
pub fn row(&self) -> PwbPadRow {
self.row
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(try_from = "usize", into = "usize")]
pub struct TpcPadColumn(usize);
impl TryFrom<usize> for TpcPadColumn {
type Error = TryPositionFromIndexError;
fn try_from(value: usize) -> Result<Self, Self::Error> {
if value < TPC_PAD_COLUMNS {
Ok(TpcPadColumn(value))
} else {
Err(TryPositionFromIndexError { input: value })
}
}
}
impl From<TpcPadColumn> for usize {
fn from(pad_column: TpcPadColumn) -> Self {
pad_column.0
}
}
impl TpcPadColumn {
pub fn phi(&self) -> f64 {
let column = self.0;
(column as f64 + 0.5) * PAD_PITCH_PHI
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(try_from = "usize", into = "usize")]
pub struct TpcPadRow(usize);
impl TryFrom<usize> for TpcPadRow {
type Error = TryPositionFromIndexError;
fn try_from(value: usize) -> Result<Self, Self::Error> {
if value < TPC_PAD_ROWS {
Ok(TpcPadRow(value))
} else {
Err(TryPositionFromIndexError { input: value })
}
}
}
impl From<TpcPadRow> for usize {
fn from(pad_row: TpcPadRow) -> Self {
pad_row.0
}
}
impl TpcPadRow {
pub fn z(&self) -> f64 {
let row = self.0;
const DETECTOR_HALF_LENGTH: f64 = 0.5 * DETECTOR_LENGTH;
(row as f64 + 0.5) * PAD_PITCH_Z - DETECTOR_HALF_LENGTH
}
}
#[derive(Debug, Error)]
pub enum MapTpcPadPositionError {
#[error("unable to map PWB in the rTPC")]
BadTpcPwbPosition(#[from] MapTpcPwbPositionError),
#[error("unable to map pad in the PWB")]
BadPwbPadPosition(#[from] MapPwbPadPositionError),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TpcPadPosition {
pub column: TpcPadColumn,
pub row: TpcPadRow,
}
impl TpcPadPosition {
pub fn new(board_position: TpcPwbPosition, pad_position: PwbPadPosition) -> Self {
let TpcPwbPosition { column, row } = board_position;
let PwbPadPosition {
column: pad_column,
row: pad_row,
} = pad_position;
let column = TpcPadColumn::try_from(column.0 * PWB_PAD_COLUMNS + pad_column.0).unwrap();
let row = TpcPadRow::try_from(row.0 * PWB_PAD_ROWS + pad_row.0).unwrap();
TpcPadPosition { column, row }
}
pub fn try_new(
run_number: u32,
board_id: BoardId,
after_id: AfterId,
pad_channel_id: PadChannelId,
) -> Result<Self, MapTpcPadPositionError> {
let board_position = TpcPwbPosition::try_new(run_number, board_id)?;
let pad_position = PwbPadPosition::try_new(run_number, after_id, pad_channel_id)?;
Ok(TpcPadPosition::new(board_position, pad_position))
}
pub fn z(&self) -> f64 {
self.row.z()
}
pub fn phi(&self) -> f64 {
self.column.phi()
}
}
#[cfg(test)]
mod tests;