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 return afwImage.Mask.getPlaneBitMask(self.config.badMaskPlanes)
113 """Constructs SkyInfo used by coaddition tasks for multiple
118 skyMap : `lsst.skyMap.SkyMap`
122 patchId : `str` or `int` or `tuple` of `int`
123 Either Gen2-style comma delimited string (e.g. '4,5'),
124 tuple of integers (e.g (4, 5), Gen3-style integer.
128 makeSkyInfo : `lsst.pipe.base.Struct`
129 pipe_base Struct with attributes:
132 Sky map (`lsst.skyMap.SkyMap`).
134 Information for chosen tract of sky map (`lsst.skyMap.TractInfo`).
136 Information about chosen patch of tract (`lsst.skyMap.PatchInfo`).
138 WCS of tract (`lsst.afw.image.SkyWcs`).
140 Outer bbox of patch, as an geom Box2I (`lsst.afw.geom.Box2I`).
142 tractInfo = skyMap[tractId]
144 if isinstance(patchId, str)
and ',' in patchId:
146 patchIndex = tuple(int(i)
for i
in patchId.split(
","))
150 patchInfo = tractInfo.getPatchInfo(patchIndex)
152 return pipeBase.Struct(
156 wcs=tractInfo.getWcs(),
157 bbox=patchInfo.getOuterBBox(),
162 """Match the order of one list to another, padding if necessary
167 List to be reordered and padded. Elements can be any type.
168 inputKeys : `iterable`
169 Iterable of values to be compared with outputKeys. Length must match `inputList`.
170 outputKeys : `iterable`
171 Iterable of values to be compared with inputKeys.
173 Any value to be inserted where inputKey not in outputKeys.
178 Copy of inputList reordered per outputKeys and padded with `padWith`
179 so that the length matches length of outputKeys.
184 outputList.append(inputList[inputKeys.index(d)])
186 outputList.append(padWith)
191 """Reorder inputRefs per outputSortKeyOrder.
193 Any inputRefs which are lists will be resorted per specified key e.g.,
194 'detector.' Only iterables will be reordered, and values can be of type
195 `lsst.pipe.base.connections.DeferredDatasetRef` or
196 `lsst.daf.butler.core.datasets.ref.DatasetRef`.
198 Returned lists of refs have the same length as the outputSortKeyOrder.
199 If an outputSortKey not in the inputRef, then it will be padded with None.
200 If an inputRef contains an inputSortKey that is not in the
201 outputSortKeyOrder it will be removed.
205 inputRefs : `lsst.pipe.base.connections.QuantizedConnection`
206 Input references to be reordered and padded.
207 outputSortKeyOrder : `iterable`
208 Iterable of values to be compared with inputRef's dataId[dataIdKey].
210 The data ID key in the dataRefs to compare with the outputSortKeyOrder.
214 inputRefs : `lsst.pipe.base.connections.QuantizedConnection`
215 Quantized Connection with sorted DatasetRef values sorted if iterable.
217 for connectionName, refs
in inputRefs:
218 if isinstance(refs, Iterable):
219 if hasattr(refs[0],
"dataId"):
220 inputSortKeyOrder = [ref.dataId[dataIdKey]
for ref
in refs]
222 inputSortKeyOrder = [handle.datasetRef.dataId[dataIdKey]
for handle
in refs]
223 if inputSortKeyOrder != outputSortKeyOrder:
224 setattr(inputRefs, connectionName,
230 """Iterate over subregions of a bbox.
234 bbox : `lsst.geom.Box2I`
235 Bounding box over which to iterate.
236 subregionSize : `lsst.geom.Extent2I`
241 subBBox : `lsst.geom.Box2I`
242 Next sub-bounding box of size ``subregionSize`` or smaller; each ``subBBox``
243 is contained within ``bbox``, so it may be smaller than ``subregionSize`` at
244 the edges of ``bbox``, but it will never be empty.
249 Raised if any of the following occur:
250 - The given bbox is empty.
251 - The subregionSize is 0.
254 raise RuntimeError(
"bbox %s is empty" % (bbox,))
255 if subregionSize[0] < 1
or subregionSize[1] < 1:
256 raise RuntimeError(
"subregionSize %s must be nonzero" % (subregionSize,))
258 for rowShift
in range(0, bbox.getHeight(), subregionSize[1]):
259 for colShift
in range(0, bbox.getWidth(), subregionSize[0]):
262 if subBBox.isEmpty():
263 raise RuntimeError(
"Bug: empty bbox! bbox=%s, subregionSize=%s, "
264 "colShift=%s, rowShift=%s" %
265 (bbox, subregionSize, colShift, rowShift))
275 """Grow coaddInputs' ccds' ValidPolygons in place.
277 Either modify each ccd's validPolygon in place, or if CoaddInputs
278 does not have a validPolygon, create one from its bbox.
282 coaddInputs : `lsst.afw.image.coaddInputs`
283 CoaddInputs object containing the ccds to grow the valid polygons of.
285 The value to grow the valid polygons by.
289 Negative values for ``growBy`` can shrink the polygons.
291 for ccd
in coaddInputs.ccds:
292 polyOrig = ccd.getValidPolygon()
293 validPolyBBox = polyOrig.getBBox()
if polyOrig
else ccd.getBBox()
294 validPolyBBox.grow(growBy)
296 validPolygon = polyOrig.intersectionSingle(validPolyBBox)
298 validPolygon = Polygon(
geom.Box2D(validPolyBBox))
300 ccd.validPolygon = validPolygon
304 mask: afwImage.Mask, mask_planes: Iterable, logger: Logger |
None =
None
306 """Unset the mask of an image for mask planes specified in the config.
310 mask : `lsst.afw.image.Mask`
311 The mask to be modified.
313 The list of mask planes to be removed.
314 logger : `logging.Logger`, optional
315 Logger to log messages.
317 for maskPlane
in mask_planes:
319 mask &= ~mask.getPlaneBitMask(maskPlane)
320 except InvalidParameterError:
323 "Unable to remove mask plane %s: no mask plane with that name was found.",
329 """Map certain mask planes of the warps to new planes for the coadd.
331 If a pixel is rejected due to a mask value other than EDGE, NO_DATA,
332 or CLIPPED, set it to REJECTED on the coadd.
333 If a pixel is rejected due to EDGE, set the coadd pixel to SENSOR_EDGE.
334 If a pixel is rejected due to CLIPPED, set the coadd pixel to CLIPPED.
338 statsCtrl : `lsst.afw.math.StatisticsControl`
339 Statistics control object for coadd.
343 maskMap : `list` of `tuple` of `int`
344 A list of mappings of mask planes of the warped exposures to
345 mask planes of the coadd.
347 edge = 2 ** afwImage.Mask.addMaskPlane(
"EDGE")
348 noData = 2 ** afwImage.Mask.addMaskPlane(
"NO_DATA")
349 clipped = 2 ** afwImage.Mask.addMaskPlane(
"CLIPPED")
350 toReject = statsCtrl.getAndMask() & (~noData) & (~edge) & (~clipped)
352 (toReject, 2 ** afwImage.Mask.addMaskPlane(
"REJECTED")),
353 (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)