B
    bQ             +   @   s  d Z ddlmZmZmZ ddlZddlZddlmZ ddlm	Z	 ddl
mZ ddlmZ dd	lmZmZmZ dd
lmZmZmZ ddlmZmZmZ ddlmZ ddlmZmZ ddl m!Z!m"Z" ddl#m$Z$ ddddddddddddddddd d!d"d#d$d%d&d'd(d)d*d+d,d-d.d/d0d1d2d3d4d5d6d7d8d9d:g+Z%e&dej' ej(ej)d;   Z*e*+ej,Z*ej-ej.j/d<d=d>Z0e01ej.e* e2ej3ej4e0ej5ej6ej7ej8gZ9dd?d@Z:dAdB Z;ddCdDZ<G dEdF dFe=Z>G dGdH dHe=Z?G dIdJ dJe=Z@dKdL ZAdMdN ZBG dOd de>ZCG dPd2 d2eCZDG dQd3 d3e=ZEG dRd4 d4e=ZFG dSd5 d5e=ZGG dTd de=ZHG dUd de@e>ZIG dVd de=ZJG dWd de@e>ZKG dXd de?ZLG dYd de@e>ZMG dZd6 d6e@e>ZNG d[d deOe>ZPG d\d) d)eHZQG d]d de?ZRG d^d de@e>ZSG d_d de=ZTG d`d de@e>ZUG dadb dbe=ZVG dcd' d'e?ZWG ddd de?ZXG ded* d*e@e>ZYG dfd  d e=ZZG dgd# d#e=Z[G dhd$ d$e@e>Z\G did% d%eTZ]G djdk dke=Z^G dld& d&e@e>Z_G dmd de=Z`G dnd de=ZaG dod9 d9eCZbG dpd: d:e?ZcG dqd! d!e>Zddrds ZeG dtd( d(eOZfG dud" d"efZgG dvd defZhG dwd+ d+e=ZiG dxd, d,e@e>ZjG dyd- d-ejZkG dzd0 d0e=ZlG d{d| d|e=Zmem Znenem kstod}G d~d. d.e=ZpG dd/ d/e=ZqG dd8 d8e=ZreIddZsdS )z
This module contains objects that deal with system topology and parameters, such
as atoms, residues, bonds, angles, etc.

by Jason Swails
    )absolute_importdivisionprint_functionN)copy)wraps   )unit)TINY_DIGITS)
DEG_TO_RAD
RAD_TO_DEGTINY)MoleculeErrorParameterErrorParameterWarning)angledihedral	distance2)
deprecated)	iteritemsstring_types)rangezip)ElementAngle	AngleTypeAtomAtomListBondBondTypeChiralFrameCmapCmapTypeDihedralDihedralTypeDihedralTypeListImproperImproperTypeMultipoleFrameOutOfPlaneBend	PiTorsionResidueResidueListStretchBendStretchBendTypeTorsionTorsionTorsionTorsionTypeTrigonalAngleTrackedListUreyBradleyOutOfPlaneBendTypeNonbondedExceptionNonbondedExceptionTypeAmoebaNonbondedExceptionTypeAcceptorDonorGroupAtomTypeNoUreyBradley
ExtraPointTwoParticleExtraPointFrameThreeParticleExtraPointFrameOutOfPlaneExtraPointFrameRBTorsionTypeUnassignedAtomTypeLink	DrudeAtomDrudeAnisotropy   z	akma timeZaks)Zsymbolc             C   sD   t | r@|dkr6| jt jr,| t jS | tS | |S | S )zu
    Strips any units from the given value by casting them into the AKMA unit
    system (or the requested unit)
    N)uis_quantityr   Zis_compatibledegreesvalue_in_unitZvalue_in_unit_systemakma_unit_system)valuer    rK   5lib/python3.7/site-packages/parmed/topologyobjects.py_strip_units+   s    


rM   c                s   t   fdd}|S )zf
    Wraps comparison operators to return NotImplemented instead of raising an
    AttributeError
    c                s$   y
 | |S  t k
r   tS X d S )N)AttributeErrorNotImplemented)selfother)funcrK   rL   wrapper?   s    
z-_exception_to_notimplemented.<locals>.wrapper)r   )rR   rS   rK   )rR   rL   _exception_to_notimplemented:   s    rT   c                s    dkrdg  fdd}|S )a   Serializes based on all attributes except requested exclusions

    Parameters
    ----------
    exclusions : list of str, optional
        List of all attributes to exclude from serialization (should be
        descriptors and 'list'). Default is None

    Notes
    -----
    If exclusions is None, it defaults to excluding 'list'. If this is not
    desired, set exclusions to the empty list.
    Nlistc                s    fddt | jD S )Nc                s   i | ]\}}| kr||qS rK   rK   ).0keyval)
exclusionsrK   rL   
<dictcomp>X   s    zC_getstate_with_exclusions.<locals>.__getstate__.<locals>.<dictcomp>)r   __dict__)rP   )rY   rK   rL   __getstate__W   s    z/_getstate_with_exclusions.<locals>.__getstate__rK   )rY   r\   rK   )rY   rL   _getstate_with_exclusionsG   s    r]   c               @   s   e Zd ZdZedd ZdS )	_ListItema  
    Helpful methods for items that appear in some kind of tracked list.
    Subclasses of this method interact with their `list` attribute, if they have
    one, in order to determine _where_ in the list they occur.

    Attributes
    ----------
    idx : ``int``
        This is intended to be a read-only variable that determines where in the
        list this particular object is. If there is no `list` attribute for this
        object, or the item is not in the list at all, `idx` is -1

    Notes
    -----
    For lists that support indexing its members and tracking when that list is
    changed (so we know when to update indexes), this is a fast lookup, as
    indexing only needs to be done once, at most, for the entire list (until
    changes are made that could change the indexing).

    The ``idx`` lookup in a TrackedList is therefore an O(1) operation, while
    it is O(N) for standard containers (O(N^2) for looking up *all* indexes).
    c             C   s   y
| j }W n tk
r   dS X |d kr,dS y
|j}W n6 tk
rl   x t|D ]\}}|| krP|S qPW dS X |s|| jdkryd| _|  | jS  tk
r   x t|D ]\}}|| kr|S qW dS X n| jS d S )N)rU   rN   needs_indexing	enumerate_idxindex_members)rP   Zmylistr`   iitemrK   rK   rL   idxw   s0    

  z_ListItem.idxN)__name__
__module____qualname____doc__propertyrf   rK   rK   rK   rL   r^   _   s   r^   c               @   s(   e Zd ZdZdd Zdd Zdd ZdS )	_FourAtomTerma;  
    A base class for a parameter that spans 4 atoms

    Parameters
    ----------
    atom1 : :class:`Atom`
        The first atom in the term
    atom2 : :class:`Atom`
        The second atom in the term
    atom3 : :class:`Atom`
        The third atom in the term
    atom4 : :class:`Atom`
        The fourth atom in the term

    Notes
    -----
    This is a base class that should be overridden by terms consisting of 4
    atoms (like torsions). There are a lot of such terms in the Amoeba force
    field, so this functionality is broken out into a new form
    c             C   s`   ||ks0||ks0||ks0||ks0||ks0||krDt d||||f || _|| _|| _|| _d S )Nz?4-atom term cannot have duplicate atoms! Atoms are: %s %s %s %s)r   atom1atom2atom3atom4)rP   rm   rn   ro   rp   rK   rK   rL   __init__   s     z_FourAtomTerm.__init__c             C   s(   | j |kp&| j|kp&| j|kp&| j|kS )N)rm   rn   ro   rp   )rP   objrK   rK   rL   __contains__   s    z_FourAtomTerm.__contains__c             C   s,   d | _  | _ | _| _t| dr(d| _dS )z@ Sets all atoms in this term to None, and its type if it exists Ntype)rm   rn   ro   rp   hasattrrt   )rP   rK   rK   rL   delete   s    
 z_FourAtomTerm.deleteN)rg   rh   ri   rj   rq   rs   rv   rK   rK   rK   rL   rl      s   rl   c               @   s    e Zd ZdZdd Zdd ZdS )_ParameterTypea}  
    A parameter type that defines the nature of a particular molecular
    interaction. This is a base class that simply indicates whether a particular
    parameter type is "used", which in turn is used to determine whether or not
    this parameter type needs to be printed

    Attributes
    ----------
    used : ``bool``
        If ``True``, then this parameter type should be considered *used*. If
        ``False``, it is not being used and does not need to be printed.
    penalty : float or None
        If this is assigned from a database, there might be a penalty assigned
        to the determination of this parameter
    c             C   s   d| _ d | _d S )NF)usedpenalty)rP   rK   rK   rL   rq      s    z_ParameterType.__init__c             C   s
   | |k S )NrK   )rP   rQ   rK   rK   rL   __ne__   s    z_ParameterType.__ne__N)rg   rh   ri   rj   rq   rz   rK   rK   rK   rL   rw      s   rw   c             C   s   |  | | dS )a  
    Deletes a requested item from a list. If the item does not exist in the
    list, a ValueError is raised

    Parameters
    ----------
    list : ``list``
        The list from which an item will be deleted
    item : ``object``
        The object to delete from the list
    N)popindex)rU   re   rK   rK   rL   _delete_from_list   s    r}   c             C   s4   x.|D ]&}t ||sqt||}t| || qW dS )z
    Shallow-copies all requested attributes from `source` to `dest` if they
    are present. If not present, nothing is done
    N)ru   getattrsetattr)destsourceZattrsattrZmyattrrK   rK   rL   _safe_assigns   s
    

 
r   c               @   s$  e Zd ZdZdadd	Zed
d Zdd Zedd Z	edd Z
edd Zedd Zedd Zedd Zejdd Zedd Zedd Zejdd Zed d! Zed"d# Zejd$d# Zed%d& Zed'd( Zejd)d( Zed*d+ Zed,d- Zejd.d- Zed/d0 Zed1d2 Zejd3d2 Zed4d5 Zed6d7 Zejd8d7 Zed9d: Zed;d< Zed=d> Zdbd@dAZedBdC Z e jdDdC Z edEdF Z!dGdH Z"dIdJ Z#dKdL Z$dMdN Z%dOdP Z&dQdR Z'dSdT Z(dUdV Z)dWdX Z*dYdZ Z+d[d\ Z,d]d^ Z-d_d` Z.dS )cr   a*  
    An atom. Only use these as elements in AtomList instances, since AtomList
    will keep track of when indexes and other stuff needs to be updated. All
    parameters are optional.

    Parameters
    ----------
    atomic_number : ``int``
        The atomic number of this atom
    name : ``str``
        The name of this atom
    type : ``str``
        The type name of this atom
    charge : ``float``
        The partial atomic charge of this atom in fractions of an electron
    mass : ``float``
        The atomic mass of this atom in daltons
    nb_idx : ``int``
        The nonbonded index. This is a pointer that is relevant in the context
        of an Amber topology file and identifies its Lennard-Jones atom type
    solvent_radius : ``float``
        The intrinsic solvation radius of this atom.
    screen : ``float``
        The Generalized Born screening factor for this atom.
    occupancy : ``float``
        The occupancy of the atom (see PDB file)
    bfactor : ``float``
        The B-factor of the atom (see PDB file)
    altloc : ``str``
        Alternate location indicator (see PDB file)

    Other Parameters
    ----------------
    list : :class:`AtomList`
        The AtomList that this atom belongs to. If None, this atom does not
        belong to any list. This can be any iterable, but should typically be an
        AtomList or None
    tree : ``str``
        The tree chain identifier assigned to this atom. Relevant in the context
        of an Amber topology file, and not used for very much.
    join : ``int``
        The 'join` property of atoms stored in the Amber topology file. At the
        time of writing this class, `join` is unused, but still oddly required.
        Add support for future-proofing
    irotat : ``int``
        The `irotat` property of atoms stored in the Amber topology file.
        Unused, but included for future-proofing.
    number : ``int``
        The serial number given to the atom (see PDB file)
    rmin : ``float``
        The Rmin/2 Lennard-Jones parameter for this atom. Default evaluates to 0
    epsilon : ``float``
        The epsilon (well depth) Lennard-Jones parameter for this atom. Default
        evaluates to 0
    rmin14 : ``float``
        The Rmin/2 Lennard-Jones parameter for this atom in 1-4 interactions.
        Default evaluates to 0
    epsilon14 : ``float``
        The epsilon (well depth) Lennard-Jones parameter for this atom in 1-4
        interactions. Default evaluates to 0

    Other Attributes
    ----------------
    element : ``int``
        This is an alias for atomic_number
    atom_type : :class:`AtomType`
        In some cases, "type" is an ambiguous choice of an integer serial number
        or a string descriptor. In this case, atom_type is an AtomType instance
        that disambiguates this discrepancy.
    anisou : numpy.ndarray(float64) (or list of floats)
        Anisotropic temperature scaling factors. This is a 6-element numpy array
        They are the 3x3 symmetric matrix elements U(1,1), U(2,2), U(3,3),
        U(1,2), U(1,3), U(2,3). If no factors available, it is None.
    idx : ``int``
        The index of this atom in the list. Set to -1 if this atom is not part
        of a list or the index cannot otherwise be determined (i.e., if the
        containing list does not support indexing its members)
    residue : :class:`Residue`
        The Residue that this atom belongs to. This is assigned when this atom
        is passed to `Residue.add_atom` -- see below for more information. Until
        it is set there, it is None
    other_locations : ``dict`` of :class:`Atom`
        A dict of Atom instances that represent alternate conformers of this
        atom. The keys are the `altloc` characters for those Atoms.
    bonds : ``list`` of :class:`Bond`
        list of Bond objects in which this atom is a member. This attribute
        should not be modified.
    angles : ``list`` of :class:`Angle`
        list of Angle objects in which this atom is a member. This attribute
        should not be modified.
    dihedrals : ``list`` of :class:`Dihedral`
        list of Dihedral objects in which this atom is a member. This attribute
        should not be modified.
    urey_bradleys : ``list`` of :class:`UreyBradley`
        list of UreyBradley objects in which this atom is a member (CHARMM,
        AMOEBA). This attribute should not be modified.
    impropers : ``list`` of :class:`Improper`
        list of Improper objects in which the atom is a member (CHARMM). This
        attribute should not be modified.
    cmaps : ``list`` of :class:`Cmap`
        list of Cmap objects in which the atom is a member (CHARMM, AMOEBA).
        This attribute should not be modified.
    tortors : ``list`` of :class:`TorsionTorsion`
        list of TorsionTorsion objects in which the atom is a member (AMOEBA).
        This attribute should not be modified.
    bond_partners : ``list`` of :class:`Atom`
        list of Atoms to which this atom is bonded. Do not modify this
        attribute -- it will almost certainly not do what you think it will
    angle_partners : ``list`` of :class:`Atom`
        list of Atoms to which this atom forms an angle, but not a bond. Do not
        modify this attribute -- it will almost certainly not do what you think
        it will
    dihedral_partners : ``list`` of :class:`Atom`
        list of Atoms to which this atom forms an dihedral, but not a bond or
        angle. Do not modify this attribute -- it will almost certainly not do
        what you think it will
    tortor_partners : ``list`` of :class:`Atom`
        list of Atoms to which this atom forms a coupled Torsion-Torsion, but
        not a bond or angle (AMOEBA). Do not modify this attribute -- it will
        almost certainly not do what you think it will
    exclusion_partners : ``list`` of :class:`Atom`
        list of Atoms with which this atom is excluded, but not bonded, angled,
        or dihedraled to. Do not modify this attribute -- it will almost
        certainly not do what you think it will
    marked : ``int``
        Mainly for internal use, it is used to indicate when certain atoms have
        been "marked" when traversing the bond network identifying topological
        features (like molecules and rings)
    children : ``list`` of :class:`ExtraPoint`
        This is the list of "child" ExtraPoint objects bonded to this atom
    number : ``int``
        The serial number of the atom in the input structure (e.g., PDB file).
        If not present in the original structure, a default value of -1 is used
    rmin : ``float``
        The Rmin/2 Lennard-Jones parameter for this atom. Default value is 0.
        If not set, it is taken from the `atom_type` attribute (if available)
    epsilon : ``float``
        The epsilon (well depth) Lennard-Jones parameter for this atom. Default
        value is 0. If not set, it is taken from the `atom_type` attribute (if
        available)
    rmin_14 : ``float``
        The Rmin/2 L-J parameter for 1-4 pairs. Default value is `rmin` (see
        above). If not set, it is taken from the `atom_type` attribute (if
        available).
    epsilon_14 : ``float``
        The epsilon L-J parameter for 1-4 pairs. Default value is `epsilon` (see
        above). If not set, it is taken from the `atom_type` attribute (if
        available).

    Possible Attributes
    -------------------
    xx : ``float``
        The X-component of the position of this atom. Only present if
        coordinates have been loaded. Otherwise raises AttributeError
    xy : ``float``
        The Y-component of the position of this atom. Only present if
        coordinates have been loaded. Otherwise raises AttributeError
    xz : ``float``
        The Z-component of the position of this atom. Only present if
        coordinates have been loaded. Otherwise raises AttributeError
    vx : ``float``
        The X-component of the velocity of this atom. Only present if the
        velocities have been loaded. Otherwise raises AttributeError
    vy : ``float``
        The Y-component of the velocity of this atom. Only present if the
        velocities have been loaded. Otherwise raises AttributeError
    vz : ``float``
        The Z-component of the velocity of this atom. Only present if the
        velocities have been loaded. Otherwise raises AttributeError
    type_idx : ``int``
        The AMOEBA atom type index. Only present if initialized with the AMOEBA
        force field. Otherwise raises AttributeError.
    class_idx : ``int``
        The AMOEBA class type index Only present if initialized with the AMOEBA
        force field. Otherwise raises AttributeError.
    multipoles : ``list(float)``
        The list of the 10 multipole moments up through quadrupoles Only present
        if initialized with the AMOEBA force field. Otherwise raises
        AttributeError.
    polarizability : ``list(float)``
        The polarizability of the atom. Only present if initialized with the
        AMOEBA force field. Otherwise raises AttributeError.
    vdw_parent : :class:`Atom`
        In the AMOEBA force field, this is the parent atom for the van der Waals
        term
    vdw_weight : ``float``
        In the AMOEBA force field, this is the weight of the van der Waals
        interaction on the parent atom

    Notes
    -----
    The bond_partners, angle_partners, dihedral_partners, and exclusion_partners
    arrays are actually generated as properties by taking differences of sets
    and sorting them. As a result, accessing this attribute constantly can be
    less efficient than you would expect. Iterating over them in a loop requires
    minimal overhead. But if frequent access is needed and these sets are
    guaranteed not to change, you should save a reference to the object and use
    that instead.

    Binary comparisons are done by atom index and are used primarily for sorting
    sublists of atoms. The == operator is not defined, so Atom equality should
    be done using the `is` operator. This allows Atom instances to be hashable
    (and so used as `dict` keys and put in `set`s)

    Examples
    --------
    >>> a1 = Atom(name='CO', type='C', charge=0.5, mass=12.01)
    >>> a2 = Atom(name='OC', type='O', charge=-0.5, mass=12.01)
    >>> a1.bond_to(a2)
    >>> a1 in a2.bond_partners and a2 in a1.bond_partners
    True
    >>> a1.idx # Not part of a container
    -1

    This object also supports automatic indexing when it is part of a container

    >>> atom_list = []
    >>> atom_list.append(Atom(list=atom_list, name='CO', charge=0.5))
    >>> atom_list.append(Atom(list=atom_list, name='OC', charge=-0.5))
    >>> atom_list[0].idx
    0
    >>> atom_list[1].idx
    1
    Nr            BLAr_   c             C   sV  || _ d| _|| _| | _y| | _W n tk
rD   || _Y nX t|tj	| _
t|tj| _|| _t|tj| _|	| _|
| _|| _|| _|| _|| _|| _g | _g | _g | _g | _g | _d | _d| _g g g   | _| _| _ g g g   | _!| _"| _#g | _$i | _%t&| _'|| _(d | _)t|tj*| _+t|tj,| _-t|tj*| _.t|tj,| _/g | _0d S )Nr_   r   )1rU   rb   atomic_numberstripnamert   rN   rM   rE   elementary_charge_chargeZdaltonmassnb_idxangstromsolvent_radiusscreentreejoinirotatbfactoraltloc	occupancy_bond_partners_angle_partners_dihedral_partners_tortor_partners_exclusion_partnersresiduemarkedbondsangles	dihedralsurey_bradleys	improperscmapstortorsother_locationsr@   	atom_typenumberanisou	angstroms_rminkilocalories_per_mole_epsilon_rmin14
_epsilon14children)rP   rU   r   r   rt   charger   r   r   r   r   r   r   r   r   r   r   rminepsilonrmin14	epsilon14rK   rK   rL   rq     sL    
zAtom.__init__c             C   s   | |j |j|j|j|j|j|j|j|j|j	|j
|j|j|jd}|j|_t|j|_x"|jD ]}t|j| |j|< q\W t||d |S )N)r   r   rt   r   r   r   r   r   r   r   r   r   r   r   )xxxyxzvxvyvztype_idx	class_idx
multipolespolarizabilityZ
vdw_parent
vdw_weight)r   r   rt   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )clsre   newrW   rK   rK   rL   _copy   s    z
Atom._copyc             C   s   t | | S )z@ Returns a deep copy of this atom, but not attached to any list )rt   r   )rP   rK   rK   rL   __copy__  s    zAtom.__copy__c             C   s@   t | j}x(| jD ]}x|jD ]}|| qW qW tt|S )z  Go through all bonded partners )setr   r   addsortedrU   )rP   bppcrK   rK   rL   bond_partners  s
    
