use crate::element::Element;
#[derive(Debug, Clone)]
pub struct Molecule {
pub name: String,
pub atoms: Vec<Element>,
pub bonds: Vec<Bond>,
}
#[derive(Debug, Clone)]
pub struct Bond {
pub between: (usize, usize),
pub kind: BondKind,
pub energy: f32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BondKind {
Single,
Double,
Triple,
Hydrogen,
Ionic,
VanDerWaals,
Metallic,
}
impl BondKind {
pub fn typical_energy(a: Element, b: Element, kind: BondKind) -> f32 {
match kind {
BondKind::Single => {
match (a, b) {
(Element::H, Element::H) => 436.0,
(Element::O, Element::H) | (Element::H, Element::O) => 463.0,
(Element::C, Element::H) | (Element::H, Element::C) => 413.0,
(Element::C, Element::C) => 347.0,
(Element::C, Element::O) | (Element::O, Element::C) => 358.0,
(Element::C, Element::N) | (Element::N, Element::C) => 305.0,
(Element::N, Element::H) | (Element::H, Element::N) => 391.0,
(Element::O, Element::O) => 146.0,
_ => 350.0, }
}
BondKind::Double => {
match (a, b) {
(Element::C, Element::C) => 614.0,
(Element::C, Element::O) | (Element::O, Element::C) => 799.0,
(Element::O, Element::O) => 498.0,
(Element::C, Element::N) | (Element::N, Element::C) => 615.0,
_ => 600.0,
}
}
BondKind::Triple => {
match (a, b) {
(Element::C, Element::C) => 839.0,
(Element::N, Element::N) => 946.0,
(Element::C, Element::N) | (Element::N, Element::C) => 891.0,
_ => 800.0,
}
}
BondKind::Hydrogen => 20.0,
BondKind::Ionic => 700.0,
BondKind::VanDerWaals => 5.0,
BondKind::Metallic => {
let avg = (a.cohesive_energy() + b.cohesive_energy()) * 0.5;
avg * 96.485 }
}
}
}
impl Molecule {
pub fn molecular_mass(&self) -> f32 {
self.atoms.iter().map(|a| a.atomic_mass()).sum()
}
pub fn total_bond_energy(&self) -> f32 {
self.bonds.iter().map(|b| b.energy).sum()
}
pub fn has_hydrogen_bonds(&self) -> bool {
self.bonds.iter().any(|b| {
let a = self.atoms[b.between.0];
let b_elem = self.atoms[b.between.1];
(a == Element::H && matches!(b_elem, Element::O | Element::N | Element::F))
|| (b_elem == Element::H && matches!(a, Element::O | Element::N | Element::F))
})
}
pub fn water() -> Self {
let atoms = vec![Element::O, Element::H, Element::H];
let bonds = vec![
Bond {
between: (0, 1),
kind: BondKind::Single,
energy: BondKind::typical_energy(Element::O, Element::H, BondKind::Single),
},
Bond {
between: (0, 2),
kind: BondKind::Single,
energy: BondKind::typical_energy(Element::O, Element::H, BondKind::Single),
},
];
Self { name: "water".into(), atoms, bonds }
}
pub fn methane() -> Self {
let atoms = vec![Element::C, Element::H, Element::H, Element::H, Element::H];
let bonds = (1..5)
.map(|i| Bond {
between: (0, i),
kind: BondKind::Single,
energy: BondKind::typical_energy(Element::C, Element::H, BondKind::Single),
})
.collect();
Self { name: "methane".into(), atoms, bonds }
}
pub fn carbon_dioxide() -> Self {
let atoms = vec![Element::C, Element::O, Element::O];
let bonds = vec![
Bond {
between: (0, 1),
kind: BondKind::Double,
energy: BondKind::typical_energy(Element::C, Element::O, BondKind::Double),
},
Bond {
between: (0, 2),
kind: BondKind::Double,
energy: BondKind::typical_energy(Element::C, Element::O, BondKind::Double),
},
];
Self { name: "carbon_dioxide".into(), atoms, bonds }
}
pub fn oxygen_gas() -> Self {
let atoms = vec![Element::O, Element::O];
let bonds = vec![Bond {
between: (0, 1),
kind: BondKind::Double,
energy: BondKind::typical_energy(Element::O, Element::O, BondKind::Double),
}];
Self { name: "oxygen".into(), atoms, bonds }
}
pub fn hydrogen_gas() -> Self {
let atoms = vec![Element::H, Element::H];
let bonds = vec![Bond {
between: (0, 1),
kind: BondKind::Single,
energy: BondKind::typical_energy(Element::H, Element::H, BondKind::Single),
}];
Self { name: "hydrogen".into(), atoms, bonds }
}
pub fn carbon_monoxide() -> Self {
let atoms = vec![Element::C, Element::O];
let bonds = vec![Bond {
between: (0, 1),
kind: BondKind::Triple,
energy: 1072.0, }];
Self { name: "carbon_monoxide".into(), atoms, bonds }
}
pub fn sulfur_dioxide() -> Self {
let atoms = vec![Element::O, Element::S, Element::O];
let e = BondKind::typical_energy(Element::S, Element::O, BondKind::Double);
let bonds = vec![
Bond { between: (0, 1), kind: BondKind::Double, energy: e },
Bond { between: (1, 2), kind: BondKind::Double, energy: e },
];
Self { name: "sulfur_dioxide".into(), atoms, bonds }
}
pub fn glucose() -> Self {
let mut atoms = Vec::new();
for _ in 0..6 { atoms.push(Element::C); }
for _ in 0..12 { atoms.push(Element::H); }
for _ in 0..6 { atoms.push(Element::O); }
let cc = BondKind::typical_energy(Element::C, Element::C, BondKind::Single);
let ch = BondKind::typical_energy(Element::C, Element::H, BondKind::Single);
let co = BondKind::typical_energy(Element::C, Element::O, BondKind::Single);
let oh = BondKind::typical_energy(Element::O, Element::H, BondKind::Single);
let mut bonds = Vec::new();
for i in 0..5 {
bonds.push(Bond { between: (i, i + 1), kind: BondKind::Single, energy: cc });
}
for i in 0..7 {
bonds.push(Bond { between: (i % 6, 6 + i), kind: BondKind::Single, energy: ch });
}
for i in 0..5 {
bonds.push(Bond { between: (i, 18 + i), kind: BondKind::Single, energy: co });
}
for i in 0..5 {
bonds.push(Bond { between: (18 + i, 13 + i), kind: BondKind::Single, energy: oh });
}
Self { name: "glucose".into(), atoms, bonds }
}
pub fn ethanol() -> Self {
let atoms = vec![
Element::C, Element::C, Element::H, Element::H, Element::H, Element::H, Element::H, Element::O, Element::H, ];
let cc = BondKind::typical_energy(Element::C, Element::C, BondKind::Single);
let ch = BondKind::typical_energy(Element::C, Element::H, BondKind::Single);
let co = BondKind::typical_energy(Element::C, Element::O, BondKind::Single);
let oh = BondKind::typical_energy(Element::O, Element::H, BondKind::Single);
let bonds = vec![
Bond { between: (0, 1), kind: BondKind::Single, energy: cc },
Bond { between: (0, 2), kind: BondKind::Single, energy: ch },
Bond { between: (0, 3), kind: BondKind::Single, energy: ch },
Bond { between: (0, 4), kind: BondKind::Single, energy: ch },
Bond { between: (1, 5), kind: BondKind::Single, energy: ch },
Bond { between: (1, 6), kind: BondKind::Single, energy: ch },
Bond { between: (1, 7), kind: BondKind::Single, energy: co },
Bond { between: (7, 8), kind: BondKind::Single, energy: oh },
];
Self { name: "ethanol".into(), atoms, bonds }
}
}