gchemol_readwrite/formats/
vasp_input.rs1use super::parser::*;
7use super::*;
8fn poscar_cell_vectors(s: &str) -> IResult<&str, [[f64; 3]; 3]> {
12 do_parse!(
13 s,
14 space0 >> va: xyz_array >> eol >> space0 >> vb: xyz_array >> eol >> space0 >> vc: xyz_array >> eol >> ([va, vb, vc])
18 )
19}
20
21#[test]
22fn test_poscar_cell_vectors() {
23 let lines = " 21.23300000 0.00000000 0.00000000
24 0.00000000 26.60400000 0.00000000
25 0.00000000 0.00000000 12.67600000
26";
27
28 let (_, x) = poscar_cell_vectors(lines).expect("POSCAR cell vectors");
29 assert_eq!(21.233, x[0][0]);
30}
31fn poscar_ion_types(s: &str) -> IResult<&str, (Vec<&str>, Vec<usize>)> {
35 let mut elements = separated_list0(space1, alpha1);
36 let mut natoms = separated_list0(space1, unsigned_digit);
37 do_parse!(
38 s,
39 space0 >> e: elements >> eol >> space0 >> n: natoms >> eol >> ((e, n))
42 )
43}
44
45#[test]
46fn test_formats_vasp_poscar_ion_types() {
47 let lines = " O Si C N H
48 225 112 8 1 19 \n";
49 let (_, v) = poscar_ion_types(lines).expect("POSCAR ion types");
50 assert_eq!(5, v.0.len());
51}
52fn selective_dynamics(s: &str) -> IResult<&str, &str> {
58 let selective = tag_no_case("S");
59 do_parse!(s, x: selective >> read_line >> (x))
60}
61
62fn selective_dynamics_flags(s: &str) -> IResult<&str, [bool; 3]> {
65 let tf_flag = one_of("TF");
66 do_parse!(
67 s,
68 x: tf_flag >> space1 >> y: tf_flag >> space1 >> z: tf_flag >> ([x == 'T', y == 'T', z == 'T'])
70 )
71}
72
73#[test]
74fn test_poscar_select_dynamics() {
75 let (_, x) = selective_dynamics_flags("T T F").unwrap();
76 assert_eq!(x, [true, true, false]);
77}
78
79fn direct_or_catersian(s: &str) -> IResult<&str, &str> {
82 let mut coords_type = alt((tag_no_case("D"), tag_no_case("C")));
83 do_parse!(s, d: coords_type >> read_line >> (d))
84}
85
86fn poscar_select_direct(s: &str) -> IResult<&str, (bool, bool)> {
88 let mut selective_line = opt(selective_dynamics);
89 let direct_line = direct_or_catersian;
90 do_parse!(
91 s,
92 s: selective_line >> d: direct_line >> ({
95 let d = d.to_lowercase();
96 (s.is_some(), d == "d")
97 })
98 )
99}
100
101#[test]
102fn test_poscar_select_direct() {
103 let lines = "Selective dynamics
104Direct\n";
105
106 let (_, (s, d)) = poscar_select_direct(lines).expect("poscar selective/direct");
107 assert_eq!(true, s);
108 assert_eq!(true, d);
109
110 let (_, (s, d)) = poscar_select_direct("Direct\n").expect("poscar direct");
111 assert_eq!(false, s);
112 assert_eq!(true, d);
113 let (_, (s, d)) = poscar_select_direct("Cartesian\n").expect("poscar catersian");
114 assert_eq!(false, s);
115 assert_eq!(false, d);
116}
117
118fn poscar_position(s: &str) -> IResult<&str, ([f64; 3], Option<[bool; 3]>)> {
121 let mut frozen_flags = opt(selective_dynamics_flags);
122 do_parse!(
123 s,
124 space0 >> p: xyz_array >> space0 >> f: frozen_flags >> read_line >> ((p, f))
127 )
128}
129
130#[test]
131fn test_poscar_position() {
132 let line = " 0.05185 0.39121 0.29921 T T T # O \n";
133 let (_, (position, sflags)) = poscar_position(line).expect("POSCAR position style 1");
134 assert_eq!(0.05185, position[0]);
135 assert_eq!(0.39121, position[1]);
136 assert_eq!(0.29921, position[2]);
137 assert_eq!(Some([true, true, true]), sflags);
138
139 let line = " 0.05185 0.39121 0.29921\n";
140 let (_, (position, sflags)) = poscar_position(line).expect("POSCAR position style 1");
141 assert_eq!(None, sflags);
142}
143pub(crate) fn parse_poscar_molecule(s: &str) -> IResult<&str, Molecule> {
148 let mut read_ion_positions = many1(poscar_position);
149 do_parse!(
150 s,
151 title : read_until_eol >> lattice_constant : read_double >> cell_vectors : poscar_cell_vectors >> ion_types : poscar_ion_types >> select_direct : poscar_select_direct >> ion_positions : read_ion_positions >> (
158 {
159 let selective_dynamics = select_direct.0;
160 let direct_coordinates = select_direct.1;
161
162 let mut mol = Molecule::new(title.trim());
163 let mut lat = Lattice::new(cell_vectors);
164 let x = lat.to_cart([0.0; 3]);
165 lat.scale_by(lattice_constant);
166
167 let mut symbols = vec![];
168 let (syms, nums) = ion_types;
169
170 for (&sym, &num) in syms.iter().zip(nums.iter()) {
171 for _ in 0..num {
172 symbols.push(sym);
173 }
174 }
175
176 if symbols.len() != ion_positions.len() {
177 eprintln!("WARNING: some ions data not correctly parsed!");
178 }
179
180 for (i, (&sym, (pos, sflags))) in symbols.iter().zip(ion_positions).enumerate() {
181 let pos: Vector3f = if direct_coordinates {
182 lat.to_cart(pos)
183 } else {
184 pos.into()
185 };
186 let mut a = Atom::new(sym, pos);
187 if selective_dynamics {
189 if let Some([xs, ys, zs]) = sflags {
190 a.set_freezing([!xs, !ys, !zs]);
191 }
192 }
193 mol.add_atom(i+1, a);
194 }
195 mol.set_lattice(lat);
196 mol
197 }
198 )
199 )
200}
201
202#[test]
203fn test_poscar_molecule() {
204 let lines = "title
2051.0
206 21.23300000 0.00000000 0.00000000
207 0.00000000 26.60400000 0.00000000
208 0.00000000 0.00000000 12.67600000
209 O Si C N H
210225 112 8 1 19
211Selective dynamics
212Direct
213 0.05185 0.39121 0.29921 T T T # O
214 0.81339 0.57337 0.68777 T T T # O
215 0.73422 0.23229 0.85313 T T T # O
216 0.02246 0.05156 0.49349 T T T # O
217 0.64451 0.66726 0.17130 T T T # O
218 0.05185 0.07337 0.29921 T T T # O
219 0.60095 0.57471 0.17096 T T T # O
220 0.64451 0.66726 0.81569 T T T # O
221 0.33416 0.64745 0.88951 T T T # O
222 0.33416 0.31713 0.09747 T T T # O
223 0.93262 0.92263 0.99349 T T T # O
224 0.43262 0.79195 0.99349 T T T # O
225 0.73422 0.73229 0.13386 T T T # O
226 0.22073 0.66726 0.81569 T T T # O
227\n";
228
229 let txt = "Generated by cif2cell 1.1.5 from COD reference: 9008833. BAs : Wyckoff, R. W. G., Crystal Structures 1, 85-237 (1963).
234 4.777000
235 1.000000000000000 0.000000000000000 0.000000000000000
236 0.000000000000000 1.000000000000000 0.000000000000000
237 0.000000000000000 0.000000000000000 1.000000000000000
238 As B
239 4 4
240Direct
241 0.250000000000000 0.250000000000000 0.250000000000000
242 0.750000000000000 0.250000000000000 0.750000000000000
243 0.750000000000000 0.750000000000000 0.250000000000000
244 0.250000000000000 0.750000000000000 0.750000000000000
245 0.000000000000000 0.000000000000000 0.000000000000000
246 0.000000000000000 0.500000000000000 0.500000000000000
247 0.500000000000000 0.000000000000000 0.500000000000000
248 0.500000000000000 0.500000000000000 0.000000000000000
249";
250 let (_, mol) = parse_poscar_molecule(txt).expect("poscar molecule");
251 let lat = mol.get_lattice();
252 assert!(lat.is_some());
253 let [a, b, c] = lat.unwrap().lengths();
254 assert_eq!(a, 4.777);
255 assert_eq!(b, 4.777);
256 assert_eq!(c, 4.777);
257}
258fn format_molecule(mol: &Molecule) -> String {
262 let mut lines = String::new();
263 let title = mol.title();
264
265 lines.push_str(&format!("{}\n", title));
266 lines.push_str("1.0\n");
267 let mut lattice = mol.lattice.expect("poscar lattice");
268 let va = lattice.vector_a();
269 let vb = lattice.vector_b();
270 let vc = lattice.vector_c();
271
272 for v in [va, vb, vc].iter() {
273 let line = format!("{:12.8}{:12.8}{:12.8}\n", v[0], v[1], v[2]);
274 lines.push_str(&line);
275 }
276
277 let mut line1 = String::new();
279 let mut line2 = String::new();
280 for (s, n) in count_symbols(mol.symbols().collect()) {
281 line1.push_str(&format!(" {:^4}", s));
282 line2.push_str(&format!(" {:^4}", n));
283 }
284 lines.push_str(&format!("{}\n", line1));
285 lines.push_str(&format!("{}\n", line2));
286
287 lines.push_str("Selective dynamics\nDirect\n");
289 for (_, a) in mol.atoms() {
290 let p = lattice.to_frac(a.position());
291 let freezing = a.freezing();
292 let line = format!(
293 "{x:18.12} {y:18.12} {z:18.12} {fx} {fy} {fz}\n",
294 x = p.x,
295 y = p.y,
296 z = p.z,
297 fx = if freezing[0] { "F" } else { "T" },
298 fy = if freezing[1] { "F" } else { "T" },
299 fz = if freezing[2] { "F" } else { "T" },
300 );
301 lines.push_str(&line);
302 }
303
304 let write_velocity = mol.atoms().flat_map(|(_, a)| a.velocity()).any(|x| x != 0.0);
306 if write_velocity {
307 lines.push_str("\n");
308 for (_, a) in mol.atoms() {
309 let [vx, vy, vz] = a.velocity();
310 let line = format!("{:18.12} {:18.12} {:18.12}\n", vx, vy, vz);
311 lines.push_str(&line);
312 }
313 }
314 lines.push_str("\n");
316
317 lines
318}
319
320fn count_symbols(symbols: Vec<&str>) -> Vec<(&str, usize)> {
322 let mut lines = String::new();
323
324 let mut syms1 = symbols.iter();
325 let mut syms2 = symbols.iter().skip(1);
326 let mut counts = vec![];
327
328 let mut c = 1;
329 let mut s = symbols[0];
330 for (&sym1, &sym2) in syms1.zip(syms2) {
331 if sym2 == sym1 {
332 c += 1;
333 } else {
334 counts.push((sym1, c));
335 c = 1;
336 }
337 s = sym2;
338 }
339 counts.push((s, c));
341
342 counts
343}
344
345#[test]
346fn test_poscar_symbols_counts() {
347 let symbols = ["C", "C", "C", "H", "O", "O", "C"];
348 let x = count_symbols(symbols.to_vec());
349 assert_eq!([("C", 3), ("H", 1), ("O", 2), ("C", 1)].to_vec(), x);
350
351 let symbols = ["C", "C"];
352 let x = count_symbols(symbols.to_vec());
353 assert_eq!([("C", 2)].to_vec(), x);
354
355 let symbols = ["C", "H"];
356 let x = count_symbols(symbols.to_vec());
357 assert_eq!([("C", 1), ("H", 1)].to_vec(), x);
358
359 let symbols = ["C"];
360 let x = count_symbols(symbols.to_vec());
361 assert_eq!([("C", 1)].to_vec(), x);
362}
363#[derive(Clone, Copy, Debug)]
367pub struct PoscarFile();
368
369impl ChemicalFile for PoscarFile {
370 fn ftype(&self) -> &str {
371 "vasp/input"
372 }
373
374 fn possible_extensions(&self) -> Vec<&str> {
375 vec!["poscar", "vasp"]
376 }
377
378 fn parsable(&self, path: &Path) -> bool {
381 let possible_filenames = vec!["CONTCAR", "POSCAR"];
382 if let Some(e) = path.extension() {
383 let e = e.to_string_lossy().to_lowercase();
384 self.possible_extensions().contains(&e.as_str())
385 } else
386 {
388 if let Some(filename) = path.file_name() {
389 let f = filename.to_string_lossy().to_uppercase();
390 for x in possible_filenames {
391 if f.starts_with(x) {
392 return true;
393 }
394 }
395 }
396 false
397 }
398 }
399
400 fn format_molecule(&self, mol: &Molecule) -> Result<String> {
401 Ok(format_molecule(mol))
402 }
403}
404
405impl ParseMolecule for PoscarFile {
406 fn parse_molecule(&self, input: &str) -> Result<Molecule> {
407 let (_, mol) = parse_poscar_molecule(input).map_err(|e| format_err!("parse POSCAR format failure: {:?}", e))?;
408 Ok(mol)
409 }
410}
411
412#[test]
413fn test_vasp_input_parsable() {
414 let cf = PoscarFile();
415 let parsable = |x: &str| cf.parsable(x.as_ref());
416
417 assert!(parsable("POSCAR"));
418 assert!(parsable("POSCAR1"));
419 assert!(parsable("POSCAR-1"));
420 assert!(!parsable("POSCAR.1"));
421 assert!(parsable("CONTCAR"));
422 assert!(parsable("CONTCAR1"));
423 assert!(parsable("POSCAR2"));
424 assert!(parsable("poscar2"));
425 assert!(parsable("x.poscar"));
426 assert!(parsable("x.vasp"));
427}
428crate::cf_impl_partitions!(PoscarFile);
432