Coverage for tests/test_ptc.py: 9%

297 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-03-23 02:55 -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 

27import unittest 

28import numpy as np 

29import copy 

30 

31import lsst.utils 

32import lsst.utils.tests 

33 

34import lsst.cp.pipe as cpPipe 

35import lsst.ip.isr.isrMock as isrMock 

36from lsst.ip.isr import PhotonTransferCurveDataset 

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

38 

39from lsst.pipe.base import TaskMetadata 

40 

41 

42class FakeCamera(list): 

43 def getName(self): 

44 return "FakeCam" 

45 

46 

47class PretendRef(): 

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

49 def __init__(self, exposure): 

50 self.exp = exposure 

51 

52 def get(self, component=None): 

53 if component == 'visitInfo': 

54 return self.exp.getVisitInfo() 

55 elif component == 'detector': 

56 return self.exp.getDetector() 

57 elif component == 'metadata': 

58 return self.exp.getMetadata() 

59 else: 

60 return self.exp 

61 

62 

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

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

65 

66 def setUp(self): 

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

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

69 

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

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

72 

73 self.flatMean = 2000 

74 self.readNoiseAdu = 10 

75 mockImageConfig = isrMock.IsrMock.ConfigClass() 

76 

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

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

79 mockImageConfig.flatDrop = 0.99999 

80 mockImageConfig.isTrimmed = True 

81 

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

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

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

85 

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

87 

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

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

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

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

92 

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

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

95 

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

97 self.flux = 1000. # ADU/sec 

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

99 self.k2NonLinearity = -5e-6 

100 # quadratic signal-chain non-linearity 

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

102 self.gain = 0.75 # e-/ADU 

103 self.c1 = 1./self.gain 

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

105 self.a00 = -1.2e-6 

106 self.c2 = -1.5e-6 

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

108 

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

110 self.dataset = PhotonTransferCurveDataset(self.ampNames, ptcFitType="PARTIAL") 

111 self.covariancesSqrtWeights = {} 

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

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

114 self.dataset.rawMeans[ampName] = muVec 

115 self.dataset.covariancesSqrtWeights[ampName] = np.zeros((1, 

116 self.dataset.covMatrixSide, 

117 self.dataset.covMatrixSide)) 

118 

119 # ISR metadata 

120 self.metadataContents = TaskMetadata() 

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

122 # Overscan readout noise [in ADU] 

123 for amp in self.ampNames: 

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

125 

126 def test_covAstier(self): 

127 """Test to check getCovariancesAstier 

128 

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

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

131 MeasurePhotonTransferCurveTask when doCovariancesAstier=True) 

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

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

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

135 

136 """ 

137 extractConfig = self.defaultConfigExtract 

138 extractConfig.minNumberGoodPixelsForCovariance = 5000 

139 extractConfig.detectorMeasurementRegion = 'FULL' 

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

141 

142 solveConfig = self.defaultConfigSolve 

143 solveConfig.ptcFitType = 'FULLCOVARIANCE' 

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

145 

146 inputGain = self.gain 

147 

148 muStandard, varStandard = {}, {} 

149 expDict = {} 

150 expIds = [] 

151 idCounter = 0 

152 for expTime in self.timeVec: 

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

154 readNoiseElectrons=3, 

155 expId1=idCounter, expId2=idCounter+1) 

156 mockExpRef1 = PretendRef(mockExp1) 

157 mockExpRef2 = PretendRef(mockExp2) 

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

159 expIds.append(idCounter) 

160 expIds.append(idCounter+1) 

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

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

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

164 mockExp2) 

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

166 mu1, mu2) 

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

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

169 idCounter += 2 

170 

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

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

173 

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

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

176 # affect the output comparison. 

177 resultsExtract.outputCovariances[-2].rawMeans['C:0,0'] = np.array([np.nan]) 

178 resultsExtract.outputCovariances[-2].rawVars['C:0,0'] = np.array([np.nan]) 

179 