zAtom.bond_partnersc             C   sZ   t | j}t | j| }t  }x&|D ]}x|jD ]}|| q0W q$W ||O }tt|S )z7 List of all angle partners that are NOT bond partners )r   r   r   r   r   r   rU   )rP   r   aptoaddr   r   rK   rK   rL   angle_partners  s    

zAtom.angle_partnersc             C   sh   t | j}t | j}t | j| | }t  }x&|D ]}x|jD ]}|| q>W q2W ||O }tt|S )zC List of all dihedral partners that are NOT angle or bond partners )r   r   r   r   r   r   r   rU   )rP   r   r   dpr   r   r   rK   rK   rL   dihedral_partners*  s    


zAtom.dihedral_partnersc             C   sv   t | j}t | j}t | j}t | j| | | }t  }x&|D ]}x|jD ]}|| qLW q@W ||O }tt|S )z
        List of all 1-5 partners that are NOT in angle or bond partners. This is
        *only* used in the Amoeba force field
        )	r   r   r   r   r   r   r   r   rU   )rP   r   r   r   tpr   r   r   rK   rK   rL   tortor_partners7  s    



zAtom.tortor_partnersc       	      C   s   t | j}t | j}t | j}t | j}t | j| | | | }t  }x&|D ]}x|jD ]}|| qZW qNW ||O }tt	|S )zX
        List of all exclusions not otherwise excluded by bonds/angles/torsions
        )
r   r   r   r   r   r   r   r   r   rU   )	rP   r   r   r   r   epr   r   r   rK   rK   rL   exclusion_partnersH  s    




zAtom.exclusion_partnersc             C   s2   | j d kr,| jtks | jjd kr$dS | jjS | j S )Ng        )r   r   r@   r   )rP   rK   rK   rL   r   `  s
    
zAtom.chargec             C   s   t |tjd| _d S )N)r   )rM   rE   r   r   )rP   rJ   rK   rK   rL   r   h  s    c             C   s   | j tj S )z Charge with units )r   rE   r   )rP   rK   rK   rL   uchargel  s    zAtom.uchargec             C   s2   | j dkr,| jtks | jjdkr$dS | jjS | j S )z; Lennard-Jones Rmin/2 parameter (the Lennard-Jones radius) Ng        )r   r   r@   r   )rP   rK   rK   rL   r   q  s
    
z	Atom.rminc             C   s   t |tjd| _dS )z; Lennard-Jones Rmin/2 parameter (the Lennard-Jones radius) )r   N)rM   rE   r   r   )rP   rJ   rK   rK   rL   r   z  s    c             C   s   | j tj S )z+ Lennard-Jones Rmin/2 parameter with units )r   rE   r   )rP   rK   rK   rL   urmin  s    z
Atom.urminc             C   s   | j d d S )z; Lennard-Jones sigma parameter -- directly related to Rmin g)N>?rD   )r   )rP   rK   rK   rL   sigma  s    z
Atom.sigmac             C   s   t |tjdd d | _d S )N)r   gÚ?rD   )rM   rE   r   r   )rP   rJ   rK   rK   rL   r     s    c             C   s   | j tj S )z* Lennard-Jones sigma parameter with units )r   rE   r   )rP   rK   rK   rL   usigma  s    zAtom.usigmac             C   s2   | j dkr,| jtks | jjdkr$dS | jjS | j S )z@ Lennard-Jones epsilon parameter (the Lennard-Jones well depth) Ng        )r   r   r@   r   )rP   rK   rK   rL   r     s
    
zAtom.epsilonc             C   s   t |tjd| _dS )z@ Lennard-Jones epsilon parameter (the Lennard-Jones well depth) )r   N)rM   rE   Zkilocalorie_per_moler   )rP   rJ   rK   rK   rL   r     s    c             C   s   | j tj S )z, Lennard-Jones epsilon parameter with units )r   rE   r   )rP   rK   rK   rL   uepsilon  s    zAtom.uepsilonc             C   s4   | j dkr.| jtks | jjdkr&| jS | jjS | j S )z( The 1-4 Lennard-Jones Rmin/2 parameter N)r   r   r@   rmin_14r   )rP   rK   rK   rL   r     s
    
zAtom.rmin_14c             C   s   t |tj| _dS )z( The 1-4 Lennard-Jones Rmin/2 parameter N)rM   rE   r   r   )rP   rJ   rK   rK   rL   r     s    c             C   s   | j tj S )z3 The 1-4 Lennard-Jones Rmin/2 parameter with units )r   rE   r   )rP   rK   rK   rL   urmin_14  s    zAtom.urmin_14c             C   sD   | j dkr6| jtks | jjdkr&| jS | jjd d S | j d d S )z; Lennard-Jones sigma parameter -- directly related to Rmin Ng)N>?rD   )r   r   r@   r   r   )rP   rK   rK   rL   sigma_14  s
    
zAtom.sigma_14c             C   s   t |tjd d | _d S )NgÚ?rD   )rM   rE   r   r   )rP   rJ   rK   rK   rL   r     s    c             C   s   | j tj S )z2 The 1-4 Lennard-Jones sigma parameter with units )r   rE   r   )rP   rK   rK   rL   	usigma_14  s    zAtom.usigma_14c             C   s4   | j dkr.| jtks | jjdkr&| jS | jjS | j S )z) The 1-4 Lennard-Jones epsilon parameter N)r   r   r@   
epsilon_14r   )rP   rK   rK   rL   r     s
    
zAtom.epsilon_14c             C   s   t |tj| _dS )z) The 1-4 Lennard-Jones epsilon parameter N)rM   rE   r   r   )rP   rJ   rK   rK   rL   r     s    c             C   s   | j tj S )z) The 1-4 Lennard-Jones epsilon parameter )r   rE   r   )rP   rK   rK   rL   uepsilon_14  s    zAtom.uepsilon_14c             C   s   | j tj S )N)Z_massrE   daltons)rP   rK   rK   rL   umass  s    z
Atom.umassc             C   s   | j tj S )z& Solvation radius with units attached )r   rE   r   )rP   rK   rK   rL   usolvent_radius  s    zAtom.usolvent_radiusTc             C   s
  | j dk rtd|r| j }nd}g }x*| jD ] }|j | }||kr.|| q.W x*| jD ] }|j | }||krZ|| qZW x*| jD ] }|j | }||kr|| qW x*| jD ] }|j | }||kr|| qW x*| jD ] }|j | }||kr|| qW t|S )a  
        Returns the total number of nonbonded atom exclusions for this atom. The
        general rules for building the exclusion list is to include both
        exceptions AND exclusions (i.e., the Amber scaling of 1-4 interactions
        means that the 1-4 terms are excluded and a special pairlist is built to
        handle those exceptions).

        All atoms in the `_partners` arrays are nonbonded exclusions.

        Parameters
        ----------
        only_greater : ``bool``, optional
            If True (default), only atoms whose `idx` value is greater than this
            `Atom`s `idx` will be counted as an exclusion (to avoid double-
            counting exclusions). If False, all exclusions will be counted.
        index_from : ``int``, optional
            This is the index of the first atom, and is intended to be 0 (for C-
            and Python-style numbering, default) or 1 (for Fortran-style
            numbering, such as that used in the Amber and CHARMM topology files)

        Returns
        -------
        ``list of int``
            The returned list will be the atom indexes of the exclusion partners
            for this atom (indexing starts from ``index_from``)

        Notes
        -----
        If this instance's `idx` attribute evaluates to -1 -- meaning it is not
        in an AtomList -- a IndexError will be raised. If you have two extra
        points (i.e., those with atomic numbers of 0) bonded to each other, this
        routine may raise a ``RuntimeError`` if the recursion depth is exceeded.
        r   z+Cannot find exclusions of an unindexed Atomr_   )	rf   
IndexErrorr   appendr   r   r   r   r   )rP   Zonly_greaterZ
index_fromZbaselineZexclatmrd   rK   rK   rL   nonbonded_exclusions  s6    "





zAtom.nonbonded_exclusionsc             C   s   | j S )N)r   )rP   rK   rK   rL   element+  s    zAtom.elementc             C   s
   || _ d S )N)r   )rP   rJ   rK   rK   rL   r   .  s    c             C   s
   t | j S )N)r   r   )rP   rK   rK   rL   element_name1  s    zAtom.element_namec             C   sb   t |tr| j| nt | tr.|j|  | |krFtd| |f | j| |j|  dS )aI  
        Log this atom as bonded to another atom.

        Parameters
        ----------
        other : :class:`Atom`
            An atom that will be added to `bond_partners`

        Notes
        -----
        This action adds `self` to `other.bond_partners`. Raises
        :class:`MoleculeError` if `other is self`
        z,Cannot bond atom to itself! Atoms are: %s %sN)
isinstancer;   r   r   r   r   )rP   rQ   rK   rK   rL   bond_to7  s    

zAtom.bond_toc             C   s4   | |krt d| |f | j| |j|  dS )aK  
        Log this atom as angled to another atom.

        Parameters
        ----------
        other : :class:`Atom`
            An atom that will be added to `angle_partners`

        Notes
        -----
        This action adds `self` to `other.angle_partners`. Raises
        :class:`MoleculeError` if `other is self`
        z2Cannot angle an atom with itself! Atoms are: %s %sN)r   r   r   )rP   rQ   rK   rK   rL   angle_toQ  s
    zAtom.angle_toc             C   s4   | |krt d| |f | j| |j|  dS )aV  
        Log this atom as dihedral-ed to another atom.

        Parameters
        ----------
        other : :class:`Atom`
            An atom that will be added to `dihedral_partners`

        Notes
        -----
        This action adds `self` to `other.dihedral_partners`. Raises
        :class:`MoleculeError` if `other is self`
        z5Cannot dihedral an atom with itself! Atoms are: %s %sN)r   r   r   )rP   rQ   rK   rK   rL   dihedral_tog  s
    zAtom.dihedral_toc             C   s4   | |krt d| |f | j| |j|  dS )aR  
        Log this atom as 1-5 partners to another atom

        Parameters
        ----------
        other : :class:`Atom`
            An atom that will be added to `tortor_partners`

        Notes
        -----
        This action adds `self` to `other.tortor_partners`. Raises
        :class:`MoleculeError` if `other is self`
        z7Cannot coupled-dihedral atom to itself Atoms are: %s %sN)r   r   r   )rP   rQ   rK   rK   rL   	tortor_to}  s
    zAtom.tortor_toc             C   s4   | |krt d| |f | j| |j|  dS )aW  
        Add one atom to my arbitrary exclusion list

        Parameters
        ----------
        other : :class:`Atom`
            An atom that will be added to `exclusion_partners`.

        Notes
        -----
        This action adds `self` to `other.exclusion_partners`. Raises
        :class:`MoleculeError` if `other is self`
        z4Cannot exclude an atom from itself! Atoms are: %s %sN)r   r   r   )rP   rQ   rK   rK   rL   exclude  s
    zAtom.excludec             C   sD   t | jt | j t | j t | j t | j }tt|| _dS )a  
        For extra points, the exclusion partners may be filled before the bond,
        angle, dihedral, and tortor partners. Since we don't want memory of
        these exclusions if any of those topological features were to break, we
        want to *remove* those from the exclusion list. This function makes sure
        that nothing in the bond, angle, dihedral, and tortor lists appears in
        the exclusion list.
        N)r   r   r   r   r   r   r   rU   )rP   ZexcludesrK   rK   rL   prune_exclusions  s    
&zAtom.prune_exclusionsc             C   s   | j |j kS )N)rf   )rP   rQ   rK   rK   rL   __gt__  s    zAtom.__gt__c             C   s   | j |j k S )N)rf   )rP   rQ   rK   rK   rL   __lt__  s    zAtom.__lt__c             C   s
   | |k  S )NrK   )rP   rQ   rK   rK   rL   __ge__  s    zAtom.__ge__c             C   s
   | |k S )NrK   )rP   rQ   rK   rK   rL   __le__  s    zAtom.__le__c             C   s^   d| j | jf }| jd k	r>t| jdr>|d| jj | jjf  S | jd k	rV|d| j  S |d S )Nz<Atom %s [%d]rf   z; In %s %d>z; In object %r>>)r   rf   r   ru   )rP   startrK   rK   rL   __repr__  s    
zAtom.__repr__c             C   s   t | j| j| j| j| j| j| j| j| j	| j
| j| j| j| j| j| j| j| j| j| j| j| jd}x6dD ].}yt| |||< W qf tk
r   wfY qfX qfW |S )N)r   rt   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )r   r   r   r   r   r   r   r   r   r   r   weights_frame_type)dictr   rt   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r~   rN   )rP   ZretvalrW   rK   rK   rL   r\     s    

zAtom.__getstate__c             C   sr   g | _ g | _g | _g | _g | _d | _d| _g g g   | _| _| _	g g g   | _
| _| _g | _i | _| j| d S )Nr   )r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r[   update)rP   drK   rK   rL   __setstate__  s    zAtom.__setstate__)Nr   r   r   Nr   r   r   r   r   r   r   r   r   r   r_   NNNN)Tr   )/rg   rh   ri   rj   rq   classmethodr   r   rk   r   r   r   r   r   r   setterr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r\   r   rK   rK   rK   rL   r      sl    a    
*						
Cc                   s   e Zd ZdZ fddZedd Zedd Zedd	 Zed
d Z	edd Z
edd Zedd Zeedd Zejedd Z  ZS )r;   a  
    An extra point is a massless, virtual site that is used to extend the
    flexibility of partial atomic charge fitting. They can be used to, for
    instance, give atoms permanent multipoles in a fixed-charge format.

    However, virtual sites are massless particles whose position is fixed with
    respect to a particular frame of reference. As a result, they must be
    treated specially when running dynamics. This class extends the `Atom` class
    with extra functionality specific to these "Extra points" or "virtual
    sites". See the documentation for the :class:`Atom` class for more
    information.

    Parameters
    ----------
    weights : list of float, optional, keyword-only
        This is the list of weights defining its frame.

    See Also
    --------
    :class:`Atom` -- The ExtraPoint constructor also takes all `Atom` arguments
    as well, and shares all of the same properties
    c                s8   d|kr| d| _nd | _tt| j|| d | _d S )Nr   )r{   r   superr;   rq   r   )rP   argskwargs)	__class__rK   rL   rq     s
    zExtraPoint.__init__c             C   s$   y
| j d S  tk
r   dS X dS )z
        The parent atom of this extra point is the atom it is bonded to (there
        should only be 1 bond partner, but the parent is defined as its first)
        r   N)r   r   )rP   rK   rK   rL   parent  s    
zExtraPoint.parentc                s<   y"t  jg fdd jjD  S  tk
r6   g S X dS )z6 List of all atoms bonded to this atom and its parent c                s   g | ]}| k	r|qS rK   rK   )rV   x)rP   rK   rL   
<listcomp>(  s    z,ExtraPoint.bond_partners.<locals>.<listcomp>N)r   r  r   rN   )rP   rK   )rP   rL   r   $  s    "zExtraPoint.bond_partnersc             C   s"   y| j jS  tk
r   g S X dS )z6 List of all atoms angled to this atom and its parent N)r  r   rN   )rP   rK   rK   rL   r   ,  s    zExtraPoint.angle_partnersc             C   s"   y| j jS  tk
r   g S X d S )N)r  r   rN   )rP   rK   rK   rL   r   4  s    zExtraPoint.dihedral_partnersc             C   s"   y| j jS  tk
r   g S X d S )N)r  r   rN   )rP   rK   rK   rL   r   ;  s    zExtraPoint.tortor_partnersc             C   s"   y| j jS  tk
r   g S X d S )N)r  r   rN   )rP   rK   rK   rL   r   B  s    zExtraPoint.exclusion_partnersc             C   sx  | j dk	r| j S t| jjdkr4d}d}x"| jjD ]}| |krF|}q4|}q4W |dk	r^|dk	sftd|jj}|j| jkr|j}n|j}y0| j	| j
| j  }}}|j	|j
|j  }	}
}W n  tk
r   t| d| _ Y nVX ||	 ||
 ||   }}}|| ||  ||  |kr"t| d| _ nt| d| _ | j S t| jjdkrVd}x:| jjD ].}| |krT|j| jkrx|j}n|j}P qTW |dk	stdyL|j	|j
|j  }}}| jj	| jj
| jj  }	}
}| j	| j
| j  }}}W n tk
r   d}xD| jjD ]8}| |kr||kr|j| jkr2|j}n|j}P qW |dkrRtd| jjdkr|jdkr|jdkrt| d| _ nt| d| _ Y nX ||	 ||
 || g}||	 ||
 || g}td	d
 t||D }tdd
 t||D }tdd
 t||D }t|t|t|  }|tjd k rHt| d| _ nt| d| _ nt| jjdkrrt| | _ | j S )a5  
        The type of frame used for this extra point. Whether the EP lies
        beyond (outside) or between (inside) the is determined from the
        positions of each atom in the frame and the extra point. If those are
        not set, the EP is assumed to be between the two points in the frame
        NrD   zStrange bond pattern detectedTF      r   c             S   s   g | ]\}}|| qS rK   rK   )rV   r  yrK   rK   rL   r    s    z)ExtraPoint.frame_type.<locals>.<listcomp>c             S   s   g | ]\}}|| qS rK   rK   )rV   r  r  rK   rK   rL   r    s    c             S   s   g | ]\}}|| qS rK   rK   )rV   r  r  rK   rK   rL   r    s       )r   lenr  r   AssertionErrorrt   reqrm   rn   r   r   r   rN   r<   RuntimeErrorr   r=   sumr   mathacossqrtZpir>   )rP   mybondZ	otherbondbondZbonddistZ	otheratomZx1Zy1Zz1Zx2Zy2Zz2dxZdyZdzZ
other_atomZx3Zy3Zz3Zother_atom2Zvec1Zvec2Zdot11Zdot22Zdot12r   rK   rK   rL   
frame_typeK  s~    



zExtraPoint.frame_typec             C   s   | j S )N)r   )rP   rK   rK   rL   radii  s    zExtraPoint.radiic             C   s
   || _ d S )N)r   )rP   rJ   rK   rK   rL   r    s    )rg   rh   ri   rj   rq   rk   r  r   r   r   r   r   r  r   r  r   __classcell__rK   rK   )r  rL   r;     s   
	Yc               @   s*   e Zd ZdZd
ddZdd Zdd Zd	S )r<   a  
    This class defines a frame of reference for a given extra point with a frame
    of reference defined by 2 particles

    Parameters
    ----------
    ep : :class:`ExtraPoint`
        The extra point defined by this frame
    inside : ``bool``
        If True, the extra point is contained inside the curve connecting all
        points in the frame of reference. If False, the point is outside that
        curve. See figures below for example

    Figures
    -------
    In the figures, x marks the real particles, e marks the extra point
    (properly numbered). The numbers refer to the ordering this class expects
    those atoms to be in. The real particle that is the parent of the extra
    point is shown in upper-case

    Inside  : x1-----e--X2

    Outside : x1--------X2--e
    Fc             C   s   || _ || _d S )N)r   inside)rP   r   r  rK   rK   rL   rq     s    z#TwoParticleExtraPointFrame.__init__c          	      sn   y fdd j jjD \}W n  ttfk
r>   tdY nX |j j jkr\ j j|jfS  j j|jfS dS )z
        Returns the particles involved in the frame

        Returns
        -------
        a1, a2 : :class:`Atom`, :class:`Atom`
            a1 is the parent atom of the extra point. a2 is the other atom
            bonded to the parent atom
        c                s   g | ]} j |kr|qS rK   )r   )rV   r  )rP   rK   rL   r    s    z8TwoParticleExtraPointFrame.get_atoms.<locals>.<listcomp>zBad bond pattern in EP frameN)r   r  r   
ValueError	TypeErrorr  rm   rn   )rP   r  rK   )rP   rL   	get_atoms  s    
z$TwoParticleExtraPointFrame.get_atomsc             C   s   | j }t|jjdkrtd|jj\}}||krD|jj}|jj}n|jj}|jj}| jrn|| | || fS || | ||  fS dS )a  
        Returns the weights for the two particles

        Returns
        -------
        w1, w2 : ``float, float``
            w1 is the weight of particle 1 (the parent atom), whereas w2 is the
            weight of particle 2 (the atom bonded to the parent atom)
        rD   zCEP parent bond pattern inconsistent with 2-point virtual site frameN)r   r	  r  r   r  rt   r  r  )rP   r   b1b2r1r2rK   rK   rL   get_weights  s    

z&TwoParticleExtraPointFrame.get_weightsN)F)rg   rh   ri   rj   rq   r  r  rK   rK   rK   rL   r<     s   
c               @   s8   e Zd ZdZdddZdd Zeddd	Zd
d ZdS )r=   a  
    This class defines a frame of reference for a given extra point with a frame
    of reference defined by 3 particles

    Parameters
    ----------
    ep : :class:`ExtraPoint`
        The extra point defined by this frame
    inside : ``bool``
        If True, the extra point is contained inside the curve connecting all
        points in the frame of reference. If False, the point is outside that
        curve. See figures below for example

    Figures
    -------
    In the figures, x marks the real particles, e marks the extra point
    (properly numbered). The numbers refer to the ordering this class expects
    those atoms to be in. The real particle that is the parent of the extra
    point is shown in upper-case

    Inside :         X
                    /|\
                   / e \
                  x     x
    Outside :
                     e
                     |
                     X
                    / \
                   /   \
                  x     x
    Tc             C   s   || _ || _d S )N)r   r  )rP   r   r  rK   rK   rL   rq     s    z%ThreeParticleExtraPointFrame.__init__c          	      s   y  fdd j jjD \}}W n  ttfk
r@   tdY nX |j j jkrX|j}n|j}|j j jkrt|j}n|j} j j||fS )a'  
        Returns the particles involved in the frame

        Returns
        -------
        a1, a2, a3 : :class:`Atom`, :class:`Atom`, :class:`Atom`
            a1 is the parent atom of the extra point. a2 and a3 are the other
            atoms that are both bonded to the parent atom
        c                s   g | ]} j |kr|qS rK   )r   )rV   r  )rP   rK   rL   r  /  s    z:ThreeParticleExtraPointFrame.get_atoms.<locals>.<listcomp>z'Unsupported bonding pattern in EP frame)r   r  r   r  r  r  rm   rn   )rP   r  r  oatom1oatom2rK   )rP   rL   r  $  s    
 z&ThreeParticleExtraPointFrame.get_atomsNc	             C   s  || j ks|| j krtd||jkr8||j kr8td||krHtd|dksX|dkrxR| jD ]H}	||	kr|	jdkr~td|	jj}||	kr`|	jdkrtd|	jj}q`W |dkrr|dkrr||jkr,x2|jD ](}	||	krq|	jdkrtd|	jj}qW t|| ||  ||  d| |  }nDx|j	D ]*}
||
kr4||
j
k	r4|
jjt }P q4W dstdn>|dkrt|| ||  ||  d| |  }n|t9 }t|| tkrtd	t|d t|d
  | S )a  
        This function determines the necessary bond length between an ExtraPoint
        and its parent atom from the weights that are calculated in
        ``get_weights``.

        Parameters
        ----------
        parent : :class:`Atom`
            The parent atom to the ExtraPoint
        a1 : :class:`Atom`
            The first atom in the frame bonded to the parent
        a2 : :class:`Atom`
            The second atom in the frame bonded to the parent
        w1 : float
            The first weight defining the ExtraPoint position wrt ``a1``
        w2 : float
            The second weight defining the ExtraPoint position wrt ``a2``
        dp1 : float, optional
            Equilibrium distance between parent and a1. If None, a bond with a
            bond type must be defined between parent and a1, and the distance
            will be taken from there. Units must be Angstroms. Default is None
        dp2 : float, optional
            Same as dp1 above, but between atoms parent and a2. Either both dp1
            and dp2 should be None or neither should be. If one is None, the
            other will be ignored if not None.
        theteq : float, optional
            Angle between bonds parent-a1 and parent-a2. If None, d12 (below)
            will be used to calculate the angle. If both are None, the angle
            or d12 distance will be calculated from parametrized bonds or
            angles between a1, parent, and a2. Units of degrees. Default None.
        d12 : float, optional
            Distance between a1 and a2 as an alternative way to specify theteq.
            Default is None.

        Returns
        -------
        dist : float
            The distance between the ExtraPoint and its parent atom that
            satisfies the weights

        Notes
        -----
        parent must form a Bond with both a1 and a2.  Then, if a1-parent-a2
        forms an angle and it has an assigned type, that equilibrium value is
        used. Otherwise, the a1-a2 bond distance is used.  If neither of those
        are defined, a ValueError is raised.

        Raises
        ------
        ValueError if the necessary geometry requirements are not set or if the
        two weights are different
        z/Parent atom not bound to other 2 atoms in framez.No geometry defined between 2 non-parent atomsz*Currently only equal weights are supportedNz)Could not determine virtual site geometryrD   FzCould not find matching anglez&Cannot deal with asymmetry in EP frameg      ?)r   r  r   r   rt   r   r  r  r  r   rn   theteqr
   r
  absr   cos)r  a1a2Zw1Zw2Zdp1Zdp2r"  Zd12r  angrK   rK   rL   from_weights<  sH    6

 
,
,z)ThreeParticleExtraPointFrame.from_weightsc             C   s  | j }t|jjdkrtd|jj\}}}||kr@|| }}n||krR|| }}d}x&|jjD ]}||kr`||kr`d}P q`W |r|jj}|jj}|jjt	 }	t
|| ||  d| | t
|	  }
n|j|jkr|j}n|j}|j|jkr|j}n|j}||jkrtdd}
x"|jD ]}||kr |jj}
q W |
dkrNtd|jj}|jj}|jjt
|| d	|
 |
   }| jrd
