Coverage for tests/test_ptc.py: 10%

305 statements  

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

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

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

173 # affect the output comparison. 

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

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

176 

177 resultsSolve = solveTask.run(resultsExtract.outputCovariances, 

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

179 

180 for amp in self.ampNames: 

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

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

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

184 

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

186 localDataset = copy.deepcopy(self.dataset) 

187 localDataset.ptcFitType = fitType 

188 configSolve = copy.copy(self.defaultConfigSolve) 

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

190 placesTests = 6 

191 if doFitBootstrap: 

192 configSolve.doFitBootstrap = True 

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

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

195 # error. 

196 placesTests = 3 

197 

198 if fitType == 'POLYNOMIAL': 

199 if order not in [2, 3]: 

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

201 if order == 2: 

202 for ampName in self.ampNames: 

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

204 mu in localDataset.rawMeans[ampName]] 

205 configSolve.polynomialFitDegree = 2 

206 if order == 3: 

207 for ampName in self.ampNames: 

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

209 for mu in localDataset.rawMeans[ampName]] 

210 configSolve.polynomialFitDegree = 3 

211 elif fitType == 'EXPAPPROXIMATION': 

212 g = self.gain 

213 for ampName in self.ampNames: 

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

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

216 for mu in localDataset.rawMeans[ampName]] 

217 else: 

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

219 

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

221 # Covariance weights values empirically determined from one of 

222 # the cases in test_covAstier. 

223 matrixSize = localDataset.covMatrixSide 

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

225 for ampName in self.ampNames: 

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

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

228 maskLength).reshape((maskLength, 

229 matrixSize, 

230 matrixSize)) 

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

232 0.00502802, 0.00383132, 0.00309475, 

233 0.00259572, 0.00223528, 0.00196273, 

234 0.00174943, 0.00157794, 0.00143707, 

235 0.00131929, 0.00121935, 0.0011334, 

236 0.00105893, 0.00099357, 0.0009358, 

237 0.00088439, 0.00083833] 

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

239 configLin.maxLinearAdu = 100000 

240 configLin.minLinearAdu = 50000 

241 if doTableArray: 

242 configLin.linearityType = "LookupTable" 

243 else: 

244 configLin.linearityType = "Polynomial" 

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

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

247 

248 if doTableArray: 

249 # Non-linearity 

250 numberAmps = len(self.ampNames) 

251 # localDataset: PTC dataset 

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

253 localDataset = solveTask.fitMeasurementsToModel(localDataset) 

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

255 linDataset = linearityTask.run(localDataset, 

256 dummy=[1.0], 

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

258 inputPhotodiodeData={}, 

259 inputDims={'detector': 0}) 

260 linDataset = linDataset.outputLinearizer 

261 else: 

262 localDataset = solveTask.fitMeasurementsToModel(localDataset) 

263 linDataset = linearityTask.run(localDataset, 

264 dummy=[1.0], 

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

266 inputPhotodiodeData={}, 

267 inputDims={'detector': 0}) 

268 linDataset = linDataset.outputLinearizer 

269 if doTableArray: 

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

271 for i in np.arange(numberAmps): 

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

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

274 signalIdeal = timeRange*self.flux 

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

276 timeRange) 

277 linearizerTableRow = signalIdeal - signalUncorrected 

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

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

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

281 places=placesTests) 

282 else: 

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

284 for ampName in self.ampNames: 

285 maskAmp = localDataset.expIdMask[ampName] 

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

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

288 linearPart = self.flux*finalTimeVec 

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

290 self.assertEqual(fitType, localDataset.ptcFitType) 

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

292 if fitType == 'POLYNOMIAL': 

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

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

295 if fitType == 'EXPAPPROXIMATION': 

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

297 # noise already in electrons for 'EXPAPPROXIMATION' fit 

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

299 

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

301 for ampName in self.ampNames: 

302 maskAmp = localDataset.expIdMask[ampName] 

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

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

305 linearPart = self.flux*finalTimeVec 

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

307 

308 # Nonlinearity fit parameters 

309 # Polynomial fits are now normalized to unit flux scaling 

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

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

312 places=5) 

313 

