Source code for schrodinger.application.desmond.ffiostructure

"""
The central interface for reading and editing Maestro format chemical
sructures with force field data.

`FFIOStructure` is a pythonic, object oriented wrapper for the mmffio library
that provides access to sites, bonds, angles, dihedrals, exclusions, pairs,
vdwtypes, restraints, virtuals, pseudos, constraints and their properties.
It inherits from `Structure` and hence provides access to atoms, bonds and
their properties.

The default error handler for the ffiostructure module is set to
mm.error_handler. If no error handler is specified for error_handler arguments,
its value is what will be used.


Copyright Schrodinger, LLC. All rights reserved.

"""

import os
from collections.abc import MutableMapping

from schrodinger.application.desmond import constants
from schrodinger.infra import mm
from schrodinger.infra.util import CreateWhenNeeded
from schrodinger.structure import Structure
from schrodinger.structure import write_ct_to_string

__all__ = [
    'FFIOStructure',
    'CMSReader',
    'write_cms',
]

# Dictionaries linking built-in property names of different standard force field blocks
# to thier short names(to be used to get/set from python interfaces), mmffio getter and
# setter methods. When the value is empty list, they are deduced from property names

ffio_sub_block_prop_links = {
    'site': {
        's_ffio_type': [
            'type', mm.mmffio_site_get_type, mm.mmffio_site_set_type
        ],
        's_ffio_site': [
            'site', mm.mmffio_site_get_site_name, mm.mmffio_site_set_site_name
        ],
        'r_ffio_charge': [
            'charge', mm.mmffio_site_get_charge, mm.mmffio_site_set_charge
        ],
        'r_ffio_mass': [
            'mass', mm.mmffio_site_get_mass, mm.mmffio_site_set_mass
        ],
        's_ffio_vdwtype': [
            'vdwtype', mm.mmffio_site_get_vdwtype, mm.mmffio_site_set_vdwtype
        ],
        'r_ffio_c1': ['c1', mm.mmffio_site_get_c1, mm.mmffio_site_set_c1],
        'r_ffio_c2': ['c2', mm.mmffio_site_get_c2, mm.mmffio_site_set_c2]
    },
    'bond': {
        'i_ffio_ai': [],
        'i_ffio_aj': [],
        's_ffio_funct': [],
        'r_ffio_c1': [],
        'r_ffio_c2': []
    },
    'angle': {
        'i_ffio_ai': [],
        'i_ffio_aj': [],
        'i_ffio_ak': [],
        's_ffio_funct': [],
        'r_ffio_c1': [],
        'r_ffio_c2': []
    },
    'dihedral': {
        'i_ffio_ai': [],
        'i_ffio_aj': [],
        'i_ffio_ak': [],
        'i_ffio_al': [],
        's_ffio_funct': [],
        'i_ffio_i1': [],
        'r_ffio_c0': [],
        'r_ffio_c1': [],
        'r_ffio_c2': [],
        'r_ffio_c3': [],
        'r_ffio_c4': [],
        'r_ffio_c5': [],
        'r_ffio_c6': [],
        'r_ffio_c7': [],
        'r_ffio_t1': [],
    },
    'exclusion': {
        'i_ffio_ai': [],
        'i_ffio_aj': []
    },
    'pair': {
        'i_ffio_ai': [],
        'i_ffio_aj': [],
        's_ffio_funct': [],
        'r_ffio_c1': [],
        'r_ffio_c2': []
    },
    'vdwtype': {
        's_ffio_name': [],
        's_ffio_funct': [],
        'r_ffio_c1': [],
        'r_ffio_c2': [],
        'r_ffio_t1': [],
    },
    'vdwtypescombined': {
        's_ffio_name1': [],
        's_ffio_name2': [],
        's_ffio_funct': [],
        'r_ffio_c1': [],
        'r_ffio_c2': [],
        'r_ffio_t1': [],
    },
    'restraint': {
        'i_ffio_ai': [],
        's_ffio_funct': [],
        'r_ffio_c1': [],
        'r_ffio_c2': [],
        'r_ffio_c3': [],
        'r_ffio_t1': [],
        'r_ffio_t2': [],
        'r_ffio_t3': [],
    },
    'virtual': {
        'i_ffio_index': [
            'virtual_index', mm.mmffio_virtual_get_index,
            mm.mmffio_virtual_set_index
        ],
        'i_ffio_ai': [],
        'i_ffio_aj': [],
        'i_ffio_ak': [],
        'i_ffio_al': [],
        'i_ffio_am': [],
        'i_ffio_an': [],
        's_ffio_funct': [],
        'r_ffio_c1': [],
        'r_ffio_c2': [],
        'r_ffio_c3': [],
        'r_ffio_c4': [],
        'r_ffio_c5': [],
        'r_ffio_c6': [],
    },
    'pseudo': {
        'r_ffio_x_coord': [
            'x_coord', mm.mmffio_pseudo_get_x_coord,
            mm.mmffio_pseudo_set_x_coord
        ],
        'r_ffio_y_coord': [
            'y_coord', mm.mmffio_pseudo_get_y_coord,
            mm.mmffio_pseudo_set_y_coord
        ],
        'r_ffio_z_coord': [
            'z_coord', mm.mmffio_pseudo_get_z_coord,
            mm.mmffio_pseudo_set_z_coord
        ],
        'r_ffio_x_vel': [
            'x_vel', mm.mmffio_pseudo_get_x_vel, mm.mmffio_pseudo_set_x_vel
        ],
        'r_ffio_y_vel': [
            'y_vel', mm.mmffio_pseudo_get_y_vel, mm.mmffio_pseudo_set_y_vel
        ],
        'r_ffio_z_vel': [
            'z_vel', mm.mmffio_pseudo_get_z_vel, mm.mmffio_pseudo_set_z_vel
        ],
    },
    'constraint': {
        'i_ffio_ai': [],
        'i_ffio_aj': [],
        'i_ffio_ak': [],
        'i_ffio_al': [],
        'i_ffio_am': [],
        's_ffio_funct': [],
        'r_ffio_c1': [],
        'r_ffio_c2': [],
        'r_ffio_c3': [],
        'r_ffio_c4': [],
        'r_ffio_c5': [],
        'r_ffio_c6': []
    },
    'posfbhw': {
        'i_ffio_ai': [],
        'r_ffio_sigma': [],
        'r_ffio_fc': [],
        'r_ffio_x0': [],
        'r_ffio_y0': [],
        'r_ffio_z0': []
    },
    'anglefbhw': {
        'i_ffio_ai': [],
        'i_ffio_aj': [],
        'i_ffio_ak': [],
        'r_ffio_sigma': [],
        'r_ffio_fc': [],
        'r_ffio_theta0': []
    },
    'improperfbhw': {
        'i_ffio_ai': [],
        'i_ffio_aj': [],
        'i_ffio_ak': [],
        'i_ffio_al': [],
        'r_ffio_sigma': [],
        'r_ffio_fc': [],
        'r_ffio_phi0': []
    },
    'stretchfbhw': {
        's_ffio_group1': [],
        's_ffio_group2': [],
        'r_ffio_lower': [],
        'r_ffio_upper': [],
        'r_ffio_sigma': [],
        'r_ffio_fc': [],
        'r_ffio_beta': []
    }
}

ffio_block_names = {
    'site': 'site',
    'bond': 'bond',
    'angle': 'angle',
    'dihedral': 'dihed',
    'exclusion': 'excl',
    'pair': 'pair',
    'vdwtype': 'vdwtype',
    'restraint': 'restraint',
    'virtual': 'virtual',
    'pseudo': 'pseudo',
    'constraint': 'constraint',
    'posfbhw': 'posfbhw',
    'anglefbhw': 'anglefbhw',
    'improperfbhw': 'improperfbhw',
    'stretchfbhw': 'stretchfbhw',
    'vdwtypescombined': 'vdwtypescombined',
}

prop_type_names = {
    "s": "string",
    "i": "int",
    "r": "real",
    "b": "boolean",
}


