Coverage for tests/test_astrometryTask.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#
2# LSST Data Management System
3# Copyright 2008-2017 LSST Corporation.
4#
5# This product includes software developed by the
6# LSST Project (http://www.lsst.org/).
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 <http://www.lsstcorp.org/LegalNotices/>.
21#
23import os.path
24import math
25import unittest
27import numpy as np
29import lsst.utils.tests
30import lsst.geom
31import lsst.afw.geom as afwGeom
32import lsst.afw.table as afwTable
33import lsst.afw.image as afwImage
34import lsst.meas.base as measBase
35from lsst.daf.persistence import Butler
36from lsst.meas.algorithms import LoadIndexedReferenceObjectsTask
37from lsst.meas.astrom import AstrometryTask
40class TestAstrometricSolver(lsst.utils.tests.TestCase):
42 def setUp(self):
43 refCatDir = os.path.join(os.path.dirname(__file__), "data", "sdssrefcat")
45 self.bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(3001, 3001))
46 crpix = lsst.geom.Box2D(self.bbox).getCenter()
47 self.tanWcs = afwGeom.makeSkyWcs(crpix=crpix,
48 crval=lsst.geom.SpherePoint(215.5, 53.0, lsst.geom.degrees),
49 cdMatrix=afwGeom.makeCdMatrix(scale=5.1e-5*lsst.geom.degrees))
50 self.exposure = afwImage.ExposureF(self.bbox)
51 self.exposure.setWcs(self.tanWcs)
52 self.exposure.setFilter(afwImage.Filter("r", True))
53 butler = Butler(refCatDir)
54 self.refObjLoader = LoadIndexedReferenceObjectsTask(butler=butler)
56 def tearDown(self):
57 del self.tanWcs
58 del self.exposure
59 del self.refObjLoader
61 def testTrivial(self):
62 """Test fit with no distortion
63 """
64 self.doTest(afwGeom.makeIdentityTransform())
66 def testRadial(self):
67 """Test fit with radial distortion
69 The offset comes from the fact that the CCD is not centered
70 """
71 self.doTest(afwGeom.makeRadialTransform([0, 1.01, 1e-7]))
73 def testUsedFlag(self):
74 """Test that the solver will record number of sources used to table
75 if it is passed a schema on initialization.
76 """
77 self.exposure.setWcs(self.tanWcs)
78 loadRes = self.refObjLoader.loadPixelBox(bbox=self.bbox, wcs=self.tanWcs, filterName="r")
79 refCat = loadRes.refCat
80 refCentroidKey = afwTable.Point2DKey(refCat.schema["centroid"])
81 refFluxRKey = refCat.schema["r_flux"].asKey()
83 sourceSchema = afwTable.SourceTable.makeMinimalSchema()
84 measBase.SingleFrameMeasurementTask(schema=sourceSchema) # expand the schema
85 config = AstrometryTask.ConfigClass()
86 config.wcsFitter.order = 2
87 config.wcsFitter.numRejIter = 0
88 # schema must be passed to the solver task constructor
89 solver = AstrometryTask(config=config, refObjLoader=self.refObjLoader, schema=sourceSchema)
90 sourceCat = afwTable.SourceCatalog(sourceSchema)
91 sourceCat.reserve(len(refCat))
92 sourceCentroidKey = afwTable.Point2DKey(sourceSchema["slot_Centroid"])
93 sourceInstFluxKey = sourceSchema["slot_ApFlux_instFlux"].asKey()
94 sourceInstFluxErrKey = sourceSchema["slot_ApFlux_instFluxErr"].asKey()
96 for refObj in refCat:
97 src = sourceCat.addNew()
98 src.set(sourceCentroidKey, refObj.get(refCentroidKey))
99 src.set(sourceInstFluxKey, refObj.get(refFluxRKey))
100 src.set(sourceInstFluxErrKey, refObj.get(refFluxRKey)/100)
102 results = solver.run(
103 sourceCat=sourceCat,
104 exposure=self.exposure,
105 )
106 # check that the used flag is set the right number of times
107 count = 0
108 for source in sourceCat:
109 if source.get('calib_astrometry_used'):
110 count += 1
111 self.assertEqual(count, len(results.matches))
113 def doTest(self, pixelsToTanPixels, order=3):
114 """Test using pixelsToTanPixels to distort the source positions
115 """
116 distortedWcs = afwGeom.makeModifiedWcs(pixelTransform=pixelsToTanPixels, wcs=self.tanWcs,
117 modifyActualPixels=False)
118 self.exposure.setWcs(distortedWcs)
119 sourceCat = self.makeSourceCat(distortedWcs)
120 config = AstrometryTask.ConfigClass()
121 config.wcsFitter.order = order
122 config.wcsFitter.numRejIter = 0
123 solver = AstrometryTask(config=config, refObjLoader=self.refObjLoader)
124 results = solver.run(
125 sourceCat=sourceCat,
126 exposure=self.exposure,
127 )
128 fitWcs = self.exposure.getWcs()
129 self.assertRaises(Exception, self.assertWcsAlmostEqualOverBBox, fitWcs, distortedWcs)
130 self.assertWcsAlmostEqualOverBBox(distortedWcs, fitWcs, self.bbox,
131 maxDiffSky=0.01*lsst.geom.arcseconds, maxDiffPix=0.02)
133 srcCoordKey = afwTable.CoordKey(sourceCat.schema["coord"])
134 refCoordKey = afwTable.CoordKey(results.refCat.schema["coord"])
135 refCentroidKey = afwTable.Point2DKey(results.refCat.schema["centroid"])
136 maxAngSep = 0*lsst.geom.radians
137 maxPixSep = 0
138 for refObj, src, d in results.matches:
139 refCoord = refObj.get(refCoordKey)
140 refPixPos = refObj.get(refCentroidKey)
141 srcCoord = src.get(srcCoordKey)
142 srcPixPos = src.getCentroid()
144 angSep = refCoord.separation(srcCoord)
145 maxAngSep = max(maxAngSep, angSep)
147 pixSep = math.hypot(*(srcPixPos-refPixPos))
148 maxPixSep = max(maxPixSep, pixSep)
149 print("max angular separation = %0.4f arcsec" % (maxAngSep.asArcseconds(),))
150 print("max pixel separation = %0.3f" % (maxPixSep,))
151 self.assertLess(maxAngSep.asArcseconds(), 0.0038)
152 self.assertLess(maxPixSep, 0.021)
154 # try again, invoking the reference selector
155 config.referenceSelector.doUnresolved = True
156 config.referenceSelector.unresolved.name = 'resolved'
157 solverRefSelect = AstrometryTask(config=config, refObjLoader=self.refObjLoader)
158 self.exposure.setWcs(distortedWcs)
159 resultsRefSelect = solverRefSelect.run(
160 sourceCat=sourceCat,
161 exposure=self.exposure,
162 )
163 self.assertLess(len(resultsRefSelect.matches), len(results.matches))
165 # try again, but without fitting the WCS, no reference selector
166 config.referenceSelector.doUnresolved = False
167 config.forceKnownWcs = True
168 solverNoFit = AstrometryTask(config=config, refObjLoader=self.refObjLoader)
169 self.exposure.setWcs(distortedWcs)
170 resultsNoFit = solverNoFit.run(
171 sourceCat=sourceCat,
172 exposure=self.exposure,
173 )
174 self.assertIsNone(resultsNoFit.scatterOnSky)
176 # fitting should result in matches that are at least as good
177 # (strictly speaking fitting might result in a larger match list with
178 # some outliers, but in practice this test passes)
179 meanFitDist = np.mean([match.distance for match in results.matches])
180 meanNoFitDist = np.mean([match.distance for match in resultsNoFit.matches])
181 self.assertLessEqual(meanFitDist, meanNoFitDist)
183 # try once again, without fitting the WCS, with the reference selector
184 # (this goes through a different code path)
185 config.referenceSelector.doUnresolved = True
186 solverNoFitRefSelect = AstrometryTask(config=config, refObjLoader=self.refObjLoader)
187 resultsNoFitRefSelect = solverNoFitRefSelect.run(
188 sourceCat=sourceCat,
189 exposure=self.exposure,
190 )
191 self.assertLess(len(resultsNoFitRefSelect.matches), len(resultsNoFit.matches))
193 def makeSourceCat(self, distortedWcs):
194 """Make a source catalog by reading the position reference stars and distorting the positions
195 """
196 loadRes = self.refObjLoader.loadPixelBox(bbox=self.bbox, wcs=distortedWcs, filterName="r")
197 refCat = loadRes.refCat
198 refCentroidKey = afwTable.Point2DKey(refCat.schema["centroid"])
199 refFluxRKey = refCat.schema["r_flux"].asKey()
201 sourceSchema = afwTable.SourceTable.makeMinimalSchema()
202 measBase.SingleFrameMeasurementTask(schema=sourceSchema) # expand the schema
203 sourceCat = afwTable.SourceCatalog(sourceSchema)
204 sourceCentroidKey = afwTable.Point2DKey(sourceSchema["slot_Centroid"])
205 sourceInstFluxKey = sourceSchema["slot_ApFlux_instFlux"].asKey()
206 sourceInstFluxErrKey = sourceSchema["slot_ApFlux_instFluxErr"].asKey()
208 sourceCat.reserve(len(refCat))
209 for refObj in refCat:
210 src = sourceCat.addNew()
211 src.set(sourceCentroidKey, refObj.get(refCentroidKey))
212 src.set(sourceInstFluxKey, refObj.get(refFluxRKey))
213 src.set(sourceInstFluxErrKey, refObj.get(refFluxRKey)/100)
214 return sourceCat
217class MemoryTester(lsst.utils.tests.MemoryTestCase):
218 pass
221def setup_module(module):
222 lsst.utils.tests.init()
225if __name__ == "__main__": 225 ↛ 226line 225 didn't jump to line 226, because the condition on line 225 was never true
226 lsst.utils.tests.init()
227 unittest.main()