Coverage for tests/test_calibrate.py: 29%
115 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-19 02:21 -0700
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-19 02:21 -0700
1# This file is part of pipe_tasks.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22"""Test ProcessCcdTask and its immediate subtasks.
23"""
24import logging
25import os
26import shutil
27import tempfile
28import unittest
30import lsst.utils.tests
31import lsst.afw.image
32import lsst.afw.math
33import lsst.afw.table
34import lsst.daf.butler.tests as butlerTests
35from lsst.utils import getPackageDir
36from lsst.pipe.base import testUtils
37from lsst.pipe.tasks.calibrate import CalibrateTask, CalibrateConfig
38from lsst.pipe.tasks.characterizeImage import CharacterizeImageTask, CharacterizeImageConfig
39import lsst.meas.extensions.piff.piffPsfDeterminer
42class CalibrateTaskTestCaseWithButler(lsst.utils.tests.TestCase):
44 @classmethod
45 def _makeTestRepo(cls, root):
46 """Create a repository with the metadata assumed by CalibrateTask.
47 """
48 # In-memory for performance
49 config = lsst.daf.butler.Config()
50 config["datastore", "cls"] = "lsst.daf.butler.datastores.inMemoryDatastore.InMemoryDatastore"
51 config["datastore", "checksum"] = False
52 config["registry", "db"] = "sqlite:///:memory:"
54 butler = lsst.daf.butler.Butler(lsst.daf.butler.Butler.makeRepo(root, config=config), writeable=True)
55 butler.registry.insertDimensionData(
56 "instrument",
57 {"name": "notACam", "visit_max": 256, "exposure_max": 256, "detector_max": 64})
58 butler.registry.insertDimensionData(
59 "physical_filter",
60 {"instrument": "notACam", "name": "r", "band": "r"},
61 )
62 if "day_obs" in butler.dimensions:
63 butler.registry.insertDimensionData(
64 "day_obs",
65 {"id": 20240201, "instrument": "notACam"},
66 )
67 butler.registry.insertDimensionData(
68 "visit",
69 {"instrument": "notACam", "id": 101, "name": "101", "physical_filter": "r", "day_obs": 20240201},
70 )
71 butler.registry.insertDimensionData("detector",
72 {"instrument": "notACam", "id": 42, "full_name": "42"})
73 return butler
75 @classmethod
76 def setUpClass(cls):
77 super().setUpClass()
79 cls.root = tempfile.mkdtemp()
80 cls.repo = cls._makeTestRepo(cls.root)
82 butlerTests.addDatasetType(
83 cls.repo, "icExp", {"instrument", "visit", "detector"},
84 "ExposureF")
85 butlerTests.addDatasetType(
86 cls.repo, "icExpBackground", {"instrument", "visit", "detector"},
87 "Background")
88 butlerTests.addDatasetType(
89 cls.repo, "icSrc", {"instrument", "visit", "detector"},
90 "SourceCatalog")
91 butlerTests.addDatasetType(
92 cls.repo, "cal_ref_cat", {"htm7"},
93 "SimpleCatalog")
94 butlerTests.addDatasetType(
95 cls.repo, "calexp", {"instrument", "visit", "detector"},
96 "ExposureF")
97 butlerTests.addDatasetType(
98 cls.repo, "src", {"instrument", "visit", "detector"},
99 "SourceCatalog")
100 butlerTests.addDatasetType(
101 cls.repo, "calexpBackground", {"instrument", "visit", "detector"},
102 "Background")
103 butlerTests.addDatasetType(
104 cls.repo, "srcMatch", {"instrument", "visit", "detector"},
105 "Catalog")
106 butlerTests.addDatasetType(
107 cls.repo, "srcMatchFull", {"instrument", "visit", "detector"},
108 "Catalog")
110 @classmethod
111 def tearDownClass(cls):
112 shutil.rmtree(cls.root, ignore_errors=True)
113 super().tearDownClass()
115 def setUp(self):
116 super().setUp()
117 self.butler = butlerTests.makeTestCollection(self.repo, uniqueId=self.id())
119 self.dataId = {"instrument": "notACam", "visit": 101, "detector": 42}
120 # CalibrateTask absolutely requires an ExpandedDataCoordinate
121 self.dataId = self.butler.registry.expandDataId(self.dataId)
122 self.refcatId = {"htm7": 189584}
124 # Tests do no processing, so we don't need real data
125 self.exposure = lsst.afw.image.ExposureF(10, 10)
126 background = lsst.afw.math.BackgroundMI(self.exposure.getBBox(), self.exposure.getMaskedImage())
127 self.backgroundlist = lsst.afw.math.BackgroundList(
128 (background, lsst.afw.math.Interpolate.UNKNOWN, lsst.afw.math.UndersampleStyle.THROW_EXCEPTION,
129 lsst.afw.math.ApproximateControl.UNKNOWN, 0, 0, 1))
130 self.icSrc = lsst.afw.table.SourceCatalog()
131 self.refcat = lsst.afw.table.SimpleCatalog()
133 self.butler.put(self.exposure, "icExp", self.dataId)
134 self.butler.put(self.backgroundlist, "icExpBackground", self.dataId)
135 self.butler.put(self.icSrc, "icSrc", self.dataId)
136 self.butler.put(self.refcat, "cal_ref_cat", self.refcatId)
138 def testDoAstrometry(self):
139 """Ensure correct inputs passed to run whether or not doAstrometry
140 is set.
141 """
142 allIds = {key: self.dataId for key in {
143 "exposure", "background", "icSourceCat", "outputExposure", "outputCat", "outputBackground",
144 "matches", "matchesDenormalized"
145 }}
146 allIds.update({key: [self.refcatId] for key in {"astromRefCat", "photoRefCat"}})
148 self._checkDoRefcats(doAstrometry=True, doPhotoCal=True, ids=allIds)
149 self._checkDoRefcats(doAstrometry=False, doPhotoCal=True, ids=allIds)
151 def testDoPhotoCal(self):
152 """Ensure correct inputs passed to run whether or not doPhotoCal
153 is set.
154 """
155 allIds = {key: self.dataId for key in {
156 "exposure", "background", "icSourceCat", "outputExposure", "outputCat", "outputBackground",
157 "matches", "matchesDenormalized"
158 }}
159 allIds.update({key: [self.refcatId] for key in {"astromRefCat", "photoRefCat"}})
161 self._checkDoRefcats(doAstrometry=True, doPhotoCal=True, ids=allIds)
162 self._checkDoRefcats(doAstrometry=True, doPhotoCal=False, ids=allIds)
164 def _checkDoRefcats(self, doAstrometry, doPhotoCal, ids):
165 """Test whether run is called with the correct arguments.
167 In the case of `CalibrateTask`, the inputs should not depend on the
168 task configuration.
170 Parameters
171 ----------
172 doAstrometry, doPhotoCal : `bool`
173 Values of the config flags of the same name.
174 ids : `dict` [`str`]
175 A mapping from the input dataset type to the data ID of the
176 dataset to process.
177 """
178 config = CalibrateConfig()
179 config.doWriteMatches = False # no real output to write
180 config.doAstrometry = doAstrometry
181 config.doPhotoCal = doPhotoCal
182 config.connections.photoRefCat = "cal_ref_cat"
183 config.connections.astromRefCat = "cal_ref_cat"
184 config.idGenerator.packer.name = "observation"
185 task = CalibrateTask(config=config)
186 quantumId = ids["exposure"]
188 quantum = testUtils.makeQuantum(task, self.butler, quantumId, ids)
189 run = testUtils.runTestQuantum(task, self.butler, quantum)
191 run.assert_called_once()
192 self.assertEqual(run.call_args[0], ())
193 # Some arguments unprintable because we don't have a full environment
194 # So just check which ones were passed in
195 self.assertEqual(run.call_args[1].keys(),
196 {"exposure", "idGenerator", "background", "icSourceCat"})
198 def testNoAperCorrMap(self):
199 expPath = os.path.join(getPackageDir("pipe_tasks"), "tests", "data", "v695833-e0-c000-a00.sci.fits")
200 exposure = lsst.afw.image.ExposureF(expPath)
202 charImConfig = CharacterizeImageConfig()
203 charImConfig.measurePsf.psfDeterminer = 'piff'
204 charImConfig.measurePsf.psfDeterminer['piff'].spatialOrder = 0
205 charImConfig.measureApCorr.sourceSelector["science"].doSignalToNoise = False
206 charImTask = CharacterizeImageTask(config=charImConfig)
207 charImResults = charImTask.run(exposure)
208 calibConfig = CalibrateConfig()
209 calibConfig.doAstrometry = False
210 calibConfig.doPhotoCal = False
211 calibConfig.doSkySources = False
212 calibConfig.doComputeSummaryStats = False
214 # Force the aperture correction map to None (DM-39626)
215 exposure.info.setApCorrMap(None)
216 calibTask = CalibrateTask(config=calibConfig)
217 with self.assertLogs(level=logging.WARNING) as cm:
218 _ = calibTask.run(charImResults.exposure)
219 self.assertIn("Image does not have valid aperture correction map", cm.output[0])
222def setup_module(module):
223 lsst.utils.tests.init()
226class MemoryTestCase(lsst.utils.tests.MemoryTestCase):
227 pass
230if __name__ == "__main__": 230 ↛ 231line 230 didn't jump to line 231, because the condition on line 230 was never true
231 lsst.utils.tests.init()
232 unittest.main()