use molrs::spatial::neighbors::QueryMode;
use molrs::types::F;
use ndarray::Array1;
use crate::compute::result::{ComputeResult, DescriptorRow};
#[derive(Debug, Clone)]
pub struct RDFResult {
pub bin_edges: Array1<F>,
pub bin_centers: Array1<F>,
pub rdf: Array1<F>,
pub n_r: Array1<F>,
pub n_points: usize,
pub n_query_points: usize,
pub mode: QueryMode,
pub volume: F,
pub r_min: F,
pub n_frames: usize,
pub dimensionality: u8,
pub finalized: bool,
}
impl RDFResult {
fn shell_volume(&self, r_inner: F, r_outer: F) -> F {
let pi: F = std::f64::consts::PI;
match self.dimensionality {
2 => pi * (r_outer.powi(2) - r_inner.powi(2)),
_ => (4.0 / 3.0) * pi * (r_outer.powi(3) - r_inner.powi(3)),
}
}
fn compute_normalized(&self) -> Array1<F> {
let nf = self.n_frames.max(1) as F;
let vol = self.volume / nf;
let n_bins = self.n_r.len();
let mut gr = Array1::<F>::zeros(n_bins);
match self.mode {
QueryMode::SelfQuery => {
let n = self.n_points as F / nf;
if vol <= 0.0 || n <= 0.0 {
return gr;
}
let rho = n / vol;
for i in 0..n_bins {
let r_inner = self.bin_edges[i];
let r_outer = self.bin_edges[i + 1];
let v_shell = self.shell_volume(r_inner, r_outer);
let ideal_count = rho * v_shell * n * nf;
if ideal_count > 0.0 {
gr[i] = 2.0 * self.n_r[i] / ideal_count;
}
}
}
QueryMode::CrossQuery => {
let n_a = self.n_query_points as F / nf;
let n_b = self.n_points as F / nf;
if vol <= 0.0 {
return gr;
}
for i in 0..n_bins {
let r_inner = self.bin_edges[i];
let r_outer = self.bin_edges[i + 1];
let v_shell = self.shell_volume(r_inner, r_outer);
let ideal_count = n_a * n_b * v_shell / vol * nf;
if ideal_count > 0.0 {
gr[i] = self.n_r[i] / ideal_count;
}
}
}
}
gr
}
}
impl ComputeResult for RDFResult {
fn finalize(&mut self) {
if self.finalized {
return;
}
self.rdf = self.compute_normalized();
self.finalized = true;
}
}
impl DescriptorRow for RDFResult {
fn as_row(&self) -> &[F] {
self.rdf
.as_slice()
.expect("RDFResult::rdf must be contiguous")
}
}