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

1# -*- coding: utf-8 -*- 

2""" 

3Created on Wed Feb 25 14:07:03 2015 

4 

5@author: Bryce Kalmbach 

6""" 

7from __future__ import print_function 

8from builtins import zip 

9from builtins import str 

10from builtins import range 

11from builtins import object 

12import numpy as np 

13import os 

14import re 

15import lsst.utils 

16from lsst.sims.photUtils import Sed 

17from lsst.sims.photUtils import Bandpass 

18from lsst.sims.utils import SpecMap 

19 

20__all__ = ["matchBase", "matchStar", "matchGalaxy"] 

21 

22class matchBase(object): 

23 

24 """ 

25 This class is designed to provide methods that will be useful to both selectStarSED and selectGalaxySED. 

26 """ 

27 

28 def calcMagNorm(self, objectMags, sedObj, bandpassDict, mag_error = None, 

29 redshift = None, filtRange = None): 

30 

31 """ 

32 This will find the magNorm value that gives the closest match to the magnitudes of the object 

33 using the matched SED. Uses scipy.optimize.leastsq to find the values of fluxNorm that minimizes 

34 the function: ((flux_obs - (fluxNorm*flux_model))/flux_error)**2. 

35 

36 @param [in] objectMags are the magnitude values for the object with extinction matching that of 

37 the SED object. In the normal case using the selectSED routines above it will be dereddened mags. 

38 

39 @param [in] sedObj is an Sed class instance that is set with the wavelength and flux of the 

40 matched SED 

41 

42 @param [in] bandpassDict is a BandpassDict class instance with the Bandpasses set to those 

43 for the magnitudes given for the catalog object 

44 

45 @param [in] mag_error are provided error values for magnitudes in objectMags. If none provided 

46 then this defaults to 1.0. This should be an array of the same length as objectMags. 

47 

48 @param [in] redshift is the redshift of the object if the magnitude is observed 

49 

50 @param [in] filtRange is a selected range of filters specified by their indices in the bandpassList 

51 to match up against. Used when missing data in some magnitude bands. 

52 

53 @param [out] bestMagNorm is the magnitude normalization for the given magnitudes and SED 

54 """ 

55 

56 import scipy.optimize as opt 

57 

58 sedTest = Sed() 

59 sedTest.setSED(sedObj.wavelen, flambda = sedObj.flambda) 

60 if redshift is not None: 

61 sedTest.redshiftSED(redshift) 

62 imSimBand = Bandpass() 

63 imSimBand.imsimBandpass() 

64 zp = -2.5*np.log10(3631) #Note using default AB zeropoint 

65 flux_obs = np.power(10,(objectMags + zp)/(-2.5)) 

66 sedTest.resampleSED(wavelen_match=bandpassDict.wavelenMatch) 

67 sedTest.flambdaTofnu() 

68 flux_model = sedTest.manyFluxCalc(bandpassDict.phiArray, bandpassDict.wavelenStep) 

69 if filtRange is not None: 

70 flux_obs = flux_obs[filtRange] 

71 flux_model = flux_model[filtRange] 

72 if mag_error is None: 

73 flux_error = np.ones(len(flux_obs)) 

74 else: 

75 flux_error = np.abs(flux_obs*(np.log(10)/(-2.5))*mag_error) 

76 bestFluxNorm = opt.leastsq(lambda x: ((flux_obs - (x*flux_model))/flux_error), 1.0)[0][0] 

77 sedTest.multiplyFluxNorm(bestFluxNorm) 

78 bestMagNorm = sedTest.calcMag(imSimBand) 

79 return bestMagNorm 

80 

81 def calcBasicColors(self, sedList, bandpassDict, makeCopy = False): 

82 

83 """ 

84 This will calculate a set of colors from a list of SED objects when there is no need to redshift 

85 the SEDs. 

86 

87 @param [in] sedList is the set of spectral objects from the models SEDs provided by loaders in 

88 rgStar or rgGalaxy. NOTE: Since this uses photometryBase.manyMagCalc_list the SED objects 

89 will be changed. 

90 

91 @param [in] bandpassDict is a BandpassDict class instance with the Bandpasses set to those 

92 for the magnitudes given for the catalog object 

93 

94 @param [in] makeCopy indicates whether or not to operate on copies of the SED objects in sedList 

95 since this method will change the wavelength grid. 

96 

97 @param [out] modelColors is the set of colors in the Bandpasses provided for the given sedList. 

98 """ 

