Coverage for tests/test_kron.py: 8%

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

388 statements  

1# 

2# LSST Data Management System 

3# 

4# Copyright 2008-2016 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# 

23import math 

24import unittest 

25import sys 

26 

27import numpy as np 

28import itertools 

29import lsst.utils.tests 

30import lsst.afw.detection as afwDetection 

31import lsst.afw.display as afwDisplay 

32import lsst.geom as geom 

33import lsst.afw.geom as afwGeom 

34import lsst.afw.geom.ellipses as afwEllipses 

35import lsst.afw.image as afwImage 

36import lsst.afw.math as afwMath 

37import lsst.afw.table as afwTable 

38import lsst.meas.algorithms as measAlg 

39import lsst.meas.base as measBase 

40# importing this package registers essential code 

41import lsst.meas.extensions.photometryKron 

42from lsst.daf.base import PropertyList 

43 

44try: 

45 display 

46except NameError: 

47 display = False 

48 

49afwDisplay.setDefaultMaskTransparency(75) 

50 

51 

52def makeGalaxy(width, height, flux, a, b, theta, dx=0.0, dy=0.0, xy0=None, xcen=None, ycen=None): 

53 """Make a fake galaxy image. 

54 """ 

55 gal = afwImage.ImageF(width, height) 

56 if xcen is None: 

57 xcen = 0.5*width + dx 

58 if ycen is None: 

59 ycen = 0.5*height + dy 

60 I0 = flux/(2*math.pi*a*b) 

61 

62 if xy0 is not None: 

63 gal.setXY0(xy0) 

64 

65 c, s = math.cos(math.radians(theta)), math.sin(math.radians(theta)) 

66 ii, iuu, ivv = 0.0, 0.0, 0.0 

67 for y in range(height): 

68 for x in range(width): 

69 dx, dy = x + gal.getX0() - xcen, y + gal.getY0() - ycen 

70 if math.hypot(dx, dy) < 10.5: 

71 nsample = 5 

72 subZ = np.linspace(-0.5*(1 - 1/nsample), 0.5*(1 - 1/nsample), nsample) 

73 else: 

74 nsample = 1 

75 subZ = [0.0] 

76 

77 val = 0 

78 for sx in subZ: 

79 for sy in subZ: 

80 u = c*(dx + sx) + s*(dy + sy) 

81 v = -s*(dx + sx) + c*(dy + sy) 

82 val += I0*math.exp(-0.5*((u/a)**2 + (v/b)**2)) 

83 

84 if val < 0: 

85 val = 0 

86 gal[geom.Point2I(x, y), afwImage.LOCAL] = val/nsample**2 

87 

88 ii += val 

89 iuu += val*u**2 

90 ivv += val*v**2 

91 

92 iuu /= ii 

93 ivv /= ii 

94 

95 exp = afwImage.makeExposure(afwImage.makeMaskedImage(gal)) 

96 exp.getMaskedImage().getVariance().set(1.0) 

97 scale = 1.0e-4*geom.degrees 

98 cdMatrix = afwGeom.makeCdMatrix(scale=scale, flipX=True) 

99 exp.setWcs(afwGeom.makeSkyWcs(crpix=geom.Point2D(0.0, 0.0), 

100 crval=geom.SpherePoint(0.0, 0.0, geom.degrees), 

101 cdMatrix=cdMatrix)) 

102 # add a dummy Psf. The new SdssCentroid needs one 

103 exp.setPsf(afwDetection.GaussianPsf(11, 11, 0.01)) 

104 return exp 

105 

106 

107def makeMeasurementConfig(forced=False, nsigma=6.0, nIterForRadius=1, kfac=2.5): 

108 """Construct a (SingleFrame|Forced)MeasurementConfig with the requested parameters. 

109 """ 

110 if forced: 

111 msConfig = measBase.ForcedMeasurementConfig() 

112 msConfig.algorithms.names = ["base_TransformedCentroid", "base_TransformedShape", 

113 "ext_photometryKron_KronFlux"] 

114 msConfig.slots.centroid = "base_TransformedCentroid" 

115 msConfig.slots.shape = "base_TransformedShape" 

