B
    by                 @   s  d Z ddlmZ ddlmZ ddlmZmZmZm	Z	m
Z
mZmZmZmZmZmZmZmZ ddlmZmZmZ ddlmZmZ ddlmZ dd	lmZ dd
lmZm Z  ddlm!Z! ddl"Z"ddl#Z#dd Z$G dd de%Z&G dd de'Z(e")da*G dd deZ+dd Z,dd Z-dS )z
Provides a Python class for parsing a PSF file and setting up a system
structure for it

Author: Jason M. Swails
Contributors:
Date: April 20, 2014
    )division)copy   )BondAngleDihedralImproperAcceptorDonorGroupCmapUreyBradleyNoUreyBradleyAtomDihedralTypeImproperTypeUnassignedAtomType)CharmmErrorCharmmWarningParameterError)needs_openmm	Structure)genopen)wraps)ziprange)string_typesNc                s   t   fdd}|S )zt
    Protects a function from raising an index error, and replace that exception
    with a CharmmError instead
    c           
      s>   y
 | |S  t k
r8 } ztd| W dd}~X Y nX dS )z Catch the index error zArray is too short: %sN)
IndexErrorr   )argskwargse)func 0lib/python3.7/site-packages/parmed/charmm/psf.pynewfunc   s    
z!_catchindexerror.<locals>.newfunc)r   )r    r#   r!   )r    r"   _catchindexerror   s    r$   c               @   s   e Zd ZdZdS )_FileEOFz For control flow N)__name__
__module____qualname____doc__r!   r!   r!   r"   r%   '   s   r%   c               @   s   e Zd ZdZdd ZdS )	_ZeroDicta1  
    Contains a dict that returns dummy (zero) arguments when a key is not
    present rather than raising a KeyError.  The return value for non-existent
    items is (0, []). It also special-case sections that have multiple pointers
    to avoid index errors if those are not present in the PSF file
    c             C   s   yt | |S  tk
r   |drVx"| D ]}|dr,t | |S q,W ddgg fS |drx"| D ]}|drft | |S qfW ddgg fS dg fS X d S )NZNGRPr   ZNUMLP)dict__getitem__KeyError
startswith)selfkeykr!   r!   r"   r,   1   s    





z_ZeroDict.__getitem__N)r&   r'   r(   r)   r,   r!   r!   r!   r"   r*   *   s   r*   z(-?\d+)([a-zA-Z]*)c                   s   e Zd ZdZedd Zedd Zedd Ze	dd	d
Z
edddZdd Zed fdd	ZdddZdd Z  ZS )CharmmPsfFilea4  
    A chemical :class:`Structure` instantiated from CHARMM files.

    Parameters
    ----------
    psf_name : str, optional
        Name of the PSF file (it must exist)

    Raises
    ------
    IOError : If file ``psf_name`` does not exist
    CharmmPsfError : If any parsing errors are encountered
    c             C   s2   y|| S  t k
r,   td|| f Y nX dS )a  
        Converts a string to a specific type, making sure to raise
        CharmmError with the given message in the event of a failure.

        Parameters
        ----------
        string : str
            Input string to process
        type : type
            Type of data to convert to (e.g., ``int``)
        message : str
            Error message to put in exception if failed
        zCould not convert %s [%s]N)
ValueErrorr   )stringtypemessager!   r!   r"   _convertS   s    zCharmmPsfFile._convertc                s   |d | d  }|| dd d    }d|krN|d | d }t|dkrn |d td}nt fdd|D }||fS )N!   :r   pointerc                s   g | ]}  |td qS )r;   )r7   int).0w)clsr!   r"   
<listcomp>s   s    z7CharmmPsfFile._parse_psf_title_line.<locals>.<listcomp>)indexsplitstripupperlenr7   r<   tuple)r?   linewordstitlepointersr!   )r?   r"   _parse_psf_title_linei   s    z#CharmmPsfFile._parse_psf_title_linec                s
  |  }x"| s*|s tdq
