pub mod structure;
pub mod trajectory;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Write};
use std::path::Path;
pub use structure::read_gro;
pub use trajectory::GroReader;
pub use trajectory::GroWriter;
use crate::errors::{ParseGroError, WriteGroError};
use crate::prelude::{AtomIterator, SimBox};
use crate::system::System;
fn get_title(
buffer: &mut BufReader<File>,
filename: impl AsRef<Path>,
) -> Result<String, ParseGroError> {
let mut title = String::new();
match buffer.read_line(&mut title) {
Ok(0) | Err(_) => Err(ParseGroError::LineNotFound(Box::from(filename.as_ref()))),
Ok(_) => Ok(title.trim().to_string()),
}
}
fn get_natoms(
buffer: &mut BufReader<File>,
filename: impl AsRef<Path>,
) -> Result<usize, ParseGroError> {
let mut line = String::new();
match buffer.read_line(&mut line) {
Ok(0) | Err(_) => Err(ParseGroError::LineNotFound(Box::from(filename.as_ref()))),
Ok(_) => match line.trim().parse::<usize>() {
Ok(x) => Ok(x),
Err(_) => Err(ParseGroError::ParseLineErr(line.trim().to_string())),
},
}
}
fn line_as_box(line: &str) -> Result<SimBox, ParseGroError> {
let mut simulation_box = [0.0f32; 9];
let mut i = 0usize;
for split in line.split_whitespace() {
simulation_box[i] = split
.trim()
.parse::<f32>()
.map_err(|_| ParseGroError::ParseBoxLineErr(line.to_string()))?;
i += 1;
}
if i != 3 && i != 9 {
Err(ParseGroError::ParseBoxLineErr(line.to_string()))?;
}
if simulation_box[3] != 0.0 || simulation_box[4] != 0.0 || simulation_box[6] != 0.0 {
return Err(ParseGroError::UnsupportedBox(line.to_string()));
}
Ok(simulation_box.into())
}
fn write_box(simbox: Option<&SimBox>, writer: &mut BufWriter<File>) -> Result<(), WriteGroError> {
match simbox {
Some(simbox) if simbox.is_orthogonal() => {
writeln!(
writer,
" {:9.5} {:9.5} {:9.5}",
simbox.x, simbox.y, simbox.z
)
.map_err(|_| WriteGroError::CouldNotWrite)?;
}
Some(simbox) => {
writeln!(
writer,
" {:9.5} {:9.5} {:9.5} {:9.5} {:9.5} {:9.5} {:9.5} {:9.5} {:9.5}",
simbox.x,
simbox.y,
simbox.z,
simbox.v1y,
simbox.v1z,
simbox.v2x,
simbox.v2z,
simbox.v3x,
simbox.v3y
)
.map_err(|_| WriteGroError::CouldNotWrite)?;
}
None => {
let x = 0.0;
writeln!(writer, " {x:9.5} {x:9.5} {x:9.5}",)
.map_err(|_| WriteGroError::CouldNotWrite)?;
}
}
Ok(())
}
#[inline(always)]
fn write_header(
writer: &mut BufWriter<File>,
title: &str,
n_atoms: usize,
) -> Result<(), WriteGroError> {
writeln!(writer, "{}", title).map_err(|_| WriteGroError::CouldNotWrite)?;
writeln!(writer, "{:>5}", n_atoms).map_err(|_| WriteGroError::CouldNotWrite)?;
Ok(())
}
#[inline(always)]
fn determine_title(system: &System, group: &str, is_trajectory: bool) -> String {
let title = match group {
"all" => system.get_name().to_owned(),
_ => format!("Group `{}` from {}", group, system.get_name()),
};
if is_trajectory {
format!(
"{} t={} step={}",
title,
system.get_simulation_time(),
system.get_simulation_step()
)
} else {
title
}
}
fn write_frame(
system: &System,
writer: &mut BufWriter<File>,
group_name: &str,
iterator: AtomIterator,
n_atoms: usize,
write_velocities: bool,
is_trajectory: bool,
) -> Result<(), WriteGroError> {
write_header(
writer,
&determine_title(system, group_name, is_trajectory),
n_atoms,
)?;
for atom in iterator {
atom.write_gro(writer, write_velocities)?;
}
write_box(system.get_box(), writer)?;
writer.flush().map_err(|_| WriteGroError::CouldNotWrite)?;
Ok(())
}