LSST Applications g0603fd7c41+022847dfd1,g0aad566f14+f45185db35,g180d380827+40e913b07a,g2079a07aa2+86d27d4dc4,g2305ad1205+696e5f3872,g2bbee38e9b+047b288a59,g337abbeb29+047b288a59,g33d1c0ed96+047b288a59,g3a166c0a6a+047b288a59,g3d1719c13e+f45185db35,g3de15ee5c7+5201731f0d,g487adcacf7+19f9b77d7d,g50ff169b8f+96c6868917,g52b1c1532d+585e252eca,g591dd9f2cf+248b16177b,g63cd9335cc+585e252eca,g858d7b2824+f45185db35,g88963caddf+0cb8e002cc,g991b906543+f45185db35,g99cad8db69+1747e75aa3,g9b9dfce982+78139cbddb,g9ddcbc5298+9a081db1e4,ga1e77700b3+a912195c07,gae0086650b+585e252eca,gb0e22166c9+60f28cb32d,gb3a676b8dc+b4feba26a1,gb4b16eec92+f82f04eb54,gba4ed39666+c2a2e4ac27,gbb8dafda3b+215b19b0ab,gc120e1dc64+b0284b5341,gc28159a63d+047b288a59,gc3e9b769f7+dcad4ace9a,gcf0d15dbbd+78139cbddb,gdaeeff99f8+f9a426f77a,ge79ae78c31+047b288a59,w.2024.19
LSST Data Management Base Package
Loading...
Searching...
No Matches
Public Member Functions | Static Public Member Functions | Static Public Attributes | Static Protected Attributes | List of all members
lsst.ip.isr.isrTaskLSST.IsrTaskLSST Class Reference
Inheritance diagram for lsst.ip.isr.isrTaskLSST.IsrTaskLSST:

Public Member Functions

 __init__ (self, **kwargs)
 
 runQuantum (self, butlerQC, inputRefs, outputRefs)
 
 validateInput (self, inputs)
 
 diffNonLinearCorrection (self, ccdExposure, dnlLUT, **kwargs)
 
 maskFullDefectAmplifiers (self, ccdExposure, detector, defects)
 
 maskSaturatedPixels (self, badAmpDict, ccdExposure, detector)
 
 overscanCorrection (self, mode, detectorConfig, detector, badAmpDict, ccdExposure)
 
 getLinearizer (self, detector)
 
 gainsCorrection (self, **kwargs)
 
 updateVariance (self, ampExposure, amp, ptcDataset=None)
 
 maskNegativeVariance (self, exposure)
 
 variancePlane (self, ccdExposure, ccd, ptc)
 
 maskDefect (self, exposure, defectBaseList)
 
 maskEdges (self, exposure, numEdgePixels=0, maskPlane="SUSPECT", level='DETECTOR')
 
 maskNan (self, exposure)
 
 countBadPixels (self, exposure)
 
 flatContext (self, exp, flat, dark=None)
 
 getBrighterFatterKernel (self, detector, bfKernel)
 
 applyBrighterFatterCorrection (self, ccdExposure, flat, dark, bfKernel, bfGains)
 
 darkCorrection (self, exposure, darkExposure, invert=False)
 
 doLinearize (self, detector)
 
 flatCorrection (self, exposure, flatExposure, invert=False)
 
 makeBinnedImages (self, exposure)
 
 run (self, ccdExposure, *dnlLUT=None, bias=None, deferredChargeCalib=None, linearizer=None, ptc=None, crosstalk=None, defects=None, bfKernel=None, bfGains=None, dark=None, flat=None, camera=None, **kwargs)
 

Static Public Member Functions

 extractCalibDate (calib)
 

Static Public Attributes

 ConfigClass = IsrTaskLSSTConfig
 

Static Protected Attributes

str _DefaultName = "isr"
 

Detailed Description

Definition at line 500 of file isrTaskLSST.py.

Constructor & Destructor Documentation

◆ __init__()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.__init__ ( self,
** kwargs )

Definition at line 504 of file isrTaskLSST.py.

504 def __init__(self, **kwargs):
505 super().__init__(**kwargs)
506 self.makeSubtask("assembleCcd")
507 self.makeSubtask("deferredChargeCorrection")
508 self.makeSubtask("crosstalk")
509 self.makeSubtask("masking")
510 self.makeSubtask("isrStats")
511

Member Function Documentation

◆ applyBrighterFatterCorrection()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.applyBrighterFatterCorrection ( self,
ccdExposure,
flat,
dark,
bfKernel,
bfGains )

Definition at line 1038 of file isrTaskLSST.py.

1038 def applyBrighterFatterCorrection(self, ccdExposure, flat, dark, bfKernel, bfGains):
1039 # We need to apply flats and darks before we can interpolate, and
1040 # we need to interpolate before we do B-F, but we do B-F without
1041 # the flats and darks applied so we can work in units of electrons
1042 # or holes. This context manager applies and then removes the darks
1043 # and flats.
1044 #
1045 # We also do not want to interpolate values here, so operate on
1046 # temporary images so we can apply only the BF-correction and roll
1047 # back the interpolation.
1048 # This won't be necessary once the gain normalization
1049 # is done appropriately.
1050 interpExp = ccdExposure.clone()
1051 with self.flatContext(interpExp, flat, dark):
1052 isrFunctions.interpolateFromMask(
1053 maskedImage=interpExp.getMaskedImage(),
1054 fwhm=self.config.brighterFatterFwhmForInterpolation,
1055 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1056 maskNameList=list(self.config.brighterFatterMaskListToInterpolate)
1057 )
1058 bfExp = interpExp.clone()
1059 self.log.info("Applying brighter-fatter correction using kernel type %s / gains %s.",
1060 type(bfKernel), type(bfGains))
1061 bfResults = isrFunctions.brighterFatterCorrection(bfExp, bfKernel,
1062 self.config.brighterFatterMaxIter,
1063 self.config.brighterFatterThreshold,
1064 self.config.brighterFatterApplyGain,
1065 bfGains)
1066 if bfResults[1] == self.config.brighterFatterMaxIter:
1067 self.log.warning("Brighter-fatter correction did not converge, final difference %f.",
1068 bfResults[0])
1069 else:
1070 self.log.info("Finished brighter-fatter correction in %d iterations.",
1071 bfResults[1])
1072
1073 image = ccdExposure.getMaskedImage().getImage()
1074 bfCorr = bfExp.getMaskedImage().getImage()
1075 bfCorr -= interpExp.getMaskedImage().getImage()
1076 image += bfCorr
1077
1078 # Applying the brighter-fatter correction applies a
1079 # convolution to the science image. At the edges this
1080 # convolution may not have sufficient valid pixels to
1081 # produce a valid correction. Mark pixels within the size
1082 # of the brighter-fatter kernel as EDGE to warn of this
1083 # fact.
1084 self.log.info("Ensuring image edges are masked as EDGE to the brighter-fatter kernel size.")
1085 self.maskEdges(ccdExposure, numEdgePixels=numpy.max(bfKernel.shape) // 2,
1086 maskPlane="EDGE")
1087
1088 if self.config.brighterFatterMaskGrowSize > 0:
1089 self.log.info("Growing masks to account for brighter-fatter kernel convolution.")
1090 for maskPlane in self.config.brighterFatterMaskListToInterpolate:
1091 isrFunctions.growMasks(ccdExposure.getMask(),
1092 radius=self.config.brighterFatterMaskGrowSize,
1093 maskNameList=maskPlane,
1094 maskValue=maskPlane)
1095
1096 return ccdExposure
1097