[docs]def make_property(getter, setter, doc): def _get(self): return getter(self._ff.handle, self._index) def _set(self, value): setter(self._ff.handle, self._index, value) return property(_get, _set, doc=doc)
[docs]def get_ffio_sub_block_std_prop_short_name(block_type, prop_name): """ Function to get short names of properties. It is picked from the ffio_sub_block_prop_links dict if avaiable, else deduced from property name """ if ffio_sub_block_prop_links[block_type][prop_name] == []: short_name = prop_name.split('_')[-1] else: short_name = ffio_sub_block_prop_links[block_type][prop_name][0] return short_name
[docs]def get_ffio_sub_block_std_prop_getter(block_type, prop_name): """ Function to get mmffio getter method of properties. It is picked from the ffio_sub_block_prop_links dict if avaiable, else deduced from property name """ if ffio_sub_block_prop_links[block_type][prop_name] == []: short_name = prop_name.split('_')[-1] func = getattr( mm, "mmffio_%s_get_%s" % (ffio_block_names[block_type], short_name)) else: func = ffio_sub_block_prop_links[block_type][prop_name][1] return func
[docs]def get_ffio_sub_block_std_prop_setter(block_type, prop_name): """ Function to get mmffio setter method of properties. It is picked from the ffio_sub_block_prop_links dict if avaiable, else deduced from property name """ if ffio_sub_block_prop_links[block_type][prop_name] == []: short_name = prop_name.split('_')[-1] func = getattr( mm, "mmffio_%s_set_%s" % (ffio_block_names[block_type], short_name)) else: func = ffio_sub_block_prop_links[block_type][prop_name][2] return func
class _BlockPropertyBase(MutableMapping): """ Base class to override common abstract methods """ def __delitem__(self, item): pass def __iter__(self): for k in self.keys(): yield k def __len__(self): return len(self.keys()) class _FFIOSubBlockProperty(_BlockPropertyBase): """ A dictionary of ffio sub-block properties. These can be accessed via the property name as it appears in the maestro file. Property names must be m2io data names, which are in the format '<type>_<author>_<property_name>', where '<type>' is a data type prefix, '<author>' is a source specification, and '<property_name>' is the actual name of the data. The data type prefix can specified as 's' for string, 'i' for integer, 'r' for real and 'b' for boolean. The author specification should be 'user' for user created properties. The property name can have embedded underscores. Some example m2io datanames are 'r_ffio_x_coord', which indicates a real property named 'x coord', and 'i_user_my_count' which indicates an integer user property named 'my count'. """ def __init__(self, ff, index, type): """Create an instance of the property dictionary. """ self._ff = ff self._index = index self._type = type def __getitem__(self, item): """ Return the given item if it is a valid property for this item, None if not. """ # Check to see if property is built-in: builtin = ffio_sub_block_prop_links.get(self._type).get(item) if builtin is not None: short_name = get_ffio_sub_block_std_prop_short_name( self._type, item) return getattr( getattr(self._ff, self._type)[self._index], short_name) try: func = getattr( mm, "mmffio_%s_prop_get_%s" % (ffio_block_names[self._type], prop_type_names[item[0]])) ret = func(self._ff.handle, item, self._index) except mm.MmException as e: raise KeyError("%s is not the name of an %s property" % (item, self._type)) except IndexError as e: if item == "": raise KeyError("The empty string is an invalid dataname.") else: raise return ret def __setitem__(self, item, value): """Set properties for the given item. """ builtin = ffio_sub_block_prop_links.get(self._type).get(item) if builtin is not None: short_name = get_ffio_sub_block_std_prop_short_name( self._type, item) setattr( getattr(self._ff, self._type)[self._index], short_name, value) return func = getattr( mm, "mmffio_%s_prop_set_%s" % (ffio_block_names[self._type], prop_type_names[item[0]])) func(self._ff.handle, item, self._index, value) def keys(self): """ Return a list of all property names. """ func = getattr( mm, "mmffio_%s_get_data_names" % ffio_block_names[self._type]) pnames = func(self._ff.handle, self._index, mm.M2IO_ALL_TYPES) return list(ffio_sub_block_prop_links.get(self._type)) + pnames class _FFIOSubBlockMeta(type): """ A meta-class to different ffio sub block type classses. This simplifies adding property descriptors for different sub-block types """ def __init__(cls, name, bases, dict): super(_FFIOSubBlockMeta, cls).__init__(name, bases, dict) block_type = cls.block_type for prop in list(ffio_sub_block_prop_links[block_type]): prop_name = get_ffio_sub_block_std_prop_short_name(block_type, prop) prop_getter = get_ffio_sub_block_std_prop_getter(block_type, prop) prop_setter = get_ffio_sub_block_std_prop_setter(block_type, prop) doc = "%s %s" % (block_type, prop_name) setattr(cls, prop_name, make_property(prop_getter, prop_setter, doc)) class _FFIOSubBlock: """ A class to make access of ffio sub-block properties more pythonic. """ block_type = None def __init__(self, ff, _index): self._ff = ff self._index = _index self._property = None def __eq__(self, that): """ Compare on ff handle and index. """ if self._ff == that._ff and self._index == that._index and type( self) == type(that): return True else: return False def __ne__(self, other): """ Check for inequality based on ff handle and index. """ return not self.__eq__(other) def __hash__(self): return hash(self._ff.handle) ^ self._index def __index__(self): return self._index def __str__(self): return "%s(%d)" % (self.block_type, int(self)) # index def _getIndex(self): return self._index index = property(_getIndex, doc="The item index. I{Read only.}") # property def _getProperty(self): if self._property is None: self._property = _FFIOSubBlockProperty(self._ff, self._index, self.block_type) return self._property property = property(_getProperty, doc="property dictionary") class _FFIOSite(_FFIOSubBlock, metaclass=_FFIOSubBlockMeta): """ A class to make access of mmffio site properties more pythonic. """ block_type = "site" class _FFIOBond(_FFIOSubBlock, metaclass=_FFIOSubBlockMeta): """ A class to make access of mmffio bond properties more pythonic. """ block_type = "bond" class _FFIOAngle(_FFIOSubBlock, metaclass=_FFIOSubBlockMeta): """ A class to make access of mmffio angle properties more pythonic. """ block_type = "angle" class _FFIODihedral(_FFIOSubBlock, metaclass=_FFIOSubBlockMeta): """ A class to make access of mmffio dihedral properties more pythonic. """ block_type = "dihedral" class _FFIOExclusion(_FFIOSubBlock, metaclass=_FFIOSubBlockMeta): """ A class to make access of mmffio exclusion properties more pythonic. """ block_type = "exclusion" class _FFIOPair(_FFIOSubBlock, metaclass=_FFIOSubBlockMeta): """ A class to make access of mmffio pair properties more pythonic. """ block_type = "pair" class _FFIOVdwtype(_FFIOSubBlock, metaclass=_FFIOSubBlockMeta): """ A class to make access of mmffio vdwtype properties more pythonic. """ block_type = "vdwtype" class _FFIOVdwtypescombined(_FFIOSubBlock, metaclass=_FFIOSubBlockMeta): """ A class to make access of mmffio vdwtype properties more pythonic. """ block_type = "vdwtypescombined" class _FFIORestraint(_FFIOSubBlock, metaclass=_FFIOSubBlockMeta): """ A class to make access of mmffio restraint properties more pythonic. """ block_type = "restraint" class _FFIOVirtual(_FFIOSubBlock, metaclass=_FFIOSubBlockMeta): """ A class to make access of mmffio virtual properties more pythonic. """ block_type = "virtual" class _FFIOPseudo(_FFIOSubBlock, metaclass=_FFIOSubBlockMeta): """ A class to make access of mmffio pseudo properties more pythonic. """ block_type = "pseudo" class _FFIOConstraint(_FFIOSubBlock, metaclass=_FFIOSubBlockMeta): """ A class to make access of mmffio constraint properties more pythonic. """ block_type = "constraint" class _FFIOPosFBHW(_FFIOSubBlock, metaclass=_FFIOSubBlockMeta): """ A class to make access of mmffio position flat bottom properties more pythonic. """ block_type = "posfbhw" class _FFIOAngleFBHW(_FFIOSubBlock, metaclass=_FFIOSubBlockMeta): """ A class to make access of mmffio angle flat bottom properties more pythonic. """ block_type = "anglefbhw" class _FFIOImproperFBHW(_FFIOSubBlock, metaclass=_FFIOSubBlockMeta): """ A class to make access of mmffio improper flat bottom properties more pythonic. """ block_type = "improperfbhw" class _FFIOStretchFBHW(_FFIOSubBlock, metaclass=_FFIOSubBlockMeta): """ A class to make access of mmffio stretch flat bottom properties more pythonic. """ block_type = "stretchfbhw" class _FFIOSubBlockContainer: def __init__(self, ff): """ Initialize the container. """ self._ff = ff def _item_count_method(self): raise NotImplementedError() def _item_delete_method(self, index): raise NotImplementedError() _item_class = None def __delitem__(self, index): """ Delete a ffio sub-block item from the CT. Note that this immediately updates the CT and therefore renumbers any items following the one deleted. """ if index < 1 or index > len(self): raise IndexError("Index out of range [1, %d]: %d" % ( len(self), index, )) self._item_delete_method(self._ff.handle, index) def __getitem__(self, index): """ Return the wrapper class for ffio sub-block item based properties. Note that the initial index is 1, as per the underlying mmffio library, not 0 as is expected for python. """ if index < 1 or index > len(self): raise IndexError("Index out of range [1, %d]: %d" % ( len(self), index, )) return self._item_class(self._ff, index) def __iter__(self): """ Provide iteration access. """ for i in range(1, len(self) + 1): yield self._item_class(self._ff, i) def __len__(self): """ Return the number of items. """ return self._item_count_method(self._ff.handle) class _FFIOSiteContainer(_FFIOSubBlockContainer): _item_count_method = mm.mmffio_ff_get_num_sites _item_delete_method = mm.mmffio_delete_site _item_class = _FFIOSite class _FFIOBondContainer(_FFIOSubBlockContainer): _item_count_method = mm.mmffio_ff_get_num_bonds _item_delete_method = mm.mmffio_delete_bond _item_class = _FFIOBond class _FFIOAngleContainer(_FFIOSubBlockContainer): _item_count_method = mm.mmffio_ff_get_num_angles _item_delete_method = mm.mmffio_delete_angle _item_class = _FFIOAngle class _FFIODihedralContainer(_FFIOSubBlockContainer): _item_count_method = mm.mmffio_ff_get_num_diheds _item_delete_method = mm.mmffio_delete_dihed _item_class = _FFIODihedral class _FFIOExclusionContainer(_FFIOSubBlockContainer): _item_count_method = mm.mmffio_ff_get_num_excls _item_delete_method = mm.mmffio_delete_excl _item_class = _FFIOExclusion class _FFIOPairContainer(_FFIOSubBlockContainer): _item_count_method = mm.mmffio_ff_get_num_pairs _item_delete_method = mm.mmffio_delete_pair _item_class = _FFIOPair class _FFIOVdwtypeContainer(_FFIOSubBlockContainer): _item_count_method = mm.mmffio_ff_get_num_vdwtypes _item_delete_method = mm.mmffio_delete_vdwtype _item_class = _FFIOVdwtype class _FFIOVdwtypescombinedContainer(_FFIOSubBlockContainer): _item_count_method = mm.mmffio_ff_get_num_vdwtypescombineds _item_delete_method = mm.mmffio_delete_vdwtypescombined _item_class = _FFIOVdwtypescombined class _FFIORestraintContainer(_FFIOSubBlockContainer): _item_count_method = mm.mmffio_ff_get_num_restraints _item_delete_method = mm.mmffio_delete_restraint _item_class = _FFIORestraint class _FFIOVirtualContainer(_FFIOSubBlockContainer): _item_count_method = mm.mmffio_ff_get_num_virtuals _item_delete_method = mm.mmffio_delete_virtual _item_class = _FFIOVirtual class _FFIOPseudoContainer(_FFIOSubBlockContainer): _item_count_method = mm.mmffio_ff_get_num_pseudos _item_delete_method = mm.mmffio_delete_pseudo _item_class = _FFIOPseudo class _FFIOConstraintContainer(_FFIOSubBlockContainer): _item_count_method = mm.mmffio_ff_get_num_constraints _item_delete_method = mm.mmffio_delete_constraint _item_class = _FFIOConstraint class _FFIOPosFBHWContainer(_FFIOSubBlockContainer): _item_count_method = mm.mmffio_ff_get_num_posfbhws _item_delete_method = mm.mmffio_delete_posfbhw _item_class = _FFIOPosFBHW class _FFIOAngleFBHWContainer(_FFIOSubBlockContainer): _item_count_method = mm.mmffio_ff_get_num_anglefbhws _item_delete_method = mm.mmffio_delete_anglefbhw _item_class = _FFIOAngleFBHW class _FFIOImproperFBHWContainer(_FFIOSubBlockContainer): _item_count_method = mm.mmffio_ff_get_num_improperfbhws _item_delete_method = mm.mmffio_delete_improperfbhw _item_class = _FFIOImproperFBHW class _FFIOStretchFBHWContainer(_FFIOSubBlockContainer): _item_count_method = mm.mmffio_ff_get_num_stretchfbhws _item_delete_method = mm.mmffio_delete_stretchfbhw _item_class = _FFIOStretchFBHW class _ElementProperty(_BlockPropertyBase): """ A dictionary of other_block element based properties. These can be accessed via the property name as it appears in the maestro file. Property names must be m2io data names, which are in the format '<type>_<author>_<property_name>', where '<type>' is a data type prefix, '<author>' is a source specification, and '<property_name>' is the actual name of the data. The data type prefix can specified as 's' for string, 'i' for integer, 'r' for real and 'b' for boolean. The author specification should be 'user' for user created properties. The property name can have embedded underscores. Some example m2io datanames are 'r_m_x_coord', which indicates a real maestro property named 'x coord', and 'i_user_my_count' which indicates an integer user property named 'my count'. """ def __init__(self, handle, block_num, index): """Create an instance of the property dictionary. """ self.handle = handle self._block_num = block_num self._index = index def _getBoolean(self, block_num, index, name): raise NotImplementedError() def _getInt(self, block_num, index, name): raise NotImplementedError() def _getReal(self, block_num, index, name): raise NotImplementedError() def _getString(self, block_num, index, name): raise NotImplementedError() def _setBoolean(self, block_num, index, name, value): raise NotImplementedError() def _setInt(self, block_num, index, name, value): raise NotImplementedError() def _setReal(self, block_num, index, name, value): raise NotImplementedError() def _setString(self, block_num, index, name, value): raise NotImplementedError() def _data_names_method(self, block_num, index, flag): raise NotImplementedError() def __getitem__(self, item): """ Return the given item if it is a valid property for this element, None if not. """ try: if item[0] == "s": ret = self._getString(self.handle, self._block_num, self._index, item) elif item[0] == "i": ret = self._getInt(self.handle, self._block_num, self._index, item) elif item[0] == "r": ret = self._getReal(self.handle, self._block_num, self._index, item) elif item[0] == "b": ret = self._getBoolean(self.handle, self._block_num, self._index, item) else: raise KeyError("%s is an invalid dataname" % item) except mm.MmException as e: raise KeyError("%s is not the name of an element property" % item) except IndexError as e: if item == "": raise KeyError("The empty string is an invalid dataname.") else: raise return ret def __setitem__(self, item, value): """Set element properties """ if item[0] == "s": self._setString(self.handle, self._block_num, self._index, item, value) elif item[0] == "i": self._setInt(self.handle, self._block_num, self._index, item, value) elif item[0] == "r": self._setReal(self.handle, self._block_num, self._index, item, value) elif item[0] == "b": self._setBoolean(self.handle, self._block_num, self._index, item, value) else: raise KeyError( "%s is an invalid property name; see _FFIOElementProperty documentation for details." % item) def keys(self): """ Return a list of the names of all properties. """ return self._data_names_method(self.handle, self._block_num, self._index, mm.M2IO_ALL_TYPES) class _FFIOElementProperty(_ElementProperty): _getInt = mm.mmffio_element_prop_get_int _getReal = mm.mmffio_element_prop_get_real _getBoolean = mm.mmffio_element_prop_get_boolean _getString = mm.mmffio_element_prop_get_string _setInt = mm.mmffio_element_prop_set_int _setReal = mm.mmffio_element_prop_set_real _setBoolean = mm.mmffio_element_prop_set_boolean _setString = mm.mmffio_element_prop_set_string _data_names_method = mm.mmffio_element_get_data_names class _Element: def __init__(self, handle, block_num, index): self.handle = handle self._block_num = block_num self._index = index self._property = None _property_class = None # property def _getElementProperty(self): if self._property is None: self._property = self._property_class(self.handle, self._block_num, self._index) return self._property property = property(_getElementProperty, doc="Element property dictionary") class _FFIOElement(_Element): _property_class = _FFIOElementProperty class _OtherBlock: def __init__(self, handle, block_num): self.handle = handle self._block_num = block_num # index def _getIndex(self): return self._block_num index = property(_getIndex, doc="The block number. I{Read only.}") def _get_name_method(self, block_num): raise NotImplementedError() def _set_name_method(self, block_num, value): raise NotImplementedError() def _num_elements_method(self, block_num): raise NotImplementedError() def _add_elements_method(handle, block_num, num_elements): raise NotImplementedError() def _delete_element_method(handle, block_num, element_num): raise NotImplementedError() _element_class = None def _getName(self): return self._get_name_method(self.handle, self._block_num) def _setName(self, value): self._set_name_method(self.handle, self._block_num, value) name = property(_getName, _setName, doc="other block name.") def __getitem__(self, index): """ Return the wrapper class for ffio constraint based properties. Note that the initial index is 1, as per the underlying library, not 0 as is expected for python. """ if index < 1 or index > len(self): raise IndexError("Index out of range [1, %d]: %d" % ( len(self), index, )) return self._element_class(self.handle, self._block_num, index) def __iter__(self): """ Provide iteration access. """ for i in range(1, len(self) + 1): yield self._element_class(self.handle, self._block_num, i) def __len__(self): """ Return the number of elements in this block """ return self._num_elements_method(self.handle, self._block_num) def addElement(self): """ Add a new element to this block and return it. """ self._add_elements_method(self.handle, self._block_num, 1) return self._element_class(self.handle, self._block_num, len(self)) def addElements(self, num_elements): """ Add the specified number of other elements to this block. """ self._add_elements_method(self.handle, self._block_num, num_elements) def deleteElements(self, indices): """ Delete multiple elements from this block. The argument indices must be a sequence or an iterable, and able to be interpreted as ints. After deletion, indices are renumbered from 1 to len(self). """ # Be sure that deleting is done from highest index to lowest. indices = [int(i) for i in indices] indices.sort() indices.reverse() for i in indices: self._delete_element_method(self.handle, self._block_num, i) class _FFIOOtherBlock(_OtherBlock): _get_name_method = mm.mmffio_other_block_get_name _set_name_method = mm.mmffio_other_block_set_name _element_class = _FFIOElement _num_elements_method = mm.mmffio_ff_get_num_elements _add_elements_method = mm.mmffio_add_elements _delete_element_method = mm.mmffio_delete_element class _OtherBlockContainer: """The class to provide access to other block instances. """ def __init__(self, handle): """ Initialize the container. """ self.handle = handle _other_block_class = None def _block_names_method(handle): raise NotImplementedError() def __getitem__(self, index): """ Return an other block instance """ if index < 0 or index > len(self): raise IndexError("Index out of range [1, %d]: %d" % ( len(self), index, )) return self._other_block_class(self.handle, index) def __iter__(self): """ Provide iteration access. """ for i in range(0, len(self)): yield self._other_block_class(self.handle, i) def __len__(self): """ Return the number of other blocks. """ return len(self._block_names_method(self.handle)) class _FFIOOtherBlockContainer(_OtherBlockContainer): _other_block_class = _FFIOOtherBlock _block_names_method = mm.mmffio_other_get_block_names class _FFIOProperty(_BlockPropertyBase): """ A dictionary of ffio properties, with all dict methods. Properties can be accessed via the m2io dataname as it appears in the maestro file. Property names must be m2io data names, which are in the format '<type>_<author>_<property_name>', where '<type>' is a data type prefix, '<author>' is a source specification, and '<property_name>' is the actual name of the data. The data type prefix can specified as 's' for string, 'i' for integer, 'r' for real and 'b' for boolean. The author specification should be 'user' for user created properties. The property name can have embedded underscores. Some example m2io datanames are 'r_m_x_coord', which indicates a real maestro property named 'x coord', and 'i_user_my_count' which indicates an integer user property named 'my count'. To convert to the _FFIOProperty to a real dictionary:: d = dict(st.ffio.property) """ def __init__(self, handle): """ Create an instance of the property 'dictionary' The instance is created when st.property is first used. """ self._handle = handle def __getitem__(self, item): if item == 's_ffio_name': return mm.mmffio_ff_get_name(self._handle) if item == 'i_ffio_version': return mm.mmffio_ff_get_version(self._handle) if item == 's_ffio_comb_rule': return mm.mmffio_ff_get_comb_rule(self._handle) try: if item[0] == "s": ret = mm.mmffio_ff_get_string_data(self._handle, item) elif item[0] == "i": ret = mm.mmffio_ff_get_int_data(self._handle, item) elif item[0] == "r": ret = mm.mmffio_ff_get_real_data(self._handle, item) elif item[0] == "b": ret = mm.mmffio_ff_get_boolean_data(self._handle, item) else: raise KeyError("%s is an invalid dataname" % item) return ret except mm.MmException as e: if e.rc == mm.MMFFIO_ERR: raise KeyError("%s is not the name of a ffio property" % item) except IndexError as e: if item == "": raise KeyError("The empty string is an invalid dataname.") else: raise def __setitem__(self, item, value): """ Set the item into the additional data handle. Usage: st.ffio.property[<name>] = value """ if item == 's_ffio_name': return mm.mmffio_ff_set_name(self._handle, value) if item == 'i_ffio_version': return mm.mmffio_ff_set_version(self._handle, value) if item == 's_ffio_comb_rule': return mm.mmffio_ff_set_comb_rule(self._handle, value) if item[0] == "s": mm.mmffio_ff_add_string_data(self._handle, item, value) elif item[0] == "i": mm.mmffio_ff_add_int_data(self._handle, item, value) elif item[0] == "r": mm.mmffio_ff_add_real_data(self._handle, item, value) elif item[0] == "b": mm.mmffio_ff_add_boolean_data(self._handle, item, value) else: raise KeyError( "%s is an invalid property name; see _FFIOProperty documentation for details." % item) def keys(self): """ Return a list of the names of all properties. """ ret = ['s_ffio_name', 'i_ffio_version', 's_ffio_comb_rule'] pnames = mm.mmffio_ff_get_data_names(self._handle, mm.M2IO_ALL_TYPES) return ret + pnames class _FFIO(): """ A class to make access of ffio block pythonic. """ def __init__(self, ffhandle): self.handle = ffhandle self._other_block = None self._property = None # name def _setName(self, name): """ Set the name for this ffio block """ mm.mmffio_ff_set_name(self.handle, name) def _getName(self): """ Get the name for this ffio block """ return mm.mmffio_ff_get_name(self.handle) name = property(_getName, _setName, doc="ffio block name") # version def _setVersion(self, version): """ Set the version """ mm.mmffio_ff_set_version(self.handle, version) def _getVersion(self): """ Get the version """ return mm.mmffio_ff_get_version(self.handle) version = property(_getVersion, _setVersion, doc="version") # combining rule def _setCombiningRule(self, combining_rule): """ Set the combining_rule """ mm.mmffio_ff_set_comb_rule(self.handle, combining_rule) def _getCombiningRule(self): """ Get the combining_rule """ return mm.mmffio_ff_get_comb_rule(self.handle) combining_rule = property(_getCombiningRule, _setCombiningRule, doc="combining rule") # # This section of the class definition consists of attributes to create # on the fly, to avoid the cost of constructing them up front. # # The CreateWhenNeeded class is a descriptor that allows an attribute # to be created on the fly. The first time the attribute is accessed, # the function is called; whatever it returns is stored as the # attribute and used in all future access. # def _createProxy(self): """ A method to create a proxy to be passed to subobjects that need to hold a reference to the parent FFIO object. This prevents cyclic references and therefore allows FFIO instances to be deallocated by reference counting rather than waiting for a garbage collection sweep. """ return _FFIO(self.handle) _proxy = CreateWhenNeeded(_createProxy, "_proxy") _doc = """ A list of ffio sites, each of which is a `_FFIOSite` instance. Example usage, where `st` is a FFIOStructure instance:: # Access an site siteobj = st.ffio.site[n] # Delete an site del st.ffio.site[n] # Find the number of sites len(st.ffio.site) # Iterate over all sites for site in st.ffio.site: take_some_action(site) :note: As with many other collections, the contents of the site list should not be modified through additions or deletions while you are iterating over it. """ def _createSiteContainer(self): return _FFIOSiteContainer(self._proxy) site = CreateWhenNeeded(_createSiteContainer, "site", _doc) _doc = """ A list of ffio bonds, each of which is a `_FFIOBond` instance. Example usage, where `st` is a FFIOStructure instance:: # Access an bond bondobj = st.ffio.bond[n] # Delete an bond del st.ffio.bond[n] # Find the number of bonds len(st.ffio.bond) # Iterate over all bonds for bond in st.ffio.bond: take_some_action(bond) :note: As with many other collections, the contents of the bond list should not be modified through additions or deletions while you are iterating over it. """ def _createFFIOBondContainer(self): return _FFIOBondContainer(self._proxy) bond = CreateWhenNeeded(_createFFIOBondContainer, "bond", _doc) _doc = """ A list of ffio angles, each of which is a `_FFIOAngle` instance. Example usage, where `st` is a FFIOStructure instance:: # Access an angle angleobj = st.ffio.angle[n] # Delete an angle del st.ffio.angle[n] # Find the number of angles len(st.ffio.angle) # Iterate over all angles for angle in st.ffio.angle: take_some_action(angle) :note: As with many other collections, the contents of the angle list should not be modified through additions or deletions while you are iterating over it. """ def _createFFIOAngleContainer(self): return _FFIOAngleContainer(self._proxy) angle = CreateWhenNeeded(_createFFIOAngleContainer, "angle", _doc) _doc = """ A list of ffio dihedrals, each of which is a `_FFIODihedral` instance. Example usage, where `st` is a FFIOStructure instance:: # Access an dihedral dihedralobj = st.ffio.dihedral[n] # Delete an dihedral del st.ffio.dihedral[n] # Find the number of dihedrals len(st.ffio.dihedral) # Iterate over all dihedrals for dihedral in st.ffio.dihedral: take_some_action(dihedral) :note: As with many other collections, the contents of the dihedral list should not be modified through additions or deletions while you are iterating over it. """ def _createFFIODihedralContainer(self): return _FFIODihedralContainer(self._proxy) dihedral = CreateWhenNeeded(_createFFIODihedralContainer, "dihedral", _doc) _doc = """ A list of ffio exclusions, each of which is a `_FFIOExclusion` instance. Example usage, where `st` is a FFIOStructure instance:: # Access an exclusion exclusionobj = st.ffio.exclusion[n] # Delete an exclusion del st.ffio.exclusion[n] # Find the number of exclusions len(st.ffio.exclusion) # Iterate over all exclusions for exclusion in st.ffio.exclusion: take_some_action(exclusion) :note: As with many other collections, the contents of the exclusion list should not be modified through additions or deletions while you are iterating over it. """ def _createFFIOExclusionContainer(self): return _FFIOExclusionContainer(self._proxy) exclusion = CreateWhenNeeded(_createFFIOExclusionContainer, "exclusion", _doc) _doc = """ A list of ffio pairs, each of which is a `_FFIOPair` instance. Example usage, where `st` is a FFIOStructure instance:: # Access an pair pairobj = st.ffio.pair[n] # Delete an pair del st.ffio.pair[n] # Find the number of pairs len(st.ffio.pair) # Iterate over all pairs for pair in st.ffio.pair: take_some_action(pair) :note: As with many other collections, the contents of the site list should not be modified through additions or deletions while you are iterating over it. """ def _createFFIOPairContainer(self): return _FFIOPairContainer(self._proxy) pair = CreateWhenNeeded(_createFFIOPairContainer, "pair", _doc) _doc = """ A list of ffio vdwtypes, each of which is a `_FFIOVdwtype` instance. Example usage, where `st` is a FFIOStructure instance:: # Access an vdwtype vdwtypeobj = st.ffio.vdwtype[n] # Delete an vdwtype del st.ffio.vdwtype[n] # Find the number of vdwtypes len(st.ffio.vdwtype) # Iterate over all vdwtypes for vdwtype in st.ffio.vdwtype: take_some_action(vdwtype) :note: As with many other collections, the contents of the vdwtype list should not be modified through additions or deletions while you are iterating over it. """ def _createFFIOVdwtypeContainer(self): return _FFIOVdwtypeContainer(self._proxy) vdwtype = CreateWhenNeeded(_createFFIOVdwtypeContainer, "vdwtype", _doc) _doc = """ A list of ffio restraints, each of which is a `_FFIORestraint` instance. Example usage, where `st` is a FFIOStructure instance:: # Access an restraint restraintobj = st.ffio.restraint[n] # Delete an restraint del st.ffio.restraint[n] # Find the number of restraints len(st.ffio.restraint) # Iterate over all restraints for restraint in st.ffio.restraint: take_some_action(restraint) :note: As with many other collections, the contents of the restraint list should not be modified through additions or deletions while you are iterating over it. """ def _createFFIOVdwtypescombinedContainer(self): return _FFIOVdwtypescombinedContainer(self._proxy) vdwtypescombined = CreateWhenNeeded(_createFFIOVdwtypescombinedContainer, "vdwtypescombined", _doc) _doc = """ A list of ffio vdwtypes in the vdwtypes_combined block, each of which is a `_FFIOVdwcombined` instance. """ def _createFFIORestraintContainer(self): return _FFIORestraintContainer(self._proxy) restraint = CreateWhenNeeded(_createFFIORestraintContainer, "restraint", _doc) _doc = """ A list of ffio virtuals, each of which is a `_FFIOVirtual` instance. Example usage, where `st` is a FFIOStructure instance:: # Access an virtual virtualobj = st.ffio.virtual[n] # Delete an virtual del st.ffio.virtual[n] # Find the number of virtuals len(st.ffio.virtual) # Iterate over all virtuals for virtual in st.ffio.virtual: take_some_action(virtual) :note: As with many other collections, the contents of the virtual list should not be modified through additions or deletions while you are iterating over it. """ def _createFFIOVirtualContainer(self): return _FFIOVirtualContainer(self._proxy) virtual = CreateWhenNeeded(_createFFIOVirtualContainer, "virtual", _doc) _doc = """ A list of ffio pseudos, each of which is a `_FFIOPseudo` instance. Example usage, where `st` is a FFIOStructure instance:: # Access an pseudo pseudoobj = st.ffio.pseudo[n] # Delete an pseudo del st.ffio.pseudo[n] # Find the number of pseudos len(st.ffio.pseudo) # Iterate over all pseudos for pseudo in st.ffio.pseudo: take_some_action(pseudo) :note: As with many other collections, the contents of the pseudo list should not be modified through additions or deletions while you are iterating over it. """ def _createFFIOPseudoContainer(self): return _FFIOPseudoContainer(self._proxy) pseudo = CreateWhenNeeded(_createFFIOPseudoContainer, "pseudo", _doc) _doc = """ A list of ffio constraints, each of which is a `_FFIOConstraint` instance. Example usage, where `st` is a FFIOStructure instance:: # Access an constraint constraintobj = st.ffio.constraint[n] # Delete an constraint del st.ffio.constraint[n] # Find the number of constraints len(st.ffio.constraint) # Iterate over all constraints for constraint in st.ffio.constraint: take_some_action(constraint) :note: As with many other collections, the contents of the constraint list should not be modified through additions or deletions while you are iterating over it. """ def _createFFIOConstraintContainer(self): return _FFIOConstraintContainer(self._proxy) constraint = CreateWhenNeeded(_createFFIOConstraintContainer, "constraint", _doc) _doc = """ A list of ffio position flat bottoms, each of which is a `_FFIOPosFBHW` instance. Example usage, where `st` is a FFIOStructure instance:: # Access an position flat bottom posfbhwobj = st.ffio.posfbhw[n] # Delete an position flat bottom del st.ffio.posfbhw[n] # Find the number of position flat bottoms len(st.ffio.posfbhw) # Iterate over all position flat bottoms for posfbhw in st.ffio.posfbhw: take_some_action(posfbhw) """ def _createFFIOPosFBHWContainer(self): return _FFIOPosFBHWContainer(self._proxy) posfbhw = CreateWhenNeeded(_createFFIOPosFBHWContainer, "posfbhw", _doc) _doc = """ A list of ffio angle flat bottoms, each of which is a `_FFIOAngleFBHW` instance. Example usage, where `st` is a FFIOStructure instance:: # Access an angle flat bottom anglefbhwobj = st.ffio.anglefbhw[n] # Delete an angle flat bottom del st.ffio.anglefbhw[n] # Find the number of angle flat bottoms len(st.ffio.anglefbhw) # Iterate over all angle flat bottoms for anglefbhw in st.ffio.anglefbhw: take_some_action(anglefbhw) """ def _createFFIOAngleFBHWContainer(self): return _FFIOAngleFBHWContainer(self._proxy) anglefbhw = CreateWhenNeeded(_createFFIOAngleFBHWContainer, "anglefbhw", _doc) _doc = """ A list of ffio improper flat bottoms, each of which is a `_FFIOImproperFBHW` instance. Example usage, where `st` is a FFIOStructure instance:: # Access an improper flat bottom improperfbhwobj = st.ffio.improperfbhw[n] # Delete an improper flat bottom del st.ffio.improperfbhw[n] # Find the number of improper flat bottoms len(st.ffio.improperfbhw) # Iterate over all improper flat bottoms for improperfbhw in st.ffio.improperfbhw: take_some_action(improperfbhw) """ def _createFFIOImproperFBHWContainer(self): return _FFIOImproperFBHWContainer(self._proxy) improperfbhw = CreateWhenNeeded(_createFFIOImproperFBHWContainer, "improperfbhw", _doc) _doc = """ A list of ffio stretch flat bottoms, each of which is a `_FFIOStretchFBHW` instance. Example usage, where `st` is a FFIOStructure instance:: # Access an stretch flat bottom stretchfbhwobj = st.ffio.stretchfbhw[n] # Delete an stretch flat bottom del st.ffio.stretchfbhw[n] # Find the number of stretch flat bottoms len(st.ffio.stretchfbhw) # Iterate over all stretch flat bottoms for stretchfbhw in st.ffio.stretchfbhw: take_some_action(stretchfbhw) """ def _createFFIOStretchFBHWContainer(self): return _FFIOStretchFBHWContainer(self._proxy) stretchfbhw = CreateWhenNeeded(_createFFIOStretchFBHWContainer, "stretchfbhw", _doc) # other_block _doc = """ A list of ffio other blocks, each of which is a `_FFIOOtherBlock` instance. Example usage, where `st` is a FFIOStructure instance:: # Access an other block other_block = st.ffio.other_block[n] # Delete an other block del st.ffio.other_block[n] # Find the number of other blocks len(st.ffio.other_block) # Iterate over all other_blocks for other_block in st.ffio.other_block: take_some_action(other_block) :note: As with many other collections, the contents of the other block list should not be modified through additions or deletions while you are iterating over it. """ def _createFFIOOtherBlockContainer(self): return _FFIOOtherBlockContainer(self.handle) other_block = CreateWhenNeeded(_createFFIOOtherBlockContainer, "other_block", _doc) def getOtherBlockNames(self): return mm.mmffio_other_get_block_names(self.handle) # property def _getFFIOProperty(self): if self._property is None: self._property = _FFIOProperty(self.handle) return self._property property = property( _getFFIOProperty, doc="A dictionary of ffio properties. Keys are strings of " "the form C{type_family_name} as described in the " "L{PropertyName} documentation and found in C{.cms} files.") def addSite(self): """ Add a new site to this ffio block, and return its Site object. """ mm.mmffio_add_sites(self.handle, 1) return self.site[len(self.site)] def addBond(self): """ Add a new bond to this ffio block, and return its Bond object. """ mm.mmffio_add_bonds(self.handle, 1) return self.bond[len(self.bond)] def addAngle(self): """ Add a new angle to this ffio block, and return its Angle object. """ mm.mmffio_add_angles(self.handle, 1) return self.angle[len(self.angle)] def addDihedral(self): """ Add a new dihedral to this ffio block, and return its Dihedral object. """ mm.mmffio_add_diheds(self.handle, 1) return self.dihedral[len(self.dihedral)] def addExclusion(self): """ Add a new exclusion to this ffio block, and return its Exclusion object. """ mm.mmffio_add_excls(self.handle, 1) return self.exclusion[len(self.exclusion)] def addPair(self): """ Add a new pair to this ffio block, and return its Pair object. """ mm.mmffio_add_pairs(self.handle, 1) return self.pair[len(self.pair)] def addVdwtype(self): """ Add a new vdwtype to this ffio block, and return its Vdwtype object. """ mm.mmffio_add_vdwtypes(self.handle, 1) return self.vdwtype[len(self.vdwtype)] def addVdwtypescombined(self): """ Add a new vdwtypescombined item to this ffio block, and return its Vdwtypescombined object. """ mm.mmffio_add_vdwtypescombineds(self.handle, 1) return self.vdwtypescombined[len(self.vdwtypescombined)] def addRestraint(self): """ Add a new restraint to this ffio block, and return its Restraint object. """ mm.mmffio_add_restraints(self.handle, 1) return self.restraint[len(self.restraint)] def addVirtual(self): """ Add a new virtual to this ffio block, and return its Virtual object. """ mm.mmffio_add_virtuals(self.handle, 1) return self.virtual[len(self.virtual)] def addPseudo(self): """ Add a new pseudo to this ffio block, and return its Pseudo object. """ mm.mmffio_add_pseudos(self.handle, 1) return self.pseudo[len(self.pseudo)] def addConstraint(self): """ Add a new constraint to this ffio block, and return its Constraint object. """ mm.mmffio_add_constraints(self.handle, 1) return self.constraint[len(self.constraint)] def addPosfbhw(self): mm.mmffio_add_posfbhws(self.handle, 1) return self.posfbhw[len(self.posfbhw)] def addAnglefbhw(self): mm.mmffio_add_anglefbhws(self.handle, 1) return self.anglefbhw[len(self.anglefbhw)] def addImproperfbhw(self): mm.mmffio_add_improperfbhws(self.handle, 1) return self.improperfbhw[len(self.improperfbhw)] def addStretchfbhw(self): mm.mmffio_add_stretchfbhws(self.handle, 1) return self.stretchfbhw[len(self.stretchfbhw)] def addOtherBlock(self, name): """ Add a new other block to this ffio block, and return it. """ length = len(self.other_block) mm.mmffio_add_other_blocks(self.handle, 1) mm.mmffio_other_block_set_name(self.handle, length, name) return self.other_block[length] def addSites(self, num_sites): """ Add the specified number of sites to this ffio block. """ mm.mmffio_add_sites(self.handle, num_sites) def addBonds(self, num_bonds): """ Add the specified number of bonds to this ffio block. """ mm.mmffio_add_bonds(self.handle, num_bonds) def addAngles(self, num_angles): """ Add the specified number of angles to this ffio block. """ mm.mmffio_add_angles(self.handle, num_angles) def addDihedrals(self, num_dihedrals): """ Add the specified number of dihedrals to this ffio block. """ mm.mmffio_add_diheds(self.handle, num_dihedrals) def addExclusions(self, num_exclusions): """ Add the specified number of exclusions to this ffio block. """ mm.mmffio_add_excls(self.handle, num_exclusions) def addPairs(self, num_pairs): """ Add the specified number of pairs to this ffio block. """ mm.mmffio_add_pairs(self.handle, num_pairs) def addVdwtypes(self, num_vdwtypes): """ Add the specified number of vdwtypes to this ffio block. """ mm.mmffio_add_vdwtypes(self.handle, num_vdwtypes) def addVdwtypescombineds(self, num_vdwtypes): """ Add the specified number of vdwtypes to this ffio block. """ mm.mmffio_add_vdwtypescombined(self.handle, num_vdwtypes) def addRestraints(self, num_restraints): """ Add the specified number of restraints to this ffio block. """ mm.mmffio_add_restraints(self.handle, num_restraints) def addVirtuals(self, num_virtuals): """ Add the specified number of virtuals to this ffio block. """ mm.mmffio_add_virtuals(self.handle, num_virtuals) def addPseudos(self, num_pseudos): """ Add the specified number of pseudos to this ffio block. """ mm.mmffio_add_pseudos(self.handle, num_pseudos) def addConstraints(self, num_constraints): """ Add the specified number of constraints to this ffio block. """ mm.mmffio_add_constraints(self.handle, num_constraints) def addPosfbhws(self, num_posfbhws): mm.mmffio_add_posfbhws(self.handle, num_posfbhws) def addAnglefbhws(self, num_anglefbhws): mm.mmffio_add_anglefbhws(self.handle, num_anglefbhws) def addImproperfbhws(self, num_improperfbhws): mm.mmffio_add_improperfbhws(self.handle, num_improperfbhws) def addStretchfbhws(self, num_stretchfbhws): mm.mmffio_add_stretchfbhws(self.handle, num_stretchfbhws) def _deleteItems(self, indices, delete_method): """ Delete multiple item from ffio sub-block. The argument indices must be a sequence or an iterable, and able to be interpreted as ints. After deletion, indices are renumbered from 1 to len(<sub-block>). Pre-existing references to items will not be correct, as they store index values. """ # Be sure that deleting is done from highest index to lowest. indices = [int(i) for i in indices] indices.sort() indices.reverse() for i in indices: delete_method(self.handle, i) def deleteSites(self, indices): self._deleteItems(indices, mm.mmffio_delete_site) def deleteBonds(self, indices): self._deleteItems(indices, mm.mmffio_delete_bond) def deleteAngles(self, indices): self._deleteItems(indices, mm.mmffio_delete_angle) def deleteDihedrals(self, indices): self._deleteItems(indices, mm.mmffio_delete_dihed) def deleteExclusions(self, indices): self._deleteItems(indices, mm.mmffio_delete_excl) def deletePairs(self, indices): self._deleteItems(indices, mm.mmffio_delete_pair) def deleteVdwtypes(self, indices): self._deleteItems(indices, mm.mmffio_delete_vdwtype) def deleteVdwtypescombined(self, indices): self._deleteItems(indices, mm.mmffio_delete_vdwtypescombined) def deleteRestraints(self, indices): self._deleteItems(indices, mm.mmffio_delete_restraint) def deleteVirtuals(self, indices): self._deleteItems(indices, mm.mmffio_delete_virtual) def deletePseudos(self, indices): self._deleteItems(indices, mm.mmffio_delete_pseudo) def deleteConstraints(self, indices): self._deleteItems(indices, mm.mmffio_delete_constraint) def deletePosfbhws(self, indices): self._deleteItems(indices, mm.mmffio_delete_posfbhw) def deleteAnglefbhws(self, indices): self._deleteItems(indices, mm.mmffio_delete_anglefbhw) def deleteImproperfbhws(self, indices): self._deleteItems(indices, mm.mmffio_delete_improperfbhw) def deleteStretchfbhws(self, indices): self._deleteItems(indices, mm.mmffio_delete_stretchfbhw) def deleteOtherBlocks(self, indices): """ Delete multiple other blocks from the ffio block. The argument indices must be a sequence or an iterable, and able to be interpreted as ints. After deletion, indices are renumbered from 0 to len(other_block) - 1. """ # Be sure that deleting is done from highest index to lowest. indices = [int(i) for i in indices] indices.sort() indices.reverse() for i in indices: mm.mmffio_delete_other_block(self.handle, i) """ FEPIO related classes """ # Dictionaries linking built-in property names of different standard fepio blocks # to thier short names(to be used to get/set from python interfaces), mmfepio getter and # setter methods. When the value is empty list, they are deduced from property names fepio_sub_block_prop_links = { 'atommap': { 'i_fepio_ai': [ 'ai', mm.mmfepio_atommap_get_ai, mm.mmfepio_atommap_set_ai ], 'i_fepio_aj': [ 'aj', mm.mmfepio_atommap_get_aj, mm.mmfepio_atommap_set_aj ] }, 'bondmap': { 'i_fepio_ai': [], 'i_fepio_aj': [], 'i_fepio_ti': [], 'i_fepio_tj': [] }, 'anglemap': { 'i_fepio_ai': [], 'i_fepio_aj': [], 'i_fepio_ak': [], 'i_fepio_ti': [], 'i_fepio_tj': [] }, 'dihedmap': { 'i_fepio_ai': [], 'i_fepio_aj': [], 'i_fepio_ak': [], 'i_fepio_al': [], 'i_fepio_ti': [], 'i_fepio_tj': [] }, 'exclmap': { 'i_fepio_ai': [], 'i_fepio_aj': [], 'i_fepio_ti': [], 'i_fepio_tj': [] }, 'pairmap': { 'i_fepio_ai': [], 'i_fepio_aj': [], 'i_fepio_ti': [], 'i_fepio_tj': [] } } class _FEPIOElementProperty(_ElementProperty): _getInt = mm.mmfepio_element_prop_get_int _getReal = mm.mmfepio_element_prop_get_real _getBoolean = mm.mmfepio_element_prop_get_boolean _getString = mm.mmfepio_element_prop_get_string _setInt = mm.mmfepio_element_prop_set_int _setReal = mm.mmfepio_element_prop_set_real _setBoolean = mm.mmfepio_element_prop_set_boolean _setString = mm.mmfepio_element_prop_set_string _data_names_method = mm.mmfepio_element_get_data_names class _FEPIOElement(_Element): _property_class = _FEPIOElementProperty class _FEPIOOtherBlock(_OtherBlock): _get_name_method = mm.mmfepio_other_block_get_name _set_name_method = mm.mmfepio_other_block_set_name _element_class = _FEPIOElement _num_elements_method = mm.mmfepio_fep_get_num_elements _add_elements_method = mm.mmfepio_add_elements _delete_element_method = mm.mmfepio_delete_element class _FEPIOOtherBlockContainer(_OtherBlockContainer): _other_block_class = _FEPIOOtherBlock _block_names_method = mm.mmfepio_other_get_block_names class _FEPIOSubBlockProperty(_BlockPropertyBase): """ A dictionary of fepio sub-block properties. These can be accessed via the property name as it appears in the maestro file. Property names must be m2io data names, which are in the format '<type>_<author>_<property_name>', where '<type>' is a data type prefix, '<author>' is a source specification, and '<property_name>' is the actual name of the data. The data type prefix can specified as 's' for string, 'i' for integer, 'r' for real and 'b' for boolean. The author specification should be 'user' for user created properties. The property name can have embedded underscores. Some example m2io datanames are 'r_fepio_x_coord', which indicates a real property named 'x coord', and 'i_user_my_count' which indicates an integer user property named 'my count'. """ def __init__(self, fep, index, type): """Create an instance of the property dictionary. """ self._fep = fep self._index = index self._type = type def __getitem__(self, item): """ Return the given item if it is a valid property for this item, None if not. """ # Check to see if property is built-in: builtin = fepio_sub_block_prop_links.get(self._type).get(item) if builtin is not None: short_name = get_fepio_sub_block_std_prop_short_name( self._type, item) return getattr( getattr(self._fep, self._type)[self._index], short_name) try: func = getattr( mm, "mmfepio_%s_prop_get_%s" % (self._type, prop_type_names[item[0]])) ret = func(self._fep.handle, item, self._index) except mm.MmException as e: raise KeyError("%s is not the name of an %s property" % (item, self._type)) except IndexError as e: if item == "": raise KeyError("The empty string is an invalid dataname.") else: raise return ret def __setitem__(self, item, value): """Set properties for the given item. """ builtin = fepio_sub_block_prop_links.get(self._type).get(item) if builtin is not None: short_name = get_fepio_sub_block_std_prop_short_name( self._type, item) setattr( getattr(self._fep, self._type)[self._index], short_name, value) return func = getattr( mm, "mmfepio_%s_prop_set_%s" % (self._type, prop_type_names[item[0]])) func(self._fep.handle, item, self._index, value) def keys(self): """ Return a list of all property names. """ func = getattr(mm, "mmfepio_%s_get_data_names" % self._type) pnames = func(self._fep.handle, self._index, mm.M2IO_ALL_TYPES) return list(fepio_sub_block_prop_links.get(self._type)) + pnames class _FEPIOSubBlock: """ A class to make access of fepio sub-block properties more pythonic. """ block_type = None def __init__(self, fep, _index): self._fep = fep self._index = _index self._property = None def __eq__(self, that): """ Compare on fep handle and index. """ if self._fep == that._fep and self._index == that._index and type( self) == type(that): return True else: return False def __ne__(self, other): """ Check for inequality based on fep handle and index. """ return not self.__eq__(other) def __hash__(self): return hash(self._fep.handle) ^ self._index def __index__(self): return self._index def __str__(self): return "%s(%d)" % (self.block_type, int(self)) # index def _getIndex(self): return self._index index = property(_getIndex, doc="The item index. I{Read only.}") # property def _getProperty(self): if self._property is None: self._property = _FEPIOSubBlockProperty(self._fep, self._index, self.block_type) return self._property property = property(_getProperty, doc="property dictionary")
[docs]def make_property_fepio(getter, setter, doc): def _get(self): return getter(self._fep.handle, self._index) def _set(self, value): setter(self._fep.handle, self._index, value) return property(_get, _set, doc=doc)
[docs]def get_fepio_sub_block_std_prop_short_name(block_type, prop_name): """ Function to get short names of properties. It is picked from the fepio_sub_block_prop_links dict if avaiable, else deduced from property name """ if fepio_sub_block_prop_links[block_type][prop_name] == []: short_name = prop_name.split('_')[-1] else: short_name = fepio_sub_block_prop_links[block_type][prop_name][0] return short_name
[docs]def get_fepio_sub_block_std_prop_getter(block_type, prop_name): """ Function to get mmfepio getter method of properties. It is picked from the fepio_sub_block_prop_links dict if avaiable, else deduced from property name """ if fepio_sub_block_prop_links[block_type][prop_name] == []: short_name = prop_name.split('_')[-1] func = getattr(mm, "mmfepio_%s_get_%s" % (block_type, short_name)) else: func = fepio_sub_block_prop_links[block_type][prop_name][1] return func
[docs]def get_fepio_sub_block_std_prop_setter(block_type, prop_name): """ Function to get mmfepio setter method of properties. It is picked from the fepio_sub_block_prop_links dict if avaiable, else deduced from property name """ if fepio_sub_block_prop_links[block_type][prop_name] == []: short_name = prop_name.split('_')[-1] func = getattr(mm, "mmfepio_%s_set_%s" % (block_type, short_name)) else: func = fepio_sub_block_prop_links[block_type][prop_name][2] return func
class _FEPIOSubBlockMeta(type): """ A meta-class to different fepio sub block type classses. This simplifies adding property descriptors for different sub-block types """ def __init__(cls, name, bases, dict): super(_FEPIOSubBlockMeta, cls).__init__(name, bases, dict) block_type = cls.block_type for prop in list(fepio_sub_block_prop_links[block_type]): prop_name = get_fepio_sub_block_std_prop_short_name( block_type, prop) prop_getter = get_fepio_sub_block_std_prop_getter(block_type, prop) prop_setter = get_fepio_sub_block_std_prop_setter(block_type, prop) doc = "%s %s" % (block_type, prop_name) setattr(cls, prop_name, make_property_fepio(prop_getter, prop_setter, doc)) class _FEPIOAtomMap(_FEPIOSubBlock, metaclass=_FEPIOSubBlockMeta): block_type = "atommap" class _FEPIOBondMap(_FEPIOSubBlock, metaclass=_FEPIOSubBlockMeta): block_type = "bondmap" class _FEPIOAngleMap(_FEPIOSubBlock, metaclass=_FEPIOSubBlockMeta): block_type = "anglemap" class _FEPIODihedMap(_FEPIOSubBlock, metaclass=_FEPIOSubBlockMeta): block_type = "dihedmap" class _FEPIOExclMap(_FEPIOSubBlock, metaclass=_FEPIOSubBlockMeta): block_type = "exclmap" class _FEPIOPairMap(_FEPIOSubBlock, metaclass=_FEPIOSubBlockMeta): block_type = "pairmap" class _FEPIOSubBlockContainer: def __init__(self, fep): """ Initialize the container. """ self._fep = fep def _item_count_method(self): raise NotImplementedError() def _delete_method(self, index): raise NotImplementedError() _item_class = None def __delitem__(self, index): """ Delete a fepio sub-block item from the CT. Note that this immediately updates the CT and therefore renumbers any items following the one deleted. """ if index < 1 or index > len(self): raise IndexError("Index out of range (starts at 1)") self._delete_method(self._fep.handle, index) def __getitem__(self, index): """ Return the wrapper class for fepio sub-block item based properties. Note that the initial index is 1, as per the underlying mmfepio library, not 0 as is expected for python. """ if index < 1 or index > len(self): raise IndexError("Index out of range (starts at 1)") return self._item_class(self._fep, index) def __iter__(self): """ Provide iteration access. """ for i in range(1, len(self) + 1): yield self._item_class(self._fep, i) def __len__(self): """ Return the number of items. """ return self._item_count_method(self._fep.handle) class _FEPIOAtomMapContainer(_FEPIOSubBlockContainer): _item_count_method = mm.mmfepio_fep_get_num_atommaps _item_delete_method = mm.mmfepio_delete_atommap _item_class = _FEPIOAtomMap class _FEPIOBondMapContainer(_FEPIOSubBlockContainer): _item_count_method = mm.mmfepio_fep_get_num_bondmaps _item_delete_method = mm.mmfepio_delete_bondmap _item_class = _FEPIOBondMap class _FEPIOAngleMapContainer(_FEPIOSubBlockContainer): _item_count_method = mm.mmfepio_fep_get_num_anglemaps _item_delete_method = mm.mmfepio_delete_anglemap _item_class = _FEPIOAngleMap class _FEPIODihedMapContainer(_FEPIOSubBlockContainer): _item_count_method = mm.mmfepio_fep_get_num_dihedmaps _item_delete_method = mm.mmfepio_delete_dihedmap _item_class = _FEPIODihedMap class _FEPIOExclMapContainer(_FEPIOSubBlockContainer): _item_count_method = mm.mmfepio_fep_get_num_exclmaps _item_delete_method = mm.mmfepio_delete_exclmap _item_class = _FEPIOExclMap class _FEPIOPairMapContainer(_FEPIOSubBlockContainer): _item_count_method = mm.mmfepio_fep_get_num_pairmaps _item_delete_method = mm.mmfepio_delete_pairmap _item_class = _FEPIOPairMap class _FEPIOProperty(_BlockPropertyBase): """ A dictionary of fepio properties, with all dict methods. Properties can be accessed via the m2io dataname as it appears in the maestro file. Property names must be m2io data names, which are in the format '<type>_<author>_<property_name>', where '<type>' is a data type prefix, '<author>' is a source specification, and '<property_name>' is the actual name of the data. The data type prefix can specified as 's' for string, 'i' for integer, 'r' for real and 'b' for boolean. The author specification should be 'user' for user created properties. The property name can have embedded underscores. Some example m2io datanames are 'r_m_x_coord', which indicates a real maestro property named 'x coord', and 'i_user_my_count' which indicates an integer user property named 'my count'. To convert to the _FEPIOProperty to a real dictionary:: d = dict(st.ffio.property) """ def __init__(self, handle): """ Create an instance of the property 'dictionary' The instance is created when st.property is first used. """ self._handle = handle def __getitem__(self, item): if item == 's_fepio_name': return mm.mmfepio_fep_get_name(self._handle) if item == constants.FEPIO_STAGE: return mm.mmfepio_fep_get_stage(self._handle) try: if item[0] == "s": ret = mm.mmfepio_fep_get_string_data(self._handle, item) elif item[0] == "i": ret = mm.mmfepio_fep_get_int_data(self._handle, item) elif item[0] == "r": ret = mm.mmfepio_fep_get_real_data(self._handle, item) elif item[0] == "b": ret = mm.mmfepio_fep_get_boolean_data(self._handle, item) else: raise KeyError("%s is an invalid dataname" % item) return ret except mm.MmException as e: if e.rc == mm.MMFFIO_ERR: raise KeyError("%s is not the name of a fepio property" % item) except IndexError as e: if item == "": raise KeyError("The empty string is an invalid dataname.") else: raise def __setitem__(self, item, value): """ Set the item into the additional data handle. Usage: st.fepio.property[<name>] = value """ if item == 's_fepio_name': return mm.mmfepio_fep_set_name(self._handle, value) if item == constants.FEPIO_STAGE: return mm.mmfepio_fep_set_version(self._handle, value) if item[0] == "s": mm.mmfepio_fep_add_string_data(self._handle, item, value) elif item[0] == "i": mm.mmfepio_fep_add_int_data(self._handle, item, value) elif item[0] == "r": mm.mmfepio_fep_add_real_data(self._handle, item, value) elif item[0] == "b": mm.mmfepio_fep_add_boolean_data(self._handle, item, value) else: raise KeyError( "%s is an invalid property name; see _FEPIOProperty documentation for details." % item) def keys(self): """ Return a list of the names of all properties. """ ret = ['s_fepio_name', constants.FEPIO_STAGE] pnames = mm.mmfepio_fep_get_data_names(self._handle, mm.M2IO_ALL_TYPES) return ret + pnames class _FEPIO(): """ A class to make access of fepio block pythonic. """ def __init__(self, handle): self.handle = handle self._other_block = None self._property = None # name def _setName(self, name): """ Set the name for this fepio block """ mm.mmfepio_fep_set_name(self.handle, name) def _getName(self): """ Get the name for this fepio block """ return mm.mmfepio_fep_get_name(self.handle) name = property(_getName, _setName, doc="fepio block name") # stage def _setStage(self, stage): """ Set the stage """ mm.mmfepio_fep_set_stage(self.handle, stage) def _getStage(self): """ Get the stage """ return mm.mmfepio_fep_get_stage(self.handle) stage = property(_getStage, _setStage, doc="stage") # # This section of the class definition consists of attributes to create # on the fly, to avoid the cost of constructing them up front. # # The CreateWhenNeeded class is a descriptor that allows an attribute # to be created on the fly. The first time the attribute is accessed, # the function is called; whatever it returns is stored as the # attribute and used in all future access. # def _createProxy(self): """ A method to create a proxy to be passed to subobjects that need to hold a reference to the parent FEPIO object. This prevents cyclic references and therefore allows FEPIO instances to be deallocated by reference counting rather than waiting for a garbage collection sweep. """ return _FEPIO(self.handle) _proxy = CreateWhenNeeded(_createProxy, "_proxy") def _createAtomMapContainer(self): return _FEPIOAtomMapContainer(self._proxy) atommap = CreateWhenNeeded(_createAtomMapContainer, "atommap") def _createBondMapContainer(self): return _FEPIOBondMapContainer(self._proxy) bondmap = CreateWhenNeeded(_createBondMapContainer, "bondmap") def _createAngleMapContainer(self): return _FEPIOAngleMapContainer(self._proxy) anglemap = CreateWhenNeeded(_createAngleMapContainer, "anglemap") def _createDihedMapContainer(self): return _FEPIODihedMapContainer(self._proxy) dihedmap = CreateWhenNeeded(_createDihedMapContainer, "dihedmap") def _createExclMapContainer(self): return _FEPIOExclMapContainer(self._proxy) exclmap = CreateWhenNeeded(_createExclMapContainer, "exclmap") def _createPairMapContainer(self): return _FEPIOPairMapContainer(self._proxy) pairmap = CreateWhenNeeded(_createPairMapContainer, "pairmap") # other_block def _createOtherBlockContainer(self): return _FEPIOOtherBlockContainer(self.handle) other_block = CreateWhenNeeded(_createOtherBlockContainer, "other_block") def getOtherBlockNames(self): return mm.mmfepio_other_get_block_names(self.handle) # property def _getProperty(self): if self._property is None: self._property = _FEPIOProperty(self.handle) return self._property property = property( _getProperty, doc="A dictionary of fepio properties. Keys are strings of " "the form C{type_family_name} as described in the " "L{PropertyName} documentation and found in C{.cms} files.") def addAtommap(self): mm.mmfepio_add_atommaps(self.handle, 1) return self.atommap[len(self.atommap)] def addBondmap(self): mm.mmfepio_add_bondmaps(self.handle, 1) return self.bondmap[len(self.bondmap)] def addAnglemap(self): mm.mmfepio_add_anglemaps(self.handle, 1) return self.anglemap[len(self.anglemap)] def addDihedmap(self): mm.mmfepio_add_dihedmaps(self.handle, 1) return self.dihedmap[len(self.dihedmap)] def addExclmap(self): mm.mmfepio_add_exclmaps(self.handle, 1) return self.exclmap[len(self.exclmap)] def addPairmap(self): mm.mmfepio_add_pairmaps(self.handle, 1) return self.pairmap[len(self.pairmap)] def addAtommaps(self, num_items): mm.mmfepio_add_atommaps(self.handle, num_items) def addBondmaps(self, num_items): mm.mmfepio_add_bondmaps(self.handle, num_items) def addAnglemaps(self, num_items): mm.mmfepio_add_anglemaps(self.handle, num_items) def addDihedmaps(self, num_items): mm.mmfepio_add_dihedmaps(self.handle, num_items) def addExclmaps(self, num_items): mm.mmfepio_add_exclmaps(self.handle, num_items) def addPairmaps(self, num_items): mm.mmfepio_add_pairmaps(self.handle, num_items) def addOtherBlock(self, name): """ Add a new other block to this fepio block, and return it. """ length = len(self.other_block) mm.mmfepio_add_other_blocks(self.handle, 1) mm.mmfepio_other_block_set_name(self.handle, length, name) return self.other_block[length] def _deleteItems(self, indices, delete_method): """ Delete multiple item from fepio sub-block. The argument indices must be a sequence or an iterable, and able to be interpreted as ints. After deletion, indices are renumbered from 1 to len(<sub-block>). Pre-existing references to items will not be correct, as they store index values. """ # Be sure that deleting is done from highest index to lowest. indices = [int(i) for i in indices] indices.sort() indices.reverse() for i in indices: delete_method(self.handle, i) def deleteAtommaps(self, indices): self._deleteItems(indices, mm.mmfepio_delete_atommap) def deleteBondmaps(self, indices): self._deleteItems(indices, mm.mmfepio_delete_bondmap) def deleteAnglemaps(self, indices): self._deleteItems(indices, mm.mmfepio_delete_anglemap) def deleteDihedmaps(self, indices): self._deleteItems(indices, mm.mmfepio_delete_dihedmap) def deleteExclmaps(self, indices): self._deleteItems(indices, mm.mmfepio_delete_exclmap) def deletePairmaps(self, indices): self._deleteItems(indices, mm.mmfepio_delete_pairmap) def deleteOtherBlocks(self, indices): """ Delete multiple other blocks from the fepio block. The argument indices must be a sequence or an iterable, and able to be interpreted as ints. After deletion, indices are renumbered from 0 to len(other_block) - 1. """ # Be sure that deleting is done from highest index to lowest. indices = [int(i) for i in indices] indices.sort() indices.reverse() for i in indices: mm.mmfepio_delete_other_block(self.handle, i)
[docs]class FFIOStructure(Structure): """ Class to create python interface to force field data. This is just a wrapper around the mmffio/mmfepio functionality. I/O happens through the underlying libraries. mmffio/mmfepio handles are created on demand when ffio/fepio blocks are accesssed. """
[docs] def __init__(self, handle, error_handler=mm.error_handler): """ Parent class would take care of basic CT data. """ Structure.__init__(self, handle, error_handler) mm.mmffio_initialize(error_handler) mm.mmfepio_initialize(error_handler) self.ffhandle = None self.fephandle = None
def _hasBlock(self, block_name): uh = mm.mmct_ct_m2io_get_unrequested_handle(self.handle) return mm.m2io_inquire_block_name(uh, block_name) def _createFFIOBlock(self): if self.ffhandle is None: if self._hasBlock('ffio_ff'): self.ffhandle = mm.mmffio_ff_mmct_get(self.handle) else: raise RuntimeError("Fail to get ffio handle.") return _FFIO(self.ffhandle) ffio = CreateWhenNeeded(_createFFIOBlock, "ffio") def _createFEPIOBlock(self): if self.fephandle is None: if self._hasBlock('fepio_fep'): self.fephandle = mm.mmfepio_fep_mmct_get(self.handle) else: raise RuntimeError("Fail to get fepio handle.") return _FEPIO(self.fephandle) fepio = CreateWhenNeeded(_createFEPIOBlock, "fepio")
[docs] def hasFfio(self): """ """ try: if (self.ffio): return True except: pass return False
[docs] def hasFepio(self): """ """ try: if (self.fepio): return True except: pass return False
def __copy__(self): """ Allows the structure to be copied by copy.copy """ new_ct = mm.mmct_ct_duplicate(self.handle) new_ffiostructure = FFIOStructure(new_ct, True) if self.hasFfio(): new_ffiostructure.ffhandle = mm.mmffio_ff_duplicate(self.ffhandle) if self.hasFepio(): new_ffiostructure.fephandle = mm.mmfepio_fep_duplicate( self.fephandle) return new_ffiostructure
[docs] def copy(self): """ Returns a copy of the structure. """ return self.__copy__()
[docs] def write(self, filename, format=None): """ Write the cms structure to a file, overwriting any previous content. Format is determined from the file suffix if None is specified. otherwise an explicit value of maestro, sd, pdb, or smiles can be used. Only cms format is supported as of know, if other types are choosen, basic CT information would be written, which is taken care by the parent class. """ if format == "CMS": write_cms(self, filename, mm.M2IO_WRITE) else: Structure.write(self, filename, format)
[docs] def append(self, filename, format=None): """ Append the structure to the file. Format is determined from the file suffix if None is specified, otherwise an explicit value of maestro, sd, pdb, or smiles can be used. Only cms format is supported as of know, if other types are choosen, basic CT information would be written, which is taken care by the parent class. """ if format == "CMS": write_cms(self, filename, mm.M2IO_APPEND) else: Structure.append(self, filename, format)
def __del__(self, mmffio_ff_delete=mm.mmffio_ff_delete, mmfepio_fep_delete=mm.mmfepio_fep_delete, mmffio_terminate=mm.mmffio_terminate, mmfepio_terminate=mm.mmfepio_terminate): """ Delete the underlying mmffio, mmfepio handles """ if hasattr(self, 'ffhandle') and self.ffhandle is not None: mmffio_ff_delete(self.ffhandle) if hasattr(self, 'fephandle') and self.fephandle is not None: mmfepio_fep_delete(self.fephandle) mmffio_terminate() mmfepio_terminate()
[docs]class CMSReader(object): """ A class for reading structures from a CMS format file. """ # Make this setting a class variable so that people can set it to M2IO_READ # if needed. read_mode = mm.M2IO_READ_FORWARD
[docs] def __init__(self, filename, index=1, input_string=None): """ Initialize with a filename, an optional starting index (default of 1) """ self._index = index mm.m2io_initialize(mm.error_handler) mm.mmct_initialize(mm.error_handler) self.error_handler = mm.error_handler self.fh = None self.filename = filename self.input_string = input_string
def __del__(self): self.close() mm.mmct_terminate() mm.m2io_terminate()
[docs] def close(self): """ Close the file. """ if self.fh is not None: mm.m2io_close_file(self.fh) self.fh = None
# required for iterator support def __iter__(self): return self def __next__(self): """ Return the next FFIOStructure object from the file. Set self.last_position to the file offset just before it was read. """ if self.fh is None: # First iteration; open the file: try: if self.input_string: self.fh = mm.m2io_open_read_from_buffer(self.input_string) else: self.fh = mm.m2io_open_file(self.filename, self.read_mode) self.type = mm.m2io_get_file_type(self.fh) except mm.MmException as e: # If m2io_open_file returned M2IO_ERR, check to see if this is due # to an empty file. if e.rc == mm.M2IO_ERR and os.path.getsize(self.filename) == 0: raise StopIteration() else: raise if self._index > 1: mm.m2io_goto_block(self.fh, mm.M2IO_BLOCK_WILDCARD_CT, (self._index - 1)) mm.m2io_leave_block(self.fh) if self.type == mm.M2IO_DISK_FILE: # File position is not supported for Mmod format or "in-core" files, # but we don't raise an exception if we're using such a file; rather, # a NameError will arise should the user try to access # self.last_position in this situation: try: self.last_position = mm.m2io_get_file_pos(self.fh) except: raise try: mm.m2io_goto_next_block(self.fh, mm.M2IO_BLOCK_WILDCARD_CT) ct = FFIOStructure(mm.mmct_ct_m2io_get(self.fh), error_handler=self.error_handler) except mm.MmException as e: if e.rc == mm.M2IO_EOF: raise StopIteration() else: raise Exception("Could not read the next structure from file") return ct
[docs]def write_cms(ct, filename, mode=mm.M2IO_WRITE): """ Write a CT to a Maestro format file. """ fh = mm.m2io_open_file(filename, mode) if ct.ffhandle is not None: mm.mmffio_ff_mmct_put(ct.ffhandle, ct.handle) if ct.fephandle is not None: mm.mmfepio_fep_mmct_put(ct.fephandle, ct.handle) mm.mmct_ct_m2io_put(ct, fh) mm.m2io_close_file(fh)
[docs]def write_cms_to_string(ct): """ Write a CT to a string. """ if ct.ffhandle is not None: mm.mmffio_ff_mmct_put(ct.ffhandle, ct.handle) if ct.fephandle is not None: mm.mmfepio_fep_mmct_put(ct.fephandle, ct.handle) return write_ct_to_string(ct)
[docs]def merge_ct(ct0, ct1): """ Returns a new ct which is the result of merging ct0 and ct1. """ for a in ct1.atom: a.property["i_tmpmerge_orig_atom_index"] = int(a) ct = ct0.merge(ct1, copy_props=True) new_index = {} # key = orig atom index, value = new atom index for a in ct.atom: try: orig_index = a.property["i_tmpmerge_orig_atom_index"] if (orig_index): new_index[orig_index] = int(a) except KeyError: pass def migrate_ffio_subblock_item(new, old, prop_name, index_map=new_index): for name in prop_name: try: if (name[:8] == "i_ffio_a"): try: new.property[name] = index_map[old.property[name]] except KeyError: new.property[name] = old.property[name] elif (name[:12] == "s_ffio_group"): new_indices = ( index_map[int(e)] for e in old.property[name].split()) new.property[name] = ' '.join(map(str, new_indices)) else: new.property[name] = old.property[name] except TypeError: pass subblock = {} for e in ffio_block_names: subblock[e] = "add" + e[0].upper() + e[1:] ct1.ffio.site ct1.ffio.bond ct1.ffio.angle ct1.ffio.dihedral ct1.ffio.exclusion ct1.ffio.pair ct1.ffio.vdwtype ct1.ffio.vdwtypescombined ct1.ffio.restraint ct1.ffio.virtual ct1.ffio.pseudo ct1.ffio.constraint ct1.ffio.posfbhw ct1.ffio.anglefbhw ct1.ffio.improperfbhw ct1.ffio.stretchfbhw ct1.ffio.other_block ct.ffio.site ct.ffio.bond ct.ffio.angle ct.ffio.dihedral ct.ffio.exclusion ct.ffio.pair ct.ffio.vdwtype ct.ffio.vdwtypescombined ct.ffio.restraint ct.ffio.virtual ct.ffio.pseudo ct.ffio.constraint ct.ffio.posfbhw ct.ffio.anglefbhw ct.ffio.improperfbhw ct.ffio.stretchfbhw ct.ffio.other_block def unroll_ffio(ct, n): # Unrolls the ffio block. if (n > 1): for e, f in subblock.items(): entries_in_subblock = [entry for entry in ct.ffio.__dict__[e]] for i in range(n - 1): for entry in entries_in_subblock: new = _FFIO.__dict__[f].__get__( ct.ffio, _FFIO)() # Calls `ct.ffio.add*()' function. migrate_ffio_subblock_item(new, entry, ffio_sub_block_prop_links[e]) unroll_ffio(ct, ct0.atom_total // len(ct.ffio.site)) unroll_ffio(ct1, ct1.atom_total // len(ct1.ffio.site)) for e, f in subblock.items(): for old in ct1.ffio.__dict__[e]: new = _FFIO.__dict__[f].__get__( ct.ffio, _FFIO)() # Calls `ct.ffio.add*()' function. i = len(ct.ffio.__dict__[e]) migrate_ffio_subblock_item(new, old, ffio_sub_block_prop_links[e]) for a in ct1.atom: del a.property["i_tmpmerge_orig_atom_index"] for a in ct.atom: if ("i_tmpmerge_orig_atom_index" in a.property): del a.property["i_tmpmerge_orig_atom_index"] return ct
if ("__main__" == __name__): fname = "md_test.cms" struc = [e for e in CMSReader(fname)] ct = merge_ct(struc[2], struc[3]) ct.write("testffiostructure.mae", "CMS")