Coverage for tests / test_objectSizeStarSelector.py: 19%

92 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-14 23:55 +0000

1# This file is part of meas_algorithms. 

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 

23import numpy as np 

24 

25import lsst.afw.table as afwTable 

26from lsst.meas.algorithms import sourceSelector, ObjectSizeNoSourcesError, \ 

27 ObjectSizeNoGoodSourcesError 

28import lsst.meas.base.tests 

29import lsst.utils.tests 

30 

31fluxField = "base_GaussianFlux_instFlux" 

32 

33 

34class TestObjectSizeSourceSelector(lsst.utils.tests.TestCase): 

35 

36 def addGoodSource(self, sourceCat, num=0): 

37 """Insert a likely-good source into the catalog. 

38 

39 Parameters 

40 ---------- 

41 sourceCat : `lsst.afw.table.SourceCatalog` 

42 The source catalog for which a "good" source is to be added for testing. 

43 num : `float` or `int` 

44 A number that is added to various values to distinguish them in catalogs with multiple objects. 

45 """ 

46 sourceCat.addNew() 

47 sourceCat["coord_ra"][-1] = 1.0 + num 

48 sourceCat["coord_dec"][-1] = 2.0 + num 

49 # Add some variability to the fluxes to form a cluster with non-finite "width" in the mag-size plane 

50 fluxFactor = self.rng.uniform(low=0.98, high=1.02) 

51 sourceCat[fluxField][-1] = 100.0*fluxFactor # We set fluxMin = 50 in setUp 

52 sourceCat[fluxField + "Err"][-1] = 1.0 

53 # Add some variability to the shapes to form a cluster with non-finite "width" in the mag-size plane 

54 widthFactor = self.rng.uniform(low=0.98, high=1.02) 

55 sourceCat["truth_xx"][-1] = 3.0*widthFactor 

56 sourceCat["truth_yy"][-1] = 3.0*widthFactor 

57 sourceCat["truth_xy"][-1] = 1.0 

58 

59 def setUp(self): 

60 self.rng = np.random.Generator(np.random.MT19937(1)) 

61 

62 self.sourceSelector = sourceSelector.sourceSelectorRegistry["objectSize"]() 

63 self.badFlags = self.sourceSelector.config.badFlags 

64 schema = lsst.meas.base.tests.TestDataset.makeMinimalSchema() 

65 self.sourceSelector.config.sourceFluxField = fluxField 

66 self.sourceSelector.config.fluxMin = 50.0 

67 schema.addField(fluxField, type=np.float64) 

68 schema.addField(fluxField + "Err", type=np.float64) 

69 for flag in self.badFlags: 

70 schema.addField(flag, type="Flag") 

71 self.sourceCat = afwTable.SourceCatalog(schema) 

72 

73 def testSelectSourcesGood(self): 

74 for i in range(5): 

75 self.addGoodSource(self.sourceCat, i) 

76 result = self.sourceSelector.selectSources(self.sourceCat) 

77 for src in self.sourceCat["id"]: 

78 self.assertIn(src, self.sourceCat[result.selected]["id"]) 

79 

80 def testSelectSourcesIndividualBadFlags(self): 

81 for i in range(len(self.badFlags)): 

82 self.addGoodSource(self.sourceCat, i) 

83 for i in range(len(self.badFlags)): 

84 for j, flag in enumerate(self.badFlags): 

85 self.sourceCat[j].set(flag, True) if i == j else self.sourceCat[j].set(flag, False) 

86 result = self.sourceSelector.selectSources(self.sourceCat) 

87 self.assertNotIn(self.sourceCat[i]["id"], self.sourceCat[result.selected]["id"], 

88 "should not have found %s" % flag) 

89 

90 def testSelectSourcesAllBadFlags(self): 

91 for i, flag in enumerate(self.badFlags): 

92 self.addGoodSource(self.sourceCat, i) 

93 self.sourceCat[i].set(flag, True) 

94 with self.assertRaisesRegex(ObjectSizeNoGoodSourcesError, 

95 f"out of {len(self.badFlags)} input sources."): 

96 self.sourceSelector.selectSources(self.sourceCat) 

97 

98 def testEmptyInputCatalog(self): 

99 """Test that an appropriate error is raised for an empty input catalog. 

100 """ 

101 with self.assertRaises(ObjectSizeNoSourcesError): 

102 self.sourceSelector.selectSources(self.sourceCat) 

103 

104 def testSelectSourcesSignalToNoiseCuts(self): 

105 for i in range(10): 

106 self.addGoodSource(self.sourceCat, i) 

107 

108 self.sourceCat[fluxField + "Err"][0] = self.sourceCat[fluxField][0]/20.0 

109 self.sourceCat[fluxField + "Err"][1] = self.sourceCat[fluxField][1]/500.0 

110 self.sourceCat[fluxField + "Err"][2] = self.sourceCat[fluxField][2]/2000.0 

111 

112 self.sourceSelector.config.signalToNoiseMax = 1000 

113 result = self.sourceSelector.run(self.sourceCat) 

114 self.assertNotIn(self.sourceCat[0]["id"], result.sourceCat["id"]) 

115 self.assertIn(self.sourceCat[1]["id"], result.sourceCat["id"]) 

116 self.assertNotIn(self.sourceCat[2]["id"], result.sourceCat["id"]) 

117 

118 def testSelectSourcesNoSignalToNoiseCut(self): 

119 for i in range(5): 

120 self.addGoodSource(self.sourceCat, i) 

121 self.sourceCat[fluxField + "Err"][0] = self.sourceCat[fluxField][0]*1e+9 # S/N ~1e-9 

122 self.sourceSelector.config.signalToNoiseMin = 0 

123 result = self.sourceSelector.run(self.sourceCat) 

124 self.assertIn(self.sourceCat[0]["id"], result.sourceCat["id"]) 

125 

126 def testSelectSourcesFluxCuts(self): 

127 for i in range(10): 

128 self.addGoodSource(self.sourceCat, i) 

129 

130 self.sourceSelector.config.doSignalToNoiseLimit = False 

131 self.sourceSelector.config.doFluxLimit = True 

132 self.sourceSelector.config.fluxMin = 98.0 # Just outside of range allowed inself.addGoodSource() 

133 self.sourceCat[fluxField][0] = 97.9 

134 self.sourceSelector.config.fluxMax = 101.3 # Just outside of range allowed inself.addGoodSource() 

135 self.sourceCat[fluxField][2] = 101.4 

136 result = self.sourceSelector.run(self.sourceCat) 

137 self.assertNotIn(self.sourceCat[0]["id"], result.sourceCat["id"]) 

138 self.assertIn(self.sourceCat[1]["id"], result.sourceCat["id"]) 

139 self.assertNotIn(self.sourceCat[2]["id"], result.sourceCat["id"]) 

140 

141 

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

143 pass 

144 

145 

146def setup_module(module): 

147 lsst.utils.tests.init() 

148 

149 

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

151 lsst.utils.tests.init() 

152 unittest.main()