
     iPX                     v    d Z ddlmZ ddlZddlZddlZddlZddlm	Z	 ddl
mZ ddlmZ  G d	 d
e          ZdS )a*  
Default Guesser
================
.. _DefaultGuesser:

DefaultGuesser is a generic guesser class that has basic guessing methods.
This class is a general purpose guesser that can be used with most topologies,
but being generic makes it the least accurate among all guessers.


Guessing behavior
-----------------

This section describes how each attribute is guessed by the DefaultGuesser.

Masses
~~~~~~

We first attempt to look up the mass of an atom based on its element if the
element TopologyAttr is available. If not, we attempt to lookup the mass based
on the atom type (``type``) TopologyAttr. If neither of these is available, we
attempt to guess the atom type based on the atom name (``name``) and then
lookup the mass based on the guessed atom type.


Types
~~~~~

We attempt to guess the atom type based on the atom name (``name``).
The name is first stripped of any numbers and symbols, and then looked up in
the :data:`MDAnalysis.guesser.tables.atomelements` table. If the name is not
found, we continue checking variations of the name following the logic in
:meth:`DefaultGuesser.guess_atom_element`. Ultimately, if no match is found,
the first character of the stripped name is returned.

Elements
~~~~~~~~

This follows the same method as guessing atom types.


Bonds
~~~~~

Bonds are guessed based on the distance between atoms.
See :meth:`DefaultGuesser.guess_bonds` for more details.

Angles
~~~~~~

Angles are guessed based on the bonds between atoms.
See :meth:`DefaultGuesser.guess_angles` for more details.

Dihedrals
~~~~~~~~~

Dihedrals are guessed based on the angles between atoms.
See :meth:`DefaultGuesser.guess_dihedrals` for more details.

Improper Dihedrals
~~~~~~~~~~~~~~~~~~

Improper dihedrals are guessed based on the angles between atoms.
See :meth:`DefaultGuesser.guess_improper_dihedrals` for more details.

Aromaticities
~~~~~~~~~~~~~

Aromaticity is guessed using RDKit's GetIsAromatic method.
See :meth:`DefaultGuesser.guess_aromaticities` for more details.




Classes
-------

.. autoclass:: DefaultGuesser
   :members:
   :inherited-members:

   )GuesserBase    N   )NoDataError)	distances)tablesc                        e Zd ZdZdZ	 	 	 	 d fd	ZddZd Zd	 Zdd
Z	d Z
ddZddZddZddZd ZddZd Z xZS )DefaultGuessera_  
    This guesser holds generic methods (not directed to specific contexts) for
    guessing different topology attribute. It has the same methods which where
    originally found in Topology.guesser.py. The attributes that can be
    guessed by this class are:
     - masses
     - types
     - elements
     - angles
     - dihedrals
     - bonds
     - improper dihedrals
     - aromaticities

    You can use this guesser either directly through an instance, or through
    the :meth:`~MDAnalysis.core.universe.Universe.guess_TopologyAttrs` method.

    Parameters
    ----------
    universe : Universe
        The Universe to apply the guesser on
    box : np.ndarray, optional
        The box of the Universe. This is used for bond guessing.
    vdwradii : dict, optional
        Dict relating atom types: vdw radii. This is used for bond guessing
    fudge_factor : float, optional
        The factor by which atoms must overlap each other to be considered
        a bond.  Larger values will increase the number of bonds found. [0.55]
    lower_bound : float, optional
        The minimum bond length. All bonds found shorter than this length
        will be ignored. This is useful for parsing PDB with altloc records
        where atoms with altloc A and B may be very close together and
        there should be no chemical bond between them. [0.1]

    Examples
    --------
    to guess bonds for a universe::

        import MDAnalysis as mda
        from MDAnalysisTests.datafiles import two_water_gro

        u = mda.Universe(two_water_gro, context='default', to_guess=['bonds'])

    .. versionadded:: 2.8.0

    defaultN皙?皙?c           	           t                      j        |f||||d| | j        | j        | j        | j        | j        | j        | j        | j        d| _	        d S )N)boxvdwradiifudge_factorlower_bound)massestypeselementsbondsangles	dihedrals	impropersaromaticities)
