23Insert fake sources into calexps
31from .insertFakes
import InsertFakesTask
33from lsst.obs.base
import ExposureIdInfo
34from lsst.pipe.base import PipelineTask, PipelineTaskConfig, PipelineTaskConnections
35import lsst.pipe.base.connectionTypes
as cT
40__all__ = [
"ProcessCcdWithFakesConfig",
"ProcessCcdWithFakesTask",
41 "ProcessCcdWithVariableFakesConfig",
"ProcessCcdWithVariableFakesTask"]
45 dimensions=(
"instrument",
"visit",
"detector"),
46 defaultTemplates={
"coaddName":
"deep",
47 "wcsName":
"jointcal",
48 "photoCalibName":
"jointcal",
49 "fakesType":
"fakes_"}):
51 doc=
"Input definition of geometry/bbox and projection/wcs for "
52 "template exposures. Needed to test which tract to generate ",
53 name=BaseSkyMap.SKYMAP_DATASET_TYPE_NAME,
54 dimensions=(
"skymap",),
55 storageClass=
"SkyMap",
59 doc=
"Exposure into which fakes are to be added.",
61 storageClass=
"ExposureF",
62 dimensions=(
"instrument",
"visit",
"detector")
66 doc=
"Set of catalogs of fake sources to draw inputs from. We "
67 "concatenate the tract catalogs for detectorVisits that cover "
69 name=
"{fakesType}fakeSourceCat",
70 storageClass=
"DataFrame",
71 dimensions=(
"tract",
"skymap"),
76 externalSkyWcsTractCatalog = cT.Input(
77 doc=(
"Per-tract, per-visit wcs calibrations. These catalogs use the detector "
78 "id for the catalog id, sorted on id for fast lookup."),
79 name=
"{wcsName}SkyWcsCatalog",
80 storageClass=
"ExposureCatalog",
81 dimensions=(
"instrument",
"visit",
"tract",
"skymap"),
86 externalSkyWcsGlobalCatalog = cT.Input(
87 doc=(
"Per-visit wcs calibrations computed globally (with no tract information). "
88 "These catalogs use the detector id for the catalog id, sorted on id for "
90 name=
"{wcsName}SkyWcsCatalog",
91 storageClass=
"ExposureCatalog",
92 dimensions=(
"instrument",
"visit"),
95 externalPhotoCalibTractCatalog = cT.Input(
96 doc=(
"Per-tract, per-visit photometric calibrations. These catalogs use the "
97 "detector id for the catalog id, sorted on id for fast lookup."),
98 name=
"{photoCalibName}PhotoCalibCatalog",
99 storageClass=
"ExposureCatalog",
100 dimensions=(
"instrument",
"visit",
"tract"),
105 externalPhotoCalibGlobalCatalog = cT.Input(
106 doc=(
"Per-visit photometric calibrations. These catalogs use the "
107 "detector id for the catalog id, sorted on id for fast lookup."),
108 name=
"{photoCalibName}PhotoCalibCatalog",
109 storageClass=
"ExposureCatalog",
110 dimensions=(
"instrument",
"visit"),
113 icSourceCat = cT.Input(
114 doc=
"Catalog of calibration sources",
116 storageClass=
"SourceCatalog",
117 dimensions=(
"instrument",
"visit",
"detector")
120 sfdSourceCat = cT.Input(
121 doc=
"Catalog of calibration sources",
123 storageClass=
"SourceCatalog",
124 dimensions=(
"instrument",
"visit",
"detector")
127 outputExposure = cT.Output(
128 doc=
"Exposure with fake sources added.",
129 name=
"{fakesType}calexp",
130 storageClass=
"ExposureF",
131 dimensions=(
"instrument",
"visit",
"detector")
134 outputCat = cT.Output(
135 doc=
"Source catalog produced in calibrate task with fakes also measured.",
136 name=
"{fakesType}src",
137 storageClass=
"SourceCatalog",
138 dimensions=(
"instrument",
"visit",
"detector"),
141 def __init__(self, *, config=None):
142 super().__init__(config=config)
144 if not config.doApplyExternalGlobalPhotoCalib:
145 self.inputs.remove(
"externalPhotoCalibGlobalCatalog")
146 if not config.doApplyExternalTractPhotoCalib:
147 self.inputs.remove(
"externalPhotoCalibTractCatalog")
149 if not config.doApplyExternalGlobalSkyWcs:
150 self.inputs.remove(
"externalSkyWcsGlobalCatalog")
151 if not config.doApplyExternalTractSkyWcs:
152 self.inputs.remove(
"externalSkyWcsTractCatalog")
155class ProcessCcdWithFakesConfig(PipelineTaskConfig,
156 pipelineConnections=ProcessCcdWithFakesConnections):
157 """Config for inserting fake sources
161 The default column names are those from the UW sims database.
164 doApplyExternalGlobalPhotoCalib = pexConfig.Field(
167 doc=
"Whether to apply an external photometric calibration via an "
168 "`lsst.afw.image.PhotoCalib` object. Uses the "
169 "`externalPhotoCalibName` config option to determine which "
170 "calibration to use. Uses a global calibration."
173 doApplyExternalTractPhotoCalib = pexConfig.Field(
176 doc=
"Whether to apply an external photometric calibration via an "
177 "`lsst.afw.image.PhotoCalib` object. Uses the "
178 "`externalPhotoCalibName` config option to determine which "
179 "calibration to use. Uses a per tract calibration."
182 externalPhotoCalibName = pexConfig.ChoiceField(
183 doc=
"What type of external photo calib to use.",
186 allowed={
"jointcal":
"Use jointcal_photoCalib",
187 "fgcm":
"Use fgcm_photoCalib",
188 "fgcm_tract":
"Use fgcm_tract_photoCalib"}
191 doApplyExternalGlobalSkyWcs = pexConfig.Field(
194 doc=
"Whether to apply an external astrometric calibration via an "
195 "`lsst.afw.geom.SkyWcs` object. Uses the "
196 "`externalSkyWcsName` config option to determine which "
197 "calibration to use. Uses a global calibration."
200 doApplyExternalTractSkyWcs = pexConfig.Field(
203 doc=
"Whether to apply an external astrometric calibration via an "
204 "`lsst.afw.geom.SkyWcs` object. Uses the "
205 "`externalSkyWcsName` config option to determine which "
206 "calibration to use. Uses a per tract calibration."
209 externalSkyWcsName = pexConfig.ChoiceField(
210 doc=
"What type of updated WCS calib to use.",
213 allowed={
"jointcal":
"Use jointcal_wcs"}
216 coaddName = pexConfig.Field(
217 doc=
"The name of the type of coadd used",
222 srcFieldsToCopy = pexConfig.ListField(
224 default=(
"calib_photometry_reserved",
"calib_photometry_used",
"calib_astrometry_used",
225 "calib_psf_candidate",
"calib_psf_used",
"calib_psf_reserved"),
226 doc=(
"Fields to copy from the `src` catalog to the output catalog "
227 "for matching sources Any missing fields will trigger a "
228 "RuntimeError exception.")
231 matchRadiusPix = pexConfig.Field(
234 doc=(
"Match radius for matching icSourceCat objects to sourceCat objects (pixels)"),
237 doMatchVisit = pexConfig.Field(
240 doc=
"Match visit to trim the fakeCat"
243 calibrate = pexConfig.ConfigurableField(target=CalibrateTask,
244 doc=
"The calibration task to use.")
246 insertFakes = pexConfig.ConfigurableField(target=InsertFakesTask,
247 doc=
"Configuration for the fake sources")
249 def setDefaults(self):
250 super().setDefaults()
251 self.calibrate.measurement.plugins[
"base_PixelFlags"].masksFpAnywhere.append(
"FAKE")
252 self.calibrate.measurement.plugins[
"base_PixelFlags"].masksFpCenter.append(
"FAKE")
253 self.calibrate.doAstrometry =
False
254 self.calibrate.doWriteMatches =
False
255 self.calibrate.doPhotoCal =
False
256 self.calibrate.detection.reEstimateBackground =
False
259class ProcessCcdWithFakesTask(PipelineTask):
260 """Insert fake objects into calexps.
262 Add fake stars and galaxies to the given calexp, specified
in the dataRef. Galaxy parameters are read
in
263 from the specified file
and then modelled using galsim. Re-runs characterize image
and calibrate image to
264 give a new background estimation
and measurement of the calexp.
266 `ProcessFakeSourcesTask` inherits six functions
from insertFakesTask that make images of the fake
267 sources
and then add them to the calexp.
270 Use the WCS information to add the pixel coordinates of each source
271 Adds an ``x``
and ``y`` column to the catalog of fake sources.
273 Trim the fake cat to about the size of the input image.
274 `mkFakeGalsimGalaxies`
275 Use Galsim to make fake double sersic galaxies
for each set of galaxy parameters
in the input file.
277 Use the PSF information
from the calexp to make a fake star using the magnitude information
from the
280 Remove rows of the input fake catalog which have half light radius, of either the bulge
or the disk,
283 Add the fake sources to the calexp.
287 The ``calexp``
with fake souces added to it
is written out
as the datatype ``calexp_fakes``.
290 _DefaultName = "processCcdWithFakes"
291 ConfigClass = ProcessCcdWithFakesConfig
293 def __init__(self, schema=None, butler=None, **kwargs):
294 """Initalize things! This should go above in the class docstring
297 super().__init__(**kwargs)
300 schema = SourceTable.makeMinimalSchema()
302 self.makeSubtask(
"insertFakes")
303 self.makeSubtask(
"calibrate")
305 def runQuantum(self, butlerQC, inputRefs, outputRefs):
306 inputs = butlerQC.get(inputRefs)
307 detectorId = inputs[
"exposure"].getInfo().getDetector().getId()
309 if 'exposureIdInfo' not in inputs.keys():
310 expId, expBits = butlerQC.quantum.dataId.pack(
"visit_detector", returnMaxBits=
True)
311 inputs[
'exposureIdInfo'] = ExposureIdInfo(expId, expBits)
313 expWcs = inputs[
"exposure"].getWcs()
314 tractId = inputs[
"skyMap"].findTract(
315 expWcs.pixelToSky(inputs[
"exposure"].getBBox().getCenter())).tract_id
316 if not self.config.doApplyExternalGlobalSkyWcs
and not self.config.doApplyExternalTractSkyWcs:
317 inputs[
"wcs"] = expWcs
318 elif self.config.doApplyExternalGlobalSkyWcs:
319 externalSkyWcsCatalog = inputs[
"externalSkyWcsGlobalCatalog"]
320 row = externalSkyWcsCatalog.find(detectorId)
321 inputs[
"wcs"] = row.getWcs()
322 elif self.config.doApplyExternalTractSkyWcs:
323 externalSkyWcsCatalogList = inputs[
"externalSkyWcsTractCatalog"]
324 externalSkyWcsCatalog =
None
325 for externalSkyWcsCatalogRef
in externalSkyWcsCatalogList:
326 if externalSkyWcsCatalogRef.dataId[
"tract"] == tractId:
327 externalSkyWcsCatalog = externalSkyWcsCatalogRef.get(
328 datasetType=self.config.connections.externalSkyWcsTractCatalog)
330 if externalSkyWcsCatalog
is None:
331 usedTract = externalSkyWcsCatalogList[-1].dataId[
"tract"]
333 f
"Warning, external SkyWcs for tract {tractId} not found. Using tract {usedTract} "
335 externalSkyWcsCatalog = externalSkyWcsCatalogList[-1].get(
336 datasetType=self.config.connections.externalSkyWcsTractCatalog)
337 row = externalSkyWcsCatalog.find(detectorId)
338 inputs[
"wcs"] = row.getWcs()
340 if not self.config.doApplyExternalGlobalPhotoCalib
and not self.config.doApplyExternalTractPhotoCalib:
341 inputs[
"photoCalib"] = inputs[
"exposure"].getPhotoCalib()
342 elif self.config.doApplyExternalGlobalPhotoCalib:
343 externalPhotoCalibCatalog = inputs[
"externalPhotoCalibGlobalCatalog"]
344 row = externalPhotoCalibCatalog.find(detectorId)
345 inputs[
"photoCalib"] = row.getPhotoCalib()
346 elif self.config.doApplyExternalTractPhotoCalib:
347 externalPhotoCalibCatalogList = inputs[
"externalPhotoCalibTractCatalog"]
348 externalPhotoCalibCatalog =
None
349 for externalPhotoCalibCatalogRef
in externalPhotoCalibCatalogList:
350 if externalPhotoCalibCatalogRef.dataId[
"tract"] == tractId:
351 externalPhotoCalibCatalog = externalPhotoCalibCatalogRef.get(
352 datasetType=self.config.connections.externalPhotoCalibTractCatalog)
354 if externalPhotoCalibCatalog
is None:
355 usedTract = externalPhotoCalibCatalogList[-1].dataId[
"tract"]
357 f
"Warning, external PhotoCalib for tract {tractId} not found. Using tract {usedTract} "
359 externalPhotoCalibCatalog = externalPhotoCalibCatalogList[-1].get(
360 datasetType=self.config.connections.externalPhotoCalibTractCatalog)
361 row = externalPhotoCalibCatalog.find(detectorId)
362 inputs[
"photoCalib"] = row.getPhotoCalib()
364 outputs = self.run(**inputs)
365 butlerQC.put(outputs, outputRefs)
367 def run(self, fakeCats, exposure, skyMap, wcs=None, photoCalib=None, exposureIdInfo=None,
368 icSourceCat=None, sfdSourceCat=None, externalSkyWcsGlobalCatalog=None,
369 externalSkyWcsTractCatalog=None, externalPhotoCalibGlobalCatalog=None,
370 externalPhotoCalibTractCatalog=None):
371 """Add fake sources to a calexp and then run detection, deblending and measurement.
375 fakeCats : `list` of `lsst.daf.butler.DeferredDatasetHandle`
376 Set of tract level fake catalogs that potentially cover
378 exposure : `lsst.afw.image.exposure.exposure.ExposureF`
379 The exposure to add the fake sources to
380 skyMap : `lsst.skymap.SkyMap`
381 SkyMap defining the tracts and patches the fakes are stored over.
383 WCS to use to add fake sources
384 photoCalib : `lsst.afw.image.photoCalib.PhotoCalib`
385 Photometric calibration to be used to calibrate the fake sources
386 exposureIdInfo : `lsst.obs.base.ExposureIdInfo`
389 Catalog to take the information about which sources were used
for calibration
from.
392 Catalog produced by singleFrameDriver, needed to copy some calibration flags
from.
396 resultStruct : `lsst.pipe.base.struct.Struct`
397 contains : outputExposure : `lsst.afw.image.exposure.exposure.ExposureF`
398 outputCat : `lsst.afw.table.source.source.SourceCatalog`
402 Adds pixel coordinates
for each source to the fakeCat
and removes objects
with bulge
or disk half
403 light radius = 0 (
if ``config.cleanCat =
True``). These columns are called ``x``
and ``y``
and are
in
406 Adds the ``Fake`` mask plane to the exposure which
is then set by `addFakeSources` to mark where fake
407 sources have been added. Uses the information
in the ``fakeCat`` to make fake galaxies (using galsim)
408 and fake stars, using the PSF models
from the PSF information
for the calexp. These are then added to
409 the calexp
and the calexp
with fakes included returned.
411 The galsim galaxies are made using a double sersic profile, one
for the bulge
and one
for the disk,
412 this
is then convolved
with the PSF at that point.
414 If exposureIdInfo
is not provided then the SourceCatalog IDs will
not be globally unique.
416 fakeCat = self.composeFakeCat(fakeCats, skyMap)
419 wcs = exposure.getWcs()
421 if photoCalib
is None:
422 photoCalib = exposure.getPhotoCalib()
424 if self.config.doMatchVisit:
425 fakeCat = self.getVisitMatchedFakeCat(fakeCat, exposure)
427 self.insertFakes.run(fakeCat, exposure, wcs, photoCalib)
430 if exposureIdInfo
is None:
431 exposureIdInfo = ExposureIdInfo()
432 returnedStruct = self.calibrate.run(exposure, exposureIdInfo=exposureIdInfo)
433 sourceCat = returnedStruct.sourceCat
435 sourceCat = self.copyCalibrationFields(sfdSourceCat, sourceCat, self.config.srcFieldsToCopy)
437 resultStruct = pipeBase.Struct(outputExposure=exposure, outputCat=sourceCat)
440 def composeFakeCat(self, fakeCats, skyMap):
441 """Concatenate the fakeCats from tracts that may cover the exposure.
445 fakeCats : `list` of `lst.daf.butler.DeferredDatasetHandle`
446 Set of fake cats to concatenate.
447 skyMap : `lsst.skymap.SkyMap`
448 SkyMap defining the geometry of the tracts and patches.
452 combinedFakeCat : `pandas.DataFrame`
453 All fakes that cover the inner polygon of the tracts
in this
456 if len(fakeCats) == 1:
457 return fakeCats[0].get(
458 datasetType=self.config.connections.fakeCats)
460 for fakeCatRef
in fakeCats:
461 cat = fakeCatRef.get(
462 datasetType=self.config.connections.fakeCats)
463 tractId = fakeCatRef.dataId[
"tract"]
465 outputCat.append(cat[
466 skyMap.findTractIdArray(cat[self.config.insertFakes.ra_col],
467 cat[self.config.insertFakes.dec_col],
471 return pd.concat(outputCat)
473 def getVisitMatchedFakeCat(self, fakeCat, exposure):
474 """Trim the fakeCat to select particular visit
478 fakeCat : `pandas.core.frame.DataFrame`
479 The catalog of fake sources to add to the exposure
480 exposure : `lsst.afw.image.exposure.exposure.ExposureF`
481 The exposure to add the fake sources to
485 movingFakeCat : `pandas.DataFrame`
486 All fakes that belong to the visit
488 selected = exposure.getInfo().getVisitInfo().getId() == fakeCat["visit"]
490 return fakeCat[selected]
492 def copyCalibrationFields(self, calibCat, sourceCat, fieldsToCopy):
493 """Match sources in calibCat and sourceCat and copy the specified fields
498 Catalog from which to copy fields.
500 Catalog to which to copy fields.
502 Fields to copy
from calibCat to SoourceCat.
507 Catalog which includes the copied fields.
509 The fields copied are those specified by `fieldsToCopy` that actually exist
510 in the schema of `calibCat`.
512 This version was based on
and adapted
from the one
in calibrateTask.
516 sourceSchemaMapper = afwTable.SchemaMapper(sourceCat.schema)
517 sourceSchemaMapper.addMinimalSchema(sourceCat.schema,
True)
519 calibSchemaMapper = afwTable.SchemaMapper(calibCat.schema, sourceCat.schema)
522 missingFieldNames = []
523 for fieldName
in fieldsToCopy:
524 if fieldName
in calibCat.schema:
525 schemaItem = calibCat.schema.find(fieldName)
526 calibSchemaMapper.editOutputSchema().addField(schemaItem.getField())
527 schema = calibSchemaMapper.editOutputSchema()
528 calibSchemaMapper.addMapping(schemaItem.getKey(), schema.find(fieldName).getField())
530 missingFieldNames.append(fieldName)
531 if missingFieldNames:
532 raise RuntimeError(f
"calibCat is missing fields {missingFieldNames} specified in "
535 if "calib_detected" not in calibSchemaMapper.getOutputSchema():
536 self.calibSourceKey = calibSchemaMapper.addOutputField(afwTable.Field[
"Flag"](
"calib_detected",
537 "Source was detected as an icSource"))
539 self.calibSourceKey =
None
541 schema = calibSchemaMapper.getOutputSchema()
542 newCat = afwTable.SourceCatalog(schema)
543 newCat.reserve(len(sourceCat))
544 newCat.extend(sourceCat, sourceSchemaMapper)
547 for k, v
in sourceCat.schema.getAliasMap().items():
548 newCat.schema.getAliasMap().set(k, v)
550 select = newCat[
"deblend_nChild"] == 0
551 matches = afwTable.matchXy(newCat[select], calibCat, self.config.matchRadiusPix)
555 numMatches = len(matches)
556 numUniqueSources = len(set(m[1].getId()
for m
in matches))
557 if numUniqueSources != numMatches:
558 self.log.warning(
"%d calibCat sources matched only %d sourceCat sources", numMatches,
561 self.log.info(
"Copying flags from calibCat to sourceCat for %s sources", numMatches)
565 for src, calibSrc, d
in matches:
566 if self.calibSourceKey:
567 src.setFlag(self.calibSourceKey,
True)
572 calibSrcFootprint = calibSrc.getFootprint()
574 calibSrc.setFootprint(src.getFootprint())
575 src.assign(calibSrc, calibSchemaMapper)
577 calibSrc.setFootprint(calibSrcFootprint)
583 ccdVisitFakeMagnitudes = cT.Output(
584 doc=
"Catalog of fakes with magnitudes scattered for this ccdVisit.",
585 name=
"{fakesType}ccdVisitFakeMagnitudes",
586 storageClass=
"DataFrame",
587 dimensions=(
"instrument",
"visit",
"detector"),
591class ProcessCcdWithVariableFakesConfig(ProcessCcdWithFakesConfig,
592 pipelineConnections=ProcessCcdWithVariableFakesConnections):
593 scatterSize = pexConfig.RangeField(
598 doc=
"Amount of scatter to add to the visit magnitude for variable "
603class ProcessCcdWithVariableFakesTask(ProcessCcdWithFakesTask):
604 """As ProcessCcdWithFakes except add variablity to the fakes catalog
605 magnitude in the observed band
for this ccdVisit.
607 Additionally, write out the modified magnitudes to the Butler.
610 _DefaultName = "processCcdWithVariableFakes"
611 ConfigClass = ProcessCcdWithVariableFakesConfig
613 def run(self, fakeCats, exposure, skyMap, wcs=None, photoCalib=None, exposureIdInfo=None,
614 icSourceCat=None, sfdSourceCat=None):
615 """Add fake sources to a calexp and then run detection, deblending and measurement.
619 fakeCat : `pandas.core.frame.DataFrame`
620 The catalog of fake sources to add to the exposure
621 exposure : `lsst.afw.image.exposure.exposure.ExposureF`
622 The exposure to add the fake sources to
623 skyMap : `lsst.skymap.SkyMap`
624 SkyMap defining the tracts and patches the fakes are stored over.
626 WCS to use to add fake sources
627 photoCalib : `lsst.afw.image.photoCalib.PhotoCalib`
628 Photometric calibration to be used to calibrate the fake sources
629 exposureIdInfo : `lsst.obs.base.ExposureIdInfo`
632 Catalog to take the information about which sources were used
for calibration
from.
635 Catalog produced by singleFrameDriver, needed to copy some calibration flags
from.
639 resultStruct : `lsst.pipe.base.struct.Struct`
640 Results Strcut containing:
642 - outputExposure : Exposure
with added fakes
643 (`lsst.afw.image.exposure.exposure.ExposureF`)
644 - outputCat : Catalog
with detected fakes
645 (`lsst.afw.table.source.source.SourceCatalog`)
646 - ccdVisitFakeMagnitudes : Magnitudes that these fakes were
647 inserted
with after being scattered (`pandas.DataFrame`)
651 Adds pixel coordinates
for each source to the fakeCat
and removes objects
with bulge
or disk half
652 light radius = 0 (
if ``config.cleanCat =
True``). These columns are called ``x``
and ``y``
and are
in
655 Adds the ``Fake`` mask plane to the exposure which
is then set by `addFakeSources` to mark where fake
656 sources have been added. Uses the information
in the ``fakeCat`` to make fake galaxies (using galsim)
657 and fake stars, using the PSF models
from the PSF information
for the calexp. These are then added to
658 the calexp
and the calexp
with fakes included returned.
660 The galsim galaxies are made using a double sersic profile, one
for the bulge
and one
for the disk,
661 this
is then convolved
with the PSF at that point.
663 If exposureIdInfo
is not provided then the SourceCatalog IDs will
not be globally unique.
665 fakeCat = self.composeFakeCat(fakeCats, skyMap)
668 wcs = exposure.getWcs()
670 if photoCalib
is None:
671 photoCalib = exposure.getPhotoCalib()
673 if exposureIdInfo
is None:
674 exposureIdInfo = ExposureIdInfo()
676 band = exposure.getFilter().bandLabel
677 ccdVisitMagnitudes = self.addVariablity(fakeCat, band, exposure, photoCalib, exposureIdInfo)
679 self.insertFakes.run(fakeCat, exposure, wcs, photoCalib)
682 returnedStruct = self.calibrate.run(exposure, exposureIdInfo=exposureIdInfo)
683 sourceCat = returnedStruct.sourceCat
685 sourceCat = self.copyCalibrationFields(sfdSourceCat, sourceCat, self.config.srcFieldsToCopy)
687 resultStruct = pipeBase.Struct(outputExposure=exposure,
689 ccdVisitFakeMagnitudes=ccdVisitMagnitudes)
692 def addVariablity(self, fakeCat, band, exposure, photoCalib, exposureIdInfo):
693 """Add scatter to the fake catalog visit magnitudes.
695 Currently just adds a simple Gaussian scatter around the static fake
696 magnitude. This function could be modified to return any number of
701 fakeCat : `pandas.DataFrame`
702 Catalog of fakes to modify magnitudes of.
704 Current observing band to modify.
705 exposure : `lsst.afw.image.ExposureF`
706 Exposure fakes will be added to.
708 Photometric calibration object of ``exposure``.
709 exposureIdInfo : `lsst.obs.base.ExposureIdInfo`
710 Exposure id information
and metadata.
714 dataFrame : `pandas.DataFrame`
715 DataFrame containing the values of the magnitudes to that will
716 be inserted into this ccdVisit.
718 expId = exposureIdInfo.expId
719 rng = np.random.default_rng(expId)
720 magScatter = rng.normal(loc=0,
721 scale=self.config.scatterSize,
723 visitMagnitudes = fakeCat[self.insertFakes.config.mag_col % band] + magScatter
724 fakeCat.loc[:, self.insertFakes.config.mag_col % band] = visitMagnitudes
725 return pd.DataFrame(data={
"variableMag": visitMagnitudes})