◆ countBadPixels()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.countBadPixels ( self,
exposure )
Notes
-----
Reset and interpolate bad pixels.

Large contiguous bad regions (which should have the BAD mask
bit set) should have their values set to the image median.
This group should include defects and bad amplifiers. As the
area covered by these defects are large, there's little
reason to expect that interpolation would provide a more
useful value.

Smaller defects can be safely interpolated after the larger
regions have had their pixel values reset.  This ensures
that the remaining defects adjacent to bad amplifiers (as an
example) do not attempt to interpolate extreme values.

Definition at line 967 of file isrTaskLSST.py.

967 def countBadPixels(self, exposure):
968 """
969 Notes
970 -----
971 Reset and interpolate bad pixels.
972
973 Large contiguous bad regions (which should have the BAD mask
974 bit set) should have their values set to the image median.
975 This group should include defects and bad amplifiers. As the
976 area covered by these defects are large, there's little
977 reason to expect that interpolation would provide a more
978 useful value.
979
980 Smaller defects can be safely interpolated after the larger
981 regions have had their pixel values reset. This ensures
982 that the remaining defects adjacent to bad amplifiers (as an
983 example) do not attempt to interpolate extreme values.
984 """
985 badPixelCount, badPixelValue = isrFunctions.setBadRegions(exposure)
986 if badPixelCount > 0:
987 self.log.info("Set %d BAD pixels to %f.", badPixelCount, badPixelValue)
988

◆ darkCorrection()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.darkCorrection ( self,
exposure,
darkExposure,
invert = False )
Apply dark correction in place.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure to process.
darkExposure : `lsst.afw.image.Exposure`
    Dark exposure of the same size as ``exposure``.
invert : `Bool`, optional
    If True, re-add the dark to an already corrected image.

Raises
------
RuntimeError
    Raised if either ``exposure`` or ``darkExposure`` do not
    have their dark time defined.

See Also
--------
lsst.ip.isr.isrFunctions.darkCorrection

Definition at line 1098 of file isrTaskLSST.py.

1098 def darkCorrection(self, exposure, darkExposure, invert=False):
1099 """Apply dark correction in place.
1100
1101 Parameters
1102 ----------
1103 exposure : `lsst.afw.image.Exposure`
1104 Exposure to process.
1105 darkExposure : `lsst.afw.image.Exposure`
1106 Dark exposure of the same size as ``exposure``.
1107 invert : `Bool`, optional
1108 If True, re-add the dark to an already corrected image.
1109
1110 Raises
1111 ------
1112 RuntimeError
1113 Raised if either ``exposure`` or ``darkExposure`` do not
1114 have their dark time defined.
1115
1116 See Also
1117 --------
1118 lsst.ip.isr.isrFunctions.darkCorrection
1119 """
1120 expScale = exposure.getInfo().getVisitInfo().getDarkTime()
1121 if math.isnan(expScale):
1122 raise RuntimeError("Exposure darktime is NAN.")
1123 if darkExposure.getInfo().getVisitInfo() is not None \
1124 and not math.isnan(darkExposure.getInfo().getVisitInfo().getDarkTime()):
1125 darkScale = darkExposure.getInfo().getVisitInfo().getDarkTime()
1126 else:
1127 # DM-17444: darkExposure.getInfo.getVisitInfo() is None
1128 # so getDarkTime() does not exist.
1129 self.log.warning("darkExposure.getInfo().getVisitInfo() does not exist. Using darkScale = 1.0.")
1130 darkScale = 1.0
1131
1132 isrFunctions.darkCorrection(
1133 maskedImage=exposure.getMaskedImage(),
1134 darkMaskedImage=darkExposure.getMaskedImage(),
1135 expScale=expScale,
1136 darkScale=darkScale,
1137 invert=invert,
1138 )
1139

◆ diffNonLinearCorrection()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.diffNonLinearCorrection ( self,
ccdExposure,
dnlLUT,
** kwargs )

Definition at line 541 of file isrTaskLSST.py.

541 def diffNonLinearCorrection(self, ccdExposure, dnlLUT, **kwargs):
542 # TODO DM 36636
543 # isrFunctions.diffNonLinearCorrection
544 pass
545

◆ doLinearize()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.doLinearize ( self,
detector )
Check if linearization is needed for the detector cameraGeom.

Checks config.doLinearize and the linearity type of the first
amplifier.

Parameters
----------
detector : `lsst.afw.cameraGeom.Detector`
    Detector to get linearity type from.

Returns
-------
doLinearize : `Bool`
    If True, linearization should be performed.

Definition at line 1165 of file isrTaskLSST.py.

1165 def doLinearize(self, detector):
1166 """Check if linearization is needed for the detector cameraGeom.
1167
1168 Checks config.doLinearize and the linearity type of the first
1169 amplifier.
1170
1171 Parameters
1172 ----------
1173 detector : `lsst.afw.cameraGeom.Detector`
1174 Detector to get linearity type from.
1175
1176 Returns
1177 -------
1178 doLinearize : `Bool`
1179 If True, linearization should be performed.
1180 """
1181 return self.config.doLinearize and \
1182 detector.getAmplifiers()[0].getLinearityType() != NullLinearityType
1183

◆ extractCalibDate()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.extractCalibDate ( calib)
static
Extract common calibration metadata values that will be written to
output header.

Parameters
----------
calib : `lsst.afw.image.Exposure` or `lsst.ip.isr.IsrCalib`
    Calibration to pull date information from.

Returns
-------
dateString : `str`
    Calibration creation date string to add to header.

Definition at line 1141 of file isrTaskLSST.py.

1141 def extractCalibDate(calib):
1142 """Extract common calibration metadata values that will be written to
1143 output header.
1144
1145 Parameters
1146 ----------
1147 calib : `lsst.afw.image.Exposure` or `lsst.ip.isr.IsrCalib`
1148 Calibration to pull date information from.
1149
1150 Returns
1151 -------
1152 dateString : `str`
1153 Calibration creation date string to add to header.
1154 """
1155 if hasattr(calib, "getMetadata"):
1156 if 'CALIB_CREATION_DATE' in calib.getMetadata():
1157 return " ".join((calib.getMetadata().get("CALIB_CREATION_DATE", "Unknown"),
1158 calib.getMetadata().get("CALIB_CREATION_TIME", "Unknown")))
1159 else:
1160 return " ".join((calib.getMetadata().get("CALIB_CREATE_DATE", "Unknown"),
1161 calib.getMetadata().get("CALIB_CREATE_TIME", "Unknown")))
1162 else:
1163 return "Unknown Unknown"
1164