| |d |d fS d
| | d | d fS dS )a>  
        Returns the weights for the three particles

        Returns
        -------
        w1, w2, w3 : ``float, float, float``
            w1 is the weight of particle 1 (the parent atom), whereas w2 and w3
            are the weights of particles 2 and 3 (the atoms bonded to the
            parent atom)
        r  zCEP parent bond pattern inconsistent with 3-point virtual site frameFTrD   znEP frame definition incomplete for 3-point virtual site... cannot determine distance between particles 2 and 3NzGCannot determine 2-3 particle distance in three-site virtual site frameg      ?r   )r   r	  r  r   r  r   rt   r  r"  r
   r  r  r$  rm   rn   r   r  r  )rP   r   r  r  Zb3foundr'  r  r  thetareq23r%  r&  r  req12req13weightrK   rK   rL   r    sL    
.

"z(ThreeParticleExtraPointFrame.get_weights)T)NNNN)	rg   rh   ri   rj   rq   r  staticmethodr(  r  rK   rK   rK   rL   r=     s    
`c               @   s*   e Zd ZdZd
ddZdd Zdd Zd	S )r>   a  
    This class defines a frame of reference for a given extra point with a frame
    of reference defined by 3 particles, but with the virtual site out of the
    plane of those 3 particles. For example, TIP5P

    Parameters
    ----------
    ep : :class:`ExtraPoint`
        The extra point defined by this frame
    angle : ``float``
        The angle out-of-plane that the extra point is. By default, it is half
        of the angle of a tetrahedron. Given in degrees

    Figures
    -------
    In the figures, x marks the real particles, e marks the extra point
    (properly numbered). The numbers refer to the ordering this class expects
    those atoms to be in. The real particle that is the parent of the extra
    point is shown in upper-case

                   e   e
                    \ /
                     X
                    / \
                   /   \
                  x     x
    Gz^K@c             C   s   || _ || _d S )N)r   r   )rP   r   r   rK   rK   rL   rq     s    z"OutOfPlaneExtraPointFrame.__init__c          	   C   s   ydd | j jjD \}}W n  ttfk
r<   tdY nX |j| j jkrT|j}n|j}|j| j jkrp|j}n|j}| j j||fS )a7  
        Returns the particles involved in the frame

        Returns
        -------
        a1, a2, a3 : :class:`Atom`, :class:`Atom`, :class:`Atom`
            a1 is the parent atom of the extra point. a2 and a3 are the other
            atoms that are both bonded to the parent atom but are not EPs
        c             S   s(   g | ] }t |jtst |jts|qS rK   )r   rm   r;   rn   )rV   r  rK   rK   rL   r  
  s    z7OutOfPlaneExtraPointFrame.get_atoms.<locals>.<listcomp>z'Unsupported bonding pattern in EP frame)r   r  r   r  r  r  rm   rn   )rP   r  r  r   r!  rK   rK   rL   r    s    
z#OutOfPlaneExtraPointFrame.get_atomsc       !         s  | j }g }d}xP|jjD ]D}t|jts@t|jts@|| q||kr|dk	rXtd|}qW t	|dkrttd|dkrt
d|dd \}}|jj}|jj}d}	x&|jjD ]}
||
kr||
krd}	P qW |	r&|
j}|jj}|jj}|
jjt }t|| ||  d| | t|  }n|j|jkr<|j}n|j}|j|jkrX|j}n|j}||jkrrt
dd}x"|jD ]}||kr~|jj}q~W |dkrt
d	dt|||   t }tjtj}||9 }||9 }||9 }t| jt }t| jt }t|t }|| | }||jj | | }||jj | t|| d
| |   }|  \}}}yp|j|j |j|j |j|j f}|j|j |j|j |j|j f}| j j|j | j j|j | j j|j fW nN t k
r0   x6| j jjD ](}|| j krP t|tr | }P q W Y nX |d |d  |d |d   |d |d  |d |d   |d |d  |d |d   f tt! fddt"dD }tt!fddt"dD }t! fddt"dD }|||  } | dk r| n|}|d |d |fS )aB  
        Returns the weights for the three particles

        Returns
        -------
        w1, w2, w3 : ``float, float, float``
            w1 and w2 are the weights with respect to the second two particles
            in the frame (i.e., NOT the parent atom). w3 is the weight of the
            cross-product
        Nz&multiple bonds detected to extra pointrD   zTEP parent bond pattern inconsistent with an out-of-plane, 3-point virtual site framez&No EP bond found... should not be hereFTznEP frame definition incomplete for 3-point virtual site... cannot determine distance between particles 2 and 3zGCannot determine 2-3 particle distance in three-site virtual site frameg      ?r   r   c                s   g | ]} |  |  qS rK   rK   )rV   rd   )crossrK   rL   r    s    z9OutOfPlaneExtraPointFrame.get_weights.<locals>.<listcomp>r  c                s   g | ]} |  |  qS rK   rK   )rV   rd   )v1erK   rL   r    s    c                s   g | ]}|  |  qS rK   rK   )rV   rd   )r1  r2  rK   rL   r    s    )#r   r  r   r   rm   r;   rn   r   r  r	  r  rt   r  r   r"  r
   r  r  r$  r   Zasinr   rE   r   Zconversion_factor_toZ
nanometersZsinr   r  r   r   r   rN   r  r   )!rP   r   Zregbondsr  r  r  r  r,  r-  r)  r'  Zt213r  r  r*  r+  r%  r&  Zlength_convZsinOOPZcosOOPZsin213ZlenCrossZweightCrossr.  Za3Zv12Zv13aZlencrossZlenv1eZv1edotcrossZcosthetarK   )r1  r2  rL   r    s    .

*"","  z%OutOfPlaneExtraPointFrame.get_weightsN)r0  )rg   rh   ri   rj   rq   r  r  rK   rK   rK   rL   r>     s   
c               @   sl   e Zd ZdZdddZdd Zdd	 Zed
d Zej	dd Zdd Z
dd Zdd Zdd Zdd ZdS )r   aQ  
    A covalent bond connecting two atoms.

    Parameters
    ----------
    atom1 : :class:`Atom`
        The first atom involved in the bond
    atom2 : :class:`Atom`
        The other atom involved in the bond
    type : :class:`BondType` or None, optional
        The bond type that defines the parameters for this bond. Default is None
    order : float, optional
        The bond order of this bond. Bonds are classified as follows:
            1.0 -- single bond
            2.0 -- double bond
            3.0 -- triple bond
            1.5 -- aromatic bond
            1.25 -- amide bond
        Default is 1.0

    Notes
    -----
    You can test whether an :class:`Atom` is contained within the bond using the
    `in` operator. A `MoleculeError` is raised if `atom1` and `atom2` are identical.
    This bond instance is `append`ed to the `bonds` list for both `atom1` and
    `atom2` and is automatically removed from those lists upon garbage
    collection

    Examples
    --------
    >>> a1, a2 = Atom(), Atom()
    >>> bond = Bond(a1, a2)
    >>> a1 in bond and a2 in bond
    True
    N      ?c             C   s   ||krt d||f t|tr4t|tr4t d|| _|| _| jj|  | jj|  || || _d| _	d| _
|| _dS )z Bond constructor z,Cannot bond atom to itself! Atoms are: %s %sz3Cannot bond two virtual sites/extra points togetherr   N)r   r   r;   rm   rn   r   r   r   rt   funct_orderorder)rP   rm   rn   rt   r7  rK   rK   rL   rq     s    
zBond.__init__c             C   s   || j kp|| jkS )z6 Quick and easy way to see if an Atom is in this Bond )rm   rn   )rP   thingrK   rK   rL   rs     s    zBond.__contains__c             C   sR   t | jj|  t | jj|  t | jj| j t | jj| j d | _ | _| _dS )z
        Deletes this bond from the atoms that make it up. This method removes
        this bond from the `bonds` list for both atom1 and atom2 as well as
        removing atom1 from atom2.bond_partners and vice versa.
        N)r}   rm   r   rn   r   rt   )rP   rK   rK   rL   rv     s
    zBond.deletec             C   s   | j S )z< Bond order. See description in :class:`Bond` argument list )r6  )rP   rK   rK   rL   r7    s    z
Bond.orderc             C   s   t || _dS )zg
        Order of the bond. Must be a float, or ValueError or TypeError will be
        raised
        N)floatr6  )rP   rJ   rK   rK   rL   r7    s    c             C   sB   d| j | jfkrdS ytt| j | jS  tk
r<   dS X dS )z Measures the current bond

        Returns
        -------
        measurement : float or None
            If the atoms have coordinates, returns the distance between the two
            atoms. If any coordinates are missing, returns None
        N)rm   rn   r  r  r   rN   )rP   rK   rK   rL   measure  s    	zBond.measurec             C   s   |   }|dk	r|tj S dS )z# Same as "measure", but with units N)r:  rE   r   )rP   mrK   rK   rL   umeasure  s    zBond.umeasurec             C   s:   |   }| jdks|dkrdS || jj }| jj| | S )a   Measures the current bond energy

        Returns
        -------
        energy : float or None
            Bond strain energy in kcal/mol. Return value is None if either the
            coordinates of either atom is not set or the bond type is not set
        N)r:  rt   r  k)rP   r   r  rK   rK   rL   energy  s
    	zBond.energyc             C   s   |   }|dk	r|tj S dS )z" Same as energy(), but with units N)r>  rE   r   )rP   enerK   rK   rL   uenergy  s    zBond.uenergyc             C   s   dt | j| j| j| j f S )Nz<%s %r--%r; type=%r>)rt   rg   rm   rn   )rP   rK   rK   rL   r   
  s    zBond.__repr__)Nr4  )rg   rh   ri   rj   rq   rs   rv   rk   r7  r   r:  r<  r>  r@  r   rK   rK   rK   rL   r     s   #
c                   sl   e Zd ZdZdddZedd Zdd Zd	d
 Ze	 Z
 fddZdd Zedd Zedd Z  ZS )r   a  
    A bond type with a set of bond parameters

    Parameters
    ----------
    k : ``float``
        Force constant in kcal/mol/Angstrom^2
    req : ``float``
        Equilibrium bond distance in Angstroms
    list : :class:`TrackedList`
        A list of :class:`BondType`s in which this is a member

    Inherited Attributes
    --------------------
    idx : int
        The index of this BondType inside its containing list

    Notes
    -----
    Two `BondType`s are equal if their `k` and `req` attributes are equal

    Examples
    --------
    >>> bt1 = BondType(10.0, 1.0)
    >>> bt2 = BondType(10.0, 1.0)
    >>> bt1 is bt2
    False
    >>> bt1 == bt2
    True
    >>> bt1.idx # Not in a list or container
    -1

    As part of a list, they can be indexed

    >>> bond_list = []
    >>> bond_list.append(BondType(10.0, 1.0, list=bond_list))
    >>> bond_list.append(BondType(10.0, 1.0, list=bond_list))
    >>> bond_list[0].idx
    0
    >>> bond_list[1].idx
    1
    Nc             C   s@   t |  t|tjtjd  | _t|tj| _|| _d| _	d S )NrD   r_   )
rw   rq   rM   rE   r   r   r=  r  rU   rb   )rP   r=  r  rU   rK   rK   rL   rq   ;  s
    
zBondType.__init__c             C   s(   t | j|j tk o&t | j|j tk S )N)r#  r=  r   r  )rP   rQ   rK   rK   rL   __eq__B  s    zBondType.__eq__c             C   s   dt | j| j| jf S )Nz<%s; k=%.3f, req=%.3f>)rt   rg   r=  r  )rP   rK   rK   rL   r   F  s    zBondType.__repr__c             C   s   | t kr| S t| j| jS )z Not bound to any list )r:   r   r=  r  )rP   rK   rK   rL   r   I  s    zBondType.__copy__c                s   | t krdS tt|  S )z
        Special-case NoUreyBradley, which should be a singleton. So if it's
        NoUreyBradley, return the same object. Otherwise, create a new one
        r:   )r:   r   r   
__reduce__)rP   )r  rK   rL   rB  R  s    zBondType.__reduce__c             C   s   t t| jtt| jtfS )N)hashroundr=  _TINY_DIGITSr  )rP   rK   rK   rL   __hash__[  s    zBondType.__hash__c             C   s   | j tj tjd  S )NrD   )r=  rE   r   r   )rP   rK   rK   rL   uk^  s    zBondType.ukc             C   s   | j tj S )N)r  rE   r   )rP   rK   rK   rL   ureqb  s    zBondType.ureq)N)rg   rh   ri   rj   rq   rT   rA  r   r   r]   r\   rB  rF  rk   rG  rH  r  rK   rK   )r  rL   r     s   *
	c               @   sR   e Zd ZdZdddZdd Zdd Zd	d
 Zdd Zdd Z	dd Z
dd ZdS )r   a  
    A valence angle between 3 atoms separated by two covalent bonds.

    Parameters
    ----------
    atom1 : :class:`Atom`
        An atom one end of the valence angle
    atom2 : :class:`Atom`
        The atom in the middle of the valence angle bonded to both other atoms
    atom3 : :class:`Atom`
        An atom on the other end of the valence angle to atom1
    type : :class:`AngleType`
        The AngleType object containing the parameters for this angle

    Notes
    -----
    An Angle can contain bonds or atoms. A bond is contained if it exists
    between atoms 1 and 2 or atoms 2 and 3.

    Examples
    --------
    >>> a1, a2, a3 = Atom(), Atom(), Atom()
    >>> angle = Angle(a1, a2, a3)
    >>> Bond(a1, a2) in angle and Bond(a3, a2) in angle
    True
    >>> a1 in angle and a2 in angle and a3 in angle
    True
    >>> Bond(a1, a3) in angle # this is not part of the angle definition
    False
    Nc             C   s   ||ks||ks||kr*t d|||f || _|| _|| _|j|  |j|  |j|  || _|| || || d| _d S )Nz0Cannot angle atom to itself! Atoms are: %s %s %sr   )	r   rm   rn   ro   r   r   rt   r   r5  )rP   rm   rn   ro   rt   rK   rK   rL   rq     s    


zAngle.__init__c             C   sP   t |tr(|| jkp&|| jkp&|| jkS | j|kr<| j|kpN| j|koN| j|kS )zA Quick and easy way to see if an Atom or a Bond is in this Angle )r   r   rm   rn   ro   )rP   rr   rK   rK   rL   rs     s    
zAngle.__contains__c             C   s   t | jj|  t | jj|  t | jj|  t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j d | _ | _ | _| _dS )z
        Deletes this angle from the atoms that make it up. This method removes
        this angle from the `angles` list for atom1, atom2, and atom3 as well as
        removing each atom form each others' angle partner lists
        N)r}   rm   r   rn   ro   r   rt   )rP   rK   rK   rL   rv     s    zAngle.deletec             C   sD   d| j | j| jfkrdS yt| j | j| jS  tk
r>   dS X dS )z Measures the current angle

        Returns
        -------
        measurement : float or None
            If the atoms have coordinates, returns the angle between the three
            atoms. If any coordinates are missing, returns None
        N)rm   rn   ro   r   rN   )rP   rK   rK   rL   r:    s    	zAngle.measurec             C   s   |   }|dk	r|tj S dS )z# Same as measure(), but with units N)r:  rE   rG   )rP   r;  rK   rK   rL   r<    s    zAngle.umeasurec             C   s>   |   }| jdks|dkrdS || jj t }| jj| | S )a
   Measures the current angle energy

        Returns
        -------
        energy : float or None
            Angle strain energy in kcal/mol. Return value is None if either the
            coordinates of either atom is not set or the angle type is not set
        N)r:  rt   r"  r
   r=  )rP   r3  ZdarK   rK   rL   r>    s
    	zAngle.energyc             C   s   |   }|dk	r|tj S dS )z" Same as energy(), but with units N)r>  rE   r   )rP   r?  rK   rK   rL   r@    s    zAngle.uenergyc             C   s    dt | j| j| j| j| j f S )Nz<%s %r--%r--%r; type=%r>)rt   rg   rm   rn   ro   )rP   rK   rK   rL   r     s    zAngle.__repr__)N)rg   rh   ri   rj   rq   rs   rv   r:  r<  r>  r@  r   rK   rK   rK   rL   r   h  s   
c               @   s\   e Zd ZdZdddZedd Zdd Zd	d
 Ze	 Z
dd Zedd Zedd ZdS )r   a  
    An angle type with a set of angle parameters

    Parameters
    ----------
    k : ``float``
        Force constant in kcal/mol/radians^2
    theteq : ``float``
        Equilibrium angle in Degrees
    list : :class:`TrackedList`
        A list of `AngleType`s in which this is a member

    Inherited Attributes
    --------------------
    idx : ``int``
        The index of this AngleType inside its containing list

    Notes
    -----
    Two `AngleType`s are equal if their `k` and `theteq` attributes are equal

    Examples
    --------
    >>> at1 = AngleType(10.0, 180.0)
    >>> at2 = AngleType(10.0, 180.0)
    >>> at1 is at2
    False
    >>> at1 == at2
    True
    >>> at1.idx # not part of any list or iterable
    -1

    As part of a list, they can be indexed

    >>> angle_list = []
    >>> angle_list.append(AngleType(10.0, 180.0, list=angle_list))
    >>> angle_list.append(AngleType(10.0, 180.0, list=angle_list))
    >>> angle_list[0].idx
    0
    >>> angle_list[1].idx
    1
    Nc             C   s@   t |  t|tjtjd  | _t|tj| _d| _	|| _
d S )NrD   r_   )rw   rq   rM   rE   r   radiansr=  rG   r"  rb   rU   )rP   r=  r"  rU   rK   rK   rL   rq     s
    
zAngleType.__init__c             C   s(   t | j|j tk o&t | j|j tk S )N)r#  r=  r   r"  )rP   rQ   rK   rK   rL   rA    s    zAngleType.__eq__c             C   s   dt | j| j| jf S )Nz<%s; k=%.3f, theteq=%.3f>)rt   rg   r=  r"  )rP   rK   rK   rL   r     s    zAngleType.__repr__c             C   s   t | j| jS )N)r   r=  r"  )rP   rK   rK   rL   r     s    zAngleType.__copy__c             C   s   t t| jtt| jtfS )N)rC  rD  r=  rE  r"  )rP   rK   rK   rL   rF  "  s    zAngleType.__hash__c             C   s   | j tj tjd  S )NrD   )r=  rE   r   rI  )rP   rK   rK   rL   rG  %  s    zAngleType.ukc             C   s   | j tj S )N)r"  rE   rG   )rP   rK   rK   rL   utheteq)  s    zAngleType.utheteq)N)rg   rh   ri   rj   rq   rT   rA  r   r   r]   r\   rF  rk   rG  rJ  rK   rK   rK   rL   r     s   *
c               @   st   e Zd ZdZdddZedd Zejdd Zd	d
 Zdd Z	dd Z
dd Zdd Zdd Zdd Zdd ZdS )r"   a   A valence dihedral between 4 atoms separated by three covalent bonds.

    Parameters
    ----------
    atom1 : :class:`Atom`
        An atom on one end of the valence dihedral bonded to atom 2
    atom2 : :class:`Atom`
        An atom in the middle of the valence dihedral bonded to atom1 and atom3
    atom3 : :class:`Atom`
        An atom in the middle of the valence dihedral bonded to atom2 and atom4
    atom4 : :class:`Atom`
        An atom on the other end of the valence dihedral bonded to atom 3
    improper : ``bool``
        If True, this is an Amber-style improper torsion, where atom3 is the
        "central" atom bonded to atoms 1, 2, and 4 (atoms 1, 2, and 4 are *only*
        bonded to atom 3 in this instance)
    ignore_end : ``bool``
        If True, the end-group interactions for this torsion are ignored, either
        because it is involved in a ring where the end-group atoms are excluded
        or because it is one term in a multi-term dihedral expansion
    type : :class:`DihedralType`
        The DihedralType object containing the parameters for this dihedral

    Notes
    -----
    A Dihedral can contain bonds or atoms. A bond is contained if it exists
    between atoms 1 and 2, between atoms 2 and 3, or between atoms 3 and 4.

    Examples
    --------
    >>> a1, a2, a3, a4 = Atom(), Atom(), Atom(), Atom()
    >>> dihed = Dihedral(a1, a2, a3, a4)
    >>> Bond(a1,a2) in dihed and Bond(a3,a2) in dihed and Bond(a3,a4) in dihed
    True
    >>> a1 in dihed and a2 in dihed and a3 in dihed and a4 in dihed
    True
    >>> Bond(a1, a4) in dihed # this is not part of the angle definition
    False

    For improper torsions, the bond pattern is different

    >>> a1, a2, a3, a4 = Atom(), Atom(), Atom(), Atom()
    >>> dihed = Dihedral(a1, a2, a3, a4, improper=True)
    >>> Bond(a1,a3) in dihed and Bond(a2,a3) in dihed and Bond(a3,a4) in dihed
    True
    >>> Bond(a1,a2) in dihed # Not like a normal dihedral!
    False
    >>> a1 in dihed and a2 in dihed and a3 in dihed and a4 in dihed
    True
    FNc             C   s   t | |||| |p|}|j|  |j|  |j|  |j|  || _|| _|| _|rhd| _nB|| || || || || || d | _d S )Nr  )	rl   rq   r   r   rt   improper
ignore_end_functr   )rP   rm   rn   ro   rp   rK  rL  rt   rK   rK   rL   rq   c  s$    





zDihedral.__init__c             C   sH   | j d krB| jd k	r$t| jtr$dS | jd k	r>t| jtr>dS dS | j S )N	   r  r   )rM  rt   r   r$   r?   )rP   rK   rK   rL   r5  z  s    
zDihedral.functc             C   s
   || _ d S )N)rM  )rP   rJ   rK   rK   rL   r5    s    c             C   s   t |trt| |S | jrX| j|kr0| j|kpV| j|krD| j|kpV| j|koV| j|kS | j|krl| j|kp| j|kr| j|kp| j|ko| j|kS )zG Quick and easy way to find out if an Atom or Bond is in this Dihedral )	r   r   rl   rs   rK  rm   ro   rn   rp   )rP   r8  rK   rK   rL   rs     s    
zDihedral.__contains__c             C   s  t |tr^| j|jkr:| j|jkr:| j|jkr:| j|jkp\| j|jko\| j|jko\| j|jkS t|}t|dkrtdt	|j
t|f | jj|d kr| jj|d kr| jj|d kr| jj|d kp| jj|d ko| jj|d ko| jj|d ko| jj|d kS )a  
        Determines if this dihedral has the same atoms (or atom indexes) as
        another object

        Parameters
        ----------
        thing : :class:`Dihedral` or other ``iterable``
            A Dihedral or an iterable with 4 indexes that will be used to
            identify whether this torsion has the same atoms

        Returns
        -------
        is_same : ``bool``
            True if ``thing`` and ``self`` have the same atoms. ``False``
            otherwise

        Notes
        -----
        This raises a ``TypeError`` if thing is not a Dihedral and is not
        iterable

        Raises
        ------
        TypeError
        r  z)comparative %s has %d elements! Expect 4.r   r   rD   r  )r   r"   rm   rn   ro   rp   rU   r	  r  rt   rg   rf   )rP   r8  rK   rK   rL   
same_atoms  s"    
zDihedral.same_atomsc             C   s   t | jj|  t | jj|  t | jj|  t | jj|  | jst | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j d | _ | _ | _ | _| _dS )a  
        Deletes this dihedral from the atoms that make it up. This method
        removes this dihedral from the `dihedrals` list for atom1, atom2, atom3,
        and atom4 as well as removing each atom form each others' dihedral
        partner lists
        N)	r}   rm   r   rn   ro   rp   rK  r   rt   )rP   rK   rK   rL   rv     s$    zDihedral.deletec             C   sL   d| j | j| j| jfkrdS yt| j | j| j| jS  tk
rF   dS X dS )a   Measures the current dihedral angle

        Returns
        -------
        measurement : float or None
            If the atoms have coordinates, returns the dihedral angle between the four atoms in
            degrees. If any coordinates are missing, returns None
        N)rm   rn   ro   rp   r   rN   )rP   rK   rK   rL   r:    s    	zDihedral.measurec             C   s   |   }|dk	r|tj S dS )z# Same as measure(), but with units N)r:  rE   rG   )rP   r;  rK   rK   rL   r<    s    zDihedral.umeasurec          	   C   s   |   }|dks| jdkrdS |t9 }t| jtr\| jjdt| jj| | jj	t    S t| jt
rd}x6| jD ],}||jdt|j| |j	t    7 }qtW |d9 }|S tddS )a!   Measures the current dihedral angle energy

        Returns
        -------
        energy : float or None
            Dihedral angle energy in kcal/mol. Return value is None if either
            the coordinates of either atom is not set or the angle type is not
            set
        Nr   r   g      ?zLOnly DihedralType and DihedralTypeList are supported for energy calculations)r:  rt   r
   r   r#   phi_kr  r$  perphaser$   NotImplementedError)rP   phieZtermrK   rK   rL   r>    s    
$zDihedral.energyc             C   s   |   }|dk	r|tj S dS )z" Same as energy(), but with units N)r>  rE   r   )rP   r?  rK   rK   rL   r@  	  s    zDihedral.uenergyc             C   sT   | j rdt| j }n | jr,dt| j }n
t| j}d|| j| j| j| j| jf S )Nz%s [imp]z%s [ign]z<%s; %r--%r--%r--%r; type=%r>)rK  rt   rg   rL  rm   rn   ro   rp   )rP   r   rK   rK   rL   r   	  s    
zDihedral.__repr__)FFN)rg   rh   ri   rj   rq   rk   r5  r   rs   rO  rv   r:  r<  r>  r@  r   rK   rK   rK   rL   r"   /  s   2

1c               @   s\   e Zd ZdZdddZedd Zdd	 Zd
d Ze	 Z
dd Zedd Zedd ZdS )r#   a  
    A dihedral type with a set of dihedral parameters

    Parameters
    ----------
    phi_k : ``float``
        The force constant in kcal/mol
    per : ``int``
        The dihedral periodicity
    phase : ``float``
        The dihedral phase in degrees
    scee : ``float``, optional
        1-4 electrostatic scaling factor. Default is 1.0
    scnb : ``float``, optional
        1-4 Lennard-Jones scaling factor. Default is 1.0
    list : :class:`TrackedList`
        A list of `DihedralType`s in which this is a member

    Inherited Attributes
    --------------------
    idx : ``int``
        The index of this DihedralType inside its containing list

    Notes
    -----
    Two :class:`DihedralType`s are equal if their `phi_k`, `per`, `phase`,
    `scee`, and `scnb` attributes are equal

    Examples
    --------
    >>> dt1 = DihedralType(10.0, 2, 180.0, 1.2, 2.0)
    >>> dt2 = DihedralType(10.0, 2, 180.0, 1.2, 2.0)
    >>> dt1 is dt2
    False
    >>> dt1 == dt2
    True
    >>> dt1.idx # not part of any list or iterable
    -1

    As part of a list, they can be indexed

    >>> dihedral_list = []
    >>> dihedral_list.append(DihedralType(10.0, 2, 180.0, 1.2, 2.0,
    ...                                   list=dihedral_list))
    >>> dihedral_list.append(DihedralType(10.0, 2, 180.0, 1.2, 2.0,
    ...                                   list=dihedral_list))
    >>> dihedral_list[0].idx
    0
    >>> dihedral_list[1].idx
    1
          ?Nc             C   sX   t |  d| _t|tj| _t|| _t|tj	| _
|| _|| _|| _d| _d| _dS )z DihedralType constructor Tr_   N)rw   rq   lockedrM   rE   r   rP  intrQ  rG   rR  sceescnbrU   rb   )rP   rP  rQ  rR  rY  rZ  rU   rK   rK   rL   rq   ]	  s    

zDihedralType.__init__c             C   s\   t | j|j tk oZ| j|jkoZt | j|j tk oZt | j|j tk oZt | j|j tk S )N)r#  rP  r   rQ  rR  rY  rZ  )rP   rQ   rK   rK   rL   rA  l	  s     (zDihedralType.__eq__c             C   s>   dt | j| j| j| jf g}|d| j| jf  d|S )Nz%<%s; phi_k=%.3f, per=%d, phase=%.3f, z scee=%.3f, scnb=%.3f>r   )	rt   rg   rP  rQ  rR  r   rY  rZ  r   )rP   retstrrK   rK   rL   r   r	  s    zDihedralType.__repr__c             C   s   t | j| j| j| j| jS )N)r#   rP  rQ  rR  rY  rZ  )rP   rK   rK   rL   r   x	  s    zDihedralType.__copy__c             C   s:   t t| jtt| jtt| jtt| jtt| jtfS )N)rC  rD  rP  rE  rQ  rR  rY  rZ  )rP   rK   rK   rL   rF  }	  s    zDihedralType.__hash__c             C   s   | j tj S )N)rP  rE   r   )rP   rK   rK   rL   uphi_k	  s    zDihedralType.uphi_kc             C   s   | j tj S )N)rR  rE   rG   )rP   rK   rK   rL   uphase	  s    zDihedralType.uphase)rV  rV  N)rg   rh   ri   rj   rq   rT   rA  r   r   r]   r\   rF  rk   r\  r]  rK   rK   rK   rL   r#   &	  s   3
c               @   s   e Zd ZdZdddZedd Zdd	 Zd
d Ze	 Z
dd Zedd Zedd Zedd Zedd Zedd Zedd ZdS )r?   a  
    A Ryckaert-Bellemans type with a set of dihedral parameters

    Parameters (and Attributes)
    ---------------------------
    c0 : float
        The coefficient of the constant term in kcal/mol
    c1 : float
        The coefficient of the linear term in kcal/mol
    c2 : float
        The coefficient of the quadratic term in kcal/mol
    c3 : float
        The coefficient of the cubic term in kcal/mol
    c4 : float
        The coefficient of the quartic term in kcal/mol
    c5 : float
        The coefficient of the quintic term in kcal/mol
    scee : float, optional
        1-4 electrostatic scaling factor. Default is 1.0
    scnb : float, optional
        1-4 van der Waals scaling factor. Default is 1.0
    list : TrackedList=None
        A list of `RBTorsionType`s in which this is a member

    Inherited Attributes
    --------------------
    idx : int
        The index of this RBTorsionType inside its containing list

    Notes
    -----
    Two `RBTorsionType`s are equal if their coefficients are all equal

    Examples
    --------
    >>> dt1 = RBTorsionType(10.0, 20.0, 30.0, 40.0, 50.0, 60.0)
    >>> dt2 = RBTorsionType(10.0, 20.0, 30.0, 40.0, 50.0, 60.0)
    >>> dt1 is dt2
    False
    >>> dt1 == dt2
    True
    >>> dt1.idx # not part of any list or iterable
    -1

    As part of a list, they can be indexed

    >>> rb_torsion_list = []
    >>> rb_torsion_list.append(RBTorsionType(10.0, 20.0, 30.0, 40.0, 50.0, 60.0,
    ...                                      list=rb_torsion_list))
    >>> rb_torsion_list.append(RBTorsionType(10.0, 20.0, 30.0, 40.0, 50.0, 60.0,
    ...                                      list=rb_torsion_list))
    >>> rb_torsion_list[0].idx
    0
    >>> rb_torsion_list[1].idx
    1
          ?Nc
       
      C   sz   t |  t|tj| _t|tj| _t|tj| _t|tj| _t|tj| _	t|tj| _
|| _|| _|	| _d| _d S )Nr_   )rw   rq   rM   rE   r   c0c1c2c3c4c5rY  rZ  rU   rb   )
rP   r_  r`  ra  rb  rc  rd  rY  rZ  rU   rK   rK   rL   rq   	  s    
zRBTorsionType.__init__c             C   sx   t | j|j tk ovt | j|j tk ovt | j|j tk ovt | j|j tk ovt | j|j tk ovt | j|j tk S )N)r#  r_  r   r`  ra  rb  rc  rd  )rP   rQ   rK   rK   rL   rA  	  s    zRBTorsionType.__eq__c          	   C   s&   t | j| j| j| j| j| j| j| jS )N)	r?   r_  r`  ra  rb  rc  rd  rY  rZ  )rP   rK   rK   rL   r   	  s    zRBTorsionType.__copy__c          	   C   s(   d| j | j| j| j| j| j| j| jf S )NzW<RBTorsionType; c0=%.3f; c1=%.3f; c2=%.3f; c3=%.3f; c4=%.3f; c5=%.3f; scee=%s; scnb=%s>)r_  r`  ra  rb  rc  rd  rY  rZ  )rP   rK   rK   rL   r   	  s    zRBTorsionType.__repr__c          	   C   sD   t t| jtt| jtt| jtt| jtt| jtt| jtfS )N)	rC  rD  r_  rE  r`  ra  rb  rc  rd  )rP   rK   rK   rL   rF  	  s    zRBTorsionType.__hash__c             C   s   | j tj S )N)r_  rE   r   )rP   rK   rK   rL   uc0	  s    zRBTorsionType.uc0c             C   s   | j tj S )N)ra  rE   r   )rP   rK   rK   rL   uc1	  s    zRBTorsionType.uc1c             C   s   | j tj S )N)ra  rE   r   )rP   rK   rK   rL   uc2	  s    zRBTorsionType.uc2c             C   s   | j tj S )N)rb  rE   r   )rP   rK   rK   rL   uc3	  s    zRBTorsionType.uc3c             C   s   | j tj S )N)rc  rE   r   )rP   rK   rK   rL   uc4
  s    zRBTorsionType.uc4c             C   s   | j tj S )N)rd  rE   r   )rP   rK   rK   rL   uc5
  s    zRBTorsionType.uc5)r^  r^  N)rg   rh   ri   rj   rq   rT   rA  r   r   r]   r\   rF  rk   re  rf  rg  rh  ri  rj  rK   rK   rK   rL   r?   	  s   8
	c               @   sj   e Zd ZdZdd Zedd Zedd Zdd	d
