Skip to content

Commit

Permalink
Merge branch 'develop' into get_connections_develop
Browse files Browse the repository at this point in the history
  • Loading branch information
jbarnoud authored May 2, 2021
2 parents 0eacf67 + fe22dc3 commit c7997b5
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 100 deletions.
1 change: 1 addition & 0 deletions package/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ Enhancements
* Added intra_bonds, intra_angles, intra_dihedrals etc. to return only
the connections involving atoms within the AtomGroup, instead of
including atoms outside the AtomGroup (Issue #1264, #2821, PR #3200)
* Added del_TopologyAttr function (PR #3069)
* Switch GNMAnalysis to AnalysisBase (Issue #3243)
* Adds python 3.9 support (Issue #2974, PR #3027, #3245)
* Added an MDAnalysis shields.io badge to the README (Issue #3227, PR #3229)
Expand Down
17 changes: 17 additions & 0 deletions package/MDAnalysis/core/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
import itertools
import numbers
import os
import contextlib
import warnings

from .. import (_CONVERTERS,
Expand Down Expand Up @@ -255,6 +256,22 @@ def setter(self, values):
property(getter, setter, None, attr.singledoc))
cls._SETATTR_WHITELIST.add(attr.singular)

@classmethod
def _del_prop(cls, attr):
"""Remove `attr` from the namespace for this class.
Parameters
----------
attr : A :class:`TopologyAttr` object
"""
with contextlib.suppress(AttributeError):
delattr(cls, attr.attrname)
with contextlib.suppress(AttributeError):
delattr(cls, attr.singular)

cls._SETATTR_WHITELIST.discard(attr.attrname)
cls._SETATTR_WHITELIST.discard(attr.singular)

def __setattr__(self, attr, value):
# `ag.this = 42` calls setattr(ag, 'this', 42)
if not (attr.startswith('_') or # 'private' allowed
Expand Down
17 changes: 17 additions & 0 deletions package/MDAnalysis/core/topology.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
.. autofunction:: make_downshift_arrays
"""
import contextlib

import numpy as np

from .topologyattrs import Atomindices, Resindices, Segindices
Expand Down Expand Up @@ -516,6 +518,21 @@ def add_TopologyAttr(self, topologyattr):
topologyattr.top = self
self.__setattr__(topologyattr.attrname, topologyattr)

def del_TopologyAttr(self, topologyattr):
"""Remove a TopologyAttr from the Topology.
If it is not present, nothing happens.
Parameters
----------
topologyattr : TopologyAttr
.. versionadded:: 2.0.0
"""
self.__delattr__(topologyattr.attrname)
self.attrs.remove(topologyattr)

@property
def guessed_attributes(self):
"""A list of the guessed attributes in this topology"""
Expand Down
101 changes: 16 additions & 85 deletions package/MDAnalysis/core/topologyobjects.py
Original file line number Diff line number Diff line change
Expand Up @@ -899,21 +899,15 @@ def values(self, **kwargs):
elif self.btype == 'improper':
return self.dihedrals(**kwargs)

def _bondsSlow(self, pbc=False): # pragma: no cover
"""Slow version of bond (numpy implementation)"""
if not self.btype == 'bond':
return TypeError("TopologyGroup is not of type 'bond'")
else:
bond_dist = self._ags[0].positions - self._ags[1].positions
if pbc:
box = self._ags[0].dimensions
# orthogonal and divide by zero check
if (box[6:9] == 90.).all() and not (box[0:3] == 0).any():
bond_dist -= np.rint(bond_dist / box[0:3]) * box[0:3]
else:
raise ValueError("Only orthogonal boxes supported")

return np.array([mdamath.norm(a) for a in bond_dist])
def _calc_connection_values(self, func, *btypes, result=None, pbc=False):
if not any(self.btype == btype for btype in btypes):
strbtype = "' or '".join(btypes)
raise TypeError(f"TopologyGroup is not of type '{strbtype}'")
if not result:
result = np.zeros(len(self), np.float64)
box = None if not pbc else self._ags[0].dimensions
positions = [ag.positions for ag in self._ags]
return func(*positions, box=box, result=result)

def bonds(self, pbc=False, result=None):
"""Calculates the distance between all bonds in this TopologyGroup
Expand All @@ -928,30 +922,8 @@ def bonds(self, pbc=False, result=None):
Uses cython implementation
"""
if not self.btype == 'bond':
raise TypeError("TopologyGroup is not of type 'bond'")
if not result:
result = np.zeros(len(self), np.float64)
if pbc:
return distances.calc_bonds(self._ags[0].positions,
self._ags[1].positions,
box=self._ags[0].dimensions,
result=result)
else:
return distances.calc_bonds(self._ags[0].positions,
self._ags[1].positions,
result=result)

def _anglesSlow(self): # pragma: no cover
"""Slow version of angle (numpy implementation)"""
if not self.btype == 'angle':
raise TypeError("TopologyGroup is not of type 'angle'")

vec1 = self._ags[0].positions - self._ags[1].positions
vec2 = self._ags[2].positions - self._ags[1].positions

angles = np.array([mdamath.angle(a, b) for a, b in zip(vec1, vec2)])
return angles
return self._calc_connection_values(distances.calc_bonds, "bond",
pbc=pbc, result=result)

def angles(self, result=None, pbc=False):
"""Calculates the angle in radians formed between a bond
Expand All @@ -975,34 +947,8 @@ def angles(self, result=None, pbc=False):
Added *pbc* option (default ``False``)
"""
if not self.btype == 'angle':
raise TypeError("TopologyGroup is not of type 'angle'")
if not result:
result = np.zeros(len(self), np.float64)
if pbc:
return distances.calc_angles(self._ags[0].positions,
self._ags[1].positions,
self._ags[2].positions,
box=self._ags[0].dimensions,
result=result)
else:
return distances.calc_angles(self._ags[0].positions,
self._ags[1].positions,
self._ags[2].positions,
result=result)

def _dihedralsSlow(self): # pragma: no cover
"""Slow version of dihedral (numpy implementation)"""
if self.btype not in ['dihedral', 'improper']:
raise TypeError("TopologyGroup is not of type 'dihedral' or "
"'improper'")

ab = self._ags[0].positions - self._ags[1].positions
bc = self._ags[1].positions - self._ags[2].positions
cd = self._ags[2].positions - self._ags[3].positions

return np.array([mdamath.dihedral(a, b, c)
for a, b, c in zip(ab, bc, cd)])
return self._calc_connection_values(distances.calc_angles, "angle",
pbc=pbc, result=result)

def dihedrals(self, result=None, pbc=False):
"""Calculate the dihedral angle in radians for this topology
Expand All @@ -1028,21 +974,6 @@ def dihedrals(self, result=None, pbc=False):
.. versionchanged:: 0.9.0
Added *pbc* option (default ``False``)
"""
if self.btype not in ['dihedral', 'improper']:
raise TypeError("TopologyGroup is not of type 'dihedral' or "
"'improper'")
if not result:
result = np.zeros(len(self), np.float64)
if pbc:
return distances.calc_dihedrals(self._ags[0].positions,
self._ags[1].positions,
self._ags[2].positions,
self._ags[3].positions,
box=self._ags[0].dimensions,
result=result)
else:
return distances.calc_dihedrals(self._ags[0].positions,
self._ags[1].positions,
self._ags[2].positions,
self._ags[3].positions,
result=result)
return self._calc_connection_values(distances.calc_dihedrals,
"dihedral", "improper",
pbc=pbc, result=result)
75 changes: 75 additions & 0 deletions package/MDAnalysis/core/universe.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import logging
import copy
import warnings
import contextlib
import collections

import MDAnalysis
Expand Down Expand Up @@ -786,6 +787,62 @@ def add_TopologyAttr(self, topologyattr, values=None):
self._topology.add_TopologyAttr(topologyattr)
self._process_attr(topologyattr)

def del_TopologyAttr(self, topologyattr):
"""Remove a topology attribute from the Universe
Removing a TopologyAttribute from the Universe makes it unavailable to
all AtomGroups etc throughout the Universe.
Parameters
----------
topologyattr: TopologyAttr or string
Either a MDAnalysis TopologyAttr object or the name of a possible
topology attribute.
Example
-------
For example to remove bfactors to a Universe:
>>> u.del_TopologyAttr('bfactors')
>>> hasattr(u.atoms[:3], 'bfactors')
False
.. versionadded:: 2.0.0
"""

if not isinstance(topologyattr, str):
try:
topologyattr = topologyattr.attrname
except AttributeError:
# either TopologyGroup or not valid
try:
# this may not end well
# e.g. matrix -> matrices
topologyattr = topologyattr.btype + "s"
except AttributeError:
raise ValueError("Topology attribute must be str or "
"TopologyAttr object or class. "
f"Given: {type(topologyattr)}") from None

try:
topologyattr = _TOPOLOGY_ATTRS[topologyattr].attrname
except KeyError:
attrs = ', '.join(sorted(_TOPOLOGY_ATTRS))
errmsg = (f"Unrecognised topology attribute: '{topologyattr}'."
f" Possible values: '{attrs}'\n"
"To raise an issue go to: "
"https://github.com/MDAnalysis/mdanalysis/issues")
raise ValueError(errmsg) from None

try:
topattr = getattr(self._topology, topologyattr)
except AttributeError:
raise ValueError(f"Topology attribute {topologyattr} "
"not in Universe.") from None
self._topology.del_TopologyAttr(topattr)
self._unprocess_attr(topattr)

def _process_attr(self, attr):
"""Squeeze a topologyattr for its information
Expand Down Expand Up @@ -820,6 +877,24 @@ def _process_attr(self, attr):
for funcname, meth in attr.transplants['Universe']:
setattr(self.__class__, funcname, meth)

def _unprocess_attr(self, attr):
"""
Undo all the stuff in _process_attr.
If the topology attribute is not present, nothing happens
(silent fail).
"""
for cls in attr.target_classes:
self._class_bases[cls]._del_prop(attr)

# Universe transplants
for funcname, _ in attr.transplants.pop("Universe", []):
delattr(self.__class__, funcname)
# Group transplants
for cls, transplants in attr.transplants.items():
for funcname, _ in transplants:
delattr(self._class_bases[cls], funcname)

def add_Residue(self, segment=None, **attrs):
"""Add a new Residue to this Universe
Expand Down
16 changes: 10 additions & 6 deletions package/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,15 +610,19 @@ def long_description(readme):
'trajectories generated by CHARMM, Gromacs, NAMD, LAMMPS, or Amber.'),
long_description=LONG_DESCRIPTION,
long_description_content_type='text/x-rst',
author='Naveen Michaud-Agrawal',
author_email='naveen.michaudagrawal@gmail.com',
maintainer='Richard Gowers',
maintainer_email='mdnalysis-discussion@googlegroups.com',
author='MDAnalysis Development Team',
author_email='mdanalysis@numfocus.org',
maintainer='MDAnalysis Core Developers',
maintainer_email='mdanalysis@numfocus.org',
url='https://www.mdanalysis.org',
download_url='https://github.com/MDAnalysis/mdanalysis/releases',
project_urls={'Documentation': 'https://www.mdanalysis.org/docs/',
project_urls={'Documentation': 'https://docs.mdanalysis.org/',
'User Guide': 'https://userguide.mdanalysis.org/',
'Issue Tracker': 'https://github.com/mdanalysis/mdanalysis/issues',
'User Group': 'https://groups.google.com/forum/#!forum/mdnalysis-discussion',
'User Group': 'https://groups.google.com/g/mdnalysis-discussion/',
'Discord': 'https://discord.com/channels/807348386012987462/',
'Blog': 'https://www.mdanalysis.org/blog/',
'Twitter': 'https://twitter.com/mdanalysis',
'Source': 'https://github.com/mdanalysis/mdanalysis',
},
license='GPL 2',
Expand Down
Loading

0 comments on commit c7997b5

Please sign in to comment.