From 12dc6b80389d777ba42403958cdbc9bbf0e9c80e Mon Sep 17 00:00:00 2001 From: Arun Kannawadi Date: Mon, 1 Dec 2025 22:38:48 -0500 Subject: [PATCH 1/3] Improve the log message when PSF is not normalized --- python/lsst/drp/tasks/assemble_cell_coadd.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/python/lsst/drp/tasks/assemble_cell_coadd.py b/python/lsst/drp/tasks/assemble_cell_coadd.py index 8b322da9..a743ec31 100644 --- a/python/lsst/drp/tasks/assemble_cell_coadd.py +++ b/python/lsst/drp/tasks/assemble_cell_coadd.py @@ -809,11 +809,12 @@ def run( psf_stacker = psf_stacker_gc[cellInfo.index] psf_stacker.add_masked_image(warped_psf_maskedImage, weight=weight) - if warped_psf_maskedImage.image.array.sum() < 0.995: + if not (0.995 < (psf_normalization := warped_psf_maskedImage.image.array.sum()) < 1.005): self.log.warning( - "PSF for %s in %s lost more than 0.5 per cent of flux", + "PSF image for %s in %s is not normalized to 1.0, but instead %f", warp_input.dataId, cellInfo.index, + psf_normalization, ) if (ap_corr_map := warp.getInfo().getApCorrMap()) is not None: From fca02cb0e2cbce1dedb206014783bfa05bf73ac5 Mon Sep 17 00:00:00 2001 From: Arun Kannawadi Date: Mon, 1 Dec 2025 22:38:23 -0500 Subject: [PATCH 2/3] Calculate the padding and zero-pad PSF image --- python/lsst/drp/tasks/assemble_cell_coadd.py | 23 ++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/python/lsst/drp/tasks/assemble_cell_coadd.py b/python/lsst/drp/tasks/assemble_cell_coadd.py index a743ec31..0653712c 100644 --- a/python/lsst/drp/tasks/assemble_cell_coadd.py +++ b/python/lsst/drp/tasks/assemble_cell_coadd.py @@ -323,6 +323,20 @@ def __init__(self, *args, **kwargs): self.makeSubtask("scale_zero_point") self.psf_warper = afwMath.Warper.fromConfig(self.config.psf_warper) + if (warping_kernel_name := self.config.psf_warper.warpingKernelName.lower()).startswith("lanczos"): + psf_padding = 2 * int(warping_kernel_name.lstrip("lanczos")) - 1 + self.log.debug( + "Padding PSF image by %d pixels since the warping kernel is %s.", + psf_padding, + self.config.psf_warper.warpingKernelName, + ) + else: + psf_padding = 10 + self.log.info( + "Padding PSF image by %d pixels since the warping kernel is not Lanczos.", + psf_padding, + ) + self.psf_padding = psf_padding def runQuantum(self, butlerQC, inputRefs, outputRefs): # Docstring inherited. @@ -790,8 +804,13 @@ def run( undistorted_psf_im.getDimensions(), ), "PSF image does not share the coordinates of the 'calexp'" - # Convert the PSF image from Image to MaskedImage. - undistorted_psf_maskedImage = afwImage.MaskedImageD(image=undistorted_psf_im) + # Convert the PSF image from Image to MaskedImage and + # zero-pad the image. + undistorted_psf_bbox = undistorted_psf_im.getBBox() + undistorted_psf_maskedImage = afwImage.MaskedImageD( + undistorted_psf_bbox.dilatedBy(self.psf_padding) + ) + undistorted_psf_maskedImage.image[undistorted_psf_bbox].array[:, :] = undistorted_psf_im.array # TODO: In DM-43585, use the variance plane value from noise. undistorted_psf_maskedImage.variance += 1.0 # Set variance to 1 From 386051a96382b2c30cc3f315f93bc421fce614be Mon Sep 17 00:00:00 2001 From: Arun Kannawadi Date: Mon, 1 Dec 2025 22:39:10 -0500 Subject: [PATCH 3/3] Add a unit test checking PSF normalization --- tests/test_assemble_cell_coadd.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_assemble_cell_coadd.py b/tests/test_assemble_cell_coadd.py index 92580e90..f12f72d5 100644 --- a/tests/test_assemble_cell_coadd.py +++ b/tests/test_assemble_cell_coadd.py @@ -284,6 +284,13 @@ def test_inputs_sorted(self): for _, singleCellCoadd in self.result.multipleCellCoadd.cells.items(): self.checkSortOrder(singleCellCoadd.inputs) + def test_psf_normalization(self): + """Check that the sum of PSF images is close to 1.""" + self.runTask() + for cellId, singleCellCoadd in self.result.multipleCellCoadd.cells.items(): + with self.subTest(x=repr(cellId.x), y=repr(cellId.y)): + self.assertFloatsAlmostEqual(singleCellCoadd.psf_image.array.sum(), 1.0, rtol=None, atol=1e-7) + class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase): pass