314 # Non-linearity coefficient for linearizer 

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

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

317 places=placesTests) 

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

319 places=placesTests) 

320 

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

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

323 # Fractional nonlinearity residuals 

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

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

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

327 

328 def test_ptcFit(self): 

329 for createArray in [True, False]: 

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

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

332 

333 def test_meanVarMeasurement(self): 

334 task = self.defaultTaskExtract 

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

336 self.flatExp2) 

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

338 

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

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

341 

342 def test_meanVarMeasurementWithNans(self): 

343 task = self.defaultTaskExtract 

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

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

346 

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

348 self.flatExp2) 

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

350 

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

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

353 expectedMu = 0.5*(expectedMu1 + expectedMu2) 

354 

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

356 im1 = self.flatExp1.maskedImage 

357 im2 = self.flatExp2.maskedImage 

358 

359 temp = im2.clone() 

360 temp *= expectedMu1 

361 diffIm = im1.clone() 

362 diffIm *= expectedMu2 

363 diffIm -= temp 

364 diffIm /= expectedMu 

365 

366 # Divide by two as it is what measureMeanVarCov returns 

367 # (variance of difference) 

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

369 

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

371 # less than 1 ADU 

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

373 self.assertLess(expectedMu - mu, 1) 

374 

375 def test_meanVarMeasurementAllNan(self): 

376 task = self.defaultTaskExtract 

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

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

379 

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

381 self.flatExp2) 

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

383 

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

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

386 self.assertTrue(covDiff is None) 

387 

388 def test_makeZeroSafe(self): 

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

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

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

392 

393 substituteValue = 1e-10 

394 

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

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

397 

398 measuredSomeZerosArray = self.defaultTaskSolve._makeZeroSafe(someZerosArray, 

399 substituteValue=substituteValue) 

400 measuredAllZerosArray = self.defaultTaskSolve._makeZeroSafe(allZerosArray, 

401 substituteValue=substituteValue) 

402 measuredNoZerosArray = self.defaultTaskSolve._makeZeroSafe(noZerosArray, 

403 substituteValue=substituteValue) 

404 

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

406 self.assertEqual(exp, meas) 

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

408 self.assertEqual(exp, meas) 

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

410 self.assertEqual(exp, meas) 

411 

412 def test_getInitialGoodPoints(self): 

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

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

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

416 consecutivePointsVarDecreases=2) 

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

418 

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

420 ys[5] = 6 

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

422 consecutivePointsVarDecreases=2) 

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

424 

425 def test_getExpIdsUsed(self): 

426 localDataset = copy.copy(self.dataset) 

427 

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

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

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

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

432 

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

434 with self.assertRaises(AssertionError): 

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

436 

437 def test_getGoodAmps(self): 

438 dataset = self.dataset 

439 

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

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

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

443 

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

445 extractConfig = self.defaultConfigExtract 

446 extractConfig.gainCorrectionType = correctionType 

447 extractConfig.minNumberGoodPixelsForCovariance = 5000 

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

449 

450 expDict = {} 

451 expIds = [] 

452 idCounter = 0 

453 inputGain = self.gain # 1.5 e/ADU 

454 for expTime in self.timeVec: 

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

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

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

458 fluxElectrons=100, 

459 expId1=idCounter, expId2=idCounter+1) 

460 mockExpRef1 = PretendRef(mockExp1) 

461 mockExpRef2 = PretendRef(mockExp2) 

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

463 expIds.append(idCounter) 

464 expIds.append(idCounter+1) 

465 idCounter += 2 

466 

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

468 taskMetadata=[self.metadataContents]) 

469 for exposurePair in resultsExtract.outputCovariances: 

470 for ampName in self.ampNames: 

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

472 continue 

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

474 

475 def test_getGainFromFlatPair(self): 

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

477 self.runGetGainFromFlatPair(gainCorrectionType) 

478 

479 

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

481 def setUp(self): 

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

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

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

485 

486 def test_generalBehaviour(self): 

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

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

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

490 

491 

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

493 pass 

494 

495 

496def setup_module(module): 

497 lsst.utils.tests.init() 

498 

499 

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

501 lsst.utils.tests.init() 

502 unittest.main()