Coverage for tests/test_makeSurveyPropertyMaps.py: 19%

138 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-03-28 05:24 -0700

1# This file is part of pipe_tasks. 

2# 

3# LSST Data Management System 

4# This product includes software developed by the 

5# LSST Project (http://www.lsst.org/). 

6# See COPYRIGHT file at the top of the source tree. 

7# 

8# This program is free software: you can redistribute it and/or modify 

9# it under the terms of the GNU General Public License as published by 

10# the Free Software Foundation, either version 3 of the License, or 

11# (at your option) any later version. 

12# 

13# This program is distributed in the hope that it will be useful, 

14# but WITHOUT ANY WARRANTY; without even the implied warranty of 

15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the LSST License Statement and 

19# the GNU General Public License along with this program. If not, 

20# see <https://www.lsstcorp.org/LegalNotices/>. 

21# 

22"""Test HealSparsePropertyMapTask. 

23""" 

24import unittest 

25import numpy as np 

26import healsparse as hsp 

27import esutil 

28import warnings 

29import logging 

30 

31import lsst.utils.tests 

32import lsst.daf.butler 

33import lsst.afw.table as afwTable 

34import lsst.afw.geom as afwGeom 

35import lsst.afw.image as afwImage 

36from lsst.skymap.discreteSkyMap import DiscreteSkyMap 

37import lsst.geom as geom 

38 

39from lsst.pipe.tasks.healSparseMapping import HealSparsePropertyMapTask 

40from lsst.pipe.tasks.healSparseMappingProperties import (register_property_map, 

41 BasePropertyMap) 

42 

43from surveyPropertyMapsTestUtils import (makeMockVisitSummary, 

44 MockVisitSummaryReference, 

45 MockCoaddReference, 

46 MockInputMapReference) 

47 

48 

49# Test creation of an arbitrary new property map by registering it here 

50# and using it in the test class. 

51@register_property_map("dist_times_psfarea") 

52class DistTimesPsfAreaPropertyMap(BasePropertyMap): 

53 """Property map to compute the distance from the boresight center 

54 by the psf area. Do not try this at home.""" 

55 requires_psf = True 

56 

57 def _compute(self, row, ra, dec, scalings, psf_array=None): 

58 boresight = row.getVisitInfo().getBoresightRaDec() 

59 dist = esutil.coords.sphdist(ra, dec, 

60 boresight.getRa().asDegrees(), boresight.getDec().asDegrees()) 

61 return np.deg2rad(dist)*psf_array['psf_area'] 

62 

63 

64class HealSparsePropertyMapTaskTestCase(lsst.utils.tests.TestCase): 

65 """Test of HealSparsePropertyMapTask. 

66 

67 These tests bypass the middleware used for accessing data and 

68 managing Task execution. 

69 """ 

70 def setUp(self): 

71 tract = 0 

72 band = 'r' 

73 patch = 0 

74 visits = [100, 101] 

75 # Good to test crossing 0. 

76 ra_center = 0.0 

77 dec_center = -45.0 

78 pixel_scale = 0.2 

79 coadd_zp = 27.0 

80 

81 # Generate a mock skymap with one patch 

82 config = DiscreteSkyMap.ConfigClass() 

83 config.raList = [ra_center] 

84 config.decList = [dec_center] 

85 config.radiusList = [150*pixel_scale/3600.] 

86 config.patchInnerDimensions = (350, 350) 

87 config.patchBorder = 50 

88 config.tractOverlap = 0.0 

89 config.pixelScale = pixel_scale 

90 sky_map = DiscreteSkyMap(config) 

91 

92 visit_summaries = [makeMockVisitSummary(visit, 

93 ra_center=ra_center, 

94 dec_center=dec_center) 

95 for visit in visits] 

96 visit_summary_refs = [MockVisitSummaryReference(visit_summary, visit) 

97 for visit_summary, visit in zip(visit_summaries, visits)] 

98 self.visit_summary_dict = {visit: ref.get() 

99 for ref, visit in zip(visit_summary_refs, visits)} 

100 

101 # Generate an input map. Note that this does not need to be consistent 

102 # with the visit_summary projections, we're just tracking values. 

103 with warnings.catch_warnings(): 

104 # Healsparse will emit a warning if nside coverage is greater than 

105 # 128. In the case of generating patch input maps, and not global 

106 # maps, high nside coverage works fine, so we can suppress this 

107 # warning. 

108 warnings.simplefilter("ignore") 

109 input_map = hsp.HealSparseMap.make_empty(nside_coverage=256, 

110 nside_sparse=32768, 

111 dtype=hsp.WIDE_MASK, 

112 wide_mask_maxbits=len(visits)*2) 

113 

114 patch_poly = afwGeom.Polygon(geom.Box2D(sky_map[tract][patch].getOuterBBox())) 

115 sph_pts = sky_map[tract].getWcs().pixelToSky(patch_poly.convexHull().getVertices()) 

116 patch_poly_radec = np.array([(sph.getRa().asDegrees(), sph.getDec().asDegrees()) 

117 for sph in sph_pts]) 

118 poly = hsp.Polygon(ra=patch_poly_radec[: -1, 0], 

119 dec=patch_poly_radec[: -1, 1], 

120 value=[0]) 

121 poly_pixels = poly.get_pixels(nside=input_map.nside_sparse) 

122 # The input map has full coverage for bits 0 and 1 

123 input_map.set_bits_pix(poly_pixels, [0]) 

124 input_map.set_bits_pix(poly_pixels, [1]) 

125 

126 input_map_ref = MockInputMapReference(input_map, patch=patch, tract=tract) 

127 self.input_map_dict = {patch: input_map_ref} 

