385 inputExpIdPair=(-1, -1),
386 inputExpPairMjdStart=np.nan,
391 rowMeanVariance=np.nan,
393 photoChargeDelta=np.nan,
396 expIdRolloffMask=
False,
402 overscanMedianLevel=np.nan,
408 Set the amp values for a partial PTC Dataset (from cpExtractPtcTask).
413 Name of the amp to set the values.
414 inputExpIdPair : `tuple` [`int`]
415 Exposure IDs of input pair.
416 inputExpPairMjdStart : `float`, optional
417 The start MJD of first exposure in the flat pair.
418 rawExpTime : `float`, optional
419 Exposure time for this exposure pair (units: sec).
420 rawMean : `float`, optional
421 Average of the means of the exposures in this pair
423 rawVar : `float`, optional
424 Variance of the difference of the exposures in this pair
426 rawDelta : `float`, optional
427 Delta of the means of the exposure in this pair
429 rowMeanVariance : `float`, optional
430 Variance of the means of the rows in the difference image
431 of the exposures in this pair (units: adu^2).
432 photoCharge : `float`, optional
433 Integrated photocharge for flat pair for linearity calibration
435 photoChargeDelta : `float`, optional
436 Delta between integrated photocharge for the flat pair
438 ampOffset : `float`, optional
439 Amp offset for this amplifier.
440 expIdMask : `bool`, optional
441 Flag setting if this exposure pair should be used (True)
443 expIdRolloffMask : `bool`, optional
444 Flag setting if this exposure pair should be used (True)
445 or not used for rolloff search (False).
446 nPixelCovariance : `int`, optional
447 Number of pixels that went into the covariance measurement.
448 covariance : `np.ndarray` or None, optional
449 Measured covariance for this exposure pair (units: adu^2).
450 covSqrtWeights : `np.ndarray` or None, optional
451 Measured sqrt of covariance weights in this exposure pair
453 gain : `float`, optional
454 Estimated gain for this exposure pair (units: electron/adu).
455 noise : `float`, optional
456 Estimated read noise for this exposure pair (units: electron).
457 overscanMedianLevel : `float`, optional
458 Average of the median overscan levels for this exposure pair.
460 histVar : `float`, optional
461 Variance estimated from fitting a histogram with a Gaussian model
463 histChi2Dof : `float`, optional
464 Chi-squared per degree of freedom from Gaussian histogram fit.
465 kspValue : `float`, optional
466 KS test p-value from the Gaussian histogram fit.
471 if covariance
is None:
472 covariance = nanMatrix
473 if covSqrtWeights
is None:
474 covSqrtWeights = nanMatrix
479 self.
rawMeans[ampName] = np.array([rawMean])
480 self.
rawVars[ampName] = np.array([rawVar])
481 self.
rawDeltas[ampName] = np.array([rawDelta])
485 self.
ampOffsets[ampName] = np.array([ampOffset])
486 self.
expIdMask[ampName] = np.array([expIdMask])
491 self.
gain[ampName] = gain
493 self.
gainList[ampName] = np.array([gain])
494 self.
noise[ampName] = noise
495 self.
noiseList[ampName] = np.array([noise])
499 self.
histVars[ampName] = np.array([histVar])
501 self.
kspValues[ampName] = np.array([kspValue])
505 self.
aMatrix[ampName] = nanMatrixFit
506 self.
bMatrix[ampName] = nanMatrixFit
510 self.
finalVars[ampName] = np.array([np.nan])
548 """Construct a calibration from a dictionary of properties.
549 Must be implemented by the specific calibration subclasses.
554 Dictionary of properties.
558 calib : `lsst.ip.isr.PhotonTransferCurveDataset`
559 Constructed calibration.
564 Raised if the supplied dictionary is for a different
568 if calib._OBSTYPE != dictionary[
'metadata'][
'OBSTYPE']:
569 raise RuntimeError(f
"Incorrect Photon Transfer Curve dataset supplied. "
570 f
"Expected {calib._OBSTYPE}, found {dictionary['metadata']['OBSTYPE']}")
571 calib.setMetadata(dictionary[
'metadata'])
572 calib.ptcFitType = dictionary[
'ptcFitType']
573 calib.covMatrixSide = dictionary[
'covMatrixSide']
574 calib.covMatrixSideFullCovFit = dictionary[
'covMatrixSideFullCovFit']
575 calib.badAmps = np.array(dictionary[
'badAmps'],
'str').tolist()
579 covMatrixSide = calib.covMatrixSide
580 covMatrixSideFullCovFit = calib.covMatrixSideFullCovFit
582 covDimensionsProduct = len(np.array(list(dictionary[
'covariances'].values())[0]).ravel())
583 nSignalPoints = int(covDimensionsProduct/(covMatrixSide*covMatrixSide))
585 for ampName
in dictionary[
'ampNames']:
586 calib.ampNames.append(ampName)
587 calib.inputExpIdPairs[ampName] = dictionary[
'inputExpIdPairs'][ampName]
588 calib.inputExpPairMjdStartList[ampName] = np.array(
589 dictionary[
'inputExpPairMjdStartList'][ampName],
591 calib.expIdMask[ampName] = np.array(dictionary[
'expIdMask'][ampName])
592 calib.expIdRolloffMask[ampName] = np.array(dictionary[
'expIdRolloffMask'][ampName])
593 calib.rawExpTimes[ampName] = np.array(dictionary[
'rawExpTimes'][ampName], dtype=np.float64)
594 calib.rawMeans[ampName] = np.array(dictionary[
'rawMeans'][ampName], dtype=np.float64)
595 calib.rawVars[ampName] = np.array(dictionary[
'rawVars'][ampName], dtype=np.float64)
596 calib.rawDeltas[ampName] = np.array(dictionary[
'rawDeltas'][ampName], dtype=np.float64)
597 calib.rowMeanVariance[ampName] = np.array(dictionary[
'rowMeanVariance'][ampName],
599 calib.gain[ampName] = float(dictionary[
'gain'][ampName])
600 calib.gainErr[ampName] = float(dictionary[
'gainErr'][ampName])
601 calib.gainUnadjusted[ampName] = float(dictionary[
'gainUnadjusted'][ampName])
602 calib.gainList[ampName] = np.array(dictionary[
'gainList'][ampName], dtype=np.float64)
603 calib.noiseList[ampName] = np.array(dictionary[
'noiseList'][ampName], dtype=np.float64)
604 calib.nPixelCovariances[ampName] = int(dictionary[
'nPixelCovariances'][ampName])
605 calib.overscanMedianLevelList[ampName] = np.array(
606 dictionary[
'overscanMedianLevelList'][ampName],
609 calib.overscanMedian[ampName] = float(dictionary[
'overscanMedian'][ampName])
610 calib.overscanMedianSigma[ampName] = float(dictionary[
'overscanMedianSigma'][ampName])
611 calib.noise[ampName] = float(dictionary[
'noise'][ampName])
612 calib.noiseErr[ampName] = float(dictionary[
'noiseErr'][ampName])
613 calib.histVars[ampName] = np.array(dictionary[
'histVars'][ampName], dtype=np.float64)
614 calib.histChi2Dofs[ampName] = np.array(dictionary[
'histChi2Dofs'][ampName], dtype=np.float64)
615 calib.kspValues[ampName] = np.array(dictionary[
'kspValues'][ampName], dtype=np.float64)
616 calib.ptcFitPars[ampName] = np.array(dictionary[
'ptcFitPars'][ampName], dtype=np.float64)
617 calib.ptcFitParsError[ampName] = np.array(dictionary[
'ptcFitParsError'][ampName],
619 calib.ptcFitChiSq[ampName] = float(dictionary[
'ptcFitChiSq'][ampName])
620 calib.ptcTurnoff[ampName] = float(dictionary[
'ptcTurnoff'][ampName])
621 calib.ptcTurnoffSamplingError[ampName] = float(dictionary[
'ptcTurnoffSamplingError'][ampName])
622 calib.ptcRolloff[ampName] = float(dictionary[
'ptcRolloff'][ampName])
623 calib.ptcRolloffError[ampName] = float(dictionary[
'ptcRolloffError'][ampName])
624 calib.ptcRolloffTau[ampName] = float(dictionary[
'ptcRolloff'][ampName])
625 calib.ptcRolloffTauError[ampName] = float(dictionary[
'ptcRolloffError'][ampName])
626 if nSignalPoints > 0:
628 calib.covariances[ampName] = np.array(dictionary[
'covariances'][ampName],
629 dtype=np.float64).reshape(
630 (nSignalPoints, covMatrixSide, covMatrixSide))
631 calib.covariancesModel[ampName] = np.array(
632 dictionary[
'covariancesModel'][ampName],
633 dtype=np.float64).reshape(
634 (nSignalPoints, covMatrixSideFullCovFit, covMatrixSideFullCovFit))
635 calib.covariancesSqrtWeights[ampName] = np.array(
636 dictionary[
'covariancesSqrtWeights'][ampName],
637 dtype=np.float64).reshape(
638 (nSignalPoints, covMatrixSide, covMatrixSide))
639 calib.aMatrix[ampName] = np.array(dictionary[
'aMatrix'][ampName],
640 dtype=np.float64).reshape(
641 (covMatrixSideFullCovFit, covMatrixSideFullCovFit))
642 calib.bMatrix[ampName] = np.array(dictionary[
'bMatrix'][ampName],
643 dtype=np.float64).reshape(
644 (covMatrixSideFullCovFit, covMatrixSideFullCovFit))
645 calib.noiseMatrix[ampName] = np.array(
646 dictionary[
'noiseMatrix'][ampName],
647 dtype=np.float64).reshape((covMatrixSideFullCovFit, covMatrixSideFullCovFit))
650 calib.covariances[ampName] = np.array([], dtype=np.float64)
651 calib.covariancesModel[ampName] = np.array([], dtype=np.float64)
652 calib.covariancesSqrtWeights[ampName] = np.array([], dtype=np.float64)
653 calib.aMatrix[ampName] = np.array([], dtype=np.float64)
654 calib.bMatrix[ampName] = np.array([], dtype=np.float64)
655 calib.noiseMatrix[ampName] = np.array([], dtype=np.float64)
657 calib.finalVars[ampName] = np.array(dictionary[
'finalVars'][ampName], dtype=np.float64)
658 calib.finalModelVars[ampName] = np.array(dictionary[
'finalModelVars'][ampName], dtype=np.float64)
659 calib.finalMeans[ampName] = np.array(dictionary[
'finalMeans'][ampName], dtype=np.float64)
660 calib.photoCharges[ampName] = np.array(dictionary[
'photoCharges'][ampName], dtype=np.float64)
661 calib.photoChargeDeltas[ampName] = np.array(
662 dictionary[
'photoChargeDeltas'][ampName],
665 calib.ampOffsets[ampName] = np.array(dictionary[
'ampOffsets'][ampName], dtype=np.float64)
667 for key, value
in dictionary[
'auxValues'].items():
668 if isinstance(value[0], numbers.Integral):
669 calib.auxValues[key] = np.atleast_1d(np.asarray(value).astype(np.int64))
670 elif isinstance(value[0], (str, np.str_, np.bytes_)):
671 calib.auxValues[key] = np.atleast_1d(np.asarray(value))
673 calib.auxValues[key] = np.atleast_1d(np.array(value, dtype=np.float64))
675 calib.updateMetadata()
679 """Return a dictionary containing the calibration properties.
680 The dictionary should be able to be round-tripped through
686 Dictionary of properties.
692 outDict[
'metadata'] = metadata
694 def _dictOfArraysToDictOfLists(dictOfArrays):
696 for key, value
in dictOfArrays.items():
697 dictOfLists[key] = value.ravel().tolist()
705 outDict[
'badAmps'] = self.
badAmps
708 outDict[
'expIdMask'] = _dictOfArraysToDictOfLists(self.
expIdMask)
709 outDict[
'expIdRolloffMask'] = _dictOfArraysToDictOfLists(self.
expIdRolloffMask)
710 outDict[
'rawExpTimes'] = _dictOfArraysToDictOfLists(self.
rawExpTimes)
711 outDict[
'rawMeans'] = _dictOfArraysToDictOfLists(self.
rawMeans)
712 outDict[
'rawVars'] = _dictOfArraysToDictOfLists(self.
rawVars)
713 outDict[
'rawDeltas'] = _dictOfArraysToDictOfLists(self.
rawDeltas)
714 outDict[
'rowMeanVariance'] = _dictOfArraysToDictOfLists(self.
rowMeanVariance)
715 outDict[
'gain'] = self.
gain
716 outDict[
'gainErr'] = self.
gainErr
718 outDict[
'gainList'] = _dictOfArraysToDictOfLists(self.
gainList)
719 outDict[
'noiseList'] = _dictOfArraysToDictOfLists(self.
noiseList)
723 outDict[
'noise'] = self.
noise
728 outDict[
'ptcFitPars'] = _dictOfArraysToDictOfLists(self.
ptcFitPars)
729 outDict[
'ptcFitParsError'] = _dictOfArraysToDictOfLists(self.
ptcFitParsError)
738 outDict[
'covariances'] = _dictOfArraysToDictOfLists(self.
covariances)
739 outDict[
'covariancesModel'] = _dictOfArraysToDictOfLists(self.
covariancesModel)
741 outDict[
'aMatrix'] = _dictOfArraysToDictOfLists(self.
aMatrix)
742 outDict[
'bMatrix'] = _dictOfArraysToDictOfLists(self.
bMatrix)
743 outDict[
'noiseMatrix'] = _dictOfArraysToDictOfLists(self.
noiseMatrix)
744 outDict[
'finalVars'] = _dictOfArraysToDictOfLists(self.
finalVars)
745 outDict[
'finalModelVars'] = _dictOfArraysToDictOfLists(self.
finalModelVars)
746 outDict[
'finalMeans'] = _dictOfArraysToDictOfLists(self.
finalMeans)
747 outDict[
'photoCharges'] = _dictOfArraysToDictOfLists(self.
photoCharges)
748 outDict[
'photoChargeDeltas'] = _dictOfArraysToDictOfLists(self.
photoChargeDeltas)
749 outDict[
'ampOffsets'] = _dictOfArraysToDictOfLists(self.
ampOffsets)
750 outDict[
'auxValues'] = _dictOfArraysToDictOfLists(self.
auxValues)
756 """Construct calibration from a list of tables.
757 This method uses the `fromDict` method to create the
758 calibration, after constructing an appropriate dictionary from
763 tableList : `list` [`lsst.afw.table.Table`]
764 List of tables to use to construct the datasetPtc.
768 calib : `lsst.ip.isr.PhotonTransferCurveDataset`
769 The calibration defined in the tables.
771 ptcTable = tableList[0]
773 metadata = ptcTable.meta
775 inDict[
'metadata'] = metadata
776 inDict[
'ampNames'] = []
777 inDict[
'ptcFitType'] = []
778 inDict[
'covMatrixSide'] = []
779 inDict[
'covMatrixSideFullCovFit'] = []
780 inDict[
'inputExpIdPairs'] = dict()
781 inDict[
'inputExpPairMjdStartList'] = dict()
782 inDict[
'expIdMask'] = dict()
783 inDict[
'expIdRolloffMask'] = dict()
784 inDict[
'rawExpTimes'] = dict()
785 inDict[
'rawMeans'] = dict()
786 inDict[
'rawVars'] = dict()
787 inDict[
'rawDeltas'] = dict()
788 inDict[
'rowMeanVariance'] = dict()
789 inDict[
'gain'] = dict()
790 inDict[
'gainErr'] = dict()
791 inDict[
'gainUnadjusted'] = dict()
792 inDict[
'gainList'] = dict()
793 inDict[
'noiseList'] = dict()
794 inDict[
'overscanMedianLevelList'] = dict()
795 inDict[
'overscanMedian'] = dict()
796 inDict[
'overscanMedianSigma'] = dict()
797 inDict[
'noise'] = dict()
798 inDict[
'noiseErr'] = dict()
799 inDict[
'histVars'] = dict()
800 inDict[
'histChi2Dofs'] = dict()
801 inDict[
'kspValues'] = dict()
802 inDict[
'ptcFitPars'] = dict()
803 inDict[
'ptcFitParsError'] = dict()
804 inDict[
'ptcFitChiSq'] = dict()
805 inDict[
'ptcTurnoff'] = dict()
806 inDict[
'ptcTurnoffSamplingError'] = dict()
807 inDict[
'ptcRolloff'] = dict()
808 inDict[
'ptcRolloffError'] = dict()
809 inDict[
'ptcRolloffTau'] = dict()
810 inDict[
'ptcRolloffTauError'] = dict()
811 inDict[
'nPixelCovariances'] = dict()
812 inDict[
'covariances'] = dict()
813 inDict[
'covariancesModel'] = dict()
814 inDict[
'covariancesSqrtWeights'] = dict()
815 inDict[
'aMatrix'] = dict()
816 inDict[
'bMatrix'] = dict()
817 inDict[
'noiseMatrix'] = dict()
818 inDict[
'finalVars'] = dict()
819 inDict[
'finalModelVars'] = dict()
820 inDict[
'finalMeans'] = dict()
821 inDict[
'badAmps'] = []
822 inDict[
'photoCharges'] = dict()
823 inDict[
'photoChargeDeltas'] = dict()
824 inDict[
'ampOffsets'] = dict()
827 inDict[
'noiseMatrixNoB'] = dict()
828 inDict[
'covariancesModelNoB'] = dict()
829 inDict[
'aMatrixNoB'] = dict()
831 calibVersion = metadata[
'PTC_VERSION']
832 if calibVersion == 1.0:
833 cls().log.warning(f
"Previous version found for PTC dataset: {calibVersion}. "
834 f
"Setting 'ptcTurnoff' in all amps to last value in 'finalMeans'.")
835 for record
in ptcTable:
836 ampName = record[
'AMPLIFIER_NAME']
838 inDict[
'ptcFitType'] = record[
'PTC_FIT_TYPE']
839 inDict[
'covMatrixSide'] = record[
'COV_MATRIX_SIDE']
840 inDict[
'ampNames'].append(ampName)
841 inDict[
'inputExpIdPairs'][ampName] = record[
'INPUT_EXP_ID_PAIRS'].tolist()
842 inDict[
'expIdMask'][ampName] = record[
'EXP_ID_MASK']
843 inDict[
'rawExpTimes'][ampName] = record[
'RAW_EXP_TIMES']
844 inDict[
'rawMeans'][ampName] = record[
'RAW_MEANS']
845 inDict[
'rawVars'][ampName] = record[
'RAW_VARS']
846 inDict[
'gain'][ampName] = record[
'GAIN']
847 inDict[
'gainErr'][ampName] = record[
'GAIN_ERR']
848 inDict[
'noise'][ampName] = record[
'NOISE']
849 inDict[
'noiseErr'][ampName] = record[
'NOISE_ERR']
850 inDict[
'ptcFitPars'][ampName] = record[
'PTC_FIT_PARS']
851 inDict[
'ptcFitParsError'][ampName] = record[
'PTC_FIT_PARS_ERROR']
852 inDict[
'ptcFitChiSq'][ampName] = record[
'PTC_FIT_CHI_SQ']
853 inDict[
'covariances'][ampName] = record[
'COVARIANCES']
854 inDict[
'covariancesModel'][ampName] = record[
'COVARIANCES_MODEL']
855 inDict[
'covariancesSqrtWeights'][ampName] = record[
'COVARIANCES_SQRT_WEIGHTS']
856 inDict[
'aMatrix'][ampName] = record[
'A_MATRIX']
857 inDict[
'bMatrix'][ampName] = record[
'B_MATRIX']
858 inDict[
'finalVars'][ampName] = record[
'FINAL_VARS']
859 inDict[
'finalModelVars'][ampName] = record[
'FINAL_MODEL_VARS']
860 inDict[
'finalMeans'][ampName] = record[
'FINAL_MEANS']
861 inDict[
'badAmps'] = record[
'BAD_AMPS'].tolist()
862 inDict[
'photoCharges'][ampName] = record[
'PHOTO_CHARGE']
863 if calibVersion == 1.0:
864 mask = record[
'FINAL_MEANS'].mask
865 array = record[
'FINAL_MEANS'][~mask]
867 inDict[
'ptcTurnoff'][ampName] = record[
'FINAL_MEANS'][~mask][-1]
869 inDict[
'ptcTurnoff'][ampName] = np.nan
871 inDict[
'ptcTurnoff'][ampName] = record[
'PTC_TURNOFF']
872 if calibVersion < 1.2:
873 inDict[
'histVars'][ampName] = np.array([np.nan])
874 inDict[
'histChi2Dofs'][ampName] = np.array([np.nan])
875 inDict[
'kspValues'][ampName] = np.array([0.0])
877 inDict[
'histVars'][ampName] = record[
'HIST_VARS']
878 inDict[
'histChi2Dofs'][ampName] = record[
'HIST_CHI2_DOFS']
879 inDict[
'kspValues'][ampName] = record[
'KS_PVALUES']
880 if calibVersion < 1.3:
881 nanMatrix = np.full_like(inDict[
'aMatrix'][ampName], np.nan)
882 inDict[
'noiseMatrix'][ampName] = nanMatrix
883 inDict[
'noiseMatrixNoB'][ampName] = nanMatrix
884 elif calibVersion >= 1.3
and calibVersion < 2.1:
885 inDict[
'noiseMatrix'][ampName] = record[
'NOISE_MATRIX']
886 inDict[
'noiseMatrixNoB'][ampName] = record[
'NOISE_MATRIX_NO_B']
888 inDict[
'noiseMatrix'][ampName] = record[
'NOISE_MATRIX']
889 nanMatrix = np.full_like(inDict[
'aMatrix'][ampName], np.nan)
890 inDict[
'noiseMatrixNoB'][ampName] = nanMatrix
891 if calibVersion < 1.5:
893 inDict[
'covMatrixSideFullCovFit'] = inDict[
'covMatrixSide']
895 inDict[
'covMatrixSideFullCovFit'] = record[
'COV_MATRIX_SIDE_FULL_COV_FIT']
896 if calibVersion < 1.6:
897 inDict[
'rowMeanVariance'][ampName] = np.full((len(inDict[
'expIdMask'][ampName]),), np.nan)
899 inDict[
'rowMeanVariance'][ampName] = record[
'ROW_MEAN_VARIANCE']
900 if calibVersion < 1.7:
901 inDict[
'noiseList'][ampName] = np.full_like(inDict[
'rawMeans'][ampName], np.nan)
903 inDict[
'noiseList'][ampName] = record[
'NOISE_LIST']
904 if calibVersion < 1.8:
905 inDict[
'ptcTurnoffSamplingError'][ampName] = np.nan
907 inDict[
'ptcTurnoffSamplingError'][ampName] = record[
'PTC_TURNOFF_SAMPLING_ERROR']
908 if calibVersion < 1.9
and inDict[
'ptcFitType'] ==
"FULLCOVARIANCE":
917 if ampName == inDict[
'ampNames'][0]:
918 cls().log.info(f
"Input PTC VERSION ({calibVersion}) < 1.9 and"
919 " ptcFitType == FULLCOVARIANCE. Applying fix for"
920 f
" the DM-45976 noise issue.")
924 inDict[
'noise'][ampName] = np.sqrt(record[
'noise'][ampName])
925 if calibVersion < 2.0:
926 inDict[
'ampOffsets'][ampName] = np.full_like(inDict[
'rawMeans'][ampName], np.nan)
927 inDict[
'gainUnadjusted'][ampName] = record[
'GAIN']
928 inDict[
'gainList'][ampName] = np.full_like(inDict[
'rawMeans'][ampName], np.nan)
930 inDict[
'ampOffsets'][ampName] = record[
'AMP_OFFSETS']
931 inDict[
'gainUnadjusted'][ampName] = record[
'GAIN_UNADJUSTED']
932 inDict[
'gainList'][ampName] = record[
'GAIN_LIST']
933 if calibVersion < 2.1:
934 inDict[
'covariancesModelNoB'][ampName] = record[
'COVARIANCES_MODEL_NO_B']
935 inDict[
'aMatrixNoB'][ampName] = record[
'A_MATRIX_NO_B']
937 nanMatrixList = np.full_like(inDict[
'covariances'][ampName], np.nan)
938 inDict[
'covariancesModelNoB'][ampName] = nanMatrixList
939 nanMatrix = np.full_like(inDict[
'aMatrix'][ampName], np.nan)
940 inDict[
'aMatrixNoB'][ampName] = nanMatrix
941 if calibVersion < 2.2:
942 inDict[
'inputExpPairMjdStartList'][ampName] = np.full_like(
943 inDict[
'rawMeans'][ampName],
946 inDict[
'overscanMedianLevelList'][ampName] = np.full_like(
947 inDict[
'rawMeans'][ampName],
951 inDict[
'inputExpPairMjdStartList'][ampName] = record[
'INPUT_EXP_PAIR_MJD_START']
952 inDict[
'overscanMedianLevelList'][ampName] = record[
'OVERSCAN_MEDIAN_LIST']
953 if calibVersion < 2.3:
954 inDict[
'overscanMedian'][ampName] = np.nan
955 inDict[
'overscanMedianSigma'][ampName] = np.nan
957 inDict[
'overscanMedian'][ampName] = record[
'OVERSCAN_MEDIAN']
958 inDict[
'overscanMedianSigma'][ampName] = record[
'OVERSCAN_MEDIAN_SIGMA']
959 if calibVersion < 2.4:
960 inDict[
'nPixelCovariances'][ampName] = -1
962 inDict[
'nPixelCovariances'][ampName] = record[
'NPIXEL_COVARIANCES']
963 if calibVersion < 2.5:
964 inDict[
'rawDeltas'][ampName] = np.full_like(
965 inDict[
'rawMeans'][ampName],
968 inDict[
'photoChargeDeltas'][ampName] = np.full_like(
969 inDict[
'rawMeans'][ampName],
973 inDict[
'rawDeltas'][ampName] = record[
'RAW_DELTAS']
974 inDict[
'photoChargeDeltas'][ampName] = record[
'PHOTO_CHARGE_DELTAS']
975 if calibVersion < 2.6:
976 inDict[
'ptcRolloff'][ampName] = np.nan
977 inDict[
'ptcRolloffError'][ampName] = np.nan
978 inDict[
'ptcRolloffTau'][ampName] = np.nan
979 inDict[
'ptcRolloffTauError'][ampName] = np.nan
980 inDict[
'expIdRolloffMask'][ampName] = np.full_like(
981 inDict[
'expIdMask'][ampName],
985 inDict[
'ptcRolloff'][ampName] = record[
'PTC_ROLLOFF']
986 inDict[
'ptcRolloffError'][ampName] = record[
'PTC_ROLLOFF_ERROR']
987 inDict[
'ptcRolloffTau'][ampName] = record[
'PTC_ROLLOFF_TAU']
988 inDict[
'ptcRolloffTauError'][ampName] = record[
'PTC_ROLLOFF_TAU_ERROR']
989 inDict[
'expIdRolloffMask'][ampName] = record[
'EXP_ID_ROLLOFF_MASK']
991 inDict[
'auxValues'] = {}
993 for col
in record.columns.keys():
994 if col.startswith(
'PTCAUX_'):
995 parts = col.split(
'PTCAUX_')
996 if isinstance(record[col][0], np.bytes_):
999 inDict[
'auxValues'][parts[1]] = record[col].astype(np.str_)
1001 inDict[
'auxValues'][parts[1]] = record[col]
1110 """Append a partial PTC dataset to this dataset.
1114 partialPtc : `lsst.ip.isr.PhotonTransferCurveDataset`
1115 Partial PTC to append. Should only have one element.
1117 if self.ampNames != partialPtc.ampNames:
1118 raise ValueError(
"partialPtc has mis-matched amps.")
1119 if len(partialPtc.rawMeans[self.ampNames[0]]) != 1
or partialPtc.ptcFitType !=
"PARTIAL":
1120 raise ValueError(
"partialPtc does not appear to be the correct format.")
1123 initialLength = len(self.expIdMask[self.ampNames[0]])
1125 for key, value
in partialPtc.auxValues.items():
1126 if key
in self.auxValues:
1127 self.auxValues[key] = np.append(self.auxValues[key], value)
1128 elif initialLength == 0:
1130 self.auxValues[key] = value
1132 raise ValueError(f
"partialPtc has mismatched auxValue key {key}.")
1134 for ampName
in self.ampNames:
1135 if initialLength == 0:
1137 self.nPixelCovariances[ampName] = partialPtc.nPixelCovariances[ampName]
1138 elif partialPtc.nPixelCovariances[ampName] != self.nPixelCovariances[ampName]:
1139 raise ValueError(f
"partialPtc has mismatched nPixelCovariances for amp {ampName}.")
1141 for ampName
in self.ampNames:
1146 self.inputExpIdPairs[ampName].append(partialPtc.inputExpIdPairs[ampName][0])
1147 self.inputExpPairMjdStartList[ampName] = np.append(
1148 self.inputExpPairMjdStartList[ampName],
1149 partialPtc.inputExpPairMjdStartList[ampName][0],
1151 self.expIdMask[ampName] = np.append(self.expIdMask[ampName],
1152 partialPtc.expIdMask[ampName][0])
1153 self.expIdRolloffMask[ampName] = np.append(self.expIdRolloffMask[ampName],
1154 partialPtc.expIdRolloffMask[ampName][0])
1155 self.rawExpTimes[ampName] = np.append(self.rawExpTimes[ampName],
1156 partialPtc.rawExpTimes[ampName][0])
1157 self.rawMeans[ampName] = np.append(self.rawMeans[ampName],
1158 partialPtc.rawMeans[ampName][0])
1159 self.rawVars[ampName] = np.append(self.rawVars[ampName],
1160 partialPtc.rawVars[ampName][0])
1161 self.rawDeltas[ampName] = np.append(self.rawDeltas[ampName],
1162 partialPtc.rawDeltas[ampName][0])
1163 self.rowMeanVariance[ampName] = np.append(self.rowMeanVariance[ampName],
1164 partialPtc.rowMeanVariance[ampName][0])
1165 self.photoCharges[ampName] = np.append(self.photoCharges[ampName],
1166 partialPtc.photoCharges[ampName][0])
1167 self.photoChargeDeltas[ampName] = np.append(self.photoChargeDeltas[ampName],
1168 partialPtc.photoChargeDeltas[ampName][0])
1169 self.ampOffsets[ampName] = np.append(self.ampOffsets[ampName],
1170 partialPtc.ampOffsets[ampName][0])
1171 self.histVars[ampName] = np.append(self.histVars[ampName],
1172 partialPtc.histVars[ampName][0])
1173 self.histChi2Dofs[ampName] = np.append(self.histChi2Dofs[ampName],
1174 partialPtc.histChi2Dofs[ampName][0])
1175 self.kspValues[ampName] = np.append(self.kspValues[ampName],
1176 partialPtc.kspValues[ampName][0])
1177 self.gainList[ampName] = np.append(self.gainList[ampName],
1178 partialPtc.gain[ampName])
1179 self.overscanMedianLevelList[ampName] = np.append(
1180 self.overscanMedianLevelList[ampName],
1181 partialPtc.overscanMedianLevelList[ampName][0],
1183 self.noiseList[ampName] = np.append(self.noiseList[ampName],
1184 partialPtc.noise[ampName])
1185 self.finalVars[ampName] = np.append(self.finalVars[ampName],
1186 partialPtc.finalVars[ampName][0])
1187 self.finalModelVars[ampName] = np.append(self.finalModelVars[ampName],
1188 partialPtc.finalModelVars[ampName][0])
1189 self.finalMeans[ampName] = np.append(self.finalMeans[ampName],
1190 partialPtc.finalMeans[ampName][0])
1191 self.covariances[ampName] = np.append(
1192 self.covariances[ampName].ravel(),
1193 partialPtc.covariances[ampName].ravel()
1196 len(self.rawExpTimes[ampName]),
1201 self.covariancesSqrtWeights[ampName] = np.append(
1202 self.covariancesSqrtWeights[ampName].ravel(),
1203 partialPtc.covariancesSqrtWeights[ampName].ravel()
1206 len(self.rawExpTimes[ampName]),
1211 self.covariancesModel[ampName] = np.append(
1212 self.covariancesModel[ampName].ravel(),
1213 partialPtc.covariancesModel[ampName].ravel()
1216 len(self.rawExpTimes[ampName]),
1378 """Computes the covariance model at specific signal levels.
1382 mu : `numpy.array`, (N,)
1383 List of mean signals in ADU.
1388 Raised if ptcFitType is invalid.
1392 covModel : `numpy.array`, (N, M, M)
1393 Covariances model at mu (in ADU^2).
1397 Computes the covModel for all mu, and it returns
1398 cov[N, M, M], where the variance model is cov[:,0,0].
1399 Both mu and cov are in ADUs and ADUs squared. This
1400 routine evaluates the approximation in Eq. 16 of
1401 Astier+19 (1905.08677)
1402 if self.ptcFitType == EXPAPPROXIMATION, and Eq. 20 of
1403 Astier+19 if self.ptcFitType == FULLCOVARIANCE(_NO_B).
1405 The EXPAPPROXIMATION model (Eq. 16 of Astier+19) is
1406 only an approximation for the variance (cov[0,0]),
1407 so the function returns covModel of shape (N,),
1408 representing an array of [C_{00}]
1409 if self.ptcFitType == EXPAPPROXIMATION.
1413 covModel = {ampName: np.array([])
for ampName
in ampNames}
1416 warnings.warn(
"The `POLYNOMIAL` fit type is deprecated; it will "
1417 "be removed from the Rubin Observatory Science "
1418 "Pipelines after v30.",
1419 category=FutureWarning)
1424 for ampName
in ampNames:
1425 c00 = poly.polyval(mu, [*pars[ampName]])
1426 covModel[ampName] = c00
1430 for ampName
in ampNames:
1431 a00, gain, noise = pars[ampName]
1432 f1 = 0.5/(a00*gain*gain)*(np.exp(2*a00*mu*gain)-1)
1433 f2 = noise/(gain*gain)
1435 covModel[ampName] = c00
1437 elif self.
ptcFitType in [
"FULLCOVARIANCE",
"FULLCOVARIANCE_NO_B"]:
1438 for ampName
in ampNames:
1440 gain = self.
gain[ampName]
1441 aMatrix = self.
aMatrix[ampName]
1442 bMatrix = self.
bMatrix[ampName]
1443 cMatrix = aMatrix*bMatrix
1446 sa = (matrixSideFit, matrixSideFit)
1449 aEnlarged = np.zeros((int(sa[0]*1.5)+1, int(sa[1]*1.5)+1))
1450 aEnlarged[0:sa[0], 0:sa[1]] = aMatrix
1454 cEnlarged = np.zeros((int(sa[0]*1.5)+1, int(sa[1]*1.5)+1))
1455 cEnlarged[0:sa[0], 0:sa[1]] = cMatrix
1458 a2 = fftconvolve(aSym, aSym, mode=
'same')
1459 a3 = fftconvolve(a2, aSym, mode=
'same')
1460 ac = fftconvolve(aSym, cSym, mode=
'same')
1461 (xc, yc) = np.unravel_index(np.abs(aSym).argmax(), a2.shape)
1463 a1 = aMatrix[np.newaxis, :, :]
1464 a2 = a2[np.newaxis, xc:xc + matrixSideFit, yc:yc + matrixSideFit]
1465 a3 = a3[np.newaxis, xc:xc + matrixSideFit, yc:yc + matrixSideFit]
1466 ac = ac[np.newaxis, xc:xc + matrixSideFit, yc:yc + matrixSideFit]
1467 c1 = cMatrix[np.newaxis, ::]
1470 bigMu = mu[:, np.newaxis, np.newaxis]*gain
1474 covModel[ampName] = (bigMu/(gain*gain)*(a1*bigMu+2./3.*(bigMu*bigMu)*(a2 + c1)
1475 + (1./3.*a3 + 5./6.*ac)*(bigMu*bigMu*bigMu))
1476 + noiseMatrix[np.newaxis, :, :]/gain**2)
1479 covModel[ampName][:, 0, 0] += mu/gain
1481 raise RuntimeError(
"Cannot compute PTC model for "