Coverage for python/lsst/meas/base/forcedPhotCoadd.py : 34%

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/>.
22import lsst.pex.config
23import lsst.pipe.base
24import lsst.coadd.utils
25import lsst.afw.table
28from .forcedPhotImage import ForcedPhotImageConfig, ForcedPhotImageTask, ForcedPhotImageConnections
31__all__ = ("ForcedPhotCoaddConfig", "ForcedPhotCoaddTask")
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 )
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 )
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']
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.")
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)
80class ForcedPhotCoaddTask(ForcedPhotImageTask):
81 """A command-line driver for performing forced measurement on coadd images.
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 """
93 ConfigClass = ForcedPhotCoaddConfig
94 RunnerClass = lsst.pipe.base.ButlerInitializedTaskRunner
95 _DefaultName = "forcedPhotCoadd"
96 dataPrefix = "deepCoadd_"
98 def getExposure(self, dataRef):
100 if self.config.hasFakes:
101 name = "fakes_" + self.config.coaddName + "Coadd_calexp"
102 else:
103 name = self.config.coaddName + "Coadd_calexp"
105 return dataRef.get(name) if dataRef.datasetExists(name) else None
107 def makeIdFactory(self, dataRef):
108 """Create an object that generates globally unique source IDs.
110 Source IDs are created based on a per-CCD ID and the ID of the CCD
111 itself.
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)
130 def getExposureId(self, dataRef):
131 return int(dataRef.get(self.config.coaddName + "CoaddId"))
133 def fetchReferences(self, dataRef, exposure):
134 """Return an iterable of reference sources which overlap the exposure.
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.
142 exposure : `lsst.afw.image.Exposure`
143 Unused.
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
158 def attachFootprints(self, sources, refCat, exposure, refWcs, dataRef):
159 r"""Attach Footprints to source records.
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())
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