Coverage for tests/test_calcFunctors.py: 26%

64 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-02-08 11:40 +0000

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, CalcEDiff, CalcShapeSize 

30from lsst.pex.config import FieldValidationError 

31 

32 

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

34 """Test ellipiticity and size calculations.""" 

35 

36 @classmethod 

37 def setUpClass(cls): 

38 cls.data = np.array( 

39 [ 

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

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

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

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

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

45 ], 

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

47 ) 

48 

49 def test_size(self): 

50 """Test CalcShapeSize functor""" 

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

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

53 

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

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

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

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

58 # Arithmetic mean >= Geometric mean implies that 

59 # trace radius is never smaller than determinant radius. 

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

61 

62 def test_complex_shear(self): 

63 """Test CalcE functor 

64 

65 Test that our ellipticity calculation under the two conventions are 

66 accurate by comparing with GalSim routines. 

67 """ 

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

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

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

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

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

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

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

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

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

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

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

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

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

81 

82 def test_halve_angle(self): 

83 """Test ``halvePhaseAngle`` parameter in CalcE 

84 

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

86 while keeping the magnitude the same. 

87 """ 

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

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

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

91 

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

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

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

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

96 self.assertFloatsAlmostEqual( 

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

98 ) 

99 

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

101 def test_shear_components(self, ellipticityType): 

102 """Test CalcE1 and CalcE2 functors 

103 

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

105 imaginary components of CalcE. 

106 """ 

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

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

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

110 

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

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

113 

114 def test_e1_validation(self): 

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

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

117 with self.assertRaises(FieldValidationError): 

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

119 

120 def test_size_validation(self): 

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

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

123 with self.assertRaises(FieldValidationError): 

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

125 

126 def test_ediff_validation(self): 

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

128 ellipA = CalcE(ellipticityType="epsilon") 

129 ellipB = CalcE(ellipticityType="chi") 

130 with self.assertRaises(FieldValidationError): 

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

132 

133 

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

135 lsst.utils.tests.init() 

136 unittest.main()