Z	e
dd Zdd Zdd ZeddgZdd ZdS )r$   a  
    Dihedral types are a Fourier expansion of terms. In some cases, they are
    stored in a list like this one inside another TrackedList. In other cases,
    each term is a separate entry in the :class:`TrackedList`.

    In cases where `DihedralType`s are stored with every term in the same
    container, this object supports list assignment and indexing like
    :class:`DihedralType`.

    Parameters
    ----------
    *args : objects
        Any arguments that ``list`` would take.
    list : TrackedList, optional
        A list that "contains" this DihedralTypeList instance. This is a
        keyword-only argument. Default is ``None`` (i.e., belonging to no list)
    **kwargs : keyword argument list
        All other keyword arguments passed directly to the ``list`` constructor
    c             O   s>   d|kr| d| _nd | _tj| f|| d| _d| _d S )NrU   r_   F)r{   rU   rq   rb   rx   )rP   r   r   rK   rK   rL   rq   
  s    zDihedralTypeList.__init__c          
   C   s:  |j }|j}|j}|j}|j}|j}dtj }d| d|  d|  | d|  d }	d| d|  d|  d }
|| d }d	| d|  d
 }|d }| d
 }|  }xHt|	|
||||fD ]0\}}t	|t
kr|t||||j|jd qW t|dkr6t	|	t
k r.|tdd||j|jd ntd|S )aa  
        Creates a Fourier series of proper torsions from a Ryckaerts-Bellemans
        torsion.

        Parameters
        ----------
        rbtorsion : RBTorsionType
            The R-B torsion type to convert to a series of proper torsions

        Raises
        ------
        ValueError if all terms in rbtorsion are zero except for c0

        r   r  g       @i      r  rD      )rY  rZ  g        z(Unable to convert RB torsion to propers.)r_  r`  ra  rb  rc  rd  rE   rG   ra   r#  r   r   r#   rY  rZ  r	  r  )r   Z	rbtorsionr_  r`  ra  rb  rc  rd  rT  Zfc0Zfc1Zfc2Zfc3Zfc4Zfc5instrd   frK   rK   rL   from_rbtorsion(
  s,    
(
 zDihedralTypeList.from_rbtorsionc             C   s<   t | t |krdS x"t| |D ]\}}||ks dS q W dS )NFT)r	  r   )rP   rQ   Zt1Zt2rK   rK   rL   rA  S
  s     zDihedralTypeList.__eq__Fc             C   sr   t |tstdxNt| D ]B\}}|j|jkr||kr<dS |rVtdt || |< qtdqW t	
| | dS )a   Adds a DihedralType to the DihedralTypeList

        Parameters
        ----------
        other : :class:`DihedralType`
            The DihedralType instance to add to this list. It cannot have the
            same periodicity as any other DihedralType in the list
        override : bool, optional, default=False
            If True, this will override an existing torsion, but will raise a
            warning if it does so

        Raises
        ------
        TypeError if other is not an instance of :class:`DihedralTypeList`

        ParameterError if other has the same periodicity as another member in
        this list and override is False

        ParameterWarning if other has same periodicit as another member in this
        list and override is True
        z-Can only add DihedralType to DihedralTypeListNzAOverriding DihedralType in DihedralTypeList with same periodicityz\Cannot add two DihedralType instances with the same periodicity to the same DihedralTypeList)r   r#   r  ra   rQ  warningswarnr   r   rU   r   )rP   rQ   overriderd   existingrK   rK   rL   r   [
  s    
 
zDihedralTypeList.appendc             C   s<   d }x2| D ]*}|j d k	r
|d kr(|j }q
t|j |}q
W |S )N)ry   max)rP   ry   ZdtrK   rK   rL   ry   
  s    

