Coverage for tests/test_fringes.py : 18%

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# This file is part of ip_isr.
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 unittest
24import numpy as np
26import lsst.utils.tests
27import lsst.afw.math as afwMath
28import lsst.afw.image as afwImage
29import lsst.afw.image.utils as afwImageUtils
30import lsst.pipe.base as pipeBase
31from lsst.ip.isr.fringe import FringeTask
33import lsst.ip.isr.isrMock as isrMock
35try:
36 display
37except NameError:
38 display = False
39else:
40 import lsst.afw.display as afwDisplay
41 afwDisplay.setDefaultMaskTransparency(75)
44class FringeDataRef(object):
45 """Quacks like a ButlerDataRef, so we can provide an in-memory fringe frame.
46 """
47 def __init__(self, fringe):
48 self.fringe = fringe
49 self.dataId = {'test': True}
51 def get(self, name="fringe", immediate=False):
52 if name == "fringe":
53 return self.fringe
54 if name == "ccdExposureId":
55 return 1000
58def createFringe(width, height, xFreq, xOffset, yFreq, yOffset):
59 """Create a fringe frame.
61 Parameters
62 ----------
63 width, height : `int`
64 Size of image.
65 xFreq, yFreq : `float`
66 Frequency of sinusoids in x and y.
67 xOffset, yOffset : `float`
68 Phase of sinusoids in x and y.
70 Returns
71 -------
72 exp : `lsst.afw.image.ExposureF`
73 Fringe frame.
74 """
75 image = afwImage.ImageF(width, height)
76 array = image.getArray()
77 x, y = np.indices(array.shape)
78 array[x, y] = np.sin(xFreq*x + xOffset) + np.sin(yFreq*y + yOffset)
79 mi = afwImage.makeMaskedImage(image)
80 exp = afwImage.makeExposure(mi)
81 exp.setFilter(afwImage.Filter('FILTER'))
82 return exp
85class FringeTestCase(lsst.utils.tests.TestCase):
86 """Tests of the FringeTask.
87 """
88 def setUp(self):
89 self.size = 512
90 self.config = FringeTask.ConfigClass()
91 self.config.filters = ['FILTER']
92 self.config.num = 5000
93 self.config.small = 1
94 self.config.large = 128
95 self.config.pedestal = False
96 self.config.iterations = 10
97 afwImageUtils.defineFilter('FILTER', lambdaEff=0)
99 def tearDown(self):
100 afwImageUtils.resetFilters()
102 def checkFringe(self, task, exp, fringes, stddevMax):
103 """Run fringe subtraction and verify.
105 Parameters
106 ----------
107 task : `lsst.ip.isr.fringe.FringeTask`
108 Task to run.
109 exp : `lsst.afw.image.ExposureF`
110 Science exposure.
111 fringes : `list` of `lsst.afw.image.ExposureF`
112 Data reference that will provide the fringes.
113 stddevMax : `float`
114 Maximum allowable standard deviation.
115 """
116 if display:
117 frame = 0
118 afwDisplay.Display(frame=frame).mtv(exp, title=self._testMethodName + ": Science exposure")
119 frame += 1
120 if not isinstance(fringes, list):
121 fringe = [fringes]
122 else:
123 fringe = fringes
124 for i, f in enumerate(fringe):
125 afwDisplay.Display(frame=frame).mtv(f, title=self._testMethodName +
126 ": Fringe frame %d" % (i + 1))
127 frame += 1
129 task.run(exp, fringes)
131 mi = exp.getMaskedImage()
133 if display:
134 afwDisplay.Display(frame=frame).mtv(exp, title=self._testMethodName + ": Subtracted")
135 frame += 1
137 mi -= afwMath.makeStatistics(mi, afwMath.MEAN).getValue()
138 self.assertLess(afwMath.makeStatistics(mi, afwMath.STDEV).getValue(), stddevMax)
140 def testSingle(self, pedestal=0.0, stddevMax=1.0e-4):
141 """Test subtraction of a single fringe frame.
143 Parameters
144 ----------
145 pedestal : `float`, optional
146 Pedestal to add into fringe frame
147 stddevMax : `float`, optional
148 Maximum allowable standard deviation.
149 """
150 xFreq = np.pi/10.0
151 xOffset = 1.0
152 yFreq = np.pi/15.0
153 yOffset = 0.5
154 scale = 1.0
155 fringe = createFringe(self.size, self.size, xFreq, xOffset, yFreq, yOffset)
156 fMi = fringe.getMaskedImage()
157 fMi += pedestal
158 exp = createFringe(self.size, self.size, xFreq, xOffset, yFreq, yOffset)
159 eMi = exp.getMaskedImage()
160 eMi *= scale
162 task = FringeTask(name="fringe", config=self.config)
163 self.checkFringe(task, exp, fringe, stddevMax)
165 def testBad(self, bad="BAD"):
166 """Test fringe subtraction with bad inputs.
168 Parameters
169 ----------
170 bad : `str`, optional
171 Mask plane to use.
172 """
173 xFreq = np.pi/10.0
174 xOffset = 1.0
175 yFreq = np.pi/15.0
176 yOffset = 0.5
177 fringe = createFringe(self.size, self.size, xFreq, xOffset, yFreq, yOffset)
178 exp = createFringe(self.size, self.size, xFreq, xOffset, yFreq, yOffset)
180 # This is a bad CCD: entirely masked
181 exp.maskedImage.image.set(0.0)
182 mask = exp.maskedImage.mask
183 mask.set(mask.getPlaneBitMask(bad))
185 self.config.stats.badMaskPlanes = [bad]
186 task = FringeTask(name="fringe", config=self.config)
187 task.run(exp, fringe)
188 self.assertFloatsEqual(exp.maskedImage.image.array, 0.0)
190 def testPedestal(self):
191 """Test subtraction of a fringe frame with a pedestal.
192 """
193 self.config.pedestal = True
194 self.testSingle(pedestal=10000.0, stddevMax=1.0e-3) # Not sure why this produces worse sttdev
195 self.testMultiple(pedestal=10000.0)
197 def testMultiple(self, pedestal=0.0):
198 """Test subtraction of multiple fringe frames
200 Paramters
201 ---------
202 pedestal : `float`, optional
203 Pedestal to add into fringe frame.
204 """
205 xFreqList = [0.1, 0.13, 0.06]
206 xOffsetList = [0.0, 0.1, 0.2]
207 yFreqList = [0.09, 0.12, 0.07]
208 yOffsetList = [0.3, 0.2, 0.1]
209 fringeList = [createFringe(self.size, self.size, xFreq, xOffset, yFreq, yOffset)
210 for xFreq, xOffset, yFreq, yOffset in
211 zip(xFreqList, xOffsetList, yFreqList, yOffsetList)]
213 for fringe in fringeList:
214 fMi = fringe.getMaskedImage()
215 fMi += pedestal
216 # Generate science frame
217 scales = [0.33, 0.33, 0.33]
218 image = afwImage.ImageF(self.size, self.size)
219 image.set(0)
220 for s, f in zip(scales, fringeList):
221 image.scaledPlus(s, f.getMaskedImage().getImage())
222 mi = afwImage.makeMaskedImage(image)
223 exp = afwImage.makeExposure(mi)
224 exp.setFilter(afwImage.Filter('FILTER'))
226 task = FringeTask(name="multiFringe", config=self.config)
227 self.checkFringe(task, exp, fringeList, stddevMax=1.0e-2)
229 def testRunDataRef(self, pedestal=0.0, stddevMax=1.0e-4):
230 """Test the .runDataRef method for complete test converage.
232 Paramters
233 ---------
234 pedestal : `float`, optional
235 Pedestal to add into fringe frame.
236 stddevMax : `float`, optional
237 Maximum allowable standard deviation.
238 """
239 xFreq = np.pi/10.0
240 xOffset = 1.0
241 yFreq = np.pi/15.0
242 yOffset = 0.5
243 scale = 1.0
244 fringe = createFringe(self.size, self.size, xFreq, xOffset, yFreq, yOffset)
245 fMi = fringe.getMaskedImage()
246 fMi += pedestal
247 exp = createFringe(self.size, self.size, xFreq, xOffset, yFreq, yOffset)
248 eMi = exp.getMaskedImage()
249 eMi *= scale
251 task = FringeTask(name="fringe", config=self.config)
252 dataRef = FringeDataRef(fringe)
253 task.runDataRef(exp, dataRef)
255 mi = exp.getMaskedImage()
256 mi -= afwMath.makeStatistics(mi, afwMath.MEAN).getValue()
257 self.assertLess(afwMath.makeStatistics(mi, afwMath.STDEV).getValue(), stddevMax)
259 def test_readFringes(self):
260 """Test that fringes can be successfully accessed from the butler.
261 """
262 task = FringeTask()
263 dataRef = isrMock.DataRefMock()
265 result = task.readFringes(dataRef, assembler=None)
266 self.assertIsInstance(result, pipeBase.Struct)
268 def test_multiFringes(self):
269 """Test that multi-fringe results are handled correctly by the task.
270 """
271 self.config.filters.append("_unknown_")
272 self.config.large = 16
273 task = FringeTask(name="multiFringeMock", config=self.config)
275 config = isrMock.IsrMockConfig()
276 config.fringeScale = [750.0, 240.0, 220.0]
277 config.fringeX0 = [100.0, 150.0, 200.0]
278 config.fringeY0 = [0.0, 200.0, 0.0]
279 dataRef = isrMock.FringeDataRefMock(config=config)
281 exp = dataRef.get("raw")
282 medianBefore = np.nanmedian(exp.getImage().getArray())
283 fringes = task.readFringes(dataRef, assembler=None)
285 solution, rms = task.run(exp, **fringes.getDict())
286 medianAfter = np.nanmedian(exp.getImage().getArray())
287 stdAfter = np.nanstd(exp.getImage().getArray())
289 self.assertLess(medianAfter, medianBefore)
290 self.assertFloatsAlmostEqual(medianAfter, 3000.925, atol=1e-4)
291 self.assertFloatsAlmostEqual(stdAfter, 3549.9885, atol=1e-4)
293 deviation = np.abs(solution - config.fringeScale)
294 self.assertTrue(np.all(deviation / rms < 1.0))
297class MemoryTester(lsst.utils.tests.MemoryTestCase):
298 pass
301def setup_module(module):
302 lsst.utils.tests.init()
305if __name__ == "__main__": 305 ↛ 306line 305 didn't jump to line 306, because the condition on line 305 was never true
306 lsst.utils.tests.init()
307 unittest.main()