Coverage for tests/nopytest_test_coadds.py: 93%
Shortcuts 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
Shortcuts 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#!/usr/bin/env python
2#
3# LSST Data Management System
4# Copyright 2008-2017 AURA/LSST.
5#
6# This product includes software developed by the
7# LSST Project (http://www.lsst.org/).
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 LSST License Statement and
20# the GNU General Public License along with this program. If not,
21# see <https://www.lsstcorp.org/LegalNotices/>.
22#
24"""
25Test the basic mechanics of coaddition, coadd processing, and forced photometry.
27In this test, we build a mock calexps using perfectly knowns WCSs, with the only sources
28being stars created from a perfectly known PSF, then coadd them, process the coadd (using
29the new measurement framework in meas_base), and then run forced photometry (again, using
30the new forced measurement tasks in meas_base).
32We do not check that the results of this processing is exactly what we'd expect, except in
33some cases where it's easy and/or particularly important to do so (e.g. CoaddPsf); we mostly
34just check that everything runs, and that the results make enough sense to let us proceed
35to the next step.
37NOTE: if this test fails with what looks like a failure to load a FITS file, try changing
38the REUSE_DATAREPO variable below to False, as sometimes this error message indicates a
39different problem that's revealed when we're not trying to cache the mock data between
40tests (but set REUSE_DATAREPO back to True when done debugging, or this test will be very
41slow).
43WARNING: This test should not be run with other tests using pytest, and should
44not be discoverable automatically by pytest. The reason for this is that the
45tests rely on 200 MB of data generated on module load, using a single
46directory visible to all the tests. When run in parallel with pytest-xdist
47this data will be created for every sub-process, leading to excessive disk
48usage, excessive test execution times and possible failure.
49"""
51import unittest
52import shutil
53import os
54import numbers
56import numpy as np
58import lsst.utils.tests
59import lsst.afw.math
60import lsst.geom
61import lsst.afw.image
62import lsst.afw.table.io
63import lsst.afw.table.testUtils
64import lsst.meas.algorithms
65import lsst.pipe.tasks.mocks
66import lsst.daf.persistence
68try:
69 import lsst.meas.base
70except ImportError:
71 haveMeasBase = False
72else:
73 haveMeasBase = True
75from lsst.pipe.tasks.assembleCoadd import AssembleCoaddConfig, SafeClipAssembleCoaddConfig
76from lsst.pipe.tasks.multiBand import (DetectCoaddSourcesTask, MergeDetectionsTask,
77 DeblendCoaddSourcesTask,
78 MeasureMergedCoaddSourcesTask, MergeMeasurementsTask)
80DATAREPO_ROOT = os.path.join(os.path.dirname(__file__), ".tests", "testCoadds-data")
83def assertWrapper(func):
84 """Decorator to intercept any test failures and reraise whilst recording
85 a failure.
87 This allows tearDownClass to behave differently depending on
88 whether any of the tests failed. In this case we clean up the test
89 data if all the tests passed but leave it behind if any of the tests
90 failed for any reason."""
91 def wrapped(self):
92 try:
93 func(self)
94 except Exception:
95 # Set the CLASS property since the data are per-test class
96 # so any failure is important in any of the tests.
97 type(self).failed = True
98 raise
100 return wrapped
103def setup_module(module):
104 lsst.utils.tests.init()
107def getCalexpIds(butler, tract=0):
108 catalog = butler.get("observations", tract=tract, immediate=True)
109 return [{"visit": int(visit), "ccd": int(ccd)} for visit, ccd in zip(catalog["visit"], catalog["ccd"])]
112def addMaskPlanes(butler):
113 # Get the dataId for each calexp in the repository
114 calexpDataIds = getCalexpIds(butler)
115 # Loop over each of the calexp and add the CROSSTALK and NOT_DEBLENDED mask planes
116 for Id in calexpDataIds:
117 image = butler.get('calexp', Id)
118 mask = image.getMaskedImage().getMask()
119 mask.addMaskPlane("CROSSTALK")
120 mask.addMaskPlane("NOT_DEBLENDED")
121 butler.put(image, 'calexp', dataId=Id)
124def runTaskOnPatches(butler, task, mocksTask, tract=0):
125 skyMap = butler.get(mocksTask.config.coaddName + "Coadd_skyMap", immediate=True)
126 tractInfo = skyMap[tract]
127 for dataRef in mocksTask.iterPatchRefs(butler, tractInfo):
128 task.runDataRef(dataRef)
131def runTaskOnPatchList(butler, task, mocksTask, tract=0, rerun=None):
132 skyMap = butler.get(mocksTask.config.coaddName + "Coadd_skyMap", immediate=True)
133 tractInfo = skyMap[tract]
134 for dataRef in mocksTask.iterPatchRefs(butler, tractInfo):
135 task.runDataRef([dataRef])
138def runTaskOnCcds(butler, task, tract=0):
139 catalog = butler.get("observations", tract=tract, immediate=True)
140 visitKey = catalog.getSchema().find("visit").key
141 ccdKey = catalog.getSchema().find("ccd").key
142 for record in catalog:
143 dataRef = butler.dataRef("forced_src", tract=tract, visit=record.getI(visitKey),
144 ccd=record.getI(ccdKey))
145 task.runDataRef(dataRef)
148def getObsDict(butler, tract=0):
149 catalog = butler.get("observations", tract=tract, immediate=True)
150 visitKey = catalog.getSchema().find("visit").key
151 ccdKey = catalog.getSchema().find("ccd").key
152 obsDict = {}
153 for record in catalog:
154 visit = record.getI(visitKey)
155 ccd = record.getI(ccdKey)
156 obsDict.setdefault(visit, {})[ccd] = record
157 return obsDict
160def runForcedPhotCoaddTask(butler, mocksTask):
161 config = lsst.meas.base.ForcedPhotCoaddConfig()
162 config.references.filter = 'r'
163 task = lsst.meas.base.ForcedPhotCoaddTask(config=config, butler=butler)
164 task.writeSchemas(butler)
165 runTaskOnPatches(butler, task, mocksTask)
168def runForcedPhotCcdTask(butler):
169 config = lsst.meas.base.ForcedPhotCcdConfig()
170 config.references.filter = 'r'
171 task = lsst.meas.base.ForcedPhotCcdTask(config=config, butler=butler)
172 task.writeSchemas(butler)
173 runTaskOnCcds(butler, task)
176class CoaddsTestCase(lsst.utils.tests.TestCase):
178 @unittest.skipUnless(haveMeasBase, "meas_base could not be imported")
179 @classmethod
180 def setUpClass(cls):
181 """Create 200MB of test data."""
182 # Start by assuming nothing failed
183 cls.failed = False
185 if os.path.exists(DATAREPO_ROOT): 185 ↛ 186line 185 didn't jump to line 186, because the condition on line 185 was never true
186 print(f"Deleting existing repo: {DATAREPO_ROOT}")
187 # Do not ignore errors since failure to clean up is indicative
188 shutil.rmtree(DATAREPO_ROOT)
190 # Create a task that creates simulated images and builds a coadd from them
191 mocksTask = lsst.pipe.tasks.mocks.MockCoaddTask()
193 # Create an instance of DetectCoaddSourcesTask to measure on the coadd.
194 # There's no noise in these images, so we set a direct-value threshold,
195 # and the background weighting (when using Approximate) to False
197 detectConfig = DetectCoaddSourcesTask.ConfigClass()
198 # Images have no noise, so we can't use the default DynamicDetectionTask
199 detectConfig.detection.retarget(lsst.meas.algorithms.SourceDetectionTask)
200 detectConfig.detection.thresholdType = "value"
201 detectConfig.detection.thresholdValue = 0.01
202 detectConfig.detection.background.weighting = False
203 detectTask = DetectCoaddSourcesTask(config=detectConfig)
205 butler = lsst.pipe.tasks.mocks.makeDataRepo(DATAREPO_ROOT)
207 mocksTask.buildAllInputs(butler)
209 addMaskPlanes(butler)
210 mocksTask.buildCoadd(butler)
211 mocksTask.buildMockCoadd(butler)
212 detectTask.writeSchemas(butler)
213 # Now run the seperate multiband tasks on the Coadd to make the reference
214 # catalog for the forced photometry tests.
215 runTaskOnPatches(butler, detectTask, mocksTask)
217 mergeDetConfig = MergeDetectionsTask.ConfigClass()
218 mergeDetConfig.priorityList = ['r', ]
219 mergeDetTask = MergeDetectionsTask(config=mergeDetConfig, butler=butler)
220 mergeDetTask.writeSchemas(butler)
221 runTaskOnPatchList(butler, mergeDetTask, mocksTask)
223 deblendSourcesConfig = DeblendCoaddSourcesTask.ConfigClass()
224 deblendSourcesTask = DeblendCoaddSourcesTask(config=deblendSourcesConfig, butler=butler)
225 deblendSourcesTask.writeSchemas(butler)
226 runTaskOnPatchList(butler, deblendSourcesTask, mocksTask)
228 measMergedConfig = MeasureMergedCoaddSourcesTask.ConfigClass()
229 measMergedConfig.measurement.slots.shape = "base_SdssShape"
230 measMergedConfig.measurement.plugins['base_PixelFlags'].masksFpAnywhere = []
231 measMergedConfig.propagateFlags.flags = {} # Disable flag propagation: no flags to propagate
232 measMergedConfig.doMatchSources = False # We don't have a reference catalog available
233 measMergedTask = MeasureMergedCoaddSourcesTask(config=measMergedConfig, butler=butler)
234 measMergedTask.writeSchemas(butler)
235 runTaskOnPatches(butler, measMergedTask, mocksTask)
237 mergeMeasConfig = MergeMeasurementsTask.ConfigClass()
238 mergeMeasConfig.priorityList = ['r', ]
239 mergeMeasTask = MergeMeasurementsTask(config=mergeMeasConfig, butler=butler)
240 mergeMeasTask.writeSchemas(butler)
241 runTaskOnPatchList(butler, mergeMeasTask, mocksTask)
243 runForcedPhotCoaddTask(butler, mocksTask)
244 runForcedPhotCcdTask(butler)
246 @classmethod
247 def tearDownClass(cls):
248 """Removes test data if all tests passed."""
249 if os.path.exists(DATAREPO_ROOT): 249 ↛ exitline 249 didn't return from function 'tearDownClass', because the condition on line 249 was never false
250 if not cls.failed: 250 ↛ 254line 250 didn't jump to line 254, because the condition on line 250 was never false
251 print(f"Deleting temporary data repository {DATAREPO_ROOT}")
252 shutil.rmtree(DATAREPO_ROOT, ignore_errors=True)
253 else:
254 print(f"Temporary data repository retained at {DATAREPO_ROOT}")
256 def setUp(self):
257 if not haveMeasBase: 257 ↛ 258line 257 didn't jump to line 258, because the condition on line 257 was never true
258 raise unittest.SkipTest("meas_base could not be imported; skipping this test")
259 self.mocksTask = lsst.pipe.tasks.mocks.MockCoaddTask()
260 self.butler = lsst.daf.persistence.Butler(DATAREPO_ROOT)
261 self.coaddNameList = ["Coadd", "CoaddPsfMatched"]
262 self.warpNameList = ["Coadd_directWarp", "Coadd_psfMatchedWarp"]
264 def tearDown(self):
265 del self.mocksTask
266 del self.butler
268 @assertWrapper
269 def testMaskPlanesExist(self):
270 # Get the dataId for each calexp in the repository
271 calexpDataIds = getCalexpIds(self.butler)
272 # Loop over each Id and verify the mask planes were added
273 for ID in calexpDataIds:
274 image = self.butler.get('calexp', ID)
275 mask = image.getMaskedImage().getMask()
276 self.assertIn('CROSSTALK', list(mask.getMaskPlaneDict().keys()))
277 self.assertIn('NOT_DEBLENDED', list(mask.getMaskPlaneDict().keys()))
279 def comparePsfs(self, a, b):
280 if a is None and b is None: 280 ↛ 281line 280 didn't jump to line 281, because the condition on line 280 was never true
281 return
282 ak = a.getKernel()
283 bk = b.getKernel()
284 self.assertEqual(type(ak), type(bk))
285 self.assertEqual(ak.getDimensions(), bk.getDimensions())
286 self.assertEqual(ak.getNKernelParameters(), ak.getNKernelParameters())
287 self.assertEqual(ak.getNSpatialParameters(), ak.getNSpatialParameters())
288 for aFuncParams, bFuncParams in zip(ak.getSpatialParameters(), bk.getSpatialParameters()):
289 for aParam, bParam in zip(aFuncParams, bFuncParams):
290 self.assertEqual(aParam, bParam)
292 # Expected to fail until DM-5174 is fixed. Then replace next line
293 # with assertWrapper
294 @unittest.expectedFailure
295 def testMasksRemoved(self):
296 for dataProduct in self.coaddNameList: 296 ↛ exitline 296 didn't return from function 'testMasksRemoved', because the loop on line 296 didn't complete
297 image = self.butler.get(self.mocksTask.config.coaddName + dataProduct + "_mock",
298 {'filter': 'r', 'tract': 0, 'patch': '0,0'})
299 keys = image.getMaskedImage().getMask().getMaskPlaneDict().keys()
300 self.assertNotIn('CROSSTALK', keys)
301 self.assertNotIn('NOT_DEBLENDED', keys)
303 @assertWrapper
304 def testTempExpInputs(self, tract=0):
305 skyMap = self.butler.get(self.mocksTask.config.coaddName + "Coadd_skyMap", immediate=True)
306 tractInfo = skyMap[tract]
307 for dataProduct in self.warpNameList:
308 for visit, obsVisitDict in getObsDict(self.butler, tract).items():
309 foundOneTempExp = False
310 for patchRef in self.mocksTask.iterPatchRefs(self.butler, tractInfo):
311 datasetType = self.mocksTask.config.coaddName + dataProduct
312 try:
313 tempExp = patchRef.get(datasetType, visit=visit, immediate=True)
314 foundOneTempExp = True
315 except Exception as e:
316 print("testTempExpInputs patchRef.get failed with datasetType=%r, visit=%r: %s" %
317 (datasetType, visit, e))
318 continue
319 self.assertEqual(tractInfo.getWcs(), tempExp.getWcs())
320 coaddInputs = tempExp.getInfo().getCoaddInputs()
321 self.assertEqual(len(coaddInputs.visits), 1)
322 visitRecord = coaddInputs.visits[0]
323 self.assertEqual(visitRecord.getWcs(), tempExp.getWcs())
324 self.assertEqual(visitRecord.getBBox(), tempExp.getBBox())
325 self.assertGreater(len(coaddInputs.ccds), 0)
326 ccdKey = coaddInputs.ccds.getSchema().find("ccd").key
327 for ccdRecord in coaddInputs.ccds:
328 ccd = ccdRecord.getI(ccdKey)
329 obsRecord = obsVisitDict[ccd]
330 self.assertEqual(obsRecord.getId(), ccdRecord.getId())
331 self.assertEqual(obsRecord.getWcs(), ccdRecord.getWcs())
332 self.assertEqual(obsRecord.getBBox(), ccdRecord.getBBox())
333 self.assertIsNotNone(ccdRecord.getTransmissionCurve())
334 self.comparePsfs(obsRecord.getPsf(), ccdRecord.getPsf())
335 self.assertTrue(foundOneTempExp)
337 @assertWrapper
338 def testCoaddInputs(self, tract=0):
339 skyMap = self.butler.get(self.mocksTask.config.coaddName + "Coadd_skyMap", immediate=True)
340 tractInfo = skyMap[tract]
341 obsCatalog = self.butler.get("observations", tract=tract, immediate=True)
342 for patchRef in self.mocksTask.iterPatchRefs(self.butler, tractInfo):
343 for dataProduct in self.coaddNameList:
344 coaddExp = patchRef.get(self.mocksTask.config.coaddName + dataProduct, immediate=True)
345 self.assertEqual(tractInfo.getWcs(), coaddExp.getWcs())
346 coaddInputs = coaddExp.getInfo().getCoaddInputs()
347 try:
348 ccdVisitKey = coaddInputs.ccds.getSchema().find("visit").key
349 except Exception:
350 print(patchRef.dataId)
351 print(coaddInputs.ccds.getSchema())
352 raise
353 for ccdRecord in coaddInputs.ccds:
354 obsRecord = obsCatalog.find(ccdRecord.getId())
355 self.assertEqual(obsRecord.getId(), ccdRecord.getId())
356 self.assertEqual(obsRecord.getWcs(), ccdRecord.getWcs())
357 self.assertEqual(obsRecord.getBBox(), ccdRecord.getBBox())
358 self.assertEqual(obsRecord.get("filter"), ccdRecord.get("filter"))
359 self.comparePsfs(obsRecord.getPsf(), ccdRecord.getPsf())
360 self.assertIsNotNone(ccdRecord.getTransmissionCurve())
361 self.assertIsNotNone(coaddInputs.visits.find(ccdRecord.getL(ccdVisitKey)))
362 for visitRecord in coaddInputs.visits:
363 nCcds = len([ccdRecord for ccdRecord in coaddInputs.ccds
364 if ccdRecord.getL(ccdVisitKey) == visitRecord.getId()])
365 self.assertGreaterEqual(nCcds, 1)
366 self.assertLessEqual(nCcds, 2)
368 @assertWrapper
369 def testPsfInstallation(self, tract=0):
370 skyMap = self.butler.get(self.mocksTask.config.coaddName + "Coadd_skyMap", immediate=True)
371 tractInfo = skyMap[tract]
372 for patchRef in self.mocksTask.iterPatchRefs(self.butler, tractInfo):
373 coaddExp = patchRef.get(self.mocksTask.config.coaddName + "Coadd", immediate=True)
374 ccdCat = coaddExp.getInfo().getCoaddInputs().ccds
375 savedPsf = coaddExp.getPsf()
376 newPsf = lsst.meas.algorithms.CoaddPsf(ccdCat, coaddExp.getWcs())
377 self.assertEqual(savedPsf.getComponentCount(), len(ccdCat))
378 self.assertEqual(newPsf.getComponentCount(), len(ccdCat))
379 for n, record in enumerate(ccdCat):
380 self.assertIs(savedPsf.getPsf(n), record.getPsf())
381 self.assertIs(newPsf.getPsf(n), record.getPsf())
382 self.assertEqual(savedPsf.getWcs(n), record.getWcs())
383 self.assertEqual(newPsf.getWcs(n), record.getWcs())
384 self.assertEqual(savedPsf.getBBox(n), record.getBBox())
385 self.assertEqual(newPsf.getBBox(n), record.getBBox())
387 @assertWrapper
388 def testCoaddPsf(self, tract=0):
389 """Test that stars on the coadd are well represented by the attached PSF
391 in both direct and PSF-matched coadds. The attached PSF is a "CoaddPsf"
392 for direct coadds and a Model Psf for PSF-matched Coadds
393 """
394 skyMap = self.butler.get(self.mocksTask.config.coaddName + "Coadd_skyMap", immediate=True)
395 tractInfo = skyMap[tract]
396 # Start by finding objects that never appeared on the edge of an image
397 simSrcCat = self.butler.get("simsrc", tract=tract, immediate=True)
398 simSrcSchema = simSrcCat.getSchema()
399 objectIdKey = simSrcSchema.find("objectId").key
400 centroidInBBoxKey = simSrcSchema.find("centroidInBBox").key
401 partialOverlapKey = simSrcSchema.find("partialOverlap").key
402 simSrcByObject = {}
403 for simSrcRecord in simSrcCat:
404 simSrcByObject.setdefault(simSrcRecord.getL(objectIdKey), []).append(simSrcRecord)
405 pureObjectIds = set() # set will contain objects that never appear on edges
406 for objectId, simSrcRecords in simSrcByObject.items():
407 inAnyImages = False
408 for simSrcRecord in simSrcRecords:
409 if simSrcRecord.getFlag(centroidInBBoxKey):
410 if simSrcRecord.getFlag(partialOverlapKey):
411 break
412 inAnyImages = True
413 else: # only get here if we didn't break
414 if inAnyImages: 414 ↛ 406line 414 didn't jump to line 406, because the condition on line 414 was never false
415 pureObjectIds.add(objectId)
417 truthCatalog = self.butler.get("truth", tract=tract, immediate=True)
418 truthCatalog.sort()
419 for dataProduct in self.coaddNameList:
420 nTested = 0
421 for patchRef in self.mocksTask.iterPatchRefs(self.butler, tractInfo):
422 coaddExp = patchRef.get(self.mocksTask.config.coaddName + dataProduct, immediate=True)
423 coaddWcs = coaddExp.getWcs()
424 coaddPsf = coaddExp.getPsf()
425 coaddBBox = lsst.geom.Box2D(coaddExp.getBBox())
426 for objectId in pureObjectIds:
427 truthRecord = truthCatalog.find(objectId)
428 position = coaddWcs.skyToPixel(truthRecord.getCoord())
429 if not coaddBBox.contains(position):
430 continue
431 try:
432 psfImage = coaddPsf.computeImage(position)
433 except Exception as e:
434 print("testCoaddPsf coaddPsf.computeImage failed on position=%s: %s" %
435 (position, e))
436 continue
437 psfImageBBox = psfImage.getBBox()
438 if not coaddExp.getBBox().contains(psfImageBBox): 438 ↛ 439line 438 didn't jump to line 439, because the condition on line 438 was never true
439 continue
440 starImage = lsst.afw.image.ImageF(coaddExp.getMaskedImage().getImage(),
441 psfImageBBox).convertD()
442 starImage /= starImage.getArray().sum()
443 psfImage /= psfImage.getArray().sum()
444 residuals = lsst.afw.image.ImageD(starImage, True)
445 residuals -= psfImage
446 self.assertFloatsAlmostEqual(starImage.getArray(), psfImage.getArray(),
447 rtol=1E-3, atol=1E-2)
448 nTested += 1
449 if nTested == 0: 449 ↛ 450line 449 didn't jump to line 450, because the condition on line 449 was never true
450 print("WARNING: CoaddPsf test inconclusive (this can occur randomly, but very rarely; "
451 "first try running the test again)")
453 @assertWrapper
454 def testCoaddTransmissionCurves(self, tract=0):
455 """Test that coadded TransmissionCurves agree with those of the inputs."""
456 skyMap = self.butler.get(self.mocksTask.config.coaddName + "Coadd_skyMap", immediate=True)
457 tractInfo = skyMap[tract]
458 truthCatalog = self.butler.get("truth", tract=tract, immediate=True)
459 wavelengths = np.linspace(4000, 7000, 10)
460 for dataProduct in self.coaddNameList:
461 nTested = 0
462 for patchRef in self.mocksTask.iterPatchRefs(self.butler, tractInfo):
463 coaddExp = patchRef.get(self.mocksTask.config.coaddName + dataProduct, immediate=True)
464 coaddWcs = coaddExp.getWcs()
465 coaddTransmissionCurve = coaddExp.getInfo().getTransmissionCurve()
466 coaddBBox = lsst.geom.Box2D(coaddExp.getBBox())
467 inputs = coaddExp.getInfo().getCoaddInputs().ccds
468 for truthRecord in truthCatalog:
469 coaddPosition = coaddWcs.skyToPixel(truthRecord.getCoord())
470 if not coaddBBox.contains(coaddPosition):
471 continue
472 summedThroughput = np.zeros(wavelengths.shape, dtype=float)
473 weightSum = 0.0
474 for sensorRecord in inputs.subsetContaining(truthRecord.getCoord(),
475 includeValidPolygon=True):
476 sensorPosition = sensorRecord.getWcs().skyToPixel(truthRecord.getCoord())
477 sensorTransmission = sensorRecord.getTransmissionCurve()
478 weight = sensorRecord.get("weight")
479 summedThroughput += sensorTransmission.sampleAt(sensorPosition, wavelengths)*weight
480 weightSum += weight
481 if weightSum == 0.0:
482 continue
483 summedThroughput /= weightSum
484 coaddThroughput = coaddTransmissionCurve.sampleAt(coaddPosition, wavelengths)
485 self.assertFloatsAlmostEqual(coaddThroughput, summedThroughput, rtol=1E-10)
486 nTested += 1
487 self.assertGreater(nTested, 5)
489 @assertWrapper
490 def testSchemaConsistency(self):
491 """Test that _schema catalogs are consistent with the data catalogs.
492 """
493 det_schema = self.butler.get("deepCoadd_det_schema").schema
494 meas_schema = self.butler.get("deepCoadd_meas_schema").schema
495 mergeDet_schema = self.butler.get("deepCoadd_mergeDet_schema").schema
496 ref_schema = self.butler.get("deepCoadd_ref_schema").schema
497 coadd_forced_schema = self.butler.get("deepCoadd_forced_src_schema").schema
498 ccd_forced_schema = self.butler.get("forced_src_schema").schema
499 patchList = ['0,0', '0,1', '1,0', '1,1']
500 for patch in patchList:
501 det = self.butler.get("deepCoadd_det", filter='r', tract=0, patch=patch)
502 self.assertSchemasEqual(det.schema, det_schema)
503 mergeDet = self.butler.get("deepCoadd_mergeDet", filter='r', tract=0, patch=patch)
504 self.assertSchemasEqual(mergeDet.schema, mergeDet_schema)
505 meas = self.butler.get("deepCoadd_meas", filter='r', tract=0, patch=patch)
506 self.assertSchemasEqual(meas.schema, meas_schema)
507 ref = self.butler.get("deepCoadd_ref", filter='r', tract=0, patch=patch)
508 self.assertSchemasEqual(ref.schema, ref_schema)
509 coadd_forced_src = self.butler.get("deepCoadd_forced_src", filter='r', tract=0, patch=patch)
510 self.assertSchemasEqual(coadd_forced_src.schema, coadd_forced_schema)
511 for visit, obsVisitDict in getObsDict(self.butler, 0).items():
512 for ccd in obsVisitDict:
513 ccd_forced_src = self.butler.get("forced_src", tract=0, visit=visit, ccd=ccd)
514 self.assertSchemasEqual(ccd_forced_src.schema, ccd_forced_schema)
516 @assertWrapper
517 def testAlgMetadataOutput(self):
518 """Test to see if algMetadata is persisted correctly from MeasureMergedCoaddSourcesTask.
520 This test fails with a NotFoundError if the algorithm metadata is not persisted"""
521 patchList = ['0,0', '0,1', '1,0', '1,1']
522 for patch in patchList:
523 cat = self.butler.get("deepCoadd_meas", filter='r', tract=0, patch=patch)
524 meta = cat.getTable().getMetadata()
525 for circApertureFluxRadius in meta.getArray('BASE_CIRCULARAPERTUREFLUX_RADII'):
526 self.assertIsInstance(circApertureFluxRadius, numbers.Number)
527 # Each time the run method of a measurement task is executed,
528 # algorithm metadata is appended to the algorithm metadata object.
529 # Depending on how many times a measurement task is run,
530 # a metadata entry may be a single value or multiple values.
531 for nOffset in meta.getArray('NOISE_OFFSET'):
532 self.assertIsInstance(nOffset, numbers.Number)
533 for noiseSrc in meta.getArray('NOISE_SOURCE'):
534 self.assertEqual(noiseSrc, 'measure')
535 for noiseExpID in meta.getArray('NOISE_EXPOSURE_ID'):
536 self.assertIsInstance(noiseExpID, numbers.Number)
537 for noiseSeedMul in meta.getArray('NOISE_SEED_MULTIPLIER'):
538 self.assertIsInstance(noiseSeedMul, numbers.Number)
540 @assertWrapper
541 def testForcedIdNames(self):
542 """Test that forced photometry ID fields are named as we expect
543 (DM-8210).
545 Specifically, coadd forced photometry should have only "id" and "parent"
546 fields, while CCD forced photometry should have those, "objectId", and
547 "parentObjectId".
548 """
549 coaddSchema = self.butler.get("deepCoadd_forced_src_schema", immediate=True).schema
550 self.assertIn("id", coaddSchema)
551 self.assertIn("parent", coaddSchema)
552 self.assertNotIn("objectId", coaddSchema)
553 self.assertNotIn("parentObjectId", coaddSchema)
554 ccdSchema = self.butler.get("forced_src_schema", immediate=True).schema
555 self.assertIn("id", ccdSchema)
556 self.assertIn("parent", ccdSchema)
557 self.assertIn("objectId", ccdSchema)
558 self.assertIn("parentObjectId", ccdSchema)
561class AssembleCoaddTestCase(lsst.utils.tests.TestCase):
563 def testSafeClipConfig(self):
564 # Test for DM-4797: ensure that AssembleCoaddConfig.setDefaults() is
565 # run when SafeClipAssembleCoaddConfig.setDefaults() is run. This
566 # simply sets the default value for badMaskPlanes.
567 self.assertEqual(AssembleCoaddConfig().badMaskPlanes, SafeClipAssembleCoaddConfig().badMaskPlanes)
570class MatchMemoryTestCase(lsst.utils.tests.MemoryTestCase):
571 pass
574if __name__ == "__main__": 574 ↛ 575line 574 didn't jump to line 575, because the condition on line 574 was never true
575 import sys
576 setup_module(sys.modules[__name__])
577 unittest.main()