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)
Example #2
0
    def mc_unit_sphere(self, Npts, **kwargs):
        r"""
        Returns Npts anisotropically distributed points on the unit sphere.
        Parameters
        ----------
        Npts : int
            Number of 3d points to generate
        seed : int, optional
            Random number seed used in the Monte Carlo realization.
            Default is None, which will produce stochastic results.
        Returns
        -------
        x, y, z : array_like
            Length-Npts arrays of the coordinate positions.
        """

        seed = kwargs.get('seed', None)

        if 'table' in kwargs:
            table = kwargs['table']
            try:
                b_to_a = table['halo_b_to_a']
            except KeyError:
                b_to_a = 1.0
            try:
                c_to_a = table['halo_c_to_a']
            except KeyError:
                c_to_a = 1.0
            try:
                halo_axisA_x = table['halo_axisA_x']
                halo_axisA_y = table['halo_axisA_y']
                halo_axisA_z = table['halo_axisA_z']
            except KeyError:
                with NumpyRNGContext(seed):
                    v = random_unit_vectors_3d(len(table))
                    halo_axisA_x = v[:,0]
                    halo_axisA_y = v[:,1]
                    halo_axisA_z = v[:,2]
            try:
                halo_axisC_x = table['halo_axisC_x']
                halo_axisC_y = table['halo_axisC_y']
                halo_axisC_z = table['halo_axisC_z']
            except KeyError:
                with NumpyRNGContext(seed):
                    v = random_unit_vectors_3d(len(table))
                    halo_axisC_x = v[:,0]
                    halo_axisC_y = v[:,1]
                    halo_axisC_z = v[:,2]
        else:
            try:
                b_to_a = np.atleast_1d(kwargs['b_to_a'])
            except KeyError:
                b_to_a = 1.0
            try:
                c_to_a = np.atleast_1d(kwargs['c_to_a'])
            except KeyError:
                c_to_a = 1.0
            try:
                halo_axisA_x = np.atleast_1d(kwargs['halo_axisA_x'])
                halo_axisA_y = np.atleast_1d(kwargs['halo_axisA_y'])
                halo_axisA_z = np.atleast_1d(kwargs['halo_axisA_z'])
            except KeyError:
                with NumpyRNGContext(seed):
                    v = random_unit_vectors_3d(1)
                    halo_axisC_x = v[:,0]
                    halo_axisC_y = v[:,1]
                    halo_axisC_z = v[:,2]
            try:
                halo_axisC_x = np.atleast_1d(kwargs['halo_axisC_x'])
                halo_axisC_y = np.atleast_1d(kwargs['halo_axisC_y'])
                halo_axisC_z = np.atleast_1d(kwargs['halo_axisC_z'])
            except KeyError:
                with NumpyRNGContext(seed):
                    v = random_unit_vectors_3d(len(halo_axisA_x))
                    halo_axisC_x = v[:,0]
                    halo_axisC_y = v[:,1]
                    halo_axisC_z = v[:,2]

        v1 = np.vstack((halo_axisA_x, halo_axisA_y, halo_axisA_z)).T
        v3 = np.vstack((halo_axisC_x, halo_axisC_y, halo_axisC_z)).T
        v2 = np.cross(v1,v3)

        with NumpyRNGContext(seed):
            phi = np.random.uniform(0, 2*np.pi, Npts)
            uran = np.random.rand(Npts)*2 - 1

        cos_t = uran
        sin_t = np.sqrt((1.-cos_t*cos_t))

        b_to_a, c_to_a = self.anisotropy_bias_response(b_to_a, c_to_a)

        c_to_b = c_to_a/b_to_a

        # temporarily use x-axis as the major axis
        x = 1.0/c_to_a*sin_t * np.cos(phi)
        y = 1.0/c_to_b*sin_t * np.sin(phi)
        z = cos_t
        x_correlated_axes = np.vstack((x, y, z)).T

        x_axes = np.tile((1, 0, 0), Npts).reshape((Npts, 3))
        major_axes = v1

        matrices = rotation_matrices_from_basis(v1,v2,v3)

        # rotate x-axis into the major axis
        #angles = angles_between_list_of_vectors(x_axes, major_axes)
        #rotation_axes = vectors_normal_to_planes(x_axes, major_axes)
        #matrices = rotation_matrices_from_angles(angles, rotation_axes)

        correlated_axes = rotate_vector_collection(matrices, x_correlated_axes)
        #correlated_axes = x_correlated_axes

        return correlated_axes[:, 0], correlated_axes[:, 1], correlated_axes[:, 2]
    def assign_positions(self, **kwargs):
        """
        assign satellite positions based on subhalo radial positions and random angular positions.
        """

        if 'table' in kwargs.keys():
            table = kwargs['table']
            halo_x = table['halo_x']
            halo_y = table['halo_y']
            halo_z = table['halo_z']
            halo_hostid = table['halo_hostid']
            halo_id = table['halo_id']
            b_to_a = table['halo_b_to_a']
            c_to_a = table['halo_c_to_a']
            halo_axisA_x = table['halo_axisA_x']
            halo_axisA_y = table['halo_axisA_y']
            halo_axisA_z = table['halo_axisA_z']
            halo_axisC_x = table['halo_axisC_x']
            halo_axisC_y = table['halo_axisC_y']
            halo_axisC_z = table['halo_axisC_z']
            concentration = table['halo_nfw_conc']
            rvir = table['halo_rvir']
            try:
                Lbox = kwargs['Lbox']
            except KeyError:
                Lbox = self._Lbox
        else:
            halo_x = kwargs['halo_x']
            halo_y = kwargs['halo_y']
            halo_z = kwargs['halo_z']
            halo_hostid = kwargs['halo_hostid']
            halo_id = kwargs['halo_id']
            b_to_a = kwargs['halo_b_to_a']
            c_to_a = kwargs['halo_c_to_a']
            halo_axisA_x = kwargs['halo_axisA_x']
            halo_axisA_y = kwargs['halo_axisA_y']
            halo_axisA_z = kwargs['halo_axisA_z']
            halo_axisC_x = kwargs['halo_axisC_x']
            halo_axisC_y = kwargs['halo_axisC_y']
            halo_axisC_z = kwargs['halo_axisC_z']
            concentration = kwargs['halo_nfw_conc']
            rvir = tabel['halo_rvir']
            try:
                Lbox = kwargs['Lbox']
            except KeyError:
                Lbox = self._Lbox

        Npts = len(halo_x)

        # get host halo properties
        inds1, inds2 = crossmatch(halo_hostid, halo_id)

        # some sub-haloes point to a host that does not exist
        no_host = ~np.in1d(halo_hostid, halo_id)
        if np.sum(no_host) > 0:
            msg = ("There are {0} sub-haloes with no host halo.".format(
                np.sum(no_host)))
            warn(msg)

        host_halo_concentration = np.zeros(Npts)
        host_halo_concentration[inds1] = concentration[inds2]

        host_halo_rvir = np.zeros(Npts)
        host_halo_rvir[inds1] = rvir[inds2]

        host_b_to_a = np.zeros(Npts)
        host_b_to_a[inds1] = b_to_a[inds2]
        host_c_to_a = np.zeros(Npts)
        host_c_to_a[inds1] = c_to_a[inds2]

        major_axis = np.vstack((halo_axisA_x, halo_axisA_y, halo_axisA_z)).T
        minor_axis = np.vstack((halo_axisC_x, halo_axisC_y, halo_axisC_z)).T
        inter_axis = np.cross(major_axis, minor_axis)

        host_major_axis = np.zeros((Npts, 3))
        host_inter_axis = np.zeros((Npts, 3))
        host_minor_axis = np.zeros((Npts, 3))
        host_major_axis[inds1] = major_axis[inds2]
        host_inter_axis[inds1] = inter_axis[inds2]
        host_minor_axis[inds1] = minor_axis[inds2]

        # host x,y,z-position
        halo_x[inds1] = halo_x[inds2]
        halo_y[inds1] = halo_y[inds2]
        halo_z[inds1] = halo_z[inds2]

        # host halo centric positions
        phi = np.random.uniform(0, 2 * np.pi, Npts)
        uran = np.random.rand(Npts) * 2 - 1

        cos_t = uran
        sin_t = np.sqrt((1. - cos_t * cos_t))

        b_to_a, c_to_a = self.anisotropy_bias_response(host_b_to_a,
                                                       host_c_to_a)

        c_to_b = c_to_a / b_to_a

        # temporarily use x-axis as the major axis
        x = 1.0 / c_to_a * sin_t * np.cos(phi)
        y = 1.0 / c_to_b * sin_t * np.sin(phi)
        z = cos_t

        x_correlated_axes = np.vstack((x, y, z)).T

        x_axes = np.tile((1, 0, 0), Npts).reshape((Npts, 3))

        matrices = rotation_matrices_from_basis(host_major_axis,
                                                host_inter_axis,
                                                host_minor_axis)

        # rotate x-axis into the major axis
        #angles = angles_between_list_of_vectors(x_axes, major_axes)
        #rotation_axes = vectors_normal_to_planes(x_axes, major_axes)
        #matrices = rotation_matrices_from_angles(angles, rotation_axes)

        correlated_axes = rotate_vector_collection(matrices, x_correlated_axes)

        x, y, z = correlated_axes[:, 0], correlated_axes[:,
                                                         1], correlated_axes[:,
                                                                             2]

        nfw = NFWPhaseSpace(conc_mass_model='direct_from_halo_catalog', )
        dimensionless_radial_distance = nfw._mc_dimensionless_radial_distance(
            host_halo_concentration)

        x *= dimensionless_radial_distance
        y *= dimensionless_radial_distance
        z *= dimensionless_radial_distance

        x *= host_halo_rvir
        y *= host_halo_rvir
        z *= host_halo_rvir

        a = 1
        b = b_to_a * a
        c = c_to_a * a
        T = (c**2 - b**2) / (c**2 - a**2)
        q = b / a
        s = c / a

        x *= np.sqrt(q * s)
        y *= np.sqrt(q * s)
        z *= np.sqrt(q * s)

        # host-halo centric radial distance
        r = np.sqrt(x * x + y * y + z * z)

        # move back into original cordinate system
        xx = halo_x + x
        yy = halo_y + y
        zz = halo_z + z

        xx[no_host] = halo_x[no_host]
        yy[no_host] = halo_y[no_host]
        zz[no_host] = halo_z[no_host]

        # account for PBCs
        xx, yy, zz = wrap_coordinates(xx, yy, zz, Lbox)

        if 'table' in kwargs.keys():
            # assign satellite galaxy positions
            try:
                mask = (table['gal_type'] == 'satellites')
            except KeyError:
                mask = np.array([True] * len(table))
                msg = (
                    "`gal_type` not indicated in `table`.",
                    "The orientation is being assigned for all galaxies in the `table`."
                )
                print(msg)

            table['x'] = halo_x * 1.0
            table['y'] = halo_y * 1.0
            table['z'] = halo_z * 1.0

            table['x'][mask] = xx[mask]
            table['y'][mask] = yy[mask]
            table['z'][mask] = zz[mask]

            table['r'] = 0.0
            table['r'][mask] = r[mask]

            table['halo_x'][mask] = halo_x[mask]
            table['halo_y'][mask] = halo_y[mask]
            table['halo_z'][mask] = halo_z[mask]

            return table
        else:
            x = xx
            y = yy
            z = zz
            return np.vstack((x, y, z)).T
    def assign_positions(self, **kwargs):
        """
        assign satellite positions based on subhalo radial positions and random angular positions.
        """

        if 'table' in kwargs.keys():
            table = kwargs['table']
            halo_x = table['halo_x']
            halo_y = table['halo_y']
            halo_z = table['halo_z']
            halo_axisA_x = table['halo_axisA_x']
            halo_axisA_y = table['halo_axisA_y']
            halo_axisA_z = table['halo_axisA_z']
            halo_hostid = table['halo_hostid']
            halo_id = table['halo_id']
            try:
                Lbox = kwargs['Lbox']
            except KeyError:
                Lbox = self._Lbox
        else:
            halo_x = kwargs['halo_x']
            halo_y = kwargs['halo_y']
            halo_z = kwargs['halo_z']
            halo_hostid = kwargs['halo_hostid']
            halo_id = kwargs['halo_id']
            try:
                Lbox = kwargs['Lbox']
            except KeyError:
                Lbox = self._Lbox

        # get subhalo positions
        x = halo_x * 1.0
        y = halo_y * 1.0
        z = halo_z * 1.0

        # get host halo positions
        inds1, inds2 = crossmatch(halo_hostid, halo_id)
        # x-position
        halo_x[inds1] = halo_x[inds2]
        # y-position
        halo_y[inds1] = halo_y[inds2]
        # z-position
        halo_z[inds1] = halo_z[inds2]

        # get host halo orientation
        host_halo_axisA_x = halo_axisA_x
        host_halo_axisA_x[inds1] = halo_axisA_x[inds2]
        host_halo_axisA_y = halo_axisA_y
        host_halo_axisA_y[inds1] = halo_axisA_y[inds2]
        host_halo_axisA_z = halo_axisA_z
        host_halo_axisA_z[inds1] = halo_axisA_z[inds2]
        host_halo_mjor_axes = np.vstack(
            (halo_axisA_x, halo_axisA_y, halo_axisA_z)).T

        # calculate radial positions
        vec_r, r = radial_distance(x, y, z, halo_x, halo_y, halo_z, Lbox)

        # rotate radial vectors arond halo major axis
        N = len(x)
        rot_angles = np.random.uniform(0.0, 2 * np.pi, N)
        rot_axes = host_halo_mjor_axes
        rot_m = rotation_matrices_from_angles(rot_angles, rot_axes)

        new_vec_r = rotate_vector_collection(rot_m, vec_r)
        xx = new_vec_r[:, 0]
        yy = new_vec_r[:, 1]
        zz = new_vec_r[:, 2]

        # move back into original cordinate system
        xx = halo_x + xx
        yy = halo_y + yy
        zz = halo_z + zz

        # account for PBCs
        mask = (xx < 0.0)
        xx[mask] = xx[mask] + Lbox[0]
        mask = (xx > Lbox[0])
        xx[mask] = xx[mask] - Lbox[0]
        mask = (yy < 0.0)
        yy[mask] = yy[mask] + Lbox[1]
        mask = (yy > Lbox[1])
        yy[mask] = yy[mask] - Lbox[1]
        mask = (zz < 0.0)
        zz[mask] = zz[mask] + Lbox[2]
        mask = (zz > Lbox[2])
        zz[mask] = zz[mask] - Lbox[2]

        if 'table' in kwargs.keys():
            # assign satellite galaxy positions
            try:
                mask = (table['gal_type'] == 'satellites')
            except KeyError:
                mask = np.array([True] * len(table))
                msg = (
                    "`gal_type` not indicated in `table`.",
                    "The orientation is being assigned for all galaxies in the `table`."
                )
                print(msg)

            table['x'] = halo_x * 1.0
            table['y'] = halo_y * 1.0
            table['z'] = halo_z * 1.0

            table['x'][mask] = xx[mask]
            table['y'][mask] = yy[mask]
            table['z'][mask] = zz[mask]

            table['halo_x'][mask] = halo_x[mask]
            table['halo_y'][mask] = halo_y[mask]
            table['halo_z'][mask] = halo_z[mask]

            return table
        else:
            x = xx
            y = yy
            z = zz
            return np.vstack((x, y, z)).T
Example #5
0
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
    --------
    Create two set of 3D vectors

    >>> npts = int(1e4)
    >>> x = np.random.random((npts, 3))
    >>> y = np.random.random((npts, 3))

    Define the parameter `p` fpr each pair of vectors

    >>> p = np.random.uniform(0, 1, npts)

    Find a set of vectors between the two sets

    >>> v = vectors_between_list_of_vectors(x, y, p)

    Note that `p` determines how close the new vector is to 
    the corresponding vectors in the original sets. 

    >>> 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))