Coverage for tests/test_ptc.py: 10%

303 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-02-05 02:57 -0800

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 else: 

59 return self.exp 

60 

61 

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

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

64 

65 def setUp(self): 

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

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

68 

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

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

71 

72 self.flatMean = 2000 

73 self.readNoiseAdu = 10 

74 mockImageConfig = isrMock.IsrMock.ConfigClass() 

75 

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

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

78 mockImageConfig.flatDrop = 0.99999 

79 mockImageConfig.isTrimmed = True 

80 

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

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

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

84 

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

86 

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

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

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

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

91 

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

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

94 

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

96 self.flux = 1000. # ADU/sec 

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

98 self.k2NonLinearity = -5e-6 

99 # quadratic signal-chain non-linearity 

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

101 self.gain = 0.75 # e-/ADU 

102 self.c1 = 1./self.gain 

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

104 self.a00 = -1.2e-6 

105 self.c2 = -1.5e-6 

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

107 

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

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

110 self.covariancesSqrtWeights = {} 

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

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

113 self.dataset.rawMeans[ampName] = muVec 

114 self.covariancesSqrtWeights[ampName] = [] 

115 

116 # ISR metadata 

117 self.metadataContents = TaskMetadata() 

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

119 # Overscan readout noise [in ADU] 

120 for amp in self.ampNames: 

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

122 

123 def test_covAstier(self): 

124 """Test to check getCovariancesAstier 

125 

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

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

128 MeasurePhotonTransferCurveTask when doCovariancesAstier=True) 

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

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

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

132 

133 """ 

134 extractConfig = self.defaultConfigExtract 

135 extractConfig.minNumberGoodPixelsForCovariance = 5000 

136 extractConfig.detectorMeasurementRegion = 'FULL' 

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

138 

139 solveConfig = self.defaultConfigSolve 

140 solveConfig.ptcFitType = 'FULLCOVARIANCE' 

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

142 

143 inputGain = self.gain 

144 

145 muStandard, varStandard = {}, {} 

146 expDict = {} 

147 expIds = [] 

148 idCounter = 0 

149 for expTime in self.timeVec: 

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

151 readNoiseElectrons=3, 

152 expId1=idCounter, expId2=idCounter+1) 

153 mockExpRef1 = PretendRef(mockExp1) 

154 mockExpRef2 = PretendRef(mockExp2) 

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

156 expIds.append(idCounter) 

157 expIds.append(idCounter+1) 

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

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

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

161 mockExp2) 

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

163 mu1, mu2) 

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

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

166 idCounter += 2 

167 

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

169 taskMetadata=[self.metadataContents]) 

170 resultsSolve = solveTask.run(resultsExtract.outputCovariances, 

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

172 

173 for amp in self.ampNames: 

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

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

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

177 

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

179 localDataset = copy.deepcopy(self.dataset) 

180 localDataset.ptcFitType = fitType 

181 configSolve = copy.copy(self.defaultConfigSolve) 

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

183 placesTests = 6 

184 if doFitBootstrap: 

185 configSolve.doFitBootstrap = True 

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

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

188 # error. 

189 placesTests = 3 

190 

191 if fitType == 'POLYNOMIAL': 

192 if order not in [2, 3]: 

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

194 if order == 2: 

195 for ampName in self.ampNames: 

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

197 mu in localDataset.rawMeans[ampName]] 

198 configSolve.polynomialFitDegree = 2 

199 if order == 3: 

200 for ampName in self.ampNames: 

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

202 for mu in localDataset.rawMeans[ampName]] 

203 configSolve.polynomialFitDegree = 3 

204 elif fitType == 'EXPAPPROXIMATION': 

205 g = self.gain 

206 for ampName in self.ampNames: 

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

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

209 for mu in localDataset.rawMeans[ampName]] 

210 else: 

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

212 

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

214 # Covariance weights values empirically determined from one of 

215 # the cases in test_covAstier. 

216 matrixSize = localDataset.covMatrixSide 

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

218 for ampName in self.ampNames: 

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

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

221 maskLength).reshape((maskLength, 

222 matrixSize, 

223 matrixSize)) 

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

225 0.00502802, 0.00383132, 0.00309475, 

226 0.00259572, 0.00223528, 0.00196273, 

227 0.00174943, 0.00157794, 0.00143707, 

228 0.00131929, 0.00121935, 0.0011334, 

229 0.00105893, 0.00099357, 0.0009358, 

230 0.00088439, 0.00083833] 

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

232 configLin.maxLinearAdu = 100000 

233 configLin.minLinearAdu = 50000 

234 if doTableArray: 

235 configLin.linearityType = "LookupTable" 

236 else: 

237 configLin.linearityType = "Polynomial" 

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

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

240 

241 if doTableArray: 

242 # Non-linearity 

243 numberAmps = len(self.ampNames) 

244 # localDataset: PTC dataset 

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

246 localDataset = solveTask.fitMeasurementsToModel(localDataset) 

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

248 linDataset = linearityTask.run(localDataset, 

249 dummy=[1.0], 

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

251 inputPhotodiodeData={}, 

252 inputDims={'detector': 0}) 

253 linDataset = linDataset.outputLinearizer 

254 else: 

255 localDataset = solveTask.fitMeasurementsToModel(localDataset) 

256 linDataset = linearityTask.run(localDataset, 

257 dummy=[1.0], 

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

259 inputPhotodiodeData={}, 

260 inputDims={'detector': 0}) 

