Coverage for python/lsst/shapelet/tests.py: 21%

103 statements  

« prev     ^ index     » next       coverage.py v7.5.1, created at 2024-05-16 03:31 -0700

1# 

2# LSST Data Management System 

3# Copyright 2008-2017 LSST Corporation. 

4# 

5# This product includes software developed by the 

6# LSST Project (http://www.lsst.org/). 

7# 

8# This program is free software: you can redistribute it and/or modify 

9# it under the terms of the GNU General Public License as published by 

10# the Free Software Foundation, either version 3 of the License, or 

11# (at your option) any later version. 

12# 

13# This program is distributed in the hope that it will be useful, 

14# but WITHOUT ANY WARRANTY; without even the implied warranty of 

15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the LSST License Statement and 

19# the GNU General Public License along with this program. If not, 

20# see <http://www.lsstcorp.org/LegalNotices/>. 

21# 

22 

23""" 

24Test utility code for shapelets library; here so it can be used 

25in multiple test scripts and tests in downstream packages. 

26""" 

27import numpy 

28try: 

29 import scipy.ndimage 

30except ImportError: 

31 scipy = None 

32 

33import lsst.utils.tests 

34import lsst.geom 

35import lsst.afw.geom.ellipses 

36 

37 

38class ShapeletTestCase(lsst.utils.tests.TestCase): 

39 

40 @staticmethod 

41 def makeUnitVector(i, n): 

42 v = numpy.zeros(n, dtype=float) 

43 v[i] = 1.0 

44 return v 

45 

46 @staticmethod 

47 def makeImage(function, x, y): 

48 z = numpy.zeros((y.size, x.size), dtype=float) 

49 e = function.evaluate() 

50 for i, py in enumerate(y): 

51 for j, px in enumerate(x): 

52 z[i, j] = e(float(px), float(py)) 

53 return z 

54 

55 @staticmethod 

56 def makeRandomShapeletFunction(order=2, zeroCenter=False, ellipse=None, scale=1.0): 

57 center = lsst.geom.Point2D() 

58 if not zeroCenter: 

59 center = lsst.geom.Point2D(numpy.random.randn(), numpy.random.randn()) 

60 if ellipse is None: 

61 ellipse = lsst.afw.geom.ellipses.Ellipse( 

62 lsst.afw.geom.ellipses.Axes( 

63 float(numpy.random.uniform(low=1, high=2)), 

64 float(numpy.random.uniform(low=1, high=2)), 

65 float(numpy.random.uniform(low=0, high=numpy.pi)) 

66 ), 

67 center 

68 ) 

69 coefficients = numpy.random.randn(lsst.shapelet.computeSize(order)) 

70 result = lsst.shapelet.ShapeletFunction(order, lsst.shapelet.HERMITE, coefficients) 

71 result.setEllipse(ellipse) 

72 result.getEllipse().scale(scale) 

73 return result 

74 

75 @staticmethod 

76 def makeRandomMultiShapeletFunction(nComponents=3, ellipse=None): 

77 components = [] 

78 for n in range(nComponents): 

79 components.append(ShapeletTestCase.makeRandomShapeletFunction(ellipse=ellipse)) 

80 return lsst.shapelet.MultiShapeletFunction(components) 

81 

82 def compareShapeletFunctions(self, a, b, rtolEllipse=1E-13, rtolCoeff=1E-13, 

83 atolEllipse=1E-14, atolCoeff=1E-14): 

84 self.assertEqual(a.getOrder(), b.getOrder()) 

85 self.assertEqual(a.getBasisType(), b.getBasisType()) 

86 self.assertFloatsAlmostEqual(a.getEllipse().getParameterVector(), 

87 b.getEllipse().getParameterVector(), 

88 rtol=rtolEllipse, atol=atolEllipse) 

89 self.assertFloatsAlmostEqual(a.getCoefficients(), b.getCoefficients(), 

90 rtol=rtolCoeff, atol=atolCoeff) 

91 

92 def simplifyMultiShapeletFunction(self, msf): 

93 keep = [] 

94 for s in msf.getComponents(): 

95 if not numpy.allclose(s.getCoefficients(), 0.0): 

96 params = tuple(s.getEllipse().getParameterVector()) + tuple(s.getCoefficients()) 

97 keep.append((params, s)) 

98 msf = lsst.shapelet.MultiShapeletFunction() 

99 keep.sort(key=lambda t: numpy.sum(t[0])) 

100 for params, s in keep: 

101 msf.addComponent(s) 

102 return msf 

103 

