Coverage for tests/test_verifyStats.py: 17%
167 statements
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-14 03:03 -0700
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-14 03:03 -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/>.
22import numpy as np
23import unittest
25import lsst.utils.tests
26import lsst.ip.isr.isrMock as isrMock
27import lsst.cp.verify as cpVerify
28import lsst.ip.isr.isrFunctions as isrFunctions
30from lsst.pipe.base import TaskMetadata
33def updateMockExp(exposure, addCR=True):
34 """Update an exposure with a mask and variance plane.
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
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)
54class ToySubClass(cpVerify.CpVerifyStatsTask):
55 """The CpVerifyStatsTask requires an implentation of verify.
56 """
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
63 return verifiedStats, successValue
66class VerifyStatsTestCase(lsst.utils.tests.TestCase):
67 """Unit test for stats code.
68 """
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 }
79 updateMockExp(self.inputExp)
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)
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)
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)
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)
121 results = task.run(self.inputExp, camera=self.camera, dimensions=self.dimensions)
122 resultStats = results.outputStats
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)
129 self.assertTrue(resultStats['VERIFY']['A REAL TEST'])
130 self.assertFalse(resultStats['VERIFY']['A BAD TEST'])
132 self.assertTrue(resultStats['SUCCESS'])
135class VerifyBiasTestCase(lsst.utils.tests.TestCase):
136 """Unit test for stats code - bias cases."""
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()
145 config.rngSeed = 54321
146 fakeBias = isrMock.BiasMock(config=config).run()
148 self.inputExp = biasExposure.clone()
149 mi = self.inputExp.getMaskedImage()
150 mi.scaledMinus(1.0, fakeBias.getMaskedImage())
151 updateMockExp(self.inputExp)
153 self.camera = isrMock.IsrMock().getCamera()
154 self.dimensions = {'instrument': self.camera.getName(),
155 'exposure': 1234,
156 'detector': self.camera[10].getName(),
157 }
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
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)
174 self.assertIn(biasStats['SUCCESS'], [True, False])
177class VerifyDarkTestCase(lsst.utils.tests.TestCase):
178 """Unit test for stats code - dark cases.
179 """
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()
188 config.rngSeed = 54321
189 fakeDark = isrMock.DarkMock(config=config).run()
191 self.inputExp = darkExposure.clone()
192 mi = self.inputExp.getMaskedImage()
193 mi.scaledMinus(1.0, fakeDark.getMaskedImage())
194 updateMockExp(self.inputExp)
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
204 self.camera = isrMock.IsrMock().getCamera()
205 self.dimensions = {'instrument': self.camera.getName(),
206 'exposure': 1234,
207 'detector': self.camera[10].getName(),
208 }
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
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)
227 self.assertIn(darkStats['SUCCESS'], [True, False])
230class VerifyDefectsTestCase(lsst.utils.tests.TestCase):
231 """Unit test for stats code - defect cases."""
233 defectFlux = 100000 # Flux to use for simulated defect.
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
250 config.skyLevel = 1000
251 config.rngSeed = 12345
252 self.inputExp = isrMock.IsrMock(config=config).run()
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
259 updateMockExp(self.inputExp, addCR=False)
261 self.inputExp.getMask().getArray()[0, 0] = 1
262 self.inputExp.getMask().getArray()[40, 50] = 1
263 self.inputExp.getMask().getArray()[75, 50] = 1
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 }
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.
283 # With defects applied
284 inputCatalogMock = np.arange(1, 100)
285 # Without defects applied
286 uncorrectedCatalogMock = np.arange(1, 200)
288 task = cpVerify.CpVerifyDefectsTask(config=config)
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
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)
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)
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)
315 self.assertIn(defectStats['SUCCESS'], [True, False])
318class MemoryTester(lsst.utils.tests.MemoryTestCase):
319 pass
322def setup_module(module):
323 lsst.utils.tests.init()
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()