Coverage for python/lsst/ip/isr/ptcDataset.py: 7%

246 statements  

« prev     ^ index     » next       coverage.py v6.4, created at 2022-05-26 10:32 +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# 

22""" 

23Define dataset class for MeasurePhotonTransferCurve task 

24""" 

25import numpy as np 

26from astropy.table import Table 

27 

28from lsst.ip.isr import IsrCalib 

29 

30__all__ = ['PhotonTransferCurveDataset'] 

31 

32 

33class PhotonTransferCurveDataset(IsrCalib): 

34 """A simple class to hold the output data from the PTC task. 

35 The dataset is made up of a dictionary for each item, keyed by the 

36 amplifiers' names, which much be supplied at construction time. 

37 New items cannot be added to the class to save accidentally saving to the 

38 wrong property, and the class can be frozen if desired. 

39 inputExpIdPairs records the exposures used to produce the data. 

40 When fitPtc() or fitCovariancesAstier() is run, a mask is built up, which 

41 is by definition always the same length as inputExpIdPairs, rawExpTimes, 

42 rawMeans and rawVars, and is a list of bools, which are incrementally set 

43 to False as points are discarded from the fits. 

44 PTC fit parameters for polynomials are stored in a list in ascending order 

45 of polynomial term, i.e. par[0]*x^0 + par[1]*x + par[2]*x^2 etc 

46 with the length of the list corresponding to the order of the polynomial 

47 plus one. 

48 

49 Parameters 

50 ---------- 

51 ampNames : `list` 

52 List with the names of the amplifiers of the detector at hand. 

53 

54 ptcFitType : `str` 

55 Type of model fitted to the PTC: "POLYNOMIAL", "EXPAPPROXIMATION", 

56 or "FULLCOVARIANCE". 

57 

58 covMatrixSide : `int` 

59 Maximum lag of covariances (size of square covariance matrices). 

60 

61 kwargs : `dict`, optional 

62 Other keyword arguments to pass to the parent init. 

63 

64 Notes 

65 ----- 

66 The stored attributes are: 

67 badAmps : `list` 

68 List with bad amplifiers names. 

69 inputExpIdPairs : `dict`, [`str`, `list`] 

70 Dictionary keyed by amp names containing the input exposures IDs. 

71 expIdMask : `dict`, [`str`, `list`] 

72 Dictionary keyed by amp names containing the mask produced after 

73 outlier rejection. The mask produced by the "FULLCOVARIANCE" 

74 option may differ from the one produced in the other two PTC 

75 fit types. 

76 rawExpTimes : `dict`, [`str`, `list`] 

77 Dictionary keyed by amp names containing the unmasked exposure times. 

78 rawMeans : `dict`, [`str`, `list`] 

79 Dictionary keyed by amp namescontaining the unmasked average of the 

80 means of the exposures in each flat pair. 

81 rawVars : `dict`, [`str`, `list`] 

82 Dictionary keyed by amp names containing the variance of the 

83 difference image of the exposures in each flat pair. 

84 gain : `dict`, [`str`, `list`] 

85 Dictionary keyed by amp names containing the fitted gains. 

86 gainErr : `dict`, [`str`, `list`] 

87 Dictionary keyed by amp names containing the errors on the 

88 fitted gains. 

89 noise : `dict`, [`str`, `list`] 

90 Dictionary keyed by amp names containing the fitted noise. 

91 noiseErr : `dict`, [`str`, `list`] 

92 Dictionary keyed by amp names containing the errors on the fitted 

93 noise. 

94 ptcFitPars : `dict`, [`str`, `list`] 

95 Dictionary keyed by amp names containing the fitted parameters of the 

96 PTC model for ptcFitTye in ["POLYNOMIAL", "EXPAPPROXIMATION"]. 

97 ptcFitParsError : `dict`, [`str`, `list`] 

98 Dictionary keyed by amp names containing the errors on the fitted 

99 parameters of the PTC model for ptcFitTye in 

100 ["POLYNOMIAL", "EXPAPPROXIMATION"]. 

101 ptcFitChiSq : `dict`, [`str`, `list`] 

102 Dictionary keyed by amp names containing the reduced chi squared 

103 of the fit for ptcFitTye in ["POLYNOMIAL", "EXPAPPROXIMATION"]. 

104 covariances : `dict`, [`str`, `list`] 

105 Dictionary keyed by amp names containing a list of measured 

106 covariances per mean flux. 

107 covariancesModel : `dict`, [`str`, `list`] 

108 Dictionary keyed by amp names containinging covariances model 

109 (Eq. 20 of Astier+19) per mean flux. 

110 covariancesSqrtWeights : `dict`, [`str`, `list`] 

111 Dictionary keyed by amp names containinging sqrt. of covariances 

112 weights. 

113 aMatrix : `dict`, [`str`, `list`] 

114 Dictionary keyed by amp names containing the "a" parameters from 

115 the model in Eq. 20 of Astier+19. 

116 bMatrix : `dict`, [`str`, `list`] 

117 Dictionary keyed by amp names containing the "b" parameters from 

118 the model in Eq. 20 of Astier+19. 

119 covariancesModelNoB : `dict`, [`str`, `list`] 

120 Dictionary keyed by amp names containing covariances model 

121 (with 'b'=0 in Eq. 20 of Astier+19) 

122 per mean flux. 

123 aMatrixNoB : `dict`, [`str`, `list`] 

124 Dictionary keyed by amp names containing the "a" parameters from the 

125 model in Eq. 20 of Astier+19 

126 (and 'b' = 0). 

127 finalVars : `dict`, [`str`, `list`] 

128 Dictionary keyed by amp names containing the masked variance of the 

129 difference image of each flat 

130 pair. If needed, each array will be right-padded with 

131 np.nan to match the length of rawExpTimes. 

132 finalModelVars : `dict`, [`str`, `list`] 

133 Dictionary keyed by amp names containing the masked modeled 

134 variance of the difference image of each flat pair. If needed, each 

135 array will be right-padded with np.nan to match the length of 

136 rawExpTimes. 

137 finalMeans : `dict`, [`str`, `list`] 

138 Dictionary keyed by amp names containing the masked average of the 

139 means of the exposures in each flat pair. If needed, each array 

140 will be right-padded with np.nan to match the length of 

141 rawExpTimes. 

142 photoCharge : `dict`, [`str`, `list`] 

143 Dictionary keyed by amp names containing the integrated photocharge 

144 for linearity calibration. 

145 

146 Returns 

147 ------- 

148 `lsst.cp.pipe.ptc.PhotonTransferCurveDataset` 

149 Output dataset from MeasurePhotonTransferCurveTask. 

150 """ 

