Coverage for tests/test_verifyStats.py: 17%

167 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-05-04 03:56 -0700

1# This file is part of cp_verify. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

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

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

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

12# (at your option) any later version. 

13# 

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

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

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

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21 

22import numpy as np 

23import unittest 

24 

25import lsst.utils.tests 

26import lsst.ip.isr.isrMock as isrMock 

27import lsst.cp.verify as cpVerify 

28import lsst.ip.isr.isrFunctions as isrFunctions 

29 

30from lsst.pipe.base import TaskMetadata 

31 

32 

33def updateMockExp(exposure, addCR=True): 

34 """Update an exposure with a mask and variance plane. 

35 

36 Parameters 

37 ---------- 

38 exposure : `lsst.afw.image.Exposure` 

39 Exposure to be modified in place. 

40 addCR : `bool` 

41 Whether a known cosmic ray should be added to ``exposure``. 

42 """ 

43 if addCR: 

44 # Add a cosmic ray 

45 image = exposure.getImage() 

46 image.getArray()[50, 50] = 10000.0 

47 

48 # Set the mask and variance planes: 

49 mask = exposure.getMask() 

50 mask.getArray()[:, 10] = 1 

51 isrFunctions.updateVariance(exposure.getMaskedImage(), 1.0, 5.0) 

52 

53 

54class ToySubClass(cpVerify.CpVerifyStatsTask): 

55 """The CpVerifyStatsTask requires an implentation of verify. 

56 """ 

57 

58 def verify(self, inputExp, outputStats): 

59 # Docstring inherited from CpVerifyStatsTask.verify() 

60 verifiedStats = {'A REAL TEST': True, 'A BAD TEST': False} 

61 successValue = True 

62 

63 return verifiedStats, successValue 

64 

65 

66class VerifyStatsTestCase(lsst.utils.tests.TestCase): 

67 """Unit test for stats code. 

68 """ 

69 

70 def setUp(self): 

71 """Generate a mock exposure/camera to test.""" 

72 self.inputExp = isrMock.CalibratedRawMock().run() 

73 self.camera = isrMock.IsrMock().getCamera() 

74 self.dimensions = {'instrument': self.camera.getName(), 

75 'exposure': 1234, 

76 'detector': self.camera[10].getName(), 

77 } 

78 

79 updateMockExp(self.inputExp) 

80 

81 def test_failures(self): 

82 """Test that all the NotImplementedError methods fail correctly.""" 

83 results = None 

84 with self.assertRaises(NotImplementedError): 

85 # We have not implemented a verify method 

86 config = cpVerify.CpVerifyStatsConfig() 

87 config.numSigmaClip = 3.0 

88 task = cpVerify.CpVerifyStatsTask(config=config) 

89 results = task.run(self.inputExp, camera=self.camera, dimensions=self.dimensions) 

90 

91 # Or the catalog stats 

92 config.catalogStatKeywords = {'CAT_MEAN', 'MEDIAN'} 

93 task = cpVerify.CpVerifyStatsTask(config=config) 

94 results = task.run(self.inputExp, camera=self.camera, dimensions=self.dimensions) 

95 

96 # Or the detector stats 

97 config.catalogStatKeywords = {} 

98 config.detectorStatKeywords = {'DET_SIGMA', 'STDEV'} 

99 task = cpVerify.CpVerifyStatsTask(config=config) 

100 results = task.run(self.inputExp, camera=self.camera, dimensions=self.dimensions) 

101 self.assertIsNone(results) 

102 

103 def test_generic(self): 

104 """Test a subset of the output values to identify that the 

105 image stat methods haven't changed. 

106 """ 

107 config = cpVerify.CpVerifyStatsConfig() 

108 config.imageStatKeywords = {'MEAN': 'MEAN', 'MEDIAN': 'MEDIAN', 'CLIPPED': 'MEANCLIP', 

109 'SIGMA': 'STDEV'} 

110 config.unmaskedImageStatKeywords = {'un_MEAN': 'MEAN', 'un_MEDIAN': 'MEDIAN', 

111 'un_CLIPPED': 'MEANCLIP', 

112 'un_SIGMA': 'STDEV'} 

113 config.crImageStatKeywords = {'cr_MEAN': 'MEAN', 'cr_MEDIAN': 'MEDIAN', 'cr_CLIPPED': 'MEANCLIP', 

114 'cr_SIGMA': 'STDEV'} 

115 config.normImageStatKeywords = {'norm_MEAN': 'MEAN', 'norm_MEDIAN': 'MEDIAN', 

116 'norm_CLIPPED': 'MEANCLIP', 

117 'norm_SIGMA': 'STDEV'} 

118 config.numSigmaClip = 3.0 

119 task = ToySubClass(config=config) 

120 

121 results = task.run(self.inputExp, camera=self.camera, dimensions=self.dimensions) 

122 resultStats = results.outputStats 

123 

