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