Coverage for tests/test_calcFunctors.py: 29%

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

58 statements  

1# This file is part of analysis_drp. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

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 GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21 

22import unittest 

23 

24import galsim 

25import numpy as np 

26 

27import lsst.utils.tests 

28from lsst.afw.geom import Quadrupole 

29from lsst.analysis.drp.calcFunctors import CalcE, CalcE1, CalcE2, CalcShapeSize 

30 

31 

32class ShapeSizeTestCase(lsst.utils.tests.TestCase): 

33 """Test ellipiticity and size calculations.""" 

34 

35 @classmethod 

36 def setUpClass(cls): 

37 cls.data = np.array( 

38 [ 

39 (1.3, 1.3, 0.0), # e1 = e2 = 0 

40 (2.4, 1.2, 0.6), # e1 = e2 != 0 

41 (1.0, 2.0, 0.0), # e1 < 0; e2 = 0 

42 (3.5, 3.5, 0.5), # e1 = 0; e2 > 0 

43 (3.0, 1.5, -1.2), # e1 > 0; e2 < 0 

44 ], 

45 dtype=[("ixx", "<f8"), ("iyy", "<f8"), ("ixy", "<f8")], 

46 ) 

47 

48 def test_size(self): 

49 """Test CalcShapeSize functor""" 

50 traceSize = CalcShapeSize(sizeType="trace")(self.data) 

51 determinantSize = CalcShapeSize(sizeType="determinant")(self.data) 

52 

53 for idx, row in enumerate(self.data): 

54 shape = Quadrupole(ixx=row["ixx"], iyy=row["iyy"], ixy=row["ixy"]) 

55 self.assertFloatsAlmostEqual(traceSize[idx], shape.getTraceRadius(), rtol=1e-8) 

56 self.assertFloatsAlmostEqual(determinantSize[idx], shape.getDeterminantRadius(), rtol=1e-8) 

57 # Arithmetic mean >= Geometric mean implies that 

58 # trace radius is never smaller than determinant radius. 

59 self.assertGreaterEqual(traceSize[idx], determinantSize[idx]) 

60 

61 def test_complex_shear(self): 

62 """Test CalcE functor 

63 

64 Test that our ellipticity calculation under the two conventions are 

65 accurate by comparing with GalSim routines. 

66 """ 

67 shear = CalcE(ellipticityType="epsilon")(self.data) 

68 distortion = CalcE(ellipticityType="chi")(self.data) 

69 size = CalcShapeSize(sizeType="determinant")(self.data) 

70 for idx, row in enumerate(self.data): 

71 galsim_shear = galsim.Shear(shear[idx]) 

72 self.assertFloatsAlmostEqual(distortion[idx].real, galsim_shear.e1) 

73 self.assertFloatsAlmostEqual(distortion[idx].imag, galsim_shear.e2) 

74 # Check that the ellipiticity values correspond to the moments. 

75 A = galsim_shear.getMatrix() * size[idx] 

76 M = np.dot(A.transpose(), A) 

77 self.assertFloatsAlmostEqual(M[0, 0], row["ixx"], rtol=1e-8) 

78 self.assertFloatsAlmostEqual(M[1, 1], row["iyy"], rtol=1e-8) 

79 self.assertFloatsAlmostEqual(M[0, 1], row["ixy"], rtol=1e-8) 

80 

81 def test_halve_angle(self): 

82 """Test ``halvePhaseAngle`` parameter in CalcE 

83 

84 Test that setting ``halvePhaseAngle`` to True halves the phase angle 

85 while keeping the magnitude the same. 

86 """ 

87 ellip = CalcE(ellipticityType="epsilon")(self.data) 

88 ellip_half = CalcE(ellipticityType="epsilon", halvePhaseAngle=True)(self.data) 

89 self.assertFloatsAlmostEqual(np.abs(ellip), np.abs(ellip_half)) 

90 

91 for idx, row in enumerate(self.data): 

92 galsim_shear = galsim.Shear(ellip[idx]) 

93 galsim_shear_half = galsim.Shear(ellip_half[idx]) 

94 self.assertFloatsAlmostEqual(np.abs(ellip_half[idx]), galsim_shear.g) 

95 self.assertFloatsAlmostEqual( 

96 galsim_shear.beta / galsim.radians, 2 * galsim_shear_half.beta / galsim.radians 

97 ) 

98 

99 @lsst.utils.tests.methodParameters(ellipticityType=("chi", "epsilon")) 

100 def test_shear_components(self, ellipticityType): 

101 """Test CalcE1 and CalcE2 functors 

102 

103 This test checks if CalcE1 and CalcE2 correspond to the real and 

104 imaginary components of CalcE. 

105 """ 

106 ellip = CalcE(ellipticityType=ellipticityType)(self.data) 

107 e1 = CalcE1(ellipticityType=ellipticityType)(self.data) 

108 e2 = CalcE2(ellipticityType=ellipticityType)(self.data) 

109 

110 self.assertFloatsAlmostEqual(np.real(ellip), e1) 

111 self.assertFloatsAlmostEqual(np.imag(ellip), e2) 

112 

113 def test_e1_validation(self): 

114 """Test that CalcE1 throws an exception when misconfigured.""" 

115 CalcE1(ellipticityType="chi", colXy=None).validate() 

116 with self.assertRaises(ValueError): 

117 CalcE1(ellipticityType="epsilon", colXy=None).validate() 

118 

119 def test_size_validation(self): 

120 """Test that CalcShapeSize throws an exception when misconfigured.""" 

121 CalcShapeSize(sizeType="trace", colXy=None).validate() 

122 with self.assertRaises(ValueError): 

123 CalcShapeSize(sizeType="determinant", colXy=None).validate() 

124 

125 

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

127 lsst.utils.tests.init() 

128 unittest.main()