zDihedralTypeList.penaltyc             C   s   dt |  S )Nz<DihedralTypes %s>)rU   r   )rP   rK   rK   rL   r   
  s    zDihedralTypeList.__repr__c             C   s   t dd | D S )Nc             S   s   g | ]}t |qS rK   )r   )rV   r  rK   rK   rL   r  
  s    z-DihedralTypeList.__copy__.<locals>.<listcomp>)r$   )rP   rK   rK   rL   r   
  s    zDihedralTypeList.__copy__rU   ry   c             C   s   t t| S )N)rC  tuple)rP   rK   rK   rL   rF  
  s    zDihedralTypeList.__hash__N)F)rg   rh   ri   rj   rq   r   rq  rT   rA  r   rk   ry   r   r   r]   r\   rF  rK   rK   rK   rL   r$   
  s   	+
%c               @   s2   e Zd ZdZdddZdd Zdd Zd	d
 ZdS )r2   a+  
    A Urey-Bradley angle type with a set of parameters. It has the same
    functional form as a bond, but it is defined between two atoms forming a
    valence angle separated by two bonds.

    Parameters
    ----------
    atom1 : :class:`Atom`
        The first atom involved in the Urey-Bradley bond
    atom2 : :class:`Atom`
        The other atom involved in the Urey-Bradley bond
    type : :class:`BondType`
        The Urey-Bradley bond type that defines the parameters for this bond

    Notes
    -----
    You can test whether an :class:`Atom` is contained within the bond using the
    `in` operator. A :class:`MoleculeError` is raised if `atom1` and `atom2` are
    identical.  You can also test that a :class:`Bond` is contained in this
    Urey-Bradley valence angle

    Examples
    --------
    >>> a1, a2, a3 = Atom(), Atom(), Atom()
    >>> b1 = Bond(a1, a2)
    >>> b2 = Bond(a2, a3)
    >>> angle = Angle(a1, a2, a3)
    >>> urey = UreyBradley(a1, a3)
    >>> a1 in urey and a3 in urey
    True
    >>> b1 in urey and b2 in urey
    True
    Nc             C   sF   ||krt d||f || _|| _|j|  |j|  || _dS )z Bond constructor z+Cannot bond atom to itself! Atoms are %s %sN)r   rm   rn   r   r   rt   )rP   rm   rn   rt   rK   rK   rL   rq   
  s    zUreyBradley.__init__c             C   sz   t |tr|| jkp|| jkS |j| krD|j| kr6dS |j}|j}n|j}|j}x$|jD ]}||krfqX|| krXdS qXW dS )zF Quick and easy way to see if an Atom or Bond is in this Urey-Bradley FT)r   r   rm   rn   r   )rP   r8  Zend1Zcentr   rK   rK   rL   rs   
  s    


 zUreyBradley.__contains__c             C   s2   t | jj|  t | jj|  d | _ | _| _dS )z
        Deletes this Urey-Bradley from the atoms that make it up. This method
        removes this urey-bradley from the `urey_bradleys` list for atom1 and
        atom2.
        N)r}   rm   r   rn   rt   )rP   rK   rK   rL   rv   
  s    zUreyBradley.deletec             C   s   dt | j| j| j| j f S )Nz<%s %r--%r; type=%r>)rt   rg   rm   rn   )rP   rK   rK   rL   r   
  s    zUreyBradley.__repr__)N)rg   rh   ri   rj   rq   rs   rv   r   rK   rK   rK   rL   r2   
  s
   !
!c               @   sZ   e Zd ZdZdddZdd Zdd Zd	d
 Zdd Zdd Z	dd Z
dd Zdd ZdS )r%   a  
    A CHARMM-style improper torsion between 4 atoms. The first atom must be the
    central atom, as shown in the schematic below

                      A3
                      |
                      |
             A4 ----- A1 ----- A2

    Parameters
    ----------
    atom1 : :class:`Atom`
        The central atom A1 in the schematic above
    atom2 : :class:`Atom`
        An atom in the improper, A2 in the schematic above
    atom3 : :class:`Atom`
        An atom in the improper, A3 in the schematic above
    atom4 : :class:`Atom`
        An atom in the improper, A4 in the schematic above
    type : :class:`ImproperType`
        The ImproperType object containing the parameters for this improper
        torsion

    Notes
    -----
    An Improper torsion can contain bonds or atoms. A bond is contained if it
    exists between atom 1 and any other atom. Raises :class:`MoleculeError` if any
    of the atoms are duplicates.

    Examples
    --------
    >>> a1, a2, a3, a4 = Atom(), Atom(), Atom(), Atom()
    >>> imp = Improper(a1, a2, a3, a4)
    >>> Bond(a1, a2) in imp and Bond(a1, a3) in imp and Bond(a1, a4) in imp
    True
    >>> Bond(a2, a3) in imp
    False
    Nc             C   sR   t | |||| |j|  |j|  |j|  |j|  || _d| _d S )NrD   )rl   rq   r   r   rt   r5  )rP   rm   rn   ro   rp   rt   rK   rK   rL   rq   !  s    zImproper.__init__c             C   sR   t |trt| |S | j|kr*| j|kpP| j|kr>| j|kpP| j|koP| j|kS )zW
        Quick and easy way to find out if an Atom or Bond is in this Improper
        )r   r   rl   rs   rm   rn   ro   rp   )rP   r8  rK   rK   rL   rs   ,  s
    
zImproper.__contains__c             C   s   t |trJ| j|jk	rdS t| j| j| jg}t|j|j|jg}||kS t|}t|dkrnt	dt| | jj
|d krdS t| jj
| jj
| jj
g}t|d |d |d g}||kS )z|
        An improper has the same 4 atoms if atom1 is the same for both and the
        other 3 can be in any order
        Fr  zImpropers have 4 atoms, not %sr   r   rD   r  )r   r%   rm   r   rn   ro   rp   rU   r	  r   rf   )rP   r8  ZselfsetZothersetrK   rK   rL   rO  7  s    
zImproper.same_atomsc             C   sL   d| j | j| j| jfkrdS yt| j | j| j| jS  tk
rF   dS X dS )a   Measures the current torsional angle

        Returns
        -------
        measurement : float or None
            If the atoms have coordinates, returns the torsional angle between
            the four atoms. If any coordinates are missing, returns None
        N)rm   rn   ro   rp   r   rN   )rP   rK   rK   rL   r:  Q  s    	zImproper.measurec             C   s   |   }|dk	r|tj S dS )z# Same as measure(), but with units N)r:  rE   rG   )rP   r;  rK   rK   rL   r<  a  s    zImproper.umeasurec             C   s>   |   }|dks| jdkrdS | jj| t }| jj| | S )a!   Measures the current dihedral angle energy

        Returns
        -------
        energy : float or None
            Dihedral angle energy in kcal/mol. Return value is None if either
            the coordinates of either atom is not set or the angle type is not
            set
        N)r:  rt   psi_eqr
   psi_k)rP   rT  ZdphirK   rK   rL   r>  f  s
    
zImproper.energyc             C   s   |   }|dk	r|tj S dS )z" Same as energy(), but with units N)r>  rE   r   )rP   r?  rK   rK   rL   r@  v  s    zImproper.uenergyc             C   sZ   t | jj|  t | jj|  t | jj|  t | jj|  d | _ | _ | _ | _| _dS )z
        Deletes this Improper from the atoms that make it up. This method
        removes this Improper from the `impropers` list for atom1, atom2, atom3,
        and atom4
        N)r}   rm   r   rn   ro   rp   rt   )rP   rK   rK   rL   rv   {  s
    zImproper.deletec             C   s$   dt | j| j| j| j| j| j f S )Nz<%s; %r--(%r,%r,%r); type=%r>)rt   rg   rm   rn   ro   rp   )rP   rK   rK   rL   r     s    
zImproper.__repr__)N)rg   rh   ri   rj   rq   rs   rO  r:  r<  r>  r@  rv   r   rK   rK   rK   rL   r%   
  s   &
c               @   s\   e Zd ZdZdddZedd Zdd Zd	d
 Ze	 Z
dd Zedd Zedd ZdS )r&   a?  
    An improper type with a set of improper torsion parameters

    Parameters
    ----------
    psi_k : ``float``
        Force constant in kcal/mol/radians^2
    psi_eq : ``float``
        Equilibrium torsion angle in Degrees
    list : :class:`TrackedList`
        A list of `ImproperType`s in which this is a member

    Inherited Attributes
    --------------------
    idx : ``int``
        The index of this ImproperType inside its containing list

    Notes
    -----
    Two `ImproperType`s are equal if their `psi_k` and `psi_eq` attributes are
    equal

    Examples
    --------
    >>> it1 = ImproperType(10.0, 180.0)
    >>> it2 = ImproperType(10.0, 180.0)
    >>> it1 is it2
    False
    >>> it1 == it2
    True
    >>> it1.idx # Not part of any list or iterable
    -1

    As part of a list, they can be indexed

    >>> improper_list = []
    >>> improper_list.append(ImproperType(10.0, 180.0, list=improper_list))
    >>> improper_list.append(ImproperType(10.0, 180.0, list=improper_list))
    >>> improper_list[0].idx
    0
    >>> improper_list[1].idx
    1
    Nc             C   s@   t |  t|tjtjd  | _t|tj| _|| _	d| _
d S )NrD   r_   )rw   rq   rM   rE   r   rI  ry  rG   rx  rU   rb   )rP   ry  rx  rU   rK   rK   rL   rq     s
    
zImproperType.__init__c             C   s(   t | j|j tk o&t | j|j tk S )N)r#  ry  r   rx  )rP   rQ   rK   rK   rL   rA    s    zImproperType.__eq__c             C   s   dt | j| j| jf S )Nz<%s; psi_k=%.3f, psi_eq=%.3f>)rt   rg   ry  rx  )rP   rK   rK   rL   r     s    zImproperType.__repr__c             C   s   t | j| jS )N)r&   ry  rx  )rP   rK   rK   rL   r     s    zImproperType.__copy__c             C   s   t t| jtt| jtfS )N)rC  rD  ry  rE  rx  )rP   rK   rK   rL   rF    s    zImproperType.__hash__c             C   s   | j tj tjd  S )NrD   )ry  rE   r   rI  )rP   rK   rK   rL   upsi_k  s    zImproperType.upsi_kc             C   s   | j tj S )N)rx  rE   rG   )rP   rK   rK   rL   upsi_eq  s    zImproperType.upsi_eq)N)rg   rh   ri   rj   rq   rT   rA  r   r   r]   r\   rF  rk   rz  r{  rK   rK   rK   rL   r&     s   +
c               @   sH   e Zd ZdZdddZedddZdd Zd	d
 Zdd Z	dd Z
dS )r    a  
    A coupled-torsion correction map term defined between 5 atoms connected by
    four covalent bonds. This is a coupled-torsion potential in which the
    torsions are consecutive.

    Parameters
    ----------
    atom1 : :class:`Atom`
        An atom on one end of the valence coupled-torsion bonded to atom2
    atom2 : :class:`Atom`
        An atom in the middle of the CMAP bonded to atoms 1 and 3
    atom3 : :class:`Atom`
        An atom in the middle of the CMAP bonded to atoms 2 and 4
    atom4 : :class:`Atom`
        An atom in the middle of the CMAP bonded to atoms 3 and 5
    atom5 : :class:`Atom`
        An atom in the middle of the CMAP bonded to atom 4
    type : :class:`CmapType`
        The CmapType object containing the parameter map for this term

    Notes
    -----
    A CMAP can contain bonds or atoms. A bond is contained if it exists between
    atoms 1 and 2, between atoms 2 and 3, between atoms 3 and 4, or between
    atoms 4 and 5.

    Examples
    --------
    >>> a1, a2, a3, a4, a5 = Atom(), Atom(), Atom(), Atom(), Atom()
    >>> cmap = Cmap(a1, a2, a3, a4, a5)
    >>> Bond(a1, a2) in cmap and Bond(a2, a3) in cmap
    True
    >>> Bond(a1, a3) in cmap
    False
    Nc       
      C   s   |||||g}xXt t|D ]H}xBt |d t|D ],}	|| ||	 kr4td|| ||	 f q4W qW || _|| _|| _|| _|| _|j	|  |j	|  |j	|  |j	|  |j	|  || _
d| _d S )Nr   z+Cannot cmap atom to itself! Atoms are %s %s)r   r	  r   rm   rn   ro   rp   atom5r   r   rt   r5  )
rP   rm   rn   ro   rp   r|  rt   Zatmlistrd   jrK   rK   rL   rq     s&    zCmap.__init__c
       
      C   s4   ||k	s||k	s||k	r t d| ||||||	dS )a  
        Alternative constructor for correction maps defined with 8 atoms (each
        torsion being separately specified). Correction maps are, to the best of
        my knowledge, used to parametrized "consecutive" torsions (i.e., atoms
        2, 3, and 4 are common to both torsions). However, the CHARMM definition
        specifies the torsions separately.

        If the torsions are _not_ consecutive (i.e., if atoms 2, 3, and 4 are
        not the same as atoms 5, 6, and 7), NotImplementedError is raised.

        Parameters
        ----------
        atom1 : :class:`Atom`
            The first atom of the first torsion
        atom2 : :class:`Atom`
            The second atom of the first torsion
        atom3 : :class:`Atom`
            The third atom of the first torsion
        atom4 : :class:`Atom`
            The fourth atom of the first torsion
        atom5 : :class:`Atom`
            The first atom of the second torsion
        atom6 : :class:`Atom`
            The second atom of the second torsion
        atom7 : :class:`Atom`
            The third atom of the second torsion
        atom8 : :class:`Atom`
            The fourth atom of the second torsion
        type : :class:`CmapType`
            The CmapType object containing the parameter map for this term
        zBOnly consecutive coupled-torsions are supported by CMAPs currently)rt   )rS  )
r   rm   rn   ro   rp   r|  atom6Zatom7Zatom8rt   rK   rK   rL   extended  s    !zCmap.extendedc             C   s   t |tr<|| jkp:|| jkp:|| jkp:|| jkp:|| jkS | j|krP| j|kp| j|krd| j|kp| j|krx| j|kp| j|ko| j|kS )zc
        Quick and easy way to find out an atom or bond is in this
        coupled-torsion
        )r   r   rm   rn   ro   rp   r|  )rP   r8  rK   rK   rL   rs   >  s    

zCmap.__contains__c             C   sP  t |tr| j|jkrF| j|jkrF| j|jkrF| j|jkrF| j|jkp| j|jko| j|jko| j|jko| j|jko| j|jkS t|}t|dkrt	dt| | jj
|d kr| jj
|d kr| jj
|d kr| jj
|d kr| jj
|d kpN| jj
|d koN| jj
|d koN| jj
|d koN| jj
|d koN| jj
|d kS )z
        A coupled-torsion is equivalent if the 5 atoms are in the same or
        reverse order Allow comparison with another type of cmap or with a
        sequence of 5 indexes
        rl  z&CMAP can compare to 5 elements, not %dr   r   rD   r  r  )r   r    rm   rn   ro   rp   r|  rU   r	  r   rf   )rP   r8  rK   rK   rL   rO  L  s     
  $$zCmap.same_atomsc             C   sn   t | jj|  t | jj|  t | jj|  t | jj|  t | jj|  d | _ | _ | _ | _ | _| _dS )z
        Deletes this Cmap from the atoms that make it up. This method removes
        the Cmap from the `cmaps` list for atom1, atom2, atom3, atom4, and atom5
        N)r}   rm   r   rn   ro   rp   r|  rt   )rP   rK   rK   rL   rv   g  s    zCmap.deletec             C   s(   dt | j| j| j| j| j| j| j f S )Nz!<%s; %r--%r--%r--%r--%r; type=%r>)rt   rg   rm   rn   ro   rp   r|  )rP   rK   rK   rL   r   t  s    
zCmap.__repr__)N)N)rg   rh   ri   rj   rq   r   r  rs   rO  rv   r   rK   rK   rK   rL   r      s   #
%c               @   sD   e Zd ZdZdddZedd Zdd Zd	d
 Ze	 Z
dd ZdS )r!   aL  
    A CMAP type with a potential energy interpoloation grid mapping out the 2-D
    potential of coupled torsions.

    Parameters
    ----------
    resolution : ``int``
        The number of grid points in the correction map potential in both
        torsion dimensions
    grid : ``iterable of floats``
        This must be a 1-dimensional list of grid values. The dimension must be
        `resolution*resolution`, and must be row-major (i.e., the second
        dimension changes the fastest) when indexed with 2 indices.
    list : :class:`TrackedList`
        A list of `CmapType`s in which this is a member

    Attributes
    ----------
    resolution : ``int``
        Potential grid resolution (see description in Parameters)
    grid : :class:`_CmapGrid`
        A _CmapGrid object defining the interpolating potential energy grid,
        with each point having the units kcal/mol
    comments : ``list(str)``
        List of strings that represent comments about this parameter type
    list : :class:`TrackedList`
        If not None, this is a list in which this instance _may_ be a member
    idx : ``int``
        The index of this CmapType inside its containing list

    Notes
    -----
    Two `CmapType`s are equal if their resolution is the same and each grid
    point is the same to within 1E-8

    See the docs for `_CmapGrid` for information on how to access the
    interpolating grid data if necessary.

    Raises
    ------
    TypeError is raised if the grid does not have the correct number of elements
    for the given resolution

    Examples
    --------
    >>> ct = CmapType(2, [0, 1, 2, 3])
    >>> ct.grid[0,0], ct.grid[0]
    (0, 0)
    >>> ct.grid[0,1], ct.grid[1]
    (1, 1)
    >>> ct.grid[1,0], ct.grid[2]
    (2, 2)
    >>> ct.grid[1,1], ct.grid[3]
    (3, 3)
    >>> ct.idx # not part of a list or iterable
    -1
    Nc             C   s^   t |  || _t||| _t|| j| j kr8td|d krHg | _n|| _d| _|| _	d S )Nz,CMAP grid does not match expected resolutionr_   )
rw   rq   
resolution	_CmapGridgridr	  r  commentsrb   rU   )rP   r  r  r  rU   rK   rK   rL   rq     s    
zCmapType.__init__c             C   s(   | j |j ko&tdd t| j|jD S )Nc             s   s"   | ]\}}t || tk V  qd S )N)r#  r   )rV   rd   r}  rK   rK   rL   	<genexpr>  s    z"CmapType.__eq__.<locals>.<genexpr>)r  allr   r  )rP   rQ   rK   rK   rL   rA    s    zCmapType.__eq__c             C   s   dt | j| jf S )Nz<%s; resolution=%d>)rt   rg   r  )rP   rK   rK   rL   r     s    zCmapType.__repr__c             C   s    t | jt| jj| jd d  S )N)r!   r  r   r  _datar  )rP   rK   rK   rL   r     s    zCmapType.__copy__c             C   s   t | j| jfS )N)rC  r  r  )rP   rK   rK   rL   rF    s    zCmapType.__hash__)NN)rg   rh   ri   rj   rq   rT   rA  r   r   r]   r\   rF  rK   rK   rK   rL   r!   z  s   9
c               @   s~   e Zd ZdZdddZedd ZeZdd Zd	d
 Z	dd Z
dd Zdd Zdd Zedd Zdd Zdd Zdd ZdS )r  a  
    A grid object for storing Correction map data. Data can be accessed in one
    of two ways; either with 1 or 2 indexes. If 2 indexes [i,j] are given, the
    index into the flattened array is i*resolution+j. Indexing starts from 0.

    The _CmapGrid usually has ranges for the two angles from -180 to 180. Some
    places will expect the range to be 0-360 degrees (e.g., OpenMM). The
    switch_range method returns a _CmapGrid with the "other" range than the
    current object.  i.e., if the current range is from 0 to 360 degrees, the
    _CmapGrid returned by `switch_range` will be from -180 to 180, and vice
    versa.

    Example:
    >>> g = _CmapGrid(2, [0, 1, 2, 3])
    >>> print('%s %s' % (g[0], g[0,0]))
    0 0
    >>> print('%s %s' % (g[1], g[0,1]))
    1 1
    >>> print(g[1,0])
    2
    >>> g[1,1] = 10
    >>> print(g[3])
    10
    >>> print(g.switch_range())
    [10.0000, 2.0000
     1.0000, 0.0000]
    Nc             C   s>   || _ |d kr,dd t| j | j  D | _nt|tj| _d S )Nc             S   s   g | ]}d qS )r   rK   )rV   rd   rK   rK   rL   r    s    z&_CmapGrid.__init__.<locals>.<listcomp>)r  r   r  rM   rE   r   )rP   r  datarK   rK   rL   rq     s    z_CmapGrid.__init__c                sj   t  dr jS g }t j}x6t jD ](} fddt|| jD }||7 }q*W t j| _ jS )z% The transpose of the potential grid 
_transposec                s   g | ]} | qS rK   rK   )rV   r}  )rP   rK   rL   r     s    z'_CmapGrid.transpose.<locals>.<listcomp>)ru   r  r	  r  r   r  r  )rP   r  sizerd   ZpiecerK   )rP   rL   	transpose  s    

