Source code for mda_openbabel_converter.OpenBabel

"""OpenBabel molecule I/O --- :mod:`mda_openbabel_converter.OpenBabel`
======================================================================

Read coordinates data from an
`OpenBabel <http://openbabel.org/api/3.0/classOpenBabel_1_1OBMol.shtml>`_
:class:`openbabel.openbabel.OBMol` with :class:`OpenBabelReader` into an
MDAnalysis Universe. Convert it back to a :class:`openbabel.openbabel.OBMol`
with :class:`OpenBabelConverter`.

Example
-------

To read an OpenBabel OBMol and then convert the AtomGroup back to an OpenBabel
OBMol::

    >>> from openbabel import openbabel as ob
    >>> import MDAnalysis as mda
    >>> obconversion = ob.OBConversion()
    >>> obconversion.SetInFormat("pdb")
    >>> mol = ob.OBMol()
    >>> obconversion.ReadFile(mol, "1crn.pdb")
    >>> u = mda.Universe(mol)
    >>> u
    <Universe with 327 atoms>
    >>> u.trajectory
    <OpenBabelReader with 1 frame of 327 atoms>
    >>> u.atoms.convert_to("OPENBABEL")
    <openbabel.openbabel.OBMol object at 0x7fcebb958148>


.. warning::
    The OpenBabel converter is currently *experimental* and may not work as
    expected for all molecules.


Classes
-------

.. autoclass:: OpenBabelReader
   :members:

.. autoclass:: OpenBabelConverter
   :members:
"""

import MDAnalysis as mda
from MDAnalysis.converters.base import ConverterBase
from MDAnalysis.coordinates.memory import MemoryReader
from MDAnalysis.core.groups import AtomGroup
import numpy as np
import warnings

HAS_OBABEL = False

try:
    from openbabel import openbabel as ob
    from openbabel.openbabel import OBMol, OBAtom, OBMolAtomIter
    HAS_OBABEL = True
except ImportError:
    warnings.warn("Cannot find openbabel, install with `mamba install -c "
                  "conda-forge openbabel`")


[docs] class OpenBabelReader(MemoryReader): """ Coordinate reader for OpenBabel. Inherits from MemoryReader and converts OpenBabel OBMol Coordinates to a MDAnalysis Trajectory which is used to build a Universe. This reader does NOT work in the reverse direction. See :class:`mda_openbabel_converter.OpenBabel.OpenBabelConverter` for MDAnalysis Universe to OpenBabel OBMol conversion. """ format = 'OPENBABEL' # Structure.coordinates always in Angstrom units = {'time': None, 'length': 'Angstrom'} @staticmethod def _format_hint(thing): """ Base function to check if the reader can actually read this “thing” (i.e., is it a file that can be converted to an OpenBabel OBMol?) """ if HAS_OBABEL is False: return False else: return isinstance(thing, OBMol) def __init__(self, filename: OBMol, **kwargs): """ Converts file to OBMol to AtomGroup """ n_atoms = filename.NumAtoms() # single position if filename.NumConformers() == 1: coordinates = np.array([ [(coords := atom.GetVector()).GetX(), coords.GetY(), coords.GetZ()] for atom in OBMolAtomIter(filename)], dtype=np.float32) else: # multiple conformers, such as for a trajectory numConf = filename.NumConformers() coordinates = np.zeros((numConf, n_atoms, 3)) for conf_id in range(numConf): filename.SetConformer(conf_id) for atom in OBMolAtomIter(filename): coordinates_inner = np.array([ [(coords := atom.GetVector()).GetX(), coords.GetY(), coords.GetZ()] for atom in OBMolAtomIter(filename)], dtype=np.float32) coordinates[conf_id] = coordinates_inner # no coordinates present if not np.any(coordinates): warnings.warn("No coordinates found in the OBMol") coordinates = np.empty((1, n_atoms, 3), dtype=np.float32) coordinates[:] = np.nan super(OpenBabelReader, self).__init__(coordinates, order='fac', **kwargs)
[docs] class OpenBabelConverter(ConverterBase): """ Inherits from ConverterBase and converts a MDAnalysis Universe to an OpenBabel OBMol. This converter does NOT work in the opposite direction. See :class:`mda_openbabel_converter.OpenBabelReader` for OpenBabel OBMol to MDAnalysis Universe conversion. """ def __repr__(self, **kwargs): """ String representation of the object (defined in Base Class) """ pass
[docs] def convert(self, obj, **kwargs): """ Converts AtomGroup to OBMol """ pass
# add getter and setter methods as required # add helper methods