Coverage for sherlock/catalogue_conesearch.py: 100%
78 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-10-10 13:58 +0000
« prev ^ index » next coverage.py v7.2.2, created at 2023-10-10 13:58 +0000
1#!/usr/local/bin/python
2# encoding: utf-8
3"""
4*perform a conesearch on a **single** sherlock crossmatch catalogue table*
6:Author:
7 David Young
9:noindex:
10"""
11from builtins import zip
12from builtins import object
13import sys
14import os
15os.environ['TERM'] = 'vt100'
16from fundamentals import tools
17from astrocalc.coords import unit_conversion
18from HMpTy.mysql import conesearch as hmptyConesearch
19import copy
22class catalogue_conesearch(object):
23 """
24 *The worker class for the conesearch module*
26 **Key Arguments**
28 - ``dbConn`` -- mysql database connection to the catalogues database
29 - ``log`` -- logger
30 - ``ra`` -- ra of transient location (sexegesimal or decimal degrees, J2000, single location or list of locations)
31 - ``dec`` -- dec of transient location (sexegesimal or decimal degrees, J2000, single location or list of locations)
32 - ``tableName`` -- the name of the database table to perform the conesearch on
33 - ``radius`` -- radius of the conesearch to perform (arcsec)
34 - ``colMaps`` -- maps of the important column names for each table/view in the crossmatch-catalogues database
35 - ``nearestOnly`` -- return only the nearest object. Default *False*
36 - ``physicalSearch`` -- is this a physical search, so only return matches with distance information. Default *False*
37 - ``upperMagnitudeLimit`` -- the upper magnitude limit if a magnitude cut is requird with the conesearch. Default *False*
40 - ``lowerMagnitudeLimit`` -- the lower magnitude limit if a magnitude cut is requird with the conesearch. Default *False*
41 - ``magnitudeLimitFilter`` -- the filter to use for the magnitude limit if requird. Default *False*, ("_u"|"_g"|"_r"|"_i"|"_z"|"_y"|"U"|"B"|"V"|"R"|"I"|"Z"|"J"|"H"|"K"|"G")
43 **Usage**
45 To setup your logger, settings and database connections, please use the ``fundamentals`` package (`see tutorial here <http://fundamentals.readthedocs.io/en/latest/#tutorial>`_).
47 .. todo::
49 - update the package tutorial if needed
51 The following examples assume you've connected to the various databases and generated the catalogue column maps in the following menner:
53 ```python
54 # SETUP ALL DATABASE CONNECTIONS
55 from sherlock import database
56 db = database(
57 log=log,
58 settings=settings
59 )
60 dbConns, dbVersions = db.connect()
61 transientsDbConn = dbConns["transients"]
62 cataloguesDbConn = dbConns["catalogues"]
64 # GET THE COLUMN MAPS FROM THE CATALOGUE DATABASE
65 from sherlock.commonutils import get_crossmatch_catalogues_column_map
66 colMaps = get_crossmatch_catalogues_column_map(
67 log=log,
68 dbConn=cataloguesDbConn
69 )
70 ```
72 To perform a single location conesearch on a catalogue in the database, for example the milliquas AGN catalogue:
74 ```python
75 from sherlock import catalogue_conesearch
76 cs = catalogue_conesearch(
77 log=log,
78 ra="23:01:07.99",
79 dec="-01:58:04.5",
80 radiusArcsec=60.,
81 colMaps=colMaps,
82 tableName="tcs_view_agn_milliquas_v4_5",
83 dbConn=cataloguesDbConn,
84 nearestOnly=False,
85 physicalSearch=False
86 )
87 # catalogueMatches ARE ORDERED BY ANGULAR SEPARATION
88 indices, catalogueMatches = cs.search()
90 print(catalogueMatches)
91 ```
93 The output of this search is:
95 ```text
96 [{'R': 20.1, 'cmSepArcsec': 0.28015184686564643, 'ra': 345.2832267, 'catalogue_object_subtype': u'QR', 'z': 0.777, 'dec': -1.9679629, 'catalogue_object_id': u'PKS 2258-022'}]
97 ```
99 Note ``catalogue_conesearch`` can accept coordinates in sexegesimal or decimal degrees (J200). It can also accept lists of corrdinates:
101 ```python
102 from sherlock import catalogue_conesearch
103 cs = catalogue_conesearch(
104 log=log,
105 ra=["23:01:07.99", 45.36722, 13.875250],
106 dec=["-01:58:04.5", 30.45671, -25.26721],
107 radiusArcsec=60.,
110 colMaps=colMaps,
111 tableName="tcs_view_agn_milliquas_v4_5",
112 dbConn=cataloguesDbConn,
113 nearestOnly=False,
114 physicalSearch=False
115 )
116 ```
118 When passing a list of transient coordinates the returned ``indices`` value becomes important as this list identifies which transient is matched with which crossmatch results (and possibly multiple crossmatch results)
120 ```python
121 indices, catalogueMatches = cs.search()
122 for i, c in zip(indices, catalogueMatches):
123 print(i, c)
124 ```
126 The output of this search is:
128 ```text
129 0 {'R': 20.1, 'cmSepArcsec': 0.28015184686564643, 'ra': 345.2832267, 'catalogue_object_subtype': u'QR', 'z': 0.777, 'dec': -1.9679629, 'catalogue_object_id': u'PKS 2258-022'}
130 2 {'R': 19.2, 'cmSepArcsec': 0.81509715903447644, 'ra': 13.875, 'catalogue_object_subtype': u'Q', 'z': 2.7, 'dec': -25.2672223, 'catalogue_object_id': u'Q 0053-2532'}
131 ```
133 .. todo ::
135 - update key arguments values and definitions with defaults
136 - update return values and definitions
137 - update usage examples and text
138 - update docstring text
139 - check sublime snippet exists
140 - clip any useful text to docs mindmap
141 - regenerate the docs and check redendering of this docstring
142 """
143 # Initialisation
145 def __init__(
146 self,
147 log,
148 ra,
149 dec,
150 tableName,
151 radiusArcsec,
152 colMaps,
153 dbConn=False,
154 nearestOnly=False,
155 physicalSearch=False,
156 upperMagnitudeLimit=False,
157 lowerMagnitudeLimit=False,
158 magnitudeLimitFilter=False
159 ):
160 self.log = log
161 log.debug("instansiating a new 'conesearcher' object")
162 self.dbConn = dbConn
163 self.radius = radiusArcsec
164 self.tableName = tableName
165 self.nearestOnly = nearestOnly
166 self.colMaps = colMaps
167 self.physicalSearch = physicalSearch
168 self.upperMagnitudeLimit = upperMagnitudeLimit
169 self.lowerMagnitudeLimit = lowerMagnitudeLimit
170 self.magnitudeLimitFilter = magnitudeLimitFilter
171 # xt-self-arg-tmpx
173 # CONVERT RA AND DEC TO DEGREES
174 if not isinstance(ra, list):
175 ra = [ra]
176 dec = [dec]
178 self.ra = []
179 self.dec = []
181 converter = unit_conversion(
182 log=self.log
183 )
185 try:
186 float(ra[0])
187 convert = False
188 except:
189 convert = True
191 if convert == True:
193 for r, d in zip(ra, dec):
194 self.ra.append(converter.ra_sexegesimal_to_decimal(
195 ra=r
196 ))
197 self.dec.append(converter.dec_sexegesimal_to_decimal(
198 dec=d
199 ))
200 else:
201 self.ra = ra
202 self.dec = dec
204 return None
206 def search(self):
207 """
208 *trigger the conesearch*
210 **Return**
212 - ``matchIndies`` -- the indicies of the input transient sources (syncs with ``uniqueMatchDicts``)
213 - ``uniqueMatchDicts`` -- the crossmatch results
216 **Usage**
218 See class docstring for usage examples
221 .. todo ::
223 - update key arguments values and definitions with defaults
224 - update return values and definitions
225 - update usage examples and text
226 - update docstring text
227 - check sublime snippet exists
228 - clip any useful text to docs mindmap
229 - regenerate the docs and check redendering of this docstring
230 """
231 self.log.debug('starting the ``search`` method')
233 # ACCOUNT FOR TYPE OF SEARCH
234 sqlWhere = False
235 magnitudeLimitFilter = self.magnitudeLimitFilter
236 disCols = ["zColName",
237 "distanceColName", "semiMajorColName"]
238 sqlWhere = ""
240 if self.physicalSearch == True:
241 for d in disCols:
242 colName = self.colMaps[self.tableName][d]
243 if colName:
244 sqlWhere += " or `%(colName)s` is not null" % locals()
245 if len(sqlWhere):
246 sqlWhere = " and (" + sqlWhere[4:] + ")"
248 if self.upperMagnitudeLimit != False and self.upperMagnitudeLimit and not self.lowerMagnitudeLimit:
249 mag = self.upperMagnitudeLimit
250 sqlWhere += " and `%(magnitudeLimitFilter)s` > %(mag)s" % locals()
251 if self.lowerMagnitudeLimit != False and self.lowerMagnitudeLimit and not self.upperMagnitudeLimit:
252 mag = self.lowerMagnitudeLimit
253 sqlWhere += " and `%(magnitudeLimitFilter)s` < %(mag)s" % locals()
254 # THE GENERAL (INBETWEEN) CASE
255 if self.lowerMagnitudeLimit != False and self.lowerMagnitudeLimit and self.upperMagnitudeLimit != False and self.upperMagnitudeLimit:
256 upperMagnitudeLimit = self.upperMagnitudeLimit
257 lowerMagnitudeLimit = self.lowerMagnitudeLimit
258 sqlWhere += " and ((`%(magnitudeLimitFilter)s` > %(upperMagnitudeLimit)s and `%(magnitudeLimitFilter)s` < %(lowerMagnitudeLimit)s))" % locals()
260 if sqlWhere and " and" == sqlWhere[0:4]:
261 sqlWhere = sqlWhere[5:]
263 # THE COLUMN MAP LIFTED FROM ``tcs_helper_catalogue_tables_info` TABLE
264 # IN CATALOGUE DATABASE (COLUMN NAMES ENDDING WITH 'ColName')
265 columns = {}
266 for k, v in list(self.colMaps[self.tableName].items()):
267 name = k.replace("ColName", "")
268 if "colname" in k.lower() and v:
269 columns[k] = "`%(v)s` as `%(name)s`" % locals()
270 columns = ", ".join(list(columns.values()))
272 cs = hmptyConesearch(
273 log=self.log,
274 dbConn=self.dbConn,
275 tableName=self.tableName,
276 columns=columns,
277 ra=self.ra,
278 dec=self.dec,
279 radiusArcsec=self.radius,
280 separations=True,
281 distinct=False,
282 sqlWhere=sqlWhere,
283 closest=self.nearestOnly,
284 raCol="ra",
285 decCol="dec"
286 )
287 matchIndies, matches = cs.search()
289 # MATCH ARE NOT NECESSARILY UNIQUE IF MANY TRANSIENT MATCH ONE SOURCE
290 uniqueMatchDicts = []
291 uniqueMatchDicts[:] = [copy.copy(d) for d in matches.list]
293 self.log.debug('completed the ``search`` method')
294 return matchIndies, uniqueMatchDicts
296 # xt-class-method