use molrs::system::atomistic::{AtomId, Atomistic};
use super::perceive::{Hybridization, Perceived};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ChiralSign {
Positive,
Negative,
Unknown,
}
#[derive(Clone, Debug)]
pub struct ChiralConstraint {
pub center: usize,
pub neighbors: [usize; 4],
pub volume_lower: f64,
pub volume_upper: f64,
pub sign: ChiralSign,
}
#[derive(Clone, Debug)]
pub struct ImproperConstraint {
pub atoms: [usize; 4],
pub center_atomic_num: u8,
pub bound_to_sp2_o: bool,
}
fn coord(mol: &Atomistic, id: AtomId) -> Option<[f64; 3]> {
let a = mol.get_atom(id).ok()?;
Some([a.get_f64("x")?, a.get_f64("y")?, a.get_f64("z")?])
}
fn signed_volume(p0: [f64; 3], p1: [f64; 3], p2: [f64; 3], p3: [f64; 3]) -> f64 {
let a = [p0[0] - p3[0], p0[1] - p3[1], p0[2] - p3[2]];
let b = [p1[0] - p3[0], p1[1] - p3[1], p1[2] - p3[2]];
let c = [p2[0] - p3[0], p2[1] - p3[1], p2[2] - p3[2]];
let cross = [
b[1] * c[2] - b[2] * c[1],
b[2] * c[0] - b[0] * c[2],
b[0] * c[1] - b[1] * c[0],
];
a[0] * cross[0] + a[1] * cross[1] + a[2] * cross[2]
}
pub fn build_chiral(mol: &Atomistic, p: &Perceived) -> Vec<ChiralConstraint> {
let mut out = Vec::new();
for (i, atom) in p.atoms.iter().enumerate() {
let z = atom.element.z();
if z == 1 {
continue;
}
let is_candidate = (z == 6 || z == 7) && atom.degree == 4;
if !is_candidate {
continue;
}
let mut nbrs: Vec<usize> = p.adj[i].clone();
if nbrs.len() < 3 {
continue;
}
let mut vol_lower = 5.0;
let vol_upper = 100.0;
if nbrs.len() < 4 {
vol_lower = 2.0;
nbrs.push(i);
}
let neighbors = [nbrs[0], nbrs[1], nbrs[2], nbrs[3]];
let sign = match (
coord(mol, p.atom_ids[neighbors[0]]),
coord(mol, p.atom_ids[neighbors[1]]),
coord(mol, p.atom_ids[neighbors[2]]),
coord(mol, p.atom_ids[neighbors[3]]),
) {
(Some(p0), Some(p1), Some(p2), Some(p3)) => {
let v = signed_volume(p0, p1, p2, p3);
if v > 1e-6 {
ChiralSign::Positive
} else if v < -1e-6 {
ChiralSign::Negative
} else {
ChiralSign::Unknown
}
}
_ => ChiralSign::Unknown,
};
let (lower, upper) = match sign {
ChiralSign::Positive => (vol_lower, vol_upper),
ChiralSign::Negative => (-vol_upper, -vol_lower),
ChiralSign::Unknown => (vol_lower, vol_upper),
};
out.push(ChiralConstraint {
center: i,
neighbors,
volume_lower: lower,
volume_upper: upper,
sign,
});
}
out
}
pub fn build_improper(p: &Perceived) -> Vec<ImproperConstraint> {
let mut out = Vec::new();
for (i, atom) in p.atoms.iter().enumerate() {
let z = atom.element.z();
if !(z == 6 || z == 7 || z == 8) {
continue;
}
if atom.hybridization != Hybridization::Sp2 || atom.degree != 3 {
continue;
}
let nbrs = &p.adj[i];
if nbrs.len() != 3 {
continue;
}
let mut bound_to_sp2_o = false;
for &nb in nbrs {
if z == 6
&& p.atoms[nb].element.z() == 8
&& p.atoms[nb].hybridization == Hybridization::Sp2
{
bound_to_sp2_o = true;
}
}
out.push(ImproperConstraint {
atoms: [nbrs[0], i, nbrs[1], nbrs[2]],
center_atomic_num: z,
bound_to_sp2_o,
});
}
out
}