104 def compareMultiShapeletFunctions(self, a, b, simplify=True, rtolEllipse=1E-13, rtolCoeff=1E-13, 

105 atolEllipse=1E-14, atolCoeff=1E-14): 

106 if simplify: 

107 a = self.simplifyMultiShapeletFunction(a) 

108 b = self.simplifyMultiShapeletFunction(b) 

109 self.assertEqual(len(a.getComponents()), len(b.getComponents())) 

110 for sa, sb in zip(a.getComponents(), b.getComponents()): 

111 self.compareShapeletFunctions(sa, sb, rtolEllipse=rtolEllipse, rtolCoeff=rtolCoeff, 

112 atolEllipse=atolEllipse, atolCoeff=atolCoeff) 

113 

114 def checkMoments(self, function, x, y, z): 

115 gx, gy = numpy.meshgrid(x, y) 

116 m = z.sum() 

117 dipole = lsst.geom.Point2D((gx * z).sum() / m, (gy * z).sum() / m) 

118 gx -= dipole.getX() 

119 gy -= dipole.getY() 

120 quadrupole = lsst.afw.geom.ellipses.Quadrupole( 

121 (gx**2 * z).sum() / m, 

122 (gy**2 * z).sum() / m, 

123 (gx * gy * z).sum() / m 

124 ) 

125 imageMoments = lsst.afw.geom.ellipses.Ellipse(quadrupole, dipole) 

126 shapeletMoments = function.evaluate().computeMoments() 

127 self.assertFloatsAlmostEqual(imageMoments.getCenter().getX(), 

128 shapeletMoments.getCenter().getX(), rtol=1E-3) 

129 self.assertFloatsAlmostEqual(imageMoments.getCenter().getY(), 

130 shapeletMoments.getCenter().getY(), rtol=1E-3) 

131 self.assertFloatsAlmostEqual(imageMoments.getCore().getIxx(), 

132 shapeletMoments.getCore().getIxx(), rtol=1E-3) 

133 self.assertFloatsAlmostEqual(imageMoments.getCore().getIyy(), 

134 shapeletMoments.getCore().getIyy(), rtol=1E-3) 

135 self.assertFloatsAlmostEqual(imageMoments.getCore().getIxy(), 

136 shapeletMoments.getCore().getIxy(), rtol=1E-3) 

137 integral = numpy.trapz(numpy.trapz(z, gx, axis=1), y, axis=0) 

138 self.assertFloatsAlmostEqual(integral, function.evaluate().integrate(), rtol=1E-3) 

139 

140 def checkConvolution(self, f1, f2): 

141 bbox = lsst.geom.Box2I(lsst.geom.Point2I(-50, -50), lsst.geom.Point2I(50, 50)) 

142 i1 = lsst.afw.image.ImageD(bbox) 

143 f1.evaluate().addToImage(i1) 

144 self.assertFloatsAlmostEqual(i1.getArray().sum(), f1.evaluate().integrate(), rtol=1E-3) 

145 i2 = lsst.afw.image.ImageD(bbox) 

146 f2.evaluate().addToImage(i2) 

147 self.assertFloatsAlmostEqual(i2.getArray().sum(), f2.evaluate().integrate(), rtol=1E-3) 

148 fc1 = f1.convolve(f2) 

149 fc2 = f2.convolve(f1) 

150 ic1 = lsst.afw.image.ImageD(bbox) 

151 fc1.evaluate().addToImage(ic1) 

152 ic2 = lsst.afw.image.ImageD(bbox) 

153 fc2.evaluate().addToImage(ic2) 

154 self.assertFloatsAlmostEqual(ic1.getArray(), ic2.getArray()) 

155 out = lsst.afw.image.ImageD(bbox) 

156 if scipy is None: 

157 print("Skipping convolution test; scipy could not be imported.") 

158 return 

159 # I'm using scipy.ndimage to convolve test images, because I can't figure 

160 # out how to make afw do it (afw can convolve images with kernels, but two similarly-sized 

161 # are apparently another matter; if I try to make a FixedKernel from one of the images, 

162 # I can't even make the operation commutative, let alone correct. 

163 scipy.ndimage.convolve(i1.getArray(), i2.getArray(), output=out.getArray(), 

164 mode="constant", cval=0.0) 

165 self.assertFloatsAlmostEqual(out.getArray(), ic1.getArray(), rtol=1E-4, atol=1E-5) 

166 self.assertFloatsAlmostEqual(out.getArray(), ic2.getArray(), rtol=1E-4, atol=1E-5) 

167 return fc1, fc2