use std::io::{BufRead, BufWriter, Seek};
use std::marker::PhantomData;
use std::path::Path;
use std::str::FromStr;
use std::{fs::File, io::BufReader};
use regex::Regex;
use crate::auxiliary::{GRO_MAX_COORDINATE, GRO_MIN_COORDINATE};
use crate::errors::{ParseGroError, WriteTrajError};
use crate::io::check_coordinate_sizes;
use crate::io::traj_write::{PrivateTrajWrite, TrajWrite};
use crate::prelude::{
AtomIterator, FrameDataTime, TrajFullReadOpen, TrajRangeRead, TrajRead, TrajReadOpen,
TrajReader, TrajStepRead, TrajStepTimeRead, Vector3D,
};
use crate::structures::group::Group;
use crate::{
errors::ReadTrajError,
prelude::{FrameData, SimBox, TrajFile},
system::System,
};
const TIME_PRECISION: f32 = 0.001;
#[derive(Debug)]
pub struct GroReader<'a> {
system: *mut System,
gro: GroFile,
phantom: PhantomData<&'a mut System>,
}
#[derive(Debug)]
pub struct GroFile {
buffer: BufReader<File>,
filename: Box<Path>,
}
impl TrajFile for GroFile {}
#[derive(Debug)]
pub struct GroFrameData {
time: f32,
step: u64,
simbox: SimBox,
positions: Vec<[f32; 3]>,
velocities: Vec<Option<[f32; 3]>>,
}
fn extract_time_step(string: &str) -> Option<(f32, u64)> {
let re = Regex::new(r"t=\s*([\d\.\-]+)\s+step=\s*(\d+)").expect(
"FATAL GROAN ERROR | gro_io::trajectory::extract_time_step | Could not construct regular expression.",
);
if let Some(caps) = re.captures(string) {
let time_str = caps.get(1)?.as_str();
let step_str = caps.get(2)?.as_str();
let time = f32::from_str(time_str).ok()?;
let step = u64::from_str(step_str).ok()?;
Some((time, step))
} else {
None
}
}
fn read_position_velocity(
reader: &mut GroFile,
) -> Result<([f32; 3], Option<[f32; 3]>), ReadTrajError> {
let mut line = String::new();
reader
.buffer
.read_line(&mut line)
.map_err(|_| ReadTrajError::FrameNotFound)?;
if line.len() < 44 {
return Err(ReadTrajError::FrameNotFound);
}
let mut position = [0.0f32; 3];
for (i, item) in position.iter_mut().enumerate() {
let curr = 20 + i * 8;
*item = line[curr..curr + 8]
.trim()
.parse::<f32>()
.map_err(|_| ReadTrajError::FrameNotFound)?;
if !item.is_finite() {
return Err(ReadTrajError::GroSpecificError(
ParseGroError::InvalidFloat(line.to_string()),
));
}
}
let velocity = if line.trim_end().len() >= 68 {
let mut velocity = [0.0f32; 3];
for (i, item) in velocity.iter_mut().enumerate() {
let curr = 44 + i * 8;
*item = line[curr..curr + 8]
.trim()
.parse::<f32>()
.map_err(|_| ReadTrajError::FrameNotFound)?;
if !item.is_finite() {
return Err(ReadTrajError::GroSpecificError(
ParseGroError::InvalidFloat(line.to_string()),
));
}
}
Some(velocity)
} else {
None
};
Ok((position, velocity))
}
fn read_box(reader: &mut GroFile) -> Result<SimBox, ReadTrajError> {
let mut line = String::new();
reader
.buffer
.read_line(&mut line)
.map_err(|_| ReadTrajError::FrameNotFound)?;
super::line_as_box(&line).map_err(|_| ReadTrajError::FrameNotFound)
}
#[allow(clippy::type_complexity)]
fn read_header(
reader: &mut GroFile,
expected_n_atoms: usize,
) -> Option<Result<(String, Option<f32>, Option<u64>, usize), ReadTrajError>> {
let mut title = String::new();
match reader.buffer.read_line(&mut title) {
Ok(0) => return None,
Ok(_) => (),
Err(_) => return Some(Err(ReadTrajError::FrameNotFound)),
}
let (time, step) = match extract_time_step(&title) {
Some((x, y)) => (Some(x), Some(y)),
None => (None, None),
};
let n_atoms = match super::get_natoms(&mut reader.buffer, reader.filename.clone()) {
Ok(x) => x,
Err(_) => return Some(Err(ReadTrajError::FrameNotFound)),
};
if n_atoms != expected_n_atoms {
Some(Err(ReadTrajError::AtomsNumberMismatch(
reader.filename.clone(),
)))
} else {
Some(Ok((title, time, step, n_atoms)))
}
}
impl FrameData for GroFrameData {
type TrajFile = GroFile;
fn from_frame(
traj_file: &mut Self::TrajFile,
system: &System,
) -> Option<Result<Self, crate::errors::ReadTrajError>>
where
Self: Sized,
{
let (_, time, step, n_atoms) = match read_header(traj_file, system.get_n_atoms())? {
Ok(x) => x,
Err(e) => return Some(Err(e)),
};
let mut positions = Vec::with_capacity(n_atoms);
let mut velocities = Vec::with_capacity(n_atoms);
for _ in 0..n_atoms {
match read_position_velocity(traj_file) {
Ok((pos, vel)) => {
positions.push(pos);
velocities.push(vel);
}
Err(e) => return Some(Err(e)),
}
}
let simbox = match read_box(traj_file) {
Ok(x) => x,
Err(e) => return Some(Err(e)),
};
Some(Ok(GroFrameData {
time: time.unwrap_or(system.get_simulation_time()),
step: step.unwrap_or(system.get_simulation_step()),
simbox,
positions,
velocities,
}))
}
fn update_system(self, system: &mut System) {
for (i, atom) in system.get_atoms_mut().iter_mut().enumerate() {
let pos = Vector3D::from(unsafe { *self.positions.get_unchecked(i) });
atom.set_position(pos);
let vel = unsafe { *self.velocities.get_unchecked(i) };
match vel {
Some(x) => atom.set_velocity(Vector3D::from(x)),
None => atom.reset_velocity(),
}
atom.reset_force();
}
system.set_simulation_step(self.step);
system.set_simulation_time(self.time);
system.set_box(self.simbox);
}
}
impl FrameDataTime for GroFrameData {
#[inline(always)]
fn get_time(&self) -> f32 {
self.time
}
}
impl<'a> TrajRead<'a> for GroReader<'a> {
type FrameData = GroFrameData;
fn get_system(&mut self) -> *mut System {
self.system
}
fn get_file_handle(
&mut self,
) -> &mut <<Self as TrajRead<'a>>::FrameData as FrameData>::TrajFile {
&mut self.gro
}
}
impl<'a> TrajReadOpen<'a> for GroReader<'a> {
fn initialize(
system: &'a mut System,
filename: impl AsRef<Path>,
group: Option<&str>,
) -> Result<Self, ReadTrajError>
where
Self: Sized,
{
match group {
None => GroReader::new(system, filename),
Some(_) => panic!("FATAL GROAN ERROR | GroReader::initialize | GroReader does not support partial-frame reading."),
}
}
}
impl<'a> TrajFullReadOpen<'a> for GroReader<'a> {
fn new(system: &'a mut System, filename: impl AsRef<Path>) -> Result<Self, ReadTrajError>
where
Self: Sized,
{
let file = File::open(&filename)
.map_err(|_| ReadTrajError::FileNotFound(Box::from(filename.as_ref())))?;
let buffer = BufReader::new(file);
Ok(GroReader {
system: system as *mut System,
gro: GroFile {
buffer,
filename: Box::from(filename.as_ref()),
},
phantom: PhantomData,
})
}
}
impl<'a> TrajStepRead<'a> for GroReader<'a> {
#[inline(always)]
fn skip_frame(&mut self) -> Result<bool, ReadTrajError> {
match self.skip_frame_time() {
Err(e) => Err(e),
Ok(None) => Ok(false),
Ok(_) => Ok(true),
}
}
}
impl<'a> TrajStepTimeRead<'a> for GroReader<'a> {
fn skip_frame_time(&mut self) -> Result<Option<f32>, ReadTrajError> {
let system = unsafe { &*self.get_system() };
let (time, n_atoms) = match read_header(&mut self.gro, system.get_n_atoms()) {
None => return Ok(None),
Some(Err(e)) => return Err(e),
Some(Ok((_, time, _, n_atoms))) => (time, n_atoms),
};
let mut buf = String::new();
for _ in 0..(n_atoms + 1) {
buf.clear();
if self
.gro
.buffer
.read_line(&mut buf)
.map_err(|_| ReadTrajError::SkipFailed)?
== 0
{
return Ok(None);
}
}
Ok(Some(time.unwrap_or(system.get_simulation_time())))
}
}
impl<'a> TrajRangeRead<'a> for GroReader<'a> {
fn jump_to_start(&mut self, start_time: f32) -> Result<(), ReadTrajError> {
let mut buf = String::new();
loop {
let system = unsafe { &*self.get_system() };
let pos = self.gro.buffer.stream_position().expect(
"FATAL GROAN ERROR | GroReader::jump_to_start | Could not get position in the stream.",
);
let (time, n_atoms) = match read_header(&mut self.gro, system.get_n_atoms()) {
None => return Err(ReadTrajError::StartNotFound(start_time.to_string())),
Some(Err(e)) => return Err(e),
Some(Ok((_, time, _, n_atoms))) => (time, n_atoms),
};
let time = time.unwrap_or(system.get_simulation_time());
if time >= start_time - TIME_PRECISION {
self.gro.buffer.seek(std::io::SeekFrom::Start(pos))
.expect("FATAL GROAN ERROR | GroReader::jump_to_start | Could not seek to an already visited position.");
return Ok(());
}
for _ in 0..(n_atoms + 1) {
buf.clear();
match self.gro.buffer.read_line(&mut buf) {
Ok(0) => return Err(ReadTrajError::FrameNotFound),
Ok(_) => (),
Err(e) => return Err(ReadTrajError::UnknownError(e.to_string())),
}
}
}
}
}
impl System {
pub fn gro_iter(
&mut self,
filename: impl AsRef<Path>,
) -> Result<TrajReader<'_, GroReader<'_>>, ReadTrajError> {
Ok(TrajReader::wrap_traj(GroReader::new(self, filename)?))
}
}
impl System {
#[inline(always)]
pub fn gro_writer_init(&mut self, filename: impl AsRef<Path>) -> Result<(), WriteTrajError> {
self.traj_writer_init::<GroWriter>(filename)
}
#[inline(always)]
pub fn gro_group_writer_init(
&mut self,
filename: impl AsRef<Path>,
group: &str,
) -> Result<(), WriteTrajError> {
self.traj_group_writer_init::<GroWriter>(filename, group)
}
}
pub struct GroWriter {
gro: BufWriter<File>,
group: Group,
group_name: String,
}
impl TrajWrite for GroWriter {}
impl PrivateTrajWrite for GroWriter {
fn new(
system: &System,
filename: impl AsRef<Path>,
group: Option<&str>,
) -> Result<Self, WriteTrajError>
where
Self: Sized,
{
let group_name = group.to_owned();
let group = match group {
Some(x) => system
.get_groups()
.get(x)
.map_err(|_| WriteTrajError::GroupNotFound(x.to_owned()))?
.clone(),
None => system
.get_groups()
.get("all")
.expect("FATAL GROAN ERROR | GroWriter::new | Group `all` should exist.")
.clone(),
};
let output = File::create(&filename)
.map_err(|_| WriteTrajError::CouldNotCreate(Box::from(filename.as_ref())))?;
let writer = BufWriter::new(output);
Ok(GroWriter {
gro: writer,
group,
group_name: group_name.unwrap_or("all").to_owned(),
})
}
fn write_frame(&mut self, system: &System) -> Result<(), WriteTrajError> {
let iterator =
AtomIterator::new(system.get_atoms(), self.group.get_atoms(), system.get_box());
if !check_coordinate_sizes(iterator.clone(), GRO_MIN_COORDINATE, GRO_MAX_COORDINATE) {
return Err(WriteTrajError::CoordinateTooLarge);
}
super::write_frame(
system,
&mut self.gro,
&self.group_name,
iterator,
self.group.get_n_atoms(),
system.has_velocities(),
true,
)
.map_err(|_| WriteTrajError::CouldNotWrite)
}
}
#[cfg(test)]
mod tests_read {
use float_cmp::assert_approx_eq;
use crate::test_utilities::utilities::{compare_atoms, compare_box_low_precision};
#[cfg(not(feature = "no-xdrfile"))]
use crate::test_utilities::utilities::compare_atoms_trr_with_gro;
use super::*;
#[cfg(any(feature = "molly", not(feature = "no-xdrfile")))]
#[test]
fn gro_iter() {
let mut system = System::from_file("test_files/protein_trajectory.gro").unwrap();
let mut system2 = System::from_file("test_files/example.gro").unwrap();
let expected_times = [
0.0, 100.0, 200.0, 300.0, 300.0, 500.0, 500.0, 700.0, 800.0, 900.0, 1000.0,
];
let expected_steps = [
0, 5000, 10000, 15000, 15000, 25000, 25000, 35000, 40000, 45000, 50000,
];
for (i, (frame1, frame2)) in system
.gro_iter("test_files/protein_trajectory.gro")
.unwrap()
.zip(system2.xtc_iter("test_files/short_trajectory.xtc").unwrap())
.enumerate()
{
let frame1 = frame1.unwrap();
let frame2 = frame2.unwrap();
assert_approx_eq!(f32, frame1.get_simulation_time(), expected_times[i]);
assert_eq!(frame1.get_simulation_step(), expected_steps[i]);
compare_box_low_precision(frame1.get_box().unwrap(), frame2.get_box().unwrap());
for (a1, a2) in frame1
.atoms_iter()
.take(61)
.zip(frame2.atoms_iter().take(61))
{
compare_atoms(a1, a2);
}
}
}
#[test]
fn gro_iter_no_zip() {
let mut system = System::from_file("test_files/protein_trajectory.gro").unwrap();
let expected_times = [
0.0, 100.0, 200.0, 300.0, 300.0, 500.0, 500.0, 700.0, 800.0, 900.0, 1000.0,
];
let expected_steps = [
0, 5000, 10000, 15000, 15000, 25000, 25000, 35000, 40000, 45000, 50000,
];
for (i, frame) in system
.gro_iter("test_files/protein_trajectory.gro")
.unwrap()
.enumerate()
{
let frame = frame.unwrap();
assert_eq!(frame.get_simulation_step(), expected_steps[i]);
assert_approx_eq!(f32, frame.get_simulation_time(), expected_times[i]);
}
}
#[test]
#[cfg(not(feature = "no-xdrfile"))]
fn gro_iter_velocities() {
let mut system = System::from_file("test_files/protein.gro").unwrap();
let mut system2 = System::from_file("test_files/example.gro").unwrap();
let expected_times = [0.0, 0.0, 480.0];
let expected_steps = [0, 0, 24000];
let no_vel_atoms = [vec![30], vec![0, 9], vec![59, 60]];
for (i, (frame1, frame2)) in system
.gro_iter("test_files/protein_trajectory_velocities.gro")
.unwrap()
.zip(
system2
.trr_iter("test_files/short_trajectory.trr")
.unwrap()
.with_step(3)
.unwrap(),
)
.enumerate()
{
let frame1 = frame1.unwrap();
let frame2 = frame2.unwrap();
assert_approx_eq!(f32, frame1.get_simulation_time(), expected_times[i]);
assert_eq!(frame1.get_simulation_step(), expected_steps[i]);
compare_box_low_precision(frame1.get_box().unwrap(), frame2.get_box().unwrap());
for (a1, a2) in frame1
.atoms_iter()
.take(61)
.zip(frame2.atoms_iter().take(61))
{
let mut is_exception = false;
for exception in &no_vel_atoms[i] {
if a1.get_index() == *exception {
assert_approx_eq!(
f32,
a1.get_position().unwrap().x,
a2.get_position().unwrap().x,
epsilon = 1e-3
);
assert_approx_eq!(
f32,
a1.get_position().unwrap().y,
a2.get_position().unwrap().y,
epsilon = 1e-3
);
assert_approx_eq!(
f32,
a1.get_position().unwrap().z,
a2.get_position().unwrap().z,
epsilon = 1e-3
);
assert!(!a1.has_velocity());
is_exception = true;
break;
}
}
if !is_exception {
compare_atoms_trr_with_gro(a1, a2);
}
}
}
}
#[cfg(any(feature = "molly", not(feature = "no-xdrfile")))]
#[test]
fn gro_iter_with_step() {
let steps = [2, 3, 4, 5, 7];
let mut system = System::from_file("test_files/protein_trajectory.gro").unwrap();
let mut system2 = System::from_file("test_files/example.gro").unwrap();
for step in steps.into_iter() {
for (frame1, frame2) in system
.gro_iter("test_files/protein_trajectory.gro")
.unwrap()
.with_step(step)
.unwrap()
.zip(
system2
.xtc_iter("test_files/short_trajectory.xtc")
.unwrap()
.with_step(step)
.unwrap(),
)
{
let frame1 = frame1.unwrap();
let frame2 = frame2.unwrap();
compare_box_low_precision(frame1.get_box().unwrap(), frame2.get_box().unwrap());
for (a1, a2) in frame1
.atoms_iter()
.take(61)
.zip(frame2.atoms_iter().take(61))
{
compare_atoms(a1, a2);
}
}
}
}
#[cfg(any(feature = "molly", not(feature = "no-xdrfile")))]
#[test]
fn gro_iter_range() {
let ranges = [
(0.0, 100_000.0),
(200.0, 600.0),
(300.0, 500.0),
(500.0, 500.0),
(300.0, 100_000.0),
];
for range in ranges.into_iter() {
let mut system = System::from_file("test_files/protein_trajectory.gro").unwrap();
let mut system2 = System::from_file("test_files/example.gro").unwrap();
for (frame1, frame2) in system
.gro_iter("test_files/protein_trajectory.gro")
.unwrap()
.with_range(range.0, range.1)
.unwrap()
.zip(
system2
.xtc_iter("test_files/short_trajectory.xtc")
.unwrap()
.with_range(range.0, range.1)
.unwrap(),
)
{
let frame1 = frame1.unwrap();
let frame2 = frame2.unwrap();
compare_box_low_precision(frame1.get_box().unwrap(), frame2.get_box().unwrap());
for (a1, a2) in frame1
.atoms_iter()
.take(61)
.zip(frame2.atoms_iter().take(61))
{
compare_atoms(a1, a2);
}
}
}
}
#[test]
fn gro_iter_no_time_with_range() {
let mut system = System::from_file("test_files/example.gro").unwrap();
if let Err(e) = system
.gro_iter("test_files/example.gro")
.unwrap()
.with_range(0.0, f32::INFINITY)
{
panic!("Function failed: `{}`", e);
}
}
#[cfg(any(feature = "molly", not(feature = "no-xdrfile")))]
#[test]
fn gro_iter_range_steps() {
let mut system = System::from_file("test_files/protein_trajectory.gro").unwrap();
let mut system2 = System::from_file("test_files/example.gro").unwrap();
for (start, end, step) in [(0.0, 100_000.0, 1), (300.0, 800.0, 2), (100.0, 900.0, 4)] {
for (frame1, frame2) in system
.gro_iter("test_files/protein_trajectory.gro")
.unwrap()
.with_range(start, end)
.unwrap()
.with_step(step)
.unwrap()
.zip(
system2
.xtc_iter("test_files/short_trajectory.xtc")
.unwrap()
.with_range(start, end)
.unwrap()
.with_step(step)
.unwrap(),
)
{
let frame1 = frame1.unwrap();
let frame2 = frame2.unwrap();
compare_box_low_precision(frame1.get_box().unwrap(), frame2.get_box().unwrap());
for (a1, a2) in frame1
.atoms_iter()
.take(61)
.zip(frame2.atoms_iter().take(61))
{
compare_atoms(a1, a2);
}
}
}
}
#[cfg(any(feature = "molly", not(feature = "no-xdrfile")))]
#[test]
fn gro_iter_cat() {
let mut system = System::from_file("test_files/protein_trajectory.gro").unwrap();
let mut system2 = System::from_file("test_files/example.gro").unwrap();
let (start, end, step) = (300.0, 800.0, 2);
for (frame1, frame2) in system
.traj_cat_iter::<GroReader>(&[
"test_files/split/traj1.gro",
"test_files/split/traj2.gro",
"test_files/split/traj3.gro",
"test_files/split/traj4.gro",
"test_files/split/traj5.gro",
])
.unwrap()
.with_range(start, end)
.unwrap()
.with_step(step)
.unwrap()
.zip(
system2
.xtc_iter("test_files/short_trajectory.xtc")
.unwrap()
.with_range(start, end)
.unwrap()
.with_step(step)
.unwrap(),
)
{
let frame1 = frame1.unwrap();
let frame2 = frame2.unwrap();
compare_box_low_precision(frame1.get_box().unwrap(), frame2.get_box().unwrap());
for (a1, a2) in frame1
.atoms_iter()
.take(61)
.zip(frame2.atoms_iter().take(61))
{
compare_atoms(a1, a2);
}
}
}
#[test]
fn gro_iter_missing_box() {
let mut system = System::from_file("test_files/protein.gro").unwrap();
match system
.gro_iter("test_files/protein_trajectory_missing_box.gro")
.unwrap()
.next()
{
Some(Ok(_)) => panic!("Function should have failed."),
Some(Err(ReadTrajError::FrameNotFound)) => (),
Some(Err(e)) => panic!("Unexpected error type `{}` returned.", e),
None => panic!("Iterator is empty."),
}
}
#[test]
fn gro_iter_missing_natoms() {
let mut system = System::from_file("test_files/protein.gro").unwrap();
match system
.gro_iter("test_files/protein_trajectory_missing_natoms.gro")
.unwrap()
.nth(1)
{
Some(Ok(_)) => panic!("Function should have failed."),
Some(Err(ReadTrajError::FrameNotFound)) => (),
Some(Err(e)) => panic!("Unexpected error type `{}` returned.", e),
None => panic!("Iterator is empty."),
}
}
#[test]
fn gro_iter_invalid_atom_numbers() {
let mut system = System::from_file("test_files/example.gro").unwrap();
match system
.gro_iter("test_files/protein_trajectory.gro")
.unwrap()
.next()
{
Some(Ok(_)) => panic!("Function should have failed."),
Some(Err(ReadTrajError::AtomsNumberMismatch(x))) => {
assert_eq!(x.to_str().unwrap(), "test_files/protein_trajectory.gro")
}
Some(Err(e)) => panic!("Unexpected error type `{}` returned.", e),
None => panic!("Iterator is empty."),
}
}
#[test]
fn gro_iter_missing_title() {
let mut system = System::from_file("test_files/protein.gro").unwrap();
match system
.gro_iter("test_files/protein_trajectory_missing_title.gro")
.unwrap()
.nth(1)
{
Some(Ok(_)) => panic!("Function should have failed."),
Some(Err(ReadTrajError::FrameNotFound)) => (),
Some(Err(e)) => panic!("Unexpected error type `{}` returned.", e),
None => panic!("Iterator is empty."),
}
}
#[test]
fn gro_iter_incomplete_line() {
let mut system = System::from_file("test_files/protein.gro").unwrap();
match system
.gro_iter("test_files/protein_trajectory_incomplete_line.gro")
.unwrap()
.nth(1)
{
Some(Ok(_)) => panic!("Function should have failed."),
Some(Err(ReadTrajError::FrameNotFound)) => (),
Some(Err(e)) => panic!("Unexpected error type `{}` returned.", e),
None => panic!("Iterator is empty."),
}
}
#[test]
fn gro_iter_nan_position() {
let mut system = System::from_file("test_files/protein.gro").unwrap();
match system
.gro_iter("test_files/nan_trajectory.gro")
.unwrap()
.nth(3)
{
Some(Ok(_)) => panic!("Function should have failed."),
Some(Err(ReadTrajError::GroSpecificError(ParseGroError::InvalidFloat(x)))) => {
assert_eq!(
x,
String::from(" 23ALA SC1 47 6.290 NaN 4.190\n")
)
}
Some(Err(e)) => panic!("Unexpected error type `{}` returned.", e),
None => panic!("Iterator is empty."),
}
}
}
#[cfg(test)]
mod tests_write {
use tempfile::NamedTempFile;
use super::*;
#[cfg(any(feature = "molly", not(feature = "no-xdrfile")))]
#[test]
fn gro_writer_no_velocities() {
let mut system = System::from_file("test_files/protein.gro").unwrap();
let gro_output = NamedTempFile::new().unwrap();
let path_to_output = gro_output.path();
system.gro_writer_init(path_to_output).unwrap();
for frame in system
.xtc_iter("test_files/short_trajectory_protein.xtc")
.unwrap()
.take(3)
{
let frame = frame.unwrap();
frame.traj_write_frame().unwrap();
}
system.traj_close();
let mut result = File::open(path_to_output).unwrap();
let mut expected = File::open("test_files/expected_protein_trajectory.gro").unwrap();
assert!(file_diff::diff_files(&mut result, &mut expected));
}
#[test]
fn gro_writer_velocities() {
let mut system = System::from_file("test_files/protein.gro").unwrap();
let gro_output = NamedTempFile::new().unwrap();
let path_to_output = gro_output.path();
system.gro_writer_init(path_to_output).unwrap();
for frame in system
.gro_iter("test_files/expected_protein_trajectory_velocities.gro")
.unwrap()
{
let frame = frame.unwrap();
frame.traj_write_frame().unwrap();
}
system.traj_close();
let mut result = File::open(path_to_output).unwrap();
let mut expected =
File::open("test_files/expected_protein_trajectory_velocities.gro").unwrap();
assert!(file_diff::diff_files(&mut result, &mut expected));
}
#[cfg(any(feature = "molly", not(feature = "no-xdrfile")))]
#[test]
fn gro_writer_group_no_velocities() {
let mut system = System::from_file("test_files/example.gro").unwrap();
system.group_create("Protein", "@protein").unwrap();
let gro_output = NamedTempFile::new().unwrap();
let path_to_output = gro_output.path();
system
.gro_group_writer_init(path_to_output, "Protein")
.unwrap();
for frame in system
.xtc_iter("test_files/short_trajectory.xtc")
.unwrap()
.take(3)
{
let frame = frame.unwrap();
frame.traj_write_frame().unwrap();
}
system.traj_close();
let mut result = File::open(path_to_output).unwrap();
let mut expected = File::open("test_files/expected_protein_trajectory.gro").unwrap();
assert!(file_diff::diff_files(&mut result, &mut expected));
}
#[test]
#[cfg(not(feature = "no-xdrfile"))]
fn gro_writer_group_velocities() {
let mut system = System::from_file("test_files/example.gro").unwrap();
system.group_create("Protein", "@protein").unwrap();
let gro_output = NamedTempFile::new().unwrap();
let path_to_output = gro_output.path();
system
.gro_group_writer_init(path_to_output, "Protein")
.unwrap();
for frame in system
.trr_iter("test_files/short_trajectory.trr")
.unwrap()
.with_step(3)
.unwrap()
{
let frame = frame.unwrap();
frame.traj_write_frame().unwrap();
}
system.traj_close();
let mut result = File::open(path_to_output).unwrap();
let mut expected =
File::open("test_files/expected_protein_trajectory_velocities.gro").unwrap();
assert!(file_diff::diff_files(&mut result, &mut expected));
}
#[cfg(any(feature = "molly", not(feature = "no-xdrfile")))]
#[test]
fn gro_writer_group_replace_group() {
let mut system = System::from_file("test_files/example.gro").unwrap();
system.group_create("Protein", "@protein").unwrap();
let gro_output = NamedTempFile::new().unwrap();
let path_to_output = gro_output.path();
system
.gro_group_writer_init(path_to_output, "Protein")
.unwrap();
if system.group_create("Protein", "serial 1").is_ok() {
panic!("Function should return warning but it did not.");
}
for frame in system
.xtc_iter("test_files/short_trajectory.xtc")
.unwrap()
.take(3)
{
let frame = frame.unwrap();
frame.traj_write_frame().unwrap();
}
system.traj_close();
let mut result = File::open(path_to_output).unwrap();
let mut expected = File::open("test_files/expected_protein_trajectory.gro").unwrap();
assert!(file_diff::diff_files(&mut result, &mut expected));
}
#[cfg(any(feature = "molly", not(feature = "no-xdrfile")))]
#[test]
fn gro_writer_group_remove_group() {
let mut system = System::from_file("test_files/example.gro").unwrap();
system.group_create("Protein", "@protein").unwrap();
let gro_output = NamedTempFile::new().unwrap();
let path_to_output = gro_output.path();
system
.gro_group_writer_init(path_to_output, "Protein")
.unwrap();
system.group_remove("Protein").unwrap();
for frame in system
.xtc_iter("test_files/short_trajectory.xtc")
.unwrap()
.take(3)
{
let frame = frame.unwrap();
frame.traj_write_frame().unwrap();
}
system.traj_close();
let mut result = File::open(path_to_output).unwrap();
let mut expected = File::open("test_files/expected_protein_trajectory.gro").unwrap();
assert!(file_diff::diff_files(&mut result, &mut expected));
}
#[test]
fn gro_writer_invalid_path() {
let mut system = System::from_file("test_files/example.gro").unwrap();
match system.gro_writer_init("test_files/nonexistent/output.gro") {
Err(WriteTrajError::CouldNotCreate(_)) => (),
_ => panic!("Output GRO file should not have been created."),
}
}
#[test]
fn gro_group_writer_invalid_group() {
let mut system = System::from_file("test_files/example.gro").unwrap();
match system.gro_group_writer_init("will_not_be_created.gro", "Protein") {
Ok(_) => panic!("Function should have failed but it succeeded."),
Err(WriteTrajError::GroupNotFound(x)) => {
assert_eq!(x, "Protein");
assert!(!Path::new("will_not_be_created.gro").exists());
}
Err(e) => panic!("Unexpected error type `{}` returned.", e),
}
}
}