99 

100 modelColors = [] 

101 

102 for specObj in sedList: 

103 if makeCopy==True: 

104 fileSED = Sed() 

105 fileSED.setSED(wavelen = specObj.wavelen, flambda = specObj.flambda) 

106 sEDMags = bandpassDict.magListForSed(fileSED) 

107 else: 

108 sEDMags = bandpassDict.magListForSed(specObj) 

109 colorInfo = [] 

110 for filtNum in range(0, len(bandpassDict)-1): 

111 colorInfo.append(sEDMags[filtNum] - sEDMags[filtNum+1]) 

112 modelColors.append(colorInfo) 

113 

114 return modelColors 

115 

116 def deReddenMags(self, ebvVals, catMags, extCoeffs): 

117 

118 """ 

119 This will correct for extinction effects in a set of catalog Magnitudes. 

120 

121 @param [in] ebvVals is a list of ebv Values from calculateEBV in ebv.py or given by user that 

122 correspond to the same set of objects as the set of magnitudes. 

123 

124 @param [in] catMags is an array of the magnitudes of the catalog objects. 

125 

126 @param [in] extCoeffs is a list of the coefficients which should come 

127 from Schlafly and Finkbeiner (2011) (ApJ, 737, 103) for the same filters and in the same order 

128 as the catalog mags. 

129 

130 @param [out] deRedMags is the array of corrected magnitudes. 

131 """ 

132 

133 deRedMags = catMags - np.outer(np.array(ebvVals), np.array(extCoeffs)) 

134 

135 return deRedMags 

136 

137class matchStar(matchBase): 

138 

139 """ 

140 This class provides loading routines for the star SEDs currently in sims_sed_library. 

141 To load one's own SED library, the user can subclass this and add their own loader following 

142 the format of those included here. 

143 """ 

144 

145 def __init__(self, sEDDir=None, kuruczDir=None, mltDir=None, wdDir=None): 

146 

147 """ 

148 @param [in] sEDDir is a place to specify a different path to a directory that follows the same 

149 directory structure as SIMS_SED_LIBRARY. If not specified, this will 

150 attempt to default to the SIMS_SED_LIBRARY. 

151 

152 @param [in] kuruczDir is a place to specify a different path to kurucz SED files than the 

153 files in the LSST sims_sed_library. If set to None it will default to the LSST library. 

154 Will probably be most useful for those who want to use loadGalfast without downloading the 

155 entire LSST sims_sed_library which contains much more than just the star SEDs. 

156 

157 @param [in] mltDir is the same as kuruczPath except that it specifies a directory for the 

158 mlt SEDs 

159 

160 @param [in] wdDir is the same as the previous two except that it specifies a path to an 

161 alternate white dwarf SED directory. 

162 """ 

163 #Use SpecMap to pull the directory locations 

164 specMap = SpecMap() 

165 self.specMapDict = {} 

166 specFileStart = ['kp', 'burrows', 'bergeron'] #The beginning of filenames of different SED types 

167 specFileTypes = ['kurucz', 'mlt', 'wd'] 

168 for specStart, specKey in zip(specFileStart, specFileTypes): 

169 for key, val in sorted(specMap.subdir_map.items()): 

170 if re.match(key, specStart): 

171 self.specMapDict[specKey] = str(val) 

172 

173 if sEDDir is None: 

174 try: 

175 self.sEDDir = lsst.utils.getPackageDir('sims_sed_library') 

176 except: 

177 self.sEDDir = None 

178 else: 

179 self.sEDDir = sEDDir 

180 

181 self.kuruczDir = kuruczDir 

182 self.mltDir = mltDir 

183 self.wdDir = wdDir 

184 

185 

186 def loadKuruczSEDs(self, subset = None): 

