Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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 lsst.pex.config 

23import lsst.pipe.base 

24import lsst.coadd.utils 

25import lsst.afw.table 

26 

27 

28from .forcedPhotImage import ForcedPhotImageConfig, ForcedPhotImageTask, ForcedPhotImageConnections 

29 

30 

31__all__ = ("ForcedPhotCoaddConfig", "ForcedPhotCoaddTask") 

32 

33 

34class ForcedPhotCoaddConfig(ForcedPhotImageConfig, pipelineConnections=ForcedPhotImageConnections): 

35 footprintDatasetName = lsst.pex.config.Field( 

36 doc="Dataset (without coadd prefix) that should be used to obtain (Heavy)Footprints for sources. " 

37 "Must have IDs that match those of the reference catalog." 

38 "If None, Footprints will be generated by transforming the reference Footprints.", 

39 dtype=str, 

40 default="meas", 

41 optional=True 

42 ) 

43 

44 hasFakes = lsst.pex.config.Field( 

45 dtype=bool, 

46 default=False, 

47 doc="Should be set to True if fake sources have been inserted into the input data." 

48 ) 

49 

50 def setDefaults(self): 

51 ForcedPhotImageTask.ConfigClass.setDefaults(self) 

52 # Copy 'id' and 'parent' columns without renaming them; since these are 

53 # the only IDs we have in coadd processing, there's no need to qualify 

54 # them with 'object'. 

55 self.measurement.copyColumns["id"] = "id" 

56 self.measurement.copyColumns["parent"] = "parent" 

57 self.references.removePatchOverlaps = False # see validate() for why 

58 self.measurement.plugins.names |= ['base_InputCount', 'base_Variance'] 

59 self.measurement.plugins['base_PixelFlags'].masksFpAnywhere = ['CLIPPED', 'SENSOR_EDGE', 

60 'REJECTED', 'INEXACT_PSF'] 

61 self.measurement.plugins['base_PixelFlags'].masksFpCenter = ['CLIPPED', 'SENSOR_EDGE', 

62 'REJECTED', 'INEXACT_PSF'] 

63 

64 def validate(self): 

65 ForcedPhotImageTask.ConfigClass.validate(self) 

66 if (self.measurement.doReplaceWithNoise and self.footprintDatasetName is not None 

67 and self.references.removePatchOverlaps): 

68 raise ValueError("Cannot use removePatchOverlaps=True with deblended footprints, as parent " 

69 "sources may be rejected while their children are not.") 

70 

71 

72class ForcedPhotCoaddRunner(lsst.pipe.base.ButlerInitializedTaskRunner): 

73 """Get the psfCache setting into ForcedPhotCoaddTask""" 

74 @staticmethod 

75 def getTargetList(parsedCmd, **kwargs): 

76 return lsst.pipe.base.ButlerInitializedTaskRunner.getTargetList(parsedCmd, 

77 psfCache=parsedCmd.psfCache) 

78 

79 

80class ForcedPhotCoaddTask(ForcedPhotImageTask): 

81 """A command-line driver for performing forced measurement on coadd images. 

82 

83 Notes 

84 ----- 

85 In addition to the run method, `ForcedPhotCcdTask` overrides several 

86 methods of `ForcedPhotImageTask` to specialize it for coadd processing, 

87 including `~ForcedPhotImageTask.makeIdFactory` and 

88 `~ForcedPhotImageTask.fetchReferences`. None of these should be called 

89 directly by the user, though it may be useful to override them further in 

90 subclasses. 

91 """ 

92 

93 ConfigClass = ForcedPhotCoaddConfig 

94 RunnerClass = lsst.pipe.base.ButlerInitializedTaskRunner 

95 _DefaultName = "forcedPhotCoadd" 

96 dataPrefix = "deepCoadd_" 

97 

98 def getExposure(self, dataRef): 

99 

100 if self.config.hasFakes: 

101 name = "fakes_" + self.config.coaddName + "Coadd_calexp" 

102 else: 

103 name = self.config.coaddName + "Coadd_calexp" 

104 

105 return dataRef.get(name) if dataRef.datasetExists(name) else None 

106 

107 def makeIdFactory(self, dataRef): 

