Coverage for python/lsst/faro/measurement/DetectorMeasurement.py: 30%

63 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-03-20 05:20 -0700

1# This file is part of faro. 

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 lsst.pipe.base as pipeBase 

23import lsst.pex.config as pexConfig 

24 

25from lsst.faro.base.CatalogMeasurementBase import ( 

26 CatalogMeasurementBaseConnections, 

27 CatalogMeasurementBaseConfig, 

28 CatalogMeasurementBaseTask, 

29) 

30 

31__all__ = ("DetectorMeasurementConfig", "DetectorMeasurementTask") 

32 

33 

34class DetectorMeasurementConnections( 

35 CatalogMeasurementBaseConnections, 

36 dimensions=("instrument", "visit", "detector", "band"), 

37 # TODO: remove deprecated templates on DM-39854. 

38 defaultTemplates={ 

39 "photoCalibName": "calexp.photoCalib", 

40 "externalPhotoCalibName": "fgcm", 

41 "wcsName": "calexp.wcs", 

42 "externalWcsName": "gbdesAstrometricFit", 

43 }, 

44 deprecatedTemplates={ 

45 "photoCalibName": "Deprecated in favor of visitSummary; will be removed after v26.", 

46 "externalPhotoCalibName": "Deprecated in favor of visitSummary; will be removed after v26.", 

47 "wcsName": "Deprecated in favor of visitSummary; will be removed after v26.", 

48 "externalWcsName": "Deprecated in favor of visitSummary; will be removed after v26.", 

49 }, 

50): 

51 

52 catalog = pipeBase.connectionTypes.Input( 

53 doc="Source catalog.", 

54 dimensions=("instrument", "visit", "detector", "band"), 

55 storageClass="SourceCatalog", 

56 name="src", 

57 ) 

58 visitSummary = pipeBase.connectionTypes.Input( 

59 doc="Exposure catalog with WCS and PhotoCalib this detector+visit combination.", 

60 dimensions=("instrument", "visit"), 

61 storageClass="ExposureCatalog", 

62 name="finalVisitSummary", 

63 ) 

64 skyWcs = pipeBase.connectionTypes.Input( 

65 doc="WCS for the catalog.", 

66 dimensions=("instrument", "visit", "detector", "band"), 

67 storageClass="Wcs", 

68 name="{wcsName}", 

69 # TODO: remove on DM-39854. 

70 deprecated="Deprecated in favor of visitSummary and already ignored; will be removed after v26." 

71 ) 

72 photoCalib = pipeBase.connectionTypes.Input( 

73 doc="Photometric calibration object.", 

74 dimensions=("instrument", "visit", "detector", "band"), 

75 storageClass="PhotoCalib", 

76 name="{photoCalibName}", 

77 # TODO: remove on DM-39854. 

78 deprecated="Deprecated in favor of visitSummary and already ignored; will be removed after v26." 

79 ) 

80 externalSkyWcsTractCatalog = pipeBase.connectionTypes.Input( 

81 doc=( 

82 "Per-tract, per-visit wcs calibrations. These catalogs use the detector " 

83 "id for the catalog id, sorted on id for fast lookup." 

84 ), 

85 name="{externalWcsName}SkyWcsCatalog", 

86 storageClass="ExposureCatalog", 

87 dimensions=("instrument", "visit", "tract"), 

88 # TODO: remove on DM-39854. 

89 deprecated="Deprecated in favor of visitSummary; will be removed after v26." 

90 ) 

91 externalSkyWcsGlobalCatalog = pipeBase.connectionTypes.Input( 

92 doc=( 

93 "Per-visit wcs calibrations computed globally (with no tract information). " 

94 "These catalogs use the detector id for the catalog id, sorted on id for " 

95 "fast lookup." 

96 ), 

97 name="{externalWcsName}SkyWcsCatalog", 

98 storageClass="ExposureCatalog", 

99 dimensions=("instrument", "visit"), 

100 # TODO: remove on DM-39854. 

101 deprecated="Deprecated in favor of visitSummary; will be removed after v26." 

102 ) 

103 externalPhotoCalibTractCatalog = pipeBase.connectionTypes.Input( 

104 doc=( 

105 "Per-tract, per-visit photometric calibrations. These catalogs use the " 

106 "detector id for the catalog id, sorted on id for fast lookup." 

107 ), 

108 name="{externalPhotoCalibName}PhotoCalibCatalog", 

109 storageClass="ExposureCatalog", 

110 dimensions=("instrument", "visit", "tract"), 

111 # TODO: remove on DM-39854. 

112 deprecated="Deprecated in favor of visitSummary; will be removed after v26." 

113 ) 

114 externalPhotoCalibGlobalCatalog = pipeBase.connectionTypes.Input( 

115 doc=( 

116 "Per-visit photometric calibrations computed globally (with no tract " 

117 "information). These catalogs use the detector id for the catalog id, " 

118 "sorted on id for fast lookup." 

119 ), 

120 name="{externalPhotoCalibName}PhotoCalibCatalog", 

121 storageClass="ExposureCatalog", 

122 dimensions=("instrument", "visit"), 

123 # TODO: remove on DM-39854. 

124 deprecated="Deprecated in favor of visitSummary; will be removed after v26." 

125 ) 