◆ flatContext()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.flatContext ( self,
exp,
flat,
dark = None )
Context manager that applies and removes flats and darks,
if the task is configured to apply them.

Parameters
----------
exp : `lsst.afw.image.Exposure`
    Exposure to process.
flat : `lsst.afw.image.Exposure`
    Flat exposure the same size as ``exp``.
dark : `lsst.afw.image.Exposure`, optional
    Dark exposure the same size as ``exp``.

Yields
------
exp : `lsst.afw.image.Exposure`
    The flat and dark corrected exposure.

Definition at line 990 of file isrTaskLSST.py.

990 def flatContext(self, exp, flat, dark=None):
991 """Context manager that applies and removes flats and darks,
992 if the task is configured to apply them.
993
994 Parameters
995 ----------
996 exp : `lsst.afw.image.Exposure`
997 Exposure to process.
998 flat : `lsst.afw.image.Exposure`
999 Flat exposure the same size as ``exp``.
1000 dark : `lsst.afw.image.Exposure`, optional
1001 Dark exposure the same size as ``exp``.
1002
1003 Yields
1004 ------
1005 exp : `lsst.afw.image.Exposure`
1006 The flat and dark corrected exposure.
1007 """
1008 if self.config.doDark and dark is not None:
1009 self.darkCorrection(exp, dark)
1010 if self.config.doFlat and flat is not None:
1011 self.flatCorrection(exp, flat)
1012 try:
1013 yield exp
1014 finally:
1015 if self.config.doFlat and flat is not None:
1016 self.flatCorrection(exp, flat, invert=True)
1017 if self.config.doDark and dark is not None:
1018 self.darkCorrection(exp, dark, invert=True)
1019

◆ flatCorrection()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.flatCorrection ( self,
exposure,
flatExposure,
invert = False )
Apply flat correction in place.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure to process.
flatExposure : `lsst.afw.image.Exposure`
    Flat exposure of the same size as ``exposure``.
invert : `Bool`, optional
    If True, unflatten an already flattened image.

See Also
--------
lsst.ip.isr.isrFunctions.flatCorrection

Definition at line 1184 of file isrTaskLSST.py.

1184 def flatCorrection(self, exposure, flatExposure, invert=False):
1185 """Apply flat correction in place.
1186
1187 Parameters
1188 ----------
1189 exposure : `lsst.afw.image.Exposure`
1190 Exposure to process.
1191 flatExposure : `lsst.afw.image.Exposure`
1192 Flat exposure of the same size as ``exposure``.
1193 invert : `Bool`, optional
1194 If True, unflatten an already flattened image.
1195
1196 See Also
1197 --------
1198 lsst.ip.isr.isrFunctions.flatCorrection
1199 """
1200 isrFunctions.flatCorrection(
1201 maskedImage=exposure.getMaskedImage(),
1202 flatMaskedImage=flatExposure.getMaskedImage(),
1203 scalingType=self.config.flatScalingType,
1204 userScale=self.config.flatUserScale,
1205 invert=invert
1206 )
1207

◆ gainsCorrection()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.gainsCorrection ( self,
** kwargs )

Definition at line 793 of file isrTaskLSST.py.

793 def gainsCorrection(self, **kwargs):
794 # TODO DM 36639
795 gains = []
796 readNoise = []
797
798 return gains, readNoise
799

◆ getBrighterFatterKernel()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.getBrighterFatterKernel ( self,
detector,
bfKernel )

Definition at line 1020 of file isrTaskLSST.py.

1020 def getBrighterFatterKernel(self, detector, bfKernel):
1021 detName = detector.getName()
1022
1023 # This is expected to be a dictionary of amp-wise gains.
1024 bfGains = bfKernel.gain
1025 if bfKernel.level == 'DETECTOR':
1026 if detName in bfKernel.detKernels:
1027 bfKernelOut = bfKernel.detKernels[detName]
1028 return bfKernelOut, bfGains
1029 else:
1030 raise RuntimeError("Failed to extract kernel from new-style BF kernel.")
1031 elif bfKernel.level == 'AMP':
1032 self.log.warning("Making DETECTOR level kernel from AMP based brighter "
1033 "fatter kernels.")
1034 bfKernel.makeDetectorKernelFromAmpwiseKernels(detName)
1035 bfKernelOut = bfKernel.detKernels[detName]
1036 return bfKernelOut, bfGains
1037

◆ getLinearizer()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.getLinearizer ( self,
detector )

Definition at line 783 of file isrTaskLSST.py.

783 def getLinearizer(self, detector):
784 # Here we assume linearizer as dict or LUT are not supported
785 # TODO DM 28741
786
787 # TODO construct isrcalib input
788 linearizer = linearize.Linearizer(detector=detector, log=self.log)
789 self.log.warning("Constructing linearizer from cameraGeom information.")
790
791 return linearizer
792

◆ makeBinnedImages()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.makeBinnedImages ( self,
exposure )
Make visualizeVisit style binned exposures.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure to bin.

Returns
-------
bin1 : `lsst.afw.image.Exposure`
    Binned exposure using binFactor1.
bin2 : `lsst.afw.image.Exposure`
    Binned exposure using binFactor2.

Definition at line 1208 of file isrTaskLSST.py.

1208 def makeBinnedImages(self, exposure):
1209 """Make visualizeVisit style binned exposures.
1210
1211 Parameters
1212 ----------
1213 exposure : `lsst.afw.image.Exposure`
1214 Exposure to bin.
1215
1216 Returns
1217 -------
1218 bin1 : `lsst.afw.image.Exposure`
1219 Binned exposure using binFactor1.
1220 bin2 : `lsst.afw.image.Exposure`
1221 Binned exposure using binFactor2.
1222 """
1223 mi = exposure.getMaskedImage()
1224
1225 bin1 = afwMath.binImage(mi, self.config.binFactor1)
1226 bin2 = afwMath.binImage(mi, self.config.binFactor2)
1227
1228 return bin1, bin2
1229
std::shared_ptr< ImageT > binImage(ImageT const &inImage, int const binX, int const binY, lsst::afw::math::Property const flags=lsst::afw::math::MEAN)
Definition binImage.cc:44

◆ maskDefect()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.maskDefect ( self,
exposure,
defectBaseList )
Mask defects using mask plane "BAD", in place.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure to process.

defectBaseList : defect-type
    List of defects to mask. Can be of type  `lsst.ip.isr.Defects`
    or `list` of `lsst.afw.image.DefectBase`.

