Coverage for tests/test_ptc.py: 10%

307 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-03-22 02:54 -0700

1#!/usr/bin/env python 

2 

3# 

4# LSST Data Management System 

5# 

6# Copyright 2008-2017 AURA/LSST. 

7# 

8# This product includes software developed by the 

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

10# 

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

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

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

14# (at your option) any later version. 

15# 

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

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

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

19# GNU General Public License for more details. 

20# 

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

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

23# see <https://www.lsstcorp.org/LegalNotices/>. 

24# 

25"""Test cases for cp_pipe.""" 

26 

27from __future__ import absolute_import, division, print_function 

28import unittest 

29import numpy as np 

30import copy 

31 

32import lsst.utils 

33import lsst.utils.tests 

34 

35import lsst.cp.pipe as cpPipe 

36import lsst.ip.isr.isrMock as isrMock 

37from lsst.ip.isr import PhotonTransferCurveDataset 

38from lsst.cp.pipe.utils import (funcPolynomial, makeMockFlats) 

39 

40from lsst.pipe.base import TaskMetadata 

41 

42 

43class FakeCamera(list): 

44 def getName(self): 

45 return "FakeCam" 

46 

47 

48class PretendRef(): 

49 "A class to act as a mock exposure reference" 

50 def __init__(self, exposure): 

51 self.exp = exposure 

52 

53 def get(self, component=None): 

54 if component == 'visitInfo': 

55 return self.exp.getVisitInfo() 

56 elif component == 'detector': 

57 return self.exp.getDetector() 

58 elif component == 'metadata': 

59 return self.exp.getMetadata() 

60 else: 

61 return self.exp 

62 

63 

64class MeasurePhotonTransferCurveTaskTestCase(lsst.utils.tests.TestCase): 

65 """A test case for the PTC tasks.""" 

66 

67 def setUp(self): 

68 self.defaultConfigExtract = cpPipe.ptc.PhotonTransferCurveExtractTask.ConfigClass() 

69 self.defaultTaskExtract = cpPipe.ptc.PhotonTransferCurveExtractTask(config=self.defaultConfigExtract) 

70 

71 self.defaultConfigSolve = cpPipe.ptc.PhotonTransferCurveSolveTask.ConfigClass() 

72 self.defaultTaskSolve = cpPipe.ptc.PhotonTransferCurveSolveTask(config=self.defaultConfigSolve) 

73 

74 self.flatMean = 2000 

75 self.readNoiseAdu = 10 

76 mockImageConfig = isrMock.IsrMock.ConfigClass() 

77 

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

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

80 mockImageConfig.flatDrop = 0.99999 

81 mockImageConfig.isTrimmed = True 

82 

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

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

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

86 

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

88 

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

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

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

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

93 

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

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

96 

97 # create fake PTC data to see if fit works, for one amp ('amp') 

98 self.flux = 1000. # ADU/sec 

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

100 self.k2NonLinearity = -5e-6 

101 # quadratic signal-chain non-linearity 

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

103 self.gain = 0.75 # e-/ADU 

104 self.c1 = 1./self.gain 

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

106 self.a00 = -1.2e-6 

107 self.c2 = -1.5e-6 

108 self.c3 = -4.7e-12 # tuned so that it turns over for 200k mean 

109 

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

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

112 self.covariancesSqrtWeights = {} 

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

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

115 self.dataset.rawMeans[ampName] = muVec 

116 self.covariancesSqrtWeights[ampName] = [] 

117 

118 # ISR metadata 

119 self.metadataContents = TaskMetadata() 

120 self.metadataContents["isr"] = {} 

121 # Overscan readout noise [in ADU] 

122 for amp in self.ampNames: 

123 self.metadataContents["isr"][f"RESIDUAL STDEV {amp}"] = np.sqrt(self.noiseSq)/self.gain 

124 

125 def test_covAstier(self): 

126 """Test to check getCovariancesAstier 

127 

128 We check that the gain is the same as the imput gain from the 

129 mock data, that the covariances via FFT (as it is in 

130 MeasurePhotonTransferCurveTask when doCovariancesAstier=True) 

131 are the same as calculated in real space, and that Cov[0, 0] 

132 (i.e., the variances) are similar to the variances calculated 

133 with the standard method (when doCovariancesAstier=false), 

134 

135 """ 

