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",
52 calExpList = connectionTypes.Input(
53 doc=
"Input exposures to be resampled and optionally PSF-matched onto a SkyMap projection/patch",
54 name=
"{calexpType}calexp",
55 storageClass=
"ExposureF",
56 dimensions=(
"instrument",
"visit",
"detector"),
60 backgroundList = connectionTypes.Input(
61 doc=
"Input backgrounds to be added back into the calexp if bgSubtracted=False",
62 name=
"calexpBackground",
63 storageClass=
"Background",
64 dimensions=(
"instrument",
"visit",
"detector"),
67 skyCorrList = connectionTypes.Input(
68 doc=
"Input Sky Correction to be subtracted from the calexp if doApplySkyCorr=True",
70 storageClass=
"Background",
71 dimensions=(
"instrument",
"visit",
"detector"),
74 skyMap = connectionTypes.Input(
75 doc=
"Input definition of geometry/bbox and projection/wcs for warped exposures",
76 name=BaseSkyMap.SKYMAP_DATASET_TYPE_NAME,
77 storageClass=
"SkyMap",
78 dimensions=(
"skymap",),
80 externalSkyWcsTractCatalog = connectionTypes.Input(
81 doc=(
"Per-tract, per-visit wcs calibrations. These catalogs use the detector "
82 "id for the catalog id, sorted on id for fast lookup."),
83 name=
"{skyWcsName}SkyWcsCatalog",
84 storageClass=
"ExposureCatalog",
85 dimensions=(
"instrument",
"visit",
"tract"),
87 externalSkyWcsGlobalCatalog = connectionTypes.Input(
88 doc=(
"Per-visit wcs calibrations computed globally (with no tract information). "
89 "These catalogs use the detector id for the catalog id, sorted on id for "
91 name=
"finalVisitSummary",
92 storageClass=
"ExposureCatalog",
93 dimensions=(
"instrument",
"visit"),
95 externalPhotoCalibTractCatalog = connectionTypes.Input(
96 doc=(
"Per-tract, per-visit photometric calibrations. These catalogs use the "
97 "detector id for the catalog id, sorted on id for fast lookup."),
98 name=
"{photoCalibName}PhotoCalibCatalog",
99 storageClass=
"ExposureCatalog",
100 dimensions=(
"instrument",
"visit",
"tract"),
102 externalPhotoCalibGlobalCatalog = connectionTypes.Input(
103 doc=(
"Per-visit photometric calibrations computed globally (with no tract "
104 "information). These catalogs use the detector id for the catalog id, "
105 "sorted on id for fast lookup."),
106 name=
"finalVisitSummary",
107 storageClass=
"ExposureCatalog",
108 dimensions=(
"instrument",
"visit"),
110 finalizedPsfApCorrCatalog = connectionTypes.Input(
111 doc=(
"Per-visit finalized psf models and aperture correction maps. "
112 "These catalogs use the detector id for the catalog id, "
113 "sorted on id for fast lookup."),
114 name=
"finalVisitSummary",
115 storageClass=
"ExposureCatalog",
116 dimensions=(
"instrument",
"visit"),
118 direct = connectionTypes.Output(
119 doc=(
"Output direct warped exposure (previously called CoaddTempExp), produced by resampling ",
120 "calexps onto the skyMap patch geometry."),
121 name=
"{coaddName}Coadd_directWarp",
122 storageClass=
"ExposureF",
123 dimensions=(
"tract",
"patch",
"skymap",
"visit",
"instrument"),
125 psfMatched = connectionTypes.Output(
126 doc=(
"Output PSF-Matched warped exposure (previously called CoaddTempExp), produced by resampling ",
127 "calexps onto the skyMap patch geometry and PSF-matching to a model PSF."),
128 name=
"{coaddName}Coadd_psfMatchedWarp",
129 storageClass=
"ExposureF",
130 dimensions=(
"tract",
"patch",
"skymap",
"visit",
"instrument"),
134 wcsList = connectionTypes.Input(
135 doc=
"WCSs of calexps used by SelectImages subtask to determine if the calexp overlaps the patch",
136 name=
"{calexpType}calexp.wcs",
138 dimensions=(
"instrument",
"visit",
"detector"),
141 bboxList = connectionTypes.Input(
142 doc=
"BBoxes of calexps used by SelectImages subtask to determine if the calexp overlaps the patch",
143 name=
"{calexpType}calexp.bbox",
144 storageClass=
"Box2I",
145 dimensions=(
"instrument",
"visit",
"detector"),
148 visitSummary = connectionTypes.Input(
149 doc=
"Consolidated exposure metadata",
150 name=
"finalVisitSummary",
151 storageClass=
"ExposureCatalog",
152 dimensions=(
"instrument",
"visit",),
155 def __init__(self, *, config=None):
156 super().__init__(config=config)
157 if config.bgSubtracted:
158 self.inputs.remove(
"backgroundList")
159 if not config.doApplySkyCorr:
160 self.inputs.remove(
"skyCorrList")
161 if config.doApplyExternalSkyWcs:
162 if config.useGlobalExternalSkyWcs:
163 self.inputs.remove(
"externalSkyWcsTractCatalog")
165 self.inputs.remove(
"externalSkyWcsGlobalCatalog")
167 self.inputs.remove(
"externalSkyWcsTractCatalog")
168 self.inputs.remove(
"externalSkyWcsGlobalCatalog")
169 if config.doApplyExternalPhotoCalib:
170 if config.useGlobalExternalPhotoCalib:
171 self.inputs.remove(
"externalPhotoCalibTractCatalog")
173 self.inputs.remove(
"externalPhotoCalibGlobalCatalog")
175 self.inputs.remove(
"externalPhotoCalibTractCatalog")
176 self.inputs.remove(
"externalPhotoCalibGlobalCatalog")
177 if not config.doApplyFinalizedPsf:
178 self.inputs.remove(
"finalizedPsfApCorrCatalog")
179 if not config.makeDirect:
180 self.outputs.remove(
"direct")
181 if not config.makePsfMatched:
182 self.outputs.remove(
"psfMatched")
184 if config.select.target != lsst.pipe.tasks.selectImages.PsfWcsSelectImagesTask:
185 self.inputs.remove(
"visitSummary")
188class MakeWarpConfig(pipeBase.PipelineTaskConfig, CoaddBaseTask.ConfigClass,
189 pipelineConnections=MakeWarpConnections):
190 """Config for MakeWarpTask."""
192 warpAndPsfMatch = pexConfig.ConfigurableField(
193 target=WarpAndPsfMatchTask,
194 doc=
"Task to warp and PSF-match calexp",
196 doWrite = pexConfig.Field(
197 doc=
"persist <coaddName>Coadd_<warpType>Warp",
201 bgSubtracted = pexConfig.Field(
202 doc=
"Work with a background subtracted calexp?",
206 coaddPsf = pexConfig.ConfigField(
207 doc=
"Configuration for CoaddPsf",
208 dtype=CoaddPsfConfig,
210 makeDirect = pexConfig.Field(
211 doc=
"Make direct Warp/Coadds",
215 makePsfMatched = pexConfig.Field(
216 doc=
"Make Psf-Matched Warp/Coadd?",
220 doWriteEmptyWarps = pexConfig.Field(
223 doc=
"Write out warps even if they are empty"
225 hasFakes = pexConfig.Field(
226 doc=
"Should be set to True if fake sources have been inserted into the input data.",
230 doApplySkyCorr = pexConfig.Field(
233 doc=
"Apply sky correction?",
235 doApplyFinalizedPsf = pexConfig.Field(
236 doc=
"Whether to apply finalized psf models and aperture correction map.",
240 idGenerator = DetectorVisitIdGeneratorConfig.make_field()
243 CoaddBaseTask.ConfigClass.validate(self)
245 if not self.makePsfMatched
and not self.makeDirect:
246 raise RuntimeError(
"At least one of config.makePsfMatched and config.makeDirect must be True")
249 log.warning(
"Config doPsfMatch deprecated. Setting makePsfMatched=True and makeDirect=False")
250 self.makePsfMatched =
True
251 self.makeDirect =
False
253 def setDefaults(self):
254 CoaddBaseTask.ConfigClass.setDefaults(self)
255 self.warpAndPsfMatch.psfMatch.kernel.active.kernelSize = self.matchingKernelSize
259 """Warp and optionally PSF-Match calexps onto an a common projection.
261 Warp and optionally PSF-Match calexps onto a common projection, by
262 performing the following operations:
263 - Group calexps by visit/run
264 - For each visit, generate a Warp by calling method
@ref run.
265 `run` loops over the visit
's calexps calling
269 ConfigClass = MakeWarpConfig
270 _DefaultName = "makeWarp"
272 def __init__(self, **kwargs):
273 CoaddBaseTask.__init__(self, **kwargs)
274 self.makeSubtask(
"warpAndPsfMatch")
275 if self.config.hasFakes:
276 self.calexpType =
"fakes_calexp"
278 self.calexpType =
"calexp"
280 @utils.inheritDoc(pipeBase.PipelineTask)
281 def runQuantum(self, butlerQC, inputRefs, outputRefs):
285 detectorOrder = [ref.datasetRef.dataId[
'detector']
for ref
in inputRefs.calExpList]
287 inputRefs = reorderRefs(inputRefs, detectorOrder, dataIdKey=
'detector')
290 inputs = butlerQC.get(inputRefs)
294 skyMap = inputs.pop(
"skyMap")
295 quantumDataId = butlerQC.quantum.dataId
296 skyInfo = makeSkyInfo(skyMap, tractId=quantumDataId[
'tract'], patchId=quantumDataId[
'patch'])
299 dataIdList = [ref.datasetRef.dataId
for ref
in inputRefs.calExpList]
302 self.config.idGenerator.apply(dataId).catalog_id
303 for dataId
in dataIdList
306 if self.config.doApplyExternalSkyWcs:
307 if self.config.useGlobalExternalSkyWcs:
308 externalSkyWcsCatalog = inputs.pop(
"externalSkyWcsGlobalCatalog")
310 externalSkyWcsCatalog = inputs.pop(
"externalSkyWcsTractCatalog")
312 externalSkyWcsCatalog =
None
314 if self.config.doApplyExternalPhotoCalib:
315 if self.config.useGlobalExternalPhotoCalib:
316 externalPhotoCalibCatalog = inputs.pop(
"externalPhotoCalibGlobalCatalog")
318 externalPhotoCalibCatalog = inputs.pop(
"externalPhotoCalibTractCatalog")
320 externalPhotoCalibCatalog =
None
322 if self.config.doApplyFinalizedPsf:
323 finalizedPsfApCorrCatalog = inputs.pop(
"finalizedPsfApCorrCatalog")
325 finalizedPsfApCorrCatalog =
None
329 completeIndices = self._prepareCalibratedExposures(
331 externalSkyWcsCatalog=externalSkyWcsCatalog,
332 externalPhotoCalibCatalog=externalPhotoCalibCatalog,
333 finalizedPsfApCorrCatalog=finalizedPsfApCorrCatalog)
334 inputs = self.filterInputs(indices=completeIndices, inputs=inputs)
340 coordList = [skyInfo.wcs.pixelToSky(pos)
for pos
in cornerPosList]
341 goodIndices = self.select.run(**inputs, coordList=coordList, dataIds=dataIdList)
342 inputs = self.filterInputs(indices=goodIndices, inputs=inputs)
345 visitId = dataIdList[0][
"visit"]
347 results = self.run(**inputs, visitId=visitId,
348 ccdIdList=[ccdIdList[i]
for i
in goodIndices],
349 dataIdList=[dataIdList[i]
for i
in goodIndices],
351 if self.config.makeDirect
and results.exposures[
"direct"]
is not None:
352 butlerQC.put(results.exposures[
"direct"], outputRefs.direct)
353 if self.config.makePsfMatched
and results.exposures[
"psfMatched"]
is not None:
354 butlerQC.put(results.exposures[
"psfMatched"], outputRefs.psfMatched)
357 def run(self, calExpList, ccdIdList, skyInfo, visitId=0, dataIdList=None, **kwargs):
358 """Create a Warp from inputs.
360 We iterate over the multiple calexps in a single exposure to construct
361 the warp (previously called a coaddTempExp) of that exposure to the
362 supplied tract/patch.
364 Pixels that receive no pixels are set to NAN; this
is not correct
365 (violates LSST algorithms group policy), but will be fixed up by
366 interpolating after the coaddition.
368 calexpRefList : `list`
369 List of data references
for calexps that (may)
370 overlap the patch of interest.
371 skyInfo : `lsst.pipe.base.Struct`
372 Struct
from `~lsst.pipe.base.coaddBase.makeSkyInfo()`
with
373 geometric information about the patch.
375 Integer identifier
for visit,
for the table that will
376 produce the CoaddPsf.
380 result : `lsst.pipe.base.Struct`
381 Results
as a struct
with attributes:
384 A dictionary containing the warps requested:
385 "direct": direct warp
if ``config.makeDirect``
386 "psfMatched": PSF-matched warp
if ``config.makePsfMatched``
389 warpTypeList = self.getWarpTypeList()
391 totGoodPix = {warpType: 0 for warpType
in warpTypeList}
392 didSetMetadata = {warpType:
False for warpType
in warpTypeList}
393 warps = {warpType: self._prepareEmptyExposure(skyInfo)
for warpType
in warpTypeList}
394 inputRecorder = {warpType: self.inputRecorder.makeCoaddTempExpRecorder(visitId, len(calExpList))
395 for warpType
in warpTypeList}
397 modelPsf = self.config.modelPsf.apply()
if self.config.makePsfMatched
else None
398 if dataIdList
is None:
399 dataIdList = ccdIdList
401 for calExpInd, (calExp, ccdId, dataId)
in enumerate(zip(calExpList, ccdIdList, dataIdList)):
402 self.log.info(
"Processing calexp %d of %d for this Warp: id=%s",
403 calExpInd+1, len(calExpList), dataId)
405 warpedAndMatched = self.warpAndPsfMatch.run(calExp, modelPsf=modelPsf,
406 wcs=skyInfo.wcs, maxBBox=skyInfo.bbox,
407 makeDirect=self.config.makeDirect,
408 makePsfMatched=self.config.makePsfMatched)
409 except Exception
as e:
410 self.log.warning(
"WarpAndPsfMatch failed for calexp %s; skipping it: %s", dataId, e)
413 numGoodPix = {warpType: 0
for warpType
in warpTypeList}
414 for warpType
in warpTypeList:
415 exposure = warpedAndMatched.getDict()[warpType]
418 warp = warps[warpType]
419 if didSetMetadata[warpType]:
420 mimg = exposure.getMaskedImage()
421 mimg *= (warp.getPhotoCalib().getInstFluxAtZeroMagnitude()
422 / exposure.getPhotoCalib().getInstFluxAtZeroMagnitude())
424 numGoodPix[warpType] = coaddUtils.copyGoodPixels(
425 warp.getMaskedImage(), exposure.getMaskedImage(), self.getBadPixelMask())
426 totGoodPix[warpType] += numGoodPix[warpType]
427 self.log.debug(
"Calexp %s has %d good pixels in this patch (%.1f%%) for %s",
428 dataId, numGoodPix[warpType],
429 100.0*numGoodPix[warpType]/skyInfo.bbox.getArea(), warpType)
430 if numGoodPix[warpType] > 0
and not didSetMetadata[warpType]:
431 warp.info.id = exposure.info.id
432 warp.setPhotoCalib(exposure.getPhotoCalib())
433 warp.setFilter(exposure.getFilter())
434 warp.getInfo().setVisitInfo(exposure.getInfo().getVisitInfo())
437 warp.setPsf(exposure.getPsf())
438 didSetMetadata[warpType] =
True
442 inputRecorder[warpType].addCalExp(calExp, ccdId, numGoodPix[warpType])
444 except Exception
as e:
445 self.log.warning(
"Error processing calexp %s; skipping it: %s", dataId, e)
448 for warpType
in warpTypeList:
449 self.log.info(
"%sWarp has %d good pixels (%.1f%%)",
450 warpType, totGoodPix[warpType], 100.0*totGoodPix[warpType]/skyInfo.bbox.getArea())
452 if totGoodPix[warpType] > 0
and didSetMetadata[warpType]:
453 inputRecorder[warpType].finish(warps[warpType], totGoodPix[warpType])
454 if warpType ==
"direct":
455 warps[warpType].setPsf(
456 CoaddPsf(inputRecorder[warpType].coaddInputs.ccds, skyInfo.wcs,
457 self.config.coaddPsf.makeControl()))
459 if not self.config.doWriteEmptyWarps:
461 warps[warpType] =
None
465 result = pipeBase.Struct(exposures=warps)
468 def filterInputs(self, indices, inputs):
469 """Filter task inputs by their indices.
473 indices : `list` [`int`]
474 inputs : `dict` [`list`]
475 A dictionary of input connections to be passed to run.
479 inputs : `dict` [`list`]
480 Task inputs with their lists filtered by indices.
482 for key
in inputs.keys():
484 if isinstance(inputs[key], list):
485 inputs[key] = [inputs[key][ind]
for ind
in indices]
488 def _prepareCalibratedExposures(self, calExpList=[], wcsList=None, backgroundList=None, skyCorrList=None,
489 externalSkyWcsCatalog=None, externalPhotoCalibCatalog=None,
490 finalizedPsfApCorrCatalog=None, **kwargs):
491 """Calibrate and add backgrounds to input calExpList in place.
496 `lsst.daf.butler.DeferredDatasetHandle`]
497 Sequence of calexps to be modified
in place.
499 The WCSs of the calexps
in ``calExpList``. When
500 ``externalSkyCatalog``
is `
None`, these are used to determine
if
501 the calexp should be included
in the warp, namely checking that it
502 is not `
None`. If ``externalSkyCatalog``
is not `
None`, this list
503 will be dynamically updated
with the external sky WCS.
504 backgroundList : `list` [`lsst.afw.math.backgroundList`], optional
505 Sequence of backgrounds to be added back
in if bgSubtracted=
False.
506 skyCorrList : `list` [`lsst.afw.math.backgroundList`], optional
507 Sequence of background corrections to be subtracted
if
510 Exposure catalog
with external skyWcs to be applied
511 if config.doApplyExternalSkyWcs=
True. Catalog uses the detector id
512 for the catalog id, sorted on id
for fast lookup.
514 Exposure catalog
with external photoCalib to be applied
515 if config.doApplyExternalPhotoCalib=
True. Catalog uses the
516 detector id
for the catalog id, sorted on id
for fast lookup.
518 Exposure catalog
with finalized psf models
and aperture correction
519 maps to be applied
if config.doApplyFinalizedPsf=
True. Catalog
520 uses the detector id
for the catalog id, sorted on id
for fast
523 Additional keyword arguments.
527 indices : `list` [`int`]
528 Indices of ``calExpList``
and friends that have valid
531 wcsList = len(calExpList)*[None]
if wcsList
is None else wcsList
532 backgroundList = len(calExpList)*[
None]
if backgroundList
is None else backgroundList
533 skyCorrList = len(calExpList)*[
None]
if skyCorrList
is None else skyCorrList
535 includeCalibVar = self.config.includeCalibVar
538 for index, (calexp, wcs, background, skyCorr)
in enumerate(zip(calExpList,
542 if externalSkyWcsCatalog
is None and wcs
is None:
543 self.log.warning(
"Detector id %d for visit %d has None for skyWcs and will not be "
544 "used in the warp", calexp.dataId[
"detector"], calexp.dataId[
"visit"])
547 if isinstance(calexp, DeferredDatasetHandle):
548 calexp = calexp.get()
550 if not self.config.bgSubtracted:
551 calexp.maskedImage += background.getImage()
553 detectorId = calexp.info.getDetector().getId()
556 if externalPhotoCalibCatalog
is not None:
557 row = externalPhotoCalibCatalog.find(detectorId)
559 self.log.warning(
"Detector id %s not found in externalPhotoCalibCatalog "
560 "and will not be used in the warp.", detectorId)
562 photoCalib = row.getPhotoCalib()
563 if photoCalib
is None:
564 self.log.warning(
"Detector id %s has None for photoCalib in externalPhotoCalibCatalog "
565 "and will not be used in the warp.", detectorId)
567 calexp.setPhotoCalib(photoCalib)
569 photoCalib = calexp.getPhotoCalib()
570 if photoCalib
is None:
571 self.log.warning(
"Detector id %s has None for photoCalib in the calexp "
572 "and will not be used in the warp.", detectorId)
576 if externalSkyWcsCatalog
is not None:
577 row = externalSkyWcsCatalog.find(detectorId)
579 self.log.warning(
"Detector id %s not found in externalSkyWcsCatalog "
580 "and will not be used in the warp.", detectorId)
582 skyWcs = row.getWcs()
583 wcsList[index] = skyWcs
585 self.log.warning(
"Detector id %s has None for skyWcs in externalSkyWcsCatalog "
586 "and will not be used in the warp.", detectorId)
588 calexp.setWcs(skyWcs)
590 skyWcs = calexp.getWcs()
591 wcsList[index] = skyWcs
593 self.log.warning(
"Detector id %s has None for skyWcs in the calexp "
594 "and will not be used in the warp.", detectorId)
598 if finalizedPsfApCorrCatalog
is not None:
599 row = finalizedPsfApCorrCatalog.find(detectorId)
601 self.log.warning(
"Detector id %s not found in finalizedPsfApCorrCatalog "
602 "and will not be used in the warp.", detectorId)
606 self.log.warning(
"Detector id %s has None for psf in finalizedPsfApCorrCatalog "
607 "and will not be used in the warp.", detectorId)
610 apCorrMap = row.getApCorrMap()
611 if apCorrMap
is None:
612 self.log.warning(
"Detector id %s has None for ApCorrMap in finalizedPsfApCorrCatalog "
613 "and will not be used in the warp.", detectorId)
615 calexp.info.setApCorrMap(apCorrMap)
618 if calexp.info.getApCorrMap()
is None:
619 self.log.warning(
"Detector id %s has None for ApCorrMap in the calexp "
620 "and will not be used in the warp.", detectorId)
624 calexp.maskedImage = photoCalib.calibrateImage(calexp.maskedImage,
625 includeScaleUncertainty=includeCalibVar)
626 calexp.maskedImage /= photoCalib.getCalibrationMean()
632 if self.config.doApplySkyCorr:
633 calexp.maskedImage -= skyCorr.getImage()
635 indices.append(index)
636 calExpList[index] = calexp
641 def _prepareEmptyExposure(skyInfo):
642 """Produce an empty exposure for a given patch.
646 skyInfo : `lsst.pipe.base.Struct`
647 Struct from `~lsst.pipe.base.coaddBase.makeSkyInfo()`
with
648 geometric information about the patch.
652 exp : `lsst.afw.image.exposure.ExposureF`
653 An empty exposure
for a given patch.
655 exp = afwImage.ExposureF(skyInfo.bbox, skyInfo.wcs)
656 exp.getMaskedImage().set(numpy.nan, afwImage.Mask
657 .getPlaneBitMask("NO_DATA"), numpy.inf)
660 def getWarpTypeList(self):
661 """Return list of requested warp types per the config.
664 if self.config.makeDirect:
665 warpTypeList.append(
"direct")
666 if self.config.makePsfMatched:
667 warpTypeList.append(
"psfMatched")
671def reorderRefs(inputRefs, outputSortKeyOrder, dataIdKey):
672 """Reorder inputRefs per outputSortKeyOrder.
674 Any inputRefs which are lists will be resorted per specified key e.g.,
675 'detector.' Only iterables will be reordered,
and values can be of type
676 `lsst.pipe.base.connections.DeferredDatasetRef`
or
677 `lsst.daf.butler.core.datasets.ref.DatasetRef`.
679 Returned lists of refs have the same length
as the outputSortKeyOrder.
680 If an outputSortKey
not in the inputRef, then it will be padded
with None.
681 If an inputRef contains an inputSortKey that
is not in the
682 outputSortKeyOrder it will be removed.
686 inputRefs : `lsst.pipe.base.connections.QuantizedConnection`
687 Input references to be reordered
and padded.
688 outputSortKeyOrder : `iterable`
689 Iterable of values to be compared
with inputRef
's dataId[dataIdKey].
691 The data ID key in the dataRefs to compare
with the outputSortKeyOrder.
695 inputRefs : `lsst.pipe.base.connections.QuantizedConnection`
696 Quantized Connection
with sorted DatasetRef values sorted
if iterable.
698 for connectionName, refs
in inputRefs:
699 if isinstance(refs, Iterable):
700 if hasattr(refs[0],
"dataId"):
701 inputSortKeyOrder = [ref.dataId[dataIdKey]
for ref
in refs]
703 inputSortKeyOrder = [ref.datasetRef.dataId[dataIdKey]
for ref
in refs]
704 if inputSortKeyOrder != outputSortKeyOrder:
705 setattr(inputRefs, connectionName,
706 reorderAndPadList(refs, inputSortKeyOrder, outputSortKeyOrder))