B
    bIc                 @   s   d Z ddlmZmZmZ ddlmZ ddlmZ dZ	dZ
ddlZddlZdd	lmZmZ dd
lmZ ddlmZ ddlT G dd deZG dd deZG dd deZdd Zei ZedkrddlZddlZeeje  dS )a  
Module simtk.unit

Contains classes Unit and ScaledUnit.

This is part of the OpenMM molecular simulation toolkit originating from
Simbios, the NIH National Center for Physics-Based Simulation of
Biological Structures at Stanford, funded under the NIH Roadmap for
Medical Research, grant U54 GM072970. See https://simtk.org.

Portions copyright (c) 2012 Stanford University and the Authors.
Authors: Christopher M. Bruns
Contributors: Peter Eastman

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
    )divisionprint_functionabsolute_import)iterkeys)rangezChristopher M. Brunsz0.5N   )MyMatrixzeros)BaseDimension)BaseUnit)*c               @   s   e Zd ZdZ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i Zdd Zdd  Zd!d" Zd#d$ Zi Zd%d& Zi Zd'd( Zi Zd)d* Zd+d, Zd-d. Zd/d0 Z d1S )2Unitz0
    Physical unit such as meter or ampere.
    d   c             C   sx  i | _ i | _g | _x| D ]~\}}|dkr.qt|tr|}|j}|| j krVi | j |< || j | krrd| j | |< | j | |  |7  < q| j||f qW i | _xD| j D ]:}i | j|< x*| j | D ]}| j | | | j| |< qW qW x~| jD ]t\}}	xj| D ]^\}
}|
j}|| jkr(i | j|< |
| j| krFd| j| |
< | j| |
  |	| 7  < qW qW | j	  dS )zCreate a new Unit.

        Parameters:
         - self (Unit) The newly created Unit.
         - base_or_scaled_units (dict) Keys are BaseUnits or ScaledUnits.  Values are exponents (numbers).
        r   N)
_top_base_units_all_base_units_scaled_unitsitems
isinstancer   	dimensionappenditer_base_unitssort)selfZbase_or_scaled_unitsZbase_or_scaled_unitpowerbudimduscaled_unitZ	exponent1	base_unit	exponent2 r!   /lib/python3.7/site-packages/parmed/unit/unit.py__init__8   s:    



 
$zUnit.__init__c             C   s   t | jdkstt | jdks$ttt| j}| j| }t |dksLttt|}|| }t|j||}|}	|dkrt	|d| }	|
||	 t|di}
|
S )z
        Convenience method for creating a new simple unit from another simple unit.
        Both units must consist of a single BaseUnit.
        r   r   g      ?)lenr   AssertionErrorr   nextr   r   r   mathpowZdefine_conversion_factor_tor   )r   Zscalenamesymbolr   Zbase_unit_dictZparent_base_unitZparent_exponentZnew_base_unitZ
