LSST Applications 29.1.0,g0fba68d861+6b120c4394,g123d84c11c+8c5ae1fdc5,g1ec0fe41b4+191117f6ec,g1fd858c14a+c8450ae71a,g3533f9d6cb+a04f9ee0ab,g35bb328faa+8c5ae1fdc5,g3f0dcc2b1b+7df08700bd,g4178042926+b4254969db,g44ba364a48+04455b336b,g53246c7159+8c5ae1fdc5,g60b5630c4e+a04f9ee0ab,g663da51e9b+b05e6e1875,g67b6fd64d1+250bf6acd3,g78460c75b0+7e33a9eb6d,g786e29fd12+668abc6043,g8352419a5c+8c5ae1fdc5,g87e3079a85+d3fa38de54,g8852436030+cd899e2626,g89139ef638+250bf6acd3,g93a033419f+31ead11197,g989de1cb63+250bf6acd3,g9f33ca652e+f6053ecf14,ga1e959baac+5fbc491aed,ga2f891cd6c+a04f9ee0ab,gabe3b4be73+8856018cbb,gabf8522325+1f7e6d67b9,gac2eed3f23+250bf6acd3,gb1101e3267+0c331e9486,gb89ab40317+250bf6acd3,gcf25f946ba+cd899e2626,gd107969129+8964d67276,gd6cbbdb0b4+6bbecc8878,gde0f65d7ad+d65f9e019a,ge278dab8ac+eb3bbeb12f,ge410e46f29+250bf6acd3,gf5e32f922b+8c5ae1fdc5,gff02db199a+747430a128,gffe7e49bb4+a04f9ee0ab
LSST Data Management Base Package
Loading...
Searching...
No Matches
lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask Class Reference
Inheritance diagram for lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask:
lsst.ip.diffim.subtractImages.AlardLuptonPreconvolveSubtractTask

Public Member Functions

 __init__ (self, **kwargs)
 
 run (self, template, science, sources, visitSummary=None)
 
 runConvolveTemplate (self, template, science, selectSources)
 
 runConvolveScience (self, template, science, selectSources)
 
 finalize (self, template, science, difference, kernel, templateMatched=True, preConvMode=False, preConvKernel=None, spatiallyVarying=False)
 
 updateMasks (self, template, science)
 

Public Attributes

 convolutionControl = lsst.afw.math.ConvolutionControl()
 
 templatePsfSize
 
 log = self.runConvolveScience(template, science, selectSources)
 
 sciencePsfSize = getPsfFwhm(science.psf)
 

Static Public Attributes

 ConfigClass = AlardLuptonSubtractConfig
 

Protected Member Functions

 _applyExternalCalibrations (self, exposure, visitSummary)
 
 _calculateMagLim (self, exposure, nsigma=5.0, fallbackPsfSize=None)
 
 _convolveExposure (self, exposure, kernel, convolutionControl, bbox=None, psf=None, photoCalib=None, interpolateBadMaskPlanes=False)
 
 _sourceSelector (self, sources, mask)
 
 _prepareInputs (self, template, science, visitSummary=None)
 
 _clearMask (self, mask, clearMaskPlanes=None)
 

Static Protected Member Functions

 _validateExposures (template, science)
 
 _renameMaskPlanes (mask, maskPlane, newMaskPlane)
 

Static Protected Attributes

str _DefaultName = "alardLuptonSubtract"
 

Detailed Description

Compute the image difference of a science and template image using
the Alard & Lupton (1998) algorithm.

Definition at line 271 of file subtractImages.py.

Constructor & Destructor Documentation

◆ __init__()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.__init__ ( self,
** kwargs )

Definition at line 278 of file subtractImages.py.

278 def __init__(self, **kwargs):
279 super().__init__(**kwargs)
280 self.makeSubtask("decorrelate")
281 self.makeSubtask("makeKernel")
282 self.makeSubtask("sourceSelector")
283 if self.config.doScaleVariance:
284 self.makeSubtask("scaleVariance")
285
286 self.convolutionControl = lsst.afw.math.ConvolutionControl()
287 # Normalization is an extra, unnecessary, calculation and will result
288 # in mis-subtraction of the images if there are calibration errors.
289 self.convolutionControl.setDoNormalize(False)
290 self.convolutionControl.setDoCopyEdge(True)
291
Parameters to control convolution.

Member Function Documentation

◆ _applyExternalCalibrations()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask._applyExternalCalibrations ( self,
exposure,
visitSummary )
protected
Replace calibrations (psf, and ApCorrMap) on this exposure with
external ones.".

Parameters
----------
exposure : `lsst.afw.image.exposure.Exposure`
    Input exposure to adjust calibrations.
visitSummary : `lsst.afw.table.ExposureCatalog`
    Exposure catalog with external calibrations to be applied. Catalog
    uses the detector id for the catalog id, sorted on id for fast
    lookup.

Returns
-------
exposure : `lsst.afw.image.exposure.Exposure`
    Exposure with adjusted calibrations.

Definition at line 292 of file subtractImages.py.

292 def _applyExternalCalibrations(self, exposure, visitSummary):
293 """Replace calibrations (psf, and ApCorrMap) on this exposure with
294 external ones.".
295
296 Parameters
297 ----------
298 exposure : `lsst.afw.image.exposure.Exposure`
299 Input exposure to adjust calibrations.
300 visitSummary : `lsst.afw.table.ExposureCatalog`
301 Exposure catalog with external calibrations to be applied. Catalog
302 uses the detector id for the catalog id, sorted on id for fast
303 lookup.
304
305 Returns
306 -------
307 exposure : `lsst.afw.image.exposure.Exposure`
308 Exposure with adjusted calibrations.
309 """
310 detectorId = exposure.info.getDetector().getId()
311
312 row = visitSummary.find(detectorId)
313 if row is None:
314 self.log.warning("Detector id %s not found in external calibrations catalog; "
315 "Using original calibrations.", detectorId)
316 else:
317 psf = row.getPsf()
318 apCorrMap = row.getApCorrMap()
319 if psf is None:
320 self.log.warning("Detector id %s has None for psf in "
321 "external calibrations catalog; Using original psf and aperture correction.",
322 detectorId)
323 elif apCorrMap is None:
324 self.log.warning("Detector id %s has None for apCorrMap in "
325 "external calibrations catalog; Using original psf and aperture correction.",
326 detectorId)
327 else:
328 exposure.setPsf(psf)
329 exposure.info.setApCorrMap(apCorrMap)
330
331 return exposure
332