116 msConfig.copyColumns = {"id": "objectId", "parent": "parentObjectId"} 

117 else: 

118 msConfig = measBase.SingleFrameMeasurementConfig() 

119 msConfig.algorithms.names = ["base_SdssCentroid", "base_SdssShape", 

120 "ext_photometryKron_KronFlux", "base_SkyCoord"] 

121 msConfig.slots.centroid = "base_SdssCentroid" 

122 msConfig.slots.shape = "base_SdssShape" 

123 msConfig.slots.apFlux = "ext_photometryKron_KronFlux" 

124 msConfig.slots.modelFlux = None 

125 msConfig.slots.psfFlux = None 

126 msConfig.slots.gaussianFlux = None 

127 msConfig.slots.calibFlux = None 

128 # msConfig.algorithms.names.remove("correctfluxes") 

129 msConfig.plugins["ext_photometryKron_KronFlux"].nSigmaForRadius = nsigma 

130 msConfig.plugins["ext_photometryKron_KronFlux"].nIterForRadius = nIterForRadius 

131 msConfig.plugins["ext_photometryKron_KronFlux"].nRadiusForFlux = kfac 

132 msConfig.plugins["ext_photometryKron_KronFlux"].enforceMinimumRadius = False 

133 return msConfig 

134 

135 

136def measureFree(exposure, center, msConfig): 

137 """Unforced measurement. 

138 """ 

139 schema = afwTable.SourceTable.makeMinimalSchema() 

140 algMeta = PropertyList() 

141 task = measBase.SingleFrameMeasurementTask(schema, config=msConfig, algMetadata=algMeta) 

142 measCat = afwTable.SourceCatalog(schema) 

143 source = measCat.addNew() 

144 source.getTable().setMetadata(algMeta) 

145 ss = afwDetection.FootprintSet(exposure.getMaskedImage(), afwDetection.Threshold(0.1)) 

146 fp = ss.getFootprints()[0] 

147 source.setFootprint(fp) 

148 task.run(measCat, exposure) 

149 return source 

150 

151 

152def measureForced(exposure, source, refWcs, msConfig): 

153 """Forced measurement. 

154 """ 

155 refCat = afwTable.SourceCatalog(source.table) 

156 refCat.append(source) 

157 schema = afwTable.SourceTable.makeMinimalSchema() 

158 algMeta = PropertyList() 

159 source.getTable().setMetadata(algMeta) 

160 task = measBase.ForcedMeasurementTask(schema, config=msConfig, algMetadata=algMeta) 

161 measCat = task.generateMeasCat(exposure, refCat, refWcs) 

162 task.attachTransformedFootprints(measCat, refCat, exposure, refWcs) 

163 task.run(measCat, exposure, refCat, refWcs) 

164 return measCat[0] 

165 

166 

167class KronPhotometryTestCase(lsst.utils.tests.TestCase): 

168 """A test case for measuring Kron quantities. 

169 """ 

170 

171 def setUp(self): 

172 self.flux = 1e5 

173 self.width, self.height = 200, 200 

174 self.objImg = None 

175 

176 def tearDown(self): 

177 if self.objImg: 

178 del self.objImg 

179 

180 def makeAndMeasure(self, measureKron, a, b, theta, dx=0.0, dy=0.0, nsigma=6, kfac=2, nIterForRadius=1, 

181 xcen=None, ycen=None, makeImage=True): 

182 """Make and measure an elliptical Gaussian. 

183 """ 

184 if xcen is None: 

185 xcen = 0.5*self.width + dx 

186 if ycen is None: 

187 ycen = 0.5*self.height + dy 

188 # 

189 # Make the object 

190 # 

191 if a < b: 

192 a, b = b, a 

193 theta += 90 

194 

195 currentDisp = None 

196 if self.objImg is None: 

197 makeImage = True 

198 if makeImage: 

199 self.objImg = makeGalaxy(self.width, self.height, self.flux, a, b, theta, dx=dx, dy=dy, 

200 xy0=geom.Point2I(10, 10), xcen=xcen, ycen=ycen) 

201 if display: 

202 disp = afwDisplay.Display(frame=0) 

