lsst.pipe.tasks g4a6547c0d5+de68eba77a
Loading...
Searching...
No Matches
processCcdWithFakes.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"""
23Insert fake sources into calexps
24"""
25from astropy.table import Table
26import numpy as np
27import pandas as pd
28
29import lsst.pex.config as pexConfig
30import lsst.pipe.base as pipeBase
31
32from .insertFakes import InsertFakesTask
33from lsst.meas.base import PerTractCcdDataIdContainer
34from lsst.afw.table import SourceTable
35from lsst.obs.base import ExposureIdInfo
36from lsst.pipe.base import PipelineTask, PipelineTaskConfig, CmdLineTask, PipelineTaskConnections
37import lsst.pipe.base.connectionTypes as cT
38import lsst.afw.table as afwTable
39from lsst.pipe.tasks.calibrate import CalibrateTask
40
41__all__ = ["ProcessCcdWithFakesConfig", "ProcessCcdWithFakesTask",
42 "ProcessCcdWithVariableFakesConfig", "ProcessCcdWithVariableFakesTask"]
43
44
45class ProcessCcdWithFakesConnections(PipelineTaskConnections,
46 dimensions=("skymap", "tract", "instrument", "visit", "detector"),
47 defaultTemplates={"coaddName": "deep",
48 "wcsName": "jointcal",
49 "photoCalibName": "jointcal",
50 "fakesType": "fakes_"}):
51
52 exposure = cT.Input(
53 doc="Exposure into which fakes are to be added.",
54 name="calexp",
55 storageClass="ExposureF",
56 dimensions=("instrument", "visit", "detector")
57 )
58
59 fakeCat = cT.Input(
60 doc="Catalog of fake sources to draw inputs from.",
61 name="{fakesType}fakeSourceCat",
62 storageClass="DataFrame",
63 dimensions=("tract", "skymap")
64 )
65
66 wcs = cT.Input(
67 doc="WCS information for the input exposure.",
68 name="{wcsName}_wcs",
69 storageClass="Wcs",
70 dimensions=("tract", "skymap", "instrument", "visit", "detector")
71 )
72
73 photoCalib = cT.Input(
74 doc="Calib information for the input exposure.",
75 name="{photoCalibName}_photoCalib",
76 storageClass="PhotoCalib",
77 dimensions=("tract", "skymap", "instrument", "visit", "detector")
78 )
79
80 icSourceCat = cT.Input(
81 doc="Catalog of calibration sources",
82 name="icSrc",
83 storageClass="SourceCatalog",
84 dimensions=("instrument", "visit", "detector")
85 )
86
87 sfdSourceCat = cT.Input(
88 doc="Catalog of calibration sources",
89 name="src",
90 storageClass="SourceCatalog",
91 dimensions=("instrument", "visit", "detector")
92 )
93
94 outputExposure = cT.Output(
95 doc="Exposure with fake sources added.",
96 name="{fakesType}calexp",
97 storageClass="ExposureF",
98 dimensions=("instrument", "visit", "detector")
99 )
100
101 outputCat = cT.Output(
102 doc="Source catalog produced in calibrate task with fakes also measured.",
103 name="{fakesType}src",
104 storageClass="SourceCatalog",
105 dimensions=("instrument", "visit", "detector"),
106 )
107
108 def __init__(self, *, config=None):
109 super().__init__(config=config)
110
111 if config.doApplyExternalSkyWcs is False:
112 self.inputs.remove("wcs")
113 if config.doApplyExternalPhotoCalib is False:
114 self.inputs.remove("photoCalib")
115
116
117class ProcessCcdWithFakesConfig(PipelineTaskConfig,
118 pipelineConnections=ProcessCcdWithFakesConnections):
119 """Config for inserting fake sources
120
121 Notes
122 -----
123 The default column names are those from the UW sims database.
124 """
125
126 doApplyExternalPhotoCalib = pexConfig.Field(
127 dtype=bool,
128 default=False,
129 doc="Whether to apply an external photometric calibration via an "
130 "`lsst.afw.image.PhotoCalib` object. Uses the "
131 "`externalPhotoCalibName` config option to determine which "
132 "calibration to use."
133 )
134
135 externalPhotoCalibName = pexConfig.ChoiceField(
136 doc="What type of external photo calib to use.",
137 dtype=str,
138 default="jointcal",
139 allowed={"jointcal": "Use jointcal_photoCalib",
140 "fgcm": "Use fgcm_photoCalib",
141 "fgcm_tract": "Use fgcm_tract_photoCalib"}
142 )
143
144 doApplyExternalSkyWcs = pexConfig.Field(
145 dtype=bool,
146 default=False,
147 doc="Whether to apply an external astrometric calibration via an "
148 "`lsst.afw.geom.SkyWcs` object. Uses the "
149 "`externalSkyWcsName` config option to determine which "
150 "calibration to use."
151 )
152
153 externalSkyWcsName = pexConfig.ChoiceField(
154 doc="What type of updated WCS calib to use.",
155 dtype=str,
156 default="jointcal",
157 allowed={"jointcal": "Use jointcal_wcs"}
158 )
159
160 coaddName = pexConfig.Field(
161 doc="The name of the type of coadd used",
162 dtype=str,
163 default="deep",
164 )
165
166 srcFieldsToCopy = pexConfig.ListField(
167 dtype=str,
168 default=("calib_photometry_reserved", "calib_photometry_used", "calib_astrometry_used",
169 "calib_psf_candidate", "calib_psf_used", "calib_psf_reserved"),
170 doc=("Fields to copy from the `src` catalog to the output catalog "
171 "for matching sources Any missing fields will trigger a "
172 "RuntimeError exception.")
173 )
174
175 matchRadiusPix = pexConfig.Field(
176 dtype=float,
177 default=3,
178 doc=("Match radius for matching icSourceCat objects to sourceCat objects (pixels)"),
179 )
180
181 calibrate = pexConfig.ConfigurableField(target=CalibrateTask,
182 doc="The calibration task to use.")
183
184 insertFakes = pexConfig.ConfigurableField(target=InsertFakesTask,
185 doc="Configuration for the fake sources")
186
187 def setDefaults(self):
188 super().setDefaults()
189 self.calibrate.measurement.plugins["base_PixelFlags"].masksFpAnywhere.append("FAKE")
190 self.calibrate.measurement.plugins["base_PixelFlags"].masksFpCenter.append("FAKE")
191 self.calibrate.doAstrometry = False
192 self.calibrate.doWriteMatches = False
193 self.calibrate.doPhotoCal = False
194 self.calibrate.detection.reEstimateBackground = False
195
196
197class ProcessCcdWithFakesTask(PipelineTask, CmdLineTask):
198 """Insert fake objects into calexps.
199
200 Add fake stars and galaxies to the given calexp, specified in the dataRef. Galaxy parameters are read in
201 from the specified file and then modelled using galsim. Re-runs characterize image and calibrate image to
202 give a new background estimation and measurement of the calexp.
203
204 `ProcessFakeSourcesTask` inherits six functions from insertFakesTask that make images of the fake
205 sources and then add them to the calexp.
206
207 `addPixCoords`
208 Use the WCS information to add the pixel coordinates of each source
209 Adds an ``x`` and ``y`` column to the catalog of fake sources.
210 `trimFakeCat`
211 Trim the fake cat to about the size of the input image.
212 `mkFakeGalsimGalaxies`
213 Use Galsim to make fake double sersic galaxies for each set of galaxy parameters in the input file.
214 `mkFakeStars`
215 Use the PSF information from the calexp to make a fake star using the magnitude information from the
216 input file.
217 `cleanCat`
218 Remove rows of the input fake catalog which have half light radius, of either the bulge or the disk,
219 that are 0.
220 `addFakeSources`
221 Add the fake sources to the calexp.
222
223 Notes
224 -----
225 The ``calexp`` with fake souces added to it is written out as the datatype ``calexp_fakes``.
226 """
227
228 _DefaultName = "processCcdWithFakes"
229 ConfigClass = ProcessCcdWithFakesConfig
230
231 def __init__(self, schema=None, butler=None, **kwargs):
232 """Initalize things! This should go above in the class docstring
233 """
234
235 super().__init__(**kwargs)
236
237 if schema is None:
238 schema = SourceTable.makeMinimalSchema()
239 self.schema = schema
240 self.makeSubtask("insertFakes")
241 self.makeSubtask("calibrate")
242
243 def runDataRef(self, dataRef):
244 """Read in/write out the required data products and add fake sources to the calexp.
245
246 Parameters
247 ----------
249 Data reference defining the ccd to have fakes added to it.
250 Used to access the following data products:
251 calexp
252 jointcal_wcs
253 jointcal_photoCalib
254
255 Notes
256 -----
257 Uses the calibration and WCS information attached to the calexp for the posistioning and calibration
258 of the sources unless the config option config.externalPhotoCalibName or config.externalSkyWcsName
259 are set then it uses the specified outputs. The config defualts for the column names in the catalog
260 of fakes are taken from the University of Washington simulations database.
261 Operates on one ccd at a time.
262 """
263 exposureIdInfo = dataRef.get("expIdInfo")
264
265 if self.config.insertFakes.fakeType == "snapshot":
266 fakeCat = dataRef.get("fakeSourceCat").toDataFrame()
267 elif self.config.insertFakes.fakeType == "static":
268 fakeCat = dataRef.get("deepCoadd_fakeSourceCat").toDataFrame()
269 else:
270 fakeCat = Table.read(self.config.insertFakes.fakeType).to_pandas()
271
272 calexp = dataRef.get("calexp")
273 if self.config.doApplyExternalSkyWcs:
274 self.log.info("Using external wcs from %s", self.config.externalSkyWcsName)
275 wcs = dataRef.get(self.config.externalSkyWcsName + "_wcs")
276 else:
277 wcs = calexp.getWcs()
278
279 if self.config.doApplyExternalPhotoCalib:
280 self.log.info("Using external photocalib from %s", self.config.externalPhotoCalibName)
281 photoCalib = dataRef.get(self.config.externalPhotoCalibName + "_photoCalib")
282 else:
283 photoCalib = calexp.getPhotoCalib()
284
285 icSourceCat = dataRef.get("icSrc", immediate=True)
286 sfdSourceCat = dataRef.get("src", immediate=True)
287
288 resultStruct = self.run(fakeCat, calexp, wcs=wcs, photoCalib=photoCalib,
289 exposureIdInfo=exposureIdInfo, icSourceCat=icSourceCat,
290 sfdSourceCat=sfdSourceCat)
291
292 dataRef.put(resultStruct.outputExposure, "fakes_calexp")
293 dataRef.put(resultStruct.outputCat, "fakes_src")
294 return resultStruct
295
296 def runQuantum(self, butlerQC, inputRefs, outputRefs):
297 inputs = butlerQC.get(inputRefs)
298 if 'exposureIdInfo' not in inputs.keys():
299 expId, expBits = butlerQC.quantum.dataId.pack("visit_detector", returnMaxBits=True)
300 inputs['exposureIdInfo'] = ExposureIdInfo(expId, expBits)
301
302 if not self.config.doApplyExternalSkyWcs:
303 inputs["wcs"] = inputs["exposure"].getWcs()
304
305 if not self.config.doApplyExternalPhotoCalib:
306 inputs["photoCalib"] = inputs["exposure"].getPhotoCalib()
307
308 outputs = self.run(**inputs)
309 butlerQC.put(outputs, outputRefs)
310
311 @classmethod
312 def _makeArgumentParser(cls):
313 parser = pipeBase.ArgumentParser(name=cls._DefaultName)
314 parser.add_id_argument("--id", "fakes_calexp", help="data ID with raw CCD keys [+ tract optionally], "
315 "e.g. --id visit=12345 ccd=1,2 [tract=0]",
316 ContainerClass=PerTractCcdDataIdContainer)
317 return parser
318
319 def run(self, fakeCat, exposure, wcs=None, photoCalib=None, exposureIdInfo=None, icSourceCat=None,
320 sfdSourceCat=None):
321 """Add fake sources to a calexp and then run detection, deblending and measurement.
322
323 Parameters
324 ----------
325 fakeCat : `pandas.core.frame.DataFrame`
326 The catalog of fake sources to add to the exposure
327 exposure : `lsst.afw.image.exposure.exposure.ExposureF`
328 The exposure to add the fake sources to
330 WCS to use to add fake sources
331 photoCalib : `lsst.afw.image.photoCalib.PhotoCalib`
332 Photometric calibration to be used to calibrate the fake sources
333 exposureIdInfo : `lsst.obs.base.ExposureIdInfo`
334 icSourceCat : `lsst.afw.table.SourceCatalog`
335 Default : None
336 Catalog to take the information about which sources were used for calibration from.
337 sfdSourceCat : `lsst.afw.table.SourceCatalog`
338 Default : None
339 Catalog produced by singleFrameDriver, needed to copy some calibration flags from.
340
341 Returns
342 -------
343 resultStruct : `lsst.pipe.base.struct.Struct`
344 contains : outputExposure : `lsst.afw.image.exposure.exposure.ExposureF`
345 outputCat : `lsst.afw.table.source.source.SourceCatalog`
346
347 Notes
348 -----
349 Adds pixel coordinates for each source to the fakeCat and removes objects with bulge or disk half
350 light radius = 0 (if ``config.cleanCat = True``). These columns are called ``x`` and ``y`` and are in
351 pixels.
352
353 Adds the ``Fake`` mask plane to the exposure which is then set by `addFakeSources` to mark where fake
354 sources have been added. Uses the information in the ``fakeCat`` to make fake galaxies (using galsim)
355 and fake stars, using the PSF models from the PSF information for the calexp. These are then added to
356 the calexp and the calexp with fakes included returned.
357
358 The galsim galaxies are made using a double sersic profile, one for the bulge and one for the disk,
359 this is then convolved with the PSF at that point.
360
361 If exposureIdInfo is not provided then the SourceCatalog IDs will not be globally unique.
362 """
363
364 if wcs is None:
365 wcs = exposure.getWcs()
366
367 if photoCalib is None:
368 photoCalib = exposure.getPhotoCalib()
369
370 self.insertFakes.run(fakeCat, exposure, wcs, photoCalib)
371
372 # detect, deblend and measure sources
373 if exposureIdInfo is None:
374 exposureIdInfo = ExposureIdInfo()
375 returnedStruct = self.calibrate.run(exposure, exposureIdInfo=exposureIdInfo)
376 sourceCat = returnedStruct.sourceCat
377
378 sourceCat = self.copyCalibrationFields(sfdSourceCat, sourceCat, self.config.srcFieldsToCopy)
379
380 resultStruct = pipeBase.Struct(outputExposure=exposure, outputCat=sourceCat)
381 return resultStruct
382
383 def copyCalibrationFields(self, calibCat, sourceCat, fieldsToCopy):
384 """Match sources in calibCat and sourceCat and copy the specified fields
385
386 Parameters
387 ----------
389 Catalog from which to copy fields.
390 sourceCat : `lsst.afw.table.SourceCatalog`
391 Catalog to which to copy fields.
392 fieldsToCopy : `lsst.pex.config.listField.List`
393 Fields to copy from calibCat to SoourceCat.
394
395 Returns
396 -------
398 Catalog which includes the copied fields.
399
400 The fields copied are those specified by `fieldsToCopy` that actually exist
401 in the schema of `calibCat`.
402
403 This version was based on and adapted from the one in calibrateTask.
404 """
405
406 # Make a new SourceCatalog with the data from sourceCat so that we can add the new columns to it
407 sourceSchemaMapper = afwTable.SchemaMapper(sourceCat.schema)
408 sourceSchemaMapper.addMinimalSchema(sourceCat.schema, True)
409
410 calibSchemaMapper = afwTable.SchemaMapper(calibCat.schema, sourceCat.schema)
411
412 # Add the desired columns from the option fieldsToCopy
413 missingFieldNames = []
414 for fieldName in fieldsToCopy:
415 if fieldName in calibCat.schema:
416 schemaItem = calibCat.schema.find(fieldName)
417 calibSchemaMapper.editOutputSchema().addField(schemaItem.getField())
418 schema = calibSchemaMapper.editOutputSchema()
419 calibSchemaMapper.addMapping(schemaItem.getKey(), schema.find(fieldName).getField())
420 else:
421 missingFieldNames.append(fieldName)
422 if missingFieldNames:
423 raise RuntimeError(f"calibCat is missing fields {missingFieldNames} specified in "
424 "fieldsToCopy")
425
426 if "calib_detected" not in calibSchemaMapper.getOutputSchema():
427 self.calibSourceKey = calibSchemaMapper.addOutputField(afwTable.Field["Flag"]("calib_detected",
428 "Source was detected as an icSource"))
429 else:
430 self.calibSourceKey = None
431
432 schema = calibSchemaMapper.getOutputSchema()
433 newCat = afwTable.SourceCatalog(schema)
434 newCat.reserve(len(sourceCat))
435 newCat.extend(sourceCat, sourceSchemaMapper)
436
437 # Set the aliases so it doesn't complain.
438 for k, v in sourceCat.schema.getAliasMap().items():
439 newCat.schema.getAliasMap().set(k, v)
440
441 select = newCat["deblend_nChild"] == 0
442 matches = afwTable.matchXy(newCat[select], calibCat, self.config.matchRadiusPix)
443 # Check that no sourceCat sources are listed twice (we already know
444 # that each match has a unique calibCat source ID, due to using
445 # that ID as the key in bestMatches)
446 numMatches = len(matches)
447 numUniqueSources = len(set(m[1].getId() for m in matches))
448 if numUniqueSources != numMatches:
449 self.log.warning("%d calibCat sources matched only %d sourceCat sources", numMatches,
450 numUniqueSources)
451
452 self.log.info("Copying flags from calibCat to sourceCat for %s sources", numMatches)
453
454 # For each match: set the calibSourceKey flag and copy the desired
455 # fields
456 for src, calibSrc, d in matches:
457 if self.calibSourceKey:
458 src.setFlag(self.calibSourceKey, True)
459 # src.assign copies the footprint from calibSrc, which we don't want
460 # (DM-407)
461 # so set calibSrc's footprint to src's footprint before src.assign,
462 # then restore it
463 calibSrcFootprint = calibSrc.getFootprint()
464 try:
465 calibSrc.setFootprint(src.getFootprint())
466 src.assign(calibSrc, calibSchemaMapper)
467 finally:
468 calibSrc.setFootprint(calibSrcFootprint)
469
470 return newCat
471
472
473class ProcessCcdWithVariableFakesConnections(ProcessCcdWithFakesConnections):
474 ccdVisitFakeMagnitudes = cT.Output(
475 doc="Catalog of fakes with magnitudes scattered for this ccdVisit.",
476 name="{fakesType}ccdVisitFakeMagnitudes",
477 storageClass="DataFrame",
478 dimensions=("instrument", "visit", "detector"),
479 )
480
481
482class ProcessCcdWithVariableFakesConfig(ProcessCcdWithFakesConfig,
483 pipelineConnections=ProcessCcdWithVariableFakesConnections):
484 scatterSize = pexConfig.RangeField(
485 dtype=float,
486 default=0.4,
487 min=0,
488 max=100,
489 doc="Amount of scatter to add to the visit magnitude for variable "
490 "sources."
491 )
492
493
494class ProcessCcdWithVariableFakesTask(ProcessCcdWithFakesTask):
495 """As ProcessCcdWithFakes except add variablity to the fakes catalog
496 magnitude in the observed band for this ccdVisit.
497
498 Additionally, write out the modified magnitudes to the Butler.
499 """
500
501 _DefaultName = "processCcdWithVariableFakes"
502 ConfigClass = ProcessCcdWithVariableFakesConfig
503
504 def run(self, fakeCat, exposure, wcs=None, photoCalib=None, exposureIdInfo=None, icSourceCat=None,
505 sfdSourceCat=None):
506 """Add fake sources to a calexp and then run detection, deblending and measurement.
507
508 Parameters
509 ----------
510 fakeCat : `pandas.core.frame.DataFrame`
511 The catalog of fake sources to add to the exposure
512 exposure : `lsst.afw.image.exposure.exposure.ExposureF`
513 The exposure to add the fake sources to
515 WCS to use to add fake sources
516 photoCalib : `lsst.afw.image.photoCalib.PhotoCalib`
517 Photometric calibration to be used to calibrate the fake sources
518 exposureIdInfo : `lsst.obs.base.ExposureIdInfo`
519 icSourceCat : `lsst.afw.table.SourceCatalog`
520 Default : None
521 Catalog to take the information about which sources were used for calibration from.
522 sfdSourceCat : `lsst.afw.table.SourceCatalog`
523 Default : None
524 Catalog produced by singleFrameDriver, needed to copy some calibration flags from.
525
526 Returns
527 -------
528 resultStruct : `lsst.pipe.base.struct.Struct`
529 Results Strcut containing:
530
531 - outputExposure : Exposure with added fakes
532 (`lsst.afw.image.exposure.exposure.ExposureF`)
533 - outputCat : Catalog with detected fakes
534 (`lsst.afw.table.source.source.SourceCatalog`)
535 - ccdVisitFakeMagnitudes : Magnitudes that these fakes were
536 inserted with after being scattered (`pandas.DataFrame`)
537
538 Notes
539 -----
540 Adds pixel coordinates for each source to the fakeCat and removes objects with bulge or disk half
541 light radius = 0 (if ``config.cleanCat = True``). These columns are called ``x`` and ``y`` and are in
542 pixels.
543
544 Adds the ``Fake`` mask plane to the exposure which is then set by `addFakeSources` to mark where fake
545 sources have been added. Uses the information in the ``fakeCat`` to make fake galaxies (using galsim)
546 and fake stars, using the PSF models from the PSF information for the calexp. These are then added to
547 the calexp and the calexp with fakes included returned.
548
549 The galsim galaxies are made using a double sersic profile, one for the bulge and one for the disk,
550 this is then convolved with the PSF at that point.
551
552 If exposureIdInfo is not provided then the SourceCatalog IDs will not be globally unique.
553 """
554 if wcs is None:
555 wcs = exposure.getWcs()
556
557 if photoCalib is None:
558 photoCalib = exposure.getPhotoCalib()
559
560 if exposureIdInfo is None:
561 exposureIdInfo = ExposureIdInfo()
562
563 band = exposure.getFilterLabel().bandLabel
564 ccdVisitMagnitudes = self.addVariablity(fakeCat, band, exposure, photoCalib, exposureIdInfo)
565
566 self.insertFakes.run(fakeCat, exposure, wcs, photoCalib)
567
568 # detect, deblend and measure sources
569 returnedStruct = self.calibrate.run(exposure, exposureIdInfo=exposureIdInfo)
570 sourceCat = returnedStruct.sourceCat
571
572 sourceCat = self.copyCalibrationFields(sfdSourceCat, sourceCat, self.config.srcFieldsToCopy)
573
574 resultStruct = pipeBase.Struct(outputExposure=exposure,
575 outputCat=sourceCat,
576 ccdVisitFakeMagnitudes=ccdVisitMagnitudes)
577 return resultStruct
578
579 def addVariablity(self, fakeCat, band, exposure, photoCalib, exposureIdInfo):
580 """Add scatter to the fake catalog visit magnitudes.
581
582 Currently just adds a simple Gaussian scatter around the static fake
583 magnitude. This function could be modified to return any number of
584 fake variability.
585
586 Parameters
587 ----------
588 fakeCat : `pandas.DataFrame`
589 Catalog of fakes to modify magnitudes of.
590 band : `str`
591 Current observing band to modify.
592 exposure : `lsst.afw.image.ExposureF`
593 Exposure fakes will be added to.
594 photoCalib : `lsst.afw.image.PhotoCalib`
595 Photometric calibration object of ``exposure``.
596 exposureIdInfo : `lsst.obs.base.ExposureIdInfo`
597 Exposure id information and metadata.
598
599 Returns
600 -------
601 dataFrame : `pandas.DataFrame`
602 DataFrame containing the values of the magnitudes to that will
603 be inserted into this ccdVisit.
604 """
605 expId = exposureIdInfo.expId
606 rng = np.random.default_rng(expId)
607 magScatter = rng.normal(loc=0,
608 scale=self.config.scatterSize,
609 size=len(fakeCat))
610 visitMagnitudes = fakeCat[self.insertFakes.config.mag_col % band] + magScatter
611 fakeCat.loc[:, self.insertFakes.config.mag_col % band] = visitMagnitudes
612 return pd.DataFrame(data={"variableMag": visitMagnitudes})