◆ _calculateMagLim()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask._calculateMagLim ( self,
exposure,
nsigma = 5.0,
fallbackPsfSize = None )
protected
Calculate an exposure's limiting magnitude.

This method uses the photometric zeropoint together with the
PSF size from the average position of the exposure.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    The target exposure to calculate the limiting magnitude for.
nsigma : `float`, optional
    The detection threshold in sigma.
fallbackPsfSize : `float`, optional
    PSF FWHM to use in the event the exposure PSF cannot be retrieved.

Returns
-------
maglim : `astropy.units.Quantity`
    The limiting magnitude of the exposure, or np.nan.

Definition at line 649 of file subtractImages.py.

649 def _calculateMagLim(self, exposure, nsigma=5.0, fallbackPsfSize=None):
650 """Calculate an exposure's limiting magnitude.
651
652 This method uses the photometric zeropoint together with the
653 PSF size from the average position of the exposure.
654
655 Parameters
656 ----------
657 exposure : `lsst.afw.image.Exposure`
658 The target exposure to calculate the limiting magnitude for.
659 nsigma : `float`, optional
660 The detection threshold in sigma.
661 fallbackPsfSize : `float`, optional
662 PSF FWHM to use in the event the exposure PSF cannot be retrieved.
663
664 Returns
665 -------
666 maglim : `astropy.units.Quantity`
667 The limiting magnitude of the exposure, or np.nan.
668 """
669 if exposure.photoCalib is None:
670 return np.nan
671 try:
672 psf = exposure.getPsf()
673 psf_shape = psf.computeShape(psf.getAveragePosition())
674 except (lsst.pex.exceptions.InvalidParameterError, afwDetection.InvalidPsfError):
675 if fallbackPsfSize is not None:
676 self.log.info("Unable to evaluate PSF, using fallback FWHM %f", fallbackPsfSize)
677 psf_area = np.pi*(fallbackPsfSize/2)**2
678 zeropoint = exposure.photoCalib.instFluxToMagnitude(1)
679 maglim = zeropoint - 2.5*np.log10(nsigma*np.sqrt(psf_area))
680 else:
681 self.log.info("Unable to evaluate PSF, setting maglim to nan")
682 maglim = np.nan
683 else:
684 # Get a more accurate area than `psf_shape.getArea()` via moments
685 psf_area = np.pi*np.sqrt(psf_shape.getIxx()*psf_shape.getIyy())
686 zeropoint = exposure.photoCalib.instFluxToMagnitude(1)
687 maglim = zeropoint - 2.5*np.log10(nsigma*np.sqrt(psf_area))
688 finally:
689 return maglim
690
Reports invalid arguments.
Definition Runtime.h:66

◆ _clearMask()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask._clearMask ( self,
mask,
clearMaskPlanes = None )
protected
Clear the mask plane of an exposure.

Parameters
----------
mask : `lsst.afw.image.Mask`
    The mask plane to erase, which will be modified in place.
clearMaskPlanes : `list` of `str`, optional
    Erase the specified mask planes.
    If not supplied, the entire mask will be erased.

Definition at line 956 of file subtractImages.py.

956 def _clearMask(self, mask, clearMaskPlanes=None):
957 """Clear the mask plane of an exposure.
958
959 Parameters
960 ----------
961 mask : `lsst.afw.image.Mask`
962 The mask plane to erase, which will be modified in place.
963 clearMaskPlanes : `list` of `str`, optional
964 Erase the specified mask planes.
965 If not supplied, the entire mask will be erased.
966 """
967 if clearMaskPlanes is None:
968 clearMaskPlanes = list(mask.getMaskPlaneDict().keys())
969
970 bitMaskToClear = mask.getPlaneBitMask(clearMaskPlanes)
971 mask &= ~bitMaskToClear
972
973

◆ _convolveExposure()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask._convolveExposure ( self,
exposure,
kernel,
convolutionControl,
bbox = None,
psf = None,
photoCalib = None,
interpolateBadMaskPlanes = False )
protected
Convolve an exposure with the given kernel.

Parameters
----------
exposure : `lsst.afw.Exposure`
    exposure to convolve.
kernel : `lsst.afw.math.LinearCombinationKernel`
    PSF matching kernel computed in the ``makeKernel`` subtask.
convolutionControl : `lsst.afw.math.ConvolutionControl`
    Configuration for convolve algorithm.
bbox : `lsst.geom.Box2I`, optional
    Bounding box to trim the convolved exposure to.
psf : `lsst.afw.detection.Psf`, optional
    Point spread function (PSF) to set for the convolved exposure.
photoCalib : `lsst.afw.image.PhotoCalib`, optional
    Photometric calibration of the convolved exposure.

Returns
-------
convolvedExp : `lsst.afw.Exposure`
    The convolved image.

Definition at line 721 of file subtractImages.py.

