use arraystring::{typenum::U5, ArrayString};
pub use glam::Vec3;
#[derive(Debug, Clone, PartialEq)]
pub struct Structure {
pub title: String,
pub atoms: Vec<Atom>,
pub boxvecs: BoxVecs,
}
impl Structure {
#[must_use]
pub fn center(&self) -> Vec3 {
self.atoms.iter().map(|atom| atom.position).sum::<Vec3>() / self.natoms() as f32
}
pub fn translate_to_center(&mut self) {
let center = self.center();
self.atoms
.iter_mut()
.for_each(|atom| atom.position -= center);
}
#[must_use]
pub fn natoms(&self) -> usize {
self.atoms.len()
}
}
pub type ResNum = u32;
pub type AtomNum = u32;
pub type ResName = ArrayString<U5>;
pub type AtomName = ArrayString<U5>;
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub struct Atom {
pub resnum: ResNum,
pub resname: ResName,
pub atomname: AtomName,
pub atomnum: AtomNum,
pub position: Vec3,
pub velocity: Vec3,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BoxVecs {
Short([f32; 3]),
Full([f32; 9]),
}
impl BoxVecs {
#[must_use]
pub const fn as_array(&self) -> [f32; 9] {
match *self {
BoxVecs::Short([v1x, v2y, v3z]) => [v1x, v2y, v3z, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
BoxVecs::Full(bv) => bv,
}
}
#[must_use]
pub const fn as_vecs(&self) -> [Vec3; 3] {
match *self {
BoxVecs::Short([v1x, v2y, v3z]) => [
Vec3::new(v1x, 0.0, 0.0),
Vec3::new(0.0, v2y, 0.0),
Vec3::new(0.0, 0.0, v3z),
],
BoxVecs::Full([v1x, v2y, v3z, v1y, v1z, v2x, v2z, v3x, v3y]) => [
Vec3::new(v1x, v1y, v1z),
Vec3::new(v2x, v2y, v2z),
Vec3::new(v3x, v3y, v3z),
],
}
}
}
impl std::fmt::Display for BoxVecs {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn list<const N: usize>(vs: &[f32; N]) -> String {
vs.map(|v| v.to_string()).join(" ")
}
f.write_str(&match self {
BoxVecs::Short(vs) => list(vs),
BoxVecs::Full(vs) => list(vs),
})
}
}
#[cfg(test)]
mod tests {
use std::io;
use super::*;
use crate::reader::ReadGro;
const EPS: f32 = 0.0001;
#[test]
fn center() -> io::Result<()> {
let structure = Structure::open_gro(crate::tests::PATH)?;
let center = Vec3::new(3.9875, 3.9760, 2.7035);
assert!(structure.center().abs_diff_eq(center, EPS));
Ok(())
}
#[test]
fn translate_to_center() -> io::Result<()> {
let mut structure = Structure::open_gro(crate::tests::PATH)?;
structure.translate_to_center();
assert!(structure.center().abs_diff_eq(Vec3::ZERO, EPS));
Ok(())
}
#[test]
fn boxvecs_short() {
let short = BoxVecs::Short([10.0, 20.0, 30.0]);
assert_eq!(
short.as_array(),
[10.0, 20.0, 30.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
);
assert_eq!(
short.as_vecs(),
[
Vec3::new(10.0, 0.0, 0.0),
Vec3::new(0.0, 20.0, 0.0),
Vec3::new(0.0, 0.0, 30.0)
]
);
}
#[test]
fn boxvecs_long() {
let full = BoxVecs::Full([10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0]);
assert_eq!(
full.as_array(),
[10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0]
);
assert_eq!(
full.as_vecs(),
[
Vec3::new(10.0, 40.0, 50.0),
Vec3::new(60.0, 20.0, 70.0),
Vec3::new(80.0, 90.0, 30.0)
]
);
}
}