Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions package/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ Enhancements
* Read TPR files from Gromacs 2020 (Issue #2412 and #2428)
* Added analysis.align.AverageStructure to get the average structure of an
out-of-memory trajectory (Issue #2039)
* Added _add_TopologyObjects, _delete_TopologyObjects, and public convenience
methods to Universe. Added type and order checking to _Connection
topologyattrs. (PR #2382)

Changes
* The fasta2select now always assumes that the gap character in a sequence
Expand Down
6 changes: 3 additions & 3 deletions package/MDAnalysis/core/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -2950,15 +2950,15 @@ def get_TopAttr(u, name, cls):
box = self.dimensions if self.dimensions.all() else None
b = guess_bonds(self.atoms, self.atoms.positions, vdwradii=vdwradii, box=box)
bondattr = get_TopAttr(self.universe, 'bonds', Bonds)
bondattr.add_bonds(b, guessed=True)
bondattr._add_bonds(b, guessed=True)

a = guess_angles(self.bonds)
angleattr = get_TopAttr(self.universe, 'angles', Angles)
angleattr.add_bonds(a, guessed=True)
angleattr._add_bonds(a, guessed=True)

d = guess_dihedrals(self.angles)
diheattr = get_TopAttr(self.universe, 'dihedrals', Dihedrals)
diheattr.add_bonds(d)
diheattr._add_bonds(d)

@property
def bond(self):
Expand Down
1 change: 1 addition & 0 deletions package/MDAnalysis/core/topology.py
Original file line number Diff line number Diff line change
Expand Up @@ -583,3 +583,4 @@ def add_Segment(self, **new_attrs):
attr.values = np.concatenate([attr.values, np.array([newval])])

return segidx

79 changes: 64 additions & 15 deletions package/MDAnalysis/core/topologyattrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1643,17 +1643,42 @@ def _get_named_segment(group, segid):
transplants[SegmentGroup].append(
('_get_named_segment', _get_named_segment))

def _check_connection_values(func):
"""
Checks values passed to _Connection methods for:
- appropriate number of atom indices
- coerces them to tuples of ints (for hashing)
- ensures that first value is less than last (reversibility & hashing)

class _Connection(AtomAttr):
"""Base class for connectivity between atoms"""
def __init__(self, values, types=None, guessed=False, order=None):
values = [tuple(x) for x in values]
.. versionadded:: 0.21.0

"""
@functools.wraps(func)
def wrapper(self, values, *args, **kwargs):
if not all(len(x) == self._n_atoms
and all(isinstance(y, (int, np.integer)) for y in x)
for x in values):
and all(isinstance(y, (int, np.integer)) for y in x)
for x in values):
raise ValueError(("{} must be an iterable of tuples with {}"
" atom indices").format(self.attrname,
self._n_atoms))
" atom indices").format(self.attrname,
self._n_atoms))
clean = []
for v in values:
if v[0] > v[-1]:
v = v[::-1]
clean.append(tuple(v))

return func(self, clean, *args, **kwargs)
return wrapper

class _Connection(AtomAttr):
"""Base class for connectivity between atoms

.. versionchanged:: 0.21.0
Added type checking to atom index values.
"""

@_check_connection_values
def __init__(self, values, types=None, guessed=False, order=None):
self.values = values
if types is None:
types = [None] * len(values)
Expand Down Expand Up @@ -1686,12 +1711,6 @@ def _bondDict(self):

for b, t, g, o in zip(self.values, self.types,
self._guessed, self.order):
# We always want the first index
# to be less than the last
# eg (0, 1) not (1, 0)
# and (4, 10, 8) not (8, 10, 4)
if b[0] > b[-1]:
b = b[::-1]
for a in b:
bd[a].append((b, t, g, o))
return bd
Expand All @@ -1718,7 +1737,8 @@ def get_atoms(self, ag):
guessed,
order)

def add_bonds(self, values, types=None, guessed=True, order=None):
@_check_connection_values
def _add_bonds(self, values, types=None, guessed=True, order=None):
if types is None:
types = itertools.cycle((None,))
if guessed in (True, False):
Expand All @@ -1738,7 +1758,35 @@ def add_bonds(self, values, types=None, guessed=True, order=None):
del self._cache['bd']
except KeyError:
pass

@_check_connection_values
def _delete_bonds(self, values):
"""
.. versionadded:: 0.21.0
"""

to_check = set(values)
self_values = set(self.values)
if not to_check.issubset(self_values):
missing = to_check-self_values
indices = ', '.join(map(str, missing))
raise ValueError(('Cannot delete nonexistent '
'{attrname} with atom indices:'
'{indices}').format(attrname=self.attrname,
indices=indices))
idx = [self.values.index(v) for v in to_check]
for i in sorted(idx, reverse=True):
del self.values[i]

for attr in ('types', '_guessed', 'order'):
arr = np.array(getattr(self, attr), dtype='object')
new = np.delete(arr, idx)
setattr(self, attr, list(new))
# kill the old cache of bond Dict
try:
del self._cache['bd']
except KeyError:
pass

class Bonds(_Connection):
"""Bonds between two atoms
Expand Down Expand Up @@ -1921,3 +1969,4 @@ class Impropers(_Connection):
singular = 'impropers'
transplants = defaultdict(list)
_n_atoms = 4

1 change: 1 addition & 0 deletions package/MDAnalysis/core/topologyobjects.py
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,7 @@ def __init__(self, bondidx, universe, btype=None, type=None, guessed=None,
raise ValueError("Unsupported btype, use one of '{}'"
"".format(', '.join(_BTYPE_TO_SHAPE)))

bondidx = np.asarray(bondidx)
nbonds = len(bondidx)
# remove duplicate bonds
if type is None:
Expand Down
Loading