203 disp.mtv(self.objImg, title=self._testMethodName + ": %g %g" % (a, b)) 

204 currentDisp = disp 

205 if not makeImage and display: 

206 try: 

207 currentDisp = disp 

208 except NameError: 

209 currentDisp = None 

210 if currentDisp: 

211 disp.pan(xcen, ycen) 

212 disp.dot("+", xcen, ycen, size=1, ctype=afwDisplay.RED) 

213 c, s = math.cos(math.radians(theta)), math.sin(math.radians(theta)) 

214 # N.b. add 1/12 in quadrature to allow for pixellisation 

215 disp.dot("@:%f,%f,%f" % (nsigma**2*((a**2 + 1/12.0)*c**2 + (b**2 + 1/12.0)*s**2), 

216 nsigma**2*(a**2 - b**2)*c*s, 

217 nsigma**2*((a**2 + 1/12.0)*s**2 + (b**2 + 1/12.0)*c**2)), 

218 xcen, ycen, size=1, ctype=afwDisplay.RED) 

219 # 

220 # Do the measuring 

221 # 

222 FWHM = 5 

223 ksize = 25 # size of desired kernel 

224 self.objImg.setPsf(measAlg.DoubleGaussianPsf(ksize, ksize, 

225 FWHM/(2*math.sqrt(2*math.log(2))), 1, 0.1)) 

226 

227 return measureKron(self.objImg, xcen, ycen, nsigma, kfac, nIterForRadius, disp=currentDisp) 

228 

229 def measureKron(self, objImg, xcen, ycen, nsigma, kfac, nIterForRadius, disp=None): 

230 """Measure Kron quantities using the C++ code. 

231 """ 

232 # 

233 # Now measure things 

234 # 

235 center = geom.Point2D(xcen, ycen) 

236 msConfig = makeMeasurementConfig(False, nsigma, nIterForRadius, kfac) 

237 source = measureFree(objImg, center, msConfig) 

238 algMeta = source.getTable().getMetadata() 

239 self.assertTrue(algMeta.exists('EXT_PHOTOMETRYKRON_KRONFLUX_NRADIUSFORFLUX')) 

240 

241 R_K = source.get("ext_photometryKron_KronFlux_radius") 

242 flux_K = source.get("ext_photometryKron_KronFlux_instFlux") 

243 fluxErr_K = source.get("ext_photometryKron_KronFlux_instFluxErr") 

244 flags_K = source.get("ext_photometryKron_KronFlux_flag") 

245 if not flags_K: 

246 # Forced measurement on the same image should produce exactly the same result 

247 msConfig = makeMeasurementConfig(True, nsigma, nIterForRadius, kfac) 

248 forced = measureForced(objImg, source, objImg.getWcs(), msConfig) 

249 algMeta = source.getTable().getMetadata() 

250 self.assertTrue(algMeta.exists('EXT_PHOTOMETRYKRON_KRONFLUX_NRADIUSFORFLUX')) 

251 for field in ( 

252 "ext_photometryKron_KronFlux_instFlux", 

253 "ext_photometryKron_KronFlux_instFluxErr", 

254 "ext_photometryKron_KronFlux_radius", 

255 "ext_photometryKron_KronFlux_flag" 

256 ): 

257 try: 

258 if np.isnan(source.get(field)): 

259 self.assertTrue(np.isnan(forced.get(field))) 

260 else: 

261 self.assertFloatsAlmostEqual(source.get( 

262 field), forced.get(field), rtol=1.0e-6, atol=None) 

263 except AssertionError: 

264 print("Failed:", field, source.get(field), forced.get(field)) 

265 raise 

266 

267 if disp: 

268 disp.dot("x", xcen, ycen, ctype=afwDisplay.MAGENTA, size=1) 

269 afwDisplay.utils.drawFootprint(source.getFootprint(), display=disp) 

270 shape = source.getShape() 

271 if True: # nsigma*shape, the radius used to estimate R_K 

272 shape = shape.clone() 

273 shape.scale(source.get("ext_photometryKron_KronFlux_radius_for_radius") 

274 / shape.getDeterminantRadius()) 

