sci_form/forcefield/
traits.rs1pub trait ForceFieldContribution: Send + Sync {
3 fn evaluate_energy_and_inject_gradient(&self, coords: &[f64], grad: &mut [f64]) -> f64;
6}
7
8pub struct MolecularForceField {
9 pub iter_terms: Vec<Box<dyn ForceFieldContribution>>,
10}
11
12impl Default for MolecularForceField {
13 fn default() -> Self {
14 Self::new()
15 }
16}
17
18impl MolecularForceField {
19 pub fn new() -> Self {
20 MolecularForceField {
21 iter_terms: Vec::new(),
22 }
23 }
24
25 pub fn insert_dynamic_term(&mut self, term: Box<dyn ForceFieldContribution>) {
26 self.iter_terms.push(term);
27 }
28
29 pub fn compute_system_energy_and_gradients(&self, coords: &[f64], grad: &mut [f64]) -> f64 {
31 grad.fill(0.0);
32
33 #[cfg(feature = "parallel")]
34 {
35 use rayon::prelude::*;
36
37 let (total_energy, total_grad) = self
38 .iter_terms
39 .par_iter()
40 .map(|term| {
41 let mut local_grad = vec![0.0; grad.len()];
42 let energy = term.evaluate_energy_and_inject_gradient(coords, &mut local_grad);
43 (energy, local_grad)
44 })
45 .reduce(
46 || (0.0, vec![0.0; grad.len()]),
47 |mut left, right| {
48 left.0 += right.0;
49 for (dst, src) in left.1.iter_mut().zip(right.1.into_iter()) {
50 *dst += src;
51 }
52 left
53 },
54 );
55
56 grad.copy_from_slice(&total_grad);
57 total_energy
58 }
59
60 #[cfg(not(feature = "parallel"))]
61 {
62 let mut total_energy = 0.0;
63 for term in &self.iter_terms {
64 total_energy += term.evaluate_energy_and_inject_gradient(coords, grad);
65 }
66
67 total_energy
68 }
69 }
70}