B
    bϓ                 @   s   d Z ddlmZmZ ddlZddlZddlZddl	m
Z
mZmZ ddlmZ ddlmZmZmZmZ ddlmZ ddlmZmZ ddlZd	d
dddddgZG dd deZed	Zed
ZedZedZ G dd deZ!G dd de!Z"G dd de#Z$dS )zr
This contains the basic residue template and residue building libraries
typically used in modelling applications
    )OrderedDictdefaultdictN)AminoAcidResidue
RNAResidue
DNAResidue)	Structure)AtomBondAtomListTrackedList)	iteritems)IncompatiblePatchErrorMoleculeErrorPROTEINNUCLEICSOLVENTUNKNOWNResidueTemplateResidueTemplateContainerPatchTemplatec               @   s(   e Zd ZdZdd Zdd Zdd ZdS )	_ResidueTypez) Singleton for various types of residues c             C   s
   || _ d S )N)name)selfr    r   6lib/python3.7/site-packages/parmed/modeller/residue.py__init__   s    z_ResidueType.__init__c             C   s
   d| j  S )Nz<ResidueType %s>)r   )r   r   r   r   __repr__   s    z_ResidueType.__repr__c             C   s   | j S )N)r   )r   r   r   r   __str__   s    z_ResidueType.__str__N)__name__
__module____qualname____doc__r   r   r   r   r   r   r   r      s   r   c               @   s   e Zd ZdZd6ddZdd Zedd Zd	d
 Zdd Z	d7ddZ
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 d! Zd"d# Zd8d&d'Zd9d(d)Zd*d+ Zd:d-d.Zd/d0 Zd1d2 Zd;d4d5Zd$S )<r   a  
    This is a residue template, which contains a listing of the atoms in the
    residue template as well as a mapping of which atoms are bonded to other
    atoms.

    Parameters
    ----------
    name : str, optional
        If provided, this is the name of the residue

    Attributes
    ----------
    atoms : :class:`AtomList`
        List of atoms in this residue
    bonds : :class:`TrackedList`
        List of the bonds between the atoms in this residue
    coordinates : np.ndarray(natom, 3)
        The partial atomic coordinates
    connections : list of :class:`Atom`
        A list of all atoms that should form connections with atoms of another
        residue *besides* the head and tail atoms
    head : :class:`Atom` or None
        The atom that is connected to the residue that comes before this one
    tail : :class:`Atom` or None
        The atom that is connected to the *next* residue after this one
    first_patch : :class:`ResidueTemplate` or None
        If it is not None, this is the patch whose tail is added to the head
        atom of this residue when this residue is the first in a chain
    last_patch : :class:`ResidueTemplate` or None
        If it is not None, this is the patch whose head is added to the tail
        atom of this residue when this residue is the last in a chain
    groups : list of list(:class:`Atom`)
        If set, each group is a list of Atom instances making up each group
    override_level : integer
        For use with OpenMM ResidueTemplates. If OpenMM ForceField is given multiple
        identically-matching residue templates with the same names it choses
        (overrides with) the one with the highest override_level
        (overrideLevel in OpenMM). Default is 0.
     c             C   sh   t  | _t | _t | _t | _|| _d | _d | _	g | _
t| _d | _d | _g | _d| _t | _g | _d S )Nr   )r
   atomsr   bondslistZ	lonepairsZanisotropiesr   headtailconnectionsr   typefirst_patch
last_patchgroupsZoverride_leveldict_map_impr)r   r   r   r   r   r   Q   s    zResidueTemplate.__init__c             C   sX   | j d k	r| j j}nd}| jd k	r,| jj}nd}dt| j| jt| jt| j||f S )NNonez-<%s %s: %d atoms; %d bonds; head=%s; tail=%s>)r&   r   r'   r)   r   lenr#   r$   )r   r&   r'   r   r   r   r   b   s    



