use molrs::store::frame_access::FrameAccess;
use molrs::types::F;
use crate::compute::error::ComputeError;
use crate::compute::util::MicHelper;
use super::observable::{AtomGroups, Observable, cross, displacement, dot, norm, positions};
#[derive(Debug, Clone, Default)]
pub struct DihedralObservable;
impl Observable for DihedralObservable {
fn arity(&self) -> usize {
4
}
fn is_angular(&self) -> bool {
false
}
fn natural_range(&self) -> Option<(F, F)> {
Some((-std::f64::consts::PI, std::f64::consts::PI))
}
fn sample<FA: FrameAccess>(
&self,
frame: &FA,
groups: &AtomGroups,
) -> Result<Vec<F>, ComputeError> {
let mut out = Vec::with_capacity(groups.len());
self.sample_into(frame, groups, &mut out)?;
Ok(out)
}
fn sample_into<FA: FrameAccess>(
&self,
frame: &FA,
groups: &AtomGroups,
out: &mut Vec<F>,
) -> Result<(), ComputeError> {
if groups.arity() != 4 {
return Err(ComputeError::BadShape {
expected: "arity 4".to_string(),
got: format!("arity {}", groups.arity()),
});
}
let (xp, yp, zp) = positions(frame)?;
let (xs, ys, zs) = (xp.slice(), yp.slice(), zp.slice());
let mic = MicHelper::from_simbox(frame.simbox_ref());
out.clear();
out.reserve(groups.len());
for g in 0..groups.len() {
let t = groups.tuple(g);
let (i, j, k, l) = (t[0] as usize, t[1] as usize, t[2] as usize, t[3] as usize);
let b1 = displacement(&mic, xs, ys, zs, i, j); let b2 = displacement(&mic, xs, ys, zs, j, k); let b3 = displacement(&mic, xs, ys, zs, k, l); let n2 = norm(b2);
if n2 == 0.0 {
return Err(ComputeError::NonFinite {
where_: "DihedralObservable: zero-length central bond",
index: g,
});
}
let c12 = cross(b1, b2);
let c23 = cross(b2, b3);
let x = dot(c12, c23);
let y = dot([b1[0] * n2, b1[1] * n2, b1[2] * n2], c23);
if x == 0.0 && y == 0.0 {
out.push(0.0);
} else {
out.push(y.atan2(x));
}
}
Ok(())
}
}