136 extractConfig = self.defaultConfigExtract 

137 extractConfig.minNumberGoodPixelsForCovariance = 5000 

138 extractConfig.detectorMeasurementRegion = 'FULL' 

139 extractTask = cpPipe.ptc.PhotonTransferCurveExtractTask(config=extractConfig) 

140 

141 solveConfig = self.defaultConfigSolve 

142 solveConfig.ptcFitType = 'FULLCOVARIANCE' 

143 solveTask = cpPipe.ptc.PhotonTransferCurveSolveTask(config=solveConfig) 

144 

145 inputGain = self.gain 

146 

147 muStandard, varStandard = {}, {} 

148 expDict = {} 

149 expIds = [] 

150 idCounter = 0 

151 for expTime in self.timeVec: 

152 mockExp1, mockExp2 = makeMockFlats(expTime, gain=inputGain, 

153 readNoiseElectrons=3, 

154 expId1=idCounter, expId2=idCounter+1) 

155 mockExpRef1 = PretendRef(mockExp1) 

156 mockExpRef2 = PretendRef(mockExp2) 

157 expDict[expTime] = ((mockExpRef1, idCounter), (mockExpRef2, idCounter+1)) 

158 expIds.append(idCounter) 

159 expIds.append(idCounter+1) 

160 for ampNumber, ampName in enumerate(self.ampNames): 

161 # cov has (i, j, var, cov, npix) 

162 im1Area, im2Area, imStatsCtrl, mu1, mu2 = extractTask.getImageAreasMasksStats(mockExp1, 

163 mockExp2) 

164 muDiff, varDiff, covAstier = extractTask.measureMeanVarCov(im1Area, im2Area, imStatsCtrl, 

165 mu1, mu2) 

166 muStandard.setdefault(ampName, []).append(muDiff) 

167 varStandard.setdefault(ampName, []).append(varDiff) 

168 idCounter += 2 

169 

170 resultsExtract = extractTask.run(inputExp=expDict, inputDims=expIds, 

171 taskMetadata=[self.metadataContents for x in expIds]) 

172 

173 # Force the last PTC dataset to have a NaN, and ensure that the 

174 # task runs (DM-38029). This is a minor perturbation and does not 

175 # affect the output comparison. 

176 resultsExtract.outputCovariances[-2].rawMeans['C:0,0'] = [np.nan] 

177 resultsExtract.outputCovariances[-2].rawVars['C:0,0'] = [np.nan] 

178 

179 resultsSolve = solveTask.run(resultsExtract.outputCovariances, 

180 camera=FakeCamera([self.flatExp1.getDetector()])) 

181 

182 for amp in self.ampNames: 

183 self.assertAlmostEqual(resultsSolve.outputPtcDataset.gain[amp], inputGain, places=2) 

184 for v1, v2 in zip(varStandard[amp], resultsSolve.outputPtcDataset.finalVars[amp]): 

185 self.assertAlmostEqual(v1/v2, 1.0, places=1) 

186 

187 def ptcFitAndCheckPtc(self, order=None, fitType=None, doTableArray=False, doFitBootstrap=False): 

188 localDataset = copy.deepcopy(self.dataset) 

189 localDataset.ptcFitType = fitType 

190 configSolve = copy.copy(self.defaultConfigSolve) 

191 configLin = cpPipe.linearity.LinearitySolveTask.ConfigClass() 

192 placesTests = 6 

193 if doFitBootstrap: 

194 configSolve.doFitBootstrap = True 

195 # Bootstrap method in cp_pipe/utils.py does multiple fits 

196 # in the precense of noise. Allow for more margin of 

197 # error. 

198 placesTests = 3 

199 

200 if fitType == 'POLYNOMIAL': 

201 if order not in [2, 3]: 

202 RuntimeError("Enter a valid polynomial order for this test: 2 or 3") 

203 if order == 2: 

204 for ampName in self.ampNames: 

