def particle_cos_alpha(coords, vels, L):
    """
    cosine of the angle between the orbital angular momentum
    of the particle and that of the galaxy system

    Parameters
    ----------
    coords :
        normalized particle positions

    vels : array_like
        normalized particle velocities

    L : array_like
        array of 3D angular momentum vector

    Returns
    -------
    cos_alpha : array_like
       array of cosine(alpha) values
    """

    # specific angular momomentum of particles
    j = normalized_vectors(np.cross(coords, vels))
    l = normalized_vectors(L)[0]

    return np.dot(j, l)
def rotation_matrices_from_basis(ux, uy, uz):
    """
    Calculate a collection of rotation matrices defined by a set of basis vectors

    Parameters
    ----------
    ux : array_like
        Numpy array of shape (npts, 3) storing a collection of unit vexctors

    uy : array_like
        Numpy array of shape (npts, 3) storing a collection of unit vexctors

    uz : array_like
        Numpy array of shape (npts, 3) storing a collection of unit vexctors

    Returns
    -------
    matrices : ndarray
        Numpy array of shape (npts, 3, 3) storing a collection of rotation matrices
    """

    N = np.shape(ux)[0]

    # assume initial unit vectors are the standard ones
    ex = np.array([1.0, 0.0, 0.0] * N).reshape(N, 3)
    ey = np.array([0.0, 1.0, 0.0] * N).reshape(N, 3)
    ez = np.array([0.0, 0.0, 1.0] * N).reshape(N, 3)

    ux = normalized_vectors(ux)
    uy = normalized_vectors(uy)
    uz = normalized_vectors(uz)

    r_11 = elementwise_dot(ex, ux)
    r_12 = elementwise_dot(ex, uy)
    r_13 = elementwise_dot(ex, uz)

    r_21 = elementwise_dot(ey, ux)
    r_22 = elementwise_dot(ey, uy)
    r_23 = elementwise_dot(ey, uz)

    r_31 = elementwise_dot(ez, ux)
    r_32 = elementwise_dot(ez, uy)
    r_33 = elementwise_dot(ez, uz)

    r = np.zeros((N, 3, 3))
    r[:, 0, 0] = r_11
    r[:, 0, 1] = r_12
    r[:, 0, 2] = r_13
    r[:, 1, 0] = r_21
    r[:, 1, 1] = r_22
    r[:, 1, 2] = r_23
    r[:, 2, 0] = r_31
    r[:, 2, 1] = r_32
    r[:, 2, 2] = r_33

    return r
def rotation_matrices_from_vectors(v0, v1):
    r"""
    Calculate a collection of rotation matrices defined by the unique
    transformation rotating v1 into v2 about the mutually perpendicular axis.

    Parameters
    ----------
    v0 : ndarray
        Numpy array of shape (npts, 3) storing a collection of initial vector orientations.

        Note that the normalization of `v0` will be ignored.

    v1 : ndarray
        Numpy array of shape (npts, 3) storing a collection of final vectors.

        Note that the normalization of `v1` will be ignored.

    Returns
    -------
    matrices : ndarray
        Numpy array of shape (npts, 3, 3) rotating each v0 into the corresponding v1

    Examples
    --------
    >>> npts = int(1e4)
    >>> v0 = np.random.random((npts, 3))
    >>> v1 = np.random.random((npts, 3))
    >>> rotation_matrices = rotation_matrices_from_vectors(v0, v1)

    Notes
    -----
    The function `rotate_vector_collection` can be used to efficiently
    apply the returned collection of matrices to a collection of 3d vectors

    """
    v0 = normalized_vectors(v0)
    v1 = normalized_vectors(v1)
    directions = vectors_normal_to_planes(v0, v1)
    angles = angles_between_list_of_vectors(v0, v1)

    # where angles are 0.0, replace directions with v0
    mask_a = (np.isnan(directions[:, 0]) | np.isnan(directions[:, 1])
              | np.isnan(directions[:, 2]))
    mask_b = (angles == 0.0)
    mask = mask_a | mask_b
    directions[mask] = v0[mask]

    return rotation_matrices_from_angles(angles, directions)