Definition at line 887 of file isrTaskLSST.py.

887 def maskDefect(self, exposure, defectBaseList):
888 """Mask defects using mask plane "BAD", in place.
889
890 Parameters
891 ----------
892 exposure : `lsst.afw.image.Exposure`
893 Exposure to process.
894
895 defectBaseList : defect-type
896 List of defects to mask. Can be of type `lsst.ip.isr.Defects`
897 or `list` of `lsst.afw.image.DefectBase`.
898 """
899 maskedImage = exposure.getMaskedImage()
900 if not isinstance(defectBaseList, Defects):
901 # Promotes DefectBase to Defect
902 defectList = Defects(defectBaseList)
903 else:
904 defectList = defectBaseList
905 defectList.maskPixels(maskedImage, maskName="BAD")
906

◆ maskEdges()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.maskEdges ( self,
exposure,
numEdgePixels = 0,
maskPlane = "SUSPECT",
level = 'DETECTOR' )
Mask edge pixels with applicable mask plane.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure to process.
numEdgePixels : `int`, optional
    Number of edge pixels to mask.
maskPlane : `str`, optional
    Mask plane name to use.
level : `str`, optional
    Level at which to mask edges.

Definition at line 907 of file isrTaskLSST.py.

907 def maskEdges(self, exposure, numEdgePixels=0, maskPlane="SUSPECT", level='DETECTOR'):
908 """Mask edge pixels with applicable mask plane.
909
910 Parameters
911 ----------
912 exposure : `lsst.afw.image.Exposure`
913 Exposure to process.
914 numEdgePixels : `int`, optional
915 Number of edge pixels to mask.
916 maskPlane : `str`, optional
917 Mask plane name to use.
918 level : `str`, optional
919 Level at which to mask edges.
920 """
921 maskedImage = exposure.getMaskedImage()
922 maskBitMask = maskedImage.getMask().getPlaneBitMask(maskPlane)
923
924 if numEdgePixels > 0:
925 if level == 'DETECTOR':
926 boxes = [maskedImage.getBBox()]
927 elif level == 'AMP':
928 boxes = [amp.getBBox() for amp in exposure.getDetector()]
929
930 for box in boxes:
931 # This makes a bbox numEdgeSuspect pixels smaller than the
932 # image on each side
933 subImage = maskedImage[box]
934 box.grow(-numEdgePixels)
935 # Mask pixels outside box
936 SourceDetectionTask.setEdgeBits(
937 subImage,
938 box,
939 maskBitMask)
940

◆ maskFullDefectAmplifiers()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.maskFullDefectAmplifiers ( self,
ccdExposure,
detector,
defects )
Check for fully masked bad amplifiers and mask them.

Full defect masking happens later to allow for defects which
cross amplifier boundaries.

Parameters
----------
ccdExposure : `lsst.afw.image.Exposure`
    Input exposure to be masked.
detector : `lsst.afw.cameraGeom.Detector`
    Detector object.
defects : `lsst.ip.isr.Defects`
    List of defects.  Used to determine if an entire
    amplifier is bad.

Returns
-------
badAmpDict : `str`[`bool`]
    Dictionary of amplifiers, keyed by name, value is True if
    amplifier is fully masked.

Definition at line 546 of file isrTaskLSST.py.

546 def maskFullDefectAmplifiers(self, ccdExposure, detector, defects):
547 """
548 Check for fully masked bad amplifiers and mask them.
549
550 Full defect masking happens later to allow for defects which
551 cross amplifier boundaries.
552
553 Parameters
554 ----------
555 ccdExposure : `lsst.afw.image.Exposure`
556 Input exposure to be masked.
557 detector : `lsst.afw.cameraGeom.Detector`
558 Detector object.
559 defects : `lsst.ip.isr.Defects`
560 List of defects. Used to determine if an entire
561 amplifier is bad.
562
563 Returns
564 -------
565 badAmpDict : `str`[`bool`]
566 Dictionary of amplifiers, keyed by name, value is True if
567 amplifier is fully masked.
568 """
569 badAmpDict = {}
570
571 maskedImage = ccdExposure.getMaskedImage()
572
573 for amp in detector:
574 ampName = amp.getName()
575 badAmpDict[ampName] = False
576
577 # Check if entire amp region is defined as a defect
578 # NB: need to use amp.getBBox() for correct comparison with current
579 # defects definition.
580 if defects is not None:
581 badAmpDict[ampName] = bool(sum([v.getBBox().contains(amp.getBBox()) for v in defects]))
582
583 # In the case of a bad amp, we will set mask to "BAD"
584 # (here use amp.getRawBBox() for correct association with pixels in
585 # current ccdExposure).
586 if badAmpDict[ampName]:
587 dataView = afwImage.MaskedImageF(maskedImage, amp.getRawBBox(),
588 afwImage.PARENT)
589 maskView = dataView.getMask()
590 maskView |= maskView.getPlaneBitMask("BAD")
591 del maskView
592
593 self.log.warning("Amplifier %s is bad (completely covered with defects)", ampName)
594
595 return badAmpDict
596

◆ maskNan()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.maskNan ( self,
exposure )
Mask NaNs using mask plane "UNMASKEDNAN", in place.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure to process.

Notes
-----
We mask over all non-finite values (NaN, inf), including those
that are masked with other bits (because those may or may not be
interpolated over later, and we want to remove all NaN/infs).
Despite this behaviour, the "UNMASKEDNAN" mask plane is used to
preserve the historical name.

Definition at line 941 of file isrTaskLSST.py.

941 def maskNan(self, exposure):
942 """Mask NaNs using mask plane "UNMASKEDNAN", in place.
943
944 Parameters
945 ----------
946 exposure : `lsst.afw.image.Exposure`
947 Exposure to process.
948
949 Notes
950 -----
951 We mask over all non-finite values (NaN, inf), including those
952 that are masked with other bits (because those may or may not be
953 interpolated over later, and we want to remove all NaN/infs).
954 Despite this behaviour, the "UNMASKEDNAN" mask plane is used to
955 preserve the historical name.
956 """
957 maskedImage = exposure.getMaskedImage()
958
959 # Find and mask NaNs
960 maskedImage.getMask().addMaskPlane("UNMASKEDNAN")
961 maskVal = maskedImage.getMask().getPlaneBitMask("UNMASKEDNAN")
962 numNans = maskNans(maskedImage, maskVal)
963 self.metadata["NUMNANS"] = numNans
964 if numNans > 0:
965 self.log.warning("There were %d unmasked NaNs.", numNans)
966

◆ maskNegativeVariance()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.maskNegativeVariance ( self,
exposure )
Identify and mask pixels with negative variance values.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure to process.

See Also
--------
lsst.ip.isr.isrFunctions.updateVariance

Definition at line 854 of file isrTaskLSST.py.

