molframe_spatial/
cross.rs1use molframe_core::{AtomSelection, ExecutionContext};
4
5use crate::{SpatialBackend, SpatialError, pairs_within};
6
7#[derive(Clone, Copy, Debug, PartialEq)]
9pub struct CrossPair {
10 pub first: u32,
12 pub second: u32,
14 pub distance_squared: f32,
16}
17
18pub fn cross_pairs(
27 first: &[[f32; 3]],
28 second: &[[f32; 3]],
29 cutoff: f32,
30 backend: SpatialBackend,
31 context: &ExecutionContext,
32) -> Result<Vec<CrossPair>, SpatialError> {
33 let left_count = u32::try_from(first.len()).map_err(|_| SpatialError::NumericRangeExceeded)?;
34 let count = first
35 .len()
36 .checked_add(second.len())
37 .ok_or(SpatialError::NumericRangeExceeded)?;
38 let total = u32::try_from(count).map_err(|_| SpatialError::NumericRangeExceeded)?;
39 let bytes = count
40 .checked_mul(size_of::<[f32; 3]>())
41 .ok_or(SpatialError::NumericRangeExceeded)?;
42 let _reservation = context.try_reserve(bytes)?;
43 let mut positions = Vec::with_capacity(count);
44 positions.extend_from_slice(first);
45 positions.extend_from_slice(second);
46 let pairs = pairs_within(
47 &positions,
48 &AtomSelection::range(0..left_count),
49 &AtomSelection::range(left_count..total),
50 cutoff,
51 backend,
52 None,
53 context,
54 )?;
55 Ok(pairs
56 .into_iter()
57 .map(|pair| CrossPair {
58 first: pair.first,
59 second: pair.second - left_count,
60 distance_squared: pair.distance_squared,
61 })
62 .collect())
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn cross_indices_remain_local_even_when_the_right_index_is_smaller() {
71 let first = [[10.0, 0.0, 0.0], [0.0, 0.0, 0.0]];
72 let second = [[0.5, 0.0, 0.0]];
73 let pairs = cross_pairs(
74 &first,
75 &second,
76 1.0,
77 SpatialBackend::BruteForce,
78 &ExecutionContext::default(),
79 )
80 .expect("valid independent coordinate sets");
81 assert_eq!(
82 pairs,
83 vec![CrossPair {
84 first: 1,
85 second: 0,
86 distance_squared: 0.25
87 }]
88 );
89 }
90
91 #[test]
92 fn dense_sets_do_not_emit_within_set_pairs() {
93 let first = [[0.0; 3]; 4];
94 let second = [[0.0; 3]; 3];
95 let pairs = cross_pairs(
96 &first,
97 &second,
98 1.0,
99 SpatialBackend::CellList,
100 &ExecutionContext::default(),
101 )
102 .expect("valid dense coordinate sets");
103 assert_eq!(pairs.len(), first.len() * second.len());
104 assert!(pairs.iter().all(|pair| pair.first < 4 && pair.second < 3));
105 }
106}