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

399 statements  

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# 

23 

24""" 

25Test the basic mechanics of coaddition, coadd processing, and forced photometry. 

26 

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). 

31 

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. 

36 

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). 

42 

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""" 

50 

51import unittest 

52import shutil 

53import os 

54import numbers 

55 

56import numpy as np 

57 

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 

67 

68try: 

69 import lsst.meas.base 

70except ImportError: 

71 haveMeasBase = False 

72else: 

73 haveMeasBase = True 

74 

75from lsst.pipe.tasks.assembleCoadd import AssembleCoaddConfig, SafeClipAssembleCoaddConfig 

76from lsst.pipe.tasks.multiBand import (DetectCoaddSourcesTask, MergeDetectionsTask, 

77 DeblendCoaddSourcesTask, 

78 MeasureMergedCoaddSourcesTask, MergeMeasurementsTask) 

79 

80DATAREPO_ROOT = os.path.join(os.path.dirname(__file__), ".tests", "testCoadds-data") 

81 

82 

83def assertWrapper(func): 

84 """Decorator to intercept any test failures and reraise whilst recording 

85 a failure. 

86 

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 

99 

100 return wrapped 

101 

102 

103def setup_module(module): 

104 lsst.utils.tests.init() 

105 

106 

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"])] 

110 

111 

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) 

122 

123 

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) 

129 

130 

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]) 

136 

137 

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) 

146 

147 

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 

158 

159 

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) 

166 

167 

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) 

174 

175 

176class CoaddsTestCase(lsst.utils.tests.TestCase): 

177 

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 

184 

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) 

189 

190 # Create a task that creates simulated images and builds a coadd from them 

191 mocksTask = lsst.pipe.tasks.mocks.MockCoaddTask() 

192 

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 

196 

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) 

204 

205 butler = lsst.pipe.tasks.mocks.makeDataRepo(DATAREPO_ROOT) 

206 

207 mocksTask.buildAllInputs(butler) 

208 

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) 

216 

217 mergeDetConfig = MergeDetectionsTask.ConfigClass() 

218 mergeDetConfig.priorityList = ['r', ] 

219 mergeDetTask = MergeDetectionsTask(config=mergeDetConfig, butler=butler) 

220 mergeDetTask.writeSchemas(butler) 

221 runTaskOnPatchList(butler, mergeDetTask, mocksTask) 

222 

223 deblendSourcesConfig = DeblendCoaddSourcesTask.ConfigClass() 

224 deblendSourcesTask = DeblendCoaddSourcesTask(config=deblendSourcesConfig, butler=butler) 

225 deblendSourcesTask.writeSchemas(butler) 

226 runTaskOnPatchList(butler, deblendSourcesTask, mocksTask) 

227 

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) 

236 

237 mergeMeasConfig = MergeMeasurementsTask.ConfigClass() 

238 mergeMeasConfig.priorityList = ['r', ] 

239 mergeMeasTask = MergeMeasurementsTask(config=mergeMeasConfig, butler=butler) 

240 mergeMeasTask.writeSchemas(butler) 

241 runTaskOnPatchList(butler, mergeMeasTask, mocksTask) 

242 

243 runForcedPhotCoaddTask(butler, mocksTask) 

244 runForcedPhotCcdTask(butler) 

245 

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}") 

255 

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"] 

263 

264 def tearDown(self): 

265 del self.mocksTask 

266 del self.butler 

267 

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())) 

278 

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) 

291 

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) 

302 

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) 

336 

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) 

367 

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()) 

386 

387 @assertWrapper 

388 def testCoaddPsf(self, tract=0): 

389 """Test that stars on the coadd are well represented by the attached PSF 

390 

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) 

416 

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)") 

452 

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) 

488 

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) 

515 

516 @assertWrapper 

517 def testAlgMetadataOutput(self): 

518 """Test to see if algMetadata is persisted correctly from MeasureMergedCoaddSourcesTask. 

519 

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) 

539 

540 @assertWrapper 

541 def testForcedIdNames(self): 

542 """Test that forced photometry ID fields are named as we expect 

543 (DM-8210). 

544 

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) 

559 

560 

561class AssembleCoaddTestCase(lsst.utils.tests.TestCase): 

562 

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) 

568 

569 

570class MatchMemoryTestCase(lsst.utils.tests.MemoryTestCase): 

571 pass 

572 

573 

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()