22__all__ = [
"MakeWarpTask",
"MakeWarpConfig"]
30import lsst.pipe.base
as pipeBase
31import lsst.pipe.base.connectionTypes
as connectionTypes
32import lsst.utils
as utils
34from lsst.daf.butler
import DeferredDatasetHandle
38from lsst.utils.timer
import timeMethod
39from .coaddBase
import CoaddBaseTask, makeSkyInfo, reorderAndPadList
40from .warpAndPsfMatch
import WarpAndPsfMatchTask
41from collections.abc
import Iterable
43log = logging.getLogger(__name__)
47 dimensions=(
"tract",
"patch",
"skymap",
"instrument",
"visit"),
48 defaultTemplates={
"coaddName":
"deep",
49 "skyWcsName":
"gbdesAstrometricFit",
50 "photoCalibName":
"fgcm",
53 deprecatedTemplates={
"skyWcsName":
"Deprecated; will be removed after v26.",
54 "photoCalibName":
"Deprecated; will be removed after v26."}):
55 calExpList = connectionTypes.Input(
56 doc=
"Input exposures to be resampled and optionally PSF-matched onto a SkyMap projection/patch",
57 name=
"{calexpType}calexp",
58 storageClass=
"ExposureF",
59 dimensions=(
"instrument",
"visit",
"detector"),
63 backgroundList = connectionTypes.Input(
64 doc=
"Input backgrounds to be added back into the calexp if bgSubtracted=False",
65 name=
"calexpBackground",
66 storageClass=
"Background",
67 dimensions=(
"instrument",
"visit",
"detector"),
70 skyCorrList = connectionTypes.Input(
71 doc=
"Input Sky Correction to be subtracted from the calexp if doApplySkyCorr=True",
73 storageClass=
"Background",
74 dimensions=(
"instrument",
"visit",
"detector"),
77 skyMap = connectionTypes.Input(
78 doc=
"Input definition of geometry/bbox and projection/wcs for warped exposures",
79 name=BaseSkyMap.SKYMAP_DATASET_TYPE_NAME,
80 storageClass=
"SkyMap",
81 dimensions=(
"skymap",),
83 externalSkyWcsTractCatalog = connectionTypes.Input(
84 doc=(
"Per-tract, per-visit wcs calibrations. These catalogs use the detector "
85 "id for the catalog id, sorted on id for fast lookup."),
86 name=
"{skyWcsName}SkyWcsCatalog",
87 storageClass=
"ExposureCatalog",
88 dimensions=(
"instrument",
"visit",
"tract"),
90 deprecated=
"Deprecated in favor of 'visitSummary'. Will be removed after v26.",
92 externalSkyWcsGlobalCatalog = connectionTypes.Input(
93 doc=(
"Per-visit wcs calibrations computed globally (with no tract information). "
94 "These catalogs use the detector id for the catalog id, sorted on id for "
96 name=
"finalVisitSummary",
97 storageClass=
"ExposureCatalog",
98 dimensions=(
"instrument",
"visit"),
100 deprecated=
"Deprecated in favor of 'visitSummary'. Will be removed after v26.",
102 externalPhotoCalibTractCatalog = connectionTypes.Input(
103 doc=(
"Per-tract, per-visit photometric calibrations. These catalogs use the "
104 "detector id for the catalog id, sorted on id for fast lookup."),
105 name=
"{photoCalibName}PhotoCalibCatalog",
106 storageClass=
"ExposureCatalog",
107 dimensions=(
"instrument",
"visit",
"tract"),
109 deprecated=
"Deprecated in favor of 'visitSummary'. Will be removed after v26.",
111 externalPhotoCalibGlobalCatalog = connectionTypes.Input(
112 doc=(
"Per-visit photometric calibrations computed globally (with no tract "
113 "information). These catalogs use the detector id for the catalog id, "
114 "sorted on id for fast lookup."),
115 name=
"finalVisitSummary",
116 storageClass=
"ExposureCatalog",
117 dimensions=(
"instrument",
"visit"),
119 deprecated=
"Deprecated in favor of 'visitSummary'. Will be removed after v26.",
121 finalizedPsfApCorrCatalog = connectionTypes.Input(
122 doc=(
"Per-visit finalized psf models and aperture correction maps. "
123 "These catalogs use the detector id for the catalog id, "
124 "sorted on id for fast lookup."),
125 name=
"finalVisitSummary",
126 storageClass=
"ExposureCatalog",
127 dimensions=(
"instrument",
"visit"),
129 deprecated=
"Deprecated in favor of 'visitSummary'. Will be removed after v26.",
131 direct = connectionTypes.Output(
132 doc=(
"Output direct warped exposure (previously called CoaddTempExp), produced by resampling ",
133 "calexps onto the skyMap patch geometry."),
134 name=
"{coaddName}Coadd_directWarp",
135 storageClass=
"ExposureF",
136 dimensions=(
"tract",
"patch",
"skymap",
"visit",
"instrument"),
138 psfMatched = connectionTypes.Output(
139 doc=(
"Output PSF-Matched warped exposure (previously called CoaddTempExp), produced by resampling ",
140 "calexps onto the skyMap patch geometry and PSF-matching to a model PSF."),
141 name=
"{coaddName}Coadd_psfMatchedWarp",
142 storageClass=
"ExposureF",
143 dimensions=(
"tract",
"patch",
"skymap",
"visit",
"instrument"),
145 wcsList = connectionTypes.Input(
146 doc=
"WCSs of calexps used by SelectImages subtask to determine if the calexp overlaps the patch",
147 name=
"{calexpType}calexp.wcs",
149 dimensions=(
"instrument",
"visit",
"detector"),
153 "Deprecated in favor of the 'visitSummary' connection (and already ignored). "
154 "Will be removed after v26."
157 bboxList = connectionTypes.Input(
158 doc=
"BBoxes of calexps used by SelectImages subtask to determine if the calexp overlaps the patch",
159 name=
"{calexpType}calexp.bbox",
160 storageClass=
"Box2I",
161 dimensions=(
"instrument",
"visit",
"detector"),
165 "Deprecated in favor of the 'visitSummary' connection (and already ignored). "
166 "Will be removed after v26."
169 visitSummary = connectionTypes.Input(
170 doc=
"Input visit-summary catalog with updated calibration objects.",
171 name=
"finalVisitSummary",
172 storageClass=
"ExposureCatalog",
173 dimensions=(
"instrument",
"visit",),
176 def __init__(self, *, config=None):
177 if config.bgSubtracted:
178 del self.backgroundList
179 if not config.doApplySkyCorr:
182 if config.doApplyExternalSkyWcs:
183 if config.useGlobalExternalSkyWcs:
184 del self.externalSkyWcsTractCatalog
186 del self.externalSkyWcsGlobalCatalog
188 del self.externalSkyWcsTractCatalog
189 del self.externalSkyWcsGlobalCatalog
190 if config.doApplyExternalPhotoCalib:
191 if config.useGlobalExternalPhotoCalib:
192 del self.externalPhotoCalibTractCatalog
194 del self.externalPhotoCalibGlobalCatalog
196 del self.externalPhotoCalibTractCatalog
197 del self.externalPhotoCalibGlobalCatalog
198 if not config.doApplyFinalizedPsf:
199 del self.finalizedPsfApCorrCatalog
200 if not config.makeDirect:
202 if not config.makePsfMatched:
214class MakeWarpConfig(pipeBase.PipelineTaskConfig, CoaddBaseTask.ConfigClass,
215 pipelineConnections=MakeWarpConnections):
216 """Config for MakeWarpTask."""
218 warpAndPsfMatch = pexConfig.ConfigurableField(
219 target=WarpAndPsfMatchTask,
220 doc=
"Task to warp and PSF-match calexp",
222 doWrite = pexConfig.Field(
223 doc=
"persist <coaddName>Coadd_<warpType>Warp",
227 bgSubtracted = pexConfig.Field(
228 doc=
"Work with a background subtracted calexp?",
232 coaddPsf = pexConfig.ConfigField(
233 doc=
"Configuration for CoaddPsf",
234 dtype=CoaddPsfConfig,
236 makeDirect = pexConfig.Field(
237 doc=
"Make direct Warp/Coadds",
241 makePsfMatched = pexConfig.Field(
242 doc=
"Make Psf-Matched Warp/Coadd?",
246 useVisitSummaryPsf = pexConfig.Field(
248 "If True, use the PSF model and aperture corrections from the 'visitSummary' connection. "
249 "If False, use the PSF model and aperture corrections from the 'exposure' connection. "
251 "The finalizedPsfApCorrCatalog connection (if enabled) takes precedence over either."
256 doWriteEmptyWarps = pexConfig.Field(
259 doc=
"Write out warps even if they are empty"
261 hasFakes = pexConfig.Field(
262 doc=
"Should be set to True if fake sources have been inserted into the input data.",
266 doApplySkyCorr = pexConfig.Field(
269 doc=
"Apply sky correction?",
271 doApplyFinalizedPsf = pexConfig.Field(
272 doc=
"Whether to apply finalized psf models and aperture correction map.",
276 deprecated=
"Deprecated in favor of useVisitSummaryPsf. Will be removed after v26.",
278 idGenerator = DetectorVisitIdGeneratorConfig.make_field()
281 CoaddBaseTask.ConfigClass.validate(self)
283 if not self.makePsfMatched
and not self.makeDirect:
284 raise RuntimeError(
"At least one of config.makePsfMatched and config.makeDirect must be True")
287 log.warning(
"Config doPsfMatch deprecated. Setting makePsfMatched=True and makeDirect=False")
288 self.makePsfMatched =
True
289 self.makeDirect =
False
291 def setDefaults(self):
292 CoaddBaseTask.ConfigClass.setDefaults(self)
293 self.warpAndPsfMatch.psfMatch.kernel.active.kernelSize = self.matchingKernelSize
297 """Warp and optionally PSF-Match calexps onto an a common projection.
299 Warp and optionally PSF-Match calexps onto a common projection, by
300 performing the following operations:
301 - Group calexps by visit/run
302 - For each visit, generate a Warp by calling method @ref run.
303 `run` loops over the visit's calexps calling
304 `~lsst.pipe.tasks.warpAndPsfMatch.WarpAndPsfMatchTask` on each visit
307 ConfigClass = MakeWarpConfig
308 _DefaultName =
"makeWarp"
310 def __init__(self, **kwargs):
311 CoaddBaseTask.__init__(self, **kwargs)
312 self.makeSubtask(
"warpAndPsfMatch")
313 if self.config.hasFakes:
314 self.calexpType =
"fakes_calexp"
316 self.calexpType =
"calexp"
318 @utils.inheritDoc(pipeBase.PipelineTask)
319 def runQuantum(self, butlerQC, inputRefs, outputRefs):
323 Obtain the list of input detectors from calExpList. Sort them by
324 detector order (to ensure reproducibility). Then ensure all input
325 lists are in the same sorted detector order.
327 detectorOrder = [ref.datasetRef.dataId[
'detector']
for ref
in inputRefs.calExpList]
329 inputRefs = reorderRefs(inputRefs, detectorOrder, dataIdKey=
'detector')
332 inputs = butlerQC.get(inputRefs)
336 skyMap = inputs.pop(
"skyMap")
337 quantumDataId = butlerQC.quantum.dataId
338 skyInfo = makeSkyInfo(skyMap, tractId=quantumDataId[
'tract'], patchId=quantumDataId[
'patch'])
341 dataIdList = [ref.datasetRef.dataId
for ref
in inputRefs.calExpList]
344 self.config.idGenerator.apply(dataId).catalog_id
345 for dataId
in dataIdList
348 visitSummary = inputs[
"visitSummary"]
351 for dataId
in dataIdList:
352 row = visitSummary.find(dataId[
"detector"])
355 f
"Unexpectedly incomplete visitSummary provided to makeWarp: {dataId} is missing."
357 bboxList.append(row.getBBox())
358 wcsList.append(row.getWcs())
359 inputs[
"bboxList"] = bboxList
360 inputs[
"wcsList"] = wcsList
362 if self.config.doApplyExternalSkyWcs:
363 if self.config.useGlobalExternalSkyWcs:
364 externalSkyWcsCatalog = inputs.pop(
"externalSkyWcsGlobalCatalog")
366 externalSkyWcsCatalog = inputs.pop(
"externalSkyWcsTractCatalog")
368 externalSkyWcsCatalog =
None
370 if self.config.doApplyExternalPhotoCalib:
371 if self.config.useGlobalExternalPhotoCalib:
372 externalPhotoCalibCatalog = inputs.pop(
"externalPhotoCalibGlobalCatalog")
374 externalPhotoCalibCatalog = inputs.pop(
"externalPhotoCalibTractCatalog")
376 externalPhotoCalibCatalog =
None
378 if self.config.doApplyFinalizedPsf:
379 finalizedPsfApCorrCatalog = inputs.pop(
"finalizedPsfApCorrCatalog")
381 finalizedPsfApCorrCatalog =
None
385 completeIndices = self._prepareCalibratedExposures(
387 externalSkyWcsCatalog=externalSkyWcsCatalog,
388 externalPhotoCalibCatalog=externalPhotoCalibCatalog,
389 finalizedPsfApCorrCatalog=finalizedPsfApCorrCatalog,
391 inputs = self.filterInputs(indices=completeIndices, inputs=inputs)
397 coordList = [skyInfo.wcs.pixelToSky(pos)
for pos
in cornerPosList]
398 goodIndices = self.select.run(**inputs, coordList=coordList, dataIds=dataIdList)
399 inputs = self.filterInputs(indices=goodIndices, inputs=inputs)
402 visitId = dataIdList[0][
"visit"]
404 results = self.run(**inputs,
406 ccdIdList=[ccdIdList[i]
for i
in goodIndices],
407 dataIdList=[dataIdList[i]
for i
in goodIndices],
409 if self.config.makeDirect
and results.exposures[
"direct"]
is not None:
410 butlerQC.put(results.exposures[
"direct"], outputRefs.direct)
411 if self.config.makePsfMatched
and results.exposures[
"psfMatched"]
is not None:
412 butlerQC.put(results.exposures[
"psfMatched"], outputRefs.psfMatched)
415 def run(self, calExpList, ccdIdList, skyInfo, visitId=0, dataIdList=None, **kwargs):
416 """Create a Warp from inputs.
418 We iterate over the multiple calexps in a single exposure to construct
419 the warp (previously called a coaddTempExp) of that exposure to the
420 supplied tract/patch.
422 Pixels that receive no pixels are set to NAN; this is not correct
423 (violates LSST algorithms group policy), but will be fixed up by
424 interpolating after the coaddition.
426 calexpRefList : `list`
427 List of data references for calexps that (may)
428 overlap the patch of interest.
429 skyInfo : `lsst.pipe.base.Struct`
430 Struct from `~lsst.pipe.base.coaddBase.makeSkyInfo()` with
431 geometric information about the patch.
433 Integer identifier for visit, for the table that will
434 produce the CoaddPsf.
438 result : `lsst.pipe.base.Struct`
439 Results as a struct with attributes:
442 A dictionary containing the warps requested:
443 "direct": direct warp if ``config.makeDirect``
444 "psfMatched": PSF-matched warp if ``config.makePsfMatched``
447 warpTypeList = self.getWarpTypeList()
449 totGoodPix = {warpType: 0
for warpType
in warpTypeList}
450 didSetMetadata = {warpType:
False for warpType
in warpTypeList}
451 warps = {warpType: self._prepareEmptyExposure(skyInfo)
for warpType
in warpTypeList}
452 inputRecorder = {warpType: self.inputRecorder.makeCoaddTempExpRecorder(visitId, len(calExpList))
453 for warpType
in warpTypeList}
455 modelPsf = self.config.modelPsf.apply()
if self.config.makePsfMatched
else None
456 if dataIdList
is None:
457 dataIdList = ccdIdList
459 for calExpInd, (calExp, ccdId, dataId)
in enumerate(zip(calExpList, ccdIdList, dataIdList)):
460 self.log.info(
"Processing calexp %d of %d for this Warp: id=%s",
461 calExpInd+1, len(calExpList), dataId)
463 warpedAndMatched = self.warpAndPsfMatch.run(calExp, modelPsf=modelPsf,
464 wcs=skyInfo.wcs, maxBBox=skyInfo.bbox,
465 makeDirect=self.config.makeDirect,
466 makePsfMatched=self.config.makePsfMatched)
467 except Exception
as e:
468 self.log.warning(
"WarpAndPsfMatch failed for calexp %s; skipping it: %s", dataId, e)
471 numGoodPix = {warpType: 0
for warpType
in warpTypeList}
472 for warpType
in warpTypeList:
473 exposure = warpedAndMatched.getDict()[warpType]
476 warp = warps[warpType]
477 if didSetMetadata[warpType]:
478 mimg = exposure.getMaskedImage()
479 mimg *= (warp.getPhotoCalib().getInstFluxAtZeroMagnitude()
480 / exposure.getPhotoCalib().getInstFluxAtZeroMagnitude())
482 numGoodPix[warpType] = coaddUtils.copyGoodPixels(
483 warp.getMaskedImage(), exposure.getMaskedImage(), self.getBadPixelMask())
484 totGoodPix[warpType] += numGoodPix[warpType]
485 self.log.debug(
"Calexp %s has %d good pixels in this patch (%.1f%%) for %s",
486 dataId, numGoodPix[warpType],
487 100.0*numGoodPix[warpType]/skyInfo.bbox.getArea(), warpType)
488 if numGoodPix[warpType] > 0
and not didSetMetadata[warpType]:
489 warp.info.id = exposure.info.id
490 warp.setPhotoCalib(exposure.getPhotoCalib())
491 warp.setFilter(exposure.getFilter())
492 warp.getInfo().setVisitInfo(exposure.getInfo().getVisitInfo())
495 warp.setPsf(exposure.getPsf())
496 didSetMetadata[warpType] =
True
500 inputRecorder[warpType].addCalExp(calExp, ccdId, numGoodPix[warpType])
502 except Exception
as e:
503 self.log.warning(
"Error processing calexp %s; skipping it: %s", dataId, e)
506 for warpType
in warpTypeList:
507 self.log.info(
"%sWarp has %d good pixels (%.1f%%)",
508 warpType, totGoodPix[warpType], 100.0*totGoodPix[warpType]/skyInfo.bbox.getArea())
510 if totGoodPix[warpType] > 0
and didSetMetadata[warpType]:
511 inputRecorder[warpType].finish(warps[warpType], totGoodPix[warpType])
512 if warpType ==
"direct":
513 warps[warpType].setPsf(
514 CoaddPsf(inputRecorder[warpType].coaddInputs.ccds, skyInfo.wcs,
515 self.config.coaddPsf.makeControl()))
517 if not self.config.doWriteEmptyWarps:
519 warps[warpType] =
None
523 result = pipeBase.Struct(exposures=warps)
526 def filterInputs(self, indices, inputs):
527 """Filter task inputs by their indices.
531 indices : `list` [`int`]
532 inputs : `dict` [`list`]
533 A dictionary of input connections to be passed to run.
537 inputs : `dict` [`list`]
538 Task inputs with their lists filtered by indices.
540 for key
in inputs.keys():
542 if isinstance(inputs[key], list):
543 inputs[key] = [inputs[key][ind]
for ind
in indices]
546 def _prepareCalibratedExposures(self, calExpList=[], wcsList=None, backgroundList=None, skyCorrList=None,
547 externalSkyWcsCatalog=None, externalPhotoCalibCatalog=None,
548 finalizedPsfApCorrCatalog=None, visitSummary=None, **kwargs):
549 """Calibrate and add backgrounds to input calExpList in place.
553 calExpList : `list` [`lsst.afw.image.Exposure` or
554 `lsst.daf.butler.DeferredDatasetHandle`]
555 Sequence of calexps to be modified in place.
556 wcsList : `list` [`lsst.afw.geom.SkyWcs`]
557 The WCSs of the calexps in ``calExpList``. When
558 ``externalSkyCatalog`` is `None`, these are used to determine if
559 the calexp should be included in the warp, namely checking that it
560 is not `None`. If ``externalSkyCatalog`` is not `None`, this list
561 will be dynamically updated with the external sky WCS.
562 backgroundList : `list` [`lsst.afw.math.backgroundList`], optional
563 Sequence of backgrounds to be added back in if bgSubtracted=False.
564 skyCorrList : `list` [`lsst.afw.math.backgroundList`], optional
565 Sequence of background corrections to be subtracted if
567 externalSkyWcsCatalog : `lsst.afw.table.ExposureCatalog`, optional
568 Exposure catalog with external skyWcs to be applied
569 if config.doApplyExternalSkyWcs=True. Catalog uses the detector id
570 for the catalog id, sorted on id for fast lookup.
571 Deprecated and will be removed after v26.
572 externalPhotoCalibCatalog : `lsst.afw.table.ExposureCatalog`, optional
573 Exposure catalog with external photoCalib to be applied
574 if config.doApplyExternalPhotoCalib=True. Catalog uses the
575 detector id for the catalog id, sorted on id for fast lookup.
576 Deprecated and will be removed after v26.
577 finalizedPsfApCorrCatalog : `lsst.afw.table.ExposureCatalog`, optional
578 Exposure catalog with finalized psf models and aperture correction
579 maps to be applied if config.doApplyFinalizedPsf=True. Catalog
580 uses the detector id for the catalog id, sorted on id for fast
582 Deprecated and will be removed after v26.
583 visitSummary : `lsst.afw.table.ExposureCatalog`, optional
584 Exposure catalog with potentially all calibrations. Attributes set
585 to `None` are ignored.
587 Additional keyword arguments.
591 indices : `list` [`int`]
592 Indices of ``calExpList`` and friends that have valid
595 wcsList = len(calExpList)*[
None]
if wcsList
is None else wcsList
596 backgroundList = len(calExpList)*[
None]
if backgroundList
is None else backgroundList
597 skyCorrList = len(calExpList)*[
None]
if skyCorrList
is None else skyCorrList
599 includeCalibVar = self.config.includeCalibVar
602 for index, (calexp, wcs, background, skyCorr)
in enumerate(zip(calExpList,
606 if externalSkyWcsCatalog
is None and wcs
is None:
607 self.log.warning(
"Detector id %d for visit %d has None for skyWcs and will not be "
608 "used in the warp", calexp.dataId[
"detector"], calexp.dataId[
"visit"])
611 if isinstance(calexp, DeferredDatasetHandle):
612 calexp = calexp.get()
614 if not self.config.bgSubtracted:
615 calexp.maskedImage += background.getImage()
617 detectorId = calexp.info.getDetector().getId()
620 if visitSummary
is not None:
621 row = visitSummary.find(detectorId)
624 f
"Unexpectedly incomplete visitSummary: detector={detectorId} is missing."
626 if (photoCalib := row.getPhotoCalib())
is not None:
627 calexp.setPhotoCalib(photoCalib)
628 if (skyWcs := row.getWcs())
is not None:
629 calexp.setWcs(skyWcs)
630 wcsList[index] = skyWcs
631 if self.config.useVisitSummaryPsf:
632 if (psf := row.getPsf())
is not None:
634 if (apCorrMap := row.getApCorrMap())
is not None:
635 calexp.info.setApCorrMap(apCorrMap)
641 if externalPhotoCalibCatalog
is not None:
642 row = externalPhotoCalibCatalog.find(detectorId)
644 self.log.warning(
"Detector id %s not found in externalPhotoCalibCatalog "
645 "and will not be used in the warp.", detectorId)
647 photoCalib = row.getPhotoCalib()
648 if photoCalib
is None:
649 self.log.warning(
"Detector id %s has None for photoCalib in externalPhotoCalibCatalog "
650 "and will not be used in the warp.", detectorId)
652 calexp.setPhotoCalib(photoCalib)
653 elif photoCalib
is None:
654 self.log.warning(
"Detector id %s has None for photoCalib in the visit summary "
655 "and will not be used in the warp.", detectorId)
659 if externalSkyWcsCatalog
is not None:
660 row = externalSkyWcsCatalog.find(detectorId)
662 self.log.warning(
"Detector id %s not found in externalSkyWcsCatalog "
663 "and will not be used in the warp.", detectorId)
665 skyWcs = row.getWcs()
666 wcsList[index] = skyWcs
668 self.log.warning(
"Detector id %s has None for skyWcs in externalSkyWcsCatalog "
669 "and will not be used in the warp.", detectorId)
671 calexp.setWcs(skyWcs)
673 self.log.warning(
"Detector id %s has None for skyWcs in the visit summary "
674 "and will not be used in the warp.", detectorId)
678 if finalizedPsfApCorrCatalog
is not None:
679 row = finalizedPsfApCorrCatalog.find(detectorId)
681 self.log.warning(
"Detector id %s not found in finalizedPsfApCorrCatalog "
682 "and will not be used in the warp.", detectorId)
686 self.log.warning(
"Detector id %s has None for psf in finalizedPsfApCorrCatalog "
687 "and will not be used in the warp.", detectorId)
690 apCorrMap = row.getApCorrMap()
691 if apCorrMap
is None:
692 self.log.warning(
"Detector id %s has None for ApCorrMap in finalizedPsfApCorrCatalog "
693 "and will not be used in the warp.", detectorId)
695 calexp.info.setApCorrMap(apCorrMap)
696 elif self.config.useVisitSummaryPsf:
698 self.log.warning(
"Detector id %s has None for PSF in the visit summary "
699 "and will not be used in the warp.", detectorId)
700 if apCorrMap
is None:
701 self.log.warning(
"Detector id %s has None for ApCorrMap in the visit summary "
702 "and will not be used in the warp.", detectorId)
704 if calexp.getPsf()
is None:
705 self.log.warning(
"Detector id %s has None for PSF in the calexp "
706 "and will not be used in the warp.", detectorId)
707 if calexp.info.getApCorrMap()
is None:
708 self.log.warning(
"Detector id %s has None for ApCorrMap in the calexp "
709 "and will not be used in the warp.", detectorId)
713 calexp.maskedImage = photoCalib.calibrateImage(calexp.maskedImage,
714 includeScaleUncertainty=includeCalibVar)
715 calexp.maskedImage /= photoCalib.getCalibrationMean()
721 if self.config.doApplySkyCorr:
722 calexp.maskedImage -= skyCorr.getImage()
724 indices.append(index)
725 calExpList[index] = calexp
730 def _prepareEmptyExposure(skyInfo):
731 """Produce an empty exposure for a given patch.
735 skyInfo : `lsst.pipe.base.Struct`
736 Struct from `~lsst.pipe.base.coaddBase.makeSkyInfo()` with
737 geometric information about the patch.
741 exp : `lsst.afw.image.exposure.ExposureF`
742 An empty exposure for a given patch.
744 exp = afwImage.ExposureF(skyInfo.bbox, skyInfo.wcs)
745 exp.getMaskedImage().set(numpy.nan, afwImage.Mask
746 .getPlaneBitMask(
"NO_DATA"), numpy.inf)
749 def getWarpTypeList(self):
750 """Return list of requested warp types per the config.
753 if self.config.makeDirect:
754 warpTypeList.append(
"direct")
755 if self.config.makePsfMatched:
756 warpTypeList.append(
"psfMatched")
760def reorderRefs(inputRefs, outputSortKeyOrder, dataIdKey):
761 """Reorder inputRefs per outputSortKeyOrder.
763 Any inputRefs which are lists will be resorted per specified key e.g.,
764 'detector.' Only iterables will be reordered, and values can be of type
765 `lsst.pipe.base.connections.DeferredDatasetRef` or
766 `lsst.daf.butler.core.datasets.ref.DatasetRef`.
768 Returned lists of refs have the same length as the outputSortKeyOrder.
769 If an outputSortKey not in the inputRef, then it will be padded with None.
770 If an inputRef contains an inputSortKey that is not in the
771 outputSortKeyOrder it will be removed.
775 inputRefs : `lsst.pipe.base.connections.QuantizedConnection`
776 Input references to be reordered and padded.
777 outputSortKeyOrder : `iterable`
778 Iterable of values to be compared with inputRef's dataId[dataIdKey].
780 The data ID key in the dataRefs to compare with the outputSortKeyOrder.
784 inputRefs : `lsst.pipe.base.connections.QuantizedConnection`
785 Quantized Connection with sorted DatasetRef values sorted if iterable.
787 for connectionName, refs
in inputRefs:
788 if isinstance(refs, Iterable):
789 if hasattr(refs[0],
"dataId"):
790 inputSortKeyOrder = [ref.dataId[dataIdKey]
for ref
in refs]
792 inputSortKeyOrder = [ref.datasetRef.dataId[dataIdKey]
for ref
in refs]
793 if inputSortKeyOrder != outputSortKeyOrder:
794 setattr(inputRefs, connectionName,
795 reorderAndPadList(refs, inputSortKeyOrder, outputSortKeyOrder))