Coverage for tests/test_ptc.py: 6%

565 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-07 02:49 -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 

27import unittest 

28import numpy as np 

29import copy 

30import tempfile 

31import logging 

32 

33import lsst.log 

34import lsst.utils 

35import lsst.utils.tests 

36 

37import lsst.cp.pipe as cpPipe 

38import lsst.ip.isr.isrMock as isrMock 

39from lsst.ip.isr import PhotonTransferCurveDataset, PhotodiodeCalib, AmpOffsetTask 

40from lsst.cp.pipe.utils import makeMockFlats, ampOffsetGainRatioFixup 

41 

42from lsst.pipe.base import InMemoryDatasetHandle 

43 

44 

45class FakeCamera(list): 

46 def getName(self): 

47 return "FakeCam" 

48 

49 

50class PretendRef: 

51 "A class to act as a mock exposure reference" 

52 

53 def __init__(self, exposure): 

54 self.exp = exposure 

55 

56 def get(self, component=None): 

57 if component == "visitInfo": 

58 return self.exp.info.getVisitInfo() 

59 elif component == "detector": 

60 return self.exp.getDetector() 

61 elif component == "metadata": 

62 return self.exp.getMetadata() 

63 else: 

64 return self.exp 

65 

66 

67class MeasurePhotonTransferCurveTaskTestCase(lsst.utils.tests.TestCase): 

68 """A test case for the PTC tasks.""" 

69 

70 def setUp(self): 

71 self.defaultConfigExtract = ( 

72 cpPipe.ptc.PhotonTransferCurveExtractPairTask.ConfigClass() 

73 ) 

74 self.defaultTaskExtract = cpPipe.ptc.PhotonTransferCurveExtractTask( 

75 config=self.defaultConfigExtract 

76 ) 

77 

78 self.defaultConfigSolve = cpPipe.ptc.PhotonTransferCurveSolveTask.ConfigClass() 

79 self.defaultTaskSolve = cpPipe.ptc.PhotonTransferCurveSolveTask( 

80 config=self.defaultConfigSolve 

81 ) 

82 

83 self.flatMean = 2000 

84 self.readNoiseAdu = 10 

85 mockImageConfig = isrMock.IsrMock.ConfigClass() 

86 

87 # flatDrop is not really relevant as we replace the data 

88 # but good to note it in case we change how this image is made 

89 mockImageConfig.flatDrop = 0.99999 

90 mockImageConfig.isTrimmed = True 

91 

92 self.flatExp1 = isrMock.FlatMock(config=mockImageConfig).run() 

93 self.flatExp2 = self.flatExp1.clone() 

94 (shapeY, shapeX) = self.flatExp1.getDimensions() 

95 

96 self.flatWidth = np.sqrt(self.flatMean) + self.readNoiseAdu 

97 

98 self.rng1 = np.random.RandomState(1984) 

99 flatData1 = self.rng1.normal(self.flatMean, self.flatWidth, (shapeX, shapeY)) 

100 self.rng2 = np.random.RandomState(666) 

101 flatData2 = self.rng2.normal(self.flatMean, self.flatWidth, (shapeX, shapeY)) 

102 

103 self.flatExp1.image.array[:] = flatData1 

104 self.flatExp2.image.array[:] = flatData2 

105 

106 # create fake PTC data to see if fit works, for one amp ('amp') 

107 self.flux = 1000.0 # ADU/sec 

108 self.timeVec = np.arange(1.0, 101.0, 5) 

109 self.k2NonLinearity = -5e-6 

110 # quadratic signal-chain non-linearity 

111 muVec = self.flux * self.timeVec + self.k2NonLinearity * self.timeVec**2 

112 self.gain = 0.75 # e-/ADU 

113 self.c1 = 1.0 / self.gain 

114 self.noiseSq = 2 * self.gain # 7.5 (e-)^2 

115 self.a00 = -1.2e-6 

116 self.c2 = -1.5e-6 

117 self.c3 = -4.7e-12 # tuned so that it turns over for 200k mean 

118 self.photoCharges = np.linspace(1e-8, 1e-5, len(self.timeVec)) 

119 

120 self.ampNames = [ 

121 amp.getName() for amp in self.flatExp1.getDetector().getAmplifiers() 

122 ] 

123 self.dataset = PhotonTransferCurveDataset(self.ampNames, ptcFitType="PARTIAL") 

124 self.covariancesSqrtWeights = {} 

125 for ( 

126 ampName 

127 ) in self.ampNames: # just the expTimes and means here - vars vary per function 

128 self.dataset.rawExpTimes[ampName] = self.timeVec 

129 self.dataset.rawMeans[ampName] = muVec 

130 self.dataset.covariancesSqrtWeights[ampName] = np.zeros( 

131 (1, self.dataset.covMatrixSide, self.dataset.covMatrixSide) 

132 ) 

133 

134 def test_covAstier(self): 

135 """Test to check getCovariancesAstier 

136 

137 We check that the gain is the same as the input gain from the 

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

139 MeasurePhotonTransferCurveTask when doCovariancesAstier=True) 

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

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

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

143 

144 """ 

145 extractConfig = self.defaultConfigExtract 

146 extractConfig.minNumberGoodPixelsForCovariance = 5000 

147 extractConfig.detectorMeasurementRegion = "FULL" 

148 extractConfig.doExtractPhotodiodeData = True 

149 extractConfig.doKsHistMeasurement = True 

150 extractConfig.auxiliaryHeaderKeys = ["CCOBCURR", "CCDTEMP"] 

151 extractPairTask = cpPipe.ptc.PhotonTransferCurveExtractPairTask(config=extractConfig) 

152 

153 # Create solve task config 

154 solveConfig = self.defaultConfigSolve 

155 # Cut off the low-flux point which is a bad fit, and this 

156 # also exercises this functionality and makes the tests 

157 # run a lot faster. 

158 solveConfig.minMeanSignal["ALL_AMPS"] = 2000.0 

159 # Set the outlier fit threshold higher than the default appropriate 

160 # for this test dataset. 

161 solveConfig.maxSignalInitialPtcOutlierFit = 90000.0 

162 # Given the limited nature of the test data, we can only 

163 # reliably fit out to a 3x3 covariance matrix. 

164 # Improvements will be investigated on DM-46131. 

165 solveConfig.maximumRangeCovariancesAstierFullCovFit = 3 

166 

167 inputGain = self.gain 

168 

169 muStandard, varStandard = {}, {} 

170 nPixelCovarianceStandard = {} 

171 expHandles = [] 

172 expIds = [] 

173 pdHandles = [] 

174 idCounter = 0 

175 for i, expTime in enumerate(self.timeVec): 

176 mockExp1, mockExp2 = makeMockFlats( 

177 expTime, 

178 gain=inputGain, 

179 readNoiseElectrons=3, 

180 expId1=idCounter, 

181 expId2=idCounter + 1, 

182 ampNames=self.ampNames, 

183 ) 

184 for mockExp in [mockExp1, mockExp2]: 

185 md = mockExp.getMetadata() 

186 # These values are chosen to be easily compared after 

187 # processing for correct ordering. 

188 md['CCOBCURR'] = float(idCounter) 

189 md['CCDTEMP'] = float(idCounter + 1) 

190 mockExp.setMetadata(md) 

191 md['SEQFILE'] = 'a_seqfile' 

192 md['SEQNAME'] = 'a_seqfile' 

193 md['SEQCKSUM'] = 'deadbeef' 

194 

195 mockExpRef1 = PretendRef(mockExp1) 

196 mockExpRef2 = PretendRef(mockExp2) 

197 expHandles.extend((mockExpRef1, mockExpRef2)) 

198 expIds.append(idCounter) 

199 expIds.append(idCounter + 1) 

200 maskValue = mockExp1.mask.getPlaneBitMask(extractPairTask.config.maskNameList) 

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

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

203 ( 

204 im1Area, 

205 im2Area, 

206 imStatsCtrl, 

207 mu1, 

208 mu2, 

209 ) = extractPairTask.getImageAreasMasksStats(mockExp1, mockExp2) 

210 nPixelCovariance = ((im1Area.mask.array & maskValue) == 0).sum() 

211 nPixelCovarianceStandard.setdefault(ampName, []).append(nPixelCovariance) 

212 muDiff, varDiff, covAstier, rowMeanVariance = extractPairTask.measureMeanVarCov( 

213 im1Area, im2Area, imStatsCtrl, mu1, mu2 

214 ) 

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

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

217 

218 # Make a photodiode dataset to integrate. 

219 timeSamples = np.linspace(0, 20.0, 100) 

220 currentSamples1 = np.zeros(100) 

221 currentSamples1[50] = -1.0*self.photoCharges[i] 

222 currentSamples2 = np.zeros(100) 

223 currentSamples2[50] = -1.0*(self.photoCharges[i] + 1e-12) 

224 

225 pdCalib1 = PhotodiodeCalib(timeSamples=timeSamples, currentSamples=currentSamples1) 

226 pdCalib1.currentScale = -1.0 

227 pdCalib1.integrationMethod = "CHARGE_SUM" 

228 

229 pdCalib2 = PhotodiodeCalib(timeSamples=timeSamples, currentSamples=currentSamples2) 

230 pdCalib2.currentScale = -1.0 

231 pdCalib2.integrationMethod = "CHARGE_SUM" 

232 

233 pdHandles.append( 

234 InMemoryDatasetHandle( 

235 pdCalib1, 

236 dataId={"exposure": idCounter}, 

237 ) 

238 ) 

239 pdHandles.append( 

240 InMemoryDatasetHandle( 

241 pdCalib2, 

242 dataId={"exposure": idCounter + 1}, 

243 ) 

244 ) 

245 idCounter += 2 

246 

247 # Divide out the pairs. 

248 outputCovariances = [] 

