Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
109 changes: 33 additions & 76 deletions package/MDAnalysis/core/topologyattrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,18 +247,14 @@ def stub_method(self, *args, **kwargs):
)
raise NoDataError(message)

annotation = textwrap.dedent(
"""\
annotation = textwrap.dedent("""\
.. note::

This requires the underlying topology to have {}. Otherwise, a
:exc:`~MDAnalysis.exceptions.NoDataError` is raised.


""".format(
attribute_name
)
)
""".format(attribute_name))
# The original docstring is assumed to be formatted with black
# (i.e., *all text* lines are already indented, the first line
# is a triple quote followed by newline)
Expand Down Expand Up @@ -396,9 +392,7 @@ def __init__(cls, name, bases, classdict):
if dtype is not None:
per_obj = classdict.get("per_object", bases[0].per_object)
try:
selection.gen_selection_class(
singular, attrname, dtype, per_obj
)
selection.gen_selection_class(singular, attrname, dtype, per_obj)
except ValueError:
msg = (
"A selection keyword could not be "
Expand Down Expand Up @@ -476,9 +470,7 @@ def _gen_initial_values(n_atoms, n_residues, n_segments):
raise NotImplementedError("No default values")

@classmethod
def from_blank(
cls, n_atoms=None, n_residues=None, n_segments=None, values=None
):
def from_blank(cls, n_atoms=None, n_residues=None, n_segments=None, values=None):
"""Create a blank version of this TopologyAttribute

Parameters
Expand Down Expand Up @@ -829,9 +821,7 @@ def _set_X(self, ag, values):
newnames.append(val)
newidx[i] = nextidx

self.nmidx[ag.ix] = (
newidx # newidx either single value or same size array
)
self.nmidx[ag.ix] = newidx # newidx either single value or same size array
if newnames:
self.name_lookup = np.concatenate([self.name_lookup, newnames])
self.values = self.name_lookup[self.nmidx]
Expand Down Expand Up @@ -954,8 +944,7 @@ def phi_selections(residues, c_name="C", n_name="N", ca_name="CA"):

keep_prev = [sum(r.atoms.names == c_name) == 1 for r in prev]
keep_res = [
all(sum(r.atoms.names == n) == 1 for n in ncac_names)
for r in residues
all(sum(r.atoms.names == n) == 1 for n in ncac_names) for r in residues
]
keep = np.array(keep_prev) & np.array(keep_res)
keep[invalid] = False
Expand Down Expand Up @@ -1146,8 +1135,7 @@ def psi_selections(residues, c_name="C", n_name="N", ca_name="CA"):

keep_nxt = [sum(r.atoms.names == n_name) == 1 for r in nxt]
keep_res = [
all(sum(r.atoms.names == n) == 1 for n in ncac_names)
for r in residues
all(sum(r.atoms.names == n) == 1 for n in ncac_names) for r in residues
]
keep = np.array(keep_nxt) & np.array(keep_res)
nxt = nxt[keep]
Expand Down Expand Up @@ -1259,12 +1247,9 @@ def omega_selections(residues, c_name="C", n_name="N", ca_name="CA"):

nxtatoms = [ca_name, n_name]
resatoms = [ca_name, c_name]
keep_nxt = [
all(sum(r.atoms.names == n) == 1 for n in nxtatoms) for r in nxt
]
keep_nxt = [all(sum(r.atoms.names == n) == 1 for n in nxtatoms) for r in nxt]
keep_res = [
all(sum(r.atoms.names == n) == 1 for n in resatoms)
for r in residues
all(sum(r.atoms.names == n) == 1 for n in resatoms) for r in residues
]
keep = np.array(keep_nxt) & np.array(keep_res)
nxt = nxt[keep]
Expand Down Expand Up @@ -1390,6 +1375,7 @@ class Atomtypes(AtomStringAttr):
singular = "type"
per_object = "atom"
dtype = object
missing_value_label = ""


# TODO: update docs to property doc
Expand All @@ -1399,6 +1385,7 @@ class Elements(AtomStringAttr):
attrname = "elements"
singular = "element"
dtype = object
missing_value_label = ""

@staticmethod
def _gen_initial_values(na, nr, ns):
Expand Down Expand Up @@ -1766,13 +1753,9 @@ def moment_of_inertia(group, wrap=False, unwrap=False, compound="group"):
"""
atomgroup = group.atoms

com = atomgroup.center_of_mass(
wrap=wrap, unwrap=unwrap, compound=compound
)
com = atomgroup.center_of_mass(wrap=wrap, unwrap=unwrap, compound=compound)
if compound != "group":
com = (com * group.masses[:, None]).sum(
axis=0
) / group.masses.sum()
com = (com * group.masses[:, None]).sum(axis=0) / group.masses.sum()

if wrap:
pos = atomgroup.pack_into_box(inplace=False) - com
Expand Down Expand Up @@ -1903,9 +1886,7 @@ def _gyration(recenteredpos, masses):
atomgroup = group.atoms
masses = atomgroup.masses

com = atomgroup.center_of_mass(
wrap=wrap, unwrap=unwrap, compound=compound
)
com = atomgroup.center_of_mass(wrap=wrap, unwrap=unwrap, compound=compound)

if compound == "group":
if wrap:
Expand All @@ -1923,7 +1904,7 @@ def _gyration(recenteredpos, masses):
recenteredpos = atomgroup.positions - com
eig_vals = _gyration(recenteredpos, masses)
else:
(atom_masks, compound_masks, n_compounds) = (
atom_masks, compound_masks, n_compounds = (
atomgroup._split_by_compound_indices(compound)
)

Expand Down Expand Up @@ -2040,8 +2021,7 @@ def asphericity(group, wrap=False, unwrap=False, compound="group"):
)
else:
shape = (3.0 / 2.0) * (
np.sum((eig_vals - np.mean(eig_vals)) ** 2)
/ np.sum(eig_vals) ** 2
np.sum((eig_vals - np.mean(eig_vals)) ** 2) / np.sum(eig_vals) ** 2
)

return shape
Expand Down Expand Up @@ -2125,9 +2105,7 @@ def align_principal_axis(group, axis, vector):
# print "axis = %r, angle = %f deg" % (ax, angle)
return group.rotateby(angle, ax)

transplants[GroupBase].append(
("align_principal_axis", align_principal_axis)
)
transplants[GroupBase].append(("align_principal_axis", align_principal_axis))


# TODO: update docs to property doc
Expand Down Expand Up @@ -2284,9 +2262,7 @@ def total_charge(group, compound="group"):
@warn_if_not_unique
@_pbc_to_wrap
@check_wrap_and_unwrap
def dipole_vector(
group, wrap=False, unwrap=False, compound="group", center="mass"
):
def dipole_vector(group, wrap=False, unwrap=False, compound="group", center="mass"):
r"""Dipole vector of the group.

.. math::
Expand Down Expand Up @@ -2352,9 +2328,7 @@ def dipole_vector(

if center == "mass":
masses = atomgroup.masses
ref = atomgroup.center_of_mass(
wrap=wrap, unwrap=unwrap, compound=compound
)
ref = atomgroup.center_of_mass(wrap=wrap, unwrap=unwrap, compound=compound)
elif center == "charge":
ref = atomgroup.center_of_charge(
wrap=wrap, unwrap=unwrap, compound=compound
Expand All @@ -2380,11 +2354,9 @@ def dipole_vector(
)
else:
recenteredpos = atomgroup.positions - ref
dipole_vector = np.einsum(
"ij,ij->j", recenteredpos, charges[:, np.newaxis]
)
dipole_vector = np.einsum("ij,ij->j", recenteredpos, charges[:, np.newaxis])
else:
(atom_masks, compound_masks, n_compounds) = (
atom_masks, compound_masks, n_compounds = (
atomgroup._split_by_compound_indices(compound)
)

Expand Down Expand Up @@ -2466,13 +2438,9 @@ def dipole_moment(group, **kwargs):
dipole_vector = atomgroup.dipole_vector(**kwargs)

if len(dipole_vector.shape) > 1:
dipole_moment = np.sqrt(
np.einsum("ij,ij->i", dipole_vector, dipole_vector)
)
dipole_moment = np.sqrt(np.einsum("ij,ij->i", dipole_vector, dipole_vector))
else:
dipole_moment = np.sqrt(
np.einsum("i,i->", dipole_vector, dipole_vector)
)
dipole_moment = np.sqrt(np.einsum("i,i->", dipole_vector, dipole_vector))

return dipole_moment

Expand Down Expand Up @@ -2565,9 +2533,7 @@ def __quadrupole_tensor(recenteredpos, charges):

if center == "mass":
masses = atomgroup.masses
ref = atomgroup.center_of_mass(
wrap=wrap, unwrap=unwrap, compound=compound
)
ref = atomgroup.center_of_mass(wrap=wrap, unwrap=unwrap, compound=compound)
elif center == "charge":
ref = atomgroup.center_of_charge(
wrap=wrap, unwrap=unwrap, compound=compound
Expand Down Expand Up @@ -2595,7 +2561,7 @@ def __quadrupole_tensor(recenteredpos, charges):
recenteredpos = atomgroup.positions - ref
quad_tensor = __quadrupole_tensor(recenteredpos, charges)
else:
(atom_masks, compound_masks, n_compounds) = (
atom_masks, compound_masks, n_compounds = (
atomgroup._split_by_compound_indices(compound)
)

Expand Down Expand Up @@ -2685,9 +2651,7 @@ def __quadrupole_moment(tensor):
if len(quad_tensor.shape) == 2:
quad_moment = __quadrupole_moment(quad_tensor)
else:
quad_moment = np.array(
[__quadrupole_moment(x) for x in quad_tensor]
)
quad_moment = np.array([__quadrupole_moment(x) for x in quad_tensor])

return quad_moment

Expand Down Expand Up @@ -3011,9 +2975,7 @@ def sequence(self, **kwargs):
)
)
try:
sequence = "".join(
[convert_aa_code(r) for r in self.residues.resnames]
)
sequence = "".join([convert_aa_code(r) for r in self.residues.resnames])
except KeyError as err:
errmsg = (
f"AtomGroup contains a residue name '{err.message}' that"
Expand Down Expand Up @@ -3146,14 +3108,13 @@ def _check_connection_values(func):
@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)
len(x) == self._n_atoms 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)
("{} must be an iterable of tuples with {}" " atom indices").format(
self.attrname, self._n_atoms
)
)
clean = []
for v in values:
Expand Down Expand Up @@ -3231,9 +3192,7 @@ def _bondDict(self):
"""Lazily built mapping of atoms:bonds"""
bd = defaultdict(list)

for b, t, g, o in zip(
self.values, self.types, self._guessed, self.order
):
for b, t, g, o in zip(self.values, self.types, self._guessed, self.order):
for a in b:
bd[a].append((b, t, g, o))
return bd
Expand All @@ -3252,9 +3211,7 @@ def get_atoms(self, ag):

"""
try:
unique_bonds = set(
itertools.chain(*[self._bondDict[a] for a in ag.ix])
)
unique_bonds = set(itertools.chain(*[self._bondDict[a] for a in ag.ix]))
except TypeError:
# maybe we got passed an Atom
unique_bonds = self._bondDict[ag.ix]
Expand Down
36 changes: 17 additions & 19 deletions testsuite/MDAnalysisTests/guesser/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import pytest
from MDAnalysis import _GUESSERS, _TOPOLOGY_ATTRS
from MDAnalysis.core.topology import Topology
from MDAnalysis.core.topologyattrs import Atomnames, Atomtypes, Masses
from MDAnalysis.core.topologyattrs import Atomnames, Atomtypes, Elements, Masses
from MDAnalysis.exceptions import NoDataError
from MDAnalysis.guesser.base import GuesserBase, get_guesser
from numpy.testing import assert_allclose, assert_equal
Expand Down Expand Up @@ -58,16 +58,13 @@ class TestGuesser1(GuesserBase):
def test_guess_invalid_attribute(self):
with pytest.raises(
ValueError,
match="default guesser can not guess "
"the following attribute: foo",
match="default guesser can not guess " "the following attribute: foo",
):
mda.Universe(datafiles.PDB_xsmall, to_guess=["foo"])

def test_guess_attribute_with_missing_parent_attr(self):
names = Atomnames(np.array(["C", "HB", "HA", "O"], dtype=object))
masses = Masses(
np.array([np.nan, np.nan, np.nan, np.nan], dtype=np.float64)
)
masses = Masses(np.array([np.nan, np.nan, np.nan, np.nan], dtype=np.float64))
top = Topology(4, 1, 1, attrs=[names, masses])
u = mda.Universe(top, to_guess=["masses"])
assert_allclose(
Expand All @@ -88,9 +85,7 @@ def test_partial_guessing(self):
masses = Masses(np.array([0, np.nan, np.nan, 0], dtype=np.float64))
top = Topology(4, 1, 1, attrs=[types, masses])
u = mda.Universe(top, to_guess=["masses"])
assert_allclose(
u.atoms.masses, np.array([0, 1.00800, 1.00800, 0]), atol=0
)
assert_allclose(u.atoms.masses, np.array([0, 1.00800, 1.00800, 0]), atol=0)

def test_force_guess_priority(self):
"check that passing the attribute to force_guess have higher power"
Expand All @@ -104,14 +99,21 @@ def test_force_guess_priority(self):
atol=0,
)

def test_partial_guess_attr_with_unknown_no_value_label(self):
"trying to partially guess attribute tha doesn't have declared"
"no_value_label should gives no effect"
def test_partial_guess_types_with_missing_value_label(self):
"""Atomtypes with empty string values are guessed because
missing_value_label is defined as an empty string."""
names = Atomnames(np.array(["C", "H", "H", "O"], dtype=object))
types = Atomtypes(np.array(["", "", "", ""], dtype=object))
top = Topology(4, 1, 1, attrs=[names, types])
u = mda.Universe(top, to_guess=["types"])
assert_equal(u.atoms.types, ["", "", "", ""])
assert_equal(u.atoms.types, ["C", "H", "H", "O"])

def test_partial_guess_elements_with_missing_value_label(self):
names = Atomnames(np.array(["C1", "N1"], dtype=object))
elements = Elements(np.array(["C", ""], dtype=object))
top = Topology(2, 1, 1, attrs=[names, elements])
u = mda.Universe(top, to_guess=["elements"])
assert_equal(u.atoms.elements, ["C", "N"])

def test_guess_topology_objects_existing_read(self):
u = mda.Universe(datafiles.CONECT)
Expand Down Expand Up @@ -185,9 +187,7 @@ def test_guess_topology_objects_out_of_order_guess(self):
with pytest.raises(NoDataError):
u.atoms.angles

u.guess_TopologyAttrs(
"default", to_guess=["dihedrals", "angles", "bonds"]
)
u.guess_TopologyAttrs("default", to_guess=["dihedrals", "angles", "bonds"])
assert len(u.atoms.angles) == 290
assert len(u.atoms.dihedrals) == 411

Expand Down Expand Up @@ -249,9 +249,7 @@ def test_guess_singular(self):


def test_Universe_guess_bonds_deprecated():
with pytest.warns(
DeprecationWarning, match="`guess_bonds` keyword is deprecated"
):
with pytest.warns(DeprecationWarning, match="`guess_bonds` keyword is deprecated"):
u = mda.Universe(datafiles.PDB_xsmall, guess_bonds=True)


Expand Down
Loading