super__init__guess_massesguess_typesguess_bondsguess_anglesguess_dihedralsguess_improper_dihedralsguess_aromaticities_guesser_methods)selfuniverser   r   r   r   kwargs	__class__s          l/srv/www/vhosts/g4struct/public_html/venv/lib/python3.11/site-packages/MDAnalysis/guesser/default_guesser.pyr   zDefaultGuesser.__init__   s     		
%#	
 	
 	
 	
 	
 '%(%'-6!5	!
 	!
    c                     |	  j         j        j        }nx# t          $ rk 	  j         j        j        }nU# t          $ rH 	                       j         j        j                  }n# t          $ r t          d          dw xY wY nw xY wY nw xY w|||         }t          j         fd|D             t          j	                  }|S )a	  Guess the mass of many atoms based upon their type.
        For guessing masses through :meth:`~MDAnalysis.core.universe.Universe.guess_TopologyAttrs`:

        First try to guess masses from atom elements, if not available,
        try to guess masses from types and if not available, try to guess
        types.

        Parameters
        ----------
        atom_types : Optional[np.ndarray]
          Atom types/elements to guess masses from
        indices_to_guess : Optional[np.ndarray]
          Mask array for partially guess masses for certain atoms

        Returns
        -------
        atom_masses : np.ndarray dtype float64

        Raises
        ------
        :exc:`ValueError`
            If there are no atom types or elements to guess mass from.

        N
atom_typesz`there is no reference attributes (elements, types, or names) in this universe to guess mass fromc                 :    g | ]}                     |          S  )get_atom_mass.0atomr%   s     r)   
<listcomp>z/DefaultGuesser.guess_masses.<locals>.<listcomp>   s'    ===$T%%===r*   dtype)
	_universeatomsr   r   r   r   namesnparrayfloat64)r%   r-   indices_to_guessr   s   `   r)   r   zDefaultGuesser.guess_masses   s#   2 $!^1:

 $ $ $$!%!5!;JJ" 
$ 
$ 
$	$%)%5%5'+~';'A &6 & &

 ' $ $ $)C   $	$$ #

$$ '#$45J====*===RZ
 
 
 sF    
B4B
B%A%$B%B  BBBBBc                     	 t           j        |         S # t          $ rT 	 t           j        |                                         cY S # t          $ r t	          j        dt                     Y Y dS w xY ww xY w)zReturn the atomic mass in u for *element*.
        Masses are looked up in :data:`MDAnalysis.guesser.tables.masses`.

        .. Warning:: Until version 3.0.0 unknown masses are set to 0.0

        zUnknown masses are set to 0.0 for current version, this will be deprecated in version 3.0.0 and replaced by Masse's no_value_label (np.nan)        )r   r   KeyErrorupperwarningswarnPendingDeprecationWarning)r%   elements     r)   r0   zDefaultGuesser.get_atom_mass   s    	=)) 
	 
	 
		}W]]__5555   7 .	   sss
	s,    
A2#AA2$A.)A2-A..A2c                 R    |                      |                     |                    S )a  Guess a mass based on the atom name.

        :func:`guess_atom_element` is used to determine the kind of atom.

        .. warning:: Until version 3.0.0 anything not recognized is simply
           set to 0.0; if you rely on the masses you might want to double-check.
        )r0   guess_atom_element)r%   atomnames     r)   guess_atom_masszDefaultGuesser.guess_atom_mass  s&     !!$"9"9("C"CDDDr*   c                      |1	  j         j        j        }n# t          $ r t          d          dw xY w|||         }t	          j         fd|D             t                    S )a  Guess the atom type of many atoms based on atom name

        Parameters
        ----------
        atom_types (optional)
          atoms names if types guessing is desired to be from names
        indices_to_guess (optional)
          Mask array for partially guess types for certain atoms

        Returns
        -------
        atom_types : np.ndarray dtype object

        Raises
        ------
        :exc:`ValueError`
           If there is no names to guess types from.

        NzEthere is no reference attributes in this universe to guess types fromc                 :    g | ]}                     |          S r/   )rG   r1   s     r)   r4   z.DefaultGuesser.guess_types.<locals>.<listcomp>6  s'    BBBtT$$T**BBBr*   r5   )r7   r8   r9   r   r:   r;   object)r%   r-   r=   s   `  r)   r   zDefaultGuesser.guess_types  s    ( !^17

   !*   '#$45JxBBBBzBBB
 
 
 	
