LSSTApplications  16.0-10-g0ee56ad+5,16.0-11-ga33d1f2+5,16.0-12-g3ef5c14+3,16.0-12-g71e5ef5+18,16.0-12-gbdf3636+3,16.0-13-g118c103+3,16.0-13-g8f68b0a+3,16.0-15-gbf5c1cb+4,16.0-16-gfd17674+3,16.0-17-g7c01f5c+3,16.0-18-g0a50484+1,16.0-20-ga20f992+8,16.0-21-g0e05fd4+6,16.0-21-g15e2d33+4,16.0-22-g62d8060+4,16.0-22-g847a80f+4,16.0-25-gf00d9b8+1,16.0-28-g3990c221+4,16.0-3-gf928089+3,16.0-32-g88a4f23+5,16.0-34-gd7987ad+3,16.0-37-gc7333cb+2,16.0-4-g10fc685+2,16.0-4-g18f3627+26,16.0-4-g5f3a788+26,16.0-5-gaf5c3d7+4,16.0-5-gcc1f4bb+1,16.0-6-g3b92700+4,16.0-6-g4412fcd+3,16.0-6-g7235603+4,16.0-69-g2562ce1b+2,16.0-8-g14ebd58+4,16.0-8-g2df868b+1,16.0-8-g4cec79c+6,16.0-8-gadf6c7a+1,16.0-8-gfc7ad86,16.0-82-g59ec2a54a+1,16.0-9-g5400cdc+2,16.0-9-ge6233d7+5,master-g2880f2d8cf+3,v17.0.rc1
LSSTDataManagementBasePackage
dcrAssembleCoadd.py
Go to the documentation of this file.
1 # This file is part of pipe_tasks.
2 #
3 # LSST Data Management System
4 # This product includes software developed by the
5 # LSST Project (http://www.lsst.org/).
6 # See COPYRIGHT file at the top of the source tree.
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 <https://www.lsstcorp.org/LegalNotices/>.
21 #
22 
23 from math import ceil
24 import numpy as np
25 from scipy import ndimage
26 import lsst.afw.geom as afwGeom
27 import lsst.afw.image as afwImage
28 import lsst.afw.math as afwMath
29 import lsst.coadd.utils as coaddUtils
30 from lsst.ip.diffim.dcrModel import applyDcr, calculateDcr, DcrModel
31 import lsst.pex.config as pexConfig
32 import lsst.pipe.base as pipeBase
33 from .assembleCoadd import AssembleCoaddTask, CompareWarpAssembleCoaddTask, CompareWarpAssembleCoaddConfig
34 
35 __all__ = ["DcrAssembleCoaddTask", "DcrAssembleCoaddConfig"]
36 
37 
39  dcrNumSubfilters = pexConfig.Field(
40  dtype=int,
41  doc="Number of sub-filters to forward model chromatic effects to fit the supplied exposures.",
42  default=3,
43  )
44  maxNumIter = pexConfig.Field(
45  dtype=int,
46  optional=True,
47  doc="Maximum number of iterations of forward modeling.",
48  default=None,
49  )
50  minNumIter = pexConfig.Field(
51  dtype=int,
52  optional=True,
53  doc="Minimum number of iterations of forward modeling.",
54  default=None,
55  )
56  convergenceThreshold = pexConfig.Field(
57  dtype=float,
58  doc="Target relative change in convergence between iterations of forward modeling.",
59  default=0.001,
60  )
61  useConvergence = pexConfig.Field(
62  dtype=bool,
63  doc="Use convergence test as a forward modeling end condition?"
64  "If not set, skips calculating convergence and runs for ``maxNumIter`` iterations",
65  default=True,
66  )
67  baseGain = pexConfig.Field(
68  dtype=float,
69  optional=True,
70  doc="Relative weight to give the new solution vs. the last solution when updating the model."
71  "A value of 1.0 gives equal weight to both solutions."
72  "Small values imply slower convergence of the solution, but can "
73  "help prevent overshooting and failures in the fit."
74  "If ``baseGain`` is None, a conservative gain "
75  "will be calculated from the number of subfilters. ",
76  default=None,
77  )
78  useProgressiveGain = pexConfig.Field(
79  dtype=bool,
80  doc="Use a gain that slowly increases above ``baseGain`` to accelerate convergence? "
81  "When calculating the next gain, we use up to 5 previous gains and convergence values."
82  "Can be set to False to force the model to change at the rate of ``baseGain``. ",
83  default=True,
84  )
85  doAirmassWeight = pexConfig.Field(
86  dtype=bool,
87  doc="Weight exposures by airmass? Useful if there are relatively few high-airmass observations.",
88  default=False,
89  )
90  modelWeightsWidth = pexConfig.Field(
91  dtype=float,
92  doc="Width of the region around detected sources to include in the DcrModel.",
93  default=3,
94  )
95  useModelWeights = pexConfig.Field(
96  dtype=bool,
97  doc="Width of the region around detected sources to include in the DcrModel.",
98  default=True,
99  )
100  splitSubfilters = pexConfig.Field(
101  dtype=bool,
102  doc="Calculate DCR for two evenly-spaced wavelengths in each subfilter."
103  "Instead of at the midpoint",
104  default=True,
105  )
106  regularizeModelIterations = pexConfig.Field(
107  dtype=float,
108  doc="Maximum relative change of the model allowed between iterations."
109  "Set to zero to disable.",
110  default=2.,
111  )
112  regularizeModelFrequency = pexConfig.Field(
113  dtype=float,
114  doc="Maximum relative change of the model allowed between subfilters."
115  "Set to zero to disable.",
116  default=4.,
117  )
118  convergenceMaskPlanes = pexConfig.ListField(
119  dtype=str,
120  default=["DETECTED"],
121  doc="Mask planes to use to calculate convergence."
122  )
123  regularizationWidth = pexConfig.Field(
124  dtype=int,
125  default=2,
126  doc="Minimum radius of a region to include in regularization, in pixels."
127  )
128  imageWarpMethod = pexConfig.Field(
129  dtype=str,
130  doc="Name of the warping kernel to use for shifting the image and variance planes.",
131  default="lanczos3",
132  )
133  maskWarpMethod = pexConfig.Field(
134  dtype=str,
135  doc="Name of the warping kernel to use for shifting the mask plane.",
136  default="bilinear",
137  )
138 
139  def setDefaults(self):
140  CompareWarpAssembleCoaddConfig.setDefaults(self)
141  self.assembleStaticSkyModel.retarget(CompareWarpAssembleCoaddTask)
142  self.doNImage = True
143  self.warpType = 'direct'
144  self.assembleStaticSkyModel.warpType = self.warpType
145  # The deepCoadd and nImage files will be overwritten by this Task, so don't write them the first time
146  self.assembleStaticSkyModel.doNImage = False
147  self.assembleStaticSkyModel.doWrite = False
148  self.sigmaClip = 10
149  self.statistic = 'MEAN'
150 
151 
153  """Assemble DCR coadded images from a set of warps.
154 
155  Attributes
156  ----------
157  bufferSize : `int`
158  The number of pixels to grow each subregion by to allow for DCR.
159  warpCtrl : `lsst.afw.math.WarpingControl`
160  Configuration settings for warping an image
161 
162  Notes
163  -----
164  As with AssembleCoaddTask, we want to assemble a coadded image from a set of
165  Warps (also called coadded temporary exposures), including the effects of
166  Differential Chromatic Refraction (DCR).
167  For full details of the mathematics and algorithm, please see
168  DMTN-037: DCR-matched template generation (https://dmtn-037.lsst.io).
169 
170  This Task produces a DCR-corrected deepCoadd, as well as a dcrCoadd for
171  each subfilter used in the iterative calculation.
172  It begins by dividing the bandpass-defining filter into N equal bandwidth
173  "subfilters", and divides the flux in each pixel from an initial coadd
174  equally into each as a "dcrModel". Because the airmass and parallactic
175  angle of each individual exposure is known, we can calculate the shift
176  relative to the center of the band in each subfilter due to DCR. For each
177  exposure we apply this shift as a linear transformation to the dcrModels
178  and stack the results to produce a DCR-matched exposure. The matched
179  exposures are subtracted from the input exposures to produce a set of
180  residual images, and these residuals are reverse shifted for each
181  exposures' subfilters and stacked. The shifted and stacked residuals are
182  added to the dcrModels to produce a new estimate of the flux in each pixel
183  within each subfilter. The dcrModels are solved for iteratively, which
184  continues until the solution from a new iteration improves by less than
185  a set percentage, or a maximum number of iterations is reached.
186  Two forms of regularization are employed to reduce unphysical results.
187  First, the new solution is averaged with the solution from the previous
188  iteration, which mitigates oscillating solutions where the model
189  overshoots with alternating very high and low values.
190  Second, a common degeneracy when the data have a limited range of airmass or
191  parallactic angle values is for one subfilter to be fit with very low or
192  negative values, while another subfilter is fit with very high values. This
193  typically appears in the form of holes next to sources in one subfilter,
194  and corresponding extended wings in another. Because each subfilter has
195  a narrow bandwidth we assume that physical sources that are above the noise
196  level will not vary in flux by more than a factor of `frequencyClampFactor`
197  between subfilters, and pixels that have flux deviations larger than that
198  factor will have the excess flux distributed evenly among all subfilters.
199  """
200 
201  ConfigClass = DcrAssembleCoaddConfig
202  _DefaultName = "dcrAssembleCoadd"
203 
204  @pipeBase.timeMethod
205  def runDataRef(self, dataRef, selectDataList=None, warpRefList=None):
206  """Assemble a coadd from a set of warps.
207 
208  Coadd a set of Warps. Compute weights to be applied to each Warp and
209  find scalings to match the photometric zeropoint to a reference Warp.
210  Assemble the Warps using run method.
211  Forward model chromatic effects across multiple subfilters,
212  and subtract from the input Warps to build sets of residuals.
213  Use the residuals to construct a new ``DcrModel`` for each subfilter,
214  and iterate until the model converges.
215  Interpolate over NaNs and optionally write the coadd to disk.
216  Return the coadded exposure.
217 
218  Parameters
219  ----------
220  dataRef : `lsst.daf.persistence.ButlerDataRef`
221  Data reference defining the patch for coaddition and the
222  reference Warp
223  selectDataList : `list` of `lsst.daf.persistence.ButlerDataRef`
224  List of data references to warps. Data to be coadded will be
225  selected from this list based on overlap with the patch defined by
226  the data reference.
227 
228  Returns
229  -------
230  results : `lsst.pipe.base.Struct`
231  The Struct contains the following fields:
232 
233  - ``coaddExposure``: coadded exposure (`lsst.afw.image.Exposure`)
234  - ``nImage``: exposure count image (`lsst.afw.image.ImageU`)
235  - ``dcrCoadds``: `list` of coadded exposures for each subfilter
236  - ``dcrNImages``: `list` of exposure count images for each subfilter
237  """
238  if (selectDataList is None and warpRefList is None) or (selectDataList and warpRefList):
239  raise RuntimeError("runDataRef must be supplied either a selectDataList or warpRefList")
240 
241  results = AssembleCoaddTask.runDataRef(self, dataRef, selectDataList=selectDataList,
242  warpRefList=warpRefList)
243  if results is None:
244  skyInfo = self.getSkyInfo(dataRef)
245  self.log.warn("Could not construct DcrModel for patch %s: no data to coadd.",
246  skyInfo.patchInfo.getIndex())
247  return
248  for subfilter in range(self.config.dcrNumSubfilters):
249  self.processResults(results.dcrCoadds[subfilter], dataRef)
250  if self.config.doWrite:
251  self.log.info("Persisting dcrCoadd")
252  dataRef.put(results.dcrCoadds[subfilter], "dcrCoadd", subfilter=subfilter,
253  numSubfilters=self.config.dcrNumSubfilters)
254  if self.config.doNImage and results.dcrNImages is not None:
255  dataRef.put(results.dcrNImages[subfilter], "dcrCoadd_nImage", subfilter=subfilter,
256  numSubfilters=self.config.dcrNumSubfilters)
257 
258  return results
259 
260  def prepareDcrInputs(self, templateCoadd, tempExpRefList, weightList):
261  """Prepare the DCR coadd by iterating through the visitInfo of the input warps.
262 
263  Sets the properties ``warpCtrl`` and ``bufferSize``.
264 
265  Parameters
266  ----------
267  templateCoadd : `lsst.afw.image.ExposureF`
268  The initial coadd exposure before accounting for DCR.
269  tempExpRefList : `list` of `lsst.daf.persistence.ButlerDataRef`
270  The data references to the input warped exposures.
271  weightList : `list` of `float`
272  The weight to give each input exposure in the coadd
273  Will be modified in place if ``doAirmassWeight`` is set.
274 
275  Returns
276  -------
277  dcrModels : `lsst.pipe.tasks.DcrModel`
278  Best fit model of the true sky after correcting chromatic effects.
279 
280  Raises
281  ------
282  NotImplementedError
283  If ``lambdaMin`` is missing from the Mapper class of the obs package being used.
284  """
285  filterInfo = templateCoadd.getFilter()
286  if np.isnan(filterInfo.getFilterProperty().getLambdaMin()):
287  raise NotImplementedError("No minimum/maximum wavelength information found"
288  " in the filter definition! Please add lambdaMin and lambdaMax"
289  " to the Mapper class in your obs package.")
290  tempExpName = self.getTempExpDatasetName(self.warpType)
291  dcrShifts = []
292  airmassList = {}
293  angleList = {}
294  for visitNum, tempExpRef in enumerate(tempExpRefList):
295  visitInfo = tempExpRef.get(tempExpName + "_visitInfo")
296  airmass = visitInfo.getBoresightAirmass()
297  parallacticAngle = visitInfo.getBoresightParAngle().asDegrees()
298  airmassList[tempExpRef.dataId["visit"]] = airmass
299  angleList[tempExpRef.dataId["visit"]] = parallacticAngle
300  if self.config.doAirmassWeight:
301  weightList[visitNum] *= airmass
302  dcrShifts.append(np.max(np.abs(calculateDcr(visitInfo, templateCoadd.getWcs(),
303  filterInfo, self.config.dcrNumSubfilters))))
304  self.log.info("Selected airmasses:\n%s", airmassList)
305  self.log.info("Selected parallactic angles:\n%s", angleList)
306  self.bufferSize = int(np.ceil(np.max(dcrShifts)) + 1)
307  # Turn off the warping cache, since we set the linear interpolation length to the entire subregion
308  # This warper is only used for applying DCR shifts, which are assumed to be uniform across a patch
309  warpCache = 0
310  warpInterpLength = max(self.config.subregionSize)
311  self.warpCtrl = afwMath.WarpingControl(self.config.imageWarpMethod,
312  self.config.maskWarpMethod,
313  cacheSize=warpCache, interpLength=warpInterpLength)
314  dcrModels = DcrModel.fromImage(templateCoadd.maskedImage,
315  self.config.dcrNumSubfilters,
316  filterInfo=filterInfo,
317  psf=templateCoadd.getPsf())
318  return dcrModels
319 
320  def run(self, skyInfo, tempExpRefList, imageScalerList, weightList,
321  supplementaryData=None):
322  """Assemble the coadd.
323 
324  Requires additional inputs Struct ``supplementaryData`` to contain a
325  ``templateCoadd`` that serves as the model of the static sky.
326 
327  Find artifacts and apply them to the warps' masks creating a list of
328  alternative masks with a new "CLIPPED" plane and updated "NO_DATA" plane
329  Then pass these alternative masks to the base class's assemble method.
330 
331  Divide the ``templateCoadd`` evenly between each subfilter of a
332  ``DcrModel`` as the starting best estimate of the true wavelength-
333  dependent sky. Forward model the ``DcrModel`` using the known
334  chromatic effects in each subfilter and calculate a convergence metric
335  based on how well the modeled template matches the input warps. If
336  the convergence has not yet reached the desired threshold, then shift
337  and stack the residual images to build a new ``DcrModel``. Apply
338  conditioning to prevent oscillating solutions between iterations or
339  between subfilters.
340 
341  Once the ``DcrModel`` reaches convergence or the maximum number of
342  iterations has been reached, fill the metadata for each subfilter
343  image and make them proper ``coaddExposure``s.
344 
345  Parameters
346  ----------
347  skyInfo : `lsst.pipe.base.Struct`
348  Patch geometry information, from getSkyInfo
349  tempExpRefList : `list` of `lsst.daf.persistence.ButlerDataRef`
350  The data references to the input warped exposures.
351  imageScalerList : `list` of `lsst.pipe.task.ImageScaler`
352  The image scalars correct for the zero point of the exposures.
353  weightList : `list` of `float`
354  The weight to give each input exposure in the coadd
355  supplementaryData : `lsst.pipe.base.Struct`
356  Result struct returned by ``makeSupplementaryData`` with components:
357 
358  - ``templateCoadd``: coadded exposure (`lsst.afw.image.Exposure`)
359 
360  Returns
361  -------
362  result : `lsst.pipe.base.Struct`
363  Result struct with components:
364 
365  - ``coaddExposure``: coadded exposure (`lsst.afw.image.Exposure`)
366  - ``nImage``: exposure count image (`lsst.afw.image.ImageU`)
367  - ``dcrCoadds``: `list` of coadded exposures for each subfilter
368  - ``dcrNImages``: `list` of exposure count images for each subfilter
369  """
370  minNumIter = self.config.minNumIter or self.config.dcrNumSubfilters
371  maxNumIter = self.config.maxNumIter or self.config.dcrNumSubfilters*3
372  templateCoadd = supplementaryData.templateCoadd
373  baseMask = templateCoadd.mask.clone()
374  # The variance plane is for each subfilter
375  # and should be proportionately lower than the full-band image
376  baseVariance = templateCoadd.variance.clone()
377  baseVariance /= self.config.dcrNumSubfilters
378  spanSetMaskList = self.findArtifacts(templateCoadd, tempExpRefList, imageScalerList)
379  # Note that the mask gets cleared in ``findArtifacts``, but we want to preserve the mask.
380  templateCoadd.setMask(baseMask)
381  badMaskPlanes = self.config.badMaskPlanes[:]
382  # Note that is important that we do not add "CLIPPED" to ``badMaskPlanes``
383  # This is because pixels in observations that are significantly affect by DCR
384  # are likely to have many pixels that are both "DETECTED" and "CLIPPED",
385  # but those are necessary to constrain the DCR model.
386  badPixelMask = templateCoadd.mask.getPlaneBitMask(badMaskPlanes)
387 
388  stats = self.prepareStats(mask=badPixelMask)
389  dcrModels = self.prepareDcrInputs(templateCoadd, tempExpRefList, weightList)
390  if self.config.doNImage:
391  dcrNImages = self.calculateNImage(dcrModels, skyInfo.bbox,
392  tempExpRefList, spanSetMaskList, stats.ctrl)
393  nImage = afwImage.ImageU(skyInfo.bbox)
394  # Note that this nImage will be a factor of dcrNumSubfilters higher than
395  # the nImage returned by assembleCoadd for most pixels. This is because each
396  # subfilter may have a different nImage, and fractional values are not allowed.
397  for dcrNImage in dcrNImages:
398  nImage += dcrNImage
399  else:
400  dcrNImages = None
401 
402  subregionSize = afwGeom.Extent2I(*self.config.subregionSize)
403  nSubregions = (ceil(skyInfo.bbox.getHeight()/subregionSize[1]) *
404  ceil(skyInfo.bbox.getWidth()/subregionSize[0]))
405  subIter = 0
406  for subBBox in self._subBBoxIter(skyInfo.bbox, subregionSize):
407  modelIter = 0
408  subIter += 1
409  self.log.info("Computing coadd over patch %s subregion %s of %s: %s",
410  skyInfo.patchInfo.getIndex(), subIter, nSubregions, subBBox)
411  dcrBBox = afwGeom.Box2I(subBBox)
412  dcrBBox.grow(self.bufferSize)
413  dcrBBox.clip(dcrModels.bbox)
414  modelWeights = self.calculateModelWeights(dcrModels, dcrBBox)
415  convergenceMetric = self.calculateConvergence(dcrModels, subBBox, tempExpRefList,
416  imageScalerList, weightList, spanSetMaskList,
417  stats.ctrl)
418  self.log.info("Initial convergence : %s", convergenceMetric)
419  convergenceList = [convergenceMetric]
420  gainList = []
421  convergenceCheck = 1.
422  refImage = templateCoadd.maskedImage
423  while (convergenceCheck > self.config.convergenceThreshold or modelIter <= minNumIter):
424  gain = self.calculateGain(convergenceList, gainList)
425  self.dcrAssembleSubregion(dcrModels, subBBox, dcrBBox, tempExpRefList, imageScalerList,
426  weightList, spanSetMaskList, stats.flags, stats.ctrl,
427  convergenceMetric, baseMask, gain,
428  modelWeights, refImage)
429  if self.config.useConvergence:
430  convergenceMetric = self.calculateConvergence(dcrModels, subBBox, tempExpRefList,
431  imageScalerList, weightList,
432  spanSetMaskList,
433  stats.ctrl)
434  if convergenceMetric == 0:
435  self.log.warn("Coadd patch %s subregion %s had convergence metric of 0.0 which is "
436  "most likely due to there being no valid data in the region.",
437  skyInfo.patchInfo.getIndex(), subIter)
438  break
439  convergenceCheck = (convergenceList[-1] - convergenceMetric)/convergenceMetric
440  if (convergenceCheck < 0) & (modelIter > minNumIter):
441  self.log.warn("Coadd patch %s subregion %s diverged before reaching maximum "
442  "iterations or desired convergence improvement of %s."
443  " Divergence: %s",
444  skyInfo.patchInfo.getIndex(), subIter,
445  self.config.convergenceThreshold, convergenceCheck)
446  break
447  convergenceList.append(convergenceMetric)
448  if modelIter > maxNumIter:
449  if self.config.useConvergence:
450  self.log.warn("Coadd patch %s subregion %s reached maximum iterations "
451  "before reaching desired convergence improvement of %s."
452  " Final convergence improvement: %s",
453  skyInfo.patchInfo.getIndex(), subIter,
454  self.config.convergenceThreshold, convergenceCheck)
455  break
456 
457  if self.config.useConvergence:
458  self.log.info("Iteration %s with convergence metric %s, %.4f%% improvement (gain: %.2f)",
459  modelIter, convergenceMetric, 100.*convergenceCheck, gain)
460  modelIter += 1
461  else:
462  if self.config.useConvergence:
463  self.log.info("Coadd patch %s subregion %s finished with "
464  "convergence metric %s after %s iterations",
465  skyInfo.patchInfo.getIndex(), subIter, convergenceMetric, modelIter)
466  else:
467  self.log.info("Coadd patch %s subregion %s finished after %s iterations",
468  skyInfo.patchInfo.getIndex(), subIter, modelIter)
469  if self.config.useConvergence and convergenceMetric > 0:
470  self.log.info("Final convergence improvement was %.4f%% overall",
471  100*(convergenceList[0] - convergenceMetric)/convergenceMetric)
472 
473  dcrCoadds = self.fillCoadd(dcrModels, skyInfo, tempExpRefList, weightList,
474  calibration=self.scaleZeroPoint.getCalib(),
475  coaddInputs=templateCoadd.getInfo().getCoaddInputs(),
476  mask=baseMask,
477  variance=baseVariance)
478  coaddExposure = self.stackCoadd(dcrCoadds)
479  return pipeBase.Struct(coaddExposure=coaddExposure, nImage=nImage,
480  dcrCoadds=dcrCoadds, dcrNImages=dcrNImages)
481 
482  def calculateNImage(self, dcrModels, bbox, tempExpRefList, spanSetMaskList, statsCtrl):
483  """Calculate the number of exposures contributing to each subfilter.
484 
485  Parameters
486  ----------
487  dcrModels : `lsst.pipe.tasks.DcrModel`
488  Best fit model of the true sky after correcting chromatic effects.
489  bbox : `lsst.afw.geom.box.Box2I`
490  Bounding box of the patch to coadd.
491  tempExpRefList : `list` of `lsst.daf.persistence.ButlerDataRef`
492  The data references to the input warped exposures.
493  spanSetMaskList : `list` of `dict` containing spanSet lists, or None
494  Each element is dict with keys = mask plane name to add the spans to
495  statsCtrl : `lsst.afw.math.StatisticsControl`
496  Statistics control object for coadd
497 
498  Returns
499  -------
500  dcrNImages : `list` of `lsst.afw.image.ImageU`
501  List of exposure count images for each subfilter
502  """
503  dcrNImages = [afwImage.ImageU(bbox) for subfilter in range(self.config.dcrNumSubfilters)]
504  tempExpName = self.getTempExpDatasetName(self.warpType)
505  for tempExpRef, altMaskSpans in zip(tempExpRefList, spanSetMaskList):
506  exposure = tempExpRef.get(tempExpName + "_sub", bbox=bbox)
507  visitInfo = exposure.getInfo().getVisitInfo()
508  wcs = exposure.getInfo().getWcs()
509  mask = exposure.mask
510  if altMaskSpans is not None:
511  self.applyAltMaskPlanes(mask, altMaskSpans)
512  dcrShift = calculateDcr(visitInfo, wcs, dcrModels.filter, self.config.dcrNumSubfilters)
513  for dcr, dcrNImage in zip(dcrShift, dcrNImages):
514  shiftedImage = applyDcr(exposure.maskedImage, dcr, self.warpCtrl, useInverse=True)
515  dcrNImage.array[shiftedImage.mask.array & statsCtrl.getAndMask() == 0] += 1
516  return dcrNImages
517 
518  def dcrAssembleSubregion(self, dcrModels, bbox, dcrBBox, tempExpRefList, imageScalerList, weightList,
519  spanSetMaskList, statsFlags, statsCtrl, convergenceMetric,
520  baseMask, gain, modelWeights, refImage):
521  """Assemble the DCR coadd for a sub-region.
522 
523  Build a DCR-matched template for each input exposure, then shift the
524  residuals according to the DCR in each subfilter.
525  Stack the shifted residuals and apply them as a correction to the
526  solution from the previous iteration.
527  Restrict the new model solutions from varying by more than a factor of
528  `modelClampFactor` from the last solution, and additionally restrict the
529  individual subfilter models from varying by more than a factor of
530  `frequencyClampFactor` from their average.
531  Finally, mitigate potentially oscillating solutions by averaging the new
532  solution with the solution from the previous iteration, weighted by
533  their convergence metric.
534 
535  Parameters
536  ----------
537  dcrModels : `lsst.pipe.tasks.DcrModel`
538  Best fit model of the true sky after correcting chromatic effects.
539  bbox : `lsst.afw.geom.box.Box2I`
540  Bounding box of the subregion to coadd.
541  dcrBBox : `lsst.afw.geom.box.Box2I`
542  Sub-region of the coadd which includes a buffer to allow for DCR.
543  tempExpRefList : `list` of `lsst.daf.persistence.ButlerDataRef`
544  The data references to the input warped exposures.
545  imageScalerList : `list` of `lsst.pipe.task.ImageScaler`
546  The image scalars correct for the zero point of the exposures.
547  weightList : `list` of `float`
548  The weight to give each input exposure in the coadd
549  spanSetMaskList : `list` of `dict` containing spanSet lists, or None
550  Each element is dict with keys = mask plane name to add the spans to
551  statsFlags : `lsst.afw.math.Property`
552  Statistics settings for coaddition.
553  statsCtrl : `lsst.afw.math.StatisticsControl`
554  Statistics control object for coadd
555  convergenceMetric : `float`
556  Quality of fit metric for the matched templates of the input images.
557  baseMask : `lsst.afw.image.Mask`
558  Mask of the initial template coadd.
559  gain : `float`, optional
560  Relative weight to give the new solution when updating the model.
561  modelWeights : `numpy.ndarray` or `float`
562  A 2D array of weight values that tapers smoothly to zero away from detected sources.
563  Set to a placeholder value of 1.0 if ``self.config.useModelWeights`` is False.
564  refImage : `lsst.afw.image.MaskedImage`
565  A reference image used to supply the default pixel values.
566  """
567  tempExpName = self.getTempExpDatasetName(self.warpType)
568  residualGeneratorList = []
569 
570  for tempExpRef, imageScaler, altMaskSpans in zip(tempExpRefList, imageScalerList, spanSetMaskList):
571  exposure = tempExpRef.get(tempExpName + "_sub", bbox=dcrBBox)
572  visitInfo = exposure.getInfo().getVisitInfo()
573  wcs = exposure.getInfo().getWcs()
574  maskedImage = exposure.maskedImage
575  templateImage = dcrModels.buildMatchedTemplate(warpCtrl=self.warpCtrl, visitInfo=visitInfo,
576  bbox=dcrBBox, wcs=wcs, mask=baseMask,
577  splitSubfilters=self.config.splitSubfilters)
578  imageScaler.scaleMaskedImage(maskedImage)
579  if altMaskSpans is not None:
580  self.applyAltMaskPlanes(maskedImage.mask, altMaskSpans)
581 
582  if self.config.removeMaskPlanes:
583  self.removeMaskPlanes(maskedImage)
584  maskedImage -= templateImage
585  residualGeneratorList.append(self.dcrResiduals(maskedImage, visitInfo, dcrBBox, wcs,
586  dcrModels.filter))
587 
588  dcrSubModelOut = self.newModelFromResidual(dcrModels, residualGeneratorList, dcrBBox,
589  statsFlags, statsCtrl, weightList,
590  mask=baseMask, gain=gain,
591  modelWeights=modelWeights,
592  refImage=refImage)
593  dcrModels.assign(dcrSubModelOut, bbox)
594 
595  def dcrResiduals(self, residual, visitInfo, bbox, wcs, filterInfo):
596  """Prepare a residual image for stacking in each subfilter by applying the reverse DCR shifts.
597 
598  Parameters
599  ----------
600  residual : `lsst.afw.image.MaskedImageF`
601  The residual masked image for one exposure,
602  after subtracting the matched template
603  visitInfo : `lsst.afw.image.VisitInfo`
604  Metadata for the exposure.
605  bbox : `lsst.afw.geom.box.Box2I`
606  Sub-region of the coadd
607  wcs : `lsst.afw.geom.SkyWcs`
608  Coordinate system definition (wcs) for the exposure.
609  filterInfo : `lsst.afw.image.Filter`
610  The filter definition, set in the current instruments' obs package.
611  Required for any calculation of DCR, including making matched templates.
612 
613  Yields
614  ------
615  residualImage : `lsst.afw.image.maskedImageF`
616  The residual image for the next subfilter, shifted for DCR.
617  """
618  dcrShift = calculateDcr(visitInfo, wcs, filterInfo, self.config.dcrNumSubfilters)
619  for dcr in dcrShift:
620  yield applyDcr(residual, dcr, self.warpCtrl, bbox=bbox, useInverse=True)
621 
622  def newModelFromResidual(self, dcrModels, residualGeneratorList, bbox,
623  statsFlags, statsCtrl, weightList,
624  mask, gain, modelWeights, refImage):
625  """Calculate a new DcrModel from a set of image residuals.
626 
627  Parameters
628  ----------
629  dcrModels : `lsst.pipe.tasks.DcrModel`
630  Current model of the true sky after correcting chromatic effects.
631  residualGeneratorList : `generator` of `lsst.afw.image.maskedImageF`
632  The residual image for the next subfilter, shifted for DCR.
633  bbox : `lsst.afw.geom.box.Box2I`
634  Sub-region of the coadd
635  statsFlags : `lsst.afw.math.Property`
636  Statistics settings for coaddition.
637  statsCtrl : `lsst.afw.math.StatisticsControl`
638  Statistics control object for coadd
639  weightList : `list` of `float`
640  The weight to give each input exposure in the coadd
641  mask : `lsst.afw.image.Mask`
642  Mask to use for each new model image.
643  gain : `float`
644  Relative weight to give the new solution when updating the model.
645  modelWeights : `numpy.ndarray` or `float`
646  A 2D array of weight values that tapers smoothly to zero away from detected sources.
647  Set to a placeholder value of 1.0 if ``self.config.useModelWeights`` is False.
648  refImage : `lsst.afw.image.MaskedImage`
649  A reference image used to supply the default pixel values.
650 
651  Returns
652  -------
653  dcrModel : `lsst.pipe.tasks.DcrModel`
654  New model of the true sky after correcting chromatic effects.
655  """
656  maskMap = self.setRejectedMaskMapping(statsCtrl)
657  clipped = dcrModels.mask.getPlaneBitMask("CLIPPED")
658  newModelImages = []
659  for subfilter, model in enumerate(dcrModels):
660  residualsList = [next(residualGenerator) for residualGenerator in residualGeneratorList]
661  residual = afwMath.statisticsStack(residualsList, statsFlags, statsCtrl, weightList,
662  clipped, maskMap)
663  residual.setXY0(bbox.getBegin())
664  # `MaskedImage`s only support in-place addition, so rename for readability
665  residual += model[bbox]
666  newModel = residual
667  # Catch any invalid values
668  badPixels = ~np.isfinite(newModel.image.array)
669  # Overwrite the mask with one calculated previously. If the mask is allowed to adjust
670  # every iteration, masked regions will continually expand.
671  newModel.setMask(mask[bbox])
672  newModel.image.array[badPixels] = model[bbox].image.array[badPixels]
673  if self.config.regularizeModelIterations > 0:
674  dcrModels.regularizeModelIter(subfilter, newModel, bbox,
675  self.config.regularizeModelIterations,
676  self.config.regularizationWidth)
677  newModelImages.append(newModel)
678  if self.config.regularizeModelFrequency > 0:
679  dcrModels.regularizeModelFreq(newModelImages, bbox, statsCtrl,
680  self.config.regularizeModelFrequency,
681  self.config.regularizationWidth,
682  mask=mask)
683  dcrModels.conditionDcrModel(newModelImages, bbox, gain=gain)
684  self.applyModelWeights(newModelImages, refImage[bbox], modelWeights)
685  return DcrModel(newModelImages, dcrModels.filter, dcrModels.psf)
686 
687  def calculateConvergence(self, dcrModels, bbox, tempExpRefList, imageScalerList,
688  weightList, spanSetMaskList, statsCtrl):
689  """Calculate a quality of fit metric for the matched templates.
690 
691  Parameters
692  ----------
693  dcrModels : `lsst.pipe.tasks.DcrModel`
694  Best fit model of the true sky after correcting chromatic effects.
695  bbox : `lsst.afw.geom.box.Box2I`
696  Sub-region to coadd
697  tempExpRefList : `list` of `lsst.daf.persistence.ButlerDataRef`
698  The data references to the input warped exposures.
699  imageScalerList : `list` of `lsst.pipe.task.ImageScaler`
700  The image scalars correct for the zero point of the exposures.
701  weightList : `list` of `float`
702  The weight to give each input exposure in the coadd
703  spanSetMaskList : `list` of `dict` containing spanSet lists, or None
704  Each element is dict with keys = mask plane name to add the spans to
705  statsCtrl : `lsst.afw.math.StatisticsControl`
706  Statistics control object for coadd
707 
708  Returns
709  -------
710  convergenceMetric : `float`
711  Quality of fit metric for all input exposures, within the sub-region
712  """
713  significanceImage = np.abs(dcrModels.getReferenceImage(bbox))
714  nSigma = 3.
715  significanceImage += nSigma*dcrModels.calculateNoiseCutoff(dcrModels[1], statsCtrl,
716  bufferSize=self.bufferSize)
717  if np.max(significanceImage) == 0:
718  significanceImage += 1.
719  tempExpName = self.getTempExpDatasetName(self.warpType)
720  weight = 0
721  metric = 0.
722  metricList = {}
723  zipIterables = zip(tempExpRefList, weightList, imageScalerList, spanSetMaskList)
724  for tempExpRef, expWeight, imageScaler, altMaskSpans in zipIterables:
725  exposure = tempExpRef.get(tempExpName + "_sub", bbox=bbox)
726  imageScaler.scaleMaskedImage(exposure.maskedImage)
727  singleMetric = self.calculateSingleConvergence(dcrModels, exposure, significanceImage, statsCtrl,
728  altMaskSpans=altMaskSpans)
729  metric += singleMetric*expWeight
730  metricList[tempExpRef.dataId["visit"]] = singleMetric
731  weight += expWeight
732  self.log.info("Individual metrics:\n%s", metricList)
733  return 1.0 if weight == 0.0 else metric/weight
734 
735  def calculateSingleConvergence(self, dcrModels, exposure, significanceImage,
736  statsCtrl, altMaskSpans=None):
737  """Calculate a quality of fit metric for a single matched template.
738 
739  Parameters
740  ----------
741  dcrModels : `lsst.pipe.tasks.DcrModel`
742  Best fit model of the true sky after correcting chromatic effects.
743  exposure : `lsst.afw.image.ExposureF`
744  The input warped exposure to evaluate.
745  significanceImage : `numpy.ndarray`
746  Array of weights for each pixel corresponding to its significance
747  for the convergence calculation.
748  statsCtrl : `lsst.afw.math.StatisticsControl`
749  Statistics control object for coadd
750  altMaskSpans : `dict` containing spanSet lists, or None
751  The keys of the `dict` equal the mask plane name to add the spans to
752 
753  Returns
754  -------
755  convergenceMetric : `float`
756  Quality of fit metric for one exposure, within the sub-region.
757  """
758  convergeMask = exposure.mask.getPlaneBitMask(self.config.convergenceMaskPlanes)
759  templateImage = dcrModels.buildMatchedTemplate(warpCtrl=self.warpCtrl,
760  visitInfo=exposure.getInfo().getVisitInfo(),
761  bbox=exposure.getBBox(),
762  wcs=exposure.getInfo().getWcs())
763  diffVals = np.abs(exposure.image.array - templateImage.image.array)*significanceImage
764  refVals = np.abs(exposure.image.array + templateImage.image.array)*significanceImage/2.
765 
766  finitePixels = np.isfinite(diffVals)
767  if altMaskSpans is not None:
768  self.applyAltMaskPlanes(exposure.mask, altMaskSpans)
769  goodMaskPixels = exposure.mask.array & statsCtrl.getAndMask() == 0
770  convergeMaskPixels = exposure.mask.array & convergeMask > 0
771  usePixels = finitePixels & goodMaskPixels & convergeMaskPixels
772  if np.sum(usePixels) == 0:
773  metric = 0.
774  else:
775  diffUse = diffVals[usePixels]
776  refUse = refVals[usePixels]
777  metric = np.sum(diffUse/np.median(diffUse))/np.sum(refUse/np.median(diffUse))
778  return metric
779 
780  def stackCoadd(self, dcrCoadds):
781  """Add a list of sub-band coadds together.
782 
783  Parameters
784  ----------
785  dcrCoadds : `list` of `lsst.afw.image.ExposureF`
786  A list of coadd exposures, each exposure containing
787  the model for one subfilter.
788 
789  Returns
790  -------
791  coaddExposure : `lsst.afw.image.ExposureF`
792  A single coadd exposure that is the sum of the sub-bands.
793  """
794  coaddExposure = dcrCoadds[0].clone()
795  for coadd in dcrCoadds[1:]:
796  coaddExposure.maskedImage += coadd.maskedImage
797  return coaddExposure
798 
799  def fillCoadd(self, dcrModels, skyInfo, tempExpRefList, weightList, calibration=None, coaddInputs=None,
800  mask=None, variance=None):
801  """Create a list of coadd exposures from a list of masked images.
802 
803  Parameters
804  ----------
805  dcrModels : `lsst.pipe.tasks.DcrModel`
806  Best fit model of the true sky after correcting chromatic effects.
807  skyInfo : `lsst.pipe.base.Struct`
808  Patch geometry information, from getSkyInfo
809  tempExpRefList : `list` of `lsst.daf.persistence.ButlerDataRef`
810  The data references to the input warped exposures.
811  weightList : `list` of `float`
812  The weight to give each input exposure in the coadd
813  calibration : `lsst.afw.Image.Calib`, optional
814  Scale factor to set the photometric zero point of an exposure.
815  coaddInputs : `lsst.afw.Image.CoaddInputs`, optional
816  A record of the observations that are included in the coadd.
817  mask : `lsst.afw.image.Mask`, optional
818  Optional mask to override the values in the final coadd.
819  variance : `lsst.afw.image.Image`, optional
820  Optional variance plane to override the values in the final coadd.
821 
822  Returns
823  -------
824  dcrCoadds : `list` of `lsst.afw.image.ExposureF`
825  A list of coadd exposures, each exposure containing
826  the model for one subfilter.
827  """
828  dcrCoadds = []
829  for model in dcrModels:
830  coaddExposure = afwImage.ExposureF(skyInfo.bbox, skyInfo.wcs)
831  if calibration is not None:
832  coaddExposure.setCalib(calibration)
833  if coaddInputs is not None:
834  coaddExposure.getInfo().setCoaddInputs(coaddInputs)
835  # Set the metadata for the coadd, including PSF and aperture corrections.
836  self.assembleMetadata(coaddExposure, tempExpRefList, weightList)
837  coaddUtils.setCoaddEdgeBits(model[skyInfo.bbox].mask, model[skyInfo.bbox].variance)
838  coaddExposure.setMaskedImage(model[skyInfo.bbox])
839  if mask is not None:
840  coaddExposure.setMask(mask)
841  if variance is not None:
842  coaddExposure.setVariance(variance)
843  dcrCoadds.append(coaddExposure)
844  return dcrCoadds
845 
846  def calculateGain(self, convergenceList, gainList):
847  """Calculate the gain to use for the current iteration.
848 
849  After calculating a new DcrModel, each value is averaged with the
850  value in the corresponding pixel from the previous iteration. This
851  reduces oscillating solutions that iterative techniques are plagued by,
852  and speeds convergence. By far the biggest changes to the model
853  happen in the first couple iterations, so we can also use a more
854  aggressive gain later when the model is changing slowly.
855 
856  Parameters
857  ----------
858  convergenceList : `list` of `float`
859  The quality of fit metric from each previous iteration.
860  gainList : `list` of `float`
861  The gains used in each previous iteration: appended with the new
862  gain value.
863  Gains are numbers between ``self.config.baseGain`` and 1.
864 
865  Returns
866  -------
867  gain : `float`
868  Relative weight to give the new solution when updating the model.
869  A value of 1.0 gives equal weight to both solutions.
870 
871  Raises
872  ------
873  ValueError
874  If ``len(convergenceList) != len(gainList)+1``.
875  """
876  nIter = len(convergenceList)
877  if nIter != len(gainList) + 1:
878  raise ValueError("convergenceList (%d) must be one element longer than gainList (%d)."
879  % (len(convergenceList), len(gainList)))
880 
881  if self.config.baseGain is None:
882  # If ``baseGain`` is not set, calculate it from the number of DCR subfilters
883  # The more subfilters being modeled, the lower the gain should be.
884  baseGain = 1./(self.config.dcrNumSubfilters - 1)
885  else:
886  baseGain = self.config.baseGain
887 
888  if self.config.useProgressiveGain and nIter > 2:
889  # To calculate the best gain to use, compare the past gains that have been used
890  # with the resulting convergences to estimate the best gain to use.
891  # Algorithmically, this is a Kalman filter.
892  # If forward modeling proceeds perfectly, the convergence metric should
893  # asymptotically approach a final value.
894  # We can estimate that value from the measured changes in convergence
895  # weighted by the gains used in each previous iteration.
896  estFinalConv = [((1 + gainList[i])*convergenceList[i + 1] - convergenceList[i])/gainList[i]
897  for i in range(nIter - 1)]
898  # The convergence metric is strictly positive, so if the estimated final convergence is
899  # less than zero, force it to zero.
900  estFinalConv = np.array(estFinalConv)
901  estFinalConv[estFinalConv < 0] = 0
902  # Because the estimate may slowly change over time, only use the most recent measurements.
903  estFinalConv = np.median(estFinalConv[max(nIter - 5, 0):])
904  lastGain = gainList[-1]
905  lastConv = convergenceList[-2]
906  newConv = convergenceList[-1]
907  # The predicted convergence is the value we would get if the new model calculated
908  # in the previous iteration was perfect. Recall that the updated model that is
909  # actually used is the gain-weighted average of the new and old model,
910  # so the convergence would be similarly weighted.
911  predictedConv = (estFinalConv*lastGain + lastConv)/(1. + lastGain)
912  # If the measured and predicted convergence are very close, that indicates
913  # that our forward model is accurate and we can use a more aggressive gain
914  # If the measured convergence is significantly worse (or better!) than predicted,
915  # that indicates that the model is not converging as expected and
916  # we should use a more conservative gain.
917  delta = (predictedConv - newConv)/((lastConv - estFinalConv)/(1 + lastGain))
918  newGain = 1 - abs(delta)
919  # Average the gains to prevent oscillating solutions.
920  newGain = (newGain + lastGain)/2.
921  gain = max(baseGain, newGain)
922  else:
923  gain = baseGain
924  gainList.append(gain)
925  return gain
926 
927  def calculateModelWeights(self, dcrModels, dcrBBox):
928  """Build an array that smoothly tapers to 0 away from detected sources.
929 
930  Parameters
931  ----------
932  dcrModels : `lsst.pipe.tasks.DcrModel`
933  Best fit model of the true sky after correcting chromatic effects.
934  dcrBBox : `lsst.afw.geom.box.Box2I`
935  Sub-region of the coadd which includes a buffer to allow for DCR.
936 
937  Returns
938  -------
939  weights : `numpy.ndarray` or `float`
940  A 2D array of weight values that tapers smoothly to zero away from detected sources.
941  Set to a placeholder value of 1.0 if ``self.config.useModelWeights`` is False.
942 
943  Raises
944  ------
945  ValueError
946  If ``useModelWeights`` is set and ``modelWeightsWidth`` is negative.
947  """
948  if not self.config.useModelWeights:
949  return 1.0
950  if self.config.modelWeightsWidth < 0:
951  raise ValueError("modelWeightsWidth must not be negative if useModelWeights is set")
952  convergeMask = dcrModels.mask.getPlaneBitMask(self.config.convergenceMaskPlanes)
953  convergeMaskPixels = dcrModels.mask[dcrBBox].array & convergeMask > 0
954  weights = np.zeros_like(dcrModels[0][dcrBBox].image.array)
955  weights[convergeMaskPixels] = 1.
956  weights = ndimage.filters.gaussian_filter(weights, self.config.modelWeightsWidth)
957  weights /= np.max(weights)
958  return weights
959 
960  def applyModelWeights(self, modelImages, refImage, modelWeights):
961  """Smoothly replace model pixel values with those from a
962  reference at locations away from detected sources.
963 
964  Parameters
965  ----------
966  modelImages : `list` of `lsst.afw.image.MaskedImage`
967  The new DCR model images from the current iteration.
968  The values will be modified in place.
969  refImage : `lsst.afw.image.MaskedImage`
970  A reference image used to supply the default pixel values.
971  modelWeights : `numpy.ndarray` or `float`
972  A 2D array of weight values that tapers smoothly to zero away from detected sources.
973  Set to a placeholder value of 1.0 if ``self.config.useModelWeights`` is False.
974  """
975  if self.config.useModelWeights:
976  for model in modelImages:
977  model.image.array *= modelWeights
978  model.image.array += refImage.image.array*(1. - modelWeights)/self.config.dcrNumSubfilters
Extent< int, N > ceil(Extent< double, N > const &input) noexcept
Return the component-wise ceil (round towards more positive).
Definition: Extent.cc:118
Angle abs(Angle const &a)
Definition: Angle.h:106
def runDataRef(self, dataRef, selectDataList=None, warpRefList=None)
def newModelFromResidual(self, dcrModels, residualGeneratorList, bbox, statsFlags, statsCtrl, weightList, mask, gain, modelWeights, refImage)
def findArtifacts(self, templateCoadd, tempExpRefList, imageScalerList)
def assembleMetadata(self, coaddExposure, tempExpRefList, weightList)
def calculateNImage(self, dcrModels, bbox, tempExpRefList, spanSetMaskList, statsCtrl)
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
Parameters to control convolution.
Definition: warpExposure.h:250
def fillCoadd(self, dcrModels, skyInfo, tempExpRefList, weightList, calibration=None, coaddInputs=None, mask=None, variance=None)
def calculateSingleConvergence(self, dcrModels, exposure, significanceImage, statsCtrl, altMaskSpans=None)
def applyAltMaskPlanes(self, mask, altMaskSpans)
def calculateConvergence(self, dcrModels, bbox, tempExpRefList, imageScalerList, weightList, spanSetMaskList, statsCtrl)
def dcrAssembleSubregion(self, dcrModels, bbox, dcrBBox, tempExpRefList, imageScalerList, weightList, spanSetMaskList, statsFlags, statsCtrl, convergenceMetric, baseMask, gain, modelWeights, refImage)
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:133
def getTempExpDatasetName(self, warpType="direct")
Definition: coaddBase.py:164
def dcrResiduals(self, residual, visitInfo, bbox, wcs, filterInfo)
int max
def applyModelWeights(self, modelImages, refImage, modelWeights)
def run(self, skyInfo, tempExpRefList, imageScalerList, weightList, supplementaryData=None)
def applyDcr(maskedImage, dcr, warpCtrl, bbox=None, useInverse=False, splitSubfilters=False)
Definition: dcrModel.py:561
def prepareDcrInputs(self, templateCoadd, tempExpRefList, weightList)
std::shared_ptr< image::MaskedImage< PixelT > > statisticsStack(image::MaskedImage< PixelT > const &image, Property flags, char dimension, StatisticsControl const &sctrl)
A function to compute statistics on the rows or columns of an image.
Definition: Stack.cc:478
def processResults(self, coaddExposure, dataRef)
def calculateGain(self, convergenceList, gainList)
def calculateDcr(visitInfo, wcs, filterInfo, dcrNumSubfilters, splitSubfilters=False)
Definition: dcrModel.py:608
An integer coordinate rectangle.
Definition: Box.h:54