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# 

2# LSST Data Management System 

3# Copyright 2008, 2009, 2010 LSST Corporation. 

4# 

5# This product includes software developed by the 

6# LSST Project (http://www.lsst.org/). 

7# 

8# This program is free software: you can redistribute it and/or modify 

9# it under the terms of the GNU General Public License as published by 

10# the Free Software Foundation, either version 3 of the License, or 

11# (at your option) any later version. 

12# 

13# This program is distributed in the hope that it will be useful, 

14# but WITHOUT ANY WARRANTY; without even the implied warranty of 

15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the LSST License Statement and 

19# the GNU General Public License along with this program. If not, 

20# see <http://www.lsstcorp.org/LegalNotices/>. 

21# 

22import os 

23import unittest 

24 

25import numpy as np 

26import astropy.units as u 

27 

28from lsst.daf.persistence import Butler 

29from lsst.meas.algorithms import LoadIndexedReferenceObjectsTask 

30import lsst.geom as geom 

31import lsst.afw.table as afwTable 

32import lsst.afw.image as afwImage 

33import lsst.utils.tests 

34from lsst.utils import getPackageDir 

35from lsst.log import Log 

36from lsst.pipe.tasks.photoCal import PhotoCalTask, PhotoCalConfig 

37from lsst.pipe.tasks.colorterms import Colorterm, ColortermDict, ColortermLibrary 

38 

39RefCatDir = os.path.join(getPackageDir("pipe_tasks"), "tests", "data", "sdssrefcat") 

40 

41# Quiet down meas_astrom logging, so we can see PhotoCal logs better 

42Log.getLogger("LoadIndexedReferenceObjectsTask").setLevel(Log.WARN) 

43 

44testColorterms = ColortermLibrary(data={ 

45 "test*": ColortermDict(data={ 

46 "g": Colorterm(primary="g", secondary="r", c0=0.00, c1=0.00), 

47 "r": Colorterm(primary="r", secondary="i", c0=0.00, c1=0.00, c2=0.00), 

48 "i": Colorterm(primary="i", secondary="z", c0=1.00, c1=0.00, c2=0.00), 

49 "z": Colorterm(primary="z", secondary="i", c0=0.00, c1=0.00, c2=0.00), 

50 }) 

51}) 

52 

53 

54def setup_module(module): 

55 lsst.utils.tests.init() 

56 

57 

58class PhotoCalTest(unittest.TestCase): 

59 

60 def setUp(self): 

61 

62 # Load sample input from disk 

63 testDir = os.path.dirname(__file__) 

64 self.srcCat = afwTable.SourceCatalog.readFits( 

65 os.path.join(testDir, "data", "v695833-e0-c000.xy.fits")) 

66 

67 self.srcCat["slot_ApFlux_instFluxErr"] = 1 

68 self.srcCat["slot_PsfFlux_instFluxErr"] = 1 

69 

70 # The .xy.fits file has sources in the range ~ [0,2000],[0,4500] 

71 # which is bigger than the exposure 

72 self.bbox = geom.Box2I(geom.Point2I(0, 0), geom.Extent2I(2048, 4612)) 

73 smallExposure = afwImage.ExposureF(os.path.join(testDir, "data", "v695833-e0-c000-a00.sci.fits")) 

74 self.exposure = afwImage.ExposureF(self.bbox) 

75 self.exposure.setWcs(smallExposure.getWcs()) 

76 self.exposure.setFilter(smallExposure.getFilter()) 

77 self.exposure.setPhotoCalib(smallExposure.getPhotoCalib()) 

78 

79 coordKey = self.srcCat.getCoordKey() 

80 centroidKey = self.srcCat.getCentroidSlot().getMeasKey() 

81 wcs = self.exposure.getWcs() 

82 for src in self.srcCat: 

83 src.set(coordKey, wcs.pixelToSky(src.get(centroidKey))) 

84 

85 # Make a reference loader 

86 butler = Butler(RefCatDir) 

87 self.refObjLoader = LoadIndexedReferenceObjectsTask(butler=butler) 

88 logLevel = Log.TRACE 

89 self.log = Log.getLogger('testPhotoCal') 

90 self.log.setLevel(logLevel) 

91 

92 self.config = PhotoCalConfig() 

93 self.config.match.matchRadius = 0.5 

94 self.config.match.referenceSelection.doMagLimit = True 

95 self.config.match.referenceSelection.magLimit.maximum = 22.0 

96 self.config.match.referenceSelection.magLimit.fluxField = "i_flux" 

97 self.config.match.referenceSelection.doFlags = True 

98 self.config.match.referenceSelection.flags.good = ['photometric'] 

99 self.config.match.referenceSelection.flags.bad = ['resolved'] 

100 self.config.match.sourceSelection.doUnresolved = False # Don't have star/galaxy in the srcCat 

101 

102 # The test and associated data have been prepared on the basis that we 

103 # use the PsfFlux to perform photometry. 

104 self.config.fluxField = "base_PsfFlux_instFlux" 

105 

106 def tearDown(self): 

107 del self.srcCat 

108 del self.exposure 

109 del self.refObjLoader 

110 del self.log 

111 

112 def _runTask(self): 

113 """All the common setup to actually test the results""" 

114 task = PhotoCalTask(self.refObjLoader, config=self.config, schema=self.srcCat.schema) 