z_CmapGrid.transposec             C   sT   t |trJ|d | jks&|d | jkr.td| j| j|d  |d   S | j| S )Nr   r   z_CmapGrid: Index out of range)r   rw  r  r   r  )rP   rf   rK   rK   rL   __getitem__  s
    
z_CmapGrid.__getitem__c             C   s*  t |trT|d | jks&|d | jkr.tdt|tj| j| j|d  |d  < nyt|	t
| j }W n& tk
r   t|tj| j|< Y nX yt
|}W n tk
r   d}Y nX |dkrxb|D ]}t|tj| j|< qW nB|t
|krtdn,x*t||D ]\}}t|tj| j|< qW d S )Nr   r   z_CmapGrid: Index out of rangez&Wrong number of values setting a slice)r   rw  r  r   rM   rE   r   r  r   indicesr	  rN   r  r  r   )rP   rf   rX   r  Zlenvalr  r  rK   rK   rL   __setitem__  s&    
&


z_CmapGrid.__setitem__c             C   s
   t | jS )N)r	  r  )rP   rK   rK   rL   __len__&  s    z_CmapGrid.__len__c             C   s
   t | jS )N)iterr  )rP   rK   rK   rL   __iter__)  s    z_CmapGrid.__iter__c             C   s   d| j | j f S )Nz<_CmapGrid: %dx%d>)r  )rP   rK   rK   rL   r   ,  s    z_CmapGrid.__repr__c             C   s   d| j d  }d}xpt| D ]d\}}|dkr.q||| 7 }|d | j dkrh|t| j d krh|d7 }q|t| d kr|d7 }qW |d S )Nz[%.4f,r   z %.4fr   
,])r  ra   r  r	  )rP   r[  Zfmtrd   rX   rK   rK   rL   __str__/  s     $
z_CmapGrid.__str__c             C   s@   | j |j krdS x*t| |D ]\}}t|| tkrdS qW dS )NFT)r  r   r#  r   )rP   rQ   r  r  rK   rK   rL   rA  ;  s    z_CmapGrid.__eq__c             C   sj   | j }|d }t|}xNt|D ]B}|| | }x0t|D ]$}|| | }| ||f |||f< q:W q W |S )z
        Returns a grid object whose range is 0 to 360 degrees in both dimensions
        instead of -180 to 180 degrees (or -180 to 180 degrees if the range is
        already 0 to 360 degrees)
        rD   )r  r  r   )rP   resZmidZnewgridrd   Ziir}  ZjjrK   rK   rL   switch_rangeD  s    z_CmapGrid.switch_rangec             C   s   t | jt| jS )N)r  r  r   r  )rP   rK   rK   rL   r   U  s    z_CmapGrid.__copy__c             C   s   t tdd | D S )Nc             S   s   g | ]}t |tqS rK   )rD  rE  )rV   r  rK   rK   rL   r  Y  s    z&_CmapGrid.__hash__.<locals>.<listcomp>)rC  rw  )rP   rK   rK   rL   rF  X  s    z_CmapGrid.__hash__)N)rg   rh   ri   rj   rq   rk   r  Tr  r  r  r  r   r  rT   rA  r  r   rF  rK   rK   rK   rL   r    s   
	r  c               @   s*   e Zd ZdZd	ddZdd Zdd ZdS )
r0   a  
    A trigonal-angle term in the AMOEBA force field. It exists in a pattern like
    the one shown below

                                A1
                                |
                                |
                          A4----A2----A3

    Parameters
    ----------
    atom1 : :class:`Atom`
        The first atom involved in the trigonal angle
    atom2 : :class:`Atom`
        The central atom involved in the trigonal angle
    atom3 : :class:`Atom`
        The third atom involved in the trigonal angle
    atom4 : :class:`Atom`
        The fourth atom involved in the trigonal angle
    type : :class:`AngleType`
        The angle type containing the parameters

    Notes
    -----
    Either `Atom`s or `Bond`s can be contained within this trigonal angle
    Nc             C   s   t | |||| || _d S )N)rl   rq   rt   )rP   rm   rn   ro   rp   rt   rK   rK   rL   rq   x  s    zTrigonalAngle.__init__c             C   sR   t |trt| |S | j|kr*| j|kpP| j|kr>| j|kpP| j|koP| j|kS )N)r   r   rl   rs   rm   rn   ro   rp   )rP   r8  rK   rK   rL   rs   |  s
    
zTrigonalAngle.__contains__c             C   s$   dt | j| j| j| j| j| j f S )Nz<%s; %r--(%r,%r,%r); type=%r>)rt   rg   rn   rm   ro   rp   )rP   rK   rK   rL   r     s    
zTrigonalAngle.__repr__)N)rg   rh   ri   rj   rq   rs   r   rK   rK   rK   rL   r0   ]  s   
c               @   s*   e Zd ZdZd	ddZdd Zdd ZdS )
r(   a  
    Out-of-plane bending term in the AMOEBA force field. The bond pattern is the
    same as :class:`TrigonalAngle`

    Parameters
    ----------
    atom1 : :class:`Atom`
        The first atom involved in the trigonal angle
    atom2 : :class:`Atom`
        The central atom involved in the trigonal angle
    atom3 : :class:`Atom`
        The third atom involved in the trigonal angle
    atom4 : :class:`Atom`
        The fourth atom involved in the trigonal angle
    type : :class:`OutOfPlaneBendType`
        The angle type containing the parameters

    Notes
    -----
    Either `Atom`s or `Bond`s can be contained within this trigonal angle
    Nc             C   s   t | |||| || _d S )N)rl   rq   rt   )rP   rm   rn   ro   rp   rt   rK   rK   rL   rq     s    zOutOfPlaneBend.__init__c             C   sR   t |trt| |S | j|kr*| j|kpP| j|kr>| j|kpP| j|koP| j|kS )N)r   r   rl   rs   rm   rn   ro   rp   )rP   r8  rK   rK   rL   rs     s
    
zOutOfPlaneBend.__contains__c             C   s$   dt | j| j| j| j| j| j f S )Nz<%s; %r--(%r,%r,%r); type=%r>)rt   rg   rn   rm   ro   rp   )rP   rK   rK   rL   r     s    
zOutOfPlaneBend.__repr__)N)rg   rh   ri   rj   rq   rs   r   rK   rK   rK   rL   r(     s   
c               @   sP   e Zd ZdZdddZedd Zdd Zd	d
 Ze	 Z
dd Zedd ZdS )r3   a  
    An angle type with a set of angle parameters

    Parameters
    ----------
    k : ``float``
        Force constant in kcal/mol/radians^2
    list : :class:`TrackedList`
        A list of `OutOfPlaneBendType`s in which this is a member

    Inherited Attributes
    --------------------
    idx : ``int``
        The index of this OutOfPlaneBendType inside its containing list

    Notes
    -----
    Two `OutOfPlaneBendType`s are equal if their `k` attribute is equal

    Examples
    --------
    >>> ot1 = OutOfPlaneBendType(10.0)
    >>> ot2 = OutOfPlaneBendType(10.0)
    >>> ot1 is ot2
    False
    >>> ot1 == ot2
    True
    >>> ot1.idx # not part of any list or iterable
    -1

    As part of a list, they can be indexed

    >>> oopbend_list = []
    >>> oopbend_list.append(OutOfPlaneBendType(10.0, list=oopbend_list))
    >>> oopbend_list.append(OutOfPlaneBendType(10.0, list=oopbend_list))
    >>> oopbend_list[0].idx
    0
    >>> oopbend_list[1].idx
    1
    Nc             C   s2   t |  t|tjtjd  | _d| _|| _d S )NrD   r_   )	rw   rq   rM   rE   r   rI  r=  rb   rU   )rP   r=  rU   rK   rK   rL   rq     s    
zOutOfPlaneBendType.__init__c             C   s   t | j|j tk S )N)r#  r=  r   )rP   rQ   rK   rK   rL   rA    s    zOutOfPlaneBendType.__eq__c             C   s   dt | j| jf S )Nz<%s; k=%.3f>)rt   rg   r=  )rP   rK   rK   rL   r     s    zOutOfPlaneBendType.__repr__c             C   s
   t | jS )N)r3   r=  )rP   rK   rK   rL   r     s    zOutOfPlaneBendType.__copy__c             C   s   t t| jtS )N)rC  rD  r=  rE  )rP   rK   rK   rL   rF    s    zOutOfPlaneBendType.__hash__c             C   s   | j tj tjd  S )NrD   )r=  rE   r   rI  )rP   rK   rK   rL   rG    s    zOutOfPlaneBendType.uk)N)rg   rh   ri   rj   rq   rT   rA  r   r   r]   r\   rF  rk   rG  rK   rK   rK   rL   r3     s   (
c               @   s*   e Zd ZdZd	ddZdd Zdd ZdS )
r)   a|  
    Defines a pi-torsion term in the AMOEBA force field. The Pi-torsion is
    defined around a sp2-hybridized pi-delocalized orbital (like an amide) by 6
    atoms, as shown in the schematic below.


         A2           A5-AAA
           \         /
            \       /
             A3---A4
            /       \
           /         \
     AAA-A1           A6

    In the above schematic, A3 and A4 are sp2-hybridized, and atoms A2 and A6
    are bonded *only* to A3 and A4, respectively. Atoms A1 and A5 are each
    bonded to 3 other atoms.

    Parameters
    ----------
    atom1 : :class:`Atom`
        atom A1 in the schematic above
    atom2 : :class:`Atom`
        atom A2 in the schematic above
    atom3 : :class:`Atom`
        atom A3 in the schematic above
    atom4 : :class:`Atom`
        atom A4 in the schematic above
    atom5 : :class:`Atom`
        atom A5 in the schematic above
    atom6 : :class:`Atom`
        atom A6 in the schematic above
    type : :class:`DihedralType`
        The parameters for this Pi-torsion

    Notes
    -----
    Both :class:`Bond`s and :class:`Atom`s can be contained in a pi-torsion
    Nc             C   s.   || _ || _|| _|| _|| _|| _|| _d S )N)rm   rn   ro   rp   r|  r~  rt   )rP   rm   rn   ro   rp   r|  r~  rt   rK   rK   rL   rq     s    zPiTorsion.__init__c             C   s   t |trF|| jkpD|| jkpD|| jkpD|| jkpD|| jkpD|| jkS | j|krZ| j|kp| j|krn| j|kp| j|kr| j|kp| j|kr| j|kp| j|ko| j|kS )N)r   r   rm   rn   ro   rp   r|  r~  )rP   r8  rK   rK   rL   rs   %  s    
zPiTorsion.__contains__c          	   C   s,   dt | j| j| j| j| j| j| j| j f S )Nz'<%s; (%r,%r)--%r--%r--(%r,%r); type=%r>)rt   rg   rm   rn   ro   rp   r|  r~  )rP   rK   rK   rL   r   1  s    
zPiTorsion.__repr__)N)rg   rh   ri   rj   rq   rs   r   rK   rK   rK   rL   r)     s   '
	c               @   s*   e Zd ZdZd	ddZdd Zdd ZdS )
r,   a1  
    This term models the stretching and bending of a standard valence angle, and
    is used in the AMOEBA force field

    Parameters
    ----------
    atom1 : :class:`Atom`
        The first atom on one end of the angle
    atom2 : :class:`Atom`
        The central atom in the angle
    atom3 : :class:`Atom`
        The atom on the other end of the angle
    type : :class:`StretchBendType`
        The type containing the stretch-bend parameters

    Notes
    -----
    Both :class:`Bond`s and :class:`Atom`s can be contained in a stretch-bend term
    Nc             C   s   || _ || _|| _|| _d S )N)rm   rn   ro   rt   )rP   rm   rn   ro   rt   rK   rK   rL   rq   L  s    zStretchBend.__init__c             C   sP   t |tr(| j|kp&| j|kp&| j|kS | j|kr<| j|kpN| j|koN| j|kS )N)r   r   rm   rn   ro   )rP   r8  rK   rK   rL   rs   R  s
    

zStretchBend.__contains__c             C   s    dt | j| j| j| j| j f S )Nz<%s; %r--%r--%r; type=%r>)rt   rg   rm   rn   ro   )rP   rK   rK   rL   r   Y  s    
zStretchBend.__repr__)N)rg   rh   ri   rj   rq   rs   r   rK   rK   rK   rL   r,   8  s   
c               @   s   e Zd ZdZdddZedd Zdd Zd	d
 Ze	 Z
dd Zedd Zedd Zedd Zedd Zedd ZdS )r-   a  
    A stretch-bend type with two distances and an angle in AMOEBA

    Parameters
    ----------
    k1 : ``float``
        First force constant in kcal/mol/(radians*angstroms)
    k2 : ``float``
        Second force constant in kcal/mol/(radians*angstroms)
    req1 : ``float``
        Equilibrium bond distance for bond between the first and second atoms in
        Angstroms
    req2 : ``float``
        Equilibrium bond distance for bond between the second and third atoms in
        Angstroms
    theteq : ``float``
        Equilibrium angle in degrees
    list : :class:`TrackedList`
        A list of `StretchBendType`s in which this is a member

    Inherited Attributes
    --------------------
    idx : ``int``
        The index of this StretchBendType inside its containing list

    Notes
    -----
    Two `StretchBendType`s are equal if their `req1`, `req2`, `theteq`, and `k`
    attributes are equal

    Examples
    --------
    >>> sbt1 = StretchBendType(10.0, 10.0, 1.0, 1.0, 180.0)
    >>> sbt2 = StretchBendType(10.0, 10.0, 1.0, 1.0, 180.0)
    >>> sbt1 is sbt2
    False
    >>> sbt1 == sbt2
    True
    >>> sbt1.idx # Not part of any list or iterable
    -1

    As part of a list, they can be indexed

    >>> strbnd_list = []
    >>> strbnd_list.append(StretchBendType(10.0, 10.0, 1.0, 1.0, 180.0, strbnd_list))
    >>> strbnd_list.append(StretchBendType(10.0, 10.0, 1.0, 1.0, 180.0, strbnd_list))
    >>> strbnd_list[0].idx
    0
    >>> strbnd_list[1].idx
    1
    Nc             C   sx   t |  t|tjtjtj  | _t|tjtjtj  | _t|tj	| _
t|tj	| _t|tj| _d| _|| _d S )Nr_   )rw   rq   rM   rE   r   radianr   k1k2r   req1req2rG   r"  rb   rU   )rP   r  r  r  r  r"  rU   rK   rK   rL   rq     s    
zStretchBendType.__init__c             C   sd   t | j|j tk obt | j|j tk obt | j|j tk obt | j|j tk obt | j|j tk S )N)r#  r  r   r  r  r  r"  )rP   rQ   rK   rK   rL   rA    s
    zStretchBendType.__eq__c             C   s$   dt | j| j| j| j| j| jf S )Nz9<%s; req1=%.3f, req2=%.3f, theteq=%.3f, k1=%.3f, k2=%.3f>)rt   rg   r  r  r"  r  r  )rP   rK   rK   rL   r     s    zStretchBendType.__repr__c             C   s   t | j| j| j| j| jS )N)r-   r  r  r  r  r"  )rP   rK   rK   rL   r     s    zStretchBendType.__copy__c             C   s:   t t| jtt| jtt| jtt| jtt| jtfS )N)rC  rD  r  rE  r  r  r  r"  )rP   rK   rK   rL   rF    s    zStretchBendType.__hash__c             C   s   | j tj tjtj  S )N)r  rE   r   r  r   )rP   rK   rK   rL   uk1  s    zStretchBendType.uk1c             C   s   | j tj tjtj  S )N)r  rE   r   r  r   )rP   rK   rK   rL   uk2  s    zStretchBendType.uk2c             C   s   | j tj S )N)r  rE   r   )rP   rK   rK   rL   ureq1  s    zStretchBendType.ureq1c             C   s   | j tj S )N)r  rE   r   )rP   rK   rK   rL   ureq2  s    zStretchBendType.ureq2c             C   s   | j tj S )N)r"  rE   rG   )rP   rK   rK   rL   rJ    s    zStretchBendType.utheteq)N)rg   rh   ri   rj   rq   rT   rA  r   r   r]   r\   rF  rk   r  r  r  r  rJ  rK   rK   rK   rL   r-   _  s   3

c               @   s"   e Zd ZdZdddZdd ZdS )r.   a  
    This is a coupled-torsion map used in the AMOEBA force field similar to the
    correction-map (CMAP) potential used by the CHARMM force field

    Parameters
    ----------
    atom1 : :class:`Atom`
        An atom on one end of the valence torsion-torsion bonded to atom2
    atom2 : :class:`Atom`
        An atom in the middle of the torsion-torsion bonded to atoms 1 and 3
    atom3 : :class:`Atom`
        An atom in the middle of the torsion-torsion bonded to atoms 2 and 4
    atom4 : :class:`Atom`
        An atom in the middle of the torsion-torsion bonded to atoms 3 and 5
    atom5 : :class:`Atom`
        An atom in the middle of the torsion-torsion bonded to atom 4
    type : :class:`TorsionTorsionType`
        The TorsionTorsionType object containing the parameter map for this term

    Notes
    -----
    A TorsionTorsion can contain bonds or atoms. A bond is contained if it
    exists between atoms 1 and 2, between atoms 2 and 3, between atoms 3 and 4,
    or between atoms 4 and 5.

    Examples
    --------
    >>> a1, a2, a3, a4, a5 = Atom(), Atom(), Atom(), Atom(), Atom()
    >>> tortor = TorsionTorsion(a1, a2, a3, a4, a5)
    >>> Bond(a1, a2) in tortor and Bond(a2, a3) in tortor
    True
    >>> Bond(a1, a3) in tortor
    False
    Nc          	   C   s   t | |||||| |j|  |j|  |j|  |j|  |j|  || || || || || || || || || || d S )N)r    rq   r   r   r   )rP   rm   rn   ro   rp   r|  rt   rK   rK   rL   rq     s     








zTorsionTorsion.__init__c             C   s  t | jj|  t | jj|  t | jj|  t | jj|  t | jj|  t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j t | jj| j d | _ | _ | _ | _| _d| _dS )a  
        Deletes this TorsionTorsion from the atoms that make it up. This method
        removes the TorsionTorsion from the `tortors` list for atom1, atom2,
        atom3, atom4, and atom5, and removes each atom from the others'
        tortor_partners list.
        N)	r}   rm   r   rn   ro   rp   r|  r   rt   )rP   rK   rK   rL   rv      s6    zTorsionTorsion.delete)N)rg   rh   ri   rj   rq   rv   rK   rK   rK   rL   r.     s   "
c               @   s<   e Zd ZdZdd Zdd Zdd Zedd	 Zd
d Z	dS )_TorTorTablea  
    Contains an interpolating potential grid for a coupled-torsion in the AMOEBA
    force field.

    Parameters
    ----------
    ang1 : ``list of floats``
        Angles in the first dimension of the interpolation table
    ang2 : ``list of floats``
        Angles in the second dimension of the interpolation table
    data : ``list of floats``
        Value of the potential grid at each point (ang2 varies fastest)

    Notes
    -----
    Raises `TypeError` if the dimension of the data array does not match the
    number of data required by ang1 and ang2

    Elements from the table are obtained and/or set by using angles as indexes.
    If the pair of angles was not one of the original angles passed to this
    table, a KeyError is raised.

    Examples
    --------
    >>> table = _TorTorTable([1.0, 2.0], [1.0, 2.0, 3.0], [1, 2, 3, 4, 5, 6])
    >>> table[1.0,1.0]
    1
    >>> table[1.0,2.0]
    2
    >>> table[2.0,2.0]
    5
    >>> table[2.0,2.0] = 10
    >>> table.data
    [1, 2, 3, 4, 10, 6]
    c             C   s   t |t |t | krDtdt |t |t |t | t |f t|tj| _t | _d}x0|D ](}x"|D ]}|| j||f< |d7 }qnW qdW d S )NzPCoupled torsion parameter size mismatch. %dx%d grid expects %d elements (got %d)r   r   )r	  r  rM   rE   r   r  r   _indexes)rP   ang1ang2r  rd   r%  r&  rK   rK   rL   rq   K  s    "

z_TorTorTable.__init__c             C   s   | j | j|  S )N)r  r  )rP   rf   rK   rK   rL   r  X  s    z_TorTorTable.__getitem__c             C   s(   | j t|tj }t|tj| j|< d S )N)r  rM   rE   rG   r   r  )rP   rf   rJ   rK   rK   rL   r  [  s    z_TorTorTable.__setitem__c             C   sR   y4x.| j  D ] }t| | ||  tkrdS qW W n tk
rH   dS X dS d S )NFT)r  keysr#  r   KeyError)rP   rQ   rf   rK   rK   rL   rA  _  s    z_TorTorTable.__eq__c             C   s   |  | S )N)rA  )rP   rQ   rK   rK   rL   rz   j  s    z_TorTorTable.__ne__N)
rg   rh   ri   rj   rq   r  r  rT   rA  rz   rK   rK   rK   rL   r  '  s   #r  c               @   s<   e Zd ZdZdddZedd Zdd Zd	d
 Ze	 Z