726 ):
727 """Convolve an exposure with the given kernel.
728
729 Parameters
730 ----------
731 exposure : `lsst.afw.Exposure`
732 exposure to convolve.
733 kernel : `lsst.afw.math.LinearCombinationKernel`
734 PSF matching kernel computed in the ``makeKernel`` subtask.
735 convolutionControl : `lsst.afw.math.ConvolutionControl`
736 Configuration for convolve algorithm.
737 bbox : `lsst.geom.Box2I`, optional
738 Bounding box to trim the convolved exposure to.
739 psf : `lsst.afw.detection.Psf`, optional
740 Point spread function (PSF) to set for the convolved exposure.
741 photoCalib : `lsst.afw.image.PhotoCalib`, optional
742 Photometric calibration of the convolved exposure.
743
744 Returns
745 -------
746 convolvedExp : `lsst.afw.Exposure`
747 The convolved image.
748 """
749 convolvedExposure = exposure.clone()
750 if psf is not None:
751 convolvedExposure.setPsf(psf)
752 if photoCalib is not None:
753 convolvedExposure.setPhotoCalib(photoCalib)
754 if interpolateBadMaskPlanes and self.config.badMaskPlanes is not None:
755 nInterp = _interpolateImage(convolvedExposure.maskedImage,
756 self.config.badMaskPlanes)
757 self.metadata["nInterpolated"] = nInterp
758 convolvedImage = lsst.afw.image.MaskedImageF(convolvedExposure.getBBox())
759 lsst.afw.math.convolve(convolvedImage, convolvedExposure.maskedImage, kernel, convolutionControl)
760 convolvedExposure.setMaskedImage(convolvedImage)
761 if bbox is None:
762 return convolvedExposure
763 else:
764 return convolvedExposure[bbox]
765
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, ConvolutionControl const &convolutionControl=ConvolutionControl())
Convolve an Image or MaskedImage with a Kernel, setting pixels of an existing output image.

◆ _prepareInputs()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask._prepareInputs ( self,
template,
science,
visitSummary = None )
protected
Perform preparatory calculations common to all Alard&Lupton Tasks.

Parameters
----------
template : `lsst.afw.image.ExposureF`
    Template exposure, warped to match the science exposure. The
    variance plane of the template image is modified in place.
science : `lsst.afw.image.ExposureF`
    Science exposure to subtract from the template. The variance plane
    of the science image is modified in place.
visitSummary : `lsst.afw.table.ExposureCatalog`, optional
    Exposure catalog with external calibrations to be applied.  Catalog
    uses the detector id for the catalog id, sorted on id for fast
    lookup.

Definition at line 818 of file subtractImages.py.

818 def _prepareInputs(self, template, science, visitSummary=None):
819 """Perform preparatory calculations common to all Alard&Lupton Tasks.
820
821 Parameters
822 ----------
823 template : `lsst.afw.image.ExposureF`
824 Template exposure, warped to match the science exposure. The
825 variance plane of the template image is modified in place.
826 science : `lsst.afw.image.ExposureF`
827 Science exposure to subtract from the template. The variance plane
828 of the science image is modified in place.
829 visitSummary : `lsst.afw.table.ExposureCatalog`, optional
830 Exposure catalog with external calibrations to be applied. Catalog
831 uses the detector id for the catalog id, sorted on id for fast
832 lookup.
833 """
834 self._validateExposures(template, science)
835 if visitSummary is not None:
836 self._applyExternalCalibrations(science, visitSummary=visitSummary)
837 templateCoverageFraction = checkTemplateIsSufficient(
838 template[science.getBBox()], self.log,
839 requiredTemplateFraction=self.config.requiredTemplateFraction,
840 exceptionMessage="Not attempting subtraction. To force subtraction,"
841 " set config requiredTemplateFraction=0"
842 )
843 self.metadata["templateCoveragePercent"] = 100*templateCoverageFraction
844
845 if self.config.doScaleVariance:
846 # Scale the variance of the template and science images before
847 # convolution, subtraction, or decorrelation so that they have the
848 # correct ratio.
849 templateVarFactor = self.scaleVariance.run(template.maskedImage)
850 sciVarFactor = self.scaleVariance.run(science.maskedImage)
851 self.log.info("Template variance scaling factor: %.2f", templateVarFactor)
852 self.metadata["scaleTemplateVarianceFactor"] = templateVarFactor
853 self.log.info("Science variance scaling factor: %.2f", sciVarFactor)
854 self.metadata["scaleScienceVarianceFactor"] = sciVarFactor
855
856 # Erase existing detection mask planes.
857 # We don't want the detection mask from the science image
858 self.updateMasks(template, science)
859
860 # Calling getPsfFwhm on template.psf fails on some rare occasions when
861 # the template has no input exposures at the average position of the
862 # stars. So we try getPsfFwhm first on template, and if that fails we
863 # evaluate the PSF on a grid specified by fwhmExposure* fields.
864 # To keep consistent definitions for PSF size on the template and
865 # science images, we use the same method for both.
866 # In the try block below, we catch two exceptions:
867 # 1. InvalidParameterError, in case the point where we are evaluating
868 # the PSF lands in a gap in the template.
869 # 2. RangeError, in case the template coverage is so poor that we end
870 # up near a region with no data.
871 try:
872 self.templatePsfSize = getPsfFwhm(template.psf)
873 self.sciencePsfSize = getPsfFwhm(science.psf)
875 self.log.info("Unable to evaluate PSF at the average position. "
876 "Evaluting PSF on a grid of points."
877 )
878 self.templatePsfSize = evaluateMeanPsfFwhm(
879 template,
880 fwhmExposureBuffer=self.config.makeKernel.fwhmExposureBuffer,
881 fwhmExposureGrid=self.config.makeKernel.fwhmExposureGrid
882 )
883 self.sciencePsfSize = evaluateMeanPsfFwhm(
884 science,
885 fwhmExposureBuffer=self.config.makeKernel.fwhmExposureBuffer,
886 fwhmExposureGrid=self.config.makeKernel.fwhmExposureGrid
887 )
888 self.log.info("Science PSF FWHM: %f pixels", self.sciencePsfSize)
889 self.log.info("Template PSF FWHM: %f pixels", self.templatePsfSize)
890 self.metadata["sciencePsfSize"] = self.sciencePsfSize
891 self.metadata["templatePsfSize"] = self.templatePsfSize
892
Reports when the result of an operation cannot be represented by the destination type.
Definition Runtime.h:115

