lsst.pipe.tasks g6a99470703+a85dbcd4de
makeWarp.py
Go to the documentation of this file.
2# LSST Data Management System
3# Copyright 2008, 2009, 2010, 2011, 2012 LSST Corporation.
4#
5# This product includes software developed by the
6# LSST Project (http://www.lsst.org/).
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the LSST License Statement and
19# the GNU General Public License along with this program. If not,
20# see <http://www.lsstcorp.org/LegalNotices/>.
21#
22import logging
23import numpy
24
25import lsst.pex.config as pexConfig
26import lsst.afw.image as afwImage
27import lsst.coadd.utils as coaddUtils
28import lsst.pipe.base as pipeBase
29import lsst.pipe.base.connectionTypes as connectionTypes
30import lsst.utils as utils
31import lsst.geom
32from lsst.meas.algorithms import CoaddPsf, CoaddPsfConfig
33from lsst.skymap import BaseSkyMap
34from lsst.utils.timer import timeMethod
35from .coaddBase import CoaddBaseTask, makeSkyInfo, reorderAndPadList
36from .warpAndPsfMatch import WarpAndPsfMatchTask
37from collections.abc import Iterable
38
39__all__ = ["MakeWarpTask", "MakeWarpConfig"]
40
41log = logging.getLogger(__name__)
42
43
44class MakeWarpConnections(pipeBase.PipelineTaskConnections,
45 dimensions=("tract", "patch", "skymap", "instrument", "visit"),
46 defaultTemplates={"coaddName": "deep",
47 "skyWcsName": "jointcal",
48 "photoCalibName": "fgcm",
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 externalSkyWcsTractCatalog = connectionTypes.Input(
79 doc=("Per-tract, per-visit wcs calibrations. These catalogs use the detector "
80 "id for the catalog id, sorted on id for fast lookup."),
81 name="{skyWcsName}SkyWcsCatalog",
82 storageClass="ExposureCatalog",
83 dimensions=("instrument", "visit", "tract"),
84 )
85 externalSkyWcsGlobalCatalog = connectionTypes.Input(
86 doc=("Per-visit wcs calibrations computed globally (with no tract information). "
87 "These catalogs use the detector id for the catalog id, sorted on id for "
88 "fast lookup."),
89 name="{skyWcsName}SkyWcsCatalog",
90 storageClass="ExposureCatalog",
91 dimensions=("instrument", "visit"),
92 )
93 externalPhotoCalibTractCatalog = connectionTypes.Input(
94 doc=("Per-tract, per-visit photometric calibrations. These catalogs use the "
95 "detector id for the catalog id, sorted on id for fast lookup."),
96 name="{photoCalibName}PhotoCalibCatalog",
97 storageClass="ExposureCatalog",
98 dimensions=("instrument", "visit", "tract"),
99 )
100 externalPhotoCalibGlobalCatalog = connectionTypes.Input(
101 doc=("Per-visit photometric calibrations computed globally (with no tract "
102 "information). These catalogs use the detector id for the catalog id, "
103 "sorted on id for fast lookup."),
104 name="{photoCalibName}PhotoCalibCatalog",
105 storageClass="ExposureCatalog",
106 dimensions=("instrument", "visit"),
107 )
108 finalizedPsfApCorrCatalog = connectionTypes.Input(
109 doc=("Per-visit finalized psf models and aperture correction maps. "
110 "These catalogs use the detector id for the catalog id, "
111 "sorted on id for fast lookup."),
112 name="finalized_psf_ap_corr_catalog",
113 storageClass="ExposureCatalog",
114 dimensions=("instrument", "visit"),
115 )
116 direct = connectionTypes.Output(
117 doc=("Output direct warped exposure (previously called CoaddTempExp), produced by resampling ",
118 "calexps onto the skyMap patch geometry."),
119 name="{coaddName}Coadd_directWarp",
120 storageClass="ExposureF",
121 dimensions=("tract", "patch", "skymap", "visit", "instrument"),
122 )
123 psfMatched = connectionTypes.Output(
124 doc=("Output PSF-Matched warped exposure (previously called CoaddTempExp), produced by resampling ",
125 "calexps onto the skyMap patch geometry and PSF-matching to a model PSF."),
126 name="{coaddName}Coadd_psfMatchedWarp",
127 storageClass="ExposureF",
128 dimensions=("tract", "patch", "skymap", "visit", "instrument"),
129 )
130 # TODO DM-28769, have selectImages subtask indicate which connections they need:
131 wcsList = connectionTypes.Input(
132 doc="WCSs of calexps used by SelectImages subtask to determine if the calexp overlaps the patch",
133 name="{calexpType}calexp.wcs",
134 storageClass="Wcs",
135 dimensions=("instrument", "visit", "detector"),
136 multiple=True,
137 )
138 bboxList = connectionTypes.Input(
139 doc="BBoxes of calexps used by SelectImages subtask to determine if the calexp overlaps the patch",
140 name="{calexpType}calexp.bbox",
141 storageClass="Box2I",
142 dimensions=("instrument", "visit", "detector"),
143 multiple=True,
144 )
145 visitSummary = connectionTypes.Input(
146 doc="Consolidated exposure metadata from ConsolidateVisitSummaryTask",
147 name="{calexpType}visitSummary",
148 storageClass="ExposureCatalog",
149 dimensions=("instrument", "visit",),
150 )
151
152 def __init__(self, *, config=None):
153 super().__init__(config=config)
154 if config.bgSubtracted:
155 self.inputs.remove("backgroundList")
156 if not config.doApplySkyCorr:
157 self.inputs.remove("skyCorrList")
158 if config.doApplyExternalSkyWcs:
159 if config.useGlobalExternalSkyWcs:
160 self.inputs.remove("externalSkyWcsTractCatalog")
161 else:
162 self.inputs.remove("externalSkyWcsGlobalCatalog")
163 else:
164 self.inputs.remove("externalSkyWcsTractCatalog")
165 self.inputs.remove("externalSkyWcsGlobalCatalog")
166 if config.doApplyExternalPhotoCalib:
167 if config.useGlobalExternalPhotoCalib:
168 self.inputs.remove("externalPhotoCalibTractCatalog")
169 else:
170 self.inputs.remove("externalPhotoCalibGlobalCatalog")
171 else:
172 self.inputs.remove("externalPhotoCalibTractCatalog")
173 self.inputs.remove("externalPhotoCalibGlobalCatalog")
174 if not config.doApplyFinalizedPsf:
175 self.inputs.remove("finalizedPsfApCorrCatalog")
176 if not config.makeDirect:
177 self.outputs.remove("direct")
178 if not config.makePsfMatched:
179 self.outputs.remove("psfMatched")
180 # TODO DM-28769: add connection per selectImages connections
181 if config.select.target != lsst.pipe.tasks.selectImages.PsfWcsSelectImagesTask:
182 self.inputs.remove("visitSummary")
183
184
185class MakeWarpConfig(pipeBase.PipelineTaskConfig, CoaddBaseTask.ConfigClass,
186 pipelineConnections=MakeWarpConnections):
187 """Config for MakeWarpTask."""
188 warpAndPsfMatch = pexConfig.ConfigurableField(
189 target=WarpAndPsfMatchTask,
190 doc="Task to warp and PSF-match calexp",
191 )
192 doWrite = pexConfig.Field(
193 doc="persist <coaddName>Coadd_<warpType>Warp",
194 dtype=bool,
195 default=True,
196 )
197 bgSubtracted = pexConfig.Field(
198 doc="Work with a background subtracted calexp?",
199 dtype=bool,
200 default=True,
201 )
202 coaddPsf = pexConfig.ConfigField(
203 doc="Configuration for CoaddPsf",
204 dtype=CoaddPsfConfig,
205 )
206 makeDirect = pexConfig.Field(
207 doc="Make direct Warp/Coadds",
208 dtype=bool,
209 default=True,
210 )
211 makePsfMatched = pexConfig.Field(
212 doc="Make Psf-Matched Warp/Coadd?",
213 dtype=bool,
214 default=False,
215 )
216 doWriteEmptyWarps = pexConfig.Field(
217 dtype=bool,
218 default=False,
219 doc="Write out warps even if they are empty"
220 )
221 hasFakes = pexConfig.Field(
222 doc="Should be set to True if fake sources have been inserted into the input data.",
223 dtype=bool,
224 default=False,
225 )
226 doApplySkyCorr = pexConfig.Field(
227 dtype=bool,
228 default=False,
229 doc="Apply sky correction?",
230 )
231 doApplyFinalizedPsf = pexConfig.Field(
232 doc="Whether to apply finalized psf models and aperture correction map.",
233 dtype=bool,
234 default=True,
235 )
236
237 def validate(self):
238 CoaddBaseTask.ConfigClass.validate(self)
239
240 if not self.makePsfMatched and not self.makeDirect:
241 raise RuntimeError("At least one of config.makePsfMatched and config.makeDirect must be True")
242 if self.doPsfMatch:
243 # Backwards compatibility.
244 log.warning("Config doPsfMatch deprecated. Setting makePsfMatched=True and makeDirect=False")
245 self.makePsfMatched = True
246 self.makeDirect = False
247
248 def setDefaults(self):
249 CoaddBaseTask.ConfigClass.setDefaults(self)
250 self.warpAndPsfMatch.psfMatch.kernel.active.kernelSize = self.matchingKernelSize
251
252
253class MakeWarpTask(CoaddBaseTask):
254 """Warp and optionally PSF-Match calexps onto an a common projection
255 """
256 ConfigClass = MakeWarpConfig
257 _DefaultName = "makeWarp"
258
259 def __init__(self, **kwargs):
260 CoaddBaseTask.__init__(self, **kwargs)
261 self.makeSubtask("warpAndPsfMatch")
262 if self.config.hasFakes:
263 self.calexpType = "fakes_calexp"
264 else:
265 self.calexpType = "calexp"
266
267 @utils.inheritDoc(pipeBase.PipelineTask)
268 def runQuantum(self, butlerQC, inputRefs, outputRefs):
269 """
270 Notes
271 ----
272 Construct warps for requested warp type for single epoch
273 """
274 # Obtain the list of input detectors from calExpList. Sort them by
275 # detector order (to ensure reproducibility). Then ensure all input
276 # lists are in the same sorted detector order.
277 detectorOrder = [ref.datasetRef.dataId['detector'] for ref in inputRefs.calExpList]
278 detectorOrder.sort()
279 inputRefs = reorderRefs(inputRefs, detectorOrder, dataIdKey='detector')
280
281 # Read in all inputs.
282 inputs = butlerQC.get(inputRefs)
283
284 # Construct skyInfo expected by `run`. We remove the SkyMap itself
285 # from the dictionary so we can pass it as kwargs later.
286 skyMap = inputs.pop("skyMap")
287 quantumDataId = butlerQC.quantum.dataId
288 skyInfo = makeSkyInfo(skyMap, tractId=quantumDataId['tract'], patchId=quantumDataId['patch'])
289
290 # Construct list of input DataIds expected by `run`
291 dataIdList = [ref.datasetRef.dataId for ref in inputRefs.calExpList]
292 # Construct list of packed integer IDs expected by `run`
293 ccdIdList = [dataId.pack("visit_detector") for dataId in dataIdList]
294
295 # Run the selector and filter out calexps that were not selected
296 # primarily because they do not overlap the patch
297 cornerPosList = lsst.geom.Box2D(skyInfo.bbox).getCorners()
298 coordList = [skyInfo.wcs.pixelToSky(pos) for pos in cornerPosList]
299 goodIndices = self.select.run(**inputs, coordList=coordList, dataIds=dataIdList)
300 inputs = self.filterInputs(indices=goodIndices, inputs=inputs)
301
302 # Read from disk only the selected calexps
303 inputs['calExpList'] = [ref.get() for ref in inputs['calExpList']]
304
305 # Extract integer visitId requested by `run`
306 visits = [dataId['visit'] for dataId in dataIdList]
307 visitId = visits[0]
308
309 if self.config.doApplyExternalSkyWcs:
310 if self.config.useGlobalExternalSkyWcs:
311 externalSkyWcsCatalog = inputs.pop("externalSkyWcsGlobalCatalog")
312 else:
313 externalSkyWcsCatalog = inputs.pop("externalSkyWcsTractCatalog")
314 else:
315 externalSkyWcsCatalog = None
316
317 if self.config.doApplyExternalPhotoCalib:
318 if self.config.useGlobalExternalPhotoCalib:
319 externalPhotoCalibCatalog = inputs.pop("externalPhotoCalibGlobalCatalog")
320 else:
321 externalPhotoCalibCatalog = inputs.pop("externalPhotoCalibTractCatalog")
322 else:
323 externalPhotoCalibCatalog = None
324
325 if self.config.doApplyFinalizedPsf:
326 finalizedPsfApCorrCatalog = inputs.pop("finalizedPsfApCorrCatalog")
327 else:
328 finalizedPsfApCorrCatalog = None
329
330 completeIndices = self.prepareCalibratedExposures(**inputs,
331 externalSkyWcsCatalog=externalSkyWcsCatalog,
332 externalPhotoCalibCatalog=externalPhotoCalibCatalog,
333 finalizedPsfApCorrCatalog=finalizedPsfApCorrCatalog)
334 # Redo the input selection with inputs with complete wcs/photocalib info.
335 inputs = self.filterInputs(indices=completeIndices, inputs=inputs)
336
337 results = self.run(**inputs, visitId=visitId,
338 ccdIdList=[ccdIdList[i] for i in goodIndices],
339 dataIdList=[dataIdList[i] for i in goodIndices],
340 skyInfo=skyInfo)
341 if self.config.makeDirect and results.exposures["direct"] is not None:
342 butlerQC.put(results.exposures["direct"], outputRefs.direct)
343 if self.config.makePsfMatched and results.exposures["psfMatched"] is not None:
344 butlerQC.put(results.exposures["psfMatched"], outputRefs.psfMatched)
345
346 @timeMethod
347 def run(self, calExpList, ccdIdList, skyInfo, visitId=0, dataIdList=None, **kwargs):
348 """Create a Warp from inputs
349
350 We iterate over the multiple calexps in a single exposure to construct
351 the warp (previously called a coaddTempExp) of that exposure to the
352 supplied tract/patch.
353
354 Pixels that receive no pixels are set to NAN; this is not correct
355 (violates LSST algorithms group policy), but will be fixed up by
356 interpolating after the coaddition.
357
358 @param calexpRefList: List of data references for calexps that (may)
359 overlap the patch of interest
360 @param skyInfo: Struct from CoaddBaseTask.getSkyInfo() with geometric
361 information about the patch
362 @param visitId: integer identifier for visit, for the table that will
363 produce the CoaddPsf
364 @return a pipeBase Struct containing:
365 - exposures: a dictionary containing the warps requested:
366 "direct": direct warp if config.makeDirect
367 "psfMatched": PSF-matched warp if config.makePsfMatched
368 """
369 warpTypeList = self.getWarpTypeList()
370
371 totGoodPix = {warpType: 0 for warpType in warpTypeList}
372 didSetMetadata = {warpType: False for warpType in warpTypeList}
373 warps = {warpType: self._prepareEmptyExposure(skyInfo) for warpType in warpTypeList}
374 inputRecorder = {warpType: self.inputRecorder.makeCoaddTempExpRecorder(visitId, len(calExpList))
375 for warpType in warpTypeList}
376
377 modelPsf = self.config.modelPsf.apply() if self.config.makePsfMatched else None
378 if dataIdList is None:
379 dataIdList = ccdIdList
380
381 for calExpInd, (calExp, ccdId, dataId) in enumerate(zip(calExpList, ccdIdList, dataIdList)):
382 self.log.info("Processing calexp %d of %d for this Warp: id=%s",
383 calExpInd+1, len(calExpList), dataId)
384
385 try:
386 warpedAndMatched = self.warpAndPsfMatch.run(calExp, modelPsf=modelPsf,
387 wcs=skyInfo.wcs, maxBBox=skyInfo.bbox,
388 makeDirect=self.config.makeDirect,
389 makePsfMatched=self.config.makePsfMatched)
390 except Exception as e:
391 self.log.warning("WarpAndPsfMatch failed for calexp %s; skipping it: %s", dataId, e)
392 continue
393 try:
394 numGoodPix = {warpType: 0 for warpType in warpTypeList}
395 for warpType in warpTypeList:
396 exposure = warpedAndMatched.getDict()[warpType]
397 if exposure is None:
398 continue
399 warp = warps[warpType]
400 if didSetMetadata[warpType]:
401 mimg = exposure.getMaskedImage()
402 mimg *= (warp.getPhotoCalib().getInstFluxAtZeroMagnitude()
403 / exposure.getPhotoCalib().getInstFluxAtZeroMagnitude())
404 del mimg
405 numGoodPix[warpType] = coaddUtils.copyGoodPixels(
406 warp.getMaskedImage(), exposure.getMaskedImage(), self.getBadPixelMask())
407 totGoodPix[warpType] += numGoodPix[warpType]
408 self.log.debug("Calexp %s has %d good pixels in this patch (%.1f%%) for %s",
409 dataId, numGoodPix[warpType],
410 100.0*numGoodPix[warpType]/skyInfo.bbox.getArea(), warpType)
411 if numGoodPix[warpType] > 0 and not didSetMetadata[warpType]:
412 warp.info.id = exposure.info.id
413 warp.setPhotoCalib(exposure.getPhotoCalib())
414 warp.setFilter(exposure.getFilter())
415 warp.getInfo().setVisitInfo(exposure.getInfo().getVisitInfo())
416 # PSF replaced with CoaddPsf after loop if and only if creating direct warp
417 warp.setPsf(exposure.getPsf())
418 didSetMetadata[warpType] = True
419
420 # Need inputRecorder for CoaddApCorrMap for both direct and PSF-matched
421 inputRecorder[warpType].addCalExp(calExp, ccdId, numGoodPix[warpType])
422
423 except Exception as e:
424 self.log.warning("Error processing calexp %s; skipping it: %s", dataId, e)
425 continue
426
427 for warpType in warpTypeList:
428 self.log.info("%sWarp has %d good pixels (%.1f%%)",
429 warpType, totGoodPix[warpType], 100.0*totGoodPix[warpType]/skyInfo.bbox.getArea())
430
431 if totGoodPix[warpType] > 0 and didSetMetadata[warpType]:
432 inputRecorder[warpType].finish(warps[warpType], totGoodPix[warpType])
433 if warpType == "direct":
434 warps[warpType].setPsf(
435 CoaddPsf(inputRecorder[warpType].coaddInputs.ccds, skyInfo.wcs,
436 self.config.coaddPsf.makeControl()))
437 else:
438 if not self.config.doWriteEmptyWarps:
439 # No good pixels. Exposure still empty
440 warps[warpType] = None
441 # NoWorkFound is unnecessary as the downstream tasks will
442 # adjust the quantum accordingly.
443
444 result = pipeBase.Struct(exposures=warps)
445 return result
446
447 def filterInputs(self, indices, inputs):
448 """Return task inputs with their lists filtered by indices
449
450 Parameters
451 ----------
452 indices : `list` of integers
453 inputs : `dict` of `list` of input connections to be passed to run
454 """
455 for key in inputs.keys():
456 # Only down-select on list inputs
457 if isinstance(inputs[key], list):
458 inputs[key] = [inputs[key][ind] for ind in indices]
459 return inputs
460
461 def prepareCalibratedExposures(self, calExpList, backgroundList=None, skyCorrList=None,
462 externalSkyWcsCatalog=None, externalPhotoCalibCatalog=None,
463 finalizedPsfApCorrCatalog=None,
464 **kwargs):
465 """Calibrate and add backgrounds to input calExpList in place
466
467 Parameters
468 ----------
469 calExpList : `list` of `lsst.afw.image.Exposure`
470 Sequence of calexps to be modified in place
471 backgroundList : `list` of `lsst.afw.math.backgroundList`, optional
472 Sequence of backgrounds to be added back in if bgSubtracted=False
473 skyCorrList : `list` of `lsst.afw.math.backgroundList`, optional
474 Sequence of background corrections to be subtracted if doApplySkyCorr=True
475 externalSkyWcsCatalog : `lsst.afw.table.ExposureCatalog`, optional
476 Exposure catalog with external skyWcs to be applied
477 if config.doApplyExternalSkyWcs=True. Catalog uses the detector id
478 for the catalog id, sorted on id for fast lookup.
479 externalPhotoCalibCatalog : `lsst.afw.table.ExposureCatalog`, optional
480 Exposure catalog with external photoCalib to be applied
481 if config.doApplyExternalPhotoCalib=True. Catalog uses the detector
482 id for the catalog id, sorted on id for fast lookup.
483 finalizedPsfApCorrCatalog : `lsst.afw.table.ExposureCatalog`, optional
484 Exposure catalog with finalized psf models and aperture correction
485 maps to be applied if config.doApplyFinalizedPsf=True. Catalog uses
486 the detector id for the catalog id, sorted on id for fast lookup.
487
488 Returns
489 -------
490 indices : `list` [`int`]
491 Indices of calExpList and friends that have valid photoCalib/skyWcs
492 """
493 backgroundList = len(calExpList)*[None] if backgroundList is None else backgroundList
494 skyCorrList = len(calExpList)*[None] if skyCorrList is None else skyCorrList
495
496 includeCalibVar = self.config.includeCalibVar
497
498 indices = []
499 for index, (calexp, background, skyCorr) in enumerate(zip(calExpList,
500 backgroundList,
501 skyCorrList)):
502 if not self.config.bgSubtracted:
503 calexp.maskedImage += background.getImage()
504
505 detectorId = calexp.getInfo().getDetector().getId()
506
507 # Find the external photoCalib
508 if externalPhotoCalibCatalog is not None:
509 row = externalPhotoCalibCatalog.find(detectorId)
510 if row is None:
511 self.log.warning("Detector id %s not found in externalPhotoCalibCatalog "
512 "and will not be used in the warp.", detectorId)
513 continue
514 photoCalib = row.getPhotoCalib()
515 if photoCalib is None:
516 self.log.warning("Detector id %s has None for photoCalib in externalPhotoCalibCatalog "
517 "and will not be used in the warp.", detectorId)
518 continue
519 calexp.setPhotoCalib(photoCalib)
520 else:
521 photoCalib = calexp.getPhotoCalib()
522 if photoCalib is None:
523 self.log.warning("Detector id %s has None for photoCalib in the calexp "
524 "and will not be used in the warp.", detectorId)
525 continue
526
527 # Find and apply external skyWcs
528 if externalSkyWcsCatalog is not None:
529 row = externalSkyWcsCatalog.find(detectorId)
530 if row is None:
531 self.log.warning("Detector id %s not found in externalSkyWcsCatalog "
532 "and will not be used in the warp.", detectorId)
533 continue
534 skyWcs = row.getWcs()
535 if skyWcs is None:
536 self.log.warning("Detector id %s has None for skyWcs in externalSkyWcsCatalog "
537 "and will not be used in the warp.", detectorId)
538 continue
539 calexp.setWcs(skyWcs)
540 else:
541 skyWcs = calexp.getWcs()
542 if skyWcs is None:
543 self.log.warning("Detector id %s has None for skyWcs in the calexp "
544 "and will not be used in the warp.", detectorId)
545 continue
546
547 # Find and apply finalized psf and aperture correction
548 if finalizedPsfApCorrCatalog is not None:
549 row = finalizedPsfApCorrCatalog.find(detectorId)
550 if row is None:
551 self.log.warning("Detector id %s not found in finalizedPsfApCorrCatalog "
552 "and will not be used in the warp.", detectorId)
553 continue
554 psf = row.getPsf()
555 if psf is None:
556 self.log.warning("Detector id %s has None for psf in finalizedPsfApCorrCatalog "
557 "and will not be used in the warp.", detectorId)
558 continue
559 calexp.setPsf(psf)
560 apCorrMap = row.getApCorrMap()
561 if apCorrMap is None:
562 self.log.warning("Detector id %s has None for ApCorrMap in finalizedPsfApCorrCatalog "
563 "and will not be used in the warp.", detectorId)
564 continue
565 calexp.info.setApCorrMap(apCorrMap)
566
567 # Calibrate the image
568 calexp.maskedImage = photoCalib.calibrateImage(calexp.maskedImage,
569 includeScaleUncertainty=includeCalibVar)
570 calexp.maskedImage /= photoCalib.getCalibrationMean()
571 # TODO: The images will have a calibration of 1.0 everywhere once RFC-545 is implemented.
572 # exposure.setCalib(afwImage.Calib(1.0))
573
574 # Apply skycorr
575 if self.config.doApplySkyCorr:
576 calexp.maskedImage -= skyCorr.getImage()
577
578 indices.append(index)
579
580 return indices
581
582 @staticmethod
583 def _prepareEmptyExposure(skyInfo):
584 """Produce an empty exposure for a given patch"""
585 exp = afwImage.ExposureF(skyInfo.bbox, skyInfo.wcs)
586 exp.getMaskedImage().set(numpy.nan, afwImage.Mask
587 .getPlaneBitMask("NO_DATA"), numpy.inf)
588 return exp
589
590 def getWarpTypeList(self):
591 """Return list of requested warp types per the config.
592 """
593 warpTypeList = []
594 if self.config.makeDirect:
595 warpTypeList.append("direct")
596 if self.config.makePsfMatched:
597 warpTypeList.append("psfMatched")
598 return warpTypeList
599
600
601def reorderRefs(inputRefs, outputSortKeyOrder, dataIdKey):
602 """Reorder inputRefs per outputSortKeyOrder
603
604 Any inputRefs which are lists will be resorted per specified key e.g.,
605 'detector.' Only iterables will be reordered, and values can be of type
606 `lsst.pipe.base.connections.DeferredDatasetRef` or
607 `lsst.daf.butler.core.datasets.ref.DatasetRef`.
608 Returned lists of refs have the same length as the outputSortKeyOrder.
609 If an outputSortKey not in the inputRef, then it will be padded with None.
610 If an inputRef contains an inputSortKey that is not in the
611 outputSortKeyOrder it will be removed.
612
613 Parameters
614 ----------
615 inputRefs : `lsst.pipe.base.connections.QuantizedConnection`
616 Input references to be reordered and padded.
617 outputSortKeyOrder : iterable
618 Iterable of values to be compared with inputRef's dataId[dataIdKey]
619 dataIdKey : `str`
620 dataIdKey in the dataRefs to compare with the outputSortKeyOrder.
621
622 Returns:
623 --------
624 inputRefs: `lsst.pipe.base.connections.QuantizedConnection`
625 Quantized Connection with sorted DatasetRef values sorted if iterable.
626 """
627 for connectionName, refs in inputRefs:
628 if isinstance(refs, Iterable):
629 if hasattr(refs[0], "dataId"):
630 inputSortKeyOrder = [ref.dataId[dataIdKey] for ref in refs]
631 else:
632 inputSortKeyOrder = [ref.datasetRef.dataId[dataIdKey] for ref in refs]
633 if inputSortKeyOrder != outputSortKeyOrder:
634 setattr(inputRefs, connectionName,
635 reorderAndPadList(refs, inputSortKeyOrder, outputSortKeyOrder))
636 return inputRefs
Base class for coaddition.
Definition: coaddBase.py:131
def reorderAndPadList(inputList, inputKeys, outputKeys, padWith=None)
Definition: coaddBase.py:256
def makeSkyInfo(skyMap, tractId, patchId)
Definition: coaddBase.py:199