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

1from builtins import zip 

2from builtins import object 

3import os 

4import numpy 

5from astropy.io import fits 

6 

7from lsst.sims.utils.CodeUtilities import sims_clean_up 

8from lsst.sims.utils import _galacticFromEquatorial 

9from functools import reduce 

10 

11__all__ = ["EBVmap", "EBVbase"] 

12 

13 

14def interp1D(z1, z2, offset): 

15 """ 1D interpolation on a grid""" 

16 

17 zPrime = (z2-z1)*offset + z1 

18 

19 return zPrime 

20 

21 

22class EBVmap(object): 

23 '''Class for describing a map of EBV 

24 

25 Images are read in from a fits file and assume a ZEA projection 

26 ''' 

27 

28 def __del__(self): 

29 self.hdulist.close() 

30 

31 def readMapFits(self, fileName): 

32 """ read a fits file containing the ebv data""" 

33 

34 self._file_name = fileName 

35 

36 self.hdulist = fits.open(fileName) 

37 self.header = self.hdulist[0].header 

38 self.data = self.hdulist[0].data 

39 self.nr = self.data.shape[0] 

40 self.nc = self.data.shape[1] 

41 

42 # read WCS information 

43 self.cd11 = self.header['CD1_1'] 

44 self.cd22 = self.header['CD2_2'] 

45 self.cd12 = 0. 

46 self.cd21 = 0. 

47 

48 self.crpix1 = self.header['CRPIX1'] 

49 self.crval1 = self.header['CRVAL1'] 

50 

51 self.crpix2 = self.header['CRPIX2'] 

52 self.crval2 = self.header['CRVAL2'] 

53 

54 # read projection information 

55 self.nsgp = self.header['LAM_NSGP'] 

56 self.scale = self.header['LAM_SCAL'] 

57 self.lonpole = self.header['LONPOLE'] 

58 

59 def xyFromSky(self, gLon, gLat): 

60 """ convert long, lat angles to pixel x y 

61 

62 input angles are in radians but the conversion assumes radians 

63 

64 @param [in] gLon galactic longitude in radians 

65 

66 @param [in] gLat galactic latitude in radians 

67 

68 @param [out] x is the x pixel coordinate 

69 

70 @param [out] y is the y pixel coordinate 

71 

72 """ 

73 

74 rad2deg = 180./numpy.pi 

75 

76 # use the SFD approach to define xy pixel positions 

77 # ROTATION - Equn (4) - degenerate case 

78 if (self.crval2 > 89.9999): 

79 theta = gLat*rad2deg 

80 phi = gLon*rad2deg + 180.0 + self.lonpole - self.crval1 

81 elif (self.crval2 < -89.9999): 

82 theta = -gLat*rad2deg 

83 phi = self.lonpole + self.crval1 - gLon*rad2deg 

84 else: 

85 # Assume it's an NGP projection ... 

86 theta = gLat*rad2deg 

87 phi = gLon*rad2deg + 180.0 + self.lonpole - self.crval1 

88 

89 # Put phi in the range [0,360) degrees 

90 phi = phi - 360.0 * numpy.floor(phi/360.0) 

91 

92 # FORWARD MAP PROJECTION - Equn (26) 

93 Rtheta = 2.0 * rad2deg * numpy.sin((0.5 / rad2deg) * (90.0 - theta)) 

94 

95 # Equns (10), (11) 

96 xr = Rtheta * numpy.sin(phi / rad2deg) 

97 yr = - Rtheta * numpy.cos(phi / rad2deg) 

98 

99 # SCALE FROM PHYSICAL UNITS - Equn (3) after inverting the matrix 

100 denom = self.cd11 * self.cd22 - self.cd12 * self.cd21 

101 x = (self.cd22 * xr - self.cd12 * yr) / denom + (self.crpix1 - 1.0) 

102 y = (self.cd11 * yr - self.cd21 * xr) / denom + (self.crpix2 - 1.0) 

103 

104 return x, y 

105 

106 def generateEbv(self, glon, glat, interpolate = False): 

107 """ 

108 Calculate EBV with option for interpolation 

109 

110 @param [in] glon galactic longitude in radians 

111 

112 @param [in] galactic latitude in radians 

113 

114 @param [out] ebvVal the scalar value of EBV extinction 

115 

116 """ 

117 

118 # calculate pixel values 

119 x, y = self.xyFromSky(glon, glat) 

