Coverage for python/lsst/sims/catUtils/baseCatalogModels/GalaxyModels.py : 59%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import warnings
2import numpy
3import copy
4from .BaseCatalogModels import BaseCatalogObj
5from lsst.sims.catalogs.db import ChunkIterator, CompoundCatalogDBObject
6from lsst.sims.utils import ObservationMetaData
8__all__ = ["GalaxyObj", "GalaxyTileObj", "GalaxyBulgeObj",
9 "GalaxyDiskObj", "GalaxyAgnObj", "ImageAgnObj", "LensGalaxyObj",
10 "GalaxyTileCompoundObj"]
13class GalaxyObj(BaseCatalogObj):
14 """
15 Note: building a catalog out of this object will directly call the
16 'galaxy' table. This table only contains objects for
18 -2.5 deg < RA < 2.5 deg, -2.5 deg < Dec < 2.5 deg
20 In order to cover the whole sky, call one of the objects that
21 inherits from GalaxyTileObj
22 """
23 objid = 'galaxyBase'
25 #: This is the base table for the galaxies
26 # tableid = 'final_clone_db'
27 tableid = 'galaxy'
28 idColKey = 'id'
29 raColName = '((CAST(ra AS NUMERIC(9,6))%360.)+360.)%360.'
30 decColName = 'dec'
31 objectTypeId = 24
33 doRunTest = True
34 testObservationMetaData = ObservationMetaData(boundType='circle', pointingRA=0.0, pointingDec=0.0,
35 boundLength=0.01, mjd=52000., bandpassName='r', m5 = 22.0)
37 #: Numpy can't cast a NoneType to an integer. This works with floats
38 #: as None is cast to nan, but for integers this raises and exception.
39 #: Typically it's not an issue as ints are usually ids of some sort,
40 #: but in the case of the base galaxy catalog, it's possible for the
41 #: varsimobjid to be None if the object does not contain an AGN.
42 #: I'm over riding the _postprocess_results method to take care of this.
43 #: I could also have refactored my database table so that no integer values
44 #: contain NULL values.
45 dbDefaultValues = {'varsimobjid': -1, 'myid': -1}
47 #: The following maps column names to database schema. The tuples
48 #: must be at least length 2. If column name is the same as the name
49 #: in the DB the mapping element may be None. The rest of the tuple
50 #: should be formatted like a numpy.dtype. If ommitted, the dtype
51 #: is assumed to be float.
52 columns = [('galid', None, str, 30),
53 ('raJ2000', 'ra*PI()/180.'),
54 ('decJ2000', 'dec*PI()/180.'),
55 ('raJ2000Bulge', 'bra*PI()/180.'),
56 ('decJ2000Bulge', 'bdec*PI()/180.'),
57 ('raJ2000Disk', 'dra*PI()/180.'),
58 ('decJ2000Disk', 'ddec*PI()/180.'),
59 ('raJ2000Agn', 'agnra*PI()/180.'),
60 ('decJ2000Agn', 'agndec*PI()/180.'),
61 ('magNormBulge', 'magnorm_bulge'),
62 ('magNormDisk', 'magnorm_disk'),
63 ('magNormAgn', 'magnorm_agn'),
64 ('sedFilenameBulge', 'sedname_bulge', str, 40),
65 ('sedFilenameDisk', 'sedname_disk', str, 40),
66 ('sedFilenameAgn', 'sedname_agn', str, 40),
67 ('majorAxisBulge', 'a_b*PI()/648000.'),
68 ('minorAxisBulge', 'b_b*PI()/648000.'),
69 ('positionAngleBulge', 'pa_bulge*PI()/180.'),
70 ('sindexBulge', 'bulge_n', int),
71 ('majorAxisDisk', 'a_d*PI()/648000.'),
72 ('minorAxisDisk', 'b_d*PI()/648000.'),
73 ('positionAngleDisk', 'pa_disk*PI()/180.'),
74 ('sindexDisk', 'disk_n', int),
75 ('internalExtinctionModelBulge', 'ext_model_b', str, 3),
76 ('internalAvBulge', 'av_b'),
77 ('internalRvBulge', 'rv_b'),
78 ('internalExtinctionModelDisk', 'ext_model_d', str, 3),
79 ('internalAvDisk', 'av_d'),
80 ('internalRvDisk', 'rv_d'),
81 ('lsst_u', 'u_ab'),
82 ('lsst_g', 'g_ab'),
83 ('lsst_r', 'r_ab'),
84 ('lsst_i', 'i_ab'),
85 ('lsst_z', 'z_ab'),
86 ('lsst_y', 'y_ab')]
88 def _final_pass(self, results):
89 """This is to map the values from 0 - 2*PI() as ra goes negative currently"""
90 for ra in ('raJ2000', 'raJ2000Bulge', 'raJ2000Disk', 'raJ2000Agn'):
91 if ra in results.dtype.names:
92 results[ra] = results[ra]%(numpy.pi*2.)
93 return results
96class GalaxyTileObj(BaseCatalogObj):
97 """
98 This is the parent class for galaxy BaseCatalogObjs that sample the whole
99 sky (rather than just a very small patch as in GalaxyObj)
100 """
102 objid = 'galaxyTiled'
103 #: This is the base table for the galaxies
104 tableid = 'galaxy'
105 # componentSubset must be one of "ALL" (default), "AGN", "BULGE", "DISK"
106 componentSubset = 'ALL'
107 raColName = 'ra'
108 decColName = 'dec'
109 objectTypeId = 25
111 doRunTest = True
112 testObservationMetaData = ObservationMetaData(boundType='circle', pointingRA=173.0, pointingDec=-60.0,
113 boundLength=0.01, mjd=52000., bandpassName='r', m5=22.0)
115 #: Numpy can't cast a NoneType to an integer. This works with floats
116 #: as None is cast to nan, but for integers this raises and exception.
117 #: Typically it's not an issue as ints are usually ids of some sort,
118 #: but in the case of the base galaxy catalog, it's possible for the
119 #: varsimobjid to be None if the object does not contain an AGN.
120 #: I'm over riding the _postprocess_results method to take care of this.
121 #: I could also have refactored my database table so that no integer values
122 #: contain NULL values.
123 dbDefaultValues = {'varsimobjid': -1, 'myid': -1}
125 #: The following maps column names to database schema. The tuples
126 #: must be at least length 2. If column name is the same as the name
127 #: in the DB the mapping element may be None. The rest of the tuple
128 #: should be formatted like a numpy.dtype. If ommitted, the dtype
129 #: is assumed to be float.
130 columns = [('galtileid', None, numpy.int64),
131 ('galid', None, str, 30),
132 ('raJ2000', 'ra'),
133 ('decJ2000', 'dec'),
134 ('raJ2000Bulge', 'bra*PI()/180.'),
135 ('decJ2000Bulge', 'bdec*PI()/180.'),
136 ('raJ2000Disk', 'dra*PI()/180.'),
137 ('decJ2000Disk', 'ddec*PI()/180.'),
138 ('raJ2000Agn', 'agnra*PI()/180.'),
139 ('decJ2000Agn', 'agndec*PI()/180.'),
140 ('magNormBulge', 'magnorm_bulge'),
141 ('magNormDisk', 'magnorm_disk'),
142 ('magNormAgn', 'magnorm_agn'),
143 ('sedFilenameBulge', 'sedname_bulge', str, 40),
144 ('sedFilenameDisk', 'sedname_disk', str, 40),
145 ('sedFilenameAgn', 'sedname_agn', str, 40),
146 ('majorAxisBulge', 'a_b*PI()/648000.'),
147 ('minorAxisBulge', 'b_b*PI()/648000.'),
148 ('positionAngleBulge', 'pa_bulge*PI()/180.'),
149 ('sindexBulge', 'bulge_n', int),
150 ('majorAxisDisk', 'a_d*PI()/648000.'),
151 ('minorAxisDisk', 'b_d*PI()/648000.'),
152 ('positionAngleDisk', 'pa_disk*PI()/180.'),
153 ('sindexDisk', 'disk_n', int),
154 ('internalExtinctionModelBulge', 'ext_model_b', str, 3),
155 ('internalAvBulge', 'av_b'),
156 ('internalRvBulge', 'rv_b'),
157 ('internalExtinctionModelDisk', 'ext_model_d', str, 3),
158 ('internalAvDisk', 'av_d'),
159 ('internalRvDisk', 'rv_d'),
160 ('lsst_u', 'u_ab'),
161 ('lsst_g', 'g_ab'),
162 ('lsst_r', 'r_ab'),
163 ('lsst_i', 'i_ab'),
164 ('lsst_z', 'z_ab'),
165 ('lsst_y', 'y_ab')]
167 def _get_column_query(self, colnames=None):
168 raise NotImplementedError("We are calling a stored procedure so "
169 "no need to loop over columns")
171 def _final_pass(self, results):
172 """Modify the results of raJ2000 and decJ2000 to be in radians
173 **Parameters**
175 * results : Structured array of results from query
177 **Returns**
179 * results : Modified structured array
181 """
183 results['raJ2000'] = numpy.radians(results['raJ2000'])
184 results['decJ2000'] = numpy.radians(results['decJ2000'])
185 return results
187 def getIdColKey(self):
188 return 'galtileid'
190 def query_columns(self, colnames=None, chunk_size=None, obs_metadata=None, constraint=None,
191 limit=None):
192 """Execute a query
194 **Parameters**
196 * colnames : list or None
197 a list of valid column names, corresponding to entries in the
198 `columns` class attribute. If not specified, all columns are
199 queried.
200 * chunksize : int (optional)
201 if specified, then return an iterator object to query the database,
202 each time returning the next `chunksize` elements. If not
203 specified, all matching results will be returned.
204 * obs_metadata : object (optional)
205 object containing information on the observation including the region of the sky
206 to query and time of the observation.
207 * constraint : string (optional)
208 if specified, the predicate is added to the query verbatim using AND
209 * limit:
210 This kwarg is not actually used. It exists to preserve the same interface
211 as other definitions of query_columns elsewhere in CatSim. If not None,
212 a warning will be emitted, pointing out to the user that 'limit' is not used.
214 **Returns**
216 * result : structured array or iterator
217 If chunksize is not specified, then result is a structured array of all
218 items which match the specified query with columns named by the column
219 names in the columns class attribute. If chunksize is specified,
220 then result is an iterator over structured arrays of the given size.
222 """
223 if colnames is None:
224 colnames = [k for k in self.columnMap.keys()]
226 # We know that galtileid comes back with the query, but we don't want
227 # to add it to the query since it's generated on the fly.
228 #
229 # 25 August 2015
230 # The code below has been modified to remove all column names
231 # that contain 'galtileid.' This is to accommodate the
232 # CompoundInstanceCatalog and CompoundDBObject classes, which
233 # mangle column names such that they include the objid of the
234 # specific CatalogDBObject that is asking for them.
235 query_colnames = copy.deepcopy(colnames)
236 for name in query_colnames:
237 if 'galtileid' in name:
238 query_colnames.remove(name)
240 mappedcolnames = ["%s as %s"%(self.columnMap[x], x) for x in query_colnames]
241 mappedcolnames = ",".join(mappedcolnames)
243 if obs_metadata is not None and obs_metadata.bounds is not None:
244 if obs_metadata.bounds.boundType == 'circle':
245 regionStr = 'REGION CIRCLE J2000 %f %f %f'%(obs_metadata.bounds.RAdeg,
246 obs_metadata.bounds.DECdeg,
247 60.*obs_metadata.bounds.radiusdeg)
248 elif obs_metadata.bounds.boundType == 'box':
249 regionStr = 'REGION RECT J2000 %f %f %f %f'%(obs_metadata.bounds.RAminDeg,
250 obs_metadata.bounds.DECminDeg,
251 obs_metadata.bounds.RAmaxDeg,
252 obs_metadata.bounds.DECmaxDeg)
253 else:
254 raise RuntimeError("GalaxyTileObj does not know about boundType %s "
255 % obs_metadata.bounds.boundType)
256 else:
257 regionStr = 'REGION CIRCLE J2000 180. 0. 10800.'
258 warnings.warn("Searching over entire sky "
259 "since no bounds specified. "
260 "This could be a very bad idea "
261 "if the database is large")
263 query = """EXECUTE [LSSTCATSIM].[dbo].[GalaxySearch2015]
264 @ApertureStr = '%s', @ColumnNames = '%s',
265 @ComponentSubset = '%s' """ % (regionStr, mappedcolnames, self.componentSubset)
267 if constraint is not None:
268 query += ", @WhereClause = '%s'"%(constraint)
270 if limit is not None:
271 warnings.warn("You specified a row number limit in your query of a GalaxyTileObj "
272 "daughter class. Because of the way GalaxyTileObj is searched, row "
273 "number limits are not possible. If you really want to limit the number "
274 "of rows returned by you query, consider using GalaxyObj (note that you "
275 "will have to you limit your search to -2.5<RA<2.5 -2.25<Dec<2.25 -- both in "
276 "degrees -- as this is the only region where galaxies exist in GalaxyObj).")
278 return ChunkIterator(self, query, chunk_size)
281class GalaxyTileCompoundObj(CompoundCatalogDBObject, GalaxyTileObj):
282 """
283 This is a daughter class of CompoundCatalogDBObject that specifically
284 inherits from GalaxyTileObj. This is necessary because GalaxyTileObj
285 implements a custom query_columns that executes a stored procedure
286 on fatboy (as opposed to the generic query_columns implemented in
287 CatalogDBObject, which converts self.columns into a SQL
288 query in the most naive way possible).
289 """
291 doRunTest = False # because this is not like other CatalogDBObjects
292 # and should not be tested in the same way
294 _table_restriction = ['galaxy']
296 def _final_pass(self, results):
297 for name in results.dtype.fields:
298 if 'raJ2000' in name or 'decJ2000' in name:
299 results[name] = numpy.radians(results[name])
301 return results
304class GalaxyBulgeObj(GalaxyTileObj):
305 objid = 'galaxyBulge'
306 componentSubset = 'BULGE'
307 raColName = 'ra'
308 decColName = 'dec'
309 objectTypeId = 26
310 doRunTest = True
311 testObservationMetaData = ObservationMetaData(boundType='circle', pointingRA=10.0, pointingDec=-45.0,
312 boundLength=0.01, mjd=53000., bandpassName='r', m5=22.0)
313 #: The following maps column names to database schema. The tuples
314 #: must be at least length 2. If column name is the same as the name
315 #: in the DB the mapping element may be None. The rest of the tuple
316 #: should be formatted like a numpy.dtype. If ommitted, the dtype
317 #: is assumed to be float.
318 columns = [('galtileid', None, numpy.int64),
319 ('galid', None, str, 30),
320 ('componentra', 'bra*PI()/180.'),
321 ('componentdec', 'bdec*PI()/180.'),
322 #: This is actually a problem with the stored procedure.
323 #: We need to be able to map columns other than
324 #: just ra/dec to raJ2000/decJ2000. This gets
325 #: important when we start perturbing the three galaxy components
326 ('raJ2000', 'ra'),
327 ('decJ2000', 'dec'),
328 ('magNorm', 'magnorm_bulge'),
329 ('magNormBulge', 'magnorm_bulge'),
330 ('sedFilename', 'sedname_bulge', str, 40),
331 ('sedFilenameBulge', 'sedname_bulge', str, 40),
332 ('majorAxis', 'a_b*PI()/648000.'),
333 ('minorAxis', 'b_b*PI()/648000.'),
334 ('positionAngle', 'pa_bulge*PI()/180.'),
335 ('sindex', 'bulge_n', int),
336 ('halfLightRadius', 'BulgeHalfLightRadius*PI()/648000.'),
337 ('internalExtinctionModel', 'ext_model_b', str, 3),
338 ('internalAv', 'av_b'),
339 ('internalRv', 'rv_b'),
340 ('internalAvBulge', 'av_b'),
341 ('internalRvBulge', 'rv_b'),
342 ('lsst_u', 'u_ab'),
343 ('lsst_g', 'g_ab'),
344 ('lsst_r', 'r_ab'),
345 ('lsst_i', 'i_ab'),
346 ('lsst_z', 'z_ab'),
347 ('lsst_y', 'y_ab')]
350class GalaxyDiskObj(GalaxyTileObj):
351 objid = 'galaxyDisk'
352 componentSubset = 'DISK'
353 raColName = 'ra'
354 decColName = 'dec'
355 objectTypeId = 27
356 doRunTest = True
357 testObservationMetaData = ObservationMetaData(boundType='circle', pointingRA=66.0, pointingDec=-80.0,
358 boundLength=0.01, mjd=53730., bandpassName='r', m5=22.0)
359 #: The following maps column names to database schema. The tuples
360 #: must be at least length 2. If column name is the same as the name
361 #: in the DB the mapping element may be None. The rest of the tuple
362 #: should be formatted like a numpy.dtype. If ommitted, the dtype
363 #: is assumed to be float.
364 columns = [('galtileid', None, numpy.int64),
365 ('galid', None, str, 30),
366 ('componentra', 'dra*PI()/180.'),
367 ('componentdec', 'ddec*PI()/180.'),
368 #: This is actually a problem with the stored procedure.
369 #: We need to be able to map columns other than
370 #: just ra/dec to raJ2000/decJ2000. This gets
371 #: important when we start perturbing the three galaxy components
372 ('raJ2000', 'ra'),
373 ('decJ2000', 'dec'),
374 ('magNorm', 'magnorm_disk'),
375 ('magNormDisk', 'magnorm_disk'),
376 ('sedFilename', 'sedname_disk', str, 40),
377 ('sedFilenameDisk', 'sedname_disk', str, 40),
378 ('majorAxis', 'a_d*PI()/648000.'),
379 ('minorAxis', 'b_d*PI()/648000.'),
380 ('positionAngle', 'pa_disk*PI()/180.'),
381 ('sindex', 'disk_n', int),
382 ('halfLightRadius', 'DiskHalfLightRadius*PI()/648000.'),
383 ('internalExtinctionModel', 'ext_model_d', str, 3),
384 ('internalAv', 'av_d'),
385 ('internalRv', 'rv_d'),
386 ('internalAvDisk', 'av_d'),
387 ('internalRvDisk', 'rv_d'),
388 ('lsst_u', 'u_ab'),
389 ('lsst_g', 'g_ab'),
390 ('lsst_r', 'r_ab'),
391 ('lsst_i', 'i_ab'),
392 ('lsst_z', 'z_ab'),
393 ('lsst_y', 'y_ab')]
396class GalaxyAgnObj(GalaxyTileObj):
397 objid = 'galaxyAgn'
398 componentSubset = 'AGN'
399 raColName = 'ra'
400 decColName = 'dec'
401 objectTypeId = 28
402 doRunTest = True
403 testObservationMetaData = ObservationMetaData(boundType='circle', pointingRA=234.0, pointingDec=-15.0,
404 boundLength=0.01, mjd=51000., bandpassName='r', m5=22.0)
405 #: The following maps column names to database schema. The tuples
406 #: must be at least length 2. If column name is the same as the name
407 #: in the DB the mapping element may be None. The rest of the tuple
408 #: should be formatted like a numpy.dtype. If ommitted, the dtype
409 #: is assumed to be float.
410 columns = [('galtileid', None, numpy.int64),
411 ('galid', None, str, 30),
412 ('componentra', 'agnra*PI()/180.'),
413 ('componentdec', 'agndec*PI()/180.'),
414 #: This is actually a problem with the stored procedure.
415 #: We need to be able to map columns other than
416 #: just ra/dec to raJ2000/decJ2000. This gets
417 #: important when we start perturbing the three galaxy components
418 ('raJ2000', 'ra'),
419 ('decJ2000', 'dec'),
420 ('magNorm', 'magnorm_agn'),
421 ('magNormAgn', 'magnorm_agn'),
422 ('sedFilename', 'sedname_agn', str, 40),
423 ('sedFilenameAgn', 'sedname_agn', str, 40),
424 ('variabilityParameters', 'varParamStr', str, 256),
425 ('lsst_u', 'u_ab'),
426 ('lsst_g', 'g_ab'),
427 ('lsst_r', 'r_ab'),
428 ('lsst_i', 'i_ab'),
429 ('lsst_z', 'z_ab'),
430 ('lsst_y', 'y_ab')]
433class ImageAgnObj(BaseCatalogObj):
434 objid = 'imageagn'
435 tableid = 'image'
436 idColKey = 'galid'
437 raColName = 'ra'
438 decColName = 'dec'
439 objectTypeId = 29
440 doRunTest = True
441 #: all sky since this is a small set.
442 testObservationMetaData = ObservationMetaData(mjd=53000., bandpassName='r', m5=22.0)
444 dbDefaultValues = {'varsimobjid': -1, 'myid': -1}
445 #: The following maps column names to database schema. The tuples
446 #: must be at least length 2. If column name is the same as the name
447 #: in the DB the mapping element may be None. The rest of the tuple
448 #: should be formatted like a numpy.dtype. If ommitted, the dtype
449 #: is assumed to be float.
450 columns = [('galid', 'id', int),
451 ('raJ2000', 'ra*PI()/180.'),
452 ('decJ2000', 'dec*PI()/180.'),
453 ('magNorm', 'magnorm_agn'),
454 ('sedFilename', 'sedname_agn', str, 40),
455 ('variabilityParameters', 'varParamStr', str, 256),
456 ('lsst_u', 'u_ab'),
457 ('lsst_g', 'g_ab'),
458 ('lsst_r', 'r_ab'),
459 ('lsst_i', 'i_ab'),
460 ('lsst_z', 'z_ab'),
461 ('lsst_y', 'y_ab')]
464class LensGalaxyObj(BaseCatalogObj):
465 objid = 'lensgalaxy'
466 tableid = 'lens'
467 idColKey = 'galid'
468 raColName = 'ra'
469 decColName = 'dec'
470 objectTypeId = 30
471 doRunTest = True
472 #: all sky since this is a small set.
473 testObservationMetaData = ObservationMetaData(mjd=53000., bandpassName='r', m5=22.0)
475 dbDefaultValues = {'varsimobjid': -1, 'myid': -1, 'variabilityParameters': None}
476 #: The following maps column names to database schema. The tuples
477 #: must be at least length 2. If column name is the same as the name
478 #: in the DB the mapping element may be None. The rest of the tuple
479 #: should be formatted like a numpy.dtype. If ommitted, the dtype
480 #: is assumed to be float.
481 columns = [('galid', 'id', int),
482 ('raJ2000', 'ra_bulge*PI()/180.'),
483 ('decJ2000', 'dec_bulge*PI()/180.'),
484 ('magNorm', 'magnorm_bulge'),
485 ('sedFilename', 'sedname_bulge', str, 40),
486 ('lsst_u', 'u_ab'),
487 ('lsst_g', 'g_ab'),
488 ('lsst_r', 'r_ab'),
489 ('lsst_i', 'i_ab'),
490 ('lsst_z', 'z_ab'),
491 ('lsst_y', 'y_ab')]