use chematic_core::{Molecule, implicit_hcount};
use crate::reaction::Reaction;
fn avg_mass(an: u8) -> f64 {
match an {
1 => 1.008, 2 => 4.003, 3 => 6.941, 4 => 9.012, 5 => 10.811, 6 => 12.011, 7 => 14.007, 8 => 15.999, 9 => 18.998, 11 => 22.990, 12 => 24.305, 13 => 26.982, 14 => 28.086, 15 => 30.974, 16 => 32.065, 17 => 35.453, 19 => 39.098, 20 => 40.078, 26 => 55.845, 29 => 63.546, 30 => 65.38, 35 => 79.904, 53 => 126.904, n => n as f64,
}
}
fn molecular_weight(mol: &Molecule) -> f64 {
let mut mw = 0.0f64;
for (idx, atom) in mol.atoms() {
mw += avg_mass(atom.element.atomic_number());
mw += implicit_hcount(mol, idx) as f64 * 1.008;
}
mw
}
pub fn atom_economy(rxn: &Reaction) -> f64 {
let reactant_mw: f64 = rxn.reactants.iter().map(molecular_weight).sum();
let product_mw: f64 = rxn.products.iter().map(molecular_weight).sum();
if reactant_mw == 0.0 {
return 0.0;
}
(product_mw / reactant_mw) * 100.0
}
pub fn e_factor(waste_mass: f64, product_mass: f64) -> f64 {
if product_mass == 0.0 {
return f64::INFINITY;
}
waste_mass / product_mass
}
pub fn pmi_rxn(all_masses: &[f64], product_mass: f64) -> f64 {
if product_mass == 0.0 {
return f64::INFINITY;
}
all_masses.iter().sum::<f64>() / product_mass
}
pub fn reaction_mass_efficiency(reactant_masses: &[f64], product_mass: f64) -> f64 {
let total: f64 = reactant_masses.iter().sum();
if total == 0.0 {
return 0.0;
}
(product_mass / total) * 100.0
}
#[cfg(test)]
mod tests {
use super::*;
use crate::reaction::parse_reaction;
#[test]
fn test_atom_economy_100_percent() {
let rxn = parse_reaction("CC>>CC").unwrap();
let ae = atom_economy(&rxn);
assert!(
(ae - 100.0).abs() < 0.1,
"AA→AA should be 100% atom economy, got {ae:.2}"
);
}
#[test]
fn test_atom_economy_less_than_100() {
let rxn = parse_reaction("C.O=O>>O=C=O").unwrap();
let ae = atom_economy(&rxn);
assert!(ae > 0.0, "atom economy should be > 0%");
assert!(
ae < 100.0,
"partial product should give < 100% atom economy, got {ae:.2}"
);
}
#[test]
fn test_atom_economy_no_reactants() {
let rxn = parse_reaction(">>CC").unwrap();
assert_eq!(atom_economy(&rxn), 0.0);
}
#[test]
fn test_e_factor_basic() {
assert!((e_factor(10.0, 2.0) - 5.0).abs() < 1e-9);
}
#[test]
fn test_e_factor_zero_product() {
assert!(e_factor(1.0, 0.0).is_infinite());
}
#[test]
fn test_pmi_minimum() {
assert!((pmi_rxn(&[5.0], 5.0) - 1.0).abs() < 1e-9);
}
#[test]
fn test_pmi_zero_product() {
assert!(pmi_rxn(&[1.0, 2.0], 0.0).is_infinite());
}
#[test]
fn test_rme_basic() {
assert!((reaction_mass_efficiency(&[10.0], 8.0) - 80.0).abs() < 1e-9);
}
#[test]
fn test_rme_empty() {
assert_eq!(reaction_mass_efficiency(&[], 5.0), 0.0);
}
}