use crate::{
math::{Quantity, Scalar},
physics::molecular::potential::Potential,
units::{
Energy, Force, ForcePerLength, Length, ReciprocalForcePerLength, ReciprocalLength, Stress,
},
};
#[derive(Clone, Debug)]
pub struct Morse {
pub rest_length: Scalar,
pub depth: Scalar,
pub parameter: Scalar,
}
impl Morse {
fn depth(&self) -> Quantity<Energy> {
Quantity::new(self.depth)
}
fn parameter(&self) -> Quantity<ReciprocalLength> {
Quantity::new(self.parameter)
}
}
impl Potential for Morse {
fn energy(&self, length: Quantity<Length>) -> Quantity<Energy> {
let exp = (self.parameter() * (self.rest_length() - length)).exp();
self.depth() * (1.0 - exp).powi(2)
}
fn force(&self, length: Quantity<Length>) -> Quantity<Force> {
let exp = (self.parameter() * (self.rest_length() - length)).exp();
2.0 * self.parameter() * self.depth() * exp * (1.0 - exp)
}
fn forces_at_energy(&self, energy: Quantity<Energy>) -> [Quantity<Force>; 2] {
let y = energy / self.depth();
let f = 2.0 * self.parameter() * self.depth() * y.sqrt();
let tensile = if (0.0..=1.0).contains(&y) {
f * (1.0 - y.sqrt())
} else {
Quantity::new(Scalar::NAN)
};
[tensile, -f * (1.0 + y.sqrt())]
}
fn stiffness(&self, length: Quantity<Length>) -> Quantity<ForcePerLength> {
let exp = (self.parameter() * (self.rest_length() - length)).exp();
2.0 * (self.parameter() * self.parameter()) * self.depth() * exp * (2.0 * exp - 1.0)
}
fn anharmonicity(&self, length: Quantity<Length>) -> Quantity<Stress> {
let exp = (self.parameter() * (self.rest_length() - length)).exp();
2.0 * (self.parameter() * self.parameter())
* self.depth()
* self.parameter()
* exp
* (1.0 - 4.0 * exp)
}
fn extension(&self, force: Quantity<Force>) -> Quantity<Length> {
let y = force / self.peak_force();
if y <= 1.0 {
(2.0 / (1.0 + (1.0 - y).sqrt())).ln() / self.parameter()
} else {
Quantity::new(Scalar::NAN)
}
}
fn extensions_at_energy(&self, energy: Quantity<Energy>) -> [Quantity<Length>; 2] {
let y = energy / self.depth();
let tensile = if (0.0..=1.0).contains(&y) {
(1.0 / (1.0 - y.sqrt())).ln() / self.parameter()
} else {
Quantity::new(Scalar::NAN)
};
[tensile, (1.0 / (1.0 + y.sqrt())).ln() / self.parameter()]
}
fn compliance(&self, force: Quantity<Force>) -> Quantity<ReciprocalForcePerLength> {
let y = force / self.peak_force();
if (0.0..1.0).contains(&y) {
let s = (1.0 - y).sqrt();
1.0 / (self.parameter() * self.parameter() * self.depth()) / (s * (1.0 + s))
} else if y == 0.0 {
Quantity::new(Scalar::INFINITY)
} else {
Quantity::new(Scalar::NAN)
}
}
fn peak(&self) -> Quantity<Length> {
self.rest_length() + 2.0_f64.ln() / self.parameter()
}
fn peak_force(&self) -> Quantity<Force> {
0.5 * self.parameter() * self.depth()
}
fn rest_length(&self) -> Quantity<Length> {
Quantity::new(self.rest_length)
}
}