Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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 Parameters 

49 ---------- 

50 ampNames : `list` 

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

52 ptcFitType : `str` 

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

54 or "FULLCOVARIANCE". 

55 kwargs : `dict`, optional 

56 Other keyword arguments to pass to the parent init. 

57 Notes 

58 ----- 

59 The stored attributes are: 

60 badAmps : `list` 

61 List with bad amplifiers names. 

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

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

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

65 Dictionary keyed by amp names containing the mask produced after 

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

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

68 fit types. 

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

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

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

72 Dictionary keyed by amp namescontaining the unmasked average of the 

73 means of the exposures in each flat pair. 

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

75 Dictionary keyed by amp names containing the variance of the 

76 difference image of the exposures in each flat pair. 

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

78 Dictionary keyed by amp names containing the fitted gains. 

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

80 Dictionary keyed by amp names containing the errors on the 

81 fitted gains. 

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

83 Dictionary keyed by amp names containing the fitted noise. 

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

85 Dictionary keyed by amp names containing the errors on the fitted noise. 

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

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

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

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

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

91 parameters of the PTC model for ptcFitTye in 

92 ["POLYNOMIAL", "EXPAPPROXIMATION"]. 

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

94 Dictionary keyed by amp names containing the reduced chi squared 

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

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

97 Dictionary keyed by amp names containing a list of measured 

98 covariances per mean flux. 

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

100 Dictionary keyed by amp names containinging covariances model 

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

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

103 Dictionary keyed by amp names containinging sqrt. of covariances 

104 weights. 

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

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

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

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

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

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

111 covariancesNoB : `dict`, [`str`, `list`] 

112 Dictionary keyed by amp names containing a list of measured 

113 covariances per mean flux ('b'=0 in 

114 Astier+19). 

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

116 Dictionary keyed by amp names containing covariances model 

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

118 per mean flux. 

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

120 Dictionary keyed by amp names containing sqrt. of covariances weights 

121 ('b' = 0 in Eq. 20 of 

122 Astier+19). 

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, **kwargs): 

157 

158 self.ptcFitType = ptcFitType 

159 self.ampNames = ampNames 

160 self.badAmps = [] 

161 

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

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

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

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

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

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

168 

169 self.gain = {ampName: -1. for ampName in ampNames} 

170 self.gainErr = {ampName: -1. for ampName in ampNames} 

171 self.noise = {ampName: -1. for ampName in ampNames} 

172 self.noiseErr = {ampName: -1. for ampName in ampNames} 

173 

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

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

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

177 

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

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

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

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

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

183 self.covariancesNoB = {ampName: [] for ampName in ampNames} 

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

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

186 self.aMatrixNoB = {ampName: [] 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 'covariancesNoB', 'covariancesModelNoB', 'covariancesSqrtWeightsNoB', 

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

199 'photoCharge']) 

200 

201 def __eq__(self, other): 

202 """Calibration equivalence 

203 """ 

204 if not isinstance(other, self.__class__): 

205 return False 

206 

207 for attr in self._requiredAttributes: 

208 attrSelf = getattr(self, attr) 

209 attrOther = getattr(other, attr) 

210 if isinstance(attrSelf, dict) and isinstance(attrOther, dict): 

211 for ampName in attrSelf: 

212 if not np.allclose(attrSelf[ampName], attrOther[ampName], equal_nan=True): 

213 return False 

214 else: 

215 if attrSelf != attrOther: 

216 return False 

217 return True 

218 

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

220 """Update calibration metadata. 

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

222 calibration keywords will be saved. 

223 Parameters 

224 ---------- 

225 setDate : `bool`, optional 

226 Update the CALIBDATE fields in the metadata to the current 

227 time. Defaults to False. 

228 kwargs : 

229 Other keyword parameters to set in the metadata. 

230 """ 

231 kwargs['PTC_FIT_TYPE'] = self.ptcFitType 

232 

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

234 

235 @classmethod 

236 def fromDict(cls, dictionary): 

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

238 Must be implemented by the specific calibration subclasses. 

239 Parameters 

240 ---------- 

241 dictionary : `dict` 

242 Dictionary of properties. 

243 Returns 

244 ------- 

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

246 Constructed calibration. 

247 Raises 

248 ------ 

249 RuntimeError : 

250 Raised if the supplied dictionary is for a different 

251 calibration. 