275 disp.dot(shape, xcen, ycen, ctype=afwDisplay.MAGENTA) 

276 # Show R_K 

277 shape = shape.clone() 

278 for r, ct in [(R_K, afwDisplay.BLUE), (R_K*kfac, afwDisplay.CYAN), ]: 

279 shape.scale(r/shape.getDeterminantRadius()) 

280 disp.dot(shape, xcen, ycen, ctype=ct) 

281 

282 return (R_K, flux_K, fluxErr_K, flags_K, 

283 source.get("ext_photometryKron_KronFlux_flag_bad_radius"), 

284 source.get("ext_photometryKron_KronFlux_flag_small_radius")) 

285 

286 def measureKronInPython(self, objImg, xcen, ycen, nsigma, kfac, nIterForRadius, makeImage=None): 

287 """Measure the Kron quantities of an elliptical Gaussian in python. 

288 

289 N.b. only works for XY0 == (0, 0) 

290 """ 

291 # 

292 # Measure moments using SDSS shape algorithm 

293 # 

294 # Note: this code was converted to the new meas_framework, but is not exercised. 

295 msConfig = makeMeasurementConfig(False, nsigma, nIterForRadius, kfac) 

296 center = geom.Point2D(xcen, ycen) 

297 source = self.measureFree(objImg, center, msConfig) 

298 algMeta = source.getTable().getMetadata() 

299 self.assertTrue(algMeta.exists('EXT_PHOTOMETRYKRON_KRONFLUX_NRADIUSFORFLUX')) 

300 

301 Mxx = source.getIxx() 

302 Mxy = source.getIxy() 

303 Myy = source.getIyy() 

304 # 

305 # Calculate principal axes 

306 # 

307 Muu_p_Mvv = Mxx + Myy 

308 Muu_m_Mvv = math.sqrt((Mxx - Myy)**2 + 4*Mxy**2) 

309 Muu = 0.5*(Muu_p_Mvv + Muu_m_Mvv) 

310 Mvv = 0.5*(Muu_p_Mvv - Muu_m_Mvv) 

311 theta = 0.5*math.atan2(2*Mxy, Mxx - Myy) 

312 a = math.sqrt(Muu) 

313 b = math.sqrt(Mvv) 

314 ab = a/b 

315 # 

316 # Get footprint 

317 # 

318 ellipse = afwEllipses.Ellipse(afwEllipses.Axes(nsigma*a, nsigma*b, theta), 

319 geom.Point2D(xcen, ycen)) 

320 fpEllipse = afwDetection.Footprint(ellipse) 

321 

322 sumI = 0.0 

323 sumR = (0.38259771140356325/ab*(1 + math.sqrt(2)*math.hypot(math.fmod(xcen, 1), math.fmod(ycen, 1))) 

324 * objImg.image[int(xcen), int(ycen), afwImage.LOCAL]) 

325 

326 gal = objImg.image 

327 

328 c, s = math.cos(theta), math.sin(theta) 

329 for sp in fpEllipse.getSpans(): 

330 y, x0, x1 = sp.getY(), sp.getX0(), sp.getX1() 

331 

332 for x in range(x0, x1 + 1): 

333 dx, dy = x - xcen, y - ycen 

334 u = c*dx + s*dy 

335 v = -s*dx + c*dy 

336 

337 r = math.hypot(u, v*ab) 

338 try: 

339 val = gal[x, y, afwImage.LOCAL] 

340 except Exception: 

341 continue 

342 

343 sumI += val 

344 sumR += val*r 

345 

346 R_K = sumR/sumI 

347 

348 sumI = 0.0 

349 for y in range(self.height): 

350 for x in range(self.width): 

351 dx, dy = x - xcen, y - ycen 

352 u = c*dx + s*dy 

353 v = -s*dx + c*dy 

354 if math.hypot(u/a, v/b) < kfac: 

355 sumI += gal[x, y, afwImage.LOCAL] 

356 

357 return R_K, sumI, 0, False, False, False 

358 

359 def testEllipticalGaussian(self): 

360 """Test measuring the Kron quantities of an elliptical Gaussian. 

361 """ 

362 ignoreTestFailures = False # if True, keep going after test failures but always generate a failure 