261 linDataset = linDataset.outputLinearizer 

262 if doTableArray: 

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

264 for i in np.arange(numberAmps): 

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

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

267 signalIdeal = timeRange*self.flux 

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

269 timeRange) 

270 linearizerTableRow = signalIdeal - signalUncorrected 

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

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

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

274 places=placesTests) 

275 else: 

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

277 for ampName in self.ampNames: 

278 maskAmp = localDataset.expIdMask[ampName] 

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

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

281 linearPart = self.flux*finalTimeVec 

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

283 self.assertEqual(fitType, localDataset.ptcFitType) 

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

285 if fitType == 'POLYNOMIAL': 

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

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

288 if fitType == 'EXPAPPROXIMATION': 

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

290 # noise already in electrons for 'EXPAPPROXIMATION' fit 

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

292 

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

294 for ampName in self.ampNames: 

295 maskAmp = localDataset.expIdMask[ampName] 

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

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

298 linearPart = self.flux*finalTimeVec 

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

300 

301 # Nonlinearity fit parameters 

302 # Polynomial fits are now normalized to unit flux scaling 

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

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

305 places=5) 

306 

307 # Non-linearity coefficient for linearizer 

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

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

310 places=placesTests) 

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

312 places=placesTests) 

313 

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

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

316 # Fractional nonlinearity residuals 

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

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

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

320 

321 def test_ptcFit(self): 

322 for createArray in [True, False]: 

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

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

325 

326 def test_meanVarMeasurement(self): 

327 task = self.defaultTaskExtract 

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

329 self.flatExp2) 

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

331 

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

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

334 

335 def test_meanVarMeasurementWithNans(self): 

336 task = self.defaultTaskExtract 

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

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

339 

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

341 self.flatExp2) 

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

343 

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

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

346 expectedMu = 0.5*(expectedMu1 + expectedMu2) 

347 

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

349 im1 = self.flatExp1.maskedImage 

350 im2 = self.flatExp2.maskedImage 

351 

352 temp = im2.clone() 

353 temp *= expectedMu1 

354 diffIm = im1.clone() 

355 diffIm *= expectedMu2 

356 diffIm -= temp 

357 diffIm /= expectedMu 

358 

359 # Divide by two as it is what measureMeanVarCov returns 

360 # (variance of difference) 

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

362 

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

364 # less than 1 ADU 

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

366 self.assertLess(expectedMu - mu, 1) 

367 

368 def test_meanVarMeasurementAllNan(self): 

369 task = self.defaultTaskExtract 

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

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

372 

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

374 self.flatExp2) 

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

376 

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

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

379 self.assertTrue(covDiff is None) 

380 

381 def test_makeZeroSafe(self): 

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

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

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

385 

386 substituteValue = 1e-10 

387 

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

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

390 

391 measuredSomeZerosArray = self.defaultTaskSolve._makeZeroSafe(someZerosArray, 

392 substituteValue=substituteValue) 

393 measuredAllZerosArray = self.defaultTaskSolve._makeZeroSafe(allZerosArray, 

394 substituteValue=substituteValue) 

395 measuredNoZerosArray = self.defaultTaskSolve._makeZeroSafe(noZerosArray, 

396 substituteValue=substituteValue) 

397 

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

399 self.assertEqual(exp, meas) 

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

401 self.assertEqual(exp, meas) 

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

403 self.assertEqual(exp, meas) 

404 

405 def test_getInitialGoodPoints(self): 

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

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

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

409 consecutivePointsVarDecreases=2) 

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

411 

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

413 ys[5] = 6 

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

415 consecutivePointsVarDecreases=2) 

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

417 

418 def test_getExpIdsUsed(self): 

419 localDataset = copy.copy(self.dataset) 

420 

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

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

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

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

425 

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

427 with self.assertRaises(AssertionError): 

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

429 

430 def test_getGoodAmps(self): 

431 dataset = self.dataset 

432 

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

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

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

436 

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

438 extractConfig = self.defaultConfigExtract 

439 extractConfig.gainCorrectionType = correctionType 

440 extractConfig.minNumberGoodPixelsForCovariance = 5000 

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

442 

443 expDict = {} 

444 expIds = [] 

445 idCounter = 0 

446 inputGain = self.gain # 1.5 e/ADU 

447 for expTime in self.timeVec: 

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

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

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

451 fluxElectrons=100, 

452 expId1=idCounter, expId2=idCounter+1) 

453 mockExpRef1 = PretendRef(mockExp1) 

454 mockExpRef2 = PretendRef(mockExp2) 

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

456 expIds.append(idCounter) 

457 expIds.append(idCounter+1) 

458 idCounter += 2 

459 

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

461 taskMetadata=[self.metadataContents]) 

462 for exposurePair in resultsExtract.outputCovariances: 

463 for ampName in self.ampNames: 

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

465 continue 

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

467 

468 def test_getGainFromFlatPair(self): 

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

470 self.runGetGainFromFlatPair(gainCorrectionType) 

471 

472 

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

474 def setUp(self): 

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

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

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

478 

479 def test_generalBehaviour(self): 

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

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

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

483 

484 

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

486 pass 

487 

488 

489def setup_module(module): 

490 lsst.utils.tests.init() 

491 

492 

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

494 lsst.utils.tests.init() 

495 unittest.main()