s    2c                    t          j        d          }t          j        d          }|dk    rdS 	 t          j        |                                         S # t
          $ r t          j        |d|          }t          j        |d|                                          }|t          j        v rt          j        |         cY S |r|t          j        v r|cY S |dd         t          j        v r|dd         cY S |dd         t          j        v r|dd         cY S t          |          dk    r
|d         cY S |dd         }||cY S w xY w)	a  Guess the element of the atom from the name.

        First all numbers and symbols are stripped from the name.
        Then the name is looked up in the
        :data:`MDAnalysis.guesser.tables.atomelements` table.
        If the name is not found, we remove the last character or
        first character from the name and check the table for both,
        with a preference for removing the last character. If the name is
        still not found, we iteratively continue to remove the last character
        or first character until we find a match. If ultimately no match
        is found, the first character of the stripped name is returned.

        If the input name is an empty string, an empty string is returned.

        The table comes from CHARMM and AMBER atom
        types, where the first character is not sufficient to determine the
        atom type. Some GROMOS ions have also been added.

        .. Warning: The translation table is incomplete.
           This will probably result in some mistakes,
           but it still better than nothing!

        See Also
        --------
        :func:`guess_atom_type`
        :mod:`MDAnalysis.guesser.tables`
        z[0-9]z[*+-] Nr   r   r   )	recompiler   atomelementsrA   r@   subr   len)r%   rH   NUMBERSSYMBOLS
no_symbolsnames         r)   rG   z!DefaultGuesser.guess_atom_element:  sl   8 *X&&*X&&r>>2	&x~~'7'788 	 	 	X66J6'2z2288::D v****40000 	!6?**KKK9//9$$$8v..8OOOt99>>7NNNCRCy  	! +	s0   #A A(E E E6 EE5EEc           	         || j         j        }|| j         j        j        }t          |          t          |          k    rt	          d          | j                            dd          }t          j        	                                | j                            dd          }|r
                    |           t          |d          r|j        }n|                     |j                  }t          fdt!          |          D                       sDt	          d	d
                    fdt!          |          D                       z   dz   dz             | j                            dd          }| j                            dd          }|t%          j        |          }t)          fd|D                       }g }	t+          j        |d|z  ||          \  }
}t/          |
          D ]a\  }\  }}||                  ||                  z   |z  }||         |k     r-|	                    ||         j        ||         j        f           bt5          |	          S )a  Guess if bonds exist between two atoms based on their distance.

        Bond between two atoms is created, if the two atoms are within

        .. math::

              d < f \cdot (R_1 + R_2)

        of each other, where :math:`R_1` and :math:`R_2` are the VdW radii
        of the atoms and :math:`f` is an ad-hoc *fudge_factor*. This is
        the `same algorithm that VMD uses`_.

        Parameters
        ----------
        atoms: AtomGroup
            atoms for which bonds should be guessed
        coords: np.ndarray, optional
            coordinates of the atoms. If not provided, the coordinates
            of the ``atoms`` in the universe are used.

        Returns
        -------
        list
            List of tuples suitable for use in Universe topology building.

        Warnings
        --------
        No check is done after the bonds are guessed to see if Lewis
        structure is correct. This is wrong and will burn somebody.

        Raises
        ------
        :exc:`ValueError`
           If inputs are malformed or `vdwradii` data is missing.


        .. _`same algorithm that VMD uses`:
           http://www.ks.uiuc.edu/Research/vmd/vmd-1.9.1/ug/node26.html

        Nz+'atoms' and 'coord' must be the same lengthr   r   r   r   r,   c              3       K   | ]}|v V  	d S Nr/   )r2   valr   s     r)   	<genexpr>z-DefaultGuesser.guess_bonds.<locals>.<genexpr>  s'      ==s3(?======r*   zvdw radii for types: z, c                     g | ]}|v|	S r/   r/   r2   tr   s     r)   r4   z.DefaultGuesser.guess_bonds.<locals>.<listcomp>  s#    HHHqax6G6G6G6G6Gr*   z). These can be defined manually using thez keyword 'vdwradii'r   r   r   c                      g | ]
}|         S r/   r/   r_   s     r)   r4   z.DefaultGuesser.guess_bonds.<locals>.<listcomp>  s    666qx{666r*   g       @)
max_cutoff
min_cutoffr   )r7   r8   	positionsrT   
ValueError_kwargsgetr   r   copyupdatehasattrr   r   r9   allsetjoinr:   asarraymaxr   self_capped_distance	enumerateappendindextuple)r%   r8   coordsr   user_vdwradii	atomtypesr   r   max_vdwr   pairsdistidxijdr   s                   @r)   r   zDefaultGuesser.guess_bondss  s   R =N(E>^)3Fu::V$$JKKK|''== ?''))((T:: 	+OOM*** 5'"" 	AII((EK(@@I ====c)nn===== 
	+iiHHHHC	NNHHH  B	B
 --	 	 	 l&&}c::lud++?*S//C
 6666I666774sW}#
 
 