108 """Create an object that generates globally unique source IDs. 

109 

110 Source IDs are created based on a per-CCD ID and the ID of the CCD 

111 itself. 

112 

113 Parameters 

114 ---------- 

115 dataRef : `lsst.daf.persistence.ButlerDataRef` 

116 Butler data reference. The "CoaddId_bits" and "CoaddId" datasets 

117 are accessed. The data ID must have tract and patch keys. 

118 """ 

119 # With the default configuration, this IdFactory doesn't do anything, 

120 # because the IDs it generates are immediately overwritten by the ID 

121 # from the reference catalog (since that's in 

122 # config.measurement.copyColumns). But we create one here anyway, to 

123 # allow us to revert back to the old behavior of generating new forced 

124 # source IDs, just by renaming the ID in config.copyColumns to 

125 # "object_id". 

126 expBits = dataRef.get(self.config.coaddName + "CoaddId_bits") 

127 expId = int(dataRef.get(self.config.coaddName + "CoaddId")) 

128 return lsst.afw.table.IdFactory.makeSource(expId, 64 - expBits) 

129 

130 def getExposureId(self, dataRef): 

131 return int(dataRef.get(self.config.coaddName + "CoaddId")) 

132 

133 def fetchReferences(self, dataRef, exposure): 

134 """Return an iterable of reference sources which overlap the exposure. 

135 

136 Parameters 

137 ---------- 

138 dataRef : `lsst.daf.persistence.ButlerDataRef` 

139 Butler data reference corresponding to the image to be measured; 

140 should have tract, patch, and filter keys. 

141 

142 exposure : `lsst.afw.image.Exposure` 

143 Unused. 

144 

145 Notes 

146 ----- 

147 All work is delegated to the references subtask; see 

148 `CoaddSrcReferencesTask` for information about the default behavior. 

149 """ 

150 skyMap = dataRef.get(self.dataPrefix + "skyMap", immediate=True) 

151 tractInfo = skyMap[dataRef.dataId["tract"]] 

152 patch = tuple(int(v) for v in dataRef.dataId["patch"].split(",")) 

153 patchInfo = tractInfo.getPatchInfo(patch) 

154 references = lsst.afw.table.SourceCatalog(self.references.schema) 

155 references.extend(self.references.fetchInPatches(dataRef, patchList=[patchInfo])) 

156 return references 

157 

158 def attachFootprints(self, sources, refCat, exposure, refWcs, dataRef): 

159 r"""Attach Footprints to source records. 

160 

161 For coadd forced photometry, we use the deblended "heavy" 

162 `~lsst.afw.detection.Footprint`\ s from the single-band measurements 

163 of the same band - because we've guaranteed that the peaks (and hence 

164 child sources) will be consistent across all bands before we get to 

165 measurement, this should yield reasonable deblending for most sources. 

166 It's most likely limitation is that it will not provide good flux 

167 upper limits for sources that were not detected in this band but were 

168 blended with sources that were. 

169 """ 

170 if self.config.footprintDatasetName is None: 

171 return ForcedPhotImageTask.attachFootprints(self, sources, refCat, exposure, refWcs, dataRef) 

172 self.log.info("Loading deblended footprints for sources from %s, %s" % 

173 (self.config.footprintDatasetName, dataRef.dataId)) 

174 fpCat = dataRef.get("%sCoadd_%s" % (self.config.coaddName, self.config.footprintDatasetName), 

175 immediate=True) 

176 for refRecord, srcRecord in zip(refCat, sources): 

177 fpRecord = fpCat.find(refRecord.getId()) 

178 if fpRecord is None: 

179 raise LookupError("Cannot find Footprint for source %s; please check that %sCoadd_%s " 

180 "IDs are compatible with reference source IDs" % 

181 (srcRecord.getId(), self.config.coaddName, 

182 self.config.footprintDatasetName)) 

183 srcRecord.setFootprint(fpRecord.getFootprint()) 

184 

185 @classmethod 

186 def _makeArgumentParser(cls): 

187 parser = lsst.pipe.base.ArgumentParser(name=cls._DefaultName) 

188 parser.add_id_argument("--id", "deepCoadd_forced_src", help="data ID, with raw CCD keys + tract", 

189 ContainerClass=lsst.coadd.utils.CoaddDataIdContainer) 

190 parser.add_argument("--psfCache", type=int, default=100, help="Size of CoaddPsf cache") 

191 return parser