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() {
wrap_many_ortho(cell.widths(), diffs, out);
} else {
for (d, o) in diffs.iter().zip(out.iter_mut()) {
*o = cell.displacement([0.0, 0.0, 0.0], *d);
}
}
Ok(())
}
fn wrap_many_ortho(l: [f64; 3], diffs: &[[f64; 3]], out: &mut [[f64; 3]]) {
for (d, o) in diffs.iter().zip(out.iter_mut()) {
*o = [
crate::cell::wrap_half(d[0], l[0]),
crate::cell::wrap_half(d[1], l[1]),
crate::cell::wrap_half(d[2], l[2]),
];
}
}
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 {
for (q, o) in qs.iter().zip(out.iter_mut()) {
*o = cell.dist2(p, *q);
}
}
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() {
let l = cell.widths();
let rbx = 1.0 / l[0];
let rby = 1.0 / l[1];
let rbz = 1.0 / l[2];
for i in 0..ps.len() {
out[i] = dist2_ortho_one(ps[i], qs[i], l, rbx, rby, rbz);
}
} 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);
}
let rbx = 1.0 / bx;
let rby = 1.0 / by;
let rbz = 1.0 / bz;
let mut i = 0;
while i + 4 <= n {
for k in 0..4 {
let mut ddx = dx[i + k].abs();
let mut ddy = dy[i + k].abs();
let mut ddz = dz[i + k].abs();
ddx -= bx * (ddx * rbx).round();
ddy -= by * (ddy * rby).round();
ddz -= bz * (ddz * rbz).round();
out[i + k] = ddx * ddx + ddy * ddy + ddz * ddz;
}
i += 4;
}
while i < n {
let mut ddx = dx[i].abs();
let mut ddy = dy[i].abs();
let mut ddz = dz[i].abs();
ddx -= bx * (ddx * rbx).round();
ddy -= by * (ddy * rby).round();
ddz -= bz * (ddz * rbz).round();
out[i] = ddx * ddx + ddy * ddy + ddz * ddz;
i += 1;
}
Ok(())
}
fn dist2_many_ortho(l: [f64; 3], p: [f64; 3], qs: &[[f64; 3]], out: &mut [f64]) {
let rbx = 1.0 / l[0];
let rby = 1.0 / l[1];
let rbz = 1.0 / l[2];
for (q, o) in qs.iter().zip(out.iter_mut()) {
*o = dist2_ortho_one(p, *q, l, rbx, rby, rbz);
}
}
#[inline]
fn dist2_ortho_one(p: [f64; 3], q: [f64; 3], l: [f64; 3], rbx: f64, rby: f64, rbz: f64) -> f64 {
let mut ddx = (q[0] - p[0]).abs();
let mut ddy = (q[1] - p[1]).abs();
let mut ddz = (q[2] - p[2]).abs();
ddx -= l[0] * (ddx * rbx).round();
ddy -= l[1] * (ddy * rby).round();
ddz -= l[2] * (ddz * rbz).round();
ddx * ddx + ddy * ddy + ddz * ddz
}
#[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);
}
#[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 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);
}
}
}