|  }q
W d|krD |\}}ntd|   }|s|dr|   }d|kr |\}}|   }g }|dks|dkrxV|r|| |   }qW n6x4|r| }| fdd|D  |   }qW |||fS )	a  
        This method parses a section of the PSF file

        Parameters
        ----------
        psf : file
            Open file that is pointing to the first line of the section that is
            to be parsed

        Returns
        -------
        title : str
            The label of the PSF section we are parsing
        pointers : (int/tuple of ints)
            If one pointer is set, pointers is simply the integer that is value
            of that pointer. Otherwise it is a tuple with every pointer value
            defined in the first line
        data : list
            A list of all data in the parsed section converted to integers
        zUnexpected EOF in PSF filer8   z!Could not determine section titleZNNBNATOMNTITLEc                s   g | ]}  |td qS )zPSF data)r7   r<   )r=   r>   )r?   r!   r"   r@      s    z4CharmmPsfFile._parse_psf_section.<locals>.<listcomp>)	readlinerC   r%   rK   r   r.   appendrB   extend)r?   psfrG   rI   rJ   datarH   r!   )r?   r"   _parse_psf_sectionx   s0    


z CharmmPsfFile._parse_psf_sectionNc       .      C   s~  t |  |dkrdS t|tr0t|d}d}n|}d}z.t|trJ|nd| _| }|dsrtd|	  |
 dd }|  t }x<yt|\}}}	W n tk
r   P Y nX ||	f||< qW |d	 d | _| |d
 d td}
x&t|
D ]}|d
 d | 
 }t|d }||d kr:td|d }t|d }|sftd|d  | \}}| |td}|d }|d }|d }yt|}W n tk
r   Y nX | |d td}| |d td}|dd }t||||d}||_| j||||||d qW | |d d td}t|d d |d krhtdt|d d |f t|d d }x>t||D ]0\}}| jt | j!|d  | j!|d   qW | |d d td}t|d d |d krtd t|d d |f t|d d }xZt|||D ]J\}}}| j"t#| j!|d  | j!|d  | j!|d   d| j"d! _$q"W | |d" d td#}t|d" d |d krtd$t|d" |f t|d" d }x^t||||D ]L\}}}}| j%t&| j!|d  | j!|d  | j!|d  | j!|d   qW d| j%_
| |d% d td&} t|d% d | d kr|td't|d% d | f t|d% d }x^t||||D ]L\}}}}| j't(| j!|d  | j!|d  | j!|d  | j!|d   qW | |d( d td)}!t|d( d |!d kr8td*t|d( d |!f t|d( d }x>t||D ]0\}}| j)t*| j!|d  | j!|d   qTW | |d+ d td,}"t|d+ d |"d krtd-t|d+ d |"f t|d+ d }x>t||D ]0\}}| j+t*| j!|d  | j!|d   qW y|d. d \}#}$W n tk
rV   td/Y nX |d. d }%|$| j_,t|d. d |#d krtd0t|%|#f t|d. d }x6t|||D ]&\}}}| jt-| j!| || qW |d1 d }%t.| j! d2d3 | j!D }&t|%t| j!kr^|&|%kr2t/0d4t1 |d5 d \}'}(|'dksV|(dkr^t2d6| |d7 d td8})t|d7 d |)d krtd9t|d7 |)f t|d7 d }xt||||||||D ]\}}}}}*}+},}-| j3t45| j!|d  | j!|d  | j!|d  | j!|d  | j!|*d  | j!|+d  | j!|,d  | j!|-d   qW | 6  || _7W d|rx|8  X dS ):zp
        Opens and parses a PSF file, then instantiates a CharmmPsfFile
        instance from the data.
        NrTF ZPSFz'Unrecognized PSF file. First line is %sr9   rM   rL   r   natomzNonsequential atoms detected!r   z%Could not interpret residue number %szresidue number            zpartial charge   zatomic mass   )namer5   chargemass)chaininscodesegidZNBONDznumber of bondszGot %d indexes for %d bondsZNTHETAznumber of angleszGot %d indexes for %d anglesZNPHIznumber of torsionszGot %d indexes for %d torsionsZNIMPHIznumber of improperszGot %d indexes for %d impropersZNDONznumber of donorszGot %d indexes for %d donorsZNACCznumber of acceptorszGot %d indexes for %d acceptorsz	NGRP NST2zCould not unpack GROUP pointerszGot %d indexes for %d groupsZMOLNTc             S   s   g | ]
}|j qS r!   )marked)r=   ar!   r!   r"   r@   E  s    z*CharmmPsfFile.__init__.<locals>.<listcomp>zDDetected PSF molecule section that is WRONG. Resetting molecularity.zNUMLP NUMLPHzQCannot currently handle PSFs with lone pairs defined in the NUMLP/NUMLPH section.ZNCRTERMzNumber of cross-termsz%Got %d CMAP indexes for %d cmap terms)9r   __init__
isinstancer   r   r]   rN   r.   r   rC   rB   r*   r2   rS   r%   rI   r7   r<   r   _resrematchgroupsr3   floatr   propsZadd_atomrE   iterr   bondsrO   r   atomsanglesr   Zfunct	dihedralsr   	impropersr   donorsr	   	acceptorsnst2r
   set_moleculeswarningswarnr   NotImplementedErrorcmapsr   ZextendedZunchangeflagsclose).r/   Zpsf_nameZfileobjZ
