mirror of
https://github.com/dnlbauer/xdrfile.git
synced 2026-09-10 14:15:31 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4483f8a94 | ||
|
|
faaa335e37 | ||
|
|
ee9158555e | ||
|
|
a29faf9d63 | ||
|
|
3b89309b8b | ||
|
|
daccb110db | ||
|
|
8ce523cb68 | ||
|
|
b1d92ab150 | ||
|
|
49f77c672a | ||
|
|
53c668631d | ||
|
|
5b7247962e |
@@ -4,6 +4,6 @@ rust:
|
||||
- beta
|
||||
- nightly
|
||||
git:
|
||||
depth: 10
|
||||
depth: 50
|
||||
after_success:
|
||||
./.travis_bench.sh
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[](https://travis-ci.com/danijoo/xdrfile) [](https://www.crates.io/crates/xdrfile)
|
||||
[](https://travis-ci.org/danijoo/xdrfile) [](https://www.crates.io/crates/xdrfile) [](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(())
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
89
src/frame.rs
89
src/frame.rs
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
111
src/lib.rs
111
src/lib.rs
@@ -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))
|
||||
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(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user