854 def maskNegativeVariance(self, exposure):
855 """Identify and mask pixels with negative variance values.
856
857 Parameters
858 ----------
859 exposure : `lsst.afw.image.Exposure`
860 Exposure to process.
861
862 See Also
863 --------
864 lsst.ip.isr.isrFunctions.updateVariance
865 """
866 maskPlane = exposure.getMask().getPlaneBitMask(self.config.negativeVarianceMaskName)
867 bad = numpy.where(exposure.getVariance().getArray() <= 0.0)
868 exposure.mask.array[bad] |= maskPlane
869

◆ maskSaturatedPixels()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.maskSaturatedPixels ( self,
badAmpDict,
ccdExposure,
detector )
Mask SATURATED and SUSPECT pixels and check if any amplifiers
are fully masked.

Parameters
----------
badAmpDict : `str` [`bool`]
    Dictionary of amplifiers, keyed by name, value is True if
    amplifier is fully masked.
ccdExposure : `lsst.afw.image.Exposure`
    Input exposure to be masked.
detector : `lsst.afw.cameraGeom.Detector`
    Detector object.
defects : `lsst.ip.isr.Defects`
    List of defects.  Used to determine if an entire
    amplifier is bad.

Returns
-------
badAmpDict : `str`[`bool`]
    Dictionary of amplifiers, keyed by name.

Definition at line 597 of file isrTaskLSST.py.

597 def maskSaturatedPixels(self, badAmpDict, ccdExposure, detector):
598 """
599 Mask SATURATED and SUSPECT pixels and check if any amplifiers
600 are fully masked.
601
602 Parameters
603 ----------
604 badAmpDict : `str` [`bool`]
605 Dictionary of amplifiers, keyed by name, value is True if
606 amplifier is fully masked.
607 ccdExposure : `lsst.afw.image.Exposure`
608 Input exposure to be masked.
609 detector : `lsst.afw.cameraGeom.Detector`
610 Detector object.
611 defects : `lsst.ip.isr.Defects`
612 List of defects. Used to determine if an entire
613 amplifier is bad.
614
615 Returns
616 -------
617 badAmpDict : `str`[`bool`]
618 Dictionary of amplifiers, keyed by name.
619 """
620 maskedImage = ccdExposure.getMaskedImage()
621
622 for amp in detector:
623 ampName = amp.getName()
624
625 if badAmpDict[ampName]:
626 # No need to check fully bad amplifiers.
627 continue
628
629 # Mask saturated and suspect pixels.
630 limits = {}
631 if self.config.doSaturation:
632 # Set to the default from the camera model.
633 limits.update({self.config.saturatedMaskName: amp.getSaturation()})
634 # And update if it is set in the config.
635 if math.isfinite(self.config.saturation):
636 limits.update({self.config.saturatedMaskName: self.config.saturation})
637 if self.config.doSuspect:
638 limits.update({self.config.suspectMaskName: amp.getSuspectLevel()})
639
640 for maskName, maskThreshold in limits.items():
641 if not math.isnan(maskThreshold):
642 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
643 isrFunctions.makeThresholdMask(
644 maskedImage=dataView,
645 threshold=maskThreshold,
646 growFootprints=0,
647 maskName=maskName
648 )
649
650 # Determine if we've fully masked this amplifier with SUSPECT and
651 # SAT pixels.
652 maskView = afwImage.Mask(maskedImage.getMask(), amp.getRawDataBBox(),
653 afwImage.PARENT)
654 maskVal = maskView.getPlaneBitMask([self.config.saturatedMaskName,
655 self.config.suspectMaskName])
656 if numpy.all(maskView.getArray() & maskVal > 0):
657 self.log.warning("Amplifier %s is bad (completely SATURATED or SUSPECT)", ampName)
658 badAmpDict[ampName] = True
659 maskView |= maskView.getPlaneBitMask("BAD")
660
661 return badAmpDict
662
Represent a 2-dimensional array of bitmask pixels.
Definition Mask.h:77

◆ overscanCorrection()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.overscanCorrection ( self,
mode,
detectorConfig,
detector,
badAmpDict,
ccdExposure )
Apply serial overscan correction in place to all amps.

The actual overscan subtraction is performed by the
`lsst.ip.isr.overscan.OverscanTask`, which is called here.

Parameters
----------
mode : `str`
    Must be `SERIAL` or `PARALLEL`.
detectorConfig : `lsst.ip.isr.OverscanDetectorConfig`
    Per-amplifier configurations.
detector : `lsst.afw.cameraGeom.Detector`
    Detector object.
badAmpDict : `dict`
    Dictionary of amp name to whether it is a bad amp.
ccdExposure : `lsst.afw.image.Exposure`
    Exposure to have overscan correction performed.

Returns
-------
overscans : `list` [`lsst.pipe.base.Struct` or None]
    Each result struct has components:

    ``imageFit``
        Value or fit subtracted from the amplifier image data.
        (scalar or `lsst.afw.image.Image`)
    ``overscanFit``
        Value or fit subtracted from the overscan image data.
        (scalar or `lsst.afw.image.Image`)
    ``overscanImage``
        Image of the overscan region with the overscan
        correction applied. This quantity is used to estimate
        the amplifier read noise empirically.
        (`lsst.afw.image.Image`)
    ``overscanMean``
        Mean overscan fit value. (`float`)
    ``overscanMedian``
        Median overscan fit value. (`float`)
    ``overscanSigma``
        Clipped standard deviation of the overscan fit. (`float`)
    ``residualMean``
        Mean of the overscan after fit subtraction. (`float`)
    ``residualMedian``
        Median of the overscan after fit subtraction. (`float`)
    ``residualSigma``
        Clipped standard deviation of the overscan after fit
        subtraction. (`float`)

See Also
--------
lsst.ip.isr.overscan.OverscanTask

Definition at line 663 of file isrTaskLSST.py.

