B
    dB                 @   s  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mZ ddlmZmZ d	d
ddddgZdd Zdd Zdd ZG dd deZG dd deZG dd dZG dd deeZG dd deeZG dd
 d
eZG dd	 d	eZG dd deZdS )a  
Implements rotations, including spherical rotations as defined in WCS Paper II
[1]_

`RotateNative2Celestial` and `RotateCelestial2Native` follow the convention in
WCS Paper II to rotate to/from a native sphere and the celestial sphere.

The implementation uses `EulerAngleRotation`. The model parameters are
three angles: the longitude (``lon``) and latitude (``lat``) of the fiducial point
in the celestial system (``CRVAL`` keywords in FITS), and the longitude of the celestial
pole in the native system (``lon_pole``). The Euler angles are ``lon+90``, ``90-lat``
and ``-(lon_pole-90)``.


References
----------
.. [1] Calabretta, M.R., Greisen, E.W., 2002, A&A, 395, 1077 (Paper II)
    N)rotation_matrixmatrix_product)units   )Model)	Parameter)
_to_radian_to_orig_unitRotateCelestial2NativeRotateNative2Celestial
Rotation2DEulerAngleRotationRotationSequence3DSphericalRotationSequencec             C   sb   g }xFt | |D ]8\}}t|tjr*|j}| }|t||tjd qW t	|d d d  }|S )N)unit)
zip
isinstanceuQuantityvalueitemappendr   radr   )angles
axes_orderZmatricesangleZaxisresult r   g/work/yifan.wang/ringdown/master-ringdown-env/lib/python3.7/site-packages/astropy/modeling/rotations.py_create_matrix%   s    r    c             C   sV   t | } t |}t | t | }t |t |  }t |}t |||gS )N)npZdeg2radcossinarray)alphadeltaxyzr   r   r   spherical2cartesian0   s    


