Coverage for tests/test_ptcDataset.py: 11%

144 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-04-01 02:26 -0700

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

21import unittest 

22import tempfile 

23import copy 

24 

25import numpy as np 

26 

27import lsst.utils.tests 

28 

29from lsst.ip.isr import PhotonTransferCurveDataset 

30import lsst.ip.isr.isrMock as isrMock 

31 

32 

33class PtcDatasetCases(lsst.utils.tests.TestCase): 

34 """Test that write/read methods of PhotonTransferCurveDataset work 

35 """ 

36 def setUp(self): 

37 

38 self.flatMean = 2000 

39 self.readNoiseAdu = 10 

40 mockImageConfig = isrMock.IsrMock.ConfigClass() 

41 

42 # flatDrop is not really relevant as we replace the data 

43 # but good to note it in case we change how this image is made 

44 mockImageConfig.flatDrop = 0.99999 

45 mockImageConfig.isTrimmed = True 

46 

47 self.flatExp1 = isrMock.FlatMock(config=mockImageConfig).run() 

48 self.flatExp2 = self.flatExp1.clone() 

49 (shapeY, shapeX) = self.flatExp1.getDimensions() 

50 

51 self.flatWidth = np.sqrt(self.flatMean) + self.readNoiseAdu 

52 

53 self.rng1 = np.random.RandomState(1984) 

54 flatData1 = self.rng1.normal(self.flatMean, self.flatWidth, (shapeX, shapeY)) 

55 self.rng2 = np.random.RandomState(666) 

56 flatData2 = self.rng2.normal(self.flatMean, self.flatWidth, (shapeX, shapeY)) 

57 

58 self.flatExp1.image.array[:] = flatData1 

59 self.flatExp2.image.array[:] = flatData2 

60 

61 self.flux = 1000. # ADU/sec 

62 self.gain = 1.5 # e-/ADU 

63 self.noiseSq = 5*self.gain # 7.5 (e-)^2 

64 self.c1 = 1./self.gain 

65 self.timeVec = np.arange(1., 101., 5) 

66 self.k2NonLinearity = -5e-6 

67 # quadratic signal-chain non-linearity 

68 muVec = self.flux*self.timeVec + self.k2NonLinearity*self.timeVec**2 

69 

70 self.ampNames = [amp.getName() for amp in self.flatExp1.getDetector().getAmplifiers()] 

71 self.dataset = PhotonTransferCurveDataset(self.ampNames, " ") # pack raw data for fitting 

72 self.covariancesSqrtWeights = {} 

73 for ampName in self.ampNames: # just the expTimes and means here - vars vary per function 

74 self.dataset.rawExpTimes[ampName] = self.timeVec 

75 self.dataset.rawMeans[ampName] = muVec 

76 self.covariancesSqrtWeights[ampName] = [] 

77 

78 def test_emptyPtcDataset(self): 

79 """Test an empty PTC dataset.""" 

80 emptyDataset = PhotonTransferCurveDataset( 

81 self.ampNames, 

82 ptcFitType="PARTIAL", 

83 ) 

84 

85 with tempfile.NamedTemporaryFile(suffix=".yaml") as f: 

86 usedFilename = emptyDataset.writeText(f.name) 

87 fromText = PhotonTransferCurveDataset.readText(usedFilename) 

88 self.assertEqual(emptyDataset, fromText) 

89 

90 with tempfile.NamedTemporaryFile(suffix=".fits") as f: 

91 usedFilename = emptyDataset.writeFits(f.name) 

92 fromFits = PhotonTransferCurveDataset.readFits(usedFilename) 

93 self.assertEqual(emptyDataset, fromFits) 

94 

95 def test_partialPtcDataset(self): 

96 """Test of a partial PTC dataset.""" 

97 # Fill the dataset with made up data. 

98 nSideCovMatrix = 2 

99 

100 partialDataset = PhotonTransferCurveDataset( 

101 self.ampNames, 

102 ptcFitType="PARTIAL", 

103 covMatrixSide=nSideCovMatrix 

104 ) 

105 

106 for ampName in partialDataset.ampNames: 

