Coverage for tests/test_isrTask.py: 11%
373 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 11:41 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 11:41 +0000
1#
2# LSST Data Management System
3# Copyright 2008-2017 AURA/LSST.
4#
5# This product includes software developed by the
6# LSST Project (http://www.lsst.org/).
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the LSST License Statement and
19# the GNU General Public License along with this program. If not,
20# see <https://www.lsstcorp.org/LegalNotices/>.
21#
23import unittest
24import numpy as np
26import lsst.afw.image as afwImage
27import lsst.ip.isr.isrMock as isrMock
28import lsst.utils.tests
29from lsst.ip.isr.isrTask import (IsrTask, IsrTaskConfig)
30from lsst.ip.isr.isrQa import IsrQaConfig
31from lsst.pipe.base import Struct
32from lsst.ip.isr import PhotonTransferCurveDataset
35def countMaskedPixels(maskedImage, maskPlane):
36 """Function to count the number of masked pixels of a given type.
38 Parameters
39 ----------
40 maskedImage : `lsst.afw.image.MaskedImage`
41 Image to measure the mask on.
42 maskPlane : `str`
43 Name of the mask plane to count
45 Returns
46 -------
47 nMask : `int`
48 Number of masked pixels.
49 """
50 bitMask = maskedImage.getMask().getPlaneBitMask(maskPlane)
51 isBit = maskedImage.getMask().getArray() & bitMask > 0
52 numBit = np.sum(isBit)
53 return numBit
56def computeImageMedianAndStd(image):
57 """Function to calculate median and std of image data.
59 Parameters
60 ----------
61 image : `lsst.afw.image.Image`
62 Image to measure statistics on.
64 Returns
65 -------
66 median : `float`
67 Image median.
68 std : `float`
69 Image stddev.
70 """
71 median = np.nanmedian(image.getArray())
72 std = np.nanstd(image.getArray())
73 return (median, std)
76class IsrTaskTestCases(lsst.utils.tests.TestCase):
77 """Test IsrTask methods with trimmed raw data.
78 """
79 def setUp(self):
80 self.config = IsrTaskConfig()
81 self.config.overscan.doParallelOverscan = True
82 self.config.qa = IsrQaConfig()
83 self.task = IsrTask(config=self.config)
84 self.camera = isrMock.IsrMock().getCamera()
86 self.inputExp = isrMock.TrimmedRawMock().run()
87 self.amp = self.inputExp.getDetector()[0]
88 self.mi = self.inputExp.getMaskedImage()
90 def validateIsrData(self, results):
91 """results should be a struct with components that are
92 not None if included in the configuration file.
93 """
94 self.assertIsInstance(results, Struct)
95 if self.config.doBias is True:
96 self.assertIsNotNone(results.bias)
97 if self.config.doDark is True:
98 self.assertIsNotNone(results.dark)
99 if self.config.doFlat is True:
100 self.assertIsNotNone(results.flat)
101 if self.config.doFringe is True:
102 self.assertIsNotNone(results.fringes)
103 if self.config.doDefect is True:
104 self.assertIsNotNone(results.defects)
105 if self.config.doBrighterFatter is True:
106 self.assertIsNotNone(results.bfKernel)
107 if self.config.doAttachTransmissionCurve is True:
108 self.assertIsNotNone(results.opticsTransmission)
109 self.assertIsNotNone(results.filterTransmission)
110 self.assertIsNotNone(results.sensorTransmission)
111 self.assertIsNotNone(results.atmosphereTransmission)
113 def test_ensureExposure(self):
114 """Test that an exposure has a usable instance class.
115 """
116 self.assertIsInstance(self.task.ensureExposure(self.inputExp, self.camera, 0),
117 afwImage.Exposure)
119 def test_convertItoF(self):
120 """Test conversion from integer to floating point pixels.
121 """
122 result = self.task.convertIntToFloat(self.inputExp)
123 self.assertEqual(result.getImage().getArray().dtype, np.dtype("float32"))
124 self.assertEqual(result, self.inputExp)
126 def test_updateVariance(self):
127 """Expect The variance image should have a larger median value after
128 this operation.
129 """
130 ampName = self.amp.getName()
131 effectivePtc = PhotonTransferCurveDataset([ampName], "TEST_PTC", 1)
132 effectivePtc.gain[ampName] = self.amp.getGain()
133 effectivePtc.noise[ampName] = self.amp.getReadNoise()
134 statBefore = computeImageMedianAndStd(self.inputExp.variance[self.amp.getBBox()])
135 # effectivePtc will have noise and gain.
136 self.task.updateVariance(self.inputExp, self.amp, effectivePtc)
137 statAfter = computeImageMedianAndStd(self.inputExp.variance[self.amp.getBBox()])
138 self.assertGreater(statAfter[0], statBefore[0])
139 self.assertFloatsAlmostEqual(statBefore[0], 0.0, atol=1e-2)
140 self.assertFloatsAlmostEqual(statAfter[0], 8170.0195, atol=1e-2)
142 def test_defineEffectivePtc(self):
143 ampName = self.amp.getName()
144 inputPtc = PhotonTransferCurveDataset([ampName], "TEST_PTC", 1)
145 inputPtc.gain[ampName] = 1.2
146 inputPtc.noise[ampName] = 5
148 exposureMetadata = self.inputExp.getMetadata()
149 detector = self.inputExp.getDetector()
150 overscans = [self.task.overscanCorrection(self.inputExp, self.amp)]
151 bfGains = {ampName: 1.5}
153 # Defaults
154 # doEmpiricalReadNoise = False
155 # usePtcReadNoise = False
156 # usePtcGains = False
157 # doRaiseOnCalibMismatch= False
158 effectivePtc = self.task.defineEffectivePtc(inputPtc, detector, bfGains,
159 overscans,
160 exposureMetadata)
161 self.assertEqual(effectivePtc.gain[ampName], self.amp.getGain())
162 self.assertEqual(effectivePtc.noise[ampName], self.amp.getReadNoise())
164 # Use input PTC values
165 self.config.usePtcGains = True
166 self.config.usePtcReadNoise = True
167 effectivePtc = self.task.defineEffectivePtc(inputPtc, detector, bfGains,
168 overscans,
169 exposureMetadata)
170 self.assertEqual(effectivePtc.gain[ampName], inputPtc.gain[ampName])
171 self.assertEqual(effectivePtc.noise[ampName], inputPtc.noise[ampName])
173 # use empirical readout noise from overscan and PTC gain
174 self.config.usePtcGains = True
175 self.config.doEmpiricalReadNoise = True
176 effectivePtc = self.task.defineEffectivePtc(inputPtc, detector, bfGains,
177 overscans,
178 exposureMetadata)
179 self.assertFloatsAlmostEqual(effectivePtc.noise[ampName], 39.8794613621691)
180 self.assertEqual(effectivePtc.gain[ampName], inputPtc.gain[ampName])
182 # Raise if the PTC and BFK gains don't match
183 results = None
184 with self.assertRaises(RuntimeError):
185 self.config.doRaiseOnCalibMismatch = True
186 results = self.task.defineEffectivePtc(inputPtc, detector, bfGains,
187 overscans,
188 exposureMetadata)
189 self.assertIsNone(results)
191 def test_darkCorrection(self):
192 """Expect the median image value should decrease after this operation.
193 """
194 darkIm = isrMock.DarkMock().run()
196 statBefore = computeImageMedianAndStd(self.inputExp.image[self.amp.getBBox()])
197 self.task.darkCorrection(self.inputExp, darkIm)
198 statAfter = computeImageMedianAndStd(self.inputExp.image[self.amp.getBBox()])
199 self.assertLess(statAfter[0], statBefore[0])
200 self.assertFloatsAlmostEqual(statBefore[0], 8070.0195, atol=1e-2)
201 self.assertFloatsAlmostEqual(statAfter[0], 8045.7773, atol=1e-2)
203 def test_darkCorrection_noVisitInfo(self):
204 """Expect the median image value should decrease after this operation.
205 """
206 darkIm = isrMock.DarkMock().run()
207 darkIm.getInfo().setVisitInfo(None)
209 statBefore = computeImageMedianAndStd(self.inputExp.image[self.amp.getBBox()])
210 self.task.darkCorrection(self.inputExp, darkIm)
211 statAfter = computeImageMedianAndStd(self.inputExp.image[self.amp.getBBox()])
212 self.assertLess(statAfter[0], statBefore[0])
213 self.assertFloatsAlmostEqual(statBefore[0], 8070.0195, atol=1e-2)
214 self.assertFloatsAlmostEqual(statAfter[0], 8045.7773, atol=1e-2)
216 def test_flatCorrection(self):
217 """Expect the image median should increase (divide by < 1).
218 """
219 flatIm = isrMock.FlatMock().run()
221 statBefore = computeImageMedianAndStd(self.inputExp.image[self.amp.getBBox()])
222 self.task.flatCorrection(self.inputExp, flatIm)
223 statAfter = computeImageMedianAndStd(self.inputExp.image[self.amp.getBBox()])
224 self.assertGreater(statAfter[1], statBefore[1])
225 self.assertFloatsAlmostEqual(statAfter[1], 147407.02, atol=1e-2)
226 self.assertFloatsAlmostEqual(statBefore[1], 147.55304, atol=1e-2)
228 def test_saturationDetection(self):
229 """Expect the saturation level detection/masking to scale with
230 threshold.
231 """
232 ampB = self.amp.rebuild()
233 ampB.setSaturation(9000.0)
234 self.task.saturationDetection(self.inputExp, ampB.finish())
235 countBefore = countMaskedPixels(self.mi, "SAT")
237 ampB.setSaturation(8250.0)
238 self.task.saturationDetection(self.inputExp, ampB.finish())
239 countAfter = countMaskedPixels(self.mi, "SAT")
241 self.assertLessEqual(countBefore, countAfter)
242 self.assertEqual(countBefore, 43)
243 self.assertEqual(countAfter, 136)
245 def test_measureBackground(self):
246 """Expect the background measurement runs successfully and to save
247 metadata values.
248 """
249 self.config.qa.flatness.meshX = 20
250 self.config.qa.flatness.meshY = 20
251 self.task.measureBackground(self.inputExp, self.config.qa)
252 self.assertIsNotNone(self.inputExp.getMetadata().getScalar('SKYLEVEL'))
254 def test_flatContext(self):
255 """Expect the flat context manager runs successfully (applying both
256 flat and dark within the context), and results in the same
257 image data after completion.
258 """
259 darkExp = isrMock.DarkMock().run()
260 flatExp = isrMock.FlatMock().run()
262 mi = self.inputExp.getMaskedImage().clone()
263 with self.task.flatContext(self.inputExp, flatExp, darkExp):
264 contextStat = computeImageMedianAndStd(self.inputExp.getMaskedImage().getImage())
265 self.assertFloatsAlmostEqual(contextStat[0], 37165.594, atol=1e-2)
267 self.assertMaskedImagesAlmostEqual(mi, self.inputExp.getMaskedImage())
270class IsrTaskUnTrimmedTestCases(lsst.utils.tests.TestCase):
271 """Test IsrTask methods using untrimmed raw data.
272 """
273 def setUp(self):
274 self.config = IsrTaskConfig()
275 self.config.overscan.doParallelOverscan = True
276 self.config.qa = IsrQaConfig()
277 self.task = IsrTask(config=self.config)
279 self.mockConfig = isrMock.IsrMockConfig()
280 self.mockConfig.isTrimmed = False
281 self.doGenerateImage = True
282 self.dataContainer = isrMock.MockDataContainer(config=self.mockConfig)
283 self.camera = isrMock.IsrMock(config=self.mockConfig).getCamera()
285 self.inputExp = isrMock.RawMock(config=self.mockConfig).run()
286 self.amp = self.inputExp.getDetector()[0]
287 self.mi = self.inputExp.getMaskedImage()
289 def batchSetConfiguration(self, value):
290 """Set the configuration state to a consistent value.
292 Disable options we do not need as well.
294 Parameters
295 ----------
296 value : `bool`
297 Value to switch common ISR configuration options to.
298 """
299 self.config.qa.flatness.meshX = 20
300 self.config.qa.flatness.meshY = 20
301 self.config.doWrite = False
302 self.config.doLinearize = False
303 self.config.doCrosstalk = False
305 self.config.doConvertIntToFloat = value
306 self.config.doSaturation = value
307 self.config.doSuspect = value
308 self.config.doSetBadRegions = value
309 self.config.doOverscan = value
310 self.config.doBias = value
311 self.config.doVariance = value
312 self.config.doWidenSaturationTrails = value
313 self.config.doBrighterFatter = value
314 self.config.doDefect = value
315 self.config.doSaturationInterpolation = value
316 self.config.doDark = value
317 self.config.doStrayLight = value
318 self.config.doFlat = value
319 self.config.doFringe = value
320 self.config.doMeasureBackground = value
321 self.config.doVignette = value
322 self.config.doAttachTransmissionCurve = value
323 self.config.doUseOpticsTransmission = value
324 self.config.doUseFilterTransmission = value
325 self.config.doUseSensorTransmission = value
326 self.config.doUseAtmosphereTransmission = value
327 self.config.qa.saveStats = value
328 self.config.qa.doThumbnailOss = value
329 self.config.qa.doThumbnailFlattened = value
331 self.config.doApplyGains = not value
332 self.config.doCameraSpecificMasking = value
334 def validateIsrResults(self):
335 """results should be a struct with components that are
336 not None if included in the configuration file.
338 Returns
339 -------
340 results : `pipeBase.Struct`
341 Results struct generated from the current ISR configuration.
342 """
343 self.task = IsrTask(config=self.config)
344 results = self.task.run(self.inputExp,
345 camera=self.camera,
346 bias=self.dataContainer.get("bias"),
347 dark=self.dataContainer.get("dark"),
348 flat=self.dataContainer.get("flat"),
349 bfKernel=self.dataContainer.get("bfKernel"),
350 defects=self.dataContainer.get("defects"),
351 fringes=Struct(fringes=self.dataContainer.get("fringe"), seed=1234),
352 opticsTransmission=self.dataContainer.get("transmission_"),
353 filterTransmission=self.dataContainer.get("transmission_"),
354 sensorTransmission=self.dataContainer.get("transmission_"),
355 atmosphereTransmission=self.dataContainer.get("transmission_")
356 )
358 self.assertIsInstance(results, Struct)
359 self.assertIsInstance(results.exposure, afwImage.Exposure)
360 return results
362 def test_run_allTrue(self):
363 """Expect successful run with expected outputs when all non-exclusive
364 configuration options are on.
366 Output results should be tested more precisely by the
367 individual function tests.
369 """
370 self.batchSetConfiguration(True)
371 self.validateIsrResults()
373 def test_run_allFalse(self):
374 """Expect successful run with expected outputs when all non-exclusive
375 configuration options are off.
377 Output results should be tested more precisely by the
378 individual function tests.
380 """
381 self.batchSetConfiguration(False)
382 self.validateIsrResults()
384 def test_failCases(self):
385 """Expect failure with crosstalk enabled.
387 Output results should be tested more precisely by the
388 individual function tests.
389 """
390 self.batchSetConfiguration(True)
392 # This breaks it
393 self.config.doCrosstalk = True
395 with self.assertRaises(RuntimeError):
396 self.validateIsrResults()
398 def test_maskingCase_negativeVariance(self):
399 """Test masking cases of configuration parameters.
400 """
401 self.batchSetConfiguration(True)
402 self.config.overscan.doParallelOverscan = False
403 self.config.overscan.fitType = "POLY"
404 self.config.overscan.order = 1
406 self.config.doSaturation = False
407 self.config.doWidenSaturationTrails = False
408 self.config.doSaturationInterpolation = False
409 self.config.doSuspect = False
410 self.config.doSetBadRegions = False
411 self.config.doDefect = False
412 self.config.doBrighterFatter = False
414 self.config.maskNegativeVariance = True
415 self.config.doInterpolate = False
417 results = self.validateIsrResults()
419 self.assertEqual(countMaskedPixels(results.exposure, "SAT"), 0)
420 self.assertEqual(countMaskedPixels(results.exposure, "INTRP"), 0)
421 self.assertEqual(countMaskedPixels(results.exposure, "SUSPECT"), 0)
422 self.assertEqual(countMaskedPixels(results.exposure, "BAD"), 40800)
424 def test_maskingCase_noMasking(self):
425 """Test masking cases of configuration parameters.
426 """
427 self.batchSetConfiguration(True)
428 self.config.overscan.fitType = "POLY"
429 self.config.overscan.order = 1
431 self.config.doSaturation = False
432 self.config.doWidenSaturationTrails = False
433 self.config.doSaturationInterpolation = False
434 self.config.doSuspect = False
435 self.config.doSetBadRegions = False
436 self.config.doDefect = False
437 self.config.doBrighterFatter = False
439 self.config.maskNegativeVariance = False
440 self.config.doInterpolate = False
442 results = self.validateIsrResults()
444 self.assertEqual(countMaskedPixels(results.exposure, "SAT"), 0)
445 self.assertEqual(countMaskedPixels(results.exposure, "INTRP"), 0)
446 self.assertEqual(countMaskedPixels(results.exposure, "SUSPECT"), 0)
447 self.assertEqual(countMaskedPixels(results.exposure, "BAD"), 0)
449 def test_maskingCase_satMasking(self):
450 """Test masking cases of configuration parameters.
451 """
452 self.batchSetConfiguration(True)
453 self.config.overscan.fitType = "POLY"
454 self.config.overscan.order = 1
456 self.config.saturation = 20000.0
457 self.config.doSaturation = True
458 self.config.doWidenSaturationTrails = True
460 self.config.doSaturationInterpolation = False
461 self.config.doSuspect = False
462 self.config.doSetBadRegions = False
463 self.config.doDefect = False
464 self.config.doBrighterFatter = False
466 self.config.maskNegativeVariance = False # These are mock images.
468 results = self.validateIsrResults()
470 self.assertEqual(countMaskedPixels(results.exposure, "SAT"), 0)
471 self.assertEqual(countMaskedPixels(results.exposure, "INTRP"), 0)
472 self.assertEqual(countMaskedPixels(results.exposure, "SUSPECT"), 0)
473 self.assertEqual(countMaskedPixels(results.exposure, "BAD"), 0)
475 def test_maskingCase_satMaskingAndInterp(self):
476 """Test masking cases of configuration parameters.
477 """
478 self.batchSetConfiguration(True)
479 self.config.overscan.fitType = "POLY"
480 self.config.overscan.order = 1
482 self.config.saturation = 20000.0
483 self.config.doSaturation = True
484 self.config.doWidenSaturationTrails = True
485 self.config.doSaturationInterpolation = True
487 self.config.doSuspect = False
488 self.config.doSetBadRegions = False
489 self.config.doDefect = False
490 self.config.doBrighterFatter = False
492 self.config.maskNegativeVariance = False # These are mock images.
494 results = self.validateIsrResults()
496 self.assertEqual(countMaskedPixels(results.exposure, "SAT"), 0)
497 self.assertEqual(countMaskedPixels(results.exposure, "INTRP"), 0)
498 self.assertEqual(countMaskedPixels(results.exposure, "SUSPECT"), 0)
499 self.assertEqual(countMaskedPixels(results.exposure, "BAD"), 0)
501 def test_maskingCase_throughEdge(self):
502 """Test masking cases of configuration parameters.
503 """
504 self.batchSetConfiguration(True)
505 self.config.overscan.fitType = "POLY"
506 self.config.overscan.order = 1
508 self.config.saturation = 20000.0
509 self.config.doSaturation = True
510 self.config.doWidenSaturationTrails = True
511 self.config.doSaturationInterpolation = True
512 self.config.numEdgeSuspect = 5
513 self.config.doSuspect = True
515 self.config.doSetBadRegions = False
516 self.config.doDefect = False
517 self.config.doBrighterFatter = False
519 self.config.maskNegativeVariance = False # These are mock images.
521 results = self.validateIsrResults()
523 self.assertEqual(countMaskedPixels(results.exposure, "SAT"), 0)
524 self.assertEqual(countMaskedPixels(results.exposure, "INTRP"), 0)
525 self.assertEqual(countMaskedPixels(results.exposure, "SUSPECT"), 0)
526 self.assertEqual(countMaskedPixels(results.exposure, "BAD"), 0)
528 def test_maskingCase_throughDefects(self):
529 """Test masking cases of configuration parameters.
530 """
531 self.batchSetConfiguration(True)
532 self.config.overscan.fitType = "POLY"
533 self.config.overscan.order = 1
535 self.config.saturation = 20000.0
536 self.config.doSaturation = True
537 self.config.doWidenSaturationTrails = True
538 self.config.doSaturationInterpolation = True
539 self.config.numEdgeSuspect = 5
540 self.config.doSuspect = True
541 self.config.doDefect = True
543 self.config.doSetBadRegions = False
544 self.config.doBrighterFatter = False
546 self.config.maskNegativeVariance = False # These are mock images.
548 results = self.validateIsrResults()
550 self.assertEqual(countMaskedPixels(results.exposure, "SAT"), 0)
551 self.assertEqual(countMaskedPixels(results.exposure, "INTRP"), 2000)
552 self.assertEqual(countMaskedPixels(results.exposure, "SUSPECT"), 3940)
553 self.assertEqual(countMaskedPixels(results.exposure, "BAD"), 2000)
555 def test_maskingCase_throughDefectsAmpEdges(self):
556 """Test masking cases of configuration parameters.
557 """
558 self.batchSetConfiguration(True)
559 self.config.overscan.fitType = "POLY"
560 self.config.overscan.order = 1
562 self.config.saturation = 20000.0
563 self.config.doSaturation = True
564 self.config.doWidenSaturationTrails = True
565 self.config.doSaturationInterpolation = True
566 self.config.numEdgeSuspect = 5
567 self.config.doSuspect = True
568 self.config.doDefect = True
569 self.config.edgeMaskLevel = 'AMP'
571 self.config.doSetBadRegions = False
572 self.config.doBrighterFatter = False
574 self.config.maskNegativeVariance = False # These are mock images.
576 results = self.validateIsrResults()
578 self.assertEqual(countMaskedPixels(results.exposure, "SAT"), 0)
579 self.assertEqual(countMaskedPixels(results.exposure, "INTRP"), 2000)
580 self.assertEqual(countMaskedPixels(results.exposure, "SUSPECT"), 11280)
581 self.assertEqual(countMaskedPixels(results.exposure, "BAD"), 2000)
583 def test_maskingCase_throughBad(self):
584 """Test masking cases of configuration parameters.
585 """
586 self.batchSetConfiguration(True)
587 self.config.overscan.fitType = "POLY"
588 self.config.overscan.order = 1
590 self.config.saturation = 20000.0
591 self.config.doSaturation = True
592 self.config.doWidenSaturationTrails = True
593 self.config.doSaturationInterpolation = True
595 self.config.doSuspect = True
596 self.config.doDefect = True
597 self.config.doSetBadRegions = True
598 self.config.doBrighterFatter = False
600 self.config.maskNegativeVariance = False # These are mock images.
602 results = self.validateIsrResults()
604 self.assertEqual(countMaskedPixels(results.exposure, "SAT"), 0)
605 self.assertEqual(countMaskedPixels(results.exposure, "INTRP"), 2000)
606 self.assertEqual(countMaskedPixels(results.exposure, "SUSPECT"), 0)
607 self.assertEqual(countMaskedPixels(results.exposure, "BAD"), 2000)
609 def test_binnedExposures(self):
610 """Ensure that binned exposures have correct sizes."""
611 self.batchSetConfiguration(True)
612 self.config.doBinnedExposures = True
613 self.config.binFactor1 = 8
614 self.config.binFactor2 = 64
616 results = self.validateIsrResults()
618 original = results.exposure.image.array.shape
619 bin1 = results.outputBin1Exposure.image.array.shape
620 bin2 = results.outputBin2Exposure.image.array.shape
622 # Binning truncates, so check that the original and obtained
623 # binned images have the correct offset.
624 self.assertEqual(original[0] - bin1[0] * 8, 4)
625 self.assertEqual(original[1] - bin1[1] * 8, 0)
626 self.assertEqual(original[0] - bin2[0] * 64, 12)
627 self.assertEqual(original[1] - bin2[1] * 64, 8)
630class MemoryTester(lsst.utils.tests.MemoryTestCase):
631 pass
634def setup_module(module):
635 lsst.utils.tests.init()
638if __name__ == "__main__": 638 ↛ 639line 638 didn't jump to line 639, because the condition on line 638 was never true
639 lsst.utils.tests.init()
640 unittest.main(failfast=True)