use crate::deconvolution::ls_deconvolution;
use alpha_g_detector::alpha16::{aw_map::TPC_ANODE_WIRES, ADC32_RATE};
use dyn_stack::ReborrowMut;
use lazy_static::lazy_static;
const BIN_WIDTH: usize = (1.0e9 / ADC32_RATE) as usize;
const RESPONSE_BYTES: &[u8] = include_bytes!("../../data/simulation/tpc_response/wires.json");
lazy_static! {
static ref WIRE_RESPONSE: Vec<f64> = {
let raw: Vec<f64> = serde_json::from_slice(RESPONSE_BYTES).unwrap();
raw
.chunks_exact(BIN_WIDTH)
.map(|chunk| chunk.iter().sum())
.collect()
};
}
const NEIGHBOR_FACTORS: [f64; 5] = [1.0, -0.1275, -0.0365, -0.012, -0.0042];
pub(crate) fn contiguous_ranges(
wire_signals: &[Option<Vec<f64>>; TPC_ANODE_WIRES],
) -> Vec<(usize, usize)> {
let mut ranges = Vec::new();
let mut start = 0;
let mut end = 0;
while start < TPC_ANODE_WIRES {
while end < TPC_ANODE_WIRES && wire_signals[end].is_some() {
end += 1;
}
if start < end {
ranges.push((start, end));
}
start = end + 1;
end = start;
}
if ranges.len() > 1 {
if let Some((0, _)) = ranges.first() {
if let Some((_, TPC_ANODE_WIRES)) = ranges.last() {
let (start_f, _) = ranges.pop().unwrap();
let (_, end_i) = ranges.swap_remove(0);
ranges.push((start_f, end_i));
}
}
}
ranges
}
pub(crate) fn wire_range_deconvolution(
wire_signals: &[Option<Vec<f64>>; TPC_ANODE_WIRES],
range: (usize, usize),
) -> Vec<(usize, Vec<f64>)> {
let (i, j) = problem_dimensions(wire_signals, range);
let mut a = a_matrix(j);
let mut y = y_matrix(wire_signals, range);
let mut mem = dyn_stack::GlobalMemBuffer::new(
faer_cholesky::llt::compute::cholesky_in_place_req::<f64>(
j,
faer_core::Parallelism::None,
Default::default(),
)
.unwrap(),
);
let mut stack = dyn_stack::DynStack::new(&mut mem);
faer_cholesky::llt::compute::cholesky_in_place(
a.as_mut(),
faer_core::Parallelism::None,
stack.rb_mut(),
Default::default(),
)
.unwrap();
let mut mem = dyn_stack::GlobalMemBuffer::new(
faer_cholesky::llt::solve::solve_transpose_in_place_req::<f64>(
j,
i,
faer_core::Parallelism::None,
)
.unwrap(),
);
let mut stack = dyn_stack::DynStack::new(&mut mem);
faer_cholesky::llt::solve::solve_transpose_in_place_with_conj(
a.as_ref(),
faer_core::Conj::No,
y.as_mut().transpose(),
faer_core::Parallelism::None,
stack.rb_mut(),
);
let mut sol = Vec::with_capacity(j);
for column in 0..j {
let signal = (0..i).map(|row| y.read(row, column)).collect::<Vec<_>>();
sol.push(ls_deconvolution(&signal, &WIRE_RESPONSE, 0..=1, 3..=12));
}
range_to_indices(range).zip(sol).collect()
}
fn range_to_indices(range: (usize, usize)) -> Box<dyn Iterator<Item = usize>> {
let (first, last) = range;
if first < last {
Box::new(first..last)
} else {
Box::new((first..TPC_ANODE_WIRES).chain(0..last))
}
}
fn range_to_len(range: (usize, usize)) -> usize {
let (first, last) = range;
if first < last {
last - first
} else {
TPC_ANODE_WIRES - first + last
}
}
fn a_matrix(n: usize) -> faer_core::Mat<f64> {
faer_core::Mat::with_dims(n, n, |i, j| {
let diff = if i > j { i - j } else { j - i };
NEIGHBOR_FACTORS.get(diff).copied().unwrap_or(0.0)
})
}
fn problem_dimensions(
wire_signals: &[Option<Vec<f64>>; TPC_ANODE_WIRES],
range: (usize, usize),
) -> (usize, usize) {
let max_len = range_to_indices(range)
.map(|i| wire_signals[i].as_ref().unwrap().len())
.max()
.unwrap();
(max_len, range_to_len(range))
}
fn y_matrix(
wire_signals: &[Option<Vec<f64>>; TPC_ANODE_WIRES],
range: (usize, usize),
) -> faer_core::Mat<f64> {
let (i, j) = problem_dimensions(wire_signals, range);
faer_core::Mat::with_dims(i, j, |i, j| {
let wire = range_to_indices(range).nth(j).unwrap();
wire_signals[wire]
.as_ref()
.unwrap()
.get(i)
.copied()
.unwrap_or(0.0)
})
}
#[cfg(test)]
mod tests;