25from scipy
import ndimage
30from lsst.daf.butler
import DeferredDatasetHandle
37from lsst.utils.timer
import timeMethod
38from .assembleCoadd
import (AssembleCoaddConnections,
40 CompareWarpAssembleCoaddConfig,
41 CompareWarpAssembleCoaddTask)
42from .coaddBase
import makeSkyInfo
43from .measurePsf
import MeasurePsfTask
45__all__ = [
"DcrAssembleCoaddConnections",
"DcrAssembleCoaddTask",
"DcrAssembleCoaddConfig"]
49 dimensions=(
"tract",
"patch",
"band",
"skymap"),
50 defaultTemplates={
"inputWarpName":
"deep",
51 "inputCoaddName":
"deep",
52 "outputCoaddName":
"dcr",
56 inputWarps = pipeBase.connectionTypes.Input(
57 doc=(
"Input list of warps to be assembled i.e. stacked."
58 "Note that this will often be different than the inputCoaddName."
59 "WarpType (e.g. direct, psfMatched) is controlled by the warpType config parameter"),
60 name=
"{inputWarpName}Coadd_{warpType}Warp",
61 storageClass=
"ExposureF",
62 dimensions=(
"tract",
"patch",
"skymap",
"visit",
"instrument"),
66 templateExposure = pipeBase.connectionTypes.Input(
67 doc=
"Input coadded exposure, produced by previous call to AssembleCoadd",
68 name=
"{fakesType}{inputCoaddName}Coadd{warpTypeSuffix}",
69 storageClass=
"ExposureF",
70 dimensions=(
"tract",
"patch",
"skymap",
"band"),
72 dcrCoadds = pipeBase.connectionTypes.Output(
73 doc=
"Output coadded exposure, produced by stacking input warps",
74 name=
"{fakesType}{outputCoaddName}Coadd{warpTypeSuffix}",
75 storageClass=
"ExposureF",
76 dimensions=(
"tract",
"patch",
"skymap",
"band",
"subfilter"),
79 dcrNImages = pipeBase.connectionTypes.Output(
80 doc=
"Output image of number of input images per pixel",
81 name=
"{outputCoaddName}Coadd_nImage",
82 storageClass=
"ImageU",
83 dimensions=(
"tract",
"patch",
"skymap",
"band",
"subfilter"),
87 def __init__(self, *, config=None):
88 super().__init__(config=config)
89 if not config.doWrite:
90 self.outputs.remove(
"dcrCoadds")
91 if not config.doNImage:
92 self.outputs.remove(
"dcrNImages")
94 self.outputs.remove(
"coaddExposure")
95 self.outputs.remove(
"nImage")
99 pipelineConnections=DcrAssembleCoaddConnections):
100 dcrNumSubfilters = pexConfig.Field(
102 doc=
"Number of sub-filters to forward model chromatic effects to fit the supplied exposures.",
105 maxNumIter = pexConfig.Field(
108 doc=
"Maximum number of iterations of forward modeling.",
111 minNumIter = pexConfig.Field(
114 doc=
"Minimum number of iterations of forward modeling.",
117 convergenceThreshold = pexConfig.Field(
119 doc=
"Target relative change in convergence between iterations of forward modeling.",
122 useConvergence = pexConfig.Field(
124 doc=
"Use convergence test as a forward modeling end condition?"
125 "If not set, skips calculating convergence and runs for ``maxNumIter`` iterations",
128 baseGain = pexConfig.Field(
131 doc=
"Relative weight to give the new solution vs. the last solution when updating the model."
132 "A value of 1.0 gives equal weight to both solutions."
133 "Small values imply slower convergence of the solution, but can "
134 "help prevent overshooting and failures in the fit."
135 "If ``baseGain`` is None, a conservative gain "
136 "will be calculated from the number of subfilters. ",
139 useProgressiveGain = pexConfig.Field(
141 doc=
"Use a gain that slowly increases above ``baseGain`` to accelerate convergence? "
142 "When calculating the next gain, we use up to 5 previous gains and convergence values."
143 "Can be set to False to force the model to change at the rate of ``baseGain``. ",
146 doAirmassWeight = pexConfig.Field(
148 doc=
"Weight exposures by airmass? Useful if there are relatively few high-airmass observations.",
151 modelWeightsWidth = pexConfig.Field(
153 doc=
"Width of the region around detected sources to include in the DcrModel.",
156 useModelWeights = pexConfig.Field(
158 doc=
"Width of the region around detected sources to include in the DcrModel.",
161 splitSubfilters = pexConfig.Field(
163 doc=
"Calculate DCR for two evenly-spaced wavelengths in each subfilter."
164 "Instead of at the midpoint",
167 splitThreshold = pexConfig.Field(
169 doc=
"Minimum DCR difference within a subfilter to use ``splitSubfilters``, in pixels."
170 "Set to 0 to always split the subfilters.",
173 regularizeModelIterations = pexConfig.Field(
175 doc=
"Maximum relative change of the model allowed between iterations."
176 "Set to zero to disable.",
179 regularizeModelFrequency = pexConfig.Field(
181 doc=
"Maximum relative change of the model allowed between subfilters."
182 "Set to zero to disable.",
185 convergenceMaskPlanes = pexConfig.ListField(
187 default=[
"DETECTED"],
188 doc=
"Mask planes to use to calculate convergence."
190 regularizationWidth = pexConfig.Field(
193 doc=
"Minimum radius of a region to include in regularization, in pixels."
195 imageInterpOrder = pexConfig.Field(
197 doc=
"The order of the spline interpolation used to shift the image plane.",
200 accelerateModel = pexConfig.Field(
202 doc=
"Factor to amplify the differences between model planes by to speed convergence.",
205 doCalculatePsf = pexConfig.Field(
207 doc=
"Set to detect stars and recalculate the PSF from the final coadd."
208 "Otherwise the PSF is estimated from a selection of the best input exposures",
211 detectPsfSources = pexConfig.ConfigurableField(
212 target=measAlg.SourceDetectionTask,
213 doc=
"Task to detect sources for PSF measurement, if ``doCalculatePsf`` is set.",
215 measurePsfSources = pexConfig.ConfigurableField(
216 target=SingleFrameMeasurementTask,
217 doc=
"Task to measure sources for PSF measurement, if ``doCalculatePsf`` is set."
219 measurePsf = pexConfig.ConfigurableField(
220 target=MeasurePsfTask,
221 doc=
"Task to measure the PSF of the coadd, if ``doCalculatePsf`` is set.",
223 effectiveWavelength = pexConfig.Field(
224 doc=
"Effective wavelength of the filter, in nm."
225 "Required if transmission curves aren't used."
226 "Support for using transmission curves is to be added in DM-13668.",
229 bandwidth = pexConfig.Field(
230 doc=
"Bandwidth of the physical filter, in nm."
231 "Required if transmission curves aren't used."
232 "Support for using transmission curves is to be added in DM-13668.",
237 CompareWarpAssembleCoaddConfig.setDefaults(self)
238 self.assembleStaticSkyModel.retarget(CompareWarpAssembleCoaddTask)
240 self.assembleStaticSkyModel.warpType = self.warpType
242 self.assembleStaticSkyModel.doNImage =
False
243 self.assembleStaticSkyModel.doWrite =
False
244 self.detectPsfSources.returnOriginalFootprints =
False
245 self.detectPsfSources.thresholdPolarity =
"positive"
247 self.detectPsfSources.thresholdValue = 50
248 self.detectPsfSources.nSigmaToGrow = 2
250 self.detectPsfSources.minPixels = 25
252 self.detectPsfSources.thresholdType =
"pixel_stdev"
255 self.measurePsf.starSelector[
"objectSize"].doFluxLimit =
False
257 if (self.doCalculatePsf
and self.measurePsf.psfDeterminer.name ==
"piff"
258 and self.psfDeterminer[
"piff"].kernelSize > self.makePsfCandidates.kernelSize):
259 self.makePsfCandidates.kernelSize = self.psfDeterminer[
"piff"].kernelSize
263 """Assemble DCR coadded images from a set of warps.
268 The number of pixels to grow each subregion by to allow for DCR.
272 As
with AssembleCoaddTask, we want to assemble a coadded image
from a set of
273 Warps (also called coadded temporary exposures), including the effects of
274 Differential Chromatic Refraction (DCR).
275 For full details of the mathematics
and algorithm, please see
276 DMTN-037: DCR-matched template generation (https://dmtn-037.lsst.io).
278 This Task produces a DCR-corrected deepCoadd,
as well
as a dcrCoadd
for
279 each subfilter used
in the iterative calculation.
280 It begins by dividing the bandpass-defining filter into N equal bandwidth
281 "subfilters",
and divides the flux
in each pixel
from an initial coadd
282 equally into each
as a
"dcrModel". Because the airmass
and parallactic
283 angle of each individual exposure
is known, we can calculate the shift
284 relative to the center of the band
in each subfilter due to DCR. For each
285 exposure we apply this shift
as a linear transformation to the dcrModels
286 and stack the results to produce a DCR-matched exposure. The matched
287 exposures are subtracted
from the input exposures to produce a set of
288 residual images,
and these residuals are reverse shifted
for each
289 exposures
' subfilters and stacked. The shifted and stacked residuals are
290 added to the dcrModels to produce a new estimate of the flux in each pixel
291 within each subfilter. The dcrModels are solved
for iteratively, which
292 continues until the solution
from a new iteration improves by less than
293 a set percentage,
or a maximum number of iterations
is reached.
294 Two forms of regularization are employed to reduce unphysical results.
295 First, the new solution
is averaged
with the solution
from the previous
296 iteration, which mitigates oscillating solutions where the model
297 overshoots
with alternating very high
and low values.
298 Second, a common degeneracy when the data have a limited range of airmass
or
299 parallactic angle values
is for one subfilter to be fit
with very low
or
300 negative values,
while another subfilter
is fit
with very high values. This
301 typically appears
in the form of holes next to sources
in one subfilter,
302 and corresponding extended wings
in another. Because each subfilter has
303 a narrow bandwidth we assume that physical sources that are above the noise
304 level will
not vary
in flux by more than a factor of `frequencyClampFactor`
305 between subfilters,
and pixels that have flux deviations larger than that
306 factor will have the excess flux distributed evenly among all subfilters.
307 If `splitSubfilters`
is set, then each subfilter will be further sub-
308 divided during the forward modeling step (only). This approximates using
309 a higher number of subfilters that may be necessary
for high airmass
310 observations, but does
not increase the number of free parameters
in the
311 fit. This
is needed when there are high airmass observations which would
312 otherwise have significant DCR even within a subfilter. Because calculating
313 the shifted images takes most of the time, splitting the subfilters
is
314 turned off by way of the `splitThreshold` option
for low-airmass
315 observations that do
not suffer
from DCR within a subfilter.
318 ConfigClass = DcrAssembleCoaddConfig
319 _DefaultName = "dcrAssembleCoadd"
321 def __init__(self, *args, **kwargs):
322 super().__init__(*args, **kwargs)
323 if self.config.doCalculatePsf:
324 self.schema = afwTable.SourceTable.makeMinimalSchema()
325 self.makeSubtask(
"detectPsfSources", schema=self.schema)
326 self.makeSubtask(
"measurePsfSources", schema=self.schema)
327 self.makeSubtask(
"measurePsf", schema=self.schema)
329 @utils.inheritDoc(pipeBase.PipelineTask)
330 def runQuantum(self, butlerQC, inputRefs, outputRefs):
335 Assemble a coadd from a set of Warps.
337 PipelineTask (Gen3) entry point to Coadd a set of Warps.
338 Analogous to `runDataRef`, it prepares all the data products to be
339 passed to `run`,
and processes the results before returning a struct
340 of results to be written out. AssembleCoadd cannot fit all Warps
in memory.
341 Therefore, its inputs are accessed subregion by subregion
342 by the Gen3 `DeferredDatasetHandle` that
is analagous to the Gen2
344 correspond to an update
in `runDataRef`
while both entry points
347 inputData = butlerQC.get(inputRefs)
351 skyMap = inputData[
"skyMap"]
352 outputDataId = butlerQC.quantum.dataId
355 tractId=outputDataId[
'tract'],
356 patchId=outputDataId[
'patch'])
360 warpRefList = inputData[
'inputWarps']
362 inputs = self.prepareInputs(warpRefList)
363 self.log.
info(
"Found %d %s", len(inputs.tempExpRefList),
364 self.getTempExpDatasetName(self.warpType))
365 if len(inputs.tempExpRefList) == 0:
366 self.log.
warning(
"No coadd temporary exposures found")
369 supplementaryData = self.makeSupplementaryDataGen3(butlerQC, inputRefs, outputRefs)
370 retStruct = self.run(inputData[
'skyInfo'], inputs.tempExpRefList, inputs.imageScalerList,
371 inputs.weightList, supplementaryData=supplementaryData)
373 inputData.setdefault(
'brightObjectMask',
None)
374 for subfilter
in range(self.config.dcrNumSubfilters):
376 retStruct.dcrCoadds[subfilter].setPsf(retStruct.coaddExposure.getPsf())
377 self.processResults(retStruct.dcrCoadds[subfilter], inputData[
'brightObjectMask'], outputDataId)
379 if self.config.doWrite:
380 butlerQC.put(retStruct, outputRefs)
384 def runDataRef(self, dataRef, selectDataList=None, warpRefList=None):
385 """Assemble a coadd from a set of warps.
387 Coadd a set of Warps. Compute weights to be applied to each Warp and
388 find scalings to match the photometric zeropoint to a reference Warp.
389 Assemble the Warps using run method.
390 Forward model chromatic effects across multiple subfilters,
391 and subtract
from the input Warps to build sets of residuals.
392 Use the residuals to construct a new ``DcrModel``
for each subfilter,
393 and iterate until the model converges.
394 Interpolate over NaNs
and optionally write the coadd to disk.
395 Return the coadded exposure.
400 Data reference defining the patch
for coaddition
and the
403 List of data references to warps. Data to be coadded will be
404 selected
from this list based on overlap
with the patch defined by
409 results : `lsst.pipe.base.Struct`
410 The Struct contains the following fields:
413 - ``nImage``: exposure count image (`lsst.afw.image.ImageU`)
414 - ``dcrCoadds``: `list` of coadded exposures
for each subfilter
415 - ``dcrNImages``: `list` of exposure count images
for each subfilter
417 if (selectDataList
is None and warpRefList
is None)
or (selectDataList
and warpRefList):
418 raise RuntimeError(
"runDataRef must be supplied either a selectDataList or warpRefList")
420 skyInfo = self.getSkyInfo(dataRef)
421 if warpRefList
is None:
422 calExpRefList = self.selectExposures(dataRef, skyInfo, selectDataList=selectDataList)
423 if len(calExpRefList) == 0:
424 self.log.
warning(
"No exposures to coadd")
426 self.log.
info(
"Coadding %d exposures", len(calExpRefList))
428 warpRefList = self.getTempExpRefList(dataRef, calExpRefList)
430 inputData = self.prepareInputs(warpRefList)
431 self.log.
info(
"Found %d %s", len(inputData.tempExpRefList),
432 self.getTempExpDatasetName(self.warpType))
433 if len(inputData.tempExpRefList) == 0:
434 self.log.
warning(
"No coadd temporary exposures found")
437 supplementaryData = self.makeSupplementaryData(dataRef, warpRefList=inputData.tempExpRefList)
439 results = self.run(skyInfo, inputData.tempExpRefList, inputData.imageScalerList,
440 inputData.weightList, supplementaryData=supplementaryData)
442 self.log.
warning(
"Could not construct DcrModel for patch %s: no data to coadd.",
443 skyInfo.patchInfo.getIndex())
446 if self.config.doCalculatePsf:
447 self.measureCoaddPsf(results.coaddExposure)
448 brightObjects = self.readBrightObjectMasks(dataRef)
if self.config.doMaskBrightObjects
else None
449 for subfilter
in range(self.config.dcrNumSubfilters):
451 results.dcrCoadds[subfilter].setPsf(results.coaddExposure.getPsf())
452 self.processResults(results.dcrCoadds[subfilter],
453 brightObjectMasks=brightObjects, dataId=dataRef.dataId)
454 if self.config.doWrite:
455 self.log.
info(
"Persisting dcrCoadd")
456 dataRef.put(results.dcrCoadds[subfilter],
"dcrCoadd", subfilter=subfilter,
457 numSubfilters=self.config.dcrNumSubfilters)
458 if self.config.doNImage
and results.dcrNImages
is not None:
459 dataRef.put(results.dcrNImages[subfilter],
"dcrCoadd_nImage", subfilter=subfilter,
460 numSubfilters=self.config.dcrNumSubfilters)
464 @utils.inheritDoc(AssembleCoaddTask)
466 """Load the previously-generated template coadd.
468 This can be removed entirely once we no longer support the Gen 2 butler.
472 templateCoadd : `lsst.pipe.base.Struct`
473 Result struct with components:
475 - ``templateCoadd``: coadded exposure (`lsst.afw.image.ExposureF`)
477 templateCoadd = butlerQC.get(inputRefs.templateExposure)
479 return pipeBase.Struct(templateCoadd=templateCoadd)
481 def measureCoaddPsf(self, coaddExposure):
482 """Detect sources on the coadd exposure and measure the final PSF.
487 The final coadded exposure.
489 table = afwTable.SourceTable.make(self.schema)
490 detResults = self.detectPsfSources.run(table, coaddExposure, clearMask=False)
491 coaddSources = detResults.sources
492 self.measurePsfSources.
run(
493 measCat=coaddSources,
494 exposure=coaddExposure
501 psfResults = self.measurePsf.
run(coaddExposure, coaddSources)
502 except Exception
as e:
503 self.log.
warning(
"Unable to calculate PSF, using default coadd PSF: %s", e)
505 coaddExposure.setPsf(psfResults.psf)
507 def prepareDcrInputs(self, templateCoadd, warpRefList, weightList):
508 """Prepare the DCR coadd by iterating through the visitInfo of the input warps.
510 Sets the property ``bufferSize``.
514 templateCoadd : `lsst.afw.image.ExposureF`
515 The initial coadd exposure before accounting for DCR.
516 warpRefList : `list` of `lsst.daf.butler.DeferredDatasetHandle`
or
518 The data references to the input warped exposures.
519 weightList : `list` of `float`
520 The weight to give each input exposure
in the coadd
521 Will be modified
in place
if ``doAirmassWeight``
is set.
525 dcrModels : `lsst.pipe.tasks.DcrModel`
526 Best fit model of the true sky after correcting chromatic effects.
531 If ``lambdaMin``
is missing
from the Mapper
class of the obs package being used.
533 sigma2fwhm = 2.*np.sqrt(2.*np.log(2.))
534 filterLabel = templateCoadd.getFilter()
535 tempExpName = self.getTempExpDatasetName(self.warpType)
540 for visitNum, warpExpRef
in enumerate(warpRefList):
541 if isinstance(warpExpRef, DeferredDatasetHandle):
543 visitInfo = warpExpRef.get(component=
"visitInfo")
544 psf = warpExpRef.get(component=
"psf")
547 visitInfo = warpExpRef.get(tempExpName +
"_visitInfo")
548 psf = warpExpRef.get(tempExpName).getPsf()
549 visit = warpExpRef.dataId[
"visit"]
551 psfAvgPos = psf.getAveragePosition()
552 psfSize = psf.computeShape(psfAvgPos).getDeterminantRadius()*sigma2fwhm
553 airmass = visitInfo.getBoresightAirmass()
554 parallacticAngle = visitInfo.getBoresightParAngle().asDegrees()
555 airmassDict[visit] = airmass
556 angleDict[visit] = parallacticAngle
557 psfSizeDict[visit] = psfSize
558 if self.config.doAirmassWeight:
559 weightList[visitNum] *= airmass
560 dcrShifts.append(np.max(np.abs(
calculateDcr(visitInfo, templateCoadd.getWcs(),
561 self.config.effectiveWavelength,
562 self.config.bandwidth,
563 self.config.dcrNumSubfilters))))
564 self.log.
info(
"Selected airmasses:\n%s", airmassDict)
565 self.log.
info(
"Selected parallactic angles:\n%s", angleDict)
566 self.log.
info(
"Selected PSF sizes:\n%s", psfSizeDict)
567 self.bufferSize =
int(np.ceil(np.max(dcrShifts)) + 1)
569 psf = self.selectCoaddPsf(templateCoadd, warpRefList)
570 except Exception
as e:
571 self.log.
warning(
"Unable to calculate restricted PSF, using default coadd PSF: %s", e)
573 psf = templateCoadd.getPsf()
574 dcrModels = DcrModel.fromImage(templateCoadd.maskedImage,
575 self.config.dcrNumSubfilters,
576 effectiveWavelength=self.config.effectiveWavelength,
577 bandwidth=self.config.bandwidth,
578 wcs=templateCoadd.getWcs(),
579 filterLabel=filterLabel,
584 def run(self, skyInfo, warpRefList, imageScalerList, weightList,
585 supplementaryData=None):
586 """Assemble the coadd.
588 Requires additional inputs Struct ``supplementaryData`` to contain a
589 ``templateCoadd`` that serves as the model of the static sky.
591 Find artifacts
and apply them to the warps
' masks creating a list of
592 alternative masks with a new
"CLIPPED" plane
and updated
"NO_DATA" plane
593 Then
pass these alternative masks to the base
class's assemble method.
595 Divide the ``templateCoadd`` evenly between each subfilter of a
596 ``DcrModel`` as the starting best estimate of the true wavelength-
597 dependent sky. Forward model the ``DcrModel`` using the known
598 chromatic effects
in each subfilter
and calculate a convergence metric
599 based on how well the modeled template matches the input warps. If
600 the convergence has
not yet reached the desired threshold, then shift
601 and stack the residual images to build a new ``DcrModel``. Apply
602 conditioning to prevent oscillating solutions between iterations
or
605 Once the ``DcrModel`` reaches convergence
or the maximum number of
606 iterations has been reached, fill the metadata
for each subfilter
607 image
and make them proper ``coaddExposure``s.
611 skyInfo : `lsst.pipe.base.Struct`
612 Patch geometry information,
from getSkyInfo
613 warpRefList : `list` of `lsst.daf.butler.DeferredDatasetHandle`
or
615 The data references to the input warped exposures.
616 imageScalerList : `list` of `lsst.pipe.task.ImageScaler`
617 The image scalars correct
for the zero point of the exposures.
618 weightList : `list` of `float`
619 The weight to give each input exposure
in the coadd
620 supplementaryData : `lsst.pipe.base.Struct`
621 Result struct returned by ``makeSupplementaryData``
with components:
627 result : `lsst.pipe.base.Struct`
628 Result struct
with components:
631 - ``nImage``: exposure count image (`lsst.afw.image.ImageU`)
632 - ``dcrCoadds``: `list` of coadded exposures
for each subfilter
633 - ``dcrNImages``: `list` of exposure count images
for each subfilter
635 minNumIter = self.config.minNumIter or self.config.dcrNumSubfilters
636 maxNumIter = self.config.maxNumIter
or self.config.dcrNumSubfilters*3
637 templateCoadd = supplementaryData.templateCoadd
638 baseMask = templateCoadd.mask.clone()
641 baseVariance = templateCoadd.variance.clone()
642 baseVariance /= self.config.dcrNumSubfilters
643 spanSetMaskList = self.findArtifacts(templateCoadd, warpRefList, imageScalerList)
645 templateCoadd.setMask(baseMask)
646 badMaskPlanes = self.config.badMaskPlanes[:]
651 badPixelMask = templateCoadd.mask.getPlaneBitMask(badMaskPlanes)
653 stats = self.prepareStats(mask=badPixelMask)
654 dcrModels = self.prepareDcrInputs(templateCoadd, warpRefList, weightList)
655 if self.config.doNImage:
656 dcrNImages, dcrWeights = self.calculateNImage(dcrModels, skyInfo.bbox, warpRefList,
657 spanSetMaskList, stats.ctrl)
658 nImage = afwImage.ImageU(skyInfo.bbox)
662 for dcrNImage
in dcrNImages:
668 nSubregions = (
ceil(skyInfo.bbox.getHeight()/subregionSize[1])
669 *
ceil(skyInfo.bbox.getWidth()/subregionSize[0]))
671 for subBBox
in self._subBBoxIter(skyInfo.bbox, subregionSize):
674 self.log.
info(
"Computing coadd over patch %s subregion %s of %s: %s",
675 skyInfo.patchInfo.getIndex(), subIter, nSubregions, subBBox)
677 dcrBBox.grow(self.bufferSize)
678 dcrBBox.clip(dcrModels.bbox)
679 modelWeights = self.calculateModelWeights(dcrModels, dcrBBox)
680 subExposures = self.loadSubExposures(dcrBBox, stats.ctrl, warpRefList,
681 imageScalerList, spanSetMaskList)
682 convergenceMetric = self.calculateConvergence(dcrModels, subExposures, subBBox,
683 warpRefList, weightList, stats.ctrl)
684 self.log.
info(
"Initial convergence : %s", convergenceMetric)
685 convergenceList = [convergenceMetric]
687 convergenceCheck = 1.
688 refImage = templateCoadd.image
689 while (convergenceCheck > self.config.convergenceThreshold
or modelIter <= minNumIter):
690 gain = self.calculateGain(convergenceList, gainList)
691 self.dcrAssembleSubregion(dcrModels, subExposures, subBBox, dcrBBox, warpRefList,
692 stats.ctrl, convergenceMetric, gain,
693 modelWeights, refImage, dcrWeights)
694 if self.config.useConvergence:
695 convergenceMetric = self.calculateConvergence(dcrModels, subExposures, subBBox,
696 warpRefList, weightList, stats.ctrl)
697 if convergenceMetric == 0:
698 self.log.
warning(
"Coadd patch %s subregion %s had convergence metric of 0.0 which is "
699 "most likely due to there being no valid data in the region.",
700 skyInfo.patchInfo.getIndex(), subIter)
702 convergenceCheck = (convergenceList[-1] - convergenceMetric)/convergenceMetric
703 if (convergenceCheck < 0) & (modelIter > minNumIter):
704 self.log.
warning(
"Coadd patch %s subregion %s diverged before reaching maximum "
705 "iterations or desired convergence improvement of %s."
707 skyInfo.patchInfo.getIndex(), subIter,
708 self.config.convergenceThreshold, convergenceCheck)
710 convergenceList.append(convergenceMetric)
711 if modelIter > maxNumIter:
712 if self.config.useConvergence:
713 self.log.
warning(
"Coadd patch %s subregion %s reached maximum iterations "
714 "before reaching desired convergence improvement of %s."
715 " Final convergence improvement: %s",
716 skyInfo.patchInfo.getIndex(), subIter,
717 self.config.convergenceThreshold, convergenceCheck)
720 if self.config.useConvergence:
721 self.log.
info(
"Iteration %s with convergence metric %s, %.4f%% improvement (gain: %.2f)",
722 modelIter, convergenceMetric, 100.*convergenceCheck, gain)
725 if self.config.useConvergence:
726 self.log.
info(
"Coadd patch %s subregion %s finished with "
727 "convergence metric %s after %s iterations",
728 skyInfo.patchInfo.getIndex(), subIter, convergenceMetric, modelIter)
730 self.log.
info(
"Coadd patch %s subregion %s finished after %s iterations",
731 skyInfo.patchInfo.getIndex(), subIter, modelIter)
732 if self.config.useConvergence
and convergenceMetric > 0:
733 self.log.
info(
"Final convergence improvement was %.4f%% overall",
734 100*(convergenceList[0] - convergenceMetric)/convergenceMetric)
736 dcrCoadds = self.fillCoadd(dcrModels, skyInfo, warpRefList, weightList,
737 calibration=self.scaleZeroPoint.getPhotoCalib(),
738 coaddInputs=templateCoadd.getInfo().getCoaddInputs(),
740 variance=baseVariance)
741 coaddExposure = self.stackCoadd(dcrCoadds)
742 return pipeBase.Struct(coaddExposure=coaddExposure, nImage=nImage,
743 dcrCoadds=dcrCoadds, dcrNImages=dcrNImages)
745 def calculateNImage(self, dcrModels, bbox, warpRefList, spanSetMaskList, statsCtrl):
746 """Calculate the number of exposures contributing to each subfilter.
750 dcrModels : `lsst.pipe.tasks.DcrModel`
751 Best fit model of the true sky after correcting chromatic effects.
752 bbox : `lsst.geom.box.Box2I`
753 Bounding box of the patch to coadd.
754 warpRefList : `list` of `lsst.daf.butler.DeferredDatasetHandle` or
756 The data references to the input warped exposures.
757 spanSetMaskList : `list` of `dict` containing spanSet lists,
or None
758 Each element of the `dict` contains the new mask plane name
759 (e.g.
"CLIPPED and/or "NO_DATA
") as the key,
760 and the list of SpanSets to apply to the mask.
762 Statistics control object
for coadd
766 dcrNImages : `list` of `lsst.afw.image.ImageU`
767 List of exposure count images
for each subfilter
768 dcrWeights : `list` of `lsst.afw.image.ImageF`
769 Per-pixel weights
for each subfilter.
770 Equal to 1/(number of unmasked images contributing to each pixel).
772 dcrNImages = [afwImage.ImageU(bbox) for subfilter
in range(self.config.dcrNumSubfilters)]
773 dcrWeights = [afwImage.ImageF(bbox)
for subfilter
in range(self.config.dcrNumSubfilters)]
774 tempExpName = self.getTempExpDatasetName(self.warpType)
775 for warpExpRef, altMaskSpans
in zip(warpRefList, spanSetMaskList):
776 if isinstance(warpExpRef, DeferredDatasetHandle):
778 exposure = warpExpRef.get(parameters={
'bbox': bbox})
781 exposure = warpExpRef.get(tempExpName +
"_sub", bbox=bbox)
782 visitInfo = exposure.getInfo().getVisitInfo()
783 wcs = exposure.getInfo().getWcs()
785 if altMaskSpans
is not None:
786 self.applyAltMaskPlanes(mask, altMaskSpans)
787 weightImage = np.zeros_like(exposure.image.array)
788 weightImage[(mask.array & statsCtrl.getAndMask()) == 0] = 1.
791 weightsGenerator = self.dcrResiduals(weightImage, visitInfo, wcs,
792 dcrModels.effectiveWavelength, dcrModels.bandwidth)
793 for shiftedWeights, dcrNImage, dcrWeight
in zip(weightsGenerator, dcrNImages, dcrWeights):
794 dcrNImage.array += np.rint(shiftedWeights).astype(dcrNImage.array.dtype)
795 dcrWeight.array += shiftedWeights
797 weightsThreshold = 1.
798 goodPix = dcrWeights[0].array > weightsThreshold
799 for weights
in dcrWeights[1:]:
800 goodPix = (weights.array > weightsThreshold) & goodPix
801 for subfilter
in range(self.config.dcrNumSubfilters):
802 dcrWeights[subfilter].array[goodPix] = 1./dcrWeights[subfilter].array[goodPix]
803 dcrWeights[subfilter].array[~goodPix] = 0.
804 dcrNImages[subfilter].array[~goodPix] = 0
805 return (dcrNImages, dcrWeights)
808 statsCtrl, convergenceMetric,
809 gain, modelWeights, refImage, dcrWeights):
810 """Assemble the DCR coadd for a sub-region.
812 Build a DCR-matched template for each input exposure, then shift the
813 residuals according to the DCR
in each subfilter.
814 Stack the shifted residuals
and apply them
as a correction to the
815 solution
from the previous iteration.
816 Restrict the new model solutions
from varying by more than a factor of
817 `modelClampFactor`
from the last solution,
and additionally restrict the
818 individual subfilter models
from varying by more than a factor of
819 `frequencyClampFactor`
from their average.
820 Finally, mitigate potentially oscillating solutions by averaging the new
821 solution
with the solution
from the previous iteration, weighted by
822 their convergence metric.
826 dcrModels : `lsst.pipe.tasks.DcrModel`
827 Best fit model of the true sky after correcting chromatic effects.
828 subExposures : `dict` of `lsst.afw.image.ExposureF`
829 The pre-loaded exposures
for the current subregion.
830 bbox : `lsst.geom.box.Box2I`
831 Bounding box of the subregion to coadd.
832 dcrBBox : `lsst.geom.box.Box2I`
833 Sub-region of the coadd which includes a buffer to allow
for DCR.
834 warpRefList : `list` of `lsst.daf.butler.DeferredDatasetHandle`
or
836 The data references to the input warped exposures.
838 Statistics control object
for coadd
839 convergenceMetric : `float`
840 Quality of fit metric
for the matched templates of the input images.
841 gain : `float`, optional
842 Relative weight to give the new solution when updating the model.
843 modelWeights : `numpy.ndarray`
or `float`
844 A 2D array of weight values that tapers smoothly to zero away
from detected sources.
845 Set to a placeholder value of 1.0
if ``self.config.useModelWeights``
is False.
847 A reference image used to supply the default pixel values.
849 Per-pixel weights
for each subfilter.
850 Equal to 1/(number of unmasked images contributing to each pixel).
852 residualGeneratorList = []
854 for warpExpRef
in warpRefList:
855 visit = warpExpRef.dataId[
"visit"]
856 exposure = subExposures[visit]
857 visitInfo = exposure.getInfo().getVisitInfo()
858 wcs = exposure.getInfo().getWcs()
859 templateImage = dcrModels.buildMatchedTemplate(exposure=exposure,
860 bbox=exposure.getBBox(),
861 order=self.config.imageInterpOrder,
862 splitSubfilters=self.config.splitSubfilters,
863 splitThreshold=self.config.splitThreshold,
864 amplifyModel=self.config.accelerateModel)
865 residual = exposure.image.array - templateImage.array
867 residual *= exposure.variance.array
871 residualGeneratorList.append(self.dcrResiduals(residual, visitInfo, wcs,
872 dcrModels.effectiveWavelength,
873 dcrModels.bandwidth))
875 dcrSubModelOut = self.newModelFromResidual(dcrModels, residualGeneratorList, dcrBBox, statsCtrl,
877 modelWeights=modelWeights,
879 dcrWeights=dcrWeights)
880 dcrModels.assign(dcrSubModelOut, bbox)
882 def dcrResiduals(self, residual, visitInfo, wcs, effectiveWavelength, bandwidth):
883 """Prepare a residual image for stacking in each subfilter by applying the reverse DCR shifts.
887 residual : `numpy.ndarray`
888 The residual masked image for one exposure,
889 after subtracting the matched template
891 Metadata
for the exposure.
893 Coordinate system definition (wcs)
for the exposure.
897 residualImage : `numpy.ndarray`
898 The residual image
for the next subfilter, shifted
for DCR.
900 if self.config.imageInterpOrder > 1:
903 filteredResidual = ndimage.spline_filter(residual, order=self.config.imageInterpOrder)
906 filteredResidual = residual
909 dcrShift =
calculateDcr(visitInfo, wcs, effectiveWavelength, bandwidth, self.config.dcrNumSubfilters,
910 splitSubfilters=
False)
912 yield applyDcr(filteredResidual, dcr, useInverse=
True, splitSubfilters=
False,
913 doPrefilter=
False, order=self.config.imageInterpOrder)
916 gain, modelWeights, refImage, dcrWeights):
917 """Calculate a new DcrModel from a set of image residuals.
921 dcrModels : `lsst.pipe.tasks.DcrModel`
922 Current model of the true sky after correcting chromatic effects.
923 residualGeneratorList : `generator` of `numpy.ndarray`
924 The residual image for the next subfilter, shifted
for DCR.
925 dcrBBox : `lsst.geom.box.Box2I`
926 Sub-region of the coadd which includes a buffer to allow
for DCR.
928 Statistics control object
for coadd
930 Relative weight to give the new solution when updating the model.
931 modelWeights : `numpy.ndarray`
or `float`
932 A 2D array of weight values that tapers smoothly to zero away
from detected sources.
933 Set to a placeholder value of 1.0
if ``self.config.useModelWeights``
is False.
935 A reference image used to supply the default pixel values.
937 Per-pixel weights
for each subfilter.
938 Equal to 1/(number of unmasked images contributing to each pixel).
942 dcrModel : `lsst.pipe.tasks.DcrModel`
943 New model of the true sky after correcting chromatic effects.
946 for subfilter, model
in enumerate(dcrModels):
947 residualsList = [
next(residualGenerator)
for residualGenerator
in residualGeneratorList]
948 residual = np.sum(residualsList, axis=0)
949 residual *= dcrWeights[subfilter][dcrBBox].array
951 newModel = model[dcrBBox].
clone()
952 newModel.array += residual
954 badPixels = ~np.isfinite(newModel.array)
955 newModel.array[badPixels] = model[dcrBBox].array[badPixels]
956 if self.config.regularizeModelIterations > 0:
957 dcrModels.regularizeModelIter(subfilter, newModel, dcrBBox,
958 self.config.regularizeModelIterations,
959 self.config.regularizationWidth)
960 newModelImages.append(newModel)
961 if self.config.regularizeModelFrequency > 0:
962 dcrModels.regularizeModelFreq(newModelImages, dcrBBox, statsCtrl,
963 self.config.regularizeModelFrequency,
964 self.config.regularizationWidth)
965 dcrModels.conditionDcrModel(newModelImages, dcrBBox, gain=gain)
966 self.applyModelWeights(newModelImages, refImage[dcrBBox], modelWeights)
967 return DcrModel(newModelImages, dcrModels.filter, dcrModels.effectiveWavelength,
968 dcrModels.bandwidth, dcrModels.psf,
969 dcrModels.mask, dcrModels.variance)
972 """Calculate a quality of fit metric for the matched templates.
976 dcrModels : `lsst.pipe.tasks.DcrModel`
977 Best fit model of the true sky after correcting chromatic effects.
978 subExposures : `dict` of `lsst.afw.image.ExposureF`
979 The pre-loaded exposures for the current subregion.
980 bbox : `lsst.geom.box.Box2I`
982 warpRefList : `list` of `lsst.daf.butler.DeferredDatasetHandle`
or
984 The data references to the input warped exposures.
985 weightList : `list` of `float`
986 The weight to give each input exposure
in the coadd
988 Statistics control object
for coadd
992 convergenceMetric : `float`
993 Quality of fit metric
for all input exposures, within the sub-region
995 significanceImage = np.abs(dcrModels.getReferenceImage(bbox))
997 significanceImage += nSigma*dcrModels.calculateNoiseCutoff(dcrModels[1], statsCtrl,
998 bufferSize=self.bufferSize)
999 if np.max(significanceImage) == 0:
1000 significanceImage += 1.
1004 for warpExpRef, expWeight
in zip(warpRefList, weightList):
1005 visit = warpExpRef.dataId[
"visit"]
1006 exposure = subExposures[visit][bbox]
1007 singleMetric = self.calculateSingleConvergence(dcrModels, exposure, significanceImage, statsCtrl)
1008 metric += singleMetric
1009 metricList[visit] = singleMetric
1011 self.log.
info(
"Individual metrics:\n%s", metricList)
1012 return 1.0
if weight == 0.0
else metric/weight
1015 """Calculate a quality of fit metric for a single matched template.
1019 dcrModels : `lsst.pipe.tasks.DcrModel`
1020 Best fit model of the true sky after correcting chromatic effects.
1021 exposure : `lsst.afw.image.ExposureF`
1022 The input warped exposure to evaluate.
1023 significanceImage : `numpy.ndarray`
1024 Array of weights for each pixel corresponding to its significance
1025 for the convergence calculation.
1027 Statistics control object
for coadd
1031 convergenceMetric : `float`
1032 Quality of fit metric
for one exposure, within the sub-region.
1034 convergeMask = exposure.mask.getPlaneBitMask(self.config.convergenceMaskPlanes)
1035 templateImage = dcrModels.buildMatchedTemplate(exposure=exposure,
1036 bbox=exposure.getBBox(),
1037 order=self.config.imageInterpOrder,
1038 splitSubfilters=self.config.splitSubfilters,
1039 splitThreshold=self.config.splitThreshold,
1040 amplifyModel=self.config.accelerateModel)
1041 diffVals = np.abs(exposure.image.array - templateImage.array)*significanceImage
1042 refVals = np.abs(exposure.image.array + templateImage.array)*significanceImage/2.
1044 finitePixels = np.isfinite(diffVals)
1045 goodMaskPixels = (exposure.mask.array & statsCtrl.getAndMask()) == 0
1046 convergeMaskPixels = exposure.mask.array & convergeMask > 0
1047 usePixels = finitePixels & goodMaskPixels & convergeMaskPixels
1048 if np.sum(usePixels) == 0:
1051 diffUse = diffVals[usePixels]
1052 refUse = refVals[usePixels]
1053 metric = np.sum(diffUse/np.median(diffUse))/np.sum(refUse/np.median(diffUse))
1057 """Add a list of sub-band coadds together.
1061 dcrCoadds : `list` of `lsst.afw.image.ExposureF`
1062 A list of coadd exposures, each exposure containing
1063 the model for one subfilter.
1067 coaddExposure : `lsst.afw.image.ExposureF`
1068 A single coadd exposure that
is the sum of the sub-bands.
1070 coaddExposure = dcrCoadds[0].clone()
1071 for coadd
in dcrCoadds[1:]:
1072 coaddExposure.maskedImage += coadd.maskedImage
1073 return coaddExposure
1075 def fillCoadd(self, dcrModels, skyInfo, warpRefList, weightList, calibration=None, coaddInputs=None,
1076 mask=None, variance=None):
1077 """Create a list of coadd exposures from a list of masked images.
1081 dcrModels : `lsst.pipe.tasks.DcrModel`
1082 Best fit model of the true sky after correcting chromatic effects.
1083 skyInfo : `lsst.pipe.base.Struct`
1084 Patch geometry information, from getSkyInfo
1085 warpRefList : `list` of `lsst.daf.butler.DeferredDatasetHandle`
or
1087 The data references to the input warped exposures.
1088 weightList : `list` of `float`
1089 The weight to give each input exposure
in the coadd
1090 calibration : `lsst.afw.Image.PhotoCalib`, optional
1091 Scale factor to set the photometric calibration of an exposure.
1092 coaddInputs : `lsst.afw.Image.CoaddInputs`, optional
1093 A record of the observations that are included
in the coadd.
1095 Optional mask to override the values
in the final coadd.
1097 Optional variance plane to override the values
in the final coadd.
1101 dcrCoadds : `list` of `lsst.afw.image.ExposureF`
1102 A list of coadd exposures, each exposure containing
1103 the model
for one subfilter.
1106 refModel = dcrModels.getReferenceImage()
1107 for model
in dcrModels:
1108 if self.config.accelerateModel > 1:
1109 model.array = (model.array - refModel)*self.config.accelerateModel + refModel
1110 coaddExposure = afwImage.ExposureF(skyInfo.bbox, skyInfo.wcs)
1111 if calibration
is not None:
1112 coaddExposure.setPhotoCalib(calibration)
1113 if coaddInputs
is not None:
1114 coaddExposure.getInfo().setCoaddInputs(coaddInputs)
1116 self.assembleMetadata(coaddExposure, warpRefList, weightList)
1118 coaddExposure.setPsf(dcrModels.psf)
1120 maskedImage = afwImage.MaskedImageF(dcrModels.bbox)
1121 maskedImage.image = model
1122 maskedImage.mask = dcrModels.mask
1123 maskedImage.variance = dcrModels.variance
1124 coaddExposure.setMaskedImage(maskedImage[skyInfo.bbox])
1125 coaddExposure.setPhotoCalib(self.scaleZeroPoint.getPhotoCalib())
1126 if mask
is not None:
1127 coaddExposure.setMask(mask)
1128 if variance
is not None:
1129 coaddExposure.setVariance(variance)
1130 dcrCoadds.append(coaddExposure)
1134 """Calculate the gain to use for the current iteration.
1136 After calculating a new DcrModel, each value is averaged
with the
1137 value
in the corresponding pixel
from the previous iteration. This
1138 reduces oscillating solutions that iterative techniques are plagued by,
1139 and speeds convergence. By far the biggest changes to the model
1140 happen
in the first couple iterations, so we can also use a more
1141 aggressive gain later when the model
is changing slowly.
1145 convergenceList : `list` of `float`
1146 The quality of fit metric
from each previous iteration.
1147 gainList : `list` of `float`
1148 The gains used
in each previous iteration: appended
with the new
1150 Gains are numbers between ``self.config.baseGain``
and 1.
1155 Relative weight to give the new solution when updating the model.
1156 A value of 1.0 gives equal weight to both solutions.
1161 If ``len(convergenceList) != len(gainList)+1``.
1163 nIter = len(convergenceList)
1164 if nIter != len(gainList) + 1:
1165 raise ValueError(
"convergenceList (%d) must be one element longer than gainList (%d)."
1166 % (len(convergenceList), len(gainList)))
1168 if self.config.baseGain
is None:
1171 baseGain = 1./(self.config.dcrNumSubfilters - 1)
1173 baseGain = self.config.baseGain
1175 if self.config.useProgressiveGain
and nIter > 2:
1183 estFinalConv = [((1 + gainList[i])*convergenceList[i + 1] - convergenceList[i])/gainList[i]
1184 for i
in range(nIter - 1)]
1187 estFinalConv = np.array(estFinalConv)
1188 estFinalConv[estFinalConv < 0] = 0
1190 estFinalConv = np.median(estFinalConv[
max(nIter - 5, 0):])
1191 lastGain = gainList[-1]
1192 lastConv = convergenceList[-2]
1193 newConv = convergenceList[-1]
1198 predictedConv = (estFinalConv*lastGain + lastConv)/(1. + lastGain)
1204 delta = (predictedConv - newConv)/((lastConv - estFinalConv)/(1 + lastGain))
1205 newGain = 1 -
abs(delta)
1207 newGain = (newGain + lastGain)/2.
1208 gain =
max(baseGain, newGain)
1211 gainList.append(gain)
1215 """Build an array that smoothly tapers to 0 away from detected sources.
1219 dcrModels : `lsst.pipe.tasks.DcrModel`
1220 Best fit model of the true sky after correcting chromatic effects.
1221 dcrBBox : `lsst.geom.box.Box2I`
1222 Sub-region of the coadd which includes a buffer to allow for DCR.
1226 weights : `numpy.ndarray`
or `float`
1227 A 2D array of weight values that tapers smoothly to zero away
from detected sources.
1228 Set to a placeholder value of 1.0
if ``self.config.useModelWeights``
is False.
1233 If ``useModelWeights``
is set
and ``modelWeightsWidth``
is negative.
1235 if not self.config.useModelWeights:
1237 if self.config.modelWeightsWidth < 0:
1238 raise ValueError(
"modelWeightsWidth must not be negative if useModelWeights is set")
1239 convergeMask = dcrModels.mask.getPlaneBitMask(self.config.convergenceMaskPlanes)
1240 convergeMaskPixels = dcrModels.mask[dcrBBox].array & convergeMask > 0
1241 weights = np.zeros_like(dcrModels[0][dcrBBox].array)
1242 weights[convergeMaskPixels] = 1.
1243 weights = ndimage.gaussian_filter(weights, self.config.modelWeightsWidth)
1244 weights /= np.max(weights)
1248 """Smoothly replace model pixel values with those from a
1249 reference at locations away from detected sources.
1254 The new DCR model images
from the current iteration.
1255 The values will be modified
in place.
1257 A reference image used to supply the default pixel values.
1258 modelWeights : `numpy.ndarray`
or `float`
1259 A 2D array of weight values that tapers smoothly to zero away
from detected sources.
1260 Set to a placeholder value of 1.0
if ``self.config.useModelWeights``
is False.
1262 if self.config.useModelWeights:
1263 for model
in modelImages:
1264 model.array *= modelWeights
1265 model.array += refImage.array*(1. - modelWeights)/self.config.dcrNumSubfilters
1268 """Pre-load sub-regions of a list of exposures.
1272 bbox : `lsst.geom.box.Box2I`
1275 Statistics control object for coadd
1276 warpRefList : `list` of `lsst.daf.butler.DeferredDatasetHandle`
or
1278 The data references to the input warped exposures.
1279 imageScalerList : `list` of `lsst.pipe.task.ImageScaler`
1280 The image scalars correct
for the zero point of the exposures.
1281 spanSetMaskList : `list` of `dict` containing spanSet lists,
or None
1282 Each element
is dict
with keys = mask plane name to add the spans to
1286 subExposures : `dict`
1287 The `dict` keys are the visit IDs,
1288 and the values are `lsst.afw.image.ExposureF`
1289 The pre-loaded exposures
for the current subregion.
1290 The variance plane contains weights,
and not the variance
1292 tempExpName = self.getTempExpDatasetName(self.warpType)
1293 zipIterables = zip(warpRefList, imageScalerList, spanSetMaskList)
1295 for warpExpRef, imageScaler, altMaskSpans
in zipIterables:
1296 if isinstance(warpExpRef, DeferredDatasetHandle):
1297 exposure = warpExpRef.get(parameters={
'bbox': bbox})
1299 exposure = warpExpRef.get(tempExpName +
"_sub", bbox=bbox)
1300 visit = warpExpRef.dataId[
"visit"]
1301 if altMaskSpans
is not None:
1302 self.applyAltMaskPlanes(exposure.mask, altMaskSpans)
1303 imageScaler.scaleMaskedImage(exposure.maskedImage)
1305 exposure.variance.array[:, :] = 0.
1307 exposure.variance.array[(exposure.mask.array & statsCtrl.getAndMask()) == 0] = 1.
1310 exposure.image.array[(exposure.mask.array & statsCtrl.getAndMask()) > 0] = 0.
1311 subExposures[visit] = exposure
1315 """Compute the PSF of the coadd from the exposures with the best seeing.
1319 templateCoadd : `lsst.afw.image.ExposureF`
1320 The initial coadd exposure before accounting for DCR.
1321 warpRefList : `list` of `lsst.daf.butler.DeferredDatasetHandle`
or
1323 The data references to the input warped exposures.
1328 The average PSF of the input exposures
with the best seeing.
1330 sigma2fwhm = 2.*np.sqrt(2.*np.log(2.))
1331 tempExpName = self.getTempExpDatasetName(self.warpType)
1334 ccds = templateCoadd.getInfo().getCoaddInputs().ccds
1335 templatePsf = templateCoadd.getPsf()
1337 templateAvgPos = templatePsf.getAveragePosition()
1338 psfRefSize = templatePsf.computeShape(templateAvgPos).getDeterminantRadius()*sigma2fwhm
1339 psfSizes = np.zeros(len(ccds))
1340 ccdVisits = np.array(ccds[
"visit"])
1341 for warpExpRef
in warpRefList:
1342 if isinstance(warpExpRef, DeferredDatasetHandle):
1344 psf = warpExpRef.get(component=
"psf")
1347 psf = warpExpRef.get(tempExpName).getPsf()
1348 visit = warpExpRef.dataId[
"visit"]
1349 psfAvgPos = psf.getAveragePosition()
1350 psfSize = psf.computeShape(psfAvgPos).getDeterminantRadius()*sigma2fwhm
1351 psfSizes[ccdVisits == visit] = psfSize
1355 sizeThreshold =
min(np.median(psfSizes), psfRefSize)
1356 goodPsfs = psfSizes <= sizeThreshold
1357 psf = measAlg.CoaddPsf(ccds[goodPsfs], templateCoadd.getWcs(),
1358 self.config.coaddPsf.makeControl())
A 2-dimensional celestial WCS that transform pixels to ICRS RA/Dec, using the LSST standard for pixel...
A class to contain the data, WCS, and other information needed to describe an image of the sky.
A class to represent a 2-dimensional array of pixels.
Represent a 2-dimensional array of bitmask pixels.
A class to manipulate images, masks, and variance as a single object.
Information about a single exposure of an imaging camera.
Pass parameters to a Statistics object.
An integer coordinate rectangle.
CoaddPsf is the Psf derived to be used for non-PSF-matched Coadd images.
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
void setCoaddEdgeBits(lsst::afw::image::Mask< lsst::afw::image::MaskPixel > &coaddMask, lsst::afw::image::Image< WeightPixelT > const &weightMap)
set edge bits of coadd mask based on weight map
Extent< int, N > ceil(Extent< double, N > const &input) noexcept
Return the component-wise ceil (round towards more positive).
def applyDcr(image, dcr, useInverse=False, splitSubfilters=False, splitThreshold=0., doPrefilter=True, order=3)
def calculateDcr(visitInfo, wcs, effectiveWavelength, bandwidth, dcrNumSubfilters, splitSubfilters=False)
def run(self, coaddExposures, bbox, wcs, dataIds, **kwargs)
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations.
def makeSupplementaryDataGen3(self, butlerQC, inputRefs, outputRefs)
def makeSkyInfo(skyMap, tractId, patchId)
def loadSubExposures(self, bbox, statsCtrl, warpRefList, imageScalerList, spanSetMaskList)
def fillCoadd(self, dcrModels, skyInfo, warpRefList, weightList, calibration=None, coaddInputs=None, mask=None, variance=None)
def applyModelWeights(self, modelImages, refImage, modelWeights)
def calculateSingleConvergence(self, dcrModels, exposure, significanceImage, statsCtrl)
def stackCoadd(self, dcrCoadds)
def calculateConvergence(self, dcrModels, subExposures, bbox, warpRefList, weightList, statsCtrl)
def dcrAssembleSubregion(self, dcrModels, subExposures, bbox, dcrBBox, warpRefList, statsCtrl, convergenceMetric, gain, modelWeights, refImage, dcrWeights)
def calculateGain(self, convergenceList, gainList)
def calculateModelWeights(self, dcrModels, dcrBBox)
def newModelFromResidual(self, dcrModels, residualGeneratorList, dcrBBox, statsCtrl, gain, modelWeights, refImage, dcrWeights)
def selectCoaddPsf(self, templateCoadd, warpRefList)
def dcrResiduals(self, residual, visitInfo, wcs, effectiveWavelength, bandwidth)
Angle abs(Angle const &a)