diff --git a/python/lsst/ip/diffim/getTemplate.py b/python/lsst/ip/diffim/getTemplate.py index 70382b038..d52e1e680 100644 --- a/python/lsst/ip/diffim/getTemplate.py +++ b/python/lsst/ip/diffim/getTemplate.py @@ -270,6 +270,37 @@ def getExposures(self, coaddExposureHandles, bbox, skymap, wcs): return pipeBase.Struct(coaddExposures=coaddExposures, dataIds=dataIds) + def checkPatch(self, coaddPatch, dataId): + """Check for invalid pixels in the coadd and raise warning if value is + non-zero. + + Parameters + ---------- + coaddPatch: `dict` [`int`, `list` [`lsst.afw.image.Exposure`]] + Coadd to be mosaicked, indexed on tract id. + dataId: `dict` [`int`, `list` [`lsst.daf.butler.DataCoordinate`]] + Record of the tract and patch of coaddPatch, indexed on + tract id. + + Raises + ------ + Warning + If invalid pixels are found in the coadd. + """ + bad = np.logical_not(np.isfinite(coaddPatch.image.array)) + y, x = np.nonzero(bad) + badN = len(np.nonzero(bad)[0]) + if badN > 0: + print(dataId) + self.log.warning( + "%s invalid pixels in coadd using input tract=%s, patch=%s", + badN, + dataId['tract'], + dataId['patch'], + ) + # modify patch to set mask planes correctly & get rid of invalid pixels + return coaddPatch + @timeMethod def run(self, *, coaddExposureHandles, bbox, wcs, dataIds, physical_filter): """Warp coadds from multiple tracts and patches to form a template to @@ -473,7 +504,7 @@ def _makeExposureCatalog(self, exposureRefs, dataIds): """ catalog = afwTable.ExposureCatalog(self.schema) catalog.reserve(len(exposureRefs)) - exposures = (exposureRef.get() for exposureRef in exposureRefs) + exposures = (self.checkPatch(exposureRef.get()) for exposureRef in exposureRefs) images = {} totalBox = geom.Box2I() diff --git a/tests/test_getTemplate.py b/tests/test_getTemplate.py index 6e888392c..59deb7156 100644 --- a/tests/test_getTemplate.py +++ b/tests/test_getTemplate.py @@ -22,7 +22,7 @@ import collections import itertools import unittest - +from unittest.mock import patch import numpy as np import lsst.afw.geom @@ -335,6 +335,51 @@ def testNanInputs(self, box=None, nInput=None): # in the template are closer to the original anymore. self.assertTrue(np.isfinite(result.template.image.array).all()) + @patch('lsst.log.WARNING') + def testCheckPatchGoodPixels(self, mock_warning): + """ Check that coadd with no nan pixels and doesn't raise invalid pixel + warning. + """ + getTemplate = lsst.ip.diffim.getTemplate.GetTemplateTask() + coaddPatch = self.exposure + dataId = self.dataIds[0] + getTemplate.checkPatch(coaddPatch, dataId) + self.assertEqual(mock_warning.call_count, 0) + + @patch('lsst.log.WARNING') + def testCheckPatchOneBadPixel(self, mock_warning): + """ Check that coadd with one bad pixel raises one warning. + """ + getTemplate = lsst.ip.diffim.getTemplate.GetTemplateTask() + coaddPatch = self.exposure + coaddPatch[0, 0] = (np.nan, 32, 4.0) + dataId = self.dataIds[0] + getTemplate.checkPatch(coaddPatch, dataId) + self.assertEqual(mock_warning.call_count, 1) + + @patch('lsst.log.WARNING') + def testCheckPatchBadPixels(self, mock_warning): + """ Check that coadd with multiple bad pixels raises a single warning. + """ + self.assertEqual(mock_warning.call_count, 1) + + @patch('lsst.log.WARNING') + def testRunTemplateBadPixels(self): + """Test a bounding box that fully fits inside one tract, with only + that tract passed as input. This checks that the code handles a single + tract input correctly. + """ + box = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Point2I(180, 180)) + task = lsst.ip.diffim.GetTemplateTask() + # Restrict to tract 0, since the box fits in just that tract. + # Task modifies the input bbox, so pass a copy. + result = task.run({0: self.patches[0]}, lsst.geom.Box2I(box), + self.exposure.wcs, {0: self.dataIds[0]}, "a_test") + + # All 4 patches from tract 0 are included in this template. + self._checkMetadata(result.template, task.config, box, self.exposure.wcs, 4) + self._checkPixels(result.template, task.config, box) + def setup_module(module): lsst.utils.tests.init()