def vectors_normal_to_planes(x, y):
    r""" Given a collection of 3d vectors x and y,
    return a collection of 3d unit-vectors that are orthogonal to x and y.

    Parameters
    ----------
    x : ndarray
        Numpy array of shape (npts, 3) storing a collection of 3d vectors

        Note that the normalization of `x` will be ignored.

    y : ndarray
        Numpy array of shape (npts, 3) storing a collection of 3d vectors

        Note that the normalization of `y` will be ignored.

    Returns
    -------
    z : ndarray
        Numpy array of shape (npts, 3). Each 3d vector in z will be orthogonal
        to the corresponding vector in x and y.

    Examples
    --------
    >>> npts = int(1e4)
    >>> x = np.random.random((npts, 3))
    >>> y = np.random.random((npts, 3))
    >>> normed_z = angles_between_list_of_vectors(x, y)

    """
    return normalized_vectors(np.cross(x, y))
Exemple #5
0
def rotation_matrices_from_vectors(v0, v1):
    r"""
    Calculate a collection of rotation matrices defined by the unique
    transformation rotating v1 into v2.

    Parameters
    ----------
    v0 : ndarray
        Numpy array of shape (npts, 2) storing a collection of initial vector orientations.

        Note that the normalization of `v0` will be ignored.

    v1 : ndarray
        Numpy array of shape (npts, 2) storing a collection of final vectors.

        Note that the normalization of `v1` will be ignored.

    Returns
    -------
    matrices : ndarray
        Numpy array of shape (npts, 2, 2) rotating each v0 into the corresponding v1

    Examples
    --------
    >>> npts = int(1e4)
    >>> v0 = np.random.random((npts, 2))
    >>> v1 = np.random.random((npts, 2))
    >>> rotation_matrices = rotation_matrices_from_vectors(v0, v1)

    Notes
    -----
    The function `rotate_vector_collection` can be used to efficiently
    apply the returned collection of matrices to a collection of 2d vectors

    """
    v0 = normalized_vectors(v0)
    v1 = normalized_vectors(v1)
    
    # use the atan2 function to get the direction of rotation right
    angles = np.arctan2(v0[:,0], v0[:,1])-np.arctan2(v1[:,0],v1[:,1])

    return rotation_matrices_from_angles(angles)
Exemple #6
0
def rotation_matrices_from_basis(ux, uy):
    """
    Calculate a collection of rotation matrices defined by an input collection
    of basis vectors.

    Parameters
    ----------
    ux : array_like
        Numpy array of shape (npts, 2) storing a collection of unit vectors

    uy : array_like
        Numpy array of shape (npts, 2) storing a collection of unit vectors

    Returns
    -------
    matrices : ndarray
        Numpy array of shape (npts, 2, 2) storing a collection of rotation matrices
    """

    N = np.shape(ux)[0]

    # assume initial unit vectors are the standard ones
    ex = np.array([1.0, 0.0]*N).reshape(N, 2)
    ey = np.array([0.0, 1.0]*N).reshape(N, 2)

    ux = normalized_vectors(ux)
    uy = normalized_vectors(uy)

    r_11 = elementwise_dot(ex, ux)
    r_12 = elementwise_dot(ex, uy)

    r_21 = elementwise_dot(ey, ux)
    r_22 = elementwise_dot(ey, uy)

    r = np.zeros((N, 2, 2))
    r[:,0,0] = r_11
    r[:,0,1] = r_12
    r[:,1,0] = r_21
    r[:,1,1] = r_22

    return r
