use crate::math::assert::Assert;
use crate::{
EPSILON,
math::{
Quantity, Scalar,
assert::{AssertionError, perturbation},
},
physics::molecular::potential::{Harmonic, Morse, Potential},
units::{Force, Length, Temperature},
};
const NUM: usize = 333;
#[test]
fn test_consistency() -> Result<(), AssertionError> {
let model = Harmonic {
rest_length: 1.5,
stiffness: 1.2,
};
let energy = model.energy(Quantity::new(1.7));
let forces = model.forces_at_energy(energy);
let extensions = model.extensions_at_energy(energy);
Assert::default().eq_within_tols(energy, &model.energy_at_force(forces[0]))?;
Assert::default().eq_within_tols(energy, &model.energy_at_force(forces[1]))?;
Assert::default().eq_within_tols(energy, &model.energy(extensions[0] + model.rest_length()))?;
Assert::default().eq_within_tols(energy, &model.energy(extensions[1] + model.rest_length()))?;
let model = Morse {
rest_length: 1.5,
depth: 1.9,
parameter: 1.1,
};
let energy = model.energy(Quantity::new(1.51));
let forces = model.forces_at_energy(energy);
let extensions = model.extensions_at_energy(energy);
Assert::default().eq_within_tols(energy, &model.energy_at_force(forces[0]))?;
Assert::default().eq_within_tols(energy, &model.energy_at_force(forces[1]))?;
Assert::default().eq_within_tols(energy, &model.energy(extensions[0] + model.rest_length()))?;
Assert::default().eq_within_tols(energy, &model.energy(extensions[1] + model.rest_length()))
}
#[test]
fn finite_difference() -> Result<(), AssertionError> {
let e = 1.2;
let a = 1.1;
let x0 = 1.5;
let x_max = x0 + 0.98 * 2.0_f64.ln() / a;
let t = Quantity::<Temperature>::new(1e-1);
let potential = Harmonic {
rest_length: x0,
stiffness: e,
};
(0..NUM)
.map(|k| Quantity::new(x0 + (x_max - x0) * k as Scalar / NUM as Scalar))
.into_iter()
.try_for_each(|mut x| {
let mut force = potential.force(x);
let stiffness = potential.stiffness(x);
let anharmonicity = potential.anharmonicity(x);
Assert::default()
.eq_within_tols(potential.energy(x), &potential.energy_at_force(force))?;
x += perturbation(0.5 * EPSILON);
let force_fd = potential.energy(x);
let stiffness_fd = potential.force(x);
let anharmonicity_fd = potential.stiffness(x);
x -= perturbation(EPSILON);
let force_fd = (force_fd - potential.energy(x)) / perturbation::<Length>(EPSILON);
let stiffness_fd =
(stiffness_fd - potential.force(x)) / perturbation::<Length>(EPSILON);
let anharmonicity_fd =
(anharmonicity_fd - potential.stiffness(x)) / perturbation::<Length>(EPSILON);
Assert::default().eq_within_fd_tol(force, &force_fd)?;
Assert::default().eq_within_fd_tol(stiffness, &stiffness_fd)?;
Assert::default().eq_within_fd_tol(anharmonicity, &anharmonicity_fd)?;
let extension = potential.extension(force);
let compliance = potential.compliance(force);
let nondimensional_extension = potential.nondimensional_extension(force.value(), t);
let nondimensional_force = potential.nondimensional_force(nondimensional_extension, t);
Assert::default().eq_within_tols(
potential.nondimensional_energy(nondimensional_extension, t),
&potential.nondimensional_energy_at_nondimensional_force(nondimensional_force, t),
)?;
force += perturbation(0.5 * EPSILON);
let extension_fd = potential.legendre(force);
let compliance_fd = potential.extension(force);
force -= perturbation(EPSILON);
let extension_fd =
(potential.legendre(force) - extension_fd) / perturbation::<Force>(EPSILON);
let compliance_fd =
(compliance_fd - potential.extension(force)) / perturbation::<Force>(EPSILON);
Assert::default().eq_within_fd_tol(extension, &extension_fd)?;
Assert::default().eq_within_fd_tol(compliance, &compliance_fd)
})?;
let potential = Morse {
rest_length: x0,
depth: e,
parameter: a,
};
(1..NUM)
.map(|k| Quantity::new(x0 + (x_max - x0) * k as Scalar / NUM as Scalar))
.into_iter()
.try_for_each(|mut x| {
let mut force = potential.force(x);
let stiffness = potential.stiffness(x);
let anharmonicity = potential.anharmonicity(x);
Assert::default()
.eq_within_tols(potential.energy(x), &potential.energy_at_force(force))?;
x += perturbation(0.5 * EPSILON);
let force_fd = potential.energy(x);
let stiffness_fd = potential.force(x);
let anharmonicity_fd = potential.stiffness(x);
x -= perturbation(EPSILON);
let force_fd = (force_fd - potential.energy(x)) / perturbation::<Length>(EPSILON);
let stiffness_fd =
(stiffness_fd - potential.force(x)) / perturbation::<Length>(EPSILON);
let anharmonicity_fd =
(anharmonicity_fd - potential.stiffness(x)) / perturbation::<Length>(EPSILON);
Assert::default().eq_within_fd_tol(force, &force_fd)?;
Assert::default().eq_within_fd_tol(stiffness, &stiffness_fd)?;
Assert::default().eq_within_fd_tol(anharmonicity, &anharmonicity_fd)?;
let extension = potential.extension(force);
let compliance = potential.compliance(force);
let nondimensional_extension = potential.nondimensional_extension(force.value(), t);
force += perturbation(0.5 * EPSILON);
let extension_fd = potential.legendre(force);
let compliance_fd = potential.extension(force);
let mut nondimensional_extension_fd =
potential.nondimensional_legendre(force.value(), t);
force -= perturbation(EPSILON);
let extension_fd =
(potential.legendre(force) - extension_fd) / perturbation::<Force>(EPSILON);
let compliance_fd =
(compliance_fd - potential.extension(force)) / perturbation::<Force>(EPSILON);
nondimensional_extension_fd = (potential.nondimensional_legendre(force.value(), t)
- nondimensional_extension_fd)
/ EPSILON;
Assert::default().eq_within_fd_tol(extension, &extension_fd)?;
Assert::default().eq_within_fd_tol(compliance, &compliance_fd)?;
Assert::default()
.eq_within_fd_tol(nondimensional_extension, &nondimensional_extension_fd)
})
}