Hide keyboard shortcuts

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 

7 

8__all__ = ["GalaxyObj", "GalaxyTileObj", "GalaxyBulgeObj", 

9 "GalaxyDiskObj", "GalaxyAgnObj", "ImageAgnObj", "LensGalaxyObj", 

10 "GalaxyTileCompoundObj"] 

11 

12 

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 

17 

18 -2.5 deg < RA < 2.5 deg, -2.5 deg < Dec < 2.5 deg 

19 

20 In order to cover the whole sky, call one of the objects that 

21 inherits from GalaxyTileObj 

22 """ 

23 objid = 'galaxyBase' 

24 

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 

32 

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) 

36 

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} 

46 

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')] 

87 

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 

94 

95 

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 """ 

101 

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 

110 

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) 

114 

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} 

124 

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')] 

166 

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") 

170 

171 def _final_pass(self, results): 

172 """Modify the results of raJ2000 and decJ2000 to be in radians 

173 **Parameters** 

174 

175 * results : Structured array of results from query 

176 

177 **Returns** 

178 

179 * results : Modified structured array 

180 

181 """ 

182 

183 results['raJ2000'] = numpy.radians(results['raJ2000']) 

184 results['decJ2000'] = numpy.radians(results['decJ2000']) 

185 return results 

186 

187 def getIdColKey(self): 

188 return 'galtileid' 

189 

190 def query_columns(self, colnames=None, chunk_size=None, obs_metadata=None, constraint=None, 

191 limit=None): 

192 """Execute a query 

193 

194 **Parameters** 

195 

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. 

213 

214 **Returns** 

215 

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. 

221 

222 """ 

223 if colnames is None: 

224 colnames = [k for k in self.columnMap.keys()] 

225 

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) 

239 

240 mappedcolnames = ["%s as %s"%(self.columnMap[x], x) for x in query_colnames] 

241 mappedcolnames = ",".join(mappedcolnames) 

242 

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") 

262 

263 query = """EXECUTE [LSSTCATSIM].[dbo].[GalaxySearch2015] 

264 @ApertureStr = '%s', @ColumnNames = '%s', 

265 @ComponentSubset = '%s' """ % (regionStr, mappedcolnames, self.componentSubset) 

266 

267 if constraint is not None: 

268 query += ", @WhereClause = '%s'"%(constraint) 

269 

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).") 

277 

278 return ChunkIterator(self, query, chunk_size) 

279 

280 

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 """ 

290 

291 doRunTest = False # because this is not like other CatalogDBObjects 

292 # and should not be tested in the same way 

293 

294 _table_restriction = ['galaxy'] 

295 

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]) 

300 

301 return results 

302 

303 

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')] 

348 

349 

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')] 

394 

395 

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')] 

431 

432 

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) 

443 

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')] 

462 

463 

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) 

474 

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')]