23 Insert fakes into deepCoadds 
   26 from astropy.table 
import Table
 
   35 from lsst.pipe.base import CmdLineTask, PipelineTask, PipelineTaskConfig, PipelineTaskConnections
 
   36 import lsst.pipe.base.connectionTypes 
as cT
 
   39 from lsst.geom import SpherePoint, radians, Box2D, Point2D
 
   41 __all__ = [
"InsertFakesConfig", 
"InsertFakesTask"]
 
   44 def _add_fake_sources(exposure, objects, calibFluxRadius=12.0, logger=None):
 
   45     """Add fake sources to the given exposure 
   49     exposure : `lsst.afw.image.exposure.exposure.ExposureF` 
   50         The exposure into which the fake sources should be added 
   51     objects : `typing.Iterator` [`tuple` ['lsst.geom.SpherePoint`, `galsim.GSObject`]] 
   52         An iterator of tuples that contains (or generates) locations and object 
   53         surface brightness profiles to inject. 
   54     calibFluxRadius : `float`, optional 
   55         Aperture radius (in pixels) used to define the calibration for this 
   56         exposure+catalog.  This is used to produce the correct instrumental fluxes 
   57         within the radius.  The value should match that of the field defined in 
   58         slot_CalibFlux_instFlux. 
   59     logger : `lsst.log.log.log.Log` or `logging.Logger`, optional 
   62     exposure.mask.addMaskPlane(
"FAKE")
 
   63     bitmask = exposure.mask.getPlaneBitMask(
"FAKE")
 
   65         logger.info(f
"Adding mask plane with bitmask {bitmask}")
 
   67     wcs = exposure.getWcs()
 
   68     psf = exposure.getPsf()
 
   70     bbox = exposure.getBBox()
 
   71     fullBounds = galsim.BoundsI(bbox.minX, bbox.maxX, bbox.minY, bbox.maxY)
 
   72     gsImg = galsim.Image(exposure.image.array, bounds=fullBounds)
 
   74     pixScale = wcs.getPixelScale().asArcseconds()
 
   76     for spt, gsObj 
in objects:
 
   77         pt = wcs.skyToPixel(spt)
 
   78         posd = galsim.PositionD(pt.x, pt.y)
 
   79         posi = galsim.PositionI(pt.x//1, pt.y//1)
 
   81             logger.debug(f
"Adding fake source at {pt}")
 
   83         mat = wcs.linearizePixelToSky(spt, geom.arcseconds).getMatrix()
 
   84         gsWCS = galsim.JacobianWCS(mat[0, 0], mat[0, 1], mat[1, 0], mat[1, 1])
 
   89         gsPixScale = np.sqrt(gsWCS.pixelArea())
 
   90         if gsPixScale < pixScale/2 
or gsPixScale > pixScale*2:
 
   94             psfArr = psf.computeKernelImage(pt).array
 
   95         except InvalidParameterError:
 
   98                 np.clip(pt.x, bbox.minX, bbox.maxX),
 
   99                 np.clip(pt.y, bbox.minY, bbox.maxY)
 
  101             if pt == contained_pt:  
 
  104                         "Cannot compute Psf for object at {}; skipping",
 
  110                 psfArr = psf.computeKernelImage(contained_pt).array
 
  111             except InvalidParameterError:
 
  114                         "Cannot compute Psf for object at {}; skipping",
 
  119         apCorr = psf.computeApertureFlux(calibFluxRadius)
 
  121         gsPSF = galsim.InterpolatedImage(galsim.Image(psfArr), wcs=gsWCS)
 
  123         conv = galsim.Convolve(gsObj, gsPSF)
 
  124         stampSize = conv.getGoodImageSize(gsWCS.minLinearScale())
 
  125         subBounds = galsim.BoundsI(posi).withBorder(stampSize//2)
 
  126         subBounds &= fullBounds
 
  128         if subBounds.area() > 0:
 
  129             subImg = gsImg[subBounds]
 
  130             offset = posd - subBounds.true_center
 
  147             exposure[subBox].mask.array |= bitmask
 
  150 def _isWCSGalsimDefault(wcs, hdr):
 
  151     """Decide if wcs = galsim.PixelScale(1.0) is explicitly present in header, 
  152     or if it's just the galsim default. 
  157         Potentially default WCS. 
  158     hdr : galsim.fits.FitsHeader 
  159         Header as read in by galsim. 
  164         True if default, False if explicitly set in header. 
  166     if wcs != galsim.PixelScale(1.0):
 
  168     if hdr.get(
'GS_WCS') 
is not None:
 
  170     if hdr.get(
'CTYPE1', 
'LINEAR') == 
'LINEAR':
 
  171         return not any(k 
in hdr 
for k 
in [
'CD1_1', 
'CDELT1'])
 
  172     for wcs_type 
in galsim.fitswcs.fits_wcs_types:
 
  175             wcs_type._readHeader(hdr)
 
  180         return not any(k 
in hdr 
for k 
in [
'CD1_1', 
'CDELT1'])
 
  184                              defaultTemplates={
"coaddName": 
"deep",
 
  185                                                "fakesType": 
"fakes_"},
 
  186                              dimensions=(
"tract", 
"patch", 
"band", 
"skymap")):
 
  189         doc=
"Image into which fakes are to be added.",
 
  190         name=
"{coaddName}Coadd",
 
  191         storageClass=
"ExposureF",
 
  192         dimensions=(
"tract", 
"patch", 
"band", 
"skymap")
 
  196         doc=
"Catalog of fake sources to draw inputs from.",
 
  197         name=
"{fakesType}fakeSourceCat",
 
  198         storageClass=