own_handlerG   Z	psf_flagsZpsfsectionsZsecZptrrR   rV   irH   Zatidrb   ZrematchZresidra   Zresnamer]   Zattyper^   r_   rl   atomZnbonditjZnthetar1   ZnphilZnimphiZndonZnaccZngrpru   tmpZmolecule_listZnumlpZnumlphZncrtermmnopr!   r!   r"   rf      s   



,,$$&& 

,$
zCharmmPsfFile.__init__Fc             C   s  ddl m} |jsF|jsF|jsF|jsF|jsF|jsF|jsF|j	sF|j
dkrNtd|rZt|}|  }|j|_|j|_|j|_|j|_|j|_|j|_|j|_|j|_|j|_|j|_|j|_|j|_|j|_|j|_|j|_|j|_|j|_x4|jD ]*}||j|_|jt k	r||jj!|j_!qW |jsx^|jD ]T}t"dd |D }|dk rh|j#t$|d dd n|j#t$|d d	d q.W d|j_%|S )
ay  
        Instantiates a CharmmPsfFile from an input Structure instance. This
        method makes sure all atom types have uppercase-only names

        Parameters
        ----------
        struct : :class:`parmed.structure.Structure`
            The input structure to convert to a CharmmPsfFile instance
        copy : bool, optional
            If True, a copy of all items are made. Otherwise, the resulting
            CharmmPsfFile is a shallow copy

        Returns
        -------
        psf : :class:`CharmmPsfFile`
            CHARMM PSF file

        Raises
        ------
        ValueError if the functional form is not recognized or cannot be
        implemented through the PSF and parameter/stream files

        Notes
        -----
        If copy is False, the original object may have its atom type names
        changed if any of them have lower-case letters
        r   )	_typeconvrW   z*Unsupported functional form for CHARMM PSFc             s   s   | ]}|j V  qd S )N)r^   )r=   re   r!   r!   r"   	<genexpr>  s    z/CharmmPsfFile.from_structure.<locals>.<genexpr>g-C6?r9   r   )&Zparmed.charmm.parametersr   Zrb_torsionsZtrigonal_anglesZout_of_plane_bendsZpi_torsionsZstretch_bendsZtorsion_torsionsZchiral_framesZmultipole_framesZnrexclr3   _copyro   Zresiduesrn   rp   urey_bradleysrq   rr   rt   rs   rj   rz   