def rotation_matrices_from_angles(angles, directions):
    r"""
    Calculate a collection of rotation matrices defined by
    an input collection of rotation angles and rotation axes.

    Parameters
    ----------
    angles : ndarray
        Numpy array of shape (npts, ) storing a collection of rotation angles

    directions : ndarray
        Numpy array of shape (npts, 3) storing a collection of rotation axes in 3d

    Returns
    -------
    matrices : ndarray
        Numpy array of shape (npts, 3, 3) storing a collection of rotation matrices

    Examples
    --------
    >>> npts = int(1e4)
    >>> angles = np.random.uniform(-np.pi/2., np.pi/2., npts)
    >>> directions = np.random.random((npts, 3))
    >>> rotation_matrices = rotation_matrices_from_angles(angles, directions)

    Notes
    -----
    The function `rotate_vector_collection` can be used to efficiently
    apply the returned collection of matrices to a collection of 3d vectors

    """
    directions = normalized_vectors(directions)
    angles = np.atleast_1d(angles)
    npts = directions.shape[0]

    sina = np.sin(angles)
    cosa = np.cos(angles)

    R1 = np.zeros((npts, 3, 3))
    R1[:, 0, 0] = cosa
    R1[:, 1, 1] = cosa
    R1[:, 2, 2] = cosa

    R2 = directions[..., None] * directions[:, None, :]
    R2 = R2 * np.repeat(1. - cosa, 9).reshape((npts, 3, 3))

    directions *= sina.reshape((npts, 1))
    R3 = np.zeros((npts, 3, 3))
    R3[:, [1, 2, 0], [2, 0, 1]] -= directions
    R3[:, [2, 0, 1], [1, 2, 0]] += directions

    return R1 + R2 + R3
def vectors_between_list_of_vectors(x, y, p):
    r"""
    Starting from two input lists of vectors, return a list of unit-vectors
    that lie in the same plane as the corresponding input vectors,
    and where the input `p` controls the angle between
    the returned vs. input vectors.

    Parameters
    ----------
    x : ndarray
        Numpy array of shape (npts, 3) storing a collection of 3d vectors

        Note that the normalization of `x` will be ignored.

    y : ndarray
        Numpy array of shape (npts, 3) storing a collection of 3d vectors

        Note that the normalization of `y` will be ignored.

    p : ndarray
        Numpy array of shape (npts, ) storing values in the closed interval [0, 1].
        For values of `p` equal to zero, the returned vectors will be
        exactly aligned with the input `x`; when `p` equals unity, the returned
        vectors will be aligned with `y`.

    Returns
    -------
    v : ndarray
        Numpy array of shape (npts, 3) storing a collection of 3d unit-vectors
        lying in the plane spanned by `x` and `y`. The angle between `v` and `x`
        will be equal to :math:`p*\theta_{\rm xy}`.

    Examples
    --------
    >>> npts = int(1e4)
    >>> x = np.random.random((npts, 3))
    >>> y = np.random.random((npts, 3))
    >>> p = np.random.uniform(0, 1, npts)
    >>> v = vectors_between_list_of_vectors(x, y, p)
    >>> angles_xy = angles_between_list_of_vectors(x, y)
    >>> angles_xp = angles_between_list_of_vectors(x, v)
    >>> assert np.allclose(angles_xy*p, angles_xp)
    """
    assert np.all(p >= 0), "All values of p must be non-negative"
    assert np.all(p <= 1), "No value of p can exceed unity"

    z = vectors_normal_to_planes(x, y)
    theta = angles_between_list_of_vectors(x, y)
    angles = p * theta
    rotation_matrices = rotation_matrices_from_angles(angles, z)
    return normalized_vectors(rotate_vector_collection(rotation_matrices, x))
def particle_cos_beta(coords, L):
    """
    cosine of the angle between the radial vector
    and the spin axis of the galaxy

    Parameters
    ----------
    coords :
        normalized particle positions

    L : array_like
        array of 3D angular momentum vector

    Returns
    -------
    cos_alpha : array_like
       array of cosine(alpha) values
    """

    # specific angular momomentum of particles
    r = normalized_vectors(coords)
    l = normalized_vectors(L)[0]

    return np.dot(r, l)
