Source code for aquaduct.traj.selections
# -*- coding: utf-8 -*-
# Aqua-Duct, a tool facilitating analysis of the flow of solvent molecules in molecular dynamic simulations
# Copyright (C) 2016-2017 Tomasz Magdziarz, Alicja Płuciennik, Michał Stolarczyk <info@aquaduct.pl>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
logger = logging.getLogger(__name__)
from itertools import izip_longest
import numpy as np
import MDAnalysis as mda
from aquaduct.geom.convexhull import SciPyConvexHull
from aquaduct.geom.convexhull import is_point_within_convexhull
from aquaduct.utils.helpers import int2range, are_rows_uniq
from aquaduct.utils.maths import make_default_array
[docs]class Selection(object):
"""
def __init__(self,selection,selection_string=None):
self.selection_object = selection
self.selection_string = selection_string
"""
[docs] def center_of_mass(self):
# should return numpy (3,) array
raise NotImplementedError("This is abstract class. Missing implementation in a child class.")
[docs] def iterate_over_residues(self):
# should iterate over residues in the selection returning object of the same type
raise NotImplementedError("This is abstract class. Missing implementation in a child class.")
[docs] def unique_resids(self):
# should return array of resids
raise NotImplementedError("This is abstract class. Missing implementation in a child class.")
[docs] def unique_resids_number(self):
return len(self.unique_resids(ikwid=True))
[docs] def atom_positions(self):
# should return numpy (x,3) array
raise NotImplementedError("This is abstract class. Missing implementation in a child class.")
[docs] def center_of_mass_of_residues(self):
# should resturn list of lists or generator of center of masses
return (R.center_of_mass().tolist() for R in self.iterate_over_residues())
[docs] def get_convexhull_of_atom_positions(self):
# should return modified ConvexHull object
return SciPyConvexHull(self.atom_positions())
[docs] def contains_residues(self,other,convex_hull=False,map_fun=None):
# Checks if this selection contains other.
# If convex_hull is False it checks if residues of this selection matches to other.
# If convex_hull is True it checks if residues of other selection are within convex hull of this.
# Returns list of bool values.
# Parameter map_fun is optional map function. If it is None, regular map will be used.
if map_fun is None:
map_fun = map
if convex_hull:
other_coords = list(other.center_of_mass_of_residues())
chull = self.get_convexhull_of_atom_positions()
return map_fun(is_point_within_convexhull, izip_longest(other_coords, [], fillvalue=chull))
else:
# check if other selection is empty
if other.unique_resids_number() == 0:
return []
this_uids = self.unique_resids(ikwid=True)
return [res_other.unique_resids(ikwid=True) in this_uids for res_other in other.iterate_over_residues()]
[docs] def containing_residues(self,other,*args,**kwargs):
# Convienience wrapper for contains_residues.
#def get_res_in_scope(is_res_in_scope, res):
other_new = None
for iris, other_residue in zip(self.contains_residues(other, *args, **kwargs), other.iterate_over_residues()):
if iris:
if other_new is None:
other_new = other_residue
else:
other_new += other_residue
return other_new
[docs] def uniquify(self):
# should change selection to unique atoms only
raise NotImplementedError("This is abstract class. Missing implementation in a child class.")
[docs] def __add__(self, other):
raise NotImplementedError("This is abstract class. Missing implementation in a child class.")
[docs] def first_resid(self):
return self.resids.tolist()[0]
# TODO: decide if methods should be properties or not
[docs]class SelectionMDA(mda.core.AtomGroup.AtomGroup,
Selection):
[docs] def iterate_over_residues(self):
return (self.__class__(R) for R in self.residues)
[docs] def unique_resids(self, ikwid=False):
# TODO: do something with this method!
assert ikwid, "This causes bugs! Avoid this method or take special care in using its results. If you want to use it pass additional variable ikwid = True."
return np.unique(self.resids)
[docs] def atom_positions(self):
# check if positions are correct
if len(self.atoms):
positions = self.positions
if not are_rows_uniq(positions):
logger.warning('Some atoms have the same positions, your data might me corrupted.')
return make_default_array(positions)
logger.warning('Selection comprises no atoms, check your settings.')
return make_default_array([])
[docs] def __add__(self, other):
if other is not None:
return SelectionMDA(self._atoms + other._atoms)
return SelectionMDA(self._atoms)
[docs] def uniquify(self):
self.__init__(mda.core.AtomGroup.AtomGroup(set(self._atoms)))
[docs]class CompactSelectionMDA(object):
[docs] def __init__(self, sMDA):
self.indices = map(lambda x: x + 1, map(int, sMDA.indices))
[docs] def toSelectionMDA(self, reader):
sMDA = None
for pr in int2range(self.indices).split():
if sMDA is None:
sMDA = reader.parse_selection('bynum ' + pr)
else:
sMDA += reader.parse_selection('bynum ' + pr)
return sMDA