bond_typesangle_typesurey_bradley_typesdihedral_typesimproper_types
cmap_typesr5   	atom_typer   r]   sumrO   r
   ru   )r?   Zstructr   ZtypeconvrQ   r~   ZresidueZchgr!   r!   r"   from_structuree  sN    


zCharmmPsfFile.from_structurec             C   s   | j S )N)r]   )r/   r!   r!   r"   __str__  s    zCharmmPsfFile.__str__c                s$   |dk	r|  | tt| j||S )a  
        Creates an OpenMM System object from the CHARMM PSF file. This is a
        shortcut for calling `load_parameters` followed by
        Structure.createSystem. If params is not None, `load_parameters` will be
        called on that parameter set, and Structure.createSystem will be called
        with the remaining args and kwargs

        Parameters
        ----------
        params : CharmmParameterSet=None
            If not None, this parameter set will be loaded

        See Also
        --------
        :meth:`parmed.structure.Structure.createSystem`
            In addition to `params`, this method also takes all arguments for
            :meth:`parmed.structure.Structure.createSystem`
        N)load_parameterssuperr2   createSystem)r/   Zparamsr   r   )	__class__r!   r"   r     s     
zCharmmPsfFile.createSystemTc          
   C   s(  |rt |}|j| _x~| jD ]t}y.t|jtr<|j|j }n|j|j  }W n" t	k
rp   t
d|j Y nX ||_t|j |_|j|_qW xl| jD ]b}t|jj|jjt|jj|jjf}y|j| |_W n  t	k
r   t
d| Y nX d|j_qW | jdd= x>| jD ]4}|jjr(qd|j_| j|j | j|j_qW | jdd= x| jD ]}t|jj|jj|jjt|jj|jjf}yN|j| |_d|j_|j| }|tk	rt|j|j|}	| j|	 d|_W n" t	k
r    t
d| Y nX qbW | jdd= | jdd= x>| jD ]4}	|	jjr:q(d|	j_| j|	j | j|	j_q(W x>| jD ]4}|jjrzqhd|j_| j|j | j|j_qhW t }
x| jD ]}|j|j|j|j f\}}}}|j|j|j|jf}||j!krd|j|jdf}||j!krt
d| |j!| |_d|j_|jj"|j j"f}|j|j j#ksZ|j|j j$krbd|_%n2||
krtd|_%n |
&| |
&|j j"|jj"f qW | j!dd= x>| jD ]4}|jjrqd|j_| j!|j | j!|j_qW xj| j'D ]`}|jj|jj|jj|j jf\}}}}|(|||||_|jdkrDt
d	||||d|j_qW | j)dd= x| j'D ]z}|jjrxqfd|j_t|jt*r| j)|j | j)|j_n6t|jt+r| j!|j | j!|j_ndsft,d
qfW xpt-t.t/| j'D ]Z}t| j'| jt+r| j'0|}t1|j|j|j|j dd|jd}|2  | j| qW x| j3D ]v}|jj|jj|jj|j j|jj|jj|j j|j4jf}y|j5| |_W n" t	k
r   t
d| Y nX d|j_q^W | j5dd= x>| j3D ]4}|jjrqd|j_| j5|j | j5|j_qW dS )a)
  
        Loads parameters from a parameter set that was loaded via CHARMM RTF,
        PAR, and STR files.

        Parameters
        ----------
        parmset : :class:`CharmmParameterSet`
            List of all parameters

        copy_parameters : bool, optional, default=True
            If False, parmset will not be copied.

            WARNING:
            -------
            Not copying parmset will cause ParameterSet and Structure to share
            references to types.  If you modify the original parameter set, the
            references in Structure list_types will be silently modified.
            However, if you change any reference in the parameter set, then that
            reference will no longer be shared with structure.

            Example where the reference in ParameterSet is changed. The
            following will NOT modify the parameters in the psf::

                psf.load_parameters(parmset, copy_parameters=False)
                parmset.angle_types[('a1', 'a2', a3')] = AngleType(1, 2)

            The following WILL change the parameter in the psf because the
            reference has not been changed in ``ParameterSet``::

                psf.load_parameters(parmset, copy_parameters=False)
                a = parmset.angle_types[('a1', 'a2', 'a3')]
                a.k = 10
                a.theteq = 100

            Extra care should be taken when trying this with dihedral_types.
            Since dihedral_type is a Fourier sequence, ParameterSet stores
            DihedralType for every term in DihedralTypeList. Therefore, the
            example below will STILL modify the type in the :class:`Structure`
            list_types::

                parmset.dihedral_types[('a', 'b', 'c', 'd')][0] = DihedralType(1, 2, 3)

            This assigns a new instance of DihedralType to an existing
            DihedralTypeList that ParameterSet and Structure are tracking and
            the shared reference is NOT changed.

            Use with caution!

        Notes
        -----
        - If any dihedral or improper parameters cannot be found, I will try
          inserting wildcards (at either end for dihedrals and as the two
          central atoms in impropers) and see if that matches.  Wild-cards will
          apply ONLY if specific parameters cannot be found.

        - This method will expand the dihedrals attribute by adding a separate
          Dihedral object for each term for types that have a multi-term
          expansion

        Raises
        ------
        ParameterError if any parameters cannot be found
        zCould not find atom type for %szMissing bond type for %rFNTzMissing angle type for %rXz#No dihedral parameters found for %rz'No improper type for %s, %s, %s, and %szShould not be here)improper
ignore_endr5   zNo CMAP parameters found for %r)6r   Zcombining_rulero   rg   r5   r<   Zatom_types_intZatom_types_strrD   r-   r   r   strZatomic_numberrn   minZatom1Zatom2maxr   ZusedrO   listr   rp   Zatom3r   r   r   r   setrq   Zatom4r   idxbond_partnersZangle_partnersr   addrr   Zmatch_improper_typer   r   r   AssertionErrorreversedr   rE   popr   deleterz   Zatom5r   )r/   ZparmsetZcopy_parametersr~   ZatypeZbondr0   ZangZubtZubZactive_dih_listZdihZa1Za2Za3Za4ZpairZdihedralimpr   r}   Zcmapr!   r!   r"   r     s    @
 



 
 


 $
 
 zCharmmPsfFile.load_parametersc             C   s   | j dd= dS )zD Clear the cmap list to prevent any CMAP parameters from being used N)rz   )r/   r!   r!   r"   