151 

152 _OBSTYPE = 'PTC' 

153 _SCHEMA = 'Gen3 Photon Transfer Curve' 

154 _VERSION = 1.0 

155 

156 def __init__(self, ampNames=[], ptcFitType=None, covMatrixSide=1, **kwargs): 

157 

158 self.ptcFitType = ptcFitType 

159 self.ampNames = ampNames 

160 self.covMatrixSide = covMatrixSide 

161 

162 self.badAmps = [np.nan] 

163 

164 self.inputExpIdPairs = {ampName: [] for ampName in ampNames} 

165 self.expIdMask = {ampName: [] for ampName in ampNames} 

166 self.rawExpTimes = {ampName: [] for ampName in ampNames} 

167 self.rawMeans = {ampName: [] for ampName in ampNames} 

168 self.rawVars = {ampName: [] for ampName in ampNames} 

169 self.photoCharge = {ampName: [] for ampName in ampNames} 

170 

171 self.gain = {ampName: np.nan for ampName in ampNames} 

172 self.gainErr = {ampName: np.nan for ampName in ampNames} 

173 self.noise = {ampName: np.nan for ampName in ampNames} 

174 self.noiseErr = {ampName: np.nan for ampName in ampNames} 

175 

176 self.ptcFitPars = {ampName: [] for ampName in ampNames} 

