a
    ߙfbJ                     @   sl   d Z ddlZddlZddlmZmZmZ dgZe	dZ
dd Zd	d
 ZG dd dZG dd dejZdS )z6
This module defines structured units and quantities.
    N   )UnitUnitBaseUNITYStructuredUnitOc                 C   sJ   g }| j D ]6}| j| d }|j r6||t|g q
|| q
t|S )z-Recursively extract field names from a dtype.r   )namesfieldsappend_names_from_dtypetuple)dtyper   nameZsubdtype r   7lib/python3.9/site-packages/astropy/units/structured.pyr      s    
r   c                 C   s   g }| D ]}t |tr.t|dkr.|| qt |trt|dkrt |d trt|d dkrt |d trt|d dkr||d t|d g qt |trt|dkrt|}|ddd |D |g qtd|dqt|S )	a[  Recursively normalize, inferring upper level names for unadorned tuples.

    Generally, we want the field names to be organized like dtypes, as in
    ``(['pv', ('p', 'v')], 't')``.  But we automatically infer upper
    field names if the list is absent from items like ``(('p', 'v'), 't')``,
    by concatenating the names inside the tuple.
    r      r    c                 S   s"   g | ]}t |tr|d  n|qS r   )
isinstancelist.0ir   r   r   
<listcomp>4   s   z$_normalize_names.<locals>.<listcomp>zinvalid entry zY. Should be a name, tuple of names, or 2-element list of the form [name, tuple of names].)	r   strlenr
   r   r   _normalize_namesjoin
ValueError)r   resultr   Z	new_tupler   r   r   r   !   s2    

r   c                       s6  e Zd ZdZd? fdd	Zdd Zedd Zd	d
 Zdd Z	dd Z
