molframe-spatial 0.2.1

Deterministic spatial indices and neighbour search for molframe
Documentation
use super::*;

#[test]
fn nearest_neighbours_are_sorted_by_distance_then_atom() {
    let positions = [
        [0.0, 0.0, 0.0],
        [1.0, 0.0, 0.0],
        [-1.0, 0.0, 0.0],
        [3.0, 0.0, 0.0],
    ];
    let tree = match KdTree::build(&positions, &[0, 1, 2, 3], None) {
        Ok(tree) => tree,
        Err(error) => panic!("build failed: {error}"),
    };
    let nearest = match tree.k_nearest(0, 2) {
        Ok(nearest) => nearest,
        Err(error) => panic!("query failed: {error}"),
    };
    assert_eq!(
        nearest.iter().map(|item| item.0).collect::<Vec<_>>(),
        vec![1, 2]
    );
}

#[test]
fn radius_search_excludes_self_and_deduplicates_overlapping_groups() {
    let positions = [[0.0, 0.0, 0.0], [0.5, 0.0, 0.0]];
    let tree = match KdTree::build(&positions, &[0, 1], None) {
        Ok(tree) => tree,
        Err(error) => panic!("build failed: {error}"),
    };
    let pairs = match tree.pairs(&[0, 1], 1.0) {
        Ok(pairs) => pairs,
        Err(error) => panic!("query failed: {error}"),
    };
    assert_eq!(pairs.len(), 1);
    assert_eq!((pairs[0].first, pairs[0].second), (0, 1));
}

#[test]
fn periodic_nearest_search_uses_tree_images_across_a_triclinic_boundary() {
    let positions = [[0.1, 0.2, 0.3], [7.9, 0.2, 0.3], [4.0, 4.0, 4.0]];
    let periodic = PeriodicBox::from_cell(molframe_core::structure::UnitCell {
        lengths: [8.0, 9.0, 10.0],
        angles: [70.0, 80.0, 65.0],
    });
    let periodic = match periodic {
        Ok(periodic) => periodic,
        Err(error) => panic!("cell failed: {error}"),
    };
    let tree = match KdTree::build(&positions, &[0, 1, 2], Some(periodic)) {
        Ok(tree) => tree,
        Err(error) => panic!("build failed: {error}"),
    };
    let nearest = match tree.k_nearest(0, 2) {
        Ok(nearest) => nearest,
        Err(error) => panic!("query failed: {error}"),
    };

    assert_eq!(nearest[0].0, 1);
    let expected = periodic.distance_squared(positions[0], positions[1]);
    assert!((nearest[0].1 - expected).abs() <= f32::EPSILON);
}

#[test]
fn periodic_image_budget_is_enforced_instead_of_falling_back() {
    let positions = [[0.0, 0.0, 0.0], [0.5, 0.0, 0.0]];
    let periodic = PeriodicBox::from_cell(molframe_core::structure::UnitCell {
        lengths: [8.0; 3],
        angles: [90.0; 3],
    });
    let periodic = match periodic {
        Ok(periodic) => periodic,
        Err(error) => panic!("cell failed: {error}"),
    };
    let tree = KdTree::build_with_options(
        &positions,
        &[0, 1],
        Some(periodic),
        KdPeriodicOptions {
            maximum_image_count: 1,
        },
    );
    let tree = match tree {
        Ok(tree) => tree,
        Err(error) => panic!("build failed: {error}"),
    };

    assert!(matches!(
        tree.pairs(&[0], 1.0),
        Err(SpatialError::PeriodicImageLimitExceeded { .. })
    ));
}