t %U++ 	? 	?KC!Q1&)A,)??A Cy1}}eAhneAhn=>>>U||r*   c                    ddl m} t                      }|t          | j        j        d          r| j        j        j        }n{|                    t          | j        j                            }|	                    | 
                    | j        j        | j        j        j                             |j        j        }|D ]}|D ]}|                    |          }|j        D ]p}||k    rh|                    |          }	t          |j        |j        |	j        g          }
|
d         |
d         k    r|
ddd         }
|                    |
           qt          |          S )a  Given a list of Bonds, find all angles that exist between atoms.

        Works by assuming that if atoms 1 & 2 are bonded, and 2 & 3 are bonded,
        then (1,2,3) must be an angle.

        Parameters
        ----------
        bonds : Bonds
             from which angles should be guessed

        Returns
        -------
        list of tuples
            List of tuples defining the angles.
            Suitable for use in u._topology


        See Also
        --------
        :meth:`guess_bonds`

        r   UniverseNr   n_atomsr   rO   )core.universer   rl   rj   r7   r8   r   emptyrT   	add_bondsr   rd   partnerrt   rs   add)r%   r   r   angles_foundtemp_ubr3   other_aother_bthird_adescs              r)   r    zDefaultGuesser.guess_angles  s}   . 	-,,,,,uu=t~+W55 	+,2!DN4H0I0IJJ  $$,dn.B.L   
 * 	/ 	/A / /))D//#z 	/ 	/G!||")//$"7"7$$]DJF     7T"X--#'":D$((...	// \"""r*   c                    ddl m} |t          | j        j        d          r| j        j        j        }n|                    t          | j        j                            }|                    | 	                    | j        j        | j        j        j
                             |                    |                     |j        j                             |j        j        }t                      }|D ]}t          d |D                       }t!          |j        d         |j        d         g|ddd         |g          D ]x\  }}|j        D ]k}	|	                    |          |vrR|	                    |          }
||
j        fz   }|d         |d         k    r|ddd         }|                    |           lyt          |          S )	a  Given a list of Angles, find all dihedrals that exist between atoms.

        Works by assuming that if (1,2,3) is an angle, and 3 & 4 are bonded,
        then (1,2,3,4) must be a dihedral.

        Parameters
        ----------
        angles : Angles
             from which dihedrals should be guessed

        Returns
        -------
        list of tuples
            List of tuples defining the dihedrals.
            Suitable for use in u._topology

        r   r   Nr   r   c                     g | ]	}|j         
S r/   rs   )r2   as     r)   r4   z2DefaultGuesser.guess_dihedrals.<locals>.<listcomp>9  s    ...q17...r*   r   rO   )r   r   rj   r7   r8   r   r   rT   r   r   rd   
add_anglesr    r   rl   rt   zipr   rs   r   )r%   r   r   r   dihedrals_foundr   a_tupr3   prefixr   r   r   s               r)   r!   zDefaultGuesser.guess_dihedrals  s   $ 	-,,,,,>t~+X66 --4 "DN4H0I0IJJ  $$,dn.B.L    !!$"3"3FL4F"G"GHHH,%% 	2 	2A..A...//E !$QWR[)E$$B$K+?! ! 	2 	2f  $z 2 2G"??400A55")//$"7"7%(887T"X--#'":D'++D1112	2 _%%%r*   c                   
 ddl m} |t          | j        j        d          r| j        j        j        }n|                    t          | j        j                            }|                    | 	                    | j        j        | j        j        j
                             |                    |                     |j        j                             |j        j        }t                      }|D ]