dd Zdd Zdd Zd@ fdd	ZdAddZedd Zedd Zdd Zed d! Ze fd"d#Zg fd$d%Zg fd&d'Zejg fd(d)ZdBd+d,Zd-d. ZdZd/d0 Zd1d2 Zd3d4 Z d5d6 Z!d7d8 Z"d9d: Z#d;d< Z$d=d> Z%  Z&S )Cr   a	  Container for units for a structured Quantity.

    Parameters
    ----------
    units : unit-like, tuple of unit-like, or `~astropy.units.StructuredUnit`
        Tuples can be nested.  If a `~astropy.units.StructuredUnit` is passed
        in, it will be returned unchanged unless different names are requested.
    names : tuple of str, tuple or list; `~numpy.dtype`; or `~astropy.units.StructuredUnit`, optional
        Field names for the units, possibly nested. Can be inferred from a
        structured `~numpy.dtype` or another `~astropy.units.StructuredUnit`.
        For nested tuples, by default the name of the upper entry will be the
        concatenation of the names of the lower levels.  One can pass in a
        list with the upper-level name and a tuple of lower-level names to
        avoid this.  For tuples, not all levels have to be given; for any level
        not passed in, default field names of 'f0', 'f1', etc., will be used.

    Notes
    -----
    It is recommended to initialze the class indirectly, using
    `~astropy.units.Unit`.  E.g., ``u.Unit('AU,AU/day')``.

    When combined with a structured array to produce a structured
    `~astropy.units.Quantity`, array field names will take precedence.
    Generally, passing in ``names`` is needed only if the unit is used
    unattached to a `~astropy.units.Quantity` and one needs to access its
    fields.

    Examples
    --------
    Various ways to initialize a `~astropy.units.StructuredUnit`::

        >>> import astropy.units as u
        >>> su = u.Unit('(AU,AU/day),yr')
        >>> su
        Unit("((AU, AU / d), yr)")
        >>> su.field_names
        (['f0', ('f0', 'f1')], 'f1')
        >>> su['f1']
        Unit("yr")
        >>> su2 = u.StructuredUnit(((u.AU, u.AU/u.day), u.yr), names=(('p', 'v'), 't'))
        >>> su2 == su
        True
        >>> su2.field_names
        (['pv', ('p', 'v')], 't')
        >>> su3 = u.StructuredUnit((su2['pv'], u.day), names=(['p_v', ('p', 'v')], 't'))
        >>> su3.field_names
        (['p_v', ('p', 'v')], 't')
        >>> su3.keys()
        ('p_v', 't')
        >>> su3.values()
        (Unit("(AU, AU / d)"), Unit("d"))

    Structured units share most methods with regular units::

        >>> su.physical_type
        ((PhysicalType('length'), PhysicalType({'speed', 'velocity'})), PhysicalType('time'))
        >>> su.si
        Unit("((1.49598e+11 m, 1.73146e+06 m / s), 3.15576e+07 s)")

    Nc                    s  d }|d urxt |tr&|jj}|j}nRt |tjr`|js@tdtdd |jD }t	|}nt |t
sp|f}t|}t |t
st|}t |tr|d u s|j|kr|S | }n|f}|d u rt
dd tt|D }nt|t|krtdg }t||D ]^\}}t |tr0| ||d }|d }n&t|}|d urVt |trVtd	|| qt | }|d u rtd
d |D }tt
||d |_|S )Nz(dtype should be structured, with fields.c                 S   s   g | ]}|t fqS r   )DTYPE_OBJECTr   r   r   r   r   r          z*StructuredUnit.__new__.<locals>.<listcomp>c                 s   s   | ]}d | V  qdS )fNr   r   r   r   r   	<genexpr>   r"   z)StructuredUnit.__new__.<locals>.<genexpr>z,lengths of units and field names must match.r   r   zKunits do not match in depth with field names from dtype or structured unit.c                 S   s&   g | ]}t |tr|d  n|tfqS r   )r   r   r    r!   r   r   r   r      s   r   )r   r   _unitsr   field_namesnpr	   r   r   r   r   r   r   valuesranger   zipr   r
   super__new__array)clsZunitsr   r   Z	convertedunitr   self	__class__r   r   r,   {   sP    








zStructuredUnit.__new__c                 C   s   dS )z?When de-serializing, e.g. pickle, start with a blank structure.)r   Nr   r0   r   r   r   __getnewargs__   s    zStructuredUnit.__getnewargs__c                 C   s   t dd |  D S )z6Possibly nested tuple of the field names of the parts.c                 s   s*   | ]"\}}t |tr||jgn|V  qd S N)r   r   r&   )r   r   r/   r   r   r   r$      s   z-StructuredUnit.field_names.<locals>.<genexpr>)r   itemsr3   r   r   r   r&      s    zStructuredUnit.field_namesc                 C   s   t | jjjS r5   )r   r%   r   r   r3   r   r   r   __len__   s    zStructuredUnit.__len__c                 C   s
   | j | S r5   )r%   )r0   itemr   r   r   __getitem__   s    zStructuredUnit.__getitem__c                 C   s
   | j  S r5   )r%   r8   r3   r   r   r   r(      s    zStructuredUnit.valuesc                 C   s
   | j jjS r5   r%   r   r   r3   r   r   r   keys   s    zStructuredUnit.keysc                 C   s   t t| jjj| j S r5   )r   r*   r%   r   r   r8   r3   r   r   r   r6      s    zStructuredUnit.itemsc                 c   s   | j jjE d H  d S r5   r:   r3   r   r   r   __iter__   s    zStructuredUnit.__iter__c                    sZ   t t fdd|  D | jjd }|durB|||jfS t | j	}||_|S )aq  Apply func recursively.

        Parameters
        ----------
        func : callable
            Function to apply to all parts of the structured unit,
            recursing as needed.
        cls : type, optional
            If given, should be a subclass of `~numpy.void`. By default,
            will return a new `~astropy.units.StructuredUnit` instance.
        c                    s   g | ]} |qS r   r   r   partfuncr   r   r      r"   z5StructuredUnit._recursively_apply.<locals>.<listcomp>r   N)
r'   r-   r   r(   r%   r   Zviewr+   r,   r2   )r0   r@   r.   Zresultsr   r1   r?   r   _recursively_apply   s    z!StructuredUnit._recursively_applyTc                 C   s   |rt |tr|d }q|tu r0tft|  }n0t |trJt| t|kr`td| d|  dg }t|  |D ]f\\}}}t |tr|	||j
|ddf qrt|}|j}|jdv rtt}|	|||jf qrt|S )a  Get structured dtype according to value, using our field names.

        This is useful since ``np.array(value)`` would treat tuples as lower
        levels of the array, rather than as elements of a structured array.
        The routine does presume that the type of the first tuple is
        representative of the rest.  Used in ``_get_converter``.

        For the special value of ``UNITY``, all fields are assumed to be 1.0,
        and hence this will return an all-float dtype.

        r   zcannot interpret value z
 for unit .F)enter_listsZiu)r   r   r   r   r   r   r*   r6   r   r
   _recursively_get_dtyper'   r-   r   kindfloatshape)r0   valuerC   Zdescrr   r/   r>   Z