zResidueTemplate.__repr__c             C   s   | j S )N)r.   )r   r   r   r   mapo   s    zResidueTemplate.mapc             C   s<   |j | jkrtd|j  | |_| j| || j|j < dS )a   Adds an atom to this residue template

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

        Raises
        ------
        ValueError if ``atom`` has the same name as another atom in this
        residue already
        z!Residue already has atom named %sN)r   r.   
ValueErrorresiduer#   append)r   atomr   r   r   add_atoms   s
    zResidueTemplate.add_atomc             C   s   t |tkr|}n|j}|| jkr<td|t| j f | j| }| j|krVd| _| j|krfd| _x0t| j	D ]"}|j
|ks|j|krr| | qrW x&t| jD ]}||kr| j| qW d|_| j|= | j| dS )z Delete an atom from this residue template, along with corresponding bonds.

        Parameters
        ----------
        atom : :class:`Atom` or str
            The atom or atom name to be deleted

        zECould not find atom '%s' in ResidueTemplate, which contains atoms: %sN)r)   strr   r.   KeyErrorr%   keysr&   r'   r$   atom1atom2delete_bondr/   remover4   r#   )r   r6   	atom_namebondimprr   r   r   delete_atom   s&    	



zResidueTemplate.delete_atom      ?c             C   sh   t |ts| | }t |ts$| | }|j| jk	s<|j| jk	rDtd||jkrd| jt|||d dS )a   Adds a bond between the two provided atoms in the residue

        Parameters
        ----------
        atom1 : :class:`Atom` or int or str
            One of the atoms in the bond. It must be in the ``atoms`` list of
            this ResidueTemplate. It can also be the atom index (index from 0)
            of the atom in the bond.
        atom2 : :class:`Atom` or int or str
            The other atom in the bond. It must be in the ``atoms`` list of this
            ResidueTemplate. It can also be the atom index (index from 0) of the
            atom in the bond.
        order : float
            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

        Raises
        ------
        IndexError if atom1 or atom2 are integers that are out of range of the
        number of atoms already in this template

        RuntimeError if atom1 or atom2 are :class:`Atom` instances but they are
        *not* in the atoms list of this ResidueTemplate

        Notes
        -----
        If atom1 and atom2 are already bonded, this routine does nothing. If
        atom1 or atom2 are strings, then they will match the first instance of
        the atom name that is the same as the atom name passed.
        z(Both atoms must belong to template.atoms)orderN)	
isinstancer   r%   r#   RuntimeErrorZbond_partnersr$   r5   r	   )r   r;   r<   rD   r   r   r   add_bond   s    $


zResidueTemplate.add_bondc             C   s4   || j kr |  | j | ntd|| dS )z Delete a bond from this residue template.

        Parameters
        ----------
        bond : :class:`Bond`
            The bond to be deleted

        z8The specified bond {} does not belong to this residue {}N)r$   deleter>   r3   format)r   r@   r   r   r   r=      s    	
zResidueTemplate.delete_bondc       	   
   C   s4  | |j d}x|D ]}|t| qW x|D ]}x|jD ]}y |j|j}|j|j}W n t	k
r   |j|kr|j}|j|j}n|j}|j|j}|j
j|jd kr|j| |_nN|j
j|jd kr|j| |_n.|j
j|jkrtd n|j|j|  Y q>X ||| q>W q2W |S )z
        This constructor creates a ResidueTemplate from a particular Residue
        object
        Parameters
        ----------
        residue : :class:`Residue`
            The residue from which to create a template
        )r      z2Cannot determine head/tail for unordered residues.)r   r7   _copycopyr$   r#   indexr;   r<   r3   r4   idxr&   r'   warningswarnr(   r5   rG   )	clsr4   instr6   r@   Zi1Zi2ZoatomrN   r   r   r   from_residue   s.    


