Coverage for tests/test_psfResiduals.py: 25%

65 statements  

« prev     ^ index     » next       coverage.py v7.2.5, created at 2023-05-10 10:36 +0000

1# This file is part of analysis_tools. 

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 lsst.utils.tests 

26import numpy as np 

27from lsst.afw.geom import Quadrupole 

28from lsst.analysis.tools.actions.vector import CalcE, CalcE1, CalcE2, CalcEDiff, CalcShapeSize 

29from lsst.pex.config import FieldValidationError 

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=[("i_ixx", "<f8"), ("i_iyy", "<f8"), ("i_ixy", "<f8")], 

46 ) 

47 

48 cls.kwargs = {"band": "i"} 

49 

50 def test_size(self): 

51 """Test CalcShapeSize functor""" 

52 traceSize = CalcShapeSize(sizeType="trace")(self.data, **self.kwargs) 

53 determinantSize = CalcShapeSize(sizeType="determinant")(self.data, **self.kwargs) 

54 

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

56 shape = Quadrupole(ixx=row["i_ixx"], iyy=row["i_iyy"], ixy=row["i_ixy"]) 

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

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

59 # Arithmetic mean >= Geometric mean implies that 

60 # trace radius is never smaller than determinant radius. 

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

62 

63 def test_complex_shear(self): 

64 """Test CalcE functor 

65 

66 Test that our ellipticity calculation under the two conventions are 

67 accurate by comparing with GalSim routines. 

68 """ 

69 shear = CalcE(ellipticityType="shear")(self.data, **self.kwargs) 

70 distortion = CalcE(ellipticityType="distortion")(self.data, **self.kwargs) 

71 size = CalcShapeSize(sizeType="determinant")(self.data, **self.kwargs) 

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

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

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

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

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

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

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

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

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

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

82 

83 def test_halve_angle(self): 

84 """Test ``halvePhaseAngle`` parameter in CalcE 

85 

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

87 while keeping the magnitude the same. 

88 """ 

89 ellip = CalcE(ellipticityType="shear")(self.data, **self.kwargs) 

90 ellip_half = CalcE(ellipticityType="shear", halvePhaseAngle=True)(self.data, **self.kwargs) 

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

92 

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

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

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

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

97 self.assertFloatsAlmostEqual( 

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

99 ) 

100 

101 @lsst.utils.tests.methodParameters(ellipticityType=("distortion", "shear")) 

102 def test_shear_components(self, ellipticityType): 

103 """Test CalcE1 and CalcE2 functors 

104 

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

106 imaginary components of CalcE. 

107 """ 

108 ellip = CalcE(ellipticityType=ellipticityType)(self.data, **self.kwargs) 

109 e1 = CalcE1(ellipticityType=ellipticityType)(self.data, **self.kwargs) 

110 e2 = CalcE2(ellipticityType=ellipticityType)(self.data, **self.kwargs) 

111 

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

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

114 

115 def test_e1_validation(self): 

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

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

118 with self.assertRaises(FieldValidationError): 

119 CalcE1(ellipticityType="shear", colXy=None).validate() 

120 

121 def test_size_validation(self): 

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

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

124 with self.assertRaises(FieldValidationError): 

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

126 

127 def test_ediff_validation(self): 

128 """Test that CalcEDiff takes ellipticities of same convention.""" 

129 ellipA = CalcE(ellipticityType="shear") 

130 ellipB = CalcE(ellipticityType="distortion") 

131 with self.assertRaises(FieldValidationError): 

132 CalcEDiff(colA=ellipA, colB=ellipB).validate() 

133 

134 

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

136 lsst.utils.tests.init() 

137 unittest.main()