◆ _renameMaskPlanes()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask._renameMaskPlanes ( mask,
maskPlane,
newMaskPlane )
staticprotected
Rename a mask plane by adding the new name and copying the data.

Parameters
----------
mask : `lsst.afw.image.Mask`
    The mask image to update in place.
maskPlane : `str`
    The name of the existing mask plane to copy.
newMaskPlane : `str`
    The new name of the mask plane that will be added.
    If the mask plane already exists, it will be updated in place.

Definition at line 938 of file subtractImages.py.

938 def _renameMaskPlanes(mask, maskPlane, newMaskPlane):
939 """Rename a mask plane by adding the new name and copying the data.
940
941 Parameters
942 ----------
943 mask : `lsst.afw.image.Mask`
944 The mask image to update in place.
945 maskPlane : `str`
946 The name of the existing mask plane to copy.
947 newMaskPlane : `str`
948 The new name of the mask plane that will be added.
949 If the mask plane already exists, it will be updated in place.
950 """
951 mask.addMaskPlane(newMaskPlane)
952 originBitMask = mask.getPlaneBitMask(maskPlane)
953 destinationBitMask = mask.getPlaneBitMask(newMaskPlane)
954 mask.array |= ((mask.array & originBitMask) > 0)*destinationBitMask
955

◆ _sourceSelector()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask._sourceSelector ( self,
sources,
mask )
protected
Select sources from a catalog that meet the selection criteria.

Parameters
----------
sources : `lsst.afw.table.SourceCatalog`
    Input source catalog to select sources from.
mask : `lsst.afw.image.Mask`
    The image mask plane to use to reject sources
    based on their location on the ccd.

Returns
-------
selectSources : `lsst.afw.table.SourceCatalog`
    The input source catalog, with flagged and low signal-to-noise
    sources removed.

Raises
------
RuntimeError
    If there are too few sources to compute the PSF matching kernel
    remaining after source selection.

Definition at line 766 of file subtractImages.py.

766 def _sourceSelector(self, sources, mask):
767 """Select sources from a catalog that meet the selection criteria.
768
769 Parameters
770 ----------
771 sources : `lsst.afw.table.SourceCatalog`
772 Input source catalog to select sources from.
773 mask : `lsst.afw.image.Mask`
774 The image mask plane to use to reject sources
775 based on their location on the ccd.
776
777 Returns
778 -------
779 selectSources : `lsst.afw.table.SourceCatalog`
780 The input source catalog, with flagged and low signal-to-noise
781 sources removed.
782
783 Raises
784 ------
785 RuntimeError
786 If there are too few sources to compute the PSF matching kernel
787 remaining after source selection.
788 """
789
790 selected = self.sourceSelector.selectSources(sources).selected
791 nInitialSelected = np.count_nonzero(selected)
792 nSelected = np.count_nonzero(selected)
793 self.log.info("Rejecting %i candidate sources: an excluded template mask plane is set.",
794 nInitialSelected - nSelected)
795 selectSources = sources[selected].copy(deep=True)
796 # Trim selectSources if they exceed ``maxKernelSources``.
797 # Keep the highest signal-to-noise sources of those selected.
798 if (len(selectSources) > self.config.maxKernelSources) & (self.config.maxKernelSources > 0):
799 signalToNoise = selectSources.getPsfInstFlux()/selectSources.getPsfInstFluxErr()
800 indices = np.argsort(signalToNoise)
801 indices = indices[-self.config.maxKernelSources:]
802 selected = np.zeros(len(selectSources), dtype=bool)
803 selected[indices] = True
804 selectSources = selectSources[selected].copy(deep=True)
805
806 self.log.info("%i/%i=%.1f%% of sources selected for PSF matching from the input catalog",
807 len(selectSources), len(sources), 100*len(selectSources)/len(sources))
808 if len(selectSources) < self.config.minKernelSources:
809 self.log.error("Too few sources to calculate the PSF matching kernel: "
810 "%i selected but %i needed for the calculation.",
811 len(selectSources), self.config.minKernelSources)
812 if not self.config.allowKernelSourceDetection:
813 raise RuntimeError("Cannot compute PSF matching kernel: too few sources selected.")
814 self.metadata["nPsfSources"] = len(selectSources)
815
816 return selectSources
817

◆ _validateExposures()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask._validateExposures ( template,
science )
staticprotected
Check that the WCS of the two Exposures match, the template bbox
contains the science bbox, and that the bands match.

Parameters
----------
template : `lsst.afw.image.ExposureF`
    Template exposure, warped to match the science exposure.
science : `lsst.afw.image.ExposureF`
    Science exposure to subtract from the template.

Raises
------
AssertionError
    Raised if the WCS of the template is not equal to the science WCS,
    if the science image is not fully contained in the template
    bounding box, or if the bands do not match.

Definition at line 692 of file subtractImages.py.

692 def _validateExposures(template, science):
693 """Check that the WCS of the two Exposures match, the template bbox
694 contains the science bbox, and that the bands match.
695
696 Parameters
697 ----------
698 template : `lsst.afw.image.ExposureF`
699 Template exposure, warped to match the science exposure.
700 science : `lsst.afw.image.ExposureF`
701 Science exposure to subtract from the template.
702
703 Raises
704 ------
705 AssertionError
706 Raised if the WCS of the template is not equal to the science WCS,
707 if the science image is not fully contained in the template
708 bounding box, or if the bands do not match.
709 """
710 assert template.wcs == science.wcs, \
711 "Template and science exposure WCS are not identical."
712 templateBBox = template.getBBox()
713 scienceBBox = science.getBBox()
714 assert science.filter.bandLabel == template.filter.bandLabel, \
715 "Science and template exposures have different bands: %s, %s" % \
716 (science.filter, template.filter)
717
718 assert templateBBox.contains(scienceBBox), \
719 "Template bbox does not contain all of the science image."
720