zResidueTemplate.from_residuec             C   s8   y| j S  tk
r0   tdd | D | _ Y nX | j S )z@ Atomic coordinates, in Angstroms, of all atoms in the template c             S   s   g | ]}|j |j|jgqS r   )xxxyxz).0ar   r   r   
<listcomp>  s    z/ResidueTemplate.coordinates.<locals>.<listcomp>)Z_crdAttributeErrornpZarray)r   r   r   r   coordinates  s
    zResidueTemplate.coordinatesc             C   s   t t}x | jD ]}||j  d7  < qW d|kr<|d dd }d}d|krt|||d7 }d|krt|||d7 }t| }x|D ]}||||7 }qW |S )zj Return the empirical chemical formula (in Hill notation) as a string (e.g. 'H2O', 'C6H12'), omitting EPs rJ   ZEPc             S   s"   |  |}|dkr|S |t| S )NrJ   )popr8   )element_countelementcountr   r   r   format_and_pop_element*  s    
zJResidueTemplate.empirical_chemical_formula.<locals>.format_and_pop_elementr"   CH)r   intr#   Zelement_namer]   sortedr:   )r   r^   r6   ra   Zchemical_formulaZalphabetical_elementsr_   r   r   r   empirical_chemical_formula  s    

z*ResidueTemplate.empirical_chemical_formulac             C   s   t dd | D S )Nc             S   s   g | ]
}|j qS r   )charge)rW   rX   r   r   r   rY   ?  s    z.ResidueTemplate.net_charge.<locals>.<listcomp>)sum)r   r   r   r   
net_charge=  s    zResidueTemplate.net_chargec             C   s
   t | jS )N)r1   r#   )r   r   r   r   __len__C  s    zResidueTemplate.__len__c             C   s
   t | jS )N)iterr#   )r   r   r   r   __iter__F  s    zResidueTemplate.__iter__c             C   s4   t |tr|| jkS t |tr(|| jkS tdd S )NzShould not be here!)rE   r   r#   r8   r.   AssertionError)r   r6   r   r   r   __contains__I  s
    



zResidueTemplate.__contains__c             C   s   t | | jd}x| jD ]}|t| qW x"| jD ]}||jj	|j
j	 q8W | j |_ | jd k	rv|j| jj	 |_| jd k	r|j| jj	 |_x"| jD ]}|j|j|j	  qW | j|_| j|_|S )N)r   )r)   r   r#   r7   rK   rL   r$   rG   r;   rN   r<   r&   r'   r(   r5   r*   r+   )r   otherr6   r@   Z
connectionr   r   r   __copy__P  s    

zResidueTemplate.__copy__c                sj   t |tr<x jD ]}|j|kr|S qW td| jf n*t |ttfr\ fdd|D S  j| S d S )NzAtom %s not found in %sc                s   g | ]} | qS r   r   )rW   key)r   r   r   rY   k  s    z/ResidueTemplate.__getitem__.<locals>.<listcomp>)rE   r8   r#   r   
IndexErrorr%   tuple)r   rN   r6   r   )r   r   __getitem__d  s    

zResidueTemplate.__getitem__N   c             C   s   | j std| j}|dkr&t|}n
t||}||kr<| S || t|  }x| D ]}t|j| ||_qRW | j d  j|tdd | j D  7  _| S )a  
        Adjusts the partial charge of all atoms in the residue to match the
        requested target charge. The default target charge is the closest
        integer

        Parameters
        ----------
        to : float, optional
            The desired net charge of this residue template. Default is the
            closest integer charge
        precision : int, optional
            The number of decimal places that each charge should be rounded to.
            Default is 4

        Returns
        -------
        self : :class:`ResidueTemplate`
            The current residue template whose charges are being modified

        Notes
        -----
        This method modifies the atomic charges of this residue template
        in-place. Any residual charge (which is accumulated roundoff beyond the
        requested precision) is added to the first atom of the residue. This
        will typically be 10^-precision in magnitude, and should almost never be
        higher than 2*10^-precision. As long as a reasonable precision is chosen
        (no fewer than 3 or 4 decimal places), this will have only a negligible
        impact on a force field.

        If provided, "to" will be rounded to the ``precision``'th decimal place
        to make sure that the sum of the charges come out as close as possible
        to the target charge while still obeying the requested precision.

        Raises
        ------
        ValueError
            If you try to call fix_charges on a residue template with no atoms
        z&Cannot fix charges on an empty residueNr   c             s   s   | ]}|j V  qd S )N)rg   )rW   r6   r   r   r   	<genexpr>  s    z.ResidueTemplate.fix_charges.<locals>.<genexpr>)r#   r3   ri   roundr1   rg   rh   )r   to	precisionri   Zsmearr6   r   r   r   fix_chargeso  s    '


