use gut::prelude::*;
use crate::element::*;
use crate::property::PropertyStore;
pub type Vector3f = vecfx::Vector3f;
pub type Point3 = [f64; 3];
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Atom {
pub properties: PropertyStore,
kind: AtomKind,
position: Vector3f,
label: Option<String>,
pub(crate) velocity: Vector3f,
pub(crate) mass: Option<f64>,
pub(crate) partial_charge: Option<f64>,
freezing: [bool; 3],
}
impl Default for Atom {
fn default() -> Self {
Self {
properties: PropertyStore::default(),
kind: "C".into(),
position: Vector3f::new(0.0, 0.0, 0.0),
velocity: Vector3f::new(0.0, 0.0, 0.0),
partial_charge: None,
mass: None,
label: None,
freezing: [false; 3],
}
}
}
impl Atom {
pub fn new<S: Into<AtomKind>, P: Into<Vector3f>>(s: S, p: P) -> Self {
Self {
kind: s.into(),
position: p.into(),
..Default::default()
}
}
pub fn symbol(&self) -> &str {
self.kind.symbol()
}
pub fn number(&self) -> usize {
self.kind.number()
}
pub fn position(&self) -> Point3 {
self.position.into()
}
pub fn set_position<P: Into<Vector3f>>(&mut self, p: P) {
self.position = p.into();
}
pub fn kind(&self) -> &AtomKind {
&self.kind
}
pub fn set_label<S: Into<String>>(&mut self, lbl: S) {
self.label = Some(lbl.into());
}
#[deprecated(note = "use get_label instead")]
pub fn label(&self) -> &str {
if let Some(ref l) = self.label {
return l;
}
self.symbol()
}
pub fn get_label(&self) -> Option<&str> {
self.label.as_deref()
}
pub fn set_symbol<S: Into<AtomKind>>(&mut self, symbol: S) {
self.kind = symbol.into()
}
pub fn set_partial_charge(&mut self, c: f64) {
self.partial_charge = Some(c);
}
pub fn is_dummy(&self) -> bool {
match self.kind {
AtomKind::Element(_) => false,
AtomKind::Dummy(_) => true,
}
}
pub fn is_element(&self) -> bool {
!self.is_dummy()
}
pub fn is_fixed(&self) -> bool {
self.freezing.iter().all(|f| *f)
}
pub fn set_freezing(&mut self, freezing: [bool; 3]) {
self.freezing = freezing;
}
pub fn freezing(&self) -> [bool; 3] {
self.freezing
}
pub fn update_position<P: Into<Vector3f>>(&mut self, p: P) {
let new_position: Vector3f = p.into();
for (i, masked) in self.freezing.iter().enumerate() {
if !*masked {
self.position[i] = new_position[i];
}
}
}
}
use std::convert::From;
use std::str::FromStr;
impl FromStr for Atom {
type Err = Error;
fn from_str(line: &str) -> Result<Self> {
let parts: Vec<_> = line.split_whitespace().collect();
let nparts = parts.len();
ensure!(nparts >= 4, "Incorrect number of data fields: {line:?}");
let sym = parts[0];
let px: f64 = parts[1].parse()?;
let py: f64 = parts[2].parse()?;
let pz: f64 = parts[3].parse()?;
let mut atom = Atom::new(sym, [px, py, pz]);
if nparts >= 6 {
let vxyz: Vec<_> = parts[4..7].iter().filter_map(|x| x.parse().ok()).collect();
if vxyz.len() == 3 {
atom.velocity = [vxyz[0], vxyz[1], vxyz[2]].into();
}
}
Ok(atom)
}
}
impl std::fmt::Display for Atom {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{:6} {:-12.6} {:-12.6} {:-12.6}",
self.symbol(),
self.position[0],
self.position[1],
self.position[2]
)
}
}
impl<S, P> From<(S, P)> for Atom
where
S: Into<AtomKind>,
P: Into<Vector3f>,
{
fn from(item: (S, P)) -> Self {
Self::new(item.0, item.1)
}
}
#[test]
fn test_atom_basic() {
let _ = Atom::default();
let atom = Atom::new("Fe", [9.3; 3]);
assert_eq!(9.3, atom.position()[0]);
assert_eq!("Fe", atom.symbol());
assert_eq!(26, atom.number());
let mut atom = Atom::new(6, [1.0, 0.0, 0.0]);
assert_eq!(atom.symbol(), "C");
atom.set_symbol("H");
assert_eq!(atom.symbol(), "H");
let atom = Atom::new("X", [9.3; 3]);
assert_eq!("X", atom.symbol());
assert_eq!(0, atom.number());
}
#[test]
fn test_atom_convert() {
let line = "H 1.0 1.0 1.0";
let a: Atom = line.parse().unwrap();
let line = a.to_string();
let b: Atom = line.parse().unwrap();
assert_eq!(a.symbol(), b.symbol());
assert_eq!(a.position(), b.position());
let a: Atom = (1, [0.0; 3]).into();
assert_eq!(a.number(), 1);
let line = "H 1.0 1.0 1.0 2.0 0.0 0";
let a: Atom = line.parse().unwrap();
assert_eq!(a.velocity.x, 2.0);
assert_eq!(a.velocity.y, 0.0);
assert_eq!(a.velocity.z, 0.0);
}