Coverage for tests / test_empiricalVariance.py: 17%

103 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-05-07 08:28 +0000

1# 

2# LSST Data Management System 

3# Copyright 2018 LSST Corporation. 

4# 

5# This product includes software developed by the 

6# LSST Project (http://www.lsst.org/). 

7# 

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

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

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

11# (at your option) any later version. 

12# 

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

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

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

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the LSST License Statement and 

19# the GNU General Public License along with this program. If not, 

20# see <http://www.lsstcorp.org/LegalNotices/>. 

21# 

22import numpy as np 

23import unittest 

24 

25import lsst.utils.tests 

26 

27from lsst.daf.base import PropertyList 

28import lsst.afw.cameraGeom as cameraGeom 

29from lsst.geom import Point2I, Extent2I, Box2I, Extent2D 

30from lsst.afw.image import ExposureF, VisitInfo 

31 

32from lsst.ip.isr.isrTask import IsrTask 

33 

34 

35def makeAmplifier(name, bbox, rawImageBox, overscanBox, gain, readNoise, saturation): 

36 amp = cameraGeom.Amplifier.Builder() 

37 amp.setName(name) 

38 amp.setBBox(bbox) 

39 amp.setRawDataBBox(rawImageBox) 

40 amp.setRawHorizontalOverscanBBox(overscanBox) 

41 amp.setGain(gain) 

42 amp.setReadNoise(readNoise) 

43 amp.setSaturation(saturation) 

44 amp.setSuspectLevel(np.nan) 

45 return amp 

46 

47 

48class EmpiricalVarianceTestCast(lsst.utils.tests.TestCase): 

49 def setUp(self): 

50 """Constructs a CCD with two amplifiers and prepares for ISR""" 

51 rng = np.random.Generator(np.random.MT19937(42)) 

52 

53 baseValue = 100.0 

54 gain = 1.0 

55 readNoise = 123456789.0 

56 saturation = 987654321.0 

57 height = 234 

58 imageSize = Extent2I(123, height) 

59 overscanSize = Extent2I(16, height) 

60 self.sigma = 1.234 

61 

62 # Set up the various regions 

63 overscan1 = Box2I(Point2I(0, 0), overscanSize) 

64 image1 = Box2I(Point2I(overscanSize[0], 0), imageSize) 

65 image2 = Box2I(Point2I(overscanSize[0] + imageSize[0], 0), imageSize) 

66 overscan2 = Box2I(Point2I(overscanSize[0] + 2*imageSize[0], 0), overscanSize) 

67 

68 leftBox = Box2I(overscan1.getMin(), Extent2I(overscan1.getWidth() + image1.getWidth(), height)) 

69 rightBox = Box2I(image2.getMin(), Extent2I(image2.getWidth() + overscan2.getWidth(), height)) 

70 

71 target1 = Box2I(Point2I(0, 0), imageSize) 

72 target2 = Box2I(Point2I(image1.getWidth(), 0), imageSize) 

73 

74 # Set the pixels 

75 exposure = ExposureF(Box2I(Point2I(0, 0), Extent2I(imageSize[0]*2 + overscanSize[0]*2, height))) 

76 yy = np.arange(0, height, 1, dtype=np.float32) 

77 leftImage = ExposureF(exposure, leftBox) 

78 leftImage.image.array[:] = baseValue + yy[:, np.newaxis] 

79 rightImage = ExposureF(exposure, rightBox) 

80 rightImage.image.array[:] = baseValue - yy[:, np.newaxis] 

81 

82 leftOverscan = ExposureF(exposure, overscan1) 

83 leftOverscan.image.array += rng.normal(0.0, self.sigma, leftOverscan.image.array.shape) 

84 rightOverscan = ExposureF(exposure, overscan2) 

85 rightOverscan.image.array += rng.normal(0.0, self.sigma, leftOverscan.image.array.shape) 

86 exposure.mask.array[:] = 0.0 

87 exposure.variance.array[:] = np.nan 

88 

89 # Construct the detectors 

90 amp1 = makeAmplifier("left", target1, image1, overscan1, gain, readNoise, saturation) 

91 amp2 = makeAmplifier("right", target2, image2, overscan2, gain, readNoise, saturation) 

92 ccdBox = Box2I(Point2I(0, 0), Extent2I(image1.getWidth() + image2.getWidth(), height)) 

93 camBuilder = cameraGeom.Camera.Builder("fakeCam") 

94 detBuilder = camBuilder.add("detector", 1) 

95 detBuilder.setSerial("det1") 

96 detBuilder.setBBox(ccdBox) 

97 detBuilder.setPixelSize(Extent2D(1.0, 1.0)) 

98 detBuilder.setOrientation(cameraGeom.Orientation()) 

99 detBuilder.append(amp1) 

100 detBuilder.append(amp2) 

101 cam = camBuilder.finish() 

102 exposure.setDetector(cam.get('detector')) 

103 

104 header = PropertyList() 

105 header.add("EXPTIME", 0.0) 

106 exposure.getInfo().setVisitInfo(VisitInfo(header)) 

107 

108 self.exposure = exposure 

109 self.config = IsrTask.ConfigClass() 

110 

111 # Disable everything we don't care about 

112 self.config.doBias = False 

113 self.config.doDark = False 

114 self.config.doFlat = False 

115 self.config.doFringe = False 

116 self.config.doDefect = False 

117 self.config.doWrite = False 

118 self.config.expectWcs = False 

119 self.config.doLinearize = False 

120 self.config.doCrosstalk = False 

121 self.config.doBrighterFatter = False 

122 self.config.doAttachTransmissionCurve = False 

123 self.config.doAssembleCcd = False 

124 self.config.doNanMasking = False 

125 self.config.doInterpolate = False 

126 

127 self.config.maskNegativeVariance = False # This runs on mocks. 

128 # Set the things that match our test setup 

129 self.config.overscan.fitType = "CHEB" 

130 self.config.overscan.order = 1 

131 self.config.doEmpiricalReadNoise = True 

132 

133 self.task = IsrTask(config=self.config) 

134 

135 def tearDown(self): 

136 del self.exposure 

137 

138 def testEmpiricalVariance(self): 

139 results = self.task.run(self.exposure) 

140 postIsr = results.exposure 

141 

142 self.assertFloatsEqual(postIsr.mask.array, 0) 

143 # Image is not exactly zero because the noise in the overscan (required 

144 # to be able to set the empirical variance) leads to a slight 

145 # misestimate in the polynomial fit. 

146 self.assertFloatsAlmostEqual(np.median(postIsr.image.array), 0.0, atol=5.0e-2) 

147 self.assertFloatsAlmostEqual(np.nanmedian(postIsr.variance.array), self.sigma**2, rtol=5.0e-2) 

148 

149 

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

151 pass 

152 

153 

154def setup_module(module): 

155 lsst.utils.tests.init() 

156 

157 

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

159 import sys 

160 setup_module(sys.modules[__name__]) 

161 unittest.main()