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