◆ finalize()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.finalize ( self,
template,
science,
difference,
kernel,
templateMatched = True,
preConvMode = False,
preConvKernel = None,
spatiallyVarying = False )
Decorrelate the difference image to undo the noise correlations
caused by convolution.

Parameters
----------
template : `lsst.afw.image.ExposureF`
    Template exposure, warped to match the science exposure.
science : `lsst.afw.image.ExposureF`
    Science exposure to subtract from the template.
difference : `lsst.afw.image.ExposureF`
    Result of subtracting template and science.
kernel : `lsst.afw.math.Kernel`
    An (optionally spatially-varying) PSF matching kernel
templateMatched : `bool`, optional
    Was the template PSF-matched to the science image?
preConvMode : `bool`, optional
    Was the science image preconvolved with its own PSF
    before PSF matching the template?
preConvKernel : `lsst.afw.detection.Psf`, optional
    If not `None`, then the science image was pre-convolved with
    (the reflection of) this kernel. Must be normalized to sum to 1.
spatiallyVarying : `bool`, optional
    Compute the decorrelation kernel spatially varying across the image?

Returns
-------
correctedExposure : `lsst.afw.image.ExposureF`
    The decorrelated image difference.

Definition at line 600 of file subtractImages.py.

604 spatiallyVarying=False):
605 """Decorrelate the difference image to undo the noise correlations
606 caused by convolution.
607
608 Parameters
609 ----------
610 template : `lsst.afw.image.ExposureF`
611 Template exposure, warped to match the science exposure.
612 science : `lsst.afw.image.ExposureF`
613 Science exposure to subtract from the template.
614 difference : `lsst.afw.image.ExposureF`
615 Result of subtracting template and science.
616 kernel : `lsst.afw.math.Kernel`
617 An (optionally spatially-varying) PSF matching kernel
618 templateMatched : `bool`, optional
619 Was the template PSF-matched to the science image?
620 preConvMode : `bool`, optional
621 Was the science image preconvolved with its own PSF
622 before PSF matching the template?
623 preConvKernel : `lsst.afw.detection.Psf`, optional
624 If not `None`, then the science image was pre-convolved with
625 (the reflection of) this kernel. Must be normalized to sum to 1.
626 spatiallyVarying : `bool`, optional
627 Compute the decorrelation kernel spatially varying across the image?
628
629 Returns
630 -------
631 correctedExposure : `lsst.afw.image.ExposureF`
632 The decorrelated image difference.
633 """
634 if self.config.doDecorrelation:
635 self.log.info("Decorrelating image difference.")
636 # We have cleared the template mask plane, so copy the mask plane of
637 # the image difference so that we can calculate correct statistics
638 # during decorrelation
639 correctedExposure = self.decorrelate.run(science, template[science.getBBox()], difference, kernel,
640 templateMatched=templateMatched,
641 preConvMode=preConvMode,
642 preConvKernel=preConvKernel,
643 spatiallyVarying=spatiallyVarying).correctedExposure
644 else:
645 self.log.info("NOT decorrelating image difference.")
646 correctedExposure = difference
647 return correctedExposure
648

◆ run()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.run ( self,
template,
science,
sources,
visitSummary = None )
PSF match, subtract, and decorrelate two images.

Parameters
----------
template : `lsst.afw.image.ExposureF`
    Template exposure, warped to match the science exposure.
science : `lsst.afw.image.ExposureF`
    Science exposure to subtract from the template.
sources : `lsst.afw.table.SourceCatalog`
    Identified sources on the science exposure. This catalog is used to
    select sources in order to perform the AL PSF matching on stamp
    images around them.
visitSummary : `lsst.afw.table.ExposureCatalog`, optional
    Exposure catalog with external calibrations to be applied. Catalog
    uses the detector id for the catalog id, sorted on id for fast
    lookup.

Returns
-------
results : `lsst.pipe.base.Struct`
    ``difference`` : `lsst.afw.image.ExposureF`
        Result of subtracting template and science.
    ``matchedTemplate`` : `lsst.afw.image.ExposureF`
        Warped and PSF-matched template exposure.
    ``backgroundModel`` : `lsst.afw.math.Function2D`
        Background model that was fit while solving for the
        PSF-matching kernel
    ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
        Kernel used to PSF-match the convolved image.

Raises
------
RuntimeError
    If an unsupported convolution mode is supplied.
RuntimeError
    If there are too few sources to calculate the PSF matching kernel.
lsst.pipe.base.NoWorkFound
    Raised if fraction of good pixels, defined as not having NO_DATA
    set, is less then the configured requiredTemplateFraction

Reimplemented in lsst.ip.diffim.subtractImages.AlardLuptonPreconvolveSubtractTask.

Definition at line 334 of file subtractImages.py.

