Coverage for tests/test_make_direct_warp.py: 19%

98 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-05-01 16:58 -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/>. 

21 

22import unittest 

23 

24import numpy as np 

25 

26import lsst.utils.tests 

27 

28import lsst.afw.image 

29from lsst.daf.butler import DataCoordinate, DimensionUniverse 

30from lsst.pipe.base import InMemoryDatasetHandle 

31from lsst.pipe.tasks.make_direct_warp import (MakeDirectWarpConfig, MakeDirectWarpTask,) 

32from lsst.pipe.tasks.coaddBase import makeSkyInfo 

33import lsst.skymap as skyMap 

34from lsst.afw.detection import GaussianPsf 

35import lsst.afw.cameraGeom.testUtils 

36 

37 

38def generate_data_id( 

39 *, 

40 tract: int = 9813, 

41 patch: int = 42, 

42 band: str = "r", 

43 detector_id: int = 9, 

44 visit_id: int = 1234, 

45 detector_max: int = 109, 

46 visit_max: int = 10000 

47) -> DataCoordinate: 

48 """Generate a DataCoordinate instance to use as data_id. 

49 

50 Parameters 

51 ---------- 

52 tract : `int`, optional 

53 Tract ID for the data_id 

54 patch : `int`, optional 

55 Patch ID for the data_id 

56 band : `str`, optional 

57 Band for the data_id 

58 detector_id : `int`, optional 

59 Detector ID for the data_id 

60 visit_id : `int`, optional 

61 Visit ID for the data_id 

62 detector_max : `int`, optional 

63 Maximum detector ID for the data_id 

64 visit_max : `int`, optional 

65 Maximum visit ID for the data_id 

66 

67 Returns 

68 ------- 

69 data_id : `lsst.daf.butler.DataCoordinate` 

70 An expanded data_id instance. 

71 """ 

72 universe = DimensionUniverse() 

73 

74 instrument = universe["instrument"] 

75 instrument_record = instrument.RecordClass( 

76 name="DummyCam", 

77 class_name="lsst.obs.base.instrument_tests.DummyCam", 

78 detector_max=detector_max, 

79 visit_max=visit_max, 

80 ) 

81 

82 skymap = universe["skymap"] 

83 skymap_record = skymap.RecordClass(name="test_skymap") 

84 

85 band_element = universe["band"] 

86 band_record = band_element.RecordClass(name=band) 

87 

88 visit = universe["visit"] 

89 visit_record = visit.RecordClass(id=visit_id, instrument="test") 

90 

91 detector = universe["detector"] 

92 detector_record = detector.RecordClass(id=detector_id, instrument="test") 

93 

94 physical_filter = universe["physical_filter"] 

95 physical_filter_record = physical_filter.RecordClass(name=band, instrument="test", band=band) 

96 

97 patch_element = universe["patch"] 

98 patch_record = patch_element.RecordClass( 

99 skymap="test_skymap", tract=tract, patch=patch, 

100 ) 

101 

102 if "day_obs" in universe: 

103 day_obs_element = universe["day_obs"] 

104 day_obs_record = day_obs_element.RecordClass(id=20240201, instrument="test") 

105 else: 

106 day_obs_record = None 

107 

108 # A dictionary with all the relevant records. 

109 record = { 

110 "instrument": instrument_record, 

111 "visit": visit_record, 

112 "detector": detector_record, 

113 "patch": patch_record, 

114 "tract": 9813, 

115 "band": band_record.name, 

116 "skymap": skymap_record.name, 

117 "physical_filter": physical_filter_record, 

118 } 

119 

120 if day_obs_record: 

121 record["day_obs"] = day_obs_record 

122 

123 # A dictionary with all the relevant recordIds. 

124 record_id = record.copy() 

125 for key in ("visit", "detector"): 

126 record_id[key] = record_id[key].id 

127 

128 # TODO: Catching mypy failures on Github Actions should be made easier, 

129 # perhaps in DM-36873. Igroring these for now. 

130 data_id = DataCoordinate.standardize(record_id, universe=universe) 

131 return data_id.expanded(record) 

132 

133 

134class MakeWarpTestCase(lsst.utils.tests.TestCase): 

135 def setUp(self): 

136 np.random.seed(12345) 

137 

138 self.config = MakeDirectWarpConfig() 

139 

140 meanCalibration = 1e-4 

141 calibrationErr = 1e-5 

142 self.exposurePhotoCalib = lsst.afw.image.PhotoCalib(meanCalibration, calibrationErr) 