true_scalenew_unitr!   r!   r"   create_unitc   s    
zUnit.create_unitc             c   s`   xZt | j D ]H}d}x,t | j|  D ]}|| j| | 7 }q,W |dkr||fV  qW dS )zO
        Yields (BaseDimension, exponent) tuples comprising this unit.
        r   N)sortedr   keys)r   r   exponentr   r!   r!   r"   iter_base_dimensionsy   s    zUnit.iter_base_dimensionsc             c   sP   xJt | j D ]8}x2t | j|  D ]}| j| | }||fV  q(W qW dS )z
        Yields (BaseUnit, exponent) tuples comprising this unit, including those BaseUnits
        found within ScaledUnits.

        There might be multiple BaseUnits with the same dimension.
        N)r-   r   r.   )r   r   r   r/   r!   r!   r"   iter_all_base_units   s    zUnit.iter_all_base_unitsc             c   sP   xJt | j D ]8}x2t | j|  D ]}| j| | }||fV  q(W qW dS )zd
        Yields (BaseUnit, exponent) tuples in this Unit, excluding those within BaseUnits.
        N)r-   r   r.   )r   r   unitr/   r!   r!   r"   iter_top_base_units   s    zUnit.iter_top_base_unitsc             c   s"   x| j D ]\}}||fV  qW d S )N)r   )r   r2   r/   r!   r!   r"   iter_scaled_units   s    zUnit.iter_scaled_unitsc             c   s4   x|   D ]
}|V  q
W x|  D ]
}|V  q"W d S )N)r3   r4   )r   itemr!   r!   r"   iter_base_or_scaled_units   s    
zUnit.iter_base_or_scaled_unitsc             C   s*   d}x | j D ]\}}||j| 9 }qW |S )z
        There may be ScaleUnit components to this Unit.
        Returns conversion factor to the set of BaseUnits returned by iter_all_base_units().

        Units comprised of only BaseUnits return 1.0
        g      ?)r   factor)r   r7   r   r/   r!   r!   r"   #get_conversion_factor_to_base_units   s    z(Unit.get_conversion_factor_to_base_unitsc             C   s   t |sdS |  | kS )NF)is_unitget_name)r   otherr!   r!   r"   __eq__   s    zUnit.__eq__c             C   s   |  | S )N)r<   )r   r;   r!   r!   r"   __ne__   s    zUnit.__ne__c             C   s&   |  |std| |f| |dk S )zCompare two Units.

        Raises a TypeError if the units have different dimensions.

        Returns True if self < other, False otherwise.
        z+Unit "%s" is not compatible with Unit "%s".g      ?)is_compatible	TypeErrorconversion_factor_to)r   r;   r!   r!   r"   __lt__   s    
zUnit.__lt__c             C   s0   y| j S  tk
r   Y nX t|  | _ | j S )z6
        Compute a hash code for this object.
        )_hashAttributeErrorhashr:   )r   r!   r!   r"   __hash__   s    zUnit.__hash__c             C   s   | t |d S )a+  Divide a Unit by another object.

        Returns a composite Unit if other is another Unit.

        Returns a Quantity otherwise.  UNLESS other is a Quantity AND
        the resulting unit type is dimensionless, in which case the underlying
        value type of the Quantity is returned.
        )r(   )r   r;   r!   r!   r"   __truediv__   s    	zUnit.__truediv__c             C   sr   | t jkr(|t j|  kr2t j|  | S n
i t j| < i }x |  D ]\}}|| ||< q@W t |}|t j|  |< |S )zhRaise a Unit to a power.

        Returns a new Unit with different exponents on the BaseUnits.
        )r   
_pow_cacher6   )r   r/   resultr2   r    r+   r!   r!   r"   __pow__   s    

zUnit.__pow__c       	      C   s   i }d}x2|   D ]&\}}|d dkr,d}P |d ||< qW |si }i }xL|  D ]@\}}|j}||krz|||< |||< qR|| }||  |7  < qRW x4| D ](\}}|d dkrtd|d ||< qW t|S )z
        Returns square root of a unit.

        Raises ArithmeticError if component exponents are not even.
        This behavior can be changed if you present a reasonable real life case to me.
        T   r   Fz&Exponents in Unit.sqrt() must be even.)r6   r1   r   r   ArithmeticErrorr   )	r   Z	new_unitsZnice_and_evenr   r/   Zbase_units_by_dimensionr   r   r   r!   r!   r"   sqrt   s,    
z	Unit.sqrtc             C   s   |   S )z,Returns the human-readable name of this unit)r:   )r   r!   r!   r"   __str__  s    zUnit.__str__c             C   s.   i }x|   D ]\}}|||< qW dt| S )z
        Returns a unit name (string) for this Unit, composed of its various
        BaseUnit symbols.  e.g. 'kilogram meter**2 second**-1'
        zUnit(%s))r6   repr)r   unitsr2   r   r!   r!   r"   __repr__   s    zUnit.__repr__c             C   s   | t jkr&|t j|  kr&t j|  | S t|s>|  r:dS dS i }x|  D ]\}}|||< qLW i }x| D ]\}}|||< qnW t|t|krd}n||k}| t jkri t j| < |t j|  |< |S )zf
        Returns True if two Units share the same dimension.
        Returns False otherwise.
        TF)r   _is_compatible_cacher9   is_dimensionlessr0   r$   )r   r;   Z	self_dimsr   r/   
other_dimsrI   r!   r!   r"   r>   -  s(    


zUnit.is_compatiblec             C   sN   | t jkrt j|  S x*|  D ]\}}|dkrdt j| < dS qW dt j| < dS )zVReturns True if this Unit has no dimensions.
        Returns False otherwise.
        r   FT)r   _is_dimensionless_cacher0   )r   r   r/   r!   r!   r"   rS   K  s    



zUnit.is_dimensionlessc             C   s(  d}| |kr|S | t jkr6|t j|  kr6t j|  | S | |sDt||  9 }||  }i }xN|  D ]B\}}|j}||kr||| kr|||| | 9 }qj|||< qjW xN| D ]B\}}|j}||kr||| kr|||| |  }q|||< qW | t jkri t j| < |t j|  |< |S )a  
        Returns conversion factor for computing all of the common dimensions
        between self and other from self base units to other base units.

        The two units need not share all of the same dimensions.  In case they
        do not, the conversion factor applies only to the BaseUnits of self
        that correspond to different BaseUnits in other.

        This method requires strict compatibility between the two units.
        g      ?)r   _conversion_factor_cacher>   r%   r8   r1   r   r@   )r   r;   r7   Zcanonical_unitsr2   r   r   r!   r!   r"   r@   [  s4    

zUnit.conversion_factor_toc             C   s
   | | S )a  
        Returns a new Unit with the same dimensions as this one, expressed in a particular unit system.

        Strips off any ScaledUnits in the Unit, leaving only BaseUnits.

        Parameters
         - system: a dictionary of (BaseDimension, BaseUnit) pairs
        )express_unit)r   systemr!   r!   r"   in_unit_system  s    	zUnit.in_unit_systemc             C   s8  d}d}d}xR|   D ]F\}}|dkr|d7 }|dkr>|d7 }||j7 }|dkr|d| 7 }qW d}d}d}xX|   D ]L\}}|dk rv|d7 }|dkr|d7 }||j7 }|dkrv|d|  7 }d	}qvW d|krd}	nd|kr|rd
| }	nd| }	d|krd}
n|}
d|  kr|kr(n nd}nd|
|	f }|S )z
        Returns a unit symbol (string) for this Unit, composed of its various
        BaseUnit symbols.  e.g. 'kg m**2 s**-1'
         r   r    g      ?z**%gTg      Fz/%sz/(%s)dimensionlessz%s%s)r6   r*   )r   r*   pos	pos_countr2   r   neg	neg_countsimple_denominator
neg_string
pos_stringr!   r!   r"   
get_symbol  sF     
 


zUnit.get_symbolc             C   sZ  y| j S  tk
r   Y nX d}d}xR|  D ]F\}}|dkr.|d7 }|dkrV|d7 }||j7 }|dkr.|d| 7 }q.W d}d}d}xX|  D ]L\}}|dk r|d7 }|dkr|d7 }||j7 }|dkr|d|  7 }d	}qW d|krd}n"d|kr|rd
| }nd| }d|krd}	n|}	d|  kr:|krDn nd}
nd|	|f }
|
| _ |
S )z
        Returns a unit name (string) for this Unit, composed of its various
        BaseUnit symbols.  e.g. 'kilogram meter**2 secon**-1'.
        rZ   r   r   r   g      ?z**%gTg      Fz/%sz/(%s)r\   z%s%s)_namerC   r6   r)   )r   r]   r^   r2   r   r_   r`   ra   rb   rc   r)   r!   r!   r"   r:     sN     
 


