Coverage for tests/test_calibrate.py: 26%

113 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-23 10:59 +0000

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/>. 

21 

22"""Test ProcessCcdTask and its immediate subtasks. 

23""" 

24import logging 

25import os 

26import shutil 

27import tempfile 

28import unittest 

29 

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 

40 

41 

42class CalibrateTaskTestCaseWithButler(lsst.utils.tests.TestCase): 

43 

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:" 

53 

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 butler.registry.insertDimensionData( 

63 "visit", 

64 {"instrument": "notACam", "id": 101, "name": "101", "physical_filter": "r"}, 

65 ) 

66 butler.registry.insertDimensionData("detector", 

67 {"instrument": "notACam", "id": 42, "full_name": "42"}) 

68 return butler 

69 

70 @classmethod 

71 def setUpClass(cls): 

72 super().setUpClass() 

73 

74 cls.root = tempfile.mkdtemp() 

75 cls.repo = cls._makeTestRepo(cls.root) 

76 

77 butlerTests.addDatasetType( 

78 cls.repo, "icExp", {"instrument", "visit", "detector"}, 

79 "ExposureF") 

80 butlerTests.addDatasetType( 

81 cls.repo, "icExpBackground", {"instrument", "visit", "detector"}, 

82 "Background") 

83 butlerTests.addDatasetType( 

84 cls.repo, "icSrc", {"instrument", "visit", "detector"}, 

85 "SourceCatalog") 

86 butlerTests.addDatasetType( 

87 cls.repo, "cal_ref_cat", {"htm7"}, 

88 "SimpleCatalog") 

89 butlerTests.addDatasetType( 

90 cls.repo, "calexp", {"instrument", "visit", "detector"}, 

91 "ExposureF") 

92 butlerTests.addDatasetType( 

93 cls.repo, "src", {"instrument", "visit", "detector"}, 

94 "SourceCatalog") 

95 butlerTests.addDatasetType( 

96 cls.repo, "calexpBackground", {"instrument", "visit", "detector"}, 

97 "Background") 

98 butlerTests.addDatasetType( 

99 cls.repo, "srcMatch", {"instrument", "visit", "detector"}, 

100 "Catalog") 

101 butlerTests.addDatasetType( 

102 cls.repo, "srcMatchFull", {"instrument", "visit", "detector"}, 

103 "Catalog") 

104 

105 @classmethod 

106 def tearDownClass(cls): 

107 shutil.rmtree(cls.root, ignore_errors=True) 

108 super().tearDownClass() 

109 

110 def setUp(self): 

111 super().setUp() 

112 self.butler = butlerTests.makeTestCollection(self.repo) 

113 

114 self.dataId = {"instrument": "notACam", "visit": 101, "detector": 42} 

115 # CalibrateTask absolutely requires an ExpandedDataCoordinate 

116 self.dataId = self.butler.registry.expandDataId(self.dataId) 

117 self.refcatId = {"htm7": 189584} 

118 

119 # Tests do no processing, so we don't need real data 

120 self.exposure = lsst.afw.image.ExposureF(10, 10) 

121 background = lsst.afw.math.BackgroundMI(self.exposure.getBBox(), self.exposure.getMaskedImage()) 

122 self.backgroundlist = lsst.afw.math.BackgroundList( 

123 (background, lsst.afw.math.Interpolate.UNKNOWN, lsst.afw.math.UndersampleStyle.THROW_EXCEPTION, 

124 lsst.afw.math.ApproximateControl.UNKNOWN, 0, 0, 1)) 

125 self.icSrc = lsst.afw.table.SourceCatalog() 

126 self.refcat = lsst.afw.table.SimpleCatalog() 

127 

128 self.butler.put(self.exposure, "icExp", self.dataId) 

129 self.butler.put(self.backgroundlist, "icExpBackground", self.dataId) 

130 self.butler.put(self.icSrc, "icSrc", self.dataId) 

131 self.butler.put(self.refcat, "cal_ref_cat", self.refcatId) 

132 

133 def testDoAstrometry(self): 

134 """Ensure correct inputs passed to run whether or not doAstrometry 

135 is set. 

136 """ 

137 allIds = {key: self.dataId for key in { 

138 "exposure", "background", "icSourceCat", "outputExposure", "outputCat", "outputBackground", 

139 "matches", "matchesDenormalized" 

140 }} 

