23from __future__
import annotations
25__all__ = [
"CoaddBaseTask",
"makeSkyInfo"]
27from collections.abc
import Iterable
28from typing
import TYPE_CHECKING
33import lsst.pipe.base
as pipeBase
37from .coaddInputRecorder
import CoaddInputRecorderTask
38from .selectImages
import PsfWcsSelectImagesTask
41 from logging
import Logger
47 """Configuration parameters for CoaddBaseTask
49 Configuration parameters shared between MakeCoaddTempExp and AssembleCoadd
52 coaddName = pexConfig.Field(
53 doc=
"Coadd name: typically one of deep or goodSeeing.",
57 select = pexConfig.ConfigurableField(
58 doc=
"Image selection subtask.",
59 target=PsfWcsSelectImagesTask,
61 badMaskPlanes = pexConfig.ListField(
63 doc=
"Mask planes that, if set, the associated pixel should not be included in the coaddTempExp.",
66 inputRecorder = pexConfig.ConfigurableField(
67 doc=
"Subtask that helps fill CoaddInputs catalogs added to the final Exposure",
68 target=CoaddInputRecorderTask,
71 includeCalibVar = pexConfig.Field(
73 doc=
"Add photometric calibration variance to warp variance plane.",
75 deprecated=
"Deprecated and ignored. Will be removed after v29.",
80 """Base class for coaddition.
82 Subclasses must specify _DefaultName
85 ConfigClass = CoaddBaseConfig
89 self.makeSubtask(
"select")
90 self.makeSubtask(
"inputRecorder")
93 """Return warp name for given warpType and task config
98 Either 'direct' or 'psfMatched'.
102 WarpDatasetName : `str`
104 return self.config.coaddName +
"Coadd_" + warpType +
"Warp"
107 """Convenience method to provide the bitmask from the mask plane names
109 badMaskPlanes = [mp
for mp
in self.config.badMaskPlanes
110 if mp
in afwImage.Mask().getMaskPlaneDict().keys()]
111 return afwImage.Mask.getPlaneBitMask(badMaskPlanes)
115 """Constructs SkyInfo used by coaddition tasks for multiple
120 skyMap : `lsst.skyMap.SkyMap`
124 patchId : `str` or `int` or `tuple` of `int`
125 Either Gen2-style comma delimited string (e.g. '4,5'),
126 tuple of integers (e.g (4, 5), Gen3-style integer.
130 makeSkyInfo : `lsst.pipe.base.Struct`
131 pipe_base Struct with attributes:
134 Sky map (`lsst.skyMap.SkyMap`).
136 Information for chosen tract of sky map (`lsst.skyMap.TractInfo`).
138 Information about chosen patch of tract (`lsst.skyMap.PatchInfo`).
140 WCS of tract (`lsst.afw.image.SkyWcs`).
142 Outer bbox of patch, as an geom Box2I (`lsst.afw.geom.Box2I`).
144 tractInfo = skyMap[tractId]
146 if isinstance(patchId, str)
and ',' in patchId:
148 patchIndex = tuple(int(i)
for i
in patchId.split(
","))
152 patchInfo = tractInfo.getPatchInfo(patchIndex)
154 return pipeBase.Struct(
158 wcs=tractInfo.getWcs(),
159 bbox=patchInfo.getOuterBBox(),
164 """Match the order of one list to another, padding if necessary
169 List to be reordered and padded. Elements can be any type.
170 inputKeys : `iterable`
171 Iterable of values to be compared with outputKeys. Length must match `inputList`.
172 outputKeys : `iterable`
173 Iterable of values to be compared with inputKeys.
175 Any value to be inserted where inputKey not in outputKeys.
180 Copy of inputList reordered per outputKeys and padded with `padWith`
181 so that the length matches length of outputKeys.
186 outputList.append(inputList[inputKeys.index(d)])
188 outputList.append(padWith)
193 """Reorder inputRefs per outputSortKeyOrder.
195 Any inputRefs which are lists will be resorted per specified key e.g.,
196 'detector.' Only iterables will be reordered, and values can be of type
197 `lsst.pipe.base.connections.DeferredDatasetRef` or
198 `lsst.daf.butler.core.datasets.ref.DatasetRef`.
200 Returned lists of refs have the same length as the outputSortKeyOrder.
201 If an outputSortKey not in the inputRef, then it will be padded with None.
202 If an inputRef contains an inputSortKey that is not in the
203 outputSortKeyOrder it will be removed.
207 inputRefs : `lsst.pipe.base.connections.QuantizedConnection`
208 Input references to be reordered and padded.
209 outputSortKeyOrder : `iterable`
210 Iterable of values to be compared with inputRef's dataId[dataIdKey].
212 The data ID key in the dataRefs to compare with the outputSortKeyOrder.
216 inputRefs : `lsst.pipe.base.connections.QuantizedConnection`
217 Quantized Connection with sorted DatasetRef values sorted if iterable.
219 for connectionName, refs
in inputRefs:
220 if isinstance(refs, Iterable):
221 if hasattr(refs[0],
"dataId"):
222 inputSortKeyOrder = [ref.dataId[dataIdKey]
for ref
in refs]
224 inputSortKeyOrder = [handle.datasetRef.dataId[dataIdKey]
for handle
in refs]
225 if inputSortKeyOrder != outputSortKeyOrder:
226 setattr(inputRefs, connectionName,
232 """Iterate over subregions of a bbox.
236 bbox : `lsst.geom.Box2I`
237 Bounding box over which to iterate.
238 subregionSize : `lsst.geom.Extent2I`
243 subBBox : `lsst.geom.Box2I`
244 Next sub-bounding box of size ``subregionSize`` or smaller; each ``subBBox``
245 is contained within ``bbox``, so it may be smaller than ``subregionSize`` at
246 the edges of ``bbox``, but it will never be empty.
251 Raised if any of the following occur:
252 - The given bbox is empty.
253 - The subregionSize is 0.
256 raise RuntimeError(
"bbox %s is empty" % (bbox,))
257 if subregionSize[0] < 1
or subregionSize[1] < 1:
258 raise RuntimeError(
"subregionSize %s must be nonzero" % (subregionSize,))
260 for rowShift
in range(0, bbox.getHeight(), subregionSize[1]):
261 for colShift
in range(0, bbox.getWidth(), subregionSize[0]):
264 if subBBox.isEmpty():
265 raise RuntimeError(
"Bug: empty bbox! bbox=%s, subregionSize=%s, "
266 "colShift=%s, rowShift=%s" %
267 (bbox, subregionSize, colShift, rowShift))
277 """Grow coaddInputs' ccds' ValidPolygons in place.
279 Either modify each ccd's validPolygon in place, or if CoaddInputs
280 does not have a validPolygon, create one from its bbox.
284 coaddInputs : `lsst.afw.image.coaddInputs`
285 CoaddInputs object containing the ccds to grow the valid polygons of.
287 The value to grow the valid polygons by.
291 Negative values for ``growBy`` can shrink the polygons.
293 for ccd
in coaddInputs.ccds:
294 polyOrig = ccd.getValidPolygon()
295 validPolyBBox = polyOrig.getBBox()
if polyOrig
else ccd.getBBox()
296 validPolyBBox.grow(growBy)
298 validPolygon = polyOrig.intersectionSingle(validPolyBBox)
300 validPolygon = Polygon(
geom.Box2D(validPolyBBox))
302 ccd.validPolygon = validPolygon
306 mask: afwImage.Mask, mask_planes: Iterable, logger: Logger |
None =
None
308 """Unset the mask of an image for mask planes specified in the config.
312 mask : `lsst.afw.image.Mask`
313 The mask to be modified.
315 The list of mask planes to be removed.
316 logger : `logging.Logger`, optional
317 Logger to log messages.
319 for maskPlane
in mask_planes:
321 mask &= ~mask.getPlaneBitMask(maskPlane)
322 except InvalidParameterError:
325 "Unable to remove mask plane %s: no mask plane with that name was found.",
331 """Map certain mask planes of the warps to new planes for the coadd.
333 If a pixel is rejected due to a mask value other than EDGE, NO_DATA,
334 or CLIPPED, set it to REJECTED on the coadd.
335 If a pixel is rejected due to EDGE, set the coadd pixel to SENSOR_EDGE.
336 If a pixel is rejected due to CLIPPED, set the coadd pixel to CLIPPED.
340 statsCtrl : `lsst.afw.math.StatisticsControl`
341 Statistics control object for coadd.
345 maskMap : `list` of `tuple` of `int`
346 A list of mappings of mask planes of the warped exposures to
347 mask planes of the coadd.
349 edge = 2 ** afwImage.Mask.addMaskPlane(
"EDGE")
350 noData = 2 ** afwImage.Mask.addMaskPlane(
"NO_DATA")
351 clipped = 2 ** afwImage.Mask.addMaskPlane(
"CLIPPED")
352 toReject = statsCtrl.getAndMask() & (~noData) & (~edge) & (~clipped)
354 (toReject, 2 ** afwImage.Mask.addMaskPlane(
"REJECTED")),
355 (edge, 2 ** afwImage.Mask.addMaskPlane(
"SENSOR_EDGE")),
getTempExpDatasetName(self, warpType="direct")
reorderAndPadList(inputList, inputKeys, outputKeys, padWith=None)
list[tuple[int, int]] setRejectedMaskMapping(StatisticsControl statsCtrl)
makeSkyInfo(skyMap, tractId, patchId)
None growValidPolygons(coaddInputs, int growBy)
subBBoxIter(bbox, subregionSize)
reorderRefs(inputRefs, outputSortKeyOrder, dataIdKey)
removeMaskPlanes(afwImage.Mask mask, Iterable mask_planes, Logger|None logger=None)