1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
|
# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding: utf-8 -*-
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
#
# MDAnalysis --- https://www.mdanalysis.org
# Copyright (c) 2006-2017 The MDAnalysis Development Team and contributors
# (see the file AUTHORS for the full list of names)
#
# Released under the Lesser GNU Public Licence, v2 or any higher version
#
# Please cite your use of MDAnalysis in published work:
#
# R. J. Gowers, M. Linke, J. Barnoud, T. J. E. Reddy, M. N. Melo, S. L. Seyler,
# D. L. Dotson, J. Domanski, S. Buchoux, I. M. Kenney, and O. Beckstein.
# MDAnalysis: A Python package for the rapid analysis of molecular dynamics
# simulations. In S. Benthall and S. Rostrup editors, Proceedings of the 15th
# Python in Science Conference, pages 102-109, Austin, TX, 2016. SciPy.
# doi: 10.25080/majora-629e541a-00e
#
# N. Michaud-Agrawal, E. J. Denning, T. B. Woolf, and O. Beckstein.
# MDAnalysis: A Toolkit for the Analysis of Molecular Dynamics Simulations.
# J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787
#
"""
Base guesser classes --- :mod:`MDAnalysis.guesser.base`
================================================================
Derive context-specific guesser classes from the base class in this module.
Classes
-------
.. autoclass:: GuesserBase
:members:
:inherited-members:
.. autofunction:: get_guesser
"""
from .. import _GUESSERS, _TOPOLOGY_ATTRS
from ..core.topologyattrs import _Connection
import numpy as np
import logging
from typing import Dict
import copy
logger = logging.getLogger("MDAnalysis.guesser.base")
class _GuesserMeta(type):
"""Internal: guesser classes registration
When classes which inherit from GuesserBase are *defined*
this metaclass makes it known to MDAnalysis. 'context'
attribute are read:
- `context` defines the context of the guesser class for example:
forcefield specific context as MartiniGuesser
and file specific context as PDBGuesser.
Eg::
class FooGuesser(GuesserBase):
format = 'foo'
.. versionadded:: 2.8.0
"""
def __init__(cls, name, bases, classdict):
type.__init__(type, name, bases, classdict)
_GUESSERS[classdict["context"].upper()] = cls
class GuesserBase(metaclass=_GuesserMeta):
"""Base class for context-specific guessers to inherit from
Parameters
----------
universe : Universe, optional
Supply a Universe to the Guesser. This then becomes the source of atom
attributes to be used in guessing processes. (this is relevant to how
the universe's guess_TopologyAttrs API works.
See :meth:`~MDAnalysis.core.universe.Universe.guess_TopologyAttrs`).
**kwargs : dict, optional
To pass additional data to the guesser that can be used with
different methods.
.. versionadded:: 2.8.0
"""
context = "base"
_guesser_methods: Dict = {}
def __init__(self, universe=None, **kwargs):
self._universe = universe
self._kwargs = kwargs
def update_kwargs(self, **kwargs):
self._kwargs.update(kwargs)
def copy(self):
"""Return a copy of this Guesser"""
kwargs = copy.deepcopy(self._kwargs)
new = self.__class__(universe=None, **kwargs)
return new
def is_guessable(self, attr_to_guess):
"""check if the passed atrribute can be guessed by the guesser class
Parameters
----------
guess: str
Attribute to be guessed then added to the Universe
Returns
-------
bool
"""
if attr_to_guess.lower() in self._guesser_methods:
return True
return False
def guess_attr(self, attr_to_guess, force_guess=False):
"""map the attribute to be guessed with the apporpiate guessing method
Parameters
----------
attr_to_guess: str
an atrribute to be guessed then to be added to the universe
force_guess: bool
To indicate wether to only partialy guess the empty values of the
attribute or to overwrite all existing values by guessed one
Returns
-------
NDArray of guessed values
"""
try:
top_attr = _TOPOLOGY_ATTRS[attr_to_guess]
except KeyError:
raise KeyError(
f"{attr_to_guess} is not a recognized MDAnalysis "
"topology attribute"
)
# make attribute to guess plural
attr_to_guess = top_attr.attrname
try:
guesser_method = self._guesser_methods[attr_to_guess]
except KeyError:
raise ValueError(
f"{type(self).__name__} cannot guess this "
f"attribute: {attr_to_guess}"
)
# Connection attributes should be just returned as they are always
# appended to the Universe. ``force_guess`` handling should happen
# at Universe level.
if issubclass(top_attr, _Connection):
return guesser_method()
# check if the topology already has the attribute to partially guess it
if hasattr(self._universe.atoms, attr_to_guess) and not force_guess:
attr_values = np.array(
getattr(self._universe.atoms, attr_to_guess, None)
)
empty_values = top_attr.are_values_missing(attr_values)
if True in empty_values:
# pass to the guesser_method boolean mask to only guess the
# empty values
attr_values[empty_values] = guesser_method(
indices_to_guess=empty_values
)
return attr_values
else:
logger.info(
f"There is no empty {attr_to_guess} values. Guesser did "
f"not guess any new values for {attr_to_guess} attribute"
)
return None
else:
return np.array(guesser_method())
def get_guesser(context, u=None, **kwargs):
"""get an appropiate guesser to the Universe and pass
the Universe to the guesser
Parameters
----------
u: Universe
to be passed to the guesser
context: str or Guesser
**kwargs : dict, optional
Extra arguments are passed to the guesser.
Returns
-------
Guesser class
Raises
------
* :exc:`KeyError` upon failing to return a guesser class
.. versionadded:: 2.8.0
"""
if isinstance(context, GuesserBase):
context._universe = u
context.update_kwargs(**kwargs)
return context
try:
if issubclass(context, GuesserBase):
return context(u, **kwargs)
except TypeError:
pass
try:
guesser = _GUESSERS[context.upper()](u, **kwargs)
except KeyError:
raise KeyError("Unidentified guesser type {0}".format(context))
return guesser
|