120 

121 ix = (x + 0.5).astype(int) 

122 iy = (y + 0.5).astype(int) 

123 

124 unity = numpy.ones(len(ix), dtype=int) 

125 

126 if (interpolate): 

127 

128 # find the indices of the pixels bounding the point of interest 

129 ixLow = numpy.minimum(ix, (self.nc - 2)*unity) 

130 ixHigh = ixLow + 1 

131 dx = x - ixLow 

132 

133 iyLow = numpy.minimum(iy, (self.nr - 2)*unity) 

134 iyHigh = iyLow + 1 

135 dy = y - iyLow 

136 

137 # interpolate the EBV value at the point of interest by interpolating 

138 # first in x and then in y 

139 x1 = numpy.array([self.data[ii][jj] for (ii, jj) in zip(iyLow, ixLow)]) 

140 x2 = numpy.array([self.data[ii][jj] for (ii, jj) in zip(iyLow, ixHigh)]) 

141 xLow = interp1D(x1, x2, dx) 

142 

143 x1 = numpy.array([self.data[ii][jj] for (ii, jj) in zip(iyHigh, ixLow)]) 

144 x2 = numpy.array([self.data[ii][jj] for (ii, jj) in zip(iyHigh, ixHigh)]) 

145 xHigh = interp1D(x1, x2, dx) 

146 

147 ebvVal = interp1D(xLow, xHigh, dy) 

148 

149 else: 

150 ebvVal = numpy.array([self.data[ii][jj] for (ii, jj) in zip(iy, ix)]) 

151 

152 return ebvVal 

153 

154 def xyIntFromSky(self, gLong, gLat): 

155 x, y = self.xyFromSky(gLong, gLat) 

156 ix = int(x + 0.5) 

157 iy = int(y + 0.5) 

158 

159 return ix, iy 

160 

161 

162class EBVbase(object): 

163 """ 

164 This class will give users access to calculateEbv oustide of the framework of a catalog. 

165 

166 To find the value of EBV at a point on the sky, create an instance of this object, and 

167 then call calculateEbv passing the coordinates of interest as kwargs 

168 

169 e.g. 

170 

171 ebvObject = EBVbase() 

172 ebvValue = ebvObject.calculateEbv(galacticCoordinates = myGalacticCoordinates) 

173 

174 or 

175 

176 ebvValue = ebvObject.calculateEbv(equatorialCoordinates = myEquatorialCoordinates) 

177 

178 where myGalacticCoordinates is a 2-d numpy array where the first row is galactic longitude 

179 and the second row is galactic latitude. 

180 

181 myEquatorialCoordinates is a 2-d numpy array where the first row is RA and the second row 

182 is dec 

183 

184 All coordinates are in radians 

185 

186 You can also specify dust maps in the northern and southern galactic hemispheres, but 

187 there are default values that the code will automatically load (see the class variables 

188 below). 

189 

190 The information regarding where the dust maps are located is stored in 

191 member variables ebvDataDir, ebvMapNorthName, ebvMapSouthName 

192 

193 The actual dust maps (when loaded) are stored in ebvMapNorth and ebvMapSouth 

194 """ 

195 

196 # these variables will tell the mixin where to get the dust maps 

197 ebvDataDir = os.environ.get("SIMS_MAPS_DIR") 

198 ebvMapNorthName = "DustMaps/SFD_dust_4096_ngp.fits" 

199 ebvMapSouthName = "DustMaps/SFD_dust_4096_sgp.fits" 

200 ebvMapNorth = None 

201 ebvMapSouth = None 

202 

203 # A dict to hold every open instance of an EBVmap. 

204 # Since this is being declared outside of the constructor, 

205 # it will be a class member, which means that, every time 

206 # an EBVmap is added to the cache, all EBVBase instances will 

207 # know about it. 

208 _ebv_map_cache = {} 

209 

210 # the set_xxxx routines below will allow the user to point elsewhere for the dust maps 

211 def set_ebvMapNorth(self, word): 

212 """ 

213 This allows the user to pick a new northern SFD map file 

214 """ 

215 self.ebvMapNorthName = word 

216 

217 def set_ebvMapSouth(self, word): 

218 """ 

219 This allows the user to pick a new southern SFD map file 

220 """ 

221 self.ebvMapSouthName = word 

222 

223 # these routines will load the dust maps for the galactic north and south hemispheres 