334 def run(self, template, science, sources, visitSummary=None):
335 """PSF match, subtract, and decorrelate two images.
336
337 Parameters
338 ----------
339 template : `lsst.afw.image.ExposureF`
340 Template exposure, warped to match the science exposure.
341 science : `lsst.afw.image.ExposureF`
342 Science exposure to subtract from the template.
343 sources : `lsst.afw.table.SourceCatalog`
344 Identified sources on the science exposure. This catalog is used to
345 select sources in order to perform the AL PSF matching on stamp
346 images around them.
347 visitSummary : `lsst.afw.table.ExposureCatalog`, optional
348 Exposure catalog with external calibrations to be applied. Catalog
349 uses the detector id for the catalog id, sorted on id for fast
350 lookup.
351
352 Returns
353 -------
354 results : `lsst.pipe.base.Struct`
355 ``difference`` : `lsst.afw.image.ExposureF`
356 Result of subtracting template and science.
357 ``matchedTemplate`` : `lsst.afw.image.ExposureF`
358 Warped and PSF-matched template exposure.
359 ``backgroundModel`` : `lsst.afw.math.Function2D`
360 Background model that was fit while solving for the
361 PSF-matching kernel
362 ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
363 Kernel used to PSF-match the convolved image.
364
365 Raises
366 ------
367 RuntimeError
368 If an unsupported convolution mode is supplied.
369 RuntimeError
370 If there are too few sources to calculate the PSF matching kernel.
371 lsst.pipe.base.NoWorkFound
372 Raised if fraction of good pixels, defined as not having NO_DATA
373 set, is less then the configured requiredTemplateFraction
374 """
375 self._prepareInputs(template, science, visitSummary=visitSummary)
376
377 # Calculate estimated image depths, i.e., limiting magnitudes
378 maglim_science = self._calculateMagLim(science, fallbackPsfSize=self.sciencePsfSize)
379 if np.isnan(maglim_science):
380 self.log.warning("Limiting magnitude of the science image is NaN!")
381 fluxlim_science = (maglim_science*u.ABmag).to_value(u.nJy)
382 maglim_template = self._calculateMagLim(template, fallbackPsfSize=self.templatePsfSize)
383 if np.isnan(maglim_template):
384 self.log.info("Cannot evaluate template limiting mag; adopting science limiting mag for diffim")
385 maglim_diffim = maglim_science
386 else:
387 fluxlim_template = (maglim_template*u.ABmag).to_value(u.nJy)
388 maglim_diffim = (np.sqrt(fluxlim_science**2 + fluxlim_template**2)*u.nJy).to(u.ABmag).value
389 self.metadata["scienceLimitingMagnitude"] = maglim_science
390 self.metadata["templateLimitingMagnitude"] = maglim_template
391 self.metadata["diffimLimitingMagnitude"] = maglim_diffim
392
393 if self.config.mode == "auto":
394 convolveTemplate = _shapeTest(template,
395 science,
396 fwhmExposureBuffer=self.config.makeKernel.fwhmExposureBuffer,
397 fwhmExposureGrid=self.config.makeKernel.fwhmExposureGrid)
398 if convolveTemplate:
399 if self.sciencePsfSize < self.templatePsfSize:
400 self.log.info("Average template PSF size is greater, "
401 "but science PSF greater in one dimension: convolving template image.")
402 else:
403 self.log.info("Science PSF size is greater: convolving template image.")
404 else:
405 self.log.info("Template PSF size is greater: convolving science image.")
406 elif self.config.mode == "convolveTemplate":
407 self.log.info("`convolveTemplate` is set: convolving template image.")
408 convolveTemplate = True
409 elif self.config.mode == "convolveScience":
410 self.log.info("`convolveScience` is set: convolving science image.")
411 convolveTemplate = False
412 else:
413 raise RuntimeError("Cannot handle AlardLuptonSubtract mode: %s", self.config.mode)
414
415 try:
416 sourceMask = science.mask.clone()
417 sourceMask.array |= template[science.getBBox()].mask.array
418 selectSources = self._sourceSelector(sources, sourceMask)
419 if convolveTemplate:
420 self.metadata["convolvedExposure"] = "Template"
421 subtractResults = self.runConvolveTemplate(template, science, selectSources)
422 else:
423 self.metadata["convolvedExposure"] = "Science"
424 subtractResults = self.runConvolveScience(template, science, selectSources)
425
426 except (RuntimeError, lsst.pex.exceptions.Exception) as e:
427 self.log.warning("Failed to match template. Checking coverage")
428 # Raise NoWorkFound if template fraction is insufficient
429 checkTemplateIsSufficient(template[science.getBBox()], self.log,
430 self.config.minTemplateFractionForExpectedSuccess,
431 exceptionMessage="Template coverage lower than expected to succeed."
432 f" Failure is tolerable: {e}")
433 # checkTemplateIsSufficient did not raise NoWorkFound, so raise original exception
434 raise e
435
436 metrics = computeDifferenceImageMetrics(science, subtractResults.difference, sources)
437
438 self.metadata["differenceFootprintRatioMean"] = metrics.differenceFootprintRatioMean
439 self.metadata["differenceFootprintRatioStdev"] = metrics.differenceFootprintRatioStdev
440 self.metadata["differenceFootprintSkyRatioMean"] = metrics.differenceFootprintSkyRatioMean
441 self.metadata["differenceFootprintSkyRatioStdev"] = metrics.differenceFootprintSkyRatioStdev
442 self.log.info("Mean, stdev of ratio of difference to science "
443 "pixels in star footprints: %5.4f, %5.4f",
444 self.metadata["differenceFootprintRatioMean"],
445 self.metadata["differenceFootprintRatioStdev"])
446
447 return subtractResults
448
Provides consistent interface for LSST exceptions.
Definition Exception.h:107

◆ runConvolveScience()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.runConvolveScience ( self,
template,
science,
selectSources )
Convolve the science image with a PSF-matching kernel and subtract
the template image.

Parameters
----------
template : `lsst.afw.image.ExposureF`
    Template exposure, warped to match the science exposure.
science : `lsst.afw.image.ExposureF`
    Science exposure to subtract from the template.
selectSources : `lsst.afw.table.SourceCatalog`
    Identified sources on the science exposure. This catalog is used to
    select sources in order to perform the AL PSF matching on stamp
    images around them.