187 """ 

188 By default will load all seds in kurucz directory. The user can also define a subset of 

189 what's in the directory and load only those SEDs instead. Will skip over extraneous 

190 files in sed folder. 

191 

192 @param [in] subset is the list of the subset of files wanted if one doesn't want all files 

193 in the kurucz directory. 

194 

195 @param [out] sedList is the set of model SED spectra objects to be passed onto the matching 

196 routines. 

197 """ 

198 

199 if self.kuruczDir is None: 

200 try: 

201 self.kuruczDir = str(self.sEDDir + '/' + 

202 self.specMapDict['kurucz'] + '/') 

203 except: 

204 raise ValueError(str('self.kuruczDir is None. ' + 

205 'Add path to kurucz directory.')) 

206 

207 files = [] 

208 

209 if subset is None: 

210 for fileName in os.listdir(self.kuruczDir): 

211 files.append(fileName) 

212 else: 

213 for fileName in subset: 

214 files.append(fileName) 

215 

216 numFiles = len(files) 

217 numOn = 0 

218 

219 sedList = [] 

220 

221 for fileName in files: 

222 if numOn % 100 == 0: 

223 print('Loading %i of %i: Kurucz SEDs' % (numOn, numFiles)) 

224 

225 try: 

226 spec = Sed() 

227 spec.readSED_flambda(str(self.kuruczDir + '/' + fileName)) 

228 

229 logZTimesTen, temp, gravity, fineTemp = [x.split(".")[0] for x in fileName.split("_")] 

230 

231 if logZTimesTen[1] == 'm': 

232 spec.logZ = -1.0 * float(logZTimesTen[2:]) * 0.1 

233 else: 

234 spec.logZ = float(logZTimesTen[2:]) * 0.1 

235 

236 spec.logg = float(gravity[1:]) * 0.1 

237 spec.temp = float(fineTemp) 

238 spec.name = fileName 

239 

240 except: 

241 continue 

242 

243 sedList.append(spec) 

244 

245 numOn += 1 

246 

247 return sedList 

248 

249 def loadmltSEDs(self, subset = None): 

250 

251 """ 

252 By default will load all seds in mlt directory. The user can also define a subset of 

253 what's in the directory and load only those SEDs instead. Will skip over extraneous 

254 files in sed folder. 

255 

256 @param [in] subset is the list of the subset of files wanted if one doesn't want all files 

257 in the mlt directory. 

258 

259 @param [out] sedList is the set of model SED spectra objects to be passed onto the matching 

260 routines. 

261 """ 

262 

263 if self.mltDir is None: 

264 try: 

265 self.mltDir = str(self.sEDDir + '/' + 

266 self.specMapDict['mlt'] + '/') 

267 except: 

268 raise ValueError(str('self.mltDir is None. ' + 

269 'Add path to mlt directory.')) 

270 

271 files = [] 

272 

273 if subset is None: 

274 for fileName in os.listdir(self.mltDir): 

275 files.append(fileName) 

276 else: 

277 for fileName in subset: 

278 files.append(fileName) 

279 

280 numFiles = len(files) 

281 numOn = 0 

282 

283 sedList = [] 

284 

285 for fileName in files: 

286 if numOn % 100 == 0: 

287 print('Loading %i of %i: MLT SEDs' % (numOn, numFiles)) 

288 

289 try: 

290 spec = Sed() 

291 spec.readSED_flambda(str(self.mltDir + '/' + fileName)) 

292 spec.name = fileName 

293 

294 except: 

295 continue 

296 

297 sedList.append(spec) 

298 

299 numOn += 1 

300 

301 return sedList 

302 

303 

304 def loadwdSEDs(self, subset = None): 

305 

306 """ 

307 By default will load all seds in wd directory. The user can also define a subset of 

308 what's in the directory and load only those SEDs instead. Will skip over extraneous 

309 files in sed folder. 

310 

311 @param [in] subset is the list of the subset of files wanted if one doesn't want all files 

312 in the kurucz directory. 

313 

314 @param [out] sedListH is the set of model SED spectra objects for Hydrogen WDs to be passed onto 

315 the matching routines. 

316 

317 @param [out] sedListHE is the set of model SED spectra objects for Helium WDs to be passed onto 

318 the matching routines. 

319 """ 

