Coverage for tests/test_diaForcedSource.py : 17%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of ap_association.
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/>.
22import numpy as np
23import unittest
24import unittest.mock
26from lsst.afw.cameraGeom.testUtils import DetectorWrapper
27import lsst.afw.geom as afwGeom
28import lsst.afw.image as afwImage
29import lsst.afw.image.utils as afwImageUtils
30import lsst.afw.table as afwTable
31import lsst.daf.base as dafBase
32import lsst.meas.algorithms as measAlg
33from lsst.ap.association import \
34 DiaForcedSourceTask, \
35 make_dia_object_schema
36import lsst.utils.tests
39def create_test_dia_objects(n_points, wcs, startPos=100):
40 """Create dummy DIASources or DIAObjects for use in our tests.
42 Parameters
43 ----------
44 n_points : `int`
45 Number of DiaObject test points to create.
46 wcs : `lsst.afw.geom.SkyWcs`
47 Wcs to convert RA/Dec to pixel x/y.
48 startPos : `int`
49 Start position to iterate from when creating test DiaObjects
51 Returns
52 -------
53 test_points : `lsst.afw.table.SourceCatalog`
54 Catalog of points to test.
55 """
56 objects = afwTable.SourceCatalog(make_dia_object_schema())
58 for src_idx in range(n_points):
59 src = objects.addNew()
60 src['id'] = src_idx
61 src.setCoord(wcs.pixelToSky(startPos + src_idx,
62 startPos + src_idx))
64 return objects
67class TestDiaForcedSource(unittest.TestCase):
69 def setUp(self):
70 # CFHT Filters from the camera mapper.
71 self.filter_names = ["u", "g", "r", "i", "z"]
72 afwImageUtils.resetFilters()
73 afwImageUtils.defineFilter('u', lambdaEff=374, alias="u.MP9301")
74 afwImageUtils.defineFilter('g', lambdaEff=487, alias="g.MP9401")
75 afwImageUtils.defineFilter('r', lambdaEff=628, alias="r.MP9601")
76 afwImageUtils.defineFilter('i', lambdaEff=778, alias="i.MP9701")
77 afwImageUtils.defineFilter('z', lambdaEff=1170, alias="z.MP9801")
79 # metadata taken from CFHT data
80 # v695856-e0/v695856-e0-c000-a00.sci_img.fits
81 self.metadata = dafBase.PropertySet()
83 self.metadata.set("SIMPLE", "T")
84 self.metadata.set("BITPIX", -32)
85 self.metadata.set("NAXIS", 2)
86 self.metadata.set("NAXIS1", 1024)
87 self.metadata.set("NAXIS2", 1153)
88 self.metadata.set("RADECSYS", 'FK5')
89 self.metadata.set("EQUINOX", 2000.)
91 self.metadata.setDouble("CRVAL1", 215.604025685476)
92 self.metadata.setDouble("CRVAL2", 53.1595451514076)
93 self.metadata.setDouble("CRPIX1", 1109.99981456774)
94 self.metadata.setDouble("CRPIX2", 560.018167811613)
95 self.metadata.set("CTYPE1", 'RA---SIN')
96 self.metadata.set("CTYPE2", 'DEC--SIN')
98 self.metadata.setDouble("CD1_1", 5.10808596133527E-05)
99 self.metadata.setDouble("CD1_2", 1.85579539217196E-07)
100 self.metadata.setDouble("CD2_2", -5.10281493481982E-05)
101 self.metadata.setDouble("CD2_1", -8.27440751733828E-07)
103 self.wcs = afwGeom.makeSkyWcs(self.metadata)
105 self.calibration = 10000
106 self.calibrationErr = 100
107 self.exposureId = 1234
108 self.exposureTime = 200.
109 self.imageSize = [1024, 1153]
110 self.dateTime = "2014-05-13T17:00:00.000000000"
112 # Make images with one source in them and distinct values and
113 # variance for each image.
114 # Direct Image
115 source_image = afwImage.MaskedImageF(
116 lsst.geom.ExtentI(self.imageSize[0] + 1, self.imageSize[1] + 1))
117 source_image.image[100, 100, afwImage.LOCAL] = 10
118 source_image.getVariance().set(1)
119 bbox = lsst.geom.BoxI(
120 lsst.geom.PointI(1, 1),
121 lsst.geom.ExtentI(self.imageSize[0],
122 self.imageSize[1]))
123 masked_image = afwImage.MaskedImageF(source_image, bbox, afwImage.LOCAL)
124 self.exposure = afwImage.makeExposure(masked_image, self.wcs)
126 detector = DetectorWrapper(
127 id=23, bbox=self.exposure.getBBox()).detector
128 visit = afwImage.VisitInfo(
129 exposureId=self.exposureId,
130 exposureTime=self.exposureTime,
131 date=dafBase.DateTime(self.dateTime,
132 dafBase.DateTime.Timescale.TAI))
133 self.exposure.setDetector(detector)
134 self.exposure.getInfo().setVisitInfo(visit)
135 self.exposure.setFilter(afwImage.Filter('g'))
136 self.exposure.setPhotoCalib(afwImage.PhotoCalib(self.calibration, self.calibrationErr))
138 # Difference Image
139 source_image = afwImage.MaskedImageF(
140 lsst.geom.ExtentI(self.imageSize[0] + 1, self.imageSize[1] + 1))
141 source_image.image[100, 100, afwImage.LOCAL] = 20
142 source_image.getVariance().set(2)
143 bbox = lsst.geom.BoxI(
144 lsst.geom.PointI(1, 1),
145 lsst.geom.ExtentI(self.imageSize[0],
146 self.imageSize[1]))
147 masked_image = afwImage.MaskedImageF(source_image, bbox, afwImage.LOCAL)
148 self.diffim = afwImage.makeExposure(masked_image, self.wcs)
149 self.diffim.setDetector(detector)
150 self.diffim.getInfo().setVisitInfo(visit)
151 self.diffim.setFilter(afwImage.Filter('g'))
152 self.diffim.setPhotoCalib(afwImage.PhotoCalib(self.calibration, self.calibrationErr))
154 self.expIdBits = 16
156 FWHM = 5
157 psf = measAlg.DoubleGaussianPsf(15, 15, FWHM/(2*np.sqrt(2*np.log(2))))
158 self.exposure.setPsf(psf)
159 self.diffim.setPsf(psf)
161 self.testDiaObjects = create_test_dia_objects(5, self.wcs)
162 # Add additional diaObjects that are outside of the above difference
163 # and calexp visit images.
164 # xy outside
165 src = self.testDiaObjects.addNew()
166 src['id'] = 10000000
167 src.setCoord(self.wcs.pixelToSky(-100000,
168 -100000))
169 # y outside
170 src = self.testDiaObjects.addNew()
171 src['id'] = 10000001
172 src.setCoord(self.wcs.pixelToSky(100,
173 -100000))
174 # x outside
175 src = self.testDiaObjects.addNew()
176 src['id'] = 10000002
177 src.setCoord(self.wcs.pixelToSky(-100000,
178 100))
179 # Ids of objects that were "updated" during "ap_association"
180 # processing.
181 self.updatedTestIds = np.array([1, 2, 3, 4, 10000001], dtype=np.uint64)
182 # Expecdted number of sources is the number of updated ids plus
183 # any that are within the CCD footprint but are not in the
184 # above list of ids.
185 self.expectedDiaForcedSources = 6
187 self.expected_n_columns = 11
189 def testRun(self):
190 """Test that forced source catalogs are successfully created and have
191 sensible values.
192 """
193 test_objects = self._convert_to_pandas(self.testDiaObjects)
194 test_objects.rename(columns={"id": "diaObjectId"},
195 inplace=True)
196 test_objects.set_index("diaObjectId", inplace=True, drop=False)
197 dfs = DiaForcedSourceTask()
198 dia_forced_sources = dfs.run(
199 test_objects, self.updatedTestIds, self.expIdBits, self.exposure, self.diffim)
201 direct_values = [199854.48417094944, 160097.40719241602,
202 82299.17897267535, 27148.604434624354,
203 5746.988388215507]
204 direct_var = [75240.939811168, 75231.42933749466,
205 75218.89495113207, 75214.88248249644,
206 75214.41447602339]
207 diff_values = [399708.9683418989, 320194.81438483205,
208 164598.3579453507, 54297.20886924871,
209 11493.976776431015]
210 diff_var = [106444.28782374493, 106417.39592887461,
211 106381.94840437356, 106370.59980584883,
212 106369.27608815048]
214 # Should be number of test objects minus one as one object is purposely
215 # outside of the ccd area.
216 self.assertEqual(len(dia_forced_sources), self.expectedDiaForcedSources)
217 self.assertEqual(len(dia_forced_sources.columns),
218 self.expected_n_columns)
220 for (diaFS_id, diaFS), testObj, dirVal, diffVal, dirVar, diffVar in zip(dia_forced_sources.iterrows(),
221 self.testDiaObjects,
222 direct_values,
223 diff_values,
224 direct_var,
225 diff_var):
226 self.assertAlmostEqual(diaFS["psFlux"] / diffVal, 1.)
227 self.assertAlmostEqual(diaFS["psFluxErr"] / diffVar, 1.)
229 self.assertAlmostEqual(diaFS["totFlux"] / dirVal, 1.)
230 self.assertAlmostEqual(diaFS["totFluxErr"] / dirVar, 1.)
232 self.assertEqual(diaFS["ccdVisitId"], self.exposureId)
234 def _convert_to_pandas(self, dia_objects):
235 """Convert input afw table to pandas.
237 Parameters
238 ----------
239 dia_objects : `lsst.afw.table.SourceCatalog`
240 Catalog to convert
242 Returns
243 -------
244 output_catalog : `pandas.DataFrame`
245 Converted catalog
246 """
247 output_catalog = dia_objects.asAstropy().to_pandas()
248 output_catalog.rename(columns={"id": "diaObjectId",
249 "coord_ra": "ra",
250 "coord_dec": "decl"},
251 inplace=True)
253 output_catalog.loc[:, "ra"] = np.degrees(output_catalog["ra"])
254 output_catalog.loc[:, "decl"] = np.degrees(output_catalog["decl"])
256 return output_catalog
259class MemoryTester(lsst.utils.tests.MemoryTestCase):
260 pass
263def setup_module(module):
264 lsst.utils.tests.init()
267if __name__ == "__main__": 267 ↛ 268line 267 didn't jump to line 268, because the condition on line 267 was never true
268 lsst.utils.tests.init()
269 unittest.main()