use crate::kernel::{self, n2};
use crate::{Cell, Error};
pub fn wrap_many(cell: &Cell, diffs: &[[f64; 3]], out: &mut [[f64; 3]]) -> Result<(), Error> {
if out.len() != diffs.len() {
return Err(Error::BufferSize);
}
if cell.is_ortho() {
crate::simd::wrap_many_ortho(cell.widths(), diffs, out);
} else {
for (d, o) in diffs.iter().zip(out.iter_mut()) {
*o = cell.wrap_diff(*d);
}
}
Ok(())
}
pub fn dist2_many(cell: &Cell, p: [f64; 3], qs: &[[f64; 3]], out: &mut [f64]) -> Result<(), Error> {
if out.len() != qs.len() {
return Err(Error::BufferSize);
}
if cell.is_ortho() {
dist2_many_ortho(cell.widths(), p, qs, out);
} else if cell.is_restricted() {
for (q, o) in qs.iter().zip(out.iter_mut()) {
let d = cell.wrap_diff([q[0] - p[0], q[1] - p[1], q[2] - p[2]]);
*o = n2(d);
}
} else {
let h = cell.h();
let hinv = cell.hinv();
for (q, o) in qs.iter().zip(out.iter_mut()) {
let d = kernel::general_wrap(h, hinv, [q[0] - p[0], q[1] - p[1], q[2] - p[2]]);
*o = n2(d);
}
}
Ok(())
}
pub fn dist2_pairs(
cell: &Cell,
ps: &[[f64; 3]],
qs: &[[f64; 3]],
out: &mut [f64],
) -> Result<(), Error> {
if ps.len() != qs.len() || out.len() != ps.len() {
return Err(Error::BufferSize);
}
if cell.is_ortho() {
dist2_pairs_ortho(cell.widths(), ps, qs, out);
} else {
for i in 0..ps.len() {
out[i] = cell.dist2(ps[i], qs[i]);
}
}
Ok(())
}
pub fn dist2_ortho_diffs(
dx: &[f64],
dy: &[f64],
dz: &[f64],
bx: f64,
by: f64,
bz: f64,
out: &mut [f64],
) -> Result<(), Error> {
let n = dx.len().min(dy.len()).min(dz.len()).min(out.len());
if n == 0 {
return Ok(());
}
if !(bx > 0.0 && by > 0.0 && bz > 0.0) {
return Err(Error::BadBox);
}
crate::simd::dist2_ortho_diffs(&dx[..n], &dy[..n], &dz[..n], bx, by, bz, &mut out[..n]);
Ok(())
}
pub(crate) fn dist2_pairs_ortho(l: [f64; 3], ps: &[[f64; 3]], qs: &[[f64; 3]], out: &mut [f64]) {
const CHUNK: usize = 64;
let mut dx = [0.0; CHUNK];
let mut dy = [0.0; CHUNK];
let mut dz = [0.0; CHUNK];
let mut start = 0;
while start < ps.len() {
let n = (ps.len() - start).min(CHUNK);
for k in 0..n {
let i = start + k;
dx[k] = qs[i][0] - ps[i][0];
dy[k] = qs[i][1] - ps[i][1];
dz[k] = qs[i][2] - ps[i][2];
}
crate::simd::dist2_ortho_diffs(
&dx[..n],
&dy[..n],
&dz[..n],
l[0],
l[1],
l[2],
&mut out[start..start + n],
);
start += n;
}
}
pub(crate) fn dist2_many_ortho(l: [f64; 3], p: [f64; 3], qs: &[[f64; 3]], out: &mut [f64]) {
const CHUNK: usize = 64;
let mut dx = [0.0; CHUNK];
let mut dy = [0.0; CHUNK];
let mut dz = [0.0; CHUNK];
let mut start = 0;
while start < qs.len() {
let n = (qs.len() - start).min(CHUNK);
for (k, q) in qs[start..start + n].iter().enumerate() {
dx[k] = q[0] - p[0];
dy[k] = q[1] - p[1];
dz[k] = q[2] - p[2];
}
crate::simd::dist2_ortho_diffs(
&dx[..n],
&dy[..n],
&dz[..n],
l[0],
l[1],
l[2],
&mut out[start..start + n],
);
start += n;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn batch_matches_scalar_ortho() {
let cell = Cell::ortho(10.0, 10.0, 10.0).unwrap();
let p = [0.2, 0.0, 0.0];
let qs = [[9.4, 0.0, 0.0], [1.0, 0.0, 0.0], [5.0, 0.0, 0.0]];
let mut out = [0.0; 3];
dist2_many(&cell, p, &qs, &mut out).unwrap();
for i in 0..3 {
assert!((out[i] - cell.dist2(p, qs[i])).abs() < 1e-12);
}
assert!((out[0] - 0.64).abs() < 1e-12);
let wide: Vec<[f64; 3]> = (0..70).map(|i| [f64::from(i) - 20.0, 0.0, 0.0]).collect();
let mut wide_out = vec![0.0; wide.len()];
dist2_many(&cell, p, &wide, &mut wide_out).unwrap();
for (q, got) in wide.iter().zip(wide_out.iter()) {
assert!((got - cell.dist2(p, *q)).abs() < 1e-12);
}
}
#[test]
fn diffs_kernel_matches_highway_shape() {
let dx = [9.2_f64, 0.8, 5.0, 11.0, -3.0];
let dy = [0.0; 5];
let dz = [0.0; 5];
let mut out = [0.0; 5];
dist2_ortho_diffs(&dx, &dy, &dz, 10.0, 10.0, 10.0, &mut out).unwrap();
assert!((out[0] - 0.64).abs() < 1e-12);
assert!((out[1] - 0.64).abs() < 1e-12);
assert!((out[2] - 25.0).abs() < 1e-12);
assert!((out[3] - 1.0).abs() < 1e-12);
assert!((out[4] - 9.0).abs() < 1e-12);
}
#[test]
fn sheared_batch_quarter() {
let cell =
Cell::from_lammps_bounds(15.0, 8.660254037844386, 10.0, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0)
.unwrap();
let p = [0.2, 0.1, 1.0];
let qs = [[9.7, 0.1, 1.0]];
let mut out = [0.0];
dist2_many(&cell, p, &qs, &mut out).unwrap();
assert!((out[0] - 0.25).abs() < 1e-12);
}
#[test]
fn wrap_many_ortho_face() {
let cell = Cell::ortho(10.0, 10.0, 10.0).unwrap();
let diffs = [[9.2, 0.0, 0.0], [0.2, 0.0, 0.0]];
let mut out = [[0.0; 3]; 2];
wrap_many(&cell, &diffs, &mut out).unwrap();
assert!((out[0][0] + 0.8).abs() < 1e-12);
assert!((out[1][0] - 0.2).abs() < 1e-12);
}
#[test]
fn pairs_match_scalar_including_later_images() {
let cell = Cell::ortho(10.0, 11.0, 12.0).unwrap();
let mut ps = vec![[0.0; 3]; 70];
let mut qs = vec![[0.0; 3]; 70];
for i in 0..70 {
let t = f64::from(i as i32) - 20.0;
ps[i] = [0.2, 0.0, 0.0];
qs[i] = [t, -18.0, 3.0];
}
let mut out = vec![0.0; 70];
dist2_pairs(&cell, &ps, &qs, &mut out).unwrap();
for i in 0..70 {
assert!((out[i] - cell.dist2(ps[i], qs[i])).abs() < 1e-9);
}
}
#[test]
fn wrap_many_keeps_negative_half() {
let cell = Cell::ortho(10.0, 10.0, 10.0).unwrap();
let diffs = [[-5.0, 0.0, 0.0], [5.0, 0.0, 0.0]];
let mut out = [[0.0; 3]; 2];
wrap_many(&cell, &diffs, &mut out).unwrap();
assert!((out[0][0] + 5.0).abs() < 1e-12);
assert!((out[1][0] + 5.0).abs() < 1e-12);
for (d, o) in diffs.iter().zip(out.iter()) {
let s = cell.displacement([0.0, 0.0, 0.0], *d);
assert!((o[0] - s[0]).abs() < 1e-12);
}
}
}