(zResidueTemplate.fix_chargesc             C   s  t | }d}x|jD ]}y|| d}W q ttfk
r } zR|drp|dd | krp|dd |jkrpn td|t|j	
 t|f W dd}~X Y qX qW xT|jD ]J}|j|kr|j||j _|j||j _n|t|j|j|jd d}qW x|jD ]\}}	}
ybxJ||	gD ]>}|jr:||jjkr:d|_|jr||jjkrd|_qW |||	|
 d}W nJ ttfk
r } z&td||	t|j	
 t|f W dd}~X Y nX qW xR|jD ]H}y|j| W n0 tk
r } ztd	| W dd}~X Y nX qW |j}t||t| d
k}|sDtd| ddl}|d}||sdd | |D }tdt| |std|S )aC  
        Apply the specified PatchTemplate to the ResidueTemplate.

        This only handles patches that affect a single residue.

        An exception is thrown if patch is incompatible because
        * The patch specifies that an atom is to be deleted that doesn't exist in the residue
        * A bond specified as being added in the patch does not have both atom names present after adding/deleting atoms from the patch
        * The new net charge is not integral to the specified precision
        * The residue is not modified in any way (no atoms or bonds added/changed/deleted)

        Parameters
        ----------

        patch : PatchTemplate
            The patch to apply to this residue

        precision : int, optional
            Each valid patch should be produce a net charge that is integral to
            this many decimal places.
            Default is 4

        Returns
        -------

        residue : ResidueTemplate
            A new ResidueTemplate corresponding to the patched residue is returned.
            The original remains unmodified.

        FTDrJ   NzSAtom %s could not be deleted from the patched residue: atoms are %s (exception: %s))r   r)   rg   zNBond %s-%s could not be added to patched residue: atoms are %s (exception: %s)z3Improper %s was not found in residue to be patched.g        zPPatch is not compatible with residue due to non-integral charge (charge was %f).r   c             S   s   g | ]}|qS r   r   )rW   cr   r   r   rY      s    z/ResidueTemplate.apply_patch.<locals>.<listcomp>z7Patched residue bond graph is not a connected graph: %szPatch did not modify residue.)!rK   rL   delete_atomsrB   r9   r   
startswithr   r%   r.   r:   r8   r#   r   r)   rg   r7   r   	add_bondsr&   r'   rG   rr   delete_impropersr/   r>   r3   ri   rw   networkxto_networkxZis_connectedZconnected_components)r   patchry   r4   Zmodifications_mader?   er6   Z
atom1_nameZ
atom2_namerD   r   rA   ri   Zis_integralZnxGZ
componentsr   r   r   apply_patch  sX    !

,6
:$
zResidueTemplate.apply_patchc             C   s(   y|  | dS  tk
r"   dS X dS )a  Determine whether a specified patch is compatible with this residue.

        Compatibility is determined by whether Residue.Template.apply_patch(patch) raises as
        exception or not.

        Parameters
        ----------
        patch : PatchTemplate
            The patch to be applied to this residue.

        Returns
        -------
        is_compatible : bool
            True if patch is compatible with the residue; False if not.

        TFN)r   r   )r   r   r   r   r   patch_is_compatible  s
    