Exemple #10
0
    def assign_orientation(self, **kwargs):
        r"""
        """

        if 'table' in kwargs.keys():
            table = kwargs['table']
            N = len(table)
        else:
            N = kwargs['size']

        # assign random orientations
        major_v = random_unit_vectors_3d(N)
        inter_v = random_perpendicular_directions(major_v)
        minor_v = normalized_vectors(np.cross(major_v, inter_v))

        if 'table' in kwargs.keys():
            try:
                mask = (table['gal_type'] == self.gal_type)
            except KeyError:
                mask = np.array([True] * N)
                msg = (
                    "Because `gal_type` not indicated in `table`.",
                    "The orientation is being assigned for all galaxies in the `table`."
                )
                print(msg)

            # check to see if the columns exist
            for key in list(self._galprop_dtypes_to_allocate.names):
                if key not in table.keys():
                    table[key] = 0.0

            table['galaxy_axisA_x'][mask] = major_v[mask, 0]
            table['galaxy_axisA_y'][mask] = major_v[mask, 1]
            table['galaxy_axisA_z'][mask] = major_v[mask, 2]

            table['galaxy_axisB_x'][mask] = inter_v[mask, 0]
            table['galaxy_axisB_y'][mask] = inter_v[mask, 1]
            table['galaxy_axisB_z'][mask] = inter_v[mask, 2]

            table['galaxy_axisC_x'][mask] = minor_v[mask, 0]
            table['galaxy_axisC_y'][mask] = minor_v[mask, 1]
            table['galaxy_axisC_z'][mask] = minor_v[mask, 2]

            return table
        else:
            return major_v, inter_v, minor_v
def axes_correlated_with_input_vector(input_vectors, p=0., seed=None):
    r"""
    Calculate a list of 3d unit-vectors whose orientation is correlated
    with the orientation of `input_vectors`.

    Parameters
    ----------
    input_vectors : ndarray
        Numpy array of shape (npts, 3) storing a list of 3d vectors defining the
        preferred orientation with which the returned vectors will be correlated.
        Note that the normalization of `input_vectors` will be ignored.

    p : ndarray, optional
        Numpy array with shape (npts, ) defining the strength of the correlation
        between the orientation of the returned vectors and the z-axis.
        Default is zero, for no correlation.
        Positive (negative) values of `p` produce galaxy principal axes
        that are statistically aligned with the positive (negative) z-axis;
        the strength of this alignment increases with the magnitude of p.
        When p = 0, galaxy axes are randomly oriented.

    seed : int, optional
        Random number seed used to choose a random orthogonal direction

    Returns
    -------
    unit_vectors : ndarray
        Numpy array of shape (npts, 3)
    """

    input_unit_vectors = normalized_vectors(input_vectors)
    assert input_unit_vectors.shape[1] == 3
    npts = input_unit_vectors.shape[0]

    z_correlated_axes = axes_correlated_with_z(p, seed)

    z_axes = np.tile((0, 0, 1), npts).reshape((npts, 3))

    angles = angles_between_list_of_vectors(z_axes, input_unit_vectors)
    rotation_axes = vectors_normal_to_planes(z_axes, input_unit_vectors)
    matrices = rotation_matrices_from_angles(angles, rotation_axes)

    return rotate_vector_collection(matrices, z_correlated_axes)
