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# 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/>. 

21 

22import unittest 

23 

24import numpy as np 

25 

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 

32 

33import lsst.ip.isr.isrMock as isrMock 

34 

35try: 

36 display 

37except NameError: 

38 display = False 

39else: 

40 import lsst.afw.display as afwDisplay 

41 afwDisplay.setDefaultMaskTransparency(75) 

42 

43 

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} 

50 

51 def get(self, name="fringe", immediate=False): 

52 if name == "fringe": 

53 return self.fringe 

54 if name == "ccdExposureId": 

55 return 1000 

56 

57 

58def createFringe(width, height, xFreq, xOffset, yFreq, yOffset): 

59 """Create a fringe frame. 

60 

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. 

69 

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 

83 

84 

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) 

98 

99 def tearDown(self): 

100 afwImageUtils.resetFilters() 

101 

102 def checkFringe(self, task, exp, fringes, stddevMax): 

103 """Run fringe subtraction and verify. 

104 

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 

128 

129 task.run(exp, fringes) 

130 

131 mi = exp.getMaskedImage() 

132 

133 if display: 

134 afwDisplay.Display(frame=frame).mtv(exp, title=self._testMethodName + ": Subtracted") 

135 frame += 1 

136 

137 mi -= afwMath.makeStatistics(mi, afwMath.MEAN).getValue() 

138 self.assertLess(afwMath.makeStatistics(mi, afwMath.STDEV).getValue(), stddevMax) 

139 

140 def testSingle(self, pedestal=0.0, stddevMax=1.0e-4): 

141 """Test subtraction of a single fringe frame. 

142 

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 

161 

162 task = FringeTask(name="fringe", config=self.config) 

163 self.checkFringe(task, exp, fringe, stddevMax) 

164 

165 def testBad(self, bad="BAD"): 

166 """Test fringe subtraction with bad inputs. 

167 

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) 

179 

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)) 

184 

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) 

189 

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) 

196 

197 def testMultiple(self, pedestal=0.0): 

198 """Test subtraction of multiple fringe frames 

199 

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)] 

212 

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')) 

225 

226 task = FringeTask(name="multiFringe", config=self.config) 

227 self.checkFringe(task, exp, fringeList, stddevMax=1.0e-2) 

228 

229 def testRunDataRef(self, pedestal=0.0, stddevMax=1.0e-4): 

230 """Test the .runDataRef method for complete test converage. 

231 

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 

250 

251 task = FringeTask(name="fringe", config=self.config) 

252 dataRef = FringeDataRef(fringe) 

253 task.runDataRef(exp, dataRef) 

254 

255 mi = exp.getMaskedImage() 

256 mi -= afwMath.makeStatistics(mi, afwMath.MEAN).getValue() 

257 self.assertLess(afwMath.makeStatistics(mi, afwMath.STDEV).getValue(), stddevMax) 

258 

259 def test_readFringes(self): 

260 """Test that fringes can be successfully accessed from the butler. 

261 """ 

262 task = FringeTask() 

263 dataRef = isrMock.DataRefMock() 

264 

265 result = task.readFringes(dataRef, assembler=None) 

266 self.assertIsInstance(result, pipeBase.Struct) 

267 

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) 

274 

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) 

280 

281 exp = dataRef.get("raw") 

282 medianBefore = np.nanmedian(exp.getImage().getArray()) 

283 fringes = task.readFringes(dataRef, assembler=None) 

284 

285 solution, rms = task.run(exp, **fringes.getDict()) 

286 medianAfter = np.nanmedian(exp.getImage().getArray()) 

287 stdAfter = np.nanstd(exp.getImage().getArray()) 

288 

289 self.assertLess(medianAfter, medianBefore) 

290 self.assertFloatsAlmostEqual(medianAfter, 3000.925, atol=1e-4) 

291 self.assertFloatsAlmostEqual(stdAfter, 3549.9885, atol=1e-4) 

292 

293 deviation = np.abs(solution - config.fringeScale) 

294 self.assertTrue(np.all(deviation / rms < 1.0)) 

295 

296 

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

298 pass 

299 

300 

301def setup_module(module): 

302 lsst.utils.tests.init() 

303 

304 

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()