Coverage for tests/test_makeSurveyPropertyMaps.py: 19%
138 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-24 03:54 -0700
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-24 03:54 -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
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
39from lsst.pipe.tasks.healSparseMapping import HealSparsePropertyMapTask
40from lsst.pipe.tasks.healSparseMappingProperties import (register_property_map,
41 BasePropertyMap)
43from surveyPropertyMapsTestUtils import (makeMockVisitSummary,
44 MockVisitSummaryReference,
45 MockCoaddReference,
46 MockInputMapReference)
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
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']
64class HealSparsePropertyMapTaskTestCase(lsst.utils.tests.TestCase):
65 """Test of HealSparsePropertyMapTask.
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
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)
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)}
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)
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])
126 input_map_ref = MockInputMapReference(input_map, patch=patch, tract=tract)
127 self.input_map_dict = {patch: input_map_ref}
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)
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())
155 inputs = afwImage.CoaddInputs()
156 inputs.ccds = ccds
157 coadd.getInfo().setCoaddInputs(inputs)
159 coadd_ref = MockCoaddReference(coadd, patch=patch, tract=tract)
160 self.coadd_dict = {patch: coadd_ref}
162 self.tract = tract
163 self.band = band
164 self.sky_map = sky_map
165 self.input_map = input_map
167 def testPropertyMapCreation(self):
168 """Test creation of property maps."""
169 config = HealSparsePropertyMapTask.ConfigClass()
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
177 property_task = HealSparsePropertyMapTask(config=config)
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)
186 valid_pixels = self.input_map.valid_pixels
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'))
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 )
227 config = HealSparsePropertyMapTask.ConfigClass()
229 property_task = HealSparsePropertyMapTask(config=config)
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])
240 # Ensure we have no valid pixels output.
241 self.assertEqual(property_task.property_maps["exposure_time"].sum_map.valid_pixels.size, 0)
244class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase):
245 pass
248def setup_module(module):
249 lsst.utils.tests.init()
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()