part_dtyper   r   r   rD      s&    





z%StructuredUnit._recursively_get_dtypec                 C   s   |  tdS )z*The `StructuredUnit` instance in SI units.sirA   operator
attrgetterr3   r   r   r   rI     s    zStructuredUnit.sic                 C   s   |  tdS )z+The `StructuredUnit` instance in cgs units.cgsrJ   r3   r   r   r   rM     s    zStructuredUnit.cgsc                 C   s   | j tdtdS )N_get_physical_type_idr.   )rA   rK   methodcaller	Structurer3   r   r   r   rN     s    
z$StructuredUnit._get_physical_type_idc                 C   s   | j tdtdS )z!Physical types of all the fields.physical_typerO   )rA   rK   rL   rQ   r3   r   r   r   rR     s    
zStructuredUnit.physical_typec                 C   s   |  tjd|dS )a\  The `StructuredUnit` composed of only irreducible units.

        Parameters
        ----------
        bases : sequence of `~astropy.units.UnitBase`, optional
            The bases to decompose into.  When not provided,
            decomposes down to any irreducible units.  When provided,
            the decomposed result will only contain the given units.
            This will raises a `UnitsError` if it's not possible
            to do so.

        Returns
        -------
        `~astropy.units.StructuredUnit`
            With the unit for each field containing only irreducible units.
        	decompose)bases)rA   rK   rP   )r0   rT   r   r   r   rS   $  s    zStructuredUnit.decomposec                 C   sj   zt |}W n ty    Y dS 0 t| t|kr6dS t|  | D ]\}}|j||dsH dS qHdS )a  `True` if all fields are equivalent to the other's fields.

        Parameters
        ----------
        other : `~astropy.units.StructuredUnit`
            The structured unit to compare with, or what can initialize one.
        equivalencies : list of tuple, optional
            A list of equivalence pairs to try if the units are not
            directly convertible.  See :ref:`unit_equivalencies`.
            The list will be applied to all fields.

        Returns
        -------
        bool
        FequivalenciesT)r   	Exceptionr   r*   r(   is_equivalent)r0   otherrV   	self_part