363 # 

364 # Choose function that does the measuring 

365 # 

366 if False: # testing only; requires XY0 == (0, 0) 

367 measureKron = self.measureKronInPython 

368 else: 

369 measureKron = self.measureKron 

370 # 

371 # Make and the objects 

372 # 

373 ab_vals = (0.5, 1.0, 2.0, 3.0, 5.0, ) 

374 nIter = 2 

375 for dx in (0.0, 0.5, ): 

376 for dy in (0.0, 0.5, ): 

377 if measureKron == self.measureKronInPython and dx + dy != 0.0: 

378 continue 

379 

380 for theta in (0.0, 20.0, 45.0, ): 

381 for a in ab_vals: 

382 for b in ab_vals: 

383 if b > a: 

384 continue 

385 

386 makeImage = True 

387 for kfac in (1.5, 2.5, ): # multiple of R_Kron to use for Flux_Kron 

388 R_K, flux_K, fluxErr_K, flags_K, flags_radius, flags_small_radius = ( 

389 self.makeAndMeasure(measureKron, a, b, theta, dx=dx, dy=dy, kfac=kfac, 

390 nIterForRadius=nIter, makeImage=makeImage)) 

391 makeImage = False 

392 

393 self.assertFalse(flags_radius) 

394 self.assertFalse(flags_small_radius) 

395 # 

396 # We'll have to correct for the pixelisation as we sum over the central 

397 # few pixels when making models, mostly do deal with b ~ 0.5 models. 

398 # 

399 # See Section 5 of 

400 # http://www.astro.princeton.edu/~rhl/photomisc/aperture.pdf 

401 # for the source of 0.00286 etc. 

402 # 

403 R_truth0 = math.sqrt(math.pi/2) 

404 R_truth = R_truth0*math.sqrt(1 + 0.8*1/(12.0*a*b)) 

405 

406 flux_truth = self.flux*(1 - math.exp(-0.5*(kfac*R_truth)**2)) 

407 R_truth = R_truth0*math.sqrt(a*b + 1/12.0*(1 + 0.00286/min(a, b)**3.9)) 

408 

409 failR = math.isnan(R_K) or flags_K or ( 

410 abs(R_truth - R_K) > 1e-2*self.getTolRad(a, b)) 

411 failFlux = math.isnan(flux_K) or flags_K or ( 

412 abs(flux_K/flux_truth - 1) > 1e-2*self.getTolFlux(a, b, kfac)) 

413 

414 ID = ("a,b,theta %4.1f %4.1f %4.1f dx,dy = %.1f,%.1f kfac=%g" % 

415 (a, b, theta, dx, dy, kfac)) 

416 if ((failR or failFlux) and verbose) or verbose > 1: 

417 print("%s R_K %10.3f %10.3f %6.3f pixels (tol %5.3f)%s" % 

418 (ID, R_K, R_truth, (R_K - R_truth), 1e-2*self.getTolRad(a, b), 

419 " *" if failR else "")) 

420 print("%s flux_K %10.3f %10.3f %6.2f%% (tol %5.3f) %s" % 

421 (ID, flux_K, flux_truth, 

422 100*(flux_K/flux_truth - 1), self.getTolFlux(a, b, kfac), 

423 " *" if failFlux else "")) 

424 

425 if ignoreTestFailures: 

426 continue 

427 

428 self.assertFalse(failR, (("%s R_Kron: %g v. exact value %g " 

429 "(error %.3f pixels; limit %.3f)") % 

430 (ID, R_K, R_truth, (R_K - R_truth), 

431 1e-2*self.getTolRad(a, b)))) 

432 

433 self.assertFalse(failFlux, 

434 (("%s flux_Kron: %g v. exact value %g " 

435 "(error %.2f%% limit %.2f%%)") % 

436 (ID, flux_K, flux_truth, 100*(flux_K/flux_truth - 1), 

437 self.getTolFlux(a, b, kfac)))) 

438 

439 self.assertFalse(ignoreTestFailures, "You are ignoring possible test failures") 

440 

441 def testBadFlags(self): 

442 a, b, theta = 5, 1, 20.0 

