#[derive(Debug, Clone, Copy)]
pub enum CantileverMode {
Fundamental,
FirstOvertone,
Torsional,
}
#[derive(Debug, Clone)]
pub struct Cantilever {
pub f0: f64,
pub q_factor: f64,
pub mass: f64,
pub k_spring: f64,
pub displacement: f64,
pub velocity: f64,
}
impl Cantilever {
pub fn new(f0: f64, q_factor: f64, mass: f64) -> Self {
let omega = 2.0 * std::f64::consts::PI * f0;
let k_spring = mass * omega * omega;
Self {
f0,
q_factor,
mass,
k_spring,
displacement: 0.0,
velocity: 0.0,
}
}
pub fn afm_cantilever() -> Self {
Self::new(
300.0e3, 10000.0, 1.0e-12, )
}
pub fn evolve(&mut self, force: f64, dt: f64) {
let omega0 = 2.0 * std::f64::consts::PI * self.f0;
let damping = self.mass * omega0 / self.q_factor;
let spring_force = -self.k_spring * self.displacement;
let damping_force = -damping * self.velocity;
let total_force = spring_force + damping_force + force;
let a1 = total_force / self.mass;
self.displacement += self.velocity * dt + 0.5 * a1 * dt * dt;
let spring_force_new = -self.k_spring * self.displacement;
let damping_force_new = -damping * self.velocity; let total_force_new = spring_force_new + damping_force_new + force;
let a2 = total_force_new / self.mass;
self.velocity += 0.5 * (a1 + a2) * dt;
}
pub fn kinetic_energy(&self) -> f64 {
0.5 * self.mass * self.velocity * self.velocity
}
pub fn potential_energy(&self) -> f64 {
0.5 * self.k_spring * self.displacement * self.displacement
}
pub fn total_energy(&self) -> f64 {
self.kinetic_energy() + self.potential_energy()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cantilever_creation() {
let cant = Cantilever::afm_cantilever();
assert!(cant.f0 > 0.0);
assert!(cant.q_factor > 0.0);
}
#[test]
fn test_energy_conservation() {
let mut cant = Cantilever::afm_cantilever();
cant.displacement = 1.0e-9;
let e0 = cant.total_energy();
for _ in 0..100 {
cant.evolve(0.0, 1.0e-8);
}
assert!(cant.total_energy() < e0);
assert!(cant.total_energy() > 0.0);
}
}