320 

321 if self.wdDir is None: 

322 try: 

323 self.wdDir = str(self.sEDDir + '/' + 

324 self.specMapDict['wd'] + '/') 

325 except: 

326 raise ValueError(str('self.wdDir is None. ' + 

327 'Add path to wddirectory.')) 

328 

329 

330 files = [] 

331 

332 if subset is None: 

333 for fileName in os.listdir(self.wdDir): 

334 files.append(fileName) 

335 else: 

336 for fileName in subset: 

337 files.append(fileName) 

338 

339 numFiles = len(files) 

340 numOn = 0 

341 

342 sedListH = [] 

343 sedListHE = [] 

344 

345 for fileName in files: 

346 if numOn % 100 == 0: 

347 print('Loading %i of %i: WD SEDs' % (numOn, numFiles)) 

348 

349 try: 

350 spec = Sed() 

351 spec.readSED_flambda(str(self.wdDir + '/' + fileName)) 

352 spec.name = fileName 

353 if fileName.split("_")[1] == 'He': 

354 sedListHE.append(spec) 

355 else: 

356 sedListH.append(spec) 

357 

358 except: 

359 continue 

360 

361 numOn += 1 

362 

363 return sedListH, sedListHE 

364 

365class matchGalaxy(matchBase): 

366 

367 """ 

368 This class provides loading routines for the galaxy SEDs currently in sims_sed_library. 

369 To load one's own SED library, the user can subclass this and add their own loader following 

370 the format of those included here. 

371 """ 

372 

373 def __init__(self, galDir = None): 

374 

375 """ 

376 @param [in] galDir is the directory where the galaxy SEDs are stored 

377 """ 

378 if galDir is None: 

379 # Use SpecMap to pull in directory's location in LSST Stack 

380 specMap = SpecMap() 

381 specFileStart = 'Exp' #Start of sample BC03 name in sims_sed_library 

382 for key, val in sorted(specMap.subdir_map.items()): 

383 if re.match(key, specFileStart): 

384 galSpecDir = str(val) 

385 self.galDir = str(lsst.utils.getPackageDir('sims_sed_library') + '/' + galSpecDir) 

386 else: 

387 self.galDir = galDir 

388 

389 

390 def loadBC03(self, subset = None): 

391 

392 """ 

393 This loads the Bruzual and Charlot SEDs that are currently in the SIMS_SED_LIBRARY. 

394 If the user wants to use different SEDs another loading method can be created and used in place 

395 of this. 

396 

397 @param [in] subset is the list of the subset of files in the galDir that the user 

398 can specify if using all the SEDs in the directory is not desired. 

399 

400 @param [out] sedList is the set of model SED spectra objects to be passed onto the matching routines. 

401 """ 

402 

403 if self.galDir is None: 

404 raise ValueError('self.galDir is None. Add path to galaxy directory.') 

405 

406 files = [] 

407 

408 if subset is None: 

409 for fileName in os.listdir(self.galDir): 

410 files.append(fileName) 

411 else: 

412 for fileName in subset: 

413 files.append(fileName) 

414 

415 numFiles = len(files) 

416 numOn = 0 

417 

418 sedList = [] 

419 

420 for fileName in files: 

421 if numOn % 100 == 0: 

422 print('Loading %i of %i: BC Galaxy SEDs' % (numOn, numFiles)) 

423 

424 try: 

425 spec = Sed() 

426 spec.readSED_flambda(str(self.galDir + '/' + fileName)) 

427 spec.name = fileName 

428 fileNameAsList = fileName.split('.') 

429 spec.type = fileNameAsList[0] 

430 spec.age = float(fileNameAsList[1]) 

431 metallicity = fileNameAsList[2].split('Z')[0] 

432 #Final form is z/zSun 

433 spec.metallicity = float(metallicity) * (10 ** ((len(metallicity)-1)*-1)) 

434 

435 except: 

436 continue 

437 

438 sedList.append(spec) 

439 

440 numOn += 1 

441 

442 return sedList