z#ResidueTemplate.patch_is_compatibleTc             C   s   ddl }| }x2| jD ](}|jdks*|r|j|j|j|jd qW x>| jD ]4}|j	jdkrh|j
jdksl|rL||j	j|j
j qLW |S )ad   Create a NetworkX graph of atoms and bonds

        Parameters
        ----------
        include_extra_particles : bool
            Whether to include "atoms" that actually represent extra particles (atomic_number == 0).

        Returns
        -------
        G : :class:`networkx.Graph`
            A NetworkX Graph representing the molecule

        r   N)rg   r)   )r   ZGraphr#   atomic_numberZadd_noder   rg   r)   r$   r;   r<   Zadd_edge)r   Zinclude_extra_particlesr   r   r6   r@   r   r   r   r     s    zResidueTemplate.to_networkxc             C   s0  ddl }| }dd | jD |d< dd | jD |d< dd | jD |d	< d
d | jD |d< dd | jD |d< dd | jD |d< dd | jD |d< dd | jD |d< dd | jD |d< dd | jD |d< dd | jD |d< dd | jD |d< dd | jD |d< dd | jD |d< d d | jD |d!< d"d | jD |d#< d$d | jD |d%< d&d | jD |d'< d(d | jD |d)< d*d | jD |d+< y$|jd,d | jD d-d.d/gd0}W n tk
r   Y nX ||}y$|jd1d | jD d2d3d4gd0}W n tk
r    Y nX ||}|S )5aB   Create a pandas dataframe from the atom information

        Returns
        -------
        df : :class:`pandas.DataFrame`
            The pandas DataFrame with all of the atomic properties

        Notes
        -----
        The DataFrame will be over all atoms. The columns will be the attributes
        of the atom (as well as its containing residue). Some columns will
        *always* exist. Others will only exist if those attributes have been set
        on the Atom instances (see the :class:`Atom` docs for possible
        attributes and their meaning). The columns that will always be present
        are:

            - number : int
            - name : str
            - type : str
            - atomic_number : int
            - charge : float
            - mass : float
            - nb_idx : int
            - solvent_radius : float
            - screen : float
            - occupancy : float
            - bfactor : float
            - altloc : str
            - tree : str
            - join : int
            - irotat : int
            - rmin : float
            - epsilon : float
            - rmin_14 : float
            - epsilon_14 : float

        The following attributes are optionally present if they were present in
        the original file defining the structure:

            - xx : float (x-coordinate position)
            - xy : float (y-coordinate position)
            - xz : float (z-coordinate position)
            - vx : float (x-coordinate velocity)
            - vy : float (y-coordinate velocity)
            - vz : float (z-coordinate velocity)
        r   Nc             S   s   g | ]
}|j qS r   )number)rW   r6   r   r   r   rY   i  s    z0ResidueTemplate.to_dataframe.<locals>.<listcomp>r   c             S   s   g | ]
}|j qS r   )r   )rW   r6   r   r   r   rY   j  s    r   c             S   s   g | ]
}|j qS r   )r)   )rW   r6   r   r   r   rY   k  s    r)   c             S   s   g | ]
}|j qS r   )r   )rW   r6   r   r   r   rY   l  s    r   c             S   s   g | ]
}|j qS r   )rg   )rW   r6   r   r   r   rY   m  s    rg   c             S   s   g | ]
}|j qS r   )mass)rW   r6   r   r   r   rY   n  s    r   c             S   s   g | ]
}|j qS r   )nb_idx)rW   r6   r   r   r   rY   o  s    r   c             S   s   g | ]
}|j qS r   )solvent_radius)rW   r6   r   r   r   rY   p  s    r   c             S   s   g | ]
}|j qS r   )screen)rW   r6   r   r   r   rY   q  s    r   c             S   s   g | ]
}|j qS r   )	occupancy)rW   r6   r   r   r   rY   r  s    r   c             S   s   g | ]
}|j qS r   )bfactor)rW   r6   r   r   r   rY   s  s    r   c             S   s   g | ]
}|j qS r   )altloc)rW   r6   r   r   r   rY   t  s    r   c             S   s   g | ]
}|j qS r   )tree)rW   r6   r   r   r   rY   u  s    r   c             S   s   g | ]
}|j qS r   )join)rW   r6   r   r   r   rY   v  s    r   c             S   s   g | ]
}|j qS r   )irotat)rW   r6   r   r   r   rY   w  s    r   c             S   s   g | ]
}|j qS r   )rmin)rW   r6   r   r   r   rY   x  s    r   c             S   s   g | ]
}|j qS r   )epsilon)rW   r6   r   r   r   rY   y  s    r   c             S   s   g | ]
}|j qS r   )rmin_14)rW   r6   r   r   r   rY   z  s    r   c             S   s   g | ]
}|j qS r   )
epsilon_14)rW   r6   r   r   r   rY   {  s    r   c             S   s   g | ]}|j jqS r   )r4   r   )rW   r6   r   r   r   rY   |  s    Zresnamec             S   s   g | ]}|j |j|jgqS r   )rT   rU   rV   )rW   r6   r   r   r   rY     s    rT   rU   rV   )columnsc             S   s   g | ]}|j |j|jgqS r   )vxvyvz)rW   r6   r   r   r   rY     s    r   r   r   )ZpandasZ	DataFramer#   rZ   r   )r   ZpdretZcoordsZvelsr   r   r   to_dataframe7  sJ    /