126 measurement = pipeBase.connectionTypes.Output( 

127 doc="Per-detector measurement.", 

128 dimensions=("instrument", "visit", "detector", "band"), 

129 storageClass="MetricValue", 

130 name="metricvalue_{package}_{metric}", 

131 ) 

132 

133 def __init__(self, *, config=None): 

134 super().__init__(config=config) 

135 # TODO: remove references to deprecates things after DM-39854 (may 

136 # allow the __init__ override to go away entirely). 

137 if config.doApplyExternalSkyWcs: 

138 if config.useGlobalExternalSkyWcs: 

139 self.inputs.remove("externalSkyWcsTractCatalog") 

140 else: 

141 self.inputs.remove("externalSkyWcsGlobalCatalog") 

142 else: 

143 self.inputs.remove("externalSkyWcsTractCatalog") 

144 self.inputs.remove("externalSkyWcsGlobalCatalog") 

145 if config.doApplyExternalPhotoCalib: 

146 if config.useGlobalExternalPhotoCalib: 

147 self.inputs.remove("externalPhotoCalibTractCatalog") 

148 else: 

149 self.inputs.remove("externalPhotoCalibGlobalCatalog") 

150 else: 

151 self.inputs.remove("externalPhotoCalibTractCatalog") 

152 self.inputs.remove("externalPhotoCalibGlobalCatalog") 

153 del self.skyWcs 

154 del self.photoCalib 

155 

156 

157class DetectorMeasurementConfig( 

158 CatalogMeasurementBaseConfig, pipelineConnections=DetectorMeasurementConnections 

159): 

160 doApplyExternalSkyWcs = pexConfig.Field( 

161 doc="Whether or not to use the external wcs.", dtype=bool, default=False, 

162 # TODO: remove on DM-39854. 

163 deprecated="Deprecated in favor of the visitSummary connection; will be removed after v26." 

164 ) 

165 useGlobalExternalSkyWcs = pexConfig.Field( 

166 doc="Whether or not to use the global external wcs.", dtype=bool, default=False, 

167 # TODO: remove on DM-39854. 

168 deprecated="Deprecated in favor of the visitSummary connection; will be removed after v26." 

169 ) 

170 doApplyExternalPhotoCalib = pexConfig.Field( 

171 doc="Whether or not to use the external photoCalib.", dtype=bool, default=False, 

172 # TODO: remove on DM-39854. 

173 deprecated="Deprecated in favor of the visitSummary connection; will be removed after v26." 

174 ) 

175 useGlobalExternalPhotoCalib = pexConfig.Field( 

176 doc="Whether or not to use the global external photoCalib.", 

177 dtype=bool, 

178 default=False, 

179 # TODO: remove on DM-39854. 

180 deprecated="Deprecated in favor of the visitSummary connection; will be removed after v26." 

181 ) 

182 

183 

184class DetectorMeasurementTask(CatalogMeasurementBaseTask): 

185 ConfigClass = DetectorMeasurementConfig 

186 _DefaultName = "detectorMeasurementTask" 

187 

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

189 inputs = butlerQC.get(inputRefs) 

190 visitSummary = inputs.pop("visitSummary") 

191 detector = inputRefs.catalog.dataId["detector"] 

192 row = visitSummary.find(detector) 

193 inputs["photoCalib"] = row.getPhotoCalib() 

194 inputs["skyWcs"] = row.getSkyWcs() 

195 # TODO: significant simplification should be possible here on DM-39854. 

196 if self.config.doApplyExternalPhotoCalib: 

197 if self.config.useGlobalExternalPhotoCalib: 

198 externalPhotoCalibCatalog = inputs.pop( 

199 "externalPhotoCalibGlobalCatalog" 

200 ) 

201 else: 

202 externalPhotoCalibCatalog = inputs.pop("externalPhotoCalibTractCatalog") 

203 row = externalPhotoCalibCatalog.find(detector) 

204 externalPhotoCalib = None if row is None else row.getPhotoCalib() 

205 inputs["photoCalib"] = externalPhotoCalib 

206 if self.config.doApplyExternalSkyWcs: 

207 if self.config.useGlobalExternalSkyWcs: 

208 externalSkyWcsCatalog = inputs.pop("externalSkyWcsGlobalCatalog") 

209 else: 

210 externalSkyWcsCatalog = inputs.pop("externalSkyWcsTractCatalog") 

211 row = externalSkyWcsCatalog.find(detector) 

212 externalSkyWcs = None if row is None else row.getWcs() 

213 inputs["skyWcs"] = externalSkyWcs 

214 

215 outputs = self.run(**inputs) 

216 if outputs.measurement is not None: 

217 butlerQC.put(outputs, outputRefs) 

218 else: 

219 self.log.debug( 

220 "Skipping measurement of {!r} on {} " "as not applicable.", 

221 self, 

222 inputRefs, 

223 )