180 resultsSolve = solveTask.run(resultsExtract.outputCovariances, 

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

182 

183 for amp in self.ampNames: 

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

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

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

187 

188 # Test various operations on the PTC output from the task. 

189 ptc = resultsSolve.outputPtcDataset 

190 

191 expIdsUsed = ptc.getExpIdsUsed("C:0,0") 

192 # Check that these are the same as the inputs, paired up, with the 

193 # final two removed. 

194 self.assertTrue(np.all(expIdsUsed == np.array(expIds).reshape(len(expIds) // 2, 2)[:-1])) 

195 

196 goodAmps = ptc.getGoodAmps() 

197 self.assertEqual(goodAmps, self.ampNames) 

198 

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

200 localDataset = copy.deepcopy(self.dataset) 

201 localDataset.ptcFitType = fitType 

202 configSolve = copy.copy(self.defaultConfigSolve) 

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

204 placesTests = 6 

205 if doFitBootstrap: 

206 configSolve.doFitBootstrap = True 

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

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

209 # error. 

210 placesTests = 3 

211 

212 if fitType == 'POLYNOMIAL': 

213 if order not in [2, 3]: 

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

215 if order == 2: 

216 for ampName in self.ampNames: 

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

218 mu in localDataset.rawMeans[ampName]] 

219 configSolve.polynomialFitDegree = 2 

220 if order == 3: 

221 for ampName in self.ampNames: 

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

223 for mu in localDataset.rawMeans[ampName]] 

224 configSolve.polynomialFitDegree = 3 

225 elif fitType == 'EXPAPPROXIMATION': 

226 g = self.gain 

227 for ampName in self.ampNames: 

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

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

230 for mu in localDataset.rawMeans[ampName]] 

231 else: 

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

233 

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

235 # Covariance weights values empirically determined from one of 

236 # the cases in test_covAstier. 

237 matrixSize = localDataset.covMatrixSide 

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

239 for ampName in self.ampNames: 

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

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

242 maskLength).reshape((maskLength, 

243 matrixSize, 

244 matrixSize)) 

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

246 0.00502802, 0.00383132, 0.00309475, 

247 0.00259572, 0.00223528, 0.00196273, 

248 0.00174943, 0.00157794, 0.00143707, 

249 0.00131929, 0.00121935, 0.0011334, 

250 0.00105893, 0.00099357, 0.0009358, 

251 0.00088439, 0.00083833] 

252 

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

254 configLin.maxLinearAdu = 100000 

255 configLin.minLinearAdu = 50000 

256 if doTableArray: 

257 configLin.linearityType = "LookupTable" 

258 else: 

259 configLin.linearityType = "Polynomial" 

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

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

262 

263 if doTableArray: 

264 # Non-linearity 

265 numberAmps = len(self.ampNames) 

266 # localDataset: PTC dataset 

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

268 localDataset = solveTask.fitMeasurementsToModel(localDataset) 

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

270 linDataset = linearityTask.run(localDataset, 

271 dummy=[1.0], 

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

273 inputPhotodiodeData={}, 

274 inputDims={'detector': 0}) 

275 linDataset = linDataset.outputLinearizer 

276 else: 

277 localDataset = solveTask.fitMeasurementsToModel(localDataset) 

278 linDataset = linearityTask.run(localDataset, 

279 dummy=[1.0], 

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

281 inputPhotodiodeData={}, 

282 inputDims={'detector': 0}) 

283 linDataset = linDataset.outputLinearizer 

284 if doTableArray: 

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

286 for i in np.arange(numberAmps): 

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

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

289 signalIdeal = timeRange*self.flux 

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

291 timeRange) 

292 linearizerTableRow = signalIdeal - signalUncorrected 

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

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

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

296 places=placesTests) 

297 else: 

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

299 for ampName in self.ampNames: 

300 maskAmp = localDataset.expIdMask[ampName] 

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

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

303 linearPart = self.flux*finalTimeVec 

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

305 self.assertEqual(fitType, localDataset.ptcFitType) 

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

307 if fitType == 'POLYNOMIAL': 

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

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

310 if fitType == 'EXPAPPROXIMATION': 

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

312 # noise already in electrons for 'EXPAPPROXIMATION' fit 

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

314 

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

316 for ampName in self.ampNames: 