115 pCal = task.run(exposure=self.exposure, sourceCat=self.srcCat) 

116 matches = pCal.matches 

117 refFluxField = pCal.arrays.refFluxFieldList[0] 

118 

119 # These are *all* the matches; we don't really expect to do that well. 

120 diff = [] 

121 for m in matches: 

122 refFlux = m[0].get(refFluxField) # reference catalog flux 

123 if refFlux <= 0: 123 ↛ 124line 123 didn't jump to line 124, because the condition on line 123 was never true

124 continue 

125 refMag = u.Quantity(refFlux, u.nJy).to_value(u.ABmag) 

126 instFlux = m[1].getPsfInstFlux() # Instrumental Flux 

127 if instFlux <= 0: 127 ↛ 128line 127 didn't jump to line 128, because the condition on line 127 was never true

128 continue 

129 instMag = pCal.photoCalib.instFluxToMagnitude(instFlux) # Instrumental mag 

130 diff.append(instMag - refMag) 

131 self.diff = np.array(diff) 

132 # Differences of matched objects that were used in the fit. 

133 self.zp = pCal.photoCalib.instFluxToMagnitude(1.) 

134 self.fitdiff = pCal.arrays.srcMag + self.zp - pCal.arrays.refMag 

135 

136 def testFlags(self): 

137 """test that all the calib_photometry flags are set to reasonable values""" 

138 schema = self.srcCat.schema 

139 task = PhotoCalTask(self.refObjLoader, config=self.config, schema=schema) 

140 mapper = afwTable.SchemaMapper(self.srcCat.schema, schema) 

141 cat = afwTable.SourceCatalog(schema) 

142 for name in self.srcCat.schema.getNames(): 

143 mapper.addMapping(self.srcCat.schema.find(name).key) 

144 cat.extend(self.srcCat, mapper=mapper) 

145 

146 # test that by default, no stars are reserved and all used are candidates 

147 task.run(exposure=self.exposure, sourceCat=cat) 

148 used = 0 

149 for source in cat: 

150 if source.get("calib_photometry_used"): 

151 used += 1 

152 self.assertFalse(source.get("calib_photometry_reserved")) 

153 # test that some are actually used 

154 self.assertGreater(used, 0) 

155 

156 def testZeroPoint(self): 

157 """ Test to see if we can compute a photometric zeropoint given a reference task""" 

158 self._runTask() 

159 self.assertGreater(len(self.diff), 50) 

160 self.log.info('%i magnitude differences; mean difference %g; mean abs diff %g' % 

161 (len(self.diff), np.mean(self.diff), np.mean(np.abs(self.diff)))) 

162 self.assertLess(np.mean(self.diff), 0.6) 

163 

164 # Differences of matched objects that were used in the fit. 

165 self.log.debug('zeropoint: %g', self.zp) 

166 self.log.debug('number of sources used in fit: %i', len(self.fitdiff)) 

167 self.log.debug('rms diff: %g', np.mean(self.fitdiff**2)**0.5) 

168 self.log.debug('median abs(diff): %g', np.median(np.abs(self.fitdiff))) 

169 

170 # zeropoint: 31.3145 

171 # number of sources used in fit: 65 

172 # median diff: -0.009681 

173 # mean diff: 0.00331871 

174 # median abs(diff): 0.0368904 

175 # mean abs(diff): 0.0516589 

176 

177 self.assertLess(abs(self.zp - 31.3145), 0.05) 

178 self.assertGreater(len(self.fitdiff), 50) 

179 # Tolerances are somewhat arbitrary; they're set simply to avoid regressions, and 

180 # are not based on we'd expect to get given the data quality. 

181 lq, uq = np.percentile(self.fitdiff, (25, 75)) 

182 rms = 0.741*(uq - lq) # Convert IQR to stdev assuming a Gaussian 

183 self.assertLess(rms, 0.07) # rms difference 

184 self.assertLess(np.median(np.abs(self.fitdiff)), 0.06) # median absolution difference 

185 

186 def testColorTerms(self): 

187 """ Test to see if we can apply colorterm corrections while computing photometric zeropoints""" 

188 # Turn colorterms on. The colorterm library used here is simple - we just apply a 1 mag 

189 # color-independentcolorterm correction to everything. This should change the photometric zeropoint. 

190 # by 1 mag. 

191 self.config.applyColorTerms = True 

192 self.config.colorterms = testColorterms 

193 self.config.photoCatName = "testglob" # Check glo expansion 

194 # zerPointOffset is the offset in the zeropoint that we expect from a uniform (i.e. color-independent) 

195 # colorterm correction. 

196 zeroPointOffset = testColorterms.data['test*'].data['i'].c0 

197 self._runTask() 

198 

199 self.assertLess(np.mean(self.diff), 0.6 + zeroPointOffset) 

200 self.log.debug('zeropoint: %g', self.zp) 

201 # zeropoint: 32.3145 

202 self.assertLess(abs(self.zp - (31.3145 + zeroPointOffset)), 0.05) 

203 

204 

205class MemoryTester(lsst.utils.tests.MemoryTestCase): 

206 pass 

207 

208 

209if __name__ == "__main__": 209 ↛ 210line 209 didn't jump to line 210, because the condition on line 209 was never true

210 lsst.utils.tests.init() 

211 unittest.main()