use crate::bond::BondOrder;
use crate::error::CError;
use crate::property::Properties;
use crate::residue::Residue;
use crate::unit_cell::UnitCell;
use crate::{atom::Atom, topology::Topology};
use std::ops::{Deref, Index, IndexMut};
#[derive(Debug, Default)]
pub struct Frame {
pub(crate) index: usize,
pub unit_cell: UnitCell,
pub properties: Properties,
positions: Vec<[f64; 3]>,
pub(crate) velocities: Option<Vec<[f64; 3]>>,
topology: Topology,
}
impl Frame {
#[must_use]
pub fn new() -> Self {
Frame {
index: 0,
unit_cell: UnitCell::new(),
properties: Properties::new(),
positions: vec![],
velocities: None,
topology: Topology::default(),
}
}
#[must_use]
pub fn from_unitcell(unit_cell: UnitCell) -> Self {
Frame {
index: 0,
unit_cell,
properties: Properties::new(),
positions: vec![],
velocities: None,
topology: Topology::default(),
}
}
pub fn get_frame_index(&self) -> usize {
self.index
}
pub fn set_frame_index(&mut self, index: usize) {
self.index = index;
}
#[must_use]
pub fn topology(&self) -> &Topology {
&self.topology
}
pub fn set_topology(&mut self, topology: Topology) -> Result<(), CError> {
if topology.len() != self.size() {
return Err(CError::GenericError(format!(
"the topology contains {} atoms, but the frame contains {} atoms",
topology.len(),
self.size(),
)));
}
self.topology = topology;
Ok(())
}
pub fn topology_mut(&mut self) -> &mut Topology {
&mut self.topology
}
#[must_use]
pub fn cell(&self) -> &UnitCell {
&self.unit_cell
}
pub fn set_unit_cell(&mut self, cell: UnitCell) {
self.unit_cell = cell;
}
#[must_use]
pub fn positions(&self) -> &Vec<[f64; 3]> {
&self.positions
}
pub fn positions_mut(&mut self) -> &mut [[f64; 3]] {
self.positions.as_mut_slice()
}
#[must_use]
pub fn velocities(&self) -> Option<&[[f64; 3]]> {
self.velocities.as_deref()
}
pub fn velocities_mut(&mut self) -> Option<&mut [[f64; 3]]> {
self.velocities.as_deref_mut()
}
pub fn add_atom(&mut self, atom: Atom, position: [f64; 3]) {
self.topology.atoms.push(atom);
self.positions.push(position);
}
pub fn add_atom_with_velocity(&mut self, atom: Atom, position: [f64; 3], velocity: [f64; 3]) {
self.topology.atoms.push(atom);
self.positions.push(position);
if let Some(velocities) = self.velocities.as_mut() {
velocities.push(velocity);
}
}
pub fn clear_bonds(&mut self) {
self.topology.clear_bonds();
}
pub fn add_residue(&mut self, residue: Residue) -> Result<(), CError> {
self.topology.add_residue(residue)
}
pub fn resize(&mut self, size: usize) -> Result<(), CError> {
self.topology.resize(size)?;
self.positions.resize(size, [0.0; 3]);
if let Some(velocities) = self.velocities.as_mut() {
velocities.resize(size, [0.0; 3]);
}
Ok(())
}
pub fn reserve(&mut self, size: usize) {
self.topology.reserve(size);
self.positions.reserve(size);
}
pub fn add_bond(&mut self, i: usize, j: usize, bond_order: BondOrder) -> Result<(), CError> {
self.topology.add_bond(i, j, bond_order)
}
pub(crate) fn add_velocities(&mut self) {
if self.velocities.is_none() {
self.velocities = Some(vec![[0.0; 3]; self.size()]);
}
}
pub(crate) fn size(&self) -> usize {
debug_assert!(self.positions.len() == self.topology.len());
if let Some(velocities) = self.velocities.as_ref() {
debug_assert!(self.positions.len() == velocities.len());
}
self.positions.len()
}
}
impl Index<usize> for Frame {
type Output = Atom;
fn index(&self, index: usize) -> &Self::Output {
&self.topology.atoms[index]
}
}
impl IndexMut<usize> for Frame {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.topology.atoms[index]
}
}
impl Deref for Frame {
type Target = Vec<Atom>;
fn deref(&self) -> &Self::Target {
&self.topology.atoms
}
}
#[cfg(test)]
mod tests {
use assert_approx_eq::assert_approx_eq;
use super::*;
#[test]
fn test_frame_indexing() {
let mut frame = Frame::new();
let atom1 = Atom {
symbol: "H".to_string(),
name: "hydrogen".to_string(),
charge: 0.0,
mass: 0.0,
properties: Properties::new(),
};
let atom2 = Atom {
symbol: "O".to_string(),
name: "oxygen".to_string(),
charge: 0.0,
mass: 0.0,
properties: Properties::new(),
};
frame.add_atom(atom1, [1.0, 2.0, 3.0]);
frame.add_atom(atom2, [4.0, 5.0, 6.0]);
assert_eq!(frame[0].symbol, "H");
assert_eq!(frame[1].symbol, "O");
let pos = frame.positions();
assert_approx_eq!(pos[0][0], 1.0);
assert_approx_eq!(pos[1][0], 4.0);
}
#[test]
#[should_panic(expected = "index out of bounds")]
fn test_frame_indexing_out_of_bounds() {
let frame = Frame::new();
let _ = frame[0];
}
}