use super::perceive::{Hybridization, PerceivedAtom};
const LAMBDA: f64 = 0.1332;
#[derive(Clone, Copy, Debug)]
struct UffParams {
r1: f64,
xi: f64,
}
fn params_for_label(label: &str) -> Option<UffParams> {
let (r1, xi) = match label {
"H_" => (0.354, 4.528),
"C_3" => (0.757, 5.343),
"C_R" => (0.729, 5.343),
"C_2" => (0.732, 5.343),
"C_1" => (0.706, 5.343),
"N_3" => (0.700, 6.899),
"N_R" => (0.699, 6.899),
"N_2" => (0.685, 6.899),
"N_1" => (0.656, 6.899),
"O_3" => (0.658, 8.741),
"O_R" => (0.680, 8.741),
"O_2" => (0.634, 8.741),
"O_1" => (0.639, 8.741),
"F_" => (0.668, 10.874),
"S_3+2" => (1.064, 6.928),
"S_3+4" => (1.049, 6.928),
"S_3+6" => (1.027, 6.928),
"S_R" => (1.077, 6.928),
"S_2" => (0.854, 6.928),
"Cl" => (1.044, 8.564),
"Br" => (1.192, 7.790),
"P_3+3" => (1.101, 5.463),
"P_3+5" => (1.056, 5.463),
"I_" => (1.382, 6.822),
_ => return None,
};
Some(UffParams { r1, xi })
}
pub fn atom_label(atom: &PerceivedAtom) -> Option<String> {
let sym = atom.element.symbol();
match sym {
"H" => return Some("H_".to_string()),
"F" => return Some("F_".to_string()),
"Cl" => return Some("Cl".to_string()),
"Br" => return Some("Br".to_string()),
"I" => return Some("I_".to_string()),
_ => {}
}
let mut key = sym.to_string();
if key.len() == 1 {
key.push('_');
}
let conj_r = matches!(sym, "C" | "N" | "O" | "S");
match atom.hybridization {
Hybridization::Sp => key.push('1'),
Hybridization::Sp2 => {
if (atom.aromatic || atom.conjugated) && conj_r {
key.push('R');
} else {
key.push('2');
}
}
Hybridization::Sp3 => key.push('3'),
Hybridization::Other => key.push('3'),
}
if sym == "S" && atom.hybridization != Hybridization::Sp2 {
let v = atom.total_valence.round() as i64;
let flag = match v {
2 => "+2",
4 => "+4",
6 => "+6",
_ => "+6",
};
key.push_str(flag);
}
Some(key)
}
fn rest_length(p1: UffParams, p2: UffParams, bond_order: f64) -> f64 {
let (ri, rj) = (p1.r1, p2.r1);
let r_bo = -LAMBDA * (ri + rj) * bond_order.ln();
let (xi, xj) = (p1.xi, p2.xi);
let dx = xi.sqrt() - xj.sqrt();
let r_en = ri * rj * dx * dx / (xi * ri + xj * rj);
ri + rj + r_bo - r_en
}
pub fn effective_bond_order(order: f64, aromatic: bool, amide: bool) -> f64 {
if amide {
1.41
} else if aromatic {
1.5
} else {
order
}
}
pub fn bond_rest_length(a: &PerceivedAtom, b: &PerceivedAtom, effective_order: f64) -> (f64, bool) {
let pa = atom_label(a).and_then(|l| params_for_label(&l));
let pb = atom_label(b).and_then(|l| params_for_label(&l));
match (pa, pb) {
(Some(pa), Some(pb)) if effective_order > 0.0 => {
(rest_length(pa, pb, effective_order), true)
}
_ => {
let bl = 0.5 * (rvdw(a.element.z()) + rvdw(b.element.z()));
(bl, false)
}
}
}
pub fn rvdw(z: u8) -> f64 {
match z {
1 => 1.2,
5 => 1.8,
6 => 1.7,
7 => 1.6,
8 => 1.55,
9 => 1.5,
14 => 2.1,
15 => 1.95,
16 => 1.8,
17 => 1.8,
35 => 1.9,
53 => 2.1,
_ => 2.0,
}
}