LSST Applications g0b6bd0c080+a72a5dd7e6,g1182afd7b4+2a019aa3bb,g17e5ecfddb+2b8207f7de,g1d67935e3f+06cf436103,g38293774b4+ac198e9f13,g396055baef+6a2097e274,g3b44f30a73+6611e0205b,g480783c3b1+98f8679e14,g48ccf36440+89c08d0516,g4b93dc025c+98f8679e14,g5c4744a4d9+a302e8c7f0,g613e996a0d+e1c447f2e0,g6c8d09e9e7+25247a063c,g7271f0639c+98f8679e14,g7a9cd813b8+124095ede6,g9d27549199+a302e8c7f0,ga1cf026fa3+ac198e9f13,ga32aa97882+7403ac30ac,ga786bb30fb+7a139211af,gaa63f70f4e+9994eb9896,gabf319e997+ade567573c,gba47b54d5d+94dc90c3ea,gbec6a3398f+06cf436103,gc6308e37c7+07dd123edb,gc655b1545f+ade567573c,gcc9029db3c+ab229f5caf,gd01420fc67+06cf436103,gd877ba84e5+06cf436103,gdb4cecd868+6f279b5b48,ge2d134c3d5+cc4dbb2e3f,ge448b5faa6+86d1ceac1d,gecc7e12556+98f8679e14,gf3ee170dca+25247a063c,gf4ac96e456+ade567573c,gf9f5ea5b4d+ac198e9f13,gff490e6085+8c2580be5c,w.2022.27
LSST Data Management Base Package
makeCoaddTempExp.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 numpy
23import logging
24
25import lsst.pex.config as pexConfig
26import lsst.daf.persistence as dafPersist
27import lsst.afw.image as afwImage
28import lsst.coadd.utils as coaddUtils
29import lsst.pipe.base as pipeBase
30import lsst.pipe.base.connectionTypes as connectionTypes
31import lsst.utils as utils
32import lsst.geom
33from lsst.meas.algorithms import CoaddPsf, CoaddPsfConfig
34from lsst.skymap import BaseSkyMap
35from lsst.utils.timer import timeMethod
36from .coaddBase import CoaddBaseTask, makeSkyInfo, reorderAndPadList
37from .selectImages import PsfWcsSelectImagesTask
38from .warpAndPsfMatch import WarpAndPsfMatchTask
39from .coaddHelpers import groupPatchExposures, getGroupDataRef
40from collections.abc import Iterable
41
42__all__ = ["MakeCoaddTempExpTask", "MakeWarpTask", "MakeWarpConfig"]
43
44log = logging.getLogger(__name__)
45
46
47class MissingExposureError(Exception):
48 """Raised when data cannot be retrieved for an exposure.
49 When processing patches, sometimes one exposure is missing; this lets us
50 distinguish bewteen that case, and other errors.
51 """
52 pass
53
54
55class MakeCoaddTempExpConfig(CoaddBaseTask.ConfigClass):
56 """Config for MakeCoaddTempExpTask
57 """
58 warpAndPsfMatch = pexConfig.ConfigurableField(
59 target=WarpAndPsfMatchTask,
60 doc="Task to warp and PSF-match calexp",
61 )
62 doWrite = pexConfig.Field(
63 doc="persist <coaddName>Coadd_<warpType>Warp",
64 dtype=bool,
65 default=True,
66 )
67 bgSubtracted = pexConfig.Field(
68 doc="Work with a background subtracted calexp?",
69 dtype=bool,
70 default=True,
71 )
72 coaddPsf = pexConfig.ConfigField(
73 doc="Configuration for CoaddPsf",
74 dtype=CoaddPsfConfig,
75 )
76 makeDirect = pexConfig.Field(
77 doc="Make direct Warp/Coadds",
78 dtype=bool,
79 default=True,
80 )
81 makePsfMatched = pexConfig.Field(
82 doc="Make Psf-Matched Warp/Coadd?",
83 dtype=bool,
84 default=False,
85 )
86
87 doWriteEmptyWarps = pexConfig.Field(
88 dtype=bool,
89 default=False,
90 doc="Write out warps even if they are empty"
91 )
92
93 hasFakes = pexConfig.Field(
94 doc="Should be set to True if fake sources have been inserted into the input data.",
95 dtype=bool,
96 default=False,
97 )
98 doApplySkyCorr = pexConfig.Field(dtype=bool, default=False, doc="Apply sky correction?")
99
100 doApplyFinalizedPsf = pexConfig.Field(
101 doc="Whether to apply finalized psf models and aperture correction map.",
102 dtype=bool,
103 default=True,
104 )
105
106 def validate(self):
107 CoaddBaseTask.ConfigClass.validate(self)
108 if not self.makePsfMatchedmakePsfMatched and not self.makeDirectmakeDirect:
109 raise RuntimeError("At least one of config.makePsfMatched and config.makeDirect must be True")
110 if self.doPsfMatch:
111 # Backwards compatibility.
112 log.warning("Config doPsfMatch deprecated. Setting makePsfMatched=True and makeDirect=False")
113 self.makePsfMatchedmakePsfMatched = True
114 self.makeDirectmakeDirect = False
115
116 def setDefaults(self):
117 CoaddBaseTask.ConfigClass.setDefaults(self)
118 self.warpAndPsfMatchwarpAndPsfMatch.psfMatch.kernel.active.kernelSize = self.matchingKernelSize
119 self.select.retarget(PsfWcsSelectImagesTask)
120
121
127
128
130 r"""!
131 Warp and optionally PSF-Match calexps onto an a common projection.
132
133 @anchor MakeCoaddTempExpTask_
134
135 @section pipe_tasks_makeCoaddTempExp_Contents Contents
136
137 - @ref pipe_tasks_makeCoaddTempExp_Purpose
138 - @ref pipe_tasks_makeCoaddTempExp_Initialize
139 - @ref pipe_tasks_makeCoaddTempExp_IO
140 - @ref pipe_tasks_makeCoaddTempExp_Config
141 - @ref pipe_tasks_makeCoaddTempExp_Debug
142 - @ref pipe_tasks_makeCoaddTempExp_Example
143
144 @section pipe_tasks_makeCoaddTempExp_Purpose Description
145
146 Warp and optionally PSF-Match calexps onto a common projection, by
147 performing the following operations:
148 - Group calexps by visit/run
149 - For each visit, generate a Warp by calling method @ref run.
150 `run` loops over the visit's calexps calling
151 @ref warpAndPsfMatch::WarpAndPsfMatchTask "WarpAndPsfMatchTask"
152 on each visit
153
154 The result is a `directWarp` (and/or optionally a `psfMatchedWarp`).
155
156 @section pipe_tasks_makeCoaddTempExp_Initialize Task Initialization
157
158 @copydoc \_\_init\_\_
159
160 This task has one special keyword argument: passing reuse=True will cause
161 the task to skip the creation of warps that are already present in the
162 output repositories.
163
164 @section pipe_tasks_makeCoaddTempExp_IO Invoking the Task
165
166 This task is primarily designed to be run from the command line.
167
168 The main method is `runDataRef`, which takes a single butler data reference for the patch(es)
169 to process.
170
171 @copydoc run
172
173 WarpType identifies the types of convolutions applied to Warps (previously CoaddTempExps).
174 Only two types are available: direct (for regular Warps/Coadds) and psfMatched
175 (for Warps/Coadds with homogenized PSFs). We expect to add a third type, likelihood,
176 for generating likelihood Coadds with Warps that have been correlated with their own PSF.
177
178 @section pipe_tasks_makeCoaddTempExp_Config Configuration parameters
179
180 See @ref MakeCoaddTempExpConfig and parameters inherited from
181 @link coaddBase::CoaddBaseConfig CoaddBaseConfig @endlink
182
183 @subsection pipe_tasks_MakeCoaddTempExp_psfMatching Guide to PSF-Matching Configs
184
185 To make `psfMatchedWarps`, select `config.makePsfMatched=True`. The subtask
186 @link ip::diffim::modelPsfMatch::ModelPsfMatchTask ModelPsfMatchTask @endlink
187 is responsible for the PSF-Matching, and its config is accessed via `config.warpAndPsfMatch.psfMatch`.
188 The optimal configuration depends on aspects of dataset: the pixel scale, average PSF FWHM and
189 dimensions of the PSF kernel. These configs include the requested model PSF, the matching kernel size,
190 padding of the science PSF thumbnail and spatial sampling frequency of the PSF.
191
192 *Config Guidelines*: The user must specify the size of the model PSF to which to match by setting
193 `config.modelPsf.defaultFwhm` in units of pixels. The appropriate values depends on science case.
194 In general, for a set of input images, this config should equal the FWHM of the visit
195 with the worst seeing. The smallest it should be set to is the median FWHM. The defaults
196 of the other config options offer a reasonable starting point.
197 The following list presents the most common problems that arise from a misconfigured
198 @link ip::diffim::modelPsfMatch::ModelPsfMatchTask ModelPsfMatchTask @endlink
199 and corresponding solutions. All assume the default Alard-Lupton kernel, with configs accessed via
200 ```config.warpAndPsfMatch.psfMatch.kernel['AL']```. Each item in the list is formatted as:
201 Problem: Explanation. *Solution*
202
203 *Troublshooting PSF-Matching Configuration:*
204 - Matched PSFs look boxy: The matching kernel is too small. _Increase the matching kernel size.
205 For example:_
206
207 config.warpAndPsfMatch.psfMatch.kernel['AL'].kernelSize=27 # default 21
208
209 Note that increasing the kernel size also increases runtime.
210 - Matched PSFs look ugly (dipoles, quadropoles, donuts): unable to find good solution
211 for matching kernel. _Provide the matcher with more data by either increasing
212 the spatial sampling by decreasing the spatial cell size,_
213
214 config.warpAndPsfMatch.psfMatch.kernel['AL'].sizeCellX = 64 # default 128
215 config.warpAndPsfMatch.psfMatch.kernel['AL'].sizeCellY = 64 # default 128
216
217 _or increasing the padding around the Science PSF, for example:_
218
219 config.warpAndPsfMatch.psfMatch.autoPadPsfTo=1.6 # default 1.4
220
221 Increasing `autoPadPsfTo` increases the minimum ratio of input PSF dimensions to the
222 matching kernel dimensions, thus increasing the number of pixels available to fit
223 after convolving the PSF with the matching kernel.
224 Optionally, for debugging the effects of padding, the level of padding may be manually
225 controlled by setting turning off the automatic padding and setting the number
226 of pixels by which to pad the PSF:
227
228 config.warpAndPsfMatch.psfMatch.doAutoPadPsf = False # default True
229 config.warpAndPsfMatch.psfMatch.padPsfBy = 6 # pixels. default 0
230
231 - Deconvolution: Matching a large PSF to a smaller PSF produces
232 a telltale noise pattern which looks like ripples or a brain.
233 _Increase the size of the requested model PSF. For example:_
234
235 config.modelPsf.defaultFwhm = 11 # Gaussian sigma in units of pixels.
236
237 - High frequency (sometimes checkered) noise: The matching basis functions are too small.
238 _Increase the width of the Gaussian basis functions. For example:_
239
240 config.warpAndPsfMatch.psfMatch.kernel['AL'].alardSigGauss=[1.5, 3.0, 6.0]
241 # from default [0.7, 1.5, 3.0]
242
243
244 @section pipe_tasks_makeCoaddTempExp_Debug Debug variables
245
246 MakeCoaddTempExpTask has no debug output, but its subtasks do.
247
248 @section pipe_tasks_makeCoaddTempExp_Example A complete example of using MakeCoaddTempExpTask
249
250 This example uses the package ci_hsc to show how MakeCoaddTempExp fits
251 into the larger Data Release Processing.
252 Set up by running:
253
254 setup ci_hsc
255 cd $CI_HSC_DIR
256 # if not built already:
257 python $(which scons) # this will take a while
258
259 The following assumes that `processCcd.py` and `makeSkyMap.py` have previously been run
260 (e.g. by building `ci_hsc` above) to generate a repository of calexps and an
261 output respository with the desired SkyMap. The command,
262
263 makeCoaddTempExp.py $CI_HSC_DIR/DATA --rerun ci_hsc \
264 --id patch=5,4 tract=0 filter=HSC-I \
265 --selectId visit=903988 ccd=16 --selectId visit=903988 ccd=17 \
266 --selectId visit=903988 ccd=23 --selectId visit=903988 ccd=24 \
267 --config doApplyExternalPhotoCalib=False doApplyExternalSkyWcs=False \
268 makePsfMatched=True modelPsf.defaultFwhm=11
269
270 writes a direct and PSF-Matched Warp to
271 - `$CI_HSC_DIR/DATA/rerun/ci_hsc/deepCoadd/HSC-I/0/5,4/warp-HSC-I-0-5,4-903988.fits` and
272 - `$CI_HSC_DIR/DATA/rerun/ci_hsc/deepCoadd/HSC-I/0/5,4/psfMatchedWarp-HSC-I-0-5,4-903988.fits`
273 respectively.
274
275 @note PSF-Matching in this particular dataset would benefit from adding
276 `--configfile ./matchingConfig.py` to
277 the command line arguments where `matchingConfig.py` is defined by:
278
279 echo "
280 config.warpAndPsfMatch.psfMatch.kernel['AL'].kernelSize=27
281 config.warpAndPsfMatch.psfMatch.kernel['AL'].alardSigGauss=[1.5, 3.0, 6.0]" > matchingConfig.py
282
283
284 Add the option `--help` to see more options.
285 """
286 ConfigClass = MakeCoaddTempExpConfig
287 _DefaultName = "makeCoaddTempExp"
288
289 def __init__(self, reuse=False, **kwargs):
290 CoaddBaseTask.__init__(self, **kwargs)
291 self.reusereuse = reuse
292 self.makeSubtask("warpAndPsfMatch")
293 if self.config.hasFakes:
294 self.calexpTypecalexpType = "fakes_calexp"
295 else:
296 self.calexpTypecalexpType = "calexp"
297
298 @timeMethod
299 def runDataRef(self, patchRef, selectDataList=[]):
300 """!
301 Produce @<coaddName>Coadd_@<warpType>Warp images by warping and optionally PSF-matching.
302
303 @param[in] patchRef: data reference for sky map patch. Must include keys "tract", "patch",
304 plus the camera-specific filter key (e.g. "filter" or "band")
305 @param[in] selectDataList list of @ref selectImages::SelectStruct "SelectStruct"
306 to consider for selection
307 @return: dataRefList: a list of data references for the new @<coaddName>Coadd_directWarps
308 if direct or both warp types are requested and @<coaddName>Coadd_psfMatchedWarps
309 if only psfMatched
310 warps are requested.
311
312 @warning: this task assumes that all exposures in a warp (coaddTempExp) have the same filter.
313
314 @warning: this task sets the PhotoCalib of the coaddTempExp to the PhotoCalib of the first calexp
315 with any good pixels in the patch. For a mosaic camera the resulting PhotoCalib should be ignored
316 (assembleCoadd should determine zeropoint scaling without referring to it).
317 """
318 skyInfo = self.getSkyInfogetSkyInfo(patchRef)
319
320 # DataRefs to return are of type *_directWarp unless only *_psfMatchedWarp requested
321 if self.config.makePsfMatched and not self.config.makeDirect:
322 primaryWarpDataset = self.getTempExpDatasetNamegetTempExpDatasetName("psfMatched")
323 else:
324 primaryWarpDataset = self.getTempExpDatasetNamegetTempExpDatasetName("direct")
325
326 calExpRefList = self.selectExposuresselectExposures(patchRef, skyInfo, selectDataList=selectDataList)
327
328 if len(calExpRefList) == 0:
329 self.log.warning("No exposures to coadd for patch %s", patchRef.dataId)
330 return None
331 self.log.info("Selected %d calexps for patch %s", len(calExpRefList), patchRef.dataId)
332 calExpRefList = [calExpRef for calExpRef in calExpRefList if calExpRef.datasetExists(self.calexpTypecalexpType)]
333 self.log.info("Processing %d existing calexps for patch %s", len(calExpRefList), patchRef.dataId)
334
335 groupData = groupPatchExposures(patchRef, calExpRefList, self.getCoaddDatasetNamegetCoaddDatasetName(),
336 primaryWarpDataset)
337 self.log.info("Processing %d warp exposures for patch %s", len(groupData.groups), patchRef.dataId)
338
339 dataRefList = []
340 for i, (tempExpTuple, calexpRefList) in enumerate(groupData.groups.items()):
341 tempExpRef = getGroupDataRef(patchRef.getButler(), primaryWarpDataset,
342 tempExpTuple, groupData.keys)
343 if self.reusereuse and tempExpRef.datasetExists(datasetType=primaryWarpDataset, write=True):
344 self.log.info("Skipping makeCoaddTempExp for %s; output already exists.", tempExpRef.dataId)
345 dataRefList.append(tempExpRef)
346 continue
347 self.log.info("Processing Warp %d/%d: id=%s", i, len(groupData.groups), tempExpRef.dataId)
348
349 # TODO: mappers should define a way to go from the "grouping keys" to a numeric ID (#2776).
350 # For now, we try to get a long integer "visit" key, and if we can't, we just use the index
351 # of the visit in the list.
352 try:
353 visitId = int(tempExpRef.dataId["visit"])
354 except (KeyError, ValueError):
355 visitId = i
356
357 calExpList = []
358 ccdIdList = []
359 dataIdList = []
360
361 for calExpInd, calExpRef in enumerate(calexpRefList):
362 self.log.info("Reading calexp %s of %s for Warp id=%s", calExpInd+1, len(calexpRefList),
363 calExpRef.dataId)
364 try:
365 ccdId = calExpRef.get("ccdExposureId", immediate=True)
366 except Exception:
367 ccdId = calExpInd
368 try:
369 # We augment the dataRef here with the tract, which is harmless for loading things
370 # like calexps that don't need the tract, and necessary for meas_mosaic outputs,
371 # which do.
372 calExpRef = calExpRef.butlerSubset.butler.dataRef(self.calexpTypecalexpType,
373 dataId=calExpRef.dataId,
374 tract=skyInfo.tractInfo.getId())
375 calExp = self.getCalibratedExposuregetCalibratedExposure(calExpRef, bgSubtracted=self.config.bgSubtracted)
376 except Exception as e:
377 self.log.warning("Calexp %s not found; skipping it: %s", calExpRef.dataId, e)
378 continue
379
380 if self.config.doApplySkyCorr:
381 self.applySkyCorrapplySkyCorr(calExpRef, calExp)
382
383 calExpList.append(calExp)
384 ccdIdList.append(ccdId)
385 dataIdList.append(calExpRef.dataId)
386
387 exps = self.runrun(calExpList, ccdIdList, skyInfo, visitId, dataIdList).exposures
388
389 if any(exps.values()):
390 dataRefList.append(tempExpRef)
391 else:
392 self.log.warning("Warp %s could not be created", tempExpRef.dataId)
393
394 if self.config.doWrite:
395 for (warpType, exposure) in exps.items(): # compatible w/ Py3
396 if exposure is not None:
397 self.log.info("Persisting %s", self.getTempExpDatasetNamegetTempExpDatasetName(warpType))
398 tempExpRef.put(exposure, self.getTempExpDatasetNamegetTempExpDatasetName(warpType))
399
400 return dataRefList
401
402 @timeMethod
403 def run(self, calExpList, ccdIdList, skyInfo, visitId=0, dataIdList=None, **kwargs):
404 """Create a Warp from inputs
405
406 We iterate over the multiple calexps in a single exposure to construct
407 the warp (previously called a coaddTempExp) of that exposure to the
408 supplied tract/patch.
409
410 Pixels that receive no pixels are set to NAN; this is not correct
411 (violates LSST algorithms group policy), but will be fixed up by
412 interpolating after the coaddition.
413
414 @param calexpRefList: List of data references for calexps that (may)
415 overlap the patch of interest
416 @param skyInfo: Struct from CoaddBaseTask.getSkyInfo() with geometric
417 information about the patch
418 @param visitId: integer identifier for visit, for the table that will
419 produce the CoaddPsf
420 @return a pipeBase Struct containing:
421 - exposures: a dictionary containing the warps requested:
422 "direct": direct warp if config.makeDirect
423 "psfMatched": PSF-matched warp if config.makePsfMatched
424 """
425 warpTypeList = self.getWarpTypeListgetWarpTypeList()
426
427 totGoodPix = {warpType: 0 for warpType in warpTypeList}
428 didSetMetadata = {warpType: False for warpType in warpTypeList}
429 coaddTempExps = {warpType: self._prepareEmptyExposure_prepareEmptyExposure(skyInfo) for warpType in warpTypeList}
430 inputRecorder = {warpType: self.inputRecorder.makeCoaddTempExpRecorder(visitId, len(calExpList))
431 for warpType in warpTypeList}
432
433 modelPsf = self.config.modelPsf.apply() if self.config.makePsfMatched else None
434 if dataIdList is None:
435 dataIdList = ccdIdList
436
437 for calExpInd, (calExp, ccdId, dataId) in enumerate(zip(calExpList, ccdIdList, dataIdList)):
438 self.log.info("Processing calexp %d of %d for this Warp: id=%s",
439 calExpInd+1, len(calExpList), dataId)
440
441 try:
442 warpedAndMatched = self.warpAndPsfMatch.run(calExp, modelPsf=modelPsf,
443 wcs=skyInfo.wcs, maxBBox=skyInfo.bbox,
444 makeDirect=self.config.makeDirect,
445 makePsfMatched=self.config.makePsfMatched)
446 except Exception as e:
447 self.log.warning("WarpAndPsfMatch failed for calexp %s; skipping it: %s", dataId, e)
448 continue
449 try:
450 numGoodPix = {warpType: 0 for warpType in warpTypeList}
451 for warpType in warpTypeList:
452 exposure = warpedAndMatched.getDict()[warpType]
453 if exposure is None:
454 continue
455 coaddTempExp = coaddTempExps[warpType]
456 if didSetMetadata[warpType]:
457 mimg = exposure.getMaskedImage()
458 mimg *= (coaddTempExp.getPhotoCalib().getInstFluxAtZeroMagnitude()
459 / exposure.getPhotoCalib().getInstFluxAtZeroMagnitude())
460 del mimg
461 numGoodPix[warpType] = coaddUtils.copyGoodPixels(
462 coaddTempExp.getMaskedImage(), exposure.getMaskedImage(), self.getBadPixelMaskgetBadPixelMask())
463 totGoodPix[warpType] += numGoodPix[warpType]
464 self.log.debug("Calexp %s has %d good pixels in this patch (%.1f%%) for %s",
465 dataId, numGoodPix[warpType],
466 100.0*numGoodPix[warpType]/skyInfo.bbox.getArea(), warpType)
467 if numGoodPix[warpType] > 0 and not didSetMetadata[warpType]:
468 coaddTempExp.info.id = exposure.info.id
469 coaddTempExp.setPhotoCalib(exposure.getPhotoCalib())
470 coaddTempExp.setFilter(exposure.getFilter())
471 coaddTempExp.getInfo().setVisitInfo(exposure.getInfo().getVisitInfo())
472 # PSF replaced with CoaddPsf after loop if and only if creating direct warp
473 coaddTempExp.setPsf(exposure.getPsf())
474 didSetMetadata[warpType] = True
475
476 # Need inputRecorder for CoaddApCorrMap for both direct and PSF-matched
477 inputRecorder[warpType].addCalExp(calExp, ccdId, numGoodPix[warpType])
478
479 except Exception as e:
480 self.log.warning("Error processing calexp %s; skipping it: %s", dataId, e)
481 continue
482
483 for warpType in warpTypeList:
484 self.log.info("%sWarp has %d good pixels (%.1f%%)",
485 warpType, totGoodPix[warpType], 100.0*totGoodPix[warpType]/skyInfo.bbox.getArea())
486
487 if totGoodPix[warpType] > 0 and didSetMetadata[warpType]:
488 inputRecorder[warpType].finish(coaddTempExps[warpType], totGoodPix[warpType])
489 if warpType == "direct":
490 coaddTempExps[warpType].setPsf(
491 CoaddPsf(inputRecorder[warpType].coaddInputs.ccds, skyInfo.wcs,
492 self.config.coaddPsf.makeControl()))
493 else:
494 if not self.config.doWriteEmptyWarps:
495 # No good pixels. Exposure still empty
496 coaddTempExps[warpType] = None
497 # NoWorkFound is unnecessary as the downstream tasks will
498 # adjust the quantum accordingly, and it prevents gen2
499 # MakeCoaddTempExp from continuing to loop over visits.
500
501 result = pipeBase.Struct(exposures=coaddTempExps)
502 return result
503
504 def getCalibratedExposure(self, dataRef, bgSubtracted):
505 """Return one calibrated Exposure, possibly with an updated SkyWcs.
506
507 @param[in] dataRef a sensor-level data reference
508 @param[in] bgSubtracted return calexp with background subtracted? If False get the
509 calexp's background background model and add it to the calexp.
510 @return calibrated exposure
511
512 @raises MissingExposureError If data for the exposure is not available.
513
514 If config.doApplyExternalPhotoCalib is `True`, the photometric calibration
515 (`photoCalib`) is taken from `config.externalPhotoCalibName` via the
516 `name_photoCalib` dataset. Otherwise, the photometric calibration is
517 retrieved from the processed exposure. When
518 `config.doApplyExternalSkyWcs` is `True`, the astrometric calibration
519 is taken from `config.externalSkyWcsName` with the `name_wcs` dataset.
520 Otherwise, the astrometric calibration is taken from the processed
521 exposure.
522 """
523 try:
524 exposure = dataRef.get(self.calexpTypecalexpType, immediate=True)
525 except dafPersist.NoResults as e:
526 raise MissingExposureError('Exposure not found: %s ' % str(e)) from e
527
528 if not bgSubtracted:
529 background = dataRef.get("calexpBackground", immediate=True)
530 mi = exposure.getMaskedImage()
531 mi += background.getImage()
532 del mi
533
534 if self.config.doApplyExternalPhotoCalib:
535 source = f"{self.config.externalPhotoCalibName}_photoCalib"
536 self.log.debug("Applying external photoCalib to %s from %s", dataRef.dataId, source)
537 photoCalib = dataRef.get(source)
538 exposure.setPhotoCalib(photoCalib)
539 else:
540 photoCalib = exposure.getPhotoCalib()
541
542 if self.config.doApplyExternalSkyWcs:
543 source = f"{self.config.externalSkyWcsName}_wcs"
544 self.log.debug("Applying external skyWcs to %s from %s", dataRef.dataId, source)
545 skyWcs = dataRef.get(source)
546 exposure.setWcs(skyWcs)
547
548 exposure.maskedImage = photoCalib.calibrateImage(exposure.maskedImage,
549 includeScaleUncertainty=self.config.includeCalibVar)
550 exposure.maskedImage /= photoCalib.getCalibrationMean()
551 # TODO: The images will have a calibration of 1.0 everywhere once RFC-545 is implemented.
552 # exposure.setCalib(afwImage.Calib(1.0))
553 return exposure
554
555 @staticmethod
556 def _prepareEmptyExposure(skyInfo):
557 """Produce an empty exposure for a given patch"""
558 exp = afwImage.ExposureF(skyInfo.bbox, skyInfo.wcs)
559 exp.getMaskedImage().set(numpy.nan, afwImage.Mask
560 .getPlaneBitMask("NO_DATA"), numpy.inf)
561 return exp
562
564 """Return list of requested warp types per the config.
565 """
566 warpTypeList = []
567 if self.config.makeDirect:
568 warpTypeList.append("direct")
569 if self.config.makePsfMatched:
570 warpTypeList.append("psfMatched")
571 return warpTypeList
572
573 def applySkyCorr(self, dataRef, calexp):
574 """Apply correction to the sky background level
575
576 Sky corrections can be generated with the 'skyCorrection.py'
577 executable in pipe_drivers. Because the sky model used by that
578 code extends over the entire focal plane, this can produce
579 better sky subtraction.
580
581 The calexp is updated in-place.
582
583 Parameters
584 ----------
586 Data reference for calexp.
588 Calibrated exposure.
589 """
590 bg = dataRef.get("skyCorr")
591 self.log.debug("Applying sky correction to %s", dataRef.dataId)
592 if isinstance(calexp, afwImage.Exposure):
593 calexp = calexp.getMaskedImage()
594 calexp -= bg.getImage()
595
596
597class MakeWarpConnections(pipeBase.PipelineTaskConnections,
598 dimensions=("tract", "patch", "skymap", "instrument", "visit"),
599 defaultTemplates={"coaddName": "deep",
600 "skyWcsName": "jointcal",
601 "photoCalibName": "fgcm",
602 "calexpType": ""}):
603 calExpList = connectionTypes.Input(
604 doc="Input exposures to be resampled and optionally PSF-matched onto a SkyMap projection/patch",
605 name="{calexpType}calexp",
606 storageClass="ExposureF",
607 dimensions=("instrument", "visit", "detector"),
608 multiple=True,
609 deferLoad=True,
610 )
611 backgroundList = connectionTypes.Input(
612 doc="Input backgrounds to be added back into the calexp if bgSubtracted=False",
613 name="calexpBackground",
614 storageClass="Background",
615 dimensions=("instrument", "visit", "detector"),
616 multiple=True,
617 )
618 skyCorrList = connectionTypes.Input(
619 doc="Input Sky Correction to be subtracted from the calexp if doApplySkyCorr=True",
620 name="skyCorr",
621 storageClass="Background",
622 dimensions=("instrument", "visit", "detector"),
623 multiple=True,
624 )
625 skyMap = connectionTypes.Input(
626 doc="Input definition of geometry/bbox and projection/wcs for warped exposures",
627 name=BaseSkyMap.SKYMAP_DATASET_TYPE_NAME,
628 storageClass="SkyMap",
629 dimensions=("skymap",),
630 )
631 externalSkyWcsTractCatalog = connectionTypes.Input(
632 doc=("Per-tract, per-visit wcs calibrations. These catalogs use the detector "
633 "id for the catalog id, sorted on id for fast lookup."),
634 name="{skyWcsName}SkyWcsCatalog",
635 storageClass="ExposureCatalog",
636 dimensions=("instrument", "visit", "tract"),
637 )
638 externalSkyWcsGlobalCatalog = connectionTypes.Input(
639 doc=("Per-visit wcs calibrations computed globally (with no tract information). "
640 "These catalogs use the detector id for the catalog id, sorted on id for "
641 "fast lookup."),
642 name="{skyWcsName}SkyWcsCatalog",
643 storageClass="ExposureCatalog",
644 dimensions=("instrument", "visit"),
645 )
646 externalPhotoCalibTractCatalog = connectionTypes.Input(
647 doc=("Per-tract, per-visit photometric calibrations. These catalogs use the "
648 "detector id for the catalog id, sorted on id for fast lookup."),
649 name="{photoCalibName}PhotoCalibCatalog",
650 storageClass="ExposureCatalog",
651 dimensions=("instrument", "visit", "tract"),
652 )
653 externalPhotoCalibGlobalCatalog = connectionTypes.Input(
654 doc=("Per-visit photometric calibrations computed globally (with no tract "
655 "information). These catalogs use the detector id for the catalog id, "
656 "sorted on id for fast lookup."),
657 name="{photoCalibName}PhotoCalibCatalog",
658 storageClass="ExposureCatalog",
659 dimensions=("instrument", "visit"),
660 )
661 finalizedPsfApCorrCatalog = connectionTypes.Input(
662 doc=("Per-visit finalized psf models and aperture correction maps. "
663 "These catalogs use the detector id for the catalog id, "
664 "sorted on id for fast lookup."),
665 name="finalized_psf_ap_corr_catalog",
666 storageClass="ExposureCatalog",
667 dimensions=("instrument", "visit"),
668 )
669 direct = connectionTypes.Output(
670 doc=("Output direct warped exposure (previously called CoaddTempExp), produced by resampling ",
671 "calexps onto the skyMap patch geometry."),
672 name="{coaddName}Coadd_directWarp",
673 storageClass="ExposureF",
674 dimensions=("tract", "patch", "skymap", "visit", "instrument"),
675 )
676 psfMatched = connectionTypes.Output(
677 doc=("Output PSF-Matched warped exposure (previously called CoaddTempExp), produced by resampling ",
678 "calexps onto the skyMap patch geometry and PSF-matching to a model PSF."),
679 name="{coaddName}Coadd_psfMatchedWarp",
680 storageClass="ExposureF",
681 dimensions=("tract", "patch", "skymap", "visit", "instrument"),
682 )
683 # TODO DM-28769, have selectImages subtask indicate which connections they need:
684 wcsList = connectionTypes.Input(
685 doc="WCSs of calexps used by SelectImages subtask to determine if the calexp overlaps the patch",
686 name="{calexpType}calexp.wcs",
687 storageClass="Wcs",
688 dimensions=("instrument", "visit", "detector"),
689 multiple=True,
690 )
691 bboxList = connectionTypes.Input(
692 doc="BBoxes of calexps used by SelectImages subtask to determine if the calexp overlaps the patch",
693 name="{calexpType}calexp.bbox",
694 storageClass="Box2I",
695 dimensions=("instrument", "visit", "detector"),
696 multiple=True,
697 )
698 visitSummary = connectionTypes.Input(
699 doc="Consolidated exposure metadata from ConsolidateVisitSummaryTask",
700 name="{calexpType}visitSummary",
701 storageClass="ExposureCatalog",
702 dimensions=("instrument", "visit",),
703 )
704 srcList = connectionTypes.Input(
705 doc="Source catalogs used by PsfWcsSelectImages subtask to further select on PSF stability",
706 name="src",
707 storageClass="SourceCatalog",
708 dimensions=("instrument", "visit", "detector"),
709 multiple=True,
710 )
711
712 def __init__(self, *, config=None):
713 super().__init__(config=config)
714 if config.bgSubtracted:
715 self.inputs.remove("backgroundList")
716 if not config.doApplySkyCorr:
717 self.inputs.remove("skyCorrList")
718 if config.doApplyExternalSkyWcs:
719 if config.useGlobalExternalSkyWcs:
720 self.inputs.remove("externalSkyWcsTractCatalog")
721 else:
722 self.inputs.remove("externalSkyWcsGlobalCatalog")
723 else:
724 self.inputs.remove("externalSkyWcsTractCatalog")
725 self.inputs.remove("externalSkyWcsGlobalCatalog")
726 if config.doApplyExternalPhotoCalib:
727 if config.useGlobalExternalPhotoCalib:
728 self.inputs.remove("externalPhotoCalibTractCatalog")
729 else:
730 self.inputs.remove("externalPhotoCalibGlobalCatalog")
731 else:
732 self.inputs.remove("externalPhotoCalibTractCatalog")
733 self.inputs.remove("externalPhotoCalibGlobalCatalog")
734 if not config.doApplyFinalizedPsf:
735 self.inputs.remove("finalizedPsfApCorrCatalog")
736 if not config.makeDirect:
737 self.outputs.remove("direct")
738 if not config.makePsfMatched:
739 self.outputs.remove("psfMatched")
740 # TODO DM-28769: add connection per selectImages connections
741 if config.select.target != lsst.pipe.tasks.selectImages.PsfWcsSelectImagesTask:
742 self.inputs.remove("visitSummary")
743 self.inputs.remove("srcList")
744 elif not config.select.doLegacyStarSelectionComputation:
745 # Remove backwards-compatibility connections.
746 self.inputs.remove("srcList")
747
748
749class MakeWarpConfig(pipeBase.PipelineTaskConfig, MakeCoaddTempExpConfig,
750 pipelineConnections=MakeWarpConnections):
751
752 def validate(self):
753 super().validate()
754
755
756class MakeWarpTask(MakeCoaddTempExpTask):
757 """Warp and optionally PSF-Match calexps onto an a common projection
758 """
759 ConfigClass = MakeWarpConfig
760 _DefaultName = "makeWarp"
761
762 @utils.inheritDoc(pipeBase.PipelineTask)
763 def runQuantum(self, butlerQC, inputRefs, outputRefs):
764 """
765 Notes
766 ----
767 Construct warps for requested warp type for single epoch
768
769 PipelineTask (Gen3) entry point to warp and optionally PSF-match
770 calexps. This method is analogous to `runDataRef`.
771 """
772 # Obtain the list of input detectors from calExpList. Sort them by
773 # detector order (to ensure reproducibility). Then ensure all input
774 # lists are in the same sorted detector order.
775 detectorOrder = [ref.datasetRef.dataId['detector'] for ref in inputRefs.calExpList]
776 detectorOrder.sort()
777 inputRefs = reorderRefs(inputRefs, detectorOrder, dataIdKey='detector')
778
779 # Read in all inputs.
780 inputs = butlerQC.get(inputRefs)
781
782 # Construct skyInfo expected by `run`. We remove the SkyMap itself
783 # from the dictionary so we can pass it as kwargs later.
784 skyMap = inputs.pop("skyMap")
785 quantumDataId = butlerQC.quantum.dataId
786 skyInfo = makeSkyInfo(skyMap, tractId=quantumDataId['tract'], patchId=quantumDataId['patch'])
787
788 # Construct list of input DataIds expected by `run`
789 dataIdList = [ref.datasetRef.dataId for ref in inputRefs.calExpList]
790 # Construct list of packed integer IDs expected by `run`
791 ccdIdList = [dataId.pack("visit_detector") for dataId in dataIdList]
792
793 # Run the selector and filter out calexps that were not selected
794 # primarily because they do not overlap the patch
795 cornerPosList = lsst.geom.Box2D(skyInfo.bbox).getCorners()
796 coordList = [skyInfo.wcs.pixelToSky(pos) for pos in cornerPosList]
797 goodIndices = self.select.run(**inputs, coordList=coordList, dataIds=dataIdList)
798 inputs = self.filterInputs(indices=goodIndices, inputs=inputs)
799
800 # Read from disk only the selected calexps
801 inputs['calExpList'] = [ref.get() for ref in inputs['calExpList']]
802
803 # Extract integer visitId requested by `run`
804 visits = [dataId['visit'] for dataId in dataIdList]
805 visitId = visits[0]
806
807 if self.config.doApplyExternalSkyWcs:
808 if self.config.useGlobalExternalSkyWcs:
809 externalSkyWcsCatalog = inputs.pop("externalSkyWcsGlobalCatalog")
810 else:
811 externalSkyWcsCatalog = inputs.pop("externalSkyWcsTractCatalog")
812 else:
813 externalSkyWcsCatalog = None
814
815 if self.config.doApplyExternalPhotoCalib:
816 if self.config.useGlobalExternalPhotoCalib:
817 externalPhotoCalibCatalog = inputs.pop("externalPhotoCalibGlobalCatalog")
818 else:
819 externalPhotoCalibCatalog = inputs.pop("externalPhotoCalibTractCatalog")
820 else:
821 externalPhotoCalibCatalog = None
822
823 if self.config.doApplyFinalizedPsf:
824 finalizedPsfApCorrCatalog = inputs.pop("finalizedPsfApCorrCatalog")
825 else:
826 finalizedPsfApCorrCatalog = None
827
828 completeIndices = self.prepareCalibratedExposures(**inputs,
829 externalSkyWcsCatalog=externalSkyWcsCatalog,
830 externalPhotoCalibCatalog=externalPhotoCalibCatalog,
831 finalizedPsfApCorrCatalog=finalizedPsfApCorrCatalog)
832 # Redo the input selection with inputs with complete wcs/photocalib info.
833 inputs = self.filterInputs(indices=completeIndices, inputs=inputs)
834
835 results = self.run(**inputs, visitId=visitId,
836 ccdIdList=[ccdIdList[i] for i in goodIndices],
837 dataIdList=[dataIdList[i] for i in goodIndices],
838 skyInfo=skyInfo)
839 if self.config.makeDirect and results.exposures["direct"] is not None:
840 butlerQC.put(results.exposures["direct"], outputRefs.direct)
841 if self.config.makePsfMatched and results.exposures["psfMatched"] is not None:
842 butlerQC.put(results.exposures["psfMatched"], outputRefs.psfMatched)
843
844 def filterInputs(self, indices, inputs):
845 """Return task inputs with their lists filtered by indices
846
847 Parameters
848 ----------
849 indices : `list` of integers
850 inputs : `dict` of `list` of input connections to be passed to run
851 """
852 for key in inputs.keys():
853 # Only down-select on list inputs
854 if isinstance(inputs[key], list):
855 inputs[key] = [inputs[key][ind] for ind in indices]
856 return inputs
857
858 def prepareCalibratedExposures(self, calExpList, backgroundList=None, skyCorrList=None,
859 externalSkyWcsCatalog=None, externalPhotoCalibCatalog=None,
860 finalizedPsfApCorrCatalog=None,
861 **kwargs):
862 """Calibrate and add backgrounds to input calExpList in place
863
864 Parameters
865 ----------
866 calExpList : `list` of `lsst.afw.image.Exposure`
867 Sequence of calexps to be modified in place
868 backgroundList : `list` of `lsst.afw.math.backgroundList`, optional
869 Sequence of backgrounds to be added back in if bgSubtracted=False
870 skyCorrList : `list` of `lsst.afw.math.backgroundList`, optional
871 Sequence of background corrections to be subtracted if doApplySkyCorr=True
872 externalSkyWcsCatalog : `lsst.afw.table.ExposureCatalog`, optional
873 Exposure catalog with external skyWcs to be applied
874 if config.doApplyExternalSkyWcs=True. Catalog uses the detector id
875 for the catalog id, sorted on id for fast lookup.
876 externalPhotoCalibCatalog : `lsst.afw.table.ExposureCatalog`, optional
877 Exposure catalog with external photoCalib to be applied
878 if config.doApplyExternalPhotoCalib=True. Catalog uses the detector
879 id for the catalog id, sorted on id for fast lookup.
880 finalizedPsfApCorrCatalog : `lsst.afw.table.ExposureCatalog`, optional
881 Exposure catalog with finalized psf models and aperture correction
882 maps to be applied if config.doApplyFinalizedPsf=True. Catalog uses
883 the detector id for the catalog id, sorted on id for fast lookup.
884
885 Returns
886 -------
887 indices : `list` [`int`]
888 Indices of calExpList and friends that have valid photoCalib/skyWcs
889 """
890 backgroundList = len(calExpList)*[None] if backgroundList is None else backgroundList
891 skyCorrList = len(calExpList)*[None] if skyCorrList is None else skyCorrList
892
893 includeCalibVar = self.config.includeCalibVar
894
895 indices = []
896 for index, (calexp, background, skyCorr) in enumerate(zip(calExpList,
897 backgroundList,
898 skyCorrList)):
899 if not self.config.bgSubtracted:
900 calexp.maskedImage += background.getImage()
901
902 detectorId = calexp.getInfo().getDetector().getId()
903
904 # Find the external photoCalib
905 if externalPhotoCalibCatalog is not None:
906 row = externalPhotoCalibCatalog.find(detectorId)
907 if row is None:
908 self.log.warning("Detector id %s not found in externalPhotoCalibCatalog "
909 "and will not be used in the warp.", detectorId)
910 continue
911 photoCalib = row.getPhotoCalib()
912 if photoCalib is None:
913 self.log.warning("Detector id %s has None for photoCalib in externalPhotoCalibCatalog "
914 "and will not be used in the warp.", detectorId)
915 continue
916 calexp.setPhotoCalib(photoCalib)
917 else:
918 photoCalib = calexp.getPhotoCalib()
919 if photoCalib is None:
920 self.log.warning("Detector id %s has None for photoCalib in the calexp "
921 "and will not be used in the warp.", detectorId)
922 continue
923
924 # Find and apply external skyWcs
925 if externalSkyWcsCatalog is not None:
926 row = externalSkyWcsCatalog.find(detectorId)
927 if row is None:
928 self.log.warning("Detector id %s not found in externalSkyWcsCatalog "
929 "and will not be used in the warp.", detectorId)
930 continue
931 skyWcs = row.getWcs()
932 if skyWcs is None:
933 self.log.warning("Detector id %s has None for skyWcs in externalSkyWcsCatalog "
934 "and will not be used in the warp.", detectorId)
935 continue
936 calexp.setWcs(skyWcs)
937 else:
938 skyWcs = calexp.getWcs()
939 if skyWcs is None:
940 self.log.warning("Detector id %s has None for skyWcs in the calexp "
941 "and will not be used in the warp.", detectorId)
942 continue
943
944 # Find and apply finalized psf and aperture correction
945 if finalizedPsfApCorrCatalog is not None:
946 row = finalizedPsfApCorrCatalog.find(detectorId)
947 if row is None:
948 self.log.warning("Detector id %s not found in finalizedPsfApCorrCatalog "
949 "and will not be used in the warp.", detectorId)
950 continue
951 psf = row.getPsf()
952 if psf is None:
953 self.log.warning("Detector id %s has None for psf in finalizedPsfApCorrCatalog "
954 "and will not be used in the warp.", detectorId)
955 continue
956 calexp.setPsf(psf)
957 apCorrMap = row.getApCorrMap()
958 if apCorrMap is None:
959 self.log.warning("Detector id %s has None for ApCorrMap in finalizedPsfApCorrCatalog "
960 "and will not be used in the warp.", detectorId)
961 continue
962 calexp.info.setApCorrMap(apCorrMap)
963
964 # Calibrate the image
965 calexp.maskedImage = photoCalib.calibrateImage(calexp.maskedImage,
966 includeScaleUncertainty=includeCalibVar)
967 calexp.maskedImage /= photoCalib.getCalibrationMean()
968 # TODO: The images will have a calibration of 1.0 everywhere once RFC-545 is implemented.
969 # exposure.setCalib(afwImage.Calib(1.0))
970
971 # Apply skycorr
972 if self.config.doApplySkyCorr:
973 calexp.maskedImage -= skyCorr.getImage()
974
975 indices.append(index)
976
977 return indices
978
979
980def reorderRefs(inputRefs, outputSortKeyOrder, dataIdKey):
981 """Reorder inputRefs per outputSortKeyOrder
982
983 Any inputRefs which are lists will be resorted per specified key e.g.,
984 'detector.' Only iterables will be reordered, and values can be of type
985 `lsst.pipe.base.connections.DeferredDatasetRef` or
986 `lsst.daf.butler.core.datasets.ref.DatasetRef`.
987 Returned lists of refs have the same length as the outputSortKeyOrder.
988 If an outputSortKey not in the inputRef, then it will be padded with None.
989 If an inputRef contains an inputSortKey that is not in the
990 outputSortKeyOrder it will be removed.
991
992 Parameters
993 ----------
994 inputRefs : `lsst.pipe.base.connections.QuantizedConnection`
995 Input references to be reordered and padded.
996 outputSortKeyOrder : iterable
997 Iterable of values to be compared with inputRef's dataId[dataIdKey]
998 dataIdKey : `str`
999 dataIdKey in the dataRefs to compare with the outputSortKeyOrder.
1000
1001 Returns:
1002 --------
1003 inputRefs: `lsst.pipe.base.connections.QuantizedConnection`
1004 Quantized Connection with sorted DatasetRef values sorted if iterable.
1005 """
1006 for connectionName, refs in inputRefs:
1007 if isinstance(refs, Iterable):
1008 if hasattr(refs[0], "dataId"):
1009 inputSortKeyOrder = [ref.dataId[dataIdKey] for ref in refs]
1010 else:
1011 inputSortKeyOrder = [ref.datasetRef.dataId[dataIdKey] for ref in refs]
1012 if inputSortKeyOrder != outputSortKeyOrder:
1013 setattr(inputRefs, connectionName,
1014 reorderAndPadList(refs, inputSortKeyOrder, outputSortKeyOrder))
1015 return inputRefs
A class to contain the data, WCS, and other information needed to describe an image of the sky.
Definition: Exposure.h:72
Represent a 2-dimensional array of bitmask pixels.
Definition: Mask.h:77
A class to manipulate images, masks, and variance as a single object.
Definition: MaskedImage.h:73
Custom catalog class for ExposureRecord/Table.
Definition: Exposure.h:311
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
Base class for coaddition.
Definition: coaddBase.py:141
def getTempExpDatasetName(self, warpType="direct")
Definition: coaddBase.py:208
def selectExposures(self, patchRef, skyInfo=None, selectDataList=[])
Select exposures to coadd.
Definition: coaddBase.py:154
def getCoaddDatasetName(self, warpType="direct")
Definition: coaddBase.py:194
def getSkyInfo(self, patchRef)
Use getSkyInfo to return the skyMap, tract and patch information, wcs and the outer bbox of the patch...
Definition: coaddBase.py:177
def getBadPixelMask(self)
Convenience method to provide the bitmask from the mask plane names.
Definition: coaddBase.py:243
Warp and optionally PSF-Match calexps onto an a common projection.
def getCalibratedExposure(self, dataRef, bgSubtracted)
def run(self, calExpList, ccdIdList, skyInfo, visitId=0, dataIdList=None, **kwargs)
def runDataRef(self, patchRef, selectDataList=[])
Produce <coaddName>Coadd_<warpType>Warp images by warping and optionally PSF-matching.
daf::base::PropertySet * set
Definition: fits.cc:912
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
int copyGoodPixels(lsst::afw::image::Image< ImagePixelT > &destImage, lsst::afw::image::Image< ImagePixelT > const &srcImage)
copy good pixels from one image to another
bool any(CoordinateExpr< N > const &expr) noexcept
Return true if any elements are true.
def run(self, coaddExposures, bbox, wcs, dataIds, **kwargs)
Definition: getTemplate.py:596
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations.
def reorderAndPadList(inputList, inputKeys, outputKeys, padWith=None)
def makeSkyInfo(skyMap, tractId, patchId)
Definition: coaddBase.py:293
def getGroupDataRef(butler, datasetType, groupTuple, keys)
Definition: coaddHelpers.py:99
def groupPatchExposures(patchDataRef, calexpDataRefList, coaddDatasetType="deepCoadd", tempExpDatasetType="deepCoadd_directWarp")
Definition: coaddHelpers.py:60