663 def overscanCorrection(self, mode, detectorConfig, detector, badAmpDict, ccdExposure):
664 """Apply serial overscan correction in place to all amps.
665
666 The actual overscan subtraction is performed by the
667 `lsst.ip.isr.overscan.OverscanTask`, which is called here.
668
669 Parameters
670 ----------
671 mode : `str`
672 Must be `SERIAL` or `PARALLEL`.
673 detectorConfig : `lsst.ip.isr.OverscanDetectorConfig`
674 Per-amplifier configurations.
675 detector : `lsst.afw.cameraGeom.Detector`
676 Detector object.
677 badAmpDict : `dict`
678 Dictionary of amp name to whether it is a bad amp.
679 ccdExposure : `lsst.afw.image.Exposure`
680 Exposure to have overscan correction performed.
681
682 Returns
683 -------
684 overscans : `list` [`lsst.pipe.base.Struct` or None]
685 Each result struct has components:
686
687 ``imageFit``
688 Value or fit subtracted from the amplifier image data.
689 (scalar or `lsst.afw.image.Image`)
690 ``overscanFit``
691 Value or fit subtracted from the overscan image data.
692 (scalar or `lsst.afw.image.Image`)
693 ``overscanImage``
694 Image of the overscan region with the overscan
695 correction applied. This quantity is used to estimate
696 the amplifier read noise empirically.
697 (`lsst.afw.image.Image`)
698 ``overscanMean``
699 Mean overscan fit value. (`float`)
700 ``overscanMedian``
701 Median overscan fit value. (`float`)
702 ``overscanSigma``
703 Clipped standard deviation of the overscan fit. (`float`)
704 ``residualMean``
705 Mean of the overscan after fit subtraction. (`float`)
706 ``residualMedian``
707 Median of the overscan after fit subtraction. (`float`)
708 ``residualSigma``
709 Clipped standard deviation of the overscan after fit
710 subtraction. (`float`)
711
712 See Also
713 --------
714 lsst.ip.isr.overscan.OverscanTask
715 """
716 if mode not in ["SERIAL", "PARALLEL"]:
717 raise ValueError("Mode must be SERIAL or PARALLEL")
718
719 # This returns a list in amp order, with None for uncorrected amps.
720 overscans = []
721
722 for i, amp in enumerate(detector):
723 ampName = amp.getName()
724
725 ampConfig = detectorConfig.getOverscanAmpConfig(amp)
726
727 if mode == "SERIAL" and not ampConfig.doSerialOverscan:
728 self.log.debug(
729 "ISR_OSCAN: Amplifier %s/%s configured to skip serial overscan.",
730 detector.getName(),
731 ampName,
732 )
733 results = None
734 elif mode == "PARALLEL" and not ampConfig.doParallelOverscan:
735 self.log.debug(
736 "ISR_OSCAN: Amplifier %s configured to skip parallel overscan.",
737 detector.getName(),
738 ampName,
739 )
740 results = None
741 elif badAmpDict[ampName] or not ccdExposure.getBBox().contains(amp.getBBox()):
742 results = None
743 else:
744 # This check is to confirm that we are not trying to run
745 # overscan on an already trimmed image. Therefore, always
746 # checking just the horizontal overscan bounding box is
747 # sufficient.
748 if amp.getRawHorizontalOverscanBBox().isEmpty():
749 self.log.warning(
750 "ISR_OSCAN: No overscan region for amp %s. Not performing overscan correction.",
751 ampName,
752 )
753 results = None
754 else:
755 if mode == "SERIAL":
756 # We need to set up the subtask here with a custom
757 # configuration.
758 serialOverscan = SerialOverscanCorrectionTask(config=ampConfig.serialOverscanConfig)
759 results = serialOverscan.run(ccdExposure, amp)
760 else:
761 parallelOverscan = ParallelOverscanCorrectionTask(
762 config=ampConfig.parallelOverscanConfig,
763 )
764 results = parallelOverscan.run(ccdExposure, amp)
765
766 metadata = ccdExposure.getMetadata()
767 keyBase = "LSST ISR OVERSCAN"
768 metadata[f"{keyBase} {mode} MEAN {ampName}"] = results.overscanMean
769 metadata[f"{keyBase} {mode} MEDIAN {ampName}"] = results.overscanMedian
770 metadata[f"{keyBase} {mode} STDEV {ampName}"] = results.overscanSigma
771
772 metadata[f"{keyBase} RESIDUAL {mode} MEAN {ampName}"] = results.residualMean
773 metadata[f"{keyBase} RESIDUAL {mode} MEDIAN {ampName}"] = results.residualMedian
774 metadata[f"{keyBase} RESIDUAL {mode} STDEV {ampName}"] = results.residualSigma
775
776 overscans.append(results)
777
778 # Question: should this be finer grained?
779 ccdExposure.getMetadata().set("OVERSCAN", "Overscan corrected")
780
781 return overscans
782
daf::base::PropertySet * set
Definition fits.cc:931

◆ run()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.run ( self,
ccdExposure,
* dnlLUT = None,
bias = None,
deferredChargeCalib = None,
linearizer = None,
ptc = None,
crosstalk = None,
defects = None,
bfKernel = None,
bfGains = None,
dark = None,
flat = None,
camera = None,
** kwargs )

Definition at line 1230 of file isrTaskLSST.py.