other_partr   r   r   rX   8  s    zStructuredUnit.is_equivalentc                    sN   t |tsj|d}fddt | D   fdd}|S )Nr   c                    s   g | ]\}}|j | d qS )rU   )_get_converter)r   rZ   r[   rU   r   r   r   [  s   z1StructuredUnit._get_converter.<locals>.<listcomp>c                    s`   t | dst| | } t| }t|jj D ]\}}|| | ||< q4|jrX|S |d S )Nr   r   )	hasattrr'   r-   rD   Z
empty_liker*   r   r   rG   )rH   r   r   Z
converter_)
convertersr0   r   r   	converter`  s    

z0StructuredUnit._get_converter.<locals>.converter)r   typer2   r*   r(   )r0   rY   rV   r`   r   )r_   rV   r0   r   r]   W  s    
	zStructuredUnit._get_converterc                 C   s    |t ju rt}| j||d|S )a  Return values converted to the specified unit.

        Parameters
        ----------
        other : `~astropy.units.StructuredUnit`
            The unit to convert to.  If necessary, will be converted to
            a `~astropy.units.StructuredUnit` using the dtype of ``value``.
        value : array-like, optional
            Value(s) in the current unit to be converted to the
            specified unit.  If a sequence, the first element must have
            entries of the correct type to represent all elements (i.e.,
            not have, e.g., a ``float`` where other elements have ``complex``).
            If not given, assumed to have 1. in all fields.
        equivalencies : list of tuple, optional
            A list of equivalence pairs to try if the units are not
            directly convertible.  See :ref:`unit_equivalencies`.
            This list is in addition to possible global defaults set by, e.g.,
            `set_enabled_equivalencies`.
            Use `None` to turn off all equivalencies.

        Returns
        -------
        values : scalar or array
            Converted value(s).

        Raises
        ------
        UnitsError
            If units are inconsistent
        rU   )r'   _NoValuer   r]   )r0   rY   rH   rV   r   r   r   tok  s    
zStructuredUnit.togenericc                    s\    fdd|   D }t| dkr&dnd} dkrLdd |D }d| d }|d	|S )
a  Output the unit in the given format as a string.

        Units are separated by commas.

        Parameters
        ----------
        format : `astropy.units.format.Base` instance or str
            The name of a format or a formatter object.  If not
            provided, defaults to the generic format.

        Notes
        -----
        Structured units can be written to all formats, but can be
        re-read only with 'generic'.

        c                    s   g | ]}|  qS r   	to_stringr=   formatr   r   r     r"   z,StructuredUnit.to_string.<locals>.<listcomp>r   z({})z({},)latexc                 S   s   g | ]}|d d qS )r   r   r=   r   r   r   r     r"   $z, )r(   r   rh   r   )r0   rh   partsZout_fmtr   rg   r   rf     s    zStructuredUnit.to_stringc                 C   s
   |  dS )Nri   re   r3   r   r   r   _repr_latex_  s    zStructuredUnit._repr_latex_c                    s   t  tr2zt dd W n ty0   t Y S 0 t  trdt fdd|  D }| j|| dS t  t	rrtS zddl
m} | | dW S  ty   t Y S 0 d S )	NsilentZparse_strictc                 3   s   | ]}|  V  qd S r5   r   r=   rY   r   r   r$     r"   z)StructuredUnit.__mul__.<locals>.<genexpr>r\   r   Quantity)r/   )r   r   r   rW   NotImplementedr   r   r(   r2   r   quantityrr   )r0   rY   	new_unitsrr   r   rp   r   __mul__  s    



zStructuredUnit.__mul__c                 C   s
   |  |S r5   )rv   r0   rY   r   r   r   __rmul__  s    zStructuredUnit.__rmul__c                    sh   t  tr2zt dd W n ty0   t Y S 0 t  trdt fdd|  D }| j|| dS tS )Nrn   ro   c                 3   s   | ]}|  V  qd S r5   r   r=   rp   r   r   r$     r"   z-StructuredUnit.__truediv__.<locals>.<genexpr>r\   )	r   r   r   rW   rs   r   r   r(   r2   )r0   rY   ru   r   rp   r   __truediv__  s    


zStructuredUnit.__truediv__c                 C   s:   zddl m} ||| dddW S  ty4   t Y S 0 d S )Nr   rq   FT)copyZsubok)rt   rr   rW   rs   )r0   mrr   r   r   r   __rlshift__  s
    zStructuredUnit.__rlshift__c                 C   s   |   S r5   re   r3   r   r   r   __str__  s    zStructuredUnit.__str__c                 C   s   d|    dS )NzUnit("z")re   r3   r   r   r   __repr__  s    zStructuredUnit.__repr__c                 C   s4   zt |}W n ty"   t Y S 0 |  | kS r5   )r   rW   rs   r(   rw   r   r   r   __eq__  s
    
zStructuredUnit.__eq__c                 C   sB   t |t| s2zt|}W n ty0   t Y S 0 |  | kS r5   )r   ra   r   rW   rs   r(   rw   r   r   r   __ne__  s    
zStructuredUnit.__ne__)N)N)T)rd   )'__name__
__module____qualname____doc__r,   r4   propertyr&   r7   r9   r(   r;   r6   r<   rA   rD   rI   rM   rN   rR   setrS   rX   r]   r'   rb   rc   rf   rm   Z__array_ufunc__rv   rx   ry   r|   r}   r~   r   r   __classcell__r   r   r1   r   r   >   sF   <<

"


%
c                   @   s    e Zd ZdZdd Zdd ZdS )rQ   aR  Single element structure for physical type IDs, etc.

    Behaves like a `~numpy.void` and thus mostly like a tuple which can also
    be indexed with field names, but overrides ``__eq__`` and ``__ne__`` to
    compare only the contents, not the field names.  Furthermore, this way no
    `FutureWarning` about comparisons is given.

    c                 C   s    t |tjr| }|  |kS r5   r   r'   voidr8   rw   r   r   r   r     s    zStructure.__eq__c                 C   s    t |tjr| }|  |kS r5   r   rw   r   r   r   r     s    zStructure.__ne__N)r   r   r   r   r   r   r   r   r   r   rQ     s   rQ   )r   rK   Znumpyr'   corer   r   r   __all__r   r    r   r   r   r   rQ   r   r   r   r   <module>   s   
   4