"DataFrame",
 
  199         dimensions=(
"tract", 
"skymap")
 
  202     imageWithFakes = cT.Output(
 
  203         doc=
"Image with fake sources added.",
 
  204         name=
"{fakesType}{coaddName}Coadd",
 
  205         storageClass=
"ExposureF",
 
  206         dimensions=(
"tract", 
"patch", 
"band", 
"skymap")
 
  210 class InsertFakesConfig(PipelineTaskConfig,
 
  211                         pipelineConnections=InsertFakesConnections):
 
  212     """Config for inserting fake sources 
  217     doCleanCat = pexConfig.Field(
 
  218         doc=
"If true removes bad sources from the catalog.",
 
  223     fakeType = pexConfig.Field(
 
  224         doc=
"What type of fake catalog to use, snapshot (includes variability in the magnitudes calculated " 
  225             "from the MJD of the image), static (no variability) or filename for a user defined fits" 
  231     calibFluxRadius = pexConfig.Field(
 
  232         doc=
"Aperture radius (in pixels) that was used to define the calibration for this image+catalog. " 
  233         "This will be used to produce the correct instrumental fluxes within the radius. " 
  234         "This value should match that of the field defined in slot_CalibFlux_instFlux.",
 
  239     coaddName = pexConfig.Field(
 
  240         doc=
"The name of the type of coadd used",
 
  245     doSubSelectSources = pexConfig.Field(
 
  246         doc=
"Set to True if you wish to sub select sources to be input based on the value in the column" 
  247             "set in the sourceSelectionColName config option.",
 
  252     insertImages = pexConfig.Field(
 
  253         doc=
"Insert images directly? True or False.",
 
  258     doProcessAllDataIds = pexConfig.Field(
 
  259         doc=
"If True, all input data IDs will be processed, even those containing no fake sources.",
 
  264     trimBuffer = pexConfig.Field(
 
  265         doc=
"Size of the pixel buffer surrounding the image. Only those fake sources with a centroid" 
  266         "falling within the image+buffer region will be considered for fake source injection.",
 
  271     sourceType = pexConfig.Field(
 
  272         doc=
"The column name for the source type used in the fake source catalog.",
 
  274         default=
"sourceType",
 
  277     fits_alignment = pexConfig.ChoiceField(
 
  278         doc=
"How should injections from FITS files be aligned?",
 
  282                 "Input image will be transformed such that the local WCS in " 
  283                 "the FITS header matches the local WCS in the target image. " 
  284                 "I.e., North, East, and angular distances in the input image " 
  285                 "will match North, East, and angular distances in the target " 
  289                 "Input image will _not_ be transformed.  Up, right, and pixel " 
  290                 "distances in the input image will match up, right and pixel " 
  291                 "distances in the target image." 
  299     ra_col = pexConfig.Field(
 
  300         doc=
"Source catalog column name for RA (in radians).",
 
  305     dec_col = pexConfig.Field(
 
  306         doc=
"Source catalog column name for dec (in radians).",
 
  311     bulge_semimajor_col = pexConfig.Field(
 
  312         doc=
"Source catalog column name for the semimajor axis (in arcseconds) " 
  313             "of the bulge half-light ellipse.",
 
  315         default=
"bulge_semimajor",
 
  318     bulge_axis_ratio_col = pexConfig.Field(
 
  319         doc=
"Source catalog column name for the axis ratio of the bulge " 
  320             "half-light ellipse.",
 
  322         default=
"bulge_axis_ratio",
 
  325     bulge_pa_col = pexConfig.Field(
 
  326         doc=
"Source catalog column name for the position angle (measured from " 
  327             "North through East in degrees) of the semimajor axis of the bulge " 
  328             "half-light ellipse.",
 
  333     bulge_n_col = pexConfig.Field(
 
  334         doc=
"Source catalog column name for the Sersic index of the bulge.",
 
  339     disk_semimajor_col = pexConfig.Field(
 
  340         doc=
"Source catalog column name for the semimajor axis (in arcseconds) " 
  341             "of the disk half-light ellipse.",
 
  343         default=
"disk_semimajor",
 
  346     disk_axis_ratio_col = pexConfig.Field(
 
  347         doc=
"Source catalog column name for the axis ratio of the disk " 
  348             "half-light ellipse.",
 
  350         default=
"disk_axis_ratio",
 
  353     disk_pa_col = pexConfig.Field(
 
  354         doc=
"Source catalog column name for the position angle (measured from " 
  355             "North through East in degrees) of the semimajor axis of the disk " 
  356             "half-light ellipse.",
 
  361     disk_n_col = pexConfig.Field(
 
  362         doc=
"Source catalog column name for the Sersic index of the disk.",
 
  367     bulge_disk_flux_ratio_col = pexConfig.Field(
 
  368         doc=
"Source catalog column name for the bulge/disk flux ratio.",
 
  370         default=
"bulge_disk_flux_ratio",
 
  373     mag_col = pexConfig.Field(
 
  374         doc=
"Source catalog column name template for magnitudes, in the format " 
  375             "``filter name``_mag_col.  E.g., if this config variable is set to " 
  376             "``%s_mag``, then the i-band magnitude will be searched for in the " 
  377             "``i_mag`` column of the source catalog.",
 
  382     select_col = pexConfig.Field(
 
  383         doc=
"Source catalog column name to be used to select which sources to " 
  391     raColName = pexConfig.Field(
 
  392         doc=
"RA column name used in the fake source catalog.",
 
  395         deprecated=
"Use `ra_col` instead." 
  398     decColName = pexConfig.Field(
 
  399         doc=
"Dec. column name used in the fake source catalog.",
 
  402         deprecated=
"Use `dec_col` instead." 
  405     diskHLR = pexConfig.Field(
 
  406         doc=
"Column name for the disk half light radius used in the fake source catalog.",
 
  408         default=
"DiskHalfLightRadius",
 
  410             "Use `disk_semimajor_col`, `disk_axis_ratio_col`, and `disk_pa_col`" 
  411             " to specify disk half-light ellipse." 
  415     aDisk = pexConfig.Field(
 
  416         doc=
"The column name for the semi major axis length of the disk component used in the fake source" 
  421             "Use `disk_semimajor_col`, `disk_axis_ratio_col`, and `disk_pa_col`" 
  422             " to specify disk half-light ellipse." 
  426     bDisk = pexConfig.Field(
 
  427         doc=
"The column name for the semi minor axis length of the disk component.",
 
  431             "Use `disk_semimajor_col`, `disk_axis_ratio_col`, and `disk_pa_col`" 
  432             " to specify disk half-light ellipse." 
  436     paDisk = pexConfig.Field(
 
  437         doc=
"The column name for the PA of the disk component used in the fake source catalog.",
 
  441             "Use `disk_semimajor_col`, `disk_axis_ratio_col`, and `disk_pa_col`" 
  442             " to specify disk half-light ellipse." 
  446     nDisk = pexConfig.Field(
 
  447         doc=
"The column name for the sersic index of the disk component used in the fake source catalog.",
 
  450         deprecated=
"Use `disk_n` instead." 
  453     bulgeHLR = pexConfig.Field(
 
  454         doc=
"Column name for the bulge half light radius used in the fake source catalog.",
 
  456         default=
"BulgeHalfLightRadius",
 
  458             "Use `bulge_semimajor_col`, `bulge_axis_ratio_col`, and " 
  459             "`bulge_pa_col` to specify disk half-light ellipse." 
  463     aBulge = pexConfig.Field(
 
  464         doc=
"The column name for the semi major axis length of the bulge component.",
 
  468             "Use `bulge_semimajor_col`, `bulge_axis_ratio_col`, and " 
  469             "`bulge_pa_col` to specify disk half-light ellipse." 
  473     bBulge = pexConfig.Field(
 
  474         doc=
"The column name for the semi minor axis length of the bulge component used in the fake source " 
  479             "Use `bulge_semimajor_col`, `bulge_axis_ratio_col`, and " 
  480             "`bulge_pa_col` to specify disk half-light ellipse." 
  484     paBulge = pexConfig.Field(
 
  485         doc=
"The column name for the PA of the bulge component used in the fake source catalog.",
 
  489             "Use `bulge_semimajor_col`, `bulge_axis_ratio_col`, and " 
  490             "`bulge_pa_col` to specify disk half-light ellipse." 
  494     nBulge = pexConfig.Field(
 
  495         doc=
"The column name for the sersic index of the bulge component used in the fake source catalog.",
 
  498         deprecated=
"Use `bulge_n` instead." 
  501     magVar = pexConfig.Field(
 
  502         doc=
"The column name for the magnitude calculated taking variability into account. In the format " 
  503             "``filter name``magVar, e.g. imagVar for the magnitude in the i band.",
 
  506         deprecated=
"Use `mag_col` instead." 
  509     sourceSelectionColName = pexConfig.Field(
 
  510         doc=
"The name of the column in the input fakes catalogue to be used to determine which sources to" 
  511             "add, default is none and when this is used all sources are added.",
 
  513         default=
"templateSource",
 
  514         deprecated=
"Use `select_col` instead." 
  518 class InsertFakesTask(PipelineTask, CmdLineTask):
 
  519     """Insert fake objects into images. 
  521     Add fake stars and galaxies to the given image, read in through the dataRef. Galaxy parameters are read in 
  522     from the specified file and then modelled using galsim. 
  524     `InsertFakesTask` has five functions that make images of the fake sources and then add them to the 
  528         Use the WCS information to add the pixel coordinates of each source. 
  529     `mkFakeGalsimGalaxies` 
  530         Use Galsim to make fake double sersic galaxies for each set of galaxy parameters in the input file. 
  532         Use the PSF information from the image to make a fake star using the magnitude information from the 
  535         Remove rows of the input fake catalog which have half light radius, of either the bulge or the disk, 
  536         that are 0. Also removes rows that have Sersic index outside of galsim's allowed paramters. If 
  537         the config option sourceSelectionColName is set then this function limits the catalog of input fakes 
  538         to only those which are True in this column. 
  540         Add the fake sources to the image. 
  544     _DefaultName = 
"insertFakes" 
  545     ConfigClass = InsertFakesConfig
 
  547     def runDataRef(self, dataRef):
 
  548         """Read in/write out the required data products and add fake sources to the deepCoadd. 
  552         dataRef : `lsst.daf.persistence.butlerSubset.ButlerDataRef` 
  553             Data reference defining the image to have fakes added to it 
  554             Used to access the following data products: 
  558         self.log.
info(
"Adding fakes to: tract: %d, patch: %s, filter: %s",
 
  559                       dataRef.dataId[
"tract"], dataRef.dataId[
"patch"], dataRef.dataId[
"filter"])
 
  563         if self.config.fakeType == 
"static":
 
  564             fakeCat = dataRef.get(
"deepCoadd_fakeSourceCat").toDataFrame()
 
  567             self.fakeSourceCatType = 
"deepCoadd_fakeSourceCat" 
  569             fakeCat = Table.read(self.config.fakeType).to_pandas()
 
  571         coadd = dataRef.get(
"deepCoadd")
 
  573         photoCalib = coadd.getPhotoCalib()
 
  575         imageWithFakes = self.run(fakeCat, coadd, wcs, photoCalib)
 
  577         dataRef.put(imageWithFakes.imageWithFakes, 
"fakes_deepCoadd")
 
  579     def runQuantum(self, butlerQC, inputRefs, outputRefs):
 
  580         inputs = butlerQC.get(inputRefs)
 
  581         inputs[
"wcs"] = inputs[
"image"].getWcs()
 
  582         inputs[
"photoCalib"] = inputs[
"image"].getPhotoCalib()
 
  584         outputs = self.run(**inputs)
 
  585         butlerQC.put(outputs, outputRefs)
 
  588     def _makeArgumentParser(cls):
 
  589         parser = pipeBase.ArgumentParser(name=cls._DefaultName)
 
  590         parser.add_id_argument(name=
"--id", datasetType=
"deepCoadd",
 
  591                                help=
"data IDs for the deepCoadd, e.g. --id tract=12345 patch=1,2 filter=r",
 
  592                                ContainerClass=ExistingCoaddDataIdContainer)
 
  595     def run(self, fakeCat, image, wcs, photoCalib):
 
  596         """Add fake sources to an image. 
  600         fakeCat : `pandas.core.frame.DataFrame` 
  601                     The catalog of fake sources to be input 
  602         image : `lsst.afw.image.exposure.exposure.ExposureF` 
  603                     The image into which the fake sources should be added 
  604         wcs : `lsst.afw.geom.SkyWcs` 
  605                     WCS to use to add fake sources 
  606         photoCalib : `lsst.afw.image.photoCalib.PhotoCalib` 
  607                     Photometric calibration to be used to calibrate the fake sources 
  611         resultStruct : `lsst.pipe.base.struct.Struct` 
  612             contains : image : `lsst.afw.image.exposure.exposure.ExposureF` 
  616         Adds pixel coordinates for each source to the fakeCat and removes objects with bulge or disk half 
  617         light radius = 0 (if ``config.doCleanCat = True``). 
  619         Adds the ``Fake`` mask plane to the image which is then set by `addFakeSources` to mark where fake 
  620         sources have been added. Uses the information in the ``fakeCat`` to make fake galaxies (using galsim) 
  621         and fake stars, using the PSF models from the PSF information for the image. These are then added to 
  622         the image and the image with fakes included returned. 
  624         The galsim galaxies are made using a double sersic profile, one for the bulge and one for the disk, 
  625         this is then convolved with the PSF at that point. 
  629         origWcs = image.getWcs()
 
  630         origPhotoCalib = image.getPhotoCalib()
 
  632         image.setPhotoCalib(photoCalib)
 
  634         band = image.getFilterLabel().bandLabel
 
  635         fakeCat = self._standardizeColumns(fakeCat, band)
 
  637         fakeCat = self.addPixCoords(fakeCat, image)
 
  638         fakeCat = self.trimFakeCat(fakeCat, image)
 
  641             if not self.config.insertImages:
 
  642                 if isinstance(fakeCat[self.config.sourceType].iloc[0], str):
 
  643                     galCheckVal = 
"galaxy" 
  644                     starCheckVal = 
"star" 
  645                 elif isinstance(fakeCat[self.config.sourceType].iloc[0], bytes):
 
  646                     galCheckVal = b
"galaxy" 
  647                     starCheckVal = b
"star" 
  648                 elif isinstance(fakeCat[self.config.sourceType].iloc[0], (int, float)):
 
  653                         "sourceType column does not have required type, should be str, bytes or int" 
  655                 if self.config.doCleanCat:
 
  656                     fakeCat = self.cleanCat(fakeCat, starCheckVal)
 
  658                 generator = self._generateGSObjectsFromCatalog(image, fakeCat, galCheckVal, starCheckVal)
 
  660                 generator = self._generateGSObjectsFromImages(image, fakeCat)
 
  661             _add_fake_sources(image, generator, calibFluxRadius=self.config.calibFluxRadius, logger=self.log)
 
  662         elif len(fakeCat) == 0 
and self.config.doProcessAllDataIds:
 
  663             self.log.
warning(
"No fakes found for this dataRef; processing anyway.")
 
  664             image.mask.addMaskPlane(
"FAKE")
 
  666             raise RuntimeError(
"No fakes found for this dataRef.")
 
  669         image.setWcs(origWcs)
 
  670         image.setPhotoCalib(origPhotoCalib)
 
  672         resultStruct = pipeBase.Struct(imageWithFakes=image)
 
  676     def _standardizeColumns(self, fakeCat, band):
 
  677         """Use config variables to 'standardize' the expected columns and column 
  678         names in the input catalog. 
  682         fakeCat : `pandas.core.frame.DataFrame` 
  683             The catalog of fake sources to be input 
  685             Label for the current band being processed. 
  689         outCat : `pandas.core.frame.DataFrame` 
  690             The standardized catalog of fake sources 
  695         def add_to_replace_dict(new_name, depr_name, std_name):
 
  696             if new_name 
in fakeCat.columns:
 
  697                 replace_dict[new_name] = std_name
 
  698             elif depr_name 
in fakeCat.columns:
 
  699                 replace_dict[depr_name] = std_name
 
  701                 raise ValueError(f
"Could not determine column for {std_name}.")
 
  705         for new_name, depr_name, std_name 
in [
 
  706             (cfg.ra_col, cfg.raColName, 
'ra'),
 
  707             (cfg.dec_col, cfg.decColName, 
'dec'),
 
  708             (cfg.mag_col%band, cfg.magVar%band, 
'mag')
 
  710             add_to_replace_dict(new_name, depr_name, std_name)
 
  712         if not cfg.insertImages:
 
  713             for new_name, depr_name, std_name 
in [
 
  714                 (cfg.bulge_n_col, cfg.nBulge, 
'bulge_n'),
 
  715                 (cfg.bulge_pa_col, cfg.paBulge, 
'bulge_pa'),
 
  716                 (cfg.disk_n_col, cfg.nDisk, 
'disk_n'),
 
  717                 (cfg.disk_pa_col, cfg.paDisk, 
'disk_pa'),
 
  719                 add_to_replace_dict(new_name, depr_name, std_name)
 
  721         if cfg.doSubSelectSources:
 
  724                 cfg.sourceSelectionColName,
 
  727         fakeCat = fakeCat.rename(columns=replace_dict, copy=
False)
 
  732         if not cfg.insertImages:
 
  734                 cfg.bulge_semimajor_col 
in fakeCat.columns
 
  735                 and cfg.bulge_axis_ratio_col 
in fakeCat.columns
 
  737                 fakeCat = fakeCat.rename(
 
  739                         cfg.bulge_semimajor_col: 
'bulge_semimajor',
 
  740                         cfg.bulge_axis_ratio_col: 
'bulge_axis_ratio',
 
  741                         cfg.disk_semimajor_col: 
'disk_semimajor',
 
  742                         cfg.disk_axis_ratio_col: 
'disk_axis_ratio',
 
  747                 cfg.bulgeHLR 
in fakeCat.columns
 
  748                 and cfg.aBulge 
in fakeCat.columns
 
  749                 and cfg.bBulge 
in fakeCat.columns
 
  751                 fakeCat[
'bulge_axis_ratio'] = (
 
  752                     fakeCat[cfg.bBulge]/fakeCat[cfg.aBulge]
 
  754                 fakeCat[
'bulge_semimajor'] = (
 
  755                     fakeCat[cfg.bulgeHLR]/np.sqrt(fakeCat[
'bulge_axis_ratio'])
 
  757                 fakeCat[
'disk_axis_ratio'] = (
 
  758                     fakeCat[cfg.bDisk]/fakeCat[cfg.aDisk]
 
  760                 fakeCat[
'disk_semimajor'] = (
 
  761                     fakeCat[cfg.diskHLR]/np.sqrt(fakeCat[
'disk_axis_ratio'])
 
  765                     "Could not determine columns for half-light radius and " 
  770             if cfg.bulge_disk_flux_ratio_col 
in fakeCat.columns:
 
  771                 fakeCat = fakeCat.rename(
 
  773                         cfg.bulge_disk_flux_ratio_col: 
'bulge_disk_flux_ratio' 
  778                 fakeCat[
'bulge_disk_flux_ratio'] = 1.0
 
  782     def _generateGSObjectsFromCatalog(self, exposure, fakeCat, galCheckVal, starCheckVal):
 
  783         """Process catalog to generate `galsim.GSObject` s. 
  787         exposure : `lsst.afw.image.exposure.exposure.ExposureF` 
  788             The exposure into which the fake sources should be added 
  789         fakeCat : `pandas.core.frame.DataFrame` 
  790             The catalog of fake sources to be input 
  791         galCheckVal : `str`, `bytes` or `int` 
  792             The value that is set in the sourceType column to specifiy an object is a galaxy. 
  793         starCheckVal : `str`, `bytes` or `int` 
  794             The value that is set in the sourceType column to specifiy an object is a star. 
  798         gsObjects : `generator` 
  799             A generator of tuples of `lsst.geom.SpherePoint` and `galsim.GSObject`. 
  801         wcs = exposure.getWcs()
 
  802         photoCalib = exposure.getPhotoCalib()
 
  804         self.log.
info(
"Making %d objects for insertion", len(fakeCat))
 
  806         for (index, row) 
in fakeCat.iterrows():
 
  810             xy = wcs.skyToPixel(skyCoord)
 
  813                 flux = photoCalib.magnitudeToInstFlux(row[
'mag'], xy)
 
  817             sourceType = row[self.config.sourceType]
 
  818             if sourceType == galCheckVal:
 
  820                 bulge_gs_HLR = row[
'bulge_semimajor']*np.sqrt(row[
'bulge_axis_ratio'])
 
  821                 bulge = galsim.Sersic(n=row[
'bulge_n'], half_light_radius=bulge_gs_HLR)
 
  822                 bulge = bulge.shear(q=row[
'bulge_axis_ratio'], beta=((90 - row[
'bulge_pa'])*galsim.degrees))
 
  824                 disk_gs_HLR = row[
'disk_semimajor']*np.sqrt(row[
'disk_axis_ratio'])
 
  825                 disk = galsim.Sersic(n=row[
'disk_n'], half_light_radius=disk_gs_HLR)
 
  826                 disk = disk.shear(q=row[
'disk_axis_ratio'], beta=((90 - row[
'disk_pa'])*galsim.degrees))
 
  828                 gal = bulge*row[
'bulge_disk_flux_ratio'] + disk
 
  829                 gal = gal.withFlux(flux)
 
  832             elif sourceType == starCheckVal:
 
  833                 star = galsim.DeltaFunction()
 
  834                 star = star.withFlux(flux)
 
  837                 raise TypeError(f
"Unknown sourceType {sourceType}")
 
  839     def _generateGSObjectsFromImages(self, exposure, fakeCat):
 
  840         """Process catalog to generate `galsim.GSObject` s. 
  844         exposure : `lsst.afw.image.exposure.exposure.ExposureF` 
  845             The exposure into which the fake sources should be added 
  846         fakeCat : `pandas.core.frame.DataFrame` 
  847             The catalog of fake sources to be input 
  851         gsObjects : `generator` 
  852             A generator of tuples of `lsst.geom.SpherePoint` and `galsim.GSObject`. 
  854         band = exposure.getFilterLabel().bandLabel
 
  855         wcs = exposure.getWcs()
 
  856         photoCalib = exposure.getPhotoCalib()
 
  858         self.log.
info(
"Processing %d fake images", len(fakeCat))
 
  860         for (index, row) 
in fakeCat.iterrows():
 
  864             xy = wcs.skyToPixel(skyCoord)
 
  867                 flux = photoCalib.magnitudeToInstFlux(row[
'mag'], xy)
 
  871             imFile = row[band+
"imFilename"]
 
  873                 imFile = imFile.decode(
"utf-8")
 
  874             except AttributeError:
 
  876             imFile = imFile.strip()
 
  877             im = galsim.fits.read(imFile, read_header=
True)
 
  879             if self.config.fits_alignment == 
"wcs":
 
  886                 if _isWCSGalsimDefault(im.wcs, im.header):
 
  888                         f
"Cannot find WCS in input FITS file {imFile}" 
  890             elif self.config.fits_alignment == 
"pixel":
 
  893                 linWcs = wcs.linearizePixelToSky(skyCoord, geom.arcseconds)
 
  894                 mat = linWcs.getMatrix()
 
  895                 im.wcs = galsim.JacobianWCS(
 
  896                     mat[0, 0], mat[0, 1], mat[1, 0], mat[1, 1]
 
  900                     f
"Unknown fits_alignment type {self.config.fits_alignment}" 
  903             obj = galsim.InterpolatedImage(im, calculate_stepk=
False)
 
  904             obj = obj.withFlux(flux)
 
  908         """Process images from files into the format needed for insertion. 
  912         fakeCat : `pandas.core.frame.DataFrame` 
  913                     The catalog of fake sources to be input 
  914         wcs : `lsst.afw.geom.skyWcs.skyWcs.SkyWc` 
  915                     WCS to use to add fake sources 
  916         psf : `lsst.meas.algorithms.coaddPsf.coaddPsf.CoaddPsf` or 
  917               `lsst.meas.extensions.psfex.psfexPsf.PsfexPsf` 
  918                     The PSF information to use to make the PSF images 
  919         photoCalib : `lsst.afw.image.photoCalib.PhotoCalib` 
  920                     Photometric calibration to be used to calibrate the fake sources 
  922                     The filter band that the observation was taken in. 
  924                     The pixel scale of the image the sources are to be added to. 
  929                     A list of tuples of `lsst.afw.image.exposure.exposure.ExposureF` and 
  930                     `lsst.geom.Point2D` of their locations. 
  931                     For sources labelled as galaxy. 
  933                     A list of tuples of `lsst.afw.image.exposure.exposure.ExposureF` and 
  934                     `lsst.geom.Point2D` of their locations. 
  935                     For sources labelled as star. 
  939         The input fakes catalog needs to contain the absolute path to the image in the 
  940         band that is being used to add images to. It also needs to have the R.A. and 
  941         declination of the fake source in radians and the sourceType of the object. 
  946         self.log.
info(
"Processing %d fake images", len(fakeCat))
 
  948         for (imFile, sourceType, mag, x, y) 
in zip(fakeCat[band + 
"imFilename"].array,
 
  949                                                    fakeCat[
"sourceType"].array,
 
  950                                                    fakeCat[
'mag'].array,
 
  951                                                    fakeCat[
"x"].array, fakeCat[
"y"].array):
 
  953             im = afwImage.ImageF.readFits(imFile)
 
  960                 correctedFlux = psf.computeApertureFlux(self.config.calibFluxRadius, xy)
 
  961                 psfKernel = psf.computeKernelImage(xy).getArray()
 
  962                 psfKernel /= correctedFlux
 
  964             except InvalidParameterError:
 
  965                 self.log.
info(
"%s at %0.4f, %0.4f outside of image", sourceType, x, y)
 
  968             psfIm = galsim.InterpolatedImage(galsim.Image(psfKernel), scale=pixelScale)
 
  969             galsimIm = galsim.InterpolatedImage(galsim.Image(im.array), scale=pixelScale)
 
  970             convIm = galsim.Convolve([galsimIm, psfIm])
 
  973                 outIm = convIm.drawImage(scale=pixelScale, method=
"real_space").array
 
  974             except (galsim.errors.GalSimFFTSizeError, MemoryError):
 
  977             imSum = np.sum(outIm)
 
  981                 flux = photoCalib.magnitudeToInstFlux(mag, xy)
 
  985             imWithFlux = flux*divIm
 
  987             if sourceType == b
"galaxy":
 
  988                 galImages.append((afwImage.ImageF(imWithFlux), xy))
 
  989             if sourceType == b
"star":
 
  990                 starImages.append((afwImage.ImageF(imWithFlux), xy))
 
  992         return galImages, starImages
 
  996         """Add pixel coordinates to the catalog of fakes. 
 1000         fakeCat : `pandas.core.frame.DataFrame` 
 1001                     The catalog of fake sources to be input 
 1002         image : `lsst.afw.image.exposure.exposure.ExposureF` 
 1003                     The image into which the fake sources should be added 
 1007         fakeCat : `pandas.core.frame.DataFrame` 
 1009         wcs = image.getWcs()
 
 1010         ras = fakeCat[
'ra'].values
 
 1011         decs = fakeCat[
'dec'].values
 
 1012         xs, ys = wcs.skyToPixelArray(ras, decs)
 
 1019         """Trim the fake cat to about the size of the input image. 
 1021         `fakeCat` must be processed with addPixCoords before using this method. 
 1025         fakeCat : `pandas.core.frame.DataFrame` 
 1026                     The catalog of fake sources to be input 
 1027         image : `lsst.afw.image.exposure.exposure.ExposureF` 
 1028                     The image into which the fake sources should be added 
 1032         fakeCat : `pandas.core.frame.DataFrame` 
 1033                     The original fakeCat trimmed to the area of the image 
 1036         bbox = 
Box2D(image.getBBox()).dilatedBy(self.config.trimBuffer)
 
 1037         xs = fakeCat[
"x"].values
 
 1038         ys = fakeCat[
"y"].values
 
 1040         isContained = xs >= bbox.minX
 
 1041         isContained &= xs <= bbox.maxX
 
 1042         isContained &= ys >= bbox.minY
 
 1043         isContained &= ys <= bbox.maxY
 
 1045         return fakeCat[isContained]
 
 1048         """Make images of fake galaxies using GalSim. 
 1053         pixelScale : `float` 
 1054         psf : `lsst.meas.extensions.psfex.psfexPsf.PsfexPsf` 
 1055                     The PSF information to use to make the PSF images 
 1056         fakeCat : `pandas.core.frame.DataFrame` 
 1057                     The catalog of fake sources to be input 
 1058         photoCalib : `lsst.afw.image.photoCalib.PhotoCalib` 
 1059                     Photometric calibration to be used to calibrate the fake sources 
 1063         galImages : `generator` 
 1064                     A generator of tuples of `lsst.afw.image.exposure.exposure.ExposureF` and 
 1065                     `lsst.geom.Point2D` of their locations. 
 1070         Fake galaxies are made by combining two sersic profiles, one for the bulge and one for the disk. Each 
 1071         component has an individual sersic index (n), a, b and position angle (PA). The combined profile is 
 1072         then convolved with the PSF at the specified x, y position on the image. 
 1074         The names of the columns in the ``fakeCat`` are configurable and are the column names from the 
 1075         University of Washington simulations database as default. For more information see the doc strings 
 1076         attached to the config options. 
 1078         See mkFakeStars doc string for an explanation of calibration to instrumental flux. 
 1081         self.log.
info(
"Making %d fake galaxy images", len(fakeCat))
 
 1083         for (index, row) 
in fakeCat.iterrows():
 
 1089                 correctedFlux = psf.computeApertureFlux(self.config.calibFluxRadius, xy)
 
 1090                 psfKernel = psf.computeKernelImage(xy).getArray()
 
 1091                 psfKernel /= correctedFlux
 
 1093             except InvalidParameterError:
 
 1094                 self.log.
info(
"Galaxy at %0.4f, %0.4f outside of image", row[
"x"], row[
"y"])
 
 1098                 flux = photoCalib.magnitudeToInstFlux(row[
'mag'], xy)
 
 1103             bulge_gs_HLR = row[
'bulge_semimajor']*np.sqrt(row[
'bulge_axis_ratio'])
 
 1104             bulge = galsim.Sersic(n=row[
'bulge_n'], half_light_radius=bulge_gs_HLR)
 
 1105             bulge = bulge.shear(q=row[
'bulge_axis_ratio'], beta=((90 - row[
'bulge_pa'])*galsim.degrees))
 
 1107             disk_gs_HLR = row[
'disk_semimajor']*np.sqrt(row[
'disk_axis_ratio'])
 
 1108             disk = galsim.Sersic(n=row[
'disk_n'], half_light_radius=disk_gs_HLR)
 
 1109             disk = disk.shear(q=row[
'disk_axis_ratio'], beta=((90 - row[
'disk_pa'])*galsim.degrees))
 
 1111             gal = bulge*row[
'bulge_disk_flux_ratio'] + disk
 
 1112             gal = gal.withFlux(flux)
 
 1114             psfIm = galsim.InterpolatedImage(galsim.Image(psfKernel), scale=pixelScale)
 
 1115             gal = galsim.Convolve([gal, psfIm])
 
 1117                 galIm = gal.drawImage(scale=pixelScale, method=
"real_space").array
 
 1118             except (galsim.errors.GalSimFFTSizeError, MemoryError):
 
 1121             yield (afwImage.ImageF(galIm), xy)
 
 1125         """Make fake stars based off the properties in the fakeCat. 
 1130         psf : `lsst.meas.extensions.psfex.psfexPsf.PsfexPsf` 
 1131                     The PSF information to use to make the PSF images 
 1132         fakeCat : `pandas.core.frame.DataFrame` 
 1133                     The catalog of fake sources to be input 
 1134         image : `lsst.afw.image.exposure.exposure.ExposureF` 
 1135                     The image into which the fake sources should be added 
 1136         photoCalib : `lsst.afw.image.photoCalib.PhotoCalib` 
 1137                     Photometric calibration to be used to calibrate the fake sources 
 1141         starImages : `generator` 
 1142                     A generator of tuples of `lsst.afw.image.ImageF` of fake stars and 
 1143                     `lsst.geom.Point2D` of their locations. 
 1147         To take a given magnitude and translate to the number of counts in the image 
 1148         we use photoCalib.magnitudeToInstFlux, which returns the instrumental flux for the 
 1149         given calibration radius used in the photometric calibration step. 
 1150         Thus `calibFluxRadius` should be set to this same radius so that we can normalize 
 1151         the PSF model to the correct instrumental flux within calibFluxRadius. 
 1154         self.log.
info(
"Making %d fake star images", len(fakeCat))
 
 1156         for (index, row) 
in fakeCat.iterrows():
 
 1162                 correctedFlux = psf.computeApertureFlux(self.config.calibFluxRadius, xy)
 
 1163                 starIm = psf.computeImage(xy)
 
 1164                 starIm /= correctedFlux
 
 1166             except InvalidParameterError:
 
 1167                 self.log.
info(
"Star at %0.4f, %0.4f outside of image", row[
"x"], row[
"y"])
 
 1171                 flux = photoCalib.magnitudeToInstFlux(row[
'mag'], xy)
 
 1176             yield ((starIm.convertF(), xy))
 
 1179         """Remove rows from the fakes catalog which have HLR = 0 for either the buldge or disk component, 
 1180            also remove galaxies that have Sersic index outside the galsim min and max 
 1181            allowed (0.3 <= n <= 6.2). 
 1185         fakeCat : `pandas.core.frame.DataFrame` 
 1186                     The catalog of fake sources to be input 
 1187         starCheckVal : `str`, `bytes` or `int` 
 1188                     The value that is set in the sourceType column to specifiy an object is a star. 
 1192         fakeCat : `pandas.core.frame.DataFrame` 
 1193                     The input catalog of fake sources but with the bad objects removed 
 1196         rowsToKeep = (((fakeCat[
'bulge_semimajor'] != 0.0) & (fakeCat[
'disk_semimajor'] != 0.0))
 
 1197                       | (fakeCat[self.config.sourceType] == starCheckVal))
 
 1198         numRowsNotUsed = len(fakeCat) - len(np.where(rowsToKeep)[0])
 
 1199         self.log.
info(
"Removing %d rows with HLR = 0 for either the bulge or disk", numRowsNotUsed)
 
 1200         fakeCat = fakeCat[rowsToKeep]
 
 1202         minN = galsim.Sersic._minimum_n
 
 1203         maxN = galsim.Sersic._maximum_n
 
 1204         rowsWithGoodSersic = (((fakeCat[
'bulge_n'] >= minN) & (fakeCat[
'bulge_n'] <= maxN)
 
 1205                               & (fakeCat[
'disk_n'] >= minN) & (fakeCat[
'disk_n'] <= maxN))
 
 1206                               | (fakeCat[self.config.sourceType] == starCheckVal))
 
 1207         numRowsNotUsed = len(fakeCat) - len(np.where(rowsWithGoodSersic)[0])
 
 1208         self.log.
info(
"Removing %d rows of galaxies with nBulge or nDisk outside of %0.2f <= n <= %0.2f",
 
 1209                       numRowsNotUsed, minN, maxN)
 
 1210         fakeCat = fakeCat[rowsWithGoodSersic]
 
 1212         if self.config.doSubSelectSources:
 
 1213             numRowsNotUsed = len(fakeCat) - len(fakeCat[
'select'])
 
 1214             self.log.
info(
"Removing %d rows which were not designated as template sources", numRowsNotUsed)
 
 1215             fakeCat = fakeCat[fakeCat[
'select']]
 
 1220         """Add the fake sources to the given image 
 1224         image : `lsst.afw.image.exposure.exposure.ExposureF` 
 1225                     The image into which the fake sources should be added 
 1226         fakeImages : `typing.Iterator` [`tuple` ['lsst.afw.image.ImageF`, `lsst.geom.Point2d`]] 
 1227                     An iterator of tuples that contains (or generates) images of fake sources, 
 1228                     and the locations they are to be inserted at. 
 1230                     The type (star/galaxy) of fake sources input 
 1234         image : `lsst.afw.image.exposure.exposure.ExposureF` 
 1238         Uses the x, y information in the ``fakeCat`` to position an image of the fake interpolated onto the 
 1239         pixel grid of the image. Sets the ``FAKE`` mask plane for the pixels added with the fake source. 
 1242         imageBBox = image.getBBox()
 
 1243         imageMI = image.maskedImage
 
 1245         for (fakeImage, xy) 
in fakeImages:
 
 1246             X0 = xy.getX() - fakeImage.getWidth()/2 + 0.5
 
 1247             Y0 = xy.getY() - fakeImage.getHeight()/2 + 0.5
 
 1248             self.log.
debug(
"Adding fake source at %d, %d", xy.getX(), xy.getY())
 
 1249             if sourceType == 
"galaxy":
 
 1252                 interpFakeImage = fakeImage
 
 1254             interpFakeImBBox = interpFakeImage.getBBox()
 
 1255             interpFakeImBBox.clip(imageBBox)
 
 1257             if interpFakeImBBox.getArea() > 0:
 
 1258                 imageMIView = imageMI[interpFakeImBBox]
 
 1259                 clippedFakeImage = interpFakeImage[interpFakeImBBox]
 
 1260                 clippedFakeImageMI = afwImage.MaskedImageF(clippedFakeImage)
 
 1261                 clippedFakeImageMI.mask.set(self.bitmask)
 
 1262                 imageMIView += clippedFakeImageMI
 
 1266     def _getMetadataName(self):
 
 1267         """Disable metadata writing""" 
A floating-point coordinate rectangle geometry.
 
An integer coordinate rectangle.
 
Point in an unspecified spherical coordinate system.
 
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.
 
bool any(CoordinateExpr< N > const &expr) noexcept
Return true if any elements are true.
 
Point< double, 2 > Point2D
 
def run(self, coaddExposures, bbox, wcs)
 
def addPixCoords(self, fakeCat, image)
 
def mkFakeStars(self, fakeCat, band, photoCalib, psf, image)
 
def mkFakeGalsimGalaxies(self, fakeCat, band, photoCalib, pixelScale, psf, image)
 
def cleanCat(self, fakeCat, starCheckVal)
 
def processImagesForInsertion(self, fakeCat, wcs, psf, photoCalib, band, pixelScale)
 
def trimFakeCat(self, fakeCat, image)
 
def addFakeSources(self, image, fakeImages, sourceType)