317 maskAmp = localDataset.expIdMask[ampName] 

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

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

320 linearPart = self.flux*finalTimeVec 

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

322 

323 # Nonlinearity fit parameters 

324 # Polynomial fits are now normalized to unit flux scaling 

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

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

327 places=5) 

328 

329 # Non-linearity coefficient for linearizer 

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

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

332 places=placesTests) 

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

334 places=placesTests) 

335 

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

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

338 # Fractional nonlinearity residuals 

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

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

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

342 

343 def test_ptcFit(self): 

344 for createArray in [True, False]: 

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

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

347 

348 def test_meanVarMeasurement(self): 

349 task = self.defaultTaskExtract 

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

351 self.flatExp2) 

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

353 

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

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

356 

357 def test_meanVarMeasurementWithNans(self): 

358 task = self.defaultTaskExtract 

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

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

361 

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

363 self.flatExp2) 

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

365 

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

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

368 expectedMu = 0.5*(expectedMu1 + expectedMu2) 

369 

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

371 im1 = self.flatExp1.maskedImage 

372 im2 = self.flatExp2.maskedImage 

373 

374 temp = im2.clone() 

375 temp *= expectedMu1 

376 diffIm = im1.clone() 

377 diffIm *= expectedMu2 

378 diffIm -= temp 

379 diffIm /= expectedMu 

380 

381 # Divide by two as it is what measureMeanVarCov returns 

382 # (variance of difference) 

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

384 

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

386 # less than 1 ADU 

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

388 self.assertLess(expectedMu - mu, 1) 

389 

390 def test_meanVarMeasurementAllNan(self): 

391 task = self.defaultTaskExtract 

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

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

394 

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

396 self.flatExp2) 

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

398 

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

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

401 self.assertTrue(covDiff is None) 

402 

403 def test_makeZeroSafe(self): 

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

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

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

407 

408 substituteValue = 1e-10 

409 

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

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

412 

413 measuredSomeZerosArray = self.defaultTaskSolve._makeZeroSafe(someZerosArray, 

414 substituteValue=substituteValue) 

415 measuredAllZerosArray = self.defaultTaskSolve._makeZeroSafe(allZerosArray, 

416 substituteValue=substituteValue) 

417 measuredNoZerosArray = self.defaultTaskSolve._makeZeroSafe(noZerosArray, 

418 substituteValue=substituteValue) 

419 

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

421 self.assertEqual(exp, meas) 

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

423 self.assertEqual(exp, meas) 

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

425 self.assertEqual(exp, meas) 

426 

427 def test_getInitialGoodPoints(self): 

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

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

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

431 consecutivePointsVarDecreases=2) 

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

433 

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

435 ys[5] = 6 

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

437 consecutivePointsVarDecreases=2) 

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

439 

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

441 extractConfig = self.defaultConfigExtract 

442 extractConfig.gainCorrectionType = correctionType 

443 extractConfig.minNumberGoodPixelsForCovariance = 5000 

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

445 

446 expDict = {} 

447 expIds = [] 

448 idCounter = 0 

449 inputGain = self.gain # 1.5 e/ADU 

450 for expTime in self.timeVec: 

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

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

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

454 fluxElectrons=100, 

455 expId1=idCounter, expId2=idCounter+1) 

456 mockExpRef1 = PretendRef(mockExp1) 

457 mockExpRef2 = PretendRef(mockExp2) 

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

459 expIds.append(idCounter) 

460 expIds.append(idCounter+1) 

461 idCounter += 2 

462 

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

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

465 for exposurePair in resultsExtract.outputCovariances: 

466 for ampName in self.ampNames: 

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

468 continue 

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

470 

471 def test_getGainFromFlatPair(self): 

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

473 self.runGetGainFromFlatPair(gainCorrectionType) 

474 

475 

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

477 def setUp(self): 

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

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

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

481 

482 def test_generalBehaviour(self): 

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

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

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

486 

487 

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

489 pass 

490 

491 

492def setup_module(module): 

493 lsst.utils.tests.init() 

494 

495 

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

497 lsst.utils.tests.init() 

498 unittest.main()