zUnit.get_nameN)!__name__
__module____qualname____doc____array_priority__r#   r,   r0   r1   r3   r4   r6   r8   r<   r=   rA   rE   rG   Z__div__rH   rJ   rM   rN   rQ   rR   r>   rU   rS   rV   r@   rY   rd   r:   r!   r!   r!   r"   r   1   s<   +	'*-r   c               @   sd   e Zd ZdZ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S )
ScaledUnitz
    ScaledUnit is like a BaseUnit, but it is based on another Unit.

    ScaledUnit and BaseUnit are both used in the internals of Unit.  They
    should only be used during the construction of Units.
    r   c       
      C   s   || _ i }x`| D ]T\}}|j}||kr8||g||< q|| d  |7  < |  j || d |9  _ qW x(| D ]\}	}|  j |	j | 9  _ qvW || _|| _|| _|| _d S )Nr   r   )	r7   r1   r   r@   r4   
base_unitsmasterr)   r*   )
r   r7   rm   r)   r*   rl   r   r/   r   Zsbur!   r!   r"   r#     s     zScaledUnit.__init__c             c   s(   x"t | j D ]}| j| V  qW d S )N)r-   rl   r.   )r   r   r!   r!   r"   __iter__
  s    zScaledUnit.__iter__c             c   s    x| D ]\}}||fV  qW d S )Nr!   )r   r   r/   r!   r!   r"   r     s    zScaledUnit.iter_base_unitsc             c   s*   x$| D ]\}}|dkr|j |fV  qW dS )zs
        Returns a sorted tuple of (BaseDimension, exponent) pairs, describing the dimension of this unit.
        r   N)r   )r   r   r/   r!   r!   r"   r0     s    zScaledUnit.iter_base_dimensionsc             C   s   t |  }|  t|S )zr
        Returns a sorted tuple of (BaseDimension, exponent) pairs, that can be used as a dictionary key.
        )listr0   r   tuple)r   lr!   r!   r"   get_dimension_tuple  s    zScaledUnit.get_dimension_tuplec             C   s   | j S )N)r7   )r   r!   r!   r"   r8   "  s    z.ScaledUnit.get_conversion_factor_to_base_unitsc             C   s^   | |krdS i }x|   D ]\}}|||< qW t|tr>|}nt|di}| jt|| S )Ng      ?)r   r   r   r7   r@   )r   r;   r   r   r/   Zother_ur!   r!   r"   r@   %  s    
zScaledUnit.conversion_factor_toc             C   s   t | t |k S )z!Compare two ScaledUnits.
        )rD   )r   r;   r!   r!   r"   rA   2  s    zScaledUnit.__lt__c             C   s   | j S )z:Returns a string with the name of this ScaledUnit
        )r)   )r   r!   r!   r"   rN   7  s    zScaledUnit.__str__c             C   sz   d}x8|   D ],\}}t|dkr*|d7 }|d||f 7 }qW dt| j d t| j d t| j d t| j d	 S )
z	
        rZ   r   z, z%s: %dzScaledUnit(factor=z	, master=z, name=z	, symbol=))r   r$   rO   r7   strrm   r)   r*   )r   rl   r   r   r!   r!   r"   rQ   <  s    zScaledUnit.__repr__N)rf   rg   rh   ri   rj   r#   rn   r   r0   rr   r8   r@   rA   rN   rQ   r!   r!   r!   r"   rk     s   rk   c               @   s0   e Zd ZdZdd Zdd Zdd Zdd	 Zd
S )
UnitSystemz
    A complete system of units defining the *base* unit in each dimension

    Parameters
    ----------
    units: ``list``
        List of base units from which to construct the unit system
    c          
   C   sN  || _ i | _i }x:| j D ]0}x*| D ]\}}|j}||kr&|||< q&W qW || _t| jt| j ksntdt| }|	  i | _
x"tt|D ]}|| j
|| < qW tt| j }xLtt| j D ]:}	| j |	 }x*| D ]\}
}| j
|
 }|||	 |< qW qW y| | _W n4 tk
rH } ztdt| W d d }~X Y nX d S )Nz<UnitSystem must have same number of units as base dimensionsz&UnitSystem is not a valid basis set.  )rP   _unit_conversion_cacher   r   rl   r$   rL   ro   r.   r   
dimensionsr   r	   r0   from_base_unitsrt   )r   rP   rl   r2   r   r/   r   rw   Zto_base_unitsmr   r   ner!   r!   r"   r#   Q  s4    

zUnitSystem.__init__c             c   s   x| j D ]
}|V  qW d S )N)rP   )r   r2   r!   r!   r"   rn   p  s    zUnitSystem.__iter__c             C   s:   d}d}x$| D ]}||7 }|t |7 }d}qW |d7 }|S )z	
        zUnitSystem([rZ   z, z]))rt   )r   rI   sepr2   r!   r!   r"   rN   t  s    
zUnitSystem.__str__c             C   s$  || j kr| j | S t| j}dg| }i }x6| D ]*\}}|| jkrX||| j| < q6|||< q6W t|g| j }t}x:t|D ].}	|d |	 }|dkr|t| j	|	 |i9 }qW t|dkri }
xN|
 D ]B\}}|j}||krq||
krq||
|< || }|t||i9 }qW || j |< |S )z	
        r   )rv   r$   rw   r0   r   rx   r\   r   r   rP   r1   r   )r   Zold_unitry   Z	base_dimsrT   r   r/   r   r+   iZ
found_dimsr   Zuseless_exponentr!   r!   r"   rW     s8    





zUnitSystem.express_unitN)rf   rg   rh   ri   r#   rn   rN   rW   r!   r!   r!   r"   ru   H  s
   ru   c             C   s
   t | tS )zd
    Returns True if x is a Unit, False otherwise.

    Examples

    >>> is_unit(16)
    False
    )r   r   )xr!   r!   r"   r9     s    	r9   __main__)ri   Z
__future__r   r   r   Zparmed.utils.sixr   Zparmed.utils.six.movesr   
__author____version__r'   sysZmymatrixr   r	   Zbasedimensionr
   Zbaseunitr   Zstandard_dimensionsobjectr   rk   ru   r9   r\   rf   ZdoctestZtestmodmodulesr!   r!   r!   r"   <module>!   s,      AY]