molframe-spatial 0.6.4

Deterministic spatial indices and neighbour search for molframe
Documentation
use super::*;
use crate::SpatialBackend;
use crate::queries::lattice_fixture::lattice;

fn counted(positions: &[[f32; 3]], workers: usize, backend: SpatialBackend) -> usize {
    let context = ExecutionContext::builder()
        .worker_budget(workers)
        .build()
        .expect("worker context is valid");
    let all = AtomSelection::All(u32::try_from(positions.len()).expect("lattice fits u32"));
    let query = PairQuery {
        positions,
        left: &all,
        right: &all,
        cutoff: 1.5,
        options: SpatialSearchOptions::with_backend(backend),
        periodic: None,
        context: &context,
    };
    let parts = reduce_pairs_within_unsorted(&query, || 0usize, |count, _| *count += 1)
        .expect("reduction succeeds");
    parts.into_iter().sum()
}

#[test]
fn the_pair_count_is_identical_at_every_worker_count() {
    let positions = lattice(8);
    let serial = counted(&positions, 1, SpatialBackend::CellList);

    for workers in [2, 3, 4, 8, 16] {
        assert_eq!(
            counted(&positions, workers, SpatialBackend::CellList),
            serial,
            "worker count {workers} changed the pair count"
        );
    }
}

#[test]
fn the_reduction_agrees_with_the_materialising_query() {
    let positions = lattice(6);
    let all = AtomSelection::All(u32::try_from(positions.len()).expect("lattice fits u32"));
    let expected = crate::pairs_within(
        &positions,
        &all,
        &all,
        1.5,
        SpatialBackend::CellList,
        None,
        &ExecutionContext::default(),
    )
    .expect("query succeeds")
    .len();

    assert_eq!(counted(&positions, 4, SpatialBackend::CellList), expected);
}

#[test]
fn a_backend_without_a_block_decomposition_still_reduces() {
    let positions = lattice(4);

    assert_eq!(
        counted(&positions, 4, SpatialBackend::BruteForce),
        counted(&positions, 1, SpatialBackend::CellList)
    );
}

fn cross_count(positions: &[[f32; 3]], workers: usize, backend: SpatialBackend) -> usize {
    let context = ExecutionContext::builder()
        .worker_budget(workers)
        .build()
        .expect("worker context is valid");
    let left = AtomSelection::from_sorted((0..96).collect());
    let right = AtomSelection::from_sorted((96..256).collect());
    let query = PairQuery {
        positions,
        left: &left,
        right: &right,
        cutoff: 1.5,
        options: SpatialSearchOptions::with_backend(backend),
        periodic: None,
        context: &context,
    };
    let parts = reduce_pairs_within_unsorted(&query, || 0usize, |count, _| *count += 1)
        .expect("reduction succeeds");
    parts.into_iter().sum()
}

#[test]
fn a_cross_selection_query_counts_the_same_at_every_worker_count() {
    let positions = lattice(8);
    let serial = cross_count(&positions, 1, SpatialBackend::CellList);
    assert!(serial > 0, "the fixture must produce cross pairs");

    for workers in [2, 3, 4, 8, 16] {
        assert_eq!(
            cross_count(&positions, workers, SpatialBackend::CellList),
            serial,
            "worker count {workers} changed the cross-selection pair count"
        );
    }
}

#[test]
fn a_blocked_cross_selection_agrees_with_the_materialising_query() {
    let positions = lattice(8);
    let left = AtomSelection::from_sorted((0..96).collect());
    let right = AtomSelection::from_sorted((96..256).collect());
    let expected = crate::pairs_within(
        &positions,
        &left,
        &right,
        1.5,
        SpatialBackend::CellList,
        None,
        &ExecutionContext::default(),
    )
    .expect("query succeeds")
    .len();

    assert_eq!(
        cross_count(&positions, 8, SpatialBackend::CellList),
        expected
    );
}