Coverage for tests/test_makeSurveyPropertyMaps.py: 19%
139 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 02:55 -0700
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 02:55 -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.base import InMemoryDatasetHandle
40from lsst.pipe.tasks.healSparseMapping import HealSparsePropertyMapTask
41from lsst.pipe.tasks.healSparseMappingProperties import (register_property_map,
42 BasePropertyMap)
44from surveyPropertyMapsTestUtils import makeMockVisitSummary
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
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']
62class HealSparsePropertyMapTaskTestCase(lsst.utils.tests.TestCase):
63 """Test of HealSparsePropertyMapTask.
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
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)
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)}
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)
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])
124 input_map_ref = InMemoryDatasetHandle(input_map, patch=patch, tract=tract)
125 self.input_map_dict = {patch: input_map_ref}
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)
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())
153 inputs = afwImage.CoaddInputs()
154 inputs.ccds = ccds
155 coadd.getInfo().setCoaddInputs(inputs)
157 coadd_ref = InMemoryDatasetHandle(coadd, patch=patch, tract=tract, storageClass="ExposureF")
158 self.coadd_dict = {patch: coadd_ref}
160 self.tract = tract
161 self.band = band
162 self.sky_map = sky_map
163 self.input_map = input_map
165 def testPropertyMapCreation(self):
166 """Test creation of property maps."""
167 config = HealSparsePropertyMapTask.ConfigClass()
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
175 property_task = HealSparsePropertyMapTask(config=config)
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)
184 valid_pixels = self.input_map.valid_pixels
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'))
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)
225 config = HealSparsePropertyMapTask.ConfigClass()
227 property_task = HealSparsePropertyMapTask(config=config)
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])
238 # Ensure we have no valid pixels output.
239 self.assertEqual(property_task.property_maps["exposure_time"].sum_map.valid_pixels.size, 0)
242class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase):
243 pass
246def setup_module(module):
247 lsst.utils.tests.init()
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()