Coverage for tests/test_ptc.py: 9%

316 statements  

« prev     ^ index     » next       coverage.py v7.2.3, created at 2023-04-25 04:39 -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 

30import tempfile 

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, ptcFitType="PARTIAL") 

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.dataset.covariancesSqrtWeights[ampName] = np.zeros((1, 

117 self.dataset.covMatrixSide, 

118 self.dataset.covMatrixSide)) 

119 

120 # ISR metadata 

121 self.metadataContents = TaskMetadata() 

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

123 # Overscan readout noise [in ADU] 

124 for amp in self.ampNames: 

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

126 

127 def test_covAstier(self): 

128 """Test to check getCovariancesAstier 

129 

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

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

132 MeasurePhotonTransferCurveTask when doCovariancesAstier=True) 

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

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

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

136 

137 """ 

138 extractConfig = self.defaultConfigExtract 

139 extractConfig.minNumberGoodPixelsForCovariance = 5000 

140 extractConfig.detectorMeasurementRegion = 'FULL' 

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

142 

143 solveConfig = self.defaultConfigSolve 

144 solveConfig.ptcFitType = 'FULLCOVARIANCE' 

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

146 

147 inputGain = self.gain 

148 

149 muStandard, varStandard = {}, {} 

150 expDict = {} 

151 expIds = [] 

152 idCounter = 0 

153 for expTime in self.timeVec: 

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

155 readNoiseElectrons=3, 

156 expId1=idCounter, expId2=idCounter+1) 

157 mockExpRef1 = PretendRef(mockExp1) 

158 mockExpRef2 = PretendRef(mockExp2) 

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

160 expIds.append(idCounter) 

161 expIds.append(idCounter+1) 

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

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

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

165 mockExp2) 

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

167 mu1, mu2) 

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

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

170 idCounter += 2 

171 

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

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

174 

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

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

177 # affect the output comparison. 

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

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

180 

181 resultsSolve = solveTask.run(resultsExtract.outputCovariances, 

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

183 

184 for amp in self.ampNames: 

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

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

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

188 

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

190 ptc = resultsSolve.outputPtcDataset 

191 

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

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

194 # final two removed. 

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

196 

197 goodAmps = ptc.getGoodAmps() 

198 self.assertEqual(goodAmps, self.ampNames) 

199 

200 # Check that every possibly modified field has the same length. 

201 covShape = None 

202 covSqrtShape = None 

203 covModelShape = None 

204 covModelNoBShape = None 

205 

206 for ampName in self.ampNames: 

207 if covShape is None: 

208 covShape = ptc.covariances[ampName].shape 

209 covSqrtShape = ptc.covariancesSqrtWeights[ampName].shape 

210 covModelShape = ptc.covariancesModel[ampName].shape 

211 covModelNoBShape = ptc.covariancesModelNoB[ampName].shape 

212 else: 

213 self.assertEqual(ptc.covariances[ampName].shape, covShape) 

214 self.assertEqual(ptc.covariancesSqrtWeights[ampName].shape, covSqrtShape) 

215 self.assertEqual(ptc.covariancesModel[ampName].shape, covModelShape) 

216 self.assertEqual(ptc.covariancesModelNoB[ampName].shape, covModelNoBShape) 

217 

218 # And check that this is serializable 

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

220 usedFilename = ptc.writeFits(f.name) 

221 fromFits = PhotonTransferCurveDataset.readFits(usedFilename) 

222 self.assertEqual(fromFits, ptc) 

223 

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

225 localDataset = copy.deepcopy(self.dataset) 

226 localDataset.ptcFitType = fitType 

227 configSolve = copy.copy(self.defaultConfigSolve) 

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

229 placesTests = 6 

230 if doFitBootstrap: 

231 configSolve.doFitBootstrap = True 

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

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

234 # error. 

235 placesTests = 3 

236 

237 if fitType == 'POLYNOMIAL': 

238 if order not in [2, 3]: 

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

240 if order == 2: 

241 for ampName in self.ampNames: 

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

243 mu in localDataset.rawMeans[ampName]] 

244 configSolve.polynomialFitDegree = 2 

245 if order == 3: 

246 for ampName in self.ampNames: 

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

248 for mu in localDataset.rawMeans[ampName]] 

249 configSolve.polynomialFitDegree = 3 

250 elif fitType == 'EXPAPPROXIMATION': 

251 g = self.gain 

252 for ampName in self.ampNames: 

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

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

255 for mu in localDataset.rawMeans[ampName]] 

256 else: 

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

258 

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

260 # Covariance weights values empirically determined from one of 

261 # the cases in test_covAstier. 

262 matrixSize = localDataset.covMatrixSide 

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

264 for ampName in self.ampNames: 

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

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

267 maskLength).reshape((maskLength, 

268 matrixSize, 

269 matrixSize)) 

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

271 0.00502802, 0.00383132, 0.00309475, 

272 0.00259572, 0.00223528, 0.00196273, 

273 0.00174943, 0.00157794, 0.00143707, 

274 0.00131929, 0.00121935, 0.0011334, 

275 0.00105893, 0.00099357, 0.0009358, 

276 0.00088439, 0.00083833] 

277 

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

279 configLin.maxLinearAdu = 100000 

280 configLin.minLinearAdu = 50000 

281 if doTableArray: 

282 configLin.linearityType = "LookupTable" 

283 else: 

284 configLin.linearityType = "Polynomial" 

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

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

287 

288 if doTableArray: 

289 # Non-linearity 

290 numberAmps = len(self.ampNames) 

