Coverage for tests/test_Transform.py: 31%

107 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-02-02 06:51 -0800

1# This file is part of meas_base. 

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 numpy as np 

25 

26from lsst.afw.geom import makeSkyWcs 

27import lsst.afw.image as afwImage 

28import lsst.afw.table as afwTable 

29import lsst.daf.base as dafBase 

30import lsst.meas.base as measBase 

31import lsst.pex.config as pexConfig 

32import lsst.pex.exceptions as pexExcept 

33import lsst.utils.tests 

34 

35import testLib 

36 

37try: 

38 type(verbose) 

39except NameError: 

40 verbose = 0 

41 

42 

43def makeWcs(): 

44 """Provide a simple WCS for use in testing. 

45 """ 

46 # The parameters given are placeholders; their values are unimportant 

47 md = dafBase.PropertySet() 

48 md.set("NAXIS", 2) 

49 md.set("CTYPE1", "RA---TAN") 

50 md.set("CTYPE2", "DEC--TAN") 

51 md.set("CRPIX1", 0) 

52 md.set("CRPIX2", 0) 

53 md.set("CRVAL1", 0) 

54 md.set("CRVAL2", 0) 

55 md.set("RADESYS", "FK5") 

56 md.set("EQUINOX", 2000.0) 

57 return makeSkyWcs(md) 

58 

59 

60@pexConfig.wrap(testLib.SillyCentroidControl) 

61class SillyCentroidConfig(pexConfig.Config): 

62 pass 

63 

64 

65class TransformTestCase(lsst.utils.tests.TestCase): 

66 pluginName = "base_SillyCentroid" 

67 centroidPosition = (1.0, -1.0) 

68 

69 def _generateCatalog(self): 

70 """Returns a SourceCatalog with one entry""" 

71 schema = afwTable.SourceTable.makeMinimalSchema() 

72 schema.addField(self.pluginName + "_x", type=np.float64) 

73 schema.addField(self.pluginName + "_y", type=np.float64) 

74 cat = afwTable.SourceCatalog(schema) 

75 source = cat.addNew() 

76 source.set(self.pluginName + "_x", self.centroidPosition[0]) 

77 source.set(self.pluginName + "_y", self.centroidPosition[1]) 

78 return cat 

79 

80 def _performTransform(self, transformClass, inCat, doExtend=True): 

81 """Operate on ``inCat`` with a transform of class ``transformClass``. 

82 """ 

83 mapper = afwTable.SchemaMapper(inCat.schema) 

84 config = SillyCentroidConfig() 

85 transform = transformClass(config, self.pluginName, mapper) 

86 outCat = afwTable.BaseCatalog(mapper.getOutputSchema()) 

87 if doExtend: 

88 outCat.extend(inCat, mapper=mapper) 

89 transform(inCat, outCat, makeWcs(), afwImage.PhotoCalib()) 

90 return outCat 

91 

92 def _checkSillyOutputs(self, inCat, outCat): 

93 """Check that ``outCat`` matches ``inCat`` under `SillyTransform`. 

94 

95 SillyTransform looks for fields named ``name_x`` and ``name_y``; it 

96 copies them to the output, and adds ``name_reverse_x`` and ``_y`` 

97 which are equal to the corresponding inputs multiplied by -1. 

98 """ 

99 for inSrc, outSrc in zip(inCat, outCat): 

100 # The source x, y should have been copied to the output table 

101 self.assertEqual(outSrc[self.pluginName + "_x"], inSrc[self.pluginName + "_x"]) 

102 self.assertEqual(outSrc[self.pluginName + "_y"], inSrc[self.pluginName + "_y"]) 

103 

104 # And the reversed position added 

105 self.assertEqual(outSrc[self.pluginName + "_reverse_x"], -1.0 * inSrc[self.pluginName + "_x"]) 

106 self.assertEqual(outSrc[self.pluginName + "_reverse_y"], -1.0 * inSrc[self.pluginName + "_y"]) 

107 

108 # Other entries from the source table have not been copied 

109 for name in ("id", "coord_ra", "coord_dec", "parent"): 

110 self.assertIn(name, inCat.schema) 