443 # 

444 # Check that we fail if too close to the edge of the image 

445 # 

446 for cen in (10, 20, 38, 40, 50, self.width - 10): 

447 makeImage = True 

448 for kfac in (2.5, 10,): 

449 R_K, flux_K, fluxErr_K, flags_K, flags_radius, flags_small_radius = ( 

450 self.makeAndMeasure(self.measureKron, a, b, theta, makeImage=makeImage, 

451 xcen=cen, ycen=cen, kfac=kfac)) 

452 makeImage = False 

453 

454 msg = "KronFlux_flag_bad_radius: cen = (%g, %g), kfac = %g" % (cen, cen, kfac) 

455 

456 if kfac == 2.5 and (cen <= 20 or cen > self.width - 20): 

457 self.assertTrue(flags_K, msg) 

458 elif kfac == 10: 

459 self.assertTrue(flags_K, msg) 

460 else: 

461 self.assertFalse(flags_K, msg) 

462 

463 def getTolRad(self, a, b): 

464 """Return R_K tolerance in hundredths of a pixel. 

465 """ 

466 if b <= 0.5: 

467 if a <= 0.5: 

468 tol = 35 

469 elif a <= 2: 

470 tol = 350 

471 else: 

472 tol = 25*a # i.e. 0.25*a 

473 elif b <= 1: 

474 tol = 7.0 

475 else: 

476 tol = 1.0 

477 

478 return tol 

479 

480 def getTolFlux(self, a, b, kfac): 

481 """Return Flux_K tolerance in percent. 

482 """ 

483 if b <= 0.5: 

484 if a <= 0.5: 

485 if kfac > 2: 

486 tol = 5.0 

487 else: 

488 tol = 10.0 

489 elif a <= 1.0: 

490 if kfac <= 1.5: 

491 tol = 10.0 

492 else: 

493 tol = 4.0 

494 else: 

495 if kfac > 2: 

496 tol = 3.0 

497 elif kfac > 1.5: 

498 tol = 5.0 

499 else: 

500 tol = 10.0 

501 elif b <= 1: 

502 if a <= 1: 

503 tol = 2.0 

504 else: 

505 if kfac > 2: 

506 tol = 0.25 

507 elif kfac > 1.5: 

508 tol = 0.5 

509 else: 

510 tol = 1.27 

511 elif b <= 2: 

512 if kfac > 1.5: 

513 tol = 0.1 

514 else: 

515 tol = 0.5 

516 else: 

517 tol = 0.30 

518 

519 return tol 

520 

521 def testForced(self): 

522 """Check that forced photometry works in the presence of rotations and translations. 

523 """ 

524 kfac = 2.5 

525 warper = afwMath.Warper("lanczos4") 

526 a = 13 

527 for axisRatio in (0.25, 1.0): 

528 b = a*axisRatio 

529 for theta in (0, 30, 45): 

530 width, height = 256, 256 

531 center = geom.Point2D(0.5*width, 0.5*height) 

532 original = makeGalaxy(width, height, 1000.0, a, b, theta) 

533 msConfig = makeMeasurementConfig(forced=False, kfac=kfac) 

534 source = measureFree(original, center, msConfig) 

535 algMeta = source.getTable().getMetadata() 

536 self.assertTrue(algMeta.exists('EXT_PHOTOMETRYKRON_KRONFLUX_NRADIUSFORFLUX')) 

537 if source.get("ext_photometryKron_KronFlux_flag"): 

538 continue 

539 

540 angleList = [val*geom.degrees for val in (45, 90)] 

541 scaleList = [1.0, 0.5] 

542 offsetList = [(1.23, 4.56), (12.3, 45.6)] 

543 

544 for angle, scale, offset in itertools.product(angleList, scaleList, offsetList): 

545 dx, dy = offset 

546 pixelScale = original.getWcs().getPixelScale()*scale 

547 cdMatrix = afwGeom.makeCdMatrix(scale=pixelScale, orientation=angle, flipX=True) 

548 wcs = afwGeom.makeSkyWcs(crpix=geom.Point2D(dx, dy), 

549 crval=geom.SpherePoint(0.0, 0.0, geom.degrees), 

550 cdMatrix=cdMatrix) 

