From aaf3a35b4f35c62f3ec5305272d3e4b659576c11 Mon Sep 17 00:00:00 2001 From: Douwe Schulte Date: Fri, 22 Jul 2022 01:22:50 +0200 Subject: [PATCH 1/2] Preliminairy work on Arc based atom bonds (#80) --- Cargo.toml | 2 +- src/read/pdb/parser.rs | 44 +++++++++++++++++++++------------------- src/structs/conformer.rs | 27 ++++++++++++++---------- src/structs/pdb.rs | 35 ++++++++++++++------------------ 4 files changed, 55 insertions(+), 53 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 06de8bc..6e0cd5d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ include = ["src/**/*", "LICENSE", "README.md"] [dependencies] rstar = { version = "^0.9.2", optional = true} -serde = { version = "~1.0", optional = true, features = ["derive"] } +serde = { version = "~1.0", optional = true, features = ["derive", "rc"] } rayon = {version = "^1", optional = true} doc-cfg = "0.1" indexmap = "^1.8" diff --git a/src/read/pdb/parser.rs b/src/read/pdb/parser.rs index 7ef4ad8..a88ed0e 100644 --- a/src/read/pdb/parser.rs +++ b/src/read/pdb/parser.rs @@ -607,33 +607,35 @@ fn add_modifications(pdb: &mut PDB, modifications: Vec<(Context, LexItem)>) -> V /// Adds all bonds to the PDB, has to be done after all Atoms are already in place #[allow(clippy::unwrap_used)] fn add_bonds(pdb: &mut PDB, bonds: Vec<(Context, LexItem)>) -> Vec { - let mut errors = Vec::new(); - for (context, bond) in bonds { + let errors = Vec::new(); + for (_context, bond) in bonds { match bond { LexItem::SSBond(atom1, atom2, ..) => { let find = |atom: (String, isize, Option, String)| { - pdb.chains() - .find(|c| c.id() == atom.3) - .and_then(|c| { - c.residues() - .find(|r| { - r.serial_number() == atom.1 - && r.insertion_code() == atom.2.as_deref() - }) - .map(|r| { - r.conformers().find(|c| c.name() == atom.0).map(|c| { - c.atoms().find(|a| a.name() == "SG").map(Atom::counter) - }) - }) - }) - .flatten() - .flatten() + let h = pdb + .find( + Term::ConformerName(atom.0) + & Term::ResidueSerialNumber(atom.1) + & Term::ResidueInsertionCode(atom.2), + ) + .next() + .unwrap(); + ( + h.atom().serial_number().to_owned(), + h.conformer() + .alternative_location() + .map(std::borrow::ToOwned::to_owned), + ) }; let ref1 = find(atom1); let ref2 = find(atom2); - + pdb.add_bond( + (ref1.0, ref1.1.as_deref()), + (ref2.0, ref2.1.as_deref()), + Bond::Disulfide, + ); + /* if let (Some(counter1), Some(counter2)) = (ref1, ref2) { - pdb.add_bond_counters(counter1, counter2, Bond::Disulfide); } else { errors.push(PDBError::new( ErrorLevel::InvalidatingError, @@ -641,7 +643,7 @@ fn add_bonds(pdb: &mut PDB, bonds: Vec<(Context, LexItem)>) -> Vec { "One of the atoms could not be found while parsing a disulfide bond.", context, )); - } + } */ } _ => { panic!( diff --git a/src/structs/conformer.rs b/src/structs/conformer.rs index c55e197..301e90a 100644 --- a/src/structs/conformer.rs +++ b/src/structs/conformer.rs @@ -7,6 +7,7 @@ use doc_cfg::doc_cfg; use rayon::prelude::*; use std::cmp::Ordering; use std::fmt; +use std::sync::Arc; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] @@ -17,7 +18,7 @@ pub struct Conformer { /// The alternative location of this Conformer, None is blank alternative_location: Option, /// The list of atoms making up this Conformer - atoms: Vec, + atoms: Vec>, /// The modification, if present modification: Option<(String, String)>, } @@ -49,7 +50,7 @@ impl Conformer { res.alternative_location = prepare_identifier(al); } if let Some(a) = atom { - res.atoms.push(a); + res.atoms.push(Arc::new(a)); } res }) @@ -137,7 +138,11 @@ impl Conformer { /// ## Fails /// It returns `None` if the index is out of bounds. pub fn atom(&self, index: usize) -> Option<&Atom> { - self.atoms.get(index) + self.atoms.get(index).map(std::convert::AsRef::as_ref) + } + + pub(crate) fn atom_arc(&self, number: usize) -> Option<&Arc> { + self.atoms.iter().find(|a| a.serial_number() == number) } /// Get a specific atom as a mutable reference from list of atoms making up this Conformer. @@ -148,7 +153,7 @@ impl Conformer { /// ## Fails /// It returns `None` if the index is out of bounds. pub fn atom_mut(&mut self, index: usize) -> Option<&mut Atom> { - self.atoms.get_mut(index) + self.atoms.get_mut(index).and_then(Arc::get_mut) } /// Get a reference to the specified atom which is unique within a single conformer. @@ -175,7 +180,7 @@ impl Conformer { .atoms .binary_search_by(|a| a.serial_number().cmp(&serial_number)) { - unsafe { Some(self.atoms.get_unchecked_mut(i)) } + unsafe { Arc::get_mut(self.atoms.get_unchecked_mut(i)) } } else { None } @@ -196,32 +201,32 @@ impl Conformer { /// Get an iterator of references to Atoms making up this Conformer. /// Double ended so iterating from the end is just as fast as from the start. pub fn atoms(&self) -> impl DoubleEndedIterator + '_ { - self.atoms.iter() + self.atoms.iter().map(std::convert::AsRef::as_ref) } /// Get a parallel iterator of references to Atoms making up this Conformer. #[doc_cfg(feature = "rayon")] pub fn par_atoms(&self) -> impl ParallelIterator + '_ { - self.atoms.par_iter() + self.atoms.par_iter().map(std::convert::AsRef::as_ref) } /// Get an iterator of mutable references to Atoms making up this Conformer. /// Double ended so iterating from the end is just as fast as from the start. pub fn atoms_mut(&mut self) -> impl DoubleEndedIterator + '_ { - self.atoms.iter_mut() + self.atoms.iter_mut().filter_map(Arc::get_mut) } /// Get a parallel iterator of mutable references to Atoms making up this Conformer. #[doc_cfg(feature = "rayon")] pub fn par_atoms_mut(&mut self) -> impl ParallelIterator + '_ { - self.atoms.par_iter_mut() + self.atoms.par_iter_mut().filter_map(Arc::get_mut) } /// Add a new atom to the list of atoms making up this Conformer. /// ## Arguments /// * `new_atom` - the new Atom to add pub fn add_atom(&mut self, new_atom: Atom) { - self.atoms.push(new_atom); + self.atoms.push(Arc::new(new_atom)); } /// Returns whether this Conformer is an amino acid. @@ -392,7 +397,7 @@ impl Ord for Conformer { impl Extend for Conformer { /// Extend the Atoms on this Conformer by the given iterator over Atoms. fn extend>(&mut self, iter: T) { - self.atoms.extend(iter); + self.atoms.extend(iter.into_iter().map(Arc::new)); } } diff --git a/src/structs/pdb.rs b/src/structs/pdb.rs index 57c6f60..8f60dff 100644 --- a/src/structs/pdb.rs +++ b/src/structs/pdb.rs @@ -6,6 +6,7 @@ use crate::transformation::TransformationMatrix; use doc_cfg::doc_cfg; #[cfg(feature = "rayon")] use rayon::prelude::*; +use std::sync::Arc; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq)] @@ -50,7 +51,7 @@ pub struct PDB { /// The Models making up this PDB, containing all chain, residues, conformers, and atoms. models: Vec, /// Bonds in this PDB. - bonds: Vec<(usize, usize, Bond)>, + bonds: Vec<(Arc, Arc, Bond)>, } /// # Creators @@ -909,17 +910,9 @@ impl<'a> PDB { /// Get the bonds in this PDB file. Runtime is `O(bonds_count * 2 * atom_count)` because it /// has to iterate over all atoms to prevent borrowing problems. pub fn bonds(&self) -> impl DoubleEndedIterator + '_ { - self.bonds.iter().map(move |(a, b, bond)| { - ( - self.atoms() - .find(|atom| atom.counter() == *a) - .expect("Could not find an atom in the bonds list"), - self.atoms() - .find(|atom| atom.counter() == *b) - .expect("Could not find an atom in the bonds list"), - *bond, - ) - }) + self.bonds + .iter() + .map(move |(a, b, bond)| (a.as_ref(), b.as_ref(), *bond)) } /// Add a bond of the given type to the list of bonds in this PDB. @@ -934,18 +927,20 @@ impl<'a> PDB { bond: Bond, ) -> Option<()> { self.bonds.push(( - self.binary_find_atom(atom1.0, atom1.1)?.atom().counter(), - self.binary_find_atom(atom2.0, atom2.1)?.atom().counter(), + self.binary_find_atom(atom1.0, atom1.1)? + .conformer() + .atom_arc(atom1.0) + .map(Arc::clone) + .expect("Could not find atom 1"), + self.binary_find_atom(atom2.0, atom2.1)? + .conformer() + .atom_arc(atom2.0) + .map(Arc::clone) + .expect("Could not find atom 2"), bond, )); Some(()) } - - /// Add a bond of the given type to the list of bonds in this PDB. - /// The raw counters of the atoms are given. - pub(crate) fn add_bond_counters(&mut self, atom1: usize, atom2: usize, bond: Bond) { - self.bonds.push((atom1, atom2, bond)); - } } use std::fmt; From 27ca6126b11ee3720696b1769cb4d56469096830 Mon Sep 17 00:00:00 2001 From: Douwe Schulte Date: Mon, 25 Jul 2022 00:15:31 +0200 Subject: [PATCH 2/2] Closer to atom level bonds (WIP) --- src/structs/atom.rs | 34 +++-- src/structs/chain.rs | 125 +++++++-------- src/structs/conformer.rs | 153 ++++++++++--------- src/structs/model.rs | 172 ++++++++++----------- src/structs/pdb.rs | 321 ++++++++++++++++++++++++--------------- src/structs/residue.rs | 101 ++++++------ src/structs/search.rs | 6 + 7 files changed, 509 insertions(+), 403 deletions(-) diff --git a/src/structs/atom.rs b/src/structs/atom.rs index 52d17c7..c6fbc5f 100644 --- a/src/structs/atom.rs +++ b/src/structs/atom.rs @@ -2,19 +2,15 @@ use crate::reference_tables; use crate::structs::*; use crate::transformation::TransformationMatrix; +use std::cell::{Ref, RefCell}; use std::cmp::Ordering; use std::convert::TryInto; use std::fmt; -use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; - -static ATOM_COUNTER: AtomicUsize = AtomicUsize::new(0); - +use std::sync::Arc; /// A struct to represent a single Atom in a protein. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug)] pub struct Atom { - /// The unique serial number given to this atom - counter: usize, /// Determines if this atom is a hetero atom (true), a non standard atom, or a normal atom (false) hetero: bool, /// The serial number of the Atom, should be unique within its model @@ -37,6 +33,8 @@ pub struct Atom { charge: isize, /// The anisotropic temperature factors, if applicable atf: Option<[[f64; 3]; 3]>, + /// Bonds + bonds: Vec<(Arc>, Bond)>, } impl Atom { @@ -81,7 +79,6 @@ impl Atom { None }; Some(Atom { - counter: ATOM_COUNTER.fetch_add(1, AtomicOrdering::SeqCst), hetero, serial_number, name: atom_name.trim().to_ascii_uppercase(), @@ -93,17 +90,13 @@ impl Atom { element, charge, atf: None, + bonds: Vec::new(), }) } else { None } } - /// Get a unique immutable counter for this atom. - pub(crate) const fn counter(&self) -> usize { - self.counter - } - /// Determine if this atom is an hetero atom (`true`), a non standard atom, or a normal atom (`false`). pub const fn hetero(&self) -> bool { self.hetero @@ -350,6 +343,23 @@ impl Atom { self.atf = Some(factors); } + /// Add a bond to this Atom, do not forget to add the reverse bond as well yourself. + pub fn add_bond(&mut self, atom: Arc>, bond: Bond) { + self.bonds.push((atom, bond)); + //atom.add_bond(self, bond); + } + + /// Get the number of bonds on this Atom. + pub fn bond_count(&self) -> usize { + self.bonds.len() + } + + /// Get all bonds to this Atom, it returns an `Arc` which is a reference counted pointer to the Atom, + /// meaning it cannot ever be freed while the value is still being used somewhere. + pub fn bonds(&self) -> impl DoubleEndedIterator, Bond)> + '_ { + self.bonds.iter().map(|(a, b)| (a.borrow(), *b)) + } + /// Determine whether this atom is likely to be a part of the backbone of a protein. /// This is based on this Atom only, for a more precise definition use [`hierarchy::ContainsAtomConformer::is_backbone`]. pub fn is_backbone(&self) -> bool { diff --git a/src/structs/chain.rs b/src/structs/chain.rs index 7ca0fbd..6b35fb9 100644 --- a/src/structs/chain.rs +++ b/src/structs/chain.rs @@ -1,9 +1,10 @@ #![allow(dead_code)] use crate::structs::*; use crate::transformation::TransformationMatrix; -use doc_cfg::doc_cfg; -#[cfg(feature = "rayon")] -use rayon::prelude::*; +//use doc_cfg::doc_cfg; +//#[cfg(feature = "rayon")] +//use rayon::prelude::*; +use std::cell::{Ref, RefMut}; use std::cmp::Ordering; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -87,10 +88,10 @@ impl<'a> Chain { } /// Get the number of Conformers making up this Chain in parallel - #[doc_cfg(feature = "rayon")] - pub fn par_conformer_count(&self) -> usize { - self.par_residues().map(Residue::conformer_count).sum() - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_conformer_count(&self) -> usize { + // self.par_residues().map(Residue::conformer_count).sum() + //} /// Get the number of Atoms making up this Chain pub fn atom_count(&self) -> usize { @@ -98,10 +99,10 @@ impl<'a> Chain { } /// Get the number of Atoms making up this Chain in parallel - #[doc_cfg(feature = "rayon")] - pub fn par_atom_count(&self) -> usize { - self.par_residues().map(Residue::par_atom_count).sum() - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_atom_count(&self) -> usize { + // self.par_residues().map(Residue::par_atom_count).sum() + //} /// Get a reference to a specific Residue from list of Residues making up this Chain. /// @@ -154,7 +155,7 @@ impl<'a> Chain { /// /// ## Fails /// It returns `None` if the index is out of bounds. - pub fn atom(&self, index: usize) -> Option<&Atom> { + pub fn atom(&self, index: usize) -> Option> { self.atoms().nth(index) } @@ -165,7 +166,7 @@ impl<'a> Chain { /// /// ## Fails /// It returns `None` if the index is out of bounds. - pub fn atom_mut(&mut self, index: usize) -> Option<&mut Atom> { + pub fn atom_mut(&mut self, index: usize) -> Option> { self.atoms_mut().nth(index) } @@ -289,10 +290,10 @@ impl<'a> Chain { } /// Get a parallel iterator of references to Residues making up this Chain. - #[doc_cfg(feature = "rayon")] - pub fn par_residues(&self) -> impl ParallelIterator + '_ { - self.residues.par_iter() - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_residues(&self) -> impl ParallelIterator + '_ { + // self.residues.par_iter() + //} /// Get an iterator of mutable references to Residues making up this Chain. /// Double ended so iterating from the end is just as fast as from the start. @@ -301,10 +302,10 @@ impl<'a> Chain { } /// Get a parallel iterator of mutable references to Residues making up this Chain. - #[doc_cfg(feature = "rayon")] - pub fn par_residues_mut(&mut self) -> impl ParallelIterator + '_ { - self.residues.par_iter_mut() - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_residues_mut(&mut self) -> impl ParallelIterator + '_ { + //self.residues.par_iter_mut() + //} /// Get an iterator of references to Conformers making up this Chain. /// Double ended so iterating from the end is just as fast as from the start. @@ -313,10 +314,10 @@ impl<'a> Chain { } /// Get a parallel iterator of references to Conformers making up this Chain. - #[doc_cfg(feature = "rayon")] - pub fn par_conformers(&self) -> impl ParallelIterator + '_ { - self.par_residues().flat_map(Residue::par_conformers) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_conformers(&self) -> impl ParallelIterator + '_ { + //self.par_residues().flat_map(Residue::par_conformers) + //} /// Get an iterator of mutable references to Conformers making up this Chain. /// Double ended so iterating from the end is just as fast as from the start. @@ -325,35 +326,35 @@ impl<'a> Chain { } /// Get a parallel iterator of mutable references to Conformers making up this Chain. - #[doc_cfg(feature = "rayon")] - pub fn par_conformers_mut(&mut self) -> impl ParallelIterator + '_ { - self.par_residues_mut() - .flat_map(Residue::par_conformers_mut) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_conformers_mut(&mut self) -> impl ParallelIterator + '_ { + //self.par_residues_mut() + //.flat_map(Residue::par_conformers_mut) + //} /// Get an iterator of references to Atoms making up this Chain. /// Double ended so iterating from the end is just as fast as from the start. - pub fn atoms(&self) -> impl DoubleEndedIterator + '_ { + pub fn atoms(&self) -> impl DoubleEndedIterator> + '_ { self.residues().flat_map(Residue::atoms) } /// Get a parallel iterator of references to Atoms making up this Chain. - #[doc_cfg(feature = "rayon")] - pub fn par_atoms(&self) -> impl ParallelIterator + '_ { - self.par_residues().flat_map(Residue::par_atoms) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_atoms(&self) -> impl ParallelIterator + '_ { + // self.par_residues().flat_map(Residue::par_atoms) + //} /// Get an iterator of mutable references to Atoms making up this Chain. /// Double ended so iterating from the end is just as fast as from the start. - pub fn atoms_mut(&mut self) -> impl DoubleEndedIterator + '_ { + pub fn atoms_mut(&mut self) -> impl DoubleEndedIterator> + '_ { self.residues_mut().flat_map(Residue::atoms_mut) } /// Get a parallel iterator of mutablereferences to Atoms making up this Chain. - #[doc_cfg(feature = "rayon")] - pub fn par_atoms_mut(&mut self) -> impl ParallelIterator + '_ { - self.par_residues_mut().flat_map(Residue::par_atoms_mut) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_atoms_mut(&mut self) -> impl ParallelIterator + '_ { + // self.par_residues_mut().flat_map(Residue::par_atoms_mut) + //} /// Get an iterator of references to a struct containing all atoms with their hierarchy making up this Chain. pub fn atoms_with_hierarchy( @@ -424,7 +425,7 @@ impl<'a> Chain { /// Remove all Atoms matching the given predicate. As this is done in place this is the fastest way to remove Atoms from this Chain. pub fn remove_atoms_by(&mut self, predicate: F) where - F: Fn(&Atom) -> bool, + F: Fn(Ref<'_, Atom>) -> bool, { for residue in self.residues_mut() { residue.remove_atoms_by(&predicate); @@ -481,17 +482,17 @@ impl<'a> Chain { /// /// ## Arguments /// * `id` - the id construct of the Residue to remove (see Residue.id()) - #[doc_cfg(feature = "rayon")] - pub fn par_remove_residue_by_id(&mut self, id: (isize, Option<&str>)) -> bool { - let index = self.residues.par_iter().position_first(|a| a.id() == id); - - if let Some(i) = index { - self.remove_residue(i); - true - } else { - false - } - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_remove_residue_by_id(&mut self, id: (isize, Option<&str>)) -> bool { + // let index = self.residues.par_iter().position_first(|a| a.id() == id); + // + // if let Some(i) = index { + // self.remove_residue(i); + // true + // } else { + // false + // } + //} /// Remove all empty Residues from this Chain, and all empty Conformers from the Residues. pub fn remove_empty(&mut self) { @@ -508,11 +509,11 @@ impl<'a> Chain { /// Apply a transformation to the position of all atoms making up this Chain, the new position is immediately set. /// Done in parallel. - #[doc_cfg(feature = "rayon")] - pub fn par_apply_transformation(&mut self, transformation: &TransformationMatrix) { - self.par_atoms_mut() - .for_each(|atom| atom.apply_transformation(transformation)); - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_apply_transformation(&mut self, transformation: &TransformationMatrix) { + // self.par_atoms_mut() + // .for_each(|atom| atom.apply_transformation(transformation)); + //} /// Join this Chain with another Chain, this moves all atoms from the other Chain /// to this Chain. All other (meta) data of this Chain will stay the same. @@ -525,11 +526,11 @@ impl<'a> Chain { self.residues.sort(); } - /// Sort the residues of this chain in parallel - #[doc_cfg(feature = "rayon")] - pub fn par_sort(&mut self) { - self.residues.par_sort(); - } + // Sort the residues of this chain in parallel + //#[doc_cfg(feature = "rayon")] + //pub fn par_sort(&mut self) { + // self.residues.par_sort(); + //} } use std::fmt; diff --git a/src/structs/conformer.rs b/src/structs/conformer.rs index 301e90a..ede0d12 100644 --- a/src/structs/conformer.rs +++ b/src/structs/conformer.rs @@ -2,9 +2,10 @@ use crate::reference_tables; use crate::structs::*; use crate::transformation::TransformationMatrix; -use doc_cfg::doc_cfg; -#[cfg(feature = "rayon")] -use rayon::prelude::*; +//use doc_cfg::doc_cfg; +//#[cfg(feature = "rayon")] +//use rayon::prelude::*; +use std::cell::{Ref, RefCell, RefMut}; use std::cmp::Ordering; use std::fmt; use std::sync::Arc; @@ -18,7 +19,7 @@ pub struct Conformer { /// The alternative location of this Conformer, None is blank alternative_location: Option, /// The list of atoms making up this Conformer - atoms: Vec>, + atoms: Vec>>, /// The modification, if present modification: Option<(String, String)>, } @@ -50,7 +51,7 @@ impl Conformer { res.alternative_location = prepare_identifier(al); } if let Some(a) = atom { - res.atoms.push(Arc::new(a)); + res.atoms.push(Arc::new(RefCell::new(a))); } res }) @@ -137,12 +138,12 @@ impl Conformer { /// /// ## Fails /// It returns `None` if the index is out of bounds. - pub fn atom(&self, index: usize) -> Option<&Atom> { - self.atoms.get(index).map(std::convert::AsRef::as_ref) + pub fn atom(&self, index: usize) -> Option> { + self.atoms.get(index).map(|a| a.as_ref().borrow()) } - pub(crate) fn atom_arc(&self, number: usize) -> Option<&Arc> { - self.atoms.iter().find(|a| a.serial_number() == number) + pub(crate) fn atom_arc(&self, index: usize) -> Option<&Arc>> { + self.atoms.get(index) } /// Get a specific atom as a mutable reference from list of atoms making up this Conformer. @@ -152,20 +153,20 @@ impl Conformer { /// /// ## Fails /// It returns `None` if the index is out of bounds. - pub fn atom_mut(&mut self, index: usize) -> Option<&mut Atom> { - self.atoms.get_mut(index).and_then(Arc::get_mut) + pub fn atom_mut(&mut self, index: usize) -> Option> { + self.atoms.get_mut(index).map(|a| a.as_ref().borrow_mut()) } /// Get a reference to the specified atom which is unique within a single conformer. /// The algorithm is based on binary search so it is faster than an exhaustive search, but the /// underlying vector is assumed to be sorted. This assumption can be enforced /// by using `conformer.sort()`. - pub fn binary_find_atom(&self, serial_number: usize) -> Option<&Atom> { + pub fn binary_find_atom(&self, serial_number: usize) -> Option> { if let Ok(i) = self .atoms - .binary_search_by(|a| a.serial_number().cmp(&serial_number)) + .binary_search_by(|a| a.borrow().serial_number().cmp(&serial_number)) { - unsafe { Some(self.atoms.get_unchecked(i)) } + unsafe { Some(self.atoms.get_unchecked(i).borrow()) } } else { None } @@ -175,58 +176,63 @@ impl Conformer { /// The algorithm is based on binary search so it is faster than an exhaustive search, but the /// underlying vector is assumed to be sorted. This assumption can be enforced /// by using `conformer.sort()`. - pub fn binary_find_atom_mut(&mut self, serial_number: usize) -> Option<&mut Atom> { + pub fn binary_find_atom_mut(&mut self, serial_number: usize) -> Option> { if let Ok(i) = self .atoms - .binary_search_by(|a| a.serial_number().cmp(&serial_number)) + .binary_search_by(|a| a.borrow().serial_number().cmp(&serial_number)) { - unsafe { Arc::get_mut(self.atoms.get_unchecked_mut(i)) } + unsafe { self.atoms.get_unchecked(i).try_borrow_mut().ok() } } else { None } } /// Find all atoms matching the given information - pub fn find(&self, search: Search) -> impl DoubleEndedIterator + '_ { + pub fn find(&self, search: Search) -> impl DoubleEndedIterator> + '_ { self.atoms() .filter(move |a| search.add_atom_info(a).complete().unwrap_or(true)) } /// Find all atoms matching the given information - pub fn find_mut(&mut self, search: Search) -> impl DoubleEndedIterator + '_ { + pub fn find_mut( + &mut self, + search: Search, + ) -> impl DoubleEndedIterator> + '_ { self.atoms_mut() .filter(move |a| search.add_atom_info(a).complete().unwrap_or(true)) } /// Get an iterator of references to Atoms making up this Conformer. /// Double ended so iterating from the end is just as fast as from the start. - pub fn atoms(&self) -> impl DoubleEndedIterator + '_ { - self.atoms.iter().map(std::convert::AsRef::as_ref) + pub fn atoms(&self) -> impl DoubleEndedIterator> + '_ { + self.atoms.iter().map(|a| a.borrow()) } /// Get a parallel iterator of references to Atoms making up this Conformer. - #[doc_cfg(feature = "rayon")] - pub fn par_atoms(&self) -> impl ParallelIterator + '_ { - self.atoms.par_iter().map(std::convert::AsRef::as_ref) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_atoms(&self) -> impl ParallelIterator> + '_ { + // self.atoms.par_iter().map(|a| a.borrow()) + //} /// Get an iterator of mutable references to Atoms making up this Conformer. /// Double ended so iterating from the end is just as fast as from the start. - pub fn atoms_mut(&mut self) -> impl DoubleEndedIterator + '_ { - self.atoms.iter_mut().filter_map(Arc::get_mut) + pub fn atoms_mut(&mut self) -> impl DoubleEndedIterator> + '_ { + self.atoms + .iter_mut() + .filter_map(|a| a.try_borrow_mut().ok()) } /// Get a parallel iterator of mutable references to Atoms making up this Conformer. - #[doc_cfg(feature = "rayon")] - pub fn par_atoms_mut(&mut self) -> impl ParallelIterator + '_ { - self.atoms.par_iter_mut().filter_map(Arc::get_mut) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_atoms_mut(&mut self) -> impl ParallelIterator + '_ { + // self.atoms.par_iter_mut().filter_map(Arc::get_mut) + //} /// Add a new atom to the list of atoms making up this Conformer. /// ## Arguments /// * `new_atom` - the new Atom to add pub fn add_atom(&mut self, new_atom: Atom) { - self.atoms.push(Arc::new(new_atom)); + self.atoms.push(Arc::new(RefCell::new(new_atom))); } /// Returns whether this Conformer is an amino acid. @@ -237,9 +243,9 @@ impl Conformer { /// Remove all Atoms matching the given predicate. As this is done in place this is the fastest way to remove Atoms from this Conformer. pub fn remove_atoms_by(&mut self, predicate: F) where - F: Fn(&Atom) -> bool, + F: Fn(Ref<'_, Atom>) -> bool, { - self.atoms.retain(|atom| !predicate(atom)); + self.atoms.retain(|atom| !predicate(atom.borrow())); } /// Remove the Atom specified. @@ -282,20 +288,20 @@ impl Conformer { /// /// ## Panics /// Panics if the index is out of bounds. - #[doc_cfg(feature = "rayon")] - pub fn par_remove_atom_by_serial_number(&mut self, serial_number: usize) -> bool { - let index = self - .atoms - .par_iter() - .position_first(|a| a.serial_number() == serial_number); - - if let Some(i) = index { - self.remove_atom(i); - true - } else { - false - } - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_remove_atom_by_serial_number(&mut self, serial_number: usize) -> bool { + // let index = self + // .atoms + // .par_iter() + // .position_first(|a| a.serial_number() == serial_number); + // + // if let Some(i) = index { + // self.remove_atom(i); + // true + // } else { + // false + // } + //} /// Remove the specified Atom. Returns `true` if a matching Atom was found and removed. /// Removes the first matching Atom from the list. @@ -325,18 +331,18 @@ impl Conformer { /// /// ## Panics /// Panics if the index is out of bounds. - #[doc_cfg(feature = "rayon")] - pub fn par_remove_atom_by_name(&mut self, name: impl AsRef) -> bool { - let name = name.as_ref(); - let index = self.atoms.par_iter().position_first(|a| a.name() == name); - - if let Some(i) = index { - self.remove_atom(i); - true - } else { - false - } - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_remove_atom_by_name(&mut self, name: impl AsRef) -> bool { + // let name = name.as_ref(); + // let index = self.atoms.par_iter().position_first(|a| a.name() == name); + // + // if let Some(i) = index { + // self.remove_atom(i); + // true + // } else { + // false + // } + //} /// Apply a transformation to the position of all atoms making up this Conformer, the new position is immediately set. pub fn apply_transformation(&mut self, transformation: &TransformationMatrix) { @@ -347,11 +353,11 @@ impl Conformer { /// Apply a transformation to the position of all atoms making up this Conformer, the new position is immediately set. /// This is done in parallel. - #[doc_cfg(feature = "rayon")] - pub fn par_apply_transformation(&mut self, transformation: &TransformationMatrix) { - self.par_atoms_mut() - .for_each(|a| a.apply_transformation(transformation)); - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_apply_transformation(&mut self, transformation: &TransformationMatrix) { + // self.par_atoms_mut() + // .for_each(|a| a.apply_transformation(transformation)); + //} /// Join this Conformer with another Conformer, this moves all atoms from the other Conformer /// to this Conformer. All other (meta) data of this Conformer will stay the same. @@ -364,11 +370,11 @@ impl Conformer { self.atoms.sort(); } - /// Sort the Atoms of this Conformer in parallel. - #[doc_cfg(feature = "rayon")] - pub fn par_sort(&mut self) { - self.atoms.par_sort(); - } + // Sort the Atoms of this Conformer in parallel. + //#[doc_cfg(feature = "rayon")] + //pub fn par_sort(&mut self) { + // self.atoms.par_sort(); + //} } impl fmt::Display for Conformer { @@ -397,7 +403,8 @@ impl Ord for Conformer { impl Extend for Conformer { /// Extend the Atoms on this Conformer by the given iterator over Atoms. fn extend>(&mut self, iter: T) { - self.atoms.extend(iter.into_iter().map(Arc::new)); + self.atoms + .extend(iter.into_iter().map(RefCell::new).map(Arc::new)); } } @@ -456,8 +463,8 @@ mod tests { a.add_atom(atom1.clone()); a.add_atom(atom2.clone()); a.add_atom(atom2); - assert_eq!(a.atom(0), Some(&atom1)); - assert_eq!(a.atom_mut(0), Some(&mut atom1)); + assert_eq!(a.atom(0).unwrap().serial_number(), 12); + assert_eq!(a.atom_mut(0).unwrap().serial_number(), 13); a.remove_atom(0); assert!(a.remove_atom_by_name("CB")); assert!(a.remove_atom_by_serial_number(13)); diff --git a/src/structs/model.rs b/src/structs/model.rs index c8c4a63..1d35eb8 100644 --- a/src/structs/model.rs +++ b/src/structs/model.rs @@ -2,11 +2,11 @@ use crate::structs::hierarchy::*; use crate::structs::*; use crate::transformation::TransformationMatrix; -use doc_cfg::doc_cfg; -#[cfg(feature = "rayon")] -use rayon::prelude::*; +//use doc_cfg::doc_cfg; +//#[cfg(feature = "rayon")] +//use rayon::prelude::*; +use std::cell::{Ref, RefMut}; use std::cmp::Ordering; - #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] /// A Model containing multiple Chains. @@ -62,10 +62,10 @@ impl<'a> Model { } /// Get the number of Residues making up this Model in parallel. - #[doc_cfg(feature = "rayon")] - pub fn par_residue_count(&self) -> usize { - self.par_chains().map(Chain::residue_count).sum() - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_residue_count(&self) -> usize { + // self.par_chains().map(Chain::residue_count).sum() + //} /// Get the number of Conformers making up this Model. pub fn conformer_count(&self) -> usize { @@ -73,10 +73,10 @@ impl<'a> Model { } /// Get the number of Conformers making up this Model in parallel. - #[doc_cfg(feature = "rayon")] - pub fn par_conformer_count(&self) -> usize { - self.par_chains().map(Chain::par_conformer_count).sum() - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_conformer_count(&self) -> usize { + // self.par_chains().map(Chain::par_conformer_count).sum() + //} /// Get the number of Atoms making up this Model. pub fn atom_count(&self) -> usize { @@ -84,10 +84,10 @@ impl<'a> Model { } /// Get the number of Atoms making up this Model in parallel. - #[doc_cfg(feature = "rayon")] - pub fn par_atom_count(&self) -> usize { - self.par_chains().map(Chain::par_atom_count).sum() - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_atom_count(&self) -> usize { + // self.par_chains().map(Chain::par_atom_count).sum() + //} /// Get a reference to a specific Chain from list of Chains making up this Model. /// @@ -162,7 +162,7 @@ impl<'a> Model { /// /// ## Fails /// Returns `None` if the index is out of bounds. - pub fn atom(&self, index: usize) -> Option<&Atom> { + pub fn atom(&self, index: usize) -> Option> { self.atoms().nth(index) } @@ -173,12 +173,12 @@ impl<'a> Model { /// /// ## Fails /// Returns `None` if the index is out of bounds. - pub fn atom_mut(&mut self, index: usize) -> Option<&mut Atom> { + pub fn atom_mut(&mut self, index: usize) -> Option> { self.atoms_mut().nth(index) } /// Get a reference to the specified atom. Its uniqueness is guaranteed by including the - /// `insertion_code`, with its full hierarchy. The algorithm is based + /// `alternative_location`, with its full hierarchy. The algorithm is based /// on binary search so it is faster than an exhaustive search, but the /// full structure is assumed to be sorted. This assumption can be enforced /// by using `pdb.full_sort()`. @@ -186,7 +186,7 @@ impl<'a> Model { pub fn binary_find_atom( &'a self, serial_number: usize, - insertion_code: Option<&str>, + alternative_location: Option<&str>, ) -> Option> { if self.chain_count() == 0 { None @@ -215,14 +215,14 @@ impl<'a> Model { .and_then(|index| { self.chain(index) .unwrap() - .binary_find_atom(serial_number, insertion_code) + .binary_find_atom(serial_number, alternative_location) .map(|h| h.extend(self.chain(index).unwrap())) }) } } /// Get a mutable reference to the specified atom. Its uniqueness is guaranteed by - /// including the `insertion_code`, with its full hierarchy. The algorithm is based + /// including the `alternative_location`, with its full hierarchy. The algorithm is based /// on binary search so it is faster than an exhaustive search, but the /// full structure is assumed to be sorted. This assumption can be enforced /// by using `pdb.full_sort()`. @@ -230,7 +230,7 @@ impl<'a> Model { pub fn binary_find_atom_mut( &'a mut self, serial_number: usize, - insertion_code: Option<&str>, + alternative_location: Option<&str>, ) -> Option> { if self.chain_count() == 0 { None @@ -260,7 +260,7 @@ impl<'a> Model { let chain: *mut Chain = self.chain_mut(index).unwrap(); self.chain_mut(index) .unwrap() - .binary_find_atom_mut(serial_number, insertion_code) + .binary_find_atom_mut(serial_number, alternative_location) .map(|h| h.extend(chain)) }) } @@ -301,10 +301,10 @@ impl<'a> Model { } /// Get a parallel iterator of references to Chains making up this Model. - #[doc_cfg(feature = "rayon")] - pub fn par_chains(&self) -> impl ParallelIterator + '_ { - self.chains.par_iter() - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_chains(&self) -> impl ParallelIterator + '_ { + // self.chains.par_iter() + //} /// Get an iterator of mutable references to Chains making up this Model. /// Double ended so iterating from the end is just as fast as from the start. @@ -313,10 +313,10 @@ impl<'a> Model { } /// Get a parallel iterator of mutable references to Chains making up this Model. - #[doc_cfg(feature = "rayon")] - pub fn par_chains_mut(&mut self) -> impl ParallelIterator + '_ { - self.chains.par_iter_mut() - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_chains_mut(&mut self) -> impl ParallelIterator + '_ { + //self.chains.par_iter_mut() + //} /// Get an iterator of references to Residues making up this Model. /// Double ended so iterating from the end is just as fast as from the start. @@ -325,10 +325,10 @@ impl<'a> Model { } /// Get a parallel iterator of references to Residues making up this Model. - #[doc_cfg(feature = "rayon")] - pub fn par_residues(&self) -> impl ParallelIterator + '_ { - self.par_chains().flat_map(Chain::par_residues) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_residues(&self) -> impl ParallelIterator + '_ { + //self.par_chains().flat_map(Chain::par_residues) + //} /// Get an iterator of mutable references to Residues making up this Model. /// Double ended so iterating from the end is just as fast as from the start. @@ -337,10 +337,10 @@ impl<'a> Model { } /// Get a parallel iterator of mutable references to Residues making up this Model. - #[doc_cfg(feature = "rayon")] - pub fn par_residues_mut(&mut self) -> impl ParallelIterator + '_ { - self.par_chains_mut().flat_map(Chain::par_residues_mut) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_residues_mut(&mut self) -> impl ParallelIterator + '_ { + //self.par_chains_mut().flat_map(Chain::par_residues_mut) + //} /// Get an iterator of references to Conformers making up this Model. /// Double ended so iterating from the end is just as fast as from the start. @@ -349,10 +349,10 @@ impl<'a> Model { } /// Get a parallel iterator of references to Conformers making up this Model. - #[doc_cfg(feature = "rayon")] - pub fn par_conformers(&self) -> impl ParallelIterator + '_ { - self.par_chains().flat_map(Chain::par_conformers) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_conformers(&self) -> impl ParallelIterator + '_ { + //self.par_chains().flat_map(Chain::par_conformers) + //} /// Get an iterator of mutable references to Conformers making up this Model. /// Double ended so iterating from the end is just as fast as from the start. @@ -361,34 +361,34 @@ impl<'a> Model { } /// Get a parallel iterator of mutable references to Conformers making up this Model. - #[doc_cfg(feature = "rayon")] - pub fn par_conformers_mut(&mut self) -> impl ParallelIterator + '_ { - self.par_chains_mut().flat_map(Chain::par_conformers_mut) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_conformers_mut(&mut self) -> impl ParallelIterator + '_ { + //self.par_chains_mut().flat_map(Chain::par_conformers_mut) + //} /// Get an iterator of references to Atoms making up this Model. /// Double ended so iterating from the end is just as fast as from the start. - pub fn atoms(&self) -> impl DoubleEndedIterator + '_ { + pub fn atoms(&self) -> impl DoubleEndedIterator> + '_ { self.chains().flat_map(Chain::atoms) } /// Get a parallel iterator of references to Atoms making up this Model. - #[doc_cfg(feature = "rayon")] - pub fn par_atoms(&self) -> impl ParallelIterator + '_ { - self.par_chains().flat_map(Chain::par_atoms) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_atoms(&self) -> impl ParallelIterator + '_ { + //self.par_chains().flat_map(Chain::par_atoms) + //} /// Get an iterator of mutable references to Atoms making up this Model. /// Double ended so iterating from the end is just as fast as from the start. - pub fn atoms_mut(&mut self) -> impl DoubleEndedIterator + '_ { + pub fn atoms_mut(&mut self) -> impl DoubleEndedIterator> + '_ { self.chains_mut().flat_map(Chain::atoms_mut) } /// Get a parallel iterator of mutable references to Atoms making up this Model. - #[doc_cfg(feature = "rayon")] - pub fn par_atoms_mut(&mut self) -> impl ParallelIterator + '_ { - self.par_chains_mut().flat_map(Chain::par_atoms_mut) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_atoms_mut(&mut self) -> impl ParallelIterator + '_ { + //self.par_chains_mut().flat_map(Chain::par_atoms_mut) + //} /// Get an iterator of references to a struct containing all atoms with their hierarchy making up this Model. pub fn atoms_with_hierarchy( @@ -455,7 +455,7 @@ impl<'a> Model { /// As this is done in place this is the fastest way to remove Atoms from this Model. pub fn remove_atoms_by(&mut self, predicate: F) where - F: Fn(&Atom) -> bool, + F: Fn(Ref<'_, Atom>) -> bool, { for residue in self.residues_mut() { residue.remove_atoms_by(&predicate); @@ -527,18 +527,18 @@ impl<'a> Model { /// /// ## Arguments /// * `id` - the id of the Chain to remove - #[doc_cfg(feature = "rayon")] - pub fn par_remove_chain_by_id(&mut self, id: impl AsRef) -> bool { - let id = id.as_ref(); - let index = self.chains.par_iter().position_first(|a| a.id() == id); - - if let Some(i) = index { - self.remove_chain(i); - true - } else { - false - } - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_remove_chain_by_id(&mut self, id: impl AsRef) -> bool { + // let id = id.as_ref(); + // let index = self.chains.par_iter().position_first(|a| a.id() == id); + // + // if let Some(i) = index { + // self.remove_chain(i); + // true + // } else { + // false + // } + //} /// Remove all empty Chain from this Model, and all empty Residues from the Chains. pub fn remove_empty(&mut self) { @@ -547,11 +547,11 @@ impl<'a> Model { } /// Remove all empty Chain from this Model, and all empty Residues from the Chains in parallel. - #[doc_cfg(feature = "rayon")] - pub fn par_remove_empty(&mut self) { - self.par_chains_mut().for_each(Chain::remove_empty); - self.chains.retain(|c| c.residue_count() > 0); - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_remove_empty(&mut self) { + // self.par_chains_mut().for_each(Chain::remove_empty); + // self.chains.retain(|c| c.residue_count() > 0); + //} /// Apply a transformation to the position of all atoms making up this Model, the new position is immediately set. pub fn apply_transformation(&mut self, transformation: &TransformationMatrix) { @@ -562,11 +562,11 @@ impl<'a> Model { /// Apply a transformation to the position of all atoms making up this Model, the new position is immediately set. /// Done in parallel. - #[doc_cfg(feature = "rayon")] - pub fn par_apply_transformation(&mut self, transformation: &TransformationMatrix) { - self.par_atoms_mut() - .for_each(|atom| atom.apply_transformation(transformation)); - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_apply_transformation(&mut self, transformation: &TransformationMatrix) { + // self.par_atoms_mut() + // .for_each(|atom| atom.apply_transformation(transformation)); + //} /// Join this Model with another Model, this moves all atoms from the other Model /// to this Model. All other (meta) data of this Model will stay the same. It will add @@ -580,11 +580,11 @@ impl<'a> Model { self.chains.sort(); } - /// Sort the Chains of this Model in parallel. - #[doc_cfg(feature = "rayon")] - pub fn par_sort(&mut self) { - self.chains.par_sort(); - } + // Sort the Chains of this Model in parallel. + //#[doc_cfg(feature = "rayon")] + //pub fn par_sort(&mut self) { + // self.chains.par_sort(); + //} } use std::fmt; diff --git a/src/structs/pdb.rs b/src/structs/pdb.rs index 8f60dff..50e159d 100644 --- a/src/structs/pdb.rs +++ b/src/structs/pdb.rs @@ -6,6 +6,7 @@ use crate::transformation::TransformationMatrix; use doc_cfg::doc_cfg; #[cfg(feature = "rayon")] use rayon::prelude::*; +use std::cell::Ref; use std::sync::Arc; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -50,8 +51,6 @@ pub struct PDB { pub symmetry: Option, /// The Models making up this PDB, containing all chain, residues, conformers, and atoms. models: Vec, - /// Bonds in this PDB. - bonds: Vec<(Arc, Arc, Bond)>, } /// # Creators @@ -68,7 +67,6 @@ impl PDB { unit_cell: None, symmetry: None, models: Vec::new(), - bonds: Vec::new(), } } } @@ -260,10 +258,10 @@ impl<'a> PDB { } /// Get the number of Chains making up this PDB in parallel. Includes all models. - #[doc_cfg(feature = "rayon")] - pub fn par_total_chain_count(&self) -> usize { - self.models.par_iter().map(Model::chain_count).sum() - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_total_chain_count(&self) -> usize { + // self.models.par_iter().map(Model::chain_count).sum() + //} /// Get the number of Residues making up this PDB. Includes all models. pub fn total_residue_count(&self) -> usize { @@ -273,10 +271,10 @@ impl<'a> PDB { } /// Get the number of Residues making up this PDB in parallel. Includes all models. - #[doc_cfg(feature = "rayon")] - pub fn par_total_residue_count(&self) -> usize { - self.models.par_iter().map(Model::par_residue_count).sum() - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_total_residue_count(&self) -> usize { + // self.models.par_iter().map(Model::par_residue_count).sum() + //} /// Get the number of Conformer making up this PDB. Includes all models. pub fn total_conformer_count(&self) -> usize { @@ -286,10 +284,10 @@ impl<'a> PDB { } /// Get the number of Conformer making up this PDB in parallel. Includes all models. - #[doc_cfg(feature = "rayon")] - pub fn par_total_conformer_count(&self) -> usize { - self.models.par_iter().map(Model::par_conformer_count).sum() - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_total_conformer_count(&self) -> usize { + // self.models.par_iter().map(Model::par_conformer_count).sum() + //} /// Get the number of Atoms making up this PDB. Includes all models. pub fn total_atom_count(&self) -> usize { @@ -299,10 +297,10 @@ impl<'a> PDB { } /// Get the number of Atoms making up this PDB in parallel. Includes all models. - #[doc_cfg(feature = "rayon")] - pub fn par_total_atom_count(&self) -> usize { - self.models.par_iter().map(Model::par_atom_count).sum() - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_total_atom_count(&self) -> usize { + // self.models.par_iter().map(Model::par_atom_count).sum() + //} /// Get a reference to a specific Model from the list of Models making up this PDB. /// @@ -415,7 +413,7 @@ impl<'a> PDB { } /// Get a reference to the specified atom. Its uniqueness is guaranteed by including the - /// `insertion_code`, with its full hierarchy. The algorithm is based + /// `alternative_location`, with its full hierarchy. The algorithm is based /// on binary search so it is faster than an exhaustive search, but the /// full structure is assumed to be sorted. This assumption can be enforced /// by using `pdb.full_sort()`. @@ -431,7 +429,7 @@ impl<'a> PDB { } /// Get a mutable reference to the specified atom. Its uniqueness is guaranteed by - /// including the `insertion_code`, with its full hierarchy. The algorithm is based + /// including the `alternative_location`, with its full hierarchy. The algorithm is based /// on binary search so it is faster than an exhaustive search, but the /// full structure is assumed to be sorted. This assumption can be enforced /// by using `pdb.full_sort()`. @@ -492,10 +490,10 @@ impl<'a> PDB { } /// Get a parallel iterator of references to Models making up this PDB. - #[doc_cfg(feature = "rayon")] - pub fn par_models(&self) -> impl ParallelIterator + '_ { - self.models.par_iter() - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_models(&self) -> impl ParallelIterator + '_ { + // self.models.par_iter() + //} /// Get an iterator of mutable references to Models making up this PDB. /// Double ended so iterating from the end is just as fast as from the start. @@ -504,10 +502,10 @@ impl<'a> PDB { } /// Get a parallel iterator of mutable references to Models making up this PDB. - #[doc_cfg(feature = "rayon")] - pub fn par_models_mut(&mut self) -> impl ParallelIterator + '_ { - self.models.par_iter_mut() - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_models_mut(&mut self) -> impl ParallelIterator + '_ { + // self.models.par_iter_mut() + //} /// Get an iterator of references to Chains making up this PDB. /// Double ended so iterating from the end is just as fast as from the start. @@ -516,10 +514,10 @@ impl<'a> PDB { } /// Get a parallel iterator of references to Chains making up this PDB. - #[doc_cfg(feature = "rayon")] - pub fn par_chains(&self) -> impl ParallelIterator + '_ { - self.par_models().flat_map(Model::par_chains) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_chains(&self) -> impl ParallelIterator + '_ { + // self.par_models().flat_map(Model::par_chains) + //} /// Get a iterator of mutable references to Chains making up this PDB. /// Double ended so iterating from the end is just as fast as from the start. @@ -528,10 +526,10 @@ impl<'a> PDB { } /// Get a parallel iterator of mutable references to Chains making up this PDB. - #[doc_cfg(feature = "rayon")] - pub fn par_chains_mut(&mut self) -> impl ParallelIterator + '_ { - self.par_models_mut().flat_map(Model::par_chains_mut) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_chains_mut(&mut self) -> impl ParallelIterator + '_ { + // self.par_models_mut().flat_map(Model::par_chains_mut) + //} /// Get an iterator of references to Residues making up this PDB. /// Double ended so iterating from the end is just as fast as from the start. @@ -540,10 +538,10 @@ impl<'a> PDB { } /// Get a parallel iterator of references to Residues making up this PDB. - #[doc_cfg(feature = "rayon")] - pub fn par_residues(&self) -> impl ParallelIterator + '_ { - self.par_models().flat_map(Model::par_residues) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_residues(&self) -> impl ParallelIterator + '_ { + // self.par_models().flat_map(Model::par_residues) + //} /// Get an iterator of mutable references to Residues making up this PDB. /// Double ended so iterating from the end is just as fast as from the start. @@ -552,10 +550,10 @@ impl<'a> PDB { } /// Get a parallel iterator of mutable references to Residues making up this PDB. - #[doc_cfg(feature = "rayon")] - pub fn par_residues_mut(&mut self) -> impl ParallelIterator + '_ { - self.par_models_mut().flat_map(Model::par_residues_mut) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_residues_mut(&mut self) -> impl ParallelIterator + '_ { + // self.par_models_mut().flat_map(Model::par_residues_mut) + //} /// Get an iterator of references to Conformers making up this PDB. /// Double ended so iterating from the end is just as fast as from the start. @@ -564,10 +562,10 @@ impl<'a> PDB { } /// Get a parallel iterator of references to Conformers making up this PDB. - #[doc_cfg(feature = "rayon")] - pub fn par_conformers(&self) -> impl ParallelIterator + '_ { - self.par_models().flat_map(Model::par_conformers) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_conformers(&self) -> impl ParallelIterator + '_ { + // self.par_models().flat_map(Model::par_conformers) + //} /// Get an iterator of mutable references to Conformers making up this PDB. /// Double ended so iterating from the end is just as fast as from the start. @@ -576,10 +574,10 @@ impl<'a> PDB { } /// Get a parallel iterator of mutable references to Conformers making up this PDB. - #[doc_cfg(feature = "rayon")] - pub fn par_conformers_mut(&mut self) -> impl ParallelIterator + '_ { - self.par_models_mut().flat_map(Model::par_conformers_mut) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_conformers_mut(&mut self) -> impl ParallelIterator + '_ { + // self.par_models_mut().flat_map(Model::par_conformers_mut) + //} /// Get an iterator of references to Atom making up this PDB. /// Double ended so iterating from the end is just as fast as from the start. @@ -588,10 +586,10 @@ impl<'a> PDB { } /// Get a parallel iterator of references to Atom making up this PDB. - #[doc_cfg(feature = "rayon")] - pub fn par_atoms(&self) -> impl ParallelIterator + '_ { - self.par_models().flat_map(Model::par_atoms) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_atoms(&self) -> impl ParallelIterator + '_ { + // self.par_models().flat_map(Model::par_atoms) + //} /// Get an iterator of mutable references to Atom making up this PDB. /// Double ended so iterating from the end is just as fast as from the start. @@ -600,10 +598,10 @@ impl<'a> PDB { } /// Get a parallel iterator of mutable references to Atom making up this PDB. - #[doc_cfg(feature = "rayon")] - pub fn par_atoms_mut(&mut self) -> impl ParallelIterator + '_ { - self.par_models_mut().flat_map(Model::par_atoms_mut) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_atoms_mut(&mut self) -> impl ParallelIterator + '_ { + // self.par_models_mut().flat_map(Model::par_atoms_mut) + //} /// Get an iterator of references to a struct containing all atoms with their hierarchy making up this PDB. pub fn atoms_with_hierarchy( @@ -628,7 +626,7 @@ impl<'a> PDB { /// As this is done in place this is the fastest way to remove Atoms from this PDB. pub fn remove_atoms_by(&mut self, predicate: F) where - F: Fn(&Atom) -> bool, + F: Fn(Ref<'_, Atom>) -> bool, { for residue in self.residues_mut() { residue.remove_atoms_by(&predicate); @@ -713,20 +711,20 @@ impl<'a> PDB { /// /// ## Arguments /// * `serial_number` - the serial number of the Model to remove - #[doc_cfg(feature = "rayon")] - pub fn par_remove_model_serial_number(&mut self, serial_number: usize) -> bool { - let index = self - .models - .par_iter() - .position_first(|a| a.serial_number() == serial_number); - - if let Some(i) = index { - self.remove_model(i); - true - } else { - false - } - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_remove_model_serial_number(&mut self, serial_number: usize) -> bool { + // let index = self + // .models + // .par_iter() + // .position_first(|a| a.serial_number() == serial_number); + // + // if let Some(i) = index { + // self.remove_model(i); + // true + // } else { + // false + // } + //} /// Remove all empty Models from this PDB, and all empty Chains from the Model, and all empty Residues from the Chains. pub fn remove_empty(&mut self) { @@ -736,11 +734,11 @@ impl<'a> PDB { /// Remove all empty Models from this PDB, and all empty Chains from the Model, and all empty Residues from the Chains. /// Done in parallel. - #[doc_cfg(feature = "rayon")] - pub fn par_remove_empty(&mut self) { - self.models.par_iter_mut().for_each(Model::remove_empty); - self.models.retain(|m| m.chain_count() > 0); - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_remove_empty(&mut self) { + // self.models.par_iter_mut().for_each(Model::remove_empty); + // self.models.retain(|m| m.chain_count() > 0); + //} /// This renumbers all numbered structs in the PDB. /// So it renumbers models, atoms, residues, chains and [`MtriX`]s. @@ -788,11 +786,11 @@ impl<'a> PDB { /// Apply a transformation to the position of all atoms making up this PDB, the new position is immediately set. /// Done in parallel. - #[doc_cfg(feature = "rayon")] - pub fn par_apply_transformation(&mut self, transformation: &TransformationMatrix) { - self.par_atoms_mut() - .for_each(|atom| atom.apply_transformation(transformation)); - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_apply_transformation(&mut self, transformation: &TransformationMatrix) { + // self.par_atoms_mut() + // .for_each(|atom| atom.apply_transformation(transformation)); + //} /// Joins two PDBs. If one has multiple models it extends the models of this PDB with the models of the other PDB. If this PDB does /// not have any models it moves the models of the other PDB to this PDB. If both have one model it moves all chains/residues/atoms @@ -816,10 +814,10 @@ impl<'a> PDB { } /// Sort the Models of this PDB in parallel. - #[doc_cfg(feature = "rayon")] - pub fn par_sort(&mut self) { - self.models.par_sort(); - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_sort(&mut self) { + // self.models.par_sort(); + //} /// Sort all structs in this PDB. pub fn full_sort(&mut self) { @@ -839,14 +837,14 @@ impl<'a> PDB { } /// Sort all structs in this PDB in parallel. - #[doc_cfg(feature = "rayon")] - pub fn par_full_sort(&mut self) { - self.par_sort(); - self.par_models_mut().for_each(Model::par_sort); - self.par_chains_mut().for_each(Chain::par_sort); - self.par_residues_mut().for_each(Residue::par_sort); - self.par_conformers_mut().for_each(Conformer::par_sort); - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_full_sort(&mut self) { + // self.par_sort(); + // self.par_models_mut().for_each(Model::par_sort); + // self.par_chains_mut().for_each(Chain::par_sort); + // self.par_residues_mut().for_each(Residue::par_sort); + // self.par_conformers_mut().for_each(Conformer::par_sort); + //} /// Create an R star tree of Atoms which can be used for fast lookup of /// spatially close atoms. See the crate rstar for documentation @@ -907,14 +905,6 @@ impl<'a> PDB { ((min[0], min[1], min[2]), (max[0], max[1], max[2])) } - /// Get the bonds in this PDB file. Runtime is `O(bonds_count * 2 * atom_count)` because it - /// has to iterate over all atoms to prevent borrowing problems. - pub fn bonds(&self) -> impl DoubleEndedIterator + '_ { - self.bonds - .iter() - .map(move |(a, b, bond)| (a.as_ref(), b.as_ref(), *bond)) - } - /// Add a bond of the given type to the list of bonds in this PDB. /// The atoms are selected by serial number and alternative location. /// It uses `binary_find_atom` in the background so the PDB should be sorted. @@ -926,19 +916,38 @@ impl<'a> PDB { atom2: (usize, Option<&str>), bond: Bond, ) -> Option<()> { - self.bonds.push(( - self.binary_find_atom(atom1.0, atom1.1)? - .conformer() - .atom_arc(atom1.0) - .map(Arc::clone) - .expect("Could not find atom 1"), - self.binary_find_atom(atom2.0, atom2.1)? - .conformer() - .atom_arc(atom2.0) - .map(Arc::clone) - .expect("Could not find atom 2"), - bond, - )); + let mut hierarchy1 = self + .binary_find_atom_mut(atom1.0, atom1.1) + .expect("Could not find Atom1"); + let conformer1 = hierarchy1.conformer_mut(); + let atom1_arc = conformer1 + .atom_arc(atom1.0) + .map(Arc::clone) + .expect("Could not find atom1 arc"); + let conformer1: *mut Conformer = conformer1; + let mut hierarchy2 = self + .binary_find_atom_mut(atom2.0, atom2.1) + .expect("Could not find Atom2"); + let conformer2 = hierarchy2.conformer_mut(); + let atom2_arc = conformer2 + .atom_arc(atom2.0) + .map(Arc::clone) + .expect("Could not find Atom2 arc"); + dbg!(atom1, atom2, bond); + unsafe { + dbg!((*conformer1).atoms_mut().collect::>()); + (*conformer1) + .atoms_mut() + .find(|a| a.serial_number() == atom1.0) // Because the underlying vec uses an Arc this will not find the actual Atom because it is borrowed in this same function before. Start to use RefCell? + .expect("Could not find atom 1 for bond") + .add_bond(atom2_arc, bond); + } + conformer2 + .atoms_mut() + .find(|a| a.serial_number() == atom2.0) + .expect("Could not find atom2 for bond") + .add_bond(atom1_arc, bond); + Some(()) } } @@ -1142,7 +1151,8 @@ mod tests { (0, None), ("MET", None), ); - let pdb = PDB::new(); + let mut pdb = PDB::new(); + pdb.add_model(model); let json = serde_json::to_string(&pdb).unwrap(); let parsed = serde_json::from_str(&json).unwrap(); @@ -1174,4 +1184,75 @@ mod tests { pdb.add_model(model); assert_eq!(((-1., -1., -1.), (2., 2., 2.)), pdb.bounding_box()); } + + #[test] + fn add_bonds() { + let mut model = Model::new(0); + model.add_atom( + Atom::new(false, 0, "A", -1.0, 0.0, 2.0, 0.0, 0.0, "", 0).unwrap(), + "A", + (0, None), + ("MET", None), + ); + model.add_atom( + Atom::new(false, 1, "B", 1.0, 2.0, -1.0, 0.0, 0.0, "", 0).unwrap(), + "A", + (0, None), + ("MET", None), + ); + model.add_atom( + Atom::new(false, 2, "C", 2.0, -1.0, 0.5, 0.0, 0.0, "", 0).unwrap(), + "A", + (0, None), + ("MET", None), + ); + let mut pdb = PDB::new(); + pdb.add_model(model); + pdb.full_sort(); + + println!("{:#?}", pdb); + assert!(pdb.binary_find_atom(0, None).is_some()); + + assert!(pdb.add_bond((0, None), (1, None), Bond::Covalent).is_some()); + assert!(pdb.add_bond((1, None), (2, None), Bond::Covalent).is_some()); + + assert_eq!( + pdb.find(Term::AtomSerialNumber(0).into()) + .next() + .unwrap() + .atom() + .bond_count(), + 1 + ); + assert_eq!( + pdb.find(Term::AtomSerialNumber(1).into()) + .next() + .unwrap() + .atom() + .bond_count(), + 2 + ); + assert_eq!( + pdb.find(Term::AtomSerialNumber(2).into()) + .next() + .unwrap() + .atom() + .bond_count(), + 1 + ); + + assert_eq!( + pdb.find(Term::AtomSerialNumber(1).into()) + .next() + .unwrap() + .atom() + .bonds() + .map(|(a, b)| (a.name().to_string(), b)) + .collect::>(), + vec![ + ("A".to_string(), Bond::Covalent), + ("C".to_string(), Bond::Covalent) + ] + ); + } } diff --git a/src/structs/residue.rs b/src/structs/residue.rs index f63262b..cd9ecdd 100644 --- a/src/structs/residue.rs +++ b/src/structs/residue.rs @@ -1,9 +1,10 @@ #![allow(dead_code)] use crate::structs::*; use crate::transformation::TransformationMatrix; -use doc_cfg::doc_cfg; -#[cfg(feature = "rayon")] -use rayon::prelude::*; +//use doc_cfg::doc_cfg; +//#[cfg(feature = "rayon")] +//use rayon::prelude::*; +use std::cell::{Ref, RefMut}; use std::cmp::Ordering; use std::fmt; @@ -115,10 +116,10 @@ impl<'a> Residue { } /// Get the number of Atoms making up this Residue in parallel. - #[doc_cfg(feature = "rayon")] - pub fn par_atom_count(&self) -> usize { - self.par_conformers().map(Conformer::atom_count).sum() - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_atom_count(&self) -> usize { + // self.par_conformers().map(Conformer::atom_count).sum() + //} /// Get a reference to a specific Conformer from the list of Conformers making up this Residue. /// @@ -149,7 +150,7 @@ impl<'a> Residue { /// /// ## Fails /// Returns `None` if the index is out of bounds. - pub fn atom(&self, index: usize) -> Option<&Atom> { + pub fn atom(&self, index: usize) -> Option> { self.atoms().nth(index) } @@ -160,12 +161,12 @@ impl<'a> Residue { /// /// ## Fails /// Returns `None` if the index is out of bounds. - pub fn atom_mut(&mut self, index: usize) -> Option<&mut Atom> { + pub fn atom_mut(&mut self, index: usize) -> Option> { self.atoms_mut().nth(index) } /// Get A reference to the specified Atom. Its uniqueness is guaranteed by including the - /// `insertion_code`, with its full hierarchy. The algorithm is based + /// `alternative_location`, with its full hierarchy. The algorithm is based /// on binary search so it is faster than an exhaustive search, but the /// full structure is assumed to be sorted. This assumption can be enforced /// by using `pdb.full_sort()`. @@ -265,10 +266,10 @@ impl<'a> Residue { } /// Get a parallel iterator of references to Conformers making up this Model. - #[doc_cfg(feature = "rayon")] - pub fn par_conformers(&self) -> impl ParallelIterator + '_ { - self.conformers.par_iter() - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_conformers(&self) -> impl ParallelIterator + '_ { + // self.conformers.par_iter() + //} /// Get an iterator of mutable references to Conformers making up this Model. /// Double ended so iterating from the end is just as fast as from the start. @@ -277,34 +278,34 @@ impl<'a> Residue { } /// Get a parallel iterator of mutable references to Conformers making up this Model. - #[doc_cfg(feature = "rayon")] - pub fn par_conformers_mut(&mut self) -> impl ParallelIterator + '_ { - self.conformers.par_iter_mut() - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_conformers_mut(&mut self) -> impl ParallelIterator + '_ { + // self.conformers.par_iter_mut() + //} /// Get an iterator of references to Atoms making up this Model. /// Double ended so iterating from the end is just as fast as from the start. - pub fn atoms(&self) -> impl DoubleEndedIterator + '_ { + pub fn atoms(&self) -> impl DoubleEndedIterator> + '_ { self.conformers().flat_map(Conformer::atoms) } /// Get a parallel iterator of references to Atoms making up this Model. - #[doc_cfg(feature = "rayon")] - pub fn par_atoms(&self) -> impl ParallelIterator + '_ { - self.par_conformers().flat_map(Conformer::par_atoms) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_atoms(&self) -> impl ParallelIterator + '_ { + // self.par_conformers().flat_map(Conformer::par_atoms) + //} /// Get an iterator of mutable references to Atoms making up this Model. /// Double ended so iterating from the end is just as fast as from the start. - pub fn atoms_mut(&mut self) -> impl DoubleEndedIterator + '_ { + pub fn atoms_mut(&mut self) -> impl DoubleEndedIterator> + '_ { self.conformers_mut().flat_map(Conformer::atoms_mut) } /// Get a parallel iterator of mutable references to Atoms making up this Model. - #[doc_cfg(feature = "rayon")] - pub fn par_atoms_mut(&mut self) -> impl ParallelIterator + '_ { - self.par_conformers_mut().flat_map(Conformer::par_atoms_mut) - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_atoms_mut(&mut self) -> impl ParallelIterator + '_ { + // self.par_conformers_mut().flat_map(Conformer::par_atoms_mut) + //} /// Get an iterator of references to a struct containing all atoms with their hierarchy making up this Model. pub fn atoms_with_hierarchy( @@ -385,7 +386,7 @@ impl<'a> Residue { /// Remove all atoms matching the given predicate. As this is done in place this is the fastest way to remove atoms from this Residue. pub fn remove_atoms_by(&mut self, predicate: F) where - F: Fn(&Atom) -> bool, + F: Fn(Ref<'_, Atom>) -> bool, { // for conformer in self.conformers_mut() { // conformer.remove_atoms_by(&predicate); @@ -434,17 +435,17 @@ impl<'a> Residue { /// /// ## Panics /// Panics when the index is outside bounds. - #[doc_cfg(feature = "rayon")] - pub fn par_remove_conformer_by_id(&mut self, id: (&str, Option<&str>)) -> bool { - let index = self.conformers.par_iter().position_first(|a| a.id() == id); - - if let Some(i) = index { - self.remove_conformer(i); - true - } else { - false - } - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_remove_conformer_by_id(&mut self, id: (&str, Option<&str>)) -> bool { + // let index = self.conformers.par_iter().position_first(|a| a.id() == id); + // + // if let Some(i) = index { + // self.remove_conformer(i); + // true + // } else { + // false + // } + //} /// Apply a transformation to the position of all Conformers making up this Residue, the new position is immediately set. pub fn apply_transformation(&mut self, transformation: &TransformationMatrix) { @@ -455,11 +456,11 @@ impl<'a> Residue { /// Apply a transformation to the position of all Conformers making up this Residue, the new position is immediately set. /// Done in parallel - #[doc_cfg(feature = "rayon")] - pub fn par_apply_transformation(&mut self, transformation: &TransformationMatrix) { - self.par_conformers_mut() - .for_each(|conformer| conformer.apply_transformation(transformation)); - } + //#[doc_cfg(feature = "rayon")] + //pub fn par_apply_transformation(&mut self, transformation: &TransformationMatrix) { + // self.par_conformers_mut() + // .for_each(|conformer| conformer.apply_transformation(transformation)); + //} /// Join this Residue with another Residue, this moves all Conformers from the other Residue /// to this Residue. All other (meta) data of this Residue will stay the same. @@ -472,11 +473,11 @@ impl<'a> Residue { self.conformers.sort(); } - /// Sort the Conformers of this Residue in parallel - #[doc_cfg(feature = "rayon")] - pub fn par_sort(&mut self) { - self.conformers.par_sort(); - } + // Sort the Conformers of this Residue in parallel + //#[doc_cfg(feature = "rayon")] + //pub fn par_sort(&mut self) { + // self.conformers.par_sort(); + //} } impl fmt::Display for Residue { diff --git a/src/structs/search.rs b/src/structs/search.rs index 8330d79..22c5b39 100644 --- a/src/structs/search.rs +++ b/src/structs/search.rs @@ -122,6 +122,12 @@ impl Term { } } +impl From for Search { + fn from(t: Term) -> Self { + Self::Single(t) + } +} + /// A collection of multiple search [Term]s in the search for (an) atom(s) in a PDB. /// You can use bitwise and (`&`), or (`|`), and xor (`^`) to chain a search. /// In the same way you can use not `!` to negate a search term.