1use crate::types::ConFrame;
2use std::fs::File;
3use std::io::{self, BufWriter, Write};
4use std::path::Path;
5
6const FLOAT_PRECISION: usize = 6;
8const FIXED_ATOM_FLAG: usize = 1;
11const FREE_ATOM_FLAG: usize = 0;
13
14pub struct ConFrameWriter<W: Write> {
29 writer: BufWriter<W>,
30}
31
32impl<W: Write> ConFrameWriter<W> {
34 pub fn new(writer: W) -> Self {
40 Self {
41 writer: BufWriter::new(writer),
42 }
43 }
44
45 pub fn write_frame(&mut self, frame: &ConFrame) -> io::Result<()> {
47 writeln!(self.writer, "{}", frame.header.prebox_header[0])?;
49 writeln!(self.writer, "{}", frame.header.prebox_header[1])?;
50 writeln!(
51 self.writer,
52 "{1:.0$} {2:.0$} {3:.0$}",
53 FLOAT_PRECISION, frame.header.boxl[0], frame.header.boxl[1], frame.header.boxl[2]
54 )?;
55 writeln!(
56 self.writer,
57 "{1:.0$} {2:.0$} {3:.0$}",
58 FLOAT_PRECISION, frame.header.angles[0], frame.header.angles[1], frame.header.angles[2]
59 )?;
60 writeln!(self.writer, "{}", frame.header.postbox_header[0])?;
61 writeln!(self.writer, "{}", frame.header.postbox_header[1])?;
62 writeln!(self.writer, "{}", frame.header.natm_types)?;
63
64 let natms_str: Vec<String> = frame
65 .header
66 .natms_per_type
67 .iter()
68 .map(|n| n.to_string())
69 .collect();
70 writeln!(self.writer, "{}", natms_str.join(" "))?;
71
72 let masses_str: Vec<String> = frame
73 .header
74 .masses_per_type
75 .iter()
76 .map(|m| format!("{:.1$}", m, FLOAT_PRECISION))
77 .collect();
78 writeln!(self.writer, "{}", masses_str.join(" "))?;
79
80 let mut atom_idx_offset = 0;
82 for (type_idx, &num_atoms_in_type) in frame.header.natms_per_type.iter().enumerate() {
83 let symbol = &frame.atom_data[atom_idx_offset].symbol;
84 writeln!(self.writer, "{}", symbol)?;
85 writeln!(self.writer, "Coordinates of Component {}", type_idx + 1)?;
86
87 for i in 0..num_atoms_in_type {
88 let atom = &frame.atom_data[atom_idx_offset + i];
89 writeln!(
90 self.writer,
91 "{x:.prec$} {y:.prec$} {z:.prec$} {fixed_flag:.0} {atom_id}",
92 prec = FLOAT_PRECISION,
93 x = atom.x,
94 y = atom.y,
95 z = atom.z,
96 fixed_flag = if atom.is_fixed {
97 FIXED_ATOM_FLAG
98 } else {
99 FREE_ATOM_FLAG
100 },
101 atom_id = atom.atom_id
102 )?;
103 }
104 atom_idx_offset += num_atoms_in_type;
105 }
106
107 if frame.has_velocities() {
109 writeln!(self.writer)?;
111
112 let mut vel_idx_offset = 0;
113 for (type_idx, &num_atoms_in_type) in frame.header.natms_per_type.iter().enumerate() {
114 let symbol = &frame.atom_data[vel_idx_offset].symbol;
115 writeln!(self.writer, "{}", symbol)?;
116 writeln!(self.writer, "Velocities of Component {}", type_idx + 1)?;
117
118 for i in 0..num_atoms_in_type {
119 let atom = &frame.atom_data[vel_idx_offset + i];
120 writeln!(
121 self.writer,
122 "{vx:.prec$} {vy:.prec$} {vz:.prec$} {fixed_flag:.0} {atom_id}",
123 prec = FLOAT_PRECISION,
124 vx = atom.vx.unwrap_or(0.0),
125 vy = atom.vy.unwrap_or(0.0),
126 vz = atom.vz.unwrap_or(0.0),
127 fixed_flag = if atom.is_fixed {
128 FIXED_ATOM_FLAG
129 } else {
130 FREE_ATOM_FLAG
131 },
132 atom_id = atom.atom_id
133 )?;
134 }
135 vel_idx_offset += num_atoms_in_type;
136 }
137 }
138
139 Ok(())
140 }
141
142 pub fn extend<'a>(&mut self, frames: impl Iterator<Item = &'a ConFrame>) -> io::Result<()> {
146 for frame in frames {
147 self.write_frame(frame)?;
148 }
149 Ok(())
150 }
151}
152
153impl ConFrameWriter<File> {
155 pub fn from_path<P: AsRef<Path>>(path: P) -> io::Result<Self> {
159 let file = File::create(path)?;
160 Ok(Self::new(file))
161 }
162}