Coverage for tests/test_kron.py: 9%
390 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-16 11:28 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-16 11:28 +0000
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
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
44try:
45 display
46except NameError:
47 display = False
49afwDisplay.setDefaultMaskTransparency(75)
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)
62 if xy0 is not None:
63 gal.setXY0(xy0)
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]
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))
84 if val < 0:
85 val = 0
86 gal[geom.Point2I(x, y), afwImage.LOCAL] = val/nsample**2
88 ii += val
89 iuu += val*u**2
90 ivv += val*v**2
92 iuu /= ii
93 ivv /= ii
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
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
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
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]
167class KronPhotometryTestCase(lsst.utils.tests.TestCase):
168 """A test case for measuring Kron quantities.
169 """
171 def setUp(self):
172 self.flux = 1e5
173 self.width, self.height = 200, 200
174 self.objImg = None
176 def tearDown(self):
177 if self.objImg:
178 del self.objImg
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
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))
227 return measureKron(self.objImg, xcen, ycen, nsigma, kfac, nIterForRadius, disp=currentDisp)
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'))
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
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)
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"))
286 def measureKronInPython(self, objImg, xcen, ycen, nsigma, kfac, nIterForRadius, makeImage=None):
287 """Measure the Kron quantities of an elliptical Gaussian in python.
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'))
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)
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])
326 gal = objImg.image
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()
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
337 r = math.hypot(u, v*ab)
338 try:
339 val = gal[x, y, afwImage.LOCAL]
340 except Exception:
341 continue
343 sumI += val
344 sumR += val*r
346 R_K = sumR/sumI
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]
357 return R_K, sumI, 0, False, False, False
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
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
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
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))
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))
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))
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 ""))
425 if ignoreTestFailures:
426 continue
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))))
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))))
439 self.assertFalse(ignoreTestFailures, "You are ignoring possible test failures")
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
454 msg = "KronFlux_flag_bad_radius: cen = (%g, %g), kfac = %g" % (cen, cen, kfac)
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)
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
478 return tol
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
519 return tol
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
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)]
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)
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'))
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
604class TestMemory(lsst.utils.tests.MemoryTestCase):
605 pass
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
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()