pub(crate) mod aromaticity;
pub(crate) mod atomtype;
pub(crate) mod charges;
pub mod energy;
mod hybrid;
pub mod tables;
pub(crate) mod topo;
pub use energy::{MmffEnergyBreakdown, MmffForceField};
use molrs::Atomistic;
use molrs::error::MolRsError;
use topo::Topo;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MmffVariant {
Mmff94,
Mmff94s,
}
#[derive(Debug, Clone)]
pub struct MmffMolProperties {
variant: MmffVariant,
atom_types: Vec<u8>,
partial_charges: Vec<f64>,
valid: bool,
}
impl MmffMolProperties {
pub fn compute(mol: &Atomistic, variant: MmffVariant) -> Result<Self, MolRsError> {
let base = Topo::build(mol).map_err(|sym| {
MolRsError::validation(format!("MMFF: unsupported element symbol '{sym}'"))
})?;
let topo = aromaticity::set_mmff_aromaticity(&base);
let atom_types = atomtype::assign_atom_types(&topo);
if let Some(bad) = atom_types.iter().position(|&t| t == 0) {
let z = topo.atno[bad];
return Err(MolRsError::validation(format!(
"MMFF: could not assign an atom type to atom index {bad} (Z={z})"
)));
}
let partial_charges = charges::compute_partial_charges(&topo, &atom_types);
Ok(Self {
variant,
atom_types,
partial_charges,
valid: true,
})
}
pub fn variant(&self) -> MmffVariant {
self.variant
}
pub fn atom_type(&self, i: usize) -> u8 {
self.atom_types[i]
}
pub fn partial_charge(&self, i: usize) -> f64 {
self.partial_charges[i]
}
pub fn is_setup_complete(&self) -> bool {
self.valid
}
pub fn len(&self) -> usize {
self.atom_types.len()
}
pub fn is_empty(&self) -> bool {
self.atom_types.is_empty()
}
}