zResidueTemplate.to_dataframec             C   sd   t  }x"| D ]}|t|| jd qW x4| jD ]*}|jt|j|j	j
 |j|jj
  q2W |S )a  
        Generates a Structure instance with a single residue from this
        ResidueTemplate

        Returns
        -------
        struct : :class:`parmed.structure.Structure`
            The Structure with all of the bonds and connectivity of this
            template
        r   )r   r7   rK   rL   r   r$   r5   r	   r#   r;   rN   r<   )r   structr6   r@   r   r   r   to_structure  s    
zResidueTemplate.to_structureFc       
      K   s  ddl m} ddlm} ddddddd	}|d
k	r<| }nFtj|\}}	|	dkrdtj|d }	|	|krv||	 }ntd| |dkr|j	| |fddi| nr|dkr|j	| |fddi| nP|dkr|j	| j
| i|f| n0|dkr|  j|f||d| ntdd
S )a  
        Saves the current ResidueTemplate in the requested file format.
        Supported formats can be specified explicitly or determined by file-name
        extension. The following formats are supported, with the recognized
        suffix shown in parentheses:

            - MOL2 (.mol2)
            - MOL3 (.mol3)
            - OFF (.lib/.off)
            - PDB (.pdb)
            - PQR (.pqr)

        Parameters
        ----------
        fname : str
            Name of the file to save. If ``format`` is ``None`` (see below), the
            file type will be determined based on the filename extension. If the
            type cannot be determined, a ValueError is raised.
        format : str, optional
            The case-insensitive keyword specifying what type of file ``fname``
            should be saved as. If ``None`` (default), the file type will be
            determined from filename extension of ``fname``
        overwrite : bool, optional
            If True, allow the target file to be overwritten. Otherwise, an
            IOError is raised if the file exists. Default is False
        kwargs : keyword-arguments
            Remaining arguments are passed on to the file writing routines that
            are called by this function

        Raises
        ------
        ValueError if either filename extension or ``format`` are not recognized
        TypeError if the structure cannot be converted to the desired format for
        whatever reason
        r   )AmberOFFLibrary)Mol2FileMOL2MOL3OFFLIBPDBPQR)z.mol2z.mol3z.offz.libz.pdbz.pqrN)z.bz2z.gzrJ   z#Could not determine file type of %smol3FT)r   OFF)r   r   )rI   	overwritez,Unrecognized format for ResidueTemplate save)parmed.amber.offlibr   parmed.formats.mol2r   upperospathsplitextr3   writer   r   save)
r   fnamerI   r   kwargsr   r   extmapbaseextr   r   r   r     s4    $