dS )r/   a  
    The type containing the parameter maps for the Amoeba torsion-torsion
    potentials. It contains the original potential as well as interpolated first
    and second derivatives for the AMOEBA force field.

    Parameters
    ----------
    dims : ``tuple of 2 ints``
        The table dimensions
    ang1 : ``list of floats``
        The list of angles in the first dimension
    ang2 : ``list of floats``
        The list of angles in the second dimension
    f : ``list of floats``
        The interpolation table for the energy
    dfda1 : ``list of floats``
        The interpolation table of the gradient w.r.t. angle 1
    dfda2 : ``list of floats``
        The interpolation table of the gradient w.r.t. angle 2
    d2fda1da2 : ``list of floats``
        The interpolation table of the 2nd derivative w.r.t. both angles
    list : :class:`TrackedList`
        The list containing this coupled torsion-torsion map

    Attributes
    ----------
    dims : ``tuple of 2 ints``
        The table dimensions
    ang1 : ``list of floats``
        The list of angles in the first dimension
    ang2 : ``list of floats``
        The list of angles in the second dimension
    f : :class:`_TorTorTable`
        The interpolation table for the energy as a _TorTorTable
    dfda1 : :class:`_TorTorTable`
        The interpolation table for the first gradient as a _TorTorTable
    dfda2 : :class:`_TorTorTable`
        The interpolation table for the second gradient as a _TorTorTable
    d2fda1da2 : :class:`_TorTorTable`
        The interpolation table for the second derivative as a _TorTorTable
    list : :class:`TrackedList`
        The list that may, or may not, contain this TorsionTorsionType
    idx : ``int``
        The index of this item in the list or iterable defined by `list`
    Nc	       	      C   s   t |  t|dkrtdt||d ks>t||d krFtdt|| _t|tj| _	t|tj| _
t|||| _|d krd | _nt|||| _|d krd | _nt|||| _|d krd | _nt|||| _d| _|| _d S )NrD   z%dims must be a 2-dimensional iterabler   r   z%dims does match the angle definitionsr_   )rw   rq   r	  r  rw  dimsrM   rE   rG   r  r  r  rp  dfda1dfda2	d2fda1da2rb   rU   )	rP   r  r  r  rp  r  r  r  rU   rK   rK   rL   rq     s(    
 
zTorsionTorsionType.__init__c             C   s`   | j |j krdS | j|jkr dS | j|jkr0dS | j|jko^| j|jko^| j|jko^| j|jkS )NF)r  r  r  rp  r  r  r  )rP   rQ   rK   rK   rL   rA    s       zTorsionTorsionType.__eq__c             C   s    dt | j| jd | jd f S )Nz<%s; %dx%d>r   r   )rt   rg   r  )rP   rK   rK   rL   r     s    zTorsionTorsionType.__repr__c             C   sz   t | jj}| jd krd }nt | jj}| jd kr8d }nt | jj}| jd krTd }nt | jj}t| j| j| j	||||S )N)
r   rp  r  r  r  r  r/   r  r  r  )rP   rp  r  r  r  rK   rK   rL   r     s    


zTorsionTorsionType.__copy__)NNNN)rg   rh   ri   rj   rq   rT   rA  r   r   r]   r\   rK   rK   rK   rL   r/   o  s   -
c               @   s(   e Zd ZdZdd Zdd Zdd ZdS )	r   a  
    A chiral frame as defined in the AMOEBA force field. It defines the frame of
    reference for a chiral center

    Parameters
    ----------
    atom1 : :class:`Atom`
        The first atom defined in the chiral frame
    atom2 : :class:`Atom`
        The second atom defined in the chiral frame
    chirality : ``int``
        Either 1 or -1 to identify directionality. A ValueError is raised if a
        different value is provided

    Notes
    -----
    A chiral frame can only contain atoms.
    c             C   s.   || _ || _|dkr$|dkr$td|| _d S )Nr   r_   zchirality must be 1 or -1)rm   rn   r  	chirality)rP   rm   rn   r  rK   rK   rL   rq     s
    zChiralFrame.__init__c             C   s   || j kp|| jkS )N)rm   rn   )rP   r8  rK   rK   rL   rs     s    zChiralFrame.__contains__c             C   s   dt | j| j| j| jf S )Nz<%s; %r--%r, direction=%d>)rt   rg   rm   rn   r  )rP   rK   rK   rL   r     s    zChiralFrame.__repr__N)rg   rh   ri   rj   rq   rs   r   rK   rK   rK   rL   r     s   c               @   s    e Zd ZdZdd Zdd ZdS )r'   a  
    This defines the frame of reference for computing multipole interactions in
    the AMOEBA force field.

    Parameters
    ----------
    atom : :class:`Atom`
        The atom for which the frame of reference is defined
    frame_pt_num : ``int``
        The frame point number
    vectail : ``int``
        The vector tail index
    vechead : ``int``
        The vector head index
    nvec : ``int``
        The number of vectors

    Examples
    --------
    >>> atom = Atom()
    >>> mf = MultipoleFrame(atom, 0, 1, 2, 3)
    >>> atom in mf
    True
    >>> mf.frame_pt_num
    0
    >>> mf.vectail
    1
    >>> mf.vechead
    2
    >>> mf.nvec
    3
    c             C   s"   || _ || _|| _|| _|| _d S )N)atomframe_pt_numvectailvecheadnvec)rP   r  r  r  r  r  rK   rK   rL   rq     s
    zMultipoleFrame.__init__c             C   s
   | j |kS )N)r  )rP   r8  rK   rK   rL   rs   &  s    zMultipoleFrame.__contains__N)rg   rh   ri   rj   rq   rs   rK   rK   rK   rL   r'     s    c               @   s&   e Zd ZdZd
ddZedd Zd	S )rB   a6  
    An Atom that has a Drude particle attached to it.  This is a subclass of
    Atom, so it also has all the properties defined for regular Atoms.

    Parameters
    ----------
    alpha : ``float``
        the atomic polarizability
    thole : ``float``
        the Thole damping facior
    drude_type : ``str``
        the atom type to use for the Drude particle.

    Other Attributes
    ----------------
    anisotropy : :class:`DrudeAnisotropy`
        describes how this atom is anisotropically polarizable.  For isotropic
        atoms, this is None.
            ?DRUDc             K   s*   t j| f| || _|| _|| _d | _d S )N)r   rq   alphathole
drude_typeZ
anisotropy)rP   r  r  r  r   rK   rK   rL   rq   A  s
    zDrudeAtom.__init__c             C   s\   | j dk rdnd}t| j tjd  dtj tj  }|t|d dtj tjd    S )Nr   r_   r   r  gkA]a@rD   i  )	r  r#  rE   r   Zkilojoules_per_moleZ	nanometerr  r  r   )rP   Zsignr  rK   rK   rL   drude_chargeH  s    $zDrudeAtom.drude_chargeN)r  r  r  )rg   rh   ri   rj   rq   rk   r  rK   rK   rK   rL   rB   +  s   
c               @   s   e Zd ZdZdd ZdS )rC   a   
    A description of an anisotropically polarizable atom.

    Atom 1 is a :class:`DrudeAtom` whose polarizability is anisotropic.  The
    other three atoms define the coordinate frame.

    Parameters
    ----------
    atom1 : :class:`DrudeAtom`
        the polarizable atom
    atom2 : :class:`Atom`
        the second atom defining the coordinate frame
    atom3 : :class:`Atom`
        the third atom defining the coordinate frame
    atom4 : :class:`Atom`
        the fourth atom defining the coordinate frame
    a11 : ``float``
        the scale factor for the polarizability along the direction defined by
        atom1 and atom2
    a22 : ``float``
        the scale factor for the polarizability along the direction defined by
        atom3 and atom4
    c             C   s"   t | |||| || _|| _d S )N)rl   rq   a11a22)rP   rm   rn   ro   rp   r  r  rK   rK   rL   rq   i  s    zDrudeAnisotropy.__init__N)rg   rh   ri   rj   rq   rK   rK   rK   rL   rC   P  s   c               @   s   e Zd ZdZd#ddZdd Zd	d
 Zdd Zdd Zdd Z	dd Z
dd Zdd Zdd Zdd Zdd Zdd Zdd  Ze Zd!d" ZdS )$r*   a  
    A single residue that is composed of a small number of atoms

    Parameters
    ----------
    name : ``str``
        Name of the residue. Typical convention is to choose a name that is 4
        characters or shorter
    number : ``int``, optional
        Residue number assigned in the input structure. Default is -1
    chain : ``str``, optional
        The 1-letter chain identifier for this residue. Default is empty string
    insertion_code : ``str``, optional
        The insertion code (used in PDB files) for this residue. Default is
        empty string
    segid : ``str``, optional
        The segment identifier, used by CHARMM in a way similar to chain. Dfault
        is empty string
    list : :class:`TrackedList`
        List of residues in which this residue is a member

    Attributes
    ----------
    name : ``str``
        The name of this residue
    number : ``int``
        The number of this residue in the input structure
    idx : ``int``
        The index of this residue inside the container. If this residue has no
        container, or it is not present in the container, idx is -1
    chain : ``str``
        The 1-letter chain identifier for this residue
    insertion_code : ``str``
        The insertion code (used in PDB files) for this residue
    ter : ``bool``
        If True, there is a TER card directly after this residue (i.e., a
        molecule or chain ends). By default, it is False
    list : :class:`TrackedList`
        The container that _may_ have this residue contained inside
    atoms : ``list of`` :`class`Atom` ``instances``
        This is the list of `Atom`s that make up this residue

    Notes
    -----
    - Iterating over a residue will iterate over the atoms. It is exactly
      equivalent to iterating over the `atoms` attribute
    - Supports testing if an Atom instance is contained `in` this residue
    - `len()` returns the number of atoms in this residue
    r_   r   Nc             C   sF   |  | _|| _|  | _|  | _|| _d| _g | _d| _|| _	d S )Nr_   F)
r   r   r   chaininsertion_coderU   rb   atomsZtersegid)rP   r   r   r  r  r  rU   rK   rK   rL   rq     s    


zResidue.__init__c             C   s   | |_ | j| dS )z Adds an atom to this residue

        Parameters
        ----------
        atom : :class:`Atom`
            The atom to add to this residue

        Notes
        -----
        This action assigns the `residue` attribute to `atom`
        N)r   r  r   )rP   r  rK   rK   rL   add_atom  s    zResidue.add_atomc                s:   x4| j D ]*} j| krd _ fdd| j D | _ qW dS )z
        If an atom is present in this residue, delete it from the list of
        atoms. No change if an atom is not present in this residue.
        Nc                s   g | ]}| k	r|qS rK   rK   )rV   r3  )r  rK   rL   r    s    z'Residue.delete_atom.<locals>.<listcomp>)r  r   )rP   r  r3  rK   )r  rL   delete_atom  s    
zResidue.delete_atomc             C   s
   || j kS )z, True if an atom is present in this residue )r  )rP   r8  rK   rK   rL   rs     s    zResidue.__contains__c             C   s
   t | jS )N)r	  r  )rP   rK   rK   rL   r    s    zResidue.__len__c             C   s
   t | jS )N)r  r  )rP   rK   rK   rL   r    s    zResidue.__iter__c             C   s   t | dkS )z
        Determines if there are any atoms in this residue

        Returns
        -------
        empty: ``bool``
            ``True`` if there are no atoms left. ``False`` otherwise.
        r   )r	  )rP   rK   rK   rL   is_empty  s    	zResidue.is_emptyc             C   s   | j   dS )z, Sorts the atoms in this list by atom index N)r  sort)rP   rK   rK   rL   r    s    zResidue.sortc             C   s   | j |S )N)r  r  )rP   rf   rK   rK   rL   r    s    zResidue.__getitem__c             C   s   | j d j|j d jk S )Nr   )r  rf   )rP   rQ   rK   rK   rL   r     s    zResidue.__lt__c             C   s   | j d j|j d jkS )Nr   )r  rf   )rP   rQ   rK   rK   rL   r     s    zResidue.__gt__c             C   s   | j d j|j d jk S )Nr   )r  rf   )rP   rQ   rK   rK   rL   r     s    zResidue.__le__c             C   s   | j d j|j d jk  S )Nr   )r  rf   )rP   rQ   rK   rK   rL   r     s    zResidue.__ge__c             C   sr   | j dkr| j}n| j }dt| j| j|f }| jrB|d| j 7 }| jrV|d| j 7 }| jrj|d| j 7 }|d S )Nr_   z
<%s %s[%d]z
; chain=%sz; insertion_code=%sz
; segid=%sr   )r   rf   rt   rg   r   r  r  r  )rP   ZnumZreprK   rK   rL   r     s    
zResidue.__repr__c             C   s$   | j | x| D ]
}| |_qW d S )N)r[   r   r   )rP   r   r3  rK   rK   rL   r     s    
 zResidue.__setstate__)r_   r   r   r   N)rg   rh   ri   rj   rq   r  r  rs   r  r  r  r  r  r   r   r   r   r   r]   r\   r   rK   rK   rK   rL   r*   p  s$   1 
c                s    fdd}|S )z, Decorator to indicate the list has changed c                s   d| _ d| _ | f||S )NT)changedr`   )rP   r   r   )rR   rK   rL   new_func  s    z_changes.<locals>.new_funcrK   )rR   r  rK   )rR   rL   _changes  s    r  c               @   s   e Zd ZdZdd Zdd Zedd Zedd	 ZedddZ	edd Z
eejZeejZeejZeejZeejZeejZdd Zdd Zdd Zdd Zdd ZdS )r1   aM  
    This creates a list type that allows you to see if anything has changed

    Attributes
    ----------
    changed : ``bool``
        Determines if something has been done to fundamentally change the
        underlying topology defined by this list such that the topology needs to
        be rebuilt
    needs_indexing : ``bool``
        A flag to determine whether or not the items in a tracked list need to
        be indexed or not.

    Examples
    --------
    >>> tl = TrackedList()
    >>> tl.append(Atom())
    >>> tl.append(Atom())
    >>> tl.append(Atom())
    >>> tl.needs_indexing, tl.changed
    (True, True)
    >>> tl.index_members()
    >>> tl.needs_indexing, tl.changed
    (False, True)
    >>> tl.changed = False # Must do when changes have been incorporated
    >>> tl.needs_indexing, tl.changed
    (False, False)
    c             G   s   d| _ d| _tj| f| S )NF)r  r`   rU   rq   )rP   r   rK   rK   rL   rq   *  s    zTrackedList.__init__c                s   dt  j g}t dkrb| fddtdD  |d | fddtdd	D  n|d
d  D  |d d|S )Nz%s([
   c             3   s   | ]}d  |  V  qdS )z	%r
NrK   )rV   rd   )rP   rK   rL   r  2  s    z'TrackedList.__repr__.<locals>.<genexpr>   z	...
c             3   s   | ]}d  |  V  qdS )z	%r
NrK   )rV   rd   )rP   rK   rL   r  4  s    r   c             s   s   | ]}d | V  qdS )z	%r
NrK   )rV   rd   rK   rK   rL   r  6  s    z])r   )rt   rg   r	  extendr   r   r   )rP   r[  rK   )rP   rL   r   /  s    
 
zTrackedList.__repr__c          	   C   s   yt |t|  }W n tk
r0   |g}Y nX xT|D ]L}yd| | _W n tk
r^   Y nX yd| | _W q8 tk
r   Y q8X q8W t| |S )z/ Deletes items and slices. Make sure all items r_   N)r   r  r	  rN   rb   rU   __delitem__)rP   re   r  r|   rK   rK   rL   r  :  s    

zTrackedList.__delitem__c             C   s   |  t|| dS )z% Python 2 still uses __delslice__... N)r  slice)rP   r   stoprK   rK   rL   __delslice__P  s    zTrackedList.__delslice__r_   c             C   s    t | |}t|drd|_|S )Nrb   r_   )rU   r{   ru   rb   )rP   rf   re   rK   rK   rL   r{   U  s    
zTrackedList.popc             C   s    t | | t|drd|_d S )Nrb   r_   )rU   removeru   rb   )rP   r8  rK   rK   rL   r  \  s    
zTrackedList.removec             C   s   t t| |S )N)r1   rU   __add__)rP   rQ   rK   rK   rL   r  l  s    zTrackedList.__add__c             C   s   t t| |S )N)r1   rU   __mul__)rP   ZfacrK   rK   rL   r  o  s    zTrackedList.__mul__c          	   C   s@   x4t | D ](\}}y
||_W q
 tk
