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 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
def assign_central_orientation(self, **kwargs): r""" Assign a set of three orthoganl unit vectors indicating the orientation of the galaxies' major, intermediate, and minor axis Parameters ========== halo_axisA_x, halo_axisA_y, halo_axisA_z : array_like x,y,z components of halo alignment axis Returns ======= major_aixs, intermediate_axis, minor_axis : numpy nd.arrays arrays of galaxies' axes """ if 'table' in kwargs.keys(): table = kwargs['table'] Ax = table[self.list_of_haloprops_needed[0]] Ay = table[self.list_of_haloprops_needed[1]] Az = table[self.list_of_haloprops_needed[2]] else: Ax = kwargs[self.list_of_haloprops_needed[0]] Ay = kwargs[self.list_of_haloprops_needed[1]] Az = kwargs[self.list_of_haloprops_needed[2]] # number of haloes N = len(Ax) # set prim_gal_axis orientation major_input_vectors = np.vstack((Ax, Ay, Az)).T theta_ma = self.misalignment_rvs(size=N) # rotate alignment vector by theta_ma ran_vecs = random_unit_vectors_3d(N) mrot = rotation_matrices_from_angles(theta_ma, ran_vecs) A_v = rotate_vector_collection(rotm, major_input_vectors) # randomly set secondary axis orientation B_v = random_perpendicular_directions(A_v) # the tertiary axis is determined C_v = vectors_normal_to_planes(A_v, B_v) # depending on the prim_gal_axis, assign correlated axes if self.prim_gal_axis == 'A': major_v = A_v inter_v = B_v minor_v = C_v elif self.prim_gal_axis == 'B': major_v = B_v inter_v = A_v minor_v = C_v elif self.prim_gal_axis == 'C': major_v = B_v inter_v = C_v minor_v = A_v else: msg = ('primary galaxy axis {0} is not recognized.'.format( self.prim_gal_axis)) raise ValueError(msg) if 'table' in kwargs.keys(): try: mask = (table['gal_type'] == self.gal_type) except KeyError: mask = np.array([True] * len(table)) 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 # 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 else: return major_v, inter_v, minor_v
def assign_satellite_orientation(self, **kwargs): r""" assign a a set of three orthoganl unit vectors indicating the orientation of the galaxies' major, intermediate, and minor axis Returns ======= major_aixs, intermediate_axis, minor_axis : numpy nd.arrays arrays of galaxies' axies """ if 'table' in kwargs.keys(): table = kwargs['table'] try: Lbox = kwargs['Lbox'] except KeyError: Lbox = self._Lbox else: try: Lbox = kwargs['Lbox'] except KeyError: Lbox = self._Lbox # calculate the radial vector between satellites and centrals major_input_vectors, r = self.get_radial_vector(Lbox=Lbox, **kwargs) # check for length 0 radial vectors mask = (r <= 0.0) | (~np.isfinite(r)) if np.sum(mask) > 0: major_input_vectors[mask, 0] = np.random.random((np.sum(mask))) major_input_vectors[mask, 1] = np.random.random((np.sum(mask))) major_input_vectors[mask, 2] = np.random.random((np.sum(mask))) msg = ( '{0} galaxies have a radial distance equal to zero (or infinity) from their host. ' 'These galaxies will be re-assigned random alignment vectors.'. format(int(np.sum(mask)))) warn(msg) # set prim_gal_axis orientation theta_ma = self.misalignment_rvs(size=N) # rotate alignment vector by theta_ma ran_vecs = random_unit_vectors_3d(N) mrot = rotation_matrices_from_angles(theta_ma, ran_vecs) A_v = rotate_vector_collection(rotm, major_input_vectors) # check for nan vectors mask = (~np.isfinite(np.sum(np.prod(A_v, axis=-1)))) if np.sum(mask) > 0: A_v[mask, 0] = np.random.random((np.sum(mask))) A_v[mask, 1] = np.random.random((np.sum(mask))) A_v[mask, 2] = np.random.random((np.sum(mask))) msg = ( '{0} correlated alignment axis(axes) were not found to be not finite. ' 'These will be re-assigned random vectors.'.format( int(np.sum(mask)))) warn(msg) # randomly set secondary axis orientation B_v = random_perpendicular_directions(A_v) # the tertiary axis is determined C_v = vectors_normal_to_planes(A_v, B_v) # use galaxy major axis as orientation axis major_v = A_v inter_v = B_v minor_v = C_v if 'table' in kwargs.keys(): try: mask = (table['gal_type'] == self.gal_type) 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) # 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 # 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 else: return major_v, inter_v, minor_v
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]