zResidueTemplate.save)r"   )rC   )Nru   )ru   )T)NF)r   r   r    r!   r   r   propertyr2   r7   rB   rG   r=   classmethodrS   r\   rf   ri   rj   rl   rn   rp   rt   rz   r   r   r   r   r   r   r   r   r   r   r   (   s0   '
*
.(	
;
^
^c                   s"   e Zd ZdZd fdd	Z  ZS )r   a  
    A residue patch (typically used for CHARMM) that is used to modify existing
    residues in some way (e.g., terminal patches, disulfide bridges, etc.)

    Parameters
    ----------
    name : str, optional
        If provided, this is the name of the residue

    Attributes
    ----------
    add_bonds : list of (str, str, order)
        List of bonds that need to be added in applying the patch
    delete_atoms : list of str
        List of atom names that need to be deleted in applying the patch
    delete_impropers : list of tuple of str
        List of impropers (tuple of atom names) that need to be deleted in applying the patch

    See Also
    --------
    :class:`ResidueTemplate`

    Notes
    -----
    This class basically just provides an additional list of atoms that need to
    be deleted when applying this patch -- something that does not apply to
    standard Residues
    r"   c                s&   t t| | g | _g | _g | _d S )N)superr   r   r   r}   r   )r   r   )	__class__r   r   r     s    zPatchTemplate.__init__)r"   )r   r   r    r!   r   __classcell__r   r   )r   r   r     s   c               @   sZ   e Zd ZdZdddZedddZdd	 ZdddZdd Z	edddZ
dddZdS )r   z
    A container of ResidueTemplate objects representing a unit with multiple
    residues

    Parameters
    ----------
    name : str, optional
        The name of the residue container
    r"   c             C   s   d | _ || _d S )N)boxr   )r   r   r   r   r   r     s    z!ResidueTemplateContainer.__init__Tc             C   sH  |  }x2|j D ]&}t|}|jdkr|jdk	r|rt|jrnt|jdks`|jd dkrd|j |_n2t	|jst
|jr|jd dkrd|j |_n|jdkr.|jdk	r.|r.t|jrt|jdks|jd d	krd
|j |_n8t	|jst
|jr.|jd dkr.d|j |_|| qW |j|_|S )a<  
        Instantiates a ResidueTemplateContainer from a Structure instance filled
        with residues

        Parameters
        ----------
        struct : :class:`parmed.structure.Structure`
            The structure from which to generate the ResidueTemplateContainer
            from
        term_decorate : bool, optional
            If True, terminal amino and nucleic acid residues will be adorned as
            follows:

                * N-prepended if it is an N-terminal amino acid
                * C-prepended if it is a C-terminal amino acid
                * 5-appended if it is a 5'-terminal nucleic acid
                * 3-appended if it is a 3'-terminal nucleic acid

            For example, an N-terminal GLY will become NGLY, while a 5'-terminal
            DA will become DA5. Default is True
        Nru   r   NzN%s5z%s5rb   zC%s3z%s3)Zresiduesr   rS   r&   r'   r   Zhasr   r1   r   r   r5   r   )rQ   r   Zterm_decoraterR   resZrtr   r   r   from_structure"  s(    
z'ResidueTemplateContainer.from_structurec             C   s2   t |tr&x| D ]}|j|kr|S qW t| |S )N)rE   r8   r   r%   rt   )r   valuer   r   r   r   rt   O  s
    


 z$ResidueTemplateContainer.__getitem__ru   c             C   s2   t | dkrtdx| D ]}|j|d qW | S )a  
        Adjusts the net charge of all residues in this ResidueContainer to match
        the closest integer charge

        Parameters
        ----------
        precision : int, optional
            The number of decimal places that each charge should be rounded to.
            Default is 4

        Returns
        -------
        self : :class:`ResidueTemplateContainer`
            The current residue template container whose ResidueTemplates are
            being modified

        Notes
        -----
        This method modifies everything in-place.

        Raises
        ------
        ValueError
            If you try to call fix_charges on a container with no templates
        r   z(Cannot fix charges on an empty container)ry   )r1   r3   rz   )r   ry   r   r   r   r   rz   V  s
    
