molframe_spatial/queries/
count.rs1use super::{PairQuery, indices::IndexWorkspace, search::validate_cutoff, visit};
4use crate::{CellList, SpatialBackend, SpatialError};
5use molframe_core::parallel::{BlockExecutionError, BlockPlan, try_for_each_block_in};
6
7pub fn count_pairs_within(query: &PairQuery<'_>) -> Result<u64, SpatialError> {
18 validate_cutoff(query.cutoff)?;
19 let indices = IndexWorkspace::new(
20 query.left,
21 query.right,
22 query.positions.len(),
23 query.context,
24 )?;
25 let left = indices.left();
26 let right = indices.right();
27 let plan = query.options.plan(left.len(), right.len(), query.cutoff)?;
28 let mut total = 0_u64;
29 if !matches!(
30 plan.backend,
31 SpatialBackend::CellList | SpatialBackend::NeighborList
32 ) {
33 visit::visit_indices(query, left, right, |_| total += 1)?;
34 return Ok(total);
35 }
36 let index = CellList::build_in(
37 query.positions,
38 right,
39 query.cutoff,
40 query.periodic.copied(),
41 query.options.cell_grid,
42 query.context,
43 )?;
44 let same = left == right;
45 let owned_cells = if same { index.owned_cell_count() } else { None };
46 let blocks = owned_cells.map_or_else(
47 || BlockPlan::new(left.len(), if same { 64 } else { 256 }),
48 |count| BlockPlan::new(count, 64),
49 );
50 try_for_each_block_in(
51 blocks,
52 query.context,
53 size_of::<u64>(),
54 |_, range| {
55 let mut count = 0_u64;
56 if owned_cells.is_some() {
57 index.for_each_owned_cell(range, query.cutoff, |_| count += 1)?;
58 return Ok(count);
59 }
60 if same {
61 index.for_each_upper_candidate(&left[range], query.cutoff, |_| count += 1)?;
62 return Ok(count);
63 }
64 index.for_each_candidate(&left[range], query.cutoff, |a, b, distance| {
65 visit::emit_unique_cross_candidate(a, b, distance, left, right, &mut |_| {
66 count += 1;
67 });
68 })?;
69 Ok(count)
70 },
71 |count| {
72 total = total
73 .checked_add(count)
74 .ok_or(SpatialError::NumericRangeExceeded)?;
75 Ok(())
76 },
77 )
78 .map_err(|error| match error {
79 BlockExecutionError::Memory(error) => SpatialError::Memory(error),
80 BlockExecutionError::Cancelled => SpatialError::Cancelled,
81 BlockExecutionError::Worker(_) => SpatialError::WorkerPanicked,
82 BlockExecutionError::Operation(error) => error,
83 })?;
84 Ok(total)
85}
86
87#[cfg(test)]
88#[path = "count_tests.rs"]
89mod tests;