11 Commits

Author SHA1 Message Date
daniel
c4483f8a94 travis clone depth 2020-11-19 10:26:18 +01:00
daniel
faaa335e37 test appending 2020-11-19 10:24:11 +01:00
daniel
ee9158555e array-like frame indexing 2020-11-17 09:17:39 +01:00
daniel
a29faf9d63 Merge branch 'clippy' 2020-11-17 08:41:19 +01:00
daniel
3b89309b8b test filter coords 2020-11-17 08:41:11 +01:00
Josh Mitchell
daccb110db ErrorCode::is_eof() now takes self by value (8 bits) rather than reference (64 bits) 2020-11-17 00:21:11 +11:00
Josh Mitchell
8ce523cb68 Explicit returns for error cases 2020-11-17 00:19:33 +11:00
Daniel Bauer
b1d92ab150 Update README.md 2020-11-16 14:17:22 +01:00
Josh Mitchell
49f77c672a Deref rather than clone in Frame::filter_coords() 2020-11-17 00:17:04 +11:00
Daniel Bauer
53c668631d Update README.md 2020-11-16 14:14:27 +01:00
daniel
5b7247962e add repo 2020-11-16 14:01:33 +01:00
7 changed files with 173 additions and 42 deletions

View File

@@ -4,6 +4,6 @@ rust:
- beta
- nightly
git:
depth: 10
depth: 50
after_success:
./.travis_bench.sh

View File

@@ -4,6 +4,7 @@ version = "0.3.0"
authors = ["Daniel Bauer <bauer@cbs.tu-darmstadt.de>"]
license = "LGPL-3.0-only"
edition = "2018"
repository = "https://github.com/danijoo/xdrfile"
description = "Wrapper around the gromacs libxdrfile library. Can be used to read and write gromacs trajectories in xtc and trr format."
readme = "README.md"
categories = ["external-ffi-bindings", "encoding", "science"]

View File