111 self.assertNotIn(name, outCat.schema) 

112 

113 def testNullTransform(self): 

114 """The `NullTransform` passes through nothing. 

115 """ 

116 inCat = self._generateCatalog() 

117 with self.assertRaises(pexExcept.LengthError): 

118 self._performTransform(measBase.NullTransform, inCat, False) 

119 outCat = self._performTransform(measBase.NullTransform, inCat) 

120 self.assertEqual(len(inCat), len(outCat)) 

121 self.assertEqual(outCat.schema.getFieldCount(), 0) 

122 

123 def testPassThroughTransform(self): 

124 """Copies all fields starting with the plugin name. 

125 """ 

126 inCat = self._generateCatalog() 

127 with self.assertRaises(pexExcept.LengthError): 

128 self._performTransform(measBase.PassThroughTransform, inCat, False) 

129 outCat = self._performTransform(measBase.PassThroughTransform, inCat) 

130 self.assertEqual(len(inCat), len(outCat)) 

131 for inSrc, outSrc in zip(inCat, outCat): 

132 for fieldname in inCat.schema.extract(self.pluginName + "*"): 

133 self.assertEqual(inSrc.get(fieldname), outSrc.get(fieldname)) 

134 

135 def testPythonConfig(self): 

136 """Python configs are converted to Control when using a C++ transform. 

137 """ 

138 inCat = self._generateCatalog() 

139 with self.assertRaises(pexExcept.LengthError): 

140 self._performTransform(testLib.SillyTransform, inCat, False) 

141 outCat = self._performTransform(testLib.SillyTransform, inCat) 

142 self._checkSillyOutputs(inCat, outCat) 

143 

144 def testApplyCppTransform(self): 

145 """Test that we can apply a simple C++ transform. 

146 """ 

147 inCat = self._generateCatalog() 

148 sillyControl = testLib.SillyCentroidControl() 

149 mapper = afwTable.SchemaMapper(inCat.schema) 

150 sillyTransform = testLib.SillyTransform(sillyControl, self.pluginName, mapper) 

151 outCat = afwTable.BaseCatalog(mapper.getOutputSchema()) 

152 outCat.extend(inCat, mapper=mapper) 

153 self.assertEqual(len(inCat), len(outCat)) 

154 sillyTransform(inCat, outCat, makeWcs(), afwImage.PhotoCalib()) 

155 self._checkSillyOutputs(inCat, outCat) 

156 

157 

158class AlgorithmConfigurationTestCase(lsst.utils.tests.TestCase): 

159 

160 def testDefaultTransform(self): 

161 """By default, we perform no transformations. 

162 """ 

163 self.assertEqual(measBase.BasePlugin.getTransformClass(), measBase.PassThroughTransform) 

164 

165 def testWrapAlgorithm(self): 

166 """Test the appropriate transform is provided for wrapped algorithms. 

167 """ 

168 # By default, we inherit from BasePlugin 

169 # NB the choice of algorithm and executionOrder is arbitrary 

170 singleFrame, forced = measBase.wrapSimpleAlgorithm(measBase.PsfFluxAlgorithm, 

171 Control=measBase.PsfFluxControl, 

172 executionOrder=measBase.BasePlugin.FLUX_ORDER, 

173 doRegister=False) 

174 self.assertEqual(singleFrame.getTransformClass(), measBase.BasePlugin.getTransformClass()) 

175 # Unless we override 

176 singleFrame, forced = measBase.wrapSimpleAlgorithm(measBase.PsfFluxAlgorithm, 

177 Control=measBase.PsfFluxControl, 

178 executionOrder=measBase.BasePlugin.FLUX_ORDER, 

179 doRegister=False, 

180 TransformClass=measBase.PassThroughTransform) 

181 self.assertEqual(singleFrame.getTransformClass(), measBase.PassThroughTransform) 

182 

183 

184class TestMemory(lsst.utils.tests.MemoryTestCase): 

185 pass 

186 

187 

188def setup_module(module): 

189 lsst.utils.tests.init() 

190 

191 

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

193 lsst.utils.tests.init() 

194 unittest.main()