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