Coverage for python / lsst / ap / association / ssSingleFrameAssociation.py: 55%

40 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-05-06 08:53 +0000

1# 

2# LSST Data Management System 

3# Copyright 2008-2016 AURA/LSST. 

4# 

5# This product includes software developed by the 

6# LSST Project (http://www.lsst.org/). 

7# 

8# This program is free software: you can redistribute it and/or modify 

9# it under the terms of the GNU General Public License as published by 

10# the Free Software Foundation, either version 3 of the License, or 

11# (at your option) any later version. 

12# 

13# This program is distributed in the hope that it will be useful, 

14# but WITHOUT ANY WARRANTY; without even the implied warranty of 

15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the LSST License Statement and 

19# the GNU General Public License along with this program. If not, 

20# see <https://www.lsstcorp.org/LegalNotices/>. 

21# 

22 

23"""PipelineTask for associating single-frame sources with solar system objects. 

24 

25""" 

26 

27__all__ = ("SsSingleFrameAssociationConfig", 

28 "SsSingleFrameAssociationTask", 

29 "SsSingleFrameAssociationConnections") 

30from astropy.units import deg 

31 

32import lsst.pex.config as pexConfig 

33import lsst.pipe.base as pipeBase 

34import lsst.pipe.base.connectionTypes as connTypes 

35from lsst.pipe.tasks.ssoAssociation import SolarSystemAssociationTask 

36from lsst.utils.timer import timeMethod 

37 

38 

39class SsSingleFrameAssociationConnections( 

40 pipeBase.PipelineTaskConnections, 

41 dimensions=("instrument", "visit", "detector"), 

42 defaultTemplates={"fakesType": ""}): 

43 """Butler connections for SsSingleFrameAssociationTask. 

44 """ 

45 exposure = connTypes.Input( 

46 doc="Exposure from which source table was generated", 

47 name="initial_pvi", 

48 storageClass="ExposureF", 

49 dimensions=("instrument", "visit", "detector"), 

50 ) 

51 sourceTable = connTypes.Input( 

52 doc="Catalog of calibrated Sources.", 

53 name="initial_stars_footprints_detector", 

54 storageClass="SourceCatalog", 

55 dimensions=("instrument", "visit", "detector"), 

56 ) 

57 solarSystemObjectTable = connTypes.Input( 

58 doc="Optional catalog of SolarSolarSystem objects expected to be" 

59 "observable in this detectorVisit.", 

60 name="preloaded_SsObjects", 

61 storageClass="ArrowAstropy", 

62 dimensions=("instrument", "group", "detector"), 

63 minimum=0, 

64 ) 

65 associatedSsSources = connTypes.Output( 

66 doc="ssSource record columns which can be computed as of association", 

67 name="ssSingleFrameAssociatedSources", 

68 storageClass="ArrowAstropy", 

69 dimensions=("instrument", "visit", "detector"), 

70 ) 

71 unassociatedSsObjects = connTypes.Output( 

72 doc="Expected locations of an ssObject with no source", 

73 name="ssSingleFrameUnassociatedObjects", 

74 storageClass="ArrowAstropy", 

75 dimensions=("instrument", "visit", "detector"), 

76 ) 

77 

78 

79class SsSingleFrameAssociationConfig(pipeBase.PipelineTaskConfig, 

80 pipelineConnections=SsSingleFrameAssociationConnections): 

81 """Config for DiaPipelineTask. 

82 """ 

83 solarSystemAssociator = pexConfig.ConfigurableField( 

84 target=SolarSystemAssociationTask, 

85 doc="Task used to associate DiaSources with SolarSystemObjects.", 

86 ) 

87 imagePixelMargin = pexConfig.RangeField( 

88 dtype=int, 

89 default=10, 

90 min=0, 

91 doc="Pad the image by this many pixels before removing off-image " 

92 "diaObjects for association.", 

93 ) 

