Coverage for tests/test_calcFunctors.py: 28%
58 statements
« prev ^ index » next coverage.py v6.4, created at 2022-05-25 12:45 +0000
« prev ^ index » next coverage.py v6.4, created at 2022-05-25 12:45 +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/>.
22import unittest
24import galsim
25import numpy as np
27import lsst.utils.tests
28from lsst.afw.geom import Quadrupole
29from lsst.analysis.drp.calcFunctors import CalcE, CalcE1, CalcE2, CalcShapeSize
32class ShapeSizeTestCase(lsst.utils.tests.TestCase):
33 """Test ellipiticity and size calculations."""
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 )
48 def test_size(self):
49 """Test CalcShapeSize functor"""
50 traceSize = CalcShapeSize(sizeType="trace")(self.data)
51 determinantSize = CalcShapeSize(sizeType="determinant")(self.data)
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])
61 def test_complex_shear(self):
62 """Test CalcE functor
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)
81 def test_halve_angle(self):
82 """Test ``halvePhaseAngle`` parameter in CalcE
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))
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 )
99 @lsst.utils.tests.methodParameters(ellipticityType=("chi", "epsilon"))
100 def test_shear_components(self, ellipticityType):
101 """Test CalcE1 and CalcE2 functors
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)
110 self.assertFloatsAlmostEqual(np.real(ellip), e1)
111 self.assertFloatsAlmostEqual(np.imag(ellip), e2)
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()
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()
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()