clear_cmap  s    zCharmmPsfFile.clear_cmap)N)F)N)T)r&   r'   r(   r)   staticmethodr7   classmethodrK   rS   r$   rf   r   r   r   r   r   r   __classcell__r!   r!   )r   r"   r2   E   s   ; 2L
 Or2   c             C   s   ddl m}m} |tt| |  |   g }d}xLtt| D ]<}| | jsB|g}t| ||| |	  |
| |d7 }qBW |S )zN
    Correctly sets the molecularity of the system based on connectivity.
    r   )setrecursionlimitgetrecursionlimitr9   )sysr   r   r   rE   Zunmarkr   rd   
_set_ownersortrO   )ro   r   r   ownerZmolecule_numberr}   r   r!   r!   r"   rv     s    	

rv   c             C   sV   || | _ xF| | jD ]8}|j s<||j t| ||j| |j |kstdqW dS )zB Recursively sets ownership of given atom and all bonded partners zAtom in multiple molecules!N)rd   r   rO   r   r   r   )ro   Zowner_arrayZatmZmol_idZpartnerr!   r!   r"   r     s    
r   ).r)   Z
__future__r   r   r   Ztopologyobjectsr   r   r   r   r	   r
   r   r   r   r   r   r   r   
exceptionsr   r   r   Z	structurer   r   Zutils.ior   Z	utils.sixr   Zutils.six.movesr   r   r   rerw   r$   	Exceptionr%   r+   r*   compilerh   r2   rv   r   r!   r!   r!   r"   <module>   s,   <
    c(