r*   c             C   s8   t | |}t t || }t t ||}||fS )N)r!   hypotZrad2degZarctan2)r'   r(   r)   hr%   r&   r   r   r   cartesian2spherical9   s    r-   c                   sT   e Zd ZdZdZdZdZdZeg e	e
dZd fdd	Zedd	 Zd
d Z  ZS )r   a  
    Perform a series of rotations about different axis in 3D space.

    Positive angles represent a counter-clockwise rotation.

    Parameters
    ----------
    angles : array-like
        Angles of rotation in deg in the order of axes_order.
    axes_order : str
        A sequence of 'x', 'y', 'z' corresponding to axis of rotation.

    Examples
    --------
    >>> model = RotationSequence3D([1.1, 2.1, 3.1, 4.1], axes_order='xyzx')

    F   )defaultgettersetterNc                s   dddg| _ t|| j }|r2td|| j || _t|t|kr`tdt|t|t j||d d| _	d| _
d S )Nr'   r(   r)   z2Unrecognized axis label {0}; should be one of {1} z[The number of angles {0} should match the number                               of axes {1}.)name)r'   r(   r)   )axesset
difference
ValueErrorformatr   lensuper__init___inputs_outputs)selfr   r   r2   unrecognized)	__class__r   r   r:   Y   s    

zRotationSequence3D.__init__c             C   s0   | j jddd d }| j|| jddd dS )zInverse rotation.Nr   )r   )r   r   r?   r   )r=   r   r   r   r   inversei   s    zRotationSequence3D.inversec             C   s   |j |j   kr|j kr&n ntd|j p.d}t| | | g}tt|d | j|}|d |d |d   }}}| |_  |_ |_ |||fS )zJ
        Apply the rotation to a set of 3D Cartesian coordinates.
        z,Expected input arrays to have the same shape)r   r   r      )shaper6   r!   r$   flattendotr    r   )r=   r'   r(   r)   r   
orig_shapeinarrr   r   r   r   evaluateo   s    
zRotationSequence3D.evaluate)N)__name__
__module____qualname____doc__Zstandard_broadcasting
_separablen_inputs	n_outputsr   r	   r   r   r:   propertyr@   rG   __classcell__r   r   )r?   r   r   @   s   c                   sF   e Zd ZdZd fdd	Zedd Zedd Z fd	d
Z  Z	S )r   a\  
    Perform a sequence of rotations about arbitrary number of axes
    in spherical coordinates.

    Parameters
    ----------
    angles : list
        A sequence of angles (in deg).
    axes_order : str
        A sequence of characters ('x', 'y', or 'z') corresponding to the
        axis of rotation and matching the order in ``angles``.

    Nc                s6   d| _ d| _t j|f||d| d| _d| _d S )NrA   )r   r2   )lonlat)	_n_inputs
_n_outputsr9   r:   r;   r<   )r=   r   r   r2   kwargs)r?   r   r   r:      s
    z"SphericalRotationSequence.__init__c             C   s   | j S )N)rS   )r=   r   r   r   rM      s    z"SphericalRotationSequence.n_inputsc             C   s   | j S )N)rT   )r=   r   r   r   rN      s    z#SphericalRotationSequence.n_outputsc       
         s@   t ||\}}}t ||||\}}}	t|||	\}}||fS )N)r*   r9   rG   r-   )
r=   rQ   rR   r   r'   r(   r)   x1y1Zz1)r?   r   r   rG      s    z"SphericalRotationSequence.evaluate)N)
rH   rI   rJ   rK   r:   rO   rM   rN   rG   rP   r   r   )r?   r   r      s
   c               @   s<   e Zd ZdZdZdd ZdZdZedd Z	edd	 Z
d
S )_EulerRotationz7
    Base class which does the actual computation.
    Fc             C   st   d }t |tjr&| }| }|j}t||}t|||g|}	t|	|}
t|
 \}}|d k	rl||_||_||fS )N)	r   r!   ndarrayrC   rB   r*   r    rD   r-   )r=   r%   r&   phithetapsir   rB   Zinpmatrixr   abr   r   r   rG      s    
z_EulerRotation.evaluateTc             C   s   | j d tj| j d tjiS )z Input units. r   r   )inputsr   deg)r=   r   r   r   input_units   s    z_EulerRotation.input_unitsc             C   s   | j d tj| j d tjiS )z Output units. r   r   )outputsr   ra   )r=   r   r   r   return_units   s    z_EulerRotation.return_unitsN)rH   rI   rJ   rK   rL   rG   Z_input_units_strictZ _input_units_allow_dimensionlessrO   rb   rd   r   r   r   r   rX      s   rX   c                   sj   e Zd ZdZdZdZedeedZ	edeedZ
edeedZ fddZedd Z fd	d
Z  ZS )r   a1  
    Implements Euler angle intrinsic rotations.

    Rotates one coordinate system into another (fixed) coordinate system.
    All coordinate systems are right-handed. The sign of the angles is
    determined by the right-hand rule..

    Parameters
    ----------
    phi, theta, psi : float or `~astropy.units.Quantity` ['angle']
        "proper" Euler angles in deg.
        If floats, they should be in deg.
    axes_order : str
        A 3 character string, a combination of 'x', 'y' and 'z',
        where each character denotes an axis in 3D space.
    rA   r   )r/   r0   r1   c                s   dddg| _ t|dkr&td|t|| j }|rLtd|| j || _dd |||gD }t|r~t	|s~td	t
 jf |||d
| d| _d| _d S )Nr'   r(   r)   r.   zAExpected axes_order to be a character sequence of length 3,got {}z0Unrecognized axis label {}; should be one of {} c             S   s   g | ]}t |tjqS r   )r   r   r   ).0parr   r   r   
<listcomp>   s    z/EulerAngleRotation.__init__.<locals>.<listcomp>z>All parameters should be of the same type - float or Quantity.)rZ   r[   r\   )r%   r&   )r3   r8   	TypeErrorr7   r4   r5   r6   r   anyallr9   r:   r;   r<   )r=   rZ   r[   r\   r   rU   r>   qs)r?   r   r   r:      s     zEulerAngleRotation.__init__c             C   s*   | j | j | j | j | jd d d dS )Nr   )rZ   r[   r\   r   )r?   r\   r[   rZ   r   )r=   r   r   r   r@      s    
zEulerAngleRotation.inversec                s$   t  |||||| j\}}||fS )N)r9   rG   r   )r=   r%   r&   rZ   r[   r\   r^   r_   )r?   r   r   rG      s    zEulerAngleRotation.evaluate)rH   rI   rJ   rK   rM   rN   r   r	   r   rZ   r[   r\   r:   rO   r@   rG   rP   r   r   )r?   r   r      s   c                   sV   e Zd ZdZedeedZedeedZedeedZ	 fddZ
 fddZ  ZS )_SkyRotationzK
    Base class for RotateNative2Celestial and RotateCelestial2Native.
    r   )r/   r0   r1   c                sJ   dd |||gD }t |r,t|s,tdt j|||f| d| _d S )Nc             S   s   g | ]}t |tjqS r   )r   r   r   )re   rf   r   r   r   rg     s    z)_SkyRotation.__init__.<locals>.<listcomp>z>All parameters should be of the same type - float or Quantity.Zzxz)ri   rj   rh   r9   r:   r   )r=   rQ   rR   lon_polerU   rk   )r?   r   r   r:     s
    z_SkyRotation.__init__c       	         sR   t  |||||| j\}}|dk }t|tjrB||  d7  < n|d7 }||fS )Nr   ih  )r9   rG   r   r   r!   rY   )	r=   rZ   r[   rQ   rR   rm   r%   r&   mask)r?   r   r   	_evaluate  s    z_SkyRotation._evaluate)rH   rI   rJ   rK   r   r	   r   rQ   rR   rm   r:   ro   rP   r   r   )r?   r   rl     s   rl   c                   sX   e Zd ZdZdZdZedd Zedd Z fddZ	 fd	d
Z
edd Z  ZS )r   a~  
    Transform from Native to Celestial Spherical Coordinates.

    Parameters
    ----------
    lon : float or `~astropy.units.Quantity` ['angle']
        Celestial longitude of the fiducial point.
    lat : float or `~astropy.units.Quantity` ['angle']
        Celestial latitude of the fiducial point.
    lon_pole : float or `~astropy.units.Quantity` ['angle']
        Longitude of the celestial pole in the native system.

    Notes
    -----
    If ``lon``, ``lat`` and ``lon_pole`` are numerical values they
    should be in units of deg. Inputs are angles on the native sphere.
    Outputs are angles on the celestial sphere.
    rA   c             C   s   | j d tj| j d tjiS )z Input units. r   r   )r`   r   ra   )r=   r   r   r   rb   4  s    z"RotateNative2Celestial.input_unitsc             C   s   | j d tj| j d tjiS )z Output units. r   r   )rc   r   ra   )r=   r   r   r   rd   :  s    z#RotateNative2Celestial.return_unitsc                s$   t  j|||f| d| _d| _d S )N)phi_Ntheta_N)alpha_Cdelta_C)r9   r:   r`   rc   )r=   rQ   rR   rm   rU   )r?   r   r   r:   ?  s    zRotateNative2Celestial.__init__c                sl   t |tjr|j}|j}|j}|tjd  }tjd |  }tjd |  }t |||||\}	}
