Skip to main content

sci_form/forcefield/
traits.rs

1/// Interfaz conductiva fundamental para cualquier operador matemático empírico molecular
2pub trait ForceFieldContribution: Send + Sync {
3    /// Computa algebraicamente el escalar de energía asumiendo posición local
4    /// e injerta los gradientes geométricos acumulativamente a lo largo del array
5    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    /// Método de evaluación crítica masiva invocado exhaustivamente iteración por iteración.
30    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}