291 # localDataset: PTC dataset 

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

293 localDataset = solveTask.fitMeasurementsToModel(localDataset) 

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

295 linDataset = linearityTask.run(localDataset, 

296 dummy=[1.0], 

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

298 inputPhotodiodeData={}, 

299 inputDims={'detector': 0}) 

300 linDataset = linDataset.outputLinearizer 

301 else: 

302 localDataset = solveTask.fitMeasurementsToModel(localDataset) 

303 linDataset = linearityTask.run(localDataset, 

304 dummy=[1.0], 

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

306 inputPhotodiodeData={}, 

307 inputDims={'detector': 0}) 

308 linDataset = linDataset.outputLinearizer 

309 if doTableArray: 

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

311 for i in np.arange(numberAmps): 

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

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

314 signalIdeal = timeRange*self.flux 

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

316 timeRange) 

317 linearizerTableRow = signalIdeal - signalUncorrected 

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

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

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

321 places=placesTests) 

322 else: 

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

324 for ampName in self.ampNames: 

325 maskAmp = localDataset.expIdMask[ampName] 

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

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

328 linearPart = self.flux*finalTimeVec 

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

330 self.assertEqual(fitType, localDataset.ptcFitType) 

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

332 if fitType == 'POLYNOMIAL': 

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

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

335 if fitType == 'EXPAPPROXIMATION': 

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

337 # noise already in electrons for 'EXPAPPROXIMATION' fit 

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

339 

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

341 for ampName in self.ampNames: 

342 maskAmp = localDataset.expIdMask[ampName] 

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

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

345 linearPart = self.flux*finalTimeVec 

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

347 

348 # Nonlinearity fit parameters 

349 # Polynomial fits are now normalized to unit flux scaling 

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

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

352 places=5) 

353 

354 # Non-linearity coefficient for linearizer 

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

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

357 places=placesTests) 

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

359 places=placesTests) 

360 

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

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

363 # Fractional nonlinearity residuals 

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

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

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

367 

368 def test_ptcFit(self): 

369 for createArray in [True, False]: 

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

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

372 

373 def test_meanVarMeasurement(self): 

374 task = self.defaultTaskExtract 

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

376 self.flatExp2) 

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

378 

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

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

381 

382 def test_meanVarMeasurementWithNans(self): 

383 task = self.defaultTaskExtract 

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

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

386 

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

388 self.flatExp2) 

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

390 

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

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

393 expectedMu = 0.5*(expectedMu1 + expectedMu2) 

394 

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

396 im1 = self.flatExp1.maskedImage 

397 im2 = self.flatExp2.maskedImage 

398 

399 temp = im2.clone() 

400 temp *= expectedMu1 

401 diffIm = im1.clone() 

402 diffIm *= expectedMu2 

403 diffIm -= temp 

404 diffIm /= expectedMu 

405 

406 # Divide by two as it is what measureMeanVarCov returns 

407 # (variance of difference) 

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

409 

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

411 # less than 1 ADU 

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

413 self.assertLess(expectedMu - mu, 1) 

414 

415 def test_meanVarMeasurementAllNan(self): 

416 task = self.defaultTaskExtract 

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

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

419 

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

421 self.flatExp2) 

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

423 

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

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

426 self.assertTrue(covDiff is None) 

427 

428 def test_makeZeroSafe(self): 

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

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

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

432 

433 substituteValue = 1e-10 

434 

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

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

437 

438 measuredSomeZerosArray = self.defaultTaskSolve._makeZeroSafe(someZerosArray, 

439 substituteValue=substituteValue) 

440 measuredAllZerosArray = self.defaultTaskSolve._makeZeroSafe(allZerosArray, 

441 substituteValue=substituteValue) 

442 measuredNoZerosArray = self.defaultTaskSolve._makeZeroSafe(noZerosArray, 

443 substituteValue=substituteValue) 

444 

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

446 self.assertEqual(exp, meas) 

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

448 self.assertEqual(exp, meas) 

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

450 self.assertEqual(exp, meas) 

451 

452 def test_getInitialGoodPoints(self): 

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

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

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

456 consecutivePointsVarDecreases=2) 

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

458 

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

460 ys[5] = 6 

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

462 consecutivePointsVarDecreases=2) 

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

464 

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

466 extractConfig = self.defaultConfigExtract 

467 extractConfig.gainCorrectionType = correctionType 

468 extractConfig.minNumberGoodPixelsForCovariance = 5000 

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

470 

471 expDict = {} 

472 expIds = [] 

473 idCounter = 0 

474 inputGain = self.gain # 1.5 e/ADU 

475 for expTime in self.timeVec: 

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

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

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

479 fluxElectrons=100, 

480 expId1=idCounter, expId2=idCounter+1) 

481 mockExpRef1 = PretendRef(mockExp1) 

482 mockExpRef2 = PretendRef(mockExp2) 

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

484 expIds.append(idCounter) 

485 expIds.append(idCounter+1) 

486 idCounter += 2 

487 

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

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

490 for exposurePair in resultsExtract.outputCovariances: 

491 for ampName in self.ampNames: 

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

493 continue 

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

495 

496 def test_getGainFromFlatPair(self): 

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

498 self.runGetGainFromFlatPair(gainCorrectionType) 

499 

500 

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

502 def setUp(self): 

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

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

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

506 

507 def test_generalBehaviour(self): 

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

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

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

511 

512 

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

514 pass 

515 

516 

517def setup_module(module): 

518 lsst.utils.tests.init() 

519 

520 

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

522 lsst.utils.tests.init() 

523 unittest.main()