205 localDataset.rawVars[ampName] = [self.noiseSq + self.c1*mu + self.c2*mu**2 for 

206 mu in localDataset.rawMeans[ampName]] 

207 configSolve.polynomialFitDegree = 2 

208 if order == 3: 

209 for ampName in self.ampNames: 

210 localDataset.rawVars[ampName] = [self.noiseSq + self.c1*mu + self.c2*mu**2 + self.c3*mu**3 

211 for mu in localDataset.rawMeans[ampName]] 

212 configSolve.polynomialFitDegree = 3 

213 elif fitType == 'EXPAPPROXIMATION': 

214 g = self.gain 

215 for ampName in self.ampNames: 

216 localDataset.rawVars[ampName] = [(0.5/(self.a00*g**2)*(np.exp(2*self.a00*mu*g)-1) 

217 + self.noiseSq/(g*g)) 

218 for mu in localDataset.rawMeans[ampName]] 

219 else: 

220 RuntimeError("Enter a fit function type: 'POLYNOMIAL' or 'EXPAPPROXIMATION'") 

221 

222 # Initialize mask and covariance weights that will be used in fits. 

223 # Covariance weights values empirically determined from one of 

224 # the cases in test_covAstier. 

225 matrixSize = localDataset.covMatrixSide 

226 maskLength = len(localDataset.rawMeans[ampName]) 

227 for ampName in self.ampNames: 

228 localDataset.expIdMask[ampName] = np.repeat(True, maskLength) 

229 localDataset.covariancesSqrtWeights[ampName] = np.repeat(np.ones((matrixSize, matrixSize)), 

230 maskLength).reshape((maskLength, 

231 matrixSize, 

232 matrixSize)) 

233 localDataset.covariancesSqrtWeights[ampName][:, 0, 0] = [0.07980188, 0.01339653, 0.0073118, 

234 0.00502802, 0.00383132, 0.00309475, 

235 0.00259572, 0.00223528, 0.00196273, 

236 0.00174943, 0.00157794, 0.00143707, 

237 0.00131929, 0.00121935, 0.0011334, 

238 0.00105893, 0.00099357, 0.0009358, 

239 0.00088439, 0.00083833] 

240 configLin.maxLookupTableAdu = 200000 # Max ADU in input mock flats 

241 configLin.maxLinearAdu = 100000 

242 configLin.minLinearAdu = 50000 

243 if doTableArray: 

244 configLin.linearityType = "LookupTable" 

245 else: 

246 configLin.linearityType = "Polynomial" 

247 solveTask = cpPipe.ptc.PhotonTransferCurveSolveTask(config=configSolve) 

248 linearityTask = cpPipe.linearity.LinearitySolveTask(config=configLin) 

249 

250 if doTableArray: 

251 # Non-linearity 

252 numberAmps = len(self.ampNames) 

253 # localDataset: PTC dataset 

254 # (`lsst.ip.isr.ptcDataset.PhotonTransferCurveDataset`) 

255 localDataset = solveTask.fitMeasurementsToModel(localDataset) 

256 # linDataset here is a lsst.pipe.base.Struct 

257 linDataset = linearityTask.run(localDataset, 

258 dummy=[1.0], 

259 camera=FakeCamera([self.flatExp1.getDetector()]), 

260 inputPhotodiodeData={}, 

261 inputDims={'detector': 0}) 

262 linDataset = linDataset.outputLinearizer 

263 else: 

264 localDataset = solveTask.fitMeasurementsToModel(localDataset) 

265 linDataset = linearityTask.run(localDataset, 

266 dummy=[1.0], 

267 camera=FakeCamera([self.flatExp1.getDetector()]), 

268 inputPhotodiodeData={}, 

269 inputDims={'detector': 0}) 

270 linDataset = linDataset.outputLinearizer 

271 if doTableArray: 

272 # check that the linearizer table has been filled out properly 

273 for i in np.arange(numberAmps): 

274 tMax = (configLin.maxLookupTableAdu)/self.flux 

275 timeRange = np.linspace(0., tMax, configLin.maxLookupTableAdu) 