107 partialDataset.setAmpValuesPartialDataset( 

108 ampName, 

109 inputExpIdPair=(10, 11), 

110 rawExpTime=10.0, 

111 rawMean=10.0, 

112 rawVar=10.0, 

113 ) 

114 

115 with tempfile.NamedTemporaryFile(suffix=".yaml") as f: 

116 usedFilename = partialDataset.writeText(f.name) 

117 fromText = PhotonTransferCurveDataset.readText(usedFilename) 

118 self.assertEqual(partialDataset, fromText) 

119 

120 with tempfile.NamedTemporaryFile(suffix=".fits") as f: 

121 usedFilename = partialDataset.writeFits(f.name) 

122 fromFits = PhotonTransferCurveDataset.readFits(usedFilename) 

123 self.assertEqual(partialDataset, fromFits) 

124 

125 def test_ptcDatset(self): 

126 """Test of a full PTC dataset.""" 

127 # Fill the dataset with made up data. 

128 nSignalPoints = 5 

129 nSideCovMatrix = 2 

130 for fitType in ['POLYNOMIAL', 'EXPAPPROXIMATION', 'FULLCOVARIANCE']: 

131 localDataset = PhotonTransferCurveDataset( 

132 self.ampNames, 

133 ptcFitType=fitType, 

134 covMatrixSide=nSideCovMatrix, 

135 ) 

136 localDataset.badAmps = [localDataset.ampNames[0], localDataset.ampNames[1]] 

137 for ampName in localDataset.ampNames: 

138 

139 localDataset.inputExpIdPairs[ampName] = [(1, 2)]*nSignalPoints 

140 localDataset.expIdMask[ampName] = np.ones(nSignalPoints, dtype=bool) 

141 localDataset.expIdMask[ampName][1] = False 

142 localDataset.rawExpTimes[ampName] = np.arange(nSignalPoints) 

143 localDataset.rawMeans[ampName] = self.flux*np.arange(nSignalPoints) 

144 localDataset.rawVars[ampName] = self.c1*self.flux*np.arange(nSignalPoints) 

145 localDataset.photoCharges[ampName] = np.full(nSignalPoints, np.nan) 

146 localDataset.gain[ampName] = self.gain 

147 localDataset.gainErr[ampName] = 0.1 

148 localDataset.noise[ampName] = self.noiseSq 

149 localDataset.noiseErr[ampName] = 2.0 

150 

151 localDataset.finalVars[ampName] = self.c1*self.flux*np.arange(nSignalPoints) 

152 localDataset.finalModelVars[ampName] = np.full(nSignalPoints, 100.0) 

153 localDataset.finalMeans[ampName] = self.flux*np.arange(nSignalPoints) 

154 

155 if fitType in ['POLYNOMIAL', 'EXPAPPROXIMATION', ]: 

156 localDataset.ptcFitPars[ampName] = np.array([10.0, 1.5, 1e-6]) 

157 localDataset.ptcFitParsError[ampName] = np.array([1.0, 0.2, 1e-7]) 

158 localDataset.ptcFitChiSq[ampName] = 1.0 

159 localDataset.ptcTurnoff[ampName] = localDataset.rawMeans[ampName][-1] 

160 

161 localDataset.covariances[ampName] = np.full( 

162 (nSignalPoints, nSideCovMatrix, nSideCovMatrix), 105.0) 

163 localDataset.covariancesModel[ampName] = np.full( 

164 (nSignalPoints, nSideCovMatrix, nSideCovMatrix), np.nan) 

165 localDataset.covariancesSqrtWeights[ampName] = np.full((nSignalPoints, nSideCovMatrix, 

166 nSideCovMatrix), 10.0) 

167 localDataset.aMatrix[ampName] = np.full((nSideCovMatrix, nSideCovMatrix), np.nan) 

168 localDataset.bMatrix[ampName] = np.full((nSideCovMatrix, nSideCovMatrix), np.nan) 

169 localDataset.covariancesModelNoB[ampName] = np.full((nSignalPoints, nSideCovMatrix, 

170 nSideCovMatrix), np.nan) 

171 localDataset.aMatrixNoB[ampName] = np.full( 

172 (nSideCovMatrix, nSideCovMatrix), np.nan) 