177 self.ptcFitParsError = {ampName: [] for ampName in ampNames} 

178 self.ptcFitChiSq = {ampName: np.nan for ampName in ampNames} 

179 

180 self.covariances = {ampName: [] for ampName in ampNames} 

181 self.covariancesModel = {ampName: [] for ampName in ampNames} 

182 self.covariancesSqrtWeights = {ampName: [] for ampName in ampNames} 

183 self.aMatrix = {ampName: np.nan for ampName in ampNames} 

184 self.bMatrix = {ampName: np.nan for ampName in ampNames} 

185 self.covariancesModelNoB = {ampName: [] for ampName in ampNames} 

186 self.aMatrixNoB = {ampName: np.nan for ampName in ampNames} 

187 

188 self.finalVars = {ampName: [] for ampName in ampNames} 

189 self.finalModelVars = {ampName: [] for ampName in ampNames} 

190 self.finalMeans = {ampName: [] for ampName in ampNames} 

191 

192 super().__init__(**kwargs) 

193 self.requiredAttributes.update(['badAmps', 'inputExpIdPairs', 'expIdMask', 'rawExpTimes', 

194 'rawMeans', 'rawVars', 'gain', 'gainErr', 'noise', 'noiseErr', 

195 'ptcFitPars', 'ptcFitParsError', 'ptcFitChiSq', 'aMatrixNoB', 

196 'covariances', 'covariancesModel', 'covariancesSqrtWeights', 

197 'covariancesModelNoB', 

198 'aMatrix', 'bMatrix', 'finalVars', 'finalModelVars', 'finalMeans', 

199 'photoCharge']) 

200 

201 def setAmpValues(self, ampName, inputExpIdPair=[(np.nan, np.nan)], expIdMask=[np.nan], 

202 rawExpTime=[np.nan], rawMean=[np.nan], rawVar=[np.nan], photoCharge=[np.nan], 

203 gain=np.nan, gainErr=np.nan, noise=np.nan, noiseErr=np.nan, ptcFitPars=[np.nan], 

204 ptcFitParsError=[np.nan], ptcFitChiSq=np.nan, covArray=[], covArrayModel=[], 

205 covSqrtWeights=[], aMatrix=[], bMatrix=[], covArrayModelNoB=[], aMatrixNoB=[], 

206 finalVar=[np.nan], finalModelVar=[np.nan], finalMean=[np.nan]): 

207 """Function to initialize an amp of a PhotonTransferCurveDataset. 

208 

209 Notes 

210 ----- 

211 The parameters are all documented in `init`. 

212 """ 

213 nanMatrix = np.full((self.covMatrixSide, self.covMatrixSide), np.nan) 

214 if len(covArray) == 0: 

215 covArray = [nanMatrix] 

216 if len(covArrayModel) == 0: 

217 covArrayModel = [nanMatrix] 

218 if len(covSqrtWeights) == 0: 

219 covSqrtWeights = [nanMatrix] 

220 if len(covArrayModelNoB) == 0: 

221 covArrayModelNoB = [nanMatrix] 

222 if len(aMatrix) == 0: 

223 aMatrix = nanMatrix 

224 if len(bMatrix) == 0: 

225 bMatrix = nanMatrix 

226 if len(aMatrixNoB) == 0: 

227 aMatrixNoB = nanMatrix 

228 

229 self.inputExpIdPairs[ampName] = inputExpIdPair 

230 self.expIdMask[ampName] = expIdMask 

231 self.rawExpTimes[ampName] = rawExpTime 

232 self.rawMeans[ampName] = rawMean 

233 self.rawVars[ampName] = rawVar 

234 self.photoCharge[ampName] = photoCharge 

235 self.gain[ampName] = gain 

236 self.gainErr[ampName] = gainErr 

237 self.noise[ampName] = noise 

238 self.noiseErr[ampName] = noiseErr 

239 self.ptcFitPars[ampName] = ptcFitPars 

240 self.ptcFitParsError[ampName] = ptcFitParsError 

241 self.ptcFitChiSq[ampName] 

242 self.covariances[ampName] = covArray 

243 self.covariancesSqrtWeights[ampName] = covSqrtWeights 

244 self.covariancesModel[ampName] = covArrayModel 

245 self.covariancesModelNoB[ampName] = covArrayModelNoB 

246 self.aMatrix[ampName] = aMatrix 

247 self.bMatrix[ampName] = bMatrix 

248 self.aMatrixNoB[ampName] = aMatrixNoB 

249 self.ptcFitPars[ampName] = ptcFitPars 

250 self.ptcFitParsError[ampName] = ptcFitParsError 

251 self.ptcFitChiSq[ampName] = ptcFitChiSq 

252 self.finalVars[ampName] = finalVar 

253 self.finalModelVars[ampName] = finalModelVar 

254 self.finalMeans[ampName] = finalMean 

255 

256 def updateMetadata(self, setDate=False, **kwargs): 

257 """Update calibration metadata. 

258 This calls the base class's method after ensuring the required 

259 calibration keywords will be saved. 

260 Parameters 

261 ---------- 

262 setDate : `bool`, optional 

263 Update the CALIBDATE fields in the metadata to the current 

264 time. Defaults to False. 

265 kwargs : 

266 Other keyword parameters to set in the metadata. 

267 """ 

268 kwargs['PTC_FIT_TYPE'] = self.ptcFitType 

269 

270 super().updateMetadata(setDate=setDate, **kwargs) 

271 

272 @classmethod 

273 def fromDict(cls, dictionary): 

274 """Construct a calibration from a dictionary of properties. 

275 Must be implemented by the specific calibration subclasses. 

276 Parameters 

277 ---------- 

278 dictionary : `dict` 

279 Dictionary of properties. 

280 Returns 

281 ------- 

282 calib : `lsst.ip.isr.CalibType` 

283 Constructed calibration. 

284 Raises 

285 ------ 

286 RuntimeError : 

287 Raised if the supplied dictionary is for a different 

288 calibration. 

289 """ 

290 calib = cls() 

291 if calib._OBSTYPE != dictionary['metadata']['OBSTYPE']: 

292 raise RuntimeError(f"Incorrect Photon Transfer Curve dataset supplied. " 

293 f"Expected {calib._OBSTYPE}, found {dictionary['metadata']['OBSTYPE']}") 

294 calib.setMetadata(dictionary['metadata']) 

295 calib.ptcFitType = dictionary['ptcFitType'] 

296 calib.covMatrixSide = dictionary['covMatrixSide'] 

297 calib.badAmps = np.array(dictionary['badAmps'], 'str').tolist() 

298 # The cov matrices are square 

299 covMatrixSide = calib.covMatrixSide 

300 # Number of final signal levels 

301 covDimensionsProduct = len(np.array(list(dictionary['covariances'].values())[0]).ravel()) 

302 nSignalPoints = int(covDimensionsProduct/(covMatrixSide*covMatrixSide)) 

303 

304 for ampName in dictionary['ampNames']: 

305 calib.ampNames.append(ampName) 

306 calib.inputExpIdPairs[ampName] = np.array(dictionary['inputExpIdPairs'][ampName]).tolist() 

307 calib.expIdMask[ampName] = np.array(dictionary['expIdMask'][ampName]).tolist() 

308 calib.rawExpTimes[ampName] = np.array(dictionary['rawExpTimes'][ampName]).tolist() 

309 calib.rawMeans[ampName] = np.array(dictionary['rawMeans'][ampName]).tolist() 

310 calib.rawVars[ampName] = np.array(dictionary['rawVars'][ampName]).tolist() 

311 calib.gain[ampName] = np.array(dictionary['gain'][ampName]).tolist() 

312 calib.gainErr[ampName] = np.array(dictionary['gainErr'][ampName]).tolist() 

313 calib.noise[ampName] = np.array(dictionary['noise'][ampName]).tolist() 

314 calib.noiseErr[ampName] = np.array(dictionary['noiseErr'][ampName]).tolist() 

315 calib.ptcFitPars[ampName] = np.array(dictionary['ptcFitPars'][ampName]).tolist() 

316 calib.ptcFitParsError[ampName] = np.array(dictionary['ptcFitParsError'][ampName]).tolist() 

317 calib.ptcFitChiSq[ampName] = np.array(dictionary['ptcFitChiSq'][ampName]).tolist() 

318 calib.covariances[ampName] = np.array(dictionary['covariances'][ampName]).reshape( 

319 (nSignalPoints, covMatrixSide, covMatrixSide)).tolist() 

320 calib.covariancesModel[ampName] = np.array( 

321 dictionary['covariancesModel'][ampName]).reshape( 

322 (nSignalPoints, covMatrixSide, covMatrixSide)).tolist() 

323 calib.covariancesSqrtWeights[ampName] = np.array( 

324 dictionary['covariancesSqrtWeights'][ampName]).reshape( 

325 (nSignalPoints, covMatrixSide, covMatrixSide)).tolist() 

326 calib.aMatrix[ampName] = np.array(dictionary['aMatrix'][ampName]).reshape( 

327 (covMatrixSide, covMatrixSide)).tolist() 

328 calib.bMatrix[ampName] = np.array(dictionary['bMatrix'][ampName]).reshape( 

329 (covMatrixSide, covMatrixSide)).tolist() 

330 calib.covariancesModelNoB[ampName] = np.array( 

331 dictionary['covariancesModelNoB'][ampName]).reshape( 

332 (nSignalPoints, covMatrixSide, covMatrixSide)).tolist() 

333 calib.aMatrixNoB[ampName] = np.array( 

334 dictionary['aMatrixNoB'][ampName]).reshape((covMatrixSide, covMatrixSide)).tolist() 

335 calib.finalVars[ampName] = np.array(dictionary['finalVars'][ampName]).tolist() 

336 calib.finalModelVars[ampName] = np.array(dictionary['finalModelVars'][ampName]).tolist() 

337 calib.finalMeans[ampName] = np.array(dictionary['finalMeans'][ampName]).tolist() 

338 calib.photoCharge[ampName] = np.array(dictionary['photoCharge'][ampName]).tolist() 

339 calib.updateMetadata() 

340 return calib 

341 

342 def toDict(self): 

343 """Return a dictionary containing the calibration properties. 

344 The dictionary should be able to be round-tripped through 

345 `fromDict`. 

346 Returns 

347 ------- 

348 dictionary : `dict` 

349 Dictionary of properties. 

350 """ 

351 self.updateMetadata() 

352 

353 outDict = dict() 

354 metadata = self.getMetadata() 

355 outDict['metadata'] = metadata 

356 

357 outDict['ptcFitType'] = self.ptcFitType 

358 outDict['covMatrixSide'] = self.covMatrixSide 

359 outDict['ampNames'] = self.ampNames 

360 outDict['badAmps'] = self.badAmps 

361 outDict['inputExpIdPairs'] = self.inputExpIdPairs 

362 outDict['expIdMask'] = self.expIdMask 

363 outDict['rawExpTimes'] = self.rawExpTimes 

364 outDict['rawMeans'] = self.rawMeans 

365 outDict['rawVars'] = self.rawVars 

366 outDict['gain'] = self.gain 

367 outDict['gainErr'] = self.gainErr 

368 outDict['noise'] = self.noise 

369 outDict['noiseErr'] = self.noiseErr 

370 outDict['ptcFitPars'] = self.ptcFitPars 

371 outDict['ptcFitParsError'] = self.ptcFitParsError 

372 outDict['ptcFitChiSq'] = self.ptcFitChiSq 

373 outDict['covariances'] = self.covariances 

374 outDict['covariancesModel'] = self.covariancesModel 

375 outDict['covariancesSqrtWeights'] = self.covariancesSqrtWeights 

376 outDict['aMatrix'] = self.aMatrix 

377 outDict['bMatrix'] = self.bMatrix 

378 outDict['covariancesModelNoB'] = self.covariancesModelNoB 

379 outDict['aMatrixNoB'] = self.aMatrixNoB 

380 outDict['finalVars'] = self.finalVars 

381 outDict['finalModelVars'] = self.finalModelVars 

382 outDict['finalMeans'] = self.finalMeans 

383 outDict['photoCharge'] = self.photoCharge 

384 

385 return outDict 

386 

387 @classmethod 

388 def fromTable(cls, tableList): 

389 """Construct calibration from a list of tables. 

390 This method uses the `fromDict` method to create the 

391 calibration, after constructing an appropriate dictionary from 

392 the input tables. 

393 Parameters 

394 ---------- 

395 tableList : `list` [`lsst.afw.table.Table`] 

396 List of tables to use to construct the datasetPtc. 

397 Returns 

398 ------- 

399 calib : `lsst.cp.pipe.` 

400 The calibration defined in the tables. 

401 """ 

402 ptcTable = tableList[0] 

403 

404 metadata = ptcTable.meta 

405 inDict = dict() 

406 inDict['metadata'] = metadata 

407 inDict['ampNames'] = [] 

408 inDict['ptcFitType'] = [] 

409 inDict['covMatrixSide'] = [] 

410 inDict['inputExpIdPairs'] = dict() 

411 inDict['expIdMask'] = dict() 

412 inDict['rawExpTimes'] = dict() 

413 inDict['rawMeans'] = dict() 

414 inDict['rawVars'] = dict() 

415 inDict['gain'] = dict() 

416 inDict['gainErr'] = dict() 

417 inDict['noise'] = dict() 

418 inDict['noiseErr'] = dict() 

419 inDict['ptcFitPars'] = dict() 

420 inDict['ptcFitParsError'] = dict() 

421 inDict['ptcFitChiSq'] = dict() 

422 inDict['covariances'] = dict() 

423 inDict['covariancesModel'] = dict() 

424 inDict['covariancesSqrtWeights'] = dict() 

425 inDict['aMatrix'] = dict() 

426 inDict['bMatrix'] = dict() 

427 inDict['covariancesModelNoB'] = dict() 

428 inDict['aMatrixNoB'] = dict() 

429 inDict['finalVars'] = dict() 

430 inDict['finalModelVars'] = dict() 

431 inDict['finalMeans'] = dict() 

432 inDict['badAmps'] = [] 

433 inDict['photoCharge'] = dict() 

434 

435 for record in ptcTable: 

436 ampName = record['AMPLIFIER_NAME'] 

437 

438 inDict['ptcFitType'] = record['PTC_FIT_TYPE'] 

439 inDict['covMatrixSide'] = record['COV_MATRIX_SIDE'] 

440 inDict['ampNames'].append(ampName) 

441 inDict['inputExpIdPairs'][ampName] = record['INPUT_EXP_ID_PAIRS'] 

442 inDict['expIdMask'][ampName] = record['EXP_ID_MASK'] 

443 inDict['rawExpTimes'][ampName] = record['RAW_EXP_TIMES'] 

444 inDict['rawMeans'][ampName] = record['RAW_MEANS'] 

445 inDict['rawVars'][ampName] = record['RAW_VARS'] 

446 inDict['gain'][ampName] = record['GAIN'] 

447 inDict['gainErr'][ampName] = record['GAIN_ERR'] 

448 inDict['noise'][ampName] = record['NOISE'] 

449 inDict['noiseErr'][ampName] = record['NOISE_ERR'] 

450 inDict['ptcFitPars'][ampName] = record['PTC_FIT_PARS'] 

451 inDict['ptcFitParsError'][ampName] = record['PTC_FIT_PARS_ERROR'] 

452 inDict['ptcFitChiSq'][ampName] = record['PTC_FIT_CHI_SQ'] 

453 inDict['covariances'][ampName] = record['COVARIANCES'] 

454 inDict['covariancesModel'][ampName] = record['COVARIANCES_MODEL'] 

455 inDict['covariancesSqrtWeights'][ampName] = record['COVARIANCES_SQRT_WEIGHTS'] 

456 inDict['aMatrix'][ampName] = record['A_MATRIX'] 

457 inDict['bMatrix'][ampName] = record['B_MATRIX'] 

458 inDict['covariancesModelNoB'][ampName] = record['COVARIANCES_MODEL_NO_B'] 

459 inDict['aMatrixNoB'][ampName] = record['A_MATRIX_NO_B'] 

460 inDict['finalVars'][ampName] = record['FINAL_VARS'] 

461 inDict['finalModelVars'][ampName] = record['FINAL_MODEL_VARS'] 

462 inDict['finalMeans'][ampName] = record['FINAL_MEANS'] 

463 inDict['badAmps'] = record['BAD_AMPS'] 

464 inDict['photoCharge'][ampName] = record['PHOTO_CHARGE'] 

465 return cls().fromDict(inDict) 

466 

467 def toTable(self): 

468 """Construct a list of tables containing the information in this 

469 calibration. 

470 

471 The list of tables should create an identical calibration 

472 after being passed to this class's fromTable method. 

473 Returns 

474 ------- 

475 tableList : `list` [`astropy.table.Table`] 

476 List of tables containing the linearity calibration 

477 information. 

478 """ 

479 tableList = [] 

480 self.updateMetadata() 

481 nPoints = [] 

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

483 nPoints.append(len(list(self.covariances.values())[i])) 

484 nSignalPoints = max(nPoints) 

485 nPadPoints = {} 

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

487 nPadPoints[ampName] = nSignalPoints - len(list(self.covariances.values())[i]) 

488 covMatrixSide = self.covMatrixSide 

489 

490 catalog = Table([{'AMPLIFIER_NAME': ampName, 

491 'PTC_FIT_TYPE': self.ptcFitType, 

492 'COV_MATRIX_SIDE': self.covMatrixSide, 

493 'INPUT_EXP_ID_PAIRS': self.inputExpIdPairs[ampName] 

494 if len(self.expIdMask[ampName]) else np.nan, 

495 'EXP_ID_MASK': self.expIdMask[ampName] 

496 if len(self.expIdMask[ampName]) else np.nan, 

497 'RAW_EXP_TIMES': np.array(self.rawExpTimes[ampName]).tolist() 

498 if len(self.rawExpTimes[ampName]) else np.nan, 

499 'RAW_MEANS': np.array(self.rawMeans[ampName]).tolist() 

500 if len(self.rawMeans[ampName]) else np.nan, 

501 'RAW_VARS': np.array(self.rawVars[ampName]).tolist() 

502 if len(self.rawVars[ampName]) else np.nan, 

503 'GAIN': self.gain[ampName], 

504 'GAIN_ERR': self.gainErr[ampName], 

505 'NOISE': self.noise[ampName], 

506 'NOISE_ERR': self.noiseErr[ampName], 

507 'PTC_FIT_PARS': np.array(self.ptcFitPars[ampName]).tolist(), 

508 'PTC_FIT_PARS_ERROR': np.array(self.ptcFitParsError[ampName]).tolist(), 

509 'PTC_FIT_CHI_SQ': self.ptcFitChiSq[ampName], 

510 'COVARIANCES': np.pad(np.array(self.covariances[ampName]), 

511 ((0, nPadPoints[ampName]), (0, 0), (0, 0)), 

512 'constant', constant_values=np.nan).reshape( 

513 nSignalPoints*covMatrixSide**2).tolist(), 

514 'COVARIANCES_MODEL': np.pad(np.array(self.covariancesModel[ampName]), 

515 ((0, nPadPoints[ampName]), (0, 0), (0, 0)), 

516 'constant', constant_values=np.nan).reshape( 

517 nSignalPoints*covMatrixSide**2).tolist(), 

518 'COVARIANCES_SQRT_WEIGHTS': np.pad(np.array(self.covariancesSqrtWeights[ampName]), 

519 ((0, nPadPoints[ampName]), (0, 0), (0, 0)), 

520 'constant', constant_values=0.0).reshape( 

521 nSignalPoints*covMatrixSide**2).tolist(), 

522 'A_MATRIX': np.array(self.aMatrix[ampName]).reshape(covMatrixSide**2).tolist(), 

523 'B_MATRIX': np.array(self.bMatrix[ampName]).reshape(covMatrixSide**2).tolist(), 

524 'COVARIANCES_MODEL_NO_B': 

525 np.pad(np.array(self.covariancesModelNoB[ampName]), 

526 ((0, nPadPoints[ampName]), (0, 0), (0, 0)), 

527 'constant', constant_values=np.nan).reshape( 

528 nSignalPoints*covMatrixSide**2).tolist(), 

529 'A_MATRIX_NO_B': np.array(self.aMatrixNoB[ampName]).reshape( 

530 covMatrixSide**2).tolist(), 

531 'FINAL_VARS': np.pad(np.array(self.finalVars[ampName]), (0, nPadPoints[ampName]), 

532 'constant', constant_values=np.nan).tolist(), 

533 'FINAL_MODEL_VARS': np.pad(np.array(self.finalModelVars[ampName]), 

534 (0, nPadPoints[ampName]), 

535 'constant', constant_values=np.nan).tolist(), 

536 'FINAL_MEANS': np.pad(np.array(self.finalMeans[ampName]), 

537 (0, nPadPoints[ampName]), 

538 'constant', constant_values=np.nan).tolist(), 

539 'BAD_AMPS': np.array(self.badAmps).tolist() if len(self.badAmps) else np.nan, 

540 'PHOTO_CHARGE': np.array(self.photoCharge[ampName]).tolist(), 

541 } for ampName in self.ampNames]) 

542 inMeta = self.getMetadata().toDict() 

543 outMeta = {k: v for k, v in inMeta.items() if v is not None} 

544 outMeta.update({k: "" for k, v in inMeta.items() if v is None}) 

545 catalog.meta = outMeta 

546 tableList.append(catalog) 

547 

548 return(tableList) 

549 

550 def getExpIdsUsed(self, ampName): 

551 """Get the exposures used, i.e. not discarded, for a given amp. 

552 If no mask has been created yet, all exposures are returned. 

553 """ 

554 if len(self.expIdMask[ampName]) == 0: 

555 return self.inputExpIdPairs[ampName] 

556 

557 # if the mask exists it had better be the same length as the expIdPairs 

558 assert len(self.expIdMask[ampName]) == len(self.inputExpIdPairs[ampName]) 

559 

560 pairs = self.inputExpIdPairs[ampName] 

561 mask = self.expIdMask[ampName] 

562 # cast to bool required because numpy 

563 return [(exp1, exp2) for ((exp1, exp2), m) in zip(pairs, mask) if bool(m) is True] 

564 

565 def getGoodAmps(self): 

566 return [amp for amp in self.ampNames if amp not in self.badAmps]