Coverage for tests/test_verifyStats.py: 17%

163 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-11 03:43 -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 

75 updateMockExp(self.inputExp) 

76 

77 def test_failures(self): 

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

79 results = None 

80 with self.assertRaises(NotImplementedError): 

81 # We have not implemented a verify method 

82 config = cpVerify.CpVerifyStatsConfig() 

83 config.numSigmaClip = 3.0 

84 task = cpVerify.CpVerifyStatsTask(config=config) 

85 results = task.run(self.inputExp, self.camera) 

86 

87 # Or the catalog stats 

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

89 task = cpVerify.CpVerifyStatsTask(config=config) 

90 results = task.run(self.inputExp, self.camera) 

91 

92 # Or the detector stats 

93 config.catalogStatKeywords = {} 

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

95 task = cpVerify.CpVerifyStatsTask(config=config) 

96 results = task.run(self.inputExp, self.camera) 

97 self.assertIsNone(results) 

98 

99 def test_generic(self): 

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

101 image stat methods haven't changed. 

102 """ 

103 config = cpVerify.CpVerifyStatsConfig() 

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

105 'SIGMA': 'STDEV'} 

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

107 'un_CLIPPED': 'MEANCLIP', 

108 'un_SIGMA': 'STDEV'} 

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

110 'cr_SIGMA': 'STDEV'} 

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

112 'norm_CLIPPED': 'MEANCLIP', 

113 'norm_SIGMA': 'STDEV'} 

114 config.numSigmaClip = 3.0 

115 task = ToySubClass(config=config) 

116 

117 results = task.run(self.inputExp, self.camera) 

118 resultStats = results.outputStats 

119 

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

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

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

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

124 

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

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

127 

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

129 

130 

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

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

133 

134 def setUp(self): 

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

136 config = isrMock.IsrMockConfig() 

137 config.isTrimmed = True 

138 config.rngSeed = 12345 

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

140 

141 config.rngSeed = 54321 

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

143 

144 self.inputExp = biasExposure.clone() 

145 mi = self.inputExp.getMaskedImage() 

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

147 updateMockExp(self.inputExp) 

148 

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

150 

151 def test_bias(self): 

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

153 image stat methods haven't changed. 

154 """ 

155 config = cpVerify.CpVerifyBiasConfig() 

156 config.numSigmaClip = 3.0 

157 config.ampCornerBoxSize = 15 

158 task = cpVerify.CpVerifyBiasTask(config=config) 

159 results = task.run(self.inputExp, self.camera) 

160 biasStats = results.outputStats 

161 

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

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

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

165 

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

167 

168 

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

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

171 """ 

172 

173 def setUp(self): 

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

175 config = isrMock.IsrMockConfig() 

176 config.isTrimmed = True 

177 config.rngSeed = 12345 

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

179 

180 config.rngSeed = 54321 

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

182 

183 self.inputExp = darkExposure.clone() 

184 mi = self.inputExp.getMaskedImage() 

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

186 updateMockExp(self.inputExp) 

187 

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

189 # it's designed to fix. 

190 metadataContents = TaskMetadata() 

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

192 metadataContents["RESIDUAL STDEV"] = 24.0 

193 self.metadata = TaskMetadata() 

194 self.metadata["subGroup"] = metadataContents 

195 

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

197 

198 def test_dark(self): 

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

200 image stat methods haven't changed. 

201 """ 

202 config = cpVerify.CpVerifyDarkConfig() 

203 config.numSigmaClip = 3.0 

204 task = cpVerify.CpVerifyDarkTask(config=config) 

205 results = task.run(self.inputExp, self.camera, taskMetadata=self.metadata) 

206 darkStats = results.outputStats 

207 

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

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

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

211 

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

213 

214 

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

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

217 

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

219 

220 def setUp(self): 

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

222 config = isrMock.IsrMockConfig() 

223 config.isTrimmed = True 

224 config.doGenerateImage = True 

225 config.doAddFringe = False 

226 config.doAddSource = False 

227 config.doAddSky = True 

228 config.doAddOverscan = False 

229 config.doAddCrosstalk = False 

230 config.doAddBias = False 

231 config.doAddDark = False 

232 config.doAddFlat = False 

233 config.doAddFringe = False 

234 

235 config.skyLevel = 1000 

236 config.rngSeed = 12345 

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

238 

239 # These are simulated defects 

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

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

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

243 

244 updateMockExp(self.inputExp, addCR=False) 

245 

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

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

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

249 

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

251 

252 def test_defects(self): 

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

254 image stat methods haven't changed. 

255 """ 

256 config = cpVerify.CpVerifyDefectsConfig() 

257 config.numSigmaClip = 3.0 

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

259 # but the task catalog tests only check number of 

260 # detections before and after applying defects, so 

261 # arrays will do in this case. 

262 

263 # With defects applied 

264 inputCatalogMock = np.arange(1, 100) 

265 # Without defects applied 

266 uncorrectedCatalogMock = np.arange(1, 200) 

267 

268 task = cpVerify.CpVerifyDefectsTask(config=config) 

269 

270 # Also use the inputExp as uncorrectedExposure. 

271 results = task.run(self.inputExp, self.camera, 

272 uncorrectedExp=self.inputExp, inputCatalog=inputCatalogMock, 

273 uncorrectedCatalog=uncorrectedCatalogMock) 

274 defectStats = results.outputStats 

275 

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

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

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

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

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

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

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

283 

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

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

286 

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

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

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

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

291 

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

293 

294 

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

296 pass 

297 

298 

299def setup_module(module): 

300 lsst.utils.tests.init() 

301 

302 

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

304 lsst.utils.tests.init() 

305 unittest.main()