1233 ):
1234
1235 detector = ccdExposure.getDetector()
1236
1237 overscanDetectorConfig = self.config.overscanCamera.getOverscanDetectorConfig(detector)
1238
1239 gains = ptc.gain
1240
1241 if self.config.doHeaderProvenance:
1242 # Inputs have been validated, so we can add their date
1243 # information to the output header.
1244 exposureMetadata = ccdExposure.getMetadata()
1245 exposureMetadata["LSST CALIB OVERSCAN HASH"] = overscanDetectorConfig.md5
1246 exposureMetadata["LSST CALIB DATE PTC"] = self.extractCalibDate(ptc)
1247 if self.config.doDiffNonLinearCorrection:
1248 exposureMetadata["LSST CALIB DATE DNL"] = self.extractCalibDate(dnlLUT)
1249 if self.config.doBias:
1250 exposureMetadata["LSST CALIB DATE BIAS"] = self.extractCalibDate(bias)
1251 if self.config.doDeferredCharge:
1252 exposureMetadata["LSST CALIB DATE CTI"] = self.extractCalibDate(deferredChargeCalib)
1253 if self.doLinearize(detector):
1254 exposureMetadata["LSST CALIB DATE LINEARIZER"] = self.extractCalibDate(linearizer)
1255 if self.config.doCrosstalk or overscanDetectorConfig.doAnyParallelOverscanCrosstalk:
1256 exposureMetadata["LSST CALIB DATE CROSSTALK"] = self.extractCalibDate(crosstalk)
1257 if self.config.doDefect:
1258 exposureMetadata["LSST CALIB DATE DEFECTS"] = self.extractCalibDate(defects)
1259 if self.config.doBrighterFatter:
1260 exposureMetadata["LSST CALIB DATE BFK"] = self.extractCalibDate(bfKernel)
1261 if self.config.doDark:
1262 exposureMetadata["LSST CALIB DATE DARK"] = self.extractCalibDate(dark)
1263 if self.config.doFlat:
1264 exposureMetadata["LSST CALIB DATE FLAT"] = self.extractCalibDate(flat)
1265
1266 # First we mark which amplifiers are completely bad from defects.
1267 badAmpDict = self.maskFullDefectAmplifiers(ccdExposure, detector, defects)
1268
1269 if self.config.doDiffNonLinearCorrection:
1270 self.diffNonLinearCorrection(ccdExposure, dnlLUT)
1271
1272 if overscanDetectorConfig.doAnySerialOverscan:
1273 # Input units: ADU
1274 serialOverscans = self.overscanCorrection(
1275 "SERIAL",
1276 overscanDetectorConfig,
1277 detector,
1278 badAmpDict,
1279 ccdExposure,
1280 )
1281 else:
1282 serialOverscans = [None]*len(detector)
1283
1284 if overscanDetectorConfig.doAnyParallelOverscanCrosstalk:
1285 # Input units: ADU
1286 # Make sure that the units here are consistent with later
1287 # application.
1288 self.crosstalk.run(
1289 ccdExposure,
1290 crosstalk=crosstalk,
1291 camera=camera,
1292 parallelOverscanRegion=True,
1293 detectorConfig=overscanDetectorConfig,
1294 )
1295
1296 # After serial overscan correction, we can mask SATURATED and
1297 # SUSPECT pixels. This updates badAmpDict if any amplifier
1298 # is fully saturated after serial overscan correction.
1299 badAmpDict = self.maskSaturatedPixels(badAmpDict, ccdExposure, detector)
1300
1301 if overscanDetectorConfig.doAnyParallelOverscan:
1302 # Input units: ADU
1303 # At the moment we do not use the parallelOverscans return value.
1304 _ = self.overscanCorrection(
1305 "PARALLEL",
1306 overscanDetectorConfig,
1307 detector,
1308 badAmpDict,
1309 ccdExposure,
1310 )
1311
1312 if self.config.doAssembleCcd:
1313 # Input units: ADU
1314 self.log.info("Assembling CCD from amplifiers.")
1315 ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
1316
1317 if self.config.expectWcs and not ccdExposure.getWcs():
1318 self.log.warning("No WCS found in input exposure.")
1319
1320 if self.config.doLinearize:
1321 # Input units: ADU
1322 self.log.info("Applying linearizer.")
1323 linearizer = self.getLinearizer(detector=detector)
1324 linearizer.applyLinearity(image=ccdExposure.getMaskedImage().getImage(),
1325 detector=detector, log=self.log)
1326
1327 if self.config.doCrosstalk:
1328 # Input units: ADU
1329 self.log.info("Applying crosstalk correction.")
1330 self.crosstalk.run(ccdExposure, crosstalk=crosstalk, isTrimmed=True)
1331
1332 if self.config.doBias:
1333 # Input units: ADU
1334 self.log.info("Applying bias correction.")
1335 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage())
1336
1337 if self.config.doGainsCorrection:
1338 # TODO DM 36639
1339 self.log.info("Apply temperature dependence to the gains.")
1340 gains, readNoise = self.gainsCorrection(**kwargs)
1341
1342 if self.config.doApplyGains:
1343 # Input units: ADU
1344 # Output units: electrons
1345 self.log.info("Apply PTC gains (temperature corrected or not) to the image.")
1346 isrFunctions.applyGains(ccdExposure, normalizeGains=False, ptcGains=gains)
1347
1348 if self.config.doDeferredCharge:
1349 # Input units: electrons
1350 self.log.info("Applying deferred charge/CTI correction.")
1351 self.deferredChargeCorrection.run(ccdExposure, deferredChargeCalib)
1352
1353 if self.config.doVariance:
1354 # Input units: electrons
1355 self.variancePlane(ccdExposure, detector, ptc)
1356
1357 # Masking block (defects, NAN pixels and trails).
1358 # Saturated and suspect pixels have already been masked.
1359 if self.config.doDefect:
1360 # Input units: electrons
1361 self.log.info("Applying defects masking.")
1362 self.maskDefect(ccdExposure, defects)
1363
1364 if self.config.doNanMasking:
1365 self.log.info("Masking non-finite (NAN, inf) value pixels.")
1366 self.maskNan(ccdExposure)
1367
1368 if self.config.doWidenSaturationTrails:
1369 self.log.info("Widening saturation trails.")
1370 isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
1371
1372 if self.config.doDark:
1373 # Input units: electrons
1374 self.log.info("Applying dark subtraction.")
1375 self.darkCorrection(ccdExposure, dark)
1376
1377 if self.config.doBrighterFatter:
1378 # Input units: electrons
1379 self.log.info("Applying Bright-Fatter kernels.")
1380 bfKernelOut, bfGains = self.getBrighterFatterKernel(detector, bfKernel)
1381 ccdExposure = self.applyBrighterFatterCorrection(ccdExposure, flat, dark, bfKernelOut, bfGains)
1382
1383 if self.config.doFlat:
1384 # Input units: electrons
1385 self.log.info("Applying flat correction.")
1386 # Placeholder while the LSST flat procedure is done.
1387 # The flat here would be a background flat.
1388 self.flatCorrection(ccdExposure, flat)
1389
1390 # Pixel values for masked regions are set here
1391 preInterpExp = None
1392 if self.config.doSaveInterpPixels:
1393 preInterpExp = ccdExposure.clone()
1394
1395 if self.config.doSetBadRegions:
1396 self.log.info('Counting pixels in BAD regions.')
1397 self.countBadPixels(ccdExposure)
1398
1399 if self.config.doInterpolate:
1400 self.log.info("Interpolating masked pixels.")
1401 isrFunctions.interpolateFromMask(
1402 maskedImage=ccdExposure.getMaskedImage(),
1403 fwhm=self.config.brighterFatterFwhmForInterpolation,
1404 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1405 maskNameList=list(self.config.maskListToInterpolate)
1406 )
1407
1408 # Calculate standard image quality statistics
1409 if self.config.doStandardStatistics:
1410 metadata = ccdExposure.getMetadata()
1411 for amp in detector:
1412 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1413 ampName = amp.getName()
1414 metadata[f"LSST ISR MASK SAT {ampName}"] = isrFunctions.countMaskedPixels(
1415 ampExposure.getMaskedImage(),
1416 [self.config.saturatedMaskName]
1417 )
1418 metadata[f"LSST ISR MASK BAD {ampName}"] = isrFunctions.countMaskedPixels(
1419 ampExposure.getMaskedImage(),
1420 ["BAD"]
1421 )
1422 qaStats = afwMath.makeStatistics(ampExposure.getImage(),
1423 afwMath.MEAN | afwMath.MEDIAN | afwMath.STDEVCLIP)
1424
1425 metadata[f"LSST ISR FINAL MEAN {ampName}"] = qaStats.getValue(afwMath.MEAN)
1426 metadata[f"LSST ISR FINAL MEDIAN {ampName}"] = qaStats.getValue(afwMath.MEDIAN)
1427 metadata[f"LSST ISR FINAL STDEV {ampName}"] = qaStats.getValue(afwMath.STDEVCLIP)
1428
1429 k1 = f"LSST ISR FINAL MEDIAN {ampName}"
1430 k2 = f"LSST ISR OVERSCAN SERIAL MEDIAN {ampName}"
1431 if overscanDetectorConfig.doAnySerialOverscan and k1 in metadata and k2 in metadata:
1432 metadata[f"LSST ISR LEVEL {ampName}"] = metadata[k1] - metadata[k2]
1433 else:
1434 metadata[f"LSST ISR LEVEL {ampName}"] = numpy.nan
1435
1436 # calculate additional statistics.
1437 outputStatistics = None
1438 if self.config.doCalculateStatistics:
1439 outputStatistics = self.isrStats.run(ccdExposure, overscanResults=serialOverscans,
1440 bias=bias, dark=dark, flat=flat, ptc=ptc,
1441 defects=defects).results
1442
1443 # do image binning.
1444 outputBin1Exposure = None
1445 outputBin2Exposure = None
1446 if self.config.doBinnedExposures:
1447 outputBin1Exposure, outputBin2Exposure = self.makeBinnedImages(ccdExposure)
1448
1449 return pipeBase.Struct(
1450 exposure=ccdExposure,
1451
1452 outputBin1Exposure=outputBin1Exposure,
1453 outputBin2Exposure=outputBin2Exposure,
1454
1455 preInterpExposure=preInterpExp,
1456 outputExposure=ccdExposure,
1457 outputStatistics=outputStatistics,
1458 )
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Handle a watered-down front-end to the constructor (no variance)
Definition Statistics.h:361