224 def _load_ebv_map(self, file_name): 

225 """ 

226 Load the EBV map specified by file_name. If that map has already been loaded, 

227 just return the map stored in self._ebv_map_cache. If it must be loaded, store 

228 it in the cache. 

229 """ 

230 if file_name in self._ebv_map_cache: 

231 return self._ebv_map_cache[file_name] 

232 

233 ebv_map = EBVmap() 

234 ebv_map.readMapFits(file_name) 

235 self._ebv_map_cache[file_name] = ebv_map 

236 return ebv_map 

237 

238 def load_ebvMapNorth(self): 

239 """ 

240 This will load the northern SFD map 

241 """ 

242 file_name = os.path.join(self.ebvDataDir, self.ebvMapNorthName) 

243 self.ebvMapNorth = self._load_ebv_map(file_name) 

244 return None 

245 

246 def load_ebvMapSouth(self): 

247 """ 

248 This will load the southern SFD map 

249 """ 

250 file_name = os.path.join(self.ebvDataDir, self.ebvMapSouthName) 

251 self.ebvMapSouth = self._load_ebv_map(file_name) 

252 return None 

253 

254 def calculateEbv(self, galacticCoordinates=None, equatorialCoordinates=None, northMap=None, southMap=None, 

255 interp=False): 

256 """ 

257 For an array of Gal long, lat calculate E(B-V) 

258 

259 

260 @param [in] galacticCoordinates is a numpy.array; the first row is galactic longitude, 

261 the second row is galactic latitude in radians 

262 

263 @param [in] equatorialCoordinates is a numpy.array; the first row is RA, the second row is Dec in 

264 radians 

265 

266 @param [in] northMap the northern dust map 

267 

268 @param [in] southMap the southern dust map 

269 

270 @param [in] interp is a boolean determining whether or not to interpolate the EBV value 

271 

272 @param [out] ebv is a list of EBV values for all of the gLon, gLat pairs 

273 

274 """ 

275 

276 # raise an error if the coordinates are specified in both systems 

277 if galacticCoordinates is not None: 

278 if equatorialCoordinates is not None: 

279 raise RuntimeError("Specified both galacticCoordinates and " 

280 "equatorialCoordinates in calculateEbv") 

281 

282 # convert (ra,dec) into gLon, gLat 

283 if galacticCoordinates is None: 

284 

285 # raise an error if you did not specify ra or dec 

286 if equatorialCoordinates is None: 

287 raise RuntimeError("Must specify coordinates in calculateEbv") 

288 

289 galacticCoordinates = numpy.array(_galacticFromEquatorial(equatorialCoordinates[0, :], 

290 equatorialCoordinates[1, :])) 

291 

292 if northMap is None: 

293 if self.ebvMapNorth is None: 

294 self.load_ebvMapNorth() 

295 

296 northMap = self.ebvMapNorth 

297 

298 if southMap is None: 

299 if self.ebvMapSouth is None: 

300 self.load_ebvMapSouth() 

301 

302 southMap = self.ebvMapSouth 

303 

304 ebv = None 

305 

306 if galacticCoordinates.shape[1] > 0: 

307 

308 ebv = numpy.zeros(len(galacticCoordinates[0, :])) 

309 

310 # identify (by index) which points are in the galactic northern hemisphere 

311 # and which points are in the galactic southern hemisphere 

312 # taken from 

313 # http://stackoverflow.com/questions/4578590/python-equivalent-of-filter-getting-two-output-lists-i-e-partition-of-a-list 

314 inorth, isouth = reduce(lambda x, y: x[not y[1] > 0.0].append(y[0]) or x, 

315 enumerate(galacticCoordinates[1, :]), ([], [])) 

316 

317 nSet = galacticCoordinates[:, inorth] 

318 sSet = galacticCoordinates[:, isouth] 

319 

320 ebvNorth = northMap.generateEbv(nSet[0, :], nSet[1, :], interpolate=interp) 

321 ebvSouth = southMap.generateEbv(sSet[0, :], sSet[1, :], interpolate=interp) 

322 

323 for (i, ee) in zip(inorth, ebvNorth): 

324 ebv[i] = ee 

325 

326 for (i, ee) in zip(isouth, ebvSouth): 

327 ebv[i] = ee 

328 

329 return ebv 

330 

331 

332sims_clean_up.targets.append(EBVbase._ebv_map_cache)