94 

95 

96class SsSingleFrameAssociationTask(pipeBase.PipelineTask): 

97 """Task for loading, associating and storing Difference Image Analysis 

98 (DIA) Objects and Sources. 

99 """ 

100 ConfigClass = SsSingleFrameAssociationConfig 

101 _DefaultName = "ssSingleFrameAssociation" 

102 

103 def __init__(self, initInputs=None, **kwargs): 

104 super().__init__(**kwargs) 

105 self.makeSubtask("solarSystemAssociator") 

106 

107 def runQuantum(self, butlerQC, inputRefs, outputRefs): 

108 inputs = butlerQC.get(inputRefs) 

109 inputs["band"] = butlerQC.quantum.dataId["band"] 

110 outputs = self.run(**inputs) 

111 

112 butlerQC.put(outputs, outputRefs) 

113 

114 @timeMethod 

115 def run(self, 

116 exposure, 

117 sourceTable, 

118 band, 

119 solarSystemObjectTable=None): 

120 """Process DiaSources and DiaObjects. 

121 

122 Load previous DiaObjects and their DiaSource history. Calibrate the 

123 values in the diaSourceCat. Associate new DiaSources with previous 

124 DiaObjects. Run forced photometry at the updated DiaObject locations. 

125 Store the results in the Alert Production Database (Apdb). 

126 

127 Parameters 

128 ---------- 

129 exposure : `lsst.afw.image.ExposureF` 

130 Calibrated exposure with wcs and midpoint time. 

131 sourceTable : `lsst.afw.table.SourceCatalog` 

132 Newly detected sources. 

133 band : `str` 

134 The band in which the new DiaSources were detected. 

135 solarSystemObjectTable : `astropy.table.Table` or `None` 

136 Preloaded Solar System objects expected to be visible in the image. 

137 

138 Returns 

139 ------- 

140 results : `lsst.pipe.base.Struct` 

141 Results struct with components. 

142 - ``ssoAssocDiaSources`` : DiaSources that were associated with 

143 solar system objects in this visit. (`Astropy.table.Table`) 

144 - ``unAssocDiaSources`` : Set of DiaSources that were not 

145 associated with any solar system object. (`astropy.table.Table`) 

146 - ``nTotalSsObjects`` : Total number of SolarSystemObjects 

147 contained in the CCD footprint. (`int`) 

148 - ``nAssociatedSsObjects`` : Number of SolarSystemObjects 

149 that were associated with DiaSources. 

150 - ``ssSourceData`` : ssSource table data. (`Astropy.table.Table`) 

151 

152 

153 Raises 

154 ------ 

155 RuntimeError 

156 Raised if duplicate DiaObjects or duplicate DiaSources are found. 

157 """ 

158 if solarSystemObjectTable is None: 

159 raise pipeBase.NoWorkFound("No ephemerides to associate. Skipping ssSingleFrameAssociation.") 

160 

161 # Associate DiaSources with DiaObjects 

162 sourceTable = sourceTable.asAstropy() 

163 sourceTable['ra'] = sourceTable['coord_ra'].to(deg).value 

164 sourceTable['dec'] = sourceTable['coord_dec'].to(deg).value 

165 sourceTable['midpointMjdTai'] = exposure.visitInfo.date.toAstropy().tai.mjd 

166 sourceTable['band'] = exposure.getFilter().bandLabel 

167 colNames = ['base_ClassificationSizeExtendedness_value', 

168 'base_PsfFlux_instFlux', 

169 'base_PsfFlux_instFluxErr', 

170 ] 

171 newNames = ["extendedness", 

172 "psfFlux", 

173 "psfFluxErr", 

174 ] 

175 sourceTable.rename_columns(colNames, newNames) 

176 return self.solarSystemAssociator.run(sourceTable, solarSystemObjectTable, 

177 exposure.visitInfo, exposure.getBBox(), exposure.wcs)