|	|
fS )a  
        Parameters
        ----------
        phi_N, theta_N : float or `~astropy.units.Quantity` ['angle']
            Angles in the Native coordinate system.
            it is assumed that numerical only inputs are in degrees.
            If float, assumed in degrees.
        lon, lat, lon_pole : float or `~astropy.units.Quantity` ['angle']
            Parameter values when the model was initialized.
            If float, assumed in degrees.

        Returns
        -------
        alpha_C, delta_C : float or `~astropy.units.Quantity` ['angle']
            Angles on the Celestial sphere.
            If float, in degrees.
        rA   )r   r   r   r   r!   pir9   ro   )r=   rp   rq   rQ   rR   rm   rZ   r[   r\   rr   rs   )r?   r   r   rG   D  s    zRotateNative2Celestial.evaluatec             C   s   t | j| j| jS )N)r
   rQ   rR   rm   )r=   r   r   r   r@   b  s    zRotateNative2Celestial.inverse)rH   rI   rJ   rK   rM   rN   rO   rb   rd   r:   rG   r@   rP   r   r   )r?   r   r     s   c                   sX   e Zd ZdZdZdZedd Zedd Z fddZ	 fd	d
Z
edd Z  ZS )r
   a~  
    Transform from Celestial to Native Spherical Coordinates.

    Parameters
    ----------
    lon : float or `~astropy.units.Quantity` ['angle']
        Celestial longitude of the fiducial point.
    lat : float or `~astropy.units.Quantity` ['angle']
        Celestial latitude of the fiducial point.
    lon_pole : float or `~astropy.units.Quantity` ['angle']
        Longitude of the celestial pole in the native system.

    Notes
    -----
    If ``lon``, ``lat`` and ``lon_pole`` are numerical values they should be
    in units of deg. Inputs are angles on the celestial sphere.
    Outputs are angles on the native sphere.
    rA   c             C   s   | j d tj| j d tjiS )z Input units. r   r   )r`   r   ra   )r=   r   r   r   rb   ~  s    z"RotateCelestial2Native.input_unitsc             C   s   | j d tj| j d tjiS )z Output units. r   r   )rc   r   ra   )r=   r   r   r   rd     s    z#RotateCelestial2Native.return_unitsc                s$   t  j|||f| d| _d| _d S )N)rr   rs   )rp   rq   )r9   r:   r`   rc   )r=   rQ   rR   rm   rU   )r?   r   r   r:     s    zRotateCelestial2Native.__init__c                sj   t |tjr|j}|j}|j}tjd | }tjd | }|tjd   }t |||||\}	}