249 for i in range(len(expHandles) // 2): 

250 inputExp = [expHandles[2*i], expHandles[2*i + 1]] 

251 inputDims = [expIds[2*i], expIds[2*i + 1]] 

252 inputPhotodiodeData = [pdHandles[2*i], pdHandles[2*i + 1]] 

253 results = extractPairTask.run( 

254 inputExp=inputExp, 

255 inputDims=inputDims, 

256 inputPhotodiodeData=inputPhotodiodeData, 

257 ) 

258 outputCovariances.append(results.outputCovariance) 

259 

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

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

262 # affect the output comparison. 

263 outputCovariances[-1].rawMeans["C:0,0"] = np.array([np.nan]) 

264 outputCovariances[-1].rawVars["C:0,0"] = np.array([np.nan]) 

265 

266 # Force the next-to-last PTC dataset to have a decreased variance to 

267 # ensure that the outlier fit rejection works. 

268 rawVar = outputCovariances[-2].rawVars["C:0,0"] 

269 outputCovariances[-2].rawVars["C:0,0"] = rawVar * 0.9 

270 

271 # Reorganize the outputCovariances so we can confirm they come 

272 # out sorted afterwards. 

273 outputCovariancesRev = outputCovariances[::-1] 

274 

275 # Some expected values for noise matrix, just to check that 

276 # it was calculated. 

277 expectedNoiseMatrix = { 

278 "FULLCOVARIANCE": np.array( 

279 [[8.99474598, 9.94916264, -27.90587299], 

280 [-2.95079527, -17.11827641, -47.88156244], 

281 [5.24915021, -3.25786165, 26.09634067]], 

282 ), 

283 "FULLCOVARIANCE_NO_B": np.array( 

284 [[8.71049338, 12.48584043, -37.06585088], 

285 [-4.80523971, -23.29102809, -66.37815343], 

286 [7.48654766, -4.10168337, 35.64469824]], 

287 ), 

288 } 

289 

290 for fitType in ["FULLCOVARIANCE", "FULLCOVARIANCE_NO_B"]: 

291 solveConfig.ptcFitType = fitType 

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

293 

294 # Ensure no warnings re: read noise mismatches are logged. 

295 with self.assertNoLogs(level=logging.WARNING): 

296 resultsSolve = solveTask.run( 

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

298 ) 

299 

300 ptc = resultsSolve.outputPtcDataset 

301 

302 # Confirm that metadata keywords are set. 

303 for key in ["SEQFILE", "SEQNAME", "SEQCKSUM"]: 

304 self.assertEqual(ptc.metadata[key], expHandles[0].get().metadata[key]) 

305 self.assertEqual(ptc.metadata["INSTRUME"], FakeCamera().getName()) 

306 det = expHandles[0].get().getDetector() 

307 self.assertEqual(ptc.metadata["DETECTOR"], det.getId()) 

308 self.assertEqual(ptc.metadata["DET_NAME"], det.getName()) 

309 self.assertEqual(ptc.metadata["DET_SER"], det.getSerial()) 

310 

311 for amp in self.ampNames: 

312 self.assertAlmostEqual(ptc.gain[amp], inputGain, places=2) 

313 self.assertFloatsAlmostEqual( 

314 np.asarray(varStandard[amp])[ptc.expIdMask[amp]] / ptc.finalVars[amp][ptc.expIdMask[amp]], 

315 1.0, 

316 rtol=1e-4, 

317 ) 

318 np.testing.assert_array_equal(ptc.nPixelCovariances[amp], nPixelCovarianceStandard[amp][0]) 

319 

320 # Check that the PTC turnoff is correctly computed. 

321 # This will be different for the C:0,0 amp. 

322 if amp == "C:0,0": 

323 self.assertAlmostEqual(ptc.ptcTurnoff[amp], ptc.rawMeans[amp][-3]) 

324 else: 

325 self.assertAlmostEqual(ptc.ptcTurnoff[amp], ptc.rawMeans[amp][-1]) 

326 

327 # Test that all the quantities are correctly ordered and 

328 # have not accidentally been masked. 

329 for i, extractPtc in enumerate(outputCovariances): 

330 self.assertFloatsAlmostEqual( 

331 extractPtc.rawExpTimes[ampName][0], 

332 ptc.rawExpTimes[ampName][i], 

333 ) 

334 self.assertFloatsAlmostEqual( 

335 extractPtc.rawMeans[ampName][0], 

336 ptc.rawMeans[ampName][i], 

337 ) 

338 self.assertFloatsAlmostEqual( 

339 extractPtc.rawVars[ampName][0], 

340 ptc.rawVars[ampName][i], 

341 ) 

342 self.assertFloatsAlmostEqual( 

343 extractPtc.rawDeltas[ampName][0], 

344 ptc.rawDeltas[ampName][i], 

345 ) 

346 # Ensure these are set to a non-zero value. 

347 self.assertFloatsNotEqual( 

348 extractPtc.rawDeltas[ampName][0], 

349 0.0, 

350 ) 

351 self.assertFloatsAlmostEqual( 

352 extractPtc.photoCharges[ampName][0], 

353 ptc.photoCharges[ampName][i], 

354 ) 

355 self.assertFloatsAlmostEqual( 

356 extractPtc.photoChargeDeltas[ampName][0], 

357 ptc.photoChargeDeltas[ampName][i], 

358 ) 

359 # Ensure these are set to a non-zero value. 

360 self.assertFloatsNotEqual( 

361 extractPtc.photoChargeDeltas[ampName][0], 

362 0.0, 

363 ) 

364 self.assertFloatsAlmostEqual( 

365 extractPtc.histVars[ampName][0], 

366 ptc.histVars[ampName][i], 

367 ) 

368 self.assertFloatsAlmostEqual( 

369 extractPtc.histChi2Dofs[ampName][0], 

370 ptc.histChi2Dofs[ampName][i], 

371 ) 

372 self.assertFloatsAlmostEqual( 

373 extractPtc.kspValues[ampName][0], 

374 ptc.kspValues[ampName][i], 

375 ) 

376 self.assertFloatsAlmostEqual( 

377 extractPtc.covariances[ampName][0], 

378 ptc.covariances[ampName][i], 

379 ) 

380 self.assertFloatsAlmostEqual( 

381 extractPtc.covariancesSqrtWeights[ampName][0], 

382 ptc.covariancesSqrtWeights[ampName][i], 

383 ) 

384 self.assertFloatsAlmostEqual( 

385 ptc.noiseMatrix[ampName], 

386 expectedNoiseMatrix[fitType], 

387 atol=1e-8, 

388 rtol=None, 

389 ) 

390 self.assertFloatsAlmostEqual( 

391 ptc.ampOffsets[ampName], 

392 0.0, 

393 ) 

394 self.assertFloatsAlmostEqual( 

395 ptc.noise[ampName], 

396 np.nanmedian(ptc.noiseList[ampName]) * ptc.gain[ampName], 

397 rtol=0.05, 

398 ) 

399 # If the noise error is greater than the noise, 

400 # something is seriously wrong. Possibly some 

401 # kind of gain application mismatch. 

402 self.assertLess( 

403 ptc.noiseErr[ampName], 

404 ptc.noise[ampName], 

405 ) 

406 

407 mask = ptc.getGoodPoints(amp) 

408 

409 values = ( 

410 ptc.covariancesModel[amp][mask, 0, 0] - ptc.covariances[amp][mask, 0, 0] 

411 ) / ptc.covariancesModel[amp][mask, 0, 0] 

412 np.testing.assert_array_less(np.abs(values), 2e-3) 

413 

414 if ptc.ptcFitType == "FULLCOVARIANCE": 

415 values = ( 

416 ptc.covariancesModel[amp][mask, 0, 1] - ptc.covariances[amp][mask, 0, 1] 

417 ) / ptc.covariancesModel[amp][mask, 0, 1] 

418 np.testing.assert_array_less(np.abs(values), 0.3) 

419 

420 values = ( 

421 ptc.covariancesModel[amp][mask, 1, 0] - ptc.covariances[amp][mask, 1, 0] 

422 ) / ptc.covariancesModel[amp][mask, 1, 0] 

423 np.testing.assert_array_less(np.abs(values), 0.3) 

424 

425 # And test that the auxiliary values are there and 

426 # correctly ordered. 

427 self.assertIn('CCOBCURR', ptc.auxValues) 

428 self.assertIn('CCDTEMP', ptc.auxValues) 

429 firstExpIds = np.array([i for i, _ in ptc.inputExpIdPairs['C:0,0']], dtype=np.float64) 

430 self.assertFloatsAlmostEqual(ptc.auxValues['CCOBCURR'], firstExpIds) 

431 self.assertFloatsAlmostEqual(ptc.auxValues['CCDTEMP'], firstExpIds + 1) 

432 

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

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

435 # first two (low flux) and final four (outliers, nans) removed. 

436 self.assertTrue( 

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

438 ) 

439 

440 goodAmps = ptc.getGoodAmps() 

441 self.assertEqual(goodAmps, self.ampNames) 

442 

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

444 covShape = None 

445 covSqrtShape = None 

446 covModelShape = None 

447 

448 for ampName in self.ampNames: 

449 if covShape is None: 

450 covShape = ptc.covariances[ampName].shape 

451 covSqrtShape = ptc.covariancesSqrtWeights[ampName].shape 

452 covModelShape = ptc.covariancesModel[ampName].shape 

453 else: 

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

455 self.assertEqual( 

456 ptc.covariancesSqrtWeights[ampName].shape, covSqrtShape 

457 ) 

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

459 

460 # Check if evalPtcModel produces expected values 

461 nanMask = ~np.isnan(ptc.finalMeans[ampName]) 

462 means = ptc.finalMeans[ampName][nanMask] 

463 covModel = ptc.covariancesModel[ampName][nanMask] 

464 covariancesModel = ptc.evalPtcModel(means)[ampName] 

465 self.assertFloatsAlmostEqual(covariancesModel, covModel, atol=1e-12) 

466 

467 # And check that this is serializable 

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

469 usedFilename = ptc.writeFits(f.name) 

470 fromFits = PhotonTransferCurveDataset.readFits(usedFilename) 

471 self.assertEqual(fromFits, ptc) 

472 

473 def ptcFitAndCheckPtc( 

474 self, 

475 fitType=None, 

476 doFitBootstrap=False, 

477 ): 

478 localDataset = copy.deepcopy(self.dataset) 

479 localDataset.ptcFitType = fitType 

480 configSolve = copy.copy(self.defaultConfigSolve) 

481 if doFitBootstrap: 

482 configSolve.doFitBootstrap = True 

483 

484 if fitType == "EXPAPPROXIMATION": 

485 g = self.gain 

486 for ampName in self.ampNames: 

487 localDataset.rawVars[ampName] = [ 

488 ( 

489 0.5 / (self.a00 * g**2) * (np.exp(2 * self.a00 * mu * g) - 1) 

490 + self.noiseSq / (g * g) 

491 ) 

492 for mu in localDataset.rawMeans[ampName] 

493 ] 

494 else: 

495 raise RuntimeError( 

496 "Fit type must be 'EXPAPPROXIMATION'" 

497 ) 

498 

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

500 # Covariance weights values empirically determined from one of 

501 # the cases in test_covAstier. 

502 matrixSize = localDataset.covMatrixSide 

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

504 for ampName in self.ampNames: 

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

506 localDataset.covariancesSqrtWeights[ampName] = np.repeat( 

507 np.ones((matrixSize, matrixSize)), maskLength 

508 ).reshape((maskLength, matrixSize, matrixSize)) 

509 localDataset.covariancesSqrtWeights[ampName][:, 0, 0] = [ 

510 0.07980188, 

511 0.01339653, 

512 0.0073118, 

513 0.00502802, 

514 0.00383132, 

515 0.00309475, 

516 0.00259572, 

517 0.00223528, 

518 0.00196273, 

519 0.00174943, 

520 0.00157794, 

521 0.00143707, 

522 0.00131929, 

523 0.00121935, 

524 0.0011334, 

525 0.00105893, 

526 0.00099357, 

527 0.0009358, 

528 0.00088439, 

529 0.00083833, 

530 ] 

531 

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

533 

534 if fitType == "EXPAPPROXIMATION": 

535 localDataset = solveTask.fitPtc(localDataset) 

536 else: 

537 localDataset = solveTask.fitDataFullCovariance(localDataset) 

538 

539 # Check entries in localDataset, which was modified by the function 

540 for ampName in self.ampNames: 

541 self.assertEqual(fitType, localDataset.ptcFitType) 

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

543 if fitType == "EXPAPPROXIMATION": 

544 self.assertAlmostEqual( 

545 self.a00, localDataset.ptcFitPars[ampName][0] 

546 ) 

547 # Noise already in electrons 

548 self.assertAlmostEqual( 

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

550 ) 

551 # If the noise error is greater than the noise, something 

552 # is seriously wrong. Possibly some kind of gain application 

553 # mismatch. 

554 self.assertLess( 

555 localDataset.noiseErr[ampName], 

556 np.sqrt(self.noiseSq) 

557 ) 

558 

559 # Check if evalColModel produces expected values 

560 if not doFitBootstrap: 

561 nanMask = ~np.isnan(localDataset.finalMeans[ampName]) 

562 means = localDataset.finalMeans[ampName][nanMask] 

563 model = localDataset.finalModelVars[ampName][nanMask] 

564 evalVarModel = localDataset.evalPtcModel(means)[ampName] 

565 self.assertFloatsEqual(evalVarModel, model) 

566 

567 def test_lsstcam_samples(self): 

568 for dense in [False, True]: 

569 for mode in ["normal", "upturn", "dip"]: 

570 for doModelPtcRolloff in [False, True]: 

571 rawMeans, rawVars, ptcTurnoff = self._getSampleMeanAndVar(dense=dense, mode=mode) 

572 

573 # We only need a single amp. 

574 ampName = self.ampNames[0] 

575 dataset = PhotonTransferCurveDataset([ampName], ptcFitType="EXPAPPROXIMATION") 

576 dataset.rawExpTimes[ampName] = np.arange(len(rawMeans), dtype=np.float64) + 1.0 

577 dataset.rawMeans[ampName] = rawMeans 

578 dataset.rawVars[ampName] = rawVars 

579 dataset.covariancesSqrtWeights[ampName] = np.repeat( 

580 np.ones((dataset.covMatrixSide, dataset.covMatrixSide)), len(rawMeans) 

581 ).reshape((len(rawMeans), dataset.covMatrixSide, dataset.covMatrixSide)) 

582 dataset.covariancesSqrtWeights[ampName][:, 0, 0] = np.sqrt(rawVars) 

583 mask = np.ones(len(rawMeans), dtype=np.bool_) 

584 mask[~np.isfinite(rawMeans) | ~np.isfinite(rawVars)] = False 

585 dataset.expIdMask[ampName] = mask 

586 

587 configSolve = copy.copy(self.defaultConfigSolve) 

588 configSolve.doModelPtcRolloff = doModelPtcRolloff 

589 configSolve.doFitBootstrap = False 

590 

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

592 

593 initialFitDataset = solveTask.fitPtc(dataset, computePtcTurnoff=True) 

594 

595 # Model the PTC rolloff 

596 if doModelPtcRolloff: 

597 initialFitDataset = solveTask.fitPtcRolloff(initialFitDataset) 

598 

599 solvedDataset = solveTask.fitPtc(initialFitDataset, computePtcTurnoff=False) 

600 # Check that the ptcTurnoff is what is expected. 

601 self.assertFloatsAlmostEqual( 

602 solvedDataset.ptcTurnoff[ampName], 

603 ptcTurnoff, 

604 msg=( 

605 f"Dense: {dense}; Mode: {mode}, " 

606 f"doModelPtcRolloff: {doModelPtcRolloff}; Amp: {ampName}" 

607 ), 

608 ) 

609 

610 # Check that no values above the 

611 # turnoff/rolloff are "good". 

612 maxMean = solvedDataset.ptcTurnoff[ampName] 

613 if doModelPtcRolloff: 

614 # Check that the PTC rolloff is less 

615 # than the PTC turnoff and within 10% 

616 # of the turnoff. 

617 if dense: 

618 # Check that 

619 self.assertLess( 

620 solvedDataset.ptcRolloff[ampName], 

621 solvedDataset.ptcTurnoff[ampName], 

622 msg=( 

623 f"Dense: {dense}; Mode: {mode}, " 

624 f"doModelPtcRolloff: {doModelPtcRolloff}; " 

625 f"Amp: {ampName}" 

626 ), 

627 ) 

628 self.assertFloatsAlmostEqual( 

629 solvedDataset.ptcRolloff[ampName], 

630 solvedDataset.ptcTurnoff[ampName], 

631 atol=0.1 * solvedDataset.ptcTurnoff[ampName], 

632 msg=( 

633 f"Dense: {dense}; Mode: {mode}, " 

634 f"doModelPtcRolloff: {doModelPtcRolloff}; " 

635 f"Amp: {ampName}" 

636 ), 

637 ) 

638 else: 

639 # In all other cases, it should not be able to 

640 # find points beyond the turnoff, and the 

641 # rolloff should be set to the PTC turnoff. 

642 self.assertEqual( 

643 solvedDataset.ptcRolloff[ampName], 

644 solvedDataset.ptcTurnoff[ampName], 

645 msg=( 

646 f"Dense: {dense}; Mode: {mode}, " 

647 f"doModelPtcRolloff: {doModelPtcRolloff}; " 

648 f"Amp: {ampName}" 

649 ), 

650 ) 

651 

652 # If that passes set the maxMean to the PTC Rolloff 

653 maxMean = solvedDataset.ptcRolloff[ampName] 

654 

655 above = (solvedDataset.finalMeans[ampName] > maxMean) 

656 self.assertEqual( 

657 np.sum(above), 

658 0, 

659 msg=( 

660 f"Dense: {dense}; Mode: {mode}, " 

661 f"doModelPtcRolloff: {doModelPtcRolloff}; Amp: {ampName}" 

662 ), 

663 ) 

664 

665 # Check the sampling error on the turnoff. 

666 turnoffIdx = np.argwhere( 

667 solvedDataset.rawMeans[ampName] == solvedDataset.ptcTurnoff[ampName] 

668 ) 

669 samplingError = (rawMeans[turnoffIdx + 1] - rawMeans[turnoffIdx - 1])/2. 

670 self.assertFloatsAlmostEqual( 

671 solvedDataset.ptcTurnoffSamplingError[ampName], 

672 samplingError, 

673 msg=f"Dense: {dense}; Mode: {mode}", 

674 ) 

675 

676 def test_ptcFit(self): 

677 for fitType in ["EXPAPPROXIMATION"]: 

678 self.ptcFitAndCheckPtc( 

679 fitType=fitType 

680 ) 

681 

682 def test_meanVarMeasurement(self): 

683 task = self.defaultTaskExtract 

684 im1Area, im2Area, imStatsCtrl, mu1, mu2 = task.getImageAreasMasksStats( 

685 self.flatExp1, self.flatExp2 

686 ) 

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

688 

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

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

691 

692 def test_meanVarMeasurementWithNans(self): 

693 task = self.defaultTaskExtract 

694 

695 flatExp1 = self.flatExp1.clone() 

696 flatExp2 = self.flatExp2.clone() 

697 

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

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

700 

701 im1Area, im2Area, imStatsCtrl, mu1, mu2 = task.getImageAreasMasksStats( 

702 flatExp1, flatExp2 

703 ) 

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

705 

706 expectedMu1 = np.nanmean(flatExp1.image.array) 

707 expectedMu2 = np.nanmean(flatExp2.image.array) 

708 expectedMu = 0.5 * (expectedMu1 + expectedMu2) 

709 

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

711 im1 = flatExp1.maskedImage 

712 im2 = flatExp2.maskedImage 

713 

714 temp = im2.clone() 

715 temp *= expectedMu1 

716 diffIm = im1.clone() 

717 diffIm *= expectedMu2 

718 diffIm -= temp 

719 diffIm /= expectedMu 

720 

721 # Divide by two as it is what measureMeanVarCov returns 

722 # (variance of difference) 

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

724 

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

726 # less than 1 ADU 

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

728 self.assertLess(expectedMu - mu, 1) 

729 

730 def test_meanVarMeasurementAllNan(self): 

731 task = self.defaultTaskExtract 

732 flatExp1 = self.flatExp1.clone() 

733 flatExp2 = self.flatExp2.clone() 

734 

735 flatExp1.image.array[:, :] = np.nan 

736 flatExp2.image.array[:, :] = np.nan 

737 

738 im1Area, im2Area, imStatsCtrl, mu1, mu2 = task.getImageAreasMasksStats( 

739 flatExp1, flatExp2 

740 ) 

741 mu, varDiff, covDiff, rowMeanVariance = task.measureMeanVarCov( 

742 im1Area, im2Area, imStatsCtrl, mu1, mu2 

743 ) 

744 

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

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

747 self.assertTrue(covDiff is None) 

748 self.assertTrue(np.isnan(rowMeanVariance)) 

749 

750 def test_meanVarMeasurementTooFewPixels(self): 

751 task = self.defaultTaskExtract 

752 flatExp1 = self.flatExp1.clone() 

753 flatExp2 = self.flatExp2.clone() 

754 

755 flatExp1.image.array[0:190, :] = np.nan 

756 flatExp2.image.array[0:190, :] = np.nan 

757 

758 bit = flatExp1.mask.getMaskPlaneDict()["NO_DATA"] 

759 flatExp1.mask.array[0:190, :] &= 2**bit 

760 flatExp2.mask.array[0:190, :] &= 2**bit 

761 

762 im1Area, im2Area, imStatsCtrl, mu1, mu2 = task.getImageAreasMasksStats( 

763 flatExp1, flatExp2 

764 ) 

765 with self.assertLogs(level=logging.WARNING) as cm: 

766 mu, varDiff, covDiff, rowMeanVariance = task.measureMeanVarCov( 

767 im1Area, im2Area, imStatsCtrl, mu1, mu2 

768 ) 

769 self.assertIn("Number of good points", cm.output[0]) 

770 

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

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

773 self.assertTrue(covDiff is None) 

774 self.assertTrue(np.isnan(rowMeanVariance)) 

775 

776 def test_meanVarMeasurementTooNarrowStrip(self): 

777 # We need a new config to make sure the second covariance cut is 

778 # triggered. 

779 config = cpPipe.ptc.PhotonTransferCurveExtractTask.ConfigClass() 

780 config.minNumberGoodPixelsForCovariance = 10 

781 task = cpPipe.ptc.PhotonTransferCurveExtractTask(config=config) 

782 flatExp1 = self.flatExp1.clone() 

783 flatExp2 = self.flatExp2.clone() 

784 

785 flatExp1.image.array[0:195, :] = np.nan 

786 flatExp2.image.array[0:195, :] = np.nan 

787 flatExp1.image.array[:, 0:195] = np.nan 

788 flatExp2.image.array[:, 0:195] = np.nan 

789 

790 bit = flatExp1.mask.getMaskPlaneDict()["NO_DATA"] 

791 flatExp1.mask.array[0:195, :] &= 2**bit 

792 flatExp2.mask.array[0:195, :] &= 2**bit 

793 flatExp1.mask.array[:, 0:195] &= 2**bit 

794 flatExp2.mask.array[:, 0:195] &= 2**bit 

795 

796 im1Area, im2Area, imStatsCtrl, mu1, mu2 = task.getImageAreasMasksStats( 

797 flatExp1, flatExp2 

798 ) 

799 with self.assertLogs(level=logging.WARNING) as cm: 

800 mu, varDiff, covDiff, rowMeanVariance = task.measureMeanVarCov( 

801 im1Area, im2Area, imStatsCtrl, mu1, mu2 

802 ) 

803 self.assertIn("Not enough pixels", cm.output[0]) 

804 

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

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

807 self.assertTrue(covDiff is None) 

808 self.assertTrue(np.isnan(rowMeanVariance)) 

809 

810 def test_makeZeroSafe(self): 

811 noZerosArray = [1.0, 20, -35, 45578.98, 90.0, 897, 659.8] 

812 someZerosArray = [1.0, 20, 0, 0, 90, 879, 0] 

813 allZerosArray = [0.0, 0.0, 0, 0, 0.0, 0, 0] 

814 

815 substituteValue = 1e-10 

816 

817 expectedSomeZerosArray = [ 

818 1.0, 

819 20, 

820 substituteValue, 

821 substituteValue, 

822 90, 

823 879, 

824 substituteValue, 

825 ] 

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

827 

828 measuredSomeZerosArray = self.defaultTaskSolve._makeZeroSafe( 

829 someZerosArray, substituteValue=substituteValue 

830 ) 

831 measuredAllZerosArray = self.defaultTaskSolve._makeZeroSafe( 

832 allZerosArray, substituteValue=substituteValue 

833 ) 

834 measuredNoZerosArray = self.defaultTaskSolve._makeZeroSafe( 

835 noZerosArray, substituteValue=substituteValue 

836 ) 

837 

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

839 self.assertEqual(exp, meas) 

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

841 self.assertEqual(exp, meas) 

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

843 self.assertEqual(exp, meas) 

844 

845 def runGetGainFromFlatPair(self, correctionType="NONE"): 

846 extractConfig = self.defaultConfigExtract 

847 extractConfig.gainCorrectionType = correctionType 

848 extractConfig.minNumberGoodPixelsForCovariance = 5000 

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

850 

851 expDict = {} 

852 expIds = [] 

853 idCounter = 0 

854 inputGain = self.gain # 1.5 e/ADU 

855 for expTime in self.timeVec: 

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

857 mockExp1, mockExp2 = makeMockFlats( 

858 expTime, 

859 gain=inputGain, 

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

861 fluxElectrons=100, 

862 expId1=idCounter, 

863 expId2=idCounter + 1, 

864 ampNames=self.ampNames, 

865 ) 

866 mockExpRef1 = PretendRef(mockExp1) 

867 mockExpRef2 = PretendRef(mockExp2) 

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

869 expIds.append(idCounter) 

870 expIds.append(idCounter + 1) 

871 idCounter += 2 

872 

873 # This will test the full extract task. 

874 resultsExtract = extractTask.run( 

875 inputExp=expDict, 

876 inputDims=expIds, 

877 ) 

878 for exposurePair in resultsExtract.outputCovariances: 

879 for ampName in self.ampNames: 

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

881 continue 

882 self.assertAlmostEqual( 

883 exposurePair.gain[ampName], inputGain, delta=0.04 

884 ) 

885 

886 # Run through the solver and check that the gains from flat pairs 

887 # are recorded in the gainList. 

888 solveConfig = self.defaultConfigSolve 

889 solveConfig.ptcFitType = "EXPAPPROXIMATION" 

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

891 

892 resultsSolve = solveTask.run( 

893 resultsExtract.outputCovariances, 

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

895 ) 

896 ptc = resultsSolve.outputPtcDataset 

897 

898 for i in range(len(ptc.inputExpIdPairs)): 

899 for ampName in self.ampNames: 

900 if np.isnan(ptc.gainList[ampName][i]): 

901 continue 

902 self.assertAlmostEqual( 

903 ptc.gainList[ampName][i], inputGain, delta=0.04, 

904 ) 

905 self.assertAlmostEqual( 

906 ptc.noiseList[ampName][i], np.sqrt(self.noiseSq) / self.gain, 

907 ) 

908 

909 def test_getGainFromFlatPair(self): 

910 for gainCorrectionType in [ 

911 "NONE", 

912 "SIMPLE", 

913 "FULL", 

914 ]: 

915 self.runGetGainFromFlatPair(gainCorrectionType) 

916 

917 def test_ptcFitBootstrap(self): 

918 """Test the bootstrap fit option for the PTC""" 

919 for fitType in ['EXPAPPROXIMATION']: 

920 self.ptcFitAndCheckPtc(fitType=fitType, doFitBootstrap=True) 

921 

922 def test_ampOffsetGainRatioFixup(self): 

923 """Test the ampOffsetGainRatioFixup code via 

924 PhotonTransferCurveFixupGainRatiosTask.""" 

925 rng = np.random.RandomState(12345) 

926 

927 gainsTruth = rng.normal(loc=1.7, scale=0.05, size=len(self.ampNames)) 

928 gainsMedian = np.median(gainsTruth) 

929 

930 gainsMeasured = rng.normal(loc=gainsTruth, scale=0.02, size=len(self.ampNames)) 

931 

932 # Make one of the measured gains a much larger outlier to ensure 

933 # the code can handle a wacky measurement. 

934 weird_amp_index = 1 

935 gainsMeasured[weird_amp_index] = 1.2 

936 

937 # We have a perfectly flat illuminated detector (in electrons) 

938 nFlat = 20 

939 meansElectron = gainsMedian * np.linspace(500.0, 30000.0, nFlat) 

940 

941 # Set up the fixup task. 

942 fixupConfig = cpPipe.ptc.PhotonTransferCurveFixupGainRatiosConfig() 

943 fixupConfig.ampOffsetGainRatioMinAdu = 1000.0 

944 fixupConfig.ampOffsetGainRatioMaxAdu = 20000.0 

945 fixupTask = cpPipe.ptc.PhotonTransferCurveFixupGainRatiosTask(config=fixupConfig) 

946 

947 for testMode in ["full", "badamp"]: 

948 badAmp = None 

949 

950 ptc = PhotonTransferCurveDataset(self.ampNames, ptcFitType="FULLCOVARIANCE") 

951 

952 # Build the PTC to test amp offsets. 

953 for i, ampName in enumerate(self.ampNames): 

954 ptc.gain[ampName] = gainsMeasured[i] 

955 ptc.gainUnadjusted[ampName] = gainsMeasured[i] 

956 

957 if testMode == "badamp" and i == 3: 

958 ptc.gain[ampName] = np.nan 

959 ptc.gainUnadjusted[ampName] = np.nan 

960 badAmp = ampName 

961 

962 # The measured amp means are given by the true gains. 

963 ptc.finalMeans[ampName] = meansElectron / gainsTruth[i] 

964 ptc.expIdMask[ampName] = np.ones(nFlat, dtype=np.bool_) 

965 

966 # Fill the amp offsets temporarily. 

967 ptc.ampOffsets[ampName] = np.zeros(len(meansElectron)) 

968 

969 # Build each toy flat and measure the amp offsets. 

970 config = AmpOffsetTask.ConfigClass() 

971 config.ampEdgeMaxOffset = 100000.0 

972 config.ampEdgeWidth = 16 

973 config.ampEdgeInset = 10 

974 config.doBackground = False 

975 config.doDetection = False 

976 config.doApplyAmpOffset = False 

977 

978 ampOffset = AmpOffsetTask(config=config) 

979 

980 detector = self.flatExp1.getDetector() 

981 metadatas = [] 

982 for i in range(nFlat): 

983 exp = self.flatExp1.clone() 

984 for amp in detector: 

985 amp_name = amp.getName() 

986 exp[amp.getBBox()].image.array[:, :] = ptc.finalMeans[amp_name][i] 

987 md = exp.metadata 

988 

989 md[f"LSST ISR FINAL MEDIAN {amp_name}"] = ptc.finalMeans[amp_name][i] 

990 md[f"LSST ISR FINAL STDEV {amp_name}"] = np.sqrt(ptc.finalMeans[amp_name][i]) 

991 md[f"LSST ISR READNOISE {amp_name}"] = 0.0 

992 

993 ampOffset.run(exp) 

994 metadatas.append(exp.metadata) 

995 

996 result = fixupTask.run(inputPtc=ptc, exposureMetadata=metadatas) 

997 ptc = result.outputPtc 

998 

999 # Check that the flats are flat after adjustment. 

1000 for i in range(nFlat): 

1001 for j, amp in enumerate(detector): 

1002 gain = ptc.gain[amp.getName()] 

1003 exp[amp.getBBox()].image.array[:, :] = ptc.finalMeans[amp.getName()][i] * gain 

1004 

1005 testImage = exp.image.array / np.nanmedian(exp.image.array) 

1006 if testMode == "badamp": 

1007 # Force the bad values to 1.0 

1008 testImage[~np.isfinite(testImage)] = 1.0 

1009 

1010 self.assertFloatsAlmostEqual(testImage.ravel(), 1.0, rtol=1e-6) 

1011 

1012 # Confirm that the median gain is reasonably unchanged. 

1013 gainsAdjusted = np.array([ptc.gain[ampName] for ampName in self.ampNames]) 

1014 self.assertFloatsAlmostEqual(np.nanmedian(gainsAdjusted), np.nanmedian(gainsMeasured), rtol=0.007) 

1015 

1016 # Confirm that the median of the corrections is 1.0 

1017 gainCorrections = np.array([ptc.gain[ampName]/ptc.gainUnadjusted[ampName] 

1018 for ampName in self.ampNames]) 

1019 self.assertFloatsAlmostEqual(np.nanmedian(gainCorrections), 1.0, rtol=1e-7) 

1020 

1021 # Check that the gain ratios are matched as expected. 

1022 for i, ampName1 in enumerate(self.ampNames): 

1023 for j, ampName2 in enumerate(self.ampNames): 

1024 ratioTruth = gainsTruth[i] / gainsTruth[j] 

1025 ratio = ptc.gain[ampName1] / ptc.gain[ampName2] 

1026 

1027 if ampName1 != badAmp and ampName2 != badAmp: 

1028 self.assertFloatsAlmostEqual(ratio, ratioTruth, rtol=1e-7) 

1029 

1030 # Check that everything is logged correctly when things are very bad. 

1031 for ampName in self.ampNames[2:]: 

1032 ptc.ampOffsets[ampName][:] = np.nan 

1033 

1034 with self.assertLogs(level=logging.WARNING) as cm: 

1035 ampOffsetGainRatioFixup(ptc, 1000.0, 20000.0) 

1036 self.assertIn("Not enough good amp offset measurements", cm.output[0]) 

1037 

1038 for ampName in self.ampNames[1:]: 

1039 ptc.gain[ampName] = np.nan 

1040 

1041 with self.assertLogs(level=logging.WARNING) as cm: 

1042 ampOffsetGainRatioFixup(ptc, 1000.0, 20000.0) 

1043 self.assertIn("Cannot apply ampOffsetGainRatioFixup", cm.output[0]) 

1044 

1045 def test_maskVignetteFunctionRegion(self): 

1046 # The function coefficients are chosen to trigger on some amps. 

1047 # The amps with no masked pixels are C:1,0 and C:1,3 due to the 

1048 # curvature. 

1049 extractConfig = self.defaultConfigExtract 

1050 extractConfig.doVignetteFunctionRegionSelection = True 

1051 extractConfig.vignetteFunctionPolynomialCoeffs = [-100.0, -18.0, 1.0] 

1052 extractConfig.vignetteFunctionRegionSelectionMinimumPixels = 2_000 

1053 

1054 task = cpPipe.ptc.PhotonTransferCurveExtractPairTask(config=extractConfig) 

1055 exposures = [self.flatExp1, self.flatExp2] 

1056 with self.assertNoLogs(level=logging.WARNING): 

1057 task._maskVignetteFunctionRegion(exposures) 

1058 

1059 bitmask = self.flatExp1.mask.getPlaneBitMask("SUSPECT") 

1060 for amp in self.flatExp1.getDetector(): 

1061 bbox = amp.getBBox() 

1062 nMasked = ((self.flatExp1.mask[bbox].array & bitmask) > 0).sum() 

1063 if amp.getName() not in ["C:1,0", "C:1,3"]: 

1064 self.assertGreater(nMasked, 0, msg=f"Missing masked pixels in {amp.getName()}") 

1065 else: 

1066 self.assertEqual(nMasked, 0, msg=f"Unexpected masked pixels in {amp.getName()}") 

1067 

1068 # Check what happens if we ask for too many pixels. 

1069 extractConfig = self.defaultConfigExtract 

1070 extractConfig.doVignetteFunctionRegionSelection = True 

1071 extractConfig.vignetteFunctionPolynomialCoeffs = [-100.0, -18.0, 1.0] 

1072 extractConfig.vignetteFunctionRegionSelectionMinimumPixels = 10_000 

1073 

1074 self.flatExp1.mask.array[:, :] = 0 

1075 self.flatExp2.mask.array[:, :] = 0 

1076 task = cpPipe.ptc.PhotonTransferCurveExtractPairTask(config=extractConfig) 

1077 exposures = [self.flatExp1, self.flatExp2] 

1078 with self.assertLogs(level=logging.WARNING): 

1079 task._maskVignetteFunctionRegion(exposures) 

1080 

1081 # This should not have any masked pixels because we essentially 

1082 # insisted that the full amp be selected. 

1083 for amp in self.flatExp1.getDetector(): 

1084 self.assertEqual(nMasked, 0, msg=f"Unexpected masked pixels in {amp.getName()}") 

1085 

1086 def _getSampleMeanAndVar(self, dense=False, mode="normal"): 

1087 """Get sample mean/var vectors and ptcTurnoff from LSSTCam data. 

1088 

1089 The ptcTurnoff values here were obtained by looking at the data 

1090 and what the code does. These may be changed in the future if 

1091 we decide on a different way of computing the turnoff. 

1092 

1093 Parameters 

1094 ---------- 

1095 dense : `bool`, optional 

1096 Return a dense PTC? 

1097 mode : `str`, optional 

1098 The ptc type, should be "normal", "upturn", or "dip". 

1099 

1100 Returns 

1101 ------- 

1102 rawMeans : `np.ndarray` 

1103 Array of raw mean values. 

1104 rawVars : `np.ndarray` 

1105 Array of raw variance values. 

1106 ptcTurnoff : `float` 

1107 PTC turnoff determined from fitting by eye. 

1108 """ 

1109 if dense and mode == "normal": 

1110 # Taken from dense run 13591, detector 94, amplifier C02 

1111 ptcTurnoff = 92239.4794 

1112 rawMeans = np.array([ 

1113 3.72806883e+01, 3.86850679e+01, 4.09319941e+01, 4.30288886e+01, 

1114 4.59546561e+01, 4.77510985e+01, 5.06130505e+01, 5.35060747e+01, 

1115 5.61016577e+01, 5.95244630e+01, 6.21722799e+01, 6.59958508e+01, 

1116 7.32513785e+01, 7.91695151e+01, 8.19214651e+01, 8.66175502e+01, 

1117 9.06656207e+01, 9.57917501e+01, 1.00925700e+02, 1.07661026e+02, 

1118 1.12211207e+02, 1.18038278e+02, 1.24776787e+02, 1.33127394e+02, 

1119 1.39086972e+02, 1.47962442e+02, 1.55007859e+02, 1.64972119e+02, 

1120 1.72224552e+02, 1.81663660e+02, 1.92083706e+02, 2.01818483e+02, 

1121 2.12893098e+02, 2.25865898e+02, 2.36777769e+02, 2.50805751e+02, 

1122 2.63582968e+02, 2.78848126e+02, 2.93430577e+02, 3.10974538e+02, 

1123 3.28617662e+02, 3.44447122e+02, 3.63779087e+02, 3.85447934e+02, 

1124 4.04865571e+02, 4.26619625e+02, 4.50642349e+02, 4.75572605e+02, 

1125 5.00924590e+02, 5.29463072e+02, 5.56894775e+02, 5.89400667e+02, 

1126 6.22194672e+02, 6.55411968e+02, 6.90233178e+02, 7.28617776e+02, 

1127 7.70386654e+02, 8.11275732e+02, 8.56899058e+02, 9.04075885e+02, 

1128 9.53041280e+02, 1.00428165e+03, 1.05984066e+03, 1.11936964e+03, 

1129 1.17993499e+03, 1.24493657e+03, 1.31333429e+03, 1.38477571e+03, 

1130 1.46288220e+03, 1.54131684e+03, 1.62718454e+03, 1.71685496e+03, 

1131 1.91104434e+03, 1.91128408e+03, 2.01496942e+03, 2.01510504e+03, 

1132 2.12643136e+03, 2.12665024e+03, 2.24351981e+03, 2.36534141e+03, 

1133 2.49658670e+03, 2.77859948e+03, 2.92932915e+03, 3.21897902e+03, 

1134 3.39732732e+03, 3.58413491e+03, 3.78057120e+03, 3.98731229e+03, 

1135 4.20691253e+03, 4.43947873e+03, 4.68353906e+03, 4.94068272e+03, 

1136 5.20957520e+03, 5.21201592e+03, 5.49832249e+03, 5.80143284e+03, 

1137 6.12091976e+03, 6.81333865e+03, 7.18560833e+03, 7.76381518e+03, 

1138 8.33998116e+03, 8.91777512e+03, 9.49514657e+03, 1.00697547e+04, 

1139 1.06495655e+04, 1.12197718e+04, 1.18024551e+04, 1.23803888e+04, 

1140 1.29546730e+04, 1.35302383e+04, 1.41095292e+04, 1.46864242e+04, 

1141 1.52554127e+04, 1.58424382e+04, 1.64174609e+04, 1.69934436e+04, 

1142 1.75654540e+04, 1.81523967e+04, 1.87298339e+04, 1.93014337e+04, 

1143 2.04450305e+04, 2.10377447e+04, 2.16102067e+04, 2.16123196e+04, 

1144 2.21851791e+04, 2.21921696e+04, 2.33433082e+04, 2.39171545e+04, 

1145 2.44958327e+04, 2.50717007e+04, 2.56486425e+04, 2.62185599e+04, 

1146 2.68048566e+04, 2.73814219e+04, 2.79519282e+04, 2.85325879e+04, 

1147 2.91077169e+04, 2.96844973e+04, 3.02554411e+04, 3.14125545e+04, 

1148 3.25683638e+04, 3.31324135e+04, 3.33243063e+04, 3.38868072e+04, 

1149 3.44574798e+04, 3.50295131e+04, 3.55959947e+04, 3.61681182e+04, 

1150 3.67288051e+04, 3.73019527e+04, 3.78659087e+04, 3.84175071e+04, 

1151 3.90035052e+04, 3.95715617e+04, 4.06979100e+04, 4.12778387e+04, 

1152 4.18444142e+04, 4.24067921e+04, 4.29743477e+04, 4.35479754e+04, 

1153 4.41086362e+04, 4.46748906e+04, 4.57912581e+04, 4.63620286e+04, 

1154 4.69293084e+04, 4.80512660e+04, 4.86149041e+04, 4.91798146e+04, 

1155 5.03083769e+04, 5.08678825e+04, 5.14329108e+04, 5.20101245e+04, 

1156 5.25674618e+04, 5.31040686e+04, 5.36908774e+04, 5.42697605e+04, 

1157 5.47843148e+04, 5.53743324e+04, 5.59452125e+04, 5.70753786e+04, 

1158 5.76356662e+04, 5.81990397e+04, 5.87644952e+04, 5.98894309e+04, 

1159 6.04630169e+04, 6.10270338e+04, 6.15960865e+04, 6.21427559e+04, 

1160 6.27197146e+04, 6.32781202e+04, 6.38170695e+04, 6.44264810e+04, 

1161 6.49720024e+04, 6.55524996e+04, 6.66814853e+04, 6.72313467e+04, 

1162 6.78274362e+04, 6.83859289e+04, 6.89427312e+04, 6.95198678e+04, 

1163 7.00847030e+04, 7.06620283e+04, 7.12327432e+04, 7.17769841e+04, 

1164 7.23610736e+04, 7.29237272e+04, 7.34938353e+04, 7.40400864e+04, 

1165 7.46399787e+04, 7.52085787e+04, 7.57175033e+04, 7.63346271e+04, 

1166 7.69141477e+04, 7.74690838e+04, 7.80403707e+04, 7.85672045e+04, 

1167 7.91728742e+04, 7.97519543e+04, 8.03063026e+04, 8.08869068e+04, 

1168 8.14577271e+04, 8.20105730e+04, 8.25824739e+04, 8.31490218e+04, 

1169 8.37308110e+04, 8.42758180e+04, 8.48493221e+04, 8.54126656e+04, 

1170 8.59826959e+04, 8.65501659e+04, 8.70696403e+04, 8.76932979e+04, 

1171 8.82464074e+04, 8.93795020e+04, 8.99537321e+04, 9.05097100e+04, 

1172 9.10881246e+04, 9.22394794e+04, 9.27870984e+04, 9.33315296e+04, 

1173 9.39230922e+04, 9.44926266e+04, 9.56192429e+04, 9.61483057e+04, 

1174 9.72621754e+04, 9.78325850e+04, 9.83972793e+04, 9.89552969e+04, 

1175 9.94332509e+04, 1.00047506e+05, 1.00569417e+05, 1.01105830e+05, 

1176 1.01634936e+05, 1.02121705e+05, 1.02641415e+05, 1.03123767e+05, 

1177 1.03599733e+05, 1.04062370e+05, 1.04907216e+05, 1.05256436e+05, 

1178 1.05665701e+05, 1.06007083e+05, 1.06318115e+05, 1.06593953e+05, 

1179 1.06893542e+05, 1.07121150e+05, 1.07346243e+05, 1.07625934e+05, 

1180 1.08044429e+05, 1.08167080e+05, 1.08308995e+05, 1.08399830e+05, 

1181 1.08526582e+05, 1.08620505e+05, 1.08713803e+05, 1.08784455e+05, 

1182 1.08862350e+05, 1.08937801e+05, 1.08989964e+05, 1.09060085e+05, 

1183 1.09162026e+05, 1.09220593e+05, 1.09273416e+05, 1.09333464e+05, 

1184 1.09372863e+05, 1.09468530e+05, 1.09513735e+05, 1.09553322e+05, 

1185 1.09594566e+05, 1.09641845e+05, 1.09679236e+05, 1.09723860e+05, 

1186 1.09753056e+05, 1.09826579e+05, 1.09872135e+05, 1.09895670e+05, 

1187 1.09902080e+05, 1.09943174e+05, 1.10002465e+05, 1.10019329e+05, 

1188 1.10068730e+05, 1.10095848e+05, 1.10145789e+05, 1.10193658e+05, 

1189 1.10213581e+05, 1.10248075e+05, 1.10291320e+05, 1.10316883e+05, 

1190 1.10358975e+05, 1.10350689e+05, 1.10391552e+05, 1.10400507e+05, 

1191 1.10409523e+05, 1.10409459e+05, 1.10412119e+05, 1.10409689e+05, 

1192 1.10414648e+05, 1.10391061e+05, 1.10420513e+05, 1.10424437e+05, 

1193 1.10435588e+05, 1.10438362e+05, 1.10443905e+05, 1.10450180e+05, 

1194 1.10452714e+05, 1.10449100e+05, 1.10466222e+05, 1.10458324e+05, 

1195 1.10478871e+05, 1.10486620e+05, 1.10485105e+05, 1.10489515e+05, 

1196 1.10491243e+05, 1.10499598e+05, 1.10501449e+05, 1.10501009e+05, 

1197 1.10515083e+05, 1.10533044e+05, 1.10531740e+05, 1.10548636e+05, 

1198 1.10558815e+05, 1.10560570e+05, 1.10569349e+05, 1.10572778e+05, 

1199 1.10588800e+05, 1.10593287e+05, 1.10596037e+05, 1.10584233e+05, 

1200 1.10611084e+05, 1.10616525e+05, 1.10621289e+05, 1.10631455e+05, 

1201 1.10639320e+05, 1.10647906e+05, 1.10646043e+05, 1.10635789e+05, 

1202 1.10635305e+05, 1.10652093e+05, 1.10638755e+05, 1.10655426e+05, 

1203 1.10651182e+05, 1.10653604e+05, 1.10665648e+05, 1.10655049e+05, 

1204 1.10650105e+05, 1.10655658e+05, 1.10660937e+05, 1.10673337e+05, 

1205 1.10673003e+05, 1.10655815e+05, 1.10661151e+05, 1.10662036e+05, 

1206 1.10676951e+05, 1.10667334e+05, 1.10679642e+05, 1.10678497e+05]) 

1207 rawVars = np.array([ 

1208 4.47787277e+01, 4.64353847e+01, 4.85247580e+01, 4.93948408e+01, 

1209 5.15948411e+01, 5.30389406e+01, 5.58813000e+01, 5.88525810e+01, 

1210 6.11585484e+01, 6.41104167e+01, 6.56152371e+01, 6.82093774e+01, 

1211 7.14063291e+01, 7.45066055e+01, 7.64755940e+01, 7.93540481e+01, 

1212 8.18907727e+01, 8.66402959e+01, 8.97887750e+01, 9.68782459e+01, 

1213 9.95221383e+01, 1.02767714e+02, 1.08068716e+02, 1.16108758e+02, 

1214 1.20246097e+02, 1.26018524e+02, 1.30397686e+02, 1.36138570e+02, 

1215 1.41942986e+02, 1.46772865e+02, 1.53919845e+02, 1.60521709e+02, 

1216 1.68353264e+02, 1.78909843e+02, 1.88788533e+02, 2.00792706e+02, 

1217 2.07611019e+02, 2.16057203e+02, 2.26987335e+02, 2.39701806e+02, 

1218 2.51287870e+02, 2.64963149e+02, 2.81918185e+02, 2.93010615e+02, 

1219 3.04772726e+02, 3.21723049e+02, 3.43194608e+02, 3.60791523e+02, 

1220 3.72312673e+02, 3.92534946e+02, 4.18291901e+02, 4.40825475e+02, 

1221 4.58818537e+02, 4.88340078e+02, 5.12852998e+02, 5.35522179e+02, 

1222 5.69935148e+02, 5.92975103e+02, 6.31052929e+02, 6.61056947e+02, 

1223 6.95541286e+02, 7.33787875e+02, 7.74291674e+02, 8.19211863e+02, 

1224 8.62765375e+02, 8.98546352e+02, 9.55344080e+02, 1.00237170e+03, 

1225 1.05926161e+03, 1.10844186e+03, 1.17604012e+03, 1.24027865e+03, 

1226 1.37334609e+03, 1.37631570e+03, 1.44433952e+03, 1.44613237e+03, 

1227 1.53642103e+03, 1.53144123e+03, 1.60103165e+03, 1.69486912e+03, 

1228 1.79228744e+03, 1.97807118e+03, 2.08913779e+03, 2.29127569e+03, 

1229 2.41035376e+03, 2.54559185e+03, 2.68285671e+03, 2.82061302e+03, 

1230 2.98565936e+03, 3.13680879e+03, 3.29910967e+03, 3.48157975e+03, 

1231 3.66147828e+03, 3.67694292e+03, 3.84787540e+03, 4.07925681e+03, 

1232 4.28699288e+03, 4.75578456e+03, 5.00956756e+03, 5.39321737e+03, 

1233 5.79686635e+03, 6.17477624e+03, 6.55132737e+03, 6.93000705e+03, 

1234 7.31757235e+03, 7.68650298e+03, 8.05377657e+03, 8.42789583e+03, 

1235 8.77475510e+03, 9.17341164e+03, 9.54927300e+03, 9.89596158e+03, 

1236 1.02637150e+04, 1.06167914e+04, 1.09873368e+04, 1.13685550e+04, 

1237 1.17026479e+04, 1.20765605e+04, 1.23946750e+04, 1.27577017e+04, 

1238 1.34485509e+04, 1.37927193e+04, 1.42006355e+04, 1.41857628e+04, 

1239 1.44769908e+04, 1.45162172e+04, 1.51614067e+04, 1.55571196e+04, 

1240 1.58809609e+04, 1.61752079e+04, 1.65104318e+04, 1.68369000e+04, 

1241 1.71408213e+04, 1.75355600e+04, 1.78226398e+04, 1.81932474e+04, 

1242 1.84482292e+04, 1.88389901e+04, 1.91150651e+04, 1.97375210e+04, 

1243 2.03591057e+04, 2.07155451e+04, 2.07138350e+04, 2.11073361e+04, 

1244 2.13837002e+04, 2.16588926e+04, 2.19612146e+04, 2.22753789e+04, 

1245 2.25303517e+04, 2.27928117e+04, 2.31125410e+04, 2.33679689e+04, 

1246 2.37393625e+04, 2.40488975e+04, 2.45764011e+04, 2.48064050e+04, 

1247 2.50721617e+04, 2.53965873e+04, 2.56131331e+04, 2.59277943e+04, 

1248 2.61709392e+04, 2.64119896e+04, 2.68955682e+04, 2.72267063e+04, 

1249 2.74887295e+04, 2.79083033e+04, 2.82310485e+04, 2.84366770e+04, 

1250 2.90226269e+04, 2.92324828e+04, 2.94521967e+04, 2.97949458e+04, 

1251 2.99735234e+04, 3.02749574e+04, 3.05427850e+04, 3.07518546e+04, 

1252 3.09317879e+04, 3.12692332e+04, 3.15643550e+04, 3.21293639e+04, 

1253 3.24007437e+04, 3.26147619e+04, 3.28979579e+04, 3.34807881e+04, 

1254 3.37109710e+04, 3.40246283e+04, 3.43898469e+04, 3.45302267e+04, 

1255 3.47818944e+04, 3.51593681e+04, 3.52477656e+04, 3.56490287e+04, 

1256 3.59240161e+04, 3.61669226e+04, 3.66423677e+04, 3.68827117e+04, 

1257 3.71622239e+04, 3.74768274e+04, 3.76872328e+04, 3.79405515e+04, 

1258 3.81364527e+04, 3.84119822e+04, 3.86423009e+04, 3.88877574e+04, 

1259 3.91248488e+04, 3.92990610e+04, 3.96215459e+04, 3.98297091e+04, 

1260 4.00712366e+04, 4.04322148e+04, 4.05178037e+04, 4.07431736e+04, 

1261 4.10193223e+04, 4.11740695e+04, 4.13790755e+04, 4.14632263e+04, 

1262 4.18525778e+04, 4.20979322e+04, 4.23130642e+04, 4.24582022e+04, 

1263 4.24946930e+04, 4.27860167e+04, 4.30933820e+04, 4.32894483e+04, 

1264 4.33858206e+04, 4.34411628e+04, 4.36629531e+04, 4.39919091e+04, 

1265 4.40293360e+04, 4.42002784e+04, 4.42310862e+04, 4.45443328e+04, 

1266 4.45481958e+04, 4.48604145e+04, 4.48891948e+04, 4.48416001e+04, 

1267 4.50553039e+04, 4.50927543e+04, 4.51136486e+04, 4.51126520e+04, 

1268 4.49857031e+04, 4.48705631e+04, 4.46276191e+04, 4.43327388e+04, 

1269 4.37938194e+04, 4.32211550e+04, 4.27332194e+04, 4.23177306e+04, 

1270 4.14539982e+04, 4.06974694e+04, 3.96551098e+04, 3.82440396e+04, 

1271 3.67165456e+04, 3.47661502e+04, 3.28393493e+04, 3.02395522e+04, 

1272 2.76939927e+04, 2.48272541e+04, 1.92526286e+04, 1.63986811e+04, 

1273 1.42095886e+04, 1.22350406e+04, 1.06293553e+04, 9.24838723e+03, 

1274 8.09429377e+03, 7.27582028e+03, 6.47441127e+03, 5.21985266e+03, 

1275 4.36279092e+03, 4.00953782e+03, 3.69585190e+03, 3.43405611e+03, 

1276 3.18567804e+03, 3.01195379e+03, 2.89123841e+03, 2.68282359e+03, 

1277 2.60091022e+03, 2.51301062e+03, 2.43890788e+03, 2.35838231e+03, 

1278 2.26256855e+03, 2.21204078e+03, 2.20282902e+03, 2.18205860e+03, 

1279 2.15007675e+03, 2.08949483e+03, 2.10732780e+03, 2.09600072e+03, 

1280 2.07407783e+03, 2.11741997e+03, 2.08083045e+03, 2.06397688e+03, 

1281 2.04765529e+03, 2.05836012e+03, 2.04093663e+03, 1.98334647e+03, 

1282 2.01999612e+03, 1.99394914e+03, 1.96972450e+03, 1.88216817e+03, 

1283 1.86902307e+03, 1.98290800e+03, 1.94714119e+03, 1.92046621e+03, 

1284 1.83796316e+03, 1.87468459e+03, 1.89491779e+03, 1.83110571e+03, 

1285 1.75025929e+03, 1.66067524e+03, 1.68052010e+03, 1.67404206e+03, 

1286 1.65221787e+03, 1.63380745e+03, 1.61258211e+03, 1.57173282e+03, 

1287 1.55635385e+03, 1.59914440e+03, 1.63280915e+03, 1.62416209e+03, 

1288 1.55550327e+03, 1.59790786e+03, 1.63913552e+03, 1.62123248e+03, 

1289 1.59571147e+03, 1.48508783e+03, 1.60763833e+03, 1.60573267e+03, 

1290 1.66286509e+03, 1.56023986e+03, 1.60226461e+03, 1.62178972e+03, 

1291 1.63248481e+03, 1.63982132e+03, 1.67350316e+03, 1.62120745e+03, 

1292 1.69261497e+03, 1.67835970e+03, 1.71259617e+03, 1.66567572e+03, 

1293 1.73107044e+03, 1.60808418e+03, 1.62957233e+03, 1.65949976e+03, 

1294 1.62773379e+03, 1.74438786e+03, 1.61948665e+03, 1.83335873e+03, 

1295 1.65774501e+03, 1.65605436e+03, 1.72054542e+03, 1.68968657e+03, 

1296 1.73787963e+03, 1.74437326e+03, 1.71408015e+03, 1.93871967e+03, 

1297 1.87508731e+03, 1.70631906e+03, 1.92219749e+03, 1.72410949e+03, 

1298 1.86172638e+03, 1.88692937e+03, 1.77337717e+03, 1.95303579e+03, 

1299 1.86292107e+03, 1.87948406e+03, 1.91966097e+03, 1.76238101e+03, 

1300 1.59694869e+03, 1.89306746e+03, 1.84068186e+03, 1.86018512e+03, 

1301 1.81578862e+03, 1.83413719e+03, 1.80771751e+03, 1.79214601e+03]) 

1302 elif dense and mode == "upturn": 

1303 # Taken from dense run 13591, detector 73, amplifier C07 

1304 ptcTurnoff = 86917.0045 

1305 rawMeans = np.array([ 

1306 2.87448603e+01, 3.03861239e+01, 3.20301466e+01, 3.35831624e+01, 

1307 3.51161960e+01, 3.69857753e+01, 3.91580507e+01, 4.12179225e+01, 

1308 4.36662086e+01, 4.58399375e+01, 4.80202775e+01, 5.10071275e+01, 

1309 5.68263968e+01, 5.99352524e+01, 6.34210182e+01, 6.68834309e+01, 

1310 7.01195882e+01, 7.43313750e+01, 7.80758357e+01, 8.27309929e+01, 

1311 8.73124933e+01, 9.17856382e+01, 9.70086101e+01, 1.02546128e+02, 

1312 1.08169091e+02, 1.13738150e+02, 1.20058621e+02, 1.26792703e+02, 

1313 1.33422104e+02, 1.40893068e+02, 1.48727555e+02, 1.56662520e+02, 

1314 1.65129261e+02, 1.74130948e+02, 1.83878223e+02, 1.93976598e+02, 

1315 2.04599993e+02, 2.16044601e+02, 2.27838642e+02, 2.40360862e+02, 

1316 2.53772624e+02, 2.67443055e+02, 2.82013487e+02, 2.97705423e+02, 

1317 3.14297001e+02, 3.31260825e+02, 3.49758342e+02, 3.68963998e+02, 

1318 3.88634613e+02, 4.10112249e+02, 4.32301833e+02, 4.56407219e+02, 

1319 4.81690100e+02, 5.08370293e+02, 5.35921852e+02, 5.65828928e+02, 

1320 5.96685398e+02, 6.29615188e+02, 6.64802708e+02, 7.00563142e+02, 

1321 7.39711995e+02, 7.79679559e+02, 8.22530629e+02, 8.67814522e+02, 

1322 9.15951880e+02, 9.65946953e+02, 1.01948611e+03, 1.07504268e+03, 

1323 1.13509200e+03, 1.19727942e+03, 1.26351949e+03, 1.33211624e+03, 

1324 1.48320209e+03, 1.48384430e+03, 1.56467822e+03, 1.56485206e+03, 

1325 1.65060918e+03, 1.65147654e+03, 1.74229201e+03, 1.83706229e+03, 

1326 1.93811946e+03, 2.15719262e+03, 2.27546155e+03, 2.50056748e+03, 

1327 2.63872700e+03, 2.78379012e+03, 2.93745238e+03, 3.09833186e+03, 

1328 3.27013288e+03, 3.44990418e+03, 3.63968250e+03, 3.83921968e+03, 

1329 4.04711266e+03, 4.05013387e+03, 4.27266981e+03, 4.50809670e+03, 

1330 4.75635569e+03, 5.29310771e+03, 5.58427306e+03, 6.03306708e+03, 

1331 6.48160541e+03, 6.92983470e+03, 7.37874615e+03, 7.82619855e+03, 

1332 8.27493037e+03, 8.72049208e+03, 9.17227788e+03, 9.62234260e+03, 

1333 1.00674503e+04, 1.05166951e+04, 1.09652006e+04, 1.14145457e+04, 

1334 1.18581337e+04, 1.23138210e+04, 1.27597933e+04, 1.32094995e+04, 

1335 1.36537794e+04, 1.41079990e+04, 1.45579666e+04, 1.50042564e+04, 

1336 1.58932106e+04, 1.63527856e+04, 1.67995854e+04, 1.68014549e+04, 

1337 1.72456751e+04, 1.72511595e+04, 1.81472850e+04, 1.85967925e+04, 

1338 1.90437339e+04, 1.94933795e+04, 1.99423590e+04, 2.03854630e+04, 

1339 2.08408610e+04, 2.12909264e+04, 2.17356857e+04, 2.21873924e+04, 

1340 2.26346682e+04, 2.30822802e+04, 2.35248689e+04, 2.44292445e+04, 

1341 2.53306721e+04, 2.57709779e+04, 2.59292740e+04, 2.63672474e+04, 

1342 2.68118979e+04, 2.72586202e+04, 2.76977437e+04, 2.81455545e+04, 

1343 2.85840092e+04, 2.90309073e+04, 2.94738200e+04, 2.99007273e+04, 

1344 3.03581316e+04, 3.08035397e+04, 3.16820715e+04, 3.21348581e+04, 

1345 3.25795035e+04, 3.30160938e+04, 3.34614077e+04, 3.39098464e+04, 

1346 3.43488841e+04, 3.47938018e+04, 3.56683528e+04, 3.61120617e+04, 

1347 3.65571740e+04, 3.74373798e+04, 3.78795706e+04, 3.83213509e+04, 

1348 3.92106205e+04, 3.96436191e+04, 4.00888939e+04, 4.05395713e+04, 

1349 4.09729696e+04, 4.13953138e+04, 4.18558141e+04, 4.23098575e+04, 

1350 4.27184098e+04, 4.31754813e+04, 4.36191519e+04, 4.45068672e+04, 

1351 4.49463419e+04, 4.53858299e+04, 4.58261673e+04, 4.67014085e+04, 

1352 4.71515103e+04, 4.75882657e+04, 4.80322253e+04, 4.84586953e+04, 

1353 4.89039488e+04, 4.93442503e+04, 4.97561230e+04, 5.02341725e+04, 

1354 5.06588581e+04, 5.11035621e+04, 5.19836216e+04, 5.24056394e+04, 

1355 5.28686518e+04, 5.33018757e+04, 5.37265598e+04, 5.41807663e+04, 

1356 5.46136176e+04, 5.50609130e+04, 5.55007300e+04, 5.59337737e+04, 

1357 5.63788843e+04, 5.68094757e+04, 5.72570533e+04, 5.76741866e+04, 

1358 5.81383020e+04, 5.85826153e+04, 5.89753856e+04, 5.94617576e+04, 

1359 5.99083532e+04, 6.03419978e+04, 6.07815876e+04, 6.11916990e+04, 

1360 6.16679074e+04, 6.21070006e+04, 6.25498996e+04, 6.30000329e+04, 

1361 6.34432415e+04, 6.38632967e+04, 6.43145751e+04, 6.47608638e+04, 

1362 6.52134266e+04, 6.56348895e+04, 6.60841662e+04, 6.65220989e+04, 

1363 6.69687269e+04, 6.74171257e+04, 6.78295038e+04, 6.83037417e+04, 

1364 6.87381890e+04, 6.96234966e+04, 7.00749883e+04, 7.05130803e+04, 

1365 7.09702156e+04, 7.18669367e+04, 7.23008238e+04, 7.27326728e+04, 

1366 7.31879639e+04, 7.36444652e+04, 7.45313822e+04, 7.49512933e+04, 

1367 7.58422569e+04, 7.63002282e+04, 7.67542279e+04, 7.71971910e+04, 

1368 7.75899806e+04, 7.80880043e+04, 7.85207688e+04, 7.89717058e+04, 

1369 7.94257076e+04, 7.98485141e+04, 8.03023282e+04, 8.07414358e+04, 

1370 8.11910981e+04, 8.16398955e+04, 8.25250712e+04, 8.29401845e+04, 

1371 8.34236581e+04, 8.38641888e+04, 8.42971844e+04, 8.47295006e+04, 

1372 8.52114910e+04, 8.56328503e+04, 8.60862279e+04, 8.69170045e+04, 

1373 8.78165034e+04, 8.82689547e+04, 8.87058177e+04, 8.90804208e+04, 

1374 8.95362169e+04, 8.99496410e+04, 9.03236457e+04, 9.06958198e+04, 

1375 9.11193888e+04, 9.14557890e+04, 9.17189073e+04, 9.21437093e+04, 

1376 9.28449356e+04, 9.31434208e+04, 9.33064667e+04, 9.34994900e+04, 

1377 9.37926812e+04, 9.41725477e+04, 9.45383949e+04, 9.46575696e+04, 

1378 9.49209096e+04, 9.49952393e+04, 9.50539106e+04, 9.52347786e+04, 

1379 9.52460768e+04, 9.54989344e+04, 9.56298553e+04, 9.58490767e+04, 

1380 9.62648021e+04, 9.61075892e+04, 9.63538129e+04, 9.63559508e+04, 

1381 9.64708846e+04, 9.64756350e+04, 9.66256538e+04, 9.64809810e+04, 

1382 9.65366387e+04, 9.68214859e+04, 9.69137722e+04, 9.68210311e+04, 

1383 9.71873201e+04, 9.71342102e+04, 9.71078451e+04, 9.71069892e+04, 

1384 9.75296912e+04, 9.74999383e+04, 9.73480623e+04, 9.76524037e+04, 

1385 9.74981803e+04, 9.76922174e+04, 9.76337785e+04, 9.75835125e+04, 

1386 9.75674694e+04, 9.75367570e+04, 9.77438281e+04, 9.76871410e+04, 

1387 9.78773702e+04, 9.78321613e+04, 9.77808877e+04, 9.77359422e+04, 

1388 9.79278808e+04, 9.79329075e+04, 9.77675254e+04, 9.78952318e+04, 

1389 9.77270368e+04, 9.79509533e+04, 9.77760093e+04, 9.80460286e+04, 

1390 9.81360499e+04, 9.79637125e+04, 9.79664967e+04, 9.78575509e+04, 

1391 9.80556911e+04, 9.81417648e+04, 9.80352945e+04, 9.81329700e+04, 

1392 9.81188437e+04, 9.79678779e+04, 9.79598626e+04, 9.80560275e+04, 

1393 9.79542087e+04, 9.79987939e+04, 9.81534114e+04, 9.83899105e+04, 

1394 9.84065000e+04, 9.82811922e+04, 9.82840840e+04, 9.82784134e+04, 

1395 9.82871998e+04, 9.84461381e+04, 9.84682635e+04, 9.85020571e+04, 

1396 np.nan, np.nan, np.nan, np.nan, 

1397 np.nan, np.nan, np.nan, np.nan, 

1398 np.nan, np.nan, np.nan, np.nan, 

1399 np.nan, np.nan, np.nan, np.nan]) 

1400 rawVars = np.array([ 

1401 4.87069525e+01, 4.96960311e+01, 5.04910053e+01, 5.17128726e+01, 

1402 5.25955479e+01, 5.38353690e+01, 5.53191132e+01, 5.66941478e+01, 

1403 5.82926522e+01, 5.98938552e+01, 6.12951161e+01, 6.37287578e+01, 

1404 6.80636332e+01, 7.04028909e+01, 7.29998462e+01, 7.48586667e+01, 

1405 7.66622012e+01, 7.82391455e+01, 7.99899592e+01, 8.21891213e+01, 

1406 8.44852817e+01, 8.74729321e+01, 9.05085253e+01, 9.45153523e+01, 

1407 9.84325033e+01, 1.02490363e+02, 1.05547104e+02, 1.08782968e+02, 

1408 1.12593458e+02, 1.17458820e+02, 1.22086446e+02, 1.27863525e+02, 

1409 1.34404768e+02, 1.42189276e+02, 1.48361964e+02, 1.52241417e+02, 

1410 1.56903695e+02, 1.63487388e+02, 1.71250175e+02, 1.79776726e+02, 

1411 1.87500190e+02, 1.95127335e+02, 2.05237738e+02, 2.17941739e+02, 

1412 2.27601893e+02, 2.34445474e+02, 2.46136553e+02, 2.58638437e+02, 

1413 2.70612240e+02, 2.88057360e+02, 3.01436607e+02, 3.11174829e+02, 

1414 3.27060261e+02, 3.47777215e+02, 3.68419809e+02, 3.80518820e+02, 

1415 3.97377177e+02, 4.25381795e+02, 4.42049448e+02, 4.59452324e+02, 

1416 4.92530031e+02, 5.14495733e+02, 5.34196108e+02, 5.72228281e+02, 

1417 5.92914611e+02, 6.29229419e+02, 6.63254402e+02, 7.10047069e+02, 

1418 7.31110299e+02, 7.74635732e+02, 8.06624321e+02, 8.55180585e+02, 

1419 9.44435441e+02, 9.43183163e+02, 1.00413513e+03, 9.99872981e+02, 

1420 1.05311443e+03, 1.05562621e+03, 1.10199374e+03, 1.15531802e+03, 

1421 1.22522414e+03, 1.36520090e+03, 1.42667128e+03, 1.56495960e+03, 

1422 1.65397576e+03, 1.74424087e+03, 1.84231293e+03, 1.94740328e+03, 

1423 2.03765806e+03, 2.14414450e+03, 2.27212692e+03, 2.38935653e+03, 

1424 2.50602833e+03, 2.51661588e+03, 2.64775661e+03, 2.78538088e+03, 

1425 2.93987651e+03, 3.26826498e+03, 3.44330599e+03, 3.69978469e+03, 

1426 3.97061173e+03, 4.24728849e+03, 4.50971380e+03, 4.77595875e+03, 

1427 5.05755732e+03, 5.30327839e+03, 5.58773282e+03, 5.81966058e+03, 

1428 6.10932503e+03, 6.36332817e+03, 6.61863136e+03, 6.90433479e+03, 

1429 7.13413117e+03, 7.43562156e+03, 7.66582807e+03, 7.94754672e+03, 

1430 8.18686206e+03, 8.45779399e+03, 8.71402609e+03, 8.95964156e+03, 

1431 9.47591061e+03, 9.76959463e+03, 9.95917651e+03, 9.97584309e+03, 

1432 1.02555717e+04, 1.02417814e+04, 1.07674493e+04, 1.09981921e+04, 

1433 1.12352859e+04, 1.15551940e+04, 1.17333125e+04, 1.19690736e+04, 

1434 1.22534101e+04, 1.24640828e+04, 1.27500496e+04, 1.29625045e+04, 

1435 1.32703257e+04, 1.34926746e+04, 1.37251524e+04, 1.42302648e+04, 

1436 1.46793658e+04, 1.49722647e+04, 1.49709034e+04, 1.52549107e+04, 

1437 1.55088982e+04, 1.57314736e+04, 1.59816003e+04, 1.61640169e+04, 

1438 1.64548981e+04, 1.66413940e+04, 1.68686050e+04, 1.71125336e+04, 

1439 1.73511674e+04, 1.75817535e+04, 1.80470011e+04, 1.82624491e+04, 

1440 1.84524739e+04, 1.87019552e+04, 1.89575762e+04, 1.91930866e+04, 

1441 1.94004074e+04, 1.96460005e+04, 2.00479314e+04, 2.02955099e+04, 

1442 2.04822881e+04, 2.09531956e+04, 2.11697303e+04, 2.13633602e+04, 

1443 2.18346507e+04, 2.19539919e+04, 2.22252821e+04, 2.25155821e+04, 

1444 2.26702123e+04, 2.28414558e+04, 2.30830649e+04, 2.32726468e+04, 

1445 2.34988786e+04, 2.37395021e+04, 2.39101878e+04, 2.43317884e+04, 

1446 2.45771060e+04, 2.47054420e+04, 2.49622825e+04, 2.52797662e+04, 

1447 2.55292494e+04, 2.57138336e+04, 2.59366428e+04, 2.61122036e+04, 

1448 2.63896695e+04, 2.64562386e+04, 2.66907602e+04, 2.69102279e+04, 

1449 2.71833821e+04, 2.73303887e+04, 2.77661838e+04, 2.79006930e+04, 

1450 2.81874904e+04, 2.83607526e+04, 2.86045106e+04, 2.87425416e+04, 

1451 2.89632711e+04, 2.91293885e+04, 2.93514145e+04, 2.96850582e+04, 

1452 2.98715928e+04, 3.01274873e+04, 3.03033141e+04, 3.04572121e+04, 

1453 3.07470469e+04, 3.10501508e+04, 3.11450218e+04, 3.14122871e+04, 

1454 3.16022955e+04, 3.18325755e+04, 3.21339141e+04, 3.23047828e+04, 

1455 3.24219992e+04, 3.28022196e+04, 3.30193533e+04, 3.32907509e+04, 

1456 3.34017470e+04, 3.36009585e+04, 3.39733107e+04, 3.40535547e+04, 

1457 3.42458795e+04, 3.44664410e+04, 3.47643491e+04, 3.50165080e+04, 

1458 3.51727941e+04, 3.53854796e+04, 3.55711407e+04, 3.58190707e+04, 

1459 3.60454029e+04, 3.63969548e+04, 3.66329292e+04, 3.68771986e+04, 

1460 3.71032547e+04, 3.73983989e+04, 3.76425541e+04, 3.79247481e+04, 

1461 3.80882104e+04, 3.82317170e+04, 3.86471187e+04, 3.89086176e+04, 

1462 3.92339037e+04, 3.95436246e+04, 3.96460919e+04, 3.98028414e+04, 

1463 3.99125771e+04, 4.01715067e+04, 4.03722901e+04, 4.05626968e+04, 

1464 4.06933838e+04, 4.10184964e+04, 4.11922744e+04, 4.13557454e+04, 

1465 4.16157584e+04, 4.16356931e+04, 4.21157583e+04, 4.21816345e+04, 

1466 4.24543184e+04, 4.26313857e+04, 4.27797954e+04, 4.29586701e+04, 

1467 4.31317246e+04, 4.31196229e+04, 4.30508028e+04, 4.29439940e+04, 

1468 4.14814510e+04, 4.17104998e+04, 4.12950588e+04, 4.00762620e+04, 

1469 3.95745952e+04, 3.79603760e+04, 3.68764412e+04, 3.59324071e+04, 

1470 3.41774826e+04, 3.35146378e+04, 3.01416949e+04, 3.10962443e+04, 

1471 2.93723225e+04, 2.88267934e+04, 2.93057515e+04, 2.78885387e+04, 

1472 2.97481790e+04, 2.89003096e+04, 2.98475325e+04, 3.15227067e+04, 

1473 3.15157345e+04, 3.32593077e+04, 3.30733852e+04, 3.37623662e+04, 

1474 3.58784503e+04, 3.75160267e+04, 3.82803313e+04, 3.89001912e+04, 

1475 4.17283047e+04, 4.20086699e+04, 4.22993774e+04, 4.34867653e+04, 

1476 4.17054368e+04, 4.52190044e+04, 4.55586401e+04, 4.39034690e+04, 

1477 4.49939183e+04, 4.75700720e+04, 4.78978238e+04, 4.62120749e+04, 

1478 4.85317658e+04, 4.97305999e+04, 4.70691830e+04, 4.77667215e+04, 

1479 5.19257836e+04, 5.01885056e+04, 4.83671721e+04, 5.21924342e+04, 

1480 5.10329762e+04, 5.25552111e+04, 5.17040972e+04, 5.11958488e+04, 

1481 5.19165668e+04, 4.88842210e+04, 5.17286013e+04, 5.13182361e+04, 

1482 5.16342048e+04, 5.17850241e+04, 5.16475818e+04, 5.00165868e+04, 

1483 5.19391672e+04, 5.21771011e+04, 5.01633917e+04, 5.19270231e+04, 

1484 4.93839429e+04, 4.96516804e+04, 4.93706114e+04, 5.25025570e+04, 

1485 5.43045043e+04, 5.27477605e+04, 5.23609667e+04, 4.89296194e+04, 

1486 5.26232764e+04, 5.24395882e+04, 4.90790820e+04, 5.19268918e+04, 

1487 5.21608822e+04, 4.89292620e+04, 4.86074588e+04, 4.93274381e+04, 

1488 4.84901815e+04, 4.91868029e+04, 5.25082453e+04, 5.44279458e+04, 

1489 5.55797293e+04, 5.20003094e+04, 5.19304424e+04, 5.17391458e+04, 

1490 5.24042964e+04, 5.53566164e+04, 5.53202737e+04, 5.58931187e+04, 

1491 np.nan, np.nan, np.nan, np.nan, 

1492 np.nan, np.nan, np.nan, np.nan, 

1493 np.nan, np.nan, np.nan, np.nan, 

1494 np.nan, np.nan, np.nan, np.nan]) 

1495 elif dense and mode == "dip": 

1496 # Taken from dense run 13591, detector 46, amplifier C16 

1497 ptcTurnoff = 78592.643 

1498 rawMeans = np.array( 

1499 [3.46319253e+01, 3.66430301e+01, 3.86622690e+01, 4.05725802e+01, 

1500 4.25802432e+01, 4.40269809e+01, 4.80275071e+01, 5.05005628e+01, 

1501 5.24638704e+01, 5.51726716e+01, 5.80431294e+01, 6.18363693e+01, 

1502 6.76872491e+01, 7.54744430e+01, 7.59445874e+01, 8.07200670e+01, 

1503 8.39801977e+01, 8.89822266e+01, 9.34165914e+01, 1.00259640e+02, 

1504 1.04115753e+02, 1.09746401e+02, 1.16495426e+02, 1.24702365e+02, 

1505 1.29617918e+02, 1.38517235e+02, 1.44598282e+02, 1.55474182e+02, 

1506 1.60372893e+02, 1.68952583e+02, 1.79169853e+02, 1.87803850e+02, 

1507 1.97901901e+02, 2.12236200e+02, 2.20703858e+02, 2.33721460e+02, 

1508 2.45632287e+02, 2.60078861e+02, 2.73309812e+02, 2.91303550e+02, 

1509 3.08138900e+02, 3.20634527e+02, 3.39157572e+02, 3.61285287e+02, 

1510 3.77306857e+02, 3.97532592e+02, 4.19725123e+02, 4.42637799e+02, 

1511 4.66432222e+02, 4.92760774e+02, 5.18439719e+02, 5.49043359e+02, 

1512 5.80725784e+02, 6.10607872e+02, 6.42130865e+02, 6.78015970e+02, 

1513 7.19634201e+02, 7.56125967e+02, 7.97647549e+02, 8.44250515e+02, 

1514 8.88164221e+02, 9.35554605e+02, 9.87551014e+02, 1.04304478e+03, 

1515 1.09859274e+03, 1.15934514e+03, 1.22290957e+03, 1.28928061e+03, 

1516 1.36205339e+03, 1.43517932e+03, 1.51527821e+03, 1.59829513e+03, 

1517 1.77964089e+03, 1.78170273e+03, 1.87682761e+03, 1.87684189e+03, 

1518 1.98075997e+03, 1.98113130e+03, 2.08945214e+03, 2.20305152e+03, 

1519 2.32556464e+03, 2.58962409e+03, 2.72814882e+03, 2.99794092e+03, 

1520 3.16447505e+03, 3.33946592e+03, 3.52147949e+03, 3.71394579e+03, 

1521 3.91907040e+03, 4.13438382e+03, 4.36189594e+03, 4.60043753e+03, 

1522 4.85130253e+03, 4.85473652e+03, 5.12061926e+03, 5.40324180e+03, 

1523 5.70061253e+03, 6.34723550e+03, 6.69436828e+03, 7.23257375e+03, 

1524 7.76958269e+03, 8.30697359e+03, 8.84549436e+03, 9.38149894e+03, 

1525 9.92214412e+03, 1.04540863e+04, 1.09984280e+04, 1.15336125e+04, 

1526 1.20730516e+04, 1.26066598e+04, 1.31475457e+04, 1.36835704e+04, 

1527 1.42142220e+04, 1.47607783e+04, 1.52995772e+04, 1.58349683e+04, 

1528 1.63699779e+04, 1.69169118e+04, 1.74527183e+04, 1.79882677e+04, 

1529 1.90590055e+04, 1.96042099e+04, 2.01412931e+04, 2.01415525e+04, 

1530 2.06773002e+04, 2.06836599e+04, 2.17563308e+04, 2.22945949e+04, 

1531 2.28326494e+04, 2.33730224e+04, 2.39083711e+04, 2.44415981e+04, 

1532 2.49887745e+04, 2.55254849e+04, 2.60582748e+04, 2.66026296e+04, 

1533 2.71388848e+04, 2.76745959e+04, 2.82098429e+04, 2.92893984e+04, 

1534 3.03685478e+04, 3.08997866e+04, 3.10729575e+04, 3.16013527e+04, 

1535 3.21337166e+04, 3.26668783e+04, 3.31930745e+04, 3.37295906e+04, 

1536 3.42575950e+04, 3.47930111e+04, 3.53194913e+04, 3.58360666e+04, 

1537 3.63796428e+04, 3.69119003e+04, 3.79618999e+04, 3.85046991e+04, 

1538 3.90341729e+04, 3.95622456e+04, 4.00906725e+04, 4.06248524e+04, 

1539 4.11506714e+04, 4.16820728e+04, 4.27281690e+04, 4.32583738e+04, 

1540 4.37923105e+04, 4.48436280e+04, 4.53713042e+04, 4.58983070e+04, 

1541 4.69589412e+04, 4.74759399e+04, 4.80036320e+04, 4.85414573e+04, 

1542 4.90619668e+04, 4.95708602e+04, 5.01124419e+04, 5.06550432e+04, 

1543 5.11459970e+04, 5.16864720e+04, 5.22155963e+04, 5.32732837e+04, 

1544 5.37950395e+04, 5.43202717e+04, 5.48449050e+04, 5.58936896e+04, 

1545 5.64268047e+04, 5.69454503e+04, 5.74788533e+04, 5.79886165e+04, 

1546 5.85227316e+04, 5.90467563e+04, 5.95529633e+04, 6.01107425e+04, 

1547 6.06190504e+04, 6.11482815e+04, 6.22041090e+04, 6.27192672e+04, 

1548 6.32698650e+04, 6.37882800e+04, 6.43035282e+04, 6.48401842e+04, 

1549 6.53646656e+04, 6.59030142e+04, 6.64333556e+04, 6.69479267e+04, 

1550 6.74848468e+04, 6.80079972e+04, 6.85445388e+04, 6.90509171e+04, 

1551 6.96092229e+04, 7.01385610e+04, 7.06212128e+04, 7.11939523e+04, 

1552 7.17266360e+04, 7.22471223e+04, 7.27780652e+04, 7.32761803e+04, 

1553 7.38361226e+04, 7.43656998e+04, 7.48934046e+04, 7.54352821e+04, 

1554 7.59644966e+04, 7.64724502e+04, 7.70119423e+04, 7.75439221e+04, 

1555 7.80842181e+04, 7.85926430e+04, 7.91251970e+04, 7.96491204e+04, 

1556 8.01869810e+04, 8.07146073e+04, 8.12202954e+04, 8.17785695e+04, 

1557 8.22963087e+04, 8.33550384e+04, 8.38994212e+04, 8.44155447e+04, 

1558 8.49563789e+04, 8.60281829e+04, 8.65433251e+04, 8.70627645e+04, 

1559 8.76007341e+04, 8.81511125e+04, 8.92018550e+04, 8.97044207e+04, 

1560 9.07592037e+04, 9.13032564e+04, 9.18479184e+04, 9.23763279e+04, 

1561 9.28541040e+04, 9.34276276e+04, 9.39508203e+04, 9.44807722e+04, 

1562 9.50201270e+04, 9.55260238e+04, 9.60584060e+04, 9.65900977e+04, 

1563 9.71082260e+04, 9.76499634e+04, 9.86927409e+04, 9.92001749e+04, 

1564 9.97605950e+04, 1.00281826e+05, 1.00799323e+05, 1.01317520e+05, 

1565 1.01880214e+05, 1.02375482e+05, 1.02921781e+05, 1.03914664e+05, 

1566 1.04996919e+05, 1.05531177e+05, 1.06045662e+05, 1.06553960e+05, 

1567 1.07074502e+05, 1.07620081e+05, 1.08135030e+05, 1.08625699e+05, 

1568 1.09176443e+05, 1.09713812e+05, 1.10216327e+05, 1.10733831e+05, 

1569 1.11710810e+05, 1.12193799e+05, 1.12690561e+05, 1.13133767e+05, 

1570 1.13607554e+05, 1.14479014e+05, 1.14907269e+05, 1.15294108e+05, 

1571 1.15680838e+05, 1.16050756e+05, 1.16395663e+05, 1.16738698e+05, 

1572 1.17036744e+05, 1.17606634e+05, 1.17864854e+05, 1.18107937e+05, 

1573 1.18338933e+05, 1.18484492e+05, 1.18751107e+05, 1.18925783e+05, 

1574 1.19127596e+05, 1.19257553e+05, 1.19452513e+05, 1.19608706e+05, 

1575 1.19753398e+05, 1.19883813e+05, 1.20021670e+05, 1.20155507e+05, 

1576 1.20406612e+05, 1.20605600e+05, 1.20753078e+05, 1.20866466e+05, 

1577 np.nan, np.nan, np.nan, np.nan, 

1578 np.nan, np.nan, np.nan, np.nan, 

1579 np.nan, np.nan, np.nan, np.nan, 

1580 np.nan, np.nan, np.nan, np.nan, 

1581 np.nan, np.nan, np.nan, np.nan, 

1582 np.nan, np.nan, np.nan, np.nan, 

1583 np.nan, np.nan, np.nan, np.nan, 

1584 np.nan, np.nan, np.nan, np.nan, 

1585 np.nan, np.nan, np.nan, np.nan, 

1586 np.nan, np.nan, np.nan, np.nan, 

1587 np.nan, np.nan, np.nan, np.nan, 

1588 np.nan, np.nan, np.nan, np.nan, 

1589 np.nan, np.nan, np.nan, np.nan, 

1590 np.nan, np.nan, np.nan, np.nan, 

1591 np.nan, np.nan, np.nan, np.nan, 

1592 np.nan, np.nan, np.nan, np.nan]) 

1593 rawVars = np.array( 

1594 [3.70611558e+01, 3.83737395e+01, 3.99468861e+01, 4.10014338e+01, 

1595 4.23251266e+01, 4.33678155e+01, 4.59071461e+01, 4.73626968e+01, 

1596 4.87148063e+01, 5.03835098e+01, 5.25225011e+01, 5.49763960e+01, 

1597 5.89144406e+01, 6.46901096e+01, 6.46921274e+01, 6.77000641e+01, 

1598 7.03062675e+01, 7.37864491e+01, 7.78022510e+01, 8.44978037e+01, 

1599 8.74723267e+01, 9.13765683e+01, 9.51046462e+01, 9.90038919e+01, 

1600 1.01309767e+02, 1.06498820e+02, 1.10600587e+02, 1.18170347e+02, 

1601 1.21408878e+02, 1.28214192e+02, 1.36647653e+02, 1.42148979e+02, 

1602 1.48491678e+02, 1.59805162e+02, 1.65823057e+02, 1.73305507e+02, 

1603 1.80522937e+02, 1.87449934e+02, 1.96245721e+02, 2.08352732e+02, 

1604 2.21201432e+02, 2.32523118e+02, 2.44009905e+02, 2.56991468e+02, 

1605 2.68047397e+02, 2.79329180e+02, 2.97712914e+02, 3.15304298e+02, 

1606 3.27789122e+02, 3.44980267e+02, 3.61446379e+02, 3.84494985e+02, 

1607 4.05677044e+02, 4.22749240e+02, 4.44709272e+02, 4.77813761e+02, 

1608 5.01815280e+02, 5.21252820e+02, 5.49793847e+02, 5.80608964e+02, 

1609 6.10172377e+02, 6.44282320e+02, 6.76618722e+02, 7.14200019e+02, 

1610 7.52150585e+02, 7.90093939e+02, 8.43584018e+02, 8.74257958e+02, 

1611 9.25137165e+02, 9.77217130e+02, 1.02268591e+03, 1.07911844e+03, 

1612 1.20579007e+03, 1.21094193e+03, 1.26536689e+03, 1.26783114e+03, 

1613 1.33667935e+03, 1.34071853e+03, 1.40959965e+03, 1.49236868e+03, 

1614 1.56214455e+03, 1.73911080e+03, 1.84452009e+03, 2.01282493e+03, 

1615 2.11914383e+03, 2.23859893e+03, 2.35101284e+03, 2.48665441e+03, 

1616 2.61201878e+03, 2.74350197e+03, 2.89732268e+03, 3.05252644e+03, 

1617 3.22199981e+03, 3.22767156e+03, 3.38954315e+03, 3.58149621e+03, 

1618 3.76614023e+03, 4.19955412e+03, 4.39939398e+03, 4.74116692e+03, 

1619 5.08774295e+03, 5.43362489e+03, 5.76587686e+03, 6.09147076e+03, 

1620 6.46434875e+03, 6.77118428e+03, 7.10758829e+03, 7.41292668e+03, 

1621 7.75391200e+03, 8.07133480e+03, 8.40331959e+03, 8.71723040e+03, 

1622 9.03873483e+03, 9.36871022e+03, 9.69206829e+03, 9.99510413e+03, 

1623 1.03183250e+04, 1.06519986e+04, 1.09717629e+04, 1.12984284e+04, 

1624 1.19017371e+04, 1.22009054e+04, 1.25073348e+04, 1.24828917e+04, 

1625 1.28123892e+04, 1.27965364e+04, 1.34024560e+04, 1.36798307e+04, 

1626 1.40186807e+04, 1.43485421e+04, 1.45916270e+04, 1.48802247e+04, 

1627 1.51990105e+04, 1.54775801e+04, 1.57746393e+04, 1.60211115e+04, 

1628 1.63459546e+04, 1.66499888e+04, 1.68983125e+04, 1.74427484e+04, 

1629 1.80456894e+04, 1.82652348e+04, 1.83690177e+04, 1.86208031e+04, 

1630 1.89412363e+04, 1.91602864e+04, 1.94785697e+04, 1.97091334e+04, 

1631 1.99827654e+04, 2.02492520e+04, 2.04397095e+04, 2.07912874e+04, 

1632 2.09363768e+04, 2.12446667e+04, 2.17637411e+04, 2.19817593e+04, 

1633 2.22395694e+04, 2.24653630e+04, 2.27254307e+04, 2.29678916e+04, 

1634 2.32155716e+04, 2.34915971e+04, 2.39308788e+04, 2.41386350e+04, 

1635 2.43300723e+04, 2.47776731e+04, 2.49855450e+04, 2.52822560e+04, 

1636 2.57310565e+04, 2.58705668e+04, 2.61365827e+04, 2.63745002e+04, 

1637 2.64761919e+04, 2.67763879e+04, 2.69546269e+04, 2.72247330e+04, 

1638 2.73854794e+04, 2.75964799e+04, 2.78536768e+04, 2.83219440e+04, 

1639 2.84473603e+04, 2.86066766e+04, 2.88858064e+04, 2.93640042e+04, 

1640 2.95802005e+04, 2.97953000e+04, 2.99784114e+04, 3.01818213e+04, 

1641 3.04537120e+04, 3.07186903e+04, 3.09159639e+04, 3.11340544e+04, 

1642 3.13467981e+04, 3.16593731e+04, 3.19829978e+04, 3.22855092e+04, 

1643 3.25921574e+04, 3.27154499e+04, 3.28698071e+04, 3.31481419e+04, 

1644 3.34606399e+04, 3.37260080e+04, 3.38331508e+04, 3.40882291e+04, 

1645 3.43298166e+04, 3.44626343e+04, 3.47352389e+04, 3.48844453e+04, 

1646 3.50976243e+04, 3.53422532e+04, 3.54703155e+04, 3.57850397e+04, 

1647 3.60218788e+04, 3.61282209e+04, 3.63718905e+04, 3.64159312e+04, 

1648 3.67264583e+04, 3.68810487e+04, 3.70236104e+04, 3.73025531e+04, 

1649 3.74696213e+04, 3.74774745e+04, 3.76885954e+04, 3.77461428e+04, 

1650 3.77311980e+04, 3.76449769e+04, 3.71874494e+04, 3.67403027e+04, 

1651 3.58397731e+04, 3.44249565e+04, 3.25971681e+04, 3.12036933e+04, 

1652 2.94985871e+04, 2.71719366e+04, 2.69809068e+04, 2.72483125e+04, 

1653 2.83267080e+04, 3.15350383e+04, 3.33307911e+04, 3.48927220e+04, 

1654 3.65827728e+04, 3.79316518e+04, 3.97114600e+04, 4.02038182e+04, 

1655 4.10252529e+04, 4.14747301e+04, 4.16607368e+04, 4.18321712e+04, 

1656 4.18311337e+04, 4.21298732e+04, 4.21384783e+04, 4.21559876e+04, 

1657 4.23156351e+04, 4.22011377e+04, 4.22592203e+04, 4.20913479e+04, 

1658 4.15029042e+04, 4.09470822e+04, 3.85767751e+04, 3.64665853e+04, 

1659 3.45138148e+04, 3.21709915e+04, 2.96629755e+04, 2.69203438e+04, 

1660 2.43537600e+04, 2.23428644e+04, 2.01581545e+04, 1.62544599e+04, 

1661 1.34824506e+04, 1.18721963e+04, 1.05452615e+04, 9.46843007e+03, 

1662 8.23841467e+03, 7.23997272e+03, 6.36586857e+03, 5.65708162e+03, 

1663 4.99963764e+03, 4.48251363e+03, 4.09078951e+03, 3.72817220e+03, 

1664 3.21067198e+03, 3.03262162e+03, 2.89743032e+03, 2.77343484e+03, 

1665 2.69667301e+03, 2.60188277e+03, 2.55172731e+03, 2.52540578e+03, 

1666 2.56130915e+03, 2.52293303e+03, 2.50717444e+03, 2.51044664e+03, 

1667 2.51531215e+03, 2.53768779e+03, 2.56201926e+03, 2.56217908e+03, 

1668 2.60528686e+03, 2.61531849e+03, 2.62971142e+03, 2.66896083e+03, 

1669 2.69706371e+03, 2.73927492e+03, 2.74999442e+03, 2.75180096e+03, 

1670 2.78694836e+03, 2.80704605e+03, 2.83222240e+03, 2.86654398e+03, 

1671 2.90678852e+03, 2.95139745e+03, 2.95942911e+03, 3.00791154e+03, 

1672 np.nan, np.nan, np.nan, np.nan, 

1673 np.nan, np.nan, np.nan, np.nan, 

1674 np.nan, np.nan, np.nan, np.nan, 

1675 np.nan, np.nan, np.nan, np.nan, 

1676 np.nan, np.nan, np.nan, np.nan, 

1677 np.nan, np.nan, np.nan, np.nan, 

1678 np.nan, np.nan, np.nan, np.nan, 

1679 np.nan, np.nan, np.nan, np.nan, 

1680 np.nan, np.nan, np.nan, np.nan, 

1681 np.nan, np.nan, np.nan, np.nan, 

1682 np.nan, np.nan, np.nan, np.nan, 

1683 np.nan, np.nan, np.nan, np.nan, 

1684 np.nan, np.nan, np.nan, np.nan, 

1685 np.nan, np.nan, np.nan, np.nan, 

1686 np.nan, np.nan, np.nan, np.nan, 

1687 np.nan, np.nan, np.nan, np.nan]) 

1688 elif not dense and mode == "normal": 

1689 # Taken from B protocol run 13557, detector 94, amplifier C02 

1690 ptcTurnoff = 92173.9596 

1691 rawMeans = np.array([ 

1692 7.35242203e+01, 9.29532799e+01, 1.12149391e+02, 1.39776279e+02, 

1693 1.74220020e+02, 2.15692281e+02, 2.68814926e+02, 3.34446868e+02, 

1694 4.14359931e+02, 5.14691775e+02, 6.40642848e+02, 7.95596873e+02, 

1695 9.90753280e+02, 1.22885224e+03, 1.52684706e+03, 1.89907469e+03, 

1696 2.35701770e+03, 2.93106806e+03, 3.59466174e+03, 4.46720318e+03, 

1697 5.55232373e+03, 6.90091293e+03, 8.57660238e+03, 1.06583769e+04, 

1698 1.32457052e+04, 1.64618675e+04, 2.04605037e+04, 2.54301936e+04, 

1699 3.15926481e+04, 3.87784321e+04, 4.81318315e+04, 5.97145570e+04, 

1700 7.41859291e+04, 9.21739596e+04, 1.08484387e+05, 1.10456942e+05, 

1701 1.10677645e+05, 1.10691134e+05, 1.10693202e+05, 1.10692292e+05, 

1702 1.10699619e+05, 1.10703407e+05, 1.10704764e+05]) 

1703 rawVars = np.array([ 

1704 71.66098099, 85.85187266, 100.07444552, 121.59045422, 

1705 143.54998243, 171.39262057, 210.51220342, 257.40796789, 

1706 313.11677494, 381.91333248, 476.46542075, 583.72632638, 

1707 729.26397749, 888.46194891, 1101.58606157, 1363.39263745, 

1708 1686.28916615, 2096.14920835, 2552.12897667, 3159.70329711, 

1709 3894.29254595, 4800.37079501, 5940.17760433, 7310.2253724, 

1710 9015.14374818, 11022.81586492, 13509.18049715, 16391.89059679, 

1711 19905.15871788, 23628.84875886, 28128.21785629, 33476.91933085, 

1712 40023.07406229, 45198.48421179, 3390.76561947, 1601.38678043, 

1713 1890.12715102, 1805.34862035, 1904.84156084, 1903.84859699, 

1714 1857.15885789, 1889.45649514, 1892.01672711]) 

1715 elif not dense and mode == "upturn": 

1716 # Taken from B protocol run 13557, detector 73, amplifier C07 

1717 ptcTurnoff = 71832.2986 

1718 rawMeans = np.array([ 

1719 5.65607783e+01, 7.04062631e+01, 8.74047057e+01, 1.08812996e+02, 

1720 1.35333046e+02, 1.67636667e+02, 2.08445200e+02, 2.59202618e+02, 

1721 3.21602277e+02, 3.99545078e+02, 4.96963648e+02, 6.17791959e+02, 

1722 7.67578708e+02, 9.54040040e+02, 1.18587264e+03, 1.47297327e+03, 

1723 1.83109922e+03, 2.27527491e+03, 2.79309339e+03, 3.47193365e+03, 

1724 4.31520802e+03, 5.36244611e+03, 6.66482002e+03, 8.28357528e+03, 

1725 1.02962387e+04, 1.27972565e+04, 1.59069356e+04, 1.97746199e+04, 

1726 2.45737678e+04, 3.01873089e+04, 3.75075197e+04, 4.65677579e+04, 

1727 5.77977537e+04, 7.18322986e+04, 8.92553708e+04, 9.74408608e+04, 

1728 np.nan, np.nan, np.nan, np.nan, 

1729 np.nan, np.nan, np.nan]) 

1730 rawVars = np.array([ 

1731 68.2700079, 76.51661413, 84.52771312, 98.70960506, 

1732 113.95644441, 137.2544902, 158.39159813, 190.7985178, 

1733 229.92447148, 279.52534113, 338.90663196, 414.97072284, 

1734 510.16012626, 622.00951125, 763.81268011, 938.07098006, 

1735 1149.75983481, 1424.88125193, 1745.36382679, 2165.89927287, 

1736 2666.43505016, 3303.63407541, 4094.4066631, 5072.7831761, 

1737 6238.35971003, 7697.01580574, 9479.41363292, 11654.65158713, 

1738 14268.54471588, 17316.88016853, 21012.8242229, 25275.74644538, 

1739 30725.05799423, 37363.24467221, 39830.3337401, 48083.26241611, 

1740 np.nan, np.nan, np.nan, np.nan, 

1741 np.nan, np.nan, np.nan]) 

1742 elif not dense and mode == "dip": 

1743 # Taken from B protocol run 13557, detector 46, amplifier C16 

1744 ptcTurnoff = 87557.8289 

1745 rawMeans = np.array( 

1746 [6.94187281e+01, 8.92438988e+01, 1.05958756e+02, 1.32423251e+02, 

1747 1.64877564e+02, 2.04367731e+02, 2.55076247e+02, 3.16940500e+02, 

1748 3.92525299e+02, 4.87631833e+02, 6.06783494e+02, 7.53540982e+02, 

1749 9.39742534e+02, 1.16367754e+03, 1.44670172e+03, 1.80047945e+03, 

1750 2.23357272e+03, 2.77872101e+03, 3.40646845e+03, 4.23306945e+03, 

1751 5.26085005e+03, 6.53969254e+03, 8.12914423e+03, 1.01016373e+04, 

1752 1.25546410e+04, 1.56072062e+04, 1.93987924e+04, 2.41153677e+04, 

1753 2.99669024e+04, 3.67956267e+04, 4.56879445e+04, 5.66901053e+04, 

1754 7.04360489e+04, 8.75578289e+04, 1.08738204e+05, np.nan, 

1755 np.nan, np.nan, np.nan, np.nan, 

1756 np.nan, np.nan, np.nan]) 

1757 rawVars = np.array( 

1758 [61.7553328, 75.60175792, 88.98931208, 106.95977, 

1759 129.78637495, 153.5130247, 192.25182776, 233.4904232, 

1760 287.02595043, 349.57554198, 428.71515892, 528.0987701, 

1761 656.97011401, 820.88213784, 1003.62289116, 1243.67283887, 

1762 1543.24210179, 1905.00050382, 2321.63329694, 2889.08652787, 

1763 3574.52610263, 4393.02299998, 5427.50087472, 6691.62708604, 

1764 8226.42941205, 10104.75767742, 12365.56291744, 15029.66357377, 

1765 18233.5785582, 21684.66746226, 25811.97832361, 30686.30643311, 

1766 36834.73747467, 41993.20346722, 30079.64884394, np.nan, 

1767 np.nan, np.nan, np.nan, np.nan, 

1768 np.nan, np.nan, np.nan]) 

1769 else: 

1770 raise RuntimeError("Illegal mode") 

1771 

1772 return rawMeans, rawVars, ptcTurnoff 

1773 

1774 

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

1776 def setUp(self): 

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

1778 self.ptcData.inputExpIdPairs = { 

1779 "C00": [(123, 234), (345, 456), (567, 678)], 

1780 "C01": [(123, 234), (345, 456), (567, 678)], 

1781 } 

1782 

1783 def test_generalBehaviour(self): 

1784 test = PhotonTransferCurveDataset(["C00", "C01"], " ") 

1785 test.inputExpIdPairs = { 

1786 "C00": [(123, 234), (345, 456), (567, 678)], 

1787 "C01": [(123, 234), (345, 456), (567, 678)], 

1788 } 

1789 

1790 

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

1792 pass 

1793 

1794 

1795def setup_module(module): 

1796 lsst.utils.tests.init() 

1797 

1798 

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

1800 lsst.utils.tests.init() 

1801 unittest.main()