Skip to main content

moldyn_core/forces/
custom.rs

1//! This module contains the [LennardJonesForce] struct, which implements
2//! the [Force] trait according to the Lennard-Jones potential.
3
4use crate::{Force, Particle};
5use meval::Expr;
6
7pub struct CustomForce {
8    func: Box<dyn Fn(&Particle, &Particle) -> f64>,
9}
10
11impl Force for CustomForce {
12    fn system_name(&self) -> &str {
13        "custom-potential"
14    }
15
16    fn potential(&self, particle: &Particle, other: &Particle) -> f64 {
17        (self.func)(particle, other)
18    }
19}
20
21impl CustomForce {
22    pub fn new(func: Box<dyn Fn(&Particle, &Particle) -> f64>) -> Self {
23        Self { func }
24    }
25
26    /// Quickly creates a force system from a string-like, panicking on any
27    /// problem. Useful in test environments and for benchmarks.
28    #[allow(dead_code)]
29    #[cfg(test)]
30    pub fn from_expr(expr: &str) -> Self {
31        expr.parse::<Expr>()
32            .expect("failed to parse expression")
33            .try_into()
34            .expect("failed to convert expression to custom force")
35    }
36}
37
38impl TryFrom<Expr> for CustomForce {
39    type Error = meval::Error;
40
41    fn try_from(value: Expr) -> Result<Self, Self::Error> {
42        let func = value.bind2("r", "M")?;
43
44        let wrap = move |p1: &Particle, p2: &Particle| {
45            let distance = Particle::distance(p1, p2);
46            let mul_mass = Particle::mass_product(p1, p2);
47
48            if distance == 0.0 {
49                0.0
50            } else {
51                -func(distance, mul_mass)
52            }
53        };
54
55        Ok(CustomForce::new(Box::new(wrap)))
56    }
57}