◆ runQuantum()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.runQuantum ( self,
butlerQC,
inputRefs,
outputRefs )

Definition at line 512 of file isrTaskLSST.py.

512 def runQuantum(self, butlerQC, inputRefs, outputRefs):
513
514 inputs = butlerQC.get(inputRefs)
515 self.validateInput(inputs)
516 super().runQuantum(butlerQC, inputRefs, outputRefs)
517

◆ updateVariance()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.updateVariance ( self,
ampExposure,
amp,
ptcDataset = None )
Set the variance plane using the gain and read noise.

Parameters
----------
ampExposure : `lsst.afw.image.Exposure`
    Exposure to process.
amp : `lsst.afw.cameraGeom.Amplifier` or `FakeAmp`
    Amplifier detector data.
ptcDataset : `lsst.ip.isr.PhotonTransferCurveDataset`, optional
    PTC dataset containing the gains and read noise.

Raises
------
RuntimeError
    Raised if ptcDataset is not provided.

See also
--------
lsst.ip.isr.isrFunctions.updateVariance

Definition at line 800 of file isrTaskLSST.py.

800 def updateVariance(self, ampExposure, amp, ptcDataset=None):
801 """Set the variance plane using the gain and read noise.
802
803 Parameters
804 ----------
805 ampExposure : `lsst.afw.image.Exposure`
806 Exposure to process.
807 amp : `lsst.afw.cameraGeom.Amplifier` or `FakeAmp`
808 Amplifier detector data.
809 ptcDataset : `lsst.ip.isr.PhotonTransferCurveDataset`, optional
810 PTC dataset containing the gains and read noise.
811
812 Raises
813 ------
814 RuntimeError
815 Raised if ptcDataset is not provided.
816
817 See also
818 --------
819 lsst.ip.isr.isrFunctions.updateVariance
820 """
821 # Get gains from PTC
822 if ptcDataset is None:
823 raise RuntimeError("No ptcDataset provided to use PTC gains.")
824 else:
825 gain = ptcDataset.gain[amp.getName()]
826 self.log.debug("Getting gain from Photon Transfer Curve.")
827
828 if math.isnan(gain):
829 gain = 1.0
830 self.log.warning("Gain set to NAN! Updating to 1.0 to generate Poisson variance.")
831 elif gain <= 0:
832 patchedGain = 1.0
833 self.log.warning("Gain for amp %s == %g <= 0; setting to %f.",
834 amp.getName(), gain, patchedGain)
835 gain = patchedGain
836
837 # Get read noise from PTC
838 if ptcDataset is None:
839 raise RuntimeError("No ptcDataset provided to use PTC readnoise.")
840 else:
841 readNoise = ptcDataset.noise[amp.getName()]
842 self.log.debug("Getting read noise from Photon Transfer Curve.")
843
844 metadata = ampExposure.getMetadata()
845 metadata[f'LSST GAIN {amp.getName()}'] = gain
846 metadata[f'LSST READNOISE {amp.getName()}'] = readNoise
847
848 isrFunctions.updateVariance(
849 maskedImage=ampExposure.getMaskedImage(),
850 gain=gain,
851 readNoise=readNoise,
852 )
853

◆ validateInput()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.validateInput ( self,
inputs )
This is a check that all the inputs required by the config
are available.

Definition at line 518 of file isrTaskLSST.py.

518 def validateInput(self, inputs):
519 """
520 This is a check that all the inputs required by the config
521 are available.
522 """
523
524 doCrosstalk = self.config.doCrosstalk or self.config.overscanCamera.doAnyParallelOverscanCrosstalk
525
526 inputMap = {'dnlLUT': self.config.doDiffNonLinearCorrection,
527 'bias': self.config.doBias,
528 'deferredChargeCalib': self.config.doDeferredCharge,
529 'linearizer': self.config.doLinearize,
530 'ptc': self.config.doApplyGains,
531 'crosstalk': doCrosstalk,
532 'defects': self.config.doDefect,
533 'bfKernel': self.config.doBrighterFatter,
534 'dark': self.config.doDark,
535 }
536
537 for calibrationFile, configValue in inputMap.items():
538 if configValue and inputs[calibrationFile] is None:
539 raise RuntimeError("Must supply ", calibrationFile)
540

◆ variancePlane()

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.variancePlane ( self,
ccdExposure,
ccd,
ptc )

Definition at line 870 of file isrTaskLSST.py.

870 def variancePlane(self, ccdExposure, ccd, ptc):
871 for amp in ccd:
872 if ccdExposure.getBBox().contains(amp.getBBox()):
873 self.log.debug("Constructing variance map for amplifer %s.", amp.getName())
874 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
875
876 self.updateVariance(ampExposure, amp, ptcDataset=ptc)
877
878 if self.config.qa is not None and self.config.qa.saveStats is True:
879 qaStats = afwMath.makeStatistics(ampExposure.getVariance(),
880 afwMath.MEDIAN | afwMath.STDEVCLIP)
881 self.log.debug(" Variance stats for amplifer %s: %f +/- %f.",
882 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
883 qaStats.getValue(afwMath.STDEVCLIP))
884 if self.config.maskNegativeVariance:
885 self.maskNegativeVariance(ccdExposure)
886

Member Data Documentation

◆ _DefaultName

str lsst.ip.isr.isrTaskLSST.IsrTaskLSST._DefaultName = "isr"
staticprotected

Definition at line 502 of file isrTaskLSST.py.

◆ ConfigClass

lsst.ip.isr.isrTaskLSST.IsrTaskLSST.ConfigClass = IsrTaskLSSTConfig
static

Definition at line 501 of file isrTaskLSST.py.


The documentation for this class was generated from the following file: