LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
snapPsfMatch.py
Go to the documentation of this file.
1 # This file is part of ip_diffim.
2 #
3 # Developed for the LSST Data Management System.
4 # This product includes software developed by the LSST Project
5 # (https://www.lsst.org).
6 # See the COPYRIGHT file at the top-level directory of this distribution
7 # for details of code ownership.
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <https://www.gnu.org/licenses/>.
21 
22 __all__ = ["SnapPsfMatchConfigDF", "SnapPsfMatchConfigAL", "SnapPsfMatchConfig", "SnapPsfMatchTask"]
23 
24 import lsst.pex.config as pexConfig
25 from .psfMatch import PsfMatchConfigDF, PsfMatchConfigAL
26 from .imagePsfMatch import ImagePsfMatchTask, ImagePsfMatchConfig
27 
28 
30  """Delta-function Psf-matching config optimized for snap subtraction"""
31 
32  def setDefaults(self):
33  PsfMatchConfigDF.setDefaults(self)
34 
35  # No regularization
36  self.useRegularizationuseRegularizationuseRegularization = False
37 
38  # Pca
41  self.numPrincipalComponentsnumPrincipalComponentsnumPrincipalComponents = 5
42 
43 
45  """Sum-of-Gaussian (Alard-Lupton) Psf-matching config optimized for snap subtraction"""
46 
47  def setDefaults(self):
48  PsfMatchConfigAL.setDefaults(self)
49 
50  # Simple basis set
51  self.alardNGaussalardNGaussalardNGauss = 2
52  self.alardDegGaussalardDegGaussalardDegGauss = (4, 2)
53  self.alardSigGaussalardSigGaussalardSigGauss = (1.0, 2.5)
54 
55 
57  kernel = pexConfig.ConfigChoiceField(
58  doc="kernel type",
59  typemap=dict(
60  AL=SnapPsfMatchConfigAL,
61  DF=SnapPsfMatchConfigDF
62  ),
63  default="AL",
64  )
65 
66  doWarping = pexConfig.Field(
67  dtype=bool,
68  doc="Warp the snaps?",
69  default=False
70  )
71 
72  def setDefaults(self):
73  ImagePsfMatchConfig.setDefaults(self)
74 
75  # No spatial variation in model
76  self.kernelkernelkernel.active.spatialKernelOrder = 0
77 
78  # Don't fit for differential background
79  self.kernelkernelkernel.active.fitForBackground = False
80 
81  # Small kernel size
82  self.kernelkernelkernel.active.kernelSize = 7
83 
84  # With zero spatial order don't worry about spatial clipping
85  self.kernelkernelkernel.active.spatialKernelClipping = False
86 
87 
89  """Image-based Psf-matching of two subsequent snaps from the same visit
90 
91  Notes
92  -----
93  This Task differs from ImagePsfMatchTask in that it matches two Exposures assuming that the images have
94  been acquired very closely in time. Under this assumption, the astrometric misalignments and/or
95  relative distortions should be within a pixel, and the Psf-shapes should be very similar. As a
96  consequence, the default configurations for this class assume a very simple solution.
97 
98  - The spatial variation in the kernel (SnapPsfMatchConfig.spatialKernelOrder) is assumed to be zero
99 
100  - With no spatial variation, we turn of the spatial
101  clipping loops (SnapPsfMatchConfig.spatialKernelClipping)
102 
103  - The differential background is not fit for (SnapPsfMatchConfig.fitForBackground)
104 
105  - The kernel is expected to be appx.
106  a delta function, and has a small size (SnapPsfMatchConfig.kernelSize)
107 
108  The sub-configurations for the Alard-Lupton (SnapPsfMatchConfigAL)
109  and delta-function (SnapPsfMatchConfigDF)
110  bases also are designed to generate a small, simple kernel.
111 
112  Task initialization
113 
114  Initialization is the same as base class ImagePsfMatch.__init__,
115  with the difference being that the Task's
116  ConfigClass is SnapPsfMatchConfig.
117 
118  Invoking the Task
119 
120  The Task is only configured to have a subtractExposures method, which in turn calls
121  ImagePsfMatchTask.subtractExposures.
122 
123  Configuration parameters
124 
125  See SnapPsfMatchConfig, which uses either SnapPsfMatchConfigDF and SnapPsfMatchConfigAL
126  as its active configuration.
127 
128  Debug variables
129 
130  The lsst.pipe.base.cmdLineTask.CmdLineTask command line task interface supports a
131  flag -d/--debug to importdebug.py from your PYTHONPATH. The relevant contents of debug.py
132  for this Task include:
133 
134  .. code-block:: py
135 
136  import sys
137  import lsstDebug
138  def DebugInfo(name):
139  di = lsstDebug.getInfo(name)
140  if name == "lsst.ip.diffim.psfMatch":
141  di.display = True # enable debug output
142  di.maskTransparency = 80 # display mask transparency
143  di.displayCandidates = True # show all the candidates and residuals
144  di.displayKernelBasis = False # show kernel basis functions
145  di.displayKernelMosaic = True # show kernel realized across the image
146  di.plotKernelSpatialModel = False # show coefficients of spatial model
147  di.showBadCandidates = True # show the bad candidates (red) along with good (green)
148  elif name == "lsst.ip.diffim.imagePsfMatch":
149  di.display = True # enable debug output
150  di.maskTransparency = 30 # display mask transparency
151  di.displayTemplate = True # show full (remapped) template
152  di.displaySciIm = True # show science image to match to
153  di.displaySpatialCells = True # show spatial cells
154  di.displayDiffIm = True # show difference image
155  di.showBadCandidates = True # show the bad candidates (red) along with good (green)
156  elif name == "lsst.ip.diffim.diaCatalogSourceSelector":
157  di.display = False # enable debug output
158  di.maskTransparency = 30 # display mask transparency
159  di.displayExposure = True # show exposure with candidates indicated
160  di.pauseAtEnd = False # pause when done
161  return di
162  lsstDebug.Info = DebugInfo
163  lsstDebug.frame = 1
164 
165  Note that if you want addional logging info, you may add to your scripts:
166 
167  .. code-block:: py
168 
169  import lsst.log.utils as logUtils
170  logUtils.traceSetAt("lsst.ip.diffim", 4)
171 
172  Examples
173  --------
174  This code is snapPsfMatchTask.py in the examples directory, and can be run as e.g.
175 
176  .. code-block:: py
177 
178  examples/snapPsfMatchTask.py
179  examples/snapPsfMatchTask.py --debug
180  examples/snapPsfMatchTask.py --debug --template /path/to/templateExp.fits
181  --science /path/to/scienceExp.fits
182 
183  First, create a subclass of SnapPsfMatchTask that accepts two exposures.
184  Ideally these exposures would have been taken back-to-back,
185  such that the pointing/background/Psf does not vary substantially between the two:
186 
187  .. code-block:: py
188 
189  class MySnapPsfMatchTask(SnapPsfMatchTask):
190  def __init__(self, *args, **kwargs):
191  SnapPsfMatchTask.__init__(self, *args, **kwargs)
192  def run(self, templateExp, scienceExp):
193  return self.subtractExposures(templateExp, scienceExp)
194 
195  And allow the user the freedom to either run the script in default mode,
196  or point to their own images on disk. Note that these images must be
197  readable as an lsst.afw.image.Exposure
198 
199  .. code-block:: py
200 
201  if __name__ == "__main__":
202  import argparse
203  parser = argparse.ArgumentParser(description="Demonstrate the use of ImagePsfMatchTask")
204  parser.add_argument("--debug", "-d", action="store_true", help="Load debug.py?", default=False)
205  parser.add_argument("--template", "-t", help="Template Exposure to use", default=None)
206  parser.add_argument("--science", "-s", help="Science Exposure to use", default=None)
207  args = parser.parse_args()
208 
209  We have enabled some minor display debugging in this script via the –debug option. However,
210  if you have an lsstDebug debug.in your PYTHONPATH you will get additional debugging displays.
211  The following block checks for this script
212 
213  .. code-block:: py
214 
215  if args.debug:
216  try:
217  import debug
218  # Since I am displaying 2 images here, set the starting frame number for the LSST debug LSST
219  debug.lsstDebug.frame = 3
220  except ImportError as e:
221  print(e, file=sys.stderr)
222 
223  Finally, we call a run method that we define below.
224  First set up a Config and choose the basis set to use:
225 
226  .. code-block:: py
227 
228  def run(args):
229  #
230  # Create the Config and use sum of gaussian basis
231  #
232  config = SnapPsfMatchTask.ConfigClass()
233  config.doWarping = True
234  config.kernel.name = "AL"
235 
236  Make sure the images (if any) that were sent to the script exist on disk and are readable.
237  If no images are sent, make some fake data up for the sake of this example script
238  (have a look at the code if you want more details on generateFakeImages;
239  as a detail of how the fake images were made, you do have to fit for a differential background):
240 
241  .. code-block:: py
242 
243  # Run the requested method of the Task
244  if args.template is not None and args.science is not None:
245  if not os.path.isfile(args.template):
246  raise FileNotFoundError("Template image %s does not exist" % (args.template))
247  if not os.path.isfile(args.science):
248  raise FileNotFoundError("Science image %s does not exist" % (args.science))
249  try:
250  templateExp = afwImage.ExposureF(args.template)
251  except Exception as e:
252  raise RuntimeError("Cannot read template image %s" % (args.template))
253  try:
254  scienceExp = afwImage.ExposureF(args.science)
255  except Exception as e:
256  raise RuntimeError("Cannot read science image %s" % (args.science))
257  else:
258  templateExp, scienceExp = generateFakeImages()
259  config.kernel.active.fitForBackground = True
260  config.kernel.active.spatialBgOrder = 0
261  config.kernel.active.sizeCellX = 128
262  config.kernel.active.sizeCellY = 128
263 
264  Display the two images if -debug
265 
266  .. code-block:: py
267 
268  if args.debug:
269  afwDisplay.Display(frame=1).mtv(templateExp, title="Example script: Input Template")
270  afwDisplay.Display(frame=2).mtv(scienceExp, title="Example script: Input Science Image")
271 
272  Create and run the Task
273 
274  .. code-block:: py
275 
276  # Create the Task
277  psfMatchTask = MySnapPsfMatchTask(config=config)
278  # Run the Task
279  result = psfMatchTask.run(templateExp, scienceExp)
280 
281  And finally provide optional debugging display of the Psf-matched (via the Psf models) science image:
282 
283  .. code-block:: py
284 
285  if args.debug:
286  # See if the LSST debug has incremented the frame number; if not start with frame 3
287  try:
288  frame = debug.lsstDebug.frame + 1
289  except Exception:
290  frame = 3
291  afwDisplay.Display(frame=frame).mtv(result.matchedExposure,
292  title="Example script: Matched Template Image")
293  if "subtractedExposure" in result.getDict():
294  afwDisplay.Display(frame=frame + 1).mtv(result.subtractedExposure,
295  title="Example script: Subtracted Image")
296 
297  """
298 
299  ConfigClass = SnapPsfMatchConfig
300 
301  # Override ImagePsfMatchTask.subtractExposures to set doWarping on config.doWarping
302  def subtractExposures(self, templateExposure, scienceExposure,
303  templateFwhmPix=None, scienceFwhmPix=None,
304  candidateList=None):
305  return ImagePsfMatchTask.subtractExposures(self,
306  templateExposure=templateExposure,
307  scienceExposure=scienceExposure,
308  templateFwhmPix=templateFwhmPix,
309  scienceFwhmPix=scienceFwhmPix,
310  candidateList=candidateList,
311  doWarping=self.config.doWarping,
312  )
def subtractExposures(self, templateExposure, scienceExposure, templateFwhmPix=None, scienceFwhmPix=None, candidateList=None)