143 # An external photoCalib calibration to return 

144 self.externalPhotoCalib = lsst.afw.image.PhotoCalib(1e-6, 1e-8) 

145 

146 crpix = lsst.geom.Point2D(0, 0) 

147 crval = lsst.geom.SpherePoint(0, 45, lsst.geom.degrees) 

148 cdMatrix = lsst.afw.geom.makeCdMatrix(scale=1.0*lsst.geom.arcseconds) 

149 self.skyWcs = lsst.afw.geom.makeSkyWcs(crpix, crval, cdMatrix) 

150 externalCdMatrix = lsst.afw.geom.makeCdMatrix(scale=0.9*lsst.geom.arcseconds) 

151 # An external skyWcs to return 

152 self.externalSkyWcs = lsst.afw.geom.makeSkyWcs(crpix, crval, externalCdMatrix) 

153 

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

155 self.exposure.maskedImage.image.array = np.random.random((10, 10)).astype(np.float32) * 1000 

156 self.exposure.maskedImage.variance.array = np.random.random((10, 10)).astype(np.float32) 

157 # mask at least one pixel 

158 self.exposure.maskedImage.mask[5, 5] = 3 

159 # set the PhotoCalib and Wcs objects of this exposure. 

160 self.exposure.setPhotoCalib(lsst.afw.image.PhotoCalib(meanCalibration, calibrationErr)) 

161 self.exposure.setWcs(self.skyWcs) 

162 self.exposure.setPsf(GaussianPsf(5, 5, 2.5)) 

163 self.exposure.setFilter(lsst.afw.image.FilterLabel(physical="fakeFilter", band="fake")) 

164 

165 self.visit = 100 

166 self.detector = 5 

167 detectorName = f"detector {self.detector}" 

168 detector = lsst.afw.cameraGeom.testUtils.DetectorWrapper(name=detectorName, id=self.detector).detector 

169 self.exposure.setDetector(detector) 

170 

171 dataId_dict = {"detector_id": self.detector, "visit_id": 1248, "band": "i"} 

172 dataId = generate_data_id(**dataId_dict) 

173 self.dataRef = InMemoryDatasetHandle(self.exposure, dataId=dataId) 

174 simpleMapConfig = skyMap.discreteSkyMap.DiscreteSkyMapConfig() 

175 simpleMapConfig.raList = [crval.getRa().asDegrees()] 

176 simpleMapConfig.decList = [crval.getDec().asDegrees()] 

177 simpleMapConfig.radiusList = [0.1] 

178 

179 self.simpleMap = skyMap.DiscreteSkyMap(simpleMapConfig) 

180 self.tractId = 0 

181 self.patchId = self.simpleMap[0].findPatch(crval).sequential_index 

182 self.skyInfo = makeSkyInfo(self.simpleMap, self.tractId, self.patchId) 

183 

184 def test_makeWarp(self): 

185 """Test basic MakeWarpTask.""" 

186 makeWarp = MakeDirectWarpTask(config=self.config) 

187 inputs = {"calexp_list": [self.dataRef]} 

188 result = makeWarp.run( 

189 inputs, 

190 sky_info=self.skyInfo, 

191 visit_summary=None 

192 ) 

193 

194 warp = result.warp 

195 mfrac = result.masked_fraction_warp 

196 noise = result.noise_warp0 

197 

198 # Ensure we got an exposure out 

199 self.assertIsInstance(warp, lsst.afw.image.ExposureF) 

200 # Ensure that masked fraction is an ImageF object. 

201 self.assertIsInstance(mfrac, lsst.afw.image.ImageF) 

202 # Ensure that the noise image is a MaskedImageF object. 

203 self.assertIsInstance(noise, lsst.afw.image.MaskedImageF) 

204 # Ensure the warp has valid pixels 

205 self.assertGreater(np.isfinite(warp.image.array.ravel()).sum(), 0) 

206 # Ensure the warp has the correct WCS 

207 self.assertEqual(warp.getWcs(), self.skyInfo.wcs) 

208 # Ensure that mfrac has pixels between 0 and 1 

209 self.assertTrue(np.nanmax(mfrac.array) <= 1) 

210 self.assertTrue(np.nanmin(mfrac.array) >= 0) 

211 

212 

213def setup_module(module): 

214 lsst.utils.tests.init() 

215 

216 

217class MatchMemoryTestCase(lsst.utils.tests.MemoryTestCase): 

218 pass 

219 

220 

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

222 lsst.utils.tests.init() 

223 unittest.main()