128 

129 coadd = afwImage.ExposureF(sky_map[tract][patch].getOuterBBox(), 

130 sky_map[tract].getWcs()) 

131 instFluxMag0 = 10.**(coadd_zp/2.5) 

132 pc = afwImage.makePhotoCalibFromCalibZeroPoint(instFluxMag0) 

133 coadd.setPhotoCalib(pc) 

134 

135 # Mock the coadd input ccd table 

136 schema = afwTable.ExposureTable.makeMinimalSchema() 

137 schema.addField("ccd", type="I") 

138 schema.addField("visit", type="I") 

139 schema.addField("weight", type="F") 

140 ccds = afwTable.ExposureCatalog(schema) 

141 ccds.resize(2) 

142 ccds['id'] = np.arange(2) 

143 ccds['visit'][0] = visits[0] 

144 ccds['visit'][1] = visits[1] 

145 ccds['ccd'][0] = 0 

146 ccds['ccd'][1] = 1 

147 ccds['weight'] = 10.0 

148 for ccd_row in ccds: 

149 summary = self.visit_summary_dict[ccd_row['visit']].find(ccd_row['ccd']) 

150 ccd_row.setWcs(summary.getWcs()) 

151 ccd_row.setPsf(summary.getPsf()) 

152 ccd_row.setBBox(summary.getBBox()) 

153 ccd_row.setPhotoCalib(summary.getPhotoCalib()) 

154 

155 inputs = afwImage.CoaddInputs() 

156 inputs.ccds = ccds 

157 coadd.getInfo().setCoaddInputs(inputs) 

158 

159 coadd_ref = MockCoaddReference(coadd, patch=patch, tract=tract) 

160 self.coadd_dict = {patch: coadd_ref} 

161 

162 self.tract = tract 

163 self.band = band 

164 self.sky_map = sky_map 

165 self.input_map = input_map 

166 

167 def testPropertyMapCreation(self): 

168 """Test creation of property maps.""" 

169 config = HealSparsePropertyMapTask.ConfigClass() 

170 

171 # Add our new test map to the set of maps 

172 config.property_maps.names |= ['dist_times_psfarea'] 

173 config.property_maps['dist_times_psfarea'].do_min = True 

174 config.property_maps['dist_times_psfarea'].do_max = True 

175 config.property_maps['dist_times_psfarea'].do_mean = True 

176 

177 property_task = HealSparsePropertyMapTask(config=config) 

178 

179 property_task.run(self.sky_map, 

180 self.tract, 

181 self.band, 

182 self.coadd_dict, 

183 self.input_map_dict, 

184 self.visit_summary_dict) 

185 

186 valid_pixels = self.input_map.valid_pixels 

187 

188 # Verify each map exists and has the correct pixels set. 

189 for name, map_config, PropertyMapClass in config.property_maps.apply(): 

190 self.assertTrue(name in property_task.property_maps) 

191 property_map = property_task.property_maps[name] 

192 if map_config.do_min: 

193 self.assertTrue(hasattr(property_map, 'min_map')) 

194 np.testing.assert_array_equal(property_map.min_map.valid_pixels, valid_pixels) 

195 else: 

196 self.assertFalse(hasattr(property_map, 'min_map')) 

197 if map_config.do_max: 

198 self.assertTrue(hasattr(property_map, 'max_map')) 

199 np.testing.assert_array_equal(property_map.max_map.valid_pixels, valid_pixels) 

200 else: 

201 self.assertFalse(hasattr(property_map, 'max_map')) 

202 if map_config.do_mean: 

203 self.assertTrue(hasattr(property_map, 'mean_map')) 

204 np.testing.assert_array_equal(property_map.mean_map.valid_pixels, valid_pixels) 

205 else: 

206 self.assertFalse(hasattr(property_map, 'mean_map')) 

207 if map_config.do_weighted_mean: 

208 self.assertTrue(hasattr(property_map, 'weighted_mean_map')) 

209 np.testing.assert_array_equal(property_map.weighted_mean_map.valid_pixels, valid_pixels) 

210 else: 

211 self.assertFalse(hasattr(property_map, 'weighted_mean_map')) 

212 if map_config.do_sum: 

213 self.assertTrue(hasattr(property_map, 'sum_map')) 

214 np.testing.assert_array_equal(property_map.sum_map.valid_pixels, valid_pixels) 

215 else: 

216 self.assertFalse(hasattr(property_map, 'sum_map')) 

217 

218 def testPropertyMapCreationEmptyInputMap(self): 

219 """Test creation of maps with an empty input map (DM-37837).""" 

220 # Replace the input map with an empty one. 

221 with warnings.catch_warnings(): 

222 warnings.simplefilter("ignore") 

223 self.input_map_dict[0].input_map = hsp.HealSparseMap.make_empty_like( 

224 self.input_map_dict[0].input_map 

225 ) 

226 

227 config = HealSparsePropertyMapTask.ConfigClass() 

228 

229 property_task = HealSparsePropertyMapTask(config=config) 

230 

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

232 property_task.run(self.sky_map, 

233 self.tract, 

234 self.band, 

235 self.coadd_dict, 

236 self.input_map_dict, 

237 self.visit_summary_dict) 

238 self.assertIn("No valid pixels", cm.output[0]) 

239 

240 # Ensure we have no valid pixels output. 

241 self.assertEqual(property_task.property_maps["exposure_time"].sum_map.valid_pixels.size, 0) 

242 

243 

244class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase): 

245 pass 

246 

247 

248def setup_module(module): 

249 lsst.utils.tests.init() 

250 

251 

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

253 lsst.utils.tests.init() 

254 unittest.main()