551 

552 warped = warper.warpExposure(wcs, original) 

553 # add a Psf if there is none. The new SdssCentroid needs a Psf. 

554 if warped.getPsf() is None: 

555 warped.setPsf(afwDetection.GaussianPsf(11, 11, 0.01)) 

556 msConfig = makeMeasurementConfig(kfac=kfac, forced=True) 

557 forced = measureForced(warped, source, original.getWcs(), msConfig) 

558 algMeta = source.getTable().getMetadata() 

559 self.assertTrue(algMeta.exists('EXT_PHOTOMETRYKRON_KRONFLUX_NRADIUSFORFLUX')) 

560 

561 if display: 

562 disp1 = afwDisplay.Display(frame=1) 

563 disp1.mtv(original, title=self._testMethodName + ": original image") 

564 shape = source.getShape().clone() 

565 xc, yc = source.getCentroid() 

566 radius = source.get("ext_photometryKron_KronFlux_radius") 

567 for r, ct in [(radius, afwDisplay.BLUE), (radius*kfac, afwDisplay.CYAN), ]: 

568 shape.scale(r/shape.getDeterminantRadius()) 

569 disp1.dot(shape, xc, yc, ctype=ct) 

570 disp2 = afwDisplay.Display(frame=2) 

571 disp2.mtv(warped, title=self._testMethodName + ": warped image") 

572 transform = (wcs.linearizeSkyToPixel(source.getCoord(), lsst.geom.degrees) 

573 * original.getWcs().linearizePixelToSky(source.getCoord(), 

574 lsst.geom.degrees)) 

575 shape = shape.transform(transform.getLinear()) 

576 radius = source.get("ext_photometryKron_KronFlux_radius") 

577 xc, yc = wcs.skyToPixel(source.getCoord()) 

578 for r, ct in [(radius, afwDisplay.BLUE), (radius*kfac, afwDisplay.CYAN), ]: 

579 shape.scale(r/shape.getDeterminantRadius()) 

580 disp2.dot(shape, xc, yc, ctype=ct) 

581 try: 

582 self.assertFloatsAlmostEqual(source.get("ext_photometryKron_KronFlux_instFlux"), 

583 forced.get("ext_photometryKron_KronFlux_instFlux"), 

584 rtol=1.0e-3 

585 ) 

586 self.assertFloatsAlmostEqual(source.get("ext_photometryKron_KronFlux_radius"), 

587 scale*forced.get("ext_photometryKron_KronFlux_radius"), 

588 rtol=1.0e-3 

589 ) 

590 self.assertEqual(source.get("ext_photometryKron_KronFlux_flag"), 

591 forced.get("ext_photometryKron_KronFlux_flag") 

592 ) 

593 except Exception: 

594 print(("Failed:", angle, scale, offset, 

595 [(source.get(f), forced.get(f)) for f in 

596 ("ext_photometryKron_KronFlux_instFlux", 

597 "ext_photometryKron_KronFlux_radius", 

598 "ext_photometryKron_KronFlux_flag" 

599 ) 

600 ])) 

601 raise 

602 

603 

604class TestMemory(lsst.utils.tests.MemoryTestCase): 

605 pass 

606 

607 

608def setup_module(module): 

609 lsst.utils.tests.init() 

610 # Enable verbose mode for pytest. The verbose content only appears to the 

611 # user on error or in the JUnit XML. 

612 global verbose 

613 verbose = 2 

614 

615 

616if __name__ == "__main__": 616 ↛ 617line 616 didn't jump to line 617, because the condition on line 616 was never true

617 from argparse import ArgumentParser 

618 parser = ArgumentParser() 

619 parser.add_argument("--display", default=False, action="store_true", help="Activate display?") 

620 parser.add_argument("--verbose", type=int, default=0, help="Verbosity level") 

621 parser.add_argument('unittest_args', nargs='*') 

622 args = parser.parse_args() 

623 display = args.display 

624 verbose = args.verbose 

625 sys.argv[1:] = args.unittest_args 

626 lsst.utils.tests.init() 

627 unittest.main()