use std::time::Instant;
use lin_alg::f32::Vec3;
use rayon::prelude::*;
#[cfg(feature = "cuda")]
use crate::gpu_interface::PerNeighborGpu;
use crate::{ComputationDevice, MdState, barostat::SimBox};
#[derive(Default)]
pub struct NeighborsNb {
pub std_std: Vec<Vec<usize>>,
pub std_water: Vec<Vec<usize>>,
pub water_water: Vec<Vec<usize>>,
pub atom_posits_last_rebuild: Vec<Vec3>,
pub water_o_posits_last_rebuild: Vec<Vec3>,
pub max_displacement_sq: f32,
pub half_skin_sq: f32,
pub skin_sq_w_cutoff: f32,
}
impl NeighborsNb {
pub fn new(skin: f32, cutoff: f32) -> Self {
Self {
half_skin_sq: (skin * 0.5).powi(2),
skin_sq_w_cutoff: (cutoff + skin) * (cutoff + skin),
..Default::default()
}
}
}
impl MdState {
pub(crate) fn update_max_displacement_since_rebuild(&mut self) {
for (i, a) in self.atoms.iter().enumerate() {
if a.static_ {
continue;
}
let dv = a.posit - self.neighbors_nb.atom_posits_last_rebuild[i];
self.neighbors_nb.max_displacement_sq = self
.neighbors_nb
.max_displacement_sq
.max(dv.magnitude_squared());
}
for (i, w) in self.water.iter().enumerate() {
let diff_water_o = self
.cell
.min_image(w.o.posit - self.neighbors_nb.water_o_posits_last_rebuild[i]);
let dist_sq = diff_water_o.magnitude_squared();
self.neighbors_nb.max_displacement_sq =
self.neighbors_nb.max_displacement_sq.max(dist_sq);
}
}
fn save_rebuild_posits(&mut self) {
self.neighbors_nb.atom_posits_last_rebuild.clear();
self.neighbors_nb
.atom_posits_last_rebuild
.extend(self.atoms.iter().map(|a| a.posit));
self.neighbors_nb.water_o_posits_last_rebuild.clear();
self.neighbors_nb
.water_o_posits_last_rebuild
.extend(self.water.iter().map(|w| self.cell.wrap(w.o.posit)));
self.neighbors_nb.max_displacement_sq = 0.0;
}
#[allow(unused)] pub(crate) fn build_all_neighbors(&mut self, dev: &ComputationDevice) {
let atom_posits: Vec<_> = self.atoms.iter().map(|a| a.posit).collect();
let water_posits: Vec<_> = self
.water
.iter()
.map(|m| self.cell.wrap(m.o.posit))
.collect();
let is_static: Vec<_> = self.atoms.iter().map(|a| a.static_).collect();
self.neighbors_nb.std_std = build_neighbors(
&atom_posits,
&atom_posits,
Some(&is_static),
&self.cell,
true,
self.neighbors_nb.skin_sq_w_cutoff,
);
self.neighbors_nb.std_water = build_neighbors(
&atom_posits,
&water_posits,
None,
&self.cell,
false,
self.neighbors_nb.skin_sq_w_cutoff,
);
self.neighbors_nb.water_water = build_neighbors(
&water_posits,
&water_posits,
None,
&self.cell,
true,
self.neighbors_nb.skin_sq_w_cutoff,
);
self.setup_pairs();
#[cfg(feature = "cuda")]
if let ComputationDevice::Gpu(stream) = dev {
self.per_neighbor_gpu = Some(PerNeighborGpu::new(
stream,
&self.nb_pairs,
&self.atoms,
&self.water,
&self.lj_tables,
));
}
self.save_rebuild_posits();
}
pub(crate) fn build_neighbors_if_needed(&mut self, dev: &ComputationDevice) {
if self.neighbors_nb.max_displacement_sq >= self.neighbors_nb.half_skin_sq {
let start = Instant::now();
self.build_all_neighbors(dev);
self.computation_time.neighbor_rebuild_count += 1;
let elapsed = start.elapsed().as_micros() as u64;
self.computation_time.neighbor_rebuild_sum += elapsed;
}
}
}
pub fn build_neighbors(
posits_outer: &[Vec3],
posits_inner: &[Vec3],
is_static: Option<&[bool]>,
cell: &SimBox,
symmetric: bool,
skin_sq_w_cutoff: f32,
) -> Vec<Vec<usize>> {
let outer_len = posits_outer.len();
let inner_len = posits_inner.len();
if is_static.is_some() && !symmetric {
panic!("Invalid neighbor build config; can't pass static indices if non-symmetric.")
}
if symmetric {
assert_eq!(
inner_len, outer_len,
"symmetric=true requires identical sets"
);
let n = outer_len;
let half: Vec<Vec<usize>> = (0..n)
.into_par_iter()
.map(|i_outer| {
let mut out = Vec::new();
let posit_tgt = posits_outer[i_outer];
for i_inner in (i_outer + 1)..n {
let mut st_st = false;
if let Some(st) = is_static
&& st[i_outer]
&& st[i_inner]
{
st_st = true;
}
if !st_st {
let d = cell.min_image(posit_tgt - posits_inner[i_inner]);
if d.magnitude_squared() < skin_sq_w_cutoff {
out.push(i_inner);
}
}
}
out
})
.collect();
let mut deg = vec![0; n];
for i in 0..n {
deg[i] += half[i].len();
for &j in &half[i] {
deg[j] += 1;
}
}
let mut full: Vec<Vec<usize>> = (0..n).map(|i| Vec::with_capacity(deg[i])).collect();
for i in 0..n {
for &j in &half[i] {
full[i].push(j);
full[j].push(i);
}
}
full
} else {
(0..outer_len)
.into_par_iter()
.map(|i_outer| {
let mut out = Vec::new();
let pos_outer = posits_outer[i_outer];
for i_inner in 0..inner_len {
let mut st_st = false;
if let Some(st) = is_static
&& st[i_outer]
&& st[i_inner]
{
st_st = true;
}
if !st_st {
let pos_inner = posits_inner[i_inner];
let d = cell.min_image(pos_outer - pos_inner);
if d.magnitude_squared() < skin_sq_w_cutoff {
out.push(i_inner);
}
}
}
out
})
.collect()
}
}