Coverage for tests/test_makeSurveyPropertyMaps.py: 19%

139 statements  

« prev     ^ index     » next       coverage.py v7.2.5, created at 2023-05-06 03:31 -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.base import InMemoryDatasetHandle 

40from lsst.pipe.tasks.healSparseMapping import HealSparsePropertyMapTask 

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

42 BasePropertyMap) 

43 

44from surveyPropertyMapsTestUtils import makeMockVisitSummary 

45 

46 

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

48# and using it in the test class. 

49@register_property_map("dist_times_psfarea") 

50class DistTimesPsfAreaPropertyMap(BasePropertyMap): 

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

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

53 requires_psf = True 

54 

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

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

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

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

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

60 

61 

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

63 """Test of HealSparsePropertyMapTask. 

64 

65 These tests bypass the middleware used for accessing data and 

66 managing Task execution. 

67 """ 

68 def setUp(self): 

69 tract = 0 

70 band = 'r' 

71 patch = 0 

72 visits = [100, 101] 

73 # Good to test crossing 0. 

74 ra_center = 0.0 

75 dec_center = -45.0 

76 pixel_scale = 0.2 

77 coadd_zp = 27.0 

78 

79 # Generate a mock skymap with one patch 

80 config = DiscreteSkyMap.ConfigClass() 

81 config.raList = [ra_center] 

82 config.decList = [dec_center] 

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

84 config.patchInnerDimensions = (350, 350) 

85 config.patchBorder = 50 

86 config.tractOverlap = 0.0 

87 config.pixelScale = pixel_scale 

88 sky_map = DiscreteSkyMap(config) 

89 

90 visit_summaries = [makeMockVisitSummary(visit, 

91 ra_center=ra_center, 

92 dec_center=dec_center) 

93 for visit in visits] 

94 visit_summary_refs = [InMemoryDatasetHandle(visit_summary, visit=visit) 

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

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

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

98 

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

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

101 with warnings.catch_warnings(): 

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

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

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

105 # warning. 

106 warnings.simplefilter("ignore") 

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

108 nside_sparse=32768, 

109 dtype=hsp.WIDE_MASK, 

110 wide_mask_maxbits=len(visits)*2) 

111 

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

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

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

115 for sph in sph_pts]) 

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

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

118 value=[0]) 

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

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

121 input_map.set_bits_pix(poly_pixels, [0]) 

122 input_map.set_bits_pix(poly_pixels, [1]) 

123 

124 input_map_ref = InMemoryDatasetHandle(input_map, patch=patch, tract=tract) 

125 self.input_map_dict = {patch: input_map_ref} 

126 

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

128 sky_map[tract].getWcs()) 

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

130 pc = afwImage.makePhotoCalibFromCalibZeroPoint(instFluxMag0) 

131 coadd.setPhotoCalib(pc) 

132 

133 # Mock the coadd input ccd table 

134 schema = afwTable.ExposureTable.makeMinimalSchema() 

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

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

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

138 ccds = afwTable.ExposureCatalog(schema) 

139 ccds.resize(2) 

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

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

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

143 ccds['ccd'][0] = 0 

144 ccds['ccd'][1] = 1 

145 ccds['weight'] = 10.0 

146 for ccd_row in ccds: 

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

148 ccd_row.setWcs(summary.getWcs()) 

149 ccd_row.setPsf(summary.getPsf()) 

150 ccd_row.setBBox(summary.getBBox()) 

151 ccd_row.setPhotoCalib(summary.getPhotoCalib()) 

152 

153 inputs = afwImage.CoaddInputs() 

154 inputs.ccds = ccds 

155 coadd.getInfo().setCoaddInputs(inputs) 

156 

157 coadd_ref = InMemoryDatasetHandle(coadd, patch=patch, tract=tract, storageClass="ExposureF") 

158 self.coadd_dict = {patch: coadd_ref} 

159 

160 self.tract = tract 

161 self.band = band 

162 self.sky_map = sky_map 

163 self.input_map = input_map 

164 

165 def testPropertyMapCreation(self): 

166 """Test creation of property maps.""" 

167 config = HealSparsePropertyMapTask.ConfigClass() 

168 

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

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

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

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

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

174 

175 property_task = HealSparsePropertyMapTask(config=config) 

176 

177 property_task.run(self.sky_map, 

178 self.tract, 

179 self.band, 

180 self.coadd_dict, 

181 self.input_map_dict, 

182 self.visit_summary_dict) 

183 

184 valid_pixels = self.input_map.valid_pixels 

185 

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

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

188 self.assertTrue(name in property_task.property_maps) 

189 property_map = property_task.property_maps[name] 

190 if map_config.do_min: 

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

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

193 else: 

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

195 if map_config.do_max: 

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

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

198 else: 

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

200 if map_config.do_mean: 

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

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

203 else: 

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

205 if map_config.do_weighted_mean: 

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

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

208 else: 

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

210 if map_config.do_sum: 

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

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

213 else: 

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

215 

216 def testPropertyMapCreationEmptyInputMap(self): 

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

218 # Replace the input map with an empty one. 

219 with warnings.catch_warnings(): 

220 warnings.simplefilter("ignore") 

221 self.input_map_dict[0] = InMemoryDatasetHandle(hsp.HealSparseMap.make_empty_like( 

222 self.input_map_dict[0].inMemoryDataset 

223 ), **self.input_map_dict[0].dataId) 

224 

225 config = HealSparsePropertyMapTask.ConfigClass() 

226 

227 property_task = HealSparsePropertyMapTask(config=config) 

228 

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

230 property_task.run(self.sky_map, 

231 self.tract, 

232 self.band, 

233 self.coadd_dict, 

234 self.input_map_dict, 

235 self.visit_summary_dict) 

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

237 

238 # Ensure we have no valid pixels output. 

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

240 

241 

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

243 pass 

244 

245 

246def setup_module(module): 

247 lsst.utils.tests.init() 

248 

249 

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

251 lsst.utils.tests.init() 

252 unittest.main()