z$ResidueTemplateContainer.fix_chargesc             C   s.   t  }x"| D ]}|j|krq|||j< qW |S )a_  
        Converts the ResidueTemplateContainer instance to a library of unique
        :class:`ResidueTemplate` instances. The first of each kind of residue is
        taken

        Returns
        -------
        residues : dict {str : :class:`ResidueTemplate`}
            The residue library with all residues from this residue collection
        )r   r   )r   r   r   r   r   r   
to_libraryv  s    

 z#ResidueTemplateContainer.to_libraryFc             C   sV   |  }xJt |D ]>\}}t|ts.td| |rD|t| q|| qW |S )aT  
        Converts a dictionary of ResidueTemplate items into a
        ResidueTemplateContainer.

        Parameters
        ----------
        library : dict or OrderedDict
            The library of ResidueTemplate objects to add to this container
        copy : bool, optional
            If True, copies of each ResidueTemplate in library is added to the
            ResidueTemplateContainer. Default is False

        Returns
        -------
        cont : ResidueTemplateContainer
            A ResidueTemplateContainer containing all of the residues defined in
            ``library``

        Notes
        -----
        If the library is ordered, that order is maintained

        Raises
        ------
        TypeError if any of the items in the input library is not a
        ResidueTemplate instance (or an instance of a subclass)
        z$%r is not a ResidueTemplate instance)r   rE   r   r3   r5   rK   rL   )rQ   ZlibraryrL   Zcont_r   r   r   r   from_library  s    
z%ResidueTemplateContainer.from_libraryNc       	      K   s   ddl m} ddlm} ddddd}|dk	r8| }nFtj|\}}|d	kr`tj|d
 }||krr|| }ntd| |dkr|j	| |fddd| nJ|dkr|j	| |fddd| n&|dkr|j	| 
 |f| ntddS )a  
        Saves the current ResidueTemplateContainer in the requested file format.
        Supported formats can be specified explicitly or determined by file-name
        extension. The following formats are supported, with the recognized
        suffix and ``format`` keyword shown in parentheses:

            - MOL2 (.mol2)
            - MOL3 (.mol3)
            - OFF (.lib/.off)

        Parameters
        ----------
        fname : str
            Name of the file to save. If ``format`` is ``None`` (see below), the
            file type will be determined based on the filename extension. If the
            type cannot be determined, a ValueError is raised.
        format : str, optional
            The case-insensitive keyword specifying what type of file ``fname``
            should be saved as. If ``None`` (default), the file type will be
            determined from filename extension of ``fname``
        kwargs : keyword-arguments
            Remaining arguments are passed on to the file writing routines that
            are called by this function

        Raises
        ------
        ValueError if either filename extension or ``format`` are not recognized
        TypeError if the structure cannot be converted to the desired format for
        whatever reason

        Notes
        -----
        Mol2 and Mol3 files are saved as concatenated multiple @<MOLECULE>s. By
        contrast, ``Structure.save`` will save a single @<MOLECULE> mol2 file
        with multiple residues if the mol2 format is requested.
        r   )r   )r   r   r   r   )z.mol2z.mol3z.offz.libN)z.bz2z.gzrJ   z#Could not determine file type of %sFT)r   split)r   r   z,Unrecognized format for ResidueTemplate save)r   r   r   r   r   r   r   r   r3   r   r   )	r   r   rI   r   r   r   r   r   r   r   r   r   r     s*    %

zResidueTemplateContainer.save)r"   )T)ru   )F)N)r   r   r    r!   r   r   r   rt   rz   r   r   r   r   r   r   r   r     s   	
,
 &)%r!   collectionsr   r   rL   rK   Znumpyr[   r   Zparmed.residuer   r   r   Zparmed.structurer   Zparmed.topologyobjectsr   r	   r
   r   Zparmed.utils.sixr   Zparmed.exceptionsr   r   rO   __all__objectr   r   r   r   r   r   r   r%   r   r   r   r   r   <module>   s2   
     L%