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 // Parallel evaluation using rayon
34 // We use a mutex or temporary local gradients to avoid data races when writing to 'grad'
35 // For simplicity and matching r.md, we use a local grad vector per term if needed,
36 // or just sequential for now if the number of terms is small.
37 // Actually r.md suggests:
38 /*
39 let mut local_grads = vec![0.0; grad.len()];
40 let total_energy = self.iter_terms.iter().map(|term| {
41 term.evaluate_energy_and_inject_gradient(coords, &mut local_grads)
42 }).sum();
43 */
44 // But the above has a shared local_grads which is still a problem if parallel.
45
46 // Let's implement it sequentially first as a baseline, or use rayon's fold/reduce if parallel.
47 let mut total_energy = 0.0;
48 for term in &self.iter_terms {
49 total_energy += term.evaluate_energy_and_inject_gradient(coords, grad);
50 }
51
52 total_energy
53 }
54}