def run(self,positions,delay=50,length=150): ## Builds gridsearch and finds nearby neighbors gsp = GriSPy(positions, N_cells = 10, periodic={0:(0,self.width), 1:(0,self.height)}) dub = max(self.alignCohRadius, self.sepRadius) _, self.neighbor_indices = gsp.bubble_neighbors(positions,distance_upper_bound=dub) ## Calculates and records data for each individual boid for i in range(len(self.boids)): this_boid = self.boids[i] neighbors = [self.boids[j] for j in self.neighbor_indices[i]] ret_atts = this_boid.run(neighbors) for i,att in enumerate(ret_atts): self.lists[i].append(att) ## Manages the number of instances calculated and # halting criteria. self.frames+=1 if self.frames == delay: self.record = True if self.frames == delay+length: self.stop = True if self.screen_cap and self.frames==100: self.capture = True if self.screen_cap and self.frames==101: self.capture = False
def center_overdensity(center, galaxy_data, max_velocity): # calculate overdensity of cluster # select 300 random points (RA and dec) n = 300 ra_random = np.random.uniform(low=min(galaxy_data[:, 0]), high=max(galaxy_data[:, 0]), size=n) dec_random = np.random.uniform(low=min(galaxy_data[:, 1]), high=max(galaxy_data[:, 1]), size=n) points = np.vstack((ra_random, dec_random)).T assert points.shape == (n, 2) # select all galaxies within max velocity velocity_bin = galaxy_data[ abs(redshift_to_velocity(galaxy_data[:, 2], center.z)) <= max_velocity] virial_gsp = GriSPy(velocity_bin[:, :2], metric="haversine") max_dist = linear_to_angular_dist(0.5 * u.Mpc / u.littleh, center.z).value v_dist, v_idx = virial_gsp.bubble_neighbors(points, distance_upper_bound=max_dist) # find the N(0.5) mean and rms for the 300 points N_arr = np.array([len(idx) for idx in v_idx]) N_mean = np.mean(N_arr) N_rms = np.sqrt(np.mean(N_arr**2)) # rms D = (center.N - N_mean) / N_rms return D
def test_valid_periodic_empty(grispy_init): # both initializations should be equivalent periodic_explicit = {0: None, 1: None, 2: None} periodic_implicit = dict() exp = GriSPy(grispy_init["data"], periodic=periodic_explicit) imp = GriSPy(grispy_init["data"], periodic=periodic_implicit) assert exp == imp
def test_invalid_metric(grispy_init): # Metric name is not a string bad_metric = 42 with pytest.raises(ValueError): GriSPy(grispy_init["data"], metric=bad_metric) # Metric name is wrong bad_metric = "euclidean" with pytest.raises(ValueError): GriSPy(grispy_init["data"], metric=bad_metric)
def test_invalid_periodic(self, gsp): # Axis 0 with invalid type: string instead of dict bad_periodic = '{0: [-1, 1], 1: (-1, 1), 2: None}' with pytest.raises(TypeError): GriSPy( self.data, N_cells=self.N_cells, periodic=bad_periodic, metric=self.metric, copy_data=self.copy_data, ) # Axis 0 with invalid value type: list instead of tuple bad_periodic = {0: [-1, 1], 1: (-1, 1), 2: None} with pytest.raises(TypeError): GriSPy( self.data, N_cells=self.N_cells, periodic=bad_periodic, metric=self.metric, copy_data=self.copy_data, ) # Axis is not integer bad_periodic = {'A': (-1, 1), 1: (-1, 1), 2: None} with pytest.raises(TypeError): GriSPy( self.data, N_cells=self.N_cells, periodic=bad_periodic, metric=self.metric, copy_data=self.copy_data, ) # Edge 0 is larger than edge 1 bad_periodic = {0: (1, -1), 1: (-1, 1), 2: None} with pytest.raises(ValueError): GriSPy( self.data, N_cells=self.N_cells, periodic=bad_periodic, metric=self.metric, copy_data=self.copy_data, ) # Edge has wrong type bad_periodic = {0: (-1, [1]), 1: (-1, 1), 2: None} with pytest.raises(TypeError): GriSPy( self.data, N_cells=self.N_cells, periodic=bad_periodic, metric=self.metric, copy_data=self.copy_data, )
def consolidateSchools(schoolData, radius, verbose): radius = radius / 1000 #Create empty dataframes #resultsDF = pd.DataFrame(columns=['pointsInRadius','nearestPoint']) #def computeBandwidth(lat,lon,radius): if (verbose): print('Reading.....') #schoolData = pd.read_excel(args.File) latlonArray = schoolData[['Lat', 'Lon']].to_numpy() #Build grid if (verbose): print('Building Grid.....') gsp = GriSPy(latlonArray) degPerKM = 0.0089 #Degrees per 1 km upper_radii = radius * degPerKM numSchools = len(schoolData.index) dropList = [] numberDropped = 0 for i in range(numSchools): center = latlonArray[i].reshape(1, 2) #Find all other points in latlonArray which are within upper_radii #from the current center point bubble_dist, bubble_ind = gsp.bubble_neighbors( center, distance_upper_bound=upper_radii) #These are the indices of schools inside the radius bubble_ind = np.array(bubble_ind) schoolsInRadius = bubble_ind.size - 1 #A bit of a hack but don't drop *multiple* schools -- only one. If more #than one school is found that probably means it's a high density area. #For later -- use multiple column matching (names etc) if schoolsInRadius == 1: numberDropped = numberDropped + 1 #print(bubble_ind[0][0]) #drop the data - ignore errors if schoolData = schoolData.drop([bubble_ind[0][0]], errors='ignore') #print(dropList) #schoolData.drop(dropList) schoolData = schoolData.reset_index(drop=True) print('NUMBER DROPPED = ', numberDropped) #print(schoolData.head()) input() return (schoolData)
def test_load_invalidfile(self, grid): # Invalid filename file = ["invalid_file.gsp"] with mock.patch('builtins.open', mock.mock_open()): with pytest.raises(TypeError): GriSPy.load_grid(file=file) # Invalid instance of GriSPy file = "invalid_file.gsp" with mock.patch('os.path.isfile', return_value=True): with mock.patch('pickle.load', return_value=grid.__dict__): with pytest.raises(TypeError): GriSPy.load_grid(file=file)
def gsp(self): self.lbox = 10.0 self.data = np.array([[2, 0, 0], [-2, 0, 0], [0, 2, 0], [0, -2, 0], [0, 0, 2], [0, 0, -2]]) self.eps = 1e-6 return GriSPy(self.data)
def test_load_samestate_ag(self, grid): file = "test_load_grid.gsp" with mock.patch('builtins.open', mock.mock_open()): with mock.patch('pickle.dump') as pd: # Save a first time grid.save_grid(file=file) args_pd, kwargs_pd = pd.call_args_list[0] # Load again to check the state is the same with mock.patch('os.path.isfile', return_value=True): with mock.patch('pickle.load', return_value=args_pd[0]): gsp_tmp = GriSPy.load_grid(file) assert_(isinstance(gsp_tmp["dim"], int)) assert_(isinstance(gsp_tmp["data"], np.ndarray)) assert_(isinstance(gsp_tmp["k_bins"], np.ndarray)) assert_(isinstance(gsp_tmp["metric"], str)) assert_(isinstance(gsp_tmp["N_cells"], int)) assert_(isinstance(gsp_tmp["grid"], dict)) assert_(isinstance(gsp_tmp["periodic"], dict)) assert_(isinstance(gsp_tmp["periodic_flag"], bool)) assert_(isinstance(gsp_tmp["time"], dict)) assert_equal(grid["data"], gsp_tmp["data"]) assert_equal(grid["dim"], gsp_tmp["dim"]) assert_equal(grid["N_cells"], gsp_tmp["N_cells"]) assert_equal(grid["metric"], gsp_tmp["metric"]) assert_equal(grid["periodic"], gsp_tmp["periodic"]) assert_equal(grid["grid"], gsp_tmp["grid"]) assert_equal(grid["k_bins"], gsp_tmp["k_bins"]) assert_equal(grid["time"], gsp_tmp["time"])
def def_metric(metric): data = np.random.uniform(-10, 10, size=(10, 2)) periodic = {0: None, 1: None} return GriSPy( data=data, N_cells=2, copy_data=False, periodic=periodic, metric=metric, )
def test_custom_distance_lev(): def levenshtein(c0, centres, dim): c0 = tuple(c0) distances = np.empty(len(centres)) for idx, c1 in enumerate(centres): c1 = tuple(c1) dis = textdistance.levenshtein(c0, c1) distances[idx] = dis return distances random = np.random.RandomState(42) Npoints = 10**3 Ncentres = 2 dim = 2 Lbox = 100.0 data = random.uniform(0, Lbox, size=(Npoints, dim)) centres = random.uniform(0, Lbox, size=(Ncentres, dim)) gsp = GriSPy(data, N_cells=20, metric=levenshtein) upper_radii = 10.0 lev_dist, lev_ind = gsp.bubble_neighbors(centres, distance_upper_bound=upper_radii) assert len(centres) == len(lev_dist) == len(lev_ind) assert np.all(lev_dist[0] == 2) assert np.all(lev_dist[1] == 2) assert np.all(lev_ind[0] == [ 648, 516, 705, 910, 533, 559, 61, 351, 954, 214, 90, 645, 846, 818, 39, 433, 7, 700, 2, 364, 547, 427, 660, 548, 333, 246, 193, 55, 83, 159, 684, 310, 777, 112, 535, 780, 334, 300, 467, 30, 613, 564, 134, 534, 435, 901, 296, 800, 391, 321, 763, 208, 42, 413, 97 ]) assert np.all(lev_ind[1] == [ 580, 740, 498, 89, 610, 792, 259, 647, 58, 722, 360, 685, 552, 619, 6, 555, 935, 268, 615, 661, 680, 817, 75, 919, 922, 927, 52, 77, 859, 70, 544, 189, 340, 691, 453, 570, 126, 140, 67, 284, 662, 590, 527 ])
def test_invalid_copy_data(self, gsp): # copy_data is not bool bad_copy_data = 42 with pytest.raises(TypeError): GriSPy( self.data, N_cells=self.N_cells, periodic=self.periodic, metric=self.metric, copy_data=bad_copy_data, )
def test_invalid_data(self, gsp): bad_data = np.random.uniform(-1, 1, size=(100, 3)) bad_data[42, 1] = np.inf # add one invalid value with pytest.raises(ValueError): GriSPy( bad_data, N_cells=self.N_cells, periodic=self.periodic, metric=self.metric, copy_data=self.copy_data, )
def test__init__A_01(): # Data type data = 4 periodic = {0: None, 1: None} with pytest.raises(TypeError, match=r".*must be a numpy array*"): GriSPy( data=data, N_cells=2, copy_data=False, periodic=periodic, metric="sphere", )
def test_floatX_precision(self, floatX): rng = np.random.default_rng(1234) data_floatX = rng.random(size=(1000, 3), dtype=floatX) centres_floatX = rng.random(size=(100, 3), dtype=floatX) upper_radii = 0.2 gsp_floatX = GriSPy(data_floatX) dist_floatX, ind_floatX = gsp_floatX.bubble_neighbors( centres_floatX, distance_upper_bound=upper_radii ) eps = np.finfo(floatX).resolution for i, ind_list in enumerate(ind_floatX): for j, il in enumerate(ind_list): delta = data_floatX[il] - centres_floatX[i] dist = np.sqrt(np.sum(delta**2)) assert_(dist <= upper_radii + eps) gsp_dist = dist_floatX[i][j] assert_(abs(dist - gsp_dist) <= eps)
def test__init__A_04(): # Data value data = np.array([[]]) periodic = {0: None, 1: None} with pytest.raises(ValueError): GriSPy( data=data, N_cells=2, copy_data=False, periodic=periodic, metric="sphere", )
def find_number_count(center, galaxies, distance=0.5 * u.Mpc / u.littleh): """ Computes number count of the center of interest, within a distance d. Number count, N(d) is the number of galaxies surrounding the center of interest. Note: Uses haversine metric so angular coordinates must be used. Parameters ---------- center: ndarray, shape (1, 3) or cluster.Cluster. Center of interest galaxies: ndarray, shape (n, 2) Sample of galaxies to search. distance: float with units of [Mpc/littleh], default: 0.5*u.Mpc/u.littleh distance, d. Returns ------- len(n_points): int N(d), Number of points around center of interest, within distance d. """ if isinstance(center, Cluster): coords = [center.ra, center.dec] z = center.z else: coords = center[:2] z = center[2] N_gsp = GriSPy(galaxies[:, :2], metric="haversine") distance = linear_to_angular_dist(distance, z).value # convert to angular distance n_dist, n_idx = N_gsp.bubble_neighbors(np.array([coords]), distance_upper_bound=distance) n_points = galaxies[tuple(n_idx)] return len(n_points)
def test_invalid_Ncells(self, gsp): # N_cells is not integer bad_N_cells = 10.5 with pytest.raises(TypeError): GriSPy( self.data, N_cells=bad_N_cells, periodic=self.periodic, metric=self.metric, copy_data=self.copy_data, ) # N_cells is not positive bad_N_cells = -10 with pytest.raises(ValueError): GriSPy( self.data, N_cells=bad_N_cells, periodic=self.periodic, metric=self.metric, copy_data=self.copy_data, )
def test_invalid_metric(self, gsp): # Metric name is not a string bad_metric = 42 with pytest.raises(ValueError): GriSPy( self.data, N_cells=self.N_cells, periodic=self.periodic, metric=bad_metric, copy_data=self.copy_data, ) # Metric name is wrong bad_metric = "euclidean" with pytest.raises(ValueError): GriSPy( self.data, N_cells=self.N_cells, periodic=self.periodic, metric=bad_metric, copy_data=self.copy_data, )
def gsp(self): # Define valid input data self.centres = np.random.uniform(-1, 1, size=(10, 3)) self.upper_radii = 0.8 self.lower_radii = 0.4 self.kind = "quicksort" self.sorted = True self.n = 5 data = np.random.uniform(-1, 1, size=(100, 3)) periodic = {0: (-1, 1)} return GriSPy(data, periodic=periodic)
def test_custom_distance_hamming(): def hamming(c0, centres, dim): c0 = c0.reshape((-1, dim)) d = cdist(c0, centres, metric="hamming").reshape((-1, )) return d random = np.random.RandomState(42) Npoints = 10**3 Ncentres = 2 dim = 2 Lbox = 100.0 data = random.uniform(0, Lbox, size=(Npoints, dim)) centres = random.uniform(0, Lbox, size=(Ncentres, dim)) gsp = GriSPy(data, N_cells=20, metric=hamming) upper_radii = 10.0 ham_dist, ham_ind = gsp.bubble_neighbors(centres, distance_upper_bound=upper_radii) assert len(centres) == len(ham_dist) == len(ham_ind) assert np.all(ham_dist[0] == 1) assert np.all(ham_dist[1] == 1) assert np.all(ham_ind[0] == [ 648, 516, 705, 910, 533, 559, 61, 351, 954, 214, 90, 645, 846, 818, 39, 433, 7, 700, 2, 364, 547, 427, 660, 548, 333, 246, 193, 55, 83, 159, 684, 310, 777, 112, 535, 780, 334, 300, 467, 30, 613, 564, 134, 534, 435, 901, 296, 800, 391, 321, 763, 208, 42, 413, 97 ]) assert np.all(ham_ind[1] == [ 580, 740, 498, 89, 610, 792, 259, 647, 58, 722, 360, 685, 552, 619, 6, 555, 935, 268, 615, 661, 680, 817, 75, 919, 922, 927, 52, 77, 859, 70, 544, 189, 340, 691, 453, 570, 126, 140, 67, 284, 662, 590, 527 ])
def test_floatX_precision(dim, N_cells, floatX): rng = np.random.default_rng(1234) data_floatX = rng.random(size=(100, dim), dtype=floatX) centres_floatX = rng.random(size=(10, dim), dtype=floatX) upper_radii = 0.2 gsp_floatX = GriSPy(data_floatX, N_cells=N_cells) dist_floatX, ind_floatX = gsp_floatX.bubble_neighbors( centres_floatX, distance_upper_bound=upper_radii) eps = np.finfo(floatX).resolution decimal = np.abs(int(np.log10(eps))) for i, ind_list in enumerate(ind_floatX): for j, il in enumerate(ind_list): gsp_dist = dist_floatX[i][j] delta = data_floatX[il] - centres_floatX[i] dist = np.linalg.norm(delta) assert (dist <= upper_radii).all() npt.assert_almost_equal(dist, gsp_dist, decimal)
def gsp(self): np.random.seed(1234) npoints = 10**5 self.lbox = 100.0 self.centres = self.lbox * (0.5 - np.random.rand(1, 10).T) self.data = np.random.uniform(-0.5 * self.lbox, 0.5 * self.lbox, size=(npoints, 1)) self.upper_radii = 0.25 * self.lbox self.lower_radii = 0.20 * self.lbox self.n_nearest = 32 self.eps = 1e-6 return GriSPy(self.data)
def gsp(self): np.random.seed(1234) self.lbox = 100.0 self.upper_radii = 0.25 * self.lbox self.lower_radii = 0.20 * self.lbox self.n_nearest = 32 self.eps = 1e-6 ############################################ # # Follow # http://mathworld.wolfram.com/HyperspherePointPicking.html # Marsaglia, G. # "Choosing a Point from the Surface of a Sphere." # Ann. Math. Stat. 43, 645-646, 1972. # ############################################ npoints = 10 ** 5 x = np.random.uniform(-1.0, 1.0, size=(npoints, 1)) y = np.random.uniform(-1.0, 1.0, size=(npoints, 1)) z = np.random.uniform(-1.0, 1.0, size=(npoints, 1)) w = np.random.uniform(-1.0, 1.0, size=(npoints, 1)) tttt = (x ** 2 + y ** 2 < 1.0) * (z ** 2 + w ** 2 < 1.0) npoints = np.sum(tttt) self.radius = self.lbox * np.random.rand(npoints) x = x[tttt] y = y[tttt] z = z[tttt] w = w[tttt] tttt = np.sqrt((1.0 - x ** 2 - y ** 2) / (z ** 2 + w ** 2)) x = self.radius * x y = self.radius * y z = self.radius * z * tttt w = self.radius * w * tttt tttt = np.sqrt(x ** 2 + y ** 2 + z ** 2 + w ** 2) assert_almost_equal(self.radius, tttt, decimal=12) self.data = np.array([x, y, z, w]).T ############################################ ############################################ return GriSPy(self.data)
def gsp(self): np.random.seed(1234) self.centres = np.random.rand(3, 5).T self.data = np.array([ [0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1], ]) self.upper_radii = [0.7] self.lower_radii = [0.5] self.n_nearest = 5 return GriSPy(self.data)
def gsp(self, valid_input): ############################################ # # Follow # http://mathworld.wolfram.com/HyperspherePointPicking.html # Marsaglia, G. # "Choosing a Point from the Surface of a Sphere." # Ann. Math. Stat. 43, 645-646, 1972. # ############################################ rng = np.random.default_rng(1234) npoints = 10**5 x = rng.uniform(-1.0, 1.0, size=(npoints, 1)) y = rng.uniform(-1.0, 1.0, size=(npoints, 1)) z = rng.uniform(-1.0, 1.0, size=(npoints, 1)) w = rng.uniform(-1.0, 1.0, size=(npoints, 1)) tttt = (x**2 + y**2 < 1.0) * (z**2 + w**2 < 1.0) npoints = np.sum(tttt) self.radius = valid_input["lbox"] * rng.random(npoints) x = x[tttt] y = y[tttt] z = z[tttt] w = w[tttt] tttt = np.sqrt((1.0 - x**2 - y**2) / (z**2 + w**2)) x = self.radius * x y = self.radius * y z = self.radius * z * tttt w = self.radius * w * tttt tttt = np.sqrt(x**2 + y**2 + z**2 + w**2) npt.assert_almost_equal(self.radius, tttt, decimal=12) data = np.array([x, y, z, w]).T return GriSPy(data)
Ncentres = 2 dim = 2 Lbox = 100.0 np.random.seed(2) data = np.random.uniform(0, Lbox, size=(Npoints, dim)) centres = np.random.uniform(0, Lbox, size=(Ncentres, dim)) # Grispy params upper_radii = 15.0 lower_radii = 10.0 n_nearest = 100 periodic = {0: (0, Lbox), 1: (0, Lbox)} # Build the grid with the data gsp = GriSPy(data, periodic=periodic) # Query for neighbors within upper_radii bubble_dist, bubble_ind = gsp.bubble_neighbors( centres, distance_upper_bound=upper_radii) # Query for neighbors in a shell within lower_radii and upper_radii shell_dist, shell_ind = gsp.shell_neighbors(centres, distance_lower_bound=lower_radii, distance_upper_bound=upper_radii) # Query for nth nearest neighbors near_dist, near_ind = gsp.nearest_neighbors(centres, n=n_nearest) # Plot results plt.figure(4, figsize=(10, 3.2))
if i[j][x + 3] < k1[x] * 0.5: k[1][x] = 1 k2 = (i[j - 1][3:] + i[j][3:] + i[j + 1][3:] + (k[0, :] + k[1, :] + k[2, :]) * k1) * 0.33333 for x in range(3): if k2[x] >= k1[x]: k2[x] -= k1[x] vector.append([ k2, (i[j + 1][3:] - i[j - 1][3:] + k[0, :] * k1[0] - k[2, :] * k1[2]) ]) vector_np.append(k2) vector_np = np.array(vector_np) periodic = {0: (0, float(k1[0])), 1: (0, float(k1[1])), 2: (0, float(k1[2]))} gsp = GriSPy(vector_np) gsp.set_periodicity(periodic) bubble_dist, bubble_ind = gsp.bubble_neighbors(vector_np, distance_upper_bound=R) density_map = np.zeros((len(vector), 4), dtype=float) for i in range(len(vector)): D = 0 print(i) for j in range(len(bubble_ind[i])): if bubble_ind[i][j] != i: unit_point = vector[i][1] / np.linalg.norm(vector[i][1]) unit_neighbor = vector[bubble_ind[i][j]][1] / np.linalg.norm( vector[bubble_ind[i][j]][1]) dot = np.dot(unit_point, unit_neighbor) if dot > 1: dot = 1
def FoF( galaxy_data, candidate_centers, richness, overdensity, max_velocity=2000 * u.km / u.s, linking_length_factor=0.1, virial_radius=1.5 * u.Mpc / u.littleh, ): """ The Friends-of-Friends algorithm is a clustering algorithm used to identify groups of particles. In this instance, FoF is used to identify clusters of galaxies. FoF uses a linking length, l, whereby galaxies within a distance l from another galaxy are linked directly (as friends) and galaxies within a distance l from its friends are linked indirectly (as friends of friends). This network of particles are considered a cluster. After locating all candidate clusters, overlapping clusters are merged, with preference towards the center with larger N(d) and abs magnitude. A new cluster center is then defined as the brightess galaxy within 0.5 Mpc away from the current center. Finally, a cluster is only initialized if it has met the threshold richness and overdensity. The algorithm is sped up with: - numpy vectorization - grispy nearest neighbor implementation, which uses cell techniques to efficiently locate neighbors. This is preferred as it allows the use of the haversine metric for spherical coordinates. Parameters ---------- galaxy_data: ndarray, shape (n,7) Galaxy data with compulsory properties: ['ra', 'dec', 'z', 'abs mag', 'id', 'LR', 'N'] candidate_centers: ndarray, shape (m,7) Array of candidate centers with compulsory properties: ['ra', 'dec', 'z', 'abs mag', 'id', 'LR', 'N'] max_velocity: float, units [km/s] Default value: 2000 km/s linking_length_factor: float Default value: 0.1 virial_radius: float, units [Mpc/littleh] Default value: 1.5 hMpc richness: integer overdensity: float Returns ------- candidates: list of cluster.Cluster object """ candidates = [] # sep_arr = [] # tracks change in linking length with redshift # tracker identifies galaxies that have been included in another cluster previously to speed up algorithm. # candidate_centers was sorted by N(0.5) before to ensure larger clusters are prioritized tracker = np.ones(len(candidate_centers)) # identify cluster candidates for i, center in enumerate( candidate_centers ): # each row is a candidate center to search around if tracker[i]: velocity_bin = galaxy_data[ abs(redshift_to_velocity(galaxy_data[:, 2], center[2])) <= max_velocity] # select galaxies within max velocity virial_gsp = GriSPy(velocity_bin[:, :2], metric="haversine") # given virial radius is in proper distances, we convert to comoving distance to account for cosmological expansion. ang_virial_radius = linear_to_angular_dist( virial_radius, center[2] ).to("rad") # convert proper virial radius to angular separation max_dist = ( ang_virial_radius * cosmo.comoving_transverse_distance(center[2])).to( u.Mpc, u.dimensionless_angles()) # convert to comoving distance max_dist = linear_to_angular_dist( max_dist, center[2] ).value # convert comoving distance to angular separation virial_dist, virial_idx = virial_gsp.bubble_neighbors( np.array([center[:2]]), distance_upper_bound=max_dist ) # center must be a ndarray of (n,2) virial_points = velocity_bin[tuple( virial_idx)] # convert to tuple for deprecation warning if ( len(virial_points) >= 12 ): # reject if <12 galaxies within virial radius (to save time) mean_sep = mean_separation( len(virial_points), center[2], max_dist * u.degree, max_velocity, survey_area=1.7, ) # Mpc linking_length = ( linking_length_factor * mean_sep ) # determine transverse LL from local mean separation # sep_arr.append([linking_length.value, center[2]]) linking_length = linear_to_angular_dist( linking_length, center[2]).value # fix linking length here f_gsp = GriSPy(virial_points[:, :2], metric="haversine") f_dist, f_idx = f_gsp.bubble_neighbors( np.array([center[:2]]), distance_upper_bound=linking_length ) # select galaxies within linking length f_points = virial_points[tuple(f_idx)] member_galaxies = f_points fof_dist, fof_idx = f_gsp.bubble_neighbors( f_points[:, :2], distance_upper_bound=linking_length ) # select all galaxies within 2 linking lengths for idx in fof_idx: fof_points = virial_points[idx] # ensure no repeated points in cluster mask = np.isin( fof_points, member_galaxies, invert=True ) # filter for points not already accounted for vec_mask = np.isin(mask.sum(axis=1), center.shape[0]) fof_points = fof_points[vec_mask].reshape( (-1, center.shape[0])) # points of 2 linking lengths (FoF) if len(fof_points): member_galaxies = np.concatenate( (member_galaxies, fof_points) ) # merge all FoF points within 2 linking lengths if len(member_galaxies) >= richness: # must have >= richness c = Cluster(center, member_galaxies) candidates.append(c) if not i % 100: logging.info(f"{i} " + c.__str__()) # locate centers within member_galaxies (centers of interest) member_gal_id = member_galaxies[:, 4] luminous_gal_id = candidate_centers[:, 4] coi, _, coi_idx = np.intersect1d(member_gal_id, luminous_gal_id, return_indices=True) # update tracker to 0 for these points for i in coi_idx: tracker[i] = 0 # if len(candidates) >= 100: # for quick testing # break # plot_clusters(candidates, flagging=False) # for quick check of clusters # tracks mean separation across redshift # sep_arr = np.array(sep_arr) # plt.plot(sep_arr[:,1], sep_arr[:,0], '.') # plt.show() # perform overlap removal and merger print("Performing overlap removal") candidate_clusters = np.array([ [c.ra, c.dec, c.z, c.gal_id] for c in candidates ]) # get specific attributes from candidate center sample candidates = np.array(candidates) merged_candidates = candidates.copy() gal_id_space = candidate_clusters[:, 3] for center in candidates: # identity overlapping centers (centers lying within virial radius of current cluster) velocity_bin = candidate_clusters[ abs(redshift_to_velocity(candidate_clusters[:, 2], center.z)) <= max_velocity] # select galaxies within max velocity center_gsp = GriSPy(velocity_bin[:, :2], metric="haversine") c_coords = [center.ra, center.dec] max_dist = linear_to_angular_dist( virial_radius, center.z).value # convert virial radius to angular distance c_dist, c_idx = center_gsp.bubble_neighbors( np.array([c_coords]), distance_upper_bound=max_dist) # center must be a ndarray of (n,2) c_points = velocity_bin[tuple(c_idx)] # merge each overlapping cluster if len(c_points): for c in c_points: c = candidates[gal_id_space == c[-1]][0] if center.gal_id == c.gal_id: # if same center, ignore continue # modify the cluster's galaxies in merged_candidates array if len(c.galaxies) and len( center.galaxies): # check both clusters are not empty S = setdiff2d( c.galaxies, center.galaxies) # identify overlapping galaxies if len(S): new_c = merged_candidates[gal_id_space == c.gal_id][ 0] # c from merged_candidates new_center = merged_candidates[ gal_id_space == center.gal_id][ 0] # center from merged_candidates c_galaxies, center_galaxies = c.remove_overlap(center) new_c.galaxies = c_galaxies new_center.galaxies = center_galaxies merged_candidates = np.array([ c for c in merged_candidates if c.richness >= richness ]) # select only clusters >= richness if len(merged_candidates) >= len(candidates): logging.warning("No candidates were merged!") bcg_clusters = merged_candidates.copy() # replace candidate center with brightest galaxy in cluster print("Searching for BCGs") merged_candidates = sorted(merged_candidates, key=lambda x: x.N, reverse=True) # sort by N for center in merged_candidates: bcg_space_gal_id = np.array([c.gal_id for c in bcg_clusters]) # identify galaxies within 0.25*virial radius cluster_gsp = GriSPy( center.galaxies[:, :2], metric="haversine") # for galaxies within a cluster c_coords = [center.ra, center.dec] max_dist = 0.25 * (linear_to_angular_dist(virial_radius, center.z).value ) # convert virial radius to angular distance c_dist, c_idx = cluster_gsp.bubble_neighbors( np.array([c_coords]), distance_upper_bound=max_dist) # center must be a ndarray of (n,2) bcg_arr = center.galaxies[tuple(c_idx)] if len(bcg_arr) and len( center.galaxies ): # check for galaxies within 0.25*virial radius mag_sort = bcg_arr[bcg_arr[:, 3].argsort( )] # sort selected galaxies by abs mag (brightness) mask = np.isin( mag_sort[:, 4], bcg_space_gal_id, invert=True ) # filter for galaxies that are not existing centers mag_sort = mag_sort[mask] if len(mag_sort): bcg = mag_sort[0] # brightest cluster galaxy (bcg) # if bcg brighter than current center, replace it as center if (abs(bcg[3]) > abs(center.bcg_absMag)) and (bcg[4] != center.gal_id): new_cluster = Cluster( bcg, center.galaxies) # initialize new center bcg_clusters = np.delete( bcg_clusters, np.where([c.gal_id for c in bcg_clusters] == center.gal_id), ) bcg_clusters = np.concatenate( (bcg_clusters, np.array([new_cluster]))) # add new center to array bcg_clusters = np.array([ c for c in bcg_clusters if c.richness >= richness ]) # select only clusters >= richness final_clusters = [] # N(0.5) and galaxy overdensity print("Selecting appropriate clusters") for center in bcg_clusters: center.N = find_number_count(center, center.galaxies, distance=0.5 * u.Mpc / u.littleh) # find number count N(0.5) center.D = center_overdensity(center, galaxy_data, max_velocity) # find overdensity D # Initialize the cluster only if N(0.5) >= 8 and D >= overdensity if ((center.N >= 8) and (center.richness >= richness) and (center.D >= overdensity)): final_clusters.append(center) return final_clusters
def test_invalid_periodic_bad_edge_type(grispy_init): # Edge has wrong type bad_periodic = {0: (-1, [1]), 1: (-1, 1), 2: None} with pytest.raises(TypeError): GriSPy(grispy_init["data"], periodic=bad_periodic)