Loading [MathJax]/extensions/tex2jax.js
LSST Applications g0fba68d861+05816baf74,g1ec0fe41b4+f536777771,g1fd858c14a+a9301854fb,g35bb328faa+fcb1d3bbc8,g4af146b050+a5c07d5b1d,g4d2262a081+6e5fcc2a4e,g53246c7159+fcb1d3bbc8,g56a49b3a55+9c12191793,g5a012ec0e7+3632fc3ff3,g60b5630c4e+ded28b650d,g67b6fd64d1+ed4b5058f4,g78460c75b0+2f9a1b4bcd,g786e29fd12+cf7ec2a62a,g8352419a5c+fcb1d3bbc8,g87b7deb4dc+7b42cf88bf,g8852436030+e5453db6e6,g89139ef638+ed4b5058f4,g8e3bb8577d+d38d73bdbd,g9125e01d80+fcb1d3bbc8,g94187f82dc+ded28b650d,g989de1cb63+ed4b5058f4,g9d31334357+ded28b650d,g9f33ca652e+50a8019d8c,gabe3b4be73+1e0a283bba,gabf8522325+fa80ff7197,gb1101e3267+d9fb1f8026,gb58c049af0+f03b321e39,gb665e3612d+2a0c9e9e84,gb89ab40317+ed4b5058f4,gcf25f946ba+e5453db6e6,gd6cbbdb0b4+bb83cc51f8,gdd1046aedd+ded28b650d,gde0f65d7ad+941d412827,ge278dab8ac+d65b3c2b70,ge410e46f29+ed4b5058f4,gf23fb2af72+b7cae620c0,gf5e32f922b+fcb1d3bbc8,gf67bdafdda+ed4b5058f4,w.2025.16
LSST Data Management Base Package
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
coaddBase.py
Go to the documentation of this file.
1# This file is part of pipe_tasks.
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
22
23from __future__ import annotations
24
25__all__ = ["CoaddBaseTask", "makeSkyInfo"]
26
27from collections.abc import Iterable
28from typing import TYPE_CHECKING
29
30import lsst.afw.image as afwImage
31import lsst.geom as geom
32import lsst.pex.config as pexConfig
33import lsst.pipe.base as pipeBase
34from lsst.afw.geom import Polygon
35from lsst.pex.exceptions import InvalidParameterError
36
37from .coaddInputRecorder import CoaddInputRecorderTask
38from .selectImages import PsfWcsSelectImagesTask
39
40if TYPE_CHECKING:
41 from logging import Logger
42
43 from lsst.afw.math import StatisticsControl
44
45
46class CoaddBaseConfig(pexConfig.Config):
47 """Configuration parameters for CoaddBaseTask
48
49 Configuration parameters shared between MakeCoaddTempExp and AssembleCoadd
50 """
51
52 coaddName = pexConfig.Field(
53 doc="Coadd name: typically one of deep or goodSeeing.",
54 dtype=str,
55 default="deep",
56 )
57 select = pexConfig.ConfigurableField(
58 doc="Image selection subtask.",
59 target=PsfWcsSelectImagesTask,
60 )
61 badMaskPlanes = pexConfig.ListField(
62 dtype=str,
63 doc="Mask planes that, if set, the associated pixel should not be included in the coaddTempExp.",
64 default=("NO_DATA",),
65 )
66 inputRecorder = pexConfig.ConfigurableField(
67 doc="Subtask that helps fill CoaddInputs catalogs added to the final Exposure",
68 target=CoaddInputRecorderTask,
69 )
70 # TODO[DM-49400]: remove this field (it already does nothing).
71 includeCalibVar = pexConfig.Field(
72 dtype=bool,
73 doc="Add photometric calibration variance to warp variance plane.",
74 default=False,
75 deprecated="Deprecated and ignored. Will be removed after v29.",
76 )
77 # TODO: Remove this field in DM-44792.
78 matchingKernelSize = pexConfig.Field(
79 dtype=int,
80 doc="Size in pixels of matching kernel. Must be odd.",
81 default=None,
82 optional=True,
83 check=lambda x: x % 2 == 1,
84 deprecated=("This field is deprecated without a replacement. Its value has no effect. "
85 "Will be removed after v29."
86 ),
87 )
88
89
90class CoaddBaseTask(pipeBase.PipelineTask):
91 """Base class for coaddition.
92
93 Subclasses must specify _DefaultName
94 """
95
96 ConfigClass = CoaddBaseConfig
97
98 def __init__(self, **kwargs):
99 super().__init__(**kwargs)
100 self.makeSubtask("select")
101 self.makeSubtask("inputRecorder")
102
103 def getTempExpDatasetName(self, warpType="direct"):
104 """Return warp name for given warpType and task config
105
106 Parameters
107 ----------
108 warpType : `str`
109 Either 'direct' or 'psfMatched'.
110
111 Returns
112 -------
113 WarpDatasetName : `str`
114 """
115 return self.config.coaddName + "Coadd_" + warpType + "Warp"
116
118 """Convenience method to provide the bitmask from the mask plane names
119 """
120 return afwImage.Mask.getPlaneBitMask(self.config.badMaskPlanes)
121
122
123def makeSkyInfo(skyMap, tractId, patchId):
124 """Constructs SkyInfo used by coaddition tasks for multiple
125 patchId formats.
126
127 Parameters
128 ----------
129 skyMap : `lsst.skyMap.SkyMap`
130 Sky map.
131 tractId : `int`
132 The ID of the tract.
133 patchId : `str` or `int` or `tuple` of `int`
134 Either Gen2-style comma delimited string (e.g. '4,5'),
135 tuple of integers (e.g (4, 5), Gen3-style integer.
136
137 Returns
138 -------
139 makeSkyInfo : `lsst.pipe.base.Struct`
140 pipe_base Struct with attributes:
141
142 ``skyMap``
143 Sky map (`lsst.skyMap.SkyMap`).
144 ``tractInfo``
145 Information for chosen tract of sky map (`lsst.skyMap.TractInfo`).
146 ``patchInfo``
147 Information about chosen patch of tract (`lsst.skyMap.PatchInfo`).
148 ``wcs``
149 WCS of tract (`lsst.afw.image.SkyWcs`).
150 ``bbox``
151 Outer bbox of patch, as an geom Box2I (`lsst.afw.geom.Box2I`).
152 """
153 tractInfo = skyMap[tractId]
154
155 if isinstance(patchId, str) and ',' in patchId:
156 # patch format is "xIndex,yIndex"
157 patchIndex = tuple(int(i) for i in patchId.split(","))
158 else:
159 patchIndex = patchId
160
161 patchInfo = tractInfo.getPatchInfo(patchIndex)
162
163 return pipeBase.Struct(
164 skyMap=skyMap,
165 tractInfo=tractInfo,
166 patchInfo=patchInfo,
167 wcs=tractInfo.getWcs(),
168 bbox=patchInfo.getOuterBBox(),
169 )
170
171
172def reorderAndPadList(inputList, inputKeys, outputKeys, padWith=None):
173 """Match the order of one list to another, padding if necessary
174
175 Parameters
176 ----------
177 inputList : `list`
178 List to be reordered and padded. Elements can be any type.
179 inputKeys : `iterable`
180 Iterable of values to be compared with outputKeys. Length must match `inputList`.
181 outputKeys : `iterable`
182 Iterable of values to be compared with inputKeys.
183 padWith : `Unknown`
184 Any value to be inserted where inputKey not in outputKeys.
185
186 Returns
187 -------
188 outputList : `list`
189 Copy of inputList reordered per outputKeys and padded with `padWith`
190 so that the length matches length of outputKeys.
191 """
192 outputList = []
193 for d in outputKeys:
194 if d in inputKeys:
195 outputList.append(inputList[inputKeys.index(d)])
196 else:
197 outputList.append(padWith)
198 return outputList
199
200
201def subBBoxIter(bbox, subregionSize):
202 """Iterate over subregions of a bbox.
203
204 Parameters
205 ----------
206 bbox : `lsst.geom.Box2I`
207 Bounding box over which to iterate.
208 subregionSize : `lsst.geom.Extent2I`
209 Size of sub-bboxes.
210
211 Yields
212 ------
213 subBBox : `lsst.geom.Box2I`
214 Next sub-bounding box of size ``subregionSize`` or smaller; each ``subBBox``
215 is contained within ``bbox``, so it may be smaller than ``subregionSize`` at
216 the edges of ``bbox``, but it will never be empty.
217
218 Raises
219 ------
220 RuntimeError
221 Raised if any of the following occur:
222 - The given bbox is empty.
223 - The subregionSize is 0.
224 """
225 if bbox.isEmpty():
226 raise RuntimeError("bbox %s is empty" % (bbox,))
227 if subregionSize[0] < 1 or subregionSize[1] < 1:
228 raise RuntimeError("subregionSize %s must be nonzero" % (subregionSize,))
229
230 for rowShift in range(0, bbox.getHeight(), subregionSize[1]):
231 for colShift in range(0, bbox.getWidth(), subregionSize[0]):
232 subBBox = geom.Box2I(bbox.getMin() + geom.Extent2I(colShift, rowShift), subregionSize)
233 subBBox.clip(bbox)
234 if subBBox.isEmpty():
235 raise RuntimeError("Bug: empty bbox! bbox=%s, subregionSize=%s, "
236 "colShift=%s, rowShift=%s" %
237 (bbox, subregionSize, colShift, rowShift))
238 yield subBBox
239
240
241# Note that this is implemented as a free-floating function to enable reuse in
242# lsst.pipe.tasks.makeWarp and in lsst.drp.tasks.make_psf_matched_warp
243# without creating any relationships between the two classes.
244# This may be converted to a method after makeWarp.py is removed altogether in
245# DM-47916.
246def growValidPolygons(coaddInputs, growBy: int) -> None:
247 """Grow coaddInputs' ccds' ValidPolygons in place.
248
249 Either modify each ccd's validPolygon in place, or if CoaddInputs
250 does not have a validPolygon, create one from its bbox.
251
252 Parameters
253 ----------
254 coaddInputs : `lsst.afw.image.coaddInputs`
255 CoaddInputs object containing the ccds to grow the valid polygons of.
256 growBy : `int`
257 The value to grow the valid polygons by.
258
259 Notes
260 -----
261 Negative values for ``growBy`` can shrink the polygons.
262 """
263 for ccd in coaddInputs.ccds:
264 polyOrig = ccd.getValidPolygon()
265 validPolyBBox = polyOrig.getBBox() if polyOrig else ccd.getBBox()
266 validPolyBBox.grow(growBy)
267 if polyOrig:
268 validPolygon = polyOrig.intersectionSingle(validPolyBBox)
269 else:
270 validPolygon = Polygon(geom.Box2D(validPolyBBox))
271
272 ccd.validPolygon = validPolygon
273
274
276 mask: afwImage.Mask, mask_planes: Iterable, logger: Logger | None = None
277):
278 """Unset the mask of an image for mask planes specified in the config.
279
280 Parameters
281 ----------
282 mask : `lsst.afw.image.Mask`
283 The mask to be modified.
284 mask_planes : `list`
285 The list of mask planes to be removed.
286 logger : `logging.Logger`, optional
287 Logger to log messages.
288 """
289 for maskPlane in mask_planes:
290 try:
291 mask &= ~mask.getPlaneBitMask(maskPlane)
292 except InvalidParameterError:
293 if logger:
294 logger.warning(
295 "Unable to remove mask plane %s: no mask plane with that name was found.",
296 maskPlane,
297 )
298
299
300def setRejectedMaskMapping(statsCtrl: StatisticsControl) -> list[tuple[int, int]]:
301 """Map certain mask planes of the warps to new planes for the coadd.
302
303 If a pixel is rejected due to a mask value other than EDGE, NO_DATA,
304 or CLIPPED, set it to REJECTED on the coadd.
305 If a pixel is rejected due to EDGE, set the coadd pixel to SENSOR_EDGE.
306 If a pixel is rejected due to CLIPPED, set the coadd pixel to CLIPPED.
307
308 Parameters
309 ----------
310 statsCtrl : `lsst.afw.math.StatisticsControl`
311 Statistics control object for coadd.
312
313 Returns
314 -------
315 maskMap : `list` of `tuple` of `int`
316 A list of mappings of mask planes of the warped exposures to
317 mask planes of the coadd.
318 """
319 edge = 2 ** afwImage.Mask.addMaskPlane("EDGE")
320 noData = 2 ** afwImage.Mask.addMaskPlane("NO_DATA")
321 clipped = 2 ** afwImage.Mask.addMaskPlane("CLIPPED")
322 toReject = statsCtrl.getAndMask() & (~noData) & (~edge) & (~clipped)
323 maskMap = [
324 (toReject, 2 ** afwImage.Mask.addMaskPlane("REJECTED")),
325 (edge, 2 ** afwImage.Mask.addMaskPlane("SENSOR_EDGE")),
326 (clipped, clipped),
327 ]
328 return maskMap
Cartesian polygons.
Definition Polygon.h:59
A floating-point coordinate rectangle geometry.
Definition Box.h:413
An integer coordinate rectangle.
Definition Box.h:55
getTempExpDatasetName(self, warpType="direct")
Definition coaddBase.py:103
reorderAndPadList(inputList, inputKeys, outputKeys, padWith=None)
Definition coaddBase.py:172
list[tuple[int, int]] setRejectedMaskMapping(StatisticsControl statsCtrl)
Definition coaddBase.py:300
makeSkyInfo(skyMap, tractId, patchId)
Definition coaddBase.py:123
None growValidPolygons(coaddInputs, int growBy)
Definition coaddBase.py:246
subBBoxIter(bbox, subregionSize)
Definition coaddBase.py:201
removeMaskPlanes(afwImage.Mask mask, Iterable mask_planes, Logger|None logger=None)
Definition coaddBase.py:277