Coverage for tests/test_ptc.py: 9%
316 statements
« prev ^ index » next coverage.py v7.2.3, created at 2023-04-22 03:07 -0700
« prev ^ index » next coverage.py v7.2.3, created at 2023-04-22 03:07 -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 extractTask = cpPipe.ptc.PhotonTransferCurveExtractTask(config=extractConfig)
143 solveConfig = self.defaultConfigSolve
144 solveConfig.ptcFitType = 'FULLCOVARIANCE'
145 solveTask = cpPipe.ptc.PhotonTransferCurveSolveTask(config=solveConfig)
147 inputGain = self.gain
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
172 resultsExtract = extractTask.run(inputExp=expDict, inputDims=expIds,
173 taskMetadata=[self.metadataContents for x in expIds])
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])
181 resultsSolve = solveTask.run(resultsExtract.outputCovariances,
182 camera=FakeCamera([self.flatExp1.getDetector()]))
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)
189 # Test various operations on the PTC output from the task.
190 ptc = resultsSolve.outputPtcDataset
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]))
197 goodAmps = ptc.getGoodAmps()
198 self.assertEqual(goodAmps, self.ampNames)
200 # Check that every possibly modified field has the same length.
201 covShape = None
202 covSqrtShape = None
203 covModelShape = None
204 covModelNoBShape = None
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)
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)
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
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'")
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]
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)
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])
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
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)
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)
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)
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)
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)
379 self.assertLess(self.flatWidth - np.sqrt(varDiff), 1)
380 self.assertLess(self.flatMean - mu, 1)
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
387 im1Area, im2Area, imStatsCtrl, mu1, mu2 = task.getImageAreasMasksStats(self.flatExp1,
388 self.flatExp2)
389 mu, varDiff, _ = task.measureMeanVarCov(im1Area, im2Area, imStatsCtrl, mu1, mu2)
391 expectedMu1 = np.nanmean(self.flatExp1.image.array)
392 expectedMu2 = np.nanmean(self.flatExp2.image.array)
393 expectedMu = 0.5*(expectedMu1 + expectedMu2)
395 # Now the variance of the difference. First, create the diff image.
396 im1 = self.flatExp1.maskedImage
397 im2 = self.flatExp2.maskedImage
399 temp = im2.clone()
400 temp *= expectedMu1
401 diffIm = im1.clone()
402 diffIm *= expectedMu2
403 diffIm -= temp
404 diffIm /= expectedMu
406 # Divide by two as it is what measureMeanVarCov returns
407 # (variance of difference)
408 expectedVar = 0.5*np.nanvar(diffIm.image.array)
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)
415 def test_meanVarMeasurementAllNan(self):
416 task = self.defaultTaskExtract
417 self.flatExp1.image.array[:, :] = np.nan
418 self.flatExp2.image.array[:, :] = np.nan
420 im1Area, im2Area, imStatsCtrl, mu1, mu2 = task.getImageAreasMasksStats(self.flatExp1,
421 self.flatExp2)
422 mu, varDiff, covDiff = task.measureMeanVarCov(im1Area, im2Area, imStatsCtrl, mu1, mu2)
424 self.assertTrue(np.isnan(mu))
425 self.assertTrue(np.isnan(varDiff))
426 self.assertTrue(covDiff is None)
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]
433 substituteValue = 1e-10
435 expectedSomeZerosArray = [1., 20, substituteValue, substituteValue, 90, 879, substituteValue]
436 expectedAllZerosArray = np.repeat(substituteValue, len(allZerosArray))
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)
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)
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]))
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]))
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)
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
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)
496 def test_getGainFromFlatPair(self):
497 for gainCorrectionType in ['NONE', 'SIMPLE', 'FULL', ]:
498 self.runGetGainFromFlatPair(gainCorrectionType)
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)]}
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)]}
513class TestMemory(lsst.utils.tests.MemoryTestCase):
514 pass
517def setup_module(module):
518 lsst.utils.tests.init()
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()