276 signalIdeal = timeRange*self.flux 

277 signalUncorrected = funcPolynomial(np.array([0.0, self.flux, self.k2NonLinearity]), 

278 timeRange) 

279 linearizerTableRow = signalIdeal - signalUncorrected 

280 self.assertEqual(len(linearizerTableRow), len(linDataset.tableData[i, :])) 

281 for j in np.arange(len(linearizerTableRow)): 

282 self.assertAlmostEqual(linearizerTableRow[j], linDataset.tableData[i, :][j], 

283 places=placesTests) 

284 else: 

285 # check entries in localDataset, which was modified by the function 

286 for ampName in self.ampNames: 

287 maskAmp = localDataset.expIdMask[ampName] 

288 finalMuVec = localDataset.rawMeans[ampName][maskAmp] 

289 finalTimeVec = localDataset.rawExpTimes[ampName][maskAmp] 

290 linearPart = self.flux*finalTimeVec 

291 inputFracNonLinearityResiduals = 100*(linearPart - finalMuVec)/linearPart 

292 self.assertEqual(fitType, localDataset.ptcFitType) 

293 self.assertAlmostEqual(self.gain, localDataset.gain[ampName]) 

294 if fitType == 'POLYNOMIAL': 

295 self.assertAlmostEqual(self.c1, localDataset.ptcFitPars[ampName][1]) 

296 self.assertAlmostEqual(np.sqrt(self.noiseSq)*self.gain, localDataset.noise[ampName]) 

297 if fitType == 'EXPAPPROXIMATION': 

298 self.assertAlmostEqual(self.a00, localDataset.ptcFitPars[ampName][0]) 

299 # noise already in electrons for 'EXPAPPROXIMATION' fit 

300 self.assertAlmostEqual(np.sqrt(self.noiseSq), localDataset.noise[ampName]) 

301 

302 # check entries in returned dataset (a dict of , for nonlinearity) 

303 for ampName in self.ampNames: 

304 maskAmp = localDataset.expIdMask[ampName] 

305 finalMuVec = localDataset.rawMeans[ampName][maskAmp] 

306 finalTimeVec = localDataset.rawExpTimes[ampName][maskAmp] 

307 linearPart = self.flux*finalTimeVec 

308 inputFracNonLinearityResiduals = 100*(linearPart - finalMuVec)/linearPart 

309 

310 # Nonlinearity fit parameters 

311 # Polynomial fits are now normalized to unit flux scaling 

312 self.assertAlmostEqual(0.0, linDataset.fitParams[ampName][0], places=1) 

313 self.assertAlmostEqual(1.0, linDataset.fitParams[ampName][1], 

314 places=5) 

315 

316 # Non-linearity coefficient for linearizer 

317 squaredCoeff = self.k2NonLinearity/(self.flux**2) 

318 self.assertAlmostEqual(squaredCoeff, linDataset.fitParams[ampName][2], 

319 places=placesTests) 

320 self.assertAlmostEqual(-squaredCoeff, linDataset.linearityCoeffs[ampName][2], 

321 places=placesTests) 

322 

323 linearPartModel = linDataset.fitParams[ampName][1]*finalTimeVec*self.flux 

324 outputFracNonLinearityResiduals = 100*(linearPartModel - finalMuVec)/linearPartModel 

325 # Fractional nonlinearity residuals 

326 self.assertEqual(len(outputFracNonLinearityResiduals), len(inputFracNonLinearityResiduals)) 

327 for calc, truth in zip(outputFracNonLinearityResiduals, inputFracNonLinearityResiduals): 

328 self.assertAlmostEqual(calc, truth, places=3) 

329 

330 def test_ptcFit(self): 

331 for createArray in [True, False]: 

332 for (fitType, order) in [('POLYNOMIAL', 2), ('POLYNOMIAL', 3), ('EXPAPPROXIMATION', None)]: 

333 self.ptcFitAndCheckPtc(fitType=fitType, order=order, doTableArray=createArray) 

334 

335 def test_meanVarMeasurement(self): 

336 task = self.defaultTaskExtract 

