Loading [MathJax]/extensions/tex2jax.js
LSST Applications g0fba68d861+aa97b6e50c,g1ec0fe41b4+f536777771,g1fd858c14a+a9301854fb,g35bb328faa+fcb1d3bbc8,g4af146b050+a5c07d5b1d,g4d2262a081+78f4f01b60,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,gb89ab40317+ed4b5058f4,gcf25f946ba+e5453db6e6,gcf6002c91b+2a0c9e9e84,gd6cbbdb0b4+bb83cc51f8,gdd1046aedd+ded28b650d,gde0f65d7ad+66b3a48cb7,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
makeWarp.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__all__ = ["MakeWarpTask", "MakeWarpConfig"]
23
24import logging
25import numpy
26
27import lsst.pex.config as pexConfig
28import lsst.afw.image as afwImage
29import lsst.coadd.utils as coaddUtils
30import lsst.pipe.base as pipeBase
31import lsst.pipe.base.connectionTypes as connectionTypes
32import lsst.utils as utils
33import lsst.geom
34from deprecated.sphinx import deprecated
35from lsst.daf.butler import DeferredDatasetHandle
36from lsst.meas.base import DetectorVisitIdGeneratorConfig
37from lsst.meas.algorithms import CoaddPsf, CoaddPsfConfig, GaussianPsfFactory
38from lsst.skymap import BaseSkyMap
39from lsst.utils.timer import timeMethod
40from .coaddBase import CoaddBaseTask, growValidPolygons, makeSkyInfo, reorderAndPadList
41from .warpAndPsfMatch import WarpAndPsfMatchTask
42from collections.abc import Iterable
43
44log = logging.getLogger(__name__)
45
46
47class MakeWarpConnections(pipeBase.PipelineTaskConnections,
48 dimensions=("tract", "patch", "skymap", "instrument", "visit"),
49 defaultTemplates={"coaddName": "deep",
50 "calexpType": ""}):
51 calExpList = connectionTypes.Input(
52 doc="Input exposures to be resampled and optionally PSF-matched onto a SkyMap projection/patch",
53 name="{calexpType}calexp",
54 storageClass="ExposureF",
55 dimensions=("instrument", "visit", "detector"),
56 multiple=True,
57 deferLoad=True,
58 )
59 backgroundList = connectionTypes.Input(
60 doc="Input backgrounds to be added back into the calexp if bgSubtracted=False",
61 name="calexpBackground",
62 storageClass="Background",
63 dimensions=("instrument", "visit", "detector"),
64 multiple=True,
65 )
66 skyCorrList = connectionTypes.Input(
67 doc="Input Sky Correction to be subtracted from the calexp if doApplySkyCorr=True",
68 name="skyCorr",
69 storageClass="Background",
70 dimensions=("instrument", "visit", "detector"),
71 multiple=True,
72 )
73 skyMap = connectionTypes.Input(
74 doc="Input definition of geometry/bbox and projection/wcs for warped exposures",
75 name=BaseSkyMap.SKYMAP_DATASET_TYPE_NAME,
76 storageClass="SkyMap",
77 dimensions=("skymap",),
78 )
79 direct = connectionTypes.Output(
80 doc=("Output direct warped exposure (previously called CoaddTempExp), produced by resampling "
81 "calexps onto the skyMap patch geometry."),
82 name="{coaddName}Coadd_directWarp",
83 storageClass="ExposureF",
84 dimensions=("tract", "patch", "skymap", "visit", "instrument"),
85 )
86 psfMatched = connectionTypes.Output(
87 doc=("Output PSF-Matched warped exposure (previously called CoaddTempExp), produced by resampling "
88 "calexps onto the skyMap patch geometry and PSF-matching to a model PSF."),
89 name="{coaddName}Coadd_psfMatchedWarp",
90 storageClass="ExposureF",
91 dimensions=("tract", "patch", "skymap", "visit", "instrument"),
92 )
93 visitSummary = connectionTypes.Input(
94 doc="Input visit-summary catalog with updated calibration objects.",
95 name="finalVisitSummary",
96 storageClass="ExposureCatalog",
97 dimensions=("instrument", "visit",),
98 )
99
100 def __init__(self, *, config=None):
101 if config.bgSubtracted:
102 del self.backgroundList
103 if not config.doApplySkyCorr:
104 del self.skyCorrList
105 if not config.makeDirect:
106 del self.direct
107 if not config.makePsfMatched:
108 del self.psfMatched
109
110
111@deprecated(reason="The Task corresponding to this Config is no longer in use. Will be removed after v29.",
112 version="v29.0", category=FutureWarning)
113class MakeWarpConfig(pipeBase.PipelineTaskConfig, CoaddBaseTask.ConfigClass,
114 pipelineConnections=MakeWarpConnections):
115 """Config for MakeWarpTask."""
116
117 warpAndPsfMatch = pexConfig.ConfigurableField(
118 target=WarpAndPsfMatchTask,
119 doc="Task to warp and PSF-match calexp",
120 )
121 doWrite = pexConfig.Field(
122 doc="persist <coaddName>Coadd_<warpType>Warp",
123 dtype=bool,
124 default=True,
125 )
126 bgSubtracted = pexConfig.Field(
127 doc="Work with a background subtracted calexp?",
128 dtype=bool,
129 default=True,
130 )
131 coaddPsf = pexConfig.ConfigField(
132 doc="Configuration for CoaddPsf",
133 dtype=CoaddPsfConfig,
134 )
135 makeDirect = pexConfig.Field(
136 doc="Make direct Warp/Coadds",
137 dtype=bool,
138 default=True,
139 )
140 makePsfMatched = pexConfig.Field(
141 doc="Make Psf-Matched Warp/Coadd?",
142 dtype=bool,
143 default=False,
144 )
145 modelPsf = GaussianPsfFactory.makeField(doc="Model Psf factory")
146 useVisitSummaryPsf = pexConfig.Field(
147 doc=(
148 "If True, use the PSF model and aperture corrections from the 'visitSummary' connection. "
149 "If False, use the PSF model and aperture corrections from the 'exposure' connection. "
150 ),
151 dtype=bool,
152 default=True,
153 )
154 doWriteEmptyWarps = pexConfig.Field(
155 dtype=bool,
156 default=False,
157 doc="Write out warps even if they are empty"
158 )
159 hasFakes = pexConfig.Field(
160 doc="Should be set to True if fake sources have been inserted into the input data.",
161 dtype=bool,
162 default=False,
163 )
164 doApplySkyCorr = pexConfig.Field(
165 dtype=bool,
166 default=False,
167 doc="Apply sky correction?",
168 )
169 idGenerator = DetectorVisitIdGeneratorConfig.make_field()
170
171 def validate(self):
172 CoaddBaseTask.ConfigClass.validate(self)
173
174 if not self.makePsfMatched and not self.makeDirect:
175 raise ValueError("At least one of config.makePsfMatched and config.makeDirect must be True")
176 if self.warpAndPsfMatch.warp.cacheSize != self.coaddPsf.cacheSize:
177 # This is an incomplete check: usually the CoaddPsf cache size
178 # configured here in MakeWarpTask is superseded by the one in
179 # AssembleCoaddTask. A pipeline contract in the drp_pipe is
180 # present to check that.
181 raise ValueError("Image warping cache size and CoaddPSf warping cache size do not agree.")
182
183 def setDefaults(self):
184 CoaddBaseTask.ConfigClass.setDefaults(self)
185 self.warpAndPsfMatch.warp.cacheSize = 0
186 self.coaddPsf.cacheSize = 0
187
188
189@deprecated(reason="The MakeWarpTask is replaced by MakeDirectWarpTask and MakePsfMatchedWarpTask. "
190 "This Task will be removed after v29.",
191 version="v29.0", category=FutureWarning)
192class MakeWarpTask(CoaddBaseTask):
193 """Warp and optionally PSF-Match calexps onto an a common projection.
194
195 Warp and optionally PSF-Match calexps onto a common projection, by
196 performing the following operations:
197 - Group calexps by visit/run
198 - For each visit, generate a Warp by calling method @ref run.
199 `run` loops over the visit's calexps calling
200 `~lsst.pipe.tasks.warpAndPsfMatch.WarpAndPsfMatchTask` on each visit
201
202 """
203 ConfigClass = MakeWarpConfig
204 _DefaultName = "makeWarp"
205
206 def __init__(self, **kwargs):
207 CoaddBaseTask.__init__(self, **kwargs)
208 self.makeSubtask("warpAndPsfMatch")
209 if self.config.hasFakes:
210 self.calexpType = "fakes_calexp"
211 else:
212 self.calexpType = "calexp"
213
214 @utils.inheritDoc(pipeBase.PipelineTask)
215 def runQuantum(self, butlerQC, inputRefs, outputRefs):
216 # Docstring to be augmented with info from PipelineTask.runQuantum
217 """Notes
218 -----
219 Obtain the list of input detectors from calExpList. Sort them by
220 detector order (to ensure reproducibility). Then ensure all input
221 lists are in the same sorted detector order.
222 """
223 detectorOrder = [handle.datasetRef.dataId['detector'] for handle in inputRefs.calExpList]
224 detectorOrder.sort()
225 inputRefs = reorderRefs(inputRefs, detectorOrder, dataIdKey='detector')
226
227 # Read in all inputs.
228 inputs = butlerQC.get(inputRefs)
229
230 # Construct skyInfo expected by `run`. We remove the SkyMap itself
231 # from the dictionary so we can pass it as kwargs later.
232 skyMap = inputs.pop("skyMap")
233 quantumDataId = butlerQC.quantum.dataId
234 skyInfo = makeSkyInfo(skyMap, tractId=quantumDataId['tract'], patchId=quantumDataId['patch'])
235
236 # Construct list of input DataIds expected by `run`.
237 dataIdList = [ref.datasetRef.dataId for ref in inputRefs.calExpList]
238 # Construct list of packed integer IDs expected by `run`.
239 ccdIdList = [
240 self.config.idGenerator.apply(dataId).catalog_id
241 for dataId in dataIdList
242 ]
243
244 # Check early that the visitSummary contains everything we need.
245 visitSummary = inputs["visitSummary"]
246 bboxList = []
247 wcsList = []
248 for dataId in dataIdList:
249 row = visitSummary.find(dataId["detector"])
250 if row is None:
251 bboxList.append(None)
252 wcsList.append(None)
253 else:
254 bboxList.append(row.getBBox())
255 wcsList.append(row.getWcs())
256 inputs["bboxList"] = bboxList
257 inputs["wcsList"] = wcsList
258
259 # Do an initial selection on inputs with complete wcs/photoCalib info.
260 # Qualifying calexps will be read in the following call.
261 completeIndices = self._prepareCalibratedExposures(**inputs)
262 inputs = self.filterInputs(indices=completeIndices, inputs=inputs)
263
264 # Do another selection based on the configured selection task
265 # (using updated WCSs to determine patch overlap if an external
266 # calibration was applied).
267 cornerPosList = lsst.geom.Box2D(skyInfo.bbox).getCorners()
268 coordList = [skyInfo.wcs.pixelToSky(pos) for pos in cornerPosList]
269 goodIndices = self.select.run(**inputs, coordList=coordList, dataIds=dataIdList)
270 inputs = self.filterInputs(indices=goodIndices, inputs=inputs)
271
272 # Extract integer visitId requested by `run`.
273 visitId = dataIdList[0]["visit"]
274
275 results = self.run(**inputs,
276 visitId=visitId,
277 ccdIdList=[ccdIdList[i] for i in goodIndices],
278 dataIdList=[dataIdList[i] for i in goodIndices],
279 skyInfo=skyInfo)
280 if self.config.makeDirect and results.exposures["direct"] is not None:
281 butlerQC.put(results.exposures["direct"], outputRefs.direct)
282 if self.config.makePsfMatched and results.exposures["psfMatched"] is not None:
283 butlerQC.put(results.exposures["psfMatched"], outputRefs.psfMatched)
284
285 @timeMethod
286 def run(self, calExpList, ccdIdList, skyInfo, visitId=0, dataIdList=None, **kwargs):
287 """Create a Warp from inputs.
288
289 We iterate over the multiple calexps in a single exposure to construct
290 the warp (previously called a coaddTempExp) of that exposure to the
291 supplied tract/patch.
292
293 Pixels that receive no pixels are set to NAN; this is not correct
294 (violates LSST algorithms group policy), but will be fixed up by
295 interpolating after the coaddition.
296
297 calExpList : `list` [ `lsst.afw.image.Exposure` ]
298 List of single-detector input images that (may) overlap the patch
299 of interest.
300 skyInfo : `lsst.pipe.base.Struct`
301 Struct from `~lsst.pipe.base.coaddBase.makeSkyInfo()` with
302 geometric information about the patch.
303 visitId : `int`
304 Integer identifier for visit, for the table that will
305 produce the CoaddPsf.
306
307 Returns
308 -------
309 result : `lsst.pipe.base.Struct`
310 Results as a struct with attributes:
311
312 ``exposures``
313 A dictionary containing the warps requested:
314 "direct": direct warp if ``config.makeDirect``
315 "psfMatched": PSF-matched warp if ``config.makePsfMatched``
316 (`dict`).
317 """
318 warpTypeList = self.getWarpTypeList()
319
320 totGoodPix = {warpType: 0 for warpType in warpTypeList}
321 didSetMetadata = {warpType: False for warpType in warpTypeList}
322 warps = {warpType: self._prepareEmptyExposure(skyInfo) for warpType in warpTypeList}
323 inputRecorder = {warpType: self.inputRecorder.makeCoaddTempExpRecorder(visitId, len(calExpList))
324 for warpType in warpTypeList}
325
326 modelPsf = self.config.modelPsf.apply() if self.config.makePsfMatched else None
327 if dataIdList is None:
328 dataIdList = ccdIdList
329
330 for calExpInd, (calExp, ccdId, dataId) in enumerate(zip(calExpList, ccdIdList, dataIdList)):
331 self.log.info("Processing calexp %d of %d for this Warp: id=%s",
332 calExpInd+1, len(calExpList), dataId)
333 try:
334 warpedAndMatched = self.warpAndPsfMatch.run(calExp, modelPsf=modelPsf,
335 wcs=skyInfo.wcs, maxBBox=skyInfo.bbox,
336 makeDirect=self.config.makeDirect,
337 makePsfMatched=self.config.makePsfMatched)
338 except Exception as e:
339 self.log.warning("WarpAndPsfMatch failed for calexp %s; skipping it: %s", dataId, e)
340 continue
341 try:
342 numGoodPix = {warpType: 0 for warpType in warpTypeList}
343 for warpType in warpTypeList:
344 exposure = warpedAndMatched.getDict()[warpType]
345 if exposure is None:
346 continue
347 warp = warps[warpType]
348 numGoodPix[warpType] = coaddUtils.copyGoodPixels(
349 warp.getMaskedImage(), exposure.getMaskedImage(), self.getBadPixelMask())
350 totGoodPix[warpType] += numGoodPix[warpType]
351 self.log.debug("Calexp %s has %d good pixels in this patch (%.1f%%) for %s",
352 dataId, numGoodPix[warpType],
353 100.0*numGoodPix[warpType]/skyInfo.bbox.getArea(), warpType)
354 if numGoodPix[warpType] > 0 and not didSetMetadata[warpType]:
355 warp.info.id = exposure.info.id
356 warp.setPhotoCalib(exposure.getPhotoCalib())
357 warp.setFilter(exposure.getFilter())
358 warp.getInfo().setVisitInfo(exposure.getInfo().getVisitInfo())
359 # PSF replaced with CoaddPsf after loop if and only if
360 # creating direct warp.
361 warp.setPsf(exposure.getPsf())
362 didSetMetadata[warpType] = True
363
364 # Need inputRecorder for CoaddApCorrMap for both direct and
365 # PSF-matched.
366 inputRecorder[warpType].addCalExp(calExp, ccdId, numGoodPix[warpType])
367
368 except Exception as e:
369 self.log.warning("Error processing calexp %s; skipping it: %s", dataId, e)
370 continue
371
372 for warpType in warpTypeList:
373 self.log.info("%sWarp has %d good pixels (%.1f%%)",
374 warpType, totGoodPix[warpType], 100.0*totGoodPix[warpType]/skyInfo.bbox.getArea())
375
376 if totGoodPix[warpType] > 0 and didSetMetadata[warpType]:
377 inputRecorder[warpType].finish(warps[warpType], totGoodPix[warpType])
378 if warpType == "direct":
379 warps[warpType].setPsf(
380 CoaddPsf(inputRecorder[warpType].coaddInputs.ccds, skyInfo.wcs,
381 self.config.coaddPsf.makeControl()))
382 else: # warpType == "psfMached"
383 growValidPolygons(
384 inputRecorder[warpType].coaddInputs,
385 -self.config.warpAndPsfMatch.psfMatch.kernel.active.kernelSize // 2,
386 )
387 else:
388 if not self.config.doWriteEmptyWarps:
389 # No good pixels. Exposure still empty.
390 warps[warpType] = None
391 # NoWorkFound is unnecessary as the downstream tasks will
392 # adjust the quantum accordingly.
393
394 result = pipeBase.Struct(exposures=warps)
395 return result
396
397 def filterInputs(self, indices, inputs):
398 """Filter task inputs by their indices.
399
400 Parameters
401 ----------
402 indices : `list` [`int`]
403 inputs : `dict` [`list`]
404 A dictionary of input connections to be passed to run.
405
406 Returns
407 -------
408 inputs : `dict` [`list`]
409 Task inputs with their lists filtered by indices.
410 """
411 for key in inputs.keys():
412 # Only down-select on list inputs
413 if isinstance(inputs[key], list):
414 inputs[key] = [inputs[key][ind] for ind in indices]
415 return inputs
416
417 def _prepareCalibratedExposures(self, *, visitSummary, calExpList=[], wcsList=None,
418 backgroundList=None, skyCorrList=None, **kwargs):
419 """Calibrate and add backgrounds to input calExpList in place.
420
421 Parameters
422 ----------
423 visitSummary : `lsst.afw.table.ExposureCatalog`
424 Exposure catalog with potentially all calibrations. Attributes set
425 to `None` are ignored.
426 calExpList : `list` [`lsst.afw.image.Exposure` or
427 `lsst.daf.butler.DeferredDatasetHandle`]
428 Sequence of single-epoch images (or deferred load handles for
429 images) to be modified in place. On return this always has images,
430 not handles.
431 wcsList : `list` [`lsst.afw.geom.SkyWcs` or `None` ]
432 The WCSs of the calexps in ``calExpList``. These will be used to
433 determine if the calexp should be used in the warp. The list is
434 dynamically updated with the WCSs from the visitSummary.
435 backgroundList : `list` [`lsst.afw.math.BackgroundList`], optional
436 Sequence of backgrounds to be added back in if bgSubtracted=False.
437 skyCorrList : `list` [`lsst.afw.math.BackgroundList`], optional
438 Sequence of background corrections to be subtracted if
439 doApplySkyCorr=True.
440 **kwargs
441 Additional keyword arguments.
442
443 Returns
444 -------
445 indices : `list` [`int`]
446 Indices of ``calExpList`` and friends that have valid
447 photoCalib/skyWcs.
448 """
449 wcsList = len(calExpList)*[None] if wcsList is None else wcsList
450 backgroundList = len(calExpList)*[None] if backgroundList is None else backgroundList
451 skyCorrList = len(calExpList)*[None] if skyCorrList is None else skyCorrList
452
453 indices = []
454 for index, (calexp, background, skyCorr) in enumerate(zip(calExpList,
455 backgroundList,
456 skyCorrList)):
457 if isinstance(calexp, DeferredDatasetHandle):
458 calexp = calexp.get()
459
460 if not self.config.bgSubtracted:
461 calexp.maskedImage += background.getImage()
462
463 detectorId = calexp.info.getDetector().getId()
464
465 # Load all calibrations from visitSummary.
466 row = visitSummary.find(detectorId)
467 if row is None:
468 self.log.warning(
469 "Detector id %d has no row in the visitSummary and will "
470 "not be used in the warp", detectorId,
471 )
472 continue
473 if (photoCalib := row.getPhotoCalib()) is not None:
474 calexp.setPhotoCalib(photoCalib)
475 else:
476 self.log.warning(
477 "Detector id %d for visit %d has None for photoCalib in the visitSummary and will "
478 "not be used in the warp", detectorId, row["visit"],
479 )
480 continue
481 if (skyWcs := row.getWcs()) is not None:
482 calexp.setWcs(skyWcs)
483 wcsList[index] = skyWcs
484 else:
485 self.log.warning(
486 "Detector id %d for visit %d has None for wcs in the visitSummary and will "
487 "not be used in the warp", detectorId, row["visit"],
488 )
489 continue
490 if self.config.useVisitSummaryPsf:
491 if (psf := row.getPsf()) is not None:
492 calexp.setPsf(psf)
493 else:
494 self.log.warning(
495 "Detector id %d for visit %d has None for psf in the visitSummary and will "
496 "not be used in the warp", detectorId, row["visit"],
497 )
498 continue
499 if (apCorrMap := row.getApCorrMap()) is not None:
500 calexp.info.setApCorrMap(apCorrMap)
501 else:
502 self.log.warning(
503 "Detector id %d for visit %d has None for apCorrMap in the visitSummary and will "
504 "not be used in the warp", detectorId, row["visit"],
505 )
506 continue
507 else:
508 if calexp.getPsf() is None:
509 self.log.warning(
510 "Detector id %d for visit %d has None for psf for the calexp and will "
511 "not be used in the warp", detectorId, row["visit"],
512 )
513 continue
514 if calexp.info.getApCorrMap() is None:
515 self.log.warning(
516 "Detector id %d for visit %d has None for apCorrMap in the calexp and will "
517 "not be used in the warp", detectorId, row["visit"],
518 )
519 continue
520
521 # Apply skycorr
522 if self.config.doApplySkyCorr:
523 calexp.maskedImage -= skyCorr.getImage()
524
525 # Calibrate the image.
526 calexp.maskedImage = photoCalib.calibrateImage(calexp.maskedImage)
527 # This new PhotoCalib shouldn't need to be used, but setting it
528 # here to reflect the fact that the image now has calibrated pixels
529 # might help avoid future bugs.
530 calexp.setPhotoCalib(afwImage.PhotoCalib(1.0))
531
532 indices.append(index)
533 calExpList[index] = calexp
534
535 return indices
536
537 @staticmethod
538 def _prepareEmptyExposure(skyInfo):
539 """Produce an empty exposure for a given patch.
540
541 Parameters
542 ----------
543 skyInfo : `lsst.pipe.base.Struct`
544 Struct from `~lsst.pipe.base.coaddBase.makeSkyInfo()` with
545 geometric information about the patch.
546
547 Returns
548 -------
549 exp : `lsst.afw.image.exposure.ExposureF`
550 An empty exposure for a given patch.
551 """
552 exp = afwImage.ExposureF(skyInfo.bbox, skyInfo.wcs)
553 exp.getMaskedImage().set(numpy.nan, afwImage.Mask
554 .getPlaneBitMask("NO_DATA"), numpy.inf)
555 exp.setPhotoCalib(afwImage.PhotoCalib(1.0))
556 exp.metadata["BUNIT"] = "nJy"
557 return exp
558
560 """Return list of requested warp types per the config.
561 """
562 warpTypeList = []
563 if self.config.makeDirect:
564 warpTypeList.append("direct")
565 if self.config.makePsfMatched:
566 warpTypeList.append("psfMatched")
567 return warpTypeList
568
569
570def reorderRefs(inputRefs, outputSortKeyOrder, dataIdKey):
571 """Reorder inputRefs per outputSortKeyOrder.
572
573 Any inputRefs which are lists will be resorted per specified key e.g.,
574 'detector.' Only iterables will be reordered, and values can be of type
575 `lsst.pipe.base.connections.DeferredDatasetRef` or
576 `lsst.daf.butler.core.datasets.ref.DatasetRef`.
577
578 Returned lists of refs have the same length as the outputSortKeyOrder.
579 If an outputSortKey not in the inputRef, then it will be padded with None.
580 If an inputRef contains an inputSortKey that is not in the
581 outputSortKeyOrder it will be removed.
582
583 Parameters
584 ----------
585 inputRefs : `lsst.pipe.base.connections.QuantizedConnection`
586 Input references to be reordered and padded.
587 outputSortKeyOrder : `iterable`
588 Iterable of values to be compared with inputRef's dataId[dataIdKey].
589 dataIdKey : `str`
590 The data ID key in the dataRefs to compare with the outputSortKeyOrder.
591
592 Returns
593 -------
594 inputRefs : `lsst.pipe.base.connections.QuantizedConnection`
595 Quantized Connection with sorted DatasetRef values sorted if iterable.
596 """
597 for connectionName, refs in inputRefs:
598 if isinstance(refs, Iterable):
599 if hasattr(refs[0], "dataId"):
600 inputSortKeyOrder = [ref.dataId[dataIdKey] for ref in refs]
601 else:
602 inputSortKeyOrder = [handle.datasetRef.dataId[dataIdKey] for handle in refs]
603 if inputSortKeyOrder != outputSortKeyOrder:
604 setattr(inputRefs, connectionName,
605 reorderAndPadList(refs, inputSortKeyOrder, outputSortKeyOrder))
606 return inputRefs
Represent a 2-dimensional array of bitmask pixels.
Definition Mask.h:82
The photometric calibration of an exposure.
Definition PhotoCalib.h:114
A floating-point coordinate rectangle geometry.
Definition Box.h:413
CoaddPsf is the Psf derived to be used for non-PSF-matched Coadd images.
Definition CoaddPsf.h:58
int copyGoodPixels(lsst::afw::image::Image< ImagePixelT > &destImage, lsst::afw::image::Image< ImagePixelT > const &srcImage)
copy good pixels from one image to another
reorderRefs(inputRefs, outputSortKeyOrder, dataIdKey)
Definition makeWarp.py:570