Returns
-------
results : `lsst.pipe.base.Struct`

    ``difference`` : `lsst.afw.image.ExposureF`
        Result of subtracting template and science.
    ``matchedTemplate`` : `lsst.afw.image.ExposureF`
        Warped template exposure. Note that in this case, the template
        is not PSF-matched to the science image.
    ``backgroundModel`` : `lsst.afw.math.Function2D`
        Background model that was fit while solving for the PSF-matching kernel
    ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
       Kernel used to PSF-match the science image to the template.

Definition at line 529 of file subtractImages.py.

529 def runConvolveScience(self, template, science, selectSources):
530 """Convolve the science image with a PSF-matching kernel and subtract
531 the template image.
532
533 Parameters
534 ----------
535 template : `lsst.afw.image.ExposureF`
536 Template exposure, warped to match the science exposure.
537 science : `lsst.afw.image.ExposureF`
538 Science exposure to subtract from the template.
539 selectSources : `lsst.afw.table.SourceCatalog`
540 Identified sources on the science exposure. This catalog is used to
541 select sources in order to perform the AL PSF matching on stamp
542 images around them.
543
544 Returns
545 -------
546 results : `lsst.pipe.base.Struct`
547
548 ``difference`` : `lsst.afw.image.ExposureF`
549 Result of subtracting template and science.
550 ``matchedTemplate`` : `lsst.afw.image.ExposureF`
551 Warped template exposure. Note that in this case, the template
552 is not PSF-matched to the science image.
553 ``backgroundModel`` : `lsst.afw.math.Function2D`
554 Background model that was fit while solving for the PSF-matching kernel
555 ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
556 Kernel used to PSF-match the science image to the template.
557 """
558 bbox = science.getBBox()
559 kernelSources = self.makeKernel.selectKernelSources(science, template,
560 candidateList=selectSources,
561 preconvolved=False,
562 templateFwhmPix=self.templatePsfSize,
563 scienceFwhmPix=self.sciencePsfSize)
564 kernelResult = self.makeKernel.run(science, template, kernelSources,
565 preconvolved=False,
566 templateFwhmPix=self.templatePsfSize,
567 scienceFwhmPix=self.sciencePsfSize)
568 modelParams = kernelResult.backgroundModel.getParameters()
569 # We must invert the background model if the matching kernel is solved for the science image.
570 kernelResult.backgroundModel.setParameters([-p for p in modelParams])
571
572 kernelImage = lsst.afw.image.ImageD(kernelResult.psfMatchingKernel.getDimensions())
573 norm = kernelResult.psfMatchingKernel.computeImage(kernelImage, doNormalize=False)
574
575 matchedScience = self._convolveExposure(science, kernelResult.psfMatchingKernel,
576 self.convolutionControl,
577 psf=template.psf)
578
579 # Place back on native photometric scale
580 matchedScience.maskedImage /= norm
581 matchedTemplate = template.clone()[bbox]
582 matchedTemplate.maskedImage /= norm
583 matchedTemplate.setPhotoCalib(science.photoCalib)
584
585 difference = _subtractImages(matchedScience, matchedTemplate,
586 backgroundModel=(kernelResult.backgroundModel
587 if self.config.doSubtractBackground else None))
588
589 correctedExposure = self.finalize(template, science, difference,
590 kernelResult.psfMatchingKernel,
591 templateMatched=False)
592
593 return lsst.pipe.base.Struct(difference=correctedExposure,
594 matchedTemplate=matchedTemplate,
595 matchedScience=matchedScience,
596 backgroundModel=kernelResult.backgroundModel,
597 psfMatchingKernel=kernelResult.psfMatchingKernel,
598 kernelSources=kernelSources)
599

◆ runConvolveTemplate()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.runConvolveTemplate ( self,
template,
science,
selectSources )
Convolve the template image with a PSF-matching kernel and subtract
from the science image.

Parameters
----------
template : `lsst.afw.image.ExposureF`
    Template exposure, warped to match the science exposure.
science : `lsst.afw.image.ExposureF`
    Science exposure to subtract from the template.
selectSources : `lsst.afw.table.SourceCatalog`
    Identified sources on the science exposure. This catalog is used to
    select sources in order to perform the AL PSF matching on stamp
    images around them.

Returns
-------
results : `lsst.pipe.base.Struct`

    ``difference`` : `lsst.afw.image.ExposureF`
        Result of subtracting template and science.
    ``matchedTemplate`` : `lsst.afw.image.ExposureF`
        Warped and PSF-matched template exposure.
    ``backgroundModel`` : `lsst.afw.math.Function2D`
        Background model that was fit while solving for the PSF-matching kernel
    ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
        Kernel used to PSF-match the template to the science image.

Definition at line 449 of file subtractImages.py.