252 """ 

253 calib = cls() 

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

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

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

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

258 calib.ptcFitType = dictionary['ptcFitType'] 

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

260 # Number of final signal levels 

261 nSignalPoints = len(list(dictionary['rawMeans'].values())[0]) 

262 # The cov matrices are square 

263 covMatrixSide = int(np.sqrt(len(np.array(list(dictionary['aMatrix'].values())[0]).ravel()))) 

264 for ampName in dictionary['ampNames']: 

265 calib.ampNames.append(ampName) 

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

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

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

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

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

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

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

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

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

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

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

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

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

279 (nSignalPoints, covMatrixSide, covMatrixSide)).tolist() 

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

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

282 (nSignalPoints, covMatrixSide, covMatrixSide)).tolist() 

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

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

285 (nSignalPoints, covMatrixSide, covMatrixSide)).tolist() 

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

287 (covMatrixSide, covMatrixSide)).tolist() 

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

289 (covMatrixSide, covMatrixSide)).tolist() 

290 calib.covariancesNoB[ampName] = np.array( 

291 dictionary['covariancesNoB'][ampName]).reshape( 

292 (nSignalPoints, covMatrixSide, covMatrixSide)).tolist() 

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

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

295 (nSignalPoints, covMatrixSide, covMatrixSide)).tolist() 

296 calib.covariancesSqrtWeightsNoB[ampName] = np.array( 

297 dictionary['covariancesSqrtWeightsNoB'][ampName]).reshape( 

298 (nSignalPoints, covMatrixSide, covMatrixSide)).tolist() 

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

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

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

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

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

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

305 calib.updateMetadata() 

306 return calib 

307 

308 def toDict(self): 

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

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

311 `fromDict`. 

312 Returns 

313 ------- 

314 dictionary : `dict` 

315 Dictionary of properties. 

316 """ 

317 self.updateMetadata() 

318 

319 outDict = dict() 

320 metadata = self.getMetadata() 

321 outDict['metadata'] = metadata 

322 

323 outDict['ptcFitType'] = self.ptcFitType 

324 outDict['ampNames'] = self.ampNames 

325 outDict['badAmps'] = self.badAmps 

326 outDict['inputExpIdPairs'] = self.inputExpIdPairs 

327 outDict['expIdMask'] = self.expIdMask 

328 outDict['rawExpTimes'] = self.rawExpTimes 

329 outDict['rawMeans'] = self.rawMeans 

330 outDict['rawVars'] = self.rawVars 

331 outDict['gain'] = self.gain 

332 outDict['gainErr'] = self.gainErr 

333 outDict['noise'] = self.noise 

334 outDict['noiseErr'] = self.noiseErr 

335 outDict['ptcFitPars'] = self.ptcFitPars 

336 outDict['ptcFitParsError'] = self.ptcFitParsError 

337 outDict['ptcFitChiSq'] = self.ptcFitChiSq 

338 outDict['covariances'] = self.covariances 

339 outDict['covariancesModel'] = self.covariancesModel 

340 outDict['covariancesSqrtWeights'] = self.covariancesSqrtWeights 

341 outDict['aMatrix'] = self.aMatrix 

342 outDict['bMatrix'] = self.bMatrix 

343 outDict['covariancesNoB'] = self.covariancesNoB 

344 outDict['covariancesModelNoB'] = self.covariancesModelNoB 

345 outDict['covariancesSqrtWeightsNoB'] = self.covariancesSqrtWeightsNoB 

346 outDict['aMatrixNoB'] = self.aMatrixNoB 

347 outDict['finalVars'] = self.finalVars 

348 outDict['finalModelVars'] = self.finalModelVars 

349 outDict['finalMeans'] = self.finalMeans 

350 outDict['photoCharge'] = self.photoCharge 

351 

352 return outDict 

353 

354 @classmethod 

355 def fromTable(cls, tableList): 

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

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

358 calibration, after constructing an appropriate dictionary from 

359 the input tables. 

360 Parameters 

361 ---------- 

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

363 List of tables to use to construct the datasetPtc. 

364 Returns 

365 ------- 

366 calib : `lsst.cp.pipe.` 

367 The calibration defined in the tables. 

368 """ 

369 ptcTable = tableList[0] 

370 

371 metadata = ptcTable.meta 

372 inDict = dict() 

373 inDict['metadata'] = metadata 

374 inDict['ampNames'] = [] 

375 inDict['ptcFitType'] = [] 

376 inDict['inputExpIdPairs'] = dict() 

377 inDict['expIdMask'] = dict() 

378 inDict['rawExpTimes'] = dict() 

379 inDict['rawMeans'] = dict() 

380 inDict['rawVars'] = dict() 

381 inDict['gain'] = dict() 

382 inDict['gainErr'] = dict() 

383 inDict['noise'] = dict() 

384 inDict['noiseErr'] = dict() 

385 inDict['ptcFitPars'] = dict() 

386 inDict['ptcFitParsError'] = dict() 

387 inDict['ptcFitChiSq'] = dict() 

388 inDict['covariances'] = dict() 

389 inDict['covariancesModel'] = dict() 

390 inDict['covariancesSqrtWeights'] = dict() 

391 inDict['aMatrix'] = dict() 

392 inDict['bMatrix'] = dict() 

393 inDict['covariancesNoB'] = dict() 

394 inDict['covariancesModelNoB'] = dict() 

395 inDict['covariancesSqrtWeightsNoB'] = dict() 

396 inDict['aMatrixNoB'] = dict() 

397 inDict['finalVars'] = dict() 

398 inDict['finalModelVars'] = dict() 

399 inDict['finalMeans'] = dict() 

400 inDict['badAmps'] = [] 

401 inDict['photoCharge'] = dict() 

402 

403 for record in ptcTable: 

404 ampName = record['AMPLIFIER_NAME'] 

405 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

425 inDict['covariancesNoB'][ampName] = record['COVARIANCES_NO_B'] 

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

427 inDict['covariancesSqrtWeightsNoB'][ampName] = record['COVARIANCES_SQRT_WEIGHTS_NO_B'] 

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

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

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

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

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

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

434 return cls().fromDict(inDict) 

435 

436 def toTable(self): 

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

438 calibration. 

439 

440 The list of tables should create an identical calibration 

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

442 Returns 

443 ------- 

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

445 List of tables containing the linearity calibration 

446 information. 

447 """ 

448 tableList = [] 

449 self.updateMetadata() 

450 nSignalPoints = len(list(self.rawExpTimes.values())[0]) 

451 covMatrixSide = np.array(list(self.aMatrix.values())[0]).shape[0] 

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

453 'PTC_FIT_TYPE': self.ptcFitType, 

454 'INPUT_EXP_ID_PAIRS': self.inputExpIdPairs[ampName], 

455 'EXP_ID_MASK': self.expIdMask[ampName], 

456 'RAW_EXP_TIMES': self.rawExpTimes[ampName], 

457 'RAW_MEANS': self.rawMeans[ampName], 

458 'RAW_VARS': self.rawVars[ampName], 

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

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

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

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

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

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

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

466 'COVARIANCES': np.array(self.covariances[ampName]).reshape( 

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

468 'COVARIANCES_MODEL': 

469 np.array(self.covariancesModel[ampName]).reshape( 

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

471 'COVARIANCES_SQRT_WEIGHTS': 

472 np.array(self.covariancesSqrtWeights[ampName]).reshape( 

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

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

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

476 'COVARIANCES_NO_B': 

477 np.array(self.covariancesNoB[ampName]).reshape( 

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

479 'COVARIANCES_MODEL_NO_B': 

480 np.array(self.covariancesModelNoB[ampName]).reshape( 

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

482 'COVARIANCES_SQRT_WEIGHTS_NO_B': 

483 np.array(self.covariancesSqrtWeightsNoB[ampName]).reshape( 

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

485 'A_MATRIX_NO_B': 

486 np.array(self.aMatrixNoB[ampName]).reshape(covMatrixSide**2).tolist(), 

487 'FINAL_VARS': self.finalVars[ampName], 

488 'FINAL_MODEL_VARS': self.finalModelVars[ampName], 

489 'FINAL_MEANS': self.finalMeans[ampName], 

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

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

492 } for ampName in self.ampNames]) 

493 

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

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

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

497 catalog.meta = outMeta 

498 tableList.append(catalog) 

499 

500 return(tableList) 

501 

502 def getExpIdsUsed(self, ampName): 

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

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

505 """ 

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

507 return self.inputExpIdPairs[ampName] 

508 

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

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

511 

512 pairs = self.inputExpIdPairs[ampName] 

513 mask = self.expIdMask[ampName] 

514 # cast to bool required because numpy 

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

516 

517 def getGoodAmps(self): 

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