r0   Y q
X q
W d| _dS )z
        Assigns the idx variable for every member of this list to its place in
        the list, if the members of this list permit
        FN)ra   rb   rN   r`   )rP   rd   re   rK   rK   rL   rc   r  s    

zTrackedList.index_membersc          	   C   sB   x4t | D ](\}}y
| |_W q
 tk
r0   Y q
X q
W |   dS )z
        This method causes this list to "claim" all of the items it contains and
        subsequently indexes all of its items.
        N)ra   rU   rN   rc   )rP   rd   re   rK   rK   rL   claim  s    

zTrackedList.claimc          	   C   sH   xBt tt| D ].}y| | js(| |= W q tk
r>   Y qX qW dS )z
        This method inspects the `used` attribute of all of its members, if it
        has one, and deletes any item in which it is set to `False`
        N)reversedr   r	  rx   rN   )rP   rd   rK   rK   rL   prune_unused  s    

zTrackedList.prune_unusedN)r_   )rg   rh   ri   rj   rq   r   r  r  r  r{   r  rU   r   r  insertr  __iadd____imul__r  r  rc   r  r  rK   rK   rK   rL   r1     s&   





c               @   s"   e Zd ZdZdddZdd ZdS )	r+   z Array of `Residue` instances r   c       	      C   s   |  }|  }y| d }W n< tk
rX   t|||||| d}|| | | Y nxX |j|ks|j|  ks|j|  ks|j|  ks|j	|  krt|||||| d}|| | | n
|| dS )ay  
        Adds a new atom to the ResidueList, adding a new residue to this list if
        it has a different name or number as the last residue

        Parameters
        ----------
        atom : :class:`Atom`
            The atom to add to this residue list
        resname : ``str``
            The name of the residue this atom belongs to
        resnum : ``int``
            The number of the residue this atom belongs to
        chain : ``str``
            The chain ID character for this residue
        inscode : ``str``
            The insertion code ID character for this residue (it is stripped)
        segid : ``str``
            The segment identifier for this residue (it is stripped)

        Notes
        -----
        If the residue name and number differ from the last residue in this
        list, a new residue is added and the atom is added to that residue
        r_   )rU   N)
r   r   r*   r  r   r   r   r  r  r  )	rP   r  ZresnameZresnumr  Zinscoder  ZlastZnew_resrK   rK   rL   r    s"    



zResidueList.add_atomc             C   s4   x.t tt| D ]}| | }| r| |= qW dS )a  
        This function goes through the residue list and removes all empty
        residues from the list. This isn't done automatically when atoms are
        deleted, since it will become very slow. You must remember to do this to
        avoid including empty residues
        N)r  r   r	  r  )rP   rd   r  rK   rK   rL   prune  s     zResidueList.pruneN)r   r   r   )rg   rh   ri   rj   r  r  rK   rK   rK   rL   r+     s   
.c               @   sz   e Zd ZdZedd ZedddZedd Zd	d
 Zedd Z	edd Z
edd Zdd Zdd Zdd ZdS )r   z
    Array of Atoms

    Notes
    -----
    Deleting an atom from the AtomList also deletes that atom from the residue
    it belongs to.
    c             C   sz   yt |t|  }W n tk
r0   |g}Y nX x6|D ].}| | }d|_d|_|jdk	r8|j| q8W t| | dS )z; Deleting an atom also needs to delete it from the residue r_   N)	r   r  r	  rN   rb   rU   r   r  r  )rP   rf   r  r|   r  rK   rK   rL   r    s    

 zAtomList.__delitem__r_   c             C   s2   t | |}d|_d |_ |jd k	r.|j| |S )Nr_   )rU   r{   rb   r   r  )rP   rf   r  rK   rK   rL   r{     s    
 zAtomList.popc             C   sH   |j | k	rtd| |jd k	r,|j| d|_d |_ t | | d S )Nz%r is not in listr_   )rU   r  r   r  rb   r  )rP   r  rK   rK   rL   r    s    

 zAtomList.removec             C   s   x| D ]
}d|_ qW dS )z Unmark all atoms in this list r   N)r   )rP   r   rK   rK   rL   unmark  s    
 zAtomList.unmarkc             C   s   | |_ t | |S )a  
        Add an Atom to the end of the list and have this list claim ownership of
        the item.

        Parameters
        ----------
        item : :class:`Atom`
            The atom to add to this list

        Notes
        -----
        Only Atom objects should be added here, so if `item` does not have a
        `list` attribute, an AttributeError will be raised. This action assigns
        this list as the `list` attribute to the passed Atom.
        )rU   r   )rP   re   rK   rK   rL   r   
  s    zAtomList.appendc             C   s    x|D ]
}| |_ qW t | |S )a0  
        Add an iterable of `Atom`s to the end of the list and have this list
        claim ownership of the items.

        Parameters
        ----------
        items : iterable of :class:`Atom`s
            The iterable containing Atom instances to add to the end of this
            list

        Notes
        -----
        Any generator passed here will be exhausted. The `list` attribute for
        every object in `items` will have their `list` attribute set to this
        list, and an AttributeError will be raised if this is not possible.
        )rU   r  )rP   itemsre   rK   rK   rL   r    s    

zAtomList.extendc             C   s   | |_ t | ||S )aB  
        Insert an Atom into the atom list

        Parameters
        ----------
        idx : ``int``
            The index in front of (i.e., before) which to insert the item
        item : :class:`Atom`
            The atom to insert in the desired index. This atom will be claimed
            by the AtomList
        )rU   r  )rP   rf   re   rK   rK   rL   r  4  s    zAtomList.insertc          
   C   s  d}t | }t }g }x.t| D ]"\}}|jtkr:tdd|j_q W xt| D ]~\}}|j}|jdkrjqP||_||t|< || x>t	|d |D ],}| | }	|	j}
|
jdkrq||
kr||
_qW |d7 }qPW x| D ]}|jj|_
qW dd t	|d D }xt|D ]t\}}xh|jD ]^}|j| \}}}}y|| }W n tk
rV   wY n X |j||||f}|| | qW q
W |S )a  
        Assigns the nb_idx attribute of every atom inside here from the
        atom_type definition. If the atom_type is not assigned, RuntimeError is
        raised.

        Returns
        -------
        ``list of dict``
            Each element is a `set` of the `nb_idx` indices for which NBFIX
            alterations are defined for the type with that given that index
            (minus 1, to adjust for indexing from 0 and nb_idx starting from 1)
        r   zatom types are not assignedr_   c             S   s   g | ]
}t  qS rK   )r   )rV   rd   rK   rK   rL   r  m  s    z4AtomList.assign_nbidx_from_types.<locals>.<listcomp>)r	  r   ra   r   r@   r  rb   strr   r   r   nbfixr  r   )rP   rf   ZnatomsZatom_type_lookupsZatom_type_listrd   r  Ztype1r}  rn   Ztype2Z
nbfix_listrt   rW   r   epsr   eps14Zotyperr   rK   rK   rL   assign_nbidx_from_typesD  sH    

 

 


z AtomList.assign_nbidx_from_typesc             C   s,   x| D ]}|j |kr|S qW td| dS )a4  
        Finds an atom with the given original index. Cannot assume that the
        original indexes are in order, since reordering may have been necessary.
        As a result, the complexity of this algorithm is O(N)

        Parameters
        ----------
        idx : int
            The integer corresponding to the original index

        Returns
        -------
        atom : :class:`Atom`
            The atom with the original index ``idx``

        Raises
        ------
        IndexError
            If no atom has the original index ``idx``
        zNo atom found with index %dN)r   r   )rP   rf   r  rK   rK   rL   find_original_index|  s    

 zAtomList.find_original_indexc             C   s   t S )N)rO   )rP   rQ   rK   rK   rL   r    s    zAtomList.__iadd__N)r_   )rg   rh   ri   rj   r  r  r{   r  r  r   r  r  r  r  r  rK   rK   rK   rL   r     s   	8c               @   s*   e Zd ZdZd	ddZdd Zdd ZdS )
r4   a  
    The AMOEBA force field has complex exclusion and exception rules (referred
    to as "adjustments" in the Amber-converted files). This class stores
    per-particle exceptions.

    Parameters
    ----------
    atom1 : :class:`Atom`
        One of the atoms in the exclusion pair
    atom2 : :class:`Atom`
        The other atom in the exclusion pair
    type : :class:`NonbondedExceptionType`
        The nonbonded exception type that describes how the various nonbonded
        interactions between these two atoms should work

    Notes
    -----
    NonbondedException objects "contain" two atoms and will return True when
    used with the binary `in` operator
    Nc             C   s   || _ || _|| _d| _d S )Nr   )rm   rn   rt   r5  )rP   rm   rn   rt   rK   rK   rL   rq     s    zNonbondedException.__init__c             C   s   || j kp|| jkS )N)rm   rn   )rP   r8  rK   rK   rL   rs     s    zNonbondedException.__contains__c             C   sJ   dt | j| j| jf g}| j d k	r6|d| j   n
|d d|S )Nz<%s; %r and %rz
, type=%r>r   r   )rt   rg   rm   rn   r   r   )rP   r[  rK   rK   rL   r     s    

zNonbondedException.__repr__)N)rg   rh   ri   rj   rq   rs   r   rK   rK   rK   rL   r4     s   
c               @   sz   e Zd ZdZdddZedd Zejdd Zed	d
 Zedd Z	edd Z
dd Zedd Ze Zdd ZdS )r5   a  
    A parameter describing how the various nonbonded interactions between a
    particular pair of atoms behaves in a specified nonbonded exception (e.g.,
    in 1-4 interacting terms)

    Parameters
    ----------
    rmin : float
        The combined Rmin value for this particular pair of atom types
        (dimension length, default units are Angstroms)
    epsilon : float
        The combined well-depth value for this particular pair of atom types
        (dimension energy, default units are kcal/mol)
    chgscale : float, optional
        The scaling factor by which to multiply the product of the charges for
        this pair. Default is 1.0.
    list : :class:`TrackedList`
        The list containing this nonbonded exception
          ?Nc             C   s<   t |  t|tj| _t|tj| _|| _d | _	|| _
d S )N)rw   rq   rM   rE   r   r   r   r   chgscalerb   rU   )rP   r   r   r  rU   rK   rK   rL   rq     s    
zNonbondedExceptionType.__init__c             C   s
   | j d S )Ng)N>?)r   )rP   rK   rK   rL   r     s    zNonbondedExceptionType.sigmac             C   s   |d | _ d S )NgÚ?)r   )rP   rJ   rK   rK   rL   r     s    c             C   s   | j tj S )N)r   rE   r   )rP   rK   rK   rL   r     s    zNonbondedExceptionType.usigmac             C   s   | j tj S )N)r   rE   r   )rP   rK   rK   rL   r     s    zNonbondedExceptionType.urminc             C   s   | j tj S )N)r   rE   r   )rP   rK   rK   rL   r     s    zNonbondedExceptionType.uepsilonc             C   s   dt | j| j| j| jf S )Nz,<%s; rmin=%.4f, epsilon=%.4f, chgscale=%.4f>)rt   rg   r   r   r  )rP   rK   rK   rL   r     s    zNonbondedExceptionType.__repr__c             C   s<   t | j|j tk o:t | j|j tk o:t | j|j tk S )N)r#  r   r   r   r  )rP   rQ   rK   rK   rL   rA    s    (zNonbondedExceptionType.__eq__c             C   s&   t t| jtt| jtt| jtfS )N)rC  rD  r   rE  r   r  )rP   rK   rK   rL   rF    s    zNonbondedExceptionType.__hash__)r  N)rg   rh   ri   rj   rq   rk   r   r   r   r   r   r   rT   rA  r]   r\   rF  rK   rK   rK   rL   r5     s   
c               @   s<   e Zd ZdZdddZedd Zdd Ze Z	d	d
 Z
dS )r6   a  
    A parameter describing how the various nonbonded interactions between a
    particular pair of atoms is scaled in the AMOEBA force field

    Parameters
    ----------
    vdw_weight : ``float``
        The scaling factor by which van der Waals interactions are multiplied
    multipole_weight : ``float``
        The scaling factor by which multipole interactions are multiplied
    direct_weight : ``float``
        The scaling factor by which direct-space interactions are multiplied
    polar_weight : ``float``
        The scaling factor by which polarization interactions are multiplied
    mutual_weight : ``float``
        The scaling factor by which mutual interactions are multiplied
    list : :class:`TrackedList`
        The list containing this nonbonded exception

    Other Attributes
    ----------------
    idx : ``int``
        The index of this term in the list that contains it
    Nc             C   s.   || _ || _|| _|| _|| _d| _|| _d S )Nr_   )r   multipole_weightdirect_weightpolar_weightmutual_weightrb   rU   )rP   r   r  r  r  r  rU   rK   rK   rL   rq     s    z%AmoebaNonbondedExceptionType.__init__c             C   sd   t | j|j tk obt | j|j tk obt | j|j tk obt | j|j tk obt | j|j tk S )N)r#  r   r   r  r  r  r  )rP   rQ   rK   rK   rL   rA  (  s
    z#AmoebaNonbondedExceptionType.__eq__c             C   s   t | j| j| j| j| jS )N)r6   r   r  r  r  r  )rP   rK   rK   rL   r   0  s    z%AmoebaNonbondedExceptionType.__copy__c             C   s:   t t| jtt| jtt| jtt| jtt| jtfS )N)rC  rD  r   rE  r  r  r  r  )rP   rK   rK   rL   rF  8  s
    


z%AmoebaNonbondedExceptionType.__hash__)N)rg   rh   ri   rj   rq   rT   rA  r   r]   r\   rF  rK   rK   rK   rL   r6     s   
	c               @   s   e Zd ZdZd*ddZedd Zd+d	d
Zedd Z	e	j
dd Z	dd Zd,ddZedd Zej
dd Zedd Zedd Zej
dd Zedd Zedd Zedd Zed d! Zed"d# Zd$d% Zd&d' Zd(d) ZdS )-r9   a	  
    Atom types can either be compared by indexes or names. Can be assigned with
    a string, integer, (string is not automatically cast) or with both.

    Parameters
    ----------
    name : ``str``
        The name of the atom type
    number : ``int``
        The serial index of the atom type
    mass : ``float``
        The mass of the atom type
    atomic_number : ``int``, optional
        The atomic number of the element of the atom type. Default -1
    bond_type : ``str``, optional
        If defined, this is the type name used to look up bonded parameters.
        Default is None (which falls back to ``name``)
    charge : ``float``, optional
        If defined, this is the partial atomic charge in elementary charge
        units. Default is None

    Other Attributes
    ----------------
    epsilon : ``float``
        If set, it is the Lennard-Jones well depth of this atom type
    rmin : ``float``
        If set, it is the Lennard-Jones Rmin/2 parameter of this atom type
    epsilon_14 : ``float``
        If set, it is the Lennard-Jones well depth of this atom type in 1-4
        nonbonded interactions
    rmin_14 : ``float``
        If set, it is the Lennard-Jones Rmin/2 parameter of this atom type in
        1-4 nonbonded interactions
    sigma : ``float``
        This is the sigma parameter, which is just equal to Rmin*2^(1/6)
    sigma_14 : ``float``
        This is the sigma parameter corresponding to rmin_14, which is just equal to Rmin_14*2^(1/6)
    nbfix : ``dict(str:tuple)``
        A hash that maps atom type names of other atom types with which _this_
        atom type has a defined NBFIX with a tuple containing the terms
        (Rmin, epsilon, Rmin14, Epsilon14)
    _bond_type : str or None
        If an explicit value was given to bond_type, _bond_type will be set to a
        value. Otherwise, _bond_type will be None (and bond_type will be the
        same as ``name``). This can be used to determine if a bond_type was
        specified by comparing to None.

    Notes
    -----
    This object is primarily used to build parameter databases from parameter
    files. Also, sigma is related to Rmin, but rmin is Rmin/2, so there is an
    extra factor of 2 in the sigma for this reason.

    Examples
    --------
    >>> at = AtomType('HA', 1, 1.008, 1)
    >>> at.name, at.number
    ('HA', 1)
    >>> at2 = AtomType('CA', 2, 12.01, 6)
    >>> at2.name, at2.number
    ('CA', 2)
    >>> print("%s: %d" % (str(at), int(at)))
    HA: 1
    r_   N        c             C   s   |d kr6|d k	r6t |tr(|| _d | _qF|| _d | _n|| _t|| _t|tj| _|| _d  | _	 | _
 | _| _t | _d| _|| _|| _d S )Nr_   )r   rX  r   r   rM   rE   r   r   r   r   r   r   r   r   r  rb   
_bond_typer   )rP   r   r   r   r   	bond_typer   rK   rK   rL   rq     s    

zAtomType.__init__c             C   s  t |trH| j|jks$| j|jkr(dS d}d}xTdD ]L}t| |dkr^t||dkr^d}q6q6t| |dkszt||dkr~dS d}q6W |r|r|r|rtd|sdS | jdks|jdkr| j|jk	rdS nt| j|j tkrdS t| j	|j	 tk oFt| j
|j
 tk oFt| j|j tk oFt| j|j tk oF| j|jkS t |tr^| j|kS t |trt| j|kS || j| jfkS )zm
        Compares based on available properties (name and number, just name,
        or just number)
        FT)r   r   r   r   Nz%Should have all or none at this point)r   r9   r   r   r~   r
  r   r#  r   r   r   r   r   r  r   rX  )rP   rQ   Zhas_allZhas_noner   rK   rK   rL   rA    s>    


zAtomType.__eq__c             C   s`   t |r|t j}t |r,|t j}|dkr8|}|dkrD|}|| _|| _|| _|| _dS )z1 Sets Lennard-Jones parameters on this atom type N)	rE   rF   rH   r   r   r   r   r   r   )rP   r  r   r  r   rK   rK   rL   set_lj_params  s    

zAtomType.set_lj_paramsc             C   s   | j d kr| jS | j S )N)r  r   )rP   rK   rK   rL   r    s    
zAtomType.bond_typec             C   s
   || _ d S )N)r  )rP   rJ   rK   rK   rL   r    s    c             C   s   | j S )z8 The integer representation of an AtomType is its index )r   )rP   rK   rK   rL   __int__  s    zAtomType.__int__c             C   s.   |dkr|}|dkr|}||||f| j |< dS )a    Adds a new NBFIX exclusion for this atom type

        Parameters
        ----------
        typename : str
            The name of the *other* type with which this NBFIX is defined
        rmin : float
            The combined Rmin value for this NBFIXed pair. If no units, assumed
            to be in Angstroms
        epsilon : float
            The combined epsilon value for this NBFIXed pair. If no units,
            assumed to be in kcal/mol
        rmin14 : float, optional
            Same as rmin, but for 1-4 interactions. If None (default), it is
            given the same value as rmin
        epsilon14 : float, optional
            Same as epsilon, but for 1-4 interactions. If None (default), it is
            given the same value as rmin
        N)r  )rP   typenamer   r   r   r   rK   rK   rL   	add_nbfix  s
      zAtomType.add_nbfixc             C   s   | j d d S )z Sigma is Rmin / 2^(1/6) g)N>?rD   )r   )rP   rK   rK   rL   r     s    zAtomType.sigmac             C   s   |d d | _ d S )NgÚ?rD   )r   )rP   rJ   rK   rK   rL   r     s    c             C   s   | j tj S )N)r   rE   r   )rP   rK   rK   rL   r     s    zAtomType.usigmac             C   s   | j d d S )z Sigma is Rmin / 2^(1/6) g)N>?rD   )r   )rP   rK   rK   rL   r   	  s    zAtomType.sigma_14c             C   s   |d d | _ d S )NgÚ?rD   )r   )rP   rJ   rK   rK   rL   r     s    c             C   s   | j tj S )N)r   rE   r   )rP   rK   rK   rL   r     s    zAtomType.usigma_14c             C   s   | j tj S )N)r   rE   r   )rP   rK   rK   rL   r     s    zAtomType.urminc             C   s   | j tj S )N)r   rE   r   )rP   rK   rK   rL   r     s    zAtomType.uepsilonc             C   s   | j tj S )N)r   rE   r   )rP   rK   rK   rL   r     s    zAtomType.urmin_14c             C   s   | j tj S )N)r   rE   r   )rP   rK   rK   rL   r   "  s    zAtomType.uepsilon_14c             C   s   | j S )N)r   )rP   rK   rK   rL   r  &  s    zAtomType.__str__c             C   sP   t | j| j| j| j| j| jd}| j|_| j|_| j	|_	| j
|_
| j |_|S )N)r  r   )r9   r   r   r   r   r  r   r   r   r   r   r  r   )rP   ZcprK   rK   rL   r   )  s    zAtomType.__copy__c             C   s8   t | j| j| j| j| j| j| j| j| j	t
| j f
S )N)rC  r   r   r   r  r   r   r   r   r   rw  r  r  )rP   rK   rK   rL   rF  3  s    zAtomType.__hash__)r_   Nr  )NN)NN)rg   rh   ri   rj   rq   rT   rA  r  rk   r  r   r  r  r   r   r   r   r   r   r   r   r  r   rF  rK   rK   rK   rL   r9   A  s*   @
+


c                   sD   e Zd ZdZdZ fddZdd Zdd Zd	d
 Zdd Z	  Z
S )_UnassignedAtomTypezk
    This raises the appropriate exceptions (ParameterError) when you try to
    access its properties
    Nc                s"   | j d krtt| | | _ | j S )N)_OBJr   r  __new__)r   )r  rK   rL   r  A  s    
z_UnassignedAtomType.__new__c             C   s   t dd S )NzAtom type is not defined)r   )rP   rK   rK   rL   r  F  s    z_UnassignedAtomType.__int__c             C   s   t dd S )NzAtom type is not defined)r   )rP   rK   rK   rL   r  I  s    z_UnassignedAtomType.__str__c             C   s
   t |tS )N)r   r  )rP   rQ   rK   rK   rL   rA  L  s    z_UnassignedAtomType.__eq__c             C   s   dS )Nr@   rK   )rP   rK   rK   rL   rB  O  s    z_UnassignedAtomType.__reduce__)rg   rh   ri   rj   r  r  r  r  rA  rB  r  rK   rK   )r  rL   r  :  s   r  zNot a singletonc               @   s(   e Zd ZdZdd Zdd Zdd ZdS )	r7   z Just a holder for donors and acceptors in CHARMM speak

    Parameters
    ----------
    atom1 : :class:`Atom`
        First atom in the donor/acceptor group
    atom2 : :class:`Atom`
        Second atom in the donor/acceptor group
    c             C   s   || _ || _d S )N)rm   rn   )rP   rm   rn   rK   rK   rL   rq   b  s    zAcceptorDonor.__init__c             C   s   d| j | jf S )Nz<AcceptorDonor; %r %r>)rm   rn   )rP   rK   rK   rL   r   f  s    zAcceptorDonor.__repr__c             C   s   || j kp|| jkS )z+ See if the atom is in this donor/acceptor )rm   rn   )rP   r8  rK   rK   rL   rs   i  s    zAcceptorDonor.__contains__N)rg   rh   ri   rj   rq   r   rs   rK   rK   rK   rL   r7   X  s   	c               @   s$   e Zd ZdZdd Zedd ZdS )r8   a~   An 'interacting' group defined by CHARMM PSF files

    Parameters
    ----------
    atom : :class:`Atom`
        The first atom within a group
    type : ``int``
        Flag for group information; 0 when all atoms have zero charge,
        1 when group has a net zero charge but at least one atom has a non-zero
        partial charge, 2 when the net charge of the group is not zero
    move : ``int``
        0 if the atoms are not fixed, 1 when they are

    Notes
    -----
    See the discussion on Github for the source of the meanings of these
    variables: https://github.com/ParmEd/ParmEd/pull/307#issuecomment-128244134
    c             C   s   || _ || _|| _d S )N)r  rt   move)rP   r  rt   r  rK   rK   rL   rq     s    zGroup.__init__c             C   s$   | j |j ko"| j|jko"| j|jkS )N)r  rt   r  )rP   rQ   rK   rK   rL   rA    s    zGroup.__eq__N)rg   rh   ri   rj   rq   rT   rA  rK   rK   rK   rL   r8   o  s   c               @   s   e Zd ZdZdddZdS )rA   aF   An intra-residue "Link" as defined by the PDB standard:

    See http://www.wwpdb.org/documentation/file-format-content/format33/sect6.html#LINK for more
    information

    Parameters
    ----------
    atom1 : :class:`Atom`
        The first Atom involved in the Link
    atom2 : :class:`Atom`
        The other atom to which ``atom1`` is bonded in this link
    length : float
        The length of the link
    symmetry_op1 : str, optional
        The first symmetry operator for the link
    symmetry_op2 : str, optional
        The second symmetry operator for the link
    1555c             C   s"   || _ || _|| _|| _|| _d S )N)rm   rn   lengthsymmetry_op1symmetry_op2)rP   rm   rn   r  r  r  rK   rK   rL   rq     s
    zLink.__init__N)r  r  )rg   rh   ri   rj   rq   rK   rK   rK   rL   rA     s   g        )N)N)trj   Z
__future__r   r   r   r  rr  r   	functoolsr   r   r   rE   Z	constantsr	   rE  r
   r   r   
exceptionsr   r   r   Zgeometryr   r   r   Zutils.decoratorsr   Z	utils.sixr   r   Zutils.six.movesr   r   Zperiodic_tabler   __all__r  r   r   r   Zscale_factorrH   ZpicosecondsZBaseUnitZpicosecond_base_unitZ	dimensionZakma_time_unitZdefine_conversion_factor_toZ
UnitSystemZangstrom_base_unitZdalton_base_unitZelementary_charge_base_unitZkelvin_base_unitZmole_base_unitZradian_base_unitrI   rM   rT   r]   objectr^   rl   rw   r}   r   r   r;   r<   r=   r>   r   r   r   r   r"   r#   r?   rU   r$   r2   r%   r&   r    r!   r  r0   r(   r3   r)   r,   r-   r.   r  r/   r   r'   rB   rC   r*   r  r1   r+   r   r4   r5   r6   r9   r  r@   r
  r7   r8   rA   r:   rK   rK   rK   rL   <module>   s    

6(       :N a / Y|K xf b M  Z 
,'DD'l\Hl#-%   ? A)B< z