1use crate::types::ConFrame;
2use std::fs::File;
3use std::io::{self, BufWriter, Write};
4use std::path::Path;
5
6const DEFAULT_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 precision: usize,
31}
32
33impl<W: Write> ConFrameWriter<W> {
35 pub fn new(writer: W) -> Self {
41 Self {
42 writer: BufWriter::new(writer),
43 precision: DEFAULT_FLOAT_PRECISION,
44 }
45 }
46
47 pub fn with_precision(writer: W, precision: usize) -> Self {
54 Self {
55 writer: BufWriter::new(writer),
56 precision,
57 }
58 }
59
60 pub fn write_frame(&mut self, frame: &ConFrame) -> io::Result<()> {
62 let prec = self.precision;
63
64 writeln!(self.writer, "{}", frame.header.prebox_header[0])?;
66 writeln!(self.writer, "{}", frame.header.prebox_header[1])?;
67 writeln!(
68 self.writer,
69 "{1:.0$} {2:.0$} {3:.0$}",
70 prec, frame.header.boxl[0], frame.header.boxl[1], frame.header.boxl[2]
71 )?;
72 writeln!(
73 self.writer,
74 "{1:.0$} {2:.0$} {3:.0$}",
75 prec, frame.header.angles[0], frame.header.angles[1], frame.header.angles[2]
76 )?;
77 writeln!(self.writer, "{}", frame.header.postbox_header[0])?;
78 writeln!(self.writer, "{}", frame.header.postbox_header[1])?;
79 writeln!(self.writer, "{}", frame.header.natm_types)?;
80
81 let natms_str: Vec<String> = frame
82 .header
83 .natms_per_type
84 .iter()
85 .map(|n| n.to_string())
86 .collect();
87 writeln!(self.writer, "{}", natms_str.join(" "))?;
88
89 let masses_str: Vec<String> = frame
90 .header
91 .masses_per_type
92 .iter()
93 .map(|m| format!("{:.1$}", m, prec))
94 .collect();
95 writeln!(self.writer, "{}", masses_str.join(" "))?;
96
97 let mut atom_idx_offset = 0;
99 for (type_idx, &num_atoms_in_type) in frame.header.natms_per_type.iter().enumerate() {
100 let symbol = &frame.atom_data[atom_idx_offset].symbol;
101 writeln!(self.writer, "{}", symbol)?;
102 writeln!(self.writer, "Coordinates of Component {}", type_idx + 1)?;
103
104 for i in 0..num_atoms_in_type {
105 let atom = &frame.atom_data[atom_idx_offset + i];
106 writeln!(
107 self.writer,
108 "{x:.prec$} {y:.prec$} {z:.prec$} {fixed_flag:.0} {atom_id}",
109 prec = prec,
110 x = atom.x,
111 y = atom.y,
112 z = atom.z,
113 fixed_flag = if atom.is_fixed {
114 FIXED_ATOM_FLAG
115 } else {
116 FREE_ATOM_FLAG
117 },
118 atom_id = atom.atom_id
119 )?;
120 }
121 atom_idx_offset += num_atoms_in_type;
122 }
123
124 if frame.has_velocities() {
126 writeln!(self.writer)?;
128
129 let mut vel_idx_offset = 0;
130 for (type_idx, &num_atoms_in_type) in frame.header.natms_per_type.iter().enumerate() {
131 let symbol = &frame.atom_data[vel_idx_offset].symbol;
132 writeln!(self.writer, "{}", symbol)?;
133 writeln!(self.writer, "Velocities of Component {}", type_idx + 1)?;
134
135 for i in 0..num_atoms_in_type {
136 let atom = &frame.atom_data[vel_idx_offset + i];
137 writeln!(
138 self.writer,
139 "{vx:.prec$} {vy:.prec$} {vz:.prec$} {fixed_flag:.0} {atom_id}",
140 prec = prec,
141 vx = atom.vx.unwrap_or(0.0),
142 vy = atom.vy.unwrap_or(0.0),
143 vz = atom.vz.unwrap_or(0.0),
144 fixed_flag = if atom.is_fixed {
145 FIXED_ATOM_FLAG
146 } else {
147 FREE_ATOM_FLAG
148 },
149 atom_id = atom.atom_id
150 )?;
151 }
152 vel_idx_offset += num_atoms_in_type;
153 }
154 }
155
156 Ok(())
157 }
158
159 pub fn extend<'a>(&mut self, frames: impl Iterator<Item = &'a ConFrame>) -> io::Result<()> {
163 for frame in frames {
164 self.write_frame(frame)?;
165 }
166 Ok(())
167 }
168}
169
170impl ConFrameWriter<File> {
172 pub fn from_path<P: AsRef<Path>>(path: P) -> io::Result<Self> {
176 let file = File::create(path)?;
177 Ok(Self::new(file))
178 }
179
180 pub fn from_path_with_precision<P: AsRef<Path>>(path: P, precision: usize) -> io::Result<Self> {
182 let file = File::create(path)?;
183 Ok(Self::with_precision(file, precision))
184 }
185}