LSSTApplications  19.0.0-14-gb0260a2+72efe9b372,20.0.0+7927753e06,20.0.0+8829bf0056,20.0.0+995114c5d2,20.0.0+b6f4b2abd1,20.0.0+bddc4f4cbe,20.0.0-1-g253301a+8829bf0056,20.0.0-1-g2b7511a+0d71a2d77f,20.0.0-1-g5b95a8c+7461dd0434,20.0.0-12-g321c96ea+23efe4bbff,20.0.0-16-gfab17e72e+fdf35455f6,20.0.0-2-g0070d88+ba3ffc8f0b,20.0.0-2-g4dae9ad+ee58a624b3,20.0.0-2-g61b8584+5d3db074ba,20.0.0-2-gb780d76+d529cf1a41,20.0.0-2-ged6426c+226a441f5f,20.0.0-2-gf072044+8829bf0056,20.0.0-2-gf1f7952+ee58a624b3,20.0.0-20-geae50cf+e37fec0aee,20.0.0-25-g3dcad98+544a109665,20.0.0-25-g5eafb0f+ee58a624b3,20.0.0-27-g64178ef+f1f297b00a,20.0.0-3-g4cc78c6+e0676b0dc8,20.0.0-3-g8f21e14+4fd2c12c9a,20.0.0-3-gbd60e8c+187b78b4b8,20.0.0-3-gbecbe05+48431fa087,20.0.0-38-ge4adf513+a12e1f8e37,20.0.0-4-g97dc21a+544a109665,20.0.0-4-gb4befbc+087873070b,20.0.0-4-gf910f65+5d3db074ba,20.0.0-5-gdfe0fee+199202a608,20.0.0-5-gfbfe500+d529cf1a41,20.0.0-6-g64f541c+d529cf1a41,20.0.0-6-g9a5b7a1+a1cd37312e,20.0.0-68-ga3f3dda+5fca18c6a4,20.0.0-9-g4aef684+e18322736b,w.2020.45
LSSTDataManagementBasePackage
insertFakes.py
Go to the documentation of this file.
1 # This file is part of pipe tasks
2 #
3 # Developed for the LSST Data Management System.
4 # This product includes software developed by the LSST Project
5 # (http://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 <http://www.gnu.org/licenses/>.
21 
22 """
23 Insert fakes into deepCoadds
24 """
25 import galsim
26 from astropy.table import Table
27 import numpy as np
28 
29 import lsst.geom as geom
30 import lsst.afw.image as afwImage
31 import lsst.afw.math as afwMath
32 import lsst.pex.config as pexConfig
33 import lsst.pipe.base as pipeBase
34 
35 from lsst.pipe.base import CmdLineTask, PipelineTask, PipelineTaskConfig, PipelineTaskConnections
37 from lsst.pex.exceptions import LogicError, InvalidParameterError
38 from lsst.coadd.utils.coaddDataIdContainer import ExistingCoaddDataIdContainer
39 from lsst.geom import SpherePoint, radians, Box2D
40 from lsst.sphgeom import ConvexPolygon
41 
42 __all__ = ["InsertFakesConfig", "InsertFakesTask"]
43 
44 
45 class InsertFakesConnections(PipelineTaskConnections, defaultTemplates={"CoaddName": "deep"},
46  dimensions=("tract", "patch", "band", "skymap")):
47 
48  image = cT.Input(
49  doc="Image into which fakes are to be added.",
50  name="{CoaddName}Coadd",
51  storageClass="ExposureF",
52  dimensions=("tract", "patch", "band", "skymap")
53  )
54 
55  fakeCat = cT.Input(
56  doc="Catalog of fake sources to draw inputs from.",
57  name="{CoaddName}Coadd_fakeSourceCat",
58  storageClass="DataFrame",
59  dimensions=("tract", "skymap")
60  )
61 
62  imageWithFakes = cT.Output(
63  doc="Image with fake sources added.",
64  name="fakes_{CoaddName}Coadd",
65  storageClass="ExposureF",
66  dimensions=("tract", "patch", "band", "skymap")
67  )
68 
69 
70 class InsertFakesConfig(PipelineTaskConfig,
71  pipelineConnections=InsertFakesConnections):
72  """Config for inserting fake sources
73 
74  Notes
75  -----
76  The default column names are those from the University of Washington sims database.
77  """
78 
79  raColName = pexConfig.Field(
80  doc="RA column name used in the fake source catalog.",
81  dtype=str,
82  default="raJ2000",
83  )
84 
85  decColName = pexConfig.Field(
86  doc="Dec. column name used in the fake source catalog.",
87  dtype=str,
88  default="decJ2000",
89  )
90 
91  doCleanCat = pexConfig.Field(
92  doc="If true removes bad sources from the catalog.",
93  dtype=bool,
94  default=True,
95  )
96 
97  diskHLR = pexConfig.Field(
98  doc="Column name for the disk half light radius used in the fake source catalog.",
99  dtype=str,
100  default="DiskHalfLightRadius",
101  )
102 
103  bulgeHLR = pexConfig.Field(
104  doc="Column name for the bulge half light radius used in the fake source catalog.",
105  dtype=str,
106  default="BulgeHalfLightRadius",
107  )
108 
109  magVar = pexConfig.Field(
110  doc="The column name for the magnitude calculated taking variability into account. In the format "
111  "``filter name``magVar, e.g. imagVar for the magnitude in the i band.",
112  dtype=str,
113  default="%smagVar",
114  )
115 
116  nDisk = pexConfig.Field(
117  doc="The column name for the sersic index of the disk component used in the fake source catalog.",
118  dtype=str,
119  default="disk_n",
120  )
121 
122  nBulge = pexConfig.Field(
123  doc="The column name for the sersic index of the bulge component used in the fake source catalog.",
124  dtype=str,
125  default="bulge_n",
126  )
127 
128  aDisk = pexConfig.Field(
129  doc="The column name for the semi major axis length of the disk component used in the fake source"
130  "catalog.",
131  dtype=str,
132  default="a_d",
133  )
134 
135  aBulge = pexConfig.Field(
136  doc="The column name for the semi major axis length of the bulge component.",
137  dtype=str,
138  default="a_b",
139  )
140 
141  bDisk = pexConfig.Field(
142  doc="The column name for the semi minor axis length of the disk component.",
143  dtype=str,
144  default="b_d",
145  )
146 
147  bBulge = pexConfig.Field(
148  doc="The column name for the semi minor axis length of the bulge component used in the fake source "
149  "catalog.",
150  dtype=str,
151  default="b_b",
152  )
153 
154  paDisk = pexConfig.Field(
155  doc="The column name for the PA of the disk component used in the fake source catalog.",
156  dtype=str,
157  default="pa_disk",
158  )
159 
160  paBulge = pexConfig.Field(
161  doc="The column name for the PA of the bulge component used in the fake source catalog.",
162  dtype=str,
163  default="pa_bulge",
164  )
165 
166  sourceType = pexConfig.Field(
167  doc="The column name for the source type used in the fake source catalog.",
168  dtype=str,
169  default="sourceType",
170  )
171 
172  fakeType = pexConfig.Field(
173  doc="What type of fake catalog to use, snapshot (includes variability in the magnitudes calculated "
174  "from the MJD of the image), static (no variability) or filename for a user defined fits"
175  "catalog.",
176  dtype=str,
177  default="static",
178  )
179 
180  calibFluxRadius = pexConfig.Field(
181  doc="Aperture radius (in pixels) that was used to define the calibration for this image+catalog. "
182  "This will be used to produce the correct instrumental fluxes within the radius. "
183  "This value should match that of the field defined in slot_CalibFlux_instFlux.",
184  dtype=float,
185  default=12.0,
186  )
187 
188  coaddName = pexConfig.Field(
189  doc="The name of the type of coadd used",
190  dtype=str,
191  default="deep",
192  )
193 
194  doSubSelectSources = pexConfig.Field(
195  doc="Set to True if you wish to sub select sources to be input based on the value in the column"
196  "set in the sourceSelectionColName config option.",
197  dtype=bool,
198  default=False
199  )
200 
201  sourceSelectionColName = pexConfig.Field(
202  doc="The name of the column in the input fakes catalogue to be used to determine which sources to"
203  "add, default is none and when this is used all sources are added.",
204  dtype=str,
205  default="templateSource"
206  )
207 
208  insertImages = pexConfig.Field(
209  doc="Insert images directly? True or False.",
210  dtype=bool,
211  default=False,
212  )
213 
214  doProcessAllDataIds = pexConfig.Field(
215  doc="If True, all input data IDs will be processed, even those containing no fake sources.",
216  dtype=bool,
217  default=False,
218  )
219 
220 
221 class InsertFakesTask(PipelineTask, CmdLineTask):
222  """Insert fake objects into images.
223 
224  Add fake stars and galaxies to the given image, read in through the dataRef. Galaxy parameters are read in
225  from the specified file and then modelled using galsim.
226 
227  `InsertFakesTask` has five functions that make images of the fake sources and then add them to the
228  image.
229 
230  `addPixCoords`
231  Use the WCS information to add the pixel coordinates of each source.
232  `mkFakeGalsimGalaxies`
233  Use Galsim to make fake double sersic galaxies for each set of galaxy parameters in the input file.
234  `mkFakeStars`
235  Use the PSF information from the image to make a fake star using the magnitude information from the
236  input file.
237  `cleanCat`
238  Remove rows of the input fake catalog which have half light radius, of either the bulge or the disk,
239  that are 0. Also removes rows that have Sersic index outside of galsim's allowed paramters. If
240  the config option sourceSelectionColName is set then this function limits the catalog of input fakes
241  to only those which are True in this column.
242  `addFakeSources`
243  Add the fake sources to the image.
244 
245  """
246 
247  _DefaultName = "insertFakes"
248  ConfigClass = InsertFakesConfig
249 
250  def runDataRef(self, dataRef):
251  """Read in/write out the required data products and add fake sources to the deepCoadd.
252 
253  Parameters
254  ----------
255  dataRef : `lsst.daf.persistence.butlerSubset.ButlerDataRef`
256  Data reference defining the image to have fakes added to it
257  Used to access the following data products:
258  deepCoadd
259  """
260 
261  infoStr = "Adding fakes to: tract: %d, patch: %s, filter: %s" % (dataRef.dataId["tract"],
262  dataRef.dataId["patch"],
263  dataRef.dataId["filter"])
264  self.log.info(infoStr)
265 
266  # To do: should it warn when asked to insert variable sources into the coadd
267 
268  if self.config.fakeType == "static":
269  fakeCat = dataRef.get("deepCoadd_fakeSourceCat").toDataFrame()
270  # To do: DM-16254, the read and write of the fake catalogs will be changed once the new pipeline
271  # task structure for ref cats is in place.
272  self.fakeSourceCatType = "deepCoadd_fakeSourceCat"
273  else:
274  fakeCat = Table.read(self.config.fakeType).to_pandas()
275 
276  coadd = dataRef.get("deepCoadd")
277  wcs = coadd.getWcs()
278  photoCalib = coadd.getPhotoCalib()
279 
280  imageWithFakes = self.run(fakeCat, coadd, wcs, photoCalib)
281 
282  dataRef.put(imageWithFakes.imageWithFakes, "fakes_deepCoadd")
283 
284  def runQuantum(self, butlerQC, inputRefs, outputRefs):
285  inputs = butlerQC.get(inputRefs)
286  inputs["wcs"] = inputs["image"].getWcs()
287  inputs["photoCalib"] = inputs["image"].getPhotoCalib()
288 
289  outputs = self.run(**inputs)
290  butlerQC.put(outputs, outputRefs)
291 
292  @classmethod
293  def _makeArgumentParser(cls):
294  parser = pipeBase.ArgumentParser(name=cls._DefaultName)
295  parser.add_id_argument(name="--id", datasetType="deepCoadd",
296  help="data IDs for the deepCoadd, e.g. --id tract=12345 patch=1,2 filter=r",
297  ContainerClass=ExistingCoaddDataIdContainer)
298  return parser
299 
300  def run(self, fakeCat, image, wcs, photoCalib):
301  """Add fake sources to an image.
302 
303  Parameters
304  ----------
305  fakeCat : `pandas.core.frame.DataFrame`
306  The catalog of fake sources to be input
307  image : `lsst.afw.image.exposure.exposure.ExposureF`
308  The image into which the fake sources should be added
309  wcs : `lsst.afw.geom.SkyWcs`
310  WCS to use to add fake sources
311  photoCalib : `lsst.afw.image.photoCalib.PhotoCalib`
312  Photometric calibration to be used to calibrate the fake sources
313 
314  Returns
315  -------
316  resultStruct : `lsst.pipe.base.struct.Struct`
317  contains : image : `lsst.afw.image.exposure.exposure.ExposureF`
318 
319  Notes
320  -----
321  Adds pixel coordinates for each source to the fakeCat and removes objects with bulge or disk half
322  light radius = 0 (if ``config.doCleanCat = True``).
323 
324  Adds the ``Fake`` mask plane to the image which is then set by `addFakeSources` to mark where fake
325  sources have been added. Uses the information in the ``fakeCat`` to make fake galaxies (using galsim)
326  and fake stars, using the PSF models from the PSF information for the image. These are then added to
327  the image and the image with fakes included returned.
328 
329  The galsim galaxies are made using a double sersic profile, one for the bulge and one for the disk,
330  this is then convolved with the PSF at that point.
331  """
332 
333  image.mask.addMaskPlane("FAKE")
334  self.bitmask = image.mask.getPlaneBitMask("FAKE")
335  self.log.info("Adding mask plane with bitmask %d" % self.bitmask)
336 
337  fakeCat = self.addPixCoords(fakeCat, wcs)
338  fakeCat = self.trimFakeCat(fakeCat, image, wcs)
339  band = image.getFilter().getCanonicalName()[0]
340  psf = image.getPsf()
341  pixelScale = wcs.getPixelScale().asArcseconds()
342 
343  if len(fakeCat) > 0:
344  if isinstance(fakeCat[self.config.sourceType].iloc[0], str):
345  galCheckVal = "galaxy"
346  starCheckVal = "star"
347  elif isinstance(fakeCat[self.config.sourceType].iloc[0], bytes):
348  galCheckVal = b"galaxy"
349  starCheckVal = b"star"
350  elif isinstance(fakeCat[self.config.sourceType].iloc[0], (int, float)):
351  galCheckVal = 1
352  starCheckVal = 0
353  else:
354  raise TypeError("sourceType column does not have required type, should be str, bytes or int")
355 
356  if not self.config.insertImages:
357  if self.config.doCleanCat:
358  fakeCat = self.cleanCat(fakeCat, starCheckVal)
359 
360  galaxies = (fakeCat[self.config.sourceType] == galCheckVal)
361  galImages = self.mkFakeGalsimGalaxies(fakeCat[galaxies], band, photoCalib, pixelScale, psf,
362  image)
363 
364  stars = (fakeCat[self.config.sourceType] == starCheckVal)
365  starImages = self.mkFakeStars(fakeCat[stars], band, photoCalib, psf, image)
366  else:
367  galImages, starImages = self.processImagesForInsertion(fakeCat, wcs, psf, photoCalib, band,
368  pixelScale)
369 
370  image = self.addFakeSources(image, galImages, "galaxy")
371  image = self.addFakeSources(image, starImages, "star")
372  elif len(fakeCat) == 0 and self.config.doProcessAllDataIds:
373  self.log.warn("No fakes found for this dataRef; processing anyway.")
374  else:
375  raise RuntimeError("No fakes found for this dataRef.")
376 
377  resultStruct = pipeBase.Struct(imageWithFakes=image)
378 
379  return resultStruct
380 
381  def processImagesForInsertion(self, fakeCat, wcs, psf, photoCalib, band, pixelScale):
382  """Process images from files into the format needed for insertion.
383 
384  Parameters
385  ----------
386  fakeCat : `pandas.core.frame.DataFrame`
387  The catalog of fake sources to be input
388  wcs : `lsst.afw.geom.skyWcs.skyWcs.SkyWc`
389  WCS to use to add fake sources
390  psf : `lsst.meas.algorithms.coaddPsf.coaddPsf.CoaddPsf` or
391  `lsst.meas.extensions.psfex.psfexPsf.PsfexPsf`
392  The PSF information to use to make the PSF images
393  photoCalib : `lsst.afw.image.photoCalib.PhotoCalib`
394  Photometric calibration to be used to calibrate the fake sources
395  band : `str`
396  The filter band that the observation was taken in.
397  pixelScale : `float`
398  The pixel scale of the image the sources are to be added to.
399 
400  Returns
401  -------
402  galImages : `list`
403  A list of tuples of `lsst.afw.image.exposure.exposure.ExposureF` and
404  `lsst.geom.Point2D` of their locations.
405  For sources labelled as galaxy.
406  starImages : `list`
407  A list of tuples of `lsst.afw.image.exposure.exposure.ExposureF` and
408  `lsst.geom.Point2D` of their locations.
409  For sources labelled as star.
410 
411  Notes
412  -----
413  The input fakes catalog needs to contain the absolute path to the image in the
414  band that is being used to add images to. It also needs to have the R.A. and
415  declination of the fake source in radians and the sourceType of the object.
416  """
417  galImages = []
418  starImages = []
419 
420  self.log.info("Processing %d fake images" % len(fakeCat))
421 
422  for (imFile, sourceType, mag, x, y) in zip(fakeCat[band + "imFilename"].array,
423  fakeCat["sourceType"].array,
424  fakeCat[self.config.magVar % band].array,
425  fakeCat["x"].array, fakeCat["y"].array):
426 
427  im = afwImage.ImageF.readFits(imFile)
428 
429  xy = geom.Point2D(x, y)
430 
431  # We put these two PSF calculations within this same try block so that we catch cases
432  # where the object's position is outside of the image.
433  try:
434  correctedFlux = psf.computeApertureFlux(self.config.calibFluxRadius, xy)
435  psfKernel = psf.computeKernelImage(xy).getArray()
436  psfKernel /= correctedFlux
437 
438  except InvalidParameterError:
439  self.log.info("%s at %0.4f, %0.4f outside of image" % (sourceType, x, y))
440  continue
441 
442  psfIm = galsim.InterpolatedImage(galsim.Image(psfKernel), scale=pixelScale)
443  galsimIm = galsim.InterpolatedImage(galsim.Image(im.array), scale=pixelScale)
444  convIm = galsim.Convolve([galsimIm, psfIm])
445 
446  try:
447  outIm = convIm.drawImage(scale=pixelScale, method="real_space").array
448  except (galsim.errors.GalSimFFTSizeError, MemoryError):
449  continue
450 
451  imSum = np.sum(outIm)
452  divIm = outIm/imSum
453 
454  try:
455  flux = photoCalib.magnitudeToInstFlux(mag, xy)
456  except LogicError:
457  flux = 0
458 
459  imWithFlux = flux*divIm
460 
461  if sourceType == b"galaxy":
462  galImages.append((afwImage.ImageF(imWithFlux), xy))
463  if sourceType == b"star":
464  starImages.append((afwImage.ImageF(imWithFlux), xy))
465 
466  return galImages, starImages
467 
468  def addPixCoords(self, fakeCat, wcs):
469 
470  """Add pixel coordinates to the catalog of fakes.
471 
472  Parameters
473  ----------
474  fakeCat : `pandas.core.frame.DataFrame`
475  The catalog of fake sources to be input
476  wcs : `lsst.afw.geom.SkyWcs`
477  WCS to use to add fake sources
478 
479  Returns
480  -------
481  fakeCat : `pandas.core.frame.DataFrame`
482 
483  Notes
484  -----
485  The default option is to use the WCS information from the image. If the ``useUpdatedCalibs`` config
486  option is set then it will use the updated WCS from jointCal.
487  """
488 
489  ras = fakeCat[self.config.raColName].values
490  decs = fakeCat[self.config.decColName].values
491  skyCoords = [SpherePoint(ra, dec, radians) for (ra, dec) in zip(ras, decs)]
492  pixCoords = wcs.skyToPixel(skyCoords)
493  xs = [coord.getX() for coord in pixCoords]
494  ys = [coord.getY() for coord in pixCoords]
495  fakeCat["x"] = xs
496  fakeCat["y"] = ys
497 
498  return fakeCat
499 
500  def trimFakeCat(self, fakeCat, image, wcs):
501  """Trim the fake cat to about the size of the input image.
502 
503  Parameters
504  ----------
505  fakeCat : `pandas.core.frame.DataFrame`
506  The catalog of fake sources to be input
507  image : `lsst.afw.image.exposure.exposure.ExposureF`
508  The image into which the fake sources should be added
509  wcs : `lsst.afw.geom.SkyWcs`
510  WCS to use to add fake sources
511 
512  Returns
513  -------
514  fakeCat : `pandas.core.frame.DataFrame`
515  The original fakeCat trimmed to the area of the image
516  """
517 
518  bbox = Box2D(image.getBBox())
519  corners = bbox.getCorners()
520 
521  skyCorners = wcs.pixelToSky(corners)
522  region = ConvexPolygon([s.getVector() for s in skyCorners])
523 
524  def trim(row):
525  coord = SpherePoint(row[self.config.raColName], row[self.config.decColName], radians)
526  return region.contains(coord.getVector())
527 
528  return fakeCat[fakeCat.apply(trim, axis=1)]
529 
530  def mkFakeGalsimGalaxies(self, fakeCat, band, photoCalib, pixelScale, psf, image):
531  """Make images of fake galaxies using GalSim.
532 
533  Parameters
534  ----------
535  band : `str`
536  pixelScale : `float`
537  psf : `lsst.meas.extensions.psfex.psfexPsf.PsfexPsf`
538  The PSF information to use to make the PSF images
539  fakeCat : `pandas.core.frame.DataFrame`
540  The catalog of fake sources to be input
541  photoCalib : `lsst.afw.image.photoCalib.PhotoCalib`
542  Photometric calibration to be used to calibrate the fake sources
543 
544  Yields
545  -------
546  galImages : `generator`
547  A generator of tuples of `lsst.afw.image.exposure.exposure.ExposureF` and
548  `lsst.geom.Point2D` of their locations.
549 
550  Notes
551  -----
552 
553  Fake galaxies are made by combining two sersic profiles, one for the bulge and one for the disk. Each
554  component has an individual sersic index (n), a, b and position angle (PA). The combined profile is
555  then convolved with the PSF at the specified x, y position on the image.
556 
557  The names of the columns in the ``fakeCat`` are configurable and are the column names from the
558  University of Washington simulations database as default. For more information see the doc strings
559  attached to the config options.
560 
561  See mkFakeStars doc string for an explanation of calibration to instrumental flux.
562  """
563 
564  self.log.info("Making %d fake galaxy images" % len(fakeCat))
565 
566  for (index, row) in fakeCat.iterrows():
567  xy = geom.Point2D(row["x"], row["y"])
568 
569  # We put these two PSF calculations within this same try block so that we catch cases
570  # where the object's position is outside of the image.
571  try:
572  correctedFlux = psf.computeApertureFlux(self.config.calibFluxRadius, xy)
573  psfKernel = psf.computeKernelImage(xy).getArray()
574  psfKernel /= correctedFlux
575 
576  except InvalidParameterError:
577  self.log.info("Galaxy at %0.4f, %0.4f outside of image" % (row["x"], row["y"]))
578  continue
579 
580  try:
581  flux = photoCalib.magnitudeToInstFlux(row[self.config.magVar % band], xy)
582  except LogicError:
583  flux = 0
584 
585  bulge = galsim.Sersic(row[self.config.nBulge], half_light_radius=row[self.config.bulgeHLR])
586  axisRatioBulge = row[self.config.bBulge]/row[self.config.aBulge]
587  bulge = bulge.shear(q=axisRatioBulge, beta=((90 - row[self.config.paBulge])*galsim.degrees))
588 
589  disk = galsim.Sersic(row[self.config.nDisk], half_light_radius=row[self.config.diskHLR])
590  axisRatioDisk = row[self.config.bDisk]/row[self.config.aDisk]
591  disk = disk.shear(q=axisRatioDisk, beta=((90 - row[self.config.paDisk])*galsim.degrees))
592 
593  gal = disk + bulge
594  gal = gal.withFlux(flux)
595 
596  psfIm = galsim.InterpolatedImage(galsim.Image(psfKernel), scale=pixelScale)
597  gal = galsim.Convolve([gal, psfIm])
598  try:
599  galIm = gal.drawImage(scale=pixelScale, method="real_space").array
600  except (galsim.errors.GalSimFFTSizeError, MemoryError):
601  continue
602 
603  yield (afwImage.ImageF(galIm), xy)
604 
605  def mkFakeStars(self, fakeCat, band, photoCalib, psf, image):
606 
607  """Make fake stars based off the properties in the fakeCat.
608 
609  Parameters
610  ----------
611  band : `str`
612  psf : `lsst.meas.extensions.psfex.psfexPsf.PsfexPsf`
613  The PSF information to use to make the PSF images
614  fakeCat : `pandas.core.frame.DataFrame`
615  The catalog of fake sources to be input
616  image : `lsst.afw.image.exposure.exposure.ExposureF`
617  The image into which the fake sources should be added
618  photoCalib : `lsst.afw.image.photoCalib.PhotoCalib`
619  Photometric calibration to be used to calibrate the fake sources
620 
621  Yields
622  -------
623  starImages : `generator`
624  A generator of tuples of `lsst.afw.image.ImageF` of fake stars and
625  `lsst.geom.Point2D` of their locations.
626 
627  Notes
628  -----
629  To take a given magnitude and translate to the number of counts in the image
630  we use photoCalib.magnitudeToInstFlux, which returns the instrumental flux for the
631  given calibration radius used in the photometric calibration step.
632  Thus `calibFluxRadius` should be set to this same radius so that we can normalize
633  the PSF model to the correct instrumental flux within calibFluxRadius.
634  """
635 
636  self.log.info("Making %d fake star images" % len(fakeCat))
637 
638  for (index, row) in fakeCat.iterrows():
639  xy = geom.Point2D(row["x"], row["y"])
640 
641  # We put these two PSF calculations within this same try block so that we catch cases
642  # where the object's position is outside of the image.
643  try:
644  correctedFlux = psf.computeApertureFlux(self.config.calibFluxRadius, xy)
645  starIm = psf.computeImage(xy)
646  starIm /= correctedFlux
647 
648  except InvalidParameterError:
649  self.log.info("Star at %0.4f, %0.4f outside of image" % (row["x"], row["y"]))
650  continue
651 
652  try:
653  flux = photoCalib.magnitudeToInstFlux(row[self.config.magVar % band], xy)
654  except LogicError:
655  flux = 0
656 
657  starIm *= flux
658  yield ((starIm.convertF(), xy))
659 
660  def cleanCat(self, fakeCat, starCheckVal):
661  """Remove rows from the fakes catalog which have HLR = 0 for either the buldge or disk component,
662  also remove galaxies that have Sersic index outside the galsim min and max
663  allowed (0.3 <= n <= 6.2).
664 
665  Parameters
666  ----------
667  fakeCat : `pandas.core.frame.DataFrame`
668  The catalog of fake sources to be input
669  starCheckVal : `str`, `bytes` or `int`
670  The value that is set in the sourceType column to specifiy an object is a star.
671 
672  Returns
673  -------
674  fakeCat : `pandas.core.frame.DataFrame`
675  The input catalog of fake sources but with the bad objects removed
676 
677  Notes
678  -----
679  If the config option sourceSelectionColName is set then only objects with this column set to True
680  will be added.
681  """
682 
683  rowsToKeep = (((fakeCat[self.config.bulgeHLR] != 0.0) & (fakeCat[self.config.diskHLR] != 0.0))
684  | (fakeCat[self.config.sourceType] == starCheckVal))
685  numRowsNotUsed = len(fakeCat) - len(np.where(rowsToKeep)[0])
686  self.log.info("Removing %d rows with HLR = 0 for either the bulge or disk" % numRowsNotUsed)
687  fakeCat = fakeCat[rowsToKeep]
688 
689  minN = galsim.Sersic._minimum_n
690  maxN = galsim.Sersic._maximum_n
691  rowsWithGoodSersic = (((fakeCat[self.config.nBulge] >= minN) & (fakeCat[self.config.nBulge] <= maxN)
692  & (fakeCat[self.config.nDisk] >= minN) & (fakeCat[self.config.nDisk] <= maxN))
693  | (fakeCat[self.config.sourceType] == starCheckVal))
694  numRowsNotUsed = len(fakeCat) - len(np.where(rowsWithGoodSersic)[0])
695  self.log.info("Removing %d rows of galaxies with nBulge or nDisk outside of %0.2f <= n <= %0.2f" %
696  (numRowsNotUsed, minN, maxN))
697  fakeCat = fakeCat[rowsWithGoodSersic]
698 
699  if self.config.doSubSelectSources:
700  try:
701  rowsSelected = (fakeCat[self.config.sourceSelectionColName])
702  except KeyError:
703  raise KeyError("Given column, %s, for source selection not found." %
704  self.config.sourceSelectionColName)
705  numRowsNotUsed = len(fakeCat) - len(rowsSelected)
706  self.log.info("Removing %d rows which were not designated as template sources" % numRowsNotUsed)
707  fakeCat = fakeCat[rowsSelected]
708 
709  return fakeCat
710 
711  def addFakeSources(self, image, fakeImages, sourceType):
712  """Add the fake sources to the given image
713 
714  Parameters
715  ----------
716  image : `lsst.afw.image.exposure.exposure.ExposureF`
717  The image into which the fake sources should be added
718  fakeImages : `typing.Iterator` [`tuple` ['lsst.afw.image.ImageF`, `lsst.geom.Point2d`]]
719  An iterator of tuples that contains (or generates) images of fake sources,
720  and the locations they are to be inserted at.
721  sourceType : `str`
722  The type (star/galaxy) of fake sources input
723 
724  Returns
725  -------
726  image : `lsst.afw.image.exposure.exposure.ExposureF`
727 
728  Notes
729  -----
730  Uses the x, y information in the ``fakeCat`` to position an image of the fake interpolated onto the
731  pixel grid of the image. Sets the ``FAKE`` mask plane for the pixels added with the fake source.
732  """
733 
734  imageBBox = image.getBBox()
735  imageMI = image.maskedImage
736 
737  for (fakeImage, xy) in fakeImages:
738  X0 = xy.getX() - fakeImage.getWidth()/2 + 0.5
739  Y0 = xy.getY() - fakeImage.getHeight()/2 + 0.5
740  self.log.debug("Adding fake source at %d, %d" % (xy.getX(), xy.getY()))
741  if sourceType == "galaxy":
742  interpFakeImage = afwMath.offsetImage(fakeImage, X0, Y0, "lanczos3")
743  interpFakeImBBox = interpFakeImage.getBBox()
744  else:
745  interpFakeImage = fakeImage
746  interpFakeImBBox = fakeImage.getBBox()
747 
748  interpFakeImBBox.clip(imageBBox)
749  imageMIView = imageMI.Factory(imageMI, interpFakeImBBox)
750 
751  if interpFakeImBBox.getArea() > 0:
752  clippedFakeImage = interpFakeImage.Factory(interpFakeImage, interpFakeImBBox)
753  clippedFakeImageMI = afwImage.MaskedImageF(clippedFakeImage)
754  clippedFakeImageMI.mask.set(self.bitmask)
755  imageMIView += clippedFakeImageMI
756 
757  return image
758 
759  def _getMetadataName(self):
760  """Disable metadata writing"""
761  return None
lsst.pipe.base.connections.PipelineTaskConnections.config
config
Definition: connections.py:375
lsst::afw::image
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
Definition: imageAlgorithm.dox:1
lsst::log.log.logContinued.warn
def warn(fmt, *args)
Definition: logContinued.py:205
lsst::log.log.logContinued.info
def info(fmt, *args)
Definition: logContinued.py:201
lsst.pipe.tasks.insertFakes.trimFakeCat
def trimFakeCat(self, fakeCat, image, wcs)
Definition: insertFakes.py:500
lsst.pipe.tasks.insertFakes.cleanCat
def cleanCat(self, fakeCat, starCheckVal)
Definition: insertFakes.py:660
lsst.gdb.afw.printers.debug
bool debug
Definition: printers.py:9
lsst.pipe.tasks.insertFakes.InsertFakesConnections
Definition: insertFakes.py:45
lsst.pipe.tasks.insertFakes.addPixCoords
def addPixCoords(self, fakeCat, wcs)
Definition: insertFakes.py:468
lsst::sphgeom::ConvexPolygon
ConvexPolygon is a closed convex polygon on the unit sphere.
Definition: ConvexPolygon.h:57
lsst::sphgeom
Definition: Angle.h:38
lsst.pipe.tasks.assembleCoadd.run
def run(self, skyInfo, tempExpRefList, imageScalerList, weightList, altMaskList=None, mask=None, supplementaryData=None)
Definition: assembleCoadd.py:720
lsst.pipe.base.pipelineTask.PipelineTask
Definition: pipelineTask.py:32
lsst.pipe.tasks.insertFakes.addFakeSources
def addFakeSources(self, image, fakeImages, sourceType)
Definition: insertFakes.py:711
lsst.pex.config
Definition: __init__.py:1
lsst.pipe.base.connections.PipelineTaskConnections
Definition: connections.py:260
lsst.pipe.tasks.insertFakes.mkFakeGalsimGalaxies
def mkFakeGalsimGalaxies(self, fakeCat, band, photoCalib, pixelScale, psf, image)
Definition: insertFakes.py:530
lsst.pipe.base.config.PipelineTaskConfig
Definition: config.py:115
lsst::geom
Definition: AffineTransform.h:36
lsst::afw::math
Definition: statistics.dox:6
lsst::geom::Point< double, 2 >
lsst::afw::math::offsetImage
std::shared_ptr< ImageT > offsetImage(ImageT const &image, float dx, float dy, std::string const &algorithmName="lanczos5", unsigned int buffer=0)
Return an image offset by (dx, dy) using the specified algorithm.
Definition: offsetImage.cc:41
lsst.pipe.tasks.insertFakes.mkFakeStars
def mkFakeStars(self, fakeCat, band, photoCalib, psf, image)
Definition: insertFakes.py:605
lsst.pex::exceptions
Definition: Exception.h:37
lsst::geom::SpherePoint
Point in an unspecified spherical coordinate system.
Definition: SpherePoint.h:57
lsst::coadd::utils.coaddDataIdContainer
Definition: coaddDataIdContainer.py:1
lsst::geom::Box2D
A floating-point coordinate rectangle geometry.
Definition: Box.h:413
lsst.pipe.base
Definition: __init__.py:1
lsst.pipe.base.connectionTypes
Definition: connectionTypes.py:1
lsst.pipe.base.cmdLineTask.CmdLineTask
Definition: cmdLineTask.py:544