173 

174 if localDataset.ptcFitType in ['FULLCOVARIANCE', ]: 

175 localDataset.ptcFitPars[ampName] = np.array([np.nan, np.nan]) 

176 localDataset.ptcFitParsError[ampName] = np.array([np.nan, np.nan]) 

177 localDataset.ptcFitChiSq[ampName] = np.array([np.nan, np.nan]) 

178 localDataset.ptcTurnoff[ampName] = np.array([np.nan, np.nan]) 

179 

180 localDataset.covariances[ampName] = np.full( 

181 (nSignalPoints, nSideCovMatrix, nSideCovMatrix), 105.0) 

182 localDataset.covariancesModel[ampName] = np.full( 

183 (nSignalPoints, nSideCovMatrix, nSideCovMatrix), 100.0) 

184 localDataset.covariancesSqrtWeights[ampName] = np.full((nSignalPoints, nSideCovMatrix, 

185 nSideCovMatrix), 10.0) 

186 localDataset.aMatrix[ampName] = np.full((nSideCovMatrix, nSideCovMatrix), 1e-6) 

187 localDataset.bMatrix[ampName] = np.full((nSideCovMatrix, nSideCovMatrix), 1e-7) 

188 localDataset.covariancesModelNoB[ampName] = np.full((nSignalPoints, nSideCovMatrix, 

189 nSideCovMatrix), 15.0) 

190 localDataset.aMatrixNoB[ampName] = np.full( 

191 (nSideCovMatrix, nSideCovMatrix), 2e-6) 

192 

193 with tempfile.NamedTemporaryFile(suffix=".yaml") as f: 

194 usedFilename = localDataset.writeText(f.name) 

195 fromText = PhotonTransferCurveDataset.readText(usedFilename) 

196 self.assertEqual(localDataset, fromText) 

197 

198 with tempfile.NamedTemporaryFile(suffix=".fits") as f: 

199 usedFilename = localDataset.writeFits(f.name) 

200 fromFits = PhotonTransferCurveDataset.readFits(usedFilename) 

201 self.assertEqual(localDataset, fromFits) 

202 

203 def test_getExpIdsUsed(self): 

204 localDataset = copy.copy(self.dataset) 

205 

206 for pair in [(12, 34), (56, 78), (90, 10)]: 

207 localDataset.inputExpIdPairs["C:0,0"].append(pair) 

208 localDataset.expIdMask["C:0,0"] = np.array([True, False, True]) 

209 self.assertTrue(np.all(localDataset.getExpIdsUsed("C:0,0") == [(12, 34), (90, 10)])) 

210 

211 localDataset.expIdMask["C:0,0"] = np.array([True, False, True, True]) # wrong length now 

212 with self.assertRaises(AssertionError): 

213 localDataset.getExpIdsUsed("C:0,0") 

214 

215 def test_getGoodAmps(self): 

216 dataset = self.dataset 

217 

218 self.assertTrue(dataset.ampNames == self.ampNames) 

219 dataset.badAmps.append("C:0,1") 

220 self.assertTrue(dataset.getGoodAmps() == [amp for amp in self.ampNames if amp != "C:0,1"]) 

221 

222 def test_ptcDataset_pre_dm38309(self): 

223 """Test for PTC datasets created by cpSolvePtcTask prior to DM-38309. 

224 """ 

225 localDataset = copy.copy(self.dataset) 

226 

227 for pair in [[(12, 34)], [(56, 78)], [(90, 10)]]: 

228 localDataset.inputExpIdPairs["C:0,0"].append(pair) 

229 localDataset.expIdMask["C:0,0"] = np.array([True, False, True]) 

230 

231 with self.assertWarnsRegex(RuntimeWarning, "PTC file was written incorrectly"): 

232 used = localDataset.getExpIdsUsed("C:0,0") 

233 

234 self.assertTrue(np.all(used == [(12, 34), (90, 10)])) 

235 

236 

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

238 pass 

239 

240 

241def setup_module(module): 

242 lsst.utils.tests.init() 

243 

244 

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

246 import sys 

247 setup_module(sys.modules[__name__]) 

248 unittest.main()