Source code for MDAnalysis.topology.DMSParser

# -*- 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.1 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
#

"""
DESRES Molecular Structure file format topology parser
======================================================

Classes to read a topology from a DESRES_ Molecular Structure file
format (DMS_) coordinate files (as used by the Desmond_ MD package).

.. _DESRES: http://www.deshawresearch.com
.. _Desmond: http://www.deshawresearch.com/resources_desmond.html
.. _DMS: http://www.deshawresearch.com/Desmond_Users_Guide-0.7.pdf

Classes
-------

.. autoclass:: DMSParser
   :members:
   :inherited-members:

"""
import numpy as np
import sqlite3
import os

from .base import TopologyReaderBase, change_squash
from ..core.topology import Topology
from ..core.topologyattrs import (
    Atomids,
    Atomnames,
    Bonds,
    Charges,
    ChainIDs,
    Masses,
    Resids,
    Resnums,
    Resnames,
    Segids,
    AtomAttr,  # for custom Attributes
)


class Atomnums(AtomAttr):
    """The number for each Atom"""

    attrname = "atomnums"
    singular = "atomnum"


[docs] class DMSParser(TopologyReaderBase): """Read a topology from a DESRES_ Molecular Structure file. Format (DMS_) coordinate files (as used by the Desmond_ MD package). Reads the following attributes: Atom: - Atomids - Atomnums - Atomnames - Masses - Charges - Chainids Residue: - Resnames - Resids Segment: - Segids .. note:: By default, atomtypes will be guessed on Universe creation. This may change in release 3.0. See :ref:`Guessers` for more information. .. _DESRES: http://www.deshawresearch.com .. _Desmond: http://www.deshawresearch.com/resources_desmond.html .. _DMS: http://www.deshawresearch.com/Desmond_Users_Guide-0.7.pdf .. versionchanged:: 2.8.0 Removed type guessing (attributes guessing takes place now through universe.guess_TopologyAttrs() API). """ format = "DMS"
[docs] def parse(self, **kwargs): """Parse DMS file *filename* and return the Topology object""" # Fix by SB: Needed because sqlite3.connect does not raise anything # if file is not there if not os.path.isfile(self.filename): raise IOError("No such file: {0}".format(self.filename)) def dict_factory(cursor, row): """ Fetch SQL records as dictionaries, rather than the default tuples. """ d = {} for idx, col in enumerate(cursor.description): d[col[0]] = row[idx] return d attrs = {} # Row factories for different data types facs = { np.int32: lambda c, r: r[0], np.float32: lambda c, r: r[0], object: lambda c, r: str(r[0].strip()), } with sqlite3.connect(self.filename) as con: # Selecting single column, so just strip tuple for attrname, dt in [ ("id", np.int32), ("anum", np.int32), ("mass", np.float32), ("charge", np.float32), ("name", object), ("resname", object), ("resid", np.int32), ("chain", object), ("segid", object), ]: try: cur = con.cursor() cur.row_factory = facs[dt] cur.execute("SELECT {} FROM particle" "".format(attrname)) vals = cur.fetchall() except sqlite3.DatabaseError: errmsg = "Failed reading the atoms from DMS Database" raise IOError(errmsg) from None else: attrs[attrname] = np.array(vals, dtype=dt) try: cur.row_factory = dict_factory cur.execute("SELECT * FROM bond") bonds = cur.fetchall() except sqlite3.DatabaseError: errmsg = "Failed reading the bonds from DMS Database" raise IOError(errmsg) from None else: bondlist = [] bondorder = {} for b in bonds: desc = tuple(sorted([b["p0"], b["p1"]])) bondlist.append(desc) bondorder[desc] = b["order"] attrs["bond"] = bondlist attrs["bondorder"] = bondorder topattrs = [] # Bundle in Atom level objects for attr, cls in [ ("id", Atomids), ("anum", Atomnums), ("mass", Masses), ("charge", Charges), ("name", Atomnames), ("chain", ChainIDs), ]: topattrs.append(cls(attrs[attr])) # Residues atom_residx, (res_resids, res_resnums, res_resnames, res_segids) = ( change_squash( (attrs["resid"], attrs["resname"], attrs["segid"]), ( attrs["resid"], attrs["resid"].copy(), attrs["resname"], attrs["segid"], ), ) ) n_residues = len(res_resids) topattrs.append(Resids(res_resids)) topattrs.append(Resnums(res_resnums)) topattrs.append(Resnames(res_resnames)) if any(res_segids) and not any(val is None for val in res_segids): res_segidx, (res_segids,) = change_squash( (res_segids,), (res_segids,) ) uniq_seg = np.unique(res_segids) idx2seg = {idx: res_segids[idx] for idx in res_segidx} res_segids = uniq_seg nidx = {segid: nidx for nidx, segid in enumerate(uniq_seg)} res_segidx = np.array([nidx[idx2seg[idx]] for idx in res_segidx]) n_segments = len(res_segids) topattrs.append(Segids(res_segids)) else: n_segments = 1 topattrs.append(Segids(np.array(["SYSTEM"], dtype=object))) res_segidx = None topattrs.append(Bonds(attrs["bond"])) top = Topology( len(attrs["id"]), n_residues, n_segments, attrs=topattrs, atom_resindex=atom_residx, residue_segindex=res_segidx, ) return top