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