337 im1Area, im2Area, imStatsCtrl, mu1, mu2 = task.getImageAreasMasksStats(self.flatExp1, 

338 self.flatExp2) 

339 mu, varDiff, _ = task.measureMeanVarCov(im1Area, im2Area, imStatsCtrl, mu1, mu2) 

340 

341 self.assertLess(self.flatWidth - np.sqrt(varDiff), 1) 

342 self.assertLess(self.flatMean - mu, 1) 

343 

344 def test_meanVarMeasurementWithNans(self): 

345 task = self.defaultTaskExtract 

346 self.flatExp1.image.array[20:30, :] = np.nan 

347 self.flatExp2.image.array[20:30, :] = np.nan 

348 

349 im1Area, im2Area, imStatsCtrl, mu1, mu2 = task.getImageAreasMasksStats(self.flatExp1, 

350 self.flatExp2) 

351 mu, varDiff, _ = task.measureMeanVarCov(im1Area, im2Area, imStatsCtrl, mu1, mu2) 

352 

353 expectedMu1 = np.nanmean(self.flatExp1.image.array) 

354 expectedMu2 = np.nanmean(self.flatExp2.image.array) 

355 expectedMu = 0.5*(expectedMu1 + expectedMu2) 

356 

357 # Now the variance of the difference. First, create the diff image. 

358 im1 = self.flatExp1.maskedImage 

359 im2 = self.flatExp2.maskedImage 

360 

361 temp = im2.clone() 

362 temp *= expectedMu1 

363 diffIm = im1.clone() 

364 diffIm *= expectedMu2 

365 diffIm -= temp 

366 diffIm /= expectedMu 

367 

368 # Divide by two as it is what measureMeanVarCov returns 

369 # (variance of difference) 

370 expectedVar = 0.5*np.nanvar(diffIm.image.array) 

371 

372 # Check that the standard deviations and the emans agree to 

373 # less than 1 ADU 

374 self.assertLess(np.sqrt(expectedVar) - np.sqrt(varDiff), 1) 

375 self.assertLess(expectedMu - mu, 1) 

376 

377 def test_meanVarMeasurementAllNan(self): 

378 task = self.defaultTaskExtract 

379 self.flatExp1.image.array[:, :] = np.nan 

380 self.flatExp2.image.array[:, :] = np.nan 

381 

382 im1Area, im2Area, imStatsCtrl, mu1, mu2 = task.getImageAreasMasksStats(self.flatExp1, 

383 self.flatExp2) 

384 mu, varDiff, covDiff = task.measureMeanVarCov(im1Area, im2Area, imStatsCtrl, mu1, mu2) 

385 

386 self.assertTrue(np.isnan(mu)) 

387 self.assertTrue(np.isnan(varDiff)) 

388 self.assertTrue(covDiff is None) 

389 

390 def test_makeZeroSafe(self): 

391 noZerosArray = [1., 20, -35, 45578.98, 90.0, 897, 659.8] 

392 someZerosArray = [1., 20, 0, 0, 90, 879, 0] 

393 allZerosArray = [0., 0.0, 0, 0, 0.0, 0, 0] 

394 

395 substituteValue = 1e-10 

396 

397 expectedSomeZerosArray = [1., 20, substituteValue, substituteValue, 90, 879, substituteValue] 

398 expectedAllZerosArray = np.repeat(substituteValue, len(allZerosArray)) 

399 

400 measuredSomeZerosArray = self.defaultTaskSolve._makeZeroSafe(someZerosArray, 

401 substituteValue=substituteValue) 

402 measuredAllZerosArray = self.defaultTaskSolve._makeZeroSafe(allZerosArray, 

403 substituteValue=substituteValue) 

404 measuredNoZerosArray = self.defaultTaskSolve._makeZeroSafe(noZerosArray, 

405 substituteValue=substituteValue) 

406 

407 for exp, meas in zip(expectedSomeZerosArray, measuredSomeZerosArray): 

408 self.assertEqual(exp, meas) 

409 for exp, meas in zip(expectedAllZerosArray, measuredAllZerosArray): 

410 self.assertEqual(exp, meas) 

