Coverage for tests/test_photoCal.py : 21%

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, 2009, 2010 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#
22import os
23import unittest
25import numpy as np
26import astropy.units as u
28from lsst.daf.persistence import Butler
29from lsst.meas.algorithms import LoadIndexedReferenceObjectsTask
30import lsst.geom as geom
31import lsst.afw.table as afwTable
32import lsst.afw.image as afwImage
33import lsst.utils.tests
34from lsst.utils import getPackageDir
35from lsst.log import Log
36from lsst.pipe.tasks.photoCal import PhotoCalTask, PhotoCalConfig
37from lsst.pipe.tasks.colorterms import Colorterm, ColortermDict, ColortermLibrary
39RefCatDir = os.path.join(getPackageDir("pipe_tasks"), "tests", "data", "sdssrefcat")
41# Quiet down meas_astrom logging, so we can see PhotoCal logs better
42Log.getLogger("LoadIndexedReferenceObjectsTask").setLevel(Log.WARN)
44testColorterms = ColortermLibrary(data={
45 "test*": ColortermDict(data={
46 "test-g": Colorterm(primary="g", secondary="r", c0=0.00, c1=0.00),
47 "test-r": Colorterm(primary="r", secondary="i", c0=0.00, c1=0.00, c2=0.00),
48 "test-i": Colorterm(primary="i", secondary="z", c0=1.00, c1=0.00, c2=0.00),
49 "test-z": Colorterm(primary="z", secondary="i", c0=0.00, c1=0.00, c2=0.00),
50 })
51})
54def setup_module(module):
55 lsst.utils.tests.init()
58class PhotoCalTest(unittest.TestCase):
60 def setUp(self):
62 # Load sample input from disk
63 testDir = os.path.dirname(__file__)
64 self.srcCat = afwTable.SourceCatalog.readFits(
65 os.path.join(testDir, "data", "v695833-e0-c000.xy.fits"))
67 self.srcCat["slot_ApFlux_instFluxErr"] = 1
68 self.srcCat["slot_PsfFlux_instFluxErr"] = 1
70 # The .xy.fits file has sources in the range ~ [0,2000],[0,4500]
71 # which is bigger than the exposure
72 self.bbox = geom.Box2I(geom.Point2I(0, 0), geom.Extent2I(2048, 4612))
73 smallExposure = afwImage.ExposureF(os.path.join(testDir, "data", "v695833-e0-c000-a00.sci.fits"))
74 self.exposure = afwImage.ExposureF(self.bbox)
75 self.exposure.setWcs(smallExposure.getWcs())
76 self.exposure.setFilterLabel(afwImage.FilterLabel(band="i", physical="test-i"))
77 self.exposure.setPhotoCalib(smallExposure.getPhotoCalib())
79 coordKey = self.srcCat.getCoordKey()
80 centroidKey = self.srcCat.getCentroidSlot().getMeasKey()
81 wcs = self.exposure.getWcs()
82 for src in self.srcCat:
83 src.set(coordKey, wcs.pixelToSky(src.get(centroidKey)))
85 # Make a reference loader
86 butler = Butler(RefCatDir)
87 self.refObjLoader = LoadIndexedReferenceObjectsTask(butler=butler)
88 logLevel = Log.TRACE
89 self.log = Log.getLogger('testPhotoCal')
90 self.log.setLevel(logLevel)
92 self.config = PhotoCalConfig()
93 self.config.match.matchRadius = 0.5
94 self.config.match.referenceSelection.doMagLimit = True
95 self.config.match.referenceSelection.magLimit.maximum = 22.0
96 self.config.match.referenceSelection.magLimit.fluxField = "i_flux"
97 self.config.match.referenceSelection.doFlags = True
98 self.config.match.referenceSelection.flags.good = ['photometric']
99 self.config.match.referenceSelection.flags.bad = ['resolved']
100 self.config.match.sourceSelection.doUnresolved = False # Don't have star/galaxy in the srcCat
102 # The test and associated data have been prepared on the basis that we
103 # use the PsfFlux to perform photometry.
104 self.config.fluxField = "base_PsfFlux_instFlux"
106 def tearDown(self):
107 del self.srcCat
108 del self.exposure
109 del self.refObjLoader
110 del self.log
112 def _runTask(self):
113 """All the common setup to actually test the results"""
114 task = PhotoCalTask(self.refObjLoader, config=self.config, schema=self.srcCat.schema)
115 pCal = task.run(exposure=self.exposure, sourceCat=self.srcCat)
116 matches = pCal.matches
117 refFluxField = pCal.arrays.refFluxFieldList[0]
119 # These are *all* the matches; we don't really expect to do that well.
120 diff = []
121 for m in matches:
122 refFlux = m[0].get(refFluxField) # reference catalog flux
123 if refFlux <= 0:
124 continue
125 refMag = u.Quantity(refFlux, u.nJy).to_value(u.ABmag)
126 instFlux = m[1].getPsfInstFlux() # Instrumental Flux
127 if instFlux <= 0:
128 continue
129 instMag = pCal.photoCalib.instFluxToMagnitude(instFlux) # Instrumental mag
130 diff.append(instMag - refMag)
131 self.diff = np.array(diff)
132 # Differences of matched objects that were used in the fit.
133 self.zp = pCal.photoCalib.instFluxToMagnitude(1.)
134 self.fitdiff = pCal.arrays.srcMag + self.zp - pCal.arrays.refMag
136 def testFlags(self):
137 """test that all the calib_photometry flags are set to reasonable values"""
138 schema = self.srcCat.schema
139 task = PhotoCalTask(self.refObjLoader, config=self.config, schema=schema)
140 mapper = afwTable.SchemaMapper(self.srcCat.schema, schema)
141 cat = afwTable.SourceCatalog(schema)
142 for name in self.srcCat.schema.getNames():
143 mapper.addMapping(self.srcCat.schema.find(name).key)
144 cat.extend(self.srcCat, mapper=mapper)
146 # test that by default, no stars are reserved and all used are candidates
147 task.run(exposure=self.exposure, sourceCat=cat)
148 used = 0
149 for source in cat:
150 if source.get("calib_photometry_used"):
151 used += 1
152 self.assertFalse(source.get("calib_photometry_reserved"))
153 # test that some are actually used
154 self.assertGreater(used, 0)
156 def testZeroPoint(self):
157 """ Test to see if we can compute a photometric zeropoint given a reference task"""
158 self._runTask()
159 self.assertGreater(len(self.diff), 50)
160 self.log.info('%i magnitude differences; mean difference %g; mean abs diff %g' %
161 (len(self.diff), np.mean(self.diff), np.mean(np.abs(self.diff))))
162 self.assertLess(np.mean(self.diff), 0.6)
164 # Differences of matched objects that were used in the fit.
165 self.log.debug('zeropoint: %g', self.zp)
166 self.log.debug('number of sources used in fit: %i', len(self.fitdiff))
167 self.log.debug('rms diff: %g', np.mean(self.fitdiff**2)**0.5)
168 self.log.debug('median abs(diff): %g', np.median(np.abs(self.fitdiff)))
170 # zeropoint: 31.3145
171 # number of sources used in fit: 65
172 # median diff: -0.009681
173 # mean diff: 0.00331871
174 # median abs(diff): 0.0368904
175 # mean abs(diff): 0.0516589
177 self.assertLess(abs(self.zp - 31.3145), 0.05)
178 self.assertGreater(len(self.fitdiff), 50)
179 # Tolerances are somewhat arbitrary; they're set simply to avoid regressions, and
180 # are not based on we'd expect to get given the data quality.
181 lq, uq = np.percentile(self.fitdiff, (25, 75))
182 rms = 0.741*(uq - lq) # Convert IQR to stdev assuming a Gaussian
183 self.assertLess(rms, 0.07) # rms difference
184 self.assertLess(np.median(np.abs(self.fitdiff)), 0.06) # median absolution difference
186 def testColorTerms(self):
187 """ Test to see if we can apply colorterm corrections while computing photometric zeropoints"""
188 # Turn colorterms on. The colorterm library used here is simple - we just apply a 1 mag
189 # color-independentcolorterm correction to everything. This should change the photometric zeropoint.
190 # by 1 mag.
191 self.config.applyColorTerms = True
192 self.config.colorterms = testColorterms
193 self.config.photoCatName = "testglob" # Check glo expansion
194 # zerPointOffset is the offset in the zeropoint that we expect from a uniform (i.e. color-independent)
195 # colorterm correction.
196 zeroPointOffset = testColorterms.data['test*'].data['test-i'].c0
197 self._runTask()
199 self.assertLess(np.mean(self.diff), 0.6 + zeroPointOffset)
200 self.log.debug('zeropoint: %g', self.zp)
201 # zeropoint: 32.3145
202 self.assertLess(abs(self.zp - (31.3145 + zeroPointOffset)), 0.05)
205class MemoryTester(lsst.utils.tests.MemoryTestCase):
206 pass
209if __name__ == "__main__": 209 ↛ 210line 209 didn't jump to line 210, because the condition on line 209 was never true
210 lsst.utils.tests.init()
211 unittest.main()