Skip to main content

conspire/physics/molecular/potential/
mod.rs

1#[cfg(test)]
2mod test;
3
4mod harmonic;
5// mod lennard_jones;
6mod morse;
7
8pub use harmonic::Harmonic;
9pub use morse::Morse;
10
11use crate::{
12    math::{Quantity, Scalar},
13    units::{
14        BOLTZMANN_CONSTANT, Energy, Force, ForcePerLength, Length, ReciprocalForcePerLength,
15        Stress, Temperature,
16    },
17};
18use std::fmt::Debug;
19
20/// Potential models.
21pub trait Potential
22where
23    Self: Clone + Debug,
24{
25    /// ```math
26    /// u = u(x)
27    /// ```
28    fn energy(&self, length: Quantity<Length>) -> Quantity<Energy>;
29    /// ```math
30    /// \upsilon(\lambda) = \beta u
31    /// ```
32    fn nondimensional_energy(
33        &self,
34        nondimensional_length: Scalar,
35        temperature: Quantity<Temperature>,
36    ) -> Scalar {
37        let length = self.rest_length() * nondimensional_length;
38        (self.energy(length) / (BOLTZMANN_CONSTANT * temperature)).value()
39    }
40    /// ```math
41    /// u = u[x(f)]
42    /// ```
43    fn energy_at_force(&self, force: Quantity<Force>) -> Quantity<Energy> {
44        let extension = self.extension(force);
45        let length = self.rest_length() + extension;
46        self.energy(length)
47    }
48    /// ```math
49    /// \upsilon = \upsilon[\lambda(\eta)]
50    /// ```
51    fn nondimensional_energy_at_nondimensional_force(
52        &self,
53        nondimensional_force: Scalar,
54        temperature: Quantity<Temperature>,
55    ) -> Scalar {
56        let force = BOLTZMANN_CONSTANT * temperature / self.rest_length() * nondimensional_force;
57        (self.energy_at_force(force) / (BOLTZMANN_CONSTANT * temperature)).value()
58    }
59    /// ```math
60    /// f(x) = \frac{\partial u}{\partial x}
61    /// ```
62    fn force(&self, length: Quantity<Length>) -> Quantity<Force>;
63    /// ```math
64    /// \eta(\lambda) = \frac{\partial\upsilon}{\partial \lambda}
65    /// ```
66    fn nondimensional_force(
67        &self,
68        nondimensional_length: Scalar,
69        temperature: Quantity<Temperature>,
70    ) -> Scalar {
71        let length = self.rest_length() * nondimensional_length;
72        ((self.force(length) * self.rest_length()) / (BOLTZMANN_CONSTANT * temperature)).value()
73    }
74    /// ```math
75    /// f = x^{-1}[u^{-1}(u)]
76    /// ```
77    fn forces_at_energy(&self, energy: Quantity<Energy>) -> [Quantity<Force>; 2];
78    /// ```math
79    /// \eta = \lambda^{-1}[\upsilon^{-1}(\upsilon)]
80    /// ```
81    fn nondimensional_forces_at_nondimensional_energy(
82        &self,
83        nondimensional_energy: Scalar,
84        temperature: Quantity<Temperature>,
85    ) -> [Scalar; 2] {
86        let energy = BOLTZMANN_CONSTANT * temperature * nondimensional_energy;
87        self.forces_at_energy(energy).map(|force| {
88            ((force * self.rest_length()) / (BOLTZMANN_CONSTANT * temperature)).value()
89        })
90    }
91    /// ```math
92    /// k(x) = \frac{\partial f}{\partial x}
93    /// ```
94    fn stiffness(&self, length: Quantity<Length>) -> Quantity<ForcePerLength>;
95    /// ```math
96    /// \kappa(x) = \frac{\partial\eta}{\partial\lambda}
97    /// ```
98    fn nondimensional_stiffness(
99        &self,
100        nondimensional_length: Scalar,
101        temperature: Quantity<Temperature>,
102    ) -> Scalar {
103        let length = self.rest_length() * nondimensional_length;
104        ((self.stiffness(length) * (self.rest_length() * self.rest_length()))
105            / (BOLTZMANN_CONSTANT * temperature))
106            .value()
107    }
108    /// ```math
109    /// h(x) = \frac{\partial k}{\partial x}
110    /// ```
111    fn anharmonicity(&self, length: Quantity<Length>) -> Quantity<Stress>;
112    /// ```math
113    /// g(x) = \frac{\partial\kappa}{\partial\lambda}
114    /// ```
115    fn nondimensional_anharmonicity(
116        &self,
117        nondimensional_length: Scalar,
118        temperature: Quantity<Temperature>,
119    ) -> Scalar {
120        let length = self.rest_length() * nondimensional_length;
121        ((self.anharmonicity(length)
122            * (self.rest_length() * self.rest_length() * self.rest_length()))
123            / (BOLTZMANN_CONSTANT * temperature))
124            .value()
125    }
126    /// ```math
127    /// v(f) = u(x) - f\Delta x
128    /// ```
129    fn legendre(&self, force: Quantity<Force>) -> Quantity<Energy> {
130        let extension = self.extension(force);
131        let length = self.rest_length() + extension;
132        self.energy(length) - force * extension
133    }
134    /// ```math
135    /// \nu(\eta) = \upsilon(\lambda) - \eta\Delta\lambda
136    /// ```
137    fn nondimensional_legendre(
138        &self,
139        nondimensional_force: Scalar,
140        temperature: Quantity<Temperature>,
141    ) -> Scalar {
142        let force = BOLTZMANN_CONSTANT * temperature / self.rest_length() * nondimensional_force;
143        (self.legendre(force) / (BOLTZMANN_CONSTANT * temperature)).value()
144    }
145    /// ```math
146    /// \Delta x(f) = -\frac{\partial v}{\partial f}
147    /// ```
148    fn extension(&self, force: Quantity<Force>) -> Quantity<Length>;
149    /// ```math
150    /// \Delta\lambda(\eta) = -\frac{\partial\nu}{\partial\eta}
151    /// ```
152    fn nondimensional_extension(
153        &self,
154        nondimensional_force: Scalar,
155        temperature: Quantity<Temperature>,
156    ) -> Scalar {
157        let force = BOLTZMANN_CONSTANT * temperature / self.rest_length() * nondimensional_force;
158        (self.extension(force) / self.rest_length()).value()
159    }
160    /// ```math
161    /// x(f) = x_0 + \Delta x(f)
162    /// ```
163    fn length(&self, force: Quantity<Force>) -> Quantity<Length> {
164        self.rest_length() + self.extension(force)
165    }
166    /// ```math
167    /// \lambda(\eta) = 1 + \Delta\lambda(\eta)
168    /// ```
169    fn nondimensional_length(
170        &self,
171        nondimensional_force: Scalar,
172        temperature: Quantity<Temperature>,
173    ) -> Scalar {
174        1.0 + self.nondimensional_extension(nondimensional_force, temperature)
175    }
176    /// ```math
177    /// \Delta x = u^{-1}(u) - x_0
178    /// ```
179    fn extensions_at_energy(&self, energy: Quantity<Energy>) -> [Quantity<Length>; 2];
180    /// ```math
181    /// \Delta\lambda = \upsilon^{-1}(\upsilon) - 1
182    /// ```
183    fn nondimensional_extensions_at_nondimensional_energy(
184        &self,
185        nondimensional_energy: Scalar,
186        temperature: Quantity<Temperature>,
187    ) -> [Scalar; 2] {
188        let energy = BOLTZMANN_CONSTANT * temperature * nondimensional_energy;
189        self.extensions_at_energy(energy)
190            .map(|extension| (extension / self.rest_length()).value())
191    }
192    /// ```math
193    /// x = u^{-1}(u)
194    /// ```
195    fn lengths_at_energy(&self, energy: Quantity<Energy>) -> [Quantity<Length>; 2] {
196        self.extensions_at_energy(energy)
197            .map(|extension| extension + self.rest_length())
198    }
199    /// ```math
200    /// \lambda = \upsilon^{-1}(\upsilon)
201    /// ```
202    fn nondimensional_lengths_at_nondimensional_energy(
203        &self,
204        nondimensional_energy: Scalar,
205        temperature: Quantity<Temperature>,
206    ) -> [Scalar; 2] {
207        self.nondimensional_extensions_at_nondimensional_energy(nondimensional_energy, temperature)
208            .map(|extension| extension + 1.0)
209    }
210    /// ```math
211    /// c(x) = \frac{\partial\Delta x}{\partial f}
212    /// ```
213    fn compliance(&self, force: Quantity<Force>) -> Quantity<ReciprocalForcePerLength>;
214    /// ```math
215    /// \zeta(x) = \frac{\partial\Delta\lambda}{\partial\eta}
216    /// ```
217    fn nondimensional_compliance(
218        &self,
219        nondimensional_force: Scalar,
220        temperature: Quantity<Temperature>,
221    ) -> Scalar {
222        let force = BOLTZMANN_CONSTANT * temperature / self.rest_length() * nondimensional_force;
223        ((self.compliance(force) * (BOLTZMANN_CONSTANT * temperature))
224            / (self.rest_length() * self.rest_length()))
225        .value()
226    }
227    /// ```math
228    /// \text{arg max }u(x) = x_\mathrm{peak}
229    /// ```
230    fn peak(&self) -> Quantity<Length>;
231    /// ```math
232    /// f(x_\mathrm{peak}) = f_\mathrm{peak}
233    /// ```
234    fn peak_force(&self) -> Quantity<Force>;
235    /// ```math
236    /// \text{arg min }u(x) = x_0
237    /// ```
238    fn rest_length(&self) -> Quantity<Length>;
239}