use std::collections::HashMap;
use lin_alg::f64::Vec3;
use na_seq::{
Element,
Element::{Carbon, Hydrogen, Nitrogen, Oxygen, Sulfur},
};
use rayon::prelude::*;
use crate::{AtomGeneric, BondGeneric, BondType};
struct BondSpecs {
len: f64,
elements: (Element, Element),
bond_type: BondType,
}
impl BondSpecs {
pub fn new(len: f64, elements: (Element, Element), bond_type: BondType) -> Self {
Self {
len,
elements,
bond_type,
}
}
}
const COV_BOND_LEN_THRESH: f64 = 0.04; const COV_DIST_GRID: f64 = 1.6;
#[rustfmt::skip]
fn get_specs() -> Vec<BondSpecs> {
let single = BondType::Single;
let amide = BondType::Aromatic;
let double = BondType::Double;
let triple = BondType::Triple;
vec![
BondSpecs::new(1.54, (Carbon, Carbon), single),
BondSpecs::new(1.51, (Carbon, Carbon), single),
BondSpecs::new(1.50, (Carbon, Carbon), amide),
BondSpecs::new(1.47, (Carbon, Carbon), amide),
BondSpecs::new(1.44, (Carbon, Carbon), amide),
BondSpecs::new(1.41, (Carbon, Carbon), amide),
BondSpecs::new(1.39, (Carbon, Carbon), amide),
BondSpecs::new(1.36, (Carbon, Carbon), amide),
BondSpecs::new(1.33, (Carbon, Carbon), double),
BondSpecs::new(1.20, (Carbon, Carbon), triple),
BondSpecs::new(1.46, (Carbon, Nitrogen), single),
BondSpecs::new(1.37, (Carbon, Nitrogen), single),
BondSpecs::new(1.33, (Carbon, Nitrogen), single),
BondSpecs::new(1.28, (Carbon, Nitrogen), double),
BondSpecs::new(1.16, (Carbon, Nitrogen), triple),
BondSpecs::new(1.43, (Carbon, Oxygen), single),
BondSpecs::new(1.37, (Carbon, Oxygen), single),
BondSpecs::new(1.26, (Carbon, Oxygen), double),
BondSpecs::new(1.22, (Carbon, Oxygen), double),
BondSpecs::new(1.09, (Hydrogen, Carbon), single),
BondSpecs::new(1.01, (Hydrogen, Nitrogen), single),
BondSpecs::new(1.01, (Hydrogen, Oxygen), single),
BondSpecs::new(0.95, (Hydrogen, Oxygen), single),
BondSpecs::new(1.34, (Sulfur, Hydrogen), single),
BondSpecs::new(1.81, (Sulfur, Carbon), single),
]
}
pub fn create_bonds(atoms: &[AtomGeneric]) -> Vec<BondGeneric> {
let specs = get_specs();
let posits: Vec<_> = atoms.iter().map(|a| &a.posit).collect();
let indices: Vec<_> = (0..posits.len()).collect();
let neighbor_pairs = setup_neighbor_pairs(&posits, &indices, COV_DIST_GRID);
neighbor_pairs
.par_iter()
.filter_map(|(i, j)| {
let atom_0 = &atoms[*i];
let atom_1 = &atoms[*j];
let dist = (atom_0.posit - atom_1.posit).magnitude();
specs.iter().find_map(|spec| {
let matches_elements = (atom_0.element == spec.elements.0
&& atom_1.element == spec.elements.1)
|| (atom_0.element == spec.elements.1 && atom_1.element == spec.elements.0);
if matches_elements && (dist - spec.len).abs() < COV_BOND_LEN_THRESH {
Some(BondGeneric {
bond_type: spec.bond_type,
atom_0_sn: atom_0.serial_number,
atom_1_sn: atom_1.serial_number,
})
} else {
None
}
})
})
.collect()
}
fn _find_atom<'a>(
atoms: &'a [AtomGeneric],
indices: &[usize],
i_to_find: usize,
) -> Option<&'a AtomGeneric> {
for (i_set, atom) in atoms.iter().enumerate() {
if indices[i_set] == i_to_find {
return Some(atom);
}
}
None
}
fn setup_neighbor_pairs(
posits: &[&Vec3],
indexes: &[usize],
grid_size: f64,
) -> Vec<(usize, usize)> {
let mut grid: HashMap<(i32, i32, i32), Vec<usize>> = HashMap::new();
for (i, posit) in posits.iter().enumerate() {
let grid_pos = (
(posit.x / grid_size).floor() as i32,
(posit.y / grid_size).floor() as i32,
(posit.z / grid_size).floor() as i32,
);
grid.entry(grid_pos).or_default().push(indexes[i]);
}
let mut result = Vec::new();
for (&cell, indices) in &grid {
for dx in -1..=1 {
for dy in -1..=1 {
for dz in -1..=1 {
let neighbor_cell = (cell.0 + dx, cell.1 + dy, cell.2 + dz);
if let Some(neighbor_indices) = grid.get(&neighbor_cell) {
for &i in indices {
for &j in neighbor_indices {
if i < j {
result.push((i, j));
}
}
}
}
}
}
}
}
result
}