141 allIds.update({key: [self.refcatId] for key in {"astromRefCat", "photoRefCat"}}) 

142 

143 self._checkDoRefcats(doAstrometry=True, doPhotoCal=True, ids=allIds) 

144 self._checkDoRefcats(doAstrometry=False, doPhotoCal=True, ids=allIds) 

145 

146 def testDoPhotoCal(self): 

147 """Ensure correct inputs passed to run whether or not doPhotoCal 

148 is set. 

149 """ 

150 allIds = {key: self.dataId for key in { 

151 "exposure", "background", "icSourceCat", "outputExposure", "outputCat", "outputBackground", 

152 "matches", "matchesDenormalized" 

153 }} 

154 allIds.update({key: [self.refcatId] for key in {"astromRefCat", "photoRefCat"}}) 

155 

156 self._checkDoRefcats(doAstrometry=True, doPhotoCal=True, ids=allIds) 

157 self._checkDoRefcats(doAstrometry=True, doPhotoCal=False, ids=allIds) 

158 

159 def _checkDoRefcats(self, doAstrometry, doPhotoCal, ids): 

160 """Test whether run is called with the correct arguments. 

161 

162 In the case of `CalibrateTask`, the inputs should not depend on the 

163 task configuration. 

164 

165 Parameters 

166 ---------- 

167 doAstrometry, doPhotoCal : `bool` 

168 Values of the config flags of the same name. 

169 ids : `dict` [`str`] 

170 A mapping from the input dataset type to the data ID of the 

171 dataset to process. 

172 """ 

173 config = CalibrateConfig() 

174 config.doWriteMatches = False # no real output to write 

175 config.doAstrometry = doAstrometry 

176 config.doPhotoCal = doPhotoCal 

177 config.connections.photoRefCat = "cal_ref_cat" 

178 config.connections.astromRefCat = "cal_ref_cat" 

179 config.idGenerator.packer.name = "observation" 

180 task = CalibrateTask(config=config) 

181 quantumId = ids["exposure"] 

182 

183 quantum = testUtils.makeQuantum(task, self.butler, quantumId, ids) 

184 run = testUtils.runTestQuantum(task, self.butler, quantum) 

185 

186 run.assert_called_once() 

187 self.assertEqual(run.call_args[0], ()) 

188 # Some arguments unprintable because we don't have a full environment 

189 # So just check which ones were passed in 

190 self.assertEqual(run.call_args[1].keys(), 

191 {"exposure", "idGenerator", "background", "icSourceCat"}) 

192 

193 def testNoAperCorrMap(self): 

194 expPath = os.path.join(getPackageDir("pipe_tasks"), "tests", "data", "v695833-e0-c000-a00.sci.fits") 

195 exposure = lsst.afw.image.ExposureF(expPath) 

196 

197 charImConfig = CharacterizeImageConfig() 

198 charImConfig.measurePsf.psfDeterminer = 'piff' 

199 charImConfig.measurePsf.psfDeterminer['piff'].spatialOrder = 0 

200 charImConfig.measureApCorr.sourceSelector["science"].doSignalToNoise = False 

201 charImTask = CharacterizeImageTask(config=charImConfig) 

202 charImResults = charImTask.run(exposure) 

203 calibConfig = CalibrateConfig() 

204 calibConfig.doAstrometry = False 

205 calibConfig.doPhotoCal = False 

206 calibConfig.doSkySources = False 

207 calibConfig.doComputeSummaryStats = False 

208 

209 # Force the aperture correction map to None (DM-39626) 

210 exposure.info.setApCorrMap(None) 

211 calibTask = CalibrateTask(config=calibConfig) 

212 with self.assertLogs(level=logging.WARNING) as cm: 

213 _ = calibTask.run(charImResults.exposure) 

214 self.assertIn("Image does not have valid aperture correction map", cm.output[0]) 

215 

216 

217def setup_module(module): 

218 lsst.utils.tests.init() 

219 

220 

221class MemoryTestCase(lsst.utils.tests.MemoryTestCase): 

222 pass 

223 

224 

225if __name__ == "__main__": 225 ↛ 226line 225 didn't jump to line 226, because the condition on line 225 was never true

226 lsst.utils.tests.init() 

227 unittest.main()