449 def runConvolveTemplate(self, template, science, selectSources):
450 """Convolve the template image with a PSF-matching kernel and subtract
451 from the science image.
452
453 Parameters
454 ----------
455 template : `lsst.afw.image.ExposureF`
456 Template exposure, warped to match the science exposure.
457 science : `lsst.afw.image.ExposureF`
458 Science exposure to subtract from the template.
459 selectSources : `lsst.afw.table.SourceCatalog`
460 Identified sources on the science exposure. This catalog is used to
461 select sources in order to perform the AL PSF matching on stamp
462 images around them.
463
464 Returns
465 -------
466 results : `lsst.pipe.base.Struct`
467
468 ``difference`` : `lsst.afw.image.ExposureF`
469 Result of subtracting template and science.
470 ``matchedTemplate`` : `lsst.afw.image.ExposureF`
471 Warped and PSF-matched template exposure.
472 ``backgroundModel`` : `lsst.afw.math.Function2D`
473 Background model that was fit while solving for the PSF-matching kernel
474 ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
475 Kernel used to PSF-match the template to the science image.
476 """
477 try:
478 kernelSources = self.makeKernel.selectKernelSources(template, science,
479 candidateList=selectSources,
480 preconvolved=False,
481 templateFwhmPix=self.templatePsfSize,
482 scienceFwhmPix=self.sciencePsfSize)
483 kernelResult = self.makeKernel.run(template, science, kernelSources,
484 preconvolved=False,
485 templateFwhmPix=self.templatePsfSize,
486 scienceFwhmPix=self.sciencePsfSize)
487 except Exception as e:
488 if self.config.allowKernelSourceDetection:
489 self.log.warning("Error encountered trying to construct the matching kernel"
490 f" Running source detection and retrying. {e}")
491 kernelSize = self.makeKernel.makeKernelBasisList(
492 self.templatePsfSize, self.sciencePsfSize)[0].getWidth()
493 sigmaToFwhm = 2*np.log(2*np.sqrt(2))
494 candidateList = self.makeKernel.makeCandidateList(template, science, kernelSize,
495 candidateList=None,
496 sigma=self.sciencePsfSize/sigmaToFwhm)
497 kernelSources = self.makeKernel.selectKernelSources(template, science,
498 candidateList=candidateList,
499 preconvolved=False,
500 templateFwhmPix=self.templatePsfSize,
501 scienceFwhmPix=self.sciencePsfSize)
502 kernelResult = self.makeKernel.run(template, science, kernelSources,
503 preconvolved=False,
504 templateFwhmPix=self.templatePsfSize,
505 scienceFwhmPix=self.sciencePsfSize)
506 else:
507 raise e
508
509 matchedTemplate = self._convolveExposure(template, kernelResult.psfMatchingKernel,
510 self.convolutionControl,
511 bbox=science.getBBox(),
512 psf=science.psf,
513 photoCalib=science.photoCalib)
514
515 difference = _subtractImages(science, matchedTemplate,
516 backgroundModel=(kernelResult.backgroundModel
517 if self.config.doSubtractBackground else None))
518 correctedExposure = self.finalize(template, science, difference,
519 kernelResult.psfMatchingKernel,
520 templateMatched=True)
521
522 return lsst.pipe.base.Struct(difference=correctedExposure,
523 matchedTemplate=matchedTemplate,
524 matchedScience=science,
525 backgroundModel=kernelResult.backgroundModel,
526 psfMatchingKernel=kernelResult.psfMatchingKernel,
527 kernelSources=kernelSources)
528

◆ updateMasks()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.updateMasks ( self,
template,
science )
Update the science and template mask planes before differencing.

Parameters
----------
template : `lsst.afw.image.Exposure`
    Template exposure, warped to match the science exposure.
    The template mask planes will be erased, except for a few specified
    in the task config.
science : `lsst.afw.image.Exposure`
    Science exposure to subtract from the template.
    The DETECTED and DETECTED_NEGATIVE mask planes of the science image
    will be erased.

Definition at line 893 of file subtractImages.py.

893 def updateMasks(self, template, science):
894 """Update the science and template mask planes before differencing.
895
896 Parameters
897 ----------
898 template : `lsst.afw.image.Exposure`
899 Template exposure, warped to match the science exposure.
900 The template mask planes will be erased, except for a few specified
901 in the task config.
902 science : `lsst.afw.image.Exposure`
903 Science exposure to subtract from the template.
904 The DETECTED and DETECTED_NEGATIVE mask planes of the science image
905 will be erased.
906 """
907 self._clearMask(science.mask, clearMaskPlanes=["DETECTED", "DETECTED_NEGATIVE"])
908
909 # We will clear ALL template mask planes, except for those specified
910 # via the `preserveTemplateMask` config. Mask planes specified via
911 # the `renameTemplateMask` config will be copied to new planes with
912 # "_TEMPLATE" appended to their names, and the original mask plane will
913 # be cleared.
914 clearMaskPlanes = [mp for mp in template.mask.getMaskPlaneDict().keys()
915 if mp not in self.config.preserveTemplateMask]
916 renameMaskPlanes = [mp for mp in self.config.renameTemplateMask
917 if mp in template.mask.getMaskPlaneDict().keys()]
918
919 # propagate the mask plane related to Fake source injection
920 # NOTE: the fake source injection sets FAKE plane, but it should be INJECTED
921 # NOTE: This can be removed in DM-40796
922 if "FAKE" in science.mask.getMaskPlaneDict().keys():
923 self.log.info("Adding injected mask plane to science image")
924 self._renameMaskPlanes(science.mask, "FAKE", "INJECTED")
925 if "FAKE" in template.mask.getMaskPlaneDict().keys():
926 self.log.info("Adding injected mask plane to template image")
927 self._renameMaskPlanes(template.mask, "FAKE", "INJECTED_TEMPLATE")
928 if "INJECTED" in renameMaskPlanes:
929 renameMaskPlanes.remove("INJECTED")
930 if "INJECTED_TEMPLATE" in clearMaskPlanes:
931 clearMaskPlanes.remove("INJECTED_TEMPLATE")
932
933 for maskPlane in renameMaskPlanes:
934 self._renameMaskPlanes(template.mask, maskPlane, maskPlane + "_TEMPLATE")
935 self._clearMask(template.mask, clearMaskPlanes=clearMaskPlanes)
936

Member Data Documentation

◆ _DefaultName

str lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask._DefaultName = "alardLuptonSubtract"
staticprotected

Definition at line 276 of file subtractImages.py.

◆ ConfigClass

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.ConfigClass = AlardLuptonSubtractConfig
static

Definition at line 275 of file subtractImages.py.

◆ convolutionControl

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.convolutionControl = lsst.afw.math.ConvolutionControl()

Definition at line 286 of file subtractImages.py.

◆ log

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.log = self.runConvolveScience(template, science, selectSources)

Definition at line 429 of file subtractImages.py.

◆ sciencePsfSize

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.sciencePsfSize = getPsfFwhm(science.psf)

Definition at line 873 of file subtractImages.py.

◆ templatePsfSize

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.templatePsfSize

Definition at line 399 of file subtractImages.py.


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