367 inputExpIdPair=(-1, -1),
368 inputExpPairMjdStart=np.nan,
373 rowMeanVariance=np.nan,
375 photoChargeDelta=np.nan,
383 overscanMedianLevel=np.nan,
389 Set the amp values for a partial PTC Dataset (from cpExtractPtcTask).
394 Name of the amp to set the values.
395 inputExpIdPair : `tuple` [`int`]
396 Exposure IDs of input pair.
397 inputExpPairMjdStart : `float`, optional
398 The start MJD of first exposure in the flat pair.
399 rawExpTime : `float`, optional
400 Exposure time for this exposure pair (units: sec).
401 rawMean : `float`, optional
402 Average of the means of the exposures in this pair
404 rawVar : `float`, optional
405 Variance of the difference of the exposures in this pair
407 rawDelta : `float`, optional
408 Delta of the means of the exposure in this pair
410 rowMeanVariance : `float`, optional
411 Variance of the means of the rows in the difference image
412 of the exposures in this pair (units: adu^2).
413 photoCharge : `float`, optional
414 Integrated photocharge for flat pair for linearity calibration
416 photoChargeDelta : `float`, optional
417 Delta between integrated photocharge for the flat pair
419 ampOffset : `float`, optional
420 Amp offset for this amplifier.
421 expIdMask : `bool`, optional
422 Flag setting if this exposure pair should be used (True)
424 nPixelCovariance : `int`, optional
425 Number of pixels that went into the covariance measurement.
426 covariance : `np.ndarray` or None, optional
427 Measured covariance for this exposure pair (units: adu^2).
428 covSqrtWeights : `np.ndarray` or None, optional
429 Measured sqrt of covariance weights in this exposure pair
431 gain : `float`, optional
432 Estimated gain for this exposure pair (units: electron/adu).
433 noise : `float`, optional
434 Estimated read noise for this exposure pair (units: electron).
435 overscanMedianLevel : `float`, optional
436 Average of the median overscan levels for this exposure pair.
438 histVar : `float`, optional
439 Variance estimated from fitting a histogram with a Gaussian model
441 histChi2Dof : `float`, optional
442 Chi-squared per degree of freedom from Gaussian histogram fit.
443 kspValue : `float`, optional
444 KS test p-value from the Gaussian histogram fit.
449 if covariance
is None:
450 covariance = nanMatrix
451 if covSqrtWeights
is None:
452 covSqrtWeights = nanMatrix
457 self.
rawMeans[ampName] = np.array([rawMean])
458 self.
rawVars[ampName] = np.array([rawVar])
459 self.
rawDeltas[ampName] = np.array([rawDelta])
463 self.
ampOffsets[ampName] = np.array([ampOffset])
464 self.
expIdMask[ampName] = np.array([expIdMask])
468 self.
gain[ampName] = gain
470 self.
gainList[ampName] = np.array([gain])
471 self.
noise[ampName] = noise
472 self.
noiseList[ampName] = np.array([noise])
476 self.
histVars[ampName] = np.array([histVar])
478 self.
kspValues[ampName] = np.array([kspValue])
482 self.
aMatrix[ampName] = nanMatrixFit
483 self.
bMatrix[ampName] = nanMatrixFit
486 self.
finalVars[ampName] = np.array([np.nan])
524 """Construct a calibration from a dictionary of properties.
525 Must be implemented by the specific calibration subclasses.
530 Dictionary of properties.
534 calib : `lsst.ip.isr.PhotonTransferCurveDataset`
535 Constructed calibration.
540 Raised if the supplied dictionary is for a different
544 if calib._OBSTYPE != dictionary[
'metadata'][
'OBSTYPE']:
545 raise RuntimeError(f
"Incorrect Photon Transfer Curve dataset supplied. "
546 f
"Expected {calib._OBSTYPE}, found {dictionary['metadata']['OBSTYPE']}")
547 calib.setMetadata(dictionary[
'metadata'])
548 calib.ptcFitType = dictionary[
'ptcFitType']
549 calib.covMatrixSide = dictionary[
'covMatrixSide']
550 calib.covMatrixSideFullCovFit = dictionary[
'covMatrixSideFullCovFit']
551 calib.badAmps = np.array(dictionary[
'badAmps'],
'str').tolist()
555 covMatrixSide = calib.covMatrixSide
556 covMatrixSideFullCovFit = calib.covMatrixSideFullCovFit
558 covDimensionsProduct = len(np.array(list(dictionary[
'covariances'].values())[0]).ravel())
559 nSignalPoints = int(covDimensionsProduct/(covMatrixSide*covMatrixSide))
561 for ampName
in dictionary[
'ampNames']:
562 calib.ampNames.append(ampName)
563 calib.inputExpIdPairs[ampName] = dictionary[
'inputExpIdPairs'][ampName]
564 calib.inputExpPairMjdStartList[ampName] = np.array(
565 dictionary[
'inputExpPairMjdStartList'][ampName],
567 calib.expIdMask[ampName] = np.array(dictionary[
'expIdMask'][ampName])
568 calib.rawExpTimes[ampName] = np.array(dictionary[
'rawExpTimes'][ampName], dtype=np.float64)
569 calib.rawMeans[ampName] = np.array(dictionary[
'rawMeans'][ampName], dtype=np.float64)
570 calib.rawVars[ampName] = np.array(dictionary[
'rawVars'][ampName], dtype=np.float64)
571 calib.rawDeltas[ampName] = np.array(dictionary[
'rawDeltas'][ampName], dtype=np.float64)
572 calib.rowMeanVariance[ampName] = np.array(dictionary[
'rowMeanVariance'][ampName],
574 calib.gain[ampName] = float(dictionary[
'gain'][ampName])
575 calib.gainErr[ampName] = float(dictionary[
'gainErr'][ampName])
576 calib.gainUnadjusted[ampName] = float(dictionary[
'gainUnadjusted'][ampName])
577 calib.gainList[ampName] = np.array(dictionary[
'gainList'][ampName], dtype=np.float64)
578 calib.noiseList[ampName] = np.array(dictionary[
'noiseList'][ampName], dtype=np.float64)
579 calib.nPixelCovariances[ampName] = int(dictionary[
'nPixelCovariances'][ampName])
580 calib.overscanMedianLevelList[ampName] = np.array(
581 dictionary[
'overscanMedianLevelList'][ampName],
584 calib.overscanMedian[ampName] = float(dictionary[
'overscanMedian'][ampName])
585 calib.overscanMedianSigma[ampName] = float(dictionary[
'overscanMedianSigma'][ampName])
586 calib.noise[ampName] = float(dictionary[
'noise'][ampName])
587 calib.noiseErr[ampName] = float(dictionary[
'noiseErr'][ampName])
588 calib.histVars[ampName] = np.array(dictionary[
'histVars'][ampName], dtype=np.float64)
589 calib.histChi2Dofs[ampName] = np.array(dictionary[
'histChi2Dofs'][ampName], dtype=np.float64)
590 calib.kspValues[ampName] = np.array(dictionary[
'kspValues'][ampName], dtype=np.float64)
591 calib.ptcFitPars[ampName] = np.array(dictionary[
'ptcFitPars'][ampName], dtype=np.float64)
592 calib.ptcFitParsError[ampName] = np.array(dictionary[
'ptcFitParsError'][ampName],
594 calib.ptcFitChiSq[ampName] = float(dictionary[
'ptcFitChiSq'][ampName])
595 calib.ptcTurnoff[ampName] = float(dictionary[
'ptcTurnoff'][ampName])
596 calib.ptcTurnoffSamplingError[ampName] = float(dictionary[
'ptcTurnoffSamplingError'][ampName])
597 if nSignalPoints > 0:
599 calib.covariances[ampName] = np.array(dictionary[
'covariances'][ampName],
600 dtype=np.float64).reshape(
601 (nSignalPoints, covMatrixSide, covMatrixSide))
602 calib.covariancesModel[ampName] = np.array(
603 dictionary[
'covariancesModel'][ampName],
604 dtype=np.float64).reshape(
605 (nSignalPoints, covMatrixSideFullCovFit, covMatrixSideFullCovFit))
606 calib.covariancesSqrtWeights[ampName] = np.array(
607 dictionary[
'covariancesSqrtWeights'][ampName],
608 dtype=np.float64).reshape(
609 (nSignalPoints, covMatrixSide, covMatrixSide))
610 calib.aMatrix[ampName] = np.array(dictionary[
'aMatrix'][ampName],
611 dtype=np.float64).reshape(
612 (covMatrixSideFullCovFit, covMatrixSideFullCovFit))
613 calib.bMatrix[ampName] = np.array(dictionary[
'bMatrix'][ampName],
614 dtype=np.float64).reshape(
615 (covMatrixSideFullCovFit, covMatrixSideFullCovFit))
616 calib.noiseMatrix[ampName] = np.array(
617 dictionary[
'noiseMatrix'][ampName],
618 dtype=np.float64).reshape((covMatrixSideFullCovFit, covMatrixSideFullCovFit))
621 calib.covariances[ampName] = np.array([], dtype=np.float64)
622 calib.covariancesModel[ampName] = np.array([], dtype=np.float64)
623 calib.covariancesSqrtWeights[ampName] = np.array([], dtype=np.float64)
624 calib.aMatrix[ampName] = np.array([], dtype=np.float64)
625 calib.bMatrix[ampName] = np.array([], dtype=np.float64)
626 calib.noiseMatrix[ampName] = np.array([], dtype=np.float64)
628 calib.finalVars[ampName] = np.array(dictionary[
'finalVars'][ampName], dtype=np.float64)
629 calib.finalModelVars[ampName] = np.array(dictionary[
'finalModelVars'][ampName], dtype=np.float64)
630 calib.finalMeans[ampName] = np.array(dictionary[
'finalMeans'][ampName], dtype=np.float64)
631 calib.photoCharges[ampName] = np.array(dictionary[
'photoCharges'][ampName], dtype=np.float64)
632 calib.photoChargeDeltas[ampName] = np.array(
633 dictionary[
'photoChargeDeltas'][ampName],
636 calib.ampOffsets[ampName] = np.array(dictionary[
'ampOffsets'][ampName], dtype=np.float64)
638 for key, value
in dictionary[
'auxValues'].items():
639 if isinstance(value[0], numbers.Integral):
640 calib.auxValues[key] = np.atleast_1d(np.asarray(value).astype(np.int64))
641 elif isinstance(value[0], (str, np.str_, np.bytes_)):
642 calib.auxValues[key] = np.atleast_1d(np.asarray(value))
644 calib.auxValues[key] = np.atleast_1d(np.array(value, dtype=np.float64))
646 calib.updateMetadata()
650 """Return a dictionary containing the calibration properties.
651 The dictionary should be able to be round-tripped through
657 Dictionary of properties.
663 outDict[
'metadata'] = metadata
665 def _dictOfArraysToDictOfLists(dictOfArrays):
667 for key, value
in dictOfArrays.items():
668 dictOfLists[key] = value.ravel().tolist()
676 outDict[
'badAmps'] = self.
badAmps
679 outDict[
'expIdMask'] = _dictOfArraysToDictOfLists(self.
expIdMask)
680 outDict[
'rawExpTimes'] = _dictOfArraysToDictOfLists(self.
rawExpTimes)
681 outDict[
'rawMeans'] = _dictOfArraysToDictOfLists(self.
rawMeans)
682 outDict[
'rawVars'] = _dictOfArraysToDictOfLists(self.
rawVars)
683 outDict[
'rawDeltas'] = _dictOfArraysToDictOfLists(self.
rawDeltas)
684 outDict[
'rowMeanVariance'] = _dictOfArraysToDictOfLists(self.
rowMeanVariance)
685 outDict[
'gain'] = self.
gain
686 outDict[
'gainErr'] = self.
gainErr
688 outDict[
'gainList'] = _dictOfArraysToDictOfLists(self.
gainList)
689 outDict[
'noiseList'] = _dictOfArraysToDictOfLists(self.
noiseList)
693 outDict[
'noise'] = self.
noise
698 outDict[
'ptcFitPars'] = _dictOfArraysToDictOfLists(self.
ptcFitPars)
699 outDict[
'ptcFitParsError'] = _dictOfArraysToDictOfLists(self.
ptcFitParsError)
704 outDict[
'covariances'] = _dictOfArraysToDictOfLists(self.
covariances)
705 outDict[
'covariancesModel'] = _dictOfArraysToDictOfLists(self.
covariancesModel)
707 outDict[
'aMatrix'] = _dictOfArraysToDictOfLists(self.
aMatrix)
708 outDict[
'bMatrix'] = _dictOfArraysToDictOfLists(self.
bMatrix)
709 outDict[
'noiseMatrix'] = _dictOfArraysToDictOfLists(self.
noiseMatrix)
710 outDict[
'finalVars'] = _dictOfArraysToDictOfLists(self.
finalVars)
711 outDict[
'finalModelVars'] = _dictOfArraysToDictOfLists(self.
finalModelVars)
712 outDict[
'finalMeans'] = _dictOfArraysToDictOfLists(self.
finalMeans)
713 outDict[
'photoCharges'] = _dictOfArraysToDictOfLists(self.
photoCharges)
714 outDict[
'photoChargeDeltas'] = _dictOfArraysToDictOfLists(self.
photoChargeDeltas)
715 outDict[
'ampOffsets'] = _dictOfArraysToDictOfLists(self.
ampOffsets)
716 outDict[
'auxValues'] = _dictOfArraysToDictOfLists(self.
auxValues)
722 """Construct calibration from a list of tables.
723 This method uses the `fromDict` method to create the
724 calibration, after constructing an appropriate dictionary from
729 tableList : `list` [`lsst.afw.table.Table`]
730 List of tables to use to construct the datasetPtc.
734 calib : `lsst.ip.isr.PhotonTransferCurveDataset`
735 The calibration defined in the tables.
737 ptcTable = tableList[0]
739 metadata = ptcTable.meta
741 inDict[
'metadata'] = metadata
742 inDict[
'ampNames'] = []
743 inDict[
'ptcFitType'] = []
744 inDict[
'covMatrixSide'] = []
745 inDict[
'covMatrixSideFullCovFit'] = []
746 inDict[
'inputExpIdPairs'] = dict()
747 inDict[
'inputExpPairMjdStartList'] = dict()
748 inDict[
'expIdMask'] = dict()
749 inDict[
'rawExpTimes'] = dict()
750 inDict[
'rawMeans'] = dict()
751 inDict[
'rawVars'] = dict()
752 inDict[
'rawDeltas'] = dict()
753 inDict[
'rowMeanVariance'] = dict()
754 inDict[
'gain'] = dict()
755 inDict[
'gainErr'] = dict()
756 inDict[
'gainUnadjusted'] = dict()
757 inDict[
'gainList'] = dict()
758 inDict[
'noiseList'] = dict()
759 inDict[
'overscanMedianLevelList'] = dict()
760 inDict[
'overscanMedian'] = dict()
761 inDict[
'overscanMedianSigma'] = dict()
762 inDict[
'noise'] = dict()
763 inDict[
'noiseErr'] = dict()
764 inDict[
'histVars'] = dict()
765 inDict[
'histChi2Dofs'] = dict()
766 inDict[
'kspValues'] = dict()
767 inDict[
'ptcFitPars'] = dict()
768 inDict[
'ptcFitParsError'] = dict()
769 inDict[
'ptcFitChiSq'] = dict()
770 inDict[
'ptcTurnoff'] = dict()
771 inDict[
'ptcTurnoffSamplingError'] = dict()
772 inDict[
'nPixelCovariances'] = dict()
773 inDict[
'covariances'] = dict()
774 inDict[
'covariancesModel'] = dict()
775 inDict[
'covariancesSqrtWeights'] = dict()
776 inDict[
'aMatrix'] = dict()
777 inDict[
'bMatrix'] = dict()
778 inDict[
'noiseMatrix'] = dict()
779 inDict[
'finalVars'] = dict()
780 inDict[
'finalModelVars'] = dict()
781 inDict[
'finalMeans'] = dict()
782 inDict[
'badAmps'] = []
783 inDict[
'photoCharges'] = dict()
784 inDict[
'photoChargeDeltas'] = dict()
785 inDict[
'ampOffsets'] = dict()
788 inDict[
'noiseMatrixNoB'] = dict()
789 inDict[
'covariancesModelNoB'] = dict()
790 inDict[
'aMatrixNoB'] = dict()
792 calibVersion = metadata[
'PTC_VERSION']
793 if calibVersion == 1.0:
794 cls().log.warning(f
"Previous version found for PTC dataset: {calibVersion}. "
795 f
"Setting 'ptcTurnoff' in all amps to last value in 'finalMeans'.")
796 for record
in ptcTable:
797 ampName = record[
'AMPLIFIER_NAME']
799 inDict[
'ptcFitType'] = record[
'PTC_FIT_TYPE']
800 inDict[
'covMatrixSide'] = record[
'COV_MATRIX_SIDE']
801 inDict[
'ampNames'].append(ampName)
802 inDict[
'inputExpIdPairs'][ampName] = record[
'INPUT_EXP_ID_PAIRS'].tolist()
803 inDict[
'expIdMask'][ampName] = record[
'EXP_ID_MASK']
804 inDict[
'rawExpTimes'][ampName] = record[
'RAW_EXP_TIMES']
805 inDict[
'rawMeans'][ampName] = record[
'RAW_MEANS']
806 inDict[
'rawVars'][ampName] = record[
'RAW_VARS']
807 inDict[
'gain'][ampName] = record[
'GAIN']
808 inDict[
'gainErr'][ampName] = record[
'GAIN_ERR']
809 inDict[
'noise'][ampName] = record[
'NOISE']
810 inDict[
'noiseErr'][ampName] = record[
'NOISE_ERR']
811 inDict[
'ptcFitPars'][ampName] = record[
'PTC_FIT_PARS']
812 inDict[
'ptcFitParsError'][ampName] = record[
'PTC_FIT_PARS_ERROR']
813 inDict[
'ptcFitChiSq'][ampName] = record[
'PTC_FIT_CHI_SQ']
814 inDict[
'covariances'][ampName] = record[
'COVARIANCES']
815 inDict[
'covariancesModel'][ampName] = record[
'COVARIANCES_MODEL']
816 inDict[
'covariancesSqrtWeights'][ampName] = record[
'COVARIANCES_SQRT_WEIGHTS']
817 inDict[
'aMatrix'][ampName] = record[
'A_MATRIX']
818 inDict[
'bMatrix'][ampName] = record[
'B_MATRIX']
819 inDict[
'finalVars'][ampName] = record[
'FINAL_VARS']
820 inDict[
'finalModelVars'][ampName] = record[
'FINAL_MODEL_VARS']
821 inDict[
'finalMeans'][ampName] = record[
'FINAL_MEANS']
822 inDict[
'badAmps'] = record[
'BAD_AMPS'].tolist()
823 inDict[
'photoCharges'][ampName] = record[
'PHOTO_CHARGE']
824 if calibVersion == 1.0:
825 mask = record[
'FINAL_MEANS'].mask
826 array = record[
'FINAL_MEANS'][~mask]
828 inDict[
'ptcTurnoff'][ampName] = record[
'FINAL_MEANS'][~mask][-1]
830 inDict[
'ptcTurnoff'][ampName] = np.nan
832 inDict[
'ptcTurnoff'][ampName] = record[
'PTC_TURNOFF']
833 if calibVersion < 1.2:
834 inDict[
'histVars'][ampName] = np.array([np.nan])
835 inDict[
'histChi2Dofs'][ampName] = np.array([np.nan])
836 inDict[
'kspValues'][ampName] = np.array([0.0])
838 inDict[
'histVars'][ampName] = record[
'HIST_VARS']
839 inDict[
'histChi2Dofs'][ampName] = record[
'HIST_CHI2_DOFS']
840 inDict[
'kspValues'][ampName] = record[
'KS_PVALUES']
841 if calibVersion < 1.3:
842 nanMatrix = np.full_like(inDict[
'aMatrix'][ampName], np.nan)
843 inDict[
'noiseMatrix'][ampName] = nanMatrix
844 inDict[
'noiseMatrixNoB'][ampName] = nanMatrix
845 elif calibVersion >= 1.3
and calibVersion < 2.1:
846 inDict[
'noiseMatrix'][ampName] = record[
'NOISE_MATRIX']
847 inDict[
'noiseMatrixNoB'][ampName] = record[
'NOISE_MATRIX_NO_B']
849 inDict[
'noiseMatrix'][ampName] = record[
'NOISE_MATRIX']
850 nanMatrix = np.full_like(inDict[
'aMatrix'][ampName], np.nan)
851 inDict[
'noiseMatrixNoB'][ampName] = nanMatrix
852 if calibVersion < 1.5:
854 inDict[
'covMatrixSideFullCovFit'] = inDict[
'covMatrixSide']
856 inDict[
'covMatrixSideFullCovFit'] = record[
'COV_MATRIX_SIDE_FULL_COV_FIT']
857 if calibVersion < 1.6:
858 inDict[
'rowMeanVariance'][ampName] = np.full((len(inDict[
'expIdMask'][ampName]),), np.nan)
860 inDict[
'rowMeanVariance'][ampName] = record[
'ROW_MEAN_VARIANCE']
861 if calibVersion < 1.7:
862 inDict[
'noiseList'][ampName] = np.full_like(inDict[
'rawMeans'][ampName], np.nan)
864 inDict[
'noiseList'][ampName] = record[
'NOISE_LIST']
865 if calibVersion < 1.8:
866 inDict[
'ptcTurnoffSamplingError'][ampName] = np.nan
868 inDict[
'ptcTurnoffSamplingError'][ampName] = record[
'PTC_TURNOFF_SAMPLING_ERROR']
869 if calibVersion < 1.9
and inDict[
'ptcFitType'] ==
"FULLCOVARIANCE":
878 if ampName == inDict[
'ampNames'][0]:
879 cls().log.info(f
"Input PTC VERSION ({calibVersion}) < 1.9 and"
880 " ptcFitType == FULLCOVARIANCE. Applying fix for"
881 f
" the DM-45976 noise issue.")
885 inDict[
'noise'][ampName] = np.sqrt(record[
'noise'][ampName])
886 if calibVersion < 2.0:
887 inDict[
'ampOffsets'][ampName] = np.full_like(inDict[
'rawMeans'][ampName], np.nan)
888 inDict[
'gainUnadjusted'][ampName] = record[
'GAIN']
889 inDict[
'gainList'][ampName] = np.full_like(inDict[
'rawMeans'][ampName], np.nan)
891 inDict[
'ampOffsets'][ampName] = record[
'AMP_OFFSETS']
892 inDict[
'gainUnadjusted'][ampName] = record[
'GAIN_UNADJUSTED']
893 inDict[
'gainList'][ampName] = record[
'GAIN_LIST']
894 if calibVersion < 2.1:
895 inDict[
'covariancesModelNoB'][ampName] = record[
'COVARIANCES_MODEL_NO_B']
896 inDict[
'aMatrixNoB'][ampName] = record[
'A_MATRIX_NO_B']
898 nanMatrixList = np.full_like(inDict[
'covariances'][ampName], np.nan)
899 inDict[
'covariancesModelNoB'][ampName] = nanMatrixList
900 nanMatrix = np.full_like(inDict[
'aMatrix'][ampName], np.nan)
901 inDict[
'aMatrixNoB'][ampName] = nanMatrix
902 if calibVersion < 2.2:
903 inDict[
'inputExpPairMjdStartList'][ampName] = np.full_like(
904 inDict[
'rawMeans'][ampName],
907 inDict[
'overscanMedianLevelList'][ampName] = np.full_like(
908 inDict[
'rawMeans'][ampName],
912 inDict[
'inputExpPairMjdStartList'][ampName] = record[
'INPUT_EXP_PAIR_MJD_START']
913 inDict[
'overscanMedianLevelList'][ampName] = record[
'OVERSCAN_MEDIAN_LIST']
914 if calibVersion < 2.3:
915 inDict[
'overscanMedian'][ampName] = np.nan
916 inDict[
'overscanMedianSigma'][ampName] = np.nan
918 inDict[
'overscanMedian'][ampName] = record[
'OVERSCAN_MEDIAN']
919 inDict[
'overscanMedianSigma'][ampName] = record[
'OVERSCAN_MEDIAN_SIGMA']
920 if calibVersion < 2.4:
921 inDict[
'nPixelCovariances'][ampName] = -1
923 inDict[
'nPixelCovariances'][ampName] = record[
'NPIXEL_COVARIANCES']
924 if calibVersion < 2.5:
925 inDict[
'rawDeltas'][ampName] = np.full_like(
926 inDict[
'rawMeans'][ampName],
929 inDict[
'photoChargeDeltas'][ampName] = np.full_like(
930 inDict[
'rawMeans'][ampName],
934 inDict[
'rawDeltas'][ampName] = record[
'RAW_DELTAS']
935 inDict[
'photoChargeDeltas'][ampName] = record[
'PHOTO_CHARGE_DELTAS']
937 inDict[
'auxValues'] = {}
939 for col
in record.columns.keys():
940 if col.startswith(
'PTCAUX_'):
941 parts = col.split(
'PTCAUX_')
942 if isinstance(record[col][0], np.bytes_):
945 inDict[
'auxValues'][parts[1]] = record[col].astype(np.str_)
947 inDict[
'auxValues'][parts[1]] = record[col]
1051 """Append a partial PTC dataset to this dataset.
1055 partialPtc : `lsst.ip.isr.PhotonTransferCurveDataset`
1056 Partial PTC to append. Should only have one element.
1058 if self.ampNames != partialPtc.ampNames:
1059 raise ValueError(
"partialPtc has mis-matched amps.")
1060 if len(partialPtc.rawMeans[self.ampNames[0]]) != 1
or partialPtc.ptcFitType !=
"PARTIAL":
1061 raise ValueError(
"partialPtc does not appear to be the correct format.")
1064 initialLength = len(self.expIdMask[self.ampNames[0]])
1066 for key, value
in partialPtc.auxValues.items():
1067 if key
in self.auxValues:
1068 self.auxValues[key] = np.append(self.auxValues[key], value)
1069 elif initialLength == 0:
1071 self.auxValues[key] = value
1073 raise ValueError(f
"partialPtc has mismatched auxValue key {key}.")
1075 for ampName
in self.ampNames:
1076 if initialLength == 0:
1078 self.nPixelCovariances[ampName] = partialPtc.nPixelCovariances[ampName]
1079 elif partialPtc.nPixelCovariances[ampName] != self.nPixelCovariances[ampName]:
1080 raise ValueError(f
"partialPtc has mismatched nPixelCovariances for amp {ampName}.")
1082 for ampName
in self.ampNames:
1087 self.inputExpIdPairs[ampName].append(partialPtc.inputExpIdPairs[ampName][0])
1088 self.inputExpPairMjdStartList[ampName] = np.append(
1089 self.inputExpPairMjdStartList[ampName],
1090 partialPtc.inputExpPairMjdStartList[ampName][0],
1092 self.expIdMask[ampName] = np.append(self.expIdMask[ampName],
1093 partialPtc.expIdMask[ampName][0])
1094 self.rawExpTimes[ampName] = np.append(self.rawExpTimes[ampName],
1095 partialPtc.rawExpTimes[ampName][0])
1096 self.rawMeans[ampName] = np.append(self.rawMeans[ampName],
1097 partialPtc.rawMeans[ampName][0])
1098 self.rawVars[ampName] = np.append(self.rawVars[ampName],
1099 partialPtc.rawVars[ampName][0])
1100 self.rawDeltas[ampName] = np.append(self.rawDeltas[ampName],
1101 partialPtc.rawDeltas[ampName][0])
1102 self.rowMeanVariance[ampName] = np.append(self.rowMeanVariance[ampName],
1103 partialPtc.rowMeanVariance[ampName][0])
1104 self.photoCharges[ampName] = np.append(self.photoCharges[ampName],
1105 partialPtc.photoCharges[ampName][0])
1106 self.photoChargeDeltas[ampName] = np.append(self.photoChargeDeltas[ampName],
1107 partialPtc.photoChargeDeltas[ampName][0])
1108 self.ampOffsets[ampName] = np.append(self.ampOffsets[ampName],
1109 partialPtc.ampOffsets[ampName][0])
1110 self.histVars[ampName] = np.append(self.histVars[ampName],
1111 partialPtc.histVars[ampName][0])
1112 self.histChi2Dofs[ampName] = np.append(self.histChi2Dofs[ampName],
1113 partialPtc.histChi2Dofs[ampName][0])
1114 self.kspValues[ampName] = np.append(self.kspValues[ampName],
1115 partialPtc.kspValues[ampName][0])
1116 self.gainList[ampName] = np.append(self.gainList[ampName],
1117 partialPtc.gain[ampName])
1118 self.overscanMedianLevelList[ampName] = np.append(
1119 self.overscanMedianLevelList[ampName],
1120 partialPtc.overscanMedianLevelList[ampName][0],
1122 self.noiseList[ampName] = np.append(self.noiseList[ampName],
1123 partialPtc.noise[ampName])
1124 self.finalVars[ampName] = np.append(self.finalVars[ampName],
1125 partialPtc.finalVars[ampName][0])
1126 self.finalModelVars[ampName] = np.append(self.finalModelVars[ampName],
1127 partialPtc.finalModelVars[ampName][0])
1128 self.finalMeans[ampName] = np.append(self.finalMeans[ampName],
1129 partialPtc.finalMeans[ampName][0])
1130 self.covariances[ampName] = np.append(
1131 self.covariances[ampName].ravel(),
1132 partialPtc.covariances[ampName].ravel()
1135 len(self.rawExpTimes[ampName]),
1140 self.covariancesSqrtWeights[ampName] = np.append(
1141 self.covariancesSqrtWeights[ampName].ravel(),
1142 partialPtc.covariancesSqrtWeights[ampName].ravel()
1145 len(self.rawExpTimes[ampName]),
1150 self.covariancesModel[ampName] = np.append(
1151 self.covariancesModel[ampName].ravel(),
1152 partialPtc.covariancesModel[ampName].ravel()
1155 len(self.rawExpTimes[ampName]),
1316 """Computes the covariance model at specific signal levels.
1320 mu : `numpy.array`, (N,)
1321 List of mean signals in ADU.
1326 Raised if ptcFitType is invalid.
1330 covModel : `numpy.array`, (N, M, M)
1331 Covariances model at mu (in ADU^2).
1335 Computes the covModel for all mu, and it returns
1336 cov[N, M, M], where the variance model is cov[:,0,0].
1337 Both mu and cov are in ADUs and ADUs squared. This
1338 routine evaulates the n-degree polynomial model (defined
1339 by polynomialFitDegree) if self.ptcFitType == POLYNOMIAL,
1340 the approximation in Eq. 16 of Astier+19 (1905.08677)
1341 if self.ptcFitType == EXPAPPROXIMATION, and Eq. 20 of
1342 Astier+19 if self.ptcFitType == FULLCOVARIANCE.
1344 The POLYNOMIAL model and the EXPAPPROXIMATION model
1345 (Eq. 16 of Astier+19) are only approximations for the
1346 variance (cov[0,0]), so the function returns covModel
1347 of shape (N,), representing an array of [C_{00}]
1348 if self.ptcFitType == EXPAPPROXIMATION or
1349 self.ptcFitType == POLYNOMAIL.
1353 covModel = {ampName: np.array([])
for ampName
in ampNames}
1358 for ampName
in ampNames:
1359 c00 = poly.polyval(mu, [*pars[ampName]])
1360 covModel[ampName] = c00
1365 for ampName
in ampNames:
1366 a00, gain, noise = pars[ampName]
1367 f1 = 0.5/(a00*gain*gain)*(np.exp(2*a00*mu*gain)-1)
1368 f2 = noise/(gain*gain)
1370 covModel[ampName] = c00
1372 elif self.
ptcFitType in [
"FULLCOVARIANCE",
"FULLCOVARIANCE_NO_B"]:
1373 for ampName
in ampNames:
1375 gain = self.
gain[ampName]
1376 aMatrix = self.
aMatrix[ampName]
1377 bMatrix = self.
bMatrix[ampName]
1378 cMatrix = aMatrix*bMatrix
1381 sa = (matrixSideFit, matrixSideFit)
1384 aEnlarged = np.zeros((int(sa[0]*1.5)+1, int(sa[1]*1.5)+1))
1385 aEnlarged[0:sa[0], 0:sa[1]] = aMatrix
1389 cEnlarged = np.zeros((int(sa[0]*1.5)+1, int(sa[1]*1.5)+1))
1390 cEnlarged[0:sa[0], 0:sa[1]] = cMatrix
1393 a2 = fftconvolve(aSym, aSym, mode=
'same')
1394 a3 = fftconvolve(a2, aSym, mode=
'same')
1395 ac = fftconvolve(aSym, cSym, mode=
'same')
1396 (xc, yc) = np.unravel_index(np.abs(aSym).argmax(), a2.shape)
1398 a1 = aMatrix[np.newaxis, :, :]
1399 a2 = a2[np.newaxis, xc:xc + matrixSideFit, yc:yc + matrixSideFit]
1400 a3 = a3[np.newaxis, xc:xc + matrixSideFit, yc:yc + matrixSideFit]
1401 ac = ac[np.newaxis, xc:xc + matrixSideFit, yc:yc + matrixSideFit]
1402 c1 = cMatrix[np.newaxis, ::]
1405 bigMu = mu[:, np.newaxis, np.newaxis]*gain
1409 covModel[ampName] = (bigMu/(gain*gain)*(a1*bigMu+2./3.*(bigMu*bigMu)*(a2 + c1)
1410 + (1./3.*a3 + 5./6.*ac)*(bigMu*bigMu*bigMu))
1411 + noiseMatrix[np.newaxis, :, :]/gain**2)
1414 covModel[ampName][:, 0, 0] += mu/gain
1416 raise RuntimeError(
"Cannot compute PTC model for "