d         }t          
fddD                       }|j        D ]X}|                    |          }|
vr=||j        fz   }	|	d	         |	d
         k    r|	ddd
         }	|                    |	           Yt          |          S )a  Given a list of Angles, find all improper dihedrals
        that exist between atoms.

        Works by assuming that if (1,2,3) is an angle, and 2 & 4 are bonded,
        then (2, 1, 3, 4) must be an improper dihedral.
        ie the improper dihedral is the angle between the planes formed by
        (1, 2, 3) and (1, 3, 4)

        Returns
        -------
            List of tuples defining the improper dihedrals.
            Suitable for use in u._topology

        r   r   Nr   r   r   c                 *    g | ]}|         j         S r/   r   )r2   r   r   s     r)   r4   z;DefaultGuesser.guess_improper_dihedrals.<locals>.<listcomp>q  s    999!1Q4:999r*   )r   r   r   r   rO   )r   r   rj   r7   r8   r   r   rT   r   r   rd   r   r    r   rl   rt   r   rs   r   )r%   r   r   r   r   r3   r   r   
other_atomr   r   s             @r)   r"   z'DefaultGuesser.guess_improper_dihedralsI  s     	-,,,,,>t~+X66 --4 "DN4H0I0IJJ  $$,dn.B.L    !!$"3"3FL4F"G"GHHH,%% 	. 	.AQ4D9999y999::E  : . .$__T22
Q&& J$4#66DAwb))#DDbDz#''---. _%%%r*   c                     dS )zbGuess atom charge from the name.

        .. Warning:: Not implemented; simply returns 0.
        r?   r/   )r%   r8   s     r)   guess_atom_chargez DefaultGuesser.guess_atom_charge  s	     sr*   c                     || j         j        }|                    d          }t          j        d |                                D                       S )zGuess aromaticity of atoms using RDKit

        Returns
        -------
        aromaticities : numpy.ndarray
            Array of boolean values for the aromaticity of each atom

        NRDKITc                 6    g | ]}|                                 S r/   )GetIsAromaticr2   r3   s     r)   r4   z6DefaultGuesser.guess_aromaticities.<locals>.<listcomp>  s$    III$++--IIIr*   )r7   r8   
convert_tor:   r;   GetAtoms)r%   	atomgroupmols      r)   r#   z"DefaultGuesser.guess_aromaticities  sN     ,I""7++xII#,,..IIIJJJr*   c                     |                     d          }ddlm}  ||d           t          j        d |                                D             t          j                  S )aJ  Guess Gasteiger partial charges using RDKit

        Parameters
        ----------
        atomgroup : mda.core.groups.AtomGroup
            Atoms for which the charges will be guessed

        Returns
        -------
        charges : numpy.ndarray
            Array of float values representing the charge of each atom

        r   r   )ComputeGasteigerChargesT)throwOnParamFailurec                 8    g | ]}|                     d           S )_GasteigerCharge)GetDoublePropr   s     r)   r4   z:DefaultGuesser.guess_gasteiger_charges.<locals>.<listcomp>  s7        ""#566  r*   r5   )r   rdkit.Chem.rdPartialChargesr   r:   r;   r   float32)r%   r   r   r   s       r)   guess_gasteiger_chargesz&DefaultGuesser.guess_gasteiger_charges  s     ""7++GGGGGG>>>>x LLNN   *
 
 
 	
r*   )NNr   r   )NNr[   )__name__
__module____qualname____doc__contextr   r   r0   rI   r   rG   r   r    r!   r"   r   r#   r   __classcell__)r(   s   @r)   r
   r
   u   sF       - -^ G
 
 
 
 
 
 
81 1 1 1f  *E E E#
 #
 #
 #
J7 7 7re e e eN5# 5# 5# 5#n6& 6& 6& 6&p4& 4& 4& 4&l  K K K K
 
 
 
 
 
 
r*   r
   )r   baser   numpyr:   rB   mathrP   
exceptionsr   libr   rN   r   r
   r/   r*   r)   <module>r      s   .Q Qd             				 $ $ $ $ $ $            z
 z
 z
 z
 z
[ z
 z
 z
 z
 z
r*   