1use gut::prelude::*;
3
4use crate::element::*;
5use crate::property::PropertyStore;
6pub type Vector3f = vecfx::Vector3f;
11
12pub type Point3 = [f64; 3];
14
15#[derive(Clone, Debug, Deserialize, Serialize)]
22pub struct Atom {
23 pub properties: PropertyStore,
26
27 kind: AtomKind,
29
30 position: Vector3f,
32
33 label: Option<String>,
35
36 pub(crate) velocity: Vector3f,
38
39 pub(crate) mass: Option<f64>,
41
42 pub(crate) partial_charge: Option<f64>,
44
45 freezing: [bool; 3],
47}
48
49impl Default for Atom {
50 fn default() -> Self {
51 Self {
52 properties: PropertyStore::default(),
53 kind: "C".into(),
54 position: Vector3f::new(0.0, 0.0, 0.0),
55 velocity: Vector3f::new(0.0, 0.0, 0.0),
56 partial_charge: None,
57
58 mass: None,
60 label: None,
61 freezing: [false; 3],
62 }
63 }
64}
65
66impl Atom {
67 pub fn new<S: Into<AtomKind>, P: Into<Vector3f>>(s: S, p: P) -> Self {
69 Self {
70 kind: s.into(),
71 position: p.into(),
72 ..Default::default()
73 }
74 }
75
76 pub fn symbol(&self) -> &str {
78 self.kind.symbol()
79 }
80
81 pub fn number(&self) -> usize {
83 self.kind.number()
84 }
85
86 pub fn position(&self) -> Point3 {
88 self.position.into()
89 }
90
91 pub fn set_position<P: Into<Vector3f>>(&mut self, p: P) {
93 self.position = p.into();
94 }
95
96 pub fn kind(&self) -> &AtomKind {
98 &self.kind
99 }
100
101 pub fn set_label<S: Into<String>>(&mut self, lbl: S) {
103 self.label = Some(lbl.into());
104 }
105
106 #[deprecated(note = "use get_label instead")]
108 pub fn label(&self) -> &str {
109 if let Some(ref l) = self.label {
110 return l;
111 }
112
113 self.symbol()
115 }
116
117 pub fn get_label(&self) -> Option<&str> {
119 self.label.as_deref()
120 }
121
122 pub fn set_symbol<S: Into<AtomKind>>(&mut self, symbol: S) {
124 self.kind = symbol.into()
125 }
126
127 pub fn set_partial_charge(&mut self, c: f64) {
130 self.partial_charge = Some(c);
131 }
132
133 pub fn is_dummy(&self) -> bool {
135 match self.kind {
136 AtomKind::Element(_) => false,
137 AtomKind::Dummy(_) => true,
138 }
139 }
140
141 pub fn is_element(&self) -> bool {
143 !self.is_dummy()
144 }
145
146 pub fn is_fixed(&self) -> bool {
148 self.freezing.iter().all(|f| *f)
149 }
150
151 pub fn set_freezing(&mut self, freezing: [bool; 3]) {
153 self.freezing = freezing;
154 }
155
156 pub fn freezing(&self) -> [bool; 3] {
158 self.freezing
159 }
160
161 pub fn update_position<P: Into<Vector3f>>(&mut self, p: P) {
164 let new_position: Vector3f = p.into();
166 for (i, masked) in self.freezing.iter().enumerate() {
167 if !*masked {
168 self.position[i] = new_position[i];
169 }
170 }
171 }
172}
173use std::convert::From;
177use std::str::FromStr;
178
179impl FromStr for Atom {
180 type Err = Error;
181
182 fn from_str(line: &str) -> Result<Self> {
183 let parts: Vec<_> = line.split_whitespace().collect();
184 let nparts = parts.len();
185 ensure!(nparts >= 4, "Incorrect number of data fields: {line:?}");
186 let sym = parts[0];
187 let px: f64 = parts[1].parse()?;
188 let py: f64 = parts[2].parse()?;
189 let pz: f64 = parts[3].parse()?;
190
191 let mut atom = Atom::new(sym, [px, py, pz]);
192 if nparts >= 6 {
194 let vxyz: Vec<_> = parts[4..7].iter().filter_map(|x| x.parse().ok()).collect();
195 if vxyz.len() == 3 {
196 atom.velocity = [vxyz[0], vxyz[1], vxyz[2]].into();
197 }
198 }
199
200 Ok(atom)
201 }
202}
203
204impl std::fmt::Display for Atom {
205 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
206 write!(
207 f,
208 "{:6} {:-12.6} {:-12.6} {:-12.6}",
209 self.symbol(),
210 self.position[0],
211 self.position[1],
212 self.position[2]
213 )
214 }
215}
216
217impl<S, P> From<(S, P)> for Atom
218where
219 S: Into<AtomKind>,
220 P: Into<Vector3f>,
221{
222 fn from(item: (S, P)) -> Self {
223 Self::new(item.0, item.1)
224 }
225}
226#[test]
230fn test_atom_basic() {
231 let _ = Atom::default();
232 let atom = Atom::new("Fe", [9.3; 3]);
233 assert_eq!(9.3, atom.position()[0]);
234 assert_eq!("Fe", atom.symbol());
235 assert_eq!(26, atom.number());
236
237 let mut atom = Atom::new(6, [1.0, 0.0, 0.0]);
238 assert_eq!(atom.symbol(), "C");
239 atom.set_symbol("H");
240 assert_eq!(atom.symbol(), "H");
241
242 let atom = Atom::new("X", [9.3; 3]);
243 assert_eq!("X", atom.symbol());
244 assert_eq!(0, atom.number());
245}
246
247#[test]
248fn test_atom_convert() {
249 let line = "H 1.0 1.0 1.0";
250 let a: Atom = line.parse().unwrap();
251 let line = a.to_string();
252 let b: Atom = line.parse().unwrap();
253 assert_eq!(a.symbol(), b.symbol());
254 assert_eq!(a.position(), b.position());
255
256 let a: Atom = (1, [0.0; 3]).into();
258 assert_eq!(a.number(), 1);
259
260 let line = "H 1.0 1.0 1.0 2.0 0.0 0";
262 let a: Atom = line.parse().unwrap();
263 assert_eq!(a.velocity.x, 2.0);
264 assert_eq!(a.velocity.y, 0.0);
265 assert_eq!(a.velocity.z, 0.0);
266}
267