From dbf1f712342c252530c85a23bd320b734ee50334 Mon Sep 17 00:00:00 2001 From: "sunil.simha95@gmail.com" Date: Thu, 24 Apr 2025 17:05:30 -0500 Subject: [PATCH 01/11] cluster cats --- frb/surveys/cluster_search.py | 179 ++++++++++++++++++++++++++++------ 1 file changed, 149 insertions(+), 30 deletions(-) diff --git a/frb/surveys/cluster_search.py b/frb/surveys/cluster_search.py index 1b4738c4..4f195356 100644 --- a/frb/surveys/cluster_search.py +++ b/frb/surveys/cluster_search.py @@ -22,11 +22,11 @@ class VizierCatalogSearch(surveycoord.SurveyCoord): """ - def __init__(self, coord, radius = 90*u.deg, catalog = None, cosmo=None, **kwargs): + def __init__(self, coord, radius = 90*u.deg, survey=None, viziercatalog = None, cosmo=None, **kwargs): # Initialize a SurveyCoord object surveycoord.SurveyCoord.__init__(self, coord, radius, **kwargs) - self.survey = None # Name - self.catalog = catalog # Name of the Vizier table to draw from. + self.survey = survey # Name + self.viziercatalog = viziercatalog # Name of the Vizier table to draw from. self.coord = coord # Location around which to perform the search self.radius = radius.to('deg').value # Radius of cone search if cosmo is None: # Use the same cosmology as elsewhere in this repository unless specified. @@ -34,13 +34,40 @@ def __init__(self, coord, radius = 90*u.deg, catalog = None, cosmo=None, **kwarg else: self.cosmo = cosmo - - def get_catalog(self, query_fields=None, transverse_distance_cut = 5*u.Mpc, **kwargs): + def clean_catalog(self, catalog): + """ + This will be survey specific. + """ pass - + def _transverse_distance_cut(self, catalog, transverse_distance_cut, distance_column='Dist'): + # Apply a transverse distance cut + angular_dist = self.coord.separation(SkyCoord(catalog['ra'], catalog['dec'], unit='deg')).to('rad').value + transverse_dist = catalog[distance_column]*np.sin(angular_dist) + catalog = catalog[transverse_dist=richness_cut] + self.catalog = result + + return self.catalog + +# Wen+2024 +class WenGroupCat(VizierCatalogSearch): + """ + A class to query sources within the Wen+2024 + group/cluster catalog. + """ + + + def __init__(self, coord, radius = 90*u.deg, cosmo=None, **kwargs): + # Initialize a SurveyCoord object + super(WenGroupCat, self).__init__(self, coord, radius, + survey="Wen+2024", + viziercatalog="J/ApJS/272/39/table2", + cosmo=cosmo, **kwargs) - # Convert distances from h^-1 Mpc to Mpc based on the cosmology being used. - result['Dist'] /=self.cosmo.h + def clean_catalog(self, catalog): + + catalog.rename_columns(['RAJ2000', 'DEJ2000', 'zcl'], ['ra', 'dec', 'z']) # Rename the columns to match the SurveyCoord class + + # Add a distance estimate in Mpc using the given cosmology + catalog['Dist'] = self.cosmo.lookback_distance(catalog['z']).to('Mpc').value + + return catalog + + def get_catalog(self, query_fields=None, + transverse_distance_cut = np.inf*u.Mpc, richness_cut = 5): + """ + Get the catalog of objects + Args: + z_lim (float): The maximum redshift of the objects to include in the catalog. + transverse_distance_cut (Quantity): The maximum impact parameter of the objects to include in the catalog. + richness_cut (int): The minimum number of members in any group/cluster returned. + query_fields (list): The fields to include in the catalog. If None, all fields are used. + Returns: + A table of objects within the given limits. + """ + result = super(WenGroupCat, self)._get_catalog(query_fields=query_fields) + + result = self.clean_catalog(result) # Apply a transverse distance cut - angular_dist = self.coord.separation(SkyCoord(result['ra'], result['dec'], unit='deg')).to('rad').value - transverse_dist = result['Dist']*np.sin(angular_dist) - result = result[transverse_dist=richness_cut] + if transverse_distance_cut=richness_cut] self.catalog = result + return self.catalog + +# Bahk and Hwang 2024 (Updated Planck+2015) +class UPClusterSZCat(VizierCatalogSearch): + """ + A class to query sources within the Bahk and Hwang 2024 + group/cluster catalog. + """ - return self.catalog \ No newline at end of file + + def __init__(self, coord, radius = 90*u.deg, cosmo=None, **kwargs): + # Initialize a SurveyCoord object + super(UPClusterSZCat, self).__init__(self, coord, radius, + survey="UPClusterSZ", + viziercatalog="J/ApJS/272/7/table2", + cosmo=cosmo, **kwargs) + + def clean_catalog(self, catalog): + + catalog.rename_columns(['RAJ2000', 'DEJ2000'], ['ra', 'dec']) # Rename the columns to match the SurveyCoord class + + # Add a distance estimate in Mpc using the given cosmology + catalog['Dist'] = self.cosmo.lookback_distance(catalog['z']).to('Mpc').value + + return catalog + + def get_catalog(self, query_fields=None, + transverse_distance_cut = np.inf*u.Mpc): + """ + Get the catalog of objects + Args: + z_lim (float): The maximum redshift of the objects to include in the catalog. + transverse_distance_cut (Quantity): The maximum impact parameter of the objects to include in the catalog. + richness_cut (int): The minimum number of members in any group/cluster returned. + query_fields (list): The fields to include in the catalog. If None, all fields are used. + Returns: + A table of objects within the given limits. + """ + result = super(UPClusterSZCat, self)._get_catalog(query_fields=query_fields) + + result = self.clean_catalog(result) + + # Apply a transverse distance cut + if transverse_distance_cut Date: Thu, 24 Apr 2025 17:05:30 -0500 Subject: [PATCH 02/11] cluster cats --- frb/surveys/cluster_search.py | 179 ++++++++++++++++++++++++++++------ 1 file changed, 149 insertions(+), 30 deletions(-) diff --git a/frb/surveys/cluster_search.py b/frb/surveys/cluster_search.py index 1b4738c4..4f195356 100644 --- a/frb/surveys/cluster_search.py +++ b/frb/surveys/cluster_search.py @@ -22,11 +22,11 @@ class VizierCatalogSearch(surveycoord.SurveyCoord): """ - def __init__(self, coord, radius = 90*u.deg, catalog = None, cosmo=None, **kwargs): + def __init__(self, coord, radius = 90*u.deg, survey=None, viziercatalog = None, cosmo=None, **kwargs): # Initialize a SurveyCoord object surveycoord.SurveyCoord.__init__(self, coord, radius, **kwargs) - self.survey = None # Name - self.catalog = catalog # Name of the Vizier table to draw from. + self.survey = survey # Name + self.viziercatalog = viziercatalog # Name of the Vizier table to draw from. self.coord = coord # Location around which to perform the search self.radius = radius.to('deg').value # Radius of cone search if cosmo is None: # Use the same cosmology as elsewhere in this repository unless specified. @@ -34,13 +34,40 @@ def __init__(self, coord, radius = 90*u.deg, catalog = None, cosmo=None, **kwarg else: self.cosmo = cosmo - - def get_catalog(self, query_fields=None, transverse_distance_cut = 5*u.Mpc, **kwargs): + def clean_catalog(self, catalog): + """ + This will be survey specific. + """ pass - + def _transverse_distance_cut(self, catalog, transverse_distance_cut, distance_column='Dist'): + # Apply a transverse distance cut + angular_dist = self.coord.separation(SkyCoord(catalog['ra'], catalog['dec'], unit='deg')).to('rad').value + transverse_dist = catalog[distance_column]*np.sin(angular_dist) + catalog = catalog[transverse_dist=richness_cut] + self.catalog = result + + return self.catalog + +# Wen+2024 +class WenGroupCat(VizierCatalogSearch): + """ + A class to query sources within the Wen+2024 + group/cluster catalog. + """ + + + def __init__(self, coord, radius = 90*u.deg, cosmo=None, **kwargs): + # Initialize a SurveyCoord object + super(WenGroupCat, self).__init__(self, coord, radius, + survey="Wen+2024", + viziercatalog="J/ApJS/272/39/table2", + cosmo=cosmo, **kwargs) - # Convert distances from h^-1 Mpc to Mpc based on the cosmology being used. - result['Dist'] /=self.cosmo.h + def clean_catalog(self, catalog): + + catalog.rename_columns(['RAJ2000', 'DEJ2000', 'zcl'], ['ra', 'dec', 'z']) # Rename the columns to match the SurveyCoord class + + # Add a distance estimate in Mpc using the given cosmology + catalog['Dist'] = self.cosmo.lookback_distance(catalog['z']).to('Mpc').value + + return catalog + + def get_catalog(self, query_fields=None, + transverse_distance_cut = np.inf*u.Mpc, richness_cut = 5): + """ + Get the catalog of objects + Args: + z_lim (float): The maximum redshift of the objects to include in the catalog. + transverse_distance_cut (Quantity): The maximum impact parameter of the objects to include in the catalog. + richness_cut (int): The minimum number of members in any group/cluster returned. + query_fields (list): The fields to include in the catalog. If None, all fields are used. + Returns: + A table of objects within the given limits. + """ + result = super(WenGroupCat, self)._get_catalog(query_fields=query_fields) + + result = self.clean_catalog(result) # Apply a transverse distance cut - angular_dist = self.coord.separation(SkyCoord(result['ra'], result['dec'], unit='deg')).to('rad').value - transverse_dist = result['Dist']*np.sin(angular_dist) - result = result[transverse_dist=richness_cut] + if transverse_distance_cut=richness_cut] self.catalog = result + return self.catalog + +# Bahk and Hwang 2024 (Updated Planck+2015) +class UPClusterSZCat(VizierCatalogSearch): + """ + A class to query sources within the Bahk and Hwang 2024 + group/cluster catalog. + """ - return self.catalog \ No newline at end of file + + def __init__(self, coord, radius = 90*u.deg, cosmo=None, **kwargs): + # Initialize a SurveyCoord object + super(UPClusterSZCat, self).__init__(self, coord, radius, + survey="UPClusterSZ", + viziercatalog="J/ApJS/272/7/table2", + cosmo=cosmo, **kwargs) + + def clean_catalog(self, catalog): + + catalog.rename_columns(['RAJ2000', 'DEJ2000'], ['ra', 'dec']) # Rename the columns to match the SurveyCoord class + + # Add a distance estimate in Mpc using the given cosmology + catalog['Dist'] = self.cosmo.lookback_distance(catalog['z']).to('Mpc').value + + return catalog + + def get_catalog(self, query_fields=None, + transverse_distance_cut = np.inf*u.Mpc): + """ + Get the catalog of objects + Args: + z_lim (float): The maximum redshift of the objects to include in the catalog. + transverse_distance_cut (Quantity): The maximum impact parameter of the objects to include in the catalog. + richness_cut (int): The minimum number of members in any group/cluster returned. + query_fields (list): The fields to include in the catalog. If None, all fields are used. + Returns: + A table of objects within the given limits. + """ + result = super(UPClusterSZCat, self)._get_catalog(query_fields=query_fields) + + result = self.clean_catalog(result) + + # Apply a transverse distance cut + if transverse_distance_cut Date: Thu, 24 Apr 2025 17:11:29 -0500 Subject: [PATCH 03/11] ROSAT X ray --- frb/surveys/cluster_search.py | 46 +++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/frb/surveys/cluster_search.py b/frb/surveys/cluster_search.py index 4f195356..059b8db8 100644 --- a/frb/surveys/cluster_search.py +++ b/frb/surveys/cluster_search.py @@ -208,3 +208,49 @@ def get_catalog(self, query_fields=None, self.catalog = result return self.catalog +# Xu+2022 (ROSAT X ray cluster) + +class ROSATXClusterCat(VizierCatalogSearch): + """ + A class to query sources within the Xu+2022 + group/cluster catalog. + """ + + + def __init__(self, coord, radius = 90*u.deg, cosmo=None, **kwargs): + # Initialize a SurveyCoord object + super(ROSATXClusterCat, self).__init__(self, coord, radius, + survey="ROSATXCluster", + viziercatalog="J/A+A/658/A59/table3", + cosmo=cosmo, **kwargs) + + def clean_catalog(self, catalog): + + catalog.rename_columns(['RAJ2000', 'DEJ2000'], ['ra', 'dec']) # Rename the columns to match the SurveyCoord class + + # Add a distance estimate in Mpc using the given cosmology + catalog['Dist'] = self.cosmo.lookback_distance(catalog['z']).to('Mpc').value + + return catalog + + def get_catalog(self, query_fields=None, + transverse_distance_cut = np.inf*u.Mpc): + """ + Get the catalog of objects + Args: + z_lim (float): The maximum redshift of the objects to include in the catalog. + transverse_distance_cut (Quantity): The maximum impact parameter of the objects to include in the catalog. + richness_cut (int): The minimum number of members in any group/cluster returned. + query_fields (list): The fields to include in the catalog. If None, all fields are used. + Returns: + A table of objects within the given limits. + """ + result = super(ROSATXClusterCat, self)._get_catalog(query_fields=query_fields) + + result = self.clean_catalog(result) + + # Apply a transverse distance cut + if transverse_distance_cut Date: Thu, 24 Apr 2025 17:18:18 -0500 Subject: [PATCH 04/11] tempel+2018 --- frb/surveys/cluster_search.py | 47 +++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/frb/surveys/cluster_search.py b/frb/surveys/cluster_search.py index 059b8db8..ad87a7d1 100644 --- a/frb/surveys/cluster_search.py +++ b/frb/surveys/cluster_search.py @@ -253,4 +253,51 @@ def get_catalog(self, query_fields=None, if transverse_distance_cut Date: Mon, 5 May 2025 13:28:51 -0400 Subject: [PATCH 05/11] bug fixes --- frb/surveys/cluster_search.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/frb/surveys/cluster_search.py b/frb/surveys/cluster_search.py index 0bf8c3af..57d87e0a 100644 --- a/frb/surveys/cluster_search.py +++ b/frb/surveys/cluster_search.py @@ -24,7 +24,7 @@ class VizierCatalogSearch(surveycoord.SurveyCoord): def __init__(self, coord, radius = 90*u.deg, survey=None, viziercatalog = None, cosmo=None, **kwargs): # Initialize a SurveyCoord object - surveycoord.SurveyCoord.__init__(self, coord, radius, **kwargs) + super(VizierCatalogSearch, self).__init__(coord, radius, **kwargs) self.survey = survey # Name self.viziercatalog = viziercatalog # Name of the Vizier table to draw from. self.coord = coord # Location around which to perform the search @@ -63,7 +63,8 @@ def _get_catalog(self, query_fields=None, **kwargs): # Query Vizier v = Vizier(catalog = self.viziercatalog, columns=query_fields, row_limit= -1, **kwargs) # No row limit - result = v.query_region(self.coord, radius=self.radius*u.deg)[0] # Just get the first (and only table here) + result = v.query_region(self.coord, radius=self.radius*u.deg) + import pdb; pdb.set_trace() # Just get the first (and only table here) return result # Tully 2015 @@ -76,7 +77,7 @@ class TullyGroupCat(VizierCatalogSearch): def __init__(self, coord, radius = 90*u.deg, cosmo=None, **kwargs): # Initialize a SurveyCoord object - super(TullyGroupCat, self).__init__(self, coord, radius, + super(TullyGroupCat, self).__init__(coord, radius, survey="Tully+2015", viziercatalog="J/AJ/149/171/table5", cosmo=cosmo, **kwargs) @@ -125,7 +126,7 @@ class WenGroupCat(VizierCatalogSearch): def __init__(self, coord, radius = 90*u.deg, cosmo=None, **kwargs): # Initialize a SurveyCoord object - super(WenGroupCat, self).__init__(self, coord, radius, + super(WenGroupCat, self).__init__(coord, radius, survey="Wen+2024", viziercatalog="J/ApJS/272/39/table2", cosmo=cosmo, **kwargs) @@ -172,7 +173,7 @@ class UPClusterSZCat(VizierCatalogSearch): def __init__(self, coord, radius = 90*u.deg, cosmo=None, **kwargs): # Initialize a SurveyCoord object - super(UPClusterSZCat, self).__init__(self, coord, radius, + super(UPClusterSZCat, self).__init__(coord, radius, survey="UPClusterSZ", viziercatalog="J/ApJS/272/7/table2", cosmo=cosmo, **kwargs) @@ -219,7 +220,7 @@ class ROSATXClusterCat(VizierCatalogSearch): def __init__(self, coord, radius = 90*u.deg, cosmo=None, **kwargs): # Initialize a SurveyCoord object - super(ROSATXClusterCat, self).__init__(self, coord, radius, + super(ROSATXClusterCat, self).__init__(coord, radius, survey="ROSATXCluster", viziercatalog="J/A+A/658/A59/table3", cosmo=cosmo, **kwargs) @@ -266,7 +267,7 @@ class TempelClusterCat(VizierCatalogSearch): def __init__(self, coord, radius = 90*u.deg, cosmo=None, **kwargs): # Initialize a SurveyCoord object - super(TempelClusterCat, self).__init__(self, coord, radius, + super(TempelClusterCat, self).__init__(coord, radius, survey="TempelCluster", viziercatalog="J/A+A/618/A81/2mrs_gr", cosmo=cosmo, **kwargs) From b2beb3d695887a0b5afcb3b6551de91615609ac8 Mon Sep 17 00:00:00 2001 From: "sunil.simha95@gmail.com" Date: Mon, 5 May 2025 16:28:50 -0400 Subject: [PATCH 06/11] handle len 0 catalog searches --- frb/surveys/cluster_search.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frb/surveys/cluster_search.py b/frb/surveys/cluster_search.py index 57d87e0a..0a1574c9 100644 --- a/frb/surveys/cluster_search.py +++ b/frb/surveys/cluster_search.py @@ -9,6 +9,8 @@ from astropy.coordinates import SkyCoord from astropy import units as u +from astropy.table import Table + try: from astroquery.vizier import Vizier except ImportError: @@ -64,7 +66,11 @@ def _get_catalog(self, query_fields=None, **kwargs): # Query Vizier v = Vizier(catalog = self.viziercatalog, columns=query_fields, row_limit= -1, **kwargs) # No row limit result = v.query_region(self.coord, radius=self.radius*u.deg) - import pdb; pdb.set_trace() # Just get the first (and only table here) + if len(result) == 0: + print("No objects found within the given radius.") + return Table() + else: + result = result[0] return result # Tully 2015 From c3405c4b66e1288395da2636eea8ef36af5ffbc6 Mon Sep 17 00:00:00 2001 From: "sunil.simha95@gmail.com" Date: Wed, 7 May 2025 22:13:07 -0500 Subject: [PATCH 07/11] 3d catalog matching --- frb/surveys/catalog_utils.py | 38 ++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/frb/surveys/catalog_utils.py b/frb/surveys/catalog_utils.py index 8aded82e..04ebf7b6 100644 --- a/frb/surveys/catalog_utils.py +++ b/frb/surveys/catalog_utils.py @@ -7,7 +7,6 @@ from astropy.table import Table, hstack, vstack, setdiff, join from astropy import units from frb.galaxies.defs import valid_filters -import warnings from IPython import embed @@ -164,9 +163,10 @@ def summarize_catalog(frbc, catalog, summary_radius, photom_column, magnitude): return summary_list -def xmatch_catalogs(cat1:Table, cat2:Table, skydist:units.Quantity = 5*units.arcsec, +def xmatch_catalogs(cat1:Table, cat2:Table, dist:units.Quantity = 5*units.arcsec, RACol1:str = "ra", DecCol1:str = "dec", RACol2:str = "ra", DecCol2:str = "dec", + distcol1:str = None, distcol2:str = None, return_match_idx:bool=False)->tuple: """ Cross matches two astronomical catalogs and returns @@ -175,15 +175,21 @@ def xmatch_catalogs(cat1:Table, cat2:Table, skydist:units.Quantity = 5*units.arc cat1, cat2: astropy Tables Two tables with sky coordinates to be matched. - skydist: astropy Quantity, optional + dist: astropy Quantity, optional Maximum separation for a valid match. - 5 arcsec by default. + 5 arcsec by default. Can be length units + if distcol1 and distcol2 are provided. RACol1, RACol2: str, optional Names of columns in cat1 and cat2 respectively that contain RA in degrees. DecCol1, DecCol2: str, optional Names of columns in cat1 and cat2 respectively that contain Dec in degrees. + distcol1, distcol2: str, optional + Names of columns in cat1 and cat2 + respectively that contain the radial distance + as floats in Mpc. If None, 2D cross-matches + are returned. return_match_idx: bool, optional Return the indices of the matched entries with with the distance instead? @@ -197,19 +203,31 @@ def xmatch_catalogs(cat1:Table, cat2:Table, skydist:units.Quantity = 5*units.arc assert isinstance(cat1, (Table, QTable))&isinstance(cat1, (Table, QTable)), "Catalogs must be astropy Table instances." assert (RACol1 in cat1.colnames)&(DecCol1 in cat1.colnames), " Could not find either {:s} or {:s} in cat1".format(RACol1, DecCol1) assert (RACol2 in cat2.colnames)&(DecCol2 in cat2.colnames), " Could not find either {:s} or {:s} in cat2".format(RACol2, DecCol2) + do_3d = (distcol1 is not None)&(distcol2 is not None) + if do_3d: + assert (distcol1 in cat1.colnames)&(distcol2 in cat2.colnames), "Could not find either {:s} or {:s} in cat1".format(distcol1, distcol2) + dist1 = cat1[distcol1]*units.Mpc + dist2 = cat2[distcol2]*units.Mpc + else: + dist1 = None + dist2 = None # Get corodinates - cat1_coord = SkyCoord(cat1[RACol1], cat1[DecCol1], unit = "deg") - cat2_coord = SkyCoord(cat2[RACol2], cat2[DecCol2], unit = "deg") + cat1_coord = SkyCoord(cat1[RACol1]*units.deg, cat1[DecCol1]*units.deg, distance=dist1) + cat2_coord = SkyCoord(cat2[RACol2]*units.deg, cat2[DecCol2]*units.deg, distance=dist2) # Match 2D - idx, d2d, _ = cat1_coord.match_to_catalog_sky(cat2_coord) + idx, d2d, d3d = cat1_coord.match_to_catalog_sky(cat2_coord) # Get matched tables - match1 = cat1[d2d < skydist] - match2 = cat2[idx[d2d < skydist]] + if do_3d: + separation = d3d + else: + separation = d2d + match1 = cat1[separation < dist] + match2 = cat2[idx[separation < dist]] if return_match_idx: - return idx, d2d + return idx, d2d, d3d else: return match1, match2 From 2da8eba8601756da937f978c32044c739869edc5 Mon Sep 17 00:00:00 2001 From: Calvin Leung <42367504+leungcalvin@users.noreply.github.com> Date: Thu, 3 Jul 2025 19:09:23 +0000 Subject: [PATCH 08/11] only run clean_catalog if not empty --- frb/surveys/cluster_search.py | 87 +++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/frb/surveys/cluster_search.py b/frb/surveys/cluster_search.py index 0a1574c9..6582ebb0 100644 --- a/frb/surveys/cluster_search.py +++ b/frb/surveys/cluster_search.py @@ -59,6 +59,7 @@ def _get_catalog(self, query_fields=None, **kwargs): query_fields (list): The fields to include in the catalog. If None, all fields are used. Returns: A table of objects within the given limits. + If no objects found, returns an empty table with fields ['ra','dec', and 'z']. """ if query_fields is None: query_fields = ['**'] # Get all. @@ -68,7 +69,7 @@ def _get_catalog(self, query_fields=None, **kwargs): result = v.query_region(self.coord, radius=self.radius*u.deg) if len(result) == 0: print("No objects found within the given radius.") - return Table() + return Table(names = ('ra','dec','z')) else: result = result[0] return result @@ -111,13 +112,13 @@ def get_catalog(self, query_fields=None, A table of objects within the given limits. """ result = super(TullyGroupCat, self)._get_catalog(query_fields=query_fields) + if len(result) > 0: + result = self.clean_catalog(result) - result = self.clean_catalog(result) - - # Apply a transverse distance cut - if transverse_distance_cut=richness_cut] + # Apply a transverse distance cut + if transverse_distance_cut=richness_cut] self.catalog = result return self.catalog @@ -130,7 +131,7 @@ class WenGroupCat(VizierCatalogSearch): """ - def __init__(self, coord, radius = 90*u.deg, cosmo=None, **kwargs): + def __init__(self, coord, radius = 0.2*u.deg, cosmo=None, **kwargs): # Initialize a SurveyCoord object super(WenGroupCat, self).__init__(coord, radius, survey="Wen+2024", @@ -138,8 +139,19 @@ def __init__(self, coord, radius = 90*u.deg, cosmo=None, **kwargs): cosmo=cosmo, **kwargs) def clean_catalog(self, catalog): + try: + catalog.rename_columns(['RAJ2000'],['ra']) + except KeyError: + assert 'ra' in catalog.keys() + try: + catalog.rename_columns(['DEJ2000'],['dec']) + except KeyError: + assert 'dec' in catalog.keys() + try: + catalog.rename_columns(['zCl'],['z']) + except KeyError: + assert 'z' in catalog.keys() - catalog.rename_columns(['RAJ2000', 'DEJ2000', 'zcl'], ['ra', 'dec', 'z']) # Rename the columns to match the SurveyCoord class # Add a distance estimate in Mpc using the given cosmology catalog['Dist'] = self.cosmo.lookback_distance(catalog['z']).to('Mpc').value @@ -159,13 +171,13 @@ def get_catalog(self, query_fields=None, A table of objects within the given limits. """ result = super(WenGroupCat, self)._get_catalog(query_fields=query_fields) + if len(result) > 0: + result = self.clean_catalog(result) - result = self.clean_catalog(result) - - # Apply a transverse distance cut - if transverse_distance_cut=richness_cut] + # Apply a transverse distance cut + if transverse_distance_cut=richness_cut] self.catalog = result return self.catalog @@ -185,11 +197,16 @@ def __init__(self, coord, radius = 90*u.deg, cosmo=None, **kwargs): cosmo=cosmo, **kwargs) def clean_catalog(self, catalog): + if len(catalog) > 0: + try: + catalog.rename_columns(['RAJ2000', 'DEJ2000'], ['ra', 'dec']) # Rename the columns to match the SurveyCoord class + except KeyError: + print(catalog.keys()) - catalog.rename_columns(['RAJ2000', 'DEJ2000'], ['ra', 'dec']) # Rename the columns to match the SurveyCoord class - - # Add a distance estimate in Mpc using the given cosmology - catalog['Dist'] = self.cosmo.lookback_distance(catalog['z']).to('Mpc').value + assert 'ra' in catalog.keys() and 'dec' in catalog.keys() + + # Add a distance estimate in Mpc using the given cosmology + catalog['Dist'] = self.cosmo.lookback_distance(catalog['z']).to('Mpc').value return catalog @@ -206,8 +223,8 @@ def get_catalog(self, query_fields=None, A table of objects within the given limits. """ result = super(UPClusterSZCat, self)._get_catalog(query_fields=query_fields) - - result = self.clean_catalog(result) + if len(result) > 0: + result = self.clean_catalog(result) # Apply a transverse distance cut if transverse_distance_cut 0: + catalog.rename_columns(['RAJ2000', 'DEJ2000'], ['ra', 'dec']) # Rename the columns to match the SurveyCoord class + + # Add a distance estimate in Mpc using the given cosmology + catalog['Dist'] = self.cosmo.lookback_distance(catalog['z']).to('Mpc').value return catalog @@ -253,8 +270,8 @@ def get_catalog(self, query_fields=None, A table of objects within the given limits. """ result = super(ROSATXClusterCat, self)._get_catalog(query_fields=query_fields) - - result = self.clean_catalog(result) + if len(result) > 0: + result = self.clean_catalog(result) # Apply a transverse distance cut if transverse_distance_cut 0: + catalog.rename_columns(['RAJ2000', 'DEJ2000', 'zcmb'], ['ra', 'dec', 'z']) # Rename the columns to match the SurveyCoord class + + # Add a distance estimate in Mpc using the given cosmology + catalog['Dist'] = self.cosmo.lookback_distance(catalog['z']).to('Mpc').value return catalog @@ -300,8 +317,8 @@ def get_catalog(self, query_fields=None, A table of objects within the given limits. """ result = super(TempelClusterCat, self)._get_catalog(query_fields=query_fields) - - result = self.clean_catalog(result) + if len(result) > 0: + result = self.clean_catalog(result) # Apply a transverse distance cut if transverse_distance_cut Date: Mon, 8 Dec 2025 11:05:47 -0700 Subject: [PATCH 09/11] added some additional surveys and converted distances to angular diameter distances --- frb/surveys/cluster_search.py | 209 +++++++++++++++++++++++++++++++++- 1 file changed, 203 insertions(+), 6 deletions(-) diff --git a/frb/surveys/cluster_search.py b/frb/surveys/cluster_search.py index 6582ebb0..d8347ecb 100644 --- a/frb/surveys/cluster_search.py +++ b/frb/surveys/cluster_search.py @@ -8,7 +8,7 @@ from frb.defs import frb_cosmo from astropy.coordinates import SkyCoord from astropy import units as u - +from astropy.cosmology import z_at_value from astropy.table import Table try: @@ -95,7 +95,8 @@ def clean_catalog(self, catalog): # Convert distances from h^-1 Mpc to Mpc based on the cosmology being used. catalog['Dist'] /=self.cosmo.h - + redshift = z_at_value(self.cosmo.luminosity_distance, catalog['Dist']*u.Mpc) + catalog['Dist'] = self.cosmo.angular_diameter_distance(redshift).value return catalog @@ -154,7 +155,7 @@ def clean_catalog(self, catalog): # Add a distance estimate in Mpc using the given cosmology - catalog['Dist'] = self.cosmo.lookback_distance(catalog['z']).to('Mpc').value + catalog['Dist'] = self.cosmo.angular_diameter_distance(catalog['z']).value return catalog @@ -206,7 +207,7 @@ def clean_catalog(self, catalog): assert 'ra' in catalog.keys() and 'dec' in catalog.keys() # Add a distance estimate in Mpc using the given cosmology - catalog['Dist'] = self.cosmo.lookback_distance(catalog['z']).to('Mpc').value + catalog['Dist'] = self.cosmo.angular_diameter_distance(catalog['z']).value return catalog @@ -253,7 +254,7 @@ def clean_catalog(self, catalog): catalog.rename_columns(['RAJ2000', 'DEJ2000'], ['ra', 'dec']) # Rename the columns to match the SurveyCoord class # Add a distance estimate in Mpc using the given cosmology - catalog['Dist'] = self.cosmo.lookback_distance(catalog['z']).to('Mpc').value + catalog['Dist'] = self.cosmo.angular_diameter_distance(catalog['z']).value return catalog @@ -300,7 +301,7 @@ def clean_catalog(self, catalog): catalog.rename_columns(['RAJ2000', 'DEJ2000', 'zcmb'], ['ra', 'dec', 'z']) # Rename the columns to match the SurveyCoord class # Add a distance estimate in Mpc using the given cosmology - catalog['Dist'] = self.cosmo.lookback_distance(catalog['z']).to('Mpc').value + catalog['Dist'] = self.cosmo.angular_diameter_distance(catalog['z']).to('Mpc').value return catalog @@ -325,3 +326,199 @@ def get_catalog(self, query_fields=None, result = super(TempelClusterCat, self)._transverse_distance_cut(result, transverse_distance_cut) self.catalog = result return self.catalog + + +# Klein+ 2023 RASS-MCMF (Rosat All Sky Survey Multi Component Matched Filter) + +class RASSClusterCat(VizierCatalogSearch): + """ + A class to query sources within the Klein+ 2023 + group/cluster catalog. + """ + + + def __init__(self, coord, radius = 90*u.deg, cosmo=None, **kwargs): + # Initialize a SurveyCoord object + super(RASSClusterCat, self).__init__(coord, radius, + survey="RASSCluster", + viziercatalog="J/MNRAS/526/3757/catalog", + cosmo=cosmo, **kwargs) + + def clean_catalog(self, catalog): + if len(catalog) > 0: + catalog.rename_columns(['RAJ2000', 'DEJ2000','zsp1'], ['ra', 'dec','z']) # Rename the columns to match the SurveyCoord class + + # Add a distance estimate in Mpc using the given cosmology + catalog['Dist'] = self.cosmo.angular_diameter_distance(catalog['z']).value + + return catalog + + def get_catalog(self, query_fields=None, + transverse_distance_cut = np.inf*u.Mpc): + """ + Get the catalog of objects + Args: + z_lim (float): The maximum redshift of the objects to include in the catalog. + transverse_distance_cut (Quantity): The maximum impact parameter of the objects to include in the catalog. + richness_cut (int): The minimum number of members in any group/cluster returned. + query_fields (list): The fields to include in the catalog. If None, all fields are used. + Returns: + A table of objects within the given limits. + """ + result = super(RASSClusterCat, self)._get_catalog(query_fields=query_fields) + if len(result) > 0: + result = self.clean_catalog(result) + + # Apply a transverse distance cut + if transverse_distance_cut 0: + catalog.rename_columns(['RAJ2000', 'DEJ2000','zspec'], ['ra', 'dec','z']) # Rename the columns to match the SurveyCoord class + + # Add a distance estimate in Mpc using the given cosmology + redshift = catalog['z'] + redshift[redshift<0] = catalog['zlambda'][redshift<0] + catalog['Dist'] = self.cosmo.angular_diameter_distance(redshift).value + + return catalog + + def get_catalog(self, query_fields=None, + transverse_distance_cut = np.inf*u.Mpc): + """ + Get the catalog of objects + Args: + z_lim (float): The maximum redshift of the objects to include in the catalog. + transverse_distance_cut (Quantity): The maximum impact parameter of the objects to include in the catalog. + richness_cut (int): The minimum number of members in any group/cluster returned. + query_fields (list): The fields to include in the catalog. If None, all fields are used. + Returns: + A table of objects within the given limits. + """ + result = super(RedMapperClusterCat, self)._get_catalog(query_fields=query_fields) + if len(result) > 0: + result = self.clean_catalog(result) + + # Apply a transverse distance cut + if transverse_distance_cut 0: + catalog.rename_columns(['RAJ2000', 'DEJ2000', 'z1C'], ['ra', 'dec','z']) # Rename the columns to match the SurveyCoord class + + # Add a distance estimate in Mpc using the given cosmology + redshift = catalog['z'] + catalog['Dist'] = self.cosmo.angular_diameter_distance(redshift).value + + return catalog + + def get_catalog(self, query_fields=None, + transverse_distance_cut = np.inf*u.Mpc): + """ + Get the catalog of objects + Args: + z_lim (float): The maximum redshift of the objects to include in the catalog. + transverse_distance_cut (Quantity): The maximum impact parameter of the objects to include in the catalog. + richness_cut (int): The minimum number of members in any group/cluster returned. + query_fields (list): The fields to include in the catalog. If None, all fields are used. + Returns: + A table of objects within the given limits. + """ + result = super(ACTDR5ClusterCat, self)._get_catalog(query_fields=query_fields) + if len(result) > 0: + result = self.clean_catalog(result) + + # Apply a transverse distance cut + if transverse_distance_cut 0: + catalog.rename_columns(['RAJ2000', 'DEJ2000', 'Bestz'], ['ra', 'dec','z']) # Rename the columns to match the SurveyCoord class + + # Add a distance estimate in Mpc using the given cosmology + redshift = catalog['z'] + catalog['Dist'] = self.cosmo.angular_diameter_distance(redshift).value + + return catalog + + def get_catalog(self, query_fields=None, + transverse_distance_cut = np.inf*u.Mpc): + """ + Get the catalog of objects + Args: + z_lim (float): The maximum redshift of the objects to include in the catalog. + transverse_distance_cut (Quantity): The maximum impact parameter of the objects to include in the catalog. + richness_cut (int): The minimum number of members in any group/cluster returned. + query_fields (list): The fields to include in the catalog. If None, all fields are used. + Returns: + A table of objects within the given limits. + """ + result = super(ERASSClusterCat, self)._get_catalog(query_fields=query_fields) + if len(result) > 0: + result = self.clean_catalog(result) + + # Apply a transverse distance cut + if transverse_distance_cut Date: Tue, 9 Dec 2025 14:24:04 -0700 Subject: [PATCH 10/11] updated tully group catalog to correclty convert to angular diameter distance --- frb/surveys/cluster_search.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frb/surveys/cluster_search.py b/frb/surveys/cluster_search.py index d8347ecb..17d22c2b 100644 --- a/frb/surveys/cluster_search.py +++ b/frb/surveys/cluster_search.py @@ -8,7 +8,6 @@ from frb.defs import frb_cosmo from astropy.coordinates import SkyCoord from astropy import units as u -from astropy.cosmology import z_at_value from astropy.table import Table try: @@ -95,7 +94,9 @@ def clean_catalog(self, catalog): # Convert distances from h^-1 Mpc to Mpc based on the cosmology being used. catalog['Dist'] /=self.cosmo.h - redshift = z_at_value(self.cosmo.luminosity_distance, catalog['Dist']*u.Mpc) + rec_velocity = catalog['Dist']*self.cosmo.H0.value + c_kms = 299792.458 + redshift = ((1+rec_velocity/c_kms)/(1-rec_velocity/c_kms))**0.5-1 catalog['Dist'] = self.cosmo.angular_diameter_distance(redshift).value return catalog @@ -521,4 +522,4 @@ def get_catalog(self, query_fields=None, if transverse_distance_cut Date: Tue, 9 Dec 2025 15:11:55 -0700 Subject: [PATCH 11/11] bug in tables use --- frb/surveys/cluster_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frb/surveys/cluster_search.py b/frb/surveys/cluster_search.py index 17d22c2b..2402ac1f 100644 --- a/frb/surveys/cluster_search.py +++ b/frb/surveys/cluster_search.py @@ -94,7 +94,7 @@ def clean_catalog(self, catalog): # Convert distances from h^-1 Mpc to Mpc based on the cosmology being used. catalog['Dist'] /=self.cosmo.h - rec_velocity = catalog['Dist']*self.cosmo.H0.value + rec_velocity = catalog['Dist'].value*self.cosmo.H0.value c_kms = 299792.458 redshift = ((1+rec_velocity/c_kms)/(1-rec_velocity/c_kms))**0.5-1 catalog['Dist'] = self.cosmo.angular_diameter_distance(redshift).value