Coverage for tests/test_ptc.py: 9%
324 statements
« prev ^ index » next coverage.py v7.2.3, created at 2023-04-28 04:59 -0700
« prev ^ index » next coverage.py v7.2.3, created at 2023-04-28 04:59 -0700
1#!/usr/bin/env python
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."""
27import unittest
28import numpy as np
29import copy
30import tempfile
32import lsst.utils
33import lsst.utils.tests
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)
40from lsst.pipe.base import TaskMetadata
43class FakeCamera(list):
44 def getName(self):
45 return "FakeCam"
48class PretendRef():
49 "A class to act as a mock exposure reference"
50 def __init__(self, exposure):
51 self.exp = exposure
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
64class MeasurePhotonTransferCurveTaskTestCase(lsst.utils.tests.TestCase):
65 """A test case for the PTC tasks."""
67 def setUp(self):
68 self.defaultConfigExtract = cpPipe.ptc.PhotonTransferCurveExtractTask.ConfigClass()
69 self.defaultTaskExtract = cpPipe.ptc.PhotonTransferCurveExtractTask(config=self.defaultConfigExtract)
71 self.defaultConfigSolve = cpPipe.ptc.PhotonTransferCurveSolveTask.ConfigClass()
72 self.defaultTaskSolve = cpPipe.ptc.PhotonTransferCurveSolveTask(config=self.defaultConfigSolve)
74 self.flatMean = 2000
75 self.readNoiseAdu = 10
76 mockImageConfig = isrMock.IsrMock.ConfigClass()
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
83 self.flatExp1 = isrMock.FlatMock(config=mockImageConfig).run()
84 self.flatExp2 = self.flatExp1.clone()
85 (shapeY, shapeX) = self.flatExp1.getDimensions()
87 self.flatWidth = np.sqrt(self.flatMean) + self.readNoiseAdu
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))
94 self.flatExp1.image.array[:] = flatData1
95 self.flatExp2.image.array[:] = flatData2
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
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))
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
127 def test_covAstier(self):
128 """Test to check getCovariancesAstier
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),
137 """
138 extractConfig = self.defaultConfigExtract
139 extractConfig.minNumberGoodPixelsForCovariance = 5000
140 extractConfig.detectorMeasurementRegion = 'FULL'
141 # Cut off the low-flux point which is a bad fit, and this
142 # also exercises this functionality and makes the tests
143 # run a lot faster.
144 extractConfig.minMeanSignal["ALL_AMPS"] = 2000.0
145 extractTask = cpPipe.ptc.PhotonTransferCurveExtractTask(config=extractConfig)
147 solveConfig = self.defaultConfigSolve
148 solveConfig.ptcFitType = 'FULLCOVARIANCE'
149 solveTask = cpPipe.ptc.PhotonTransferCurveSolveTask(config=solveConfig)
151 inputGain = self.gain
153 muStandard, varStandard = {}, {}
154 expDict = {}
155 expIds = []
156 idCounter = 0
157 for expTime in self.timeVec:
158 mockExp1, mockExp2 = makeMockFlats(expTime, gain=inputGain,
159 readNoiseElectrons=3,
160 expId1=idCounter, expId2=idCounter+1)
161 mockExpRef1 = PretendRef(mockExp1)
162 mockExpRef2 = PretendRef(mockExp2)
163 expDict[expTime] = ((mockExpRef1, idCounter), (mockExpRef2, idCounter+1))
164 expIds.append(idCounter)
165 expIds.append(idCounter+1)
166 for ampNumber, ampName in enumerate(self.ampNames):
167 # cov has (i, j, var, cov, npix)
168 im1Area, im2Area, imStatsCtrl, mu1, mu2 = extractTask.getImageAreasMasksStats(mockExp1,
169 mockExp2)
170 muDiff, varDiff, covAstier = extractTask.measureMeanVarCov(im1Area, im2Area, imStatsCtrl,
171 mu1, mu2)
172 muStandard.setdefault(ampName, []).append(muDiff)
173 varStandard.setdefault(ampName, []).append(varDiff)
174 idCounter += 2
176 resultsExtract = extractTask.run(inputExp=expDict, inputDims=expIds,
177 taskMetadata=[self.metadataContents for x in expIds])
179 # Force the last PTC dataset to have a NaN, and ensure that the
180 # task runs (DM-38029). This is a minor perturbation and does not
181 # affect the output comparison.
182 resultsExtract.outputCovariances[-2].rawMeans['C:0,0'] = np.array([np.nan])
183 resultsExtract.outputCovariances[-2].rawVars['C:0,0'] = np.array([np.nan])
185 resultsSolve = solveTask.run(resultsExtract.outputCovariances,
186 camera=FakeCamera([self.flatExp1.getDetector()]))
188 ptc = resultsSolve.outputPtcDataset
190 for amp in self.ampNames:
191 self.assertAlmostEqual(ptc.gain[amp], inputGain, places=2)
192 for v1, v2 in zip(varStandard[amp], ptc.finalVars[amp]):
193 self.assertAlmostEqual(v1/v2, 1.0, places=1)
195 mask = ptc.getGoodPoints(amp)
197 values = ((ptc.covariancesModel[amp][mask, 0, 0] - ptc.covariances[amp][mask, 0, 0])
198 / ptc.covariancesModel[amp][mask, 0, 0])
199 np.testing.assert_array_less(np.abs(values), 2e-3)
201 values = ((ptc.covariancesModel[amp][mask, 1, 1] - ptc.covariances[amp][mask, 1, 1])
202 / ptc.covariancesModel[amp][mask, 1, 1])
203 np.testing.assert_array_less(np.abs(values), 0.2)
205 values = ((ptc.covariancesModel[amp][mask, 1, 2] - ptc.covariances[amp][mask, 1, 2])
206 / ptc.covariancesModel[amp][mask, 1, 2])
207 np.testing.assert_array_less(np.abs(values), 0.2)
209 expIdsUsed = ptc.getExpIdsUsed("C:0,0")
210 # Check that these are the same as the inputs, paired up, with the
211 # first two (low flux) and final two (nans) removed.
212 self.assertTrue(np.all(expIdsUsed == np.array(expIds).reshape(len(expIds) // 2, 2)[1:-1]))
214 goodAmps = ptc.getGoodAmps()
215 self.assertEqual(goodAmps, self.ampNames)
217 # Check that every possibly modified field has the same length.
218 covShape = None
219 covSqrtShape = None
220 covModelShape = None
221 covModelNoBShape = None
223 for ampName in self.ampNames:
224 if covShape is None:
225 covShape = ptc.covariances[ampName].shape
226 covSqrtShape = ptc.covariancesSqrtWeights[ampName].shape
227 covModelShape = ptc.covariancesModel[ampName].shape
228 covModelNoBShape = ptc.covariancesModelNoB[ampName].shape
229 else:
230 self.assertEqual(ptc.covariances[ampName].shape, covShape)
231 self.assertEqual(ptc.covariancesSqrtWeights[ampName].shape, covSqrtShape)
232 self.assertEqual(ptc.covariancesModel[ampName].shape, covModelShape)
233 self.assertEqual(ptc.covariancesModelNoB[ampName].shape, covModelNoBShape)
235 # And check that this is serializable
236 with tempfile.NamedTemporaryFile(suffix=".fits") as f:
237 usedFilename = ptc.writeFits(f.name)
238 fromFits = PhotonTransferCurveDataset.readFits(usedFilename)
239 self.assertEqual(fromFits, ptc)
241 def ptcFitAndCheckPtc(self, order=None, fitType=None, doTableArray=False, doFitBootstrap=False):
242 localDataset = copy.deepcopy(self.dataset)
243 localDataset.ptcFitType = fitType
244 configSolve = copy.copy(self.defaultConfigSolve)
245 configLin = cpPipe.linearity.LinearitySolveTask.ConfigClass()
246 placesTests = 6
247 if doFitBootstrap:
248 configSolve.doFitBootstrap = True
249 # Bootstrap method in cp_pipe/utils.py does multiple fits
250 # in the precense of noise. Allow for more margin of
251 # error.
252 placesTests = 3
254 if fitType == 'POLYNOMIAL':
255 if order not in [2, 3]:
256 RuntimeError("Enter a valid polynomial order for this test: 2 or 3")
257 if order == 2:
258 for ampName in self.ampNames:
259 localDataset.rawVars[ampName] = [self.noiseSq + self.c1*mu + self.c2*mu**2 for
260 mu in localDataset.rawMeans[ampName]]
261 configSolve.polynomialFitDegree = 2
262 if order == 3:
263 for ampName in self.ampNames:
264 localDataset.rawVars[ampName] = [self.noiseSq + self.c1*mu + self.c2*mu**2 + self.c3*mu**3
265 for mu in localDataset.rawMeans[ampName]]
266 configSolve.polynomialFitDegree = 3
267 elif fitType == 'EXPAPPROXIMATION':
268 g = self.gain
269 for ampName in self.ampNames:
270 localDataset.rawVars[ampName] = [(0.5/(self.a00*g**2)*(np.exp(2*self.a00*mu*g)-1)
271 + self.noiseSq/(g*g))
272 for mu in localDataset.rawMeans[ampName]]
273 else:
274 raise RuntimeError("Enter a fit function type: 'POLYNOMIAL' or 'EXPAPPROXIMATION'")
276 # Initialize mask and covariance weights that will be used in fits.
277 # Covariance weights values empirically determined from one of
278 # the cases in test_covAstier.
279 matrixSize = localDataset.covMatrixSide
280 maskLength = len(localDataset.rawMeans[ampName])
281 for ampName in self.ampNames:
282 localDataset.expIdMask[ampName] = np.repeat(True, maskLength)
283 localDataset.covariancesSqrtWeights[ampName] = np.repeat(np.ones((matrixSize, matrixSize)),
284 maskLength).reshape((maskLength,
285 matrixSize,
286 matrixSize))
287 localDataset.covariancesSqrtWeights[ampName][:, 0, 0] = [0.07980188, 0.01339653, 0.0073118,
288 0.00502802, 0.00383132, 0.00309475,
289 0.00259572, 0.00223528, 0.00196273,
290 0.00174943, 0.00157794, 0.00143707,
291 0.00131929, 0.00121935, 0.0011334,
292 0.00105893, 0.00099357, 0.0009358,
293 0.00088439, 0.00083833]
295 configLin.maxLookupTableAdu = 200000 # Max ADU in input mock flats
296 configLin.maxLinearAdu = 100000
297 configLin.minLinearAdu = 50000
298 if doTableArray:
299 configLin.linearityType = "LookupTable"
300 else:
301 configLin.linearityType = "Polynomial"
302 solveTask = cpPipe.ptc.PhotonTransferCurveSolveTask(config=configSolve)
303 linearityTask = cpPipe.linearity.LinearitySolveTask(config=configLin)
305 if doTableArray:
306 # Non-linearity
307 numberAmps = len(self.ampNames)
308 # localDataset: PTC dataset
309 # (`lsst.ip.isr.ptcDataset.PhotonTransferCurveDataset`)
310 localDataset = solveTask.fitMeasurementsToModel(localDataset)
311 # linDataset here is a lsst.pipe.base.Struct
312 linDataset = linearityTask.run(localDataset,
313 dummy=[1.0],
314 camera=FakeCamera([self.flatExp1.getDetector()]),
315 inputPhotodiodeData={},
316 inputDims={'detector': 0})
317 linDataset = linDataset.outputLinearizer
318 else:
319 localDataset = solveTask.fitMeasurementsToModel(localDataset)
320 linDataset = linearityTask.run(localDataset,
321 dummy=[1.0],
322 camera=FakeCamera([self.flatExp1.getDetector()]),
323 inputPhotodiodeData={},
324 inputDims={'detector': 0})
325 linDataset = linDataset.outputLinearizer
326 if doTableArray:
327 # check that the linearizer table has been filled out properly
328 for i in np.arange(numberAmps):
329 tMax = (configLin.maxLookupTableAdu)/self.flux
330 timeRange = np.linspace(0., tMax, configLin.maxLookupTableAdu)
331 signalIdeal = timeRange*self.flux
332 signalUncorrected = funcPolynomial(np.array([0.0, self.flux, self.k2NonLinearity]),
333 timeRange)
334 linearizerTableRow = signalIdeal - signalUncorrected
335 self.assertEqual(len(linearizerTableRow), len(linDataset.tableData[i, :]))
336 for j in np.arange(len(linearizerTableRow)):
337 self.assertAlmostEqual(linearizerTableRow[j], linDataset.tableData[i, :][j],
338 places=placesTests)
339 else:
340 # check entries in localDataset, which was modified by the function
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 self.assertEqual(fitType, localDataset.ptcFitType)
348 self.assertAlmostEqual(self.gain, localDataset.gain[ampName])
349 if fitType == 'POLYNOMIAL':
350 self.assertAlmostEqual(self.c1, localDataset.ptcFitPars[ampName][1])
351 self.assertAlmostEqual(np.sqrt(self.noiseSq)*self.gain, localDataset.noise[ampName])
352 if fitType == 'EXPAPPROXIMATION':
353 self.assertAlmostEqual(self.a00, localDataset.ptcFitPars[ampName][0])
354 # noise already in electrons for 'EXPAPPROXIMATION' fit
355 self.assertAlmostEqual(np.sqrt(self.noiseSq), localDataset.noise[ampName])
357 # check entries in returned dataset (a dict of , for nonlinearity)
358 for ampName in self.ampNames:
359 maskAmp = localDataset.expIdMask[ampName]
360 finalMuVec = localDataset.rawMeans[ampName][maskAmp]
361 finalTimeVec = localDataset.rawExpTimes[ampName][maskAmp]
362 linearPart = self.flux*finalTimeVec
363 inputFracNonLinearityResiduals = 100*(linearPart - finalMuVec)/linearPart
365 # Nonlinearity fit parameters
366 # Polynomial fits are now normalized to unit flux scaling
367 self.assertAlmostEqual(0.0, linDataset.fitParams[ampName][0], places=1)
368 self.assertAlmostEqual(1.0, linDataset.fitParams[ampName][1],
369 places=5)
371 # Non-linearity coefficient for linearizer
372 squaredCoeff = self.k2NonLinearity/(self.flux**2)
373 self.assertAlmostEqual(squaredCoeff, linDataset.fitParams[ampName][2],
374 places=placesTests)
375 self.assertAlmostEqual(-squaredCoeff, linDataset.linearityCoeffs[ampName][2],
376 places=placesTests)
378 linearPartModel = linDataset.fitParams[ampName][1]*finalTimeVec*self.flux
379 outputFracNonLinearityResiduals = 100*(linearPartModel - finalMuVec)/linearPartModel
380 # Fractional nonlinearity residuals
381 self.assertEqual(len(outputFracNonLinearityResiduals), len(inputFracNonLinearityResiduals))
382 for calc, truth in zip(outputFracNonLinearityResiduals, inputFracNonLinearityResiduals):
383 self.assertAlmostEqual(calc, truth, places=3)
385 def test_ptcFit(self):
386 for createArray in [True, False]:
387 for (fitType, order) in [('POLYNOMIAL', 2), ('POLYNOMIAL', 3), ('EXPAPPROXIMATION', None)]:
388 self.ptcFitAndCheckPtc(fitType=fitType, order=order, doTableArray=createArray)
390 def test_meanVarMeasurement(self):
391 task = self.defaultTaskExtract
392 im1Area, im2Area, imStatsCtrl, mu1, mu2 = task.getImageAreasMasksStats(self.flatExp1,
393 self.flatExp2)
394 mu, varDiff, _ = task.measureMeanVarCov(im1Area, im2Area, imStatsCtrl, mu1, mu2)
396 self.assertLess(self.flatWidth - np.sqrt(varDiff), 1)
397 self.assertLess(self.flatMean - mu, 1)
399 def test_meanVarMeasurementWithNans(self):
400 task = self.defaultTaskExtract
401 self.flatExp1.image.array[20:30, :] = np.nan
402 self.flatExp2.image.array[20:30, :] = np.nan
404 im1Area, im2Area, imStatsCtrl, mu1, mu2 = task.getImageAreasMasksStats(self.flatExp1,
405 self.flatExp2)
406 mu, varDiff, _ = task.measureMeanVarCov(im1Area, im2Area, imStatsCtrl, mu1, mu2)
408 expectedMu1 = np.nanmean(self.flatExp1.image.array)
409 expectedMu2 = np.nanmean(self.flatExp2.image.array)
410 expectedMu = 0.5*(expectedMu1 + expectedMu2)
412 # Now the variance of the difference. First, create the diff image.
413 im1 = self.flatExp1.maskedImage
414 im2 = self.flatExp2.maskedImage
416 temp = im2.clone()
417 temp *= expectedMu1
418 diffIm = im1.clone()
419 diffIm *= expectedMu2
420 diffIm -= temp
421 diffIm /= expectedMu
423 # Divide by two as it is what measureMeanVarCov returns
424 # (variance of difference)
425 expectedVar = 0.5*np.nanvar(diffIm.image.array)
427 # Check that the standard deviations and the emans agree to
428 # less than 1 ADU
429 self.assertLess(np.sqrt(expectedVar) - np.sqrt(varDiff), 1)
430 self.assertLess(expectedMu - mu, 1)
432 def test_meanVarMeasurementAllNan(self):
433 task = self.defaultTaskExtract
434 self.flatExp1.image.array[:, :] = np.nan
435 self.flatExp2.image.array[:, :] = np.nan
437 im1Area, im2Area, imStatsCtrl, mu1, mu2 = task.getImageAreasMasksStats(self.flatExp1,
438 self.flatExp2)
439 mu, varDiff, covDiff = task.measureMeanVarCov(im1Area, im2Area, imStatsCtrl, mu1, mu2)
441 self.assertTrue(np.isnan(mu))
442 self.assertTrue(np.isnan(varDiff))
443 self.assertTrue(covDiff is None)
445 def test_makeZeroSafe(self):
446 noZerosArray = [1., 20, -35, 45578.98, 90.0, 897, 659.8]
447 someZerosArray = [1., 20, 0, 0, 90, 879, 0]
448 allZerosArray = [0., 0.0, 0, 0, 0.0, 0, 0]
450 substituteValue = 1e-10
452 expectedSomeZerosArray = [1., 20, substituteValue, substituteValue, 90, 879, substituteValue]
453 expectedAllZerosArray = np.repeat(substituteValue, len(allZerosArray))
455 measuredSomeZerosArray = self.defaultTaskSolve._makeZeroSafe(someZerosArray,
456 substituteValue=substituteValue)
457 measuredAllZerosArray = self.defaultTaskSolve._makeZeroSafe(allZerosArray,
458 substituteValue=substituteValue)
459 measuredNoZerosArray = self.defaultTaskSolve._makeZeroSafe(noZerosArray,
460 substituteValue=substituteValue)
462 for exp, meas in zip(expectedSomeZerosArray, measuredSomeZerosArray):
463 self.assertEqual(exp, meas)
464 for exp, meas in zip(expectedAllZerosArray, measuredAllZerosArray):
465 self.assertEqual(exp, meas)
466 for exp, meas in zip(noZerosArray, measuredNoZerosArray):
467 self.assertEqual(exp, meas)
469 def test_getInitialGoodPoints(self):
470 xs = [1, 2, 3, 4, 5, 6]
471 ys = [2*x for x in xs]
472 points = self.defaultTaskSolve._getInitialGoodPoints(xs, ys, minVarPivotSearch=0.,
473 consecutivePointsVarDecreases=2)
474 assert np.all(points) == np.all(np.array([True for x in xs]))
476 ys[4] = 7 # Variance decreases in two consecutive points after ys[3]=8
477 ys[5] = 6
478 points = self.defaultTaskSolve._getInitialGoodPoints(xs, ys, minVarPivotSearch=0.,
479 consecutivePointsVarDecreases=2)
480 assert np.all(points) == np.all(np.array([True, True, True, True, False]))
482 def runGetGainFromFlatPair(self, correctionType='NONE'):
483 extractConfig = self.defaultConfigExtract
484 extractConfig.gainCorrectionType = correctionType
485 extractConfig.minNumberGoodPixelsForCovariance = 5000
486 extractTask = cpPipe.ptc.PhotonTransferCurveExtractTask(config=extractConfig)
488 expDict = {}
489 expIds = []
490 idCounter = 0
491 inputGain = self.gain # 1.5 e/ADU
492 for expTime in self.timeVec:
493 # Approximation works better at low flux, e.g., < 10000 ADU
494 mockExp1, mockExp2 = makeMockFlats(expTime, gain=inputGain,
495 readNoiseElectrons=np.sqrt(self.noiseSq),
496 fluxElectrons=100,
497 expId1=idCounter, expId2=idCounter+1)
498 mockExpRef1 = PretendRef(mockExp1)
499 mockExpRef2 = PretendRef(mockExp2)
500 expDict[expTime] = ((mockExpRef1, idCounter), (mockExpRef2, idCounter+1))
501 expIds.append(idCounter)
502 expIds.append(idCounter+1)
503 idCounter += 2
505 resultsExtract = extractTask.run(inputExp=expDict, inputDims=expIds,
506 taskMetadata=[self.metadataContents for x in expIds])
507 for exposurePair in resultsExtract.outputCovariances:
508 for ampName in self.ampNames:
509 if exposurePair.gain[ampName] is np.nan:
510 continue
511 self.assertAlmostEqual(exposurePair.gain[ampName], inputGain, delta=0.04)
513 def test_getGainFromFlatPair(self):
514 for gainCorrectionType in ['NONE', 'SIMPLE', 'FULL', ]:
515 self.runGetGainFromFlatPair(gainCorrectionType)
518class MeasurePhotonTransferCurveDatasetTestCase(lsst.utils.tests.TestCase):
519 def setUp(self):
520 self.ptcData = PhotonTransferCurveDataset(['C00', 'C01'], " ")
521 self.ptcData.inputExpIdPairs = {'C00': [(123, 234), (345, 456), (567, 678)],
522 'C01': [(123, 234), (345, 456), (567, 678)]}
524 def test_generalBehaviour(self):
525 test = PhotonTransferCurveDataset(['C00', 'C01'], " ")
526 test.inputExpIdPairs = {'C00': [(123, 234), (345, 456), (567, 678)],
527 'C01': [(123, 234), (345, 456), (567, 678)]}
530class TestMemory(lsst.utils.tests.MemoryTestCase):
531 pass
534def setup_module(module):
535 lsst.utils.tests.init()
538if __name__ == "__main__": 538 ↛ 539line 538 didn't jump to line 539, because the condition on line 538 was never true
539 lsst.utils.tests.init()
540 unittest.main()