@@ -1,4 +1,4 @@
[![Build Status](https://travis-ci.com/danijoo/xdrfile.svg?branch=master)](https://travis-ci.com/danijoo/xdrfile) [![crates.io](https://img.shields.io/badge/crates.io-orange.svg?longCache=true)](https://www.crates.io/crates/xdrfile)
[![Build Status](https://travis-ci.org/danijoo/xdrfile.svg?branch=master)](https://travis-ci.org/danijoo/xdrfile) [![crates.io](https://img.shields.io/badge/crates.io-orange.svg?longCache=true)](https://www.crates.io/crates/xdrfile) [![Latest Documentation](https://docs.rs/xdrfile/badge.svg)](https://docs.rs/xdrfile)
# xdrfile
Read and write xdr trajectory files in .xtc and .trr file format
@@ -29,7 +29,7 @@ fn main() -> Result<()> {
assert_eq!(frame.step, 1);
assert_eq!(frame.len(), num_atoms);
let first_atom_coords = frame.coords[0];
let first_atom_coords = frame[0]; // shorthand for frame.coords[0]
assert_eq!(first_atom_coords, [-0.8901, 0.4127, -0.055499997]);
Ok(())

View File

@@ -64,7 +64,7 @@ impl std::error::Error for Error {
} else {
None
}
},
}
Error::CouldNotCheckNAtoms(err) => Some(err.as_ref()),
_ => None,
}
@@ -200,7 +200,7 @@ pub enum ErrorCode {
impl ErrorCode {
/// True if the error is an end of file error, false otherwise
pub fn is_eof(&self) -> bool {
pub fn is_eof(self) -> bool {
matches!(self, Self::ExdrEndOfFile)
}
}

View File

@@ -1,3 +1,5 @@
use std::ops::{Index, IndexMut};
/// A frame represents a single step in a trajectory.
#[derive(Clone, Debug)]
pub struct Frame {
@@ -40,14 +42,13 @@ impl Frame {
}
/// Filters the frame by removing all atoms not matching the given indeces.
pub fn filter_coords(self: &mut Frame, indeces: &[usize]) {
pub fn filter_coords(self: &mut Frame, indices: &[usize]) {
self.coords = self
.coords
.iter()
.map(|elem| elem.clone())
.enumerate()
.filter(|&(i, _)| indeces.contains(&i))
.map(|(_, elem)| elem)
.filter(|(i, _)| indices.contains(i))
.map(|(_, elem)| *elem)
.collect();
}
@@ -67,6 +68,20 @@ impl Frame {
}
}
impl Index<usize> for Frame {
type Output = [f32; 3];
fn index(&self, index: usize) -> &Self::Output {
&self.coords[index]
}
}
impl IndexMut<usize> for Frame {
fn index_mut(&mut self, index: usize) -> &mut Self::Output { {
&mut self.coords[index]
}}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -81,15 +96,15 @@ mod tests {
#[test]
fn test_frame_filter_atoms() {
let mut frame = Frame::with_len(3);
frame.coords[0] = [1.0, 2.0, 3.0];
frame.coords[1] = [4.0, 5.0, 6.0];
frame.coords[2] = [7.0, 8.0, 9.0];
frame[0] = [1.0, 2.0, 3.0];
frame[1] = [4.0, 5.0, 6.0];
frame[2] = [7.0, 8.0, 9.0];
let filter: Vec<usize> = vec![1, 2];
let mut frame_new = frame.clone();
frame_new.filter_coords(&filter);
assert!(frame_new.len() == filter.len());
assert!(frame_new.coords[0] == frame.coords[1]);
assert!(frame_new.coords[1] == frame.coords[2]);
assert!(frame_new.coords[0] == frame[1]);
assert!(frame_new.coords[1] == frame[2]);
}
#[test]
@@ -97,4 +112,60 @@ mod tests {
let frame = Frame::with_len(10);
assert_eq!(frame.len(), 10);
}
#[test]
fn test_filter_coords() {
let mut frame = Frame {
step: 0,
time: 0.0,
box_vector: [[0.0; 3]; 3],
coords: vec![[0.0; 3], [1.0; 3], [2.0; 3]]
};
frame.filter_coords(&[1]);
for i in 0..3 {
assert_approx_eq!(frame[0][i], 1.0);
}
}
#[test]
#[allow(unused_mut)]
fn test_index() {
// test Index
let frame = Frame {
step: 0,
time: 0.0,
box_vector: [[0.0; 3]; 3],
coords: vec![[0.0; 3], [1.0; 3], [2.0; 3]]
};
for i in 0..frame.len() {
for j in 0..3 {
assert_approx_eq!(frame[i][j], frame[i][j]);
}
}
// test IndexMut
let mut frame = Frame {
step: 0,
time: 0.0,
box_vector: [[0.0; 3]; 3],
coords: vec![[0.0; 3], [1.0; 3], [2.0; 3]]
};
for i in 0..frame.len() {
for j in 0..3 {
assert_approx_eq!(frame[i][j], frame.coords[i][j]);
}
}
frame[0] = [123.0; 3];
for i in 0..frame.len() {
for j in 0..3 {
assert_approx_eq!(frame[i][j], frame.coords[i][j]);
if i == 0 {
assert_approx_eq!(frame[i][j], 123.0);
}
}
}
}
}

View File

@@ -49,7 +49,7 @@ impl<T: Trajectory> TrajectoryIterator<T> {
// It's OK to do this every frame because the result is cached by Trajectory
let num_atoms = match &self.trajectory.get_num_atoms() {
&Ok(n) => n,
Err(e) => Err(Error::CouldNotCheckNAtoms(Box::new(e.clone())))?,
Err(e) => return Err(Error::CouldNotCheckNAtoms(Box::new(e.clone()))),
};
// Reuse old frame

View File

@@ -26,7 +26,7 @@
//! assert_eq!(frame.step, 1);
//! assert_eq!(frame.len(), num_atoms);
//!
//! let first_atom_coords = frame.coords[0];
//! let first_atom_coords = frame[0];
//! assert_eq!(first_atom_coords, [-0.8901, 0.4127, -0.055499997]);
//!
//! Ok(())
@@ -175,7 +175,7 @@ impl XDRFile {
})
} else {
// Something went wrong. But the C api does not tell us what
Err((path, filemode))?
Err((path, filemode).into())
}
}
}
@@ -232,6 +232,7 @@ pub trait Trajectory {
/// Get the number of atoms from the give trajectory
fn get_num_atoms(&mut self) -> Result<usize>;
}
/// Handle to Read/Write XTC Trajectories
@@ -275,8 +276,8 @@ impl Trajectory for XTCTrajectory {
.get_num_atoms()
.map_err(|e| Error::CouldNotCheckNAtoms(Box::new(e)))?;
if num_atoms != frame.coords.len() {
Err((&*frame, num_atoms))?;
};
return Err((&*frame, num_atoms).into());
}
unsafe {
let code = xdrfile_xtc::read_xtc(
@@ -402,7 +403,7 @@ impl Trajectory for TRRTrajectory {
.get_num_atoms()
.map_err(|e| Error::CouldNotCheckNAtoms(Box::new(e)))?;
if num_atoms != frame.coords.len() {
Err((&*frame, num_atoms))?;
return Err((&*frame, num_atoms).into());
}
unsafe {
@@ -501,14 +502,15 @@ mod tests {
use tempfile::NamedTempFile;
#[test]
fn test_read_write_xtc() -> Result<()> {
fn test_write_append_read_xtc() -> Result<()> {
let tempfile = NamedTempFile::new().expect("Could not create temporary file");
let tmp_path = tempfile.path();
let natoms = 2;
// write frame 1
let frame = Frame {
step: 5,
time: 2.0,
step: 1,
time: 1.0,
box_vector: [[1.0, 2.0, 3.0], [2.0, 1.0, 3.0], [3.0, 2.0, 1.0]],
coords: vec![[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]],
};
@@ -520,11 +522,28 @@ mod tests {
}
f.flush()?;
// append frame 2
let frame2 = Frame {
step: 2,
time: 2.0,
box_vector: [[1.0, 2.0, 3.0], [2.0, 1.0, 3.0], [3.0, 2.0, 1.0]],
coords: vec![[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]],
};
let mut f = XTCTrajectory::open_append(&tmp_path)?;
let write_status = f.write(&frame2);
match write_status {
Err(_) => panic!("Failed"),
Ok(()) => {}
}
f.flush()?;
// open trj for read
let mut new_frame = Frame::with_len(natoms);
let mut f = XTCTrajectory::open_read(tmp_path)?;
let num_atoms = f.get_num_atoms()?;
assert_eq!(num_atoms, natoms);
// check frame 1 ...
let read_status = f.read(&mut new_frame);
match read_status {
Err(e) => assert!(false, "{:?}", e),
@@ -536,22 +555,36 @@ mod tests {
assert_approx_eq!(new_frame.time, frame.time);
assert_eq!(new_frame.box_vector, frame.box_vector);
assert_eq!(new_frame.coords, frame.coords);
// and check frame 1 ...
let read_status = f.read(&mut new_frame);
match read_status {
Err(e) => assert!(false, "{:?}", e),
Ok(()) => {}
}
assert_eq!(new_frame.len(), frame2.len());
assert_eq!(new_frame.step, frame2.step);
assert_approx_eq!(new_frame.time, frame2.time);
assert_eq!(new_frame.box_vector, frame2.box_vector);
assert_eq!(new_frame.coords, frame2.coords);
Ok(())
}
#[test]
fn test_read_write_trr() -> Result<()> {
fn test_write_append_read_trr() -> Result<()> {
let tempfile = NamedTempFile::new().expect("Could not create temporary file");
let tmp_path = tempfile.path();
let natoms = 2;
// write frame 1
let frame = Frame {
step: 5,
time: 2.0,
step: 1,
time: 1.0,
box_vector: [[1.0, 2.0, 3.0], [2.0, 1.0, 3.0], [3.0, 2.0, 1.0]],
coords: vec![[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]],
};
let mut f = TRRTrajectory::open_write(tmp_path)?;
let mut f = TRRTrajectory::open_write(&tmp_path)?;
let write_status = f.write(&frame);
match write_status {
Err(_) => panic!("Failed"),
@@ -559,11 +592,28 @@ mod tests {
}
f.flush()?;
// append frame 2
let frame2 = Frame {
step: 2,
time: 2.0,
box_vector: [[1.0, 2.0, 3.0], [2.0, 1.0, 3.0], [3.0, 2.0, 1.0]],
coords: vec![[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]],
};
let mut f = TRRTrajectory::open_append(&tmp_path)?;
let write_status = f.write(&frame2);
match write_status {
Err(_) => panic!("Failed"),
Ok(()) => {}
}
f.flush()?;
// open trj for read
let mut new_frame = Frame::with_len(natoms);
let mut f = TRRTrajectory::open_read(tmp_path)?;
// let num_atoms = f.get_num_atoms()?;
// assert_eq!(num_atoms, natoms);
let num_atoms = f.get_num_atoms()?;
assert_eq!(num_atoms, natoms);
// check frame 1 ...
let read_status = f.read(&mut new_frame);
match read_status {
Err(e) => assert!(false, "{:?}", e),
@@ -572,9 +622,22 @@ mod tests {
assert_eq!(new_frame.len(), frame.len());
assert_eq!(new_frame.step, frame.step);
assert_eq!(new_frame.time, frame.time);
assert_approx_eq!(new_frame.time, frame.time);
assert_eq!(new_frame.box_vector, frame.box_vector);
assert_eq!(new_frame.coords, frame.coords);
// and check frame 1 ...
let read_status = f.read(&mut new_frame);
match read_status {
Err(e) => assert!(false, "{:?}", e),
Ok(()) => {}
}
assert_eq!(new_frame.len(), frame2.len());
assert_eq!(new_frame.step, frame2.step);
assert_approx_eq!(new_frame.time, frame2.time);
assert_eq!(new_frame.box_vector, frame2.box_vector);
assert_eq!(new_frame.coords, frame2.coords);
Ok(())
}
@@ -631,21 +694,17 @@ mod tests {
Ok(s) => {
assert_eq!(s, CString::new("test")?);
}
Err(_) => panic!("Valid Path failed to convert to CString.")
Err(_) => panic!("Valid Path failed to convert to CString."),
}
// \0 in path should result in an InvalidOsStr(Some(NulError))
// \0 in path should result in an InvalidOsStr(Some(NulError))
let result = path_to_cstring(PathBuf::from("invalid/\0path"));
match result {
Ok(_) => panic!("Cstring conversion did not fail"),
Err(e) => {
match e {
Error::InvalidOsStr(opt) => {
assert!(opt.is_some())
}
_ => panic!("Wrong error type. (This should never happend).")
}
}
Err(e) => match e {
Error::InvalidOsStr(opt) => assert!(opt.is_some()),
_ => panic!("Wrong error type. (This should never happend)."),
},
}
Ok(())
}