411 for exp, meas in zip(noZerosArray, measuredNoZerosArray): 

412 self.assertEqual(exp, meas) 

413 

414 def test_getInitialGoodPoints(self): 

415 xs = [1, 2, 3, 4, 5, 6] 

416 ys = [2*x for x in xs] 

417 points = self.defaultTaskSolve._getInitialGoodPoints(xs, ys, minVarPivotSearch=0., 

418 consecutivePointsVarDecreases=2) 

419 assert np.all(points) == np.all(np.array([True for x in xs])) 

420 

421 ys[4] = 7 # Variance decreases in two consecutive points after ys[3]=8 

422 ys[5] = 6 

423 points = self.defaultTaskSolve._getInitialGoodPoints(xs, ys, minVarPivotSearch=0., 

424 consecutivePointsVarDecreases=2) 

425 assert np.all(points) == np.all(np.array([True, True, True, True, False])) 

426 

427 def test_getExpIdsUsed(self): 

428 localDataset = copy.copy(self.dataset) 

429 

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

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

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

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

434 

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

436 with self.assertRaises(AssertionError): 

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

438 

439 def test_getGoodAmps(self): 

440 dataset = self.dataset 

441 

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

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

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

445 

446 def runGetGainFromFlatPair(self, correctionType='NONE'): 

447 extractConfig = self.defaultConfigExtract 

448 extractConfig.gainCorrectionType = correctionType 

449 extractConfig.minNumberGoodPixelsForCovariance = 5000 

450 extractTask = cpPipe.ptc.PhotonTransferCurveExtractTask(config=extractConfig) 

451 

452 expDict = {} 

453 expIds = [] 

454 idCounter = 0 

455 inputGain = self.gain # 1.5 e/ADU 

456 for expTime in self.timeVec: 

457 # Approximation works better at low flux, e.g., < 10000 ADU 

458 mockExp1, mockExp2 = makeMockFlats(expTime, gain=inputGain, 

459 readNoiseElectrons=np.sqrt(self.noiseSq), 

460 fluxElectrons=100, 

461 expId1=idCounter, expId2=idCounter+1) 

462 mockExpRef1 = PretendRef(mockExp1) 

463 mockExpRef2 = PretendRef(mockExp2) 

464 expDict[expTime] = ((mockExpRef1, idCounter), (mockExpRef2, idCounter+1)) 

465 expIds.append(idCounter) 

466 expIds.append(idCounter+1) 

467 idCounter += 2 

468 

469 resultsExtract = extractTask.run(inputExp=expDict, inputDims=expIds, 

470 taskMetadata=[self.metadataContents for x in expIds]) 

471 for exposurePair in resultsExtract.outputCovariances: 

472 for ampName in self.ampNames: 

473 if exposurePair.gain[ampName] is np.nan: 

474 continue 

475 self.assertAlmostEqual(exposurePair.gain[ampName], inputGain, delta=0.04) 

476 

477 def test_getGainFromFlatPair(self): 

478 for gainCorrectionType in ['NONE', 'SIMPLE', 'FULL', ]: 

479 self.runGetGainFromFlatPair(gainCorrectionType) 

480 

481 

482class MeasurePhotonTransferCurveDatasetTestCase(lsst.utils.tests.TestCase): 

483 def setUp(self): 

484 self.ptcData = PhotonTransferCurveDataset(['C00', 'C01'], " ") 

485 self.ptcData.inputExpIdPairs = {'C00': [(123, 234), (345, 456), (567, 678)], 

486 'C01': [(123, 234), (345, 456), (567, 678)]} 

487 

488 def test_generalBehaviour(self): 

489 test = PhotonTransferCurveDataset(['C00', 'C01'], " ") 

490 test.inputExpIdPairs = {'C00': [(123, 234), (345, 456), (567, 678)], 

491 'C01': [(123, 234), (345, 456), (567, 678)]} 

492 

493 

494class TestMemory(lsst.utils.tests.MemoryTestCase): 

495 pass 

496 

497 

498def setup_module(module): 

499 lsst.utils.tests.init() 

500 

501 

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

503 lsst.utils.tests.init() 

504 unittest.main()