124 self.assertAlmostEqual(resultStats['AMP']['C:0,0']['MEAN'], 1506.06976, 4) 

125 self.assertAlmostEqual(resultStats['AMP']['C:0,0']['un_MEAN'], 1501.0299, 4) 

126 self.assertAlmostEqual(resultStats['AMP']['C:0,0']['norm_MEAN'], 301.213957, 4) 

127 self.assertAlmostEqual(resultStats['AMP']['C:0,0']['cr_MEAN'], 1504.2776, 4) 

128 

129 self.assertTrue(resultStats['VERIFY']['A REAL TEST']) 

130 self.assertFalse(resultStats['VERIFY']['A BAD TEST']) 

131 

132 self.assertTrue(resultStats['SUCCESS']) 

133 

134 

135class VerifyBiasTestCase(lsst.utils.tests.TestCase): 

136 """Unit test for stats code - bias cases.""" 

137 

138 def setUp(self): 

139 """Generate a mock exposure/camera to test.""" 

140 config = isrMock.IsrMockConfig() 

141 config.isTrimmed = True 

142 config.rngSeed = 12345 

143 biasExposure = isrMock.BiasMock(config=config).run() 

144 

145 config.rngSeed = 54321 

146 fakeBias = isrMock.BiasMock(config=config).run() 

147 

148 self.inputExp = biasExposure.clone() 

149 mi = self.inputExp.getMaskedImage() 

150 mi.scaledMinus(1.0, fakeBias.getMaskedImage()) 

151 updateMockExp(self.inputExp) 

152 

153 self.camera = isrMock.IsrMock().getCamera() 

154 self.dimensions = {'instrument': self.camera.getName(), 

155 'exposure': 1234, 

156 'detector': self.camera[10].getName(), 

157 } 

158 

159 def test_bias(self): 

160 """Test a subset of the output values to identify that the 

161 image stat methods haven't changed. 

162 """ 

163 config = cpVerify.CpVerifyBiasConfig() 

164 config.numSigmaClip = 3.0 

165 config.ampCornerBoxSize = 15 

166 task = cpVerify.CpVerifyBiasTask(config=config) 

167 results = task.run(self.inputExp, camera=self.camera, dimensions=self.dimensions) 

168 biasStats = results.outputStats 

169 

170 self.assertAlmostEqual(biasStats['AMP']['C:0,0']['MEAN'], 2.08672, 4) 

171 self.assertAlmostEqual(biasStats['AMP']['C:0,0']['NOISE'], 13.99547, 4) 

172 self.assertAlmostEqual(biasStats['AMP']['C:0,0']['CR_NOISE'], 14.11526, 4) 

173 

174 self.assertIn(biasStats['SUCCESS'], [True, False]) 

175 

176 

177class VerifyDarkTestCase(lsst.utils.tests.TestCase): 

178 """Unit test for stats code - dark cases. 

179 """ 

180 

181 def setUp(self): 

182 """Generate a mock exposure/camera to test.""" 

183 config = isrMock.IsrMockConfig() 

184 config.isTrimmed = True 

185 config.rngSeed = 12345 

186 darkExposure = isrMock.DarkMock(config=config).run() 

187 

188 config.rngSeed = 54321 

189 fakeDark = isrMock.DarkMock(config=config).run() 

190 

191 self.inputExp = darkExposure.clone() 

192 mi = self.inputExp.getMaskedImage() 

193 mi.scaledMinus(1.0, fakeDark.getMaskedImage()) 

194 updateMockExp(self.inputExp) 

195 

196 # Use this to test the metadataStats code, as this is the case 

197 # it's designed to fix. 

198 metadataContents = TaskMetadata() 

199 metadataContents["RESIDUAL STDEV C:0,0"] = 12.0 

200 metadataContents["RESIDUAL STDEV"] = 24.0 

201 self.metadata = TaskMetadata() 

202 self.metadata["subGroup"] = metadataContents 

203 

204 self.camera = isrMock.IsrMock().getCamera() 

205 self.dimensions = {'instrument': self.camera.getName(), 

206 'exposure': 1234, 

207 'detector': self.camera[10].getName(), 

208 } 

209 

210 def test_dark(self): 

211 """Test a subset of the output values to identify that the 

212 image stat methods haven't changed. 

213 """ 

214 config = cpVerify.CpVerifyDarkConfig() 

215 config.numSigmaClip = 3.0 

216 task = cpVerify.CpVerifyDarkTask(config=config) 

217 results = task.run(self.inputExp, 

218 camera=self.camera, 

219 taskMetadata=self.metadata, 

220 dimensions=self.dimensions) 

221 darkStats = results.outputStats 

222 

223 self.assertAlmostEqual(darkStats['AMP']['C:0,0']['MEAN'], 2.0043, 4) 

224 self.assertAlmostEqual(darkStats['AMP']['C:0,0']['NOISE'], 3.12948, 4) 