def project_onto_plane(x1, x2):
    r"""
    Given a collection of 3D vectors, x1 and x2, project each vector
    in x1 onto the plane normal to the corresponding vector x2

    Parameters
    ----------
    x1 : ndarray
        Numpy array of shape (npts, 3) storing a collection of 3d points

    x2 : ndarray
        Numpy array of shape (npts, 3) storing a collection of 3d points

    Returns
    -------
    result : ndarray
        Numpy array of shape (npts, 3) storing a collection of 3d points

    """

    n = normalized_vectors(x2)
    d = elementwise_dot(x1, n)

    return x - d[:, np.newaxis] * n
    def assign_orientation(self, **kwargs):
        r"""
        assign a a set of three orthoganl unit vectors indicating the orientation
        of the galaxies' major, intermediate, and minor axis
        """
        if 'table' in kwargs.keys():
            table = kwargs['table']
            halo_x = table['halo_x']
            halo_y = table['halo_y']
            halo_z = table['halo_z']
            Ax = table[self.list_of_haloprops_needed[3]]
            Ay = table[self.list_of_haloprops_needed[4]]
            Az = table[self.list_of_haloprops_needed[5]]
            halo_r = table['halo_rvir']
            Lbox = self._Lbox
        else:
            halo_x = kwargs['halo_x']
            halo_y = kwargs['halo_y']
            halo_z = kwargs['halo_z']
            Ax = kwargs['halo_axisA_x']
            Ay = kwargs['halo_axisA_y']
            Az = kwargs['halo_axisA_z']
            halo_r = kwargs['halo_rvir']
            Lbox = kwargs['Lbox']

        Ngal = len(Ax)

        # define halo-center - satellite vector
        dx = (x - halo_x)
        mask = dx > Lbox[0] / 2.0
        dx[mask] = dx[mask] - Lbox[0]
        mask = dx < -1.0 * Lbox[0] / 2.0
        dx[mask] = dx[mask] + Lbox[0]

        dy = (y - halo_y)
        mask = dy > Lbox[1] / 2.0
        dy[mask] = dy[mask] - Lbox[1]
        mask = dy < -1.0 * Lbox[1] / 2.0
        dy[mask] = dy[mask] + Lbox[1]

        dz = (z - halo_z)
        mask = dz > Lbox[2] / 2.0
        dz[mask] = dz[mask] - Lbox[2]
        mask = dz < -1.0 * Lbox[2] / 2.0
        dz[mask] = dz[mask] + Lbox[2]

        # radial vector
        v1 = normalized_vectors(np.vstack((dx, dy, dz)).T)

        # major axis orientation
        v2 = normalized_vectors(np.vstack((Ax, Ay, Az)).T)

        # account for handedness by randomly flipping alignment components
        seed = kwargs.get('seed', None)
        with NumpyRNGContext(seed):
            uran1 = np.random.random(Ngal)
        if seed is not None:
            seed = seed + 1
        with NumpyRNGContext(seed):
            uran2 = np.random.random(Ngal)
        flip1 = np.ones(Ngal)
        flip1[uran1 < 0.5] = -1.0
        flip2 = np.ones(Ngal)
        flip2[uran2 < 0.5] = -1.0
        v1 = flip1[:, np.newaxis] * v1
        v2 = flip2[:, np.newaxis] * v2

        # calculate scaled halo virial radius
        r = np.sqrt(dx**2 + dy**2 + dz**2) / halo_r

        # get alignment strength for each galaxy
        if 'table' in kwargs.keys():
            try:
                p = table['satellite_alignment_strength']
            except KeyError:
                msg = (
                    '`satellite_alignment_strength` not detected in the table, using value in self.param_dict.'
                )
                warn(msg)
                p = np.ones(len(
                    table)) * self.param_dict['satellite_alignment_strength']
        else:
            N = len(self.param_dict['x'])
            p = np.ones(N * self.param_dict['satellite_alignment_strength'])

        # get major to radial parameter
        a = self.radial_hybrid_alignment_vector_parameter(r)

        # define alignment vector inbetween v1 and v2
        v3 = normalized_vectors(vectors_between_list_of_vectors(v1, v2, a))

        # get galaxy major axis
        major_v = axes_correlated_with_input_vector(v3, p=p)

        # randomly set minor axis orientation
        minor_v = random_perpendicular_directions(major_v)

        # the intermediate axis is determined
        inter_v = vectors_normal_to_planes(major_v, minor_v)

        mask = (table['gal_type'] == self.gal_type)

        # add orientations to the galaxy table
        table['galaxy_axisA_x'][mask] = major_v[mask, 0]
        table['galaxy_axisA_y'][mask] = major_v[mask, 1]
        table['galaxy_axisA_z'][mask] = major_v[mask, 2]

        table['galaxy_axisB_x'][mask] = inter_v[mask, 0]
        table['galaxy_axisB_y'][mask] = inter_v[mask, 1]
        table['galaxy_axisB_z'][mask] = inter_v[mask, 2]

        table['galaxy_axisC_x'][mask] = minor_v[mask, 0]
        table['galaxy_axisC_y'][mask] = minor_v[mask, 1]
        table['galaxy_axisC_z'][mask] = minor_v[mask, 2]

        return table