|	|
fS )a;  
        Parameters
        ----------
        alpha_C, delta_C : float or `~astropy.units.Quantity` ['angle']
            Angles in the Celestial coordinate frame.
            If float, assumed in degrees.
        lon, lat, lon_pole : float or `~astropy.units.Quantity` ['angle']
            Parameter values when the model was initialized.
            If float, assumed in degrees.

        Returns
        -------
        phi_N, theta_N : float or `~astropy.units.Quantity` ['angle']
            Angles on the Native sphere.
            If float, in degrees.

        rA   )r   r   r   r   r!   rt   r9   ro   )r=   rr   rs   rQ   rR   rm   rZ   r[   r\   rp   rq   )r?   r   r   rG     s    zRotateCelestial2Native.evaluatec             C   s   t | j| j| jS )N)r   rQ   rR   rm   )r=   r   r   r   r@     s    zRotateCelestial2Native.inverse)rH   rI   rJ   rK   rM   rN   rO   rb   rd   r:   rG   r@   rP   r   r   )r?   r   r
   h  s   c                   sb   e Zd ZdZdZdZdZedee	dZ
e
f fdd	Zedd	 Zed
d Zedd Z  ZS )r   a  
    Perform a 2D rotation given an angle.

    Positive angles represent a counter-clockwise rotation and vice-versa.

    Parameters
    ----------
    angle : float or `~astropy.units.Quantity` ['angle']
        Angle of rotation (if float it should be in deg).
    rA   Fg        )r/   r0   r1   c                s&   t  jf d|i| d| _d| _d S )Nr   )r'   r(   )r9   r:   r;   r<   )r=   r   rU   )r?   r   r   r:     s    zRotation2D.__init__c             C   s   | j | j dS )zInverse rotation.)r   )r?   r   )r=   r   r   r   r@     s    zRotation2D.inversec       
      C   s   |j |j krtdt|dd}t|dd}|dk	o:|dk	}||krl|rb||rb||}|}n
td|j ptd}t|	 |	 g}t
|tjr|tj}t| ||}	|	d |	d  }}| |_ |_ |rtj||dtj||dfS ||fS )	a  
        Rotate (x, y) about ``angle``.

        Parameters
        ----------
        x, y : array-like
            Input quantities
        angle : float or `~astropy.units.Quantity` ['angle']
            Angle of rotations.
            If float, assumed in degrees.

        z,Expected input arrays to have the same shaper   Nz"x and y must have compatible units)r   r   r   )r   )rB   r6   getattrZis_equivalenttor   Z
UnitsErrorr!   r$   rC   r   r   Zto_valuer   rD   _compute_matrix)
clsr'   r(   r   Zx_unitZy_unitZ	has_unitsrE   rF   r   r   r   r   rG     s(    


zRotation2D.evaluatec             C   s6   t jt| t|  gt| t| ggt jdS )N)Zdtype)r!   r$   mathr"   r#   Zfloat64)r   r   r   r   rw     s    zRotation2D._compute_matrix)rH   rI   rJ   rK   rM   rN   rL   r   r	   r   r   r:   rO   r@   classmethodrG   staticmethodrw   rP   r   r   )r?   r   r     s   
*)rK   ry   numpyr!   Z$astropy.coordinates.matrix_utilitiesr   r   Zastropyr   r   corer   
parametersr   utilsr   r	   __all__r    r*   r-   r   r   rX   r   rl   r   r
   r   r   r   r   r   <module>   s(   
	?$'8KM