225 self.assertAlmostEqual(darkStats['AMP']['C:0,0']['CR_NOISE'], 3.15946, 4) 

226 

227 self.assertIn(darkStats['SUCCESS'], [True, False]) 

228 

229 

230class VerifyDefectsTestCase(lsst.utils.tests.TestCase): 

231 """Unit test for stats code - defect cases.""" 

232 

233 defectFlux = 100000 # Flux to use for simulated defect. 

234 

235 def setUp(self): 

236 """Generate a mock exposure/camera to test.""" 

237 config = isrMock.IsrMockConfig() 

238 config.isTrimmed = True 

239 config.doGenerateImage = True 

240 config.doAddFringe = False 

241 config.doAddSource = False 

242 config.doAddSky = True 

243 config.doAddOverscan = False 

244 config.doAddCrosstalk = False 

245 config.doAddBias = False 

246 config.doAddDark = False 

247 config.doAddFlat = False 

248 config.doAddFringe = False 

249 

250 config.skyLevel = 1000 

251 config.rngSeed = 12345 

252 self.inputExp = isrMock.IsrMock(config=config).run() 

253 

254 # These are simulated defects 

255 self.inputExp.getImage().getArray()[0, 0] = -1.0 * self.defectFlux 

256 self.inputExp.getImage().getArray()[40, 50] = self.defectFlux 

257 self.inputExp.getImage().getArray()[75, 50] = np.nan 

258 

259 updateMockExp(self.inputExp, addCR=False) 

260 

261 self.inputExp.getMask().getArray()[0, 0] = 1 

262 self.inputExp.getMask().getArray()[40, 50] = 1 

263 self.inputExp.getMask().getArray()[75, 50] = 1 

264 

265 self.camera = isrMock.IsrMock().getCamera() 

266 self.dimensions = {'instrument': self.camera.getName(), 

267 'exposure': 1234, 

268 'visit': 1234, 

269 'detector': self.camera[10].getName(), 

270 } 

271 

272 def test_defects(self): 

273 """Test a subset of the output values to identify that the 

274 image stat methods haven't changed. 

275 """ 

276 config = cpVerify.CpVerifyDefectsConfig() 

277 config.numSigmaClip = 3.0 

278 # The catalog objects are `lsst.afw.table.SourceCatalog` 

279 # but the task catalog tests only check number of 

280 # detections before and after applying defects, so 

281 # arrays will do in this case. 

282 

283 # With defects applied 

284 inputCatalogMock = np.arange(1, 100) 

285 # Without defects applied 

286 uncorrectedCatalogMock = np.arange(1, 200) 

287 

288 task = cpVerify.CpVerifyDefectsTask(config=config) 

289 

290 # Also use the inputExp as uncorrectedExposure. 

291 results = task.run(self.inputExp, 

292 camera=self.camera, 

293 uncorrectedExp=self.inputExp, 

294 inputCatalog=inputCatalogMock, 

295 uncorrectedCatalog=uncorrectedCatalogMock, 

296 dimensions=self.dimensions) 

297 defectStats = results.outputStats 

298 

299 self.assertEqual(defectStats['AMP']['C:0,0']['DEFECT_PIXELS'], 53) 

300 self.assertEqual(defectStats['AMP']['C:0,0']['OUTLIERS'], 17) 

301 self.assertEqual(defectStats['AMP']['C:0,0']['STAT_OUTLIERS'], 3) 

302 self.assertAlmostEqual(defectStats['AMP']['C:0,0']['MEDIAN'], 999.466, 4) 

303 self.assertAlmostEqual(defectStats['AMP']['C:0,0']['STDEV'], 30.96303, 4) 

304 self.assertAlmostEqual(defectStats['AMP']['C:0,0']['MIN'], 881.56146, 4) 

305 self.assertAlmostEqual(defectStats['AMP']['C:0,0']['MAX'], 1124.19934, 4) 

306 

307 self.assertEqual(defectStats['AMP']['C:0,0']['UNMASKED_MIN'], -1.0 * self.defectFlux, 4) 

308 self.assertEqual(defectStats['AMP']['C:0,0']['UNMASKED_MAX'], self.defectFlux, 4) 

309 

310 self.assertEqual(defectStats['CATALOG']['NUM_OBJECTS_BEFORE'], 199) 

311 self.assertEqual(defectStats['CATALOG']['NUM_OBJECTS_AFTER'], 99) 

312 self.assertEqual(defectStats['DET']['NUM_COSMICS_BEFORE'], 0) 

313 self.assertEqual(defectStats['DET']['NUM_COSMICS_AFTER'], 0) 

314 

315 self.assertIn(defectStats['SUCCESS'], [True, False]) 

316 

317 

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

319 pass 

320 

321 

322def setup_module(module): 

323 lsst.utils.tests.init() 

324 

325 

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

327 lsst.utils.tests.init() 

328 unittest.main()