use molrs::types::F;
use super::cell::VoronoiCells;
use crate::compute::error::ComputeError;
use crate::core::system::topology::Topology;
#[derive(Debug, Clone)]
pub struct VoidResult {
pub cavity_volumes: Vec<F>,
pub total_void_volume: F,
pub void_fraction: F,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct VoidAnalysis;
impl VoidAnalysis {
pub fn analyze(
&self,
cells: &VoronoiCells,
is_void: &[bool],
box_volume: F,
) -> Result<VoidResult, ComputeError> {
let n = cells.len();
if is_void.len() != n {
return Err(ComputeError::DimensionMismatch {
expected: n,
got: is_void.len(),
what: "void mask length",
});
}
if !box_volume.is_finite() || box_volume <= 0.0 {
return Err(ComputeError::OutOfRange {
field: "VoidAnalysis::box_volume",
value: box_volume.to_string(),
});
}
let mut topo = Topology::with_atoms(n);
for (i, &vi) in is_void.iter().enumerate() {
if !vi {
continue;
}
for j in cells.neighbors(i) {
let j = j as usize;
if j < n && j > i && is_void[j] {
topo.add_bond(i, j);
}
}
}
let component_of = topo.connected_components();
let mut vol_of = vec![0.0 as F; n];
let mut seen = vec![false; n];
let mut total = 0.0;
for (i, &vi) in is_void.iter().enumerate() {
if !vi {
continue;
}
let c = component_of[i] as usize;
vol_of[c] += cells.volumes[i];
seen[c] = true;
total += cells.volumes[i];
}
let mut cavity_volumes: Vec<F> = (0..n).filter(|&c| seen[c]).map(|c| vol_of[c]).collect();
cavity_volumes.sort_unstable_by(|a, b| b.partial_cmp(a).unwrap());
Ok(VoidResult {
cavity_volumes,
total_void_volume: total,
void_fraction: total / box_volume,
})
}
}