"""
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")