use ndarray::{Array1, ArrayView1};
pub struct SpringForces {
pub parallel: Array1<f64>,
pub full: Array1<f64>,
}
#[derive(Clone, Debug)]
pub enum SpringKind {
Uniform { k: f64 },
Weighted { ks: Vec<f64> },
OnsagerMachlup { k: f64, l_vecs: Vec<Array1<f64>> },
}
impl SpringKind {
#[allow(clippy::too_many_arguments)]
pub fn compute(
&self,
i: usize,
tangent: ArrayView1<f64>,
dist_next: f64,
dist_prev: f64,
pos_diff_next: ArrayView1<f64>,
pos_diff_prev: ArrayView1<f64>,
pos: ArrayView1<f64>,
pos_prev: ArrayView1<f64>,
pos_next: ArrayView1<f64>,
) -> SpringForces {
match self {
SpringKind::Uniform { k } => SpringForces {
parallel: tangent.to_owned() * (k * (dist_next - dist_prev)),
full: (&pos_diff_next - &pos_diff_prev) * *k,
},
SpringKind::Weighted { ks } => {
let k_next = ks[i];
let k_prev = ks[i - 1];
SpringForces {
parallel: tangent.to_owned() * (k_next * dist_next - k_prev * dist_prev),
full: Array1::zeros(tangent.len()),
}
}
SpringKind::OnsagerMachlup { k, l_vecs } => {
let diff = &pos_next + &pos_prev - &(&pos * 2.0) + &l_vecs[i + 1] - &l_vecs[i];
let f_om = diff * *k;
let par = f_om.dot(&tangent);
SpringForces {
parallel: tangent.to_owned() * par,
full: Array1::zeros(tangent.len()),
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_abs_diff_eq;
use ndarray::array;
#[test]
fn uniform_parallel_balances_equal_spacing() {
let t = array![1.0, 0.0];
let pdn = array![0.5, 0.0];
let pdp = array![0.5, 0.0];
let s = SpringKind::Uniform { k: 2.0 };
let f = s.compute(
1,
t.view(),
0.5,
0.5,
pdn.view(),
pdp.view(),
array![0.0, 0.0].view(),
array![-0.5, 0.0].view(),
array![0.5, 0.0].view(),
);
assert_abs_diff_eq!(f.parallel[0], 0.0, epsilon = 1e-12);
assert_abs_diff_eq!(f.full[0], 0.0, epsilon = 1e-12);
}
#[test]
fn uniform_parallel_pulls_toward_longer_segment() {
let t = array![1.0, 0.0];
let pdn = array![0.7, 0.0];
let pdp = array![0.3, 0.0];
let s = SpringKind::Uniform { k: 2.0 };
let f = s.compute(
1,
t.view(),
0.7,
0.3,
pdn.view(),
pdp.view(),
array![0.0, 0.0].view(),
array![-0.3, 0.0].view(),
array![0.7, 0.0].view(),
);
assert_abs_diff_eq!(f.parallel[0], 2.0 * 0.4, epsilon = 1e-12);
assert_abs_diff_eq!(f.full[0], 2.0 * 0.4, epsilon = 1e-12);
}
}